date-time is a date plus a time: it uniquely identifies an instant in time. to the nearest second. Tibbles print this as <dttm>
R doesn’t have a native class for storing times. If you need one, you se the hms package
To get the date or date-time you can use today() or now()
library(lubridate)
Attaching package: <U+393C><U+3E31>lubridate<U+393C><U+3E32>
The following object is masked from <U+393C><U+3E31>package:base<U+393C><U+3E32>:
date
today()
[1] "2017-04-20"
now()
[1] "2017-04-20 14:21:40 MYT"
The three ways to create a date/time field
1. From a string 2. From individual date-time components 3. From an existing date/time object
To use the lubridate helpere functions, you need to identify the order in which year, month and day appear in your dates in the string, then arange “y”, “m”, and “d” in the same order.
ymd("2017-01-31")
[1] "2017-01-31"
mdy("January 31st, 2017")
[1] "2017-01-31"
mdy("Jan 31, 2017")
[1] "2017-01-31"
dmy("31-Jan-2017")
[1] "2017-01-31"
dmy("31-January-2017")
[1] "2017-01-31"
These functions also take unquote numbers. This is the most concise way to create a single date/time object
ymd(20170101)
[1] "2017-01-01"
To add a time to a date, add an underscore and one or more h,m and s
ymd_hm("2017-01-01 12:30")
[1] "2017-01-01 12:30:00 UTC"
YOu can force the creation of a time zone.
ymd(20170101, tz = "UTC")
[1] "2017-01-01 UTC"
ymd_hm("2017-01-01 11:30", tz = "UTC")
[1] "2017-01-01 11:30:00 UTC"
mdy("Mar 7, 2018", tz = "UTC")
[1] "2018-03-07 UTC"
ymd_hm(201701011130, tz = "UTC")
[1] "2017-01-01 11:30:00 UTC"
From Individual Components
When you want to get the individual components of the date-time spread across multiple columns.
library(nycflights13)
library(tidyverse)
flights %>%
select (year, month,day, hour, minute, arr_time)
use make_date() for dates, or make_datetime() for date-times
flights %>%
select(year, month, day, hour, minute) %>%
mutate(departure = make_datetime(year, month,day, hour, minute)
)
flights %>%
head (10)
We need to use modulus arithmetic to pull out the hour and minute componennts. So 517 is 5 hours and 17 minutes. that is why we need to divide time by 100 and the remainder as minutes.
make_datetime_100 <- function(year, month, day, time){
make_datetime(year, month, day, time %/% 100, time %% 100)
}
flights_dt <- flights %>%
filter(!is.na(dep_time), !is.na(arr_time)) %>%
mutate(
dep_time = make_datetime_100(year, month, day, dep_time),
arr_time = make_datetime_100(year, month, day, arr_time),
sched_dep_time = make_datetime_100(year, month, day, sched_dep_time),
sched_arr_time = make_datetime_100(year, month, day, sched_arr_time)
)
flights_dt %>%
select(origin, dest, ends_with("delay"), ends_with("time"))
# visualize
flights_dt %>%
ggplot(aes(dep_time)) +
geom_freqpoly(binwidth=86400
)

Or within a single day:
flights_dt %>%
filter(dep_time < ymd(20130102)) %>%
ggplot(aes(dep_time)) +
geom_freqpoly(binwidth = 600)

Note: when using date-times in a numeric context, 1 means 1 second, so a bindwidth of 86400 means one day. For dates, 1 means 1 day.
From Other Types
Yo may want to switch between a date-time and a date. The the job of as_datetime() and as_date()
as_datetime(today())
[1] "2017-04-20 UTC"
as_date(now())
[1] "2017-04-20"
When your dat/times is a nuemric offset from the unix epoch (1970 01 01)
as_datetime(1492672742)
[1] "2017-04-20 07:19:02 UTC"
as_date(365*40 +2)
[1] "2009-12-24"
Exercise
What happens if you parse a string that contains invalid dates?
ymd(c("2010-10-10","bananas"))
1 failed to parse.
[1] "2010-10-10" NA
It produces an NA and an warning message.
What does the tzone argument to today() do? Why is it important?
today(tzone = "Asia/Manila")
[1] "2017-04-20"
tzone argument specifies which time zone you would like to find the current date of. tzone defaults to the system time zone set on your computer.
Use the appropriate lubridate function to parse each of the following dates:
d1 <- "January 1, 2010"
d2 <- "2015-mar-07"
d3 <- "06-Jun_2017"
d4 <- c("August 19 (2015)","July 1 (2015)")
d5 <- "12/30/14" # dec 30 2014
mdy(d1)
[1] "2010-01-01"
ymd(d2)
[1] "2015-03-07"
dmy(d3)
[1] "2017-06-06"
mdy(d4)
[1] "2015-08-19" "2015-07-01"
mdy(d5)
[1] "2014-12-30"
Date-Time Components
Focus now on accessor functions that letyou get and set individual components.
Getting components
You can pull out individual parts of the date with the functions : year(), month(), mday() [day of month], yday() [day of year], wday() [day of the week], hour(), minute(), and second().
datetime <- ymd_hms("2016-07-08 12:34:56")
year(datetime)
[1] 2016
month(datetime)
[1] 7
mday(datetime)
[1] 8
yday(datetime)
[1] 190
wday(datetime)
[1] 6
hour(datetime)
[1] 12
minute(datetime)
[1] 34
second(datetime)
[1] 56
Month() and wday() can set label = true and abbr = FALSE
month(datetime, label = TRUE, abbr = FALSE)
[1] July
12 Levels: January < February < March < April < May < June < July < August < September < ... < December
wday(datetime, label = TRUE, abbr = TRUE)
[1] Fri
Levels: Sun < Mon < Tues < Wed < Thurs < Fri < Sat
flights_dt %>%
mutate(wday = wday(dep_time, label = TRUE)) %>%
ggplot(aes(x = wday)) +
geom_bar()

Let’s look at the average departure delay by minute wihtin the hour. It looks like flights leaving in minutes 20-30 and 50-60 have much lower delays than the rest of the hour.
flights_dt %>%
mutate(minute = minute(dep_time)) %>%
group_by(minute) %>%
summarize(
avg_delay = mean(arr_delay, na.rm = TRUE),
n = n()) %>%
ggplot(aes(minute, avg_delay)) +
geom_line()

Interestingly, the scheduled departure time don’t have a similar pattern.
sched_dep <- flights_dt %>%
mutate(minute = minute(sched_dep_time)) %>%
group_by(minute) %>%
summarize(avg_delay = mean(arr_delay, na.rm = TRUE),
n = n())
ggplot(sched_dep, aes(minute, avg_delay)) +
geom_line()

NA
ggplot(sched_dep, aes(minute, n)) +
geom_line()

Rounding
An alternative approach to plotting individual components is to round the date to a nearby unit of time, with floor_date(), round_date() and ceiling_date(). Each ceiling_date() function takes a vector of dates to adjust and then the name of the unit to round down (floor).
flights_dt %>%
count(week = floor_date(dep_time, "week")) %>%
ggplot(aes(week, n)) +
geom_line()

Computing the difference between a rounded and unrounded date can be particularly useful.
Setting Components
datetime <- ymd_hms("2016-07-08 12:34:56")
year(datetime) <- 2020
month(datetime) <- 01
hour(datetime) <- hour(datetime) + 2
datetime
[1] "2020-01-08 14:34:56 UTC"
You can also create a new date-time with update(). This allows you to set multile avlues at once.
update(datetime, year=2020, month=4, mday = 2, hour = 2)
[1] "2020-04-02 02:34:56 UTC"
If values are too big, they will rollover to the next valid date
ymd("2015-02-01") %>%
update(mday = 30)
[1] "2015-03-02"
ymd("2015-02-01") %>%
update(hour = 400)
[1] "2015-02-17 16:00:00 UTC"
Here, update is used to show the distribution of flights across the course of the day for every day of the year:
flights_dt %>%
mutate(dep_hour = update(dep_time, yday = 20 )) %>%
ggplot(aes(dep_hour)) +
geom_freqpoly(binwidth = 300)

Setting larger components of a date to a constant is a powerful technique that allows you to drill down into the smaller components.
Exercises> How does the distribution of flight times within a day change over the course of the year?
flights_dt %>%
mutate(time = hour(dep_time) * 100 + minute(dep_time),
mon = as.factor(month
(dep_time))) %>%
ggplot(aes(x = time, group = mon, color = mon)) +
geom_freqpoly(binwidth = 100)

Compare dep_time, sched_dep_time and dep_delay. Are they consistent? Explain your findings. If they are consistent, then dep_time = sched_dep_time + dep_delay.
flights_dt %>%
mutate(dep_time_ = sched_dep_time + dep_delay * 60) %>%
filter(dep_time_ != dep_time) %>%
select(dep_time_, dep_time, sched_dep_time, dep_delay)
There exist discrepencies. It looks like there are mistakes in the dates. These are flights in which the actual departure time is on the next day relative to the scheduled departure time. We forgot to account for this when creating the date-times. The code would have had to check if the departure time is less than the scheduled departure time. Alternatively, simply adding the delay time is more robust because it will automatically account for crossing into the next day.
Compare air_time with the duration between the departure and arrival. Explain your findings.
flights_dt %>%
mutate(flight_duration = as.numeric(arr_time - dep_time),
air_time_mins = air_time,
diff = flight_duration - air_time_mins) %>%
select(origin, dest, flight_duration, air_time_mins, diff)
How does the average delay time change over the course of a day? Should you use dep_time or sched_dep_time? Why? Use sched_dep_time because that is the relevant metric for someone scheduling a flight. Also, using dep_time will always bias delays to later in the day since delays will push flights later.
flights_dt %>%
mutate(sched_dep_hour = hour(sched_dep_time)) %>%
group_by(sched_dep_hour) %>%
summarise(dep_delay = mean(dep_delay)) %>%
ggplot(aes(y = dep_delay, x = sched_dep_hour)) +
geom_point() +
geom_smooth()

On what day of the week should you leave if you want to minimise the chance of a delay? Saturday has the lowest combined delays.
flights_dt %>%
mutate(dow = wday(sched_dep_time, label = TRUE)) %>%
group_by(dow) %>%
summarise(dep_delay = mean(dep_delay),
arr_delay = mean(arr_delay, na.rm = TRUE)) %>%
arrange(desc(dep_delay+arr_delay))
What makes the distribution of diamonds\(carat and flights\)sched_dep_time similar?
ggplot(diamonds, aes(x = carat)) +
geom_density()

In both carat and sched_dep_time there are abnormally large numbers of values are at nice “human” numbers. In sched_dep_time it is at 00 and 30 minutes. In carats, it is at 0, 1/3, 1/2, 2/3,
ggplot(diamonds, aes(x = carat %% 1 * 100)) +
geom_histogram(binwidth = 1)

In scheduled departure times it is 00 and 30 minutes, and minutes ending in 0 and 5.
ggplot(flights_dt, aes(x = minute(sched_dep_time))) +
geom_histogram(binwidth = 1)

Confirm my hypothesis that the early departures of flights in minutes 20-30 and 50-60 are caused by scheduled flights that leave early. Hint: create a binary variable that tells you whether or not a flight was delayed.
flights_dt %>%
mutate(early = dep_delay < 0,
minute = minute(sched_dep_time)) %>%
group_by(minute) %>%
summarise(early = mean(early)) %>%
ggplot(aes(x = minute, y = early)) +
geom_point()

At the minute level, there doesn’t appear to be anything. But if grouped in 10 minute intervals, there is a higher proportion of early flights during those minutes.
flights_dt %>%
mutate(early = dep_delay < 0,
minute = minute(sched_dep_time) %% 10) %>%
group_by(minute) %>%
summarise(early = mean(early)) %>%
ggplot(aes(x = minute, y = early)) +
geom_point()

Time Spanse
Arithmetic of dates, including subtraction, additin, and division. And the three important classes: durations which represent an exact number of seconds periods, which represent human units like weeks and months intervals, which repreent a starting and ending points
Durations
In R, when you subtract two dates, you get a difftime object.
h_age <- today() - ymd(19630509)
h_age
Time difference of 19705 days
as.duration(h_age)
[1] "1702512000s (~53.95 years)"
Durations come with a bunch of conenient constructors: dseconds(), dminutes(), dhours() and ddays(),dweeks(), dyears()
dseconds(15)
[1] "15s"
dminutes(10)
[1] "600s (~10 minutes)"
ddays(0:5)
[1] "0s" "86400s (~1 days)" "172800s (~2 days)" "259200s (~3 days)" "345600s (~4 days)"
[6] "432000s (~5 days)"
dweeks(3)
[1] "1814400s (~3 weeks)"
dyears(1)
[1] "31536000s (~52.14 weeks)"
Duration is always recorded in seconds. Larger units are created by converting minutes, hours, days, weeks and years to seconds at the standard rate. You can add and multiply durations.
2 * dyears(1)
[1] "63072000s (~2 years)"
dyears(1) + dweeks(12) + dhours(15)
[1] "38847600s (~1.23 years)"
You can add and subtract durations to and from days.
tomorrow <- today() + ddays(1)
lst_year <- today() - dyears(1)
tomorrow
[1] "2017-04-21"
lst_year
[1] "2016-04-20"
Duration includes Daylight savings time.
Periods
To solve the DST problem, lubridate provides periods. Periods are time spans but don’t have a fixed lenth in seconds. Instead they work with “human times”“, like days and months.
one_pm <- ymd_hms("2016-03-12 13:00:00", tz="America/New_York")
one_pm
[1] "2016-03-12 13:00:00 EST"
one_pm + ddays(1)
[1] "2016-03-13 14:00:00 EDT"
one_pm + days(1)
[1] "2016-03-13 13:00:00 EDT"
Like durations, Periods can be created with a number of constructor functions: seconds(), minutes(), hours(), days(), months(), weeks(), years()
seconds(15)
[1] "15S"
minutes(10)
[1] "10M 0S"
hours(c(12,24))
[1] "12H 0M 0S" "24H 0M 0S"
days(7)
[1] "7d 0H 0M 0S"
months(1:6)
[1] "1m 0d 0H 0M 0S" "2m 0d 0H 0M 0S" "3m 0d 0H 0M 0S" "4m 0d 0H 0M 0S" "5m 0d 0H 0M 0S" "6m 0d 0H 0M 0S"
weeks(3)
[1] "21d 0H 0M 0S"
years(1)
[1] "1y 0m 0d 0H 0M 0S"
You can add and multiply periods
10 * (months(6) + days(1))
[1] "60m 10d 0H 0M 0S"
days(50) + hours(25) + minutes(2)
[1] "50d 25H 2M 0S"
You can add them to dates. Compared to duratins, periods are more likely to do what you expect.
ymd('2016-01-01') + dyears(1)
[1] "2016-12-31"
ymd('2016-01-01') + years(1)
[1] "2017-01-01"
Periods can be useful to fix flights that appear to arrive before they departed.
flights_dt %>%
filter(arr_time < dep_time)
We can fix this by adding days(1) to the arrival time of each overnight flight
flights_dt <- flights_dt %>%
mutate(
overnight = arr_time < dep_time,
arr_time = arr_time + days(overnight * 1),
sched_arr_time = sched_arr_time + days(overnight * 1))
flights_dt
Intervals
years(1) / days(1)
estimate only: convert to intervals for accuracy
[1] 365.25
If you want more accurate measurement, you’ll have to use an interval. An interval is a duration with a starting point. That makes it precise so you can determine exactly ow long it is.
next_year <- today() + years(1)
(today() %--% next_year /ddays(1))
[1] 365
To find out how many periods fall into an interval, you need to sue itneger division
(today() %--% next_year) %/% days(1)
Note: method with signature <U+393C><U+3E31>Timespan#Timespan<U+393C><U+3E32> chosen for function <U+393C><U+3E31>%/%<U+393C><U+3E32>,
target signature <U+393C><U+3E31>Interval#Period<U+393C><U+3E32>.
"Interval#ANY", "ANY#Period" would also be valid
[1] 365
These are the permitted arithmetic operations between the different data types.
Exercises
Why is there months() but no dmonths()? There is no direct unambigous value of months in seconds
Explain days(overnight * 1) to someone who has just started learning R. How does it work?
overnight is equal to TRUE (1) or FALSE (0). So if it is an overnight flight, this becomes 1 day, and if not, then overnight = 0, and no days are added to the date.
Create a vector of dates giving the first day of every month in 2015.
ymd("2015-01-01") + months(0:11)
[1] "2015-01-01" "2015-02-01" "2015-03-01" "2015-04-01" "2015-05-01" "2015-06-01" "2015-07-01" "2015-08-01"
[9] "2015-09-01" "2015-10-01" "2015-11-01" "2015-12-01"
Create a vector of dates giving the first day of every month in the current year.
floor_date(today(), unit = "year") + months(0:11)
[1] "2017-01-01" "2017-02-01" "2017-03-01" "2017-04-01" "2017-05-01" "2017-06-01" "2017-07-01" "2017-08-01"
[9] "2017-09-01" "2017-10-01" "2017-11-01" "2017-12-01"
Write a function that given your birthday (as a date), returns how old you are in years.
age <- function(bday) {
(bday %--% today()) %/% years(1)
}
age(ymd('1963-05-09'))
[1] 53
Time Zones
R uses international standard IANA time zones. These use a consistent naming scheme with “/” typically in the form “<continent>/<city>”
You can find out your current time zone is with Sys.timezone()
Sys.timezone()
[1] "Asia/Kuala_Lumpur"
Sys.time()
[1] "2017-04-20 17:37:32 MYT"
To see the complete list of all time zone names with OlsonNames()
length(OlsonNames())
[1] 589
head(OlsonNames())
[1] "Africa/Abidjan" "Africa/Accra" "Africa/Addis_Ababa" "Africa/Algiers" "Africa/Asmara"
[6] "Africa/Asmera"
To change the underlying instant in time - force_tz(). Use this whe you have an instant that has been labeled with the incorrect time zone, and you need to fix it
x1 <- ymd_hms("2015-06-01 12:00:00", tz = "America/New_York")
x2 <- ymd_hms("2015-06-01 18:00:00", tz = "Europe/Copenhagen")
x3 <- ymd_hms("2015-06-02 04:00:00", tz = "Pacific/Auckland")
x4 <- c(x1,x2,x3)
x4b <- force_tz(x4, tzone = "Australia/Lord_Howe")
x4b
[1] "2015-06-02 LHST" "2015-06-02 LHST" "2015-06-02 LHST"
Exercise answers from : https://jrnold.github.io/e4qf/dates-and-times.html
LS0tDQp0aXRsZTogIlIgRm9yIERhdGEgU2NpZW5jZSBDaGFwdGVyIDEzIg0Kb3V0cHV0OiBodG1sX25vdGVib29rDQotLS0NCg0KPGgxPiBEYXRlcyBhbmQgVGltZXMgd2l0aCBMdWJyaWRhdGUgPGgxPg0KDQo8aDI+IFByZXJlcXVpc2l0ZXMgPC9oMj4NCmxpYnJhcnkodGlkeXZlcnNlKSA8L2JyPg0KbGlicmFyeShsdWJyaWRhdGUpIDwvYnI+DQpsaWJyYXJ5KG55Y2ZsaWdodHMxMykgPC9icj4NCg0KDQo8aDI+IENyZWF0aW5nIGRhdGUvdGltZXMgPC9oMj4NClRoZSB0aHJlZSB0eXBlcyBvZiBkYXRlL3RpbWUgZGF0YSB0aGF0IHJlZmVyIHRvIGFuIGluc3RhbnQgaW4gdGltZTogPC9icj4NCjxsaT4gRGF0ZS4gVGliYmxlcyBwcmludCB0aGlzIGFzIFw8ZGF0ZVw+IDwvbGk+DQo8bGk+IHRpbWUgd2l0aGluIGEgZGF5LiBUaWJibGVzIHByaW50IHRoaXMgYXMgXDx0aW1lXD4gPC9saT4NCjxsaT4gZGF0ZS10aW1lIGlzIGEgZGF0ZSBwbHVzIGEgdGltZTogaXQgdW5pcXVlbHkgaWRlbnRpZmllcyBhbiBpbnN0YW50IGluIHRpbWUuIHRvIHRoZSBuZWFyZXN0IHNlY29uZC4gIFRpYmJsZXMgcHJpbnQgdGhpcyBhcyBcPGR0dG1cPiA8L3A+DQoNClIgZG9lc24ndCBoYXZlIGEgbmF0aXZlIGNsYXNzIGZvciBzdG9yaW5nIHRpbWVzLiBJZiB5b3UgbmVlZCBvbmUsIHlvdSBzZSB0aGUgPGI+aG1zPC9iPiBwYWNrYWdlIDwvcD4NClRvIGdldCB0aGUgZGF0ZSBvciBkYXRlLXRpbWUgeW91IGNhbiB1c2UgdG9kYXkoKSBvciBub3coKQ0KDQpgYGB7cn0NCmxpYnJhcnkobHVicmlkYXRlKQ0KdG9kYXkoKQ0Kbm93KCkNCmBgYA0KDQpUaGUgdGhyZWUgd2F5cyB0byBjcmVhdGUgYSBkYXRlL3RpbWUgZmllbGQgPGJyPg0KMS4gRnJvbSBhIHN0cmluZyA8L2JyPg0KMi4gRnJvbSBpbmRpdmlkdWFsIGRhdGUtdGltZSBjb21wb25lbnRzIDwvYnI+DQozLiBGcm9tIGFuIGV4aXN0aW5nIGRhdGUvdGltZSBvYmplY3QgPC9wPg0KDQpUbyB1c2UgdGhlIGx1YnJpZGF0ZSBoZWxwZXJlIGZ1bmN0aW9ucywgeW91IG5lZWQgdG8gaWRlbnRpZnkgdGhlIG9yZGVyIGluIHdoaWNoIHllYXIsIG1vbnRoIGFuZCBkYXkgYXBwZWFyIGluIHlvdXIgZGF0ZXMgaW4gdGhlIHN0cmluZywgdGhlbiBhcmFuZ2UgInkiLCAibSIsIGFuZCAiZCIgaW4gdGhlIHNhbWUgb3JkZXIuIA0KDQpgYGB7cn0NCnltZCgiMjAxNy0wMS0zMSIpDQptZHkoIkphbnVhcnkgMzFzdCwgMjAxNyIpDQptZHkoIkphbiAzMSwgMjAxNyIpDQpkbXkoIjMxLUphbi0yMDE3IikNCmRteSgiMzEtSmFudWFyeS0yMDE3IikNCmBgYA0KVGhlc2UgZnVuY3Rpb25zIGFsc28gdGFrZSB1bnF1b3RlIG51bWJlcnMuIFRoaXMgaXMgdGhlIG1vc3QgY29uY2lzZSB3YXkgdG8gY3JlYXRlIGEgc2luZ2xlIGRhdGUvdGltZSBvYmplY3QgDQoNCmBgYHtyfQ0KeW1kKDIwMTcwMTAxKQ0KYGBgDQoNClRvIGFkZCBhIHRpbWUgdG8gYSBkYXRlLCBhZGQgYW4gdW5kZXJzY29yZSBhbmQgb25lIG9yIG1vcmUgaCxtIGFuZCBzDQoNCmBgYHtyfQ0KeW1kX2htKCIyMDE3LTAxLTAxIDEyOjMwIikNCmBgYA0KDQpZT3UgY2FuIGZvcmNlIHRoZSBjcmVhdGlvbiBvZiBhIHRpbWUgem9uZS4NCmBgYHtyfQ0KeW1kKDIwMTcwMTAxLCB0eiA9ICJVVEMiKQ0KeW1kX2htKCIyMDE3LTAxLTAxIDExOjMwIiwgdHogPSAiVVRDIikNCm1keSgiTWFyIDcsIDIwMTgiLCB0eiA9ICJVVEMiKQ0KeW1kX2htKDIwMTcwMTAxMTEzMCwgdHogPSAiVVRDIikNCg0KYGBgDQoNCjxoMj4gRnJvbSBJbmRpdmlkdWFsIENvbXBvbmVudHMgPC9oMj4NCldoZW4geW91IHdhbnQgdG8gZ2V0IHRoZSBpbmRpdmlkdWFsIGNvbXBvbmVudHMgb2YgdGhlIGRhdGUtdGltZSBzcHJlYWQgYWNyb3NzIG11bHRpcGxlIGNvbHVtbnMuDQpgYGB7cn0NCmxpYnJhcnkobnljZmxpZ2h0czEzKQ0KbGlicmFyeSh0aWR5dmVyc2UpDQpmbGlnaHRzICU+JQ0KICBzZWxlY3QgKHllYXIsIG1vbnRoLGRheSwgaG91ciwgbWludXRlKQ0KDQpgYGANCg0KdXNlIG1ha2VfZGF0ZSgpIGZvciBkYXRlcywgb3IgbWFrZV9kYXRldGltZSgpIGZvciBkYXRlLXRpbWVzDQoNCmBgYHtyfQ0KZmxpZ2h0cyAlPiUNCiAgc2VsZWN0KHllYXIsIG1vbnRoLCBkYXksIGhvdXIsIG1pbnV0ZSkgJT4lDQogIG11dGF0ZShkZXBhcnR1cmUgPSBtYWtlX2RhdGV0aW1lKHllYXIsIG1vbnRoLGRheSwgaG91ciwgbWludXRlKQ0KICAgICAgICApDQoNCmBgYA0KDQpgYGB7cn0NCmZsaWdodHMgJT4lDQogIGhlYWQgICgxMCkNCmBgYA0KDQpXZSBuZWVkIHRvIHVzZSBtb2R1bHVzIGFyaXRobWV0aWMgdG8gcHVsbCBvdXQgdGhlIGhvdXIgYW5kIG1pbnV0ZSBjb21wb25lbm50cy4gU28gNTE3IGlzIDUgaG91cnMgYW5kIDE3IG1pbnV0ZXMuIHRoYXQgaXMgd2h5IHdlIG5lZWQgdG8gZGl2aWRlIHRpbWUgYnkgMTAwIGFuZCB0aGUgcmVtYWluZGVyIGFzIG1pbnV0ZXMuDQoNCmBgYHtyfQ0KbWFrZV9kYXRldGltZV8xMDAgPC0gIGZ1bmN0aW9uKHllYXIsIG1vbnRoLCBkYXksIHRpbWUpew0KICBtYWtlX2RhdGV0aW1lKHllYXIsIG1vbnRoLCBkYXksIHRpbWUgJS8lIDEwMCwgdGltZSAlJSAxMDApDQp9DQoNCmZsaWdodHNfZHQgPC0gIGZsaWdodHMgJT4lDQogIGZpbHRlcighaXMubmEoZGVwX3RpbWUpLCAhaXMubmEoYXJyX3RpbWUpKSAlPiUNCiAgbXV0YXRlKA0KICAgIGRlcF90aW1lID0gbWFrZV9kYXRldGltZV8xMDAoeWVhciwgbW9udGgsIGRheSwgZGVwX3RpbWUpLA0KICAgIGFycl90aW1lID0gbWFrZV9kYXRldGltZV8xMDAoeWVhciwgbW9udGgsIGRheSwgYXJyX3RpbWUpLA0KICAgIHNjaGVkX2RlcF90aW1lID0gbWFrZV9kYXRldGltZV8xMDAoeWVhciwgbW9udGgsIGRheSwgc2NoZWRfZGVwX3RpbWUpLA0KICAgIHNjaGVkX2Fycl90aW1lID0gbWFrZV9kYXRldGltZV8xMDAoeWVhciwgbW9udGgsIGRheSwgc2NoZWRfYXJyX3RpbWUpDQogICkgDQpmbGlnaHRzX2R0ICU+JQ0KICBzZWxlY3Qob3JpZ2luLCBkZXN0LCBlbmRzX3dpdGgoImRlbGF5IiksIGVuZHNfd2l0aCgidGltZSIpKQ0KYGBgIA0KYGBge3J9DQojIHZpc3VhbGl6ZQ0KZmxpZ2h0c19kdCAlPiUNCmdncGxvdChhZXMoZGVwX3RpbWUpKSArDQogIGdlb21fZnJlcXBvbHkoYmlud2lkdGg9ODY0MDANCiAgICAgICAgICAgICAgICApDQpgYGANCk9yIHdpdGhpbiBhIHNpbmdsZSBkYXk6DQoNCmBgYHtyfQ0KZmxpZ2h0c19kdCAlPiUNCmZpbHRlcihkZXBfdGltZSA8IHltZCgyMDEzMDEwMikpICU+JQ0KICBnZ3Bsb3QoYWVzKGRlcF90aW1lKSkgKw0KICBnZW9tX2ZyZXFwb2x5KGJpbndpZHRoID0gNjAwKQ0KYGBgDQoNCk5vdGU6IHdoZW4gdXNpbmcgZGF0ZS10aW1lcyBpbiBhIG51bWVyaWMgY29udGV4dCwgMSBtZWFucyAxIHNlY29uZCwgc28gYSBiaW5kd2lkdGggb2YgODY0MDAgbWVhbnMgb25lIGRheS4gRm9yIGRhdGVzLCAxIG1lYW5zIDEgZGF5Lg0KDQo8aDI+IEZyb20gT3RoZXIgVHlwZXMgPC9oMj4NCg0KWW8gbWF5IHdhbnQgdG8gc3dpdGNoIGJldHdlZW4gYSBkYXRlLXRpbWUgYW5kIGEgZGF0ZS4gVGhlIHRoZSBqb2Igb2YgYXNfZGF0ZXRpbWUoKSBhbmQgYXNfZGF0ZSgpDQoNCmBgYHtyfQ0KDQphc19kYXRldGltZSh0b2RheSgpKQ0KYXNfZGF0ZShub3coKSkNCg0KYGBgDQoNCldoZW4geW91ciBkYXQvdGltZXMgaXMgYSBudWVtcmljIG9mZnNldCBmcm9tIHRoZSB1bml4IGVwb2NoICgxOTcwIDAxIDAxKQ0KDQpgYGB7cn0NCmFzX2RhdGV0aW1lKDE0OTI2NzI3NDIpDQphc19kYXRlKDM2NSAqIDQwICsgMikNCmBgYA0KDQoNCjxoMz4gRXhlcmNpc2UgPC9oMz4NCg0KV2hhdCBoYXBwZW5zIGlmIHlvdSBwYXJzZSBhIHN0cmluZyB0aGF0IGNvbnRhaW5zIGludmFsaWQgZGF0ZXM/IA0KYGBge3J9DQp5bWQoYygiMjAxMC0xMC0xMCIsImJhbmFuYXMiKSkNCmBgYA0KDQpJdCBwcm9kdWNlcyBhbiBOQSBhbmQgYW4gd2FybmluZyBtZXNzYWdlLg0KDQoNCldoYXQgZG9lcyB0aGUgdHpvbmUgYXJndW1lbnQgdG8gdG9kYXkoKSBkbz8gV2h5IGlzIGl0IGltcG9ydGFudD8gPC9icj4NCg0KYGBge3J9DQp0b2RheSh0em9uZSA9ICJBc2lhL01hbmlsYSIpDQpgYGANCnR6b25lIGFyZ3VtZW50IHNwZWNpZmllcyB3aGljaCB0aW1lIHpvbmUgeW91IHdvdWxkIGxpa2UgdG8gZmluZCB0aGUgY3VycmVudCBkYXRlIG9mLiB0em9uZSBkZWZhdWx0cyB0byB0aGUgc3lzdGVtIHRpbWUgem9uZSBzZXQgb24geW91ciBjb21wdXRlci4gPC9wPg0KDQpVc2UgdGhlIGFwcHJvcHJpYXRlIGx1YnJpZGF0ZSBmdW5jdGlvbiB0byBwYXJzZSBlYWNoIG9mIHRoZSBmb2xsb3dpbmcgZGF0ZXM6DQoNCmBgYHtyfQ0KZDEgPC0gIkphbnVhcnkgIDEsIDIwMTAiDQpkMiA8LSAiMjAxNS1tYXItMDciDQpkMyA8LSAiMDYtSnVuXzIwMTciDQpkNCA8LSAgYygiQXVndXN0IDE5ICgyMDE1KSIsIkp1bHkgMSAoMjAxNSkiKQ0KZDUgPC0gIjEyLzMwLzE0IiAjIGRlYyAzMCAyMDE0DQoNCm1keShkMSkNCnltZChkMikNCmRteShkMykNCm1keShkNCkNCm1keShkNSkNCmBgYA0KDQoNCjxoMj4gRGF0ZS1UaW1lIENvbXBvbmVudHMgPC9oMj4NCkZvY3VzIG5vdyBvbiBhY2Nlc3NvciBmdW5jdGlvbnMgdGhhdCBsZXR5b3UgZ2V0IGFuZCBzZXQgaW5kaXZpZHVhbCBjb21wb25lbnRzLiANCg0KPGgzPiBHZXR0aW5nIGNvbXBvbmVudHMgPC9oMz4NCg0KWW91IGNhbiBwdWxsIG91dCBpbmRpdmlkdWFsIHBhcnRzIG9mIHRoZSBkYXRlIHdpdGggdGhlIGZ1bmN0aW9ucyA6IHllYXIoKSwgbW9udGgoKSwgbWRheSgpIFtkYXkgb2YgbW9udGhdLCB5ZGF5KCkgW2RheSBvZiB5ZWFyXSwgd2RheSgpIFtkYXkgb2YgdGhlIHdlZWtdLCBob3VyKCksIG1pbnV0ZSgpLCBhbmQgc2Vjb25kKCkuDQoNCmBgYHtyfQ0KZGF0ZXRpbWUgPC0geW1kX2htcygiMjAxNi0wNy0wOCAxMjozNDo1NiIpDQp5ZWFyKGRhdGV0aW1lKQ0KbW9udGgoZGF0ZXRpbWUpDQptZGF5KGRhdGV0aW1lKQ0KeWRheShkYXRldGltZSkNCndkYXkoZGF0ZXRpbWUpDQpob3VyKGRhdGV0aW1lKQ0KbWludXRlKGRhdGV0aW1lKQ0Kc2Vjb25kKGRhdGV0aW1lKQ0KYGBgDQoNCk1vbnRoKCkgIGFuZCB3ZGF5KCkgY2FuIHNldCBsYWJlbCA9IHRydWUgYW5kIGFiYnIgPSBGQUxTRQ0KDQpgYGB7cn0NCm1vbnRoKGRhdGV0aW1lLCBsYWJlbCA9IFRSVUUsIGFiYnIgPSBGQUxTRSkNCg0Kd2RheShkYXRldGltZSwgbGFiZWwgPSBUUlVFLCBhYmJyID0gVFJVRSkNCg0KYGBgDQoNCmBgYHtyfQ0KZmxpZ2h0c19kdCAlPiUNCiAgbXV0YXRlKHdkYXkgPSB3ZGF5KGRlcF90aW1lLCBsYWJlbCA9IFRSVUUpKSAlPiUNCiAgZ2dwbG90KGFlcyh4ID0gd2RheSkpICsgDQogIGdlb21fYmFyKCkNCmBgYA0KDQoNCg0KDQoNCkxldCdzIGxvb2sgYXQgdGhlIGF2ZXJhZ2UgZGVwYXJ0dXJlIGRlbGF5IGJ5IG1pbnV0ZSB3aWh0aW4gdGhlIGhvdXIuIEl0IGxvb2tzIGxpa2UgZmxpZ2h0cyBsZWF2aW5nIGluIG1pbnV0ZXMgMjAtMzAgYW5kIDUwLTYwIGhhdmUgbXVjaCBsb3dlciBkZWxheXMgdGhhbiB0aGUgcmVzdCBvZiB0aGUgaG91ci4gDQoNCmBgYHtyfQ0KZmxpZ2h0c19kdCAlPiUNCiAgbXV0YXRlKG1pbnV0ZSA9IG1pbnV0ZShkZXBfdGltZSkpICU+JQ0KICBncm91cF9ieShtaW51dGUpICU+JQ0KICBzdW1tYXJpemUoDQogICAgYXZnX2RlbGF5ID0gbWVhbihhcnJfZGVsYXksIG5hLnJtID0gVFJVRSksIA0KICAgIG4gPSBuKCkpICU+JQ0KICBnZ3Bsb3QoYWVzKG1pbnV0ZSwgYXZnX2RlbGF5KSkgKw0KICBnZW9tX2xpbmUoKQ0KDQpgYGANCg0KDQoNCkludGVyZXN0aW5nbHksIHRoZSBzY2hlZHVsZWQgZGVwYXJ0dXJlIHRpbWUgZG9uJ3QgaGF2ZSBhIHNpbWlsYXIgcGF0dGVybi4NCg0KYGBge3J9DQpzY2hlZF9kZXAgPC0gIGZsaWdodHNfZHQgJT4lDQogIG11dGF0ZShtaW51dGUgPSBtaW51dGUoc2NoZWRfZGVwX3RpbWUpKSAlPiUNCiAgZ3JvdXBfYnkobWludXRlKSAlPiUNCiAgc3VtbWFyaXplKGF2Z19kZWxheSA9IG1lYW4oYXJyX2RlbGF5LCBuYS5ybSA9IFRSVUUpLA0KICAgICAgICAgICAgIG4gPSBuKCkpDQpnZ3Bsb3Qoc2NoZWRfZGVwLCBhZXMobWludXRlLCBhdmdfZGVsYXkpKSArDQogICAgICAgICBnZW9tX2xpbmUoKQ0KICAgICAgIA0KYGBgDQoNCg0KDQpgYGB7cn0NCmdncGxvdChzY2hlZF9kZXAsIGFlcyhtaW51dGUsIG4pKSArDQogIGdlb21fbGluZSgpDQpgYGANCg0KPGgyPiBSb3VuZGluZyA8L2gyPg0KDQpBbiBhbHRlcm5hdGl2ZSBhcHByb2FjaCB0byBwbG90dGluZyBpbmRpdmlkdWFsIGNvbXBvbmVudHMgaXMgdG8gcm91bmQgdGhlIGRhdGUgdG8gYSBuZWFyYnkgdW5pdCBvZiB0aW1lLCB3aXRoIGZsb29yX2RhdGUoKSwgcm91bmRfZGF0ZSgpIGFuZCBjZWlsaW5nX2RhdGUoKS4gRWFjaCBjZWlsaW5nX2RhdGUoKSBmdW5jdGlvbiB0YWtlcyBhIHZlY3RvciBvZiBkYXRlcyB0byBhZGp1c3QgYW5kIHRoZW4gdGhlIG5hbWUgb2YgdGhlIHVuaXQgdG8gcm91bmQgZG93biAoZmxvb3IpLiANCg0KDQpgYGB7cn0NCmZsaWdodHNfZHQgJT4lIA0KICBjb3VudCh3ZWVrID0gZmxvb3JfZGF0ZShkZXBfdGltZSwgIndlZWsiKSkgJT4lDQogIGdncGxvdChhZXMod2VlaywgbikpICsNCiAgZ2VvbV9saW5lKCkNCmBgYA0KDQpDb21wdXRpbmcgdGhlIGRpZmZlcmVuY2UgYmV0d2VlbiBhIHJvdW5kZWQgYW5kIHVucm91bmRlZCBkYXRlIGNhbiBiZSBwYXJ0aWN1bGFybHkgdXNlZnVsLiANCg0KPGgyPiBTZXR0aW5nIENvbXBvbmVudHMgPC9oMj4NCg0KYGBge3J9DQpkYXRldGltZSA8LSB5bWRfaG1zKCIyMDE2LTA3LTA4IDEyOjM0OjU2IikNCnllYXIoZGF0ZXRpbWUpIDwtICAyMDIwDQptb250aChkYXRldGltZSkgPC0gMDENCmhvdXIoZGF0ZXRpbWUpIDwtICBob3VyKGRhdGV0aW1lKSArIDINCg0KZGF0ZXRpbWUNCmBgYA0KWW91IGNhbiBhbHNvIGNyZWF0ZSBhIG5ldyBkYXRlLXRpbWUgd2l0aCB1cGRhdGUoKS4gVGhpcyBhbGxvd3MgeW91IHRvIHNldCBtdWx0aWxlIGF2bHVlcyBhdCBvbmNlLg0KDQpgYGB7cn0NCnVwZGF0ZShkYXRldGltZSwgeWVhciA9MjAyMCwgbW9udGg9NCwgbWRheSA9IDIsIGhvdXIgPSAyKQ0KYGBgDQoNCklmIHZhbHVlcyBhcmUgdG9vIGJpZywgdGhleSB3aWxsIHJvbGxvdmVyIHRvIHRoZSBuZXh0IHZhbGlkIGRhdGUNCg0KYGBge3J9DQp5bWQoIjIwMTUtMDItMDEiKSAlPiUNCiAgdXBkYXRlKG1kYXkgPSAzMCkNCg0KeW1kKCIyMDE1LTAyLTAxIikgJT4lDQogIHVwZGF0ZShob3VyID0gNDAwKQ0KYGBgDQoNCg0KSGVyZSwgdXBkYXRlIGlzIHVzZWQgdG8gc2hvdyB0aGUgZGlzdHJpYnV0aW9uIG9mIGZsaWdodHMgYWNyb3NzIHRoZSBjb3Vyc2Ugb2YgdGhlIGRheSBmb3IgZXZlcnkgZGF5IG9mIHRoZSB5ZWFyOg0KDQpgYGB7cn0NCiBmbGlnaHRzX2R0ICU+JQ0KICBtdXRhdGUoZGVwX2hvdXIgPSB1cGRhdGUoZGVwX3RpbWUsIHlkYXkgPSAyMCApKSAlPiUNCiAgZ2dwbG90KGFlcyhkZXBfaG91cikpICsNCiAgZ2VvbV9mcmVxcG9seShiaW53aWR0aCA9IDMwMCkNCg0KYGBgDQoNCg0KU2V0dGluZyBsYXJnZXIgY29tcG9uZW50cyBvZiBhIGRhdGUgdG8gYSBjb25zdGFudCBpcyBhIHBvd2VyZnVsIHRlY2huaXF1ZSB0aGF0IGFsbG93cyB5b3UgdG8gZHJpbGwgZG93biBpbnRvIHRoZSBzbWFsbGVyIGNvbXBvbmVudHMuIA0KDQoNCjxoMz4gRXhlcmNpc2VzPg0KSG93IGRvZXMgdGhlIGRpc3RyaWJ1dGlvbiBvZiBmbGlnaHQgdGltZXMgd2l0aGluIGEgZGF5IGNoYW5nZSBvdmVyIHRoZSBjb3Vyc2Ugb2YgdGhlIHllYXI/DQpgYGB7cn0NCmZsaWdodHNfZHQgJT4lDQogIG11dGF0ZSh0aW1lID0gaG91cihkZXBfdGltZSkgKiAxMDAgKyBtaW51dGUoZGVwX3RpbWUpLA0KICAgICAgICAgbW9uID0gYXMuZmFjdG9yKG1vbnRoDQogICAgICAgICAgICAgICAgICAgICAgICAgKGRlcF90aW1lKSkpICU+JQ0KICBnZ3Bsb3QoYWVzKHggPSB0aW1lLCBncm91cCA9IG1vbiwgY29sb3IgPSBtb24pKSArDQogIGdlb21fZnJlcXBvbHkoYmlud2lkdGggPSAxMDApDQoNCmBgYA0KDQpDb21wYXJlIGRlcF90aW1lLCBzY2hlZF9kZXBfdGltZSBhbmQgZGVwX2RlbGF5LiBBcmUgdGhleSBjb25zaXN0ZW50PyBFeHBsYWluIHlvdXIgZmluZGluZ3MuDQpJZiB0aGV5IGFyZSBjb25zaXN0ZW50LCB0aGVuIGRlcF90aW1lID0gc2NoZWRfZGVwX3RpbWUgKyBkZXBfZGVsYXkuDQoNCg0KYGBge3J9DQpmbGlnaHRzX2R0ICU+JQ0KICBtdXRhdGUoZGVwX3RpbWVfID0gc2NoZWRfZGVwX3RpbWUgKyBkZXBfZGVsYXkgKiA2MCkgJT4lDQogIGZpbHRlcihkZXBfdGltZV8gIT0gZGVwX3RpbWUpICU+JQ0KICBzZWxlY3QoZGVwX3RpbWVfLCBkZXBfdGltZSwgc2NoZWRfZGVwX3RpbWUsIGRlcF9kZWxheSkNCmBgYA0KDQpUaGVyZSBleGlzdCBkaXNjcmVwZW5jaWVzLiBJdCBsb29rcyBsaWtlIHRoZXJlIGFyZSBtaXN0YWtlcyBpbiB0aGUgZGF0ZXMuIFRoZXNlIGFyZSBmbGlnaHRzIGluIHdoaWNoIHRoZSBhY3R1YWwgZGVwYXJ0dXJlIHRpbWUgaXMgb24gdGhlIG5leHQgZGF5IHJlbGF0aXZlIHRvIHRoZSBzY2hlZHVsZWQgZGVwYXJ0dXJlIHRpbWUuIFdlIGZvcmdvdCB0byBhY2NvdW50IGZvciB0aGlzIHdoZW4gY3JlYXRpbmcgdGhlIGRhdGUtdGltZXMuIFRoZSBjb2RlIHdvdWxkIGhhdmUgaGFkIHRvIGNoZWNrIGlmIHRoZSBkZXBhcnR1cmUgdGltZSBpcyBsZXNzIHRoYW4gdGhlIHNjaGVkdWxlZCBkZXBhcnR1cmUgdGltZS4gQWx0ZXJuYXRpdmVseSwgc2ltcGx5IGFkZGluZyB0aGUgZGVsYXkgdGltZSBpcyBtb3JlIHJvYnVzdCBiZWNhdXNlIGl0IHdpbGwgYXV0b21hdGljYWxseSBhY2NvdW50IGZvciBjcm9zc2luZyBpbnRvIHRoZSBuZXh0IGRheS4gPC9wPg0KDQoNCkNvbXBhcmUgYWlyX3RpbWUgd2l0aCB0aGUgZHVyYXRpb24gYmV0d2VlbiB0aGUgZGVwYXJ0dXJlIGFuZCBhcnJpdmFsLiBFeHBsYWluIHlvdXIgZmluZGluZ3MuDQoNCmBgYHtyfQ0KZmxpZ2h0c19kdCAlPiUNCiAgbXV0YXRlKGZsaWdodF9kdXJhdGlvbiA9IGFzLm51bWVyaWMoYXJyX3RpbWUgLSBkZXBfdGltZSksDQogICAgICAgICBhaXJfdGltZV9taW5zID0gYWlyX3RpbWUsDQogICAgICAgICBkaWZmID0gZmxpZ2h0X2R1cmF0aW9uIC0gYWlyX3RpbWVfbWlucykgJT4lDQogIHNlbGVjdChvcmlnaW4sIGRlc3QsIGZsaWdodF9kdXJhdGlvbiwgYWlyX3RpbWVfbWlucywgZGlmZikNCg0KYGBgDQoNCkhvdyBkb2VzIHRoZSBhdmVyYWdlIGRlbGF5IHRpbWUgY2hhbmdlIG92ZXIgdGhlIGNvdXJzZSBvZiBhIGRheT8gU2hvdWxkIHlvdSB1c2UgZGVwX3RpbWUgb3Igc2NoZWRfZGVwX3RpbWU/IFdoeT8gPC9icj4NClVzZSBzY2hlZF9kZXBfdGltZSBiZWNhdXNlIHRoYXQgaXMgdGhlIHJlbGV2YW50IG1ldHJpYyBmb3Igc29tZW9uZSBzY2hlZHVsaW5nIGEgZmxpZ2h0LiBBbHNvLCB1c2luZyBkZXBfdGltZSB3aWxsIGFsd2F5cyBiaWFzIGRlbGF5cyB0byBsYXRlciBpbiB0aGUgZGF5IHNpbmNlIGRlbGF5cyB3aWxsIHB1c2ggZmxpZ2h0cyBsYXRlci4NCg0KDQpgYGB7cn0NCmZsaWdodHNfZHQgJT4lDQogIG11dGF0ZShzY2hlZF9kZXBfaG91ciA9IGhvdXIoc2NoZWRfZGVwX3RpbWUpKSAlPiUNCiAgZ3JvdXBfYnkoc2NoZWRfZGVwX2hvdXIpICU+JQ0KICBzdW1tYXJpc2UoZGVwX2RlbGF5ID0gbWVhbihkZXBfZGVsYXkpKSAlPiUNCiAgZ2dwbG90KGFlcyh5ID0gZGVwX2RlbGF5LCB4ID0gc2NoZWRfZGVwX2hvdXIpKSArDQogIGdlb21fcG9pbnQoKSArDQogIGdlb21fc21vb3RoKCkNCg0KYGBgDQoNCk9uIHdoYXQgZGF5IG9mIHRoZSB3ZWVrIHNob3VsZCB5b3UgbGVhdmUgaWYgeW91IHdhbnQgdG8gbWluaW1pc2UgdGhlIGNoYW5jZSBvZiBhIGRlbGF5PyA8L2JyPg0KU2F0dXJkYXkgaGFzIHRoZSBsb3dlc3QgY29tYmluZWQgZGVsYXlzLiANCg0KYGBge3J9DQpmbGlnaHRzX2R0ICU+JQ0KICBtdXRhdGUoZG93ID0gd2RheShzY2hlZF9kZXBfdGltZSwgbGFiZWwgPSBUUlVFKSkgJT4lDQogIGdyb3VwX2J5KGRvdykgJT4lDQogIHN1bW1hcmlzZShkZXBfZGVsYXkgPSBtZWFuKGRlcF9kZWxheSksDQogICAgICAgICAgICBhcnJfZGVsYXkgPSBtZWFuKGFycl9kZWxheSwgbmEucm0gPSBUUlVFKSkgJT4lDQogIGFycmFuZ2UoZGVzYyhkZXBfZGVsYXkrYXJyX2RlbGF5KSkNCmBgYA0KDQoNCldoYXQgbWFrZXMgdGhlIGRpc3RyaWJ1dGlvbiBvZiBkaWFtb25kcyRjYXJhdCBhbmQgZmxpZ2h0cyRzY2hlZF9kZXBfdGltZSBzaW1pbGFyP1wNCg0KDQpgYGB7cn0NCmdncGxvdChkaWFtb25kcywgYWVzKHggPSBjYXJhdCkpICsgDQogIGdlb21fZGVuc2l0eSgpDQpgYGANCg0KSW4gYm90aCBjYXJhdCBhbmQgc2NoZWRfZGVwX3RpbWUgdGhlcmUgYXJlIGFibm9ybWFsbHkgbGFyZ2UgbnVtYmVycyBvZiB2YWx1ZXMgYXJlIGF0IG5pY2UgImh1bWFuIiBudW1iZXJzLiBJbiBzY2hlZF9kZXBfdGltZSBpdCBpcyBhdCAwMCBhbmQgMzAgbWludXRlcy4gSW4gY2FyYXRzLCBpdCBpcyBhdCAwLCAxLzMsIDEvMiwgMi8zLCA8L3A+DQoNCg0KYGBge3J9DQpnZ3Bsb3QoZGlhbW9uZHMsIGFlcyh4ID0gY2FyYXQgJSUgMSAqIDEwMCkpICsNCiAgZ2VvbV9oaXN0b2dyYW0oYmlud2lkdGggPSAxKQ0KDQpgYGANCg0KSW4gc2NoZWR1bGVkIGRlcGFydHVyZSB0aW1lcyBpdCBpcyAwMCBhbmQgMzAgbWludXRlcywgYW5kIG1pbnV0ZXMgZW5kaW5nIGluIDAgYW5kIDUuDQoNCmBgYHtyfQ0KZ2dwbG90KGZsaWdodHNfZHQsIGFlcyh4ID0gbWludXRlKHNjaGVkX2RlcF90aW1lKSkpICsNCiAgZ2VvbV9oaXN0b2dyYW0oYmlud2lkdGggPSAxKQ0KDQpgYGANCg0KDQpDb25maXJtIG15IGh5cG90aGVzaXMgdGhhdCB0aGUgZWFybHkgZGVwYXJ0dXJlcyBvZiBmbGlnaHRzIGluIG1pbnV0ZXMgMjAtMzAgYW5kIDUwLTYwIGFyZSBjYXVzZWQgYnkgc2NoZWR1bGVkIGZsaWdodHMgdGhhdCBsZWF2ZSBlYXJseS4gSGludDogY3JlYXRlIGEgYmluYXJ5IHZhcmlhYmxlIHRoYXQgdGVsbHMgeW91IHdoZXRoZXIgb3Igbm90IGEgZmxpZ2h0IHdhcyBkZWxheWVkLg0KDQpgYGB7cn0NCmZsaWdodHNfZHQgJT4lDQogIG11dGF0ZShlYXJseSA9IGRlcF9kZWxheSA8IDAsDQogICAgICAgICBtaW51dGUgPSBtaW51dGUoc2NoZWRfZGVwX3RpbWUpKSAlPiUNCiAgZ3JvdXBfYnkobWludXRlKSAlPiUNCiAgc3VtbWFyaXNlKGVhcmx5ID0gbWVhbihlYXJseSkpICU+JQ0KICBnZ3Bsb3QoYWVzKHggPSBtaW51dGUsIHkgPSBlYXJseSkpICsNCiAgZ2VvbV9wb2ludCgpDQpgYGANCkF0IHRoZSBtaW51dGUgbGV2ZWwsIHRoZXJlIGRvZXNuJ3QgYXBwZWFyIHRvIGJlIGFueXRoaW5nLiBCdXQgaWYgZ3JvdXBlZCBpbiAxMCBtaW51dGUgaW50ZXJ2YWxzLCB0aGVyZSBpcyBhIGhpZ2hlciBwcm9wb3J0aW9uIG9mIGVhcmx5IGZsaWdodHMgZHVyaW5nIHRob3NlIG1pbnV0ZXMuDQoNCmBgYHtyfQ0KZmxpZ2h0c19kdCAlPiUNCiAgbXV0YXRlKGVhcmx5ID0gZGVwX2RlbGF5IDwgMCwNCiAgICAgICAgIG1pbnV0ZSA9IG1pbnV0ZShzY2hlZF9kZXBfdGltZSkgJSUgMTApICU+JQ0KICBncm91cF9ieShtaW51dGUpICU+JQ0KICBzdW1tYXJpc2UoZWFybHkgPSBtZWFuKGVhcmx5KSkgJT4lDQogIGdncGxvdChhZXMoeCA9IG1pbnV0ZSwgeSA9IGVhcmx5KSkgKw0KICBnZW9tX3BvaW50KCkNCg0KYGBgDQoNCg0KPGgyPiBUaW1lIFNwYW5zZSA8L2gyPg0KDQpBcml0aG1ldGljIG9mIGRhdGVzLCBpbmNsdWRpbmcgc3VidHJhY3Rpb24sIGFkZGl0aW4sIGFuZCBkaXZpc2lvbi4gIEFuZCB0aGUgdGhyZWUgaW1wb3J0YW50IGNsYXNzZXM6IDwvYnI+DQpkdXJhdGlvbnMgd2hpY2ggcmVwcmVzZW50IGFuIGV4YWN0IG51bWJlciBvZiBzZWNvbmRzIDwvYnI+DQpwZXJpb2RzLCB3aGljaCByZXByZXNlbnQgaHVtYW4gdW5pdHMgbGlrZSB3ZWVrcyBhbmQgbW9udGhzIDwvYnI+DQppbnRlcnZhbHMsIHdoaWNoIHJlcHJlZW50IGEgc3RhcnRpbmcgYW5kIGVuZGluZyBwb2ludHMgPC9wPg0KDQo8aDM+IER1cmF0aW9ucyA8L2gzPg0KDQpJbiBSLCB3aGVuIHlvdSBzdWJ0cmFjdCB0d28gZGF0ZXMsIHlvdSBnZXQgYSBkaWZmdGltZSBvYmplY3QuDQoNCmBgYHtyfQ0KaF9hZ2UgPC0gIHRvZGF5KCkgLSB5bWQoMTk2MzA1MDkpDQpoX2FnZQ0KYGBgDQoNCmBgYHtyfQ0KYXMuZHVyYXRpb24oaF9hZ2UpDQpgYGANCg0KDQoNCg0KDQoNCg0KDQoNCg0KRHVyYXRpb25zIGNvbWUgd2l0aCBhIGJ1bmNoIG9mIGNvbmVuaWVudCBjb25zdHJ1Y3RvcnM6IGRzZWNvbmRzKCksIGRtaW51dGVzKCksIGRob3VycygpIGFuZCBkZGF5cygpLGR3ZWVrcygpLCBkeWVhcnMoKQ0KDQpgYGB7cn0NCmRzZWNvbmRzKDE1KQ0KZG1pbnV0ZXMoMTApDQpkZGF5cygwOjUpDQpgYGANCg0KYGBge3J9DQpkd2Vla3MoMykNCmR5ZWFycygxKQ0KYGBgDQpEdXJhdGlvbiBpcyBhbHdheXMgcmVjb3JkZWQgaW4gc2Vjb25kcy4gTGFyZ2VyIHVuaXRzIGFyZSBjcmVhdGVkIGJ5IGNvbnZlcnRpbmcgbWludXRlcywgaG91cnMsIGRheXMsIHdlZWtzIGFuZCB5ZWFycyB0byBzZWNvbmRzIGF0IHRoZSBzdGFuZGFyZCByYXRlLiBZb3UgY2FuIGFkZCBhbmQgbXVsdGlwbHkgZHVyYXRpb25zLiANCg0KYGBge3J9DQoyICogZHllYXJzKDEpDQpkeWVhcnMoMSkgKyBkd2Vla3MoMTIpICsgZGhvdXJzKDE1KQ0KYGBgDQoNCllvdSBjYW4gYWRkIGFuZCBzdWJ0cmFjdCBkdXJhdGlvbnMgdG8gYW5kIGZyb20gZGF5cy4gDQpgYGB7cn0NCnRvbW9ycm93IDwtICB0b2RheSgpICsgZGRheXMoMSkNCmxzdF95ZWFyIDwtICB0b2RheSgpIC0gZHllYXJzKDEpDQp0b21vcnJvdw0KbHN0X3llYXINCmBgYA0KDQpEdXJhdGlvbiBpbmNsdWRlcyBEYXlsaWdodCBzYXZpbmdzIHRpbWUuIA0KDQo8aDM+IFBlcmlvZHM8L2gzPg0KVG8gc29sdmUgdGhlIERTVCBwcm9ibGVtLCBsdWJyaWRhdGUgcHJvdmlkZXMgcGVyaW9kcy4gUGVyaW9kcyBhcmUgdGltZSBzcGFucyBidXQgZG9uJ3QgaGF2ZSBhIGZpeGVkIGxlbnRoIGluIHNlY29uZHMuIEluc3RlYWQgdGhleSB3b3JrIHdpdGggImh1bWFuIHRpbWVzIiIsIGxpa2UgZGF5cyBhbmQgbW9udGhzLiANCg0KYGBge3J9DQpvbmVfcG0gPC0gIHltZF9obXMoIjIwMTYtMDMtMTIgMTM6MDA6MDAiLCB0eiA9IkFtZXJpY2EvTmV3X1lvcmsiKQ0Kb25lX3BtDQpvbmVfcG0gKyBkZGF5cygxKQ0Kb25lX3BtICsgZGF5cygxKQ0KYGBgDQoNCkxpa2UgZHVyYXRpb25zLCBQZXJpb2RzIGNhbiBiZSBjcmVhdGVkIHdpdGggYSBudW1iZXIgb2YgY29uc3RydWN0b3IgZnVuY3Rpb25zOiBzZWNvbmRzKCksIG1pbnV0ZXMoKSwgaG91cnMoKSwgZGF5cygpLCBtb250aHMoKSwgd2Vla3MoKSwgeWVhcnMoKQ0KDQpgYGB7cn0NCnNlY29uZHMoMTUpDQptaW51dGVzKDEwKQ0KaG91cnMoYygxMiwyNCkpDQpkYXlzKDcpDQptb250aHMoMTo2KQ0Kd2Vla3MoMykNCnllYXJzKDEpDQpgYGANCg0KDQoNCllvdSBjYW4gYWRkIGFuZCBtdWx0aXBseSBwZXJpb2RzDQpgYGB7cn0NCjEwICogKG1vbnRocyg2KSArIGRheXMoMSkpDQpkYXlzKDUwKSArIGhvdXJzKDI1KSArIG1pbnV0ZXMoMikNCmBgYA0KDQpZb3UgY2FuIGFkZCB0aGVtIHRvIGRhdGVzLiBDb21wYXJlZCB0byBkdXJhdGlucywgcGVyaW9kcyBhcmUgbW9yZSBsaWtlbHkgdG8gZG8gd2hhdCB5b3UgZXhwZWN0LiANCg0KYGBge3J9DQp5bWQoJzIwMTYtMDEtMDEnKSArIGR5ZWFycygxKQ0KeW1kKCcyMDE2LTAxLTAxJykgKyB5ZWFycygxKQ0KYGBgDQoNClBlcmlvZHMgY2FuIGJlIHVzZWZ1bCB0byBmaXggZmxpZ2h0cyB0aGF0IGFwcGVhciB0byBhcnJpdmUgYmVmb3JlIHRoZXkgZGVwYXJ0ZWQuDQoNCmBgYHtyfQ0KZmxpZ2h0c19kdCAlPiUNCiAgZmlsdGVyKGFycl90aW1lIDwgZGVwX3RpbWUpDQoNCmBgYA0KDQoNCg0KV2UgY2FuIGZpeCB0aGlzIGJ5IGFkZGluZyBkYXlzKDEpIHRvIHRoZSBhcnJpdmFsIHRpbWUgb2YgZWFjaCBvdmVybmlnaHQgZmxpZ2h0DQoNCmBgYHtyfQ0KZmxpZ2h0c19kdCA8LSAgZmxpZ2h0c19kdCAlPiUNCiAgbXV0YXRlKCANCiAgICBvdmVybmlnaHQgPSBhcnJfdGltZSA8IGRlcF90aW1lLA0KICAgIGFycl90aW1lID0gYXJyX3RpbWUgKyBkYXlzKG92ZXJuaWdodCAqIDEpLA0KICAgIHNjaGVkX2Fycl90aW1lID0gc2NoZWRfYXJyX3RpbWUgKyBkYXlzKG92ZXJuaWdodCAqIDEpKQ0KDQpmbGlnaHRzX2R0DQpgYGANCg0KPGgzPiBJbnRlcnZhbHMgPC9oMz4gDQpgYGB7cn0NCnllYXJzKDEpIC8gZGF5cygxKQ0KDQpgYGANCg0KSWYgeW91IHdhbnQgbW9yZSBhY2N1cmF0ZSBtZWFzdXJlbWVudCwgeW91J2xsIGhhdmUgdG8gdXNlIGFuIGludGVydmFsLiBBbiBpbnRlcnZhbCBpcyBhIGR1cmF0aW9uIHdpdGggYSBzdGFydGluZyBwb2ludC4gVGhhdCBtYWtlcyBpdCBwcmVjaXNlIHNvIHlvdSBjYW4gZGV0ZXJtaW5lIGV4YWN0bHkgb3cgbG9uZyBpdCBpcy4gDQoNCmBgYHtyfQ0KbmV4dF95ZWFyIDwtICB0b2RheSgpICsgeWVhcnMoMSkNCih0b2RheSgpICUtLSUgbmV4dF95ZWFyIC8gZGRheXMoMSkpDQpgYGANCg0KVG8gZmluZCBvdXQgaG93IG1hbnkgcGVyaW9kcyBmYWxsIGludG8gYW4gaW50ZXJ2YWwsIHlvdSBuZWVkIHRvIHN1ZSBpdG5lZ2VyIGRpdmlzaW9uDQoNCmBgYHtyfQ0KICh0b2RheSgpICUtLSUgbmV4dF95ZWFyKSAlLyUgZGF5cygxKQ0KYGBgDQoNCg0KDQpUaGVzZSBhcmUgdGhlIHBlcm1pdHRlZCBhcml0aG1ldGljIG9wZXJhdGlvbnMgYmV0d2VlbiB0aGUgZGlmZmVyZW50IGRhdGEgdHlwZXMuIA0KPGltZyBzcmM9Imh0dHA6Ly9yNGRzLmhhZC5jby5uei9kaWFncmFtcy9kYXRldGltZXMtYXJpdGhtZXRpYy5wbmciPiA8L2ltZz4NCg0KPGgzPiBFeGVyY2lzZXMgPC9oMz4NCg0KV2h5IGlzIHRoZXJlIG1vbnRocygpIGJ1dCBubyBkbW9udGhzKCk/IDwvYnI+DQpUaGVyZSBpcyBubyBkaXJlY3QgdW5hbWJpZ291cyB2YWx1ZSBvZiBtb250aHMgaW4gc2Vjb25kcyA8L3A+DQoNCkV4cGxhaW4gZGF5cyhvdmVybmlnaHQgKiAxKSB0byBzb21lb25lIHdobyBoYXMganVzdCBzdGFydGVkIGxlYXJuaW5nIFIuIEhvdyBkb2VzIGl0IHdvcms/IDwvYnI+DQoNCm92ZXJuaWdodCBpcyBlcXVhbCB0byBUUlVFICgxKSBvciBGQUxTRSAoMCkuIFNvIGlmIGl0IGlzIGFuIG92ZXJuaWdodCBmbGlnaHQsIHRoaXMgYmVjb21lcyAxIGRheSwgYW5kIGlmIG5vdCwgdGhlbiBvdmVybmlnaHQgPSAwLCBhbmQgbm8gZGF5cyBhcmUgYWRkZWQgdG8gdGhlIGRhdGUuIDwvcD4NCg0KDQpDcmVhdGUgYSB2ZWN0b3Igb2YgZGF0ZXMgZ2l2aW5nIHRoZSBmaXJzdCBkYXkgb2YgZXZlcnkgbW9udGggaW4gMjAxNS4gDQoNCmBgYHtyfQ0KeW1kKCIyMDE1LTAxLTAxIikgKyBtb250aHMoMDoxMSkNCmBgYA0KDQpDcmVhdGUgYSB2ZWN0b3Igb2YgZGF0ZXMgZ2l2aW5nIHRoZSBmaXJzdCBkYXkgb2YgZXZlcnkgbW9udGggaW4gdGhlIGN1cnJlbnQgeWVhci48L2JyPg0KYGBge3J9DQpmbG9vcl9kYXRlKHRvZGF5KCksIHVuaXQgPSAieWVhciIpICsgbW9udGhzKDA6MTEpDQpgYGANCg0KV3JpdGUgYSBmdW5jdGlvbiB0aGF0IGdpdmVuIHlvdXIgYmlydGhkYXkgKGFzIGEgZGF0ZSksIHJldHVybnMgaG93IG9sZCB5b3UgYXJlIGluIHllYXJzLg0KDQpgYGB7cn0NCmFnZSA8LSAgZnVuY3Rpb24oYmRheSkgew0KICAoYmRheSAlLS0lIHRvZGF5KCkpICUvJSB5ZWFycygxKQ0KfQ0KDQphZ2UoeW1kKCcxOTYzLTA1LTA5JykpDQoNCmBgYA0KPGgyPiBUaW1lIFpvbmVzIDwvaDI+DQpSIHVzZXMgaW50ZXJuYXRpb25hbCBzdGFuZGFyZCBJQU5BIHRpbWUgem9uZXMuIFRoZXNlIHVzZSBhIGNvbnNpc3RlbnQgbmFtaW5nIHNjaGVtZSB3aXRoICIvIiB0eXBpY2FsbHkgaW4gdGhlIGZvcm0gIlw8Y29udGluZW50XD5cL1w8Y2l0eVw+IiA8L3A+DQpZb3UgY2FuIGZpbmQgb3V0IHlvdXIgY3VycmVudCB0aW1lIHpvbmUgaXMgd2l0aCBTeXMudGltZXpvbmUoKQ0KDQpgYGB7cn0NClN5cy50aW1lem9uZSgpDQpTeXMudGltZSgpDQpgYGANClRvIHNlZSB0aGUgY29tcGxldGUgbGlzdCBvZiBhbGwgdGltZSB6b25lIG5hbWVzIHdpdGggT2xzb25OYW1lcygpDQoNCmBgYHtyfQ0KbGVuZ3RoKE9sc29uTmFtZXMoKSkNCmhlYWQoT2xzb25OYW1lcygpKQ0KYGBgDQoNClRvIGNoYW5nZSB0aGUgdW5kZXJseWluZyBpbnN0YW50IGluIHRpbWUgLSBmb3JjZV90eigpLiBVc2UgdGhpcyB3aGUgeW91IGhhdmUgYW4gaW5zdGFudCB0aGF0IGhhcyBiZWVuIGxhYmVsZWQgd2l0aCB0aGUgaW5jb3JyZWN0IHRpbWUgem9uZSwgYW5kIHlvdSBuZWVkIHRvIGZpeCBpdA0KDQpgYGB7cn0NCngxIDwtICB5bWRfaG1zKCIyMDE1LTA2LTAxIDEyOjAwOjAwIiwgdHogPSAiQW1lcmljYS9OZXdfWW9yayIpDQp4MiA8LSAgeW1kX2htcygiMjAxNS0wNi0wMSAxODowMDowMCIsIHR6ID0gIkV1cm9wZS9Db3BlbmhhZ2VuIikNCngzIDwtICB5bWRfaG1zKCIyMDE1LTA2LTAyIDA0OjAwOjAwIiwgdHogPSAiUGFjaWZpYy9BdWNrbGFuZCIpDQp4NCA8LSBjKHgxLHgyLHgzKQ0KeDRiIDwtICBmb3JjZV90eih4NCwgdHpvbmUgPSAiQXVzdHJhbGlhL0xvcmRfSG93ZSIpDQp4NGINCmBgYA0KDQoNCg0KDQoNCg0KDQoNCg0KDQoNCg0KDQoNCg0KDQoNCg0KDQoNCg0KDQoNCg0KDQoNCg0KDQoNCg0KDQoNCg0KDQoNCkV4ZXJjaXNlIGFuc3dlcnMgZnJvbSA6IGh0dHBzOi8vanJub2xkLmdpdGh1Yi5pby9lNHFmL2RhdGVzLWFuZC10aW1lcy5odG1sIA0K