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.Refer back to the lists of useful mutate and filtering functions. Describe how each operation changes when you combine it with grouping.
Summary functions (mean()
), offset functions (lead()
, lag()
), ranking functions (min_rank()
, row_number()
), operate within each group when used with group_by()
in mutate()
or filter()
. Arithmetic operators (+
, -
), logical operators (<
, ==
), modular arithmetic operators (%%
, %/%
), logarithmic functions (log
) are not affected by group_by.
Summary functions like mean()
, median()
, sum()
, std()
and others calculate their values within each group when used with mutate()
or filter()
and group_by()
.
tibble(
x = 1:9,
group = rep(c("a", "b", "c"), each = 3)
) %>%
mutate(x_mean = mean(x)) %>%
group_by(group) %>%
mutate(x_mean_2 = mean(x))
Arithmetic operators +
, -
, *
, /
, ^
are not affected by group_by()
.
tibble(
x = 1:9,
group = rep(c("a", "b", "c"), each = 3)
) %>%
mutate(y = x + 2) %>%
group_by(group) %>%
mutate(z = x + 2)
The modular arithmetic operators %/%
and %%
are not affected by group_by()
tibble(
x = 1:9,
group = rep(c("a", "b", "c"), each = 3)
) %>%
mutate(y = x %% 2) %>%
group_by(group) %>%
mutate(z = x %% 2)
The logarithmic functions log()
, log2()
, and log10()
are not affected by group_by()
.
tibble(
x = 1:9,
group = rep(c("a", "b", "c"), each = 3)
) %>%
mutate(y = log(x)) %>%
group_by(group) %>%
mutate(z = log(x))
The offset functions lead()
and lag()
respect the groupings in group_by()
. The functions lag()
and lead()
will only return values within each group.
tibble(
x = 1:9,
group = rep(c("a", "b", "c"), each = 3)
) %>%
group_by(group) %>%
mutate(
lag_x = lag(x),
lead_x = lead(x)
)
The cumulative and rolling aggregate functions cumsum()
, cumprod()
, cummin()
, cummax()
, and cummean()
calculate values within each group.
tibble(
x = 1:9,
group = rep(c("a", "b", "c"), each = 3)
) %>%
mutate(x_cumsum = cumsum(x)) %>%
group_by(group) %>%
mutate(x_cumsum_2 = cumsum(x))
Logical comparisons, <
, <=
, >
, >=
, !=
, and ==
are not affected by group_by()
.
tibble(
x = 1:9,
y = 9:1,
group = rep(c("a", "b", "c"), each = 3)
) %>%
mutate(x_lte_y = x <= y) %>%
group_by(group) %>%
mutate(x_lte_y_2 = x <= y)
Ranking functions like min_rank()
work within each group when used with group_by()
.
tibble(
x = 1:9,
group = rep(c("a", "b", "c"), each = 3)
) %>%
mutate(rnk = min_rank(x)) %>%
group_by(group) %>%
mutate(rnk2 = min_rank(x))
Though not asked in the question, note that arrange()
ignores groups when sorting values.
tibble(
x = runif(9),
group = rep(c("a", "b", "c"), each = 3)
) %>%
group_by(group) %>%
arrange(x)
However, the order of values from arrange()
can interact with groups when used with functions that rely on the ordering of elements, such as lead()
, lag()
, or cumsum()
.
tibble(
group = rep(c("a", "b", "c"), each = 3),
x = runif(9)
) %>%
group_by(group) %>%
arrange(x) %>%
mutate(lag_x = lag(x))
2. Which plane (tailnum) has the worst on-time record?
The question does not define a way to measure on-time record, so I will consider two metrics:
- proportion of flights not delayed or cancelled, and
- mean arrival delay.
The first metric is the proportion of not-cancelled and on-time flights. I use the presence of an arrival time to mean that a flight was not cancelled. However, there are many planes that have never flown an on-time flight. Many of the planes that have the lowest proportion have only flown a small number of flights.
flights %>%
filter(!is.na(tailnum)) %>%
mutate(on_time = !is.na(arr_time) & (arr_delay <= 0)) %>%
group_by(tailnum) %>%
summarise(on_time = mean(on_time), n = n()) %>%
filter(min_rank(on_time) == 1)
So, I will remove planes that flew at least 20 flights. The choice of 20 was chosen because it round number near the first quartile of the number of flights by plane.
quantile(count(flights, tailnum)$n)
0% 25% 50% 75% 100%
1 23 54 110 2512
The plane which few at least 20 flights with the worst on time record is:
flights %>%
filter(!is.na(tailnum)) %>%
mutate(on_time = !is.na(arr_time) & (arr_delay <= 0)) %>%
group_by(tailnum) %>%
summarise(on_time = mean(on_time), n = n()) %>%
filter(n >= 20) %>%
filter(min_rank(on_time) == 1)
The second metric is the mean minutes delayed. As with the previous metric, I will only consider planes which flew least 20 flights. A different plane has the worst on-time record when measured as average minutes delayed.
flights %>%
group_by(tailnum) %>%
summarise(arr_delay = mean(arr_delay), n = n()) %>%
filter(n >= 20) %>%
filter(min_rank(desc(arr_delay)) == 1)
3. What time of day should you fly if you want to avoid delays as much as possible?
Let’s group by the hour of the flight. The earlier the flight is scheduled, the lower its expected delay. This is intuitive as delays will affect later flights. Morning flights have fewer (if any) previous flights that can delay them.
flights %>%
group_by(hour) %>%
summarise(arr_delay = mean(arr_delay, na.rm = TRUE)) %>%
arrange(arr_delay)
4. For each destination, compute the total minutes of delay. For each flight, compute the proportion of the total delay for its destination.
The key to answering this question is to only include delayed flights when calculating the total delay and proportion of delay.
flights %>%
filter(arr_delay > 0) %>%
group_by(dest) %>%
mutate(
arr_delay_total = sum(arr_delay),
arr_delay_prop = arr_delay / arr_delay_total
) %>%
select(
dest, month, day, dep_time, carrier, flight,
arr_delay, arr_delay_prop
) %>%
arrange(dest, desc(arr_delay_prop))
There is some ambiguity in the meaning of flights in the question. The first example defined a flight as a row in the flights
table, a particular trip by aircraft from a particular airport. However, flight could also refer to the flight number, which is the code a carrier uses for an airline service of a route. For example, AA1 is the flight number of the 09:00 American Airlines flight between JFK and LAX. The flight number is contained flights$flight
, though what is called a “flight” combination of the flights$carrier
and flights$flight
.
flights %>%
filter(arr_delay > 0) %>%
group_by(dest, origin, carrier, flight) %>%
summarise(arr_delay = sum(arr_delay)) %>%
group_by(dest) %>%
mutate(
arr_delay_prop = arr_delay / sum(arr_delay)
) %>%
arrange(dest, desc(arr_delay_prop)) %>%
select(carrier, flight, origin, dest, arr_delay_prop)
6. Look at each destination. Can you find flights that are suspiciously fast? (i.e. flights that represent a potential data entry error). Compute the air time of a flight relative to the shortest flight to that destination. Which flights were most delayed in the air?
When calculating this answer we should only compare flights within the same (origin, destination) pair.
To find unusual observations, we need to first put them on the same scale. I will standardize values by subtracting the mean from each and then dividing each by the standard deviation.
\(z = \frac{x - \mu}{\sigma}\)
A standardized variable is often called a z-score. The units of the standardized variable are standard deviations from the mean. This will put the flight times from different routes on the same scale. The larger the magnitude of the standardized variable for an observation, the more unusual the observation is. Flights with negative values of the standardized variable are faster than the mean flight for that route, while those with positive values are slower than the mean flight for that route.
standardized_flights <- flights %>%
filter(!is.na(air_time)) %>%
group_by(dest, origin) %>%
mutate(
air_time_mean = mean(air_time),
air_time_sd = sd(air_time),
n = n()
) %>%
ungroup() %>%
mutate(air_time_standard = (air_time - air_time_mean) / (air_time_sd + 1))
I add 1 to the denominator and numerator to avoid dividing by zero. Note that the ungroup()
here is not necessary. However, I will be using this data frame later. Through experience, I have found that I have fewer bugs when I keep a data frame grouped for only those verbs that need it. If I did not ungroup()
this data frame, the arrange()
used later would not work as expected. It is better to err on the side of using ungroup()
when unnecessary.
The distribution of the standardized air flights has long right tail.
ggplot(standardized_flights, aes(x = air_time_standard)) +
geom_density()

Unusually fast flights are those flights with the smallest standardized values.
standardized_flights %>%
arrange(air_time_standard) %>%
select(
carrier, flight, origin, dest, month, day,
air_time, air_time_mean, air_time_standard
) %>%
head(10) %>%
print(width = Inf)
I used width = Inf to ensure that all columns will be printed.
The fastest flight is DL1499 from LGA to ATL which departed on 2013-05-25 at 17:09. It has an air time of 65 minutes, compared to an average flight time of 114 minutes for its route. This is 4.6 standard deviations below the average flight on its route.
It is important to note that this does not necessarily imply that there was a data entry error. We should check these flights to see whether there was some reason for the difference. It may be that we are missing some piece of information that explains these unusual times.
A potential issue with the way that we standardized the flights is that the mean and standard deviation used to calculate are sensitive to outliers and outliers is what we are looking for. Instead of standardizing variables with the mean and variance, we could use the median as a measure of central tendency and the interquartile range (IQR) as a measure of spread. The median and IQR are more resistant to outliers than the mean and standard deviation. The following method uses the median and inter-quartile range, which are less sensitive to outliers.
standardized_flights2 <- flights %>%
filter(!is.na(air_time)) %>%
group_by(dest, origin) %>%
mutate(
air_time_median = median(air_time),
air_time_iqr = IQR(air_time),
n = n(),
air_time_standard = (air_time - air_time_median) / air_time_iqr
)
The distribution of the standardized air flights using this new definition also has long right tail of slow flights.
ggplot(standardized_flights2, aes(x = air_time_standard)) +
geom_density()

Unusually fast flights are those flights with the smallest standardized values.
standardized_flights2 %>%
arrange(air_time_standard) %>%
select(
carrier, flight, origin, dest, month, day, air_time,
air_time_median, air_time_standard
) %>%
head(10) %>%
print(width = Inf)
All of these answers have relied only on using a distribution of comparable observations to find unusual observations. In this case, the comparable observations were flights from the same origin to the same destination. Apart from our knowledge that flights from the same origin to the same destination should have similar air times, we have not used any other domain-specific knowledge. But we know much more about this problem. The most obvious piece of knowledge we have is that we know that flights cannot travel back in time, so there should never be a flight with a negative airtime. But we also know that aircraft have maximum speeds. While different aircraft have different cruising speeds, commercial airliners typically cruise at air speeds around 547–575 mph. Calculating the ground speed of aircraft is complicated by the way in which winds, especially the influence of wind, especially jet streams, on the ground-speed of flights. A strong tailwind can increase ground-speed of the aircraft by 200 mph. Apart from the retired Concorde. For example, in 2018, a transatlantic flight traveled at 770 mph due to a strong jet stream tailwind. This means that any flight traveling at speeds greater than 800 mph is implausible, and it may be worth checking flights traveling at greater than 600 or 700 mph. Ground speed could also be used to identify aircraft flying implausibly slow. Joining flights data with the air craft type in the planes table and getting information about typical or top speeds of those aircraft could provide a more detailed way to identify implausibly fast or slow flights. Additional data on high altitude wind speeds at the time of the flight would further help.
Knowing the substance of the data analysis at hand is one of the most important tools of a data scientist. The tools of statistics are a complement, not a substitute, for that knowledge.
With that in mind, Let’s plot the distribution of the ground speed of flights. The modal flight in this data has a ground speed of between 400 and 500 mph. The distribution of ground speeds has a large left tail of slower flights below 400 mph constituting the majority. There are very few flights with a ground speed over 500 mph.
flights %>%
mutate(mph = distance / (air_time / 60)) %>%
ggplot(aes(x = mph)) +
geom_histogram(binwidth = 10)

The fastest flight is the same one identified as the largest outlier earlier. Its ground speed was 703 mph. This is fast for a commercial jet, but not impossible.
flights %>%
mutate(mph = distance / (air_time / 60)) %>%
arrange(desc(mph)) %>%
select(mph, flight, carrier, flight, month, day, dep_time) %>%
head(5)
One explanation for unusually fast flights is that they are “making up time” in the air by flying faster. Commercial aircraft do not fly at their top speed since the airlines are also concerned about fuel consumption. But, if a flight is delayed on the ground, it may fly faster than usual in order to avoid a late arrival. So, I would expect that some of the unusually fast flights were delayed on departure.
flights %>%
mutate(mph = distance / (air_time / 60)) %>%
arrange(desc(mph)) %>%
select(
origin, dest, mph, year, month, day, dep_time, flight, carrier,
dep_delay, arr_delay
)
head(5)
[1] 5
Five of the top ten flights had departure delays, and three of those were able to make up that time in the air and arrive ahead of schedule.
Overall, there were a few flights that seemed unusually fast, but they all fall into the realm of plausibility and likely are not data entry problems. [Ed. Please correct me if I am missing something]
The second part of the question asks us to compare flights to the fastest flight on a route to find the flights most delayed in the air. I will calculate the amount a flight is delayed in air in two ways. The first is the absolute delay, defined as the number of minutes longer than the fastest flight on that route,air_time - min(air_time)
. The second is the relative delay, which is the percentage increase in air time relative to the time of the fastest flight along that route, (air_time - min(air_time)) / min(air_time) * 100
.
air_time_delayed <-
flights %>%
group_by(origin, dest) %>%
mutate(
air_time_min = min(air_time, na.rm = TRUE),
air_time_delay = air_time - air_time_min,
air_time_delay_pct = air_time_delay / air_time_min * 100
)
The most delayed flight in air in minutes was DL841 from JFK to SFO which departed on 2013-07-28 at 17:27. It took 189 minutes longer than the flight with the shortest air time on its route.
air_time_delayed %>%
arrange(desc(air_time_delay)) %>%
select(
air_time_delay, carrier, flight,
origin, dest, year, month, day, dep_time,
air_time, air_time_min
) %>%
head() %>%
print(width = Inf)
The most delayed flight in air as a percentage of the fastest flight along that route was US2136 from LGA to BOS departing on 2013-06-17 at 16:52. It took 410% longer than the flight with the shortest air time on its route.
air_time_delayed %>%
arrange(desc(air_time_delay)) %>%
select(
air_time_delay_pct, carrier, flight,
origin, dest, year, month, day, dep_time,
air_time, air_time_min
) %>%
head() %>%
print(width = Inf)
8. For each plane, count the number of flights before the first delay of greater than 1 hour.
The question does not specify arrival or departure delay. I consider dep_delay in this answer, though similar code could be used for arr_delay.
flights %>%
# sort in increasing order
select(tailnum, year, month, day, dep_delay) %>%
filter(!is.na(dep_delay)) %>%
arrange(tailnum, year, month, day) %>%
group_by(tailnum) %>%
# cumulative number of flights delayed over one hour
mutate(cumulative_hr_delays = cumsum(dep_delay > 60)) %>%
# count the number of flights == 0
summarise(total_flights = sum(cumulative_hr_delays < 1)) %>%
arrange(total_flights)
LS0tDQp0aXRsZTogIkdyb3VwZWQgbXV0YXRlcyAoYW5kIGZpbHRlcnMpIg0Kb3V0cHV0OiANCiAgaHRtbF9ub3RlYm9vazoNCiAgICB0b2M6IHRydWUNCiAgICB0b2NfZmxvYXQ6IHRydWUNCi0tLQ0KDQpgYGB7ciBsb2FkbGlicmFyeX0NCnN1cHByZXNzUGFja2FnZVN0YXJ0dXBNZXNzYWdlcyhsaWJyYXJ5KG55Y2ZsaWdodHMxMykpDQpzdXBwcmVzc1BhY2thZ2VTdGFydHVwTWVzc2FnZXMobGlicmFyeSh0aWR5dmVyc2UpKQ0KYGBgDQoNCiMjIyAxLlJlZmVyIGJhY2sgdG8gdGhlIGxpc3RzIG9mIHVzZWZ1bCBtdXRhdGUgYW5kIGZpbHRlcmluZyBmdW5jdGlvbnMuIERlc2NyaWJlIGhvdyBlYWNoIG9wZXJhdGlvbiBjaGFuZ2VzIHdoZW4geW91IGNvbWJpbmUgaXQgd2l0aCBncm91cGluZy4NCg0KU3VtbWFyeSBmdW5jdGlvbnMgKGBtZWFuKClgKSwgb2Zmc2V0IGZ1bmN0aW9ucyAoYGxlYWQoKWAsIGBsYWcoKWApLCByYW5raW5nIGZ1bmN0aW9ucyAoYG1pbl9yYW5rKClgLCBgcm93X251bWJlcigpYCksIG9wZXJhdGUgd2l0aGluIGVhY2ggZ3JvdXAgd2hlbiB1c2VkIHdpdGggYGdyb3VwX2J5KClgIGluIGBtdXRhdGUoKWAgb3IgYGZpbHRlcigpYC4gQXJpdGhtZXRpYyBvcGVyYXRvcnMgKGArYCwgYC1gKSwgbG9naWNhbCBvcGVyYXRvcnMgKGA8YCwgYD09YCksIG1vZHVsYXIgYXJpdGhtZXRpYyBvcGVyYXRvcnMgKGAlJWAsIGAlLyVgKSwgbG9nYXJpdGhtaWMgZnVuY3Rpb25zIChgbG9nYCkgYXJlIG5vdCBhZmZlY3RlZCBieSBncm91cF9ieS4NCg0KU3VtbWFyeSBmdW5jdGlvbnMgbGlrZSBgbWVhbigpYCwgYG1lZGlhbigpYCwgYHN1bSgpYCwgYHN0ZCgpYCBhbmQgb3RoZXJzIGNhbGN1bGF0ZSB0aGVpciB2YWx1ZXMgd2l0aGluIGVhY2ggZ3JvdXAgd2hlbiB1c2VkIHdpdGggYG11dGF0ZSgpYCBvciBgZmlsdGVyKClgIGFuZCBgZ3JvdXBfYnkoKWAuDQoNCmBgYHtyfQ0KdGliYmxlKA0KICB4ID0gMTo5LA0KICBncm91cCA9IHJlcChjKCJhIiwgImIiLCAiYyIpLCBlYWNoID0gMykNCikgJT4lDQogIG11dGF0ZSh4X21lYW4gPSBtZWFuKHgpKSAlPiUNCiAgZ3JvdXBfYnkoZ3JvdXApICU+JQ0KICBtdXRhdGUoeF9tZWFuXzIgPSBtZWFuKHgpKQ0KYGBgDQoNCkFyaXRobWV0aWMgb3BlcmF0b3JzIGArYCwgYC1gLCBgKmAsIGAvYCwgYF5gIGFyZSBub3QgYWZmZWN0ZWQgYnkgYGdyb3VwX2J5KClgLg0KDQpgYGB7cn0NCnRpYmJsZSgNCiAgeCA9IDE6OSwNCiAgZ3JvdXAgPSByZXAoYygiYSIsICJiIiwgImMiKSwgZWFjaCA9IDMpDQopICU+JQ0KICBtdXRhdGUoeSA9IHggKyAyKSAlPiUNCiAgZ3JvdXBfYnkoZ3JvdXApICU+JQ0KICBtdXRhdGUoeiA9IHggKyAyKQ0KYGBgDQoNClRoZSBtb2R1bGFyIGFyaXRobWV0aWMgb3BlcmF0b3JzIGAlLyVgIGFuZCBgJSVgIGFyZSBub3QgYWZmZWN0ZWQgYnkgYGdyb3VwX2J5KClgDQoNCmBgYHtyfQ0KdGliYmxlKA0KICB4ID0gMTo5LA0KICBncm91cCA9IHJlcChjKCJhIiwgImIiLCAiYyIpLCBlYWNoID0gMykNCikgJT4lDQogIG11dGF0ZSh5ID0geCAlJSAyKSAlPiUNCiAgZ3JvdXBfYnkoZ3JvdXApICU+JQ0KICBtdXRhdGUoeiA9IHggJSUgMikNCmBgYA0KDQpUaGUgbG9nYXJpdGhtaWMgZnVuY3Rpb25zIGBsb2coKWAsIGBsb2cyKClgLCBhbmQgYGxvZzEwKClgIGFyZSBub3QgYWZmZWN0ZWQgYnkgYGdyb3VwX2J5KClgLg0KDQpgYGB7cn0NCnRpYmJsZSgNCiAgeCA9IDE6OSwNCiAgZ3JvdXAgPSByZXAoYygiYSIsICJiIiwgImMiKSwgZWFjaCA9IDMpDQopICU+JQ0KICBtdXRhdGUoeSA9IGxvZyh4KSkgJT4lDQogIGdyb3VwX2J5KGdyb3VwKSAlPiUNCiAgbXV0YXRlKHogPSBsb2coeCkpDQpgYGANCg0KDQpUaGUgb2Zmc2V0IGZ1bmN0aW9ucyBgbGVhZCgpYCBhbmQgYGxhZygpYCByZXNwZWN0IHRoZSBncm91cGluZ3MgaW4gYGdyb3VwX2J5KClgLiBUaGUgZnVuY3Rpb25zIGBsYWcoKWAgYW5kIGBsZWFkKClgIHdpbGwgb25seSByZXR1cm4gdmFsdWVzIHdpdGhpbiBlYWNoIGdyb3VwLg0KDQpgYGB7cn0NCnRpYmJsZSgNCiAgeCA9IDE6OSwNCiAgZ3JvdXAgPSByZXAoYygiYSIsICJiIiwgImMiKSwgZWFjaCA9IDMpDQopICU+JQ0KICBncm91cF9ieShncm91cCkgJT4lDQogIG11dGF0ZSgNCiAgICBsYWdfeCA9IGxhZyh4KSwNCiAgICBsZWFkX3ggPSBsZWFkKHgpDQogICkNCmBgYA0KDQpUaGUgY3VtdWxhdGl2ZSBhbmQgcm9sbGluZyBhZ2dyZWdhdGUgZnVuY3Rpb25zIGBjdW1zdW0oKWAsIGBjdW1wcm9kKClgLCBgY3VtbWluKClgLCBgY3VtbWF4KClgLCBhbmQgYGN1bW1lYW4oKWAgY2FsY3VsYXRlIHZhbHVlcyB3aXRoaW4gZWFjaCBncm91cC4NCg0KYGBge3J9DQp0aWJibGUoDQogIHggPSAxOjksDQogIGdyb3VwID0gcmVwKGMoImEiLCAiYiIsICJjIiksIGVhY2ggPSAzKQ0KKSAlPiUNCiAgbXV0YXRlKHhfY3Vtc3VtID0gY3Vtc3VtKHgpKSAlPiUNCiAgZ3JvdXBfYnkoZ3JvdXApICU+JQ0KICBtdXRhdGUoeF9jdW1zdW1fMiA9IGN1bXN1bSh4KSkNCmBgYA0KDQpMb2dpY2FsIGNvbXBhcmlzb25zLCBgPGAsIGA8PWAsIGA+YCwgYD49YCwgYCE9YCwgYW5kIGA9PWAgYXJlIG5vdCBhZmZlY3RlZCBieSBgZ3JvdXBfYnkoKWAuDQoNCmBgYHtyfQ0KdGliYmxlKA0KICB4ID0gMTo5LA0KICB5ID0gOToxLA0KICBncm91cCA9IHJlcChjKCJhIiwgImIiLCAiYyIpLCBlYWNoID0gMykNCikgJT4lDQogIG11dGF0ZSh4X2x0ZV95ID0geCA8PSB5KSAlPiUNCiAgZ3JvdXBfYnkoZ3JvdXApICU+JQ0KICBtdXRhdGUoeF9sdGVfeV8yID0geCA8PSB5KQ0KYGBgDQoNClJhbmtpbmcgZnVuY3Rpb25zIGxpa2UgYG1pbl9yYW5rKClgIHdvcmsgd2l0aGluIGVhY2ggZ3JvdXAgd2hlbiB1c2VkIHdpdGggYGdyb3VwX2J5KClgLg0KDQpgYGB7cn0NCnRpYmJsZSgNCiAgeCA9IDE6OSwNCiAgZ3JvdXAgPSByZXAoYygiYSIsICJiIiwgImMiKSwgZWFjaCA9IDMpDQopICU+JQ0KICBtdXRhdGUocm5rID0gbWluX3JhbmsoeCkpICU+JQ0KICBncm91cF9ieShncm91cCkgJT4lDQogIG11dGF0ZShybmsyID0gbWluX3JhbmsoeCkpDQpgYGANCg0KVGhvdWdoIG5vdCBhc2tlZCBpbiB0aGUgcXVlc3Rpb24sIG5vdGUgdGhhdCBgYXJyYW5nZSgpYCBpZ25vcmVzIGdyb3VwcyB3aGVuIHNvcnRpbmcgdmFsdWVzLg0KDQpgYGB7cn0NCnRpYmJsZSgNCiAgeCA9IHJ1bmlmKDkpLA0KICBncm91cCA9IHJlcChjKCJhIiwgImIiLCAiYyIpLCBlYWNoID0gMykNCikgJT4lDQogIGdyb3VwX2J5KGdyb3VwKSAlPiUNCiAgYXJyYW5nZSh4KQ0KYGBgDQoNCkhvd2V2ZXIsIHRoZSBvcmRlciBvZiB2YWx1ZXMgZnJvbSBgYXJyYW5nZSgpYCBjYW4gaW50ZXJhY3Qgd2l0aCBncm91cHMgd2hlbiB1c2VkIHdpdGggZnVuY3Rpb25zIHRoYXQgcmVseSBvbiB0aGUgb3JkZXJpbmcgb2YgZWxlbWVudHMsIHN1Y2ggYXMgYGxlYWQoKWAsIGBsYWcoKWAsIG9yIGBjdW1zdW0oKWAuDQoNCmBgYHtyfQ0KdGliYmxlKA0KICBncm91cCA9IHJlcChjKCJhIiwgImIiLCAiYyIpLCBlYWNoID0gMyksDQogIHggPSBydW5pZig5KQ0KKSAlPiUNCiAgZ3JvdXBfYnkoZ3JvdXApICU+JQ0KICBhcnJhbmdlKHgpICU+JQ0KICBtdXRhdGUobGFnX3ggPSBsYWcoeCkpDQpgYGANCg0KIyMjIDIuIFdoaWNoIHBsYW5lICh0YWlsbnVtKSBoYXMgdGhlIHdvcnN0IG9uLXRpbWUgcmVjb3JkPw0KDQpUaGUgcXVlc3Rpb24gZG9lcyBub3QgZGVmaW5lIGEgd2F5IHRvIG1lYXN1cmUgb24tdGltZSByZWNvcmQsIHNvIEkgd2lsbCBjb25zaWRlciB0d28gbWV0cmljczoNCg0KMS4gcHJvcG9ydGlvbiBvZiBmbGlnaHRzIG5vdCBkZWxheWVkIG9yIGNhbmNlbGxlZCwgYW5kDQoyLiBtZWFuIGFycml2YWwgZGVsYXkuDQoNClRoZSBmaXJzdCBtZXRyaWMgaXMgdGhlIHByb3BvcnRpb24gb2Ygbm90LWNhbmNlbGxlZCBhbmQgb24tdGltZSBmbGlnaHRzLiBJIHVzZSB0aGUgcHJlc2VuY2Ugb2YgYW4gYXJyaXZhbCB0aW1lIHRvIG1lYW4gdGhhdCBhIGZsaWdodCB3YXMgbm90IGNhbmNlbGxlZC4gSG93ZXZlciwgdGhlcmUgYXJlIG1hbnkgcGxhbmVzIHRoYXQgaGF2ZSBuZXZlciBmbG93biBhbiBvbi10aW1lIGZsaWdodC4gTWFueSBvZiB0aGUgcGxhbmVzIHRoYXQgaGF2ZSB0aGUgbG93ZXN0IHByb3BvcnRpb24gaGF2ZSBvbmx5IGZsb3duIGEgc21hbGwgbnVtYmVyIG9mIGZsaWdodHMuDQoNCmBgYHtyfQ0KZmxpZ2h0cyAlPiUNCiAgZmlsdGVyKCFpcy5uYSh0YWlsbnVtKSkgJT4lDQogIG11dGF0ZShvbl90aW1lID0gIWlzLm5hKGFycl90aW1lKSAmIChhcnJfZGVsYXkgPD0gMCkpICU+JQ0KICBncm91cF9ieSh0YWlsbnVtKSAlPiUNCiAgc3VtbWFyaXNlKG9uX3RpbWUgPSBtZWFuKG9uX3RpbWUpLCBuID0gbigpKSAlPiUNCiAgZmlsdGVyKG1pbl9yYW5rKG9uX3RpbWUpID09IDEpDQpgYGANCg0KU28sIEkgd2lsbCByZW1vdmUgcGxhbmVzIHRoYXQgZmxldyBhdCBsZWFzdCAyMCBmbGlnaHRzLiBUaGUgY2hvaWNlIG9mIDIwIHdhcyBjaG9zZW4gYmVjYXVzZSBpdCByb3VuZCBudW1iZXIgbmVhciB0aGUgZmlyc3QgcXVhcnRpbGUgb2YgdGhlIG51bWJlciBvZiBmbGlnaHRzIGJ5IHBsYW5lLg0KDQpgYGB7cn0NCnF1YW50aWxlKGNvdW50KGZsaWdodHMsIHRhaWxudW0pJG4pDQpgYGANCg0KVGhlIHBsYW5lIHdoaWNoIGZldyBhdCBsZWFzdCAyMCBmbGlnaHRzIHdpdGggdGhlIHdvcnN0IG9uIHRpbWUgcmVjb3JkIGlzOg0KDQpgYGB7cn0NCmZsaWdodHMgJT4lDQogIGZpbHRlcighaXMubmEodGFpbG51bSkpICU+JQ0KICBtdXRhdGUob25fdGltZSA9ICFpcy5uYShhcnJfdGltZSkgJiAoYXJyX2RlbGF5IDw9IDApKSAlPiUNCiAgZ3JvdXBfYnkodGFpbG51bSkgJT4lDQogIHN1bW1hcmlzZShvbl90aW1lID0gbWVhbihvbl90aW1lKSwgbiA9IG4oKSkgJT4lDQogIGZpbHRlcihuID49IDIwKSAlPiUNCiAgZmlsdGVyKG1pbl9yYW5rKG9uX3RpbWUpID09IDEpDQpgYGANCg0KVGhlIHNlY29uZCBtZXRyaWMgaXMgdGhlIG1lYW4gbWludXRlcyBkZWxheWVkLiBBcyB3aXRoIHRoZSBwcmV2aW91cyBtZXRyaWMsIEkgd2lsbCBvbmx5IGNvbnNpZGVyIHBsYW5lcyB3aGljaCBmbGV3IGxlYXN0IDIwIGZsaWdodHMuIEEgZGlmZmVyZW50IHBsYW5lIGhhcyB0aGUgd29yc3Qgb24tdGltZSByZWNvcmQgd2hlbiBtZWFzdXJlZCBhcyBhdmVyYWdlIG1pbnV0ZXMgZGVsYXllZC4NCg0KYGBge3J9DQpmbGlnaHRzICU+JQ0KICBncm91cF9ieSh0YWlsbnVtKSAlPiUNCiAgc3VtbWFyaXNlKGFycl9kZWxheSA9IG1lYW4oYXJyX2RlbGF5KSwgbiA9IG4oKSkgJT4lDQogIGZpbHRlcihuID49IDIwKSAlPiUNCiAgZmlsdGVyKG1pbl9yYW5rKGRlc2MoYXJyX2RlbGF5KSkgPT0gMSkNCmBgYA0KDQojIyMgMy4gV2hhdCB0aW1lIG9mIGRheSBzaG91bGQgeW91IGZseSBpZiB5b3Ugd2FudCB0byBhdm9pZCBkZWxheXMgYXMgbXVjaCBhcyBwb3NzaWJsZT8NCg0KTGV04oCZcyBncm91cCBieSB0aGUgaG91ciBvZiB0aGUgZmxpZ2h0LiBUaGUgZWFybGllciB0aGUgZmxpZ2h0IGlzIHNjaGVkdWxlZCwgdGhlIGxvd2VyIGl0cyBleHBlY3RlZCBkZWxheS4gVGhpcyBpcyBpbnR1aXRpdmUgYXMgZGVsYXlzIHdpbGwgYWZmZWN0IGxhdGVyIGZsaWdodHMuIE1vcm5pbmcgZmxpZ2h0cyBoYXZlIGZld2VyIChpZiBhbnkpIHByZXZpb3VzIGZsaWdodHMgdGhhdCBjYW4gZGVsYXkgdGhlbS4NCg0KYGBge3J9DQpmbGlnaHRzICU+JQ0KICBncm91cF9ieShob3VyKSAlPiUNCiAgc3VtbWFyaXNlKGFycl9kZWxheSA9IG1lYW4oYXJyX2RlbGF5LCBuYS5ybSA9IFRSVUUpKSAlPiUNCiAgYXJyYW5nZShhcnJfZGVsYXkpDQpgYGANCg0KIyMjIDQuIEZvciBlYWNoIGRlc3RpbmF0aW9uLCBjb21wdXRlIHRoZSB0b3RhbCBtaW51dGVzIG9mIGRlbGF5LiBGb3IgZWFjaCBmbGlnaHQsIGNvbXB1dGUgdGhlIHByb3BvcnRpb24gb2YgdGhlIHRvdGFsIGRlbGF5IGZvciBpdHMgZGVzdGluYXRpb24uDQoNClRoZSBrZXkgdG8gYW5zd2VyaW5nIHRoaXMgcXVlc3Rpb24gaXMgdG8gb25seSBpbmNsdWRlIGRlbGF5ZWQgZmxpZ2h0cyB3aGVuIGNhbGN1bGF0aW5nIHRoZSB0b3RhbCBkZWxheSBhbmQgcHJvcG9ydGlvbiBvZiBkZWxheS4NCg0KYGBge3J9DQpmbGlnaHRzICU+JQ0KICBmaWx0ZXIoYXJyX2RlbGF5ID4gMCkgJT4lDQogIGdyb3VwX2J5KGRlc3QpICU+JQ0KICBtdXRhdGUoDQogICAgYXJyX2RlbGF5X3RvdGFsID0gc3VtKGFycl9kZWxheSksDQogICAgYXJyX2RlbGF5X3Byb3AgPSBhcnJfZGVsYXkgLyBhcnJfZGVsYXlfdG90YWwNCiAgKSAlPiUNCiAgc2VsZWN0KA0KICAgIGRlc3QsIG1vbnRoLCBkYXksIGRlcF90aW1lLCBjYXJyaWVyLCBmbGlnaHQsDQogICAgYXJyX2RlbGF5LCBhcnJfZGVsYXlfcHJvcA0KICApICU+JQ0KICBhcnJhbmdlKGRlc3QsIGRlc2MoYXJyX2RlbGF5X3Byb3ApKQ0KYGBgDQoNClRoZXJlIGlzIHNvbWUgYW1iaWd1aXR5IGluIHRoZSBtZWFuaW5nIG9mICpmbGlnaHRzKiBpbiB0aGUgcXVlc3Rpb24uIFRoZSBmaXJzdCBleGFtcGxlIGRlZmluZWQgYSBmbGlnaHQgYXMgYSByb3cgaW4gdGhlIGBmbGlnaHRzYCB0YWJsZSwgYSBwYXJ0aWN1bGFyIHRyaXAgYnkgYWlyY3JhZnQgZnJvbSBhIHBhcnRpY3VsYXIgYWlycG9ydC4gSG93ZXZlciwgZmxpZ2h0IGNvdWxkIGFsc28gcmVmZXIgdG8gdGhlIFtmbGlnaHQgbnVtYmVyXShodHRwczovL2VuLndpa2lwZWRpYS5vcmcvd2lraS9GbGlnaHRfbnVtYmVyKSwgd2hpY2ggaXMgdGhlIGNvZGUgYSBjYXJyaWVyIHVzZXMgZm9yIGFuIGFpcmxpbmUgc2VydmljZSBvZiBhIHJvdXRlLiBGb3IgZXhhbXBsZSwgQUExIGlzIHRoZSBmbGlnaHQgbnVtYmVyIG9mIHRoZSAwOTowMCBBbWVyaWNhbiBBaXJsaW5lcyBmbGlnaHQgYmV0d2VlbiBKRksgYW5kIExBWC4gVGhlIGZsaWdodCBudW1iZXIgaXMgY29udGFpbmVkIGBmbGlnaHRzJGZsaWdodGAsIHRob3VnaCB3aGF0IGlzIGNhbGxlZCBhIOKAnGZsaWdodOKAnSBjb21iaW5hdGlvbiBvZiB0aGUgYGZsaWdodHMkY2FycmllcmAgYW5kIGBmbGlnaHRzJGZsaWdodGAuDQoNCmBgYHtyfQ0KZmxpZ2h0cyAlPiUNCiAgZmlsdGVyKGFycl9kZWxheSA+IDApICU+JQ0KICBncm91cF9ieShkZXN0LCBvcmlnaW4sIGNhcnJpZXIsIGZsaWdodCkgJT4lDQogIHN1bW1hcmlzZShhcnJfZGVsYXkgPSBzdW0oYXJyX2RlbGF5KSkgJT4lDQogIGdyb3VwX2J5KGRlc3QpICU+JQ0KICBtdXRhdGUoDQogICAgYXJyX2RlbGF5X3Byb3AgPSBhcnJfZGVsYXkgLyBzdW0oYXJyX2RlbGF5KQ0KICApICU+JQ0KICBhcnJhbmdlKGRlc3QsIGRlc2MoYXJyX2RlbGF5X3Byb3ApKSAlPiUNCiAgc2VsZWN0KGNhcnJpZXIsIGZsaWdodCwgb3JpZ2luLCBkZXN0LCBhcnJfZGVsYXlfcHJvcCkNCmBgYA0KDQojIyMgNS4gRGVsYXlzIGFyZSB0eXBpY2FsbHkgdGVtcG9yYWxseSBjb3JyZWxhdGVkOiBldmVuIG9uY2UgdGhlIHByb2JsZW0gdGhhdCBjYXVzZWQgdGhlIGluaXRpYWwgZGVsYXkgaGFzIGJlZW4gcmVzb2x2ZWQsIGxhdGVyIGZsaWdodHMgYXJlIGRlbGF5ZWQgdG8gYWxsb3cgZWFybGllciBmbGlnaHRzIHRvIGxlYXZlLiBVc2luZyBgbGFnKClgIGV4cGxvcmUgaG93IHRoZSBkZWxheSBvZiBhIGZsaWdodCBpcyByZWxhdGVkIHRvIHRoZSBkZWxheSBvZiB0aGUgaW1tZWRpYXRlbHkgcHJlY2VkaW5nIGZsaWdodC4NCg0KVGhpcyBjYWxjdWxhdGVzIHRoZSBkZXBhcnR1cmUgZGVsYXkgb2YgdGhlIHByZWNlZGluZyBmbGlnaHQgZnJvbSB0aGUgc2FtZSBhaXJwb3J0Lg0KDQpgYGB7cn0NCmxhZ2dlZF9kZWxheXMgPC0gZmxpZ2h0cyAlPiUNCiAgYXJyYW5nZShvcmlnaW4sIG1vbnRoLCBkYXksIGRlcF90aW1lKSAlPiUNCiAgZ3JvdXBfYnkob3JpZ2luKSAlPiUNCiAgbXV0YXRlKGRlcF9kZWxheV9sYWcgPSBsYWcoZGVwX2RlbGF5KSkgJT4lDQogIGZpbHRlcighaXMubmEoZGVwX2RlbGF5KSwgIWlzLm5hKGRlcF9kZWxheV9sYWcpKQ0KYGBgDQoNClRoaXMgcGxvdHMgdGhlIHJlbGF0aW9uc2hpcCBiZXR3ZWVuIHRoZSBtZWFuIGRlbGF5IG9mIGEgZmxpZ2h0IGZvciBhbGwgdmFsdWVzIG9mIHRoZSBwcmV2aW91cyBmbGlnaHQuIEZvciBkZWxheXMgbGVzcyB0aGFuIHR3byBob3VycywgdGhlIHJlbGF0aW9uc2hpcCBiZXR3ZWVuIHRoZSBkZWxheSBvZiB0aGUgcHJlY2VkaW5nIGZsaWdodCBhbmQgdGhlIGN1cnJlbnQgZmxpZ2h0IGlzIG5lYXJseSBhIGxpbmUuIEFmdGVyIHRoYXQgdGhlIHJlbGF0aW9uc2hpcCBiZWNvbWVzIG1vcmUgdmFyaWFibGUsIGFzIGxvbmctZGVsYXllZCBmbGlnaHRzIGFyZSBpbnRlcnNwZXJzZWQgd2l0aCBmbGlnaHRzIGxlYXZpbmcgb24tdGltZS4gQWZ0ZXIgYWJvdXQgOC1ob3VycywgYSBkZWxheWVkIGZsaWdodCBpcyBsaWtlbHkgdG8gYmUgZm9sbG93ZWQgYnkgYSBmbGlnaHQgbGVhdmluZyBvbiB0aW1lLg0KDQpgYGB7cn0NCmxhZ2dlZF9kZWxheXMgJT4lDQogIGdyb3VwX2J5KGRlcF9kZWxheV9sYWcpICU+JQ0KICBzdW1tYXJpc2UoZGVwX2RlbGF5X21lYW4gPSBtZWFuKGRlcF9kZWxheSkpICU+JQ0KICBnZ3Bsb3QoYWVzKHkgPSBkZXBfZGVsYXlfbWVhbiwgeCA9IGRlcF9kZWxheV9sYWcpKSArDQogIGdlb21fcG9pbnQoKSArDQogIHNjYWxlX3hfY29udGludW91cyhicmVha3MgPSBzZXEoMCwgMTUwMCwgYnkgPSAxMjApKSArDQogIGxhYnMoeSA9ICJEZXBhcnR1cmUgRGVsYXkiLCB4ID0gIlByZXZpb3VzIERlcGFydHVyZSBEZWxheSIpDQpgYGANCg0KVGhlIG92ZXJhbGwgcmVsYXRpb25zaGlwIGxvb2tzIHNpbWlsYXIgaW4gYWxsIHRocmVlIG9yaWdpbiBhaXJwb3J0cy4NCg0KYGBge3J9DQpsYWdnZWRfZGVsYXlzICU+JQ0KICBncm91cF9ieShvcmlnaW4sIGRlcF9kZWxheV9sYWcpICU+JQ0KICBzdW1tYXJpc2UoZGVwX2RlbGF5X21lYW4gPSBtZWFuKGRlcF9kZWxheSkpICU+JQ0KICBnZ3Bsb3QoYWVzKHkgPSBkZXBfZGVsYXlfbWVhbiwgeCA9IGRlcF9kZWxheV9sYWcpKSArDQogIGdlb21fcG9pbnQoKSArDQogIGZhY2V0X3dyYXAofm9yaWdpbiwgbmNvbCA9IDEpICsNCiAgbGFicyh5ID0gIkRlcGFydHVyZSBEZWxheSIsIHggPSAiUHJldmlvdXMgRGVwYXJ0dXJlIERlbGF5IikNCmBgYA0KDQoNCiMjIyA2LiBMb29rIGF0IGVhY2ggZGVzdGluYXRpb24uIENhbiB5b3UgZmluZCBmbGlnaHRzIHRoYXQgYXJlIHN1c3BpY2lvdXNseSBmYXN0PyAoaS5lLiBmbGlnaHRzIHRoYXQgcmVwcmVzZW50IGEgcG90ZW50aWFsIGRhdGEgZW50cnkgZXJyb3IpLiBDb21wdXRlIHRoZSBhaXIgdGltZSBvZiBhIGZsaWdodCByZWxhdGl2ZSB0byB0aGUgc2hvcnRlc3QgZmxpZ2h0IHRvIHRoYXQgZGVzdGluYXRpb24uIFdoaWNoIGZsaWdodHMgd2VyZSBtb3N0IGRlbGF5ZWQgaW4gdGhlIGFpcj8NCg0KV2hlbiBjYWxjdWxhdGluZyB0aGlzIGFuc3dlciB3ZSBzaG91bGQgb25seSBjb21wYXJlIGZsaWdodHMgd2l0aGluIHRoZSBzYW1lIChvcmlnaW4sIGRlc3RpbmF0aW9uKSBwYWlyLg0KDQpUbyBmaW5kIHVudXN1YWwgb2JzZXJ2YXRpb25zLCB3ZSBuZWVkIHRvIGZpcnN0IHB1dCB0aGVtIG9uIHRoZSBzYW1lIHNjYWxlLiBJIHdpbGwgW3N0YW5kYXJkaXplXShodHRwczovL2VuLndpa2lwZWRpYS5vcmcvd2lraS9TdGFuZGFyZF9zY29yZSkgdmFsdWVzIGJ5IHN1YnRyYWN0aW5nIHRoZSBtZWFuIGZyb20gZWFjaCBhbmQgdGhlbiBkaXZpZGluZyBlYWNoIGJ5IHRoZSBzdGFuZGFyZCBkZXZpYXRpb24uDQoNCiR6ID0gXGZyYWN7eCAtIFxtdX17XHNpZ21hfSQNCiANCkEgc3RhbmRhcmRpemVkIHZhcmlhYmxlIGlzIG9mdGVuIGNhbGxlZCBhIHotc2NvcmUuIFRoZSB1bml0cyBvZiB0aGUgc3RhbmRhcmRpemVkIHZhcmlhYmxlIGFyZSBzdGFuZGFyZCBkZXZpYXRpb25zIGZyb20gdGhlIG1lYW4uIFRoaXMgd2lsbCBwdXQgdGhlIGZsaWdodCB0aW1lcyBmcm9tIGRpZmZlcmVudCByb3V0ZXMgb24gdGhlIHNhbWUgc2NhbGUuIFRoZSBsYXJnZXIgdGhlIG1hZ25pdHVkZSBvZiB0aGUgc3RhbmRhcmRpemVkIHZhcmlhYmxlIGZvciBhbiBvYnNlcnZhdGlvbiwgdGhlIG1vcmUgdW51c3VhbCB0aGUgb2JzZXJ2YXRpb24gaXMuIEZsaWdodHMgd2l0aCBuZWdhdGl2ZSB2YWx1ZXMgb2YgdGhlIHN0YW5kYXJkaXplZCB2YXJpYWJsZSBhcmUgZmFzdGVyIHRoYW4gdGhlIG1lYW4gZmxpZ2h0IGZvciB0aGF0IHJvdXRlLCB3aGlsZSB0aG9zZSB3aXRoIHBvc2l0aXZlIHZhbHVlcyBhcmUgc2xvd2VyIHRoYW4gdGhlIG1lYW4gZmxpZ2h0IGZvciB0aGF0IHJvdXRlLg0KDQpgYGB7cn0NCnN0YW5kYXJkaXplZF9mbGlnaHRzIDwtIGZsaWdodHMgJT4lDQogIGZpbHRlcighaXMubmEoYWlyX3RpbWUpKSAlPiUNCiAgZ3JvdXBfYnkoZGVzdCwgb3JpZ2luKSAlPiUNCiAgbXV0YXRlKA0KICAgIGFpcl90aW1lX21lYW4gPSBtZWFuKGFpcl90aW1lKSwNCiAgICBhaXJfdGltZV9zZCA9IHNkKGFpcl90aW1lKSwNCiAgICBuID0gbigpDQogICkgJT4lDQogIHVuZ3JvdXAoKSAlPiUNCiAgbXV0YXRlKGFpcl90aW1lX3N0YW5kYXJkID0gKGFpcl90aW1lIC0gYWlyX3RpbWVfbWVhbikgLyAoYWlyX3RpbWVfc2QgKyAxKSkNCmBgYA0KDQpJIGFkZCAxIHRvIHRoZSBkZW5vbWluYXRvciBhbmQgbnVtZXJhdG9yIHRvIGF2b2lkIGRpdmlkaW5nIGJ5IHplcm8uIE5vdGUgdGhhdCB0aGUgYHVuZ3JvdXAoKWAgaGVyZSBpcyBub3QgbmVjZXNzYXJ5LiBIb3dldmVyLCBJIHdpbGwgYmUgdXNpbmcgdGhpcyBkYXRhIGZyYW1lIGxhdGVyLiBUaHJvdWdoIGV4cGVyaWVuY2UsIEkgaGF2ZSBmb3VuZCB0aGF0IEkgaGF2ZSBmZXdlciBidWdzIHdoZW4gSSBrZWVwIGEgZGF0YSBmcmFtZSBncm91cGVkIGZvciBvbmx5IHRob3NlIHZlcmJzIHRoYXQgbmVlZCBpdC4gSWYgSSBkaWQgbm90IGB1bmdyb3VwKClgIHRoaXMgZGF0YSBmcmFtZSwgdGhlIGBhcnJhbmdlKClgIHVzZWQgbGF0ZXIgd291bGQgbm90IHdvcmsgYXMgZXhwZWN0ZWQuIEl0IGlzIGJldHRlciB0byBlcnIgb24gdGhlIHNpZGUgb2YgdXNpbmcgYHVuZ3JvdXAoKWAgd2hlbiB1bm5lY2Vzc2FyeS4NCg0KVGhlIGRpc3RyaWJ1dGlvbiBvZiB0aGUgc3RhbmRhcmRpemVkIGFpciBmbGlnaHRzIGhhcyBsb25nIHJpZ2h0IHRhaWwuDQoNCmBgYHtyfQ0KZ2dwbG90KHN0YW5kYXJkaXplZF9mbGlnaHRzLCBhZXMoeCA9IGFpcl90aW1lX3N0YW5kYXJkKSkgKw0KICBnZW9tX2RlbnNpdHkoKQ0KYGBgDQoNClVudXN1YWxseSBmYXN0IGZsaWdodHMgYXJlIHRob3NlIGZsaWdodHMgd2l0aCB0aGUgc21hbGxlc3Qgc3RhbmRhcmRpemVkIHZhbHVlcy4NCg0KYGBge3J9DQpzdGFuZGFyZGl6ZWRfZmxpZ2h0cyAlPiUNCiAgYXJyYW5nZShhaXJfdGltZV9zdGFuZGFyZCkgJT4lDQogIHNlbGVjdCgNCiAgICBjYXJyaWVyLCBmbGlnaHQsIG9yaWdpbiwgZGVzdCwgbW9udGgsIGRheSwNCiAgICBhaXJfdGltZSwgYWlyX3RpbWVfbWVhbiwgYWlyX3RpbWVfc3RhbmRhcmQNCiAgKSAlPiUNCiAgaGVhZCgxMCkgJT4lDQogIHByaW50KHdpZHRoID0gSW5mKQ0KYGBgDQoNCkkgdXNlZCB3aWR0aCA9IEluZiB0byBlbnN1cmUgdGhhdCBhbGwgY29sdW1ucyB3aWxsIGJlIHByaW50ZWQuDQoNClRoZSBmYXN0ZXN0IGZsaWdodCBpcyBETDE0OTkgZnJvbSBMR0EgdG8gQVRMIHdoaWNoIGRlcGFydGVkIG9uIDIwMTMtMDUtMjUgYXQgMTc6MDkuIEl0IGhhcyBhbiBhaXIgdGltZSBvZiA2NSBtaW51dGVzLCBjb21wYXJlZCB0byBhbiBhdmVyYWdlIGZsaWdodCB0aW1lIG9mIDExNCBtaW51dGVzIGZvciBpdHMgcm91dGUuIFRoaXMgaXMgNC42IHN0YW5kYXJkIGRldmlhdGlvbnMgYmVsb3cgdGhlIGF2ZXJhZ2UgZmxpZ2h0IG9uIGl0cyByb3V0ZS4NCg0KSXQgaXMgaW1wb3J0YW50IHRvIG5vdGUgdGhhdCB0aGlzIGRvZXMgbm90IG5lY2Vzc2FyaWx5IGltcGx5IHRoYXQgdGhlcmUgd2FzIGEgZGF0YSBlbnRyeSBlcnJvci4gV2Ugc2hvdWxkIGNoZWNrIHRoZXNlIGZsaWdodHMgdG8gc2VlIHdoZXRoZXIgdGhlcmUgd2FzIHNvbWUgcmVhc29uIGZvciB0aGUgZGlmZmVyZW5jZS4gSXQgbWF5IGJlIHRoYXQgd2UgYXJlIG1pc3Npbmcgc29tZSBwaWVjZSBvZiBpbmZvcm1hdGlvbiB0aGF0IGV4cGxhaW5zIHRoZXNlIHVudXN1YWwgdGltZXMuDQoNCkEgcG90ZW50aWFsIGlzc3VlIHdpdGggdGhlIHdheSB0aGF0IHdlIHN0YW5kYXJkaXplZCB0aGUgZmxpZ2h0cyBpcyB0aGF0IHRoZSBtZWFuIGFuZCBzdGFuZGFyZCBkZXZpYXRpb24gdXNlZCB0byBjYWxjdWxhdGUgYXJlIHNlbnNpdGl2ZSB0byBvdXRsaWVycyBhbmQgb3V0bGllcnMgaXMgd2hhdCB3ZSBhcmUgbG9va2luZyBmb3IuIEluc3RlYWQgb2Ygc3RhbmRhcmRpemluZyB2YXJpYWJsZXMgd2l0aCB0aGUgbWVhbiBhbmQgdmFyaWFuY2UsIHdlIGNvdWxkIHVzZSB0aGUgbWVkaWFuIGFzIGEgbWVhc3VyZSBvZiBjZW50cmFsIHRlbmRlbmN5IGFuZCB0aGUgaW50ZXJxdWFydGlsZSByYW5nZSAoSVFSKSBhcyBhIG1lYXN1cmUgb2Ygc3ByZWFkLiBUaGUgbWVkaWFuIGFuZCBJUVIgYXJlIG1vcmUgW3Jlc2lzdGFudCB0byBvdXRsaWVyc10oaHR0cHM6Ly9lbi53aWtpcGVkaWEub3JnL3dpa2kvUm9idXN0X3N0YXRpc3RpY3MpIHRoYW4gdGhlIG1lYW4gYW5kIHN0YW5kYXJkIGRldmlhdGlvbi4gVGhlIGZvbGxvd2luZyBtZXRob2QgdXNlcyB0aGUgbWVkaWFuIGFuZCBpbnRlci1xdWFydGlsZSByYW5nZSwgd2hpY2ggYXJlIGxlc3Mgc2Vuc2l0aXZlIHRvIG91dGxpZXJzLg0KDQpgYGB7cn0NCnN0YW5kYXJkaXplZF9mbGlnaHRzMiA8LSBmbGlnaHRzICU+JQ0KICBmaWx0ZXIoIWlzLm5hKGFpcl90aW1lKSkgJT4lDQogIGdyb3VwX2J5KGRlc3QsIG9yaWdpbikgJT4lDQogIG11dGF0ZSgNCiAgICBhaXJfdGltZV9tZWRpYW4gPSBtZWRpYW4oYWlyX3RpbWUpLA0KICAgIGFpcl90aW1lX2lxciA9IElRUihhaXJfdGltZSksDQogICAgbiA9IG4oKSwNCiAgICBhaXJfdGltZV9zdGFuZGFyZCA9IChhaXJfdGltZSAtIGFpcl90aW1lX21lZGlhbikgLyBhaXJfdGltZV9pcXINCiAgKQ0KYGBgDQoNClRoZSBkaXN0cmlidXRpb24gb2YgdGhlIHN0YW5kYXJkaXplZCBhaXIgZmxpZ2h0cyB1c2luZyB0aGlzIG5ldyBkZWZpbml0aW9uIGFsc28gaGFzIGxvbmcgcmlnaHQgdGFpbCBvZiBzbG93IGZsaWdodHMuDQoNCmBgYHtyfQ0KZ2dwbG90KHN0YW5kYXJkaXplZF9mbGlnaHRzMiwgYWVzKHggPSBhaXJfdGltZV9zdGFuZGFyZCkpICsNCiAgZ2VvbV9kZW5zaXR5KCkNCmBgYA0KDQpVbnVzdWFsbHkgZmFzdCBmbGlnaHRzIGFyZSB0aG9zZSBmbGlnaHRzIHdpdGggdGhlIHNtYWxsZXN0IHN0YW5kYXJkaXplZCB2YWx1ZXMuDQoNCmBgYHtyfQ0Kc3RhbmRhcmRpemVkX2ZsaWdodHMyICU+JQ0KICBhcnJhbmdlKGFpcl90aW1lX3N0YW5kYXJkKSAlPiUNCiAgc2VsZWN0KA0KICAgIGNhcnJpZXIsIGZsaWdodCwgb3JpZ2luLCBkZXN0LCBtb250aCwgZGF5LCBhaXJfdGltZSwNCiAgICBhaXJfdGltZV9tZWRpYW4sIGFpcl90aW1lX3N0YW5kYXJkDQogICkgJT4lDQogIGhlYWQoMTApICU+JQ0KICBwcmludCh3aWR0aCA9IEluZikNCmBgYA0KDQpBbGwgb2YgdGhlc2UgYW5zd2VycyBoYXZlIHJlbGllZCBvbmx5IG9uIHVzaW5nIGEgZGlzdHJpYnV0aW9uIG9mIGNvbXBhcmFibGUgb2JzZXJ2YXRpb25zIHRvIGZpbmQgdW51c3VhbCBvYnNlcnZhdGlvbnMuIEluIHRoaXMgY2FzZSwgdGhlIGNvbXBhcmFibGUgb2JzZXJ2YXRpb25zIHdlcmUgZmxpZ2h0cyBmcm9tIHRoZSBzYW1lIG9yaWdpbiB0byB0aGUgc2FtZSBkZXN0aW5hdGlvbi4gQXBhcnQgZnJvbSBvdXIga25vd2xlZGdlIHRoYXQgZmxpZ2h0cyBmcm9tIHRoZSBzYW1lIG9yaWdpbiB0byB0aGUgc2FtZSBkZXN0aW5hdGlvbiBzaG91bGQgaGF2ZSBzaW1pbGFyIGFpciB0aW1lcywgd2UgaGF2ZSBub3QgdXNlZCBhbnkgb3RoZXIgZG9tYWluLXNwZWNpZmljIGtub3dsZWRnZS4gQnV0IHdlIGtub3cgbXVjaCBtb3JlIGFib3V0IHRoaXMgcHJvYmxlbS4gVGhlIG1vc3Qgb2J2aW91cyBwaWVjZSBvZiBrbm93bGVkZ2Ugd2UgaGF2ZSBpcyB0aGF0IHdlIGtub3cgdGhhdCBmbGlnaHRzIGNhbm5vdCB0cmF2ZWwgYmFjayBpbiB0aW1lLCBzbyB0aGVyZSBzaG91bGQgbmV2ZXIgYmUgYSBmbGlnaHQgd2l0aCBhIG5lZ2F0aXZlIGFpcnRpbWUuIEJ1dCB3ZSBhbHNvIGtub3cgdGhhdCBhaXJjcmFmdCBoYXZlIG1heGltdW0gc3BlZWRzLiBXaGlsZSBkaWZmZXJlbnQgYWlyY3JhZnQgaGF2ZSBkaWZmZXJlbnQgW2NydWlzaW5nIHNwZWVkc10oaHR0cHM6Ly9lbi53aWtpcGVkaWEub3JnL3dpa2kvQ3J1aXNlXyhhZXJvbmF1dGljcykpLCBjb21tZXJjaWFsIGFpcmxpbmVycyB0eXBpY2FsbHkgY3J1aXNlIGF0IGFpciBzcGVlZHMgYXJvdW5kIDU0N+KAkzU3NSBtcGguIENhbGN1bGF0aW5nIHRoZSBncm91bmQgc3BlZWQgb2YgYWlyY3JhZnQgaXMgY29tcGxpY2F0ZWQgYnkgdGhlIHdheSBpbiB3aGljaCB3aW5kcywgZXNwZWNpYWxseSB0aGUgaW5mbHVlbmNlIG9mIHdpbmQsIGVzcGVjaWFsbHkgamV0IHN0cmVhbXMsIG9uIHRoZSBncm91bmQtc3BlZWQgb2YgZmxpZ2h0cy4gQSBzdHJvbmcgdGFpbHdpbmQgY2FuIGluY3JlYXNlIGdyb3VuZC1zcGVlZCBvZiB0aGUgYWlyY3JhZnQgYnkgWzIwMCBtcGhdKGh0dHBzOi8vd3d3LndpcmVkLmNvbS9zdG9yeS9ub3J3ZWdpYW4tYWlyLXRyYW5zYXRsYW50aWMtc3BlZWQtcmVjb3JkLykuIEFwYXJ0IGZyb20gdGhlIHJldGlyZWQgW0NvbmNvcmRlXShodHRwczovL2VuLndpa2lwZWRpYS5vcmcvd2lraS9Db25jb3JkZSkuIEZvciBleGFtcGxlLCBpbiAyMDE4LCBbYSB0cmFuc2F0bGFudGljIGZsaWdodF0oaHR0cHM6Ly93d3cud2lyZWQuY29tL3N0b3J5L25vcndlZ2lhbi1haXItdHJhbnNhdGxhbnRpYy1zcGVlZC1yZWNvcmQvKSB0cmF2ZWxlZCBhdCA3NzAgbXBoIGR1ZSB0byBhIHN0cm9uZyBqZXQgc3RyZWFtIHRhaWx3aW5kLiBUaGlzIG1lYW5zIHRoYXQgYW55IGZsaWdodCB0cmF2ZWxpbmcgYXQgc3BlZWRzIGdyZWF0ZXIgdGhhbiA4MDAgbXBoIGlzIGltcGxhdXNpYmxlLCBhbmQgaXQgbWF5IGJlIHdvcnRoIGNoZWNraW5nIGZsaWdodHMgdHJhdmVsaW5nIGF0IGdyZWF0ZXIgdGhhbiA2MDAgb3IgNzAwIG1waC4gR3JvdW5kIHNwZWVkIGNvdWxkIGFsc28gYmUgdXNlZCB0byBpZGVudGlmeSBhaXJjcmFmdCBmbHlpbmcgaW1wbGF1c2libHkgc2xvdy4gSm9pbmluZyBmbGlnaHRzIGRhdGEgd2l0aCB0aGUgYWlyIGNyYWZ0IHR5cGUgaW4gdGhlIHBsYW5lcyB0YWJsZSBhbmQgZ2V0dGluZyBpbmZvcm1hdGlvbiBhYm91dCB0eXBpY2FsIG9yIHRvcCBzcGVlZHMgb2YgdGhvc2UgYWlyY3JhZnQgY291bGQgcHJvdmlkZSBhIG1vcmUgZGV0YWlsZWQgd2F5IHRvIGlkZW50aWZ5IGltcGxhdXNpYmx5IGZhc3Qgb3Igc2xvdyBmbGlnaHRzLiBBZGRpdGlvbmFsIGRhdGEgb24gaGlnaCBhbHRpdHVkZSB3aW5kIHNwZWVkcyBhdCB0aGUgdGltZSBvZiB0aGUgZmxpZ2h0IHdvdWxkIGZ1cnRoZXIgaGVscC4NCg0KS25vd2luZyB0aGUgc3Vic3RhbmNlIG9mIHRoZSBkYXRhIGFuYWx5c2lzIGF0IGhhbmQgaXMgb25lIG9mIHRoZSBtb3N0IGltcG9ydGFudCB0b29scyBvZiBhIGRhdGEgc2NpZW50aXN0LiBUaGUgdG9vbHMgb2Ygc3RhdGlzdGljcyBhcmUgYSBjb21wbGVtZW50LCBub3QgYSBzdWJzdGl0dXRlLCBmb3IgdGhhdCBrbm93bGVkZ2UuDQoNCldpdGggdGhhdCBpbiBtaW5kLCBMZXTigJlzIHBsb3QgdGhlIGRpc3RyaWJ1dGlvbiBvZiB0aGUgZ3JvdW5kIHNwZWVkIG9mIGZsaWdodHMuIFRoZSBtb2RhbCBmbGlnaHQgaW4gdGhpcyBkYXRhIGhhcyBhIGdyb3VuZCBzcGVlZCBvZiBiZXR3ZWVuIDQwMCBhbmQgNTAwIG1waC4gVGhlIGRpc3RyaWJ1dGlvbiBvZiBncm91bmQgc3BlZWRzIGhhcyBhIGxhcmdlIGxlZnQgdGFpbCBvZiBzbG93ZXIgZmxpZ2h0cyBiZWxvdyA0MDAgbXBoIGNvbnN0aXR1dGluZyB0aGUgbWFqb3JpdHkuIFRoZXJlIGFyZSB2ZXJ5IGZldyBmbGlnaHRzIHdpdGggYSBncm91bmQgc3BlZWQgb3ZlciA1MDAgbXBoLg0KDQpgYGB7cn0NCmZsaWdodHMgJT4lDQogIG11dGF0ZShtcGggPSBkaXN0YW5jZSAvIChhaXJfdGltZSAvIDYwKSkgJT4lDQogIGdncGxvdChhZXMoeCA9IG1waCkpICsNCiAgZ2VvbV9oaXN0b2dyYW0oYmlud2lkdGggPSAxMCkNCmBgYA0KDQpUaGUgZmFzdGVzdCBmbGlnaHQgaXMgdGhlIHNhbWUgb25lIGlkZW50aWZpZWQgYXMgdGhlIGxhcmdlc3Qgb3V0bGllciBlYXJsaWVyLiBJdHMgZ3JvdW5kIHNwZWVkIHdhcyA3MDMgbXBoLiBUaGlzIGlzIGZhc3QgZm9yIGEgY29tbWVyY2lhbCBqZXQsIGJ1dCBub3QgaW1wb3NzaWJsZS4NCg0KYGBge3J9DQpmbGlnaHRzICU+JQ0KICBtdXRhdGUobXBoID0gZGlzdGFuY2UgLyAoYWlyX3RpbWUgLyA2MCkpICU+JQ0KICBhcnJhbmdlKGRlc2MobXBoKSkgJT4lDQogIHNlbGVjdChtcGgsIGZsaWdodCwgY2FycmllciwgZmxpZ2h0LCBtb250aCwgZGF5LCBkZXBfdGltZSkgJT4lDQogIGhlYWQoNSkNCmBgYA0KDQpPbmUgZXhwbGFuYXRpb24gZm9yIHVudXN1YWxseSBmYXN0IGZsaWdodHMgaXMgdGhhdCB0aGV5IGFyZSDigJxtYWtpbmcgdXAgdGltZeKAnSBpbiB0aGUgYWlyIGJ5IGZseWluZyBmYXN0ZXIuIENvbW1lcmNpYWwgYWlyY3JhZnQgZG8gbm90IGZseSBhdCB0aGVpciB0b3Agc3BlZWQgc2luY2UgdGhlIGFpcmxpbmVzIGFyZSBhbHNvIGNvbmNlcm5lZCBhYm91dCBmdWVsIGNvbnN1bXB0aW9uLiBCdXQsIGlmIGEgZmxpZ2h0IGlzIGRlbGF5ZWQgb24gdGhlIGdyb3VuZCwgaXQgbWF5IGZseSBmYXN0ZXIgdGhhbiB1c3VhbCBpbiBvcmRlciB0byBhdm9pZCBhIGxhdGUgYXJyaXZhbC4gU28sIEkgd291bGQgZXhwZWN0IHRoYXQgc29tZSBvZiB0aGUgdW51c3VhbGx5IGZhc3QgZmxpZ2h0cyB3ZXJlIGRlbGF5ZWQgb24gZGVwYXJ0dXJlLg0KDQpgYGB7cn0NCmZsaWdodHMgJT4lDQogIG11dGF0ZShtcGggPSBkaXN0YW5jZSAvIChhaXJfdGltZSAvIDYwKSkgJT4lDQogIGFycmFuZ2UoZGVzYyhtcGgpKSAlPiUNCiAgc2VsZWN0KA0KICAgIG9yaWdpbiwgZGVzdCwgbXBoLCB5ZWFyLCBtb250aCwgZGF5LCBkZXBfdGltZSwgZmxpZ2h0LCBjYXJyaWVyLA0KICAgIGRlcF9kZWxheSwgYXJyX2RlbGF5DQogICkNCmhlYWQoNSkNCmBgYA0KDQpGaXZlIG9mIHRoZSB0b3AgdGVuIGZsaWdodHMgaGFkIGRlcGFydHVyZSBkZWxheXMsIGFuZCB0aHJlZSBvZiB0aG9zZSB3ZXJlIGFibGUgdG8gbWFrZSB1cCB0aGF0IHRpbWUgaW4gdGhlIGFpciBhbmQgYXJyaXZlIGFoZWFkIG9mIHNjaGVkdWxlLg0KDQpPdmVyYWxsLCB0aGVyZSB3ZXJlIGEgZmV3IGZsaWdodHMgdGhhdCBzZWVtZWQgdW51c3VhbGx5IGZhc3QsIGJ1dCB0aGV5IGFsbCBmYWxsIGludG8gdGhlIHJlYWxtIG9mIHBsYXVzaWJpbGl0eSBhbmQgbGlrZWx5IGFyZSBub3QgZGF0YSBlbnRyeSBwcm9ibGVtcy4gW0VkLiBQbGVhc2UgY29ycmVjdCBtZSBpZiBJIGFtIG1pc3Npbmcgc29tZXRoaW5nXQ0KDQpUaGUgc2Vjb25kIHBhcnQgb2YgdGhlIHF1ZXN0aW9uIGFza3MgdXMgdG8gY29tcGFyZSBmbGlnaHRzIHRvIHRoZSBmYXN0ZXN0IGZsaWdodCBvbiBhIHJvdXRlIHRvIGZpbmQgdGhlIGZsaWdodHMgbW9zdCBkZWxheWVkIGluIHRoZSBhaXIuIEkgd2lsbCBjYWxjdWxhdGUgdGhlIGFtb3VudCBhIGZsaWdodCBpcyBkZWxheWVkIGluIGFpciBpbiB0d28gd2F5cy4gVGhlIGZpcnN0IGlzIHRoZSBhYnNvbHV0ZSBkZWxheSwgZGVmaW5lZCBhcyB0aGUgbnVtYmVyIG9mIG1pbnV0ZXMgbG9uZ2VyIHRoYW4gdGhlIGZhc3Rlc3QgZmxpZ2h0IG9uIHRoYXQgcm91dGUsYGFpcl90aW1lIC0gbWluKGFpcl90aW1lKWAuIFRoZSBzZWNvbmQgaXMgdGhlIHJlbGF0aXZlIGRlbGF5LCB3aGljaCBpcyB0aGUgcGVyY2VudGFnZSBpbmNyZWFzZSBpbiBhaXIgdGltZSByZWxhdGl2ZSB0byB0aGUgdGltZSBvZiB0aGUgZmFzdGVzdCBmbGlnaHQgYWxvbmcgdGhhdCByb3V0ZSwgYChhaXJfdGltZSAtIG1pbihhaXJfdGltZSkpIC8gbWluKGFpcl90aW1lKSAqIDEwMGAuDQoNCmBgYHtyfQ0KYWlyX3RpbWVfZGVsYXllZCA8LQ0KICBmbGlnaHRzICU+JQ0KICBncm91cF9ieShvcmlnaW4sIGRlc3QpICU+JQ0KICBtdXRhdGUoDQogICAgYWlyX3RpbWVfbWluID0gbWluKGFpcl90aW1lLCBuYS5ybSA9IFRSVUUpLA0KICAgIGFpcl90aW1lX2RlbGF5ID0gYWlyX3RpbWUgLSBhaXJfdGltZV9taW4sDQogICAgYWlyX3RpbWVfZGVsYXlfcGN0ID0gYWlyX3RpbWVfZGVsYXkgLyBhaXJfdGltZV9taW4gKiAxMDANCiAgKQ0KYGBgDQoNClRoZSBtb3N0IGRlbGF5ZWQgZmxpZ2h0IGluIGFpciBpbiBtaW51dGVzIHdhcyBETDg0MSBmcm9tIEpGSyB0byBTRk8gd2hpY2ggZGVwYXJ0ZWQgb24gMjAxMy0wNy0yOCBhdCAxNzoyNy4gSXQgdG9vayAxODkgbWludXRlcyBsb25nZXIgdGhhbiB0aGUgZmxpZ2h0IHdpdGggdGhlIHNob3J0ZXN0IGFpciB0aW1lIG9uIGl0cyByb3V0ZS4NCg0KYGBge3J9DQphaXJfdGltZV9kZWxheWVkICU+JQ0KICBhcnJhbmdlKGRlc2MoYWlyX3RpbWVfZGVsYXkpKSAlPiUNCiAgc2VsZWN0KA0KICAgIGFpcl90aW1lX2RlbGF5LCBjYXJyaWVyLCBmbGlnaHQsDQogICAgb3JpZ2luLCBkZXN0LCB5ZWFyLCBtb250aCwgZGF5LCBkZXBfdGltZSwNCiAgICBhaXJfdGltZSwgYWlyX3RpbWVfbWluDQogICkgJT4lDQogIGhlYWQoKSAlPiUNCiAgcHJpbnQod2lkdGggPSBJbmYpDQpgYGANCg0KVGhlIG1vc3QgZGVsYXllZCBmbGlnaHQgaW4gYWlyIGFzIGEgcGVyY2VudGFnZSBvZiB0aGUgZmFzdGVzdCBmbGlnaHQgYWxvbmcgdGhhdCByb3V0ZSB3YXMgVVMyMTM2IGZyb20gTEdBIHRvIEJPUyBkZXBhcnRpbmcgb24gMjAxMy0wNi0xNyBhdCAxNjo1Mi4gSXQgdG9vayA0MTAlIGxvbmdlciB0aGFuIHRoZSBmbGlnaHQgd2l0aCB0aGUgc2hvcnRlc3QgYWlyIHRpbWUgb24gaXRzIHJvdXRlLg0KDQpgYGB7cn0NCmFpcl90aW1lX2RlbGF5ZWQgJT4lDQogIGFycmFuZ2UoZGVzYyhhaXJfdGltZV9kZWxheSkpICU+JQ0KICBzZWxlY3QoDQogICAgYWlyX3RpbWVfZGVsYXlfcGN0LCBjYXJyaWVyLCBmbGlnaHQsDQogICAgb3JpZ2luLCBkZXN0LCB5ZWFyLCBtb250aCwgZGF5LCBkZXBfdGltZSwNCiAgICBhaXJfdGltZSwgYWlyX3RpbWVfbWluDQogICkgJT4lDQogIGhlYWQoKSAlPiUNCiAgcHJpbnQod2lkdGggPSBJbmYpDQpgYGANCg0KIyMjIDcuIEZpbmQgYWxsIGRlc3RpbmF0aW9ucyB0aGF0IGFyZSBmbG93biBieSBhdCBsZWFzdCB0d28gY2FycmllcnMuIFVzZSB0aGF0IGluZm9ybWF0aW9uIHRvIHJhbmsgdGhlIGNhcnJpZXJzLg0KDQpUbyByZXN0YXRlIHRoaXMgcXVlc3Rpb24sIHdlIGFyZSBhc2tlZCB0byByYW5rIGFpcmxpbmVzIGJ5IHRoZSBudW1iZXIgb2YgZGVzdGluYXRpb25zIHRoYXQgdGhleSBmbHkgdG8sIGNvbnNpZGVyaW5nIG9ubHkgdGhvc2UgYWlycG9ydHMgdGhhdCBhcmUgZmxvd24gdG8gYnkgdHdvIG9yIG1vcmUgYWlybGluZXMuIFRoZXJlIGFyZSB0d28gc3RlcHMgdG8gY2FsY3VsYXRpbmcgdGhpcyByYW5raW5nLiBGaXJzdCwgZmluZCBhbGwgYWlycG9ydHMgc2VydmljZWQgYnkgdHdvIG9yIG1vcmUgY2FycmllcnMuIFRoZW4sIHJhbmsgY2FycmllcnMgYnkgdGhlIG51bWJlciBvZiB0aG9zZSBkZXN0aW5hdGlvbnMgdGhhdCB0aGV5IHNlcnZpY2UuDQoNCmBgYHtyfQ0KZmxpZ2h0cyAlPiUNCiAgIyBmaW5kIGFsbCBhaXJwb3J0cyB3aXRoID4gMSBjYXJyaWVyDQogIGdyb3VwX2J5KGRlc3QpICU+JQ0KICBtdXRhdGUobl9jYXJyaWVycyA9IG5fZGlzdGluY3QoY2FycmllcikpICU+JQ0KICBmaWx0ZXIobl9jYXJyaWVycyA+IDEpICU+JQ0KICAjIHJhbmsgY2FycmllcnMgYnkgbnVtZXIgb2YgZGVzdGluYXRpb25zDQogIGdyb3VwX2J5KGNhcnJpZXIpICU+JQ0KICBzdW1tYXJpemUobl9kZXN0ID0gbl9kaXN0aW5jdChkZXN0KSkgJT4lDQogIGFycmFuZ2UoZGVzYyhuX2Rlc3QpKQ0KYGBgDQoNClRoZSBjYXJyaWVyIGAiRVYiYCBmbGllcyB0byB0aGUgbW9zdCBkZXN0aW5hdGlvbnMsIGNvbnNpZGVyaW5nIG9ubHkgYWlycG9ydHMgZmxvd24gdG8gYnkgdHdvIG9yIG1vcmUgY2FycmllcnMuIFdoYXQgaXMgYWlybGluZSBkb2VzIHRoZSBgIkVWImAgY2FycmllciBjb2RlIGNvcnJlc3BvbmQgdG8/DQoNCmBgYHtyfQ0KZmlsdGVyKGFpcmxpbmVzLCBjYXJyaWVyID09ICJFViIpDQpgYGANCg0KVW5sZXNzIHlvdSBrbm93IHRoZSBhaXJwbGFuZSBpbmR1c3RyeSwgaXQgaXMgbGlrZWx5IHRoYXQgeW91IGRvbuKAmXQgcmVjb2duaXplIFtFeHByZXNzSmV0XShodHRwczovL2VuLndpa2lwZWRpYS5vcmcvd2lraS9FeHByZXNzSmV0KTsgSSBjZXJ0YWlubHkgZGlkbuKAmXQuIEl0IGlzIGEgcmVnaW9uYWwgYWlybGluZSB0aGF0IHBhcnRuZXJzIHdpdGggbWFqb3IgYWlybGluZXMgdG8gZmx5IGZyb20gaHVicyAobGFyZ2VyIGFpcnBvcnRzKSB0byBzbWFsbGVyIGFpcnBvcnRzLiBUaGlzIG1lYW5zIHRoYXQgbWFueSBvZiB0aGUgc2hvcnRlciBmbGlnaHRzIG9mIG1ham9yIGNhcnJpZXJzIGFyZSBvcGVyYXRlZCBieSBFeHByZXNzSmV0LiBUaGlzIGJ1c2luZXNzIG1vZGVsIGV4cGxhaW5zIHdoeSBFeHByZXNzSmV0IHNlcnZpY2VzIHRoZSBtb3N0IGRlc3RpbmF0aW9ucy4NCg0KQW1vbmcgdGhlIGFpcmxpbmVzIHRoYXQgZmx5IHRvIG9ubHkgb25lIGRlc3RpbmF0aW9uIGZyb20gTmV3IFlvcmsgYXJlIEFsYXNrYSBBaXJsaW5lcyBhbmQgSGF3YWlpYW4gQWlybGluZXMuDQoNCmBgYHtyfQ0KZmlsdGVyKGFpcmxpbmVzLCBjYXJyaWVyICVpbiUgYygiQVMiLCAiRjkiLCAiSEEiKSkNCmBgYA0KDQojIyMgOC4gRm9yIGVhY2ggcGxhbmUsIGNvdW50IHRoZSBudW1iZXIgb2YgZmxpZ2h0cyBiZWZvcmUgdGhlIGZpcnN0IGRlbGF5IG9mIGdyZWF0ZXIgdGhhbiAxIGhvdXIuDQoNClRoZSBxdWVzdGlvbiBkb2VzIG5vdCBzcGVjaWZ5IGFycml2YWwgb3IgZGVwYXJ0dXJlIGRlbGF5LiBJIGNvbnNpZGVyIGRlcF9kZWxheSBpbiB0aGlzIGFuc3dlciwgdGhvdWdoIHNpbWlsYXIgY29kZSBjb3VsZCBiZSB1c2VkIGZvciBhcnJfZGVsYXkuDQoNCmBgYHtyfQ0KZmxpZ2h0cyAlPiUNCiAgIyBzb3J0IGluIGluY3JlYXNpbmcgb3JkZXINCiAgc2VsZWN0KHRhaWxudW0sIHllYXIsIG1vbnRoLCBkYXksIGRlcF9kZWxheSkgJT4lDQogIGZpbHRlcighaXMubmEoZGVwX2RlbGF5KSkgJT4lDQogIGFycmFuZ2UodGFpbG51bSwgeWVhciwgbW9udGgsIGRheSkgJT4lDQogIGdyb3VwX2J5KHRhaWxudW0pICU+JQ0KICAjIGN1bXVsYXRpdmUgbnVtYmVyIG9mIGZsaWdodHMgZGVsYXllZCBvdmVyIG9uZSBob3VyDQogIG11dGF0ZShjdW11bGF0aXZlX2hyX2RlbGF5cyA9IGN1bXN1bShkZXBfZGVsYXkgPiA2MCkpICU+JQ0KICAjIGNvdW50IHRoZSBudW1iZXIgb2YgZmxpZ2h0cyA9PSAwDQogIHN1bW1hcmlzZSh0b3RhbF9mbGlnaHRzID0gc3VtKGN1bXVsYXRpdmVfaHJfZGVsYXlzIDwgMSkpICU+JQ0KICBhcnJhbmdlKHRvdGFsX2ZsaWdodHMpDQpgYGANCg==