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