Dates and Times in R

R doesn’t know something is a date or time unless you tell it.

ISO 8601 dates = YYYY-MM-DD

Specifying dates

If you have a character string that represents a date in the ISO 8601 standard you can turn it into a Date using the as.Date() function. Just pass the character string (or a vector of character strings) as the first argument.

# The date R 3.0.0 was released
x <- "2013-04-03"

# Examine structure of x
str(x)
 chr "2013-04-03"
# Use as.Date() to interpret x as a date
x_date <- as.Date(x)

# Examine structure of x_date
str(x_date)
 Date[1:1], format: "2013-04-03"
# Store April 10 2014 as a Date
april_10_2014 <- as.Date("2014-04-10")

Automatic import

Sometimes you’ll need to input a couple of dates by hand using as.Date() but it’s much more common to have a column of dates in a data file.

Some functions that read in data will automatically recognize and parse dates in a variety of formats. In particular the import functions, like read_csv(), in the readr package will recognize dates in a few common formats.

There is also the anytime() function in the anytime package whose sole goal is to automatically parse strings as dates regardless of the format.

# Load the readr package
library(readr)

# Use read_csv() to import rversions.csv
releases <- read_csv("rversions.csv")
Parsed with column specification:
cols(
  major = col_double(),
  minor = col_double(),
  patch = col_double(),
  date = col_date(format = ""),
  datetime = col_datetime(format = ""),
  time = col_time(format = ""),
  type = col_character()
)
# Examine the structure of the date column
str(releases$date)
 Date[1:105], format: "1997-12-04" "1997-12-21" "1998-01-10" "1998-03-14" "1998-05-02" "1998-06-14" "1998-06-14" ...
# Load the anytime package
library(anytime)
package 㤼㸱anytime㤼㸲 was built under R version 3.6.3
# Various ways of writing Sep 10 2009
sep_10_2009 <- c("September 10 2009", "2009-09-10", "10 Sep 2009", "09-10-2009")

# Use anytime() to parse sep_10_2009
anytime(sep_10_2009)
[1] "2009-09-10 01:00:00 BST" "2009-09-10 01:00:00 BST" "2009-09-10 01:00:00 BST"
[4] "2009-09-10 01:00:00 BST"

Sometimes these functions won’t work, especially if dates are ambiguous (e.g. Is 2004-10-4, Oct 4th or April 10th?)

Plotting

If you plot a Date on the axis of a plot, you expect the dates to be in calendar order, and that’s exactly what happens with plot() or ggplot().

We’ll make some plots with the R version releases data using ggplot2. There are two big differences when a Date is on an axis:

  1. If you specify limits they must be Date objects.
  2. To control the behavior of the scale you use the scale_x_date() function.
library(ggplot2)
package 㤼㸱ggplot2㤼㸲 was built under R version 3.6.3Registered S3 method overwritten by 'dplyr':
  method           from
  print.rowwise_df     
# Set the x axis to the date column
ggplot(releases, aes(x = date, y = type)) +
  geom_line(aes(group = 1, color = factor(major)))


# Limit the axis to between 2010-01-01 and 2014-01-01
ggplot(releases, aes(x = date, y = type)) +
  geom_line(aes(group = 1, color = factor(major))) +
  xlim(as.Date("2010-01-01"), as.Date("2014-01-01"))


# Specify breaks every ten years and labels with "%Y"
ggplot(releases, aes(x = date, y = type)) +
  geom_line(aes(group = 1, color = factor(major))) +
  scale_x_date(date_breaks = "10 years", date_labels = "%Y")

Arithmetic and logical operators

Since Date objects are internally represented as the number of days since 1970-01-01 you can do basic math and comparisons with dates. You can compare dates with the usual logical operators (<, ==, > etc.), find extremes with min() and max(), and even subtract two dates to find out the time between them.

Sys.date() in the code, it simply returns today’s date.

library(dplyr)

Attaching package: 㤼㸱dplyr㤼㸲

The following objects are masked from 㤼㸱package:lubridate㤼㸲:

    intersect, setdiff, union

The following objects are masked from 㤼㸱package:stats㤼㸲:

    filter, lag

The following objects are masked from 㤼㸱package:base㤼㸲:

    intersect, setdiff, setequal, union
# Find the largest date
last_release_date <- max(releases$date)

# Filter row for last release
last_release <- filter(releases, date == last_release_date)

# Print last_release
print(last_release)

# How long since last release?
Sys.Date() - last_release$date
Time difference of 1061 days

Date times

ISO 8601 = HH:MM:SS

Getting datetimes into R

Just like dates without times, if you want R to recognize a string as a datetime you need to convert it, although now you use as.POSIXct(). as.POSIXct() expects strings to be in the format YYYY-MM-DD HH:MM:SS.

The only tricky thing is that times will be interpreted in local time based on your machine’s set up. You can check your timezone with Sys.timezone(). If you want the time to be interpreted in a different timezone, you just set the tz argument of as.POSIXct().

# Use as.POSIXct to enter the datetime 
as.POSIXct("2010-10-01 12:12:00")
[1] "2010-10-01 12:12:00 BST"
# Use as.POSIXct again but set the timezone to `"America/Los_Angeles"`
as.POSIXct("2010-10-01 12:12:00", tz = "America/Los_Angeles")
[1] "2010-10-01 12:12:00 PDT"
# Use read_csv to import rversions.csv
releases <- read_csv("rversions.csv")
Parsed with column specification:
cols(
  major = col_double(),
  minor = col_double(),
  patch = col_double(),
  date = col_date(format = ""),
  datetime = col_datetime(format = ""),
  time = col_time(format = ""),
  type = col_character()
)
# Examine structure of datetime column
str(releases$datetime)
 POSIXct[1:105], format: "1997-12-04 08:47:58" "1997-12-21 13:09:22" "1998-01-10 00:31:55" "1998-03-14 19:25:55" ...

Datetimes behave nicely too

ust like Date objects, you can plot and do math with POSIXct objects.

As an example, in this exercise you’ll see how quickly people download new versions of R, by examining the download logs from the RStudio CRAN mirror.

R 3.2.0 was released at “2015-04-16 07:13:33” so cran-logs_2015-04-17.csv contains a random sample of downloads on the 16th, 17th and 18th.

# Import "cran-logs_2015-04-17.csv" with read_csv()
logs <- read_csv("cran-logs_2015-04-17.csv")
Parsed with column specification:
cols(
  datetime = col_datetime(format = ""),
  r_version = col_character(),
  country = col_character()
)
# Print logs
print(logs)

# Store the release time as a POSIXct object
release_time <- as.POSIXct("2015-04-16 07:13:33", tz = "UTC")

# When is the first download of 3.2.0?
logs %>% 
  filter(logs$datetime > release_time,
    r_version == "3.2.0")

# Examine histograms of downloads by version
ggplot(logs, aes(x = datetime)) +
  geom_histogram() +
  geom_vline(aes(xintercept = as.numeric(release_time)))+
  facet_wrap(~ r_version, ncol = 1)

Did you see how it takes about two days for downloads of the new version (3.2.0) to overtake downloads of the old version (3.1.3)?

Parsing and Manipulating Dates and Times with lubridate

Parsing dates with lubridate

Selecting the right parsing function

lubridate provides a set of functions for parsing dates of a known order. For example, ymd() will parse dates with year first, followed by month and then day. The parsing is flexible, for example, it will parse the m whether it is numeric (e.g. 9 or 09), a full month name (e.g. September), or an abbreviated month name (e.g. Sep).

All the functions with y, m and d in any order exist. If your dates have times as well, you can use the functions that start with ymd, dmy, mdy or ydm and are followed by any of _h, _hm or _hms.

To see all the functions available look at ymd() for dates and ymd_hms() for datetimes.

library(lubridate)

# Parse x 
x <- "2010 September 20th" # 2010-09-20
ymd(x)
[1] "2010-09-20"
# Parse y 
y <- "02.01.2010"  # 2010-01-02
dmy(y)
[1] "2010-01-02"
# Parse z 
z <- "Sep, 12th 2010 14:00"  # 2010-09-12T14:00
mdy_hm(z)
[1] "2010-09-12 14:00:00 UTC"

Did you notice the message after you called library(lubridate)? Whenever you see an object “is masked by”, it means an object in the package, in this case the date() function in lubridate has the same name as an object in another loaded package, in this case date() in the base package. If you ask for date() you’ll get the lubridate one, you can always get the one it masked with base::date().

Specifying an order with parse_date_time()

What about if you have something in a really weird order like dym_msh? There’s no named function just for that order, but that is where parse_date_time() comes in. parse_date_time() takes an additional argument, orders, where you can specify the order of the components in the date.

or example, to parse “2010 September 20th” you could say parse_date_time(“2010 September 20th”, orders = “ymd”) and that would be equivalent to using the ymd() function from the previous exercise.

One advantage of parse_date_time() is that you can use more format characters. For example, you can specify weekday names with A, I for 12 hour time, am/pm indicators with p and many others.

Another big advantage is that you can specify a vector of orders, and that allows parsing of dates where multiple formats might be used.

# Specify an order string to parse x
x <- "Monday June 1st 2010 at 4pm"
parse_date_time(x, orders = "AmdyIp")
[1] "2010-06-01 16:00:00 UTC"
# Specify order to include both "mdy" and "dmy"
two_orders <- c("October 7, 2001", "October 13, 2002", "April 13, 2003", 
  "17 April 2005", "23 April 2017")
parse_date_time(two_orders, orders = c("mdy", "dmy"))
[1] "2001-10-07 UTC" "2002-10-13 UTC" "2003-04-13 UTC" "2005-04-17 UTC" "2017-04-23 UTC"
# Specify order to include "dOmY", "OmY" and "Y"
short_dates <- c("11 December 1282", "May 1372", "1253")
parse_date_time(short_dates, orders = c("dOmY", "OmY", "Y"))
[1] "1282-12-11 UTC" "1372-05-01 UTC" "1253-01-01 UTC"

Did you notice that when a date component is missing, it’s just set to 1? For example, the input 1253 resulted in the date 1253-01-01.

Weather in Auckland

Import daily weather data

In practice you won’t be parsing isolated dates and times, they’ll be part of a larger dataset. We’ll practice your lubridate skills in context by working with weather data from Auckland NZ.

There are two data sets: akl_weather_daily.csv a set of once daily summaries for 10 years, and akl_weather_hourly_2016.csv observations every half hour for 2016.

library(lubridate)
library(readr)
library(dplyr)
library(ggplot2)

# Import CSV with read_csv()
akl_daily_raw <- read_csv("akl_weather_daily.csv")
Parsed with column specification:
cols(
  date = col_character(),
  max_temp = col_double(),
  min_temp = col_double(),
  mean_temp = col_double(),
  mean_rh = col_double(),
  events = col_character(),
  cloud_cover = col_double()
)
# Print akl_daily_raw
akl_daily_raw

# Parse date 
akl_daily <- akl_daily_raw %>%
  mutate(date = ymd(date))

# Print akl_daily
akl_daily

# Plot to check work
ggplot(akl_daily, aes(x = date, y = max_temp)) +
  geom_line() 

Can you see when it is hot in Auckland? Those temperatures are in farenheit. Yup, summer falls in Dec-Jan-Feb.

Import hourly weather data

The hourly data is a little different. The date information is spread over three columns year, month and mday, so you’ll need to use make_date() to combine them.

Then the time information is in a separate column again, time. It’s quite common to find date and time split across different variables. One way to construct the datetimes is to paste the date and time together and then parse them.

# Import "akl_weather_hourly_2016.csv"
akl_hourly_raw <- read_csv("akl_weather_hourly_2016.csv")
Parsed with column specification:
cols(
  year = col_double(),
  month = col_double(),
  mday = col_double(),
  time = col_time(format = ""),
  temperature = col_double(),
  weather = col_character(),
  conditions = col_character(),
  events = col_character(),
  humidity = col_double(),
  date_utc = col_datetime(format = "")
)
# Print akl_hourly_raw
akl_hourly_raw
# Use make_date() to combine year, month and mday 
akl_hourly  <- akl_hourly_raw  %>% 
  mutate(date = make_date(year = year, month = month, day = mday))

# Parse datetime_string 
akl_hourly <- akl_hourly  %>% 
  mutate(
    datetime_string = paste(date, time, sep = "T"),
    datetime = parse_date_time(datetime_string, orders = "ymdHMS")
  )
# Print date, time and datetime columns of akl_hourly
akl_hourly %>% select(date, time, datetime)

# Plot to check work
ggplot(akl_hourly, aes(x = datetime, y = temperature)) +
  geom_line()

it’s interesting how the day to day variation is about half the size of the yearly variation.

Extracting parts of a datetime

What can you extract?

Components of a datetime can be extracted by lubridate functions with the same name like year(), month(), day(), hour(), minute() and second(). They all work the same way just pass in a datetime or vector of datetimes.

There are also a few useful functions that return other aspects of a datetime like if it occurs in the morning am(), during daylight savings dst(), in a leap_year(), or which quarter() or semester() it occurs in.

release_time <- releases[["datetime"]]
head(release_time)
[1] "1997-12-04 08:47:58 UTC" "1997-12-21 13:09:22 UTC" "1998-01-10 00:31:55 UTC"
[4] "1998-03-14 19:25:55 UTC" "1998-05-02 07:58:17 UTC" "1998-06-14 12:56:20 UTC"
# Examine the head() of the months of release_time
head(month(release_time))
[1] 12 12  1  3  5  6
# Extract the month of releases 
month(release_time) %>% table()
.
 1  2  3  4  5  6  7  8  9 10 11 12 
 5  6  8 18  5 16  4  7  2 15  6 13 
# Extract the year of releases
year(release_time) %>% table()
.
1997 1998 1999 2000 2001 2002 2003 2004 2005 2006 2007 2008 2009 2010 2011 2012 2013 2014 2015 2016 2017 
   2   10    9    6    6    5    5    4    4    4    4    6    5    4    6    4    4    4    5    5    3 
# How often is the hour before 12 (noon)?
mean(hour(release_time) < 12)
[1] 0.752381
# How often is the release in am?
mean(am(release_time))
[1] 0.752381

R versions have historically been released most in April, June, October and December, 1998 saw 10 releases and about 75% of releases happen in the morning (at least according to UTC).

Adding useful labels

Sometimes it’s nicer (especially for plotting or tables) to have named months. Both the month() and wday() (day of the week) functions have additional arguments label and abbr to achieve just that. Set label = TRUE to have the output labelled with month (or weekday) names, and abbr = FALSE for those names to be written in full rather than abbreviated.

head(month(release_time, label = TRUE, abbr = FALSE))
# Use wday() to tabulate release by day of the week
wday(releases$datetime) %>% table()
.
 1  2  3  4  5  6  7 
 3 29  9 12 18 31  3 
# Add label = TRUE to make table more readable
wday(releases$datetime, label = TRUE) %>% table()
.
Sun Mon Tue Wed Thu Fri Sat 
  3  29   9  12  18  31   3 
# Create column wday to hold labelled week days
releases$wday <- wday(releases$datetime, label = TRUE)

# Plot barchart of weekday by type of release
ggplot(releases, aes(x = wday)) +
  geom_bar() +
  facet_wrap(~ type, ncol = 1, scale = "free_y")

Looks like not too many releases occur on the weekends, and there is quite a different weekday pattern between minor and patch releases.

Extracting for plotting

Extracting components from a datetime is particularly useful when exploring data. Earlier in the chapter you imported daily data for weather in Auckland, and created a time series plot of ten years of daily maximum temperature. While that plot gives you a good overview of the whole ten years, it’s hard to see the annual pattern.

We’ll use components of the dates to help explore the pattern of maximum temperature over the year. The first step is to create some new columns to hold the extracted pieces, then you’ll use them in a couple of plots.

library(ggplot2)
library(dplyr)
library(ggridges)
package 㤼㸱ggridges㤼㸲 was built under R version 3.6.3
# Add columns for year, yday and month
akl_daily <- akl_daily %>%
  mutate(
    year = year(date),
    yday = yday(date),
    month = month(date, label = TRUE))

# Plot max_temp by yday for all years
ggplot(akl_daily, aes(x = yday, y = max_temp)) +
  geom_line(aes(group = year), alpha = 0.5)


# Examine distribution of max_temp by month
ggplot(akl_daily, aes(x = max_temp, y = month, height = ..density..)) +
  geom_density_ridges(stat = "density")

Both plots give a great view into both the expected temperatures and how much they vary. Looks like Jan, Feb and Mar are great months to visit if you want warm temperatures. Did you notice the warning messages? These are a consequence of some missing values in the max_temp column. They are a reminder to think carefully about what you might miss by ignoring missing values.

Extracting for filtering and summarizing

Another reason to extract components is to help with filtering observations or creating summaries. For example, if you are only interested in observations made on weekdays (i.e. not on weekends) you could extract the weekdays then filter out weekends, e.g. wday(date) %in% 2:6.

In the last exercise you saw that January, February and March were great times to visit Auckland for warm temperatures, but will you need a raincoat?

# Create new columns hour, month and rainy
akl_hourly <- akl_hourly %>%
  mutate(
    hour = hour(datetime),
    month = month(datetime, label = TRUE),
    rainy = weather == "Precipitation"
  )

# Filter for hours between 8am and 10pm (inclusive)
akl_day <- akl_hourly %>% 
  filter(hour >= 8, hour<=22)

# Summarise for each date if there is any rain
rainy_days <- akl_day %>% 
  group_by(month, date) %>%
  summarise(
    any_rain = any(rainy)
  )

# Summarise for each month, the number of days with rain
rainy_days %>% 
  summarise(
    days_rainy = sum(any_rain)
  )

At least in 2016, it looks like you’ll still need to pack a raincoat if you visit in Jan, Feb or March. Months of course are different lengths so we should really correct for that, take a look at days_in_month() for helping with that.

Rounding datetimes

round_date() rounds a date to the nearest value, floor_date() rounds down, and ceiling_date() rounds up.

All three take a unit argument which specifies the resolution of rounding. You can specify “second”, “minute”, “hour”, “day”, “week”, “month”, “bimonth”, “quarter”, “halfyear”, or “year”. Or, you can specify any multiple of those units, e.g. “5 years”, “3 minutes” etc.

r_3_4_1 <- ymd_hms("2016-05-03 07:13:28 UTC")

# Round down to day
floor_date(r_3_4_1, unit = "day")
[1] "2016-05-03 UTC"
# Round to nearest 5 minutes
round_date(r_3_4_1, unit = "5 minutes")
[1] "2016-05-03 07:15:00 UTC"
# Round up to week 
ceiling_date(r_3_4_1, unit = "week")
[1] "2016-05-08 UTC"
# Subtract r_3_4_1 rounded down to day
r_3_4_1 - floor_date(r_3_4_1, unit = "day")
Time difference of 7.224444 hours

That last technique of subtracting a rounded datetime from an unrounded one is a really useful trick to remember.

Rounding with the weather data

When is rounding useful? In a lot of the same situations extracting date components is useful. The advantage of rounding over extracting is that it maintains the context of the unit. For example, extracting the hour gives you the hour the datetime occurred, but you lose the day that hour occurred on (unless you extract that too), on the other hand, rounding to the nearest hour maintains the day, month and year.

We’ll explore how many observations per hour there really are in the hourly Auckland weather data.

# Create day_hour, datetime rounded down to hour
akl_hourly <- akl_hourly %>%
  mutate(
    day_hour = floor_date(datetime, unit = "hour")
  )

# Count observations per hour  
akl_hourly %>% 
  count(day_hour) 

# Find day_hours with n != 2  
akl_hourly %>% 
  count(day_hour) %>%
  filter(n != 2) %>% 
  arrange(desc(n))

92 hours that don’t have two measurements. Interestingly there are four measurements on 2016-04-03 and 2016-09-25, they happen to be the days Daylight Saving starts and ends.

Arithmetic with Dates and Times

Taking differenes of datetimes

How long has it been?

To get finer control over a difference between datetimes use the base function difftime(). For example instead of time1 - time2, you use difftime(time1, time2).

difftime() takes an argument units which specifies the units for the difference. Your options are “secs”, “mins”, “hours”, “days”, or “weeks”

We’ll also see the lubridate functions today() and now() which when called with no arguments return the current date and time in your system’s timezone.

# The date of landing and moment of step
date_landing <- mdy("July 20, 1969")
moment_step <- mdy_hms("July 20, 1969, 02:56:15", tz = "UTC")

# How many days since the first man on the moon?
difftime(today(), date_landing, units = "days")
Time difference of 18573 days
# How many seconds since the first man on the moon?
difftime(now(), moment_step, units = "secs")
Time difference of 1604763817 secs

How many seconds are in a day?

How many seconds are in a day? There are 24 hours in a day, 60 minutes in an hour, and 60 seconds in a minute, so there should be 246060 = 86400 seconds, right?

Not always!

# Three dates
mar_11 <- ymd_hms("2017-03-11 12:00:00", 
  tz = "America/Los_Angeles")
mar_12 <- ymd_hms("2017-03-12 12:00:00", 
  tz = "America/Los_Angeles")
mar_13 <- ymd_hms("2017-03-13 12:00:00", 
  tz = "America/Los_Angeles")

# Difference between mar_13 and mar_12 in seconds
difftime(mar_13, mar_12, units = "secs")
Time difference of 86400 secs
# Difference between mar_12 and mar_11 in seconds
difftime(mar_12, mar_11, units = "secs")
Time difference of 82800 secs

Why would a day only have 82800 seconds? At 2am on Mar 12th 2017, Daylight Savings started in the Pacific timezone. That means a whole hour of seconds gets skipped between noon on the 11th and noon on the 12th

Time spans

Adding or subtracting a time span to a datetime

A common use of time spans is to add or subtract them from a moment in time. For, example to calculate the time one day in the future from mar_11 you could do either of:

mar_11 + days(1)
mar_11 + ddays(1)

If you want to account for the fact that time units, in this case days, have different lengths (i.e. due to daylight savings), you want a period days(). If you want the time 86400 seconds in the future you use a duration ddays().

# Add a period of one week to mon_2pm
mon_2pm <- dmy_hm("27 Aug 2018 14:00")
mon_2pm + weeks(1)
[1] "2018-09-03 14:00:00 UTC"
# Add a duration of 81 hours to tue_9am
tue_9am <- dmy_hm("28 Aug 2018 9:00")
tue_9am + hours(81)
[1] "2018-08-31 18:00:00 UTC"
# Subtract a period of five years from today()
today() - years(5)
[1] "2015-05-26"
# Subtract a duration of five years from today()
today() - dyears(5)
[1] "2015-05-28"

Why did subtracting a duration of five years from today, give a different answer to subtracting a period of five years? Periods know about leap years, and since five years ago includes at least one leap year (assuming you aren’t taking this course in 2100) the period of five years is longer than the duration of 365*5 days.

When dealing with human interpretaions of dates and time you want to use periods.

Arithmetic with timespans

You can add and subtract timespans to create different length timespans, and even multiply them by numbers. For example, to create a duration of three days and three hours you could do: ddays(3) + dhours(3), or 3*ddays(1) + 3*dhours(1) or even 3*(ddays(1) + dhours(1)).

There was an eclipse over North America on 2017-08-21 at 18:26:40. It’s possible to predict the next eclipse with similar geometry by calculating the time and date one Saros in the future. A Saros is a length of time that corresponds to 223 Synodic months, a Synodic month being the period of the Moon’s phases, a duration of 29 days, 12 hours, 44 minutes and 3 seconds.

# Time of North American Eclipse 2017
eclipse_2017 <- ymd_hms("2017-08-21 18:26:40")

# Duration of 29 days, 12 hours, 44 mins and 3 secs
synodic <- ddays(29) + dhours(12) + dminutes(44) + dseconds(3)

# 223 synodic months
saros <- 223*synodic

# Add saros to eclipse_2017
eclipse_2017 + saros
[1] "2035-09-02 02:09:49 UTC"

2035 is a long way away for an eclipse, but luckily there are eclipses on different Saros cycles, so you can see one much sooner.

Generating sequences of datetimes

By combining addition and multiplication with sequences you can generate sequences of datetimes. For example, you can generate a sequence of periods from 1 day up to 10 days with:

1:10 * days(1)

Then by adding this sequence to a specific datetime, you can construct a sequence of datetimes from 1 day up to 10 days into the future

today() + 1:10 * days(1)
# Add a period of 8 hours to today
today_8am <- today() + hours(8)

# Sequence of two weeks from 1 to 26
every_two_weeks <- 1:26 * weeks(2)

# Create datetime for every two weeks for a year
every_two_weeks + today_8am
 [1] "2020-06-09 08:00:00 UTC" "2020-06-23 08:00:00 UTC" "2020-07-07 08:00:00 UTC"
 [4] "2020-07-21 08:00:00 UTC" "2020-08-04 08:00:00 UTC" "2020-08-18 08:00:00 UTC"
 [7] "2020-09-01 08:00:00 UTC" "2020-09-15 08:00:00 UTC" "2020-09-29 08:00:00 UTC"
[10] "2020-10-13 08:00:00 UTC" "2020-10-27 08:00:00 UTC" "2020-11-10 08:00:00 UTC"
[13] "2020-11-24 08:00:00 UTC" "2020-12-08 08:00:00 UTC" "2020-12-22 08:00:00 UTC"
[16] "2021-01-05 08:00:00 UTC" "2021-01-19 08:00:00 UTC" "2021-02-02 08:00:00 UTC"
[19] "2021-02-16 08:00:00 UTC" "2021-03-02 08:00:00 UTC" "2021-03-16 08:00:00 UTC"
[22] "2021-03-30 08:00:00 UTC" "2021-04-13 08:00:00 UTC" "2021-04-27 08:00:00 UTC"
[25] "2021-05-11 08:00:00 UTC" "2021-05-25 08:00:00 UTC"

The tricky thing about months

What should ymd(“2018-01-31”) + months(1) return? Should it be 30, 31 or 28 days in the future? Try it. In general lubridate returns the same day of the month in the next month, but since the 31st of February doesn’t exist lubridate returns a missing value, NA.

There are alternative addition and subtraction operators: %m+% and %m-% that have different behavior. Rather than returning an NA for a non-existent date, they roll back to the last existing date.

jan_31 <- as.Date("2020-01-31")
# A sequence of 1 to 12 periods of 1 month
month_seq <- 1:12 * months(1)

# Add 1 to 12 months to jan_31
jan_31 + month_seq
 [1] NA           "2020-03-31" NA           "2020-05-31" NA           "2020-07-31" "2020-08-31"
 [8] NA           "2020-10-31" NA           "2020-12-31" "2021-01-31"
# Replace + with %m+%
jan_31 %m+% month_seq
 [1] "2020-02-29" "2020-03-31" "2020-04-30" "2020-05-31" "2020-06-30" "2020-07-31" "2020-08-31"
 [8] "2020-09-30" "2020-10-31" "2020-11-30" "2020-12-31" "2021-01-31"
# Replace + with %m-%
jan_31 %m-% month_seq
 [1] "2019-12-31" "2019-11-30" "2019-10-31" "2019-09-30" "2019-08-31" "2019-07-31" "2019-06-30"
 [8] "2019-05-31" "2019-04-30" "2019-03-31" "2019-02-28" "2019-01-31"

use these operators with caution, unlike + and -, you might not get x back from x %m+% months(1) %m-% months(1). If you’d prefer that the date was rolled forward check out add_with_rollback() which has roll_to_first argument.

Intervals

Examining intervals. Reigns of kings and queens

You can create an interval by using the operator %–% with two datetimes. For example ymd(“2001-01-01”) %–% ymd(“2001-12-31”) creates an interval for the year of 2001. Once you have an interval you can find out certain properties like its start, end and length with int_start(), int_end() and int_length() respectively.

monarchs <- read.csv("monarchs.csv", stringsAsFactors=FALSE)
# Create an interval for reign
monarchs <- monarchs %>%
  mutate(reign = from %--% to) 

# Find the length of reign, and arrange
monarchs %>%
  mutate(length = int_length (reign)) %>% 
  arrange(desc(length)) %>%
  select(name, length, dominion)

The current queen, Elizabeth II, has ruled for 2070144000 seconds…you’ll see a better way to display the length later. If you know your British monarchs, you might notice George III doesn’t appear in the the top 5. In this data, his reign is spread over two rows for U.K. And Great Britain and you would need to add their lengths to see his total reign.

Comparing intervals and datetimes

A common task with intervals is to ask if a certain time is inside the interval or whether it overlaps with another interval.

The operator %within% tests if the datetime (or interval) on the left hand side is within the interval of the right hand side.

For example, if y2001 is the interval covering the year 2001,

y2001 <- ymd("2001-01-01") %--% ymd("2001-12-31")

Then ymd(“2001-03-30”) %within% y2001 will return TRUE and ymd(“2002-03-30”) %within% y2001 will return FALSE.

int_overlaps() performs a similar test, but will return true if two intervals overlap at all.

halleys <- read_csv("halleys.csv")
Parsed with column specification:
cols(
  designation = col_character(),
  year = col_double(),
  perihelion_date = col_date(format = ""),
  start_date = col_date(format = ""),
  end_date = col_date(format = ""),
  distance = col_character(),
  visible = col_character()
)
# Print halleys
head(halleys)

# New column for interval from start to end date
halleys <- halleys %>% 
  mutate(visible = start_date %--% end_date)

# The visitation of 1066
halleys_1066 <- halleys[14, ] 

# Monarchs in power on perihelion date
monarchs %>% 
  filter(halleys_1066$perihelion_date %within% reign) %>%
  select(name, from, to, dominion)

# Monarchs whose reign overlaps visible time
monarchs %>% 
  filter(int_overlaps(halleys_1066$visible, reign)) %>%
  select(name, from, to, dominion)

Looks like the Kings of England Edward the Confessor and Harold II would have been able to see the comet. It may have been a bad omen, neither were in power by 1067.

Converting to durations and periods

Intervals are the most specific way to represent a span of time since they retain information about the exact start and end moments. They can be converted to periods and durations exactly: it’s possible to calculate both the exact number of seconds elapsed between the start and end date, as well as the perceived change in clock time.

To do so you use the as.period(), and as.duration() functions, parsing in an interval as the only argument.

# New columns for duration and period
monarchs <- monarchs %>%
  mutate(
    duration = as.duration(reign),
    period = as.period(reign)) 
    
# Examine results    
monarchs %>%
  select(name, duration, period)

See how much easier it is to interpret the length of their reigns as periods or durations.

Problems in practice

Timezones

Setting the timezone

If you import a datetime and it has the wrong timezone, you can set it with force_tz(). Pass in the datetime as the first argument and the appropriate timezone to the tzone argument. Remember the timezone needs to be one from OlsonNames().

# Game2: CAN vs NZL in Edmonton
game2 <- mdy_hm("June 11 2015 19:00")

# Game3: CHN vs NZL in Winnipeg
game3 <- mdy_hm("June 15 2015 18:30")

# Set the timezone to "America/Edmonton"
game2_local <- force_tz(game2, tzone = "America/Edmonton")
game2_local
[1] "2015-06-11 19:00:00 MDT"
# Set the timezone to "America/Winnipeg"
game3_local <- force_tz(game3, tzone = "America/Winnipeg")
game3_local
[1] "2015-06-15 18:30:00 CDT"
# How long does the team have to rest?
as.period(game2_local %--% game3_local)
[1] "3d 22H 30M 0S"

Edmonton and Winnipeg are in different timezones, so even though the start times of the games only look 30 minutes apart, they are in fact 1 hour and 30 minutes apart, and the team only has 3 days, 22 hours and 30 minutes to prepare.

Viewing in a timezone

To view a datetime in another timezone use with_tz(). The syntax of with_tz() is the same as force_tz(), passing a datetime and set the tzone argument to the desired timezone. Unlike force_tz(), with_tz() isn’t changing the underlying moment of time, just how it is displayed.

# What time is game2_local in NZ?
with_tz(game2_local, tzone = "Pacific/Auckland")
[1] "2015-06-12 13:00:00 NZST"
# What time is game2_local in Corvallis, Oregon?
with_tz(game2_local, tzone = "America/Los_Angeles")
[1] "2015-06-11 18:00:00 PDT"
# What time is game3_local in NZ?
with_tz(game3_local, tzone = "Pacific/Auckland")
[1] "2015-06-16 11:30:00 NZST"

Timezones in the weather data

Did you ever notice that in the hourly Auckland weather data there was another datetime column, date_utc? Take a look:

tibble::glimpse(akl_hourly)

The datetime column you created represented local time in Auckland, NZ. I suspect this additional column, date_utc represents the observation time in UTC (the name seems a big clue). But does it really?

Use your new timezone skills to find out.

# Examine datetime and date_utc columns
head(akl_hourly$datetime)
[1] "2016-01-01 00:00:00 UTC" "2016-01-01 00:30:00 UTC" "2016-01-01 01:00:00 UTC"
[4] "2016-01-01 01:30:00 UTC" "2016-01-01 02:00:00 UTC" "2016-01-01 02:30:00 UTC"
head(akl_hourly$date_utc)
[1] "2015-12-31 11:00:00 UTC" "2015-12-31 11:30:00 UTC" "2015-12-31 12:00:00 UTC"
[4] "2015-12-31 12:30:00 UTC" "2015-12-31 13:00:00 UTC" "2015-12-31 13:30:00 UTC"
  
# Force datetime to Pacific/Auckland
akl_hourly <- akl_hourly %>%
  mutate(
    datetime = force_tz(datetime, tzone = "Pacific/Auckland"))

# Reexamine datetime
head(akl_hourly$datetime)
[1] "2016-01-01 00:00:00 NZDT" "2016-01-01 00:30:00 NZDT" "2016-01-01 01:00:00 NZDT"
[4] "2016-01-01 01:30:00 NZDT" "2016-01-01 02:00:00 NZDT" "2016-01-01 02:30:00 NZDT"
  
# Are datetime and date_utc the same moments
table(akl_hourly$datetime - akl_hourly$date_utc)

-82800      0   3600 
     2  17450      2 

Looks like for 17,450 rows datetime and date_utc describe the same moment, but for 4 rows they are different. Can you guess which? Yup, the times where DST kicks in.

Times without dates

Sometimes we just have a time without a date.

In this situation, the hms package provides an hms class of object for holding times without dates, and the best place to start would be with as.hms().

# Import auckland hourly data 
akl_hourly <- read_csv("akl_weather_hourly_2016.csv")
Parsed with column specification:
cols(
  year = col_double(),
  month = col_double(),
  mday = col_double(),
  time = col_time(format = ""),
  temperature = col_double(),
  weather = col_character(),
  conditions = col_character(),
  events = col_character(),
  humidity = col_double(),
  date_utc = col_datetime(format = "")
)
# Examine structure of time column
str(akl_hourly$time)
 'hms' num [1:17454] 00:00:00 00:30:00 01:00:00 01:30:00 ...
 - attr(*, "units")= chr "secs"
# Examine head of time column
head(akl_hourly$time)
00:00:00
00:30:00
01:00:00
01:30:00
02:00:00
02:30:00
# A plot using just time
ggplot(akl_hourly, aes(x = time, y = temperature)) +
  geom_line(aes(group = make_date(year, month, mday)), alpha = 0.2)

Using time without date is a great way to examine daily patterns.

More on importing and exporting datetimes

Fast parsing with fasttime

The fasttime package provides a single function fastPOSIXct(), designed to read in datetimes formatted according to ISO 8601. Because it only reads in one format, and doesn’t have to guess a format, it is really fast!

You’ll see how fast in this exercise by comparing how fast it reads in the dates from the Auckland hourly weather data (over 17,000 dates) to lubridates ymd_hms().

To compare run times you’ll use the microbenchmark() function from the package of the same name. You pass in as many arguments as you want each being an expression to time.

library(microbenchmark)
library(fasttime)

dates <- readRDS("dates.rds")

# Examine structure of dates
str(dates)
 chr [1:17454] "2015-12-31T11:00:00Z" "2015-12-31T11:30:00Z" "2015-12-31T12:00:00Z" ...
# Use fastPOSIXct() to parse dates
fastPOSIXct(dates) %>% str()
 POSIXct[1:17454], format: "2015-12-31 11:00:00" "2015-12-31 11:30:00" "2015-12-31 12:00:00" "2015-12-31 12:30:00" ...
# Compare speed of fastPOSIXct() to ymd_hms()
microbenchmark(
  ymd_hms = ymd_hms(dates),
  fasttime = fastPOSIXct(dates),
  times = 20)
Unit: milliseconds

To compare speed, you can compare the average run time in the mean column. You should see fasttime is about 20 times faster than ymd_hms().

Fast parsing with lubridate::fast_strptime

lubridate provides its own fast datetime parser: fast_strptime(). Instead of taking an order argument like parse_date_time() it takes a format argument and the format must comply with the strptime() style.

# Head of dates
head(dates)
[1] "2015-12-31T11:00:00Z" "2015-12-31T11:30:00Z" "2015-12-31T12:00:00Z" "2015-12-31T12:30:00Z"
[5] "2015-12-31T13:00:00Z" "2015-12-31T13:30:00Z"
# Parse dates with fast_strptime
fast_strptime(dates, 
    format = "%Y-%m-%dT%H:%M:%SZ") %>% str()
 POSIXlt[1:17454], format: "2015-12-31 11:00:00" "2015-12-31 11:30:00" "2015-12-31 12:00:00" "2015-12-31 12:30:00" ...
# Comparse speed to ymd_hms() and fasttime
microbenchmark(
  ymd_hms = ymd_hms(dates),
  fasttime = fastPOSIXct(dates),
  fast_strptime = fast_strptime(dates, 
    format = "%Y-%m-%dT%H:%M:%SZ"),
  times = 20)
Unit: milliseconds

fast_strptime() is much faster than ymd_hms() but just a little slower than fasttime.

Outputting pretty dates and times

An easy way to output dates is to use the stamp() function in lubridate. stamp() takes a string which should be an example of how the date should be formatted, and returns a function that can be used to format dates.

finished <- "I finished 'Dates and Times in R' on Thursday, September 4, 2017!"
# Create a stamp based on "Saturday, Jan 1, 2000"
date_stamp <- stamp("Saturday, Jan 1, 2000")
Multiple formats matched: "%A, %b %d, %Y"(1), "Saturday, Jan %Om, %Y"(1), "Saturday, %Om %d, %Y"(1), "Saturday, %b %d, %Y"(1), "Saturday, Jan %m, %Y"(1), "%A, Jan %Om, %Y"(0), "%A, %Om %d, %Y"(0), "%A, Jan %m, %Y"(0)
Using: "%A, %b %d, %Y"
# Print date_stamp
date_stamp
function (x, locale = "English_United Kingdom.1252") 
{
    {
        old_lc_time <- Sys.getlocale("LC_TIME")
        if (old_lc_time != locale) {
            on.exit(Sys.setlocale("LC_TIME", old_lc_time))
            Sys.setlocale("LC_TIME", locale)
        }
    }
    format(x, format = "%A, %b %d, %Y")
}
<environment: 0x000002aa3f3ecc10>
# Call date_stamp on today
date_stamp(today())
[1] "Tuesday, May 26, 2020"
# Create and call a stamp based on "12/31/1999"
stamp("12/31/1999")(today())
Multiple formats matched: "%Om/%d/%Y"(1), "%m/%d/%Y"(1)
Using: "%Om/%d/%Y"
[1] "05/26/2020"
# Use string finished for stamp()
stamp(finished)(today())
Multiple formats matched: "I finished 'Dates and Times in R' on %A, %B %d, %Y!"(1), "I finished 'Dates and Times in R' on Thursday, September %Om, %Y!"(1), "I finished 'Dates and Times in R' on Thursday, %Om %d, %Y!"(1), "I finished 'Dates and Times in R' on Thursday, %B %d, %Y!"(1), "I finished 'Dates and Times in R' on Thursday, September %m, %Y!"(1), "I finished 'Dates and Times in R' on %A, September %Om, %Y!"(0), "I finished 'Dates and Times in R' on %A, %Om %d, %Y!"(0), "I finished 'Dates and Times in R' on %A, September %m, %Y!"(0)
Using: "I finished 'Dates and Times in R' on %A, %B %d, %Y!"
[1] "I finished 'Dates and Times in R' on Tuesday, May 26, 2020!"
LS0tDQp0aXRsZTogIldvcmtpbmcgd2l0aCBEYXRlcyBhbmQgVGltZXMgaW4gUiINCm91dHB1dDoNCiAgaHRtbF9ub3RlYm9vazoNCiAgICB0b2M6IHRydWUNCiAgICB0b2NfZmxvYXQ6IHRydWUNCiAgICB0b2NfY29sbGFwc2VkOiBmYWxzZQ0KICAgIA0KdG9jX2RlcHRoOiAzDQotLS0NCiMgRGF0ZXMgYW5kIFRpbWVzIGluIFINCg0KUiBkb2Vzbid0IGtub3cgc29tZXRoaW5nIGlzIGEgZGF0ZSBvciB0aW1lIHVubGVzcyB5b3UgdGVsbCBpdC4gDQoNCklTTyA4NjAxIGRhdGVzID0gWVlZWS1NTS1ERA0KDQojIyBTcGVjaWZ5aW5nIGRhdGVzDQoNCklmIHlvdSBoYXZlIGEgY2hhcmFjdGVyIHN0cmluZyB0aGF0IHJlcHJlc2VudHMgYSBkYXRlIGluIHRoZSBJU08gODYwMSBzdGFuZGFyZCB5b3UgY2FuIHR1cm4gaXQgaW50byBhIERhdGUgdXNpbmcgdGhlIGFzLkRhdGUoKSBmdW5jdGlvbi4gSnVzdCBwYXNzIHRoZSBjaGFyYWN0ZXIgc3RyaW5nIChvciBhIHZlY3RvciBvZiBjaGFyYWN0ZXIgc3RyaW5ncykgYXMgdGhlIGZpcnN0IGFyZ3VtZW50Lg0KDQpgYGB7cn0NCiMgVGhlIGRhdGUgUiAzLjAuMCB3YXMgcmVsZWFzZWQNCnggPC0gIjIwMTMtMDQtMDMiDQoNCiMgRXhhbWluZSBzdHJ1Y3R1cmUgb2YgeA0Kc3RyKHgpDQoNCiMgVXNlIGFzLkRhdGUoKSB0byBpbnRlcnByZXQgeCBhcyBhIGRhdGUNCnhfZGF0ZSA8LSBhcy5EYXRlKHgpDQoNCiMgRXhhbWluZSBzdHJ1Y3R1cmUgb2YgeF9kYXRlDQpzdHIoeF9kYXRlKQ0KDQojIFN0b3JlIEFwcmlsIDEwIDIwMTQgYXMgYSBEYXRlDQphcHJpbF8xMF8yMDE0IDwtIGFzLkRhdGUoIjIwMTQtMDQtMTAiKQ0KYGBgDQojIyMgQXV0b21hdGljIGltcG9ydA0KDQpTb21ldGltZXMgeW91J2xsIG5lZWQgdG8gaW5wdXQgYSBjb3VwbGUgb2YgZGF0ZXMgYnkgaGFuZCB1c2luZyBhcy5EYXRlKCkgYnV0IGl0J3MgbXVjaCBtb3JlIGNvbW1vbiB0byBoYXZlIGEgY29sdW1uIG9mIGRhdGVzIGluIGEgZGF0YSBmaWxlLg0KDQpTb21lIGZ1bmN0aW9ucyB0aGF0IHJlYWQgaW4gZGF0YSB3aWxsIGF1dG9tYXRpY2FsbHkgcmVjb2duaXplIGFuZCBwYXJzZSBkYXRlcyBpbiBhIHZhcmlldHkgb2YgZm9ybWF0cy4gSW4gcGFydGljdWxhciB0aGUgaW1wb3J0IGZ1bmN0aW9ucywgbGlrZSByZWFkX2NzdigpLCBpbiB0aGUgcmVhZHIgcGFja2FnZSB3aWxsIHJlY29nbml6ZSBkYXRlcyBpbiBhIGZldyBjb21tb24gZm9ybWF0cy4NCg0KVGhlcmUgaXMgYWxzbyB0aGUgYW55dGltZSgpIGZ1bmN0aW9uIGluIHRoZSBhbnl0aW1lIHBhY2thZ2Ugd2hvc2Ugc29sZSBnb2FsIGlzIHRvIGF1dG9tYXRpY2FsbHkgcGFyc2Ugc3RyaW5ncyBhcyBkYXRlcyByZWdhcmRsZXNzIG9mIHRoZSBmb3JtYXQuDQpgYGB7cn0NCiMgTG9hZCB0aGUgcmVhZHIgcGFja2FnZQ0KbGlicmFyeShyZWFkcikNCg0KIyBVc2UgcmVhZF9jc3YoKSB0byBpbXBvcnQgcnZlcnNpb25zLmNzdg0KcmVsZWFzZXMgPC0gcmVhZF9jc3YoInJ2ZXJzaW9ucy5jc3YiKQ0KDQojIEV4YW1pbmUgdGhlIHN0cnVjdHVyZSBvZiB0aGUgZGF0ZSBjb2x1bW4NCnN0cihyZWxlYXNlcyRkYXRlKQ0KDQojIExvYWQgdGhlIGFueXRpbWUgcGFja2FnZQ0KbGlicmFyeShhbnl0aW1lKQ0KDQojIFZhcmlvdXMgd2F5cyBvZiB3cml0aW5nIFNlcCAxMCAyMDA5DQpzZXBfMTBfMjAwOSA8LSBjKCJTZXB0ZW1iZXIgMTAgMjAwOSIsICIyMDA5LTA5LTEwIiwgIjEwIFNlcCAyMDA5IiwgIjA5LTEwLTIwMDkiKQ0KDQojIFVzZSBhbnl0aW1lKCkgdG8gcGFyc2Ugc2VwXzEwXzIwMDkNCmFueXRpbWUoc2VwXzEwXzIwMDkpDQpgYGANClNvbWV0aW1lcyB0aGVzZSBmdW5jdGlvbnMgd29uJ3Qgd29yaywgZXNwZWNpYWxseSBpZiBkYXRlcyBhcmUgYW1iaWd1b3VzIChlLmcuIElzIDIwMDQtMTAtNCwgT2N0IDR0aCBvciBBcHJpbCAxMHRoPykNCg0KIyMgUGxvdHRpbmcNCg0KSWYgeW91IHBsb3QgYSBEYXRlIG9uIHRoZSBheGlzIG9mIGEgcGxvdCwgeW91IGV4cGVjdCB0aGUgZGF0ZXMgdG8gYmUgaW4gY2FsZW5kYXIgb3JkZXIsIGFuZCB0aGF0J3MgZXhhY3RseSB3aGF0IGhhcHBlbnMgd2l0aCBwbG90KCkgb3IgZ2dwbG90KCkuDQoNCldlJ2xsIG1ha2Ugc29tZSBwbG90cyB3aXRoIHRoZSBSIHZlcnNpb24gcmVsZWFzZXMgZGF0YSB1c2luZyBnZ3Bsb3QyLiBUaGVyZSBhcmUgdHdvIGJpZyBkaWZmZXJlbmNlcyB3aGVuIGEgRGF0ZSBpcyBvbiBhbiBheGlzOg0KDQoxLiBJZiB5b3Ugc3BlY2lmeSBsaW1pdHMgdGhleSBtdXN0IGJlIERhdGUgb2JqZWN0cy4NCjIuIFRvIGNvbnRyb2wgdGhlIGJlaGF2aW9yIG9mIHRoZSBzY2FsZSB5b3UgdXNlIHRoZSBzY2FsZV94X2RhdGUoKSBmdW5jdGlvbi4NCmBgYHtyfQ0KbGlicmFyeShnZ3Bsb3QyKQ0KDQojIFNldCB0aGUgeCBheGlzIHRvIHRoZSBkYXRlIGNvbHVtbg0KZ2dwbG90KHJlbGVhc2VzLCBhZXMoeCA9IGRhdGUsIHkgPSB0eXBlKSkgKw0KICBnZW9tX2xpbmUoYWVzKGdyb3VwID0gMSwgY29sb3IgPSBmYWN0b3IobWFqb3IpKSkNCg0KIyBMaW1pdCB0aGUgYXhpcyB0byBiZXR3ZWVuIDIwMTAtMDEtMDEgYW5kIDIwMTQtMDEtMDENCmdncGxvdChyZWxlYXNlcywgYWVzKHggPSBkYXRlLCB5ID0gdHlwZSkpICsNCiAgZ2VvbV9saW5lKGFlcyhncm91cCA9IDEsIGNvbG9yID0gZmFjdG9yKG1ham9yKSkpICsNCiAgeGxpbShhcy5EYXRlKCIyMDEwLTAxLTAxIiksIGFzLkRhdGUoIjIwMTQtMDEtMDEiKSkNCg0KIyBTcGVjaWZ5IGJyZWFrcyBldmVyeSB0ZW4geWVhcnMgYW5kIGxhYmVscyB3aXRoICIlWSINCmdncGxvdChyZWxlYXNlcywgYWVzKHggPSBkYXRlLCB5ID0gdHlwZSkpICsNCiAgZ2VvbV9saW5lKGFlcyhncm91cCA9IDEsIGNvbG9yID0gZmFjdG9yKG1ham9yKSkpICsNCiAgc2NhbGVfeF9kYXRlKGRhdGVfYnJlYWtzID0gIjEwIHllYXJzIiwgZGF0ZV9sYWJlbHMgPSAiJVkiKQ0KYGBgDQojIyBBcml0aG1ldGljIGFuZCBsb2dpY2FsIG9wZXJhdG9ycw0KDQpTaW5jZSBEYXRlIG9iamVjdHMgYXJlIGludGVybmFsbHkgcmVwcmVzZW50ZWQgYXMgdGhlIG51bWJlciBvZiBkYXlzIHNpbmNlIDE5NzAtMDEtMDEgeW91IGNhbiBkbyBiYXNpYyBtYXRoIGFuZCBjb21wYXJpc29ucyB3aXRoIGRhdGVzLiBZb3UgY2FuIGNvbXBhcmUgZGF0ZXMgd2l0aCB0aGUgdXN1YWwgbG9naWNhbCBvcGVyYXRvcnMgKDwsID09LCA+IGV0Yy4pLCBmaW5kIGV4dHJlbWVzIHdpdGggbWluKCkgYW5kIG1heCgpLCBhbmQgZXZlbiBzdWJ0cmFjdCB0d28gZGF0ZXMgdG8gZmluZCBvdXQgdGhlIHRpbWUgYmV0d2VlbiB0aGVtLg0KDQpTeXMuZGF0ZSgpIGluIHRoZSBjb2RlLCBpdCBzaW1wbHkgcmV0dXJucyB0b2RheSdzIGRhdGUuDQpgYGB7cn0NCmxpYnJhcnkoZHBseXIpDQojIEZpbmQgdGhlIGxhcmdlc3QgZGF0ZQ0KbGFzdF9yZWxlYXNlX2RhdGUgPC0gbWF4KHJlbGVhc2VzJGRhdGUpDQoNCiMgRmlsdGVyIHJvdyBmb3IgbGFzdCByZWxlYXNlDQpsYXN0X3JlbGVhc2UgPC0gZmlsdGVyKHJlbGVhc2VzLCBkYXRlID09IGxhc3RfcmVsZWFzZV9kYXRlKQ0KDQojIFByaW50IGxhc3RfcmVsZWFzZQ0KcHJpbnQobGFzdF9yZWxlYXNlKQ0KDQojIEhvdyBsb25nIHNpbmNlIGxhc3QgcmVsZWFzZT8NClN5cy5EYXRlKCkgLSBsYXN0X3JlbGVhc2UkZGF0ZQ0KYGBgDQojIyBEYXRlIHRpbWVzDQoNCklTTyA4NjAxID0gSEg6TU06U1MNCg0KIyMjIEdldHRpbmcgZGF0ZXRpbWVzIGludG8gUg0KDQpKdXN0IGxpa2UgZGF0ZXMgd2l0aG91dCB0aW1lcywgaWYgeW91IHdhbnQgUiB0byByZWNvZ25pemUgYSBzdHJpbmcgYXMgYSBkYXRldGltZSB5b3UgbmVlZCB0byBjb252ZXJ0IGl0LCBhbHRob3VnaCBub3cgeW91IHVzZSBhcy5QT1NJWGN0KCkuIGFzLlBPU0lYY3QoKSBleHBlY3RzIHN0cmluZ3MgdG8gYmUgaW4gdGhlIGZvcm1hdCBZWVlZLU1NLUREIEhIOk1NOlNTLg0KDQpUaGUgb25seSB0cmlja3kgdGhpbmcgaXMgdGhhdCB0aW1lcyB3aWxsIGJlIGludGVycHJldGVkIGluIGxvY2FsIHRpbWUgYmFzZWQgb24geW91ciBtYWNoaW5lJ3Mgc2V0IHVwLiBZb3UgY2FuIGNoZWNrIHlvdXIgdGltZXpvbmUgd2l0aCBTeXMudGltZXpvbmUoKS4gSWYgeW91IHdhbnQgdGhlIHRpbWUgdG8gYmUgaW50ZXJwcmV0ZWQgaW4gYSBkaWZmZXJlbnQgdGltZXpvbmUsIHlvdSBqdXN0IHNldCB0aGUgdHogYXJndW1lbnQgb2YgYXMuUE9TSVhjdCgpLiANCmBgYHtyfQ0KIyBVc2UgYXMuUE9TSVhjdCB0byBlbnRlciB0aGUgZGF0ZXRpbWUgDQphcy5QT1NJWGN0KCIyMDEwLTEwLTAxIDEyOjEyOjAwIikNCg0KIyBVc2UgYXMuUE9TSVhjdCBhZ2FpbiBidXQgc2V0IHRoZSB0aW1lem9uZSB0byBgIkFtZXJpY2EvTG9zX0FuZ2VsZXMiYA0KYXMuUE9TSVhjdCgiMjAxMC0xMC0wMSAxMjoxMjowMCIsIHR6ID0gIkFtZXJpY2EvTG9zX0FuZ2VsZXMiKQ0KDQojIFVzZSByZWFkX2NzdiB0byBpbXBvcnQgcnZlcnNpb25zLmNzdg0KcmVsZWFzZXMgPC0gcmVhZF9jc3YoInJ2ZXJzaW9ucy5jc3YiKQ0KDQojIEV4YW1pbmUgc3RydWN0dXJlIG9mIGRhdGV0aW1lIGNvbHVtbg0Kc3RyKHJlbGVhc2VzJGRhdGV0aW1lKQ0KYGBgDQojIyMgRGF0ZXRpbWVzIGJlaGF2ZSBuaWNlbHkgdG9vDQoNCnVzdCBsaWtlIERhdGUgb2JqZWN0cywgeW91IGNhbiBwbG90IGFuZCBkbyBtYXRoIHdpdGggUE9TSVhjdCBvYmplY3RzLg0KDQpBcyBhbiBleGFtcGxlLCBpbiB0aGlzIGV4ZXJjaXNlIHlvdSdsbCBzZWUgaG93IHF1aWNrbHkgcGVvcGxlIGRvd25sb2FkIG5ldyB2ZXJzaW9ucyBvZiBSLCBieSBleGFtaW5pbmcgdGhlIGRvd25sb2FkIGxvZ3MgZnJvbSB0aGUgUlN0dWRpbyBDUkFOIG1pcnJvci4NCg0KUiAzLjIuMCB3YXMgcmVsZWFzZWQgYXQgIjIwMTUtMDQtMTYgMDc6MTM6MzMiIHNvIGNyYW4tbG9nc18yMDE1LTA0LTE3LmNzdiBjb250YWlucyBhIHJhbmRvbSBzYW1wbGUgb2YgZG93bmxvYWRzIG9uIHRoZSAxNnRoLCAxN3RoIGFuZCAxOHRoLg0KDQpgYGB7cn0NCiMgSW1wb3J0ICJjcmFuLWxvZ3NfMjAxNS0wNC0xNy5jc3YiIHdpdGggcmVhZF9jc3YoKQ0KbG9ncyA8LSByZWFkX2NzdigiY3Jhbi1sb2dzXzIwMTUtMDQtMTcuY3N2IikNCg0KIyBQcmludCBsb2dzDQpwcmludChsb2dzKQ0KDQojIFN0b3JlIHRoZSByZWxlYXNlIHRpbWUgYXMgYSBQT1NJWGN0IG9iamVjdA0KcmVsZWFzZV90aW1lIDwtIGFzLlBPU0lYY3QoIjIwMTUtMDQtMTYgMDc6MTM6MzMiLCB0eiA9ICJVVEMiKQ0KDQojIFdoZW4gaXMgdGhlIGZpcnN0IGRvd25sb2FkIG9mIDMuMi4wPw0KbG9ncyAlPiUgDQogIGZpbHRlcihsb2dzJGRhdGV0aW1lID4gcmVsZWFzZV90aW1lLA0KICAgIHJfdmVyc2lvbiA9PSAiMy4yLjAiKQ0KDQojIEV4YW1pbmUgaGlzdG9ncmFtcyBvZiBkb3dubG9hZHMgYnkgdmVyc2lvbg0KZ2dwbG90KGxvZ3MsIGFlcyh4ID0gZGF0ZXRpbWUpKSArDQogIGdlb21faGlzdG9ncmFtKCkgKw0KICBnZW9tX3ZsaW5lKGFlcyh4aW50ZXJjZXB0ID0gYXMubnVtZXJpYyhyZWxlYXNlX3RpbWUpKSkrDQogIGZhY2V0X3dyYXAofiByX3ZlcnNpb24sIG5jb2wgPSAxKQ0KYGBgDQpEaWQgeW91IHNlZSBob3cgaXQgdGFrZXMgYWJvdXQgdHdvIGRheXMgZm9yIGRvd25sb2FkcyBvZiB0aGUgbmV3IHZlcnNpb24gKDMuMi4wKSB0byBvdmVydGFrZSBkb3dubG9hZHMgb2YgdGhlIG9sZCB2ZXJzaW9uICgzLjEuMyk/DQoNCiMgUGFyc2luZyBhbmQgTWFuaXB1bGF0aW5nIERhdGVzIGFuZCBUaW1lcyB3aXRoIGx1YnJpZGF0ZQ0KDQojIyBQYXJzaW5nIGRhdGVzIHdpdGggbHVicmlkYXRlDQoNCiMjIyBTZWxlY3RpbmcgdGhlIHJpZ2h0IHBhcnNpbmcgZnVuY3Rpb24NCg0KbHVicmlkYXRlIHByb3ZpZGVzIGEgc2V0IG9mIGZ1bmN0aW9ucyBmb3IgcGFyc2luZyBkYXRlcyBvZiBhIGtub3duIG9yZGVyLiBGb3IgZXhhbXBsZSwgeW1kKCkgd2lsbCBwYXJzZSBkYXRlcyB3aXRoIHllYXIgZmlyc3QsIGZvbGxvd2VkIGJ5IG1vbnRoIGFuZCB0aGVuIGRheS4gVGhlIHBhcnNpbmcgaXMgZmxleGlibGUsIGZvciBleGFtcGxlLCBpdCB3aWxsIHBhcnNlIHRoZSBtIHdoZXRoZXIgaXQgaXMgbnVtZXJpYyAoZS5nLiA5IG9yIDA5KSwgYSBmdWxsIG1vbnRoIG5hbWUgKGUuZy4gU2VwdGVtYmVyKSwgb3IgYW4gYWJicmV2aWF0ZWQgbW9udGggbmFtZSAoZS5nLiBTZXApLg0KDQpBbGwgdGhlIGZ1bmN0aW9ucyB3aXRoIHksIG0gYW5kIGQgaW4gYW55IG9yZGVyIGV4aXN0LiBJZiB5b3VyIGRhdGVzIGhhdmUgdGltZXMgYXMgd2VsbCwgeW91IGNhbiB1c2UgdGhlIGZ1bmN0aW9ucyB0aGF0IHN0YXJ0IHdpdGggeW1kLCBkbXksIG1keSBvciB5ZG0gYW5kIGFyZSBmb2xsb3dlZCBieSBhbnkgb2YgX2gsIF9obSBvciBfaG1zLg0KDQpUbyBzZWUgYWxsIHRoZSBmdW5jdGlvbnMgYXZhaWxhYmxlIGxvb2sgYXQgW3ltZCgpXShodHRwczovL3d3dy5yZG9jdW1lbnRhdGlvbi5vcmcvcGFja2FnZXMvbHVicmlkYXRlL3ZlcnNpb25zLzEuNy44L3RvcGljcy95bWQpIGZvciBkYXRlcyBhbmQgW3ltZF9obXMoKV0oaHR0cHM6Ly93d3cucmRvY3VtZW50YXRpb24ub3JnL3BhY2thZ2VzL2x1YnJpZGF0ZS92ZXJzaW9ucy8xLjcuOC90b3BpY3MveW1kX2htcykgZm9yIGRhdGV0aW1lcy4NCg0KYGBge3J9DQpsaWJyYXJ5KGx1YnJpZGF0ZSkNCg0KIyBQYXJzZSB4IA0KeCA8LSAiMjAxMCBTZXB0ZW1iZXIgMjB0aCIgIyAyMDEwLTA5LTIwDQp5bWQoeCkNCg0KIyBQYXJzZSB5IA0KeSA8LSAiMDIuMDEuMjAxMCIgICMgMjAxMC0wMS0wMg0KZG15KHkpDQoNCiMgUGFyc2UgeiANCnogPC0gIlNlcCwgMTJ0aCAyMDEwIDE0OjAwIiAgIyAyMDEwLTA5LTEyVDE0OjAwDQptZHlfaG0oeikNCmBgYA0KRGlkIHlvdSBub3RpY2UgdGhlIG1lc3NhZ2UgYWZ0ZXIgeW91IGNhbGxlZCBsaWJyYXJ5KGx1YnJpZGF0ZSk/IFdoZW5ldmVyIHlvdSBzZWUgYW4gb2JqZWN0IOKAnGlzIG1hc2tlZCBieeKAnSwgaXQgbWVhbnMgYW4gb2JqZWN0IGluIHRoZSBwYWNrYWdlLCBpbiB0aGlzIGNhc2UgdGhlIGRhdGUoKSBmdW5jdGlvbiBpbiBsdWJyaWRhdGUgaGFzIHRoZSBzYW1lIG5hbWUgYXMgYW4gb2JqZWN0IGluIGFub3RoZXIgbG9hZGVkIHBhY2thZ2UsIGluIHRoaXMgY2FzZSBkYXRlKCkgaW4gdGhlIGJhc2UgcGFja2FnZS4gSWYgeW91IGFzayBmb3IgZGF0ZSgpIHlvdSdsbCBnZXQgdGhlIGx1YnJpZGF0ZSBvbmUsIHlvdSBjYW4gYWx3YXlzIGdldCB0aGUgb25lIGl0IG1hc2tlZCB3aXRoIGJhc2U6OmRhdGUoKS4NCg0KIyMjIFNwZWNpZnlpbmcgYW4gb3JkZXIgd2l0aCBgcGFyc2VfZGF0ZV90aW1lKClgDQoNCldoYXQgYWJvdXQgaWYgeW91IGhhdmUgc29tZXRoaW5nIGluIGEgcmVhbGx5IHdlaXJkIG9yZGVyIGxpa2UgZHltX21zaD8gVGhlcmUncyBubyBuYW1lZCBmdW5jdGlvbiBqdXN0IGZvciB0aGF0IG9yZGVyLCBidXQgdGhhdCBpcyB3aGVyZSBbcGFyc2VfZGF0ZV90aW1lKCldKGh0dHBzOi8vd3d3LnJkb2N1bWVudGF0aW9uLm9yZy9wYWNrYWdlcy9sdWJyaWRhdGUvdmVyc2lvbnMvMS43LjgvdG9waWNzL3BhcnNlX2RhdGVfdGltZSkgY29tZXMgaW4uIHBhcnNlX2RhdGVfdGltZSgpIHRha2VzIGFuIGFkZGl0aW9uYWwgYXJndW1lbnQsIG9yZGVycywgd2hlcmUgeW91IGNhbiBzcGVjaWZ5IHRoZSBvcmRlciBvZiB0aGUgY29tcG9uZW50cyBpbiB0aGUgZGF0ZS4NCg0Kb3IgZXhhbXBsZSwgdG8gcGFyc2UgIjIwMTAgU2VwdGVtYmVyIDIwdGgiIHlvdSBjb3VsZCBzYXkgcGFyc2VfZGF0ZV90aW1lKCIyMDEwIFNlcHRlbWJlciAyMHRoIiwgb3JkZXJzID0gInltZCIpIGFuZCB0aGF0IHdvdWxkIGJlIGVxdWl2YWxlbnQgdG8gdXNpbmcgdGhlIHltZCgpIGZ1bmN0aW9uIGZyb20gdGhlIHByZXZpb3VzIGV4ZXJjaXNlLg0KDQpPbmUgYWR2YW50YWdlIG9mIHBhcnNlX2RhdGVfdGltZSgpIGlzIHRoYXQgeW91IGNhbiB1c2UgbW9yZSBmb3JtYXQgY2hhcmFjdGVycy4gRm9yIGV4YW1wbGUsIHlvdSBjYW4gc3BlY2lmeSB3ZWVrZGF5IG5hbWVzIHdpdGggQSwgSSBmb3IgMTIgaG91ciB0aW1lLCBhbS9wbSBpbmRpY2F0b3JzIHdpdGggcCBhbmQgbWFueSBvdGhlcnMuIA0KDQpBbm90aGVyIGJpZyBhZHZhbnRhZ2UgaXMgdGhhdCB5b3UgY2FuIHNwZWNpZnkgYSB2ZWN0b3Igb2Ygb3JkZXJzLCBhbmQgdGhhdCBhbGxvd3MgcGFyc2luZyBvZiBkYXRlcyB3aGVyZSBtdWx0aXBsZSBmb3JtYXRzIG1pZ2h0IGJlIHVzZWQuDQpgYGB7cn0NCiMgU3BlY2lmeSBhbiBvcmRlciBzdHJpbmcgdG8gcGFyc2UgeA0KeCA8LSAiTW9uZGF5IEp1bmUgMXN0IDIwMTAgYXQgNHBtIg0KcGFyc2VfZGF0ZV90aW1lKHgsIG9yZGVycyA9ICJBbWR5SXAiKQ0KDQojIFNwZWNpZnkgb3JkZXIgdG8gaW5jbHVkZSBib3RoICJtZHkiIGFuZCAiZG15Ig0KdHdvX29yZGVycyA8LSBjKCJPY3RvYmVyIDcsIDIwMDEiLCAiT2N0b2JlciAxMywgMjAwMiIsICJBcHJpbCAxMywgMjAwMyIsIA0KICAiMTcgQXByaWwgMjAwNSIsICIyMyBBcHJpbCAyMDE3IikNCnBhcnNlX2RhdGVfdGltZSh0d29fb3JkZXJzLCBvcmRlcnMgPSBjKCJtZHkiLCAiZG15IikpDQoNCiMgU3BlY2lmeSBvcmRlciB0byBpbmNsdWRlICJkT21ZIiwgIk9tWSIgYW5kICJZIg0Kc2hvcnRfZGF0ZXMgPC0gYygiMTEgRGVjZW1iZXIgMTI4MiIsICJNYXkgMTM3MiIsICIxMjUzIikNCnBhcnNlX2RhdGVfdGltZShzaG9ydF9kYXRlcywgb3JkZXJzID0gYygiZE9tWSIsICJPbVkiLCAiWSIpKQ0KYGBgDQogRGlkIHlvdSBub3RpY2UgdGhhdCB3aGVuIGEgZGF0ZSBjb21wb25lbnQgaXMgbWlzc2luZywgaXQncyBqdXN0IHNldCB0byAxPyBGb3IgZXhhbXBsZSwgdGhlIGlucHV0IDEyNTMgcmVzdWx0ZWQgaW4gdGhlIGRhdGUgMTI1My0wMS0wMS4NCg0KIyMgV2VhdGhlciBpbiBBdWNrbGFuZA0KDQojIyMgSW1wb3J0IGRhaWx5IHdlYXRoZXIgZGF0YQ0KDQpJbiBwcmFjdGljZSB5b3Ugd29uJ3QgYmUgcGFyc2luZyBpc29sYXRlZCBkYXRlcyBhbmQgdGltZXMsIHRoZXknbGwgYmUgcGFydCBvZiBhIGxhcmdlciBkYXRhc2V0LiBXZSdsbCBwcmFjdGljZSB5b3VyIGx1YnJpZGF0ZSBza2lsbHMgaW4gY29udGV4dCBieSB3b3JraW5nIHdpdGggd2VhdGhlciBkYXRhIGZyb20gQXVja2xhbmQgTlouDQoNClRoZXJlIGFyZSB0d28gZGF0YSBzZXRzOiBha2xfd2VhdGhlcl9kYWlseS5jc3YgYSBzZXQgb2Ygb25jZSBkYWlseSBzdW1tYXJpZXMgZm9yIDEwIHllYXJzLCBhbmQgYWtsX3dlYXRoZXJfaG91cmx5XzIwMTYuY3N2IG9ic2VydmF0aW9ucyBldmVyeSBoYWxmIGhvdXIgZm9yIDIwMTYuIA0KYGBge3J9DQpsaWJyYXJ5KGx1YnJpZGF0ZSkNCmxpYnJhcnkocmVhZHIpDQpsaWJyYXJ5KGRwbHlyKQ0KbGlicmFyeShnZ3Bsb3QyKQ0KDQojIEltcG9ydCBDU1Ygd2l0aCByZWFkX2NzdigpDQpha2xfZGFpbHlfcmF3IDwtIHJlYWRfY3N2KCJha2xfd2VhdGhlcl9kYWlseS5jc3YiKQ0KDQojIFByaW50IGFrbF9kYWlseV9yYXcNCmFrbF9kYWlseV9yYXcNCg0KIyBQYXJzZSBkYXRlIA0KYWtsX2RhaWx5IDwtIGFrbF9kYWlseV9yYXcgJT4lDQogIG11dGF0ZShkYXRlID0geW1kKGRhdGUpKQ0KDQojIFByaW50IGFrbF9kYWlseQ0KYWtsX2RhaWx5DQoNCiMgUGxvdCB0byBjaGVjayB3b3JrDQpnZ3Bsb3QoYWtsX2RhaWx5LCBhZXMoeCA9IGRhdGUsIHkgPSBtYXhfdGVtcCkpICsNCiAgZ2VvbV9saW5lKCkgDQpgYGANCkNhbiB5b3Ugc2VlIHdoZW4gaXQgaXMgaG90IGluIEF1Y2tsYW5kPyBUaG9zZSB0ZW1wZXJhdHVyZXMgYXJlIGluIGZhcmVuaGVpdC4gWXVwLCBzdW1tZXIgZmFsbHMgaW4gRGVjLUphbi1GZWIuDQoNCiMjIyBJbXBvcnQgaG91cmx5IHdlYXRoZXIgZGF0YQ0KDQpUaGUgaG91cmx5IGRhdGEgaXMgYSBsaXR0bGUgZGlmZmVyZW50LiBUaGUgZGF0ZSBpbmZvcm1hdGlvbiBpcyBzcHJlYWQgb3ZlciB0aHJlZSBjb2x1bW5zIHllYXIsIG1vbnRoIGFuZCBtZGF5LCBzbyB5b3UnbGwgbmVlZCB0byB1c2UgbWFrZV9kYXRlKCkgdG8gY29tYmluZSB0aGVtLg0KDQpUaGVuIHRoZSB0aW1lIGluZm9ybWF0aW9uIGlzIGluIGEgc2VwYXJhdGUgY29sdW1uIGFnYWluLCB0aW1lLiBJdCdzIHF1aXRlIGNvbW1vbiB0byBmaW5kIGRhdGUgYW5kIHRpbWUgc3BsaXQgYWNyb3NzIGRpZmZlcmVudCB2YXJpYWJsZXMuIE9uZSB3YXkgdG8gY29uc3RydWN0IHRoZSBkYXRldGltZXMgaXMgdG8gcGFzdGUgdGhlIGRhdGUgYW5kIHRpbWUgdG9nZXRoZXIgYW5kIHRoZW4gcGFyc2UgdGhlbS4gDQpgYGB7cn0NCiMgSW1wb3J0ICJha2xfd2VhdGhlcl9ob3VybHlfMjAxNi5jc3YiDQpha2xfaG91cmx5X3JhdyA8LSByZWFkX2NzdigiYWtsX3dlYXRoZXJfaG91cmx5XzIwMTYuY3N2IikNCg0KIyBQcmludCBha2xfaG91cmx5X3Jhdw0KYWtsX2hvdXJseV9yYXcNCmBgYA0KDQoNCmBgYHtyfQ0KIyBVc2UgbWFrZV9kYXRlKCkgdG8gY29tYmluZSB5ZWFyLCBtb250aCBhbmQgbWRheSANCmFrbF9ob3VybHkgIDwtIGFrbF9ob3VybHlfcmF3ICAlPiUgDQogIG11dGF0ZShkYXRlID0gbWFrZV9kYXRlKHllYXIgPSB5ZWFyLCBtb250aCA9IG1vbnRoLCBkYXkgPSBtZGF5KSkNCg0KIyBQYXJzZSBkYXRldGltZV9zdHJpbmcgDQpha2xfaG91cmx5IDwtIGFrbF9ob3VybHkgICU+JSANCiAgbXV0YXRlKA0KICAgIGRhdGV0aW1lX3N0cmluZyA9IHBhc3RlKGRhdGUsIHRpbWUsIHNlcCA9ICJUIiksDQogICAgZGF0ZXRpbWUgPSBwYXJzZV9kYXRlX3RpbWUoZGF0ZXRpbWVfc3RyaW5nLCBvcmRlcnMgPSAieW1kSE1TIikNCiAgKQ0KIyBQcmludCBkYXRlLCB0aW1lIGFuZCBkYXRldGltZSBjb2x1bW5zIG9mIGFrbF9ob3VybHkNCmFrbF9ob3VybHkgJT4lIHNlbGVjdChkYXRlLCB0aW1lLCBkYXRldGltZSkNCg0KIyBQbG90IHRvIGNoZWNrIHdvcmsNCmdncGxvdChha2xfaG91cmx5LCBhZXMoeCA9IGRhdGV0aW1lLCB5ID0gdGVtcGVyYXR1cmUpKSArDQogIGdlb21fbGluZSgpDQpgYGANCml0J3MgaW50ZXJlc3RpbmcgaG93IHRoZSBkYXkgdG8gZGF5IHZhcmlhdGlvbiBpcyBhYm91dCBoYWxmIHRoZSBzaXplIG9mIHRoZSB5ZWFybHkgdmFyaWF0aW9uLg0KDQojIyBFeHRyYWN0aW5nIHBhcnRzIG9mIGEgZGF0ZXRpbWUNCg0KIyMjIFdoYXQgY2FuIHlvdSBleHRyYWN0Pw0KDQpDb21wb25lbnRzIG9mIGEgZGF0ZXRpbWUgY2FuIGJlIGV4dHJhY3RlZCBieSBsdWJyaWRhdGUgZnVuY3Rpb25zIHdpdGggdGhlIHNhbWUgbmFtZSBsaWtlIHllYXIoKSwgbW9udGgoKSwgZGF5KCksIGhvdXIoKSwgbWludXRlKCkgYW5kIHNlY29uZCgpLiBUaGV5IGFsbCB3b3JrIHRoZSBzYW1lIHdheSBqdXN0IHBhc3MgaW4gYSBkYXRldGltZSBvciB2ZWN0b3Igb2YgZGF0ZXRpbWVzLg0KDQpUaGVyZSBhcmUgYWxzbyBhIGZldyB1c2VmdWwgZnVuY3Rpb25zIHRoYXQgcmV0dXJuIG90aGVyIGFzcGVjdHMgb2YgYSBkYXRldGltZSBsaWtlIGlmIGl0IG9jY3VycyBpbiB0aGUgbW9ybmluZyBhbSgpLCBkdXJpbmcgZGF5bGlnaHQgc2F2aW5ncyBkc3QoKSwgaW4gYSBsZWFwX3llYXIoKSwgb3Igd2hpY2ggcXVhcnRlcigpIG9yIHNlbWVzdGVyKCkgaXQgb2NjdXJzIGluLg0KYGBge3J9DQpyZWxlYXNlX3RpbWUgPC0gcmVsZWFzZXNbWyJkYXRldGltZSJdXQ0KaGVhZChyZWxlYXNlX3RpbWUpDQpgYGANCg0KDQpgYGB7cn0NCiMgRXhhbWluZSB0aGUgaGVhZCgpIG9mIHRoZSBtb250aHMgb2YgcmVsZWFzZV90aW1lDQpoZWFkKG1vbnRoKHJlbGVhc2VfdGltZSkpDQoNCiMgRXh0cmFjdCB0aGUgbW9udGggb2YgcmVsZWFzZXMgDQptb250aChyZWxlYXNlX3RpbWUpICU+JSB0YWJsZSgpDQoNCiMgRXh0cmFjdCB0aGUgeWVhciBvZiByZWxlYXNlcw0KeWVhcihyZWxlYXNlX3RpbWUpICU+JSB0YWJsZSgpDQoNCiMgSG93IG9mdGVuIGlzIHRoZSBob3VyIGJlZm9yZSAxMiAobm9vbik/DQptZWFuKGhvdXIocmVsZWFzZV90aW1lKSA8IDEyKQ0KDQojIEhvdyBvZnRlbiBpcyB0aGUgcmVsZWFzZSBpbiBhbT8NCm1lYW4oYW0ocmVsZWFzZV90aW1lKSkNCmBgYA0KUiB2ZXJzaW9ucyBoYXZlIGhpc3RvcmljYWxseSBiZWVuIHJlbGVhc2VkIG1vc3QgaW4gQXByaWwsIEp1bmUsIE9jdG9iZXIgYW5kIERlY2VtYmVyLCAxOTk4IHNhdyAxMCByZWxlYXNlcyBhbmQgYWJvdXQgNzUlIG9mIHJlbGVhc2VzIGhhcHBlbiBpbiB0aGUgbW9ybmluZyAoYXQgbGVhc3QgYWNjb3JkaW5nIHRvIFVUQykuDQoNCiMjIyBBZGRpbmcgdXNlZnVsIGxhYmVscw0KDQogU29tZXRpbWVzIGl0J3MgbmljZXIgKGVzcGVjaWFsbHkgZm9yIHBsb3R0aW5nIG9yIHRhYmxlcykgdG8gaGF2ZSBuYW1lZCBtb250aHMuIEJvdGggdGhlIG1vbnRoKCkgYW5kIHdkYXkoKSAoZGF5IG9mIHRoZSB3ZWVrKSBmdW5jdGlvbnMgaGF2ZSBhZGRpdGlvbmFsIGFyZ3VtZW50cyBsYWJlbCBhbmQgYWJiciB0byBhY2hpZXZlIGp1c3QgdGhhdC4gU2V0IGxhYmVsID0gVFJVRSB0byBoYXZlIHRoZSBvdXRwdXQgbGFiZWxsZWQgd2l0aCBtb250aCAob3Igd2Vla2RheSkgbmFtZXMsIGFuZCBhYmJyID0gRkFMU0UgZm9yIHRob3NlIG5hbWVzIHRvIGJlIHdyaXR0ZW4gaW4gZnVsbCByYXRoZXIgdGhhbiBhYmJyZXZpYXRlZC4NCg0KICAgIGhlYWQobW9udGgocmVsZWFzZV90aW1lLCBsYWJlbCA9IFRSVUUsIGFiYnIgPSBGQUxTRSkpDQoNCmBgYHtyfQ0KIyBVc2Ugd2RheSgpIHRvIHRhYnVsYXRlIHJlbGVhc2UgYnkgZGF5IG9mIHRoZSB3ZWVrDQp3ZGF5KHJlbGVhc2VzJGRhdGV0aW1lKSAlPiUgdGFibGUoKQ0KDQojIEFkZCBsYWJlbCA9IFRSVUUgdG8gbWFrZSB0YWJsZSBtb3JlIHJlYWRhYmxlDQp3ZGF5KHJlbGVhc2VzJGRhdGV0aW1lLCBsYWJlbCA9IFRSVUUpICU+JSB0YWJsZSgpDQoNCiMgQ3JlYXRlIGNvbHVtbiB3ZGF5IHRvIGhvbGQgbGFiZWxsZWQgd2VlayBkYXlzDQpyZWxlYXNlcyR3ZGF5IDwtIHdkYXkocmVsZWFzZXMkZGF0ZXRpbWUsIGxhYmVsID0gVFJVRSkNCg0KIyBQbG90IGJhcmNoYXJ0IG9mIHdlZWtkYXkgYnkgdHlwZSBvZiByZWxlYXNlDQpnZ3Bsb3QocmVsZWFzZXMsIGFlcyh4ID0gd2RheSkpICsNCiAgZ2VvbV9iYXIoKSArDQogIGZhY2V0X3dyYXAofiB0eXBlLCBuY29sID0gMSwgc2NhbGUgPSAiZnJlZV95IikNCmBgYA0KTG9va3MgbGlrZSBub3QgdG9vIG1hbnkgcmVsZWFzZXMgb2NjdXIgb24gdGhlIHdlZWtlbmRzLCBhbmQgdGhlcmUgaXMgcXVpdGUgYSBkaWZmZXJlbnQgd2Vla2RheSBwYXR0ZXJuIGJldHdlZW4gbWlub3IgYW5kIHBhdGNoIHJlbGVhc2VzLg0KDQojIyMgRXh0cmFjdGluZyBmb3IgcGxvdHRpbmcNCg0KRXh0cmFjdGluZyBjb21wb25lbnRzIGZyb20gYSBkYXRldGltZSBpcyBwYXJ0aWN1bGFybHkgdXNlZnVsIHdoZW4gZXhwbG9yaW5nIGRhdGEuIEVhcmxpZXIgaW4gdGhlIGNoYXB0ZXIgeW91IGltcG9ydGVkIGRhaWx5IGRhdGEgZm9yIHdlYXRoZXIgaW4gQXVja2xhbmQsIGFuZCBjcmVhdGVkIGEgdGltZSBzZXJpZXMgcGxvdCBvZiB0ZW4geWVhcnMgb2YgZGFpbHkgbWF4aW11bSB0ZW1wZXJhdHVyZS4gV2hpbGUgdGhhdCBwbG90IGdpdmVzIHlvdSBhIGdvb2Qgb3ZlcnZpZXcgb2YgdGhlIHdob2xlIHRlbiB5ZWFycywgaXQncyBoYXJkIHRvIHNlZSB0aGUgYW5udWFsIHBhdHRlcm4uDQoNCldlJ2xsIHVzZSBjb21wb25lbnRzIG9mIHRoZSBkYXRlcyB0byBoZWxwIGV4cGxvcmUgdGhlIHBhdHRlcm4gb2YgbWF4aW11bSB0ZW1wZXJhdHVyZSBvdmVyIHRoZSB5ZWFyLiBUaGUgZmlyc3Qgc3RlcCBpcyB0byBjcmVhdGUgc29tZSBuZXcgY29sdW1ucyB0byBob2xkIHRoZSBleHRyYWN0ZWQgcGllY2VzLCB0aGVuIHlvdSdsbCB1c2UgdGhlbSBpbiBhIGNvdXBsZSBvZiBwbG90cy4NCmBgYHtyfQ0KbGlicmFyeShnZ3Bsb3QyKQ0KbGlicmFyeShkcGx5cikNCmxpYnJhcnkoZ2dyaWRnZXMpDQoNCiMgQWRkIGNvbHVtbnMgZm9yIHllYXIsIHlkYXkgYW5kIG1vbnRoDQpha2xfZGFpbHkgPC0gYWtsX2RhaWx5ICU+JQ0KICBtdXRhdGUoDQogICAgeWVhciA9IHllYXIoZGF0ZSksDQogICAgeWRheSA9IHlkYXkoZGF0ZSksDQogICAgbW9udGggPSBtb250aChkYXRlLCBsYWJlbCA9IFRSVUUpKQ0KDQojIFBsb3QgbWF4X3RlbXAgYnkgeWRheSBmb3IgYWxsIHllYXJzDQpnZ3Bsb3QoYWtsX2RhaWx5LCBhZXMoeCA9IHlkYXksIHkgPSBtYXhfdGVtcCkpICsNCiAgZ2VvbV9saW5lKGFlcyhncm91cCA9IHllYXIpLCBhbHBoYSA9IDAuNSkNCg0KIyBFeGFtaW5lIGRpc3RyaWJ1dGlvbiBvZiBtYXhfdGVtcCBieSBtb250aA0KZ2dwbG90KGFrbF9kYWlseSwgYWVzKHggPSBtYXhfdGVtcCwgeSA9IG1vbnRoLCBoZWlnaHQgPSAuLmRlbnNpdHkuLikpICsNCiAgZ2VvbV9kZW5zaXR5X3JpZGdlcyhzdGF0ID0gImRlbnNpdHkiKQ0KYGBgDQpCb3RoIHBsb3RzIGdpdmUgYSBncmVhdCB2aWV3IGludG8gYm90aCB0aGUgZXhwZWN0ZWQgdGVtcGVyYXR1cmVzIGFuZCBob3cgbXVjaCB0aGV5IHZhcnkuIExvb2tzIGxpa2UgSmFuLCBGZWIgYW5kIE1hciBhcmUgZ3JlYXQgbW9udGhzIHRvIHZpc2l0IGlmIHlvdSB3YW50IHdhcm0gdGVtcGVyYXR1cmVzLiBEaWQgeW91IG5vdGljZSB0aGUgd2FybmluZyBtZXNzYWdlcz8gVGhlc2UgYXJlIGEgY29uc2VxdWVuY2Ugb2Ygc29tZSBtaXNzaW5nIHZhbHVlcyBpbiB0aGUgbWF4X3RlbXAgY29sdW1uLiBUaGV5IGFyZSBhIHJlbWluZGVyIHRvIHRoaW5rIGNhcmVmdWxseSBhYm91dCB3aGF0IHlvdSBtaWdodCBtaXNzIGJ5IGlnbm9yaW5nIG1pc3NpbmcgdmFsdWVzLg0KDQojIyMgRXh0cmFjdGluZyBmb3IgZmlsdGVyaW5nIGFuZCBzdW1tYXJpemluZw0KDQpBbm90aGVyIHJlYXNvbiB0byBleHRyYWN0IGNvbXBvbmVudHMgaXMgdG8gaGVscCB3aXRoIGZpbHRlcmluZyBvYnNlcnZhdGlvbnMgb3IgY3JlYXRpbmcgc3VtbWFyaWVzLiBGb3IgZXhhbXBsZSwgaWYgeW91IGFyZSBvbmx5IGludGVyZXN0ZWQgaW4gb2JzZXJ2YXRpb25zIG1hZGUgb24gd2Vla2RheXMgKGkuZS4gbm90IG9uIHdlZWtlbmRzKSB5b3UgY291bGQgZXh0cmFjdCB0aGUgd2Vla2RheXMgdGhlbiBmaWx0ZXIgb3V0IHdlZWtlbmRzLCBlLmcuIHdkYXkoZGF0ZSkgJWluJSAyOjYuDQoNCkluIHRoZSBsYXN0IGV4ZXJjaXNlIHlvdSBzYXcgdGhhdCBKYW51YXJ5LCBGZWJydWFyeSBhbmQgTWFyY2ggd2VyZSBncmVhdCB0aW1lcyB0byB2aXNpdCBBdWNrbGFuZCBmb3Igd2FybSB0ZW1wZXJhdHVyZXMsIGJ1dCB3aWxsIHlvdSBuZWVkIGEgcmFpbmNvYXQ/DQpgYGB7cn0NCiMgQ3JlYXRlIG5ldyBjb2x1bW5zIGhvdXIsIG1vbnRoIGFuZCByYWlueQ0KYWtsX2hvdXJseSA8LSBha2xfaG91cmx5ICU+JQ0KICBtdXRhdGUoDQogICAgaG91ciA9IGhvdXIoZGF0ZXRpbWUpLA0KICAgIG1vbnRoID0gbW9udGgoZGF0ZXRpbWUsIGxhYmVsID0gVFJVRSksDQogICAgcmFpbnkgPSB3ZWF0aGVyID09ICJQcmVjaXBpdGF0aW9uIg0KICApDQoNCiMgRmlsdGVyIGZvciBob3VycyBiZXR3ZWVuIDhhbSBhbmQgMTBwbSAoaW5jbHVzaXZlKQ0KYWtsX2RheSA8LSBha2xfaG91cmx5ICU+JSANCiAgZmlsdGVyKGhvdXIgPj0gOCwgaG91cjw9MjIpDQoNCiMgU3VtbWFyaXNlIGZvciBlYWNoIGRhdGUgaWYgdGhlcmUgaXMgYW55IHJhaW4NCnJhaW55X2RheXMgPC0gYWtsX2RheSAlPiUgDQogIGdyb3VwX2J5KG1vbnRoLCBkYXRlKSAlPiUNCiAgc3VtbWFyaXNlKA0KICAgIGFueV9yYWluID0gYW55KHJhaW55KQ0KICApDQoNCiMgU3VtbWFyaXNlIGZvciBlYWNoIG1vbnRoLCB0aGUgbnVtYmVyIG9mIGRheXMgd2l0aCByYWluDQpyYWlueV9kYXlzICU+JSANCiAgc3VtbWFyaXNlKA0KICAgIGRheXNfcmFpbnkgPSBzdW0oYW55X3JhaW4pDQogICkNCmBgYA0KQXQgbGVhc3QgaW4gMjAxNiwgaXQgbG9va3MgbGlrZSB5b3UnbGwgc3RpbGwgbmVlZCB0byBwYWNrIGEgcmFpbmNvYXQgaWYgeW91IHZpc2l0IGluIEphbiwgRmViIG9yIE1hcmNoLiBNb250aHMgb2YgY291cnNlIGFyZSBkaWZmZXJlbnQgbGVuZ3RocyBzbyB3ZSBzaG91bGQgcmVhbGx5IGNvcnJlY3QgZm9yIHRoYXQsIHRha2UgYSBsb29rIGF0IGRheXNfaW5fbW9udGgoKSBmb3IgaGVscGluZyB3aXRoIHRoYXQuDQoNCiMjIFJvdW5kaW5nIGRhdGV0aW1lcw0KDQogcm91bmRfZGF0ZSgpIHJvdW5kcyBhIGRhdGUgdG8gdGhlIG5lYXJlc3QgdmFsdWUsIGZsb29yX2RhdGUoKSByb3VuZHMgZG93biwgYW5kIGNlaWxpbmdfZGF0ZSgpIHJvdW5kcyB1cC4NCg0KQWxsIHRocmVlIHRha2UgYSB1bml0IGFyZ3VtZW50IHdoaWNoIHNwZWNpZmllcyB0aGUgcmVzb2x1dGlvbiBvZiByb3VuZGluZy4gWW91IGNhbiBzcGVjaWZ5ICJzZWNvbmQiLCAibWludXRlIiwgImhvdXIiLCAiZGF5IiwgIndlZWsiLCAibW9udGgiLCAiYmltb250aCIsICJxdWFydGVyIiwgImhhbGZ5ZWFyIiwgb3IgInllYXIiLiBPciwgeW91IGNhbiBzcGVjaWZ5IGFueSBtdWx0aXBsZSBvZiB0aG9zZSB1bml0cywgZS5nLiAiNSB5ZWFycyIsICIzIG1pbnV0ZXMiIGV0Yy4NCmBgYHtyfQ0Kcl8zXzRfMSA8LSB5bWRfaG1zKCIyMDE2LTA1LTAzIDA3OjEzOjI4IFVUQyIpDQoNCiMgUm91bmQgZG93biB0byBkYXkNCmZsb29yX2RhdGUocl8zXzRfMSwgdW5pdCA9ICJkYXkiKQ0KDQojIFJvdW5kIHRvIG5lYXJlc3QgNSBtaW51dGVzDQpyb3VuZF9kYXRlKHJfM180XzEsIHVuaXQgPSAiNSBtaW51dGVzIikNCg0KIyBSb3VuZCB1cCB0byB3ZWVrIA0KY2VpbGluZ19kYXRlKHJfM180XzEsIHVuaXQgPSAid2VlayIpDQoNCiMgU3VidHJhY3Qgcl8zXzRfMSByb3VuZGVkIGRvd24gdG8gZGF5DQpyXzNfNF8xIC0gZmxvb3JfZGF0ZShyXzNfNF8xLCB1bml0ID0gImRheSIpDQpgYGANClRoYXQgbGFzdCB0ZWNobmlxdWUgb2Ygc3VidHJhY3RpbmcgYSByb3VuZGVkIGRhdGV0aW1lIGZyb20gYW4gdW5yb3VuZGVkIG9uZSBpcyBhIHJlYWxseSB1c2VmdWwgdHJpY2sgdG8gcmVtZW1iZXIuDQoNCiMjIyBSb3VuZGluZyB3aXRoIHRoZSB3ZWF0aGVyIGRhdGENCg0KV2hlbiBpcyByb3VuZGluZyB1c2VmdWw/IEluIGEgbG90IG9mIHRoZSBzYW1lIHNpdHVhdGlvbnMgZXh0cmFjdGluZyBkYXRlIGNvbXBvbmVudHMgaXMgdXNlZnVsLiBUaGUgYWR2YW50YWdlIG9mIHJvdW5kaW5nIG92ZXIgZXh0cmFjdGluZyBpcyB0aGF0IGl0IG1haW50YWlucyB0aGUgY29udGV4dCBvZiB0aGUgdW5pdC4gRm9yIGV4YW1wbGUsIGV4dHJhY3RpbmcgdGhlIGhvdXIgZ2l2ZXMgeW91IHRoZSBob3VyIHRoZSBkYXRldGltZSBvY2N1cnJlZCwgYnV0IHlvdSBsb3NlIHRoZSBkYXkgdGhhdCBob3VyIG9jY3VycmVkIG9uICh1bmxlc3MgeW91IGV4dHJhY3QgdGhhdCB0b28pLCBvbiB0aGUgb3RoZXIgaGFuZCwgcm91bmRpbmcgdG8gdGhlIG5lYXJlc3QgaG91ciBtYWludGFpbnMgdGhlIGRheSwgbW9udGggYW5kIHllYXIuDQoNCldlJ2xsIGV4cGxvcmUgaG93IG1hbnkgb2JzZXJ2YXRpb25zIHBlciBob3VyIHRoZXJlIHJlYWxseSBhcmUgaW4gdGhlIGhvdXJseSBBdWNrbGFuZCB3ZWF0aGVyIGRhdGEuDQpgYGB7cn0NCiMgQ3JlYXRlIGRheV9ob3VyLCBkYXRldGltZSByb3VuZGVkIGRvd24gdG8gaG91cg0KYWtsX2hvdXJseSA8LSBha2xfaG91cmx5ICU+JQ0KICBtdXRhdGUoDQogICAgZGF5X2hvdXIgPSBmbG9vcl9kYXRlKGRhdGV0aW1lLCB1bml0ID0gImhvdXIiKQ0KICApDQoNCiMgQ291bnQgb2JzZXJ2YXRpb25zIHBlciBob3VyICANCmFrbF9ob3VybHkgJT4lIA0KICBjb3VudChkYXlfaG91cikgDQoNCiMgRmluZCBkYXlfaG91cnMgd2l0aCBuICE9IDIgIA0KYWtsX2hvdXJseSAlPiUgDQogIGNvdW50KGRheV9ob3VyKSAlPiUNCiAgZmlsdGVyKG4gIT0gMikgJT4lIA0KICBhcnJhbmdlKGRlc2MobikpDQpgYGANCjkyIGhvdXJzIHRoYXQgZG9uJ3QgaGF2ZSB0d28gbWVhc3VyZW1lbnRzLiBJbnRlcmVzdGluZ2x5IHRoZXJlIGFyZSBmb3VyIG1lYXN1cmVtZW50cyBvbiAyMDE2LTA0LTAzIGFuZCAyMDE2LTA5LTI1LCB0aGV5IGhhcHBlbiB0byBiZSB0aGUgZGF5cyBEYXlsaWdodCBTYXZpbmcgc3RhcnRzIGFuZCBlbmRzLg0KDQojIEFyaXRobWV0aWMgd2l0aCBEYXRlcyBhbmQgVGltZXMNCg0KIyMgVGFraW5nIGRpZmZlcmVuZXMgb2YgZGF0ZXRpbWVzDQoNCiMjIyBIb3cgbG9uZyBoYXMgaXQgYmVlbj8NCg0KVG8gZ2V0IGZpbmVyIGNvbnRyb2wgb3ZlciBhIGRpZmZlcmVuY2UgYmV0d2VlbiBkYXRldGltZXMgdXNlIHRoZSBiYXNlIGZ1bmN0aW9uIGRpZmZ0aW1lKCkuIEZvciBleGFtcGxlIGluc3RlYWQgb2YgdGltZTEgLSB0aW1lMiwgeW91IHVzZSBkaWZmdGltZSh0aW1lMSwgdGltZTIpLg0KDQpkaWZmdGltZSgpIHRha2VzIGFuIGFyZ3VtZW50IHVuaXRzIHdoaWNoIHNwZWNpZmllcyB0aGUgdW5pdHMgZm9yIHRoZSBkaWZmZXJlbmNlLiBZb3VyIG9wdGlvbnMgYXJlICJzZWNzIiwgIm1pbnMiLCAiaG91cnMiLCAiZGF5cyIsIG9yICJ3ZWVrcyINCg0KV2UnbGwgYWxzbyBzZWUgdGhlIGx1YnJpZGF0ZSBmdW5jdGlvbnMgdG9kYXkoKSBhbmQgbm93KCkgd2hpY2ggd2hlbiBjYWxsZWQgd2l0aCBubyBhcmd1bWVudHMgcmV0dXJuIHRoZSBjdXJyZW50IGRhdGUgYW5kIHRpbWUgaW4geW91ciBzeXN0ZW0ncyB0aW1lem9uZS4NCmBgYHtyfQ0KIyBUaGUgZGF0ZSBvZiBsYW5kaW5nIGFuZCBtb21lbnQgb2Ygc3RlcA0KZGF0ZV9sYW5kaW5nIDwtIG1keSgiSnVseSAyMCwgMTk2OSIpDQptb21lbnRfc3RlcCA8LSBtZHlfaG1zKCJKdWx5IDIwLCAxOTY5LCAwMjo1NjoxNSIsIHR6ID0gIlVUQyIpDQoNCiMgSG93IG1hbnkgZGF5cyBzaW5jZSB0aGUgZmlyc3QgbWFuIG9uIHRoZSBtb29uPw0KZGlmZnRpbWUodG9kYXkoKSwgZGF0ZV9sYW5kaW5nLCB1bml0cyA9ICJkYXlzIikNCg0KIyBIb3cgbWFueSBzZWNvbmRzIHNpbmNlIHRoZSBmaXJzdCBtYW4gb24gdGhlIG1vb24/DQpkaWZmdGltZShub3coKSwgbW9tZW50X3N0ZXAsIHVuaXRzID0gInNlY3MiKQ0KYGBgDQojIyMgSG93IG1hbnkgc2Vjb25kcyBhcmUgaW4gYSBkYXk/DQoNCkhvdyBtYW55IHNlY29uZHMgYXJlIGluIGEgZGF5PyBUaGVyZSBhcmUgMjQgaG91cnMgaW4gYSBkYXksIDYwIG1pbnV0ZXMgaW4gYW4gaG91ciwgYW5kIDYwIHNlY29uZHMgaW4gYSBtaW51dGUsIHNvIHRoZXJlIHNob3VsZCBiZSAyNCo2MCo2MCA9IDg2NDAwIHNlY29uZHMsIHJpZ2h0Pw0KDQpOb3QgYWx3YXlzIQ0KYGBge3J9DQojIFRocmVlIGRhdGVzDQptYXJfMTEgPC0geW1kX2htcygiMjAxNy0wMy0xMSAxMjowMDowMCIsIA0KICB0eiA9ICJBbWVyaWNhL0xvc19BbmdlbGVzIikNCm1hcl8xMiA8LSB5bWRfaG1zKCIyMDE3LTAzLTEyIDEyOjAwOjAwIiwgDQogIHR6ID0gIkFtZXJpY2EvTG9zX0FuZ2VsZXMiKQ0KbWFyXzEzIDwtIHltZF9obXMoIjIwMTctMDMtMTMgMTI6MDA6MDAiLCANCiAgdHogPSAiQW1lcmljYS9Mb3NfQW5nZWxlcyIpDQoNCiMgRGlmZmVyZW5jZSBiZXR3ZWVuIG1hcl8xMyBhbmQgbWFyXzEyIGluIHNlY29uZHMNCmRpZmZ0aW1lKG1hcl8xMywgbWFyXzEyLCB1bml0cyA9ICJzZWNzIikNCg0KIyBEaWZmZXJlbmNlIGJldHdlZW4gbWFyXzEyIGFuZCBtYXJfMTEgaW4gc2Vjb25kcw0KZGlmZnRpbWUobWFyXzEyLCBtYXJfMTEsIHVuaXRzID0gInNlY3MiKQ0KYGBgDQpXaHkgd291bGQgYSBkYXkgb25seSBoYXZlIDgyODAwIHNlY29uZHM/IEF0IDJhbSBvbiBNYXIgMTJ0aCAyMDE3LCBEYXlsaWdodCBTYXZpbmdzIHN0YXJ0ZWQgaW4gdGhlIFBhY2lmaWMgdGltZXpvbmUuIFRoYXQgbWVhbnMgYSB3aG9sZSBob3VyIG9mIHNlY29uZHMgZ2V0cyBza2lwcGVkIGJldHdlZW4gbm9vbiBvbiB0aGUgMTF0aCBhbmQgbm9vbiBvbiB0aGUgMTJ0aA0KDQojIyBUaW1lIHNwYW5zDQojIyMgQWRkaW5nIG9yIHN1YnRyYWN0aW5nIGEgdGltZSBzcGFuIHRvIGEgZGF0ZXRpbWUNCkEgY29tbW9uIHVzZSBvZiB0aW1lIHNwYW5zIGlzIHRvIGFkZCBvciBzdWJ0cmFjdCB0aGVtIGZyb20gYSBtb21lbnQgaW4gdGltZS4gRm9yLCBleGFtcGxlIHRvIGNhbGN1bGF0ZSB0aGUgdGltZSBvbmUgZGF5IGluIHRoZSBmdXR1cmUgZnJvbSBtYXJfMTEgeW91IGNvdWxkIGRvIGVpdGhlciBvZjoNCg0KICAgIG1hcl8xMSArIGRheXMoMSkNCiAgICBtYXJfMTEgKyBkZGF5cygxKQ0KDQpJZiB5b3Ugd2FudCB0byBhY2NvdW50IGZvciB0aGUgZmFjdCB0aGF0IHRpbWUgdW5pdHMsIGluIHRoaXMgY2FzZSBkYXlzLCBoYXZlIGRpZmZlcmVudCBsZW5ndGhzIChpLmUuIGR1ZSB0byBkYXlsaWdodCBzYXZpbmdzKSwgeW91IHdhbnQgYSBwZXJpb2QgZGF5cygpLiBJZiB5b3Ugd2FudCB0aGUgdGltZSA4NjQwMCBzZWNvbmRzIGluIHRoZSBmdXR1cmUgeW91IHVzZSBhIGR1cmF0aW9uIGRkYXlzKCkuDQpgYGB7cn0NCiMgQWRkIGEgcGVyaW9kIG9mIG9uZSB3ZWVrIHRvIG1vbl8ycG0NCm1vbl8ycG0gPC0gZG15X2htKCIyNyBBdWcgMjAxOCAxNDowMCIpDQptb25fMnBtICsgd2Vla3MoMSkNCg0KIyBBZGQgYSBkdXJhdGlvbiBvZiA4MSBob3VycyB0byB0dWVfOWFtDQp0dWVfOWFtIDwtIGRteV9obSgiMjggQXVnIDIwMTggOTowMCIpDQp0dWVfOWFtICsgaG91cnMoODEpDQoNCiMgU3VidHJhY3QgYSBwZXJpb2Qgb2YgZml2ZSB5ZWFycyBmcm9tIHRvZGF5KCkNCnRvZGF5KCkgLSB5ZWFycyg1KQ0KDQojIFN1YnRyYWN0IGEgZHVyYXRpb24gb2YgZml2ZSB5ZWFycyBmcm9tIHRvZGF5KCkNCnRvZGF5KCkgLSBkeWVhcnMoNSkNCmBgYA0KV2h5IGRpZCBzdWJ0cmFjdGluZyBhIGR1cmF0aW9uIG9mIGZpdmUgeWVhcnMgZnJvbSB0b2RheSwgZ2l2ZSBhIGRpZmZlcmVudCBhbnN3ZXIgdG8gc3VidHJhY3RpbmcgYSBwZXJpb2Qgb2YgZml2ZSB5ZWFycz8gUGVyaW9kcyBrbm93IGFib3V0IGxlYXAgeWVhcnMsIGFuZCBzaW5jZSBmaXZlIHllYXJzIGFnbyBpbmNsdWRlcyBhdCBsZWFzdCBvbmUgbGVhcCB5ZWFyIChhc3N1bWluZyB5b3UgYXJlbid0IHRha2luZyB0aGlzIGNvdXJzZSBpbiAyMTAwKSB0aGUgcGVyaW9kIG9mIGZpdmUgeWVhcnMgaXMgbG9uZ2VyIHRoYW4gdGhlIGR1cmF0aW9uIG9mIDM2NSo1IGRheXMuDQoNCldoZW4gZGVhbGluZyB3aXRoIGh1bWFuIGludGVycHJldGFpb25zIG9mIGRhdGVzIGFuZCB0aW1lIHlvdSB3YW50IHRvIHVzZSBwZXJpb2RzLg0KDQojIyMgQXJpdGhtZXRpYyB3aXRoIHRpbWVzcGFucw0KDQpZb3UgY2FuIGFkZCBhbmQgc3VidHJhY3QgdGltZXNwYW5zIHRvIGNyZWF0ZSBkaWZmZXJlbnQgbGVuZ3RoIHRpbWVzcGFucywgYW5kIGV2ZW4gbXVsdGlwbHkgdGhlbSBieSBudW1iZXJzLiBGb3IgZXhhbXBsZSwgdG8gY3JlYXRlIGEgZHVyYXRpb24gb2YgdGhyZWUgZGF5cyBhbmQgdGhyZWUgaG91cnMgeW91IGNvdWxkIGRvOiBkZGF5cygzKSArIGRob3VycygzKSwgb3IgM1wqZGRheXMoMSkgKyAzXCpkaG91cnMoMSkgb3IgZXZlbiAzXCooZGRheXMoMSkgKyBkaG91cnMoMSkpLg0KDQpUaGVyZSB3YXMgYW4gZWNsaXBzZSBvdmVyIE5vcnRoIEFtZXJpY2Egb24gMjAxNy0wOC0yMSBhdCAxODoyNjo0MC4gSXQncyBwb3NzaWJsZSB0byBwcmVkaWN0IHRoZSBuZXh0IGVjbGlwc2Ugd2l0aCBzaW1pbGFyIGdlb21ldHJ5IGJ5IGNhbGN1bGF0aW5nIHRoZSB0aW1lIGFuZCBkYXRlIG9uZSBbU2Fyb3NdKGh0dHBzOi8vZWNsaXBzZS5nc2ZjLm5hc2EuZ292L1NFc2Fyb3MvU0VzYXJvcy5odG1sKSBpbiB0aGUgZnV0dXJlLiBBIFNhcm9zIGlzIGEgbGVuZ3RoIG9mIHRpbWUgdGhhdCBjb3JyZXNwb25kcyB0byAyMjMgU3lub2RpYyBtb250aHMsIGEgU3lub2RpYyBtb250aCBiZWluZyB0aGUgcGVyaW9kIG9mIHRoZSBNb29uJ3MgcGhhc2VzLCBhIGR1cmF0aW9uIG9mIDI5IGRheXMsIDEyIGhvdXJzLCA0NCBtaW51dGVzIGFuZCAzIHNlY29uZHMuDQpgYGB7cn0NCiMgVGltZSBvZiBOb3J0aCBBbWVyaWNhbiBFY2xpcHNlIDIwMTcNCmVjbGlwc2VfMjAxNyA8LSB5bWRfaG1zKCIyMDE3LTA4LTIxIDE4OjI2OjQwIikNCg0KIyBEdXJhdGlvbiBvZiAyOSBkYXlzLCAxMiBob3VycywgNDQgbWlucyBhbmQgMyBzZWNzDQpzeW5vZGljIDwtIGRkYXlzKDI5KSArIGRob3VycygxMikgKyBkbWludXRlcyg0NCkgKyBkc2Vjb25kcygzKQ0KDQojIDIyMyBzeW5vZGljIG1vbnRocw0Kc2Fyb3MgPC0gMjIzKnN5bm9kaWMNCg0KIyBBZGQgc2Fyb3MgdG8gZWNsaXBzZV8yMDE3DQplY2xpcHNlXzIwMTcgKyBzYXJvcw0KYGBgDQoyMDM1IGlzIGEgbG9uZyB3YXkgYXdheSBmb3IgYW4gZWNsaXBzZSwgYnV0IGx1Y2tpbHkgdGhlcmUgYXJlIGVjbGlwc2VzIG9uIGRpZmZlcmVudCBTYXJvcyBjeWNsZXMsIHNvIHlvdSBjYW4gc2VlIG9uZSBtdWNoIHNvb25lci4NCg0KIyMjIEdlbmVyYXRpbmcgc2VxdWVuY2VzIG9mIGRhdGV0aW1lcw0KDQpCeSBjb21iaW5pbmcgYWRkaXRpb24gYW5kIG11bHRpcGxpY2F0aW9uIHdpdGggc2VxdWVuY2VzIHlvdSBjYW4gZ2VuZXJhdGUgc2VxdWVuY2VzIG9mIGRhdGV0aW1lcy4gRm9yIGV4YW1wbGUsIHlvdSBjYW4gZ2VuZXJhdGUgYSBzZXF1ZW5jZSBvZiBwZXJpb2RzIGZyb20gMSBkYXkgdXAgdG8gMTAgZGF5cyB3aXRoOg0KDQogICAgMToxMCAqIGRheXMoMSkNCiAgICANClRoZW4gYnkgYWRkaW5nIHRoaXMgc2VxdWVuY2UgdG8gYSBzcGVjaWZpYyBkYXRldGltZSwgeW91IGNhbiBjb25zdHJ1Y3QgYSBzZXF1ZW5jZSBvZiBkYXRldGltZXMgZnJvbSAxIGRheSB1cCB0byAxMCBkYXlzIGludG8gdGhlIGZ1dHVyZQ0KDQogICAgdG9kYXkoKSArIDE6MTAgKiBkYXlzKDEpDQoNCg0KYGBge3J9DQojIEFkZCBhIHBlcmlvZCBvZiA4IGhvdXJzIHRvIHRvZGF5DQp0b2RheV84YW0gPC0gdG9kYXkoKSArIGhvdXJzKDgpDQoNCiMgU2VxdWVuY2Ugb2YgdHdvIHdlZWtzIGZyb20gMSB0byAyNg0KZXZlcnlfdHdvX3dlZWtzIDwtIDE6MjYgKiB3ZWVrcygyKQ0KDQojIENyZWF0ZSBkYXRldGltZSBmb3IgZXZlcnkgdHdvIHdlZWtzIGZvciBhIHllYXINCmV2ZXJ5X3R3b193ZWVrcyArIHRvZGF5XzhhbQ0KYGBgDQojIyMgVGhlIHRyaWNreSB0aGluZyBhYm91dCBtb250aHMNCg0KV2hhdCBzaG91bGQgeW1kKCIyMDE4LTAxLTMxIikgKyBtb250aHMoMSkgcmV0dXJuPyBTaG91bGQgaXQgYmUgMzAsIDMxIG9yIDI4IGRheXMgaW4gdGhlIGZ1dHVyZT8gVHJ5IGl0LiBJbiBnZW5lcmFsIGx1YnJpZGF0ZSByZXR1cm5zIHRoZSBzYW1lIGRheSBvZiB0aGUgbW9udGggaW4gdGhlIG5leHQgbW9udGgsIGJ1dCBzaW5jZSB0aGUgMzFzdCBvZiBGZWJydWFyeSBkb2Vzbid0IGV4aXN0IGx1YnJpZGF0ZSByZXR1cm5zIGEgbWlzc2luZyB2YWx1ZSwgTkEuDQoNClRoZXJlIGFyZSBhbHRlcm5hdGl2ZSBhZGRpdGlvbiBhbmQgc3VidHJhY3Rpb24gb3BlcmF0b3JzOiAlbSslIGFuZCAlbS0lIHRoYXQgaGF2ZSBkaWZmZXJlbnQgYmVoYXZpb3IuIFJhdGhlciB0aGFuIHJldHVybmluZyBhbiBOQSBmb3IgYSBub24tZXhpc3RlbnQgZGF0ZSwgdGhleSByb2xsIGJhY2sgdG8gdGhlIGxhc3QgZXhpc3RpbmcgZGF0ZS4NCmBgYHtyfQ0KamFuXzMxIDwtIGFzLkRhdGUoIjIwMjAtMDEtMzEiKQ0KYGBgDQpgYGB7cn0NCiMgQSBzZXF1ZW5jZSBvZiAxIHRvIDEyIHBlcmlvZHMgb2YgMSBtb250aA0KbW9udGhfc2VxIDwtIDE6MTIgKiBtb250aHMoMSkNCg0KIyBBZGQgMSB0byAxMiBtb250aHMgdG8gamFuXzMxDQpqYW5fMzEgKyBtb250aF9zZXENCg0KIyBSZXBsYWNlICsgd2l0aCAlbSslDQpqYW5fMzEgJW0rJSBtb250aF9zZXENCg0KIyBSZXBsYWNlICsgd2l0aCAlbS0lDQpqYW5fMzEgJW0tJSBtb250aF9zZXENCmBgYA0KdXNlIHRoZXNlIG9wZXJhdG9ycyB3aXRoIGNhdXRpb24sIHVubGlrZSArIGFuZCAtLCB5b3UgbWlnaHQgbm90IGdldCB4IGJhY2sgZnJvbSB4ICVtKyUgbW9udGhzKDEpICVtLSUgbW9udGhzKDEpLiBJZiB5b3UnZCBwcmVmZXIgdGhhdCB0aGUgZGF0ZSB3YXMgcm9sbGVkIGZvcndhcmQgY2hlY2sgb3V0IGFkZF93aXRoX3JvbGxiYWNrKCkgd2hpY2ggaGFzIHJvbGxfdG9fZmlyc3QgYXJndW1lbnQuDQoNCiMjIEludGVydmFscw0KDQojIyMgRXhhbWluaW5nIGludGVydmFscy4gUmVpZ25zIG9mIGtpbmdzIGFuZCBxdWVlbnMNCg0KWW91IGNhbiBjcmVhdGUgYW4gaW50ZXJ2YWwgYnkgdXNpbmcgdGhlIG9wZXJhdG9yICUtLSUgd2l0aCB0d28gZGF0ZXRpbWVzLiBGb3IgZXhhbXBsZSB5bWQoIjIwMDEtMDEtMDEiKSAlLS0lIHltZCgiMjAwMS0xMi0zMSIpIGNyZWF0ZXMgYW4gaW50ZXJ2YWwgZm9yIHRoZSB5ZWFyIG9mIDIwMDEuDQpPbmNlIHlvdSBoYXZlIGFuIGludGVydmFsIHlvdSBjYW4gZmluZCBvdXQgY2VydGFpbiBwcm9wZXJ0aWVzIGxpa2UgaXRzIHN0YXJ0LCBlbmQgYW5kIGxlbmd0aCB3aXRoIGludF9zdGFydCgpLCBpbnRfZW5kKCkgYW5kIGludF9sZW5ndGgoKSByZXNwZWN0aXZlbHkuDQpgYGB7cn0NCm1vbmFyY2hzIDwtIHJlYWQuY3N2KCJtb25hcmNocy5jc3YiLCBzdHJpbmdzQXNGYWN0b3JzPUZBTFNFKQ0KYGBgDQpgYGB7cn0NCiMgQ3JlYXRlIGFuIGludGVydmFsIGZvciByZWlnbg0KbW9uYXJjaHMgPC0gbW9uYXJjaHMgJT4lDQogIG11dGF0ZShyZWlnbiA9IGZyb20gJS0tJSB0bykgDQoNCiMgRmluZCB0aGUgbGVuZ3RoIG9mIHJlaWduLCBhbmQgYXJyYW5nZQ0KbW9uYXJjaHMgJT4lDQogIG11dGF0ZShsZW5ndGggPSBpbnRfbGVuZ3RoIChyZWlnbikpICU+JSANCiAgYXJyYW5nZShkZXNjKGxlbmd0aCkpICU+JQ0KICBzZWxlY3QobmFtZSwgbGVuZ3RoLCBkb21pbmlvbikNCmBgYA0KVGhlIGN1cnJlbnQgcXVlZW4sIEVsaXphYmV0aCBJSSwgaGFzIHJ1bGVkIGZvciAyMDcwMTQ0MDAwIHNlY29uZHPigKZ5b3UnbGwgc2VlIGEgYmV0dGVyIHdheSB0byBkaXNwbGF5IHRoZSBsZW5ndGggbGF0ZXIuIElmIHlvdSBrbm93IHlvdXIgQnJpdGlzaCBtb25hcmNocywgeW91IG1pZ2h0IG5vdGljZSBHZW9yZ2UgSUlJIGRvZXNuJ3QgYXBwZWFyIGluIHRoZSB0aGUgdG9wIDUuIEluIHRoaXMgZGF0YSwgaGlzIHJlaWduIGlzIHNwcmVhZCBvdmVyIHR3byByb3dzIGZvciBVLksuIEFuZCBHcmVhdCBCcml0YWluIGFuZCB5b3Ugd291bGQgbmVlZCB0byBhZGQgdGhlaXIgbGVuZ3RocyB0byBzZWUgaGlzIHRvdGFsIHJlaWduLg0KDQojIyMgQ29tcGFyaW5nIGludGVydmFscyBhbmQgZGF0ZXRpbWVzDQoNCkEgY29tbW9uIHRhc2sgd2l0aCBpbnRlcnZhbHMgaXMgdG8gYXNrIGlmIGEgY2VydGFpbiB0aW1lIGlzIGluc2lkZSB0aGUgaW50ZXJ2YWwgb3Igd2hldGhlciBpdCBvdmVybGFwcyB3aXRoIGFub3RoZXIgaW50ZXJ2YWwuDQoNClRoZSBvcGVyYXRvciAld2l0aGluJSB0ZXN0cyBpZiB0aGUgZGF0ZXRpbWUgKG9yIGludGVydmFsKSBvbiB0aGUgbGVmdCBoYW5kIHNpZGUgaXMgd2l0aGluIHRoZSBpbnRlcnZhbCBvZiB0aGUgcmlnaHQgaGFuZCBzaWRlLg0KDQpGb3IgZXhhbXBsZSwgaWYgeTIwMDEgaXMgdGhlIGludGVydmFsIGNvdmVyaW5nIHRoZSB5ZWFyIDIwMDEsDQoNCiAgICB5MjAwMSA8LSB5bWQoIjIwMDEtMDEtMDEiKSAlLS0lIHltZCgiMjAwMS0xMi0zMSIpDQogICAgDQpUaGVuIHltZCgiMjAwMS0wMy0zMCIpICV3aXRoaW4lIHkyMDAxIHdpbGwgcmV0dXJuIFRSVUUgYW5kIHltZCgiMjAwMi0wMy0zMCIpICV3aXRoaW4lIHkyMDAxIHdpbGwgcmV0dXJuIEZBTFNFLg0KDQppbnRfb3ZlcmxhcHMoKSBwZXJmb3JtcyBhIHNpbWlsYXIgdGVzdCwgYnV0IHdpbGwgcmV0dXJuIHRydWUgaWYgdHdvIGludGVydmFscyBvdmVybGFwIGF0IGFsbC4NCmBgYHtyfQ0KaGFsbGV5cyA8LSByZWFkX2NzdigiaGFsbGV5cy5jc3YiKQ0KYGBgDQpgYGB7cn0NCiMgUHJpbnQgaGFsbGV5cw0KaGVhZChoYWxsZXlzKQ0KDQojIE5ldyBjb2x1bW4gZm9yIGludGVydmFsIGZyb20gc3RhcnQgdG8gZW5kIGRhdGUNCmhhbGxleXMgPC0gaGFsbGV5cyAlPiUgDQogIG11dGF0ZSh2aXNpYmxlID0gc3RhcnRfZGF0ZSAlLS0lIGVuZF9kYXRlKQ0KDQojIFRoZSB2aXNpdGF0aW9uIG9mIDEwNjYNCmhhbGxleXNfMTA2NiA8LSBoYWxsZXlzWzE0LCBdIA0KDQojIE1vbmFyY2hzIGluIHBvd2VyIG9uIHBlcmloZWxpb24gZGF0ZQ0KbW9uYXJjaHMgJT4lIA0KICBmaWx0ZXIoaGFsbGV5c18xMDY2JHBlcmloZWxpb25fZGF0ZSAld2l0aGluJSByZWlnbikgJT4lDQogIHNlbGVjdChuYW1lLCBmcm9tLCB0bywgZG9taW5pb24pDQoNCiMgTW9uYXJjaHMgd2hvc2UgcmVpZ24gb3ZlcmxhcHMgdmlzaWJsZSB0aW1lDQptb25hcmNocyAlPiUgDQogIGZpbHRlcihpbnRfb3ZlcmxhcHMoaGFsbGV5c18xMDY2JHZpc2libGUsIHJlaWduKSkgJT4lDQogIHNlbGVjdChuYW1lLCBmcm9tLCB0bywgZG9taW5pb24pDQpgYGANCkxvb2tzIGxpa2UgdGhlIEtpbmdzIG9mIEVuZ2xhbmQgRWR3YXJkIHRoZSBDb25mZXNzb3IgYW5kIEhhcm9sZCBJSSB3b3VsZCBoYXZlIGJlZW4gYWJsZSB0byBzZWUgdGhlIGNvbWV0LiBJdCBtYXkgaGF2ZSBiZWVuIGEgYmFkIG9tZW4sIG5laXRoZXIgd2VyZSBpbiBwb3dlciBieSAxMDY3Lg0KDQojIyMgQ29udmVydGluZyB0byBkdXJhdGlvbnMgYW5kIHBlcmlvZHMNCg0KSW50ZXJ2YWxzIGFyZSB0aGUgbW9zdCBzcGVjaWZpYyB3YXkgdG8gcmVwcmVzZW50IGEgc3BhbiBvZiB0aW1lIHNpbmNlIHRoZXkgcmV0YWluIGluZm9ybWF0aW9uIGFib3V0IHRoZSBleGFjdCBzdGFydCBhbmQgZW5kIG1vbWVudHMuIFRoZXkgY2FuIGJlIGNvbnZlcnRlZCB0byBwZXJpb2RzIGFuZCBkdXJhdGlvbnMgZXhhY3RseTogaXQncyBwb3NzaWJsZSB0byBjYWxjdWxhdGUgYm90aCB0aGUgZXhhY3QgbnVtYmVyIG9mIHNlY29uZHMgZWxhcHNlZCBiZXR3ZWVuIHRoZSBzdGFydCBhbmQgZW5kIGRhdGUsIGFzIHdlbGwgYXMgdGhlIHBlcmNlaXZlZCBjaGFuZ2UgaW4gY2xvY2sgdGltZS4NCg0KVG8gZG8gc28geW91IHVzZSB0aGUgYXMucGVyaW9kKCksIGFuZCBhcy5kdXJhdGlvbigpIGZ1bmN0aW9ucywgcGFyc2luZyBpbiBhbiBpbnRlcnZhbCBhcyB0aGUgb25seSBhcmd1bWVudC4NCg0KYGBge3J9DQojIE5ldyBjb2x1bW5zIGZvciBkdXJhdGlvbiBhbmQgcGVyaW9kDQptb25hcmNocyA8LSBtb25hcmNocyAlPiUNCiAgbXV0YXRlKA0KICAgIGR1cmF0aW9uID0gYXMuZHVyYXRpb24ocmVpZ24pLA0KICAgIHBlcmlvZCA9IGFzLnBlcmlvZChyZWlnbikpIA0KICAgIA0KIyBFeGFtaW5lIHJlc3VsdHMgICAgDQptb25hcmNocyAlPiUNCiAgc2VsZWN0KG5hbWUsIGR1cmF0aW9uLCBwZXJpb2QpDQpgYGANClNlZSBob3cgbXVjaCBlYXNpZXIgaXQgaXMgdG8gaW50ZXJwcmV0IHRoZSBsZW5ndGggb2YgdGhlaXIgcmVpZ25zIGFzIHBlcmlvZHMgb3IgZHVyYXRpb25zLg0KDQojIFByb2JsZW1zIGluIHByYWN0aWNlDQoNCiMjIFRpbWV6b25lcw0KDQojIyMgU2V0dGluZyB0aGUgdGltZXpvbmUNCg0KSWYgeW91IGltcG9ydCBhIGRhdGV0aW1lIGFuZCBpdCBoYXMgdGhlIHdyb25nIHRpbWV6b25lLCB5b3UgY2FuIHNldCBpdCB3aXRoIGZvcmNlX3R6KCkuIFBhc3MgaW4gdGhlIGRhdGV0aW1lIGFzIHRoZSBmaXJzdCBhcmd1bWVudCBhbmQgdGhlIGFwcHJvcHJpYXRlIHRpbWV6b25lIHRvIHRoZSB0em9uZSBhcmd1bWVudC4gUmVtZW1iZXIgdGhlIHRpbWV6b25lIG5lZWRzIHRvIGJlIG9uZSBmcm9tIE9sc29uTmFtZXMoKS4NCmBgYHtyfQ0KIyBHYW1lMjogQ0FOIHZzIE5aTCBpbiBFZG1vbnRvbg0KZ2FtZTIgPC0gbWR5X2htKCJKdW5lIDExIDIwMTUgMTk6MDAiKQ0KDQojIEdhbWUzOiBDSE4gdnMgTlpMIGluIFdpbm5pcGVnDQpnYW1lMyA8LSBtZHlfaG0oIkp1bmUgMTUgMjAxNSAxODozMCIpDQoNCiMgU2V0IHRoZSB0aW1lem9uZSB0byAiQW1lcmljYS9FZG1vbnRvbiINCmdhbWUyX2xvY2FsIDwtIGZvcmNlX3R6KGdhbWUyLCB0em9uZSA9ICJBbWVyaWNhL0VkbW9udG9uIikNCmdhbWUyX2xvY2FsDQoNCiMgU2V0IHRoZSB0aW1lem9uZSB0byAiQW1lcmljYS9XaW5uaXBlZyINCmdhbWUzX2xvY2FsIDwtIGZvcmNlX3R6KGdhbWUzLCB0em9uZSA9ICJBbWVyaWNhL1dpbm5pcGVnIikNCmdhbWUzX2xvY2FsDQoNCiMgSG93IGxvbmcgZG9lcyB0aGUgdGVhbSBoYXZlIHRvIHJlc3Q/DQphcy5wZXJpb2QoZ2FtZTJfbG9jYWwgJS0tJSBnYW1lM19sb2NhbCkNCmBgYA0KRWRtb250b24gYW5kIFdpbm5pcGVnIGFyZSBpbiBkaWZmZXJlbnQgdGltZXpvbmVzLCBzbyBldmVuIHRob3VnaCB0aGUgc3RhcnQgdGltZXMgb2YgdGhlIGdhbWVzIG9ubHkgbG9vayAzMCBtaW51dGVzIGFwYXJ0LCB0aGV5IGFyZSBpbiBmYWN0IDEgaG91ciBhbmQgMzAgbWludXRlcyBhcGFydCwgYW5kIHRoZSB0ZWFtIG9ubHkgaGFzIDMgZGF5cywgMjIgaG91cnMgYW5kIDMwIG1pbnV0ZXMgdG8gcHJlcGFyZS4NCg0KIyMjIFZpZXdpbmcgaW4gYSB0aW1lem9uZQ0KDQpUbyB2aWV3IGEgZGF0ZXRpbWUgaW4gYW5vdGhlciB0aW1lem9uZSB1c2Ugd2l0aF90eigpLiBUaGUgc3ludGF4IG9mIHdpdGhfdHooKSBpcyB0aGUgc2FtZSBhcyBmb3JjZV90eigpLCBwYXNzaW5nIGEgZGF0ZXRpbWUgYW5kIHNldCB0aGUgdHpvbmUgYXJndW1lbnQgdG8gdGhlIGRlc2lyZWQgdGltZXpvbmUuIFVubGlrZSBmb3JjZV90eigpLCB3aXRoX3R6KCkgaXNuJ3QgY2hhbmdpbmcgdGhlIHVuZGVybHlpbmcgbW9tZW50IG9mIHRpbWUsIGp1c3QgaG93IGl0IGlzIGRpc3BsYXllZC4NCmBgYHtyfQ0KIyBXaGF0IHRpbWUgaXMgZ2FtZTJfbG9jYWwgaW4gTlo/DQp3aXRoX3R6KGdhbWUyX2xvY2FsLCB0em9uZSA9ICJQYWNpZmljL0F1Y2tsYW5kIikNCg0KIyBXaGF0IHRpbWUgaXMgZ2FtZTJfbG9jYWwgaW4gQ29ydmFsbGlzLCBPcmVnb24/DQp3aXRoX3R6KGdhbWUyX2xvY2FsLCB0em9uZSA9ICJBbWVyaWNhL0xvc19BbmdlbGVzIikNCg0KIyBXaGF0IHRpbWUgaXMgZ2FtZTNfbG9jYWwgaW4gTlo/DQp3aXRoX3R6KGdhbWUzX2xvY2FsLCB0em9uZSA9ICJQYWNpZmljL0F1Y2tsYW5kIikNCmBgYA0KIyMjIFRpbWV6b25lcyBpbiB0aGUgd2VhdGhlciBkYXRhDQpEaWQgeW91IGV2ZXIgbm90aWNlIHRoYXQgaW4gdGhlIGhvdXJseSBBdWNrbGFuZCB3ZWF0aGVyIGRhdGEgdGhlcmUgd2FzIGFub3RoZXIgZGF0ZXRpbWUgY29sdW1uLCBkYXRlX3V0Yz8gVGFrZSBhIGxvb2s6DQoNCiAgICB0aWJibGU6OmdsaW1wc2UoYWtsX2hvdXJseSkNCiAgICANClRoZSBkYXRldGltZSBjb2x1bW4geW91IGNyZWF0ZWQgcmVwcmVzZW50ZWQgbG9jYWwgdGltZSBpbiBBdWNrbGFuZCwgTlouIEkgc3VzcGVjdCB0aGlzIGFkZGl0aW9uYWwgY29sdW1uLCBkYXRlX3V0YyByZXByZXNlbnRzIHRoZSBvYnNlcnZhdGlvbiB0aW1lIGluIFVUQyAodGhlIG5hbWUgc2VlbXMgYSBiaWcgY2x1ZSkuIEJ1dCBkb2VzIGl0IHJlYWxseT8NCg0KVXNlIHlvdXIgbmV3IHRpbWV6b25lIHNraWxscyB0byBmaW5kIG91dC4NCmBgYHtyfQ0KIyBFeGFtaW5lIGRhdGV0aW1lIGFuZCBkYXRlX3V0YyBjb2x1bW5zDQpoZWFkKGFrbF9ob3VybHkkZGF0ZXRpbWUpDQpoZWFkKGFrbF9ob3VybHkkZGF0ZV91dGMpDQogIA0KIyBGb3JjZSBkYXRldGltZSB0byBQYWNpZmljL0F1Y2tsYW5kDQpha2xfaG91cmx5IDwtIGFrbF9ob3VybHkgJT4lDQogIG11dGF0ZSgNCiAgICBkYXRldGltZSA9IGZvcmNlX3R6KGRhdGV0aW1lLCB0em9uZSA9ICJQYWNpZmljL0F1Y2tsYW5kIikpDQoNCiMgUmVleGFtaW5lIGRhdGV0aW1lDQpoZWFkKGFrbF9ob3VybHkkZGF0ZXRpbWUpDQogIA0KIyBBcmUgZGF0ZXRpbWUgYW5kIGRhdGVfdXRjIHRoZSBzYW1lIG1vbWVudHMNCnRhYmxlKGFrbF9ob3VybHkkZGF0ZXRpbWUgLSBha2xfaG91cmx5JGRhdGVfdXRjKQ0KYGBgDQpMb29rcyBsaWtlIGZvciAxNyw0NTAgcm93cyBkYXRldGltZSBhbmQgZGF0ZV91dGMgZGVzY3JpYmUgdGhlIHNhbWUgbW9tZW50LCBidXQgZm9yIDQgcm93cyB0aGV5IGFyZSBkaWZmZXJlbnQuIENhbiB5b3UgZ3Vlc3Mgd2hpY2g/IFl1cCwgdGhlIHRpbWVzIHdoZXJlIERTVCBraWNrcyBpbi4NCg0KIyMjIFRpbWVzIHdpdGhvdXQgZGF0ZXMNCg0KU29tZXRpbWVzIHdlIGp1c3QgaGF2ZSBhIHRpbWUgd2l0aG91dCBhIGRhdGUuDQoNCkluIHRoaXMgc2l0dWF0aW9uLCB0aGUgaG1zIHBhY2thZ2UgcHJvdmlkZXMgYW4gaG1zIGNsYXNzIG9mIG9iamVjdCBmb3IgaG9sZGluZyB0aW1lcyB3aXRob3V0IGRhdGVzLCBhbmQgdGhlIGJlc3QgcGxhY2UgdG8gc3RhcnQgd291bGQgYmUgd2l0aCBhcy5obXMoKS4NCmBgYHtyfQ0KIyBJbXBvcnQgYXVja2xhbmQgaG91cmx5IGRhdGEgDQpha2xfaG91cmx5IDwtIHJlYWRfY3N2KCJha2xfd2VhdGhlcl9ob3VybHlfMjAxNi5jc3YiKQ0KDQojIEV4YW1pbmUgc3RydWN0dXJlIG9mIHRpbWUgY29sdW1uDQpzdHIoYWtsX2hvdXJseSR0aW1lKQ0KDQojIEV4YW1pbmUgaGVhZCBvZiB0aW1lIGNvbHVtbg0KaGVhZChha2xfaG91cmx5JHRpbWUpDQoNCiMgQSBwbG90IHVzaW5nIGp1c3QgdGltZQ0KZ2dwbG90KGFrbF9ob3VybHksIGFlcyh4ID0gdGltZSwgeSA9IHRlbXBlcmF0dXJlKSkgKw0KICBnZW9tX2xpbmUoYWVzKGdyb3VwID0gbWFrZV9kYXRlKHllYXIsIG1vbnRoLCBtZGF5KSksIGFscGhhID0gMC4yKQ0KYGBgDQpVc2luZyB0aW1lIHdpdGhvdXQgZGF0ZSBpcyBhIGdyZWF0IHdheSB0byBleGFtaW5lIGRhaWx5IHBhdHRlcm5zLg0KDQojIyBNb3JlIG9uIGltcG9ydGluZyBhbmQgZXhwb3J0aW5nIGRhdGV0aW1lcw0KDQojIyMgRmFzdCBwYXJzaW5nIHdpdGggZmFzdHRpbWUNCg0KVGhlIGZhc3R0aW1lIHBhY2thZ2UgcHJvdmlkZXMgYSBzaW5nbGUgZnVuY3Rpb24gZmFzdFBPU0lYY3QoKSwgZGVzaWduZWQgdG8gcmVhZCBpbiBkYXRldGltZXMgZm9ybWF0dGVkIGFjY29yZGluZyB0byBJU08gODYwMS4gQmVjYXVzZSBpdCBvbmx5IHJlYWRzIGluIG9uZSBmb3JtYXQsIGFuZCBkb2Vzbid0IGhhdmUgdG8gZ3Vlc3MgYSBmb3JtYXQsIGl0IGlzIHJlYWxseSBmYXN0IQ0KDQpZb3UnbGwgc2VlIGhvdyBmYXN0IGluIHRoaXMgZXhlcmNpc2UgYnkgY29tcGFyaW5nIGhvdyBmYXN0IGl0IHJlYWRzIGluIHRoZSBkYXRlcyBmcm9tIHRoZSBBdWNrbGFuZCBob3VybHkgd2VhdGhlciBkYXRhIChvdmVyIDE3LDAwMCBkYXRlcykgdG8gbHVicmlkYXRlcyB5bWRfaG1zKCkuDQoNClRvIGNvbXBhcmUgcnVuIHRpbWVzIHlvdSdsbCB1c2UgdGhlIG1pY3JvYmVuY2htYXJrKCkgZnVuY3Rpb24gZnJvbSB0aGUgcGFja2FnZSBvZiB0aGUgc2FtZSBuYW1lLiBZb3UgcGFzcyBpbiBhcyBtYW55IGFyZ3VtZW50cyBhcyB5b3Ugd2FudCBlYWNoIGJlaW5nIGFuIGV4cHJlc3Npb24gdG8gdGltZS4NCmBgYHtyfQ0KbGlicmFyeShtaWNyb2JlbmNobWFyaykNCmxpYnJhcnkoZmFzdHRpbWUpDQoNCmRhdGVzIDwtIHJlYWRSRFMoImRhdGVzLnJkcyIpDQoNCiMgRXhhbWluZSBzdHJ1Y3R1cmUgb2YgZGF0ZXMNCnN0cihkYXRlcykNCg0KIyBVc2UgZmFzdFBPU0lYY3QoKSB0byBwYXJzZSBkYXRlcw0KZmFzdFBPU0lYY3QoZGF0ZXMpICU+JSBzdHIoKQ0KDQojIENvbXBhcmUgc3BlZWQgb2YgZmFzdFBPU0lYY3QoKSB0byB5bWRfaG1zKCkNCm1pY3JvYmVuY2htYXJrKA0KICB5bWRfaG1zID0geW1kX2htcyhkYXRlcyksDQogIGZhc3R0aW1lID0gZmFzdFBPU0lYY3QoZGF0ZXMpLA0KICB0aW1lcyA9IDIwKQ0KYGBgDQpUbyBjb21wYXJlIHNwZWVkLCB5b3UgY2FuIGNvbXBhcmUgdGhlIGF2ZXJhZ2UgcnVuIHRpbWUgaW4gdGhlIG1lYW4gY29sdW1uLiBZb3Ugc2hvdWxkIHNlZSBmYXN0dGltZSBpcyBhYm91dCAyMCB0aW1lcyBmYXN0ZXIgdGhhbiB5bWRfaG1zKCkuDQoNCiMjIyBGYXN0IHBhcnNpbmcgd2l0aCBsdWJyaWRhdGU6OmZhc3Rfc3RycHRpbWUNCg0KbHVicmlkYXRlIHByb3ZpZGVzIGl0cyBvd24gZmFzdCBkYXRldGltZSBwYXJzZXI6IGZhc3Rfc3RycHRpbWUoKS4gSW5zdGVhZCBvZiB0YWtpbmcgYW4gb3JkZXIgYXJndW1lbnQgbGlrZSBwYXJzZV9kYXRlX3RpbWUoKSBpdCB0YWtlcyBhIGZvcm1hdCBhcmd1bWVudCBhbmQgdGhlIGZvcm1hdCBtdXN0IGNvbXBseSB3aXRoIHRoZSBzdHJwdGltZSgpIHN0eWxlLg0KYGBge3J9DQojIEhlYWQgb2YgZGF0ZXMNCmhlYWQoZGF0ZXMpDQoNCiMgUGFyc2UgZGF0ZXMgd2l0aCBmYXN0X3N0cnB0aW1lDQpmYXN0X3N0cnB0aW1lKGRhdGVzLCANCiAgICBmb3JtYXQgPSAiJVktJW0tJWRUJUg6JU06JVNaIikgJT4lIHN0cigpDQoNCiMgQ29tcGFyc2Ugc3BlZWQgdG8geW1kX2htcygpIGFuZCBmYXN0dGltZQ0KbWljcm9iZW5jaG1hcmsoDQogIHltZF9obXMgPSB5bWRfaG1zKGRhdGVzKSwNCiAgZmFzdHRpbWUgPSBmYXN0UE9TSVhjdChkYXRlcyksDQogIGZhc3Rfc3RycHRpbWUgPSBmYXN0X3N0cnB0aW1lKGRhdGVzLCANCiAgICBmb3JtYXQgPSAiJVktJW0tJWRUJUg6JU06JVNaIiksDQogIHRpbWVzID0gMjApDQpgYGANCmZhc3Rfc3RycHRpbWUoKSBpcyBtdWNoIGZhc3RlciB0aGFuIHltZF9obXMoKSBidXQganVzdCBhIGxpdHRsZSBzbG93ZXIgdGhhbiBmYXN0dGltZS4NCg0KIyMjIE91dHB1dHRpbmcgcHJldHR5IGRhdGVzIGFuZCB0aW1lcw0KQW4gZWFzeSB3YXkgdG8gb3V0cHV0IGRhdGVzIGlzIHRvIHVzZSB0aGUgc3RhbXAoKSBmdW5jdGlvbiBpbiBsdWJyaWRhdGUuIHN0YW1wKCkgdGFrZXMgYSBzdHJpbmcgd2hpY2ggc2hvdWxkIGJlIGFuIGV4YW1wbGUgb2YgaG93IHRoZSBkYXRlIHNob3VsZCBiZSBmb3JtYXR0ZWQsIGFuZCByZXR1cm5zIGEgZnVuY3Rpb24gdGhhdCBjYW4gYmUgdXNlZCB0byBmb3JtYXQgZGF0ZXMuDQpgYGB7cn0NCmZpbmlzaGVkIDwtICJJIGZpbmlzaGVkICdEYXRlcyBhbmQgVGltZXMgaW4gUicgb24gVGh1cnNkYXksIFNlcHRlbWJlciA0LCAyMDE3ISINCmBgYA0KYGBge3J9DQojIENyZWF0ZSBhIHN0YW1wIGJhc2VkIG9uICJTYXR1cmRheSwgSmFuIDEsIDIwMDAiDQpkYXRlX3N0YW1wIDwtIHN0YW1wKCJTYXR1cmRheSwgSmFuIDEsIDIwMDAiKQ0KDQojIFByaW50IGRhdGVfc3RhbXANCmRhdGVfc3RhbXANCg0KIyBDYWxsIGRhdGVfc3RhbXAgb24gdG9kYXkNCmRhdGVfc3RhbXAodG9kYXkoKSkNCg0KIyBDcmVhdGUgYW5kIGNhbGwgYSBzdGFtcCBiYXNlZCBvbiAiMTIvMzEvMTk5OSINCnN0YW1wKCIxMi8zMS8xOTk5IikodG9kYXkoKSkNCg0KIyBVc2Ugc3RyaW5nIGZpbmlzaGVkIGZvciBzdGFtcCgpDQpzdGFtcChmaW5pc2hlZCkodG9kYXkoKSkNCmBgYA0KDQoNCg0K