Licença

This work is licensed under the Creative Commons Attribution-ShareAlike 4.0 International License. To view a copy of this license, visit http://creativecommons.org/licenses/by-sa/4.0/ or send a letter to Creative Commons, PO Box 1866, Mountain View, CA 94042, USA.

License: CC BY-SA 4.0

Citação

Sugestão de citação: FIGUEIREDO, Adriano Marcos Rodrigues. Séries Temporais: Estimação do método theta com série diária de ação LAME4. Campo Grande-MS,Brasil: RStudio/Rpubs, 2021. Disponível em <http://rpubs.com/amrofi/acoes_lame4_theta>.

1 Dados para exemplo

Neste exercício, usarei a série de preços da ação das Lojas Americanas (LAME4).

Vou chamar os dados por uma função derivada do quantmod que está no tidyquant, tq_get. Veja que ele vem em tibble. Então geramos o tsibble. Depois preenchemos os gaps implicitos com o fill_gaps.

library(tidyquant)
lame.tbl<-tq_get("LAME4.SA")

#XTS PARA TSIBBLE
library(tsibble)
library(tsibbledata)
library(magrittr)
library(dplyr)
LAME_tbl_ts<-as_tsibble(lame.tbl)
head(LAME_tbl_ts)
fabletools::autoplot(LAME_tbl_ts, close)

# fill gaps com ultimo valor ----------
library(tidyr)
dados.full2 <- LAME_tbl_ts[-1] %>% 
  tsibble::fill_gaps() %>% 
  tidyr::fill(c(open,high,low,close,volume,adjusted), .direction = "down")
print(dados.full2)
# A tsibble: 3,806 x 7 [1D]
   date        open  high   low close  volume adjusted
   <date>     <dbl> <dbl> <dbl> <dbl>   <dbl>    <dbl>
 1 2011-01-03  7.90  7.94  7.81  7.85 2735138     7.33
 2 2011-01-04  7.90  7.91  7.69  7.80 5385557     7.28
 3 2011-01-05  7.80  8.06  7.74  8.06 8986715     7.52
 4 2011-01-06  8.06  8.07  7.92  7.93 5828372     7.40
 5 2011-01-07  7.94  8.03  7.91  7.99 3071662     7.45
 6 2011-01-08  7.94  8.03  7.91  7.99 3071662     7.45
 7 2011-01-09  7.94  8.03  7.91  7.99 3071662     7.45
 8 2011-01-10  7.73  7.93  7.70  7.92 6060762     7.39
 9 2011-01-11  7.93  7.98  7.80  7.98 5422818     7.45
10 2011-01-12  7.95  8.00  7.86  7.93 4718197     7.41
# ... with 3,796 more rows

O gráfico para o preço de fechamento (close) será:

fabletools::autoplot(dados.full2, close)

O leitor pode testar agora as ferramentas de forecast do fable e fabletools.

library(fpp3)
fit <- dados.full2 %>% 
  model(
    arima = ARIMA(close), 
    ets = ETS(close),
    theta = THETA(close))
fit_fc <- fit %>% forecast(h = 365)
fit_fc %>% autoplot(dados.full2, level = NULL) + 
  labs(y = "valor de X", title = "Exemplo de forecast após fill_gaps")

2 Usando o pacote prophet

Agora farei um ajuste com o Facebook Prophet:

# Prophet model ------------------
# seguindo https://rpubs.com/amrofi/prophet_bitcoin
library(prophet)
library(tidyverse)
dados.ph<-dados.full2[,c(1,5)] # peguei apenas a date e a close
colnames(dados.ph)<-c("ds","y")  # o prophet requer esses nomes de colunas
Model1 <- prophet(dados.ph,daily.seasonality=TRUE)
class(Model1)  # objeto do prophet
[1] "prophet" "list"   
## Criar dataframe com mais 365 obs a frente (fora da amostra) ---------
Future1 <- make_future_dataframe(Model1, periods = 365)
tail(Future1)
## Previsao pelo Model1 para as datas de Future1 ------------
Forecast1 <- predict(Model1, Future1)
class(Forecast1)
[1] "data.frame"
## gráficos do forecast -------------
dyplot.prophet(Model1, Forecast1)

Fica a dica para o leitor avaliar a presença de efeitos ARCH.

3 Usando o pacote fable.prophet

Neste caso, conforme Hyndman e Athanasopoulos (2020), capítulo 12.2, o modelo Facebook (TAYLOR e LETHAM, 2018) é útil para estimar séries diárias, e principalmente aquelas com sazonalidades semanal e anual, além de efeitos de feriados. Ele pode ser considerado um modelo de regressão não linear da forma:

\[ y_t = g(t) + s(t) + h(t) + \varepsilon_t, \]

em que \(g(t)\) descreve a tendência linear (ou “termo de crescimento”), \(s(t)\) descreve os vários padrões sazonais, \(h(t)\) captura os efeitos do feriado, e \(\varepsilon_t\) é um termo de erro de ruído branco. A tendência linear é selecionada automaticamente se não for especificada explicitamente. O componente sazonal consiste em termos de Fourier dos períodos relevantes. Por padrão, a ordem 10 é usada para sazonalidade anual e a ordem 3 é usada para sazonalidade semanal, conforme Hyndman e Athanasopoulos (2020, tradução livre). Os efeitos de feriado são adicionados como variáveis ​​dummies simples. O modelo é estimado usando uma abordagem bayesiana para permitir a seleção automática dos pontos de mudança e outras características do modelo.

Estimaremos juntamente aos modelos ARIMA, ETS, THETA anteriormente estimados, dentro do ambiente fable e fable.prophet. Farei a regra 80-20 para amostras treino (3806*0.80 = 3044 dias = “2019-05-04”) e teste (3806-3044 = 762 dias = após “2019-05-05”), obtido a partir do dataset completo para os gaps implicitos.

library(fable.prophet);library(fable)
train <- dados.full2 %>% filter_index(~"2019-05-04")  # 3806*0.80 = 3044
h=length(dados.full2$close)-length(train$close)
h
[1] 762
fit.all <- train %>%
  model(
    arima = ARIMA(close,stepwise=FALSE,approximation = FALSE),
    ets = ETS(close),
    theta = THETA(close),
    prophet = prophet(close ~  
                        growth("linear") + 
            season(period = "day", order = 10) +
            season(period = "week", order = 5) +
            season(period = "year", order = 3))
  )%>%
  mutate(combination = (ets + arima+theta+prophet) / 4)
fitall_fc <- fit.all %>% forecast(h = h)
fitall_fc %>% autoplot(dados.full2, level = NULL) + 
  labs(y = "valor da ação", title = "Forecast com ARIMA, ETS, THETA e FABLE.PROPHET, para o Varejo de MS")

fc_accuracy <-fitall_fc %>% accuracy(dados.full2)
# acurácia ordenada pelo RMSE
fc_accuracy %>% group_by(.model) %>% summarise(RMSE = mean(RMSE), MAE = mean(MAE), 
    MASE = mean(MASE)) %>% arrange(RMSE)

Forecasts:

library(fable);library(fable.prophet)
fitall_fc2 <- fit.all %>% forecast(h = h+60)
fitall_fc2 %>% autoplot(dados.full2, level = c(95)) + 
  labs(y = "valor da ação", title = "Forecast com ARIMA, ETS, THETA e FABLE.PROPHET, para o Varejo de MS")

Representarei apenas os forecasts do período de “2021-06-01” a “2021-06-15” para não ficar muito extenso.

fitall_fc2 %>%
  filter_index("2021-06-01" ~ "2021-06-15") %>% knitr::kable()
.model date close .mean
arima 2021-06-01 N(16, 41) 15.59990
arima 2021-06-02 N(16, 41) 15.59990
arima 2021-06-03 N(16, 41) 15.59990
arima 2021-06-04 N(16, 41) 15.59990
arima 2021-06-05 N(16, 41) 15.59990
arima 2021-06-06 N(16, 41) 15.59990
arima 2021-06-07 N(16, 41) 15.59990
arima 2021-06-08 N(16, 41) 15.59990
arima 2021-06-09 N(16, 41) 15.59990
arima 2021-06-10 N(16, 41) 15.59990
arima 2021-06-11 N(16, 41) 15.59990
arima 2021-06-12 N(16, 42) 15.59990
arima 2021-06-13 N(16, 42) 15.59990
arima 2021-06-14 N(16, 42) 15.59991
arima 2021-06-15 N(16, 42) 15.59991
ets 2021-06-01 N(16, 63) 15.64643
ets 2021-06-02 N(16, 63) 15.64643
ets 2021-06-03 N(16, 63) 15.64643
ets 2021-06-04 N(16, 63) 15.64643
ets 2021-06-05 N(16, 63) 15.64643
ets 2021-06-06 N(16, 63) 15.64643
ets 2021-06-07 N(16, 63) 15.64643
ets 2021-06-08 N(16, 63) 15.64643
ets 2021-06-09 N(16, 63) 15.64643
ets 2021-06-10 N(16, 64) 15.64643
ets 2021-06-11 N(16, 64) 15.64643
ets 2021-06-12 N(16, 64) 15.64643
ets 2021-06-13 N(16, 64) 15.64643
ets 2021-06-14 N(16, 64) 15.64643
ets 2021-06-15 N(16, 64) 15.64643
theta 2021-06-01 N(17, 45) 17.15431
theta 2021-06-02 N(17, 45) 17.16208
theta 2021-06-03 N(17, 45) 17.16243
theta 2021-06-04 N(17, 45) 17.16500
theta 2021-06-05 N(17, 45) 17.16290
theta 2021-06-06 N(17, 45) 17.16144
theta 2021-06-07 N(17, 45) 17.16945
theta 2021-06-08 N(17, 45) 17.16822
theta 2021-06-09 N(17, 45) 17.17600
theta 2021-06-10 N(17, 45) 17.17635
theta 2021-06-11 N(17, 45) 17.17892
theta 2021-06-12 N(17, 45) 17.17681
theta 2021-06-13 N(17, 46) 17.17535
theta 2021-06-14 N(17, 46) 17.18337
theta 2021-06-15 N(17, 46) 17.18213
prophet 2021-06-01 sample[5000] 20.27055
prophet 2021-06-02 sample[5000] 20.28071
prophet 2021-06-03 sample[5000] 20.26173
prophet 2021-06-04 sample[5000] 20.26027
prophet 2021-06-05 sample[5000] 20.21970
prophet 2021-06-06 sample[5000] 20.23131
prophet 2021-06-07 sample[5000] 20.19852
prophet 2021-06-08 sample[5000] 20.21045
prophet 2021-06-09 sample[5000] 20.25915
prophet 2021-06-10 sample[5000] 20.18836
prophet 2021-06-11 sample[5000] 20.20295
prophet 2021-06-12 sample[5000] 20.18492
prophet 2021-06-13 sample[5000] 20.19415
prophet 2021-06-14 sample[5000] 20.20745
prophet 2021-06-15 sample[5000] 20.17323
combination 2021-06-01 17.21997 17.21997
combination 2021-06-02 17.22646 17.22646
combination 2021-06-03 17.21925 17.21925
combination 2021-06-04 17.21928 17.21928
combination 2021-06-05 17.21497 17.21497
combination 2021-06-06 17.21357 17.21357
combination 2021-06-07 17.21329 17.21329
combination 2021-06-08 17.21882 17.21882
combination 2021-06-09 17.2123 17.21230
combination 2021-06-10 17.21061 17.21061
combination 2021-06-11 17.21338 17.21338
combination 2021-06-12 17.21147 17.21147
combination 2021-06-13 17.20688 17.20688
combination 2021-06-14 17.21332 17.21332
combination 2021-06-15 17.20762 17.20762

Como podemos ver, as previsões ainda podem ser melhores, e podemos preocupar com os valores atípicos ao final da série.

4 Modelo com a série completa

Neste caso, supondo a série completa, estimaremos tudo junto (ETS, ARIMA, THETA, PROPHET, Combination) sem fazer o split de treino e teste.

fit.semsplit <- dados.full2 %>%
  model(
    arima = ARIMA(close,stepwise=FALSE,approximation = FALSE),
    ets = ETS(close),
    theta = THETA(close),
    prophet = prophet(close ~  
                        growth("linear") + 
            season(period = "day", order = 10) +
            season(period = "week", order = 5) +
            season(period = "year", order = 3))
  )%>%
  mutate(combination = (ets + arima+theta+prophet) / 4)

Colocarei um forecast de 150 dias a frente (para melhorar a visualização gráfica).

fitsem_fc <- fit.semsplit %>% forecast(h = 150)
fitsem_fc %>% autoplot(dados.full2, level = c(95)) + 
  labs(y = "valor da ação", title = "Forecast com ARIMA, ETS, THETA e FABLE.PROPHET, para o Varejo de MS")

Interessante observar que a previsão com o dataset completo levou o prophet a prever muito acima do nível da última observação. No caso, considerando a amostra de treino, o forecast estava mais perto do nível recente, embora pouco abaixo. Nessas situações é que a combinação aparece como interessante, mesclando as estimativas dos diferentes modelos.
Deixo ao leitor para investigar a estabilidade do modelo e propriedades dos resíduos.

Referências

HYNDMAN, Rob J. (2018). fpp2: Data for “Forecasting: Principles and Practice” (2nd Edition). R package version 2.3. Disponível em: https://CRAN.R-project.org/package=fpp2. Accessed on 20 May 2021.

HYNDMAN, Rob J. (2019). fpp3: Data for “Forecasting: Principles and Practice” (3rd Edition). R package. Disponível em: https://github.com/robjhyndman/fpp3-package, https://OTexts.org/fpp3/. Accessed on 20 May 2021.

HYNDMAN, R.J.; ATHANASOPOULOS, G. (2020) Forecasting: principles and practice, 3rd edition, OTexts: Melbourne, Australia. Disponível em: https://otexts.com/fpp3/. Accessed on 20 May 2021.

O’HARA-WILD, Mitchell; HYNDMAN, Rob J.; WANG, Earo. (2021). feasts: Feature Extraction and Statistics for Time Series. R package version 0.2.1. Disponível em: https://CRAN.R-project.org/package=feasts. Accessed on 20 May 2021.

TAYLOR, S. J.; LETHAM, B. (2018). Forecasting at scale. The American Statistician, 72(1), 37–45.

LS0tDQp0aXRsZTogIlPDqXJpZXMgVGVtcG9yYWlzOiBFc3RpbWHDp8OjbyBkbyBtw6l0b2RvIHRoZXRhIGNvbSBzw6lyaWUgZGnDoXJpYSBkZSBhw6fDo28gTEFNRTQiDQphdXRob3I6ICJBZHJpYW5vIE1hcmNvcyBSb2RyaWd1ZXMgRmlndWVpcmVkbywgKmUtbWFpbDogYWRyaWFuby5maWd1ZWlyZWRvQHVmbXMuYnIqIg0KbGlua2NvbG9yOiBibHVlDQphYnN0cmFjdDogDQogIFRoaXMgaXMgYW4gdW5kZXJncmFkIHN0dWRlbnQgbGV2ZWwgaW5zdHJ1Y3Rpb24gZm9yIGNsYXNzIHVzZS4gVGhlIHJlYWRlciBpcyBlbmNvdXJhZ2VkIHRvIGVuaGFuY2UgdGhlIG1vZGVsIGNoYW5naW5nIHRoZSB0cmFpbiBkYXRhLiAgDQpkYXRlOiAiYHIgZm9ybWF0KFN5cy5EYXRlKCksICclZCAlQiAlWScpYCINCm91dHB1dDoNCiAgaHRtbF9kb2N1bWVudDoNCiAgICBjb2RlX2Rvd25sb2FkOiB0cnVlDQogICAgdGhlbWU6IGRlZmF1bHQNCiAgICBudW1iZXJfc2VjdGlvbnM6IHRydWUNCiAgICB0b2M6IHllcw0KICAgIHRvY19mbG9hdDogbm8NCiAgICBkZl9wcmludDogcGFnZWQNCiAgICBmaWdfY2FwdGlvbjogdHJ1ZQ0KICBwZGZfZG9jdW1lbnQ6DQogICAgdG9jOiB5ZXMNCi0tLQ0KDQpgYGB7ciBrbml0cl9pbml0LCBlY2hvPUZBTFNFLCBjYWNoZT1GQUxTRX0NCmxpYnJhcnkoa25pdHIpDQpsaWJyYXJ5KHJtYXJrZG93bikNCmxpYnJhcnkocm1kZm9ybWF0cykNCg0KIyMgR2xvYmFsIG9wdGlvbnMNCm9wdGlvbnMobWF4LnByaW50PSIxMDAiKQ0Kb3B0c19jaHVuayRzZXQoZWNobz1UUlVFLA0KCSAgICAgICAgICAgICBjYWNoZT1ULA0KICAgICAgICAgICAgICAgcHJvbXB0PUZBTFNFLA0KICAgICAgICAgICAgICAgdGlkeT1UUlVFLA0KICAgICAgICAgICAgICAgY29tbWVudD1OQSwNCiAgICAgICAgICAgICAgIG1lc3NhZ2U9RkFMU0UsDQogICAgICAgICAgICAgICB3YXJuaW5nPUZBTFNFKQ0Kb3B0c19rbml0JHNldCh3aWR0aD0xMDApDQpgYGANCg0KIyBMaWNlbsOnYSB7I0xpY2Vuw6dhIC51bm51bWJlcmVkfQ0KDQpUaGlzIHdvcmsgaXMgbGljZW5zZWQgdW5kZXIgdGhlIENyZWF0aXZlIENvbW1vbnMgQXR0cmlidXRpb24tU2hhcmVBbGlrZSA0LjAgSW50ZXJuYXRpb25hbCBMaWNlbnNlLiBUbyB2aWV3IGEgY29weSBvZiB0aGlzIGxpY2Vuc2UsIHZpc2l0IDxodHRwOi8vY3JlYXRpdmVjb21tb25zLm9yZy9saWNlbnNlcy9ieS1zYS80LjAvPiBvciBzZW5kIGEgbGV0dGVyIHRvIENyZWF0aXZlIENvbW1vbnMsIFBPIEJveCAxODY2LCBNb3VudGFpbiBWaWV3LCBDQSA5NDA0MiwgVVNBLg0KDQohW0xpY2Vuc2U6IENDIEJZLVNBIDQuMF0oaHR0cHM6Ly9taXJyb3JzLmNyZWF0aXZlY29tbW9ucy5vcmcvcHJlc3NraXQvYnV0dG9ucy84OHgzMS9wbmcvYnktc2EucG5nKXt3aWR0aD0iMjUlIn0NCg0KIyBDaXRhw6fDo28geyNDaXRhw6fDo28gLnVubnVtYmVyZWR9DQoNClN1Z2VzdMOjbyBkZSBjaXRhw6fDo286IEZJR1VFSVJFRE8sIEFkcmlhbm8gTWFyY29zIFJvZHJpZ3Vlcy4gU8OpcmllcyBUZW1wb3JhaXM6IEVzdGltYcOnw6NvIGRvIG3DqXRvZG8gdGhldGEgY29tIHPDqXJpZSBkacOhcmlhIGRlIGHDp8OjbyBMQU1FNC4gQ2FtcG8gR3JhbmRlLU1TLEJyYXNpbDogUlN0dWRpby9ScHVicywgMjAyMS4gRGlzcG9uw612ZWwgZW0gW1w8aHR0cDovL3JwdWJzLmNvbS9hbXJvZmkvYWNvZXNfbGFtZTRfdGhldGFcPl0oaHR0cDovL3JwdWJzLmNvbS9hbXJvZmkvYWNvZXNfbGFtZTRfdGhldGEpey51cml9Lg0KDQojIERhZG9zIHBhcmEgZXhlbXBsbw0KDQpOZXN0ZSBleGVyY8OtY2lvLCB1c2FyZWkgYSBzw6lyaWUgZGUgcHJlw6dvcyBkYSBhw6fDo28gZGFzIExvamFzIEFtZXJpY2FuYXMgKExBTUU0KS4NCg0KVm91IGNoYW1hciBvcyBkYWRvcyBwb3IgdW1hIGZ1bsOnw6NvIGRlcml2YWRhIGRvIHF1YW50bW9kIHF1ZSBlc3TDoSBubyB0aWR5cXVhbnQsIGB0cV9nZXRgLiBWZWphIHF1ZSBlbGUgdmVtIGVtIHRpYmJsZS4gRW50w6NvIGdlcmFtb3MgbyBgdHNpYmJsZWAuIERlcG9pcyBwcmVlbmNoZW1vcyBvcyBnYXBzIGltcGxpY2l0b3MgY29tIG8gZmlsbF9nYXBzLg0KDQpgYGB7ciBkYWRvc30NCmxpYnJhcnkodGlkeXF1YW50KQ0KbGFtZS50Ymw8LXRxX2dldCgiTEFNRTQuU0EiKQ0KDQojWFRTIFBBUkEgVFNJQkJMRQ0KbGlicmFyeSh0c2liYmxlKQ0KbGlicmFyeSh0c2liYmxlZGF0YSkNCmxpYnJhcnkobWFncml0dHIpDQpsaWJyYXJ5KGRwbHlyKQ0KTEFNRV90YmxfdHM8LWFzX3RzaWJibGUobGFtZS50YmwpDQpoZWFkKExBTUVfdGJsX3RzKQ0KZmFibGV0b29sczo6YXV0b3Bsb3QoTEFNRV90YmxfdHMsIGNsb3NlKQ0KDQoNCiMgZmlsbCBnYXBzIGNvbSB1bHRpbW8gdmFsb3IgLS0tLS0tLS0tLQ0KbGlicmFyeSh0aWR5cikNCmRhZG9zLmZ1bGwyIDwtIExBTUVfdGJsX3RzWy0xXSAlPiUgDQogIHRzaWJibGU6OmZpbGxfZ2FwcygpICU+JSANCiAgdGlkeXI6OmZpbGwoYyhvcGVuLGhpZ2gsbG93LGNsb3NlLHZvbHVtZSxhZGp1c3RlZCksIC5kaXJlY3Rpb24gPSAiZG93biIpDQpwcmludChkYWRvcy5mdWxsMikNCg0KYGBgDQoNCk8gZ3LDoWZpY28gcGFyYSBvIHByZcOnbyBkZSBmZWNoYW1lbnRvIChjbG9zZSkgc2Vyw6E6DQoNCmBgYHtyfQ0KZmFibGV0b29sczo6YXV0b3Bsb3QoZGFkb3MuZnVsbDIsIGNsb3NlKQ0KYGBgDQoNCk8gbGVpdG9yIHBvZGUgdGVzdGFyIGFnb3JhIGFzIGZlcnJhbWVudGFzIGRlIGZvcmVjYXN0IGRvIGZhYmxlIGUgZmFibGV0b29scy4NCg0KYGBge3IgZm9yZWNhc3RfYXJpbWFfZXRzX3RoZXRhfQ0KbGlicmFyeShmcHAzKQ0KZml0IDwtIGRhZG9zLmZ1bGwyICU+JSANCiAgbW9kZWwoDQogICAgYXJpbWEgPSBBUklNQShjbG9zZSksIA0KICAgIGV0cyA9IEVUUyhjbG9zZSksDQogICAgdGhldGEgPSBUSEVUQShjbG9zZSkpDQpmaXRfZmMgPC0gZml0ICU+JSBmb3JlY2FzdChoID0gMzY1KQ0KZml0X2ZjICU+JSBhdXRvcGxvdChkYWRvcy5mdWxsMiwgbGV2ZWwgPSBOVUxMKSArIA0KICBsYWJzKHkgPSAidmFsb3IgZGUgWCIsIHRpdGxlID0gIkV4ZW1wbG8gZGUgZm9yZWNhc3QgYXDDs3MgZmlsbF9nYXBzIikNCg0KYGBgDQoNCiMgVXNhbmRvIG8gcGFjb3RlIGBwcm9waGV0YA0KDQpBZ29yYSBmYXJlaSB1bSBhanVzdGUgY29tIG8gRmFjZWJvb2sgUHJvcGhldDoNCg0KYGBge3IgcHJvcGhldH0NCiMgUHJvcGhldCBtb2RlbCAtLS0tLS0tLS0tLS0tLS0tLS0NCiMgc2VndWluZG8gaHR0cHM6Ly9ycHVicy5jb20vYW1yb2ZpL3Byb3BoZXRfYml0Y29pbg0KbGlicmFyeShwcm9waGV0KQ0KbGlicmFyeSh0aWR5dmVyc2UpDQpkYWRvcy5waDwtZGFkb3MuZnVsbDJbLGMoMSw1KV0gIyBwZWd1ZWkgYXBlbmFzIGEgZGF0ZSBlIGEgY2xvc2UNCmNvbG5hbWVzKGRhZG9zLnBoKTwtYygiZHMiLCJ5IikgICMgbyBwcm9waGV0IHJlcXVlciBlc3NlcyBub21lcyBkZSBjb2x1bmFzDQpNb2RlbDEgPC0gcHJvcGhldChkYWRvcy5waCxkYWlseS5zZWFzb25hbGl0eT1UUlVFKQ0KY2xhc3MoTW9kZWwxKSAgIyBvYmpldG8gZG8gcHJvcGhldA0KIyMgQ3JpYXIgZGF0YWZyYW1lIGNvbSBtYWlzIDM2NSBvYnMgYSBmcmVudGUgKGZvcmEgZGEgYW1vc3RyYSkgLS0tLS0tLS0tDQpGdXR1cmUxIDwtIG1ha2VfZnV0dXJlX2RhdGFmcmFtZShNb2RlbDEsIHBlcmlvZHMgPSAzNjUpDQp0YWlsKEZ1dHVyZTEpDQojIyBQcmV2aXNhbyBwZWxvIE1vZGVsMSBwYXJhIGFzIGRhdGFzIGRlIEZ1dHVyZTEgLS0tLS0tLS0tLS0tDQpGb3JlY2FzdDEgPC0gcHJlZGljdChNb2RlbDEsIEZ1dHVyZTEpDQpjbGFzcyhGb3JlY2FzdDEpDQojIyBncsOhZmljb3MgZG8gZm9yZWNhc3QgLS0tLS0tLS0tLS0tLQ0KZHlwbG90LnByb3BoZXQoTW9kZWwxLCBGb3JlY2FzdDEpDQpgYGANCg0KRmljYSBhIGRpY2EgcGFyYSBvIGxlaXRvciBhdmFsaWFyIGEgcHJlc2Vuw6dhIGRlIGVmZWl0b3MgQVJDSC4NCg0KIyBVc2FuZG8gbyBwYWNvdGUgYGZhYmxlLnByb3BoZXRgDQoNCk5lc3RlIGNhc28sIGNvbmZvcm1lIEh5bmRtYW4gZSBBdGhhbmFzb3BvdWxvcyAoMjAyMCksIGNhcMOtdHVsbyAxMi4yLCBvIG1vZGVsbyBGYWNlYm9vayAoVEFZTE9SIGUgTEVUSEFNLCAyMDE4KSDDqSDDunRpbCBwYXJhIGVzdGltYXIgc8OpcmllcyBkacOhcmlhcywgZSBwcmluY2lwYWxtZW50ZSBhcXVlbGFzIGNvbSBzYXpvbmFsaWRhZGVzIHNlbWFuYWwgZSBhbnVhbCwgYWzDqW0gZGUgZWZlaXRvcyBkZSBmZXJpYWRvcy4gRWxlIHBvZGUgc2VyIGNvbnNpZGVyYWRvIHVtIG1vZGVsbyBkZSByZWdyZXNzw6NvIG7Do28gbGluZWFyIGRhIGZvcm1hOg0KDQokJA0KeV90ID0gZyh0KSArIHModCkgKyBoKHQpICsgXHZhcmVwc2lsb25fdCwNCiQkDQoNCmVtIHF1ZSAkZyh0KSQgZGVzY3JldmUgYSB0ZW5kw6puY2lhIGxpbmVhciAob3UgInRlcm1vIGRlIGNyZXNjaW1lbnRvIiksICRzKHQpJCBkZXNjcmV2ZSBvcyB2w6FyaW9zIHBhZHLDtWVzIHNhem9uYWlzLCAkaCh0KSQgY2FwdHVyYSBvcyBlZmVpdG9zIGRvIGZlcmlhZG8sIGUgJFx2YXJlcHNpbG9uX3QkIMOpIHVtIHRlcm1vIGRlIGVycm8gZGUgcnXDrWRvIGJyYW5jby4gQSB0ZW5kw6puY2lhIGxpbmVhciDDqSBzZWxlY2lvbmFkYSBhdXRvbWF0aWNhbWVudGUgc2UgbsOjbyBmb3IgZXNwZWNpZmljYWRhIGV4cGxpY2l0YW1lbnRlLiBPIGNvbXBvbmVudGUgc2F6b25hbCBjb25zaXN0ZSBlbSB0ZXJtb3MgZGUgRm91cmllciBkb3MgcGVyw61vZG9zIHJlbGV2YW50ZXMuIFBvciBwYWRyw6NvLCBhIG9yZGVtIDEwIMOpIHVzYWRhIHBhcmEgc2F6b25hbGlkYWRlIGFudWFsIGUgYSBvcmRlbSAzIMOpIHVzYWRhIHBhcmEgc2F6b25hbGlkYWRlIHNlbWFuYWwsIGNvbmZvcm1lIEh5bmRtYW4gZSBBdGhhbmFzb3BvdWxvcyAoMjAyMCwgdHJhZHXDp8OjbyBsaXZyZSkuIE9zIGVmZWl0b3MgZGUgZmVyaWFkbyBzw6NvIGFkaWNpb25hZG9zIGNvbW8gdmFyacOhdmVpcyDigIvigItkdW1taWVzIHNpbXBsZXMuIE8gbW9kZWxvIMOpIGVzdGltYWRvIHVzYW5kbyB1bWEgYWJvcmRhZ2VtIGJheWVzaWFuYSBwYXJhIHBlcm1pdGlyIGEgc2VsZcOnw6NvIGF1dG9tw6F0aWNhIGRvcyBwb250b3MgZGUgbXVkYW7Dp2EgZSBvdXRyYXMgY2FyYWN0ZXLDrXN0aWNhcyBkbyBtb2RlbG8uDQoNCkVzdGltYXJlbW9zIGp1bnRhbWVudGUgYW9zIG1vZGVsb3MgYEFSSU1BYCwgYEVUU2AsIGBUSEVUQWAgYW50ZXJpb3JtZW50ZSBlc3RpbWFkb3MsIGRlbnRybyBkbyBhbWJpZW50ZSBgZmFibGVgIGUgYGZhYmxlLnByb3BoZXRgLiBGYXJlaSBhIHJlZ3JhIDgwLTIwIHBhcmEgYW1vc3RyYXMgdHJlaW5vICgzODA2XCowLjgwID0gMzA0NCBkaWFzID0gIjIwMTktMDUtMDQiKSBlIHRlc3RlICgzODA2LTMwNDQgPSA3NjIgZGlhcyA9IGFww7NzICIyMDE5LTA1LTA1IiksIG9idGlkbyBhIHBhcnRpciBkbyBkYXRhc2V0IGNvbXBsZXRvIHBhcmEgb3MgZ2FwcyBpbXBsaWNpdG9zLg0KDQpgYGB7ciBzcGxpdHNhbXBsZX0NCmxpYnJhcnkoZmFibGUucHJvcGhldCk7bGlicmFyeShmYWJsZSkNCnRyYWluIDwtIGRhZG9zLmZ1bGwyICU+JSBmaWx0ZXJfaW5kZXgofiIyMDE5LTA1LTA0IikgICMgMzgwNiowLjgwID0gMzA0NA0KaD1sZW5ndGgoZGFkb3MuZnVsbDIkY2xvc2UpLWxlbmd0aCh0cmFpbiRjbG9zZSkNCmgNCmBgYA0KDQpgYGB7ciBmaXRhbGx9DQpmaXQuYWxsIDwtIHRyYWluICU+JQ0KICBtb2RlbCgNCiAgICBhcmltYSA9IEFSSU1BKGNsb3NlLHN0ZXB3aXNlPUZBTFNFLGFwcHJveGltYXRpb24gPSBGQUxTRSksDQogICAgZXRzID0gRVRTKGNsb3NlKSwNCiAgICB0aGV0YSA9IFRIRVRBKGNsb3NlKSwNCiAgICBwcm9waGV0ID0gcHJvcGhldChjbG9zZSB+ICANCiAgICAgICAgICAgICAgICAgICAgICAgIGdyb3d0aCgibGluZWFyIikgKyANCiAgICAgICAgICAgIHNlYXNvbihwZXJpb2QgPSAiZGF5Iiwgb3JkZXIgPSAxMCkgKw0KICAgICAgICAgICAgc2Vhc29uKHBlcmlvZCA9ICJ3ZWVrIiwgb3JkZXIgPSA1KSArDQogICAgICAgICAgICBzZWFzb24ocGVyaW9kID0gInllYXIiLCBvcmRlciA9IDMpKQ0KICApJT4lDQogIG11dGF0ZShjb21iaW5hdGlvbiA9IChldHMgKyBhcmltYSt0aGV0YStwcm9waGV0KSAvIDQpDQpgYGANCg0KYGBge3J9DQpmaXRhbGxfZmMgPC0gZml0LmFsbCAlPiUgZm9yZWNhc3QoaCA9IGgpDQpmaXRhbGxfZmMgJT4lIGF1dG9wbG90KGRhZG9zLmZ1bGwyLCBsZXZlbCA9IE5VTEwpICsgDQogIGxhYnMoeSA9ICJ2YWxvciBkYSBhw6fDo28iLCB0aXRsZSA9ICJGb3JlY2FzdCBjb20gQVJJTUEsIEVUUywgVEhFVEEgZSBGQUJMRS5QUk9QSEVULCBwYXJhIG8gVmFyZWpvIGRlIE1TIikNCmBgYA0KDQpgYGB7cn0NCmZjX2FjY3VyYWN5IDwtZml0YWxsX2ZjICU+JSBhY2N1cmFjeShkYWRvcy5mdWxsMikNCiMgYWN1csOhY2lhIG9yZGVuYWRhIHBlbG8gUk1TRQ0KZmNfYWNjdXJhY3kgJT4lIGdyb3VwX2J5KC5tb2RlbCkgJT4lIHN1bW1hcmlzZShSTVNFID0gbWVhbihSTVNFKSwgTUFFID0gbWVhbihNQUUpLCANCiAgICBNQVNFID0gbWVhbihNQVNFKSkgJT4lIGFycmFuZ2UoUk1TRSkNCmBgYA0KDQpGb3JlY2FzdHM6DQoNCmBgYHtyfQ0KbGlicmFyeShmYWJsZSk7bGlicmFyeShmYWJsZS5wcm9waGV0KQ0KZml0YWxsX2ZjMiA8LSBmaXQuYWxsICU+JSBmb3JlY2FzdChoID0gaCs2MCkNCmZpdGFsbF9mYzIgJT4lIGF1dG9wbG90KGRhZG9zLmZ1bGwyLCBsZXZlbCA9IGMoOTUpKSArIA0KICBsYWJzKHkgPSAidmFsb3IgZGEgYcOnw6NvIiwgdGl0bGUgPSAiRm9yZWNhc3QgY29tIEFSSU1BLCBFVFMsIFRIRVRBIGUgRkFCTEUuUFJPUEhFVCwgcGFyYSBvIFZhcmVqbyBkZSBNUyIpDQpgYGANCg0KUmVwcmVzZW50YXJlaSBhcGVuYXMgb3MgZm9yZWNhc3RzIGRvIHBlcsOtb2RvIGRlICIyMDIxLTA2LTAxIiBhICIyMDIxLTA2LTE1IiBwYXJhIG7Do28gZmljYXIgbXVpdG8gZXh0ZW5zby4NCg0KYGBge3J9DQpmaXRhbGxfZmMyICU+JQ0KICBmaWx0ZXJfaW5kZXgoIjIwMjEtMDYtMDEiIH4gIjIwMjEtMDYtMTUiKSAlPiUga25pdHI6OmthYmxlKCkNCmBgYA0KDQpDb21vIHBvZGVtb3MgdmVyLCBhcyBwcmV2aXPDtWVzIGFpbmRhIHBvZGVtIHNlciBtZWxob3JlcywgZSBwb2RlbW9zIHByZW9jdXBhciBjb20gb3MgdmFsb3JlcyBhdMOtcGljb3MgYW8gZmluYWwgZGEgc8OpcmllLg0KDQojIE1vZGVsbyBjb20gYSBzw6lyaWUgY29tcGxldGENCg0KTmVzdGUgY2Fzbywgc3Vwb25kbyBhIHPDqXJpZSBjb21wbGV0YSwgZXN0aW1hcmVtb3MgdHVkbyBqdW50byAoRVRTLCBBUklNQSwgVEhFVEEsIFBST1BIRVQsIENvbWJpbmF0aW9uKSBzZW0gZmF6ZXIgbyBzcGxpdCBkZSB0cmVpbm8gZSB0ZXN0ZS4NCg0KYGBge3Igc2Vtc3BsaXR9DQpmaXQuc2Vtc3BsaXQgPC0gZGFkb3MuZnVsbDIgJT4lDQogIG1vZGVsKA0KICAgIGFyaW1hID0gQVJJTUEoY2xvc2Usc3RlcHdpc2U9RkFMU0UsYXBwcm94aW1hdGlvbiA9IEZBTFNFKSwNCiAgICBldHMgPSBFVFMoY2xvc2UpLA0KICAgIHRoZXRhID0gVEhFVEEoY2xvc2UpLA0KICAgIHByb3BoZXQgPSBwcm9waGV0KGNsb3NlIH4gIA0KICAgICAgICAgICAgICAgICAgICAgICAgZ3Jvd3RoKCJsaW5lYXIiKSArIA0KICAgICAgICAgICAgc2Vhc29uKHBlcmlvZCA9ICJkYXkiLCBvcmRlciA9IDEwKSArDQogICAgICAgICAgICBzZWFzb24ocGVyaW9kID0gIndlZWsiLCBvcmRlciA9IDUpICsNCiAgICAgICAgICAgIHNlYXNvbihwZXJpb2QgPSAieWVhciIsIG9yZGVyID0gMykpDQogICklPiUNCiAgbXV0YXRlKGNvbWJpbmF0aW9uID0gKGV0cyArIGFyaW1hK3RoZXRhK3Byb3BoZXQpIC8gNCkNCmBgYA0KDQpDb2xvY2FyZWkgdW0gZm9yZWNhc3QgZGUgMTUwIGRpYXMgYSBmcmVudGUgKHBhcmEgbWVsaG9yYXIgYSB2aXN1YWxpemHDp8OjbyBncsOhZmljYSkuDQoNCmBgYHtyIGZjc3RzZW19DQpmaXRzZW1fZmMgPC0gZml0LnNlbXNwbGl0ICU+JSBmb3JlY2FzdChoID0gMTUwKQ0KZml0c2VtX2ZjICU+JSBhdXRvcGxvdChkYWRvcy5mdWxsMiwgbGV2ZWwgPSBjKDk1KSkgKyANCiAgbGFicyh5ID0gInZhbG9yIGRhIGHDp8OjbyIsIHRpdGxlID0gIkZvcmVjYXN0IGNvbSBBUklNQSwgRVRTLCBUSEVUQSBlIEZBQkxFLlBST1BIRVQsIHBhcmEgbyBWYXJlam8gZGUgTVMiKQ0KYGBgDQoNCkludGVyZXNzYW50ZSBvYnNlcnZhciBxdWUgYSBwcmV2aXPDo28gY29tIG8gZGF0YXNldCBjb21wbGV0byBsZXZvdSBvIHByb3BoZXQgYSBwcmV2ZXIgbXVpdG8gYWNpbWEgZG8gbsOtdmVsIGRhIMO6bHRpbWEgb2JzZXJ2YcOnw6NvLiBObyBjYXNvLCBjb25zaWRlcmFuZG8gYSBhbW9zdHJhIGRlIHRyZWlubywgbyBmb3JlY2FzdCBlc3RhdmEgbWFpcyBwZXJ0byBkbyBuw612ZWwgcmVjZW50ZSwgZW1ib3JhIHBvdWNvIGFiYWl4by4gTmVzc2FzIHNpdHVhw6fDtWVzIMOpIHF1ZSBhIGNvbWJpbmHDp8OjbyBhcGFyZWNlIGNvbW8gaW50ZXJlc3NhbnRlLCBtZXNjbGFuZG8gYXMgZXN0aW1hdGl2YXMgZG9zIGRpZmVyZW50ZXMgbW9kZWxvcy4gICAgDQpEZWl4byBhbyBsZWl0b3IgcGFyYSBpbnZlc3RpZ2FyIGEgZXN0YWJpbGlkYWRlIGRvIG1vZGVsbyBlIHByb3ByaWVkYWRlcyBkb3MgcmVzw61kdW9zLg0KDQojIFJlZmVyw6puY2lhcyB7I1JlZmVyw6puY2lhcyAudW5udW1iZXJlZH0NCg0KSFlORE1BTiwgUm9iIEouICgyMDE4KS4gZnBwMjogRGF0YSBmb3IgIkZvcmVjYXN0aW5nOiBQcmluY2lwbGVzIGFuZCBQcmFjdGljZSIgKDJuZCBFZGl0aW9uKS4gUiBwYWNrYWdlIHZlcnNpb24gMi4zLiBEaXNwb27DrXZlbCBlbTogPGh0dHBzOi8vQ1JBTi5SLXByb2plY3Qub3JnL3BhY2thZ2U9ZnBwMj4uIEFjY2Vzc2VkIG9uIDIwIE1heSAyMDIxLg0KDQpIWU5ETUFOLCBSb2IgSi4gKDIwMTkpLiBmcHAzOiBEYXRhIGZvciAiRm9yZWNhc3Rpbmc6IFByaW5jaXBsZXMgYW5kIFByYWN0aWNlIiAoM3JkIEVkaXRpb24pLiBSIHBhY2thZ2UuIERpc3BvbsOtdmVsIGVtOiA8aHR0cHM6Ly9naXRodWIuY29tL3JvYmpoeW5kbWFuL2ZwcDMtcGFja2FnZT4sIDxodHRwczovL09UZXh0cy5vcmcvZnBwMy8+LiBBY2Nlc3NlZCBvbiAyMCBNYXkgMjAyMS4NCg0KSFlORE1BTiwgUi5KLjsgQVRIQU5BU09QT1VMT1MsIEcuICgyMDIwKSBGb3JlY2FzdGluZzogcHJpbmNpcGxlcyBhbmQgcHJhY3RpY2UsIDNyZCBlZGl0aW9uLCBPVGV4dHM6IE1lbGJvdXJuZSwgQXVzdHJhbGlhLiBEaXNwb27DrXZlbCBlbTogPGh0dHBzOi8vb3RleHRzLmNvbS9mcHAzLz4uIEFjY2Vzc2VkIG9uIDIwIE1heSAyMDIxLg0KDQpPJ0hBUkEtV0lMRCwgTWl0Y2hlbGw7IEhZTkRNQU4sIFJvYiBKLjsgV0FORywgRWFyby4gKDIwMjEpLiBmZWFzdHM6IEZlYXR1cmUgRXh0cmFjdGlvbiBhbmQgU3RhdGlzdGljcyBmb3IgVGltZSBTZXJpZXMuIFIgcGFja2FnZSB2ZXJzaW9uIDAuMi4xLiBEaXNwb27DrXZlbCBlbTogPGh0dHBzOi8vQ1JBTi5SLXByb2plY3Qub3JnL3BhY2thZ2U9ZmVhc3RzPi4gQWNjZXNzZWQgb24gMjAgTWF5IDIwMjEuDQoNClRBWUxPUiwgUy4gSi47IExFVEhBTSwgQi4gKDIwMTgpLiBGb3JlY2FzdGluZyBhdCBzY2FsZS4gVGhlIEFtZXJpY2FuIFN0YXRpc3RpY2lhbiwgNzIoMSksIDM3LS00NS4NCg==