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: Rotina rápida para lidar com missings no fabletools. Campo Grande-MS,Brasil: RStudio/Rpubs, 2021. Disponível em <http://rpubs.com/amrofi/missings_fabletools>.

1 Introdução

Segue uma rotina rápida para lidar com os missings (observações ausentes), com o pacote fabletools e tidyr.
Hyndman e Athanasopoulos (2020) no FPP 3, seção 13.9, mencionam o fato de ser comum termos séries temporais com dados ausentes, por exemplo, devido aos finais de semana, ou por outro motivo qualquer (esquecimento, ausência de coleta de dados, impedimento de negociação em bolsa, etc.). O caso mais importante que cito é aquele em que a série apresenta “falhas” implícitas. Ou seja, imagine um caso de um fim de semana (sábado e domingo) sem dados. A série terá então falhas no calendário. Seja o caso do mês de maio de 2021, teremos falhas em todos os dias em vermelho (Figura 1).

Figura 1. Maio de 2021

Uma tabela com dados diários para os dias úteis seria algo como:

Tabela 1. Dados aleatórios.

Observe que na Tabela 1, temos a data de 21/05/2021 vazia, e as demais estão preenchidas. O leitor poderia dizer que basta excluir a data de 21/05/2021 e não teremos mais nenhuma observação ausente (NA = not available = o nosso missing explícito). Mas temos missings implícitos, que não aparecem diretamente, que são os finais de semana (08, 09, 15, 16 de maio de 2021).

Então, aqui veremos como lidar com essas situações, pois o fable terá algumas restrições quando houver dados ausentes (explícitos ou implícitos), por exemplo, no ETS() e no STL(), conforme Hyndman e Athanasopoulos (2020) no FPP 3, seção 13.9.

A sugestão para este post é trabalhar com objetos tsibble, em consonância com Hyndman e Athanasopoulos (2020).

2 Dados para exemplo

Primeiro chamamos os dados. Eles estão embeded no code Rmd. Como gerei no Excel e a coluna estava já como formato de data, ao importar para o Excel (antes de eu gerar o dput), o R já entendeu que era coluna de data e mudou daquele formato da Tabela 1 (dd/mm/aaaa) para o formato (aaaa-mm-dd).

dados <- structure(list(Data = c("03/05/2021", "04/05/2021", "05/05/2021", "06/05/2021", 
    "07/05/2021", "10/05/2021", "11/05/2021", "12/05/2021", "13/05/2021", "14/05/2021", 
    "17/05/2021", "18/05/2021", "19/05/2021", "20/05/2021", "21/05/2021"), X = c(264, 
    281, 287, 284, 278, 281, 293, 292, 289, 294, 295, 293, 290, 288, NA)), row.names = c(NA, 
    -15L), class = c("tbl_df", "tbl", "data.frame"))
class(dados$Data)
[1] "character"

Veja que ele vem em data.frame. Então vamos gerar o tsibble.

Peço vossa atenção para o fato de que a coluna data nem sempre está em formato de data do R. NO chunk acima está como data.frame.

Caso você tenha uma coluna Data em formato de caracter (testar fazendo class(dados$Data)) então, antes de criar o tsibble, criar uma coluna date para facilitar a indexação conforme chunk abaixo.

dados$date <- as.Date(dados$Data, format = "%d/%m/%Y")
print(dados)
         Data   X       date
1  03/05/2021 264 2021-05-03
2  04/05/2021 281 2021-05-04
3  05/05/2021 287 2021-05-05
4  06/05/2021 284 2021-05-06
5  07/05/2021 278 2021-05-07
6  10/05/2021 281 2021-05-10
7  11/05/2021 293 2021-05-11
8  12/05/2021 292 2021-05-12
9  13/05/2021 289 2021-05-13
10 14/05/2021 294 2021-05-14
11 17/05/2021 295 2021-05-17
12 18/05/2021 293 2021-05-18
13 19/05/2021 290 2021-05-19
14 20/05/2021 288 2021-05-20
15 21/05/2021  NA 2021-05-21

Agora, além da coluna da data inicial (Data), temos a coluna (date).

Então criaremos o tsibble no próximo chunk. Chamaremos o pacote fable para executar a função as_tsibble() e colocaremos a coluna date (pode ser a Data desde que esteja como na structure do nosso primeiro chunk.

library(fable)
dados.tsb <- as_tsibble(dados[, c(3, 2)], index = date, regular = T)
class(dados.tsb)  # 'tbl_ts'     'tbl_df'     'tbl'        'data.frame'
[1] "tbl_ts"     "tbl_df"     "tbl"        "data.frame"
fabletools::autoplot(dados.tsb, X)

O leitor atento à mensagens de erro verá que aparece a mensagem Removed 1 row(s) containing missing values (geom_path). Ou seja, ele removeu automaticamente a linha do dia 2021-05-21, onde aparecia o NA na coluna de X. O objeto tsibble de nome dados.tsb foi criado. Neste exemplo, já exclui a coluna Data para não gerar confusão, e o index do tsibble é a coluna date.

O pacote já entendeu que a série é diária e uniu os pontos. Mas continuamos com as falhas dos finais de semana.

Portanto agora mostrarei duas formas de preencher os dados e recomendo ao leitor que consulte as opções do exemplo do manual do tsibble : Exemplos do fill_gaps no tsibble , onde existem opções para preencher com NAs, interpolar, preencher com zeros, preencher com a média, preencher com o último valor etc. As duas opções aqui desenvolvidas são: com NAs explícitos, e a de preencher com o último valor disponível.

3 Opção 1: preencher com NAs

Neste caso, o tsibble entenderá as datas que faltam, criará estas novas linhas e preencherá com o símbolo de missing padrão: NA. Encorajo o leitor a observar a tabela do print, e comparar com a tabela 1, e verá que agora a nova saída (dados.full) tem as datas inclusive para os finais de semana (sábado e domingo).

dados.full <- tsibble::fill_gaps(dados.tsb)
options(max.print = 100)
print(dados.full)
# A tsibble: 19 x 2 [1D]
   date           X
   <date>     <dbl>
 1 2021-05-03   264
 2 2021-05-04   281
 3 2021-05-05   287
 4 2021-05-06   284
 5 2021-05-07   278
 6 2021-05-08    NA
 7 2021-05-09    NA
 8 2021-05-10   281
 9 2021-05-11   293
10 2021-05-12   292
11 2021-05-13   289
12 2021-05-14   294
13 2021-05-15    NA
14 2021-05-16    NA
15 2021-05-17   295
16 2021-05-18   293
17 2021-05-19   290
18 2021-05-20   288
19 2021-05-21    NA

4 Opção 2: preencher com último valor

Neste caso, o tsibble entenderá as datas que faltam, criará estas novas linhas e preencherá com o último valor da série X, preenchendo para baixo (.direction = "down") com uso da função fill do pacote tidyr. Encorajo o leitor a observar a tabela do print, e comparar com a tabela 1, e verá que agora a nova saída (dados.full2) tem as datas inclusive para os finais de semana (sábado e domingo) e não constam NAs.

library(tidyr)
dados.full2 <- dados.tsb %>% tsibble::fill_gaps() %>% tidyr::fill(X, .direction = "down")
print(dados.full2)
# A tsibble: 19 x 2 [1D]
   date           X
   <date>     <dbl>
 1 2021-05-03   264
 2 2021-05-04   281
 3 2021-05-05   287
 4 2021-05-06   284
 5 2021-05-07   278
 6 2021-05-08   278
 7 2021-05-09   278
 8 2021-05-10   281
 9 2021-05-11   293
10 2021-05-12   292
11 2021-05-13   289
12 2021-05-14   294
13 2021-05-15   294
14 2021-05-16   294
15 2021-05-17   295
16 2021-05-18   293
17 2021-05-19   290
18 2021-05-20   288
19 2021-05-21   288

Deste modo, o leitor pode verificar que os valores de 08 e 09 de maio de 2021 foram preenchidos com o mesmo valor de 07 de maio de 2021. Do mesmo modo, para 15 e 16 de maio, teremos os valores de 14 de maio repetidos. O mesmo será feito para preencher o dia 21.

O gráfico será agora:

fabletools::autoplot(dados.full2, X)

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

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

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.

LS0tDQp0aXRsZTogIlPDqXJpZXMgVGVtcG9yYWlzOiBSb3RpbmEgcsOhcGlkYSBwYXJhIGxpZGFyIGNvbSBtaXNzaW5ncyBubyBmYWJsZXRvb2xzIg0KYXV0aG9yOiAiQWRyaWFubyBNYXJjb3MgUm9kcmlndWVzIEZpZ3VlaXJlZG8sICplLW1haWw6IGFkcmlhbm8uZmlndWVpcmVkb0B1Zm1zLmJyKiINCmxpbmtjb2xvcjogYmx1ZQ0KYWJzdHJhY3Q6IA0KICBUaGlzIGlzIGFuIHVuZGVyZ3JhZCBzdHVkZW50IGxldmVsIGluc3RydWN0aW9uIGZvciBjbGFzcyB1c2UuICANCmRhdGU6ICJgciBmb3JtYXQoU3lzLkRhdGUoKSwgJyVkICVCICVZJylgIg0Kb3V0cHV0Og0KICBodG1sX2RvY3VtZW50Og0KICAgIGNvZGVfZG93bmxvYWQ6IHRydWUNCiAgICB0aGVtZTogZGVmYXVsdA0KICAgIG51bWJlcl9zZWN0aW9uczogdHJ1ZQ0KICAgIHRvYzogeWVzDQogICAgdG9jX2Zsb2F0OiBubw0KICAgIGRmX3ByaW50OiBwYWdlZA0KICAgIGZpZ19jYXB0aW9uOiB0cnVlDQogIHBkZl9kb2N1bWVudDoNCiAgICB0b2M6IHllcw0KLS0tDQoNCmBgYHtyIGtuaXRyX2luaXQsIGVjaG89RkFMU0UsIGNhY2hlPUZBTFNFfQ0KbGlicmFyeShrbml0cikNCmxpYnJhcnkocm1hcmtkb3duKQ0KbGlicmFyeShybWRmb3JtYXRzKQ0KDQojIyBHbG9iYWwgb3B0aW9ucw0Kb3B0aW9ucyhtYXgucHJpbnQ9IjEwMCIpDQpvcHRzX2NodW5rJHNldChlY2hvPVRSVUUsDQoJICAgICAgICAgICAgIGNhY2hlPUYsDQogICAgICAgICAgICAgICBwcm9tcHQ9RkFMU0UsDQogICAgICAgICAgICAgICB0aWR5PVRSVUUsDQogICAgICAgICAgICAgICBjb21tZW50PU5BLA0KICAgICAgICAgICAgICAgbWVzc2FnZT1GQUxTRSwNCiAgICAgICAgICAgICAgIHdhcm5pbmc9RkFMU0UpDQpvcHRzX2tuaXQkc2V0KHdpZHRoPTEwMCkNCmBgYA0KDQojIExpY2Vuw6dhIHsjTGljZW7Dp2EgLnVubnVtYmVyZWR9DQoNClRoaXMgd29yayBpcyBsaWNlbnNlZCB1bmRlciB0aGUgQ3JlYXRpdmUgQ29tbW9ucyBBdHRyaWJ1dGlvbi1TaGFyZUFsaWtlIDQuMCBJbnRlcm5hdGlvbmFsIExpY2Vuc2UuIFRvIHZpZXcgYSBjb3B5IG9mIHRoaXMgbGljZW5zZSwgdmlzaXQgPGh0dHA6Ly9jcmVhdGl2ZWNvbW1vbnMub3JnL2xpY2Vuc2VzL2J5LXNhLzQuMC8+IG9yIHNlbmQgYSBsZXR0ZXIgdG8gQ3JlYXRpdmUgQ29tbW9ucywgUE8gQm94IDE4NjYsIE1vdW50YWluIFZpZXcsIENBIDk0MDQyLCBVU0EuDQoNCiFbTGljZW5zZTogQ0MgQlktU0EgNC4wXShodHRwczovL21pcnJvcnMuY3JlYXRpdmVjb21tb25zLm9yZy9wcmVzc2tpdC9idXR0b25zLzg4eDMxL3BuZy9ieS1zYS5wbmcpe3dpZHRoPSIyNSUifQ0KDQojIENpdGHDp8OjbyB7I0NpdGHDp8OjbyAudW5udW1iZXJlZH0NCg0KU3VnZXN0w6NvIGRlIGNpdGHDp8OjbzogRklHVUVJUkVETywgQWRyaWFubyBNYXJjb3MgUm9kcmlndWVzLiBTw6lyaWVzIFRlbXBvcmFpczogUm90aW5hIHLDoXBpZGEgcGFyYSBsaWRhciBjb20gbWlzc2luZ3Mgbm8gYGZhYmxldG9vbHNgLiBDYW1wbyBHcmFuZGUtTVMsQnJhc2lsOiBSU3R1ZGlvL1JwdWJzLCAyMDIxLiBEaXNwb27DrXZlbCBlbSBbXDxodHRwOi8vcnB1YnMuY29tL2Ftcm9maS9taXNzaW5nc19mYWJsZXRvb2xzXD5dKGh0dHA6Ly9ycHVicy5jb20vYW1yb2ZpL21pc3NpbmdzX2ZhYmxldG9vbHMpey51cml9Lg0KDQojIEludHJvZHXDp8Ojbw0KDQpTZWd1ZSB1bWEgcm90aW5hIHLDoXBpZGEgcGFyYSBsaWRhciBjb20gb3MgbWlzc2luZ3MgKG9ic2VydmHDp8O1ZXMgYXVzZW50ZXMpLCBjb20gbyBwYWNvdGUgYGZhYmxldG9vbHNgIGUgYHRpZHlyYC5cDQpIeW5kbWFuIGUgQXRoYW5hc29wb3Vsb3MgKDIwMjApIG5vIFtGUFAgMywgc2XDp8OjbyAxMy45XShodHRwczovL290ZXh0cy5jb20vZnBwMy9taXNzaW5nLW91dGxpZXJzLmh0bWwgIkZQUCAzLCBzZcOnw6NvIDEzLjkiKSwgbWVuY2lvbmFtIG8gZmF0byBkZSBzZXIgY29tdW0gdGVybW9zIHPDqXJpZXMgdGVtcG9yYWlzIGNvbSBkYWRvcyBhdXNlbnRlcywgcG9yIGV4ZW1wbG8sIGRldmlkbyBhb3MgZmluYWlzIGRlIHNlbWFuYSwgb3UgcG9yIG91dHJvIG1vdGl2byBxdWFscXVlciAoZXNxdWVjaW1lbnRvLCBhdXPDqm5jaWEgZGUgY29sZXRhIGRlIGRhZG9zLCBpbXBlZGltZW50byBkZSBuZWdvY2lhw6fDo28gZW0gYm9sc2EsIGV0Yy4pLiBPIGNhc28gbWFpcyBpbXBvcnRhbnRlIHF1ZSBjaXRvIMOpIGFxdWVsZSBlbSBxdWUgYSBzw6lyaWUgYXByZXNlbnRhICJmYWxoYXMiIGltcGzDrWNpdGFzLiBPdSBzZWphLCBpbWFnaW5lIHVtIGNhc28gZGUgdW0gZmltIGRlIHNlbWFuYSAoc8OhYmFkbyBlIGRvbWluZ28pIHNlbSBkYWRvcy4gQSBzw6lyaWUgdGVyw6EgZW50w6NvIGZhbGhhcyBubyBjYWxlbmTDoXJpby4gU2VqYSBvIGNhc28gZG8gbcOqcyBkZSBtYWlvIGRlIDIwMjEsIHRlcmVtb3MgZmFsaGFzIGVtIHRvZG9zIG9zIGRpYXMgZW0gdmVybWVsaG8gKEZpZ3VyYSAxKS4NCg0KIVtGaWd1cmEgMS4gTWFpbyBkZSAyMDIxXShJbWFnZW0xLnBuZyAiRmlndXJhIDEuIE1haW8gZGUgMjAyMSIpDQoNClVtYSB0YWJlbGEgY29tIGRhZG9zIGRpw6FyaW9zIHBhcmEgb3MgZGlhcyDDunRlaXMgc2VyaWEgYWxnbyBjb21vOg0KDQohW1RhYmVsYSAxLiBEYWRvcyBhbGVhdMOzcmlvcy5dKEltYWdlbTIucG5nICJUYWJlbGEgMS4gRGFkb3MgYWxlYXTDs3Jpb3MuIikNCg0KT2JzZXJ2ZSBxdWUgbmEgVGFiZWxhIDEsIHRlbW9zIGEgZGF0YSBkZSAyMS8wNS8yMDIxIHZhemlhLCBlIGFzIGRlbWFpcyBlc3TDo28gcHJlZW5jaGlkYXMuIE8gbGVpdG9yIHBvZGVyaWEgZGl6ZXIgcXVlIGJhc3RhIGV4Y2x1aXIgYSBkYXRhIGRlIDIxLzA1LzIwMjEgZSBuw6NvIHRlcmVtb3MgbWFpcyBuZW5odW1hIG9ic2VydmHDp8OjbyBhdXNlbnRlIChOQSA9ICpub3QgYXZhaWxhYmxlID0qIG8gbm9zc28gbWlzc2luZyBleHBsw61jaXRvKS4gTWFzIHRlbW9zIG1pc3NpbmdzIGltcGzDrWNpdG9zLCBxdWUgbsOjbyBhcGFyZWNlbSBkaXJldGFtZW50ZSwgcXVlIHPDo28gb3MgZmluYWlzIGRlIHNlbWFuYSAoMDgsIDA5LCAxNSwgMTYgZGUgbWFpbyBkZSAyMDIxKS4NCg0KRW50w6NvLCBhcXVpIHZlcmVtb3MgY29tbyBsaWRhciBjb20gZXNzYXMgc2l0dWHDp8O1ZXMsIHBvaXMgbyBgZmFibGVgIHRlcsOhIGFsZ3VtYXMgcmVzdHJpw6fDtWVzIHF1YW5kbyBob3V2ZXIgZGFkb3MgYXVzZW50ZXMgKGV4cGzDrWNpdG9zIG91IGltcGzDrWNpdG9zKSwgcG9yIGV4ZW1wbG8sIG5vIGBFVFMoKWAgZSBubyBgU1RMKClgLCBjb25mb3JtZSBIeW5kbWFuIGUgQXRoYW5hc29wb3Vsb3MgKDIwMjApIG5vIFtGUFAgMywgc2XDp8OjbyAxMy45XShodHRwczovL290ZXh0cy5jb20vZnBwMy9taXNzaW5nLW91dGxpZXJzLmh0bWwgIkZQUCAzLCBzZcOnw6NvIDEzLjkiKS4NCg0KQSBzdWdlc3TDo28gcGFyYSBlc3RlIHBvc3Qgw6kgdHJhYmFsaGFyIGNvbSBvYmpldG9zIGB0c2liYmxlYCwgZW0gY29uc29uw6JuY2lhIGNvbSBIeW5kbWFuIGUgQXRoYW5hc29wb3Vsb3MgKDIwMjApLg0KDQojIERhZG9zIHBhcmEgZXhlbXBsbw0KDQpQcmltZWlybyBjaGFtYW1vcyBvcyBkYWRvcy4gRWxlcyBlc3TDo28gZW1iZWRlZCBubyBjb2RlIFJtZC4gQ29tbyBnZXJlaSBubyBFeGNlbCBlIGEgY29sdW5hIGVzdGF2YSBqw6EgY29tbyBmb3JtYXRvIGRlIGRhdGEsIGFvIGltcG9ydGFyIHBhcmEgbyBFeGNlbCAoYW50ZXMgZGUgZXUgZ2VyYXIgbyBgZHB1dGApLCBvIFIgasOhIGVudGVuZGV1IHF1ZSBlcmEgY29sdW5hIGRlIGRhdGEgZSBtdWRvdSBkYXF1ZWxlIGZvcm1hdG8gZGEgVGFiZWxhIDEgKGRkL21tL2FhYWEpIHBhcmEgbyBmb3JtYXRvIChhYWFhLW1tLWRkKS4NCg0KYGBge3IgZGFkb3N9DQpkYWRvczwtIHN0cnVjdHVyZShsaXN0KERhdGEgPSBjKCIwMy8wNS8yMDIxIiwgIjA0LzA1LzIwMjEiLCAiMDUvMDUvMjAyMSIsIA0KIjA2LzA1LzIwMjEiLCAiMDcvMDUvMjAyMSIsICIxMC8wNS8yMDIxIiwgIjExLzA1LzIwMjEiLCAiMTIvMDUvMjAyMSIsIA0KIjEzLzA1LzIwMjEiLCAiMTQvMDUvMjAyMSIsICIxNy8wNS8yMDIxIiwgIjE4LzA1LzIwMjEiLCAiMTkvMDUvMjAyMSIsIA0KIjIwLzA1LzIwMjEiLCAiMjEvMDUvMjAyMSIpLCBYID0gYygyNjQsIDI4MSwgMjg3LCAyODQsIDI3OCwgMjgxLCANCjI5MywgMjkyLCAyODksIDI5NCwgMjk1LCAyOTMsIDI5MCwgMjg4LCBOQSkpLCByb3cubmFtZXMgPSBjKE5BLCANCi0xNUwpLCBjbGFzcyA9IGMoInRibF9kZiIsICJ0YmwiLCAiZGF0YS5mcmFtZSIpKQ0KY2xhc3MoZGFkb3MkRGF0YSkNCmBgYA0KDQpWZWphIHF1ZSBlbGUgdmVtIGVtIGRhdGEuZnJhbWUuIEVudMOjbyB2YW1vcyBnZXJhciBvIGB0c2liYmxlYC4NCg0KUGXDp28gdm9zc2EgYXRlbsOnw6NvIHBhcmEgbyBmYXRvIGRlIHF1ZSBhIGNvbHVuYSBkYXRhIG5lbSBzZW1wcmUgZXN0w6EgZW0gZm9ybWF0byBkZSBkYXRhIGRvIFIuIE5PIGNodW5rIGFjaW1hIGVzdMOhIGNvbW8gZGF0YS5mcmFtZS4NCg0KQ2FzbyB2b2PDqiB0ZW5oYSB1bWEgY29sdW5hIERhdGEgZW0gZm9ybWF0byBkZSBjYXJhY3RlciAodGVzdGFyIGZhemVuZG8gYGNsYXNzKGRhZG9zJERhdGEpYCkgZW50w6NvLCBhbnRlcyBkZSBjcmlhciBvIGB0c2liYmxlYCwgY3JpYXIgdW1hIGNvbHVuYSBkYXRlIHBhcmEgZmFjaWxpdGFyIGEgaW5kZXhhw6fDo28gY29uZm9ybWUgY2h1bmsgYWJhaXhvLg0KDQpgYGB7ciBjb2x1bmFkYXRlLCBldmFsPVR9DQpkYWRvcyRkYXRlIDwtIGFzLkRhdGUoZGFkb3MkRGF0YSwgZm9ybWF0ID0gIiVkLyVtLyVZIikNCnByaW50KGRhZG9zKQ0KYGBgDQoNCkFnb3JhLCBhbMOpbSBkYSBjb2x1bmEgZGEgZGF0YSBpbmljaWFsIChEYXRhKSwgdGVtb3MgYSBjb2x1bmEgKGRhdGUpLg0KDQpFbnTDo28gY3JpYXJlbW9zIG8gYHRzaWJibGVgIG5vIHByw7N4aW1vIGNodW5rLiBDaGFtYXJlbW9zIG8gcGFjb3RlIGBmYWJsZWAgcGFyYSBleGVjdXRhciBhIGZ1bsOnw6NvIGBhc190c2liYmxlKClgIGUgY29sb2NhcmVtb3MgYSBjb2x1bmEgZGF0ZSAocG9kZSBzZXIgYSBEYXRhIGRlc2RlIHF1ZSBlc3RlamEgY29tbyBuYSBzdHJ1Y3R1cmUgZG8gbm9zc28gcHJpbWVpcm8gY2h1bmsuDQoNCmBgYHtyIHRzaWJibGV9DQpsaWJyYXJ5KGZhYmxlKQ0KZGFkb3MudHNiPC1hc190c2liYmxlKGRhZG9zWyxjKDMsMildLGluZGV4ID0gZGF0ZSxyZWd1bGFyID0gVCkNCmNsYXNzKGRhZG9zLnRzYikgICAjICJ0YmxfdHMiICAgICAidGJsX2RmIiAgICAgInRibCIgICAgICAgICJkYXRhLmZyYW1lIg0KZmFibGV0b29sczo6YXV0b3Bsb3QoZGFkb3MudHNiLFgpDQpgYGANCg0KTyBsZWl0b3IgYXRlbnRvIMOgIG1lbnNhZ2VucyBkZSBlcnJvIHZlcsOhIHF1ZSBhcGFyZWNlIGEgbWVuc2FnZW0gYFJlbW92ZWQgMSByb3cocykgY29udGFpbmluZyBtaXNzaW5nIHZhbHVlcyAoZ2VvbV9wYXRoKS5gIE91IHNlamEsIGVsZSByZW1vdmV1IGF1dG9tYXRpY2FtZW50ZSBhIGxpbmhhIGRvIGRpYSAyMDIxLTA1LTIxLCBvbmRlIGFwYXJlY2lhIG8gYE5BYCBuYSBjb2x1bmEgZGUgYFhgLiBPIG9iamV0byBgdHNpYmJsZWAgZGUgbm9tZSBgZGFkb3MudHNiYCBmb2kgY3JpYWRvLiBOZXN0ZSBleGVtcGxvLCBqw6EgZXhjbHVpIGEgY29sdW5hIGBEYXRhYCBwYXJhIG7Do28gZ2VyYXIgY29uZnVzw6NvLCBlIG8gYGluZGV4YCBkbyBgdHNpYmJsZWAgw6kgYSBjb2x1bmEgYGRhdGVgLg0KDQpPIHBhY290ZSBqw6EgZW50ZW5kZXUgcXVlIGEgc8OpcmllIMOpIGRpw6FyaWEgZSB1bml1IG9zIHBvbnRvcy4gTWFzIGNvbnRpbnVhbW9zIGNvbSBhcyBmYWxoYXMgZG9zIGZpbmFpcyBkZSBzZW1hbmEuDQoNClBvcnRhbnRvIGFnb3JhIG1vc3RyYXJlaSBkdWFzIGZvcm1hcyBkZSBwcmVlbmNoZXIgb3MgZGFkb3MgZSByZWNvbWVuZG8gYW8gbGVpdG9yIHF1ZSBjb25zdWx0ZSBhcyBvcMOnw7VlcyBkbyBleGVtcGxvIGRvIG1hbnVhbCBkbyBgdHNpYmJsZWAgOiBbRXhlbXBsb3MgZG8gZmlsbF9nYXBzIG5vIHRzaWJibGVdKGh0dHBzOi8vd3d3LnJkb2N1bWVudGF0aW9uLm9yZy9wYWNrYWdlcy90c2liYmxlL3ZlcnNpb25zLzEuMC4xL3RvcGljcy9maWxsX2dhcHMgIkV4ZW1wbG9zIGRvIGZpbGxfZ2FwcyBubyB0c2liYmxlIikgLCBvbmRlIGV4aXN0ZW0gb3DDp8O1ZXMgcGFyYSBwcmVlbmNoZXIgY29tIE5BcywgaW50ZXJwb2xhciwgcHJlZW5jaGVyIGNvbSB6ZXJvcywgcHJlZW5jaGVyIGNvbSBhIG3DqWRpYSwgcHJlZW5jaGVyIGNvbSBvIMO6bHRpbW8gdmFsb3IgZXRjLiBBcyBkdWFzIG9ww6fDtWVzIGFxdWkgZGVzZW52b2x2aWRhcyBzw6NvOiBjb20gTkFzIGV4cGzDrWNpdG9zLCBlIGEgZGUgcHJlZW5jaGVyIGNvbSBvIMO6bHRpbW8gdmFsb3IgZGlzcG9uw612ZWwuDQoNCiMgT3DDp8OjbyAxOiBwcmVlbmNoZXIgY29tIE5Bcw0KDQpOZXN0ZSBjYXNvLCBvIGB0c2liYmxlYCBlbnRlbmRlcsOhIGFzIGRhdGFzIHF1ZSBmYWx0YW0sIGNyaWFyw6EgZXN0YXMgbm92YXMgbGluaGFzIGUgcHJlZW5jaGVyw6EgY29tIG8gc8OtbWJvbG8gZGUgbWlzc2luZyBwYWRyw6NvOiBgTkFgLiBFbmNvcmFqbyBvIGxlaXRvciBhIG9ic2VydmFyIGEgdGFiZWxhIGRvIGBwcmludGAsIGUgY29tcGFyYXIgY29tIGEgdGFiZWxhIDEsIGUgdmVyw6EgcXVlIGFnb3JhIGEgbm92YSBzYcOtZGEgKGBkYWRvcy5mdWxsYCkgdGVtIGFzIGRhdGFzIGluY2x1c2l2ZSBwYXJhIG9zIGZpbmFpcyBkZSBzZW1hbmEgKHPDoWJhZG8gZSBkb21pbmdvKS4NCg0KYGBge3J9DQpkYWRvcy5mdWxsPC10c2liYmxlOjpmaWxsX2dhcHMoZGFkb3MudHNiKQ0Kb3B0aW9ucyhtYXgucHJpbnQgPSAxMDApDQpwcmludChkYWRvcy5mdWxsKQ0KYGBgDQoNCiMgT3DDp8OjbyAyOiBwcmVlbmNoZXIgY29tIMO6bHRpbW8gdmFsb3INCg0KTmVzdGUgY2FzbywgbyBgdHNpYmJsZWAgZW50ZW5kZXLDoSBhcyBkYXRhcyBxdWUgZmFsdGFtLCBjcmlhcsOhIGVzdGFzIG5vdmFzIGxpbmhhcyBlIHByZWVuY2hlcsOhIGNvbSBvIMO6bHRpbW8gdmFsb3IgZGEgc8OpcmllIGBYYCwgcHJlZW5jaGVuZG8gcGFyYSBiYWl4byAoYC5kaXJlY3Rpb24gPSAiZG93biJgKSBjb20gdXNvIGRhIGZ1bsOnw6NvIGBmaWxsYCBkbyBwYWNvdGUgYHRpZHlyYC4gRW5jb3Jham8gbyBsZWl0b3IgYSBvYnNlcnZhciBhIHRhYmVsYSBkbyBgcHJpbnRgLCBlIGNvbXBhcmFyIGNvbSBhIHRhYmVsYSAxLCBlIHZlcsOhIHF1ZSBhZ29yYSBhIG5vdmEgc2HDrWRhIChgZGFkb3MuZnVsbDJgKSB0ZW0gYXMgZGF0YXMgaW5jbHVzaXZlIHBhcmEgb3MgZmluYWlzIGRlIHNlbWFuYSAoc8OhYmFkbyBlIGRvbWluZ28pIGUgbsOjbyBjb25zdGFtIE5Bcy4NCg0KYGBge3J9DQpsaWJyYXJ5KHRpZHlyKQ0KZGFkb3MuZnVsbDI8LWRhZG9zLnRzYiAlPiUgDQogIHRzaWJibGU6OmZpbGxfZ2FwcygpICU+JQ0KICB0aWR5cjo6ZmlsbChYLCAuZGlyZWN0aW9uID0gImRvd24iKQ0KcHJpbnQoZGFkb3MuZnVsbDIpDQpgYGANCg0KRGVzdGUgbW9kbywgbyBsZWl0b3IgcG9kZSB2ZXJpZmljYXIgcXVlIG9zIHZhbG9yZXMgZGUgMDggZSAwOSBkZSBtYWlvIGRlIDIwMjEgZm9yYW0gcHJlZW5jaGlkb3MgY29tIG8gbWVzbW8gdmFsb3IgZGUgMDcgZGUgbWFpbyBkZSAyMDIxLiBEbyBtZXNtbyBtb2RvLCBwYXJhIDE1IGUgMTYgZGUgbWFpbywgdGVyZW1vcyBvcyB2YWxvcmVzIGRlIDE0IGRlIG1haW8gcmVwZXRpZG9zLiBPIG1lc21vIHNlcsOhIGZlaXRvIHBhcmEgcHJlZW5jaGVyIG8gZGlhIDIxLg0KDQpPIGdyw6FmaWNvIHNlcsOhIGFnb3JhOg0KDQpgYGB7cn0NCmZhYmxldG9vbHM6OmF1dG9wbG90KGRhZG9zLmZ1bGwyLFgpDQpgYGANCg0KTyBsZWl0b3IgcG9kZSB0ZXN0YXIgYWdvcmEgYXMgZmVycmFtZW50YXMgZGUgZm9yZWNhc3QgZG8gZmFibGUgZSBmYWJsZXRvb2xzLg0KDQpgYGB7cn0NCmxpYnJhcnkoZnBwMykNCmZpdCA8LSBkYWRvcy5mdWxsMiAlPiUgbW9kZWwoDQogICAgICAgICAgICAgICAgICAgYXJpbWEgPSBBUklNQShYKSwNCiAgICAgICAgICAgICAgICAgICBldHMgPSBFVFMoWCkNCiAgICAgICAgICAgICAgICAgICkNCmZpdF9mYyA8LSBmaXQgJT4lDQogIGZvcmVjYXN0KGggPSAxMCkNCmZpdF9mYyAlPiUNCiAgYXV0b3Bsb3QoZGFkb3MudHNiLA0KICAgICAgICAgICBsZXZlbCA9IE5VTEwpICsNCiAgbGFicyh5ID0gInZhbG9yIGRlIFgiLA0KICAgICAgIHRpdGxlID0gIkV4ZW1wbG8gZ2Vuw6lyaWNvIGRlIGZvcmVjYXN0IGFww7NzIGZpbGxfZ2FwcyIpDQpgYGANCg0KIyBSZWZlcsOqbmNpYXMgeyNSZWZlcsOqbmNpYXMgLnVubnVtYmVyZWR9DQoNCkhZTkRNQU4sIFJvYiBKLiAoMjAxOCkuIGZwcDI6IERhdGEgZm9yICJGb3JlY2FzdGluZzogUHJpbmNpcGxlcyBhbmQgUHJhY3RpY2UiICgybmQgRWRpdGlvbikuIFIgcGFja2FnZSB2ZXJzaW9uIDIuMy4gRGlzcG9uw612ZWwgZW06IDxodHRwczovL0NSQU4uUi1wcm9qZWN0Lm9yZy9wYWNrYWdlPWZwcDI+LiBBY2Nlc3NlZCBvbiAyMCBNYXkgMjAyMS4NCg0KSFlORE1BTiwgUm9iIEouICgyMDE5KS4gZnBwMzogRGF0YSBmb3IgIkZvcmVjYXN0aW5nOiBQcmluY2lwbGVzIGFuZCBQcmFjdGljZSIgKDNyZCBFZGl0aW9uKS4gUiBwYWNrYWdlLiBEaXNwb27DrXZlbCBlbTogPGh0dHBzOi8vZ2l0aHViLmNvbS9yb2JqaHluZG1hbi9mcHAzLXBhY2thZ2U+LCA8aHR0cHM6Ly9PVGV4dHMub3JnL2ZwcDMvPi4gQWNjZXNzZWQgb24gMjAgTWF5IDIwMjEuDQoNCkhZTkRNQU4sIFIuSi47IEFUSEFOQVNPUE9VTE9TLCBHLiAoMjAyMCkgRm9yZWNhc3Rpbmc6IHByaW5jaXBsZXMgYW5kIHByYWN0aWNlLCAzcmQgZWRpdGlvbiwgT1RleHRzOiBNZWxib3VybmUsIEF1c3RyYWxpYS4gRGlzcG9uw612ZWwgZW06IDxodHRwczovL290ZXh0cy5jb20vZnBwMy8+LiBBY2Nlc3NlZCBvbiAyMCBNYXkgMjAyMS4NCg0KTydIQVJBLVdJTEQsIE1pdGNoZWxsOyBIWU5ETUFOLCBSb2IgSi47IFdBTkcsIEVhcm8uICgyMDIxKS4gZmVhc3RzOiBGZWF0dXJlIEV4dHJhY3Rpb24gYW5kIFN0YXRpc3RpY3MgZm9yIFRpbWUgU2VyaWVzLiBSIHBhY2thZ2UgdmVyc2lvbiAwLjIuMS4gRGlzcG9uw612ZWwgZW06IDxodHRwczovL0NSQU4uUi1wcm9qZWN0Lm9yZy9wYWNrYWdlPWZlYXN0cz4uIEFjY2Vzc2VkIG9uIDIwIE1heSAyMDIxLg0K