This is the companion notebook to the course on Time-series analysis. The dataset consists of 3 series of indicators characterising the French economy.
library(tidyverse)
# Then change your working directory
if(!require(lubridate)){install.packages("lubridate")}
library(lubridate)
load("economics.RData")
summary(economics) # Descriptive statistics
Quarter GDP Unemployment CPI
Min. :199001 Min. :261484 Min. : 6.80 Min. : 66.56
1st Qu.:199701 1st Qu.:318372 1st Qu.: 8.10 1st Qu.: 77.47
Median :200401 Median :421974 Median : 8.90 Median : 85.59
Mean :200365 Mean :419099 Mean : 8.91 Mean : 85.92
3rd Qu.:201101 3rd Qu.:511961 3rd Qu.: 9.80 3rd Qu.: 95.67
Max. :201801 Max. :582503 Max. :10.40 Max. :101.72
economics <- mutate(economics, Year = as.numeric(substr(Quarter,1,4)), Quarter = as.numeric(substr(Quarter,5,6)))
economics <- mutate(economics, Date = make_date(year = Year, month = 3*Quarter, day = 1))
First analysis
The first thing to do is often to have a look at the data:
eco <- economics %>% select(-Year, -Quarter) %>% gather(key = Variable, value = Value, -Date)
ggplot(eco, aes(x = Date, y = Value)) + geom_line() + facet_grid(Variable ~ ., scales = 'free')

Obviously, these series are not stationary (at least the first two), if only because of their trend which implies that their mean increases with time (the range of the distribution is clearly not constant). One possible solution is to study variations: \[\Delta X_t = X_t-X_{t-1}\]
economics <- economics %>% mutate(GDP_var = GDP - lag(GDP)) # Raw variation in GDP
ggplot(economics, aes(x = Date, y = GDP_var)) + geom_point()

Clearly, this is better. Indeed, it seems that the variations in the French GDP display a somewhat coherent pattern. Nevertheless, the classical transform used to `stationarise’ series consists in computing growth, i.e., relative variations: \[r_t=\frac{X_t}{X_{t-1}}-1\] I use the notation r because in finance, these series are called ‘returns’. Henceforth, I will often use this abuse of language.
economics <- economics %>% mutate(GDP_growth = GDP / lag(GDP) - 1) # Growth in GDP
ggplot(economics, aes(x = Date, y = GDP_growth)) + geom_bar(stat = "identity")

The original process can be reconstructed from growth/returns: \[X_t=X_0\prod_{s=1}^t(1+r_s).\]
Below, we show an example that starts from random returns and the recomposes the integrated process.
r <- runif(12, min = -0.5, max = 0.5) # We generate completely random returns
x <- cumprod(1+r) # We use the cumulative product over 1+r
time <- rep(1:12) # We generate time
data <- data.frame(time, r, x) # We aggregate the data
data <- data %>% gather(key = type, value = value, -time) # And put it into ggplot format
data %>% ggplot(aes(x = time, y = value)) + geom_line() +facet_grid(type~., scales = "free")

When returns (top plot) are negative (resp. positive), the process x (bottom plot) decreases (resp. increases).
Classical processes
The random walk
The most typical random process. Increasing or decreasing by one with probability 1/2 (sum of Bernoulli realisations).
x <- 0 # A first example, step-by-step. The process starts at zero.
nb_steps <- 15 # We simulate 15 points
for(n in 2:nb_steps){
x[n] <- x[n-1] + 2 * rbinom(1, 1, 0.5) - 1 # The Bernoulli variables are not straightforwadly generated
}
data.frame(date = 1:nb_steps, x) %>% ggplot(aes(x = date, y = x)) + geom_line()

Instead of a loop, we can directly use the cumulative sum.
x <- 0 # A quicker, neater way to proceed.
nb_steps <- 80 # A larger number of points
x <- (2*rbinom(nb_steps, 1, 0.5) - 1) %>% cumsum() # Here, we directly use the cumulative sum
df <- data.frame(time = 1:nb_steps, x) # Wrapping the data
ggplot(df, aes(x = time, y = x)) + geom_line()

Autoregressive processes
Autoregressive processes add memory to the trajectory: increments are no longer independent. Below, we build a trajectory of one such process, the AR(1), defined by \[X_t = a+bX_{t-1}+\epsilon_t\]
a <- 1 # Parameter
b <- 0.5 # Parameter: must be smaller than 1 in absolute value. If not: explosion
x <- 1 # Initial value.
nb_steps <- 20
for(n in 2:nb_steps){
x[n] <- a + b * x[n-1] + rnorm(1) # Updating the process according to formula
}
data.frame(date = 1:nb_steps, x) %>% ggplot(aes(x = date, y = x)) + geom_line()

Let’s use a dedicated function. Remember, R has many useful functions/packages. The AR process is a special case of a more general family: the ARIMA processes. Simulation is done via arima.sim. The documentation is available at https://stat.ethz.ch/R-manual/R-devel/library/stats/html/arima.sim.html
Nb_points <- 100
AR <- arima.sim(list(ar = c(0.9)), Nb_points) # Here is the simulation part
AR <- data.frame(Time = 1:Nb_points, AR) # Wrapping the data
AR %>% ggplot(aes(x = Time, y = AR)) + geom_line() # Plotting

Autocorrelogram
The autocorrelogram computes the correlation between lagged values of the series. Assuming the series is stationary with constant mean \(m\) and variance \(v\), it is defined as: \[\rho_j=v^{-1}\mathbb{E}[(X_t-m)(X_{t-j}-m)]\] It is computed in R with the acf() function and automatically provides the values for \(j\in (0,20)\).
ac <- acf(economics$GDP_growth, na.action = na.pass, plot = FALSE)
plot(ac, main = "") # Removes the title

ac2 <- acf(AR$AR, plot = FALSE)
plot(ac2, main = "")

We see autocorrelation in the French economic growth. This probably comes from so-called economic cycles (periods of growth followed by recessions). For an AR(1) process, the autocorrelation decreases at the rate \(b^j\). In the above example, b=0.9, so the decrease is slow.
Estimation
For a given series, one may want to estimate the corresponding AR parameters (if that seems relevant). In R, several estimators exist (OLS, MLE). We look a several below.
ar(economics$GDP_growth, na.action = na.pass, order.max = 1) # Autoregressive coefficient
Call:
ar(x = economics$GDP_growth, order.max = 1, na.action = na.pass)
Coefficients:
1
0.5829
Order selected 1 sigma^2 estimated as 1.896e-05
series <- economics$GDP_growth[2: nrow(economics)] # Taking out the NA term
ar(series, order.max = 1, method = "ols") # Classical least-squares method
Call:
ar(x = series, order.max = 1, method = "ols")
Coefficients:
1
0.5833
Intercept: -4.826e-05 (0.0004093)
Order selected 1 sigma^2 estimated as 1.86e-05
ar(series, order.max = 1, method = "mle") # Max likelihood
Call:
ar(x = series, order.max = 1, method = "mle")
Coefficients:
1
0.5819
Order selected 1 sigma^2 estimated as 1.856e-05
rho <- cor(economics$GDP_growth, lag(economics$GDP_growth), use = "complete")
rho # Moment-based value
[1] 0.5850411
Once the model is estimated, we can forecast the future value.
Forecast
In R, a predict() function is often provided in time-series packages. But be careful, the output changes from one to another.
ar_est <- ar(series, order.max = 1, method = "mle")
predict(ar_est)
$pred
Time Series:
Start = 113
End = 113
Frequency = 1
[1] 0.006385801
$se
Time Series:
Start = 113
End = 113
Frequency = 1
[1] 0.004307705
0.583*series[112] + mean(series)*(1-0.583) # Manual check!
[1] 0.006384121
We compare it to the conditional mean: pretty close. The conditional mean is simply \(\mathbb{E}[X_{t+1}|X_t]=\hat{a}+\hat{b}X_t\).
Multivariate analysis
In this section, we come back to macro-economic data. ### First steps Often, variables inside a sample are expected to be related. Hence, understanding the relationships between them is crucial, especially for prediction purposes.
economics <- economics %>% mutate(Unemp_growth = Unemployment / lag(Unemployment) - 1) # Growth of unemployment
cor(economics$GDP, economics$Unemployment) # Correlation GDP/Unemp
[1] 0.02307862
cor(economics$GDP_growth, economics$Unemp_growth, use = "complete") # Correlation GDP growth / Unemp growth
[1] -0.5047352
cor(lag(economics$GDP_growth), economics$Unemp_growth, use = "complete") # Correlation past GDP growth / Unemp growth
[1] -0.4970082
Raw GDP and unemployment appear unrelated. But relative variations in both variables are negatively correlated: when GDP increases, unemployment decreases. The last correlation show that even the previous value of change in growth is correlated to current change in unemployment.
Below, we visually confirm these results. First, variations indeed co-move in opposite directions. Second, the linear approximation between the two variables does have a negative slope, thereby indicating negative correlation.
data <- economics %>% select(Date, GDP_growth, Unemp_growth) %>% gather(key = Indicator, value = Value, -Date)
data %>% ggplot(aes(x = Date, y = Value)) +
geom_bar(stat = "identity") + facet_grid(Indicator~., scales = "free") +
theme(axis.text.x = element_text(angle = 90))

economics %>% ggplot(aes(x = GDP_growth, y = Unemp_growth)) + geom_point() + geom_smooth(method = "lm")

Estimation
A simple approach for multivariate time-series is the vector auto-regressive model. It reads \[\mathbf{X}_t=\mathbf{A}\mathbf{X}_{t-1}+\mathbf{\epsilon}_t,\] where \(\mathbf{X}_t\) and \(\mathbf{\epsilon}_t\) are \(n\)-dimensional vectors and \(\mathbf{A}\) is an \(n\times n\) matrix.
Below, we estimate a vector autoregression model on GDP and unemployment growths.
if(!require(vars)){install.packages("vars")}
library(vars) # This is the package for VAR models
library(tidyverse)
# FIRST, we prepare the data
var_data <- economics %>% dplyr::select(GDP_growth, Unemp_growth) # Beware: MASS package overrides select() function
train_data <- var_data[2:112,] # Fitting the model on all dates but the last
test_data <- var_data[113,] # Testing the model on the last date
VAR_est <- VAR(train_data, p = 2) # Fitting
VAR_est # Showing the result
VAR Estimation Results:
=======================
Estimated coefficients for equation GDP_growth:
===============================================
Call:
GDP_growth = GDP_growth.l1 + Unemp_growth.l1 + GDP_growth.l2 + Unemp_growth.l2 + const
GDP_growth.l1 Unemp_growth.l1 GDP_growth.l2 Unemp_growth.l2 const
0.479483246 -0.015960002 0.191987786 0.023239009 0.002365023
Estimated coefficients for equation Unemp_growth:
=================================================
Call:
Unemp_growth = GDP_growth.l1 + Unemp_growth.l1 + GDP_growth.l2 + Unemp_growth.l2 + const
GDP_growth.l1 Unemp_growth.l1 GDP_growth.l2 Unemp_growth.l2 const
-1.62836886 0.26023443 -0.35321054 0.04992616 0.01502423
The interpretation is interesting. The first coefficients pertain to the GDP equation. We see that the strong coefficients (especially the first one) are associated to GDP. This means that past GDP is the most important to forecast future GDP (the so-called economic cycles).
The second batch of coefficients are linked to unemployment. Here, again, the strongest values come from past realisations of GDP. Hence, in the determination of future unemployment rates, growth in GDP is more important than past growth in unemployment. Which makes sense.
Forecast
Finally, the forecast.
predict(VAR_est, n.ahead = 1) # The predict() function
$GDP_growth
fcst lower upper CI
GDP_growth.fcst 0.009536154 0.001051062 0.01802125 0.008485093
$Unemp_growth
fcst lower upper CI
Unemp_growth.fcst -0.02055491 -0.06647668 0.02536686 0.04592177
test_data
The quality of the forecast in this particular exemple is not impressive…
Exercises
Pursuing the economic study => CPI
- Add the relative variation (growth) of the CPI in the data.
- Plot its time-series
- Fit (estimate) an AR(1) models both for the CPI and, afterwards, for its relative variations. Is the raw CPI stationary? How can we interpret the outome of the second estimation?
Unemployment: can ARCH models improve the prediction?
- Install and load the tseries package.
- Have a look at the documentation of the package: https://cran.r-project.org/web/packages/tseries/tseries.pdf More precisely, we will use the garch() and predict() functions.
- Fit an ARCH model on all values of the relative Unemp variations, except the last one. Predict the last value. For a simple arch, the second argument is order = c(0,1).
LS0tCnRpdGxlOiAiVGltZS1zZXJpZXMgYW5hbHlzaXMiCm91dHB1dDogCiAgaHRtbF9ub3RlYm9vazoKICAgICN0b2M6IHRydWUKICAgICN0b2NfZmxvYXQ6IHRydWUKIyAgZ2l0aHViX2RvY3VtZW50OiBkZWZhdWx0Ci0tLQoKClRoaXMgaXMgdGhlIGNvbXBhbmlvbiBub3RlYm9vayB0byB0aGUgY291cnNlIG9uIFRpbWUtc2VyaWVzIGFuYWx5c2lzLiBUaGUgZGF0YXNldCBjb25zaXN0cyBvZiAzIHNlcmllcyBvZiBpbmRpY2F0b3JzIGNoYXJhY3RlcmlzaW5nIHRoZSBGcmVuY2ggZWNvbm9teS4KCmBgYHtyIHBhY2thZ2UsIG1lc3NhZ2UgPSBGQUxTRSwgd2FybmluZyA9IEZBTFNFfQpsaWJyYXJ5KHRpZHl2ZXJzZSkKIyBUaGVuIGNoYW5nZSB5b3VyIHdvcmtpbmcgZGlyZWN0b3J5CmlmKCFyZXF1aXJlKGx1YnJpZGF0ZSkpe2luc3RhbGwucGFja2FnZXMoImx1YnJpZGF0ZSIpfQpsaWJyYXJ5KGx1YnJpZGF0ZSkKbG9hZCgiZWNvbm9taWNzLlJEYXRhIikKc3VtbWFyeShlY29ub21pY3MpICAjIERlc2NyaXB0aXZlIHN0YXRpc3RpY3MKZWNvbm9taWNzIDwtIG11dGF0ZShlY29ub21pY3MsIFllYXIgPSBhcy5udW1lcmljKHN1YnN0cihRdWFydGVyLDEsNCkpLCBRdWFydGVyID0gYXMubnVtZXJpYyhzdWJzdHIoUXVhcnRlciw1LDYpKSkKZWNvbm9taWNzIDwtIG11dGF0ZShlY29ub21pY3MsIERhdGUgPSBtYWtlX2RhdGUoeWVhciA9IFllYXIsIG1vbnRoID0gMypRdWFydGVyLCBkYXkgPSAxKSkKYGBgCgoKIyMgRmlyc3QgYW5hbHlzaXMKClRoZSBmaXJzdCB0aGluZyB0byBkbyBpcyBvZnRlbiB0byBoYXZlIGEgbG9vayBhdCB0aGUgZGF0YToKYGBge3IgcGxvdHNfMSwgbWVzc2FnZSA9IEZBTFNFfQplY28gPC0gZWNvbm9taWNzICU+JSBzZWxlY3QoLVllYXIsIC1RdWFydGVyKSAlPiUgZ2F0aGVyKGtleSA9IFZhcmlhYmxlLCB2YWx1ZSA9IFZhbHVlLCAtRGF0ZSkgCmdncGxvdChlY28sIGFlcyh4ID0gRGF0ZSwgeSA9IFZhbHVlKSkgKyBnZW9tX2xpbmUoKSArIGZhY2V0X2dyaWQoVmFyaWFibGUgfiAuLCAgc2NhbGVzID0gJ2ZyZWUnKQpgYGAKCk9idmlvdXNseSwgdGhlc2Ugc2VyaWVzIGFyZSBub3Qgc3RhdGlvbmFyeSAoYXQgbGVhc3QgdGhlIGZpcnN0IHR3byksIGlmIG9ubHkgYmVjYXVzZSBvZiB0aGVpciB0cmVuZCB3aGljaCBpbXBsaWVzIHRoYXQgdGhlaXIgbWVhbiBpbmNyZWFzZXMgd2l0aCB0aW1lICh0aGUgcmFuZ2Ugb2YgdGhlIGRpc3RyaWJ1dGlvbiBpcyBjbGVhcmx5IG5vdCBjb25zdGFudCkuCk9uZSBwb3NzaWJsZSBzb2x1dGlvbiBpcyB0byBzdHVkeSB2YXJpYXRpb25zOiAKJCRcRGVsdGEgWF90ID0gWF90LVhfe3QtMX0kJApgYGB7ciB2YXJpYXRpb25zLCB3YXJuaW5nID0gRkFMU0V9CmVjb25vbWljcyA8LSBlY29ub21pY3MgJT4lIG11dGF0ZShHRFBfdmFyID0gR0RQIC0gbGFnKEdEUCkpICAgICMgUmF3IHZhcmlhdGlvbiBpbiBHRFAKZ2dwbG90KGVjb25vbWljcywgYWVzKHggPSBEYXRlLCB5ID0gR0RQX3ZhcikpICsgZ2VvbV9wb2ludCgpCmBgYAoKQ2xlYXJseSwgdGhpcyBpcyBiZXR0ZXIuIEluZGVlZCwgaXQgc2VlbXMgdGhhdCB0aGUgdmFyaWF0aW9ucyBpbiB0aGUgRnJlbmNoIEdEUCBkaXNwbGF5IGEgc29tZXdoYXQgY29oZXJlbnQgcGF0dGVybi4gCk5ldmVydGhlbGVzcywgdGhlIGNsYXNzaWNhbCB0cmFuc2Zvcm0gdXNlZCB0byBgc3RhdGlvbmFyaXNlJyBzZXJpZXMgY29uc2lzdHMgaW4gY29tcHV0aW5nIGdyb3d0aCwgaS5lLiwgcmVsYXRpdmUgdmFyaWF0aW9uczogJCRyX3Q9XGZyYWN7WF90fXtYX3t0LTF9fS0xJCQKSSB1c2UgdGhlIG5vdGF0aW9uIHIgYmVjYXVzZSBpbiBmaW5hbmNlLCB0aGVzZSBzZXJpZXMgYXJlIGNhbGxlZCAncmV0dXJucycuIEhlbmNlZm9ydGgsIEkgd2lsbCBvZnRlbiB1c2UgdGhpcyBhYnVzZSBvZiBsYW5ndWFnZS4KCmBgYHtyIHJldHVybnMsIHdhcm5pbmcgPSBGQUxTRX0KZWNvbm9taWNzIDwtIGVjb25vbWljcyAlPiUgbXV0YXRlKEdEUF9ncm93dGggPSBHRFAgLyBsYWcoR0RQKSAtIDEpICMgR3Jvd3RoIGluIEdEUApnZ3Bsb3QoZWNvbm9taWNzLCBhZXMoeCA9IERhdGUsIHkgPSBHRFBfZ3Jvd3RoKSkgKyBnZW9tX2JhcihzdGF0ID0gImlkZW50aXR5IikgCmBgYAoKVGhlIG9yaWdpbmFsIHByb2Nlc3MgY2FuIGJlIHJlY29uc3RydWN0ZWQgZnJvbSBncm93dGgvcmV0dXJuczogJCRYX3Q9WF8wXHByb2Rfe3M9MX1edCgxK3JfcykuJCQgIAoKQmVsb3csIHdlIHNob3cgYW4gZXhhbXBsZSB0aGF0IHN0YXJ0cyBmcm9tIHJhbmRvbSByZXR1cm5zIGFuZCB0aGUgcmVjb21wb3NlcyB0aGUgaW50ZWdyYXRlZCBwcm9jZXNzLgoKYGBge3IgcmVjb25zdHJ1Y3Rpb259CnIgPC0gcnVuaWYoMTIsIG1pbiA9IC0wLjUsIG1heCA9IDAuNSkgICAjIFdlIGdlbmVyYXRlIGNvbXBsZXRlbHkgcmFuZG9tIHJldHVybnMKeCA8LSBjdW1wcm9kKDErcikgICAgICAgICAgICAgICAgICAgICAgICMgV2UgdXNlIHRoZSBjdW11bGF0aXZlIHByb2R1Y3Qgb3ZlciAxK3IKdGltZSA8LSByZXAoMToxMikgICAgICAgICAgICAgICAgICAgICAgICMgV2UgZ2VuZXJhdGUgdGltZQpkYXRhIDwtIGRhdGEuZnJhbWUodGltZSwgciwgeCkgICAgICAgICAgIyBXZSBhZ2dyZWdhdGUgdGhlIGRhdGEKZGF0YSA8LSBkYXRhICU+JSBnYXRoZXIoa2V5ID0gdHlwZSwgdmFsdWUgPSB2YWx1ZSwgLXRpbWUpICMgQW5kIHB1dCBpdCBpbnRvIGdncGxvdCBmb3JtYXQKZGF0YSAlPiUgZ2dwbG90KGFlcyh4ID0gdGltZSwgeSA9IHZhbHVlKSkgKyBnZW9tX2xpbmUoKSArZmFjZXRfZ3JpZCh0eXBlfi4sIHNjYWxlcyA9ICJmcmVlIikKYGBgCgoKV2hlbiByZXR1cm5zICh0b3AgcGxvdCkgYXJlIG5lZ2F0aXZlIChyZXNwLiBwb3NpdGl2ZSksIHRoZSBwcm9jZXNzIHggKGJvdHRvbSBwbG90KSBkZWNyZWFzZXMgKHJlc3AuIGluY3JlYXNlcykuCgoKIyMgQ2xhc3NpY2FsIHByb2Nlc3NlcwojIyMgVGhlIHJhbmRvbSB3YWxrClRoZSBtb3N0IHR5cGljYWwgcmFuZG9tIHByb2Nlc3MuIEluY3JlYXNpbmcgb3IgZGVjcmVhc2luZyBieSBvbmUgd2l0aCBwcm9iYWJpbGl0eSAxLzIgKHN1bSBvZiBCZXJub3VsbGkgcmVhbGlzYXRpb25zKS4KYGBge3IgUldfMX0KeCA8LSAwICAgICAgICAgICAgICAjIEEgZmlyc3QgZXhhbXBsZSwgc3RlcC1ieS1zdGVwLiBUaGUgcHJvY2VzcyBzdGFydHMgYXQgemVyby4KbmJfc3RlcHMgPC0gMTUgICAgICAjIFdlIHNpbXVsYXRlIDE1IHBvaW50cwpmb3IobiBpbiAyOm5iX3N0ZXBzKXsKICB4W25dIDwtIHhbbi0xXSArIDIgKiByYmlub20oMSwgMSwgMC41KSAtIDEgIyBUaGUgQmVybm91bGxpIHZhcmlhYmxlcyBhcmUgbm90IHN0cmFpZ2h0Zm9yd2FkbHkgZ2VuZXJhdGVkCn0KZGF0YS5mcmFtZShkYXRlID0gMTpuYl9zdGVwcywgeCkgJT4lIGdncGxvdChhZXMoeCA9IGRhdGUsIHkgPSB4KSkgKyBnZW9tX2xpbmUoKQpgYGAKCkluc3RlYWQgb2YgYSBsb29wLCB3ZSBjYW4gZGlyZWN0bHkgdXNlIHRoZSBjdW11bGF0aXZlIHN1bS4KYGBge3IgUldfMn0KeCA8LSAwICAgICAgICAgICAgICAjIEEgcXVpY2tlciwgbmVhdGVyIHdheSB0byBwcm9jZWVkLgpuYl9zdGVwcyA8LSA4MCAgICAgICMgQSBsYXJnZXIgbnVtYmVyIG9mIHBvaW50cwp4IDwtICgyKnJiaW5vbShuYl9zdGVwcywgMSwgMC41KSAtIDEpICU+JSBjdW1zdW0oKSAgIyBIZXJlLCB3ZSBkaXJlY3RseSB1c2UgdGhlIGN1bXVsYXRpdmUgc3VtCmRmIDwtIGRhdGEuZnJhbWUodGltZSA9IDE6bmJfc3RlcHMsIHgpICAgICAgICAgICAgICAjIFdyYXBwaW5nIHRoZSBkYXRhCmdncGxvdChkZiwgYWVzKHggPSB0aW1lLCB5ID0geCkpICsgZ2VvbV9saW5lKCkKYGBgCgojIyMgQXV0b3JlZ3Jlc3NpdmUgcHJvY2Vzc2VzCkF1dG9yZWdyZXNzaXZlIHByb2Nlc3NlcyBhZGQgbWVtb3J5IHRvIHRoZSB0cmFqZWN0b3J5OiBpbmNyZW1lbnRzIGFyZSBubyBsb25nZXIgaW5kZXBlbmRlbnQuIEJlbG93LCB3ZSBidWlsZCBhIHRyYWplY3Rvcnkgb2Ygb25lIHN1Y2ggcHJvY2VzcywgdGhlIEFSKDEpLCBkZWZpbmVkIGJ5ICQkWF90ID0gYStiWF97dC0xfStcZXBzaWxvbl90JCQKYGBge3IgQVIoMSl9CmEgPC0gMSAgICMgUGFyYW1ldGVyCmIgPC0gMC41ICMgUGFyYW1ldGVyOiBtdXN0IGJlIHNtYWxsZXIgdGhhbiAxIGluIGFic29sdXRlIHZhbHVlLiBJZiBub3Q6IGV4cGxvc2lvbgp4IDwtIDEgICAjIEluaXRpYWwgdmFsdWUuCm5iX3N0ZXBzIDwtIDIwCmZvcihuIGluIDI6bmJfc3RlcHMpewogIHhbbl0gPC0gYSArIGIgKiB4W24tMV0gKyBybm9ybSgxKSAjIFVwZGF0aW5nIHRoZSBwcm9jZXNzIGFjY29yZGluZyB0byBmb3JtdWxhCn0KZGF0YS5mcmFtZShkYXRlID0gMTpuYl9zdGVwcywgeCkgJT4lIGdncGxvdChhZXMoeCA9IGRhdGUsIHkgPSB4KSkgKyBnZW9tX2xpbmUoKQpgYGAKCkxldCdzIHVzZSBhIGRlZGljYXRlZCBmdW5jdGlvbi4gUmVtZW1iZXIsIFIgaGFzIG1hbnkgdXNlZnVsIGZ1bmN0aW9ucy9wYWNrYWdlcy4gVGhlIEFSIHByb2Nlc3MgaXMgYSBzcGVjaWFsIGNhc2Ugb2YgYSBtb3JlIGdlbmVyYWwgZmFtaWx5OiB0aGUgQVJJTUEgcHJvY2Vzc2VzLiBTaW11bGF0aW9uIGlzIGRvbmUgdmlhIGFyaW1hLnNpbS4gVGhlIGRvY3VtZW50YXRpb24gaXMgYXZhaWxhYmxlIGF0Cmh0dHBzOi8vc3RhdC5ldGh6LmNoL1ItbWFudWFsL1ItZGV2ZWwvbGlicmFyeS9zdGF0cy9odG1sL2FyaW1hLnNpbS5odG1sCgpgYGB7ciBBUigxKSB2MiwgbWVzc2FnZSA9IEZBTFNFLCB3YXJuaW5nID0gRkFMU0V9Ck5iX3BvaW50cyA8LSAxMDAKQVIgPC0gYXJpbWEuc2ltKGxpc3QoYXIgPSBjKDAuOSkpLCBOYl9wb2ludHMpICAgIyBIZXJlIGlzIHRoZSBzaW11bGF0aW9uIHBhcnQKQVIgPC0gZGF0YS5mcmFtZShUaW1lID0gMTpOYl9wb2ludHMsIEFSKSAgICAgICAgIyBXcmFwcGluZyB0aGUgZGF0YSAgICAgICAgICAgICAgICAgICAgICAgCkFSICU+JSBnZ3Bsb3QoYWVzKHggPSBUaW1lLCB5ID0gQVIpKSArIGdlb21fbGluZSgpICMgUGxvdHRpbmcKYGBgCgojIyMgQXV0b2NvcnJlbG9ncmFtClRoZSBhdXRvY29ycmVsb2dyYW0gY29tcHV0ZXMgdGhlIGNvcnJlbGF0aW9uIGJldHdlZW4gbGFnZ2VkIHZhbHVlcyBvZiB0aGUgc2VyaWVzLiBBc3N1bWluZyB0aGUgc2VyaWVzIGlzIHN0YXRpb25hcnkgd2l0aCBjb25zdGFudCBtZWFuICRtJCBhbmQgdmFyaWFuY2UgJHYkLCBpdCBpcyBkZWZpbmVkIGFzOiAkJFxyaG9faj12XnstMX1cbWF0aGJie0V9WyhYX3QtbSkoWF97dC1qfS1tKV0kJApJdCBpcyBjb21wdXRlZCBpbiBSIHdpdGggdGhlIGFjZigpIGZ1bmN0aW9uIGFuZCBhdXRvbWF0aWNhbGx5IHByb3ZpZGVzIHRoZSB2YWx1ZXMgZm9yICRqXGluICgwLDIwKSQuCmBgYHtyIGFjZn0KYWMgPC0gYWNmKGVjb25vbWljcyRHRFBfZ3Jvd3RoLCBuYS5hY3Rpb24gPSBuYS5wYXNzLCBwbG90ID0gRkFMU0UpCnBsb3QoYWMsIG1haW4gPSAiIikgIyBSZW1vdmVzIHRoZSB0aXRsZQphYzIgPC0gYWNmKEFSJEFSLCBwbG90ID0gRkFMU0UpCnBsb3QoYWMyLCBtYWluID0gIiIpCmBgYAoKCldlIHNlZSBhdXRvY29ycmVsYXRpb24gaW4gdGhlIEZyZW5jaCBlY29ub21pYyBncm93dGguIFRoaXMgcHJvYmFibHkgY29tZXMgZnJvbSBzby1jYWxsZWQgZWNvbm9taWMgY3ljbGVzIChwZXJpb2RzIG9mIGdyb3d0aCBmb2xsb3dlZCBieSByZWNlc3Npb25zKS4gRm9yIGFuIEFSKDEpIHByb2Nlc3MsIHRoZSBhdXRvY29ycmVsYXRpb24gZGVjcmVhc2VzIGF0IHRoZSByYXRlICRiXmokLiBJbiB0aGUgYWJvdmUgZXhhbXBsZSwgYj0wLjksIHNvIHRoZSBkZWNyZWFzZSBpcyBzbG93LgoKIyMjIEVzdGltYXRpb24KRm9yIGEgZ2l2ZW4gc2VyaWVzLCBvbmUgbWF5IHdhbnQgdG8gZXN0aW1hdGUgdGhlIGNvcnJlc3BvbmRpbmcgQVIgcGFyYW1ldGVycyAoaWYgdGhhdCBzZWVtcyByZWxldmFudCkuIEluIFIsIHNldmVyYWwgZXN0aW1hdG9ycyBleGlzdCAoT0xTLCBNTEUpLiBXZSBsb29rIGEgc2V2ZXJhbCBiZWxvdy4KYGBge3IgZXN0aW1hdGlvbn0KYXIoZWNvbm9taWNzJEdEUF9ncm93dGgsIG5hLmFjdGlvbiA9IG5hLnBhc3MsIG9yZGVyLm1heCA9IDEpICAgIyBBdXRvcmVncmVzc2l2ZSBjb2VmZmljaWVudApzZXJpZXMgPC0gZWNvbm9taWNzJEdEUF9ncm93dGhbMjogbnJvdyhlY29ub21pY3MpXSAgICAgICAgICAgICAjIFRha2luZyBvdXQgdGhlIE5BIHRlcm0KYXIoc2VyaWVzLCBvcmRlci5tYXggPSAxLCBtZXRob2QgPSAib2xzIikgICAgICAgICAgICAgICAgICAgIyBDbGFzc2ljYWwgbGVhc3Qtc3F1YXJlcyBtZXRob2QKYXIoc2VyaWVzLCBvcmRlci5tYXggPSAxLCBtZXRob2QgPSAibWxlIikgICAgICAgICAgICAgICAgICAgIyBNYXggbGlrZWxpaG9vZApyaG8gPC0gY29yKGVjb25vbWljcyRHRFBfZ3Jvd3RoLCBsYWcoZWNvbm9taWNzJEdEUF9ncm93dGgpLCB1c2UgPSAiY29tcGxldGUiKQpyaG8gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAjIE1vbWVudC1iYXNlZCB2YWx1ZQpgYGAKCk9uY2UgdGhlIG1vZGVsIGlzIGVzdGltYXRlZCwgd2UgY2FuIGZvcmVjYXN0IHRoZSBmdXR1cmUgdmFsdWUuICAKCiMjIyBGb3JlY2FzdApJbiBSLCBhIHByZWRpY3QoKSBmdW5jdGlvbiBpcyBvZnRlbiBwcm92aWRlZCBpbiB0aW1lLXNlcmllcyBwYWNrYWdlcy4gQnV0IGJlIGNhcmVmdWwsIHRoZSBvdXRwdXQgY2hhbmdlcyBmcm9tIG9uZSB0byBhbm90aGVyLgpgYGB7ciBmb3JlY2FzdH0KYXJfZXN0IDwtIGFyKHNlcmllcywgb3JkZXIubWF4ID0gMSwgbWV0aG9kID0gIm1sZSIpIApwcmVkaWN0KGFyX2VzdCkKMC41ODMqc2VyaWVzWzExMl0gKyBtZWFuKHNlcmllcykqKDEtMC41ODMpICMgTWFudWFsIGNoZWNrIQpgYGAKV2UgY29tcGFyZSBpdCB0byB0aGUgY29uZGl0aW9uYWwgbWVhbjogcHJldHR5IGNsb3NlLiBUaGUgY29uZGl0aW9uYWwgbWVhbiBpcyBzaW1wbHkgJFxtYXRoYmJ7RX1bWF97dCsxfXxYX3RdPVxoYXR7YX0rXGhhdHtifVhfdCQuCgoKIyMgTXVsdGl2YXJpYXRlIGFuYWx5c2lzCkluIHRoaXMgc2VjdGlvbiwgd2UgY29tZSBiYWNrIHRvIG1hY3JvLWVjb25vbWljIGRhdGEuCiMjIyBGaXJzdCBzdGVwcwpPZnRlbiwgdmFyaWFibGVzIGluc2lkZSBhIHNhbXBsZSBhcmUgZXhwZWN0ZWQgdG8gYmUgcmVsYXRlZC4gSGVuY2UsIHVuZGVyc3RhbmRpbmcgdGhlIHJlbGF0aW9uc2hpcHMgYmV0d2VlbiB0aGVtIGlzIGNydWNpYWwsIGVzcGVjaWFsbHkgZm9yIHByZWRpY3Rpb24gcHVycG9zZXMuCmBgYHtyIG11bHRpX2VjbywgbWVzc2FnZSA9IEZBTFNFfQplY29ub21pY3MgPC0gZWNvbm9taWNzICU+JSBtdXRhdGUoVW5lbXBfZ3Jvd3RoID0gVW5lbXBsb3ltZW50IC8gbGFnKFVuZW1wbG95bWVudCkgLSAxKSAjIEdyb3d0aCBvZiB1bmVtcGxveW1lbnQKY29yKGVjb25vbWljcyRHRFAsIGVjb25vbWljcyRVbmVtcGxveW1lbnQpICAgICAgICAgICAgICAgICAgICAjIENvcnJlbGF0aW9uIEdEUC9VbmVtcApjb3IoZWNvbm9taWNzJEdEUF9ncm93dGgsIGVjb25vbWljcyRVbmVtcF9ncm93dGgsIHVzZSA9ICJjb21wbGV0ZSIpICMgQ29ycmVsYXRpb24gR0RQIGdyb3d0aCAvIFVuZW1wIGdyb3d0aApjb3IobGFnKGVjb25vbWljcyRHRFBfZ3Jvd3RoKSwgZWNvbm9taWNzJFVuZW1wX2dyb3d0aCwgdXNlID0gImNvbXBsZXRlIikgIyBDb3JyZWxhdGlvbiBwYXN0IEdEUCBncm93dGggLyBVbmVtcCBncm93dGgKYGBgClJhdyBHRFAgYW5kIHVuZW1wbG95bWVudCBhcHBlYXIgdW5yZWxhdGVkLiBCdXQgcmVsYXRpdmUgdmFyaWF0aW9ucyBpbiBib3RoIHZhcmlhYmxlcyBhcmUgbmVnYXRpdmVseSBjb3JyZWxhdGVkOiB3aGVuIEdEUCBpbmNyZWFzZXMsIHVuZW1wbG95bWVudCBkZWNyZWFzZXMuIFRoZSBsYXN0IGNvcnJlbGF0aW9uIHNob3cgdGhhdCBldmVuIHRoZSBwcmV2aW91cyB2YWx1ZSBvZiBjaGFuZ2UgaW4gZ3Jvd3RoIGlzIGNvcnJlbGF0ZWQgdG8gY3VycmVudCBjaGFuZ2UgaW4gdW5lbXBsb3ltZW50LgoKQmVsb3csIHdlIHZpc3VhbGx5IGNvbmZpcm0gdGhlc2UgcmVzdWx0cy4gRmlyc3QsIHZhcmlhdGlvbnMgaW5kZWVkIGNvLW1vdmUgaW4gb3Bwb3NpdGUgZGlyZWN0aW9ucy4gU2Vjb25kLCB0aGUgbGluZWFyIGFwcHJveGltYXRpb24gYmV0d2VlbiB0aGUgdHdvIHZhcmlhYmxlcyBkb2VzIGhhdmUgYSBuZWdhdGl2ZSBzbG9wZSwgdGhlcmVieSBpbmRpY2F0aW5nIG5lZ2F0aXZlIGNvcnJlbGF0aW9uLgpgYGB7ciBtdWx0aV92aXN1LCBtZXNzYWdlID0gRkFMU0UsIHdhcm5pbmcgPSBGQUxTRX0KZGF0YSA8LSBlY29ub21pY3MgJT4lIHNlbGVjdChEYXRlLCBHRFBfZ3Jvd3RoLCBVbmVtcF9ncm93dGgpICU+JSBnYXRoZXIoa2V5ID0gSW5kaWNhdG9yLCB2YWx1ZSA9IFZhbHVlLCAtRGF0ZSkgCmRhdGEgJT4lIGdncGxvdChhZXMoeCA9IERhdGUsIHkgPSBWYWx1ZSkpICsgCiAgZ2VvbV9iYXIoc3RhdCA9ICJpZGVudGl0eSIpICsgZmFjZXRfZ3JpZChJbmRpY2F0b3J+Liwgc2NhbGVzID0gImZyZWUiKSArCiAgdGhlbWUoYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoYW5nbGUgPSA5MCkpCmVjb25vbWljcyAlPiUgZ2dwbG90KGFlcyh4ID0gR0RQX2dyb3d0aCwgeSA9IFVuZW1wX2dyb3d0aCkpICsgZ2VvbV9wb2ludCgpICsgZ2VvbV9zbW9vdGgobWV0aG9kID0gImxtIikKYGBgCgojIyMgRXN0aW1hdGlvbgpBIHNpbXBsZSBhcHByb2FjaCBmb3IgbXVsdGl2YXJpYXRlIHRpbWUtc2VyaWVzIGlzIHRoZSB2ZWN0b3IgYXV0by1yZWdyZXNzaXZlIG1vZGVsLiBJdCByZWFkcwokJFxtYXRoYmZ7WH1fdD1cbWF0aGJme0F9XG1hdGhiZntYfV97dC0xfStcbWF0aGJme1xlcHNpbG9ufV90LCQkCndoZXJlICRcbWF0aGJme1h9X3QkIGFuZCAkXG1hdGhiZntcZXBzaWxvbn1fdCQgYXJlICRuJC1kaW1lbnNpb25hbCB2ZWN0b3JzIGFuZCAkXG1hdGhiZntBfSQgaXMgYW4gJG5cdGltZXMgbiQgbWF0cml4LgoKQmVsb3csIHdlIGVzdGltYXRlIGEgdmVjdG9yIGF1dG9yZWdyZXNzaW9uIG1vZGVsIG9uIEdEUCBhbmQgdW5lbXBsb3ltZW50IGdyb3d0aHMuCmBgYHtyIFZBUl9lc3QsIG1lc3NhZ2UgPSBGQUxTRX0KaWYoIXJlcXVpcmUodmFycykpe2luc3RhbGwucGFja2FnZXMoInZhcnMiKX0KbGlicmFyeSh2YXJzKSAgICAgIyBUaGlzIGlzIHRoZSBwYWNrYWdlIGZvciBWQVIgbW9kZWxzCmxpYnJhcnkodGlkeXZlcnNlKQojIEZJUlNULCB3ZSBwcmVwYXJlIHRoZSBkYXRhCnZhcl9kYXRhIDwtIGVjb25vbWljcyAlPiUgZHBseXI6OnNlbGVjdChHRFBfZ3Jvd3RoLCBVbmVtcF9ncm93dGgpICMgQmV3YXJlOiBNQVNTIHBhY2thZ2Ugb3ZlcnJpZGVzIHNlbGVjdCgpIGZ1bmN0aW9uCnRyYWluX2RhdGEgPC0gdmFyX2RhdGFbMjoxMTIsXSAjIEZpdHRpbmcgdGhlIG1vZGVsIG9uIGFsbCBkYXRlcyBidXQgdGhlIGxhc3QKdGVzdF9kYXRhIDwtIHZhcl9kYXRhWzExMyxdICAgICMgVGVzdGluZyB0aGUgbW9kZWwgb24gdGhlIGxhc3QgZGF0ZQpWQVJfZXN0IDwtIFZBUih0cmFpbl9kYXRhLCBwID0gMikgICAjIEZpdHRpbmcKVkFSX2VzdCAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIyBTaG93aW5nIHRoZSByZXN1bHQKYGBgClRoZSBpbnRlcnByZXRhdGlvbiBpcyBpbnRlcmVzdGluZy4gVGhlIGZpcnN0IGNvZWZmaWNpZW50cyBwZXJ0YWluIHRvIHRoZSBHRFAgZXF1YXRpb24uIFdlIHNlZSB0aGF0IHRoZSBzdHJvbmcgY29lZmZpY2llbnRzIChlc3BlY2lhbGx5IHRoZSBmaXJzdCBvbmUpIGFyZSBhc3NvY2lhdGVkIHRvIEdEUC4gVGhpcyBtZWFucyB0aGF0IHBhc3QgR0RQIGlzIHRoZSBtb3N0IGltcG9ydGFudCB0byBmb3JlY2FzdCBmdXR1cmUgR0RQICh0aGUgc28tY2FsbGVkIGVjb25vbWljIGN5Y2xlcykuICAKVGhlIHNlY29uZCBiYXRjaCBvZiBjb2VmZmljaWVudHMgYXJlIGxpbmtlZCB0byB1bmVtcGxveW1lbnQuIEhlcmUsIGFnYWluLCB0aGUgc3Ryb25nZXN0IHZhbHVlcyBjb21lIGZyb20gcGFzdCByZWFsaXNhdGlvbnMgb2YgR0RQLiBIZW5jZSwgaW4gdGhlIGRldGVybWluYXRpb24gb2YgZnV0dXJlIHVuZW1wbG95bWVudCByYXRlcywgZ3Jvd3RoIGluIEdEUCBpcyBtb3JlIGltcG9ydGFudCB0aGFuIHBhc3QgZ3Jvd3RoIGluIHVuZW1wbG95bWVudC4gV2hpY2ggbWFrZXMgc2Vuc2UuCgoKIyMjIEZvcmVjYXN0IAoKRmluYWxseSwgdGhlIGZvcmVjYXN0LgpgYGB7ciBWQVJfcHJlZH0KcHJlZGljdChWQVJfZXN0LCBuLmFoZWFkID0gMSkgICMgVGhlIHByZWRpY3QoKSBmdW5jdGlvbgp0ZXN0X2RhdGEKYGBgCgpUaGUgcXVhbGl0eSBvZiB0aGUgZm9yZWNhc3QgaW4gdGhpcyBwYXJ0aWN1bGFyIGV4ZW1wbGUgaXMgbm90IGltcHJlc3NpdmUuLi4KCgojIyBFeGVyY2lzZXMKCiMjIyBQdXJzdWluZyB0aGUgZWNvbm9taWMgc3R1ZHkgPT4gQ1BJCjEpIEFkZCB0aGUgcmVsYXRpdmUgdmFyaWF0aW9uIChncm93dGgpIG9mIHRoZSBDUEkgaW4gdGhlIGRhdGEuCjIpIFBsb3QgaXRzIHRpbWUtc2VyaWVzCjMpIEZpdCAoZXN0aW1hdGUpIGFuIEFSKDEpIG1vZGVscyBib3RoIGZvciB0aGUgQ1BJIGFuZCwgYWZ0ZXJ3YXJkcywgZm9yIGl0cyByZWxhdGl2ZSB2YXJpYXRpb25zLiBJcyB0aGUgcmF3IENQSSBzdGF0aW9uYXJ5PyBIb3cgY2FuIHdlIGludGVycHJldCB0aGUgb3V0b21lIG9mIHRoZSBzZWNvbmQgZXN0aW1hdGlvbj8KCiMjIyBVbmVtcGxveW1lbnQ6IGNhbiBBUkNIIG1vZGVscyBpbXByb3ZlIHRoZSBwcmVkaWN0aW9uPwowKSBJbnN0YWxsIGFuZCBsb2FkIHRoZSB0c2VyaWVzIHBhY2thZ2UuCjEpIEhhdmUgYSBsb29rIGF0IHRoZSBkb2N1bWVudGF0aW9uIG9mIHRoZSBwYWNrYWdlOgpodHRwczovL2NyYW4uci1wcm9qZWN0Lm9yZy93ZWIvcGFja2FnZXMvdHNlcmllcy90c2VyaWVzLnBkZgpNb3JlIHByZWNpc2VseSwgd2Ugd2lsbCB1c2UgdGhlIGdhcmNoKCkgYW5kIHByZWRpY3QoKSBmdW5jdGlvbnMuCjIpIEZpdCBhbiBBUkNIIG1vZGVsIG9uIGFsbCB2YWx1ZXMgb2YgdGhlIHJlbGF0aXZlIFVuZW1wIHZhcmlhdGlvbnMsIGV4Y2VwdCB0aGUgbGFzdCBvbmUuIFByZWRpY3QgdGhlIGxhc3QgdmFsdWUuIEZvciBhIHNpbXBsZSBhcmNoLCB0aGUgc2Vjb25kIGFyZ3VtZW50IGlzIG9yZGVyID0gYygwLDEpLg==