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")

  • Que no es un modelo estacionario, esta serie tiene tendencia

  • por lo tanto la serie tiene tendencia

¿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
  • el Autorima nos sugiere un modelo ARIMA (1,1,3)

  • y(1-b)=b0+b1yt-e1+e2+e3

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
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
autoplot(Modelo1)

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
  • 6%

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==