Dates and Times with Lubridate

Prerequisites

library(tidyverse)
library(lubridate)
library(nycflights13)

Creating date/times

The three types of date/time data that refer to an instant in time:
  • Date. Tibbles print this as <date>
  • time within a day. Tibbles print this as <time>
  • 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