1 {lubridate}

Working with dates and times is not as simple as it looks, and the reasons are as many as they are diverse. Let us spell out a few of the major ones. For one, dates come in all shapes and sizes, as text that looks like this: “Monday, Jan10, 2010” to strings like “2020-28-01 12:49” but no matter what software you use, you have to be able to convert all dates into a standard format.

Second, if you need to calculate how much time has passed between events, for example, how many days go by before a patient returns to the Emergency Room (ER), how many days between Covid-19 deaths in an infected population, hours needed to fly from Columbus to an Francisco, and so on … you need to be able to move between months, days, hours, etc with ease, AND calculate the length of time in a way that automatically adjusts for leap years.

There are many more reasons I could advance but we might as well start working with dates. First up, some mangled date entries and we’ll see how to parse them into correct date formats!

Now we fix them up!

## [1] "2017-12-17"
## [1] "2017-12-17"
## [1] "2017-12-17"

today1, today2, and today3 all had the same structure of year-month-day and so ymd() works to get the format right. today4 has year-month-day-hours-minutes-seconds so we’ll have to do this one slightly differently. The same thing works for today5 as well.

## [1] "2017-12-17 14:32:41 UTC"
## [1] "2017-12-17 14:32:41 UTC"

today6 has a slightly different format, month-day-year-hours-minutes-seconds that is read in thus:

## [1] "2017-12-17 14:32:41 UTC"

today7 has a slightly different format, day-month-year-hours-minutes-seconds that is read in thus:

## [1] "2017-12-17 14:32:41 UTC"

1.1 Working with flight dates

Now we should be able to start working with some date variables, and the ideal candidate would be the flight date column our cmhflights data. So the first thing we will do is load that data-set so that we can work with it.

I dislike this uppercase-lowercase mixture they have in the column names and so will get rid of it as shown below, making everything nice and lowercase. This is done with the janitor package’s clean_names() command. I am also going to use select() to keep only a handful of columns since keeping 100+ is of no value.

The first thing I want to do now is to label the days of the week, the months, and then also create that flag for the weekend versus weekdays. Here goes:

Now let us ask some questions: (a) What month had the most flights? (b) What day of the week had the most flights? (c) What about weekends; did weekends have more flights than weekdays? (d) With respect to (c), does whatever pattern we see vary by month or does month not matter?

## # A tibble: 9 x 2
##   monthname     n
##   <ord>     <int>
## 1 July       4295
## 2 August     4279
## 3 June       4138
## 4 April      4123
## 5 March      4101
## 6 May        4098
## 7 September  3789
## 8 January    3757
## 9 February   3413
## # A tibble: 7 x 2
##   dayofweek     n
##   <ord>     <int>
## 1 Wednesday  5435
## 2 Thursday   5417
## 3 Sunday     5395
## 4 Tuesday    5368
## 5 Monday     5284
## 6 Saturday   4892
## 7 Friday     4202
## # A tibble: 2 x 2
##   weekend     n
##   <chr>   <int>
## 1 Weekday 25706
## 2 Weekend 10287
## # A tibble: 18 x 3
##    monthname weekend     n
##    <ord>     <chr>   <int>
##  1 August    Weekday  3165
##  2 March     Weekday  3047
##  3 June      Weekday  3023
##  4 July      Weekday  2918
##  5 May       Weekday  2908
##  6 April     Weekday  2876
##  7 September Weekday  2760
##  8 January   Weekday  2557
##  9 February  Weekday  2452
## 10 July      Weekend  1377
## 11 April     Weekend  1247
## 12 January   Weekend  1200
## 13 May       Weekend  1190
## 14 June      Weekend  1115
## 15 August    Weekend  1114
## 16 March     Weekend  1054
## 17 September Weekend  1029
## 18 February  Weekend   961

So most flights are on weekdays, but weekend flights lead in July while weekday flights lead in August.

But wait a minute, if I can calculate these frequencies, why not do it by the hour. That may allow us to answer such questions as: What hour of the day has the most flights, the most delays? What about by airline? What if we push this to the minute of the hour?

Well, first we will have to create a new variable that marks just the hour of the day in the 24-hour cycle. But to do this we will first need to create a single flight_date_time column that will be in the ymd_hms format. How? With unite().

Okay, now we create flt_date_time and note the seconds here are automatically coerced to be 00.

Now we extract just the hour of the day the flight was scheduled to depart.

All righty then, now we start digging in. What hour has the most flights, and does this vary by the day of the week? By the Month?

## # A tibble: 24 x 2
##    flt_hour     n
##       <int> <int>
##  1       10  2626
##  2       17  2454
##  3        7  2448
##  4       16  2395
##  5       15  2392
##  6       14  2390
##  7        8  2331
##  8        9  2283
##  9       18  2268
## 10        6  2106
## # … with 14 more rows
## # A tibble: 199 x 3
##    monthname flt_hour     n
##    <ord>        <int> <int>
##  1 May             10   376
##  2 August           8   328
##  3 June            10   325
##  4 March           17   323
##  5 July             8   319
##  6 May             15   317
##  7 April           17   314
##  8 May             18   314
##  9 March            7   313
## 10 January         16   312
## # … with 189 more rows

Looks like 10:00 and then 17:00, these would be your best bets if you were looking to catch a flight and wanted as many options as possible. On the flip side, this might also be the time when flights get delayed more often because there are so many flights scheduled at these hours!

Now I want to ask the question about delays: Are median delays higher at certain hours?

## # A tibble: 24 x 2
##    flt_hour md.delay
##       <int>    <dbl>
##  1        3      290
##  2        2      233
##  3        1      174
##  4        0      137
##  5       23       49
##  6       21        6
##  7       18        2
##  8       19        1
##  9       15        0
## 10       16        0
## # … with 14 more rows
## # A tibble: 24 x 2
##    flt_hour md.delay
##       <int>    <dbl>
##  1        5       -4
##  2        6       -4
##  3        7       -4
##  4        8       -3
##  5        9       -3
##  6       10       -2
##  7       11       -2
##  8       12       -2
##  9       13       -2
## 10       14       -2
## # … with 14 more rows

The expected result; Shortest median delay is at 5 AM, and delays increase by the hour. Bottom-line: Fly as early as you can. Might this vary by destination?

## # A tibble: 418 x 3
## # Groups:   dest_city_name [26]
##    dest_city_name  flt_hour md.delay
##    <chr>              <int>    <dbl>
##  1 Newark, NJ             6    1046 
##  2 Newark, NJ             7     688 
##  3 Denver, CO            14     489 
##  4 Houston, TX            7     420.
##  5 Minneapolis, MN        0     381 
##  6 Atlanta, GA            1     348 
##  7 New York, NY           0     337 
##  8 Tampa, FL              1     324 
##  9 Nashville, TN         23     323 
## 10 Fort Myers, FL         0     297 
## # … with 408 more rows

Avoid flying to Newark, NJ, even at 6 or 7 AM. Might these vary by airline?

## # A tibble: 656 x 4
## # Groups:   carrier, dest_city_name [52]
##    carrier dest_city_name  flt_hour md.delay
##    <chr>   <chr>              <int>    <dbl>
##  1 EV      Newark, NJ             6    1046 
##  2 EV      Chicago, IL            6    1024 
##  3 EV      Newark, NJ             7     688 
##  4 DL      Columbus, OH           5     526 
##  5 F9      Denver, CO            14     489 
##  6 DL      Los Angeles, CA       15     481 
##  7 AA      Phoenix, AZ           15     463 
##  8 EV      Houston, TX            7     420.
##  9 UA      Chicago, IL            0     394 
## 10 DL      Minneapolis, MN        0     381 
## # … with 646 more rows

Worst early-morning delays are for EV, to Newark and to Chicago.

1.2 Passage of Time

Let us assume we are interested in seeing how much time lapses between successive flights of each aircraft seen in the data. We know we can identify each unique aircraft by its tail_num. So let us first see how many times is each aircraft seen and create a new column called number_flew. Some rows of data are missing flt_date_time and tail_num so I will filter these out as well.

## # A tibble: 6 x 3
## # Groups:   tail_num [1]
##   tail_num flt_date_time       n_flew
##   <chr>    <dttm>               <int>
## 1 N396SW   2017-08-23 10:07:00     73
## 2 N396SW   2017-08-23 08:07:00     72
## 3 N396SW   2017-08-19 08:20:00     71
## 4 N396SW   2017-08-18 15:24:00     70
## 5 N396SW   2017-08-06 21:43:00     69
## 6 N396SW   2017-08-06 18:53:00     68

So far so good; N396SW is the winner and has well-earned its retirement.

Now we need to see how much time lapsed between flights, and this is just the difference between the preceding flt_date_time recorded and the most recent flt_date_time. As we do this, note that by default time span (ytspan) is calculated in seconds.

## # A tibble: 73 x 8
## # Groups:   tail_num [1]
##    tail_num flt_date_time       n_flew   tspan tspan.minutes tspan.hours
##    <chr>    <dttm>               <int>   <dbl>         <dbl>       <dbl>
##  1 N396SW   2017-01-05 09:30:00      1      NA            NA       NA   
##  2 N396SW   2017-01-05 12:19:00      2   10140           169        2.82
##  3 N396SW   2017-01-11 08:34:00      3  504900          8415      140.  
##  4 N396SW   2017-01-11 10:44:00      4    7800           130        2.17
##  5 N396SW   2017-01-19 10:31:00      5  690420         11507      192.  
##  6 N396SW   2017-01-19 14:28:00      6   14220           237        3.95
##  7 N396SW   2017-02-10 08:23:00      7 1878900         31315      522.  
##  8 N396SW   2017-02-10 10:32:00      8    7740           129        2.15
##  9 N396SW   2017-02-15 15:20:00      9  449280          7488      125.  
## 10 N396SW   2017-02-15 18:15:00     10   10500           175        2.92
## # … with 63 more rows, and 2 more variables: tspan.days <dbl>,
## #   tspan.weeks <dbl>

Here, tspan is being converted into, say, minutes by dividing it by 60, into hours by dividing tspan by 60 x 60 = 3600, and so on. Note that dminutes(1) is calculating the time span in one-minute intervals. Similarly for hours, days, and weeks. Thus if you ran dhours(2) you would get the time interval in 2-hour increments.

There is a lot more we could do with time but the few things we have covered so far would be the more common tasks we usually encounter.

2 Practice Exercises

2.1 Exercise 1

The data below come from tidytuesday and provide information on accidents at theme parks. You can see more of these data available here. The data give you some details of where and when the accident occurred, and something about the injured party as well.

safer_parks.csv {-}

variable class description
acc_id double Unique ID
acc_date character Accident Date
acc_state character Accident State
acc_city character Accident City
fix_port character .
source character Source of injury report
bus_type character Business type
industry_sector character Industry sector
device_category character Device category
device_type character Device type
tradename_or_generic character Common name of the device
manufacturer character Manufacturer of device
num_injured double Num injured
age_youngest double Youngest individual injured
gender character Gender of individual injured
acc_desc character Description of accident
injury_desc character Injury description
report character Report URL
category character Category of accident
mechanical double Mechanical failure (binary NA/1)
op_error double Operator error (binary NA/1)
employee double Employee error (binary NA/1)
notes character Additional notes

Working with the safer_parks data, complete the following tasks.

Problem (a)

Using acc_date, create a new date variable called idate that is a proper date column generated via {lubridate}.

Problem (b)

Now create new columns for (i) the month of the accident, and (ii) the day of the week. These should not be abbreviated (i.e., we should see the values as ‘Monday’ instead of ‘Mon’, “July” instead of “Jul”). What month had the highest number of accidents? What day of the week had the highest number of accidents?

Problem (c)

What if you look at days of the week by month? Does the same day of the week show up with the most accidents regardless of month or do we see some variation?

Problem (d)

What were the five dates with the most number of accidents?

Problem (e)

Using the Texas injury data, answer the following question: What ride was the safest? [Hint: For each ride (ride_name) you will need to calculate the number of days between accidents. The ride with the highest number of days is the safest.]

tx_injuries.csv

variable class description
injury_report_rec double Unique Record ID
name_of_operation character Company name
city character City
st character State (all TX)
injury_date character Injury date - note there are some different formats
ride_name character Ride Name
serial_no character Serial number of ride
gender character Gender of the injured individual
age character Age of the injured individual
body_part character Body part injured
alleged_injury character Alleged injury - type of injury
cause_of_injury character Approximate cause of the injury (free text)
other character Anecdotal information in addition to cause of injury

You should note that this assumes each ride was in operation for the same amount of time. If this is not true then our estimates will be unreliable.

2.2 Exercise 2

These data (see below) come from this story: The next generation: The space race is dominated by new contenders. You have data on space missions over time, with dates of the launch, the launching agency/country, type of launch vehicle, and so on.

launches

variable definition
tag Harvard or [COSPAR][cospar] id of launch
JD [Julian Date][jd] of launch
launch_date date of launch
launch_year year of launch
type type of launch vehicle
variant variant of launch vehicle
mission space mission
agency launching agency
state_code launching agency’s state
category success (O) or failure (F)
agency_type type of agency

Problem (a)

Create a new column called date that stores launch_date as a proper data field in ymd format from {lubridate}.

Problem (b)

Creating columns as needed, calculate and show the number of launches first by year, then by month, and then by day of the week. The result should be arranged in descending order of the number of launches.

Problem (c)

How many launches were successful (O) versus failed (F) by country and year? The countries of interest will be state_code values of “CN”, “F”, “J”, “RU”, “SU”, “US”. You do not need to arrange your results in any order.

LS0tCnRpdGxlOiAiTVBBIDU4MzAgLSBNb2R1bGUgMDQiCnN1YnRpdGxlOiAiU3ByaW5nIDIwMjAiCmF1dGhvcjogIlByb2Zlc3NvciBSdWhpbCIKZGF0ZTogIlVwZGF0ZWQgb24gYHIgU3lzLkRhdGUoKWAiCm91dHB1dDogCiAgaHRtbF9kb2N1bWVudDogCiAgICBjb2RlX2Rvd25sb2FkOiB5ZXMKICAgIGZpZ19jYXB0aW9uOiB5ZXMKICAgIGhpZ2hsaWdodDogemVuYnVybgogICAgbnVtYmVyX3NlY3Rpb25zOiB5ZXMKICAgIHRoZW1lOiBmbGF0bHkKICAgIHRvYzogeWVzCiAgICB0b2NfZmxvYXQ6IHllcwplZGl0b3Jfb3B0aW9uczogCiAgY2h1bmtfb3V0cHV0X3R5cGU6IGNvbnNvbGUKLS0tCgo8c3R5bGUgdHlwZT0idGV4dC9jc3MiPgoKYm9keXsgLyogTm9ybWFsICAqLwovKiAgICBmb250LWZhbWlseTogTGF0bywgc2Fucy1zZXJpZjsgIAogICAgICBmb250LWZhbWlseTogTXVrdGEsIHNhbnMtc2VyaWY7IAogICAgICBmb250LWZhbWlseTogJ051bml0byBTYW5zJywgc2Fucy1zZXJpZjsKICAgICAgZm9udC1mYW1pbHk6IEthcmxhLCBzYW5zLXNlcmlmOyAgKi8KICAgICAgZm9udC1mYW1pbHk6ICdNZXJyaXdlYXRoZXIgU2FucycsIHNhbnMtc2VyaWY7IAogICAgICBmb250LXNpemU6IDE4cHg7CiAgfQoKaDEudGl0bGUgewogIGZvbnQtc2l6ZTogMzhweDsKICBjb2xvcjogRGFya1JlZDsKfQoKaDEgeyAvKiBIZWFkZXIgMSAqLwogIGZvbnQtc2l6ZTogMjhweDsKICBjb2xvcjogRGFya0JsdWU7Cn0KCmgyIHsgLyogSGVhZGVyIDIgKi8KICAgIGZvbnQtc2l6ZTogMjJweDsKICBjb2xvcjogRGFya0JsdWU7Cn0KCmgzIHsgLyogSGVhZGVyIDMgKi8KICBmb250LXNpemU6IDE4cHg7CiAgY29sb3I6IERhcmtCbHVlOwp9Cgpjb2RlLnIgeyAvKiBDb2RlIGJsb2NrICovCiAgICBmb250LWZhbWlseTogTXVrdGEsIHNhbnMtc2VyaWY7IAogICAgZm9udC13ZWlnaHQ6IDYwMDsgIAogICAgZm9udC1zaXplOiAxNnB4Owp9CgpwcmUgeyAvKiBDb2RlIGJsb2NrIC0gZGV0ZXJtaW5lcyBjb2RlIHNwYWNpbmcgYmV0d2VlbiBsaW5lcyAqLwogICAgZm9udC1zaXplOiAxNnB4OwogICAgcGFkZGluZzogMTBweDsKICAgIGRpc3BsYXk6IGJsb2NrOwp9IAo8L3N0eWxlPgoKCmBgYHtyIGtsaXBweSwgZWNobyA9IEZBTFNFLCBpbmNsdWRlID0gVFJVRX0Ka2xpcHB5OjprbGlwcHkodG9vbHRpcF9tZXNzYWdlID0gJ0NsaWNrIHRvIGNvcHknLCB0b29sdGlwX3N1Y2Nlc3MgPSAnRG9uZScsIGNvbG9yID0gJ2Nvcm5mbG93ZXJibHVlJywgcG9zaXRpb24gPSBjKCd0b3AnLCAncmlnaHQnKSkKYGBgCgpgYGB7ciBzZXR1cCwgaW5jbHVkZT1GQUxTRX0Ka25pdHI6Om9wdHNfY2h1bmskc2V0KGVjaG8gPSBUUlVFLCB3YXJuaW5nID0gRkFMU0UsIG1lc3NhZ2UgPSBGQUxTRSwgZHBpID0gMzAwLCBjYWNoZSA9IFRSVUUsIGZpZy5hbGlnbiA9ICJjZW50ZXIiLCBmaWcud2lkdGggPSAxMCwgZmlnLmhlaWdodCA9IDgsIG91dC53aWR0aCA9ICIxMDAlIiwgaGlnaGxpZ2h0ID0gVFJVRSkgCmBgYAoKYGBge3Igc2V0dXBuLCBpbmNsdWRlPUZBTFNFfQojIGNhbW8gPSBrbml0cjo6a25pdF90aGVtZSRnZXQoImNhbW8iKQojIGtuaXRyOjprbml0X3RoZW1lJHNldChjYW1vKQpgYGAKCgojIHtsdWJyaWRhdGV9IApXb3JraW5nIHdpdGggZGF0ZXMgYW5kIHRpbWVzIGlzIG5vdCBhcyBzaW1wbGUgYXMgaXQgbG9va3MsIGFuZCB0aGUgcmVhc29ucyBhcmUgYXMgbWFueSBhcyB0aGV5IGFyZSBkaXZlcnNlLiBMZXQgdXMgc3BlbGwgb3V0IGEgZmV3IG9mIHRoZSBtYWpvciBvbmVzLiBGb3Igb25lLCBkYXRlcyBjb21lIGluIGFsbCBzaGFwZXMgYW5kIHNpemVzLCBhcyB0ZXh0IHRoYXQgbG9va3MgbGlrZSB0aGlzOiAiTW9uZGF5LCBKYW4xMCwgMjAxMCIgdG8gc3RyaW5ncyBsaWtlICIyMDIwLTI4LTAxIDEyOjQ5IiBidXQgbm8gbWF0dGVyIHdoYXQgc29mdHdhcmUgeW91IHVzZSwgeW91IGhhdmUgdG8gYmUgYWJsZSB0byBjb252ZXJ0IGFsbCBkYXRlcyBpbnRvIGEgc3RhbmRhcmQgZm9ybWF0LiAgCgpTZWNvbmQsIGlmIHlvdSBuZWVkIHRvIGNhbGN1bGF0ZSBob3cgbXVjaCB0aW1lIGhhcyBwYXNzZWQgYmV0d2VlbiBldmVudHMsIGZvciBleGFtcGxlLCBob3cgbWFueSBkYXlzIGdvIGJ5IGJlZm9yZSBhIHBhdGllbnQgcmV0dXJucyB0byB0aGUgRW1lcmdlbmN5IFJvb20gKEVSKSwgaG93IG1hbnkgZGF5cyBiZXR3ZWVuIENvdmlkLTE5IGRlYXRocyBpbiBhbiBpbmZlY3RlZCBwb3B1bGF0aW9uLCBob3VycyBuZWVkZWQgdG8gZmx5IGZyb20gQ29sdW1idXMgdG8gYW4gRnJhbmNpc2NvLCBhbmQgc28gb24gLi4uIHlvdSBuZWVkIHRvIGJlIGFibGUgdG8gbW92ZSBiZXR3ZWVuIG1vbnRocywgZGF5cywgaG91cnMsIGV0YyB3aXRoIGVhc2UsIEFORCBjYWxjdWxhdGUgdGhlIGxlbmd0aCBvZiB0aW1lIGluIGEgd2F5IHRoYXQgYXV0b21hdGljYWxseSBhZGp1c3RzIGZvciBgbGVhcGAgeWVhcnMuIAoKVGhlcmUgYXJlIG1hbnkgbW9yZSByZWFzb25zIEkgY291bGQgYWR2YW5jZSBidXQgd2UgbWlnaHQgYXMgd2VsbCBzdGFydCB3b3JraW5nIHdpdGggZGF0ZXMuIEZpcnN0IHVwLCBzb21lIG1hbmdsZWQgZGF0ZSBlbnRyaWVzIGFuZCB3ZSdsbCBzZWUgaG93IHRvIHBhcnNlIHRoZW0gaW50byBjb3JyZWN0IGRhdGUgZm9ybWF0cyEKCmBgYHtyIGx1MDF9CiIyMDE3MTIxNyIgLT4gdG9kYXkxIAoiMjAxNy0xMi0xNyIgLT4gdG9kYXkyIAoiMjAxNyBEZWNlbWJlciAxNyIgLT4gdG9kYXkzIAoiMjAxNzEyMTcxNDMyNDEiIC0+IHRvZGF5NCAKIjIwMTcgRGVjZW1iZXIgMTcgMTQ6MzI6NDEiIC0+IHRvZGF5NSAKIkRlY2VtYmVyIDE3IDIwMTcgMTQ6MzI6NDEiIC0+IHRvZGF5NiAKIjE3LURlYywgMjAxNyAxNDozMjo0MSIgLT4gdG9kYXk3IApgYGAKCk5vdyB3ZSBmaXggdGhlbSB1cCEKCmBgYHtyIGx1MDJ9CmxpYnJhcnkodGlkeXZlcnNlKQpsaWJyYXJ5KGx1YnJpZGF0ZSkKCnltZCh0b2RheTEpIC0+IGRhdGUxCnltZCh0b2RheTIpIC0+IGRhdGUyIAp5bWQodG9kYXkzKSAtPiBkYXRlMyAKZGF0ZTE7IGRhdGUyOyBkYXRlMwpgYGAKCmB0b2RheTFgLCBgdG9kYXkyYCwgYW5kIGB0b2RheTNgIGFsbCBoYWQgdGhlIHNhbWUgc3RydWN0dXJlIG9mIHllYXItbW9udGgtZGF5IGFuZCBzbyBgeW1kKClgIHdvcmtzIHRvIGdldCB0aGUgZm9ybWF0IHJpZ2h0LiBgdG9kYXk0YCBoYXMgeWVhci1tb250aC1kYXktaG91cnMtbWludXRlcy1zZWNvbmRzIHNvIHdlJ2xsIGhhdmUgdG8gZG8gdGhpcyBvbmUgc2xpZ2h0bHkgZGlmZmVyZW50bHkuIFRoZSBzYW1lIHRoaW5nIHdvcmtzIGZvciBgdG9kYXk1YCBhcyB3ZWxsLgoKYGBge3IgbHUwM30KeW1kX2htcyh0b2RheTQpIC0+IGRhdGU0IAp5bWRfaG1zKHRvZGF5NSkgLT4gZGF0ZTUgCmRhdGU0OyBkYXRlNQpgYGAKCmB0b2RheTZgIGhhcyBhIHNsaWdodGx5IGRpZmZlcmVudCBmb3JtYXQsIGBtb250aC1kYXkteWVhci1ob3Vycy1taW51dGVzLXNlY29uZHNgIHRoYXQgaXMgcmVhZCBpbiB0aHVzOgoKYGBge3IgbHUwNH0KbWR5X2htcyh0b2RheTYpIC0+IGRhdGU2IApkYXRlNgpgYGAKCmB0b2RheTdgIGhhcyBhIHNsaWdodGx5IGRpZmZlcmVudCBmb3JtYXQsIGBkYXktbW9udGgteWVhci1ob3Vycy1taW51dGVzLXNlY29uZHNgIHRoYXQgaXMgcmVhZCBpbiB0aHVzOgoKYGBge3IgbHUwNX0KZG15X2htcyh0b2RheTcpIC0+IGRhdGU3IApkYXRlNwpgYGAKCiMjIFdvcmtpbmcgd2l0aCBmbGlnaHQgZGF0ZXMKTm93IHdlIHNob3VsZCBiZSBhYmxlIHRvIHN0YXJ0IHdvcmtpbmcgd2l0aCBzb21lIGRhdGUgdmFyaWFibGVzLCBhbmQgdGhlIGlkZWFsIGNhbmRpZGF0ZSB3b3VsZCBiZSB0aGUgZmxpZ2h0IGRhdGUgY29sdW1uIG91ciBgY21oZmxpZ2h0c2AgZGF0YS4gU28gdGhlIGZpcnN0IHRoaW5nIHdlIHdpbGwgZG8gaXMgbG9hZCB0aGF0IGRhdGEtc2V0IHNvIHRoYXQgd2UgY2FuIHdvcmsgd2l0aCBpdC4gCgpgYGB7ciBsdTA2fQpsaWJyYXJ5KGhlcmUpCmxvYWQoaGVyZSgiZGF0YSIsICJjbWhmbGlnaHRzXzAxMDkyMDE3LlJEYXRhIikpCmBgYAoKSSBkaXNsaWtlIHRoaXMgdXBwZXJjYXNlLWxvd2VyY2FzZSBtaXh0dXJlIHRoZXkgaGF2ZSBpbiB0aGUgY29sdW1uIG5hbWVzIGFuZCBzbyB3aWxsIGdldCByaWQgb2YgaXQgYXMgc2hvd24gYmVsb3csIG1ha2luZyBldmVyeXRoaW5nIG5pY2UgYW5kIGxvd2VyY2FzZS4gVGhpcyBpcyBkb25lIHdpdGggdGhlIGBqYW5pdG9yYCBwYWNrYWdlJ3MgYGNsZWFuX25hbWVzKClgIGNvbW1hbmQuIEkgYW0gYWxzbyBnb2luZyB0byB1c2UgYHNlbGVjdCgpYCB0byBrZWVwIG9ubHkgYSBoYW5kZnVsIG9mIGNvbHVtbnMgc2luY2Uga2VlcGluZyAxMDArIGlzIG9mIG5vIHZhbHVlLiAKCmBgYHtyIGx1MDd9CmxpYnJhcnkoamFuaXRvcikKCmNtaGZsaWdodHMgJT4lCiAgY2xlYW5fbmFtZXMoKSAlPiUKICBzZWxlY3QoCiAgICB5ZWFyLCBtb250aCwgZGF5b2ZfbW9udGgsIGRheV9vZl93ZWVrLCBmbGlnaHRfZGF0ZSwgY2FycmllciwKICAgIHRhaWxfbnVtLCBmbGlnaHRfbnVtLCBvcmlnaW5fY2l0eV9uYW1lLCBkZXN0X2NpdHlfbmFtZSwKICAgIGRlcF90aW1lLCBkZXBfZGVsYXksIGFycl90aW1lLCBhcnJfZGVsYXksIGNhbmNlbGxlZCwgZGl2ZXJ0ZWQKICAgICkgLT4gY21oLmRmCmBgYAoKVGhlIGZpcnN0IHRoaW5nIEkgd2FudCB0byBkbyBub3cgaXMgdG8gbGFiZWwgdGhlIGRheXMgb2YgdGhlIHdlZWssIHRoZSBtb250aHMsIGFuZCB0aGVuIGFsc28gY3JlYXRlIHRoYXQgZmxhZyBmb3IgdGhlIGB3ZWVrZW5kYCB2ZXJzdXMgYHdlZWtkYXlzYC4gSGVyZSBnb2VzOgoKYGBge3IgbHUwOH0KY21oLmRmICU+JQogIG11dGF0ZSgKICAgIGRheW9md2VlayA9IHdkYXkoCiAgICAgIGRheV9vZl93ZWVrLAogICAgICBhYmJyID0gRkFMU0UsCiAgICAgIGxhYmVsID0gVFJVRQogICAgICApLAogICAgbW9udGhuYW1lID0gbW9udGgoCiAgICAgIG1vbnRoLAogICAgICBhYmJyID0gRkFMU0UsCiAgICAgIGxhYmVsID0gVFJVRQogICAgICApLAogICAgd2Vla2VuZCA9IGNhc2Vfd2hlbigKICAgICAgZGF5b2Z3ZWVrICVpbiUgYygiU2F0dXJkYXkiLCAiU3VuZGF5IikgfiAiV2Vla2VuZCIsCiAgICAgIFRSVUUgfiAiV2Vla2RheSIKICAgICAgKQogICAgKSAtPiBjbWguZGYgCmBgYAoKTm93IGxldCB1cyBhc2sgc29tZSBxdWVzdGlvbnM6IChhKSBXaGF0IG1vbnRoIGhhZCB0aGUgbW9zdCBmbGlnaHRzPyAoYikgV2hhdCBkYXkgb2YgdGhlIHdlZWsgaGFkIHRoZSBtb3N0IGZsaWdodHM/IChjKSBXaGF0IGFib3V0IHdlZWtlbmRzOyBkaWQgd2Vla2VuZHMgaGF2ZSBtb3JlIGZsaWdodHMgdGhhbiB3ZWVrZGF5cz8gKGQpIFdpdGggcmVzcGVjdCB0byAoYyksIGRvZXMgd2hhdGV2ZXIgcGF0dGVybiB3ZSBzZWUgdmFyeSBieSBtb250aCBvciBkb2VzIG1vbnRoIG5vdCBtYXR0ZXI/IAoKYGBge3IgbHUwOX0KY21oLmRmICU+JQogIGNvdW50KG1vbnRobmFtZSwgc29ydCA9IFRSVUUpICMgKGEpCgpjbWguZGYgJT4lCiAgY291bnQoZGF5b2Z3ZWVrLCBzb3J0ID0gVFJVRSkgIyAoYikgCgpjbWguZGYgJT4lCiAgY291bnQod2Vla2VuZCwgc29ydCA9IFRSVUUpICMgKGMpIAoKY21oLmRmICU+JQogIGNvdW50KG1vbnRobmFtZSwgd2Vla2VuZCwgc29ydCA9IFRSVUUpICMgKGQpIApgYGAKClNvIG1vc3QgZmxpZ2h0cyBhcmUgb24gd2Vla2RheXMsIGJ1dCB3ZWVrZW5kIGZsaWdodHMgbGVhZCBpbiBKdWx5IHdoaWxlIHdlZWtkYXkgZmxpZ2h0cyBsZWFkIGluIEF1Z3VzdC4gCgpCdXQgd2FpdCBhIG1pbnV0ZSwgaWYgSSBjYW4gY2FsY3VsYXRlIHRoZXNlIGZyZXF1ZW5jaWVzLCB3aHkgbm90IGRvIGl0IGJ5IHRoZSBob3VyLiBUaGF0IG1heSBhbGxvdyB1cyB0byBhbnN3ZXIgc3VjaCBxdWVzdGlvbnMgYXM6IFdoYXQgaG91ciBvZiB0aGUgZGF5IGhhcyB0aGUgbW9zdCBmbGlnaHRzLCB0aGUgbW9zdCBkZWxheXM/IFdoYXQgYWJvdXQgYnkgYWlybGluZT8gV2hhdCBpZiB3ZSBwdXNoIHRoaXMgdG8gdGhlIG1pbnV0ZSBvZiB0aGUgaG91cj8gCgpXZWxsLCBmaXJzdCB3ZSB3aWxsIGhhdmUgdG8gY3JlYXRlIGEgbmV3IHZhcmlhYmxlIHRoYXQgbWFya3MganVzdCB0aGUgaG91ciBvZiB0aGUgZGF5IGluIHRoZSAyNC1ob3VyIGN5Y2xlLiBCdXQgdG8gZG8gdGhpcyB3ZSB3aWxsIGZpcnN0IG5lZWQgdG8gY3JlYXRlIGEgc2luZ2xlIGBmbGlnaHRfZGF0ZV90aW1lYCBjb2x1bW4gdGhhdCB3aWxsIGJlIGluIHRoZSBgeW1kX2htc2AgZm9ybWF0LiBIb3c/IFdpdGggYHVuaXRlKClgLiAgCgpgYGB7ciBsdTEwfQpjbWguZGYgJT4lCiAgdW5pdGUoCiAgICBjb2wgPSAiZmxpZ2h0X2RhdGVfdGltZSIsCiAgICBjKGZsaWdodF9kYXRlLCBkZXBfdGltZSksCiAgICBzZXAgPSAiOiIsCiAgICByZW1vdmUgPSBUUlVFCiAgKSAtPiBjbWguZGYKYGBgCgpPa2F5LCBub3cgd2UgY3JlYXRlIGBmbHRfZGF0ZV90aW1lYCBhbmQgbm90ZSB0aGUgc2Vjb25kcyBoZXJlIGFyZSBhdXRvbWF0aWNhbGx5IGNvZXJjZWQgdG8gYmUgYDAwYC4gCgpgYGB7ciBsdTExfQpjbWguZGYgJT4lCiAgbXV0YXRlKAogICAgZmx0X2RhdGVfdGltZSA9IHltZF9obShmbGlnaHRfZGF0ZV90aW1lKQogICAgICApIC0+IGNtaC5kZgpgYGAKCk5vdyB3ZSBleHRyYWN0IGp1c3QgdGhlIGhvdXIgb2YgdGhlIGRheSB0aGUgZmxpZ2h0IHdhcyBzY2hlZHVsZWQgdG8gZGVwYXJ0LiAKCmBgYHtyIGx1MTJ9CmNtaC5kZiAlPiUKICBtdXRhdGUoCiAgICBmbHRfaG91ciA9IGhvdXIoZmx0X2RhdGVfdGltZSksCiAgICBmbHRfbWludXRlID0gbWludXRlKGZsdF9kYXRlX3RpbWUpCiAgICApIC0+IGNtaC5kZgpgYGAKCkFsbCByaWdodHkgdGhlbiwgbm93IHdlIHN0YXJ0IGRpZ2dpbmcgaW4uIFdoYXQgaG91ciBoYXMgdGhlIG1vc3QgZmxpZ2h0cywgYW5kIGRvZXMgdGhpcyB2YXJ5IGJ5IHRoZSBkYXkgb2YgdGhlIHdlZWs/IEJ5IHRoZSBNb250aD8gCgpgYGB7ciBsdTEzfQpjbWguZGYgJT4lCiAgY291bnQoZmx0X2hvdXIsIHNvcnQgPSBUUlVFKQoKY21oLmRmICU+JQogIGNvdW50KG1vbnRobmFtZSwgZmx0X2hvdXIsIHNvcnQgPSBUUlVFKQpgYGAKCkxvb2tzIGxpa2UgMTA6MDAgYW5kIHRoZW4gMTc6MDAsIHRoZXNlIHdvdWxkIGJlIHlvdXIgYmVzdCBiZXRzIGlmIHlvdSB3ZXJlIGxvb2tpbmcgdG8gY2F0Y2ggYSBmbGlnaHQgYW5kIHdhbnRlZCBhcyBtYW55IG9wdGlvbnMgYXMgcG9zc2libGUuIE9uIHRoZSBmbGlwIHNpZGUsIHRoaXMgbWlnaHQgYWxzbyBiZSB0aGUgdGltZSB3aGVuIGZsaWdodHMgZ2V0IGRlbGF5ZWQgbW9yZSBvZnRlbiBiZWNhdXNlIHRoZXJlIGFyZSBzbyBtYW55IGZsaWdodHMgc2NoZWR1bGVkIGF0IHRoZXNlIGhvdXJzISAKCk5vdyBJIHdhbnQgdG8gYXNrIHRoZSBxdWVzdGlvbiBhYm91dCBkZWxheXM6IEFyZSBtZWRpYW4gZGVsYXlzIGhpZ2hlciBhdCBjZXJ0YWluIGhvdXJzPyAKCmBgYHtyIGx1MTR9CmNtaC5kZiAlPiUKICBncm91cF9ieShmbHRfaG91cikgJT4lCiAgc3VtbWFyaXNlKG1kLmRlbGF5ID0gbWVkaWFuKGRlcF9kZWxheSwgbmEucm0gPSBUUlVFKSkgJT4lCiAgYXJyYW5nZSgtbWQuZGVsYXkpCiAgCmNtaC5kZiAlPiUKICBncm91cF9ieShmbHRfaG91cikgJT4lCiAgc3VtbWFyaXNlKG1kLmRlbGF5ID0gbWVkaWFuKGRlcF9kZWxheSwgbmEucm0gPSBUUlVFKSkgJT4lCiAgYXJyYW5nZShtZC5kZWxheSkKYGBgCgpUaGUgZXhwZWN0ZWQgcmVzdWx0OyBTaG9ydGVzdCBtZWRpYW4gZGVsYXkgaXMgYXQgNSBBTSwgYW5kIGRlbGF5cyBpbmNyZWFzZSBieSB0aGUgaG91ci4gQm90dG9tLWxpbmU6IEZseSBhcyBlYXJseSBhcyB5b3UgY2FuLiBNaWdodCB0aGlzIHZhcnkgYnkgZGVzdGluYXRpb24/CgpgYGB7ciBsdTE1fQpjbWguZGYgJT4lCiAgZ3JvdXBfYnkoZGVzdF9jaXR5X25hbWUsIGZsdF9ob3VyKSAlPiUKICBzdW1tYXJpc2UobWQuZGVsYXkgPSBtZWRpYW4oZGVwX2RlbGF5LCBuYS5ybSA9IFRSVUUpKSAlPiUKICBhcnJhbmdlKC1tZC5kZWxheSkKYGBgCgpBdm9pZCBmbHlpbmcgdG8gTmV3YXJrLCBOSiwgZXZlbiBhdCA2IG9yIDcgQU0uIE1pZ2h0IHRoZXNlIHZhcnkgYnkgYWlybGluZT8KCmBgYHtyIGx1MTZ9CmNtaC5kZiAlPiUKICBncm91cF9ieShjYXJyaWVyLCBkZXN0X2NpdHlfbmFtZSwgZmx0X2hvdXIpICU+JQogIHN1bW1hcmlzZShtZC5kZWxheSA9IG1lZGlhbihkZXBfZGVsYXksIG5hLnJtID0gVFJVRSkpICU+JQogIGFycmFuZ2UoLW1kLmRlbGF5KQpgYGAKCldvcnN0IGVhcmx5LW1vcm5pbmcgZGVsYXlzIGFyZSBmb3IgRVYsIHRvIE5ld2FyayBhbmQgdG8gQ2hpY2Fnby4gCgojIyBgciBmb250YXdlc29tZTo6ZmEoImZpcmUiLCBmaWxsID0gImRhcmtyZWQiKWAgUGFzc2FnZSBvZiBUaW1lCkxldCB1cyBhc3N1bWUgd2UgYXJlIGludGVyZXN0ZWQgaW4gc2VlaW5nIGhvdyBtdWNoIHRpbWUgbGFwc2VzIGJldHdlZW4gc3VjY2Vzc2l2ZSBmbGlnaHRzIG9mIGVhY2ggYWlyY3JhZnQgc2VlbiBpbiB0aGUgZGF0YS4gV2Uga25vdyB3ZSBjYW4gaWRlbnRpZnkgZWFjaCB1bmlxdWUgYWlyY3JhZnQgYnkgaXRzIGB0YWlsX251bWAuIFNvIGxldCB1cyBmaXJzdCBzZWUgaG93IG1hbnkgdGltZXMgaXMgZWFjaCBhaXJjcmFmdCBzZWVuIGFuZCBjcmVhdGUgYSBuZXcgY29sdW1uIGNhbGxlZCBgbnVtYmVyX2ZsZXdgLiBTb21lIHJvd3Mgb2YgZGF0YSBhcmUgbWlzc2luZyBgZmx0X2RhdGVfdGltZWAgYW5kIGB0YWlsX251bWAgc28gSSB3aWxsIGZpbHRlciB0aGVzZSBvdXQgYXMgd2VsbC4gCgpgYGB7ciBsdTE3fQpjbWguZGYgJT4lCiAgZmlsdGVyKCAjIGVsaW1pbmF0ZXMgYWxsIHJvd3Mgd2hlcmUgYm90aCB0aGVzZSBjb2x1bW5zIGFyZSBibGFuayAKICAgICFpcy5uYSh0YWlsX251bSksCiAgICAhaXMubmEoZmx0X2RhdGVfdGltZSkgCiAgICApICU+JSAKICBncm91cF9ieSh0YWlsX251bSkgJT4lIAogIGFycmFuZ2UoZmx0X2RhdGVfdGltZSkgJT4lICMgZWFjaCBhaXJjcmFmdCBpcyBub3cgc3RhY2tlZCBieSB3aGVuIGl0IGZsZXcKICBtdXRhdGUobl9mbGV3ID0gcm93X251bWJlcigpKSAlPiUgIyBlYWNoIHRpbWUgYWFuIGFpcmNyYWZ0IGlzIHNlZW4gaXQgZ2V0cyBhIG51bWJlciwgMSwgMiwgMywgYW5kIHNvIG9uIC4uLiAKICBzZWxlY3QodGFpbF9udW0sIGZsdF9kYXRlX3RpbWUsIG5fZmxldykgJT4lCiAgYXJyYW5nZSgtbl9mbGV3KSAtPiBjbWguZGYyICMgTjM5NlNXIGlzIHNlZW4gdGhlIG1vc3Qgb2Z0ZW4gaW4gdGhpcyBkYXRhLXNldCAKCmNtaC5kZjIgJT4lCiAgaGVhZCgpCmBgYAoKU28gZmFyIHNvIGdvb2Q7IFtgciBmb250YXdlc29tZTo6ZmEoInBhcGVyLXBsYW5lIiwgZmlsbCA9ICJzYWxtb24iKWAgTjM5NlNXIGlzIHRoZSB3aW5uZXIgYW5kIGhhcyB3ZWxsLWVhcm5lZCBpdHMgcmV0aXJlbWVudF0oaHR0cHM6Ly93d3cucGxhbmVzcG90dGVycy5uZXQvYWlyZnJhbWUvYm9laW5nLTczNy1uMzk2c3ctYWVyb3RocnVzdC1ob2xkaW5ncy8zOGwyZ2UpLiAKCk5vdyB3ZSBuZWVkIHRvIHNlZSBob3cgbXVjaCB0aW1lIGxhcHNlZCBiZXR3ZWVuIGZsaWdodHMsIGFuZCB0aGlzIGlzIGp1c3QgdGhlIGRpZmZlcmVuY2UgYmV0d2VlbiB0aGUgYHByZWNlZGluZyBmbHRfZGF0ZV90aW1lYCByZWNvcmRlZCBhbmQgdGhlIG1vc3QgcmVjZW50IGBmbHRfZGF0ZV90aW1lYC4gQXMgd2UgZG8gdGhpcywgbm90ZSB0aGF0IGJ5IGRlZmF1bHQgdGltZSBzcGFuIChgeXRzcGFuYCkgaXMgY2FsY3VsYXRlZCBpbiBzZWNvbmRzLiAgCgpgYGB7ciBsdTE4fQpjbWguZGYyICU+JQogIGdyb3VwX2J5KHRhaWxfbnVtKSAlPiUKICBhcnJhbmdlKGZsdF9kYXRlX3RpbWUpICU+JQogIG11dGF0ZSgKICAgIHRzcGFuID0gaW50ZXJ2YWwoCiAgICAgIGxhZyhmbHRfZGF0ZV90aW1lLCBvcmRlcl9ieSA9IHRhaWxfbnVtKSwgZmx0X2RhdGVfdGltZQogICAgICApLCAjIGNhbGN1bGF0ZSB0aGUgdGltZSBzcGFuIGJldHdlZW4gc3VjY2Vzc2l2ZSBmbGlnaHRzIHJlY29yZGVkIAogICAgdHNwYW4ubWludXRlcyA9IGFzLmR1cmF0aW9uKHRzcGFuKS9kbWludXRlcygxKSwgIyBjb252ZXJ0IHRzcGFuIGludG8gbWludXRlcyAKICAgIHRzcGFuLmhvdXJzID0gYXMuZHVyYXRpb24odHNwYW4pL2Rob3VycygxKSwgIyBjb252ZXJ0IHRzcGFuIGludG8gaG91cnMgCiAgICB0c3Bhbi5kYXlzID0gYXMuZHVyYXRpb24odHNwYW4pL2RkYXlzKDEpLCAjIGNvbnZlcnQgdHNwYW4gaW50byBkYXlzIAogICAgdHNwYW4ud2Vla3MgPSBhcy5kdXJhdGlvbih0c3BhbikvZHdlZWtzKDEpICMgY29udmVydCB0c3BhbiBpbnRvIHdlZWtzIAogICAgKSAtPiBjbWguZGYyIAoKY21oLmRmMiAlPiUKICBmaWx0ZXIodGFpbF9udW0gPT0gIk4zOTZTVyIpCmBgYAoKSGVyZSwgYHRzcGFuYCBpcyBiZWluZyBjb252ZXJ0ZWQgaW50bywgc2F5LCBtaW51dGVzIGJ5IGRpdmlkaW5nIGl0IGJ5IDYwLCBpbnRvIGhvdXJzIGJ5IGRpdmlkaW5nIHRzcGFuIGJ5IDYwIHggNjAgPSAzNjAwLCBhbmQgc28gb24uIE5vdGUgdGhhdCBgZG1pbnV0ZXMoMSlgIGlzIGNhbGN1bGF0aW5nIHRoZSB0aW1lIHNwYW4gaW4gb25lLW1pbnV0ZSBpbnRlcnZhbHMuIFNpbWlsYXJseSBmb3IgaG91cnMsIGRheXMsIGFuZCB3ZWVrcy4gVGh1cyBpZiB5b3UgcmFuIGBkaG91cnMoMilgIHlvdSB3b3VsZCBnZXQgdGhlIHRpbWUgaW50ZXJ2YWwgaW4gMi1ob3VyIGluY3JlbWVudHMuICAKClRoZXJlIGlzIGEgbG90IG1vcmUgd2UgY291bGQgZG8gd2l0aCB0aW1lIGJ1dCB0aGUgZmV3IHRoaW5ncyB3ZSBoYXZlIGNvdmVyZWQgc28gZmFyIHdvdWxkIGJlIHRoZSBtb3JlIGNvbW1vbiB0YXNrcyB3ZSB1c3VhbGx5IGVuY291bnRlci4gICAKCiMgUHJhY3RpY2UgRXhlcmNpc2VzCgojIyBFeGVyY2lzZSAxIAoKVGhlIGRhdGEgYmVsb3cgY29tZSBmcm9tIFt0aWR5dHVlc2RheV0oaHR0cHM6Ly9naXRodWIuY29tL3Jmb3JkYXRhc2NpZW5jZS90aWR5dHVlc2RheS90cmVlL21hc3Rlci9kYXRhLzIwMTkvMjAxOS0wOS0xMCkgYW5kIHByb3ZpZGUgaW5mb3JtYXRpb24gb24gYWNjaWRlbnRzIGF0IHRoZW1lIHBhcmtzLiBZb3UgY2FuIHNlZSBtb3JlIG9mIHRoZXNlIFtkYXRhIGF2YWlsYWJsZSBoZXJlXShodHRwczovL3JpZGVzZGF0YWJhc2Uub3JnL3NhZmVycGFya3MvZGF0YS8pLiBUaGUgZGF0YSBnaXZlIHlvdSBzb21lIGRldGFpbHMgb2Ygd2hlcmUgYW5kIHdoZW4gdGhlIGFjY2lkZW50IG9jY3VycmVkLCBhbmQgc29tZXRoaW5nIGFib3V0IHRoZSBpbmp1cmVkIHBhcnR5IGFzIHdlbGwuIAoKYGBge3IgZXgwMX0Kc2FmZXJfcGFya3MgPC0gcmVhZHI6OnJlYWRfY3N2KCJodHRwczovL3Jhdy5naXRodWJ1c2VyY29udGVudC5jb20vcmZvcmRhdGFzY2llbmNlL3RpZHl0dWVzZGF5L21hc3Rlci9kYXRhLzIwMTkvMjAxOS0wOS0xMC9zYWZlcnBhcmtzLmNzdiIpCmBgYAoKYHNhZmVyX3BhcmtzLmNzdmAgey19Cgp8dmFyaWFibGUgICAgICAgICAgICAgfGNsYXNzICAgICB8ZGVzY3JpcHRpb24gfAp8Oi0tLS0tLS0tLS0tLS0tLS0tLS0tfDotLS0tLS0tLS18Oi0tLS0tLS0tLS0tfAp8YWNjX2lkICAgICAgICAgICAgICAgfGRvdWJsZSAgICB8IFVuaXF1ZSBJRCB8CnxhY2NfZGF0ZSAgICAgICAgICAgICB8Y2hhcmFjdGVyIHwgQWNjaWRlbnQgRGF0ZSB8CnxhY2Nfc3RhdGUgICAgICAgICAgICB8Y2hhcmFjdGVyIHwgQWNjaWRlbnQgU3RhdGUgfAp8YWNjX2NpdHkgICAgICAgICAgICAgfGNoYXJhY3RlciB8IEFjY2lkZW50IENpdHkgfAp8Zml4X3BvcnQgICAgICAgICAgICAgfGNoYXJhY3RlciB8LiAgICAgICAgICAgfAp8c291cmNlICAgICAgICAgICAgICAgfGNoYXJhY3RlciB8IFNvdXJjZSBvZiBpbmp1cnkgcmVwb3J0IHwKfGJ1c190eXBlICAgICAgICAgICAgIHxjaGFyYWN0ZXIgfCBCdXNpbmVzcyB0eXBlIHwKfGluZHVzdHJ5X3NlY3RvciAgICAgIHxjaGFyYWN0ZXIgfCBJbmR1c3RyeSBzZWN0b3IgfAp8ZGV2aWNlX2NhdGVnb3J5ICAgICAgfGNoYXJhY3RlciB8IERldmljZSBjYXRlZ29yeSB8CnxkZXZpY2VfdHlwZSAgICAgICAgICB8Y2hhcmFjdGVyIHwgRGV2aWNlIHR5cGUgfAp8dHJhZGVuYW1lX29yX2dlbmVyaWMgfGNoYXJhY3RlciB8IENvbW1vbiBuYW1lIG9mIHRoZSBkZXZpY2UgfAp8bWFudWZhY3R1cmVyICAgICAgICAgfGNoYXJhY3RlciB8IE1hbnVmYWN0dXJlciBvZiBkZXZpY2UgfAp8bnVtX2luanVyZWQgICAgICAgICAgfGRvdWJsZSAgICB8IE51bSBpbmp1cmVkIHwKfGFnZV95b3VuZ2VzdCAgICAgICAgIHxkb3VibGUgICAgfCBZb3VuZ2VzdCBpbmRpdmlkdWFsIGluanVyZWQgfAp8Z2VuZGVyICAgICAgICAgICAgICAgfGNoYXJhY3RlciB8IEdlbmRlciBvZiBpbmRpdmlkdWFsIGluanVyZWQgfAp8YWNjX2Rlc2MgICAgICAgICAgICAgfGNoYXJhY3RlciB8IERlc2NyaXB0aW9uIG9mIGFjY2lkZW50IHwKfGluanVyeV9kZXNjICAgICAgICAgIHxjaGFyYWN0ZXIgfCBJbmp1cnkgZGVzY3JpcHRpb24gfAp8cmVwb3J0ICAgICAgICAgICAgICAgfGNoYXJhY3RlciB8IFJlcG9ydCBVUkwgfAp8Y2F0ZWdvcnkgICAgICAgICAgICAgfGNoYXJhY3RlciB8IENhdGVnb3J5IG9mIGFjY2lkZW50IHwKfG1lY2hhbmljYWwgICAgICAgICAgIHxkb3VibGUgICAgfCBNZWNoYW5pY2FsIGZhaWx1cmUgKGJpbmFyeSBOQS8xKSB8CnxvcF9lcnJvciAgICAgICAgICAgICB8ZG91YmxlICAgIHwgT3BlcmF0b3IgZXJyb3IgKGJpbmFyeSBOQS8xKXwKfGVtcGxveWVlICAgICAgICAgICAgIHxkb3VibGUgICAgfCBFbXBsb3llZSBlcnJvciAoYmluYXJ5IE5BLzEpfAp8bm90ZXMgICAgICAgICAgICAgICAgfGNoYXJhY3RlciB8IEFkZGl0aW9uYWwgbm90ZXN8IAoKV29ya2luZyB3aXRoIHRoZSBgc2FmZXJfcGFya3NgIGRhdGEsIGNvbXBsZXRlIHRoZSBmb2xsb3dpbmcgdGFza3MuIAoKIyMjIFByb2JsZW0gKGEpIHstfQpVc2luZyBgYWNjX2RhdGVgLCBjcmVhdGUgYSBuZXcgZGF0ZSB2YXJpYWJsZSBjYWxsZWQgYGlkYXRlYCB0aGF0IGlzIGEgcHJvcGVyIGRhdGUgY29sdW1uIGdlbmVyYXRlZCB2aWEge2x1YnJpZGF0ZX0uIAoKYGBge3J9CgpgYGAKCiMjIyBQcm9ibGVtIChiKSB7LX0KTm93IGNyZWF0ZSBuZXcgY29sdW1ucyBmb3IgKGkpIHRoZSBtb250aCBvZiB0aGUgYWNjaWRlbnQsIGFuZCAoaWkpIHRoZSBkYXkgb2YgdGhlIHdlZWsuIFRoZXNlIHNob3VsZCBub3QgYmUgYWJicmV2aWF0ZWQgKGkuZS4sIHdlIHNob3VsZCBzZWUgdGhlIHZhbHVlcyBhcyAnTW9uZGF5JyBpbnN0ZWFkIG9mICdNb24nLCAiSnVseSIgaW5zdGVhZCBvZiAiSnVsIikuIFdoYXQgbW9udGggaGFkIHRoZSBoaWdoZXN0IG51bWJlciBvZiBhY2NpZGVudHM/IFdoYXQgZGF5IG9mIHRoZSB3ZWVrIGhhZCB0aGUgaGlnaGVzdCBudW1iZXIgb2YgYWNjaWRlbnRzPyAKCmBgYHtyfQoKYGBgCgojIyMgUHJvYmxlbSAoYykgey19CldoYXQgaWYgeW91IGxvb2sgYXQgZGF5cyBvZiB0aGUgd2VlayBieSBtb250aD8gRG9lcyB0aGUgc2FtZSBkYXkgb2YgdGhlIHdlZWsgc2hvdyB1cCB3aXRoIHRoZSBtb3N0IGFjY2lkZW50cyByZWdhcmRsZXNzIG9mIG1vbnRoIG9yIGRvIHdlIHNlZSBzb21lIHZhcmlhdGlvbj8gCgpgYGB7cn0KCmBgYAoKIyMjIFByb2JsZW0gKGQpIHstfQpXaGF0IHdlcmUgdGhlIGBmaXZlYCBkYXRlcyB3aXRoIHRoZSBtb3N0IG51bWJlciBvZiBhY2NpZGVudHM/IAoKYGBge3J9CgpgYGAKCiMjIyBgciBmb250YXdlc29tZTo6ZmEoImZpcmUiLCBmaWxsID0gImRhcmtyZWQiKWAgUHJvYmxlbSAoZSkgey19ClVzaW5nIHRoZSBUZXhhcyBpbmp1cnkgZGF0YSwgYW5zd2VyIHRoZSBmb2xsb3dpbmcgcXVlc3Rpb246IFdoYXQgcmlkZSB3YXMgdGhlIHNhZmVzdD8gW0hpbnQ6IEZvciBlYWNoIHJpZGUgKGByaWRlX25hbWVgKSB5b3Ugd2lsbCBuZWVkIHRvIGNhbGN1bGF0ZSB0aGUgbnVtYmVyIG9mIGRheXMgYmV0d2VlbiBhY2NpZGVudHMuIFRoZSByaWRlIHdpdGggdGhlIGhpZ2hlc3QgbnVtYmVyIG9mIGRheXMgaXMgdGhlIHNhZmVzdC5dIAoKYGBge3IgdHhpbmp1cnl9CnJlYWRfY3N2KAogICJodHRwczovL3Jhdy5naXRodWJ1c2VyY29udGVudC5jb20vcmZvcmRhdGFzY2llbmNlL3RpZHl0dWVzZGF5L21hc3Rlci9kYXRhLzIwMTkvMjAxOS0wOS0xMC90eF9pbmp1cmllcy5jc3YiCiAgKSAtPiB0eF9pbmp1cmllcwpgYGAKCmB0eF9pbmp1cmllcy5jc3ZgCgp8dmFyaWFibGUgICAgICAgICAgfGNsYXNzICAgICB8ZGVzY3JpcHRpb24gfAp8Oi0tLS0tLS0tLS0tLS0tLS0tfDotLS0tLS0tLS18Oi0tLS0tLS0tLS0tfAp8aW5qdXJ5X3JlcG9ydF9yZWMgfGRvdWJsZSAgICB8IFVuaXF1ZSBSZWNvcmQgSUQgfAp8bmFtZV9vZl9vcGVyYXRpb24gfGNoYXJhY3RlciB8IENvbXBhbnkgbmFtZSB8CnxjaXR5ICAgICAgICAgICAgICB8Y2hhcmFjdGVyIHwgQ2l0eSB8CnxzdCAgICAgICAgICAgICAgICB8Y2hhcmFjdGVyIHwgU3RhdGUgKGFsbCBUWCkgfAp8aW5qdXJ5X2RhdGUgICAgICAgfGNoYXJhY3RlciB8IEluanVyeSBkYXRlIC0gbm90ZSB0aGVyZSBhcmUgc29tZSBkaWZmZXJlbnQgZm9ybWF0cyB8CnxyaWRlX25hbWUgICAgICAgICB8Y2hhcmFjdGVyIHwgUmlkZSBOYW1lIHwKfHNlcmlhbF9ubyAgICAgICAgIHxjaGFyYWN0ZXIgfCBTZXJpYWwgbnVtYmVyIG9mIHJpZGUgfAp8Z2VuZGVyICAgICAgICAgICAgfGNoYXJhY3RlciB8IEdlbmRlciBvZiB0aGUgaW5qdXJlZCBpbmRpdmlkdWFsIHwKfGFnZSAgICAgICAgICAgICAgIHxjaGFyYWN0ZXIgfCBBZ2Ugb2YgdGhlIGluanVyZWQgaW5kaXZpZHVhbCB8Cnxib2R5X3BhcnQgICAgICAgICB8Y2hhcmFjdGVyIHwgQm9keSBwYXJ0IGluanVyZWQgfAp8YWxsZWdlZF9pbmp1cnkgICAgfGNoYXJhY3RlciB8IEFsbGVnZWQgaW5qdXJ5IC0gdHlwZSBvZiBpbmp1cnkgfAp8Y2F1c2Vfb2ZfaW5qdXJ5ICAgfGNoYXJhY3RlciB8IEFwcHJveGltYXRlIGNhdXNlIG9mIHRoZSBpbmp1cnkgKGZyZWUgdGV4dCkgfAp8b3RoZXIgICAgICAgICAgICAgfGNoYXJhY3RlciB8IEFuZWNkb3RhbCBpbmZvcm1hdGlvbiBpbiBhZGRpdGlvbiB0byBjYXVzZSBvZiBpbmp1cnkgfAoKYGBge3J9CgpgYGAKCllvdSBzaG91bGQgbm90ZSB0aGF0IHRoaXMgYXNzdW1lcyBlYWNoIHJpZGUgd2FzIGluIG9wZXJhdGlvbiBmb3IgdGhlIHNhbWUgYW1vdW50IG9mIHRpbWUuIElmIHRoaXMgaXMgbm90IHRydWUgdGhlbiBvdXIgZXN0aW1hdGVzIHdpbGwgYmUgdW5yZWxpYWJsZS4gCgojIyBFeGVyY2lzZSAyClRoZXNlIGRhdGEgKHNlZSBiZWxvdykgY29tZSBmcm9tIHRoaXMgc3Rvcnk6IFtUaGUgbmV4dCBnZW5lcmF0aW9uOiBUaGUgc3BhY2UgcmFjZSBpcyBkb21pbmF0ZWQgYnkgbmV3IGNvbnRlbmRlcnNdKGh0dHBzOi8vd3d3LmVjb25vbWlzdC5jb20vZ3JhcGhpYy1kZXRhaWwvMjAxOC8xMC8xOC90aGUtc3BhY2UtcmFjZS1pcy1kb21pbmF0ZWQtYnktbmV3LWNvbnRlbmRlcnMpLiBZb3UgaGF2ZSBkYXRhIG9uIHNwYWNlIG1pc3Npb25zIG92ZXIgdGltZSwgd2l0aCBkYXRlcyBvZiB0aGUgbGF1bmNoLCB0aGUgbGF1bmNoaW5nIGFnZW5jeS9jb3VudHJ5LCB0eXBlIG9mIGxhdW5jaCB2ZWhpY2xlLCBhbmQgc28gb24uIAoKIyMjIyBgbGF1bmNoZXNgIHstfQoKfCB2YXJpYWJsZSAgICB8IGRlZmluaXRpb24gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfAp8IC0tLS0tLS0tLS0tIHwgLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLSB8CnwgdGFnICAgICAgICAgfCBIYXJ2YXJkIG9yIFtDT1NQQVJdW2Nvc3Bhcl0gaWQgb2YgbGF1bmNoIHwKfCBKRCAgICAgICAgICB8IFtKdWxpYW4gRGF0ZV1bamRdIG9mIGxhdW5jaCAgICAgICAgICAgICAgfAp8IGxhdW5jaF9kYXRlIHwgZGF0ZSBvZiBsYXVuY2ggICAgICAgICAgICAgICAgICAgICAgICAgICB8CnwgbGF1bmNoX3llYXIgfCB5ZWFyIG9mIGxhdW5jaCAgICAgICAgICAgICAgICAgICAgICAgICAgIHwKfCB0eXBlICAgICAgICB8IHR5cGUgb2YgbGF1bmNoIHZlaGljbGUgICAgICAgICAgICAgICAgICAgfAp8IHZhcmlhbnQgICAgIHwgdmFyaWFudCBvZiBsYXVuY2ggdmVoaWNsZSAgICAgICAgICAgICAgICB8CnwgbWlzc2lvbiAgICAgfCBzcGFjZSBtaXNzaW9uICAgICAgICAgICAgICAgICAgICAgICAgICAgIHwKfCBhZ2VuY3kgICAgICB8IGxhdW5jaGluZyBhZ2VuY3kgICAgICAgICAgICAgICAgICAgICAgICAgfAp8IHN0YXRlX2NvZGUgIHwgbGF1bmNoaW5nIGFnZW5jeSdzIHN0YXRlICAgICAgICAgICAgICAgICB8CnwgY2F0ZWdvcnkgICAgfCBzdWNjZXNzIChPKSBvciBmYWlsdXJlIChGKSAgICAgICAgICAgICAgIHwKfCBhZ2VuY3lfdHlwZSB8IHR5cGUgb2YgYWdlbmN5ICAgICAgICAgICAgICAgICAgICAgICAgICAgfAoKCmBgYHtyfQpyZWFkX2NzdigKICAiaHR0cHM6Ly9yYXcuZ2l0aHVidXNlcmNvbnRlbnQuY29tL3Jmb3JkYXRhc2NpZW5jZS90aWR5dHVlc2RheS9tYXN0ZXIvZGF0YS8yMDE5LzIwMTktMDEtMTUvbGF1bmNoZXMuY3N2IgogICkgLT4gbGF1bmNoZXMKYGBgCgojIyMgUHJvYmxlbSAoYSkgey19CkNyZWF0ZSBhIG5ldyBjb2x1bW4gY2FsbGVkIGBkYXRlYCB0aGF0IHN0b3JlcyBgbGF1bmNoX2RhdGVgIGFzIGEgcHJvcGVyIGRhdGEgZmllbGQgaW4geW1kIGZvcm1hdCBmcm9tIHtsdWJyaWRhdGV9LiAKCmBgYHtyfQoKYGBgCgojIyMgUHJvYmxlbSAoYikgey19CkNyZWF0aW5nIGNvbHVtbnMgYXMgbmVlZGVkLCBjYWxjdWxhdGUgYW5kIHNob3cgdGhlIG51bWJlciBvZiBsYXVuY2hlcyBmaXJzdCBieSB5ZWFyLCB0aGVuIGJ5IG1vbnRoLCBhbmQgdGhlbiBieSBkYXkgb2YgdGhlIHdlZWsuIFRoZSByZXN1bHQgc2hvdWxkIGJlIGFycmFuZ2VkIGluIGRlc2NlbmRpbmcgb3JkZXIgb2YgdGhlIG51bWJlciBvZiBsYXVuY2hlcy4gCgpgYGB7cn0KCmBgYAoKCgojIyMgUHJvYmxlbSAoYykgey19CkhvdyBtYW55IGxhdW5jaGVzIHdlcmUgc3VjY2Vzc2Z1bCBgKE8pYCB2ZXJzdXMgZmFpbGVkIGAoRilgIGJ5IGNvdW50cnkgYW5kIHllYXI/IFRoZSBjb3VudHJpZXMgb2YgaW50ZXJlc3Qgd2lsbCBiZSBzdGF0ZV9jb2RlIHZhbHVlcyBvZiAiQ04iLCAiRiIsICJKIiwgIlJVIiwgIlNVIiwgIlVTIi4gWW91IGRvIG5vdCBuZWVkIHRvIGFycmFuZ2UgeW91ciByZXN1bHRzIGluIGFueSBvcmRlci4gCgpgYGB7cn0KCmBgYAo=