—————————————————————————————-

Rappel du projet

Prédire la consommation électrique

—————————————————————————————-

Données

Analyses

Afin d’introduire votre analyse, effectuez une brève description des données (analyses univariées et bivariées).

Bilan

Questions / Réponses

Données

—————————

1 Importation des données

—————————

1.1 Sources des données

  • -> Jeu de données fichier xlsx

1.2 Installation des packages

  • -> L’installation des packages ne se fait qu’une seule fois, je les ai commentées pour éviter de réisntaller à chaque chargement du script *
#install.packages(c("Factoshiny"))
#install.packages("ggpubr")
#install.packages("rJava")
#install.packages("xlsx") 
#install.packages("tseries")
#install.packages("dataseries")
#install.packages("tidyverse")
#install.packages("caschrono")

1.3 Installation des libraries

  • -> L’activation des librairies se fait à chaque démarrage du script*
  • -> corrplot doit être appelé après ggplot2 pour éviter un warning*

1.4 Configuration des variables pour chaque fichier

  • -> définition d’une variable répertoire pour faciliter le changement d’OS *
repertoire_sources <- "/home/user/Documents/formations/oc/Formation Analyst/P9/sources/"
repertoire_images <- "/home/user/Documents/formations/oc/Formation Analyst/P9/images/"
fichier <- paste (repertoire_sources,"tab.csv",sep = "")

1.5 Création des Data Frame par lecture du fichier températures

  • -> les critères d’importation, séparateurs, virgules décimales, entête etc, ont étés définis après observation des fichiers sources à l’aide d’une éditeur de texte ou d’un tableur*
data <- read.table(fichier, header=TRUE, sep=',', fileEncoding = "ISO-8859-15")
head(data)

valeurs maxi mini de la période

data_max=max(data$Mois)
data_min=min(data$Mois)
cat("La série de données est entre : " , data_min,"et : " , data_max)

On garde les consommations de la France pour les données et

On vérifie qu’il n’y ait pas de valeurs nulles pour la consommation

df_fr <- subset(data, Territoire == "France")
df_fr <- df_fr[,c("Mois","Consommation.totale")]
sapply(df_fr, function(x) sum(is.na(x)))

On converti la variable Mois en Date

df_fr$Mois<-as.Date(as.yearmon(df_fr$Mois, "%Y-%m"))

affichage de la série temporelle

df_fr %>%
  ggplot(aes(x = Mois, y = `Consommation.totale`)) +
  geom_line()
  ggsave("img/01 - consommationTotale.png", width = 7.29, height = 4.5)
  • -> On peut voir une saisonnalité de la consommation.
  • -> Un pic de consommation important en période hivernale lié au chauffage. Et un plus petit pic en plein été lié à l’utilisation de la climatisation. On peut supposer qu’il s’agit d’une série temporelle additive.

1.6 Création des Data Frame DJU par lecture du fichier

fichier_dju <- paste (repertoire_sources,"dju.csv",sep = "")
dju <- read.csv2(fichier_dju,header = TRUE,sep = ",")

conversion du champ Mois au format date

dju$Mois<-as.Date(as.yearmon(dju$Mois, "%Y-%m"))

affichage de la série temporelle

dju %>%
  ggplot(aes(x = Mois, y = `DJU`)) +
  geom_line()
  ggsave("img/02 - DJU.png", width = 7.29, height = 4.5)
  • -> On constate qu’il y a la même saisonnarité que la consommation

1.7 Combiner les deux tableaux de données pour les dates communes

on observe sur le graphique précédent que les dernières valeurs sont à zéro

tail(dju,10)

on vérifie pour les consommations :

tail(df,10)

On restrain le jeu de données entre janvier 2012 et mai 2020 inclus

On combine les deux tableaux de données

On renomme les colonnes

df_fr <- subset(df_fr, Mois < "2020-06-01")
dju <- subset(dju, Mois < "2020-06-01")
df <- cbind( df_fr, dju$DJU) 
names(df)[names(df) == 'Consommation.totale'] <- 'conso'
names(df)[names(df) == 'dju$DJU'] <- 'dju'
names(df)[names(df) == 'Mois'] <- 'mois'
dju %>%
  ggplot(aes(x = Mois, y = `DJU`)) +
  geom_line()

1.8 Données “centré réduit” pour superposer les courbes

df_scaled <- data.frame(df, scale(df[,2:3]))
ggplot(df_scaled, aes(x = mois)) +
  geom_line(aes(y = conso.1, color = "conso")) +
  geom_line(aes(y = dju.1, color = "dju")) 
  ggsave("img/03 - consommation et DJU.png", width = 7.29, height = 4.5)
  • -> les courbes se superposent très bien à quelques exceptions près tels que les maximums, et l’usage de la climatisation car le dju donne l’écart de température par rapport au froid ce qui laisse supposer les besoins en chauffage alors que les écarts en été sont dus à la climatisation

—————————

2 Régression linéaire

—————————

plot des données

plot(conso~dju, data = df)
  • -> on y voit un diagonale se dessiner on peut donc envisager une régression linéaire

2.1 Plot avec la régression linéaire

model <- lm(conso~dju, data = df)
plot(conso~dju, data = df) + abline(model, col = "red")

Image sauvegarde du graphique

png("img/04 - regressionLineaire.png", width = 7.25, height = 4.5, units = 'in', res = 300)
plot(conso~dju, data = df) + abline(model, col = "red")

Summary du modèle de la régression

summary(model)

ANALYSE du modèle

  • -> les *** ( trois étoiles) montrent les indices les plus pertinents
  • -> R² à 0.95 indique qu’il y a une forte corrélation entre la consommation et la DJU
  • -> la P-Value < 5% indique que les coefficients sont significatifs
  • -> nous récupérons le cofficient de DJU pour corriger la consommation par rapport au DJU
model$coefficients[2]

2.2 Consommation corrigée par la regression linéaire

df$corrige<-df$conso-model$coefficients[2]*df$dju
ggplot(df, aes(x = mois)) +
  geom_line(aes(y = conso, color = "réelle")) +
  geom_line(aes(y = corrige, color = "corrigée"))
 ggsave("img/05 - consoReelleetCorrigee.png", width = 7.29, height = 4.5)
  • -> on observe que malgré la correction on peut y voir une saisonnalité.
  • -> la régression ne retire pas la saisonnalité de nos données
  • -> elle a permis de mettre en évidence les résidus

2.3 Résidus de la régression linéaire

res<-resid(model)
plot(df$mois,res,main="Résidus") + abline (v = as.Date("2012-08-01") , col = "blue") + abline (v = as.Date("2013-08-01") , col = "blue") + abline (v = as.Date("2014-08-01") , col = "blue")  + abline (v = as.Date("2015-08-01") , col = "blue") - abline(h=0,col="red")
  • -> les lignes bleues sont les mois d’août
  • -> Les résidus positifs les plus importants apparaissent en été, avec la différence de consommation dûe au climatiseur qui n’est pas prise en compte avec les calculs du chauffage.

—————————

3 Saisonnalité

—————————

consommation <- ts(df$conso, start=c(2012, 1), end=c(2020, 4), frequency = 12)
consommation_dec <- decompose(consommation) #,'additive') je ne vois pas la différence avec ou cette valeur.
plot(consommation_dec)
png("img/05b - boxPlotConsommationsMensuelles.png", width = 7.25, height = 4.5, units = 'in', res = 300)
boxplot(consommation~ cycle(consommation))
boxplot(consommation~ cycle(consommation))
corrigee <- ts(df$corrige, start=c(2012, 1), end=c(2020, 4), frequency = 12)
corrigee_dec <- decompose(corrigee) #,'additive') je ne vois pas la différence avec ou cette valeur.
png("img/05C - decompositionConsoCorrigee.png", width = 7.25, height = 4.5, units = 'in', res = 300)
plot(corrigee_dec)
plot(corrigee_dec)

Désaisonnalisation - consommation corrigée des variations saisonnières

  • -> La décomposition permet d’obtenir et de retirer l’aspect saisonnier d’une série temporelle ## 3.1 Saisonnalité sur la consommation
consommation_cvs = consommation-consommation_dec$seasonal
png("img/06 - consoDessainalisee.png", width = 7.25, height = 4.5, units = 'in', res = 300)
ts.plot(consommation, consommation_cvs, xlab="t", ylab="consommations",col=c(1,2),lwd=c(1,2))
title("Consommation réelle sans les variations saisonnières")
ts.plot(consommation, consommation_cvs, xlab="t", ylab="consommations",col=c(1,2),lwd=c(1,2))
title("Consommation réelle sans les variations saisonnières")

3.2 saisonnalité sur la consommation corrigée ( par les DJU )

corrigee_cvs = corrigee-corrigee_dec$seasonal
png("img/07 - consoCorrigeeDessainalisee.png", width = 7.25, height = 4.5, units = 'in', res = 300)
ts.plot(corrigee, corrigee_cvs, xlab="temps", ylab="consommation",col=c(1,2),lwd=c(1,2))
legend("bottomleft",legend=c("corrigée","désaisonnalisée"),col=c(1,2),lwd=c(1,2))
title("Consommation corrigée des variations saisonnières")
ts.plot(corrigee, corrigee_cvs, xlab="temps", ylab="consommation",col=c(1,2),lwd=c(1,2))
legend("bottomleft",legend=c("corrigée","désaisonnalisée"),col=c(1,2),lwd=c(1,2))
title("Consommation corrigée des variations saisonnières")

le test de Ljung-Box nous permet de confirmer la blancheur des résidus. Ils sont donc indépendants du temps si les p-value de chaque lag est >5%

Box.test.2(corrigee_dec$random,nlag = c(6,12,18,24,30,36), type = "Ljung-Box",decim = 5)
  • -> le test de blancheur n’est pas rejeté donc le modèle que l’on vient de trouver est correct

test de la normalité des résidus avec la méthode Shapiro

shapiro.test(corrigee_dec$random)
  • -> p-value > 5% donc les résidus respectent une loi normale

—————————

4 PREDICTION - HOLT WINTERS

—————————

prédiction méthode de Holt Winters (lissage exponentiel) sur la série corrigée modèle AAA pour prendre en compte l’erreur, la tendance et la saisonnalité

4.1 Holts-Winters - application de la méthode

howi=ets(corrigee,model="AAA")
summary(howi)
  • -> les smooting parameters aident à déterminer si la prédiction se fait par rapport aux dernières valeurs ou à des valeurs plus anciennes. Plus on est proche de 1 plus les dernières valeurs comptent pour prédire les nouvelles. Plus on s’approche de zéro plus il faudra se baser sur des valeurs anciennes.

4.2 Analyse a posteriori Holt Winters pour vérifier l’efficacité de ce modèle sur un tronc connu

On tronque la série de la période de janvier 2019 à janvier 2020, et on la prévoit sur les données précédentes

x_tronc=window(corrigee,end=c(2019,4))
x_a_prevoir=window(corrigee,start=c(2019,5))

On applique la méthode sur cette série tronquée

modelhowi_tronc=ets(x_tronc,model="AAA")
summary(modelhowi_tronc)

Estimation de la prédiction et de l’intervalle de 95% en plus et en moins

pred_modelhowi_tronc=forecast(modelhowi_tronc,h=12,level=95)
predhowi_tronc=pred_modelhowi_tronc$mean
predhowi_l_tronc=ts(pred_modelhowi_tronc$lower,start=c(2019,5),frequency=12)
predhowi_u_tronc=ts(pred_modelhowi_tronc$upper,start=c(2019,5),frequency=12)

affichage du résultat

png("img/08 - analysePosterioriHW.png", width = 7.25, height = 4.5, units = 'in', res = 300)
ts.plot(x_a_prevoir,predhowi_tronc,predhowi_l_tronc,predhowi_u_tronc,xlab="t",ylab="Consommation corrigée",col=c(1,2,3,3),lty=c(1,1,2,2),lwd=c(3,3,2,2))
legend("topleft",legend=c("X","X_prev"),col=c(1,2,3,3),lty=c(1,1),lwd=c(3,3))
legend("topright",legend=c("int95%_inf","int95%_sup"),col=c(3,3),lty=c(2,2),lwd=c(2,2))
title("Analyse a posteriori : Holt Winters")
ts.plot(x_a_prevoir,predhowi_tronc,predhowi_l_tronc,predhowi_u_tronc,xlab="t",ylab="Consommation corrigée",col=c(1,2,3,3),lty=c(1,1,2,2),lwd=c(3,3,2,2))
legend("topleft",legend=c("X","X_prev"),col=c(1,2,3,3),lty=c(1,1),lwd=c(3,3))
legend("topright",legend=c("int95%_inf","int95%_sup"),col=c(3,3),lty=c(2,2),lwd=c(2,2))
title("Analyse a posteriori : Holt Winters")
  • -> La prédiction est bien contenue dans l’intervealle des 95%
  • -> L’erreur de prédiction finale est due à un événément exceptionnel, température élevée pour un mois de janvier donc arrêt prématuré des chauffages electriques

4.3 Les résidus

Box.test.2(pred_modelhowi_tronc$residuals,nlag = c(6,12,18,24,30,36), type = "Ljung-Box",decim = 5)

les rédisus sont indépendants, il s’agit d’un bruit blanc, le modèle que l’on vient de trouver est correct

Normalité des résidus - méthode Shapiro

shapiro.test(pred_modelhowi_tronc$residuals)

Les résidus suivent une loi normale car p-value > 5%

4.4 Précision : RMSE et MAPE

rmse_howi=sqrt(mean((x_a_prevoir-predhowi_tronc)^2))
mape_howi=mean(abs(1-predhowi_tronc/x_a_prevoir))*100
cat('RMSE Holt Winters : ',rmse_howi,'\n')
cat('MAPE Hot Winters : ',mape_howi,'\n')

4.5 Prévision à l’aide du modèle Holt Winters sur un an

pred_model_howi=forecast(howi,h=12,level=95) # prédiction modèle HW sur séries de 12 avec un niveau de 5%
pred_howi=pred_model_howi$mean # moyenne 
pred_howi_l=ts(pred_model_howi$lower,start=c(2020,5),frequency=12) # estimation basse de l'intervalle
pred_howi_u=ts(pred_model_howi$upper,start=c(2020,5),frequency=12) # estimation haute

sauvegarde image

png("img/09 - previsionHW.png", width = 7.25, height = 4.5, units = 'in', res = 300)
ts.plot(corrigee,pred_howi,pred_howi_l,pred_howi_u,xlab="t",ylab="Consommation corrigée",col=c(1,2,3,3),lty=c(1,1,2,2),lwd=c(1,3,2,2))
legend("topleft",legend=c("int95%_inf","int95%_sup"),col=c(3,3),lty=c(2,2),lwd=c(2,2))
title("Prévision : Holt Winters")
ts.plot(corrigee,pred_howi,pred_howi_l,pred_howi_u,xlab="t",ylab="Consommation corrigée",col=c(1,2,3,3),lty=c(1,1,2,2),lwd=c(1,3,2,2))
legend("topleft",legend=c("int95%_inf","int95%_sup"),col=c(3,3),lty=c(2,2),lwd=c(2,2))
title("Prévision : Holt Winters")

Enregistrement en image

png("img/10 - previsionHW_ZOOM.png", width = 7.25, height = 4.5, units = 'in', res = 300)
ts.plot(window(corrigee,start=c(2019,5)),pred_howi,pred_howi_l,pred_howi_u,xlab="t",ylab="Consommation corrigée",col=c(1,2,3,3),lty=c(1,1,2,2),lwd=c(1,3,2,2))
legend("topleft",legend=c("int95%_inf","int95%_sup"),col=c(3,3),lty=c(2,2),lwd=c(2,2))
title("Prévision : Holt Winters - ZOOM ")
ts.plot(window(corrigee,start=c(2019,5)),pred_howi,pred_howi_l,pred_howi_u,xlab="t",ylab="Consommation corrigée",col=c(1,2,3,3),lty=c(1,1,2,2),lwd=c(1,3,2,2))
legend("topleft",legend=c("int95%_inf","int95%_sup"),col=c(3,3),lty=c(2,2),lwd=c(2,2))
title("Prévision : Holt Winters - ZOOM ")

—————————

5 Prédiction - SARIMA

—————————

méthode SARIMA - Arima sur série avec Saisonnarité : Auto Regressive Integration Mean Average donne les valeurs p / d / q P/D/Q

Cette methode s’applique sur les séries stationnaires, si on peut le supposer visuellement, on peut utiliser une fonction qui va donner un résultat ( pour pouvoir l’intégrer dans des algorythmes d’analyse par exemple )

5.1 STATIONARITÉ

Pour confirmer que la série n’est pas stationnaire on peut utiliser ADF Augmented Dickey-Fuller

# la série est-elle stationnaire ?^
stationnaire_test<-adf.test(corrigee,k=12)
stationnaire_test

Analyse : l’hypothèse H0 est que la série est stationnaire.

or la p-value est supérieure à 5% donc on rejette H0 et on retient l’Hypothèse alternative Ha, la série n’est pas stationnaire.

il faut en premier stationnariser la série temporelle par différenciation

5.2 Stationnarisation

Autocorrélogramme Simple

acf=acf(corrigee,lag.max=36,ylim=c(-1,1))
acf$lag=acf$lag*12

sauvegarde de l’image

png("img/11 - ACF01 .png", width = 7.25, height = 4.5, units = 'in', res = 300)
acf(corrigee,lag.max=36,ylim=c(-1,1))

la décroissance est très lente, ce qui empêche cette sortie ACF d’être un autocorrélogramme simple

les traits bleus pointillés sont les limites de la significativité à 95%

5.2.1 Différenciation avec une saisonnalité de 12

y_dif_1_12=diff(corrigee,lag = 12,differences = 1)
acf=acf(y_dif_1_12,lag.max=36,ylim=c(-1,1), plot=FALSE)
acf$lag=acf$lag*12
plot(acf)

sauvegarde de l’image

png("img/12 - ACF02-différenciée-Lag12 .png", width = 7.25, height = 4.5, units = 'in', res = 300)
acf(y_dif_1_12,lag.max=36,ylim=c(-1,1))

###€ La sortiE ACF de la série différenciée semble pouvoir être interprétée comme un autocorrélogramme simple empirique. On ne voit plus les décroissances lentes précédentes. La série est donc potentiellement stationnaire. On identifiera donc un modèle SARIMA . ## 5.2.2 ACF - La série est-elle stationnaire ?

stationnaire_test<-adf.test(y_dif_1_12)
stationnaire_test

p-value de l’adf < 5% donc série potentiellement stationnaire

5.2.3 PACF ( partialy auto correlation function)

pacf=pacf(y_dif_1_12,lag.max=36,ylim=c(-1,1), plot=FALSE)
pacf$lag=pacf$lag*12
plot(pacf,ylim = c(-1,1))

IMAGE sauvegarde de l’image

png("img/13 - PACF01-différenciée-Lag12 .png", width = 7.25, height = 4.5, units = 'in', res = 300)
pacf(y_dif_1_12,lag.max=36,ylim=c(-1,1))

La série temporelle étant potentiellement stationnaire, on passe à l’identification de modèle

5.3 RECHERCHE D’UN MODÈLE SARIMA POUR LA PRÉDICTION

On va rechercher de façon empirique quelle sera la meilleure combinaison des coefficients pour le modèle

SARIMA (p,d,q)(P,D,Q)[12] pour une saisonnalité de 12

p pour AR, P pour SAR, d pour la tendance, D pour la saisonnalité, q pour MA et Q pour SMA

deux fonctions nous aident à différencier et choisir les coefficients du modèle pour d et D

d = ndiffs(corrigee)
D = nsdiffs(corrigee)
cat('d = ',d,' - différentiation en tendances / D = ', D,'différenciations en saisonnalité')

on va travailler sur la série corrigée et on y applique les paramètres que nous venons de trouver et nous appliquerons la methode CSS-ML ( maximum de vraisemblance conditionnelle )

5.3.1 SARIMA (000)(111)

————————–

5.3.1.1 Modélisation

model1=Arima(corrigee,order=c(0,0,0),list(order=c(1,1,1),period=12),include.mean=FALSE,method="CSS-ML")
summary(model1)

5.3.1.2 Coefficients

t_stat(model1)
  • P-valeurs > 5% : Les coefficients ne sont pas significatifs

5.3.1.3 Test de blancheur - Ljung-Box

Box.test.2(model1$residuals,nlag = c(6,12,18,24,30,36), type = "Ljung-Box",decim = 5)
  • -> P-valeurs > 5% : les résidus sont un bruit blanc

5.3.1.4 Normalité des résidus - méthode Shapiro

shapiro.test(model1$residuals)

5.3.2 SARIMA (001)(111)

————————-

5.3.2.1 Modélisation

model2=Arima(corrigee,order=c(0,0,1),list(order=c(1,1,1),period=12),include.mean=FALSE,method="CSS-ML")
summary(model2)

5.3.2.2 Coefficients

t_stat(model2)
  • -> P-valeurs > 5% : Les coefficients ne sont pas significatifs sauf sma1

5.3.2.3 Test de blancheur - Ljung-Box

Box.test.2(model2$residuals,nlag = c(6,12,18,24,30,36), type = "Ljung-Box",decim = 5)
  • -> P-valeurs > 5% : les résidus sont un bruit blanc

5.3.2.4 Normalité des résidus - Shapiro

shapiro.test(model2$residuals)

5.3.3 SARIMA (101)(111)

—————–

5.3.3.1 Modélisation

model3=Arima(corrigee,order=c(1,0,1),list(order=c(1,1,1),period=12),include.mean=FALSE,method="CSS-ML")
summary(model3)

5.3.3.2 Coefficients

t_stat(model3)
  • -> P-valeurs > 5% : Les coefficients ne sont pas significatifs sauf sma1

5.3.3.3 Test de blancheur - Ljung-Box

Box.test.2(model3$residuals,nlag = c(6,12,18,24,30,36), type = "Ljung-Box",decim = 5)
  • -> P-valeurs > 5% : les résidus sont un bruit blanc

5.3.3.4 Normalité des résidus - Shapiro

shapiro.test(model3$residuals)
  • -> la p-valeur étant inférieure à 5% l’hypothèse de normalité est rejetée.

5.3.4 SARIMA (011)(011)

—————–

5.3.4.1 Modélisation

model4=Arima(corrigee,order=c(0,1,1),list(order=c(0,1,1),period=12),include.mean=FALSE,method="CSS-ML")
summary(model4)

5.3.4.2 Coefficients

t_stat(model4)
  • -> P-valeurs > 5% : Les coefficients sont significatifs

5.3.4.3 Test de blancheur - Ljung-Box

Box.test.2(model4$residuals,nlag = c(6,12,18,24,30,36), type = "Ljung-Box",decim = 5)
  • -> P-valeurs > 5% : les résidus sont un bruit blanc

5.3.4.4 Normalité des résidus - Shapiro

shapiro.test(model4$residuals)
  • -> la p-valeur étant supérieure à 5% l’hypothèse de normalité n’est pas rejetée, donc les résidus sont potentiellement gaussiens

5.3.5 SARIMA (101)(011)

—————–

5.3.5.1 Modélisation

model5=Arima(corrigee,order=c(1,0,1),list(order=c(0,1,1),period=12),include.mean=FALSE,method="CSS-ML")
summary(model5)

5.3.5.2 Coefficients

t_stat(model5)
  • -> P-valeurs > 5% : Les coefficients sont significatifs

5.3.5.3 Test de blancheur - Ljung-Box

Box.test.2(model5$residuals,nlag = c(6,12,18,24,30,36), type = "Ljung-Box",decim = 5)
  • -> P-valeurs > 5% : les résidus sont un bruit blanc

5.3.5.4 Normalité des résidus - Shapiro

shapiro.test(model5$residuals)

—————————

6 SARIMA AUTO

—————————

6.1 Modélisation

model_auto <- auto.arima(corrigee, stepwise=TRUE, approximation=TRUE, allowdrift=FALSE)

6.2 Coefficients

t_stat(model_auto)
  • -> P-valeurs > 5% : Les coefficients ne sont pas significatifs sauf sma1

6.3 Test de blancheur - Ljung-Box

Box.test.2(model_auto$residuals,nlag = c(6,12,18,24,30,36), type = "Ljung-Box",decim = 5)
  • -> P-valeurs > 5% : les résidus sont un bruit blanc

6.4 Normalité des résidus - Shapiro

shapiro.test(model_auto$residuals)

—————————

7 Comparaison des modèles SARIMA 4 et 5 sur la période tronquée

—————————

7.1 MODÈLE 5

7.1.1 Modélisation

model5tronc=Arima(x_tronc,order=c(1,0,1),list(order=c(0,1,1),period=12),include.mean=FALSE,method="CSS-ML")
summary(model5tronc)

7.1.2 Coefficients

t_stat(model5tronc)

seul SMA1 est significatif

7.1.3 Test de blancheur - Ljung-Box

Box.test.2(model5tronc$residuals,nlag=c(6,12,18,24,30,36),type="Ljung-Box",decim=5)
  • -> P-valeurs > 5% : les résidus sont un bruit blanc

7.1.4 Normalité des résidus - Shapiro

shapiro.test(model5tronc$residuals)
  • -> la p-valeur étant supérieure à 5% l’hypothèse de normalité n’est pas rejetée, donc les résidus sont potentiellement gaussiens

7.2 MODÈLE 4

7.2.1 Modélisation

model4tronc=Arima(x_tronc,order=c(0,1,1),list(order=c(0,1,1),period=12),include.mean=FALSE,method="CSS-ML")
summary(model3tronc)

7.2.2 Coefficients

t_stat(model4tronc)

les coefficients sont significatifs

7.2.3 Test de blancheur - Ljung-Box

Box.test.2(model4tronc$residuals,nlag=c(6,12,18,24,30,36),type="Ljung-Box",decim=5)
  • -> P-valeurs > 5% : les résidus sont un bruit blanc

7.2.4 Normalité des résidus - Shapiro

shapiro.test(model4tronc$residuals)
  • -> la p-valeur étant supérieure à 5% l’hypothèse de normalité n’est pas rejetée, donc les résidus sont potentiellement gaussiens

7.3 CHOIX DU MODÈLE SARIMA : modèle 4

le modèle retenu est le modèle 4 SARIMA (0,1,1 ) (0,1,1) [12]

  • pour lequel les coefficients sont significatifs
  • les résidus sont des bruits blancs

7.4 PRECISION DU MODÈLE SUR INTERVALLE CONNU

pred_model4tronc=forecast(model4tronc,h=12,level=95)
pred4_tronc=pred_model4tronc$mean
pred4_l_tronc=ts(pred_model4tronc$lower,start=c(2019,5),frequency=12)
pred4_u_tronc=ts(pred_model4tronc$upper,start=c(2019,5),frequency=12)
ts.plot(window(x_a_prevoir),pred4_tronc,pred4_l_tronc,pred4_u_tronc,xlab="t",ylab="Consommation corrigée",col=c(1,2,3,3),lty=c(1,1,2,2),lwd=c(3,3,2,2))
legend("topleft",legend=c("X","X_prev"),col=c(1,2,3,3),lty=c(1,1),lwd=c(3,3))
legend("topright",legend=c("int95%_inf","int95%_sup"),col=c(3,3),lty=c(2,2),lwd=c(2,2))
title("Analyse a posteriori : SARIMA ZOOM modèle 4 (0,1,1)(0,1,1)[12]")

IMAGE - sauvegarde du graphique

png("img/14 - Analyse a posteriori SARIMA ZOOM modèle 4.png", width = 7.25, height = 4.5, units = 'in', res = 300)
ts.plot(window(x_a_prevoir),pred4_tronc,pred4_l_tronc,pred4_u_tronc,xlab="t",ylab="Consommation corrigée",col=c(1,2,3,3),lty=c(1,1,2,2),lwd=c(3,3,2,2))
legend("topleft",legend=c("X","X_prev"),col=c(1,2,3,3),lty=c(1,1),lwd=c(3,3))
legend("topright",legend=c("int95%_inf","int95%_sup"),col=c(3,3),lty=c(2,2),lwd=c(2,2))
title("Analyse a posteriori : SARIMA ZOOM modèle 4 (0,1,1)(0,1,1)[12]")

Précision du modèle 4 avec RMSE et MAPE

rmse_sarima=sqrt(mean((x_a_prevoir-pred4_tronc)^2))
mape_sarima=mean(abs(1-pred4_tronc/x_a_prevoir))*100
cat('RMSE SARIMA : ',rmse_sarima,'\n')
cat('MAPE SARIMA : ',mape_sarima,'%\n')

7.5 PRÉVISION SUR UN AN

pred_model4=forecast(model4,h=12,level=95) # prédiction modèl3 sur séries de 12 avec un niveau de 5%
pred4=pred_model4$mean
pred4_l=ts(pred_model4$lower,start=c(2020,5),frequency=12)
pred4_u=ts(pred_model4$upper,start=c(2020,5),frequency=12) # estimation haute 
ts.plot(corrigee,pred4,pred4_l,pred4_u,xlab="temps",ylab="Consommation corrigée",col=c(1,2,3,3),lty=c(1,1,2,2),lwd=c(1,3,2,2))
legend("topleft",legend=c("int95%_inf","int95%_sup"),col=c(3,3),lty=c(2,2),lwd=c(2,2))
title("Prévision : SARIMA")
ts.plot(window(corrigee,start=c(2018,5)),pred4,pred4_l,pred4_u,xlab="temps",ylab="Conso corrigée",col=c(1,2,3,3),lty=c(1,1,2,2),lwd=c(1,3,2,2))
legend("topleft",legend=c("int95%_inf","int95%_sup"),col=c(3,3),lty=c(2,2),lwd=c(2,2))
title("Prévision : SARIMA - Zoom ")
png("img/15 - Prédiction SARIMA ZOOM avec modèle 4.png", width = 7.25, height = 4.5, units = 'in', res = 300)
ts.plot(window(corrigee,start=c(2018,5)),pred4,pred4_l,pred4_u,xlab="temps",ylab="Conso corrigée",col=c(1,2,3,3),lty=c(1,1,2,2),lwd=c(1,3,2,2))
legend("topleft",legend=c("int95%_inf","int95%_sup"),col=c(3,3),lty=c(2,2),lwd=c(2,2))
title("Prévision : SARIMA - Zoom ")

—————————

8 BILAN - Comparaison des modèles les plus performants holt-winters et SARIMA

—————————

8.1 Analyses a postériori superposées

ts.plot(window(x_a_prevoir),pred4_tronc,predhowi_tronc,xlab="t",ylab="Consommation corrigée",col=c(1,2,3),lty=c(1,1,1),lwd=c(3,3,3))
legend("topleft",legend=c("Réelle","SARIMA"),col=c(1,2),lty=c(1,1),lwd=c(3,3))
legend("topright",legend=c("Holt Winters"),col=c(3),lty=c(1),lwd=c(3))
title("Analyse a posteriori : SARIMA + Holt Winters ")
png("img/16 - Analyse Posteriori SARIMA et Holt-Winters.png", width = 7.25, height = 4.5, units = 'in', res = 300)
ts.plot(window(x_a_prevoir),pred4_tronc,predhowi_tronc,xlab="t",ylab="Consommation corrigée",col=c(1,2,3),lty=c(1,1,1),lwd=c(3,3,3))
legend("topleft",legend=c("Réelle","SARIMA"),col=c(1,2),lty=c(1,1),lwd=c(3,3))
legend("topright",legend=c("Holt Winters"),col=c(3),lty=c(1),lwd=c(3))
title("Analyse a posteriori : SARIMA + Holt Winters ")

8.2 Précision de la prédiction avec RMSE et MAPE

RMSE est dépendant de l’échelle, MAPE indice en pourcentage

cat('RMSE Holt Winters : ',rmse_howi,'\n')
cat('RMSE SARIMA : ',rmse_sarima,'\n')
cat('MAPE Hot Winters : ',mape_howi,'\n')
cat('MAPE SARIMA : ',mape_sarima,'\n')
  • -> Ces indicateurs mettent en évidence les écarts par rapport à la réalité, plus ils sont faibles, plus ils sont proches des vrais valeurs et donc ils sont meilleurs.
  • -> Ainsi, la méthode la meilleure est, pour ce cas, SARIMA

—————————

MODÈLE RETENU SARIMA (0.1.1)(0.1.1)[12]

—————————

LS0tCnRpdGxlOiAiUiBOb3RlYm9vayAtIFBST0pFVCA5IC0gUEVZUk9OTkUgRGF2aWQiCm91dHB1dDoKICBodG1sX25vdGVib29rOiBkZWZhdWx0CiAgcGRmX2RvY3VtZW50OiAKICAgIHRvYzogeWVzCiAgICBsYXRleF9lbmdpbmU6IGx1YWxhdGV4CiAgaHRtbF9kb2N1bWVudDoKICAgIGRmX3ByaW50OiBwYWdlZAotLS0KCiMjIyAtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tCiMjIFJhcHBlbCBkdSBwcm9qZXQKIyMjIFByw6lkaXJlIGxhIGNvbnNvbW1hdGlvbiDDqWxlY3RyaXF1ZQojIyMgLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQoKIyMgRG9ubsOpZXMKIyMgQW5hbHlzZXMKIyMjIEFmaW4gZCdpbnRyb2R1aXJlIHZvdHJlIGFuYWx5c2UsIGVmZmVjdHVleiB1bmUgYnLDqHZlIGRlc2NyaXB0aW9uIGRlcyBkb25uw6llcyAoYW5hbHlzZXMgdW5pdmFyacOpZXMgZXQgYml2YXJpw6llcykuCiMjIEJpbGFuCiMjIFF1ZXN0aW9ucyAvIFLDqXBvbnNlcwoKIyAjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjCiMgRG9ubsOpZXMKIyAjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjCgojIC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQojIDEgSW1wb3J0YXRpb24gZGVzIGRvbm7DqWVzCiMgLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tCgojIyAxLjEgU291cmNlcyBkZXMgZG9ubsOpZXMKKiAtPiBKZXUgZGUgZG9ubsOpZXMgZmljaGllciB4bHN4IAoKIyMgMS4yIEluc3RhbGxhdGlvbiBkZXMgcGFja2FnZXMgCiogLT4gTCdpbnN0YWxsYXRpb24gZGVzIHBhY2thZ2VzIG5lIHNlIGZhaXQgcXUndW5lIHNldWxlIGZvaXMsIGplIGxlcyBhaSBjb21tZW50w6llcyBwb3VyIMOpdml0ZXIgZGUgcsOpaXNudGFsbGVyIMOgIGNoYXF1ZSBjaGFyZ2VtZW50IGR1IHNjcmlwdCAqCmBgYHtyIEluc3RhbGxhdGlvbiBkZXMgcGFja2FnZXMsIGVjaG89VFJVRX0KI2luc3RhbGwucGFja2FnZXMoYygiRmFjdG9zaGlueSIpKQojaW5zdGFsbC5wYWNrYWdlcygiZ2dwdWJyIikKI2luc3RhbGwucGFja2FnZXMoInJKYXZhIikKI2luc3RhbGwucGFja2FnZXMoInhsc3giKSAKI2luc3RhbGwucGFja2FnZXMoInRzZXJpZXMiKQojaW5zdGFsbC5wYWNrYWdlcygiZGF0YXNlcmllcyIpCiNpbnN0YWxsLnBhY2thZ2VzKCJ0aWR5dmVyc2UiKQojaW5zdGFsbC5wYWNrYWdlcygiY2FzY2hyb25vIikKCmBgYAojIyMgMS4zIEluc3RhbGxhdGlvbiBkZXMgbGlicmFyaWVzCiogLT4gTCdhY3RpdmF0aW9uIGRlcyBsaWJyYWlyaWVzIHNlIGZhaXQgw6AgY2hhcXVlIGTDqW1hcnJhZ2UgZHUgc2NyaXB0KiAgCiogLT4gY29ycnBsb3QgZG9pdCDDqnRyZSBhcHBlbMOpIGFwcsOocyBnZ3Bsb3QyIHBvdXIgw6l2aXRlciB1biB3YXJuaW5nKgpgYGB7ciBJbnN0YWxsYXRpb24gZGVzIGxpYnJhcmllcywgcmVzdWx0cz1GQUxTRX0KbGlicmFyeSh0c2VyaWVzKQpsaWJyYXJ5KHpvbykgIyBwb3VyIGxlIHRyYWl0ZW1lbnQgZGVzIGRvbm7DqWVzIGVuIERhdGUgKCB5ZWFybW9uICkKbGlicmFyeSh0aWR5dmVyc2UpCmxpYnJhcnkoZGF0YXNlcmllcykKbGlicmFyeShjYXNjaHJvbm8pCmxpYnJhcnkoeGxzeCkKbGlicmFyeSgiZ2dwbG90MiIpCiNsaWJyYXJ5KGRwbHlyKQpsaWJyYXJ5KGNvcnJwbG90KSAKbGlicmFyeShmb3JlY2FzdCkgIyBwb3VyIGxlcyBmb25jdGlvbnMgQXJpbWEgZXRjCmBgYAojIyMgMS40IENvbmZpZ3VyYXRpb24gZGVzIHZhcmlhYmxlcyBwb3VyIGNoYXF1ZSBmaWNoaWVyCiogLT4gZMOpZmluaXRpb24gZCd1bmUgdmFyaWFibGUgcsOpcGVydG9pcmUgcG91ciBmYWNpbGl0ZXIgbGUgY2hhbmdlbWVudCBkJ09TICoKYGBge3IgSW1wb3J0YXRpb24gZGVzIGRvbm7DqWVzfQpyZXBlcnRvaXJlX3NvdXJjZXMgPC0gIi9ob21lL3VzZXIvRG9jdW1lbnRzL2Zvcm1hdGlvbnMvb2MvRm9ybWF0aW9uIEFuYWx5c3QvUDkvc291cmNlcy8iCnJlcGVydG9pcmVfaW1hZ2VzIDwtICIvaG9tZS91c2VyL0RvY3VtZW50cy9mb3JtYXRpb25zL29jL0Zvcm1hdGlvbiBBbmFseXN0L1A5L2ltYWdlcy8iCmZpY2hpZXIgPC0gcGFzdGUgKHJlcGVydG9pcmVfc291cmNlcywidGFiLmNzdiIsc2VwID0gIiIpCmBgYAojIyMgMS41IENyw6lhdGlvbiBkZXMgRGF0YSBGcmFtZSBwYXIgbGVjdHVyZSBkdSBmaWNoaWVyIHRlbXDDqXJhdHVyZXMKKiAtPiBsZXMgY3JpdMOocmVzIGQnaW1wb3J0YXRpb24sIHPDqXBhcmF0ZXVycywgdmlyZ3VsZXMgZMOpY2ltYWxlcywgZW50w6p0ZSBldGMsIG9udCDDqXTDqXMgZMOpZmluaXMgYXByw6hzIG9ic2VydmF0aW9uIGRlcyBmaWNoaWVycyBzb3VyY2VzIMOgIGwnYWlkZSBkJ3VuZSDDqWRpdGV1ciBkZSB0ZXh0ZSBvdSBkJ3VuIHRhYmxldXIqCmBgYHtyIENyw6lhdGlvbiBkZXMgREYgcGFyIGxlY3R1cmUgZGVzIGZpY2hpZXJzfQpkYXRhIDwtIHJlYWQudGFibGUoZmljaGllciwgaGVhZGVyPVRSVUUsIHNlcD0nLCcsIGZpbGVFbmNvZGluZyA9ICJJU08tODg1OS0xNSIpCmhlYWQoZGF0YSkKYGBgCgojIyMjIHZhbGV1cnMgbWF4aSBtaW5pIGRlIGxhIHDDqXJpb2RlCmBgYHtyfQpkYXRhX21heD1tYXgoZGF0YSRNb2lzKQpkYXRhX21pbj1taW4oZGF0YSRNb2lzKQpjYXQoIkxhIHPDqXJpZSBkZSBkb25uw6llcyBlc3QgZW50cmUgOiAiICwgZGF0YV9taW4sImV0IDogIiAsIGRhdGFfbWF4KQpgYGAKCiMjIyMgT24gZ2FyZGUgbGVzIGNvbnNvbW1hdGlvbnMgZGUgbGEgRnJhbmNlIHBvdXIgbGVzIGRvbm7DqWVzIGV0IAojIyMjIE9uIHbDqXJpZmllIHF1J2lsIG4neSBhaXQgcGFzIGRlIHZhbGV1cnMgbnVsbGVzIHBvdXIgbGEgY29uc29tbWF0aW9uCmBgYHtyfQpkZl9mciA8LSBzdWJzZXQoZGF0YSwgVGVycml0b2lyZSA9PSAiRnJhbmNlIikKZGZfZnIgPC0gZGZfZnJbLGMoIk1vaXMiLCJDb25zb21tYXRpb24udG90YWxlIildCnNhcHBseShkZl9mciwgZnVuY3Rpb24oeCkgc3VtKGlzLm5hKHgpKSkKYGBgCiMjIyBPbiBjb252ZXJ0aSBsYSB2YXJpYWJsZSBNb2lzIGVuIERhdGUKYGBge3J9CmRmX2ZyJE1vaXM8LWFzLkRhdGUoYXMueWVhcm1vbihkZl9mciRNb2lzLCAiJVktJW0iKSkKYGBgCgojIyMjIGFmZmljaGFnZSBkZSBsYSBzw6lyaWUgdGVtcG9yZWxsZQpgYGB7cn0KZGZfZnIgJT4lCiAgZ2dwbG90KGFlcyh4ID0gTW9pcywgeSA9IGBDb25zb21tYXRpb24udG90YWxlYCkpICsKICBnZW9tX2xpbmUoKQogIGdnc2F2ZSgiaW1nLzAxIC0gY29uc29tbWF0aW9uVG90YWxlLnBuZyIsIHdpZHRoID0gNy4yOSwgaGVpZ2h0ID0gNC41KQpgYGAKKiAtPiBPbiBwZXV0IHZvaXIgdW5lIHNhaXNvbm5hbGl0w6kgZGUgbGEgY29uc29tbWF0aW9uLgoqIC0+IFVuIHBpYyBkZSBjb25zb21tYXRpb24gaW1wb3J0YW50IGVuIHDDqXJpb2RlIGhpdmVybmFsZSBsacOpIGF1IGNoYXVmZmFnZS4gRXQgdW4gcGx1cyBwZXRpdCBwaWMgZW4gcGxlaW4gw6l0w6kgbGnDqSDDoCBsJ3V0aWxpc2F0aW9uIGRlIGxhIGNsaW1hdGlzYXRpb24uIE9uIHBldXQgc3VwcG9zZXIgcXUnaWwgcydhZ2l0IGQndW5lIHPDqXJpZSB0ZW1wb3JlbGxlIGFkZGl0aXZlLgoKCiMjIDEuNiBDcsOpYXRpb24gZGVzIERhdGEgRnJhbWUgREpVIHBhciBsZWN0dXJlIGR1IGZpY2hpZXIKYGBge3J9CmZpY2hpZXJfZGp1IDwtIHBhc3RlIChyZXBlcnRvaXJlX3NvdXJjZXMsImRqdS5jc3YiLHNlcCA9ICIiKQpkanUgPC0gcmVhZC5jc3YyKGZpY2hpZXJfZGp1LGhlYWRlciA9IFRSVUUsc2VwID0gIiwiKQpgYGAKCiMjIyMgY29udmVyc2lvbiBkdSBjaGFtcCBNb2lzIGF1IGZvcm1hdCBkYXRlCgpgYGB7cn0KZGp1JE1vaXM8LWFzLkRhdGUoYXMueWVhcm1vbihkanUkTW9pcywgIiVZLSVtIikpCmBgYAoKIyMjIyBhZmZpY2hhZ2UgZGUgbGEgc8OpcmllIHRlbXBvcmVsbGUKYGBge3J9CmRqdSAlPiUKICBnZ3Bsb3QoYWVzKHggPSBNb2lzLCB5ID0gYERKVWApKSArCiAgZ2VvbV9saW5lKCkKICBnZ3NhdmUoImltZy8wMiAtIERKVS5wbmciLCB3aWR0aCA9IDcuMjksIGhlaWdodCA9IDQuNSkKYGBgCiogLT4gT24gY29uc3RhdGUgcXUnaWwgeSBhIGxhIG3Dqm1lIHNhaXNvbm5hcml0w6kgcXVlIGxhIGNvbnNvbW1hdGlvbgoKIyMgMS43IENvbWJpbmVyIGxlcyBkZXV4IHRhYmxlYXV4IGRlIGRvbm7DqWVzIHBvdXIgbGVzIGRhdGVzIGNvbW11bmVzCiMjIyMgb24gb2JzZXJ2ZSBzdXIgbGUgZ3JhcGhpcXVlIHByw6ljw6lkZW50IHF1ZSBsZXMgZGVybmnDqHJlcyB2YWxldXJzIHNvbnQgw6AgesOpcm8KYGBge3J9CnRhaWwoZGp1LDEwKQpgYGAKIyMjIyBvbiB2w6lyaWZpZSBwb3VyIGxlcyBjb25zb21tYXRpb25zIDoKYGBge3J9CnRhaWwoZGYsMTApCmBgYAoKIyMjIyBPbiByZXN0cmFpbiBsZSBqZXUgZGUgZG9ubsOpZXMgZW50cmUgamFudmllciAyMDEyIGV0IG1haSAyMDIwIGluY2x1cwojIyMjIE9uIGNvbWJpbmUgbGVzIGRldXggdGFibGVhdXggZGUgZG9ubsOpZXMKIyMjIyBPbiByZW5vbW1lIGxlcyBjb2xvbm5lcwpgYGB7cn0KZGZfZnIgPC0gc3Vic2V0KGRmX2ZyLCBNb2lzIDwgIjIwMjAtMDYtMDEiKQpkanUgPC0gc3Vic2V0KGRqdSwgTW9pcyA8ICIyMDIwLTA2LTAxIikKZGYgPC0gY2JpbmQoIGRmX2ZyLCBkanUkREpVKSAKbmFtZXMoZGYpW25hbWVzKGRmKSA9PSAnQ29uc29tbWF0aW9uLnRvdGFsZSddIDwtICdjb25zbycKbmFtZXMoZGYpW25hbWVzKGRmKSA9PSAnZGp1JERKVSddIDwtICdkanUnCm5hbWVzKGRmKVtuYW1lcyhkZikgPT0gJ01vaXMnXSA8LSAnbW9pcycKZGp1ICU+JQogIGdncGxvdChhZXMoeCA9IE1vaXMsIHkgPSBgREpVYCkpICsKICBnZW9tX2xpbmUoKQpgYGAKCgojIyAxLjggRG9ubsOpZXMgImNlbnRyw6kgcsOpZHVpdCIgcG91ciBzdXBlcnBvc2VyIGxlcyBjb3VyYmVzCmBgYHtyfQpkZl9zY2FsZWQgPC0gZGF0YS5mcmFtZShkZiwgc2NhbGUoZGZbLDI6M10pKQpnZ3Bsb3QoZGZfc2NhbGVkLCBhZXMoeCA9IG1vaXMpKSArCiAgZ2VvbV9saW5lKGFlcyh5ID0gY29uc28uMSwgY29sb3IgPSAiY29uc28iKSkgKwogIGdlb21fbGluZShhZXMoeSA9IGRqdS4xLCBjb2xvciA9ICJkanUiKSkgCiAgZ2dzYXZlKCJpbWcvMDMgLSBjb25zb21tYXRpb24gZXQgREpVLnBuZyIsIHdpZHRoID0gNy4yOSwgaGVpZ2h0ID0gNC41KQoKYGBgCiogLT4gbGVzIGNvdXJiZXMgc2Ugc3VwZXJwb3NlbnQgdHLDqHMgYmllbiDDoCBxdWVscXVlcyBleGNlcHRpb25zIHByw6hzIHRlbHMgcXVlIGxlcyBtYXhpbXVtcywgZXQgbCd1c2FnZSBkZSBsYSBjbGltYXRpc2F0aW9uIGNhciBsZSBkanUgZG9ubmUgbCfDqWNhcnQgZGUgdGVtcMOpcmF0dXJlIHBhciByYXBwb3J0IGF1IGZyb2lkIGNlIHF1aSBsYWlzc2Ugc3VwcG9zZXIgbGVzIGJlc29pbnMgZW4gY2hhdWZmYWdlIGFsb3JzIHF1ZSBsZXMgw6ljYXJ0cyBlbiDDqXTDqSBzb250IGR1cyDDoCBsYSBjbGltYXRpc2F0aW9uCgojIC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQojIDIgUsOpZ3Jlc3Npb24gbGluw6lhaXJlCiMgLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tCgojIyBwbG90IGRlcyBkb25uw6llcyAKYGBge3J9CnBsb3QoY29uc29+ZGp1LCBkYXRhID0gZGYpCmBgYAoqIC0+IG9uIHkgdm9pdCB1biBkaWFnb25hbGUgc2UgZGVzc2luZXIgb24gcGV1dCBkb25jIGVudmlzYWdlciB1bmUgcsOpZ3Jlc3Npb24gbGluw6lhaXJlCgojIyAyLjEgUGxvdCBhdmVjIGxhIHLDqWdyZXNzaW9uIGxpbsOpYWlyZQpgYGB7cn0KbW9kZWwgPC0gbG0oY29uc29+ZGp1LCBkYXRhID0gZGYpCnBsb3QoY29uc29+ZGp1LCBkYXRhID0gZGYpICsgYWJsaW5lKG1vZGVsLCBjb2wgPSAicmVkIikKYGBgCiMjIyMgSW1hZ2Ugc2F1dmVnYXJkZSBkdSBncmFwaGlxdWUKYGBge3J9CnBuZygiaW1nLzA0IC0gcmVncmVzc2lvbkxpbmVhaXJlLnBuZyIsIHdpZHRoID0gNy4yNSwgaGVpZ2h0ID0gNC41LCB1bml0cyA9ICdpbicsIHJlcyA9IDMwMCkKcGxvdChjb25zb35kanUsIGRhdGEgPSBkZikgKyBhYmxpbmUobW9kZWwsIGNvbCA9ICJyZWQiKQpgYGAKIyMjIyBTdW1tYXJ5IGR1IG1vZMOobGUgZGUgbGEgcsOpZ3Jlc3Npb24KYGBge3J9CnN1bW1hcnkobW9kZWwpCmBgYAojIyMjIEFOQUxZU0UgZHUgbW9kw6hsZQoqIC0+IGxlcyAqKiogKCB0cm9pcyDDqXRvaWxlcykgIG1vbnRyZW50IGxlcyBpbmRpY2VzIGxlcyBwbHVzIHBlcnRpbmVudHMKKiAtPiBSwrIgw6AgMC45NSBpbmRpcXVlIHF1J2lsIHkgYSB1bmUgZm9ydGUgY29ycsOpbGF0aW9uIGVudHJlIGxhIGNvbnNvbW1hdGlvbiBldCBsYSBESlUKKiAtPiBsYSBQLVZhbHVlIDwgNSUgaW5kaXF1ZSBxdWUgbGVzIGNvZWZmaWNpZW50cyBzb250IHNpZ25pZmljYXRpZnMKKiAtPiBub3VzIHLDqWN1cMOpcm9ucyBsZSBjb2ZmaWNpZW50IGRlIERKVSBwb3VyIGNvcnJpZ2VyIGxhIGNvbnNvbW1hdGlvbiBwYXIgcmFwcG9ydCBhdSBESlUKCmBgYHtyfQptb2RlbCRjb2VmZmljaWVudHNbMl0KYGBgCiMjIDIuMiBDb25zb21tYXRpb24gY29ycmlnw6llIHBhciBsYSByZWdyZXNzaW9uIGxpbsOpYWlyZQoKYGBge3J9CmRmJGNvcnJpZ2U8LWRmJGNvbnNvLW1vZGVsJGNvZWZmaWNpZW50c1syXSpkZiRkanUKZ2dwbG90KGRmLCBhZXMoeCA9IG1vaXMpKSArCiAgZ2VvbV9saW5lKGFlcyh5ID0gY29uc28sIGNvbG9yID0gInLDqWVsbGUiKSkgKwogIGdlb21fbGluZShhZXMoeSA9IGNvcnJpZ2UsIGNvbG9yID0gImNvcnJpZ8OpZSIpKQogZ2dzYXZlKCJpbWcvMDUgLSBjb25zb1JlZWxsZWV0Q29ycmlnZWUucG5nIiwgd2lkdGggPSA3LjI5LCBoZWlnaHQgPSA0LjUpCmBgYAoqIC0+ICBvbiBvYnNlcnZlIHF1ZSBtYWxncsOpIGxhIGNvcnJlY3Rpb24gb24gcGV1dCB5IHZvaXIgdW5lIHNhaXNvbm5hbGl0w6kuCiogLT4gbGEgcsOpZ3Jlc3Npb24gbmUgcmV0aXJlIHBhcyBsYSBzYWlzb25uYWxpdMOpIGRlIG5vcyBkb25uw6llcwoqIC0+IGVsbGUgYSBwZXJtaXMgZGUgbWV0dHJlIGVuIMOpdmlkZW5jZSBsZXMgcsOpc2lkdXMKCiMjIDIuMyBSw6lzaWR1cyBkZSBsYSByw6lncmVzc2lvbiBsaW7DqWFpcmUKYGBge3J9CnJlczwtcmVzaWQobW9kZWwpCnBsb3QoZGYkbW9pcyxyZXMsbWFpbj0iUsOpc2lkdXMiKSArIGFibGluZSAodiA9IGFzLkRhdGUoIjIwMTItMDgtMDEiKSAsIGNvbCA9ICJibHVlIikgKyBhYmxpbmUgKHYgPSBhcy5EYXRlKCIyMDEzLTA4LTAxIikgLCBjb2wgPSAiYmx1ZSIpICsgYWJsaW5lICh2ID0gYXMuRGF0ZSgiMjAxNC0wOC0wMSIpICwgY29sID0gImJsdWUiKSAgKyBhYmxpbmUgKHYgPSBhcy5EYXRlKCIyMDE1LTA4LTAxIikgLCBjb2wgPSAiYmx1ZSIpIC0gYWJsaW5lKGg9MCxjb2w9InJlZCIpCmBgYAoKKiAtPiBsZXMgbGlnbmVzIGJsZXVlcyBzb250IGxlcyBtb2lzIGQnYW/Du3QKKiAtPiBMZXMgcsOpc2lkdXMgcG9zaXRpZnMgbGVzIHBsdXMgaW1wb3J0YW50cyBhcHBhcmFpc3NlbnQgZW4gw6l0w6ksIGF2ZWMgbGEgZGlmZsOpcmVuY2UgZGUgY29uc29tbWF0aW9uIGTDu2UgYXUgY2xpbWF0aXNldXIgcXVpIG4nZXN0IHBhcyBwcmlzZSBlbiBjb21wdGUgYXZlYyBsZXMgY2FsY3VscyBkdSBjaGF1ZmZhZ2UuCgoKIyAjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIwojIC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQojIDMgU2Fpc29ubmFsaXTDqSAKIyAtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0KIyAjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIwoKKiAtPiBwb3VyIGTDqXNhaXNvbm5hbGlzZXIgbGEgc8OpcmllLCBvbiB2YSBsYSBkw6ljb21wb3NlcgpgYGB7cn0KY29uc29tbWF0aW9uIDwtIHRzKGRmJGNvbnNvLCBzdGFydD1jKDIwMTIsIDEpLCBlbmQ9YygyMDIwLCA0KSwgZnJlcXVlbmN5ID0gMTIpCmNvbnNvbW1hdGlvbl9kZWMgPC0gZGVjb21wb3NlKGNvbnNvbW1hdGlvbikgIywnYWRkaXRpdmUnKSBqZSBuZSB2b2lzIHBhcyBsYSBkaWZmw6lyZW5jZSBhdmVjIG91IGNldHRlIHZhbGV1ci4KcGxvdChjb25zb21tYXRpb25fZGVjKQpgYGAKCgpgYGB7cn0KcG5nKCJpbWcvMDViIC0gYm94UGxvdENvbnNvbW1hdGlvbnNNZW5zdWVsbGVzLnBuZyIsIHdpZHRoID0gNy4yNSwgaGVpZ2h0ID0gNC41LCB1bml0cyA9ICdpbicsIHJlcyA9IDMwMCkKYm94cGxvdChjb25zb21tYXRpb25+IGN5Y2xlKGNvbnNvbW1hdGlvbikpCmBgYAoKYGBge3J9CmJveHBsb3QoY29uc29tbWF0aW9ufiBjeWNsZShjb25zb21tYXRpb24pKQpgYGAKKiAtPiBjZSBib3hwbG90IG1vbnRyZSBzdXIgbCdlbnNlbWJsZSBkZXMgYW5uw6llcyBsYSBjb25zb21tYXRpb24gcG91ciBjaGFxdWUgbW9pcyAuCiogLT4gb24geSB2b2l0IHVuZSBmb3J0ZSBjb25zb21tYXRpb24gZW50cmUgbGVzIG1vaXMgMTEgZXQgMyBldCBmYWlibGVzIGVuIMOpdMOpIGF2ZWMgdW4gcGV0aXQgcmVib25kIGVuIGp1aWxsZXQgKCBjbGltYXRpc2F0aW9uICkKKiAtPiBjZXJ0YWlucyBtb2lzIG9udCBkZXMgw6ljYXJ0cyBpbXBvcnRhbnRzIGphbnZpZXIgLyBmw6l2cmllciwgZCdhdXRyZXMgc29udCB0csOocyByw6lndWxpZXJzIHRlbHMgcXVlIEp1aW4gLyBKdWlsbGV0CgoKYGBge3J9CmNvcnJpZ2VlIDwtIHRzKGRmJGNvcnJpZ2UsIHN0YXJ0PWMoMjAxMiwgMSksIGVuZD1jKDIwMjAsIDQpLCBmcmVxdWVuY3kgPSAxMikKY29ycmlnZWVfZGVjIDwtIGRlY29tcG9zZShjb3JyaWdlZSkgIywnYWRkaXRpdmUnKSBqZSBuZSB2b2lzIHBhcyBsYSBkaWZmw6lyZW5jZSBhdmVjIG91IGNldHRlIHZhbGV1ci4KYGBgCgoKYGBge3J9CnBuZygiaW1nLzA1QyAtIGRlY29tcG9zaXRpb25Db25zb0NvcnJpZ2VlLnBuZyIsIHdpZHRoID0gNy4yNSwgaGVpZ2h0ID0gNC41LCB1bml0cyA9ICdpbicsIHJlcyA9IDMwMCkKcGxvdChjb3JyaWdlZV9kZWMpCmBgYAoKYGBge3J9CnBsb3QoY29ycmlnZWVfZGVjKQpgYGAKKiAtPiBsYSB0ZW5kYW5jZSDDoCBsYSBiYWlzc2UgcXVpIG4nw6l0YWl0IHBhcyBwZXJjZXB0aWJsZSBzdXIgbGUgZ3JhcGhlIGRlcyBjb25zb21tYXRpb25zCiogLT4gbGEgc2Fpc29ubmFsaXTDqSBlc3QgY29uZmlybcOpZQoqIC0+IGxhIHZhbGV1ciByw6lzaWR1ZWxsZSBlc3QgcmVwcsOpc2VudMOpZQoKIyMgRMOpc2Fpc29ubmFsaXNhdGlvbiAtIGNvbnNvbW1hdGlvbiBjb3JyaWfDqWUgZGVzIHZhcmlhdGlvbnMgc2Fpc29ubmnDqHJlcwoqIC0+IExhIGTDqWNvbXBvc2l0aW9uIHBlcm1ldCBkJ29idGVuaXIgZXQgZGUgcmV0aXJlciBsJ2FzcGVjdCBzYWlzb25uaWVyIGQndW5lIHPDqXJpZSB0ZW1wb3JlbGxlCiMjIDMuMSBTYWlzb25uYWxpdMOpIHN1ciBsYSBjb25zb21tYXRpb24gCmBgYHtyfQpjb25zb21tYXRpb25fY3ZzID0gY29uc29tbWF0aW9uLWNvbnNvbW1hdGlvbl9kZWMkc2Vhc29uYWwKYGBgCgoKYGBge3J9CnBuZygiaW1nLzA2IC0gY29uc29EZXNzYWluYWxpc2VlLnBuZyIsIHdpZHRoID0gNy4yNSwgaGVpZ2h0ID0gNC41LCB1bml0cyA9ICdpbicsIHJlcyA9IDMwMCkKdHMucGxvdChjb25zb21tYXRpb24sIGNvbnNvbW1hdGlvbl9jdnMsIHhsYWI9InQiLCB5bGFiPSJjb25zb21tYXRpb25zIixjb2w9YygxLDIpLGx3ZD1jKDEsMikpCnRpdGxlKCJDb25zb21tYXRpb24gcsOpZWxsZSBzYW5zIGxlcyB2YXJpYXRpb25zIHNhaXNvbm5pw6hyZXMiKQpgYGAKCmBgYHtyfQp0cy5wbG90KGNvbnNvbW1hdGlvbiwgY29uc29tbWF0aW9uX2N2cywgeGxhYj0idCIsIHlsYWI9ImNvbnNvbW1hdGlvbnMiLGNvbD1jKDEsMiksbHdkPWMoMSwyKSkKdGl0bGUoIkNvbnNvbW1hdGlvbiByw6llbGxlIHNhbnMgbGVzIHZhcmlhdGlvbnMgc2Fpc29ubmnDqHJlcyIpCmBgYAoKIyMgMy4yIHNhaXNvbm5hbGl0w6kgc3VyIGxhIGNvbnNvbW1hdGlvbiBjb3JyaWfDqWUgKCBwYXIgbGVzIERKVSApCmBgYHtyfQpjb3JyaWdlZV9jdnMgPSBjb3JyaWdlZS1jb3JyaWdlZV9kZWMkc2Vhc29uYWwKYGBgCgoKYGBge3J9CnBuZygiaW1nLzA3IC0gY29uc29Db3JyaWdlZURlc3NhaW5hbGlzZWUucG5nIiwgd2lkdGggPSA3LjI1LCBoZWlnaHQgPSA0LjUsIHVuaXRzID0gJ2luJywgcmVzID0gMzAwKQp0cy5wbG90KGNvcnJpZ2VlLCBjb3JyaWdlZV9jdnMsIHhsYWI9InRlbXBzIiwgeWxhYj0iY29uc29tbWF0aW9uIixjb2w9YygxLDIpLGx3ZD1jKDEsMikpCmxlZ2VuZCgiYm90dG9tbGVmdCIsbGVnZW5kPWMoImNvcnJpZ8OpZSIsImTDqXNhaXNvbm5hbGlzw6llIiksY29sPWMoMSwyKSxsd2Q9YygxLDIpKQp0aXRsZSgiQ29uc29tbWF0aW9uIGNvcnJpZ8OpZSBkZXMgdmFyaWF0aW9ucyBzYWlzb25uacOocmVzIikKYGBgCgpgYGB7cn0KdHMucGxvdChjb3JyaWdlZSwgY29ycmlnZWVfY3ZzLCB4bGFiPSJ0ZW1wcyIsIHlsYWI9ImNvbnNvbW1hdGlvbiIsY29sPWMoMSwyKSxsd2Q9YygxLDIpKQpsZWdlbmQoImJvdHRvbWxlZnQiLGxlZ2VuZD1jKCJjb3JyaWfDqWUiLCJkw6lzYWlzb25uYWxpc8OpZSIpLGNvbD1jKDEsMiksbHdkPWMoMSwyKSkKdGl0bGUoIkNvbnNvbW1hdGlvbiBjb3JyaWfDqWUgZGVzIHZhcmlhdGlvbnMgc2Fpc29ubmnDqHJlcyIpCmBgYAoKIyMjIGxlIHRlc3QgZGUgTGp1bmctQm94IG5vdXMgcGVybWV0IGRlIGNvbmZpcm1lciBsYSBibGFuY2hldXIgZGVzIHLDqXNpZHVzLiBJbHMgc29udCBkb25jIGluZMOpcGVuZGFudHMgZHUgdGVtcHMgc2kgbGVzIHAtdmFsdWUgZGUgY2hhcXVlIGxhZyBlc3QgPjUlCmBgYHtyfQpCb3gudGVzdC4yKGNvcnJpZ2VlX2RlYyRyYW5kb20sbmxhZyA9IGMoNiwxMiwxOCwyNCwzMCwzNiksIHR5cGUgPSAiTGp1bmctQm94IixkZWNpbSA9IDUpCmBgYAoqIC0+IGxlIHRlc3QgZGUgYmxhbmNoZXVyIG4nZXN0IHBhcyByZWpldMOpIGRvbmMgbGUgbW9kw6hsZSBxdWUgbCdvbiB2aWVudCBkZSB0cm91dmVyIGVzdCBjb3JyZWN0CgojIyMgdGVzdCBkZSBsYSBub3JtYWxpdMOpIGRlcyByw6lzaWR1cyBhdmVjIGxhIG3DqXRob2RlIFNoYXBpcm8KYGBge3J9CnNoYXBpcm8udGVzdChjb3JyaWdlZV9kZWMkcmFuZG9tKQpgYGAKKiAtPiBwLXZhbHVlID4gNSUgZG9uYyBsZXMgcsOpc2lkdXMgcmVzcGVjdGVudCB1bmUgbG9pIG5vcm1hbGUKCiMgIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMKIyAtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0KIyA0IFBSRURJQ1RJT04gLSBIT0xUIFdJTlRFUlMKIyAtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0KIyAjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIwoKIyMjIyBwcsOpZGljdGlvbiBtw6l0aG9kZSBkZSBIb2x0IFdpbnRlcnMgKGxpc3NhZ2UgZXhwb25lbnRpZWwpIHN1ciBsYSBzw6lyaWUgY29ycmlnw6llIG1vZMOobGUgQUFBIHBvdXIgcHJlbmRyZSBlbiBjb21wdGUgbCdlcnJldXIsIGxhIHRlbmRhbmNlIGV0IGxhIHNhaXNvbm5hbGl0w6kKCiMjIDQuMSBIb2x0cy1XaW50ZXJzIC0gYXBwbGljYXRpb24gZGUgbGEgbcOpdGhvZGUKYGBge3J9Cmhvd2k9ZXRzKGNvcnJpZ2VlLG1vZGVsPSJBQUEiKQpzdW1tYXJ5KGhvd2kpCmBgYAoqIC0+IGxlcyBzbW9vdGluZyBwYXJhbWV0ZXJzIGFpZGVudCDDoCBkw6l0ZXJtaW5lciBzaSBsYSBwcsOpZGljdGlvbiBzZSBmYWl0IHBhciByYXBwb3J0IGF1eCBkZXJuacOocmVzIHZhbGV1cnMgb3Ugw6AgZGVzIHZhbGV1cnMgcGx1cyBhbmNpZW5uZXMuIFBsdXMgb24gZXN0IHByb2NoZSBkZSAxIHBsdXMgbGVzIGRlcm5pw6hyZXMgdmFsZXVycyBjb21wdGVudCBwb3VyIHByw6lkaXJlIGxlcyBub3V2ZWxsZXMuIFBsdXMgb24gcydhcHByb2NoZSBkZSB6w6lybyBwbHVzIGlsIGZhdWRyYSBzZSBiYXNlciBzdXIgZGVzIHZhbGV1cnMgYW5jaWVubmVzLgoKIyMgNC4yIEFuYWx5c2UgYSBwb3N0ZXJpb3JpIEhvbHQgV2ludGVycyBwb3VyIHbDqXJpZmllciBsJ2VmZmljYWNpdMOpIGRlIGNlIG1vZMOobGUgc3VyIHVuIHRyb25jIGNvbm51CgojIyMjIE9uIHRyb25xdWUgbGEgc8OpcmllIGRlIGxhIHDDqXJpb2RlIGRlIGphbnZpZXIgMjAxOSDDoCBqYW52aWVyIDIwMjAsIGV0IG9uIGxhIHByw6l2b2l0IHN1ciBsZXMgZG9ubsOpZXMgcHLDqWPDqWRlbnRlcwpgYGB7cn0KeF90cm9uYz13aW5kb3coY29ycmlnZWUsZW5kPWMoMjAxOSw0KSkKeF9hX3ByZXZvaXI9d2luZG93KGNvcnJpZ2VlLHN0YXJ0PWMoMjAxOSw1KSkKYGBgCgojIyMjIE9uIGFwcGxpcXVlIGxhIG3DqXRob2RlIHN1ciBjZXR0ZSBzw6lyaWUgdHJvbnF1w6llIApgYGB7cn0KbW9kZWxob3dpX3Ryb25jPWV0cyh4X3Ryb25jLG1vZGVsPSJBQUEiKQpzdW1tYXJ5KG1vZGVsaG93aV90cm9uYykKYGBgCiMjIyMgRXN0aW1hdGlvbiBkZSBsYSBwcsOpZGljdGlvbiBldCBkZSBsJ2ludGVydmFsbGUgZGUgOTUlIGVuIHBsdXMgZXQgZW4gbW9pbnMKCmBgYHtyfQpwcmVkX21vZGVsaG93aV90cm9uYz1mb3JlY2FzdChtb2RlbGhvd2lfdHJvbmMsaD0xMixsZXZlbD05NSkKcHJlZGhvd2lfdHJvbmM9cHJlZF9tb2RlbGhvd2lfdHJvbmMkbWVhbgpwcmVkaG93aV9sX3Ryb25jPXRzKHByZWRfbW9kZWxob3dpX3Ryb25jJGxvd2VyLHN0YXJ0PWMoMjAxOSw1KSxmcmVxdWVuY3k9MTIpCnByZWRob3dpX3VfdHJvbmM9dHMocHJlZF9tb2RlbGhvd2lfdHJvbmMkdXBwZXIsc3RhcnQ9YygyMDE5LDUpLGZyZXF1ZW5jeT0xMikKYGBgCgojIyMjIGFmZmljaGFnZSBkdSByw6lzdWx0YXQKYGBge3J9CnBuZygiaW1nLzA4IC0gYW5hbHlzZVBvc3RlcmlvcmlIVy5wbmciLCB3aWR0aCA9IDcuMjUsIGhlaWdodCA9IDQuNSwgdW5pdHMgPSAnaW4nLCByZXMgPSAzMDApCnRzLnBsb3QoeF9hX3ByZXZvaXIscHJlZGhvd2lfdHJvbmMscHJlZGhvd2lfbF90cm9uYyxwcmVkaG93aV91X3Ryb25jLHhsYWI9InQiLHlsYWI9IkNvbnNvbW1hdGlvbiBjb3JyaWfDqWUiLGNvbD1jKDEsMiwzLDMpLGx0eT1jKDEsMSwyLDIpLGx3ZD1jKDMsMywyLDIpKQpsZWdlbmQoInRvcGxlZnQiLGxlZ2VuZD1jKCJYIiwiWF9wcmV2IiksY29sPWMoMSwyLDMsMyksbHR5PWMoMSwxKSxsd2Q9YygzLDMpKQpsZWdlbmQoInRvcHJpZ2h0IixsZWdlbmQ9YygiaW50OTUlX2luZiIsImludDk1JV9zdXAiKSxjb2w9YygzLDMpLGx0eT1jKDIsMiksbHdkPWMoMiwyKSkKdGl0bGUoIkFuYWx5c2UgYSBwb3N0ZXJpb3JpIDogSG9sdCBXaW50ZXJzIikKYGBgCmBgYHtyfQp0cy5wbG90KHhfYV9wcmV2b2lyLHByZWRob3dpX3Ryb25jLHByZWRob3dpX2xfdHJvbmMscHJlZGhvd2lfdV90cm9uYyx4bGFiPSJ0Iix5bGFiPSJDb25zb21tYXRpb24gY29ycmlnw6llIixjb2w9YygxLDIsMywzKSxsdHk9YygxLDEsMiwyKSxsd2Q9YygzLDMsMiwyKSkKbGVnZW5kKCJ0b3BsZWZ0IixsZWdlbmQ9YygiWCIsIlhfcHJldiIpLGNvbD1jKDEsMiwzLDMpLGx0eT1jKDEsMSksbHdkPWMoMywzKSkKbGVnZW5kKCJ0b3ByaWdodCIsbGVnZW5kPWMoImludDk1JV9pbmYiLCJpbnQ5NSVfc3VwIiksY29sPWMoMywzKSxsdHk9YygyLDIpLGx3ZD1jKDIsMikpCnRpdGxlKCJBbmFseXNlIGEgcG9zdGVyaW9yaSA6IEhvbHQgV2ludGVycyIpCmBgYAoqIC0+IExhIHByw6lkaWN0aW9uIGVzdCBiaWVuIGNvbnRlbnVlIGRhbnMgbCdpbnRlcnZlYWxsZSBkZXMgOTUlIAoqIC0+IEwnZXJyZXVyIGRlIHByw6lkaWN0aW9uIGZpbmFsZSBlc3QgZHVlIMOgIHVuIMOpdsOpbsOpbWVudCBleGNlcHRpb25uZWwsIHRlbXDDqXJhdHVyZSDDqWxldsOpZSBwb3VyIHVuIG1vaXMgZGUgamFudmllciBkb25jIGFycsOqdCBwcsOpbWF0dXLDqSBkZXMgY2hhdWZmYWdlcyBlbGVjdHJpcXVlcwoKIyMgNC4zIExlcyByw6lzaWR1cwpgYGB7cn0KQm94LnRlc3QuMihwcmVkX21vZGVsaG93aV90cm9uYyRyZXNpZHVhbHMsbmxhZyA9IGMoNiwxMiwxOCwyNCwzMCwzNiksIHR5cGUgPSAiTGp1bmctQm94IixkZWNpbSA9IDUpCmBgYAojIyMjIGxlcyByw6lkaXN1cyBzb250IGluZMOpcGVuZGFudHMsIGlsIHMnYWdpdCBkJ3VuIGJydWl0IGJsYW5jLCBsZSBtb2TDqGxlIHF1ZSBsJ29uIHZpZW50IGRlIHRyb3V2ZXIgZXN0IGNvcnJlY3QKCiMjIyMgTm9ybWFsaXTDqSBkZXMgcsOpc2lkdXMgLSBtw6l0aG9kZSBTaGFwaXJvCmBgYHtyfQpzaGFwaXJvLnRlc3QocHJlZF9tb2RlbGhvd2lfdHJvbmMkcmVzaWR1YWxzKQpgYGAKIyMjIExlcyByw6lzaWR1cyBzdWl2ZW50IHVuZSBsb2kgbm9ybWFsZSBjYXIgcC12YWx1ZSA+IDUlCgojIyA0LjQgUHLDqWNpc2lvbiA6IFJNU0UgZXQgTUFQRQoKYGBge3J9CnJtc2VfaG93aT1zcXJ0KG1lYW4oKHhfYV9wcmV2b2lyLXByZWRob3dpX3Ryb25jKV4yKSkKbWFwZV9ob3dpPW1lYW4oYWJzKDEtcHJlZGhvd2lfdHJvbmMveF9hX3ByZXZvaXIpKSoxMDAKY2F0KCdSTVNFIEhvbHQgV2ludGVycyA6ICcscm1zZV9ob3dpLCdcbicpCmNhdCgnTUFQRSBIb3QgV2ludGVycyA6ICcsbWFwZV9ob3dpLCdcbicpCmBgYAoKIyMgNC41IFByw6l2aXNpb24gw6AgbOKAmWFpZGUgZHUgbW9kw6hsZSBIb2x0IFdpbnRlcnMgc3VyIHVuIGFuCmBgYHtyfQpwcmVkX21vZGVsX2hvd2k9Zm9yZWNhc3QoaG93aSxoPTEyLGxldmVsPTk1KSAjIHByw6lkaWN0aW9uIG1vZMOobGUgSFcgc3VyIHPDqXJpZXMgZGUgMTIgYXZlYyB1biBuaXZlYXUgZGUgNSUKcHJlZF9ob3dpPXByZWRfbW9kZWxfaG93aSRtZWFuICMgbW95ZW5uZSAKcHJlZF9ob3dpX2w9dHMocHJlZF9tb2RlbF9ob3dpJGxvd2VyLHN0YXJ0PWMoMjAyMCw1KSxmcmVxdWVuY3k9MTIpICMgZXN0aW1hdGlvbiBiYXNzZSBkZSBsJ2ludGVydmFsbGUKcHJlZF9ob3dpX3U9dHMocHJlZF9tb2RlbF9ob3dpJHVwcGVyLHN0YXJ0PWMoMjAyMCw1KSxmcmVxdWVuY3k9MTIpICMgZXN0aW1hdGlvbiBoYXV0ZQpgYGAKCiMjIyMgc2F1dmVnYXJkZSBpbWFnZQpgYGB7cn0KcG5nKCJpbWcvMDkgLSBwcmV2aXNpb25IVy5wbmciLCB3aWR0aCA9IDcuMjUsIGhlaWdodCA9IDQuNSwgdW5pdHMgPSAnaW4nLCByZXMgPSAzMDApCnRzLnBsb3QoY29ycmlnZWUscHJlZF9ob3dpLHByZWRfaG93aV9sLHByZWRfaG93aV91LHhsYWI9InQiLHlsYWI9IkNvbnNvbW1hdGlvbiBjb3JyaWfDqWUiLGNvbD1jKDEsMiwzLDMpLGx0eT1jKDEsMSwyLDIpLGx3ZD1jKDEsMywyLDIpKQpsZWdlbmQoInRvcGxlZnQiLGxlZ2VuZD1jKCJpbnQ5NSVfaW5mIiwiaW50OTUlX3N1cCIpLGNvbD1jKDMsMyksbHR5PWMoMiwyKSxsd2Q9YygyLDIpKQp0aXRsZSgiUHLDqXZpc2lvbiA6IEhvbHQgV2ludGVycyIpCmBgYAoKYGBge3J9CnRzLnBsb3QoY29ycmlnZWUscHJlZF9ob3dpLHByZWRfaG93aV9sLHByZWRfaG93aV91LHhsYWI9InQiLHlsYWI9IkNvbnNvbW1hdGlvbiBjb3JyaWfDqWUiLGNvbD1jKDEsMiwzLDMpLGx0eT1jKDEsMSwyLDIpLGx3ZD1jKDEsMywyLDIpKQpsZWdlbmQoInRvcGxlZnQiLGxlZ2VuZD1jKCJpbnQ5NSVfaW5mIiwiaW50OTUlX3N1cCIpLGNvbD1jKDMsMyksbHR5PWMoMiwyKSxsd2Q9YygyLDIpKQp0aXRsZSgiUHLDqXZpc2lvbiA6IEhvbHQgV2ludGVycyIpCmBgYAojIyMjIEVucmVnaXN0cmVtZW50IGVuIGltYWdlCmBgYHtyfQpwbmcoImltZy8xMCAtIHByZXZpc2lvbkhXX1pPT00ucG5nIiwgd2lkdGggPSA3LjI1LCBoZWlnaHQgPSA0LjUsIHVuaXRzID0gJ2luJywgcmVzID0gMzAwKQp0cy5wbG90KHdpbmRvdyhjb3JyaWdlZSxzdGFydD1jKDIwMTksNSkpLHByZWRfaG93aSxwcmVkX2hvd2lfbCxwcmVkX2hvd2lfdSx4bGFiPSJ0Iix5bGFiPSJDb25zb21tYXRpb24gY29ycmlnw6llIixjb2w9YygxLDIsMywzKSxsdHk9YygxLDEsMiwyKSxsd2Q9YygxLDMsMiwyKSkKbGVnZW5kKCJ0b3BsZWZ0IixsZWdlbmQ9YygiaW50OTUlX2luZiIsImludDk1JV9zdXAiKSxjb2w9YygzLDMpLGx0eT1jKDIsMiksbHdkPWMoMiwyKSkKdGl0bGUoIlByw6l2aXNpb24gOiBIb2x0IFdpbnRlcnMgLSBaT09NICIpCmBgYAoKYGBge3J9CnRzLnBsb3Qod2luZG93KGNvcnJpZ2VlLHN0YXJ0PWMoMjAxOSw1KSkscHJlZF9ob3dpLHByZWRfaG93aV9sLHByZWRfaG93aV91LHhsYWI9InQiLHlsYWI9IkNvbnNvbW1hdGlvbiBjb3JyaWfDqWUiLGNvbD1jKDEsMiwzLDMpLGx0eT1jKDEsMSwyLDIpLGx3ZD1jKDEsMywyLDIpKQpsZWdlbmQoInRvcGxlZnQiLGxlZ2VuZD1jKCJpbnQ5NSVfaW5mIiwiaW50OTUlX3N1cCIpLGNvbD1jKDMsMyksbHR5PWMoMiwyKSxsd2Q9YygyLDIpKQp0aXRsZSgiUHLDqXZpc2lvbiA6IEhvbHQgV2ludGVycyAtIFpPT00gIikKYGBgCgojICMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjCiMgLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tCiMgNSBQcsOpZGljdGlvbiAtIFNBUklNQQojIC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQojICMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjCgojIyMgbcOpdGhvZGUgU0FSSU1BIC0gQXJpbWEgc3VyIHPDqXJpZSBhdmVjIFNhaXNvbm5hcml0w6kgOiAgQXV0byBSZWdyZXNzaXZlIEludGVncmF0aW9uIE1lYW4gQXZlcmFnZSBkb25uZSBsZXMgdmFsZXVycyBwIC8gZCAvIHEgUC9EL1EKCiMjIyMgQ2V0dGUgbWV0aG9kZSBzJ2FwcGxpcXVlIHN1ciBsZXMgc8OpcmllcyBzdGF0aW9ubmFpcmVzLCBzaSBvbiBwZXV0IGxlIHN1cHBvc2VyIHZpc3VlbGxlbWVudCwgb24gcGV1dCB1dGlsaXNlciB1bmUgZm9uY3Rpb24gcXVpIHZhIGRvbm5lciB1biByw6lzdWx0YXQgKCBwb3VyIHBvdXZvaXIgbCdpbnTDqWdyZXIgZGFucyBkZXMgYWxnb3J5dGhtZXMgZCdhbmFseXNlIHBhciBleGVtcGxlICkKCiMjIDUuMSBTVEFUSU9OQVJJVMOJCiMjIyMgUG91ciBjb25maXJtZXIgcXVlIGxhIHPDqXJpZSBuJ2VzdCBwYXMgc3RhdGlvbm5haXJlIG9uIHBldXQgdXRpbGlzZXIgQURGIEF1Z21lbnRlZCBEaWNrZXktRnVsbGVyCmBgYHtyfQojIGxhIHPDqXJpZSBlc3QtZWxsZSBzdGF0aW9ubmFpcmUgP14Kc3RhdGlvbm5haXJlX3Rlc3Q8LWFkZi50ZXN0KGNvcnJpZ2VlLGs9MTIpCnN0YXRpb25uYWlyZV90ZXN0CmBgYAojIyMjIEFuYWx5c2UgOiBsJ2h5cG90aMOoc2UgSDAgZXN0IHF1ZSBsYSBzw6lyaWUgZXN0IHN0YXRpb25uYWlyZS4KIyMjIyBvciBsYSBwLXZhbHVlIGVzdCBzdXDDqXJpZXVyZSDDoCA1JSBkb25jIG9uIHJlamV0dGUgSDAgZXQgb24gcmV0aWVudCBsJ0h5cG90aMOoc2UgYWx0ZXJuYXRpdmUgSGEsIGxhIHPDqXJpZSBuJ2VzdCBwYXMgc3RhdGlvbm5haXJlLgoKIyMjIGlsIGZhdXQgZW4gcHJlbWllciBzdGF0aW9ubmFyaXNlciBsYSBzw6lyaWUgdGVtcG9yZWxsZSBwYXIgZGlmZsOpcmVuY2lhdGlvbgoKIyMgNS4yIFN0YXRpb25uYXJpc2F0aW9uCiMjIyMgQXV0b2NvcnLDqWxvZ3JhbW1lIFNpbXBsZQpgYGB7cn0KYWNmPWFjZihjb3JyaWdlZSxsYWcubWF4PTM2LHlsaW09YygtMSwxKSkKYWNmJGxhZz1hY2YkbGFnKjEyCmBgYAojIyMjIHNhdXZlZ2FyZGUgZGUgbCdpbWFnZQpgYGB7cn0KcG5nKCJpbWcvMTEgLSBBQ0YwMSAucG5nIiwgd2lkdGggPSA3LjI1LCBoZWlnaHQgPSA0LjUsIHVuaXRzID0gJ2luJywgcmVzID0gMzAwKQphY2YoY29ycmlnZWUsbGFnLm1heD0zNix5bGltPWMoLTEsMSkpCmBgYAojIyMjIGxhIGTDqWNyb2lzc2FuY2UgZXN0IHRyw6hzIGxlbnRlLCBjZSBxdWkgZW1ww6pjaGUgY2V0dGUgc29ydGllIEFDRiBkJ8OqdHJlIHVuIGF1dG9jb3Jyw6lsb2dyYW1tZSBzaW1wbGUKIyMjIyBsZXMgdHJhaXRzIGJsZXVzIHBvaW50aWxsw6lzIHNvbnQgbGVzIGxpbWl0ZXMgZGUgbGEgc2lnbmlmaWNhdGl2aXTDqSDDoCA5NSUKCiMjIyA1LjIuMSBEaWZmw6lyZW5jaWF0aW9uIGF2ZWMgdW5lIHNhaXNvbm5hbGl0w6kgZGUgMTIKYGBge3J9CnlfZGlmXzFfMTI9ZGlmZihjb3JyaWdlZSxsYWcgPSAxMixkaWZmZXJlbmNlcyA9IDEpCmFjZj1hY2YoeV9kaWZfMV8xMixsYWcubWF4PTM2LHlsaW09YygtMSwxKSwgcGxvdD1GQUxTRSkKYWNmJGxhZz1hY2YkbGFnKjEyCnBsb3QoYWNmKQpgYGAKIyMjIyBzYXV2ZWdhcmRlIGRlIGwnaW1hZ2UKYGBge3J9CnBuZygiaW1nLzEyIC0gQUNGMDItZGlmZsOpcmVuY2nDqWUtTGFnMTIgLnBuZyIsIHdpZHRoID0gNy4yNSwgaGVpZ2h0ID0gNC41LCB1bml0cyA9ICdpbicsIHJlcyA9IDMwMCkKYWNmKHlfZGlmXzFfMTIsbGFnLm1heD0zNix5bGltPWMoLTEsMSkpCmBgYAojIyPigqwgTGEgc29ydGlFIEFDRiBkZSBsYSBzw6lyaWUgZGlmZsOpcmVuY2nDqWUgc2VtYmxlIHBvdXZvaXIgw6p0cmUgaW50ZXJwcsOpdMOpZSBjb21tZSB1biBhdXRvY29ycsOpbG9ncmFtbWUgc2ltcGxlIGVtcGlyaXF1ZS4gT24gbmUgdm9pdCBwbHVzIGxlcyBkw6ljcm9pc3NhbmNlcyBsZW50ZXMgcHLDqWPDqWRlbnRlcy4gTGEgc8OpcmllIGVzdCBkb25jIHBvdGVudGllbGxlbWVudCBzdGF0aW9ubmFpcmUuIE9uIGlkZW50aWZpZXJhIGRvbmMgdW4gbW9kw6hsZSBTQVJJTUEgLgojIyA1LjIuMiBBQ0YgLSBMYSBzw6lyaWUgZXN0LWVsbGUgc3RhdGlvbm5haXJlID8KYGBge3J9CnN0YXRpb25uYWlyZV90ZXN0PC1hZGYudGVzdCh5X2RpZl8xXzEyKQpzdGF0aW9ubmFpcmVfdGVzdApgYGAKIyMjIyBwLXZhbHVlIGRlIGwnYWRmIDwgNSUgZG9uYyBzw6lyaWUgcG90ZW50aWVsbGVtZW50IHN0YXRpb25uYWlyZQoKIyMgNS4yLjMgIFBBQ0YgKCBwYXJ0aWFseSBhdXRvIGNvcnJlbGF0aW9uIGZ1bmN0aW9uKQpgYGB7cn0KcGFjZj1wYWNmKHlfZGlmXzFfMTIsbGFnLm1heD0zNix5bGltPWMoLTEsMSksIHBsb3Q9RkFMU0UpCnBhY2YkbGFnPXBhY2YkbGFnKjEyCnBsb3QocGFjZix5bGltID0gYygtMSwxKSkKYGBgCiMjIyMgSU1BR0Ugc2F1dmVnYXJkZSBkZSBsJ2ltYWdlCmBgYHtyfQpwbmcoImltZy8xMyAtIFBBQ0YwMS1kaWZmw6lyZW5jacOpZS1MYWcxMiAucG5nIiwgd2lkdGggPSA3LjI1LCBoZWlnaHQgPSA0LjUsIHVuaXRzID0gJ2luJywgcmVzID0gMzAwKQpwYWNmKHlfZGlmXzFfMTIsbGFnLm1heD0zNix5bGltPWMoLTEsMSkpCmBgYAojIyMgTGEgc8OpcmllIHRlbXBvcmVsbGUgw6l0YW50IHBvdGVudGllbGxlbWVudCBzdGF0aW9ubmFpcmUsIG9uIHBhc3NlIMOgIGwnaWRlbnRpZmljYXRpb24gZGUgbW9kw6hsZQoKIyMgNS4zIFJFQ0hFUkNIRSBEJ1VOIE1PRMOITEUgU0FSSU1BIFBPVVIgTEEgUFLDiURJQ1RJT04KIyMjIyBPbiB2YSByZWNoZXJjaGVyIGRlIGZhw6dvbiBlbXBpcmlxdWUgcXVlbGxlIHNlcmEgbGEgbWVpbGxldXJlIGNvbWJpbmFpc29uIGRlcyBjb2VmZmljaWVudHMgcG91ciBsZSBtb2TDqGxlIAojIyMjIFNBUklNQSAocCxkLHEpKFAsRCxRKVsxMl0gcG91ciB1bmUgc2Fpc29ubmFsaXTDqSBkZSAxMgojIyMjIHAgcG91ciBBUiwgUCBwb3VyIFNBUiwgZCBwb3VyIGxhIHRlbmRhbmNlLCBEIHBvdXIgbGEgc2Fpc29ubmFsaXTDqSwgcSBwb3VyIE1BIGV0IFEgcG91ciBTTUEKIyMjIyBkZXV4IGZvbmN0aW9ucyBub3VzIGFpZGVudCDDoCBkaWZmw6lyZW5jaWVyIGV0IGNob2lzaXIgbGVzIGNvZWZmaWNpZW50cyBkdSBtb2TDqGxlIHBvdXIgZCBldCBECmBgYHtyfQpkID0gbmRpZmZzKGNvcnJpZ2VlKQpEID0gbnNkaWZmcyhjb3JyaWdlZSkKY2F0KCdkID0gJyxkLCcgLSBkaWZmw6lyZW50aWF0aW9uIGVuIHRlbmRhbmNlcyAvIEQgPSAnLCBELCdkaWZmw6lyZW5jaWF0aW9ucyBlbiBzYWlzb25uYWxpdMOpJykKYGBgCgojIyMjIG9uIHZhIHRyYXZhaWxsZXIgc3VyIGxhIHPDqXJpZSBjb3JyaWfDqWUgZXQgb24geSBhcHBsaXF1ZSBsZXMgcGFyYW3DqHRyZXMgcXVlIG5vdXMgdmVub25zIGRlIHRyb3V2ZXIgZXQgbm91cyBhcHBsaXF1ZXJvbnMgbGEgbWV0aG9kZSBDU1MtTUwgKCBtYXhpbXVtIGRlIHZyYWlzZW1ibGFuY2UgY29uZGl0aW9ubmVsbGUgKQoKIyMgNS4zLjEgU0FSSU1BICgwMDApKDExMSkKIyMgLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0KIyMjIDUuMy4xLjEgTW9kw6lsaXNhdGlvbgpgYGB7cn0KbW9kZWwxPUFyaW1hKGNvcnJpZ2VlLG9yZGVyPWMoMCwwLDApLGxpc3Qob3JkZXI9YygxLDEsMSkscGVyaW9kPTEyKSxpbmNsdWRlLm1lYW49RkFMU0UsbWV0aG9kPSJDU1MtTUwiKQpzdW1tYXJ5KG1vZGVsMSkKYGBgCiMjIyA1LjMuMS4yIENvZWZmaWNpZW50cwpgYGB7cn0KdF9zdGF0KG1vZGVsMSkKYGBgCiogUC12YWxldXJzID4gNSUgOiBMZXMgY29lZmZpY2llbnRzIG5lIHNvbnQgcGFzIHNpZ25pZmljYXRpZnMKCiMjIyA1LjMuMS4zIFRlc3QgZGUgYmxhbmNoZXVyIC0gTGp1bmctQm94CmBgYHtyfQpCb3gudGVzdC4yKG1vZGVsMSRyZXNpZHVhbHMsbmxhZyA9IGMoNiwxMiwxOCwyNCwzMCwzNiksIHR5cGUgPSAiTGp1bmctQm94IixkZWNpbSA9IDUpCmBgYAoqIC0+IFAtdmFsZXVycyA+IDUlIDogbGVzIHLDqXNpZHVzIHNvbnQgdW4gYnJ1aXQgYmxhbmMKCiMjIyA1LjMuMS40IE5vcm1hbGl0w6kgZGVzIHLDqXNpZHVzIC0gbcOpdGhvZGUgU2hhcGlybwpgYGB7cn0Kc2hhcGlyby50ZXN0KG1vZGVsMSRyZXNpZHVhbHMpCmBgYAoKIyMgNS4zLjIgU0FSSU1BICgwMDEpKDExMSkKIyMgLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQojIyMgNS4zLjIuMSBNb2TDqWxpc2F0aW9uCmBgYHtyfQptb2RlbDI9QXJpbWEoY29ycmlnZWUsb3JkZXI9YygwLDAsMSksbGlzdChvcmRlcj1jKDEsMSwxKSxwZXJpb2Q9MTIpLGluY2x1ZGUubWVhbj1GQUxTRSxtZXRob2Q9IkNTUy1NTCIpCnN1bW1hcnkobW9kZWwyKQpgYGAKIyMjIDUuMy4yLjIgQ29lZmZpY2llbnRzCmBgYHtyfQp0X3N0YXQobW9kZWwyKQpgYGAKKiAtPiBQLXZhbGV1cnMgPiA1JSA6IExlcyBjb2VmZmljaWVudHMgbmUgc29udCBwYXMgc2lnbmlmaWNhdGlmcyBzYXVmIHNtYTEKCiMjIyA1LjMuMi4zIFRlc3QgZGUgYmxhbmNoZXVyIC0gTGp1bmctQm94CmBgYHtyfQpCb3gudGVzdC4yKG1vZGVsMiRyZXNpZHVhbHMsbmxhZyA9IGMoNiwxMiwxOCwyNCwzMCwzNiksIHR5cGUgPSAiTGp1bmctQm94IixkZWNpbSA9IDUpCmBgYAoqIC0+IFAtdmFsZXVycyA+IDUlIDogbGVzIHLDqXNpZHVzIHNvbnQgdW4gYnJ1aXQgYmxhbmMKCiMjIyA1LjMuMi40IE5vcm1hbGl0w6kgZGVzIHLDqXNpZHVzIC0gU2hhcGlybwpgYGB7cn0Kc2hhcGlyby50ZXN0KG1vZGVsMiRyZXNpZHVhbHMpCmBgYAoKIyMgNS4zLjMgU0FSSU1BICgxMDEpKDExMSkKIyMgLS0tLS0tLS0tLS0tLS0tLS0KIyMjIDUuMy4zLjEgTW9kw6lsaXNhdGlvbgpgYGB7cn0KbW9kZWwzPUFyaW1hKGNvcnJpZ2VlLG9yZGVyPWMoMSwwLDEpLGxpc3Qob3JkZXI9YygxLDEsMSkscGVyaW9kPTEyKSxpbmNsdWRlLm1lYW49RkFMU0UsbWV0aG9kPSJDU1MtTUwiKQpzdW1tYXJ5KG1vZGVsMykKYGBgCiMjIyA1LjMuMy4yIENvZWZmaWNpZW50cwpgYGB7cn0KdF9zdGF0KG1vZGVsMykKYGBgCiogLT4gUC12YWxldXJzID4gNSUgOiBMZXMgY29lZmZpY2llbnRzIG5lIHNvbnQgcGFzIHNpZ25pZmljYXRpZnMgc2F1ZiBzbWExCgojIyMgNS4zLjMuMyBUZXN0IGRlIGJsYW5jaGV1ciAtIExqdW5nLUJveApgYGB7cn0KQm94LnRlc3QuMihtb2RlbDMkcmVzaWR1YWxzLG5sYWcgPSBjKDYsMTIsMTgsMjQsMzAsMzYpLCB0eXBlID0gIkxqdW5nLUJveCIsZGVjaW0gPSA1KQpgYGAKKiAtPiBQLXZhbGV1cnMgPiA1JSA6IGxlcyByw6lzaWR1cyBzb250IHVuIGJydWl0IGJsYW5jCgojIyMgNS4zLjMuNCBOb3JtYWxpdMOpIGRlcyByw6lzaWR1cyAtIFNoYXBpcm8KYGBge3J9CnNoYXBpcm8udGVzdChtb2RlbDMkcmVzaWR1YWxzKQpgYGAKKiAtPiBsYSBwLXZhbGV1ciDDqXRhbnQgaW5mw6lyaWV1cmUgw6AgNSUgbCdoeXBvdGjDqHNlIGRlIG5vcm1hbGl0w6kgZXN0IHJlamV0w6llLgoKCiMjIDUuMy40IFNBUklNQSAoMDExKSgwMTEpCiMjIC0tLS0tLS0tLS0tLS0tLS0tCiMjIyA1LjMuNC4xIE1vZMOpbGlzYXRpb24KYGBge3J9Cm1vZGVsND1BcmltYShjb3JyaWdlZSxvcmRlcj1jKDAsMSwxKSxsaXN0KG9yZGVyPWMoMCwxLDEpLHBlcmlvZD0xMiksaW5jbHVkZS5tZWFuPUZBTFNFLG1ldGhvZD0iQ1NTLU1MIikKc3VtbWFyeShtb2RlbDQpCmBgYAojIyMgNS4zLjQuMiBDb2VmZmljaWVudHMKYGBge3J9CnRfc3RhdChtb2RlbDQpCmBgYAoqIC0+IFAtdmFsZXVycyA+IDUlIDogTGVzIGNvZWZmaWNpZW50cyBzb250IHNpZ25pZmljYXRpZnMKCiMjIyA1LjMuNC4zIFRlc3QgZGUgYmxhbmNoZXVyIC0gTGp1bmctQm94CmBgYHtyfQpCb3gudGVzdC4yKG1vZGVsNCRyZXNpZHVhbHMsbmxhZyA9IGMoNiwxMiwxOCwyNCwzMCwzNiksIHR5cGUgPSAiTGp1bmctQm94IixkZWNpbSA9IDUpCmBgYAoqIC0+IFAtdmFsZXVycyA+IDUlIDogbGVzIHLDqXNpZHVzIHNvbnQgdW4gYnJ1aXQgYmxhbmMKCiMjIyA1LjMuNC40IE5vcm1hbGl0w6kgZGVzIHLDqXNpZHVzIC0gU2hhcGlybwpgYGB7cn0Kc2hhcGlyby50ZXN0KG1vZGVsNCRyZXNpZHVhbHMpCmBgYAoqIC0+IGxhIHAtdmFsZXVyIMOpdGFudCBzdXDDqXJpZXVyZSDDoCA1JSBsJ2h5cG90aMOoc2UgZGUgbm9ybWFsaXTDqSBuJ2VzdCBwYXMgcmVqZXTDqWUsIGRvbmMgbGVzIHLDqXNpZHVzIHNvbnQgcG90ZW50aWVsbGVtZW50IGdhdXNzaWVucwoKCiMjIDUuMy41IFNBUklNQSAoMTAxKSgwMTEpCiMjIC0tLS0tLS0tLS0tLS0tLS0tCiMjIyA1LjMuNS4xIE1vZMOpbGlzYXRpb24KYGBge3J9Cm1vZGVsNT1BcmltYShjb3JyaWdlZSxvcmRlcj1jKDEsMCwxKSxsaXN0KG9yZGVyPWMoMCwxLDEpLHBlcmlvZD0xMiksaW5jbHVkZS5tZWFuPUZBTFNFLG1ldGhvZD0iQ1NTLU1MIikKc3VtbWFyeShtb2RlbDUpCmBgYAojIyMgNS4zLjUuMiBDb2VmZmljaWVudHMKYGBge3J9CnRfc3RhdChtb2RlbDUpCmBgYAoqIC0+IFAtdmFsZXVycyA+IDUlIDogTGVzIGNvZWZmaWNpZW50cyBzb250IHNpZ25pZmljYXRpZnMKCiMjIyA1LjMuNS4zIFRlc3QgZGUgYmxhbmNoZXVyIC0gTGp1bmctQm94CmBgYHtyfQpCb3gudGVzdC4yKG1vZGVsNSRyZXNpZHVhbHMsbmxhZyA9IGMoNiwxMiwxOCwyNCwzMCwzNiksIHR5cGUgPSAiTGp1bmctQm94IixkZWNpbSA9IDUpCmBgYAoqIC0+IFAtdmFsZXVycyA+IDUlIDogbGVzIHLDqXNpZHVzIHNvbnQgdW4gYnJ1aXQgYmxhbmMKCiMjIyA1LjMuNS40IE5vcm1hbGl0w6kgZGVzIHLDqXNpZHVzIC0gU2hhcGlybwpgYGB7cn0Kc2hhcGlyby50ZXN0KG1vZGVsNSRyZXNpZHVhbHMpCmBgYAojICMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjCiMgLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tCiMgNiBTQVJJTUEgQVVUTwojIC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQojICMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjCgojIyMgNi4xIE1vZMOpbGlzYXRpb24KYGBge3J9Cm1vZGVsX2F1dG8gPC0gYXV0by5hcmltYShjb3JyaWdlZSwgc3RlcHdpc2U9VFJVRSwgYXBwcm94aW1hdGlvbj1UUlVFLCBhbGxvd2RyaWZ0PUZBTFNFKQpgYGAKCiMjIyA2LjIgQ29lZmZpY2llbnRzCmBgYHtyfQp0X3N0YXQobW9kZWxfYXV0bykKYGBgCiogLT4gUC12YWxldXJzID4gNSUgOiBMZXMgY29lZmZpY2llbnRzIG5lIHNvbnQgcGFzIHNpZ25pZmljYXRpZnMgc2F1ZiBzbWExCgojIyMgNi4zIFRlc3QgZGUgYmxhbmNoZXVyIC0gTGp1bmctQm94CmBgYHtyfQpCb3gudGVzdC4yKG1vZGVsX2F1dG8kcmVzaWR1YWxzLG5sYWcgPSBjKDYsMTIsMTgsMjQsMzAsMzYpLCB0eXBlID0gIkxqdW5nLUJveCIsZGVjaW0gPSA1KQpgYGAKKiAtPiBQLXZhbGV1cnMgPiA1JSA6IGxlcyByw6lzaWR1cyBzb250IHVuIGJydWl0IGJsYW5jCgojIyMgNi40IE5vcm1hbGl0w6kgZGVzIHLDqXNpZHVzIC0gU2hhcGlybwpgYGB7cn0Kc2hhcGlyby50ZXN0KG1vZGVsX2F1dG8kcmVzaWR1YWxzKQpgYGAKIyAjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIwojIC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQojIDcgQ29tcGFyYWlzb24gZGVzIG1vZMOobGVzIFNBUklNQSA0IGV0IDUgc3VyIGxhIHDDqXJpb2RlIHRyb25xdcOpZSAgCiMgLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tCiMgIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMKCiMjIDcuMSBNT0TDiExFIDUKIyMjIDcuMS4xIE1vZMOpbGlzYXRpb24KYGBge3J9Cm1vZGVsNXRyb25jPUFyaW1hKHhfdHJvbmMsb3JkZXI9YygxLDAsMSksbGlzdChvcmRlcj1jKDAsMSwxKSxwZXJpb2Q9MTIpLGluY2x1ZGUubWVhbj1GQUxTRSxtZXRob2Q9IkNTUy1NTCIpCnN1bW1hcnkobW9kZWw1dHJvbmMpCmBgYAojIyMgNy4xLjIgQ29lZmZpY2llbnRzCmBgYHtyfQp0X3N0YXQobW9kZWw1dHJvbmMpCmBgYAojIyMgc2V1bCBTTUExIGVzdCBzaWduaWZpY2F0aWYKCiMjIyA3LjEuMyBUZXN0IGRlIGJsYW5jaGV1ciAtIExqdW5nLUJveApgYGB7cn0KQm94LnRlc3QuMihtb2RlbDV0cm9uYyRyZXNpZHVhbHMsbmxhZz1jKDYsMTIsMTgsMjQsMzAsMzYpLHR5cGU9IkxqdW5nLUJveCIsZGVjaW09NSkKYGBgCiogLT4gUC12YWxldXJzID4gNSUgOiBsZXMgcsOpc2lkdXMgc29udCB1biBicnVpdCBibGFuYwoKIyMjIDcuMS40IE5vcm1hbGl0w6kgZGVzIHLDqXNpZHVzIC0gU2hhcGlybwpgYGB7cn0Kc2hhcGlyby50ZXN0KG1vZGVsNXRyb25jJHJlc2lkdWFscykKYGBgCiogLT4gbGEgcC12YWxldXIgw6l0YW50IHN1cMOpcmlldXJlIMOgIDUlIGwnaHlwb3Row6hzZSBkZSBub3JtYWxpdMOpIG4nZXN0IHBhcyByZWpldMOpZSwgZG9uYyBsZXMgcsOpc2lkdXMgc29udCBwb3RlbnRpZWxsZW1lbnQgZ2F1c3NpZW5zCgojIyA3LjIgTU9Ew4hMRSA0CiMjIyA3LjIuMSBNb2TDqWxpc2F0aW9uCmBgYHtyfQptb2RlbDR0cm9uYz1BcmltYSh4X3Ryb25jLG9yZGVyPWMoMCwxLDEpLGxpc3Qob3JkZXI9YygwLDEsMSkscGVyaW9kPTEyKSxpbmNsdWRlLm1lYW49RkFMU0UsbWV0aG9kPSJDU1MtTUwiKQpzdW1tYXJ5KG1vZGVsM3Ryb25jKQpgYGAKIyMjIDcuMi4yIENvZWZmaWNpZW50cwpgYGB7cn0KdF9zdGF0KG1vZGVsNHRyb25jKQpgYGAKIyMjIGxlcyBjb2VmZmljaWVudHMgc29udCBzaWduaWZpY2F0aWZzCgojIyMgNy4yLjMgVGVzdCBkZSBibGFuY2hldXIgLSBManVuZy1Cb3gKYGBge3J9CkJveC50ZXN0LjIobW9kZWw0dHJvbmMkcmVzaWR1YWxzLG5sYWc9Yyg2LDEyLDE4LDI0LDMwLDM2KSx0eXBlPSJManVuZy1Cb3giLGRlY2ltPTUpCmBgYAoqIC0+IFAtdmFsZXVycyA+IDUlIDogbGVzIHLDqXNpZHVzIHNvbnQgdW4gYnJ1aXQgYmxhbmMKCiMjIyA3LjIuNCBOb3JtYWxpdMOpIGRlcyByw6lzaWR1cyAtIFNoYXBpcm8KYGBge3J9CnNoYXBpcm8udGVzdChtb2RlbDR0cm9uYyRyZXNpZHVhbHMpCmBgYAoqIC0+IGxhIHAtdmFsZXVyIMOpdGFudCBzdXDDqXJpZXVyZSDDoCA1JSBsJ2h5cG90aMOoc2UgZGUgbm9ybWFsaXTDqSBuJ2VzdCBwYXMgcmVqZXTDqWUsIGRvbmMgbGVzIHLDqXNpZHVzIHNvbnQgcG90ZW50aWVsbGVtZW50IGdhdXNzaWVucwoKIyMgNy4zIENIT0lYIERVIE1PRMOITEUgU0FSSU1BIDogbW9kw6hsZSA0CiMjIyMgbGUgbW9kw6hsZSByZXRlbnUgZXN0IGxlIG1vZMOobGUgNCBTQVJJTUEgKDAsMSwxICkgKDAsMSwxKSBbMTJdIAotIHBvdXIgbGVxdWVsIGxlcyBjb2VmZmljaWVudHMgc29udCBzaWduaWZpY2F0aWZzCi0gbGVzIHLDqXNpZHVzIHNvbnQgZGVzIGJydWl0cyBibGFuY3MKCiMjIDcuNCBQUkVDSVNJT04gRFUgTU9Ew4hMRSBTVVIgSU5URVJWQUxMRSBDT05OVQpgYGB7cn0KcHJlZF9tb2RlbDR0cm9uYz1mb3JlY2FzdChtb2RlbDR0cm9uYyxoPTEyLGxldmVsPTk1KQpwcmVkNF90cm9uYz1wcmVkX21vZGVsNHRyb25jJG1lYW4KcHJlZDRfbF90cm9uYz10cyhwcmVkX21vZGVsNHRyb25jJGxvd2VyLHN0YXJ0PWMoMjAxOSw1KSxmcmVxdWVuY3k9MTIpCnByZWQ0X3VfdHJvbmM9dHMocHJlZF9tb2RlbDR0cm9uYyR1cHBlcixzdGFydD1jKDIwMTksNSksZnJlcXVlbmN5PTEyKQp0cy5wbG90KHdpbmRvdyh4X2FfcHJldm9pcikscHJlZDRfdHJvbmMscHJlZDRfbF90cm9uYyxwcmVkNF91X3Ryb25jLHhsYWI9InQiLHlsYWI9IkNvbnNvbW1hdGlvbiBjb3JyaWfDqWUiLGNvbD1jKDEsMiwzLDMpLGx0eT1jKDEsMSwyLDIpLGx3ZD1jKDMsMywyLDIpKQpsZWdlbmQoInRvcGxlZnQiLGxlZ2VuZD1jKCJYIiwiWF9wcmV2IiksY29sPWMoMSwyLDMsMyksbHR5PWMoMSwxKSxsd2Q9YygzLDMpKQpsZWdlbmQoInRvcHJpZ2h0IixsZWdlbmQ9YygiaW50OTUlX2luZiIsImludDk1JV9zdXAiKSxjb2w9YygzLDMpLGx0eT1jKDIsMiksbHdkPWMoMiwyKSkKdGl0bGUoIkFuYWx5c2UgYSBwb3N0ZXJpb3JpIDogU0FSSU1BIFpPT00gbW9kw6hsZSA0ICgwLDEsMSkoMCwxLDEpWzEyXSIpCmBgYAojIyMgSU1BR0UgLSBzYXV2ZWdhcmRlIGR1IGdyYXBoaXF1ZQpgYGB7cn0KcG5nKCJpbWcvMTQgLSBBbmFseXNlIGEgcG9zdGVyaW9yaSBTQVJJTUEgWk9PTSBtb2TDqGxlIDQucG5nIiwgd2lkdGggPSA3LjI1LCBoZWlnaHQgPSA0LjUsIHVuaXRzID0gJ2luJywgcmVzID0gMzAwKQp0cy5wbG90KHdpbmRvdyh4X2FfcHJldm9pcikscHJlZDRfdHJvbmMscHJlZDRfbF90cm9uYyxwcmVkNF91X3Ryb25jLHhsYWI9InQiLHlsYWI9IkNvbnNvbW1hdGlvbiBjb3JyaWfDqWUiLGNvbD1jKDEsMiwzLDMpLGx0eT1jKDEsMSwyLDIpLGx3ZD1jKDMsMywyLDIpKQpsZWdlbmQoInRvcGxlZnQiLGxlZ2VuZD1jKCJYIiwiWF9wcmV2IiksY29sPWMoMSwyLDMsMyksbHR5PWMoMSwxKSxsd2Q9YygzLDMpKQpsZWdlbmQoInRvcHJpZ2h0IixsZWdlbmQ9YygiaW50OTUlX2luZiIsImludDk1JV9zdXAiKSxjb2w9YygzLDMpLGx0eT1jKDIsMiksbHdkPWMoMiwyKSkKdGl0bGUoIkFuYWx5c2UgYSBwb3N0ZXJpb3JpIDogU0FSSU1BIFpPT00gbW9kw6hsZSA0ICgwLDEsMSkoMCwxLDEpWzEyXSIpCmBgYAoKIyMjIFByw6ljaXNpb24gZHUgbW9kw6hsZSA0IGF2ZWMgUk1TRSBldCBNQVBFCmBgYHtyfQpybXNlX3NhcmltYT1zcXJ0KG1lYW4oKHhfYV9wcmV2b2lyLXByZWQ0X3Ryb25jKV4yKSkKbWFwZV9zYXJpbWE9bWVhbihhYnMoMS1wcmVkNF90cm9uYy94X2FfcHJldm9pcikpKjEwMApjYXQoJ1JNU0UgU0FSSU1BIDogJyxybXNlX3NhcmltYSwnXG4nKQpjYXQoJ01BUEUgU0FSSU1BIDogJyxtYXBlX3NhcmltYSwnJVxuJykKYGBgCiMjIDcuNSBQUsOJVklTSU9OIFNVUiBVTiBBTgoKYGBge3J9CnByZWRfbW9kZWw0PWZvcmVjYXN0KG1vZGVsNCxoPTEyLGxldmVsPTk1KSAjIHByw6lkaWN0aW9uIG1vZMOobDMgc3VyIHPDqXJpZXMgZGUgMTIgYXZlYyB1biBuaXZlYXUgZGUgNSUKcHJlZDQ9cHJlZF9tb2RlbDQkbWVhbgpwcmVkNF9sPXRzKHByZWRfbW9kZWw0JGxvd2VyLHN0YXJ0PWMoMjAyMCw1KSxmcmVxdWVuY3k9MTIpCnByZWQ0X3U9dHMocHJlZF9tb2RlbDQkdXBwZXIsc3RhcnQ9YygyMDIwLDUpLGZyZXF1ZW5jeT0xMikgIyBlc3RpbWF0aW9uIGhhdXRlIAp0cy5wbG90KGNvcnJpZ2VlLHByZWQ0LHByZWQ0X2wscHJlZDRfdSx4bGFiPSJ0ZW1wcyIseWxhYj0iQ29uc29tbWF0aW9uIGNvcnJpZ8OpZSIsY29sPWMoMSwyLDMsMyksbHR5PWMoMSwxLDIsMiksbHdkPWMoMSwzLDIsMikpCmxlZ2VuZCgidG9wbGVmdCIsbGVnZW5kPWMoImludDk1JV9pbmYiLCJpbnQ5NSVfc3VwIiksY29sPWMoMywzKSxsdHk9YygyLDIpLGx3ZD1jKDIsMikpCnRpdGxlKCJQcsOpdmlzaW9uIDogU0FSSU1BIikKYGBgCgoKYGBge3J9CnRzLnBsb3Qod2luZG93KGNvcnJpZ2VlLHN0YXJ0PWMoMjAxOCw1KSkscHJlZDQscHJlZDRfbCxwcmVkNF91LHhsYWI9InRlbXBzIix5bGFiPSJDb25zbyBjb3JyaWfDqWUiLGNvbD1jKDEsMiwzLDMpLGx0eT1jKDEsMSwyLDIpLGx3ZD1jKDEsMywyLDIpKQpsZWdlbmQoInRvcGxlZnQiLGxlZ2VuZD1jKCJpbnQ5NSVfaW5mIiwiaW50OTUlX3N1cCIpLGNvbD1jKDMsMyksbHR5PWMoMiwyKSxsd2Q9YygyLDIpKQp0aXRsZSgiUHLDqXZpc2lvbiA6IFNBUklNQSAtIFpvb20gIikKYGBgCmBgYHtyfQpwbmcoImltZy8xNSAtIFByw6lkaWN0aW9uIFNBUklNQSBaT09NIGF2ZWMgbW9kw6hsZSA0LnBuZyIsIHdpZHRoID0gNy4yNSwgaGVpZ2h0ID0gNC41LCB1bml0cyA9ICdpbicsIHJlcyA9IDMwMCkKdHMucGxvdCh3aW5kb3coY29ycmlnZWUsc3RhcnQ9YygyMDE4LDUpKSxwcmVkNCxwcmVkNF9sLHByZWQ0X3UseGxhYj0idGVtcHMiLHlsYWI9IkNvbnNvIGNvcnJpZ8OpZSIsY29sPWMoMSwyLDMsMyksbHR5PWMoMSwxLDIsMiksbHdkPWMoMSwzLDIsMikpCmxlZ2VuZCgidG9wbGVmdCIsbGVnZW5kPWMoImludDk1JV9pbmYiLCJpbnQ5NSVfc3VwIiksY29sPWMoMywzKSxsdHk9YygyLDIpLGx3ZD1jKDIsMikpCnRpdGxlKCJQcsOpdmlzaW9uIDogU0FSSU1BIC0gWm9vbSAiKQpgYGAKCiMgIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMKIyAtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0KIyA4IEJJTEFOIC0gQ29tcGFyYWlzb24gZGVzIG1vZMOobGVzIGxlcyBwbHVzIHBlcmZvcm1hbnRzIGhvbHQtd2ludGVycyBldCBTQVJJTUEKIyAtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0KIyAjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIwoKIyMgOC4xIEFuYWx5c2VzIGEgcG9zdMOpcmlvcmkgc3VwZXJwb3PDqWVzCmBgYHtyfQp0cy5wbG90KHdpbmRvdyh4X2FfcHJldm9pcikscHJlZDRfdHJvbmMscHJlZGhvd2lfdHJvbmMseGxhYj0idCIseWxhYj0iQ29uc29tbWF0aW9uIGNvcnJpZ8OpZSIsY29sPWMoMSwyLDMpLGx0eT1jKDEsMSwxKSxsd2Q9YygzLDMsMykpCmxlZ2VuZCgidG9wbGVmdCIsbGVnZW5kPWMoIlLDqWVsbGUiLCJTQVJJTUEiKSxjb2w9YygxLDIpLGx0eT1jKDEsMSksbHdkPWMoMywzKSkKbGVnZW5kKCJ0b3ByaWdodCIsbGVnZW5kPWMoIkhvbHQgV2ludGVycyIpLGNvbD1jKDMpLGx0eT1jKDEpLGx3ZD1jKDMpKQp0aXRsZSgiQW5hbHlzZSBhIHBvc3RlcmlvcmkgOiBTQVJJTUEgKyBIb2x0IFdpbnRlcnMgIikKYGBgCmBgYHtyfQpwbmcoImltZy8xNiAtIEFuYWx5c2UgUG9zdGVyaW9yaSBTQVJJTUEgZXQgSG9sdC1XaW50ZXJzLnBuZyIsIHdpZHRoID0gNy4yNSwgaGVpZ2h0ID0gNC41LCB1bml0cyA9ICdpbicsIHJlcyA9IDMwMCkKdHMucGxvdCh3aW5kb3coeF9hX3ByZXZvaXIpLHByZWQ0X3Ryb25jLHByZWRob3dpX3Ryb25jLHhsYWI9InQiLHlsYWI9IkNvbnNvbW1hdGlvbiBjb3JyaWfDqWUiLGNvbD1jKDEsMiwzKSxsdHk9YygxLDEsMSksbHdkPWMoMywzLDMpKQpsZWdlbmQoInRvcGxlZnQiLGxlZ2VuZD1jKCJSw6llbGxlIiwiU0FSSU1BIiksY29sPWMoMSwyKSxsdHk9YygxLDEpLGx3ZD1jKDMsMykpCmxlZ2VuZCgidG9wcmlnaHQiLGxlZ2VuZD1jKCJIb2x0IFdpbnRlcnMiKSxjb2w9YygzKSxsdHk9YygxKSxsd2Q9YygzKSkKdGl0bGUoIkFuYWx5c2UgYSBwb3N0ZXJpb3JpIDogU0FSSU1BICsgSG9sdCBXaW50ZXJzICIpCmBgYAoKIyMgOC4yIFByw6ljaXNpb24gZGUgbGEgcHLDqWRpY3Rpb24gYXZlYyBSTVNFIGV0IE1BUEUKIyMjIFJNU0UgZXN0IGTDqXBlbmRhbnQgZGUgbCfDqWNoZWxsZSwgTUFQRSBpbmRpY2UgZW4gcG91cmNlbnRhZ2UKYGBge3J9CmNhdCgnUk1TRSBIb2x0IFdpbnRlcnMgOiAnLHJtc2VfaG93aSwnXG4nKQpjYXQoJ1JNU0UgU0FSSU1BIDogJyxybXNlX3NhcmltYSwnXG4nKQpjYXQoJ01BUEUgSG90IFdpbnRlcnMgOiAnLG1hcGVfaG93aSwnXG4nKQpjYXQoJ01BUEUgU0FSSU1BIDogJyxtYXBlX3NhcmltYSwnXG4nKQpgYGAKKiAtPiBDZXMgaW5kaWNhdGV1cnMgbWV0dGVudCBlbiDDqXZpZGVuY2UgbGVzIMOpY2FydHMgcGFyIHJhcHBvcnQgw6AgbGEgcsOpYWxpdMOpLCBwbHVzIGlscyBzb250IGZhaWJsZXMsIHBsdXMgaWxzIHNvbnQgcHJvY2hlcyBkZXMgdnJhaXMgdmFsZXVycyBldCBkb25jIGlscyBzb250IG1laWxsZXVycy4KKiAtPiBBaW5zaSwgbGEgbcOpdGhvZGUgbGEgbWVpbGxldXJlIGVzdCwgcG91ciBjZSBjYXMsIFNBUklNQQoKIyAjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIwojIC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQojIE1PRMOITEUgUkVURU5VIFNBUklNQSAoMC4xLjEpKDAuMS4xKVsxMl0KIyAtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0KIyAjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIwo=