Actividad 2

Ejercicio 1: serie Tasa Actividad

datos1=read.csv2("Tasa actividad, paro y empleo Alicante.csv")
#------------SEGMENTACIÓN DE DATOS-------------------------------------
datos1_tasa.actividad= datos1 %>%
  filter(Tasas=="Tasa de actividad")
datos1_tasa.empleo= datos1 %>%
  filter(Tasas=="Tasa de empleo de la poblaci\xf3n")
datos1_tasa.paro= datos1 %>%
  filter(Tasas=="Tasa de paro de la poblaci\xf3n")

#----------------------------------------------------------------------
transpose1 <- t(datos1_tasa.actividad)
transpose1 <- as.data.frame(transpose1)
rev_data_frame1 <- rev(transpose1)
rev_data_frame1 <- t(rev_data_frame1)
rev_data_frame1 <- as.data.frame(rev_data_frame1)
datos1_tasa.actividad=rev_data_frame1

transpose2 <- t(datos1_tasa.empleo)
transpose2 <- as.data.frame(transpose2)
rev_data_frame2 <- rev(transpose2)
rev_data_frame2 <- t(rev_data_frame2)
rev_data_frame2 <- as.data.frame(rev_data_frame2)
datos1_tasa.empleo=rev_data_frame2

transpose3<- t(datos1_tasa.paro)
transpose3 <- as.data.frame(transpose3)
rev_data_frame3 <- rev(transpose3)
rev_data_frame3 <- t(rev_data_frame3)
rev_data_frame3 <- as.data.frame(rev_data_frame3)
datos1_tasa.paro=rev_data_frame3

Tasa Actividad

knitr::kable(head(datos1_tasa.actividad))
Sexo Provincias Tasas Periodo Total
V84 Ambos sexos 03 Alicante/Alacant Tasa de actividad 2002T1 54.40
V83 Ambos sexos 03 Alicante/Alacant Tasa de actividad 2002T2 55.51
V82 Ambos sexos 03 Alicante/Alacant Tasa de actividad 2002T3 55.17
V81 Ambos sexos 03 Alicante/Alacant Tasa de actividad 2002T4 55.19
V80 Ambos sexos 03 Alicante/Alacant Tasa de actividad 2003T1 55.11
V79 Ambos sexos 03 Alicante/Alacant Tasa de actividad 2003T2 54.98
datos2_tasa.paro=datos1_tasa.paro
n=nrow(datos1_tasa.paro)
Tasas=rep(c("Tasa de paro"),each=n)
datos2_tasa.paro$Tasas=Tasas
comprobacion=datos2_tasa.paro==datos1_tasa.paro
datos1_tasa.paro=datos2_tasa.paro
#knitr::kable(head(datos1_tasa.paro))

A) Análisis básico

filas=nrow(datos1_tasa.actividad)
ultimovalor=datos1_tasa.actividad[filas,];ultimovalor
Sexo Provincias Tasas Periodo Total
V1 Ambos sexos 03 Alicante/Alacant Tasa de actividad 2022T4 60.21
  • Las serie termina el cuarto trimestre de 2022
filter(datos1_tasa.actividad,Periodo=="2014T1")
Sexo Provincias Tasas Periodo Total
V36 Ambos sexos 03 Alicante/Alacant Tasa de actividad 2014T1 57.49
  • El primer cuatrimestre de 2014 tenemos un valor de 57.49

B) Visualización básica de la serie

  • En este gráfico observamos una visualización básica de la gráfica donde de primera mano podemos detectar algunos puntos importantes
actividad=ts(as.numeric(datos1_tasa.actividad[,"Total"]),
               start=c(2002,1),
               frequency=4)
autoplot(actividad,
         xlab="",
         ylab="Actividad",
         main="Actividad por años")

  • En este gráfico tratamos de observar la tendencia y los ciclos de la serie de Actividad, hemos marcado los puntos más relevantes de la serie para observar sucesos importantes (por ejemplo el Covid en 2020)
actividadAnual=aggregate(actividad,FUN=sum)
min=min(actividadAnual)
min=max(actividadAnual)
autoplot(actividadAnual,
         xlab="",
         ylab="Actividad",
         main="Actividad por años") +
         geom_hline(yintercept = c(220.27,225.02,240.09),linetype="dashed",color="red")+
         geom_vline(xintercept=c(2002,2009,2020),linetype="dashed",color="red")

  • En este gráfico, observamos como no hay ninguna tendencia clara, pues los puntos del gráfico se reparten más bien de forma aleatoria por todo el “mapa”
actividadDesv=aggregate(actividad,FUN=sd)
plot(as.numeric(actividadAnual),as.numeric(actividadDesv))

  • En este gráfico comparamos la Actividad por trimestres y años; de forma clara, vemos que los últimos trimestres del año presentan de media más actividad que los primeros, además, podemos ver que el año anterior fue un año de baja actividad y que este está siendo una recuperación, particularmente para el segundo cuatrimestre, donde en el año anterior se obtuvieron datos catastróficos.
actividadb=window(actividad,start=2002)
ggsubseriesplot(actividadb)+
  ylab("Actividad")+
  xlab("")+
  ggtitle("")

  • Finalmente en este gráfico pretendemos buscar observaciones anómalas en los años comparando la actividad total entre todos ellos. Aún viendo que no hay ninguna, el gráfico nos ayuda a ver que la Activida tiende a crecer, pues los registros más altos son 2022, 2008,2009 y 2019, no estando 2020 y 2021 por el Covid.
ggseasonplot(actividadb,
             year.labels=TRUE,
             xlab="",
             ylab="Actividad",
             main="")

C) Regresiones locales

actividad_stl=stl(actividad,
                 s.window="periodic",
                 robust=TRUE)
autoplot(actividad_stl,
         xlab="",
         main="Descomposición de Actividad por regresores locales ponderados")

  • Observamos en este gráfico como la tendencia actual de la Actividad es mantenerse a niveles medios.

D) Medias móviles

  • Gracias a la siguiente gráfica podemos suavizar la tendencia que ha llevado la Actividad
  • En 2-Ma estamos tomando la media de dos trimestres, el anterior, y el actual.
  • En 4-Ma estamos tomando la media de un año, un trimestre anterior, el actual y dos posteriores.
  • En 2*4-Ma estamos tomando la media de dos años, el año anterior y el actual.
actividad_tsibble=tsibble(
  time= as.numeric(time(actividad)),
  actividad=as.numeric(datos1_tasa.actividad$Total),
  index=time)|>
  mutate(
    `2-Ma` = slider::slide_dbl(actividad,mean, .before=1, .after=0, .complete=FALSE),
    `4-Ma` = slider::slide_dbl(actividad,mean,.before=1,.after=2,.complete=FALSE),
    `2*4-Ma` = slider::slide_dbl(`4-Ma`,mean,.before=1,.after=0,.complete=FALSE),
  )
p=actividad_tsibble |>
  ggplot()+
  geom_line(aes(x=time,y=actividad),col="gray45",size=0.55)+
  geom_line(aes(x=time,y=`2-Ma`),col="red",size=0.65)+
  geom_line(aes(x=time,y=`2*4-Ma`),col="green",size=0.65)+
  xlab("año")+
  scale_color_manual(values=c("grey45","red","green"),labels=c("A","B","C"))
p + theme(legend.position="left")

E) Predicción de la Serie Actividad

  • Predicción de la serie bajo el método Ingenuo II en los próximos dos años
derivaActividad=rwf(actividad,h=8,drift=TRUE)
knitr::kable(derivaActividad)
Point Forecast Lo 80 Hi 80 Lo 95 Hi 95
2023 Q1 60.28 58.95313 61.60687 58.25073 62.30927
2023 Q2 60.35 58.46239 62.23761 57.46315 63.23685
2023 Q3 60.42 58.09460 62.74540 56.86361 63.97639
2023 Q4 60.49 57.78929 63.19071 56.35962 64.62038
2024 Q1 60.56 57.52321 63.59679 55.91563 65.20437
2024 Q2 60.63 57.28452 63.97548 55.51353 65.74647
2024 Q3 60.70 57.06622 64.33378 55.14262 66.25738
2024 Q4 60.77 56.86381 64.67619 54.79599 66.74401
autoplot(actividad,
         series="Actividad",
         xlab="",
         ylab="Actividad",
         main="Predicción sin estacionalidad")+
  autolayer(derivaActividad,series="Ingenuo II", PI=FALSE)+
  labs(colour="Métodos")+
  theme(legend.position=c(0.1,0.8))

snaive.Actividad=snaive(actividad,h=8,level=95)
autoplot(snaive.Actividad,
         xlab="",
         ylab="Actividad",
         main="Predicción con estacionalidad",
         PI=FALSE)

  • Utilizamos el método sin estacionalidad porque cuando lo analizamos vemos que este ofrece mejor error porcentual (MAPE=1.355733 frente al estacional MAPE=2.094922), además, el intervalo de confianza de las predicciones es mucho más fiable (ACF1=-0.1053811 frente a ACF1=0.5038194)
datosderiva=accuracy(derivaActividad)
datossnaive=accuracy(snaive.Actividad)


comparacion=as.data.frame(rbind(datosderiva,datossnaive))
rownames(comparacion)=c("Sin estacionalidad","Con estacionalidad")
comparacion
ME RMSE MAE MPE MAPE MASE ACF1
Sin estacionalidad 0.000000 1.022960 0.7826506 -0.0147015 1.355733 0.6459512 -0.1053811
Con estacionalidad 0.163125 1.437565 1.2116250 0.2558152 2.094921 1.0000000 0.5038194

Ejercicio 2: Serire Tasa Paro

A) Graficar y comentar la serie y su esquema

paro=ts(as.numeric(datos1_tasa.paro[,"Total"]),
               start=c(2002,1),
               frequency=4)
autoplot(paro,
         xlab="",
         ylab="Paro",
         main="Paro trimestral")

  • Podemos observar de una forma clara, como el paro tenía su nivel más bajo en 2005, donde el empleo era pleno y este aumenta en 2007 a raíz de la crisis económica de ese periodo. Vuelve a bajar desde 2013 hasta 2019 cuando por la Covid-19 vuelve a aumentar. Actualmente y desde la pandemia, el paro está bajando.
paroAnual=aggregate(paro,FUN=sum)
min=min(paroAnual)
max=max(paroAnual)
autoplot(paroAnual,
         xlab="",
         ylab="Paro",
         main="Paro anual") +
         geom_hline(yintercept = c(max,min),linetype="dashed",color="red")+
         geom_vline(xintercept=c(2007,2019),linetype="dashed",color="red")

  • En este gráfico vemos que se presenta un esquema aditivo por la aleatoriedad de los puntos en el gráfico
paroDesv=aggregate(paro,FUN=sd)
plot(as.numeric(paroAnual),as.numeric(paroDesv))

B) Predicción de ingenuo con estacionalidad para los próximos dos años

snaive.Paro=snaive(paro,h=8,level=95)
autoplot(snaive.Paro,
         xlab="",
         ylab="Paro",
         main="Predicción con estacionalidad",
         PI=FALSE)

knitr::kable(snaive.Paro)
Point Forecast Lo 95 Hi 95
2023 Q1 14.78 8.603176 20.95682
2023 Q2 14.13 7.953176 20.30682
2023 Q3 13.61 7.433176 19.78682
2023 Q4 15.21 9.033176 21.38682
2024 Q1 14.78 6.044652 23.51535
2024 Q2 14.13 5.394652 22.86535
2024 Q3 13.61 4.874652 22.34535
2024 Q4 15.21 6.474652 23.94535

C) Aplicación del método Holt-Winters

  • Ofrecemos la vista de ambas posibilidades para mostrar la similitud entre ambos modelos, pero nos debemos quedar con el esquema aditivo, pues en la graficación de puntos no observamos ninguna tendencia creciente.
# Aditivo
paropred <- window(paro, start = c(2002,1), end = c(2022, 4)) 

paroets <- ets(paropred, 
                       model = "AAA", 
                       damped = FALSE)
aditivo=data.frame(alpha=paroets$par["alpha"],beta=paroets$par["beta"],gamma=paroets$par["gamma"],row.names=c("Aditivo"))
# Multiplicativo
parob <- window(paro, start = 2002)
parobEts <- ets(parob, 
                       model = "MAM", 
                       damped = FALSE)
multiplicativo=data.frame(alpha=parobEts$par["alpha"],beta=parobEts$par["beta"],gamma=parobEts$par["gamma"],row.names=c("Multiplicativo"))
rbind(aditivo,multiplicativo)
alpha beta gamma
Aditivo 0.9789324 0.1034869 0.0001000
Multiplicativo 0.8939725 0.1469861 0.0001001
  • Los valores bajos en Beta (0.103) y Gamma (0.0001) nos indica que la pendiente y la estacionalidad se modifican muy lentamente. Sin embargo, el Alfa tan grande (0.9789324) indica que la serie varía constantemente en el tiempo

D) Analizar los errores del modelo Aditivo

  • Identificamos periodos cruciales como puede ser 2009, donde el paro ascendió de forma desorbitada, al igual que en el primer trimestre de 2020, con el coronavirus.
error=residuals(paroets)
sderror=sd(error)

autoplot(error,series="Error",
         colour="black",
         xlab="Semana",
         ylab="Error",
         main="")+
  geom_hline(yintercept= c(-3,-2,2,3)*sderror,
             colour=c("red","blue","blue","red"),lty=2)+
  scale_x_continuous(breaks=seq(6,26,2))+
  geom_vline(xintercept=c(2009,2020.25),linetype="dashed",color="red")

E) Predicción método Holt-Winters aditivo

parodf=forecast(paroets,
                h=8,
                level=95)
knitr::kable(parodf)
Point Forecast Lo 95 Hi 95
2023 Q1 15.26319 12.247755 18.27862
2023 Q2 14.42260 9.978919 18.86628
2023 Q3 13.59436 7.890484 19.29824
2023 Q4 14.29876 7.395760 21.20176
2024 Q1 14.37313 6.292589 22.45366
2024 Q2 13.53254 4.277539 22.78754
2024 Q3 12.70430 2.267422 23.14118
2024 Q4 13.40870 1.776409 25.04098
autoplot(paro,
         xlab="",
         ylab="Paro",
         main="Predicción paro Holt-Winters aditivo",
         colour="black",
         PI=FALSE)+
  autolayer(parodf,series="Holt-Winters aditivo",PI=FALSE)+
  autolayer(snaive.Paro,series="Ingenuo con Estacionalidad", PI=FALSE)+
  labs(colour="Métodos")+
  theme(legend.position=c(0.1,0.8))

  • Observamos que la serie predecida con Ingenuo con tendencia no tiene un intervalo de confianza de las predicciones fiable (ACF1=0.804918751). Además, tiene un error porcentual menor la predicción Holt-Winters (MAPE=6.702592%).
datosHolt=accuracy(parodf)
datossnaiveParo=accuracy(snaive.Paro)


comparacion=as.data.frame(rbind(datosHolt,datossnaiveParo))
rownames(comparacion)=c("Holt-Winters Aditivo","Ingenuo con Estacionalidad")
comparacion
ME RMSE MAE MPE MAPE MASE ACF1
Holt-Winters Aditivo -0.1193827 1.463420 1.097746 -0.8978805 6.702592 0.4634041 0.0053174
Ingenuo con Estacionalidad 0.1873750 3.151499 2.368875 -0.1132280 13.362802 1.0000000 0.8049188

F) Validación Training Set

  • Hemos realizado la predicción del modelo para Holt-Winters Aditivo y para Ingenuo con Estacionalidad
  • Aunque ambos tengan un error alto, el que mejor se adecua es Holt-Winters Aditivo (MAPE=6.595903%),
#Eliminamos los últimos 14 trimestres
paroIntra=subset(paro,end=length(paro)-14)
# additive predicción para esos 14 trimestres para Holt-Winters aditivo
paroIntraEts=ets(paroIntra,
                    mode="AAA",
                    damped=FALSE)
paroEtsExtraPre=forecast(paroIntraEts,
                         h=14,
                         level=95)
# Hacemos predicción para Ingenuo con Estacionalidad
snaive.paroIntra=snaive(paroIntra,h=14,level=95)

#Información
informacionmodelos=data.frame(rbind(accuracy(snaive.paroIntra),accuracy(paroEtsExtraPre)))
rownames(informacionmodelos)=c("Ingenuo II Estacionalidad","Holt-Winters Aditivo")
informacionmodelos
ME RMSE MAE MPE MAPE MASE ACF1
Ingenuo II Estacionalidad 0.2754545 3.125400 2.297273 0.6106416 12.693378 1.0000000 0.8191554
Holt-Winters Aditivo -0.0386868 1.373433 1.067346 0.0436654 6.595903 0.4646142 0.0871120
#Gráfica
autoplot(paro, series="Paro",linetype="solid")+
  autolayer(paroIntra,series="Reducida",linetype="dashed")+
  autolayer(paroEtsExtraPre,series="Holt-Winter Aditivo",linetype="solid",PI=F)+
  autolayer(snaive.paroIntra,series="Ingenuo Estacionalidad",linetype="solid",PI=F)

Ejercicio 3: Serie Consumo de Alimentos por Cápita

A) Análisis Básico

  • La serie termina en el año 2021
  • Llama mucho la atención 2019 y 2020, donde por el Coronavirus se produce este movimiento tan extraño en la serie, donde pasamos de 606.64 a 671.82 en tan solo un año
  • Como se comenta en el apartado anterior, la explicación es la Covid-19
consumo=read.csv2("alimentacionpc.csv")
consumo=ts(consumo,
               start=c(1987),
               frequency=1)

B) Graficar las componentes de la serie

  • En este gráfico vemos los cortes que presenta el gráfico en sus momentos más cruciales (1993 - 1995) una bajada drástica en el Consumo de Alimentos por Cápita y (2019 - 2020) una drástica subida
autoplot(consumo,
         xlab="",
         ylab="Consumo de Alimentos por Cápita",
         main="Consumo anual")+
  geom_vline(xintercept=c(2019,2020,1993,1995),linetype="dashed",color="red")

  • Con este gráfico vemos como claramente estamos ante un esquema multiplicativo, pues los puntos dispersos en el gráfico muestran una tendencia creciente
  • Al tratarse de una serie anual, no podremos estudiar la estacionalidad de la serie
consumoDesv=aggregate(consumo,FUN=sd)
plot(as.numeric(consumo),as.numeric(consumoDesv))

C) Estacionariedad de la Serie

  • La estacionariedad hace referencia a la característica de una serie que la hace estacionaria en el tiempo, es decir, que el transcurso del tiempo no influye en la variación de los valores de la misma.
  • Según la definición anterior podemos decir que la serie que estamos trabajando no es estacionaria, pues podemos ver como el paso del tiempo si influye en el aumento o la reducción de los valores de consumo.

D) Predicción de valores con método de la media

mediaConsumo=meanf(consumo,h=2)
autoplot(consumo,
        seires="Consumo Anual per Cápita",
        xlab="",
        ylab="Consumo",
        main="Consumo Anual de Alimentos per Cápita")+
  autolayer(mediaConsumo,series="Media",PI=FALSE)+
  labs(colour="Métodos")+
  theme(legend.position=c(0,0.8))

  • Podemos remarcar que las predicciones no presentan un intervalo de confianza muy fiable (ACF1=0.292032).
info=as.data.frame(accuracy(mediaConsumo))
rownames(info)="Método de la media"
info
ME RMSE MAE MPE MAPE MASE ACF1
Método de la media 0 16.33048 13.07631 -0.0649985 2.04427 1.068095 0.292032

E) Alisado Exponencial Simple

  • Vemos que alpha es igual a 0.3742, por lo que la serie no varía muy drásticamente con el tiempo, además, el error porcentual (MAPE=1.930308) es realmente bajo indicando así un ajuste bueno

  • Además, \(y_{t+2}=y_{t+1}=l_t=633.055\)

  • \(l_{t}=\alpha*y_t+(1-\alpha)l_{t-1}\)

consumo.alisado=window(consumo,start=c(1987),end=c(2021))
consumoEts=ets(consumo.alisado,model="ANN")
alpha=consumoEts$par["alpha"]
lt=consumoEts$states[36,]
info2=data.frame(Alpha=alpha,Lt=lt,"Lt-1"=653.9005)
rownames(info2)="Datos"
info2
Alpha Lt Lt.1
Datos 0.3741634 633.055 653.9005

F) Alisado Exponencial Simple

  • Alpha es un valor entre 0 y 1, significando lo siguiente para cada caso:
  • Si Alpha=0, la serie permanece constante en el tiempo
  • Si Alpha=1, la serie varía constantemente en el tiempo
  • En nuestro caso, como se ha comentado antes, alpha=0.3742, por lo que podemos decir que se mantiene medianamente constante en el tiempo.
consumodf=forecast(consumoEts,h=2,level=95)
autoplot(consumodf,xlab="",ylab="Consumo per cápita",main="Predicción del consumo per cápita")

G) Comparación de ambas predicciones

  • Gráficamente podemos ver como ambas predicciones son bastante similares
autoplot(consumo,series="Consumo")+
  autolayer(mediaConsumo,series="Predicción Media",PI=F)+
  autolayer(consumodf,series="Alisado Exponencial Simple",PI=F)

  • Si vemos los datos más técnicos, vemos como aunque ambos son parecidos en cierto sentido, podemos decir que el Alisado Exponencial Simple tiene un error menor (ACF1=0.0853%), teniendo así un intervalo de confianza aceptable.
infocomparacion=data.frame(rbind(accuracy(mediaConsumo),accuracy(consumodf)))
rownames(infocomparacion)=c("Método Medias","Alisado Exponencial Simple")

Ejercicio 4: Serie Población de Niños

Datos

poblacion=read.csv2("Población en miles de personas.csv")
poblacion=poblacion%>%filter(Edad=="De 0 a 4 anos")
poblacion=poblacion%>%filter(Unidad=="Valor absoluto")
poblacion=poblacion%>%filter(Sexo=="Ambos sexos")
poblacion=as.data.frame(apply(poblacion,2,rev))
head(poblacion)
Edad Sexo Unidad Periodo Total
De 0 a 4 anos Ambos sexos Valor absoluto 2002T1 1945.0
De 0 a 4 anos Ambos sexos Valor absoluto 2002T2 1963.5
De 0 a 4 anos Ambos sexos Valor absoluto 2002T3 1982.9
De 0 a 4 anos Ambos sexos Valor absoluto 2002T4 2007.6
De 0 a 4 anos Ambos sexos Valor absoluto 2003T1 2027.3
De 0 a 4 anos Ambos sexos Valor absoluto 2003T2 2050.5
poblacion=ts(as.numeric(poblacion[,"Total"]),
             start=c(2002,1),
             frequency=4)

A) Graficar y comentar análisis básico

  • Vemos como el punto más alto de niños se encuentra en el último cuatrimestre de 2010, punto a partir del cual la cifra comienza a bajar considerablemente, teniendo un parón en la bajada en el tercer cuatrimestre de 2018.

  • Este parón en la bajada no es siquiera notable si observamos la serie de forma anual

autoplot(poblacion,
         xlab="",
         ylab="Poblacion",
         main="Poblacion")+
  geom_vline(xintercept=c(2010.75,2018.5),linetype="dashed",color="red")+
  geom_hline(yintercept=c(as.numeric(max(poblacion)),2084.1),color="blue",linetype="dashed")

poblacionAnual=aggregate(poblacion, FUN = sum)
autoplot(poblacionAnual)+
  geom_hline(yintercept=c(max(poblacionAnual)),color="red",linetype="dashed")

  • En el siguiente gráfico observamos el tipo de esquema que sigue la serie, si nos fijamos, los puntos se esparcen de forma “aleatoria” por el gráfico, por lo que es un esquema aditivo.
poblacionDesv <- aggregate(poblacion, FUN = sd) 
plot(as.numeric(poblacionAnual  ), as.numeric(poblacionDesv))

  • Como vemos, se trata de una serie que se ha movido exactamente igual a lo largo de cada trimestre en los últimos años, por lo que tiene la misma forma y prácticamente la misma media
ggsubseriesplot(poblacion)+
  ylab("Poblacion")+
  xlab("Años")+
  ggtitle("")

  • Como vemos en este gráfico y se podía intuir anteriormente, la población en niños no aumenta estacionalmente, sino que se mantiene igual durante todo el año
ggseasonplot(poblacion,
             year.labels=TRUE,
             xlab="",
             ylab="Poblacion",
             main="")

B) Holt-Winters

  • \(\alpha=0.9994\) Lo que indica que esta serie varía mucho con el paso del tiempo.
  • \(\beta=0.1913\) indica que la pendiente apenas se mueve con el paso de tiempo, y tiende a mantenerse constante.
  • \(\gamma\approx0\) indica que la estacionalidad permanece constante en el tiempo. Esta componente no aporta apenas información
poblacionholt=window(poblacion,start=c(2002,1),end=c(2022,4))
poblacionEts=ets(poblacionholt,
                 model="AAA",
                 damped=FALSE)
summary(poblacionEts)
## ETS(A,A,A) 
## 
## Call:
##  ets(y = poblacionholt, model = "AAA", damped = FALSE) 
## 
##   Smoothing parameters:
##     alpha = 0.9994 
##     beta  = 0.1913 
##     gamma = 6e-04 
## 
##   Initial states:
##     l = 2076.3123 
##     b = 13.9701 
##     s = 2.5819 4.433 -3.3095 -3.7054
## 
##   sigma:  19.5961
## 
##      AIC     AICc      BIC 
## 881.6368 884.0692 903.5142 
## 
## Training set error measures:
##                     ME     RMSE      MAE         MPE      MAPE      MASE
## Training set -1.823973 18.63957 10.04761 -0.07581853 0.4718947 0.1680589
##                    ACF1
## Training set 0.01296637

C) Componentes estacionales s1,s2,s3 y s4

  • Como terminamos en el último cuatrismetre de 2022, tenemos que s1 es el valor estacional para el último dato (último trimester de 2022), s2 tercer trimestre de 2022, s3 segundo trimestre y s1 primer trimestre.
tail(poblacionEts$states,1)
##                l         b       s1       s2        s3        s4
## 2022 Q4 1796.757 -15.34179 2.642959 4.357625 -3.349597 -3.749964

D) Predicción a 5 años

  • En este ejercicio he probado a bajar el confidence level para estrechar el canal de predicción y ver otra alternativa. Vemos que este modelo está destinado a seguir bajando, a no ser que se produzca algo independiente a la serie que lo haga ser modificado.
poblaciondf=forecast(poblacionEts,
                     h=20,
                     level=70)
autoplot(poblaciondf,
         xlab="Población + 5 años predicción",
         ylab="Población",
         main="Predicción Holt-Winters Aditivo",
         PI=TRUE)

E) Validación Cruzada

  • El error a un trimestre vista es de 7.77, sin embargo, si nos vamos al final del periodo (8 trimestres o 2 años), el error asciende a 58.58
k <- 32                  #Minimo numero de datos para estimar
h <- 8                   #Horizonte de las prediciciones
TT <- length(poblacion)#Longitud serie
s <- TT - k - h           #Total de estimaciones

rmse <- matrix(NA, s + 1, h)
mape <- matrix(NA, s + 1, h)
for (i in 0:s) {
  train.set <- subset(poblacion, start = i + 1, end = i + k)
  test.set <-  subset(poblacion, start = i + k + 1, end = i + k + h)
  
  poblacionEts <- ets(train.set, 
                       model = "AAA", 
                       damped = FALSE)
  fcast <- forecast(poblacionEts, h = h)
  rmse[i + 1,] <- (test.set - fcast$mean)^2
  mape[i + 1,] <- 100*abs(test.set - fcast$mean)/test.set
}

rmse.ets <- sqrt(colMeans(rmse))
mape.ets <- colMeans(mape)

error=data.frame(t(round(rmse.ets, 2)))
colnames(error)=c("1T","2T","3T","4T","5T","6T","7T","8T")
rownames(error)=c("Error")
error
1T 2T 3T 4T 5T 6T 7T 8T
Error 7.77 14.1 20.86 26.68 32.64 41.51 49.96 58.58

Ejercicio 5: Serie Pasajeros

A) Graficar la serie completa y comentar

  • Salta claramente a la vista el año 2020, como vemos el año comenzó como cualquier otro, pero en el momento en el que la Covid se hizo una realidad, el transporte bajó drásticamente.
pasajeros=read.csv2("Pasajeros.csv")
pasajeros=ts(pasajeros,
               start=c(1996,1),
               frequency=12)
pasajerosb=window(pasajeros,start=1996)
ggseasonplot(pasajerosb,
             year.labels=T,
             xlab="Años",
             ylab="Pasajeros",
             main="")

B) Recortar la serie

  • Seguimos con el ejercicio a partir de aquí
pasajeros2=as.data.frame(pasajeros)
pasajeros=pasajeros2[1:(312-24),]
tibble(pasajeros=head(pasajeros))
pasajeros
216721
213787
221582
204038
220739
201742
pasajeros=ts(pasajeros,
             start=c(1996,1),
             frequency=12)

C) Graficas y comentar

  • En este gráfico apreciamos como el crecimiento de usuarios en el transporte público desde 1996 creción significativamente hasta llegar a un punto (2007) donde se paró el crecimiento y comenzó a bajar. Todo esto hasta 2013 donde de nuevo se retoma el uso de transporte y crece hasta sus niveles más altos en 2019.
pasajerosAnual=aggregate(pasajeros,FUN=sum)
autoplot(pasajerosAnual,
         xlab="Años",
         ylab="Pasajeros",
         main="Pasajeros transporte público 1996-2019")+
  geom_vline(xintercept=c(2007,2013,2019),linetype="dashed",color="red")

  • En el siguiente gráfico observamos el esquema del gráfico, donde vemos que es un esqauema aditivo por la aleatoriedad de los puntos en el gráfico
pasajerosDesv=aggregate(pasajeros,FUN=sd)
pasajerosAnual=aggregate(pasajeros,FUN=sum)
plot(as.numeric(pasajerosAnual),as.numeric(pasajerosDesv))

  • En este gráfico estudiamos la estacinalidad de los pasajeros en la serie. Como vemos, en los periodos vacacionales el uso de transporte público disminuye drásticamente (de Junio a Septiembre se mantiene bajo, al igual que en Navidad) y se encuentra en sus mayores niveles en momentos de máximo trabajo en el calendario anual (Octubre y Marzo).
pasajerosb=window(pasajeros,start=1996)
ggsubseriesplot(pasajerosb)+
  ylab("Pasajeros")+
  xlab("")+
  ggtitle("Estacionalidad de pasajeros")

D) Medias móviles

actividad_tsibble=tsibble(
  time= as.numeric(time(pasajeros)),
  actividad=as.numeric(pasajeros),
  index=time)|>
  mutate(
    `3-Ma` = slider::slide_dbl(actividad,mean, .before=1, .after=1, .complete=FALSE),
    `12-Ma` = slider::slide_dbl(actividad,mean,.before=5,.after=6,.complete=FALSE),
    `2*12-Ma` = slider::slide_dbl(`12-Ma`,mean,.before=1,.after=0,.complete=FALSE),
  )
p=actividad_tsibble |>
  ggplot()+
  geom_line(aes(x=time,y=actividad),col="gray45",size=0.55)+
  geom_line(aes(x=time,y=`3-Ma`),col="red",size=0.65)+
  geom_line(aes(x=time,y=`12-Ma`),col="green",size=0.65)+
  geom_line(aes(x=time,y=`2*12-Ma`),col="blue",size=0.65)+
  xlab("año")+
  scale_color_manual(values=c("grey45","red","green"),labels=c("A","B","C"))
p + theme(legend.position="left")

Ejercicio 6: Serie Aforo Oropesa

A) Graficar y comentar las componentes principales

  • En el siguiente gráfico vemos la evolución anual de los vehículos que circulan por Oropesa, donde observamosdos tramos principales. De 1960 a 1982 donde vemos una montaña de crecimiento en la circulación (se puede deber quizá a una industria muy explotada en la zona que requería mucha circulación de tráfico). Y el siguiente tramo desde 1982 en adelante, donde vemos un crecimiento sustancial de vehículos en la zona. A este aumento podemos atribuirle la expansión del automóvil, pues es evidente que en este tramo el número de vehículos disponibles para la población creción sustancialmente.

  • Desde 2007 en adelante el número de vehículos en ese km disminuye por la construcción de una autopista que permite una mayor fluidez y velocidad en el tráfico.

vehiculos=read.csv2("aforo_oropesa.csv")
vehiculos=ts(vehiculos,
               start=c(1960),
               frequency=1)

autoplot(vehiculos,
         xlab="Años",
         ylab="Vehículos",
         main="Vehículos por Oropesa")+
  geom_hline(yintercept = c(max(vehiculos)),color="red",linetype="dotdash",lwd=1)+
  geom_vline(xintercept=c(1982,2007),linetype="twodash",color="red")

  • Gracias al siguiente gráfico podemos afirmar que se trata de un esquema multiplicativo debido a la continuidad de los puntos en el gráfico.
vehiculosDesv=aggregate(vehiculos,FUN=sd)
plot(as.numeric(vehiculos),as.numeric(vehiculosDesv))

B) Predicción método media y deriva

  • El método que mejor resultado obtiene es el método Deriva, pero realmente ninguno de los dos ofrece un intervalo de confianza para el error bueno.

  • La diferencia principal entre ambos métodos es que la media simplemente calcula la media total de la serie para ofrecer la información y el método deriva añade a la observación anterior el incremento medio de la serie * el número de periodos. (Si se quiere obtener predicción de dos años en una serie anual, se calcula el incremento medio y se multiplica por dos)

mediaVehiculos=meanf(vehiculos,h=5)
mediaVehiculosdf=as.data.frame(mediaVehiculos)

# método deriva
derivaVehiculos=rwf(vehiculos, h=5, drift=T)
derivaVehiculosdf=as.data.frame(derivaVehiculos)
predicciones=data.frame(Metodo_Media=mediaVehiculosdf[,"Point Forecast"],Metodo_deriva=derivaVehiculosdf[,"Point Forecast"])
rownames(predicciones)=c(2020:2024)
predicciones
Metodo_Media Metodo_deriva
2020 10379.37 9824.746
2021 10379.37 9961.492
2022 10379.37 10098.237
2023 10379.37 10234.983
2024 10379.37 10371.729
vehiculosderiva=accuracy(derivaVehiculos)
vehiculosmedia=accuracy(mediaVehiculos)

autoplot(vehiculos,
         series="Original")+
  autolayer(derivaVehiculos,PI=F,series="predicción deriva")+
  autolayer(mediaVehiculos,PI=F,series="predicción media")

comparacion=as.data.frame(rbind(vehiculosderiva,vehiculosmedia))
rownames(comparacion)=c("Método Deriva","Método Media")
comparacion
ME RMSE MAE MPE MAPE MASE ACF1
Método Deriva 0 925.7564 640.9457 0.8655602 6.865007 0.9444505 0.3001172
Método Media 0 3638.2852 2826.2300 -30.5042998 50.645009 4.1645247 0.9188694

C) Tipos de Alisado Exponencial posibles

  • Gracias a las gráficas del apartado “A)”, sabemos que el esquema de la serie es multiplicativo, por lo que solamente podemos aplicar métodos sin estacionalidad (porque es una serie anual) y con error multiplicativo.

  • Hemos aplicado los métodos MAN (error multiplicativo, tendencia aditiva y sin estacionalidad) y MNN (error multiplicativo, sin tendencia y sin estacionalidad)

vehiculosb=window(vehiculos,start=1960)
vehiculosbEts=ets(vehiculosb,
                  model="MAN",
                  damped=FALSE)

vehiculosbEts2=ets(vehiculosb,
                  model="MNN",
                  damped=FALSE)
vehiculosbEts1=accuracy(vehiculosbEts)
vehiculosbEts21=accuracy(vehiculosbEts2)
comparacion=as.data.frame(rbind(vehiculosbEts1,vehiculosbEts21))
rownames(comparacion)=c("Método MAN","Método MNN")
comparacion
ME RMSE MAE MPE MAPE MASE ACF1
Método MAN -100.9985 908.9398 582.1795 -0.4348065 6.089607 0.8578568 0.1983227
Método MNN 134.8033 928.0030 667.6877 2.4499440 7.437629 0.9838555 0.2998183
  • Analizamos el error del modelo MAN (que es con el que nos quedamos) y vemos que no hay ninguna observación importante que se salga de los límites.
error <- residuals(vehiculosbEts)
sderror <- sd(error)

autoplot(error, series="Error",
         colour = "black",
         xlab = "Semana",
         ylab = "Error",
         main = "") +
  geom_hline(yintercept = c(-3, -2, 2 ,3)*sderror, 
             colour = c("red", "blue", "blue", "red"), lty = 2) + 
  scale_x_continuous(breaks= seq(6, 26, 2)) 
## Scale for x is already present.
## Adding another scale for x, which will replace the existing scale.

D) Modelo y las ecuaciones recursivas del mismo

  • El modelo que tenemos es MAN, es decir:

  • \(y_{t+h|t}= l_t + hb_t\), con ecuaciones recursivas tal que:

  • \(l_t=\alpha\,y_t+(1-\alpha)(l_{t-1}+b_{t-1}),\)

  • \(b_t=\beta(l_t-l_{t-1})+(1-\beta)b_{t-1}.\)

  • Donde: \(\alpha=0.9998997\),\(\beta=0.1010386\), \(l_t=901.131\) y \(b_t=398.8961\)

  • El modelo MNN es:

  • \(y_{t+h|t}=l_t\)

  • \(l_t=\alpha y_t+(1-\alpha)l_{t-1}\)

  • Donde: \(\alpha=0.9998995\) y \(l_t=1600.6263\)

tibble(alpha=vehiculosbEts$par["alpha"],beta=vehiculosbEts$par["beta"])
alpha beta
0.9998997 0.1010386
tibble(alpha=vehiculosbEts2$par["alpha"])
alpha
0.9998995

E) Validación Cruzada

  • El error inicial es 6.69, sin embargo, 5 años más tarde pasa a ser 31.52 aumentando de una forma considerada
k <- 20                  #Minimo numero de datos para estimar
h <- 5                   #Horizonte de las prediciciones
TT <- length(vehiculos)#Longitud serie
s <- TT - k - h           #Total de estimaciones

rmse <- matrix(NA, s + 1, h)
mape <- matrix(NA, s + 1, h)
for (i in 0:s) {
  train.set <- subset(vehiculos, start = i + 1, end = i + k)
  test.set <-  subset(vehiculos, start = i + k + 1, end = i + k + h)
  vehiculosbEts=ets(train.set,
                  model="MAN",
                  damped=FALSE)
  fcast <- forecast(vehiculosbEts, h = h,level=95)
  
  rmse[i + 1,] <- (test.set - fcast$mean)^2
  mape[i + 1,] <- 100*abs(test.set - fcast$mean)/test.set
}

rmse.snaive <- sqrt(colMeans(rmse))
mape.snaive <- colMeans(mape)

error=data.frame(t(round(rmse.snaive, 2)))
colnames(error)=c("1A","2A","3A","4A","5A")

mape=data.frame(t(round(mape.snaive, 2)))
colnames(mape)=c("1A","2A","3A","4A","5A")
rownames(mape)=c("Mape")
informacion=rbind(error,mape)
rownames(informacion)=c("Error","Mape")
informacion
1A 2A 3A 4A 5A
Error 1054.97 1773.37 2516.90 3545.14 4535.10
Mape 6.69 11.67 17.71 24.62 31.52

Ejercicio 7: Serie Producción de Chocolate

A) Graficar y comentar las componentes principales

  • Esta gráfica es un primer vistazo general de la evolución de la producción de chocolate en Australia
chocolate=read.csv2("Chocolate.csv")
chocolate=ts(chocolate,
               start=c(1958,1),
               frequency=12)
autoplot(chocolate,
         xlab="",
         ylab="Chocolate vendido",
         main="Chocolate vendido en Australila")

  • Sin necesidad de crear lineas de marcación, vemos de forma clara como el chocolate ha sido y es un bien cuya producción no ha hecho más que crecer durante el trascurso de toda la serie, en algunos momentos ha sufrido de momentos de decadencia, pero nunca han logrado un efecto dañino para el crecimiento.
chocolateAnual=aggregate(chocolate, FUN=sum)
autoplot(chocolateAnual,
         xlab="Años",
         ylab="Producción de chocolate",
         main="Producción de chocolate en Australia")

  • Podemos interpretar dicho gráfico como un esquema multiplicativo de la serie, pues a mayor tiempo, mayor valor de los puntos en el gráfico
chocolateDesv=aggregate(chocolate, FUN=sd)
plot(as.numeric(chocolateAnual),as.numeric(chocolateDesv))

  • En este gráfico de estacionalidad vemos de una forma muy clara como la producción de chocolate aumenta a partir de febrero y comienza a disminuir en septiembre hasta alcanzar el punto más bajo en Enero.
chocolateb=window(chocolate,start=1958)
ggsubseriesplot(chocolateb)+
  ylab("Chocolate")

  • Observando la serie completa vemos que no hay ningún año raro, y que todos parecen bastante corrientes.
ggseasonplot(chocolateb,
             year.labels=T,
             ylab="Producción de Chocolate")

B) Descomposición en regresiones locales

#logchocolate_stl <- stl(log(chocolate),
                       #s.window = "periodic",
                       #robust = TRUE)

C) Alisado Exponencial

  • Como hemos visto anteriormente estamos ante un esquema multiplicativo, por lo que tendremos que utilizar un Alisado de Holt-Winters multiplicativo

  • Este modelo se describe como \(y_{t+h|t}= (l_t +hb_t)*s_{t+h-m(k+1)}\), donde:

  • \(\alpha=0.1638\) Un valor tan bajo indica que la serie apenas varía con el tiempo

  • \(\beta=0\) Este valor indica que la pendiente no varía con el paso del tiempo

  • \(\gamma=0.3358\) Decimos que la estacionalidad permanece más bien constante en el tiempo

chocolateb=window(chocolate,star=1958)
chocolatebEts=ets(chocolateb,
                  model="MAM",
                  damped=F)
summary(chocolatebEts)
## ETS(M,A,M) 
## 
## Call:
##  ets(y = chocolateb, model = "MAM", damped = F) 
## 
##   Smoothing parameters:
##     alpha = 0.1638 
##     beta  = 1e-04 
##     gamma = 0.3358 
## 
##   Initial states:
##     l = 2389.3933 
##     b = 15.3823 
##     s = 0.6671 0.8847 0.95 0.9934 1.0893 1.2565
##            1.2352 1.3236 1.1559 1.0285 0.8593 0.5564
## 
##   sigma:  0.1071
## 
##      AIC     AICc      BIC 
## 8246.489 8247.926 8316.118 
## 
## Training set error measures:
##                     ME     RMSE      MAE       MPE    MAPE      MASE      ACF1
## Training set -15.45195 553.6932 407.4784 -1.031004 8.16777 0.7761905 0.1515466

D) Valores de la última fecha de la serie

  • Obtenemos que \(l=9784.753\) y \(b=15.80399\), para poder entender mejor qué es esto, vemos como l es la estimación del nivel de la serie y b es la estimación de la pendiente de la serie.
  • Por otro lado \(s_i \forall\,i\in (1,2,...,12)\) tenemos la estimación de la estacionalidad en los meses en orden inverso, s1 es el valor para Diciembre (el último dato), s2 para Noviembre y así hasta s12 que corresponde a Enero
tail(chocolatebEts$states,1)
##                 l        b        s1       s2       s3       s4       s5
## Dec 1994 9784.753 15.80399 0.9459899 1.056467 1.057233 1.042047 1.140944
##                 s6        s7        s8        s9     s10       s11       s12
## Dec 1994 0.9891633 0.9695922 0.9650341 0.8533319 1.00468 0.9157171 0.6283261

E) Predicción para los próximos años

chocolatebf=forecast(chocolatebEts,
                     h=36,
                     level=95)

chocolatebf$mean
##            Jan       Feb       Mar       Apr       May       Jun       Jul
## 1995  6157.946  8989.010  9878.183  8403.586  9518.878  9579.161  9788.148
## 1996  6281.023  9168.390 10075.000  8570.762  9707.948  9769.134  9981.965
## 1997  6404.206  9347.926 10271.988  8738.084  9897.181  9959.271 10175.951
##            Aug       Sep       Oct       Nov       Dec
## 1995 11308.110 10344.393 10511.848 10520.931  9435.683
## 1996 11531.679 10548.593 10719.034 10727.978  9621.088
## 1997 11755.441 10752.970 10926.401 10935.205  9806.655
autoplot(chocolatebf,
         xlab="Años",
         ylab="Producción de chocolate",
         main="Predicción de Chocolate + 3 años",
         PI=F)

F) Training Set

chocolateIntra <- subset(chocolate, end = length(chocolate) - 120)
chocolateExtra <- subset(chocolate, start = length(chocolate) - 119)
chocolateExtraPre <- snaive(chocolateIntra, h = 56)
accuracy(chocolateExtraPre, chocolateExtra)
##                     ME      RMSE       MAE       MPE     MAPE     MASE
## Training set  127.3429  538.4386  427.7212  2.443063 10.16616 1.000000
## Test set     1176.8036 1612.5819 1359.9107 16.919180 19.88413 3.179433
##                   ACF1 Theil's U
## Training set 0.3381947        NA
## Test set     0.5861061 0.6683054
LS0tDQp0aXRsZTogIkFjdGl2aWRhZCAyIg0KRGF0ZTogIjIwMjMtMDQtMDQiDQpvdXRwdXQ6IA0KICBybWRmb3JtYXRzOjpyZWFkdGhlZG93biA6DQogICAgY29kZV9kb3dubG9hZDogVFJVRQ0KICAgIGhpZ2hsaWd0aDogImthdGUiDQogICAgc2VsZl9jb250YWluZWQ6IHRydWUNCiAgICBjb2RlX2ZvbGRpbmc6ICJoaWRlIg0KICAgIHRodW1ibmFpbHM6IHRydWUNCiAgICBnYWxsZXJ5OiB0cnVlDQogICAgZmlnX3dpZHRoOiA0DQogICAgZmlnX2hlaWdodDogNA0KICAgIGRmX3ByaW50OiBrYWJsZQ0KcGtnZG93bjoNCiAgYXNfaXM6dHJ1ZQ0KZWRpdG9yX29wdGlvbnM6IA0KICBjaHVua19vdXRwdXRfdHlwZTogaW5saW5lDQotLS0NCg0KYGBge3Igc2V0dXAsIGluY2x1ZGU9RkFMU0V9DQprbml0cjo6b3B0c19jaHVuayRzZXQoZWNobyA9IFRSVUUpDQpsaWJyYXJ5KHV0aWxzKQ0KbGlicmFyeShkcGx5cikNCmxpYnJhcnkoZ2dwbG90MikNCmxpYnJhcnkodHNpYmJsZSkNCmxpYnJhcnkoZm9yZWNhc3QpDQpgYGANCg0KIyBFamVyY2ljaW8gMTogc2VyaWUgVGFzYSBBY3RpdmlkYWQNCg0KYGBge3J9DQoNCmRhdG9zMT1yZWFkLmNzdjIoIlRhc2EgYWN0aXZpZGFkLCBwYXJvIHkgZW1wbGVvIEFsaWNhbnRlLmNzdiIpDQojLS0tLS0tLS0tLS0tU0VHTUVOVEFDScOTTiBERSBEQVRPUy0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0NCmRhdG9zMV90YXNhLmFjdGl2aWRhZD0gZGF0b3MxICU+JQ0KICBmaWx0ZXIoVGFzYXM9PSJUYXNhIGRlIGFjdGl2aWRhZCIpDQpkYXRvczFfdGFzYS5lbXBsZW89IGRhdG9zMSAlPiUNCiAgZmlsdGVyKFRhc2FzPT0iVGFzYSBkZSBlbXBsZW8gZGUgbGEgcG9ibGFjaVx4ZjNuIikNCmRhdG9zMV90YXNhLnBhcm89IGRhdG9zMSAlPiUNCiAgZmlsdGVyKFRhc2FzPT0iVGFzYSBkZSBwYXJvIGRlIGxhIHBvYmxhY2lceGYzbiIpDQoNCiMtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tDQp0cmFuc3Bvc2UxIDwtIHQoZGF0b3MxX3Rhc2EuYWN0aXZpZGFkKQ0KdHJhbnNwb3NlMSA8LSBhcy5kYXRhLmZyYW1lKHRyYW5zcG9zZTEpDQpyZXZfZGF0YV9mcmFtZTEgPC0gcmV2KHRyYW5zcG9zZTEpDQpyZXZfZGF0YV9mcmFtZTEgPC0gdChyZXZfZGF0YV9mcmFtZTEpDQpyZXZfZGF0YV9mcmFtZTEgPC0gYXMuZGF0YS5mcmFtZShyZXZfZGF0YV9mcmFtZTEpDQpkYXRvczFfdGFzYS5hY3RpdmlkYWQ9cmV2X2RhdGFfZnJhbWUxDQoNCnRyYW5zcG9zZTIgPC0gdChkYXRvczFfdGFzYS5lbXBsZW8pDQp0cmFuc3Bvc2UyIDwtIGFzLmRhdGEuZnJhbWUodHJhbnNwb3NlMikNCnJldl9kYXRhX2ZyYW1lMiA8LSByZXYodHJhbnNwb3NlMikNCnJldl9kYXRhX2ZyYW1lMiA8LSB0KHJldl9kYXRhX2ZyYW1lMikNCnJldl9kYXRhX2ZyYW1lMiA8LSBhcy5kYXRhLmZyYW1lKHJldl9kYXRhX2ZyYW1lMikNCmRhdG9zMV90YXNhLmVtcGxlbz1yZXZfZGF0YV9mcmFtZTINCg0KdHJhbnNwb3NlMzwtIHQoZGF0b3MxX3Rhc2EucGFybykNCnRyYW5zcG9zZTMgPC0gYXMuZGF0YS5mcmFtZSh0cmFuc3Bvc2UzKQ0KcmV2X2RhdGFfZnJhbWUzIDwtIHJldih0cmFuc3Bvc2UzKQ0KcmV2X2RhdGFfZnJhbWUzIDwtIHQocmV2X2RhdGFfZnJhbWUzKQ0KcmV2X2RhdGFfZnJhbWUzIDwtIGFzLmRhdGEuZnJhbWUocmV2X2RhdGFfZnJhbWUzKQ0KZGF0b3MxX3Rhc2EucGFybz1yZXZfZGF0YV9mcmFtZTMNCmBgYA0KDQojIyBUYXNhIEFjdGl2aWRhZA0KDQpgYGB7cn0NCmtuaXRyOjprYWJsZShoZWFkKGRhdG9zMV90YXNhLmFjdGl2aWRhZCkpDQpgYGANCg0KYGBge3J9DQpkYXRvczJfdGFzYS5wYXJvPWRhdG9zMV90YXNhLnBhcm8NCm49bnJvdyhkYXRvczFfdGFzYS5wYXJvKQ0KVGFzYXM9cmVwKGMoIlRhc2EgZGUgcGFybyIpLGVhY2g9bikNCmRhdG9zMl90YXNhLnBhcm8kVGFzYXM9VGFzYXMNCmNvbXByb2JhY2lvbj1kYXRvczJfdGFzYS5wYXJvPT1kYXRvczFfdGFzYS5wYXJvDQpkYXRvczFfdGFzYS5wYXJvPWRhdG9zMl90YXNhLnBhcm8NCiNrbml0cjo6a2FibGUoaGVhZChkYXRvczFfdGFzYS5wYXJvKSkNCmBgYA0KDQojIyBBKSBBbsOhbGlzaXMgYsOhc2ljbw0KDQpgYGB7cn0NCmZpbGFzPW5yb3coZGF0b3MxX3Rhc2EuYWN0aXZpZGFkKQ0KdWx0aW1vdmFsb3I9ZGF0b3MxX3Rhc2EuYWN0aXZpZGFkW2ZpbGFzLF07dWx0aW1vdmFsb3INCmBgYA0KDQotICAgTGFzIHNlcmllIHRlcm1pbmEgZWwgY3VhcnRvIHRyaW1lc3RyZSBkZSAyMDIyDQoNCmBgYHtyfQ0KZmlsdGVyKGRhdG9zMV90YXNhLmFjdGl2aWRhZCxQZXJpb2RvPT0iMjAxNFQxIikNCmBgYA0KDQotICAgRWwgcHJpbWVyIGN1YXRyaW1lc3RyZSBkZSAyMDE0IHRlbmVtb3MgdW4gdmFsb3IgZGUgNTcuNDkNCg0KIyMgQikgVmlzdWFsaXphY2nDs24gYsOhc2ljYSBkZSBsYSBzZXJpZQ0KDQotICAgRW4gZXN0ZSBncsOhZmljbyBvYnNlcnZhbW9zIHVuYSB2aXN1YWxpemFjacOzbiBiw6FzaWNhIGRlIGxhIGdyw6FmaWNhIGRvbmRlIGRlIHByaW1lcmEgbWFubyBwb2RlbW9zIGRldGVjdGFyIGFsZ3Vub3MgcHVudG9zIGltcG9ydGFudGVzDQoNCmBgYHtyLGZpZy5hbGlnbj0nY2VudGVyJyxvdXQuZXh0cmE9J2FuZ2xlPTkwJywgZWNobz1UUlVFfQ0KYWN0aXZpZGFkPXRzKGFzLm51bWVyaWMoZGF0b3MxX3Rhc2EuYWN0aXZpZGFkWywiVG90YWwiXSksDQogICAgICAgICAgICAgICBzdGFydD1jKDIwMDIsMSksDQogICAgICAgICAgICAgICBmcmVxdWVuY3k9NCkNCmF1dG9wbG90KGFjdGl2aWRhZCwNCiAgICAgICAgIHhsYWI9IiIsDQogICAgICAgICB5bGFiPSJBY3RpdmlkYWQiLA0KICAgICAgICAgbWFpbj0iQWN0aXZpZGFkIHBvciBhw7FvcyIpDQpgYGANCg0KLSAgIEVuIGVzdGUgZ3LDoWZpY28gdHJhdGFtb3MgZGUgb2JzZXJ2YXIgbGEgdGVuZGVuY2lhIHkgbG9zIGNpY2xvcyBkZSBsYSBzZXJpZSBkZSBBY3RpdmlkYWQsIGhlbW9zIG1hcmNhZG8gbG9zIHB1bnRvcyBtw6FzIHJlbGV2YW50ZXMgZGUgbGEgc2VyaWUgcGFyYSBvYnNlcnZhciBzdWNlc29zIGltcG9ydGFudGVzIChwb3IgZWplbXBsbyBlbCBDb3ZpZCBlbiAyMDIwKQ0KDQpgYGB7cixmaWcuYWxpZ249J2NlbnRlcicsb3V0LmV4dHJhPSdhbmdsZT05MCcsIGVjaG89VFJVRX0NCmFjdGl2aWRhZEFudWFsPWFnZ3JlZ2F0ZShhY3RpdmlkYWQsRlVOPXN1bSkNCm1pbj1taW4oYWN0aXZpZGFkQW51YWwpDQptaW49bWF4KGFjdGl2aWRhZEFudWFsKQ0KYXV0b3Bsb3QoYWN0aXZpZGFkQW51YWwsDQogICAgICAgICB4bGFiPSIiLA0KICAgICAgICAgeWxhYj0iQWN0aXZpZGFkIiwNCiAgICAgICAgIG1haW49IkFjdGl2aWRhZCBwb3IgYcOxb3MiKSArDQogICAgICAgICBnZW9tX2hsaW5lKHlpbnRlcmNlcHQgPSBjKDIyMC4yNywyMjUuMDIsMjQwLjA5KSxsaW5ldHlwZT0iZGFzaGVkIixjb2xvcj0icmVkIikrDQogICAgICAgICBnZW9tX3ZsaW5lKHhpbnRlcmNlcHQ9YygyMDAyLDIwMDksMjAyMCksbGluZXR5cGU9ImRhc2hlZCIsY29sb3I9InJlZCIpDQpgYGANCg0KLSAgIEVuIGVzdGUgZ3LDoWZpY28sIG9ic2VydmFtb3MgY29tbyBubyBoYXkgbmluZ3VuYSB0ZW5kZW5jaWEgY2xhcmEsIHB1ZXMgbG9zIHB1bnRvcyBkZWwgZ3LDoWZpY28gc2UgcmVwYXJ0ZW4gbcOhcyBiaWVuIGRlIGZvcm1hIGFsZWF0b3JpYSBwb3IgdG9kbyBlbCAibWFwYSINCg0KYGBge3IsZmlnLmFsaWduPSdjZW50ZXInLG91dC5leHRyYT0nYW5nbGU9OTAnLCBlY2hvPVRSVUV9DQoNCmFjdGl2aWRhZERlc3Y9YWdncmVnYXRlKGFjdGl2aWRhZCxGVU49c2QpDQpwbG90KGFzLm51bWVyaWMoYWN0aXZpZGFkQW51YWwpLGFzLm51bWVyaWMoYWN0aXZpZGFkRGVzdikpDQpgYGANCg0KLSAgIEVuIGVzdGUgZ3LDoWZpY28gY29tcGFyYW1vcyBsYSBBY3RpdmlkYWQgcG9yIHRyaW1lc3RyZXMgeSBhw7FvczsgZGUgZm9ybWEgY2xhcmEsIHZlbW9zIHF1ZSBsb3Mgw7psdGltb3MgdHJpbWVzdHJlcyBkZWwgYcOxbyBwcmVzZW50YW4gZGUgbWVkaWEgbcOhcyBhY3RpdmlkYWQgcXVlIGxvcyBwcmltZXJvcywgYWRlbcOhcywgcG9kZW1vcyB2ZXIgcXVlIGVsIGHDsW8gYW50ZXJpb3IgZnVlIHVuIGHDsW8gZGUgYmFqYSBhY3RpdmlkYWQgeSBxdWUgZXN0ZSBlc3TDoSBzaWVuZG8gdW5hIHJlY3VwZXJhY2nDs24sIHBhcnRpY3VsYXJtZW50ZSBwYXJhIGVsIHNlZ3VuZG8gY3VhdHJpbWVzdHJlLCBkb25kZSBlbiBlbCBhw7FvIGFudGVyaW9yIHNlIG9idHV2aWVyb24gZGF0b3MgY2F0YXN0csOzZmljb3MuDQoNCmBgYHtyLGZpZy5hbGlnbj0nY2VudGVyJyxvdXQuZXh0cmE9J2FuZ2xlPTkwJywgZWNobz1UUlVFfQ0KYWN0aXZpZGFkYj13aW5kb3coYWN0aXZpZGFkLHN0YXJ0PTIwMDIpDQpnZ3N1YnNlcmllc3Bsb3QoYWN0aXZpZGFkYikrDQogIHlsYWIoIkFjdGl2aWRhZCIpKw0KICB4bGFiKCIiKSsNCiAgZ2d0aXRsZSgiIikNCmBgYA0KDQotICAgRmluYWxtZW50ZSBlbiBlc3RlIGdyw6FmaWNvIHByZXRlbmRlbW9zIGJ1c2NhciBvYnNlcnZhY2lvbmVzIGFuw7NtYWxhcyBlbiBsb3MgYcOxb3MgY29tcGFyYW5kbyBsYSBhY3RpdmlkYWQgdG90YWwgZW50cmUgdG9kb3MgZWxsb3MuIEHDum4gdmllbmRvIHF1ZSBubyBoYXkgbmluZ3VuYSwgZWwgZ3LDoWZpY28gbm9zIGF5dWRhIGEgdmVyIHF1ZSBsYSBBY3RpdmlkYSB0aWVuZGUgYSBjcmVjZXIsIHB1ZXMgbG9zIHJlZ2lzdHJvcyBtw6FzIGFsdG9zIHNvbiAyMDIyLCAyMDA4LDIwMDkgeSAyMDE5LCBubyBlc3RhbmRvIDIwMjAgeSAyMDIxIHBvciBlbCBDb3ZpZC4NCg0KYGBge3IsZmlnLmFsaWduPSdjZW50ZXInLG91dC5leHRyYT0nYW5nbGU9OTAnLCBlY2hvPVRSVUV9DQpnZ3NlYXNvbnBsb3QoYWN0aXZpZGFkYiwNCiAgICAgICAgICAgICB5ZWFyLmxhYmVscz1UUlVFLA0KICAgICAgICAgICAgIHhsYWI9IiIsDQogICAgICAgICAgICAgeWxhYj0iQWN0aXZpZGFkIiwNCiAgICAgICAgICAgICBtYWluPSIiKQ0KICANCmBgYA0KDQojIyBDKSBSZWdyZXNpb25lcyBsb2NhbGVzDQoNCmBgYHtyLGZpZy5hbGlnbj0nY2VudGVyJyxvdXQuZXh0cmE9J2FuZ2xlPTkwJywgZWNobz1UUlVFfQ0KYWN0aXZpZGFkX3N0bD1zdGwoYWN0aXZpZGFkLA0KICAgICAgICAgICAgICAgICBzLndpbmRvdz0icGVyaW9kaWMiLA0KICAgICAgICAgICAgICAgICByb2J1c3Q9VFJVRSkNCmF1dG9wbG90KGFjdGl2aWRhZF9zdGwsDQogICAgICAgICB4bGFiPSIiLA0KICAgICAgICAgbWFpbj0iRGVzY29tcG9zaWNpw7NuIGRlIEFjdGl2aWRhZCBwb3IgcmVncmVzb3JlcyBsb2NhbGVzIHBvbmRlcmFkb3MiKQ0KYGBgDQoNCi0gICBPYnNlcnZhbW9zIGVuIGVzdGUgZ3LDoWZpY28gY29tbyBsYSB0ZW5kZW5jaWEgYWN0dWFsIGRlIGxhIEFjdGl2aWRhZCBlcyBtYW50ZW5lcnNlIGEgbml2ZWxlcyBtZWRpb3MuDQoNCiMjIEQpIE1lZGlhcyBtw7N2aWxlcw0KDQotICAgR3JhY2lhcyBhIGxhIHNpZ3VpZW50ZSBncsOhZmljYSBwb2RlbW9zIHN1YXZpemFyIGxhIHRlbmRlbmNpYSBxdWUgaGEgbGxldmFkbyBsYSBBY3RpdmlkYWQNCi0gICBFbiAyLU1hIGVzdGFtb3MgdG9tYW5kbyBsYSBtZWRpYSBkZSBkb3MgdHJpbWVzdHJlcywgZWwgYW50ZXJpb3IsIHkgZWwgYWN0dWFsLg0KLSAgIEVuIDQtTWEgZXN0YW1vcyB0b21hbmRvIGxhIG1lZGlhIGRlIHVuIGHDsW8sIHVuIHRyaW1lc3RyZSBhbnRlcmlvciwgZWwgYWN0dWFsIHkgZG9zIHBvc3RlcmlvcmVzLg0KLSAgIEVuIDJcKjQtTWEgZXN0YW1vcyB0b21hbmRvIGxhIG1lZGlhIGRlIGRvcyBhw7FvcywgZWwgYcOxbyBhbnRlcmlvciB5IGVsIGFjdHVhbC4NCg0KYGBge3IsZmlnLmFsaWduPSdjZW50ZXInLG91dC5leHRyYT0nYW5nbGU9OTAnLCBlY2hvPVRSVUUsIHdhcm5pbmc9RkFMU0V9DQphY3RpdmlkYWRfdHNpYmJsZT10c2liYmxlKA0KICB0aW1lPSBhcy5udW1lcmljKHRpbWUoYWN0aXZpZGFkKSksDQogIGFjdGl2aWRhZD1hcy5udW1lcmljKGRhdG9zMV90YXNhLmFjdGl2aWRhZCRUb3RhbCksDQogIGluZGV4PXRpbWUpfD4NCiAgbXV0YXRlKA0KICAgIGAyLU1hYCA9IHNsaWRlcjo6c2xpZGVfZGJsKGFjdGl2aWRhZCxtZWFuLCAuYmVmb3JlPTEsIC5hZnRlcj0wLCAuY29tcGxldGU9RkFMU0UpLA0KICAgIGA0LU1hYCA9IHNsaWRlcjo6c2xpZGVfZGJsKGFjdGl2aWRhZCxtZWFuLC5iZWZvcmU9MSwuYWZ0ZXI9MiwuY29tcGxldGU9RkFMU0UpLA0KICAgIGAyKjQtTWFgID0gc2xpZGVyOjpzbGlkZV9kYmwoYDQtTWFgLG1lYW4sLmJlZm9yZT0xLC5hZnRlcj0wLC5jb21wbGV0ZT1GQUxTRSksDQogICkNCnA9YWN0aXZpZGFkX3RzaWJibGUgfD4NCiAgZ2dwbG90KCkrDQogIGdlb21fbGluZShhZXMoeD10aW1lLHk9YWN0aXZpZGFkKSxjb2w9ImdyYXk0NSIsc2l6ZT0wLjU1KSsNCiAgZ2VvbV9saW5lKGFlcyh4PXRpbWUseT1gMi1NYWApLGNvbD0icmVkIixzaXplPTAuNjUpKw0KICBnZW9tX2xpbmUoYWVzKHg9dGltZSx5PWAyKjQtTWFgKSxjb2w9ImdyZWVuIixzaXplPTAuNjUpKw0KICB4bGFiKCJhw7FvIikrDQogIHNjYWxlX2NvbG9yX21hbnVhbCh2YWx1ZXM9YygiZ3JleTQ1IiwicmVkIiwiZ3JlZW4iKSxsYWJlbHM9YygiQSIsIkIiLCJDIikpDQpwICsgdGhlbWUobGVnZW5kLnBvc2l0aW9uPSJsZWZ0IikNCg0KYGBgDQoNCiMjIEUpIFByZWRpY2Npw7NuIGRlIGxhIFNlcmllIEFjdGl2aWRhZA0KDQotICAgUHJlZGljY2nDs24gZGUgbGEgc2VyaWUgYmFqbyBlbCBtw6l0b2RvIEluZ2VudW8gSUkgZW4gbG9zIHByw7N4aW1vcyBkb3MgYcOxb3MNCg0KYGBge3IsZmlnLmFsaWduPSdjZW50ZXInLG91dC5leHRyYT0nYW5nbGU9OTAnLCBlY2hvPVRSVUV9DQpkZXJpdmFBY3RpdmlkYWQ9cndmKGFjdGl2aWRhZCxoPTgsZHJpZnQ9VFJVRSkNCmtuaXRyOjprYWJsZShkZXJpdmFBY3RpdmlkYWQpDQoNCmF1dG9wbG90KGFjdGl2aWRhZCwNCiAgICAgICAgIHNlcmllcz0iQWN0aXZpZGFkIiwNCiAgICAgICAgIHhsYWI9IiIsDQogICAgICAgICB5bGFiPSJBY3RpdmlkYWQiLA0KICAgICAgICAgbWFpbj0iUHJlZGljY2nDs24gc2luIGVzdGFjaW9uYWxpZGFkIikrDQogIGF1dG9sYXllcihkZXJpdmFBY3RpdmlkYWQsc2VyaWVzPSJJbmdlbnVvIElJIiwgUEk9RkFMU0UpKw0KICBsYWJzKGNvbG91cj0iTcOpdG9kb3MiKSsNCiAgdGhlbWUobGVnZW5kLnBvc2l0aW9uPWMoMC4xLDAuOCkpDQoNCnNuYWl2ZS5BY3RpdmlkYWQ9c25haXZlKGFjdGl2aWRhZCxoPTgsbGV2ZWw9OTUpDQphdXRvcGxvdChzbmFpdmUuQWN0aXZpZGFkLA0KICAgICAgICAgeGxhYj0iIiwNCiAgICAgICAgIHlsYWI9IkFjdGl2aWRhZCIsDQogICAgICAgICBtYWluPSJQcmVkaWNjacOzbiBjb24gZXN0YWNpb25hbGlkYWQiLA0KICAgICAgICAgUEk9RkFMU0UpDQoNCg0KYGBgDQoNCi0gICBVdGlsaXphbW9zIGVsIG3DqXRvZG8gc2luIGVzdGFjaW9uYWxpZGFkIHBvcnF1ZSBjdWFuZG8gbG8gYW5hbGl6YW1vcyB2ZW1vcyBxdWUgZXN0ZSBvZnJlY2UgbWVqb3IgZXJyb3IgcG9yY2VudHVhbCAoTUFQRT0xLjM1NTczMyBmcmVudGUgYWwgZXN0YWNpb25hbCBNQVBFPTIuMDk0OTIyKSwgYWRlbcOhcywgZWwgaW50ZXJ2YWxvIGRlIGNvbmZpYW56YSBkZSBsYXMgcHJlZGljY2lvbmVzIGVzIG11Y2hvIG3DoXMgZmlhYmxlIChBQ0YxPS0wLjEwNTM4MTEgZnJlbnRlIGEgQUNGMT0wLjUwMzgxOTQpDQoNCmBgYHtyfQ0KZGF0b3NkZXJpdmE9YWNjdXJhY3koZGVyaXZhQWN0aXZpZGFkKQ0KZGF0b3NzbmFpdmU9YWNjdXJhY3koc25haXZlLkFjdGl2aWRhZCkNCg0KDQpjb21wYXJhY2lvbj1hcy5kYXRhLmZyYW1lKHJiaW5kKGRhdG9zZGVyaXZhLGRhdG9zc25haXZlKSkNCnJvd25hbWVzKGNvbXBhcmFjaW9uKT1jKCJTaW4gZXN0YWNpb25hbGlkYWQiLCJDb24gZXN0YWNpb25hbGlkYWQiKQ0KY29tcGFyYWNpb24NCmBgYA0KDQojIEVqZXJjaWNpbyAyOiBTZXJpcmUgVGFzYSBQYXJvDQoNCiMjIEEpIEdyYWZpY2FyIHkgY29tZW50YXIgbGEgc2VyaWUgeSBzdSBlc3F1ZW1hDQoNCmBgYHtyLGZpZy5hbGlnbj0nY2VudGVyJyxvdXQuZXh0cmE9J2FuZ2xlPTkwJywgZWNobz1UUlVFfQ0KcGFybz10cyhhcy5udW1lcmljKGRhdG9zMV90YXNhLnBhcm9bLCJUb3RhbCJdKSwNCiAgICAgICAgICAgICAgIHN0YXJ0PWMoMjAwMiwxKSwNCiAgICAgICAgICAgICAgIGZyZXF1ZW5jeT00KQ0KYXV0b3Bsb3QocGFybywNCiAgICAgICAgIHhsYWI9IiIsDQogICAgICAgICB5bGFiPSJQYXJvIiwNCiAgICAgICAgIG1haW49IlBhcm8gdHJpbWVzdHJhbCIpDQpgYGANCg0KLSAgIFBvZGVtb3Mgb2JzZXJ2YXIgZGUgdW5hIGZvcm1hIGNsYXJhLCBjb21vIGVsIHBhcm8gdGVuw61hIHN1IG5pdmVsIG3DoXMgYmFqbyBlbiAyMDA1LCBkb25kZSBlbCBlbXBsZW8gZXJhIHBsZW5vIHkgZXN0ZSBhdW1lbnRhIGVuIDIwMDcgYSByYcOteiBkZSBsYSBjcmlzaXMgZWNvbsOzbWljYSBkZSBlc2UgcGVyaW9kby4gVnVlbHZlIGEgYmFqYXIgZGVzZGUgMjAxMyBoYXN0YSAyMDE5IGN1YW5kbyBwb3IgbGEgQ292aWQtMTkgdnVlbHZlIGEgYXVtZW50YXIuIEFjdHVhbG1lbnRlIHkgZGVzZGUgbGEgcGFuZGVtaWEsIGVsIHBhcm8gZXN0w6EgYmFqYW5kby4NCg0KYGBge3IsZmlnLmFsaWduPSdjZW50ZXInLG91dC5leHRyYT0nYW5nbGU9OTAnLCBlY2hvPVRSVUV9DQpwYXJvQW51YWw9YWdncmVnYXRlKHBhcm8sRlVOPXN1bSkNCm1pbj1taW4ocGFyb0FudWFsKQ0KbWF4PW1heChwYXJvQW51YWwpDQphdXRvcGxvdChwYXJvQW51YWwsDQogICAgICAgICB4bGFiPSIiLA0KICAgICAgICAgeWxhYj0iUGFybyIsDQogICAgICAgICBtYWluPSJQYXJvIGFudWFsIikgKw0KICAgICAgICAgZ2VvbV9obGluZSh5aW50ZXJjZXB0ID0gYyhtYXgsbWluKSxsaW5ldHlwZT0iZGFzaGVkIixjb2xvcj0icmVkIikrDQogICAgICAgICBnZW9tX3ZsaW5lKHhpbnRlcmNlcHQ9YygyMDA3LDIwMTkpLGxpbmV0eXBlPSJkYXNoZWQiLGNvbG9yPSJyZWQiKQ0KYGBgDQoNCi0gICBFbiBlc3RlIGdyw6FmaWNvIHZlbW9zIHF1ZSBzZSBwcmVzZW50YSB1biBlc3F1ZW1hICoqYWRpdGl2byoqIHBvciBsYSBhbGVhdG9yaWVkYWQgZGUgbG9zIHB1bnRvcyBlbiBlbCBncsOhZmljbw0KDQpgYGB7cixmaWcuYWxpZ249J2NlbnRlcicsb3V0LmV4dHJhPSdhbmdsZT05MCcsIGVjaG89VFJVRX0NCnBhcm9EZXN2PWFnZ3JlZ2F0ZShwYXJvLEZVTj1zZCkNCnBsb3QoYXMubnVtZXJpYyhwYXJvQW51YWwpLGFzLm51bWVyaWMocGFyb0Rlc3YpKQ0KDQpgYGANCg0KIyMgQikgUHJlZGljY2nDs24gZGUgaW5nZW51byBjb24gZXN0YWNpb25hbGlkYWQgcGFyYSBsb3MgcHLDs3hpbW9zIGRvcyBhw7Fvcw0KDQpgYGB7cixmaWcuYWxpZ249J2NlbnRlcicsb3V0LmV4dHJhPSdhbmdsZT05MCcsIGVjaG89VFJVRX0NCnNuYWl2ZS5QYXJvPXNuYWl2ZShwYXJvLGg9OCxsZXZlbD05NSkNCmF1dG9wbG90KHNuYWl2ZS5QYXJvLA0KICAgICAgICAgeGxhYj0iIiwNCiAgICAgICAgIHlsYWI9IlBhcm8iLA0KICAgICAgICAgbWFpbj0iUHJlZGljY2nDs24gY29uIGVzdGFjaW9uYWxpZGFkIiwNCiAgICAgICAgIFBJPUZBTFNFKQ0Ka25pdHI6OmthYmxlKHNuYWl2ZS5QYXJvKQ0KYGBgDQoNCiMjIEMpIEFwbGljYWNpw7NuIGRlbCBtw6l0b2RvIEhvbHQtV2ludGVycw0KDQotICAgT2ZyZWNlbW9zIGxhIHZpc3RhIGRlIGFtYmFzIHBvc2liaWxpZGFkZXMgcGFyYSBtb3N0cmFyIGxhIHNpbWlsaXR1ZCBlbnRyZSBhbWJvcyBtb2RlbG9zLCBwZXJvIG5vcyBkZWJlbW9zIHF1ZWRhciBjb24gZWwgZXNxdWVtYSAqKmFkaXRpdm8qKiwgcHVlcyBlbiBsYSBncmFmaWNhY2nDs24gZGUgcHVudG9zIG5vIG9ic2VydmFtb3MgbmluZ3VuYSB0ZW5kZW5jaWEgY3JlY2llbnRlLg0KDQpgYGB7cn0NCiMgQWRpdGl2bw0KcGFyb3ByZWQgPC0gd2luZG93KHBhcm8sIHN0YXJ0ID0gYygyMDAyLDEpLCBlbmQgPSBjKDIwMjIsIDQpKSANCg0KcGFyb2V0cyA8LSBldHMocGFyb3ByZWQsIA0KICAgICAgICAgICAgICAgICAgICAgICBtb2RlbCA9ICJBQUEiLCANCiAgICAgICAgICAgICAgICAgICAgICAgZGFtcGVkID0gRkFMU0UpDQphZGl0aXZvPWRhdGEuZnJhbWUoYWxwaGE9cGFyb2V0cyRwYXJbImFscGhhIl0sYmV0YT1wYXJvZXRzJHBhclsiYmV0YSJdLGdhbW1hPXBhcm9ldHMkcGFyWyJnYW1tYSJdLHJvdy5uYW1lcz1jKCJBZGl0aXZvIikpDQojIE11bHRpcGxpY2F0aXZvDQpwYXJvYiA8LSB3aW5kb3cocGFybywgc3RhcnQgPSAyMDAyKQ0KcGFyb2JFdHMgPC0gZXRzKHBhcm9iLCANCiAgICAgICAgICAgICAgICAgICAgICAgbW9kZWwgPSAiTUFNIiwgDQogICAgICAgICAgICAgICAgICAgICAgIGRhbXBlZCA9IEZBTFNFKQ0KbXVsdGlwbGljYXRpdm89ZGF0YS5mcmFtZShhbHBoYT1wYXJvYkV0cyRwYXJbImFscGhhIl0sYmV0YT1wYXJvYkV0cyRwYXJbImJldGEiXSxnYW1tYT1wYXJvYkV0cyRwYXJbImdhbW1hIl0scm93Lm5hbWVzPWMoIk11bHRpcGxpY2F0aXZvIikpDQpyYmluZChhZGl0aXZvLG11bHRpcGxpY2F0aXZvKQ0KYGBgDQoNCi0gICBMb3MgdmFsb3JlcyBiYWpvcyBlbiBCZXRhICgwLjEwMykgeSBHYW1tYSAoMC4wMDAxKSBub3MgaW5kaWNhIHF1ZSBsYSBwZW5kaWVudGUgeSBsYSBlc3RhY2lvbmFsaWRhZCBzZSBtb2RpZmljYW4gbXV5IGxlbnRhbWVudGUuIFNpbiBlbWJhcmdvLCBlbCBBbGZhIHRhbiBncmFuZGUgKDAuOTc4OTMyNCkgaW5kaWNhIHF1ZSBsYSBzZXJpZSB2YXLDrWEgY29uc3RhbnRlbWVudGUgZW4gZWwgdGllbXBvDQoNCiMjIEQpIEFuYWxpemFyIGxvcyBlcnJvcmVzIGRlbCBtb2RlbG8gQWRpdGl2bw0KDQotICAgSWRlbnRpZmljYW1vcyBwZXJpb2RvcyBjcnVjaWFsZXMgY29tbyBwdWVkZSBzZXIgMjAwOSwgZG9uZGUgZWwgcGFybyBhc2NlbmRpw7MgZGUgZm9ybWEgZGVzb3JiaXRhZGEsIGFsIGlndWFsIHF1ZSBlbiBlbCBwcmltZXIgdHJpbWVzdHJlIGRlIDIwMjAsIGNvbiBlbCBjb3JvbmF2aXJ1cy4NCg0KYGBge3IsZmlnLmFsaWduPSdjZW50ZXInLG91dC5leHRyYT0nYW5nbGU9OTAnLCBlY2hvPVRSVUUsIHdhcm5pbmc9RkFMU0UsIG1lc3NhZ2U9RkFMU0V9DQplcnJvcj1yZXNpZHVhbHMocGFyb2V0cykNCnNkZXJyb3I9c2QoZXJyb3IpDQoNCmF1dG9wbG90KGVycm9yLHNlcmllcz0iRXJyb3IiLA0KICAgICAgICAgY29sb3VyPSJibGFjayIsDQogICAgICAgICB4bGFiPSJTZW1hbmEiLA0KICAgICAgICAgeWxhYj0iRXJyb3IiLA0KICAgICAgICAgbWFpbj0iIikrDQogIGdlb21faGxpbmUoeWludGVyY2VwdD0gYygtMywtMiwyLDMpKnNkZXJyb3IsDQogICAgICAgICAgICAgY29sb3VyPWMoInJlZCIsImJsdWUiLCJibHVlIiwicmVkIiksbHR5PTIpKw0KICBzY2FsZV94X2NvbnRpbnVvdXMoYnJlYWtzPXNlcSg2LDI2LDIpKSsNCiAgZ2VvbV92bGluZSh4aW50ZXJjZXB0PWMoMjAwOSwyMDIwLjI1KSxsaW5ldHlwZT0iZGFzaGVkIixjb2xvcj0icmVkIikNCg0KDQoNCmBgYA0KDQojIyBFKSBQcmVkaWNjacOzbiBtw6l0b2RvIEhvbHQtV2ludGVycyBhZGl0aXZvDQoNCmBgYHtyLGZpZy5hbGlnbj0nY2VudGVyJyxvdXQuZXh0cmE9J2FuZ2xlPTkwJywgZWNobz1UUlVFLCB3YXJuaW5nPUZBTFNFfQ0KcGFyb2RmPWZvcmVjYXN0KHBhcm9ldHMsDQogICAgICAgICAgICAgICAgaD04LA0KICAgICAgICAgICAgICAgIGxldmVsPTk1KQ0Ka25pdHI6OmthYmxlKHBhcm9kZikNCg0KYXV0b3Bsb3QocGFybywNCiAgICAgICAgIHhsYWI9IiIsDQogICAgICAgICB5bGFiPSJQYXJvIiwNCiAgICAgICAgIG1haW49IlByZWRpY2Npw7NuIHBhcm8gSG9sdC1XaW50ZXJzIGFkaXRpdm8iLA0KICAgICAgICAgY29sb3VyPSJibGFjayIsDQogICAgICAgICBQST1GQUxTRSkrDQogIGF1dG9sYXllcihwYXJvZGYsc2VyaWVzPSJIb2x0LVdpbnRlcnMgYWRpdGl2byIsUEk9RkFMU0UpKw0KICBhdXRvbGF5ZXIoc25haXZlLlBhcm8sc2VyaWVzPSJJbmdlbnVvIGNvbiBFc3RhY2lvbmFsaWRhZCIsIFBJPUZBTFNFKSsNCiAgbGFicyhjb2xvdXI9Ik3DqXRvZG9zIikrDQogIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbj1jKDAuMSwwLjgpKQ0KDQoNCg0KYGBgDQoNCi0gICBPYnNlcnZhbW9zIHF1ZSBsYSBzZXJpZSBwcmVkZWNpZGEgY29uIEluZ2VudW8gY29uIHRlbmRlbmNpYSBubyB0aWVuZSB1biBpbnRlcnZhbG8gZGUgY29uZmlhbnphIGRlIGxhcyBwcmVkaWNjaW9uZXMgZmlhYmxlIChBQ0YxPTAuODA0OTE4NzUxKS4gQWRlbcOhcywgdGllbmUgdW4gZXJyb3IgcG9yY2VudHVhbCBtZW5vciBsYSBwcmVkaWNjacOzbiBIb2x0LVdpbnRlcnMgKE1BUEU9Ni43MDI1OTIlKS4NCg0KYGBge3J9DQpkYXRvc0hvbHQ9YWNjdXJhY3kocGFyb2RmKQ0KZGF0b3NzbmFpdmVQYXJvPWFjY3VyYWN5KHNuYWl2ZS5QYXJvKQ0KDQoNCmNvbXBhcmFjaW9uPWFzLmRhdGEuZnJhbWUocmJpbmQoZGF0b3NIb2x0LGRhdG9zc25haXZlUGFybykpDQpyb3duYW1lcyhjb21wYXJhY2lvbik9YygiSG9sdC1XaW50ZXJzIEFkaXRpdm8iLCJJbmdlbnVvIGNvbiBFc3RhY2lvbmFsaWRhZCIpDQpjb21wYXJhY2lvbg0KYGBgDQoNCiMjIEYpIFZhbGlkYWNpw7NuIFRyYWluaW5nIFNldA0KDQotICAgSGVtb3MgcmVhbGl6YWRvIGxhIHByZWRpY2Npw7NuIGRlbCBtb2RlbG8gcGFyYSBIb2x0LVdpbnRlcnMgQWRpdGl2byB5IHBhcmEgSW5nZW51byBjb24gRXN0YWNpb25hbGlkYWQNCi0gICBBdW5xdWUgYW1ib3MgdGVuZ2FuIHVuIGVycm9yIGFsdG8sIGVsIHF1ZSBtZWpvciBzZSBhZGVjdWEgZXMgSG9sdC1XaW50ZXJzIEFkaXRpdm8gKE1BUEU9Ni41OTU5MDMlKSwNCg0KYGBge3IsZmlnLmFsaWduPSdjZW50ZXInLG91dC5leHRyYT0nYW5nbGU9OTAnLCBlY2hvPVRSVUUsIHdhcm5pbmc9RkFMU0V9DQojRWxpbWluYW1vcyBsb3Mgw7psdGltb3MgMTQgdHJpbWVzdHJlcw0KcGFyb0ludHJhPXN1YnNldChwYXJvLGVuZD1sZW5ndGgocGFybyktMTQpDQojIGFkZGl0aXZlIHByZWRpY2Npw7NuIHBhcmEgZXNvcyAxNCB0cmltZXN0cmVzIHBhcmEgSG9sdC1XaW50ZXJzIGFkaXRpdm8NCnBhcm9JbnRyYUV0cz1ldHMocGFyb0ludHJhLA0KICAgICAgICAgICAgICAgICAgICBtb2RlPSJBQUEiLA0KICAgICAgICAgICAgICAgICAgICBkYW1wZWQ9RkFMU0UpDQpwYXJvRXRzRXh0cmFQcmU9Zm9yZWNhc3QocGFyb0ludHJhRXRzLA0KICAgICAgICAgICAgICAgICAgICAgICAgIGg9MTQsDQogICAgICAgICAgICAgICAgICAgICAgICAgbGV2ZWw9OTUpDQojIEhhY2Vtb3MgcHJlZGljY2nDs24gcGFyYSBJbmdlbnVvIGNvbiBFc3RhY2lvbmFsaWRhZA0Kc25haXZlLnBhcm9JbnRyYT1zbmFpdmUocGFyb0ludHJhLGg9MTQsbGV2ZWw9OTUpDQoNCiNJbmZvcm1hY2nDs24NCmluZm9ybWFjaW9ubW9kZWxvcz1kYXRhLmZyYW1lKHJiaW5kKGFjY3VyYWN5KHNuYWl2ZS5wYXJvSW50cmEpLGFjY3VyYWN5KHBhcm9FdHNFeHRyYVByZSkpKQ0Kcm93bmFtZXMoaW5mb3JtYWNpb25tb2RlbG9zKT1jKCJJbmdlbnVvIElJIEVzdGFjaW9uYWxpZGFkIiwiSG9sdC1XaW50ZXJzIEFkaXRpdm8iKQ0KaW5mb3JtYWNpb25tb2RlbG9zDQojR3LDoWZpY2ENCmF1dG9wbG90KHBhcm8sIHNlcmllcz0iUGFybyIsbGluZXR5cGU9InNvbGlkIikrDQogIGF1dG9sYXllcihwYXJvSW50cmEsc2VyaWVzPSJSZWR1Y2lkYSIsbGluZXR5cGU9ImRhc2hlZCIpKw0KICBhdXRvbGF5ZXIocGFyb0V0c0V4dHJhUHJlLHNlcmllcz0iSG9sdC1XaW50ZXIgQWRpdGl2byIsbGluZXR5cGU9InNvbGlkIixQST1GKSsNCiAgYXV0b2xheWVyKHNuYWl2ZS5wYXJvSW50cmEsc2VyaWVzPSJJbmdlbnVvIEVzdGFjaW9uYWxpZGFkIixsaW5ldHlwZT0ic29saWQiLFBJPUYpDQpgYGANCg0KIyBFamVyY2ljaW8gMzogU2VyaWUgQ29uc3VtbyBkZSBBbGltZW50b3MgcG9yIEPDoXBpdGENCg0KIyMgQSkgQW7DoWxpc2lzIELDoXNpY28NCg0KLSAgIExhIHNlcmllIHRlcm1pbmEgZW4gZWwgYcOxbyAyMDIxDQotICAgTGxhbWEgbXVjaG8gbGEgYXRlbmNpw7NuIDIwMTkgeSAyMDIwLCBkb25kZSBwb3IgZWwgQ29yb25hdmlydXMgc2UgcHJvZHVjZSBlc3RlIG1vdmltaWVudG8gdGFuIGV4dHJhw7FvIGVuIGxhIHNlcmllLCBkb25kZSBwYXNhbW9zIGRlIDYwNi42NCBhIDY3MS44MiBlbiB0YW4gc29sbyB1biBhw7FvDQotICAgQ29tbyBzZSBjb21lbnRhIGVuIGVsIGFwYXJ0YWRvIGFudGVyaW9yLCBsYSBleHBsaWNhY2nDs24gZXMgbGEgQ292aWQtMTkNCg0KYGBge3J9DQpjb25zdW1vPXJlYWQuY3N2MigiYWxpbWVudGFjaW9ucGMuY3N2IikNCmNvbnN1bW89dHMoY29uc3VtbywNCiAgICAgICAgICAgICAgIHN0YXJ0PWMoMTk4NyksDQogICAgICAgICAgICAgICBmcmVxdWVuY3k9MSkNCg0KDQpgYGANCg0KIyMgQikgR3JhZmljYXIgbGFzIGNvbXBvbmVudGVzIGRlIGxhIHNlcmllDQoNCi0gICBFbiBlc3RlIGdyw6FmaWNvIHZlbW9zIGxvcyBjb3J0ZXMgcXVlIHByZXNlbnRhIGVsIGdyw6FmaWNvIGVuIHN1cyBtb21lbnRvcyBtw6FzIGNydWNpYWxlcyAoMTk5MyAtIDE5OTUpIHVuYSBiYWphZGEgZHLDoXN0aWNhIGVuIGVsIENvbnN1bW8gZGUgQWxpbWVudG9zIHBvciBDw6FwaXRhIHkgKDIwMTkgLSAyMDIwKSB1bmEgZHLDoXN0aWNhIHN1YmlkYQ0KDQpgYGB7cixmaWcuYWxpZ249J2NlbnRlcicsb3V0LmV4dHJhPSdhbmdsZT05MCcsIGVjaG89VFJVRSwgd2FybmluZz1GQUxTRX0NCmF1dG9wbG90KGNvbnN1bW8sDQogICAgICAgICB4bGFiPSIiLA0KICAgICAgICAgeWxhYj0iQ29uc3VtbyBkZSBBbGltZW50b3MgcG9yIEPDoXBpdGEiLA0KICAgICAgICAgbWFpbj0iQ29uc3VtbyBhbnVhbCIpKw0KICBnZW9tX3ZsaW5lKHhpbnRlcmNlcHQ9YygyMDE5LDIwMjAsMTk5MywxOTk1KSxsaW5ldHlwZT0iZGFzaGVkIixjb2xvcj0icmVkIikNCmBgYA0KDQotICAgQ29uIGVzdGUgZ3LDoWZpY28gdmVtb3MgY29tbyBjbGFyYW1lbnRlIGVzdGFtb3MgYW50ZSB1biBlc3F1ZW1hIG11bHRpcGxpY2F0aXZvLCBwdWVzIGxvcyBwdW50b3MgZGlzcGVyc29zIGVuIGVsIGdyw6FmaWNvIG11ZXN0cmFuIHVuYSB0ZW5kZW5jaWEgY3JlY2llbnRlDQotICAgQWwgdHJhdGFyc2UgZGUgdW5hIHNlcmllIGFudWFsLCBubyBwb2RyZW1vcyBlc3R1ZGlhciBsYSBlc3RhY2lvbmFsaWRhZCBkZSBsYSBzZXJpZQ0KDQpgYGB7cixmaWcuYWxpZ249J2NlbnRlcicsb3V0LmV4dHJhPSdhbmdsZT05MCcsIGVjaG89VFJVRSwgd2FybmluZz1GQUxTRX0NCmNvbnN1bW9EZXN2PWFnZ3JlZ2F0ZShjb25zdW1vLEZVTj1zZCkNCnBsb3QoYXMubnVtZXJpYyhjb25zdW1vKSxhcy5udW1lcmljKGNvbnN1bW9EZXN2KSkNCg0KYGBgDQoNCiMjIEMpIEVzdGFjaW9uYXJpZWRhZCBkZSBsYSBTZXJpZQ0KDQotICAgTGEgZXN0YWNpb25hcmllZGFkIGhhY2UgcmVmZXJlbmNpYSBhIGxhIGNhcmFjdGVyw61zdGljYSBkZSB1bmEgc2VyaWUgcXVlIGxhIGhhY2UgZXN0YWNpb25hcmlhIGVuIGVsIHRpZW1wbywgZXMgZGVjaXIsIHF1ZSBlbCB0cmFuc2N1cnNvIGRlbCB0aWVtcG8gbm8gaW5mbHV5ZSBlbiBsYSB2YXJpYWNpw7NuIGRlIGxvcyB2YWxvcmVzIGRlIGxhIG1pc21hLg0KLSAgIFNlZ8O6biBsYSBkZWZpbmljacOzbiBhbnRlcmlvciBwb2RlbW9zIGRlY2lyIHF1ZSBsYSBzZXJpZSBxdWUgZXN0YW1vcyB0cmFiYWphbmRvICoqbm8gZXMgZXN0YWNpb25hcmlhKiosIHB1ZXMgcG9kZW1vcyB2ZXIgY29tbyBlbCBwYXNvIGRlbCB0aWVtcG8gc2kgaW5mbHV5ZSBlbiBlbCBhdW1lbnRvIG8gbGEgcmVkdWNjacOzbiBkZSBsb3MgdmFsb3JlcyBkZSBjb25zdW1vLg0KDQojIyBEKSBQcmVkaWNjacOzbiBkZSB2YWxvcmVzIGNvbiBtw6l0b2RvIGRlIGxhIG1lZGlhDQoNCmBgYHtyLGZpZy5hbGlnbj0nY2VudGVyJyxvdXQuZXh0cmE9J2FuZ2xlPTkwJywgZWNobz1UUlVFLCB3YXJuaW5nPUZBTFNFfQ0KbWVkaWFDb25zdW1vPW1lYW5mKGNvbnN1bW8saD0yKQ0KYXV0b3Bsb3QoY29uc3VtbywNCiAgICAgICAgc2VpcmVzPSJDb25zdW1vIEFudWFsIHBlciBDw6FwaXRhIiwNCiAgICAgICAgeGxhYj0iIiwNCiAgICAgICAgeWxhYj0iQ29uc3VtbyIsDQogICAgICAgIG1haW49IkNvbnN1bW8gQW51YWwgZGUgQWxpbWVudG9zIHBlciBDw6FwaXRhIikrDQogIGF1dG9sYXllcihtZWRpYUNvbnN1bW8sc2VyaWVzPSJNZWRpYSIsUEk9RkFMU0UpKw0KICBsYWJzKGNvbG91cj0iTcOpdG9kb3MiKSsNCiAgdGhlbWUobGVnZW5kLnBvc2l0aW9uPWMoMCwwLjgpKQ0KDQpgYGANCg0KLSAgIFBvZGVtb3MgcmVtYXJjYXIgcXVlIGxhcyBwcmVkaWNjaW9uZXMgbm8gcHJlc2VudGFuIHVuIGludGVydmFsbyBkZSBjb25maWFuemEgbXV5IGZpYWJsZSAoQUNGMT0wLjI5MjAzMikuDQoNCmBgYHtyfQ0KaW5mbz1hcy5kYXRhLmZyYW1lKGFjY3VyYWN5KG1lZGlhQ29uc3VtbykpDQpyb3duYW1lcyhpbmZvKT0iTcOpdG9kbyBkZSBsYSBtZWRpYSINCmluZm8NCmBgYA0KDQojIyBFKSBBbGlzYWRvIEV4cG9uZW5jaWFsIFNpbXBsZQ0KDQotICAgVmVtb3MgcXVlIGFscGhhIGVzIGlndWFsIGEgMC4zNzQyLCBwb3IgbG8gcXVlIGxhIHNlcmllIG5vIHZhcsOtYSBtdXkgZHLDoXN0aWNhbWVudGUgY29uIGVsIHRpZW1wbywgYWRlbcOhcywgZWwgZXJyb3IgcG9yY2VudHVhbCAoTUFQRT0xLjkzMDMwOCkgZXMgcmVhbG1lbnRlIGJham8gaW5kaWNhbmRvIGFzw60gdW4gYWp1c3RlIGJ1ZW5vDQoNCi0gICBBZGVtw6FzLCAkeV97dCsyfT15X3t0KzF9PWxfdD02MzMuMDU1JA0KDQotICAgJGxfe3R9PVxhbHBoYSp5X3QrKDEtXGFscGhhKWxfe3QtMX0kDQoNCmBgYHtyfQ0KY29uc3Vtby5hbGlzYWRvPXdpbmRvdyhjb25zdW1vLHN0YXJ0PWMoMTk4NyksZW5kPWMoMjAyMSkpDQpjb25zdW1vRXRzPWV0cyhjb25zdW1vLmFsaXNhZG8sbW9kZWw9IkFOTiIpDQphbHBoYT1jb25zdW1vRXRzJHBhclsiYWxwaGEiXQ0KbHQ9Y29uc3Vtb0V0cyRzdGF0ZXNbMzYsXQ0KaW5mbzI9ZGF0YS5mcmFtZShBbHBoYT1hbHBoYSxMdD1sdCwiTHQtMSI9NjUzLjkwMDUpDQpyb3duYW1lcyhpbmZvMik9IkRhdG9zIg0KaW5mbzINCmBgYA0KDQojIyBGKSBBbGlzYWRvIEV4cG9uZW5jaWFsIFNpbXBsZQ0KLSAgIEFscGhhIGVzIHVuIHZhbG9yIGVudHJlIDAgeSAxLCBzaWduaWZpY2FuZG8gbG8gc2lndWllbnRlIHBhcmEgY2FkYSBjYXNvOg0KLSAgIFNpIEFscGhhPTAsIGxhIHNlcmllIHBlcm1hbmVjZSBjb25zdGFudGUgZW4gZWwgdGllbXBvDQotICAgU2kgQWxwaGE9MSwgbGEgc2VyaWUgdmFyw61hIGNvbnN0YW50ZW1lbnRlIGVuIGVsIHRpZW1wbw0KLSAgIEVuIG51ZXN0cm8gY2FzbywgY29tbyBzZSBoYSBjb21lbnRhZG8gYW50ZXMsIGFscGhhPTAuMzc0MiwgcG9yIGxvIHF1ZSBwb2RlbW9zIGRlY2lyIHF1ZSBzZSBtYW50aWVuZSBtZWRpYW5hbWVudGUgY29uc3RhbnRlIGVuIGVsIHRpZW1wby4gDQoNCmBgYHtyLGZpZy5hbGlnbj0nY2VudGVyJyxvdXQuZXh0cmE9J2FuZ2xlPTkwJywgZWNobz1UUlVFLCB3YXJuaW5nPUZBTFNFfQ0KY29uc3Vtb2RmPWZvcmVjYXN0KGNvbnN1bW9FdHMsaD0yLGxldmVsPTk1KQ0KYXV0b3Bsb3QoY29uc3Vtb2RmLHhsYWI9IiIseWxhYj0iQ29uc3VtbyBwZXIgY8OhcGl0YSIsbWFpbj0iUHJlZGljY2nDs24gZGVsIGNvbnN1bW8gcGVyIGPDoXBpdGEiKQ0KYGBgDQoNCiMjIEcpIENvbXBhcmFjacOzbiBkZSBhbWJhcyBwcmVkaWNjaW9uZXMNCg0KLSAgIEdyw6FmaWNhbWVudGUgcG9kZW1vcyB2ZXIgY29tbyBhbWJhcyBwcmVkaWNjaW9uZXMgc29uIGJhc3RhbnRlIHNpbWlsYXJlcw0KDQpgYGB7cixmaWcuYWxpZ249J2NlbnRlcicsb3V0LmV4dHJhPSdhbmdsZT05MCcsIGVjaG89VFJVRSwgd2FybmluZz1GQUxTRX0NCmF1dG9wbG90KGNvbnN1bW8sc2VyaWVzPSJDb25zdW1vIikrDQogIGF1dG9sYXllcihtZWRpYUNvbnN1bW8sc2VyaWVzPSJQcmVkaWNjacOzbiBNZWRpYSIsUEk9RikrDQogIGF1dG9sYXllcihjb25zdW1vZGYsc2VyaWVzPSJBbGlzYWRvIEV4cG9uZW5jaWFsIFNpbXBsZSIsUEk9RikNCg0KYGBgDQoNCi0gICBTaSB2ZW1vcyBsb3MgZGF0b3MgbcOhcyB0w6ljbmljb3MsIHZlbW9zIGNvbW8gYXVucXVlIGFtYm9zIHNvbiBwYXJlY2lkb3MgZW4gY2llcnRvIHNlbnRpZG8sIHBvZGVtb3MgZGVjaXIgcXVlIGVsIEFsaXNhZG8gRXhwb25lbmNpYWwgU2ltcGxlIHRpZW5lIHVuIGVycm9yIG1lbm9yIChBQ0YxPTAuMDg1MyUpLCB0ZW5pZW5kbyBhc8OtIHVuIGludGVydmFsbyBkZSBjb25maWFuemEgYWNlcHRhYmxlLiANCg0KYGBge3J9DQppbmZvY29tcGFyYWNpb249ZGF0YS5mcmFtZShyYmluZChhY2N1cmFjeShtZWRpYUNvbnN1bW8pLGFjY3VyYWN5KGNvbnN1bW9kZikpKQ0Kcm93bmFtZXMoaW5mb2NvbXBhcmFjaW9uKT1jKCJNw6l0b2RvIE1lZGlhcyIsIkFsaXNhZG8gRXhwb25lbmNpYWwgU2ltcGxlIikNCmBgYA0KDQojIEVqZXJjaWNpbyA0OiBTZXJpZSBQb2JsYWNpw7NuIGRlIE5pw7Fvcw0KIyMgRGF0b3MNCmBgYHtyfQ0KcG9ibGFjaW9uPXJlYWQuY3N2MigiUG9ibGFjacOzbiBlbiBtaWxlcyBkZSBwZXJzb25hcy5jc3YiKQ0KcG9ibGFjaW9uPXBvYmxhY2lvbiU+JWZpbHRlcihFZGFkPT0iRGUgMCBhIDQgYW5vcyIpDQpwb2JsYWNpb249cG9ibGFjaW9uJT4lZmlsdGVyKFVuaWRhZD09IlZhbG9yIGFic29sdXRvIikNCnBvYmxhY2lvbj1wb2JsYWNpb24lPiVmaWx0ZXIoU2V4bz09IkFtYm9zIHNleG9zIikNCnBvYmxhY2lvbj1hcy5kYXRhLmZyYW1lKGFwcGx5KHBvYmxhY2lvbiwyLHJldikpDQpoZWFkKHBvYmxhY2lvbikNCnBvYmxhY2lvbj10cyhhcy5udW1lcmljKHBvYmxhY2lvblssIlRvdGFsIl0pLA0KICAgICAgICAgICAgIHN0YXJ0PWMoMjAwMiwxKSwNCiAgICAgICAgICAgICBmcmVxdWVuY3k9NCkNCg0KYGBgDQojIyBBKSBHcmFmaWNhciB5IGNvbWVudGFyIGFuw6FsaXNpcyBiw6FzaWNvDQotICAgVmVtb3MgY29tbyBlbCBwdW50byBtw6FzIGFsdG8gZGUgbmnDsW9zIHNlIGVuY3VlbnRyYSBlbiBlbCDDumx0aW1vIGN1YXRyaW1lc3RyZSBkZSAyMDEwLCBwdW50byBhIHBhcnRpciBkZWwgY3VhbCBsYSBjaWZyYSBjb21pZW56YSBhIGJhamFyIGNvbnNpZGVyYWJsZW1lbnRlLCB0ZW5pZW5kbyB1biBwYXLDs24gZW4gbGEgYmFqYWRhIGVuIGVsIHRlcmNlciBjdWF0cmltZXN0cmUgZGUgMjAxOC4NCg0KLSAgIEVzdGUgcGFyw7NuIGVuIGxhIGJhamFkYSBubyBlcyBzaXF1aWVyYSBub3RhYmxlIHNpIG9ic2VydmFtb3MgbGEgc2VyaWUgZGUgZm9ybWEgYW51YWwNCmBgYHtyLGZpZy5hbGlnbj0nY2VudGVyJyxvdXQuZXh0cmE9J2FuZ2xlPTkwJywgZWNobz1UUlVFLCB3YXJuaW5nPUZBTFNFfQ0KYXV0b3Bsb3QocG9ibGFjaW9uLA0KICAgICAgICAgeGxhYj0iIiwNCiAgICAgICAgIHlsYWI9IlBvYmxhY2lvbiIsDQogICAgICAgICBtYWluPSJQb2JsYWNpb24iKSsNCiAgZ2VvbV92bGluZSh4aW50ZXJjZXB0PWMoMjAxMC43NSwyMDE4LjUpLGxpbmV0eXBlPSJkYXNoZWQiLGNvbG9yPSJyZWQiKSsNCiAgZ2VvbV9obGluZSh5aW50ZXJjZXB0PWMoYXMubnVtZXJpYyhtYXgocG9ibGFjaW9uKSksMjA4NC4xKSxjb2xvcj0iYmx1ZSIsbGluZXR5cGU9ImRhc2hlZCIpDQpgYGANCg0KDQpgYGB7cixmaWcuYWxpZ249J2NlbnRlcicsb3V0LmV4dHJhPSdhbmdsZT05MCcsIGVjaG89VFJVRSwgd2FybmluZz1GQUxTRX0NCnBvYmxhY2lvbkFudWFsPWFnZ3JlZ2F0ZShwb2JsYWNpb24sIEZVTiA9IHN1bSkNCmF1dG9wbG90KHBvYmxhY2lvbkFudWFsKSsNCiAgZ2VvbV9obGluZSh5aW50ZXJjZXB0PWMobWF4KHBvYmxhY2lvbkFudWFsKSksY29sb3I9InJlZCIsbGluZXR5cGU9ImRhc2hlZCIpDQpgYGANCg0KLSAgIEVuIGVsIHNpZ3VpZW50ZSBncsOhZmljbyBvYnNlcnZhbW9zIGVsIHRpcG8gZGUgZXNxdWVtYSBxdWUgc2lndWUgbGEgc2VyaWUsIHNpIG5vcyBmaWphbW9zLCBsb3MgcHVudG9zIHNlIGVzcGFyY2VuIGRlIGZvcm1hICJhbGVhdG9yaWEiIHBvciBlbCBncsOhZmljbywgcG9yIGxvIHF1ZSBlcyB1biBlc3F1ZW1hICoqYWRpdGl2byoqLg0KDQoNCmBgYHtyLGZpZy5hbGlnbj0nY2VudGVyJyxvdXQuZXh0cmE9J2FuZ2xlPTkwJywgZWNobz1UUlVFLCB3YXJuaW5nPUZBTFNFfQ0KcG9ibGFjaW9uRGVzdiA8LSBhZ2dyZWdhdGUocG9ibGFjaW9uLCBGVU4gPSBzZCkgDQpwbG90KGFzLm51bWVyaWMocG9ibGFjaW9uQW51YWwgICksIGFzLm51bWVyaWMocG9ibGFjaW9uRGVzdikpDQpgYGANCg0KLSAgIENvbW8gdmVtb3MsIHNlIHRyYXRhIGRlIHVuYSBzZXJpZSBxdWUgc2UgaGEgbW92aWRvIGV4YWN0YW1lbnRlIGlndWFsIGEgbG8gbGFyZ28gZGUgY2FkYSB0cmltZXN0cmUgZW4gbG9zIMO6bHRpbW9zIGHDsW9zLCBwb3IgbG8gcXVlIHRpZW5lIGxhIG1pc21hIGZvcm1hIHkgcHLDoWN0aWNhbWVudGUgbGEgbWlzbWEgbWVkaWENCg0KYGBge3IsZmlnLmFsaWduPSdjZW50ZXInLG91dC5leHRyYT0nYW5nbGU9OTAnLCBlY2hvPVRSVUUsIHdhcm5pbmc9RkFMU0V9DQpnZ3N1YnNlcmllc3Bsb3QocG9ibGFjaW9uKSsNCiAgeWxhYigiUG9ibGFjaW9uIikrDQogIHhsYWIoIkHDsW9zIikrDQogIGdndGl0bGUoIiIpDQpgYGANCg0KLSAgIENvbW8gdmVtb3MgZW4gZXN0ZSBncsOhZmljbyB5IHNlIHBvZMOtYSBpbnR1aXIgYW50ZXJpb3JtZW50ZSwgbGEgcG9ibGFjacOzbiBlbiBuacOxb3Mgbm8gYXVtZW50YSBlc3RhY2lvbmFsbWVudGUsIHNpbm8gcXVlIHNlIG1hbnRpZW5lIGlndWFsIGR1cmFudGUgdG9kbyBlbCBhw7FvDQoNCmBgYHtyLGZpZy5hbGlnbj0nY2VudGVyJyxvdXQuZXh0cmE9J2FuZ2xlPTkwJywgZWNobz1UUlVFLCB3YXJuaW5nPUZBTFNFfQ0KZ2dzZWFzb25wbG90KHBvYmxhY2lvbiwNCiAgICAgICAgICAgICB5ZWFyLmxhYmVscz1UUlVFLA0KICAgICAgICAgICAgIHhsYWI9IiIsDQogICAgICAgICAgICAgeWxhYj0iUG9ibGFjaW9uIiwNCiAgICAgICAgICAgICBtYWluPSIiKQ0KYGBgDQoNCg0KIyMgQikgSG9sdC1XaW50ZXJzIA0KLSAgICRcYWxwaGE9MC45OTk0JCBMbyBxdWUgaW5kaWNhIHF1ZSBlc3RhIHNlcmllIHZhcsOtYSBtdWNobyBjb24gZWwgcGFzbyBkZWwgdGllbXBvLg0KLSAgICRcYmV0YT0wLjE5MTMkIGluZGljYSBxdWUgbGEgcGVuZGllbnRlIGFwZW5hcyBzZSBtdWV2ZSBjb24gZWwgcGFzbyBkZSB0aWVtcG8sIHkgdGllbmRlIGEgbWFudGVuZXJzZSBjb25zdGFudGUuDQotICAgJFxnYW1tYVxhcHByb3gwJCBpbmRpY2EgcXVlIGxhIGVzdGFjaW9uYWxpZGFkIHBlcm1hbmVjZSBjb25zdGFudGUgZW4gZWwgdGllbXBvLiBFc3RhIGNvbXBvbmVudGUgbm8gYXBvcnRhIGFwZW5hcyBpbmZvcm1hY2nDs24NCg0KYGBge3J9DQpwb2JsYWNpb25ob2x0PXdpbmRvdyhwb2JsYWNpb24sc3RhcnQ9YygyMDAyLDEpLGVuZD1jKDIwMjIsNCkpDQpwb2JsYWNpb25FdHM9ZXRzKHBvYmxhY2lvbmhvbHQsDQogICAgICAgICAgICAgICAgIG1vZGVsPSJBQUEiLA0KICAgICAgICAgICAgICAgICBkYW1wZWQ9RkFMU0UpDQpzdW1tYXJ5KHBvYmxhY2lvbkV0cykNCg0KYGBgDQoNCiMjIEMpIENvbXBvbmVudGVzIGVzdGFjaW9uYWxlcyBzMSxzMixzMyB5IHM0DQotICAgQ29tbyB0ZXJtaW5hbW9zIGVuIGVsIMO6bHRpbW8gY3VhdHJpc21ldHJlIGRlIDIwMjIsIHRlbmVtb3MgcXVlIHMxIGVzIGVsIHZhbG9yIGVzdGFjaW9uYWwgcGFyYSBlbCDDumx0aW1vIGRhdG8gKMO6bHRpbW8gdHJpbWVzdGVyIGRlIDIwMjIpLCBzMiB0ZXJjZXIgdHJpbWVzdHJlIGRlIDIwMjIsIHMzIHNlZ3VuZG8gdHJpbWVzdHJlIHkgczEgcHJpbWVyIHRyaW1lc3RyZS4gDQoNCmBgYHtyfQ0KdGFpbChwb2JsYWNpb25FdHMkc3RhdGVzLDEpDQpgYGANCiMjIEQpIFByZWRpY2Npw7NuIGEgNSBhw7Fvcw0KLSAgIEVuIGVzdGUgZWplcmNpY2lvIGhlIHByb2JhZG8gYSBiYWphciBlbCBjb25maWRlbmNlIGxldmVsIHBhcmEgZXN0cmVjaGFyIGVsIGNhbmFsIGRlIHByZWRpY2Npw7NuIHkgdmVyIG90cmEgYWx0ZXJuYXRpdmEuIFZlbW9zIHF1ZSBlc3RlIG1vZGVsbyBlc3TDoSBkZXN0aW5hZG8gYSBzZWd1aXIgYmFqYW5kbywgYSBubyBzZXIgcXVlIHNlIHByb2R1emNhIGFsZ28gaW5kZXBlbmRpZW50ZSBhIGxhIHNlcmllIHF1ZSBsbyBoYWdhIHNlciBtb2RpZmljYWRvLiANCg0KYGBge3IsZmlnLmFsaWduPSdjZW50ZXInLG91dC5leHRyYT0nYW5nbGU9OTAnLCBlY2hvPVRSVUUsIHdhcm5pbmc9RkFMU0V9DQpwb2JsYWNpb25kZj1mb3JlY2FzdChwb2JsYWNpb25FdHMsDQogICAgICAgICAgICAgICAgICAgICBoPTIwLA0KICAgICAgICAgICAgICAgICAgICAgbGV2ZWw9NzApDQphdXRvcGxvdChwb2JsYWNpb25kZiwNCiAgICAgICAgIHhsYWI9IlBvYmxhY2nDs24gKyA1IGHDsW9zIHByZWRpY2Npw7NuIiwNCiAgICAgICAgIHlsYWI9IlBvYmxhY2nDs24iLA0KICAgICAgICAgbWFpbj0iUHJlZGljY2nDs24gSG9sdC1XaW50ZXJzIEFkaXRpdm8iLA0KICAgICAgICAgUEk9VFJVRSkNCmBgYA0KDQojIyBFKSBWYWxpZGFjacOzbiBDcnV6YWRhDQotICAgRWwgZXJyb3IgYSB1biB0cmltZXN0cmUgdmlzdGEgZXMgZGUgNy43Nywgc2luIGVtYmFyZ28sIHNpIG5vcyB2YW1vcyBhbCBmaW5hbCBkZWwgcGVyaW9kbyAoOCB0cmltZXN0cmVzIG8gMiBhw7FvcyksIGVsIGVycm9yIGFzY2llbmRlIGEgNTguNTgNCg0KYGBge3J9DQprIDwtIDMyICAgICAgICAgICAgICAgICAgI01pbmltbyBudW1lcm8gZGUgZGF0b3MgcGFyYSBlc3RpbWFyDQpoIDwtIDggICAgICAgICAgICAgICAgICAgI0hvcml6b250ZSBkZSBsYXMgcHJlZGljaWNpb25lcw0KVFQgPC0gbGVuZ3RoKHBvYmxhY2lvbikjTG9uZ2l0dWQgc2VyaWUNCnMgPC0gVFQgLSBrIC0gaCAgICAgICAgICAgI1RvdGFsIGRlIGVzdGltYWNpb25lcw0KDQpybXNlIDwtIG1hdHJpeChOQSwgcyArIDEsIGgpDQptYXBlIDwtIG1hdHJpeChOQSwgcyArIDEsIGgpDQpmb3IgKGkgaW4gMDpzKSB7DQogIHRyYWluLnNldCA8LSBzdWJzZXQocG9ibGFjaW9uLCBzdGFydCA9IGkgKyAxLCBlbmQgPSBpICsgaykNCiAgdGVzdC5zZXQgPC0gIHN1YnNldChwb2JsYWNpb24sIHN0YXJ0ID0gaSArIGsgKyAxLCBlbmQgPSBpICsgayArIGgpDQogIA0KICBwb2JsYWNpb25FdHMgPC0gZXRzKHRyYWluLnNldCwgDQogICAgICAgICAgICAgICAgICAgICAgIG1vZGVsID0gIkFBQSIsIA0KICAgICAgICAgICAgICAgICAgICAgICBkYW1wZWQgPSBGQUxTRSkNCiAgZmNhc3QgPC0gZm9yZWNhc3QocG9ibGFjaW9uRXRzLCBoID0gaCkNCiAgcm1zZVtpICsgMSxdIDwtICh0ZXN0LnNldCAtIGZjYXN0JG1lYW4pXjINCiAgbWFwZVtpICsgMSxdIDwtIDEwMCphYnModGVzdC5zZXQgLSBmY2FzdCRtZWFuKS90ZXN0LnNldA0KfQ0KDQpybXNlLmV0cyA8LSBzcXJ0KGNvbE1lYW5zKHJtc2UpKQ0KbWFwZS5ldHMgPC0gY29sTWVhbnMobWFwZSkNCg0KZXJyb3I9ZGF0YS5mcmFtZSh0KHJvdW5kKHJtc2UuZXRzLCAyKSkpDQpjb2xuYW1lcyhlcnJvcik9YygiMVQiLCIyVCIsIjNUIiwiNFQiLCI1VCIsIjZUIiwiN1QiLCI4VCIpDQpyb3duYW1lcyhlcnJvcik9YygiRXJyb3IiKQ0KZXJyb3INCmBgYA0KIyBFamVyY2ljaW8gNTogU2VyaWUgUGFzYWplcm9zDQojIyBBKSBHcmFmaWNhciBsYSBzZXJpZSBjb21wbGV0YSB5IGNvbWVudGFyDQoNCi0gICBTYWx0YSBjbGFyYW1lbnRlIGEgbGEgdmlzdGEgZWwgYcOxbyAyMDIwLCBjb21vIHZlbW9zIGVsIGHDsW8gY29tZW56w7MgY29tbyBjdWFscXVpZXIgb3RybywgcGVybyBlbiBlbCBtb21lbnRvIGVuIGVsIHF1ZSBsYSBDb3ZpZCBzZSBoaXpvIHVuYSByZWFsaWRhZCwgZWwgdHJhbnNwb3J0ZSBiYWrDsyBkcsOhc3RpY2FtZW50ZS4gDQoNCmBgYHtyLGZpZy5hbGlnbj0nY2VudGVyJyxvdXQuZXh0cmE9J2FuZ2xlPTkwJywgZWNobz1UUlVFLCB3YXJuaW5nPUZBTFNFfQ0KcGFzYWplcm9zPXJlYWQuY3N2MigiUGFzYWplcm9zLmNzdiIpDQpwYXNhamVyb3M9dHMocGFzYWplcm9zLA0KICAgICAgICAgICAgICAgc3RhcnQ9YygxOTk2LDEpLA0KICAgICAgICAgICAgICAgZnJlcXVlbmN5PTEyKQ0KcGFzYWplcm9zYj13aW5kb3cocGFzYWplcm9zLHN0YXJ0PTE5OTYpDQpnZ3NlYXNvbnBsb3QocGFzYWplcm9zYiwNCiAgICAgICAgICAgICB5ZWFyLmxhYmVscz1ULA0KICAgICAgICAgICAgIHhsYWI9IkHDsW9zIiwNCiAgICAgICAgICAgICB5bGFiPSJQYXNhamVyb3MiLA0KICAgICAgICAgICAgIG1haW49IiIpDQoNCg0KICAgICAgICAgICANCmBgYA0KDQojIyBCKSBSZWNvcnRhciBsYSBzZXJpZQ0KLSAgIFNlZ3VpbW9zIGNvbiBlbCBlamVyY2ljaW8gYSBwYXJ0aXIgZGUgYXF1w60NCg0KYGBge3J9DQpwYXNhamVyb3MyPWFzLmRhdGEuZnJhbWUocGFzYWplcm9zKQ0KcGFzYWplcm9zPXBhc2FqZXJvczJbMTooMzEyLTI0KSxdDQp0aWJibGUocGFzYWplcm9zPWhlYWQocGFzYWplcm9zKSkNCnBhc2FqZXJvcz10cyhwYXNhamVyb3MsDQogICAgICAgICAgICAgc3RhcnQ9YygxOTk2LDEpLA0KICAgICAgICAgICAgIGZyZXF1ZW5jeT0xMikNCmBgYA0KDQojIyBDKSBHcmFmaWNhcyB5IGNvbWVudGFyDQoNCi0gICBFbiBlc3RlIGdyw6FmaWNvIGFwcmVjaWFtb3MgY29tbyBlbCBjcmVjaW1pZW50byBkZSB1c3VhcmlvcyBlbiBlbCB0cmFuc3BvcnRlIHDDumJsaWNvIGRlc2RlIDE5OTYgY3JlY2nDs24gc2lnbmlmaWNhdGl2YW1lbnRlIGhhc3RhIGxsZWdhciBhIHVuIHB1bnRvICgyMDA3KSBkb25kZSBzZSBwYXLDsyBlbCBjcmVjaW1pZW50byB5IGNvbWVuesOzIGEgYmFqYXIuIFRvZG8gZXN0byBoYXN0YSAyMDEzIGRvbmRlIGRlIG51ZXZvIHNlIHJldG9tYSBlbCB1c28gZGUgdHJhbnNwb3J0ZSB5IGNyZWNlIGhhc3RhIHN1cyBuaXZlbGVzIG3DoXMgYWx0b3MgZW4gMjAxOS4gDQoNCmBgYHtyLGZpZy5hbGlnbj0nY2VudGVyJyxvdXQuZXh0cmE9J2FuZ2xlPTkwJywgZWNobz1UUlVFLCB3YXJuaW5nPUZBTFNFfQ0KcGFzYWplcm9zQW51YWw9YWdncmVnYXRlKHBhc2FqZXJvcyxGVU49c3VtKQ0KYXV0b3Bsb3QocGFzYWplcm9zQW51YWwsDQogICAgICAgICB4bGFiPSJBw7FvcyIsDQogICAgICAgICB5bGFiPSJQYXNhamVyb3MiLA0KICAgICAgICAgbWFpbj0iUGFzYWplcm9zIHRyYW5zcG9ydGUgcMO6YmxpY28gMTk5Ni0yMDE5IikrDQogIGdlb21fdmxpbmUoeGludGVyY2VwdD1jKDIwMDcsMjAxMywyMDE5KSxsaW5ldHlwZT0iZGFzaGVkIixjb2xvcj0icmVkIikNCmBgYA0KDQotICAgRW4gZWwgc2lndWllbnRlIGdyw6FmaWNvIG9ic2VydmFtb3MgZWwgZXNxdWVtYSBkZWwgZ3LDoWZpY28sIGRvbmRlIHZlbW9zIHF1ZSBlcyB1biBlc3FhdWVtYSAqKmFkaXRpdm8qKiBwb3IgbGEgYWxlYXRvcmllZGFkIGRlIGxvcyBwdW50b3MgZW4gZWwgZ3LDoWZpY28NCg0KYGBge3IsZmlnLmFsaWduPSdjZW50ZXInLG91dC5leHRyYT0nYW5nbGU9OTAnLCBlY2hvPVRSVUUsIHdhcm5pbmc9RkFMU0V9DQpwYXNhamVyb3NEZXN2PWFnZ3JlZ2F0ZShwYXNhamVyb3MsRlVOPXNkKQ0KcGFzYWplcm9zQW51YWw9YWdncmVnYXRlKHBhc2FqZXJvcyxGVU49c3VtKQ0KcGxvdChhcy5udW1lcmljKHBhc2FqZXJvc0FudWFsKSxhcy5udW1lcmljKHBhc2FqZXJvc0Rlc3YpKQ0KDQoNCmBgYA0KDQotICAgRW4gZXN0ZSBncsOhZmljbyBlc3R1ZGlhbW9zIGxhIGVzdGFjaW5hbGlkYWQgZGUgbG9zIHBhc2FqZXJvcyBlbiBsYSBzZXJpZS4gQ29tbyB2ZW1vcywgZW4gbG9zIHBlcmlvZG9zIHZhY2FjaW9uYWxlcyBlbCB1c28gZGUgdHJhbnNwb3J0ZSBww7pibGljbyBkaXNtaW51eWUgZHLDoXN0aWNhbWVudGUgKGRlIEp1bmlvIGEgU2VwdGllbWJyZSBzZSBtYW50aWVuZSBiYWpvLCBhbCBpZ3VhbCBxdWUgZW4gTmF2aWRhZCkgeSBzZSBlbmN1ZW50cmEgZW4gc3VzIG1heW9yZXMgbml2ZWxlcyBlbiBtb21lbnRvcyBkZSBtw6F4aW1vIHRyYWJham8gZW4gZWwgY2FsZW5kYXJpbyBhbnVhbCAoT2N0dWJyZSB5IE1hcnpvKS4gDQoNCmBgYHtyLGZpZy5hbGlnbj0nY2VudGVyJyxvdXQuZXh0cmE9J2FuZ2xlPTkwJywgZWNobz1UUlVFLCB3YXJuaW5nPUZBTFNFfQ0KcGFzYWplcm9zYj13aW5kb3cocGFzYWplcm9zLHN0YXJ0PTE5OTYpDQpnZ3N1YnNlcmllc3Bsb3QocGFzYWplcm9zYikrDQogIHlsYWIoIlBhc2FqZXJvcyIpKw0KICB4bGFiKCIiKSsNCiAgZ2d0aXRsZSgiRXN0YWNpb25hbGlkYWQgZGUgcGFzYWplcm9zIikNCmBgYA0KDQojIyBEKSBNZWRpYXMgbcOzdmlsZXMNCg0KYGBge3IsZmlnLmFsaWduPSdjZW50ZXInLG91dC5leHRyYT0nYW5nbGU9OTAnLCBlY2hvPVRSVUUsIHdhcm5pbmc9RkFMU0V9DQphY3RpdmlkYWRfdHNpYmJsZT10c2liYmxlKA0KICB0aW1lPSBhcy5udW1lcmljKHRpbWUocGFzYWplcm9zKSksDQogIGFjdGl2aWRhZD1hcy5udW1lcmljKHBhc2FqZXJvcyksDQogIGluZGV4PXRpbWUpfD4NCiAgbXV0YXRlKA0KICAgIGAzLU1hYCA9IHNsaWRlcjo6c2xpZGVfZGJsKGFjdGl2aWRhZCxtZWFuLCAuYmVmb3JlPTEsIC5hZnRlcj0xLCAuY29tcGxldGU9RkFMU0UpLA0KICAgIGAxMi1NYWAgPSBzbGlkZXI6OnNsaWRlX2RibChhY3RpdmlkYWQsbWVhbiwuYmVmb3JlPTUsLmFmdGVyPTYsLmNvbXBsZXRlPUZBTFNFKSwNCiAgICBgMioxMi1NYWAgPSBzbGlkZXI6OnNsaWRlX2RibChgMTItTWFgLG1lYW4sLmJlZm9yZT0xLC5hZnRlcj0wLC5jb21wbGV0ZT1GQUxTRSksDQogICkNCnA9YWN0aXZpZGFkX3RzaWJibGUgfD4NCiAgZ2dwbG90KCkrDQogIGdlb21fbGluZShhZXMoeD10aW1lLHk9YWN0aXZpZGFkKSxjb2w9ImdyYXk0NSIsc2l6ZT0wLjU1KSsNCiAgZ2VvbV9saW5lKGFlcyh4PXRpbWUseT1gMy1NYWApLGNvbD0icmVkIixzaXplPTAuNjUpKw0KICBnZW9tX2xpbmUoYWVzKHg9dGltZSx5PWAxMi1NYWApLGNvbD0iZ3JlZW4iLHNpemU9MC42NSkrDQogIGdlb21fbGluZShhZXMoeD10aW1lLHk9YDIqMTItTWFgKSxjb2w9ImJsdWUiLHNpemU9MC42NSkrDQogIHhsYWIoImHDsW8iKSsNCiAgc2NhbGVfY29sb3JfbWFudWFsKHZhbHVlcz1jKCJncmV5NDUiLCJyZWQiLCJncmVlbiIpLGxhYmVscz1jKCJBIiwiQiIsIkMiKSkNCnAgKyB0aGVtZShsZWdlbmQucG9zaXRpb249ImxlZnQiKQ0KDQpgYGANCg0KIyBFamVyY2ljaW8gNjogU2VyaWUgQWZvcm8gT3JvcGVzYQ0KIyMgQSkgR3JhZmljYXIgeSBjb21lbnRhciBsYXMgY29tcG9uZW50ZXMgcHJpbmNpcGFsZXMNCg0KLSAgIEVuIGVsIHNpZ3VpZW50ZSBncsOhZmljbyB2ZW1vcyBsYSBldm9sdWNpw7NuIGFudWFsIGRlIGxvcyB2ZWjDrWN1bG9zIHF1ZSBjaXJjdWxhbiBwb3IgT3JvcGVzYSwgZG9uZGUgb2JzZXJ2YW1vc2RvcyB0cmFtb3MgcHJpbmNpcGFsZXMuIERlIDE5NjAgYSAxOTgyIGRvbmRlIHZlbW9zIHVuYSBtb250YcOxYSBkZSBjcmVjaW1pZW50byBlbiBsYSBjaXJjdWxhY2nDs24gKHNlIHB1ZWRlIGRlYmVyIHF1aXrDoSBhIHVuYSBpbmR1c3RyaWEgbXV5IGV4cGxvdGFkYSBlbiBsYSB6b25hIHF1ZSByZXF1ZXLDrWEgbXVjaGEgY2lyY3VsYWNpw7NuIGRlIHRyw6FmaWNvKS4gWSBlbCBzaWd1aWVudGUgdHJhbW8gZGVzZGUgMTk4MiBlbiBhZGVsYW50ZSwgZG9uZGUgdmVtb3MgdW4gY3JlY2ltaWVudG8gc3VzdGFuY2lhbCBkZSB2ZWjDrWN1bG9zIGVuIGxhIHpvbmEuIEEgZXN0ZSBhdW1lbnRvIHBvZGVtb3MgYXRyaWJ1aXJsZSBsYSBleHBhbnNpw7NuIGRlbCBhdXRvbcOzdmlsLCBwdWVzIGVzIGV2aWRlbnRlIHF1ZSBlbiBlc3RlIHRyYW1vIGVsIG7Dum1lcm8gZGUgdmVow61jdWxvcyBkaXNwb25pYmxlcyBwYXJhIGxhIHBvYmxhY2nDs24gY3JlY2nDs24gc3VzdGFuY2lhbG1lbnRlLiANCg0KLSAgIERlc2RlIDIwMDcgZW4gYWRlbGFudGUgZWwgbsO6bWVybyBkZSB2ZWjDrWN1bG9zIGVuIGVzZSBrbSBkaXNtaW51eWUgcG9yIGxhIGNvbnN0cnVjY2nDs24gZGUgdW5hIGF1dG9waXN0YSBxdWUgcGVybWl0ZSB1bmEgbWF5b3IgZmx1aWRleiB5IHZlbG9jaWRhZCBlbiBlbCB0csOhZmljby4gDQoNCmBgYHtyLGZpZy5hbGlnbj0nY2VudGVyJyxvdXQuZXh0cmE9J2FuZ2xlPTkwJywgZWNobz1UUlVFLCB3YXJuaW5nPUZBTFNFfQ0KdmVoaWN1bG9zPXJlYWQuY3N2MigiYWZvcm9fb3JvcGVzYS5jc3YiKQ0KdmVoaWN1bG9zPXRzKHZlaGljdWxvcywNCiAgICAgICAgICAgICAgIHN0YXJ0PWMoMTk2MCksDQogICAgICAgICAgICAgICBmcmVxdWVuY3k9MSkNCg0KYXV0b3Bsb3QodmVoaWN1bG9zLA0KICAgICAgICAgeGxhYj0iQcOxb3MiLA0KICAgICAgICAgeWxhYj0iVmVow61jdWxvcyIsDQogICAgICAgICBtYWluPSJWZWjDrWN1bG9zIHBvciBPcm9wZXNhIikrDQogIGdlb21faGxpbmUoeWludGVyY2VwdCA9IGMobWF4KHZlaGljdWxvcykpLGNvbG9yPSJyZWQiLGxpbmV0eXBlPSJkb3RkYXNoIixsd2Q9MSkrDQogIGdlb21fdmxpbmUoeGludGVyY2VwdD1jKDE5ODIsMjAwNyksbGluZXR5cGU9InR3b2Rhc2giLGNvbG9yPSJyZWQiKQ0KYGBgDQoNCi0gICBHcmFjaWFzIGFsIHNpZ3VpZW50ZSBncsOhZmljbyBwb2RlbW9zIGFmaXJtYXIgcXVlIHNlIHRyYXRhIGRlIHVuIGVzcXVlbWEgKiptdWx0aXBsaWNhdGl2byoqIGRlYmlkbyBhIGxhIGNvbnRpbnVpZGFkIGRlIGxvcyBwdW50b3MgZW4gZWwgZ3LDoWZpY28uDQoNCmBgYHtyLGZpZy5hbGlnbj0nY2VudGVyJyxvdXQuZXh0cmE9J2FuZ2xlPTkwJywgZWNobz1UUlVFLCB3YXJuaW5nPUZBTFNFfQ0KdmVoaWN1bG9zRGVzdj1hZ2dyZWdhdGUodmVoaWN1bG9zLEZVTj1zZCkNCnBsb3QoYXMubnVtZXJpYyh2ZWhpY3Vsb3MpLGFzLm51bWVyaWModmVoaWN1bG9zRGVzdikpDQpgYGANCg0KIyMgQikgUHJlZGljY2nDs24gbcOpdG9kbyBtZWRpYSB5IGRlcml2YQ0KDQotICAgRWwgbcOpdG9kbyBxdWUgbWVqb3IgcmVzdWx0YWRvIG9idGllbmUgZXMgZWwgbcOpdG9kbyBEZXJpdmEsIHBlcm8gcmVhbG1lbnRlIG5pbmd1bm8gZGUgbG9zIGRvcyBvZnJlY2UgdW4gaW50ZXJ2YWxvIGRlIGNvbmZpYW56YSBwYXJhIGVsIGVycm9yIGJ1ZW5vLg0KDQotICAgTGEgZGlmZXJlbmNpYSBwcmluY2lwYWwgZW50cmUgYW1ib3MgbcOpdG9kb3MgZXMgcXVlIGxhIG1lZGlhIHNpbXBsZW1lbnRlIGNhbGN1bGEgbGEgbWVkaWEgdG90YWwgZGUgbGEgc2VyaWUgcGFyYSBvZnJlY2VyIGxhIGluZm9ybWFjacOzbiB5IGVsIG3DqXRvZG8gZGVyaXZhIGHDsWFkZSBhIGxhIG9ic2VydmFjacOzbiBhbnRlcmlvciBlbCBpbmNyZW1lbnRvIG1lZGlvIGRlIGxhIHNlcmllICogZWwgbsO6bWVybyBkZSBwZXJpb2Rvcy4gKFNpIHNlIHF1aWVyZSBvYnRlbmVyIHByZWRpY2Npw7NuIGRlIGRvcyBhw7FvcyBlbiB1bmEgc2VyaWUgYW51YWwsIHNlIGNhbGN1bGEgZWwgaW5jcmVtZW50byBtZWRpbyB5IHNlIG11bHRpcGxpY2EgcG9yIGRvcykNCmBgYHtyLGZpZy5hbGlnbj0nY2VudGVyJyxvdXQuZXh0cmE9J2FuZ2xlPTkwJywgZWNobz1UUlVFLCB3YXJuaW5nPUZBTFNFfQ0KDQptZWRpYVZlaGljdWxvcz1tZWFuZih2ZWhpY3Vsb3MsaD01KQ0KbWVkaWFWZWhpY3Vsb3NkZj1hcy5kYXRhLmZyYW1lKG1lZGlhVmVoaWN1bG9zKQ0KDQojIG3DqXRvZG8gZGVyaXZhDQpkZXJpdmFWZWhpY3Vsb3M9cndmKHZlaGljdWxvcywgaD01LCBkcmlmdD1UKQ0KZGVyaXZhVmVoaWN1bG9zZGY9YXMuZGF0YS5mcmFtZShkZXJpdmFWZWhpY3Vsb3MpDQpwcmVkaWNjaW9uZXM9ZGF0YS5mcmFtZShNZXRvZG9fTWVkaWE9bWVkaWFWZWhpY3Vsb3NkZlssIlBvaW50IEZvcmVjYXN0Il0sTWV0b2RvX2Rlcml2YT1kZXJpdmFWZWhpY3Vsb3NkZlssIlBvaW50IEZvcmVjYXN0Il0pDQpyb3duYW1lcyhwcmVkaWNjaW9uZXMpPWMoMjAyMDoyMDI0KQ0KcHJlZGljY2lvbmVzDQoNCnZlaGljdWxvc2Rlcml2YT1hY2N1cmFjeShkZXJpdmFWZWhpY3Vsb3MpDQp2ZWhpY3Vsb3NtZWRpYT1hY2N1cmFjeShtZWRpYVZlaGljdWxvcykNCg0KYXV0b3Bsb3QodmVoaWN1bG9zLA0KICAgICAgICAgc2VyaWVzPSJPcmlnaW5hbCIpKw0KICBhdXRvbGF5ZXIoZGVyaXZhVmVoaWN1bG9zLFBJPUYsc2VyaWVzPSJwcmVkaWNjacOzbiBkZXJpdmEiKSsNCiAgYXV0b2xheWVyKG1lZGlhVmVoaWN1bG9zLFBJPUYsc2VyaWVzPSJwcmVkaWNjacOzbiBtZWRpYSIpDQoNCmNvbXBhcmFjaW9uPWFzLmRhdGEuZnJhbWUocmJpbmQodmVoaWN1bG9zZGVyaXZhLHZlaGljdWxvc21lZGlhKSkNCnJvd25hbWVzKGNvbXBhcmFjaW9uKT1jKCJNw6l0b2RvIERlcml2YSIsIk3DqXRvZG8gTWVkaWEiKQ0KY29tcGFyYWNpb24NCmBgYA0KIyMgQykgVGlwb3MgZGUgQWxpc2FkbyBFeHBvbmVuY2lhbCBwb3NpYmxlcw0KDQotICAgR3JhY2lhcyBhIGxhcyBncsOhZmljYXMgZGVsIGFwYXJ0YWRvICJBKSIsIHNhYmVtb3MgcXVlIGVsIGVzcXVlbWEgZGUgbGEgc2VyaWUgZXMgKiptdWx0aXBsaWNhdGl2byoqLCBwb3IgbG8gcXVlIHNvbGFtZW50ZSBwb2RlbW9zIGFwbGljYXIgbcOpdG9kb3Mgc2luIGVzdGFjaW9uYWxpZGFkIChwb3JxdWUgZXMgdW5hIHNlcmllIGFudWFsKSB5IGNvbiBlcnJvciBtdWx0aXBsaWNhdGl2by4gDQoNCi0gICBIZW1vcyBhcGxpY2FkbyBsb3MgbcOpdG9kb3MgTUFOIChlcnJvciBtdWx0aXBsaWNhdGl2bywgdGVuZGVuY2lhIGFkaXRpdmEgeSBzaW4gZXN0YWNpb25hbGlkYWQpIHkgTU5OIChlcnJvciBtdWx0aXBsaWNhdGl2bywgc2luIHRlbmRlbmNpYSB5IHNpbiBlc3RhY2lvbmFsaWRhZCkNCg0KDQpgYGB7cn0NCnZlaGljdWxvc2I9d2luZG93KHZlaGljdWxvcyxzdGFydD0xOTYwKQ0KdmVoaWN1bG9zYkV0cz1ldHModmVoaWN1bG9zYiwNCiAgICAgICAgICAgICAgICAgIG1vZGVsPSJNQU4iLA0KICAgICAgICAgICAgICAgICAgZGFtcGVkPUZBTFNFKQ0KDQp2ZWhpY3Vsb3NiRXRzMj1ldHModmVoaWN1bG9zYiwNCiAgICAgICAgICAgICAgICAgIG1vZGVsPSJNTk4iLA0KICAgICAgICAgICAgICAgICAgZGFtcGVkPUZBTFNFKQ0KdmVoaWN1bG9zYkV0czE9YWNjdXJhY3kodmVoaWN1bG9zYkV0cykNCnZlaGljdWxvc2JFdHMyMT1hY2N1cmFjeSh2ZWhpY3Vsb3NiRXRzMikNCmNvbXBhcmFjaW9uPWFzLmRhdGEuZnJhbWUocmJpbmQodmVoaWN1bG9zYkV0czEsdmVoaWN1bG9zYkV0czIxKSkNCnJvd25hbWVzKGNvbXBhcmFjaW9uKT1jKCJNw6l0b2RvIE1BTiIsIk3DqXRvZG8gTU5OIikNCmNvbXBhcmFjaW9uDQpgYGANCi0gICBBbmFsaXphbW9zIGVsIGVycm9yIGRlbCBtb2RlbG8gTUFOIChxdWUgZXMgY29uIGVsIHF1ZSBub3MgcXVlZGFtb3MpIHkgdmVtb3MgcXVlIG5vIGhheSBuaW5ndW5hIG9ic2VydmFjacOzbiBpbXBvcnRhbnRlIHF1ZSBzZSBzYWxnYSBkZSBsb3MgbMOtbWl0ZXMuIA0KDQpgYGB7cixmaWcuYWxpZ249J2NlbnRlcicsb3V0LmV4dHJhPSdhbmdsZT05MCcsIGVjaG89VFJVRSwgd2FybmluZz1GQUxTRX0NCmVycm9yIDwtIHJlc2lkdWFscyh2ZWhpY3Vsb3NiRXRzKQ0Kc2RlcnJvciA8LSBzZChlcnJvcikNCg0KYXV0b3Bsb3QoZXJyb3IsIHNlcmllcz0iRXJyb3IiLA0KICAgICAgICAgY29sb3VyID0gImJsYWNrIiwNCiAgICAgICAgIHhsYWIgPSAiU2VtYW5hIiwNCiAgICAgICAgIHlsYWIgPSAiRXJyb3IiLA0KICAgICAgICAgbWFpbiA9ICIiKSArDQogIGdlb21faGxpbmUoeWludGVyY2VwdCA9IGMoLTMsIC0yLCAyICwzKSpzZGVycm9yLCANCiAgICAgICAgICAgICBjb2xvdXIgPSBjKCJyZWQiLCAiYmx1ZSIsICJibHVlIiwgInJlZCIpLCBsdHkgPSAyKSArIA0KICBzY2FsZV94X2NvbnRpbnVvdXMoYnJlYWtzPSBzZXEoNiwgMjYsIDIpKSANCmBgYA0KDQojIyBEKSBNb2RlbG8geSBsYXMgZWN1YWNpb25lcyByZWN1cnNpdmFzIGRlbCBtaXNtbw0KDQotICAgRWwgbW9kZWxvIHF1ZSB0ZW5lbW9zIGVzIE1BTiwgZXMgZGVjaXI6IA0KDQotICAgJHlfe3QraHx0fT0gbF90ICsgaGJfdCQsIGNvbiBlY3VhY2lvbmVzIHJlY3Vyc2l2YXMgdGFsIHF1ZToNCi0gICAkbF90PVxhbHBoYVwseV90KygxLVxhbHBoYSkobF97dC0xfStiX3t0LTF9KSwkDQotICAgJGJfdD1cYmV0YShsX3QtbF97dC0xfSkrKDEtXGJldGEpYl97dC0xfS4kDQoNCi0gICBEb25kZTogJFxhbHBoYT0wLjk5OTg5OTckLCRcYmV0YT0wLjEwMTAzODYkLCAkbF90PTkwMS4xMzEkIHkgJGJfdD0zOTguODk2MSQNCg0KLSAgIEVsIG1vZGVsbyBNTk4gZXM6IA0KDQotICAgJHlfe3QraHx0fT1sX3QkDQotICAgJGxfdD1cYWxwaGEgeV90KygxLVxhbHBoYSlsX3t0LTF9JA0KLSAgIERvbmRlOiAkXGFscGhhPTAuOTk5ODk5NSQgeSAkbF90PTE2MDAuNjI2MyQNCg0KYGBge3J9DQp0aWJibGUoYWxwaGE9dmVoaWN1bG9zYkV0cyRwYXJbImFscGhhIl0sYmV0YT12ZWhpY3Vsb3NiRXRzJHBhclsiYmV0YSJdKQ0KdGliYmxlKGFscGhhPXZlaGljdWxvc2JFdHMyJHBhclsiYWxwaGEiXSkNCg0KYGBgDQoNCg0KIyMgRSkgVmFsaWRhY2nDs24gQ3J1emFkYQ0KDQotICAgRWwgZXJyb3IgaW5pY2lhbCBlcyA2LjY5LCBzaW4gZW1iYXJnbywgNSBhw7FvcyBtw6FzIHRhcmRlIHBhc2EgYSBzZXIgMzEuNTIgYXVtZW50YW5kbyBkZSB1bmEgZm9ybWEgY29uc2lkZXJhZGENCmBgYHtyfQ0KayA8LSAyMCAgICAgICAgICAgICAgICAgICNNaW5pbW8gbnVtZXJvIGRlIGRhdG9zIHBhcmEgZXN0aW1hcg0KaCA8LSA1ICAgICAgICAgICAgICAgICAgICNIb3Jpem9udGUgZGUgbGFzIHByZWRpY2ljaW9uZXMNClRUIDwtIGxlbmd0aCh2ZWhpY3Vsb3MpI0xvbmdpdHVkIHNlcmllDQpzIDwtIFRUIC0gayAtIGggICAgICAgICAgICNUb3RhbCBkZSBlc3RpbWFjaW9uZXMNCg0Kcm1zZSA8LSBtYXRyaXgoTkEsIHMgKyAxLCBoKQ0KbWFwZSA8LSBtYXRyaXgoTkEsIHMgKyAxLCBoKQ0KZm9yIChpIGluIDA6cykgew0KICB0cmFpbi5zZXQgPC0gc3Vic2V0KHZlaGljdWxvcywgc3RhcnQgPSBpICsgMSwgZW5kID0gaSArIGspDQogIHRlc3Quc2V0IDwtICBzdWJzZXQodmVoaWN1bG9zLCBzdGFydCA9IGkgKyBrICsgMSwgZW5kID0gaSArIGsgKyBoKQ0KICB2ZWhpY3Vsb3NiRXRzPWV0cyh0cmFpbi5zZXQsDQogICAgICAgICAgICAgICAgICBtb2RlbD0iTUFOIiwNCiAgICAgICAgICAgICAgICAgIGRhbXBlZD1GQUxTRSkNCiAgZmNhc3QgPC0gZm9yZWNhc3QodmVoaWN1bG9zYkV0cywgaCA9IGgsbGV2ZWw9OTUpDQogIA0KICBybXNlW2kgKyAxLF0gPC0gKHRlc3Quc2V0IC0gZmNhc3QkbWVhbileMg0KICBtYXBlW2kgKyAxLF0gPC0gMTAwKmFicyh0ZXN0LnNldCAtIGZjYXN0JG1lYW4pL3Rlc3Quc2V0DQp9DQoNCnJtc2Uuc25haXZlIDwtIHNxcnQoY29sTWVhbnMocm1zZSkpDQptYXBlLnNuYWl2ZSA8LSBjb2xNZWFucyhtYXBlKQ0KDQplcnJvcj1kYXRhLmZyYW1lKHQocm91bmQocm1zZS5zbmFpdmUsIDIpKSkNCmNvbG5hbWVzKGVycm9yKT1jKCIxQSIsIjJBIiwiM0EiLCI0QSIsIjVBIikNCg0KbWFwZT1kYXRhLmZyYW1lKHQocm91bmQobWFwZS5zbmFpdmUsIDIpKSkNCmNvbG5hbWVzKG1hcGUpPWMoIjFBIiwiMkEiLCIzQSIsIjRBIiwiNUEiKQ0Kcm93bmFtZXMobWFwZSk9YygiTWFwZSIpDQppbmZvcm1hY2lvbj1yYmluZChlcnJvcixtYXBlKQ0Kcm93bmFtZXMoaW5mb3JtYWNpb24pPWMoIkVycm9yIiwiTWFwZSIpDQppbmZvcm1hY2lvbg0KYGBgDQoNCg0KIyBFamVyY2ljaW8gNzogU2VyaWUgUHJvZHVjY2nDs24gZGUgQ2hvY29sYXRlDQoNCiMjIEEpIEdyYWZpY2FyIHkgY29tZW50YXIgbGFzIGNvbXBvbmVudGVzIHByaW5jaXBhbGVzDQoNCi0gICBFc3RhIGdyw6FmaWNhIGVzIHVuIHByaW1lciB2aXN0YXpvIGdlbmVyYWwgZGUgbGEgZXZvbHVjacOzbiBkZSBsYSBwcm9kdWNjacOzbiBkZSBjaG9jb2xhdGUgZW4gQXVzdHJhbGlhIA0KDQpgYGB7cixmaWcuYWxpZ249J2NlbnRlcicsb3V0LmV4dHJhPSdhbmdsZT05MCcsIGVjaG89VFJVRSwgd2FybmluZz1GQUxTRX0NCmNob2NvbGF0ZT1yZWFkLmNzdjIoIkNob2NvbGF0ZS5jc3YiKQ0KY2hvY29sYXRlPXRzKGNob2NvbGF0ZSwNCiAgICAgICAgICAgICAgIHN0YXJ0PWMoMTk1OCwxKSwNCiAgICAgICAgICAgICAgIGZyZXF1ZW5jeT0xMikNCmF1dG9wbG90KGNob2NvbGF0ZSwNCiAgICAgICAgIHhsYWI9IiIsDQogICAgICAgICB5bGFiPSJDaG9jb2xhdGUgdmVuZGlkbyIsDQogICAgICAgICBtYWluPSJDaG9jb2xhdGUgdmVuZGlkbyBlbiBBdXN0cmFsaWxhIikNCg0KYGBgDQoNCi0gICBTaW4gbmVjZXNpZGFkIGRlIGNyZWFyIGxpbmVhcyBkZSBtYXJjYWNpw7NuLCB2ZW1vcyBkZSBmb3JtYSBjbGFyYSBjb21vIGVsIGNob2NvbGF0ZSBoYSBzaWRvIHkgZXMgdW4gYmllbiBjdXlhIHByb2R1Y2Npw7NuIG5vIGhhIGhlY2hvIG3DoXMgcXVlIGNyZWNlciBkdXJhbnRlIGVsIHRyYXNjdXJzbyBkZSB0b2RhIGxhIHNlcmllLCBlbiBhbGd1bm9zIG1vbWVudG9zIGhhIHN1ZnJpZG8gZGUgbW9tZW50b3MgZGUgZGVjYWRlbmNpYSwgcGVybyBudW5jYSBoYW4gbG9ncmFkbyB1biBlZmVjdG8gZGHDsWlubyBwYXJhIGVsIGNyZWNpbWllbnRvLiANCg0KYGBge3IsZmlnLmFsaWduPSdjZW50ZXInLG91dC5leHRyYT0nYW5nbGU9OTAnLCBlY2hvPVRSVUUsIHdhcm5pbmc9RkFMU0V9DQpjaG9jb2xhdGVBbnVhbD1hZ2dyZWdhdGUoY2hvY29sYXRlLCBGVU49c3VtKQ0KYXV0b3Bsb3QoY2hvY29sYXRlQW51YWwsDQogICAgICAgICB4bGFiPSJBw7FvcyIsDQogICAgICAgICB5bGFiPSJQcm9kdWNjacOzbiBkZSBjaG9jb2xhdGUiLA0KICAgICAgICAgbWFpbj0iUHJvZHVjY2nDs24gZGUgY2hvY29sYXRlIGVuIEF1c3RyYWxpYSIpDQpgYGANCg0KLSAgIFBvZGVtb3MgaW50ZXJwcmV0YXIgZGljaG8gZ3LDoWZpY28gY29tbyB1biBlc3F1ZW1hICoqbXVsdGlwbGljYXRpdm8qKiBkZSBsYSBzZXJpZSwgcHVlcyBhIG1heW9yIHRpZW1wbywgbWF5b3IgdmFsb3IgZGUgbG9zIHB1bnRvcyBlbiBlbCBncsOhZmljbw0KDQpgYGB7cixmaWcuYWxpZ249J2NlbnRlcicsb3V0LmV4dHJhPSdhbmdsZT05MCcsIGVjaG89VFJVRSwgd2FybmluZz1GQUxTRX0NCmNob2NvbGF0ZURlc3Y9YWdncmVnYXRlKGNob2NvbGF0ZSwgRlVOPXNkKQ0KcGxvdChhcy5udW1lcmljKGNob2NvbGF0ZUFudWFsKSxhcy5udW1lcmljKGNob2NvbGF0ZURlc3YpKQ0KYGBgDQoNCi0gICBFbiBlc3RlIGdyw6FmaWNvIGRlIGVzdGFjaW9uYWxpZGFkIHZlbW9zIGRlIHVuYSBmb3JtYSBtdXkgY2xhcmEgY29tbyBsYSBwcm9kdWNjacOzbiBkZSBjaG9jb2xhdGUgYXVtZW50YSBhIHBhcnRpciBkZSBmZWJyZXJvIHkgY29taWVuemEgYSBkaXNtaW51aXIgZW4gc2VwdGllbWJyZSBoYXN0YSBhbGNhbnphciBlbCBwdW50byBtw6FzIGJham8gZW4gRW5lcm8uIA0KDQpgYGB7cixmaWcuYWxpZ249J2NlbnRlcicsb3V0LmV4dHJhPSdhbmdsZT05MCcsIGVjaG89VFJVRSwgd2FybmluZz1GQUxTRX0NCmNob2NvbGF0ZWI9d2luZG93KGNob2NvbGF0ZSxzdGFydD0xOTU4KQ0KZ2dzdWJzZXJpZXNwbG90KGNob2NvbGF0ZWIpKw0KICB5bGFiKCJDaG9jb2xhdGUiKQ0KYGBgDQoNCi0gICBPYnNlcnZhbmRvIGxhIHNlcmllIGNvbXBsZXRhIHZlbW9zIHF1ZSBubyBoYXkgbmluZ8O6biBhw7FvIHJhcm8sIHkgcXVlIHRvZG9zIHBhcmVjZW4gYmFzdGFudGUgY29ycmllbnRlcy4gDQoNCmBgYHtyLGZpZy5hbGlnbj0nY2VudGVyJyxvdXQuZXh0cmE9J2FuZ2xlPTkwJywgZWNobz1UUlVFLCB3YXJuaW5nPUZBTFNFfQ0KZ2dzZWFzb25wbG90KGNob2NvbGF0ZWIsDQogICAgICAgICAgICAgeWVhci5sYWJlbHM9VCwNCiAgICAgICAgICAgICB5bGFiPSJQcm9kdWNjacOzbiBkZSBDaG9jb2xhdGUiKQ0KYGBgDQoNCiMjIEIpIERlc2NvbXBvc2ljacOzbiBlbiByZWdyZXNpb25lcyBsb2NhbGVzDQoNCmBgYHtyfQ0KI2xvZ2Nob2NvbGF0ZV9zdGwgPC0gc3RsKGxvZyhjaG9jb2xhdGUpLA0KICAgICAgICAgICAgICAgICAgICAgICAjcy53aW5kb3cgPSAicGVyaW9kaWMiLA0KICAgICAgICAgICAgICAgICAgICAgICAjcm9idXN0ID0gVFJVRSkNCg0KYGBgDQoNCiMjIEMpIEFsaXNhZG8gRXhwb25lbmNpYWwNCg0KLSAgIENvbW8gaGVtb3MgdmlzdG8gYW50ZXJpb3JtZW50ZSBlc3RhbW9zIGFudGUgdW4gZXNxdWVtYSBtdWx0aXBsaWNhdGl2bywgcG9yIGxvIHF1ZSB0ZW5kcmVtb3MgcXVlIHV0aWxpemFyIHVuIEFsaXNhZG8gZGUgSG9sdC1XaW50ZXJzIG11bHRpcGxpY2F0aXZvDQoNCi0gICBFc3RlIG1vZGVsbyBzZSBkZXNjcmliZSBjb21vICR5X3t0K2h8dH09IChsX3QgK2hiX3QpKnNfe3QraC1tKGsrMSl9JCwgZG9uZGU6IA0KLSAgICRcYWxwaGE9MC4xNjM4JCBVbiB2YWxvciB0YW4gYmFqbyBpbmRpY2EgcXVlIGxhIHNlcmllIGFwZW5hcyB2YXLDrWEgY29uIGVsIHRpZW1wbw0KLSAgICRcYmV0YT0wJCBFc3RlIHZhbG9yIGluZGljYSBxdWUgbGEgcGVuZGllbnRlIG5vIHZhcsOtYSBjb24gZWwgcGFzbyBkZWwgdGllbXBvDQotICAgJFxnYW1tYT0wLjMzNTgkIERlY2ltb3MgcXVlIGxhIGVzdGFjaW9uYWxpZGFkIHBlcm1hbmVjZSBtw6FzIGJpZW4gY29uc3RhbnRlIGVuIGVsIHRpZW1wbw0KDQpgYGB7cn0NCmNob2NvbGF0ZWI9d2luZG93KGNob2NvbGF0ZSxzdGFyPTE5NTgpDQpjaG9jb2xhdGViRXRzPWV0cyhjaG9jb2xhdGViLA0KICAgICAgICAgICAgICAgICAgbW9kZWw9Ik1BTSIsDQogICAgICAgICAgICAgICAgICBkYW1wZWQ9RikNCnN1bW1hcnkoY2hvY29sYXRlYkV0cykNCg0KYGBgDQoNCiMjIEQpIFZhbG9yZXMgZGUgbGEgw7psdGltYSBmZWNoYSBkZSBsYSBzZXJpZQ0KDQotICAgT2J0ZW5lbW9zIHF1ZSAkbD05Nzg0Ljc1MyQgeSAkYj0xNS44MDM5OSQsIHBhcmEgcG9kZXIgZW50ZW5kZXIgbWVqb3IgcXXDqSBlcyBlc3RvLCB2ZW1vcyBjb21vIGwgZXMgbGEgZXN0aW1hY2nDs24gZGVsIG5pdmVsIGRlIGxhIHNlcmllIHkgYiBlcyBsYSBlc3RpbWFjacOzbiBkZSBsYSBwZW5kaWVudGUgZGUgbGEgc2VyaWUuIA0KLSAgIFBvciBvdHJvIGxhZG8gJHNfaSAgIFxmb3JhbGxcLGlcaW4gKDEsMiwuLi4sMTIpJCB0ZW5lbW9zIGxhIGVzdGltYWNpw7NuIGRlIGxhIGVzdGFjaW9uYWxpZGFkIGVuIGxvcyBtZXNlcyBlbiBvcmRlbiBpbnZlcnNvLCBzMSBlcyBlbCB2YWxvciBwYXJhIERpY2llbWJyZSAoZWwgw7psdGltbyBkYXRvKSwgczIgcGFyYSBOb3ZpZW1icmUgeSBhc8OtIGhhc3RhIHMxMiBxdWUgY29ycmVzcG9uZGUgYSBFbmVybw0KDQpgYGB7cn0NCnRhaWwoY2hvY29sYXRlYkV0cyRzdGF0ZXMsMSkNCmBgYA0KDQojIyBFKSBQcmVkaWNjacOzbiBwYXJhIGxvcyBwcsOzeGltb3MgYcOxb3MNCg0KYGBge3IsZmlnLmFsaWduPSdjZW50ZXInLG91dC5leHRyYT0nYW5nbGU9OTAnLCBlY2hvPVRSVUUsIHdhcm5pbmc9RkFMU0V9DQpjaG9jb2xhdGViZj1mb3JlY2FzdChjaG9jb2xhdGViRXRzLA0KICAgICAgICAgICAgICAgICAgICAgaD0zNiwNCiAgICAgICAgICAgICAgICAgICAgIGxldmVsPTk1KQ0KDQpjaG9jb2xhdGViZiRtZWFuDQphdXRvcGxvdChjaG9jb2xhdGViZiwNCiAgICAgICAgIHhsYWI9IkHDsW9zIiwNCiAgICAgICAgIHlsYWI9IlByb2R1Y2Npw7NuIGRlIGNob2NvbGF0ZSIsDQogICAgICAgICBtYWluPSJQcmVkaWNjacOzbiBkZSBDaG9jb2xhdGUgKyAzIGHDsW9zIiwNCiAgICAgICAgIFBJPUYpDQpgYGANCg0KIyMgRikgVHJhaW5pbmcgU2V0DQoNCmBgYHtyfQ0KY2hvY29sYXRlSW50cmEgPC0gc3Vic2V0KGNob2NvbGF0ZSwgZW5kID0gbGVuZ3RoKGNob2NvbGF0ZSkgLSAxMjApDQpjaG9jb2xhdGVFeHRyYSA8LSBzdWJzZXQoY2hvY29sYXRlLCBzdGFydCA9IGxlbmd0aChjaG9jb2xhdGUpIC0gMTE5KQ0KY2hvY29sYXRlRXh0cmFQcmUgPC0gc25haXZlKGNob2NvbGF0ZUludHJhLCBoID0gNTYpDQphY2N1cmFjeShjaG9jb2xhdGVFeHRyYVByZSwgY2hvY29sYXRlRXh0cmEpDQoNCmBgYA0KDQo=