Series con Tendencia Aleatoria- ARIMA
Consultar un activo financiero desde Yahoo
finance
library(quantmod) # api yahoo finance
library(ggplot2) # graficos
library(dplyr) # pandas de r
## serie de tiempo
library(pdfetch)
library(urca)
library(TSA)
library(fBasics)
library(TSstudio)
library(dygraphs)
library(car)
library(forecast)
library(corrplot)
library(fpp2)
library(MASS)
library(tseries)
library(lmtest)
Concexion con Api
datos=as.data.frame(pdfetch_YAHOO(c("GOOG"),from = as.Date("2010-01-01"),to=as.Date("2022-11-18")))
tail(datos)
Dimensión
dim(datos)
[1] 3243 6
names(datos)=c( "open", "high","low","close","adjclose","volume" )
datos$date=as.Date(row.names(datos))
head(datos)
Separamos en Train y Test
min(datos$date)
[1] "2010-01-04"
max(datos$date)
[1] "2022-11-17"
df_train= datos[datos$date <= "2022-10-31",]
df_test= datos[datos$date>"2022-10-31",]
Análisis Gráfico
ggplot(aes(x=date,y=close),data = df_train)+geom_line(color="darkblue")

Evolución del test
ggplot(aes(x=date,y=close),data = df_test)+geom_line(color="darkblue")

ts_plot(df_test[,c("date","close")],title = "Evolución Precio Google dÃa a dÃa")
¿Es Necesario Estabilizar la Varianza?
- Logaritmo Natural
- Potencia2
- Raiza cuadrada
- Invertir la serie
library(MASS)
box_cox=boxcox(close~ date,
data=df_train,
lambda=c(0,0.5,1))

lambda=box_cox$x[which.max(box_cox$y)]
lambda
[1] 0
df_train$LogClose=log(df_train$close)
ggplot(aes(x=date,y=LogClose),data = df_train)+geom_line(color="darkblue")

Análisis de las funciones de autocorrelación simple y
parcial
datos.train.ts=ts(df_train$LogClose) # formato de series de tiempo
plot.ts(datos.train.ts,tipe="0", col="darkgreen")

Función de autocorrelación simple
acf(datos.train.ts,lag.max = 550, main="Funcion Autocorrelacion Simple")

pacf(datos.train.ts,lag.max = 50, main="Funcion Autocorrelacion Parcial")

¿Tendencia es Aleatoria o Deterministica? : Test de Dickey -
Fuller
adf.test(datos.train.ts,alternative="stationary")
Augmented Dickey-Fuller Test
data: datos.train.ts
Dickey-Fuller = -3.2499, Lag order = 14, p-value = 0.07928
alternative hypothesis: stationary
- P valor es menor a 0.05; la serie es estacionaria en tendencia
(modelo matematico para explicar la tendencia)
- P valor es mayor a 0.05, la serie tiene al menos una raiz unitaria
por lo tanto es una seria con tendecia aleatoria
debemos aplicar un modelo ARIMA
¿Cuntas veces deberiamos diferenciar la serie?
plot.ts(diff(datos.train.ts))

Gráficas de Autocorrelación Simple y Parcial de la ST en
diferencia
par(mfrow=c(2,1))
acf(diff(datos.train.ts))
pacf(diff(datos.train.ts))

- Modelo es ARIMA con p=0, y q=0 ARIMA(0,1,0)
plot.ts(diff(diff(datos.train.ts)))

- Por ahora la Seria es ARIMA, con d=1, cual es p y q
Selección Automatica
auto.arima(datos.train.ts)
Series: datos.train.ts
ARIMA(3,1,3)
Coefficients:
ar1 ar2 ar3 ma1 ma2 ma3
-0.7998 0.6785 0.8747 0.7577 -0.6899 -0.8580
s.e. 0.0818 0.1391 0.0769 0.0776 0.1267 0.0717
sigma^2 = 0.0002821: log likelihood = 8617.13
AIC=-17220.27 AICc=-17220.23 BIC=-17177.71
Autorima con un BIC
auto.arima(datos.train.ts,d=1,max.p=5, max.q=5, ic=c("bic"))
Series: datos.train.ts
ARIMA(0,1,0)
sigma^2 = 0.0002845: log likelihood = 8600.42
AIC=-17198.84 AICc=-17198.84 BIC=-17192.76
Autorima con un AIC
auto.arima(datos.train.ts,d=1,max.p=5, max.q=5, ic=c("aic"))
Series: datos.train.ts
ARIMA(3,1,3)
Coefficients:
ar1 ar2 ar3 ma1 ma2 ma3
-0.7998 0.6785 0.8747 0.7577 -0.6899 -0.8580
s.e. 0.0818 0.1391 0.0769 0.0776 0.1267 0.0717
sigma^2 = 0.0002821: log likelihood = 8617.13
AIC=-17220.27 AICc=-17220.23 BIC=-17177.71
- Tenemos dos posibles modelos: ARIMA(0,1,0) y un Modelo
ARIMA(1,1,3)
Etapa de Estimación
Modelo1 = Arima(datos.train.ts,order = c(1,1,3),include.drift = TRUE,method = "CSS-ML")
summary(Modelo1)
Series: datos.train.ts
ARIMA(1,1,3) with drift
Coefficients:
Warning: NaNs produced
ar1 ma1 ma2 ma3 drift
-0.0242 -0.0258 0.0077 -0.0181 6e-04
s.e. NaN NaN NaN 0.0109 3e-04
sigma^2 = 0.0002838: log likelihood = 8606.85
AIC=-17201.71 AICc=-17201.68 BIC=-17165.23
Training set error measures:
ME RMSE MAE MPE MAPE MASE ACF1
Training set 9.304636e-07 0.01683029 0.01142656 -0.001693577 0.3251243 0.9985824 -0.0001070563
- y(1-0.000006)=0.96yt-1–1.012e1+0.06e2 …..
coeftest(Modelo1)
Warning: NaNs produced
z test of coefficients:
Estimate Std. Error z value Pr(>|z|)
ar1 -0.02418651 NaN NaN NaN
ma1 -0.02584458 NaN NaN NaN
ma2 0.00771704 NaN NaN NaN
ma3 -0.01806271 0.01088007 -1.6602 0.09688 .
drift 0.00055851 0.00027932 1.9995 0.04555 *
---
Signif. codes: 0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1
- Verificamos si el proceso es invertible
autoplot(Modelo1)

- Normalidad de los residuales
residuales= Modelo1$residuals
acf(residuales)

pacf(residuales)

checkresiduals(residuales,lag=15)

Test Normalidad
shapiro.test(residuales)
Shapiro-Wilk normality test
data: residuales
W = 0.91669, p-value < 2.2e-16
- p valor menor a 0.05; no hay normalidad
jarque.bera.test(residuales)
Jarque Bera Test
data: residuales
X-squared = 9174.4, df = 2, p-value < 2.2e-16
p valor menor a 0.05, no hay normalidad
La normalidad puede estar afectada por valores atipicos; hay que
retomar
Evaluar el poder predictivo
ajust=Modelo1$fitted # predicciones de train
ts.plot(datos.train.ts,ajust)
lines(ajust, col="red", type="o", cex=.5)

Evaluacion Test
df_test$LogClose=log(df_test$close)
datos.test.ts=ts(df_test$LogClose) # formato de series de tiempo
ts.plot(datos.test.ts)

Preciccion 24 dias
Tiempo=13
z_pred=forecast(Modelo1,h=Tiempo,level = c(80,95),biasadj = TRUE)
z_pred=as.data.frame(z_pred)
rownames(z_pred) <- 1:nrow(z_pred)
z_pred
ts.plot(ts(z_pred$`Point Forecast`),datos.test.ts,type="o")
lines(ts(z_pred$`Point Forecast`),col="red")

library(MLmetrics)
MAPE(exp(df_test$LogClose),exp(z_pred$`Point Forecast`))
[1] 0.05294389
RMSE
RMSE(exp(df_test$LogClose),exp(z_pred$`Point Forecast`))
[1] 5.923233
ts.plot(exp(ts(z_pred$`Point Forecast`)),exp(datos.test.ts),type="o")
lines(exp(ts(z_pred$`Point Forecast`)),col="red")

cbind(exp(df_test$LogClose),exp(z_pred$`Point Forecast`))
[,1] [,2]
[1,] 90.50 94.88985
[2,] 87.07 94.85301
[3,] 83.49 94.94275
[4,] 86.70 94.99490
[5,] 88.65 95.04799
[6,] 88.91 95.10109
[7,] 87.40 95.15422
[8,] 94.17 95.20738
[9,] 96.73 95.26057
[10,] 96.03 95.31379
[11,] 98.72 95.36704
[12,] 98.99 95.42032
[13,] 98.50 95.47363
Componentes Estacionales Complejas
- ARIMA para la parte stacional
SARIMA
par(mfrow=c(2,1))
Acf(diff(datos.train.ts),208,ci=0)
Pacf(diff(datos.train.ts),208,ci=0)

par(mfrow=c(2,1))
Acf(diff(diff(datos.train.ts)),208,ci=0)
Pacf(diff(diff(datos.train.ts)),208,ci=0)

- Estacional ARIMA(0,1,1) para la estacionalidad
Modelo1 = Arima(datos.train.ts,order = c(1,1,2),seasonal = list(order=c(1,1,2),period=12),include.drift = TRUE,method = "CSS-ML")
Warning: No drift term fitted as the order of difference is 2 or more.
coeftest(Modelo1)
z test of coefficients:
Estimate Std. Error z value Pr(>|z|)
ar1 0.956077 0.034123 28.0182 < 2.2e-16 ***
ma1 -1.006639 0.038397 -26.2164 < 2.2e-16 ***
ma2 0.040317 0.018165 2.2195 0.02645 *
sar1 -0.732106 0.151346 -4.8373 1.316e-06 ***
sma1 -0.249149 0.146974 -1.6952 0.09004 .
sma2 -0.750849 0.146786 -5.1153 3.133e-07 ***
---
Signif. codes: 0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1
Residuales
checkresiduals(Modelo1$residuals,lag=13)

shapiro.test(Modelo1$residuals)
Shapiro-Wilk normality test
data: Modelo1$residuals
W = 0.92081, p-value < 2.2e-16
Pronostico
Tiempo=13
z_pred=forecast(Modelo1,h=Tiempo,level = c(80,95))
z_pred=as.data.frame(z_pred)
rownames(z_pred) <- 1:nrow(z_pred)
z_pred
ts.plot(ts(z_pred$`Point Forecast`),datos.test.ts,type="o")
lines(ts(z_pred$`Point Forecast`),col="red")

Datos Atipicos
LS0tCnRpdGxlOiAiUiBOb3RlYm9vayIKb3V0cHV0OiBodG1sX25vdGVib29rCi0tLQoKIyAqKlNlcmllcyBjb24gVGVuZGVuY2lhIEFsZWF0b3JpYS0gQVJJTUEqKgoKIyMgKipDb25zdWx0YXIgdW4gYWN0aXZvIGZpbmFuY2llcm8gZGVzZGUgWWFob28gZmluYW5jZSoqCgpgYGB7cn0KbGlicmFyeShxdWFudG1vZCkgIyBhcGkgeWFob28gZmluYW5jZQpsaWJyYXJ5KGdncGxvdDIpICMgZ3JhZmljb3MKbGlicmFyeShkcGx5cikgIyBwYW5kYXMgZGUgcgojIyBzZXJpZSBkZSB0aWVtcG8KbGlicmFyeShwZGZldGNoKQpsaWJyYXJ5KHVyY2EpCmxpYnJhcnkoVFNBKQpsaWJyYXJ5KGZCYXNpY3MpCmxpYnJhcnkoVFNzdHVkaW8pCmxpYnJhcnkoZHlncmFwaHMpCmxpYnJhcnkoY2FyKQpsaWJyYXJ5KGZvcmVjYXN0KQpsaWJyYXJ5KGNvcnJwbG90KQpsaWJyYXJ5KGZwcDIpCmxpYnJhcnkoTUFTUykKbGlicmFyeSh0c2VyaWVzKQpsaWJyYXJ5KGxtdGVzdCkKCmBgYAoKIyMgKipDb25jZXhpb24gY29uIEFwaSoqCgpgYGB7cn0KZGF0b3M9YXMuZGF0YS5mcmFtZShwZGZldGNoX1lBSE9PKGMoIkdPT0ciKSxmcm9tID0gYXMuRGF0ZSgiMjAxMC0wMS0wMSIpLHRvPWFzLkRhdGUoIjIwMjItMTEtMTgiKSkpCnRhaWwoZGF0b3MpCmBgYAoKKipEaW1lbnNpw7NuKioKCmBgYHtyfQpkaW0oZGF0b3MpCmBgYAoKCgpgYGB7cn0KbmFtZXMoZGF0b3MpPWMoICJvcGVuIiwgImhpZ2giLCJsb3ciLCJjbG9zZSIsImFkamNsb3NlIiwidm9sdW1lIiAgKQpkYXRvcyRkYXRlPWFzLkRhdGUocm93Lm5hbWVzKGRhdG9zKSkKaGVhZChkYXRvcykKYGBgCgojIyAqKlNlcGFyYW1vcyBlbiBUcmFpbiB5IFRlc3QqKgoKYGBge3J9Cm1pbihkYXRvcyRkYXRlKQptYXgoZGF0b3MkZGF0ZSkKCmRmX3RyYWluPSBkYXRvc1tkYXRvcyRkYXRlIDw9ICIyMDIyLTEwLTMxIixdCmRmX3Rlc3Q9IGRhdG9zW2RhdG9zJGRhdGU+IjIwMjItMTAtMzEiLF0KCgpgYGAKCgojIyAqKkFuw6FsaXNpcyBHcsOhZmljbyoqCgpgYGB7cn0KZ2dwbG90KGFlcyh4PWRhdGUseT1jbG9zZSksZGF0YSA9IGRmX3RyYWluKStnZW9tX2xpbmUoY29sb3I9ImRhcmtibHVlIikKYGBgCgoqKkV2b2x1Y2nDs24gZGVsIHRlc3QqKgoKYGBge3J9CmdncGxvdChhZXMoeD1kYXRlLHk9Y2xvc2UpLGRhdGEgPSBkZl90ZXN0KStnZW9tX2xpbmUoY29sb3I9ImRhcmtibHVlIikKYGBgCgoKCmBgYHtyfQp0c19wbG90KGRmX3Rlc3RbLGMoImRhdGUiLCJjbG9zZSIpXSx0aXRsZSA9ICJFdm9sdWNpw7NuIFByZWNpbyBHb29nbGUgZMOtYSBhIGTDrWEiKQpgYGAKCgoKIyMgKirCv0VzIE5lY2VzYXJpbyBFc3RhYmlsaXphciBsYSBWYXJpYW56YT8qKgoKKiBMb2dhcml0bW8gTmF0dXJhbAoqIFBvdGVuY2lhMgoqIFJhaXphIGN1YWRyYWRhCiogSW52ZXJ0aXIgbGEgc2VyaWUKCmBgYHtyfQpsaWJyYXJ5KE1BU1MpCmJveF9jb3g9Ym94Y294KGNsb3NlfiBkYXRlLAogICAgICAgICAgICAgICBkYXRhPWRmX3RyYWluLAogICAgICAgICAgICAgICBsYW1iZGE9YygwLDAuNSwxKSkKYGBgCmBgYHtyfQpsYW1iZGE9Ym94X2NveCR4W3doaWNoLm1heChib3hfY294JHkpXQpsYW1iZGEKYGBgCgpgYGB7cn0KZGZfdHJhaW4kTG9nQ2xvc2U9bG9nKGRmX3RyYWluJGNsb3NlKQpgYGAKCmBgYHtyfQpnZ3Bsb3QoYWVzKHg9ZGF0ZSx5PUxvZ0Nsb3NlKSxkYXRhID0gZGZfdHJhaW4pK2dlb21fbGluZShjb2xvcj0iZGFya2JsdWUiKQpgYGAKCiMjICoqQW7DoWxpc2lzIGRlIGxhcyBmdW5jaW9uZXMgZGUgYXV0b2NvcnJlbGFjacOzbiBzaW1wbGUgeSBwYXJjaWFsKioKCmBgYHtyfQpkYXRvcy50cmFpbi50cz10cyhkZl90cmFpbiRMb2dDbG9zZSkgIyBmb3JtYXRvIGRlIHNlcmllcyBkZSB0aWVtcG8KcGxvdC50cyhkYXRvcy50cmFpbi50cyx0aXBlPSIwIiwgY29sPSJkYXJrZ3JlZW4iKQpgYGAKCgojIyAqKkZ1bmNpw7NuIGRlIGF1dG9jb3JyZWxhY2nDs24gc2ltcGxlKioKYGBge3J9CmFjZihkYXRvcy50cmFpbi50cyxsYWcubWF4ID0gNTUwLCBtYWluPSJGdW5jaW9uIEF1dG9jb3JyZWxhY2lvbiBTaW1wbGUiKQpgYGAKCmBgYHtyfQpwYWNmKGRhdG9zLnRyYWluLnRzLGxhZy5tYXggPSA1MCwgbWFpbj0iRnVuY2lvbiBBdXRvY29ycmVsYWNpb24gUGFyY2lhbCIpCmBgYAoKKiBRdWUgbm8gZXMgdW4gbW9kZWxvIGVzdGFjaW9uYXJpbywgZXN0YSBzZXJpZSB0aWVuZSB0ZW5kZW5jaWEKCiogcG9yIGxvIHRhbnRvIGxhIHNlcmllIHRpZW5lIHRlbmRlbmNpYQoKIyMgKirCv1RlbmRlbmNpYSBlcyBBbGVhdG9yaWEgbyBEZXRlcm1pbmlzdGljYT8gOiBUZXN0IGRlIERpY2tleSAtIEZ1bGxlcioqCgpgYGB7cn0KYWRmLnRlc3QoZGF0b3MudHJhaW4udHMsYWx0ZXJuYXRpdmU9InN0YXRpb25hcnkiKQpgYGAKCiogUCB2YWxvciBlcyBtZW5vciBhIDAuMDU7IGxhIHNlcmllIGVzIGVzdGFjaW9uYXJpYSBlbiAgdGVuZGVuY2lhIChtb2RlbG8gbWF0ZW1hdGljbyBwYXJhIGV4cGxpY2FyIGxhIHRlbmRlbmNpYSkKKiBQIHZhbG9yIGVzIG1heW9yIGEgMC4wNSwgbGEgc2VyaWUgdGllbmUgYWwgbWVub3MgdW5hIHJhaXogdW5pdGFyaWEgcG9yIGxvIHRhbnRvIGVzIHVuYSBzZXJpYSBjb24gdGVuZGVjaWEgYWxlYXRvcmlhCgoqKmRlYmVtb3MgYXBsaWNhciB1biBtb2RlbG8gQVJJTUEqKgoKCiMjICoqwr9DdW50YXMgdmVjZXMgZGViZXJpYW1vcyBkaWZlcmVuY2lhciBsYSBzZXJpZT8qKgoKYGBge3J9CnBsb3QudHMoZGlmZihkYXRvcy50cmFpbi50cykpCmBgYAoKKipHcsOhZmljYXMgZGUgQXV0b2NvcnJlbGFjacOzbiBTaW1wbGUgeSBQYXJjaWFsIGRlIGxhIFNUIGVuIGRpZmVyZW5jaWEqKgoKYGBge3J9CnBhcihtZnJvdz1jKDIsMSkpCmFjZihkaWZmKGRhdG9zLnRyYWluLnRzKSkKcGFjZihkaWZmKGRhdG9zLnRyYWluLnRzKSkKYGBgCgoqIE1vZGVsbyBlcyBBUklNQSBjb24gcD0wLCB5IHE9MCAgQVJJTUEoMCwxLDApCgoKYGBge3J9CnBsb3QudHMoZGlmZihkaWZmKGRhdG9zLnRyYWluLnRzKSkpCmBgYAoKCiogUG9yIGFob3JhIGxhIFNlcmlhIGVzIEFSSU1BLCBjb24gZD0xLCBjdWFsIGVzIHAgeSBxCgoKIyMgKipTZWxlY2Npw7NuIEF1dG9tYXRpY2EqKgoKYGBge3J9CmF1dG8uYXJpbWEoZGF0b3MudHJhaW4udHMpCmBgYAoKKiBlbCBBdXRvcmltYSBub3Mgc3VnaWVyZSB1biBtb2RlbG8gQVJJTUEgKDEsMSwzKQoKKiB5KDEtYik9YjArYjF5dC1lMStlMitlMwoKKipBdXRvcmltYSBjb24gdW4gQklDKioKYGBge3J9CmF1dG8uYXJpbWEoZGF0b3MudHJhaW4udHMsZD0xLG1heC5wPTUsIG1heC5xPTUsIGljPWMoImJpYyIpKQpgYGAKCioqQXV0b3JpbWEgY29uIHVuIEFJQyoqCgoKYGBge3J9CmF1dG8uYXJpbWEoZGF0b3MudHJhaW4udHMsZD0xLG1heC5wPTUsIG1heC5xPTUsIGljPWMoImFpYyIpKQpgYGAKCiogVGVuZW1vcyBkb3MgcG9zaWJsZXMgbW9kZWxvczogQVJJTUEoMCwxLDApIHkgdW4gTW9kZWxvIEFSSU1BKDEsMSwzKQoKCgojICoqRXRhcGEgZGUgRXN0aW1hY2nDs24qKgoKCmBgYHtyfQpNb2RlbG8xID0gQXJpbWEoZGF0b3MudHJhaW4udHMsb3JkZXIgPSBjKDEsMSwzKSxpbmNsdWRlLmRyaWZ0ID0gVFJVRSxtZXRob2QgPSAiQ1NTLU1MIikKc3VtbWFyeShNb2RlbG8xKQpgYGAKCgoKKiB5KDEtMC4wMDAwMDYpPTAuOTZ5dC0xLS0xLjAxMmUxKzAuMDZlMiAuLi4uLgoKCmBgYHtyfQpjb2VmdGVzdChNb2RlbG8xKQpgYGAKCiogVmVyaWZpY2Ftb3Mgc2kgZWwgcHJvY2VzbyBlcyBpbnZlcnRpYmxlCgpgYGB7cn0KYXV0b3Bsb3QoTW9kZWxvMSkKYGBgCgoqIE5vcm1hbGlkYWQgZGUgbG9zIHJlc2lkdWFsZXMKCmBgYHtyfQpyZXNpZHVhbGVzPSBNb2RlbG8xJHJlc2lkdWFscwphY2YocmVzaWR1YWxlcykKYGBgCmBgYHtyfQpwYWNmKHJlc2lkdWFsZXMpCmBgYAoKYGBge3J9CmNoZWNrcmVzaWR1YWxzKHJlc2lkdWFsZXMsbGFnPTE1KQpgYGAKCiMjIyAqKlRlc3QgTm9ybWFsaWRhZCoqCgpgYGB7cn0Kc2hhcGlyby50ZXN0KHJlc2lkdWFsZXMpCmBgYAoKKiBwIHZhbG9yIG1lbm9yIGEgMC4wNTsgbm8gaGF5IG5vcm1hbGlkYWQKCgpgYGB7cn0KamFycXVlLmJlcmEudGVzdChyZXNpZHVhbGVzKQpgYGAKCiogcCB2YWxvciBtZW5vciBhIDAuMDUsIG5vIGhheSBub3JtYWxpZGFkCgoKKiBMYSBub3JtYWxpZGFkIHB1ZWRlIGVzdGFyIGFmZWN0YWRhIHBvciB2YWxvcmVzIGF0aXBpY29zOyBoYXkgcXVlIHJldG9tYXIKCgojIyAqKkV2YWx1YXIgZWwgcG9kZXIgcHJlZGljdGl2byoqCgpgYGB7cn0KYWp1c3Q9TW9kZWxvMSRmaXR0ZWQgIyBwcmVkaWNjaW9uZXMgZGUgdHJhaW4gIAp0cy5wbG90KGRhdG9zLnRyYWluLnRzLGFqdXN0KQpsaW5lcyhhanVzdCwgY29sPSJyZWQiLCB0eXBlPSJvIiwgY2V4PS41KQpgYGAKCgojIyAqKkV2YWx1YWNpb24gVGVzdCoqCgoKYGBge3J9CmRmX3Rlc3QkTG9nQ2xvc2U9bG9nKGRmX3Rlc3QkY2xvc2UpCmRhdG9zLnRlc3QudHM9dHMoZGZfdGVzdCRMb2dDbG9zZSkgIyBmb3JtYXRvIGRlIHNlcmllcyBkZSB0aWVtcG8KdHMucGxvdChkYXRvcy50ZXN0LnRzKQpgYGAKCgoqKlByZWNpY2Npb24gMjQgZGlhcyoqCgpgYGB7cn0KVGllbXBvPTEzCnpfcHJlZD1mb3JlY2FzdChNb2RlbG8xLGg9VGllbXBvLGxldmVsID0gYyg4MCw5NSksYmlhc2FkaiA9IFRSVUUpCnpfcHJlZD1hcy5kYXRhLmZyYW1lKHpfcHJlZCkKcm93bmFtZXMoel9wcmVkKSA8LSAxOm5yb3coel9wcmVkKQp6X3ByZWQKYGBgCgoKYGBge3J9CnRzLnBsb3QodHMoel9wcmVkJGBQb2ludCBGb3JlY2FzdGApLGRhdG9zLnRlc3QudHMsdHlwZT0ibyIpCmxpbmVzKHRzKHpfcHJlZCRgUG9pbnQgRm9yZWNhc3RgKSxjb2w9InJlZCIpCmBgYAoKYGBge3J9CmxpYnJhcnkoTUxtZXRyaWNzKQpgYGAKCmBgYHtyfQpNQVBFKGV4cChkZl90ZXN0JExvZ0Nsb3NlKSxleHAoel9wcmVkJGBQb2ludCBGb3JlY2FzdGApKQpgYGAKCiogNiUKCioqUk1TRSoqCgpgYGB7cn0KUk1TRShleHAoZGZfdGVzdCRMb2dDbG9zZSksZXhwKHpfcHJlZCRgUG9pbnQgRm9yZWNhc3RgKSkKYGBgCgoKYGBge3J9CnRzLnBsb3QoZXhwKHRzKHpfcHJlZCRgUG9pbnQgRm9yZWNhc3RgKSksZXhwKGRhdG9zLnRlc3QudHMpLHR5cGU9Im8iKQpsaW5lcyhleHAodHMoel9wcmVkJGBQb2ludCBGb3JlY2FzdGApKSxjb2w9InJlZCIpCmBgYApgYGB7cn0KY2JpbmQoZXhwKGRmX3Rlc3QkTG9nQ2xvc2UpLGV4cCh6X3ByZWQkYFBvaW50IEZvcmVjYXN0YCkpCmBgYAoKIyMgKipDb21wb25lbnRlcyBFc3RhY2lvbmFsZXMgQ29tcGxlamFzKioKCiogQVJJTUEgcGFyYSBsYSBwYXJ0ZSBzdGFjaW9uYWwKCioqU0FSSU1BKioKCmBgYHtyfQpwYXIobWZyb3c9YygyLDEpKSAKQWNmKGRpZmYoZGF0b3MudHJhaW4udHMpLDIwOCxjaT0wKQpQYWNmKGRpZmYoZGF0b3MudHJhaW4udHMpLDIwOCxjaT0wKQoKYGBgCmBgYHtyfQpwYXIobWZyb3c9YygyLDEpKSAKQWNmKGRpZmYoZGlmZihkYXRvcy50cmFpbi50cykpLDIwOCxjaT0wKQpQYWNmKGRpZmYoZGlmZihkYXRvcy50cmFpbi50cykpLDIwOCxjaT0wKQoKYGBgCiogRXN0YWNpb25hbCBBUklNQSgwLDEsMSkgcGFyYSBsYSBlc3RhY2lvbmFsaWRhZAoKYGBge3J9Ck1vZGVsbzEgPSBBcmltYShkYXRvcy50cmFpbi50cyxvcmRlciA9IGMoMSwxLDIpLHNlYXNvbmFsID0gbGlzdChvcmRlcj1jKDEsMSwyKSxwZXJpb2Q9MTIpLGluY2x1ZGUuZHJpZnQgPSBUUlVFLG1ldGhvZCA9ICJDU1MtTUwiKQpzdW1tYXJ5KE1vZGVsbzEpCmBgYAoKCmBgYHtyfQpjb2VmdGVzdChNb2RlbG8xKQpgYGAKCioqUmVzaWR1YWxlcyoqCgpgYGB7cn0KY2hlY2tyZXNpZHVhbHMoTW9kZWxvMSRyZXNpZHVhbHMsbGFnPTEzKQpgYGAKCmBgYHtyfQpzaGFwaXJvLnRlc3QoTW9kZWxvMSRyZXNpZHVhbHMpCmBgYAoKCiMjICoqUHJvbm9zdGljbyoqCgpgYGB7cn0KVGllbXBvPTEzCnpfcHJlZD1mb3JlY2FzdChNb2RlbG8xLGg9VGllbXBvLGxldmVsID0gYyg4MCw5NSkpCnpfcHJlZD1hcy5kYXRhLmZyYW1lKHpfcHJlZCkKcm93bmFtZXMoel9wcmVkKSA8LSAxOm5yb3coel9wcmVkKQp6X3ByZWQKYGBgCgpgYGB7cn0KdHMucGxvdCh0cyh6X3ByZWQkYFBvaW50IEZvcmVjYXN0YCksZGF0b3MudGVzdC50cyx0eXBlPSJvIikKbGluZXModHMoel9wcmVkJGBQb2ludCBGb3JlY2FzdGApLGNvbD0icmVkIikKYGBgCgojIyAqKkRhdG9zIEF0aXBpY29zKioKCgoKCg==