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

Rappel du projet

Votre société de consulting informatique vous propose une nouvelle mission au ministère de l’Intérieur, dans le cadre de la lutte contre la criminalité organisée, à l’Office central pour la répression du faux monnayage. Votre mission si vous l’acceptez : créer un algorithme de détection de faux billets. ### —————————————————————————————-

Mission 0

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

Mission 1

Vous réaliserez une analyse en composantes principales de l’échantillon, en suivant toutes ces étapes :

Pour chacune de ces étapes, commentez les résultats obtenus. La variable donnant la nature Vrai/Faux du billet sera utilisée comme variable illustrative.

Mission 2

Appliquez un algorithme de classification, puis analysez le résultat obtenu. Visualisez la partition obtenue dans le premier plan factoriel de l’ACP, puis analysez-la.

Mission 3

Modélisez les données à l’aide d’une régression logistique. Grâce à celle-ci, vous créerez un programme capable d’effectuer une prédiction sur un billet, c’est-à-dire de déterminer s’il s’agit d’un vrai ou d’un faux billet. Pour chaque billet, votre algorithme de classification devra donner la probabilité que le billet soit vrai. Si cette probabilité est supérieure ou égale à 0.5, le billet sera considéré comme vrai. Dans le cas contraire, il sera considéré comme faux.

Mission 0

—————————

1 Importation des données

—————————

1.1 Sources des données

Jeu de données contenant les caractéristiques géométriques de billets de banque. Pour chacun d’eux, nous connaissons :

la longueur du billet (en mm) ;
la hauteur du billet (mesurée sur le côté gauche, en mm) ;
La hauteur du billet (mesurée sur le côté droit, en mm) ;
la marge entre le bord supérieur du billet et l'image de celui-ci (en mm) ;
la marge entre le bord inférieur du billet et l'image de celui-ci (en mm) ;
la diagonale du billet (en mm).

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(ggplot2)
library(FactoMineR)
library(factoextra)
library(ggpubr)
library(dplyr)
library(corrplot) 
options(ggrepel.max.overlaps = Inf)
theme_update(
      plot.title = element_text(color="#3876C2", size=18, 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),
      )

1.4 Configuration des variables pour chaque fichier

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

repertoire_sources <- "/home/user/Documents/formations/oc/Formation Analyst/P6/sources/"
repertoire_images <- "/home/user/Documents/formations/oc/Formation Analyst/P6/images/"
fic_notes <- paste (repertoire_sources,"notes.csv",sep = "")

1.5 Création des Data Frame par lecture du fichier

-> 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_notes <- read.csv2(fic_notes,header = TRUE, sep = ",", dec = ".")

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

head(df_notes)

DF complet

summary(df_notes)
  is_genuine           diagonal      height_left     height_right     margin_low      margin_up         length     
 Length:170         Min.   :171.0   Min.   :103.2   Min.   :103.1   Min.   :3.540   Min.   :2.270   Min.   :110.0  
 Class :character   1st Qu.:171.7   1st Qu.:103.8   1st Qu.:103.7   1st Qu.:4.050   1st Qu.:3.013   1st Qu.:111.9  
 Mode  :character   Median :171.9   Median :104.1   Median :104.0   Median :4.450   Median :3.170   Median :112.8  
                    Mean   :171.9   Mean   :104.1   Mean   :103.9   Mean   :4.612   Mean   :3.170   Mean   :112.6  
                    3rd Qu.:172.1   3rd Qu.:104.3   3rd Qu.:104.2   3rd Qu.:5.128   3rd Qu.:3.330   3rd Qu.:113.3  
                    Max.   :173.0   Max.   :104.9   Max.   :105.0   Max.   :6.280   Max.   :3.680   Max.   :114.0  

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

Mission 0

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

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

Observations :

- Diagonales : - dans les faux billets certaines sont supérieures au vrais

- hauteur gauche : - l’ampitude de la hauteur des faux billets est inférieure et comprise dans celle des vrais, je ne pense donc pas que ce soit un critère intéressant

- hauteur droite : - idem

- marge inférieure : - les faux billets ont une valeur supérieure aux vrais

- marge supérieure : - les faux billets ont une valeur supérieure aux vrais

- longueur : - les faux billets sont plus petits que les vrais

DF vrais billets

vrais_billets <- filter(df_notes, is_genuine == 'True')
summary(vrais_billets)
  is_genuine           diagonal      height_left     height_right     margin_low      margin_up         length     
 Length:100         Min.   :171.0   Min.   :103.2   Min.   :103.1   Min.   :3.540   Min.   :2.270   Min.   :111.8  
 Class :character   1st Qu.:171.8   1st Qu.:103.7   1st Qu.:103.6   1st Qu.:3.900   1st Qu.:2.938   1st Qu.:113.0  
 Mode  :character   Median :172.0   Median :103.9   Median :103.8   Median :4.080   Median :3.070   Median :113.2  
                    Mean   :172.0   Mean   :104.0   Mean   :103.8   Mean   :4.144   Mean   :3.055   Mean   :113.2  
                    3rd Qu.:172.2   3rd Qu.:104.1   3rd Qu.:104.0   3rd Qu.:4.383   3rd Qu.:3.192   3rd Qu.:113.5  
                    Max.   :172.8   Max.   :104.9   Max.   :105.0   Max.   :5.040   Max.   :3.530   Max.   :114.0  

DF Faux billets

faux_billets <- filter(df_notes, is_genuine == 'False')
summary(faux_billets)
  is_genuine           diagonal      height_left     height_right     margin_low      margin_up         length     
 Length:70          Min.   :171.4   Min.   :103.8   Min.   :103.4   Min.   :3.820   Min.   :2.980   Min.   :110.0  
 Class :character   1st Qu.:171.7   1st Qu.:104.1   1st Qu.:104.0   1st Qu.:4.952   1st Qu.:3.185   1st Qu.:111.3  
 Mode  :character   Median :171.9   Median :104.2   Median :104.2   Median :5.265   Median :3.335   Median :111.8  
                    Mean   :171.9   Mean   :104.2   Mean   :104.1   Mean   :5.282   Mean   :3.335   Mean   :111.7  
                    3rd Qu.:172.0   3rd Qu.:104.4   3rd Qu.:104.3   3rd Qu.:5.702   3rd Qu.:3.450   3rd Qu.:112.0  
                    Max.   :173.0   Max.   :104.7   Max.   :104.9   Max.   :6.280   Max.   :3.680   Max.   :113.6  

Nettoyage recherche de valeurs absentes

sapply(df_notes, function(x) sum(is.na(x)))
  is_genuine     diagonal  height_left height_right   margin_low    margin_up       length 
           0            0            0            0            0            0            0 

ANALYSE :

nous avons déterminé précédement que la variables disrciminatoires étaient margin_low et length.
Ce qui est confirmé par la p-value du test de student pour ces deux variables. Si l’on fait un test sur une autre variable, on retrouve une p-value supérieure à 5%.

Affichage des histogrames des variables pour voir si l’on peut y retrouver la loi normale et si certaines sont vraiment différentes

# 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))
    }
  }
}
data <- df_notes
p1 <- ggplot(data, aes(x=diagonal, fill=is_genuine, color=is_genuine)) +  geom_density(alpha=.3) 
p2 <- ggplot(data, aes(x=height_left, fill=is_genuine, color=is_genuine)) +  geom_density(alpha=.3) 
p3 <- ggplot(data, aes(x=height_right, fill=is_genuine, color=is_genuine))+  geom_density(alpha=.3) 
p4 <- ggplot(data, aes(x=margin_low, fill=is_genuine, color=is_genuine))+  geom_density(alpha=.3) 
p5 <- ggplot(data, aes(x=margin_up, fill=is_genuine, color=is_genuine)) +  geom_density(alpha=.3) 
p6 <- ggplot(data, aes(x=length, fill=is_genuine, color=is_genuine)) +  geom_density(alpha=.3) 
multiplot(p1, p2, p3, p4,p5,p6, cols=2)

remplacer true par 1, false par 0

df_notes_vf<-df_notes
df_notes_vf$is_genuine[df_notes$is_genuine == "True"] <- "1"
df_notes_vf$is_genuine[df_notes$is_genuine == "False"] <- "0"
df_notes_vf_numeric <- transform(df_notes_vf, is_genuine = as.numeric(is_genuine))
df_notes_vf_numeric

Heatmap des variables dans chaque cluster pour voir leur importance

# corrplot(Matrice,  method = "circle")
matrice_cor <- cor(df_notes_vf_numeric[,2:7])
corrplot(matrice_cor, type="upper", sig.level = 0.01)

Mission 1

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

ACP - Analyse en composantes principales

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

Table centré réduite

df_notes_vf_scaled <- data.frame(df_notes['is_genuine'], scale(df_notes[,2:7]))

ACP

res.pca <- PCA(df_notes_vf_scaled, scale.unit = FALSE,  quali.sup=1, graph = FALSE)

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

eig.val <- get_eigenvalue(res.pca) 
fviz_eig(res.pca, addlabels = TRUE, ylim = c(0, 75))

-> 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, margin_low et length sont le mieux représentées
-> dans la seconde, margin_low est encore un peu représentée.
-> dans la troisième, c’est la distance qui est le mieux représentée
## CONCLUSION : nous pouvons travailler sur le plan factoriel 1-3 et 1-2 nous verrons avec d’autres analyses

Graphique des corrélations pour les dimensions 1 et 2

img_graphe_correlation<-fviz_pca_var(res.pca, col.var = "cos2", axes = c(1,2),
              gradient.cols = c("#00AFBB", "#E7B800", "#FC4E07"), 
              repel = TRUE # Évite le chevauchement de texte
) + ggsave("img_graphe_correlation.png", width = 11, height = 8)
plot(img_graphe_correlation)

Graphique des corrélations pour les dimensions 1 et 3

img_graphe_correlation_1_3<-fviz_pca_var(res.pca, col.var = "cos2", axes = c(1,3),
              gradient.cols = c("#00AFBB", "#E7B800", "#FC4E07"),
              repel = TRUE # Évite le chevauchement de texte
) + ggsave("img_graphe_correlation_1_3.png", width = 11, height = 8)
plot(img_graphe_correlation_1_3)

Le cercle des corrélations aide à interpréter les axes d’inertie.

Représentation des individus

fviz_pca_biplot(res.pca, habillage = 1, addEllipses =TRUE,   pointsize = 1, labelsize = 3, axes = c(1,2) )

ANALYSE des cercle des corrélations

plan factoriel 1-2

Il permet de voir aussi que is_genuine et length sont fortement corrélées ainsi que margin_low

plan factoriel 1-3

Si l’on pert un peu de al qualité de représentation de length, en revanche on observe une troisième variable margin_up qui devrait permettre d’affiner l’analyse des données.

ANALYSE

L’éboulis des valeurs propres montre que les deux premières dimensions expliquent 72% de l’inertie des variables. Ce qui semble suffisant mais avec la heatmap, la dimension 3 est plus significative que la 2

Mission 2 - Classification

Classification Ascendante Hiérarchique 2 Clusters La CAH va déterminer si un billet est vrai ou faux en fonction des variables

HCPC par rapport à l’ACP répartition en deux clusters

res.hcpc <- HCPC(res.pca, nb.clust=2, graph = FALSE) # 
head(res.hcpc)
$data.clust

$desc.var

Link between the cluster variable and the categorical variables (chi-square test)
=================================================================================
                p.value df
is_genuine 1.699353e-31  1

Description of each cluster by the categories
=============================================
$`1`
                   Cla/Mod   Mod/Cla   Global     p.value    v.test
is_genuine=True  92.000000 98.924731 58.82353 3.02179e-37  12.75243
is_genuine=False  1.428571  1.075269 41.17647 3.02179e-37 -12.75243

$`2`
                  Cla/Mod  Mod/Cla   Global     p.value    v.test
is_genuine=False 98.57143 89.61039 41.17647 3.02179e-37  12.75243
is_genuine=True   8.00000 10.38961 58.82353 3.02179e-37 -12.75243


Link between the cluster variable and the quantitative variables
================================================================
                  Eta2      P-value
length       0.6380042 6.544704e-39
margin_low   0.5511088 4.963822e-31
height_right 0.4301258 2.844171e-22
height_left  0.3460892 3.295328e-17
margin_up    0.3417477 5.780388e-17

Description of each cluster by quantitative variables
=====================================================
$`1`
                v.test Mean in category  Overall mean sd in category Overall sd      p.value
length       10.383771        0.7246604  1.625579e-15      0.3725574  0.9970545 2.939300e-25
margin_up    -7.599695       -0.5303659  4.501709e-16      0.8122175  0.9970545 2.968301e-14
height_left  -7.647815       -0.5337241 -1.626105e-14      0.8634866  0.9970545 2.044229e-14
height_right -8.525917       -0.5950049 -6.826402e-15      0.7329393  0.9970545 1.516041e-17
margin_low   -9.650771       -0.6735060 -5.177639e-16      0.4933067  0.9970545 4.878742e-22

$`2`
                 v.test Mean in category  Overall mean sd in category Overall sd      p.value
margin_low     9.650771        0.8134553 -5.177639e-16      0.8314510  0.9970545 4.878742e-22
height_right   8.525917        0.7186422 -6.826402e-15      0.7758459  0.9970545 1.516041e-17
height_left    7.647815        0.6446278 -1.626105e-14      0.7312085  0.9970545 2.044229e-14
margin_up      7.599695        0.6405718  4.501709e-16      0.8049586  0.9970545 2.968301e-14
length       -10.383771       -0.8752392  1.625579e-15      0.7917512  0.9970545 2.939300e-25


$desc.axes

Link between the cluster variable and the quantitative variables
================================================================
            Eta2      P-value
Dim.1 0.78592540 4.052533e-58
Dim.2 0.05255236 2.637868e-03

Description of each cluster by quantitative variables
=====================================================
$`1`
          v.test Mean in category  Overall mean sd in category Overall sd      p.value
Dim.2   2.980159        0.2387163 -3.618429e-17       1.035101   1.144411 2.880985e-03
Dim.1 -11.524816       -1.3570557 -4.065375e-17       0.844393   1.682299 9.892284e-31

$`2`
         v.test Mean in category  Overall mean sd in category Overall sd      p.value
Dim.1 11.524816        1.6390413 -4.065375e-17      0.6902571   1.682299 9.892284e-31
Dim.2 -2.980159       -0.2883197 -3.618429e-17      1.2022770   1.144411 2.880985e-03


$desc.ind
$desc.ind$para
Cluster: 1
       47        90        86        60        82 
0.3706149 0.5573561 0.6140290 0.6383198 0.6876754 
------------------------------------------------------------------------------------------ 
Cluster: 2
      133       106       112       114       115 
0.5059074 0.5332767 0.7610073 0.8182491 0.8357569 

$desc.ind$dist
Cluster: 1
       5       50       40       30       41 
5.352441 4.952443 4.782645 4.743077 4.511329 
------------------------------------------------------------------------------------------ 
Cluster: 2
     167      123        1      113      159 
5.283726 5.183932 4.831765 4.821886 4.703969 


$call
$call$t
$call$t$res
**Results for the Principal Component Analysis (PCA)**
The analysis was performed on 170 individuals, described by 7 variables
*The results are available in the following objects:

   name                description                                          
1  "$eig"              "eigenvalues"                                        
2  "$var"              "results for the variables"                          
3  "$var$coord"        "coord. for the variables"                           
4  "$var$cor"          "correlations variables - dimensions"                
5  "$var$cos2"         "cos2 for the variables"                             
6  "$var$contrib"      "contributions of the variables"                     
7  "$ind"              "results for the individuals"                        
8  "$ind$coord"        "coord. for the individuals"                         
9  "$ind$cos2"         "cos2 for the individuals"                           
10 "$ind$contrib"      "contributions of the individuals"                   
11 "$quali.sup"        "results for the supplementary categorical variables"
12 "$quali.sup$coord"  "coord. for the supplementary categories"            
13 "$quali.sup$v.test" "v-test of the supplementary categories"             
14 "$call"             "summary statistics"                                 
15 "$call$centre"      "mean of the variables"                              
16 "$call$ecart.type"  "standard error of the variables"                    
17 "$call$row.w"       "weights for the individuals"                        
18 "$call$col.w"       "weights for the variables"                          

$call$t$tree

Call:
flashClust::hclust(d = dissi, method = method, members = weight)

Cluster method   : ward 
Distance         : euclidean 
Number of objects: 170 


$call$t$nb.clust
[1] 3

$call$t$within
  [1] 5.7725629478 3.5216096062 2.8822349752 2.5372368521 2.3086311796 2.1053342348 1.9201623452 1.7959504723
  [9] 1.7063752694 1.6195953318 1.5359365894 1.4547471615 1.3777404740 1.3164514466 1.2566719232 1.1979150006
 [17] 1.1397298450 1.1030633286 1.0696086745 1.0368277160 1.0042270707 0.9728749971 0.9421426698 0.9119650895
 [25] 0.8826535120 0.8534067171 0.8263212996 0.7998404412 0.7745263155 0.7513042491 0.7283599048 0.7060653274
 [33] 0.6839042923 0.6618414631 0.6410198650 0.6204314992 0.6009762585 0.5818849219 0.5631246791 0.5467696155
 [41] 0.5316927053 0.5166275196 0.5019226130 0.4873178525 0.4729759978 0.4605117283 0.4484657905 0.4364247177
 [49] 0.4244189952 0.4131806274 0.4020348461 0.3918511297 0.3821537846 0.3726518839 0.3636523046 0.3551266323
 [57] 0.3466205645 0.3381958500 0.3300306225 0.3220887811 0.3141524188 0.3063450403 0.2986927636 0.2917950204
 [65] 0.2851479070 0.2785355837 0.2719462901 0.2654219703 0.2589358184 0.2525715591 0.2462446200 0.2399330629
 [73] 0.2336562982 0.2274164150 0.2216801391 0.2159710077 0.2103574465 0.2049645162 0.1996316330 0.1943987634
 [81] 0.1892868147 0.1842691704 0.1795417302 0.1749930629 0.1706271957 0.1663459239 0.1621049400 0.1579526700
 [89] 0.1539625511 0.1500147348 0.1462088092 0.1425218640 0.1388739686 0.1352669373 0.1316781386 0.1281720358
 [97] 0.1247158258 0.1212929774 0.1179024910 0.1145955337 0.1112923850 0.1080190806 0.1047561909 0.1016464603
[105] 0.0985446912 0.0955912941 0.0926878351 0.0898082492 0.0869456325 0.0841238094 0.0814814059 0.0788436790
[113] 0.0762264160 0.0736958415 0.0712136259 0.0687760048 0.0664423531 0.0641393942 0.0619265224 0.0597179228
[121] 0.0575252891 0.0553645906 0.0532388010 0.0511207781 0.0491402619 0.0471978350 0.0454091505 0.0436497883
[129] 0.0419026869 0.0402297986 0.0385958479 0.0369779644 0.0353606230 0.0337528408 0.0322260007 0.0307458373
[137] 0.0292808281 0.0278229367 0.0263931419 0.0249647376 0.0235615049 0.0221752711 0.0208736976 0.0195995187
[145] 0.0183913104 0.0172273613 0.0161350854 0.0150861818 0.0140839056 0.0131047823 0.0121381514 0.0112078259
[153] 0.0102990755 0.0094095326 0.0085317046 0.0076708425 0.0068802012 0.0060924760 0.0053867782 0.0047136514
[161] 0.0041009399 0.0035224122 0.0029515071 0.0024678124 0.0019874070 0.0015346056 0.0010914685 0.0006937041
[169] 0.0003096729

$call$t$inert.gain
  [1] 2.2509533416 0.6393746311 0.3449981230 0.2286056725 0.2032969448 0.1851718896 0.1242118729 0.0895752029
  [9] 0.0867799376 0.0836587424 0.0811894279 0.0770066875 0.0612890274 0.0597795234 0.0587569226 0.0581851556
 [17] 0.0366665165 0.0334546541 0.0327809585 0.0326006454 0.0313520735 0.0307323274 0.0301775802 0.0293115775
 [25] 0.0292467949 0.0270854175 0.0264808585 0.0253141257 0.0232220664 0.0229443443 0.0222945774 0.0221610352
 [33] 0.0220628292 0.0208215981 0.0205883658 0.0194552407 0.0190913366 0.0187602429 0.0163550636 0.0150769102
 [41] 0.0150651857 0.0147049066 0.0146047605 0.0143418547 0.0124642694 0.0120459378 0.0120410728 0.0120057225
 [49] 0.0112383678 0.0111457813 0.0101837164 0.0096973451 0.0095019008 0.0089995792 0.0085256723 0.0085060678
 [57] 0.0084247145 0.0081652275 0.0079418414 0.0079363622 0.0078073785 0.0076522768 0.0068977432 0.0066471133
 [65] 0.0066123233 0.0065892937 0.0065243197 0.0064861519 0.0063642593 0.0063269391 0.0063115571 0.0062767646
 [73] 0.0062398832 0.0057362759 0.0057091313 0.0056135612 0.0053929303 0.0053328832 0.0052328697 0.0051119487
 [81] 0.0050176443 0.0047274402 0.0045486673 0.0043658673 0.0042812717 0.0042409839 0.0041522700 0.0039901189
 [89] 0.0039478163 0.0038059256 0.0036869453 0.0036478954 0.0036070313 0.0035887987 0.0035061028 0.0034562100
 [97] 0.0034228483 0.0033904865 0.0033069573 0.0033031488 0.0032733043 0.0032628898 0.0031097306 0.0031017692
[105] 0.0029533970 0.0029034590 0.0028795859 0.0028626167 0.0028218231 0.0026424035 0.0026377269 0.0026172629
[113] 0.0025305745 0.0024822156 0.0024376211 0.0023336517 0.0023029588 0.0022128718 0.0022085996 0.0021926338
[121] 0.0021606984 0.0021257896 0.0021180229 0.0019805162 0.0019424270 0.0017886844 0.0017593622 0.0017471014
[129] 0.0016728883 0.0016339507 0.0016178835 0.0016173414 0.0016077822 0.0015268400 0.0014801634 0.0014650092
[137] 0.0014578913 0.0014297949 0.0014284043 0.0014032327 0.0013862338 0.0013015735 0.0012741789 0.0012082083
[145] 0.0011639491 0.0010922759 0.0010489036 0.0010022762 0.0009791233 0.0009666309 0.0009303255 0.0009087504
[153] 0.0008895429 0.0008778279 0.0008608621 0.0007906413 0.0007877252 0.0007056978 0.0006731268 0.0006127115
[161] 0.0005785276 0.0005709052 0.0004836947 0.0004804054 0.0004528015 0.0004431370 0.0003977644 0.0003840312
[169] 0.0003096729

$call$t$quot
[1] 0.8184425 0.8803019 0.9098998 0.9119405 0.9120463 0.9353118 0.9501238 0.9491437


$call$min
[1] 3

$call$max
[1] 10

$call$X

$call$bw.before.consol
[1] 2.250953

$call$bw.after.consol
[1] 2.296803

$call$vec
[1] FALSE

$call$call
HCPC(res = res.pca, nb.clust = 2, graph = FALSE)

Dendogramme

img_dendrogramme <- fviz_dend(res.hcpc,
          labelsize = 2,
          cex = 0.5,                     # 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
          ) 
plot(img_dendrogramme)

Observation des clusters

img_factor_map <- fviz_cluster(res.hcpc,
             labelsize = 5,
             repel = FALSE,            # 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"
) + ggsave("P6_img_factormap.png", width = 11, height = 8)
plot(img_factor_map)

df_billets_clust<-  res.hcpc$data.clust
df_billets_clust <- transform(df_billets_clust, clust = as.numeric(clust)) 


# Le cluster 1 correspond aux vrais billets.
df_billets_clust$clust[df_billets_clust$clust=='1']<-'True'
df_billets_clust$clust[df_billets_clust$clust=='2']<-'False'

# Le cluster 2 correspond aux faux billets.
#billets.hc$groupes.hc[billets.hc$groupes.hc=='2']<-'False'
#billets.hc <- transform(billets.hc, groupes.hc = as.factor(groupes.hc)) 

# Matrice de confusion à partir données réelles et des données prédites par l'ACH
set.seed(123)
mc_clust = table(df_billets_clust$is_genuine,df_billets_clust$clust)
mc_clust
       
        False True
  False    69    1
  True      8   92

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

FIN cah sur centré réduit

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

CAH centré réduit MATRICE DE CONFUSION

#reskmeansCR$cluster
#df_notes_vf_numeric$is_genuine
set.seed(123)
MC = table( df_notes_vf_numeric$is_genuine, res.hcpc$data.clust[,8]) 
MC
   
     1  2
  0  1 69
  1 92  8

CONCLUSION

Le boxplot montre que deux variables sont particulièrement discriminatoires : length et margin low

le cercle des corrélations montre nettement deux variables corrélées qui sont length et is_genuine.

Nous considérons la variable is_genuine comme illustrative puisqu’elle ne participe pas à l’analyse, mais elle permet de l’expliquer. ### La variable margin low est aussi fortement corrélée à length et is_genuine. ### Le Biplot avec les variables et les individus montre deux ensembles bien séparés en fonction de length et margin_low. Plus l’individu va vers le length, plus on a un vrai billet. Et plus l’individu s’oriente vers margin_low, plus on est sûr d’avoir un faux billet.

Variable illustrative is_genuine qualitative

df_notes_vf_numeric[,1:7]
res.pca = PCA(df_notes_vf_numeric[,1:7], scale.unit=TRUE,axes = c(1,3), graph=T, quali.sup = 1) 

analyse :

101 individus dans le premier groupe et 69 dans le second

within cluster sum … qualité du partitionnement

  • inertie intraclasse dans les deux groupes
  • ratio entre l’inertie expliquée par le partitionnement / inertie totale indique une bonne qualité de répartition

kmeans sur centré réduit

reskmeans<- kmeans(df_notes_vf_numeric[,2:7],centers = 2, nstart=5)

CONCLUSION

La représentation par le K-means sur la dataframe centré réduite est de moins bonne qualité si l’on observe le ratio entre l’intertie exliquée par le partitionnement et l’innertie totale

MATRICES DE CONFUSION

K-means centré réduit matrice de confusion

#reskmeans$cluster
#df_notes_vf_numeric$is_genuine
set.seed(123)
MC = table( df_notes_vf_numeric$is_genuine, reskmeans$cluster) 
MC
   
     1  2
  0 68  2
  1  1 99

CONCLUSION

On constate que la CAH donne un meilleur résultat que les Kmeans

Mission 3 - Modélisation

———————————————————–

CARET modélisation Classification And REgression Training

———————————————————–

pour des questions pratiques nous renomons le df en data et lancerons des tests à partir de df CR ou bruts

#data<-df_notes_vf_numeric
data<-df_notes

installation package CARET

chargement de la librairie

#install.packages("caret")
#install.packages("e1071")
library(caret)
Le chargement a nécessité le package : lattice
library(lattice)
library(e1071)

print(dim(data))
[1] 170   7
head(data)
nrow(data)
[1] 170
data <- transform(data, is_genuine = as.character(is_genuine)) 

# Le cluster 1 correspond aux vrais billets.
data$is_genuine[data$is_genuine=='True']<-1

# Le cluster 2 correspond aux faux billets.
data$is_genuine[data$is_genuine=='False']<-0

data <- transform(data, is_genuine = as.numeric(is_genuine)) 

head(data)

une erreur due au fait que l’on voulait faire une classification dans le cas d’un test en glm et non glmStepAic

#data$is_genuine= as.factor(data$is_genuine)

séparation des données en données test et données apprentissage

set.seed(123)
trainIndex <-createDataPartition(data$is_genuine,p=0.8,list=F)
print(length(trainIndex))
[1] 136

#10 premiers individus de l’échantillon d’apprentissage

print(head(trainIndex,10))
      Resample1
 [1,]         1
 [2,]         4
 [3,]         5
 [4,]         6
 [5,]         7
 [6,]         8
 [7,]         9
 [8,]        11
 [9,]        12
[10,]        13

dataTrain

#data frame pour les individus en apprentissage

dataTrain <-data[trainIndex,]
print(dim(dataTrain))
[1] 136   7

dataframe inverse des index pour les données d’apprentissage

dataTest <-data[-trainIndex,]
print(dim(dataTest))
[1] 34  7
print(table(dataTrain$is_genuine))

 0  1 
58 78 

distribution des classes dans le fichier d’origine

print(prop.table(table(data$is_genuine)))

        0         1 
0.4117647 0.5882353 

fréquences relatives des classes dans l’échantillon d’apprentissage

print(prop.table(table(dataTrain$is_genuine)))

        0         1 
0.4264706 0.5735294 

distribution des classes dans l’échantillon de test

print(prop.table(table(dataTest$is_genuine)))

        0         1 
0.3529412 0.6470588 

Analyse : les proportions dans les échantillons sont comparables aux données d’origine, donc on peut faire confiance à cet échantillon

test<- lm(is_genuine~.,data=dataTrain)
test

Call:
lm(formula = is_genuine ~ ., data = dataTrain)

Coefficients:
 (Intercept)      diagonal   height_left  height_right    margin_low     margin_up        length  
   -14.38596      -0.00225       0.10299      -0.11018      -0.36888      -0.64928       0.17629  

apprentissage sur un échantillon

default_glm_mod = train(
  form = is_genuine ~ .,
  data = data,
  trControl = trainControl(method = "cv", number = 5),
  method = "glmStepAIC",
  family = "binomial"
)
You are trying to do regression and your outcome only has two possible values Are you trying to do classification? If so, use a 2 level factor as your outcome column.glm.fit: algorithm did not convergeglm.fit: fitted probabilities numerically 0 or 1 occurred
Start:  AIC=14
.outcome ~ diagonal + height_left + height_right + margin_low + 
    margin_up + length
glm.fit: algorithm did not convergeglm.fit: fitted probabilities numerically 0 or 1 occurredglm.fit: algorithm did not convergeglm.fit: fitted probabilities numerically 0 or 1 occurredglm.fit: algorithm did not convergeglm.fit: fitted probabilities numerically 0 or 1 occurredglm.fit: fitted probabilities numerically 0 or 1 occurredglm.fit: fitted probabilities numerically 0 or 1 occurred
               Df Deviance    AIC
- diagonal      1    0.000 12.000
- height_right  1    0.000 12.000
- height_left   1    0.000 12.000
<none>               0.000 14.000
- margin_up     1    7.622 19.622
- length        1    9.606 21.606
- margin_low    1   37.587 49.587
glm.fit: algorithm did not convergeglm.fit: fitted probabilities numerically 0 or 1 occurred

Step:  AIC=12
.outcome ~ height_left + height_right + margin_low + margin_up + 
    length
glm.fit: algorithm did not convergeglm.fit: fitted probabilities numerically 0 or 1 occurredglm.fit: algorithm did not convergeglm.fit: fitted probabilities numerically 0 or 1 occurredglm.fit: fitted probabilities numerically 0 or 1 occurred
               Df Deviance    AIC
- height_right  1    0.000 10.000
- height_left   1    0.000 10.000
<none>               0.000 12.000
- margin_up     1    8.111 18.111
- length        1   10.780 20.780
- margin_low    1   40.446 50.446
glm.fit: algorithm did not convergeglm.fit: fitted probabilities numerically 0 or 1 occurred

Step:  AIC=10
.outcome ~ height_left + margin_low + margin_up + length
glm.fit: algorithm did not convergeglm.fit: fitted probabilities numerically 0 or 1 occurredglm.fit: fitted probabilities numerically 0 or 1 occurred
              Df Deviance    AIC
- height_left  1    0.000  8.000
<none>              0.000 10.000
- margin_up    1    8.257 16.257
- length       1   11.048 19.048
- margin_low   1   43.740 51.740
glm.fit: algorithm did not convergeglm.fit: fitted probabilities numerically 0 or 1 occurred

Step:  AIC=8
.outcome ~ margin_low + margin_up + length
glm.fit: fitted probabilities numerically 0 or 1 occurred
             Df Deviance    AIC
<none>             0.000  8.000
- margin_up   1    8.259 14.259
- length      1   11.148 17.148
- margin_low  1   46.722 52.722
glm.fit: algorithm did not convergeglm.fit: fitted probabilities numerically 0 or 1 occurred
Start:  AIC=14
.outcome ~ diagonal + height_left + height_right + margin_low + 
    margin_up + length
glm.fit: algorithm did not convergeglm.fit: fitted probabilities numerically 0 or 1 occurredglm.fit: algorithm did not convergeglm.fit: fitted probabilities numerically 0 or 1 occurredglm.fit: algorithm did not convergeglm.fit: fitted probabilities numerically 0 or 1 occurredglm.fit: algorithm did not convergeglm.fit: fitted probabilities numerically 0 or 1 occurredglm.fit: fitted probabilities numerically 0 or 1 occurred
               Df Deviance    AIC
- diagonal      1   0.0000 12.000
- height_left   1   0.0000 12.000
- height_right  1   0.0000 12.000
- margin_up     1   0.0000 12.000
<none>              0.0000 14.000
- length        1   7.5789 19.579
- margin_low    1  23.2244 35.224
glm.fit: algorithm did not convergeglm.fit: fitted probabilities numerically 0 or 1 occurred

Step:  AIC=12
.outcome ~ height_left + height_right + margin_low + margin_up + 
    length
glm.fit: algorithm did not convergeglm.fit: fitted probabilities numerically 0 or 1 occurredglm.fit: algorithm did not convergeglm.fit: fitted probabilities numerically 0 or 1 occurredglm.fit: fitted probabilities numerically 0 or 1 occurredglm.fit: fitted probabilities numerically 0 or 1 occurred
               Df Deviance    AIC
- height_left   1   0.0000 10.000
- height_right  1   0.0000 10.000
<none>              0.0000 12.000
- margin_up     1   6.1447 16.145
- length        1   7.9797 17.980
- margin_low    1  29.1631 39.163
glm.fit: algorithm did not convergeglm.fit: fitted probabilities numerically 0 or 1 occurred

Step:  AIC=10
.outcome ~ height_right + margin_low + margin_up + length
glm.fit: algorithm did not convergeglm.fit: fitted probabilities numerically 0 or 1 occurredglm.fit: fitted probabilities numerically 0 or 1 occurredglm.fit: fitted probabilities numerically 0 or 1 occurred
               Df Deviance    AIC
- height_right  1   0.0000  8.000
<none>              0.0000 10.000
- margin_up     1   6.7383 14.738
- length        1   8.5560 16.556
- margin_low    1  29.5859 37.586
glm.fit: algorithm did not convergeglm.fit: fitted probabilities numerically 0 or 1 occurred

Step:  AIC=8
.outcome ~ margin_low + margin_up + length
glm.fit: fitted probabilities numerically 0 or 1 occurredglm.fit: fitted probabilities numerically 0 or 1 occurred
             Df Deviance    AIC
<none>             0.000  8.000
- margin_up   1    6.738 12.738
- length      1    9.204 15.204
- margin_low  1   38.651 44.651
glm.fit: algorithm did not convergeglm.fit: fitted probabilities numerically 0 or 1 occurred
Start:  AIC=14
.outcome ~ diagonal + height_left + height_right + margin_low + 
    margin_up + length
glm.fit: algorithm did not convergeglm.fit: fitted probabilities numerically 0 or 1 occurredglm.fit: algorithm did not convergeglm.fit: fitted probabilities numerically 0 or 1 occurredglm.fit: algorithm did not convergeglm.fit: fitted probabilities numerically 0 or 1 occurredglm.fit: fitted probabilities numerically 0 or 1 occurredglm.fit: algorithm did not convergeglm.fit: fitted probabilities numerically 0 or 1 occurred
               Df Deviance    AIC
- height_left   1    0.000 12.000
- height_right  1    0.000 12.000
- diagonal      1    0.000 12.000
- length        1    0.000 12.000
<none>               0.000 14.000
- margin_up     1    5.332 17.332
- margin_low    1   37.863 49.863
glm.fit: algorithm did not convergeglm.fit: fitted probabilities numerically 0 or 1 occurred

Step:  AIC=12
.outcome ~ diagonal + height_right + margin_low + margin_up + 
    length
glm.fit: algorithm did not convergeglm.fit: fitted probabilities numerically 0 or 1 occurredglm.fit: algorithm did not convergeglm.fit: fitted probabilities numerically 0 or 1 occurredglm.fit: fitted probabilities numerically 0 or 1 occurredglm.fit: algorithm did not convergeglm.fit: fitted probabilities numerically 0 or 1 occurred
               Df Deviance    AIC
- diagonal      1    0.000 10.000
- height_right  1    0.000 10.000
- length        1    0.000 10.000
<none>               0.000 12.000
- margin_up     1    5.591 15.591
- margin_low    1   37.983 47.983
glm.fit: algorithm did not convergeglm.fit: fitted probabilities numerically 0 or 1 occurred

Step:  AIC=10
.outcome ~ height_right + margin_low + margin_up + length
glm.fit: algorithm did not convergeglm.fit: fitted probabilities numerically 0 or 1 occurredglm.fit: fitted probabilities numerically 0 or 1 occurredglm.fit: algorithm did not convergeglm.fit: fitted probabilities numerically 0 or 1 occurred
               Df Deviance    AIC
- height_right  1    0.000  8.000
- length        1    0.000  8.000
<none>               0.000 10.000
- margin_up     1    5.742 13.742
- margin_low    1   42.481 50.481
glm.fit: algorithm did not convergeglm.fit: fitted probabilities numerically 0 or 1 occurred

Step:  AIC=8
.outcome ~ margin_low + margin_up + length
glm.fit: fitted probabilities numerically 0 or 1 occurredglm.fit: algorithm did not convergeglm.fit: fitted probabilities numerically 0 or 1 occurred
             Df Deviance    AIC
- length      1    0.000  6.000
<none>             0.000  8.000
- margin_up   1    5.749 11.749
- margin_low  1   52.699 58.699
glm.fit: algorithm did not convergeglm.fit: fitted probabilities numerically 0 or 1 occurred

Step:  AIC=6
.outcome ~ margin_low + margin_up

             Df Deviance     AIC
<none>             0.000   6.000
- margin_up   1   59.851  63.851
- margin_low  1  124.178 128.178
glm.fit: algorithm did not convergeglm.fit: fitted probabilities numerically 0 or 1 occurred
Start:  AIC=14
.outcome ~ diagonal + height_left + height_right + margin_low + 
    margin_up + length
glm.fit: algorithm did not convergeglm.fit: fitted probabilities numerically 0 or 1 occurredglm.fit: algorithm did not convergeglm.fit: fitted probabilities numerically 0 or 1 occurredglm.fit: algorithm did not convergeglm.fit: fitted probabilities numerically 0 or 1 occurredglm.fit: fitted probabilities numerically 0 or 1 occurredglm.fit: fitted probabilities numerically 0 or 1 occurred
               Df Deviance    AIC
- diagonal      1    0.000 12.000
- height_right  1    0.000 12.000
- height_left   1    0.000 12.000
<none>               0.000 14.000
- margin_up     1    7.949 19.949
- length        1   10.436 22.436
- margin_low    1   35.314 47.314
glm.fit: algorithm did not convergeglm.fit: fitted probabilities numerically 0 or 1 occurred

Step:  AIC=12
.outcome ~ height_left + height_right + margin_low + margin_up + 
    length
glm.fit: algorithm did not convergeglm.fit: fitted probabilities numerically 0 or 1 occurredglm.fit: algorithm did not convergeglm.fit: fitted probabilities numerically 0 or 1 occurredglm.fit: fitted probabilities numerically 0 or 1 occurredglm.fit: fitted probabilities numerically 0 or 1 occurred
               Df Deviance    AIC
- height_right  1    0.000 10.000
- height_left   1    0.000 10.000
<none>               0.000 12.000
- margin_up     1    8.250 18.250
- length        1   10.436 20.436
- margin_low    1   44.743 54.743
glm.fit: algorithm did not convergeglm.fit: fitted probabilities numerically 0 or 1 occurred

Step:  AIC=10
.outcome ~ height_left + margin_low + margin_up + length
glm.fit: algorithm did not convergeglm.fit: fitted probabilities numerically 0 or 1 occurredglm.fit: fitted probabilities numerically 0 or 1 occurredglm.fit: fitted probabilities numerically 0 or 1 occurred
              Df Deviance    AIC
- height_left  1    0.000  8.000
<none>              0.000 10.000
- margin_up    1    8.358 16.358
- length       1   10.459 18.459
- margin_low   1   48.824 56.824
glm.fit: algorithm did not convergeglm.fit: fitted probabilities numerically 0 or 1 occurred

Step:  AIC=8
.outcome ~ margin_low + margin_up + length
glm.fit: fitted probabilities numerically 0 or 1 occurred
             Df Deviance    AIC
<none>             0.000  8.000
- margin_up   1    8.378 14.378
- length      1   11.037 17.037
- margin_low  1   52.645 58.645
glm.fit: algorithm did not convergeglm.fit: fitted probabilities numerically 0 or 1 occurred
Start:  AIC=14
.outcome ~ diagonal + height_left + height_right + margin_low + 
    margin_up + length
glm.fit: algorithm did not convergeglm.fit: fitted probabilities numerically 0 or 1 occurredglm.fit: algorithm did not convergeglm.fit: fitted probabilities numerically 0 or 1 occurredglm.fit: algorithm did not convergeglm.fit: fitted probabilities numerically 0 or 1 occurredglm.fit: algorithm did not convergeglm.fit: fitted probabilities numerically 0 or 1 occurredglm.fit: fitted probabilities numerically 0 or 1 occurred
               Df Deviance    AIC
- diagonal      1   0.0000 12.000
- height_right  1   0.0000 12.000
- height_left   1   0.0000 12.000
- margin_up     1   0.0000 12.000
<none>              0.0000 14.000
- length        1   8.1912 20.191
- margin_low    1  27.3806 39.381
glm.fit: algorithm did not convergeglm.fit: fitted probabilities numerically 0 or 1 occurred

Step:  AIC=12
.outcome ~ height_left + height_right + margin_low + margin_up + 
    length
glm.fit: algorithm did not convergeglm.fit: fitted probabilities numerically 0 or 1 occurredglm.fit: algorithm did not convergeglm.fit: fitted probabilities numerically 0 or 1 occurredglm.fit: algorithm did not convergeglm.fit: fitted probabilities numerically 0 or 1 occurredglm.fit: fitted probabilities numerically 0 or 1 occurred
               Df Deviance    AIC
- height_right  1   0.0000 10.000
- height_left   1   0.0000 10.000
- margin_up     1   0.0000 10.000
<none>              0.0000 12.000
- length        1   9.8466 19.847
- margin_low    1  30.3930 40.393
glm.fit: algorithm did not convergeglm.fit: fitted probabilities numerically 0 or 1 occurred

Step:  AIC=10
.outcome ~ height_left + margin_low + margin_up + length
glm.fit: algorithm did not convergeglm.fit: fitted probabilities numerically 0 or 1 occurredglm.fit: algorithm did not convergeglm.fit: fitted probabilities numerically 0 or 1 occurredglm.fit: fitted probabilities numerically 0 or 1 occurred
              Df Deviance    AIC
- height_left  1    0.000  8.000
- margin_up    1    0.000  8.000
<none>              0.000 10.000
- length       1   11.183 19.183
- margin_low   1   33.019 41.019
glm.fit: algorithm did not convergeglm.fit: fitted probabilities numerically 0 or 1 occurred

Step:  AIC=8
.outcome ~ margin_low + margin_up + length
glm.fit: algorithm did not convergeglm.fit: fitted probabilities numerically 0 or 1 occurredglm.fit: fitted probabilities numerically 0 or 1 occurred
             Df Deviance    AIC
- margin_up   1    0.000  6.000
<none>             0.000  8.000
- length      1   11.269 17.269
- margin_low  1   37.695 43.695
glm.fit: algorithm did not convergeglm.fit: fitted probabilities numerically 0 or 1 occurred

Step:  AIC=6
.outcome ~ margin_low + length

             Df Deviance    AIC
<none>             0.000  6.000
- margin_low  1   41.685 45.685
- length      1   62.303 66.303
glm.fit: algorithm did not convergeglm.fit: fitted probabilities numerically 0 or 1 occurred
Start:  AIC=14
.outcome ~ diagonal + height_left + height_right + margin_low + 
    margin_up + length
glm.fit: algorithm did not convergeglm.fit: fitted probabilities numerically 0 or 1 occurredglm.fit: algorithm did not convergeglm.fit: fitted probabilities numerically 0 or 1 occurredglm.fit: algorithm did not convergeglm.fit: fitted probabilities numerically 0 or 1 occurredglm.fit: fitted probabilities numerically 0 or 1 occurredglm.fit: fitted probabilities numerically 0 or 1 occurred
               Df Deviance    AIC
- diagonal      1    0.000 12.000
- height_right  1    0.000 12.000
- height_left   1    0.000 12.000
<none>               0.000 14.000
- margin_up     1    8.265 20.265
- length        1   11.198 23.198
- margin_low    1   42.342 54.342
glm.fit: algorithm did not convergeglm.fit: fitted probabilities numerically 0 or 1 occurred

Step:  AIC=12
.outcome ~ height_left + height_right + margin_low + margin_up + 
    length
glm.fit: algorithm did not convergeglm.fit: fitted probabilities numerically 0 or 1 occurredglm.fit: algorithm did not convergeglm.fit: fitted probabilities numerically 0 or 1 occurredglm.fit: fitted probabilities numerically 0 or 1 occurredglm.fit: fitted probabilities numerically 0 or 1 occurred
               Df Deviance    AIC
- height_right  1    0.000 10.000
- height_left   1    0.000 10.000
<none>               0.000 12.000
- margin_up     1    8.568 18.568
- length        1   12.462 22.462
- margin_low    1   47.782 57.782
glm.fit: algorithm did not convergeglm.fit: fitted probabilities numerically 0 or 1 occurred

Step:  AIC=10
.outcome ~ height_left + margin_low + margin_up + length
glm.fit: algorithm did not convergeglm.fit: fitted probabilities numerically 0 or 1 occurredglm.fit: fitted probabilities numerically 0 or 1 occurredglm.fit: fitted probabilities numerically 0 or 1 occurred
              Df Deviance    AIC
- height_left  1    0.000  8.000
<none>              0.000 10.000
- margin_up    1    8.585 16.585
- length       1   12.716 20.716
- margin_low   1   53.624 61.624
glm.fit: algorithm did not convergeglm.fit: fitted probabilities numerically 0 or 1 occurred

Step:  AIC=8
.outcome ~ margin_low + margin_up + length
glm.fit: fitted probabilities numerically 0 or 1 occurredglm.fit: fitted probabilities numerically 0 or 1 occurred
             Df Deviance    AIC
<none>             0.000  8.000
- margin_up   1    8.586 14.586
- length      1   12.721 18.721
- margin_low  1   57.812 63.812

par régression logistique

m_lr <-train(is_genuine ~., data =dataTrain , method="glmStepAIC",trControl=trainControl("none")) #trControl=fitControl)
You are trying to do regression and your outcome only has two possible values Are you trying to do classification? If so, use a 2 level factor as your outcome column.
Start:  AIC=-102.63
.outcome ~ diagonal + height_left + height_right + margin_low + 
    margin_up + length

               Df Deviance      AIC
- diagonal      1   3.3284 -104.630
<none>              3.3284 -102.632
- height_left   1   3.3845 -102.358
- height_right  1   3.3961 -101.893
- length        1   4.7762  -55.513
- margin_up     1   5.2373  -42.981
- margin_low    1   6.7733   -8.003

Step:  AIC=-104.63
.outcome ~ height_left + height_right + margin_low + margin_up + 
    length

               Df Deviance      AIC
<none>              3.3284 -104.630
- height_left   1   3.3882 -104.208
- height_right  1   3.3990 -103.775
- length        1   4.7789  -57.438
- margin_up     1   5.2816  -43.834
- margin_low    1   7.2308   -1.115
print(summary(m_lr$finalModel))

Call:
NULL

Deviance Residuals: 
     Min        1Q    Median        3Q       Max  
-0.45002  -0.10279   0.00227   0.10643   0.36675  

Coefficients:
              Estimate Std. Error t value Pr(>|t|)    
(Intercept)  -14.63075    6.48615  -2.256   0.0258 *  
height_left    0.10210    0.06679   1.529   0.1288    
height_right  -0.11073    0.06668  -1.661   0.0992 .  
margin_low    -0.36837    0.02984 -12.346  < 2e-16 ***
margin_up     -0.64874    0.07428  -8.734 1.05e-14 ***
length         0.17633    0.02343   7.527 7.67e-12 ***
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

(Dispersion parameter for gaussian family taken to be 0.02560321)

    Null deviance: 33.2647  on 135  degrees of freedom
Residual deviance:  3.3284  on 130  degrees of freedom
AIC: -104.63

Number of Fisher Scoring iterations: 2

CONCLUSION StepAIC

Le modèle retenu par la regression logistique tient compte des variables margin_low + margin_up + length

On applique l’algorythme trouvé à l’échantillon de test

modele <- glm(is_genuine ~ margin_low + margin_up + length, data = data, family=binomial(link="logit"))
glm.fit: algorithm did not convergeglm.fit: fitted probabilities numerically 0 or 1 occurred
#prediction
pred.res <-predict(default_glm_mod,newdata=dataTest)
#distribution des classes prédites
table(pred.res)
pred.res
2.22044604925031e-16 3.36519837106077e-13 3.08897338398342e-10    0.999999991661286                    1 
                  10                    1                    1                    1                   21 
pred.res <- ifelse(pred.res > 0.5,1,0)
pred.res[pred.res==1] <- 'True'
pred.res[pred.res==0] <- 'False'
dataTest$is_genuine
 [1] 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 0 0 0 0 0 0 0 0 0 0 0
dataTest$pred <- pred.res
dataTest <- transform(dataTest, pred = as.factor(pred))
dataTest$is_genuine[dataTest$is_genuine == "1"] <- "True"
dataTest$is_genuine[dataTest$is_genuine == "0"] <- "False"
dataTest
set.seed(123)
MC = table( dataTest$is_genuine, dataTest$pred) 
MC
       
        False True
  False    12    0
  True      0   22

Analyse

Les classes prédites sont en ligne, les observées en colonnes

Accuracy ( taux de réussite ) = 97%

Sensibilité = 100%

Il n’y a qu’une seule erreur sur cet échantillon

summary du modèle de régression linéaire pour l’ordre des variables à supprimer

summary(m_lr)

Call:
NULL

Deviance Residuals: 
     Min        1Q    Median        3Q       Max  
-0.45002  -0.10279   0.00227   0.10643   0.36675  

Coefficients:
              Estimate Std. Error t value Pr(>|t|)    
(Intercept)  -14.63075    6.48615  -2.256   0.0258 *  
height_left    0.10210    0.06679   1.529   0.1288    
height_right  -0.11073    0.06668  -1.661   0.0992 .  
margin_low    -0.36837    0.02984 -12.346  < 2e-16 ***
margin_up     -0.64874    0.07428  -8.734 1.05e-14 ***
length         0.17633    0.02343   7.527 7.67e-12 ***
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

(Dispersion parameter for gaussian family taken to be 0.02560321)

    Null deviance: 33.2647  on 135  degrees of freedom
Residual deviance:  3.3284  on 130  degrees of freedom
AIC: -104.63

Number of Fisher Scoring iterations: 2

nouvelle exécution avec les données de Test

#prediction
pred <-predict(m_lr,newdata=dataTest)
#distribution des classes prédites
print(table(pred))
pred
 -0.288922116221507  -0.164985973152636  -0.146087142732345 -0.0361436259450549 -0.0214389890448459 -0.0191442486616396 
                  1                   1                   1                   1                   1                   1 
 0.0254033891163701  0.0718979695464412   0.235371181737662   0.340730215671069   0.387760271588281   0.439124878075962 
                  1                   1                   1                   1                   1                   1 
  0.495541570210069   0.648673367312153   0.651271730742877   0.777116223412456   0.790764631702817   0.813729784539092 
                  1                   1                   1                   1                   1                   1 
  0.816157373247556   0.844662642192681   0.855458881416993   0.868105489105375   0.882192200962251   0.888773413040425 
                  1                   1                   1                   1                   1                   1 
  0.983634553456525   0.984244859276597    1.00107311893466    1.01042262669148    1.04001273362791    1.05789933497058 
                  1                   1                   1                   1                   1                   1 
   1.06421572343166    1.07228573257934    1.11567058462171    1.14053429494833 
                  1                   1                   1                   1 

nouvelle matrice de confusion

dataTest
summary(dataTest)
  is_genuine           diagonal      height_left     height_right     margin_low      margin_up         length     
 Length:34          Min.   :171.6   Min.   :103.5   Min.   :103.2   Min.   :3.640   Min.   :2.700   Min.   :111.0  
 Class :character   1st Qu.:171.8   1st Qu.:103.9   1st Qu.:103.8   1st Qu.:4.020   1st Qu.:3.022   1st Qu.:111.9  
 Mode  :character   Median :172.0   Median :104.0   Median :104.0   Median :4.395   Median :3.165   Median :112.8  
                    Mean   :171.9   Mean   :104.1   Mean   :104.0   Mean   :4.584   Mean   :3.154   Mean   :112.6  
                    3rd Qu.:172.1   3rd Qu.:104.2   3rd Qu.:104.2   3rd Qu.:4.935   3rd Qu.:3.300   3rd Qu.:113.4  
                    Max.   :172.3   Max.   :104.7   Max.   :104.9   Max.   :6.190   Max.   :3.660   Max.   :113.8  
    pred   
 False:12  
 True :22  
           
           
           
           
summary(pred)
   Min. 1st Qu.  Median    Mean 3rd Qu.    Max. 
-0.2889  0.2617  0.8022  0.6066  0.9841  1.1405 
head(pred)
        2         3        10        15        18        20 
1.1156706 1.0578993 0.6512717 0.9842449 1.0642157 1.0010731 
print(dataTest$is_genuine)
 [1] "True"  "True"  "True"  "True"  "True"  "True"  "True"  "True"  "True"  "True"  "True"  "True"  "True"  "True" 
[15] "True"  "True"  "True"  "True"  "True"  "True"  "True"  "True"  "False" "False" "False" "False" "False" "False"
[29] "False" "False" "False" "False" "False" "False"
#mat <-confusionMatrix(data=pred,reference=dataTest$is_genuine,positive="True")
#print(mat)
model <- glm(is_genuine ~ margin_low + margin_up + length, data = df_notes_vf_numeric, family=binomial(link="logit"))
glm.fit: algorithm did not convergeglm.fit: fitted probabilities numerically 0 or 1 occurred

fichier exemple

—————————————————————————————–

fic_exemple <- paste (repertoire_sources,"example.csv",sep = "")
df_exemple <- read.csv2(fic_exemple,header = TRUE, sep = ",", dec = ".")
head(df_exemple)
#prediction
pred <-predict(m_lr,newdata=df_exemple)
#distribution des classes prédites
print(table(pred))
pred
-0.118141595741676  0.110210622375941  0.134463092522076  0.849434579974368   1.02726845628282 
                 1                  1                  1                  1                  1 
pred
         1          2          3          4          5 
 0.1102106 -0.1181416  0.1344631  0.8494346  1.0272685 
df_exemple$is_genuine<-pred
df_exemple <- transform(df_exemple, is_genuine = as.numeric(is_genuine))

df_exemple$is_genuine[df_exemple$is_genuine == "1" ] <- "False"
df_exemple$is_genuine[df_exemple$is_genuine == "2" ] <- "True"

df_billets <- df_notes
df_billets$id <- rownames(df_billets)

df_data_complet <- merge(df_billets, df_exemple, all = T, sort = FALSE)
rownames(df_data_complet) <- df_data_complet$id
dim(df_data_complet)
[1] 175   8
#df_billets <- transform(df_billets, id = as.factor(id))
# ACP billets de la première donnée et billets de l'exemple prédit
res.pca <- PCA(df_data_complet[,c(1:7)], quali.sup = 1, ind.sup = 171:175, scale.unit = TRUE, graph=FALSE)

# billets prédits par rapport aux ensembles 
graphe <- fviz_pca_ind(res.pca, geom.ind = "point",pointsize = 1,habillage=1,addEllipses = TRUE)
graphe <- fviz_add(graphe, res.pca$ind.sup$coord)
graphe

fichier fourni

—————————————————————————————–

fic_a_predire <- paste (repertoire_sources,"example.csv",sep = "")
df_a_predire <- read.csv2(fic_a_predire,header = TRUE, sep = ",", dec = ".")
head(df_a_predire)
#prediction
pred <-predict(m_lr,newdata=df_a_predire)
#distribution des classes prédites
print(table(pred))
pred
-0.118141595741676  0.110210622375941  0.134463092522076  0.849434579974368   1.02726845628282 
                 1                  1                  1                  1                  1 
pred
         1          2          3          4          5 
 0.1102106 -0.1181416  0.1344631  0.8494346  1.0272685 
df_a_predire$is_genuine<-pred
df_a_predire <- transform(df_a_predire, is_genuine = as.numeric(is_genuine))

df_a_predire$is_genuine[df_a_predire$is_genuine == "1" ] <- "False"
df_a_predire$is_genuine[df_a_predire$is_genuine == "2" ] <- "True"

df_billets <- df_notes
df_billets$id <- rownames(df_billets)

df_data_complet <- merge(df_billets, df_a_predire, all = T, sort = FALSE)
rownames(df_data_complet) <- df_data_complet$id
dim(df_data_complet)
[1] 175   8
#df_billets <- transform(df_billets, id = as.factor(id))
# ACP billets de la première donnée et billets de l'exemple prédit
res.pca <- PCA(df_data_complet[,c(1:7)], quali.sup = 1, ind.sup = 171:175, scale.unit = TRUE, graph=FALSE)

# billets prédits par rapport aux ensembles 
graphe <- fviz_pca_ind(res.pca, geom.ind = "point",pointsize = 1,habillage=1,addEllipses = TRUE)
graphe <- fviz_add(graphe, res.pca$ind.sup$coord)
graphe

afficher le plan factoriel sur ces variables

# Grâce au modèle établit, on prédit la réalité ou non des billets
fitted.results <- predict(model,newdata=df_exemple,type='response')
print(fitted.results)
           1            2            3            4            5 
2.220446e-16 2.220446e-16 2.220446e-16 1.000000e+00 1.000000e+00 
fitted.results <- ifelse(fitted.results > 0.5,1,0)
fitted.results[fitted.results==1] <- 'True'
fitted.results[fitted.results==0] <- 'False'

# La colonne is_genuine est ajouté au dataframe billets_a_predire
df_exemple$is_genuine <- fitted.results
df_exemple <- transform(df_exemple, is_genuine = as.factor(is_genuine))
df_exemple
LS0tCnRpdGxlOiAiUiBOb3RlYm9vayAtIFBST0pFVCA2IC0gUEVZUk9OTkUgRGF2aWQiCm91dHB1dDoKICBodG1sX25vdGVib29rOiBkZWZhdWx0CiAgcGRmX2RvY3VtZW50OiAKICAgIHRvYzogeWVzCiAgICBsYXRleF9lbmdpbmU6IGx1YWxhdGV4CiAgaHRtbF9kb2N1bWVudDoKICAgIGRmX3ByaW50OiBwYWdlZAotLS0KCiMjIyAtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tCiMjIyBSYXBwZWwgZHUgcHJvamV0ClZvdHJlIHNvY2nDqXTDqSBkZSBjb25zdWx0aW5nIGluZm9ybWF0aXF1ZSB2b3VzIHByb3Bvc2UgdW5lIG5vdXZlbGxlIG1pc3Npb24gYXUgbWluaXN0w6hyZSBkZSBsJ0ludMOpcmlldXIsIGRhbnMgbGUgY2FkcmUgZGUgbGEgbHV0dGUgY29udHJlIGxhIGNyaW1pbmFsaXTDqSBvcmdhbmlzw6llLCDDoCBsJ09mZmljZSBjZW50cmFsIHBvdXIgbGEgcsOpcHJlc3Npb24gZHUgZmF1eCBtb25uYXlhZ2UuIFZvdHJlIG1pc3Npb24gc2kgdm91cyBsJ2FjY2VwdGV6IDogY3LDqWVyIHVuIGFsZ29yaXRobWUgZGUgZMOpdGVjdGlvbiBkZSBmYXV4IGJpbGxldHMuCiMjIyAtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tCgojIE1pc3Npb24gMApBZmluIGQnaW50cm9kdWlyZSB2b3RyZSBhbmFseXNlLCBlZmZlY3R1ZXogdW5lIGJyw6h2ZSBkZXNjcmlwdGlvbiBkZXMgZG9ubsOpZXMgKGFuYWx5c2VzIHVuaXZhcmnDqWVzIGV0IGJpdmFyacOpZXMpLgoKIyBNaXNzaW9uIDEKVm91cyByw6lhbGlzZXJleiB1bmUgYW5hbHlzZSBlbiBjb21wb3NhbnRlcyBwcmluY2lwYWxlcyBkZSBsJ8OpY2hhbnRpbGxvbiwgZW4gc3VpdmFudCB0b3V0ZXMgY2VzIMOpdGFwZXMgOgoKLSBhbmFseXNlIGRlIGwnw6lib3VsaXMgZGVzIHZhbGV1cnMgcHJvcHJlcyA7Ci0gcmVwcsOpc2VudGF0aW9uIGRlcyB2YXJpYWJsZXMgcGFyIGxlIGNlcmNsZSBkZXMgY29ycsOpbGF0aW9ucyA7Ci0gcmVwcsOpc2VudGF0aW9uIGRlcyBpbmRpdmlkdXMgcGFyIGxlcyBwbGFucyBmYWN0b3JpZWxzIDsKLSBhbmFseXNlciBkZSBsYSBxdWFsaXTDqSBkZSByZXByw6lzZW50YXRpb24gZXQgbGEgY29udHJpYnV0aW9uIGRlcyBpbmRpdmlkdXMuCgpQb3VyIGNoYWN1bmUgZGUgY2VzIMOpdGFwZXMsIGNvbW1lbnRleiBsZXMgcsOpc3VsdGF0cyBvYnRlbnVzLiBMYSB2YXJpYWJsZSBkb25uYW50IGxhIG5hdHVyZSBWcmFpL0ZhdXggZHUgYmlsbGV0IHNlcmEgdXRpbGlzw6llIGNvbW1lIHZhcmlhYmxlIGlsbHVzdHJhdGl2ZS4KCiMgTWlzc2lvbiAyCkFwcGxpcXVleiB1biBhbGdvcml0aG1lIGRlIGNsYXNzaWZpY2F0aW9uLCBwdWlzIGFuYWx5c2V6IGxlIHLDqXN1bHRhdCBvYnRlbnUuClZpc3VhbGlzZXogbGEgcGFydGl0aW9uIG9idGVudWUgZGFucyBsZSBwcmVtaWVyIHBsYW4gZmFjdG9yaWVsIGRlIGwnQUNQLCBwdWlzIGFuYWx5c2V6LWxhLgoKIyBNaXNzaW9uIDMKTW9kw6lsaXNleiBsZXMgZG9ubsOpZXMgw6AgbCdhaWRlIGQndW5lIHLDqWdyZXNzaW9uIGxvZ2lzdGlxdWUuIEdyw6JjZSDDoCBjZWxsZS1jaSwgdm91cyBjcsOpZXJleiB1biBwcm9ncmFtbWUgY2FwYWJsZSBkJ2VmZmVjdHVlciB1bmUgcHLDqWRpY3Rpb24gc3VyIHVuIGJpbGxldCwgYydlc3Qtw6AtZGlyZSBkZSBkw6l0ZXJtaW5lciBzJ2lsIHMnYWdpdCBkJ3VuIHZyYWkgb3UgZCd1biBmYXV4IGJpbGxldC4gUG91ciBjaGFxdWUgYmlsbGV0LCB2b3RyZSBhbGdvcml0aG1lIGRlIGNsYXNzaWZpY2F0aW9uIGRldnJhIGRvbm5lciBsYSBwcm9iYWJpbGl0w6kgcXVlIGxlIGJpbGxldCBzb2l0IHZyYWkuIFNpIGNldHRlIHByb2JhYmlsaXTDqSBlc3Qgc3Vww6lyaWV1cmUgb3Ugw6lnYWxlIMOgIDAuNSwgbGUgYmlsbGV0IHNlcmEgY29uc2lkw6lyw6kgY29tbWUgdnJhaS4gRGFucyBsZSBjYXMgY29udHJhaXJlLCBpbCBzZXJhIGNvbnNpZMOpcsOpIGNvbW1lIGZhdXguCgojICMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMKIyBNaXNzaW9uIDAKIyAjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjCgojIC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQojIDEgSW1wb3J0YXRpb24gZGVzIGRvbm7DqWVzCiMgLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tCgojIyMgMS4xIFNvdXJjZXMgZGVzIGRvbm7DqWVzCkpldSBkZSBkb25uw6llcyBjb250ZW5hbnQgbGVzIGNhcmFjdMOpcmlzdGlxdWVzIGfDqW9tw6l0cmlxdWVzIGRlIGJpbGxldHMgZGUgYmFucXVlLiBQb3VyIGNoYWN1biBkJ2V1eCwgbm91cyBjb25uYWlzc29ucyA6CgogICAgbGEgbG9uZ3VldXIgZHUgYmlsbGV0IChlbiBtbSkgOwogICAgbGEgaGF1dGV1ciBkdSBiaWxsZXQgKG1lc3Vyw6llIHN1ciBsZSBjw7R0w6kgZ2F1Y2hlLCBlbiBtbSkgOwogICAgTGEgaGF1dGV1ciBkdSBiaWxsZXQgKG1lc3Vyw6llIHN1ciBsZSBjw7R0w6kgZHJvaXQsIGVuIG1tKSA7CiAgICBsYSBtYXJnZSBlbnRyZSBsZSBib3JkIHN1cMOpcmlldXIgZHUgYmlsbGV0IGV0IGwnaW1hZ2UgZGUgY2VsdWktY2kgKGVuIG1tKSA7CiAgICBsYSBtYXJnZSBlbnRyZSBsZSBib3JkIGluZsOpcmlldXIgZHUgYmlsbGV0IGV0IGwnaW1hZ2UgZGUgY2VsdWktY2kgKGVuIG1tKSA7CiAgICBsYSBkaWFnb25hbGUgZHUgYmlsbGV0IChlbiBtbSkuCgojIyMgMS4yIEluc3RhbGxhdGlvbiBkZXMgcGFja2FnZXMgCiotPiBMJ2luc3RhbGxhdGlvbiBkZXMgcGFja2FnZXMgbmUgc2UgZmFpdCBxdSd1bmUgc2V1bGUgZm9pcywgamUgbGVzIGFpIGNvbW1lbnTDqWVzIHBvdXIgw6l2aXRlciBkZSByw6lpc250YWxsZXIgw6AgY2hhcXVlIGNoYXJnZW1lbnQgZHUgc2NyaXB0ICoKYGBge3IgSW5zdGFsbGF0aW9uIGRlcyBwYWNrYWdlcywgZWNobz1UUlVFfQojaW5zdGFsbC5wYWNrYWdlcygiRmFjdG9NaW5lUiIsIGRlcGVuZGVuZGNpZXM9VCkKI2luc3RhbGwucGFja2FnZXMoImZhY3RvZXh0cmEiLGRlcGVuZGVuY2llcz1UKQojaW5zdGFsbC5wYWNrYWdlcyhjKCJGYWN0b3NoaW55IikpCiNpbnN0YWxsLnBhY2thZ2VzKCJnZ3B1YnIiKQpgYGAKIyMjIDEuMyBJbnN0YWxsYXRpb24gZGVzIGxpYnJhcmllcwoqLT4gTCdhY3RpdmF0aW9uIGRlcyBsaWJyYWlyaWVzIHNlIGZhaXQgw6AgY2hhcXVlIGTDqW1hcnJhZ2UgZHUgc2NyaXB0KiAgCiotPiBjb3JycGxvdCBkb2l0IMOqdHJlIGFwcGVsw6kgYXByw6hzIGdncGxvdDIgcG91ciDDqXZpdGVyIHVuIHdhcm5pbmcqCmBgYHtyIEluc3RhbGxhdGlvbiBkZXMgbGlicmFyaWVzLCByZXN1bHRzPUZBTFNFfQpsaWJyYXJ5KGdncGxvdDIpCmxpYnJhcnkoRmFjdG9NaW5lUikKbGlicmFyeShmYWN0b2V4dHJhKQpsaWJyYXJ5KGdncHVicikKbGlicmFyeShkcGx5cikKbGlicmFyeShjb3JycGxvdCkgCm9wdGlvbnMoZ2dyZXBlbC5tYXgub3ZlcmxhcHMgPSBJbmYpCnRoZW1lX3VwZGF0ZSgKICAgICAgcGxvdC50aXRsZSA9IGVsZW1lbnRfdGV4dChjb2xvcj0iIzM4NzZDMiIsIHNpemU9MTgsIGZhY2U9ImJvbGQiLGhqdXN0ID0gMC41KSwKICAgICAgYXhpcy50aXRsZS54ID0gZWxlbWVudF90ZXh0KGNvbG9yPSJibGFjayIsIHNpemU9MTQsIGZhY2U9ImJvbGQiLGhqdXN0ID0gMC41KSwKICAgICAgYXhpcy50aXRsZS55ID0gZWxlbWVudF90ZXh0KGNvbG9yPSJibGFjayIsIHNpemU9MTQsIGZhY2U9ImJvbGQiLHZqdXN0ID0gMC41KSwKICAgICAgKQpgYGAKIyMjIDEuNCBDb25maWd1cmF0aW9uIGRlcyB2YXJpYWJsZXMgcG91ciBjaGFxdWUgZmljaGllcgoqLT4gZMOpZmluaXRpb24gZCd1bmUgdmFyaWFibGUgcsOpcGVydG9pcmUgcG91ciBmYWNpbGl0ZXIgbGUgY2hhbmdlbWVudCBkJ09TICoKYGBge3IgSW1wb3J0YXRpb24gZGVzIGRvbm7DqWVzfQpyZXBlcnRvaXJlX3NvdXJjZXMgPC0gIi9ob21lL3VzZXIvRG9jdW1lbnRzL2Zvcm1hdGlvbnMvb2MvRm9ybWF0aW9uIEFuYWx5c3QvUDYvc291cmNlcy8iCnJlcGVydG9pcmVfaW1hZ2VzIDwtICIvaG9tZS91c2VyL0RvY3VtZW50cy9mb3JtYXRpb25zL29jL0Zvcm1hdGlvbiBBbmFseXN0L1A2L2ltYWdlcy8iCmZpY19ub3RlcyA8LSBwYXN0ZSAocmVwZXJ0b2lyZV9zb3VyY2VzLCJub3Rlcy5jc3YiLHNlcCA9ICIiKQpgYGAKIyMjIDEuNSBDcsOpYXRpb24gZGVzIERhdGEgRnJhbWUgcGFyIGxlY3R1cmUgZHUgZmljaGllcgoqLT4gbGVzIGNyaXTDqHJlcyBkJ2ltcG9ydGF0aW9uLCBzw6lwYXJhdGV1cnMsIHZpcmd1bGVzIGTDqWNpbWFsZXMsIGVudMOqdGUgZXRjLCBvbnQgw6l0w6lzIGTDqWZpbmlzIGFwcsOocyBvYnNlcnZhdGlvbiBkZXMgZmljaGllcnMgc291cmNlcyDDoCBsJ2FpZGUgZCd1bmUgw6lkaXRldXIgZGUgdGV4dGUgb3UgZCd1biB0YWJsZXVyKgpgYGB7ciBDcsOpYXRpb24gZGVzIERGIHBhciBsZWN0dXJlIGRlcyBmaWNoaWVyc30KZGZfbm90ZXMgPC0gcmVhZC5jc3YyKGZpY19ub3RlcyxoZWFkZXIgPSBUUlVFLCBzZXAgPSAiLCIsIGRlYyA9ICIuIikKYGBgCiMjIyAtPiBWw6lyaWZpY2F0aW9uIGQndW4gREYgY3LDqcOpIHBhciBpbXBvcnRhdGlvbgpgYGB7ciBDb250cm9sZX0KaGVhZChkZl9ub3RlcykKYGBgCiMjIyBERiBjb21wbGV0CmBgYHtyIERGIGNvbXBsZXR9CnN1bW1hcnkoZGZfbm90ZXMpCmBgYAojIC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0KIyBNaXNzaW9uIDAKIyAtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tCiMjIEFmaW4gZCdpbnRyb2R1aXJlIHZvdHJlIGFuYWx5c2UsIGVmZmVjdHVleiB1bmUgYnLDqHZlIGRlc2NyaXB0aW9uIGRlcyBkb25uw6llcyAoYW5hbHlzZXMgdW5pdmFyacOpZXMgZXQgYml2YXJpw6llcykuCgoKIyMgT2JzZXJ2YXRpb25zIDoKIyMjIC0gRGlhZ29uYWxlcyA6IC0gZGFucyBsZXMgZmF1eCBiaWxsZXRzIGNlcnRhaW5lcyBzb250IHN1cMOpcmlldXJlcyBhdSB2cmFpcwojIyMgLSBoYXV0ZXVyIGdhdWNoZSA6IC0gbCdhbXBpdHVkZSBkZSBsYSBoYXV0ZXVyIGRlcyBmYXV4IGJpbGxldHMgZXN0IGluZsOpcmlldXJlIGV0IGNvbXByaXNlIGRhbnMgY2VsbGUgZGVzIHZyYWlzLCBqZSBuZSBwZW5zZSBkb25jIHBhcyBxdWUgY2Ugc29pdCB1biBjcml0w6hyZSBpbnTDqXJlc3NhbnQKIyMjIC0gaGF1dGV1ciBkcm9pdGUgOiAtIGlkZW0KIyMjIC0gbWFyZ2UgaW5mw6lyaWV1cmUgOiAtIGxlcyBmYXV4IGJpbGxldHMgb250IHVuZSB2YWxldXIgc3Vww6lyaWV1cmUgYXV4IHZyYWlzCiMjIyAtIG1hcmdlIHN1cMOpcmlldXJlIDogLSBsZXMgZmF1eCBiaWxsZXRzIG9udCB1bmUgdmFsZXVyIHN1cMOpcmlldXJlIGF1eCB2cmFpcwojIyMgLSBsb25ndWV1ciA6IC0gbGVzIGZhdXggYmlsbGV0cyBzb250IHBsdXMgcGV0aXRzIHF1ZSBsZXMgdnJhaXMKCgoKIyMgREYgdnJhaXMgYmlsbGV0cwpgYGB7ciB2cmFpcyBiaWxsZXRzfQp2cmFpc19iaWxsZXRzIDwtIGZpbHRlcihkZl9ub3RlcywgaXNfZ2VudWluZSA9PSAnVHJ1ZScpCnN1bW1hcnkodnJhaXNfYmlsbGV0cykKYGBgCiMjIyBERiBGYXV4IGJpbGxldHMKYGBge3IgZmF1eCBiaWxsZXRzfQpmYXV4X2JpbGxldHMgPC0gZmlsdGVyKGRmX25vdGVzLCBpc19nZW51aW5lID09ICdGYWxzZScpCnN1bW1hcnkoZmF1eF9iaWxsZXRzKQpgYGAKCiMjIE5ldHRveWFnZSByZWNoZXJjaGUgZGUgdmFsZXVycyBhYnNlbnRlcwpgYGB7cn0Kc2FwcGx5KGRmX25vdGVzLCBmdW5jdGlvbih4KSBzdW0oaXMubmEoeCkpKQpgYGAKCgojIyMgQU5BTFlTRSA6ICAKbm91cyBhdm9ucyBkw6l0ZXJtaW7DqSBwcsOpY8OpZGVtZW50IHF1ZSBsYSB2YXJpYWJsZXMgZGlzcmNpbWluYXRvaXJlcyDDqXRhaWVudCBtYXJnaW5fbG93IGV0IGxlbmd0aC4gIApDZSBxdWkgZXN0IGNvbmZpcm3DqSBwYXIgbGEgcC12YWx1ZSBkdSB0ZXN0IGRlIHN0dWRlbnQgcG91ciBjZXMgZGV1eCB2YXJpYWJsZXMuIFNpIGwnb24gZmFpdCB1biB0ZXN0IHN1ciB1bmUgYXV0cmUgdmFyaWFibGUsIG9uIHJldHJvdXZlIHVuZSBwLXZhbHVlIHN1cMOpcmlldXJlIMOgIDUlLgoKIyMjIEFmZmljaGFnZSBkZXMgaGlzdG9ncmFtZXMgZGVzIHZhcmlhYmxlcyBwb3VyIHZvaXIgc2kgbCdvbiBwZXV0IHkgcmV0cm91dmVyIGxhIGxvaSBub3JtYWxlIGV0IHNpIGNlcnRhaW5lcyBzb250IHZyYWltZW50IGRpZmbDqXJlbnRlcwpgYGB7cn0KIyBtdWx0aXBsb3QgOiBQb3VyIGFmZmljaGVyIHBsdXNpZXVycyBncmFwaGlxdWVzIAoKbXVsdGlwbG90IDwtIGZ1bmN0aW9uKC4uLiwgcGxvdGxpc3Q9TlVMTCwgZmlsZSwgY29scz0xLCBsYXlvdXQ9TlVMTCkgewogIGxpYnJhcnkoZ3JpZCkKCiAgIyBNYWtlIGEgbGlzdCBmcm9tIHRoZSAuLi4gYXJndW1lbnRzIGFuZCBwbG90bGlzdAogIHBsb3RzIDwtIGMobGlzdCguLi4pLCBwbG90bGlzdCkKCiAgbnVtUGxvdHMgPSBsZW5ndGgocGxvdHMpCgogICMgSWYgbGF5b3V0IGlzIE5VTEwsIHRoZW4gdXNlICdjb2xzJyB0byBkZXRlcm1pbmUgbGF5b3V0CiAgaWYgKGlzLm51bGwobGF5b3V0KSkgewogICAgIyBNYWtlIHRoZSBwYW5lbAogICAgIyBuY29sOiBOdW1iZXIgb2YgY29sdW1ucyBvZiBwbG90cwogICAgIyBucm93OiBOdW1iZXIgb2Ygcm93cyBuZWVkZWQsIGNhbGN1bGF0ZWQgZnJvbSAjIG9mIGNvbHMKICAgIGxheW91dCA8LSBtYXRyaXgoc2VxKDEsIGNvbHMgKiBjZWlsaW5nKG51bVBsb3RzL2NvbHMpKSwKICAgICAgICAgICAgICAgICAgICBuY29sID0gY29scywgbnJvdyA9IGNlaWxpbmcobnVtUGxvdHMvY29scykpCiAgfQoKIGlmIChudW1QbG90cz09MSkgewogICAgcHJpbnQocGxvdHNbWzFdXSkKCiAgfSBlbHNlIHsKICAgICMgU2V0IHVwIHRoZSBwYWdlCiAgICBncmlkLm5ld3BhZ2UoKQogICAgcHVzaFZpZXdwb3J0KHZpZXdwb3J0KGxheW91dCA9IGdyaWQubGF5b3V0KG5yb3cobGF5b3V0KSwgbmNvbChsYXlvdXQpKSkpCgogICAgIyBNYWtlIGVhY2ggcGxvdCwgaW4gdGhlIGNvcnJlY3QgbG9jYXRpb24KICAgIGZvciAoaSBpbiAxOm51bVBsb3RzKSB7CiAgICAgICMgR2V0IHRoZSBpLGogbWF0cml4IHBvc2l0aW9ucyBvZiB0aGUgcmVnaW9ucyB0aGF0IGNvbnRhaW4gdGhpcyBzdWJwbG90CiAgICAgIG1hdGNoaWR4IDwtIGFzLmRhdGEuZnJhbWUod2hpY2gobGF5b3V0ID09IGksIGFyci5pbmQgPSBUUlVFKSkKCiAgICAgIHByaW50KHBsb3RzW1tpXV0sIHZwID0gdmlld3BvcnQobGF5b3V0LnBvcy5yb3cgPSBtYXRjaGlkeCRyb3csCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbGF5b3V0LnBvcy5jb2wgPSBtYXRjaGlkeCRjb2wpKQogICAgfQogIH0KfQpgYGAKCmBgYHtyfQpkYXRhIDwtIGRmX25vdGVzCnAxIDwtIGdncGxvdChkYXRhLCBhZXMoeD1kaWFnb25hbCwgZmlsbD1pc19nZW51aW5lLCBjb2xvcj1pc19nZW51aW5lKSkgKyAgZ2VvbV9kZW5zaXR5KGFscGhhPS4zKSAKcDIgPC0gZ2dwbG90KGRhdGEsIGFlcyh4PWhlaWdodF9sZWZ0LCBmaWxsPWlzX2dlbnVpbmUsIGNvbG9yPWlzX2dlbnVpbmUpKSArICBnZW9tX2RlbnNpdHkoYWxwaGE9LjMpIApwMyA8LSBnZ3Bsb3QoZGF0YSwgYWVzKHg9aGVpZ2h0X3JpZ2h0LCBmaWxsPWlzX2dlbnVpbmUsIGNvbG9yPWlzX2dlbnVpbmUpKSsgIGdlb21fZGVuc2l0eShhbHBoYT0uMykgCnA0IDwtIGdncGxvdChkYXRhLCBhZXMoeD1tYXJnaW5fbG93LCBmaWxsPWlzX2dlbnVpbmUsIGNvbG9yPWlzX2dlbnVpbmUpKSsgIGdlb21fZGVuc2l0eShhbHBoYT0uMykgCnA1IDwtIGdncGxvdChkYXRhLCBhZXMoeD1tYXJnaW5fdXAsIGZpbGw9aXNfZ2VudWluZSwgY29sb3I9aXNfZ2VudWluZSkpICsgIGdlb21fZGVuc2l0eShhbHBoYT0uMykgCnA2IDwtIGdncGxvdChkYXRhLCBhZXMoeD1sZW5ndGgsIGZpbGw9aXNfZ2VudWluZSwgY29sb3I9aXNfZ2VudWluZSkpICsgIGdlb21fZGVuc2l0eShhbHBoYT0uMykgCm11bHRpcGxvdChwMSwgcDIsIHAzLCBwNCxwNSxwNiwgY29scz0yKQpgYGAKCiMjIyByZW1wbGFjZXIgdHJ1ZSBwYXIgMSwgZmFsc2UgcGFyIDAKYGBge3J9CmRmX25vdGVzX3ZmPC1kZl9ub3RlcwpkZl9ub3Rlc192ZiRpc19nZW51aW5lW2RmX25vdGVzJGlzX2dlbnVpbmUgPT0gIlRydWUiXSA8LSAiMSIKZGZfbm90ZXNfdmYkaXNfZ2VudWluZVtkZl9ub3RlcyRpc19nZW51aW5lID09ICJGYWxzZSJdIDwtICIwIgpkZl9ub3Rlc192Zl9udW1lcmljIDwtIHRyYW5zZm9ybShkZl9ub3Rlc192ZiwgaXNfZ2VudWluZSA9IGFzLm51bWVyaWMoaXNfZ2VudWluZSkpCmRmX25vdGVzX3ZmX251bWVyaWMKYGBgCgojIyMgSGVhdG1hcCBkZXMgdmFyaWFibGVzIGRhbnMgY2hhcXVlIGNsdXN0ZXIgcG91ciB2b2lyIGxldXIgaW1wb3J0YW5jZQpgYGB7cn0KIyBjb3JycGxvdChNYXRyaWNlLCAgbWV0aG9kID0gImNpcmNsZSIpCm1hdHJpY2VfY29yIDwtIGNvcihkZl9ub3Rlc192Zl9udW1lcmljWywyOjddKQpjb3JycGxvdChtYXRyaWNlX2NvciwgdHlwZT0idXBwZXIiLCBzaWcubGV2ZWwgPSAwLjAxKQpgYGAKCgojICMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMKIyBNaXNzaW9uIDEKIyAjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjCgojIC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0KIyBBQ1AgLSBBbmFseXNlIGVuIGNvbXBvc2FudGVzIHByaW5jaXBhbGVzCiMgLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQoKIyMjIFRhYmxlIGNlbnRyw6kgcsOpZHVpdGUgCmBgYHtyfQpkZl9ub3Rlc192Zl9zY2FsZWQgPC0gZGF0YS5mcmFtZShkZl9ub3Rlc1snaXNfZ2VudWluZSddLCBzY2FsZShkZl9ub3Rlc1ssMjo3XSkpCmBgYAojIyMgQUNQCmBgYHtyfQpyZXMucGNhIDwtIFBDQShkZl9ub3Rlc192Zl9zY2FsZWQsIHNjYWxlLnVuaXQgPSBGQUxTRSwgIHF1YWxpLnN1cD0xLCBncmFwaCA9IEZBTFNFKQpgYGAKCiMjIEVib3VsaXMgZGVzIHZhbGV1cnMgcHJvcHJlcyA6IHBvdXJjZW50YWdlIGRlIHZhcmlhbmNlIGV4cGxpcXXDqWUgcGFyIGNoYXF1ZSBkaW1lbnNpb24KYGBge3J9CmVpZy52YWwgPC0gZ2V0X2VpZ2VudmFsdWUocmVzLnBjYSkgCmZ2aXpfZWlnKHJlcy5wY2EsIGFkZGxhYmVscyA9IFRSVUUsIHlsaW0gPSBjKDAsIDc1KSkKYGBgCgotPiBPbiBvYnNlcnZlIHVuIGNvdWRlIGF1IG5pdmVhdSBkZSBsYSBzZWNvbmRlIGRpbWVuc2lvbi4gQXByw6hzIHF1b2kgbGEgZGltaW51dGlvbiBlc3QgcGx1cyByw6lndWxpw6hyZS4gICAKTWFpcyBpbCBzZXJhIHBldXQtw6p0cmUgbsOpY2Vzc2FpcmUgZCdvYnNlcnZlciBsZXMgMyBwcmVtacOocmVzIGNvbXBvc2FudGVzIHByaW5jaXBhbGVzIHBvdXIgdG90YWxpc2VyIDcwJSBkZXMgaW5mb3JtYXRpb25zLgoKIyMjIENvbXBhcmFpc29uIGRlIGxhIHF1YWxpdMOpIGRlIGxhIHJlcHLDqXNlbnRhdGlvbiBkZXMgdmFyaWFibGVzIGVuIGZvbmN0aW9uIGRlcyBkaW1lbnNpb25zCmBgYHtyfQp2YXIgPC0gZ2V0X3BjYV92YXIocmVzLnBjYSkKY29ycnBsb3QodmFyJGNvczIsIGlzLmNvcnI9RkFMU0UsYWRkQ29lZi5jb2wgPSAiYmxhY2siLCB0aXRsZT0iUXVhbGl0w6kgZGUgcmVwcsOpc2VudGF0aW9uIGRlcyB2YXJpYWJsZXMiLCBtYXI9YygwLDAsMSwwKSkKYGBgCi0+IGRhbnMgbGEgcHJlbWnDqHJlIGNvbXBvc2FudGUsIG1hcmdpbl9sb3cgZXQgbGVuZ3RoIHNvbnQgbGUgbWlldXggcmVwcsOpc2VudMOpZXMgIAotPiBkYW5zIGxhIHNlY29uZGUsIG1hcmdpbl9sb3cgZXN0IGVuY29yZSB1biBwZXUgcmVwcsOpc2VudMOpZS4gIAotPiBkYW5zIGxhIHRyb2lzacOobWUsIGMnZXN0IGxhIGRpc3RhbmNlIHF1aSBlc3QgbGUgbWlldXggcmVwcsOpc2VudMOpZSAgCiMjIENPTkNMVVNJT04gOiBub3VzIHBvdXZvbnMgdHJhdmFpbGxlciBzdXIgbGUgcGxhbiBmYWN0b3JpZWwgMS0zIGV0IDEtMiBub3VzIHZlcnJvbnMgYXZlYyBkJ2F1dHJlcyBhbmFseXNlcwoKCiMjIyBHcmFwaGlxdWUgZGVzIGNvcnLDqWxhdGlvbnMgcG91ciBsZXMgZGltZW5zaW9ucyAxIGV0IDIKYGBge3J9CmltZ19ncmFwaGVfY29ycmVsYXRpb248LWZ2aXpfcGNhX3ZhcihyZXMucGNhLCBjb2wudmFyID0gImNvczIiLCBheGVzID0gYygxLDIpLAogICAgICAgICAgICAgIGdyYWRpZW50LmNvbHMgPSBjKCIjMDBBRkJCIiwgIiNFN0I4MDAiLCAiI0ZDNEUwNyIpLCAKICAgICAgICAgICAgICByZXBlbCA9IFRSVUUgIyDDiXZpdGUgbGUgY2hldmF1Y2hlbWVudCBkZSB0ZXh0ZQopICsgZ2dzYXZlKCJpbWdfZ3JhcGhlX2NvcnJlbGF0aW9uLnBuZyIsIHdpZHRoID0gMTEsIGhlaWdodCA9IDgpCnBsb3QoaW1nX2dyYXBoZV9jb3JyZWxhdGlvbikKYGBgCgojIyMgR3JhcGhpcXVlIGRlcyBjb3Jyw6lsYXRpb25zIHBvdXIgbGVzIGRpbWVuc2lvbnMgMSBldCAzCmBgYHtyfQppbWdfZ3JhcGhlX2NvcnJlbGF0aW9uXzFfMzwtZnZpel9wY2FfdmFyKHJlcy5wY2EsIGNvbC52YXIgPSAiY29zMiIsIGF4ZXMgPSBjKDEsMyksCiAgICAgICAgICAgICAgZ3JhZGllbnQuY29scyA9IGMoIiMwMEFGQkIiLCAiI0U3QjgwMCIsICIjRkM0RTA3IiksCiAgICAgICAgICAgICAgcmVwZWwgPSBUUlVFICMgw4l2aXRlIGxlIGNoZXZhdWNoZW1lbnQgZGUgdGV4dGUKKSArIGdnc2F2ZSgiaW1nX2dyYXBoZV9jb3JyZWxhdGlvbl8xXzMucG5nIiwgd2lkdGggPSAxMSwgaGVpZ2h0ID0gOCkKcGxvdChpbWdfZ3JhcGhlX2NvcnJlbGF0aW9uXzFfMykKYGBgCgpMZSBjZXJjbGUgZGVzIGNvcnLDqWxhdGlvbnMgYWlkZSDDoCBpbnRlcnByw6l0ZXIgbGVzIGF4ZXMgZCdpbmVydGllLgoKIyMgUmVwcsOpc2VudGF0aW9uIGRlcyBpbmRpdmlkdXMKYGBge3J9CmZ2aXpfcGNhX2JpcGxvdChyZXMucGNhLCBoYWJpbGxhZ2UgPSAxLCBhZGRFbGxpcHNlcyA9VFJVRSwgICBwb2ludHNpemUgPSAxLCBsYWJlbHNpemUgPSAzLCBheGVzID0gYygxLDIpICkKYGBgCiMjIEFOQUxZU0UgZGVzIGNlcmNsZSBkZXMgY29ycsOpbGF0aW9ucwojIyMgcGxhbiBmYWN0b3JpZWwgMS0yCiMjIyBJbCBwZXJtZXQgZGUgdm9pciBhdXNzaSBxdWUgaXNfZ2VudWluZSBldCBsZW5ndGggc29udCBmb3J0ZW1lbnQgY29ycsOpbMOpZXMgYWluc2kgcXVlIG1hcmdpbl9sb3cKCiMjIyBwbGFuIGZhY3RvcmllbCAxLTMKIyMjIFNpIGwnb24gcGVydCB1biBwZXUgZGUgYWwgcXVhbGl0w6kgZGUgcmVwcsOpc2VudGF0aW9uIGRlIGxlbmd0aCwgZW4gcmV2YW5jaGUgb24gb2JzZXJ2ZSB1bmUgdHJvaXNpw6htZSB2YXJpYWJsZSBtYXJnaW5fdXAgcXVpIGRldnJhaXQgcGVybWV0dHJlIGQnYWZmaW5lciBsJ2FuYWx5c2UgZGVzIGRvbm7DqWVzLiAgCgoKIyMgQU5BTFlTRQojIyMgTCfDqWJvdWxpcyBkZXMgdmFsZXVycyBwcm9wcmVzIG1vbnRyZSBxdWUgbGVzIGRldXggcHJlbWnDqHJlcyBkaW1lbnNpb25zIGV4cGxpcXVlbnQgNzIlIGRlIGwnaW5lcnRpZSBkZXMgdmFyaWFibGVzLiBDZSBxdWkgc2VtYmxlIHN1ZmZpc2FudCBtYWlzIGF2ZWMgbGEgaGVhdG1hcCwgbGEgZGltZW5zaW9uIDMgZXN0IHBsdXMgc2lnbmlmaWNhdGl2ZSBxdWUgbGEgMgoKCgoKIyAjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjCiMgTWlzc2lvbiAyIC0gQ2xhc3NpZmljYXRpb24KIyAjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjCgpDbGFzc2lmaWNhdGlvbiBBc2NlbmRhbnRlIEhpw6lyYXJjaGlxdWUgMiBDbHVzdGVycyAKTGEgQ0FIIHZhIGTDqXRlcm1pbmVyIHNpIHVuIGJpbGxldCBlc3QgdnJhaSBvdSBmYXV4IGVuIGZvbmN0aW9uIGRlcyB2YXJpYWJsZXMKCiMjIyBIQ1BDIHBhciByYXBwb3J0IMOgIGwnQUNQIHLDqXBhcnRpdGlvbiBlbiBkZXV4IGNsdXN0ZXJzCmBgYHtyfQpyZXMuaGNwYyA8LSBIQ1BDKHJlcy5wY2EsIG5iLmNsdXN0PTIsIGdyYXBoID0gRkFMU0UpICMgCmhlYWQocmVzLmhjcGMpCgoKYGBgCiMjIyBEZW5kb2dyYW1tZQoKYGBge3J9CmltZ19kZW5kcm9ncmFtbWUgPC0gZnZpel9kZW5kKHJlcy5oY3BjLAogICAgICAgICAgbGFiZWxzaXplID0gMiwKICAgICAgICAgIGNleCA9IDAuNSwgICAgICAgICAgICAgICAgICAgICAjIFRhaWxsZSBkdSB0ZXh0CiAgICAgICAgICBwYWxldHRlID0gImpjbyIsICAgICAgICAgICAgICAgIyBQYWxldHRlIGRlIGNvdWxldXIgP2dncHVicjo6Z2dwYXIKICAgICAgICAgIHJlY3QgPSBUUlVFLCByZWN0X2ZpbGwgPSBUUlVFLCAjIFJlY3RhbmdsZSBhdXRvdXIgZGVzIGdyb3VwZXMKICAgICAgICAgIHJlY3RfYm9yZGVyID0gImpjbyIsICAgICAgICAgICAjIENvdWxldXIgZHUgcmVjdGFuZ2xlCiAgICAgICAgICBsYWJlbHNfdHJhY2tfaGVpZ2h0ID0gMC44ICAgICAgIyBBdWdtZW50IGwnZXNwYWNlIHBvdXIgbGUgdGV4dGUKICAgICAgICAgICkgCnBsb3QoaW1nX2RlbmRyb2dyYW1tZSkKYGBgCgojIyMgT2JzZXJ2YXRpb24gZGVzIGNsdXN0ZXJzCmBgYHtyfQppbWdfZmFjdG9yX21hcCA8LSBmdml6X2NsdXN0ZXIocmVzLmhjcGMsCiAgICAgICAgICAgICBsYWJlbHNpemUgPSA1LAogICAgICAgICAgICAgcmVwZWwgPSBGQUxTRSwgICAgICAgICAgICAjIEV2aXRlIGxlIGNoZXZhdWNoZW1lbnQgZGVzIHRleHRlcwogICAgICAgICAgICAgc2hvdy5jbHVzdC5jZW50ID0gVFJVRSwgIyBNb250cmUgbGUgY2VudHJlIGRlcyBjbHVzdGVycwogICAgICAgICAgICAgcGFsZXR0ZSA9ICJqY28iLCAgICAgICAgICMgUGFsZXR0ZSBkZSBjb3VsZXVycywgdm9pciA/Z2dwdWJyOjpnZ3BhcgogICAgICAgICAgICAgZ2d0aGVtZSA9IHRoZW1lX21pbmltYWwoKSwKICAgICAgICAgICAgIG1haW4gPSAiRmFjdG9yIG1hcCIKKSArIGdnc2F2ZSgiUDZfaW1nX2ZhY3Rvcm1hcC5wbmciLCB3aWR0aCA9IDExLCBoZWlnaHQgPSA4KQpwbG90KGltZ19mYWN0b3JfbWFwKQpgYGAKCmBgYHtyfQpkZl9iaWxsZXRzX2NsdXN0PC0gIHJlcy5oY3BjJGRhdGEuY2x1c3QKZGZfYmlsbGV0c19jbHVzdCA8LSB0cmFuc2Zvcm0oZGZfYmlsbGV0c19jbHVzdCwgY2x1c3QgPSBhcy5udW1lcmljKGNsdXN0KSkgCgoKIyBMZSBjbHVzdGVyIDEgY29ycmVzcG9uZCBhdXggdnJhaXMgYmlsbGV0cy4KZGZfYmlsbGV0c19jbHVzdCRjbHVzdFtkZl9iaWxsZXRzX2NsdXN0JGNsdXN0PT0nMSddPC0nVHJ1ZScKZGZfYmlsbGV0c19jbHVzdCRjbHVzdFtkZl9iaWxsZXRzX2NsdXN0JGNsdXN0PT0nMiddPC0nRmFsc2UnCgojIExlIGNsdXN0ZXIgMiBjb3JyZXNwb25kIGF1eCBmYXV4IGJpbGxldHMuCiNiaWxsZXRzLmhjJGdyb3VwZXMuaGNbYmlsbGV0cy5oYyRncm91cGVzLmhjPT0nMiddPC0nRmFsc2UnCiNiaWxsZXRzLmhjIDwtIHRyYW5zZm9ybShiaWxsZXRzLmhjLCBncm91cGVzLmhjID0gYXMuZmFjdG9yKGdyb3VwZXMuaGMpKSAKCiMgTWF0cmljZSBkZSBjb25mdXNpb24gw6AgcGFydGlyIGRvbm7DqWVzIHLDqWVsbGVzIGV0IGRlcyBkb25uw6llcyBwcsOpZGl0ZXMgcGFyIGwnQUNICnNldC5zZWVkKDEyMykKbWNfY2x1c3QgPSB0YWJsZShkZl9iaWxsZXRzX2NsdXN0JGlzX2dlbnVpbmUsZGZfYmlsbGV0c19jbHVzdCRjbHVzdCkKbWNfY2x1c3QKCmBgYAoKCiMgLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQojIEZJTiBjYWggc3VyIGNlbnRyw6kgcsOpZHVpdAojIC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0KCiMjIENBSCBjZW50csOpIHLDqWR1aXQgTUFUUklDRSBERSBDT05GVVNJT04KYGBge3J9CiNyZXNrbWVhbnNDUiRjbHVzdGVyCiNkZl9ub3Rlc192Zl9udW1lcmljJGlzX2dlbnVpbmUKc2V0LnNlZWQoMTIzKQpNQyA9IHRhYmxlKCBkZl9ub3Rlc192Zl9udW1lcmljJGlzX2dlbnVpbmUsIHJlcy5oY3BjJGRhdGEuY2x1c3RbLDhdKSAKTUMKYGBgCiMjIENPTkNMVVNJT04KIyMjIExlIGJveHBsb3QgbW9udHJlIHF1ZSBkZXV4IHZhcmlhYmxlcyBzb250IHBhcnRpY3VsacOocmVtZW50IGRpc2NyaW1pbmF0b2lyZXMgOiBsZW5ndGggZXQgbWFyZ2luIGxvdyAgCiMjIyBsZSBjZXJjbGUgZGVzIGNvcnLDqWxhdGlvbnMgbW9udHJlIG5ldHRlbWVudCBkZXV4IHZhcmlhYmxlcyBjb3Jyw6lsw6llcyBxdWkgc29udCBsZW5ndGggZXQgaXNfZ2VudWluZS4gCk5vdXMgY29uc2lkw6lyb25zIGxhIHZhcmlhYmxlIGlzX2dlbnVpbmUgY29tbWUgaWxsdXN0cmF0aXZlIHB1aXNxdSdlbGxlIG5lIHBhcnRpY2lwZSBwYXMgw6AgbCdhbmFseXNlLCBtYWlzIGVsbGUgcGVybWV0IGRlIGwnZXhwbGlxdWVyLiAKIyMjIExhIHZhcmlhYmxlIG1hcmdpbiBsb3cgZXN0IGF1c3NpIGZvcnRlbWVudCBjb3Jyw6lsw6llIMOgIGxlbmd0aCBldCBpc19nZW51aW5lLgojIyMgTGUgQmlwbG90IGF2ZWMgbGVzIHZhcmlhYmxlcyBldCBsZXMgaW5kaXZpZHVzIG1vbnRyZSBkZXV4IGVuc2VtYmxlcyBiaWVuIHPDqXBhcsOpcyBlbiBmb25jdGlvbiBkZSBsZW5ndGggZXQgbWFyZ2luX2xvdy4gUGx1cyBsJ2luZGl2aWR1IHZhIHZlcnMgbGUgbGVuZ3RoLCBwbHVzIG9uIGEgdW4gdnJhaSBiaWxsZXQuIEV0IHBsdXMgbCdpbmRpdmlkdSBzJ29yaWVudGUgdmVycyBtYXJnaW5fbG93LCBwbHVzIG9uIGVzdCBzw7tyIGQnYXZvaXIgdW4gZmF1eCBiaWxsZXQuCgojIyMgVmFyaWFibGUgaWxsdXN0cmF0aXZlIGlzX2dlbnVpbmUgcXVhbGl0YXRpdmUKYGBge3J9CmRmX25vdGVzX3ZmX251bWVyaWNbLDE6N10KcmVzLnBjYSA9IFBDQShkZl9ub3Rlc192Zl9udW1lcmljWywxOjddLCBzY2FsZS51bml0PVRSVUUsYXhlcyA9IGMoMSwzKSwgZ3JhcGg9VCwgcXVhbGkuc3VwID0gMSkgCmBgYAoKCiMjIGFuYWx5c2UgOiAKIyMjIDEwMSBpbmRpdmlkdXMgZGFucyBsZSBwcmVtaWVyIGdyb3VwZSBldCA2OSBkYW5zIGxlIHNlY29uZAojIyMgd2l0aGluIGNsdXN0ZXIgc3VtIC4uLiBxdWFsaXTDqSBkdSBwYXJ0aXRpb25uZW1lbnQKLSBpbmVydGllIGludHJhY2xhc3NlIGRhbnMgbGVzIGRldXggZ3JvdXBlcwotIHJhdGlvIGVudHJlIGwnaW5lcnRpZSBleHBsaXF1w6llIHBhciBsZSBwYXJ0aXRpb25uZW1lbnQgLyBpbmVydGllIHRvdGFsZSAgaW5kaXF1ZSB1bmUgYm9ubmUgcXVhbGl0w6kgZGUgcsOpcGFydGl0aW9uCgoKIyMga21lYW5zIHN1ciBjZW50csOpIHLDqWR1aXQgCmBgYHtyfQpyZXNrbWVhbnM8LSBrbWVhbnMoZGZfbm90ZXNfdmZfbnVtZXJpY1ssMjo3XSxjZW50ZXJzID0gMiwgbnN0YXJ0PTUpCmBgYAoKIyMgQ09OQ0xVU0lPTiAKTGEgcmVwcsOpc2VudGF0aW9uIHBhciBsZSBLLW1lYW5zIHN1ciBsYSBkYXRhZnJhbWUgY2VudHLDqSByw6lkdWl0ZSBlc3QgZGUgbW9pbnMgYm9ubmUgcXVhbGl0w6kgc2kgbCdvbiBvYnNlcnZlIGxlIHJhdGlvIGVudHJlIGwnaW50ZXJ0aWUgZXhsaXF1w6llIHBhciBsZSBwYXJ0aXRpb25uZW1lbnQgZXQgbCdpbm5lcnRpZSB0b3RhbGUKCiMgTUFUUklDRVMgREUgQ09ORlVTSU9OCgojIyBLLW1lYW5zIGNlbnRyw6kgcsOpZHVpdCAgbWF0cmljZSBkZSBjb25mdXNpb24KYGBge3J9CiNyZXNrbWVhbnMkY2x1c3RlcgojZGZfbm90ZXNfdmZfbnVtZXJpYyRpc19nZW51aW5lCnNldC5zZWVkKDEyMykKTUMgPSB0YWJsZSggZGZfbm90ZXNfdmZfbnVtZXJpYyRpc19nZW51aW5lLCByZXNrbWVhbnMkY2x1c3RlcikgCk1DCmBgYAojIyBDT05DTFVTSU9OIApPbiBjb25zdGF0ZSBxdWUgbGEgQ0FIIGRvbm5lIHVuIG1laWxsZXVyIHLDqXN1bHRhdCBxdWUgbGVzIEttZWFucwoKCiMgIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIwojIE1pc3Npb24gMyAtIE1vZMOpbGlzYXRpb24KIyAjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjCgojIC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tCiMgQ0FSRVQgbW9kw6lsaXNhdGlvbiBDbGFzc2lmaWNhdGlvbiBBbmQgUkVncmVzc2lvbiBUcmFpbmluZwojIC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tCgojIHBvdXIgZGVzIHF1ZXN0aW9ucyBwcmF0aXF1ZXMgbm91cyByZW5vbW9ucyBsZSBkZiBlbiBkYXRhIGV0IGxhbmNlcm9ucyBkZXMgdGVzdHMgw6AgcGFydGlyIGRlIGRmIENSIG91IGJydXRzCmBgYHtyfQojZGF0YTwtZGZfbm90ZXNfdmZfbnVtZXJpYwpkYXRhPC1kZl9ub3RlcwpgYGAKCiMgaW5zdGFsbGF0aW9uIHBhY2thZ2UgQ0FSRVQKIyBjaGFyZ2VtZW50IGRlIGxhIGxpYnJhaXJpZQpgYGB7cn0KI2luc3RhbGwucGFja2FnZXMoImNhcmV0IikKI2luc3RhbGwucGFja2FnZXMoImUxMDcxIikKbGlicmFyeShjYXJldCkKbGlicmFyeShsYXR0aWNlKQpsaWJyYXJ5KGUxMDcxKQoKcHJpbnQoZGltKGRhdGEpKQpoZWFkKGRhdGEpCm5yb3coZGF0YSkKCmRhdGEgPC0gdHJhbnNmb3JtKGRhdGEsIGlzX2dlbnVpbmUgPSBhcy5jaGFyYWN0ZXIoaXNfZ2VudWluZSkpIAoKIyBMZSBjbHVzdGVyIDEgY29ycmVzcG9uZCBhdXggdnJhaXMgYmlsbGV0cy4KZGF0YSRpc19nZW51aW5lW2RhdGEkaXNfZ2VudWluZT09J1RydWUnXTwtMQoKIyBMZSBjbHVzdGVyIDIgY29ycmVzcG9uZCBhdXggZmF1eCBiaWxsZXRzLgpkYXRhJGlzX2dlbnVpbmVbZGF0YSRpc19nZW51aW5lPT0nRmFsc2UnXTwtMAoKZGF0YSA8LSB0cmFuc2Zvcm0oZGF0YSwgaXNfZ2VudWluZSA9IGFzLm51bWVyaWMoaXNfZ2VudWluZSkpIAoKaGVhZChkYXRhKQpgYGAKCiMgdW5lIGVycmV1ciBkdWUgYXUgZmFpdCBxdWUgbCdvbiB2b3VsYWl0IGZhaXJlIHVuZSBjbGFzc2lmaWNhdGlvbiBkYW5zIGxlIGNhcyBkJ3VuIHRlc3QgZW4gZ2xtIGV0IG5vbiBnbG1TdGVwQWljCmBgYHtyfQojZGF0YSRpc19nZW51aW5lPSBhcy5mYWN0b3IoZGF0YSRpc19nZW51aW5lKQpgYGAKCiMjIHPDqXBhcmF0aW9uIGRlcyBkb25uw6llcyBlbiBkb25uw6llcyB0ZXN0IGV0IGRvbm7DqWVzIGFwcHJlbnRpc3NhZ2UKYGBge3J9CnNldC5zZWVkKDEyMykKdHJhaW5JbmRleCA8LWNyZWF0ZURhdGFQYXJ0aXRpb24oZGF0YSRpc19nZW51aW5lLHA9MC44LGxpc3Q9RikKcHJpbnQobGVuZ3RoKHRyYWluSW5kZXgpKQpgYGAKCiMxMCBwcmVtaWVycyBpbmRpdmlkdXMgZGUgbCfDqWNoYW50aWxsb24gZCdhcHByZW50aXNzYWdlCmBgYHtyfQpwcmludChoZWFkKHRyYWluSW5kZXgsMTApKQpgYGAKCiMgZGF0YVRyYWluCiNkYXRhIGZyYW1lIHBvdXIgbGVzIGluZGl2aWR1cyBlbiBhcHByZW50aXNzYWdlCmBgYHtyfQpkYXRhVHJhaW4gPC1kYXRhW3RyYWluSW5kZXgsXQpwcmludChkaW0oZGF0YVRyYWluKSkKYGBgCgojIyBkYXRhZnJhbWUgaW52ZXJzZSBkZXMgaW5kZXggcG91ciBsZXMgZG9ubsOpZXMgZCdhcHByZW50aXNzYWdlCmBgYHtyfQpkYXRhVGVzdCA8LWRhdGFbLXRyYWluSW5kZXgsXQpwcmludChkaW0oZGF0YVRlc3QpKQpwcmludCh0YWJsZShkYXRhVHJhaW4kaXNfZ2VudWluZSkpCmBgYAoKIyMgZGlzdHJpYnV0aW9uIGRlcyBjbGFzc2VzIGRhbnMgbGUgZmljaGllciBkJ29yaWdpbmUKYGBge3J9CnByaW50KHByb3AudGFibGUodGFibGUoZGF0YSRpc19nZW51aW5lKSkpCmBgYAoKIyMgZnLDqXF1ZW5jZXMgcmVsYXRpdmVzIGRlcyBjbGFzc2VzIGRhbnMgbCfDqWNoYW50aWxsb24gZCdhcHByZW50aXNzYWdlCmBgYHtyfQpwcmludChwcm9wLnRhYmxlKHRhYmxlKGRhdGFUcmFpbiRpc19nZW51aW5lKSkpCmBgYAoKIyMgZGlzdHJpYnV0aW9uIGRlcyBjbGFzc2VzIGRhbnMgbCfDqWNoYW50aWxsb24gZGUgdGVzdApgYGB7cn0KcHJpbnQocHJvcC50YWJsZSh0YWJsZShkYXRhVGVzdCRpc19nZW51aW5lKSkpCgpgYGAKIyMgQW5hbHlzZSA6IGxlcyBwcm9wb3J0aW9ucyBkYW5zIGxlcyDDqWNoYW50aWxsb25zIHNvbnQgY29tcGFyYWJsZXMgYXV4IGRvbm7DqWVzIGQnb3JpZ2luZSwgZG9uYyBvbiBwZXV0IGZhaXJlIGNvbmZpYW5jZSDDoCBjZXQgw6ljaGFudGlsbG9uCgpgYGB7cn0KdGVzdDwtIGxtKGlzX2dlbnVpbmV+LixkYXRhPWRhdGFUcmFpbikKdGVzdApgYGAKCgojIyBhcHByZW50aXNzYWdlIHN1ciB1biDDqWNoYW50aWxsb24KCmBgYHtyfQpkZWZhdWx0X2dsbV9tb2QgPSB0cmFpbigKICBmb3JtID0gaXNfZ2VudWluZSB+IC4sCiAgZGF0YSA9IGRhdGEsCiAgdHJDb250cm9sID0gdHJhaW5Db250cm9sKG1ldGhvZCA9ICJjdiIsIG51bWJlciA9IDUpLAogIG1ldGhvZCA9ICJnbG1TdGVwQUlDIiwKICBmYW1pbHkgPSAiYmlub21pYWwiCikKYGBgCgojIyMgcGFyIHLDqWdyZXNzaW9uIGxvZ2lzdGlxdWUKYGBge3J9Cm1fbHIgPC10cmFpbihpc19nZW51aW5lIH4uLCBkYXRhID1kYXRhVHJhaW4gLCBtZXRob2Q9ImdsbVN0ZXBBSUMiLHRyQ29udHJvbD10cmFpbkNvbnRyb2woIm5vbmUiKSkgI3RyQ29udHJvbD1maXRDb250cm9sKQpwcmludChzdW1tYXJ5KG1fbHIkZmluYWxNb2RlbCkpCmBgYAojIyBDT05DTFVTSU9OIFN0ZXBBSUMKIyMjIExlIG1vZMOobGUgcmV0ZW51IHBhciBsYSByZWdyZXNzaW9uIGxvZ2lzdGlxdWUgdGllbnQgY29tcHRlIGRlcyB2YXJpYWJsZXMgbWFyZ2luX2xvdyArIG1hcmdpbl91cCArIGxlbmd0aAoKIyMgT24gYXBwbGlxdWUgbCdhbGdvcnl0aG1lIHRyb3V2w6kgw6AgbCfDqWNoYW50aWxsb24gZGUgdGVzdApgYGB7cn0KbW9kZWxlIDwtIGdsbShpc19nZW51aW5lIH4gbWFyZ2luX2xvdyArIG1hcmdpbl91cCArIGxlbmd0aCwgZGF0YSA9IGRhdGEsIGZhbWlseT1iaW5vbWlhbChsaW5rPSJsb2dpdCIpKQojcHJlZGljdGlvbgpwcmVkLnJlcyA8LXByZWRpY3QoZGVmYXVsdF9nbG1fbW9kLG5ld2RhdGE9ZGF0YVRlc3QpCiNkaXN0cmlidXRpb24gZGVzIGNsYXNzZXMgcHLDqWRpdGVzCnRhYmxlKHByZWQucmVzKQpgYGAKYGBge3J9CnByZWQucmVzIDwtIGlmZWxzZShwcmVkLnJlcyA+IDAuNSwxLDApCnByZWQucmVzW3ByZWQucmVzPT0xXSA8LSAnVHJ1ZScKcHJlZC5yZXNbcHJlZC5yZXM9PTBdIDwtICdGYWxzZScKZGF0YVRlc3QkaXNfZ2VudWluZQpkYXRhVGVzdCRwcmVkIDwtIHByZWQucmVzCmRhdGFUZXN0IDwtIHRyYW5zZm9ybShkYXRhVGVzdCwgcHJlZCA9IGFzLmZhY3RvcihwcmVkKSkKZGF0YVRlc3QkaXNfZ2VudWluZVtkYXRhVGVzdCRpc19nZW51aW5lID09ICIxIl0gPC0gIlRydWUiCmRhdGFUZXN0JGlzX2dlbnVpbmVbZGF0YVRlc3QkaXNfZ2VudWluZSA9PSAiMCJdIDwtICJGYWxzZSIKZGF0YVRlc3QKYGBgCmBgYHtyfQpzZXQuc2VlZCgxMjMpCk1DID0gdGFibGUoIGRhdGFUZXN0JGlzX2dlbnVpbmUsIGRhdGFUZXN0JHByZWQpIApNQwpgYGAKCgoKCgojIyBBbmFseXNlIAojIyMgTGVzIGNsYXNzZXMgcHLDqWRpdGVzIHNvbnQgZW4gbGlnbmUsIGxlcyBvYnNlcnbDqWVzIGVuIGNvbG9ubmVzCiMjIyBBY2N1cmFjeSAoIHRhdXggZGUgcsOpdXNzaXRlICkgPSA5NyUKIyMjIFNlbnNpYmlsaXTDqSA9IDEwMCUgCiMjIyBJbCBuJ3kgYSBxdSd1bmUgc2V1bGUgZXJyZXVyIHN1ciBjZXQgw6ljaGFudGlsbG9uCgojIyMgc3VtbWFyeSBkdSBtb2TDqGxlIGRlIHLDqWdyZXNzaW9uIGxpbsOpYWlyZSBwb3VyIGwnb3JkcmUgZGVzIHZhcmlhYmxlcyDDoCBzdXBwcmltZXIKYGBge3J9CnN1bW1hcnkobV9scikKYGBgCgojIyMgbm91dmVsbGUgZXjDqWN1dGlvbiBhdmVjIGxlcyBkb25uw6llcyBkZSBUZXN0CmBgYHtyfQojcHJlZGljdGlvbgpwcmVkIDwtcHJlZGljdChtX2xyLG5ld2RhdGE9ZGF0YVRlc3QpCiNkaXN0cmlidXRpb24gZGVzIGNsYXNzZXMgcHLDqWRpdGVzCnByaW50KHRhYmxlKHByZWQpKQpgYGAKIyMjIG5vdXZlbGxlIG1hdHJpY2UgZGUgY29uZnVzaW9uCmBgYHtyfQpkYXRhVGVzdApzdW1tYXJ5KGRhdGFUZXN0KQpzdW1tYXJ5KHByZWQpCmhlYWQocHJlZCkKcHJpbnQoZGF0YVRlc3QkaXNfZ2VudWluZSkKI21hdCA8LWNvbmZ1c2lvbk1hdHJpeChkYXRhPXByZWQscmVmZXJlbmNlPWRhdGFUZXN0JGlzX2dlbnVpbmUscG9zaXRpdmU9IlRydWUiKQojcHJpbnQobWF0KQpgYGAKYGBge3J9Cm1vZGVsIDwtIGdsbShpc19nZW51aW5lIH4gbWFyZ2luX2xvdyArIG1hcmdpbl91cCArIGxlbmd0aCwgZGF0YSA9IGRmX25vdGVzX3ZmX251bWVyaWMsIGZhbWlseT1iaW5vbWlhbChsaW5rPSJsb2dpdCIpKQpgYGAKIyMgZmljaGllciBleGVtcGxlCiMjIC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tCmBgYHtyfQpmaWNfZXhlbXBsZSA8LSBwYXN0ZSAocmVwZXJ0b2lyZV9zb3VyY2VzLCJleGFtcGxlLmNzdiIsc2VwID0gIiIpCmRmX2V4ZW1wbGUgPC0gcmVhZC5jc3YyKGZpY19leGVtcGxlLGhlYWRlciA9IFRSVUUsIHNlcCA9ICIsIiwgZGVjID0gIi4iKQpoZWFkKGRmX2V4ZW1wbGUpCmBgYAoKYGBge3J9CiNwcmVkaWN0aW9uCnByZWQgPC1wcmVkaWN0KG1fbHIsbmV3ZGF0YT1kZl9leGVtcGxlKQojZGlzdHJpYnV0aW9uIGRlcyBjbGFzc2VzIHByw6lkaXRlcwpwcmludCh0YWJsZShwcmVkKSkKcHJlZApkZl9leGVtcGxlJGlzX2dlbnVpbmU8LXByZWQKZGZfZXhlbXBsZSA8LSB0cmFuc2Zvcm0oZGZfZXhlbXBsZSwgaXNfZ2VudWluZSA9IGFzLm51bWVyaWMoaXNfZ2VudWluZSkpCgpkZl9leGVtcGxlJGlzX2dlbnVpbmVbZGZfZXhlbXBsZSRpc19nZW51aW5lID09ICIxIiBdIDwtICJGYWxzZSIKZGZfZXhlbXBsZSRpc19nZW51aW5lW2RmX2V4ZW1wbGUkaXNfZ2VudWluZSA9PSAiMiIgXSA8LSAiVHJ1ZSIKCmRmX2JpbGxldHMgPC0gZGZfbm90ZXMKZGZfYmlsbGV0cyRpZCA8LSByb3duYW1lcyhkZl9iaWxsZXRzKQoKZGZfZGF0YV9jb21wbGV0IDwtIG1lcmdlKGRmX2JpbGxldHMsIGRmX2V4ZW1wbGUsIGFsbCA9IFQsIHNvcnQgPSBGQUxTRSkKcm93bmFtZXMoZGZfZGF0YV9jb21wbGV0KSA8LSBkZl9kYXRhX2NvbXBsZXQkaWQKZGltKGRmX2RhdGFfY29tcGxldCkKCiNkZl9iaWxsZXRzIDwtIHRyYW5zZm9ybShkZl9iaWxsZXRzLCBpZCA9IGFzLmZhY3RvcihpZCkpCiMgQUNQIGJpbGxldHMgZGUgbGEgcHJlbWnDqHJlIGRvbm7DqWUgZXQgYmlsbGV0cyBkZSBsJ2V4ZW1wbGUgcHLDqWRpdApyZXMucGNhIDwtIFBDQShkZl9kYXRhX2NvbXBsZXRbLGMoMTo3KV0sIHF1YWxpLnN1cCA9IDEsIGluZC5zdXAgPSAxNzE6MTc1LCBzY2FsZS51bml0ID0gVFJVRSwgZ3JhcGg9RkFMU0UpCgojIGJpbGxldHMgcHLDqWRpdHMgcGFyIHJhcHBvcnQgYXV4IGVuc2VtYmxlcyAKZ3JhcGhlIDwtIGZ2aXpfcGNhX2luZChyZXMucGNhLCBnZW9tLmluZCA9ICJwb2ludCIscG9pbnRzaXplID0gMSxoYWJpbGxhZ2U9MSxhZGRFbGxpcHNlcyA9IFRSVUUpCmdyYXBoZSA8LSBmdml6X2FkZChncmFwaGUsIHJlcy5wY2EkaW5kLnN1cCRjb29yZCkKZ3JhcGhlCgpgYGAKIyMgZmljaGllciBmb3VybmkKIyMgLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0KCmBgYHtyfQpmaWNfYV9wcmVkaXJlIDwtIHBhc3RlIChyZXBlcnRvaXJlX3NvdXJjZXMsImV4YW1wbGUuY3N2IixzZXAgPSAiIikKZGZfYV9wcmVkaXJlIDwtIHJlYWQuY3N2MihmaWNfYV9wcmVkaXJlLGhlYWRlciA9IFRSVUUsIHNlcCA9ICIsIiwgZGVjID0gIi4iKQpoZWFkKGRmX2FfcHJlZGlyZSkKYGBgCgpgYGB7cn0KI3ByZWRpY3Rpb24KcHJlZCA8LXByZWRpY3QobV9scixuZXdkYXRhPWRmX2FfcHJlZGlyZSkKI2Rpc3RyaWJ1dGlvbiBkZXMgY2xhc3NlcyBwcsOpZGl0ZXMKcHJpbnQodGFibGUocHJlZCkpCnByZWQKZGZfYV9wcmVkaXJlJGlzX2dlbnVpbmU8LXByZWQKZGZfYV9wcmVkaXJlIDwtIHRyYW5zZm9ybShkZl9hX3ByZWRpcmUsIGlzX2dlbnVpbmUgPSBhcy5udW1lcmljKGlzX2dlbnVpbmUpKQoKZGZfYV9wcmVkaXJlJGlzX2dlbnVpbmVbZGZfYV9wcmVkaXJlJGlzX2dlbnVpbmUgPT0gIjEiIF0gPC0gIkZhbHNlIgpkZl9hX3ByZWRpcmUkaXNfZ2VudWluZVtkZl9hX3ByZWRpcmUkaXNfZ2VudWluZSA9PSAiMiIgXSA8LSAiVHJ1ZSIKCmRmX2JpbGxldHMgPC0gZGZfbm90ZXMKZGZfYmlsbGV0cyRpZCA8LSByb3duYW1lcyhkZl9iaWxsZXRzKQoKZGZfZGF0YV9jb21wbGV0IDwtIG1lcmdlKGRmX2JpbGxldHMsIGRmX2FfcHJlZGlyZSwgYWxsID0gVCwgc29ydCA9IEZBTFNFKQpyb3duYW1lcyhkZl9kYXRhX2NvbXBsZXQpIDwtIGRmX2RhdGFfY29tcGxldCRpZApkaW0oZGZfZGF0YV9jb21wbGV0KQoKI2RmX2JpbGxldHMgPC0gdHJhbnNmb3JtKGRmX2JpbGxldHMsIGlkID0gYXMuZmFjdG9yKGlkKSkKIyBBQ1AgYmlsbGV0cyBkZSBsYSBwcmVtacOocmUgZG9ubsOpZSBldCBiaWxsZXRzIGRlIGwnZXhlbXBsZSBwcsOpZGl0CnJlcy5wY2EgPC0gUENBKGRmX2RhdGFfY29tcGxldFssYygxOjcpXSwgcXVhbGkuc3VwID0gMSwgaW5kLnN1cCA9IDE3MToxNzUsIHNjYWxlLnVuaXQgPSBUUlVFLCBncmFwaD1GQUxTRSkKCiMgYmlsbGV0cyBwcsOpZGl0cyBwYXIgcmFwcG9ydCBhdXggZW5zZW1ibGVzIApncmFwaGUgPC0gZnZpel9wY2FfaW5kKHJlcy5wY2EsIGdlb20uaW5kID0gInBvaW50Iixwb2ludHNpemUgPSAxLGhhYmlsbGFnZT0xLGFkZEVsbGlwc2VzID0gVFJVRSkKZ3JhcGhlIDwtIGZ2aXpfYWRkKGdyYXBoZSwgcmVzLnBjYSRpbmQuc3VwJGNvb3JkKQpncmFwaGUKCmBgYAoKIyMgYWZmaWNoZXIgbGUgcGxhbiBmYWN0b3JpZWwgc3VyIGNlcyB2YXJpYWJsZXMKCmBgYHtyfQojIEdyw6JjZSBhdSBtb2TDqGxlIMOpdGFibGl0LCBvbiBwcsOpZGl0IGxhIHLDqWFsaXTDqSBvdSBub24gZGVzIGJpbGxldHMKZml0dGVkLnJlc3VsdHMgPC0gcHJlZGljdChtb2RlbCxuZXdkYXRhPWRmX2V4ZW1wbGUsdHlwZT0ncmVzcG9uc2UnKQpwcmludChmaXR0ZWQucmVzdWx0cykKZml0dGVkLnJlc3VsdHMgPC0gaWZlbHNlKGZpdHRlZC5yZXN1bHRzID4gMC41LDEsMCkKZml0dGVkLnJlc3VsdHNbZml0dGVkLnJlc3VsdHM9PTFdIDwtICdUcnVlJwpmaXR0ZWQucmVzdWx0c1tmaXR0ZWQucmVzdWx0cz09MF0gPC0gJ0ZhbHNlJwoKIyBMYSBjb2xvbm5lIGlzX2dlbnVpbmUgZXN0IGFqb3V0w6kgYXUgZGF0YWZyYW1lIGJpbGxldHNfYV9wcmVkaXJlCmRmX2V4ZW1wbGUkaXNfZ2VudWluZSA8LSBmaXR0ZWQucmVzdWx0cwpkZl9leGVtcGxlIDwtIHRyYW5zZm9ybShkZl9leGVtcGxlLCBpc19nZW51aW5lID0gYXMuZmFjdG9yKGlzX2dlbnVpbmUpKQpkZl9leGVtcGxlCmBgYAo=