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 com Python. Campo Grande-MS,Brasil: RStudio/Rpubs, 2025. Disponível em http://rpubs.com/amrofi/stocks_ETS_py.

1 Introdução

2 Dados e gráfico

2.1 VALE3 e PETR4

Primeiro, instale os pacotes pelo terminal:

pip install yfinance pandas matplotlib statsmodels scikit-learn.

Depois, segue o code:

# Importing necessary libraries
import yfinance as yf
import pandas as pd
import matplotlib.pyplot as plt
from statsmodels.tsa.holtwinters import ExponentialSmoothing
from sklearn.metrics import mean_absolute_percentage_error as mape
from sklearn.metrics import mean_absolute_error as mae

# Configurations
plt.style.use('seaborn')
plt.rcParams['figure.figsize'] = [12, 6]

# Downloading PETR4 and VALE3 data
petro_data = yf.download("PETR4.SA", start="2000-01-01", end="2019-09-01", interval="1mo", progress=False)
YF.download() has changed argument auto_adjust default to True
vale_data = yf.download("VALE3.SA", start="2000-01-01", end="2019-09-01", interval="1mo", progress=False)

# Check if 'Adj Close' column exists, otherwise use 'Close'
if 'Adj Close' in petro_data.columns:
    petro = petro_data['Adj Close']
else:
    print("'Adj Close' not found for PETR4.SA. Using 'Close' instead.")
    petro = petro_data['Close']
'Adj Close' not found for PETR4.SA. Using 'Close' instead.
if 'Adj Close' in vale_data.columns:
    vale = vale_data['Adj Close']
else:
    print("'Adj Close' not found for VALE3.SA. Using 'Close' instead.")
    vale = vale_data['Close']
'Adj Close' not found for VALE3.SA. Using 'Close' instead.
# Plotting the adjusted closing prices (or regular closing prices if 'Adj Close' is missing)
plt.figure(figsize=(14, 8))
plt.subplot(2, 1, 1)
plt.plot(petro, color='darkblue')
plt.title('PETR4.SA - Adjusted Closing Price' if 'Adj Close' in petro_data.columns else 'PETR4.SA - Closing Price')
plt.xlabel('Month/Year')
plt.ylabel('R$')

plt.subplot(2, 1, 2)
plt.plot(vale, color='darkblue')
plt.title('VALE3.SA - Adjusted Closing Price' if 'Adj Close' in vale_data.columns else 'VALE3.SA - Closing Price')
plt.xlabel('Month/Year')
plt.ylabel('R$')
plt.tight_layout()
plt.show()

# Calculating monthly log returns
petro_returns = petro.pct_change().dropna()
vale_returns = vale.pct_change().dropna()

# Plotting the returns
plt.figure(figsize=(14, 8))
plt.subplot(2, 1, 1)
plt.plot(petro_returns, color='darkblue')
plt.title('PETR4.SA Monthly Returns')
plt.xlabel('Month/Year')
plt.ylabel('Return')

plt.subplot(2, 1, 2)
plt.plot(vale_returns, color='darkblue')
plt.title('VALE3.SA Monthly Returns')
plt.xlabel('Month/Year')
plt.ylabel('Return')
plt.tight_layout()
plt.show()

# Splitting data into training and testing sets
train_size = int(len(petro_returns) * 0.8)
petro_train, petro_test = petro_returns[:train_size], petro_returns[train_size:]
vale_train, vale_test = vale_returns[:train_size], vale_returns[train_size:]

# Holt-Winters Forecasting
# PETR4
petro_hw = ExponentialSmoothing(petro_train, seasonal='add', seasonal_periods=12).fit()
petro_hw_forecast = petro_hw.forecast(len(petro_test))

# VALE3
vale_hw = ExponentialSmoothing(vale_train, seasonal='add', seasonal_periods=12).fit()
vale_hw_forecast = vale_hw.forecast(len(vale_test))

# Plotting Holt-Winters forecasts
plt.figure(figsize=(14, 8))
plt.subplot(2, 1, 1)
plt.plot(petro_returns, label='Actual Data')
plt.plot(petro_hw_forecast, label='Holt-Winters Forecast', color='red')
plt.title('PETR4.SA - Holt-Winters Forecast')
plt.xlabel('Month/Year')
plt.ylabel('Return')
plt.legend()

plt.subplot(2, 1, 2)
plt.plot(vale_returns, label='Actual Data')
plt.plot(vale_hw_forecast, label='Holt-Winters Forecast', color='red')
plt.title('VALE3.SA - Holt-Winters Forecast')
plt.xlabel('Month/Year')
plt.ylabel('Return')
plt.legend()
plt.tight_layout()
plt.show()

# ETS (Error, Trend, Seasonal) Forecasting
# PETR4
petro_ets = ExponentialSmoothing(petro_train, trend='add', seasonal='add', seasonal_periods=12).fit()
petro_ets_forecast = petro_ets.forecast(len(petro_test))

# VALE3
vale_ets = ExponentialSmoothing(vale_train, trend='add', seasonal='add', seasonal_periods=12).fit()
vale_ets_forecast = vale_ets.forecast(len(vale_test))

# Plotting ETS forecasts
plt.figure(figsize=(14, 8))
plt.subplot(2, 1, 1)
plt.plot(petro_returns, label='Actual Data')
plt.plot(petro_ets_forecast, label='ETS Forecast', color='green')
plt.title('PETR4.SA - ETS Forecast')
plt.xlabel('Month/Year')
plt.ylabel('Return')
plt.legend()

plt.subplot(2, 1, 2)
plt.plot(vale_returns, label='Actual Data')
plt.plot(vale_ets_forecast, label='ETS Forecast', color='green')
plt.title('VALE3.SA - ETS Forecast')
plt.xlabel('Month/Year')
plt.ylabel('Return')
plt.legend()
plt.tight_layout()
plt.show()

# Evaluating accuracy
# Holt-Winters
petro_hw_smape = mape(petro_test, petro_hw_forecast) * 100
vale_hw_smape = mape(vale_test, vale_hw_forecast) * 100

petro_hw_mape = mae(petro_test, petro_hw_forecast)
vale_hw_mape = mae(vale_test, vale_hw_forecast)

# ETS
petro_ets_smape = mape(petro_test, petro_ets_forecast) * 100
vale_ets_smape = mape(vale_test, vale_ets_forecast) * 100

petro_ets_mape = mae(petro_test, petro_ets_forecast)
vale_ets_mape = mae(vale_test, vale_ets_forecast)

# Printing results
print("Holt-Winters Accuracy:")
Holt-Winters Accuracy:
print(f"PETR4 SMAPE: {petro_hw_smape:.2f}%")
PETR4 SMAPE: 115.80%
print(f"VALE3 SMAPE: {vale_hw_smape:.2f}%")
VALE3 SMAPE: 108.74%
print(f"PETR4 MAPE: {petro_hw_mape:.4f}")
PETR4 MAPE: 0.1140
print(f"VALE3 MAPE: {vale_hw_mape:.4f}")
VALE3 MAPE: 0.1078
print("\nETS Accuracy:")

ETS Accuracy:
print(f"PETR4 SMAPE: {petro_ets_smape:.2f}%")
PETR4 SMAPE: 130.68%
print(f"VALE3 SMAPE: {vale_ets_smape:.2f}%")
VALE3 SMAPE: 120.51%
print(f"PETR4 MAPE: {petro_ets_mape:.4f}")
PETR4 MAPE: 0.1173
print(f"VALE3 MAPE: {vale_ets_mape:.4f}")
VALE3 MAPE: 0.1116
LS0tDQp0aXRsZTogIlPDqXJpZXMgVGVtcG9yYWlzOiBwcmV2aXPDo28gZGUgYcOnw7VlcyBjb20gRVRTLCBjb20gUHl0aG9uIg0KYXV0aG9yOiAiQWRyaWFubyBNYXJjb3MgUm9kcmlndWVzIEZpZ3VlaXJlZG8sICplLW1haWw6IGFkcmlhbm8uZmlndWVpcmVkb0B1Zm1zLmJyKiINCmxpbmtjb2xvcjogYmx1ZQ0KYWJzdHJhY3Q6IA0KICBUaGlzIGlzIGFuIHVuZGVyZ3JhZCBzdHVkZW50IGxldmVsIGluc3RydWN0aW9uIGZvciBjbGFzcyB1c2UuICANCmRhdGU6ICJgciBmb3JtYXQoU3lzLkRhdGUoKSwgJyVkICVCICVZJylgIg0Kb3V0cHV0Og0KICBodG1sX2RvY3VtZW50Og0KICAgIGNvZGVfZG93bmxvYWQ6IHRydWUNCiAgICB0aGVtZTogZGVmYXVsdA0KICAgIG51bWJlcl9zZWN0aW9uczogdHJ1ZQ0KICAgIHRvYzogeWVzDQogICAgdG9jX2Zsb2F0OiB5ZXMNCiAgICBkZl9wcmludDogcGFnZWQNCiAgICBmaWdfY2FwdGlvbjogdHJ1ZQ0KICBybWRmb3JtYXRzOjpyZWFkdGhlZG93bjoNCiAgICBzZWxmX2NvbnRhaW5lZDogdHJ1ZQ0KICAgIHRodW1ibmFpbHM6IHRydWUNCiAgICBsaWdodGJveDogdHJ1ZQ0KICAgIGdhbGxlcnk6IGZhbHNlDQogICAgaGlnaGxpZ2h0OiAidGFuZ28iDQogICAgdXNlX2Jvb2tkb3duOiB0cnVlDQogICAgdG9jX2RlcHRoOiA0DQogICAgbnVtYmVyX3NlY3Rpb25zOiBUUlVFDQogICAgZGZfcHJpbnQ6IHBhZ2VkDQogIGh0bWxfbm90ZWJvb2s6DQogICAgaGlnaGxpZ2h0OiAidGFuZ28iDQogICAgdGhlbWU6IGpvdXJuYWwNCiAgICBudW1iZXJfc2VjdGlvbnM6IHRydWUNCiAgICB0b2M6IHllcw0KICAgIHRvY19mbG9hdDogeWVzDQogICAgZGZfcHJpbnQ6IHBhZ2VkDQogIHBkZl9kb2N1bWVudDoNCiAgICB0b2M6IHllcw0KZ3JhcGhpY3M6IHllcw0KLS0tDQoNCmBgYHtyIGtuaXRyX2luaXQsIGVjaG89RkFMU0UsIGNhY2hlPUZBTFNFfQ0KbGlicmFyeShrbml0cikNCmxpYnJhcnkocm1hcmtkb3duKQ0KbGlicmFyeShybWRmb3JtYXRzKQ0KDQojIyBHbG9iYWwgb3B0aW9ucw0Kb3B0aW9ucyhtYXgucHJpbnQ9IjEwMCIpDQpvcHRzX2NodW5rJHNldChlY2hvPVRSVUUsDQoJICAgICAgICAgICAgICNjYWNoZT1UUlVFLA0KICAgICAgICAgICAgICAgcHJvbXB0PUZBTFNFLA0KICAgICAgICAgICAgICAgdGlkeT1UUlVFLA0KICAgICAgICAgICAgICAgY29tbWVudD1OQSwNCiAgICAgICAgICAgICAgIG1lc3NhZ2U9RkFMU0UsDQogICAgICAgICAgICAgICB3YXJuaW5nPUZBTFNFKQ0Kb3B0c19rbml0JHNldCh3aWR0aD0xMDApDQpgYGANCg0KIyBMaWNlbsOnYSB7I0xpY2Vuw6dhIC51bm51bWJlcmVkfQ0KDQpUaGlzIHdvcmsgaXMgbGljZW5zZWQgdW5kZXIgdGhlIENyZWF0aXZlIENvbW1vbnMgQXR0cmlidXRpb24tU2hhcmVBbGlrZSA0LjAgSW50ZXJuYXRpb25hbCBMaWNlbnNlLiBUbyB2aWV3IGEgY29weSBvZiB0aGlzIGxpY2Vuc2UsIHZpc2l0IDxodHRwOi8vY3JlYXRpdmVjb21tb25zLm9yZy9saWNlbnNlcy9ieS1zYS80LjAvPiBvciBzZW5kIGEgbGV0dGVyIHRvIENyZWF0aXZlIENvbW1vbnMsIFBPIEJveCAxODY2LCBNb3VudGFpbiBWaWV3LCBDQSA5NDA0MiwgVVNBLg0KDQohW0xpY2Vuc2U6IENDIEJZLVNBIDQuMF0oaHR0cHM6Ly9taXJyb3JzLmNyZWF0aXZlY29tbW9ucy5vcmcvcHJlc3NraXQvYnV0dG9ucy84OHgzMS9wbmcvYnktc2EucG5nKXt3aWR0aD0iMjUlIn0NCg0KIyBDaXRhw6fDo28geyNDaXRhw6fDo28gLnVubnVtYmVyZWR9DQoNClN1Z2VzdMOjbyBkZSBjaXRhw6fDo286IEZJR1VFSVJFRE8sIEFkcmlhbm8gTWFyY29zIFJvZHJpZ3Vlcy4gU8OpcmllcyBUZW1wb3JhaXM6IHByZXZpc8OjbyBkZSBhw6fDtWVzIGNvbSBFVFMgY29tIFB5dGhvbi4gQ2FtcG8gR3JhbmRlLU1TLEJyYXNpbDogUlN0dWRpby9ScHVicywgMjAyNS4gRGlzcG9uw612ZWwgZW0gPGh0dHA6Ly9ycHVicy5jb20vYW1yb2ZpL3N0b2Nrc19FVFNfcHk+Lg0KDQojIEludHJvZHXDp8Ojbw0KDQojIERhZG9zIGUgZ3LDoWZpY28NCg0KIyMgVkFMRTMgZSBQRVRSNA0KDQpQcmltZWlybywgaW5zdGFsZSBvcyBwYWNvdGVzIHBlbG8gdGVybWluYWw6DQoNCmBgYCAgICAgICAgIA0KcGlwIGluc3RhbGwgeWZpbmFuY2UgcGFuZGFzIG1hdHBsb3RsaWIgc3RhdHNtb2RlbHMgc2Npa2l0LWxlYXJuLg0KYGBgDQoNCkRlcG9pcywgc2VndWUgbyBjb2RlOg0KDQpgYGB7cHl0aG9ufQ0KIyBJbXBvcnRpbmcgbmVjZXNzYXJ5IGxpYnJhcmllcw0KaW1wb3J0IHlmaW5hbmNlIGFzIHlmDQppbXBvcnQgcGFuZGFzIGFzIHBkDQppbXBvcnQgbWF0cGxvdGxpYi5weXBsb3QgYXMgcGx0DQpmcm9tIHN0YXRzbW9kZWxzLnRzYS5ob2x0d2ludGVycyBpbXBvcnQgRXhwb25lbnRpYWxTbW9vdGhpbmcNCmZyb20gc2tsZWFybi5tZXRyaWNzIGltcG9ydCBtZWFuX2Fic29sdXRlX3BlcmNlbnRhZ2VfZXJyb3IgYXMgbWFwZQ0KZnJvbSBza2xlYXJuLm1ldHJpY3MgaW1wb3J0IG1lYW5fYWJzb2x1dGVfZXJyb3IgYXMgbWFlDQoNCiMgQ29uZmlndXJhdGlvbnMNCnBsdC5zdHlsZS51c2UoJ3NlYWJvcm4nKQ0KcGx0LnJjUGFyYW1zWydmaWd1cmUuZmlnc2l6ZSddID0gWzEyLCA2XQ0KDQojIERvd25sb2FkaW5nIFBFVFI0IGFuZCBWQUxFMyBkYXRhDQpwZXRyb19kYXRhID0geWYuZG93bmxvYWQoIlBFVFI0LlNBIiwgc3RhcnQ9IjIwMDAtMDEtMDEiLCBlbmQ9IjIwMTktMDktMDEiLCBpbnRlcnZhbD0iMW1vIiwgcHJvZ3Jlc3M9RmFsc2UpDQp2YWxlX2RhdGEgPSB5Zi5kb3dubG9hZCgiVkFMRTMuU0EiLCBzdGFydD0iMjAwMC0wMS0wMSIsIGVuZD0iMjAxOS0wOS0wMSIsIGludGVydmFsPSIxbW8iLCBwcm9ncmVzcz1GYWxzZSkNCg0KIyBDaGVjayBpZiAnQWRqIENsb3NlJyBjb2x1bW4gZXhpc3RzLCBvdGhlcndpc2UgdXNlICdDbG9zZScNCmlmICdBZGogQ2xvc2UnIGluIHBldHJvX2RhdGEuY29sdW1uczoNCiAgICBwZXRybyA9IHBldHJvX2RhdGFbJ0FkaiBDbG9zZSddDQplbHNlOg0KICAgIHByaW50KCInQWRqIENsb3NlJyBub3QgZm91bmQgZm9yIFBFVFI0LlNBLiBVc2luZyAnQ2xvc2UnIGluc3RlYWQuIikNCiAgICBwZXRybyA9IHBldHJvX2RhdGFbJ0Nsb3NlJ10NCg0KaWYgJ0FkaiBDbG9zZScgaW4gdmFsZV9kYXRhLmNvbHVtbnM6DQogICAgdmFsZSA9IHZhbGVfZGF0YVsnQWRqIENsb3NlJ10NCmVsc2U6DQogICAgcHJpbnQoIidBZGogQ2xvc2UnIG5vdCBmb3VuZCBmb3IgVkFMRTMuU0EuIFVzaW5nICdDbG9zZScgaW5zdGVhZC4iKQ0KICAgIHZhbGUgPSB2YWxlX2RhdGFbJ0Nsb3NlJ10NCg0KIyBQbG90dGluZyB0aGUgYWRqdXN0ZWQgY2xvc2luZyBwcmljZXMgKG9yIHJlZ3VsYXIgY2xvc2luZyBwcmljZXMgaWYgJ0FkaiBDbG9zZScgaXMgbWlzc2luZykNCnBsdC5maWd1cmUoZmlnc2l6ZT0oMTQsIDgpKQ0KcGx0LnN1YnBsb3QoMiwgMSwgMSkNCnBsdC5wbG90KHBldHJvLCBjb2xvcj0nZGFya2JsdWUnKQ0KcGx0LnRpdGxlKCdQRVRSNC5TQSAtIEFkanVzdGVkIENsb3NpbmcgUHJpY2UnIGlmICdBZGogQ2xvc2UnIGluIHBldHJvX2RhdGEuY29sdW1ucyBlbHNlICdQRVRSNC5TQSAtIENsb3NpbmcgUHJpY2UnKQ0KcGx0LnhsYWJlbCgnTW9udGgvWWVhcicpDQpwbHQueWxhYmVsKCdSJCcpDQoNCnBsdC5zdWJwbG90KDIsIDEsIDIpDQpwbHQucGxvdCh2YWxlLCBjb2xvcj0nZGFya2JsdWUnKQ0KcGx0LnRpdGxlKCdWQUxFMy5TQSAtIEFkanVzdGVkIENsb3NpbmcgUHJpY2UnIGlmICdBZGogQ2xvc2UnIGluIHZhbGVfZGF0YS5jb2x1bW5zIGVsc2UgJ1ZBTEUzLlNBIC0gQ2xvc2luZyBQcmljZScpDQpwbHQueGxhYmVsKCdNb250aC9ZZWFyJykNCnBsdC55bGFiZWwoJ1IkJykNCnBsdC50aWdodF9sYXlvdXQoKQ0KcGx0LnNob3coKQ0KDQojIENhbGN1bGF0aW5nIG1vbnRobHkgbG9nIHJldHVybnMNCnBldHJvX3JldHVybnMgPSBwZXRyby5wY3RfY2hhbmdlKCkuZHJvcG5hKCkNCnZhbGVfcmV0dXJucyA9IHZhbGUucGN0X2NoYW5nZSgpLmRyb3BuYSgpDQoNCiMgUGxvdHRpbmcgdGhlIHJldHVybnMNCnBsdC5maWd1cmUoZmlnc2l6ZT0oMTQsIDgpKQ0KcGx0LnN1YnBsb3QoMiwgMSwgMSkNCnBsdC5wbG90KHBldHJvX3JldHVybnMsIGNvbG9yPSdkYXJrYmx1ZScpDQpwbHQudGl0bGUoJ1BFVFI0LlNBIE1vbnRobHkgUmV0dXJucycpDQpwbHQueGxhYmVsKCdNb250aC9ZZWFyJykNCnBsdC55bGFiZWwoJ1JldHVybicpDQoNCnBsdC5zdWJwbG90KDIsIDEsIDIpDQpwbHQucGxvdCh2YWxlX3JldHVybnMsIGNvbG9yPSdkYXJrYmx1ZScpDQpwbHQudGl0bGUoJ1ZBTEUzLlNBIE1vbnRobHkgUmV0dXJucycpDQpwbHQueGxhYmVsKCdNb250aC9ZZWFyJykNCnBsdC55bGFiZWwoJ1JldHVybicpDQpwbHQudGlnaHRfbGF5b3V0KCkNCnBsdC5zaG93KCkNCg0KIyBTcGxpdHRpbmcgZGF0YSBpbnRvIHRyYWluaW5nIGFuZCB0ZXN0aW5nIHNldHMNCnRyYWluX3NpemUgPSBpbnQobGVuKHBldHJvX3JldHVybnMpICogMC44KQ0KcGV0cm9fdHJhaW4sIHBldHJvX3Rlc3QgPSBwZXRyb19yZXR1cm5zWzp0cmFpbl9zaXplXSwgcGV0cm9fcmV0dXJuc1t0cmFpbl9zaXplOl0NCnZhbGVfdHJhaW4sIHZhbGVfdGVzdCA9IHZhbGVfcmV0dXJuc1s6dHJhaW5fc2l6ZV0sIHZhbGVfcmV0dXJuc1t0cmFpbl9zaXplOl0NCg0KIyBIb2x0LVdpbnRlcnMgRm9yZWNhc3RpbmcNCiMgUEVUUjQNCnBldHJvX2h3ID0gRXhwb25lbnRpYWxTbW9vdGhpbmcocGV0cm9fdHJhaW4sIHNlYXNvbmFsPSdhZGQnLCBzZWFzb25hbF9wZXJpb2RzPTEyKS5maXQoKQ0KcGV0cm9faHdfZm9yZWNhc3QgPSBwZXRyb19ody5mb3JlY2FzdChsZW4ocGV0cm9fdGVzdCkpDQoNCiMgVkFMRTMNCnZhbGVfaHcgPSBFeHBvbmVudGlhbFNtb290aGluZyh2YWxlX3RyYWluLCBzZWFzb25hbD0nYWRkJywgc2Vhc29uYWxfcGVyaW9kcz0xMikuZml0KCkNCnZhbGVfaHdfZm9yZWNhc3QgPSB2YWxlX2h3LmZvcmVjYXN0KGxlbih2YWxlX3Rlc3QpKQ0KDQojIFBsb3R0aW5nIEhvbHQtV2ludGVycyBmb3JlY2FzdHMNCnBsdC5maWd1cmUoZmlnc2l6ZT0oMTQsIDgpKQ0KcGx0LnN1YnBsb3QoMiwgMSwgMSkNCnBsdC5wbG90KHBldHJvX3JldHVybnMsIGxhYmVsPSdBY3R1YWwgRGF0YScpDQpwbHQucGxvdChwZXRyb19od19mb3JlY2FzdCwgbGFiZWw9J0hvbHQtV2ludGVycyBGb3JlY2FzdCcsIGNvbG9yPSdyZWQnKQ0KcGx0LnRpdGxlKCdQRVRSNC5TQSAtIEhvbHQtV2ludGVycyBGb3JlY2FzdCcpDQpwbHQueGxhYmVsKCdNb250aC9ZZWFyJykNCnBsdC55bGFiZWwoJ1JldHVybicpDQpwbHQubGVnZW5kKCkNCg0KcGx0LnN1YnBsb3QoMiwgMSwgMikNCnBsdC5wbG90KHZhbGVfcmV0dXJucywgbGFiZWw9J0FjdHVhbCBEYXRhJykNCnBsdC5wbG90KHZhbGVfaHdfZm9yZWNhc3QsIGxhYmVsPSdIb2x0LVdpbnRlcnMgRm9yZWNhc3QnLCBjb2xvcj0ncmVkJykNCnBsdC50aXRsZSgnVkFMRTMuU0EgLSBIb2x0LVdpbnRlcnMgRm9yZWNhc3QnKQ0KcGx0LnhsYWJlbCgnTW9udGgvWWVhcicpDQpwbHQueWxhYmVsKCdSZXR1cm4nKQ0KcGx0LmxlZ2VuZCgpDQpwbHQudGlnaHRfbGF5b3V0KCkNCnBsdC5zaG93KCkNCg0KIyBFVFMgKEVycm9yLCBUcmVuZCwgU2Vhc29uYWwpIEZvcmVjYXN0aW5nDQojIFBFVFI0DQpwZXRyb19ldHMgPSBFeHBvbmVudGlhbFNtb290aGluZyhwZXRyb190cmFpbiwgdHJlbmQ9J2FkZCcsIHNlYXNvbmFsPSdhZGQnLCBzZWFzb25hbF9wZXJpb2RzPTEyKS5maXQoKQ0KcGV0cm9fZXRzX2ZvcmVjYXN0ID0gcGV0cm9fZXRzLmZvcmVjYXN0KGxlbihwZXRyb190ZXN0KSkNCg0KIyBWQUxFMw0KdmFsZV9ldHMgPSBFeHBvbmVudGlhbFNtb290aGluZyh2YWxlX3RyYWluLCB0cmVuZD0nYWRkJywgc2Vhc29uYWw9J2FkZCcsIHNlYXNvbmFsX3BlcmlvZHM9MTIpLmZpdCgpDQp2YWxlX2V0c19mb3JlY2FzdCA9IHZhbGVfZXRzLmZvcmVjYXN0KGxlbih2YWxlX3Rlc3QpKQ0KDQojIFBsb3R0aW5nIEVUUyBmb3JlY2FzdHMNCnBsdC5maWd1cmUoZmlnc2l6ZT0oMTQsIDgpKQ0KcGx0LnN1YnBsb3QoMiwgMSwgMSkNCnBsdC5wbG90KHBldHJvX3JldHVybnMsIGxhYmVsPSdBY3R1YWwgRGF0YScpDQpwbHQucGxvdChwZXRyb19ldHNfZm9yZWNhc3QsIGxhYmVsPSdFVFMgRm9yZWNhc3QnLCBjb2xvcj0nZ3JlZW4nKQ0KcGx0LnRpdGxlKCdQRVRSNC5TQSAtIEVUUyBGb3JlY2FzdCcpDQpwbHQueGxhYmVsKCdNb250aC9ZZWFyJykNCnBsdC55bGFiZWwoJ1JldHVybicpDQpwbHQubGVnZW5kKCkNCg0KcGx0LnN1YnBsb3QoMiwgMSwgMikNCnBsdC5wbG90KHZhbGVfcmV0dXJucywgbGFiZWw9J0FjdHVhbCBEYXRhJykNCnBsdC5wbG90KHZhbGVfZXRzX2ZvcmVjYXN0LCBsYWJlbD0nRVRTIEZvcmVjYXN0JywgY29sb3I9J2dyZWVuJykNCnBsdC50aXRsZSgnVkFMRTMuU0EgLSBFVFMgRm9yZWNhc3QnKQ0KcGx0LnhsYWJlbCgnTW9udGgvWWVhcicpDQpwbHQueWxhYmVsKCdSZXR1cm4nKQ0KcGx0LmxlZ2VuZCgpDQpwbHQudGlnaHRfbGF5b3V0KCkNCnBsdC5zaG93KCkNCg0KIyBFdmFsdWF0aW5nIGFjY3VyYWN5DQojIEhvbHQtV2ludGVycw0KcGV0cm9faHdfc21hcGUgPSBtYXBlKHBldHJvX3Rlc3QsIHBldHJvX2h3X2ZvcmVjYXN0KSAqIDEwMA0KdmFsZV9od19zbWFwZSA9IG1hcGUodmFsZV90ZXN0LCB2YWxlX2h3X2ZvcmVjYXN0KSAqIDEwMA0KDQpwZXRyb19od19tYXBlID0gbWFlKHBldHJvX3Rlc3QsIHBldHJvX2h3X2ZvcmVjYXN0KQ0KdmFsZV9od19tYXBlID0gbWFlKHZhbGVfdGVzdCwgdmFsZV9od19mb3JlY2FzdCkNCg0KIyBFVFMNCnBldHJvX2V0c19zbWFwZSA9IG1hcGUocGV0cm9fdGVzdCwgcGV0cm9fZXRzX2ZvcmVjYXN0KSAqIDEwMA0KdmFsZV9ldHNfc21hcGUgPSBtYXBlKHZhbGVfdGVzdCwgdmFsZV9ldHNfZm9yZWNhc3QpICogMTAwDQoNCnBldHJvX2V0c19tYXBlID0gbWFlKHBldHJvX3Rlc3QsIHBldHJvX2V0c19mb3JlY2FzdCkNCnZhbGVfZXRzX21hcGUgPSBtYWUodmFsZV90ZXN0LCB2YWxlX2V0c19mb3JlY2FzdCkNCg0KIyBQcmludGluZyByZXN1bHRzDQpwcmludCgiSG9sdC1XaW50ZXJzIEFjY3VyYWN5OiIpDQpwcmludChmIlBFVFI0IFNNQVBFOiB7cGV0cm9faHdfc21hcGU6LjJmfSUiKQ0KcHJpbnQoZiJWQUxFMyBTTUFQRToge3ZhbGVfaHdfc21hcGU6LjJmfSUiKQ0KcHJpbnQoZiJQRVRSNCBNQVBFOiB7cGV0cm9faHdfbWFwZTouNGZ9IikNCnByaW50KGYiVkFMRTMgTUFQRToge3ZhbGVfaHdfbWFwZTouNGZ9IikNCg0KcHJpbnQoIlxuRVRTIEFjY3VyYWN5OiIpDQpwcmludChmIlBFVFI0IFNNQVBFOiB7cGV0cm9fZXRzX3NtYXBlOi4yZn0lIikNCnByaW50KGYiVkFMRTMgU01BUEU6IHt2YWxlX2V0c19zbWFwZTouMmZ9JSIpDQpwcmludChmIlBFVFI0IE1BUEU6IHtwZXRyb19ldHNfbWFwZTouNGZ9IikNCnByaW50KGYiVkFMRTMgTUFQRToge3ZhbGVfZXRzX21hcGU6LjRmfSIpDQpgYGANCg==