Actividad 2. Generación de escenarios futuros con modelos de pronósticos en series de tiempo

Los ejercicios de esta actividad realizados en equipo fueron elaborados por Astrid Paola González Diaz, José Santiago González Padilla, y Ana Estefanía López Alanís.

Teoría y Ejemplo de Producción Trimestral

Concepto

Una serie de tiempo es una colección de observaciones sobre un determinado fenómeno, efectuadas en momentos sucesivos, usualmente equiespaciados.

Algunos ejemplos de series de tiempo son:
1. Precios de acciones
2. Niveles de inventario
3. Rotación de personal
4. Ventas
5. PIB (GDP)

Mas información:
Libro R for Data Science (2e)

Crear la serie de tiempo

Ejemplo:

Los siguientes datos de producción trimestral inician en el primer trimestre de 2020. Se busca pronosticar la producción de los siguientes 5 trimestres.

produccion <- c(50,53,55,57,55,60)

ts1 <- ts(data = produccion, start= c(2020,1), frequency = 4)
ts1
##      Qtr1 Qtr2 Qtr3 Qtr4
## 2020   50   53   55   57
## 2021   55   60

Crear modelo ARIMA

ARIMA significa Autoregressive Integrated Moving Average o Modelo Autorregresivo Integrado de Promedio Móvil.

arima1 <- auto.arima(ts1, D=1)
arima1
## Series: ts1 
## ARIMA(0,0,0)(0,1,0)[4] with drift 
## 
## Coefficients:
##        drift
##       1.5000
## s.e.  0.1768
## 
## sigma^2 = 2.01:  log likelihood = -2.84
## AIC=9.68   AICc=-2.32   BIC=7.06
summary(arima1)
## Series: ts1 
## ARIMA(0,0,0)(0,1,0)[4] with drift 
## 
## Coefficients:
##        drift
##       1.5000
## s.e.  0.1768
## 
## sigma^2 = 2.01:  log likelihood = -2.84
## AIC=9.68   AICc=-2.32   BIC=7.06
## 
## Training set error measures:
##                      ME      RMSE       MAE        MPE      MAPE       MASE
## Training set 0.03333332 0.5787923 0.3666667 0.03685269 0.6429133 0.06111111
##                    ACF1
## Training set -0.5073047

Actividad Hershey’s

Ejemplo:

Los siguientes datos de producción mensual inician en el primer mes de 2020. Se busca pronosticar la producción de los siguientes 5 meses.

Importar base de datos y crear serie de tiempo

lechita <- read.csv("C:/Users/alexa/OneDrive/YO/Ventas_Históricas_Lechitas.csv")

ts2 <- ts(data = lechita$Ventas, start= c(2017,1), frequency = 12)
ts2
##           Jan      Feb      Mar      Apr      May      Jun      Jul      Aug
## 2017 25520.51 23740.11 26253.58 25868.43 27072.87 27150.50 27067.10 28145.25
## 2018 28463.69 26996.11 29768.20 29292.51 29950.68 30099.17 30851.26 32271.76
## 2019 32496.44 31287.28 33376.02 32949.77 34004.11 33757.89 32927.30 34324.12
##           Sep      Oct      Nov      Dec
## 2017 27546.29 28400.37 27441.98 27852.47
## 2018 31940.74 32995.93 32197.12 31984.82
## 2019 35151.28 36133.07 34799.91 34846.17

Crear modelo ARIMA

arima2 <- auto.arima(ts2, D=1)
arima2
## Series: ts2 
## ARIMA(1,0,0)(1,1,0)[12] with drift 
## 
## Coefficients:
##          ar1     sar1     drift
##       0.6383  -0.5517  288.8980
## s.e.  0.1551   0.2047   14.5026
## 
## sigma^2 = 202700:  log likelihood = -181.5
## AIC=371   AICc=373.11   BIC=375.72
summary(arima2)
## Series: ts2 
## ARIMA(1,0,0)(1,1,0)[12] with drift 
## 
## Coefficients:
##          ar1     sar1     drift
##       0.6383  -0.5517  288.8980
## s.e.  0.1551   0.2047   14.5026
## 
## sigma^2 = 202700:  log likelihood = -181.5
## AIC=371   AICc=373.11   BIC=375.72
## 
## Training set error measures:
##                    ME    RMSE      MAE        MPE      MAPE       MASE
## Training set 25.22163 343.863 227.1699 0.08059942 0.7069541 0.06491041
##                   ACF1
## Training set 0.2081043

Generar el pronóstico

pronostico2 <- forecast(arima2, level=c(95), h=5)
pronostico2
##          Point Forecast    Lo 95    Hi 95
## Jan 2020       35498.90 34616.48 36381.32
## Feb 2020       34202.17 33155.29 35249.05
## Mar 2020       36703.01 35596.10 37809.92
## Apr 2020       36271.90 35141.44 37402.36
## May 2020       37121.98 35982.07 38261.90
plot(pronostico2)

En el pronóstico realizado anteriormente, se puede observar que a pesar de haber puntos más bajos en la producción en los próximos meses (causado probablemente por tendencias de estacionalidad), se espera que la tendencia sea positiva.

Actividad Finanzas Corporativas

Información disponible

Con la función finreportr podemos obtener la siguiente información:

  • CompnayInfo() = Brinda información general como nombre, ubicación, ZIP, etc.
  • AnnualReports() = Brinda el nombre, fecha y número de acceso.
  • GetIncome() = Brinda el Estado de Resultados.
  • GetBalanceSheet() = Brinda el Balance General.
  • GetCashFlow() = Brinda el Flujo de Efectivo.
options(HTTPUserAgent = "a a@gmail.com")
CompanyInfo("JPM")
##               company        CIK  SIC state state.inc FY.end     street.address
## 1 JPMORGAN CHASE & CO 0000019617 6021    NY        DE   1231 383 MADISON AVENUE
##          city.state
## 1 NEW YORK NY 10017
AnnualReports("BABA", foreign = TRUE)
##    filing.name filing.date         accession.no
## 1       20-F/A  2024-02-23 0001193125-24-044480
## 2         20-F  2023-07-21 0000950170-23-033752
## 3         20-F  2022-07-26 0001104659-22-082622
## 4         20-F  2021-07-27 0001104659-21-096092
## 5         20-F  2020-07-09 0001104659-20-082409
## 6         20-F  2019-06-05 0001047469-19-003492
## 7         20-F  2018-07-27 0001047469-18-005257
## 8         20-F  2017-06-15 0001047469-17-004019
## 9         20-F  2016-05-24 0001047469-16-013400
## 10        20-F  2015-06-25 0001047469-15-005768
#google_income <- GetIncome("GOOG", 2016)
amazon_balance <- GetBalanceSheet("AMZN", 2015)
apple_cash <- GetCashFlow("AAPL", 2014)

Análisis Financiero de Empresa

La empresa seleccionada por el equipo 5 para llevar a cabo este análisis financiero fue Netflix. Los años que se extraerán para llevar a cabo este pronóstico son 2015 y 2018, para así obtener los datos de 6 años a partir del 2012.

Crear base de datos y serie de tiempo

En esta parte se crearán dos series de tiempo usando las métricas de marketing expense y revenue, con el fin de comparar los pronósticos de ambas métricas y ver si existe alguna relación entre las variables.

Selección métricas

options(HTTPUserAgent = "a a@gmail.com")
netflix18<- GetIncome("NFLX", 2018)
netflix15<- GetIncome("NFLX", 2015)

netflix15$Metric<- as.factor(netflix15$Metric)
netflix18$Metric<- as.factor(netflix18$Metric)
head(netflix15)
##     Metric Units     Amount  startDate    endDate
## 1 Revenues   usd 3609282000 2012-01-01 2012-12-31
## 2 Revenues   usd 1023961000 2013-01-01 2013-03-31
## 3 Revenues   usd 1069372000 2013-04-01 2013-06-30
## 4 Revenues   usd 1105999000 2013-07-01 2013-09-30
## 5 Revenues   usd 1175230000 2013-10-01 2013-12-31
## 6 Revenues   usd 4374562000 2013-01-01 2013-12-31
head(netflix18)
##     Metric Units     Amount  startDate    endDate
## 1 Revenues   usd 6779511000 2015-01-01 2015-12-31
## 2 Revenues   usd 1957736000 2016-01-01 2016-03-31
## 3 Revenues   usd 2105204000 2016-04-01 2016-06-30
## 4 Revenues   usd 2290188000 2016-07-01 2016-09-30
## 5 Revenues   usd 2477541000 2016-10-01 2016-12-31
## 6 Revenues   usd 8830669000 2016-01-01 2016-12-31

Selección marketing expense

marketing_netflix1 <- netflix15[netflix15$Metric == "Marketing Expense",]
marketing_netflix2 <- netflix18[netflix18$Metric== "Marketing Expense",]

marketing <- rbind(marketing_netflix1, marketing_netflix2)

marketing$Amount<- as.numeric(marketing$Amount)

head(marketing)
##                Metric Units     Amount  startDate    endDate
## 15  Marketing Expense   usd  439208000 2012-01-01 2012-12-31
## 16  Marketing Expense   usd  469942000 2013-01-01 2013-12-31
## 17  Marketing Expense   usd  607186000 2014-01-01 2014-12-31
## 151 Marketing Expense   usd  824092000 2015-01-01 2015-12-31
## 161 Marketing Expense   usd  991078000 2016-01-01 2016-12-31
## 171 Marketing Expense   usd 1278022000 2017-01-01 2017-12-31
marketing<- ts(data = marketing$Amount, start = c(2012,1), frequency = 1)
marketing
## Time Series:
## Start = 2012 
## End = 2017 
## Frequency = 1 
## [1]  439208000  469942000  607186000  824092000  991078000 1278022000

Selección revenues

revenue_netflix1 <- netflix15[netflix15$Metric == "Revenues",]
revenue_netflix2<- netflix18[netflix18$Metric== "Revenues",]

revenue<- rbind(revenue_netflix1, revenue_netflix2)

revenue$Amount<- as.numeric(revenue$Amount)

revenue <- revenue[-c(2:5, 7:10,13:16, 18:21),]

head(revenue)
##      Metric Units      Amount  startDate    endDate
## 1  Revenues   usd  3609282000 2012-01-01 2012-12-31
## 6  Revenues   usd  4374562000 2013-01-01 2013-12-31
## 11 Revenues   usd  5504656000 2014-01-01 2014-12-31
## 12 Revenues   usd  6779511000 2015-01-01 2015-12-31
## 17 Revenues   usd  8830669000 2016-01-01 2016-12-31
## 22 Revenues   usd 11692713000 2017-01-01 2017-12-31
revenue <- ts(data = revenue$Amount, start = c(2012,1), frequency = 1)
revenue
## Time Series:
## Start = 2012 
## End = 2017 
## Frequency = 1 
## [1]  3609282000  4374562000  5504656000  6779511000  8830669000 11692713000

Crear modelo ARIMA

Modelo marketing expense

arima_m <- auto.arima(marketing, D=1)
arima_m
## Series: marketing 
## ARIMA(0,1,0) with drift 
## 
## Coefficients:
##           drift
##       167762800
## s.e.   38132573
## 
## sigma^2 = 9.082e+15:  log likelihood = -98.4
## AIC=200.8   AICc=206.8   BIC=200.02
summary(arima_m)
## Series: marketing 
## ARIMA(0,1,0) with drift 
## 
## Coefficients:
##           drift
##       167762800
## s.e.   38132573
## 
## sigma^2 = 9.082e+15:  log likelihood = -98.4
## AIC=200.8   AICc=206.8   BIC=200.02
## 
## Training set error measures:
##                    ME     RMSE      MAE       MPE     MAPE      MASE       ACF1
## Training set 45240.85 77811930 56153374 -3.152125 8.268978 0.3347189 0.06935673

Modelo revenue

arima_r<- auto.arima(revenue, D=1)
arima_r
## Series: revenue 
## ARIMA(0,2,0) 
## 
## sigma^2 = 3.536e+17:  log likelihood = -86.49
## AIC=174.98   AICc=176.98   BIC=174.37
summary(arima_r)
## Series: revenue 
## ARIMA(0,2,0) 
## 
## sigma^2 = 3.536e+17:  log likelihood = -86.49
## AIC=174.98   AICc=176.98   BIC=174.37
## 
## Training set error measures:
##                     ME      RMSE       MAE      MPE     MAPE      MASE
## Training set 349207831 485496286 350251542 4.076958 4.100816 0.2166478
##                   ACF1
## Training set 0.3276528

Al analizar y utilizar los modelos seleccionados para llevar a cabo los pronósticos, se tiene que tomar en cuenta que el RMSE resultó elevado en el caso de ambos modelos estimados. Esto podría indicar que no se está prediciendo de manera precisa con estos modelos.

Generar el pronóstico

Pronóstico marketing expense

pronostico_m <- forecast(arima_m, level =c(95), h=5 )
pronostico_m
##      Point Forecast      Lo 95      Hi 95
## 2018     1445784800 1259000698 1632568902
## 2019     1613547600 1349394990 1877700210
## 2020     1781310400 1457790845 2104829955
## 2021     1949073200 1575504996 2322641404
## 2022     2116836000 1699174051 2534497949
plot(pronostico_m)

En el pronóstico de la métrica marketing expense se puede observar un crecimiento positivo, ligeramente exponencial. Observando más a detalle los datos, se pronostica que para 2022 haya un incremento de 78.65% en el gasto dirigido a mercadotecnia.

Pronóstico revenue

pronostico_r <- forecast(arima_r, level =c(95), h=5 )
pronostico_r
##      Point Forecast       Lo 95       Hi 95
## 2018    14554757000 13389344605 15720169395
## 2019    17416801000 14810859663 20022742337
## 2020    20278845000 15918271104 24639418896
## 2021    23140889000 16757662426 29524115574
## 2022    26002933000 17360003361 34645862639
plot(pronostico_r)

En el pronóstico de la métrica revenue se puede observar un crecimiento positivo lineal. Observando más a detalle los datos, se pronostica que para 2022 haya un incremento de 46.41% en los ingresos de la empresa.

Estos pronósticos nos muestran a grandes rasgos que los ingresos de Netflix incrementan, a medida que se invierte más en mercadotecnia. Sin embargo, hay que aclarar que esto necesariamente refleja correlación o causalidad, pero se podría comprobar con otras herramientas.

LS0tDQp0aXRsZTogIkFjdGl2aWRhZCAyLiBHZW5lcmFjacOzbiBkZSBlc2NlbmFyaW9zIGZ1dHVyb3MgY29uIG1vZGVsb3MgZGUgcHJvbsOzc3RpY29zIGVuIHNlcmllcyBkZSB0aWVtcG8iDQphdXRob3I6ICJBbmEgRXN0ZWZhbsOtYSBMw7NwZXogQWxhbsOtcyINCmRhdGU6ICIyMDI0LTAyLTE2Ig0Kb3V0cHV0OiANCiAgcm1kZm9ybWF0czo6cmVhZHRoZWRvd246DQogICAgdG9jX2RlcHRoOiAzDQogICAgY29kZV9kb3dubG9hZDogVFJVRQ0KLS0tDQoNCmBgYHtyIHNldHVwLCBpbmNsdWRlPUZBTFNFfQ0Ka25pdHI6Om9wdHNfY2h1bmskc2V0KGVjaG8gPSBUUlVFLCB3YXJuaW5nID0gRkFMU0UpDQpgYGANCg0KKkxvcyBlamVyY2ljaW9zIGRlIGVzdGEgYWN0aXZpZGFkIHJlYWxpemFkb3MgZW4gZXF1aXBvIGZ1ZXJvbiBlbGFib3JhZG9zIHBvciBBc3RyaWQgUGFvbGEgR29uesOhbGV6IERpYXosIEpvc8OpIFNhbnRpYWdvIEdvbnrDoWxleiBQYWRpbGxhLCB5IEFuYSBFc3RlZmFuw61hIEzDs3BleiBBbGFuw61zLioNCg0KIVtdKGltYWdlcy9naXBoeSAoMikgYW5hLmdpZikNCg0KYGBge3IsIGluY2x1ZGU9RkFMU0V9DQpsaWJyYXJ5KGZvcmVjYXN0KQ0KbGlicmFyeShmaW5yZXBvcnRyKQ0KbGlicmFyeShkcGx5cikNCmBgYA0KDQojIyBUZW9yw61hIHkgRWplbXBsbyBkZSBQcm9kdWNjacOzbiBUcmltZXN0cmFsDQoNCiMjIyBDb25jZXB0bw0KDQpVbmEgKipzZXJpZSBkZSB0aWVtcG8qKiBlcyB1bmEgY29sZWNjacOzbiBkZSBvYnNlcnZhY2lvbmVzIHNvYnJlIHVuIGRldGVybWluYWRvIGZlbsOzbWVubywgZWZlY3R1YWRhcyBlbiBtb21lbnRvcyBzdWNlc2l2b3MsIHVzdWFsbWVudGUgZXF1aWVzcGFjaWFkb3MuDQoNCkFsZ3Vub3MgZWplbXBsb3MgZGUgc2VyaWVzIGRlIHRpZW1wbyBzb246XA0KMS4gUHJlY2lvcyBkZSBhY2Npb25lc1wNCjIuIE5pdmVsZXMgZGUgaW52ZW50YXJpb1wNCjMuIFJvdGFjacOzbiBkZSBwZXJzb25hbFwNCjQuIFZlbnRhc1wNCjUuIFBJQiAoKkdEUCopDQoNCk1hcyBpbmZvcm1hY2nDs246XA0KW0xpYnJvICpSIGZvciBEYXRhIFNjaWVuY2UgKDJlKSpdKGh0dHBzOi8vcjRkcy5oYWRsZXkubnovKQ0KDQojIyMgQ3JlYXIgbGEgc2VyaWUgZGUgdGllbXBvDQoNCioqRWplbXBsbzoqKg0KDQpMb3Mgc2lndWllbnRlcyBkYXRvcyBkZSBwcm9kdWNjacOzbiB0cmltZXN0cmFsIGluaWNpYW4gZW4gZWwgcHJpbWVyIHRyaW1lc3RyZSBkZSAyMDIwLiBTZSBidXNjYSBwcm9ub3N0aWNhciBsYSBwcm9kdWNjacOzbiBkZSBsb3Mgc2lndWllbnRlcyA1IHRyaW1lc3RyZXMuDQoNCmBgYHtyfQ0KcHJvZHVjY2lvbiA8LSBjKDUwLDUzLDU1LDU3LDU1LDYwKQ0KDQp0czEgPC0gdHMoZGF0YSA9IHByb2R1Y2Npb24sIHN0YXJ0PSBjKDIwMjAsMSksIGZyZXF1ZW5jeSA9IDQpDQp0czENCmBgYA0KDQojIyMgQ3JlYXIgbW9kZWxvIEFSSU1BDQoNCioqQVJJTUEqKiBzaWduaWZpY2EgKkF1dG9yZWdyZXNzaXZlIEludGVncmF0ZWQgTW92aW5nIEF2ZXJhZ2UqIG8gTW9kZWxvIEF1dG9ycmVncmVzaXZvIEludGVncmFkbyBkZSBQcm9tZWRpbyBNw7N2aWwuDQoNCmBgYHtyfQ0KYXJpbWExIDwtIGF1dG8uYXJpbWEodHMxLCBEPTEpDQphcmltYTENCnN1bW1hcnkoYXJpbWExKQ0KYGBgDQoNCiMjIEFjdGl2aWRhZCBIZXJzaGV5J3MNCg0KIVtdKGltYWdlcy90c19sZWNoaXRhLmdpZikNCg0KKipFamVtcGxvOioqDQoNCkxvcyBzaWd1aWVudGVzIGRhdG9zIGRlIHByb2R1Y2Npw7NuIG1lbnN1YWwgaW5pY2lhbiBlbiBlbCBwcmltZXIgbWVzIGRlIDIwMjAuIFNlIGJ1c2NhIHByb25vc3RpY2FyIGxhIHByb2R1Y2Npw7NuIGRlIGxvcyBzaWd1aWVudGVzIDUgbWVzZXMuDQoNCiMjIyBJbXBvcnRhciBiYXNlIGRlIGRhdG9zIHkgY3JlYXIgc2VyaWUgZGUgdGllbXBvDQoNCmBgYHtyfQ0KbGVjaGl0YSA8LSByZWFkLmNzdigiQzovVXNlcnMvYWxleGEvT25lRHJpdmUvWU8vVmVudGFzX0hpc3TDs3JpY2FzX0xlY2hpdGFzLmNzdiIpDQoNCnRzMiA8LSB0cyhkYXRhID0gbGVjaGl0YSRWZW50YXMsIHN0YXJ0PSBjKDIwMTcsMSksIGZyZXF1ZW5jeSA9IDEyKQ0KdHMyDQpgYGANCg0KIyMjIENyZWFyIG1vZGVsbyBBUklNQQ0KDQpgYGB7cn0NCmFyaW1hMiA8LSBhdXRvLmFyaW1hKHRzMiwgRD0xKQ0KYXJpbWEyDQpzdW1tYXJ5KGFyaW1hMikNCmBgYA0KDQojIyMgR2VuZXJhciBlbCBwcm9uw7NzdGljbw0KDQpgYGB7cn0NCnByb25vc3RpY28yIDwtIGZvcmVjYXN0KGFyaW1hMiwgbGV2ZWw9Yyg5NSksIGg9NSkNCnByb25vc3RpY28yDQpwbG90KHByb25vc3RpY28yKQ0KYGBgDQoNCkVuIGVsIHByb27Ds3N0aWNvIHJlYWxpemFkbyBhbnRlcmlvcm1lbnRlLCBzZSBwdWVkZSBvYnNlcnZhciBxdWUgYSBwZXNhciBkZSBoYWJlciBwdW50b3MgbcOhcyBiYWpvcyBlbiBsYSBwcm9kdWNjacOzbiBlbiBsb3MgcHLDs3hpbW9zIG1lc2VzIChjYXVzYWRvIHByb2JhYmxlbWVudGUgcG9yIHRlbmRlbmNpYXMgZGUgZXN0YWNpb25hbGlkYWQpLCBzZSBlc3BlcmEgcXVlIGxhIHRlbmRlbmNpYSBzZWEgcG9zaXRpdmEuDQoNCiMjIEFjdGl2aWRhZCBGaW5hbnphcyBDb3Jwb3JhdGl2YXMNCg0KIyMjIEluZm9ybWFjacOzbiBkaXNwb25pYmxlDQoNCkNvbiBsYSBmdW5jacOzbiAqZmlucmVwb3J0ciogcG9kZW1vcyBvYnRlbmVyIGxhIHNpZ3VpZW50ZSBpbmZvcm1hY2nDs246DQoNCi0gICBDb21wbmF5SW5mbygpID0gQnJpbmRhIGluZm9ybWFjacOzbiBnZW5lcmFsIGNvbW8gbm9tYnJlLCB1YmljYWNpw7NuLCBaSVAsIGV0Yy4NCi0gICBBbm51YWxSZXBvcnRzKCkgPSBCcmluZGEgZWwgbm9tYnJlLCBmZWNoYSB5IG7Dum1lcm8gZGUgYWNjZXNvLg0KLSAgIEdldEluY29tZSgpID0gQnJpbmRhIGVsIEVzdGFkbyBkZSBSZXN1bHRhZG9zLg0KLSAgIEdldEJhbGFuY2VTaGVldCgpID0gQnJpbmRhIGVsIEJhbGFuY2UgR2VuZXJhbC4NCi0gICBHZXRDYXNoRmxvdygpID0gQnJpbmRhIGVsIEZsdWpvIGRlIEVmZWN0aXZvLg0KDQpgYGB7cn0NCm9wdGlvbnMoSFRUUFVzZXJBZ2VudCA9ICJhIGFAZ21haWwuY29tIikNCkNvbXBhbnlJbmZvKCJKUE0iKQ0KQW5udWFsUmVwb3J0cygiQkFCQSIsIGZvcmVpZ24gPSBUUlVFKQ0KI2dvb2dsZV9pbmNvbWUgPC0gR2V0SW5jb21lKCJHT09HIiwgMjAxNikNCmFtYXpvbl9iYWxhbmNlIDwtIEdldEJhbGFuY2VTaGVldCgiQU1aTiIsIDIwMTUpDQphcHBsZV9jYXNoIDwtIEdldENhc2hGbG93KCJBQVBMIiwgMjAxNCkNCmBgYA0KDQojIyBBbsOhbGlzaXMgRmluYW5jaWVybyBkZSBFbXByZXNhDQoNCiFbXShpbWFnZXMvZ2lwaHkgKDMpIGFuYS5naWYpDQoNCkxhIGVtcHJlc2Egc2VsZWNjaW9uYWRhIHBvciBlbCBlcXVpcG8gNSBwYXJhIGxsZXZhciBhIGNhYm8gZXN0ZSBhbsOhbGlzaXMgZmluYW5jaWVybyBmdWUgTmV0ZmxpeC4gTG9zIGHDsW9zIHF1ZSBzZSBleHRyYWVyw6FuIHBhcmEgbGxldmFyIGEgY2FibyBlc3RlIHByb27Ds3N0aWNvIHNvbiAyMDE1IHkgMjAxOCwgcGFyYSBhc8OtIG9idGVuZXIgbG9zIGRhdG9zIGRlIDYgYcOxb3MgYSBwYXJ0aXIgZGVsIDIwMTIuDQoNCiMjIyBDcmVhciBiYXNlIGRlIGRhdG9zIHkgc2VyaWUgZGUgdGllbXBvDQoNCkVuIGVzdGEgcGFydGUgc2UgY3JlYXLDoW4gZG9zIHNlcmllcyBkZSB0aWVtcG8gdXNhbmRvIGxhcyBtw6l0cmljYXMgZGUgKm1hcmtldGluZyBleHBlbnNlKiB5ICpyZXZlbnVlKiwgY29uIGVsIGZpbiBkZSBjb21wYXJhciBsb3MgcHJvbsOzc3RpY29zIGRlIGFtYmFzIG3DqXRyaWNhcyB5IHZlciBzaSBleGlzdGUgYWxndW5hIHJlbGFjacOzbiBlbnRyZSBsYXMgdmFyaWFibGVzLg0KDQojIyMjIFNlbGVjY2nDs24gbcOpdHJpY2FzDQoNCmBgYHtyfQ0Kb3B0aW9ucyhIVFRQVXNlckFnZW50ID0gImEgYUBnbWFpbC5jb20iKQ0KbmV0ZmxpeDE4PC0gR2V0SW5jb21lKCJORkxYIiwgMjAxOCkNCm5ldGZsaXgxNTwtIEdldEluY29tZSgiTkZMWCIsIDIwMTUpDQoNCm5ldGZsaXgxNSRNZXRyaWM8LSBhcy5mYWN0b3IobmV0ZmxpeDE1JE1ldHJpYykNCm5ldGZsaXgxOCRNZXRyaWM8LSBhcy5mYWN0b3IobmV0ZmxpeDE4JE1ldHJpYykNCmhlYWQobmV0ZmxpeDE1KQ0KaGVhZChuZXRmbGl4MTgpDQpgYGANCg0KIyMjIyBTZWxlY2Npw7NuICptYXJrZXRpbmcgZXhwZW5zZSoNCg0KYGBge3J9DQptYXJrZXRpbmdfbmV0ZmxpeDEgPC0gbmV0ZmxpeDE1W25ldGZsaXgxNSRNZXRyaWMgPT0gIk1hcmtldGluZyBFeHBlbnNlIixdDQptYXJrZXRpbmdfbmV0ZmxpeDIgPC0gbmV0ZmxpeDE4W25ldGZsaXgxOCRNZXRyaWM9PSAiTWFya2V0aW5nIEV4cGVuc2UiLF0NCg0KbWFya2V0aW5nIDwtIHJiaW5kKG1hcmtldGluZ19uZXRmbGl4MSwgbWFya2V0aW5nX25ldGZsaXgyKQ0KDQptYXJrZXRpbmckQW1vdW50PC0gYXMubnVtZXJpYyhtYXJrZXRpbmckQW1vdW50KQ0KDQpoZWFkKG1hcmtldGluZykNCg0KbWFya2V0aW5nPC0gdHMoZGF0YSA9IG1hcmtldGluZyRBbW91bnQsIHN0YXJ0ID0gYygyMDEyLDEpLCBmcmVxdWVuY3kgPSAxKQ0KbWFya2V0aW5nDQpgYGANCg0KIyMjIyBTZWxlY2Npw7NuICpyZXZlbnVlcyoNCg0KYGBge3J9DQpyZXZlbnVlX25ldGZsaXgxIDwtIG5ldGZsaXgxNVtuZXRmbGl4MTUkTWV0cmljID09ICJSZXZlbnVlcyIsXQ0KcmV2ZW51ZV9uZXRmbGl4MjwtIG5ldGZsaXgxOFtuZXRmbGl4MTgkTWV0cmljPT0gIlJldmVudWVzIixdDQoNCnJldmVudWU8LSByYmluZChyZXZlbnVlX25ldGZsaXgxLCByZXZlbnVlX25ldGZsaXgyKQ0KDQpyZXZlbnVlJEFtb3VudDwtIGFzLm51bWVyaWMocmV2ZW51ZSRBbW91bnQpDQoNCnJldmVudWUgPC0gcmV2ZW51ZVstYygyOjUsIDc6MTAsMTM6MTYsIDE4OjIxKSxdDQoNCmhlYWQocmV2ZW51ZSkNCg0KcmV2ZW51ZSA8LSB0cyhkYXRhID0gcmV2ZW51ZSRBbW91bnQsIHN0YXJ0ID0gYygyMDEyLDEpLCBmcmVxdWVuY3kgPSAxKQ0KcmV2ZW51ZQ0KYGBgDQoNCiMjIyBDcmVhciBtb2RlbG8gQVJJTUENCg0KIyMjIyBNb2RlbG8gKm1hcmtldGluZyBleHBlbnNlKg0KDQpgYGB7cn0NCmFyaW1hX20gPC0gYXV0by5hcmltYShtYXJrZXRpbmcsIEQ9MSkNCmFyaW1hX20NCnN1bW1hcnkoYXJpbWFfbSkNCmBgYA0KDQojIyMjIE1vZGVsbyAqcmV2ZW51ZSoNCg0KYGBge3J9DQphcmltYV9yPC0gYXV0by5hcmltYShyZXZlbnVlLCBEPTEpDQphcmltYV9yDQpzdW1tYXJ5KGFyaW1hX3IpDQpgYGANCg0KQWwgYW5hbGl6YXIgeSB1dGlsaXphciBsb3MgbW9kZWxvcyBzZWxlY2Npb25hZG9zIHBhcmEgbGxldmFyIGEgY2FibyBsb3MgcHJvbsOzc3RpY29zLCBzZSB0aWVuZSBxdWUgdG9tYXIgZW4gY3VlbnRhIHF1ZSBlbCBSTVNFIHJlc3VsdMOzIGVsZXZhZG8gZW4gZWwgY2FzbyBkZSBhbWJvcyBtb2RlbG9zIGVzdGltYWRvcy4gRXN0byBwb2Ryw61hIGluZGljYXIgcXVlIG5vIHNlIGVzdMOhIHByZWRpY2llbmRvIGRlIG1hbmVyYSBwcmVjaXNhIGNvbiBlc3RvcyBtb2RlbG9zLg0KDQojIyMgR2VuZXJhciBlbCBwcm9uw7NzdGljbw0KDQojIyMjIFByb27Ds3N0aWNvICptYXJrZXRpbmcgZXhwZW5zZSoNCg0KYGBge3J9DQpwcm9ub3N0aWNvX20gPC0gZm9yZWNhc3QoYXJpbWFfbSwgbGV2ZWwgPWMoOTUpLCBoPTUgKQ0KcHJvbm9zdGljb19tDQpwbG90KHByb25vc3RpY29fbSkNCmBgYA0KDQpFbiBlbCBwcm9uw7NzdGljbyBkZSBsYSBtw6l0cmljYSAqbWFya2V0aW5nIGV4cGVuc2UqIHNlIHB1ZWRlIG9ic2VydmFyIHVuIGNyZWNpbWllbnRvIHBvc2l0aXZvLCBsaWdlcmFtZW50ZSBleHBvbmVuY2lhbC4gT2JzZXJ2YW5kbyBtw6FzIGEgZGV0YWxsZSBsb3MgZGF0b3MsIHNlIHByb25vc3RpY2EgcXVlIHBhcmEgMjAyMiBoYXlhIHVuIGluY3JlbWVudG8gZGUgNzguNjUlIGVuIGVsIGdhc3RvIGRpcmlnaWRvIGEgbWVyY2Fkb3RlY25pYS4NCg0KIyMjIyBQcm9uw7NzdGljbyAqcmV2ZW51ZSoNCg0KYGBge3J9DQpwcm9ub3N0aWNvX3IgPC0gZm9yZWNhc3QoYXJpbWFfciwgbGV2ZWwgPWMoOTUpLCBoPTUgKQ0KcHJvbm9zdGljb19yDQpwbG90KHByb25vc3RpY29fcikNCmBgYA0KDQpFbiBlbCBwcm9uw7NzdGljbyBkZSBsYSBtw6l0cmljYSAqcmV2ZW51ZSogc2UgcHVlZGUgb2JzZXJ2YXIgdW4gY3JlY2ltaWVudG8gcG9zaXRpdm8gbGluZWFsLiBPYnNlcnZhbmRvIG3DoXMgYSBkZXRhbGxlIGxvcyBkYXRvcywgc2UgcHJvbm9zdGljYSBxdWUgcGFyYSAyMDIyIGhheWEgdW4gaW5jcmVtZW50byBkZSA0Ni40MSUgZW4gbG9zIGluZ3Jlc29zIGRlIGxhIGVtcHJlc2EuDQoNCkVzdG9zIHByb27Ds3N0aWNvcyBub3MgbXVlc3RyYW4gYSBncmFuZGVzIHJhc2dvcyBxdWUgbG9zIGluZ3Jlc29zIGRlIE5ldGZsaXggaW5jcmVtZW50YW4sIGEgbWVkaWRhIHF1ZSBzZSBpbnZpZXJ0ZSBtw6FzIGVuIG1lcmNhZG90ZWNuaWEuIFNpbiBlbWJhcmdvLCBoYXkgcXVlIGFjbGFyYXIgcXVlIGVzdG8gbmVjZXNhcmlhbWVudGUgcmVmbGVqYSBjb3JyZWxhY2nDs24gbyBjYXVzYWxpZGFkLCBwZXJvIHNlIHBvZHLDrWEgY29tcHJvYmFyIGNvbiBvdHJhcyBoZXJyYW1pZW50YXMuDQo=