library(tidyverse)
library(visreg)
library(broom)
library(lubridate)
Introduction: Time Series - Basic Concepts
In this workshop, we will introduce a new concept in statistics -
time series. Time series are observations or
measurements that are indexed according to time
regularly. Examples of time series are the number of
newly confirmed COVID-19 cases per day in NZ, the
global sales of iPhone per month, the
quarterly unemployment rate in NZ, and the
daily closing value of the NZX50.
Give a few examples on time series.
It is easy to notice that all the time series data are collected with
a natural temporal ordering. In an abstract sense, we record a time
series like \(y_1,y_2,...,y_T\), or
just \(y_t,t=1,...,T\) where \(t=1,...,T\) is the time
index.
Why does that make things different?
What happened today may affect tomorrow’s world but nothing could
change the outcomes of yesterday. The time series has a natural temporal
ordering which implies a unidirectional causal
relationship. The structure of time series implied by this ordering
distinguishes it from other types of data that are commonly
analysed.
In addition to the time index, it is important to notice that data
points in a time series are collected regularly over
time. In the beginning of this workshop, we highlight the words, ‘per
day’, ‘per month’, ‘quarterly’, and ‘daily’. These words define the
frequency or periodicity of time
series within one year, i.e. the number of observations
within a fixed time period. The frequency is 12 for monthly time series
and 365 for daily time series within one year.
List a few more adjectives/adverbs describing the
frequency.
Answer: weekly, hourly, annually …
What is the frequency of the wind speed in Wellington per hour
within one day?
Answer: 24
What is the frequency of the daily precipitation in Auckland
within one week?
Answer: 7
The above concepts will be pretty easy to understand by exploring
some real time series. So we will perform an exploratory data analysis
on NZ housing data collected by the Reserve Bank of New Zealand(RBNZ) in
Exercise 1. Interestingly, time can still be modelled by our linear
model easily and the fitted linear model can be further used to make
time series predictions. In Exercise 2 and 3, we will learn how to
analyse time series via linear models.
You first need to install RBNZ and janitor
using the Packages menu in the bottom right. Just click the
install button and type in RBNZ, then install. Or you can
install the package with R command install.packages().
RStudio may have prompted you about installing them when you loaded this
file.
Exercise 1: Exploring NZ House Price
Before we start the formal statistical analysis on any real time
series, it is essential to load the data first and then turn the data to
a suitable format ready for any further analysis.
- The following R code chunk retrieves the quarterly time series on
housing from the website of the Reserve Bank of New Zealand (RBNZ) (https://www.rbnz.govt.nz/statistics).
housing <- read_csv("https://www.massey.ac.nz/~jcmarsha/161122/data/housing.csv")
## Rows: 125 Columns: 5
## ── Column specification ────────────────────────────────────────────────────────
## Delimiter: ","
## dbl (4): number, index, m, real_m
## dttm (1): date
##
## ℹ Use `spec()` to retrieve the full column specification for this data.
## ℹ Specify the column types or set `show_col_types = FALSE` to quiet this message.
housing
## # A tibble: 125 × 5
## date number index m real_m
## <dttm> <dbl> <dbl> <dbl> <dbl>
## 1 1990-03-31 00:00:00 22856 477 123000 1790
## 2 1990-06-30 00:00:00 22685 480 125000 2043
## 3 1990-09-30 00:00:00 20175 483 126000 1873
## 4 1990-12-31 00:00:00 17476 482 126000 1985
## 5 1991-03-31 00:00:00 17331 479 124000 1567
## 6 1991-06-30 00:00:00 17898 470 123000 1626
## 7 1991-09-30 00:00:00 17774 468 125000 1541
## 8 1991-12-31 00:00:00 16785 466 125000 1684
## 9 1992-03-31 00:00:00 18656 468 125000 1498
## 10 1992-06-30 00:00:00 18561 467 127000 1701
## # … with 115 more rows
housing data contains one column for time information
(date) and four columns for different housing information,
i.e. total value of housing stock m, residential investment
real_m, house price index (HPI) index, and the
number of house sales number.
Specify the frequency of housing data.
Answer: 4
- Let’s make a scatter-line plot of house price index over time. You
may customise it a bit to get a better appearance.
housing |> ggplot(aes(x=date,y=index)) + geom_point() + geom_line() +
xlab('Time') + ylab('House Price Index') + ggtitle('Will Kiwi afford the house price?')

The warning message
Removed 1 rows containing missing values (geom_point). in
your HTML reminds you that there is an NA value in our
data. Let’s remove this NA value from the tibble via
drop_na() as
housing <- housing |> drop_na()
We can see clearly over the last three decades HPI was increasing in
most years. There are a few exceptions, i.e., around 1998, 2008, 2011.
What happened in these years?
Answer: the Asian Financial crisis in 1997, the
global financial crisis of 2007–2008, and the 2011 Christchurch
earthquake.
Another feature of this time series is that HPI seems increase with
different speeds over different decades. In 1990s, HPI tends to be
flatten or just increase moderately. But the first two decades in 21
century look quite crazy. The affordability of NZ house has become a
core social issue. Generally speaking, HPI shows an increasing
trend over the past thirty years.
What will happen in the next decade with the shock of COVID-19? Will
the house price in NZ become more affordable in the future? To answer
these questions, it is important for us to extract the long term
trend from this time series and make predictions on the
future HPI.
- Make scatter-line plot for the rest three variables in
housing. Briefly summarise their features.
Answer:
housing |> ggplot(aes(x=date,y=number)) + geom_point() + geom_line() +
xlab('Time') + ylab('Number of House Sales') + ggtitle('How many houses are sold in the past years?')

No clear trend or seasonality can be observed here. But the pattern
can be linked to HPI. When the house price is increasing, the number of
house sales tends to be high. However, when the house price is fatten,
the number of house sales tends to be low. One can also expect the
internal correlation between successive numbers given the frequent short
term movements in this time series (increasing or decreasing within
several successive quarters).
housing |> ggplot(aes(x=date,y=m)) + geom_point() + geom_line() +
xlab('Time') + ylab('Total Value of Housing Stock') + ggtitle('Is housing market always prospering?')

Almost same features as we can see from HPI. Just notice that the
total value of housing stock can be roughly calculated by the average
house price times the total number of houses. The newly built houses
make the depression caused by the Asian Financial crisis in 1997 and the
2011 Christchurch earthquake less obvious.
housing |> ggplot(aes(x=date,y=real_m)) + geom_point() + geom_line() +
xlab('Time') + ylab('Residential Investment') + ggtitle('
Residential Investment Opportunity!')

The residential investment has a similar general pattern as the
number of house sales as it contributes a certain portion to the whole
housing market (another important contributor is the first home buyer.)
However, the details of residential investment are different from the
number of house sales. We see many zigzag segments in this time series
especially in the first two decades. This may reflect the
self-regulatory rationality of the investors who may not follow the
trend of an over-heated housing market. OK, I have to say that maybe
the housing market in our country becomes a little crazy in the recent
years. The last sentence is purely a personal
comment.
Exercise 2: Capturing the Trend of House Price
The time index \(t=1,2,3,...,T\)
implied by the natural temporal ordering is always increasing. A very
simple idea is to consider the pairs \((1,y_1)\), \((2,y_2)\),…,\((T,y_T)\) just like the pairs of
observation \((x_i,y_i),i=1,2,...,n\)
in the linear model.
If the underlying trend of a time series shows either increasing or
decreasing pattern consistently, it is natural to link it with the time
index of each observation. Why?
Answer: The correlation coefficient will confirm
your intuition.
Moreover, if the underlying trend looks linear, a simple linear model
like y~t becomes a perfect candidate to discover the
underlying trend in a time series.
Anyway, let’s try it on HPI! Before fitting our linear model, it is
necessary to expand our tibble by adding the time indices \(1,2,3,...\) as follows.
housing.ts <- housing |> mutate(time=1:n())
housing.ts
## # A tibble: 125 × 6
## date number index m real_m time
## <dttm> <dbl> <dbl> <dbl> <dbl> <int>
## 1 1990-03-31 00:00:00 22856 477 123000 1790 1
## 2 1990-06-30 00:00:00 22685 480 125000 2043 2
## 3 1990-09-30 00:00:00 20175 483 126000 1873 3
## 4 1990-12-31 00:00:00 17476 482 126000 1985 4
## 5 1991-03-31 00:00:00 17331 479 124000 1567 5
## 6 1991-06-30 00:00:00 17898 470 123000 1626 6
## 7 1991-09-30 00:00:00 17774 468 125000 1541 7
## 8 1991-12-31 00:00:00 16785 466 125000 1684 8
## 9 1992-03-31 00:00:00 18656 468 125000 1498 9
## 10 1992-06-30 00:00:00 18561 467 127000 1701 10
## # … with 115 more rows
Why we don’t use date immediately?
Answer: Actually, you can use date. I
prefer the time index as it provides a consistent way to analyse any
time series. A tricky issue of using date is that the
intercept may not be interpretable. For the time index, it is easy to
understand that the intercept is the observation at time 0, i.e. the
day/month/quarter right before the first one in our time series. For
date, interpreting the intercept will force us to go back
to the birth of Jesus.
- Now we can fit a simple linear regression model as follows
hpi.lm <- lm(index~time,data=housing.ts)
summary(hpi.lm)
##
## Call:
## lm(formula = index ~ time, data = housing.ts)
##
## Residuals:
## Min 1Q Median 3Q Max
## -290.66 -190.74 -6.63 144.37 854.40
##
## Coefficients:
## Estimate Std. Error t value Pr(>|t|)
## (Intercept) 90.8132 38.5253 2.357 0.02 *
## time 18.9983 0.5306 35.803 <2e-16 ***
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
##
## Residual standard error: 214.1 on 123 degrees of freedom
## Multiple R-squared: 0.9124, Adjusted R-squared: 0.9117
## F-statistic: 1282 on 1 and 123 DF, p-value: < 2.2e-16
Comment on the R summary of this model.
Answer: Very high \(R^2\). Both coefficients are highly
significant. Looks good!
We can further visualise the fitted linear model as
visreg(hpi.lm, gg=TRUE) + xlab('Time Index') + ylab('House Price Index')

Comment on the goodness of fit of the linear trend model based on
the visualisation.
Answer: Even with a very high \(R^2\), the plot make me feel worried. A
straight line can not capture the complicated cycles in the house
market.
Perform a standard residual diagnostics for your fitted linear
model by following the steps in Lab C3.
plot(hpi.lm)




Answer: The residuals diagnostics confirm our
concerns on the visualisation. None of the residuals plots looks good.
Strange patterns in all plots suggest that there is a lot of information
hidden in the residuals.
Optional Challenge: Make a log transformation on HPI and fit
another linear model. Compare the R summary with the model without
transformation. Perform the standard residual diagnostics on this
model.
hpi.lm.log <- lm(log(index)~time,data=housing.ts)
summary(hpi.lm.log)
##
## Call:
## lm(formula = log(index) ~ time, data = housing.ts)
##
## Residuals:
## Min 1Q Median 3Q Max
## -0.193098 -0.064345 0.002189 0.056390 0.215112
##
## Coefficients:
## Estimate Std. Error t value Pr(>|t|)
## (Intercept) 6.0198407 0.0165470 363.80 <2e-16 ***
## time 0.0155930 0.0002279 68.42 <2e-16 ***
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
##
## Residual standard error: 0.09195 on 123 degrees of freedom
## Multiple R-squared: 0.9744, Adjusted R-squared: 0.9742
## F-statistic: 4681 on 1 and 123 DF, p-value: < 2.2e-16
visreg(hpi.lm.log,trans = exp,partial=TRUE)

plot(hpi.lm.log)




Answer: \(R^2\)
gets even higher! The visualisation looks slightly better. The residuals
plots are still worrying except for the Q-Q plot.
- A group of talented school students studied HPI before they learned
the log transformation. They noticed that HPI was increasing slowly at
the beginning but increasing faster later. In other words, the increase
in HPI occurs at an increasing rate for each successive time period. The
students suggested that we may use a quadratic function
instead of the linear function to model the trend as follows. \[
mean(y_t)=a+bt+ct^2.
\] But the students didn’t know how to find the coefficients
\(a,b,c\). Can we help?
The answer is YES! Interestingly, this can be done via
lm() easily as follows.
hpi.qm <- lm(index~time+I(time^2), data=housing.ts)
summary(hpi.qm)
##
## Call:
## lm(formula = index ~ time + I(time^2), data = housing.ts)
##
## Residuals:
## Min 1Q Median 3Q Max
## -204.37 -74.09 -11.28 65.60 471.59
##
## Coefficients:
## Estimate Std. Error t value Pr(>|t|)
## (Intercept) 4.925e+02 3.306e+01 14.898 <2e-16 ***
## time 2.311e-02 1.211e+00 0.019 0.985
## I(time^2) 1.506e-01 9.312e-03 16.173 <2e-16 ***
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
##
## Residual standard error: 121.2 on 122 degrees of freedom
## Multiple R-squared: 0.9722, Adjusted R-squared: 0.9717
## F-statistic: 2129 on 2 and 122 DF, p-value: < 2.2e-16
It is important to use I(time^2), not
time^2 if you want to fit a quadratic trend to a time
series.
We can find an additional row in Coefficients: as
I(time^2). There is the estimate for \(c\) as you may expected. The interpretation
of rest is just like what we have done in Lab C2. One interesting thing
is that the estimate for \(b\) becomes
insignificant!
Compare the R summary with that in Step 1. Discuss your
findings.
Answer: \(R^2\)
gets higher just like the transformed model. The quadratic term is
highly significant but the linear term becomes insignificant.
Visualising this model can be done with visreg() in the
usual way, but let’s use the broom library instead to do it
here:
augment(hpi.qm) |> ggplot(aes(x = time ,y=index)) + geom_point() + geom_line() +
geom_line(aes(y=.fitted),col='red') + xlab('Time Index') + ylab('House Price Index')

You may add the confidence band or prediction band by following
Exercise 1 in Lab C3.
Sometimes people prefer making a plot in the calendar time rather
than in the time index. This can be done easily by using
broom as follows. Notice that we add the original data set
housing.ts into augment() and change
x = time to x = date in ggplot().
This is very handy in many cases especially you are fitting a linear
model with transformation.
augment(hpi.qm,housing.ts) |> ggplot(aes(x = date ,y=index)) + geom_point() + geom_line() +
geom_line(aes(y=.fitted),col='red') + xlab('Time') + ylab('House Price Index')

Comment on the goodness of fit of the quadratic trend model based
on the visualisation.
Answer: It is slightly better than the linear model.
But the general pattern in HPI time series still deviate from the curve
frequently.
- Now we have two candidate models from Step 1 and 2 for HPI time
series. A natural question arises: Which model is better?
Both R summary and visualisation can be used. The residual analysis
is another tool to find the better model. While these tools are useful,
we have a more delicate way to compare the above two models performance.
Particularly, the linear model can be regarded as a special case of the
quadratic model. If we set \(c=0\), the
quadratic model reduces to the linear model. If two models can be linked
in such manner, we call these two models are nested.
The simple model is usually called a reduced model
(linear one in this case) and the complicated model is usually called an
extended model (quadratic one in this case).
An ANOVA (ANalysis Of VAriance) test can be used to
compare two nested candidate models as
anova(hpi.lm, hpi.qm)
## Analysis of Variance Table
##
## Model 1: index ~ time
## Model 2: index ~ time + I(time^2)
## Res.Df RSS Df Sum of Sq F Pr(>F)
## 1 123 5636696
## 2 122 1792830 1 3843866 261.57 < 2.2e-16 ***
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
anova() tests if the additional parameter(s) \(c\) in the quadratic model is zero or not,
i.e. the null hypothesis is \(c=0\) and
the p-value is reported by Pr(>F). What does the
output of anova() suggest? If the P-value is small we
can conclude that that Model 2 - the quadratic trend model
is better.
It is easy to find the improvement in goodness of fit from the
quadratic model is significant for HPI time series. In fact, with the
additional coefficient \(c\), the
quadratic trend model (and any extended model) is guaranteed to get
closer to the real time series than the linear trend model(and any
reduced model).
A pure quadratic model as \(y=a+cx^2\) is also nested with the
quadratic model \(y=a+bx+cx^2\). Why?
Use ANOVA to tell which model is better.
Answer: Make sure that you still have
I() in your pure quadratic model. anova()
tells us that the linear term can be excluded for a more concise
model.
hpi.pqm <- lm(index~I(time^2), data=housing.ts)
anova(hpi.pqm, hpi.qm)
## Analysis of Variance Table
##
## Model 1: index ~ I(time^2)
## Model 2: index ~ time + I(time^2)
## Res.Df RSS Df Sum of Sq F Pr(>F)
## 1 123 1792835
## 2 122 1792830 1 5.3509 4e-04 0.9848
However, the quadratic trend model is more complicated than the
linear trend model and the pure quadratic model as it has one more
coefficient. If the improvement is just marginal, we may prefer the
linear model or the pure quadratic model as they are so concise. The
ANOVA test examines if the extended model (quadratic trend model )
explains sufficient variability in \(y\) compared to the reduced model (linear
trend model) at a price of adding one more parameter. We will revisit
this topic in Lab C6.
- Predict HPI for Q3 and Q4 of 2020 with both linear trend model
and quadratic trend model with 95% prediction intervals. Comment on your
predictions.
Answer: One has to figure out that the time indices
for Q3 and Q4 of 2020 are 123 and 124.
newtime <- data.frame(time=c(123,124))
predict(hpi.lm,newdata=newtime)
## 1 2
## 2427.600 2446.598
predict(hpi.qm,newdata=newtime)
## 1 2
## 2773.671 2810.891
The linear trend model is certainly underestimating the HPI. The
quadratic one may do a better job. However, both two models are unable
to quantify the uncertainties in a right way due the highly ill-posed
residuals.
Exercise 3: Addressing Seasonalities in Consumption
Besides the trend extracted by our linear models, another important
feature of time series is the seasonality or
seasonal effects. Many things exhibit seasonal patterns
over different time periods (e.g. months or quarters) of a year. For
example:
Sales of ski equipment are very high during winter and almost
nonexistent during other seasons.
Sales at department stores around Christmas holidays are much
higher than during other times of the year.
Power bills are expected to be higher during winter than in other
seasons.
Enumerate a few more time series with a seasonal
pattern.
Answer: electricity production per month/week/day,
sales of ice creams, etc.
Clearly, seasonality is closely related to the frequency and their
linkages can be expected just by reading the words ‘monthly’, ‘weekly’,
or ‘quarterly’. In fact, in a time series the seasonal pattern for a
particular quarter or month is exactly repeated by the frequency of the
time series.
Obviously, it is necessary to include the seasonal effects in time
series modelling in most cases. Otherwise, the prediction becomes rather
superficial as it only accounts for the long-term trend.
How can we make use of the seasonal effects? We will start by
exploring the data set M2 Consumption from RBNZ.
- First of all, we load
consumption data from the RBNZ
website - see (https://www.rbnz.govt.nz/statistics/m2) for more
information on this time series. We then subset the data, filtering out
the 2020 data and before. We’ll reserve 2020 and 2021 for
prediction.
consumption <- read_csv("https://www.massey.ac.nz/~jcmarsha/161122/data/consumption.csv")
## Rows: 104 Columns: 2
## ── Column specification ────────────────────────────────────────────────────────
## Delimiter: ","
## dbl (1): x_m
## dttm (1): date
##
## ℹ Use `spec()` to retrieve the full column specification for this data.
## ℹ Specify the column types or set `show_col_types = FALSE` to quiet this message.
retail.trade.sales <- consumption |>
filter(date < ymd('2020-01-01'))
retail.trade.sales |>
ggplot(aes(x=date,y=x_m)) + geom_point() + geom_line() +
xlab('Time') + ylab('Retail Trade Sales')

Specify the frequency of this time series.
Answer: 4
We can clearly see an increasing trend over these years in general.
The regular fluctuations of sales within one year suggest strong
seasonal effects. Why? One shall also notice that the seasonal
variability is increasing over time.
- First of all, we can extract a linear trend from this time series as
follows
rts.ts <- retail.trade.sales |> mutate(time=1:n())
rts.lm <- lm(x_m~time,data=rts.ts)
summary(rts.lm)
##
## Call:
## lm(formula = x_m ~ time, data = rts.ts)
##
## Residuals:
## Min 1Q Median 3Q Max
## -1533.6 -848.5 -184.9 664.4 3535.4
##
## Coefficients:
## Estimate Std. Error t value Pr(>|t|)
## (Intercept) 7347.250 221.589 33.16 <2e-16 ***
## time 166.483 3.887 42.84 <2e-16 ***
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
##
## Residual standard error: 1088 on 96 degrees of freedom
## Multiple R-squared: 0.9503, Adjusted R-squared: 0.9498
## F-statistic: 1835 on 1 and 96 DF, p-value: < 2.2e-16
The R output reads pretty good with all coefficient significant and a
very high \(R^2=0.9457\)!
Let’s visualise the model as follows:
visreg(rts.lm, gg=TRUE) + xlab('Time Index') + ylab('Retail Trade Sales')

Oops. A simple line won’t be able to capture the seasonality.
- Let’s perform a standard residual diagnostics on the fitted
model.
plot(rts.lm)




Except for Q-Q plot, rest diagnostic plots look really really weird!
Add a few detailed comments on each plot.
In addition to the above standard diagnostic plots, the
residuals versus time plot is frequently used in the
residual diagnostics of time series modelling as
augment(rts.lm) |>
ggplot(aes(x=time,y=.resid)) + geom_point() + geom_line() +
ggtitle('Residuals versus Time Index') + xlab('Time Index')

The pattern in the residuals versus time plot seems
containing the information on the seasonality! Such pattern actually
suggests that the residuals violate the i.i.d. condition, i.e. the
independently and identically distributed condition. We can conjecture
that the residuals in different seasons is coming from different
distributions and they are correlated in the time order. How can we
check the internal correlation in residuals?
Answer: This one is not straightforward. To discover
the internal correlation in a time series, we need a bit more background
knowledge. Nevertheless, we may conjecture that there is a one-step
correlation in a time series, say \(x_1\) is correlated with \(x_2\), \(x_2\) is correlated with \(x_3\),…,\(x_{t-1}\) is correlated with \(x_t\). Like \((x_i,y_i)\) is paired for computing the
correlation coefficient, we have the pairs \((x_1,x_2)\), \((x_2,x_3)\),…,\(x_{t-1},x_t\) and we can compute the
correlation between \(\{x_t\}\) and
\(\{x_{t+1}\}\). Such a correlation
coefficient within a time series is called
autocorrelation. We can further examine the higher
order (multi-step) autocorrelation. These issues will be explored in the
last two lectures.
set.seed(2020)
tibble(e=rnorm(100)) |> summarise(acf1=cor(e[-1],e[-length(e)]))
## # A tibble: 1 × 1
## acf1
## <dbl>
## 1 0.0988
We further make a box plot to compare the residuals over different
quarters. Here we add the data set rts.ts to
augment() to make sure that we have the access to the
calendar time and we can get the corresponding quarter by
quarter() from lubridate().
augment(rts.lm, rts.ts) |> mutate(quarter=quarter(date)) |>
ggplot(aes(y=.resid,x=quarter)) + geom_boxplot(aes(group=quarter))

Each box in the above boxplot characterises the residuals, i.e. the
deviations from the trend line, at a specific quarter. The residuals
here are a combination of true random errors and seasonal effects.
How can we extract the information on seasonality? A simple idea is
to estimate the seasonal effect at a particular quarter by the mean of
residuals at this quarter. This will add a constant shift to the trend
line for each quarter. The rest deviations will be regarded as the final
residuals.
Try to compute the residuals mean of each quarter by
summarise().
Answer:
augment(rts.lm, rts.ts) |> mutate(quarter=quarter(date)) |> group_by(quarter) |> summarise(resid.mean=mean(.resid))
## # A tibble: 4 × 2
## quarter resid.mean
## <int> <dbl>
## 1 1 -242.
## 2 2 -580.
## 3 3 -534.
## 4 4 1323.
- The above procedures seems work well but tedious. A surprising fact
is that we can complete these jobs with one line by
lm() as
follows. The trick is that we must turn quarter into a
factor (categorical, qualitative) variable via factor() and
include it in the regression formula in lm().
rts.ts.q <- rts.ts |> mutate(quarter=factor(quarter(date)))
rts.lm.q <- lm(x_m~time+quarter,data=rts.ts.q)
summary(rts.lm.q)
##
## Call:
## lm(formula = x_m ~ time + quarter, data = rts.ts.q)
##
## Residuals:
## Min 1Q Median 3Q Max
## -1075.9 -636.0 -136.9 492.4 2224.5
##
## Coefficients:
## Estimate Std. Error t value Pr(>|t|)
## (Intercept) 7117.490 203.721 34.937 < 2e-16 ***
## time 166.239 2.706 61.433 < 2e-16 ***
## quarter2 -338.197 218.743 -1.546 0.125
## quarter3 -291.888 216.528 -1.348 0.181
## quarter4 1564.593 216.545 7.225 1.36e-10 ***
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
##
## Residual standard error: 757.7 on 93 degrees of freedom
## Multiple R-squared: 0.9767, Adjusted R-squared: 0.9757
## F-statistic: 972.8 on 4 and 93 DF, p-value: < 2.2e-16
We have obtained a few more rows in Coefficients:,
including
quarter2,quarter3,quarter4 with
Estimate, Std. Error, etc.
Looks good! Oh, wait, where is quarter1?
In fact, the seasonal effect at the first quarter is included in
(Intercept)\(=True.Intercept +
Seasonal.Effect.of.Q1\)! More importantly, Estimates
for the rest three quarters is not the seasonal effects at corresponding
quarters. They are instead the difference between \(Seasonal.Effect.of.Q1\) and \(Seasonal.Effect.of.Q2.Q3.and .Q4\)
Therefore, we can write the follow equation for our fitted linear
model as \[
mean(Retail.Trade.Sales)=7240.8 + 162.6 \times Time + D_Q, Q=1,2,3,4.
\] where \(D_1=0\), \(D_2=-330.0\),\(D_3=-278.0\) and \(D_4=1521.8\).
More details on this strange issue after adding factor variables in
the linear model will be revealed in the coming Lectures C5 and C6, and
corresponding Labs.
Visualise our model with both linear trend and seasonal effects:
augment(rts.lm.q) |> ggplot(aes(x = time ,y=x_m)) + geom_point() + geom_line(alpha=0.3) +
geom_line(aes(y=.fitted),col='red') + xlab('Time Index') + ylab('Retail Trade Sales')

Now the fitted curve looks much more reasonable! Do you think
there is any room for further improvement?
Answer: Yes. We can see that the seasonal variations
are increasing over time. A log transform can help stabilise the
increasing variability.
Perform a time series residual analysis on your fitted model.
Remember the residuals versus time plot.
Answer:
plot(rts.lm.q)




augment(rts.lm.q) |> ggplot(aes(x=time,y=.resid)) + geom_point()
Not really good! Still have some obvious patterns in the residuals. None
of the plots pass the diagonostics. Need some better models to address
it.
Try to visualise the model with visreg(). What do
you find from the plots of visreg().
Answer:
visreg(rts.lm.q)


visreg() splits the effect of trend and seasonality to
two separate graphs.
- We can predict the sales in 2020 as follows
newtime <- data.frame(time=nrow(rts.ts)+(1:4),quarter=factor(1:4))
augment(rts.lm.q,newdata=newtime)
## # A tibble: 4 × 3
## time quarter .fitted
## <int> <fct> <dbl>
## 1 99 1 23575.
## 2 100 2 23403.
## 3 101 3 23616.
## 4 102 4 25638.
By adding newdata=newtime in augment(), we
can get the prediction immediately just like predict() and
the results are organised well in a tibble.
Compute the mean square errors of your prediction in
2020.
rts.2020 <- consumption |>
filter(date >= ymd('2020-01-01') & date <= ymd('2021-01-01') ) |>
cbind(augment(rts.lm.q,newdata=newtime))
rts.2020
## date x_m time quarter .fitted
## 1 2020-03-31 24794 99 1 23575.17
## 2 2020-06-30 20052 100 2 23403.21
## 3 2020-09-30 25545 101 3 23615.76
## 4 2020-12-31 28505 102 4 25638.48
rts.2020 |> summarise(mse=mean((x_m-.fitted)^2))
## mse
## 1 6163773
The predictions are not really good as the sales have been
underestimated consistently.
What will happen if we use the fitted model to predict the sales
in 2021?
Answer: We won’t get good predictions given the
shock of COVID-19
Optional Challenge: Will log transform improve your model
fit? Try it!
Answer:
rts.lm.q.log <- lm(log(x_m)~time+quarter,data=rts.ts.q)
summary(rts.lm.q.log)
##
## Call:
## lm(formula = log(x_m) ~ time + quarter, data = rts.ts.q)
##
## Residuals:
## Min 1Q Median 3Q Max
## -0.076174 -0.026552 -0.008532 0.015924 0.112484
##
## Coefficients:
## Estimate Std. Error t value Pr(>|t|)
## (Intercept) 9.0461066 0.0112518 803.968 < 2e-16 ***
## time 0.0110025 0.0001495 73.616 < 2e-16 ***
## quarter2 -0.0207053 0.0120815 -1.714 0.0899 .
## quarter3 -0.0200914 0.0119592 -1.680 0.0963 .
## quarter4 0.0951143 0.0119601 7.953 4.26e-12 ***
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
##
## Residual standard error: 0.04185 on 93 degrees of freedom
## Multiple R-squared: 0.9836, Adjusted R-squared: 0.9829
## F-statistic: 1391 on 4 and 93 DF, p-value: < 2.2e-16
augment(rts.lm.q.log,newdata=newtime) |> mutate(.pred=exp(.fitted))
## # A tibble: 4 × 4
## time quarter .fitted .pred
## <int> <fct> <dbl> <dbl>
## 1 99 1 10.1 25219.
## 2 100 2 10.1 24976.
## 3 101 3 10.1 25267.
## 4 102 4 10.3 28666.
Predictions are much better! You can check residuals and
visualisation accordingly.
LS0tCnRpdGxlOiAiV29ya3Nob3AgQzA0OiBUaW1lIFNlcmllcyBSZWdyZXNzaW9uIgpvdXRwdXQ6CiAgaHRtbF9kb2N1bWVudDogCiAgICB0b2M6IHllcwogICAgY29kZV9kb3dubG9hZDogdHJ1ZQotLS0KCmBgYHtyLCBtZXNzYWdlPUZBTFNFfQpsaWJyYXJ5KHRpZHl2ZXJzZSkKbGlicmFyeSh2aXNyZWcpCmxpYnJhcnkoYnJvb20pCmxpYnJhcnkobHVicmlkYXRlKQpgYGAKCiMjIEludHJvZHVjdGlvbjogVGltZSBTZXJpZXMgLSBCYXNpYyBDb25jZXB0cwoKSW4gdGhpcyB3b3Jrc2hvcCwgd2Ugd2lsbCBpbnRyb2R1Y2UgYSBuZXcgY29uY2VwdCBpbiBzdGF0aXN0aWNzIC0gKip0aW1lIHNlcmllcyoqLiBUaW1lIHNlcmllcyBhcmUgb2JzZXJ2YXRpb25zIG9yIG1lYXN1cmVtZW50cyB0aGF0IGFyZSBpbmRleGVkIGFjY29yZGluZyB0byB0aW1lICoqcmVndWxhcmx5KiouIEV4YW1wbGVzIG9mIHRpbWUgc2VyaWVzIGFyZSB0aGUgbnVtYmVyIG9mIG5ld2x5IGNvbmZpcm1lZCBDT1ZJRC0xOSBjYXNlcyAqKnBlciBkYXkqKiBpbiBOWiwgdGhlIGdsb2JhbCBzYWxlcyBvZiBpUGhvbmUgKipwZXIgbW9udGgqKiwgdGhlICoqcXVhcnRlcmx5KiogdW5lbXBsb3ltZW50IHJhdGUgaW4gTlosIGFuZCB0aGUgKipkYWlseSoqIGNsb3NpbmcgdmFsdWUgb2YgdGhlIE5aWDUwLgoKKkdpdmUgYSBmZXcgZXhhbXBsZXMgb24gdGltZSBzZXJpZXMuKgoKSXQgaXMgZWFzeSB0byBub3RpY2UgdGhhdCBhbGwgdGhlIHRpbWUgc2VyaWVzIGRhdGEgYXJlIGNvbGxlY3RlZCB3aXRoIGEgbmF0dXJhbCB0ZW1wb3JhbCBvcmRlcmluZy4gSW4gYW4gYWJzdHJhY3Qgc2Vuc2UsIHdlIHJlY29yZCBhIHRpbWUgc2VyaWVzIGxpa2UgJHlfMSx5XzIsLi4uLHlfVCQsIG9yIGp1c3QgJHlfdCx0PTEsLi4uLFQkIHdoZXJlICR0PTEsLi4uLFQkIGlzIHRoZSAqKnRpbWUgaW5kZXgqKi4gCgpXaHkgZG9lcyB0aGF0IG1ha2UgdGhpbmdzIGRpZmZlcmVudD8KCldoYXQgaGFwcGVuZWQgdG9kYXkgbWF5IGFmZmVjdCB0b21vcnJvdydzIHdvcmxkIGJ1dCBub3RoaW5nIGNvdWxkIGNoYW5nZSB0aGUgb3V0Y29tZXMgb2YgeWVzdGVyZGF5LiBUaGUgdGltZSBzZXJpZXMgaGFzIGEgbmF0dXJhbCB0ZW1wb3JhbCBvcmRlcmluZyB3aGljaCBpbXBsaWVzIGEgKip1bmlkaXJlY3Rpb25hbCoqIGNhdXNhbCByZWxhdGlvbnNoaXAuIFRoZSBzdHJ1Y3R1cmUgb2YgdGltZSBzZXJpZXMgaW1wbGllZCBieSB0aGlzIG9yZGVyaW5nIGRpc3Rpbmd1aXNoZXMgaXQgZnJvbSBvdGhlciB0eXBlcyBvZiBkYXRhIHRoYXQgYXJlIGNvbW1vbmx5IGFuYWx5c2VkLiAKCkluIGFkZGl0aW9uIHRvIHRoZSB0aW1lIGluZGV4LCBpdCBpcyBpbXBvcnRhbnQgdG8gbm90aWNlIHRoYXQgZGF0YSBwb2ludHMgaW4gYSB0aW1lIHNlcmllcyBhcmUgY29sbGVjdGVkICoqcmVndWxhcmx5Kiogb3ZlciB0aW1lLiBJbiB0aGUgYmVnaW5uaW5nIG9mIHRoaXMgd29ya3Nob3AsIHdlIGhpZ2hsaWdodCB0aGUgd29yZHMsICdwZXIgZGF5JywgJ3BlciBtb250aCcsICdxdWFydGVybHknLCBhbmQgJ2RhaWx5Jy4gVGhlc2Ugd29yZHMgZGVmaW5lIHRoZSAqKmZyZXF1ZW5jeSoqIG9yICoqcGVyaW9kaWNpdHkqKiBvZiB0aW1lIHNlcmllcyAqKndpdGhpbiBvbmUgeWVhcioqLCBpLmUuIHRoZSBudW1iZXIgb2Ygb2JzZXJ2YXRpb25zIHdpdGhpbiBhIGZpeGVkIHRpbWUgcGVyaW9kLiBUaGUgZnJlcXVlbmN5IGlzIDEyIGZvciBtb250aGx5IHRpbWUgc2VyaWVzIGFuZCAzNjUgZm9yIGRhaWx5IHRpbWUgc2VyaWVzIHdpdGhpbiBvbmUgeWVhci4KCipMaXN0IGEgZmV3IG1vcmUgYWRqZWN0aXZlcy9hZHZlcmJzIGRlc2NyaWJpbmcgdGhlIGZyZXF1ZW5jeS4qCgoqKkFuc3dlcioqOiB3ZWVrbHksIGhvdXJseSwgYW5udWFsbHkgLi4uIAoKKldoYXQgaXMgdGhlIGZyZXF1ZW5jeSBvZiB0aGUgd2luZCBzcGVlZCBpbiBXZWxsaW5ndG9uIHBlciBob3VyIHdpdGhpbiBvbmUgZGF5PyoKCioqQW5zd2VyKio6IDI0CgoKKldoYXQgaXMgdGhlIGZyZXF1ZW5jeSBvZiB0aGUgZGFpbHkgcHJlY2lwaXRhdGlvbiBpbiBBdWNrbGFuZCB3aXRoaW4gb25lIHdlZWs/KgoKKipBbnN3ZXIqKjogNwoKVGhlIGFib3ZlIGNvbmNlcHRzIHdpbGwgYmUgcHJldHR5IGVhc3kgdG8gdW5kZXJzdGFuZCBieSBleHBsb3Jpbmcgc29tZSByZWFsIHRpbWUgc2VyaWVzLiBTbyB3ZSB3aWxsIHBlcmZvcm0gYW4gZXhwbG9yYXRvcnkgZGF0YSBhbmFseXNpcyBvbiBOWiBob3VzaW5nIGRhdGEgY29sbGVjdGVkIGJ5IHRoZSBSZXNlcnZlIEJhbmsgb2YgTmV3IFplYWxhbmQoUkJOWikgaW4gRXhlcmNpc2UgMS4gSW50ZXJlc3RpbmdseSwgdGltZSBjYW4gc3RpbGwgYmUgbW9kZWxsZWQgYnkgb3VyIGxpbmVhciBtb2RlbCBlYXNpbHkgYW5kIHRoZSBmaXR0ZWQgbGluZWFyIG1vZGVsIGNhbiBiZSBmdXJ0aGVyIHVzZWQgdG8gbWFrZSB0aW1lIHNlcmllcyBwcmVkaWN0aW9ucy4gSW4gRXhlcmNpc2UgMiBhbmQgMywgd2Ugd2lsbCBsZWFybiBob3cgdG8gYW5hbHlzZSB0aW1lIHNlcmllcyB2aWEgbGluZWFyIG1vZGVscy4KCllvdSBmaXJzdCBuZWVkIHRvIGluc3RhbGwgYFJCTlpgIGFuZCBgamFuaXRvcmAgdXNpbmcgdGhlIGBQYWNrYWdlc2AgbWVudSBpbiB0aGUgYm90dG9tIHJpZ2h0LiBKdXN0IGNsaWNrIHRoZSBpbnN0YWxsIGJ1dHRvbiBhbmQgdHlwZSBpbiBgUkJOWmAsIHRoZW4gaW5zdGFsbC4gT3IgeW91IGNhbiBpbnN0YWxsIHRoZSBwYWNrYWdlIHdpdGggUiBjb21tYW5kIGBpbnN0YWxsLnBhY2thZ2VzKClgLiBSU3R1ZGlvIG1heSBoYXZlIHByb21wdGVkIHlvdSBhYm91dCBpbnN0YWxsaW5nIHRoZW0gd2hlbiB5b3UgbG9hZGVkIHRoaXMgZmlsZS4KCiMjIEV4ZXJjaXNlIDE6IEV4cGxvcmluZyBOWiBIb3VzZSBQcmljZQoKQmVmb3JlIHdlIHN0YXJ0IHRoZSBmb3JtYWwgc3RhdGlzdGljYWwgYW5hbHlzaXMgb24gYW55IHJlYWwgdGltZSBzZXJpZXMsIGl0IGlzIGVzc2VudGlhbCB0byBsb2FkIHRoZSBkYXRhIGZpcnN0IGFuZCB0aGVuIHR1cm4gdGhlIGRhdGEgdG8gYSBzdWl0YWJsZSBmb3JtYXQgcmVhZHkgZm9yIGFueSBmdXJ0aGVyIGFuYWx5c2lzLiAKCjEuIFRoZSBmb2xsb3dpbmcgUiBjb2RlIGNodW5rIHJldHJpZXZlcyB0aGUgcXVhcnRlcmx5IHRpbWUgc2VyaWVzIG9uIGhvdXNpbmcgZnJvbSB0aGUgd2Vic2l0ZSBvZiB0aGUgUmVzZXJ2ZSBCYW5rIG9mIE5ldyBaZWFsYW5kIChSQk5aKSAoaHR0cHM6Ly93d3cucmJuei5nb3Z0Lm56L3N0YXRpc3RpY3MpLgoKYGBge3J9CmhvdXNpbmcgPC0gcmVhZF9jc3YoImh0dHBzOi8vd3d3Lm1hc3NleS5hYy5uei9+amNtYXJzaGEvMTYxMTIyL2RhdGEvaG91c2luZy5jc3YiKQpob3VzaW5nCmBgYAoKYGhvdXNpbmdgIGRhdGEgY29udGFpbnMgb25lIGNvbHVtbiBmb3IgdGltZSBpbmZvcm1hdGlvbiAoYGRhdGVgKSBhbmQgZm91ciBjb2x1bW5zIGZvciBkaWZmZXJlbnQgaG91c2luZyBpbmZvcm1hdGlvbiwgaS5lLiB0b3RhbCB2YWx1ZSBvZiBob3VzaW5nIHN0b2NrIGBtYCwgcmVzaWRlbnRpYWwgaW52ZXN0bWVudCBgcmVhbF9tYCwgaG91c2UgcHJpY2UgaW5kZXggKEhQSSkgYGluZGV4YCwgYW5kIHRoZSBudW1iZXIgb2YgaG91c2Ugc2FsZXMgYG51bWJlcmAuCgoqU3BlY2lmeSB0aGUgZnJlcXVlbmN5IG9mIGBob3VzaW5nYCBkYXRhLioKCioqQW5zd2VyKio6IDQKCjIuIExldCdzIG1ha2UgYSBzY2F0dGVyLWxpbmUgcGxvdCBvZiBob3VzZSBwcmljZSBpbmRleCBvdmVyIHRpbWUuIFlvdSBtYXkgY3VzdG9taXNlIGl0IGEgYml0IHRvIGdldCBhIGJldHRlciBhcHBlYXJhbmNlLiAKCmBgYHtyfQpob3VzaW5nIHw+IGdncGxvdChhZXMoeD1kYXRlLHk9aW5kZXgpKSArIGdlb21fcG9pbnQoKSArIGdlb21fbGluZSgpICsKICB4bGFiKCdUaW1lJykgKyB5bGFiKCdIb3VzZSBQcmljZSBJbmRleCcpICsgZ2d0aXRsZSgnV2lsbCBLaXdpIGFmZm9yZCB0aGUgaG91c2UgcHJpY2U/JykKYGBgCgoKVGhlIHdhcm5pbmcgbWVzc2FnZSBgUmVtb3ZlZCAxIHJvd3MgY29udGFpbmluZyBtaXNzaW5nIHZhbHVlcyAoZ2VvbV9wb2ludCkuYCBpbiB5b3VyIEhUTUwgcmVtaW5kcyB5b3UgdGhhdCB0aGVyZSBpcyBhbiBgTkFgIHZhbHVlIGluIG91ciBkYXRhLiBMZXQncyByZW1vdmUgdGhpcyBOQSB2YWx1ZSBmcm9tIHRoZSB0aWJibGUgdmlhIGBkcm9wX25hKClgIGFzCmBgYHtyfQpob3VzaW5nIDwtIGhvdXNpbmcgfD4gZHJvcF9uYSgpCmBgYAoKV2UgY2FuIHNlZSBjbGVhcmx5IG92ZXIgdGhlIGxhc3QgdGhyZWUgZGVjYWRlcyBIUEkgd2FzIGluY3JlYXNpbmcgaW4gbW9zdCB5ZWFycy4gVGhlcmUgYXJlIGEgZmV3IGV4Y2VwdGlvbnMsIGkuZS4sIGFyb3VuZCAxOTk4LCAyMDA4LCAyMDExLiAqV2hhdCBoYXBwZW5lZCBpbiB0aGVzZSB5ZWFycz8qCgoqKkFuc3dlcioqOiAgdGhlIEFzaWFuIEZpbmFuY2lhbCBjcmlzaXMgaW4gMTk5NywgdGhlIGdsb2JhbCBmaW5hbmNpYWwgY3Jpc2lzIG9mIDIwMDfigJMyMDA4LCBhbmQgdGhlIDIwMTEgQ2hyaXN0Y2h1cmNoIGVhcnRocXVha2UuCgpBbm90aGVyIGZlYXR1cmUgb2YgdGhpcyB0aW1lIHNlcmllcyBpcyB0aGF0IEhQSSBzZWVtcyBpbmNyZWFzZSB3aXRoIGRpZmZlcmVudCBzcGVlZHMgb3ZlciBkaWZmZXJlbnQgZGVjYWRlcy4gSW4gMTk5MHMsIEhQSSB0ZW5kcyB0byBiZSBmbGF0dGVuIG9yIGp1c3QgaW5jcmVhc2UgbW9kZXJhdGVseS4gQnV0IHRoZSBmaXJzdCB0d28gZGVjYWRlcyBpbiAyMSBjZW50dXJ5IGxvb2sgcXVpdGUgY3JhenkuIFRoZSBhZmZvcmRhYmlsaXR5IG9mIE5aIGhvdXNlIGhhcyBiZWNvbWUgYSBjb3JlIHNvY2lhbCBpc3N1ZS4gR2VuZXJhbGx5IHNwZWFraW5nLCBIUEkgc2hvd3MgYW4gaW5jcmVhc2luZyAqKnRyZW5kKiogb3ZlciB0aGUgcGFzdCB0aGlydHkgeWVhcnMuIAoKV2hhdCB3aWxsIGhhcHBlbiBpbiB0aGUgbmV4dCBkZWNhZGUgd2l0aCB0aGUgc2hvY2sgb2YgQ09WSUQtMTk/IFdpbGwgdGhlIGhvdXNlIHByaWNlIGluIE5aIGJlY29tZSBtb3JlIGFmZm9yZGFibGUgaW4gdGhlIGZ1dHVyZT8gVG8gYW5zd2VyIHRoZXNlIHF1ZXN0aW9ucywgaXQgaXMgaW1wb3J0YW50IGZvciB1cyB0byBleHRyYWN0IHRoZSBsb25nIHRlcm0gKip0cmVuZCoqIGZyb20gdGhpcyB0aW1lIHNlcmllcyBhbmQgbWFrZSBwcmVkaWN0aW9ucyBvbiB0aGUgZnV0dXJlIEhQSS4gCgozLiAqTWFrZSBzY2F0dGVyLWxpbmUgcGxvdCBmb3IgdGhlIHJlc3QgdGhyZWUgdmFyaWFibGVzIGluIGBob3VzaW5nYC4gQnJpZWZseSBzdW1tYXJpc2UgdGhlaXIgZmVhdHVyZXMuKgoKKipBbnN3ZXIqKjoKCmBgYHtyfQpob3VzaW5nIHw+IGdncGxvdChhZXMoeD1kYXRlLHk9bnVtYmVyKSkgKyBnZW9tX3BvaW50KCkgKyBnZW9tX2xpbmUoKSArCiAgeGxhYignVGltZScpICsgeWxhYignTnVtYmVyIG9mIEhvdXNlIFNhbGVzJykgKyBnZ3RpdGxlKCdIb3cgbWFueSBob3VzZXMgYXJlIHNvbGQgaW4gdGhlIHBhc3QgeWVhcnM/JykKYGBgCgpObyBjbGVhciB0cmVuZCBvciBzZWFzb25hbGl0eSBjYW4gYmUgb2JzZXJ2ZWQgaGVyZS4gQnV0IHRoZSBwYXR0ZXJuIGNhbiBiZSBsaW5rZWQgdG8gSFBJLiBXaGVuIHRoZSBob3VzZSBwcmljZSBpcyBpbmNyZWFzaW5nLCB0aGUgbnVtYmVyIG9mIGhvdXNlIHNhbGVzIHRlbmRzIHRvIGJlIGhpZ2guIEhvd2V2ZXIsIHdoZW4gdGhlIGhvdXNlIHByaWNlIGlzIGZhdHRlbiwgdGhlIG51bWJlciBvZiBob3VzZSBzYWxlcyB0ZW5kcyB0byBiZSBsb3cuIE9uZSBjYW4gYWxzbyBleHBlY3QgdGhlIGludGVybmFsIGNvcnJlbGF0aW9uIGJldHdlZW4gc3VjY2Vzc2l2ZSBudW1iZXJzIGdpdmVuIHRoZSBmcmVxdWVudCBzaG9ydCB0ZXJtIG1vdmVtZW50cyBpbiB0aGlzIHRpbWUgc2VyaWVzIChpbmNyZWFzaW5nIG9yIGRlY3JlYXNpbmcgd2l0aGluIHNldmVyYWwgc3VjY2Vzc2l2ZSBxdWFydGVycykuCgpgYGB7cn0KaG91c2luZyB8PiBnZ3Bsb3QoYWVzKHg9ZGF0ZSx5PW0pKSArIGdlb21fcG9pbnQoKSArIGdlb21fbGluZSgpICsKICB4bGFiKCdUaW1lJykgKyB5bGFiKCdUb3RhbCBWYWx1ZSBvZiBIb3VzaW5nIFN0b2NrJykgKyBnZ3RpdGxlKCdJcyBob3VzaW5nIG1hcmtldCBhbHdheXMgcHJvc3BlcmluZz8nKQpgYGAKCkFsbW9zdCBzYW1lIGZlYXR1cmVzIGFzIHdlIGNhbiBzZWUgZnJvbSBIUEkuIEp1c3Qgbm90aWNlIHRoYXQgdGhlIHRvdGFsIHZhbHVlIG9mIGhvdXNpbmcgc3RvY2sgY2FuIGJlIHJvdWdobHkgY2FsY3VsYXRlZCBieSB0aGUgYXZlcmFnZSBob3VzZSBwcmljZSB0aW1lcyB0aGUgdG90YWwgbnVtYmVyIG9mIGhvdXNlcy4gVGhlIG5ld2x5IGJ1aWx0IGhvdXNlcyBtYWtlIHRoZSBkZXByZXNzaW9uIGNhdXNlZCBieSB0aGUgQXNpYW4gRmluYW5jaWFsIGNyaXNpcyBpbiAxOTk3IGFuZCB0aGUgMjAxMSBDaHJpc3RjaHVyY2ggZWFydGhxdWFrZSBsZXNzIG9idmlvdXMuIAoKYGBge3J9CmhvdXNpbmcgfD4gZ2dwbG90KGFlcyh4PWRhdGUseT1yZWFsX20pKSArIGdlb21fcG9pbnQoKSArIGdlb21fbGluZSgpICsKICB4bGFiKCdUaW1lJykgKyB5bGFiKCdSZXNpZGVudGlhbCBJbnZlc3RtZW50JykgKyBnZ3RpdGxlKCcKUmVzaWRlbnRpYWwgSW52ZXN0bWVudCBPcHBvcnR1bml0eSEnKQpgYGAKClRoZSByZXNpZGVudGlhbCBpbnZlc3RtZW50IGhhcyBhIHNpbWlsYXIgZ2VuZXJhbCBwYXR0ZXJuIGFzIHRoZSBudW1iZXIgb2YgaG91c2Ugc2FsZXMgYXMgaXQgY29udHJpYnV0ZXMgYSBjZXJ0YWluIHBvcnRpb24gdG8gdGhlIHdob2xlIGhvdXNpbmcgbWFya2V0IChhbm90aGVyIGltcG9ydGFudCBjb250cmlidXRvciBpcyB0aGUgZmlyc3QgaG9tZSBidXllci4pIEhvd2V2ZXIsIHRoZSBkZXRhaWxzIG9mIHJlc2lkZW50aWFsIGludmVzdG1lbnQgYXJlIGRpZmZlcmVudCBmcm9tIHRoZSBudW1iZXIgb2YgaG91c2Ugc2FsZXMuIFdlIHNlZSBtYW55IHppZ3phZyBzZWdtZW50cyBpbiB0aGlzIHRpbWUgc2VyaWVzIGVzcGVjaWFsbHkgaW4gdGhlIGZpcnN0IHR3byBkZWNhZGVzLiBUaGlzIG1heSByZWZsZWN0IHRoZSBzZWxmLXJlZ3VsYXRvcnkgcmF0aW9uYWxpdHkgb2YgdGhlIGludmVzdG9ycyB3aG8gbWF5IG5vdCBmb2xsb3cgdGhlIHRyZW5kIG9mIGFuIG92ZXItaGVhdGVkIGhvdXNpbmcgbWFya2V0LiAqT0ssIEkgaGF2ZSB0byBzYXkgdGhhdCBtYXliZSB0aGUgaG91c2luZyBtYXJrZXQgaW4gb3VyIGNvdW50cnkgYmVjb21lcyBhIGxpdHRsZSBjcmF6eSBpbiB0aGUgcmVjZW50IHllYXJzLiogKipUaGUgbGFzdCBzZW50ZW5jZSBpcyBwdXJlbHkgYSBwZXJzb25hbCBjb21tZW50LioqICAgCgoKIyMgRXhlcmNpc2UgMjogQ2FwdHVyaW5nIHRoZSBUcmVuZCBvZiBIb3VzZSBQcmljZQoKVGhlIHRpbWUgaW5kZXggJHQ9MSwyLDMsLi4uLFQkIGltcGxpZWQgYnkgdGhlIG5hdHVyYWwgdGVtcG9yYWwgb3JkZXJpbmcgaXMgYWx3YXlzIGluY3JlYXNpbmcuIEEgdmVyeSBzaW1wbGUgaWRlYSBpcyB0byBjb25zaWRlciB0aGUgcGFpcnMgJCgxLHlfMSkkLCAkKDIseV8yKSQsLi4uLCQoVCx5X1QpJCBqdXN0IGxpa2UgdGhlIHBhaXJzIG9mIG9ic2VydmF0aW9uICQoeF9pLHlfaSksaT0xLDIsLi4uLG4kIGluIHRoZSBsaW5lYXIgbW9kZWwuIAoKSWYgdGhlIHVuZGVybHlpbmcgdHJlbmQgb2YgYSB0aW1lIHNlcmllcyBzaG93cyBlaXRoZXIgaW5jcmVhc2luZyBvciBkZWNyZWFzaW5nIHBhdHRlcm4gY29uc2lzdGVudGx5LCBpdCBpcyBuYXR1cmFsIHRvIGxpbmsgaXQgd2l0aCB0aGUgdGltZSBpbmRleCBvZiBlYWNoIG9ic2VydmF0aW9uLiAqV2h5PyogCgoqKkFuc3dlcioqOiBUaGUgY29ycmVsYXRpb24gY29lZmZpY2llbnQgd2lsbCBjb25maXJtIHlvdXIgaW50dWl0aW9uLiAgCgpNb3Jlb3ZlciwgaWYgdGhlIHVuZGVybHlpbmcgdHJlbmQgbG9va3MgbGluZWFyLCBhIHNpbXBsZSBsaW5lYXIgbW9kZWwgbGlrZSBgeX50YCBiZWNvbWVzIGEgcGVyZmVjdCBjYW5kaWRhdGUgdG8gZGlzY292ZXIgdGhlIHVuZGVybHlpbmcgdHJlbmQgaW4gYSB0aW1lIHNlcmllcy4KCkFueXdheSwgbGV0J3MgdHJ5IGl0IG9uIEhQSSEgQmVmb3JlIGZpdHRpbmcgb3VyIGxpbmVhciBtb2RlbCwgaXQgaXMgbmVjZXNzYXJ5IHRvIGV4cGFuZCBvdXIgdGliYmxlIGJ5IGFkZGluZyB0aGUgdGltZSBpbmRpY2VzICQxLDIsMywuLi4kIGFzIGZvbGxvd3MuCgpgYGB7cn0KaG91c2luZy50cyA8LSBob3VzaW5nIHw+IG11dGF0ZSh0aW1lPTE6bigpKQpob3VzaW5nLnRzCmBgYAoKKldoeSB3ZSBkb24ndCB1c2UgYGRhdGVgIGltbWVkaWF0ZWx5PyoKCioqQW5zd2VyKio6IEFjdHVhbGx5LCB5b3UgY2FuIHVzZSBgZGF0ZWAuIEkgcHJlZmVyIHRoZSB0aW1lIGluZGV4IGFzIGl0IHByb3ZpZGVzIGEgY29uc2lzdGVudCB3YXkgdG8gYW5hbHlzZSBhbnkgdGltZSBzZXJpZXMuIEEgdHJpY2t5IGlzc3VlIG9mIHVzaW5nIGBkYXRlYCBpcyB0aGF0IHRoZSBpbnRlcmNlcHQgbWF5IG5vdCBiZSBpbnRlcnByZXRhYmxlLiBGb3IgdGhlIHRpbWUgaW5kZXgsIGl0IGlzIGVhc3kgdG8gdW5kZXJzdGFuZCB0aGF0IHRoZSBpbnRlcmNlcHQgaXMgdGhlIG9ic2VydmF0aW9uIGF0IHRpbWUgMCwgaS5lLiB0aGUgZGF5L21vbnRoL3F1YXJ0ZXIgcmlnaHQgYmVmb3JlIHRoZSBmaXJzdCBvbmUgaW4gb3VyIHRpbWUgc2VyaWVzLiBGb3IgYGRhdGVgLCBpbnRlcnByZXRpbmcgdGhlIGludGVyY2VwdCB3aWxsIGZvcmNlIHVzIHRvIGdvIGJhY2sgdG8gdGhlIGJpcnRoIG9mIEplc3VzLiAKCjEuIE5vdyB3ZSBjYW4gZml0IGEgc2ltcGxlIGxpbmVhciByZWdyZXNzaW9uIG1vZGVsIGFzIGZvbGxvd3MKYGBge3J9CmhwaS5sbSA8LSBsbShpbmRleH50aW1lLGRhdGE9aG91c2luZy50cykKc3VtbWFyeShocGkubG0pCmBgYAoKKkNvbW1lbnQgb24gdGhlIFIgc3VtbWFyeSBvZiB0aGlzIG1vZGVsLioKCioqQW5zd2VyKio6ICBWZXJ5IGhpZ2ggJFJeMiQuIEJvdGggY29lZmZpY2llbnRzIGFyZSBoaWdobHkgc2lnbmlmaWNhbnQuIExvb2tzIGdvb2QhCgpXZSBjYW4gZnVydGhlciB2aXN1YWxpc2UgdGhlIGZpdHRlZCBsaW5lYXIgbW9kZWwgYXMgCmBgYHtyfQp2aXNyZWcoaHBpLmxtLCBnZz1UUlVFKSArIHhsYWIoJ1RpbWUgSW5kZXgnKSArIHlsYWIoJ0hvdXNlIFByaWNlIEluZGV4JykKYGBgCgoqQ29tbWVudCBvbiB0aGUgZ29vZG5lc3Mgb2YgZml0IG9mIHRoZSBsaW5lYXIgdHJlbmQgbW9kZWwgYmFzZWQgb24gdGhlIHZpc3VhbGlzYXRpb24uKgoKKipBbnN3ZXIqKjogRXZlbiB3aXRoIGEgdmVyeSBoaWdoICRSXjIkLCB0aGUgcGxvdCBtYWtlIG1lIGZlZWwgd29ycmllZC4gQSBzdHJhaWdodCBsaW5lIGNhbiBub3QgY2FwdHVyZSB0aGUgY29tcGxpY2F0ZWQgY3ljbGVzIGluIHRoZSBob3VzZSBtYXJrZXQuCgoqUGVyZm9ybSBhIHN0YW5kYXJkIHJlc2lkdWFsIGRpYWdub3N0aWNzIGZvciB5b3VyIGZpdHRlZCBsaW5lYXIgbW9kZWwgYnkgZm9sbG93aW5nIHRoZSBzdGVwcyBpbiBMYWIgQzMuKgoKYGBge3J9CnBsb3QoaHBpLmxtKQpgYGAKCioqQW5zd2VyKio6IFRoZSByZXNpZHVhbHMgZGlhZ25vc3RpY3MgY29uZmlybSBvdXIgY29uY2VybnMgb24gdGhlIHZpc3VhbGlzYXRpb24uIE5vbmUgb2YgdGhlIHJlc2lkdWFscyBwbG90cyBsb29rcyBnb29kLiBTdHJhbmdlIHBhdHRlcm5zIGluIGFsbCBwbG90cyBzdWdnZXN0IHRoYXQgdGhlcmUgaXMgYSBsb3Qgb2YgaW5mb3JtYXRpb24gaGlkZGVuIGluIHRoZSByZXNpZHVhbHMuIAoKKipPcHRpb25hbCBDaGFsbGVuZ2U6IE1ha2UgYSBsb2cgdHJhbnNmb3JtYXRpb24gb24gSFBJIGFuZCBmaXQgYW5vdGhlciBsaW5lYXIgbW9kZWwuIENvbXBhcmUgdGhlIFIgc3VtbWFyeSB3aXRoIHRoZSBtb2RlbCB3aXRob3V0IHRyYW5zZm9ybWF0aW9uLiBQZXJmb3JtIHRoZSBzdGFuZGFyZCByZXNpZHVhbCBkaWFnbm9zdGljcyBvbiB0aGlzIG1vZGVsLioqIAoKYGBge3J9CmhwaS5sbS5sb2cgPC0gbG0obG9nKGluZGV4KX50aW1lLGRhdGE9aG91c2luZy50cykKc3VtbWFyeShocGkubG0ubG9nKQp2aXNyZWcoaHBpLmxtLmxvZyx0cmFucyA9IGV4cCxwYXJ0aWFsPVRSVUUpCnBsb3QoaHBpLmxtLmxvZykKYGBgCgoqKkFuc3dlcioqOiAkUl4yJCBnZXRzIGV2ZW4gaGlnaGVyISBUaGUgdmlzdWFsaXNhdGlvbiBsb29rcyBzbGlnaHRseSBiZXR0ZXIuIFRoZSByZXNpZHVhbHMgcGxvdHMgYXJlIHN0aWxsIHdvcnJ5aW5nIGV4Y2VwdCBmb3IgdGhlIFEtUSBwbG90LiAKCjIuIEEgZ3JvdXAgb2YgdGFsZW50ZWQgc2Nob29sIHN0dWRlbnRzIHN0dWRpZWQgSFBJIGJlZm9yZSB0aGV5IGxlYXJuZWQgdGhlIGxvZyB0cmFuc2Zvcm1hdGlvbi4gVGhleSBub3RpY2VkIHRoYXQgSFBJIHdhcyBpbmNyZWFzaW5nIHNsb3dseSBhdCB0aGUgYmVnaW5uaW5nIGJ1dCBpbmNyZWFzaW5nIGZhc3RlciBsYXRlci4gSW4gb3RoZXIgd29yZHMsIHRoZSBpbmNyZWFzZSBpbiBIUEkgb2NjdXJzIGF0IGFuIGluY3JlYXNpbmcgcmF0ZSBmb3IgZWFjaCBzdWNjZXNzaXZlIHRpbWUgcGVyaW9kLiBUaGUgc3R1ZGVudHMgc3VnZ2VzdGVkIHRoYXQgd2UgbWF5IHVzZSBhICoqcXVhZHJhdGljKiogZnVuY3Rpb24gaW5zdGVhZCBvZiB0aGUgbGluZWFyIGZ1bmN0aW9uIHRvIG1vZGVsIHRoZSB0cmVuZCBhcyBmb2xsb3dzLgpcWwptZWFuKHlfdCk9YStidCtjdF4yLgpcXQpCdXQgdGhlIHN0dWRlbnRzIGRpZG4ndCBrbm93IGhvdyB0byBmaW5kIHRoZSBjb2VmZmljaWVudHMgJGEsYixjJC4gQ2FuIHdlIGhlbHA/CgpUaGUgYW5zd2VyIGlzIFlFUyEgSW50ZXJlc3RpbmdseSwgdGhpcyBjYW4gYmUgZG9uZSB2aWEgYGxtKClgIGVhc2lseSBhcyBmb2xsb3dzLgoKYGBge3J9CmhwaS5xbSA8LSBsbShpbmRleH50aW1lK0kodGltZV4yKSwgZGF0YT1ob3VzaW5nLnRzKQpzdW1tYXJ5KGhwaS5xbSkKYGBgCgoqKkl0IGlzIGltcG9ydGFudCB0byB1c2UgYEkodGltZV4yKWAsIG5vdCBgdGltZV4yYCBpZiB5b3Ugd2FudCB0byBmaXQgYSBxdWFkcmF0aWMgdHJlbmQgdG8gYSB0aW1lIHNlcmllcy4qKgoKV2UgY2FuIGZpbmQgYW4gYWRkaXRpb25hbCByb3cgaW4gYENvZWZmaWNpZW50czpgIGFzIGBJKHRpbWVeMilgLiBUaGVyZSBpcyB0aGUgZXN0aW1hdGUgZm9yICRjJCBhcyB5b3UgbWF5IGV4cGVjdGVkLiBUaGUgaW50ZXJwcmV0YXRpb24gb2YgcmVzdCBpcyBqdXN0IGxpa2Ugd2hhdCB3ZSBoYXZlIGRvbmUgaW4gTGFiIEMyLiBPbmUgaW50ZXJlc3RpbmcgdGhpbmcgaXMgdGhhdCB0aGUgZXN0aW1hdGUgZm9yICRiJCBiZWNvbWVzIGluc2lnbmlmaWNhbnQhIAoKKkNvbXBhcmUgdGhlIFIgc3VtbWFyeSB3aXRoIHRoYXQgaW4gU3RlcCAxLiBEaXNjdXNzIHlvdXIgZmluZGluZ3MuKgoKKipBbnN3ZXIqKjogJFJeMiQgZ2V0cyBoaWdoZXIganVzdCBsaWtlIHRoZSB0cmFuc2Zvcm1lZCBtb2RlbC4gVGhlIHF1YWRyYXRpYyB0ZXJtIGlzIGhpZ2hseSBzaWduaWZpY2FudCBidXQgdGhlIGxpbmVhciB0ZXJtIGJlY29tZXMgaW5zaWduaWZpY2FudC4gCgpWaXN1YWxpc2luZyB0aGlzIG1vZGVsIGNhbiBiZSBkb25lIHdpdGggYHZpc3JlZygpYCBpbiB0aGUgdXN1YWwgd2F5LCBidXQgbGV0J3MgdXNlIHRoZSBgYnJvb21gIGxpYnJhcnkgaW5zdGVhZCB0byBkbyBpdCBoZXJlOgoKYGBge3J9CmF1Z21lbnQoaHBpLnFtKSB8PiBnZ3Bsb3QoYWVzKHggPSB0aW1lICx5PWluZGV4KSkgKyBnZW9tX3BvaW50KCkgKyBnZW9tX2xpbmUoKSArCiAgICAgICAgZ2VvbV9saW5lKGFlcyh5PS5maXR0ZWQpLGNvbD0ncmVkJykgKyB4bGFiKCdUaW1lIEluZGV4JykgKyB5bGFiKCdIb3VzZSBQcmljZSBJbmRleCcpCmBgYAoKWW91IG1heSBhZGQgdGhlIGNvbmZpZGVuY2UgYmFuZCBvciBwcmVkaWN0aW9uIGJhbmQgYnkgZm9sbG93aW5nIEV4ZXJjaXNlIDEgaW4gTGFiIEMzLiAKClNvbWV0aW1lcyBwZW9wbGUgcHJlZmVyIG1ha2luZyBhIHBsb3QgaW4gdGhlIGNhbGVuZGFyIHRpbWUgcmF0aGVyIHRoYW4gaW4gdGhlIHRpbWUgaW5kZXguIFRoaXMgY2FuIGJlIGRvbmUgZWFzaWx5IGJ5IHVzaW5nIGBicm9vbWAgYXMgZm9sbG93cy4gTm90aWNlIHRoYXQgd2UgYWRkIHRoZSBvcmlnaW5hbCBkYXRhIHNldCBgaG91c2luZy50c2AgaW50byBgYXVnbWVudCgpYCBhbmQgY2hhbmdlIGB4ID0gdGltZWAgdG8gYHggPSBkYXRlYCBpbiBgZ2dwbG90KClgLiBUaGlzIGlzIHZlcnkgaGFuZHkgaW4gbWFueSBjYXNlcyBlc3BlY2lhbGx5IHlvdSBhcmUgZml0dGluZyBhIGxpbmVhciBtb2RlbCB3aXRoIHRyYW5zZm9ybWF0aW9uLiAKCmBgYHtyfQphdWdtZW50KGhwaS5xbSxob3VzaW5nLnRzKSB8PiBnZ3Bsb3QoYWVzKHggPSBkYXRlICx5PWluZGV4KSkgKyBnZW9tX3BvaW50KCkgKyBnZW9tX2xpbmUoKSArCiAgICAgICAgZ2VvbV9saW5lKGFlcyh5PS5maXR0ZWQpLGNvbD0ncmVkJykgKyB4bGFiKCdUaW1lJykgKyB5bGFiKCdIb3VzZSBQcmljZSBJbmRleCcpCmBgYAoKKkNvbW1lbnQgb24gdGhlIGdvb2RuZXNzIG9mIGZpdCBvZiB0aGUgcXVhZHJhdGljIHRyZW5kIG1vZGVsIGJhc2VkIG9uIHRoZSB2aXN1YWxpc2F0aW9uLioKCioqQW5zd2VyKio6IEl0IGlzIHNsaWdodGx5IGJldHRlciB0aGFuIHRoZSBsaW5lYXIgbW9kZWwuIEJ1dCB0aGUgZ2VuZXJhbCBwYXR0ZXJuIGluIEhQSSB0aW1lIHNlcmllcyBzdGlsbCBkZXZpYXRlIGZyb20gdGhlIGN1cnZlIGZyZXF1ZW50bHkuIAoKMy4gTm93IHdlIGhhdmUgdHdvIGNhbmRpZGF0ZSBtb2RlbHMgZnJvbSBTdGVwIDEgYW5kIDIgZm9yIEhQSSB0aW1lIHNlcmllcy4gCkEgbmF0dXJhbCBxdWVzdGlvbiBhcmlzZXM6IFdoaWNoIG1vZGVsIGlzIGJldHRlcj8gCgpCb3RoIFIgc3VtbWFyeSBhbmQgdmlzdWFsaXNhdGlvbiBjYW4gYmUgdXNlZC4gVGhlIHJlc2lkdWFsIGFuYWx5c2lzIGlzIGFub3RoZXIgdG9vbCB0byBmaW5kIHRoZSBiZXR0ZXIgbW9kZWwuIFdoaWxlIHRoZXNlIHRvb2xzIGFyZSB1c2VmdWwsIHdlIGhhdmUgYSBtb3JlIGRlbGljYXRlIHdheSB0byBjb21wYXJlIHRoZSBhYm92ZSB0d28gbW9kZWxzIHBlcmZvcm1hbmNlLiBQYXJ0aWN1bGFybHksIHRoZSBsaW5lYXIgbW9kZWwgY2FuIGJlIHJlZ2FyZGVkIGFzIGEgc3BlY2lhbCBjYXNlIG9mIHRoZSBxdWFkcmF0aWMgbW9kZWwuIElmIHdlIHNldCAkYz0wJCwgdGhlIHF1YWRyYXRpYyBtb2RlbCByZWR1Y2VzIHRvIHRoZSBsaW5lYXIgbW9kZWwuIElmIHR3byBtb2RlbHMgY2FuIGJlIGxpbmtlZCBpbiBzdWNoIG1hbm5lciwgd2UgY2FsbCB0aGVzZSB0d28gbW9kZWxzIGFyZSAqKm5lc3RlZCoqLiBUaGUgc2ltcGxlIG1vZGVsIGlzIHVzdWFsbHkgY2FsbGVkIGEgKipyZWR1Y2VkIG1vZGVsKiogKGxpbmVhciBvbmUgaW4gdGhpcyBjYXNlKSBhbmQgdGhlIGNvbXBsaWNhdGVkIG1vZGVsIGlzIHVzdWFsbHkgY2FsbGVkIGFuICoqZXh0ZW5kZWQgbW9kZWwqKiAocXVhZHJhdGljIG9uZSBpbiB0aGlzIGNhc2UpLgoKQW4gKipBTk9WQSAoQU5hbHlzaXMgT2YgVkFyaWFuY2UpIHRlc3QqKiBjYW4gYmUgdXNlZCB0byBjb21wYXJlIHR3byBuZXN0ZWQgY2FuZGlkYXRlIG1vZGVscyBhcwpgYGB7cn0KYW5vdmEoaHBpLmxtLCBocGkucW0pCmBgYAoKYGFub3ZhKClgIHRlc3RzIGlmIHRoZSBhZGRpdGlvbmFsIHBhcmFtZXRlcihzKSAkYyQgaW4gdGhlIHF1YWRyYXRpYyBtb2RlbCBpcyB6ZXJvIG9yIG5vdCwgaS5lLiB0aGUgbnVsbCBoeXBvdGhlc2lzIGlzICRjPTAkIGFuZCB0aGUgcC12YWx1ZSBpcyByZXBvcnRlZCBieSBgUHIoPkYpYC4gKldoYXQgZG9lcyB0aGUgb3V0cHV0IG9mIGBhbm92YSgpYCBzdWdnZXN0PyogSWYgdGhlIFAtdmFsdWUgaXMgc21hbGwgd2UgY2FuIGNvbmNsdWRlIHRoYXQgdGhhdCBgTW9kZWwgMmAgLSB0aGUgcXVhZHJhdGljIHRyZW5kIG1vZGVsIGlzIGJldHRlci4gCgpJdCBpcyBlYXN5IHRvIGZpbmQgdGhlIGltcHJvdmVtZW50IGluIGdvb2RuZXNzIG9mIGZpdCBmcm9tIHRoZSBxdWFkcmF0aWMgbW9kZWwgaXMgc2lnbmlmaWNhbnQgZm9yIEhQSSB0aW1lIHNlcmllcy4gSW4gZmFjdCwgd2l0aCB0aGUgYWRkaXRpb25hbCBjb2VmZmljaWVudCAkYyQsIHRoZSBxdWFkcmF0aWMgdHJlbmQgbW9kZWwgKGFuZCBhbnkgZXh0ZW5kZWQgbW9kZWwpIGlzIGd1YXJhbnRlZWQgdG8gZ2V0IGNsb3NlciB0byB0aGUgcmVhbCB0aW1lIHNlcmllcyB0aGFuIHRoZSBsaW5lYXIgdHJlbmQgbW9kZWwoYW5kIGFueSByZWR1Y2VkIG1vZGVsKS4gCgoqQSBwdXJlIHF1YWRyYXRpYyBtb2RlbCBhcyAkeT1hK2N4XjIkIGlzIGFsc28gbmVzdGVkIHdpdGggdGhlIHF1YWRyYXRpYyBtb2RlbCAkeT1hK2J4K2N4XjIkLiBXaHk/IFVzZSBBTk9WQSB0byB0ZWxsIHdoaWNoIG1vZGVsIGlzIGJldHRlci4qCgoqKkFuc3dlcioqOiAqTWFrZSBzdXJlIHRoYXQgeW91IHN0aWxsIGhhdmUgYEkoKWAgaW4geW91ciBwdXJlIHF1YWRyYXRpYyBtb2RlbC4qIGBhbm92YSgpYCB0ZWxscyB1cyB0aGF0IHRoZSBsaW5lYXIgdGVybSBjYW4gYmUgZXhjbHVkZWQgZm9yIGEgbW9yZSBjb25jaXNlIG1vZGVsLiAKCmBgYHtyfQpocGkucHFtIDwtIGxtKGluZGV4fkkodGltZV4yKSwgZGF0YT1ob3VzaW5nLnRzKQphbm92YShocGkucHFtLCBocGkucW0pCmBgYAoKCkhvd2V2ZXIsIHRoZSBxdWFkcmF0aWMgdHJlbmQgbW9kZWwgaXMgbW9yZSBjb21wbGljYXRlZCB0aGFuIHRoZSBsaW5lYXIgdHJlbmQgbW9kZWwgYW5kIHRoZSBwdXJlIHF1YWRyYXRpYyBtb2RlbCBhcyBpdCBoYXMgb25lIG1vcmUgY29lZmZpY2llbnQuIElmIHRoZSBpbXByb3ZlbWVudCBpcyBqdXN0IG1hcmdpbmFsLCB3ZSBtYXkgcHJlZmVyIHRoZSBsaW5lYXIgbW9kZWwgb3IgdGhlIHB1cmUgcXVhZHJhdGljIG1vZGVsIGFzIHRoZXkgYXJlIHNvIGNvbmNpc2UuIFRoZSBBTk9WQSB0ZXN0IGV4YW1pbmVzIGlmIHRoZSBleHRlbmRlZCBtb2RlbCAocXVhZHJhdGljIHRyZW5kIG1vZGVsICkgZXhwbGFpbnMgc3VmZmljaWVudCB2YXJpYWJpbGl0eSBpbiAkeSQgY29tcGFyZWQgdG8gdGhlIHJlZHVjZWQgbW9kZWwgKGxpbmVhciB0cmVuZCBtb2RlbCkgYXQgYSBwcmljZSBvZiBhZGRpbmcgb25lIG1vcmUgcGFyYW1ldGVyLiBXZSB3aWxsIHJldmlzaXQgdGhpcyB0b3BpYyBpbiBMYWIgQzYuCgo0LiAqUHJlZGljdCBIUEkgZm9yIFEzIGFuZCBRNCBvZiAyMDIwIHdpdGggYm90aCBsaW5lYXIgdHJlbmQgbW9kZWwgYW5kIHF1YWRyYXRpYyB0cmVuZCBtb2RlbCB3aXRoIDk1JSBwcmVkaWN0aW9uIGludGVydmFscy4gQ29tbWVudCBvbiB5b3VyIHByZWRpY3Rpb25zLioKCioqQW5zd2VyKio6IE9uZSBoYXMgdG8gZmlndXJlIG91dCB0aGF0IHRoZSB0aW1lIGluZGljZXMgZm9yIFEzIGFuZCBRNCBvZiAyMDIwIGFyZSAxMjMgYW5kIDEyNC4gCgpgYGB7cn0KbmV3dGltZSA8LSBkYXRhLmZyYW1lKHRpbWU9YygxMjMsMTI0KSkKcHJlZGljdChocGkubG0sbmV3ZGF0YT1uZXd0aW1lKQpwcmVkaWN0KGhwaS5xbSxuZXdkYXRhPW5ld3RpbWUpCmBgYAoKVGhlIGxpbmVhciB0cmVuZCBtb2RlbCBpcyBjZXJ0YWlubHkgdW5kZXJlc3RpbWF0aW5nIHRoZSBIUEkuIFRoZSBxdWFkcmF0aWMgb25lIG1heSBkbyBhIGJldHRlciBqb2IuIEhvd2V2ZXIsIGJvdGggdHdvIG1vZGVscyBhcmUgdW5hYmxlIHRvIHF1YW50aWZ5IHRoZSB1bmNlcnRhaW50aWVzIGluIGEgcmlnaHQgd2F5IGR1ZSB0aGUgaGlnaGx5IGlsbC1wb3NlZCByZXNpZHVhbHMuCgoKIyMgRXhlcmNpc2UgMzogQWRkcmVzc2luZyBTZWFzb25hbGl0aWVzIGluIENvbnN1bXB0aW9uCgpCZXNpZGVzIHRoZSB0cmVuZCBleHRyYWN0ZWQgYnkgb3VyIGxpbmVhciBtb2RlbHMsIGFub3RoZXIgaW1wb3J0YW50IGZlYXR1cmUgb2YgdGltZSBzZXJpZXMgaXMgdGhlICoqc2Vhc29uYWxpdHkqKiBvciAqKnNlYXNvbmFsIGVmZmVjdHMqKi4gTWFueSB0aGluZ3MgZXhoaWJpdCBzZWFzb25hbCBwYXR0ZXJucyBvdmVyIGRpZmZlcmVudCB0aW1lIHBlcmlvZHMgKGUuZy4gbW9udGhzIG9yIHF1YXJ0ZXJzKSBvZiBhIHllYXIuIEZvciBleGFtcGxlOgogICAgCiAgLSBTYWxlcyBvZiBza2kgZXF1aXBtZW50IGFyZSB2ZXJ5IGhpZ2ggZHVyaW5nIHdpbnRlciBhbmQgYWxtb3N0IG5vbmV4aXN0ZW50IGR1cmluZyBvdGhlciBzZWFzb25zLgoKICAtIFNhbGVzIGF0IGRlcGFydG1lbnQgc3RvcmVzIGFyb3VuZCBDaHJpc3RtYXMgaG9saWRheXMgYXJlIG11Y2ggaGlnaGVyIHRoYW4gZHVyaW5nIG90aGVyIHRpbWVzIG9mIHRoZSB5ZWFyLgoKICAtIFBvd2VyIGJpbGxzIGFyZSBleHBlY3RlZCB0byBiZSBoaWdoZXIgZHVyaW5nIHdpbnRlciB0aGFuIGluIG90aGVyIHNlYXNvbnMuIAoKKkVudW1lcmF0ZSBhIGZldyBtb3JlIHRpbWUgc2VyaWVzIHdpdGggYSBzZWFzb25hbCBwYXR0ZXJuLioKCioqQW5zd2VyKio6IGVsZWN0cmljaXR5IHByb2R1Y3Rpb24gcGVyIG1vbnRoL3dlZWsvZGF5LCBzYWxlcyBvZiBpY2UgY3JlYW1zLCBldGMuCgoKQ2xlYXJseSwgc2Vhc29uYWxpdHkgaXMgY2xvc2VseSByZWxhdGVkIHRvIHRoZSBmcmVxdWVuY3kgYW5kIHRoZWlyIGxpbmthZ2VzIGNhbiBiZSBleHBlY3RlZCBqdXN0IGJ5IHJlYWRpbmcgdGhlIHdvcmRzICdtb250aGx5JywgJ3dlZWtseScsIG9yICdxdWFydGVybHknLiBJbiBmYWN0LCBpbiBhIHRpbWUgc2VyaWVzIHRoZSBzZWFzb25hbCBwYXR0ZXJuIGZvciBhIHBhcnRpY3VsYXIgcXVhcnRlciBvciBtb250aCBpcyBleGFjdGx5IHJlcGVhdGVkIGJ5IHRoZSBmcmVxdWVuY3kgb2YgdGhlIHRpbWUgc2VyaWVzLiAKCk9idmlvdXNseSwgaXQgaXMgbmVjZXNzYXJ5IHRvIGluY2x1ZGUgdGhlIHNlYXNvbmFsIGVmZmVjdHMgaW4gdGltZSBzZXJpZXMgbW9kZWxsaW5nIGluIG1vc3QgY2FzZXMuIE90aGVyd2lzZSwgdGhlIHByZWRpY3Rpb24gYmVjb21lcyByYXRoZXIgc3VwZXJmaWNpYWwgYXMgaXQgb25seSBhY2NvdW50cyBmb3IgdGhlIGxvbmctdGVybSB0cmVuZC4gCgpIb3cgY2FuIHdlIG1ha2UgdXNlIG9mIHRoZSBzZWFzb25hbCBlZmZlY3RzPyBXZSB3aWxsIHN0YXJ0IGJ5IGV4cGxvcmluZyB0aGUgZGF0YSBzZXQgYE0yIENvbnN1bXB0aW9uYCBmcm9tIFJCTlouCgoxLiBGaXJzdCBvZiBhbGwsIHdlIGxvYWQgYGNvbnN1bXB0aW9uYCBkYXRhIGZyb20gdGhlIFJCTlogd2Vic2l0ZSAtIHNlZSAoaHR0cHM6Ly93d3cucmJuei5nb3Z0Lm56L3N0YXRpc3RpY3MvbTIpIGZvciBtb3JlIGluZm9ybWF0aW9uIG9uIHRoaXMgdGltZSBzZXJpZXMuIFdlIHRoZW4gc3Vic2V0IHRoZSBkYXRhLCBmaWx0ZXJpbmcgb3V0IHRoZSAyMDIwIGRhdGEgYW5kIGJlZm9yZS4gV2UnbGwgcmVzZXJ2ZSAyMDIwIGFuZCAyMDIxIGZvciBwcmVkaWN0aW9uLgoKYGBge3J9CmNvbnN1bXB0aW9uIDwtIHJlYWRfY3N2KCJodHRwczovL3d3dy5tYXNzZXkuYWMubnovfmpjbWFyc2hhLzE2MTEyMi9kYXRhL2NvbnN1bXB0aW9uLmNzdiIpCnJldGFpbC50cmFkZS5zYWxlcyA8LSBjb25zdW1wdGlvbiB8PgogIGZpbHRlcihkYXRlIDwgeW1kKCcyMDIwLTAxLTAxJykpCnJldGFpbC50cmFkZS5zYWxlcyB8PgogIGdncGxvdChhZXMoeD1kYXRlLHk9eF9tKSkgKyBnZW9tX3BvaW50KCkgKyBnZW9tX2xpbmUoKSArIAogIHhsYWIoJ1RpbWUnKSArIHlsYWIoJ1JldGFpbCBUcmFkZSBTYWxlcycpCmBgYAoKKlNwZWNpZnkgdGhlIGZyZXF1ZW5jeSBvZiB0aGlzIHRpbWUgc2VyaWVzLioKCioqQW5zd2VyKio6IDQKCldlIGNhbiBjbGVhcmx5IHNlZSBhbiBpbmNyZWFzaW5nIHRyZW5kIG92ZXIgdGhlc2UgeWVhcnMgaW4gZ2VuZXJhbC4gVGhlIHJlZ3VsYXIgZmx1Y3R1YXRpb25zIG9mIHNhbGVzIHdpdGhpbiBvbmUgeWVhciBzdWdnZXN0IHN0cm9uZyBzZWFzb25hbCBlZmZlY3RzLiAqV2h5PyogT25lIHNoYWxsIGFsc28gbm90aWNlIHRoYXQgdGhlIHNlYXNvbmFsIHZhcmlhYmlsaXR5IGlzIGluY3JlYXNpbmcgb3ZlciB0aW1lLiAgCgoyLiBGaXJzdCBvZiBhbGwsIHdlIGNhbiBleHRyYWN0IGEgbGluZWFyIHRyZW5kIGZyb20gdGhpcyB0aW1lIHNlcmllcyBhcyBmb2xsb3dzCgpgYGB7cn0KcnRzLnRzIDwtIHJldGFpbC50cmFkZS5zYWxlcyB8PiBtdXRhdGUodGltZT0xOm4oKSkKcnRzLmxtIDwtIGxtKHhfbX50aW1lLGRhdGE9cnRzLnRzKQpzdW1tYXJ5KHJ0cy5sbSkKYGBgCgpUaGUgUiBvdXRwdXQgcmVhZHMgcHJldHR5IGdvb2Qgd2l0aCBhbGwgY29lZmZpY2llbnQgc2lnbmlmaWNhbnQgYW5kIGEgdmVyeSBoaWdoICRSXjI9MC45NDU3JCEgCgpMZXQncyB2aXN1YWxpc2UgdGhlIG1vZGVsIGFzIGZvbGxvd3M6CmBgYHtyfQp2aXNyZWcocnRzLmxtLCBnZz1UUlVFKSArIHhsYWIoJ1RpbWUgSW5kZXgnKSArIHlsYWIoJ1JldGFpbCBUcmFkZSBTYWxlcycpCmBgYAoKT29wcy4gQSBzaW1wbGUgbGluZSB3b24ndCBiZSBhYmxlIHRvIGNhcHR1cmUgdGhlIHNlYXNvbmFsaXR5LgoKMy4gTGV0J3MgcGVyZm9ybSBhIHN0YW5kYXJkIHJlc2lkdWFsIGRpYWdub3N0aWNzIG9uIHRoZSBmaXR0ZWQgbW9kZWwuCgpgYGB7cn0KcGxvdChydHMubG0pCmBgYAoKRXhjZXB0IGZvciBRLVEgcGxvdCwgcmVzdCBkaWFnbm9zdGljIHBsb3RzIGxvb2sgcmVhbGx5IHJlYWxseSB3ZWlyZCEgKkFkZCBhIGZldyBkZXRhaWxlZCBjb21tZW50cyBvbiBlYWNoIHBsb3QuKgoKSW4gYWRkaXRpb24gdG8gdGhlIGFib3ZlIHN0YW5kYXJkIGRpYWdub3N0aWMgcGxvdHMsIHRoZSAqKnJlc2lkdWFscyB2ZXJzdXMgdGltZSoqIHBsb3QgaXMgZnJlcXVlbnRseSB1c2VkIGluIHRoZSByZXNpZHVhbCBkaWFnbm9zdGljcyBvZiB0aW1lIHNlcmllcyBtb2RlbGxpbmcgYXMKCmBgYHtyfQphdWdtZW50KHJ0cy5sbSkgfD4KICBnZ3Bsb3QoYWVzKHg9dGltZSx5PS5yZXNpZCkpICsgZ2VvbV9wb2ludCgpICsgZ2VvbV9saW5lKCkgKwogIGdndGl0bGUoJ1Jlc2lkdWFscyB2ZXJzdXMgVGltZSBJbmRleCcpICsgeGxhYignVGltZSBJbmRleCcpCmBgYAoKVGhlIHBhdHRlcm4gaW4gdGhlICoqcmVzaWR1YWxzIHZlcnN1cyB0aW1lKiogcGxvdCBzZWVtcyBjb250YWluaW5nIHRoZSBpbmZvcm1hdGlvbiBvbiB0aGUgc2Vhc29uYWxpdHkhIFN1Y2ggcGF0dGVybiBhY3R1YWxseSBzdWdnZXN0cyB0aGF0IHRoZSByZXNpZHVhbHMgdmlvbGF0ZSB0aGUgaS5pLmQuIGNvbmRpdGlvbiwgaS5lLiB0aGUgaW5kZXBlbmRlbnRseSBhbmQgaWRlbnRpY2FsbHkgZGlzdHJpYnV0ZWQgY29uZGl0aW9uLiBXZSBjYW4gY29uamVjdHVyZSB0aGF0IHRoZSByZXNpZHVhbHMgaW4gZGlmZmVyZW50IHNlYXNvbnMgaXMgY29taW5nIGZyb20gZGlmZmVyZW50IGRpc3RyaWJ1dGlvbnMgYW5kIHRoZXkgYXJlIGNvcnJlbGF0ZWQgaW4gdGhlIHRpbWUgb3JkZXIuICpIb3cgY2FuIHdlIGNoZWNrIHRoZSBpbnRlcm5hbCBjb3JyZWxhdGlvbiBpbiByZXNpZHVhbHM/KgoKKipBbnN3ZXIqKjogVGhpcyBvbmUgaXMgbm90IHN0cmFpZ2h0Zm9yd2FyZC4gVG8gZGlzY292ZXIgdGhlIGludGVybmFsIGNvcnJlbGF0aW9uIGluIGEgdGltZSBzZXJpZXMsIHdlIG5lZWQgYSBiaXQgbW9yZSBiYWNrZ3JvdW5kIGtub3dsZWRnZS4gTmV2ZXJ0aGVsZXNzLCB3ZSBtYXkgY29uamVjdHVyZSB0aGF0IHRoZXJlIGlzIGEgb25lLXN0ZXAgY29ycmVsYXRpb24gaW4gYSB0aW1lIHNlcmllcywgc2F5ICR4XzEkIGlzIGNvcnJlbGF0ZWQgd2l0aCAkeF8yJCwgICR4XzIkIGlzIGNvcnJlbGF0ZWQgd2l0aCAkeF8zJCwuLi4sJHhfe3QtMX0kIGlzIGNvcnJlbGF0ZWQgd2l0aCAkeF90JC4gTGlrZSAkKHhfaSx5X2kpJCBpcyBwYWlyZWQgZm9yIGNvbXB1dGluZyB0aGUgY29ycmVsYXRpb24gY29lZmZpY2llbnQsIHdlIGhhdmUgdGhlIHBhaXJzICQoeF8xLHhfMikkLCAkKHhfMix4XzMpJCwuLi4sJHhfe3QtMX0seF90JCBhbmQgd2UgY2FuIGNvbXB1dGUgdGhlIGNvcnJlbGF0aW9uIGJldHdlZW4gJFx7eF90XH0kIGFuZCAkXHt4X3t0KzF9XH0kLiBTdWNoIGEgY29ycmVsYXRpb24gY29lZmZpY2llbnQgd2l0aGluIGEgdGltZSBzZXJpZXMgaXMgY2FsbGVkICoqYXV0b2NvcnJlbGF0aW9uKiouIFdlIGNhbiBmdXJ0aGVyIGV4YW1pbmUgdGhlIGhpZ2hlciBvcmRlciAobXVsdGktc3RlcCkgYXV0b2NvcnJlbGF0aW9uLiBUaGVzZSBpc3N1ZXMgd2lsbCBiZSBleHBsb3JlZCBpbiB0aGUgbGFzdCB0d28gbGVjdHVyZXMuCgpgYGB7cn0Kc2V0LnNlZWQoMjAyMCkKdGliYmxlKGU9cm5vcm0oMTAwKSkgfD4gc3VtbWFyaXNlKGFjZjE9Y29yKGVbLTFdLGVbLWxlbmd0aChlKV0pKQpgYGAKCldlIGZ1cnRoZXIgbWFrZSBhIGJveCBwbG90IHRvIGNvbXBhcmUgdGhlIHJlc2lkdWFscyBvdmVyIGRpZmZlcmVudCBxdWFydGVycy4KSGVyZSB3ZSBhZGQgdGhlIGRhdGEgc2V0IGBydHMudHNgIHRvIGBhdWdtZW50KClgIHRvIG1ha2Ugc3VyZSB0aGF0IHdlIGhhdmUgdGhlIGFjY2VzcyB0byB0aGUgY2FsZW5kYXIgdGltZSBhbmQgd2UgY2FuIGdldCB0aGUgY29ycmVzcG9uZGluZyBxdWFydGVyIGJ5IGBxdWFydGVyKClgIGZyb20gYGx1YnJpZGF0ZSgpYC4gCgpgYGB7cn0KYXVnbWVudChydHMubG0sIHJ0cy50cykgfD4gbXV0YXRlKHF1YXJ0ZXI9cXVhcnRlcihkYXRlKSkgfD4KICBnZ3Bsb3QoYWVzKHk9LnJlc2lkLHg9cXVhcnRlcikpICsgZ2VvbV9ib3hwbG90KGFlcyhncm91cD1xdWFydGVyKSkKYGBgCgpFYWNoIGJveCBpbiB0aGUgYWJvdmUgYm94cGxvdCBjaGFyYWN0ZXJpc2VzIHRoZSByZXNpZHVhbHMsIGkuZS4gdGhlIGRldmlhdGlvbnMgZnJvbSB0aGUgdHJlbmQgbGluZSwgYXQgYSBzcGVjaWZpYyBxdWFydGVyLiBUaGUgcmVzaWR1YWxzIGhlcmUgYXJlIGEgY29tYmluYXRpb24gb2YgdHJ1ZSByYW5kb20gZXJyb3JzIGFuZCBzZWFzb25hbCBlZmZlY3RzLiAKCkhvdyBjYW4gd2UgZXh0cmFjdCB0aGUgaW5mb3JtYXRpb24gb24gc2Vhc29uYWxpdHk/IEEgc2ltcGxlIGlkZWEgaXMgdG8gZXN0aW1hdGUgdGhlIHNlYXNvbmFsIGVmZmVjdCBhdCBhIHBhcnRpY3VsYXIgcXVhcnRlciBieSB0aGUgbWVhbiBvZiByZXNpZHVhbHMgYXQgdGhpcyBxdWFydGVyLiBUaGlzIHdpbGwgYWRkIGEgY29uc3RhbnQgc2hpZnQgdG8gdGhlIHRyZW5kIGxpbmUgZm9yIGVhY2ggcXVhcnRlci4gVGhlIHJlc3QgZGV2aWF0aW9ucyB3aWxsIGJlIHJlZ2FyZGVkIGFzIHRoZSBmaW5hbCByZXNpZHVhbHMuIAoKKlRyeSB0byBjb21wdXRlIHRoZSByZXNpZHVhbHMgbWVhbiBvZiBlYWNoIHF1YXJ0ZXIgYnkgYHN1bW1hcmlzZSgpLmAqCgoKKipBbnN3ZXIqKjoKYGBge3J9CmF1Z21lbnQocnRzLmxtLCBydHMudHMpIHw+IG11dGF0ZShxdWFydGVyPXF1YXJ0ZXIoZGF0ZSkpIHw+IGdyb3VwX2J5KHF1YXJ0ZXIpIHw+IHN1bW1hcmlzZShyZXNpZC5tZWFuPW1lYW4oLnJlc2lkKSkKYGBgCgoKNC4gVGhlIGFib3ZlIHByb2NlZHVyZXMgc2VlbXMgd29yayB3ZWxsIGJ1dCB0ZWRpb3VzLiBBIHN1cnByaXNpbmcgZmFjdCBpcyB0aGF0IHdlIGNhbiBjb21wbGV0ZSB0aGVzZSBqb2JzIHdpdGggb25lIGxpbmUgYnkgYGxtKClgIGFzIGZvbGxvd3MuIFRoZSB0cmljayBpcyB0aGF0IHdlIG11c3QgdHVybiBgcXVhcnRlcmAgaW50byBhIGZhY3RvciAoY2F0ZWdvcmljYWwsIHF1YWxpdGF0aXZlKSB2YXJpYWJsZSB2aWEgYGZhY3RvcigpYCBhbmQgaW5jbHVkZSBpdCBpbiB0aGUgcmVncmVzc2lvbiBmb3JtdWxhIGluIGBsbSgpYC4KCmBgYHtyfQpydHMudHMucSA8LSBydHMudHMgfD4gbXV0YXRlKHF1YXJ0ZXI9ZmFjdG9yKHF1YXJ0ZXIoZGF0ZSkpKSAKcnRzLmxtLnEgPC0gbG0oeF9tfnRpbWUrcXVhcnRlcixkYXRhPXJ0cy50cy5xKQpzdW1tYXJ5KHJ0cy5sbS5xKQpgYGAKCldlIGhhdmUgb2J0YWluZWQgYSBmZXcgbW9yZSByb3dzIGluIGBDb2VmZmljaWVudHM6YCwgaW5jbHVkaW5nIGBxdWFydGVyMmAsYHF1YXJ0ZXIzYCxgcXVhcnRlcjRgIHdpdGggYEVzdGltYXRlYCwgYFN0ZC4gRXJyb3JgLCBldGMuIAoKTG9va3MgZ29vZCEgT2gsIHdhaXQsIHdoZXJlIGlzIGBxdWFydGVyMWA/CgpJbiBmYWN0LCB0aGUgc2Vhc29uYWwgZWZmZWN0IGF0IHRoZSBmaXJzdCBxdWFydGVyIGlzIGluY2x1ZGVkIGluIGAoSW50ZXJjZXB0KWAkPVRydWUuSW50ZXJjZXB0ICsgU2Vhc29uYWwuRWZmZWN0Lm9mLlExJCEgTW9yZSBpbXBvcnRhbnRseSwgYEVzdGltYXRlYHMgZm9yIHRoZSByZXN0IHRocmVlIHF1YXJ0ZXJzIGlzIG5vdCB0aGUgc2Vhc29uYWwgZWZmZWN0cyBhdCBjb3JyZXNwb25kaW5nIHF1YXJ0ZXJzLiBUaGV5IGFyZSBpbnN0ZWFkIHRoZSBkaWZmZXJlbmNlIGJldHdlZW4gJFNlYXNvbmFsLkVmZmVjdC5vZi5RMSQgYW5kICRTZWFzb25hbC5FZmZlY3Qub2YuUTIuUTMuYW5kIC5RNCQKClRoZXJlZm9yZSwgd2UgY2FuIHdyaXRlIHRoZSBmb2xsb3cgZXF1YXRpb24gZm9yIG91ciBmaXR0ZWQgbGluZWFyIG1vZGVsIGFzClxbCm1lYW4oUmV0YWlsLlRyYWRlLlNhbGVzKT03MjQwLjggKyAxNjIuNiBcdGltZXMgVGltZSArIERfUSwgUT0xLDIsMyw0LgpcXQp3aGVyZSAkRF8xPTAkLCAkRF8yPS0zMzAuMCQsJERfMz0tMjc4LjAkIGFuZCAkRF80PTE1MjEuOCQuCgpNb3JlIGRldGFpbHMgb24gdGhpcyBzdHJhbmdlIGlzc3VlIGFmdGVyIGFkZGluZyBmYWN0b3IgdmFyaWFibGVzIGluIHRoZSBsaW5lYXIgbW9kZWwgd2lsbCBiZSByZXZlYWxlZCBpbiB0aGUgY29taW5nIExlY3R1cmVzIEM1IGFuZCBDNiwgYW5kIGNvcnJlc3BvbmRpbmcgTGFicy4KClZpc3VhbGlzZSBvdXIgbW9kZWwgd2l0aCBib3RoIGxpbmVhciB0cmVuZCBhbmQgc2Vhc29uYWwgZWZmZWN0czoKYGBge3J9CmF1Z21lbnQocnRzLmxtLnEpIHw+IGdncGxvdChhZXMoeCA9IHRpbWUgLHk9eF9tKSkgKyBnZW9tX3BvaW50KCkgKyBnZW9tX2xpbmUoYWxwaGE9MC4zKSArCiAgZ2VvbV9saW5lKGFlcyh5PS5maXR0ZWQpLGNvbD0ncmVkJykgKyB4bGFiKCdUaW1lIEluZGV4JykgKyB5bGFiKCdSZXRhaWwgVHJhZGUgU2FsZXMnKQpgYGAKCk5vdyB0aGUgZml0dGVkIGN1cnZlIGxvb2tzIG11Y2ggbW9yZSByZWFzb25hYmxlISAqRG8geW91IHRoaW5rIHRoZXJlIGlzIGFueSByb29tIGZvciBmdXJ0aGVyIGltcHJvdmVtZW50PyoKCioqQW5zd2VyKio6IFllcy4gV2UgY2FuIHNlZSB0aGF0IHRoZSBzZWFzb25hbCB2YXJpYXRpb25zIGFyZSBpbmNyZWFzaW5nIG92ZXIgdGltZS4gQSBsb2cgdHJhbnNmb3JtIGNhbiBoZWxwIHN0YWJpbGlzZSB0aGUgaW5jcmVhc2luZyB2YXJpYWJpbGl0eS4KCipQZXJmb3JtIGEgdGltZSBzZXJpZXMgcmVzaWR1YWwgYW5hbHlzaXMgb24geW91ciBmaXR0ZWQgbW9kZWwuIFJlbWVtYmVyIHRoZSByZXNpZHVhbHMgdmVyc3VzIHRpbWUgcGxvdC4qCgoqKkFuc3dlcioqOiAKYGBge3J9CnBsb3QocnRzLmxtLnEpCmF1Z21lbnQocnRzLmxtLnEpIHw+IGdncGxvdChhZXMoeD10aW1lLHk9LnJlc2lkKSkgKyBnZW9tX3BvaW50KCkKYGBgCk5vdCByZWFsbHkgZ29vZCEgU3RpbGwgaGF2ZSBzb21lIG9idmlvdXMgcGF0dGVybnMgaW4gdGhlIHJlc2lkdWFscy4gTm9uZSBvZiB0aGUgcGxvdHMgcGFzcyB0aGUgZGlhZ29ub3N0aWNzLiBOZWVkIHNvbWUgYmV0dGVyIG1vZGVscyB0byBhZGRyZXNzIGl0LgoKKlRyeSB0byB2aXN1YWxpc2UgdGhlIG1vZGVsIHdpdGggYHZpc3JlZygpYC4gV2hhdCBkbyB5b3UgZmluZCBmcm9tIHRoZSBwbG90cyBvZiBgdmlzcmVnKClgLioKCioqQW5zd2VyKio6IApgYGB7cn0KdmlzcmVnKHJ0cy5sbS5xKQpgYGAKCmB2aXNyZWcoKWAgc3BsaXRzIHRoZSBlZmZlY3Qgb2YgdHJlbmQgYW5kIHNlYXNvbmFsaXR5IHRvIHR3byBzZXBhcmF0ZSBncmFwaHMuCgo1LiBXZSBjYW4gcHJlZGljdCB0aGUgc2FsZXMgaW4gMjAyMCBhcyBmb2xsb3dzCmBgYHtyfQpuZXd0aW1lIDwtIGRhdGEuZnJhbWUodGltZT1ucm93KHJ0cy50cykrKDE6NCkscXVhcnRlcj1mYWN0b3IoMTo0KSkKYXVnbWVudChydHMubG0ucSxuZXdkYXRhPW5ld3RpbWUpCmBgYApCeSBhZGRpbmcgYG5ld2RhdGE9bmV3dGltZWAgaW4gYGF1Z21lbnQoKWAsIHdlIGNhbiBnZXQgdGhlIHByZWRpY3Rpb24gaW1tZWRpYXRlbHkganVzdCBsaWtlIGBwcmVkaWN0KClgIGFuZCB0aGUgcmVzdWx0cyBhcmUgb3JnYW5pc2VkIHdlbGwgaW4gYSB0aWJibGUuIAoKKkNvbXB1dGUgdGhlIG1lYW4gc3F1YXJlIGVycm9ycyBvZiB5b3VyIHByZWRpY3Rpb24gaW4gMjAyMC4qCgpgYGB7cn0KcnRzLjIwMjAgPC0gY29uc3VtcHRpb24gfD4KICBmaWx0ZXIoZGF0ZSA+PSB5bWQoJzIwMjAtMDEtMDEnKSAmIGRhdGUgPD0geW1kKCcyMDIxLTAxLTAxJykgKSB8PiAKICBjYmluZChhdWdtZW50KHJ0cy5sbS5xLG5ld2RhdGE9bmV3dGltZSkpIApydHMuMjAyMApydHMuMjAyMCB8PiBzdW1tYXJpc2UobXNlPW1lYW4oKHhfbS0uZml0dGVkKV4yKSkKYGBgClRoZSBwcmVkaWN0aW9ucyBhcmUgbm90IHJlYWxseSBnb29kIGFzIHRoZSBzYWxlcyBoYXZlIGJlZW4gdW5kZXJlc3RpbWF0ZWQgY29uc2lzdGVudGx5LiAKCipXaGF0IHdpbGwgaGFwcGVuIGlmIHdlIHVzZSB0aGUgZml0dGVkIG1vZGVsIHRvIHByZWRpY3QgdGhlIHNhbGVzIGluIDIwMjE/KgoKKipBbnN3ZXIqKjogV2Ugd29uJ3QgZ2V0IGdvb2QgcHJlZGljdGlvbnMgZ2l2ZW4gdGhlIHNob2NrIG9mIENPVklELTE5CgoqKk9wdGlvbmFsIENoYWxsZW5nZTogV2lsbCBsb2cgdHJhbnNmb3JtIGltcHJvdmUgeW91ciBtb2RlbCBmaXQ/IFRyeSBpdCEqKgoKKipBbnN3ZXIqKjoKYGBge3J9CnJ0cy5sbS5xLmxvZyA8LSBsbShsb2coeF9tKX50aW1lK3F1YXJ0ZXIsZGF0YT1ydHMudHMucSkKc3VtbWFyeShydHMubG0ucS5sb2cpCmF1Z21lbnQocnRzLmxtLnEubG9nLG5ld2RhdGE9bmV3dGltZSkgfD4gbXV0YXRlKC5wcmVkPWV4cCguZml0dGVkKSkKYGBgCgpQcmVkaWN0aW9ucyBhcmUgbXVjaCBiZXR0ZXIhIFlvdSBjYW4gY2hlY2sgcmVzaWR1YWxzIGFuZCB2aXN1YWxpc2F0aW9uIGFjY29yZGluZ2x5LiA=