This work is licensed under the Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International (CC BY-NC-SA 4.0)

1 Introduction

  • Associate Professor of Statistical Genomics in Ghent University

  • Advocate of open research and open teaching: All our teaching materials, methods, tools and analyses for papers are open source and freely available on github

  • Method development for differential analysis of gene and protein expression

  • Background in environmental engineering with a PhD in environmental statistics with a focus on timeseries analysis of river water quality data

  • Intro to

    • State Space Models
    • Kalman filter & smoother
    • Applied on cryptocurrency data
    • Brief intro and illustration only, by no means the current state of the art
    • More advanced State Space Models required for hedging or predicting cryptocurrency data in practice.


1.1 Useful functions and load libraries

Click to see loaded libraries

library(astsa)
library(tidyverse)
## ── Attaching packages ─────────────────────────────────────── tidyverse 1.3.1 ──
## ✔ ggplot2 3.3.3     ✔ purrr   0.3.4
## ✔ tibble  3.1.1     ✔ dplyr   1.0.5
## ✔ tidyr   1.1.3     ✔ stringr 1.4.0
## ✔ readr   1.4.0     ✔ forcats 0.5.1
## ── Conflicts ────────────────────────────────────────── tidyverse_conflicts() ──
## ✖ dplyr::filter() masks stats::filter()
## ✖ dplyr::lag()    masks stats::lag()
library(GGally)
## Registered S3 method overwritten by 'GGally':
##   method from   
##   +.gg   ggplot2
library(gridExtra)
## 
## Attaching package: 'gridExtra'
## The following object is masked from 'package:dplyr':
## 
##     combine
library(gifski)

Click to see function to retreive coin data from coingecko

# coinSymbol: symbol of cryptocurrency token or coin
# days: past number of days you want to retrieve data for. "max" returns all available data for a token or coin.
# currency: fiat currency you want to use for price ("usd","eur","jpy", ...)
# interval: time interval on which you want to retreive data.

retreiveCoinData <- function(
                              coinSymbol="btc", 
                              days = "max", 
                              currency = "usd", 
                              interval = "daily")
{   
  #Retreive coinlist to lookup coin id
  coinlist <- httr::GET("https://api.coingecko.com/api/v3/coins/list")$content %>% 
    rawToChar %>% 
    jsonlite::fromJSON(.)
  
  coinId <- coinlist %>% filter(symbol == coinSymbol) %>% pull(id)
  
  #retreive coin data via API
  coinData <- httr::GET(
    paste0(
      "https://api.coingecko.com/api/v3/coins/",
      coinId,
      "/market_chart?vs_currency=",
      currency,
      "&days=",
      days,
      "&interval=",
      interval)
    )$content %>% 
    rawToChar %>% 
    jsonlite::fromJSON(.)
  
  #store coin data as tible, include coin symbol in price name and covert unix time string to timestamp object
  coinData <- lapply(names(coinData), function(var,coinData) 
          {
            data <- coinData[[var]] %>% as_tibble()
            names(data) <- c(
                             "timestamp",
                             paste0(coinSymbol,"_",var)
                             )
            data <- data %>% 
              mutate(
                timestamp =  as.POSIXct(
                  timestamp/1000, 
                  origin = "1970-01-01",tz="GMT"
                  )
                ) 
            return(data)
          }, coinData = coinData
        )  %>% reduce(left_join, by = "timestamp")
  #rename prices column to include currency
  pricesId <- grep(names(coinData), pattern="prices")
  names(coinData)[pricesId] <- paste(
    names(coinData)[pricesId],
    currency,
    sep="_")
  
  #get rid of last time because coin gecko returns all daily data at 00:00 GMT and includes the last entry of the current day. 
  if(days=="max") 
      return(coinData) else 
        return (coinData[1:days,])
}

Click to see function for acf plots

Function to make ACF and PACF plots copied from https://github.com/mdelhey/mdutils

ggacf <- function(x, type = c("correlation", "covariance", "partial"), ci = 0.95, ...) {
    type <- match.arg(type)
    x.acf <- acf(x, type = type, plot = FALSE, ...)
    x.df <- with(x.acf, data.frame(lag, acf))
    x.ci <- qnorm((1 + ci)/2) / sqrt(length(x))
    print(x.ci)
    ggplot(data = x.df, aes(x = lag, y = acf)) +
        geom_hline(aes(yintercept = 0)) +
        geom_segment(aes(xend = lag, yend = 0)) +
        geom_hline(yintercept = c(x.ci, -x.ci), color = "blue", linetype = "dashed") +
        scale_y_continuous(breaks = seq(0.0, 1.0, 0.2))
}



1.2 Data exploration

Select coins bitcoin (btc), etherium (eth) and litecoin (ltc)

coinSymbols <- c("btc", "eth", "ltc")
  • We want to retreive data from January First 2019 up to today.
  • We have to calculate the number of days for which we want to retreive data for the coingecko API
todaysDate <- as.POSIXlt(Sys.Date(),tz="GMT")
startDate <- as.POSIXlt("2019-01-01",tz="GMT")
ndays <- as.double(todaysDate - startDate) + 1

Retreive data from coingecko. We will use the default currency (USD).

coinData <-  coinSymbols %>%
      map(retreiveCoinData,days=ndays) %>%
      reduce(left_join, by = "timestamp") %>%
      arrange(timestamp) %>% 
      mutate(date=as.Date(timestamp))
## Warning: The `x` argument of `as_tibble.matrix()` must have unique column names if `.name_repair` is omitted as of tibble 2.0.0.
## Using compatibility `.name_repair`.



1.2.1 Time series plots

Click to see code for plot

tsPlot <- coinData %>%
  select(date,ends_with("prices_usd")) %>%
  gather(coin, usd, -date) %>%
  ggplot(aes(date,usd)) +
  geom_line() +
  facet_wrap(~coin,nrow=3,scales="free_y")

tsPlot

tsPlot + scale_y_continuous(trans='log10')

Click to see code for plot

returnPlot1 <- coinData %>%
  select(ends_with("prices_usd")) %>%
  apply(.,2,function(x) diff(x)) %>% 
  as_tibble %>% 
  mutate(date = coinData$date[-1]) %>% 
  gather(coin, usd,-date) %>% 
  ggplot(aes(date,usd)) +
  geom_point() +
  ylab("Price Difference (USD)") +
  facet_wrap(~coin,nrow=3,scales="free_y")

returnPlot1

Click to see code for plot

returnPlot2 <- coinData %>%
  select(ends_with("prices_usd")) %>%
  apply(.,2,function(x) log10(x) %>% diff) %>% 
  as_tibble %>% 
  mutate(date = coinData$date[-1]) %>% 
  gather(coin, log_return,-date) %>% 
  ggplot(aes(date,log_return)) +
  geom_point() +
  ylab("Daily Log Return (log10)") +
  facet_wrap(~coin,nrow=3,scales="free_y")

returnPlot2



1.2.2 Correlation Structure

1.2.2.1 Correlation between cryptocurrency timeseries

Click to see code for plot

plotCoinData <- coinData %>%
  mutate(year = format(date,"%Y") %>% as.factor) %>%
  select(year,ends_with("prices_usd")) %>%
  ggpairs(progress = FALSE,
  mapping=ggplot2::aes(colour = year))

plotCoinData
## `stat_bin()` using `bins = 30`. Pick better value with `binwidth`.
## `stat_bin()` using `bins = 30`. Pick better value with `binwidth`.
## `stat_bin()` using `bins = 30`. Pick better value with `binwidth`.

  • There is a strong correlation between the cryptocurrency timeseries!

  • The correlation between the cryptocurrencies seems to change over time (Dynamic hedge ratio’s?)


1.2.2.2 Autocorrelation (within timeseries)

Autocorrelation: \[\text{cor}[y_t,y_{t-k}]\] with lag \(k=1,2,3,\ldots\)

Click to see code for plot

acfPlots <- paste0(coinSymbols,"_prices_usd") %>%
  map(
    function(x,coinData)
      coinData %>%
      pull(x) %>%
      log10 %>%
      ggacf +
      ggtitle(x),
      coinData=coinData
      )
## [1] 0.06730544
## [1] 0.06730544
## [1] 0.06730544

grid.arrange(grobs=acfPlots)

  • There is a very strong autocorrelation within time series!
  • Indeed the price at time t is strongly correlated to that at a few days (lags k) earlier.



Partial correlation?

\[\text{cor}[y_t,y_{t-k}\vert y_{t-1}\ldots y_{t-k+1}]\]

Click to see code for plot

pacfPlots <- paste0(coinSymbols,"_prices_usd") %>%
  map(
    function(x,coinData)
      coinData %>%
      pull(x) %>%
      log10 %>%
      ggacf(type="partial") +
      ylab("Partial ACF") +
      ggtitle(x),
      coinData=coinData
      )
## [1] 0.06730544
## [1] 0.06730544
## [1] 0.06730544

grid.arrange(grobs=pacfPlots)

  • The partial ACF only shows a direct correlation between the current price and the price at the previous day (lag 1).
  • Hence, the price of cryptocurrencies seems uncorrelated with that in the past when we condition on yesterdays price.

\[\ldots \rightarrow y_{t-2} \rightarrow y_{t-1} \rightarrow y_t \rightarrow y_{t+1} \rightarrow \ldots \]


1.2.3 Normality

Click to see code for plot

qqplots <- coinData %>%
  select(ends_with("prices_usd")) %>%
  apply(.,2,function(x) log10(x) %>% diff) %>% 
  as_tibble %>% 
  mutate(date = coinData$date[-1]) %>% 
  gather(coin, log_return,-date) %>% 
  ggplot(aes(sample = log_return)) +
  geom_qq() + 
  geom_qq_line() + 
  facet_wrap(~coin,nrow=3,scales="free_y")

qqplots

  • The log-differenced data are symmetric but non-normally distributed
  • In the remainder we will introduce Gaussian State Space Models
  • However, if the timeseries are non-Gaussian the parameter estimators are still assymptotically normally distributed.
  • In short time-series we can resort to the bootstrap to provide better inference.



2 State Space Models

2.1 Cryptocurrency example

  • We will log\(_\text{10}\)-transform the data and model it with a state space model.

  • Let \(\mathbf{y}_t\) be a vector of the log-transformed prices of btc, eth and ltc at time \(t\) \[\mathbf{y}_t=\begin{bmatrix}y^\text{btc}_t\\y^\text{eth}_t\\y^\text{ltc}_t\end{bmatrix}\]

  • Suppose that they are driven by an underlying unknown first order Markovian cryptocurrency price level, referred to as the state \(\mathbf{X}\) \[\mathbf{X}=\begin{bmatrix}X^\text{btc}_t\\X^\text{eth}_t\\X^\text{ltc}_t\end{bmatrix}\]

  • So the actual observed price \(\mathbf{y}_t\) is a noisy realisation of the underlying unknown price level (state) \(\mathbf{X}_t\).

\[ \begin{array}{ccccccccccc}\ldots & \rightarrow & \mathbf{X}_{t-2} & \rightarrow & \mathbf{X}_{t-1} & \rightarrow & \mathbf{X}_t & \rightarrow & \mathbf{X}_{t+1} & \rightarrow & \ldots\\ &&\downarrow&&\downarrow&&\downarrow&&\downarrow\\ \ldots & & \mathbf{y}_{t-2} & & \mathbf{y}_{t-1} & & \mathbf{y}_t & & \mathbf{y}_{t+1} & & \ldots \end{array} \]


  • Assume that \(\mathbf{y}_t\vert \mathbf{X}_t\) and \(\mathbf{X}_t\vert \mathbf{X}_{t-1}\) are multivariate normally (MVN) distributed


  • We can than formulate the following State Space Model:

\[ \left\lbrace \begin{array}{llll} \mathbf{y}_t&=&\mathbf{X}_t+\mathbf{v}_t &\text{with}\ \mathbf{v}_t\sim MVN(\mathbf{0},\mathbf{R})\\ \mathbf{X}_t&=&\boldsymbol{\Phi} \mathbf{X}_{t-1} + \mathbf{w}_t &\text{with}\ \mathbf{w}_t\sim MVN(\mathbf{0},\mathbf{Q}) \end{array} \right. \]

with

  • \(t = 1, \ldots,n\)
  • an \(3 \times 3\) transition matrix \(\boldsymbol{\Phi}\) and
  • independent \(3 \times 1\) vectors \(\mathbf{v}_t\) and \(\mathbf{w}_t\) with mean zero and variance-covariance matrix \(\mathbf{R}\) and \(\mathbf{Q}\), respectively.



2.2 General formulation of State Space Model

\[ \left\lbrace \begin{array}{llll} \mathbf{y}_t&=&\mathbf{A}_t\mathbf{X}_t+\mathbf{v}_t &with\ \mathbf{v}_t\sim MVN(\mathbf{0},\mathbf{R}_t)\\ \mathbf{X}_t&=&\boldsymbol{\Phi}_t \mathbf{X}_{t-1} + \mathbf{w}_t &with\ \mathbf{w}_t\sim MVN(\mathbf{0},\mathbf{Q}_t) \end{array} \right.\]

with

  • \(t=1,\ldots, n\)
  • first order Markovian state process \(\mathbf{X}_t=(X_{1t}...X_{mt})^T\),
  • \(\mathbf{y}_t=(y_{1t}...y_{pt})^T\)
  • \(\mathbf{v}_t \perp \!\!\! \perp \mathbf{w}_t\)
  • \(\mathbf{A}_t\) the \(p \times m\) measurement or observation matrix that links \(\mathbf{y}_t\) to \(\mathbf{X}_t\)
  • an \(m \times m\) transition matrix \(\boldsymbol{\Phi}_t\) and


Note, that the State Space Model is very flexible!

  • By embedding the AR(1) structure in the measurement equation, the time series model becomes much more general.

  • When \(\mathbf{v}_t\) and \(\mathbf{w}_t\) are allowed to be correlated, it can be shown that for specific choices the state-space model can also model specific ARMA models.

  • Extensions are very flexible: e.g. 

    • missing data,
    • exogeneous covariates,
    • more complex autocorrelation structures


2.3 Prediction, Filtering & Smoothing

  • From a practical view, a primary aim of the state-space analysis is to produce estimates of the states \(\mathbf{X}_t\) by using a set of observations \(\mathbf{Y}_r=(\mathbf{y}_1,\ldots, \mathbf{y}_r)^T\).

  • Indeed we want to infer on the underlying latent price level!

  • The estimation of \(\mathbf{X}_t\) given \(\mathbf{Y}_r\) is referred to as

    1. prediction for \(t>r\),
    2. filtering for \(t=r\), and
    3. smoothing for \(t<r\)


2.4 Kalman Filter and Smoother

2.4.1 Cryptocurrency example

\[ \begin{array}{ccccccccccc}\ldots & \rightarrow & \mathbf{X}_{t-2} & \rightarrow & \mathbf{X}_{t-1} & \rightarrow & \mathbf{X}_t & \rightarrow & \mathbf{X}_{t+1} & \rightarrow & \ldots\\ &&\downarrow&&\downarrow&&\downarrow&&\downarrow\\ \ldots & & \mathbf{y}_{t-2} & & \mathbf{y}_{t-1} & & \mathbf{y}_t & & \mathbf{y}_{t+1} & & \ldots\\\\ \end{array} \]

\[ \left\lbrace \begin{array}{llll} \mathbf{y}_t&=&\mathbf{X}_t+\mathbf{v}_t &\text{with}\ \mathbf{v}_t\sim MVN(\mathbf{0},\mathbf{R})\\ \mathbf{X}_t&=&\boldsymbol{\Phi} \mathbf{X}_{t-1} + \mathbf{w}_t &\text{with}\ \mathbf{w}_t\sim MVN(\mathbf{0},\mathbf{Q}) \end{array} \right. \]

So in our example \(\mathbf{A}_t=\mathbf{I}\)



2.4.1.1 Kalman Filter

  • Important for e.g. online estimation and prediction

  • Let \(\mathbf{Y}_r=(\mathbf{y}_1^T,\ldots,\mathbf{y}_r^T)^T\)

  • Prediction step: \[\text{E}\left[\mathbf{X}_t\vert \mathbf{Y}_{t-1}\right]\]

  • Update step when new observation comes in: \[\text{E}\left[\mathbf{X}_t\vert \mathbf{Y}_{t}\right]\]

  • Provides efficient factorisation of the likelihood

y <- coinData %>% 
  select(ends_with("_prices_usd")) %>% 
  log10 %>%
  as.matrix

num <- nrow(y)

# Measurement matrix A = I 
A=diag(3)  # creates 3x3 diagonal matrix

# initial values
mu0 <- matrix(0,3,1)
Sigma0 <- diag(c(.1,.1,.1),3)
Phi <- diag(1,3)
cQ <- diag(c(.1,.1,.1),3)
cR <- diag(c(.1,.1,.1),3)

em <- EM0(num = num, y = y, A = A, mu0 = mu0, Sigma0 = Sigma0, Phi = Phi, cQ = cQ, cR = cR, max.iter = 100, tol = .00001)

kf <- Kfilter0(num = num, y = y, A = A, mu0 = mu0, Sigma0 = em$Sigma0, Phi = em$Phi, cQ = chol(em$Q), cR = chol(em$R))
Click to see code to make plots

dataHlp <- coinData %>%
  select(date,ends_with("prices_usd")) %>%
  gather(coin, usd, -date) %>% 
  mutate(xf = kf$xf[,1,] %>% t(.) %>% c) %>%
  mutate(xfll = (kf$xf[,1,] -
           2 * sapply(1:ndays,function(i) kf$Pf[,,i] %>% diag %>% sqrt)) %>% t(.) %>% c) %>%
 mutate(xful = (kf$xf[,1,] +
           2 * sapply(1:ndays,function(i) kf$Pf[,,i] %>% diag %>% sqrt)) %>% t(.) %>% c) %>%
  mutate(xp = kf$xp[,1,] %>% t(.) %>% c) %>%
  mutate(xpll = (kf$xp[,1,] -
           2 * sapply(1:ndays,function(i) kf$Pp[,,i] %>% diag %>% sqrt)) %>% t(.) %>% c) %>%
 mutate(xpul = (kf$xp[,1,] +
           2 * sapply(1:ndays,function(i) kf$Pp[,,i] %>% diag %>% sqrt)) %>% t(.) %>% c) 
tplot <- as.Date("2020-12-31")
basePlot <- dataHlp %>% 
  filter(date > tplot) %>%
  ggplot(aes(date,usd)) +
  geom_point(alpha=0) +
  facet_wrap(~coin,nrow=3,scales="free_y") +
  theme_minimal()

stopDate <- dataHlp %>% pull(date) %>% max
startDate <- stopDate - 30

dataHlp2 <- dataHlp %>% 
  filter(date > tplot & date < startDate)

i <- 1
plotKFilterList <- list(
  basePlot +
  geom_point(shape=21,size=.5, data = dataHlp2) + 
  geom_ribbon(aes(ymin = 10^xful,ymax = 10^xfll), alpha = 0.45, data = dataHlp2) +
  geom_line(aes(date,10^xf), data = dataHlp2) + 
  ggtitle(" ") 
  )

for (t in seq(startDate,stopDate,1))
{
dataHlp2 <- dataHlp %>% 
  filter(date > tplot & date < t)
i <- i+1 
plotKFilterList[[i]] <- basePlot +
  geom_point(shape=21,size=.5, data = dataHlp2) + 
  geom_ribbon(aes(ymin = 10^xful,ymax = 10^xfll), alpha = 0.45, data = dataHlp2) +
  geom_line(aes(date,10^xf), data = dataHlp2) +
  geom_point(aes(date,10^xp),data = dataHlp %>% filter(date == t),color=2) +
   geom_segment(aes(x = date, y = 10^xpll, xend = date, yend = 10^xpul), color = 2, data = dataHlp %>% filter(date == t)) +
   ggtitle("Prediction")

i <- i+1 
plotKFilterList[[i]] <- plotKFilterList[[i-1]] +
  geom_point(aes(date,usd),data = dataHlp %>% filter(date == t)) + 
  ggtitle("New observation arrives")

i <- i + 1
dataHlp2 <- dataHlp %>% 
  filter(date > tplot & date <= t)
plotKFilterList[[i]] <-   basePlot +
  geom_point(shape=21,size=.5, data = dataHlp2) + 
  geom_ribbon(aes(ymin = 10^xful,ymax = 10^xfll), alpha = 0.45, data = dataHlp2) +
  geom_line(aes(date,10^xf), data = dataHlp2) + 
  geom_point(aes(date,10^xp),data = dataHlp %>% filter(date == t),color=2) +
  geom_segment(aes(x = date, y = 10^xpll, xend = date, yend = 10^xpul), color = 2, data = dataHlp %>% filter(date == t)) +
  geom_point(aes(date,usd),data = dataHlp %>% filter(date == t)) +
  ggtitle("Update Filter") 
}

2.4.1.2 Kalman Smoother

  • Offline applications: Use all available information \(\mathbf{Y}_N=(\mathbf{y}_1^T,\ldots,\mathbf{y}_n^T)^T\).
  • Smoothed estimator also based on measurements on later time instants \(t+1,\ldots,n\): \(\text{E}\left[\mathbf{X}_t\vert \mathbf{Y}_N\right]\)
ks <- Ksmooth0(num = num, y = y, A = A, mu0 = mu0, Sigma0 = em$Sigma0, Phi = em$Phi, cQ = chol(em$Q), cR = chol(em$R))
Click to see code for plot

tplot <- as.Date("2020-12-31")
basePlot <- dataHlp %>% 
  filter(date > tplot) %>%
  ggplot(aes(date,usd)) +
  geom_point(alpha=0) +
  facet_wrap(~coin,nrow=3,scales="free_y") +
  theme_minimal()

dataHlp2 <- dataHlp  %>% 
  mutate(xs = ks$xs[,1,] %>% t(.) %>% c) %>%
  mutate(xsll = (ks$xs[,1,] -
           2 * sapply(1:ndays,function(i) ks$Ps[,,i] %>% diag %>% sqrt)) %>% t(.) %>% c) %>%
 mutate(xsul = (ks$xs[,1,] +
           2 * sapply(1:ndays,function(i) ks$Pf[,,i] %>% diag %>% sqrt)) %>% t(.) %>% c)


plotKFilterPrediction <- basePlot +
  geom_point(shape=21,size=.5, data = dataHlp2 %>% filter(date > tplot)) + 
  geom_ribbon(aes(ymin = 10^xpul,ymax = 10^xpll), alpha = 0.45, data = dataHlp2 %>% filter(date > tplot)) +
  geom_line(aes(date,10^xp), data = dataHlp2 %>% filter(date > tplot)) + 
  ggtitle("Kalman one-step-ahead prediction") 

plotKFilter <- basePlot +
  geom_point(shape=21,size=.5, data = dataHlp2 %>% filter(date > tplot)) + 
  geom_ribbon(aes(ymin = 10^xful,ymax = 10^xfll), alpha = 0.45, data = dataHlp2 %>% filter(date > tplot)) +
  geom_line(aes(date,10^xf), data = dataHlp2 %>% filter(date > tplot)) + 
  ggtitle("Kalman filter") 

plotKSmoother <- basePlot +
  geom_point(shape=21,size=.5, data = dataHlp2 %>% filter(date > tplot)) + 
  geom_ribbon(aes(ymin = 10^xsul,ymax = 10^xsll), alpha = 0.45, data = dataHlp2 %>% filter(date > tplot)) +
  geom_line(aes(date,10^xs), data = dataHlp2 %>% filter(date > tplot)) + 
  ggtitle("Kalman smoother") 

plotKFilterPrediction
plotKFilter
plotKSmoother



2.4.2 Under the hood of the Kalman Filter

General formulation of the State Space Model

\[ \left\lbrace \begin{array}{llll} \mathbf{y}_t&=&\mathbf{A}_t\mathbf{X}_t+\mathbf{v}_t &with\ \mathbf{v}_t\sim MVN(\mathbf{0},\mathbf{R}_t)\\ \mathbf{X}_t&=&\boldsymbol{\Phi}_t \mathbf{X}_{t-1} + \mathbf{w}_t &with\ \mathbf{w}_t\sim MVN(\mathbf{0},\mathbf{Q}_t) \end{array} \right.\]

Assume that following are known

  • \(\mathbf{Y}_{t-1}=(\mathbf{y}_1^T,\ldots,\mathbf{y}_{t-1}^T)^T\)
  • \(\mathbf{X}_{t-1\vert t-1}=\text{E}\left[\mathbf{X}_{t-1}\vert \mathbf{Y}_{t-1}\right]\)
  • \(\mathbf{P}_{t-1\vert t-1}=\text{Var}\left[\mathbf{X}_{t-1}\vert \mathbf{Y}_{t-1}\right]\)


2.4.2.1 Prediction Step

\[ \left\lbrace \begin{array}{llll} \mathbf{y}_t&=&\mathbf{A}_t\mathbf{X}_t+\mathbf{v}_t &with\ \mathbf{v}_t\sim MVN(\mathbf{0},\mathbf{R}_t)\\ \mathbf{X}_t&=&\boldsymbol{\Phi}_t \mathbf{X}_{t-1} + \mathbf{w}_t &with\ \mathbf{w}_t\sim MVN(\mathbf{0},\mathbf{Q}_t) \end{array} \right.\]


\[ \begin{array}{rcl} \mathbf{X}_t\vert\mathbf{Y}_{t-1}&\sim& MVN(\mathbf{X}_{t\vert t-1}, \mathbf{P}_{t\vert t-1})\\\\ \mathbf{X}_{t\vert t-1}&=&\text{E}\left[\mathbf{X}_t\vert\mathbf{Y}_{t-1}\right]\\&=&\boldsymbol{\Phi}_t\text{E}\left[\mathbf{X}_{t-1}\vert\mathbf{Y}_{t-1}\right]\\ &=&\boldsymbol{\Phi}_t \mathbf{X}_{t-1\vert t-1}\\\\ \mathbf{P}_{t\vert t-1}&=&\text{Var}\left[\mathbf{X}_t\vert\mathbf{Y}_{t-1}\right] \\&=& \boldsymbol{\Phi}_t \text{Var}\left[\mathbf{X}_{t-1}\vert\mathbf{Y}_{t-1}\right] \boldsymbol{\Phi}_t^T+\text{Var}\left[\mathbf{w}_t\vert \mathbf{Y}_{t-1}\right]\\ &=&\boldsymbol{\Phi}_t \mathbf{P}_{t-1\vert t-1} \boldsymbol{\Phi}_t^T+\mathbf{Q}_t. \end{array} \]

One step ahead prediction: \[\text{E}\left[\mathbf{y}_{t}\vert \mathbf{Y}_{t-1}\right]=\mathbf{A}_t\mathbf{X}_{t\vert t-1}\]



2.4.2.2 Update Step

\[ \left\lbrace \begin{array}{llll} \mathbf{y}_t&=&\mathbf{A}_t\mathbf{X}_t+\mathbf{v}_t &with\ \mathbf{v}_t\sim MVN(\mathbf{0},\mathbf{R}_t)\\ \mathbf{X}_t&=&\boldsymbol{\Phi}_t \mathbf{X}_{t-1} + \mathbf{w}_t &with\ \mathbf{w}_t\sim MVN(\mathbf{0},\mathbf{Q}_t) \end{array} \right.\]


If observation \(\mathbf{y}_t\) at time \(t\) becomes available

\[ \begin{array}{rcl} \mathbf{X}_t\vert\mathbf{Y}_{t-1}&\sim& MVN(\mathbf{X}_{t\vert t}, \mathbf{P}_{t\vert t})\\\\ \mathbf{X}_{t\vert t}&=&\text{E}\left[\mathbf{X}_t\vert \mathbf{Y}_{t}\right]\\ &=&\text{E}\left[\mathbf{X}_t\vert \mathbf{Y}_{t-1}, \boldsymbol{y}_t\right]\\\\ \mathbf{P}_{t\vert t} &=& \text{Var}\left[\mathbf{X}_t\vert\mathbf{Y}_t\right]\\ &=&\text{Var}\left[\mathbf{X}_t\vert\mathbf{Y}_{t-1},\mathbf{y}_t\right] \end{array} \]

Exploit that \(\mathbf{X}_t\) and \(\mathbf{y}_t\) given \(\mathbf{Y}_{t-1}\) are multivariate normally distributed! \[\left.\begin{bmatrix} \mathbf{X}_t\\ \mathbf{y}_t \end{bmatrix} \right\vert \mathbf{Y}_{t-1} \sim MVN\left(\begin{bmatrix} \mathbf{X}_{t\vert t-1}\\ \mathbf{A}_{t}\mathbf{X}_{t\vert t-1} \end{bmatrix}, \begin{bmatrix} \mathbf{P}_{t\vert t-1}&\mathbf{P}_{t\vert t-1}\mathbf{A}_{t}^T\\ \mathbf{A}_{t} \mathbf{P}_{t\vert t-1}&\boldsymbol{\Sigma}_t \end{bmatrix}\right) \]

With

\[\begin{array}{lcl} \boldsymbol{\Sigma}_t=\text{Var}\left[\mathbf{y}_t\vert \mathbf{Y}_{t-1}\right]&=&\text{Var}\left[\mathbf{A}_t\mathbf{X}_t+\mathbf{v}_t\vert\mathbf{Y}_{t-1}\right]\\&=&\mathbf{A}_t\text{Var}\left[\mathbf{X}_t\vert \mathbf{Y}_{t-1}\right]\mathbf{A}_t^T+\text{Var}\left[\mathbf{v}_t\right]\\ &=&\mathbf{A}_t \mathbf{P}_{t\vert t-1} \mathbf{A}_t^T + \mathbf{R}_t \end{array}\]

It can be shown that \(\mathbf{X}_t\vert \mathbf{Y}_t\) is the Best Linear Unbiased Predictor BLUP by exploiting the conditional expectation for jointly multivariate normal data (See wikipedia ).

\[ \begin{array}{rcl} \text{E}\left[\mathbf{X}_t\vert\mathbf{Y}_{t-1},\mathbf{y}_t\right] &=&\text{E}\left[\mathbf{X}_t\vert \mathbf{Y}_{t-1}\right]+\text{Cov}\left[\mathbf{X}_t,\mathbf{y}_t\vert\mathbf{Y}_{t-1}\right]\text{Var}\left[\mathbf{y}_t\vert\mathbf{Y}_{t-1}\right]^{-1}\left(\mathbf{y}_t-\mathbf{A}_t\mathbf{X}_{t\vert t-1 }\right),\\ \mathbf{X}_{t\vert t}&=&\mathbf{X}_{t\vert t-1}+\mathbf{P}_{t\vert t-1} \mathbf{A}_t^T\boldsymbol{\Sigma}_t^{-1}\left(\mathbf{y}_t-\mathbf{A}_t\mathbf{X}_{t\vert t-1 }\right), \end{array} \]

and

\[ \begin{array}{rcl} \text{Var}\left[\mathbf{X}_t\vert\mathbf{Y}_{t-1},\mathbf{y}_t\right]&=&\text{Var}\left[\mathbf{X}_t\vert\mathbf{Y}_{t-1}\right]-\text{Cov}\left[\mathbf{X}_t,\mathbf{y}_t\vert\mathbf{Y}_{t-1}\right]\text{Var}\left[\mathbf{y}_t\vert\mathbf{Y}_{t-1}\right]^{-1}\text{Cov}\left[\mathbf{X}_t,\mathbf{y}_t\vert\mathbf{Y}_{t-1}\right]^T\\ \mathbf{P}_{t\vert t}&=&\mathbf{P}_{t\vert t-1}-\mathbf{P}_{t\vert t-1} \mathbf{A}_t^T \boldsymbol{\Sigma}_t^{-1} \mathbf{A}_t \mathbf{P}_{t\vert t-1}, \end{array} \]

Note, that prediction error / innovation: \[\boldsymbol{\epsilon}_t =\mathbf{y}_t-\mathbf{A}_t\mathbf{X}_{t\vert t-1}\]

  • \(\boldsymbol{\epsilon}_t\ \perp \!\!\! \perp \ \mathbf{y}_r\) for all \(r=1,\ldots,t-1\)
  • \[\boldsymbol{\epsilon}_t \sim \text{MVN}\left(\mathbf{0},\boldsymbol{\Sigma }_t\right)\]
  • innovations are key to use Kalman Filter for decomposition of the likelihood.


2.4.2.3 Summary Kalman Filter

If \(\mathbf{X}_{0\vert 0}\) and \(\mathbf{P}_{0\vert 0}\) are known, then the Kalman filter becomes:

for \(t=1,\ldots, n\)

  1. Prediction step

\[ \begin{array}{rcl} \mathbf{X}_{t\vert t-1}&=&\boldsymbol{\Phi}_t \mathbf{X}_{t-1\vert t-1} \\\\ \mathbf{P}_{t\vert t-1}&=&\boldsymbol{\Phi}_t \mathbf{P}_{t-1\vert t-1} \boldsymbol{\Phi}_t^T + \mathbf{Q}_t. \end{array} \]

  1. Update step \[ \begin{array}{rcl} \mathbf{X}_{t\vert t}&=&\mathbf{X}_{t\vert t-1}+ \mathbf{P}_{t\vert t-1} \mathbf{A}_t^T \boldsymbol{\Sigma}_t^{-1} (\mathbf{y}_t - \mathbf{A}_t \mathbf{X}_{t\vert t-1} )\\\\ \mathbf{P}_{t\vert t}&=&\mathbf{P}_{t\vert t-1}-\mathbf{P}_{t\vert t-1} \mathbf{A}_t^T \boldsymbol{\Sigma}_t^{-1} \mathbf{A}_t \mathbf{P}_{t\vert t-1}\\\\ \boldsymbol{\Sigma}_t&=&\mathbf{A}_t \mathbf{P}_{t\vert t-1} \mathbf{A}_t^T + \mathbf{R}_t. \end{array} \]


2.4.3 Under the hood of the Kalman Smoother

  • \(\text{E}\left[\mathbf{X}_t\vert \mathbf{Y}_N\right]\)

  • Start with the final quantities, \(\mathbf{X}_{n\vert n}\) and \(\mathbf{P}_{n\vert n}\) and proceeds backwards.

  • For \(t=n-1, \ldots, 0\), it consists of the following backward recursions

\[\begin{align} &\mathbf{X}_{t\vert n}=\mathbf{X}_{t\vert t}+\mathbf{J}_t(\mathbf{X}_{t+1\vert n}-\mathbf{X}_{t+1\vert t}) \label{SS_B1} \\ &\mathbf{P}_{t\vert n}=\mathbf{P}_{t\vert t}+\mathbf{J}_{t}(\mathbf{P}_{t+1\vert n}-\mathbf{P}_{t+1\vert t})\mathbf{J}_t^{T} \label{SS_B2}\\ &\mathbf{J}_t=\mathbf{P}_{t\vert t}\Phi_{t+1}^T\mathbf{P}_{t+1\vert t}^{-1}. \label{SS_B3} \end{align}\]



2.5 Parameter Estimation

2.5.1 Likelihood and the prediction error decomposition

Classical iid setting, \[\begin{equation*} \log L_{\mathbf{Y_N}}(\boldsymbol{\Psi})=\sum\limits_{t=1}^n \log p(\mathbf{y_t}), \end{equation*}\] Not possible for dependent observations. State-space model allows factorisation using conditional density functions \[\begin{equation*} \log L_{\mathbf{Y}_N}(\boldsymbol{\Psi})=\sum\limits_{t=1}^n\log p(\mathbf{y}_t\vert \mathbf{Y}_{t-1}) \end{equation*}\] \[\mathbf{y}_t\vert\mathbf{Y}_{t-1}\sim MVN(\mathbf{A}_t \mathbf{X}_{t\vert t-1},\boldsymbol{\Sigma}_t)\] \[\begin{equation*} \log L_{\mathbf{Y}_N}(\boldsymbol{\Psi})=-\frac{pn}{2}\log 2 \pi - \frac{1}{2} \sum\limits_{t=1}^n \log \vert \boldsymbol{\Sigma}_t\vert -\frac{1}{2} \sum\limits_{t=1}^n \boldsymbol{\epsilon}_t^T \boldsymbol{\Sigma}_t^{-1} \boldsymbol{\epsilon}_t, \end{equation*}\]

Which can be efficiently calculated by the Kalman filter and can thus be used to estimate the model parameters.



2.5.2 Maximum Likelihood

  • Numerical optimisation of likelihood (often troublesome):
    1. Choose initial values for the parameters
    2. Run Kalman Filter and calculate likelihood
    3. Run one iteration of Newton-Raphson with the -2 log likelihood as criterion
    4. Repeat steps 2-3 with the current Newton-Raphson parameter estimates until convergence.


  • For time invariant system: EM algorithm (often slow)


2.5.3 EM algorithm

2.5.3.1 Complete likelihood of \(\mathbf{X}\) and \(\mathbf{Y}\)

\[\begin{multline*} \log L_{\mathbf{Y},\mathbf{X}}(\boldsymbol{\Psi}) \sim -\frac{1}{2} \log|\boldsymbol{\Sigma}_{0}| -\frac{1}{2} (\mathbf{X}_0-\boldsymbol{\mu}_0)^T \boldsymbol{\Sigma}_{0}^{-1} (\mathbf{X}_0-\boldsymbol{\mu}_0)\\ -\frac{n}{2} \log |\mathbf{Q}|-\frac{1}{2}\sum_{t=1}^n (\mathbf{X}_t-\boldsymbol{\Phi X}_{t-1})^T \mathbf{Q}^{-1}(\mathbf{X}_t-\boldsymbol{\Phi X}_{t-1})\\ -\frac{n}{2} \log|\mathbf{R}| -\frac{1}{2} \sum_{t=1}^n (\mathbf{Y}_t-\mathbf{A}_t\mathbf{X}_t)^T\mathbf{R}^{-1}(\mathbf{Y}_t-\mathbf{A}_t\mathbf{X}_t), \end{multline*}\]

  • Cannot be calculated: state is unobservable


2.5.3.2 EM algorithm

  1. E-step: \(Q(\boldsymbol{\Psi}, \boldsymbol{\Psi}^k) = \text{E}\left[-2\log L_{\mathbf{Y},\mathbf{X}}(\boldsymbol{\Psi}) \vert \mathbf{Y}_N, \boldsymbol{\Psi}^k\right]\)

  2. M-step: Minimise \(Q(\boldsymbol{\Psi}, \boldsymbol{\Psi}^k)\) to obtain the update of the parameter set \(\boldsymbol{\Psi}^{k+1}\).

  3. Repeate 1-2 until convergence

Before we derive the algorithm we first introduce the lag one covariance estimators

\[\mathbf{P}_{t,t-1\vert r}=\text{cov}\left[\mathbf{X}_t,\mathbf{X}_{t-1}\vert \mathbf{Y}_r\right]\].

  • Filtered values \[\begin{equation} \mathbf{P}_{t,t-1\vert t}=(\mathbf{I}-\mathbf{P}_{t\vert t-1}\mathbf{A}_t^T\boldsymbol{\Sigma}_t^{-1}\mathbf{A}_t)\boldsymbol{\Phi}_t\mathbf{P}_{t-1\vert t-1}, \label{SS_F6} \end{equation}\]

  • smoothed values \[\begin{equation} \mathbf{P}_{t,t-1\vert n}=\mathbf{P}_{t,t-1\vert t}+(\mathbf{P}_{t\vert n}-\mathbf{P}_{t\vert t})\mathbf{P}_{t\vert t}^{-1}\mathbf{P}_{t,t-1\vert t}. \label{SS_B4} \end{equation}\]



2.5.3.3 EM-algorithm: E-step

\[\begin{multline*} Q(\boldsymbol{\Psi}, \boldsymbol{\Psi}^k) \sim \text{E}\left[ \log|\boldsymbol{\Sigma}_{0}| + (\mathbf{X}_0-\boldsymbol{\mu}_{0})^T \boldsymbol{\Sigma}_{0}^{-1} (\mathbf{X}_0-\boldsymbol{\mu}_{0})\vert \mathbf{Y}_N, \boldsymbol{\Psi}^k\right]\\\\ +\text{E}\left[n \log |\mathbf{Q}|+\sum_{t=1}^n (\mathbf{X}_t-\boldsymbol{\Phi X}_{t-1})^T \mathbf{Q}^{-1}(\mathbf{X}_t-\boldsymbol{\Phi X}_{t-1})\vert \mathbf{Y}_N, \boldsymbol{\Psi}^k\right]\\\\ +\text{E}\left[n \log|\mathbf{R}| + \sum_{t=1}^n (\mathbf{Y}_t-\mathbf{A}_t\mathbf{X}_t)^T\mathbf{R}^{-1}(\mathbf{Y}_t-\mathbf{A}_t\mathbf{X}_t)\vert \mathbf{Y}_N, \boldsymbol{\Psi}^k\right] \end{multline*}\]

\[ \quad \]

\[\begin{multline*} Q(\boldsymbol{\Psi}, \boldsymbol{\Psi}^k)\sim \log|\boldsymbol{\Sigma}_{0}| + \text{tr}\left[ \boldsymbol{\Sigma}_{0}^{-1} \left \lbrace \mathbf{P}_{0\vert n} + (\mathbf{X}_{0\vert n}- \boldsymbol{\mu}_{0})(\mathbf{X}_{0\vert n}- \boldsymbol{\mu}_{0})^T\right\rbrace\right]\\\\ +n \log |\mathbf{Q}| + \text{tr}\left[ \mathbf{Q^{-1}}\left\lbrace\mathbf{S}_{11} - \mathbf{S}_{10}\boldsymbol{\Phi}^T-\boldsymbol{\Phi} \mathbf{S}_{10}^T+\boldsymbol{\Phi} \mathbf{S}_{00} \boldsymbol{\Phi}^T\right\rbrace\right]\\\\+n\log |\mathbf{R}| + \text{tr}\left[\mathbf{R^{-1}}\sum\limits_{t=1}^n \left\lbrace(\mathbf{y}_t-\mathbf{A}_t \mathbf{X}_{t\vert n})(\mathbf{y}_t-\mathbf{A}_t\mathbf{X}_{t\vert n})^T+\mathbf{A}_t\mathbf{P}_{t\vert n} \mathbf{A}_t^T\right\rbrace\right], \end{multline*}\]

with \[\begin{align*} \mathbf{S}_{11}&=\sum\limits_{t=1}^n (\mathbf{X}_{t\vert n} \mathbf{X}_{t\vert n}^T + \mathbf{P}_{t\vert n})\\ \mathbf{S}_{10}&=\sum\limits_{t=1}^n (\mathbf{X}_{t\vert n}\mathbf{X}_{t-1\vert n}^T + \mathbf{P}_{t,t-1\vert n})\\ \mathbf{S}_{00}&=\sum\limits_{t=1}^n (\mathbf{X}_{t-1\vert n} \mathbf{X}_{t-1\vert n}^T + \mathbf{P}_{t-1\vert n})\\ \end{align*}\]



2.5.3.4 EM algorithm: M-step minimise \(Q(\boldsymbol{\Psi}, \boldsymbol{\Psi}^k)\)

\[\begin{align*} \boldsymbol{\Phi}^{k+1}=&\mathbf{S}_{10}\mathbf{S}_{00}^{-1}\\\\ \mathbf{Q}^{k+1}=& n^{-1}(\mathbf{S}_{11}-\mathbf{S}_{10}\mathbf{S}_{00}^{-1}\mathbf{S}_{10}^T),\\\\ \mathbf{R}^{k+1}=&n^{-1}\sum\limits_{t=1}^n \left[(\mathbf{y}_t-\mathbf{A}_t \mathbf{X}_{t\vert n})(\mathbf{y}_t-\mathbf{A}_t\mathbf{X}_{t\vert n})^T+\mathbf{A}_t\mathbf{P}_{t\vert n} \mathbf{A}_t^T\right]\\\\ \boldsymbol{\mu}_0^{k+1}=& \mathbf{X}_{0\vert n}\\\\ \boldsymbol{\Sigma}_0^{k+1}=&\mathbf{P}_{0\vert n} \end{align*}\]



3 Extensions

  1. Link between State Space models and cubic smoothing splines
  2. More complex temporal structures
  3. Time varying coefficient model
  4. Missing data
  5. Kalman filter for Generalized Least Squares (GLS)
  6. Advanced topics


3.2 More complex temporal structures

  • Extend state vector with states at previous lags, i.e. \(t-1, \ldots, t-p\)

\[\mathbf{X}_t=[X_{1t} \ldots X_{mt}, \ldots, X_{1t-p}\ldots X_{mt-p}]^T\]

  • By embedding the AR(p) State in the measurement equation, the correlation structure again becomes much more flexible.


3.3 Time Varying Coefficient Model

Regression models with time varying model parameters can also be easily provided by extending the state vector, i.e. 

\[\mathbf{X}_t=[X_{1t} \ldots X_{mt},\mathbf{\beta}_t]^T\]

3.3.1 Example Hedge ratio crypto-currencies

Click to see code for plot

coinData <- coinData %>% 
  mutate(
    year = format(date,"%Y") %>% as.factor,
    colors = as.double(date)-mean(as.double(date))
    ) 

plotHedge <- coinData %>%
  ggplot(aes(eth_prices_usd, ltc_prices_usd, color=year)) +
  geom_point(size = .7, shape = 1) 

plotHedge

Note, that - this example is for illustration purposes only. - It show-cases time-varying regression with state space models - The data are modelled on the original price scale so the errors will probably be heteroscedastic.

3.3.2 Define state space model

\[y_t = \beta_{0t} + \beta_{1t} z_t + v_t\]

with

  • \(y_t\): ltc price and
  • \(z_t\): eth price

State space model: \[ \left\lbrace \begin{array}{llll} y_t&=&\mathbf{A}_t\mathbf{X}_t+v_t &with\ v_t\sim N(0,\sigma_v^2)\\ \mathbf{X}_t&=&\boldsymbol{\Phi} \mathbf{X}_{t-1} + \mathbf{w}_t &with\ \mathbf{w}_t\sim MVN(\mathbf{0},\mathbf{Q}) \end{array} \right.\]

with

  • \(\mathbf{A}_t = \begin{bmatrix} 1 & z_t\end{bmatrix}\)
  • \(\mathbf{X}_t= \begin{bmatrix} \beta_{0t}\\ \beta_{1t} \end{bmatrix}\)

3.3.3 R implementation

y <- coinData %>% 
  pull("ltc_prices_usd") %>% 
  as.matrix(ncol=1)

z <- coinData %>% 
  pull("eth_prices_usd") 

num <- nrow(y)

# Measurement matrix A = 
A <- array(0, dim=c(1,2,num))  
A[,1,] <- 1
A[,2,] <- z

# initial values
mu0 <- matrix(c(0,1),2,1)
Sigma0 <- diag(c(.1,.1),2)
Phi <- diag(1,2)
cQ <- diag(c(.1,.1),2)
cR <- diag(c(.1),1)

em <- EM1(num, y, A, mu0, Sigma0, Phi, cQ, cR, 100, .001)
ks <- Ksmooth1(num, y, A, em$mu0, em$Sigma0, em$Phi, 0, 0, chol(em$Q), chol(em$R), 0)
Click to see code for plot

plotBetasVarying <- data.frame(
    time=coinData$date, 
    beta=c(ks$xf[1,1,], ks$xf[2,1,]),
    state=c(rep("intercept",ndays), rep("slope",ndays))
  ) %>% 
  ggplot(aes(time, beta)) +
  geom_line() + 
  facet_wrap(~state, nrow=2, scales="free_y") +
  theme_minimal()

plotBetasVarying

Click to see code for plot

coinData <- coinData %>% 
  mutate(
    hedgeInt = ks$xf[1,1,],
    hedgeSlp = ks$xf[2,1,]  
    )

plotHedge <- list()
i<- 0
for (t in pretty(coinData$date, 36))
#for (t in c(
#  seq(
#    min(coinData$date),
#    as.Date("2019-12-31"),
#    7),
#  seq(
#    as.Date("2021-01-01"),
#    max(coinData$date),
#    7)
#  )
#  )
{
  i <- i+1
  plotHedge[[i]] <- coinData %>%
    ggplot(aes(eth_prices_usd, ltc_prices_usd, color=year)) +
    geom_point(size = .7, shape = 1) +
    geom_point(aes(x=eth_prices_usd, y = ltc_prices_usd, color = year), data = coinData %>% filter(date == t), shape = 17, size = 4) +
    geom_abline(aes(intercept=hedgeInt, slope=hedgeSlp, color = year), data = coinData %>% filter(date == t))
}

Animation includes regression line for first day of each month.



3.4 Missing Data

3.4.1 Data

  • Data from Jones (1984)

  • Measurements made for 91 days on three variables:

    • \(y_1\): log(white blood count) [WBC]
    • \(y_2\): log(platelet) [PLT]
    • \(y_3\): hematocrit [HCT]
Click to see code for plot

bloodTidy <- blood %>% 
  as.data.frame %>%
  mutate(t=1:nrow(blood)) %>% 
  gather(variable, concentration, -t)
plotBlood <- bloodTidy %>% 
  ggplot(aes(t,concentration)) +
  geom_point() +
  facet_wrap(~variable,nrow=3,scales="free_y") +
  theme_minimal()

plotBlood
## Warning: Removed 111 rows containing missing values (geom_point).

  • Some data are missing!

3.4.2 Solution

  1. Replace missing data with 0 and remove the correlations use

\[ \mathbf{y}_t=\left[\mathbf{y}_t^{(obs)} 0\right]^T \]

\[\mathbf{R}_t=\begin{bmatrix}\mathbf{R}_{11t}& \mathbf{0}\\\mathbf{0}&\mathbf{R}_{22t}\end{bmatrix}\]

\[\mathbf{A}_t=\begin{bmatrix}\mathbf{A}^{(obs)}\\\mathbf{0}\end{bmatrix}\]

  1. Then you can apply the existing recursions for the Kalman Filter and Smoother

  2. The EM algorithm is designed to handle missing data.

3.4.3 Example

  • Define state space model and estimation

\[ \left\lbrace \begin{array}{llll} \mathbf{y}_t&=&\mathbf{A}_t\mathbf{X}_t+ \mathbf{v}_t &with\ \mathbf{v}_t\sim N(0,\mathbf{R}_t)\\ \mathbf{X}_t&=&\boldsymbol{\Phi} \mathbf{X}_{t-1} + \mathbf{w}_t &with\ \mathbf{w}_t\sim MVN(\mathbf{0},\mathbf{Q}) \end{array} \right.\]

with \(\mathbf{A}=\mathbf{I}\)

  1. Replace missing values by 0
y <- blood %>% as.matrix
y[is.na(y)] = 0
num <- nrow(y)
  1. Construct time variant matrix A.
A  <- array(0, dim=c(3,3,num))
for(k in 1:num) { if (y[k,1] > 0) A[,,k] <- diag(1,3) } # 
  1. Initalisation and estimation. Note, that we do not have to adjust covariance matrix \(\mathbf{R}\) to account for missingness because all observations are measured or missing at a particular timestep.
#Initial values
mu0 <- matrix(0, 3, 1)
Sigma0 <- diag(c(.1, .1, 1), 3)
Phi <- diag(1, 3)
cQ <- diag(c(.1, .1, 1), 3) 
cR <- diag(c(.1, .1, 1), 3)

em <- EM1(num, y, A, mu0, Sigma0, Phi, cQ, cR, 100, .001)
ks <- Ksmooth1(num, y, A, em$mu0, em$Sigma0, em$Phi, 0, 0, chol(em$Q), chol(em$R), 0)
Click to see code for plot

bloodTidy <- bloodTidy %>% 
  mutate(
    xs=ks$xs[,1,] %>% t %>% c,
    xsll = (ks$xs[,1,] - 2 * sapply(1:num,function(i) ks$Ps[,,i] %>% diag %>% sqrt)) %>% t(.) %>% c,
    xsul = (ks$xs[,1,] + 2 * sapply(1:num,function(i) ks$Pf[,,i] %>% diag %>% sqrt)) %>% t(.) %>% c
    )

plotBloodResults <- plotBlood +
  geom_ribbon(
    aes(ymin = xsll,ymax = xsul), 
    alpha = 0.45, 
    data = bloodTidy) +
  geom_line(aes(x=t,y=xs),data=bloodTidy) +
  xlab("time (days)") +
  ggtitle("Kalman Smoother")

plotBloodResults
## Warning: Removed 111 rows containing missing values (geom_point).



3.5 Kalman Filter for Generalized Least Squares (GLS)

\[ \left\lbrace \begin{array}{llll} \mathbf{y}_t &=& \mathbf{A}_t \mathbf{X}_t + \mathbf{Z}_t\boldsymbol{\beta}+ \mathbf{v}_t\\ \mathbf{X}_t &=&\boldsymbol{\Phi}_t \mathbf{X}_{t-1} + \mathbf{w}_t\\ \end{array} \right. \]

The state-space model can be further reformulated as a regression model \[ \mathbf{y}_t=\mathbf{Z}_t\boldsymbol{\beta}+ \mathbf{u}_t, \text{ with } \mathbf{u}_t=\mathbf{A}_t \mathbf{X}_t + \mathbf{v}_t \]

\[\begin{equation} \mathbf{Y}_N=\mathbf{Z}_N \boldsymbol{\beta} + \mathbf{U}_N, \label{measurement.reg.gls} \end{equation}\] with \(\mathbf{U}_N \sim MVN(\mathbf{0},\mathbf{V})\). \[\begin{equation} \hat{\boldsymbol{\beta}}_{GLS}=(\mathbf{Z}_N^T\mathbf{V}^{-1}\mathbf{Z}_N)^{-1}\mathbf{Z}_N^T \mathbf{V}^{-1} \mathbf{Y}_N. \end{equation}\]

From GLS theory, \[\begin{equation} \text{Var}\left[\hat{\boldsymbol{\beta}}_{GLS}\right]=(\mathbf{Z}_N^T\mathbf{V}^{-1}\mathbf{Z}_N)^{-1}. \end{equation}\]

Kalman Filter and Smoother

  1. If \(\beta\) is known, the kalman filter and smoother recursions still hold when we account for \(\mathbf{Z}_t\boldsymbol{\beta}\) in the update step. \[ \mathbf{X}_{t\vert t}=\mathbf{X}_{t\vert t-1}+ \mathbf{P}_{t\vert t-1} \mathbf{A}_t^T \boldsymbol{\Sigma}_t^{-1} (\mathbf{y}_t - \mathbf{A}_t \mathbf{X}_{t\vert t-1} - \mathbf{Z}_t\boldsymbol{\beta}) \]

  2. Harvey (1989) showed that for a given \(\boldsymbol{\Psi}\)

  • applying the same Kalman filter (without recalculating \(\mathbf{P}_{t\vert t-1}\), \(\mathbf{P}_{t\vert t}\) and \(\boldsymbol{\Sigma}_t\)) to \(\mathbf{Y}_t\) and each of the columns of \(\mathbf{Z}_t\)
  • results in
    • A \(p\times1\) vector of innovations, \(\mathbf{Y}_t^*\) on \(\mathbf{Y}_t\) and
    • a \(p\times m\) matrix of innovations, \(\mathbf{Z_t^*}\) on \((\mathbf{Z_{1t}},\ldots, \mathbf{Z_{mt}})\) are produced.
  • so that the GLS estimator of \(\boldsymbol{\beta}\) can be obtained

\[ \boldsymbol{\hat \beta_{GLS}}=\left[\sum\limits_{t=1}^N \mathbf{Z_t^{*T}\Sigma_t^{-1}Z_t^{*}}\right]^{-1}\sum\limits_{t=1}^N \mathbf{Z_t^{*T}\Sigma_t^{-1}y_t^{*}}. \]

  • the innovations \(\boldsymbol{\epsilon}_t\) are then equivalent to \(\boldsymbol{\epsilon}_t=\mathbf{y}_t^* -\mathbf{z}_t^* \hat{ \boldsymbol{\beta}}_{GLS}\) \[\begin{equation} \log L_{\mathbf{Y}_N}(\boldsymbol{\Psi})=-\frac{pn}{2}\log 2 \pi - \frac{1}{2} \sum\limits_{t=1}^n \log \vert \boldsymbol{\Sigma}_t\vert -\frac{1}{2} \sum\limits_{t=1}^n \boldsymbol{\epsilon}_t^T \boldsymbol{\Sigma}_t^{-1} \boldsymbol{\epsilon}_t. \end{equation}\]
  1. Hence, we can thus update the M-step of the EM algorithm with the GLS estimator for \(\boldsymbol{\beta}\).


3.6 More Advanced Topics

  • State Space models can also be used to model processes with stochastic volatility, e.g. when modeling returns.

  • When the states are discrete, the State Space Model becomes a Hidden Markov Model

  • Non-stationary time series. Specific formulations of State-Space Models for instance allow to model seasonal variation and trends.

  • Non-normality. Note, that the errors of the log-normal model are non-guassian. But, they are symmetric with broader tails. The estimators that are used here are assymptotically normally distributed. In relatively short time series we can

    • resort to bootstrapping the innovations to provide better inference or
    • we can explicitely model the errors with another distribution


4 Literature


LS0tCnRpdGxlOiAiQW4gaW50cm9kdWN0aW9uIHRvIHN0YXRlLXNwYWNlIG1vZGVscyBmb3IgdGltZS1zZXJpZXMgYW5hbHlzaXMgd2l0aCBleGFtcGxlcyBvbiB0aHJlZSBtYWpvciBjcnlwdG8tY3VycmVuY2llcyIKYXV0aG9yOiAiTGlldmVuIENsZW1lbnQiCmRhdGU6ICdgciBmb3JtYXQoU3lzLkRhdGUoKSwgIiVCICVkLCAlWSIpYCcKb3V0cHV0OgogICAgaHRtbF9kb2N1bWVudDoKICAgICAgY29kZV9kb3dubG9hZDogdHJ1ZQogICAgICB0aGVtZTogY29zbW8KICAgICAgdG9jOiB0cnVlCiAgICAgIHRvY19mbG9hdDogdHJ1ZQogICAgICBoaWdobGlnaHQ6IHRhbmdvCiAgICAgIG51bWJlcl9zZWN0aW9uczogdHJ1ZQotLS0KClRoaXMgd29yayBpcyBsaWNlbnNlZCB1bmRlciB0aGUgW0NyZWF0aXZlIENvbW1vbnMgQXR0cmlidXRpb24tTm9uQ29tbWVyY2lhbC1TaGFyZUFsaWtlIDQuMCBJbnRlcm5hdGlvbmFsIChDQyBCWS1OQy1TQSA0LjApXShodHRwczovL2NyZWF0aXZlY29tbW9ucy5vcmcvbGljZW5zZXMvYnktbmMtc2EvNC4wKQoKIyBJbnRyb2R1Y3Rpb24KCi0gQXNzb2NpYXRlIFByb2Zlc3NvciBvZiBTdGF0aXN0aWNhbCBHZW5vbWljcyBpbiBHaGVudCBVbml2ZXJzaXR5CgotIEFkdm9jYXRlIG9mIG9wZW4gcmVzZWFyY2ggYW5kIG9wZW4gdGVhY2hpbmc6CkFsbCBvdXIgdGVhY2hpbmcgbWF0ZXJpYWxzLCBtZXRob2RzLCB0b29scyBhbmQgYW5hbHlzZXMgZm9yIHBhcGVycyBhcmUgb3BlbiBzb3VyY2UgYW5kIGZyZWVseSBhdmFpbGFibGUgb24gZ2l0aHViIAoKICAgIC0gaHR0cHM6Ly9zdGF0b21pY3MuZ2l0aHViLmlvLwogICAgLSBodHRwczovL3N0YXRvbWljcy5naXRodWIuaW8vcGFnZXMvdGVhY2hpbmcuaHRtbAogICAgLSBodHRwczovL2dpdGh1Yi5jb20vc3RhdG9taWNzCgotIE1ldGhvZCBkZXZlbG9wbWVudCBmb3IgZGlmZmVyZW50aWFsIGFuYWx5c2lzIG9mIGdlbmUgYW5kIHByb3RlaW4gZXhwcmVzc2lvbgoKLSBCYWNrZ3JvdW5kIGluIGVudmlyb25tZW50YWwgZW5naW5lZXJpbmcgd2l0aCBhIFBoRCBpbiBlbnZpcm9ubWVudGFsIHN0YXRpc3RpY3Mgd2l0aCBhIGZvY3VzIG9uIHRpbWVzZXJpZXMgYW5hbHlzaXMgb2Ygcml2ZXIgd2F0ZXIgcXVhbGl0eSBkYXRhIAoKLSBJbnRybyB0byAKCiAgICAtIFN0YXRlIFNwYWNlIE1vZGVscyAKICAgIC0gS2FsbWFuIGZpbHRlciAgJiBzbW9vdGhlcgogICAgLSBBcHBsaWVkIG9uIGNyeXB0b2N1cnJlbmN5IGRhdGEKICAgIC0gQnJpZWYgaW50cm8gYW5kIGlsbHVzdHJhdGlvbiBvbmx5LCBieSBubyBtZWFucyB0aGUgY3VycmVudCBzdGF0ZSBvZiB0aGUgYXJ0IAogICAgLSBNb3JlIGFkdmFuY2VkIFN0YXRlIFNwYWNlIE1vZGVscyByZXF1aXJlZCBmb3IgaGVkZ2luZyBvciBwcmVkaWN0aW5nIGNyeXB0b2N1cnJlbmN5IGRhdGEgaW4gcHJhY3RpY2UuCgoKPGJyIC8+IAoKCiMjIFVzZWZ1bCBmdW5jdGlvbnMgYW5kIGxvYWQgbGlicmFyaWVzCgo8ZGV0YWlscz48c3VtbWFyeT5DbGljayB0byBzZWUgbG9hZGVkIGxpYnJhcmllcyA8L3N1bW1hcnk+PHA+CmBgYHtyfQpsaWJyYXJ5KGFzdHNhKQpsaWJyYXJ5KHRpZHl2ZXJzZSkKbGlicmFyeShHR2FsbHkpCmxpYnJhcnkoZ3JpZEV4dHJhKQpsaWJyYXJ5KGdpZnNraSkKYGBgCjwvcD48L2RldGFpbHM+Cgo8ZGV0YWlscz48c3VtbWFyeT5DbGljayB0byBzZWUgZnVuY3Rpb24gdG8gcmV0cmVpdmUgY29pbiBkYXRhIGZyb20gY29pbmdlY2tvIDwvc3VtbWFyeT48cD4KCmBgYHtyfQojIGNvaW5TeW1ib2w6IHN5bWJvbCBvZiBjcnlwdG9jdXJyZW5jeSB0b2tlbiBvciBjb2luCiMgZGF5czogcGFzdCBudW1iZXIgb2YgZGF5cyB5b3Ugd2FudCB0byByZXRyaWV2ZSBkYXRhIGZvci4gIm1heCIgcmV0dXJucyBhbGwgYXZhaWxhYmxlIGRhdGEgZm9yIGEgdG9rZW4gb3IgY29pbi4KIyBjdXJyZW5jeTogZmlhdCBjdXJyZW5jeSB5b3Ugd2FudCB0byB1c2UgZm9yIHByaWNlICgidXNkIiwiZXVyIiwianB5IiwgLi4uKQojIGludGVydmFsOiB0aW1lIGludGVydmFsIG9uIHdoaWNoIHlvdSB3YW50IHRvIHJldHJlaXZlIGRhdGEuCgpyZXRyZWl2ZUNvaW5EYXRhIDwtIGZ1bmN0aW9uKAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICBjb2luU3ltYm9sPSJidGMiLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZGF5cyA9ICJtYXgiLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgY3VycmVuY3kgPSAidXNkIiwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGludGVydmFsID0gImRhaWx5IikKeyAgIAogICNSZXRyZWl2ZSBjb2lubGlzdCB0byBsb29rdXAgY29pbiBpZAogIGNvaW5saXN0IDwtIGh0dHI6OkdFVCgiaHR0cHM6Ly9hcGkuY29pbmdlY2tvLmNvbS9hcGkvdjMvY29pbnMvbGlzdCIpJGNvbnRlbnQgJT4lIAogICAgcmF3VG9DaGFyICU+JSAKICAgIGpzb25saXRlOjpmcm9tSlNPTiguKQogIAogIGNvaW5JZCA8LSBjb2lubGlzdCAlPiUgZmlsdGVyKHN5bWJvbCA9PSBjb2luU3ltYm9sKSAlPiUgcHVsbChpZCkKICAKICAjcmV0cmVpdmUgY29pbiBkYXRhIHZpYSBBUEkKICBjb2luRGF0YSA8LSBodHRyOjpHRVQoCiAgICBwYXN0ZTAoCiAgICAgICJodHRwczovL2FwaS5jb2luZ2Vja28uY29tL2FwaS92My9jb2lucy8iLAogICAgICBjb2luSWQsCiAgICAgICIvbWFya2V0X2NoYXJ0P3ZzX2N1cnJlbmN5PSIsCiAgICAgIGN1cnJlbmN5LAogICAgICAiJmRheXM9IiwKICAgICAgZGF5cywKICAgICAgIiZpbnRlcnZhbD0iLAogICAgICBpbnRlcnZhbCkKICAgICkkY29udGVudCAlPiUgCiAgICByYXdUb0NoYXIgJT4lIAogICAganNvbmxpdGU6OmZyb21KU09OKC4pCiAgCiAgI3N0b3JlIGNvaW4gZGF0YSBhcyB0aWJsZSwgaW5jbHVkZSBjb2luIHN5bWJvbCBpbiBwcmljZSBuYW1lIGFuZCBjb3ZlcnQgdW5peCB0aW1lIHN0cmluZyB0byB0aW1lc3RhbXAgb2JqZWN0CiAgY29pbkRhdGEgPC0gbGFwcGx5KG5hbWVzKGNvaW5EYXRhKSwgZnVuY3Rpb24odmFyLGNvaW5EYXRhKSAKICAgICAgICAgIHsKICAgICAgICAgICAgZGF0YSA8LSBjb2luRGF0YVtbdmFyXV0gJT4lIGFzX3RpYmJsZSgpCiAgICAgICAgICAgIG5hbWVzKGRhdGEpIDwtIGMoCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInRpbWVzdGFtcCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcGFzdGUwKGNvaW5TeW1ib2wsIl8iLHZhcikKICAgICAgICAgICAgICAgICAgICAgICAgICAgICApCiAgICAgICAgICAgIGRhdGEgPC0gZGF0YSAlPiUgCiAgICAgICAgICAgICAgbXV0YXRlKAogICAgICAgICAgICAgICAgdGltZXN0YW1wID0gIGFzLlBPU0lYY3QoCiAgICAgICAgICAgICAgICAgIHRpbWVzdGFtcC8xMDAwLCAKICAgICAgICAgICAgICAgICAgb3JpZ2luID0gIjE5NzAtMDEtMDEiLHR6PSJHTVQiCiAgICAgICAgICAgICAgICAgICkKICAgICAgICAgICAgICAgICkgCiAgICAgICAgICAgIHJldHVybihkYXRhKQogICAgICAgICAgfSwgY29pbkRhdGEgPSBjb2luRGF0YQogICAgICAgICkgICU+JSByZWR1Y2UobGVmdF9qb2luLCBieSA9ICJ0aW1lc3RhbXAiKQogICNyZW5hbWUgcHJpY2VzIGNvbHVtbiB0byBpbmNsdWRlIGN1cnJlbmN5CiAgcHJpY2VzSWQgPC0gZ3JlcChuYW1lcyhjb2luRGF0YSksIHBhdHRlcm49InByaWNlcyIpCiAgbmFtZXMoY29pbkRhdGEpW3ByaWNlc0lkXSA8LSBwYXN0ZSgKICAgIG5hbWVzKGNvaW5EYXRhKVtwcmljZXNJZF0sCiAgICBjdXJyZW5jeSwKICAgIHNlcD0iXyIpCiAgCiAgI2dldCByaWQgb2YgbGFzdCB0aW1lIGJlY2F1c2UgY29pbiBnZWNrbyByZXR1cm5zIGFsbCBkYWlseSBkYXRhIGF0IDAwOjAwIEdNVCBhbmQgaW5jbHVkZXMgdGhlIGxhc3QgZW50cnkgb2YgdGhlIGN1cnJlbnQgZGF5LiAKICBpZihkYXlzPT0ibWF4IikgCiAgICAgIHJldHVybihjb2luRGF0YSkgZWxzZSAKICAgICAgICByZXR1cm4gKGNvaW5EYXRhWzE6ZGF5cyxdKQp9CmBgYAo8L3A+PC9kZXRhaWxzPgoKPGRldGFpbHM+PHN1bW1hcnk+Q2xpY2sgdG8gc2VlIGZ1bmN0aW9uIGZvciBhY2YgcGxvdHMgPC9zdW1tYXJ5PjxwPgpGdW5jdGlvbiB0byBtYWtlIEFDRiBhbmQgUEFDRiBwbG90cyBjb3BpZWQgZnJvbSBodHRwczovL2dpdGh1Yi5jb20vbWRlbGhleS9tZHV0aWxzCgpgYGB7cn0KZ2dhY2YgPC0gZnVuY3Rpb24oeCwgdHlwZSA9IGMoImNvcnJlbGF0aW9uIiwgImNvdmFyaWFuY2UiLCAicGFydGlhbCIpLCBjaSA9IDAuOTUsIC4uLikgewogICAgdHlwZSA8LSBtYXRjaC5hcmcodHlwZSkKICAgIHguYWNmIDwtIGFjZih4LCB0eXBlID0gdHlwZSwgcGxvdCA9IEZBTFNFLCAuLi4pCiAgICB4LmRmIDwtIHdpdGgoeC5hY2YsIGRhdGEuZnJhbWUobGFnLCBhY2YpKQogICAgeC5jaSA8LSBxbm9ybSgoMSArIGNpKS8yKSAvIHNxcnQobGVuZ3RoKHgpKQogICAgcHJpbnQoeC5jaSkKICAgIGdncGxvdChkYXRhID0geC5kZiwgYWVzKHggPSBsYWcsIHkgPSBhY2YpKSArCiAgICAgICAgZ2VvbV9obGluZShhZXMoeWludGVyY2VwdCA9IDApKSArCiAgICAgICAgZ2VvbV9zZWdtZW50KGFlcyh4ZW5kID0gbGFnLCB5ZW5kID0gMCkpICsKICAgICAgICBnZW9tX2hsaW5lKHlpbnRlcmNlcHQgPSBjKHguY2ksIC14LmNpKSwgY29sb3IgPSAiYmx1ZSIsIGxpbmV0eXBlID0gImRhc2hlZCIpICsKICAgICAgICBzY2FsZV95X2NvbnRpbnVvdXMoYnJlYWtzID0gc2VxKDAuMCwgMS4wLCAwLjIpKQp9CmBgYAo8L3A+PC9kZXRhaWxzPgoKCiAgICAKPGJyIC8+IAoKLS0tCgojIyBEYXRhIGV4cGxvcmF0aW9uCgpTZWxlY3QgY29pbnMgYml0Y29pbiAoYnRjKSwgZXRoZXJpdW0gKGV0aCkgYW5kIGxpdGVjb2luIChsdGMpIAoKYGBge3IgbWVzc2FnZT1GQUxTRX0KY29pblN5bWJvbHMgPC0gYygiYnRjIiwgImV0aCIsICJsdGMiKQpgYGAKCi0gV2Ugd2FudCB0byByZXRyZWl2ZSBkYXRhIGZyb20gSmFudWFyeSBGaXJzdCAyMDE5IHVwIHRvIHRvZGF5LgotIFdlIGhhdmUgdG8gY2FsY3VsYXRlIHRoZSBudW1iZXIgb2YgZGF5cyBmb3Igd2hpY2ggd2Ugd2FudCB0byByZXRyZWl2ZSBkYXRhIGZvciB0aGUgY29pbmdlY2tvIEFQSQoKYGBge3J9CnRvZGF5c0RhdGUgPC0gYXMuUE9TSVhsdChTeXMuRGF0ZSgpLHR6PSJHTVQiKQpzdGFydERhdGUgPC0gYXMuUE9TSVhsdCgiMjAxOS0wMS0wMSIsdHo9IkdNVCIpCm5kYXlzIDwtIGFzLmRvdWJsZSh0b2RheXNEYXRlIC0gc3RhcnREYXRlKSArIDEKYGBgCgpSZXRyZWl2ZSBkYXRhIGZyb20gY29pbmdlY2tvLiBXZSB3aWxsIHVzZSB0aGUgZGVmYXVsdCBjdXJyZW5jeSAoVVNEKS4KCmBgYHtyfQpjb2luRGF0YSA8LSAgY29pblN5bWJvbHMgJT4lCiAgICAgIG1hcChyZXRyZWl2ZUNvaW5EYXRhLGRheXM9bmRheXMpICU+JQogICAgICByZWR1Y2UobGVmdF9qb2luLCBieSA9ICJ0aW1lc3RhbXAiKSAlPiUKICAgICAgYXJyYW5nZSh0aW1lc3RhbXApICU+JSAKICAgICAgbXV0YXRlKGRhdGU9YXMuRGF0ZSh0aW1lc3RhbXApKQpgYGAKCgo8YnIgLz4gCgoKLS0tCgojIyMgVGltZSBzZXJpZXMgcGxvdHMgCgo8ZGV0YWlscz48c3VtbWFyeT5DbGljayB0byBzZWUgY29kZSBmb3IgcGxvdCA8L3N1bW1hcnk+PHA+CmBgYHtyfQp0c1Bsb3QgPC0gY29pbkRhdGEgJT4lCiAgc2VsZWN0KGRhdGUsZW5kc193aXRoKCJwcmljZXNfdXNkIikpICU+JQogIGdhdGhlcihjb2luLCB1c2QsIC1kYXRlKSAlPiUKICBnZ3Bsb3QoYWVzKGRhdGUsdXNkKSkgKwogIGdlb21fbGluZSgpICsKICBmYWNldF93cmFwKH5jb2luLG5yb3c9MyxzY2FsZXM9ImZyZWVfeSIpCmBgYAo8L3A+PC9kZXRhaWxzPgoKYGBge3J9CnRzUGxvdApgYGAKCmBgYHtyfQp0c1Bsb3QgKyBzY2FsZV95X2NvbnRpbnVvdXModHJhbnM9J2xvZzEwJykKYGBgCiAgICAKCjxkZXRhaWxzPjxzdW1tYXJ5PkNsaWNrIHRvIHNlZSBjb2RlIGZvciBwbG90IDwvc3VtbWFyeT48cD4KYGBge3J9CnJldHVyblBsb3QxIDwtIGNvaW5EYXRhICU+JQogIHNlbGVjdChlbmRzX3dpdGgoInByaWNlc191c2QiKSkgJT4lCiAgYXBwbHkoLiwyLGZ1bmN0aW9uKHgpIGRpZmYoeCkpICU+JSAKICBhc190aWJibGUgJT4lIAogIG11dGF0ZShkYXRlID0gY29pbkRhdGEkZGF0ZVstMV0pICU+JSAKICBnYXRoZXIoY29pbiwgdXNkLC1kYXRlKSAlPiUgCiAgZ2dwbG90KGFlcyhkYXRlLHVzZCkpICsKICBnZW9tX3BvaW50KCkgKwogIHlsYWIoIlByaWNlIERpZmZlcmVuY2UgKFVTRCkiKSArCiAgZmFjZXRfd3JhcCh+Y29pbixucm93PTMsc2NhbGVzPSJmcmVlX3kiKQpgYGAKPC9wPjwvZGV0YWlscz4KCmBgYHtyfQpyZXR1cm5QbG90MQpgYGAKCjxkZXRhaWxzPjxzdW1tYXJ5PkNsaWNrIHRvIHNlZSBjb2RlIGZvciBwbG90IDwvc3VtbWFyeT48cD4KYGBge3J9CnJldHVyblBsb3QyIDwtIGNvaW5EYXRhICU+JQogIHNlbGVjdChlbmRzX3dpdGgoInByaWNlc191c2QiKSkgJT4lCiAgYXBwbHkoLiwyLGZ1bmN0aW9uKHgpIGxvZzEwKHgpICU+JSBkaWZmKSAlPiUgCiAgYXNfdGliYmxlICU+JSAKICBtdXRhdGUoZGF0ZSA9IGNvaW5EYXRhJGRhdGVbLTFdKSAlPiUgCiAgZ2F0aGVyKGNvaW4sIGxvZ19yZXR1cm4sLWRhdGUpICU+JSAKICBnZ3Bsb3QoYWVzKGRhdGUsbG9nX3JldHVybikpICsKICBnZW9tX3BvaW50KCkgKwogIHlsYWIoIkRhaWx5IExvZyBSZXR1cm4gKGxvZzEwKSIpICsKICBmYWNldF93cmFwKH5jb2luLG5yb3c9MyxzY2FsZXM9ImZyZWVfeSIpCmBgYAo8L3A+PC9kZXRhaWxzPgoKYGBge3J9CnJldHVyblBsb3QyCmBgYCAgCgoKCi0tLQoKPGJyIC8+IAoKIyMjIENvcnJlbGF0aW9uIFN0cnVjdHVyZQoKIyMjIyBDb3JyZWxhdGlvbiBiZXR3ZWVuIGNyeXB0b2N1cnJlbmN5IHRpbWVzZXJpZXMKCjxkZXRhaWxzPjxzdW1tYXJ5PkNsaWNrIHRvIHNlZSBjb2RlIGZvciBwbG90IDwvc3VtbWFyeT48cD4KYGBge3IgbWVzc2FnZT1GQUxTRX0KcGxvdENvaW5EYXRhIDwtIGNvaW5EYXRhICU+JQogIG11dGF0ZSh5ZWFyID0gZm9ybWF0KGRhdGUsIiVZIikgJT4lIGFzLmZhY3RvcikgJT4lCiAgc2VsZWN0KHllYXIsZW5kc193aXRoKCJwcmljZXNfdXNkIikpICU+JQogIGdncGFpcnMocHJvZ3Jlc3MgPSBGQUxTRSwKICBtYXBwaW5nPWdncGxvdDI6OmFlcyhjb2xvdXIgPSB5ZWFyKSkKYGBgCjwvcD48L2RldGFpbHM+CgpgYGB7ciByZXN1bHRzPSJoaWRlIn0KcGxvdENvaW5EYXRhCmBgYAoKLSBUaGVyZSBpcyBhIHN0cm9uZyBjb3JyZWxhdGlvbiBiZXR3ZWVuIHRoZSBjcnlwdG9jdXJyZW5jeSB0aW1lc2VyaWVzIQoKLSBUaGUgY29ycmVsYXRpb24gYmV0d2VlbiB0aGUgY3J5cHRvY3VycmVuY2llcyBzZWVtcyB0byBjaGFuZ2Ugb3ZlciB0aW1lIChEeW5hbWljIGhlZGdlIHJhdGlvJ3M/KQogICAgCjxiciAvPiAKCgojIyMjIEF1dG9jb3JyZWxhdGlvbiAod2l0aGluIHRpbWVzZXJpZXMpCgpBdXRvY29ycmVsYXRpb246IApcW1x0ZXh0e2Nvcn1beV90LHlfe3Qta31dXF0gd2l0aCBsYWcgJGs9MSwyLDMsXGxkb3RzJAoKPGRldGFpbHM+PHN1bW1hcnk+Q2xpY2sgdG8gc2VlIGNvZGUgZm9yIHBsb3QgPC9zdW1tYXJ5PjxwPgpgYGB7cn0KYWNmUGxvdHMgPC0gcGFzdGUwKGNvaW5TeW1ib2xzLCJfcHJpY2VzX3VzZCIpICU+JQogIG1hcCgKICAgIGZ1bmN0aW9uKHgsY29pbkRhdGEpCiAgICAgIGNvaW5EYXRhICU+JQogICAgICBwdWxsKHgpICU+JQogICAgICBsb2cxMCAlPiUKICAgICAgZ2dhY2YgKwogICAgICBnZ3RpdGxlKHgpLAogICAgICBjb2luRGF0YT1jb2luRGF0YQogICAgICApCmBgYAo8L3A+PC9kZXRhaWxzPgoKYGBge3J9CmdyaWQuYXJyYW5nZShncm9icz1hY2ZQbG90cykKYGBgCgotIFRoZXJlIGlzICBhIHZlcnkgc3Ryb25nIGF1dG9jb3JyZWxhdGlvbiB3aXRoaW4gdGltZSBzZXJpZXMhCi0gSW5kZWVkIHRoZSBwcmljZSBhdCB0aW1lIHQgaXMgc3Ryb25nbHkgY29ycmVsYXRlZCB0byB0aGF0IGF0IGEgZmV3IGRheXMgKGxhZ3MgaykgZWFybGllci4KCiAgICAKPGJyIC8+IAoKLS0tCgpQYXJ0aWFsIGNvcnJlbGF0aW9uPwoKXFtcdGV4dHtjb3J9W3lfdCx5X3t0LWt9XHZlcnQgeV97dC0xfVxsZG90cyB5X3t0LWsrMX1dXF0KCjxkZXRhaWxzPjxzdW1tYXJ5PkNsaWNrIHRvIHNlZSBjb2RlIGZvciBwbG90IDwvc3VtbWFyeT48cD4KYGBge3J9CnBhY2ZQbG90cyA8LSBwYXN0ZTAoY29pblN5bWJvbHMsIl9wcmljZXNfdXNkIikgJT4lCiAgbWFwKAogICAgZnVuY3Rpb24oeCxjb2luRGF0YSkKICAgICAgY29pbkRhdGEgJT4lCiAgICAgIHB1bGwoeCkgJT4lCiAgICAgIGxvZzEwICU+JQogICAgICBnZ2FjZih0eXBlPSJwYXJ0aWFsIikgKwogICAgICB5bGFiKCJQYXJ0aWFsIEFDRiIpICsKICAgICAgZ2d0aXRsZSh4KSwKICAgICAgY29pbkRhdGE9Y29pbkRhdGEKICAgICAgKQpgYGAKPC9wPjwvZGV0YWlscz4KCmBgYHtyfQpncmlkLmFycmFuZ2UoZ3JvYnM9cGFjZlBsb3RzKQpgYGAKCgotIFRoZSBwYXJ0aWFsIEFDRiBvbmx5IHNob3dzIGEgZGlyZWN0IGNvcnJlbGF0aW9uIGJldHdlZW4gdGhlIGN1cnJlbnQgcHJpY2UgYW5kIHRoZSBwcmljZSBhdCB0aGUgcHJldmlvdXMgZGF5IChsYWcgMSkuICAKLSBIZW5jZSwgdGhlIHByaWNlIG9mIGNyeXB0b2N1cnJlbmNpZXMgc2VlbXMgdW5jb3JyZWxhdGVkIHdpdGggdGhhdCBpbiB0aGUgcGFzdCB3aGVuIHdlIGNvbmRpdGlvbiBvbiB5ZXN0ZXJkYXlzIHByaWNlLgoKXFtcbGRvdHMgXHJpZ2h0YXJyb3cgeV97dC0yfSBccmlnaHRhcnJvdyB5X3t0LTF9IFxyaWdodGFycm93IHlfdCBccmlnaHRhcnJvdyB5X3t0KzF9IFxyaWdodGFycm93IFxsZG90cyBcXQogICAgCjxiciAvPiAKCgojIyMgTm9ybWFsaXR5Cgo8ZGV0YWlscz48c3VtbWFyeT5DbGljayB0byBzZWUgY29kZSBmb3IgcGxvdCA8L3N1bW1hcnk+PHA+CmBgYHtyfQpxcXBsb3RzIDwtIGNvaW5EYXRhICU+JQogIHNlbGVjdChlbmRzX3dpdGgoInByaWNlc191c2QiKSkgJT4lCiAgYXBwbHkoLiwyLGZ1bmN0aW9uKHgpIGxvZzEwKHgpICU+JSBkaWZmKSAlPiUgCiAgYXNfdGliYmxlICU+JSAKICBtdXRhdGUoZGF0ZSA9IGNvaW5EYXRhJGRhdGVbLTFdKSAlPiUgCiAgZ2F0aGVyKGNvaW4sIGxvZ19yZXR1cm4sLWRhdGUpICU+JSAKICBnZ3Bsb3QoYWVzKHNhbXBsZSA9IGxvZ19yZXR1cm4pKSArCiAgZ2VvbV9xcSgpICsgCiAgZ2VvbV9xcV9saW5lKCkgKyAKICBmYWNldF93cmFwKH5jb2luLG5yb3c9MyxzY2FsZXM9ImZyZWVfeSIpCmBgYAo8L3A+PC9kZXRhaWxzPgoKYGBge3J9CnFxcGxvdHMKYGBgCgotIFRoZSBsb2ctZGlmZmVyZW5jZWQgZGF0YSBhcmUgc3ltbWV0cmljIGJ1dCBub24tbm9ybWFsbHkgZGlzdHJpYnV0ZWQKLSBJbiB0aGUgcmVtYWluZGVyIHdlIHdpbGwgaW50cm9kdWNlIEdhdXNzaWFuIFN0YXRlIFNwYWNlIE1vZGVscwotIEhvd2V2ZXIsIGlmIHRoZSB0aW1lc2VyaWVzIGFyZSBub24tR2F1c3NpYW4gdGhlIHBhcmFtZXRlciBlc3RpbWF0b3JzIGFyZSBzdGlsbCBhc3N5bXB0b3RpY2FsbHkgbm9ybWFsbHkgZGlzdHJpYnV0ZWQuIAotIEluIHNob3J0IHRpbWUtc2VyaWVzIHdlIGNhbiByZXNvcnQgdG8gdGhlIGJvb3RzdHJhcCB0byBwcm92aWRlIGJldHRlciBpbmZlcmVuY2UuIAoKPGJyIC8+IAoKLS0tCgojIFN0YXRlIFNwYWNlIE1vZGVscwoKIyMgQ3J5cHRvY3VycmVuY3kgZXhhbXBsZSAKCi0gV2Ugd2lsbCBsb2ckX1x0ZXh0ezEwfSQtdHJhbnNmb3JtIHRoZSBkYXRhIGFuZCBtb2RlbCBpdCB3aXRoIGEgc3RhdGUgc3BhY2UgbW9kZWwuIAoKLSBMZXQgJFxtYXRoYmZ7eX1fdCQgYmUgYSB2ZWN0b3Igb2YgdGhlIGxvZy10cmFuc2Zvcm1lZCBwcmljZXMgb2YgYnRjLCBldGggYW5kIGx0YyBhdCB0aW1lICR0JApcW1xtYXRoYmZ7eX1fdD1cYmVnaW57Ym1hdHJpeH15Xlx0ZXh0e2J0Y31fdFxceV5cdGV4dHtldGh9X3RcXHleXHRleHR7bHRjfV90XGVuZHtibWF0cml4fVxdCi0gU3VwcG9zZSB0aGF0IHRoZXkgYXJlIGRyaXZlbiBieSBhbiB1bmRlcmx5aW5nIHVua25vd24gZmlyc3Qgb3JkZXIgTWFya292aWFuIGNyeXB0b2N1cnJlbmN5IHByaWNlIGxldmVsLCByZWZlcnJlZCB0byBhcyB0aGUgc3RhdGUgJFxtYXRoYmZ7WH0kClxbXG1hdGhiZntYfT1cYmVnaW57Ym1hdHJpeH1YXlx0ZXh0e2J0Y31fdFxcWF5cdGV4dHtldGh9X3RcXFheXHRleHR7bHRjfV90XGVuZHtibWF0cml4fVxdCi0gU28gdGhlIGFjdHVhbCBvYnNlcnZlZCBwcmljZSAkXG1hdGhiZnt5fV90JCBpcyBhIG5vaXN5IHJlYWxpc2F0aW9uIG9mIHRoZSB1bmRlcmx5aW5nIHVua25vd24gcHJpY2UgbGV2ZWwgKHN0YXRlKSAgJFxtYXRoYmZ7WH1fdCQuIAoKXFsKXGJlZ2lue2FycmF5fXtjY2NjY2NjY2NjY31cbGRvdHMgJiBccmlnaHRhcnJvdyAmIFxtYXRoYmZ7WH1fe3QtMn0gJiBccmlnaHRhcnJvdyAmIFxtYXRoYmZ7WH1fe3QtMX0gJiBccmlnaHRhcnJvdyAmIFxtYXRoYmZ7WH1fdCAmIFxyaWdodGFycm93ICYgXG1hdGhiZntYfV97dCsxfSAmIFxyaWdodGFycm93ICYgXGxkb3RzXFwKJiZcZG93bmFycm93JiZcZG93bmFycm93JiZcZG93bmFycm93JiZcZG93bmFycm93XFwKXGxkb3RzICYgICYgXG1hdGhiZnt5fV97dC0yfSAmICYgXG1hdGhiZnt5fV97dC0xfSAmICAmIFxtYXRoYmZ7eX1fdCAmICYgXG1hdGhiZnt5fV97dCsxfSAmICAmIFxsZG90cwpcZW5ke2FycmF5fQpcXQoKICAgIAo8YnIgLz4gCgotIEFzc3VtZSB0aGF0ICRcbWF0aGJme3l9X3RcdmVydCBcbWF0aGJme1h9X3QkIGFuZCAkXG1hdGhiZntYfV90XHZlcnQgXG1hdGhiZntYfV97dC0xfSQgYXJlIG11bHRpdmFyaWF0ZSBub3JtYWxseSAoTVZOKSBkaXN0cmlidXRlZCAKCjxiciAvPgoKLSBXZSBjYW4gdGhhbiBmb3JtdWxhdGUgdGhlIGZvbGxvd2luZyBTdGF0ZSBTcGFjZSBNb2RlbDogCgpcWwpcbGVmdFxsYnJhY2UKXGJlZ2lue2FycmF5fXtsbGxsfQpcbWF0aGJme3l9X3QmPSZcbWF0aGJme1h9X3QrXG1hdGhiZnt2fV90ICZcdGV4dHt3aXRofVwgXG1hdGhiZnt2fV90XHNpbSBNVk4oXG1hdGhiZnswfSxcbWF0aGJme1J9KVxcClxtYXRoYmZ7WH1fdCY9Jlxib2xkc3ltYm9se1xQaGl9IFxtYXRoYmZ7WH1fe3QtMX0gKyBcbWF0aGJme3d9X3QgJlx0ZXh0e3dpdGh9XCBcbWF0aGJme3d9X3Rcc2ltIE1WTihcbWF0aGJmezB9LFxtYXRoYmZ7UX0pClxlbmR7YXJyYXl9ClxyaWdodC4KXF0KCndpdGggCgotICR0ID0gMSwgXGxkb3RzLG4kIAotIGFuICQzIFx0aW1lcyAzJCB0cmFuc2l0aW9uIG1hdHJpeCAkXGJvbGRzeW1ib2x7XFBoaX0kIGFuZCAKLSBpbmRlcGVuZGVudCAkMyBcdGltZXMgMSQgdmVjdG9ycyAkXG1hdGhiZnt2fV90JCBhbmQgJFxtYXRoYmZ7d31fdCQgd2l0aCBtZWFuIHplcm8gYW5kIHZhcmlhbmNlLWNvdmFyaWFuY2UgbWF0cml4ICRcbWF0aGJme1J9JCBhbmQgJFxtYXRoYmZ7UX0kLCByZXNwZWN0aXZlbHkuCgoKPGJyIC8+CgotLS0KCiMjIEdlbmVyYWwgZm9ybXVsYXRpb24gb2YgU3RhdGUgU3BhY2UgTW9kZWwKClxbClxsZWZ0XGxicmFjZQpcYmVnaW57YXJyYXl9e2xsbGx9ClxtYXRoYmZ7eX1fdCY9JlxtYXRoYmZ7QX1fdFxtYXRoYmZ7WH1fdCtcbWF0aGJme3Z9X3QgJndpdGhcIFxtYXRoYmZ7dn1fdFxzaW0gTVZOKFxtYXRoYmZ7MH0sXG1hdGhiZntSfV90KVxcClxtYXRoYmZ7WH1fdCY9Jlxib2xkc3ltYm9se1xQaGl9X3QgXG1hdGhiZntYfV97dC0xfSArIFxtYXRoYmZ7d31fdCAmd2l0aFwgXG1hdGhiZnt3fV90XHNpbSBNVk4oXG1hdGhiZnswfSxcbWF0aGJme1F9X3QpClxlbmR7YXJyYXl9ClxyaWdodC5cXQoKd2l0aCAKCi0gJHQ9MSxcbGRvdHMsIG4kCi0gZmlyc3Qgb3JkZXIgTWFya292aWFuIHN0YXRlIHByb2Nlc3MgJFxtYXRoYmZ7WH1fdD0oWF97MXR9Li4uWF97bXR9KV5UJCwgCi0gJFxtYXRoYmZ7eX1fdD0oeV97MXR9Li4ueV97cHR9KV5UJAotICRcbWF0aGJme3Z9X3QgXHBlcnAgXCFcIVwhIFxwZXJwIFxtYXRoYmZ7d31fdCQKLSAkXG1hdGhiZntBfV90JCB0aGUgJHAgXHRpbWVzIG0kIG1lYXN1cmVtZW50IG9yIG9ic2VydmF0aW9uIG1hdHJpeCB0aGF0IGxpbmtzICRcbWF0aGJme3l9X3QkIHRvICRcbWF0aGJme1h9X3QkCi0gYW4gJG0gXHRpbWVzIG0kIHRyYW5zaXRpb24gbWF0cml4ICRcYm9sZHN5bWJvbHtcUGhpfV90JCBhbmQgCgo8YnIgLz4gCgpOb3RlLCB0aGF0IHRoZSBTdGF0ZSBTcGFjZSBNb2RlbCBpcyB2ZXJ5IGZsZXhpYmxlISAKCi0gQnkgZW1iZWRkaW5nIHRoZSBBUigxKSBzdHJ1Y3R1cmUgaW4gdGhlIG1lYXN1cmVtZW50IGVxdWF0aW9uLCB0aGUgdGltZSBzZXJpZXMgbW9kZWwgYmVjb21lcyBtdWNoIG1vcmUgZ2VuZXJhbC4gCi0gV2hlbiAkXG1hdGhiZnt2fV90JCBhbmQgJFxtYXRoYmZ7d31fdCQgYXJlIGFsbG93ZWQgdG8gYmUgY29ycmVsYXRlZCwgaXQgY2FuIGJlIHNob3duIHRoYXQgZm9yIHNwZWNpZmljIGNob2ljZXMgdGhlIHN0YXRlLXNwYWNlIG1vZGVsIGNhbiBhbHNvIG1vZGVsIHNwZWNpZmljIEFSTUEgbW9kZWxzLiAKLSBFeHRlbnNpb25zIGFyZSB2ZXJ5IGZsZXhpYmxlOiBlLmcuIAogICAgIAogICAgIC0gbWlzc2luZyBkYXRhLCAKICAgICAtIGV4b2dlbmVvdXMgY292YXJpYXRlcywgCiAgICAgLSBtb3JlIGNvbXBsZXggYXV0b2NvcnJlbGF0aW9uIHN0cnVjdHVyZXMgCiAgICAgLSAuLi4KCi0tLQoKPGJyIC8+CgojIyBQcmVkaWN0aW9uLCBGaWx0ZXJpbmcgJiBTbW9vdGhpbmcKCi0gRnJvbSBhIHByYWN0aWNhbCB2aWV3LCBhIHByaW1hcnkgYWltIG9mIHRoZSBzdGF0ZS1zcGFjZSBhbmFseXNpcyBpcyB0byBwcm9kdWNlIGVzdGltYXRlcyBvZiB0aGUgc3RhdGVzICRcbWF0aGJme1h9X3QkIGJ5IHVzaW5nIGEgc2V0IG9mIG9ic2VydmF0aW9ucyAkXG1hdGhiZntZfV9yPShcbWF0aGJme3l9XzEsXGxkb3RzLCBcbWF0aGJme3l9X3IpXlQkLgoKLSBJbmRlZWQgd2Ugd2FudCB0byBpbmZlciBvbiB0aGUgdW5kZXJseWluZyBsYXRlbnQgcHJpY2UgbGV2ZWwhCgotIFRoZSBlc3RpbWF0aW9uIG9mICRcbWF0aGJme1h9X3QkIGdpdmVuICRcbWF0aGJme1l9X3IkIGlzIHJlZmVycmVkIHRvIGFzCiAgICAKICAgIDEuIHByZWRpY3Rpb24gZm9yICR0PnIkLAogICAgMi4gZmlsdGVyaW5nIGZvciAkdD1yJCwgYW5kCiAgICAzLiBzbW9vdGhpbmcgZm9yICR0PHIkCgotLS0KCjxiciAvPgoKCiMjIEthbG1hbiBGaWx0ZXIgYW5kIFNtb290aGVyCgojIyMgQ3J5cHRvY3VycmVuY3kgZXhhbXBsZSAKClxbClxiZWdpbnthcnJheX17Y2NjY2NjY2NjY2N9XGxkb3RzICYgXHJpZ2h0YXJyb3cgJiBcbWF0aGJme1h9X3t0LTJ9ICYgXHJpZ2h0YXJyb3cgJiBcbWF0aGJme1h9X3t0LTF9ICYgXHJpZ2h0YXJyb3cgJiBcbWF0aGJme1h9X3QgJiBccmlnaHRhcnJvdyAmIFxtYXRoYmZ7WH1fe3QrMX0gJiBccmlnaHRhcnJvdyAmIFxsZG90c1xcCiYmXGRvd25hcnJvdyYmXGRvd25hcnJvdyYmXGRvd25hcnJvdyYmXGRvd25hcnJvd1xcClxsZG90cyAmICAmIFxtYXRoYmZ7eX1fe3QtMn0gJiAmIFxtYXRoYmZ7eX1fe3QtMX0gJiAgJiBcbWF0aGJme3l9X3QgJiAmIFxtYXRoYmZ7eX1fe3QrMX0gJiAgJiBcbGRvdHNcXFxcClxlbmR7YXJyYXl9ClxdCgpcWwpcbGVmdFxsYnJhY2UKXGJlZ2lue2FycmF5fXtsbGxsfQpcbWF0aGJme3l9X3QmPSZcbWF0aGJme1h9X3QrXG1hdGhiZnt2fV90ICZcdGV4dHt3aXRofVwgXG1hdGhiZnt2fV90XHNpbSBNVk4oXG1hdGhiZnswfSxcbWF0aGJme1J9KVxcClxtYXRoYmZ7WH1fdCY9Jlxib2xkc3ltYm9se1xQaGl9IFxtYXRoYmZ7WH1fe3QtMX0gKyBcbWF0aGJme3d9X3QgJlx0ZXh0e3dpdGh9XCBcbWF0aGJme3d9X3Rcc2ltIE1WTihcbWF0aGJmezB9LFxtYXRoYmZ7UX0pClxlbmR7YXJyYXl9ClxyaWdodC4KXF0KClNvIGluIG91ciBleGFtcGxlICRcbWF0aGJme0F9X3Q9XG1hdGhiZntJfSQKCi0tLQoKPGJyIC8+CgoKIyMjIyBLYWxtYW4gRmlsdGVyCgotIEltcG9ydGFudCBmb3IgZS5nLiBvbmxpbmUgZXN0aW1hdGlvbiBhbmQgcHJlZGljdGlvbgotIExldCAkXG1hdGhiZntZfV9yPShcbWF0aGJme3l9XzFeVCxcbGRvdHMsXG1hdGhiZnt5fV9yXlQpXlQkICAgCi0gUHJlZGljdGlvbiBzdGVwOiBcW1x0ZXh0e0V9XGxlZnRbXG1hdGhiZntYfV90XHZlcnQgXG1hdGhiZntZfV97dC0xfVxyaWdodF1cXQotIFVwZGF0ZSBzdGVwIHdoZW4gbmV3IG9ic2VydmF0aW9uIGNvbWVzIGluOiAKXFtcdGV4dHtFfVxsZWZ0W1xtYXRoYmZ7WH1fdFx2ZXJ0IFxtYXRoYmZ7WX1fe3R9XHJpZ2h0XVxdCgotIFByb3ZpZGVzIGVmZmljaWVudCBmYWN0b3Jpc2F0aW9uIG9mIHRoZSBsaWtlbGlob29kCgpgYGB7ciBtZXNzYWdlPUZBTFNFLCByZXN1bHRzPSJoaWRlIn0KeSA8LSBjb2luRGF0YSAlPiUgCiAgc2VsZWN0KGVuZHNfd2l0aCgiX3ByaWNlc191c2QiKSkgJT4lIAogIGxvZzEwICU+JQogIGFzLm1hdHJpeAoKbnVtIDwtIG5yb3coeSkKCiMgTWVhc3VyZW1lbnQgbWF0cml4IEEgPSBJIApBPWRpYWcoMykgICMgY3JlYXRlcyAzeDMgZGlhZ29uYWwgbWF0cml4CgojIGluaXRpYWwgdmFsdWVzCm11MCA8LSBtYXRyaXgoMCwzLDEpClNpZ21hMCA8LSBkaWFnKGMoLjEsLjEsLjEpLDMpClBoaSA8LSBkaWFnKDEsMykKY1EgPC0gZGlhZyhjKC4xLC4xLC4xKSwzKQpjUiA8LSBkaWFnKGMoLjEsLjEsLjEpLDMpCgplbSA8LSBFTTAobnVtID0gbnVtLCB5ID0geSwgQSA9IEEsIG11MCA9IG11MCwgU2lnbWEwID0gU2lnbWEwLCBQaGkgPSBQaGksIGNRID0gY1EsIGNSID0gY1IsIG1heC5pdGVyID0gMTAwLCB0b2wgPSAuMDAwMDEpCgprZiA8LSBLZmlsdGVyMChudW0gPSBudW0sIHkgPSB5LCBBID0gQSwgbXUwID0gbXUwLCBTaWdtYTAgPSBlbSRTaWdtYTAsIFBoaSA9IGVtJFBoaSwgY1EgPSBjaG9sKGVtJFEpLCBjUiA9IGNob2woZW0kUikpCmBgYAoKPGRldGFpbHM+PHN1bW1hcnk+Q2xpY2sgdG8gc2VlIGNvZGUgdG8gbWFrZSBwbG90cyA8L3N1bW1hcnk+PHA+CmBgYHtyfQpkYXRhSGxwIDwtIGNvaW5EYXRhICU+JQogIHNlbGVjdChkYXRlLGVuZHNfd2l0aCgicHJpY2VzX3VzZCIpKSAlPiUKICBnYXRoZXIoY29pbiwgdXNkLCAtZGF0ZSkgJT4lIAogIG11dGF0ZSh4ZiA9IGtmJHhmWywxLF0gJT4lIHQoLikgJT4lIGMpICU+JQogIG11dGF0ZSh4ZmxsID0gKGtmJHhmWywxLF0gLQogICAgICAgICAgIDIgKiBzYXBwbHkoMTpuZGF5cyxmdW5jdGlvbihpKSBrZiRQZlssLGldICU+JSBkaWFnICU+JSBzcXJ0KSkgJT4lIHQoLikgJT4lIGMpICU+JQogbXV0YXRlKHhmdWwgPSAoa2YkeGZbLDEsXSArCiAgICAgICAgICAgMiAqIHNhcHBseSgxOm5kYXlzLGZ1bmN0aW9uKGkpIGtmJFBmWywsaV0gJT4lIGRpYWcgJT4lIHNxcnQpKSAlPiUgdCguKSAlPiUgYykgJT4lCiAgbXV0YXRlKHhwID0ga2YkeHBbLDEsXSAlPiUgdCguKSAlPiUgYykgJT4lCiAgbXV0YXRlKHhwbGwgPSAoa2YkeHBbLDEsXSAtCiAgICAgICAgICAgMiAqIHNhcHBseSgxOm5kYXlzLGZ1bmN0aW9uKGkpIGtmJFBwWywsaV0gJT4lIGRpYWcgJT4lIHNxcnQpKSAlPiUgdCguKSAlPiUgYykgJT4lCiBtdXRhdGUoeHB1bCA9IChrZiR4cFssMSxdICsKICAgICAgICAgICAyICogc2FwcGx5KDE6bmRheXMsZnVuY3Rpb24oaSkga2YkUHBbLCxpXSAlPiUgZGlhZyAlPiUgc3FydCkpICU+JSB0KC4pICU+JSBjKSAKYGBgCgpgYGB7cn0KdHBsb3QgPC0gYXMuRGF0ZSgiMjAyMC0xMi0zMSIpCmJhc2VQbG90IDwtIGRhdGFIbHAgJT4lIAogIGZpbHRlcihkYXRlID4gdHBsb3QpICU+JQogIGdncGxvdChhZXMoZGF0ZSx1c2QpKSArCiAgZ2VvbV9wb2ludChhbHBoYT0wKSArCiAgZmFjZXRfd3JhcCh+Y29pbixucm93PTMsc2NhbGVzPSJmcmVlX3kiKSArCiAgdGhlbWVfbWluaW1hbCgpCgpzdG9wRGF0ZSA8LSBkYXRhSGxwICU+JSBwdWxsKGRhdGUpICU+JSBtYXgKc3RhcnREYXRlIDwtIHN0b3BEYXRlIC0gMzAKCmRhdGFIbHAyIDwtIGRhdGFIbHAgJT4lIAogIGZpbHRlcihkYXRlID4gdHBsb3QgJiBkYXRlIDwgc3RhcnREYXRlKQoKaSA8LSAxCnBsb3RLRmlsdGVyTGlzdCA8LSBsaXN0KAogIGJhc2VQbG90ICsKICBnZW9tX3BvaW50KHNoYXBlPTIxLHNpemU9LjUsIGRhdGEgPSBkYXRhSGxwMikgKyAKICBnZW9tX3JpYmJvbihhZXMoeW1pbiA9IDEwXnhmdWwseW1heCA9IDEwXnhmbGwpLCBhbHBoYSA9IDAuNDUsIGRhdGEgPSBkYXRhSGxwMikgKwogIGdlb21fbGluZShhZXMoZGF0ZSwxMF54ZiksIGRhdGEgPSBkYXRhSGxwMikgKyAKICBnZ3RpdGxlKCIgIikgCiAgKQoKZm9yICh0IGluIHNlcShzdGFydERhdGUsc3RvcERhdGUsMSkpCnsKZGF0YUhscDIgPC0gZGF0YUhscCAlPiUgCiAgZmlsdGVyKGRhdGUgPiB0cGxvdCAmIGRhdGUgPCB0KQppIDwtIGkrMSAKcGxvdEtGaWx0ZXJMaXN0W1tpXV0gPC0gYmFzZVBsb3QgKwogIGdlb21fcG9pbnQoc2hhcGU9MjEsc2l6ZT0uNSwgZGF0YSA9IGRhdGFIbHAyKSArIAogIGdlb21fcmliYm9uKGFlcyh5bWluID0gMTBeeGZ1bCx5bWF4ID0gMTBeeGZsbCksIGFscGhhID0gMC40NSwgZGF0YSA9IGRhdGFIbHAyKSArCiAgZ2VvbV9saW5lKGFlcyhkYXRlLDEwXnhmKSwgZGF0YSA9IGRhdGFIbHAyKSArCiAgZ2VvbV9wb2ludChhZXMoZGF0ZSwxMF54cCksZGF0YSA9IGRhdGFIbHAgJT4lIGZpbHRlcihkYXRlID09IHQpLGNvbG9yPTIpICsKICAgZ2VvbV9zZWdtZW50KGFlcyh4ID0gZGF0ZSwgeSA9IDEwXnhwbGwsIHhlbmQgPSBkYXRlLCB5ZW5kID0gMTBeeHB1bCksIGNvbG9yID0gMiwgZGF0YSA9IGRhdGFIbHAgJT4lIGZpbHRlcihkYXRlID09IHQpKSArCiAgIGdndGl0bGUoIlByZWRpY3Rpb24iKQoKaSA8LSBpKzEgCnBsb3RLRmlsdGVyTGlzdFtbaV1dIDwtIHBsb3RLRmlsdGVyTGlzdFtbaS0xXV0gKwogIGdlb21fcG9pbnQoYWVzKGRhdGUsdXNkKSxkYXRhID0gZGF0YUhscCAlPiUgZmlsdGVyKGRhdGUgPT0gdCkpICsgCiAgZ2d0aXRsZSgiTmV3IG9ic2VydmF0aW9uIGFycml2ZXMiKQoKaSA8LSBpICsgMQpkYXRhSGxwMiA8LSBkYXRhSGxwICU+JSAKICBmaWx0ZXIoZGF0ZSA+IHRwbG90ICYgZGF0ZSA8PSB0KQpwbG90S0ZpbHRlckxpc3RbW2ldXSA8LSAgIGJhc2VQbG90ICsKICBnZW9tX3BvaW50KHNoYXBlPTIxLHNpemU9LjUsIGRhdGEgPSBkYXRhSGxwMikgKyAKICBnZW9tX3JpYmJvbihhZXMoeW1pbiA9IDEwXnhmdWwseW1heCA9IDEwXnhmbGwpLCBhbHBoYSA9IDAuNDUsIGRhdGEgPSBkYXRhSGxwMikgKwogIGdlb21fbGluZShhZXMoZGF0ZSwxMF54ZiksIGRhdGEgPSBkYXRhSGxwMikgKyAKICBnZW9tX3BvaW50KGFlcyhkYXRlLDEwXnhwKSxkYXRhID0gZGF0YUhscCAlPiUgZmlsdGVyKGRhdGUgPT0gdCksY29sb3I9MikgKwogIGdlb21fc2VnbWVudChhZXMoeCA9IGRhdGUsIHkgPSAxMF54cGxsLCB4ZW5kID0gZGF0ZSwgeWVuZCA9IDEwXnhwdWwpLCBjb2xvciA9IDIsIGRhdGEgPSBkYXRhSGxwICU+JSBmaWx0ZXIoZGF0ZSA9PSB0KSkgKwogIGdlb21fcG9pbnQoYWVzKGRhdGUsdXNkKSxkYXRhID0gZGF0YUhscCAlPiUgZmlsdGVyKGRhdGUgPT0gdCkpICsKICBnZ3RpdGxlKCJVcGRhdGUgRmlsdGVyIikgCn0KYGBgCjwvcD48L2RldGFpbHM+CgpgYGB7ciwgYW5pbWF0aW9uLmhvb2s9ImdpZnNraSIsIGVjaG89RkFMU0UsIG1lc3NhZ2UgPSBGQUxTRSwgcmVzdWx0cz0iaGlkZSIsIGludGVydmFsPS41fQpmb3IgKGsgaW4gMTo3KSAKewogICAgZm9yIChpIGluIDE6NSkgcHJpbnQocGxvdEtGaWx0ZXJMaXN0W1trXV0pCn0KZm9yIChrIGluIDg6bGVuZ3RoKHBsb3RLRmlsdGVyTGlzdCkpIHByaW50KHBsb3RLRmlsdGVyTGlzdFtba11dKQpgYGAKCgojIyMjIEthbG1hbiBTbW9vdGhlcgoKLSBPZmZsaW5lIGFwcGxpY2F0aW9uczogVXNlIGFsbCBhdmFpbGFibGUgaW5mb3JtYXRpb24gJFxtYXRoYmZ7WX1fTj0oXG1hdGhiZnt5fV8xXlQsXGxkb3RzLFxtYXRoYmZ7eX1fbl5UKV5UJC4KLSBTbW9vdGhlZCBlc3RpbWF0b3IgYWxzbyBiYXNlZCBvbiBtZWFzdXJlbWVudHMgb24gbGF0ZXIgdGltZSBpbnN0YW50cyAkdCsxLFxsZG90cyxuJDogJFx0ZXh0e0V9XGxlZnRbXG1hdGhiZntYfV90XHZlcnQgXG1hdGhiZntZfV9OXHJpZ2h0XSQKCgpgYGB7cn0Ka3MgPC0gS3Ntb290aDAobnVtID0gbnVtLCB5ID0geSwgQSA9IEEsIG11MCA9IG11MCwgU2lnbWEwID0gZW0kU2lnbWEwLCBQaGkgPSBlbSRQaGksIGNRID0gY2hvbChlbSRRKSwgY1IgPSBjaG9sKGVtJFIpKQpgYGAKCgo8ZGV0YWlscz48c3VtbWFyeT5DbGljayB0byBzZWUgY29kZSBmb3IgcGxvdCA8L3N1bW1hcnk+PHA+CmBgYHtyfQp0cGxvdCA8LSBhcy5EYXRlKCIyMDIwLTEyLTMxIikKYmFzZVBsb3QgPC0gZGF0YUhscCAlPiUgCiAgZmlsdGVyKGRhdGUgPiB0cGxvdCkgJT4lCiAgZ2dwbG90KGFlcyhkYXRlLHVzZCkpICsKICBnZW9tX3BvaW50KGFscGhhPTApICsKICBmYWNldF93cmFwKH5jb2luLG5yb3c9MyxzY2FsZXM9ImZyZWVfeSIpICsKICB0aGVtZV9taW5pbWFsKCkKCmRhdGFIbHAyIDwtIGRhdGFIbHAgICU+JSAKICBtdXRhdGUoeHMgPSBrcyR4c1ssMSxdICU+JSB0KC4pICU+JSBjKSAlPiUKICBtdXRhdGUoeHNsbCA9IChrcyR4c1ssMSxdIC0KICAgICAgICAgICAyICogc2FwcGx5KDE6bmRheXMsZnVuY3Rpb24oaSkga3MkUHNbLCxpXSAlPiUgZGlhZyAlPiUgc3FydCkpICU+JSB0KC4pICU+JSBjKSAlPiUKIG11dGF0ZSh4c3VsID0gKGtzJHhzWywxLF0gKwogICAgICAgICAgIDIgKiBzYXBwbHkoMTpuZGF5cyxmdW5jdGlvbihpKSBrcyRQZlssLGldICU+JSBkaWFnICU+JSBzcXJ0KSkgJT4lIHQoLikgJT4lIGMpCgoKcGxvdEtGaWx0ZXJQcmVkaWN0aW9uIDwtIGJhc2VQbG90ICsKICBnZW9tX3BvaW50KHNoYXBlPTIxLHNpemU9LjUsIGRhdGEgPSBkYXRhSGxwMiAlPiUgZmlsdGVyKGRhdGUgPiB0cGxvdCkpICsgCiAgZ2VvbV9yaWJib24oYWVzKHltaW4gPSAxMF54cHVsLHltYXggPSAxMF54cGxsKSwgYWxwaGEgPSAwLjQ1LCBkYXRhID0gZGF0YUhscDIgJT4lIGZpbHRlcihkYXRlID4gdHBsb3QpKSArCiAgZ2VvbV9saW5lKGFlcyhkYXRlLDEwXnhwKSwgZGF0YSA9IGRhdGFIbHAyICU+JSBmaWx0ZXIoZGF0ZSA+IHRwbG90KSkgKyAKICBnZ3RpdGxlKCJLYWxtYW4gb25lLXN0ZXAtYWhlYWQgcHJlZGljdGlvbiIpIAoKcGxvdEtGaWx0ZXIgPC0gYmFzZVBsb3QgKwogIGdlb21fcG9pbnQoc2hhcGU9MjEsc2l6ZT0uNSwgZGF0YSA9IGRhdGFIbHAyICU+JSBmaWx0ZXIoZGF0ZSA+IHRwbG90KSkgKyAKICBnZW9tX3JpYmJvbihhZXMoeW1pbiA9IDEwXnhmdWwseW1heCA9IDEwXnhmbGwpLCBhbHBoYSA9IDAuNDUsIGRhdGEgPSBkYXRhSGxwMiAlPiUgZmlsdGVyKGRhdGUgPiB0cGxvdCkpICsKICBnZW9tX2xpbmUoYWVzKGRhdGUsMTBeeGYpLCBkYXRhID0gZGF0YUhscDIgJT4lIGZpbHRlcihkYXRlID4gdHBsb3QpKSArIAogIGdndGl0bGUoIkthbG1hbiBmaWx0ZXIiKSAKCnBsb3RLU21vb3RoZXIgPC0gYmFzZVBsb3QgKwogIGdlb21fcG9pbnQoc2hhcGU9MjEsc2l6ZT0uNSwgZGF0YSA9IGRhdGFIbHAyICU+JSBmaWx0ZXIoZGF0ZSA+IHRwbG90KSkgKyAKICBnZW9tX3JpYmJvbihhZXMoeW1pbiA9IDEwXnhzdWwseW1heCA9IDEwXnhzbGwpLCBhbHBoYSA9IDAuNDUsIGRhdGEgPSBkYXRhSGxwMiAlPiUgZmlsdGVyKGRhdGUgPiB0cGxvdCkpICsKICBnZW9tX2xpbmUoYWVzKGRhdGUsMTBeeHMpLCBkYXRhID0gZGF0YUhscDIgJT4lIGZpbHRlcihkYXRlID4gdHBsb3QpKSArIAogIGdndGl0bGUoIkthbG1hbiBzbW9vdGhlciIpIApgYGAKPC9wPjwvZGV0YWlscz4KCmBgYHtyLCBhbmltYXRpb24uaG9vaz0iZ2lmc2tpIiwgbWVzc2FnZSA9IEZBTFNFLCByZXN1bHRzPSJoaWRlIiwgaW50ZXJ2YWwgPSA0fQpwbG90S0ZpbHRlclByZWRpY3Rpb24KcGxvdEtGaWx0ZXIKcGxvdEtTbW9vdGhlcgpgYGAKCi0tLQoKPGJyIC8+CgojIyMgVW5kZXIgdGhlIGhvb2Qgb2YgdGhlIEthbG1hbiBGaWx0ZXIKCkdlbmVyYWwgZm9ybXVsYXRpb24gb2YgdGhlIFN0YXRlIFNwYWNlIE1vZGVsIAoKXFsKXGxlZnRcbGJyYWNlClxiZWdpbnthcnJheX17bGxsbH0KXG1hdGhiZnt5fV90Jj0mXG1hdGhiZntBfV90XG1hdGhiZntYfV90K1xtYXRoYmZ7dn1fdCAmd2l0aFwgXG1hdGhiZnt2fV90XHNpbSBNVk4oXG1hdGhiZnswfSxcbWF0aGJme1J9X3QpXFwKXG1hdGhiZntYfV90Jj0mXGJvbGRzeW1ib2x7XFBoaX1fdCBcbWF0aGJme1h9X3t0LTF9ICsgXG1hdGhiZnt3fV90ICZ3aXRoXCBcbWF0aGJme3d9X3Rcc2ltIE1WTihcbWF0aGJmezB9LFxtYXRoYmZ7UX1fdCkKXGVuZHthcnJheX0KXHJpZ2h0LlxdCgpBc3N1bWUgdGhhdCBmb2xsb3dpbmcgYXJlIGtub3duCgotICRcbWF0aGJme1l9X3t0LTF9PShcbWF0aGJme3l9XzFeVCxcbGRvdHMsXG1hdGhiZnt5fV97dC0xfV5UKV5UJAotICRcbWF0aGJme1h9X3t0LTFcdmVydCB0LTF9PVx0ZXh0e0V9XGxlZnRbXG1hdGhiZntYfV97dC0xfVx2ZXJ0IFxtYXRoYmZ7WX1fe3QtMX1ccmlnaHRdJCAKLSAkXG1hdGhiZntQfV97dC0xXHZlcnQgdC0xfT1cdGV4dHtWYXJ9XGxlZnRbXG1hdGhiZntYfV97dC0xfVx2ZXJ0IFxtYXRoYmZ7WX1fe3QtMX1ccmlnaHRdJCAKCgotLS0KCgo8YnIgLz4KCgojIyMjIFByZWRpY3Rpb24gU3RlcAoKXFsKXGxlZnRcbGJyYWNlClxiZWdpbnthcnJheX17bGxsbH0KXG1hdGhiZnt5fV90Jj0mXG1hdGhiZntBfV90XG1hdGhiZntYfV90K1xtYXRoYmZ7dn1fdCAmd2l0aFwgXG1hdGhiZnt2fV90XHNpbSBNVk4oXG1hdGhiZnswfSxcbWF0aGJme1J9X3QpXFwKXG1hdGhiZntYfV90Jj0mXGJvbGRzeW1ib2x7XFBoaX1fdCBcbWF0aGJme1h9X3t0LTF9ICsgXG1hdGhiZnt3fV90ICZ3aXRoXCBcbWF0aGJme3d9X3Rcc2ltIE1WTihcbWF0aGJmezB9LFxtYXRoYmZ7UX1fdCkKXGVuZHthcnJheX0KXHJpZ2h0LlxdCgo8YnIgLz4KCgpcWwpcYmVnaW57YXJyYXl9e3JjbH0KXG1hdGhiZntYfV90XHZlcnRcbWF0aGJme1l9X3t0LTF9JlxzaW0mIE1WTihcbWF0aGJme1h9X3t0XHZlcnQgdC0xfSwgXG1hdGhiZntQfV97dFx2ZXJ0IHQtMX0pXFxcXApcbWF0aGJme1h9X3t0XHZlcnQgdC0xfSY9Jlx0ZXh0e0V9XGxlZnRbXG1hdGhiZntYfV90XHZlcnRcbWF0aGJme1l9X3t0LTF9XHJpZ2h0XVxcJj0mXGJvbGRzeW1ib2x7XFBoaX1fdFx0ZXh0e0V9XGxlZnRbXG1hdGhiZntYfV97dC0xfVx2ZXJ0XG1hdGhiZntZfV97dC0xfVxyaWdodF1cXAomPSZcYm9sZHN5bWJvbHtcUGhpfV90IFxtYXRoYmZ7WH1fe3QtMVx2ZXJ0IHQtMX1cXFxcClxtYXRoYmZ7UH1fe3RcdmVydCB0LTF9Jj0mXHRleHR7VmFyfVxsZWZ0W1xtYXRoYmZ7WH1fdFx2ZXJ0XG1hdGhiZntZfV97dC0xfVxyaWdodF0gXFwmPSYgXGJvbGRzeW1ib2x7XFBoaX1fdCBcdGV4dHtWYXJ9XGxlZnRbXG1hdGhiZntYfV97dC0xfVx2ZXJ0XG1hdGhiZntZfV97dC0xfVxyaWdodF0gIFxib2xkc3ltYm9se1xQaGl9X3ReVCtcdGV4dHtWYXJ9XGxlZnRbXG1hdGhiZnt3fV90XHZlcnQgXG1hdGhiZntZfV97dC0xfVxyaWdodF1cXAomPSZcYm9sZHN5bWJvbHtcUGhpfV90IFxtYXRoYmZ7UH1fe3QtMVx2ZXJ0IHQtMX0gXGJvbGRzeW1ib2x7XFBoaX1fdF5UK1xtYXRoYmZ7UX1fdC4KIFxlbmR7YXJyYXl9ClxdCgpPbmUgc3RlcCBhaGVhZCBwcmVkaWN0aW9uOiBcW1x0ZXh0e0V9XGxlZnRbXG1hdGhiZnt5fV97dH1cdmVydCBcbWF0aGJme1l9X3t0LTF9XHJpZ2h0XT1cbWF0aGJme0F9X3RcbWF0aGJme1h9X3t0XHZlcnQgdC0xfVxdCgotLS0KCgo8YnIgLz4KCiMjIyMgVXBkYXRlIFN0ZXAKXFsKXGxlZnRcbGJyYWNlClxiZWdpbnthcnJheX17bGxsbH0KXG1hdGhiZnt5fV90Jj0mXG1hdGhiZntBfV90XG1hdGhiZntYfV90K1xtYXRoYmZ7dn1fdCAmd2l0aFwgXG1hdGhiZnt2fV90XHNpbSBNVk4oXG1hdGhiZnswfSxcbWF0aGJme1J9X3QpXFwKXG1hdGhiZntYfV90Jj0mXGJvbGRzeW1ib2x7XFBoaX1fdCBcbWF0aGJme1h9X3t0LTF9ICsgXG1hdGhiZnt3fV90ICZ3aXRoXCBcbWF0aGJme3d9X3Rcc2ltIE1WTihcbWF0aGJmezB9LFxtYXRoYmZ7UX1fdCkKXGVuZHthcnJheX0KXHJpZ2h0LlxdCgo8YnIgLz4KCklmIG9ic2VydmF0aW9uICRcbWF0aGJme3l9X3QkIGF0IHRpbWUgJHQkIGJlY29tZXMgYXZhaWxhYmxlCgpcWwpcYmVnaW57YXJyYXl9e3JjbH0KXG1hdGhiZntYfV90XHZlcnRcbWF0aGJme1l9X3t0LTF9JlxzaW0mIE1WTihcbWF0aGJme1h9X3t0XHZlcnQgdH0sIFxtYXRoYmZ7UH1fe3RcdmVydCB0fSlcXFxcClxtYXRoYmZ7WH1fe3RcdmVydCB0fSY9Jlx0ZXh0e0V9XGxlZnRbXG1hdGhiZntYfV90XHZlcnQgXG1hdGhiZntZfV97dH1ccmlnaHRdXFwKJj0mXHRleHR7RX1cbGVmdFtcbWF0aGJme1h9X3RcdmVydCBcbWF0aGJme1l9X3t0LTF9LCBcYm9sZHN5bWJvbHt5fV90XHJpZ2h0XVxcXFwKXG1hdGhiZntQfV97dFx2ZXJ0IHR9ICY9JiBcdGV4dHtWYXJ9XGxlZnRbXG1hdGhiZntYfV90XHZlcnRcbWF0aGJme1l9X3RccmlnaHRdXFwKJj0mXHRleHR7VmFyfVxsZWZ0W1xtYXRoYmZ7WH1fdFx2ZXJ0XG1hdGhiZntZfV97dC0xfSxcbWF0aGJme3l9X3RccmlnaHRdClxlbmR7YXJyYXl9ClxdCgpFeHBsb2l0IHRoYXQgJFxtYXRoYmZ7WH1fdCQgYW5kICRcbWF0aGJme3l9X3QkIGdpdmVuICRcbWF0aGJme1l9X3t0LTF9JCBhcmUgbXVsdGl2YXJpYXRlIG5vcm1hbGx5IGRpc3RyaWJ1dGVkISAKXFtcbGVmdC5cYmVnaW57Ym1hdHJpeH0KXG1hdGhiZntYfV90XFwKXG1hdGhiZnt5fV90ClxlbmR7Ym1hdHJpeH0gXHJpZ2h0XHZlcnQgXG1hdGhiZntZfV97dC0xfSAgXHNpbSBNVk5cbGVmdChcYmVnaW57Ym1hdHJpeH0KXG1hdGhiZntYfV97dFx2ZXJ0IHQtMX1cXApcbWF0aGJme0F9X3t0fVxtYXRoYmZ7WH1fe3RcdmVydCB0LTF9ClxlbmR7Ym1hdHJpeH0sIFxiZWdpbntibWF0cml4fQpcbWF0aGJme1B9X3t0XHZlcnQgdC0xfSZcbWF0aGJme1B9X3t0XHZlcnQgdC0xfVxtYXRoYmZ7QX1fe3R9XlRcXApcbWF0aGJme0F9X3t0fSBcbWF0aGJme1B9X3t0XHZlcnQgdC0xfSZcYm9sZHN5bWJvbHtcU2lnbWF9X3QKXGVuZHtibWF0cml4fVxyaWdodCkKXF0KCldpdGggCgpcW1xiZWdpbnthcnJheX17bGNsfQpcYm9sZHN5bWJvbHtcU2lnbWF9X3Q9XHRleHR7VmFyfVxsZWZ0W1xtYXRoYmZ7eX1fdFx2ZXJ0IFxtYXRoYmZ7WX1fe3QtMX1ccmlnaHRdJj0mXHRleHR7VmFyfVxsZWZ0W1xtYXRoYmZ7QX1fdFxtYXRoYmZ7WH1fdCtcbWF0aGJme3Z9X3RcdmVydFxtYXRoYmZ7WX1fe3QtMX1ccmlnaHRdXFwmPSZcbWF0aGJme0F9X3RcdGV4dHtWYXJ9XGxlZnRbXG1hdGhiZntYfV90XHZlcnQgXG1hdGhiZntZfV97dC0xfVxyaWdodF1cbWF0aGJme0F9X3ReVCtcdGV4dHtWYXJ9XGxlZnRbXG1hdGhiZnt2fV90XHJpZ2h0XVxcCiY9JlxtYXRoYmZ7QX1fdCBcbWF0aGJme1B9X3t0XHZlcnQgdC0xfSBcbWF0aGJme0F9X3ReVCArIFxtYXRoYmZ7Un1fdApcZW5ke2FycmF5fVxdCgoKSXQgY2FuIGJlIHNob3duIHRoYXQgJFxtYXRoYmZ7WH1fdFx2ZXJ0IFxtYXRoYmZ7WX1fdCQgaXMgdGhlIEJlc3QgTGluZWFyIFVuYmlhc2VkIFByZWRpY3RvciBCTFVQIGJ5IGV4cGxvaXRpbmcgdGhlIGNvbmRpdGlvbmFsIGV4cGVjdGF0aW9uIGZvciBqb2ludGx5IG11bHRpdmFyaWF0ZSBub3JtYWwgZGF0YSAoU2VlIFt3aWtpcGVkaWFdKGh0dHBzOi8vZW4ud2lraXBlZGlhLm9yZy93aWtpL011bHRpdmFyaWF0ZV9ub3JtYWxfZGlzdHJpYnV0aW9uI0NvbmRpdGlvbmFsX2Rpc3RyaWJ1dGlvbnMpCikuCgpcWwpcYmVnaW57YXJyYXl9e3JjbH0KXHRleHR7RX1cbGVmdFtcbWF0aGJme1h9X3RcdmVydFxtYXRoYmZ7WX1fe3QtMX0sXG1hdGhiZnt5fV90XHJpZ2h0XQomPSZcdGV4dHtFfVxsZWZ0W1xtYXRoYmZ7WH1fdFx2ZXJ0IFxtYXRoYmZ7WX1fe3QtMX1ccmlnaHRdK1x0ZXh0e0Nvdn1cbGVmdFtcbWF0aGJme1h9X3QsXG1hdGhiZnt5fV90XHZlcnRcbWF0aGJme1l9X3t0LTF9XHJpZ2h0XVx0ZXh0e1Zhcn1cbGVmdFtcbWF0aGJme3l9X3RcdmVydFxtYXRoYmZ7WX1fe3QtMX1ccmlnaHRdXnstMX1cbGVmdChcbWF0aGJme3l9X3QtXG1hdGhiZntBfV90XG1hdGhiZntYfV97dFx2ZXJ0IHQtMQp9XHJpZ2h0KSxcXApcbWF0aGJme1h9X3t0XHZlcnQgdH0mPSZcbWF0aGJme1h9X3t0XHZlcnQgdC0xfStcbWF0aGJme1B9X3t0XHZlcnQgdC0xfSBcbWF0aGJme0F9X3ReVFxib2xkc3ltYm9se1xTaWdtYX1fdF57LTF9XGxlZnQoXG1hdGhiZnt5fV90LVxtYXRoYmZ7QX1fdFxtYXRoYmZ7WH1fe3RcdmVydCB0LTEKfVxyaWdodCksClxlbmR7YXJyYXl9ClxdCgphbmQKClxbClxiZWdpbnthcnJheX17cmNsfQpcdGV4dHtWYXJ9XGxlZnRbXG1hdGhiZntYfV90XHZlcnRcbWF0aGJme1l9X3t0LTF9LFxtYXRoYmZ7eX1fdFxyaWdodF0mPSZcdGV4dHtWYXJ9XGxlZnRbXG1hdGhiZntYfV90XHZlcnRcbWF0aGJme1l9X3t0LTF9XHJpZ2h0XS1cdGV4dHtDb3Z9XGxlZnRbXG1hdGhiZntYfV90LFxtYXRoYmZ7eX1fdFx2ZXJ0XG1hdGhiZntZfV97dC0xfVxyaWdodF1cdGV4dHtWYXJ9XGxlZnRbXG1hdGhiZnt5fV90XHZlcnRcbWF0aGJme1l9X3t0LTF9XHJpZ2h0XV57LTF9XHRleHR7Q292fVxsZWZ0W1xtYXRoYmZ7WH1fdCxcbWF0aGJme3l9X3RcdmVydFxtYXRoYmZ7WX1fe3QtMX1ccmlnaHRdXlRcXApcbWF0aGJme1B9X3t0XHZlcnQgdH0mPSZcbWF0aGJme1B9X3t0XHZlcnQgdC0xfS1cbWF0aGJme1B9X3t0XHZlcnQgdC0xfSBcbWF0aGJme0F9X3ReVCBcYm9sZHN5bWJvbHtcU2lnbWF9X3Reey0xfSBcbWF0aGJme0F9X3QgXG1hdGhiZntQfV97dFx2ZXJ0IHQtMX0sClxlbmR7YXJyYXl9ClxdCgpOb3RlLCB0aGF0IHByZWRpY3Rpb24gZXJyb3IgLyBpbm5vdmF0aW9uOiBcW1xib2xkc3ltYm9se1xlcHNpbG9ufV90ID1cbWF0aGJme3l9X3QtXG1hdGhiZntBfV90XG1hdGhiZntYfV97dFx2ZXJ0IHQtMX1cXQoKLSAkXGJvbGRzeW1ib2x7XGVwc2lsb259X3RcIFxwZXJwIFwhXCFcISBccGVycCBcIFxtYXRoYmZ7eX1fciQgZm9yIGFsbCAkcj0xLFxsZG90cyx0LTEkCi0gXFtcYm9sZHN5bWJvbHtcZXBzaWxvbn1fdCBcc2ltIFx0ZXh0e01WTn1cbGVmdChcbWF0aGJmezB9LFxib2xkc3ltYm9se1xTaWdtYQp9X3RccmlnaHQpXF0KLSBpbm5vdmF0aW9ucyBhcmUga2V5IHRvIHVzZSBLYWxtYW4gRmlsdGVyIGZvciBkZWNvbXBvc2l0aW9uIG9mIHRoZSBsaWtlbGlob29kLgoKCgotLS0KCjxiciAvPgoKIyMjIyBTdW1tYXJ5IEthbG1hbiBGaWx0ZXIKCklmICRcbWF0aGJme1h9X3swXHZlcnQgMH0kIGFuZCAkXG1hdGhiZntQfV97MFx2ZXJ0IDB9JCBhcmUga25vd24sIHRoZW4gdGhlIEthbG1hbiBmaWx0ZXIgYmVjb21lczoKCmZvciAkdD0xLFxsZG90cywgbiQgICAKCjEuIFByZWRpY3Rpb24gc3RlcAoKXFsKXGJlZ2lue2FycmF5fXtyY2x9ClxtYXRoYmZ7WH1fe3RcdmVydCB0LTF9Jj0mXGJvbGRzeW1ib2x7XFBoaX1fdCBcbWF0aGJme1h9X3t0LTFcdmVydCB0LTF9IFxcXFwKXG1hdGhiZntQfV97dFx2ZXJ0IHQtMX0mPSZcYm9sZHN5bWJvbHtcUGhpfV90IFxtYXRoYmZ7UH1fe3QtMVx2ZXJ0IHQtMX0gXGJvbGRzeW1ib2x7XFBoaX1fdF5UICsgXG1hdGhiZntRfV90LgpcZW5ke2FycmF5fQpcXQoKMi4gVXBkYXRlIHN0ZXAKXFsKXGJlZ2lue2FycmF5fXtyY2x9ClxtYXRoYmZ7WH1fe3RcdmVydCB0fSY9JlxtYXRoYmZ7WH1fe3RcdmVydCB0LTF9KyBcbWF0aGJme1B9X3t0XHZlcnQgdC0xfSAgXG1hdGhiZntBfV90XlQgXGJvbGRzeW1ib2x7XFNpZ21hfV90XnstMX0gKFxtYXRoYmZ7eX1fdCAtIFxtYXRoYmZ7QX1fdCBcbWF0aGJme1h9X3t0XHZlcnQgdC0xfSApXFxcXApcbWF0aGJme1B9X3t0XHZlcnQgdH0mPSZcbWF0aGJme1B9X3t0XHZlcnQgdC0xfS1cbWF0aGJme1B9X3t0XHZlcnQgdC0xfSBcbWF0aGJme0F9X3ReVCBcYm9sZHN5bWJvbHtcU2lnbWF9X3Reey0xfSBcbWF0aGJme0F9X3QgXG1hdGhiZntQfV97dFx2ZXJ0IHQtMX1cXFxcClxib2xkc3ltYm9se1xTaWdtYX1fdCY9JlxtYXRoYmZ7QX1fdCBcbWF0aGJme1B9X3t0XHZlcnQgdC0xfSBcbWF0aGJme0F9X3ReVCArIFxtYXRoYmZ7Un1fdC4KXGVuZHthcnJheX0KXF0KCi0tLQoKPGJyIC8+CgojIyMgVW5kZXIgdGhlIGhvb2Qgb2YgdGhlIEthbG1hbiBTbW9vdGhlciAKCi0gJFx0ZXh0e0V9XGxlZnRbXG1hdGhiZntYfV90XHZlcnQgXG1hdGhiZntZfV9OXHJpZ2h0XSQKCi0gU3RhcnQgd2l0aCB0aGUgZmluYWwgcXVhbnRpdGllcywgJFxtYXRoYmZ7WH1fe25cdmVydCBufSQgYW5kICRcbWF0aGJme1B9X3tuXHZlcnQgbn0kIGFuZCBwcm9jZWVkcyBiYWNrd2FyZHMuCgotIEZvciAkdD1uLTEsIFxsZG90cywgMCQsIGl0IGNvbnNpc3RzIG9mIHRoZSBmb2xsb3dpbmcgYmFja3dhcmQgcmVjdXJzaW9ucyAKClxiZWdpbnthbGlnbn0KJlxtYXRoYmZ7WH1fe3RcdmVydCBufT1cbWF0aGJme1h9X3t0XHZlcnQgdH0rXG1hdGhiZntKfV90KFxtYXRoYmZ7WH1fe3QrMVx2ZXJ0IG59LVxtYXRoYmZ7WH1fe3QrMVx2ZXJ0IHR9KSBcbGFiZWx7U1NfQjF9IFxcCiZcbWF0aGJme1B9X3t0XHZlcnQgbn09XG1hdGhiZntQfV97dFx2ZXJ0IHR9K1xtYXRoYmZ7Sn1fe3R9KFxtYXRoYmZ7UH1fe3QrMVx2ZXJ0IG59LVxtYXRoYmZ7UH1fe3QrMVx2ZXJ0IHR9KVxtYXRoYmZ7Sn1fdF57VH0gXGxhYmVse1NTX0IyfVxcCiZcbWF0aGJme0p9X3Q9XG1hdGhiZntQfV97dFx2ZXJ0IHR9XFBoaV97dCsxfV5UXG1hdGhiZntQfV97dCsxXHZlcnQgdH1eey0xfS4gXGxhYmVse1NTX0IzfQpcZW5ke2FsaWdufQoKCmBgYHtyLCBhbmltYXRpb24uaG9vaz0iZ2lmc2tpIiwgbWVzc2FnZSA9IEZBTFNFLCByZXN1bHRzPSJoaWRlIiwgaW50ZXJ2YWwgPSA0LCBlY2hvID0gRkFMU0V9CnBsb3RLRmlsdGVyUHJlZGljdGlvbgpwbG90S0ZpbHRlcgpwbG90S1Ntb290aGVyCmBgYAoKLS0tCgo8YnIgLz4KCiMjIFBhcmFtZXRlciBFc3RpbWF0aW9uCgojIyMgTGlrZWxpaG9vZCBhbmQgdGhlIHByZWRpY3Rpb24gZXJyb3IgZGVjb21wb3NpdGlvbgoKQ2xhc3NpY2FsIGlpZCBzZXR0aW5nLCAKXGJlZ2lue2VxdWF0aW9uKn0KXGxvZyBMX3tcbWF0aGJme1lfTn19KFxib2xkc3ltYm9se1xQc2l9KT1cc3VtXGxpbWl0c197dD0xfV5uIFxsb2cgcChcbWF0aGJme3lfdH0pLCAKXGVuZHtlcXVhdGlvbip9Ck5vdCBwb3NzaWJsZSBmb3IgZGVwZW5kZW50IG9ic2VydmF0aW9ucy4KU3RhdGUtc3BhY2UgbW9kZWwgYWxsb3dzIGZhY3RvcmlzYXRpb24gdXNpbmcgY29uZGl0aW9uYWwgZGVuc2l0eSBmdW5jdGlvbnMKXGJlZ2lue2VxdWF0aW9uKn0KXGxvZyBMX3tcbWF0aGJme1l9X059KFxib2xkc3ltYm9se1xQc2l9KT1cc3VtXGxpbWl0c197dD0xfV5uXGxvZyBwKFxtYXRoYmZ7eX1fdFx2ZXJ0IFxtYXRoYmZ7WX1fe3QtMX0pClxlbmR7ZXF1YXRpb24qfQpcW1xtYXRoYmZ7eX1fdFx2ZXJ0XG1hdGhiZntZfV97dC0xfVxzaW0gTVZOKFxtYXRoYmZ7QX1fdCBcbWF0aGJme1h9X3t0XHZlcnQgdC0xfSxcYm9sZHN5bWJvbHtcU2lnbWF9X3QpXF0KXGJlZ2lue2VxdWF0aW9uKn0KXGxvZyBMX3tcbWF0aGJme1l9X059KFxib2xkc3ltYm9se1xQc2l9KT0tXGZyYWN7cG59ezJ9XGxvZyAyIFxwaSAtIFxmcmFjezF9ezJ9IFxzdW1cbGltaXRzX3t0PTF9Xm4gXGxvZyBcdmVydCBcYm9sZHN5bWJvbHtcU2lnbWF9X3RcdmVydCAtXGZyYWN7MX17Mn0gXHN1bVxsaW1pdHNfe3Q9MX1ebiBcYm9sZHN5bWJvbHtcZXBzaWxvbn1fdF5UIFxib2xkc3ltYm9se1xTaWdtYX1fdF57LTF9IFxib2xkc3ltYm9se1xlcHNpbG9ufV90LAogXGVuZHtlcXVhdGlvbip9CgpXaGljaCBjYW4gYmUgZWZmaWNpZW50bHkgY2FsY3VsYXRlZCBieSB0aGUgS2FsbWFuIGZpbHRlciBhbmQgY2FuIHRodXMgYmUgdXNlZCB0byBlc3RpbWF0ZSB0aGUgbW9kZWwgcGFyYW1ldGVycy4gCgotLS0KCjxiciAvPgoKIyMjIE1heGltdW0gTGlrZWxpaG9vZAoKLSBOdW1lcmljYWwgb3B0aW1pc2F0aW9uIG9mIGxpa2VsaWhvb2QgKG9mdGVuIHRyb3VibGVzb21lKTogCiAgICAxLiBDaG9vc2UgaW5pdGlhbCB2YWx1ZXMgZm9yIHRoZSBwYXJhbWV0ZXJzCiAgICAyLiBSdW4gS2FsbWFuIEZpbHRlciBhbmQgY2FsY3VsYXRlIGxpa2VsaWhvb2QgCiAgICAzLiBSdW4gb25lIGl0ZXJhdGlvbiBvZiBOZXd0b24tUmFwaHNvbiB3aXRoIHRoZSAtMiBsb2cgbGlrZWxpaG9vZCBhcyBjcml0ZXJpb24gCiAgICA0LiBSZXBlYXQgc3RlcHMgMi0zIHdpdGggdGhlIGN1cnJlbnQgTmV3dG9uLVJhcGhzb24gcGFyYW1ldGVyIGVzdGltYXRlcyB1bnRpbCBjb252ZXJnZW5jZS4gCiAgICAKPGJyIC8+IAoKLSBGb3IgdGltZSBpbnZhcmlhbnQgc3lzdGVtOiBFTSBhbGdvcml0aG0gKG9mdGVuIHNsb3cpCiAgICAKLS0tCgo8YnIgLz4KCiMjIyBFTSBhbGdvcml0aG0gCgojIyMjIENvbXBsZXRlIGxpa2VsaWhvb2Qgb2YgJFxtYXRoYmZ7WH0kIGFuZCAkXG1hdGhiZntZfSQKClxiZWdpbnttdWx0bGluZSp9Clxsb2cgTF97XG1hdGhiZntZfSxcbWF0aGJme1h9fShcYm9sZHN5bWJvbHtcUHNpfSkgXHNpbSAtXGZyYWN7MX17Mn0gXGxvZ3xcYm9sZHN5bWJvbHtcU2lnbWF9X3swfXwgLVxmcmFjezF9ezJ9IChcbWF0aGJme1h9XzAtXGJvbGRzeW1ib2x7XG11fV8wKV5UIFxib2xkc3ltYm9se1xTaWdtYX1fezB9XnstMX0gKFxtYXRoYmZ7WH1fMC1cYm9sZHN5bWJvbHtcbXV9XzApXFwKICAgIC1cZnJhY3tufXsyfSBcbG9nIHxcbWF0aGJme1F9fC1cZnJhY3sxfXsyfVxzdW1fe3Q9MX1ebiAoXG1hdGhiZntYfV90LVxib2xkc3ltYm9se1xQaGkgWH1fe3QtMX0pXlQgXG1hdGhiZntRfV57LTF9KFxtYXRoYmZ7WH1fdC1cYm9sZHN5bWJvbHtcUGhpIFh9X3t0LTF9KVxcICAgIAogICAgLVxmcmFje259ezJ9IFxsb2d8XG1hdGhiZntSfXwgLVxmcmFjezF9ezJ9IFxzdW1fe3Q9MX1ebiAoXG1hdGhiZntZfV90LVxtYXRoYmZ7QX1fdFxtYXRoYmZ7WH1fdCleVFxtYXRoYmZ7Un1eey0xfShcbWF0aGJme1l9X3QtXG1hdGhiZntBfV90XG1hdGhiZntYfV90KSwKXGVuZHttdWx0bGluZSp9CgotIENhbm5vdCBiZSBjYWxjdWxhdGVkOiBzdGF0ZSBpcyB1bm9ic2VydmFibGUKCi0tLQoKPGJyIC8+CgojIyMjIEVNIGFsZ29yaXRobQoKMS4gRS1zdGVwOiAkUShcYm9sZHN5bWJvbHtcUHNpfSwgXGJvbGRzeW1ib2x7XFBzaX1eaykgPSBcdGV4dHtFfVxsZWZ0Wy0yXGxvZyBMX3tcbWF0aGJme1l9LFxtYXRoYmZ7WH19KFxib2xkc3ltYm9se1xQc2l9KSBcdmVydCBcbWF0aGJme1l9X04sIFxib2xkc3ltYm9se1xQc2l9XmtccmlnaHRdJAoKMi4gTS1zdGVwOiBNaW5pbWlzZSAkUShcYm9sZHN5bWJvbHtcUHNpfSwgXGJvbGRzeW1ib2x7XFBzaX1eaykkIHRvIG9idGFpbiB0aGUgdXBkYXRlIG9mIHRoZSBwYXJhbWV0ZXIgc2V0ICRcYm9sZHN5bWJvbHtcUHNpfV57aysxfSQuCgozLiBSZXBlYXRlIDEtMiB1bnRpbCBjb252ZXJnZW5jZQoKQmVmb3JlIHdlIGRlcml2ZSB0aGUgYWxnb3JpdGhtIHdlIGZpcnN0IGludHJvZHVjZSB0aGUgbGFnIG9uZSBjb3ZhcmlhbmNlIGVzdGltYXRvcnMgCgpcW1xtYXRoYmZ7UH1fe3QsdC0xXHZlcnQgcn09XHRleHR7Y292fVxsZWZ0W1xtYXRoYmZ7WH1fdCxcbWF0aGJme1h9X3t0LTF9XHZlcnQgXG1hdGhiZntZfV9yXHJpZ2h0XVxdLgoKLSBGaWx0ZXJlZCB2YWx1ZXMgClxiZWdpbntlcXVhdGlvbn0KXG1hdGhiZntQfV97dCx0LTFcdmVydCB0fT0oXG1hdGhiZntJfS1cbWF0aGJme1B9X3t0XHZlcnQgdC0xfVxtYXRoYmZ7QX1fdF5UXGJvbGRzeW1ib2x7XFNpZ21hfV90XnstMX1cbWF0aGJme0F9X3QpXGJvbGRzeW1ib2x7XFBoaX1fdFxtYXRoYmZ7UH1fe3QtMVx2ZXJ0IHQtMX0sIFxsYWJlbHtTU19GNn0KXGVuZHtlcXVhdGlvbn0KCi0gc21vb3RoZWQgdmFsdWVzIApcYmVnaW57ZXF1YXRpb259ClxtYXRoYmZ7UH1fe3QsdC0xXHZlcnQgbn09XG1hdGhiZntQfV97dCx0LTFcdmVydCB0fSsoXG1hdGhiZntQfV97dFx2ZXJ0IG59LVxtYXRoYmZ7UH1fe3RcdmVydCB0fSlcbWF0aGJme1B9X3t0XHZlcnQgdH1eey0xfVxtYXRoYmZ7UH1fe3QsdC0xXHZlcnQgdH0uIFxsYWJlbHtTU19CNH0KXGVuZHtlcXVhdGlvbn0KCi0tLQoKPGJyIC8+CgojIyMjIEVNLWFsZ29yaXRobTogRS1zdGVwCgpcYmVnaW57bXVsdGxpbmUqfQpRKFxib2xkc3ltYm9se1xQc2l9LCBcYm9sZHN5bWJvbHtcUHNpfV5rKSBcc2ltIFx0ZXh0e0V9XGxlZnRbIFxsb2d8XGJvbGRzeW1ib2x7XFNpZ21hfV97MH18ICsgKFxtYXRoYmZ7WH1fMC1cYm9sZHN5bWJvbHtcbXV9X3swfSleVCBcYm9sZHN5bWJvbHtcU2lnbWF9X3swfV57LTF9IChcbWF0aGJme1h9XzAtXGJvbGRzeW1ib2x7XG11fV97MH0pXHZlcnQgXG1hdGhiZntZfV9OLCBcYm9sZHN5bWJvbHtcUHNpfV5rXHJpZ2h0XVxcXFwKICAgICtcdGV4dHtFfVxsZWZ0W24gXGxvZyB8XG1hdGhiZntRfXwrXHN1bV97dD0xfV5uIChcbWF0aGJme1h9X3QtXGJvbGRzeW1ib2x7XFBoaSBYfV97dC0xfSleVCBcbWF0aGJme1F9XnstMX0oXG1hdGhiZntYfV90LVxib2xkc3ltYm9se1xQaGkgWH1fe3QtMX0pXHZlcnQgXG1hdGhiZntZfV9OLCBcYm9sZHN5bWJvbHtcUHNpfV5rXHJpZ2h0XVxcXFwgICAgCiAgICArXHRleHR7RX1cbGVmdFtuIFxsb2d8XG1hdGhiZntSfXwgKyBcc3VtX3t0PTF9Xm4gKFxtYXRoYmZ7WX1fdC1cbWF0aGJme0F9X3RcbWF0aGJme1h9X3QpXlRcbWF0aGJme1J9XnstMX0oXG1hdGhiZntZfV90LVxtYXRoYmZ7QX1fdFxtYXRoYmZ7WH1fdClcdmVydCBcbWF0aGJme1l9X04sIFxib2xkc3ltYm9se1xQc2l9XmtccmlnaHRdClxlbmR7bXVsdGxpbmUqfQoKXFsgXHF1YWQgXF0KClxiZWdpbnttdWx0bGluZSp9ClEoXGJvbGRzeW1ib2x7XFBzaX0sIFxib2xkc3ltYm9se1xQc2l9XmspXHNpbSBcbG9nfFxib2xkc3ltYm9se1xTaWdtYX1fezB9fCArIFx0ZXh0e3RyfVxsZWZ0WwpcYm9sZHN5bWJvbHtcU2lnbWF9X3swfV57LTF9IFxsZWZ0IFxsYnJhY2UgXG1hdGhiZntQfV97MFx2ZXJ0IG59ICsgKFxtYXRoYmZ7WH1fezBcdmVydCBufS0gXGJvbGRzeW1ib2x7XG11fV97MH0pKFxtYXRoYmZ7WH1fezBcdmVydCBufS0gXGJvbGRzeW1ib2x7XG11fV97MH0pXlRccmlnaHRccmJyYWNlXHJpZ2h0XVxcXFwKK24gXGxvZyB8XG1hdGhiZntRfXwgKyBcdGV4dHt0cn1cbGVmdFsgXG1hdGhiZntRXnstMX19XGxlZnRcbGJyYWNlXG1hdGhiZntTfV97MTF9IC0gXG1hdGhiZntTfV97MTB9XGJvbGRzeW1ib2x7XFBoaX1eVC1cYm9sZHN5bWJvbHtcUGhpfSBcbWF0aGJme1N9X3sxMH1eVCtcYm9sZHN5bWJvbHtcUGhpfSBcbWF0aGJme1N9X3swMH0gXGJvbGRzeW1ib2x7XFBoaX1eVFxyaWdodFxyYnJhY2VccmlnaHRdXFxcXCtuXGxvZyB8XG1hdGhiZntSfXwKKyBcdGV4dHt0cn1cbGVmdFtcbWF0aGJme1Jeey0xfX1cc3VtXGxpbWl0c197dD0xfV5uIFxsZWZ0XGxicmFjZShcbWF0aGJme3l9X3QtXG1hdGhiZntBfV90IFxtYXRoYmZ7WH1fe3RcdmVydCBufSkoXG1hdGhiZnt5fV90LVxtYXRoYmZ7QX1fdFxtYXRoYmZ7WH1fe3RcdmVydCBufSleVCtcbWF0aGJme0F9X3RcbWF0aGJme1B9X3t0XHZlcnQgbn0gXG1hdGhiZntBfV90XlRccmlnaHRccmJyYWNlXHJpZ2h0XSwKXGVuZHttdWx0bGluZSp9Cgp3aXRoIApcYmVnaW57YWxpZ24qfQpcbWF0aGJme1N9X3sxMX0mPVxzdW1cbGltaXRzX3t0PTF9Xm4gKFxtYXRoYmZ7WH1fe3RcdmVydCBufSBcbWF0aGJme1h9X3t0XHZlcnQgbn1eVCArIFxtYXRoYmZ7UH1fe3RcdmVydCBufSlcXApcbWF0aGJme1N9X3sxMH0mPVxzdW1cbGltaXRzX3t0PTF9Xm4gKFxtYXRoYmZ7WH1fe3RcdmVydCBufVxtYXRoYmZ7WH1fe3QtMVx2ZXJ0IG59XlQgKyBcbWF0aGJme1B9X3t0LHQtMVx2ZXJ0IG59KVxcClxtYXRoYmZ7U31fezAwfSY9XHN1bVxsaW1pdHNfe3Q9MX1ebiAoXG1hdGhiZntYfV97dC0xXHZlcnQgbn0gXG1hdGhiZntYfV97dC0xXHZlcnQgbn1eVCArIFxtYXRoYmZ7UH1fe3QtMVx2ZXJ0IG59KVxcClxlbmR7YWxpZ24qfQoKCi0tLQoKPGJyIC8+CgoKIyMjIyBFTSBhbGdvcml0aG06IE0tc3RlcCBtaW5pbWlzZSAkUShcYm9sZHN5bWJvbHtcUHNpfSwgXGJvbGRzeW1ib2x7XFBzaX1eaykkCgpcYmVnaW57YWxpZ24qfQpcYm9sZHN5bWJvbHtcUGhpfV57aysxfT0mXG1hdGhiZntTfV97MTB9XG1hdGhiZntTfV97MDB9XnstMX1cXFxcClxtYXRoYmZ7UX1ee2srMX09JiBuXnstMX0oXG1hdGhiZntTfV97MTF9LVxtYXRoYmZ7U31fezEwfVxtYXRoYmZ7U31fezAwfV57LTF9XG1hdGhiZntTfV97MTB9XlQpLFxcXFwKXG1hdGhiZntSfV57aysxfT0mbl57LTF9XHN1bVxsaW1pdHNfe3Q9MX1ebiBcbGVmdFsoXG1hdGhiZnt5fV90LVxtYXRoYmZ7QX1fdCBcbWF0aGJme1h9X3t0XHZlcnQgbn0pKFxtYXRoYmZ7eX1fdC1cbWF0aGJme0F9X3RcbWF0aGJme1h9X3t0XHZlcnQgbn0pXlQrXG1hdGhiZntBfV90XG1hdGhiZntQfV97dFx2ZXJ0IG59IFxtYXRoYmZ7QX1fdF5UXHJpZ2h0XVxcXFwKXGJvbGRzeW1ib2x7XG11fV8wXntrKzF9PSYgXG1hdGhiZntYfV97MFx2ZXJ0IG59XFxcXApcYm9sZHN5bWJvbHtcU2lnbWF9XzBee2srMX09JlxtYXRoYmZ7UH1fezBcdmVydCBufQpcZW5ke2FsaWduKn0KCi0tLQoKPGJyIC8+CgoKIyBFeHRlbnNpb25zCgoxLiBMaW5rIGJldHdlZW4gU3RhdGUgU3BhY2UgbW9kZWxzIGFuZCBjdWJpYyBzbW9vdGhpbmcgc3BsaW5lcwoyLiBNb3JlIGNvbXBsZXggdGVtcG9yYWwgc3RydWN0dXJlcwozLiBUaW1lIHZhcnlpbmcgY29lZmZpY2llbnQgbW9kZWwKNC4gTWlzc2luZyBkYXRhCjUuIEthbG1hbiBmaWx0ZXIgZm9yIEdlbmVyYWxpemVkIExlYXN0IFNxdWFyZXMgKEdMUykKNi4gQWR2YW5jZWQgdG9waWNzIAoKLS0tCgo8YnIgLz4KCiMjIExpbmsgd2l0aCBDdWJpYyBTbW9vdGhpbmcgU3BsaW5lcyAKCkJhc2ljIGlkZWEgb2Ygc21vb3RoaW5nIHNwbGluZXM6CgpcW3lfdCA9IFxtdV90ICsgdl90XF0KCndpdGggJHQgPSAxLC4uLixuLCQgYW5kICRcbXVfdCQgYSBzbW9vdGggZnVuY3Rpb24gb2YgdCwgYW5kICR2X3QkIGlzICROKDAsXHNpZ21hXjJfdikkLiAKCkluIGN1YmljIHNtb290aGluZyAkXG11X3QkIGlzIGVzdGltYXRlZCBieSBtaW5pbWl6aW5nCgpcWwpcc3VtXGxpbWl0c197dD0xfV5uIFt5X3QgLSBcbXVfdF1eMiArIFxsYW1iZGEgXGludFxsaW1pdHNfe3Q9MX1ebiBcbGVmdChcZnJhY3tkXjJcbXVfdH17ZHReMn1ccmlnaHQpXjIgZHQKXF0KCndpdGggcmVzcGVjdCB0byAkXG11X3QkICwgd2hlcmUgJFxsYW1iZGEgPiAwJCBpcyBhIHNtb290aGluZyBwYXJhbWV0ZXIuIAoKLSAkXGxhbWJkYSQgY29udHJvbHMgdGhlCmRlZ3JlZSBvZiBzbW9vdGhuZXNzLCB3aXRoIGxhcmdlciB2YWx1ZXMgeWllbGRpbmcgc21vb3RoZXIgZXN0aW1hdGVzLiAKCi0gSWYgJFxsYW1iZGEgPSAwJCwgdGhlbiBtaW5pbWl6ZXIgaXMgdGhlIGRhdGEgaXRzZWxmICRcaGF0IFxtdV90ID0geV90JCwgbm90IHNtb290aCEgICAKLSBJZiAkXGxhbWJkYSA9XGluZnR5JCAgd2lsbCBmb3JjZSB0aGUgc2Vjb25kIGRlcml2YXRpdmUgdG8gYmUsIHJlc3VsdGluZyBpbiBhIGxpbmVhciByZWdyZXNzaW9uIDogJFxtdV90ID0gXGJldGFfMCtcYmV0YSB0JAoKPGJyIC8+IAoKV2hlbiBrbm90cyBhcmUgcGxhY2VkIGF0IHRoZSB0aW1lIHBvaW50cyAkdCQsIGl0IGNhbiBiZSBzaG93biB0aGF0IHRoZSBsb3NzIGZ1bmN0aW9uIHJlZHVjZXMgdG8KClxbClxzdW1cbGltaXRzX3t0PTF9Xm4gW3lfdCAtIFxtdV90XV4yICsgXGxhbWJkYSBcc3VtXGxpbWl0c197dD0xfV5uICBcbGVmdChcYmlndHJpYW5nbGVkb3duXjIgXG11X3RccmlnaHQpXjIKXF0KCndpdGggJFxiaWd0cmlhbmdsZWRvd25eMiBcbXVfdCA9IFxtdV90IC0gMiBcbXVfe3QtMX0gKyBcbXVfe3QtMn0kIGZpbml0ZSBkaWZmZXJlbmNlcy4KCgotLS0KCjxiciAvPgoKCiMjIyBSZWNhc3RpbmcgaW4gYSBzdGF0ZSBzcGFjZSBtb2RlbCAKCgpcWwpcYmlndHJpYW5nbGVkb3duXjIgXG11X3QgPSB3X3QgXFwKXF0KCmFuZCByZXdyaXRpbmcgaXQgCgpcWwpcbXVfdCAgPSAgMiBcbXVfe3QtMX0gLSBcbXVfe3QtMn0gKyB3X3QgClxdCgplbmFibGUgdXMgdG8gcmVjYXN0IHRoZSBzcGxpbmUgaW4gYSBzdGF0ZSBzcGFjZSBtb2RlbDoKClxiZWdpbntlcW5hcnJheSp9ClxiZWdpbntibWF0cml4fQpcbXVfdFxcClxtdV97dC0xfQpcZW5ke2JtYXRyaXh9ICY9JiBcYmVnaW57Ym1hdHJpeH0gMiAmIC0gMVxcIDEgJiAwXGVuZHtibWF0cml4fSBcYmVnaW57Ym1hdHJpeH0gXG11X3t0LTF9IFxcIFxtdV97dC0yfSBcZW5ke2JtYXRyaXh9ICsgXGJlZ2lue2JtYXRyaXh9MVxcMFxlbmR7Ym1hdHJpeH0gd190XFwKeV90ICY9JiAgXGJlZ2lue2JtYXRyaXh9IDEmMFxlbmR7Ym1hdHJpeH1cYmVnaW57Ym1hdHJpeH0gXG11X3t0LTF9IFxcIFxtdV97dC0yfSBcZW5ke2JtYXRyaXh9ICsgdl90ClxlbmR7ZXFuYXJyYXkqfQoKLS0tCgo8YnIgLz4KCgojIyMgTWF4aW1pemUgY29tcGxldGUgZGF0YSBsaWtlbGlob29kIHRvIHRoZSBTdGF0ZXM6IApcWwpcbG9nIExfe1xtYXRoYmZ7WX0sXG1hdGhiZntYfX0oXGJvbGRzeW1ib2x7XFBzaX0pICBccHJvcHRvIFxzaWdtYV92XnstMn0gXHN1bVxsaW1pdHNfe3Q9MX1ebiBcbGVmdCh5X3QgLSBcbXVfdFxyaWdodCleMiArIFxzaWdtYV93XnstMn0gXHN1bVxsaW1pdHNfe3Q9MX1ebiBcbGVmdChcYmlndHJpYW5nbGVkb3duXjIgXG11X3RccmlnaHQpXjIgIApcXQoKaXMgZXF1aXZhbGVudCB0byBtYXhpbWl6aW5nOiAKClxbClxsb2cgTF97XG1hdGhiZntZfSxcbWF0aGJme1h9fShcYm9sZHN5bWJvbHtcUHNpfSkgIFxwcm9wdG8gIFxzdW1cbGltaXRzX3t0PTF9Xm4gXGxlZnQoeV90IC0gXG11X3RccmlnaHQpXjIgKyBcZnJhY3tcc2lnbWFfdl57Mn19e1xzaWdtYV93XnsyfX0gXHN1bVxsaW1pdHNfe3Q9MX1ebiBcbGVmdChcYmlndHJpYW5nbGVkb3duXjIgXG11X3RccmlnaHQpXjIgIApcXQoKLSBTbyB0aGUgU3RhdGUgU3BhY2UgbW9kZWwgaGFzIHRoZSBzYW1lIHNvbHV0aW9uIGFzIGEgc21vb3RoaW5nIHNwbGluZSB3aXRoIHBlbmFsdHkgJFxsYW1iZGEgPSBcZnJhY3tcc2lnbWFfdl57Mn19e1xzaWdtYV93XnsyfX0kIQoKLSBNZWFuICRcbGVmdHJpZ2h0YXJyb3ckIFZhcmlhbmNlCgotIExpbmsgbWl4ZWQgbW9kZWxzIGFuZCByaWRnZSByZWdyZXNzaW9uL3BlbmFsaXNhdGlvbgoKLS0tCgo8YnIgLz4KCgojIyMgRXhhbXBsZSAKCi0gV2Ugc2ltdWxhdGUgdGhlIHNpZ25hbCwgb3Igc3RhdGUgcHJvY2VzcywgJFxtdV90JCBhbmQgb2JzZXJ2YXRpb25zICR5X3QkIHdpdGggJG4gPSA1MCQsICRcc2lnbWFeMl9cb21lZ2EgPSAuMSQgYW5kICRcc2lnbWFeMl9cZXBzaWxvbiQgPSAxLgoKYGBge3J9CnNldC5zZWVkKDEyMykKbnVtID0gNTAKdyA9IHJub3JtKG51bSwwLC4xKQptdSAgID0gY3Vtc3VtKGN1bXN1bSh3KSkKCnkgPSBtdSArIHJub3JtKG51bSwwLDEpCmBgYAoKPGRldGFpbHM+PHN1bW1hcnk+Q2xpY2sgdG8gc2VlIGNvZGUgZm9yIHBsb3QgPC9zdW1tYXJ5PjxwPgpgYGB7cn0KcGxvdFNtb290aGVyUmVhbCA8LSBkYXRhLmZyYW1lKHQ9MTpudW0sbXU9bXUseT15KSAlPiUgCiAgZ2dwbG90KGFlcyh4PXQseT15KSkgKwogIGdlb21fcG9pbnQoKSArCiAgZ2VvbV9saW5lKGFlcyh4PXQseT1tdSkpCmBgYAo8L3A+PC9kZXRhaWxzPgoKYGBge3J9CnBsb3RTbW9vdGhlclJlYWwKYGBgCgojIyMjIFN0YXRlIHNwYWNlIG1vZGVsIAoKYGBge3J9CiMjIFN0YXRlIFNwYWNlICMjClBoaSA8LSBtYXRyaXgoYygyLDEsLTEsMCksMikgCkEgPC0gbWF0cml4KGMoMSwwKSwxKSAKbXUwIDwtIG1hdHJpeCgwLDIpClNpZ21hMCA8LSBkaWFnKDEsMikKYGBgCgojIyMjIEVzdGltYXRpb24KCkV4cGxhaW4gbWF4aW11bSBsaWtlbGlob29kIGltcGxlbWVudGF0aW9uLiAKCmBgYHtyIHJlc3VsdHM9ImhpZGUifQojIyBGaXR0aW5nIApQaGkgPC0gbWF0cml4KGMoMiwxLC0xLDApLDIpIApBIDwtIG1hdHJpeChjKDEsMCksMSkgCm11MCA8LSBtYXRyaXgoMCwyKSAKU2lnbWEwIDwtIGRpYWcoMSwyKQpMaW5uIDwtIGZ1bmN0aW9uKHBhcmEpewogIHNpZ3cgPC0gcGFyYVsxXQogIHNpZ3YgPC0gcGFyYVsyXQogIGNRICAgPC0gZGlhZyhjKHNpZ3csMCkpCiAga2YgICA8LSBLZmlsdGVyMChudW0sIHksIEEsIG11MCwgU2lnbWEwLCBQaGksIGNRLCBzaWd2KQogIHJldHVybihrZiRsaWtlKSAgIH0KCmluaXQucGFyIDwtIGMoLjEsIDEpCgplc3QgPC0gb3B0aW0oCiAgcGFyID0gaW5pdC5wYXIsIAogIGZuID0gTGlubiwgCiAgbWV0aG9kPSJCRkdTIiwgCiAgaGVzc2lhbj1UUlVFLAogIGNvbnRyb2w9bGlzdCh0cmFjZT0xLFJFUE9SVD0xKSkKYGBgCgojIyMjIFNtb290aGVyCgpgYGB7cn0Kc2lndyA8LSBlc3QkcGFyWzFdCmNRIDwtIGRpYWcoYyhzaWd3LDApKQpzaWd2IDwtIGVzdCRwYXJbMl0Ka3MgPC0gS3Ntb290aDAobnVtLCB5LCBBLCBtdTAsIFNpZ21hMCwgUGhpLCBjUSwgc2lndikKYGBgCgoKCjxkZXRhaWxzPjxzdW1tYXJ5PkNsaWNrIHRvIHNlZSBjb2RlIGZvciBwbG90IDwvc3VtbWFyeT48cD4KYGBge3J9CmRhdGFIbHAzIDwtIGRhdGEuZnJhbWUodCA9IDE6bnVtLCBtdUZpdCA9IGtzJHhzWzEsMSxdKSAlPiUgCiAgbXV0YXRlKAogICAgbXVsbCA9IG11Rml0IC0gMiAqIHNxcnQoa3MkUHNbMSwxLF0pLAogICAgbXV1bCA9IG11Rml0ICsgMiAqIHNxcnQoa3MkUHNbMSwxLF0pLAogICAgICAgICApCgpkYXRhSGxwNCA8LSBkYXRhLmZyYW1lKAogIHQ9cmVwKDE6bnVtLDMpLAogIG11PWMoc21vb3RoLnNwbGluZSh5KSR5LG11LGtzJHhzWzEsMSxdKSwKICBtZXRob2Q9cmVwKGMoIkdDViBzcGxpbmUiLCJHcm91bmQgVHJ1dGgiLCJLYWxtYW4gU21vb3RoZXIiKSxlYWNoPW51bSkpCgpjdWJpY1Ntb290aGVyUGxvdCA8LSBnZ3Bsb3QoKSArCiAgZ2VvbV9yaWJib24oCiAgICBhZXMoeD10LHltaW4gPSBtdWxsLHltYXggPSBtdXVsKSwgCiAgICBhbHBoYSA9IDAuNDUsIAogICAgY29sb3IgPSBOQSwKICAgIGRhdGEgPSBkYXRhSGxwMykgKwogIGdlb21fcG9pbnQoCiAgICBhZXMoeD14LHk9b2JzKSwKICAgIGNvbG9yPSJibGFjayIsIAogICAgZGF0YT1kYXRhLmZyYW1lKHg9MTpudW0sb2JzPXkpKSArCiAgZ2VvbV9saW5lKAogICAgYWVzKHggPSB0LCB5ID0gbXUsIGNvbG9yID0gbWV0aG9kLCBsaW5ldHlwZSA9IG1ldGhvZCksCiAgICBkYXRhID0gZGF0YUhscDQpICsKICBzY2FsZV9jb2xvcl9tYW51YWwodmFsdWVzID0gYygiYmx1ZSIsImJsYWNrIiwicmVkIikpICsKICBzY2FsZV9saW5ldHlwZV9tYW51YWwodmFsdWVzID0gYygyLDEsMSkpIApgYGAKPC9wPjwvZGV0YWlscz4KCmBgYHtyfQpjdWJpY1Ntb290aGVyUGxvdApgYGAKCi0tLQoKPGJyIC8+CgojIyBNb3JlIGNvbXBsZXggdGVtcG9yYWwgc3RydWN0dXJlcyAKCi0gRXh0ZW5kIHN0YXRlIHZlY3RvciB3aXRoIHN0YXRlcyBhdCBwcmV2aW91cyBsYWdzLCBpLmUuICR0LTEsIFxsZG90cywgdC1wJCAKCiBcW1xtYXRoYmZ7WH1fdD1bWF97MXR9IFxsZG90cyBYX3ttdH0sIFxsZG90cywgWF97MXQtcH1cbGRvdHMgWF97bXQtcH1dXlRcXQoKLSBCeSBlbWJlZGRpbmcgdGhlIEFSKHApIFN0YXRlIGluIHRoZSBtZWFzdXJlbWVudCBlcXVhdGlvbiwgdGhlIGNvcnJlbGF0aW9uIHN0cnVjdHVyZSBhZ2FpbiBiZWNvbWVzIG11Y2ggbW9yZSBmbGV4aWJsZS4gCgotLS0KCjxiciAvPgoKIyMgVGltZSBWYXJ5aW5nIENvZWZmaWNpZW50IE1vZGVsCgpSZWdyZXNzaW9uIG1vZGVscyB3aXRoIHRpbWUgdmFyeWluZyBtb2RlbCBwYXJhbWV0ZXJzIGNhbiBhbHNvIGJlIGVhc2lseSBwcm92aWRlZCBieSBleHRlbmRpbmcgdGhlIHN0YXRlIHZlY3RvciwgaS5lLiAKClxbXG1hdGhiZntYfV90PVtYX3sxdH0gXGxkb3RzIFhfe210fSxcbWF0aGJme1xiZXRhfV90XV5UXF0KCgojIyMgRXhhbXBsZSBIZWRnZSByYXRpbyBjcnlwdG8tY3VycmVuY2llcwoKPGRldGFpbHM+PHN1bW1hcnk+Q2xpY2sgdG8gc2VlIGNvZGUgZm9yIHBsb3QgPC9zdW1tYXJ5PjxwPgpgYGB7cn0KY29pbkRhdGEgPC0gY29pbkRhdGEgJT4lIAogIG11dGF0ZSgKICAgIHllYXIgPSBmb3JtYXQoZGF0ZSwiJVkiKSAlPiUgYXMuZmFjdG9yLAogICAgY29sb3JzID0gYXMuZG91YmxlKGRhdGUpLW1lYW4oYXMuZG91YmxlKGRhdGUpKQogICAgKSAKCnBsb3RIZWRnZSA8LSBjb2luRGF0YSAlPiUKICBnZ3Bsb3QoYWVzKGV0aF9wcmljZXNfdXNkLCBsdGNfcHJpY2VzX3VzZCwgY29sb3I9eWVhcikpICsKICBnZW9tX3BvaW50KHNpemUgPSAuNywgc2hhcGUgPSAxKSAKYGBgCjwvcD48L2RldGFpbHM+CgpgYGB7cn0KcGxvdEhlZGdlCmBgYAoKTm90ZSwgdGhhdCAKLSB0aGlzIGV4YW1wbGUgaXMgZm9yIGlsbHVzdHJhdGlvbiBwdXJwb3NlcyBvbmx5LiAKLSBJdCBzaG93LWNhc2VzIHRpbWUtdmFyeWluZyByZWdyZXNzaW9uIHdpdGggc3RhdGUgc3BhY2UgbW9kZWxzCi0gVGhlIGRhdGEgYXJlIG1vZGVsbGVkIG9uIHRoZSBvcmlnaW5hbCBwcmljZSBzY2FsZSBzbyB0aGUgZXJyb3JzIHdpbGwgcHJvYmFibHkgYmUgaGV0ZXJvc2NlZGFzdGljLiAKCiMjIyBEZWZpbmUgc3RhdGUgc3BhY2UgbW9kZWwgCgpcW3lfdCA9IFxiZXRhX3swdH0gKyBcYmV0YV97MXR9IHpfdCArIHZfdFxdIAoKCndpdGggCgotICR5X3QkOiBsdGMgcHJpY2UgYW5kIAotICR6X3QkOiBldGggcHJpY2UKClN0YXRlIHNwYWNlIG1vZGVsOgpcWwpcbGVmdFxsYnJhY2UKXGJlZ2lue2FycmF5fXtsbGxsfQp5X3QmPSZcbWF0aGJme0F9X3RcbWF0aGJme1h9X3Qrdl90ICZ3aXRoXCB2X3Rcc2ltIE4oMCxcc2lnbWFfdl4yKVxcClxtYXRoYmZ7WH1fdCY9Jlxib2xkc3ltYm9se1xQaGl9IFxtYXRoYmZ7WH1fe3QtMX0gKyBcbWF0aGJme3d9X3QgJndpdGhcIFxtYXRoYmZ7d31fdFxzaW0gTVZOKFxtYXRoYmZ7MH0sXG1hdGhiZntRfSkKXGVuZHthcnJheX0KXHJpZ2h0LlxdCgp3aXRoIAoKLSAkXG1hdGhiZntBfV90ID0gXGJlZ2lue2JtYXRyaXh9IDEgJiB6X3RcZW5ke2JtYXRyaXh9JAotICRcbWF0aGJme1h9X3Q9IFxiZWdpbntibWF0cml4fSBcYmV0YV97MHR9XFwgXGJldGFfezF0fSBcZW5ke2JtYXRyaXh9JAoKCiMjIyBSIGltcGxlbWVudGF0aW9uCgpgYGB7ciByZXN1bHRzPSJoaWRlIn0KeSA8LSBjb2luRGF0YSAlPiUgCiAgcHVsbCgibHRjX3ByaWNlc191c2QiKSAlPiUgCiAgYXMubWF0cml4KG5jb2w9MSkKCnogPC0gY29pbkRhdGEgJT4lIAogIHB1bGwoImV0aF9wcmljZXNfdXNkIikgCgpudW0gPC0gbnJvdyh5KQoKIyBNZWFzdXJlbWVudCBtYXRyaXggQSA9IApBIDwtIGFycmF5KDAsIGRpbT1jKDEsMixudW0pKSAgCkFbLDEsXSA8LSAxCkFbLDIsXSA8LSB6CgojIGluaXRpYWwgdmFsdWVzCm11MCA8LSBtYXRyaXgoYygwLDEpLDIsMSkKU2lnbWEwIDwtIGRpYWcoYyguMSwuMSksMikKUGhpIDwtIGRpYWcoMSwyKQpjUSA8LSBkaWFnKGMoLjEsLjEpLDIpCmNSIDwtIGRpYWcoYyguMSksMSkKCmVtIDwtIEVNMShudW0sIHksIEEsIG11MCwgU2lnbWEwLCBQaGksIGNRLCBjUiwgMTAwLCAuMDAxKQprcyA8LSBLc21vb3RoMShudW0sIHksIEEsIGVtJG11MCwgZW0kU2lnbWEwLCBlbSRQaGksIDAsIDAsIGNob2woZW0kUSksIGNob2woZW0kUiksIDApCmBgYAoKCjxkZXRhaWxzPjxzdW1tYXJ5PkNsaWNrIHRvIHNlZSBjb2RlIGZvciBwbG90IDwvc3VtbWFyeT48cD4KYGBge3J9CnBsb3RCZXRhc1ZhcnlpbmcgPC0gZGF0YS5mcmFtZSgKICAgIHRpbWU9Y29pbkRhdGEkZGF0ZSwgCiAgICBiZXRhPWMoa3MkeGZbMSwxLF0sIGtzJHhmWzIsMSxdKSwKICAgIHN0YXRlPWMocmVwKCJpbnRlcmNlcHQiLG5kYXlzKSwgcmVwKCJzbG9wZSIsbmRheXMpKQogICkgJT4lIAogIGdncGxvdChhZXModGltZSwgYmV0YSkpICsKICBnZW9tX2xpbmUoKSArIAogIGZhY2V0X3dyYXAofnN0YXRlLCBucm93PTIsIHNjYWxlcz0iZnJlZV95IikgKwogIHRoZW1lX21pbmltYWwoKQpgYGAKPC9wPjwvZGV0YWlscz4KCmBgYHtyfQpwbG90QmV0YXNWYXJ5aW5nCmBgYAoKPGRldGFpbHM+PHN1bW1hcnk+Q2xpY2sgdG8gc2VlIGNvZGUgZm9yIHBsb3QgPC9zdW1tYXJ5PjxwPgpgYGB7cn0KY29pbkRhdGEgPC0gY29pbkRhdGEgJT4lIAogIG11dGF0ZSgKICAgIGhlZGdlSW50ID0ga3MkeGZbMSwxLF0sCiAgICBoZWRnZVNscCA9IGtzJHhmWzIsMSxdICAKICAgICkKCnBsb3RIZWRnZSA8LSBsaXN0KCkKaTwtIDAKZm9yICh0IGluIHByZXR0eShjb2luRGF0YSRkYXRlLCAzNikpCiNmb3IgKHQgaW4gYygKIyAgc2VxKAojICAgIG1pbihjb2luRGF0YSRkYXRlKSwKIyAgICBhcy5EYXRlKCIyMDE5LTEyLTMxIiksCiMgICAgNyksCiMgIHNlcSgKIyAgICBhcy5EYXRlKCIyMDIxLTAxLTAxIiksCiMgICAgbWF4KGNvaW5EYXRhJGRhdGUpLAojICAgIDcpCiMgICkKIyAgKQp7CiAgaSA8LSBpKzEKICBwbG90SGVkZ2VbW2ldXSA8LSBjb2luRGF0YSAlPiUKICAgIGdncGxvdChhZXMoZXRoX3ByaWNlc191c2QsIGx0Y19wcmljZXNfdXNkLCBjb2xvcj15ZWFyKSkgKwogICAgZ2VvbV9wb2ludChzaXplID0gLjcsIHNoYXBlID0gMSkgKwogICAgZ2VvbV9wb2ludChhZXMoeD1ldGhfcHJpY2VzX3VzZCwgeSA9IGx0Y19wcmljZXNfdXNkLCBjb2xvciA9IHllYXIpLCBkYXRhID0gY29pbkRhdGEgJT4lIGZpbHRlcihkYXRlID09IHQpLCBzaGFwZSA9IDE3LCBzaXplID0gNCkgKwogICAgZ2VvbV9hYmxpbmUoYWVzKGludGVyY2VwdD1oZWRnZUludCwgc2xvcGU9aGVkZ2VTbHAsIGNvbG9yID0geWVhciksIGRhdGEgPSBjb2luRGF0YSAlPiUgZmlsdGVyKGRhdGUgPT0gdCkpCn0KYGBgCjwvcD48L2RldGFpbHM+CgpBbmltYXRpb24gaW5jbHVkZXMgcmVncmVzc2lvbiBsaW5lIGZvciBmaXJzdCBkYXkgb2YgZWFjaCBtb250aC4KCmBgYHtyLCBhbmltYXRpb24uaG9vaz0iZ2lmc2tpIiwgZWNobz1GQUxTRSwgbWVzc2FnZSA9IEZBTFNFLCByZXN1bHRzPSJoaWRlIiwgaW50ZXJ2YWwgPSAuNX0KZm9yIChrIGluIDE6NSkgZm9yIChpIGluIDE6MikgcHJpbnQocGxvdEhlZGdlW1trXV0pCmZvciAoayBpbiA2Omxlbmd0aChwbG90SGVkZ2UpKSBwcmludChwbG90SGVkZ2VbW2tdXSkKYGBgCgotLS0KCjxiciAvPgoKCiMjIE1pc3NpbmcgRGF0YSAKCiMjIyBEYXRhIAotIERhdGEgZnJvbSBKb25lcyAoMTk4NCkKLSBNZWFzdXJlbWVudHMgbWFkZSBmb3IgOTEgZGF5cyBvbiB0aHJlZSB2YXJpYWJsZXM6IAoKICAgIC0gJHlfMSQ6IGxvZyh3aGl0ZSBibG9vZCBjb3VudCkgW1dCQ10KICAgIC0gJHlfMiQ6IGxvZyhwbGF0ZWxldCkgW1BMVF0KICAgIC0gJHlfMyQ6IGhlbWF0b2NyaXQgW0hDVF0KCjxkZXRhaWxzPjxzdW1tYXJ5PkNsaWNrIHRvIHNlZSBjb2RlIGZvciBwbG90IDwvc3VtbWFyeT48cD4KYGBge3J9CmJsb29kVGlkeSA8LSBibG9vZCAlPiUgCiAgYXMuZGF0YS5mcmFtZSAlPiUKICBtdXRhdGUodD0xOm5yb3coYmxvb2QpKSAlPiUgCiAgZ2F0aGVyKHZhcmlhYmxlLCBjb25jZW50cmF0aW9uLCAtdCkKcGxvdEJsb29kIDwtIGJsb29kVGlkeSAlPiUgCiAgZ2dwbG90KGFlcyh0LGNvbmNlbnRyYXRpb24pKSArCiAgZ2VvbV9wb2ludCgpICsKICBmYWNldF93cmFwKH52YXJpYWJsZSxucm93PTMsc2NhbGVzPSJmcmVlX3kiKSArCiAgdGhlbWVfbWluaW1hbCgpCmBgYAo8L3A+PC9kZXRhaWxzPgoKYGBge3IgcmVzdWx0cz0iaGlkZSJ9CnBsb3RCbG9vZApgYGAKCi0gU29tZSBkYXRhIGFyZSBtaXNzaW5nIQoKIyMjIFNvbHV0aW9uICAgIAoKMS4gUmVwbGFjZSBtaXNzaW5nIGRhdGEgd2l0aCAwIGFuZCByZW1vdmUgdGhlIGNvcnJlbGF0aW9ucyB1c2UgCiAgICAKXFsKXG1hdGhiZnt5fV90PVxsZWZ0W1xtYXRoYmZ7eX1fdF57KG9icyl9IDBccmlnaHRdXlQKXF0KICAgIApcW1xtYXRoYmZ7Un1fdD1cYmVnaW57Ym1hdHJpeH1cbWF0aGJme1J9X3sxMXR9JiBcbWF0aGJmezB9XFxcbWF0aGJmezB9JlxtYXRoYmZ7Un1fezIydH1cZW5ke2JtYXRyaXh9XF0KClxbXG1hdGhiZntBfV90PVxiZWdpbntibWF0cml4fVxtYXRoYmZ7QX1eeyhvYnMpfVxcXG1hdGhiZnswfVxlbmR7Ym1hdHJpeH1cXQoKMi4gVGhlbiB5b3UgY2FuIGFwcGx5IHRoZSBleGlzdGluZyByZWN1cnNpb25zIGZvciB0aGUgS2FsbWFuIEZpbHRlciBhbmQgU21vb3RoZXIKICAgIAozLiBUaGUgRU0gYWxnb3JpdGhtIGlzIGRlc2lnbmVkIHRvIGhhbmRsZSBtaXNzaW5nIGRhdGEuCgojIyMgRXhhbXBsZQoKLSBEZWZpbmUgc3RhdGUgc3BhY2UgbW9kZWwgYW5kIGVzdGltYXRpb24gCgpcWwpcbGVmdFxsYnJhY2UKXGJlZ2lue2FycmF5fXtsbGxsfQpcbWF0aGJme3l9X3QmPSZcbWF0aGJme0F9X3RcbWF0aGJme1h9X3QrIFxtYXRoYmZ7dn1fdCAmd2l0aFwgXG1hdGhiZnt2fV90XHNpbSBOKDAsXG1hdGhiZntSfV90KVxcClxtYXRoYmZ7WH1fdCY9Jlxib2xkc3ltYm9se1xQaGl9IFxtYXRoYmZ7WH1fe3QtMX0gKyBcbWF0aGJme3d9X3QgJndpdGhcIFxtYXRoYmZ7d31fdFxzaW0gTVZOKFxtYXRoYmZ7MH0sXG1hdGhiZntRfSkKXGVuZHthcnJheX0KXHJpZ2h0LlxdCgoKd2l0aCAkXG1hdGhiZntBfT1cbWF0aGJme0l9JCAgCgoxLiBSZXBsYWNlIG1pc3NpbmcgdmFsdWVzIGJ5IDAKCmBgYHtyIHJlc3VsdHM9ImhpZGUifQp5IDwtIGJsb29kICU+JSBhcy5tYXRyaXgKeVtpcy5uYSh5KV0gPSAwCm51bSA8LSBucm93KHkpCmBgYAoKMi4gQ29uc3RydWN0IHRpbWUgdmFyaWFudCBtYXRyaXggQS4gCgpgYGB7ciByZXN1bHRzPSJoaWRlIn0KQSAgPC0gYXJyYXkoMCwgZGltPWMoMywzLG51bSkpCmZvcihrIGluIDE6bnVtKSB7IGlmICh5W2ssMV0gPiAwKSBBWywsa10gPC0gZGlhZygxLDMpIH0gIyAKYGBgCgoKMy4gSW5pdGFsaXNhdGlvbiBhbmQgZXN0aW1hdGlvbi4gTm90ZSwgdGhhdCB3ZSBkbyBub3QgaGF2ZSB0byBhZGp1c3QgY292YXJpYW5jZSBtYXRyaXggJFxtYXRoYmZ7Un0kIHRvIGFjY291bnQgZm9yIG1pc3NpbmduZXNzIGJlY2F1c2UgYWxsIG9ic2VydmF0aW9ucyBhcmUgbWVhc3VyZWQgb3IgbWlzc2luZyBhdCBhIHBhcnRpY3VsYXIgdGltZXN0ZXAuIAoKYGBge3IgcmVzdWx0cz0iaGlkZSJ9CiNJbml0aWFsIHZhbHVlcwptdTAgPC0gbWF0cml4KDAsIDMsIDEpClNpZ21hMCA8LSBkaWFnKGMoLjEsIC4xLCAxKSwgMykKUGhpIDwtIGRpYWcoMSwgMykKY1EgPC0gZGlhZyhjKC4xLCAuMSwgMSksIDMpIApjUiA8LSBkaWFnKGMoLjEsIC4xLCAxKSwgMykKCmVtIDwtIEVNMShudW0sIHksIEEsIG11MCwgU2lnbWEwLCBQaGksIGNRLCBjUiwgMTAwLCAuMDAxKQprcyA8LSBLc21vb3RoMShudW0sIHksIEEsIGVtJG11MCwgZW0kU2lnbWEwLCBlbSRQaGksIDAsIDAsIGNob2woZW0kUSksIGNob2woZW0kUiksIDApCmBgYAoKPGRldGFpbHM+PHN1bW1hcnk+Q2xpY2sgdG8gc2VlIGNvZGUgZm9yIHBsb3QgPC9zdW1tYXJ5PjxwPgpgYGB7cn0KYmxvb2RUaWR5IDwtIGJsb29kVGlkeSAlPiUgCiAgbXV0YXRlKAogICAgeHM9a3MkeHNbLDEsXSAlPiUgdCAlPiUgYywKICAgIHhzbGwgPSAoa3MkeHNbLDEsXSAtIDIgKiBzYXBwbHkoMTpudW0sZnVuY3Rpb24oaSkga3MkUHNbLCxpXSAlPiUgZGlhZyAlPiUgc3FydCkpICU+JSB0KC4pICU+JSBjLAogICAgeHN1bCA9IChrcyR4c1ssMSxdICsgMiAqIHNhcHBseSgxOm51bSxmdW5jdGlvbihpKSBrcyRQZlssLGldICU+JSBkaWFnICU+JSBzcXJ0KSkgJT4lIHQoLikgJT4lIGMKICAgICkKCnBsb3RCbG9vZFJlc3VsdHMgPC0gcGxvdEJsb29kICsKICBnZW9tX3JpYmJvbigKICAgIGFlcyh5bWluID0geHNsbCx5bWF4ID0geHN1bCksIAogICAgYWxwaGEgPSAwLjQ1LCAKICAgIGRhdGEgPSBibG9vZFRpZHkpICsKICBnZW9tX2xpbmUoYWVzKHg9dCx5PXhzKSxkYXRhPWJsb29kVGlkeSkgKwogIHhsYWIoInRpbWUgKGRheXMpIikgKwogIGdndGl0bGUoIkthbG1hbiBTbW9vdGhlciIpCmBgYAo8L3A+PC9kZXRhaWxzPgoKYGBge3J9CnBsb3RCbG9vZFJlc3VsdHMKYGBgCgotLS0KCjxiciAvPgoKCiMjIEthbG1hbiBGaWx0ZXIgZm9yIEdlbmVyYWxpemVkIExlYXN0IFNxdWFyZXMgKEdMUykKClxbClxsZWZ0XGxicmFjZQpcYmVnaW57YXJyYXl9e2xsbGx9ClxtYXRoYmZ7eX1fdCAmPSYgXG1hdGhiZntBfV90IFxtYXRoYmZ7WH1fdCArIFxtYXRoYmZ7Wn1fdFxib2xkc3ltYm9se1xiZXRhfSsgXG1hdGhiZnt2fV90XFwKXG1hdGhiZntYfV90ICY9Jlxib2xkc3ltYm9se1xQaGl9X3QgXG1hdGhiZntYfV97dC0xfSArIFxtYXRoYmZ7d31fdFxcClxlbmR7YXJyYXl9ClxyaWdodC4KXF0KClRoZSBzdGF0ZS1zcGFjZSBtb2RlbCBjYW4gYmUgZnVydGhlciByZWZvcm11bGF0ZWQgYXMgYSByZWdyZXNzaW9uIG1vZGVsClxbClxtYXRoYmZ7eX1fdD1cbWF0aGJme1p9X3RcYm9sZHN5bWJvbHtcYmV0YX0rIFxtYXRoYmZ7dX1fdCwKXHRleHR7IHdpdGggfSBcbWF0aGJme3V9X3Q9XG1hdGhiZntBfV90IFxtYXRoYmZ7WH1fdCArIFxtYXRoYmZ7dn1fdApcXQoKXGJlZ2lue2VxdWF0aW9ufQpcbWF0aGJme1l9X049XG1hdGhiZntafV9OIFxib2xkc3ltYm9se1xiZXRhfSArIFxtYXRoYmZ7VX1fTiwKXGxhYmVse21lYXN1cmVtZW50LnJlZy5nbHN9ClxlbmR7ZXF1YXRpb259CndpdGggJFxtYXRoYmZ7VX1fTiBcc2ltIE1WTihcbWF0aGJmezB9LFxtYXRoYmZ7Vn0pJC4KXGJlZ2lue2VxdWF0aW9ufQpcaGF0e1xib2xkc3ltYm9se1xiZXRhfX1fe0dMU309KFxtYXRoYmZ7Wn1fTl5UXG1hdGhiZntWfV57LTF9XG1hdGhiZntafV9OKV57LTF9XG1hdGhiZntafV9OXlQgXG1hdGhiZntWfV57LTF9IFxtYXRoYmZ7WX1fTi4KXGVuZHtlcXVhdGlvbn0KCkZyb20gR0xTIHRoZW9yeSwgClxiZWdpbntlcXVhdGlvbn0KXHRleHR7VmFyfVxsZWZ0W1xoYXR7XGJvbGRzeW1ib2x7XGJldGF9fV97R0xTfVxyaWdodF09KFxtYXRoYmZ7Wn1fTl5UXG1hdGhiZntWfV57LTF9XG1hdGhiZntafV9OKV57LTF9LgpcZW5ke2VxdWF0aW9ufQoKS2FsbWFuIEZpbHRlciBhbmQgU21vb3RoZXIKCjEuIElmICRcYmV0YSQgaXMga25vd24sIHRoZSBrYWxtYW4gZmlsdGVyIGFuZCBzbW9vdGhlciByZWN1cnNpb25zIHN0aWxsIGhvbGQgd2hlbiB3ZSBhY2NvdW50IGZvciAkXG1hdGhiZntafV90XGJvbGRzeW1ib2x7XGJldGF9JCBpbiB0aGUgdXBkYXRlIHN0ZXAuClxbClxtYXRoYmZ7WH1fe3RcdmVydCB0fT1cbWF0aGJme1h9X3t0XHZlcnQgdC0xfSsgXG1hdGhiZntQfV97dFx2ZXJ0IHQtMX0gIFxtYXRoYmZ7QX1fdF5UIFxib2xkc3ltYm9se1xTaWdtYX1fdF57LTF9IChcbWF0aGJme3l9X3QgLSBcbWF0aGJme0F9X3QgXG1hdGhiZntYfV97dFx2ZXJ0IHQtMX0gLSBcbWF0aGJme1p9X3RcYm9sZHN5bWJvbHtcYmV0YX0pClxdCgoKMi4gSGFydmV5ICgxOTg5KSBzaG93ZWQgdGhhdCBmb3IgYSBnaXZlbiAkXGJvbGRzeW1ib2x7XFBzaX0kCiAgCiAgLSBhcHBseWluZyB0aGUgc2FtZSBLYWxtYW4gZmlsdGVyICh3aXRob3V0IHJlY2FsY3VsYXRpbmcgJFxtYXRoYmZ7UH1fe3RcdmVydCB0LTF9JCwgJFxtYXRoYmZ7UH1fe3RcdmVydCB0fSQgYW5kICRcYm9sZHN5bWJvbHtcU2lnbWF9X3QkKSB0byAkXG1hdGhiZntZfV90JCBhbmQgZWFjaCBvZiB0aGUgY29sdW1ucyBvZiAkXG1hdGhiZntafV90JAogIC0gcmVzdWx0cyBpbiAKICAgICAgLSBBICRwXHRpbWVzMSQgdmVjdG9yIG9mIGlubm92YXRpb25zLCAkXG1hdGhiZntZfV90XiokIG9uICRcbWF0aGJme1l9X3QkIGFuZAogICAgICAtIGEgJHBcdGltZXMgbSQgbWF0cml4IG9mIGlubm92YXRpb25zLCAkXG1hdGhiZntaX3ReKn0kIG9uICQoXG1hdGhiZntaX3sxdH19LFxsZG90cywgXG1hdGhiZntaX3ttdH19KSQgYXJlIHByb2R1Y2VkLgoKICAtIHNvIHRoYXQgdGhlIEdMUyBlc3RpbWF0b3Igb2YgJFxib2xkc3ltYm9se1xiZXRhfSQgY2FuIGJlIG9idGFpbmVkCgpcWwpcYm9sZHN5bWJvbHtcaGF0IFxiZXRhX3tHTFN9fT1cbGVmdFtcc3VtXGxpbWl0c197dD0xfV5OIFxtYXRoYmZ7Wl90XnsqVH1cU2lnbWFfdF57LTF9Wl90XnsqfX1ccmlnaHRdXnstMX1cc3VtXGxpbWl0c197dD0xfV5OIFxtYXRoYmZ7Wl90XnsqVH1cU2lnbWFfdF57LTF9eV90XnsqfX0uClxdCgogIC0gdGhlIGlubm92YXRpb25zICRcYm9sZHN5bWJvbHtcZXBzaWxvbn1fdCQgYXJlIHRoZW4gZXF1aXZhbGVudCB0byAkXGJvbGRzeW1ib2x7XGVwc2lsb259X3Q9XG1hdGhiZnt5fV90XiogLVxtYXRoYmZ7en1fdF4qIFxoYXR7IFxib2xkc3ltYm9se1xiZXRhfX1fe0dMU30kClxiZWdpbntlcXVhdGlvbn0KXGxvZyBMX3tcbWF0aGJme1l9X059KFxib2xkc3ltYm9se1xQc2l9KT0tXGZyYWN7cG59ezJ9XGxvZyAyIFxwaSAtIFxmcmFjezF9ezJ9IFxzdW1cbGltaXRzX3t0PTF9Xm4gXGxvZyBcdmVydCBcYm9sZHN5bWJvbHtcU2lnbWF9X3RcdmVydCAtXGZyYWN7MX17Mn0gXHN1bVxsaW1pdHNfe3Q9MX1ebiBcYm9sZHN5bWJvbHtcZXBzaWxvbn1fdF5UIFxib2xkc3ltYm9se1xTaWdtYX1fdF57LTF9IFxib2xkc3ltYm9se1xlcHNpbG9ufV90LgpcZW5ke2VxdWF0aW9ufQoKMy4gSGVuY2UsIHdlIGNhbiB0aHVzIHVwZGF0ZSB0aGUgTS1zdGVwIG9mIHRoZSBFTSBhbGdvcml0aG0gd2l0aCB0aGUgR0xTIGVzdGltYXRvciBmb3IgJFxib2xkc3ltYm9se1xiZXRhfSQuCgotLS0KCjxiciAvPgoKCiMjIE1vcmUgQWR2YW5jZWQgVG9waWNzCgotIFN0YXRlIFNwYWNlIG1vZGVscyBjYW4gYWxzbyBiZSB1c2VkIHRvIG1vZGVsIHByb2Nlc3NlcyB3aXRoIHN0b2NoYXN0aWMgdm9sYXRpbGl0eSwgZS5nLiB3aGVuIG1vZGVsaW5nIHJldHVybnMuIAoKLSBXaGVuIHRoZSBzdGF0ZXMgYXJlIGRpc2NyZXRlLCB0aGUgU3RhdGUgU3BhY2UgTW9kZWwgYmVjb21lcyBhIEhpZGRlbiBNYXJrb3YgTW9kZWwKCi0gTm9uLXN0YXRpb25hcnkgdGltZSBzZXJpZXMuIFNwZWNpZmljIGZvcm11bGF0aW9ucyBvZiBTdGF0ZS1TcGFjZSBNb2RlbHMgZm9yIGluc3RhbmNlIGFsbG93IHRvIG1vZGVsIHNlYXNvbmFsIHZhcmlhdGlvbiBhbmQgdHJlbmRzLiAKCi0gTm9uLW5vcm1hbGl0eS4gTm90ZSwgdGhhdCB0aGUgZXJyb3JzIG9mIHRoZSBsb2ctbm9ybWFsIG1vZGVsIGFyZSBub24tZ3Vhc3NpYW4uIEJ1dCwgdGhleSBhcmUgc3ltbWV0cmljIHdpdGggYnJvYWRlciB0YWlscy4gVGhlIGVzdGltYXRvcnMgdGhhdCBhcmUgdXNlZCBoZXJlIGFyZSBhc3N5bXB0b3RpY2FsbHkgbm9ybWFsbHkgZGlzdHJpYnV0ZWQuIEluIHJlbGF0aXZlbHkgc2hvcnQgdGltZSBzZXJpZXMgd2UgY2FuIAogICAgCiAgICAtIHJlc29ydCB0byBib290c3RyYXBwaW5nIHRoZSBpbm5vdmF0aW9ucyB0byBwcm92aWRlIGJldHRlciBpbmZlcmVuY2Ugb3IgCiAgICAtIHdlIGNhbiBleHBsaWNpdGVseSBtb2RlbCB0aGUgZXJyb3JzIHdpdGggYW5vdGhlciBkaXN0cmlidXRpb24gCgoKCi0tLQoKPGJyIC8+CgoKIyBMaXRlcmF0dXJlIAoKLSBUaGUgcHJlc2VudGF0aW9uIGlzIGxhcmdlbHkgYmFzZWQgb24gdGhlIG1ldGhvZHMgaW4gY2hhcHRlciA2IG9mIFIuIFNodW13YXkgYW5kIEQuIFN0b2ZmZXIgKDIwMTcpIFRpbWUgU2VyaWVzIEFuYWx5c2lzIGFuZCBJdHMgQXBwbGljYXRpb25zIFdpdGggUiBFeGFtcGxlcyAtICA0dGggRWRpdGlvbiwgd2hpY2ggaXMgZnJlZWx5IGF2YWlsYWJsZSBvbiBQcm9mLiBEYXZpZCBTdG9mZmVyJ3Mgd2Vic2l0ZSAgaHR0cHM6Ly93d3cuc3RhdC5waXR0LmVkdS9zdG9mZmVyL3RzYTQvdHNhNC5odG0KCi0gSSBoYXZlIG5vdCByZXZpZXdlZCB0aGlzIGNvZGUsIGJ1dCB0aGlzIG1pZ2h0IGJlIGhlbHBmdWxsIHRvIGdldCBzdGFydGVkIGluIFB5dGhvbjogCgogICAgLSBTdGF0ZSBTcGFjZSBNb2RlbHMgaW4gUHl0aG9uOiBodHRwczovL3B5a2FsbWFuLmdpdGh1Yi5pby8KCiAgICAtIEhlZGdlIHJhdGlvIGV4YW1wbGUgaW4gUHl0aG9uOiBodHRwczovL3d3dy5xdWFudHN0YXJ0LmNvbS9hcnRpY2xlcy9EeW5hbWljLUhlZGdlLVJhdGlvLUJldHdlZW4tRVRGLVBhaXJzLVVzaW5nLXRoZS1LYWxtYW4tRmlsdGVyLwoKLS0tCgo=