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

Rappel du projet

L’objectif de ce projet est de réaliser une étude de marché à l’aide d’informations que l’on peut choisir librement pour trouver des pays dans lesquels il serait possible d’exporter des poulets depuis la France. Ainsi, idéalement, nous recherchons des pays riches et sûrs, voisins de la France dans lesquesl la consommation de protéines animale est élevée mais la production y est faible.

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

—————————

1 Importation des données

—————————

1.1 Sources des données

Les données sont extraites du site de la FAO qui fournit de nombreuses informations au niveau mondial sur des thèmes tels que la population, l’agriculture, la sécurité etc.

Il y a des variables imposées :

  • différence de population entre une année antérieure (au choix) et l’année courante, exprimée en pourcentage
  • proportion de protéines d’origine animale par rapport à la quantité totale de protéines dans la disponibilité alimentaire du pays
  • disponibilité alimentaire en protéines par habitant
  • disponibilité alimentaire en calories par habitant

Et des variables au choix : - PIB par habitant ( pour avoir une idée de la possibilité des vendre quelquechose ) - Distance depuis la France ( pour évaluer le coût de transport et l’impact écologique ) - Nom des pays au format ISO trois caractères ( pour combiner le tableau des distances ) - Production de poulets ( pour voir la concurrence potentielle ) - Indice de sécurité ( pour évaluer le risque de s’implanter dans ce pays )

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("FactoMineR", dependendcies=T)
#install.packages("factoextra",dependencies=T)
#install.packages(c("Factoshiny"))
#install.packages("ggpubr")

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

library(FactoMineR)
library(factoextra)
library(ggpubr)
library(dplyr)
library(ggplot2)
library(corrplot) 
options(ggrepel.max.overlaps = Inf)

1.4 Configuration des variables pour chaque fichier

-> définition d’une variable répertoire pour faciliter le changement d’OS

repertoire <- "/home/user/Documents/formations/oc/Formation Analyst/P5/sources/"
fic_pop <- paste (repertoire,"population.csv",sep = "")
fic_pop2017 <- paste (repertoire,"pop_2017.csv",sep = "")
fic_cal <- paste(repertoire,"kcal_personne.csv",sep="")
fic_prot_volaille <- paste(repertoire,"prot_volaille.csv",sep="")
fic_poulet_prod <- paste(repertoire,"poulets_production_2019.csv",sep="")
fic_prot_ani <- paste(repertoire,"prot_ani.csv",sep="")
fic_prot_veg <- paste(repertoire,"prot_veg.csv",sep="")
fic_pib <- paste (repertoire,"pib_gdp.csv",sep = "")
fic_dis <- paste (repertoire,"distances.csv",sep = "")
fic_iso <- paste (repertoire,"sql-pays.csv",sep = "")
fic_prod <- paste (repertoire,"production_poulet.csv",sep = "")
fic_secu <- paste (repertoire,"security.csv",sep = "")
fic_full <- paste (repertoire,"full.csv", sep = "")
fic_export_pays_cluster <- paste (repertoire,"../P5_pays_cluster.csv", sep = "")
fic_export_centroid <- paste (repertoire,"../P5_centroid.csv", sep = "")

1.5 Création des Data Frame par lecture des fichiers

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

df_pop <- read.csv2(fic_pop,header = TRUE, sep = ",", dec = ".")
df_pop2017 <- read.csv2(fic_pop2017,header = TRUE, sep = ",", dec = ".")
df_cal <- read.csv2(fic_cal,header = TRUE, sep = ",", dec = ".")
df_prot_volaille <- read.csv2(fic_prot_volaille,header = TRUE, sep = ",", dec = ".")
df_prot_ani <- read.csv2(fic_prot_ani,header = TRUE, sep = ",", dec = ".")
df_prot_veg <- read.csv2(fic_prot_veg,header = TRUE, sep = ",", dec = ".")
df_pib <- read.csv2(fic_pib,header = TRUE, sep = ",", dec = ".")
df_dis <- read.csv2(fic_dis,header = TRUE, sep = ",", dec = ".")
df_iso <- read.csv2(fic_iso,header = FALSE, sep = ",", dec = ".")
df_prod <- read.csv2(fic_prod,header = TRUE, sep = ",", dec = ".")
df_secu <- read.csv2(fic_secu,header = TRUE, sep = ",", dec = ".")

-> Vérification d’un DF créé par importation

head(df_prot_ani)

1.6 Nettoyage des DataFrames en supprimant et renommant des colonnes

-> Je supprime les colonnes superflues et normalise les noms des variables ou les rend plus explicites

df_pop <- subset(df_pop, select = c(Code.zone, Area, Value))
names(df_pop)[names(df_pop)=="Area"]<-"Zone"
names(df_pop)[names(df_pop)=="Value"]<-"Hab1000"
df_pop2017 <- subset(df_pop2017, select = c(Code.zone, Valeur))
names(df_pop2017)[names(df_pop2017)=="Valeur"]<-"Hab2017x1000"
df_cal <- subset(df_cal, select = c(Code.zone, Valeur))
names(df_cal)[names(df_cal)=="Valeur"]<-"kcal_pers_jour"
df_prot_veg <- subset(df_prot_veg, select = c(Code.zone, Valeur))
names(df_prot_veg)[names(df_prot_veg)=="Valeur"]<-"prot_veg"
df_prot_ani <- subset(df_prot_ani, select = c(Code.zone, Valeur))
names(df_prot_ani)[names(df_prot_ani)=="Valeur"]<-"prot_ani"
df_prot_volaille <- subset(df_prot_volaille, select = c(Code.zone, Valeur))
names(df_prot_volaille)[names(df_prot_volaille)=="Valeur"]<-"prot_volaille"
df_pib <- subset(df_pib, select = c(Code.zone, Valeur))
names(df_pib)[names(df_pib)=="Valeur"]<-"PIB_usd_hab"
df_dis <- subset(df_dis, select = c(X, FRA))
names(df_dis)[names(df_dis)=="X"]<-"ISO"
names(df_dis)[names(df_dis)=="FRA"]<-"distance"
df_iso <- subset(df_iso, select = c(V6, V4))
names(df_iso)[names(df_iso)=="V6"]<-"Zone"
names(df_iso)[names(df_iso)=="V4"]<-"ISO"
df_prod <- subset(df_prod, select = c(Code.zone, Valeur))
names(df_prod)[names(df_prod)=="Valeur"]<-"Poulets1000"
df_secu <- subset(df_secu, select = c(Area.Code, Value))
names(df_secu)[names(df_secu)=="Value"]<-"securite"
names(df_secu)[names(df_secu)=="Area.Code"]<-"Code.zone"

-> Vérification d’une modification de DF

head(df_prot_ani)

-> Suppression des provinces de la Chine pour éviter les doublons car on a déjà vu que la chine était représentée deux fois, une fois le pays et une fois les provinces mais aussi la France car ce n’est pas un pays d’exportation

subset(df_pop,  Zone == "France")
df_pop <- df_pop[!df_pop$Code.zone %in% c("96","128","41","214","68"),]

1.7 Compilation des df pour former la DF globale nommée df_full

-> J’aurais été tenté de l’appeler data, mais n’ayant pas encore novice, je préfère lui donner un nom plus personnel en commençant par df ( data frame ) puis full pour plein et après j’y rajouterais les différentes formes ou compléments
-> Je suis parti des pays de la population et j’ai complété avec les autres données

df_full <- merge(df_pop,df_pop2017,by="Code.zone")
df_full <- merge(df_full,df_iso,by="Zone")
df_full <- merge(df_full,df_cal,by="Code.zone")
df_full <- merge(df_full,df_prot_veg,by="Code.zone")
df_full <- merge(df_full,df_prot_ani,by="Code.zone")
df_full <- merge(df_full,df_prot_volaille,by="Code.zone")
df_full <- merge(df_full,df_pib,by="Code.zone")
df_full <- merge(df_full,df_prod,by="Code.zone")
df_full <- merge(df_full,df_dis,by="ISO")
df_full <- merge(df_full,df_secu,by="Code.zone")

-> Vérification de la compilation des DF

head(df_full)

1.8 Colonnes calculées ( évolution population et rapport protéines )

df_full$pop_evo <-100-(100*df_full$Hab2017x1000/df_full$Hab1000)
df_full$prot_rapport <-df_full$prot_ani/(df_full$prot_veg+df_full$prot_ani)

-> suppression des colonnes qui ont servi pour les calculs

df_full <- subset(df_full, select = -c(Code.zone, ISO, Hab2017x1000,prot_veg,prot_ani, prot_volaille))

Nous vérifions les variables qui auraient des manques.

print(paste0('Il y a ', dim(subset(df_full, is.na(Hab1000)))[1]," données manquantes pour le nombre d'habitants."))
[1] "Il y a 0 données manquantes pour le nombre d'habitants."
print(paste0('Il y a ', dim(subset(df_full, is.na(kcal_pers_jour)))[1]," données manquantes pour le kcal_pers_jour."))
[1] "Il y a 0 données manquantes pour le kcal_pers_jour."
print(paste0('Il y a ', dim(subset(df_full, is.na(PIB_usd_hab)))[1]," données manquantes pour le PIB_usd_hab."))
[1] "Il y a 0 données manquantes pour le PIB_usd_hab."
print(paste0('Il y a ', dim(subset(df_full, is.na(Poulets1000)))[1]," données manquantes pour le Poulets1000."))
[1] "Il y a 0 données manquantes pour le Poulets1000."
print(paste0('Il y a ', dim(subset(df_full, is.na(distance)))[1]," données manquantes pour la distance."))
[1] "Il y a 0 données manquantes pour la distance."
print(paste0('Il y a ', dim(subset(df_full, is.na(securite)))[1]," données manquantes pour la securite."))
[1] "Il y a 0 données manquantes pour la securite."
print(paste0('Il y a ', dim(subset(df_full, is.na(pop_evo)))[1]," données manquantes pour le pop_evo."))
[1] "Il y a 0 données manquantes pour le pop_evo."
print(paste0('Il y a ', dim(subset(df_full, is.na(prot_rapport)))[1]," données manquantes pour le prot_rapport."))
[1] "Il y a 0 données manquantes pour le prot_rapport."
# on aurait pu ne garder que les pays ayant toutes les variables définies avec la commande suivante
df_full <- df_full[complete.cases(df_full),]

Vérification

head(df_full)

Ecriture d’un fichier CSV avec cette DF complête pour un contrôle extérieur si nécessaire

write.csv2(df_full, file = fic_full)

DF trié par nom de pays et nom du pays comme index

df_full_order <- df_full[order(df_full[,"Zone"],decreasing=F),]
df_full_individus<- data.frame(df_full_order[,-1], row.names=df_full_order[,1])
# vérification
head(df_full_individus) 

—————————

2 - CAH Classification Assendante Hiérarchique

—————————

Objetcif : Identifier des groupes similaires dans un jeu de données

data frame centré réduit avec scale - réduit le poids des variables à forte variance

df_full_centre_reduit <- scale(df_full_individus) 

HCPC par rapport à l’ACP sur 5 clusters

-> je fais dabord un premier calcul de l’ACP pour utiliser le résultat avec la fonction HCPC
-> j’exploite le res.pca issu de la fonction PCA sur la data frame centré réduit

ACP sur la table centré réduit

res.pca <- PCA(df_full_centre_reduit, scale.unit = FALSE, graph = FALSE)

HCPC

res.hcpc <- HCPC(res.pca,nb.clust = 5, graph = FALSE)

dendograme en 5 clusters à partir de l’HCPC

img_dendogramme <- fviz_dend(res.hcpc,
          labelsize = 2,
          cex = 0.7,                     # Taille du text
          palette = "jco",               # Palette de couleur ?ggpubr::ggpar
          rect = TRUE, rect_fill = TRUE, # Rectangle autour des groupes
          rect_border = "jco",           # Couleur du rectangle
          labels_track_height = 0.8      # Augment l'espace pour le texte
          ) + ggsave("P5_img_dendogramme.png", width = 11, height = 8)
plot(img_dendogramme)

Observation des clusters

fviz_cluster(res.hcpc,
             labelsize = 3,
             repel = TRUE,            # Evite le chevauchement des textes
             show.clust.cent = TRUE, # Montre le centre des clusters
             palette = "jco",         # Palette de couleurs, voir ?ggpubr::ggpar
             ggtheme = theme_minimal(),
             main = "Factor map"
)

rajout de la variable cluster au df

df_full_centre_reduit_cluster <-res.hcpc$data.clust
pays_cluster <- subset(df_full_centre_reduit_cluster, select=c(clust))
write.csv2(pays_cluster, file = fic_export_pays_cluster)
head(pays_cluster)

Centroides - grouby sur les cluster et moyennes de chaque varibles

df_full_cluster <- cbind(df_full_individus,pays_cluster$clust)
colnames(df_full_cluster)[colnames(df_full_cluster)=="pays_cluster$clust"] <- "clust"
df_full_centroides <- df_full_cluster %>%
  group_by(clust) %>%
  summarize(moy_hab = mean(Hab1000, na.rm = TRUE), moy_dis = mean(distance,na.rm=TRUE), moy_PIB=mean(PIB_usd_hab,na.rm=TRUE),moy_poulets=mean(Poulets1000,na.rm=TRUE),
            moy_secu=mean(securite,na.rm=TRUE),moy_kcal=mean(kcal_pers_jour,na.rm=TRUE), moy_pop_evo=mean(pop_evo,na.rm=TRUE),moy_prot=mean(prot_rapport,na.rm=TRUE) )
write.csv2(df_full_centroides, file = fic_export_centroid)
head(df_full_centroides)
df_centroid <- df_full_centroides[,2:9]
df_centroid_scaled <- scale(df_centroid)
Matrice <- data.matrix(df_centroid_scaled)
print(Matrice)
        moy_hab     moy_dis    moy_PIB moy_poulets    moy_secu   moy_kcal moy_pop_evo
[1,] -0.4009869 -0.09033865 -0.6414561  -0.4676152 -0.82938665 -1.2559588  1.57151929
[2,]  1.7880118  0.86341248 -0.5549279   1.7883461 -1.03896752 -0.3772466  0.06496343
[3,] -0.4537773  1.12973859 -0.3495361  -0.4324813  0.50847179 -0.3622087 -0.13029278
[4,] -0.4462014 -1.23398449 -0.2178675  -0.4156580 -0.04444845  0.7164432 -1.18600755
[5,] -0.4870461 -0.66882794  1.7637878  -0.4725916  1.40433082  1.2789709 -0.32018239
       moy_prot
[1,] -1.4253574
[2,] -0.4060094
[3,]  0.2949569
[4,]  0.2504042
[5,]  1.2860057
attr(,"scaled:center")
      moy_hab       moy_dis       moy_PIB   moy_poulets      moy_secu      moy_kcal 
 1.178189e+05  5.947875e+03  1.958858e+04  4.148921e+05 -1.261467e-01  2.943993e+03 
  moy_pop_evo      moy_prot 
 1.194850e+00  4.364698e-01 
attr(,"scaled:scale")
     moy_hab      moy_dis      moy_PIB  moy_poulets     moy_secu     moy_kcal  moy_pop_evo 
2.196203e+05 2.991056e+03 2.798917e+04 7.913869e+05 8.699533e-01 3.790431e+02 7.637357e-01 
    moy_prot 
1.405852e-01 

Description de la répartition des clusters

res.hcpc$desc.var

Link between the cluster variable and the quantitative variables
================================================================
                    Eta2      P-value
PIB_usd_hab    0.7467896 3.244785e-37
Poulets1000    0.6596635 4.762308e-29
prot_rapport   0.6233566 2.959460e-26
kcal_pers_jour 0.5553040 1.093472e-21
pop_evo        0.5416622 7.381464e-21
distance       0.5352245 1.781485e-20
Hab1000        0.4606421 2.109411e-16
securite       0.4350143 3.894074e-15

Description of each cluster by quantitative variables
=====================================================
$`1`
                  v.test Mean in category  Overall mean sd in category Overall sd
pop_evo         7.801112        0.9786282 -2.714581e-17     0.54097450  0.9962335
PIB_usd_hab    -4.315366       -0.5413509 -3.201413e-17     0.08106279  0.9962335
securite       -6.068048       -0.7612200  1.330389e-18     0.84216224  0.9962335
kcal_pers_jour -6.729919       -0.8442500  1.357519e-16     0.75417992  0.9962335
prot_rapport   -8.588433       -1.0773955  7.492571e-17     0.48658880  0.9962335
                    p.value
pop_evo        6.136397e-15
PIB_usd_hab    1.593388e-05
securite       1.294742e-09
kcal_pers_jour 1.697575e-11
prot_rapport   8.816324e-18

$`2`
              v.test Mean in category  Overall mean sd in category Overall sd      p.value
Poulets1000 9.317068         4.587946 -1.383214e-17       3.002534  0.9962335 1.196001e-20
Hab1000     7.778593         3.830364  1.943412e-18       3.933976  0.9962335 7.333563e-15

$`3`
                v.test Mean in category  Overall mean sd in category Overall sd
distance      7.196330        0.8039690  5.154607e-17      0.8196829  0.9962335
securite      4.480013        0.5005039  1.330389e-18      0.6781611  0.9962335
prot_rapport  4.169296        0.4657908  7.492571e-17      0.6649568  0.9962335
pop_evo      -2.098623       -0.2344567 -2.714581e-17      0.6774155  0.9962335
                  p.value
distance     6.185483e-13
securite     7.463847e-06
prot_rapport 3.055425e-05
pop_evo      3.585011e-02

$`4`
                  v.test Mean in category  Overall mean sd in category Overall sd
kcal_pers_jour  5.459027        0.9379086  1.357519e-16      0.6784478  0.9962335
prot_rapport    2.478485        0.4258254  7.492571e-17      0.6974020  0.9962335
pop_evo        -5.744707       -0.9869909 -2.714581e-17      0.9007136  0.9962335
distance       -6.337045       -1.0887597  5.154607e-17      0.2718960  0.9962335
                    p.value
kcal_pers_jour 4.787501e-08
prot_rapport   1.319416e-02
pop_evo        9.208006e-09
distance       2.342134e-10

$`5`
                  v.test Mean in category  Overall mean sd in category Overall sd
PIB_usd_hab     9.521384         3.064530 -3.201413e-17      1.2229449  0.9962335
kcal_pers_jour  4.493226         1.446179  1.357519e-16      0.2544259  0.9962335
prot_rapport    4.209311         1.354799  7.492571e-17      0.4681281  0.9962335
securite        4.180053         1.345382  1.330389e-18      0.2388849  0.9962335
distance       -1.976700        -0.636216  5.154607e-17      1.3324673  0.9962335
                    p.value
PIB_usd_hab    1.708871e-21
kcal_pers_jour 7.015228e-06
prot_rapport   2.561507e-05
securite       2.914417e-05
distance       4.807553e-02

Heatmap des variables dans chaque cluster pour voir leur importance

corrplot(Matrice, is.corr = FALSE, method = "circle")

Interprétation : -> plus le point est bleu, plus la variable caractérise le cluster
-> plus le point est grand, plus la variable est forte
-> plus le point est rouge moins la variable caractérise le cluster
-> plus le point est petit plus la variable est faible

CLUSTER 1
- évolution de la population est forte
- kcal et protéines sont très faibles
CLUSTER 2 - nombre d’habitants et de poulets élevés
- distance un peu favorable
- la sécurité est en revanche défavorable
CLUSTER 3
- seul la distance est l’atout de cet ensemble
CLUSTER 4
- distance et évolution de la population sont deux variables particulièrement fortes et négatives pour cet ensemble
CLUSTER 5
- des pays riches et sûrs
- une alimentation riche en protéines et en kcal

-> Le groupe 5 présente les plus intéressants paramètres pour notre projet car on y trouve les meilleurs résultats en matière de richesse et de sécurité, ce qui permet d’envisager un commerce plus stable et moins risqué. De plus l’alimentation est plutôt riche.

—————————

3 ACP - Analyse de Composantes Principales

—————————

res.desc <- dimdesc(res.pca, axes = c(1,2), proba = 0.05)
res.desc$Dim.1
$quanti
               correlation      p.value
prot_rapport     0.9031001 6.236248e-50
kcal_pers_jour   0.7653118 7.768570e-27
securite         0.7587525 3.734427e-26
PIB_usd_hab      0.7404124 2.337017e-24
Hab1000         -0.2167121 1.222769e-02
pop_evo         -0.7111666 8.718108e-22

attr(,"class")
[1] "condes" "list"  

Eboulis des valeurs propres : pourcentage de variance expliquée par chaque dimension

eig.val <- get_eigenvalue(res.pca) 
eig.val
      eigenvalue variance.percent cumulative.variance.percent
Dim.1  3.0903096        38.921512                    38.92151
Dim.2  1.4739074        18.563418                    57.48493
Dim.3  1.2049839        15.176407                    72.66134
Dim.4  0.7415876         9.340071                    82.00141
Dim.5  0.5860269         7.380831                    89.38224
Dim.6  0.3829458         4.823087                    94.20533
Dim.7  0.2666754         3.358696                    97.56402
Dim.8  0.1934130         2.435978                   100.00000
fviz_eig(res.pca, addlabels = TRUE, ylim = c(0, 50))

-> On observe un coude au niveau de la seconde dimension. Après quoi la diminution est plus régulière.
Mais il sera peut-être nécessaire d’observer les 3 premières composantes principales pour totaliser 70% des informations.

Comparaison de la qualité de la représentation des variables en fonction des dimensions

var <- get_pca_var(res.pca)
corrplot(var$cos2, is.corr=FALSE,addCoef.col = "black", title="Qualité de représentation des variables", mar=c(0,0,1,0))

-> dans la première composante, ce sont kcal, pib, sécurité et rapport en protéines qui sont le mieux représentées
-> dans la seconde, c’est le nombre de poulets et d’habitants.
-> dans la troisième, c’est la distance qui est le mieux représentée

Graphique des corrélations pour les dimensions 1 et 2

fviz_pca_var (res.pca, col.var = "cos2",
              gradient.cols = c("#00AFBB", "#E7B800", "#FC4E07"),
              repel = TRUE # Évite le chevauchement de texte
)

fviz_pca_var(res.pca, col.var = "cos2",gradient.cols = c("#00AFBB", "#E7B800", "#FC4E07"),repel = TRUE)+ labs(title = "Cercle des corrélations") + theme(plot.title = element_text(color="#3876C2", size=20, face="bold",hjust = 0.5),axis.title.x = element_text(color="black", size=14, face="bold",hjust = 0.5),axis.title.y = element_text(color="black", size=14, face="bold",vjust = 0.5))

Le graphique de corrélation des variables montre les relations entre toutes les variables et les axes factoriels. Il peut être interprété comme suit:

Les variables positivement corrélées sont regroupées :
    disp_ali_prot_g_pers_jour, disp_ali_kcal_pers_jour
    prop_ani_disp_ali, pib_hab
Les variables négativement corrélées sont positionnées sur les côtés opposés de l'origine du graphique (quadrants opposés) :
    indice_risque_pays et evolution_pop_2015_2016 par rapport aux autres
La distance entre les variables et l'origine mesure la qualité de représentation des variables. Les variables qui sont loin de l'origine sont bien représentées par l'ACP (couleur rouge / orange). Toutes les variables (un peu moins pour evolution_pop_2015_2016 : couleur bleue) sont bien expliquées par les deux premières composantes principales (Dim.1 & Dim.2) car elles sont positionnées près du le cercle de corrélation.

Soit le repère orthogonal porté par les axes (distance_fr_fm et un axe normal à ce dernier).

La dimension 1 symbolise la qualité de vie dans le pays : les pays les plus à droite sont les pays où la qualité de vie est la meilleur (fort PIB par habitant, forte disponibilité alimentaire en kcal / protéines, forte consommation de produits d'origine animale, bonne stabilité politique).
La dimension 2 symbolise la distance du pays à la France. : plus un pays est en bas, plus il est proche de la France.
Les pays qui nous intéressent sont donc les pays les plus à droite (prioritairement car la distance reste secondaire au vue de la qualité de vie dans le pays) et les plus en bas.

De manière générale, le cercle des corrélations n’est pas fait pour interpréter les corrélations entre les variables initiales. Il est fait pour interpréter les axes d’inertie F1, F2, etc. Pour vérifier la corrélation entre les variables, les coefficients de corrélations entre les variables sont calculés et affichés dans la matrice des corrélations.

qualité de représentation ( cos2 ) barplot

Variables

fviz_cos2(res.pca, choice = "var")

—————————

4 Clusters

—————————

4.1 Analyse des différents groupes

cluster 1 - évolution de la population sinificativement supérieur à la moyenne

cluster 2 - nombre de poulets sinificativement supérieur à la moyenne

cluster 3 - distances sinificativement supérieur à la moyenne

cluster 4 - évolution des protéines sinificativement supérieur à la moyenne

cluster 5 - PIB sinificativement supérieur à la moyenne

Représentation visuelle des Clusters

df_full_centre_reduit_cluster_trans <- transform(df_full_centre_reduit_cluster, clust = as.factor(clust)) 
fviz_pca_biplot(res.pca, pointsize = 1, labelsize = 2, axes = c(1,2), select.ind = list(cos2 = 0.50),addEllipses = TRUE, ellipse.level=0.60,col.ind = df_full_centre_reduit_cluster$clust, color_palette=c("#00AFBB", "#E7B800", "#FC4E07","#FC4E07","#FC4E07")) +  theme(plot.title = element_text(color="#3876C2", size=20, face="bold",hjust = 0.5),axis.title.x = element_text(color="black", size=14, face="bold",hjust = 0.5),axis.title.y = element_text(color="black", size=14, face="bold",vjust = 0.5)) + ggsave("P5_img_biplot_pca.png", width = 11, height = 8)

-> Cette représentation confirme le choix du cluster 5 car c’est le cluster qui s’approche le plus des consommations souhaitées, mais aussi pour la richesse et la sécurité

4.2 Choix du cluster 5

df_full_individus
df_full_individus$Area <- rownames(df_full_individus)
df_clust5 <- subset(df_full_centre_reduit_cluster, clust == 5)
#df_clust5_full <- df_clust5[,0]
#df_clust5_full$Area <- rownames(df_clust5_full)
#df_clust5_full <- merge(df_clust5_full,df_full_individus, by = 'Area')
#df_clust5 <- subset(df_full_centre_reduit_cluster, clust == 5)
print(df_clust5[,0])

—————————

5 Cluster numéro 5 - Application de nouvelles analyses

—————————

5.1 Suppression de variables

clust car on garde tous les pays
PIB_usb_hab car tous ces pays sont riches
securite car tous ces pays sont sûrs
Poulets1000 car ils n’ont pas une grande quantité de volailles
pop_evo car l’évolution est semblable dans tous ces pays ( plutôt faible )

df_clust5 <- subset(df_clust5, select = -c(clust,PIB_usd_hab,Poulets1000, pop_evo, prot_rapport,securite))

5.2 Ajout d’une nouvelle variable pour essayer de distinguer les individus de ce groupe

# ajout de la production de poulets
fic_poulet_prod <- paste(repertoire,"poulets_production_2019.csv",sep="")
df_poulet_prod <- read.csv2(fic_poulet_prod,header = TRUE, sep = ",", dec = ".")
df_poulet_prod <- subset(df_poulet_prod,select = c(Area,Value))
names(df_pop)[names(df_pop)=="Zone"]<-"Area"
df_poulet_prod <- merge(df_poulet_prod,df_pop,by="Area")
df_poulet_prod$prod_poulet_hab <-df_poulet_prod$Value/df_poulet_prod$Hab1000
df_poulet_prod <- subset(df_poulet_prod,select = c(Area,prod_poulet_hab))

df_clust5$Area <- rownames(df_clust5)
#df_clust5_prod <- merge(df_clust5,df_poulet_prod,by="Area")
#names(df_clust5_prod)[names(df_clust5_prod)=="Value"]<-"Prod_PouletX1000"

#df_clust5_prod<- data.frame(df_clust5_prod[,-1], row.names=df_clust5_prod[,1])
#df_full_individus
df_full_individus$Area <- rownames(df_full_individus)

df_clust5_full <- df_clust5[,0]
df_clust5_full$Area <- rownames(df_clust5_full)
df_clust5_full <- merge(df_clust5_full,df_full_individus, by = 'Area')
df_clust5_full <- merge(df_clust5_full,df_poulet_prod, by = 'Area')
df_clust5_full <- subset(df_clust5_full, select = -c(PIB_usd_hab,Poulets1000, pop_evo, prot_rapport,securite))
df_clust5_full<- data.frame(df_clust5_full[,-1], row.names=df_clust5_full[,1])

df_clust5 <- subset(df_clust5, select = -c(Area))

#df_clust5 <- subset(df_full_centre_reduit_cluster, clust == 5)
#print(df_clust5[,0])

Affichage des tables pour contrôle

df_pop
df_clust5
df_poulet_prod
#df_clust5_prod
df_clust5_full

6.3 ACP sur la table centré réduit du cluster 5

res5.pca <- PCA(df_clust5, scale.unit = FALSE, graph = FALSE)

6.4 HCPC par rapport à l’ACP sans préciser le nombre de clusters

res5.hcpc <- HCPC(res5.pca, graph = FALSE) 

6.5 dendograme à partir de l’HCPC

fviz_dend(res5.hcpc, 
          cex = 0.7,                     # Taille du text
          palette = "jco",               # Palette de couleur ?ggpubr::ggpar
          rect = TRUE, rect_fill = TRUE, # Rectangle autour des groupes
          rect_border = "jco",           # Couleur du rectangle
          labels_track_height = 0.8      # Augment l'espace pour le texte
)

-> Le dendagramme a constitué 3 clusters ce qui semble un nombre correct de groupes compte tenu du nombre de pays dans chacun d’eux.

Justifier le choix des variables pib et sécurité corrélés mais tous ces pays sont riches et sécurisés

Observation des clusters

fviz_cluster(res5.hcpc,
             labelsize = 8 ,
             show.clust.cent = TRUE, # Montre le centre des clusters
             palette = "jco",         # Palette de couleurs, voir ?ggpubr::ggpar
             ggtheme = theme_minimal(),
             main = "Factor map"
)

fviz_pca_var (res5.pca, col.var = "cos2",
              gradient.cols = c("#00AFBB", "#E7B800", "#FC4E07"),
              repel = TRUE # Évite le chevauchement de texte
)

df_clust5_scaled <- as.data.frame(df_clust5)
res5.pca <- PCA(df_clust5_scaled, graph = FALSE)

éboulis de valeurs propres

eig5.val <- get_eigenvalue(res5.pca)

perte d’inertie en fonction des dimensions

fviz_eig(res5.pca, addlabels = TRUE, ylim = c(0, 50))

var5 <- get_pca_var(res5.pca)
corrplot(var5$cos2, is.corr=FALSE, title="Qualité des variables", mar=c(0,0,1,0))

# Colorer en fonction du cos2: qualité de représentation
fviz_pca_var(res5.pca, col.var = "cos2",gradient.cols = c("#00AFBB", "#E7B800", "#FC4E07"),repel = TRUE) + labs(title = "Figure 10 - Cercle des corrélations") + theme(plot.title = element_text(color="#3876C2", size=20, face="bold",hjust = 0.5),axis.title.x = element_text(color="black", size=14, face="bold",hjust = 0.5),axis.title.y = element_text(color="black", size=14, face="bold",vjust = 0.5)) 

NA
NA
fviz_pca_biplot(res5.pca, col.var = "#2E9FDF", pointsize = 1, labelsize = 3, axes = c(1,2)) +  theme(plot.title = element_text(color="#3876C2", size=20, face="bold",hjust = 0.5),axis.title.x = element_text(color="black", size=14, face="bold",hjust = 0.5),axis.title.y = element_text(color="black", size=14, face="bold",vjust = 0.5))

df_clust_scaled_cluster <-res5.hcpc$data.clust
df_clust_scaled_cluster
pays_cluster <- subset(df_clust_scaled_cluster, select=c(clust))
# vérification
# summary(df_full_cluster)
pays_cluster
df_clust_scaled_cluster_trans <- transform(df_clust_scaled_cluster, clust = as.factor(clust)) 
fviz_pca_biplot(res5.pca, pointsize = 1, labelsize = 2, axes = c(1,2), select.ind = list(cos2 = 0.30),addEllipses = TRUE, ellipse.level=0.60,col.ind = df_clust_scaled_cluster$clust, color_palette=c("#00AFBB", "#E7B800", "#FC4E07","#FC4E07","#FC4E07")) +  theme(plot.title = element_text(color="#3876C2", size=20, face="bold",hjust = 0.5),axis.title.x = element_text(color="black", size=14, face="bold",hjust = 0.5),axis.title.y = element_text(color="black", size=14, face="bold",vjust = 0.5))

df_clust_scaled_cluster_trans <- transform(df_clust_scaled_cluster, clust = as.factor(clust)) 
fviz_pca_biplot(res5.pca, pointsize = 1, labelsize = 2, axes = c(1,3), select.ind = list(cos2 = 0.30),addEllipses = TRUE, ellipse.level=0.60,col.ind = df_clust_scaled_cluster$clust, color_palette=c("#00AFBB", "#E7B800", "#FC4E07","#FC4E07","#FC4E07")) +  theme(plot.title = element_text(color="#3876C2", size=20, face="bold",hjust = 0.5),axis.title.x = element_text(color="black", size=14, face="bold",hjust = 0.5),axis.title.y = element_text(color="black", size=14, face="bold",vjust = 0.5))

NA
NA

En faisant un ACP sur l’axe 1.2 et 1.3, on observe un groupe se distinguer des autres, le cluster 3 Islande, Luxembourg, Suisse, Finlande et Norvège.

si l’on souhaite réduire encore l’échantillon, je propose de prendre en compte la distance, ce qui réduira la sélection au Luxembourg et la Suisse, lesquels sont frontaliers, riches et qui plus est francophones.

trouver les informations concernant la production, l’importation et éventuellement la consommation

test avec df_clust5_full # ################################################### ### ACP sur la table centré réduit du cluster 5

df_clust5_full
df_clust5_full_centre_reduit
                        Hab1000 kcal_pers_jour    distance prod_poulet_hab
Australia             0.5396770     -0.5428360  2.87116921      0.21115057
Canada                1.1777444      1.1636845  0.51793967      0.24849363
Denmark              -0.4636119     -0.4453206 -0.55487032      0.03870601
Finland              -0.4756407     -1.0109102 -0.36466099     -0.59947471
France                2.6405942      0.5395855 -0.77616541     -0.03294302
Iceland              -0.7473900      2.0218205 -0.29529018     -0.90547345
Israel               -0.3258267      0.7931257 -0.05861119      0.60724951
Kuwait               -0.5482312      0.2372876  0.17909646      2.75647840
Luxembourg           -0.7333708      0.1787783 -0.71422707     -1.03864342
Norway               -0.4853149     -0.7378670 -0.48708771     -0.23466830
Switzerland          -0.3182759     -0.9036432 -0.67070839     -0.69702870
United Arab Emirates -0.2603535     -1.2937051  0.35341593     -0.35384653
attr(,"scaled:center")
        Hab1000  kcal_pers_jour        distance prod_poulet_hab 
   14599.353750     3446.666667     3608.653838        3.772712 
attr(,"scaled:scale")
        Hab1000  kcal_pers_jour        distance prod_poulet_hab 
   19083.264400      102.547845     4649.336044        3.422019 
df_clust5_full_centre_reduit <- scale(df_clust5_full)
res5.pca <- PCA(df_clust5_full_centre_reduit, scale.unit = FALSE, graph = FALSE)

HCPC par rapport à l’ACP et je laisse libre le nombre de clusters

res5.hcpc <- HCPC(res5.pca, graph = FALSE) # nb.clust = 4,

dendograme à partir de l’HCPC

fviz_dend(res5.hcpc, 
          cex = 0.7,                     # Taille du text
          palette = "jco",               # Palette de couleur ?ggpubr::ggpar
          rect = TRUE, rect_fill = TRUE, # Rectangle autour des groupes
          rect_border = "jco",           # Couleur du rectangle
          labels_track_height = 0.8      # Augment l'espace pour le texte
)

Observation des clusters

fviz_cluster(res5.hcpc,
             labelsize = 8 ,
             show.clust.cent = TRUE, # Montre le centre des clusters
             palette = "jco",         # Palette de couleurs, voir ?ggpubr::ggpar
             ggtheme = theme_minimal(),
             main = "Factor map"
)

fviz_pca_var (res5.pca, col.var = "cos2",
              gradient.cols = c("#00AFBB", "#E7B800", "#FC4E07"),
              repel = TRUE # Évite le chevauchement de texte
)


res5.pca <- PCA(df_clust5_full_centre_reduit, graph = FALSE)

éboulis de valeurs propres

eig5.val <- get_eigenvalue(res5.pca)

perte d’inertie en fonction des dimensions

fviz_eig(res5.pca, addlabels = TRUE, ylim = c(0, 50))

var5 <- get_pca_var(res5.pca)
corrplot(var5$cos2, is.corr=FALSE, title="Qualité des variables", mar=c(0,0,1,0))

# Colorer en fonction du cos2: qualité de représentation
fviz_pca_var(res5.pca, col.var = "cos2",gradient.cols = c("#00AFBB", "#E7B800", "#FC4E07"),repel = TRUE) + labs(title = "Figure 10 - Cercle des corrélations") + theme(plot.title = element_text(color="#3876C2", size=20, face="bold",hjust = 0.5),axis.title.x = element_text(color="black", size=14, face="bold",hjust = 0.5),axis.title.y = element_text(color="black", size=14, face="bold",vjust = 0.5)) 

NA
NA
fviz_pca_biplot(res5.pca, col.var = "#2E9FDF", pointsize = 1, labelsize = 3, axes = c(1,2)) +  theme(plot.title = element_text(color="#3876C2", size=20, face="bold",hjust = 0.5),axis.title.x = element_text(color="black", size=14, face="bold",hjust = 0.5),axis.title.y = element_text(color="black", size=14, face="bold",vjust = 0.5))

fviz_pca_biplot(res5.pca, pointsize = 1, labelsize = 3, axes = c(1,2), select.ind = list(cos2 = 0.30),addEllipses = TRUE, ellipse.level=0.60,col.ind = df_clust_scaled_cluster$clust, color_palette=c("#00AFBB", "#E7B800", "#FC4E07","#FC4E07","#FC4E07")) +  theme(plot.title = element_text(color="#3876C2", size=20, face="bold",hjust = 0.5),axis.title.x = element_text(color="black", size=14, face="bold",hjust = 0.5),axis.title.y = element_text(color="black", size=14, face="bold",vjust = 0.5))

df_clust_scaled_cluster_trans <- transform(df_clust_scaled_cluster, clust = as.factor(clust)) 
fviz_pca_biplot(res5.pca, pointsize = 1, labelsize = 2, axes = c(1,3), select.ind = list(cos2 = 0.30),addEllipses = TRUE, ellipse.level=0.60,col.ind = df_clust_scaled_cluster$clust, color_palette=c("#00AFBB", "#E7B800", "#FC4E07","#FC4E07","#FC4E07")) +  theme(plot.title = element_text(color="#3876C2", size=20, face="bold",hjust = 0.5),axis.title.x = element_text(color="black", size=14, face="bold",hjust = 0.5),axis.title.y = element_text(color="black", size=14, face="bold",vjust = 0.5))

En faisant un ACP sur l’axe 1.2 et 1.3, on observe un groupe se distinguer des autres, le cluster 3 Islande, Luxembourg, Suisse, Finlande et Norvège.

si l’on souhaite réduire encore l’échantillon, je propose de prendre en compte la distance, ce qui réduira la sélection au Luxembourg et la Suisse, lesquels sont frontaliers, riches et qui plus est francophones.

trouver les informations concernant la production, l’importation et éventuellement la consommation

—————————

7 - TESTS STATISTIQUES

—————————

Affichage des histogrames de plusieurs variables pour voir si l’on peut y retrouver la loi normale

# multiplot : Pour afficher plusieurs graphiques 

multiplot <- function(..., plotlist=NULL, file, cols=1, layout=NULL) {
  library(grid)

  # Make a list from the ... arguments and plotlist
  plots <- c(list(...), plotlist)

  numPlots = length(plots)

  # If layout is NULL, then use 'cols' to determine layout
  if (is.null(layout)) {
    # Make the panel
    # ncol: Number of columns of plots
    # nrow: Number of rows needed, calculated from # of cols
    layout <- matrix(seq(1, cols * ceiling(numPlots/cols)),
                    ncol = cols, nrow = ceiling(numPlots/cols))
  }

 if (numPlots==1) {
    print(plots[[1]])

  } else {
    # Set up the page
    grid.newpage()
    pushViewport(viewport(layout = grid.layout(nrow(layout), ncol(layout))))

    # Make each plot, in the correct location
    for (i in 1:numPlots) {
      # Get the i,j matrix positions of the regions that contain this subplot
      matchidx <- as.data.frame(which(layout == i, arr.ind = TRUE))

      print(plots[[i]], vp = viewport(layout.pos.row = matchidx$row,
                                      layout.pos.col = matchidx$col))
    }
  }
}
p1 <- ggplot(df_full_individus, aes(x=kcal_pers_jour)) + geom_histogram(aes(y=..density..), colour="black", fill="white",bins=30)+
 geom_density(alpha=.2, fill="#FF6666") +ggsave("img/figure12_histo.png", width = 15, height = 8)
p2 <- ggplot(df_full_individus, aes(x=prot_rapport)) + geom_histogram(aes(y=..density..), colour="black", fill="white",bins=30)+
 geom_density(alpha=.3, fill="#FF6666") 
p3 <- ggplot(df_full_individus, aes(x=Poulets1000))+ geom_histogram(aes(y=..density..), colour="black", fill="white",bins=30)+
 geom_density(alpha=.3, fill="#FF6666") 
p4 <- ggplot(df_full_individus, aes(x=pop_evo))+ geom_histogram(aes(y=..density..), colour="black", fill="white",bins=20)+
 geom_density(alpha=.3, fill="#FF6666") 
p5 <- ggplot(df_full_individus, aes(x=PIB_usd_hab)) + geom_histogram(aes(y=..density..), colour="black", fill="white",bins=6)+
 geom_density(alpha=.3, fill="#FF6666") 
p6 <- ggplot(df_full_individus, aes(x=distance)) + geom_histogram(aes(y=..density..), colour="black", fill="white",bins=50)+
 geom_density(alpha=.3, fill="#FF6666") 
p7 <- ggplot(df_full_individus, aes(x=securite)) + geom_histogram(aes(y=..density..), colour="black", fill="white",bins=8)+
 geom_density(alpha=.3, fill="#FF6666") 
multiplot(p1, p2, p3, p4,p5,p6,p7, cols=2)

La loi normale se représente par une courbe semblable à une cloche d’église avec un point culminant au milieu

Deux histogrammes s’apparentent à une loi normale, la sécurité et l’évolution de la population

Nous allons donc réaliser des test statistiques sur ces deux variables pour voir si elles respectent la loi normale

—————————

7.1 - TEST D’ADEQUATION

—————————

Pour la sécurité

df_secu_test <- transform(df_secu, securite = as.numeric(securite))
ks.test(df_secu$securite,"pnorm",mean=mean(df_secu$securite))
aucun ex-aequo ne devrait être présent pour le test de Kolmogorov-Smirnov

    One-sample Kolmogorov-Smirnov test

data:  df_secu$securite
D = 0.064163, p-value = 0.3888
alternative hypothesis: two-sided

La p-valeur est supérieure à 5%. On ne peut donc pas rejeter l’hypothèse de normalité au niveau du test à 5%. L’indice de la sécurité suit une loi normale.

Pour l’évolution de la population

df_pop_test <- transform(df_pop, Hab1000 = as.numeric(Hab1000))
ks.test(df_pop$Hab1000,"pnorm",mean=mean(df_pop$Hab1000))

    One-sample Kolmogorov-Smirnov test

data:  df_pop$Hab1000
D = 0.82759, p-value < 2.2e-16
alternative hypothesis: two-sided

Quand à la population, avec une p-valeur de 2.2e-16 on peut dire que l’indice de la population ne suit pas une loi normale.

loi normale sur cluster 4 et 5 pour sécurité

clust4_secu <- filter(df_full_centre_reduit_cluster, clust == '4')$securite
clust5_secu <- filter(df_full_centre_reduit_cluster, clust == '5')$securite

loi normale sur la sécurité du cluster 4

ks.test(clust4_secu,"pnorm",mean=mean(clust4_secu))
aucun ex-aequo ne devrait être présent pour le test de Kolmogorov-Smirnov

    One-sample Kolmogorov-Smirnov test

data:  clust4_secu
D = 0.12197, p-value = 0.8166
alternative hypothesis: two-sided

La p-valeur est supérieure à 5%. On ne peut donc pas rejeter l’hypothèse de normalité au niveau du test à 5%. L’indice de la sécurité suit une loi normale.

loi normale sur la sécurité du cluster 5

ks.test(clust5_secu,"pnorm",mean=mean(clust5_secu))

    One-sample Kolmogorov-Smirnov test

data:  clust5_secu
D = 0.3666, p-value = 0.1366
alternative hypothesis: two-sided

Pour le cluster 5. La p-valeur est inférieure à 5%. On peut donc rejeter l’hypothèse de normalité au niveau du test à 5%. L’indice de la sécurité ne suit pas une loi normale.

—————————

7.2 - TEST DE COMPARAISON

—————————

D’après le graphe on peut voir que les cluster 4 et 5 sont proches, nous allons faire une comparaison des variances sur la variable de sécurité pour ces deux clusters

var.test(clust4_secu,clust5_secu)

    F test to compare two variances

data:  clust4_secu and clust5_secu
F = 10.747, num df = 26, denom df = 8, p-value = 0.00167
alternative hypothesis: true ratio of variances is not equal to 1
95 percent confidence interval:
  2.736687 29.330586
sample estimates:
ratio of variances 
          10.74663 

La p-valeur valant 0.2833, on ne rejette donc pas l’égalité des variances au niveau de test 5%.

TEST D’ÉGALITÉ DES MOYENNES

t.test(clust5_secu,clust4_secu,var.equal=TRUE,alternative="greater")

    Two Sample t-test

data:  clust5_secu and clust4_secu
t = 4.8187, df = 34, p-value = 1.474e-05
alternative hypothesis: true difference in means is greater than 0
95 percent confidence interval:
 0.8868746       Inf
sample estimates:
  mean of x   mean of y 
 1.34538169 -0.02095088 

La p-valeur étant proche de 0 on rejette l’hypothèse d’égalité des moyennes. Les moyennes μ4 et μ5 ne sont donc pas égales.

En conclusion :

—————–

La variable sécurité a été soumise à des tests statistiques dans le but de savoir si elle suivait les mêmes influences sur deux clusters différents mais proches.

L’aspect gaussien des variables sécurité a été vérifié par un test d’adéquation via une loi normale par le test de Kolmogorov-Smirnov. Pour les clusters 4 et 5, l’hypothèse d’égalité des variances a été vérifiée.

contrairement à l’hypothèse d’égalité des moyennes de la variable sécurité pour les clusters 4 et 5 qui ne suivent pas la même distribution sur les deux clusters et sont donc distincts.

LS0tCnRpdGxlOiAiUiBOb3RlYm9vayAtIFBST0pFVCA1IC0gUEVZUk9OTkUiCm91dHB1dDoKICBodG1sX25vdGVib29rOiBkZWZhdWx0CiAgcGRmX2RvY3VtZW50OiAKICAgIHRvYzogeWVzCiAgICBsYXRleF9lbmdpbmU6IGx1YWxhdGV4CiAgaHRtbF9kb2N1bWVudDoKICAgIGRmX3ByaW50OiBwYWdlZAotLS0KCiMjIyAtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tCiMjIyBSYXBwZWwgZHUgcHJvamV0Ckwnb2JqZWN0aWYgZGUgY2UgcHJvamV0IGVzdCBkZSByw6lhbGlzZXIgdW5lIMOpdHVkZSBkZSBtYXJjaMOpIMOgIGwnYWlkZSBkJ2luZm9ybWF0aW9ucyBxdWUgbCdvbiBwZXV0IGNob2lzaXIgbGlicmVtZW50IHBvdXIgdHJvdXZlciBkZXMgcGF5cyBkYW5zIGxlc3F1ZWxzIGlsIHNlcmFpdCBwb3NzaWJsZSBkJ2V4cG9ydGVyIGRlcyBwb3VsZXRzIGRlcHVpcyBsYSBGcmFuY2UuCkFpbnNpLCBpZMOpYWxlbWVudCwgbm91cyByZWNoZXJjaG9ucyBkZXMgcGF5cyByaWNoZXMgZXQgc8O7cnMsIHZvaXNpbnMgZGUgbGEgRnJhbmNlIGRhbnMgbGVzcXVlc2wgbGEgY29uc29tbWF0aW9uIGRlIHByb3TDqWluZXMgYW5pbWFsZSBlc3Qgw6lsZXbDqWUgbWFpcyBsYSBwcm9kdWN0aW9uIHkgZXN0IGZhaWJsZS4KCiMjIyAtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tCgojIC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQojIDEgSW1wb3J0YXRpb24gZGVzIGRvbm7DqWVzCiMgLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tCgojIyMgMS4xIFNvdXJjZXMgZGVzIGRvbm7DqWVzCkxlcyBkb25uw6llcyBzb250IGV4dHJhaXRlcyBkdSBzaXRlIGRlIGxhIEZBTyBxdWkgZm91cm5pdCBkZSBub21icmV1c2VzIGluZm9ybWF0aW9ucyBhdSBuaXZlYXUgbW9uZGlhbCBzdXIgZGVzIHRow6htZXMgdGVscyBxdWUgbGEgcG9wdWxhdGlvbiwgbCdhZ3JpY3VsdHVyZSwgbGEgc8OpY3VyaXTDqSBldGMuCgpJbCB5IGEgZGVzIHZhcmlhYmxlcyBpbXBvc8OpZXMgOgoKLSBkaWZmw6lyZW5jZSBkZSBwb3B1bGF0aW9uIGVudHJlIHVuZSBhbm7DqWUgYW50w6lyaWV1cmUgKGF1IGNob2l4KSBldCBsJ2FubsOpZSBjb3VyYW50ZSwgZXhwcmltw6llIGVuIHBvdXJjZW50YWdlCi0gcHJvcG9ydGlvbiBkZSBwcm90w6lpbmVzIGQnb3JpZ2luZSBhbmltYWxlIHBhciByYXBwb3J0IMOgIGxhIHF1YW50aXTDqSB0b3RhbGUgZGUgcHJvdMOpaW5lcyBkYW5zIGxhIGRpc3BvbmliaWxpdMOpIGFsaW1lbnRhaXJlIGR1IHBheXMKLSBkaXNwb25pYmlsaXTDqSBhbGltZW50YWlyZSBlbiBwcm90w6lpbmVzIHBhciBoYWJpdGFudAotIGRpc3BvbmliaWxpdMOpIGFsaW1lbnRhaXJlIGVuIGNhbG9yaWVzIHBhciBoYWJpdGFudAoKRXQgZGVzIHZhcmlhYmxlcyBhdSBjaG9peCA6Ci0gUElCIHBhciBoYWJpdGFudCAoIHBvdXIgYXZvaXIgdW5lIGlkw6llIGRlIGxhIHBvc3NpYmlsaXTDqSBkZXMgdmVuZHJlIHF1ZWxxdWVjaG9zZSApCi0gRGlzdGFuY2UgZGVwdWlzIGxhIEZyYW5jZSAoIHBvdXIgw6l2YWx1ZXIgbGUgY2/Du3QgZGUgdHJhbnNwb3J0IGV0IGwnaW1wYWN0IMOpY29sb2dpcXVlICkKLSBOb20gZGVzIHBheXMgYXUgZm9ybWF0IElTTyB0cm9pcyBjYXJhY3TDqHJlcyAoIHBvdXIgY29tYmluZXIgbGUgdGFibGVhdSBkZXMgZGlzdGFuY2VzICkKLSBQcm9kdWN0aW9uIGRlIHBvdWxldHMgKCBwb3VyIHZvaXIgbGEgY29uY3VycmVuY2UgcG90ZW50aWVsbGUgKQotIEluZGljZSBkZSBzw6ljdXJpdMOpICggcG91ciDDqXZhbHVlciBsZSByaXNxdWUgZGUgcydpbXBsYW50ZXIgZGFucyBjZSBwYXlzICkKCiMjIyAxLjIgSW5zdGFsbGF0aW9uIGRlcyBwYWNrYWdlcyAKKi0+IEwnaW5zdGFsbGF0aW9uIGRlcyBwYWNrYWdlcyBuZSBzZSBmYWl0IHF1J3VuZSBzZXVsZSBmb2lzLCBqZSBsZXMgYWkgY29tbWVudMOpZXMgcG91ciDDqXZpdGVyIGRlIHLDqWlzbnRhbGxlciDDoCBjaGFxdWUgY2hhcmdlbWVudCBkdSBzY3JpcHQgKgpgYGB7ciBJbnN0YWxsYXRpb24gZGVzIHBhY2thZ2VzLCBlY2hvPVRSVUV9CiNpbnN0YWxsLnBhY2thZ2VzKCJGYWN0b01pbmVSIiwgZGVwZW5kZW5kY2llcz1UKQojaW5zdGFsbC5wYWNrYWdlcygiZmFjdG9leHRyYSIsZGVwZW5kZW5jaWVzPVQpCiNpbnN0YWxsLnBhY2thZ2VzKGMoIkZhY3Rvc2hpbnkiKSkKI2luc3RhbGwucGFja2FnZXMoImdncHViciIpCmBgYAojIyMgMS4zIEluc3RhbGxhdGlvbiBkZXMgbGlicmFyaWVzCiotPiBMJ2FjdGl2YXRpb24gZGVzIGxpYnJhaXJpZXMgc2UgZmFpdCDDoCBjaGFxdWUgZMOpbWFycmFnZSBkdSBzY3JpcHQqICAKKi0+IGNvcnJwbG90IGRvaXQgw6p0cmUgYXBwZWzDqSBhcHLDqHMgZ2dwbG90MiBwb3VyIMOpdml0ZXIgdW4gd2FybmluZyoKYGBge3IgSW5zdGFsbGF0aW9uIGRlcyBsaWJyYXJpZXMsIHJlc3VsdHM9RkFMU0V9CmxpYnJhcnkoRmFjdG9NaW5lUikKbGlicmFyeShmYWN0b2V4dHJhKQpsaWJyYXJ5KGdncHVicikKbGlicmFyeShkcGx5cikKbGlicmFyeShnZ3Bsb3QyKQpsaWJyYXJ5KGNvcnJwbG90KSAKb3B0aW9ucyhnZ3JlcGVsLm1heC5vdmVybGFwcyA9IEluZikKYGBgCiMjIyAxLjQgQ29uZmlndXJhdGlvbiBkZXMgdmFyaWFibGVzIHBvdXIgY2hhcXVlIGZpY2hpZXIKKi0+IGTDqWZpbml0aW9uIGQndW5lIHZhcmlhYmxlIHLDqXBlcnRvaXJlIHBvdXIgZmFjaWxpdGVyIGxlIGNoYW5nZW1lbnQgZCdPUyAqCmBgYHtyIEltcG9ydGF0aW9uIGRlcyBkb25uw6llc30KcmVwZXJ0b2lyZSA8LSAiL2hvbWUvdXNlci9Eb2N1bWVudHMvZm9ybWF0aW9ucy9vYy9Gb3JtYXRpb24gQW5hbHlzdC9QNS9zb3VyY2VzLyIKZmljX3BvcCA8LSBwYXN0ZSAocmVwZXJ0b2lyZSwicG9wdWxhdGlvbi5jc3YiLHNlcCA9ICIiKQpmaWNfcG9wMjAxNyA8LSBwYXN0ZSAocmVwZXJ0b2lyZSwicG9wXzIwMTcuY3N2IixzZXAgPSAiIikKZmljX2NhbCA8LSBwYXN0ZShyZXBlcnRvaXJlLCJrY2FsX3BlcnNvbm5lLmNzdiIsc2VwPSIiKQpmaWNfcHJvdF92b2xhaWxsZSA8LSBwYXN0ZShyZXBlcnRvaXJlLCJwcm90X3ZvbGFpbGxlLmNzdiIsc2VwPSIiKQpmaWNfcG91bGV0X3Byb2QgPC0gcGFzdGUocmVwZXJ0b2lyZSwicG91bGV0c19wcm9kdWN0aW9uXzIwMTkuY3N2IixzZXA9IiIpCmZpY19wcm90X2FuaSA8LSBwYXN0ZShyZXBlcnRvaXJlLCJwcm90X2FuaS5jc3YiLHNlcD0iIikKZmljX3Byb3RfdmVnIDwtIHBhc3RlKHJlcGVydG9pcmUsInByb3RfdmVnLmNzdiIsc2VwPSIiKQpmaWNfcGliIDwtIHBhc3RlIChyZXBlcnRvaXJlLCJwaWJfZ2RwLmNzdiIsc2VwID0gIiIpCmZpY19kaXMgPC0gcGFzdGUgKHJlcGVydG9pcmUsImRpc3RhbmNlcy5jc3YiLHNlcCA9ICIiKQpmaWNfaXNvIDwtIHBhc3RlIChyZXBlcnRvaXJlLCJzcWwtcGF5cy5jc3YiLHNlcCA9ICIiKQpmaWNfcHJvZCA8LSBwYXN0ZSAocmVwZXJ0b2lyZSwicHJvZHVjdGlvbl9wb3VsZXQuY3N2IixzZXAgPSAiIikKZmljX3NlY3UgPC0gcGFzdGUgKHJlcGVydG9pcmUsInNlY3VyaXR5LmNzdiIsc2VwID0gIiIpCmZpY19mdWxsIDwtIHBhc3RlIChyZXBlcnRvaXJlLCJmdWxsLmNzdiIsIHNlcCA9ICIiKQpmaWNfZXhwb3J0X3BheXNfY2x1c3RlciA8LSBwYXN0ZSAocmVwZXJ0b2lyZSwiLi4vUDVfcGF5c19jbHVzdGVyLmNzdiIsIHNlcCA9ICIiKQpmaWNfZXhwb3J0X2NlbnRyb2lkIDwtIHBhc3RlIChyZXBlcnRvaXJlLCIuLi9QNV9jZW50cm9pZC5jc3YiLCBzZXAgPSAiIikKYGBgCiMjIyAxLjUgQ3LDqWF0aW9uIGRlcyBEYXRhIEZyYW1lIHBhciBsZWN0dXJlIGRlcyBmaWNoaWVycwoqLT4gbGVzIGNyaXTDqHJlcyBkJ2ltcG9ydGF0aW9uLCBzw6lwYXJhdGV1cnMsIHZpcmd1bGVzIGTDqWNpbWFsZXMsIGVudMOqdGUgZXRjLCBvbnQgw6l0w6lzIGTDqWZpbmlzIGFwcsOocyBvYnNlcnZhdGlvbiBkZXMgZmljaGllcnMgc291cmNlcyDDoCBsJ2FpZGUgZCd1bmUgw6lkaXRldXIgZGUgdGV4dGUgb3UgZCd1biB0YWJsZXVyKgpgYGB7ciBDcsOpYXRpb24gZGVzIERGIHBhciBsZWN0dXJlIGRlcyBmaWNoaWVyc30KZGZfcG9wIDwtIHJlYWQuY3N2MihmaWNfcG9wLGhlYWRlciA9IFRSVUUsIHNlcCA9ICIsIiwgZGVjID0gIi4iKQpkZl9wb3AyMDE3IDwtIHJlYWQuY3N2MihmaWNfcG9wMjAxNyxoZWFkZXIgPSBUUlVFLCBzZXAgPSAiLCIsIGRlYyA9ICIuIikKZGZfY2FsIDwtIHJlYWQuY3N2MihmaWNfY2FsLGhlYWRlciA9IFRSVUUsIHNlcCA9ICIsIiwgZGVjID0gIi4iKQpkZl9wcm90X3ZvbGFpbGxlIDwtIHJlYWQuY3N2MihmaWNfcHJvdF92b2xhaWxsZSxoZWFkZXIgPSBUUlVFLCBzZXAgPSAiLCIsIGRlYyA9ICIuIikKZGZfcHJvdF9hbmkgPC0gcmVhZC5jc3YyKGZpY19wcm90X2FuaSxoZWFkZXIgPSBUUlVFLCBzZXAgPSAiLCIsIGRlYyA9ICIuIikKZGZfcHJvdF92ZWcgPC0gcmVhZC5jc3YyKGZpY19wcm90X3ZlZyxoZWFkZXIgPSBUUlVFLCBzZXAgPSAiLCIsIGRlYyA9ICIuIikKZGZfcGliIDwtIHJlYWQuY3N2MihmaWNfcGliLGhlYWRlciA9IFRSVUUsIHNlcCA9ICIsIiwgZGVjID0gIi4iKQpkZl9kaXMgPC0gcmVhZC5jc3YyKGZpY19kaXMsaGVhZGVyID0gVFJVRSwgc2VwID0gIiwiLCBkZWMgPSAiLiIpCmRmX2lzbyA8LSByZWFkLmNzdjIoZmljX2lzbyxoZWFkZXIgPSBGQUxTRSwgc2VwID0gIiwiLCBkZWMgPSAiLiIpCmRmX3Byb2QgPC0gcmVhZC5jc3YyKGZpY19wcm9kLGhlYWRlciA9IFRSVUUsIHNlcCA9ICIsIiwgZGVjID0gIi4iKQpkZl9zZWN1IDwtIHJlYWQuY3N2MihmaWNfc2VjdSxoZWFkZXIgPSBUUlVFLCBzZXAgPSAiLCIsIGRlYyA9ICIuIikKYGBgCiMjIyAtPiBWw6lyaWZpY2F0aW9uIGQndW4gREYgY3LDqcOpIHBhciBpbXBvcnRhdGlvbgpgYGB7ciBDb250cm9sZX0KaGVhZChkZl9wcm90X2FuaSkKYGBgCgojIyMgMS42IE5ldHRveWFnZSBkZXMgRGF0YUZyYW1lcyBlbiBzdXBwcmltYW50IGV0IHJlbm9tbWFudCBkZXMgY29sb25uZXMKKi0+IEplIHN1cHByaW1lIGxlcyBjb2xvbm5lcyBzdXBlcmZsdWVzIGV0IG5vcm1hbGlzZSBsZXMgbm9tcyBkZXMgdmFyaWFibGVzIG91IGxlcyByZW5kIHBsdXMgZXhwbGljaXRlcyoKCmBgYHtyIE5ldHRveWFnZSBkZXMgRGF0YUZyYW1lcyBlbiBzdXBwcmltYW50IGV0IHJlbm9tbWFudCBkZXMgY29sb25uZXN9CmRmX3BvcCA8LSBzdWJzZXQoZGZfcG9wLCBzZWxlY3QgPSBjKENvZGUuem9uZSwgQXJlYSwgVmFsdWUpKQpuYW1lcyhkZl9wb3ApW25hbWVzKGRmX3BvcCk9PSJBcmVhIl08LSJab25lIgpuYW1lcyhkZl9wb3ApW25hbWVzKGRmX3BvcCk9PSJWYWx1ZSJdPC0iSGFiMTAwMCIKZGZfcG9wMjAxNyA8LSBzdWJzZXQoZGZfcG9wMjAxNywgc2VsZWN0ID0gYyhDb2RlLnpvbmUsIFZhbGV1cikpCm5hbWVzKGRmX3BvcDIwMTcpW25hbWVzKGRmX3BvcDIwMTcpPT0iVmFsZXVyIl08LSJIYWIyMDE3eDEwMDAiCmRmX2NhbCA8LSBzdWJzZXQoZGZfY2FsLCBzZWxlY3QgPSBjKENvZGUuem9uZSwgVmFsZXVyKSkKbmFtZXMoZGZfY2FsKVtuYW1lcyhkZl9jYWwpPT0iVmFsZXVyIl08LSJrY2FsX3BlcnNfam91ciIKZGZfcHJvdF92ZWcgPC0gc3Vic2V0KGRmX3Byb3RfdmVnLCBzZWxlY3QgPSBjKENvZGUuem9uZSwgVmFsZXVyKSkKbmFtZXMoZGZfcHJvdF92ZWcpW25hbWVzKGRmX3Byb3RfdmVnKT09IlZhbGV1ciJdPC0icHJvdF92ZWciCmRmX3Byb3RfYW5pIDwtIHN1YnNldChkZl9wcm90X2FuaSwgc2VsZWN0ID0gYyhDb2RlLnpvbmUsIFZhbGV1cikpCm5hbWVzKGRmX3Byb3RfYW5pKVtuYW1lcyhkZl9wcm90X2FuaSk9PSJWYWxldXIiXTwtInByb3RfYW5pIgpkZl9wcm90X3ZvbGFpbGxlIDwtIHN1YnNldChkZl9wcm90X3ZvbGFpbGxlLCBzZWxlY3QgPSBjKENvZGUuem9uZSwgVmFsZXVyKSkKbmFtZXMoZGZfcHJvdF92b2xhaWxsZSlbbmFtZXMoZGZfcHJvdF92b2xhaWxsZSk9PSJWYWxldXIiXTwtInByb3Rfdm9sYWlsbGUiCmRmX3BpYiA8LSBzdWJzZXQoZGZfcGliLCBzZWxlY3QgPSBjKENvZGUuem9uZSwgVmFsZXVyKSkKbmFtZXMoZGZfcGliKVtuYW1lcyhkZl9waWIpPT0iVmFsZXVyIl08LSJQSUJfdXNkX2hhYiIKZGZfZGlzIDwtIHN1YnNldChkZl9kaXMsIHNlbGVjdCA9IGMoWCwgRlJBKSkKbmFtZXMoZGZfZGlzKVtuYW1lcyhkZl9kaXMpPT0iWCJdPC0iSVNPIgpuYW1lcyhkZl9kaXMpW25hbWVzKGRmX2Rpcyk9PSJGUkEiXTwtImRpc3RhbmNlIgpkZl9pc28gPC0gc3Vic2V0KGRmX2lzbywgc2VsZWN0ID0gYyhWNiwgVjQpKQpuYW1lcyhkZl9pc28pW25hbWVzKGRmX2lzbyk9PSJWNiJdPC0iWm9uZSIKbmFtZXMoZGZfaXNvKVtuYW1lcyhkZl9pc28pPT0iVjQiXTwtIklTTyIKZGZfcHJvZCA8LSBzdWJzZXQoZGZfcHJvZCwgc2VsZWN0ID0gYyhDb2RlLnpvbmUsIFZhbGV1cikpCm5hbWVzKGRmX3Byb2QpW25hbWVzKGRmX3Byb2QpPT0iVmFsZXVyIl08LSJQb3VsZXRzMTAwMCIKZGZfc2VjdSA8LSBzdWJzZXQoZGZfc2VjdSwgc2VsZWN0ID0gYyhBcmVhLkNvZGUsIFZhbHVlKSkKbmFtZXMoZGZfc2VjdSlbbmFtZXMoZGZfc2VjdSk9PSJWYWx1ZSJdPC0ic2VjdXJpdGUiCm5hbWVzKGRmX3NlY3UpW25hbWVzKGRmX3NlY3UpPT0iQXJlYS5Db2RlIl08LSJDb2RlLnpvbmUiCmBgYAoKIyMjIC0+IFbDqXJpZmljYXRpb24gZCd1bmUgbW9kaWZpY2F0aW9uIGRlIERGCmBgYHtyIFbDqXJpZmljYXRpb24gZCB1bmUgbW9kaWZpY2F0aW9uIGRlIERGfQpoZWFkKGRmX3Byb3RfYW5pKQpgYGAKCiMjIyAtPiBTdXBwcmVzc2lvbiBkZXMgcHJvdmluY2VzIGRlIGxhIENoaW5lIHBvdXIgw6l2aXRlciBsZXMgZG91YmxvbnMgY2FyIG9uIGEgZMOpasOgIHZ1IHF1ZSBsYSBjaGluZSDDqXRhaXQgcmVwcsOpc2VudMOpZSBkZXV4IGZvaXMsIHVuZSBmb2lzIGxlIHBheXMgZXQgdW5lIGZvaXMgbGVzIHByb3ZpbmNlcyBtYWlzIGF1c3NpIGxhIEZyYW5jZSBjYXIgY2Ugbidlc3QgcGFzIHVuIHBheXMgZCdleHBvcnRhdGlvbgpgYGB7ciBzdXBwcmVzc2lvbiBwcm92aW5jZXMgY2hpbm9pc2VzIH0Kc3Vic2V0KGRmX3BvcCwgIFpvbmUgPT0gIkZyYW5jZSIpCmRmX3BvcCA8LSBkZl9wb3BbIWRmX3BvcCRDb2RlLnpvbmUgJWluJSBjKCI5NiIsIjEyOCIsIjQxIiwiMjE0IiwiNjgiKSxdCmBgYAoKIyMjIDEuNyBDb21waWxhdGlvbiBkZXMgZGYgcG91ciBmb3JtZXIgbGEgREYgZ2xvYmFsZSBub21tw6llIGRmX2Z1bGwKLT4gSidhdXJhaXMgw6l0w6kgdGVudMOpIGRlIGwnYXBwZWxlciBkYXRhLCBtYWlzIG4nYXlhbnQgcGFzIGVuY29yZSBub3ZpY2UsIGplIHByw6lmw6hyZSBsdWkgZG9ubmVyIHVuIG5vbSBwbHVzIHBlcnNvbm5lbCBlbiBjb21tZW7Dp2FudCBwYXIgZGYgKCBkYXRhIGZyYW1lICkgcHVpcyBmdWxsIHBvdXIgcGxlaW4gZXQgYXByw6hzIGoneSByYWpvdXRlcmFpcyBsZXMgZGlmZsOpcmVudGVzIGZvcm1lcyBvdSBjb21wbMOpbWVudHMgIAotPiBKZSBzdWlzIHBhcnRpIGRlcyBwYXlzIGRlIGxhIHBvcHVsYXRpb24gZXQgaidhaSBjb21wbMOpdMOpIGF2ZWMgbGVzIGF1dHJlcyBkb25uw6llcwpgYGB7ciBtZXJnZSBkZXMgREYgfQpkZl9mdWxsIDwtIG1lcmdlKGRmX3BvcCxkZl9wb3AyMDE3LGJ5PSJDb2RlLnpvbmUiKQpkZl9mdWxsIDwtIG1lcmdlKGRmX2Z1bGwsZGZfaXNvLGJ5PSJab25lIikKZGZfZnVsbCA8LSBtZXJnZShkZl9mdWxsLGRmX2NhbCxieT0iQ29kZS56b25lIikKZGZfZnVsbCA8LSBtZXJnZShkZl9mdWxsLGRmX3Byb3RfdmVnLGJ5PSJDb2RlLnpvbmUiKQpkZl9mdWxsIDwtIG1lcmdlKGRmX2Z1bGwsZGZfcHJvdF9hbmksYnk9IkNvZGUuem9uZSIpCmRmX2Z1bGwgPC0gbWVyZ2UoZGZfZnVsbCxkZl9wcm90X3ZvbGFpbGxlLGJ5PSJDb2RlLnpvbmUiKQpkZl9mdWxsIDwtIG1lcmdlKGRmX2Z1bGwsZGZfcGliLGJ5PSJDb2RlLnpvbmUiKQpkZl9mdWxsIDwtIG1lcmdlKGRmX2Z1bGwsZGZfcHJvZCxieT0iQ29kZS56b25lIikKZGZfZnVsbCA8LSBtZXJnZShkZl9mdWxsLGRmX2RpcyxieT0iSVNPIikKZGZfZnVsbCA8LSBtZXJnZShkZl9mdWxsLGRmX3NlY3UsYnk9IkNvZGUuem9uZSIpCmBgYAojIyMgLT4gVsOpcmlmaWNhdGlvbiBkZSBsYSBjb21waWxhdGlvbiBkZXMgREYKYGBge3IgUsOpc3VsdGF0IGRlIGxhIGNvbXBpbGF0aW9uIGRlcyBERn0KaGVhZChkZl9mdWxsKQpgYGAKCiMjIyAxLjggQ29sb25uZXMgY2FsY3Vsw6llcyAoIMOpdm9sdXRpb24gcG9wdWxhdGlvbiBldCByYXBwb3J0IHByb3TDqWluZXMgKQpgYGB7ciBDYWxjdWwgZGUgw6l2b2x1dGlvbiBwb3B1bGF0aW9uIGV0IHJlcHBvcnQgcHJvdMOpaW5lc30KZGZfZnVsbCRwb3BfZXZvIDwtMTAwLSgxMDAqZGZfZnVsbCRIYWIyMDE3eDEwMDAvZGZfZnVsbCRIYWIxMDAwKQpkZl9mdWxsJHByb3RfcmFwcG9ydCA8LWRmX2Z1bGwkcHJvdF9hbmkvKGRmX2Z1bGwkcHJvdF92ZWcrZGZfZnVsbCRwcm90X2FuaSkKYGBgCgojIyMgLT4gc3VwcHJlc3Npb24gZGVzIGNvbG9ubmVzIHF1aSBvbnQgc2VydmkgcG91ciBsZXMgY2FsY3VscwpgYGB7ciBTdXBwcmVzc2lvbiBkZXMgY29sb25uZXMgc3VwZXJmbHVlcyBheWFudCBzZXJ2aWVzIGF1eCBjYWxjdWxzfQpkZl9mdWxsIDwtIHN1YnNldChkZl9mdWxsLCBzZWxlY3QgPSAtYyhDb2RlLnpvbmUsIElTTywgSGFiMjAxN3gxMDAwLHByb3RfdmVnLHByb3RfYW5pLCBwcm90X3ZvbGFpbGxlKSkKYGBgCgojIyMgTm91cyB2w6lyaWZpb25zIGxlcyB2YXJpYWJsZXMgcXVpIGF1cmFpZW50IGRlcyBtYW5xdWVzLgpgYGB7cn0KcHJpbnQocGFzdGUwKCdJbCB5IGEgJywgZGltKHN1YnNldChkZl9mdWxsLCBpcy5uYShIYWIxMDAwKSkpWzFdLCIgZG9ubsOpZXMgbWFucXVhbnRlcyBwb3VyIGxlIG5vbWJyZSBkJ2hhYml0YW50cy4iKSkKcHJpbnQocGFzdGUwKCdJbCB5IGEgJywgZGltKHN1YnNldChkZl9mdWxsLCBpcy5uYShrY2FsX3BlcnNfam91cikpKVsxXSwiIGRvbm7DqWVzIG1hbnF1YW50ZXMgcG91ciBsZSBrY2FsX3BlcnNfam91ci4iKSkKcHJpbnQocGFzdGUwKCdJbCB5IGEgJywgZGltKHN1YnNldChkZl9mdWxsLCBpcy5uYShQSUJfdXNkX2hhYikpKVsxXSwiIGRvbm7DqWVzIG1hbnF1YW50ZXMgcG91ciBsZSBQSUJfdXNkX2hhYi4iKSkKcHJpbnQocGFzdGUwKCdJbCB5IGEgJywgZGltKHN1YnNldChkZl9mdWxsLCBpcy5uYShQb3VsZXRzMTAwMCkpKVsxXSwiIGRvbm7DqWVzIG1hbnF1YW50ZXMgcG91ciBsZSBQb3VsZXRzMTAwMC4iKSkKcHJpbnQocGFzdGUwKCdJbCB5IGEgJywgZGltKHN1YnNldChkZl9mdWxsLCBpcy5uYShkaXN0YW5jZSkpKVsxXSwiIGRvbm7DqWVzIG1hbnF1YW50ZXMgcG91ciBsYSBkaXN0YW5jZS4iKSkKcHJpbnQocGFzdGUwKCdJbCB5IGEgJywgZGltKHN1YnNldChkZl9mdWxsLCBpcy5uYShzZWN1cml0ZSkpKVsxXSwiIGRvbm7DqWVzIG1hbnF1YW50ZXMgcG91ciBsYSBzZWN1cml0ZS4iKSkKcHJpbnQocGFzdGUwKCdJbCB5IGEgJywgZGltKHN1YnNldChkZl9mdWxsLCBpcy5uYShwb3BfZXZvKSkpWzFdLCIgZG9ubsOpZXMgbWFucXVhbnRlcyBwb3VyIGxlIHBvcF9ldm8uIikpCnByaW50KHBhc3RlMCgnSWwgeSBhICcsIGRpbShzdWJzZXQoZGZfZnVsbCwgaXMubmEocHJvdF9yYXBwb3J0KSkpWzFdLCIgZG9ubsOpZXMgbWFucXVhbnRlcyBwb3VyIGxlIHByb3RfcmFwcG9ydC4iKSkKIyBvbiBhdXJhaXQgcHUgbmUgZ2FyZGVyIHF1ZSBsZXMgcGF5cyBheWFudCB0b3V0ZXMgbGVzIHZhcmlhYmxlcyBkw6lmaW5pZXMgYXZlYyBsYSBjb21tYW5kZSBzdWl2YW50ZQpkZl9mdWxsIDwtIGRmX2Z1bGxbY29tcGxldGUuY2FzZXMoZGZfZnVsbCksXQpgYGAKIyMjIFbDqXJpZmljYXRpb24KYGBge3J9CmhlYWQoZGZfZnVsbCkKYGBgCgojIyMgRWNyaXR1cmUgZCd1biBmaWNoaWVyIENTViBhdmVjIGNldHRlIERGIGNvbXBsw6p0ZSBwb3VyIHVuIGNvbnRyw7RsZSBleHTDqXJpZXVyIHNpIG7DqWNlc3NhaXJlCmBgYHtyIEV4cG9ydCBkZl9mdWxsIGVuIGZpY2hpZXIgQ1NWfQp3cml0ZS5jc3YyKGRmX2Z1bGwsIGZpbGUgPSBmaWNfZnVsbCkKYGBgCiMjIyBERiB0cmnDqSBwYXIgbm9tIGRlIHBheXMgZXQgbm9tIGR1IHBheXMgY29tbWUgaW5kZXggCmBgYHtyIE5vbSBkdSBwYXlzIGNvbW1lIGluZGV4fQpkZl9mdWxsX29yZGVyIDwtIGRmX2Z1bGxbb3JkZXIoZGZfZnVsbFssIlpvbmUiXSxkZWNyZWFzaW5nPUYpLF0KZGZfZnVsbF9pbmRpdmlkdXM8LSBkYXRhLmZyYW1lKGRmX2Z1bGxfb3JkZXJbLC0xXSwgcm93Lm5hbWVzPWRmX2Z1bGxfb3JkZXJbLDFdKQojIHbDqXJpZmljYXRpb24KaGVhZChkZl9mdWxsX2luZGl2aWR1cykgCmBgYAoKIyAtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0KIyAyIC0gQ0FIIENsYXNzaWZpY2F0aW9uIEFzc2VuZGFudGUgSGnDqXJhcmNoaXF1ZSAKIyAtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0KKipPYmpldGNpZiA6IElkZW50aWZpZXIgZGVzIGdyb3VwZXMgc2ltaWxhaXJlcyBkYW5zIHVuIGpldSBkZSBkb25uw6llcyoqCgojIyMgZGF0YSBmcmFtZSBjZW50csOpIHLDqWR1aXQgYXZlYyBzY2FsZSAtIHLDqWR1aXQgbGUgcG9pZHMgZGVzIHZhcmlhYmxlcyDDoCBmb3J0ZSB2YXJpYW5jZQpgYGB7cn0KZGZfZnVsbF9jZW50cmVfcmVkdWl0IDwtIHNjYWxlKGRmX2Z1bGxfaW5kaXZpZHVzKSAKYGBgCgojIyMgSENQQyBwYXIgcmFwcG9ydCDDoCBsJ0FDUCBzdXIgNSBjbHVzdGVycwotPiBqZSBmYWlzIGRhYm9yZCB1biBwcmVtaWVyIGNhbGN1bCBkZSBsJ0FDUCBwb3VyIHV0aWxpc2VyIGxlIHLDqXN1bHRhdCBhdmVjIGxhIGZvbmN0aW9uIEhDUEMgIAotPiBqJ2V4cGxvaXRlIGxlIHJlcy5wY2EgaXNzdSBkZSBsYSBmb25jdGlvbiBQQ0Egc3VyIGxhIGRhdGEgZnJhbWUgY2VudHLDqSByw6lkdWl0CgojIyMgQUNQIHN1ciBsYSB0YWJsZSBjZW50csOpIHLDqWR1aXQKYGBge3J9CnJlcy5wY2EgPC0gUENBKGRmX2Z1bGxfY2VudHJlX3JlZHVpdCwgc2NhbGUudW5pdCA9IEZBTFNFLCBncmFwaCA9IEZBTFNFKQpgYGAKCiMjIyBIQ1BDCmBgYHtyfQpyZXMuaGNwYyA8LSBIQ1BDKHJlcy5wY2EsbmIuY2x1c3QgPSA1LCBncmFwaCA9IEZBTFNFKQpgYGAKCiMjIyBkZW5kb2dyYW1lIGVuIDUgY2x1c3RlcnMgw6AgcGFydGlyIGRlIGwnSENQQwpgYGB7cn0KaW1nX2RlbmRvZ3JhbW1lIDwtIGZ2aXpfZGVuZChyZXMuaGNwYywKICAgICAgICAgIGxhYmVsc2l6ZSA9IDIsCiAgICAgICAgICBjZXggPSAwLjcsICAgICAgICAgICAgICAgICAgICAgIyBUYWlsbGUgZHUgdGV4dAogICAgICAgICAgcGFsZXR0ZSA9ICJqY28iLCAgICAgICAgICAgICAgICMgUGFsZXR0ZSBkZSBjb3VsZXVyID9nZ3B1YnI6OmdncGFyCiAgICAgICAgICByZWN0ID0gVFJVRSwgcmVjdF9maWxsID0gVFJVRSwgIyBSZWN0YW5nbGUgYXV0b3VyIGRlcyBncm91cGVzCiAgICAgICAgICByZWN0X2JvcmRlciA9ICJqY28iLCAgICAgICAgICAgIyBDb3VsZXVyIGR1IHJlY3RhbmdsZQogICAgICAgICAgbGFiZWxzX3RyYWNrX2hlaWdodCA9IDAuOCAgICAgICMgQXVnbWVudCBsJ2VzcGFjZSBwb3VyIGxlIHRleHRlCiAgICAgICAgICApICsgZ2dzYXZlKCJQNV9pbWdfZGVuZG9ncmFtbWUucG5nIiwgd2lkdGggPSAxMSwgaGVpZ2h0ID0gOCkKcGxvdChpbWdfZGVuZG9ncmFtbWUpCmBgYAoKIyMjIE9ic2VydmF0aW9uIGRlcyBjbHVzdGVycwpgYGB7cn0KZnZpel9jbHVzdGVyKHJlcy5oY3BjLAogICAgICAgICAgICAgbGFiZWxzaXplID0gMywKICAgICAgICAgICAgIHJlcGVsID0gVFJVRSwgICAgICAgICAgICAjIEV2aXRlIGxlIGNoZXZhdWNoZW1lbnQgZGVzIHRleHRlcwogICAgICAgICAgICAgc2hvdy5jbHVzdC5jZW50ID0gVFJVRSwgIyBNb250cmUgbGUgY2VudHJlIGRlcyBjbHVzdGVycwogICAgICAgICAgICAgcGFsZXR0ZSA9ICJqY28iLCAgICAgICAgICMgUGFsZXR0ZSBkZSBjb3VsZXVycywgdm9pciA/Z2dwdWJyOjpnZ3BhcgogICAgICAgICAgICAgZ2d0aGVtZSA9IHRoZW1lX21pbmltYWwoKSwKICAgICAgICAgICAgIG1haW4gPSAiRmFjdG9yIG1hcCIKKQpgYGAKCiMjIyByYWpvdXQgZGUgbGEgdmFyaWFibGUgY2x1c3RlciBhdSBkZiAKYGBge3J9CmRmX2Z1bGxfY2VudHJlX3JlZHVpdF9jbHVzdGVyIDwtcmVzLmhjcGMkZGF0YS5jbHVzdApwYXlzX2NsdXN0ZXIgPC0gc3Vic2V0KGRmX2Z1bGxfY2VudHJlX3JlZHVpdF9jbHVzdGVyLCBzZWxlY3Q9YyhjbHVzdCkpCndyaXRlLmNzdjIocGF5c19jbHVzdGVyLCBmaWxlID0gZmljX2V4cG9ydF9wYXlzX2NsdXN0ZXIpCmhlYWQocGF5c19jbHVzdGVyKQpgYGAKCiMjIyBDZW50cm9pZGVzIC0gZ3JvdWJ5IHN1ciBsZXMgY2x1c3RlciBldCBtb3llbm5lcyBkZSBjaGFxdWUgdmFyaWJsZXMKYGBge3J9CmRmX2Z1bGxfY2x1c3RlciA8LSBjYmluZChkZl9mdWxsX2luZGl2aWR1cyxwYXlzX2NsdXN0ZXIkY2x1c3QpCmNvbG5hbWVzKGRmX2Z1bGxfY2x1c3RlcilbY29sbmFtZXMoZGZfZnVsbF9jbHVzdGVyKT09InBheXNfY2x1c3RlciRjbHVzdCJdIDwtICJjbHVzdCIKZGZfZnVsbF9jZW50cm9pZGVzIDwtIGRmX2Z1bGxfY2x1c3RlciAlPiUKICBncm91cF9ieShjbHVzdCkgJT4lCiAgc3VtbWFyaXplKG1veV9oYWIgPSBtZWFuKEhhYjEwMDAsIG5hLnJtID0gVFJVRSksIG1veV9kaXMgPSBtZWFuKGRpc3RhbmNlLG5hLnJtPVRSVUUpLCBtb3lfUElCPW1lYW4oUElCX3VzZF9oYWIsbmEucm09VFJVRSksbW95X3BvdWxldHM9bWVhbihQb3VsZXRzMTAwMCxuYS5ybT1UUlVFKSwKICAgICAgICAgICAgbW95X3NlY3U9bWVhbihzZWN1cml0ZSxuYS5ybT1UUlVFKSxtb3lfa2NhbD1tZWFuKGtjYWxfcGVyc19qb3VyLG5hLnJtPVRSVUUpLCBtb3lfcG9wX2V2bz1tZWFuKHBvcF9ldm8sbmEucm09VFJVRSksbW95X3Byb3Q9bWVhbihwcm90X3JhcHBvcnQsbmEucm09VFJVRSkgKQp3cml0ZS5jc3YyKGRmX2Z1bGxfY2VudHJvaWRlcywgZmlsZSA9IGZpY19leHBvcnRfY2VudHJvaWQpCmhlYWQoZGZfZnVsbF9jZW50cm9pZGVzKQpgYGAKCmBgYHtyfQpkZl9jZW50cm9pZCA8LSBkZl9mdWxsX2NlbnRyb2lkZXNbLDI6OV0KZGZfY2VudHJvaWRfc2NhbGVkIDwtIHNjYWxlKGRmX2NlbnRyb2lkKQpNYXRyaWNlIDwtIGRhdGEubWF0cml4KGRmX2NlbnRyb2lkX3NjYWxlZCkKcHJpbnQoTWF0cmljZSkKYGBgCiMjIyBEZXNjcmlwdGlvbiBkZSBsYSByw6lwYXJ0aXRpb24gZGVzIGNsdXN0ZXJzIApgYGB7cn0KcmVzLmhjcGMkZGVzYy52YXIKYGBgCgojIyMgSGVhdG1hcCBkZXMgdmFyaWFibGVzIGRhbnMgY2hhcXVlIGNsdXN0ZXIgcG91ciB2b2lyIGxldXIgaW1wb3J0YW5jZQpgYGB7cn0KY29ycnBsb3QoTWF0cmljZSwgaXMuY29yciA9IEZBTFNFLCBtZXRob2QgPSAiY2lyY2xlIikKYGBgCkludGVycHLDqXRhdGlvbiA6Ci0+IHBsdXMgbGUgcG9pbnQgZXN0IGJsZXUsIHBsdXMgbGEgdmFyaWFibGUgY2FyYWN0w6lyaXNlIGxlIGNsdXN0ZXIgIAotPiBwbHVzIGxlIHBvaW50IGVzdCBncmFuZCwgcGx1cyBsYSB2YXJpYWJsZSBlc3QgZm9ydGUgIAotPiBwbHVzIGxlIHBvaW50IGVzdCByb3VnZSBtb2lucyBsYSB2YXJpYWJsZSBjYXJhY3TDqXJpc2UgbGUgY2x1c3RlciAgCi0+IHBsdXMgbGUgcG9pbnQgZXN0IHBldGl0IHBsdXMgbGEgdmFyaWFibGUgZXN0IGZhaWJsZSAgCgpDTFVTVEVSIDEgIAotIMOpdm9sdXRpb24gZGUgbGEgcG9wdWxhdGlvbiBlc3QgZm9ydGUgIAotIGtjYWwgZXQgcHJvdMOpaW5lcyBzb250IHRyw6hzIGZhaWJsZXMgIApDTFVTVEVSIDIKLSBub21icmUgZCdoYWJpdGFudHMgZXQgZGUgcG91bGV0cyDDqWxldsOpcyAgCi0gZGlzdGFuY2UgdW4gcGV1IGZhdm9yYWJsZSAgCi0gbGEgc8OpY3VyaXTDqSBlc3QgZW4gcmV2YW5jaGUgZMOpZmF2b3JhYmxlICAKQ0xVU1RFUiAzICAKLSBzZXVsIGxhIGRpc3RhbmNlIGVzdCBsJ2F0b3V0IGRlIGNldCBlbnNlbWJsZSAgCkNMVVNURVIgNCAgCi0gZGlzdGFuY2UgZXQgw6l2b2x1dGlvbiBkZSBsYSBwb3B1bGF0aW9uIHNvbnQgZGV1eCB2YXJpYWJsZXMgcGFydGljdWxpw6hyZW1lbnQgZm9ydGVzIGV0IG7DqWdhdGl2ZXMgcG91ciBjZXQgZW5zZW1ibGUgIApDTFVTVEVSIDUgIAotIGRlcyBwYXlzIHJpY2hlcyBldCBzw7tycyAgCi0gdW5lIGFsaW1lbnRhdGlvbiByaWNoZSBlbiBwcm90w6lpbmVzIGV0IGVuIGtjYWwgIAoKLT4gTGUgZ3JvdXBlIDUgcHLDqXNlbnRlIGxlcyBwbHVzIGludMOpcmVzc2FudHMgcGFyYW3DqHRyZXMgcG91ciBub3RyZSBwcm9qZXQgY2FyIG9uIHkgdHJvdXZlIGxlcyBtZWlsbGV1cnMgcsOpc3VsdGF0cyBlbiBtYXRpw6hyZSBkZSByaWNoZXNzZSBldCBkZSBzw6ljdXJpdMOpLCBjZSBxdWkgcGVybWV0IGQnZW52aXNhZ2VyIHVuIGNvbW1lcmNlIHBsdXMgc3RhYmxlIGV0IG1vaW5zIHJpc3F1w6kuIERlIHBsdXMgbCdhbGltZW50YXRpb24gZXN0IHBsdXTDtHQgcmljaGUuIAoKCiMgLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tCiMgMyBBQ1AgLSBBbmFseXNlIGRlIENvbXBvc2FudGVzIFByaW5jaXBhbGVzIAojIC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQpgYGB7cn0KcmVzLmRlc2MgPC0gZGltZGVzYyhyZXMucGNhLCBheGVzID0gYygxLDIpLCBwcm9iYSA9IDAuMDUpCnJlcy5kZXNjJERpbS4xCmBgYAojIyBFYm91bGlzIGRlcyB2YWxldXJzIHByb3ByZXMgOiBwb3VyY2VudGFnZSBkZSB2YXJpYW5jZSBleHBsaXF1w6llIHBhciBjaGFxdWUgZGltZW5zaW9uCmBgYHtyfQplaWcudmFsIDwtIGdldF9laWdlbnZhbHVlKHJlcy5wY2EpIAplaWcudmFsCmZ2aXpfZWlnKHJlcy5wY2EsIGFkZGxhYmVscyA9IFRSVUUsIHlsaW0gPSBjKDAsIDUwKSkKYGBgCi0+IE9uIG9ic2VydmUgdW4gY291ZGUgYXUgbml2ZWF1IGRlIGxhIHNlY29uZGUgZGltZW5zaW9uLiBBcHLDqHMgcXVvaSBsYSBkaW1pbnV0aW9uIGVzdCBwbHVzIHLDqWd1bGnDqHJlLiAgIApNYWlzIGlsIHNlcmEgcGV1dC3DqnRyZSBuw6ljZXNzYWlyZSBkJ29ic2VydmVyIGxlcyAzIHByZW1pw6hyZXMgY29tcG9zYW50ZXMgcHJpbmNpcGFsZXMgcG91ciB0b3RhbGlzZXIgNzAlIGRlcyBpbmZvcm1hdGlvbnMuCgojIyMgQ29tcGFyYWlzb24gZGUgbGEgcXVhbGl0w6kgZGUgbGEgcmVwcsOpc2VudGF0aW9uIGRlcyB2YXJpYWJsZXMgZW4gZm9uY3Rpb24gZGVzIGRpbWVuc2lvbnMKYGBge3J9CnZhciA8LSBnZXRfcGNhX3ZhcihyZXMucGNhKQpjb3JycGxvdCh2YXIkY29zMiwgaXMuY29ycj1GQUxTRSxhZGRDb2VmLmNvbCA9ICJibGFjayIsIHRpdGxlPSJRdWFsaXTDqSBkZSByZXByw6lzZW50YXRpb24gZGVzIHZhcmlhYmxlcyIsIG1hcj1jKDAsMCwxLDApKQpgYGAKLT4gZGFucyBsYSBwcmVtacOocmUgY29tcG9zYW50ZSwgY2Ugc29udCBrY2FsLCBwaWIsIHPDqWN1cml0w6kgZXQgcmFwcG9ydCBlbiBwcm90w6lpbmVzIHF1aSBzb250IGxlIG1pZXV4IHJlcHLDqXNlbnTDqWVzICAKLT4gZGFucyBsYSBzZWNvbmRlLCBjJ2VzdCBsZSBub21icmUgZGUgcG91bGV0cyBldCBkJ2hhYml0YW50cy4gIAotPiBkYW5zIGxhIHRyb2lzacOobWUsIGMnZXN0IGxhIGRpc3RhbmNlIHF1aSBlc3QgbGUgbWlldXggcmVwcsOpc2VudMOpZSAgCgoKIyMjIEdyYXBoaXF1ZSBkZXMgY29ycsOpbGF0aW9ucyBwb3VyIGxlcyBkaW1lbnNpb25zIDEgZXQgMgpgYGB7cn0KZnZpel9wY2FfdmFyIChyZXMucGNhLCBjb2wudmFyID0gImNvczIiLAogICAgICAgICAgICAgIGdyYWRpZW50LmNvbHMgPSBjKCIjMDBBRkJCIiwgIiNFN0I4MDAiLCAiI0ZDNEUwNyIpLAogICAgICAgICAgICAgIHJlcGVsID0gVFJVRSAjIMOJdml0ZSBsZSBjaGV2YXVjaGVtZW50IGRlIHRleHRlCikKCmBgYApgYGB7cn0KZnZpel9wY2FfdmFyKHJlcy5wY2EsIGNvbC52YXIgPSAiY29zMiIsZ3JhZGllbnQuY29scyA9IGMoIiMwMEFGQkIiLCAiI0U3QjgwMCIsICIjRkM0RTA3IikscmVwZWwgPSBUUlVFKSsgbGFicyh0aXRsZSA9ICJDZXJjbGUgZGVzIGNvcnLDqWxhdGlvbnMiKSArIHRoZW1lKHBsb3QudGl0bGUgPSBlbGVtZW50X3RleHQoY29sb3I9IiMzODc2QzIiLCBzaXplPTIwLCBmYWNlPSJib2xkIixoanVzdCA9IDAuNSksYXhpcy50aXRsZS54ID0gZWxlbWVudF90ZXh0KGNvbG9yPSJibGFjayIsIHNpemU9MTQsIGZhY2U9ImJvbGQiLGhqdXN0ID0gMC41KSxheGlzLnRpdGxlLnkgPSBlbGVtZW50X3RleHQoY29sb3I9ImJsYWNrIiwgc2l6ZT0xNCwgZmFjZT0iYm9sZCIsdmp1c3QgPSAwLjUpKQpgYGAKCgpMZSBncmFwaGlxdWUgZGUgY29ycsOpbGF0aW9uIGRlcyB2YXJpYWJsZXMgbW9udHJlIGxlcyByZWxhdGlvbnMgZW50cmUgdG91dGVzIGxlcyB2YXJpYWJsZXMgZXQgbGVzIGF4ZXMgZmFjdG9yaWVscy4gSWwgcGV1dCDDqnRyZSBpbnRlcnByw6l0w6kgY29tbWUgc3VpdDoKCiAgICBMZXMgdmFyaWFibGVzIHBvc2l0aXZlbWVudCBjb3Jyw6lsw6llcyBzb250IHJlZ3JvdXDDqWVzIDoKICAgICAgICBkaXNwX2FsaV9wcm90X2dfcGVyc19qb3VyLCBkaXNwX2FsaV9rY2FsX3BlcnNfam91cgogICAgICAgIHByb3BfYW5pX2Rpc3BfYWxpLCBwaWJfaGFiCiAgICBMZXMgdmFyaWFibGVzIG7DqWdhdGl2ZW1lbnQgY29ycsOpbMOpZXMgc29udCBwb3NpdGlvbm7DqWVzIHN1ciBsZXMgY8O0dMOpcyBvcHBvc8OpcyBkZSBsJ29yaWdpbmUgZHUgZ3JhcGhpcXVlIChxdWFkcmFudHMgb3Bwb3PDqXMpIDoKICAgICAgICBpbmRpY2VfcmlzcXVlX3BheXMgZXQgZXZvbHV0aW9uX3BvcF8yMDE1XzIwMTYgcGFyIHJhcHBvcnQgYXV4IGF1dHJlcwogICAgTGEgZGlzdGFuY2UgZW50cmUgbGVzIHZhcmlhYmxlcyBldCBsJ29yaWdpbmUgbWVzdXJlIGxhIHF1YWxpdMOpIGRlIHJlcHLDqXNlbnRhdGlvbiBkZXMgdmFyaWFibGVzLiBMZXMgdmFyaWFibGVzIHF1aSBzb250IGxvaW4gZGUgbCdvcmlnaW5lIHNvbnQgYmllbiByZXByw6lzZW50w6llcyBwYXIgbCdBQ1AgKGNvdWxldXIgcm91Z2UgLyBvcmFuZ2UpLiBUb3V0ZXMgbGVzIHZhcmlhYmxlcyAodW4gcGV1IG1vaW5zIHBvdXIgZXZvbHV0aW9uX3BvcF8yMDE1XzIwMTYgOiBjb3VsZXVyIGJsZXVlKSBzb250IGJpZW4gZXhwbGlxdcOpZXMgcGFyIGxlcyBkZXV4IHByZW1pw6hyZXMgY29tcG9zYW50ZXMgcHJpbmNpcGFsZXMgKERpbS4xICYgRGltLjIpIGNhciBlbGxlcyBzb250IHBvc2l0aW9ubsOpZXMgcHLDqHMgZHUgbGUgY2VyY2xlIGRlIGNvcnLDqWxhdGlvbi4KClNvaXQgbGUgcmVww6hyZSBvcnRob2dvbmFsIHBvcnTDqSBwYXIgbGVzIGF4ZXMgKGRpc3RhbmNlX2ZyX2ZtIGV0IHVuIGF4ZSBub3JtYWwgw6AgY2UgZGVybmllcikuCgogICAgTGEgZGltZW5zaW9uIDEgc3ltYm9saXNlIGxhIHF1YWxpdMOpIGRlIHZpZSBkYW5zIGxlIHBheXMgOiBsZXMgcGF5cyBsZXMgcGx1cyDDoCBkcm9pdGUgc29udCBsZXMgcGF5cyBvw7kgbGEgcXVhbGl0w6kgZGUgdmllIGVzdCBsYSBtZWlsbGV1ciAoZm9ydCBQSUIgcGFyIGhhYml0YW50LCBmb3J0ZSBkaXNwb25pYmlsaXTDqSBhbGltZW50YWlyZSBlbiBrY2FsIC8gcHJvdMOpaW5lcywgZm9ydGUgY29uc29tbWF0aW9uIGRlIHByb2R1aXRzIGQnb3JpZ2luZSBhbmltYWxlLCBib25uZSBzdGFiaWxpdMOpIHBvbGl0aXF1ZSkuCiAgICBMYSBkaW1lbnNpb24gMiBzeW1ib2xpc2UgbGEgZGlzdGFuY2UgZHUgcGF5cyDDoCBsYSBGcmFuY2UuIDogcGx1cyB1biBwYXlzIGVzdCBlbiBiYXMsIHBsdXMgaWwgZXN0IHByb2NoZSBkZSBsYSBGcmFuY2UuCiAgICBMZXMgcGF5cyBxdWkgbm91cyBpbnTDqXJlc3NlbnQgc29udCBkb25jIGxlcyBwYXlzIGxlcyBwbHVzIMOgIGRyb2l0ZSAocHJpb3JpdGFpcmVtZW50IGNhciBsYSBkaXN0YW5jZSByZXN0ZSBzZWNvbmRhaXJlIGF1IHZ1ZSBkZSBsYSBxdWFsaXTDqSBkZSB2aWUgZGFucyBsZSBwYXlzKSBldCBsZXMgcGx1cyBlbiBiYXMuCgpEZSBtYW5pw6hyZSBnw6luw6lyYWxlLCBsZSBjZXJjbGUgZGVzIGNvcnLDqWxhdGlvbnMgbidlc3QgcGFzIGZhaXQgcG91ciBpbnRlcnByw6l0ZXIgbGVzIGNvcnLDqWxhdGlvbnMgZW50cmUgbGVzIHZhcmlhYmxlcyBpbml0aWFsZXMuIElsIGVzdCBmYWl0IHBvdXIgaW50ZXJwcsOpdGVyIGxlcyBheGVzIGQnaW5lcnRpZSBGMSwgRjIsIGV0Yy4gUG91ciB2w6lyaWZpZXIgbGEgY29ycsOpbGF0aW9uIGVudHJlIGxlcyB2YXJpYWJsZXMsIGxlcyBjb2VmZmljaWVudHMgZGUgY29ycsOpbGF0aW9ucyBlbnRyZSBsZXMgdmFyaWFibGVzIHNvbnQgY2FsY3Vsw6lzIGV0IGFmZmljaMOpcyBkYW5zIGxhIG1hdHJpY2UgZGVzIGNvcnLDqWxhdGlvbnMuCgoKIyMgcXVhbGl0w6kgZGUgcmVwcsOpc2VudGF0aW9uICggY29zMiApIGJhcnBsb3QKIyMjIFZhcmlhYmxlcwpgYGB7cn0KZnZpel9jb3MyKHJlcy5wY2EsIGNob2ljZSA9ICJ2YXIiKQpgYGAKCiMgLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tCiMgNCBDbHVzdGVycwojIC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQoKIyA0LjEgQW5hbHlzZSBkZXMgZGlmZsOpcmVudHMgZ3JvdXBlcwoKIyMjIGNsdXN0ZXIgMSAtIMOpdm9sdXRpb24gZGUgbGEgcG9wdWxhdGlvbiBzaW5pZmljYXRpdmVtZW50IHN1cMOpcmlldXIgw6AgbGEgbW95ZW5uZQojIyMgY2x1c3RlciAyIC0gbm9tYnJlIGRlIHBvdWxldHMgIHNpbmlmaWNhdGl2ZW1lbnQgc3Vww6lyaWV1ciDDoCBsYSBtb3llbm5lCiMjIyBjbHVzdGVyIDMgLSBkaXN0YW5jZXMgIHNpbmlmaWNhdGl2ZW1lbnQgc3Vww6lyaWV1ciDDoCBsYSBtb3llbm5lCiMjIyBjbHVzdGVyIDQgLSDDqXZvbHV0aW9uIGRlcyBwcm90w6lpbmVzICBzaW5pZmljYXRpdmVtZW50IHN1cMOpcmlldXIgw6AgbGEgbW95ZW5uZQojIyMgY2x1c3RlciA1IC0gUElCIHNpbmlmaWNhdGl2ZW1lbnQgc3Vww6lyaWV1ciDDoCBsYSBtb3llbm5lCgojIyBSZXByw6lzZW50YXRpb24gdmlzdWVsbGUgZGVzIENsdXN0ZXJzCgpgYGB7cn0KZGZfZnVsbF9jZW50cmVfcmVkdWl0X2NsdXN0ZXJfdHJhbnMgPC0gdHJhbnNmb3JtKGRmX2Z1bGxfY2VudHJlX3JlZHVpdF9jbHVzdGVyLCBjbHVzdCA9IGFzLmZhY3RvcihjbHVzdCkpIApmdml6X3BjYV9iaXBsb3QocmVzLnBjYSwgcG9pbnRzaXplID0gMSwgbGFiZWxzaXplID0gMiwgYXhlcyA9IGMoMSwyKSwgc2VsZWN0LmluZCA9IGxpc3QoY29zMiA9IDAuNTApLGFkZEVsbGlwc2VzID0gVFJVRSwgZWxsaXBzZS5sZXZlbD0wLjYwLGNvbC5pbmQgPSBkZl9mdWxsX2NlbnRyZV9yZWR1aXRfY2x1c3RlciRjbHVzdCwgY29sb3JfcGFsZXR0ZT1jKCIjMDBBRkJCIiwgIiNFN0I4MDAiLCAiI0ZDNEUwNyIsIiNGQzRFMDciLCIjRkM0RTA3IikpICsgIHRoZW1lKHBsb3QudGl0bGUgPSBlbGVtZW50X3RleHQoY29sb3I9IiMzODc2QzIiLCBzaXplPTIwLCBmYWNlPSJib2xkIixoanVzdCA9IDAuNSksYXhpcy50aXRsZS54ID0gZWxlbWVudF90ZXh0KGNvbG9yPSJibGFjayIsIHNpemU9MTQsIGZhY2U9ImJvbGQiLGhqdXN0ID0gMC41KSxheGlzLnRpdGxlLnkgPSBlbGVtZW50X3RleHQoY29sb3I9ImJsYWNrIiwgc2l6ZT0xNCwgZmFjZT0iYm9sZCIsdmp1c3QgPSAwLjUpKSArIGdnc2F2ZSgiUDVfaW1nX2JpcGxvdF9wY2EucG5nIiwgd2lkdGggPSAxMSwgaGVpZ2h0ID0gOCkKCmBgYAotPiBDZXR0ZSByZXByw6lzZW50YXRpb24gY29uZmlybWUgbGUgY2hvaXggZHUgY2x1c3RlciA1IGNhciBjJ2VzdCBsZSBjbHVzdGVyIHF1aSBzJ2FwcHJvY2hlIGxlIHBsdXMgZGVzIGNvbnNvbW1hdGlvbnMgc291aGFpdMOpZXMsIG1haXMgYXVzc2kgcG91ciBsYSByaWNoZXNzZSBldCBsYSBzw6ljdXJpdMOpIAoKIyA0LjIgQ2hvaXggZHUgY2x1c3RlciA1CmBgYHtyfQpkZl9mdWxsX2luZGl2aWR1cwpkZl9mdWxsX2luZGl2aWR1cyRBcmVhIDwtIHJvd25hbWVzKGRmX2Z1bGxfaW5kaXZpZHVzKQpkZl9jbHVzdDUgPC0gc3Vic2V0KGRmX2Z1bGxfY2VudHJlX3JlZHVpdF9jbHVzdGVyLCBjbHVzdCA9PSA1KQojZGZfY2x1c3Q1X2Z1bGwgPC0gZGZfY2x1c3Q1WywwXQojZGZfY2x1c3Q1X2Z1bGwkQXJlYSA8LSByb3duYW1lcyhkZl9jbHVzdDVfZnVsbCkKI2RmX2NsdXN0NV9mdWxsIDwtIG1lcmdlKGRmX2NsdXN0NV9mdWxsLGRmX2Z1bGxfaW5kaXZpZHVzLCBieSA9ICdBcmVhJykKI2RmX2NsdXN0NSA8LSBzdWJzZXQoZGZfZnVsbF9jZW50cmVfcmVkdWl0X2NsdXN0ZXIsIGNsdXN0ID09IDUpCnByaW50KGRmX2NsdXN0NVssMF0pCmBgYAoKIyAtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0KIyA1IENsdXN0ZXIgbnVtw6lybyA1IC0gQXBwbGljYXRpb24gZGUgbm91dmVsbGVzIGFuYWx5c2VzCiMgLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tCgojIyMgNS4xIFN1cHByZXNzaW9uIGRlIHZhcmlhYmxlcwpjbHVzdCBjYXIgb24gZ2FyZGUgdG91cyBsZXMgcGF5cyAgClBJQl91c2JfaGFiIGNhciB0b3VzIGNlcyBwYXlzIHNvbnQgcmljaGVzICAKc2VjdXJpdGUgY2FyIHRvdXMgY2VzIHBheXMgc29udCBzw7tycyAgClBvdWxldHMxMDAwIGNhciBpbHMgbidvbnQgcGFzIHVuZSBncmFuZGUgcXVhbnRpdMOpIGRlIHZvbGFpbGxlcyAgCnBvcF9ldm8gY2FyIGwnw6l2b2x1dGlvbiBlc3Qgc2VtYmxhYmxlIGRhbnMgdG91cyBjZXMgcGF5cyAoIHBsdXTDtHQgZmFpYmxlICkgIAoKYGBge3J9CmRmX2NsdXN0NSA8LSBzdWJzZXQoZGZfY2x1c3Q1LCBzZWxlY3QgPSAtYyhjbHVzdCxQSUJfdXNkX2hhYixQb3VsZXRzMTAwMCwgcG9wX2V2bywgcHJvdF9yYXBwb3J0LHNlY3VyaXRlKSkKYGBgCgojIyMgNS4yIEFqb3V0IGQndW5lIG5vdXZlbGxlIHZhcmlhYmxlIHBvdXIgZXNzYXllciBkZSBkaXN0aW5ndWVyIGxlcyBpbmRpdmlkdXMgZGUgY2UgZ3JvdXBlCgpgYGB7cn0KIyBham91dCBkZSBsYSBwcm9kdWN0aW9uIGRlIHBvdWxldHMKZmljX3BvdWxldF9wcm9kIDwtIHBhc3RlKHJlcGVydG9pcmUsInBvdWxldHNfcHJvZHVjdGlvbl8yMDE5LmNzdiIsc2VwPSIiKQpkZl9wb3VsZXRfcHJvZCA8LSByZWFkLmNzdjIoZmljX3BvdWxldF9wcm9kLGhlYWRlciA9IFRSVUUsIHNlcCA9ICIsIiwgZGVjID0gIi4iKQpkZl9wb3VsZXRfcHJvZCA8LSBzdWJzZXQoZGZfcG91bGV0X3Byb2Qsc2VsZWN0ID0gYyhBcmVhLFZhbHVlKSkKbmFtZXMoZGZfcG9wKVtuYW1lcyhkZl9wb3ApPT0iWm9uZSJdPC0iQXJlYSIKZGZfcG91bGV0X3Byb2QgPC0gbWVyZ2UoZGZfcG91bGV0X3Byb2QsZGZfcG9wLGJ5PSJBcmVhIikKZGZfcG91bGV0X3Byb2QkcHJvZF9wb3VsZXRfaGFiIDwtZGZfcG91bGV0X3Byb2QkVmFsdWUvZGZfcG91bGV0X3Byb2QkSGFiMTAwMApkZl9wb3VsZXRfcHJvZCA8LSBzdWJzZXQoZGZfcG91bGV0X3Byb2Qsc2VsZWN0ID0gYyhBcmVhLHByb2RfcG91bGV0X2hhYikpCgpkZl9jbHVzdDUkQXJlYSA8LSByb3duYW1lcyhkZl9jbHVzdDUpCiNkZl9jbHVzdDVfcHJvZCA8LSBtZXJnZShkZl9jbHVzdDUsZGZfcG91bGV0X3Byb2QsYnk9IkFyZWEiKQojbmFtZXMoZGZfY2x1c3Q1X3Byb2QpW25hbWVzKGRmX2NsdXN0NV9wcm9kKT09IlZhbHVlIl08LSJQcm9kX1BvdWxldFgxMDAwIgoKI2RmX2NsdXN0NV9wcm9kPC0gZGF0YS5mcmFtZShkZl9jbHVzdDVfcHJvZFssLTFdLCByb3cubmFtZXM9ZGZfY2x1c3Q1X3Byb2RbLDFdKQojZGZfZnVsbF9pbmRpdmlkdXMKZGZfZnVsbF9pbmRpdmlkdXMkQXJlYSA8LSByb3duYW1lcyhkZl9mdWxsX2luZGl2aWR1cykKCmRmX2NsdXN0NV9mdWxsIDwtIGRmX2NsdXN0NVssMF0KZGZfY2x1c3Q1X2Z1bGwkQXJlYSA8LSByb3duYW1lcyhkZl9jbHVzdDVfZnVsbCkKZGZfY2x1c3Q1X2Z1bGwgPC0gbWVyZ2UoZGZfY2x1c3Q1X2Z1bGwsZGZfZnVsbF9pbmRpdmlkdXMsIGJ5ID0gJ0FyZWEnKQpkZl9jbHVzdDVfZnVsbCA8LSBtZXJnZShkZl9jbHVzdDVfZnVsbCxkZl9wb3VsZXRfcHJvZCwgYnkgPSAnQXJlYScpCmRmX2NsdXN0NV9mdWxsIDwtIHN1YnNldChkZl9jbHVzdDVfZnVsbCwgc2VsZWN0ID0gLWMoUElCX3VzZF9oYWIsUG91bGV0czEwMDAsIHBvcF9ldm8sIHByb3RfcmFwcG9ydCxzZWN1cml0ZSkpCmRmX2NsdXN0NV9mdWxsPC0gZGF0YS5mcmFtZShkZl9jbHVzdDVfZnVsbFssLTFdLCByb3cubmFtZXM9ZGZfY2x1c3Q1X2Z1bGxbLDFdKQoKZGZfY2x1c3Q1IDwtIHN1YnNldChkZl9jbHVzdDUsIHNlbGVjdCA9IC1jKEFyZWEpKQoKI2RmX2NsdXN0NSA8LSBzdWJzZXQoZGZfZnVsbF9jZW50cmVfcmVkdWl0X2NsdXN0ZXIsIGNsdXN0ID09IDUpCiNwcmludChkZl9jbHVzdDVbLDBdKQpgYGAKIyMjIEFmZmljaGFnZSBkZXMgdGFibGVzIHBvdXIgY29udHLDtGxlCmBgYHtyfQpkZl9wb3AKZGZfY2x1c3Q1CmRmX3BvdWxldF9wcm9kCiNkZl9jbHVzdDVfcHJvZApkZl9jbHVzdDVfZnVsbApgYGAKCiMjIyA2LjMgQUNQIHN1ciBsYSB0YWJsZSBjZW50csOpIHLDqWR1aXQgZHUgY2x1c3RlciA1CmBgYHtyfQpyZXM1LnBjYSA8LSBQQ0EoZGZfY2x1c3Q1LCBzY2FsZS51bml0ID0gRkFMU0UsIGdyYXBoID0gRkFMU0UpCmBgYAojIyMgNi40IEhDUEMgcGFyIHJhcHBvcnQgw6AgbCdBQ1Agc2FucyBwcsOpY2lzZXIgbGUgbm9tYnJlIGRlIGNsdXN0ZXJzCmBgYHtyfQpyZXM1LmhjcGMgPC0gSENQQyhyZXM1LnBjYSwgZ3JhcGggPSBGQUxTRSkgCmBgYAojIyA2LjUgZGVuZG9ncmFtZSDDoCBwYXJ0aXIgZGUgbCdIQ1BDCmBgYHtyfQpmdml6X2RlbmQocmVzNS5oY3BjLCAKICAgICAgICAgIGNleCA9IDAuNywgICAgICAgICAgICAgICAgICAgICAjIFRhaWxsZSBkdSB0ZXh0CiAgICAgICAgICBwYWxldHRlID0gImpjbyIsICAgICAgICAgICAgICAgIyBQYWxldHRlIGRlIGNvdWxldXIgP2dncHVicjo6Z2dwYXIKICAgICAgICAgIHJlY3QgPSBUUlVFLCByZWN0X2ZpbGwgPSBUUlVFLCAjIFJlY3RhbmdsZSBhdXRvdXIgZGVzIGdyb3VwZXMKICAgICAgICAgIHJlY3RfYm9yZGVyID0gImpjbyIsICAgICAgICAgICAjIENvdWxldXIgZHUgcmVjdGFuZ2xlCiAgICAgICAgICBsYWJlbHNfdHJhY2tfaGVpZ2h0ID0gMC44ICAgICAgIyBBdWdtZW50IGwnZXNwYWNlIHBvdXIgbGUgdGV4dGUKKQoKYGBgCi0+IExlIGRlbmRhZ3JhbW1lIGEgY29uc3RpdHXDqSAzIGNsdXN0ZXJzIGNlIHF1aSBzZW1ibGUgdW4gbm9tYnJlIGNvcnJlY3QgZGUgZ3JvdXBlcyBjb21wdGUgdGVudSBkdSBub21icmUgZGUgcGF5cyBkYW5zIGNoYWN1biBkJ2V1eC4gIAoKSnVzdGlmaWVyIGxlIGNob2l4IGRlcyB2YXJpYWJsZXMgcGliIGV0IHPDqWN1cml0w6kgY29ycsOpbMOpcyBtYWlzIHRvdXMgY2VzIHBheXMgc29udCByaWNoZXMgZXQgc8OpY3VyaXPDqXMgCgojIyBPYnNlcnZhdGlvbiBkZXMgY2x1c3RlcnMKYGBge3J9CmZ2aXpfY2x1c3RlcihyZXM1LmhjcGMsCiAgICAgICAgICAgICBsYWJlbHNpemUgPSA4ICwKICAgICAgICAgICAgIHNob3cuY2x1c3QuY2VudCA9IFRSVUUsICMgTW9udHJlIGxlIGNlbnRyZSBkZXMgY2x1c3RlcnMKICAgICAgICAgICAgIHBhbGV0dGUgPSAiamNvIiwgICAgICAgICAjIFBhbGV0dGUgZGUgY291bGV1cnMsIHZvaXIgP2dncHVicjo6Z2dwYXIKICAgICAgICAgICAgIGdndGhlbWUgPSB0aGVtZV9taW5pbWFsKCksCiAgICAgICAgICAgICBtYWluID0gIkZhY3RvciBtYXAiCikKCmBgYApgYGB7cn0KZnZpel9wY2FfdmFyIChyZXM1LnBjYSwgY29sLnZhciA9ICJjb3MyIiwKICAgICAgICAgICAgICBncmFkaWVudC5jb2xzID0gYygiIzAwQUZCQiIsICIjRTdCODAwIiwgIiNGQzRFMDciKSwKICAgICAgICAgICAgICByZXBlbCA9IFRSVUUgIyDDiXZpdGUgbGUgY2hldmF1Y2hlbWVudCBkZSB0ZXh0ZQopCgpgYGAKCmBgYHtyfQpkZl9jbHVzdDVfc2NhbGVkIDwtIGFzLmRhdGEuZnJhbWUoZGZfY2x1c3Q1KQpyZXM1LnBjYSA8LSBQQ0EoZGZfY2x1c3Q1X3NjYWxlZCwgZ3JhcGggPSBGQUxTRSkKYGBgCiMjIMOpYm91bGlzIGRlIHZhbGV1cnMgcHJvcHJlcwpgYGB7cn0KZWlnNS52YWwgPC0gZ2V0X2VpZ2VudmFsdWUocmVzNS5wY2EpCmBgYAoKIyMgcGVydGUgZCdpbmVydGllIGVuIGZvbmN0aW9uIGRlcyBkaW1lbnNpb25zCmBgYHtyfQpmdml6X2VpZyhyZXM1LnBjYSwgYWRkbGFiZWxzID0gVFJVRSwgeWxpbSA9IGMoMCwgNTApKQoKYGBgCgpgYGB7cn0KdmFyNSA8LSBnZXRfcGNhX3ZhcihyZXM1LnBjYSkKY29ycnBsb3QodmFyNSRjb3MyLCBpcy5jb3JyPUZBTFNFLCB0aXRsZT0iUXVhbGl0w6kgZGVzIHZhcmlhYmxlcyIsIG1hcj1jKDAsMCwxLDApKQpgYGAKCmBgYHtyfQojIENvbG9yZXIgZW4gZm9uY3Rpb24gZHUgY29zMjogcXVhbGl0w6kgZGUgcmVwcsOpc2VudGF0aW9uCmZ2aXpfcGNhX3ZhcihyZXM1LnBjYSwgY29sLnZhciA9ICJjb3MyIixncmFkaWVudC5jb2xzID0gYygiIzAwQUZCQiIsICIjRTdCODAwIiwgIiNGQzRFMDciKSxyZXBlbCA9IFRSVUUpICsgbGFicyh0aXRsZSA9ICJGaWd1cmUgMTAgLSBDZXJjbGUgZGVzIGNvcnLDqWxhdGlvbnMiKSArIHRoZW1lKHBsb3QudGl0bGUgPSBlbGVtZW50X3RleHQoY29sb3I9IiMzODc2QzIiLCBzaXplPTIwLCBmYWNlPSJib2xkIixoanVzdCA9IDAuNSksYXhpcy50aXRsZS54ID0gZWxlbWVudF90ZXh0KGNvbG9yPSJibGFjayIsIHNpemU9MTQsIGZhY2U9ImJvbGQiLGhqdXN0ID0gMC41KSxheGlzLnRpdGxlLnkgPSBlbGVtZW50X3RleHQoY29sb3I9ImJsYWNrIiwgc2l6ZT0xNCwgZmFjZT0iYm9sZCIsdmp1c3QgPSAwLjUpKSAKCgpgYGAKCmBgYHtyfQpmdml6X3BjYV9iaXBsb3QocmVzNS5wY2EsIGNvbC52YXIgPSAiIzJFOUZERiIsIHBvaW50c2l6ZSA9IDEsIGxhYmVsc2l6ZSA9IDMsIGF4ZXMgPSBjKDEsMikpICsgIHRoZW1lKHBsb3QudGl0bGUgPSBlbGVtZW50X3RleHQoY29sb3I9IiMzODc2QzIiLCBzaXplPTIwLCBmYWNlPSJib2xkIixoanVzdCA9IDAuNSksYXhpcy50aXRsZS54ID0gZWxlbWVudF90ZXh0KGNvbG9yPSJibGFjayIsIHNpemU9MTQsIGZhY2U9ImJvbGQiLGhqdXN0ID0gMC41KSxheGlzLnRpdGxlLnkgPSBlbGVtZW50X3RleHQoY29sb3I9ImJsYWNrIiwgc2l6ZT0xNCwgZmFjZT0iYm9sZCIsdmp1c3QgPSAwLjUpKQpgYGAKYGBge3J9CmRmX2NsdXN0X3NjYWxlZF9jbHVzdGVyIDwtcmVzNS5oY3BjJGRhdGEuY2x1c3QKZGZfY2x1c3Rfc2NhbGVkX2NsdXN0ZXIKcGF5c19jbHVzdGVyIDwtIHN1YnNldChkZl9jbHVzdF9zY2FsZWRfY2x1c3Rlciwgc2VsZWN0PWMoY2x1c3QpKQojIHbDqXJpZmljYXRpb24KIyBzdW1tYXJ5KGRmX2Z1bGxfY2x1c3RlcikKcGF5c19jbHVzdGVyCmBgYAoKCmBgYHtyfQpkZl9jbHVzdF9zY2FsZWRfY2x1c3Rlcl90cmFucyA8LSB0cmFuc2Zvcm0oZGZfY2x1c3Rfc2NhbGVkX2NsdXN0ZXIsIGNsdXN0ID0gYXMuZmFjdG9yKGNsdXN0KSkgCmZ2aXpfcGNhX2JpcGxvdChyZXM1LnBjYSwgcG9pbnRzaXplID0gMSwgbGFiZWxzaXplID0gMiwgYXhlcyA9IGMoMSwyKSwgc2VsZWN0LmluZCA9IGxpc3QoY29zMiA9IDAuMzApLGFkZEVsbGlwc2VzID0gVFJVRSwgZWxsaXBzZS5sZXZlbD0wLjYwLGNvbC5pbmQgPSBkZl9jbHVzdF9zY2FsZWRfY2x1c3RlciRjbHVzdCwgY29sb3JfcGFsZXR0ZT1jKCIjMDBBRkJCIiwgIiNFN0I4MDAiLCAiI0ZDNEUwNyIsIiNGQzRFMDciLCIjRkM0RTA3IikpICsgIHRoZW1lKHBsb3QudGl0bGUgPSBlbGVtZW50X3RleHQoY29sb3I9IiMzODc2QzIiLCBzaXplPTIwLCBmYWNlPSJib2xkIixoanVzdCA9IDAuNSksYXhpcy50aXRsZS54ID0gZWxlbWVudF90ZXh0KGNvbG9yPSJibGFjayIsIHNpemU9MTQsIGZhY2U9ImJvbGQiLGhqdXN0ID0gMC41KSxheGlzLnRpdGxlLnkgPSBlbGVtZW50X3RleHQoY29sb3I9ImJsYWNrIiwgc2l6ZT0xNCwgZmFjZT0iYm9sZCIsdmp1c3QgPSAwLjUpKQoKYGBgCgpgYGB7cn0KZGZfY2x1c3Rfc2NhbGVkX2NsdXN0ZXJfdHJhbnMgPC0gdHJhbnNmb3JtKGRmX2NsdXN0X3NjYWxlZF9jbHVzdGVyLCBjbHVzdCA9IGFzLmZhY3RvcihjbHVzdCkpIApmdml6X3BjYV9iaXBsb3QocmVzNS5wY2EsIHBvaW50c2l6ZSA9IDEsIGxhYmVsc2l6ZSA9IDIsIGF4ZXMgPSBjKDEsMyksIHNlbGVjdC5pbmQgPSBsaXN0KGNvczIgPSAwLjMwKSxhZGRFbGxpcHNlcyA9IFRSVUUsIGVsbGlwc2UubGV2ZWw9MC42MCxjb2wuaW5kID0gZGZfY2x1c3Rfc2NhbGVkX2NsdXN0ZXIkY2x1c3QsIGNvbG9yX3BhbGV0dGU9YygiIzAwQUZCQiIsICIjRTdCODAwIiwgIiNGQzRFMDciLCIjRkM0RTA3IiwiI0ZDNEUwNyIpKSArICB0aGVtZShwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KGNvbG9yPSIjMzg3NkMyIiwgc2l6ZT0yMCwgZmFjZT0iYm9sZCIsaGp1c3QgPSAwLjUpLGF4aXMudGl0bGUueCA9IGVsZW1lbnRfdGV4dChjb2xvcj0iYmxhY2siLCBzaXplPTE0LCBmYWNlPSJib2xkIixoanVzdCA9IDAuNSksYXhpcy50aXRsZS55ID0gZWxlbWVudF90ZXh0KGNvbG9yPSJibGFjayIsIHNpemU9MTQsIGZhY2U9ImJvbGQiLHZqdXN0ID0gMC41KSkKCgpgYGAKIyMgRW4gZmFpc2FudCB1biBBQ1Agc3VyIGwnYXhlIDEuMiBldCAxLjMsIG9uIG9ic2VydmUgdW4gZ3JvdXBlIHNlIGRpc3Rpbmd1ZXIgZGVzIGF1dHJlcywgbGUgY2x1c3RlciAzIElzbGFuZGUsIEx1eGVtYm91cmcsIFN1aXNzZSwgRmlubGFuZGUgZXQgTm9ydsOoZ2UuICAKIyMgc2kgbCdvbiBzb3VoYWl0ZSByw6lkdWlyZSBlbmNvcmUgbCfDqWNoYW50aWxsb24sIGplIHByb3Bvc2UgZGUgcHJlbmRyZSBlbiBjb21wdGUgbGEgZGlzdGFuY2UsIGNlIHF1aSByw6lkdWlyYSBsYSBzw6lsZWN0aW9uIGF1IEx1eGVtYm91cmcgZXQgbGEgU3Vpc3NlLCBsZXNxdWVscyBzb250IGZyb250YWxpZXJzLCByaWNoZXMgZXQgcXVpIHBsdXMgZXN0IGZyYW5jb3Bob25lcy4gIAojIyB0cm91dmVyIGxlcyBpbmZvcm1hdGlvbnMgY29uY2VybmFudCBsYSBwcm9kdWN0aW9uLCBsJ2ltcG9ydGF0aW9uIGV0IMOpdmVudHVlbGxlbWVudCBsYSBjb25zb21tYXRpb24gIAoKIyAjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMKdGVzdCBhdmVjIGRmX2NsdXN0NV9mdWxsCiMgIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjCiMjIyBBQ1Agc3VyIGxhIHRhYmxlIGNlbnRyw6kgcsOpZHVpdCBkdSBjbHVzdGVyIDUKYGBge3J9CmRmX2NsdXN0NV9mdWxsCmRmX2NsdXN0NV9mdWxsX2NlbnRyZV9yZWR1aXQKZGZfY2x1c3Q1X2Z1bGxfY2VudHJlX3JlZHVpdCA8LSBzY2FsZShkZl9jbHVzdDVfZnVsbCkKcmVzNS5wY2EgPC0gUENBKGRmX2NsdXN0NV9mdWxsX2NlbnRyZV9yZWR1aXQsIHNjYWxlLnVuaXQgPSBGQUxTRSwgZ3JhcGggPSBGQUxTRSkKYGBgCgojIyMgSENQQyBwYXIgcmFwcG9ydCDDoCBsJ0FDUCBldCBqZSBsYWlzc2UgbGlicmUgbGUgbm9tYnJlIGRlIGNsdXN0ZXJzCmBgYHtyfQpyZXM1LmhjcGMgPC0gSENQQyhyZXM1LnBjYSwgZ3JhcGggPSBGQUxTRSkgIyBuYi5jbHVzdCA9IDQsCmBgYAoKIyMgZGVuZG9ncmFtZSDDoCBwYXJ0aXIgZGUgbCdIQ1BDCmBgYHtyfQpmdml6X2RlbmQocmVzNS5oY3BjLCAKICAgICAgICAgIGNleCA9IDAuNywgICAgICAgICAgICAgICAgICAgICAjIFRhaWxsZSBkdSB0ZXh0CiAgICAgICAgICBwYWxldHRlID0gImpjbyIsICAgICAgICAgICAgICAgIyBQYWxldHRlIGRlIGNvdWxldXIgP2dncHVicjo6Z2dwYXIKICAgICAgICAgIHJlY3QgPSBUUlVFLCByZWN0X2ZpbGwgPSBUUlVFLCAjIFJlY3RhbmdsZSBhdXRvdXIgZGVzIGdyb3VwZXMKICAgICAgICAgIHJlY3RfYm9yZGVyID0gImpjbyIsICAgICAgICAgICAjIENvdWxldXIgZHUgcmVjdGFuZ2xlCiAgICAgICAgICBsYWJlbHNfdHJhY2tfaGVpZ2h0ID0gMC44ICAgICAgIyBBdWdtZW50IGwnZXNwYWNlIHBvdXIgbGUgdGV4dGUKKQoKYGBgCgojIyBPYnNlcnZhdGlvbiBkZXMgY2x1c3RlcnMKYGBge3J9CmZ2aXpfY2x1c3RlcihyZXM1LmhjcGMsCiAgICAgICAgICAgICBsYWJlbHNpemUgPSA4ICwKICAgICAgICAgICAgIHNob3cuY2x1c3QuY2VudCA9IFRSVUUsICMgTW9udHJlIGxlIGNlbnRyZSBkZXMgY2x1c3RlcnMKICAgICAgICAgICAgIHBhbGV0dGUgPSAiamNvIiwgICAgICAgICAjIFBhbGV0dGUgZGUgY291bGV1cnMsIHZvaXIgP2dncHVicjo6Z2dwYXIKICAgICAgICAgICAgIGdndGhlbWUgPSB0aGVtZV9taW5pbWFsKCksCiAgICAgICAgICAgICBtYWluID0gIkZhY3RvciBtYXAiCikKCmBgYAoKYGBge3J9CmZ2aXpfcGNhX3ZhciAocmVzNS5wY2EsIGNvbC52YXIgPSAiY29zMiIsCiAgICAgICAgICAgICAgZ3JhZGllbnQuY29scyA9IGMoIiMwMEFGQkIiLCAiI0U3QjgwMCIsICIjRkM0RTA3IiksCiAgICAgICAgICAgICAgcmVwZWwgPSBUUlVFICMgw4l2aXRlIGxlIGNoZXZhdWNoZW1lbnQgZGUgdGV4dGUKKQoKYGBgCgoKYGBge3J9CgpyZXM1LnBjYSA8LSBQQ0EoZGZfY2x1c3Q1X2Z1bGxfY2VudHJlX3JlZHVpdCwgZ3JhcGggPSBGQUxTRSkKYGBgCiMjIMOpYm91bGlzIGRlIHZhbGV1cnMgcHJvcHJlcwpgYGB7cn0KZWlnNS52YWwgPC0gZ2V0X2VpZ2VudmFsdWUocmVzNS5wY2EpCmBgYAoKIyMgcGVydGUgZCdpbmVydGllIGVuIGZvbmN0aW9uIGRlcyBkaW1lbnNpb25zCmBgYHtyfQpmdml6X2VpZyhyZXM1LnBjYSwgYWRkbGFiZWxzID0gVFJVRSwgeWxpbSA9IGMoMCwgNTApKQoKYGBgCgpgYGB7cn0KdmFyNSA8LSBnZXRfcGNhX3ZhcihyZXM1LnBjYSkKY29ycnBsb3QodmFyNSRjb3MyLCBpcy5jb3JyPUZBTFNFLCB0aXRsZT0iUXVhbGl0w6kgZGVzIHZhcmlhYmxlcyIsIG1hcj1jKDAsMCwxLDApKQpgYGAKCmBgYHtyfQojIENvbG9yZXIgZW4gZm9uY3Rpb24gZHUgY29zMjogcXVhbGl0w6kgZGUgcmVwcsOpc2VudGF0aW9uCmZ2aXpfcGNhX3ZhcihyZXM1LnBjYSwgY29sLnZhciA9ICJjb3MyIixncmFkaWVudC5jb2xzID0gYygiIzAwQUZCQiIsICIjRTdCODAwIiwgIiNGQzRFMDciKSxyZXBlbCA9IFRSVUUpICsgbGFicyh0aXRsZSA9ICJGaWd1cmUgMTAgLSBDZXJjbGUgZGVzIGNvcnLDqWxhdGlvbnMiKSArIHRoZW1lKHBsb3QudGl0bGUgPSBlbGVtZW50X3RleHQoY29sb3I9IiMzODc2QzIiLCBzaXplPTIwLCBmYWNlPSJib2xkIixoanVzdCA9IDAuNSksYXhpcy50aXRsZS54ID0gZWxlbWVudF90ZXh0KGNvbG9yPSJibGFjayIsIHNpemU9MTQsIGZhY2U9ImJvbGQiLGhqdXN0ID0gMC41KSxheGlzLnRpdGxlLnkgPSBlbGVtZW50X3RleHQoY29sb3I9ImJsYWNrIiwgc2l6ZT0xNCwgZmFjZT0iYm9sZCIsdmp1c3QgPSAwLjUpKSAKCgpgYGAKCmBgYHtyfQpmdml6X3BjYV9iaXBsb3QocmVzNS5wY2EsIGNvbC52YXIgPSAiIzJFOUZERiIsIHBvaW50c2l6ZSA9IDEsIGxhYmVsc2l6ZSA9IDMsIGF4ZXMgPSBjKDEsMikpICsgIHRoZW1lKHBsb3QudGl0bGUgPSBlbGVtZW50X3RleHQoY29sb3I9IiMzODc2QzIiLCBzaXplPTIwLCBmYWNlPSJib2xkIixoanVzdCA9IDAuNSksYXhpcy50aXRsZS54ID0gZWxlbWVudF90ZXh0KGNvbG9yPSJibGFjayIsIHNpemU9MTQsIGZhY2U9ImJvbGQiLGhqdXN0ID0gMC41KSxheGlzLnRpdGxlLnkgPSBlbGVtZW50X3RleHQoY29sb3I9ImJsYWNrIiwgc2l6ZT0xNCwgZmFjZT0iYm9sZCIsdmp1c3QgPSAwLjUpKQpgYGAKCmBgYHtyfQpmdml6X3BjYV9iaXBsb3QocmVzNS5wY2EsIHBvaW50c2l6ZSA9IDEsIGxhYmVsc2l6ZSA9IDMsIGF4ZXMgPSBjKDEsMiksIHNlbGVjdC5pbmQgPSBsaXN0KGNvczIgPSAwLjMwKSxhZGRFbGxpcHNlcyA9IFRSVUUsIGVsbGlwc2UubGV2ZWw9MC42MCxjb2wuaW5kID0gZGZfY2x1c3Rfc2NhbGVkX2NsdXN0ZXIkY2x1c3QsIGNvbG9yX3BhbGV0dGU9YygiIzAwQUZCQiIsICIjRTdCODAwIiwgIiNGQzRFMDciLCIjRkM0RTA3IiwiI0ZDNEUwNyIpKSArICB0aGVtZShwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KGNvbG9yPSIjMzg3NkMyIiwgc2l6ZT0yMCwgZmFjZT0iYm9sZCIsaGp1c3QgPSAwLjUpLGF4aXMudGl0bGUueCA9IGVsZW1lbnRfdGV4dChjb2xvcj0iYmxhY2siLCBzaXplPTE0LCBmYWNlPSJib2xkIixoanVzdCA9IDAuNSksYXhpcy50aXRsZS55ID0gZWxlbWVudF90ZXh0KGNvbG9yPSJibGFjayIsIHNpemU9MTQsIGZhY2U9ImJvbGQiLHZqdXN0ID0gMC41KSkKCmBgYAoKYGBge3J9CmRmX2NsdXN0X3NjYWxlZF9jbHVzdGVyX3RyYW5zIDwtIHRyYW5zZm9ybShkZl9jbHVzdF9zY2FsZWRfY2x1c3RlciwgY2x1c3QgPSBhcy5mYWN0b3IoY2x1c3QpKSAKZnZpel9wY2FfYmlwbG90KHJlczUucGNhLCBwb2ludHNpemUgPSAxLCBsYWJlbHNpemUgPSAyLCBheGVzID0gYygxLDMpLCBzZWxlY3QuaW5kID0gbGlzdChjb3MyID0gMC4zMCksYWRkRWxsaXBzZXMgPSBUUlVFLCBlbGxpcHNlLmxldmVsPTAuNjAsY29sLmluZCA9IGRmX2NsdXN0X3NjYWxlZF9jbHVzdGVyJGNsdXN0LCBjb2xvcl9wYWxldHRlPWMoIiMwMEFGQkIiLCAiI0U3QjgwMCIsICIjRkM0RTA3IiwiI0ZDNEUwNyIsIiNGQzRFMDciKSkgKyAgdGhlbWUocGxvdC50aXRsZSA9IGVsZW1lbnRfdGV4dChjb2xvcj0iIzM4NzZDMiIsIHNpemU9MjAsIGZhY2U9ImJvbGQiLGhqdXN0ID0gMC41KSxheGlzLnRpdGxlLnggPSBlbGVtZW50X3RleHQoY29sb3I9ImJsYWNrIiwgc2l6ZT0xNCwgZmFjZT0iYm9sZCIsaGp1c3QgPSAwLjUpLGF4aXMudGl0bGUueSA9IGVsZW1lbnRfdGV4dChjb2xvcj0iYmxhY2siLCBzaXplPTE0LCBmYWNlPSJib2xkIix2anVzdCA9IDAuNSkpCgpgYGAKIyMgRW4gZmFpc2FudCB1biBBQ1Agc3VyIGwnYXhlIDEuMiBldCAxLjMsIG9uIG9ic2VydmUgdW4gZ3JvdXBlIHNlIGRpc3Rpbmd1ZXIgZGVzIGF1dHJlcywgbGUgY2x1c3RlciAzIElzbGFuZGUsIEx1eGVtYm91cmcsIFN1aXNzZSwgRmlubGFuZGUgZXQgTm9ydsOoZ2UuCiMjIHNpIGwnb24gc291aGFpdGUgcsOpZHVpcmUgZW5jb3JlIGwnw6ljaGFudGlsbG9uLCBqZSBwcm9wb3NlIGRlIHByZW5kcmUgZW4gY29tcHRlIGxhIGRpc3RhbmNlLCBjZSBxdWkgcsOpZHVpcmEgbGEgc8OpbGVjdGlvbiBhdSBMdXhlbWJvdXJnIGV0IGxhIFN1aXNzZSwgbGVzcXVlbHMgc29udCBmcm9udGFsaWVycywgcmljaGVzIGV0IHF1aSBwbHVzIGVzdCBmcmFuY29waG9uZXMuIAojIyB0cm91dmVyIGxlcyBpbmZvcm1hdGlvbnMgY29uY2VybmFudCBsYSBwcm9kdWN0aW9uLCBsJ2ltcG9ydGF0aW9uIGV0IMOpdmVudHVlbGxlbWVudCBsYSBjb25zb21tYXRpb24KCgoKIyAjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMKIyAtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0KIyA3IC0gVEVTVFMgU1RBVElTVElRVUVTCiMgLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tCiMjIyBBZmZpY2hhZ2UgZGVzIGhpc3RvZ3JhbWVzIGRlIHBsdXNpZXVycyB2YXJpYWJsZXMgcG91ciB2b2lyIHNpIGwnb24gcGV1dCB5IHJldHJvdXZlciBsYSBsb2kgbm9ybWFsZQoKYGBge3J9CiMgbXVsdGlwbG90IDogUG91ciBhZmZpY2hlciBwbHVzaWV1cnMgZ3JhcGhpcXVlcyAKCm11bHRpcGxvdCA8LSBmdW5jdGlvbiguLi4sIHBsb3RsaXN0PU5VTEwsIGZpbGUsIGNvbHM9MSwgbGF5b3V0PU5VTEwpIHsKICBsaWJyYXJ5KGdyaWQpCgogICMgTWFrZSBhIGxpc3QgZnJvbSB0aGUgLi4uIGFyZ3VtZW50cyBhbmQgcGxvdGxpc3QKICBwbG90cyA8LSBjKGxpc3QoLi4uKSwgcGxvdGxpc3QpCgogIG51bVBsb3RzID0gbGVuZ3RoKHBsb3RzKQoKICAjIElmIGxheW91dCBpcyBOVUxMLCB0aGVuIHVzZSAnY29scycgdG8gZGV0ZXJtaW5lIGxheW91dAogIGlmIChpcy5udWxsKGxheW91dCkpIHsKICAgICMgTWFrZSB0aGUgcGFuZWwKICAgICMgbmNvbDogTnVtYmVyIG9mIGNvbHVtbnMgb2YgcGxvdHMKICAgICMgbnJvdzogTnVtYmVyIG9mIHJvd3MgbmVlZGVkLCBjYWxjdWxhdGVkIGZyb20gIyBvZiBjb2xzCiAgICBsYXlvdXQgPC0gbWF0cml4KHNlcSgxLCBjb2xzICogY2VpbGluZyhudW1QbG90cy9jb2xzKSksCiAgICAgICAgICAgICAgICAgICAgbmNvbCA9IGNvbHMsIG5yb3cgPSBjZWlsaW5nKG51bVBsb3RzL2NvbHMpKQogIH0KCiBpZiAobnVtUGxvdHM9PTEpIHsKICAgIHByaW50KHBsb3RzW1sxXV0pCgogIH0gZWxzZSB7CiAgICAjIFNldCB1cCB0aGUgcGFnZQogICAgZ3JpZC5uZXdwYWdlKCkKICAgIHB1c2hWaWV3cG9ydCh2aWV3cG9ydChsYXlvdXQgPSBncmlkLmxheW91dChucm93KGxheW91dCksIG5jb2wobGF5b3V0KSkpKQoKICAgICMgTWFrZSBlYWNoIHBsb3QsIGluIHRoZSBjb3JyZWN0IGxvY2F0aW9uCiAgICBmb3IgKGkgaW4gMTpudW1QbG90cykgewogICAgICAjIEdldCB0aGUgaSxqIG1hdHJpeCBwb3NpdGlvbnMgb2YgdGhlIHJlZ2lvbnMgdGhhdCBjb250YWluIHRoaXMgc3VicGxvdAogICAgICBtYXRjaGlkeCA8LSBhcy5kYXRhLmZyYW1lKHdoaWNoKGxheW91dCA9PSBpLCBhcnIuaW5kID0gVFJVRSkpCgogICAgICBwcmludChwbG90c1tbaV1dLCB2cCA9IHZpZXdwb3J0KGxheW91dC5wb3Mucm93ID0gbWF0Y2hpZHgkcm93LAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGxheW91dC5wb3MuY29sID0gbWF0Y2hpZHgkY29sKSkKICAgIH0KICB9Cn0KYGBgCgpgYGB7cn0KcDEgPC0gZ2dwbG90KGRmX2Z1bGxfaW5kaXZpZHVzLCBhZXMoeD1rY2FsX3BlcnNfam91cikpICsgZ2VvbV9oaXN0b2dyYW0oYWVzKHk9Li5kZW5zaXR5Li4pLCBjb2xvdXI9ImJsYWNrIiwgZmlsbD0id2hpdGUiLGJpbnM9MzApKwogZ2VvbV9kZW5zaXR5KGFscGhhPS4yLCBmaWxsPSIjRkY2NjY2IikgK2dnc2F2ZSgiaW1nL2ZpZ3VyZTEyX2hpc3RvLnBuZyIsIHdpZHRoID0gMTUsIGhlaWdodCA9IDgpCnAyIDwtIGdncGxvdChkZl9mdWxsX2luZGl2aWR1cywgYWVzKHg9cHJvdF9yYXBwb3J0KSkgKyBnZW9tX2hpc3RvZ3JhbShhZXMoeT0uLmRlbnNpdHkuLiksIGNvbG91cj0iYmxhY2siLCBmaWxsPSJ3aGl0ZSIsYmlucz0zMCkrCiBnZW9tX2RlbnNpdHkoYWxwaGE9LjMsIGZpbGw9IiNGRjY2NjYiKSAKcDMgPC0gZ2dwbG90KGRmX2Z1bGxfaW5kaXZpZHVzLCBhZXMoeD1Qb3VsZXRzMTAwMCkpKyBnZW9tX2hpc3RvZ3JhbShhZXMoeT0uLmRlbnNpdHkuLiksIGNvbG91cj0iYmxhY2siLCBmaWxsPSJ3aGl0ZSIsYmlucz0zMCkrCiBnZW9tX2RlbnNpdHkoYWxwaGE9LjMsIGZpbGw9IiNGRjY2NjYiKSAKcDQgPC0gZ2dwbG90KGRmX2Z1bGxfaW5kaXZpZHVzLCBhZXMoeD1wb3BfZXZvKSkrIGdlb21faGlzdG9ncmFtKGFlcyh5PS4uZGVuc2l0eS4uKSwgY29sb3VyPSJibGFjayIsIGZpbGw9IndoaXRlIixiaW5zPTIwKSsKIGdlb21fZGVuc2l0eShhbHBoYT0uMywgZmlsbD0iI0ZGNjY2NiIpIApwNSA8LSBnZ3Bsb3QoZGZfZnVsbF9pbmRpdmlkdXMsIGFlcyh4PVBJQl91c2RfaGFiKSkgKyBnZW9tX2hpc3RvZ3JhbShhZXMoeT0uLmRlbnNpdHkuLiksIGNvbG91cj0iYmxhY2siLCBmaWxsPSJ3aGl0ZSIsYmlucz02KSsKIGdlb21fZGVuc2l0eShhbHBoYT0uMywgZmlsbD0iI0ZGNjY2NiIpIApwNiA8LSBnZ3Bsb3QoZGZfZnVsbF9pbmRpdmlkdXMsIGFlcyh4PWRpc3RhbmNlKSkgKyBnZW9tX2hpc3RvZ3JhbShhZXMoeT0uLmRlbnNpdHkuLiksIGNvbG91cj0iYmxhY2siLCBmaWxsPSJ3aGl0ZSIsYmlucz01MCkrCiBnZW9tX2RlbnNpdHkoYWxwaGE9LjMsIGZpbGw9IiNGRjY2NjYiKSAKcDcgPC0gZ2dwbG90KGRmX2Z1bGxfaW5kaXZpZHVzLCBhZXMoeD1zZWN1cml0ZSkpICsgZ2VvbV9oaXN0b2dyYW0oYWVzKHk9Li5kZW5zaXR5Li4pLCBjb2xvdXI9ImJsYWNrIiwgZmlsbD0id2hpdGUiLGJpbnM9OCkrCiBnZW9tX2RlbnNpdHkoYWxwaGE9LjMsIGZpbGw9IiNGRjY2NjYiKSAKbXVsdGlwbG90KHAxLCBwMiwgcDMsIHA0LHA1LHA2LHA3LCBjb2xzPTIpCmBgYAoKIyMjIExhIGxvaSBub3JtYWxlIHNlIHJlcHLDqXNlbnRlIHBhciB1bmUgY291cmJlIHNlbWJsYWJsZSDDoCB1bmUgY2xvY2hlIGQnw6lnbGlzZSBhdmVjIHVuIHBvaW50IGN1bG1pbmFudCBhdSBtaWxpZXUKIyMjIERldXggaGlzdG9ncmFtbWVzIHMnYXBwYXJlbnRlbnQgw6AgdW5lIGxvaSBub3JtYWxlLCBsYSBzw6ljdXJpdMOpIGV0IGwnw6l2b2x1dGlvbiBkZSBsYSBwb3B1bGF0aW9uCiMjIyBOb3VzIGFsbG9ucyBkb25jIHLDqWFsaXNlciBkZXMgdGVzdCBzdGF0aXN0aXF1ZXMgc3VyIGNlcyBkZXV4IHZhcmlhYmxlcyBwb3VyIHZvaXIgc2kgZWxsZXMgcmVzcGVjdGVudCBsYSBsb2kgbm9ybWFsZQojIC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQojIyA3LjEgLSBURVNUIEQnQURFUVVBVElPTgojIC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQoKIyMjIFBvdXIgbGEgc8OpY3VyaXTDqQpgYGB7cn0KZGZfc2VjdV90ZXN0IDwtIHRyYW5zZm9ybShkZl9zZWN1LCBzZWN1cml0ZSA9IGFzLm51bWVyaWMoc2VjdXJpdGUpKQprcy50ZXN0KGRmX3NlY3Ukc2VjdXJpdGUsInBub3JtIixtZWFuPW1lYW4oZGZfc2VjdSRzZWN1cml0ZSkpCmBgYApMYSBwLXZhbGV1ciBlc3Qgc3Vww6lyaWV1cmUgw6AgNSUuCk9uIG5lIHBldXQgZG9uYyBwYXMgcmVqZXRlciBs4oCZaHlwb3Row6hzZSBkZSBub3JtYWxpdMOpIGF1IG5pdmVhdSBkdSB0ZXN0IMOgIDUlLgpMJ2luZGljZSBkZSBsYSBzw6ljdXJpdMOpIHN1aXQgdW5lIGxvaSBub3JtYWxlLgoKIyMjIFBvdXIgbCfDqXZvbHV0aW9uIGRlIGxhIHBvcHVsYXRpb24KYGBge3J9CmRmX3BvcF90ZXN0IDwtIHRyYW5zZm9ybShkZl9wb3AsIEhhYjEwMDAgPSBhcy5udW1lcmljKEhhYjEwMDApKQprcy50ZXN0KGRmX3BvcCRIYWIxMDAwLCJwbm9ybSIsbWVhbj1tZWFuKGRmX3BvcCRIYWIxMDAwKSkKYGBgCgpRdWFuZCDDoCBsYSBwb3B1bGF0aW9uLCBhdmVjIHVuZSBwLXZhbGV1ciBkZSAyLjJlLTE2IG9uIHBldXQgZGlyZSBxdWUgbCdpbmRpY2UgZGUgbGEgcG9wdWxhdGlvbiBuZSBzdWl0IHBhcyB1bmUgbG9pIG5vcm1hbGUuCgoKIyMgbG9pIG5vcm1hbGUgc3VyIGNsdXN0ZXIgNCBldCA1IHBvdXIgc8OpY3VyaXTDqQpgYGB7cn0KY2x1c3Q0X3NlY3UgPC0gZmlsdGVyKGRmX2Z1bGxfY2VudHJlX3JlZHVpdF9jbHVzdGVyLCBjbHVzdCA9PSAnNCcpJHNlY3VyaXRlCmNsdXN0NV9zZWN1IDwtIGZpbHRlcihkZl9mdWxsX2NlbnRyZV9yZWR1aXRfY2x1c3RlciwgY2x1c3QgPT0gJzUnKSRzZWN1cml0ZQpgYGAKCiMjIyBsb2kgbm9ybWFsZSBzdXIgbGEgc8OpY3VyaXTDqSBkdSBjbHVzdGVyIDQKYGBge3J9CmtzLnRlc3QoY2x1c3Q0X3NlY3UsInBub3JtIixtZWFuPW1lYW4oY2x1c3Q0X3NlY3UpKQpgYGAKTGEgcC12YWxldXIgZXN0IHN1cMOpcmlldXJlIMOgIDUlLgpPbiBuZSBwZXV0IGRvbmMgcGFzIHJlamV0ZXIgbOKAmWh5cG90aMOoc2UgZGUgbm9ybWFsaXTDqSBhdSBuaXZlYXUgZHUgdGVzdCDDoCA1JS4KTCdpbmRpY2UgZGUgbGEgc8OpY3VyaXTDqSBzdWl0IHVuZSBsb2kgbm9ybWFsZS4KCiMjIyBsb2kgbm9ybWFsZSBzdXIgbGEgc8OpY3VyaXTDqSBkdSBjbHVzdGVyIDUKYGBge3J9CmtzLnRlc3QoY2x1c3Q1X3NlY3UsInBub3JtIixtZWFuPW1lYW4oY2x1c3Q1X3NlY3UpKQpgYGAKUG91ciBsZSBjbHVzdGVyIDUuCkxhIHAtdmFsZXVyIGVzdCBpbmbDqXJpZXVyZSDDoCA1JS4KT24gcGV1dCBkb25jIHJlamV0ZXIgbOKAmWh5cG90aMOoc2UgZGUgbm9ybWFsaXTDqSBhdSBuaXZlYXUgZHUgdGVzdCDDoCA1JS4KTCdpbmRpY2UgZGUgbGEgc8OpY3VyaXTDqSBuZSBzdWl0IHBhcyB1bmUgbG9pIG5vcm1hbGUuCgojIC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQojIyA3LjIgLSBURVNUIERFIENPTVBBUkFJU09OCiMgLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tCgpEJ2FwcsOocyBsZSBncmFwaGUgb24gcGV1dCB2b2lyIHF1ZSBsZXMgY2x1c3RlciA0IGV0IDUgc29udCBwcm9jaGVzLCBub3VzIGFsbG9ucyBmYWlyZSB1bmUgY29tcGFyYWlzb24gZGVzIHZhcmlhbmNlcyBzdXIgbGEgdmFyaWFibGUgZGUgc8OpY3VyaXTDqSBwb3VyIGNlcyBkZXV4IGNsdXN0ZXJzCgoKYGBge3J9CnZhci50ZXN0KGNsdXN0NF9zZWN1LGNsdXN0NV9zZWN1KQpgYGAKTGEgcC12YWxldXIgdmFsYW50IDAuMjgzMywgb24gbmUgcmVqZXR0ZSBkb25jIHBhcyBs4oCZw6lnYWxpdMOpIGRlcyB2YXJpYW5jZXMgYXUgbml2ZWF1IGRlIHRlc3QgNSUuCgpURVNUIEQnw4lHQUxJVMOJIERFUyBNT1lFTk5FUwoKYGBge3J9CnQudGVzdChjbHVzdDVfc2VjdSxjbHVzdDRfc2VjdSx2YXIuZXF1YWw9VFJVRSxhbHRlcm5hdGl2ZT0iZ3JlYXRlciIpCmBgYAoKTGEgcC12YWxldXIgw6l0YW50IHByb2NoZSBkZSAwIG9uIHJlamV0dGUgbCdoeXBvdGjDqHNlIGQnw6lnYWxpdMOpIGRlcyBtb3llbm5lcy4KTGVzIG1veWVubmVzIM68NCBldCDOvDUgbmUgc29udCBkb25jIHBhcyDDqWdhbGVzLgoKIyBFbiBjb25jbHVzaW9uIDoKIyAtLS0tLS0tLS0tLS0tLS0tLQojIyMgTGEgdmFyaWFibGUgc8OpY3VyaXTDqSBhIMOpdMOpIHNvdW1pc2Ugw6AgZGVzIHRlc3RzIHN0YXRpc3RpcXVlcyBkYW5zIGxlIGJ1dCBkZSBzYXZvaXIgc2kgZWxsZSBzdWl2YWl0IGxlcyBtw6ptZXMgaW5mbHVlbmNlcyBzdXIgZGV1eCBjbHVzdGVycyBkaWZmw6lyZW50cyBtYWlzIHByb2NoZXMuCiMjIyBMJ2FzcGVjdCBnYXVzc2llbiBkZXMgdmFyaWFibGVzIHPDqWN1cml0w6kgYSDDqXTDqSB2w6lyaWZpw6kgcGFyIHVuIHRlc3QgZCdhZMOpcXVhdGlvbiB2aWEgdW5lIGxvaSBub3JtYWxlIHBhciBsZSB0ZXN0IGRlIEtvbG1vZ29yb3YtU21pcm5vdi4gUG91ciBsZXMgY2x1c3RlcnMgNCBldCA1LCBsJ2h5cG90aMOoc2UgZCfDqWdhbGl0w6kgZGVzIHZhcmlhbmNlcyBhIMOpdMOpIHbDqXJpZmnDqWUuCiMjIyBjb250cmFpcmVtZW50IMOgIGwnaHlwb3Row6hzZSBkJ8OpZ2FsaXTDqSBkZXMgbW95ZW5uZXMgZGUgbGEgdmFyaWFibGUgc8OpY3VyaXTDqSBwb3VyIGxlcyBjbHVzdGVycyA0IGV0IDUgcXVpIG5lIHN1aXZlbnQgcGFzIGxhIG3Dqm1lIGRpc3RyaWJ1dGlvbiBzdXIgbGVzIGRldXggY2x1c3RlcnMgZXQgc29udCBkb25jIGRpc3RpbmN0cy4gCg==