Scénario
Mise en situation Vous êtes employé chez Enercoop, société coopérative qui s’est développée grâce à la libéralisation du marché de l’électricité en France. Elle est spécialisée dans les énergies renouvelables.
La plupart de ces énergies renouvelables est cependant intermittente, il est donc difficile de prévoir les capacités de production d’électricité. De plus, la demande en électricité des utilisateurs varie au cours du temps, et dépend de paramètres comme la météo (température, luminosité, etc.) Tout le challenge est de mettre en adéquation l’offre et la demande !
Votre mission Vous vous concentrerez uniquement sur la prédiction de la demande en électricité.
- Corrigez les données de consommation mensuelles de l’effet température (dues au chauffage électrique) en utilisant une régression linéaire.
- Effectuez une désaisonnalisation de la consommation que vous aurez obtenue après correction, grâce aux moyennes mobiles.
- Effectuez une prévision de la consommation (corrigée de l’effet température) sur un an, en utilisant la méthode de Holt Winters (lissage exponentiel) puis la méthode SARIMA sur la série temporelle.
Pour chaque traitement effectué (correction de l’effet température, désaisonnalisation, etc.), vous présenterez les 2 séries temporelles avant et après traitement, sur un graphique où les deux séries temporelles seront superposées.
Importation et Nettoyage
On joint les fichiers et on agrège par la moyenne par date.
On supprimer les valeurs égales à zéro qui sont des valeurs manquantes NA.
On applique le format Date à la colonne “mois”.
Donnée Consommation électrique
On a un data frame :
mois = date par mois
value = Donnée météo : chauffage
Consommation.totale : Consommation électrique totale en France , toutes sources de production confondues
Partie 1 : Correction de l’effet température
Énoncé : Corrigez les données de consommation mensuelles de l’effet température (dues au chauffage électrique) en utilisant une régression linéaire.
Visualisation

Régression linéaire
On visualise
`geom_smooth()` using formula 'y ~ x'

Modèle de Régression linéaire
On se doit de transformer la variable Concommation totale pour ne pas avoir des valeurs négatives.
On peut choisir une transformation Logarithmique ou BoxCox.
Nous allons chercher dans un premier temps la meilleure transformation.
Version logarithmique
Call:
lm(formula = log(Consommation.totale) ~ value, data = df2)
Residuals:
Min 1Q Median 3Q Max
-0.209752 -0.026461 0.002572 0.027318 0.227315
Coefficients:
Estimate Std. Error t value Pr(>|t|)
(Intercept) 1.033e+01 8.629e-03 1197.39 <2e-16 ***
value 1.236e-03 3.560e-05 34.73 <2e-16 ***
---
Signif. codes: 0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1
Residual standard error: 0.05085 on 112 degrees of freedom
Multiple R-squared: 0.915, Adjusted R-squared: 0.9143
F-statistic: 1206 on 1 and 112 DF, p-value: < 2.2e-16
(Intercept) value
10.331959472 0.001236492

Test de Breusch-Pagan (version log)
studentized Breusch-Pagan test
data: mod1
BP = 3.7034, df = 1, p-value = 0.0543
La p-value est supérieure à 5%, nous ne pouvons pas rejeter l’hypothèse H0 selon laquelle les variances sont constantes (l’hypothèse d’homoscédasticité).
Méthode Box Cox
Attachement du package : ‘MASS’
L'objet suivant est masqué depuis ‘package:dplyr’:
select

[1] -0.1414141
Attachement du package : ‘Ecfun’
L'objet suivant est masqué depuis ‘package:forecast’:
BoxCox
L'objet suivant est masqué depuis ‘package:base’:
sign
Call:
lm(formula = ((df2$Consommation.totale^lambda - 1)/lambda) ~
df2$value)
Residuals:
Min 1Q Median 3Q Max
-0.047546 -0.005935 0.000372 0.006054 0.051176
Coefficients:
Estimate Std. Error t value Pr(>|t|)
(Intercept) 5.432e+00 1.931e-03 2813.04 <2e-16 ***
df2$value 2.758e-04 7.967e-06 34.62 <2e-16 ***
---
Signif. codes: 0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1
Residual standard error: 0.01138 on 112 degrees of freedom
Multiple R-squared: 0.9145, Adjusted R-squared: 0.9138
F-statistic: 1198 on 1 and 112 DF, p-value: < 2.2e-16
studentized Breusch-Pagan test
data: BC_model
BP = 3.9081, df = 1, p-value = 0.04805
studentized Breusch-Pagan test
La p-value est inférieure à 5%, nous pouvons rejeter l’hypothèse H0 selon laquelle les variances sont constantes (l’hypothèse d’homoscédasticité).
On retient par conséquent l’hypothèse d’hétéroscédasticité des résidus de notre modèle BoxCox.
On retiendra la version BoxCox qui minimise les résidus pour notre transformation linéaire.
La différence entre les deux transformations est maigre.

Visualisation Partie 1 : Consommation corrigée par régression linéaire.

Partie 2 : Saisonnalité et Moyenne Mobile
Effectuez une désaisonnalisation de la consommation que vous aurez obtenue après correction, grâce aux moyennes mobiles.
Création d’un Vecteur de type serie temporelle

Décomposition saisonnale


Sur ce graphique interactif on remarque facilement que les années suivent la même tendance de 2012 à 2019
il suffit de cliqué sur une année pour l’exclure et inversement
L’année 2020 connait une baisse tendantielle ( dû probablement à la baisse d’activité lié à la pandémie)
2021 connait une tendance à la hausse qu’il faut relativiser : Effet de rattrapage de l’activité mais également un effet saisonnier.
Nous avons que les 6 premiers mois de l’année 2021, or juin / juillet est un pic de consommation comme nous le montre le “month plot”.


On peut voir sur ce graphique Mai et Juin 2021 sont des valeurs extrêmes.
Moyennes Mobiles

Conclusion Partie 2 :
Tendance assez stable de 2012 à 2019
Baisse en en 2020 ( baisse de l’activité lié au effet Covid ? )
Hausse sur les 6 premiers mois de 2021 : Effet de rattrapage de l’économie ? Pic de demande en Juin / Juillet augmente notre moyenne car nous n’avons pas la baisse de fin d’année. ( Voir graphique “month plot”)
Partie 3 : Prévision
Effectuez une prévision de la consommation (corrigée de l’effet température) sur un an, en utilisant la méthode de Holt Winters (lissage exponentiel) puis la méthode SARIMA sur la série temporelle.
Partitionnement de la série temporelle
Train : Donnée d’apprentissage Test : Donnée sur lesquelles on teste notre modèle

Effet stationnaire des series Train et Test Dickey-Fuller Test
Augmented Dickey-Fuller Test
data: ts
Dickey-Fuller = -3.1369, Lag order = 4, p-value = 0.1047
alternative hypothesis: stationary
Augmented Dickey-Fuller Test
data: ts_train
Dickey-Fuller = -6.456, Lag order = 4, p-value = 0.01
alternative hypothesis: stationary
Augmented Dickey-Fuller Test
data: ts_test
Dickey-Fuller = -0.84461, Lag order = 3, p-value = 0.9463
alternative hypothesis: stationary
I. la méthode de Holt Winters (lissage exponentiel)
Recherche d’un modèle optimal
ETS(A,N,A)
Call:
ets(y = .)
Smoothing parameters:
alpha = 1e-04
gamma = 1e-04
Initial states:
l = 30941.4567
s = 523.8601 58.3033 260.5293 -567.6645 -426.3778 2103.696
-4.7084 -1308.042 -1603.3 588.8305 -522.8931 897.766
sigma: 596.8593
AIC AICc BIC
1406.032 1413.417 1441.949

Prévision dans l’ensemble fiable.
Les extremums saisonniers du Data Test échappent à la prévision du modèle HW.
Vérifions
ME RMSE MAE MPE MAPE MASE ACF1 Theil's U
Training set -28.42323 542.8338 430.7379 -0.1233404 1.400094 0.6368697 -0.06080472 NA
Test set -221.88439 1959.4048 1204.9710 -1.1161636 3.907340 1.7816158 0.48988555 0.9540477
ME : Mean Error
MAPE : Mean absolute percentage error : Train 1.4 % >>> Test 3.9 %
RMSE : Root Mean Squared Error : La différence Train et Test s’explique en grande partie par les valeurs extrêmes non prédites

Prévision avec la méthode de Holt Winters
Notre modèle pour prévoir les 12 prochains mois :
ETS(A,N,A)
Call: ets(y = .)
Smoothing parameters: alpha = 1e-04 gamma = 1e-04

Évaluer le modèle
[1] 6.250623
[1] 1268881
ME RMSE MAE MPE MAPE MASE ACF1
Training set 6.250623 1121.52 656.5168 -0.1068567 2.127383 0.6903134 0.4297618
Ljung-Box test
data: Residuals from ETS(A,N,A)
Q* = 59.208, df = 9, p-value = 1.905e-09
Model df: 14. Total lags used: 23

Shapiro-Wilk normality test
data: residuals(hw)
W = 0.77275, p-value = 5.38e-12
On peut rejeter les hypothèses suivantes :
- La serie est un bruit blanc Ljung-Box test p-value < 0.05
- les résidus suivent une loi Normale Shapiro-Wilk test p-value < 0.05
/ /
II. Modèle SARIMA
Recherche d’un modèle avec la fonction auto.arima du package forecast.
On teste auto aurima sur l’ensemble du jeu de donnée.
Modélisation Train and Test

Évaluation sur Data Test

ME RMSE MAE MPE MAPE MASE ACF1 Theil's U
Training set -28.42323 542.8338 430.7379 -0.1233404 1.400094 0.6368697 -0.06080472 NA
Test set -221.88439 1959.4048 1204.9710 -1.1161636 3.907340 1.7816158 0.48988555 0.9540477
ME RMSE MAE MPE MAPE MASE ACF1 Theil's U
Training set 4.776656 595.6995 453.3286 -0.02383615 1.473304 0.6702712 -0.07046697 NA
Test set -213.668027 1944.7607 1184.1389 -1.08247871 3.840731 1.7508144 0.49239583 0.9492737
MAPE/RMSE Sarima : légèrement plus performant sur le test set que le modèle Holt Winter
Prévision avec ce modèle sur 12 mois

Évaluation du modèle SARIMA
ME RMSE MAE MPE MAPE MASE ACF1
Training set -15.01439 1231.917 676.9028 -0.192516 2.186639 0.7117488 0.4507758
On test le modèle
Ljung-Box test
data: Residuals from ARIMA(0,0,0)(0,1,1)[12]
Q* = 66.219, df = 22, p-value = 2.567e-06
Model df: 1. Total lags used: 23

Shapiro-Wilk normality test
data: residuals(new.f.arima)
W = 0.72489, p-value = 2.543e-13
On peut rejeter les hypothèses : - de bruit blanc de la série : Ljung-Box test :p value < 0.05 - et de normalité des résidus : Shapiro-Wilk normality test : p value < 0.05
Comparaison des deux modèles
Partie Test

Prévision

Accuracy
ME RMSE MAE MPE MAPE MASE ACF1
Training set 6.250623 1121.52 656.5168 -0.1068567 2.127383 0.6903134 0.4297618
ME RMSE MAE MPE MAPE MASE ACF1
Training set -15.01439 1231.917 676.9028 -0.192516 2.186639 0.7117488 0.4507758
Conclusion Partie 3 : - Deux modèles assez similaires. - Holt Winter a cependant de meilleurs performances : les données sont plus adaptées au lissage exponentiel. - Les erreurs moyennes sont notamment plus faibles dans ce modèle.
Pour aller plus loin
- Librarie Prophet pour un nouveau modèle prédictif.
- Fable pour la comparaison des modèles.
- Highcharter pour la data Viz.
Nous allons également tester la librairie Prophet de Facebook.
Error in select(., mois, Consommation_corrigée) :
unused arguments (mois, Consommation_corrigée)


Le modèle Prophet est très similaire à nos modèles précédents.
FABLE
Librairie
Plot variable not specified, automatically selected `y = Consommation_corrigée`




Comparaison des trois modèles avec Fable.

DATA VIZ amélioration
Librarie highcharter
Warning: Deprecated function. Use the `create_axis` function.
Warning: Deprecated function. Use the `create_axis` function.
LS0tDQp0aXRsZTogIlByb2pldCA5IFByw6lkaXNleiBsYSBkZW1hbmRlIGVuIMOpbGVjdHJpY2l0w6kiDQpvdXRwdXQ6IGh0bWxfbm90ZWJvb2sNCi0tLQ0KDQpgYGB7ciBzZXR1cCwgaW5jbHVkZT1GQUxTRX0NCmtuaXRyOjpvcHRzX2NodW5rJHNldChlY2hvID0gRikNCmBgYA0KDQojIFNjw6luYXJpbyANCg0KTWlzZSBlbiBzaXR1YXRpb24NClZvdXMgw6p0ZXMgZW1wbG95w6kgY2hleiBFbmVyY29vcCwgc29jacOpdMOpIGNvb3DDqXJhdGl2ZSBxdWkgcydlc3QgZMOpdmVsb3Bww6llIGdyw6JjZSDDoCBsYSBsaWLDqXJhbGlzYXRpb24gZHUgbWFyY2jDqSBkZSBs4oCZw6lsZWN0cmljaXTDqSBlbiBGcmFuY2UuIEVsbGUgZXN0IHNww6ljaWFsaXPDqWUgZGFucyBsZXMgw6luZXJnaWVzIHJlbm91dmVsYWJsZXMuDQoNCkxhIHBsdXBhcnQgZGUgY2VzIMOpbmVyZ2llcyByZW5vdXZlbGFibGVzIGVzdCBjZXBlbmRhbnQgaW50ZXJtaXR0ZW50ZSwgaWwgZXN0IGRvbmMgZGlmZmljaWxlIGRlIHByw6l2b2lyIGxlcyBjYXBhY2l0w6lzIGRlIHByb2R1Y3Rpb24gZCfDqWxlY3RyaWNpdMOpLiBEZSBwbHVzLCBsYSBkZW1hbmRlIGVuIMOpbGVjdHJpY2l0w6kgZGVzIHV0aWxpc2F0ZXVycyB2YXJpZSBhdSBjb3VycyBkdSB0ZW1wcywgZXQgZMOpcGVuZCBkZSBwYXJhbcOodHJlcyBjb21tZSBsYSBtw6l0w6lvICh0ZW1ww6lyYXR1cmUsIGx1bWlub3NpdMOpLCBldGMuKSBUb3V0IGxlIGNoYWxsZW5nZSBlc3QgZGUgbWV0dHJlIGVuIGFkw6lxdWF0aW9uIGwnb2ZmcmUgZXQgbGEgZGVtYW5kZSAhDQoNCg0KVm90cmUgbWlzc2lvbg0KVm91cyB2b3VzIGNvbmNlbnRyZXJleiB1bmlxdWVtZW50IHN1ciBsYSBwcsOpZGljdGlvbiBkZSBsYSBkZW1hbmRlIGVuIMOpbGVjdHJpY2l0w6kuDQoNCjEuIENvcnJpZ2V6IGxlcyBkb25uw6llcyBkZSBjb25zb21tYXRpb24gbWVuc3VlbGxlcyBkZSBsJ2VmZmV0IHRlbXDDqXJhdHVyZSAoZHVlcyBhdSBjaGF1ZmZhZ2Ugw6lsZWN0cmlxdWUpIGVuIHV0aWxpc2FudCB1bmUgcsOpZ3Jlc3Npb24gbGluw6lhaXJlLg0KMi4gRWZmZWN0dWV6IHVuZSBkw6lzYWlzb25uYWxpc2F0aW9uIGRlIGxhIGNvbnNvbW1hdGlvbiBxdWUgdm91cyBhdXJleiBvYnRlbnVlIGFwcsOocyBjb3JyZWN0aW9uLCBncsOiY2UgYXV4IG1veWVubmVzIG1vYmlsZXMuDQozLiBFZmZlY3R1ZXogdW5lIHByw6l2aXNpb24gZGUgbGEgY29uc29tbWF0aW9uIChjb3JyaWfDqWUgZGUgbCdlZmZldCB0ZW1ww6lyYXR1cmUpIHN1ciB1biBhbiwgZW4gdXRpbGlzYW50IGxhIG3DqXRob2RlIGRlIEhvbHQgV2ludGVycyAobGlzc2FnZSBleHBvbmVudGllbCkgcHVpcyBsYSBtw6l0aG9kZSBTQVJJTUEgc3VyIGxhIHPDqXJpZSB0ZW1wb3JlbGxlLg0KDQpQb3VyIGNoYXF1ZSB0cmFpdGVtZW50IGVmZmVjdHXDqSAoY29ycmVjdGlvbiBkZSBsJ2VmZmV0IHRlbXDDqXJhdHVyZSwgZMOpc2Fpc29ubmFsaXNhdGlvbiwgZXRjLiksIHZvdXMgcHLDqXNlbnRlcmV6IGxlcyAyIHPDqXJpZXMgdGVtcG9yZWxsZXMgYXZhbnQgZXQgYXByw6hzIHRyYWl0ZW1lbnQsIHN1ciB1biBncmFwaGlxdWUgb8O5IGxlcyBkZXV4IHPDqXJpZXMgdGVtcG9yZWxsZXMgc2Vyb250IHN1cGVycG9zw6llcy4NCg0KDQpgYGB7ciBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFLCBpbmNsdWRlPUZBTFNFfQ0KbGlicmFyeShyZWFkeGwpDQpsaWJyYXJ5KHZpc2RhdCkNCmxpYnJhcnkodGlkeXZlcnNlKQ0KbGlicmFyeShsdWJyaWRhdGUpDQpsaWJyYXJ5KGZvcmVjYXN0KQ0KbGlicmFyeShsaW5kaWEpDQpgYGANCg0KIyMgSW1wb3J0YXRpb24gIGV0IE5ldHRveWFnZSANCg0KDQpgYGB7ciAsIGluY2x1ZGU9RkFMU0V9DQojIDggUsOpZ2lvbnMgcmVwcsOpc2VudGF0aXZlcyBkZSBsYSBtw6l0w6lvIGVuIEZyYW5jZSBtw6l0cm9wb2xpdGFpbmUgDQoNCm0xIDwtIHJlYWRfZXhjZWwoIm0xLnhsc3giKSANCm0yIDwtIHJlYWRfZXhjZWwoIm0yLnhsc3giKQ0KbTMgPC0gcmVhZF9leGNlbCgibTMueGxzeCIpDQptNCA8LSByZWFkX2V4Y2VsKCJtNC54bHN4IikNCm01IDwtIHJlYWRfZXhjZWwoIm01Lnhsc3giKQ0KbTYgPC0gcmVhZF9leGNlbCgibTYueGxzeCIpDQptNyA8LSByZWFkX2V4Y2VsKCJtNy54bHN4IikNCm04IDwtICByZWFkX2V4Y2VsKCJjYWxjdWxfREpVXzE0XzExXzIwMjEueGxzeCIpDQoNCg0KDQoNCm0xIDwtIG0xWzEyOjIxLDE6MTNdDQpjb2xuYW1lcyhtMSkgPC0gYygieWVhciIsIjAxIiwiMDIiLCIwMyIsIjA0IiwiMDUiLCIwNiIsIjA3IiwiMDgiLCIwOSIsIjEwIiwiMTEiLCIxMiIpDQptMVsyOjEzXSA8LSBsYXBwbHkobTFbMjoxM10sZnVuY3Rpb24oeCkgYXMubnVtZXJpYyhhcy5jaGFyYWN0ZXIoeCkpKQ0KbTEgPC0gcGl2b3RfbG9uZ2VyKG0xLC15ZWFyKQ0KbTEgPC0gbTEgJT4lIHVuaXRlKG1vaXMseWVhcixuYW1lLHNlcCA9ICItIikNCg0KbTIgPC0gbTJbMTI6MjEsMToxM10NCmNvbG5hbWVzKG0yKSA8LSBjKCJ5ZWFyIiwiMDEiLCIwMiIsIjAzIiwiMDQiLCIwNSIsIjA2IiwiMDciLCIwOCIsIjA5IiwiMTAiLCIxMSIsIjEyIikNCm0yWzI6MTNdIDwtIGxhcHBseShtMlsyOjEzXSxmdW5jdGlvbih4KSBhcy5udW1lcmljKGFzLmNoYXJhY3Rlcih4KSkpDQptMiA8LSBwaXZvdF9sb25nZXIobTIsLXllYXIpDQptMiA8LSBtMiAlPiUgdW5pdGUobW9pcyx5ZWFyLG5hbWUsc2VwID0gIi0iKQ0KDQptMyA8LSBtM1sxMjoyMSwxOjEzXQ0KY29sbmFtZXMobTMpIDwtIGMoInllYXIiLCIwMSIsIjAyIiwiMDMiLCIwNCIsIjA1IiwiMDYiLCIwNyIsIjA4IiwiMDkiLCIxMCIsIjExIiwiMTIiKQ0KbTNbMjoxM10gPC0gbGFwcGx5KG0zWzI6MTNdLGZ1bmN0aW9uKHgpIGFzLm51bWVyaWMoYXMuY2hhcmFjdGVyKHgpKSkNCm0zIDwtIHBpdm90X2xvbmdlcihtMywteWVhcikNCm0zIDwtIG0zICU+JSB1bml0ZShtb2lzLHllYXIsbmFtZSxzZXAgPSAiLSIpDQoNCm00IDwtIG00WzEyOjIxLDE6MTNdDQpjb2xuYW1lcyhtNCkgPC0gYygieWVhciIsIjAxIiwiMDIiLCIwMyIsIjA0IiwiMDUiLCIwNiIsIjA3IiwiMDgiLCIwOSIsIjEwIiwiMTEiLCIxMiIpDQptNFsyOjEzXSA8LSBsYXBwbHkobTRbMjoxM10sZnVuY3Rpb24oeCkgYXMubnVtZXJpYyhhcy5jaGFyYWN0ZXIoeCkpKQ0KbTQgPC0gcGl2b3RfbG9uZ2VyKG00LC15ZWFyKQ0KbTQgPC0gbTQgJT4lIHVuaXRlKG1vaXMseWVhcixuYW1lLHNlcCA9ICItIikNCg0KDQptNSA8LSBtNVsxMjoyMSwxOjEzXQ0KY29sbmFtZXMobTUpIDwtIGMoInllYXIiLCIwMSIsIjAyIiwiMDMiLCIwNCIsIjA1IiwiMDYiLCIwNyIsIjA4IiwiMDkiLCIxMCIsIjExIiwiMTIiKQ0KbTVbMjoxM10gPC0gbGFwcGx5KG01WzI6MTNdLGZ1bmN0aW9uKHgpIGFzLm51bWVyaWMoYXMuY2hhcmFjdGVyKHgpKSkNCm01IDwtIHBpdm90X2xvbmdlcihtNSwteWVhcikNCm01IDwtIG01ICU+JSB1bml0ZShtb2lzLHllYXIsbmFtZSxzZXAgPSAiLSIpDQoNCm02IDwtIG02WzEyOjIxLDE6MTNdDQpjb2xuYW1lcyhtNikgPC0gYygieWVhciIsIjAxIiwiMDIiLCIwMyIsIjA0IiwiMDUiLCIwNiIsIjA3IiwiMDgiLCIwOSIsIjEwIiwiMTEiLCIxMiIpDQptNlsyOjEzXSA8LSBsYXBwbHkobTZbMjoxM10sZnVuY3Rpb24oeCkgYXMubnVtZXJpYyhhcy5jaGFyYWN0ZXIoeCkpKQ0KbTYgPC0gcGl2b3RfbG9uZ2VyKG02LC15ZWFyKQ0KbTYgPC0gbTYgJT4lIHVuaXRlKG1vaXMseWVhcixuYW1lLHNlcCA9ICItIikNCg0KbTcgPC0gbTdbMTI6MjEsMToxM10NCmNvbG5hbWVzKG03KSA8LSBjKCJ5ZWFyIiwiMDEiLCIwMiIsIjAzIiwiMDQiLCIwNSIsIjA2IiwiMDciLCIwOCIsIjA5IiwiMTAiLCIxMSIsIjEyIikNCm03WzI6MTNdIDwtIGxhcHBseShtN1syOjEzXSxmdW5jdGlvbih4KSBhcy5udW1lcmljKGFzLmNoYXJhY3Rlcih4KSkpDQptNyA8LSBwaXZvdF9sb25nZXIobTcsLXllYXIpDQptNyA8LSBtNyAlPiUgdW5pdGUobW9pcyx5ZWFyLG5hbWUsc2VwID0gIi0iKQ0KDQptOCA8LSBtOFsxMjoyMSwxOjEzXQ0KY29sbmFtZXMobTgpIDwtIGMoInllYXIiLCIwMSIsIjAyIiwiMDMiLCIwNCIsIjA1IiwiMDYiLCIwNyIsIjA4IiwiMDkiLCIxMCIsIjExIiwiMTIiKQ0KbThbMjoxM10gPC0gbGFwcGx5KG04WzI6MTNdLGZ1bmN0aW9uKHgpIGFzLm51bWVyaWMoYXMuY2hhcmFjdGVyKHgpKSkNCm04IDwtIHBpdm90X2xvbmdlcihtOCwteWVhcikNCm04IDwtIG04ICU+JSB1bml0ZShtb2lzLHllYXIsbmFtZSxzZXAgPSAiLSIpDQpgYGANCg0KLSBPbiBqb2ludCBsZXMgZmljaGllcnMgZXQgb24gYWdyw6hnZSBwYXIgbGEgbW95ZW5uZSBwYXIgZGF0ZS4NCg0KLSBPbiBzdXBwcmltZXIgbGVzIHZhbGV1cnMgw6lnYWxlcyDDoCB6w6lybyBxdWkgc29udCBkZXMgdmFsZXVycyBtYW5xdWFudGVzICpOQSouDQoNCi0gT24gYXBwbGlxdWUgbGUgZm9ybWF0IERhdGUgw6AgbGEgY29sb25uZSAibW9pcyIuDQoNCg0KYGBge3J9DQpNMSA8LWxlZnRfam9pbihtMSxtMixieT0ibW9pcyIpICU+JSBsZWZ0X2pvaW4obTMsIGJ5PSJtb2lzIikgJT4lIA0KICBsZWZ0X2pvaW4obTQsYnk9Im1vaXMiKSAlPiUgbGVmdF9qb2luKG01LGJ5PSJtb2lzIikgJT4lDQogIGxlZnRfam9pbihtNixieT0ibW9pcyIpICU+JSBsZWZ0X2pvaW4obTcsYnk9Im1vaXMiKSAlPiUgbGVmdF9qb2luKG04LGJ5PSJtb2lzIikgJT4lDQogIG11dGF0ZSh2YWx1ZSA9ICh2YWx1ZS54K3ZhbHVlLnkrdmFsdWUueC54K3ZhbHVlLnkueSt2YWx1ZS54LngueCt2YWx1ZS55LnkueSt2YWx1ZS54LngueC54K3ZhbHVlLnkueS55LnkpLzggKSAlPiUNCiAgc2VsZWN0KG1vaXMsdmFsdWUpICU+JSBmaWx0ZXIodmFsdWUgIT0iMCIpICU+JQ0KICBtdXRhdGUobW9pcz15bShtb2lzKSkNCg0KYGBgDQoNCg0KDQoNCg0KRG9ubsOpZSBDb25zb21tYXRpb24gw6lsZWN0cmlxdWUgDQpgYGB7cn0NCmRmIDwtIGVDTzJtaXhfUlRFX2VuZXJnaWVfTVssMToxNV0gIyBvbiBleGNsdXQgbGVzIGRvbm7DqWVzIE5BID09IGNvbW1lcmNlIGludGVybmF0aW9uYWxlIA0KDQpkZiA8LSBkZlssYygxLDIsMywxNCldICU+JSBmaWx0ZXIoVGVycml0b2lyZT09IkZyYW5jZSIpICMgT24gc8OpbGVjdGlvbm5lIGxhIEZyYW5jZQ0KDQpkZiA8LSBtdXRhdGUoZGYsIG1vaXM9IHltKE1vaXMpICkgIyBGb3JtYXQgZGF0ZSBwb3VyIGxhIGNvbG9ubmUgbW9pcw0KDQpkZjIgPC0gbWVyZ2UoTTEsZGYpICU+JSBzZWxlY3QobW9pcyx2YWx1ZSxDb25zb21tYXRpb24udG90YWxlKSAjIE9uIG1lcmdlIG5vcyAyIGRmLCBvbiBzw6lsZWN0aW9ubmUgbGVzIGNvbG9ubmVzIHBlcnRpbmFudGVzLg0KDQpoZWFkKGRmMikgIyBSw6lzdWx0YXQgOiBtb2lzPWRhdGUgLCB2YWx1ZT0gZWZmZXQgbcOpdMOpbyBjaGF1ZmZhZ2UgLCBjb25zb21tYXRpb24gdG90YWxlIGQnw6lsZWN0cmljaXTDqQ0KDQpgYGANCk9uIGEgdW4gZGF0YSBmcmFtZSA6DQoNCi0gbW9pcyA9IGRhdGUgcGFyIG1vaXMgDQoNCi0gdmFsdWUgPSBEb25uw6llIG3DqXTDqW8gOiBjaGF1ZmZhZ2UgDQoNCi0gQ29uc29tbWF0aW9uLnRvdGFsZSA6IENvbnNvbW1hdGlvbiDDqWxlY3RyaXF1ZSB0b3RhbGUgZW4gRnJhbmNlICwgdG91dGVzIHNvdXJjZXMgZGUgcHJvZHVjdGlvbiBjb25mb25kdWVzDQoNCg0KIyMgUGFydGllIDEgOiBDb3JyZWN0aW9uIGRlIGwnZWZmZXQgdGVtcMOpcmF0dXJlIA0KDQojIyMjIMOJbm9uY8OpIDogKkNvcnJpZ2V6IGxlcyBkb25uw6llcyBkZSBjb25zb21tYXRpb24gbWVuc3VlbGxlcyBkZSBsJ2VmZmV0IHRlbXDDqXJhdHVyZSAoZHVlcyBhdSBjaGF1ZmZhZ2Ugw6lsZWN0cmlxdWUpIGVuIHV0aWxpc2FudCB1bmUgcsOpZ3Jlc3Npb24gbGluw6lhaXJlLioNCg0KDQoNCiMjIyMjIFZpc3VhbGlzYXRpb24gDQoNCmBgYHtyfQ0KIyBsaWJyYXJ5KGdyaWRFeHRyYSkNCg0KIyBWaXN1YWxpc2F0aW9uIA0KcDEgPC0gZ2dwbG90KE0xKSArDQogIGFlcyh4PW1vaXMseT12YWx1ZSkgKw0KICBnZW9tX2xpbmUoY29sb3I9InBlcnUiKSArDQogIGdndGl0bGUoIk3DqXTDqW8gOiBCZXNvaW4gZW4gQ2hhdWZmYWdlIMOJbGVjdHJpcXVlIikgKw0KICB4bGFiKCIiKSArDQogIHlsYWIoIsOpY2FydCBkZSB0ZW1ww6lyYXR1cmUgIikNCg0KDQojIENvbnNvbW1hdGlvbiBFbGVjdHJpcXVlIGVuIEZyYW5jZSAyMDEyLTIwMjINCnAyIDwtIGdncGxvdChkZikgKw0KICBhZXMoeD1tb2lzLHk9Q29uc29tbWF0aW9uLnRvdGFsZSkgKw0KICBnZW9tX2xpbmUoY29sb3I9InN0ZWVsYmx1ZTMiKSArDQogIGdndGl0bGUoIkNvbnNvbW1hdGlvbiDDiWxlY3RyaXF1ZSBlbiBGcmFuY2UiKSArDQogIHlsYWIoIkNvbnNvbW1hdGlvbiB0b3RhbGUgKEdXaCkiKSArDQogIHhsYWIoJycpIA0KDQpncmlkRXh0cmE6OmdyaWQuYXJyYW5nZShwMSxwMikgIA0KYGBgDQoNCiMjIyMjIFLDqWdyZXNzaW9uIGxpbsOpYWlyZSANCg0KT24gdmlzdWFsaXNlIA0KYGBge3IgbWVzc2FnZT1GQUxTRX0NCmdncGxvdChkZjIpICsNCiAgYWVzKHg9Q29uc29tbWF0aW9uLnRvdGFsZSx5PXZhbHVlKSArDQogIGdlb21fcG9pbnQoKSArDQogIGdlb21fc21vb3RoKG1ldGhvZCA9ICJsbSIpICsNCiAgeWxhYigiTcOpdMOpbyA6IGNoYXVmZmFnZSIpICsNCiAgeGxhYigiQ29uc29tbWF0aW9uIHRvdGFsZSAoR1doKSIpICsNCiAgZ2d0aXRsZSgiUsOpZ3Jlc3Npb24gTGluw6lhaXJlIDogQ29uc29tbWF0aW9uIGQnw6lsZWN0cmljaXTDqSB+IEJlc29pbiBlbiBjaGF1ZmZhZ2Ugw6lsZWN0cmlxdWUgIikNCmBgYA0KTW9kw6hsZSBkZSBSw6lncmVzc2lvbiBsaW7DqWFpcmUNCg0KT24gc2UgZG9pdCBkZSB0cmFuc2Zvcm1lciBsYSB2YXJpYWJsZSBDb25jb21tYXRpb24gdG90YWxlIHBvdXIgbmUgcGFzIGF2b2lyIGRlcyB2YWxldXJzIG7DqWdhdGl2ZXMuDQoNCk9uIHBldXQgY2hvaXNpciB1bmUgdHJhbnNmb3JtYXRpb24gTG9nYXJpdGhtaXF1ZSBvdSBCb3hDb3guDQoNCk5vdXMgYWxsb25zIGNoZXJjaGVyIGRhbnMgdW4gcHJlbWllciB0ZW1wcyBsYSBtZWlsbGV1cmUgdHJhbnNmb3JtYXRpb24uDQoNCg0KIyMjIyBWZXJzaW9uIGxvZ2FyaXRobWlxdWUNCmBgYHtyfQ0KIyBSZWdyZXNzaW9uIGxpbsOpYWlyZSBMb2cgDQoNCm1vZDEgPC0gbG0obG9nKENvbnNvbW1hdGlvbi50b3RhbGUpIH4gdmFsdWUgLCBkYXRhID0gZGYyKQ0Kc3VtbWFyeShtb2QxKQ0KbW9kMSRjb2VmZmljaWVudHMNCg0KIyDDiXZhbHVhdGlvbiBkZXMgUsOpc2lkdXMgDQoNCnBhcihtZnJvdz1jKDIsMikpDQpwbG90KG1vZDEpDQoNCiMgZ2dfZGlhZ25vc2UobW9kMSkNCmBgYA0KVGVzdCBkZSBCcmV1c2NoLVBhZ2FuICh2ZXJzaW9uIGxvZykNCmBgYHtyfQ0KbG10ZXN0OjpicHRlc3QobW9kMSkNCmBgYA0KDQpMYSBwLXZhbHVlIGVzdCBzdXDDqXJpZXVyZSDDoCA1JSwgbm91cyBuZSBwb3V2b25zIHBhcyByZWpldGVyIGwnaHlwb3Row6hzZSBIMCBzZWxvbiBsYXF1ZWxsZSBsZXMgdmFyaWFuY2VzIHNvbnQgY29uc3RhbnRlcyAobCdoeXBvdGjDqHNlIGQnaG9tb3Njw6lkYXN0aWNpdMOpKS4NCg0KDQoNCiMjIyMgTcOpdGhvZGUgQm94IENveA0KDQpgYGB7ciBtZXNzYWdlPUZBTFNFfQ0KIyBCb3ggQ294IA0KbGlicmFyeShNQVNTKQ0KYmMgPC0gYm94Y294KGRmMiRDb25zb21tYXRpb24udG90YWxlIH4gZGYyJHZhbHVlKSAjIG1vZMOobGUgbGluw6lhaXJlIA0KKGxhbWJkYSA8LSBiYyR4W3doaWNoLm1heChiYyR5KV0pICMgTGFtYmRhIG9wdGltYWwgOiAtMC4xODE4MTgyIDogcHJvY2hlIGRlIHrDqXJvIA0KDQojZml0IG5ldyBsaW5lYXIgcmVncmVzc2lvbiBtb2RlbCB1c2luZyB0aGUgQm94LUNveCB0cmFuc2Zvcm1hdGlvbg0KQkNfbW9kZWwgPC0gbG0oKChkZjIkQ29uc29tbWF0aW9uLnRvdGFsZV5sYW1iZGEtMSkvbGFtYmRhKSB+IGRmMiR2YWx1ZSkNCg0KbGlicmFyeShFY2Z1bikNCmIgPC0gKChkZjIkQ29uc29tbWF0aW9uLnRvdGFsZV5sYW1iZGEtMSkvbGFtYmRhKSAtIGRmMiR2YWx1ZSAqIEJDX21vZGVsW1siY29lZmZpY2llbnRzIl1dW1siZGYyJHZhbHVlIl1dDQpiIDwtIGludkJveENveChiLGxhbWJkYSA9IGxhbWJkYSkNCmBgYA0KDQoNCmBgYHtyfQ0Kc3VtbWFyeShCQ19tb2RlbCkNCmBgYA0KDQpgYGB7cn0NCmxtdGVzdDo6YnB0ZXN0KEJDX21vZGVsKQ0KYGBgDQoNCg0Kc3R1ZGVudGl6ZWQgQnJldXNjaC1QYWdhbiB0ZXN0DQoNCkxhIHAtdmFsdWUgZXN0IGluZsOpcmlldXJlIMOgIDUlLCBub3VzIHBvdXZvbnMgcmVqZXRlciBsJ2h5cG90aMOoc2UgSDAgc2Vsb24gbGFxdWVsbGUgbGVzIHZhcmlhbmNlcyBzb250IGNvbnN0YW50ZXMgKGwnaHlwb3Row6hzZSBkJ2hvbW9zY8OpZGFzdGljaXTDqSkuDQoNCk9uIHJldGllbnQgcGFyIGNvbnPDqXF1ZW50IGwnaHlwb3Row6hzZSBkJ2jDqXTDqXJvc2PDqWRhc3RpY2l0w6kgZGVzIHLDqXNpZHVzIGRlIG5vdHJlIG1vZMOobGUgQm94Q294Lg0KDQoNCk9uIHJldGllbmRyYSBsYSB2ZXJzaW9uIEJveENveCBxdWkgbWluaW1pc2UgbGVzIHLDqXNpZHVzIHBvdXIgbm90cmUgdHJhbnNmb3JtYXRpb24gbGluw6lhaXJlLg0KDQpgYGB7cn0NCmEgPC0gbG9nKGRmMiRDb25zb21tYXRpb24udG90YWxlKSAtIGRmMiR2YWx1ZSAqIG1vZDFbWyJjb2VmZmljaWVudHMiXV1bWyJ2YWx1ZSJdXQ0KIyBleHAoYSkgPSBsb2cgICAgICAgICAgYiA9IGJveGNveCANCg0KDQpkZjIgPC0gbXV0YXRlKGRmMiwgQ29uc29tbWF0aW9uX2NvcnJpZ8OpZSA9IGIpICMgT24gY2hvaXNpdCBsYSB2ZXJzaW9uIEJveENveCANCmBgYA0KDQpMYSBkaWZmw6lyZW5jZSBlbnRyZSBsZXMgZGV1eCB0cmFuc2Zvcm1hdGlvbnMgZXN0IG1haWdyZS4NCg0KYGBge3J9DQptdXRhdGUoZGYyLCBDb25zb21tYXRpb25fY29ycmlnw6llX2JjID0gYikgJT4lIA0KICBnZ3Bsb3QoKSArDQogIGdlb21fbGluZShhZXMoeT1Db25zb21tYXRpb25fY29ycmlnw6llLHg9bW9pcyxjb2xvcj0iTG9nIiksc2l6ZT0wLjgsbGluZXR5cGU9InR3b2Rhc2giKSArDQogIGdlb21fbGluZShhZXMoeT1Db25zb21tYXRpb25fY29ycmlnw6llX2JjLHg9bW9pcyxjb2xvcj0iQm94Q294Iiksc2l6ZT0wLjgpICsNCiAgc2NhbGVfY29sb3JfbWFudWFsKHZhbHVlcyA9YygnTG9nJyA9ICdibHVlJywnQm94Q294JyA9ICdkZWVwcGluaycpKSArDQogIGxhYnMoeD0iIix5PSJDb25zb21tYXRpb24gQ29ycmlnw6llIix0aXRsZSA9ICJUcmFuc2Zvcm1hdGlvbiBMb2dhcml0aG1pcXVlIGNvbnRyZSBCb3hDb3giLGNvbG9yPSJDb25zb21tYXRpb24gQ29ycmlnw6llIikrDQogIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJ0b3AiKQ0KYGBgDQoNCg0KDQoNCiMjIyBWaXN1YWxpc2F0aW9uIFBhcnRpZSAxIDogQ29uc29tbWF0aW9uIGNvcnJpZ8OpZSBwYXIgcsOpZ3Jlc3Npb24gbGluw6lhaXJlLg0KDQpgYGB7cn0NCmdncGxvdChkZjIpKw0KICBnZW9tX2xpbmUobWFwcGluZz1hZXMoeT1Db25zb21tYXRpb24udG90YWxlLHggPSBtb2lzLGNvbG9yPSJDb25zb21tYXRpb24gVG90YWxlIiksc2l6ZT0xLGxpbmV0eXBlPSJ0d29kYXNoIikgKw0KICBnZW9tX2xpbmUobWFwcGluZz1hZXMoeT1Db25zb21tYXRpb25fY29ycmlnw6llLHg9IG1vaXMsY29sb3I9IkNvbnNvbW1hdGlvbiBDb3JyaWfDqWUgKGhvcnMgY2hhdWZmYWdlIMOpbGVjdHJpcXVlKSIpLHNpemU9MSkgKw0KICBzY2FsZV9jb2xvcl9tYW51YWwodmFsdWVzID0gYygNCiAgICAnQ29uc29tbWF0aW9uIFRvdGFsZScgPSAncGVydScsDQogICAgJ0NvbnNvbW1hdGlvbiBDb3JyaWfDqWUgKGhvcnMgY2hhdWZmYWdlIMOpbGVjdHJpcXVlKScgPSAnc3RlZWxibHVlJykpICsNCiAgbGFicyhjb2xvciA9ICcnLHk9IkNvbnNvbW1hdGlvbiB0b3RhbGUgKEdXaCkiLHg9IiIsdGl0bGUgPSBjKCJDb25zb21tYXRpb24gw4lsZWN0cmlxdWUgZW4gRnJhbmNlIikpICsNCiAgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gInRvcCIpDQoNCmBgYA0KIyMgUGFydGllIDIgOiBTYWlzb25uYWxpdMOpIGV0IE1veWVubmUgTW9iaWxlIA0KDQojIyMjICpFZmZlY3R1ZXogdW5lIGTDqXNhaXNvbm5hbGlzYXRpb24gZGUgbGEgY29uc29tbWF0aW9uIHF1ZSB2b3VzIGF1cmV6IG9idGVudWUgYXByw6hzIGNvcnJlY3Rpb24sIGdyw6JjZSBhdXggbW95ZW5uZXMgbW9iaWxlcy4qDQoNCkNyw6lhdGlvbiBkJ3VuIFZlY3RldXIgZGUgdHlwZSBzZXJpZSB0ZW1wb3JlbGxlIA0KYGBge3J9DQp0cyA8LSB0cyhkZjIkQ29uc29tbWF0aW9uX2NvcnJpZ8OpZSwgc3RhcnQ9YygyMDEyLCAxKSwgZW5kPWMoMjAyMSwgNiksIGZyZXF1ZW5jeT0xMikNCmF1dG9wbG90KHRzKSArDQogIGdndGl0bGUoIkNyw6lhdGlvbiBkJ3VuIG9iamV0IFIgOiBzZXJpZSB0ZW1wb3JlbGxlIikNCmBgYA0KIyMjIETDqWNvbXBvc2l0aW9uIHNhaXNvbm5hbGUgDQpgYGB7cn0NCiMgU2Vhc29uYWwgZGVjb21wb3NpdGlvbiAvIGTDqWNvbXBvc2l0aW9uIHNhaXNvbm5hbGUNCiNmaXQgPC0gc3RsKHRzLCBzLndpbmRvdz0icGVyaW9kIikNCiNwbG90KGZpdCkNCiNtb250aHBsb3QodHMpDQojZm9yZWNhc3Q6OnNlYXNvbnBsb3QodHMpDQpmaXQgPC0gYXV0b3Bsb3Qoc3RsKHRzLCBzLndpbmRvdz0icGVyaW9kIikpDQpmaXQgDQpgYGANCg0KDQpgYGB7cn0NCiMgc2Vhc29uIHBsb3QgDQoNCmdnc2Vhc29ucGxvdCh3aW5kb3codHMpKStnZ3RpdGxlKCJTZWFzb24gcGxvdCIpK2xhYnMoeD0iIix5PSIiKQ0KYGBgDQoNCmBgYHtyfQ0KIyBzZWFzb24gcGxvdCANCg0KcGxvdGx5OjpnZ3Bsb3RseShnZ3NlYXNvbnBsb3Qod2luZG93KHRzKSkrZ2d0aXRsZSgiU2Vhc29uYWwgcGxvdCA6IENvbnNvbW1hdGlvbiBjb3JyaWfDqWUiKSkNCmBgYA0KU3VyIGNlIGdyYXBoaXF1ZSBpbnRlcmFjdGlmIG9uIHJlbWFycXVlIGZhY2lsZW1lbnQgcXVlIGxlcyBhbm7DqWVzIHN1aXZlbnQgbGEgbcOqbWUgdGVuZGFuY2UgZGUgMjAxMiDDoCAyMDE5DQoNCippbCBzdWZmaXQgZGUgY2xpcXXDqSBzdXIgdW5lIGFubsOpZSBwb3VyIGwnZXhjbHVyZSBldCBpbnZlcnNlbWVudCoNCg0KDQpMJ2FubsOpZSAyMDIwIGNvbm5haXQgdW5lIGJhaXNzZSB0ZW5kYW50aWVsbGUgKCBkw7sgcHJvYmFibGVtZW50IMOgIGxhIGJhaXNzZSBkJ2FjdGl2aXTDqSBsacOpIMOgIGxhIHBhbmTDqW1pZSkNCg0KMjAyMSBjb25uYWl0IHVuZSB0ZW5kYW5jZSDDoCBsYSBoYXVzc2UgcXUnaWwgZmF1dCByZWxhdGl2aXNlciA6IEVmZmV0IGRlIHJhdHRyYXBhZ2UgZGUgbCdhY3Rpdml0w6kgbWFpcyDDqWdhbGVtZW50IHVuIGVmZmV0IHNhaXNvbm5pZXIuDQoNCk5vdXMgYXZvbnMgcXVlIGxlcyA2IHByZW1pZXJzIG1vaXMgZGUgbCdhbm7DqWUgMjAyMSwgb3IganVpbiAvIGp1aWxsZXQgZXN0IHVuIHBpYyBkZSBjb25zb21tYXRpb24gY29tbWUgbm91cyBsZSBtb250cmUgbGUgIm1vbnRoIHBsb3QgIi4NCg0KDQpgYGB7cn0NCmdnc3Vic2VyaWVzcGxvdCh3aW5kb3codHMpLHBvbGFyPVQpK2xhYnMoeD0iIix5PSJDb25zb21tYXRpb24gY29ycmlnw6llIikgKyBnZ3RpdGxlKCJNb250aCBwbG90IikNCmBgYA0KYGBge3J9DQpnZ3NlYXNvbnBsb3Qod2luZG93KHRzKSxwb2xhciA9IFQpICsgZ2d0aXRsZSgiU2Vhc29uIHBsb3QgUG9sYXIiKQ0KYGBgDQpPbiBwZXV0IHZvaXIgc3VyIGNlIGdyYXBoaXF1ZSBNYWkgZXQgSnVpbiAgMjAyMSBzb250IGRlcyB2YWxldXJzIGV4dHLDqm1lcy4gDQoNCg0KDQojIyMjIE1veWVubmVzIE1vYmlsZXMgDQoNCmBgYHtyfQ0KTUFfdHMgPC0gZm9yZWNhc3Q6Om1hKHRzLCBvcmRlcj0xMiwgY2VudHJlID0gVFJVRSkNCiNwbG90KHRzLCBtYWluPSJDb25zb21tYXRpb24gw4lsZWN0cmlxdWUgZW4gRnJhbmNlIixzdWI9IkNvbnNvbW1hdGlvbiBob3JzIGNoYXVmZmFnZSDDqWxlY3RyaXF1ZSIseGxhYj1OVUxMLHlsYWI9IkNvbnNvbW1hdGlvbiB0b3RhbGUgKEdXaCkiKQ0KI2xpbmVzKE1BX3RzLGNvbD0iYmx1ZSIsbHdkPTMpDQpgYGANCg0KYGBge3J9DQpkZWNvbXBfdHMgPC0gZGVjb21wb3NlKHRzKQ0KZGVjb21wX3RzX2FkaiA8LSB0cyAtIGRlY29tcF90cyRzZWFzb25hbCAjIFRpbWUgc2VyaWUgOiBjb25zb21tYXRpb24gY29ycmlnw6llIGRlcyBlZmZldHMgc2Fpc29ubmllcnMNCmBgYA0KDQoNCg0KYGBge3J9DQphdXRvcGxvdCh0cywgc2VyaWVzPSJDb25zb21tYXRpb24gQ29ycmlnw6llIixzaXplPTAuOCxsaW5ldHlwZT0idHdvZGFzaCIpICsNCiAgYXV0b2xheWVyKE1BX3RzLCBzZXJpZXM9IlRlbmRhbmNlIDogTW95ZW5uZSBNb2JpbGUiLHNpemU9MS4xLG5hLnJtID0gVCkgKw0KICAjYXV0b2xheWVyKGRlY29tcF90c19hZGosIHNlcmllcz0iRWZmZXQgU2Fpc29ubmllciBDb3JyaWfDqWUiLHNpemU9MC44LGxpbmV0eXBlPSJzb2xpZCIpICsNCiAgZ2d0aXRsZSgiQ29uc29tbWF0aW9uIMOJbGVjdHJpcXVlIGVuIEZyYW5jZSIsc3VidGl0bGUgPSAiSG9ycyBjaGF1ZmZhZ2Ugw6lsZWN0cmlxdWUiKSArDQogIGxhYnMoeD0iIix5PSJDb25zb21tYXRpb24gQ29ycmlnw6llIChHV2gpIikgKw0KICB0aGVtZShsZWdlbmQucG9zaXRpb249ImJvdHRvbSIscGxvdC50aXRsZSA9IGVsZW1lbnRfdGV4dChzaXplPTEwKSkgKw0KICBzY2FsZV9jb2xvcl9tYW51YWwodmFsdWVzPWMoJ3N0ZWVsYmx1ZScsJ3JlZCcsJ29saXZlZHJhYicpKQ0KDQpgYGANCkNvbmNsdXNpb24gUGFydGllIDIgOg0KDQotIFRlbmRhbmNlIGFzc2V6IHN0YWJsZSBkZSAyMDEyIMOgIDIwMTkNCg0KLSBCYWlzc2UgZW4gZW4gMjAyMCAoIGJhaXNzZSBkZSBsJ2FjdGl2aXTDqSBsacOpIGF1IGVmZmV0IENvdmlkID8gKQ0KDQotIEhhdXNzZSBzdXIgbGVzIDYgcHJlbWllcnMgbW9pcyBkZSAyMDIxIDogRWZmZXQgZGUgcmF0dHJhcGFnZSBkZSBsJ8OpY29ub21pZSA/IFBpYyBkZSBkZW1hbmRlIGVuIEp1aW4gLyBKdWlsbGV0IGF1Z21lbnRlIG5vdHJlIG1veWVubmUgY2FyIG5vdXMgbidhdm9ucyBwYXMgbGEgYmFpc3NlIGRlIGZpbiBkJ2FubsOpZS4gKCBWb2lyIGdyYXBoaXF1ZSAibW9udGggcGxvdCIpIA0KDQoNCg0KIyMgUGFydGllIDMgOiBQcsOpdmlzaW9uIA0KDQojIyMjICpFZmZlY3R1ZXogdW5lIHByw6l2aXNpb24gZGUgbGEgY29uc29tbWF0aW9uIChjb3JyaWfDqWUgZGUgbCdlZmZldCB0ZW1ww6lyYXR1cmUpIHN1ciB1biBhbiwgZW4gdXRpbGlzYW50IGxhIG3DqXRob2RlIGRlIEhvbHQgV2ludGVycyAobGlzc2FnZSBleHBvbmVudGllbCkgcHVpcyBsYSBtw6l0aG9kZSBTQVJJTUEgc3VyIGxhIHPDqXJpZSB0ZW1wb3JlbGxlLioNCg0KIyMjIyBQYXJ0aXRpb25uZW1lbnQgZGUgbGEgc8OpcmllIHRlbXBvcmVsbGUNCg0KVHJhaW4gOiBEb25uw6llIGQnYXBwcmVudGlzc2FnZSANClRlc3QgOiBEb25uw6llIHN1ciBsZXNxdWVsbGVzIG9uIHRlc3RlIG5vdHJlIG1vZMOobGUgDQoNCmBgYHtyfQ0KdHNfdHJhaW4gPC0gd2luZG93KHRzLCBzdGFydCA9IDIwMTIsIGVuZCA9IDIwMTguNjY2NjY2NykgIyB+IDcxICUgZGVzIGRvbm7DqWVzIA0KdHNfdGVzdCA8LSB3aW5kb3codHMsIHN0YXJ0ID0gMjAxOC43NSkgIyB+IDI5ICUgZGVzIGRvbm7DqWVzIA0KYGBgDQoNCmBgYHtyfQ0KYXV0b3Bsb3QodHNfdHJhaW4sIHNlcmllcz0iIERhdGEgdHJhaW4gfiA3MCAlIixzaXplPTAuOCkgKw0KICBhdXRvbGF5ZXIodHNfdGVzdCwgc2VyaWVzPSJEYXRhIHRlc3QgfiAzMCAlIixzaXplPTAuOCkgKw0KICBnZ3RpdGxlKCJQYXJ0aW9ubmVtZW50IGRlcyBkb25uw6llcyIpICsNCiAgdGhlbWUocGxvdC50aXRsZSA9IGVsZW1lbnRfdGV4dChzaXplPTEwKSkgKw0KICBsYWJzKHg9IiIseT0iQ29uc29tbWF0aW9uIGNvcnJpZ8OpZSAoR1doKSIpICsNCiAgdGhlbWUobGVnZW5kLnBvc2l0aW9uPSJ0b3AiLHBsb3QudGl0bGUgPSBlbGVtZW50X3RleHQoc2l6ZT0xMCkpICsNCiAgc2NhbGVfY29sb3JfbWFudWFsKHZhbHVlcz1jKCdzdGVlbGJsdWUnLCdncmVlbicpKQ0KYGBgDQoNCkVmZmV0IHN0YXRpb25uYWlyZSBkZXMgc2VyaWVzIFRyYWluIGV0IFRlc3QgIERpY2tleS1GdWxsZXIgVGVzdA0KDQpgYGB7ciB3YXJuaW5nPUZBTFNFfQ0KIyB0ZXN0DQp0c2VyaWVzOjphZGYudGVzdCh0cykgIyBOb24gU3RhdGlvbm5haXJlDQp0c2VyaWVzOjphZGYudGVzdCh0c190cmFpbikgIyBTDQp0c2VyaWVzOjphZGYudGVzdCh0c190ZXN0KSAjIE5TIA0KYGBgDQoNCi0gRGF0YSA6IE5vbiBTdGF0aW9ubmFpcmUgDQoNCi0gRGF0YSBUcmFpbiA6ICBTdGF0aW9ubmFpcmUgDQoNCi0gRGF0YSBUZXN0ICA6ICBOb24gU3RhdGlvbm5haXJlIA0KDQoNCg0KDQojIyMjIEkuIGxhIG3DqXRob2RlIGRlIEhvbHQgV2ludGVycyAobGlzc2FnZSBleHBvbmVudGllbCkNCg0KDQpSZWNoZXJjaGUgZCd1biBtb2TDqGxlIG9wdGltYWwNCmBgYHtyfQ0KdHNfZXRzIDwtIHRzX3RyYWluICU+JSBldHMoKSAjIE1vZMOobGUgZGUgdHlwZSBFVFMoQSxOLEEpDQp0c19mYyA8LSB0c19ldHMgJT4lIGZvcmVjYXN0KGg9MzMpDQoNCnRzX2V0cw0KYGBgDQoNCg0KYGBge3J9DQphdXRvcGxvdCh0c190cmFpbiwgc2VyaWVzPSIgRGF0YSBUcmFpbiIsc2l6ZT0wLjgpICsNCiAgYXV0b2xheWVyKHRzX3Rlc3QsIHNlcmllcz0iRGF0YSBUZXN0IixzaXplPTAuOCkgKw0KICBhdXRvbGF5ZXIodHNfZmMsc2VyaWU9IkhXIDogUHLDqXZpc2lvbiBzdXIgRGF0YSBUZXN0IikgKw0KICBnZ3RpdGxlKCJNb2TDqGxlIEhPTFQgV0lOVEVSUyA6IMOJdmFsdWF0aW9uIGR1IG1vZMOobGUgc3VyIERhdGEgVGVzdCIpICsNCiAgdGhlbWUocGxvdC50aXRsZSA9IGVsZW1lbnRfdGV4dChzaXplPTEwKSkgKw0KICBsYWJzKHg9IiIseT0iQ29uc29tbWF0aW9uIGNvcnJpZ8OpZSAoR1doKSIpICsNCiAgdGhlbWUobGVnZW5kLnBvc2l0aW9uPSJ0b3AiLHBsb3QudGl0bGUgPSBlbGVtZW50X3RleHQoc2l6ZT0xMCkpICsNCiAgc2NhbGVfY29sb3JfbWFudWFsKHZhbHVlcz1jKCdzdGVlbGJsdWUnLCdncmVlbicsJyNFNjlGMDAnKSkNCg0KYGBgDQpQcsOpdmlzaW9uIGRhbnMgbCdlbnNlbWJsZSBmaWFibGUuIA0KDQpMZXMgZXh0cmVtdW1zIHNhaXNvbm5pZXJzIGR1IERhdGEgVGVzdCDDqWNoYXBwZW50IMOgIGxhIHByw6l2aXNpb24gZHUgbW9kw6hsZSBIVy4NCg0KVsOpcmlmaW9ucw0KDQpgYGB7cn0NCmFjY3VyYWN5KHRzX2ZjLHRzKQ0KYGBgDQpNRSA6IE1lYW4gRXJyb3IgDQoNCk1BUEUgIDogTWVhbiBhYnNvbHV0ZSBwZXJjZW50YWdlIGVycm9yIDogIFRyYWluICAxLjQgJSAgPj4+ICBUZXN0IDMuOSAlDQoNClJNU0UgOiBSb290IE1lYW4gU3F1YXJlZCBFcnJvciA6ICBMYSBkaWZmw6lyZW5jZSBUcmFpbiBldCBUZXN0IHMnZXhwbGlxdWUgZW4gZ3JhbmRlIHBhcnRpZSANCnBhciBsZXMgdmFsZXVycyBleHRyw6ptZXMgbm9uIHByw6lkaXRlcyANCg0KDQoNCg0KYGBge3J9DQp0MSA8LWF1dG9wbG90KHRzX3Rlc3QsIHNlcmllcz0iIERhdGEgVGVzdCIsc2l6ZT0wLjgpICsNCiAgYXV0b2xheWVyKHRzX2ZjLHNlcmllPSJIVyA6IFByw6l2aXNpb24gc3VyIERhdGEgVGVzdCIpICsNCiAgZ2d0aXRsZSgiSE9MVCBXSU5URVJTIDogw4l2YWx1YXRpb24gZHUgbW9kw6hsZSBzdXIgRGF0YSBUZXN0IikgKw0KICB0aGVtZShwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KHNpemU9MTApKSArDQogIGxhYnMoeD0iIix5PSJDb25zb21tYXRpb24gY29ycmlnw6llIChHV2gpIikgKw0KICB0aGVtZShsZWdlbmQucG9zaXRpb249InRvcCIscGxvdC50aXRsZSA9IGVsZW1lbnRfdGV4dChzaXplPTEwKSkgKw0KICBzY2FsZV9jb2xvcl9tYW51YWwodmFsdWVzPWMoJ2dyZWVuJywnI0U2OUYwMCcpKQ0KDQp0MQ0KDQpgYGANCg0KDQoNCg0KDQoNCg0KIyMjIyBQcsOpdmlzaW9uIGF2ZWMgbGEgbcOpdGhvZGUgZGUgSG9sdCBXaW50ZXJzIA0KDQpOb3RyZSBtb2TDqGxlIHBvdXIgcHLDqXZvaXIgbGVzIDEyIHByb2NoYWlucyBtb2lzIDogDQoNCkVUUyhBLE4sQSkgDQoNCkNhbGw6DQogZXRzKHkgPSAuKSANCg0KICBTbW9vdGhpbmcgcGFyYW1ldGVyczoNCiAgICBhbHBoYSA9IDFlLTA0IA0KICAgIGdhbW1hID0gMWUtMDQgDQoNCg0KYGBge3J9DQoNCmh3PWV0cyh0cyxtb2RlbD0iQU5BIixhbHBoYSA9IDFlLTA0ICxnYW1tYSA9IDFlLTA0ICApICMgT24gcmVwcmVuZCBsZXMgcGFyYW3DqHRyZXMgIGFscGhhIGV0IGdhbW1hIGRlIG5vdHJlIG1vZMOobGUgDQpody5wcmVkPXByZWRpY3QoaHcsMTIpICMgUHLDqXZpc2lvbiBzdXIgbGVzIDEyIHByb2NoYWluIG1vaXMgDQojcGxvdChody5wcmVkKQ0KDQoNCmF1dG9wbG90KHRzLCBzZXJpZXM9IiBDb25zb21tYXRpb24gY29ycmlnw6llIixzaXplPTAuOCkgKw0KICBhdXRvbGF5ZXIoaHcucHJlZCwgc2VyaWVzPSIgUHLDqXZpc2lvbiBIVyB+IDEyIG1vaXMiLHNpemU9MC44KSArDQogIGdndGl0bGUoIk1vZMOobGUgSE9MVCBXSU5URVJTIDogUHLDqXZpc2lvbiBkZSBsYSBjb25zb21tYXRpb24gw6lsZWN0cmlxdWUgZW4gRnJhbmNlIikgKw0KICB0aGVtZShwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KHNpemU9MTApKSArDQogIGxhYnMoeD0iIix5PSJDb25zb21tYXRpb24gY29ycmlnw6llIChHV2gpIikgKw0KICB0aGVtZShsZWdlbmQucG9zaXRpb249InRvcCIscGxvdC50aXRsZSA9IGVsZW1lbnRfdGV4dChzaXplPTEwKSkgKw0KICBzY2FsZV9jb2xvcl9tYW51YWwodmFsdWVzPWMoJ3N0ZWVsYmx1ZScsJyNFNjlGMDAnKSkNCmBgYA0KDQrDiXZhbHVlciBsZSBtb2TDqGxlIA0KYGBge3J9DQptZWFuKGh3JHJlc2lkdWFscykNCg0KaHckYW1zZQ0KDQphY2N1cmFjeShodykNCmBgYA0KDQpgYGB7cn0NCmNoZWNrcmVzaWR1YWxzKGh3KQ0Kc2hhcGlyby50ZXN0KHJlc2lkdWFscyhodykpDQpgYGANCk9uIHBldXQgcmVqZXRlciBsZXMgaHlwb3Row6hzZXMgc3VpdmFudGVzIDoNCg0KLSBMYSBzZXJpZSBlc3QgdW4gYnJ1aXQgYmxhbmMgICAgICAgICAgICAgICAgICAgTGp1bmctQm94IHRlc3QgICAgICBwLXZhbHVlIDwgMC4wNQ0KLSBsZXMgcsOpc2lkdXMgc3VpdmVudCB1bmUgbG9pIE5vcm1hbGUgICAgICAgICAgIFNoYXBpcm8tV2lsayB0ZXN0ICAgcC12YWx1ZSAgPCAwLjA1DQoNCi8NCi8NCg0KDQoNCg0KDQoNCg0KDQojIyMjIElJLiBNb2TDqGxlIFNBUklNQSANCg0KDQoNCg0KDQoNClJlY2hlcmNoZSBkJ3VuIG1vZMOobGUgYXZlYyBsYSBmb25jdGlvbiBhdXRvLmFyaW1hIGR1IHBhY2thZ2UgZm9yZWNhc3QuDQoNCk9uIHRlc3RlIGF1dG8gYXVyaW1hIHN1ciBsJ2Vuc2VtYmxlIGR1IGpldSBkZSBkb25uw6llLiANCg0KYGBge3J9DQojIGF1dG8uYXJpbWEodHMsIHNlYXNvbmFsID0gVCx0cmFjZSA9IFQsdGVzdCA9ICJrcHNzIiwgaWM9ImJpYyIpDQpgYGANCg0KYGBge3J9DQpkLmFyaW1hIDwtIGF1dG8uYXJpbWEodHMpDQpkLmZvcmVjYXN0IDwtIGZvcmVjYXN0KGQuYXJpbWEsIGxldmVsID0gYyg5NSksIGggPSAxMikNCiMgYXV0b3Bsb3QoZC5mb3JlY2FzdCxzZXJpZT0iY29uc28iKQ0KYGBgDQoNCmBgYHtyfQ0KIyBhdXRvcGxvdCh0cywgc2VyaWVzPSIgQ29uc29tbWF0aW9uIGNvcnJpZ8OpZSIsc2l6ZT0wLjgpICsNCiMgICBhdXRvbGF5ZXIoZC5mb3JlY2FzdCwgc2VyaWVzPSIgUHLDqXZpc2lvbiBBUklNQSIsc2l6ZT0wLjgpICsNCiMgICBnZ3RpdGxlKCIgUHLDqXZpc2lvbiBkZSBjb25zb21tYXRpb24gw6lsZWN0cmlxdWUgbW9kw6hsZSBBUklNQSIpICsNCiMgICB0aGVtZShwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KHNpemU9MTApKSArDQojICAgbGFicyh4PSIiLHk9IkNvbnNvbW1hdGlvbiBjb3JyaWfDqWUgKEdXaCkiKSArDQojICAgdGhlbWUobGVnZW5kLnBvc2l0aW9uPSJ0b3AiLHBsb3QudGl0bGUgPSBlbGVtZW50X3RleHQoc2l6ZT0xMCkpICsNCiMgICBzY2FsZV9jb2xvcl9tYW51YWwodmFsdWVzPWMoJ3N0ZWVsYmx1ZScsJ21hZ2VudGEnKSkNCmBgYA0KIyMjIyBNb2TDqWxpc2F0aW9uIFRyYWluIGFuZCBUZXN0IA0KDQpgYGB7cn0NCnRyYWluLmFyaW1hIDwtIGF1dG8uYXJpbWEodHNfdHJhaW4pDQp0cmFpbi5mb3JlY2FzdCA8LSBmb3JlY2FzdCh0cmFpbi5hcmltYSwgbGV2ZWwgPSBjKDk1KSwgaCA9IDMzKQ0KYGBgDQoNCmBgYHtyfQ0KYXV0b3Bsb3QodHJhaW4uZm9yZWNhc3Qsc2VyaWU9ImNvbnNvIikNCmBgYA0Kw4l2YWx1YXRpb24gc3VyIERhdGEgVGVzdCANCg0KYGBge3J9DQp0MiA8LSBhdXRvcGxvdCh0c190ZXN0LCBzZXJpZXM9IiBDb25zb21tYXRpb24gY29ycmlnw6llIixzaXplPTAuOCkgKw0KICBhdXRvbGF5ZXIodHJhaW4uZm9yZWNhc3QsIHNlcmllcz0iIFByw6l2aXNpb24gQVJJTUEiKSArDQogIGdndGl0bGUoIiBBUklNQSA6IMOJdmFsdWF0aW9uIGR1IG1vZMOobGUgc3VyIERhdGEgVGVzdCIpICsNCiAgdGhlbWUocGxvdC50aXRsZSA9IGVsZW1lbnRfdGV4dChzaXplPTEwKSkgKw0KICBsYWJzKHg9IiIseT0iQ29uc29tbWF0aW9uIGNvcnJpZ8OpZSAoR1doKSIpICsNCiAgdGhlbWUobGVnZW5kLnBvc2l0aW9uPSJ0b3AiLHBsb3QudGl0bGUgPSBlbGVtZW50X3RleHQoc2l6ZT0xMCkpICsNCiAgc2NhbGVfY29sb3JfbWFudWFsKHZhbHVlcz1jKCdzdGVlbGJsdWUnLCdtYWdlbnRhJykpDQoNCnQyDQoNCmBgYA0KYGBge3J9DQphY2N1cmFjeSh0c19mYyx0cykgIyBIVyANCmFjY3VyYWN5KHRyYWluLmZvcmVjYXN0LHRzKSAjIFNBUklNQSANCmBgYA0KDQpNQVBFL1JNU0UgU2FyaW1hIDogbMOpZ8OocmVtZW50IHBsdXMgcGVyZm9ybWFudCBzdXIgbGUgdGVzdCBzZXQgIHF1ZSBsZSBtb2TDqGxlIEhvbHQgV2ludGVyDQoNCg0KDQojIyMjIyBQcsOpdmlzaW9uIGF2ZWMgY2UgbW9kw6hsZSBzdXIgMTIgbW9pcyANCmBgYHtyfQ0KbmV3LmFyaW1hIDwtIEFyaW1hKHk9dHMsbW9kZWwgPSB0cmFpbi5hcmltYSkNCm5ldy5mLmFyaW1hIDwtIG5ldy5hcmltYSAlPiUgZm9yZWNhc3QobGV2ZWw9Yyg5MCksaD0xMikgDQpgYGANCg0KYGBge3J9DQogYXV0b3Bsb3QodHMsIHNlcmllcz0iIENvbnNvbW1hdGlvbiBjb3JyaWfDqWUiLHNpemU9MC44KSArDQogIGF1dG9sYXllcihuZXcuZi5hcmltYSwgc2VyaWVzPSJQcsOpdmlzaW9uIFNBUklNQSB+IDEyIG1vaXMgIixzaXplPTAuMSkgKw0KICAjYXV0b2xheWVyKGQuZm9yZWNhc3Qsc2VyaWVzID0gInkiLHNpemU9MC4xKSsNCiAgI2F1dG9sYXllcihody5wcmVkLCBzZXJpZXM9IiBQcsOpdmlzaW9uIEhXIH4gMTIgbW9pcyIsc2l6ZT0wLjgpKw0KICBnZ3RpdGxlKCJTQVJJTUEgIDogUHLDqXZpc2lvbiBkZSBsYSBjb25zb21tYXRpb24gw6lsZWN0cmlxdWUgZW4gRnJhbmNlIikgKw0KICB0aGVtZShwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KHNpemU9MTApKSArDQogIGxhYnMoeD0iIix5PSJDb25zb21tYXRpb24gY29ycmlnw6llIChHV2gpIikgKw0KICB0aGVtZShsZWdlbmQucG9zaXRpb249InRvcCIscGxvdC50aXRsZSA9IGVsZW1lbnRfdGV4dChzaXplPTEwKSkgKw0KICBzY2FsZV9jb2xvcl9tYW51YWwodmFsdWVzPWMoJ3N0ZWVsYmx1ZScsJ21hZ2VudGEnLCdyZWQnKSkNCmBgYA0KDQojIyMjIyDDiXZhbHVhdGlvbiBkdSBtb2TDqGxlIFNBUklNQQ0KDQpgYGB7cn0NCmFjY3VyYWN5KG5ldy5mLmFyaW1hKQ0KYGBgDQpPbiB0ZXN0IGxlIG1vZMOobGUgDQpgYGB7cn0NCiMgVGVzdCBTQVJJTUEgDQpjaGVja3Jlc2lkdWFscyhuZXcuZi5hcmltYSkNCnNoYXBpcm8udGVzdChyZXNpZHVhbHMobmV3LmYuYXJpbWEpKQ0KYGBgDQoNCk9uIHBldXQgcmVqZXRlciBsZXMgaHlwb3Row6hzZXMgOiANCi0gIGRlIGJydWl0IGJsYW5jIGRlIGxhIHPDqXJpZSA6ICAgICAgICAgICBManVuZy1Cb3ggdGVzdCA6cCB2YWx1ZSA8IDAuMDUNCi0gIGV0IGRlIG5vcm1hbGl0w6kgZGVzIHLDqXNpZHVzIDogICAgICAgICAgU2hhcGlyby1XaWxrIG5vcm1hbGl0eSB0ZXN0IDogcCB2YWx1ZSA8IDAuMDUNCg0KDQoNCg0KDQojIyMgQ29tcGFyYWlzb24gZGVzIGRldXggbW9kw6hsZXMgDQoNCg0KIyMjIyBQYXJ0aWUgVGVzdA0KYGBge3J9DQpncmlkRXh0cmE6OmdyaWQuYXJyYW5nZSh0MSx0MikNCmBgYA0KIyMjIyBQcsOpdmlzaW9uIA0KDQpgYGB7cn0NCiMgQ29tcGFyYWlzb24gZGVzIHByw6lkaWN0aW9ucyBIVyBldCBTQVJJTUENCg0KazEgPC0gYXV0b3Bsb3QodHNfdGVzdCwgc2VyaWVzPSIgQ29uc29tbWF0aW9uIGNvcnJpZ8OpZSIsc2l6ZT0wLjgpICsNCiAgYXV0b2xheWVyKG5ldy5mLmFyaW1hLCBzZXJpZXM9IlByw6l2aXNpb24gU0FSSU1BIH4gMTIgbW9pcyAiLHNpemU9MC4xKSArDQogICNhdXRvbGF5ZXIoZC5mb3JlY2FzdCxzZXJpZXMgPSAieSIsc2l6ZT0wLjEpKw0KICAjYXV0b2xheWVyKGh3LnByZWQsIHNlcmllcz0iIFByw6l2aXNpb24gSFcgfiAxMiBtb2lzIixzaXplPTAuOCkrDQogIGdndGl0bGUoIlNBUklNQSAgOiBQcsOpdmlzaW9uIGRlIGxhIGNvbnNvbW1hdGlvbiDDqWxlY3RyaXF1ZSBlbiBGcmFuY2UiKSArDQogIHRoZW1lKHBsb3QudGl0bGUgPSBlbGVtZW50X3RleHQoc2l6ZT0xMCkpICsNCiAgbGFicyh4PSIiLHk9IkNvbnNvbW1hdGlvbiBjb3JyaWfDqWUgKEdXaCkiKSArDQogIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbj0idG9wIixwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KHNpemU9MTApKSArDQogIHNjYWxlX2NvbG9yX21hbnVhbCh2YWx1ZXM9Yygnc3RlZWxibHVlJywnbWFnZW50YScsJ3JlZCcpKQ0KDQoNCmsyIDwtIGF1dG9wbG90KHRzX3Rlc3QsIHNlcmllcz0iIENvbnNvbW1hdGlvbiBjb3JyaWfDqWUiLHNpemU9MC44KSArDQogIGF1dG9sYXllcihody5wcmVkLCBzZXJpZXM9IiBQcsOpdmlzaW9uIEhXIH4gMTIgbW9pcyIsc2l6ZT0wLjgpICsNCiAgZ2d0aXRsZSgiSE9MVCBXSU5URVJTIDogUHLDqXZpc2lvbiBkZSBsYSBjb25zb21tYXRpb24gw6lsZWN0cmlxdWUgZW4gRnJhbmNlIikgKw0KICB0aGVtZShwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KHNpemU9MTApKSArDQogIGxhYnMoeD0iIix5PSJDb25zb21tYXRpb24gY29ycmlnw6llIChHV2gpIikgKw0KICB0aGVtZShsZWdlbmQucG9zaXRpb249InRvcCIscGxvdC50aXRsZSA9IGVsZW1lbnRfdGV4dChzaXplPTEwKSkgKw0KICBzY2FsZV9jb2xvcl9tYW51YWwodmFsdWVzPWMoJ3N0ZWVsYmx1ZScsJyNFNjlGMDAnKSkNCg0KDQpjb3dwbG90OjpwbG90X2dyaWQoazIsazEsbnJvdyA9IDIpDQpgYGANCiMjIyBBY2N1cmFjeQ0KYGBge3J9DQphY2N1cmFjeShodykNCmFjY3VyYWN5KG5ldy5mLmFyaW1hKQ0KYGBgDQoNCkNvbmNsdXNpb24gUGFydGllIDMgOiANCi0gRGV1eCBtb2TDqGxlcyBhc3NleiBzaW1pbGFpcmVzLg0KLSBIb2x0IFdpbnRlciBhIGNlcGVuZGFudCBkZSBtZWlsbGV1cnMgcGVyZm9ybWFuY2VzIDogbGVzIGRvbm7DqWVzIHNvbnQgcGx1cyBhZGFwdMOpZXMgYXUgbGlzc2FnZSBleHBvbmVudGllbC4NCi0gTGVzIGVycmV1cnMgbW95ZW5uZXMgc29udCBub3RhbW1lbnQgcGx1cyBmYWlibGVzIGRhbnMgY2UgbW9kw6hsZS4NCg0KDQoNCg0KDQoNCiMgUG91ciBhbGxlciBwbHVzIGxvaW4gDQoNCjEuIExpYnJhcmllIFByb3BoZXQgcG91ciB1biBub3V2ZWF1IG1vZMOobGUgcHLDqWRpY3RpZi4NCjIuIEZhYmxlIHBvdXIgbGEgY29tcGFyYWlzb24gZGVzIG1vZMOobGVzLg0KMy4gSGlnaGNoYXJ0ZXIgcG91ciBsYSBkYXRhIFZpei4NCg0KTm91cyBhbGxvbnMgw6lnYWxlbWVudCB0ZXN0ZXIgbGEgbGlicmFpcmllIFByb3BoZXQgZGUgRmFjZWJvb2suDQoNCg0KYGBge3IgbWVzc2FnZT1GQUxTRX0NCmxpYnJhcnkocHJvcGhldCkNCmBgYA0KDQpgYGB7ciBtZXNzYWdlPUZBTFNFfQ0KIyBQcm9waGV0IA0KbSA8LSBkZjIgJT4lIHNlbGVjdChtb2lzLENvbnNvbW1hdGlvbl9jb3JyaWfDqWUpICU+JSByZW5hbWUoZHM9bW9pcyx5PUNvbnNvbW1hdGlvbl9jb3JyaWfDqWUpICU+JSBwcm9waGV0KCkNCmBgYA0KDQpgYGB7cn0NCiMgRm9yZWNhc3RpbmcgDQpmdXR1cmUgPC0gbWFrZV9mdXR1cmVfZGF0YWZyYW1lKG0scGVyaW9kcyA9IDEyLGZyZXEgPSAnbW9udGgnKQ0KcHJvcGhldF9mb3JlY2FzdCA8LSBwcmVkaWN0KG0sZnV0dXJlKQ0KcGxvdChtLHByb3BoZXRfZm9yZWNhc3QpDQpgYGANCg0KYGBge3J9DQpkeXBsb3QucHJvcGhldChtLHByb3BoZXRfZm9yZWNhc3QpDQpgYGANCg0KDQpgYGB7cn0NCnByb3BoZXRfcGxvdF9jb21wb25lbnRzKG0sIHByb3BoZXRfZm9yZWNhc3QpDQpgYGANCg0KTGUgbW9kw6hsZSBQcm9waGV0IGVzdCB0csOocyBzaW1pbGFpcmUgw6Agbm9zIG1vZMOobGVzIHByw6ljw6lkZW50cy4NCg0KDQoNCg0KIyMgRkFCTEUgDQoNCiMjIyBMaWJyYWlyaWUgDQpgYGB7cn0NCiMgRmFibGUucHJvcGhldA0KbGlicmFyeShmYWJsZSkNCmxpYnJhcnkoZmFibGUucHJvcGhldCkNCmxpYnJhcnkodHNpYmJsZSkgIyBNYW5pcHVsYXRpb24sIHRyYW5zZm9ybWF0aW9uIA0KbGlicmFyeShmZWFzdHMpDQpgYGANCg0KDQoNCmBgYHtyfQ0KZGYzIDwtIGRmMiAlPiUNCiAgbXV0YXRlKG1vaXMgPSB5ZWFybW9udGgobW9pcykpICU+JQ0KICBhc190c2liYmxlKGluZGV4ID0gbW9pcykgDQoNCg0KZGYzICU+JSBzZWxlY3QobW9pcyxDb25zb21tYXRpb25fY29ycmlnw6llKSAlPiUgZ2dfc2Vhc29uKCkNCg0KDQpkZjMgJT4lIGdnX3N1YnNlcmllcyhDb25zb21tYXRpb25fY29ycmlnw6llKQ0KDQpkZjMgJT4lIEFDRihDb25zb21tYXRpb25fY29ycmlnw6llKSAlPiUgYXV0b3Bsb3QoKQ0KDQpgYGANCmBgYHtyfQ0KIyBUcmVuZCANCg0KZGNtcCA8LSBkZjMlPiUNCiAgbW9kZWwoc3RsID0gU1RMKENvbnNvbW1hdGlvbl9jb3JyaWfDqWUpKQ0KY29tcG9uZW50cyhkY21wKQ0KDQoNCmNvbXBvbmVudHMoZGNtcCkgJT4lDQogIGFzX3RzaWJibGUoKSAlPiUNCiAgYXV0b3Bsb3QoQ29uc29tbWF0aW9uX2NvcnJpZ8OpZSwgY29sb3VyPSJncmF5IikgKw0KICBnZW9tX2xpbmUoYWVzKHk9dHJlbmQpLCBjb2xvdXIgPSAiI0Q1NUUwMCIpICsNCiAgbGFicygNCiAgICB0aXRsZSA9ICJUcmVuZCBmYWJsZSAiDQogICkNCmBgYA0KYGBge3J9DQojIGRlY29tcG9zZSANCg0KY29tcG9uZW50cyhkY21wKSAlPiUgYXV0b3Bsb3QoKQ0KYGBgDQpgYGB7cn0NCiMgc2Vhc29uIGFkanVzdGVkIA0KDQoNCmNvbXBvbmVudHMoZGNtcCkgJT4lDQogIGFzX3RzaWJibGUoKSAlPiUNCiAgYXV0b3Bsb3QoQ29uc29tbWF0aW9uX2NvcnJpZ8OpZSwgY29sb3VyID0gImdyYXkiKSArDQogIGdlb21fbGluZShhZXMoeT1zZWFzb25fYWRqdXN0KSwgY29sb3VyID0gIiMwMDcyQjIiKSArDQogIGxhYnMoeSA9ICJDb25zbyBjb3JyaWfDqWUiLA0KICAgICAgIHRpdGxlID0gIlNlYXNvbiBhZGp1c3QiKQ0KYGBgDQoNCiMjIyBDb21wYXJhaXNvbiBkZXMgdHJvaXMgbW9kw6hsZXMgYXZlYyBGYWJsZS4NCg0KYGBge3J9DQojIGZvcmVjYXN0aW5nDQoNCg0KdHJhaW4uZmFibGUgPC0gZGYzWzE6ODAsXQ0KDQpmaXQuZmFibGUgPC0gdHJhaW4uZmFibGUgJT4lDQogIG1vZGVsKA0KICAgIGFyaW1hID0gQVJJTUEoQ29uc29tbWF0aW9uX2NvcnJpZ8OpZSksDQogICAgZXRzID0gRVRTKENvbnNvbW1hdGlvbl9jb3JyaWfDqWUpLA0KICAgIHByb3BoZXQgPSBwcm9waGV0KENvbnNvbW1hdGlvbl9jb3JyaWfDqWUgfiBzZWFzb24ocGVyaW9kID0gNCwgb3JkZXIgPSAyLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB0eXBlID0gIm11bHRpcGxpY2F0aXZlIikpDQogICkNCg0KZmMuZmFibGUgPC0gZml0LmZhYmxlICU+JSBmb3JlY2FzdChoID0gIjIgeWVhcnMgOSBtb250aHMiKSAjIE9LIA0KDQoNCmZjLmZhYmxlICU+JSBhdXRvcGxvdChkZjMpDQoNCmZjLmZhYmxlICU+JSBhY2N1cmFjeShkZjMpICMgQXJpbWEsIEhXICwgcHVpcyBQcm9waGV0IC4NCmBgYA0KDQoNCiMjIERBVEEgVklaIGFtw6lsaW9yYXRpb24gDQoNCiMjIyBMaWJyYXJpZSBoaWdoY2hhcnRlcg0KYGBge3J9DQpsaWJyYXJ5KGhpZ2hjaGFydGVyKQ0KDQphaXJmb3JlY2FzdCA8LSBmb3JlY2FzdChhdXRvLmFyaW1hKEFpclBhc3NlbmdlcnMpLCBsZXZlbCA9IDk1KQ0KDQpoY2hhcnQoYWlyZm9yZWNhc3QpDQoNCg0KYGBgDQoNCmBgYHtyfQ0KdHMyIDwtIHRzKGRmMiRDb25zb21tYXRpb24udG90YWxlLCBzdGFydD1jKDIwMTIsIDEpLCBlbmQ9YygyMDIxLCA2KSwgZnJlcXVlbmN5PTEyKQ0KDQp2IDwtIGNiaW5kKHRzMix0cykNCiN2IDwtIHJlbmFtZSh2LENvbnNvbW1hdGlvbl90b3RhbGU9dHMyLENvbnNvbW1hdGlvbl9jb3JyaWfDqWU9dHMpDQoNCg0KaGMgPC0gaGNoYXJ0KHYpIA0KDQoNCiMgaGMgPC0gaGMgJT4lDQojICAgaGNhZXMoDQojICAgICBuYW1lPWMoImNvbnNvIiwiY29uc29fY29yciIpKQ0KDQoNCg0KaGMNCmBgYA0KYGBge3J9DQpoY2hhcnQoc3RsKHRzLCBzLndpbmRvdz0icGVyaW9kIikpDQpgYGANCg0KDQoNCmBgYHtyfQ0KaGNoYXJ0KHRzX2ZjKQ0KaGNoYXJ0KGh3LnByZWQpDQpgYGANCg0KDQoNCg==