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

  1. Add the relative variation (growth) of the CPI in the data.
  2. Plot its time-series
  3. 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?

  1. Install and load the tseries package.
  2. 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.
  3. 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==