In this lesson, we’ll explore the lubridate R package, by Garrett Grolemund and Hadley Wickham. According to the package authors, “lubridate has a consistent, memorable syntax, that makes working with dates fun instead of frustrating.” If you’ve ever worked with dates in R, that statement probably has your attention.
Unfortunately, due to different date and time representations, this lesson is only guaranteed to work with an “en_US.UTF-8” locale. To view your locale, type Sys.getlocale(“LC_TIME”).
Sys.getlocale("LC_TIME")
[1] "en_NZ.UTF-8"
If the output above is not “en_US.UTF-8”, this lesson is not guaranteed to work correctly. Of course, you are welcome to try it anyway. We apologize for this inconvenience.
lubridate was automatically installed (if necessary) and loaded upon starting this lesson. To build the habit, we’ll go ahead and (re)load the package now. Type library(lubridate) to do so.
library(lubridate)
lubridate contains many useful functions. We’ll only be covering the basics here. Type help(package = lubridate) to bring up an overview of the package, including the package DESCRIPTION, a list of available functions, and a link to the official package vignette.
help(package = lubridate)
The today() function returns today’s date. Give it a try, storing the result in a new variable called this_day.
this_day <- today()
Print the contents of this_day to the console.
this_day
[1] "2018-07-19"
There are three components to this date. In order, they are year, month, and day. We can extract any of these components using the year(), month(), or day() function, respectively. Try any of those on this_day now.
day(this_day)
[1] 18
We can also get the day of the week from this_day using the wday() function. It will be represented as a number, such that 1 = Sunday, 2 = Monday, 3 = Tuesday, etc. Give it a shot.
wday(this_day)
[1] 4
Now try the same thing again, except this time add a second argument, label = TRUE, to display the name of the weekday (represented as an ordered factor).
wday(this_day, label = TRUE)
[1] Wed
Levels: Sun < Mon < Tue < Wed < Thu < Fri < Sat
In addition to handling dates, lubridate is great for working with date and time combinations, referred to as date-times. The now() function returns the date-time representing this exact moment in time. Give it a try and store the result in a variable called this_moment.
this_moment <- now()
View the contents of this_moment.
this_moment
[1] "2018-07-19 16:02:22 NZST"
Just like with dates, we can extract the year, month, day, or day of week. However, we can also use hour(), minute(), and second() to extract specific time information. Try any of these three new functions now to extract one piece of time information from this_moment.
second(this_moment)
[1] 22.54331
today() and now() provide neatly formatted date-time information. When working with dates and times ‘in the wild’, this won’t always (and perhaps rarely will) be the case.
Fortunately, lubridate offers a variety of functions for parsing date-times. These functions take the form of ymd(), dmy(), hms(), ymd_hms(), etc., where each letter in the name of the function stands for the location of years (y), months (m), days (d), hours (h), minutes (m), and/or seconds (s) in the date-time being read in.
To see how these functions work, try ymd(“1989-05-17”). You must surround the date with quotes. Store the result in a variable called my_date.
my_date <- ymd("1989-05-17")
Now take a look at my_date.
my_date
[1] "1989-05-17"
It looks almost the same, except for the addition of a time zone, which we’ll discuss later in the lesson. Below the surface, there’s another important change that takes place when lubridate parses a date. Type class(my_date) to see what that is.
class(my_date)
[1] "Date"
So ymd() took a character string as input and returned an object of class POSIXct. It’s not necessary that you understand what POSIXct is, but just know that it is one way that R stores date-time information internally.
“1989-05-17” is a fairly standard format, but lubridate is ‘smart’ enough to figure out many different date-time formats. Use ymd() to parse “1989 May 17”. Don’t forget to put quotes around the date!
ymd("1989 May 17")
[1] "1989-05-17"
Despite being formatted differently, the last two dates had the year first, then the month, then the day. Hence, we used ymd() to parse them. What do you think the appropriate function is for parsing “March 12, 1975”? Give it a try.
mdy("March 12, 1975")
[1] "1975-03-12"
We can even throw something funky at it and lubridate will often know the right thing to do. Parse 25081985, which is supposed to represent the 25th day of August 1985. Note that we are actually parsing a numeric value here – not a character string – so leave off the quotes.
dmy(25081985)
[1] "1985-08-25"
But be careful, it’s not magic. Try ymd(“192012”) to see what happens when we give it something more ambiguous. Surround the number with quotes again, just to be consistent with the way most dates are represented (as character strings).
ymd("192012")
All formats failed to parse. No formats found.
[1] NA
You got a warning message because it was unclear what date you wanted. When in doubt, it’s best to be more explicit. Repeat the same command, but add two dashes OR two forward slashes to “192012” so that it’s clear we want January 2, 1920.
ymd("1920-1-2")
[1] "1920-01-02"
In addition to dates, we can parse date-times. I’ve created a date-time object called dt1. Take a look at it now.
dt1
[1] "2014-08-23 17:23:02"
Now parse dt1 with ymd_hms().
ymd_hms(dt1)
[1] "2014-08-23 17:23:02 UTC"
What if we have a time, but no date? Use the appropriate lubridate function to parse “03:22:14” (hh:mm:ss).
hms("03:22:14")
[1] "3H 22M 14S"
lubridate is also capable of handling vectors of dates, which is particularly helpful when you need to parse an entire column of data. I’ve created a vector of dates called dt2. View its contents now.
dt2
[1] "2014-05-14" "2014-09-22" "2014-07-11"
Now parse dt2 using the appropriate lubridate function.
ymd(dt2)
[1] "2014-05-14" "2014-09-22" "2014-07-11"
The update() function allows us to update one or more components of a date-time. For example, let’s say the current time is 08:34:55 (hh:mm:ss). Update this_moment to the new time using the following command: update(this_moment, hours = 8, minutes = 34, seconds = 55).
update(this_moment, hours = 8, minutes = 34, seconds = 55)
[1] "2018-07-18 08:34:55 NZST"
It’s important to recognize that the previous command does not alter this_moment unless we reassign the result to this_moment. To see this, print the contents of this_moment.
this_moment
[1] "2018-07-18 13:33:10 NZST"
Unless you’re a superhero, some time has passed since you first created this_moment. Use update() to make it match the current time, specifying at least hours and minutes. Assign the result to this_moment, so that this_moment will contain the new time.
this_moment <- update(this_moment, hours = 13, minutes = 33)
Take one more look at this_moment to see that it’s been updated.
this_moment
[1] "2018-07-18 13:33:10 NZST"
Now, pretend you are in New York City and you are planning to visit a friend in Hong Kong. You seem to have misplaced your itinerary, but you know that your flight departs New York at 17:34 (5:34pm) the day after tomorrow. You also know that your flight is scheduled to arrive in Hong Kong exactly 15 hours and 50 minutes after departure.
Let’s reconstruct your itinerary from what you can remember, starting with the full date and time of your departure. We will approach this by finding the current date in New York, adding 2 full days, then setting the time to 17:34.
To find the current date in New York, we’ll use the now() function again. This time, however, we’ll specify the time zone that we want: “America/New_York”. Store the result in a variable called nyc. Check out ?now if you need help.
nyc <- now(tzone = "America/New_York")
For a complete list of valid time zones for use with lubridate, check out the following Wikipedia page: http://en.wikipedia.org/wiki/List_of_tz_database_time_zones
View the contents of nyc, which now contains the current date and time in New York.
nyc
[1] "2018-07-18 23:57:02 EDT"
Your flight is the day after tomorrow (in New York time), so we want to add two days to nyc. One nice aspect of lubridate is that it allows you to use arithmetic operators on dates and times. In this case, we’d like to add two days to nyc, so we can use the following expression: nyc + days(2). Give it a try, storing the result in a variable called depart.
depart <- nyc + days(2)
Take a look at depart.
depart
[1] "2018-07-20 23:57:02 EDT"
So now depart contains the date of the day after tomorrow. Use update() to add the correct hours (17) and minutes (34) to depart. Reassign the result to depart.
depart <- update(depart, hours = 17, minutes = 34)
Take one more look at depart.
depart
[1] "2018-07-20 17:34:02 EDT"
Your friend wants to know what time she should pick you up from the airport in Hong Kong. Now that we have the exact date and time of your departure from New York, we can figure out the exact time of your arrival in Hong Kong.
The first step is to add 15 hours and 50 minutes to your departure time. Recall that nyc + days(2) added two days to the current time in New York. Use the same approach to add 15 hours and 50 minutes to the date-time stored in depart. Store the result in a new variable called arrive.
arrive <- depart + hours(15) + minutes(50)
The arrive variable contains the time that it will be in New York when you arrive in Hong Kong. What we really want to know is what time it will be in Hong Kong when you arrive, so that your friend knows when to meet you.
The with_tz() function returns a date-time as it would appear in another time zone. Use ?with_tz to check out the documentation.
?with_tz
Description: with_tz returns a date-time as it would appear in a different time zone. The actual moment of time measured does not change, just the time zone it is measured in. with_tz defaults to the Universal Coordinated time zone (UTC) when an unrecognized time zone is inputted. See Sys.timezone() for more information on how R recognizes time zones. Usage: with_tz(time, tzone = “”)
Use with_tz() to convert arrive to the “Asia/Hong_Kong” time zone. Reassign the result to arrive, so that it will get the new value.
arrive <- with_tz(arrive, tzone = "Asia/Hong_Kong")
Print the value of arrive to the console, so that you can tell your friend what time to pick you up from the airport.
arrive
[1] "2018-07-21 21:24:02 HKT"
Fast forward to your arrival in Hong Kong. You and your friend have just met at the airport and you realize that the last time you were together was in Singapore on June 17, 2008. Naturally, you’d like to know exactly how long it has been.
Use the appropriate lubridate function to parse “June 17, 2008”, just like you did near the beginning of this lesson. This time, however, you should specify an extra argument, tz = “Singapore”. Store the result in a variable called last_time.
last_time <- mdy("June 17, 2008", tz = "Singapore")
View the contents of last_time.
last_time
[1] "2008-06-17 +08"
Pull up the documentation for interval(), which we’ll use to explore how much time has passed between arrive and last_time.
?interval
Description: creates an Interval object with the specified start and end dates. If the start date occurs before the end date, the interval will be positive. Otherwise, it will be negative. Usage: interval(start, end = NULL, tzone = tz(start))
Create an interval() that spans from last_time to arrive. Store it in a new variable called how_long.
how_long <- interval(last_time, arrive)
Now use as.period(how_long) to see how long it’s been.
as.period(how_long)
[1] "10y 1m 4d 21H 24M 2S"
This is where things get a little tricky. Because of things like leap years, leap seconds, and daylight savings time, the length of any given minute, day, month, week, or year is relative to when it occurs. In contrast, the length of a second is always the same, regardless of when it occurs.
To address these complexities, the authors of lubridate introduce four classes of time related objects: instants, intervals, durations, and periods. These topics are beyond the scope of this lesson, but you can find a complete discussion in the 2011 Journal of Statistical Software paper titled ‘Dates and Times Made Easy with lubridate’.
This concludes our introduction to working with dates and times in lubridate. I created a little timer that started running in the background when you began this lesson. Type stopwatch() to see how long you’ve been working!
stopwatch()
END
LS0tCnRpdGxlOiAiRGF0ZXMgYW5kIFRpbWVzIHdpdGggbHVicmlkYXRlIgpvdXRwdXQ6IGh0bWxfbm90ZWJvb2sKLS0tCiAgICAKLS0tCgo8YnIgLz4KSW4gdGhpcyBsZXNzb24sIHdlJ2xsIGV4cGxvcmUgdGhlIDx1Pmx1YnJpZGF0ZTwvdT4gUiBwYWNrYWdlLCBieSAqKkdhcnJldHQgR3JvbGVtdW5kKiogYW5kICoqSGFkbGV5IFdpY2toYW0qKi4gQWNjb3JkaW5nIHRvIHRoZSBwYWNrYWdlIGF1dGhvcnMsICoibHVicmlkYXRlIGhhcyBhIGNvbnNpc3RlbnQsIG1lbW9yYWJsZSBzeW50YXgsIHRoYXQgbWFrZXMgd29ya2luZyB3aXRoIGRhdGVzIGZ1biBpbnN0ZWFkIG9mIGZydXN0cmF0aW5nLiIqIElmIHlvdSd2ZSBldmVyIHdvcmtlZCB3aXRoIGRhdGVzIGluIFIsIHRoYXQgc3RhdGVtZW50IHByb2JhYmx5IGhhcyB5b3VyIGF0dGVudGlvbi4KClVuZm9ydHVuYXRlbHksIGR1ZSB0byBkaWZmZXJlbnQgZGF0ZSBhbmQgdGltZSByZXByZXNlbnRhdGlvbnMsIHRoaXMgbGVzc29uIGlzIG9ubHkgZ3VhcmFudGVlZCB0byB3b3JrIHdpdGggYW4gKioiZW5fVVMuVVRGLTgiKiogbG9jYWxlLiBUbyB2aWV3IHlvdXIgbG9jYWxlLCB0eXBlICpTeXMuZ2V0bG9jYWxlKCJMQ19USU1FIikqLgoKYGBge3J9ClN5cy5nZXRsb2NhbGUoIkxDX1RJTUUiKQpgYGAKPGJyLz4KSWYgdGhlIG91dHB1dCBhYm92ZSBpcyBub3QgKioiZW5fVVMuVVRGLTgiKiosIHRoaXMgbGVzc29uIGlzIG5vdCBndWFyYW50ZWVkIHRvIHdvcmsgY29ycmVjdGx5LiBPZiBjb3Vyc2UsIHlvdSBhcmUgd2VsY29tZSB0byB0cnkgaXQgYW55d2F5LiBXZSBhcG9sb2dpemUgZm9yIHRoaXMgaW5jb252ZW5pZW5jZS4KCjxici8+Cmx1YnJpZGF0ZSB3YXMgYXV0b21hdGljYWxseSBpbnN0YWxsZWQgKGlmIG5lY2Vzc2FyeSkgYW5kIGxvYWRlZCB1cG9uIHN0YXJ0aW5nIHRoaXMgbGVzc29uLiBUbyBidWlsZCB0aGUgaGFiaXQsIHdlJ2xsIGdvIGFoZWFkIGFuZCAocmUpbG9hZCB0aGUgcGFja2FnZSBub3cuIFR5cGUgKmxpYnJhcnkobHVicmlkYXRlKSogdG8gZG8gc28uCgpgYGB7cn0KbGlicmFyeShsdWJyaWRhdGUpCmBgYAo8YnIvPgpsdWJyaWRhdGUgY29udGFpbnMgbWFueSB1c2VmdWwgZnVuY3Rpb25zLiBXZSdsbCBvbmx5IGJlIGNvdmVyaW5nIHRoZSBiYXNpY3MgaGVyZS4gVHlwZSAqaGVscChwYWNrYWdlID0gbHVicmlkYXRlKSogdG8gYnJpbmcgdXAgYW4gb3ZlcnZpZXcgb2YgdGhlIHBhY2thZ2UsIGluY2x1ZGluZyB0aGUgcGFja2FnZSBERVNDUklQVElPTiwgYSBsaXN0IG9mIGF2YWlsYWJsZSBmdW5jdGlvbnMsIGFuZCBhIGxpbmsgdG8gdGhlIG9mZmljaWFsIHBhY2thZ2UgdmlnbmV0dGUuCgpgYGB7cn0KaGVscChwYWNrYWdlID0gbHVicmlkYXRlKQpgYGAKPGJyLz4KVGhlICoqdG9kYXkoKSoqIGZ1bmN0aW9uIHJldHVybnMgdG9kYXkncyBkYXRlLiBHaXZlIGl0IGEgdHJ5LCBzdG9yaW5nIHRoZSByZXN1bHQgaW4gYSBuZXcgdmFyaWFibGUgY2FsbGVkICp0aGlzX2RheSouCmBgYHtyfQp0aGlzX2RheSA8LSB0b2RheSgpCmBgYApQcmludCB0aGUgY29udGVudHMgb2YgdGhpc19kYXkgdG8gdGhlIGNvbnNvbGUuCmBgYHtyfQp0aGlzX2RheQpgYGAKPGJyLz4KVGhlcmUgYXJlIHRocmVlIGNvbXBvbmVudHMgdG8gdGhpcyBkYXRlLiBJbiBvcmRlciwgdGhleSBhcmUgeWVhciwgbW9udGgsIGFuZCBkYXkuIFdlIGNhbiBleHRyYWN0IGFueSBvZiB0aGVzZSBjb21wb25lbnRzIHVzaW5nIHRoZSAqKnllYXIoKSoqLCAqKm1vbnRoKCkqKiwgb3IgKipkYXkoKSoqIGZ1bmN0aW9uLCByZXNwZWN0aXZlbHkuIFRyeSBhbnkgb2YgdGhvc2Ugb24gKnRoaXNfZGF5KiBub3cuCgpgYGB7cn0KZGF5KHRoaXNfZGF5KQpgYGAKPGJyLz4KV2UgY2FuIGFsc28gZ2V0IHRoZSBkYXkgb2YgdGhlIHdlZWsgZnJvbSAqdGhpc19kYXkqIHVzaW5nIHRoZSAqKndkYXkoKSoqIGZ1bmN0aW9uLiBJdCB3aWxsIGJlIHJlcHJlc2VudGVkIGFzIGEgbnVtYmVyLCBzdWNoIHRoYXQgMSA9IFN1bmRheSwgMiA9IE1vbmRheSwgMyA9IFR1ZXNkYXksIGV0Yy4gR2l2ZSBpdCBhIHNob3QuCgpgYGB7cn0Kd2RheSh0aGlzX2RheSkKYGBgCjxici8+Ck5vdyB0cnkgdGhlIHNhbWUgdGhpbmcgYWdhaW4sIGV4Y2VwdCB0aGlzIHRpbWUgYWRkIGEgc2Vjb25kIGFyZ3VtZW50LCAqbGFiZWwgPSBUUlVFKiwgdG8gZGlzcGxheSB0aGUgKm5hbWUqIG9mIHRoZSB3ZWVrZGF5IChyZXByZXNlbnRlZCBhcyBhbiBvcmRlcmVkIGZhY3RvcikuCgpgYGB7cn0Kd2RheSh0aGlzX2RheSwgbGFiZWwgPSBUUlVFKQpgYGAKPGJyLz4KSW4gYWRkaXRpb24gdG8gaGFuZGxpbmcgZGF0ZXMsIGx1YnJpZGF0ZSBpcyBncmVhdCBmb3Igd29ya2luZyB3aXRoIGRhdGUgYW5kIHRpbWUgY29tYmluYXRpb25zLCByZWZlcnJlZCB0byBhcyBkYXRlLXRpbWVzLiBUaGUgKipub3coKSoqIGZ1bmN0aW9uIHJldHVybnMgdGhlIGRhdGUtdGltZSByZXByZXNlbnRpbmcgdGhpcyBleGFjdCBtb21lbnQgaW4gdGltZS4gR2l2ZSBpdCBhIHRyeSBhbmQgc3RvcmUgdGhlIHJlc3VsdCBpbiBhIHZhcmlhYmxlIGNhbGxlZCB0aGlzX21vbWVudC4KCmBgYHtyfQp0aGlzX21vbWVudCA8LSBub3coKQpgYGAKVmlldyB0aGUgY29udGVudHMgb2YgdGhpc19tb21lbnQuCmBgYHtyfQp0aGlzX21vbWVudApgYGAKPGJyLz4KSnVzdCBsaWtlIHdpdGggZGF0ZXMsIHdlIGNhbiBleHRyYWN0IHRoZSAqeWVhciosICptb250aCosICpkYXkqLCBvciAqZGF5IG9mIHdlZWsqLiBIb3dldmVyLCB3ZSBjYW4gYWxzbyB1c2UgKipob3VyKCkqKiwgKiptaW51dGUoKSoqLCBhbmQgKipzZWNvbmQoKSoqIHRvIGV4dHJhY3Qgc3BlY2lmaWMgdGltZSBpbmZvcm1hdGlvbi4gVHJ5IGFueSBvZiB0aGVzZSB0aHJlZSBuZXcgZnVuY3Rpb25zIG5vdyB0byBleHRyYWN0IG9uZSBwaWVjZSBvZiB0aW1lIGluZm9ybWF0aW9uIGZyb20gKnRoaXNfbW9tZW50Ki4KCmBgYHtyfQpzZWNvbmQodGhpc19tb21lbnQpCmBgYAoKPGJyLz4KCioqdG9kYXkoKSoqIGFuZCAqKm5vdygpKiogcHJvdmlkZSBuZWF0bHkgZm9ybWF0dGVkIGRhdGUtdGltZSBpbmZvcm1hdGlvbi4gV2hlbiB3b3JraW5nIHdpdGggZGF0ZXMgYW5kIHRpbWVzICdpbiB0aGUgd2lsZCcsIHRoaXMgd29uJ3QgYWx3YXlzIChhbmQgcGVyaGFwcyByYXJlbHkgd2lsbCkgYmUgdGhlIGNhc2UuCgpGb3J0dW5hdGVseSwgbHVicmlkYXRlIG9mZmVycyBhIHZhcmlldHkgb2YgZnVuY3Rpb25zIGZvciBwYXJzaW5nIGRhdGUtdGltZXMuIFRoZXNlIGZ1bmN0aW9ucyB0YWtlIHRoZSBmb3JtIG9mICoqeW1kKCkqKiwgKipkbXkoKSoqLCAqKmhtcygpKiosICoqeW1kX2htcygpKiosIGV0Yy4sIHdoZXJlIGVhY2ggbGV0dGVyIGluIHRoZSBuYW1lIG9mIHRoZSBmdW5jdGlvbiBzdGFuZHMgZm9yIHRoZSBsb2NhdGlvbiBvZiAqeWVhcnMgKHkpLCBtb250aHMgKG0pLCBkYXlzIChkKSwgaG91cnMgKGgpLCBtaW51dGVzIChtKSosIGFuZC9vciAqc2Vjb25kcyAocykqIGluIHRoZSBkYXRlLXRpbWUgYmVpbmcgcmVhZCBpbi4KClRvIHNlZSBob3cgdGhlc2UgZnVuY3Rpb25zIHdvcmssIHRyeSAqeW1kKCIxOTg5LTA1LTE3IikqLiBZb3UgbXVzdCBzdXJyb3VuZCB0aGUgZGF0ZSB3aXRoIHF1b3Rlcy4gU3RvcmUgdGhlIHJlc3VsdCBpbiBhIHZhcmlhYmxlIGNhbGxlZCAqbXlfZGF0ZSouCgpgYGB7cn0KbXlfZGF0ZSA8LSB5bWQoIjE5ODktMDUtMTciKQpgYGAKTm93IHRha2UgYSBsb29rIGF0ICpteV9kYXRlKi4KCmBgYHtyfQpteV9kYXRlCmBgYAo8YnIvPgpJdCBsb29rcyBhbG1vc3QgdGhlIHNhbWUsIGV4Y2VwdCBmb3IgdGhlIGFkZGl0aW9uIG9mIGEgdGltZSB6b25lLCB3aGljaCB3ZSdsbCBkaXNjdXNzIGxhdGVyIGluIHRoZSBsZXNzb24uIEJlbG93IHRoZSBzdXJmYWNlLCB0aGVyZSdzIGFub3RoZXIgaW1wb3J0YW50IGNoYW5nZSB0aGF0IHRha2VzIHBsYWNlIHdoZW4gbHVicmlkYXRlIHBhcnNlcyBhIGRhdGUuIFR5cGUgKmNsYXNzKG15X2RhdGUpKiB0byBzZWUgd2hhdCB0aGF0IGlzLgoKYGBge3J9CmNsYXNzKG15X2RhdGUpCmBgYAo8YnIvPgpTbyAqKnltZCgpKiogdG9vayBhIGNoYXJhY3RlciBzdHJpbmcgYXMgaW5wdXQgYW5kIHJldHVybmVkIGFuIG9iamVjdCBvZiBjbGFzcyAqUE9TSVhjdCouIEl0J3Mgbm90IG5lY2Vzc2FyeSB0aGF0IHlvdSB1bmRlcnN0YW5kIHdoYXQgKlBPU0lYY3QqIGlzLCBidXQganVzdCBrbm93IHRoYXQgaXQgaXMgb25lIHdheSB0aGF0IFIgc3RvcmVzIGRhdGUtdGltZSBpbmZvcm1hdGlvbiBpbnRlcm5hbGx5LgoKKiIxOTg5LTA1LTE3IiogaXMgYSBmYWlybHkgc3RhbmRhcmQgZm9ybWF0LCBidXQgbHVicmlkYXRlIGlzICdzbWFydCcgZW5vdWdoIHRvIGZpZ3VyZSBvdXQgbWFueSBkaWZmZXJlbnQgZGF0ZS10aW1lIGZvcm1hdHMuIFVzZSAqKnltZCgpKiogdG8gcGFyc2UgKiIxOTg5IE1heSAxNyIqLiBEb24ndCBmb3JnZXQgdG8gcHV0IHF1b3RlcyBhcm91bmQgdGhlIGRhdGUhCgpgYGB7cn0KeW1kKCIxOTg5IE1heSAxNyIpCmBgYAo8YnIvPgpEZXNwaXRlIGJlaW5nIGZvcm1hdHRlZCBkaWZmZXJlbnRseSwgdGhlIGxhc3QgdHdvIGRhdGVzIGhhZCB0aGUgeWVhciBmaXJzdCwgdGhlbiB0aGUgbW9udGgsIHRoZW4gdGhlIGRheS4gSGVuY2UsIHdlIHVzZWQgKip5bWQoKSoqIHRvIHBhcnNlIHRoZW0uIFdoYXQgZG8geW91IHRoaW5rIHRoZSBhcHByb3ByaWF0ZSBmdW5jdGlvbiBpcyBmb3IgcGFyc2luZyAqIk1hcmNoIDEyLCAxOTc1Iio/IEdpdmUgaXQgYSB0cnkuCgpgYGB7cn0KbWR5KCJNYXJjaCAxMiwgMTk3NSIpCmBgYAo8YnIvPgpXZSBjYW4gZXZlbiB0aHJvdyBzb21ldGhpbmcgZnVua3kgYXQgaXQgYW5kIGx1YnJpZGF0ZSB3aWxsIG9mdGVuIGtub3cgdGhlIHJpZ2h0IHRoaW5nIHRvIGRvLiBQYXJzZSAqMjUwODE5ODUqLCB3aGljaCBpcyBzdXBwb3NlZCB0byByZXByZXNlbnQgdGhlIDI1dGggZGF5IG9mIEF1Z3VzdCAxOTg1LiBOb3RlIHRoYXQgd2UgYXJlIGFjdHVhbGx5IHBhcnNpbmcgYSBudW1lcmljIHZhbHVlIGhlcmUgLS0gbm90IGEgY2hhcmFjdGVyIHN0cmluZyAtLSBzbyBsZWF2ZSBvZmYgdGhlIHF1b3Rlcy4KCmBgYHtyfQpkbXkoMjUwODE5ODUpCmBgYAo8YnIvPgpCdXQgYmUgY2FyZWZ1bCwgaXQncyBub3QgbWFnaWMuIFRyeSAqeW1kKCIxOTIwMTIiKSogdG8gc2VlIHdoYXQgaGFwcGVucyB3aGVuIHdlIGdpdmUgaXQgc29tZXRoaW5nIG1vcmUgYW1iaWd1b3VzLiBTdXJyb3VuZCB0aGUgbnVtYmVyIHdpdGggcXVvdGVzIGFnYWluLCBqdXN0IHRvIGJlIGNvbnNpc3RlbnQgd2l0aCB0aGUgd2F5IG1vc3QgZGF0ZXMgYXJlIHJlcHJlc2VudGVkIChhcyBjaGFyYWN0ZXIgc3RyaW5ncykuCgpgYGB7cn0KeW1kKCIxOTIwMTIiKQpgYGAKPGJyLz4KWW91IGdvdCBhIHdhcm5pbmcgbWVzc2FnZSBiZWNhdXNlIGl0IHdhcyB1bmNsZWFyIHdoYXQgZGF0ZSB5b3Ugd2FudGVkLiBXaGVuIGluIGRvdWJ0LCBpdCdzIGJlc3QgdG8gYmUgbW9yZSBleHBsaWNpdC4gUmVwZWF0IHRoZSBzYW1lIGNvbW1hbmQsIGJ1dCBhZGQgdHdvIGRhc2hlcyBPUiB0d28gZm9yd2FyZCBzbGFzaGVzIHRvICIxOTIwMTIiIHNvIHRoYXQgaXQncyBjbGVhciB3ZSB3YW50IEphbnVhcnkgMiwgMTkyMC4KCmBgYHtyfQp5bWQoIjE5MjAtMS0yIikKYGBgCjxici8+CkluIGFkZGl0aW9uIHRvIGRhdGVzLCB3ZSBjYW4gcGFyc2UgZGF0ZS10aW1lcy4gSSd2ZSBjcmVhdGVkIGEgZGF0ZS10aW1lIG9iamVjdCBjYWxsZWQgKipkdDEqKi4gVGFrZSBhIGxvb2sgYXQgaXQgbm93LgoKYGBge3J9CmR0MQpgYGAKPGJyLz4KTm93IHBhcnNlICpkdDEqIHdpdGggKip5bWRfaG1zKCkqKi4KCmBgYHtyfQp5bWRfaG1zKGR0MSkKYGBgCjxici8+CldoYXQgaWYgd2UgaGF2ZSBhIHRpbWUsIGJ1dCBubyBkYXRlPyBVc2UgdGhlIGFwcHJvcHJpYXRlIGx1YnJpZGF0ZSBmdW5jdGlvbiB0byBwYXJzZSAqIjAzOjIyOjE0IiogKGhoOm1tOnNzKS4KCmBgYHtyfQpobXMoIjAzOjIyOjE0IikKYGBgCjxici8+Cmx1YnJpZGF0ZSBpcyBhbHNvIGNhcGFibGUgb2YgaGFuZGxpbmcgdmVjdG9ycyBvZiBkYXRlcywgd2hpY2ggaXMgcGFydGljdWxhcmx5IGhlbHBmdWwgd2hlbiB5b3UgbmVlZCB0byBwYXJzZSBhbiBlbnRpcmUgY29sdW1uIG9mIGRhdGEuIEkndmUgY3JlYXRlZCBhIHZlY3RvciBvZiBkYXRlcyBjYWxsZWQgKipkdDIqKi4gVmlldyBpdHMgY29udGVudHMgbm93LgoKYGBge3J9CmR0MgpgYGAKTm93IHBhcnNlICpkdDIqIHVzaW5nIHRoZSBhcHByb3ByaWF0ZSBsdWJyaWRhdGUgZnVuY3Rpb24uCmBgYHtyfQp5bWQoZHQyKQpgYGAKPGJyLz4KVGhlICoqdXBkYXRlKCkqKiBmdW5jdGlvbiBhbGxvd3MgdXMgdG8gdXBkYXRlIG9uZSBvciBtb3JlIGNvbXBvbmVudHMgb2YgYSBkYXRlLXRpbWUuIEZvciBleGFtcGxlLCBsZXQncyBzYXkgdGhlIGN1cnJlbnQgdGltZSBpcyAwODozNDo1NSAoaGg6bW06c3MpLiBVcGRhdGUgKnRoaXNfbW9tZW50KiB0byB0aGUgbmV3IHRpbWUgdXNpbmcgdGhlIGZvbGxvd2luZyBjb21tYW5kOiAqdXBkYXRlKHRoaXNfbW9tZW50LCBob3VycyA9IDgsIG1pbnV0ZXMgPSAzNCwgc2Vjb25kcyA9IDU1KSouCmBgYHtyfQp1cGRhdGUodGhpc19tb21lbnQsIGhvdXJzID0gOCwgbWludXRlcyA9IDM0LCBzZWNvbmRzID0gNTUpCmBgYAo8YnIvPgpJdCdzIGltcG9ydGFudCB0byByZWNvZ25pemUgdGhhdCB0aGUgcHJldmlvdXMgY29tbWFuZCBkb2VzIG5vdCBhbHRlciAqdGhpc19tb21lbnQqIHVubGVzcyB3ZSByZWFzc2lnbiB0aGUgcmVzdWx0IHRvICp0aGlzX21vbWVudCouIFRvIHNlZSB0aGlzLCBwcmludCB0aGUgY29udGVudHMgb2YgKnRoaXNfbW9tZW50Ki4KYGBge3J9CnRoaXNfbW9tZW50CmBgYAo8YnIvPgpVbmxlc3MgeW91J3JlIGEgc3VwZXJoZXJvLCBzb21lIHRpbWUgaGFzIHBhc3NlZCBzaW5jZSB5b3UgZmlyc3QgY3JlYXRlZCB0aGlzX21vbWVudC4gVXNlICoqdXBkYXRlKCkqKiB0byBtYWtlIGl0IG1hdGNoIHRoZSBjdXJyZW50IHRpbWUsIHNwZWNpZnlpbmcgYXQgbGVhc3QgaG91cnMgYW5kIG1pbnV0ZXMuIEFzc2lnbiB0aGUgcmVzdWx0IHRvICp0aGlzX21vbWVudCosIHNvIHRoYXQgKnRoaXNfbW9tZW50KiB3aWxsIGNvbnRhaW4gdGhlIG5ldyB0aW1lLgoKYGBge3J9CnRoaXNfbW9tZW50IDwtIHVwZGF0ZSh0aGlzX21vbWVudCwgaG91cnMgPSAxMywgbWludXRlcyA9IDMzKQpgYGAKVGFrZSBvbmUgbW9yZSBsb29rIGF0ICp0aGlzX21vbWVudCogdG8gc2VlIHRoYXQgaXQncyBiZWVuIHVwZGF0ZWQuCmBgYHtyfQp0aGlzX21vbWVudApgYGAKPGJyIC8+CgpOb3csIHByZXRlbmQgeW91IGFyZSBpbiBOZXcgWW9yayBDaXR5IGFuZCB5b3UgYXJlIHBsYW5uaW5nIHRvIHZpc2l0IGEgZnJpZW5kIGluIEhvbmcgS29uZy4gWW91IHNlZW0gdG8gaGF2ZSBtaXNwbGFjZWQgeW91ciBpdGluZXJhcnksIGJ1dCB5b3Uga25vdyB0aGF0IHlvdXIgZmxpZ2h0IGRlcGFydHMgTmV3IFlvcmsgYXQgMTc6MzQgKDU6MzRwbSkgdGhlIGRheSBhZnRlciB0b21vcnJvdy4gWW91IGFsc28ga25vdyB0aGF0IHlvdXIgZmxpZ2h0IGlzIHNjaGVkdWxlZCB0byBhcnJpdmUgaW4gSG9uZyBLb25nIGV4YWN0bHkgMTUgaG91cnMgYW5kIDUwIG1pbnV0ZXMgYWZ0ZXIgZGVwYXJ0dXJlLgoKTGV0J3MgcmVjb25zdHJ1Y3QgeW91ciBpdGluZXJhcnkgZnJvbSB3aGF0IHlvdSBjYW4gcmVtZW1iZXIsIHN0YXJ0aW5nIHdpdGggdGhlIGZ1bGwgZGF0ZSBhbmQgdGltZSBvZiB5b3VyIGRlcGFydHVyZS4gV2Ugd2lsbCBhcHByb2FjaCB0aGlzIGJ5IGZpbmRpbmcgdGhlIGN1cnJlbnQgZGF0ZSBpbiBOZXcgWW9yaywgYWRkaW5nIDIgZnVsbCBkYXlzLCB0aGVuIHNldHRpbmcgdGhlIHRpbWUgdG8gMTc6MzQuCgpUbyBmaW5kIHRoZSBjdXJyZW50IGRhdGUgaW4gTmV3IFlvcmssIHdlJ2xsIHVzZSB0aGUgKipub3coKSoqIGZ1bmN0aW9uIGFnYWluLiBUaGlzIHRpbWUsIGhvd2V2ZXIsIHdlJ2xsIHNwZWNpZnkgdGhlIHRpbWUgem9uZSB0aGF0IHdlIHdhbnQ6ICoiQW1lcmljYS9OZXdfWW9yayIqLiBTdG9yZSB0aGUgcmVzdWx0IGluIGEgdmFyaWFibGUgY2FsbGVkICpueWMqLiBDaGVjayBvdXQgKj9ub3cqIGlmIHlvdSBuZWVkIGhlbHAuCgpgYGB7cn0KbnljIDwtIG5vdyh0em9uZSA9ICJBbWVyaWNhL05ld19Zb3JrIikKYGBgCjxici8+CkZvciBhIGNvbXBsZXRlIGxpc3Qgb2YgdmFsaWQgdGltZSB6b25lcyBmb3IgdXNlIHdpdGggbHVicmlkYXRlLCBjaGVjayBvdXQgdGhlIGZvbGxvd2luZyBXaWtpcGVkaWEgcGFnZTogaHR0cDovL2VuLndpa2lwZWRpYS5vcmcvd2lraS9MaXN0X29mX3R6X2RhdGFiYXNlX3RpbWVfem9uZXMKClZpZXcgdGhlIGNvbnRlbnRzIG9mICpueWMqLCB3aGljaCBub3cgY29udGFpbnMgdGhlIGN1cnJlbnQgZGF0ZSBhbmQgdGltZSBpbiBOZXcgWW9yay4KYGBge3J9Cm55YwpgYGAKPGJyLz4KWW91ciBmbGlnaHQgaXMgdGhlIGRheSBhZnRlciB0b21vcnJvdyAoaW4gTmV3IFlvcmsgdGltZSksIHNvIHdlIHdhbnQgdG8gYWRkIHR3byBkYXlzIHRvICpueWMqLiBPbmUgbmljZSBhc3BlY3Qgb2YgbHVicmlkYXRlIGlzIHRoYXQgaXQgYWxsb3dzIHlvdSB0byB1c2UgYXJpdGhtZXRpYyBvcGVyYXRvcnMgb24gZGF0ZXMgYW5kIHRpbWVzLiBJbiB0aGlzIGNhc2UsIHdlJ2QgbGlrZSB0byBhZGQgdHdvIGRheXMgdG8gbnljLCBzbyB3ZSBjYW4gdXNlIHRoZSBmb2xsb3dpbmcgZXhwcmVzc2lvbjogKm55YyArIGRheXMoMikqLiBHaXZlIGl0IGEgdHJ5LCBzdG9yaW5nIHRoZSByZXN1bHQgaW4gYSB2YXJpYWJsZSBjYWxsZWQgKmRlcGFydCouCgpgYGB7cn0KZGVwYXJ0IDwtIG55YyArIGRheXMoMikKYGBgClRha2UgYSBsb29rIGF0ICpkZXBhcnQqLgpgYGB7cn0KZGVwYXJ0CmBgYAo8YnIvPgpTbyBub3cgZGVwYXJ0IGNvbnRhaW5zIHRoZSBkYXRlIG9mIHRoZSBkYXkgYWZ0ZXIgdG9tb3Jyb3cuIFVzZSAqKnVwZGF0ZSgpKiogdG8gYWRkIHRoZSBjb3JyZWN0IGhvdXJzICgqMTcqKSBhbmQgbWludXRlcyAoKjM0KikgdG8gZGVwYXJ0LiBSZWFzc2lnbiB0aGUgcmVzdWx0IHRvIGRlcGFydC4KYGBge3J9CmRlcGFydCA8LSB1cGRhdGUoZGVwYXJ0LCBob3VycyA9IDE3LCBtaW51dGVzID0gMzQpCmBgYApUYWtlIG9uZSBtb3JlIGxvb2sgYXQgKmRlcGFydCouCmBgYHtyfQpkZXBhcnQKYGBgCjxici8+CllvdXIgZnJpZW5kIHdhbnRzIHRvIGtub3cgd2hhdCB0aW1lIHNoZSBzaG91bGQgcGljayB5b3UgdXAgZnJvbSB0aGUgYWlycG9ydCBpbiBIb25nIEtvbmcuIE5vdyB0aGF0IHdlIGhhdmUgdGhlIGV4YWN0IGRhdGUgYW5kIHRpbWUgb2YgeW91ciBkZXBhcnR1cmUgZnJvbSBOZXcgWW9yaywgd2UgY2FuIGZpZ3VyZSBvdXQgdGhlIGV4YWN0IHRpbWUgb2YgeW91ciBhcnJpdmFsIGluIEhvbmcgS29uZy4KClRoZSBmaXJzdCBzdGVwIGlzIHRvIGFkZCAxNSBob3VycyBhbmQgNTAgbWludXRlcyB0byB5b3VyIGRlcGFydHVyZSB0aW1lLiBSZWNhbGwgdGhhdCAqbnljICsgZGF5cygyKSogYWRkZWQgdHdvIGRheXMgdG8gdGhlIGN1cnJlbnQgdGltZSBpbiBOZXcgWW9yay4gVXNlIHRoZSBzYW1lIGFwcHJvYWNoIHRvIGFkZCAxNSBob3VycyBhbmQgNTAgbWludXRlcyB0byB0aGUgZGF0ZS10aW1lIHN0b3JlZCBpbiBkZXBhcnQuIFN0b3JlIHRoZSByZXN1bHQgaW4gYSBuZXcgdmFyaWFibGUgY2FsbGVkIGFycml2ZS4KYGBge3J9CmFycml2ZSA8LSBkZXBhcnQgKyBob3VycygxNSkgKyBtaW51dGVzKDUwKQpgYGAKPGJyLz4KVGhlIGFycml2ZSB2YXJpYWJsZSBjb250YWlucyB0aGUgdGltZSB0aGF0IGl0IHdpbGwgYmUgaW4gTmV3IFlvcmsgd2hlbiB5b3UgYXJyaXZlIGluIEhvbmcgS29uZy4gV2hhdCB3ZSByZWFsbHkgd2FudCB0byBrbm93IGlzIHdoYXQgdGltZSBpdCB3aWxsIGJlIGluIEhvbmcgS29uZyB3aGVuIHlvdSBhcnJpdmUsIHNvIHRoYXQgeW91ciBmcmllbmQga25vd3Mgd2hlbiB0byBtZWV0IHlvdS4KClRoZSAqKndpdGhfdHooKSoqIGZ1bmN0aW9uIHJldHVybnMgYSBkYXRlLXRpbWUgYXMgaXQgd291bGQgYXBwZWFyIGluIGFub3RoZXIgdGltZSB6b25lLiBVc2UgKj93aXRoX3R6KiB0byBjaGVjayBvdXQgdGhlIGRvY3VtZW50YXRpb24uCmBgYHtyfQo/d2l0aF90egpgYGAKPiAqKkRlc2NyaXB0aW9uOioqICp3aXRoX3R6IHJldHVybnMgYSBkYXRlLXRpbWUgYXMgaXQgd291bGQgYXBwZWFyIGluIGEgZGlmZmVyZW50IHRpbWUgem9uZS4gVGhlIGFjdHVhbCBtb21lbnQgb2YgdGltZSBtZWFzdXJlZCBkb2VzIG5vdCBjaGFuZ2UsIGp1c3QgdGhlIHRpbWUgem9uZSBpdCBpcyBtZWFzdXJlZCBpbi4gd2l0aF90eiBkZWZhdWx0cyB0byB0aGUgVW5pdmVyc2FsIENvb3JkaW5hdGVkIHRpbWUgem9uZSAoVVRDKSB3aGVuIGFuIHVucmVjb2duaXplZCB0aW1lIHpvbmUgaXMgaW5wdXR0ZWQuIFNlZSAqKlN5cy50aW1lem9uZSgpKiogZm9yIG1vcmUgaW5mb3JtYXRpb24gb24gaG93IFIgcmVjb2duaXplcyB0aW1lIHpvbmVzLioKPiAqKlVzYWdlOioqICp3aXRoX3R6KHRpbWUsIHR6b25lID0gIiIpKgoKPGJyLz4KVXNlICoqd2l0aF90eigpKiogdG8gY29udmVydCBhcnJpdmUgdG8gdGhlICoiQXNpYS9Ib25nX0tvbmciKiB0aW1lIHpvbmUuIFJlYXNzaWduIHRoZSByZXN1bHQgdG8gKmFycml2ZSosIHNvIHRoYXQgaXQgd2lsbCBnZXQgdGhlIG5ldyB2YWx1ZS4KCmBgYHtyfQphcnJpdmUgPC0gd2l0aF90eihhcnJpdmUsIHR6b25lID0gIkFzaWEvSG9uZ19Lb25nIikKYGBgClByaW50IHRoZSB2YWx1ZSBvZiAqYXJyaXZlKiB0byB0aGUgY29uc29sZSwgc28gdGhhdCB5b3UgY2FuIHRlbGwgeW91ciBmcmllbmQgd2hhdCB0aW1lIHRvIHBpY2sgeW91IHVwIGZyb20gdGhlIGFpcnBvcnQuCmBgYHtyfQphcnJpdmUKYGBgCjxici8+CkZhc3QgZm9yd2FyZCB0byB5b3VyIGFycml2YWwgaW4gSG9uZyBLb25nLiBZb3UgYW5kIHlvdXIgZnJpZW5kIGhhdmUganVzdCBtZXQgYXQgdGhlIGFpcnBvcnQgYW5kIHlvdSByZWFsaXplIHRoYXQgdGhlIGxhc3QgdGltZSB5b3Ugd2VyZSB0b2dldGhlciB3YXMgaW4gU2luZ2Fwb3JlIG9uICpKdW5lIDE3LCAyMDA4Ki4gTmF0dXJhbGx5LCB5b3UnZCBsaWtlIHRvIGtub3cgZXhhY3RseSBob3cgbG9uZyBpdCBoYXMgYmVlbi4KClVzZSB0aGUgYXBwcm9wcmlhdGUgbHVicmlkYXRlIGZ1bmN0aW9uIHRvIHBhcnNlICoiSnVuZSAxNywgMjAwOCIqLCBqdXN0IGxpa2UgeW91IGRpZCBuZWFyIHRoZSBiZWdpbm5pbmcgb2YgdGhpcyBsZXNzb24uIFRoaXMgdGltZSwgaG93ZXZlciwgeW91IHNob3VsZCBzcGVjaWZ5IGFuIGV4dHJhIGFyZ3VtZW50LCAqdHogPSAiU2luZ2Fwb3JlIiouIFN0b3JlIHRoZSByZXN1bHQgaW4gYSB2YXJpYWJsZSBjYWxsZWQgKmxhc3RfdGltZSouCgpgYGB7cn0KbGFzdF90aW1lIDwtIG1keSgiSnVuZSAxNywgMjAwOCIsIHR6ID0gIlNpbmdhcG9yZSIpCmBgYApWaWV3IHRoZSBjb250ZW50cyBvZiAqbGFzdF90aW1lKi4KYGBge3J9Cmxhc3RfdGltZQpgYGAKPGJyLz4KUHVsbCB1cCB0aGUgZG9jdW1lbnRhdGlvbiBmb3IgKippbnRlcnZhbCgpKiosIHdoaWNoIHdlJ2xsIHVzZSB0byBleHBsb3JlIGhvdyBtdWNoIHRpbWUgaGFzIHBhc3NlZCBiZXR3ZWVuICphcnJpdmUqIGFuZCAqbGFzdF90aW1lKi4KYGBge3J9Cj9pbnRlcnZhbApgYGAKPiAqKkRlc2NyaXB0aW9uOioqICpjcmVhdGVzIGFuIEludGVydmFsIG9iamVjdCB3aXRoIHRoZSBzcGVjaWZpZWQgc3RhcnQgYW5kIGVuZCBkYXRlcy4gSWYgdGhlIHN0YXJ0IGRhdGUgb2NjdXJzIGJlZm9yZSB0aGUgZW5kIGRhdGUsIHRoZSBpbnRlcnZhbCB3aWxsIGJlIHBvc2l0aXZlLiBPdGhlcndpc2UsIGl0IHdpbGwgYmUgbmVnYXRpdmUuKgo+ICoqVXNhZ2U6KiogKmludGVydmFsKHN0YXJ0LCBlbmQgPSBOVUxMLCB0em9uZSA9IHR6KHN0YXJ0KSkqCgo8YnIgLz4KQ3JlYXRlIGFuICoqaW50ZXJ2YWwoKSoqIHRoYXQgc3BhbnMgZnJvbSAqbGFzdF90aW1lKiB0byAqYXJyaXZlKi4gU3RvcmUgaXQgaW4gYSBuZXcgdmFyaWFibGUgY2FsbGVkICpob3dfbG9uZyouCgpgYGB7cn0KaG93X2xvbmcgPC0gaW50ZXJ2YWwobGFzdF90aW1lLCBhcnJpdmUpCmBgYApOb3cgdXNlICphcy5wZXJpb2QoaG93X2xvbmcpKiB0byBzZWUgaG93IGxvbmcgaXQncyBiZWVuLgpgYGB7cn0KYXMucGVyaW9kKGhvd19sb25nKQpgYGAKPGJyLz4KVGhpcyBpcyB3aGVyZSB0aGluZ3MgZ2V0IGEgbGl0dGxlIHRyaWNreS4gQmVjYXVzZSBvZiB0aGluZ3MgbGlrZSBsZWFwIHllYXJzLCBsZWFwIHNlY29uZHMsIGFuZCBkYXlsaWdodCBzYXZpbmdzIHRpbWUsIHRoZSBsZW5ndGggb2YgYW55IGdpdmVuIG1pbnV0ZSwgZGF5LCBtb250aCwgd2Vlaywgb3IgeWVhciBpcyByZWxhdGl2ZSB0byB3aGVuIGl0IG9jY3Vycy4gSW4gY29udHJhc3QsIHRoZSBsZW5ndGggb2YgYSBzZWNvbmQgaXMgYWx3YXlzIHRoZSBzYW1lLCByZWdhcmRsZXNzIG9mIHdoZW4gaXQgb2NjdXJzLgoKVG8gYWRkcmVzcyB0aGVzZSBjb21wbGV4aXRpZXMsIHRoZSBhdXRob3JzIG9mIGx1YnJpZGF0ZSBpbnRyb2R1Y2UgZm91ciBjbGFzc2VzIG9mIHRpbWUgcmVsYXRlZCBvYmplY3RzOiAqaW5zdGFudHMsIGludGVydmFscywgZHVyYXRpb25zLCogYW5kICpwZXJpb2RzKi4gVGhlc2UgdG9waWNzIGFyZSBiZXlvbmQgdGhlIHNjb3BlIG9mIHRoaXMgbGVzc29uLCBidXQgeW91IGNhbiBmaW5kIGEgY29tcGxldGUgZGlzY3Vzc2lvbiBpbiB0aGUgMjAxMSBKb3VybmFsIG9mIFN0YXRpc3RpY2FsIFNvZnR3YXJlIHBhcGVyIHRpdGxlZCAqKidEYXRlcyBhbmQgVGltZXMgTWFkZSBFYXN5IHdpdGggbHVicmlkYXRlJyoqLgoKVGhpcyBjb25jbHVkZXMgb3VyIGludHJvZHVjdGlvbiB0byB3b3JraW5nIHdpdGggZGF0ZXMgYW5kIHRpbWVzIGluIGx1YnJpZGF0ZS4gSSBjcmVhdGVkIGEgbGl0dGxlIHRpbWVyIHRoYXQgc3RhcnRlZCBydW5uaW5nIGluIHRoZSBiYWNrZ3JvdW5kIHdoZW4geW91IGJlZ2FuIHRoaXMgbGVzc29uLiBUeXBlICoqc3RvcHdhdGNoKCkqKiB0byBzZWUgaG93IGxvbmcgeW91J3ZlIGJlZW4gd29ya2luZyEKCmBgYHtyfQpzdG9wd2F0Y2goKQpgYGAKCgotLS0KCjxjZW50ZXI+RU5EPC9jZW50ZXI+CgotLS0=