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
License: CC BY-SA 4.0

Citação

Sugestão de citação: FIGUEIREDO, Adriano Marcos Rodrigues. Séries Temporais: previsão de ações com ETS. Campo Grande-MS,Brasil: RStudio/Rpubs, 2019. Disponível em http://rpubs.com/amrofi/stocks_ETS.

1 Introdução

2 Dados e gráfico

2.1 VALE3 e PETR4

library(quantmod)
library(ggplot2)
library(scales)
library(ggthemes)
library(gridExtra)  # para vaios graficos juntos
library(dygraphs)  # para os graficos dinamicos
library(fpp2)  # para fazer o ETS
library(Metrics)  # para fazer acurácia
env = new.env()
# pegando cotações mensais da petrobras e do ibovespa
getSymbols(c("PETR4.SA", "VALE3.SA"), src = "yahoo", env = env, periodicity = "monthly",
    from = "2000-01-01", to = "2019-09-01")
[1] "PETR4.SA" "VALE3.SA"
vale = env$VALE3.SA[, 6]
petro = env$PETR4.SA[, 6]

g1 <- autoplot(vale) + geom_line(size = 0.8, colour = "darkblue") + scale_x_date(date_breaks = "12 months",
    labels = date_format("%b/%Y")) + theme(axis.text.x = element_text(angle = 60,
    vjust = 1, hjust = 1)) + labs(title = "VALE3.SA", caption = "Fonte: Dados do Yahoo Finance.") +
    xlab("Mês/Ano") + ylab("R$")

g2 <- autoplot(petro) + geom_line(size = 0.8, colour = "darkblue") + scale_x_date(date_breaks = "12 months",
    labels = date_format("%b/%Y")) + theme(axis.text.x = element_text(angle = 60,
    vjust = 1, hjust = 1)) + labs(title = "PETR4.SA", caption = "Fonte: Dados do Yahoo Finance.") +
    xlab("Mês/Ano") + ylab("R$")
# os dois graficos de cotacoes juntos
grid.arrange(g1, g2, nrow = 2)

2.2 Checando a periodicidade dos dados

# Checando a periodicidade dos dados 2000-01-01 até 2019-08-01
periodicity(petro)
Monthly periodicity from 2000-02-01 to 2019-08-01 
periodicity(vale)
Monthly periodicity from 2000-02-01 to 2019-08-01 

2.3 Retornos mensais PETR4 e VALE3 com base no preço ajustado

r_petro <- monthlyReturn(petro, type = "log")[-1, ]
r_vale <- monthlyReturn(vale, type = "log")[-1, ]
g3 <- autoplot(r_petro) + geom_line(size = 0.8, colour = "darkblue") + scale_x_date(date_breaks = "12 months",
    labels = date_format("%b/%Y")) + theme(axis.text.x = element_text(angle = 60,
    vjust = 1, hjust = 1)) + labs(title = "Retornos da PETR4.SA", caption = "Fonte: Dados do Yahoo Finance.") +
    xlab("Mês/Ano") + ylab("")

g4 <- autoplot(r_vale) + geom_line(size = 0.8, colour = "darkblue") + scale_x_date(date_breaks = "12 months",
    labels = date_format("%b/%Y")) + theme(axis.text.x = element_text(angle = 60,
    vjust = 1, hjust = 1)) + labs(title = "Retornos da VALE3.SA", caption = "Fonte: Dados do Yahoo Finance.") +
    xlab("Mês/Ano") + ylab("")
# os dois graficos de cotacoes juntos
grid.arrange(g3, g4, nrow = 2)

3 Forecast

3.1 Delimitando treino x teste

# transformando em ts
r_petro <- ts(r_petro, start = c(2000, 3), frequency = 12)
r_vale <- ts(r_vale, start = c(2000, 3), frequency = 12)

# treino até setembro 2015 (189 meses)
r_petro_treino <- window(r_petro, start = c(2000, 3), end = c(2015, 10))
r_vale_treino <- window(r_vale, start = c(2000, 3), end = c(2015, 10))
# teste (45 meses após outubro de 2019)
r_petro_teste <- window(r_petro, start = c(2015, 11))
r_vale_teste <- window(r_vale, start = c(2015, 11))

Vamos checar se separamos corretamente:

autoplot(r_petro) + labs(title = "Retornos da PETR4.SA", caption = "Fonte: Dados do Yahoo Finance.") +
    autolayer(r_petro_treino, series = "Training") + autolayer(r_petro_teste, series = "Test")

autoplot(r_vale) + labs(title = "Retornos da VALE3.SA", caption = "Fonte: Dados do Yahoo Finance.") +
    autolayer(r_vale_treino, series = "Training") + autolayer(r_vale_teste, series = "Test")

3.2 Holt-Winters

Farei a previsão pelo método Holt-Winters conforme Hyndman e Athanasopoulos (2018).

# Holt-Winters petro
petro.hw <- forecast::hw(r_petro_treino, h = 60, seasonal = "additive")
summary(petro.hw)

Forecast method: Holt-Winters' additive method

Model Information:
Holt-Winters' additive method 

Call:
 forecast::hw(y = r_petro_treino, h = 60, seasonal = "additive") 

  Smoothing parameters:
    alpha = 2e-04 
    beta  = 1e-04 
    gamma = 3e-04 

  Initial states:
    l = 0.0408 
    b = -3e-04 
    s = 0.0158 -0.0179 0.0289 -0.0261 -0.0139 -0.0133
           0.0273 -0.0072 -0.0187 0.0098 0.0324 -0.0171

  sigma:  0.1086

     AIC     AICc      BIC 
166.9215 170.5215 221.9411 

Error measures:
                       ME      RMSE        MAE      MPE     MAPE      MASE
Training set -0.003949467 0.1038605 0.08178791 129.5021 158.7936 0.7084127
                   ACF1
Training set 0.07341328

Forecasts:
         Point Forecast      Lo 80      Hi 80      Lo 95     Hi 95
Nov 2015  -0.0513833351 -0.1905392 0.08777248 -0.2642038 0.1614371
Dec 2015   0.0032744501 -0.1358814 0.14243027 -0.2095460 0.2160949
Jan 2016  -0.0438779452 -0.1830338 0.09527789 -0.2566984 0.1689425
Feb 2016  -0.0106221283 -0.1497780 0.12853372 -0.2234426 0.2021984
Mar 2016  -0.0437553412 -0.1829112 0.09540053 -0.2565759 0.1690652
Apr 2016   0.0051847490 -0.1339712 0.14434065 -0.2076358 0.2180053
May 2016  -0.0176256470 -0.1567816 0.12153030 -0.2304463 0.1951950
Jun 2016  -0.0465306634 -0.1856867 0.09262534 -0.2593514 0.1662901
Jul 2016  -0.0355054985 -0.1746616 0.10365057 -0.2483263 0.1773153
Aug 2016  -0.0012281328 -0.1403843 0.13792802 -0.2140491 0.2115928
Sep 2016  -0.0422636362 -0.1814199 0.09689261 -0.2550847 0.1705575
Oct 2016  -0.0431197492 -0.1822761 0.09603661 -0.2559410 0.1697015
Nov 2016  -0.0557047815 -0.1948613 0.08345177 -0.2685264 0.1571168
Dec 2016  -0.0010469964 -0.1402037 0.13810971 -0.2138688 0.2117748
Jan 2017  -0.0481993917 -0.1873563 0.09095749 -0.2610215 0.1646227
Feb 2017  -0.0149435748 -0.1541006 0.12421350 -0.2277660 0.1978788
Mar 2017  -0.0480767877 -0.1872341 0.09108051 -0.2608995 0.1647459
Apr 2017   0.0008633025 -0.1382942 0.14002084 -0.2119598 0.2136864
May 2017  -0.0219470935 -0.1611049 0.11721072 -0.2347706 0.1908764
Jun 2017  -0.0508521099 -0.1900102 0.08830601 -0.2636761 0.1619719
 [ reached 'max' / getOption("max.print") -- omitted 40 rows ]
autoplot(r_petro) + forecast::autolayer(petro.hw, series = "Holt-Winters adit", PI = FALSE) +
    xlab("Ano") + ylab("Retorno") + ggtitle("Forecasts para PETR4") + guides(colour = guide_legend(title = "Forecast"))

# Holt-Winters vale
vale.hw <- forecast::hw(r_vale_treino, h = 60, seasonal = "additive")
summary(vale.hw)

Forecast method: Holt-Winters' additive method

Model Information:
Holt-Winters' additive method 

Call:
 forecast::hw(y = r_vale_treino, h = 60, seasonal = "additive") 

  Smoothing parameters:
    alpha = 0.0109 
    beta  = 0.0014 
    gamma = 1e-04 

  Initial states:
    l = 0.0286 
    b = 7e-04 
    s = 0.022 -0.0011 0.035 -0.0145 0.0084 0.0394
           -0.0299 0.0282 -0.039 0.009 -0.0177 -0.0399

  sigma:  0.0912

     AIC     AICc      BIC 
101.2739 104.8739 156.2934 

Error measures:
                       ME       RMSE        MAE  MPE MAPE      MASE        ACF1
Training set -0.003640175 0.08722178 0.06622657 -Inf  Inf 0.7118127 -0.01658934

Forecasts:
         Point Forecast      Lo 80      Hi 80      Lo 95     Hi 95
Nov 2015  -0.0467842020 -0.1636469 0.07007845 -0.2255102 0.1319418
Dec 2015   0.0024211917 -0.1144503 0.11929273 -0.1763184 0.1811608
Jan 2016  -0.0340134802 -0.1508960 0.08286908 -0.2127699 0.1447430
Feb 2016  -0.0111219449 -0.1280179 0.10577401 -0.1898989 0.1676550
Mar 2016  -0.0733052710 -0.1902172 0.04360668 -0.2521067 0.1054961
Apr 2016  -0.0514188823 -0.1683496 0.06551188 -0.2302491 0.1274113
May 2016  -0.0249567178 -0.1419094 0.09199592 -0.2038203 0.1539069
Jun 2016  -0.0732887742 -0.1902666 0.04368902 -0.2521909 0.1056133
Jul 2016  -0.0063620942 -0.1233686 0.11064438 -0.1853080 0.1725839
Aug 2016  -0.0647846044 -0.1818235 0.05225428 -0.2437801 0.1142109
Sep 2016   0.0042778563 -0.1127974 0.12135312 -0.1747733 0.1833290
Oct 2016  -0.0270192839 -0.1441351 0.09009656 -0.2061325 0.1520939
Nov 2016  -0.0501072568 -0.1672684 0.06705391 -0.2292898 0.1290753
Dec 2016  -0.0009018631 -0.1181127 0.11630895 -0.1801603 0.1783566
Jan 2017  -0.0373365350 -0.1546019 0.07992878 -0.2166784 0.1420053
Feb 2017  -0.0144449997 -0.1317699 0.10287992 -0.1938780 0.1649880
Mar 2017  -0.0766283258 -0.1940182 0.04076151 -0.2561606 0.1029039
Apr 2017  -0.0547419371 -0.1722022 0.06271834 -0.2343819 0.1248981
May 2017  -0.0282797726 -0.1458163 0.08925671 -0.2080363 0.1514768
Jun 2017  -0.0766118290 -0.1942305 0.04100682 -0.2564940 0.1032704
 [ reached 'max' / getOption("max.print") -- omitted 40 rows ]
autoplot(r_vale) + forecast::autolayer(vale.hw, series = "Holt-Winters adit", PI = FALSE) +
    xlab("Ano") + ylab("Retorno") + ggtitle("Forecasts para VALE3") + guides(colour = guide_legend(title = "Forecast"))

3.2.1 Acurácia

Metrics::smape(r_petro_teste, petro.hw$mean)
[1] 1.562604
Metrics::smape(r_vale_teste, vale.hw$mean)
[1] 1.544907
Metrics::mape(r_petro_teste, petro.hw$mean)
[1] 1.756591
Metrics::mape(r_vale_teste, vale.hw$mean)
[1] 1.305928

3.3 ETS

Farei a previsão pelo método ETS (Error Trend Seasonal) conforme Hyndman e Athanasopoulos (2018).

# ETS (error trend seasonal) de Hyndman - pacote fpp2
set.seed(123)
petro.ets <- ets(r_petro_treino, model = "ZZZ")  # N=none, A=additive, M=multiplicative e Z=automatic
summary(petro.ets)
ETS(A,N,N) 

Call:
 ets(y = r_petro_treino, model = "ZZZ") 

  Smoothing parameters:
    alpha = 0.0138 

  Initial states:
    l = 0.0089 

  sigma:  0.1061

     AIC     AICc      BIC 
144.7746 144.9051 154.4840 

Training set error measures:
                       ME    RMSE        MAE      MPE     MAPE      MASE
Training set -0.006397972 0.10549 0.08270149 82.99026 126.7402 0.7163257
                   ACF1
Training set 0.07282587
petro.ets.forecasts <- forecast.ets(petro.ets, h = 12)
summary(petro.ets.forecasts)

Forecast method: ETS(A,N,N)

Model Information:
ETS(A,N,N) 

Call:
 ets(y = r_petro_treino, model = "ZZZ") 

  Smoothing parameters:
    alpha = 0.0138 

  Initial states:
    l = 0.0089 

  sigma:  0.1061

     AIC     AICc      BIC 
144.7746 144.9051 154.4840 

Error measures:
                       ME    RMSE        MAE      MPE     MAPE      MASE
Training set -0.006397972 0.10549 0.08270149 82.99026 126.7402 0.7163257
                   ACF1
Training set 0.07282587

Forecasts:
         Point Forecast      Lo 80     Hi 80      Lo 95     Hi 95
Nov 2015   -0.007711588 -0.1436273 0.1282041 -0.2155767 0.2001536
Dec 2015   -0.007711588 -0.1436403 0.1282171 -0.2155966 0.2001734
Jan 2016   -0.007711588 -0.1436533 0.1282301 -0.2156165 0.2001933
Feb 2016   -0.007711588 -0.1436663 0.1282431 -0.2156364 0.2002132
Mar 2016   -0.007711588 -0.1436793 0.1282561 -0.2156563 0.2002331
Apr 2016   -0.007711588 -0.1436923 0.1282691 -0.2156761 0.2002530
May 2016   -0.007711588 -0.1437053 0.1282821 -0.2156960 0.2002728
Jun 2016   -0.007711588 -0.1437183 0.1282951 -0.2157159 0.2002927
Jul 2016   -0.007711588 -0.1437313 0.1283081 -0.2157358 0.2003126
Aug 2016   -0.007711588 -0.1437443 0.1283211 -0.2157556 0.2003325
Sep 2016   -0.007711588 -0.1437573 0.1283341 -0.2157755 0.2003523
Oct 2016   -0.007711588 -0.1437703 0.1283471 -0.2157954 0.2003722
plot(petro.ets.forecasts, col = 2)

# ETS (error trend seasonal) de Hyndman - pacote fpp2
set.seed(123)
vale.ets <- ets(r_vale_treino, model = "ZZZ")  # N=none, A=additive, M=multiplicative e Z=automatic
summary(vale.ets)
ETS(A,N,N) 

Call:
 ets(y = r_vale_treino, model = "ZZZ") 

  Smoothing parameters:
    alpha = 0.0228 

  Initial states:
    l = 0.0217 

  sigma:  0.0919

      AIC      AICc       BIC 
 90.73400  90.86444 100.44333 

Training set error measures:
                       ME       RMSE        MAE  MPE MAPE      MASE        ACF1
Training set -0.007129218 0.09136758 0.07035817 -Inf  Inf 0.7562197 -0.05975729
vale.ets.forecasts <- forecast.ets(vale.ets, h = 12)
summary(vale.ets.forecasts)

Forecast method: ETS(A,N,N)

Model Information:
ETS(A,N,N) 

Call:
 ets(y = r_vale_treino, model = "ZZZ") 

  Smoothing parameters:
    alpha = 0.0228 

  Initial states:
    l = 0.0217 

  sigma:  0.0919

      AIC      AICc       BIC 
 90.73400  90.86444 100.44333 

Error measures:
                       ME       RMSE        MAE  MPE MAPE      MASE        ACF1
Training set -0.007129218 0.09136758 0.07035817 -Inf  Inf 0.7562197 -0.05975729

Forecasts:
         Point Forecast      Lo 80     Hi 80      Lo 95     Hi 95
Nov 2015   -0.008831456 -0.1265516 0.1088887 -0.1888688 0.1712059
Dec 2015   -0.008831456 -0.1265821 0.1089192 -0.1889155 0.1712526
Jan 2016   -0.008831456 -0.1266126 0.1089497 -0.1889622 0.1712993
Feb 2016   -0.008831456 -0.1266431 0.1089802 -0.1890089 0.1713460
Mar 2016   -0.008831456 -0.1266737 0.1090108 -0.1890556 0.1713926
Apr 2016   -0.008831456 -0.1267042 0.1090413 -0.1891022 0.1714393
May 2016   -0.008831456 -0.1267347 0.1090718 -0.1891489 0.1714859
Jun 2016   -0.008831456 -0.1267652 0.1091022 -0.1891955 0.1715326
Jul 2016   -0.008831456 -0.1267956 0.1091327 -0.1892421 0.1715792
Aug 2016   -0.008831456 -0.1268261 0.1091632 -0.1892887 0.1716258
Sep 2016   -0.008831456 -0.1268566 0.1091937 -0.1893353 0.1716724
Oct 2016   -0.008831456 -0.1268870 0.1092241 -0.1893819 0.1717190
plot(vale.ets.forecasts, col = 2)

3.3.1 Acurácia

Metrics::smape(r_petro_teste, petro.ets.forecasts$mean)
[1] 1.829682
Metrics::smape(r_vale_teste, vale.ets.forecasts$mean)
[1] 1.788367
Metrics::mape(r_petro_teste, petro.ets.forecasts$mean)
[1] 0.9917465
Metrics::mape(r_vale_teste, vale.ets.forecasts$mean)
[1] 0.9537846

Referências

FIGUEIREDO, A.M.R. Tópicos de econometria: ações. UFMS:Campo Grande,2019. Disponível em: http://rpubs.com/amrofi/acoesBOV_VALE3.

HYNDMAN, Rob. (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.

HYNDMAN, Rob J.; ATHANASOPOULOS, George . Forecasting: principles and practice. Otexts, 2018. Second Edition. Disponível em: https://www.otexts.org/fpp2.

LS0tDQp0aXRsZTogIlPDqXJpZXMgVGVtcG9yYWlzOiBwcmV2aXPDo28gZGUgYcOnw7VlcyBjb20gRVRTIg0KYXV0aG9yOiAiQWRyaWFubyBNYXJjb3MgUm9kcmlndWVzIEZpZ3VlaXJlZG8sICplLW1haWw6IGFkcmlhbm8uZmlndWVpcmVkb0B1Zm1zLmJyKiINCmxpbmtjb2xvcjogYmx1ZQ0KYWJzdHJhY3Q6IA0KICBUaGlzIGlzIGFuIHVuZGVyZ3JhZCBzdHVkZW50IGxldmVsIGluc3RydWN0aW9uIGZvciBjbGFzcyB1c2UuICANCmRhdGU6ICJgciBmb3JtYXQoU3lzLkRhdGUoKSwgJyVkICVCICVZJylgIg0Kb3V0cHV0Og0KICBodG1sX2RvY3VtZW50Og0KICAgIGNvZGVfZG93bmxvYWQ6IHRydWUNCiAgICB0aGVtZTogZGVmYXVsdA0KICAgIG51bWJlcl9zZWN0aW9uczogdHJ1ZQ0KICAgIHRvYzogeWVzDQogICAgdG9jX2Zsb2F0OiB5ZXMNCiAgICBkZl9wcmludDogcGFnZWQNCiAgICBmaWdfY2FwdGlvbjogdHJ1ZQ0KICBybWRmb3JtYXRzOjpyZWFkdGhlZG93bjoNCiAgICBzZWxmX2NvbnRhaW5lZDogdHJ1ZQ0KICAgIHRodW1ibmFpbHM6IHRydWUNCiAgICBsaWdodGJveDogdHJ1ZQ0KICAgIGdhbGxlcnk6IGZhbHNlDQogICAgaGlnaGxpZ2h0OiAidGFuZ28iDQogICAgdXNlX2Jvb2tkb3duOiB0cnVlDQogICAgdG9jX2RlcHRoOiA0DQogICAgbnVtYmVyX3NlY3Rpb25zOiBUUlVFDQogICAgZGZfcHJpbnQ6IHBhZ2VkDQogIGh0bWxfbm90ZWJvb2s6DQogICAgaGlnaGxpZ2h0OiAidGFuZ28iDQogICAgdGhlbWU6IGpvdXJuYWwNCiAgICBudW1iZXJfc2VjdGlvbnM6IHRydWUNCiAgICB0b2M6IHllcw0KICAgIHRvY19mbG9hdDogeWVzDQogICAgZGZfcHJpbnQ6IHBhZ2VkDQogIHBkZl9kb2N1bWVudDoNCiAgICB0b2M6IHllcw0KZ3JhcGhpY3M6IHllcw0KLS0tDQoNCg0KYGBge3Iga25pdHJfaW5pdCwgZWNobz1GQUxTRSwgY2FjaGU9RkFMU0V9DQpsaWJyYXJ5KGtuaXRyKQ0KbGlicmFyeShybWFya2Rvd24pDQpsaWJyYXJ5KHJtZGZvcm1hdHMpDQoNCiMjIEdsb2JhbCBvcHRpb25zDQpvcHRpb25zKG1heC5wcmludD0iMTAwIikNCm9wdHNfY2h1bmskc2V0KGVjaG89VFJVRSwNCgkgICAgICAgICAgICAgI2NhY2hlPVRSVUUsDQogICAgICAgICAgICAgICBwcm9tcHQ9RkFMU0UsDQogICAgICAgICAgICAgICB0aWR5PVRSVUUsDQogICAgICAgICAgICAgICBjb21tZW50PU5BLA0KICAgICAgICAgICAgICAgbWVzc2FnZT1GQUxTRSwNCiAgICAgICAgICAgICAgIHdhcm5pbmc9RkFMU0UpDQpvcHRzX2tuaXQkc2V0KHdpZHRoPTEwMCkNCmBgYA0KDQoNCkxpY2Vuw6dhIHstI0xpY2Vuw6dhfQ0KPT09PT09PT09PT09PT09PT09PQ0KDQpUaGlzIHdvcmsgaXMgbGljZW5zZWQgdW5kZXIgdGhlIENyZWF0aXZlIENvbW1vbnMgQXR0cmlidXRpb24tU2hhcmVBbGlrZSA0LjAgSW50ZXJuYXRpb25hbCBMaWNlbnNlLiBUbyB2aWV3IGEgY29weSBvZiB0aGlzIGxpY2Vuc2UsIHZpc2l0IDxodHRwOi8vY3JlYXRpdmVjb21tb25zLm9yZy9saWNlbnNlcy9ieS1zYS80LjAvPiBvciBzZW5kIGEgbGV0dGVyIHRvIENyZWF0aXZlIENvbW1vbnMsIFBPIEJveCAxODY2LCBNb3VudGFpbiBWaWV3LCBDQSA5NDA0MiwgVVNBLg0KDQohW0xpY2Vuc2U6IENDIEJZLVNBIDQuMF0oaHR0cHM6Ly9taXJyb3JzLmNyZWF0aXZlY29tbW9ucy5vcmcvcHJlc3NraXQvYnV0dG9ucy84OHgzMS9wbmcvYnktc2EucG5nKXsgd2lkdGg9MjUlIH0NCg0KQ2l0YcOnw6NvIHstI0NpdGHDp8Ojb30NCj09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09DQoNClN1Z2VzdMOjbyBkZSBjaXRhw6fDo286DQpGSUdVRUlSRURPLCBBZHJpYW5vIE1hcmNvcyBSb2RyaWd1ZXMuIFPDqXJpZXMgVGVtcG9yYWlzOiBwcmV2aXPDo28gZGUgYcOnw7VlcyBjb20gRVRTLiBDYW1wbyBHcmFuZGUtTVMsQnJhc2lsOiBSU3R1ZGlvL1JwdWJzLCAyMDE5LiBEaXNwb27DrXZlbCBlbSA8aHR0cDovL3JwdWJzLmNvbS9hbXJvZmkvc3RvY2tzX0VUUz4uIA0KDQpJbnRyb2R1w6fDo28NCj09PT09PT09PT09PT09PT09PT0NCg0KRGFkb3MgZSBncsOhZmljbw0KPT09PT09PT09PT09PT09PT09PT09PT0NCg0KIyMgVkFMRTMgZSBQRVRSNA0KDQpgYGB7cn0NCmxpYnJhcnkocXVhbnRtb2QpDQpsaWJyYXJ5KGdncGxvdDIpDQpsaWJyYXJ5KHNjYWxlcykNCmxpYnJhcnkoZ2d0aGVtZXMpDQpsaWJyYXJ5KGdyaWRFeHRyYSkgIyBwYXJhIHZhaW9zIGdyYWZpY29zIGp1bnRvcw0KbGlicmFyeShkeWdyYXBocykgICMgcGFyYSBvcyBncmFmaWNvcyBkaW5hbWljb3MNCmxpYnJhcnkoZnBwMikgICAgICAjIHBhcmEgZmF6ZXIgbyBFVFMNCmxpYnJhcnkoTWV0cmljcykgICAjIHBhcmEgZmF6ZXIgYWN1csOhY2lhDQplbnYgPSBuZXcuZW52KCkNCiMgcGVnYW5kbyBjb3Rhw6fDtWVzIG1lbnNhaXMgZGEgcGV0cm9icmFzIGUgZG8gaWJvdmVzcGENCmdldFN5bWJvbHMoYygiUEVUUjQuU0EiLCJWQUxFMy5TQSIpLA0KICAgICAgICAgICBzcmM9InlhaG9vIiwgDQogICAgICAgICAgIGVudj1lbnYsDQogICAgICAgICAgIHBlcmlvZGljaXR5PSdtb250aGx5JywgDQogICAgICAgICAgIGZyb209JzIwMDAtMDEtMDEnLA0KICAgICAgICAgICB0bz0nMjAxOS0wOS0wMScNCikNCnZhbGUgPSBlbnYkVkFMRTMuU0FbLDZdDQpwZXRybyA9IGVudiRQRVRSNC5TQVssNl0NCg0KZzE8LWF1dG9wbG90KHZhbGUpKw0KICBnZW9tX2xpbmUoc2l6ZT0uOCwgY29sb3VyPSdkYXJrYmx1ZScpKw0KICBzY2FsZV94X2RhdGUoZGF0ZV9icmVha3MgPSAnMTIgbW9udGhzJywNCiAgICAgICAgICAgICAgIGxhYmVscyA9IGRhdGVfZm9ybWF0KCIlYi8lWSIpKSsNCiAgdGhlbWUoYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoYW5nbGUgPSA2MCwgdmp1c3QgPSAxLjAsIGhqdXN0ID0gMS4wKSkrDQogIGxhYnModGl0bGU9J1ZBTEUzLlNBJywNCiAgICAgICBjYXB0aW9uPSdGb250ZTogRGFkb3MgZG8gWWFob28gRmluYW5jZS4nKSsNCiAgeGxhYignTcOqcy9Bbm8nKSt5bGFiKCdSJCcpDQoNCmcyPC1hdXRvcGxvdChwZXRybykrDQogIGdlb21fbGluZShzaXplPS44LCBjb2xvdXI9J2RhcmtibHVlJykrDQogIHNjYWxlX3hfZGF0ZShkYXRlX2JyZWFrcyA9ICcxMiBtb250aHMnLA0KICAgICAgICAgICAgICAgbGFiZWxzID0gZGF0ZV9mb3JtYXQoIiViLyVZIikpKw0KICB0aGVtZShheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChhbmdsZSA9IDYwLCB2anVzdCA9IDEuMCwgaGp1c3QgPSAxLjApKSsNCiAgbGFicyh0aXRsZT0nUEVUUjQuU0EnLA0KICAgICAgIGNhcHRpb249J0ZvbnRlOiBEYWRvcyBkbyBZYWhvbyBGaW5hbmNlLicpKw0KICB4bGFiKCdNw6pzL0FubycpK3lsYWIoJ1IkJykNCiMgb3MgZG9pcyBncmFmaWNvcyBkZSBjb3RhY29lcyBqdW50b3MgIA0KZ3JpZC5hcnJhbmdlKGcxLCBnMiwgbnJvdyA9IDIpDQpgYGANCg0KIyMgQ2hlY2FuZG8gYSBwZXJpb2RpY2lkYWRlIGRvcyBkYWRvcw0KDQpgYGB7cn0NCiMgQ2hlY2FuZG8gYSBwZXJpb2RpY2lkYWRlIGRvcyBkYWRvcw0KIyAyMDAwLTAxLTAxIGF0w6kgMjAxOS0wOC0wMQ0KcGVyaW9kaWNpdHkocGV0cm8pDQpwZXJpb2RpY2l0eSh2YWxlKQ0KYGBgDQoNCiMjIFJldG9ybm9zIG1lbnNhaXMgUEVUUjQgZSBWQUxFMyBjb20gYmFzZSBubyBwcmXDp28gYWp1c3RhZG8NCg0KYGBge3J9DQpyX3BldHJvIDwtIG1vbnRobHlSZXR1cm4ocGV0cm8sdHlwZT0nbG9nJylbLTEsXQ0Kcl92YWxlIDwtIG1vbnRobHlSZXR1cm4odmFsZSx0eXBlPSJsb2ciKVstMSxdDQpgYGANCg0KYGBge3J9DQpnMzwtYXV0b3Bsb3Qocl9wZXRybykrDQogIGdlb21fbGluZShzaXplPS44LCBjb2xvdXI9J2RhcmtibHVlJykrDQogIHNjYWxlX3hfZGF0ZShkYXRlX2JyZWFrcyA9ICcxMiBtb250aHMnLA0KICAgICAgICAgICAgICAgbGFiZWxzID0gZGF0ZV9mb3JtYXQoIiViLyVZIikpKw0KICB0aGVtZShheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChhbmdsZSA9IDYwLCB2anVzdCA9IDEuMCwgaGp1c3QgPSAxLjApKSsNCiAgbGFicyh0aXRsZT0nUmV0b3Jub3MgZGEgUEVUUjQuU0EnLA0KICAgICAgIGNhcHRpb249J0ZvbnRlOiBEYWRvcyBkbyBZYWhvbyBGaW5hbmNlLicpKw0KICB4bGFiKCdNw6pzL0FubycpK3lsYWIoJycpDQoNCmc0PC1hdXRvcGxvdChyX3ZhbGUpKw0KICBnZW9tX2xpbmUoc2l6ZT0uOCwgY29sb3VyPSdkYXJrYmx1ZScpKw0KICBzY2FsZV94X2RhdGUoZGF0ZV9icmVha3MgPSAnMTIgbW9udGhzJywNCiAgICAgICAgICAgICAgIGxhYmVscyA9IGRhdGVfZm9ybWF0KCIlYi8lWSIpKSsNCiAgdGhlbWUoYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoYW5nbGUgPSA2MCwgdmp1c3QgPSAxLjAsIGhqdXN0ID0gMS4wKSkrDQogIGxhYnModGl0bGU9J1JldG9ybm9zIGRhIFZBTEUzLlNBJywNCiAgICAgICBjYXB0aW9uPSdGb250ZTogRGFkb3MgZG8gWWFob28gRmluYW5jZS4nKSsNCiAgeGxhYignTcOqcy9Bbm8nKSt5bGFiKCcnKQ0KIyBvcyBkb2lzIGdyYWZpY29zIGRlIGNvdGFjb2VzIGp1bnRvcyAgDQpncmlkLmFycmFuZ2UoZzMsIGc0LCBucm93ID0gMikNCmBgYA0KDQpGb3JlY2FzdA0KPT09PT09PT09PT09PT09PT09PT09PT0NCg0KIyMgRGVsaW1pdGFuZG8gdHJlaW5vIHggdGVzdGUNCmBgYHtyfQ0KDQojIHRyYW5zZm9ybWFuZG8gZW0gdHMNCnJfcGV0cm8gPC0gdHMocl9wZXRybywgc3RhcnQgPSBjKDIwMDAsIDMpLCBmcmVxdWVuY3kgPSAxMikNCnJfdmFsZSA8LSB0cyhyX3ZhbGUsIHN0YXJ0ID0gYygyMDAwLCAzKSwgZnJlcXVlbmN5ID0gMTIpDQoNCiMgdHJlaW5vIGF0w6kgc2V0ZW1icm8gMjAxNSAoMTg5IG1lc2VzKQ0Kcl9wZXRyb190cmVpbm8gPC0gd2luZG93KHJfcGV0cm8sIHN0YXJ0ID0gYygyMDAwLCAzKSwgZW5kID0gYygyMDE1LCAxMCkpDQpyX3ZhbGVfdHJlaW5vIDwtIHdpbmRvdyhyX3ZhbGUsIHN0YXJ0ID0gYygyMDAwLCAzKSwgZW5kID0gYygyMDE1LCAxMCkpDQojIHRlc3RlICg0NSBtZXNlcyBhcMOzcyBvdXR1YnJvIGRlIDIwMTkpDQpyX3BldHJvX3Rlc3RlIDwtIHdpbmRvdyhyX3BldHJvLCBzdGFydCA9IGMoMjAxNSwgMTEpKQ0Kcl92YWxlX3Rlc3RlIDwtIHdpbmRvdyhyX3ZhbGUsIHN0YXJ0ID0gYygyMDE1LCAxMSkpDQpgYGANCg0KVmFtb3MgY2hlY2FyIHNlIHNlcGFyYW1vcyBjb3JyZXRhbWVudGU6DQpgYGB7cn0NCmF1dG9wbG90KHJfcGV0cm8pICsNCiAgbGFicyh0aXRsZT0nUmV0b3Jub3MgZGEgUEVUUjQuU0EnLA0KICAgICAgIGNhcHRpb249J0ZvbnRlOiBEYWRvcyBkbyBZYWhvbyBGaW5hbmNlLicpKw0KICBhdXRvbGF5ZXIocl9wZXRyb190cmVpbm8sIHNlcmllcz0iVHJhaW5pbmciKSArDQogIGF1dG9sYXllcihyX3BldHJvX3Rlc3RlLCBzZXJpZXM9IlRlc3QiKQ0KYGBgDQoNCmBgYHtyfQ0KYXV0b3Bsb3Qocl92YWxlKSArDQogIGxhYnModGl0bGU9J1JldG9ybm9zIGRhIFZBTEUzLlNBJywNCiAgICAgICBjYXB0aW9uPSdGb250ZTogRGFkb3MgZG8gWWFob28gRmluYW5jZS4nKSsNCiAgYXV0b2xheWVyKHJfdmFsZV90cmVpbm8sIHNlcmllcz0iVHJhaW5pbmciKSArDQogIGF1dG9sYXllcihyX3ZhbGVfdGVzdGUsIHNlcmllcz0iVGVzdCIpDQpgYGANCg0KIyMgSG9sdC1XaW50ZXJzDQoNCkZhcmVpIGEgcHJldmlzw6NvIHBlbG8gbcOpdG9kbyBIb2x0LVdpbnRlcnMgY29uZm9ybWUgSHluZG1hbiBlIEF0aGFuYXNvcG91bG9zICgyMDE4KS4NCg0KYGBge3J9DQojIEhvbHQtV2ludGVycyBwZXRybw0KcGV0cm8uaHcgPC0gZm9yZWNhc3Q6Omh3KHJfcGV0cm9fdHJlaW5vLCBoPTYwLHNlYXNvbmFsPSJhZGRpdGl2ZSIpIA0Kc3VtbWFyeShwZXRyby5odykNCmF1dG9wbG90KHJfcGV0cm8pICsNCiAgZm9yZWNhc3Q6OmF1dG9sYXllcihwZXRyby5odywgc2VyaWVzPSJIb2x0LVdpbnRlcnMgYWRpdCIsIFBJPUZBTFNFKSArDQogIHhsYWIoIkFubyIpICsgeWxhYigiUmV0b3JubyIpICsNCiAgZ2d0aXRsZSgiRm9yZWNhc3RzIHBhcmEgUEVUUjQiKSArDQogIGd1aWRlcyhjb2xvdXI9Z3VpZGVfbGVnZW5kKHRpdGxlPSJGb3JlY2FzdCIpKQ0KYGBgDQoNCmBgYHtyfQ0KIyBIb2x0LVdpbnRlcnMgdmFsZQ0KdmFsZS5odyA8LSBmb3JlY2FzdDo6aHcocl92YWxlX3RyZWlubywgaD02MCxzZWFzb25hbD0iYWRkaXRpdmUiKSANCnN1bW1hcnkodmFsZS5odykNCmF1dG9wbG90KHJfdmFsZSkgKw0KICBmb3JlY2FzdDo6YXV0b2xheWVyKHZhbGUuaHcsIHNlcmllcz0iSG9sdC1XaW50ZXJzIGFkaXQiLCBQST1GQUxTRSkgKw0KICB4bGFiKCJBbm8iKSArIHlsYWIoIlJldG9ybm8iKSArDQogIGdndGl0bGUoIkZvcmVjYXN0cyBwYXJhIFZBTEUzIikgKw0KICBndWlkZXMoY29sb3VyPWd1aWRlX2xlZ2VuZCh0aXRsZT0iRm9yZWNhc3QiKSkNCmBgYA0KDQojIyMgQWN1csOhY2lhDQoNCmBgYHtyfQ0KTWV0cmljczo6c21hcGUocl9wZXRyb190ZXN0ZSxwZXRyby5odyRtZWFuKQ0KTWV0cmljczo6c21hcGUocl92YWxlX3Rlc3RlLHZhbGUuaHckbWVhbikNCg0KTWV0cmljczo6bWFwZShyX3BldHJvX3Rlc3RlLHBldHJvLmh3JG1lYW4pDQpNZXRyaWNzOjptYXBlKHJfdmFsZV90ZXN0ZSx2YWxlLmh3JG1lYW4pDQpgYGANCg0KIyMgRVRTDQoNCkZhcmVpIGEgcHJldmlzw6NvIHBlbG8gbcOpdG9kbyBFVFMgKEVycm9yIFRyZW5kIFNlYXNvbmFsKSBjb25mb3JtZSBIeW5kbWFuIGUgQXRoYW5hc29wb3Vsb3MgKDIwMTgpLiAgICANCmBgYHtyfQ0KIyBFVFMgKGVycm9yIHRyZW5kIHNlYXNvbmFsKSBkZSBIeW5kbWFuIC0gcGFjb3RlIGZwcDINCnNldC5zZWVkKDEyMykNCnBldHJvLmV0czwtZXRzKHJfcGV0cm9fdHJlaW5vLCBtb2RlbCA9ICJaWloiKSAjIE49bm9uZSwgQT1hZGRpdGl2ZSwgTT1tdWx0aXBsaWNhdGl2ZSBlIFo9YXV0b21hdGljDQpzdW1tYXJ5KHBldHJvLmV0cykNCnBldHJvLmV0cy5mb3JlY2FzdHM8LWZvcmVjYXN0LmV0cyhwZXRyby5ldHMsaD0xMikNCnN1bW1hcnkocGV0cm8uZXRzLmZvcmVjYXN0cykNCnBsb3QocGV0cm8uZXRzLmZvcmVjYXN0cyxjb2w9MikNCmBgYA0KDQpgYGB7cn0NCiMgRVRTIChlcnJvciB0cmVuZCBzZWFzb25hbCkgZGUgSHluZG1hbiAtIHBhY290ZSBmcHAyDQpzZXQuc2VlZCgxMjMpDQp2YWxlLmV0czwtZXRzKHJfdmFsZV90cmVpbm8sIG1vZGVsID0gIlpaWiIpICMgTj1ub25lLCBBPWFkZGl0aXZlLCBNPW11bHRpcGxpY2F0aXZlIGUgWj1hdXRvbWF0aWMNCnN1bW1hcnkodmFsZS5ldHMpDQp2YWxlLmV0cy5mb3JlY2FzdHM8LWZvcmVjYXN0LmV0cyh2YWxlLmV0cyxoPTEyKQ0Kc3VtbWFyeSh2YWxlLmV0cy5mb3JlY2FzdHMpDQpwbG90KHZhbGUuZXRzLmZvcmVjYXN0cyxjb2w9MikNCmBgYA0KDQojIyMgQWN1csOhY2lhDQoNCmBgYHtyfQ0KTWV0cmljczo6c21hcGUocl9wZXRyb190ZXN0ZSxwZXRyby5ldHMuZm9yZWNhc3RzJG1lYW4pDQpNZXRyaWNzOjpzbWFwZShyX3ZhbGVfdGVzdGUsdmFsZS5ldHMuZm9yZWNhc3RzJG1lYW4pDQoNCk1ldHJpY3M6Om1hcGUocl9wZXRyb190ZXN0ZSxwZXRyby5ldHMuZm9yZWNhc3RzJG1lYW4pDQpNZXRyaWNzOjptYXBlKHJfdmFsZV90ZXN0ZSx2YWxlLmV0cy5mb3JlY2FzdHMkbWVhbikNCmBgYA0KDQpSZWZlcsOqbmNpYXMgey0jUmVmZXLDqm5jaWFzfQ0KPT09PT09PT09PT09PT09PT09PT09PT0NCg0KRklHVUVJUkVETywgQS5NLlIuIFTDs3BpY29zIGRlIGVjb25vbWV0cmlhOiBhw6fDtWVzLiBVRk1TOkNhbXBvIEdyYW5kZSwyMDE5LiBEaXNwb27DrXZlbCBlbTogPGh0dHA6Ly9ycHVicy5jb20vYW1yb2ZpL2Fjb2VzQk9WX1ZBTEUzPi4gIA0KDQpIWU5ETUFOLCBSb2IuICgyMDE4KS4gZnBwMjogRGF0YSBmb3IgIkZvcmVjYXN0aW5nOiBQcmluY2lwbGVzIGFuZCBQcmFjdGljZSIgKDJuZCBFZGl0aW9uKS4gUiBwYWNrYWdlIHZlcnNpb24gMi4zLiBEaXNwb27DrXZlbCBlbTogPGh0dHBzOi8vQ1JBTi5SLXByb2plY3Qub3JnL3BhY2thZ2U9ZnBwMj4uICANCg0KSFlORE1BTiwgUm9iIEouOyBBVEhBTkFTT1BPVUxPUywgR2VvcmdlIC4gRm9yZWNhc3Rpbmc6IHByaW5jaXBsZXMgYW5kIHByYWN0aWNlLiBPdGV4dHMsIDIwMTguIFNlY29uZCBFZGl0aW9uLiBEaXNwb27DrXZlbCBlbTogPGh0dHBzOi8vd3d3Lm90ZXh0cy5vcmcvZnBwMj4uDQoNCg==