Tibbles in R

A tibble, or tbl_df, is a modern reimagining of the data.frame, keeping what time has proven to be effective, and throwing out what is not.

In this work we are going to see tibble-s. For more information on this new version of data.frame please see:

https://r4ds.had.co.nz/tibbles.html https://tibble.tidyverse.org/

library(tidyverse)
head(iris,10)
as_tibble(iris)
x.tbl<-tibble(
  x1 = seq(1,10), 
  x2 = seq(11,20), 
  x3 = x1 ^ 2 + x2*0.5
)
x.tbl

Working with dates and times

Libraries we will need are:

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

To get the current date or date-time

For this information you can use function: today() or now():

today()
[1] "2021-01-07"
now()
[1] "2021-01-07 18:35:48 CET"

Otherwise, there are three ways you may use to create a date/time: •From a string. •From individual date-time components. •From an existing date/time object.

Create date/time from strings

Use library(lubridate), to identify the order in which year, month, and day appear in your dates, then arrange “y”, “m”, and “d” in the same order. That gives you the name of the lubridate function that will parse your date.

Example 1

ymd("2020-12-31")
[1] "2020-12-31"
mdy("December 31st, 2020")
[1] "2020-12-31"
dmy("1-Jan-2020")
[1] "2020-01-01"

Greenwich Mean Time (GMT) is often interchanged or confused with Coordinated Universal Time (UTC) which is a time standard that is the basis for civil time and time zones worldwide. Adding information about hour, minute and second use :“h”, “m”, and “s” to the function. Example: ymd_hms()

# will give as output the date and the hour/min/sec
ymd_hms("2020-12-31 23:58:59") 
[1] "2020-12-31 23:58:59 UTC"
# will give as output the date and the hour/min
mdy_hm("12/31/2020 11:58") 
[1] "2020-12-31 11:58:00 UTC"
# You can also force the creation of a date-time from a date by supplying a timezone:
ymd(20201231, tz = "UTC")
[1] "2020-12-31 UTC"

Import a dataframe of Covid new cases and deaths per day published by:

Ref:https://www.ecdc.europa.eu/en/publications-data/data-national-14-day-notification-rate-covid-19

covid.data <- read.csv("C:/Users/user/Desktop/Covid data-12 october 2020.csv")
View(covid.data)
head(covid.data,10)

To create a date/time from this sort of input, use make_date() for dates, or make_datetime() for date-times: the function mutate() adds a variable to your dataframe.

covid.data[,2:4] %>% mutate(information_time = make_datetime(year, month, day))

You may want to switch between a date-time and a date. That’s the job of as_datetime() and as_date():

today()
[1] "2021-01-07"
as_datetime(today())
[1] "2021-01-07 UTC"
now()
[1] "2021-01-07 18:48:18 CET"
as_date(now())
[1] "2021-01-07"

Applying in the Covid dataset:

head(covid.data[,1],10)
 [1] "2020-10-13T00:00:00Z" "2020-10-12T00:00:00Z" "2020-10-11T00:00:00Z"
 [4] "2020-10-10T00:00:00Z" "2020-10-09T00:00:00Z" "2020-10-08T00:00:00Z"
 [7] "2020-10-07T00:00:00Z" "2020-10-06T00:00:00Z" "2020-10-05T00:00:00Z"
[10] "2020-10-04T00:00:00Z"
head(as_datetime(covid.data[,1]),10)
 [1] "2020-10-13 UTC" "2020-10-12 UTC" "2020-10-11 UTC" "2020-10-10 UTC"
 [5] "2020-10-09 UTC" "2020-10-08 UTC" "2020-10-07 UTC" "2020-10-06 UTC"
 [9] "2020-10-05 UTC" "2020-10-04 UTC"
head(as_date(covid.data[,1]),10)
 [1] "2020-10-13" "2020-10-12" "2020-10-11" "2020-10-10" "2020-10-09"
 [6] "2020-10-08" "2020-10-07" "2020-10-06" "2020-10-05" "2020-10-04"

Example Use the above function to parse the data below:

d1 <- "January 1, 2020"
d1
[1] "January 1, 2020"
d1.t<-mdy(d1)
d1.t
[1] "2020-01-01"
d2 <- "2020-Mar-14"
ymd(d2)
[1] "2020-03-14"
d3 <- "06-Jun-2013"
dmy(d3)
[1] "2013-06-06"
d4 <- c("August 6 (2013)", "July 10 (2016)")
mdy(d4)
[1] "2013-08-06" "2016-07-10"
d5 <- "12/31/20" # Dec 31, 2020
mdy(d5)
[1] "2020-12-31"

Getting components

We can pull out individual parts of the date with the accessor functions: year(), month(), mday() (day of the month), yday() (day of the year), wday() (day of the week), hour(), minute(), second()

Let use some of these functions at the covid dataset. We have used the head() function to display just a few of the data.

head(covid.data[,1],10)
 [1] "2020-10-13T00:00:00Z" "2020-10-12T00:00:00Z" "2020-10-11T00:00:00Z"
 [4] "2020-10-10T00:00:00Z" "2020-10-09T00:00:00Z" "2020-10-08T00:00:00Z"
 [7] "2020-10-07T00:00:00Z" "2020-10-06T00:00:00Z" "2020-10-05T00:00:00Z"
[10] "2020-10-04T00:00:00Z"
head(year(covid.data[,1]),10)
 [1] 2020 2020 2020 2020 2020 2020 2020 2020 2020 2020
head(month(covid.data[,1]),10)
 [1] 10 10 10 10 10 10 10 10 10 10
head(day(covid.data[,1]),10)
 [1] 13 12 11 10  9  8  7  6  5  4

For month() and wday() you can set argument label = TRUE to return the abbreviated name of the month or day of the week. Set argument abbr = FALSE to return the full name. (Keep in mind that we can use TRUE or T, and FALSE or F)

head(month(covid.data[,1],label=TRUE),10)
 [1] Oct Oct Oct Oct Oct Oct Oct Oct Oct Oct
12 Levels: Jan < Feb < Mar < Apr < May < Jun < Jul < Aug < Sep < ... < Dec
head(month(covid.data[,1],label=TRUE,abbr=F),10)
 [1] October October October October October October October October October
[10] October
12 Levels: January < February < March < April < May < June < ... < December
head(wday(covid.data[,1],label=TRUE),10)
 [1] Tue Mon Sun Sat Fri Thu Wed Tue Mon Sun
Levels: Sun < Mon < Tue < Wed < Thu < Fri < Sat
head(wday(covid.data[,1],label=T,abbr=F),10)
 [1] Tuesday   Monday    Sunday    Saturday  Friday    Thursday  Wednesday
 [8] Tuesday   Monday    Sunday   
7 Levels: Sunday < Monday < Tuesday < Wednesday < Thursday < ... < Saturday

Rounding

An alternative approach to plotting individual components is to round the date to a nearby unit of time, with: floor_date() round_date() and ceiling_date().

round_date() takes a date-time object and time unit, and rounds it to the nearest value of the specified time unit.

floor_date() takes a date-time object and rounds it down to the nearest boundary of the specified time unit.

ceiling_date() takes a date-time object and rounds it up to the nearest boundary of the specified time unit.

Each function takes a vector of dates to adjust and then the name of the unit round down (floor), round up (ceiling), or round to.

Round_date

x <- ymd_hms("2021-01-06 12:01:58.36")
x
[1] "2021-01-06 12:01:58 UTC"
round_date(x, ".5s")
[1] "2021-01-06 12:01:58 UTC"
round_date(x, "sec")
[1] "2021-01-06 12:01:58 UTC"
round_date(x, "second")
[1] "2021-01-06 12:01:58 UTC"
round_date(x, "minute")
[1] "2021-01-06 12:02:00 UTC"
round_date(x, "10 mins")
[1] "2021-01-06 12:00:00 UTC"
round_date(x, "hour")
[1] "2021-01-06 12:00:00 UTC"
round_date(x, "2 hours")
[1] "2021-01-06 12:00:00 UTC"
round_date(x, "5 hours")
[1] "2021-01-06 10:00:00 UTC"
round_date(x, "day")
[1] "2021-01-07 UTC"
round_date(x, "week")
[1] "2021-01-10 UTC"
round_date(x, "month")
[1] "2021-01-01 UTC"
round_date(x, "bimonth")
[1] "2021-01-01 UTC"
round_date(x, "quarter") # same as: round_date(x, "3 months")
[1] "2021-01-01 UTC"
round_date(x, "halfyear")
[1] "2021-01-01 UTC"
round_date(x, "year")
[1] "2021-01-01 UTC"

floor_date

x <- ymd_hms("2021-05-06 12:01:58.36")
x
[1] "2021-05-06 12:01:58 UTC"
floor_date(x, ".1s")
[1] "2021-05-06 12:01:58 UTC"
floor_date(x, "second")
[1] "2021-05-06 12:01:58 UTC"
floor_date(x, " 5 minutes")
[1] "2021-05-06 12:00:00 UTC"
floor_date(x, "hour")
[1] "2021-05-06 12:00:00 UTC"
floor_date(x, "day")
[1] "2021-05-06 UTC"
floor_date(x, "week")
[1] "2021-05-02 UTC"
floor_date(x, "month")
[1] "2021-05-01 UTC"
floor_date(x, "bimonth")
[1] "2021-05-01 UTC"
floor_date(x, "quarter")
[1] "2021-04-01 UTC"
floor_date(x, "season")
[1] "2021-03-01 UTC"
floor_date(x, "halfyear")
[1] "2021-01-01 UTC"
floor_date(x, "year")
[1] "2021-01-01 UTC"

ceiling_date

x <- ymd_hms("2021-01-06 12:01:58.36")
x
[1] "2021-01-06 12:01:58 UTC"
ceiling_date(x, ".2 sec")
[1] "2021-01-06 12:01:58 UTC"
ceiling_date(x, "second")
[1] "2021-01-06 12:01:59 UTC"
ceiling_date(x, "minute")
[1] "2021-01-06 12:02:00 UTC"
ceiling_date(x, "5 mins")
[1] "2021-01-06 12:05:00 UTC"
ceiling_date(x, "hour")
[1] "2021-01-06 13:00:00 UTC"
ceiling_date(x, "day")
[1] "2021-01-07 UTC"
ceiling_date(x, "week")
[1] "2021-01-10 UTC"
ceiling_date(x, "month")
[1] "2021-02-01 UTC"
ceiling_date(x, "bimonth") # same as ceiling_date(x, "2 months")
[1] "2021-03-01 UTC"
ceiling_date(x, "quarter")
[1] "2021-04-01 UTC"
ceiling_date(x, "season")
[1] "2021-03-01 UTC"
ceiling_date(x, "halfyear")
[1] "2021-07-01 UTC"
ceiling_date(x, "year")
[1] "2022-01-01 UTC"

Setting components

We can use each accessor function to set the components of a date/time. Let’s suppose we need to correct some values in: year, month, day, hour, minute, second and time zone as well.

d.time_2 <- ymd_hms("2021-01-08 16:45:52")
d.time_2
[1] "2021-01-08 16:45:52 UTC"
year(d.time_2) <- 2022 # correct the year from 2021 to 2022
d.time_2
[1] "2022-01-08 16:45:52 UTC"
month(d.time_2) <- 03 # correct the month from 01 (january) to 03 (march)
d.time_2
[1] "2022-03-08 16:45:52 UTC"
mday(d.time_2) <- 13 # correct the date from 08 to 13
d.time_2
[1] "2022-03-13 16:45:52 UTC"
hour(d.time_2)<-06 # correct the hour
d.time_2
[1] "2022-03-13 06:45:52 UTC"
minute(d.time_2)<-05 # correct the minute
d.time_2
[1] "2022-03-13 06:05:52 UTC"
second(d.time_2)<-25 # correct the second
d.time_2
[1] "2022-03-13 06:05:25 UTC"

Otherwise we can use update() function to correct the above details.

update(d.time_2, year =2022 , month =03 , mday =13 , hour =06,minute=05, second=25 )
[1] "2022-03-13 06:05:25 UTC"
update(d.time_2, year =2025 , mday =15 )
[1] "2025-03-15 06:05:25 UTC"

Time spans

Have you thought how arithmetic with dates works, including subtraction, addition, and division. These are three important classes that represent time spans:

-> durations, which represent an exact number of seconds. -> periods, which represent human units like weeks and months. -> intervals, which represent a starting and ending point.

durations

Example How old am I?

eralda.age <- today() - ymd(19830114) # year 1983, month 01, date 14
eralda.age
Time difference of 13873 days
print(paste (" Eralda age is",eralda.age,"days"))
[1] " Eralda age is 13873 days"
eralda.years<-as.duration(eralda.age)
eralda.years
[1] "1198627200s (~37.98 years)"
print(paste (" Eralda age is",eralda.years))
[1] " Eralda age is 1198627200s (~37.98 years)"

dfunction(value) is another used function which takes a value x (which can be in units of second, minutes, hours, days, weeks, years) and returns it’s duration in seconds. Example

dseconds(10)# duration in seconds of 10 sec
[1] "10s"
dminutes(5)# duration in seconds of 5 minutes
[1] "300s (~5 minutes)"
dhours(c(2, 10))# duration in seconds of 2 hours and 10 hours
[1] "7200s (~2 hours)"   "36000s (~10 hours)"
ddays(0:7)# duration in seconds of 0 days, 1 day, 2 days up to 7 days
[1] "0s"                 "86400s (~1 days)"   "172800s (~2 days)" 
[4] "259200s (~3 days)"  "345600s (~4 days)"  "432000s (~5 days)" 
[7] "518400s (~6 days)"  "604800s (~1 weeks)"
dweeks(2)# duration in seconds of 2 weeks
[1] "1209600s (~2 weeks)"
dyears(c(1,2))#duration in seconds of 1 year and 2 years
[1] "31557600s (~1 years)" "63115200s (~2 years)"

You can add and multiply durations:

10 * dyears(1)
[1] "315576000s (~10 years)"
dyears(10)
[1] "315576000s (~10 years)"
dyears(1) + dweeks(12) + dhours(15)
[1] "38869200s (~1.23 years)"

You can add and subtract durations to and from days:

tomorrow <- today() + ddays(1)
tomorrow
[1] "2021-01-08"
dbefore.2<-today() + ddays(-2)
dbefore.2
[1] "2021-01-05"
two_year_before <- today() - dyears(2)
two_year_before
[1] "2019-01-07 12:00:00 UTC"

Periods

Periods are time spans but don’t have a fixed length in seconds, instead they work with “human” times, like days and months. That allows them to work in a more intuitive way. So you can use: • seconds() • minutes() • hours() • days() • months() • weeks() • years()

new.year <- ymd_hms("2021-01-01 00:00:01")
new.year
[1] "2021-01-01 00:00:01 UTC"
new.year + days(1)
[1] "2021-01-02 00:00:01 UTC"
new.year + days(-1)
[1] "2020-12-31 00:00:01 UTC"
new.year + days(31)
[1] "2021-02-01 00:00:01 UTC"
new.year + days(31)+ hours(12)
[1] "2021-02-01 12:00:01 UTC"
new.year + days(31)+ hours(12)+minutes(15)
[1] "2021-02-01 12:15:01 UTC"
new.year + days(31)+ hours(12)+minutes(15)+seconds(25)
[1] "2021-02-01 12:15:26 UTC"

You can add and multiply periods:

10 * (months(2) + days(1))
[1] "20m 10d 0H 0M 0S"
days(61) + hours(25) + minutes(5)
[1] "61d 25H 5M 0S"
dyears(1)
[1] "31557600s (~1 years)"
ymd("2020-01-01") + dyears(1)
[1] "2020-12-31 06:00:00 UTC"
ymd("2020-01-01") + years(1)
[1] "2021-01-01"
new.year + ddays(1)
[1] "2021-01-02 00:00:01 UTC"
new.year + days(1)
[1] "2021-01-02 00:00:01 UTC"

Introducing as_period()

Often with time series you want to aggregate your dataset to a less granular period. An example of this might be moving from a daily series to a weekly or monthly series to look at broader trends in your data. as_period() allows you to do exactly this.

The period argument in as_period() for specifying the transformation you want is a character with a general format of “frequency period” where frequency is a number like 1 or 2, and period is an interval like weekly or yearly. There must be a space between the two.

Example Energy data

apply.monthly() to obtain a monthly time series from a daily time series

library(lubridate)
library(tibbletime)

Energy.t<-as_tbl_time(energy.df,index =dateRep)# transform the dataframe into tibble
head(Energy.t[,1:7])
# A time tibble: 6 x 7
# Index: dateRep
  dateRep             Outflow.V.2010. Outflow.K..2010. Outflow.F..2010.
  <dttm>                        <dbl>            <dbl>            <dbl>
1 2010-01-01 00:00:00            242.             406.             343.
2 2010-01-02 00:00:00            257.             469.             252.
3 2010-01-03 00:00:00            446.             391.             349.
4 2010-01-04 00:00:00            571.             428.             443.
5 2010-01-05 00:00:00            554.             508.             448.
6 2010-01-06 00:00:00            543.             504.             406.
# ... with 3 more variables: Inflow.V..2010. <dbl>, Inflow..F..2010. <dbl>,
#   Inflow.K..2010. <dbl>
# transform the data to monthly from daily
Energy.monthly<-as_period(Energy.t, '1 month')
head(Energy.monthly[,1:7])
# A time tibble: 6 x 7
# Index: dateRep
  dateRep             Outflow.V.2010. Outflow.K..2010. Outflow.F..2010.
  <dttm>                        <dbl>            <dbl>            <dbl>
1 2010-01-01 00:00:00            242.             406.             343.
2 2010-02-01 00:00:00            476.             516.             361.
3 2010-03-01 00:00:00            544.             411.             317.
4 2010-04-01 00:00:00            448.             494.             337.
5 2010-05-01 00:00:00            433.             383.             400.
6 2010-06-01 00:00:00            441.             446.             384.
# ... with 3 more variables: Inflow.V..2010. <dbl>, Inflow..F..2010. <dbl>,
#   Inflow.K..2010. <dbl>
# Create monthly time series from daily (sum of energy produced daily from each HPP)
library(xts)
energy.xts.f <- xts(energy.df$Production.Fierze..2010., energy.df$dateRep) # create a xts object
head(energy.xts.f)
timezone of object (UTC) is different than current timezone ().
            [,1]
2010-01-01  6100
2010-01-02  8500
2010-01-03 10800
2010-01-04 10800
2010-01-05  9800
2010-01-06  8300
# total of monthly energy produced in F
energy.f.m<-apply.monthly(energy.xts.f, sum)
head(energy.f.m)
timezone of object (UTC) is different than current timezone ().
             [,1]
2010-01-31 298600
2010-02-28 228200
2010-03-31 304013
2010-04-30 292220
2010-05-31 281860
2010-06-30 188400
# total of weekly energy produced in F
energy.f.w<-apply.weekly(energy.xts.f,sum)
head(energy.f.w)
timezone of object (UTC) is different than current timezone ().
            [,1]
2010-01-03 25400
2010-01-10 69600
2010-01-17 69400
2010-01-24 65400
2010-01-31 68800
2010-02-07 57600
# total of yearly energy produced in F
energy.f.y<-apply.yearly(energy.xts.f,sum,na.rm=T)
head(energy.f.y)
timezone of object (UTC) is different than current timezone ().
              [,1]
2010-12-31 2630580
2011-12-31 1214409
2012-12-31 1087102
2013-12-31 2024624
2014-12-31 1045315
2015-12-31 1615998

Examples Datasets required (FB and FANG)

library(tibbletime)
library(dplyr)

Facebook stock prices dataset

data(FB)
head(FB)

Convert FB to tbl_time

FB <- as_tbl_time(FB, index = date)
head(FB)
# A time tibble: 6 x 8
# Index: date
  symbol date        open  high   low close    volume adjusted
  <chr>  <date>     <dbl> <dbl> <dbl> <dbl>     <dbl>    <dbl>
1 FB     2013-01-02  27.4  28.2  27.4  28    69846400     28  
2 FB     2013-01-03  27.9  28.5  27.6  27.8  63140600     27.8
3 FB     2013-01-04  28.0  28.9  27.8  28.8  72715400     28.8
4 FB     2013-01-07  28.7  29.8  28.6  29.4  83781800     29.4
5 FB     2013-01-08  29.5  29.6  28.9  29.1  45871300     29.1
6 FB     2013-01-09  29.7  30.6  29.5  30.6 104787700     30.6

FANG stock prices dataset

data(FANG)
head(FANG,30)

Convert FANG to tbl_time and group by symbol variable

FANG <- as_tbl_time(FANG, index = date)# create a tibble 
FANG<- FANG %>% group_by(symbol)# grouping by symbol
FANG
# A time tibble: 4,032 x 8
# Index:  date
# Groups: symbol [4]
   symbol date        open  high   low close    volume adjusted
   <chr>  <date>     <dbl> <dbl> <dbl> <dbl>     <dbl>    <dbl>
 1 FB     2013-01-02  27.4  28.2  27.4  28    69846400     28  
 2 FB     2013-01-03  27.9  28.5  27.6  27.8  63140600     27.8
 3 FB     2013-01-04  28.0  28.9  27.8  28.8  72715400     28.8
 4 FB     2013-01-07  28.7  29.8  28.6  29.4  83781800     29.4
 5 FB     2013-01-08  29.5  29.6  28.9  29.1  45871300     29.1
 6 FB     2013-01-09  29.7  30.6  29.5  30.6 104787700     30.6
 7 FB     2013-01-10  30.6  31.5  30.3  31.3  95316400     31.3
 8 FB     2013-01-11  31.3  32.0  31.1  31.7  89598000     31.7
 9 FB     2013-01-14  32.1  32.2  30.6  31.0  98892800     31.0
10 FB     2013-01-15  30.6  31.7  29.9  30.1 173242600     30.1
# ... with 4,022 more rows

as_period()

Transform Daily to monthly

Let us now transform the daily FB data set to monthly data using the function as_period() and as argument “1 month”

as_period(FB, '1 month')
# A time tibble: 48 x 8
# Index: date
   symbol date        open  high   low close    volume adjusted
   <chr>  <date>     <dbl> <dbl> <dbl> <dbl>     <dbl>    <dbl>
 1 FB     2013-01-02  27.4  28.2  27.4  28    69846400     28  
 2 FB     2013-02-01  31.0  31.0  29.6  29.7  85856700     29.7
 3 FB     2013-03-01  27.0  28.1  26.8  27.8  54064800     27.8
 4 FB     2013-04-01  25.6  25.9  25.3  25.5  22249300     25.5
 5 FB     2013-05-01  27.8  27.9  27.3  27.4  64567600     27.4
 6 FB     2013-06-03  24.3  24.3  23.7  23.8  35733800     23.8
 7 FB     2013-07-01  25.0  25.1  24.6  24.8  20582200     24.8
 8 FB     2013-08-01  37.3  38.3  36.9  37.5 106066500     37.5
 9 FB     2013-09-03  41.8  42.2  41.5  41.9  48774900     41.9
10 FB     2013-10-01  50.0  51.0  49.5  50.4  98114000     50.4
# ... with 38 more rows

Generic periods

You aren’t restricted to only 1 month periods. Maybe you wanted every 2 months? Or, 6 months?

as_period(FB, '2 m')
# A time tibble: 24 x 8
# Index: date
   symbol date        open  high   low close   volume adjusted
   <chr>  <date>     <dbl> <dbl> <dbl> <dbl>    <dbl>    <dbl>
 1 FB     2013-01-02  27.4  28.2  27.4  28   69846400     28  
 2 FB     2013-03-01  27.0  28.1  26.8  27.8 54064800     27.8
 3 FB     2013-05-01  27.8  27.9  27.3  27.4 64567600     27.4
 4 FB     2013-07-01  25.0  25.1  24.6  24.8 20582200     24.8
 5 FB     2013-09-03  41.8  42.2  41.5  41.9 48774900     41.9
 6 FB     2013-11-01  50.8  52.1  49.7  49.8 95033000     49.8
 7 FB     2014-01-02  54.8  55.2  54.2  54.7 43195500     54.7
 8 FB     2014-03-03  67.0  68.1  66.5  67.4 56824100     67.4
 9 FB     2014-05-01  60.4  62.3  60.2  61.2 82429000     61.2
10 FB     2014-07-01  67.6  68.4  67.4  68.1 33243000     68.1
# ... with 14 more rows
as_period(FB, '6 m')
# A time tibble: 8 x 8
# Index: date
  symbol date        open  high   low close   volume adjusted
  <chr>  <date>     <dbl> <dbl> <dbl> <dbl>    <dbl>    <dbl>
1 FB     2013-01-02  27.4  28.2  27.4  28   69846400     28  
2 FB     2013-07-01  25.0  25.1  24.6  24.8 20582200     24.8
3 FB     2014-01-02  54.8  55.2  54.2  54.7 43195500     54.7
4 FB     2014-07-01  67.6  68.4  67.4  68.1 33243000     68.1
5 FB     2015-01-02  78.6  78.9  77.7  78.4 18177500     78.4
6 FB     2015-07-01  86.8  87.9  86.5  86.9 25260000     86.9
7 FB     2016-01-04 102.  102.   99.8 102.  37912400    102. 
8 FB     2016-07-01 114.  115.  114.  114.  14980000    114. 

Or maybe every 15 days? Note that the dates do not line up exactly with a difference of 25 days. This is due to the data set not being completely regular (there are gaps due to weekends and holidays). as_period() chooses the first date it can find in the period specified.

as_period(FB, '15 d')
# A time tibble: 98 x 8
# Index: date
   symbol date        open  high   low close    volume adjusted
   <chr>  <date>     <dbl> <dbl> <dbl> <dbl>     <dbl>    <dbl>
 1 FB     2013-01-02  27.4  28.2  27.4  28    69846400     28  
 2 FB     2013-01-16  30.2  30.4  29.5  29.8  75332700     29.8
 3 FB     2013-01-31  29.2  31.5  28.7  31.0 190744900     31.0
 4 FB     2013-02-15  28.5  28.8  28.1  28.3  33109300     28.3
 5 FB     2013-03-04  27.8  28.1  27.4  27.7  32400700     27.7
 6 FB     2013-03-18  26.4  26.8  25.8  26.5  26653700     26.5
 7 FB     2013-04-01  25.6  25.9  25.3  25.5  22249300     25.5
 8 FB     2013-04-16  26.8  27.1  26.4  26.9  27365900     26.9
 9 FB     2013-05-01  27.8  27.9  27.3  27.4  64567600     27.4
10 FB     2013-05-16  26.5  26.5  25.9  26.1  35499100     26.1
# ... with 88 more rows

Details and the start_date argument

Without start_date

as_period(FB, '10 d')
# A time tibble: 146 x 8
# Index: date
   symbol date        open  high   low close    volume adjusted
   <chr>  <date>     <dbl> <dbl> <dbl> <dbl>     <dbl>    <dbl>
 1 FB     2013-01-02  27.4  28.2  27.4  28    69846400     28  
 2 FB     2013-01-11  31.3  32.0  31.1  31.7  89598000     31.7
 3 FB     2013-01-22  29.8  30.9  29.7  30.7  55243300     30.7
 4 FB     2013-01-31  29.2  31.5  28.7  31.0 190744900     31.0
 5 FB     2013-02-11  28.6  28.7  28.0  28.3  37361800     28.3
 6 FB     2013-02-20  28.9  29.0  28.3  28.5  42098200     28.5
 7 FB     2013-03-04  27.8  28.1  27.4  27.7  32400700     27.7
 8 FB     2013-03-12  28.1  28.3  27.6  27.8  27569600     27.8
 9 FB     2013-03-22  25.8  26.0  25.6  25.7  18456300     25.7
10 FB     2013-04-01  25.6  25.9  25.3  25.5  22249300     25.5
# ... with 136 more rows

start_date argument

as_period(FB, '10 d', start_date = "2013-01-01")
# A time tibble: 146 x 8
# Index: date
   symbol date        open  high   low close    volume adjusted
   <chr>  <date>     <dbl> <dbl> <dbl> <dbl>     <dbl>    <dbl>
 1 FB     2013-01-02  27.4  28.2  27.4  28    69846400     28  
 2 FB     2013-01-11  31.3  32.0  31.1  31.7  89598000     31.7
 3 FB     2013-01-22  29.8  30.9  29.7  30.7  55243300     30.7
 4 FB     2013-01-31  29.2  31.5  28.7  31.0 190744900     31.0
 5 FB     2013-02-11  28.6  28.7  28.0  28.3  37361800     28.3
 6 FB     2013-02-20  28.9  29.0  28.3  28.5  42098200     28.5
 7 FB     2013-03-04  27.8  28.1  27.4  27.7  32400700     27.7
 8 FB     2013-03-12  28.1  28.3  27.6  27.8  27569600     27.8
 9 FB     2013-03-22  25.8  26.0  25.6  25.7  18456300     25.7
10 FB     2013-04-01  25.6  25.9  25.3  25.5  22249300     25.5
# ... with 136 more rows

The side argument

By default, the first date per period is returned. If you want the end of each period instead, specify the side = “end” argument.

as_period(FB, 'y')
# A time tibble: 4 x 8
# Index: date
  symbol date        open  high   low close   volume adjusted
  <chr>  <date>     <dbl> <dbl> <dbl> <dbl>    <dbl>    <dbl>
1 FB     2013-01-02  27.4  28.2  27.4  28   69846400     28  
2 FB     2014-01-02  54.8  55.2  54.2  54.7 43195500     54.7
3 FB     2015-01-02  78.6  78.9  77.7  78.4 18177500     78.4
4 FB     2016-01-04 102.  102.   99.8 102.  37912400    102. 
as_period(FB, 'y', side = "end")
# A time tibble: 4 x 8
# Index: date
  symbol date        open  high   low close   volume adjusted
  <chr>  <date>     <dbl> <dbl> <dbl> <dbl>    <dbl>    <dbl>
1 FB     2013-12-31  54.1  54.9  53.9  54.7 43076200     54.7
2 FB     2014-12-31  79.5  79.8  77.9  78.0 19935400     78.0
3 FB     2015-12-31 106   106.  105.  105.  18298700    105. 
4 FB     2016-12-30 117.  117.  115.  115.  18600100    115. 

Grouped datasets

One of the neat things about working in the tidyverse is that these functions can also work with grouped datasets.

Here we transform the daily series of the 4 FANG stocks to a periodicity of every 6 months and 1 year. Try to change the periodicity to 4 months and 2 years.

FANG %>% as_period('6 m')
# A time tibble: 32 x 8
# Index:  date
# Groups: symbol [4]
   symbol date        open  high   low close   volume adjusted
   <chr>  <date>     <dbl> <dbl> <dbl> <dbl>    <dbl>    <dbl>
 1 FB     2013-01-02  27.4  28.2  27.4  28   69846400     28  
 2 FB     2013-07-01  25.0  25.1  24.6  24.8 20582200     24.8
 3 FB     2014-01-02  54.8  55.2  54.2  54.7 43195500     54.7
 4 FB     2014-07-01  67.6  68.4  67.4  68.1 33243000     68.1
 5 FB     2015-01-02  78.6  78.9  77.7  78.4 18177500     78.4
 6 FB     2015-07-01  86.8  87.9  86.5  86.9 25260000     86.9
 7 FB     2016-01-04 102.  102.   99.8 102.  37912400    102. 
 8 FB     2016-07-01 114.  115.  114.  114.  14980000    114. 
 9 AMZN   2013-01-02 256.  258.  253.  257.   3271000    257. 
10 AMZN   2013-07-01 279   283.  277.  282.   2888200    282. 
# ... with 22 more rows
FANG %>% as_period('1 y')
# A time tibble: 16 x 8
# Index:  date
# Groups: symbol [4]
   symbol date         open   high    low  close volume
   <chr>  <date>      <dbl>  <dbl>  <dbl>  <dbl>  <dbl>
 1 FB     2013-01-02   27.4   28.2   27.4   28   6.98e7
 2 FB     2014-01-02   54.8   55.2   54.2   54.7 4.32e7
 3 FB     2015-01-02   78.6   78.9   77.7   78.4 1.82e7
 4 FB     2016-01-04  102.   102.    99.8  102.  3.79e7
 5 AMZN   2013-01-02  256.   258.   253.   257.  3.27e6
 6 AMZN   2014-01-02  399.   399.   394.   398.  2.14e6
 7 AMZN   2015-01-02  313.   315.   307.   309.  2.78e6
 8 AMZN   2016-01-04  656.   658.   628.   637.  9.31e6
 9 NFLX   2013-01-02   95.2   95.8   90.7   92.0 1.94e7
10 NFLX   2014-01-02  367.   368.   361.   363.  1.23e7
11 NFLX   2015-01-02  344.   352.   341.   349.  1.35e7
12 NFLX   2016-01-04  109    110    105.   110.  2.08e7
13 GOOG   2013-01-02  719.   727.   717.   723.  5.10e6
14 GOOG   2014-01-02 1115.  1118.  1108.  1113.  3.66e6
15 GOOG   2015-01-02  529.   531.   524.   525.  1.45e6
16 GOOG   2016-01-04  743    744.   731.   742.  3.27e6
# ... with 1 more variable: adjusted <dbl>

create_series of dates

Create a tbl_time object with a sequence of regularly spaced dates

options("digits.secs" = 4)
 options("digits" = 18)
 milli$date
 [1] "2013-01-01 00:00:00.0 UTC" "2013-01-01 00:00:00.0 UTC"
 [3] "2013-01-01 00:00:00.2 UTC" "2013-01-01 00:00:00.2 UTC"
 [5] "2013-01-01 00:00:00.4 UTC" "2013-01-01 00:00:00.5 UTC"
 [7] "2013-01-01 00:00:00.5 UTC" "2013-01-01 00:00:00.7 UTC"
 [9] "2013-01-01 00:00:00.7 UTC" "2013-01-01 00:00:00.9 UTC"
[11] "2013-01-01 00:00:01.0 UTC"
 as.numeric(milli$date)
 [1] 1356998400.0000000 1356998400.0999999 1356998400.2000000
 [4] 1356998400.3000000 1356998400.4000001 1356998400.5000000
 [7] 1356998400.5999999 1356998400.7000000 1356998400.8000000
[10] 1356998400.9000001 1356998401.0000000

filter_time

filter_time(.tbl_time, time_formula) The time_formula is specified using the format from ~ to.

Each side of the time_formula is specified as the character: ‘YYYY-MM-DD HH:MM:SS’, but powerful shorthand is available.

Some examples are: • Year: ‘2013’ ~ ‘2015’ • Month: ‘2013-01’ ~ ‘2016-06’ • Day: ‘2013-01-05’ ~ ‘2016-06-04’ • Second: ‘2013-01-05 10:22:15’ ~ ‘2018-06-03 12:14:22’ • Variations: ‘2013’ ~ ‘2016-06’

The time_formula can also use a one sided formula. • Only dates in 2015: ~‘2015’ • Only dates March 2015: ~‘2015-03’

The time_formula can also use ‘start’ and ‘end’ as keywords for your filter. • Start of the series to end of 2015: ‘start’ ~ ‘2015’

filter_time(FANG, ~ '2013')
# A time tibble: 1,008 x 8
# Index:  date
# Groups: symbol [4]
   symbol date        open  high   low close    volume adjusted
   <chr>  <date>     <dbl> <dbl> <dbl> <dbl>     <dbl>    <dbl>
 1 FB     2013-01-02  27.4  28.2  27.4  28    69846400     28  
 2 FB     2013-01-03  27.9  28.5  27.6  27.8  63140600     27.8
 3 FB     2013-01-04  28.0  28.9  27.8  28.8  72715400     28.8
 4 FB     2013-01-07  28.7  29.8  28.6  29.4  83781800     29.4
 5 FB     2013-01-08  29.5  29.6  28.9  29.1  45871300     29.1
 6 FB     2013-01-09  29.7  30.6  29.5  30.6 104787700     30.6
 7 FB     2013-01-10  30.6  31.5  30.3  31.3  95316400     31.3
 8 FB     2013-01-11  31.3  32.0  31.1  31.7  89598000     31.7
 9 FB     2013-01-14  32.1  32.2  30.6  31.0  98892800     31.0
10 FB     2013-01-15  30.6  31.7  29.9  30.1 173242600     30.1
# ... with 998 more rows
#create_series('2013' ~ '2014', '1 d') 
filter_time(FANG, '2013' ~ '2014')
# A time tibble: 2,016 x 8
# Index:  date
# Groups: symbol [4]
   symbol date        open  high   low close    volume adjusted
   <chr>  <date>     <dbl> <dbl> <dbl> <dbl>     <dbl>    <dbl>
 1 FB     2013-01-02  27.4  28.2  27.4  28    69846400     28  
 2 FB     2013-01-03  27.9  28.5  27.6  27.8  63140600     27.8
 3 FB     2013-01-04  28.0  28.9  27.8  28.8  72715400     28.8
 4 FB     2013-01-07  28.7  29.8  28.6  29.4  83781800     29.4
 5 FB     2013-01-08  29.5  29.6  28.9  29.1  45871300     29.1
 6 FB     2013-01-09  29.7  30.6  29.5  30.6 104787700     30.6
 7 FB     2013-01-10  30.6  31.5  30.3  31.3  95316400     31.3
 8 FB     2013-01-11  31.3  32.0  31.1  31.7  89598000     31.7
 9 FB     2013-01-14  32.1  32.2  30.6  31.0  98892800     31.0
10 FB     2013-01-15  30.6  31.7  29.9  30.1 173242600     30.1
# ... with 2,006 more rows
# 2013-05-25 to 2014-06-04
filter_time(FANG, '2013-05-25' ~ '2014-06-04')
# A time tibble: 1,032 x 8
# Index:  date
# Groups: symbol [4]
   symbol date        open  high   low close   volume adjusted
   <chr>  <date>     <dbl> <dbl> <dbl> <dbl>    <dbl>    <dbl>
 1 FB     2013-05-28  24.5  24.5  23.9  24.1 50079700     24.1
 2 FB     2013-05-29  23.8  23.8  23.3  23.3 64237800     23.3
 3 FB     2013-05-30  24.1  24.8  23.9  24.5 60733200     24.5
 4 FB     2013-05-31  24.6  25.0  24.3  24.4 35925000     24.4
 5 FB     2013-06-03  24.3  24.3  23.7  23.8 35733800     23.8
 6 FB     2013-06-04  23.9  23.9  23.3  23.5 34760800     23.5
 7 FB     2013-06-05  23.4  23.7  22.8  22.9 53819700     22.9
 8 FB     2013-06-06  23.0  23.1  22.7  23.0 31260700     23.0
 9 FB     2013-06-07  23.0  23.4  22.9  23.3 38699200     23.3
10 FB     2013-06-10  24.1  24.6  24.0  24.3 58393000     24.3
# ... with 1,022 more rows
# we can also use the pipe operator here and obtain the same result
FANG %>% filter_time('2013-05-25' ~ '2014-06-04')
# A time tibble: 1,032 x 8
# Index:  date
# Groups: symbol [4]
   symbol date        open  high   low close   volume adjusted
   <chr>  <date>     <dbl> <dbl> <dbl> <dbl>    <dbl>    <dbl>
 1 FB     2013-05-28  24.5  24.5  23.9  24.1 50079700     24.1
 2 FB     2013-05-29  23.8  23.8  23.3  23.3 64237800     23.3
 3 FB     2013-05-30  24.1  24.8  23.9  24.5 60733200     24.5
 4 FB     2013-05-31  24.6  25.0  24.3  24.4 35925000     24.4
 5 FB     2013-06-03  24.3  24.3  23.7  23.8 35733800     23.8
 6 FB     2013-06-04  23.9  23.9  23.3  23.5 34760800     23.5
 7 FB     2013-06-05  23.4  23.7  22.8  22.9 53819700     22.9
 8 FB     2013-06-06  23.0  23.1  22.7  23.0 31260700     23.0
 9 FB     2013-06-07  23.0  23.4  22.9  23.3 38699200     23.3
10 FB     2013-06-10  24.1  24.6  24.0  24.3 58393000     24.3
# ... with 1,022 more rows

Month filtering example

Let suppose we want to select every date in February, 2014:

filter_time(FANG,~'2014-02')
# A time tibble: 76 x 8
# Index:  date
# Groups: symbol [4]
   symbol date        open  high   low close   volume adjusted
   <chr>  <date>     <dbl> <dbl> <dbl> <dbl>    <dbl>    <dbl>
 1 FB     2014-02-03  63.0  63.8  60.7  61.5 74866600     61.5
 2 FB     2014-02-04  62.0  63.1  61.8  62.8 45985500     62.8
 3 FB     2014-02-05  62.7  63.2  61.3  62.2 51685100     62.2
 4 FB     2014-02-06  61.5  62.8  61.5  62.2 42086500     62.2
 5 FB     2014-02-07  62.3  64.6  62.2  64.3 60704300     64.3
 6 FB     2014-02-10  64.3  64.5  63.5  63.5 43666100     63.5
 7 FB     2014-02-11  63.8  65    63.3  64.8 45675600     64.8
 8 FB     2014-02-12  64.9  65.1  64.1  64.4 47282100     64.4
 9 FB     2014-02-13  64.2  67.3  64.1  67.3 61911700     67.3
10 FB     2014-02-14  67.5  67.6  66.7  67.1 36694900     67.1
# ... with 66 more rows

[ syntax

For interactive use, to get an even quicker look at a dataset you can use the traditional extraction operator [ with the formula syntax.

FANG[~'2013']
# A time tibble: 1,008 x 8
# Index: date
   symbol date        open  high   low close    volume adjusted
   <chr>  <date>     <dbl> <dbl> <dbl> <dbl>     <dbl>    <dbl>
 1 FB     2013-01-02  27.4  28.2  27.4  28    69846400     28  
 2 FB     2013-01-03  27.9  28.5  27.6  27.8  63140600     27.8
 3 FB     2013-01-04  28.0  28.9  27.8  28.8  72715400     28.8
 4 FB     2013-01-07  28.7  29.8  28.6  29.4  83781800     29.4
 5 FB     2013-01-08  29.5  29.6  28.9  29.1  45871300     29.1
 6 FB     2013-01-09  29.7  30.6  29.5  30.6 104787700     30.6
 7 FB     2013-01-10  30.6  31.5  30.3  31.3  95316400     31.3
 8 FB     2013-01-11  31.3  32.0  31.1  31.7  89598000     31.7
 9 FB     2013-01-14  32.1  32.2  30.6  31.0  98892800     31.0
10 FB     2013-01-15  30.6  31.7  29.9  30.1 173242600     30.1
# ... with 998 more rows
FANG['2014'~'2014-02', c(1,2,4,5)]
# A time tibble: 160 x 4
# Index: date
   symbol date        high   low
   <chr>  <date>     <dbl> <dbl>
 1 FB     2014-01-02  55.2  54.2
 2 FB     2014-01-03  55.7  54.5
 3 FB     2014-01-06  57.3  54.0
 4 FB     2014-01-07  58.5  57.2
 5 FB     2014-01-08  58.4  57.2
 6 FB     2014-01-09  59.0  56.7
 7 FB     2014-01-10  58.3  57.1
 8 FB     2014-01-13  58.2  55.4
 9 FB     2014-01-14  57.8  56.1
10 FB     2014-01-15  58.6  57.3
# ... with 150 more rows
FANG['2014'~'2014-02', c(1,2,3:6)]
# A time tibble: 160 x 6
# Index: date
   symbol date        open  high   low close
   <chr>  <date>     <dbl> <dbl> <dbl> <dbl>
 1 FB     2014-01-02  54.8  55.2  54.2  54.7
 2 FB     2014-01-03  55.0  55.7  54.5  54.6
 3 FB     2014-01-06  54.4  57.3  54.0  57.2
 4 FB     2014-01-07  57.7  58.5  57.2  57.9
 5 FB     2014-01-08  57.6  58.4  57.2  58.2
 6 FB     2014-01-09  58.7  59.0  56.7  57.2
 7 FB     2014-01-10  57.1  58.3  57.1  57.9
 8 FB     2014-01-13  57.9  58.2  55.4  55.9
 9 FB     2014-01-14  56.5  57.8  56.1  57.7
10 FB     2014-01-15  58.0  58.6  57.3  57.6
# ... with 150 more rows

You can also cretae a date and use filter_time with that date as a start (end)

end.date <- as.Date("2015-01-01")
filter_time(FANG, 'start' ~ end.date)
# A time tibble: 2,016 x 8
# Index:  date
# Groups: symbol [4]
   symbol date        open  high   low close    volume adjusted
   <chr>  <date>     <dbl> <dbl> <dbl> <dbl>     <dbl>    <dbl>
 1 FB     2013-01-02  27.4  28.2  27.4  28    69846400     28  
 2 FB     2013-01-03  27.9  28.5  27.6  27.8  63140600     27.8
 3 FB     2013-01-04  28.0  28.9  27.8  28.8  72715400     28.8
 4 FB     2013-01-07  28.7  29.8  28.6  29.4  83781800     29.4
 5 FB     2013-01-08  29.5  29.6  28.9  29.1  45871300     29.1
 6 FB     2013-01-09  29.7  30.6  29.5  30.6 104787700     30.6
 7 FB     2013-01-10  30.6  31.5  30.3  31.3  95316400     31.3
 8 FB     2013-01-11  31.3  32.0  31.1  31.7  89598000     31.7
 9 FB     2013-01-14  32.1  32.2  30.6  31.0  98892800     31.0
10 FB     2013-01-15  30.6  31.7  29.9  30.1 173242600     30.1
# ... with 2,006 more rows

Reference:

https://lubridate.tidyverse.org/

https://rpubs.com/Sergio_Garcia/working_with_dates_and_times

https://cran.r-project.org/web/packages/tibbletime/vignettes/TT-02-changing-time-periods.html

https://cran.r-project.org/web/packages/tidyquant/vignettes/TQ03-scaling-and-modeling-with-tidyquant.html

https://cran.r-project.org/web/packages/tibbletime/tibbletime.pdf

https://stackoverflow.com/questions/33105900/sum-daily-values-into-monthly-values

Thank you!

Eralda Gjika (Dhamo)

January 2021

LS0tDQp0aXRsZTogIkRhdGVzIGFuZCBUaW1lcyBpbiBSIg0KYXV0aG9yOiAiRXJhbGRhIEdqaWthIChEaGFtbykiDQpkYXRlOiAiMDcvMDEvMjAyMCINCm91dHB1dDogaHRtbF9ub3RlYm9vaw0KLS0tDQoNCiMjIyBUaWJibGVzIGluIFINCg0KQSB0aWJibGUsIG9yIHRibF9kZiwgaXMgYSBtb2Rlcm4gcmVpbWFnaW5pbmcgb2YgdGhlIGRhdGEuZnJhbWUsIGtlZXBpbmcgd2hhdCB0aW1lIGhhcyBwcm92ZW4gdG8gYmUgZWZmZWN0aXZlLCBhbmQgdGhyb3dpbmcgb3V0IHdoYXQgaXMgbm90LiANCg0KSW4gdGhpcyB3b3JrIHdlIGFyZSBnb2luZyB0byBzZWUgdGliYmxlLXMuIEZvciBtb3JlIGluZm9ybWF0aW9uIG9uIHRoaXMgbmV3IHZlcnNpb24gb2YgZGF0YS5mcmFtZSBwbGVhc2Ugc2VlOg0KDQpodHRwczovL3I0ZHMuaGFkLmNvLm56L3RpYmJsZXMuaHRtbA0KaHR0cHM6Ly90aWJibGUudGlkeXZlcnNlLm9yZy8NCg0KYGBge3J9DQpsaWJyYXJ5KHRpZHl2ZXJzZSkNCmhlYWQoaXJpcywxMCkNCmFzX3RpYmJsZShpcmlzKQ0KYGBgDQoNCmBgYHtyfQ0KeC50Ymw8LXRpYmJsZSgNCiAgeDEgPSBzZXEoMSwxMCksIA0KICB4MiA9IHNlcSgxMSwyMCksIA0KICB4MyA9IHgxIF4gMiArIHgyKjAuNQ0KKQ0KeC50YmwNCmBgYA0KDQojIyMgV29ya2luZyB3aXRoIGRhdGVzIGFuZCB0aW1lcyANCg0KTGlicmFyaWVzIHdlIHdpbGwgbmVlZCBhcmU6DQpgYGB7cn0NCmxpYnJhcnkodGlkeXZlcnNlKQ0KbGlicmFyeShsdWJyaWRhdGUpDQpsaWJyYXJ5KG55Y2ZsaWdodHMxMykNCmBgYA0KIyMjIyBUbyBnZXQgdGhlIGN1cnJlbnQgZGF0ZSBvciBkYXRlLXRpbWUNCkZvciB0aGlzIGluZm9ybWF0aW9uIHlvdSBjYW4gdXNlIGZ1bmN0aW9uOiB0b2RheSgpIG9yIG5vdygpOg0KYGBge3J9DQp0b2RheSgpDQpub3coKQ0KYGBgDQpPdGhlcndpc2UsIHRoZXJlIGFyZSB0aHJlZSB3YXlzIHlvdSBtYXkgdXNlIHRvIGNyZWF0ZSBhIGRhdGUvdGltZToNCuKAokZyb20gYSBzdHJpbmcuDQrigKJGcm9tIGluZGl2aWR1YWwgZGF0ZS10aW1lIGNvbXBvbmVudHMuDQrigKJGcm9tIGFuIGV4aXN0aW5nIGRhdGUvdGltZSBvYmplY3QuDQoNCiMjIyBDcmVhdGUgZGF0ZS90aW1lIGZyb20gc3RyaW5ncw0KVXNlIGxpYnJhcnkobHVicmlkYXRlKSwgdG8gaWRlbnRpZnkgdGhlIG9yZGVyIGluIHdoaWNoIHllYXIsIG1vbnRoLCBhbmQgZGF5IGFwcGVhciBpbiB5b3VyIGRhdGVzLCB0aGVuIGFycmFuZ2Ug4oCceeKAnSwg4oCcbeKAnSwgYW5kIOKAnGTigJ0gaW4gdGhlIHNhbWUgb3JkZXIuDQpUaGF0IGdpdmVzIHlvdSB0aGUgbmFtZSBvZiB0aGUgbHVicmlkYXRlIGZ1bmN0aW9uIHRoYXQgd2lsbCBwYXJzZSB5b3VyIGRhdGUuDQoNCiMjIyMgRXhhbXBsZSAxDQpgYGB7cn0NCnltZCgiMjAyMC0xMi0zMSIpDQptZHkoIkRlY2VtYmVyIDMxc3QsIDIwMjAiKQ0KZG15KCIxLUphbi0yMDIwIikNCmBgYA0KR3JlZW53aWNoIE1lYW4gVGltZSAoR01UKSBpcyBvZnRlbiBpbnRlcmNoYW5nZWQgb3IgY29uZnVzZWQgd2l0aCBDb29yZGluYXRlZCBVbml2ZXJzYWwgVGltZSAoVVRDKSB3aGljaCBpcyBhIHRpbWUgc3RhbmRhcmQgdGhhdCBpcyB0aGUgYmFzaXMgZm9yIGNpdmlsIHRpbWUgYW5kIHRpbWUgem9uZXMgd29ybGR3aWRlLg0KQWRkaW5nIGluZm9ybWF0aW9uIGFib3V0IGhvdXIsIG1pbnV0ZSBhbmQgc2Vjb25kIHVzZSA64oCcaOKAnSwg4oCcbeKAnSwgYW5kIOKAnHPigJ0gdG8gdGhlIGZ1bmN0aW9uLg0KRXhhbXBsZTogeW1kX2htcygpDQpgYGB7cn0NCiMgd2lsbCBnaXZlIGFzIG91dHB1dCB0aGUgZGF0ZSBhbmQgdGhlIGhvdXIvbWluL3NlYw0KeW1kX2htcygiMjAyMC0xMi0zMSAyMzo1ODo1OSIpIA0KIyB3aWxsIGdpdmUgYXMgb3V0cHV0IHRoZSBkYXRlIGFuZCB0aGUgaG91ci9taW4NCm1keV9obSgiMTIvMzEvMjAyMCAxMTo1OCIpIA0KIyBZb3UgY2FuIGFsc28gZm9yY2UgdGhlIGNyZWF0aW9uIG9mIGEgZGF0ZS10aW1lIGZyb20gYSBkYXRlIGJ5IHN1cHBseWluZyBhIHRpbWV6b25lOg0KeW1kKDIwMjAxMjMxLCB0eiA9ICJVVEMiKQ0KYGBgDQpJbXBvcnQgYSBkYXRhZnJhbWUgb2YgQ292aWQgbmV3IGNhc2VzIGFuZCBkZWF0aHMgcGVyIGRheSBwdWJsaXNoZWQgYnk6DQoNClJlZjpodHRwczovL3d3dy5lY2RjLmV1cm9wYS5ldS9lbi9wdWJsaWNhdGlvbnMtZGF0YS9kYXRhLW5hdGlvbmFsLTE0LWRheS1ub3RpZmljYXRpb24tcmF0ZS1jb3ZpZC0xOQ0KDQpgYGB7cn0NCmNvdmlkLmRhdGEgPC0gcmVhZC5jc3YoIkM6L1VzZXJzL3VzZXIvRGVza3RvcC9Db3ZpZCBkYXRhLTEyIG9jdG9iZXIgMjAyMC5jc3YiKQ0KVmlldyhjb3ZpZC5kYXRhKQ0KaGVhZChjb3ZpZC5kYXRhLDEwKQ0KYGBgDQoNClRvIGNyZWF0ZSBhIGRhdGUvdGltZSBmcm9tIHRoaXMgc29ydCBvZiBpbnB1dCwgdXNlIG1ha2VfZGF0ZSgpIGZvciBkYXRlcywgb3IgbWFrZV9kYXRldGltZSgpIGZvciBkYXRlLXRpbWVzOg0KdGhlIGZ1bmN0aW9uIG11dGF0ZSgpIGFkZHMgYSB2YXJpYWJsZSB0byB5b3VyIGRhdGFmcmFtZS4NCg0KYGBge3J9DQpjb3ZpZC5kYXRhWywyOjRdICU+JSBtdXRhdGUoaW5mb3JtYXRpb25fdGltZSA9IG1ha2VfZGF0ZXRpbWUoeWVhciwgbW9udGgsIGRheSkpDQpgYGANCg0KWW91IG1heSB3YW50IHRvIHN3aXRjaCBiZXR3ZWVuIGEgZGF0ZS10aW1lIGFuZCBhIGRhdGUuDQpUaGF04oCZcyB0aGUgam9iIG9mIGFzX2RhdGV0aW1lKCkgYW5kIGFzX2RhdGUoKToNCg0KYGBge3J9DQp0b2RheSgpDQphc19kYXRldGltZSh0b2RheSgpKQ0Kbm93KCkNCmFzX2RhdGUobm93KCkpDQpgYGANCkFwcGx5aW5nIGluIHRoZSBDb3ZpZCBkYXRhc2V0Og0KYGBge3J9DQpoZWFkKGNvdmlkLmRhdGFbLDFdLDEwKQ0KaGVhZChhc19kYXRldGltZShjb3ZpZC5kYXRhWywxXSksMTApDQpoZWFkKGFzX2RhdGUoY292aWQuZGF0YVssMV0pLDEwKQ0KYGBgDQpFeGFtcGxlDQpVc2UgdGhlIGFib3ZlIGZ1bmN0aW9uIHRvIHBhcnNlIHRoZSBkYXRhIGJlbG93Og0KDQpgYGB7cn0NCmQxIDwtICJKYW51YXJ5IDEsIDIwMjAiDQpkMQ0KZDEudDwtbWR5KGQxKQ0KZDEudA0KZDIgPC0gIjIwMjAtTWFyLTE0Ig0KeW1kKGQyKQ0KZDMgPC0gIjA2LUp1bi0yMDEzIg0KZG15KGQzKQ0KZDQgPC0gYygiQXVndXN0IDYgKDIwMTMpIiwgIkp1bHkgMTAgKDIwMTYpIikNCm1keShkNCkNCmQ1IDwtICIxMi8zMS8yMCIgIyBEZWMgMzEsIDIwMjANCm1keShkNSkNCmBgYA0KIyMjIEdldHRpbmcgY29tcG9uZW50cw0KV2UgY2FuIHB1bGwgb3V0IGluZGl2aWR1YWwgcGFydHMgb2YgdGhlIGRhdGUgd2l0aCB0aGUgYWNjZXNzb3IgZnVuY3Rpb25zOg0KeWVhcigpLA0KbW9udGgoKSwNCm1kYXkoKSAoZGF5IG9mIHRoZSBtb250aCksDQp5ZGF5KCkgKGRheSBvZiB0aGUgeWVhciksDQp3ZGF5KCkgKGRheSBvZiB0aGUgd2VlayksDQpob3VyKCksDQptaW51dGUoKSwNCnNlY29uZCgpDQoNCkxldCB1c2Ugc29tZSBvZiB0aGVzZSBmdW5jdGlvbnMgYXQgdGhlIGNvdmlkIGRhdGFzZXQuIFdlIGhhdmUgdXNlZCB0aGUgaGVhZCgpIGZ1bmN0aW9uIHRvIGRpc3BsYXkganVzdCBhIGZldyBvZiB0aGUgZGF0YS4gDQpgYGB7cn0NCmhlYWQoY292aWQuZGF0YVssMV0sMTApDQpoZWFkKHllYXIoY292aWQuZGF0YVssMV0pLDEwKQ0KaGVhZChtb250aChjb3ZpZC5kYXRhWywxXSksMTApDQpoZWFkKGRheShjb3ZpZC5kYXRhWywxXSksMTApDQpgYGANCkZvciBtb250aCgpIGFuZCB3ZGF5KCkgeW91IGNhbiBzZXQgYXJndW1lbnQgbGFiZWwgPSBUUlVFIHRvIHJldHVybiB0aGUgYWJicmV2aWF0ZWQgbmFtZSBvZiB0aGUgbW9udGggb3IgZGF5IG9mIHRoZSB3ZWVrLg0KU2V0IGFyZ3VtZW50IGFiYnIgPSBGQUxTRSB0byByZXR1cm4gdGhlIGZ1bGwgbmFtZS4gKEtlZXAgaW4gbWluZCB0aGF0IHdlIGNhbiB1c2UgVFJVRSBvciBULCBhbmQgRkFMU0Ugb3IgRikNCg0KYGBge3J9DQpoZWFkKG1vbnRoKGNvdmlkLmRhdGFbLDFdLGxhYmVsPVRSVUUpLDEwKQ0KaGVhZChtb250aChjb3ZpZC5kYXRhWywxXSxsYWJlbD1UUlVFLGFiYnI9RiksMTApDQpoZWFkKHdkYXkoY292aWQuZGF0YVssMV0sbGFiZWw9VFJVRSksMTApDQpoZWFkKHdkYXkoY292aWQuZGF0YVssMV0sbGFiZWw9VCxhYmJyPUYpLDEwKQ0KYGBgDQojIyMgUm91bmRpbmcNCkFuIGFsdGVybmF0aXZlIGFwcHJvYWNoIHRvIHBsb3R0aW5nIGluZGl2aWR1YWwgY29tcG9uZW50cyBpcyB0byByb3VuZCB0aGUgZGF0ZSB0byBhIG5lYXJieSB1bml0IG9mIHRpbWUsIHdpdGg6IA0KZmxvb3JfZGF0ZSgpDQpyb3VuZF9kYXRlKCkNCmFuZCBjZWlsaW5nX2RhdGUoKS4NCg0Kcm91bmRfZGF0ZSgpIHRha2VzIGEgZGF0ZS10aW1lIG9iamVjdCBhbmQgdGltZSB1bml0LCBhbmQgcm91bmRzIGl0IHRvIHRoZSBuZWFyZXN0IHZhbHVlIG9mIHRoZSBzcGVjaWZpZWQgdGltZSB1bml0LiANCg0KZmxvb3JfZGF0ZSgpIHRha2VzIGEgZGF0ZS10aW1lIG9iamVjdCBhbmQgcm91bmRzIGl0IGRvd24gdG8gdGhlIG5lYXJlc3QgYm91bmRhcnkgb2YgdGhlIHNwZWNpZmllZCB0aW1lIHVuaXQuDQoNCmNlaWxpbmdfZGF0ZSgpIHRha2VzIGEgZGF0ZS10aW1lIG9iamVjdCBhbmQgcm91bmRzIGl0IHVwIHRvIHRoZSBuZWFyZXN0IGJvdW5kYXJ5IG9mIHRoZSBzcGVjaWZpZWQgdGltZSB1bml0Lg0KDQpFYWNoIGZ1bmN0aW9uIHRha2VzIGEgdmVjdG9yIG9mIGRhdGVzIHRvIGFkanVzdCBhbmQgdGhlbiB0aGUgbmFtZSBvZiB0aGUgdW5pdCByb3VuZCBkb3duIChmbG9vciksIHJvdW5kIHVwIChjZWlsaW5nKSwgb3Igcm91bmQgdG8uDQoNCiMjIyMgUm91bmRfZGF0ZQ0KYGBge3J9DQp4IDwtIHltZF9obXMoIjIwMjEtMDEtMDYgMTI6MDE6NTguMzYiKQ0KeA0Kcm91bmRfZGF0ZSh4LCAiLjVzIikNCnJvdW5kX2RhdGUoeCwgInNlYyIpDQpyb3VuZF9kYXRlKHgsICJzZWNvbmQiKQ0Kcm91bmRfZGF0ZSh4LCAibWludXRlIikNCnJvdW5kX2RhdGUoeCwgIjEwIG1pbnMiKQ0Kcm91bmRfZGF0ZSh4LCAiaG91ciIpDQpyb3VuZF9kYXRlKHgsICIyIGhvdXJzIikNCnJvdW5kX2RhdGUoeCwgIjUgaG91cnMiKQ0Kcm91bmRfZGF0ZSh4LCAiZGF5IikNCnJvdW5kX2RhdGUoeCwgIndlZWsiKQ0Kcm91bmRfZGF0ZSh4LCAibW9udGgiKQ0Kcm91bmRfZGF0ZSh4LCAiYmltb250aCIpDQpyb3VuZF9kYXRlKHgsICJxdWFydGVyIikgIyBzYW1lIGFzOiByb3VuZF9kYXRlKHgsICIzIG1vbnRocyIpDQpyb3VuZF9kYXRlKHgsICJoYWxmeWVhciIpDQpyb3VuZF9kYXRlKHgsICJ5ZWFyIikNCmBgYA0KIyMjIyBmbG9vcl9kYXRlDQpgYGB7cn0NCnggPC0geW1kX2htcygiMjAyMS0wNS0wNiAxMjowMTo1OC4zNiIpDQp4DQpmbG9vcl9kYXRlKHgsICIuMXMiKQ0KZmxvb3JfZGF0ZSh4LCAic2Vjb25kIikNCmZsb29yX2RhdGUoeCwgIiA1IG1pbnV0ZXMiKQ0KZmxvb3JfZGF0ZSh4LCAiaG91ciIpDQpmbG9vcl9kYXRlKHgsICJkYXkiKQ0KZmxvb3JfZGF0ZSh4LCAid2VlayIpDQpmbG9vcl9kYXRlKHgsICJtb250aCIpDQpmbG9vcl9kYXRlKHgsICJiaW1vbnRoIikNCmZsb29yX2RhdGUoeCwgInF1YXJ0ZXIiKQ0KZmxvb3JfZGF0ZSh4LCAic2Vhc29uIikNCmZsb29yX2RhdGUoeCwgImhhbGZ5ZWFyIikNCmZsb29yX2RhdGUoeCwgInllYXIiKQ0KYGBgDQojIyMjIGNlaWxpbmdfZGF0ZQ0KYGBge3J9DQp4IDwtIHltZF9obXMoIjIwMjEtMDEtMDYgMTI6MDE6NTguMzYiKQ0KeA0KY2VpbGluZ19kYXRlKHgsICIuMiBzZWMiKQ0KY2VpbGluZ19kYXRlKHgsICJzZWNvbmQiKQ0KY2VpbGluZ19kYXRlKHgsICJtaW51dGUiKQ0KY2VpbGluZ19kYXRlKHgsICI1IG1pbnMiKQ0KY2VpbGluZ19kYXRlKHgsICJob3VyIikNCmNlaWxpbmdfZGF0ZSh4LCAiZGF5IikNCmNlaWxpbmdfZGF0ZSh4LCAid2VlayIpDQpjZWlsaW5nX2RhdGUoeCwgIm1vbnRoIikNCmNlaWxpbmdfZGF0ZSh4LCAiYmltb250aCIpICMgc2FtZSBhczogY2VpbGluZ19kYXRlKHgsICIyIG1vbnRocyIpDQpjZWlsaW5nX2RhdGUoeCwgInF1YXJ0ZXIiKQ0KY2VpbGluZ19kYXRlKHgsICJzZWFzb24iKQ0KY2VpbGluZ19kYXRlKHgsICJoYWxmeWVhciIpDQpjZWlsaW5nX2RhdGUoeCwgInllYXIiKQ0KYGBgDQojIyMgU2V0dGluZyBjb21wb25lbnRzDQpXZSBjYW4gdXNlIGVhY2ggYWNjZXNzb3IgZnVuY3Rpb24gdG8gc2V0IHRoZSBjb21wb25lbnRzIG9mIGEgZGF0ZS90aW1lLg0KTGV0J3Mgc3VwcG9zZSB3ZSBuZWVkIHRvIGNvcnJlY3Qgc29tZSB2YWx1ZXMgaW46IHllYXIsIG1vbnRoLCBkYXksIGhvdXIsIG1pbnV0ZSwgc2Vjb25kIGFuZCB0aW1lIHpvbmUgYXMgd2VsbC4NCmBgYHtyfQ0KZC50aW1lXzIgPC0geW1kX2htcygiMjAyMS0wMS0wOCAxNjo0NTo1MiIpDQpkLnRpbWVfMg0KeWVhcihkLnRpbWVfMikgPC0gMjAyMiAjIGNvcnJlY3QgdGhlIHllYXIgZnJvbSAyMDIxIHRvIDIwMjINCmQudGltZV8yDQptb250aChkLnRpbWVfMikgPC0gMDMgIyBjb3JyZWN0IHRoZSBtb250aCBmcm9tIDAxIChqYW51YXJ5KSB0byAwMyAobWFyY2gpDQpkLnRpbWVfMg0KbWRheShkLnRpbWVfMikgPC0gMTMgIyBjb3JyZWN0IHRoZSBkYXRlIGZyb20gMDggdG8gMTMNCmQudGltZV8yDQpob3VyKGQudGltZV8yKTwtMDYgIyBjb3JyZWN0IHRoZSBob3VyDQpkLnRpbWVfMg0KbWludXRlKGQudGltZV8yKTwtMDUgIyBjb3JyZWN0IHRoZSBtaW51dGUNCmQudGltZV8yDQpzZWNvbmQoZC50aW1lXzIpPC0yNSAjIGNvcnJlY3QgdGhlIHNlY29uZA0KZC50aW1lXzINCmBgYA0KT3RoZXJ3aXNlIHdlIGNhbiB1c2UgdXBkYXRlKCkgZnVuY3Rpb24gdG8gY29ycmVjdCB0aGUgYWJvdmUgZGV0YWlscy4NCg0KYGBge3J9DQp1cGRhdGUoZC50aW1lXzIsIHllYXIgPTIwMjIgLCBtb250aCA9MDMgLCBtZGF5ID0xMyAsIGhvdXIgPTA2LG1pbnV0ZT0wNSwgc2Vjb25kPTI1ICkNCnVwZGF0ZShkLnRpbWVfMiwgeWVhciA9MjAyNSAsIG1kYXkgPTE1ICkNCmBgYA0KIyMjIFRpbWUgc3BhbnMNCkhhdmUgeW91IHRob3VnaHQgaG93IGFyaXRobWV0aWMgd2l0aCBkYXRlcyB3b3JrcywgaW5jbHVkaW5nIHN1YnRyYWN0aW9uLCBhZGRpdGlvbiwgYW5kIGRpdmlzaW9uLg0KVGhlc2UgYXJlIHRocmVlIGltcG9ydGFudCBjbGFzc2VzIHRoYXQgcmVwcmVzZW50IHRpbWUgc3BhbnM6DQoNCi0+IGR1cmF0aW9ucywgd2hpY2ggcmVwcmVzZW50IGFuIGV4YWN0IG51bWJlciBvZiBzZWNvbmRzLg0KLT4gcGVyaW9kcywgd2hpY2ggcmVwcmVzZW50IGh1bWFuIHVuaXRzIGxpa2Ugd2Vla3MgYW5kIG1vbnRocy4NCi0+IGludGVydmFscywgd2hpY2ggcmVwcmVzZW50IGEgc3RhcnRpbmcgYW5kIGVuZGluZyBwb2ludC4NCg0KIyMjIyBkdXJhdGlvbnMNCkV4YW1wbGUNCkhvdyBvbGQgYW0gST8NCmBgYHtyfQ0KZXJhbGRhLmFnZSA8LSB0b2RheSgpIC0geW1kKDE5ODMwMTE0KSAjIHllYXIgMTk4MywgbW9udGggMDEsIGRhdGUgMTQNCmVyYWxkYS5hZ2UNCnByaW50KHBhc3RlICgiIEVyYWxkYSBhZ2UgaXMiLGVyYWxkYS5hZ2UsImRheXMiKSkNCmVyYWxkYS55ZWFyczwtYXMuZHVyYXRpb24oZXJhbGRhLmFnZSkNCmVyYWxkYS55ZWFycw0KcHJpbnQocGFzdGUgKCIgRXJhbGRhIGFnZSBpcyIsZXJhbGRhLnllYXJzKSkNCmBgYA0KZGZ1bmN0aW9uKHZhbHVlKSBpcyBhbm90aGVyIHVzZWQgZnVuY3Rpb24gd2hpY2ggdGFrZXMgYSB2YWx1ZSB4ICh3aGljaCBjYW4gYmUgaW4gdW5pdHMgb2Ygc2Vjb25kLCBtaW51dGVzLCBob3VycywgZGF5cywgd2Vla3MsIHllYXJzKSBhbmQgcmV0dXJucyBpdCdzIGR1cmF0aW9uIGluIHNlY29uZHMuDQpFeGFtcGxlDQpgYGB7cn0NCmRzZWNvbmRzKDEwKSMgZHVyYXRpb24gaW4gc2Vjb25kcyBvZiAxMCBzZWMNCmRtaW51dGVzKDUpIyBkdXJhdGlvbiBpbiBzZWNvbmRzIG9mIDUgbWludXRlcw0KZGhvdXJzKGMoMiwgMTApKSMgZHVyYXRpb24gaW4gc2Vjb25kcyBvZiAyIGhvdXJzIGFuZCAxMCBob3Vycw0KZGRheXMoMDo3KSMgZHVyYXRpb24gaW4gc2Vjb25kcyBvZiAwIGRheXMsIDEgZGF5LCAyIGRheXMgdXAgdG8gNyBkYXlzDQpkd2Vla3MoMikjIGR1cmF0aW9uIGluIHNlY29uZHMgb2YgMiB3ZWVrcw0KZHllYXJzKGMoMSwyKSkjZHVyYXRpb24gaW4gc2Vjb25kcyBvZiAxIHllYXIgYW5kIDIgeWVhcnMNCmBgYA0KDQpZb3UgY2FuIGFkZCBhbmQgbXVsdGlwbHkgZHVyYXRpb25zOg0KYGBge3J9DQoxMCAqIGR5ZWFycygxKQ0KZHllYXJzKDEwKQ0KZHllYXJzKDEpICsgZHdlZWtzKDEyKSArIGRob3VycygxNSkNCmBgYA0KDQpZb3UgY2FuIGFkZCBhbmQgc3VidHJhY3QgZHVyYXRpb25zIHRvIGFuZCBmcm9tIGRheXM6DQpgYGB7cn0NCnRvbW9ycm93IDwtIHRvZGF5KCkgKyBkZGF5cygxKQ0KdG9tb3Jyb3cNCmRiZWZvcmUuMjwtdG9kYXkoKSArIGRkYXlzKC0yKQ0KZGJlZm9yZS4yDQp0d29feWVhcl9iZWZvcmUgPC0gdG9kYXkoKSAtIGR5ZWFycygyKQ0KdHdvX3llYXJfYmVmb3JlDQpgYGANCiMjIyBQZXJpb2RzDQpQZXJpb2RzIGFyZSB0aW1lIHNwYW5zIGJ1dCBkb27igJl0IGhhdmUgYSBmaXhlZCBsZW5ndGggaW4gc2Vjb25kcywgaW5zdGVhZCB0aGV5IHdvcmsgd2l0aCDigJxodW1hbuKAnSB0aW1lcywgbGlrZSBkYXlzIGFuZCBtb250aHMuDQpUaGF0IGFsbG93cyB0aGVtIHRvIHdvcmsgaW4gYSBtb3JlIGludHVpdGl2ZSB3YXkuDQpTbyB5b3UgY2FuIHVzZTogDQrigKIgc2Vjb25kcygpDQrigKIgbWludXRlcygpDQrigKIgaG91cnMoKQ0K4oCiIGRheXMoKQ0K4oCiIG1vbnRocygpDQrigKIgd2Vla3MoKQ0K4oCiIHllYXJzKCkNCmBgYHtyfQ0KbmV3LnllYXIgPC0geW1kX2htcygiMjAyMS0wMS0wMSAwMDowMDowMSIpDQpuZXcueWVhcg0KbmV3LnllYXIgKyBkYXlzKDEpDQpuZXcueWVhciArIGRheXMoLTEpDQpuZXcueWVhciArIGRheXMoMzEpDQpuZXcueWVhciArIGRheXMoMzEpKyBob3VycygxMikNCm5ldy55ZWFyICsgZGF5cygzMSkrIGhvdXJzKDEyKSttaW51dGVzKDE1KQ0KbmV3LnllYXIgKyBkYXlzKDMxKSsgaG91cnMoMTIpK21pbnV0ZXMoMTUpK3NlY29uZHMoMjUpDQpgYGANCllvdSBjYW4gYWRkIGFuZCBtdWx0aXBseSBwZXJpb2RzOg0KYGBge3J9DQoxMCAqIChtb250aHMoMikgKyBkYXlzKDEpKQ0KZGF5cyg2MSkgKyBob3VycygyNSkgKyBtaW51dGVzKDUpDQpkeWVhcnMoMSkNCnltZCgiMjAyMC0wMS0wMSIpICsgZHllYXJzKDEpDQp5bWQoIjIwMjAtMDEtMDEiKSArIHllYXJzKDEpDQpuZXcueWVhciArIGRkYXlzKDEpDQpuZXcueWVhciArIGRheXMoMSkNCmBgYA0KDQojIyMgSW50cm9kdWNpbmcgYXNfcGVyaW9kKCkNCk9mdGVuIHdpdGggdGltZSBzZXJpZXMgeW91IHdhbnQgdG8gYWdncmVnYXRlIHlvdXIgZGF0YXNldCB0byBhIGxlc3MgZ3JhbnVsYXIgcGVyaW9kLiANCkFuIGV4YW1wbGUgb2YgdGhpcyBtaWdodCBiZSBtb3ZpbmcgZnJvbSBhIGRhaWx5IHNlcmllcyB0byBhIHdlZWtseSBvciBtb250aGx5IHNlcmllcyB0byBsb29rIGF0IGJyb2FkZXIgdHJlbmRzIGluIHlvdXIgZGF0YS4gYXNfcGVyaW9kKCkgYWxsb3dzIHlvdSB0byBkbyBleGFjdGx5IHRoaXMuDQoNClRoZSBwZXJpb2QgYXJndW1lbnQgaW4gYXNfcGVyaW9kKCkgZm9yIHNwZWNpZnlpbmcgdGhlIHRyYW5zZm9ybWF0aW9uIHlvdSB3YW50IGlzIGEgY2hhcmFjdGVyIHdpdGggYSBnZW5lcmFsIGZvcm1hdCBvZiAiZnJlcXVlbmN5IHBlcmlvZCIgd2hlcmUgZnJlcXVlbmN5IGlzIGEgbnVtYmVyIGxpa2UgMSBvciAyLCBhbmQgcGVyaW9kIGlzIGFuIGludGVydmFsIGxpa2Ugd2Vla2x5IG9yIHllYXJseS4gVGhlcmUgbXVzdCBiZSBhIHNwYWNlIGJldHdlZW4gdGhlIHR3by4NCg0KIyMjIEV4YW1wbGUgRW5lcmd5IGRhdGENCiMjIyMgYXBwbHkubW9udGhseSgpIHRvIG9idGFpbiBhIG1vbnRobHkgdGltZSBzZXJpZXMgZnJvbSBhIGRhaWx5IHRpbWUgc2VyaWVzDQpgYGB7cn0NCmxpYnJhcnkobHVicmlkYXRlKQ0KbGlicmFyeSh0aWJibGV0aW1lKQ0KDQpFbmVyZ3kudDwtYXNfdGJsX3RpbWUoZW5lcmd5LmRmLGluZGV4ID1kYXRlUmVwKSMgdHJhbnNmb3JtIHRoZSBkYXRhZnJhbWUgaW50byB0aWJibGUNCmhlYWQoRW5lcmd5LnRbLDE6N10pDQoNCiMgdHJhbnNmb3JtIHRoZSBkYXRhIHRvIG1vbnRobHkgZnJvbSBkYWlseQ0KRW5lcmd5Lm1vbnRobHk8LWFzX3BlcmlvZChFbmVyZ3kudCwgJzEgbW9udGgnKQ0KaGVhZChFbmVyZ3kubW9udGhseVssMTo3XSkNCg0KIyBDcmVhdGUgbW9udGhseSB0aW1lIHNlcmllcyBmcm9tIGRhaWx5IChzdW0gb2YgZW5lcmd5IHByb2R1Y2VkIGRhaWx5IGZyb20gZWFjaCBIUFApDQpsaWJyYXJ5KHh0cykNCmVuZXJneS54dHMuZiA8LSB4dHMoZW5lcmd5LmRmJFByb2R1Y3Rpb24uRmllcnplLi4yMDEwLiwgZW5lcmd5LmRmJGRhdGVSZXApICMgY3JlYXRlIGEgeHRzIG9iamVjdA0KaGVhZChlbmVyZ3kueHRzLmYpDQoNCiMgdG90YWwgb2YgbW9udGhseSBlbmVyZ3kgcHJvZHVjZWQgaW4gRg0KZW5lcmd5LmYubTwtYXBwbHkubW9udGhseShlbmVyZ3kueHRzLmYsIHN1bSkNCmhlYWQoZW5lcmd5LmYubSkNCg0KIyB0b3RhbCBvZiB3ZWVrbHkgZW5lcmd5IHByb2R1Y2VkIGluIEYNCmVuZXJneS5mLnc8LWFwcGx5LndlZWtseShlbmVyZ3kueHRzLmYsc3VtKQ0KaGVhZChlbmVyZ3kuZi53KQ0KDQojIHRvdGFsIG9mIHllYXJseSBlbmVyZ3kgcHJvZHVjZWQgaW4gRg0KZW5lcmd5LmYueTwtYXBwbHkueWVhcmx5KGVuZXJneS54dHMuZixzdW0sbmEucm09VCkNCmhlYWQoZW5lcmd5LmYueSkNCg0KYGBgDQoNCiMjIyMgRXhhbXBsZXMgRGF0YXNldHMgcmVxdWlyZWQgKEZCIGFuZCBGQU5HKQ0KDQpgYGB7cn0NCmxpYnJhcnkodGliYmxldGltZSkNCmxpYnJhcnkoZHBseXIpDQpgYGANCg0KIyMjIyBGYWNlYm9vayBzdG9jayBwcmljZXMgZGF0YXNldA0KYGBge3J9DQpkYXRhKEZCKQ0KaGVhZChGQikNCmBgYA0KDQojIyMgQ29udmVydCBGQiB0byB0YmxfdGltZQ0KYGBge3J9DQpGQiA8LSBhc190YmxfdGltZShGQiwgaW5kZXggPSBkYXRlKQ0KaGVhZChGQikNCmBgYA0KDQojIyMgRkFORyBzdG9jayBwcmljZXMgZGF0YXNldA0KYGBge3J9DQpkYXRhKEZBTkcpDQpoZWFkKEZBTkcsMzApDQpgYGANCg0KIyMjIENvbnZlcnQgRkFORyB0byB0YmxfdGltZSBhbmQgZ3JvdXAgYnkgc3ltYm9sIHZhcmlhYmxlDQpgYGB7cn0NCkZBTkcgPC0gYXNfdGJsX3RpbWUoRkFORywgaW5kZXggPSBkYXRlKSMgY3JlYXRlIGEgdGliYmxlIA0KRkFORzwtIEZBTkcgJT4lIGdyb3VwX2J5KHN5bWJvbCkjIGdyb3VwaW5nIGJ5IHN5bWJvbA0KRkFORw0KYGBgDQojIyMgYXNfcGVyaW9kKCkNCiMjIyMgVHJhbnNmb3JtIERhaWx5IHRvIG1vbnRobHkNCkxldCB1cyBub3cgdHJhbnNmb3JtIHRoZSBkYWlseSBGQiBkYXRhIHNldCB0byBtb250aGx5IGRhdGEgdXNpbmcgdGhlIGZ1bmN0aW9uIGFzX3BlcmlvZCgpIGFuZCBhcyBhcmd1bWVudCAiMSBtb250aCINCg0KYGBge3J9DQphc19wZXJpb2QoRkIsICcxIG1vbnRoJykNCmBgYA0KIyMjIyBHZW5lcmljIHBlcmlvZHMNCllvdSBhcmVu4oCZdCByZXN0cmljdGVkIHRvIG9ubHkgMSBtb250aCBwZXJpb2RzLiBNYXliZSB5b3Ugd2FudGVkIGV2ZXJ5IDIgbW9udGhzPyBPciwgNiBtb250aHM/DQoNCmBgYHtyfQ0KYXNfcGVyaW9kKEZCLCAnMiBtJykNCmFzX3BlcmlvZChGQiwgJzYgbScpDQpgYGANCg0KT3IgbWF5YmUgZXZlcnkgMTUgZGF5cz8gTm90ZSB0aGF0IHRoZSBkYXRlcyBkbyBub3QgbGluZSB1cCBleGFjdGx5IHdpdGggYSBkaWZmZXJlbmNlIG9mIDI1IGRheXMuIFRoaXMgaXMgZHVlIHRvIHRoZSBkYXRhIHNldCBub3QgYmVpbmcgY29tcGxldGVseSByZWd1bGFyICh0aGVyZSBhcmUgZ2FwcyBkdWUgdG8gd2Vla2VuZHMgYW5kIGhvbGlkYXlzKS4gYXNfcGVyaW9kKCkgY2hvb3NlcyB0aGUgZmlyc3QgZGF0ZSBpdCBjYW4gZmluZCBpbiB0aGUgcGVyaW9kIHNwZWNpZmllZC4NCg0KYGBge3J9DQphc19wZXJpb2QoRkIsICcxNSBkJykNCmBgYA0KIyMjIERldGFpbHMgYW5kIHRoZSBzdGFydF9kYXRlIGFyZ3VtZW50DQojIyMjIFdpdGhvdXQgc3RhcnRfZGF0ZQ0KYGBge3J9DQphc19wZXJpb2QoRkIsICcxMCBkJykNCmBgYA0KIyMjIyBzdGFydF9kYXRlIGFyZ3VtZW50DQoNCmBgYHtyfQ0KYXNfcGVyaW9kKEZCLCAnMTAgZCcsIHN0YXJ0X2RhdGUgPSAiMjAxMy0wMS0wMSIpDQpgYGANCiMjIyMgVGhlIHNpZGUgYXJndW1lbnQNCkJ5IGRlZmF1bHQsIHRoZSBmaXJzdCBkYXRlIHBlciBwZXJpb2QgaXMgcmV0dXJuZWQuIA0KSWYgeW91IHdhbnQgdGhlIGVuZCBvZiBlYWNoIHBlcmlvZCBpbnN0ZWFkLCBzcGVjaWZ5IHRoZSBzaWRlID0gImVuZCIgYXJndW1lbnQuDQpgYGB7cn0NCmFzX3BlcmlvZChGQiwgJ3knKQ0KYXNfcGVyaW9kKEZCLCAneScsIHNpZGUgPSAiZW5kIikNCg0KYGBgDQojIyMgR3JvdXBlZCBkYXRhc2V0cw0KT25lIG9mIHRoZSBuZWF0IHRoaW5ncyBhYm91dCB3b3JraW5nIGluIHRoZSB0aWR5dmVyc2UgaXMgdGhhdCB0aGVzZSBmdW5jdGlvbnMgY2FuIGFsc28gd29yayB3aXRoIGdyb3VwZWQgZGF0YXNldHMuIA0KDQpIZXJlIHdlIHRyYW5zZm9ybSB0aGUgZGFpbHkgc2VyaWVzIG9mIHRoZSA0IEZBTkcgc3RvY2tzIHRvIGEgcGVyaW9kaWNpdHkgb2YgZXZlcnkgNiBtb250aHMgYW5kIDEgeWVhci4gDQpUcnkgdG8gY2hhbmdlIHRoZSBwZXJpb2RpY2l0eSB0byA0IG1vbnRocyBhbmQgMiB5ZWFycy4NCg0KYGBge3J9DQpGQU5HICU+JSBhc19wZXJpb2QoJzYgbScpDQpGQU5HICU+JSBhc19wZXJpb2QoJzEgeScpDQoNCmBgYA0KIyMjIGNyZWF0ZV9zZXJpZXMgb2YgZGF0ZXMNCg0KQ3JlYXRlIGEgdGJsX3RpbWUgb2JqZWN0IHdpdGggYSBzZXF1ZW5jZSBvZiByZWd1bGFybHkgc3BhY2VkIGRhdGVzDQpgYGB7cn0NCmxpYnJhcnkodGliYmxldGltZSkNCiMgRXZlcnkgZGF5IGluIDIwMTANCmhlYWQoY3JlYXRlX3Nlcmllcyh+JzIwMTAnLCAnZGFpbHknKSw3KQ0KDQojIEV2ZXJ5IG90aGVyIGRheSBpbiAyMDEwDQpoZWFkKGNyZWF0ZV9zZXJpZXMoficyMDEwJywgJzIgZCcpLDE0KQ0KDQojIEV2ZXJ5IHF1YXJ0ZXIgaW4gMjAxMA0KaGVhZChjcmVhdGVfc2VyaWVzKH4nMjAxMCcsICcxIHEnKSwxMikNCg0KIyBEYWlseSBzZXJpZXMgZm9yIDIwMTAtMjAxNw0KaGVhZChjcmVhdGVfc2VyaWVzKCcyMDEwJyB+ICcyMDE3JywgJzEgZCcpLDcpDQoNCiMgTWludXRlIHNlcmllcyBmb3IgMiBtb250aHMNCmhlYWQoY3JlYXRlX3NlcmllcygnMjAxMy0wMScgfiAnMjAxMy0wMicsICdNJyksNjApDQoNCiMgU2Vjb25kIHNlcmllcyBmb3IgMiBtaW51dGVzDQpoZWFkKGNyZWF0ZV9zZXJpZXMoJzIwMTEtMDEtMDEgMTI6MTA6MDAnIH4gJzIwMTEtMDEtMDEgMTI6MTI6MDAnLCAncycpLDEwKQ0KDQojIERhdGUgY2xhc3MNCmhlYWQoY3JlYXRlX3Nlcmllcyh+JzIwMTAnLCAnZGF5JywgY2xhc3MgPSAiRGF0ZSIpLDEwKQ0KDQojIHllYXJtb24gY2xhc3MNCmhlYWQoY3JlYXRlX3Nlcmllcyh+JzIwMTAnLCAnbW9udGgnLCBjbGFzcyA9ICJ5ZWFybW9uIiksMTApDQoNCiMgaG1zIGNsYXNzLiB0aW1lX2Zvcm11bGEgc3BlY2lmaWVkIGFzIEhIOk1NOlNTIGhlcmUNCmhlYWQoY3JlYXRlX3NlcmllcygnMDA6MDA6MDAnIH4gJzEyOjAwOjAwJywgJ3NlY29uZCcgLCBjbGFzcyA9ICJobXMiKSwxMCkNCg0KIyBTdWJzZWNvbmQgc2VyaWVzDQpoZWFkKGNyZWF0ZV9zZXJpZXMoJzIwMTAnIH4gJzIwMTAtMDEtMDEgMDA6MDA6MDEnLCBwZXJpb2QgPSAiMTAgbWlsbGlzZWMiKSwxMCkNCg0KbWlsbGlzZWMgPC0gY3JlYXRlX3NlcmllcygnMjAxMycgfiAnMjAxMy0wMS0wMSAwMDowMDowMScsIHBlcmlvZCA9ICIuMSBzZWMiKQ0KaGVhZChtaWxsaXNlYywxMCkNCg0KYGBgDQojIyMjIGZpbHRlcl90aW1lIA0KDQpmaWx0ZXJfdGltZSgudGJsX3RpbWUsIHRpbWVfZm9ybXVsYSkNClRoZSB0aW1lX2Zvcm11bGEgaXMgc3BlY2lmaWVkIHVzaW5nIHRoZSBmb3JtYXQgZnJvbSB+IHRvLiANCg0KRWFjaCBzaWRlIG9mIHRoZSB0aW1lX2Zvcm11bGEgaXMgc3BlY2lmaWVkIGFzIHRoZSBjaGFyYWN0ZXI6DQonWVlZWS1NTS1ERCBISDpNTTpTUycsIGJ1dCBwb3dlcmZ1bCBzaG9ydGhhbmQgaXMgYXZhaWxhYmxlLiANCg0KU29tZSBleGFtcGxlcyBhcmU6DQrigKIgWWVhcjogICAgICAgICAgJzIwMTMnIH4gJzIwMTUnDQrigKIgTW9udGg6ICAgICAgICAgJzIwMTMtMDEnIH4gJzIwMTYtMDYnDQrigKIgRGF5OiAgICAgICAgICAgJzIwMTMtMDEtMDUnIH4gJzIwMTYtMDYtMDQnDQrigKIgU2Vjb25kOiAgICAgICAgJzIwMTMtMDEtMDUgMTA6MjI6MTUnIH4gJzIwMTgtMDYtMDMgMTI6MTQ6MjInDQrigKIgVmFyaWF0aW9uczogICAgJzIwMTMnIH4gJzIwMTYtMDYnDQoNClRoZSB0aW1lX2Zvcm11bGEgY2FuIGFsc28gdXNlIGEgb25lIHNpZGVkIGZvcm11bGEuDQrigKIgT25seSBkYXRlcyBpbiAyMDE1OiAgICAgIH4nMjAxNScNCuKAoiBPbmx5IGRhdGVzIE1hcmNoIDIwMTU6ICAgficyMDE1LTAzJw0KDQpUaGUgdGltZV9mb3JtdWxhIGNhbiBhbHNvIHVzZSAnc3RhcnQnIGFuZCAnZW5kJyBhcyBrZXl3b3JkcyBmb3IgeW91ciBmaWx0ZXIuDQrigKIgU3RhcnQgb2YgdGhlIHNlcmllcyB0byBlbmQgb2YgMjAxNTogICAgJ3N0YXJ0JyB+ICcyMDE1Jw0KDQpgYGB7cn0NCmZpbHRlcl90aW1lKEZBTkcsIH4gJzIwMTMnKQ0KDQojY3JlYXRlX3NlcmllcygnMjAxMycgfiAnMjAxNCcsICcxIGQnKSANCmZpbHRlcl90aW1lKEZBTkcsICcyMDEzJyB+ICcyMDE0JykNCg0KIyAyMDEzLTA1LTI1IHRvIDIwMTQtMDYtMDQNCmZpbHRlcl90aW1lKEZBTkcsICcyMDEzLTA1LTI1JyB+ICcyMDE0LTA2LTA0JykNCg0KIyB3ZSBjYW4gYWxzbyB1c2UgdGhlIHBpcGUgb3BlcmF0b3IgaGVyZSBhbmQgb2J0YWluIHRoZSBzYW1lIHJlc3VsdA0KRkFORyAlPiUgZmlsdGVyX3RpbWUoJzIwMTMtMDUtMjUnIH4gJzIwMTQtMDYtMDQnKQ0KYGBgDQojIyMjIE1vbnRoIGZpbHRlcmluZyBleGFtcGxlDQpMZXQgc3VwcG9zZSB3ZSB3YW50IHRvIHNlbGVjdCBldmVyeSBkYXRlIGluIEZlYnJ1YXJ5LCAyMDE0Og0KYGBge3J9DQpmaWx0ZXJfdGltZShGQU5HLH4nMjAxNC0wMicpDQpgYGANCiMjIyBbIHN5bnRheA0KRm9yIGludGVyYWN0aXZlIHVzZSwgdG8gZ2V0IGFuIGV2ZW4gcXVpY2tlciBsb29rIGF0IGEgZGF0YXNldCB5b3UgY2FuIHVzZSB0aGUgdHJhZGl0aW9uYWwgZXh0cmFjdGlvbiBvcGVyYXRvciBbIHdpdGggdGhlIGZvcm11bGEgc3ludGF4Lg0KDQpgYGB7cn0NCkZBTkdbficyMDEzJ10NCkZBTkdbJzIwMTQnficyMDE0LTAyJywgYygxLDIsNCw1KV0NCkZBTkdbJzIwMTQnficyMDE0LTAyJywgYygxLDIsMzo2KV0NCmBgYA0KWW91IGNhbiBhbHNvIGNyZXRhZSBhIGRhdGUgYW5kIHVzZSBmaWx0ZXJfdGltZSB3aXRoIHRoYXQgZGF0ZSBhcyBhIHN0YXJ0IChlbmQpDQoNCmBgYHtyfQ0KZW5kLmRhdGUgPC0gYXMuRGF0ZSgiMjAxNS0wMS0wMSIpDQpmaWx0ZXJfdGltZShGQU5HLCAnc3RhcnQnIH4gZW5kLmRhdGUpDQpgYGANCg0KDQojIyMgUmVmZXJlbmNlOiANCg0KaHR0cHM6Ly9sdWJyaWRhdGUudGlkeXZlcnNlLm9yZy8NCg0KaHR0cHM6Ly9ycHVicy5jb20vU2VyZ2lvX0dhcmNpYS93b3JraW5nX3dpdGhfZGF0ZXNfYW5kX3RpbWVzDQoNCmh0dHBzOi8vY3Jhbi5yLXByb2plY3Qub3JnL3dlYi9wYWNrYWdlcy90aWJibGV0aW1lL3ZpZ25ldHRlcy9UVC0wMi1jaGFuZ2luZy10aW1lLXBlcmlvZHMuaHRtbA0KDQpodHRwczovL2NyYW4uci1wcm9qZWN0Lm9yZy93ZWIvcGFja2FnZXMvdGlkeXF1YW50L3ZpZ25ldHRlcy9UUTAzLXNjYWxpbmctYW5kLW1vZGVsaW5nLXdpdGgtdGlkeXF1YW50Lmh0bWwNCg0KaHR0cHM6Ly9jcmFuLnItcHJvamVjdC5vcmcvd2ViL3BhY2thZ2VzL3RpYmJsZXRpbWUvdGliYmxldGltZS5wZGYNCg0KaHR0cHM6Ly9zdGFja292ZXJmbG93LmNvbS9xdWVzdGlvbnMvMzMxMDU5MDAvc3VtLWRhaWx5LXZhbHVlcy1pbnRvLW1vbnRobHktdmFsdWVzDQoNCg0KIyMjIFRoYW5rIHlvdSENCiMjIyBFcmFsZGEgR2ppa2EgKERoYW1vKQ0KIyMjIyBKYW51YXJ5IDIwMjENCg==