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é.

  1. Corrigez les données de consommation mensuelles de l’effet température (dues au chauffage électrique) en utilisant une régression linéaire.
  2. Effectuez une désaisonnalisation de la consommation que vous aurez obtenue après correction, grâce aux moyennes mobiles.
  3. 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
  • Data : Non Stationnaire

  • Data Train : Stationnaire

  • Data Test : Non Stationnaire

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

  1. Librarie Prophet pour un nouveau modèle prédictif.
  2. Fable pour la comparaison des modèles.
  3. 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==