Librerias para series de tiempo
library(lubridate)
Attaching package: ‘lubridate’
The following objects are masked from ‘package:base’:
date, intersect, setdiff, union
library(tseries)
Registered S3 method overwritten by 'quantmod':
method from
as.zoo.data.frame zoo
‘tseries’ version: 0.10-49
‘tseries’ is a package for time series analysis and
computational finance.
See ‘library(help="tseries")’ for details.
library(forecast)
library(ggplot2)
library(zoo)
Attaching package: ‘zoo’
The following objects are masked from ‘package:base’:
as.Date, as.Date.numeric
library(chron)
Attaching package: ‘chron’
The following object is masked from ‘package:tseries’:
is.weekend
The following objects are masked from ‘package:lubridate’:
days, hours, minutes, seconds, years
library(dygraphs)
Registered S3 method overwritten by 'htmlwidgets':
method from
print.htmlwidget tools:rstudio
library(readxl)
library(highcharter)
Registered S3 method overwritten by 'data.table':
method from
print.data.table
Fechas
Dependiendo el valor de entrada tenemos que usar alguna de las tres, el valor de salida siempre sera: YYYY-MM-DD
ymd(19931123)
[1] "1993-11-23"
dmy(23111993)
[1] "1993-11-23"
mdy(11231993)
[1] "1993-11-23"
Fechas y tiempo
mytimepoint = ymd_hm('1993-11-23 11:23', tz = "America/Argentina/Buenos_Aires")
class(mytimepoint)
[1] "POSIXct" "POSIXt"
Para extraer los componentes:
minute(mytimepoint)
[1] 23
day(mytimepoint)
[1] 23
hour(mytimepoint)
[1] 11
year(mytimepoint)
[1] 1993
month(mytimepoint)
[1] 11
wday(mytimepoint, label = T, abbr = F)
[1] martes
Levels: domingo < lunes < martes < miércoles < jueves < viernes < sábado
Calculando intervalo entre dos momentos temporales:
time1 = ymd_hm('1993-11-23 11:23', tz = "America/Argentina/Buenos_Aires")
time2 = ymd_hm('1995-12-23 11:23', tz = "America/Argentina/Buenos_Aires")
myinterval = interval(time1, time2)
class(myinterval)
[1] "Interval"
attr(,"package")
[1] "lubridate"
Outliers y NA
mydata = read.csv('Rmissing.csv')
myts = ts(mydata$mydata)
plot(myts)

Podemos tratar los datos faltantes:
Nos llena los NA con la observación anterior
myts.NAlocf = na.locf(myts)
Rellena con el valor que queremos:
# myts.NAfil = fill(myts, 33)
Detección de outliers:
myts1 = tsoutliers(myts)
myts1
$index
[1] 45 98 172 211
$replacements
[1] 28.41242 39.78616 33.59838 37.96883
Limpia los NA y Outliers:
Se reemplazan los faltantes y se nivelan los outliers
#
# mytsclean = tsclean(myts)
#
# plot(mytsclean)
plot(tsclean(myts))

Datos de Variables Financieras:
Se importan datos de series temporales de los precios de Starbucks y Microsoft
Especificamos la fecha inicial, final y la frecuencia.
La seriede tiempo sbux inicia en: 1993 3 y finaliza en: 2008 3 con una frecuencia de: 12
La seriede tiempo msft inicia en: 1993 3 y finaliza en: 2008 3 con una frecuencia de: 12
Subconjunto de una Serie Temporal:
subconjunto = window(sbux.ts, start = c(1993,3), end = c (1993,8))
subconjunto
Mar Apr May Jun Jul Aug
1993 1.13 1.15 1.43 1.46 1.41 1.44
Combinar Series de Tiempo:
combinadas_ts = cbind(sbux.ts, msft.ts)
Plot de Starbucks:

Plot de las dos Series:

Utilizando ZOO
Creamos una fecha con secuencias:
td = seq(as.Date('1993/3/1'),as.Date('2008/3/1'), 'months')
Otra alternativa es agarrar la fecha del data frame y convertirla:
td2 = as.Date(sbux$Date, format = '%m/%d/%Y')
Juntamos ambas Series:
# Combinamos el indice de tiempo a las dos series
sbux.z = zoo(x = sbux$Adj.Close, order.by = td)
msft.z = zoo(x = msft$Adj.Close, order.by = td)
# Extraer indices y precios
indices_sbux.z = index(sbux.z )
valores_sbux.z = coredata(sbux.z )
# Combinamos las dos series
combinadas_zoom = cbind(sbux.z, msft.z)
Importar datos directamente desde ZOO
# Es identico al creado anteriorimente
sbuxzoo = read.zoo('sbuxPrices.csv',
format = '%m/%d/%Y',
sep = ",",
header = T)
Importar datos directamente de Yahoo Finance:
time series ends 2022-01-05
time series ends 2022-01-05
Grafico en conjunto:

Libreria dygraphs
Esta libreria contiene visualizaciones interactivas:
Ruido Blanco
Es un tipo especial de serie temporal, donde los datos no siguen ningún patrón.
Para que una serie sea ruido blanco tiene que tener una media constante, una varianza constante y no tener autocorrelacion en ningún periodo.
La autocorrelacion mide cuan correlacionada es una serie con versiones anteriores de si misma.
La falta de autocorrelacion es que no hay una relación clara entre valores pasados y valores presentes
\[p=corr(X_t,X_t-1) \]

Caminata Aleatoria
Es un tipo especial de serie de tiempo en donde los valores tienden a persistir en el tiempo y las diferencias entre periodos son simplemente ruido blanco.
Cualquier caminata aleatoria: Su precio es igual al precio anterior mas un ruido blanco
Las mejores estimaciones para los precios de hoy serán los precios de ayer

Estacionariedad
Prueba Dickey Fuller
La prueba nos sirve para testear si hay o no autocorrelacion en una serie temporal.
Esta propiedad hace referencia a cuando una serie es estable a lo largo del tiempo, es decir, cuando la media y la varianza son constantes y ademas no presenta tendencias
Un ejemplo de una serie débilmente estacionaria es una serie de ruido blanco, ya que la autocorrelacion entre dos puntos siempre es cero
Serie Estacionaria
Simulamos datos normalmente distribuidos de 1000 observaciones y aleatorios, el dickey fuller test lo vamos a ejecutar para ver si hay significancia o no. El p valor es menor a un nivel de significancia del 0.05, por lo tanto, la hipotesis nula es rechazada, por lo tanto la serie sera estacionaria.
Augmented Dickey-Fuller Test
data: x
Dickey-Fuller = -11.557, Lag order = 9, p-value = 0.01
alternative hypothesis: stationary

Serie No Estacionaria
Augmented Dickey-Fuller Test
data: y
Dickey-Fuller = -2.6997, Lag order = 9, p-value = 0.2821
alternative hypothesis: stationary

Descomponer Serie
Warning in adf.test(nottem) : p-value smaller than printed p-value
Augmented Dickey-Fuller Test
data: nottem
Dickey-Fuller = -12.998, Lag order = 6, p-value = 0.01
alternative hypothesis: stationary

Autocorrelacion
La correlación nos dice la similitud con respecto a las series temporales cambian sus valores. Si estamos con una sola serie temporal la correlación se calcula con valores pasados de la misma serie, es decir, la correlación entre una secuencia y si misma. Mide el nivel de semejanza entre una secuencia de varios periodos atrás y los datos reales, esta secuencia del pasado se llama retraso o rezago.
La función de autocorrelacion ACP Function nos va a proporcionar la autocorrelacion para cualquier retraso que consideremos.
Autocorrelacion Parcial
Son efectos de segunda mano, donde los valores presentes son afectados por valores pasados y estos mismos también son afectados por valores pasados
Autocorrelacion para ruido blanco


Autocorrelacion para ruido blanco.(Serie Estacionaria)
acf(x, plot = T)

Estacionalidad
Sugiere que ciertas tendencias aparecerán de forma cíclica, por ejemplo, las temperaturas suben y bajan a lo largo del tiempo. La series se pueden descomponer bajo efecto tendencia donde podemos quitar un patrón sostenido en los datos, la estacional los efectos cíclicos y el residual el error entre los datos reales y el modelo que estamos ejecutando.
La descomposición clásica puede ser aditiva o multiplicativa, en la aditiva podemos suponer que en cualquier momento del tiempo el valor observado es la suma de la tendencia, el objeto estacional y el efecto residual para ese periodo.
La descomposición multiplicativa nos dice que la serie original es un producto de los tres efectos anteriories.
Suavizado con SMA
Utilizaremos un Data Set y crearemos una variable de SMA de orden 3 y de orden 9.



Método Exponencial
etsmodel = ets(nottem)
plot(nottem, lwd = 3)
lines(etsmodel$fitted, col = "red")

# Forecast de 12 meses
# Intervalo de predicción: nivel de confianza 95%
plot(forecast(etsmodel, h = 12, level = 95))

# Holt Winters multiplicativo
etsmodmult = ets(nottem, model ="MMM") # MMM todo multiplicativo #error, tendencia, estacionalidad. Defecto ZZZ
# Comparacion serie original con el fitted luego de aplicar el modelo multiplicativo
plot(nottem, lwd = 3)
lines(etsmodmult$fitted, col = "red")

etsmodmultMAM = ets(nottem, model ="MAM")
plot(nottem, lwd = 3)
lines(etsmodmultMAM$fitted, col = "red")

compare_models = as.data.frame(nottem)
compare_models$fitted_MMM = etsmodmult$fitted
compare_models$fitted_MAM = etsmodmultMAM$fitted
# Error cuadrático medio
cat('El error cuadratico medio para tendencia multiplicativa es: ', sum((compare_models$x - compare_models$fitted_MMM) / nrow(compare_models)), 'y para tendencia aditiva: ', sum((compare_models$x - compare_models$fitted_MAM) / nrow(compare_models)))
El error cuadratico medio para tendencia multiplicativa es: 0.163262 y para tendencia aditiva: 0.02354155
Modelos Predictivos

¿Como seleccionar el mejor modelo?
1) Escoger el modelo
2) Dividir los datos de entrenamiento y de prueba
3) Ajustar el modelo a los datos de entrenamiento
4) Evaluar el modelo en los datos de prueba
5) Re-Ajustar el modelo con todos los datos
6) Predecir los datos futuros
Criterios de selección del modelo:
AIC = AKaike: Evalúa la colección de modelos que tenemos y va a estimar la calidad de cada modelo en relación con los modelos restantes, el criterio va a penalizar en base al numero de parámetros que tiene, es decir, en base a su complejidad.
BIC = Baysesian: Tiene criterios Bayesianos. Utilizar los dos criterios es lo recomendado.
Conceptos Claves:
Overfitting: Sobreajuste del modelo. Puede no funcionar de forma correcta ya que esta sobre ajustado el modelo
Los residuos del modelo deben parecerse a ruido blanco, es decir, estacionarios
Modelo Autorregresivo
La autocorrelacion nos va a permitir desarrollar este modelo que se basa en tener en cuenta valores pasados. Es un modelo lineal donde los valores del periodo actual son la suma de resultados pasados multiplicados por un coeficiente mas un error.
El coeficiente siempre tiene que estar entre - 1 y 1. No puede ser superior en valor absoluto
El residuo van a ser diferencias impredecibles, si hay un patrón se va a identificar en las variables del modelo, el residuo es la diferencia entre el valor real y el estimado
Modelo AR(1)
$$
\[\begin{aligned}
Y_i &= c + \phi_i Y_{i-1} + \epsilon_i
\end{aligned}\]
$$
Modelo AR(2)
$$
\[\begin{aligned}
Y_i &= c + \phi_i Y_{i-1} + \phi_i Y_{i-2} + \epsilon_i
\end{aligned}\]
$$ Modelo AR(n)
$$
\[\begin{aligned}
Y_i &= c + \phi_i Y_{i-1} + \phi_2 Y_{i-2} + ..\phi_n Y_{i-n} + \epsilon_i
\end{aligned}\]
$$ Estos modelos de muchos procesos AR pueden contener ruido. Es importante primero verificar de forma teórica para no hacerlo mas complejo.
Las funciones de autocorrelacion y autocorrelacion parcial nos van a decir cuantos rezagos podemos incluir en el modelo.
ARMA
Estos modelos combinan los anteriores vistos. Toma en cuenta los valores pasados y los errores.
$$
\[\begin{aligned}
Y_i &= c + \phi_i Y_{i-1} + \alpha_i \epsilon_{i-1} + \epsilon_i
\end{aligned}\]
$$ Donde el coeficiente AR es:
$$
\[\begin{aligned}
\phi_i
\end{aligned}\]
$$ Y el coeficiente MA es:
\[
\begin{aligned}
\alpha_i
\end{aligned}
\]
ARIMA
Cuando se trata de series no estacionarias utilizaremos el modelo autorregresivo integrado de medias moviles. La parte integrada del modelo explica el numero de diferencias no estacionales que debemos examinar para establecer estacionariedad.
ARIMA (p,d,q)
El modelo va a tener tres ordenes, p,d y q. Donde p = Componente AR, d = Orden de Integración y 1 = Componente MA.
Donde un ARIMA(p,0,q) = ARMA(p,q).
Numero de linces capturados anualmente en EEUU

En este gráfico obtenemos las funciones de autocorrelacion y el mismo gráfico anterior.
Observamos cierto comportamiento cíclico, no parece haber estacionalidad.
En la FACP podemos ver que el primer y segundo retraso son significativos.
Vemos informacion del data set:
tail(lynx)
Time Series:
Start = 1929
End = 1934
Frequency = 1
[1] 485 662 1000 1590 2657 3396

Probamos un modelo AR(2)
modelarima = arima(lynx, order = c(2,0,0))
modelarima
Call:
arima(x = lynx, order = c(2, 0, 0))
Coefficients:
ar1 ar2 intercept
1.1474 -0.5997 1545.4458
s.e. 0.0742 0.0740 181.6736
sigma^2 estimated as 768159: log likelihood = -935.02, aic = 1878.03
Observamos los residuos del modelo:

Ejecutamos un MA(2):
Call:
arima(x = lynx, order = c(0, 0, 2))
Coefficients:
ma1 ma2 intercept
1.1407 0.4697 1545.3670
s.e. 0.0776 0.0721 224.5215
sigma^2 estimated as 855092: log likelihood = -941.03, aic = 1890.06

Hacemos un test de p valor para ver si la serie es estacionaria al 5 % de nivel de significancia:
La serie es estacionaria con un p valor de: 0.01
Probamos un modelo AR(4) ya que en la FACP se puede ver un rezago 4 significativo:
Podemos ver residuos dentro de la zona de no significación, que siguen aproximadamente una distribución normal y la serie temporal de los residuos vemos que es estacionaria para el modelo de orden 4.
Series: lynx
ARIMA(4,0,0) with non-zero mean
Coefficients:
ar1 ar2 ar3 ar4 mean
1.1246 -0.7174 0.2634 -0.2543 1547.3859
s.e. 0.0903 0.1367 0.1361 0.0897 136.8501
sigma^2 estimated as 748457: log likelihood=-931.11
AIC=1874.22 AICc=1875.01 BIC=1890.64
Ljung-Box test
data: Residuals from ARIMA(4,0,0) with non-zero mean
Q* = 13.201, df = 5, p-value = 0.02157
Model df: 5. Total lags used: 10

Haciendo Pronósticos:
El area oscura es el intervalo de confianza

Valores estimados:
arimaforecast$mean
Time Series:
Start = 1935
End = 1949
Frequency = 1
[1] 2980.7782 2114.6447 1361.7211 839.0137 668.7873 874.3079 1281.3753 1679.8363 1933.3503 1987.5494 1868.0533 1660.2155 1462.0024
[14] 1342.9317 1326.8693
Volatilidad
Hay modelos especiales que miden la volatilidad, es decir, la magnitud de los residuos, cuan volátiles sean las predicciones
Los modelos ARCH (Autorregresivos. con heterocedasticidad condicional) son los mas comunes para estudiar series temporales con volatilidad. A diferencia de la familia ARIMA este modelo tiene varias ecuaciones, una para la media y otra para la varianza.
La combinaron entre la heterocedasticidad y la condicional nos dice que la varianza dependerá de otros valores, es decir, de valores autorregresivos. Usaremos valores pasados para medir la varianza del periodo actual.
Predicciones


ARIMA(2,0,2)(1,1,1)[12] with drift : Inf
ARIMA(0,0,0)(0,1,0)[12] with drift : 33.18552
ARIMA(1,0,0)(1,1,0)[12] with drift : 21.24655
ARIMA(0,0,1)(0,1,1)[12] with drift : 4.650892
ARIMA(0,0,0)(0,1,0)[12] : 31.10897
ARIMA(0,0,1)(0,1,0)[12] with drift : 30.61776
ARIMA(0,0,1)(1,1,1)[12] with drift : Inf
ARIMA(0,0,1)(0,1,2)[12] with drift : Inf
ARIMA(0,0,1)(1,1,0)[12] with drift : 22.19067
ARIMA(0,0,1)(1,1,2)[12] with drift : Inf
ARIMA(0,0,0)(0,1,1)[12] with drift : 3.449011
ARIMA(0,0,0)(1,1,1)[12] with drift : Inf
ARIMA(0,0,0)(0,1,2)[12] with drift : Inf
ARIMA(0,0,0)(1,1,0)[12] with drift : 21.62052
ARIMA(0,0,0)(1,1,2)[12] with drift : Inf
ARIMA(1,0,0)(0,1,1)[12] with drift : 4.220857
ARIMA(1,0,1)(0,1,1)[12] with drift : -0.587426
ARIMA(1,0,1)(0,1,0)[12] with drift : 23.21427
ARIMA(1,0,1)(1,1,1)[12] with drift : Inf
ARIMA(1,0,1)(0,1,2)[12] with drift : Inf
ARIMA(1,0,1)(1,1,0)[12] with drift : 15.62801
ARIMA(1,0,1)(1,1,2)[12] with drift : Inf
ARIMA(2,0,1)(0,1,1)[12] with drift : -1.704778
ARIMA(2,0,1)(0,1,0)[12] with drift : 23.66859
ARIMA(2,0,1)(1,1,1)[12] with drift : Inf
ARIMA(2,0,1)(0,1,2)[12] with drift : Inf
ARIMA(2,0,1)(1,1,0)[12] with drift : 14.07389
ARIMA(2,0,1)(1,1,2)[12] with drift : Inf
ARIMA(2,0,0)(0,1,1)[12] with drift : -0.02401758
ARIMA(3,0,1)(0,1,1)[12] with drift : 0.151561
ARIMA(2,0,2)(0,1,1)[12] with drift : -2.250542
ARIMA(2,0,2)(0,1,0)[12] with drift : Inf
ARIMA(2,0,2)(0,1,2)[12] with drift : Inf
ARIMA(2,0,2)(1,1,0)[12] with drift : 14.78926
ARIMA(2,0,2)(1,1,2)[12] with drift : Inf
ARIMA(1,0,2)(0,1,1)[12] with drift : -2.392319
ARIMA(1,0,2)(0,1,0)[12] with drift : 23.28728
ARIMA(1,0,2)(1,1,1)[12] with drift : Inf
ARIMA(1,0,2)(0,1,2)[12] with drift : Inf
ARIMA(1,0,2)(1,1,0)[12] with drift : 13.22757
ARIMA(1,0,2)(1,1,2)[12] with drift : Inf
ARIMA(0,0,2)(0,1,1)[12] with drift : 2.159611
ARIMA(1,0,3)(0,1,1)[12] with drift : -0.5346
ARIMA(0,0,3)(0,1,1)[12] with drift : 2.35328
ARIMA(2,0,3)(0,1,1)[12] with drift : -0.1783977
ARIMA(1,0,2)(0,1,1)[12] : -4.59822
ARIMA(1,0,2)(0,1,0)[12] : 21.08953
ARIMA(1,0,2)(1,1,1)[12] : Inf
ARIMA(1,0,2)(0,1,2)[12] : Inf
ARIMA(1,0,2)(1,1,0)[12] : 11.00814
ARIMA(1,0,2)(1,1,2)[12] : Inf
ARIMA(0,0,2)(0,1,1)[12] : 0.002026895
ARIMA(1,0,1)(0,1,1)[12] : -2.719768
ARIMA(2,0,2)(0,1,1)[12] : -4.515401
ARIMA(1,0,3)(0,1,1)[12] : -2.778658
ARIMA(0,0,1)(0,1,1)[12] : 2.592817
ARIMA(0,0,3)(0,1,1)[12] : 0.1663591
ARIMA(2,0,1)(0,1,1)[12] : -3.911039
ARIMA(2,0,3)(0,1,1)[12] : Inf
Best model: ARIMA(1,0,2)(0,1,1)[12]
Estimaremos 3 años:
Prediccion de Linces:
La serie es estacionaria con un p valor de: 0.01
Descomposición de la serie:
Podemos observar, en la serie estacionaria, en la función de autocorrelacion parcial tenemos el primer y segundo rezago muy significativos, luego el cuarto también..

Modelo Auto ARIMA:
myarima = auto.arima(lynx, trace = T,
stepwise = F,
approximation = T)
Fitting models using approximations to speed things up...
ARIMA(0,0,0) with zero mean : 2080.721
ARIMA(0,0,0) with non-zero mean : 2006.724
ARIMA(0,0,1) with zero mean : 1971.633
ARIMA(0,0,1) with non-zero mean : 1918.454
ARIMA(0,0,2) with zero mean : 1923.633
ARIMA(0,0,2) with non-zero mean : 1890.07
ARIMA(0,0,3) with zero mean : 1911.329
ARIMA(0,0,3) with non-zero mean : 1888.462
ARIMA(0,0,4) with zero mean : 1904.649
ARIMA(0,0,4) with non-zero mean : 1888.861
ARIMA(0,0,5) with zero mean : 1906.728
ARIMA(0,0,5) with non-zero mean : 1885.904
ARIMA(1,0,0) with zero mean : 1934.293
ARIMA(1,0,0) with non-zero mean : 1926.793
ARIMA(1,0,1) with zero mean : 1902.375
ARIMA(1,0,1) with non-zero mean : 1890.554
ARIMA(1,0,2) with zero mean : 1902.539
ARIMA(1,0,2) with non-zero mean : 1888.371
ARIMA(1,0,3) with zero mean : 1904.57
ARIMA(1,0,3) with non-zero mean : 1889.878
ARIMA(1,0,4) with zero mean : 1906.532
ARIMA(1,0,4) with non-zero mean : 1888.773
ARIMA(2,0,0) with zero mean : 1906.818
ARIMA(2,0,0) with non-zero mean : 1878.042
ARIMA(2,0,1) with zero mean : 1903.406
ARIMA(2,0,1) with non-zero mean : 1879.57
ARIMA(2,0,2) with zero mean : 1905.591
ARIMA(2,0,2) with non-zero mean : 1876.453
ARIMA(2,0,3) with zero mean : Inf
ARIMA(2,0,3) with non-zero mean : Inf
ARIMA(3,0,0) with zero mean : 1904.652
ARIMA(3,0,0) with non-zero mean : 1881.03
ARIMA(3,0,1) with zero mean : 1906.551
ARIMA(3,0,1) with non-zero mean : Inf
ARIMA(3,0,2) with zero mean : Inf
ARIMA(3,0,2) with non-zero mean : 1879.019
ARIMA(4,0,0) with zero mean : 1907.789
ARIMA(4,0,0) with non-zero mean : 1876.139
ARIMA(4,0,1) with zero mean : Inf
ARIMA(4,0,1) with non-zero mean : 1877.57
ARIMA(5,0,0) with zero mean : 1906.868
ARIMA(5,0,0) with non-zero mean : 1878.465
Now re-fitting the best model(s) without approximations...
Best model: ARIMA(4,0,0) with non-zero mean
Modelo Auto ARIMA con AR(8):
myarima = auto.arima(lynx, trace = T,
stepwise = F,
approximation = T,
max.order=8, max.p=8)
Fitting models using approximations to speed things up...
ARIMA(0,0,0) with zero mean : 2080.721
ARIMA(0,0,0) with non-zero mean : 2006.724
ARIMA(0,0,1) with zero mean : 1971.633
ARIMA(0,0,1) with non-zero mean : 1918.454
ARIMA(0,0,2) with zero mean : 1923.633
ARIMA(0,0,2) with non-zero mean : 1890.07
ARIMA(0,0,3) with zero mean : 1911.329
ARIMA(0,0,3) with non-zero mean : 1888.462
ARIMA(0,0,4) with zero mean : 1904.649
ARIMA(0,0,4) with non-zero mean : 1888.861
ARIMA(0,0,5) with zero mean : 1906.728
ARIMA(0,0,5) with non-zero mean : 1885.904
ARIMA(1,0,0) with zero mean : 1934.293
ARIMA(1,0,0) with non-zero mean : 1926.793
ARIMA(1,0,1) with zero mean : 1902.375
ARIMA(1,0,1) with non-zero mean : 1890.554
ARIMA(1,0,2) with zero mean : 1902.539
ARIMA(1,0,2) with non-zero mean : 1888.371
ARIMA(1,0,3) with zero mean : 1904.57
ARIMA(1,0,3) with non-zero mean : 1889.878
ARIMA(1,0,4) with zero mean : 1906.532
ARIMA(1,0,4) with non-zero mean : 1888.773
ARIMA(1,0,5) with zero mean : Inf
ARIMA(1,0,5) with non-zero mean : 1885.078
ARIMA(2,0,0) with zero mean : 1906.818
ARIMA(2,0,0) with non-zero mean : 1878.042
ARIMA(2,0,1) with zero mean : 1903.406
ARIMA(2,0,1) with non-zero mean : 1879.57
ARIMA(2,0,2) with zero mean : 1905.591
ARIMA(2,0,2) with non-zero mean : 1876.453
ARIMA(2,0,3) with zero mean : Inf
ARIMA(2,0,3) with non-zero mean : Inf
ARIMA(2,0,4) with zero mean : Inf
ARIMA(2,0,4) with non-zero mean : Inf
ARIMA(2,0,5) with zero mean : Inf
ARIMA(2,0,5) with non-zero mean : Inf
ARIMA(3,0,0) with zero mean : 1904.652
ARIMA(3,0,0) with non-zero mean : 1881.03
ARIMA(3,0,1) with zero mean : 1906.551
ARIMA(3,0,1) with non-zero mean : Inf
ARIMA(3,0,2) with zero mean : Inf
ARIMA(3,0,2) with non-zero mean : 1879.019
ARIMA(3,0,3) with zero mean : Inf
ARIMA(3,0,3) with non-zero mean : Inf
ARIMA(3,0,4) with zero mean : Inf
ARIMA(3,0,4) with non-zero mean : Inf
ARIMA(3,0,5) with zero mean : Inf
ARIMA(3,0,5) with non-zero mean : Inf
ARIMA(4,0,0) with zero mean : 1907.789
ARIMA(4,0,0) with non-zero mean : 1876.139
ARIMA(4,0,1) with zero mean : Inf
ARIMA(4,0,1) with non-zero mean : 1877.57
ARIMA(4,0,2) with zero mean : Inf
ARIMA(4,0,2) with non-zero mean : Inf
ARIMA(4,0,3) with zero mean : Inf
ARIMA(4,0,3) with non-zero mean : 1883.491
ARIMA(4,0,4) with zero mean : Inf
ARIMA(4,0,4) with non-zero mean : Inf
ARIMA(5,0,0) with zero mean : 1906.868
ARIMA(5,0,0) with non-zero mean : 1878.465
ARIMA(5,0,1) with zero mean : Inf
ARIMA(5,0,1) with non-zero mean : Inf
ARIMA(5,0,2) with zero mean : Inf
ARIMA(5,0,2) with non-zero mean : Inf
ARIMA(5,0,3) with zero mean : Inf
ARIMA(5,0,3) with non-zero mean : Inf
ARIMA(6,0,0) with zero mean : 1904.575
ARIMA(6,0,0) with non-zero mean : 1880.568
ARIMA(6,0,1) with zero mean : Inf
ARIMA(6,0,1) with non-zero mean : Inf
ARIMA(6,0,2) with zero mean : Inf
ARIMA(6,0,2) with non-zero mean : Inf
ARIMA(7,0,0) with zero mean : 1895.474
ARIMA(7,0,0) with non-zero mean : 1881.193
ARIMA(7,0,1) with zero mean : 1878.376
ARIMA(7,0,1) with non-zero mean : 1874.647
ARIMA(8,0,0) with zero mean : 1866.807
ARIMA(8,0,0) with non-zero mean : 1860.933
Now re-fitting the best model(s) without approximations...
Best model: ARIMA(8,0,0) with non-zero mean
Prediccion Arg
Prediccion saldos caja de ahorro ARG.

ARIMA(2,1,2) with drift : Inf
ARIMA(0,1,0) with drift : 4048.257
ARIMA(1,1,0) with drift : Inf
ARIMA(0,1,1) with drift : 4041.301
ARIMA(0,1,0) : 4048.841
ARIMA(1,1,1) with drift : 4033.881
ARIMA(2,1,1) with drift : Inf
ARIMA(1,1,2) with drift : 4025.809
ARIMA(0,1,2) with drift : 4023.928
ARIMA(0,1,3) with drift : 4007.848
ARIMA(1,1,3) with drift : 4006.809
ARIMA(2,1,3) with drift : Inf
ARIMA(1,1,4) with drift : Inf
ARIMA(0,1,4) with drift : Inf
ARIMA(2,1,4) with drift : Inf
ARIMA(1,1,3) : 4019.882
Best model: ARIMA(1,1,3) with drift

Filtro de Hampel
Para cada observación de la variable o serie de tiempo X, se calcula la mediana de una ventana (un subconjunto de valores). Ejemplo: Si nuestro conjunto de datos es una serie temporal de 100 observaciones diarias y nos fijamos en la observación 10, vamos a mirar un subconjunto de observaciones que se llamaran ventana, tenemos observaciones alrededor de donde estamos parados, vamos a tener vecinos, de cada lado de la observación tomada. 9 datos anteriores y 90 posteriores. Para cada ventana se calcula la mediana (medida representativa y robusta), es decir, si en esa ventana se encuentran los atípicos, la mediana va a ser una medida que represente el conjunto sin que este afectada por los atípicos. La media no es robusta porque se ve afectada por valores atípicos.
Luego se calculara para cada grupo de observaciones la distancia entre cada valor individual con la mediana del grupo, esto nos dará una desviación respecto a la mediana y para tener una medida representativa de todo el grupo se sacara la mediana de todas las desviaciones absolutas. Cambien conocida como mediana de la desviación absoluta con respecto a la mediana", que también se conoce como MAD (median absolute deviation).
La idea es que si una muestra difiere de la mediana en más de k desviaciones estándar, se considera un dato atípico y se reemplaza por el valor de la mediana.
Usualmente se suele seleccionar k=3, lo que se conoce como la regla de las “3 sigmas”. Pero esto puede depender del problema concreto.
Además entre el MAD y la desviación estándar se cumple la siguiente propiedad:
𝜎 ≈ 1.4826 MAD
Para el filtro de Hampel necesitamos definir dos cosas: 1. El tamaño de la ventana (cuántos vecinos vamos a considerar). 2. El número de desviaciones para identificar a los atípicos (k).
Un umbral más alto hace que el filtro sea más tolerante, uno más bajo identificará más puntos como valores atípicos.
library(pracma)
set.seed(33)
# Generar los datos aleatorios
x = numeric(1024)
z = rnorm(1024)
x[1] = z[1]
for (i in 2:1024) {
x[i] = 0.4*x[i-1] + 0.8*x[i-1]*z[i-1] + z[i]
}
# Aplicar el Filtro de Hampel a nuestra serie temporal X con el parametro k que representa la ventana, es la cantidad de vecinos que vamos a considerar para hacer el calculo.
omad = hampel(x, k=20)
# Indice de los atípicos detectados:
omad$ind
[1] 30 73 77 78 80 81 82 83 107 122 123 127 128 129 130
[16] 131 132 168 184 185 215 216 217 218 219 221 223 224 279 280
[31] 281 282 283 284 285 286 329 330 361 417 418 455 460 461 481
[46] 505 538 539 540 541 574 575 671 672 700 732 733 734 743 788
[61] 791 822 827 828 829 848 849 874 897 898 899 900 901 915 916
[76] 917 918 919 920 974
# Gráfico detectando atípicos
#
plot(1:1024, x, type="l")
points(omad$ind, x[omad$ind], pch=21, col="darkred")

# Nueva serie sin atípicos
x_new = omad$y
# Ambas series
plot(1:1024, x, type="l",col="red",xlim=c(0, 1050), ylim=c(-60,70))
par(new=TRUE)
plot(1:1024, x_new, type="l",col="blue",xlim=c(0, 1050), ylim=c(-60,70),axes= FALSE, xlab='', ylab='' )

NA
NA
NA
LS0tDQp0aXRsZTogIlNlcmllcyBkZSBUaWVtcG8iDQpzdWJ0aXRsZTogSW50cm9kdWNjaW9uIGFsIEFuYWxpc2lzIGRlIFNlcmllcyBkZSBUaWVtcG8NCm91dHB1dDoNCiAgaHRtbF9ub3RlYm9vazoNCiAgICB0aGVtZTogY2VydWxlYW4NCiAgICB0b2M6IHllcw0KICAgIHRvY19mbG9hdDogeWVzDQotLS0NCg0KIyBMaWJyZXJpYXMgcGFyYSBzZXJpZXMgZGUgdGllbXBvDQoNCg0KYGBge3IsIG1lc3NhZ2UgPSBGQUxTRSAsIHdhcm5pbmc9RkFMU0V9DQoNCmxpYnJhcnkobHVicmlkYXRlKQ0KbGlicmFyeSh0c2VyaWVzKQ0KbGlicmFyeShmb3JlY2FzdCkNCmxpYnJhcnkoZ2dwbG90MikNCmxpYnJhcnkoem9vKQ0KbGlicmFyeShjaHJvbikNCmxpYnJhcnkoZHlncmFwaHMpDQpsaWJyYXJ5KHJlYWR4bCkNCmxpYnJhcnkoaGlnaGNoYXJ0ZXIpDQpgYGANCg0KDQojIEZlY2hhcw0KDQoqKl9EZXBlbmRpZW5kbyBlbCB2YWxvciBkZSBlbnRyYWRhIHRlbmVtb3MgcXVlIHVzYXIgYWxndW5hIGRlIGxhcyB0cmVzLCBlbCB2YWxvciBkZSBzYWxpZGEgc2llbXByZSBzZXJhOiBZWVlZLU1NLUREXyoqDQoNCmBgYHtyfQ0KDQp5bWQoMTk5MzExMjMpDQoNCmRteSgyMzExMTk5MykNCg0KbWR5KDExMjMxOTkzKQ0KDQoNCmBgYA0KDQoNCiMgRmVjaGFzIHkgdGllbXBvDQoNCg0KYGBge3J9DQoNCg0KbXl0aW1lcG9pbnQgPSB5bWRfaG0oJzE5OTMtMTEtMjMgMTE6MjMnLCB0eiA9ICJBbWVyaWNhL0FyZ2VudGluYS9CdWVub3NfQWlyZXMiKQ0KDQpjbGFzcyhteXRpbWVwb2ludCkNCg0KDQpgYGANCioqX1BhcmEgZXh0cmFlciBsb3MgY29tcG9uZW50ZXM6XyoqDQoNCmBgYHtyfQ0KbWludXRlKG15dGltZXBvaW50KQ0KDQpkYXkobXl0aW1lcG9pbnQpDQoNCmhvdXIobXl0aW1lcG9pbnQpDQoNCnllYXIobXl0aW1lcG9pbnQpDQoNCm1vbnRoKG15dGltZXBvaW50KQ0KDQp3ZGF5KG15dGltZXBvaW50LCBsYWJlbCA9IFQsIGFiYnIgPSBGKQ0KYGBgDQoqKl9DYWxjdWxhbmRvIGludGVydmFsbyBlbnRyZSBkb3MgbW9tZW50b3MgdGVtcG9yYWxlczpfKioNCg0KYGBge3J9DQoNCg0KDQoNCnRpbWUxID0geW1kX2htKCcxOTkzLTExLTIzIDExOjIzJywgdHogPSAiQW1lcmljYS9BcmdlbnRpbmEvQnVlbm9zX0FpcmVzIikNCg0KDQp0aW1lMiA9IHltZF9obSgnMTk5NS0xMi0yMyAxMToyMycsIHR6ID0gIkFtZXJpY2EvQXJnZW50aW5hL0J1ZW5vc19BaXJlcyIpDQoNCg0KbXlpbnRlcnZhbCA9IGludGVydmFsKHRpbWUxLCB0aW1lMikNCg0KDQpjbGFzcyhteWludGVydmFsKQ0KDQpgYGANCg0KDQojIE91dGxpZXJzIHkgTkENCg0KYGBge3J9DQpteWRhdGEgPSByZWFkLmNzdignUm1pc3NpbmcuY3N2JykNCg0KbXl0cyA9IHRzKG15ZGF0YSRteWRhdGEpDQoNCnBsb3QobXl0cykNCmBgYA0KDQoqKl9Qb2RlbW9zIHRyYXRhciBsb3MgZGF0b3MgZmFsdGFudGVzOl8qKg0KDQoNCioqX05vcyBsbGVuYSBsb3MgTkEgY29uIGxhIG9ic2VydmFjacOzbiBhbnRlcmlvcl8qKg0KDQpgYGB7cn0NCg0KbXl0cy5OQWxvY2YgPSBuYS5sb2NmKG15dHMpDQoNCg0KDQpgYGANCg0KKipfUmVsbGVuYSBjb24gZWwgdmFsb3IgcXVlIHF1ZXJlbW9zOl8qKg0KDQpgYGB7cn0NCiMgbXl0cy5OQWZpbCA9IGZpbGwobXl0cywgMzMpDQpgYGANCg0KDQoqKl9EZXRlY2Npw7NuIGRlIG91dGxpZXJzOl8qKg0KDQpgYGB7cn0NCg0KbXl0czEgPSB0c291dGxpZXJzKG15dHMpDQpteXRzMQ0KDQoNCmBgYA0KDQoNCioqX0xpbXBpYSBsb3MgTkEgeSBPdXRsaWVyczpfKioNCg0KKipfU2UgcmVlbXBsYXphbiBsb3MgZmFsdGFudGVzIHkgc2Ugbml2ZWxhbiBsb3Mgb3V0bGllcnNfKioNCg0KYGBge3IsIHdhcm5pbmc9RkFMU0V9DQoNCg0KcGxvdCh0c2NsZWFuKG15dHMpKQ0KDQpgYGANCg0KYGBge3J9DQoNCg0KDQpgYGANCg0KDQojIERhdG9zIGRlIFZhcmlhYmxlcyBGaW5hbmNpZXJhczoNCg0KKipfU2UgaW1wb3J0YW4gZGF0b3MgZGUgc2VyaWVzIHRlbXBvcmFsZXMgZGUgbG9zIHByZWNpb3MgZGUgU3RhcmJ1Y2tzIHkgTWljcm9zb2Z0XyoqDQoNCg0KKipfRXNwZWNpZmljYW1vcyBsYSBmZWNoYSBpbmljaWFsLCBmaW5hbCB5IGxhIGZyZWN1ZW5jaWEuXyoqDQoNCmBgYHtyLCBlY2hvPUZBTFNFfQ0KDQpzYnV4ID0gcmVhZC5jc3YoJ3NidXhQcmljZXMuY3N2JykNCg0KDQpzYnV4LnRzID0gdHMoZGF0YSA9IHNidXgkQWRqLkNsb3NlLCBmcmVxdWVuY3kgPSAxMiwgc3RhcnQgPSBjKDE5OTMsMyksIGVuZCA9IGMoMjAwOCwzKSkNCg0KDQptc2Z0ID0gcmVhZC5jc3YoJ21zZnRQcmljZXMuY3N2JykNCg0KDQptc2Z0LnRzID0gdHMoZGF0YSA9IG1zZnQkQWRqLkNsb3NlLCBmcmVxdWVuY3kgPSAxMiwgc3RhcnQgPSBjKDE5OTMsMyksIGVuZCA9IGMoMjAwOCwzKSkNCg0KDQpjYXQoJ0xhIHNlcmllZGUgdGllbXBvIHNidXggaW5pY2lhIGVuOiAnLHN0YXJ0KHNidXgudHMpLCAneSBmaW5hbGl6YSBlbjogJyxlbmQoc2J1eC50cyksDQogICAgJ2NvbiB1bmEgZnJlY3VlbmNpYSBkZTogJywgZnJlcXVlbmN5KHNidXgudHMpLCAnXG5cbicpDQoNCg0KY2F0KCdMYSBzZXJpZWRlIHRpZW1wbyBtc2Z0IGluaWNpYSBlbjogJyxzdGFydChtc2Z0LnRzKSwgJ3kgZmluYWxpemEgZW46ICcsZW5kKG1zZnQudHMpLA0KICAgICdjb24gdW5hIGZyZWN1ZW5jaWEgZGU6ICcsIGZyZXF1ZW5jeShtc2Z0LnRzKSkNCg0KDQoNCg0KYGBgDQoNCg0KKipfU3ViY29uanVudG8gZGUgdW5hIFNlcmllIFRlbXBvcmFsOl8qKg0KDQoNCmBgYHtyfQ0KDQpzdWJjb25qdW50byA9IHdpbmRvdyhzYnV4LnRzLCBzdGFydCA9IGMoMTk5MywzKSwgZW5kID0gYyAoMTk5Myw4KSkNCnN1YmNvbmp1bnRvDQoNCg0KDQpgYGANCg0KDQoqKl9Db21iaW5hciBTZXJpZXMgZGUgVGllbXBvOl8qKg0KDQoNCmBgYHtyfQ0KDQoNCg0KY29tYmluYWRhc190cyA9IGNiaW5kKHNidXgudHMsIG1zZnQudHMpDQoNCg0KYGBgDQoNCg0KDQoqKl9QbG90IGRlIFN0YXJidWNrczpfKioNCg0KDQpgYGB7ciwgZWNobz1GQUxTRX0NCg0KDQpwbG90KHNidXgudHMsIGNvbCA9ICdyZWQnLCBsd2QgPSAyLjUsIHlsYWIgPSAnY2llcnJlJywgbWFpbiA9ICdQcmVjaW8gZGUgY2llcnJlIG1lbnN1YWwgZGUgU3RhcmJ1Y2tzJykNCg0KDQpgYGANCg0KDQoqKl9QbG90IGRlIGxhcyBkb3MgU2VyaWVzOl8qKg0KDQpgYGB7ciwgZWNobz1GQUxTRX0NCg0KDQpwbG90KGNvbWJpbmFkYXNfdHMsIHBsb3QudHlwZSA9ICdzaW5nbGUnLA0KICAgICBtYWluID0gJ1ByZWNpbyBtZW5zdWFsIGRlIFN0YXJidWNrcyB5IE1pY3Jvc29mdCcsDQogICAgIHlsYWIgPSAnUHJlY2lvIGRlIGNpZXJyZScsDQogICAgIGNvbCA9IGMoJ3JlZCcsJ2JsdWUnKSwNCiAgICAgbHR5PSAxOjEsDQogICAgIGx3ZCA9IDIuNSkNCmxlZ2VuZCh4PSAndG9wbGVmdCcsDQogICAgICAgbGVnZW5kID0gYygnc2J1eCcsICdtc2Z0JykgLA0KICAgICAgIGNvbCA9IGMoJ3JlZCcsJ2JsdWUnKSwNCiAgICAgICBsdHk9IDE6MSwNCiAgICAgICBsd2QgPSAyLjUpIA0KDQoNCmBgYA0KDQojIFV0aWxpemFuZG8gWk9PDQoNCg0KKipfQ3JlYW1vcyB1bmEgZmVjaGEgY29uIHNlY3VlbmNpYXM6XyoqDQoNCmBgYHtyfQ0KDQoNCnRkID0gc2VxKGFzLkRhdGUoJzE5OTMvMy8xJyksYXMuRGF0ZSgnMjAwOC8zLzEnKSwgJ21vbnRocycpDQoNCg0KYGBgDQoNCioqX090cmEgYWx0ZXJuYXRpdmEgZXMgYWdhcnJhciBsYSBmZWNoYSAgZGVsIGRhdGEgZnJhbWUgeSBjb252ZXJ0aXJsYTpfKioNCg0KYGBge3J9DQoNCnRkMiA9IGFzLkRhdGUoc2J1eCREYXRlLCBmb3JtYXQgPSAnJW0vJWQvJVknKQ0KDQoNCg0KYGBgDQoNCg0KKipfSnVudGFtb3MgYW1iYXMgU2VyaWVzOl8qKg0KDQpgYGB7cn0NCg0KDQojIENvbWJpbmFtb3MgZWwgaW5kaWNlIGRlIHRpZW1wbyBhIGxhcyBkb3Mgc2VyaWVzDQoNCnNidXgueiA9IHpvbyh4ID0gc2J1eCRBZGouQ2xvc2UsIG9yZGVyLmJ5ID0gdGQpDQptc2Z0LnogPSB6b28oeCA9IG1zZnQkQWRqLkNsb3NlLCBvcmRlci5ieSA9IHRkKQ0KDQojIEV4dHJhZXIgaW5kaWNlcyB5IHByZWNpb3MNCg0KaW5kaWNlc19zYnV4LnogPSBpbmRleChzYnV4LnogKQ0KDQoNCnZhbG9yZXNfc2J1eC56ID0gY29yZWRhdGEoc2J1eC56ICkNCg0KDQojIENvbWJpbmFtb3MgbGFzIGRvcyBzZXJpZXMNCg0KY29tYmluYWRhc196b29tID0gY2JpbmQoc2J1eC56LCBtc2Z0LnopDQoNCg0KYGBgDQoNCg0KDQoNCg0KKipfSW1wb3J0YXIgZGF0b3MgZGlyZWN0YW1lbnRlIGRlc2RlIFpPT18qKg0KDQoNCmBgYHtyLCBtZXNzYWdlPUZBTFNFfQ0KDQojIEVzIGlkZW50aWNvIGFsIGNyZWFkbyBhbnRlcmlvcmltZW50ZQ0KDQpzYnV4em9vID0gcmVhZC56b28oJ3NidXhQcmljZXMuY3N2JywgDQogICAgICAgICAgICAgICAgICAgZm9ybWF0ID0gJyVtLyVkLyVZJywNCiAgICAgICAgICAgICAgICAgICBzZXAgPSAiLCIsDQogICAgICAgICAgICAgICAgICAgaGVhZGVyID0gVCkNCg0KDQpgYGANCg0KKipfSW1wb3J0YXIgZGF0b3MgZGlyZWN0YW1lbnRlIGRlIFlhaG9vIEZpbmFuY2U6XyoqDQoNCg0KDQpgYGB7ciwgd2FybmluZz1GQUxTRSwgZWNobz1GQUxTRX0NCg0KaG95ID0gdG9kYXkoKQ0KDQoNCg0Kc3RhcmJfeWFob28gPSBnZXQuaGlzdC5xdW90ZShpbnN0cnVtZW50ID0gInNidXgiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICBzdGFydCA9ICIxOTkzLTAzLTAxIiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZW5kID0gaG95LA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICBxdW90ZSA9ICdBZGpDbG9zZScsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgIHByb3ZpZGVyID0gInlhaG9vIiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgb3JpZ2luID0gIjE5NzAtMDEtMDEiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICBjb21wcmVzc2lvbiA9ICdkJywNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcmV0Y2xhc3MgPSAnem9vJykNCg0KbWNzZl95YWhvbyA9IGdldC5oaXN0LnF1b3RlKGluc3RydW1lbnQgPSAibXNmdCIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgIHN0YXJ0ID0gIjE5OTMtMDMtMDEiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICBlbmQgPSBob3ksDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgIHF1b3RlID0gJ0FkakNsb3NlJywNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcHJvdmlkZXIgPSAieWFob28iLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICBvcmlnaW4gPSAiMTk3MC0wMS0wMSIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNvbXByZXNzaW9uID0gJ2QnLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICByZXRjbGFzcyA9ICd6b28nKQ0KDQoNCmBgYA0KDQoqKl9HcmFmaWNvIGVuIGNvbmp1bnRvOl8qKg0KDQpgYGB7ciwgZWNobz1GQUxTRX0NCg0KDQpwbG90KGNiaW5kKHN0YXJiX3lhaG9vLCBtY3NmX3lhaG9vKSwgcGxvdC50eXBlID0gJ3NpbmdsZScsDQogICAgICAgICAgbWFpbiA9ICdwcmVjaW8gbWVuc3VhbCBkZSBzdGFyYnVja3MgeSBtaWNyb3NvZnQnLA0KICAgICAgICAgICB5bGFiID0gJ3ByZWNpbyBkZSBjaWVycmUnLA0KICAgICAgICAgICBjb2wgPSBjKCdyZWQnLCdibHVlJyksDQogICAgICAgICAgIGx0eT0gMToxLA0KICAgICAgICAgICBsd2QgPSAyLjUpDQpsZWdlbmQoeD0gJ3RvcGxlZnQnLA0KICAgICAgIGxlZ2VuZCA9IGMoJ3NidXgnLCAnbXNmdCcpICwNCiAgICAgICBjb2wgPSBjKCdyZWQnLCdibHVlJyksDQogICAgICAgbHR5PSAxOjEsDQogICAgICAgbHdkID0gMi41KSANCg0KDQpgYGANCg0KDQojIExpYnJlcmlhIGR5Z3JhcGhzDQoNCioqX0VzdGEgbGlicmVyaWEgY29udGllbmUgdmlzdWFsaXphY2lvbmVzIGludGVyYWN0aXZhczpfKioNCg0KDQpgYGB7ciwgZWNobz1GQUxTRSwgZmlnLmRpbSA9IGMoMTAsIDgpfQ0KDQoNCg0KZHlncmFwaChzdGFyYl95YWhvbywgDQogICAgICAgIG1haW4gPSc8Yj48c3BhbiBzdHlsZSA9ICJjb2xvcjpibGFjayI+UHJlY2lvIE1lbnN1YWwgQWNjaW9uZXMgU1RCSzwvc3Bhbj48L2I+JywNCiAgICAgICAgeWxhYiA9ICc8c3BhbiBzdHlsZSA9ICJjb2xvcjpibGFjayI+UHJlY2lvPC9zcGFuPicpICU+JQ0KICBkeUxlZ2VuZChzaG93ID0gImZvbGxvdyIpICU+JSAgDQogIGR5U2VyaWVzKGxhYmVsID0gIlByZWNpbyIpICU+JSANCiAgZHlSYW5nZVNlbGVjdG9yKCkgDQoNCg0KYGBgDQoNCg0KDQojIFJ1aWRvIEJsYW5jbw0KDQoqKl9FcyB1biB0aXBvIGVzcGVjaWFsIGRlIHNlcmllIHRlbXBvcmFsLCBkb25kZSBsb3MgZGF0b3Mgbm8gc2lndWVuIG5pbmfDum4gcGF0csOzbi5fKioNCg0KKipfUGFyYSBxdWUgdW5hIHNlcmllIHNlYSBydWlkbyBibGFuY28gdGllbmUgcXVlIHRlbmVyIHVuYSBtZWRpYSBjb25zdGFudGUsIHVuYSB2YXJpYW56YSBjb25zdGFudGUgeSBubyB0ZW5lciBhdXRvY29ycmVsYWNpb24gZW4gbmluZ8O6biBwZXJpb2RvLl8qKg0KDQoqKl9MYSBhdXRvY29ycmVsYWNpb24gbWlkZSBjdWFuIGNvcnJlbGFjaW9uYWRhIGVzIHVuYSBzZXJpZSBjb24gdmVyc2lvbmVzIGFudGVyaW9yZXMgZGUgc2kgbWlzbWEuXyoqDQoNCioqX0xhIGZhbHRhIGRlIGF1dG9jb3JyZWxhY2lvbiBlcyBxdWUgbm8gaGF5IHVuYSByZWxhY2nDs24gY2xhcmEgZW50cmUgdmFsb3JlcyBwYXNhZG9zIHkgdmFsb3JlcyBwcmVzZW50ZXNfKioNCg0KJCRwPWNvcnIoWF90LFhfdC0xKSAkJA0KDQoNCmBgYHtyLCBlY2hvPUZBTFNFfQ0KDQpXTiA8LSBmdW5jdGlvbihOLCBtdSwgdmFyaWFuY2UpIHsNCiAgeDwtcm5vcm0obj1OLCBtZWFuPW11LCANCiAgICAgICAgICAgICAgICAgIHNkPXNxcnQodmFyaWFuY2UpKQ0KIA0KICByZXR1cm4oeCkNCn0NCiANCnduPC1XTigxMDAwLDAsMC4wMDA0KQ0KIA0KcGxvdCh3biwgbWFpbj0iUnVpZG8gQmxhbmNvIiwgDQogICAgIHhsYWI9InQiLA0KICAgICB0eXA9J2wnLCBjb2w9ImJsdWUiKQ0KDQpgYGANCiMgQ2FtaW5hdGEgQWxlYXRvcmlhDQoNCioqX0VzIHVuIHRpcG8gZXNwZWNpYWwgZGUgc2VyaWUgZGUgdGllbXBvIGVuIGRvbmRlIGxvcyB2YWxvcmVzIHRpZW5kZW4gYSBwZXJzaXN0aXIgZW4gZWwgdGllbXBvIHkgbGFzIGRpZmVyZW5jaWFzIGVudHJlIHBlcmlvZG9zIHNvbiBzaW1wbGVtZW50ZSBydWlkbyBibGFuY28uXyoqDQoNCioqX0N1YWxxdWllciBjYW1pbmF0YSBhbGVhdG9yaWE6IFN1IHByZWNpbyBlcyBpZ3VhbCBhbCBwcmVjaW8gYW50ZXJpb3IgbWFzIHVuIHJ1aWRvIGJsYW5jb18qKg0KDQoqKl9MYXMgbWVqb3JlcyBlc3RpbWFjaW9uZXMgcGFyYSBsb3MgcHJlY2lvcyBkZSBob3kgc2Vyw6FuIGxvcyBwcmVjaW9zIGRlIGF5ZXJfKioNCg0KDQoNCg0KYGBge3IsIGVjaG89RkFMU0V9DQoNClJXIDwtIGZ1bmN0aW9uKE4sIHgwLCBtdSwgdmFyaWFuY2UpIHsNCiAgejwtY3Vtc3VtKHJub3JtKG49TiwgbWVhbj0wLCANCiAgICAgICAgICAgICAgICAgIHNkPXNxcnQodmFyaWFuY2UpKSkNCiAgdDwtMTpODQogIHg8LXgwK3QqbXUreg0KICByZXR1cm4oeCkNCiAgfQ0KIA0KUDE8LVJXKDEwMCwxMCwwLDAuMDAwNCkNClAyPC1SVygxMDAsMTAsMCwwLjAwMDQpDQpwbG90KFAxLCBtYWluPSJSYW5kb20gV2FsayIsIA0KICAgICB4bGFiPSJ0Iix5bGFiPSJQcmljZSIsIHlsaW09Yyg5LjcsMTAuMyksDQogICAgIHR5cD0nbCcsIGNvbD0icmVkIikNCiANCg0KDQpgYGANCg0KDQojIEVzdGFjaW9uYXJpZWRhZA0KDQoNCioqX1BydWViYSBEaWNrZXkgRnVsbGVyXyoqDQoNCg0KKipfTGEgcHJ1ZWJhIG5vcyBzaXJ2ZSBwYXJhIHRlc3RlYXIgc2kgaGF5IG8gbm8gYXV0b2NvcnJlbGFjaW9uIGVuIHVuYSBzZXJpZSB0ZW1wb3JhbC4gXyoqDQoNCioqX0VzdGEgcHJvcGllZGFkIGhhY2UgcmVmZXJlbmNpYSBhIGN1YW5kbyB1bmEgc2VyaWUgZXMgZXN0YWJsZSBhIGxvIGxhcmdvIGRlbCB0aWVtcG8sIGVzIGRlY2lyLCBjdWFuZG8gbGEgbWVkaWEgeSBsYSB2YXJpYW56YSBzb24gY29uc3RhbnRlcyB5IGFkZW1hcyBubyBwcmVzZW50YSB0ZW5kZW5jaWFzXyoqDQoNCioqX1VuIGVqZW1wbG8gZGUgdW5hIHNlcmllIGTDqWJpbG1lbnRlIGVzdGFjaW9uYXJpYSBlcyB1bmEgc2VyaWUgZGUgcnVpZG8gYmxhbmNvLCB5YSBxdWUgbGEgYXV0b2NvcnJlbGFjaW9uIGVudHJlIGRvcyBwdW50b3Mgc2llbXByZSBlcyBjZXJvXyoqDQoNCg0KKipfU2VyaWUgRXN0YWNpb25hcmlhXyoqDQoNCioqX1NpbXVsYW1vcyBkYXRvcyBub3JtYWxtZW50ZSBkaXN0cmlidWlkb3MgZGUgMTAwMCBvYnNlcnZhY2lvbmVzIHkgYWxlYXRvcmlvcywgZWwgZGlja2V5IGZ1bGxlciB0ZXN0IGxvIHZhbW9zIGEgZWplY3V0YXIgcGFyYSB2ZXIgc2kgaGF5IHNpZ25pZmljYW5jaWEgbyBuby4gRWwgcCB2YWxvciBlcyBtZW5vciBhIHVuIG5pdmVsIGRlIHNpZ25pZmljYW5jaWEgZGVsIDAuMDUsIHBvciBsbyB0YW50bywgbGEgaGlwb3Rlc2lzIG51bGEgZXMgcmVjaGF6YWRhLCBwb3IgbG8gdGFudG8gbGEgc2VyaWUgc2VyYSBlc3RhY2lvbmFyaWEuXyoqDQoNCmBgYHtyLCB3YXJuaW5nPUZBTFNFLCBlY2hvPUZBTFNFfQ0KDQp4IDwtIHJub3JtKDEwMDApIA0KYWRmLnRlc3QoeCkNCnBsb3QoeCkNCg0KDQpgYGANCioqX1NlcmllIE5vIEVzdGFjaW9uYXJpYV8qKg0KDQoNCg0KYGBge3IsIHdhcm5pbmc9RkFMU0UsIGVjaG89RkFMU0V9DQoNCnkgPC0gZGlmZmludih4KSANCmFkZi50ZXN0KHkpDQpwbG90KHkpDQoNCg0KDQoNCmBgYA0KDQoNCioqX0Rlc2NvbXBvbmVyIFNlcmllXyoqDQoNCg0KYGBge3IsIGVjaG89RkFMU0V9DQoNCmFkZi50ZXN0KG5vdHRlbSkNCmF1dG9wbG90KGRlY29tcG9zZShub3R0ZW0sIHR5cGUgPSAnYWRkaXRpdmUnKSkNCg0KDQoNCmBgYA0KDQoNCg0KIyBBdXRvY29ycmVsYWNpb24NCg0KDQoqKl9MYSBjb3JyZWxhY2nDs24gbm9zIGRpY2UgbGEgc2ltaWxpdHVkIGNvbiByZXNwZWN0byBhIGxhcyBzZXJpZXMgdGVtcG9yYWxlcyBjYW1iaWFuIHN1cyB2YWxvcmVzLiBTaSBlc3RhbW9zIGNvbiB1bmEgc29sYSBzZXJpZSB0ZW1wb3JhbCBsYSBjb3JyZWxhY2nDs24gc2UgY2FsY3VsYSBjb24gdmFsb3JlcyBwYXNhZG9zIGRlIGxhIG1pc21hIHNlcmllLCBlcyBkZWNpciwgbGEgY29ycmVsYWNpw7NuIGVudHJlIHVuYSBzZWN1ZW5jaWEgeSBzaSBtaXNtYS4gTWlkZSBlbCBuaXZlbCBkZSBzZW1lamFuemEgZW50cmUgdW5hIHNlY3VlbmNpYSBkZSB2YXJpb3MgcGVyaW9kb3MgYXRyw6FzIHkgbG9zIGRhdG9zIHJlYWxlcywgZXN0YSBzZWN1ZW5jaWEgZGVsIHBhc2FkbyBzZSBsbGFtYSByZXRyYXNvIG8gcmV6YWdvLl8qKg0KDQoNCioqX0xhIGZ1bmNpw7NuIGRlIGF1dG9jb3JyZWxhY2lvbiBBQ1AgRnVuY3Rpb24gbm9zIHZhIGEgcHJvcG9yY2lvbmFyIGxhIGF1dG9jb3JyZWxhY2lvbiBwYXJhIGN1YWxxdWllciByZXRyYXNvIHF1ZSBjb25zaWRlcmVtb3MuXyoqDQoNCg0KIyBBdXRvY29ycmVsYWNpb24gUGFyY2lhbA0KDQoNCg0KKipfU29uIGVmZWN0b3MgZGUgc2VndW5kYSBtYW5vLCBkb25kZSBsb3MgdmFsb3JlcyBwcmVzZW50ZXMgc29uIGFmZWN0YWRvcyBwb3IgdmFsb3JlcyBwYXNhZG9zIHkgZXN0b3MgbWlzbW9zIHRhbWJpw6luIHNvbiBhZmVjdGFkb3MgcG9yIHZhbG9yZXMgcGFzYWRvc18qKg0KDQoNCg0KKipfQXV0b2NvcnJlbGFjaW9uIHBhcmEgcnVpZG8gYmxhbmNvXyoqDQoNCmBgYHtyLCBlY2hvPUZBTFNFfQ0KDQoNCmFjZihub3R0ZW0sIGxhZy5tYXggPSAyMCwgcGxvdCA9IFQpDQoNCg0KDQpwYWNmKG5vdHRlbSwgbGFnLm1heCA9MjAsIHBsb3QgPSBUKQ0KDQoNCg0KYGBgDQoNCg0KKipfQXV0b2NvcnJlbGFjaW9uIHBhcmEgcnVpZG8gYmxhbmNvLihTZXJpZSBFc3RhY2lvbmFyaWEpXyoqDQoNCg0KYGBge3J9DQoNCg0KYWNmKHgsIHBsb3QgPSBUKQ0KDQpgYGANCg0KDQoNCg0KDQoNCg0KIyBFc3RhY2lvbmFsaWRhZA0KDQoqKl9TdWdpZXJlIHF1ZSBjaWVydGFzIHRlbmRlbmNpYXMgYXBhcmVjZXLDoW4gZGUgZm9ybWEgY8OtY2xpY2EsIHBvciBlamVtcGxvLCBsYXMgdGVtcGVyYXR1cmFzIHN1YmVuIHkgYmFqYW4gYSBsbyBsYXJnbyBkZWwgdGllbXBvLiBMYSBzZXJpZXMgc2UgcHVlZGVuIGRlc2NvbXBvbmVyIGJham8gZWZlY3RvIHRlbmRlbmNpYSBkb25kZSBwb2RlbW9zIHF1aXRhciB1biBwYXRyw7NuIHNvc3RlbmlkbyBlbiBsb3MgZGF0b3MsIGxhIGVzdGFjaW9uYWwgbG9zIGVmZWN0b3MgY8OtY2xpY29zIHkgZWwgcmVzaWR1YWwgZWwgZXJyb3IgZW50cmUgbG9zIGRhdG9zIHJlYWxlcyB5IGVsIG1vZGVsbyBxdWUgZXN0YW1vcyBlamVjdXRhbmRvLl8qKg0KDQoqKl9MYSBkZXNjb21wb3NpY2nDs24gY2zDoXNpY2EgcHVlZGUgc2VyIGFkaXRpdmEgbyBtdWx0aXBsaWNhdGl2YSwgZW4gbGEgYWRpdGl2YSBwb2RlbW9zIHN1cG9uZXIgcXVlIGVuIGN1YWxxdWllciBtb21lbnRvIGRlbCB0aWVtcG8gZWwgdmFsb3Igb2JzZXJ2YWRvIGVzIGxhIHN1bWEgZGUgbGEgdGVuZGVuY2lhLCBlbCBvYmpldG8gZXN0YWNpb25hbCB5IGVsIGVmZWN0byByZXNpZHVhbCBwYXJhIGVzZSBwZXJpb2RvLl8qKg0KDQoqKl9MYSBkZXNjb21wb3NpY2nDs24gbXVsdGlwbGljYXRpdmEgbm9zIGRpY2UgcXVlIGxhIHNlcmllIG9yaWdpbmFsIGVzIHVuIHByb2R1Y3RvIGRlIGxvcyB0cmVzIGVmZWN0b3MgYW50ZXJpb3JpZXMuXyoqDQoNCg0KYGBge3J9DQoNCg0KDQoNCmBgYA0KDQoNCg0KDQojIFN1YXZpemFkbyBjb24gU01BDQoNCioqX1V0aWxpemFyZW1vcyB1biBEYXRhIFNldCB5IGNyZWFyZW1vcyB1bmEgdmFyaWFibGUgZGUgU01BIGRlIG9yZGVuIDMgeSBkZSBvcmRlbiA5Ll8qKg0KDQoNCmBgYHtyLCBlY2hvPUZBTFNFfQ0KDQpsaWJyYXJ5KFRUUikNCg0KDQpseW54c21vb3RoZWQzID0gU01BKGx5bngsIG4gPSAzKQ0KDQpseW54c21vb3RoZWQ5ID0gU01BKGx5bngsIG4gPSA5KQ0KDQoNCnBsb3QobHlueCwgbWFpbiA9ICdvcmlnaW5hbCcpDQpwbG90KGx5bnhzbW9vdGhlZDMsIG1haW4gPSAnb3JkZW4gMycpDQpwbG90KGx5bnhzbW9vdGhlZDksIG1haW4gPSAnb3JkZW4gOScpDQoNCg0KYGBgDQoNCg0KKipfTcOpdG9kbyBFeHBvbmVuY2lhbF8qKg0KDQoNCmBgYHtyfQ0KDQoNCmV0c21vZGVsID0gZXRzKG5vdHRlbSkNCnBsb3Qobm90dGVtLCBsd2QgPSAzKQ0KbGluZXMoZXRzbW9kZWwkZml0dGVkLCBjb2wgPSAicmVkIikNCg0KIyBGb3JlY2FzdCBkZSAxMiBtZXNlcw0KIyBJbnRlcnZhbG8gZGUgcHJlZGljY2nDs246IG5pdmVsIGRlIGNvbmZpYW56YSA5NSUNCnBsb3QoZm9yZWNhc3QoZXRzbW9kZWwsIGggPSAxMiwgbGV2ZWwgPSA5NSkpDQoNCiMgSG9sdCBXaW50ZXJzIG11bHRpcGxpY2F0aXZvDQpldHNtb2RtdWx0ID0gZXRzKG5vdHRlbSwgbW9kZWwgPSJNTU0iKSAjIE1NTSB0b2RvIG11bHRpcGxpY2F0aXZvICNlcnJvciwgdGVuZGVuY2lhLCBlc3RhY2lvbmFsaWRhZC4gRGVmZWN0byBaWloNCg0KIyBDb21wYXJhY2lvbiBzZXJpZSBvcmlnaW5hbCBjb24gZWwgZml0dGVkIGx1ZWdvIGRlIGFwbGljYXIgZWwgbW9kZWxvIG11bHRpcGxpY2F0aXZvDQpwbG90KG5vdHRlbSwgbHdkID0gMykNCmxpbmVzKGV0c21vZG11bHQkZml0dGVkLCBjb2wgPSAicmVkIikNCg0KDQpldHNtb2RtdWx0TUFNID0gZXRzKG5vdHRlbSwgbW9kZWwgPSJNQU0iKSANCnBsb3Qobm90dGVtLCBsd2QgPSAzKQ0KbGluZXMoZXRzbW9kbXVsdE1BTSRmaXR0ZWQsIGNvbCA9ICJyZWQiKQ0KDQoNCg0KY29tcGFyZV9tb2RlbHMgPSBhcy5kYXRhLmZyYW1lKG5vdHRlbSkNCg0KY29tcGFyZV9tb2RlbHMkZml0dGVkX01NTSA9IGV0c21vZG11bHQkZml0dGVkDQoNCmNvbXBhcmVfbW9kZWxzJGZpdHRlZF9NQU0gPSBldHNtb2RtdWx0TUFNJGZpdHRlZA0KDQojIEVycm9yIGN1YWRyw6F0aWNvIG1lZGlvDQoNCmNhdCgnRWwgZXJyb3IgY3VhZHJhdGljbyBtZWRpbyBwYXJhIHRlbmRlbmNpYSBtdWx0aXBsaWNhdGl2YSBlczogJywgc3VtKChjb21wYXJlX21vZGVscyR4IC0gY29tcGFyZV9tb2RlbHMkZml0dGVkX01NTSkgLyBucm93KGNvbXBhcmVfbW9kZWxzKSksICd5IHBhcmEgdGVuZGVuY2lhIGFkaXRpdmE6ICcsIHN1bSgoY29tcGFyZV9tb2RlbHMkeCAtIGNvbXBhcmVfbW9kZWxzJGZpdHRlZF9NQU0pIC8gbnJvdyhjb21wYXJlX21vZGVscykpKQ0KDQoNCmBgYA0KDQojIE1vZGVsb3MgUHJlZGljdGl2b3MNCg0KDQohW10oZnV0dXJlLmpwZyl7IHdpZHRoPTUwJSB9DQoNCg0KDQoNCioqX8K/Q29tbyBzZWxlY2Npb25hciBlbCBtZWpvciBtb2RlbG8/XyoqDQoNCg0KPHVsPg0KDQpfMSkgRXNjb2dlciBlbCBtb2RlbG9fDQoNCl8yKSBEaXZpZGlyIGxvcyBkYXRvcyBkZSBlbnRyZW5hbWllbnRvIHkgZGUgcHJ1ZWJhXw0KDQpfMykgQWp1c3RhciBlbCBtb2RlbG8gYSBsb3MgZGF0b3MgZGUgZW50cmVuYW1pZW50b18NCg0KXzQpIEV2YWx1YXIgZWwgbW9kZWxvIGVuIGxvcyBkYXRvcyBkZSBwcnVlYmFfDQoNCl81KSBSZS1BanVzdGFyIGVsIG1vZGVsbyBjb24gdG9kb3MgbG9zIGRhdG9zXw0KDQpfNikgUHJlZGVjaXIgbG9zIGRhdG9zIGZ1dHVyb3NfDQoNCg0KPC91bD4NCg0KDQoqKl9Dcml0ZXJpb3MgZGUgc2VsZWNjacOzbiBkZWwgbW9kZWxvOl8qKg0KDQoNCjx1bD4NCg0KKipfQUlDID0gQUthaWtlOiBFdmFsw7phIGxhIGNvbGVjY2nDs24gZGUgbW9kZWxvcyBxdWUgdGVuZW1vcyB5IHZhIGEgZXN0aW1hciBsYSBjYWxpZGFkIGRlIGNhZGEgbW9kZWxvIGVuIHJlbGFjacOzbiBjb24gbG9zIG1vZGVsb3MgcmVzdGFudGVzLCBlbCBjcml0ZXJpbyB2YSBhIHBlbmFsaXphciBlbiBiYXNlIGFsIG51bWVybyBkZSBwYXLDoW1ldHJvcyBxdWUgdGllbmUsIGVzIGRlY2lyLCBlbiBiYXNlIGEgc3UgY29tcGxlamlkYWQuXyoqDQoNCg0KKipfQklDID0gQmF5c2VzaWFuOiBUaWVuZSBjcml0ZXJpb3MgQmF5ZXNpYW5vcy4gVXRpbGl6YXIgbG9zIGRvcyBjcml0ZXJpb3MgZXMgbG8gcmVjb21lbmRhZG8uXyoqDQoNCg0KPC91bD4NCg0KDQoqKl9Db25jZXB0b3MgQ2xhdmVzOl8qKg0KDQoNCl9PdmVyZml0dGluZzogU29icmVhanVzdGUgZGVsIG1vZGVsby4gUHVlZGUgbm8gZnVuY2lvbmFyIGRlIGZvcm1hIGNvcnJlY3RhIHlhIHF1ZSBlc3RhIHNvYnJlIGFqdXN0YWRvIGVsIG1vZGVsb18NCg0KX0xvcyByZXNpZHVvcyBkZWwgbW9kZWxvIGRlYmVuIHBhcmVjZXJzZSBhIHJ1aWRvIGJsYW5jbywgZXMgZGVjaXIsIGVzdGFjaW9uYXJpb3NfDQoNCg0KDQojIE1vZGVsbyBBdXRvcnJlZ3Jlc2l2bw0KDQoNCioqX0xhIGF1dG9jb3JyZWxhY2lvbiBub3MgdmEgYSBwZXJtaXRpciBkZXNhcnJvbGxhciBlc3RlIG1vZGVsbyBxdWUgc2UgYmFzYSBlbiB0ZW5lciBlbiBjdWVudGEgdmFsb3JlcyBwYXNhZG9zLiBFcyB1biBtb2RlbG8gbGluZWFsIGRvbmRlIGxvcyB2YWxvcmVzIGRlbCBwZXJpb2RvIGFjdHVhbCBzb24gbGEgc3VtYSBkZSByZXN1bHRhZG9zIHBhc2Fkb3MgbXVsdGlwbGljYWRvcyBwb3IgdW4gY29lZmljaWVudGUgbWFzIHVuIGVycm9yLl8qKg0KDQoqKl9FbCBjb2VmaWNpZW50ZSBzaWVtcHJlIHRpZW5lIHF1ZSBlc3RhciBlbnRyZSAtIDEgeSAxLiBObyBwdWVkZSBzZXIgc3VwZXJpb3IgZW4gdmFsb3IgYWJzb2x1dG9fKioNCg0KKipfRWwgcmVzaWR1byB2YW4gYSBzZXIgZGlmZXJlbmNpYXMgaW1wcmVkZWNpYmxlcywgc2kgaGF5IHVuIHBhdHLDs24gc2UgdmEgYSBpZGVudGlmaWNhciBlbiBsYXMgdmFyaWFibGVzIGRlbCBtb2RlbG8sIGVsIHJlc2lkdW8gZXMgbGEgZGlmZXJlbmNpYSBlbnRyZSBlbCB2YWxvciByZWFsIHkgZWwgZXN0aW1hZG9fKioNCg0KDQpfTW9kZWxvIEFSKDEpXw0KDQokJA0KXGJlZ2lue2FsaWduZWR9DQogWV9pICY9IGMgICsgXHBoaV9pIFlfe2ktMX0gKyBcZXBzaWxvbl9pIA0KDQpcZW5ke2FsaWduZWR9DQokJA0KDQpfTW9kZWxvIEFSKDIpXw0KDQokJA0KXGJlZ2lue2FsaWduZWR9DQogWV9pICY9IGMgICsgXHBoaV9pIFlfe2ktMX0gKyAgXHBoaV9pIFlfe2ktMn0gICsgXGVwc2lsb25faSANCg0KXGVuZHthbGlnbmVkfQ0KJCQNCl9Nb2RlbG8gQVIobilfDQoNCg0KJCQNClxiZWdpbnthbGlnbmVkfQ0KIFlfaSAmPSBjICArIFxwaGlfaSBZX3tpLTF9ICsgIFxwaGlfMiBZX3tpLTJ9ICArIC4uXHBoaV9uIFlfe2ktbn0gKyBcZXBzaWxvbl9pIA0KDQpcZW5ke2FsaWduZWR9DQokJA0KX0VzdG9zIG1vZGVsb3MgZGUgbXVjaG9zIHByb2Nlc29zIEFSIHB1ZWRlbiBjb250ZW5lciBydWlkby4gRXMgaW1wb3J0YW50ZSBwcmltZXJvIHZlcmlmaWNhciBkZSBmb3JtYSB0ZcOzcmljYSBwYXJhIG5vIGhhY2VybG8gbWFzIGNvbXBsZWpvLl8NCg0KDQpfTGFzIGZ1bmNpb25lcyBkZSBhdXRvY29ycmVsYWNpb24geSBhdXRvY29ycmVsYWNpb24gcGFyY2lhbCBub3MgdmFuIGEgZGVjaXIgY3VhbnRvcyByZXphZ29zIHBvZGVtb3MgaW5jbHVpciBlbiBlbCBtb2RlbG8uXw0KDQoNCg0KDQojIEFSTUENCg0KDQpfRXN0b3MgbW9kZWxvcyBjb21iaW5hbiBsb3MgYW50ZXJpb3JlcyB2aXN0b3MuIFRvbWEgZW4gY3VlbnRhIGxvcyB2YWxvcmVzIHBhc2Fkb3MgeSBsb3MgZXJyb3Jlcy5fDQoNCg0KDQokJA0KXGJlZ2lue2FsaWduZWR9DQogWV9pICY9IGMgICsgXHBoaV9pIFlfe2ktMX0gKyBcYWxwaGFfaSBcZXBzaWxvbl97aS0xfSAgKyBcZXBzaWxvbl9pIA0KDQpcZW5ke2FsaWduZWR9DQokJA0KX0RvbmRlIGVsIGNvZWZpY2llbnRlIEFSIGVzOl8NCg0KDQokJA0KXGJlZ2lue2FsaWduZWR9DQpccGhpX2kNCg0KXGVuZHthbGlnbmVkfQ0KJCQNCl9ZIGVsIGNvZWZpY2llbnRlIE1BIGVzOl8NCg0KDQoNCiQkDQpcYmVnaW57YWxpZ25lZH0NClxhbHBoYV9pDQpcZW5ke2FsaWduZWR9DQokJA0KDQpgYGB7cn0NCg0KDQoNCmBgYA0KDQoNCiMgQVJJTUENCg0KDQpfQ3VhbmRvIHNlIHRyYXRhIGRlIHNlcmllcyBubyBlc3RhY2lvbmFyaWFzIHV0aWxpemFyZW1vcyBlbCBtb2RlbG8gYXV0b3JyZWdyZXNpdm8gaW50ZWdyYWRvIGRlIG1lZGlhcyBtb3ZpbGVzLiBMYSBwYXJ0ZSBpbnRlZ3JhZGEgZGVsIG1vZGVsbyBleHBsaWNhIGVsIG51bWVybyBkZSBkaWZlcmVuY2lhcyBubyBlc3RhY2lvbmFsZXMgcXVlIGRlYmVtb3MgZXhhbWluYXIgcGFyYSBlc3RhYmxlY2VyIGVzdGFjaW9uYXJpZWRhZC5fDQoNCl9BUklNQSAocCxkLHEpXw0KDQpfRWwgbW9kZWxvIHZhIGEgdGVuZXIgdHJlcyBvcmRlbmVzLCBwLGQgeSBxLiBEb25kZSBwID0gQ29tcG9uZW50ZSBBUiwgZCA9IE9yZGVuIGRlIEludGVncmFjacOzbiB5IDEgPSBDb21wb25lbnRlIE1BLl8NCg0KX0RvbmRlIHVuIEFSSU1BKHAsMCxxKSA9IEFSTUEocCxxKS5fDQoNCg0KDQoNCg0KDQpfTnVtZXJvIGRlIGxpbmNlcyBjYXB0dXJhZG9zIGFudWFsbWVudGUgZW4gRUVVVV8gDQoNCg0KYGBge3IsIGVjaG89RkFMU0V9DQoNCnBsb3QobHlueCkNCg0KYGBgDQoNCg0KX0VuIGVzdGUgZ3LDoWZpY28gb2J0ZW5lbW9zIGxhcyBmdW5jaW9uZXMgZGUgYXV0b2NvcnJlbGFjaW9uIHkgZWwgbWlzbW8gZ3LDoWZpY28gYW50ZXJpb3IuXw0KDQpfT2JzZXJ2YW1vcyBjaWVydG8gY29tcG9ydGFtaWVudG8gY8OtY2xpY28sIG5vIHBhcmVjZSBoYWJlciBlc3RhY2lvbmFsaWRhZC5fDQoNCl9FbiBsYSBGQUNQIHBvZGVtb3MgdmVyIHF1ZSBlbCBwcmltZXIgeSBzZWd1bmRvIHJldHJhc28gc29uIHNpZ25pZmljYXRpdm9zLl8NCg0KDQoNCg0KX1ZlbW9zIGluZm9ybWFjaW9uIGRlbCBkYXRhIHNldDpfDQoNCmBgYHtyfQ0KDQp0YWlsKGx5bngpDQoNCmBgYA0KDQpgYGB7ciwgZWNobz1GQUxTRX0NCg0KdHNkaXNwbGF5KGx5bngpDQoNCg0KYGBgDQoNCl9Qcm9iYW1vcyB1biBtb2RlbG8gQVIoMilfDQoNCmBgYHtyfQ0KDQptb2RlbGFyaW1hID0gYXJpbWEobHlueCwgb3JkZXIgPSBjKDIsMCwwKSkNCg0KbW9kZWxhcmltYQ0KDQoNCmBgYA0KDQpfT2JzZXJ2YW1vcyBsb3MgcmVzaWR1b3MgZGVsIG1vZGVsbzpfDQoNCg0KYGBge3IsIGVjaG89RkFMU0V9DQoNCg0KcGxvdChyZXNpZHVhbHMobW9kZWxhcmltYSkpDQoNCg0KYGBgDQoNCl9FamVjdXRhbW9zIHVuIE1BKDIpOl8NCg0KDQpgYGB7ciwgZWNobz1GQUxTRX0NCg0KDQptb2RlbGFyaW1hID0gYXJpbWEobHlueCwgb3JkZXIgPSBjKDAsMCwyKSkNCg0KbW9kZWxhcmltYQ0KDQpwbG90KHJlc2lkdWFscyhtb2RlbGFyaW1hKSkNCg0KYGBgDQoNCg0KX0hhY2Vtb3MgdW4gdGVzdCBkZSBwIHZhbG9yIHBhcmEgdmVyIHNpIGxhIHNlcmllIGVzIGVzdGFjaW9uYXJpYSBhbCA1ICUgZGUgbml2ZWwgZGUgc2lnbmlmaWNhbmNpYTpfDQoNCmBgYHtyLCB3YXJuaW5nPUZBTFNFLCBlY2hvPUZBTFNFfQ0KDQp0ZXN0YWRmID0gYWRmLnRlc3QobHlueCkNCg0KaWYgKCB0ZXN0YWRmJHAudmFsdWUgPD0gMC4wNSApIHsNCiAgDQogIGNhdCgnTGEgc2VyaWUgZXMgZXN0YWNpb25hcmlhIGNvbiB1biBwIHZhbG9yIGRlOiAnLHRlc3RhZGYkcC52YWx1ZSAgKQ0KICANCn0gZWxzZSB7DQogIA0KICBjYXQoJ0xhIHNlcmllIG5vIGVzIGVzdGFjaW9uYXJpYSBjb24gdW4gcCB2YWxvciBkZTogJyx0ZXN0YWRmJHAudmFsdWUgKQ0KICANCn0NCg0KDQoNCmBgYA0KDQoNCl9Qcm9iYW1vcyB1biBtb2RlbG8gQVIoNCkgeWEgcXVlIGVuIGxhIEZBQ1Agc2UgcHVlZGUgdmVyIHVuIHJlemFnbyA0IHNpZ25pZmljYXRpdm86Xw0KDQpfUG9kZW1vcyB2ZXIgcmVzaWR1b3MgZGVudHJvIGRlIGxhIHpvbmEgZGUgbm8gc2lnbmlmaWNhY2nDs24sIHF1ZSBzaWd1ZW4gYXByb3hpbWFkYW1lbnRlIHVuYSBkaXN0cmlidWNpw7NuIG5vcm1hbCB5IGxhIHNlcmllIHRlbXBvcmFsIGRlIGxvcyByZXNpZHVvcyB2ZW1vcyBxdWUgZXMgZXN0YWNpb25hcmlhIHBhcmEgZWwgbW9kZWxvIGRlIG9yZGVuIDQuXw0KDQoNCmBgYHtyLCBlY2hvPUZBTFNFfQ0KDQptb2RlbGFyaW1hYXI0ID0gQXJpbWEobHlueCwgb3JkZXIgPSBjKDQsMCwwKSkNCg0KbW9kZWxhcmltYWFyNA0KDQpjaGVja3Jlc2lkdWFscyhtb2RlbGFyaW1hYXI0KQ0KDQpgYGANCg0KX0hhY2llbmRvIFByb27Ds3N0aWNvczpfDQoNCl9FbCBhcmVhIG9zY3VyYSBlcyBlbCBpbnRlcnZhbG8gZGUgY29uZmlhbnphXw0KDQpgYGB7ciwgZWNobz1GQUxTRX0NCg0KYXJpbWFmb3JlY2FzdCA9IGZvcmVjYXN0KG1vZGVsYXJpbWFhcjQsIGggPSAxNSkgIyAxMCBhw7FvcyBkZSBlc3RpbWFjaW9uDQoNCnBsb3QoYXJpbWFmb3JlY2FzdCwgbWFpbiA9ICJGb3JlY2FzdCIsIGNleCA9IDIsIGNvbCA9ICJibHVlIikNCg0KDQpgYGANCg0KX1ZhbG9yZXMgZXN0aW1hZG9zOl8NCg0KDQpgYGB7cn0NCg0KYXJpbWFmb3JlY2FzdCRtZWFuDQoNCg0KYGBgDQoNCiMgVm9sYXRpbGlkYWQNCg0KDQpfSGF5IG1vZGVsb3MgZXNwZWNpYWxlcyBxdWUgbWlkZW4gbGEgdm9sYXRpbGlkYWQsIGVzIGRlY2lyLCBsYSBtYWduaXR1ZCBkZSBsb3MgcmVzaWR1b3MsIGN1YW4gdm9sw6F0aWxlcyBzZWFuIGxhcyBwcmVkaWNjaW9uZXNfDQoNCl9Mb3MgbW9kZWxvcyBBUkNIIChBdXRvcnJlZ3Jlc2l2b3MuIGNvbiBoZXRlcm9jZWRhc3RpY2lkYWQgY29uZGljaW9uYWwpIHNvbiBsb3MgbWFzIGNvbXVuZXMgcGFyYSBlc3R1ZGlhciBzZXJpZXMgdGVtcG9yYWxlcyBjb24gdm9sYXRpbGlkYWQuIEEgZGlmZXJlbmNpYSBkZSBsYSBmYW1pbGlhIEFSSU1BIGVzdGUgbW9kZWxvIHRpZW5lIHZhcmlhcyBlY3VhY2lvbmVzLCB1bmEgcGFyYSBsYSBtZWRpYSB5IG90cmEgcGFyYSBsYSB2YXJpYW56YS5fDQoNCg0KX0xhIGNvbWJpbmFyb24gZW50cmUgbGEgaGV0ZXJvY2VkYXN0aWNpZGFkIHkgbGEgY29uZGljaW9uYWwgbm9zIGRpY2UgcXVlIGxhIHZhcmlhbnphIGRlcGVuZGVyw6EgZGUgb3Ryb3MgdmFsb3JlcywgZXMgZGVjaXIsIGRlIHZhbG9yZXMgYXV0b3JyZWdyZXNpdm9zLiBVc2FyZW1vcyB2YWxvcmVzIHBhc2Fkb3MgcGFyYSBtZWRpciBsYSB2YXJpYW56YSBkZWwgcGVyaW9kbyBhY3R1YWwuXw0KDQoNCg0KIyBQcmVkaWNjaW9uZXMNCg0KDQpgYGB7ciwgZWNobz1GQUxTRX0NCm15ZGF0YSA9IHJlYWRfZXhjZWwoJ2dlcm1hbmluZmwueGxzeCcpDQoNCm15ZGF0YSRpbmZsYWNpb24gPSBhcy5udW1lcmljKG15ZGF0YSRpbmZsYWNpb24pDQoNCm15ZGF0YSA9IG15ZGF0YSRpbmZsYWNpb24NCg0KDQojIEFsIHNhYmVyIGVsIGHDsW8geSBsYSBmcmVjdWVuY2lhIHBvZGVtb3MgZWRpdGFybG8NCg0KIyBQb2RlbW9zIHZlciB1biBjb21wb25lbnRlIGVzdGFjaW9uYWw6DQpnZXJtYW5pbmZsID0gdHMobXlkYXRhLCBzdGFydCA9IDIwMDgsIGZyZXF1ZW5jeSA9IDEyKQ0KDQojIFlhIGVzIHVuIG9iamV0byB0aW1lIHNlcmllcw0KDQpwbG90KGdlcm1hbmluZmwpDQoNCiMgZGVzY29tcG9zaWNpb24gDQoNCg0KcGxvdChzdGwoZ2VybWFuaW5mbCwgcy53aW5kb3cgPSA3KSkNCg0KDQoNCiMgQVJJTUEgU0VBU09OQUw6DQoNCiMgTWUgdmEgYSBidXNjYXIgZWwgbWVqb3IgbW9kZWxvLCBsZSBwYXNhbW9zIGxhIHNlcmllIGNvbiBsb3MgcGFyYW1ldHJvcy4gc3RlcHdpc2UgZXMgcGFyYSBxdWUgbGEgc2VsZWNjaW9uIGRlbCBtb2RlbG8gc2VhIG1hcyByYXBpZGEsIGVuIHZleiBkZSBidXNjYXIgdG9kb3MgbG9zIG1vZGVsb3MgeSBjb21wYXJhcmxvcyBoYWNlIHVuYSBzZWxlY2Npb24gZGVsIG1vZGVsbyBwYXNvIGEgcGFzby4gYXBwcm94aW1hdGlvbiBlcyBwYXJhIHF1ZSBjb24gc2VyaWVzIHRlbW9wcmFsZXMgbXV5IGxhcmdhcyBvIHVuIHBhcmFtZXRybyBlc3RhY2lvbmFsIG11eSBsYXJnbyBubyBzZSBoYWdhIGxlbmdvLiB0cmFjZSBlcyBwYXJhIHF1ZSBub3MgbXVlc3RyZSB0b2RvcyBsb3MgbW9kZWxvcyBxdWUgZW5jb250cm8gZW4gbGEgaXRlcmFjaW9uDQoNCg0KZ2VybWFuaW5mbGFyaW1hID0gYXV0by5hcmltYShnZXJtYW5pbmZsLCANCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgc3RlcHdpc2UgPSBULCANCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgYXBwcm94aW1hdGlvbiA9IEYsIA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICB0cmFjZSA9IFQpDQoNCiMgc2kgY29uc2lkZXJhbW9zIGVsIHBlcmlvZG8gZXN0YWNpb25hbCBpZ3VhbCBhIDEyIHNlcmlhIHVuIG1vZGVsbyBTQVJJTUENCg0KDQoNCg0KDQoNCg0KYGBgDQpfRXN0aW1hcmVtb3MgMyBhw7FvczpfDQoNCmBgYHtyfQ0KDQpsaWJyYXJ5KHRzZXJpZXMpDQpsaWJyYXJ5KGhpZ2hjaGFydGVyKQ0KDQpoY2hhcnQoZm9yZWNhc3QoZ2VybWFuaW5mbGFyaW1hLCBoID0gMTIgKiAzKSkgJT4lIA0KICBoY19hZGRfdGhlbWUoaGNfdGhlbWVfc2FuZHNpZ25pa2EoKSkgJT4lIA0KICBoY190aXRsZSgNCiAgICB0ZXh0ID0gIk1vZGVsbyBBdXRvIEFyaW1hIEluZmxhY2lvbiBBbGVtYW5pYSBQcm95ZWN0YWRhIGRlc2RlIDIwMTgiDQogICkgJT4lDQogIGhjX3N1YnRpdGxlKHRleHQgPSAiRm9yZWNhc3QiKSAlPiUNCiAgaGNfY3JlZGl0cygNCiAgICBlbmFibGVkID0gVFJVRSwgdGV4dCA9ICJNR2Fsb3RvIiwNCiAgICBzdHlsZSA9IGxpc3QoZm9udFNpemUgPSAiMTVweCIpDQogICkNCg0KDQpgYGANCg0KDQpfUHJlZGljY2lvbiBkZSBMaW5jZXM6Xw0KDQoNCg0KDQpgYGB7ciwgZWNobz1GQUxTRSwgd2FybmluZz1GQUxTRX0NCmhjaGFydChseW54KQ0KDQoNCg0KdGVzdGFkZiA9IGFkZi50ZXN0KGx5bngpDQoNCg0KDQppZiAoIHRlc3RhZGYkcC52YWx1ZSA8PSAwLjA1ICkgew0KICANCiAgY2F0KCdMYSBzZXJpZSBlcyBlc3RhY2lvbmFyaWEgY29uIHVuIHAgdmFsb3IgZGU6ICcsdGVzdGFkZiRwLnZhbHVlICApDQogIA0KfSBlbHNlIHsNCiAgDQogIGNhdCgnTGEgc2VyaWUgbm8gZXMgZXN0YWNpb25hcmlhIGNvbiB1biBwIHZhbG9yIGRlOiAnLHRlc3RhZGYkcC52YWx1ZSApDQogIA0KfQ0KDQpgYGANCg0KDQpfRGVzY29tcG9zaWNpw7NuIGRlIGxhIHNlcmllOl8NCg0KX1BvZGVtb3Mgb2JzZXJ2YXIsIGVuIGxhIHNlcmllIGVzdGFjaW9uYXJpYSwgZW4gbGEgZnVuY2nDs24gZGUgYXV0b2NvcnJlbGFjaW9uIHBhcmNpYWwgdGVuZW1vcyBlbCBwcmltZXIgeSBzZWd1bmRvIHJlemFnbyBtdXkgc2lnbmlmaWNhdGl2b3MsIGx1ZWdvIGVsIGN1YXJ0byB0YW1iacOpbi4uXw0KDQoNCmBgYHtyLCBlY2hvPUZBTFNFfQ0KDQp0c2Rpc3BsYXkobHlueCkNCg0KYGBgDQoNCg0KX01vZGVsbyBBdXRvIEFSSU1BOl8NCg0KDQpgYGB7cn0NCg0KbXlhcmltYSA9IGF1dG8uYXJpbWEobHlueCwgdHJhY2UgPSBULCANCiAgICAgICAgICAgc3RlcHdpc2UgPSBGLCANCiAgICAgICAgICAgYXBwcm94aW1hdGlvbiA9IFQpDQoNCg0KYGBgDQoNCg0KYGBge3IsIGVjaG89RkFMU0V9DQoNCg0KaGNoYXJ0KGZvcmVjYXN0KG15YXJpbWEsIGggPSAyMCkpDQoNCg0KDQpgYGANCg0KX01vZGVsbyBBdXRvIEFSSU1BIGNvbiBBUig4KTpfDQoNCg0KDQpgYGB7cn0NCg0KbXlhcmltYSA9IGF1dG8uYXJpbWEobHlueCwgdHJhY2UgPSBULCANCiAgICAgICAgICAgc3RlcHdpc2UgPSBGLCANCiAgICAgICAgICAgYXBwcm94aW1hdGlvbiA9IFQsDQogICAgICAgICAgIG1heC5vcmRlcj04LCBtYXgucD04KQ0KDQoNCg0KYGBgDQoNCg0KDQpgYGB7ciwgZWNobz1GQUxTRX0NCg0KDQpoY2hhcnQoZm9yZWNhc3QobXlhcmltYSwgaCA9IDEwKSkNCg0KDQpgYGANCg0KDQoNCg0KDQojIFByZWRpY2Npb24gQXJnDQoNCl9QcmVkaWNjaW9uIHNhbGRvcyBjYWphIGRlIGFob3JybyBBUkcuXw0KDQoNCmBgYHtyLCBlY2hvPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQ0KDQpkZXBjYSA9IHJlYWRfZXhjZWwoJ2RlcF9jYS54bHMnKQ0KDQpkZXBfY2EgPSBkZXBjYSRkZXBfY2ENCg0KDQoNCg0KZGVwb3NpdG9zX2NhID0gdHMoZGVwX2NhLCAgZnJlcXVlbmN5ID0gNTIpDQoNCg0KDQpwbG90KGRlcG9zaXRvc19jYSkNCg0KDQoNCg0KZGVwb3NpdG9zX2NhX2FyaW1hID0gYXV0by5hcmltYShkZXBvc2l0b3NfY2EsIA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICBzdGVwd2lzZSA9IFQsIA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICBhcHByb3hpbWF0aW9uID0gRiwgDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgIHRyYWNlID0gVCkNCg0KDQoNCg0KcGxvdChmb3JlY2FzdChkZXBvc2l0b3NfY2FfYXJpbWEsIGggPSAzMCkpDQoNCg0KYGBgDQoNCg0KDQoNCiMgRmlsdHJvIGRlIEhhbXBlbA0KDQpQYXJhIGNhZGEgb2JzZXJ2YWNpw7NuIGRlIGxhIHZhcmlhYmxlIG8gc2VyaWUgZGUgdGllbXBvIFgsIHNlIGNhbGN1bGEgbGEgbWVkaWFuYSBkZSB1bmEgdmVudGFuYSAodW4gc3ViY29uanVudG8gZGUgdmFsb3JlcykuIEVqZW1wbG86IFNpIG51ZXN0cm8gY29uanVudG8gZGUgZGF0b3MgZXMgdW5hIHNlcmllIHRlbXBvcmFsIGRlIDEwMCBvYnNlcnZhY2lvbmVzIGRpYXJpYXMgeSBub3MgZmlqYW1vcyBlbiBsYSBvYnNlcnZhY2nDs24gMTAsIHZhbW9zIGEgbWlyYXIgdW4gc3ViY29uanVudG8gZGUgb2JzZXJ2YWNpb25lcyBxdWUgc2UgbGxhbWFyYW4gdmVudGFuYSwgdGVuZW1vcyBvYnNlcnZhY2lvbmVzIGFscmVkZWRvciBkZSBkb25kZSBlc3RhbW9zIHBhcmFkb3MsIHZhbW9zIGEgdGVuZXIgdmVjaW5vcywgZGUgY2FkYSBsYWRvIGRlIGxhIG9ic2VydmFjacOzbiB0b21hZGEuIDkgZGF0b3MgYW50ZXJpb3JlcyB5IDkwIHBvc3RlcmlvcmVzLiBQYXJhIGNhZGEgdmVudGFuYSBzZSBjYWxjdWxhIGxhIG1lZGlhbmEgKG1lZGlkYSByZXByZXNlbnRhdGl2YSB5IHJvYnVzdGEpLCBlcyBkZWNpciwgc2kgZW4gZXNhIHZlbnRhbmEgc2UgZW5jdWVudHJhbiBsb3MgYXTDrXBpY29zLCBsYSBtZWRpYW5hIHZhIGEgc2VyIHVuYSBtZWRpZGEgcXVlIHJlcHJlc2VudGUgZWwgY29uanVudG8gc2luIHF1ZSBlc3RlIGFmZWN0YWRhIHBvciBsb3MgYXTDrXBpY29zLiBMYSBtZWRpYSBubyBlcyByb2J1c3RhIHBvcnF1ZSBzZSB2ZSBhZmVjdGFkYSBwb3IgdmFsb3JlcyBhdMOtcGljb3MuDQoNCkx1ZWdvIHNlIGNhbGN1bGFyYSBwYXJhIGNhZGEgZ3J1cG8gZGUgb2JzZXJ2YWNpb25lcyBsYSBkaXN0YW5jaWEgZW50cmUgY2FkYSB2YWxvciBpbmRpdmlkdWFsIGNvbiBsYSBtZWRpYW5hIGRlbCBncnVwbywgZXN0byBub3MgZGFyw6EgdW5hIGRlc3ZpYWNpw7NuIHJlc3BlY3RvIGEgbGEgbWVkaWFuYSB5IHBhcmEgdGVuZXIgdW5hIG1lZGlkYSByZXByZXNlbnRhdGl2YSBkZSB0b2RvIGVsIGdydXBvIHNlIHNhY2FyYSBsYSBtZWRpYW5hIGRlIHRvZGFzIGxhcyBkZXN2aWFjaW9uZXMgYWJzb2x1dGFzLiBDYW1iaWVuIGNvbm9jaWRhIGNvbW8gbWVkaWFuYSBkZSBsYSBkZXN2aWFjacOzbiBhYnNvbHV0YSBjb24gcmVzcGVjdG8gYSBsYSBtZWRpYW5hIiwgcXVlIHRhbWJpw6luIHNlIGNvbm9jZSBjb21vIFtNQUQgKG1lZGlhbiBhYnNvbHV0ZSBkZXZpYXRpb24pXShodHRwczovL2VuLndpa2lwZWRpYS5vcmcvd2lraS9NZWRpYW5fYWJzb2x1dGVfZGV2aWF0aW9uKS4NCg0KDQpMYSBpZGVhIGVzIHF1ZSBzaSB1bmEgbXVlc3RyYSBkaWZpZXJlIGRlIGxhIG1lZGlhbmEgZW4gbcOhcyBkZSBrIGRlc3ZpYWNpb25lcyBlc3TDoW5kYXIsIHNlIGNvbnNpZGVyYSB1biBkYXRvIGF0w61waWNvIHkgc2UgcmVlbXBsYXphIHBvciBlbCB2YWxvciBkZSBsYSBtZWRpYW5hLg0KDQoNClVzdWFsbWVudGUgc2Ugc3VlbGUgc2VsZWNjaW9uYXIgaz0zLCBsbyBxdWUgc2UgY29ub2NlIGNvbW8gbGEgcmVnbGEgZGUgbGFzICIzIHNpZ21hcyIuIFBlcm8gZXN0byBwdWVkZSBkZXBlbmRlciBkZWwgcHJvYmxlbWEgY29uY3JldG8uDQoNCkFkZW3DoXMgZW50cmUgZWwgTUFEIHkgbGEgZGVzdmlhY2nDs24gZXN0w6FuZGFyIHNlIGN1bXBsZSBsYSBzaWd1aWVudGUgcHJvcGllZGFkOiANCg0K8J2cjiDiiYggMS40ODI2IE1BRA0KDQoNClBhcmEgZWwgZmlsdHJvIGRlIEhhbXBlbCBuZWNlc2l0YW1vcyBkZWZpbmlyIGRvcyBjb3NhczoNCjEuIEVsIHRhbWHDsW8gZGUgbGEgdmVudGFuYSAoY3XDoW50b3MgdmVjaW5vcyB2YW1vcyBhIGNvbnNpZGVyYXIpLg0KMi4gRWwgbsO6bWVybyBkZSBkZXN2aWFjaW9uZXMgcGFyYSBpZGVudGlmaWNhciBhIGxvcyBhdMOtcGljb3MgKGspLg0KDQpVbiB1bWJyYWwgbcOhcyBhbHRvIGhhY2UgcXVlIGVsIGZpbHRybyBzZWEgbcOhcyB0b2xlcmFudGUsIHVubyBtw6FzIGJham8gaWRlbnRpZmljYXLDoSBtw6FzIHB1bnRvcyBjb21vIHZhbG9yZXMgYXTDrXBpY29zLg0KDQoNCmBgYHtyfQ0KDQoNCmxpYnJhcnkocHJhY21hKQ0KDQpzZXQuc2VlZCgzMykNCg0KDQojIEdlbmVyYXIgbG9zIGRhdG9zIGFsZWF0b3Jpb3MNCg0KeCA9IG51bWVyaWMoMTAyNCkNCnogPSBybm9ybSgxMDI0KQ0KeFsxXSA9IHpbMV0NCmZvciAoaSBpbiAyOjEwMjQpIHsNCiAgeFtpXSA9IDAuNCp4W2ktMV0gKyAwLjgqeFtpLTFdKnpbaS0xXSArIHpbaV0NCn0NCg0KDQojIEFwbGljYXIgZWwgRmlsdHJvIGRlIEhhbXBlbCBhIG51ZXN0cmEgc2VyaWUgdGVtcG9yYWwgWCBjb24gZWwgcGFyYW1ldHJvIGsgcXVlIHJlcHJlc2VudGEgbGEgdmVudGFuYSwgZXMgbGEgY2FudGlkYWQgZGUgdmVjaW5vcyBxdWUgdmFtb3MgYSBjb25zaWRlcmFyIHBhcmEgaGFjZXIgZWwgY2FsY3Vsby4NCg0Kb21hZCA9IGhhbXBlbCh4LCBrPTIwKQ0KDQoNCiMgSW5kaWNlIGRlIGxvcyBhdMOtcGljb3MgZGV0ZWN0YWRvczoNCg0Kb21hZCRpbmQNCg0KDQojIEdyw6FmaWNvIGRldGVjdGFuZG8gYXTDrXBpY29zDQojDQpwbG90KDE6MTAyNCwgeCwgdHlwZT0ibCIpDQpwb2ludHMob21hZCRpbmQsIHhbb21hZCRpbmRdLCBwY2g9MjEsIGNvbD0iZGFya3JlZCIpDQoNCg0KIyBOdWV2YSBzZXJpZSBzaW4gYXTDrXBpY29zDQoNCnhfbmV3ID0gb21hZCR5DQoNCg0KIyBBbWJhcyBzZXJpZXMNCg0KcGxvdCgxOjEwMjQsIHgsIHR5cGU9ImwiLGNvbD0icmVkIix4bGltPWMoMCwgMTA1MCksIHlsaW09YygtNjAsNzApKQ0KcGFyKG5ldz1UUlVFKQ0KcGxvdCgxOjEwMjQsIHhfbmV3LCB0eXBlPSJsIixjb2w9ImJsdWUiLHhsaW09YygwLCAxMDUwKSwgeWxpbT1jKC02MCw3MCksYXhlcz0gRkFMU0UsIHhsYWI9JycsIHlsYWI9JycgKQ0KICANCiAgDQoNCmBgYA0KDQoNCg0K