TUTORIELS

Ce site a pour but de référencer un ensemble de données spatiales utilisé dans des analyses spatiales.

Degree centrality

Nous souhaitons évaluer l’importance des gares. Nous calculons le degré de centralité pour identifier les gares clés dans le réseau, c’est-à-dire celles ayant le plus grand nombre de connexions directes avec d’autres gares. Les données utilisées dans les scripts suivants proviennent du site Open Data SNCF (consulté le 19 et le 20 février 2024).

En utilisant les données GTFS, Nous cartographions le réseau des gares des Hauts-de-France à partir du fichier stops.txt. Ensuite, grâce au fichier stop_times.txt, nous relions les ID des gares à des trips (passages) spécifiques, indiquant tous les passages de véhicules dans une gare sur une journée. Le fichier trips permet ensuite de relier ces trips aux ID des routes. Chaque routes regroupent plusieurs trips sous une même ligne de service. Finalement, le fichier routes fournit les noms réels des lignes (route_long_name) associés à ces ID de route.

Using GTFS data, we are mapping the network of TER stations in Hauts-de-France from the stops.txt file. Then, leveraging stop_times.txt, we connect station IDs to specific trips—indicating all vehicle passes through a station in a day. The trips file then links these trips to route IDs, grouping multiple trips under a single service line. Finally, routes.txt gives you the actual line names (route_long_name) associated with these route IDs, effectively linking station activities to the broader network of regional public transport lines.

LIGNES TER

A partir des données GTFS je cherche les ID des gares des Hauts-de-France disponibles dans le fichier stops.txt.

pacman::p_load(dplyr, sf, stringr, igraph, tmap)
# TER
# Étape 1 : Identifier les gares
stops <- read.csv('data/GTFS/export-ter-gtfs-last/stops.txt', header = TRUE)
names(stops)
## [1] "stop_id"        "stop_name"      "stop_desc"      "stop_lat"      
## [5] "stop_lon"       "zone_id"        "stop_url"       "location_type" 
## [9] "parent_station"
stops <- stops %>% 
  filter(grepl('Train', stop_id))
dim(stops)
## [1] 2499    9
stops_sf <- stops %>%
  sf::st_as_sf(coords = c("stop_lon", "stop_lat"), crs = 4326) %>% 
  st_transform(2154)
mapview::mapview(stops_sf)
regions <- sf::st_read("data/REGION.shp")
## Reading layer `REGION' from data source 
##   `C:\Users\otheureaux\Documents\OT\6T\R\database_sf\data\REGION.shp' 
##   using driver `ESRI Shapefile'
## Simple feature collection with 13 features and 4 fields
## Geometry type: MULTIPOLYGON
## Dimension:     XY
## Bounding box:  xmin: 99040 ymin: 6046546 xmax: 1242443 ymax: 7110479
## Projected CRS: RGF93 Lambert 93
HDF <- regions %>% 
  filter(INSEE_REG == 32) # filtre sur les Hauts-de-France

stops_sf_hdf <- sf::st_intersection(stops_sf, HDF)
## Warning: attribute variables are assumed to be spatially constant throughout
## all geometries
mapview::mapview(stops_sf_hdf)
stops <- stops_sf_hdf %>% 
  st_drop_geometry()
nom_id <- list()

for( i in 1:nrow(stops)) {
gare_nom <- stops[i,'stop_name'] 
gare_id <- stops %>% 
  filter(stop_name == gare_nom) 
nom_id[i] <- gare_id$stop_id
}

nom_id <- unlist(nom_id)

Étape 2 : Identification des noms des trajets

Le fichier stop_times contient les horaires détaillés des passages des véhicules à chaque arrêt pour chaque trajet. Je peux donc connaître tous les trips sr une journée qui passe dans une gare. Grâce à ce fichier on peut relier les noms des arrêts (stops) aux trips. Une gare peut recevoir un ou plusieurs trips ou passages.

Le fichier trips.txt contient des informations sur les passages (ou “trips”) effectués par des véhicules au sein d’un service de transport public (ex : une gare). Grâce à ce fichier on peut relier les trips au ID des routes. Sachant qu’un ID de routes contient plusieurs trips.

Le fichier routes contient les informations concernant les lignes (ou “routes”) opérées par un service de transport public. Dans ce fichier nous pouvons retrouver le nom des lignes (route_long_name) correspond aux ID trouvés dans le fichier trips.

stop_times <- read.csv('data/GTFS/export-ter-gtfs-last/stop_times.txt', header = TRUE)
trips <- read.csv('data/GTFS/export-ter-gtfs-last/trips.txt', header = TRUE)
routes <- read.csv('data/GTFS/export-ter-gtfs-last/routes.txt', header = TRUE)

Exemple pour Albert

stops_filter <- stops %>% 
  filter(stop_name == 'Albert')
stops_filter
##                           stop_id stop_name stop_desc zone_id stop_url
## 1 StopPoint:OCETrain TER-87313072    Albert        NA      NA       NA
##   location_type       parent_station                       ID           NOM_M
## 1             0 StopArea:OCE87313072 REGION_FXX_0000000000005 HAUTS-DE-FRANCE
##               NOM INSEE_REG
## 1 Hauts-de-France        32
# Ici on filtre sur la gare de Lille Centre Hospotalier Régional
trajets_gare <- stop_times %>% filter(stop_id == 'StopPoint:OCETrain TER-87313072')
# On obtient tous les trains qui s'arrêtent dans cette gare
# head(trajets_gare)
# dim(trajets_gare)

# Chaque train dispo d'un trip_id
trajets_gare <- trajets_gare$trip_id

# Trips
# Grâce à la table Trips on peut retrouver les route_id qui nous permettront de retrouver la route_name de chaque trips
trips_gare <- trips %>%
  filter(trip_id %in% trajets_gare)
head(trips_gare)
##                                         route_id service_id
## 1 FR:Line::E597DAD9-42BF-4927-BEDB-0B05091BCBBE:       2179
## 2 FR:Line::E597DAD9-42BF-4927-BEDB-0B05091BCBBE:        263
## 3 FR:Line::E597DAD9-42BF-4927-BEDB-0B05091BCBBE:       2179
## 4 FR:Line::E597DAD9-42BF-4927-BEDB-0B05091BCBBE:        263
## 5 FR:Line::E597DAD9-42BF-4927-BEDB-0B05091BCBBE:        263
## 6 FR:Line::E597DAD9-42BF-4927-BEDB-0B05091BCBBE:       2179
##                                    trip_id trip_headsign direction_id block_id
## 1 OCESN843008F2645165:2024-02-17T00:32:45Z        843008            1    19324
## 2 OCESN843008F3063349:2024-02-16T00:32:35Z        843008            1    19325
## 3 OCESN843900F2648247:2024-02-17T00:32:45Z        843900            1    19670
## 4 OCESN843900F3063462:2024-02-16T00:32:35Z        843900            1    19671
## 5 OCESN843901F2696761:2024-02-16T00:32:35Z        843901            0    19672
## 6 OCESN843901F3002913:2024-02-17T00:32:45Z        843901            0    19673
##   shape_id
## 1       NA
## 2       NA
## 3       NA
## 4       NA
## 5       NA
## 6       NA
trips_gare <- trips_gare$route_id
# trips_gare

# Étape 3 : Identifier les lignes correspondantes
names(routes)
## [1] "route_id"         "agency_id"        "route_short_name" "route_long_name" 
## [5] "route_desc"       "route_type"       "route_url"        "route_color"     
## [9] "route_text_color"
lignes_gares <- routes %>% 
  filter(route_id %in% trips_gare)
# Cette gare dispose de 4 lignes de services
lignes_gares
##                                         route_id agency_id route_short_name
## 1 FR:Line::7BB51D2B-5268-45E9-AEEE-1F1053DE381C:      1187              P22
## 2 FR:Line::7BDE293E-0D29-48A4-8978-8ADB3FFEA340:      1187              K45
## 3 FR:Line::B2E5B266-DAE7-4C0A-ABA0-B46411FB2CAA:      1187              K45
## 4 FR:Line::D77B2111-D576-48D5-878E-5D6C094D611A:      1187              P21
## 5 FR:Line::E597DAD9-42BF-4927-BEDB-0B05091BCBBE:      1187              K44
##                                    route_long_name route_desc route_type
## 1                                   Amiens - Arras         NA          2
## 2                                    Rouen - Lille         NA          2
## 3 Lille - Amiens - Abancourt - (Rouen Rive-Droite)         NA          2
## 4                      Abbeville - Amiens - Albert         NA          2
## 5                          Lille Flandres - Amiens         NA          2
##   route_url route_color route_text_color
## 1        NA      006600           FFFFFF
## 2        NA      BF005F           FFFFFF
## 3        NA      BF005F           FFFFFF
## 4        NA      006600           FFFFFF
## 5        NA      BF005F           FFFFFF
nrow(lignes_gares)
## [1] 5

Extraction de toutes les lignes

lignes_gares_nb <- list()

for( i in 1:nrow(stops)) {
trajets_gare <- stop_times %>% filter(stop_id %in% nom_id[i])
head(trajets_gare)
dim(trajets_gare)

trajets_gare <- trajets_gare$trip_id

# Trips
trips_gare <- trips %>%
  filter(trip_id %in% trajets_gare)
head(trajets_gare)
dim(trajets_gare)
trips_gare <- trips_gare$route_id
trips_gare

# Étape 3 : Identifier les lignes correspondantes
names(routes)

lignes_gares <- routes %>% 
  filter(route_id %in% trips_gare)
lignes_gares
nrow(lignes_gares)

lignes_gares_nb[i] <- nrow(lignes_gares)
}
lignes_gares_nb_unlist <- unlist(lignes_gares_nb)
lignes_gares_nb_unlist
##   [1]  4  3  1  1  1  1  1  1  1  1  2  1  2  2  1  2  1  1  1  1  1  1 15  7  3
##  [26]  3  7  3  2  5  3  2  1  7  1  1  2  1  1  1  3  4  2  2  3  3  3  3  1  1
##  [51]  1  4  2  3  3  3  2  2 21  1  2  2  3  2  3  1  1  2  2  8  2  4  1  4  1
##  [76]  2  1  1  2  2  2  4  1  4  2  2  2  1  4  1  1  1  1  1  2  3  1  2  2  5
## [101]  2  1  3  3  3  2  1  1  3  7  1  1  3  1  3  1  1  2  5  7  4  1  1  4  3
## [126]  3  1  2  5  1  2  1  3  1  1  1  1  1  1  1  7  5  2  5  7  1  1  1  1  1
## [151]  2  2  1  2  1  5  1  3  5  1  1  1  1  1  1  5  1  1  1  1  1  1  2  3  1
## [176]  1  1  1  1  1  1  1  1  1  2  2  2  2  2  2  7  2  1 17  2  2  2  1  1  1
## [201]  1  1  1  2  1  1  2  4  6  1  2  2  1  2  2  2  2  2  2  2  2  2  8  6  1
## [226]  3  4  3  3  4  1  1  6 10  1  4  1  1  1  1  3  1  1  3  1  1  1  1  3  1
## [251]  1  2  3  1  1  1  4  1  1  2  1  1  1  3  3  3  3  3  1  8  4  1  5  1  1
## [276]  1  1  1  1 10  8  1  2  1  1  1  2  2  2  2  1  2  1  1  2  2  2  2  2  2
## [301]  2  1  1  1  4  1  1  3  1  1  1
gares_services <- data.frame(stops$stop_name, lignes_gares_nb_unlist)

Le tableau ci-dessous compte le nombre de lignes qui passent par chacune des gares des Hauts-de-France :

Extraction des edges pour iGraph

# Initialisation de la liste pour les noms de lignes des gares
lignes_gares_names <- list()

# Boucle sur chaque identifiant de gare
for( i in 1:nrow(stops)) {
trajets_gare <- stop_times %>% 
  filter(stop_id %in% nom_id[i])
head(trajets_gare)
dim(trajets_gare)

trajets_gare <- trajets_gare$trip_id

# Filtrer les trajets par trip_id pour obtenir les route_id correspondants
routes_ids <- trips %>%
  filter(trip_id %in% trajets_gare)
head(trajets_gare)
dim(trajets_gare)
routes_ids <- routes_ids$route_id
routes_ids

 # Filtrer les lignes/routes par route_id
names(routes)

lignes_gares <- routes %>% 
  filter(route_id %in% routes_ids)
lignes_gares
nrow(lignes_gares)

# Ajouter les noms des lignes à la liste des noms des gares
  lignes_gares_names[[i]] <- lignes_gares$route_long_name
}

# Aplatir la liste en un vecteur unique de noms de lignes
lignes_gares_names <- unlist(lignes_gares_names)
lignes_gares_names <- unique(lignes_gares_names)
lignes_gares_names <- c(
  "Lille Flandres - Don-Sainghin - Béthune",
  "Lille Flandres - Don-Sainghin - Lens",
  "Lille Flandres - Béthune",
  "PARIS - ST DIZIER",
  "PARIS - STRASBOURG",
  "Château-Thierry - Reims",
  "REIMS - LAON",
  "Paris Nord - Laon",
  "Crépy-En-Valois - Laon",
  "Compiègne - Paris", # Ici j'ai ajouté un tiret
  "Paris Nord - Beauvais",
  "Saint-Quentin - Compiègne",
  "Paris Nord - Creil - Longueau - Arras - Douai - Lille Flandres",
  "Paris Nord - Amiens",
  "Paris Nord - Saint-Quentin",
  "Paris Nord - Compiègne",
  "Paris Nord - Maubeuge - Cambrai",
  "Paris Nord - Longueau-Amiens", # Ici j'ai ajouté un espace juste avant Longueau
  "Paris Nord - Creil - Saint-Just-En-Chaussée",
  "Beauvais - Creil",
  "Amiens - Creil",
  "Paris Nord - Creil",
  "Calais - Paris", # Ici j'ai ajouté un tiret
  "Amiens - Compiègne",
  "Arras - Dunkerque Ter Et Tgv Autorisés",
  "Lille Flandres - Dunkerque",
  "Dunkerque - Hazebrouck",
  "Calais Ville - Dunkerque",
  "Lille Flandres - Courtrai",
  "Lille Flandres - Tourcoing Y Compris Tgv Autorisés",
  "Calais Ville - Amiens", # Retrait tiret avant ville
  "Calais Ville - Rang-Du-Fliers - Verton - Berck",
  "Calais Ville - Hazebrouck",
  "Calais Ville - Arras",
  "Lille Flandres - Saint-Omer - Calais Ville",
  "Lille Flandres - St Quentin",
  "Lille Flandres - Hazebrouck",
  "Rouen - Lille",
  "Charleville - Lille",
  "Lille Flandres - Valenciennes",
  "Lille - Amiens - Abancourt - (Rouen Rive-Droite)",
  "Lille Flandres - Douai",
  "Lille Flandres - Hirson - (Charleville- Mézières)",
  "Lille Flandres - Amiens",
  "Lille Flandres - Maubeuge - Jeumont",
  "Lille Flandres - Libercourt - Lens",
  "Lille Flandres - Baisieux - (Tournai)",
  "Hazebrouck - Arras",
  "Valenciennes - Jeumont",
  "(Namur) - (Charleroi) - Maubeuge",
  "Aulnoye - Aymeries - Saint-Quentin",
  "Aulnoye - Aymeries - Hirson",
  "Amiens - Saint-Quentin",
  "Douai - Saint-Quentin",
  "Amiens - Tergnier - Laon",
  "Amiens - Arras",
  "Abbeville - Amiens - Albert",
  "Amiens - Abancourt - (Rouen Rive-Droite)",
  "Amiens - Abancourt",
  "Rouen - Amiens",
  "Beauvais - Abancourt - Le Tréport",
  "Arras/Béthune - St-Pol-Sur-Ternoise - Etaples",
  "Lens - Béthune - St-Pol-sur Ternoise",
  "Arras - Douai",
  "Douai - Valenciennes",
  "Valenciennes - Cambrai",
  "Lens - Douai",
  "Serqueux - Gisors") # Ici j'ai ajouté deux esapces entre le tiret


lignes_gares_names est composé du nom des lignes dans les Hauts-de-France. Nous constatons qu’il existe des noms de lignes disposant de gares intermédiaires, exemple : “Lille Flandres - Don-Sainghin - Lens”. Nous décidons de supprimer les gares intermédiaires pour ne laisser apparaître que les gares situées en bout de ligne.


# Initialiser un vecteur vide pour stocker les résultats
lignes_simplifiees <- vector("character", length = length(lignes_gares_names))

# Boucle pour traiter chaque nom de ligne
for(i in seq_along(lignes_gares_names)) {
  nom <- lignes_gares_names[i]
  parties <- str_split(nom, pattern = " - ", simplify = FALSE)[[1]]
  debut_fin <- c(parties[1], tail(parties, 1))
  lignes_simplifiees[i] <- str_c(debut_fin, collapse = " - ")
}

# Afficher les résultats
print(lignes_simplifiees)
##  [1] "Lille Flandres - Béthune"                          
##  [2] "Lille Flandres - Lens"                             
##  [3] "Lille Flandres - Béthune"                          
##  [4] "PARIS - ST DIZIER"                                 
##  [5] "PARIS - STRASBOURG"                                
##  [6] "Château-Thierry - Reims"                           
##  [7] "REIMS - LAON"                                      
##  [8] "Paris Nord - Laon"                                 
##  [9] "Crépy-En-Valois - Laon"                            
## [10] "Compiègne - Paris"                                 
## [11] "Paris Nord - Beauvais"                             
## [12] "Saint-Quentin - Compiègne"                         
## [13] "Paris Nord - Lille Flandres"                       
## [14] "Paris Nord - Amiens"                               
## [15] "Paris Nord - Saint-Quentin"                        
## [16] "Paris Nord - Compiègne"                            
## [17] "Paris Nord - Cambrai"                              
## [18] "Paris Nord - Longueau-Amiens"                      
## [19] "Paris Nord - Saint-Just-En-Chaussée"               
## [20] "Beauvais - Creil"                                  
## [21] "Amiens - Creil"                                    
## [22] "Paris Nord - Creil"                                
## [23] "Calais - Paris"                                    
## [24] "Amiens - Compiègne"                                
## [25] "Arras - Dunkerque Ter Et Tgv Autorisés"            
## [26] "Lille Flandres - Dunkerque"                        
## [27] "Dunkerque - Hazebrouck"                            
## [28] "Calais Ville - Dunkerque"                          
## [29] "Lille Flandres - Courtrai"                         
## [30] "Lille Flandres - Tourcoing Y Compris Tgv Autorisés"
## [31] "Calais Ville - Amiens"                             
## [32] "Calais Ville - Berck"                              
## [33] "Calais Ville - Hazebrouck"                         
## [34] "Calais Ville - Arras"                              
## [35] "Lille Flandres - Calais Ville"                     
## [36] "Lille Flandres - St Quentin"                       
## [37] "Lille Flandres - Hazebrouck"                       
## [38] "Rouen - Lille"                                     
## [39] "Charleville - Lille"                               
## [40] "Lille Flandres - Valenciennes"                     
## [41] "Lille - (Rouen Rive-Droite)"                       
## [42] "Lille Flandres - Douai"                            
## [43] "Lille Flandres - (Charleville- Mézières)"          
## [44] "Lille Flandres - Amiens"                           
## [45] "Lille Flandres - Jeumont"                          
## [46] "Lille Flandres - Lens"                             
## [47] "Lille Flandres - (Tournai)"                        
## [48] "Hazebrouck - Arras"                                
## [49] "Valenciennes - Jeumont"                            
## [50] "(Namur) - Maubeuge"                                
## [51] "Aulnoye - Saint-Quentin"                           
## [52] "Aulnoye - Hirson"                                  
## [53] "Amiens - Saint-Quentin"                            
## [54] "Douai - Saint-Quentin"                             
## [55] "Amiens - Laon"                                     
## [56] "Amiens - Arras"                                    
## [57] "Abbeville - Albert"                                
## [58] "Amiens - (Rouen Rive-Droite)"                      
## [59] "Amiens - Abancourt"                                
## [60] "Rouen - Amiens"                                    
## [61] "Beauvais - Le Tréport"                             
## [62] "Arras/Béthune - Etaples"                           
## [63] "Lens - St-Pol-sur Ternoise"                        
## [64] "Arras - Douai"                                     
## [65] "Douai - Valenciennes"                              
## [66] "Valenciennes - Cambrai"                            
## [67] "Lens - Douai"                                      
## [68] "Serqueux - Gisors"
# Création du nouveau vecteur simplifié
lignes_simplifiees <- c("Lille Flandres - Béthune",
                        "Lille Flandres - Lens"   ,                
                        "Lille Flandres - Béthune",                                                     
                        "PARIS - ST DIZIER"        ,                          
                         "PARIS - STRASBOURG"        ,                               
                         "Château-Thierry - Reims"    ,                              
                         "REIMS - LAON"                ,                             
                         "Paris Nord - Laon"            ,                            
                         "Crépy-En-Valois - Laon"        ,                           
                         "Compiègne - Paris",                        
                         "Paris Nord - Beauvais"   ,                       
                         "Saint-Quentin - Compiègne"       ,                      
                         "Paris Nord - Lille Flandres"         ,                     
                         "Paris Nord - Amiens"                  ,                    
                         "Paris Nord - Saint-Quentin"            ,                   
                         "Paris Nord - Compiègne"                 ,                  
                         "Paris Nord - Cambrai"                    ,                 
                         "Paris Nord - Longueau-Amiens", # Modification manuelle
                         "Paris Nord - Saint-Just-En-Chaussée"      ,                
                         "Beauvais - Creil"                ,                         
                         "Amiens - Creil"                  ,                         
                         "Paris Nord - Creil"               ,                        
                         "Calais - Paris"       ,                       
                         "Amiens - Compiègne"                 ,                      
                         "Arras - Dunkerque Ter Et Tgv Autorisés",                   
                         "Lille Flandres - Dunkerque"           ,                    
                         "Dunkerque - Hazebrouck"                 ,                  
                         "Calais Ville - Dunkerque"                ,                 
                         "Lille Flandres - Courtrai"                ,                
                         "Lille Flandres - Tourcoing Y Compris Tgv Autorisés"       ,
                         "Calais Ville - Amiens"                                   ,
                         "Calais Ville - Berck"                                     ,
                         "Calais Ville - Hazebrouck"                                ,
                         "Calais Ville - Arras"                                     ,
                         "Lille Flandres - Calais Ville"                            ,
                         "Lille Flandres - St Quentin"                              ,
                         "Lille Flandres - Hazebrouck"                              ,
                         "Rouen - Lille"                                            ,
                         "Charleville - Lille"                                      ,
                         "Lille Flandres - Valenciennes"                            ,
                         "Lille - (Rouen Rive-Droite)"                              ,
                         "Lille Flandres - Douai"                                   ,
                         "Lille Flandres - (Charleville- Mézières)"                 ,
                         "Lille Flandres - Amiens"                                  ,
                         "Lille Flandres - Jeumont"                                 ,
                         "Lille Flandres - Lens"                                    ,
                         "Lille Flandres - (Tournai)"                               ,
                         "Hazebrouck - Arras"                                       ,
                         "Valenciennes - Jeumont"                                   ,
                         "(Namur) - Maubeuge"                                       ,
                         "Aulnoye - Saint-Quentin"                                  ,
                         "Aulnoye - Hirson"                                         ,
                         "Amiens - Saint-Quentin"                                   ,
                         "Douai - Saint-Quentin"                                    ,
                         "Amiens - Laon"                                            ,
                         "Amiens - Arras"                                           ,
                         "Abbeville - Albert"                                       ,
                         "Amiens - (Rouen Rive-Droite)"                             ,
                         "Amiens - Abancourt"                                       ,
                         "Rouen - Amiens"                                           ,
                         "Beauvais - Le Tréport"                                    ,
                         "Arras/Béthune - Etaples"                                  ,
                         "Lens - St-Pol-sur Ternoise"                               ,
                         "Arras - Douai"                                            ,
                         "Douai - Valenciennes"                                     ,
                         "Valenciennes - Cambrai"                                   ,
                         "Lens - Douai"                                             ,
                         "Serqueux - Gisors") # modification manuelle

# Créer un data frame à partir du vecteur en séparant les gares de départ et d'arrivée
df_gares <- data.frame(
  GareDepart = sapply(lignes_simplifiees, function(x) strsplit(x, " - ")[[1]][1]),
  GareArrivee = sapply(lignes_simplifiees, function(x) {
    parts <- strsplit(x, " - ")[[1]]
    if(length(parts) >= 2) return(tail(parts, n = 1))
    else return(parts[1])
  })
)

# Afficher le data frame résultant
print(df_gares)
##         GareDepart                       GareArrivee
## 1   Lille Flandres                           Béthune
## 2   Lille Flandres                              Lens
## 3   Lille Flandres                           Béthune
## 4            PARIS                         ST DIZIER
## 5            PARIS                        STRASBOURG
## 6  Château-Thierry                             Reims
## 7            REIMS                              LAON
## 8       Paris Nord                              Laon
## 9  Crépy-En-Valois                              Laon
## 10       Compiègne                             Paris
## 11      Paris Nord                          Beauvais
## 12   Saint-Quentin                         Compiègne
## 13      Paris Nord                    Lille Flandres
## 14      Paris Nord                            Amiens
## 15      Paris Nord                     Saint-Quentin
## 16      Paris Nord                         Compiègne
## 17      Paris Nord                           Cambrai
## 18      Paris Nord                   Longueau-Amiens
## 19      Paris Nord            Saint-Just-En-Chaussée
## 20        Beauvais                             Creil
## 21          Amiens                             Creil
## 22      Paris Nord                             Creil
## 23          Calais                             Paris
## 24          Amiens                         Compiègne
## 25           Arras    Dunkerque Ter Et Tgv Autorisés
## 26  Lille Flandres                         Dunkerque
## 27       Dunkerque                        Hazebrouck
## 28    Calais Ville                         Dunkerque
## 29  Lille Flandres                          Courtrai
## 30  Lille Flandres Tourcoing Y Compris Tgv Autorisés
## 31    Calais Ville                            Amiens
## 32    Calais Ville                             Berck
## 33    Calais Ville                        Hazebrouck
## 34    Calais Ville                             Arras
## 35  Lille Flandres                      Calais Ville
## 36  Lille Flandres                        St Quentin
## 37  Lille Flandres                        Hazebrouck
## 38           Rouen                             Lille
## 39     Charleville                             Lille
## 40  Lille Flandres                      Valenciennes
## 41           Lille               (Rouen Rive-Droite)
## 42  Lille Flandres                             Douai
## 43  Lille Flandres           (Charleville- Mézières)
## 44  Lille Flandres                            Amiens
## 45  Lille Flandres                           Jeumont
## 46  Lille Flandres                              Lens
## 47  Lille Flandres                         (Tournai)
## 48      Hazebrouck                             Arras
## 49    Valenciennes                           Jeumont
## 50         (Namur)                          Maubeuge
## 51         Aulnoye                     Saint-Quentin
## 52         Aulnoye                            Hirson
## 53          Amiens                     Saint-Quentin
## 54           Douai                     Saint-Quentin
## 55          Amiens                              Laon
## 56          Amiens                             Arras
## 57       Abbeville                            Albert
## 58          Amiens               (Rouen Rive-Droite)
## 59          Amiens                         Abancourt
## 60           Rouen                            Amiens
## 61        Beauvais                        Le Tréport
## 62   Arras/Béthune                           Etaples
## 63            Lens               St-Pol-sur Ternoise
## 64           Arras                             Douai
## 65           Douai                      Valenciennes
## 66    Valenciennes                           Cambrai
## 67            Lens                             Douai
## 68        Serqueux                            Gisors
df_gares_ter <- df_gares

TGV

Ensuite, il faut extraire les lignes TGV

pacman::p_load(dplyr, sf, stringr, igraph)
# TGV
# Étape 1 : Identifier la Gare d'Intérêt
stops <- read.csv('data/GTFS/export_gtfs_voyages/stops.txt', header = TRUE)
names(stops)
## [1] "stop_id"        "stop_name"      "stop_desc"      "stop_lat"      
## [5] "stop_lon"       "zone_id"        "stop_url"       "location_type" 
## [9] "parent_station"
stops <- stops %>% 
  filter(grepl('TGV', stop_id))
# dim(stops)
stops_sf <- stops %>%
  sf::st_as_sf(coords = c("stop_lon", "stop_lat"), crs = 4326) %>% 
  st_transform(2154)
mapview::mapview(stops_sf)
regions <- sf::st_read("data/REGION.shp")
## Reading layer `REGION' from data source 
##   `C:\Users\otheureaux\Documents\OT\6T\R\database_sf\data\REGION.shp' 
##   using driver `ESRI Shapefile'
## Simple feature collection with 13 features and 4 fields
## Geometry type: MULTIPOLYGON
## Dimension:     XY
## Bounding box:  xmin: 99040 ymin: 6046546 xmax: 1242443 ymax: 7110479
## Projected CRS: RGF93 Lambert 93
HDF <- regions %>% 
  filter(INSEE_REG == 32) # filtre sur les Hauts-de-France

stops_sf_hdf <- sf::st_intersection(stops_sf, HDF)
## Warning: attribute variables are assumed to be spatially constant throughout
## all geometries
mapview::mapview(stops_sf_hdf)
stops <- stops_sf_hdf %>% 
  st_drop_geometry()
nom_id <- list()

for( i in 1:nrow(stops)) {
gare_nom <- stops[i,'stop_name'] 
gare_id <- stops %>% 
  filter(stop_name == gare_nom) 
nom_id[i] <- gare_id$stop_id
}

nom_id <- unlist(nom_id)
# print(nom_id)
# Étape 2 : Trouver les trajets passant par les gares
stop_times <- read.csv('data/GTFS/export_gtfs_voyages/stop_times.txt', header = TRUE)
trips <- read.csv('data/GTFS/export_gtfs_voyages/trips.txt', header = TRUE)
routes <- read.csv('data/GTFS/export_gtfs_voyages/routes.txt', header = TRUE)

Extraction de toutes les lignes

lignes_gares_nb <- list()

for( i in 1:nrow(stops)) {
trajets_gare <- stop_times %>% filter(stop_id %in% nom_id[i])
head(trajets_gare)
dim(trajets_gare)

trajets_gare <- trajets_gare$trip_id

# Trips
trips_gare <- trips %>%
  filter(trip_id %in% trajets_gare)
head(trajets_gare)
dim(trajets_gare)
trips_gare <- trips_gare$route_id
trips_gare

# Étape 3 : Identifier les lignes correspondantes
names(routes)

lignes_gares <- routes %>% 
  filter(route_id %in% trips_gare)
lignes_gares
nrow(lignes_gares)

lignes_gares_nb[i] <- nrow(lignes_gares)
}
lignes_gares_nb_unlist <- unlist(lignes_gares_nb)
lignes_gares_nb_unlist
##  [1] 10  2  2  6  1  1  1  1  6  1  1  1  2  1  8  1  6  2
gares_services <- data.frame(stops$stop_name, lignes_gares_nb_unlist)

Le tableau ci-dessous compte le nombre de lignes qui passent par chacune des gares des Hauts-de-France :

gares_services
##                    stops.stop_name lignes_gares_nb_unlist
## 1                     Lille Europe                     10
## 2                        Dunkerque                      2
## 3                 Calais - Fréthun                      2
## 4                   Lille Flandres                      6
## 5                       Hazebrouck                      1
## 6                        Tourcoing                      1
## 7                Croix - Wasquehal                      1
## 8                          Roubaix                      1
## 9               TGV Haute Picardie                      6
## 10 Rang-du-Fliers - Verton - Berck                      1
## 11            Étaples - Le Touquet                      1
## 12                    Calais Ville                      1
## 13                Boulogne sur Mer                      2
## 14                         Béthune                      1
## 15                           Arras                      8
## 16                    Valenciennes                      1
## 17                           Douai                      6
## 18                            Lens                      2

Extraction des edges pour iGraph

# Initialisation de la liste pour les noms de lignes des gares
lignes_gares_names <- list()

# Boucle sur chaque identifiant de gare
for( i in 1:nrow(stops)) {
trajets_gare <- stop_times %>% 
  filter(stop_id %in% nom_id[i])
head(trajets_gare)
dim(trajets_gare)

trajets_gare <- trajets_gare$trip_id

# Filtrer les trajets par trip_id pour obtenir les route_id correspondants
routes_ids <- trips %>%
  filter(trip_id %in% trajets_gare)
head(trajets_gare)
dim(trajets_gare)
routes_ids <- routes_ids$route_id
routes_ids

 # Filtrer les lignes/routes par route_id
names(routes)

lignes_gares <- routes %>% 
  filter(route_id %in% routes_ids)
lignes_gares
nrow(lignes_gares)

# Ajouter les noms des lignes à la liste des noms des gares
  lignes_gares_names[[i]] <- lignes_gares$route_long_name
}

# Aplatir la liste en un vecteur unique de noms de lignes
lignes_gares_names <- unlist(lignes_gares_names)
lignes_gares_names <- unique(lignes_gares_names)

# Afficher les noms uniques des lignes de gare
print(lignes_gares_names)
##  [1] "Lille - Alpes TGV"                   "Paris - Dunkerque par Bassin Minier"
##  [3] "Nord - Bretagne TGV"                 "Strasbourg -  Bruxelles"            
##  [5] "Lille Lyon TGV"                      "Paris - Littoral"                   
##  [7] "Lille - Mediterranee TGV"            "Paris - Lille - Dunkerque"          
##  [9] "Nord - Pays de la Loire TGV"         "Nord Aquitaine TGV"                 
## [11] "Paris - Lille"                       "Paris - Valenciennes"
df_gares_tgv <- lignes_gares_names

df_gares_tgv <- c("Lille - Alpes TGV", "Paris - Dunkerque par Bassin Minier",
                  "Nord - Bretagne TGV", "Strasbourg - Bruxelles",
                  "Lille - Lyon TGV", # ici j'ai ajouté un tiret entre Lille et Lyon TGV
                  "Paris - Littoral")

# Transformer le vecteur en dataframe avec deux colonnes : GareDepart et GareArrivee
df_gares_tgv_df <- data.frame(
  GareDepart = sapply(df_gares_tgv, function(x) strsplit(x, " - ")[[1]][1]),
  GareArrivee = sapply(df_gares_tgv, function(x) {
    split <- strsplit(x, " - ")
    if (length(split[[1]]) > 1) return(split[[1]][2])
    else return(NA) # Retourne NA si aucune gare d'arrivée n'est trouvée
  })
)

# Afficher le résultat
print(df_gares_tgv_df)
##                                     GareDepart                 GareArrivee
## Lille - Alpes TGV                        Lille                   Alpes TGV
## Paris - Dunkerque par Bassin Minier      Paris Dunkerque par Bassin Minier
## Nord - Bretagne TGV                       Nord                Bretagne TGV
## Strasbourg - Bruxelles              Strasbourg                   Bruxelles
## Lille - Lyon TGV                         Lille                    Lyon TGV
## Paris - Littoral                         Paris                    Littoral
row.names(df_gares_tgv_df) <- NULL

OUIGO

Ensuite il faut les lignes OUIGO :

pacman::p_load(dplyr, sf, stringr, igraph)
# TGV
# Étape 1 : Identifier la Gare d'Intérêt
stops <- read.csv('data/GTFS/export_gtfs_voyages/stops.txt', header = TRUE)
names(stops)
## [1] "stop_id"        "stop_name"      "stop_desc"      "stop_lat"      
## [5] "stop_lon"       "zone_id"        "stop_url"       "location_type" 
## [9] "parent_station"
stops <- stops %>% 
  filter(grepl('OUIGO', stop_id))
dim(stops)
## [1] 49  9
stops_sf <- stops %>%
  sf::st_as_sf(coords = c("stop_lon", "stop_lat"), crs = 4326) %>% 
  st_transform(2154)
mapview::mapview(stops_sf)
regions <- sf::st_read("data/REGION.shp")
## Reading layer `REGION' from data source 
##   `C:\Users\otheureaux\Documents\OT\6T\R\database_sf\data\REGION.shp' 
##   using driver `ESRI Shapefile'
## Simple feature collection with 13 features and 4 fields
## Geometry type: MULTIPOLYGON
## Dimension:     XY
## Bounding box:  xmin: 99040 ymin: 6046546 xmax: 1242443 ymax: 7110479
## Projected CRS: RGF93 Lambert 93
HDF <- regions %>% 
  filter(INSEE_REG == 32) # filtre sur les Hauts-de-France

stops_sf_hdf <- sf::st_intersection(stops_sf, HDF)
## Warning: attribute variables are assumed to be spatially constant throughout
## all geometries
mapview::mapview(stops_sf_hdf)
stops <- stops_sf_hdf %>% 
  st_drop_geometry()
nom_id <- list()

for( i in 1:nrow(stops)) {
gare_nom <- stops[i,'stop_name'] 
gare_id <- stops %>% 
  filter(stop_name == gare_nom) 
nom_id[i] <- gare_id$stop_id
}

nom_id <- unlist(nom_id)
print(nom_id)
## [1] "StopPoint:OCEOUIGO-87286005" "StopPoint:OCEOUIGO-87286542"
## [3] "StopPoint:OCEOUIGO-87313882"
# Étape 2 : Trouver les trajets passant par les gares
stop_times <- read.csv('data/GTFS/export_gtfs_voyages/stop_times.txt', header = TRUE)
trips <- read.csv('data/GTFS/export_gtfs_voyages/trips.txt', header = TRUE)
routes <- read.csv('data/GTFS/export_gtfs_voyages/routes.txt', header = TRUE)

Extraction de toutes les lignes

lignes_gares_nb <- list()

for( i in 1:nrow(stops)) {
trajets_gare <- stop_times %>% filter(stop_id %in% nom_id[i])
head(trajets_gare)
dim(trajets_gare)

trajets_gare <- trajets_gare$trip_id

# Trips
trips_gare <- trips %>%
  filter(trip_id %in% trajets_gare)
head(trajets_gare)
dim(trajets_gare)
trips_gare <- trips_gare$route_id
trips_gare

# Étape 3 : Identifier les lignes correspondantes
names(routes)

lignes_gares <- routes %>% 
  filter(route_id %in% trips_gare)
lignes_gares
nrow(lignes_gares)

lignes_gares_nb[i] <- nrow(lignes_gares)
}
lignes_gares_nb_unlist <- unlist(lignes_gares_nb)
lignes_gares_nb_unlist
## [1] 2 2 2
gares_services <- data.frame(stops$stop_name, lignes_gares_nb_unlist)

Le tableau ci-dessous compte le nombre de lignes qui passent par chacune des gares des Hauts-de-France :

gares_services
##      stops.stop_name lignes_gares_nb_unlist
## 1     Lille Flandres                      2
## 2          Tourcoing                      2
## 3 TGV Haute Picardie                      2

Extraction des edges pour iGraph

# Initialisation de la liste pour les noms de lignes des gares
lignes_gares_names <- list()

# Boucle sur chaque identifiant de gare
for( i in 1:nrow(stops)) {
trajets_gare <- stop_times %>% 
  filter(stop_id %in% nom_id[i])
head(trajets_gare)
dim(trajets_gare)

trajets_gare <- trajets_gare$trip_id

# Filtrer les trajets par trip_id pour obtenir les route_id correspondants
routes_ids <- trips %>%
  filter(trip_id %in% trajets_gare)
head(trajets_gare)
dim(trajets_gare)
routes_ids <- routes_ids$route_id
routes_ids

 # Filtrer les lignes/routes par route_id
names(routes)

lignes_gares <- routes %>% 
  filter(route_id %in% routes_ids)
lignes_gares
nrow(lignes_gares)

# Ajouter les noms des lignes à la liste des noms des gares
  lignes_gares_names[[i]] <- lignes_gares$route_long_name
}

# Aplatir la liste en un vecteur unique de noms de lignes
lignes_gares_names <- unlist(lignes_gares_names)
lignes_gares_names <- unique(lignes_gares_names)

# Afficher les noms uniques des lignes de gare
print(lignes_gares_names)
## [1] "Bordeaux - Lille SEA" "IS Nord Sud"
df_gares_ouigo <- lignes_gares_names

df_gares_ouigo <- c('Bordeaux - Lille SEA" "IS Nord Sud')

# Transformer le vecteur en dataframe avec deux colonnes : GareDepart et GareArrivee
df_gares_ouigo <- data.frame(
  GareDepart = sapply(df_gares_ouigo, function(x) strsplit(x, " - ")[[1]][1]),
  GareArrivee = sapply(df_gares_ouigo, function(x) {
    split <- strsplit(x, " - ")
    if (length(split[[1]]) > 1) return(split[[1]][2])
    else return(NA) # Retourne NA si aucune gare d'arrivée n'est trouvée
  })
)

# Afficher le résultat
print(df_gares_ouigo)
##                                    GareDepart             GareArrivee
## Bordeaux - Lille SEA" "IS Nord Sud   Bordeaux Lille SEA" "IS Nord Sud
row.names(df_gares_ouigo) <- NULL

Compilation des données

df_gares_ter
##         GareDepart                       GareArrivee
## 1   Lille Flandres                           Béthune
## 2   Lille Flandres                              Lens
## 3   Lille Flandres                           Béthune
## 4            PARIS                         ST DIZIER
## 5            PARIS                        STRASBOURG
## 6  Château-Thierry                             Reims
## 7            REIMS                              LAON
## 8       Paris Nord                              Laon
## 9  Crépy-En-Valois                              Laon
## 10       Compiègne                             Paris
## 11      Paris Nord                          Beauvais
## 12   Saint-Quentin                         Compiègne
## 13      Paris Nord                    Lille Flandres
## 14      Paris Nord                            Amiens
## 15      Paris Nord                     Saint-Quentin
## 16      Paris Nord                         Compiègne
## 17      Paris Nord                           Cambrai
## 18      Paris Nord                   Longueau-Amiens
## 19      Paris Nord            Saint-Just-En-Chaussée
## 20        Beauvais                             Creil
## 21          Amiens                             Creil
## 22      Paris Nord                             Creil
## 23          Calais                             Paris
## 24          Amiens                         Compiègne
## 25           Arras    Dunkerque Ter Et Tgv Autorisés
## 26  Lille Flandres                         Dunkerque
## 27       Dunkerque                        Hazebrouck
## 28    Calais Ville                         Dunkerque
## 29  Lille Flandres                          Courtrai
## 30  Lille Flandres Tourcoing Y Compris Tgv Autorisés
## 31    Calais Ville                            Amiens
## 32    Calais Ville                             Berck
## 33    Calais Ville                        Hazebrouck
## 34    Calais Ville                             Arras
## 35  Lille Flandres                      Calais Ville
## 36  Lille Flandres                        St Quentin
## 37  Lille Flandres                        Hazebrouck
## 38           Rouen                             Lille
## 39     Charleville                             Lille
## 40  Lille Flandres                      Valenciennes
## 41           Lille               (Rouen Rive-Droite)
## 42  Lille Flandres                             Douai
## 43  Lille Flandres           (Charleville- Mézières)
## 44  Lille Flandres                            Amiens
## 45  Lille Flandres                           Jeumont
## 46  Lille Flandres                              Lens
## 47  Lille Flandres                         (Tournai)
## 48      Hazebrouck                             Arras
## 49    Valenciennes                           Jeumont
## 50         (Namur)                          Maubeuge
## 51         Aulnoye                     Saint-Quentin
## 52         Aulnoye                            Hirson
## 53          Amiens                     Saint-Quentin
## 54           Douai                     Saint-Quentin
## 55          Amiens                              Laon
## 56          Amiens                             Arras
## 57       Abbeville                            Albert
## 58          Amiens               (Rouen Rive-Droite)
## 59          Amiens                         Abancourt
## 60           Rouen                            Amiens
## 61        Beauvais                        Le Tréport
## 62   Arras/Béthune                           Etaples
## 63            Lens               St-Pol-sur Ternoise
## 64           Arras                             Douai
## 65           Douai                      Valenciennes
## 66    Valenciennes                           Cambrai
## 67            Lens                             Douai
## 68        Serqueux                            Gisors
df_gares_tgv_df
##   GareDepart                 GareArrivee
## 1      Lille                   Alpes TGV
## 2      Paris Dunkerque par Bassin Minier
## 3       Nord                Bretagne TGV
## 4 Strasbourg                   Bruxelles
## 5      Lille                    Lyon TGV
## 6      Paris                    Littoral
df_gares_ouigo
##   GareDepart             GareArrivee
## 1   Bordeaux Lille SEA" "IS Nord Sud
df_gares <- rbind(df_gares_ter,
                  df_gares_tgv_df,
                  df_gares_ouigo)

# Supprimer les lignes avec des valeurs manquantes dans les colonnes de coordonnées
df_gares <- df_gares %>%
  filter(!is.na(GareDepart) & !is.na(GareArrivee))

print(df_gares)
##         GareDepart                       GareArrivee
## 1   Lille Flandres                           Béthune
## 2   Lille Flandres                              Lens
## 3   Lille Flandres                           Béthune
## 4            PARIS                         ST DIZIER
## 5            PARIS                        STRASBOURG
## 6  Château-Thierry                             Reims
## 7            REIMS                              LAON
## 8       Paris Nord                              Laon
## 9  Crépy-En-Valois                              Laon
## 10       Compiègne                             Paris
## 11      Paris Nord                          Beauvais
## 12   Saint-Quentin                         Compiègne
## 13      Paris Nord                    Lille Flandres
## 14      Paris Nord                            Amiens
## 15      Paris Nord                     Saint-Quentin
## 16      Paris Nord                         Compiègne
## 17      Paris Nord                           Cambrai
## 18      Paris Nord                   Longueau-Amiens
## 19      Paris Nord            Saint-Just-En-Chaussée
## 20        Beauvais                             Creil
## 21          Amiens                             Creil
## 22      Paris Nord                             Creil
## 23          Calais                             Paris
## 24          Amiens                         Compiègne
## 25           Arras    Dunkerque Ter Et Tgv Autorisés
## 26  Lille Flandres                         Dunkerque
## 27       Dunkerque                        Hazebrouck
## 28    Calais Ville                         Dunkerque
## 29  Lille Flandres                          Courtrai
## 30  Lille Flandres Tourcoing Y Compris Tgv Autorisés
## 31    Calais Ville                            Amiens
## 32    Calais Ville                             Berck
## 33    Calais Ville                        Hazebrouck
## 34    Calais Ville                             Arras
## 35  Lille Flandres                      Calais Ville
## 36  Lille Flandres                        St Quentin
## 37  Lille Flandres                        Hazebrouck
## 38           Rouen                             Lille
## 39     Charleville                             Lille
## 40  Lille Flandres                      Valenciennes
## 41           Lille               (Rouen Rive-Droite)
## 42  Lille Flandres                             Douai
## 43  Lille Flandres           (Charleville- Mézières)
## 44  Lille Flandres                            Amiens
## 45  Lille Flandres                           Jeumont
## 46  Lille Flandres                              Lens
## 47  Lille Flandres                         (Tournai)
## 48      Hazebrouck                             Arras
## 49    Valenciennes                           Jeumont
## 50         (Namur)                          Maubeuge
## 51         Aulnoye                     Saint-Quentin
## 52         Aulnoye                            Hirson
## 53          Amiens                     Saint-Quentin
## 54           Douai                     Saint-Quentin
## 55          Amiens                              Laon
## 56          Amiens                             Arras
## 57       Abbeville                            Albert
## 58          Amiens               (Rouen Rive-Droite)
## 59          Amiens                         Abancourt
## 60           Rouen                            Amiens
## 61        Beauvais                        Le Tréport
## 62   Arras/Béthune                           Etaples
## 63            Lens               St-Pol-sur Ternoise
## 64           Arras                             Douai
## 65           Douai                      Valenciennes
## 66    Valenciennes                           Cambrai
## 67            Lens                             Douai
## 68        Serqueux                            Gisors
## 69           Lille                         Alpes TGV
## 70           Paris       Dunkerque par Bassin Minier
## 71            Nord                      Bretagne TGV
## 72      Strasbourg                         Bruxelles
## 73           Lille                          Lyon TGV
## 74           Paris                          Littoral
## 75        Bordeaux           Lille SEA" "IS Nord Sud

IGRAPH : nombre de connexions directes

Enfin on crée un objet qui montre le nombre de connexions directes pour chacune gares situées en bout de ligne.

# Création du graphe
g <- igraph::graph_from_data_frame(df_gares, 
                                   directed = FALSE # Cet argument spécifie que le graphe créé est non dirigé. Dans un graphe non dirigé, les arêtes n'ont pas de direction, ce qui signifie que la relation entre deux nœuds est bidirectionnelle. 
                                   )

On peut donc lire que Lille Flandres possède 17 connexions directes avec d’autres gares de la région Hauts-de-France.

# Calcul du nombre de liens connectés à chaque nœud (station)
sort(degree(g), decreasing = T)
##                    Lille Flandres                            Amiens 
##                                17                                11 
##                        Paris Nord                      Calais Ville 
##                                10                                 6 
##                     Saint-Quentin                             Arras 
##                                 5                                 5 
##                             Lille                             Douai 
##                                 5                                 5 
##                         Compiègne                        Hazebrouck 
##                                 4                                 4 
##                      Valenciennes                              Lens 
##                                 4                                 4 
##                             Paris                          Beauvais 
##                                 4                                 3 
##                         Dunkerque                              Laon 
##                                 3                                 3 
##                             Creil                             PARIS 
##                                 3                                 2 
##                             Rouen                           Aulnoye 
##                                 2                                 2 
##                           Béthune                           Cambrai 
##                                 2                                 2 
##               (Rouen Rive-Droite)                           Jeumont 
##                                 2                                 2 
##                   Château-Thierry                             REIMS 
##                                 1                                 1 
##                   Crépy-En-Valois                            Calais 
##                                 1                                 1 
##                       Charleville                           (Namur) 
##                                 1                                 1 
##                         Abbeville                     Arras/Béthune 
##                                 1                                 1 
##                          Serqueux                              Nord 
##                                 1                                 1 
##                        Strasbourg                          Bordeaux 
##                                 1                                 1 
##                         ST DIZIER                        STRASBOURG 
##                                 1                                 1 
##                             Reims                              LAON 
##                                 1                                 1 
##                   Longueau-Amiens            Saint-Just-En-Chaussée 
##                                 1                                 1 
##    Dunkerque Ter Et Tgv Autorisés                          Courtrai 
##                                 1                                 1 
## Tourcoing Y Compris Tgv Autorisés                             Berck 
##                                 1                                 1 
##                        St Quentin           (Charleville- Mézières) 
##                                 1                                 1 
##                         (Tournai)                          Maubeuge 
##                                 1                                 1 
##                            Hirson                            Albert 
##                                 1                                 1 
##                         Abancourt                        Le Tréport 
##                                 1                                 1 
##                           Etaples               St-Pol-sur Ternoise 
##                                 1                                 1 
##                            Gisors                         Alpes TGV 
##                                 1                                 1 
##       Dunkerque par Bassin Minier                      Bretagne TGV 
##                                 1                                 1 
##                         Bruxelles                          Lyon TGV 
##                                 1                                 1 
##                          Littoral           Lille SEA" "IS Nord Sud 
##                                 1                                 1

IGRAPH : uniquement l’infrastructure (premier jet)

Ce script fournit donc une méthodologie pour le traitement, la visualisation et l’analyse de données spatiales complexes, en se concentrant sur la compréhension de la structure et de l’importance des composantes d’un réseau ferroviaire.

Objectifs Généraux du Script :
* Analyser la structure du réseau ferroviaire : En fusionnant les segments de ligne et en extrayant les points terminaux, le script simplifie la représentation du réseau pour faciliter l’analyse.
* Visualiser les données spatiales : nous utilisons mapview pour montrer les lignes ferroviaires, leurs points terminaux, et les gares, aidant à comprendre visuellement la disposition spatiale.
* Évaluer l’importance des gares : Calcule la centralité de degré pour identifier les gares clés dans le réseau, c’est-à-dire celles ayant le plus grand nombre de connexions directes avec d’autres gares.

Note : Ici, je ne prends pas en compte l’ensemble des lignes en service sur l’infrastructure ferroviaire. Je me contente de compter les points terminaux de chaque ligne d’un point de vue de l’infrastructure.

if (!requireNamespace("pacman", quietly = TRUE)) install.packages("pacman")
pacman::p_load(sf, igraph, dplyr, mapview)
# Chargemnent des données
reseau <- sf::st_read('data/reseau_ferroviaire — reseau_ferroviaire_13062022_17h00 _ HDF.gpkg')
## Reading layer `reseau_ferroviaire — reseau_ferroviaire_13062022_17h00 _ HDF' from data source `C:\Users\otheureaux\Documents\OT\6T\R\database_sf\data\reseau_ferroviaire — reseau_ferroviaire_13062022_17h00 _ HDF.gpkg' 
##   using driver `GPKG'
## Simple feature collection with 141 features and 7 fields
## Geometry type: MULTILINESTRING
## Dimension:     XY
## Bounding box:  xmin: 584284.5 ymin: 6870453 xmax: 787929.8 ymax: 7109041
## Projected CRS: RGF93 v1 / Lambert-93
names(reseau)
## [1] "id"             "code_ligne"     "rg_troncon"     "pk_debut_r"    
## [5] "pk_fin_r"       "mnemo"          "infrastructure" "geom"
mapview(reseau)
reseau_select <- reseau %>% 
  filter(code_ligne == 289000)
head(reseau_select)
## Simple feature collection with 4 features and 7 fields
## Geometry type: MULTILINESTRING
## Dimension:     XY
## Bounding box:  xmin: 652369.1 ymin: 7031217 xmax: 706423.8 ymax: 7059863
## Projected CRS: RGF93 v1 / Lambert-93
##    id code_ligne rg_troncon pk_debut_r pk_fin_r mnemo          infrastructure
## 1 252     289000          0     18+710   40+631    DV Double voie électrifiée
## 2 296     289000          0      0+021   18+710    DV Double voie électrifiée
## 3 280     289000          1     44+882   73+480    VU Voie unique électrifiée
## 4 281     289000          1     40+631   44+882    DV             Double voie
##                             geom
## 1 MULTILINESTRING ((674761.5 ...
## 2 MULTILINESTRING ((694102.2 ...
## 3 MULTILINESTRING ((652369.1 ...
## 4 MULTILINESTRING ((670679.6 ...
mapview(reseau_select)
nrow(reseau)
## [1] 141
# Fusionner les segments ayant le même code_ligne
ligne_fusionnee <- reseau %>%
  group_by(code_ligne) %>%
  summarise(geom = st_union(geom)) %>%
  ungroup()
nrow(ligne_fusionnee)
## [1] 92
reseau_select <- ligne_fusionnee %>% 
  filter(code_ligne == 289000)
head(reseau_select)
## Simple feature collection with 1 feature and 1 field
## Geometry type: MULTILINESTRING
## Dimension:     XY
## Bounding box:  xmin: 652369.1 ymin: 7031217 xmax: 706423.8 ymax: 7059863
## Projected CRS: RGF93 v1 / Lambert-93
## # A tibble: 1 × 2
##   code_ligne                                                                geom
##   <chr>                                                    <MULTILINESTRING [m]>
## 1 289000     ((674761.5 7046989, 674904.8 7046911, 674984.5 7046870, 675134.1 7…
mapview(reseau_select)
# Affichage de la carte
mapview(ligne_fusionnee)
nrow(ligne_fusionnee)
## [1] 92
gares <- sf::st_read('data/gares_HDF_2154.gpkg')
## Reading layer `gares_HDF_2154' from data source 
##   `C:\Users\otheureaux\Documents\OT\6T\R\database_sf\data\gares_HDF_2154.gpkg' 
##   using driver `GPKG'
## Simple feature collection with 393 features and 65 fields
## Geometry type: MULTIPOINT
## Dimension:     XY
## Bounding box:  xmin: 595141.4 ymin: 6874373 xmax: 781232.5 ymax: 7108435
## Projected CRS: RGF93 v1 / Lambert-93
gares_select <- gares %>% 
  select(nom_gare)
mapview(gares)
nrow(gares_select)
## [1] 393
# =======================
lignes <- ligne_fusionnee  

# Extraire la géométrie des lignes
geometries <- st_geometry(lignes)

# Utiliser lapply pour appliquer une fonction à chaque géométrie de ligne
points_terminaux <- lapply(geometries, function(line_geom) {
  # Extraire les coordonnées des points terminaux de la géométrie de ligne
  coords <- st_coordinates(line_geom)[c(1, nrow(st_coordinates(line_geom))), ]
  # Créer un MULTIPOINT à partir des coordonnées des terminaux
  st_multipoint(as.matrix(coords), dim = "XY")
})

# Combiner les objets MULTIPOINT en une seule collection de features géométriques (sfc)
points_terminaux_sfc <- do.call(st_sfc, args = list(points_terminaux, crs = st_crs(lignes)))

# Supprimer les dimensions Z (altitude) et M (entité de stockage)
points_terminaux_sfc <- st_zm(points_terminaux_sfc)

# Convertir MULTIPOINT en POINT pour avoir des points individuels
points_terminaux_sfc <- st_cast(points_terminaux_sfc, "POINT")

# Affichage de la carte
mapview(st_as_sf(points_terminaux_sfc)) + mapview(lignes)
# Création d'un sf dataframe pour les points terminaux
# Ce code crée un df avec pour chaque ligne les points situés aux extrémités
points_terminaux_df <- st_sf(geometry = points_terminaux_sfc, 
                             line_id = rep(lignes$code_ligne, each = 2)) 
st_crs(points_terminaux_df) <- 'EPSG:2154' 

# Affichage de la carte
mapview(st_as_sf(points_terminaux_df)) + mapview(lignes) + mapview(gares_select, col.region = 'red')
# `gares_select` est l'objet sf contenant les gares avec leurs identifiants uniques
# et `points_terminaux_sf` est un objet sf des points terminaux

# Calcul de la gare la plus proche pour chaque point terminal
indices_gares_proches <- st_nearest_feature(points_terminaux_df, gares_select)

# Ajouter l'identifiant de la gare la plus proche à `points_terminaux_df`
points_terminaux_df$gare_id <- gares_select$nom_gare[indices_gares_proches]

head(points_terminaux_df)
## Simple feature collection with 6 features and 2 fields
## Geometry type: POINT
## Dimension:     XY
## Bounding box:  xmin: 713919.1 ymin: 6870453 xmax: 751888.7 ymax: 6897911
## Projected CRS: RGF93 v1 / Lambert-93
##   line_id                 geometry                gare_id
## 1  004000 POINT (736117.3 6885328)               Crézancy
## 2  004000 POINT (740617.6 6870453)          Condé-en-Brie
## 3  005000 POINT (713919.1 6883064)      Mareuil sur Ourcq
## 4  005000 POINT (751888.7 6897911)      Fère en Tardenois
## 5  070000 POINT (718886.1 6872593) Nogent l'Artaud-Charly
## 6  070000 POINT (742945.8 6884325)               Crézancy
# Affichage de la carte
mapview(points_terminaux_df) + mapview(lignes)
# Trier points_terminaux_df par line_id 
points_terminaux_df <- points_terminaux_df[order(points_terminaux_df$line_id),]

# Créer le dataframe d'arêtes en associant chaque paire de gares par line_id
edges <- data.frame(
  from = points_terminaux_df$gare_id[c(TRUE, FALSE)],  # Sélection des gares de départ
  to = points_terminaux_df$gare_id[c(FALSE, TRUE)]     # Sélection des gares d'arrivée
)

# Supprimer les éventuels doublons ou connexions de gare à elle-même
# Par exemple 306000 qui part d'Arras et qui y arrive
# Ce sont principalement de petit tronçon. 306000 : Voie unique fret
edges <- unique(edges[edges$from != edges$to,])
edges
##                      from                     to
## 1                Crézancy          Condé-en-Brie
## 2       Mareuil sur Ourcq      Fère en Tardenois
## 3  Nogent l'Artaud-Charly               Crézancy
## 4       Mareuil sur Ourcq               Bazoches
## 5   Aguilcourt-Variscourt                   Laon
## 8                  Fretin         Calais-Fréthun
## 9                 Lesquin                 Fretin
## 13                Cysoing  Le Plessis Belleville
## 15    Courcelles le Comte                  Arras
## 17                   Laon                Vervins
## 18        Crépy en Valois                   Anor
## 19    Estrées Saint-Denis  Longueil-Sainte-Marie
## 21                  Creil                Jeumont
## 26               Hautmont             Les Guides
## 29                 Somain                Busigny
## 32           Valenciennes           Sous le Bois
## 33               Lourches           Valenciennes
## 37                Cambrai               Chaulnes
## 38                 Amiens                   Laon
## 40           Valenciennes              Beuvrages
## 41              Beuvrages           Valenciennes
## 42              Hellemmes          Mont de Terre
## 45                Tressin                Orchies
## 46               Baisieux               Lezennes
## 47       La Borne Blanche         Lille Flandres
## 48                  Douai           Sin-le-Noble
## 49   Lille Porte de Douai                Lesquin
## 50              Tourcoing               Lezennes
## 52                   Lens             Ostricourt
## 53            Sallaumines    Pont de Sallaumines
## 55                Dourges             Ostricourt
## 56            Sallaumines           Don Sainghin
## 58             Haubourdin                 Santes
## 59       Saint-André Nord             Haubourdin
## 61     La Gorgue-Estaires            Armentières
## 62         Lille Flandres        Les Fontinettes
## 63         Comines France      La Madeleine Nord
## 64    Coudekerque Branche             Bray-Dunes
## 65              Dunkerque                  Arras
## 66            Sallaumines                  Avion
## 68          Grande-Synthe              Courghain
## 69              Courghain             Gravelines
## 70             Gravelines              Bourbourg
## 71              Dunkerque          Grande-Synthe
## 72              Dunkerque        Les Fontinettes
## 74      Dreuil lès Amiens             Saint-Roch
## 76 Saint-Pol sur Ternoise                  Arras
## 77     Etaples-le Touquet Saint-Pol sur Ternoise
## 78            Blendecques              Longfossé
## 79             Saint-Roch               Boulogne
## 80        Pont de Briques               Boulogne
## 81  Boulogne Tintelleries               Boulogne
## 82               Boulogne                 Calais
## 85            Rochy Condé               Soissons
## 87               Formerie             Saint-Roch
## 88              Abbeville              Woincourt
## 89 Laboissières le Déluge              Woincourt
## 90         Boran sur Oise                  Creil
## 91         La Villetertre               Formerie
## 92         Calais-Fréthun                Maurois
# Création du graphe
g <- igraph::graph_from_data_frame(edges, directed = FALSE)

# Calcul du nombre de liens connectés à chaque nœud (station)
sort(degree(g), decreasing = T)
##           Valenciennes               Boulogne                   Laon 
##                      4                      4                      3 
##            Sallaumines              Dunkerque             Saint-Roch 
##                      3                      3                      3 
##                  Arras               Crézancy      Mareuil sur Ourcq 
##                      3                      2                      2 
##                 Fretin                Lesquin                  Creil 
##                      2                      2                      2 
##              Beuvrages             Haubourdin         Lille Flandres 
##                      2                      2                      2 
##          Grande-Synthe              Courghain             Gravelines 
##                      2                      2                      2 
## Saint-Pol sur Ternoise               Formerie         Calais-Fréthun 
##                      2                      2                      2 
##               Lezennes             Ostricourt        Les Fontinettes 
##                      2                      2                      2 
##              Woincourt Nogent l'Artaud-Charly  Aguilcourt-Variscourt 
##                      2                      1                      1 
##                Cysoing    Courcelles le Comte        Crépy en Valois 
##                      1                      1                      1 
##    Estrées Saint-Denis               Hautmont                 Somain 
##                      1                      1                      1 
##               Lourches                Cambrai                 Amiens 
##                      1                      1                      1 
##              Hellemmes                Tressin               Baisieux 
##                      1                      1                      1 
##       La Borne Blanche                  Douai   Lille Porte de Douai 
##                      1                      1                      1 
##              Tourcoing                   Lens                Dourges 
##                      1                      1                      1 
##       Saint-André Nord     La Gorgue-Estaires         Comines France 
##                      1                      1                      1 
##    Coudekerque Branche      Dreuil lès Amiens     Etaples-le Touquet 
##                      1                      1                      1 
##            Blendecques        Pont de Briques  Boulogne Tintelleries 
##                      1                      1                      1 
##            Rochy Condé              Abbeville Laboissières le Déluge 
##                      1                      1                      1 
##         Boran sur Oise         La Villetertre          Condé-en-Brie 
##                      1                      1                      1 
##      Fère en Tardenois               Bazoches  Le Plessis Belleville 
##                      1                      1                      1 
##                Vervins                   Anor  Longueil-Sainte-Marie 
##                      1                      1                      1 
##                Jeumont             Les Guides                Busigny 
##                      1                      1                      1 
##           Sous le Bois               Chaulnes          Mont de Terre 
##                      1                      1                      1 
##                Orchies           Sin-le-Noble    Pont de Sallaumines 
##                      1                      1                      1 
##           Don Sainghin                 Santes            Armentières 
##                      1                      1                      1 
##      La Madeleine Nord             Bray-Dunes                  Avion 
##                      1                      1                      1 
##              Bourbourg              Longfossé                 Calais 
##                      1                      1                      1 
##               Soissons                Maurois 
##                      1                      1

ANALYSE

Centralité de Degré Elevée : Les stations comme Valenciennes, Boulogne, Laon, Sallaumines, Dunkerque, et Saint-Roch ont les centralités de degré les plus élevées dans le réseau, avec des valeurs de 4 ou 3. Cela signifie que ces stations sont connectées directement à 4 ou 3 autres stations. Dans le contexte d’un réseau de transport, une centralité de degré élevée pour une station indique un rôle potentiellement crucial dans le réseau, servant comme un hub ou un point de transfert majeur où de nombreuses lignes ou trajets se croisent.

Stations avec Moins de Connexions : Les stations avec une centralité de degré de 2 sont connectées directement à deux autres stations. Cela suggère qu’elles sont moins centrales que les premières mais jouent toujours un rôle dans la connectivité du réseau.

Stations Périphériques : Les stations avec une centralité de degré de 1, comme Nogent l’Artaud-Charly, Aguilcourt-Variscourt, et Cysoing, sont connectées directement à une seule autre station. Ces stations peuvent être considérées comme périphériques ou terminales dans le réseau, étant à l’extrémité d’une ligne ou ayant moins d’importance dans la structure globale du réseau.

Bibliographie


Pour plus de détails sur la théorie des graphs et le degree centrality vous pouvez consulter ce lien.


Closeness Centrality

La closeness centrality (ou centralité de proximité) est une mesure utilisée en théorie des graphes et analyse de réseaux pour évaluer l’importance ou la centralité d’un nœud (par exemple, une gare dans un réseau de transport) par rapport à tous les autres nœuds du réseau. Elle est définie comme l’inverse de la somme des distances les plus courtes entre ce nœud et tous les autres nœuds dans le réseau.

En d’autres termes, un nœud avec une haute closeness centrality est un nœud qui, en moyenne, est plus proche de tous les autres nœuds dans le réseau. Cela signifie que depuis ce nœud, on peut atteindre rapidement tous les autres nœuds, ce qui le rend stratégiquement important pour la diffusion rapide d’informations, la réduction des temps de trajet, ou l’efficacité des échanges au sein du réseau.

Pour calculer la closeness centrality des gares des Hauts-de-France nous utilisons des données GTFS. Les données utilisées dans les scripts suivants proviennent du site Open Data SNCF (consulté le 19 et le 20 février 2024).

Closeness centrality is a measure used in graph theory and network analysis to assess the importance or centrality of a node (for example, a station in a transportation network) in relation to all other nodes in the network. It is defined as the inverse of the sum of the shortest distances between this node and all other nodes in the network.

In other words, a node with high closeness centrality is one that, on average, is closer to all other nodes in the network. This means that from this node, all other nodes can be reached quickly, making it strategically important for the rapid dissemination of information, the reduction of travel times, or the efficiency of exchanges within the network.

To calculate the closeness centrality of stations in the Hauts-de-France region, we use GTFS data. The data used in the following scripts comes from the SNCF Open Data site (consulted on February 19 and 20, 2024).

pacman::p_load(sf, dplyr, mapview, geosphere, igraph, ggplot2)

stops_ter <- read.csv('data/GTFS/export-ter-gtfs-last/stops.txt', header = TRUE)
# names(stops_ter)
stops_ter_select <- stops_ter %>% 
  dplyr::select(stop_id, stop_name, stop_lat, stop_lon)
rm(stops_ter)
stops_tgv <- read.csv('data/GTFS/export_gtfs_voyages/stops.txt', header = TRUE)
stops_tgv_select <- stops_tgv %>% 
  dplyr::select(stop_id, stop_name, stop_lat, stop_lon)
rm(stops_tgv)
stops_ouigo <- read.csv('data/GTFS/export_gtfs_voyages/stops.txt', header = TRUE)
stops_ouigo_select <- stops_ouigo %>% 
  dplyr::select(stop_id, stop_name, stop_lat, stop_lon)
rm(stops_ouigo)
stops <- rbind(
  stops_ter_select, 
  stops_tgv_select,
  stops_ouigo_select
)
rm(stops_ter_select,stops_tgv_select, stops_ouigo_select)
stops_select <- stops %>% filter(grepl('Train', stop_id))
# names(stops_select)
rm(stops)
stops_select_sf <- stops_select %>%
  sf::st_as_sf(coords = c("stop_lon", "stop_lat"), crs = 4326)
stops_select_sf <- st_transform(stops_select_sf, 2154)
# mapview(stops_select_sf)

# Sélection des arrêts dans les Hauts-de-France
regions <- sf::st_read("data/REGION.shp")
## Reading layer `REGION' from data source 
##   `C:\Users\otheureaux\Documents\OT\6T\R\database_sf\data\REGION.shp' 
##   using driver `ESRI Shapefile'
## Simple feature collection with 13 features and 4 fields
## Geometry type: MULTIPOLYGON
## Dimension:     XY
## Bounding box:  xmin: 99040 ymin: 6046546 xmax: 1242443 ymax: 7110479
## Projected CRS: RGF93 Lambert 93
HDF <- regions %>% 
  filter(INSEE_REG == 32) # filtre sur les Hauts-de-France
rm(regions)
stops_select_sf_inter <- sf::st_intersection(stops_select_sf, HDF)
## Warning: attribute variables are assumed to be spatially constant throughout
## all geometries
rm(HDF)
mapview::mapview(stops_select_sf_inter)
# names(stops_select_sf_inter)
rm(stops_select_sf)

stops_select_sf_inter_select <- stops_select_sf_inter %>% 
  select(stop_id, stop_name, geometry)
head(stops_select_sf_inter_select)
## Simple feature collection with 6 features and 2 fields
## Geometry type: POINT
## Dimension:     XY
## Bounding box:  xmin: 702417.1 ymin: 6882081 xmax: 770639.6 ymax: 7057502
## Projected CRS: RGF93 v1 / Lambert-93
##                             stop_id                         stop_name
## 71  StopPoint:OCETrain TER-87109306 Lille Centre Hospitalier Régional
## 76  StopPoint:OCETrain TER-87116582                   Château-Thierry
## 163 StopPoint:OCETrain TER-87171702           Aguilcourt - Variscourt
## 166 StopPoint:OCETrain TER-87171736                       Guignicourt
## 167 StopPoint:OCETrain TER-87171744                       Amifontaine
## 168 StopPoint:OCETrain TER-87171751                        Saint-Erme
##                     geometry
## 71  POINT (702417.1 7057502)
## 76  POINT (729935.6 6882081)
## 163 POINT (770639.6 6923609)
## 166   POINT (769736 6926581)
## 167 POINT (766004.1 6932094)
## 168 POINT (762121.3 6936705)
stops_select_sf_inter_select <- st_transform(stops_select_sf_inter_select, 4326)
names(stops_select_sf_inter_select)
## [1] "stop_id"   "stop_name" "geometry"
rm(stops_select_sf_inter)
coords <- st_coordinates(stops_select_sf_inter_select)
stops_select <- data.frame(stop_id = stops_select_sf_inter_select$stop_id,
                           stop_name = stops_select_sf_inter_select$stop_name,
                           stop_lat = coords[,2], # Y est généralement la latitude
                           stop_lon = coords[,1]) # X est généralement la longitude

# Chargement des autres fichiers
stop_times <- read.csv('data/GTFS/export-ter-gtfs-last/stop_times.txt', header = TRUE)
trips <- read.csv('data/GTFS/export-ter-gtfs-last/trips.txt', header = TRUE)
routes <- read.csv('data/GTFS/export-ter-gtfs-last/routes.txt', header = TRUE)

# Joindre les tables pour obtenir une vue complète incluant l'ordre des gares, leurs coordonnées, et la ligne correspondante
stops_ordered <- stop_times %>%
  select(trip_id, stop_id, stop_sequence) %>%
  left_join(trips, by = "trip_id") %>%
  left_join(routes, by = "route_id") %>%
  left_join(stops_select, by = "stop_id") %>%
  arrange(route_id, trip_id, stop_sequence)

rm(stop_times, trips, routes)
# stops_ordered_filter <- stops_ordered %>% 
#   filter(route_id == 'FR:Line::DBF3F104-9FF0-48F6-84CE-E49FD6D7F630:')

# À ce stade, stops_ordered contient l'ordre des arrêts pour chaque ligne et trajet, avec les coordonnées géographiques

stops_ordered_clean <- stops_ordered %>%
  filter(!is.na(stop_sequence) & !is.na(stop_lat) & 
           !is.na(stop_lon)) %>% 
  filter(direction_id == 0)
rm(stops_ordered)

stops_ordered_clean_select <- stops_ordered_clean %>% 
  select(route_id, route_long_name,stop_name, direction_id, stop_sequence, stop_lat, stop_lon)
rm(stops_ordered_clean)

dim(stops_ordered_clean_select)
## [1] 12502     7
head(stops_ordered_clean_select)
##                                         route_id
## 1 FR:Line::0f72eaf9-e37d-4941-9fd4-80e732190386:
## 2 FR:Line::0f72eaf9-e37d-4941-9fd4-80e732190386:
## 3 FR:Line::0f72eaf9-e37d-4941-9fd4-80e732190386:
## 4 FR:Line::0f72eaf9-e37d-4941-9fd4-80e732190386:
## 5 FR:Line::0f72eaf9-e37d-4941-9fd4-80e732190386:
## 6 FR:Line::0f72eaf9-e37d-4941-9fd4-80e732190386:
##                                                  route_long_name      stop_name
## 1 Paris Nord - Creil - Longueau - Arras - Douai - Lille Flandres Lille Flandres
## 2 Paris Nord - Creil - Longueau - Arras - Douai - Lille Flandres          Douai
## 3 Paris Nord - Creil - Longueau - Arras - Douai - Lille Flandres          Arras
## 4 Paris Nord - Creil - Longueau - Arras - Douai - Lille Flandres       Longueau
## 5 Paris Nord - Creil - Longueau - Arras - Douai - Lille Flandres          Creil
## 6 Paris Nord - Creil - Longueau - Arras - Douai - Lille Flandres Lille Flandres
##   direction_id stop_sequence stop_lat stop_lon
## 1            0             0 50.63646 3.070840
## 2            0             1 50.37169 3.089969
## 3            0             2 50.28671 2.781895
## 4            0             3 49.86385 2.352591
## 5            0             4 49.26335 2.467837
## 6            0             0 50.63646 3.070840
# Ici je trie par route et stop_sequence puis je calcule la distance avec la gare suivante
stops_ordered_clean_select_dist <- stops_ordered_clean_select %>%
  arrange(route_id, stop_sequence) %>% # S'assurer que les données sont triées
  group_by(route_id) %>%
  mutate(
    # Calculer la distance avec la gare suivante dans la séquence
    distance_to_next = distHaversine(cbind(stop_lon, stop_lat), 
                                     cbind(lead(stop_lon), 
                                           lead(stop_lat)))
  ) %>%
  ungroup() # Dégroupement pour manipulations ultérieures
rm(stops_ordered_clean_select)

# Afficher le résultat
dim(stops_ordered_clean_select_dist)
## [1] 12502     8
# Préparer le tableau edges V2
edges <- stops_ordered_clean_select_dist %>%
  filter(!is.na(distance_to_next)) %>%          # suppression des valeurs manquantes
  group_by(route_id) %>%                        # Grouper par itinéraire
  mutate(target_stop_name = lead(stop_name),
         target_stop_sequence = lead(stop_sequence),
         target_stop_lat = lead(stop_lat),
         target_stop_lon = lead(stop_lon)) %>%
  ungroup() %>%                                 # Enlever le regroupement
  select(route_id, 
         route_long_name,
         source_stop_name = stop_name, 
         target_stop_name, 
         source_sequence = stop_sequence, 
         target_sequence = target_stop_sequence,
         source_lat = stop_lat, source_lon = stop_lon, 
         target_lat = target_stop_lat, target_lon = target_stop_lon, 
         weight = distance_to_next) %>%
  filter(!is.na(target_stop_name))  # Filtre pour enlever la dernière ligne de chaque groupe qui n'a pas de 'target'

edges_closeness <- edges %>%
  filter(source_stop_name != target_stop_name)

# Afficher le résultat
dim(edges_closeness)
## [1] 2758   11
head(edges_closeness)
## # A tibble: 6 × 11
##   route_id     route_long_name source_stop_name target_stop_name source_sequence
##   <chr>        <chr>           <chr>            <chr>                      <int>
## 1 FR:Line::0f… Paris Nord - C… Lille Flandres   Douai                          0
## 2 FR:Line::0f… Paris Nord - C… Douai            Arras                          1
## 3 FR:Line::0f… Paris Nord - C… Arras            Creil                          2
## 4 FR:Line::0f… Paris Nord - C… Creil            Arras                          2
## 5 FR:Line::0f… Paris Nord - C… Arras            Longueau                       2
## 6 FR:Line::0f… Paris Nord - C… Longueau         Creil                          3
## # ℹ 6 more variables: target_sequence <int>, source_lat <dbl>,
## #   source_lon <dbl>, target_lat <dbl>, target_lon <dbl>, weight <dbl>
edges_graph_data <- edges_closeness %>%
  select(source = source_stop_name, target = target_stop_name, weight)

dim(edges_graph_data)
## [1] 2758    3
head(edges_graph_data)
## # A tibble: 6 × 3
##   source         target    weight
##   <chr>          <chr>      <dbl>
## 1 Lille Flandres Douai     29505.
## 2 Douai          Arras     23849.
## 3 Arras          Creil    116136.
## 4 Creil          Arras    116136.
## 5 Arras          Longueau  56183.
## 6 Longueau       Creil     67363.
# Préparer les données pour iGraph
g <- graph_from_data_frame(d = edges_graph_data)

# Calcul du score centralité de proximité
closeness_scores <- closeness(g, mode = "all", weights = E(g)$weight)

closeness_df <- data.frame(
  stop_name = V(g)$name,
  closeness = closeness_scores
)

# Afficher les résultats
head(closeness_df,20)
##                                     stop_name    closeness
## Lille Flandres                 Lille Flandres 3.681793e-08
## Douai                                   Douai 3.974791e-08
## Arras                                   Arras 4.099381e-08
## Creil                                   Creil 2.705105e-08
## Longueau                             Longueau 3.517602e-08
## Hirson                                 Hirson 2.261407e-08
## Hirson Écoles                   Hirson Écoles 2.266080e-08
## Anor                                     Anor 2.366464e-08
## Fourmies                             Fourmies 2.441988e-08
## Sains-du-Nord                   Sains-du-Nord 2.607359e-08
## Avesnes-sur-Helpe           Avesnes-sur-Helpe 2.742327e-08
## Avesnelles                         Avesnelles 2.716073e-08
## Aulnoye-Aymeries             Aulnoye-Aymeries 2.985351e-08
## Saint-Hilaire                   Saint-Hilaire 2.765822e-08
## Dompierre                           Dompierre 2.820958e-08
## Leval                                   Leval 2.945566e-08
## Le Quesnoy                         Le Quesnoy 3.065550e-08
## Le Poirier Université   Le Poirier Université 3.241083e-08
## Béthune                               Béthune 3.718357e-08
## Saint-Pol-sur-Ternoise Saint-Pol-sur-Ternoise 3.273661e-08
tail(closeness_df,20)
##                                     stop_name    closeness
## Baisieux                             Baisieux 3.254482e-08
## Ascq                                     Ascq 3.437678e-08
## Pont de bois                     Pont de bois 3.512468e-08
## Annappes                             Annappes 3.466833e-08
## Hellemmes                           Hellemmes 3.546148e-08
## Lezennes                             Lezennes 3.578599e-08
## Trith-Saint-Léger           Trith-Saint-Léger 3.283934e-08
## Prouvy - Thiant               Prouvy - Thiant 3.249649e-08
## Denain                                 Denain 3.183329e-08
## Lourches                             Lourches 3.217624e-08
## Bouchain                             Bouchain 3.264395e-08
## Iwuy                                     Iwuy 3.400536e-08
## Escaudoeuvres                   Escaudoeuvres 3.569228e-08
## Pihen                                   Pihen 2.186577e-08
## Caffiers                             Caffiers 2.055701e-08
## Le Haut Banc                     Le Haut Banc 2.106342e-08
## Dannes - Camiers             Dannes - Camiers 2.538186e-08
## Pont de Briques               Pont de Briques 2.309114e-08
## Ormoy-Villers                   Ormoy-Villers 2.252836e-08
## Bailleul-Sir-Berthoult Bailleul-Sir-Berthoult 3.751580e-08
# Combiner les données de closeness centrality avec les coordonnées des gares
gares_sf <- stops_select_sf_inter_select %>%
  left_join(closeness_df, by = "stop_name")
names(gares_sf)
## [1] "stop_id"   "stop_name" "closeness" "geometry"
# Convertir gares_sf en un objet sf <
gares_sf <- st_as_sf(gares_sf, coords = c("stop_lon", "stop_lat"), crs = 4326)

# Création de la heatmap avec ggplot2
ggplot(data = gares_sf) +
  geom_sf(aes(color = closeness), size = 3) +
  scale_color_viridis_c(option = "A") +
  labs(title = "Heatmap de la Closeness Centrality des Gares",
       subtitle = "Chaque gare est colorée selon son score de closeness centrality") +
  theme_minimal() +
  theme(legend.title = element_text(size = 12),
        legend.text = element_text(size = 10))

lourches <- gares_sf %>% 
  filter(stop_name == 'Lourches')
mapview(lourches)

Lille Flandres est l’arrêt avec le score de proximité le plus élevé, ce qui suggère qu’il est le plus central ou le mieux connecté parmi tous les arrêts listés. C’est dû à sa position stratégique dans le réseau de transport et au nombre élevé de connexions disponibles à partir de cet arrêt.

En observant les scores de proximité les plus bas (par exemple, Prouvy - Thiant, Denain ou Lourches), on note une diminution significative, ce qui pourrait indiquer des arrêts moins centraux ou plus éloignés des principaux axes de transport.

Bibliographie


Pour plus de détails sur la théorie des graphs et le closeness centrality vous pouvez consulter ce lien.


OSRM

OSRM ISOCHRONES

  • Création d’isochrones avec osrmIsochrone
pacman::p_load(sf, osrm)

gares_rennes_chateau <- sf::st_read('C:/Users/Othaureau/Documents/BDD/GEOFER/gares_rennes_chateaubriant.gpkg')
gares_rennes_chateau <- gares_rennes_chateau %>% 
  st_transform(4326)

# Assurez-vous que vos points sont au format sf et en WGS84
points_sf <- st_as_sf(gares_rennes_chateau, coords = c("lon", "lat"), crs = 4326)

# Initialiser une liste pour stocker les résultats des isochrones
list_isochrones <- list()

# Boucle pour calculer les isochrones pour chaque point
for (i in 1:nrow(points_sf)) {
  # Extraire le point actuel
  point <- points_sf[i, ]
  
  # Calculer l'isochrone de 15 minutes à pied
  isochrone <- osrm::osrmIsochrone(loc = point, 
                                   returnclass = "sf", 
                                   breaks = 15,
                                   osrm.profile = 'foot') # the routing profile to use, e.g. "car", "bike" or "foot" 
  
  # Stocker l'isochrone dans la liste
  list_isochrones[[i]] <- isochrone
}

##??osrmIsochrone

# Fusionner tous les isochrones dans un seul objet sf si nécessaire
all_isochrones <- do.call(rbind, list_isochrones)

mapview::mapview(all_isochrones)

# Visualisation (optionnel)
# Si vous voulez visualiser les isochrones, vous pouvez utiliser ggplot2 ou une autre librairie graphique
# library(ggplot2)
# ggplot() + 
#   geom_sf(data = points_sf, color = 'red') +
#   geom_sf(data = all_isochrones, fill = 'blue', alpha = 0.5)

# Enregistrer le résultat dans un fichier GeoJSON, par exemple
getwd()
# st_write(all_isochrones, "C:/Users/Othaureau/Documents/BDD/ISOCHRONES_OSRM/isochrones.geojson")

Carto de l’isochrone avec le moins bon score

pk_avec_data <- sf::st_read('processed_data/all_data_standardized_stpol_foot_p15_20240222.gpkg')
## Reading layer `all_data_standardized_stpol_foot_p15_20240222' from data source 
##   `C:\Users\otheureaux\Documents\OT\6T\R\database_sf\processed_data\all_data_standardized_stpol_foot_p15_20240222.gpkg' 
##   using driver `GPKG'
## Simple feature collection with 140 features and 22 fields
## Geometry type: POINT
## Dimension:     XY
## Bounding box:  xmin: 603303 ymin: 7019253 xmax: 683443.5 ymax: 7048245
## Projected CRS: RGF93 v1 / Lambert-93
isochrones_p15min <- sf::st_read('C:/Users/otheureaux/Documents/OT/6T/R/database_sf/processed_data/isochrones_stpol_foot_15min_20240208.geojson')
## Reading layer `isochrones_stpol_foot_15min_20240208' from data source 
##   `C:\Users\otheureaux\Documents\OT\6T\R\database_sf\processed_data\isochrones_stpol_foot_15min_20240208.geojson' 
##   using driver `GeoJSON'
## Warning in CPL_read_ogr(dsn, layer, query, as.character(options), quiet, : GDAL
## Message 1: Several features with id = 1 have been found. Altering it to be
## unique. This warning will not be emitted anymore for this layer
## Simple feature collection with 140 features and 4 fields
## Geometry type: MULTIPOLYGON
## Dimension:     XY
## Bounding box:  xmin: 1.62607 ymin: 50.26405 xmax: 2.777442 ymax: 50.53843
## Geodetic CRS:  WGS 84
isochrones_p15min <- isochrones_p15min %>% 
  st_transform(2154)
pk_min <- pk_avec_data %>% 
  filter(total_sum == min(pk_avec_data$total_sum))

intersections <- sf::st_intersects(st_buffer(pk_min, 10), isochrones_p15min)

# Extraire les isochrones correspondants
iso_min <- isochrones_p15min[unlist(intersections), ]

iso_min
## Simple feature collection with 1 feature and 4 fields
## Geometry type: MULTIPOLYGON
## Dimension:     XY
## Bounding box:  xmin: 658120.8 ymin: 7036632 xmax: 659038.9 ymax: 7037982
## Projected CRS: RGF93 v1 / Lambert-93
##    id isomin isomax point.ID_number                       geometry
## 29  1      0     15          283213 MULTIPOLYGON (((658754.9 70...
pacman::p_load(tmap)
tmap_mode('view')
## tmap mode set to interactive viewing
tm_basemap(c(leaflet::providers$Esri.WorldTopoMap,
             leaflet::providers$OpenStreetMap, 
             leaflet::providers$Esri.WorldImageryiders, 
             leaflet::providers$GeoportailFrance.orthos)) + 
  tm_shape(st_union(iso_min)) + tm_fill(col = '#75a993',
                                                    alpha = 0.5) +
  tm_shape(pk_min) + tm_dots()

Carto de l’isochrone avec le meilleur score

pk_max <- pk_avec_data %>% 
  filter(total_sum == max(pk_avec_data$total_sum))

intersections <- sf::st_intersects(pk_max, isochrones_p15min)

# Extraire les isochrones correspondants
iso_max <- isochrones_p15min[unlist(intersections), ]

iso_max
## Simple feature collection with 2 features and 4 fields
## Geometry type: MULTIPOLYGON
## Dimension:     XY
## Bounding box:  xmin: 673489.2 ymin: 7045960 xmax: 675356.2 ymax: 7048195
## Projected CRS: RGF93 v1 / Lambert-93
##   id isomin isomax point.ID_number                       geometry
## 1  1      0     15           17132 MULTIPOLYGON (((674436 7048...
## 6  1      0     15          282983 MULTIPOLYGON (((674639.7 70...
pacman::p_load(tmap)
tm_basemap(c(leaflet::providers$Esri.WorldTopoMap,
             leaflet::providers$OpenStreetMap, 
             leaflet::providers$Esri.WorldImageryiders, 
             leaflet::providers$GeoportailFrance.orthos)) + 
  tm_shape(st_union(iso_max)) + tm_fill(col = '#75a993',
                                                    alpha = 0.5) +
  tm_shape(pk_max) + tm_dots()

OSRM ROUTE

# Données OSRMROUTE 1 ----
pacman::p_load(sf, dplyr, mapview, geosphere, igraph, ggplot2, osrm, googlePolylines)

edges_100 <- edges_closeness[1:100,] 

depart_gare <- edges_100 %>% 
  select(source_stop_name, source_lat, source_lon)
source_gare_sf <- sf::st_as_sf(depart_gare, coords=c('source_lon',
                                                     'source_lat'))
st_crs(source_gare_sf) <- 'epsg:4326'
mapview(source_gare_sf)
depart_lon <- st_coordinates(source_gare_sf)[2, 1]
depart_lat <- st_coordinates(source_gare_sf)[2, 2]

destination_gare <- edges_100 %>% 
  select(target_stop_name, target_lat, target_lon)
destination_gare_sf <- sf::st_as_sf(destination_gare, 
                                    coords=c( 
                                             'target_lon',
                                             'target_lat'))
st_crs(destination_gare_sf) <- 'epsg:4326'
destination_lat <- st_coordinates(destination_gare_sf)[2, 1]
destination_lon <- st_coordinates(destination_gare_sf)[2, 2]

mapview(destination_gare_sf)
loc <- cbind(source_gare_sf,
             destination_gare_sf)
st_crs(loc) <- 'epsg:4326'

# Initialiser une liste pour stocker les résultats des routes
list_routes <- list()

# Boucle pour calculer les isochrones pour chaque point
for (i in 1:nrow(source_gare_sf)) {
  depart_lon <- st_coordinates(source_gare_sf)[i, 1]
  depart_lat <- st_coordinates(source_gare_sf)[i, 2]
  destination_lon <- st_coordinates(destination_gare_sf)[i, 1]
  destination_lat <- st_coordinates(destination_gare_sf)[i, 2]
route <- osrmRoute(
  src = c(depart_lon, depart_lat),
  dst = c(destination_lon, destination_lat),
  overview = "simplified",
  osrm.server = getOption("osrm.server"), 
                          #default = "https://router.project-osrm.org/"),
  osrm.profile = "foot")
# Stocker la route dans la liste
  list_routes[[i]] <- route
}
# Fusionner tous les routes dans un seul objet sf si nécessaire
all_routes <- do.call(rbind, list_routes)

mapview::mapview(all_routes) + 
  mapview(source_gare_sf)
# Données OSRMROUTE 2 ----
data_OD_DEP51 <- sf::st_read('data/OD_DEP51.csv')
## Reading layer `OD_DEP51' from data source 
##   `C:\Users\otheureaux\Documents\OT\6T\R\database_sf\data\OD_DEP51.csv' 
##   using driver `CSV'
## Warning: no simple feature geometries present: returning a data.frame or tbl_df
names(data_OD_DEP51)
## [1] "field_1" "COMMUNE" "DCLT"
communes <- sf::st_read('C:/Users/otheureaux/Documents/OT/DECOUPAGES_FR/COMMUNE.shp')
## Reading layer `COMMUNE' from data source 
##   `C:\Users\otheureaux\Documents\OT\DECOUPAGES_FR\COMMUNE.shp' 
##   using driver `ESRI Shapefile'
## Simple feature collection with 34816 features and 11 fields
## Geometry type: MULTIPOLYGON
## Dimension:     XY
## Bounding box:  xmin: 99040 ymin: 6046546 xmax: 1242443 ymax: 7110479
## Projected CRS: RGF93 Lambert 93
names(communes)
##  [1] "ID"         "NOM"        "NOM_M"      "INSEE_COM"  "STATUT"    
##  [6] "POPULATION" "INSEE_CAN"  "INSEE_ARR"  "INSEE_DEP"  "INSEE_REG" 
## [11] "SIREN_EPCI" "geometry"
# source
source_gare <- data.frame(data_OD_DEP51$COMMUNE)
names(source_gare)
## [1] "data_OD_DEP51.COMMUNE"
source_gare <- source_gare %>% 
  dplyr::rename(INSEE_COM = data_OD_DEP51.COMMUNE)
names(source_gare)
## [1] "INSEE_COM"
source_gare_sf <- right_join(communes,
                             source_gare,
                             by = 'INSEE_COM') %>% 
  st_transform(4326) %>% 
  st_centroid()
## Warning: st_centroid assumes attributes are constant over geometries
mapview(source_gare_sf)
# destination
destination_gare <- data.frame(data_OD_DEP51$DCLT)
names(destination_gare)
## [1] "data_OD_DEP51.DCLT"
destination_gare <- destination_gare %>% 
  dplyr::rename(INSEE_COM = data_OD_DEP51.DCLT)
names(destination_gare)
## [1] "INSEE_COM"
destination_gare_sf <- right_join(communes,
                             destination_gare,
                             by = 'INSEE_COM')%>% 
  st_transform(4326)%>% 
  st_centroid()
## Warning: st_centroid assumes attributes are constant over geometries
mapview(destination_gare_sf)
# Initialiser une liste pour stocker les résultats des routes
list_routes <- list()

# Boucle pour calculer les routes pour chaque point
# for (i in 1:nrow(source_gare_sf))
for (i in 1:10) {
  depart_lon <- st_coordinates(source_gare_sf)[i, 1]
  depart_lat <- st_coordinates(source_gare_sf)[i, 2]
  destination_lon <- st_coordinates(destination_gare_sf)[i, 1]
  destination_lat <- st_coordinates(destination_gare_sf)[i, 2]
  route <- osrmRoute(
    src = c(depart_lon, depart_lat),
    dst = c(destination_lon, destination_lat),
    overview = "simplified",
    osrm.server = getOption("osrm.server"), 
    #default = "https://router.project-osrm.org/"),
    osrm.profile = "car")
  # Stocker la route dans la liste
  list_routes[[i]] <- route
}
# Fusionner tous les routes dans un seul objet sf si nécessaire
all_routes <- do.call(rbind, list_routes)

all_route_distinct <- all_routes %>% 
  distinct(distance, .keep_all = T)
mapview::mapview(all_routes) + mapview(source_gare_sf)
# Enregistrer les données
# sf::st_write(all_routes, 
#              'C:/Users/otheureaux/Downloads/all_route_test1.gpkg')

r5r

Etape 1 : Installer la bonne version de JAVA (version 11)

  • Après avoir installé Java, assurez-vous que le chemin d’installation de Java est correctement défini. Pour ce faire aller dans Système>Informations système>Paramètres avancés du système>Variables d’environnement>Variables systèmes.
  1. dans l’environnement Variables systèmes : variable d’environnement JAVA_HOME : C:/Program Files/Java/jdk-11/ (Assurez-vous de ne pas inclure bin à la fin du chemin JAVA_HOME) ET PATH : C:/Program Files/Java/jdk-11/bin (chemin du dossier bin de Java).

  2. dans l’environnement Variables utilisateurs : variable d’environnement PATH : C:/Program Files/Java/jdk-11/bin (chemin du dossier bin de Java).

Etape 2 : La dernière version de R doit être installé

Pour connaître votre version de R : R.home()) Version nécessaire : R version 4.3.2 (2023-10-31 ucrt)

  • Configurer R_HOME

  • Installer la dernière version de R

Après avoir installé R, assurez-vous que le chemin d’installation de R est correctement défini dans la variable d’environnement R_HOME/
* Sur Windows, R_HOME doit être défini pour pointer vers le répertoire d’installation de R, comme C:/Program Files/R/R-4.3.2 Pour ce faire aller dans Système>Informations système>Paramètres avancés du système>Variables d’environnement>Variables systèmes. Après avoir défini R_JAVA, redémarrez votre ordinateur.

Après avoir défini JAVA_HOME R_HOME et les bons Path, redémarrez votre ordinateur.

  • Configurez R Studio

  • Pour utiliser la dernière version de R avec RStudio sur Windows : Lancez RStudio. Accéder aux Options Globales. Allez dans Tools (Outils) dans la barre de menu. Sélectionnez Global Options (Options Globales). Changer la Version de R : Dans la fenêtre Options, cliquez sur General (Général) dans la liste de gauche. Sous R version, cliquez sur Change (Modifier) pour choisir une version différente de R. Une liste des versions de R installées sur votre système devrait apparaître. Sélectionnez la version que vous venez d’installer. Appliquer et Redémarrer RStudio : Cliquez sur OK pour enregistrer vos modifications. Vous devrez peut-être redémarrer RStudio pour que les changements prennent effet. Vérifier la Version de R dans RStudio : Après avoir redémarré RStudio, vous pouvez vérifier que la bonne version de R est utilisée en exécutant R.version.string ou sessionInfo() dans la console.

Etape 3 : Ouvrez RStudio et installez r5r et rJava

Script d’exemple : Source : https://ipeagit.github.io/intro_access_book/3_calculando_acesso.en.html

options(java.parameters = "-Xmx2G")

pacman::p_load(r5r, rJava)
data_path <- system.file("extdata/poa", package = "r5r")
data_path
fs::dir_tree(data_path)


r5r_core <- setup_r5(data_path, verbose = FALSE)
fs::dir_tree(data_path)

# read data.frame with grid centroids
points <- data.table::fread(file.path(data_path, "poa_hexgrid.csv"))
mapview::mapview(points)

ttm <- travel_time_matrix(
  r5r_core,
  origins = points,
  destinations = points,
  mode = c("WALK", "TRANSIT"),
  departure_datetime = as.POSIXct(
    "13-05-2019 14:00:00",
    format = "%d-%m-%Y %H:%M:%S"
  ),
  max_walk_time = 30,
  max_trip_duration = 120,
  verbose = FALSE,
  progress = FALSE
)

head(ttm)

Calcul d’isochrones avec r5r sur la ligne Saint-Pol-sur-Ternoise

# Avant tout
options(java.parameters = "-Xmx2G")

pacman::p_load(r5r, sf, data.table, ggplot2, tmap)

# Télécharger les données au format pbf (Protocolbuffer Binary Format)
Un fichier PBF est un format de fichier utilisé pour stocker des données géographiques, notamment celles issues d'OpenStreetMap'. 
Ici, vous pouvez trouver des fichiers pbf : https://download.geofabrik.de/

# Configurer le dossier où se trouve les éléments
r5r_core <- setup_r5(data_path = "C:/Users/otheureaux/Documents/OT/RAILENIUM/BDD_TELLI/OSM_PBF", verbose = FALSE)
# data_path est le chemin vers le dossier contenant vos données de réseau.

# Chargement de l'emprise de la zone d'étude ----
zone_etude <- sf::st_read('C:/Users/otheureaux/Documents/OT/RAILENIUM/BDD_TELLI/ISOCHRONES/isochrones_stpol_car_20min_groupe.gpkg')
zone_etude <- sf::st_transform(zone_etude, 2154)

# Chargement de la couche des points kilométriques ----
points_kms <- sf::st_read('C:/Users/otheureaux/Documents/OT/RAILENIUM/BDD_TELLI/PT_KMS/pk_etoile_de_saintpol.gpkg')
# points_kms <- st_transform(points_kms, 2154)
points_kms <- points_kms %>% mutate(ID_number = row_number())

# Convertir les valeurs de 'pk' en numérique
pk_num <- as.numeric(points_kms$pk)

# Créer un vecteur logique pour filtrer les pk entiers et ceux se terminant par ,5
filter_condition <- pk_num %% 1 == 0

# Filtrer la collection de fonctions simples en utilisant la condition
filtered_points_kms <- points_kms[filter_condition, ]

# Découpage de la couche des points kilométriques
points_kms_decoup <- sf::st_intersection(filtered_points_kms,zone_etude) 

# Ajouter une colonne 'id' à vos points d'origine
points_kms_decoup$id <- 1:nrow(points_kms_decoup)

# Vérifiez les premières lignes pour confirmer l'ajout de 'id'
head(points_kms_decoup)

# Transformation en WGS84
points_kms_decoup <- st_transform(points_kms_decoup, 4326)

# Extraire les coordonnées et créer des colonnes 'lat' et 'lon'
points_kms_decoup$lat <- st_coordinates(points_kms_decoup)[, "Y"]
points_kms_decoup$lon <- st_coordinates(points_kms_decoup)[, "X"]

# Vérifier le résultat
head(points_kms_decoup)

# sf::st_write(points_kms_decoup_light_wgs84, 'processed_data/points_kms_decoup_light_wgs84_v1.gpkg')

# routing inputs
mode <- c("WALK")
max_trip_duration <- 15      # in minutes

# calculate travel time matrix
iso1 <- r5r::isochrone(r5r_core,
                       origins = points_kms_decoup,
                       mode = mode,
                       sample_size = 1,
                       max_trip_duration = max_trip_duration,
                       walk_speed = 5,
                       progress = FALSE)

iso1_filter <- iso1 %>% 
  filter(isochrone == 15)
sf::st_write(iso1_filter, 'processed_data/r5r_iso_15min_5km_20240117.gpkg')


# extract OSM network
street_net <- street_network_to_sf(r5r_core)
main_roads <- subset(street_net$edges, street_class %like% 'PRIMARY|SECONDARY')
  
colors <- c('#ffe0a5','#ffcb69','#ffa600','#ff7c43','#f95d6a',
            '#d45087','#a05195','#665191','#2f4b7c','#003f5c')

ggplot() +
  geom_sf(data = iso1, aes(fill=factor(isochrone)), color = NA, alpha = .7) +
  geom_sf(data = main_roads, color = "gray55", size=0.01, alpha = 0.2) +
   # scale_fill_viridis_d(direction = -1, option = 'B') +
  scale_fill_manual(values = rev(colors) ) +
  scale_color_manual(values=c('Central bus\nstation'='black')) +
  labs(fill = "Travel time\n(in minutes)", color='') +
  theme_minimal() +
  theme(axis.title = element_blank())



# Création de la carte avec tmap
tmap_mode('view')
tm_shape(iso1_select) +
  tm_polygons("isochrone", id = "id", palette = rev(colors), border.col = NA, alpha = .7) +
  tm_shape(main_roads) +
  tm_lines(col = "gray55", lwd = 0.1, alpha = 0.2) +
  tm_layout(
    legend.title.size = 0.8,
    legend.text.size = 0.8,
    bg.color = "white",
    frame = FALSE,
    legend.position = c("left", "bottom")
  )

QGISPROCESS

# Chargement de la library
pacman::p_load(qgisprocess)

# Permet de créer un vecteur avec tous les algorithmes
qgis_algo <- qgis_algorithms()

# Rechercher un traitement spécifique
grep("intersect", qgis_algo$algorithm, value = T)
grep("grass", qgis_algo$algorithm, value = T)
qgis_algo$provider_title

qgis_configure()

# Montrer l'aide
qgis_show_help("native:slope")

# Exemple avec le calcul de pente de plusieurs isochrones
for(i in 1:nrow(all_isochrones)) {
  mnt_intersection <- crop(ras, all_isochrones[i,])
  a <- mask(mnt_intersection, all_isochrones[i,])
  b <- qgis_run_algorithm("native:slope", 
    INPUT = a, 
    OUTPUT = paste('C:/Users/otheureaux/Documents/OT/RAILENIUM/DONNEES_CARTO/raster/mnt_62/', i, ".tif"))
  b_ras <- raster(paste('C:/Users/otheureaux/Documents/OT/RAILENIUM/DONNEES_CARTO/raster/mnt_62/', i, ".tif"))
  mean <- raster::cellStats(b_ras, stat = "mean", na.rm = TRUE)
  sum <- data.frame(pente_moyenne = mean, 
                    ID_number = all_isochrones$point.ID_number[i])
  all_sum[[i]] <- sum
    }  

BPE

pacman::p_load(sf)

BPE <- read.csv2("C:/Users/otheureaux/Documents/OT/RAILENIUM/BDD_TELLI/BPE/bpe21_ensemble_xy_csv/bpe21_ensemble_xy.csv")
BPE_VARMOD <- read.csv2("C:/Users/otheureaux/Documents/OT/RAILENIUM/BDD_TELLI/BPE/bpe21_ensemble_xy_csv/Varmod_bpe21_ensemble_xy.csv")
BPE_VARMOD_commerces <- BPE_VARMOD %>% 
  filter(stringr::str_starts(COD_MOD, 'B'))
names(BPE_VARMOD_commerces)
(BPE_VARMOD_commerces$LIB_MOD)

BPE_clean <- BPE %>%
  dplyr::filter(LAMBERT_X != "", LAMBERT_Y != "", 
                !is.na(LAMBERT_X), !is.na(LAMBERT_Y))
BPE_clean_25 <- BPE_clean %>% 
  filter(DEP == 25)

BPE_clean_25_resto <- BPE_clean_25 %>% 
  filter(stringr::str_starts(TYPEQU, 'A504')) # RESTAURANT- RESTAURATION RAPIDE

BPE_clean_25_resto_sf <- BPE_clean_25_resto %>%
  sf::st_as_sf(coords = c("LAMBERT_X", "LAMBERT_Y"), crs = 2154)
BPE_clean_25_resto_sf_intersection <- st_intersection(BPE_clean_25_resto_sf, zone_etude)

mapview(BPE_clean_25_resto_sf_intersection)

BPE_clean_25_commerces <- BPE_clean_25 %>% 
  filter(stringr::str_starts(TYPEQU, 'B')) 
#  "Commerces"                                      "Grandes surfaces"                           
#  "Commerces alimentaires"                         "Commerces spécialisés non-alimentaires"     
#  "HYPERMARCHÉ"                                    "SUPERMARCHÉ"                                
#  "GRANDE SURFACE DE BRICOLAGE"                    "SUPÉRETTE"                                  
#  "ÉPICERIE"                                       "BOULANGERIE"                                
#  "BOUCHERIE CHARCUTERIE"                          "PRODUITS SURGELÉS"                           
#  "POISSONNERIE"                                   "LIBRAIRIE, PAPETERIE, JOURNAUX"             
#  "MAGASIN DE VÊTEMENTS"                           "MAGASIN D’ÉQUIPEMENTS DU FOYER"             
#  "MAGASIN DE CHAUSSURES"                          "MAGASIN D’ÉLECTROMÉNAGER ET DE MAT. AUDIO-VIDEO"
#  "MAGASIN DE MEUBLES"                             "MAGASIN D’ARTICLES DE SPORTS ET DE LOISIRS" 
#  "MAGASIN DE REVÊTEMENTS MURS ET SOLS"            "DROGUERIE QUINCAILLERIE BRICOLAGE"           
#  "PARFUMERIE-COSMÉTIQUE"                          "HORLOGERIE-BIJOUTERIE"                      
#  "FLEURISTE-JARDINERIE-ANIMALERIE"                "MAGASIN D’OPTIQUE"                          
#  "MAGASIN DE MATÉRIEL MÉDICAL ET ORTHOPÉDIQUE     "STATION-SERVICE" 

names(BPE_clean_25_commerces)
table(BPE_clean_25_commerces$TYPEQU)

names(BPE_clean_25_commerces)
table(BPE_clean_25_commerces$TYPEQU)
BPE_clean_25_commerces_sf <- BPE_clean_25_commerces %>%
  sf::st_as_sf(coords = c("LAMBERT_X", "LAMBERT_Y"), crs = 2154)

mapview(BPE_clean_25_commerces_sf)

IGN WFS

## Packages
pacman::p_load(sf, dplyr, httr, purrr)

## Choix du flux:  REGION, COMMUNE, EPCI, BATIMENT, ROUTES, RIVIERE, VOIE FERRE
url_bd <- "https://wxs.ign.fr/essentiels/geoportail/wfs?VERSION=2.0.0"

# DEPARTEMENT
# url_bd <- "https://wxs.ign.fr/topographie/geoportail/wfs?SERVICE=WFS&VERSION=2.0.0&REQUEST=GetCapabilities"

# IRIS
# url_bd <- "https://wxs.ign.fr/cartovecto/geoportail/wfs?SERVICE=WFS&VERSION=2.0.0&REQUEST=GetCapabilities"

## Interroger le contenu du lien, afficher tout le contenu
ign_client <- ows4R::WFSClient$new(url_bd, serviceVersion = "2.0.0") 
ign_client$getFeatureTypes(pretty = TRUE)
# options(max.print = 1000)
## Chercher une variable par son nom 
bd_vrbg <- ign_client$getCapabilities()

a <- bd_vrbg$getFeatureTypes(pretty = TRUE)
b <- a%>%filter(grepl(pattern = "batiment", x = name))
str(b)
## Rechercher les métadonnées d'une variable
ign_client$
  getCapabilities()$
  findFeatureTypeByName("BDTOPO_V3:troncon_de_route")$
  getDescription() %>%
  purrr::map_chr(function(x){x$getName()})
## isoler des données de la BDTOPO avec une bounding box
parse_url <- httr::parse_url(url_bd)
parse_url$query <- list(service = "WFS",
                        #version = "2.0.0", # optional
                        request = "GetFeature",
                        typename = "BDCARTO_BDD_WLD_WGS84G:commune", # type disponible ici : ign_client$getFeatureTypes(pretty = TRUE)
                        cql_filter = c("insee_com='34172'")) # J'utilise insee_com repéré à l'étape précédente pour charger l'emprise de Montpellier
                        # cql_filter = paste("code_insee='77316'", "code_insee='77186'", "code_insee='77463'", "code_insee='77419'", "code_insee='77014'", "code_insee='77079'", sep = " OR "))
                        # bbox = "48.88417,2.34466,48.90956,2.38308") # epsg:2154
request <- st_read(build_url(parse_url))

TRANSFORMATION

  • La standardisation (scores z) se concentre sur la reconfiguration des données pour qu’elles aient une moyenne de 0 et un écart-type de 1, rendant ainsi la distribution des données standardisée en termes d’unités d’écart-type. Elle est utile pour comparer des scores entre différentes échelles et est souvent préférée dans les analyses qui supposent une distribution normale des données.

  • La normalisation Min-Max redimensionne les données dans un intervalle fixe, souvent [0, 1], en ajustant chaque valeur selon les valeurs minimales et maximales de l’ensemble de données, ce qui est utile pour les modèles sensibles aux variations d’échelle entre les variables. Elle est particulièrement utile pour les algorithmes sensibles aux échelles des variables, comme les algorithmes basés sur les distances. La normalisation Min-Max est souvent utilisée dans les contextes où l’uniformité de l’échelle entre les variables est cruciale.

  • La normalisation par la valeur maximale ajuste les données selon la plus grande valeur absolue de l’ensemble de données, rendant toutes les valeurs relatives à cette valeur maximale, ce qui est une approche simplifiée de mise à l’échelle par rapport à une valeur de référence unique. Elle est similaire à la normalisation Min-Max mais se concentre uniquement sur le redimensionnement par rapport à la valeur maximale de chaque variable. Si toutes les valeurs sont positives, les données varient entre 0 et 1. La normalisation par la valeur maximale peut être préférée pour sa simplicité et lorsque la dominance de la valeur maximale est significative pour l’analyse.

VANOISE

Tour de la Vanoise 2014

N1 : refuge de la Leisse
N2 : refuge du plan sec
N3 : refuge de l’Aiguille Doran
N4 : refuge des Barmettes
N5 : refuge de Vallonbrun
N6 : refuge du fond des fours

## tmap mode set to interactive viewing
## Reading layer `dep_arv' from data source 
##   `C:\Users\otheureaux\Documents\OT\6T\R\database_sf\data\dep_arv.gpkg' 
##   using driver `GPKG'
## Simple feature collection with 8 features and 1 field
## Geometry type: MULTIPOINT
## Dimension:     XY
## Bounding box:  xmin: 988339.2 ymin: 6464848 xmax: 1012218 ymax: 6492680
## Projected CRS: RGF93 v1 / Lambert-93
## Reading layer `trace_vanoise' from data source 
##   `C:\Users\otheureaux\Documents\OT\6T\R\database_sf\data\trace_vanoise.gpkg' 
##   using driver `GPKG'
## Simple feature collection with 1 feature and 0 fields
## Geometry type: LINESTRING
## Dimension:     XY
## Bounding box:  xmin: 986821.2 ymin: 6464837 xmax: 1016253 ymax: 6492562
## Projected CRS: RGF93 v1 / Lambert-93

NEW ()

Citation

Pour citer ces travaux :

Olivier Theureaux. n.d. “Tutoriels R dans le cadre d’une mission de géomaticien au sein du LVMT”

LS0tDQp0aXRsZTogIiINCm91dHB1dDogaHRtbF9kb2N1bWVudA0KLS0tDQoNCmBgYHtyIHNldHVwLCBpbmNsdWRlPUZBTFNFfQ0Ka25pdHI6Om9wdHNfY2h1bmskc2V0KGVjaG8gPSBUUlVFKQ0KYGBgDQoNCiMgVFVUT1JJRUxTIHsudGFic2V0IC51bm51bWJlcmVkfQ0KDQpDZSBzaXRlIGEgcG91ciBidXQgZGUgcsOpZsOpcmVuY2VyIHVuIGVuc2VtYmxlIGRlIGRvbm7DqWVzIHNwYXRpYWxlcyB1dGlsaXPDqSBkYW5zIGRlcyBhbmFseXNlcyBzcGF0aWFsZXMuDQoNCiMjIERlZ3JlZSBjZW50cmFsaXR5DQoNCk5vdXMgc291aGFpdG9ucyDDqXZhbHVlciBs4oCZaW1wb3J0YW5jZSBkZXMgZ2FyZXMuIE5vdXMgY2FsY3Vsb25zIGxlIGRlZ3LDqSBkZSBjZW50cmFsaXTDqSBwb3VyIGlkZW50aWZpZXIgbGVzIGdhcmVzIGNsw6lzIGRhbnMgbGUgcsOpc2VhdSwgY+KAmWVzdC3DoC1kaXJlIGNlbGxlcyBheWFudCBsZSBwbHVzIGdyYW5kIG5vbWJyZSBkZSBjb25uZXhpb25zIGRpcmVjdGVzIGF2ZWMgZOKAmWF1dHJlcyBnYXJlcy4NCkxlcyBkb25uw6llcyB1dGlsaXPDqWVzIGRhbnMgbGVzIHNjcmlwdHMgc3VpdmFudHMgcHJvdmllbm5lbnQgZHUgc2l0ZSBPcGVuIERhdGEgU05DRiAoY29uc3VsdMOpIGxlIDE5IGV0IGxlIDIwIGbDqXZyaWVyIDIwMjQpLiAgIA0KDQpFbiB1dGlsaXNhbnQgbGVzIGRvbm7DqWVzIEdURlMsIE5vdXMgY2FydG9ncmFwaGlvbnMgbGUgcsOpc2VhdSBkZXMgIGdhcmVzIGRlcyBIYXV0cy1kZS1GcmFuY2Ugw6AgcGFydGlyIGR1IGZpY2hpZXIgc3RvcHMudHh0LiBFbnN1aXRlLCBncsOiY2UgYXUgZmljaGllciBzdG9wX3RpbWVzLnR4dCwgbm91cyByZWxpb25zIGxlcyBJRCBkZXMgZ2FyZXMgw6AgZGVzICp0cmlwcyogKHBhc3NhZ2VzKSBzcMOpY2lmaXF1ZXMsIGluZGlxdWFudCB0b3VzIGxlcyBwYXNzYWdlcyBkZSB2w6loaWN1bGVzIGRhbnMgdW5lIGdhcmUgc3VyIHVuZSBqb3VybsOpZS4gTGUgZmljaGllciAqdHJpcHMqIHBlcm1ldCBlbnN1aXRlIGRlIHJlbGllciBjZXMgKnRyaXBzKiBhdXggSUQgZGVzICpyb3V0ZXMqLiBDaGFxdWUgcm91dGVzIHJlZ3JvdXBlbnQgcGx1c2lldXJzICp0cmlwcyogc291cyB1bmUgbcOqbWUgbGlnbmUgZGUgc2VydmljZS4gRmluYWxlbWVudCwgbGUgZmljaGllciAqcm91dGVzKiBmb3Vybml0IGxlcyBub21zIHLDqWVscyBkZXMgbGlnbmVzIChyb3V0ZV9sb25nX25hbWUpIGFzc29jacOpcyDDoCBjZXMgSUQgZGUgKnJvdXRlKi4NCg0KKlVzaW5nIEdURlMgZGF0YSwgd2UgYXJlIG1hcHBpbmcgdGhlIG5ldHdvcmsgb2YgVEVSIHN0YXRpb25zIGluIEhhdXRzLWRlLUZyYW5jZSBmcm9tIHRoZSBzdG9wcy50eHQgZmlsZS4gVGhlbiwgbGV2ZXJhZ2luZyBzdG9wX3RpbWVzLnR4dCwgd2UgY29ubmVjdCBzdGF0aW9uIElEcyB0byBzcGVjaWZpYyB0cmlwc+KAlGluZGljYXRpbmcgYWxsIHZlaGljbGUgcGFzc2VzIHRocm91Z2ggYSBzdGF0aW9uIGluIGEgZGF5LiBUaGUgdHJpcHMgZmlsZSB0aGVuIGxpbmtzIHRoZXNlIHRyaXBzIHRvIHJvdXRlIElEcywgZ3JvdXBpbmcgbXVsdGlwbGUgdHJpcHMgdW5kZXIgYSBzaW5nbGUgc2VydmljZSBsaW5lLiBGaW5hbGx5LCByb3V0ZXMudHh0IGdpdmVzIHlvdSB0aGUgYWN0dWFsIGxpbmUgbmFtZXMgKHJvdXRlX2xvbmdfbmFtZSkgYXNzb2NpYXRlZCB3aXRoIHRoZXNlIHJvdXRlIElEcywgZWZmZWN0aXZlbHkgbGlua2luZyBzdGF0aW9uIGFjdGl2aXRpZXMgdG8gdGhlIGJyb2FkZXIgbmV0d29yayBvZiByZWdpb25hbCBwdWJsaWMgdHJhbnNwb3J0IGxpbmVzLioNCg0KIyMjIExJR05FUyBURVINCg0KQSBwYXJ0aXIgZGVzIGRvbm7DqWVzIEdURlMgamUgY2hlcmNoZSBsZXMgSUQgZGVzIGdhcmVzIGRlcyBIYXV0cy1kZS1GcmFuY2UgZGlzcG9uaWJsZXMgZGFucyBsZSBmaWNoaWVyICpzdG9wcy50eHQqLg0KDQpgYGB7ciwgb3V0LndpZHRoPScxMDAlJ30NCnBhY21hbjo6cF9sb2FkKGRwbHlyLCBzZiwgc3RyaW5nciwgaWdyYXBoLCB0bWFwKQ0KIyBURVINCiMgw4l0YXBlIDEgOiBJZGVudGlmaWVyIGxlcyBnYXJlcw0Kc3RvcHMgPC0gcmVhZC5jc3YoJ2RhdGEvR1RGUy9leHBvcnQtdGVyLWd0ZnMtbGFzdC9zdG9wcy50eHQnLCBoZWFkZXIgPSBUUlVFKQ0KbmFtZXMoc3RvcHMpDQpzdG9wcyA8LSBzdG9wcyAlPiUgDQogIGZpbHRlcihncmVwbCgnVHJhaW4nLCBzdG9wX2lkKSkNCmRpbShzdG9wcykNCnN0b3BzX3NmIDwtIHN0b3BzICU+JQ0KICBzZjo6c3RfYXNfc2YoY29vcmRzID0gYygic3RvcF9sb24iLCAic3RvcF9sYXQiKSwgY3JzID0gNDMyNikgJT4lIA0KICBzdF90cmFuc2Zvcm0oMjE1NCkNCm1hcHZpZXc6Om1hcHZpZXcoc3RvcHNfc2YpDQoNCnJlZ2lvbnMgPC0gc2Y6OnN0X3JlYWQoImRhdGEvUkVHSU9OLnNocCIpDQpIREYgPC0gcmVnaW9ucyAlPiUgDQogIGZpbHRlcihJTlNFRV9SRUcgPT0gMzIpICMgZmlsdHJlIHN1ciBsZXMgSGF1dHMtZGUtRnJhbmNlDQoNCnN0b3BzX3NmX2hkZiA8LSBzZjo6c3RfaW50ZXJzZWN0aW9uKHN0b3BzX3NmLCBIREYpDQptYXB2aWV3OjptYXB2aWV3KHN0b3BzX3NmX2hkZikNCnN0b3BzIDwtIHN0b3BzX3NmX2hkZiAlPiUgDQogIHN0X2Ryb3BfZ2VvbWV0cnkoKQ0Kbm9tX2lkIDwtIGxpc3QoKQ0KDQpmb3IoIGkgaW4gMTpucm93KHN0b3BzKSkgew0KZ2FyZV9ub20gPC0gc3RvcHNbaSwnc3RvcF9uYW1lJ10gDQpnYXJlX2lkIDwtIHN0b3BzICU+JSANCiAgZmlsdGVyKHN0b3BfbmFtZSA9PSBnYXJlX25vbSkgDQpub21faWRbaV0gPC0gZ2FyZV9pZCRzdG9wX2lkDQp9DQoNCm5vbV9pZCA8LSB1bmxpc3Qobm9tX2lkKQ0KYGBgDQoNCioqw4l0YXBlIDIgOiBJZGVudGlmaWNhdGlvbiBkZXMgbm9tcyBkZXMgdHJhamV0cyoqDQoNCkxlIGZpY2hpZXIgKnN0b3BfdGltZXMqIGNvbnRpZW50IGxlcyBob3JhaXJlcyBkw6l0YWlsbMOpcyBkZXMgcGFzc2FnZXMgZGVzIHbDqWhpY3VsZXMgw6AgY2hhcXVlIGFycsOqdCBwb3VyIGNoYXF1ZSB0cmFqZXQuIEplIHBldXggZG9uYyBjb25uYcOudHJlIHRvdXMgbGVzICp0cmlwcyogc3IgdW5lIGpvdXJuw6llIHF1aSBwYXNzZSBkYW5zIHVuZSBnYXJlLiANCkdyw6JjZSDDoCBjZSBmaWNoaWVyIG9uIHBldXQgcmVsaWVyIGxlcyBub21zIGRlcyBhcnLDqnRzICgqc3RvcHMqKSBhdXggKnRyaXBzKi4gVW5lIGdhcmUgcGV1dCByZWNldm9pciB1biBvdSBwbHVzaWV1cnMgKnRyaXBzKiBvdSBwYXNzYWdlcy4NCg0KTGUgZmljaGllciAqdHJpcHMudHh0KiBjb250aWVudCBkZXMgaW5mb3JtYXRpb25zIHN1ciBsZXMgcGFzc2FnZXMgKG91ICJ0cmlwcyIpIGVmZmVjdHXDqXMgcGFyIGRlcyB2w6loaWN1bGVzIGF1IHNlaW4gZCd1biBzZXJ2aWNlIGRlIHRyYW5zcG9ydCBwdWJsaWMgKGV4IDogdW5lIGdhcmUpLiBHcsOiY2Ugw6AgY2UgZmljaGllciBvbiBwZXV0IHJlbGllciBsZXMgKnRyaXBzKiBhdSAqSUQgZGVzIHJvdXRlcyouIFNhY2hhbnQgcXUndW4gSUQgZGUgcm91dGVzIGNvbnRpZW50IHBsdXNpZXVycyAqdHJpcHMqLiAgDQoNCkxlIGZpY2hpZXIgKnJvdXRlcyogY29udGllbnQgbGVzIGluZm9ybWF0aW9ucyBjb25jZXJuYW50IGxlcyBsaWduZXMgKG91ICIqcm91dGUqcyIpIG9ww6lyw6llcyBwYXIgdW4gc2VydmljZSBkZSB0cmFuc3BvcnQgcHVibGljLiBEYW5zIGNlIGZpY2hpZXIgbm91cyBwb3V2b25zIHJldHJvdXZlciBsZSBub20gZGVzIGxpZ25lcyAoKnJvdXRlX2xvbmdfbmFtZSopIGNvcnJlc3BvbmQgYXV4IElEIHRyb3V2w6lzIGRhbnMgbGUgZmljaGllciAqdHJpcHMqLiAgICANCg0KDQpgYGB7ciwgb3V0LndpZHRoPScxMDAlJywgY2FjaGUgPSBUfQ0KDQpzdG9wX3RpbWVzIDwtIHJlYWQuY3N2KCdkYXRhL0dURlMvZXhwb3J0LXRlci1ndGZzLWxhc3Qvc3RvcF90aW1lcy50eHQnLCBoZWFkZXIgPSBUUlVFKQ0KdHJpcHMgPC0gcmVhZC5jc3YoJ2RhdGEvR1RGUy9leHBvcnQtdGVyLWd0ZnMtbGFzdC90cmlwcy50eHQnLCBoZWFkZXIgPSBUUlVFKQ0Kcm91dGVzIDwtIHJlYWQuY3N2KCdkYXRhL0dURlMvZXhwb3J0LXRlci1ndGZzLWxhc3Qvcm91dGVzLnR4dCcsIGhlYWRlciA9IFRSVUUpDQpgYGANCg0KKipFeGVtcGxlIHBvdXIgQWxiZXJ0KioNCg0KYGBge3J9DQpzdG9wc19maWx0ZXIgPC0gc3RvcHMgJT4lIA0KICBmaWx0ZXIoc3RvcF9uYW1lID09ICdBbGJlcnQnKQ0Kc3RvcHNfZmlsdGVyDQoNCiMgSWNpIG9uIGZpbHRyZSBzdXIgbGEgZ2FyZSBkZSBMaWxsZSBDZW50cmUgSG9zcG90YWxpZXIgUsOpZ2lvbmFsDQp0cmFqZXRzX2dhcmUgPC0gc3RvcF90aW1lcyAlPiUgZmlsdGVyKHN0b3BfaWQgPT0gJ1N0b3BQb2ludDpPQ0VUcmFpbiBURVItODczMTMwNzInKQ0KIyBPbiBvYnRpZW50IHRvdXMgbGVzIHRyYWlucyBxdWkgcydhcnLDqnRlbnQgZGFucyBjZXR0ZSBnYXJlDQojIGhlYWQodHJhamV0c19nYXJlKQ0KIyBkaW0odHJhamV0c19nYXJlKQ0KDQojIENoYXF1ZSB0cmFpbiBkaXNwbyBkJ3VuIHRyaXBfaWQNCnRyYWpldHNfZ2FyZSA8LSB0cmFqZXRzX2dhcmUkdHJpcF9pZA0KDQojIFRyaXBzDQojIEdyw6JjZSDDoCBsYSB0YWJsZSBUcmlwcyBvbiBwZXV0IHJldHJvdXZlciBsZXMgcm91dGVfaWQgcXVpIG5vdXMgcGVybWV0dHJvbnQgZGUgcmV0cm91dmVyIGxhIHJvdXRlX25hbWUgZGUgY2hhcXVlIHRyaXBzDQp0cmlwc19nYXJlIDwtIHRyaXBzICU+JQ0KICBmaWx0ZXIodHJpcF9pZCAlaW4lIHRyYWpldHNfZ2FyZSkNCmhlYWQodHJpcHNfZ2FyZSkNCnRyaXBzX2dhcmUgPC0gdHJpcHNfZ2FyZSRyb3V0ZV9pZA0KIyB0cmlwc19nYXJlDQoNCiMgw4l0YXBlIDMgOiBJZGVudGlmaWVyIGxlcyBsaWduZXMgY29ycmVzcG9uZGFudGVzDQpuYW1lcyhyb3V0ZXMpDQoNCmxpZ25lc19nYXJlcyA8LSByb3V0ZXMgJT4lIA0KICBmaWx0ZXIocm91dGVfaWQgJWluJSB0cmlwc19nYXJlKQ0KIyBDZXR0ZSBnYXJlIGRpc3Bvc2UgZGUgNCBsaWduZXMgZGUgc2VydmljZXMNCmxpZ25lc19nYXJlcw0KbnJvdyhsaWduZXNfZ2FyZXMpDQoNCmBgYA0KDQoqKkV4dHJhY3Rpb24gZGUgdG91dGVzIGxlcyBsaWduZXMqKg0KDQpgYGB7ciwgb3V0LndpZHRoPScxMDAlJ30NCmxpZ25lc19nYXJlc19uYiA8LSBsaXN0KCkNCg0KZm9yKCBpIGluIDE6bnJvdyhzdG9wcykpIHsNCnRyYWpldHNfZ2FyZSA8LSBzdG9wX3RpbWVzICU+JSBmaWx0ZXIoc3RvcF9pZCAlaW4lIG5vbV9pZFtpXSkNCmhlYWQodHJhamV0c19nYXJlKQ0KZGltKHRyYWpldHNfZ2FyZSkNCg0KdHJhamV0c19nYXJlIDwtIHRyYWpldHNfZ2FyZSR0cmlwX2lkDQoNCiMgVHJpcHMNCnRyaXBzX2dhcmUgPC0gdHJpcHMgJT4lDQogIGZpbHRlcih0cmlwX2lkICVpbiUgdHJhamV0c19nYXJlKQ0KaGVhZCh0cmFqZXRzX2dhcmUpDQpkaW0odHJhamV0c19nYXJlKQ0KdHJpcHNfZ2FyZSA8LSB0cmlwc19nYXJlJHJvdXRlX2lkDQp0cmlwc19nYXJlDQoNCiMgw4l0YXBlIDMgOiBJZGVudGlmaWVyIGxlcyBsaWduZXMgY29ycmVzcG9uZGFudGVzDQpuYW1lcyhyb3V0ZXMpDQoNCmxpZ25lc19nYXJlcyA8LSByb3V0ZXMgJT4lIA0KICBmaWx0ZXIocm91dGVfaWQgJWluJSB0cmlwc19nYXJlKQ0KbGlnbmVzX2dhcmVzDQpucm93KGxpZ25lc19nYXJlcykNCg0KbGlnbmVzX2dhcmVzX25iW2ldIDwtIG5yb3cobGlnbmVzX2dhcmVzKQ0KfQ0KbGlnbmVzX2dhcmVzX25iX3VubGlzdCA8LSB1bmxpc3QobGlnbmVzX2dhcmVzX25iKQ0KbGlnbmVzX2dhcmVzX25iX3VubGlzdA0KDQpnYXJlc19zZXJ2aWNlcyA8LSBkYXRhLmZyYW1lKHN0b3BzJHN0b3BfbmFtZSwgbGlnbmVzX2dhcmVzX25iX3VubGlzdCkNCmBgYA0KICANCkxlIHRhYmxlYXUgY2ktZGVzc291cyBjb21wdGUgbGUgbm9tYnJlIGRlIGxpZ25lcyBxdWkgcGFzc2VudCBwYXIgY2hhY3VuZSBkZXMgZ2FyZXMgZGVzIEhhdXRzLWRlLUZyYW5jZSA6ICAgDQogIA0KDQo8IS0tIGBgYHtyLCBvdXQud2lkdGg9JzEwMCUnfSAtLT4NCjwhLS0gZ2FyZXNfc2VydmljZXMgLS0+DQo8IS0tIGBgYCAtLT4NCg0KKipFeHRyYWN0aW9uIGRlcyBlZGdlcyBwb3VyIGlHcmFwaCoqDQoNCmBgYHtyfQ0KIyBJbml0aWFsaXNhdGlvbiBkZSBsYSBsaXN0ZSBwb3VyIGxlcyBub21zIGRlIGxpZ25lcyBkZXMgZ2FyZXMNCmxpZ25lc19nYXJlc19uYW1lcyA8LSBsaXN0KCkNCg0KIyBCb3VjbGUgc3VyIGNoYXF1ZSBpZGVudGlmaWFudCBkZSBnYXJlDQpmb3IoIGkgaW4gMTpucm93KHN0b3BzKSkgew0KdHJhamV0c19nYXJlIDwtIHN0b3BfdGltZXMgJT4lIA0KICBmaWx0ZXIoc3RvcF9pZCAlaW4lIG5vbV9pZFtpXSkNCmhlYWQodHJhamV0c19nYXJlKQ0KZGltKHRyYWpldHNfZ2FyZSkNCg0KdHJhamV0c19nYXJlIDwtIHRyYWpldHNfZ2FyZSR0cmlwX2lkDQoNCiMgRmlsdHJlciBsZXMgdHJhamV0cyBwYXIgdHJpcF9pZCBwb3VyIG9idGVuaXIgbGVzIHJvdXRlX2lkIGNvcnJlc3BvbmRhbnRzDQpyb3V0ZXNfaWRzIDwtIHRyaXBzICU+JQ0KICBmaWx0ZXIodHJpcF9pZCAlaW4lIHRyYWpldHNfZ2FyZSkNCmhlYWQodHJhamV0c19nYXJlKQ0KZGltKHRyYWpldHNfZ2FyZSkNCnJvdXRlc19pZHMgPC0gcm91dGVzX2lkcyRyb3V0ZV9pZA0Kcm91dGVzX2lkcw0KDQogIyBGaWx0cmVyIGxlcyBsaWduZXMvcm91dGVzIHBhciByb3V0ZV9pZA0KbmFtZXMocm91dGVzKQ0KDQpsaWduZXNfZ2FyZXMgPC0gcm91dGVzICU+JSANCiAgZmlsdGVyKHJvdXRlX2lkICVpbiUgcm91dGVzX2lkcykNCmxpZ25lc19nYXJlcw0KbnJvdyhsaWduZXNfZ2FyZXMpDQoNCiMgQWpvdXRlciBsZXMgbm9tcyBkZXMgbGlnbmVzIMOgIGxhIGxpc3RlIGRlcyBub21zIGRlcyBnYXJlcw0KICBsaWduZXNfZ2FyZXNfbmFtZXNbW2ldXSA8LSBsaWduZXNfZ2FyZXMkcm91dGVfbG9uZ19uYW1lDQp9DQoNCiMgQXBsYXRpciBsYSBsaXN0ZSBlbiB1biB2ZWN0ZXVyIHVuaXF1ZSBkZSBub21zIGRlIGxpZ25lcw0KbGlnbmVzX2dhcmVzX25hbWVzIDwtIHVubGlzdChsaWduZXNfZ2FyZXNfbmFtZXMpDQpsaWduZXNfZ2FyZXNfbmFtZXMgPC0gdW5pcXVlKGxpZ25lc19nYXJlc19uYW1lcykNCmBgYA0KDQoNCmBgYHtyLCBlY2hvID0gRiwgZXZhbCA9IEZ9DQojIEFmZmljaGVyIGxlcyBub21zIHVuaXF1ZXMgZGVzIGxpZ25lcyBkZSBnYXJlDQpwcmludChsaWduZXNfZ2FyZXNfbmFtZXMpDQpgYGANCg0KYGBge3J9DQpsaWduZXNfZ2FyZXNfbmFtZXMgPC0gYygNCiAgIkxpbGxlIEZsYW5kcmVzIC0gRG9uLVNhaW5naGluIC0gQsOpdGh1bmUiLA0KICAiTGlsbGUgRmxhbmRyZXMgLSBEb24tU2FpbmdoaW4gLSBMZW5zIiwNCiAgIkxpbGxlIEZsYW5kcmVzIC0gQsOpdGh1bmUiLA0KICAiUEFSSVMgLSBTVCBESVpJRVIiLA0KICAiUEFSSVMgLSBTVFJBU0JPVVJHIiwNCiAgIkNow6J0ZWF1LVRoaWVycnkgLSBSZWltcyIsDQogICJSRUlNUyAtIExBT04iLA0KICAiUGFyaXMgTm9yZCAtIExhb24iLA0KICAiQ3LDqXB5LUVuLVZhbG9pcyAtIExhb24iLA0KICAiQ29tcGnDqGduZSAtIFBhcmlzIiwgIyBJY2kgaidhaSBham91dMOpIHVuIHRpcmV0DQogICJQYXJpcyBOb3JkIC0gQmVhdXZhaXMiLA0KICAiU2FpbnQtUXVlbnRpbiAtIENvbXBpw6hnbmUiLA0KICAiUGFyaXMgTm9yZCAtIENyZWlsIC0gTG9uZ3VlYXUgLSBBcnJhcyAtIERvdWFpIC0gTGlsbGUgRmxhbmRyZXMiLA0KICAiUGFyaXMgTm9yZCAtIEFtaWVucyIsDQogICJQYXJpcyBOb3JkIC0gU2FpbnQtUXVlbnRpbiIsDQogICJQYXJpcyBOb3JkIC0gQ29tcGnDqGduZSIsDQogICJQYXJpcyBOb3JkIC0gTWF1YmV1Z2UgLSBDYW1icmFpIiwNCiAgIlBhcmlzIE5vcmQgLSBMb25ndWVhdS1BbWllbnMiLCAjIEljaSBqJ2FpIGFqb3V0w6kgdW4gZXNwYWNlIGp1c3RlIGF2YW50IExvbmd1ZWF1DQogICJQYXJpcyBOb3JkIC0gQ3JlaWwgLSBTYWludC1KdXN0LUVuLUNoYXVzc8OpZSIsDQogICJCZWF1dmFpcyAtIENyZWlsIiwNCiAgIkFtaWVucyAtIENyZWlsIiwNCiAgIlBhcmlzIE5vcmQgLSBDcmVpbCIsDQogICJDYWxhaXMgLSBQYXJpcyIsICMgSWNpIGonYWkgYWpvdXTDqSB1biB0aXJldA0KICAiQW1pZW5zIC0gQ29tcGnDqGduZSIsDQogICJBcnJhcyAtIER1bmtlcnF1ZSBUZXIgRXQgVGd2IEF1dG9yaXPDqXMiLA0KICAiTGlsbGUgRmxhbmRyZXMgLSBEdW5rZXJxdWUiLA0KICAiRHVua2VycXVlIC0gSGF6ZWJyb3VjayIsDQogICJDYWxhaXMgVmlsbGUgLSBEdW5rZXJxdWUiLA0KICAiTGlsbGUgRmxhbmRyZXMgLSBDb3VydHJhaSIsDQogICJMaWxsZSBGbGFuZHJlcyAtIFRvdXJjb2luZyBZIENvbXByaXMgVGd2IEF1dG9yaXPDqXMiLA0KICAiQ2FsYWlzIFZpbGxlIC0gQW1pZW5zIiwgIyBSZXRyYWl0IHRpcmV0IGF2YW50IHZpbGxlDQogICJDYWxhaXMgVmlsbGUgLSBSYW5nLUR1LUZsaWVycyAtIFZlcnRvbiAtIEJlcmNrIiwNCiAgIkNhbGFpcyBWaWxsZSAtIEhhemVicm91Y2siLA0KICAiQ2FsYWlzIFZpbGxlIC0gQXJyYXMiLA0KICAiTGlsbGUgRmxhbmRyZXMgLSBTYWludC1PbWVyIC0gQ2FsYWlzIFZpbGxlIiwNCiAgIkxpbGxlIEZsYW5kcmVzIC0gU3QgUXVlbnRpbiIsDQogICJMaWxsZSBGbGFuZHJlcyAtIEhhemVicm91Y2siLA0KICAiUm91ZW4gLSBMaWxsZSIsDQogICJDaGFybGV2aWxsZSAtIExpbGxlIiwNCiAgIkxpbGxlIEZsYW5kcmVzIC0gVmFsZW5jaWVubmVzIiwNCiAgIkxpbGxlIC0gQW1pZW5zIC0gQWJhbmNvdXJ0IC0gKFJvdWVuIFJpdmUtRHJvaXRlKSIsDQogICJMaWxsZSBGbGFuZHJlcyAtIERvdWFpIiwNCiAgIkxpbGxlIEZsYW5kcmVzIC0gSGlyc29uIC0gKENoYXJsZXZpbGxlLSBNw6l6acOocmVzKSIsDQogICJMaWxsZSBGbGFuZHJlcyAtIEFtaWVucyIsDQogICJMaWxsZSBGbGFuZHJlcyAtIE1hdWJldWdlIC0gSmV1bW9udCIsDQogICJMaWxsZSBGbGFuZHJlcyAtIExpYmVyY291cnQgLSBMZW5zIiwNCiAgIkxpbGxlIEZsYW5kcmVzIC0gQmFpc2lldXggLSAoVG91cm5haSkiLA0KICAiSGF6ZWJyb3VjayAtIEFycmFzIiwNCiAgIlZhbGVuY2llbm5lcyAtIEpldW1vbnQiLA0KICAiKE5hbXVyKSAtIChDaGFybGVyb2kpIC0gTWF1YmV1Z2UiLA0KICAiQXVsbm95ZSAtIEF5bWVyaWVzIC0gU2FpbnQtUXVlbnRpbiIsDQogICJBdWxub3llIC0gQXltZXJpZXMgLSBIaXJzb24iLA0KICAiQW1pZW5zIC0gU2FpbnQtUXVlbnRpbiIsDQogICJEb3VhaSAtIFNhaW50LVF1ZW50aW4iLA0KICAiQW1pZW5zIC0gVGVyZ25pZXIgLSBMYW9uIiwNCiAgIkFtaWVucyAtIEFycmFzIiwNCiAgIkFiYmV2aWxsZSAtIEFtaWVucyAtIEFsYmVydCIsDQogICJBbWllbnMgLSBBYmFuY291cnQgLSAoUm91ZW4gUml2ZS1Ecm9pdGUpIiwNCiAgIkFtaWVucyAtIEFiYW5jb3VydCIsDQogICJSb3VlbiAtIEFtaWVucyIsDQogICJCZWF1dmFpcyAtIEFiYW5jb3VydCAtIExlIFRyw6lwb3J0IiwNCiAgIkFycmFzL0LDqXRodW5lIC0gU3QtUG9sLVN1ci1UZXJub2lzZSAtIEV0YXBsZXMiLA0KICAiTGVucyAtIELDqXRodW5lIC0gU3QtUG9sLXN1ciBUZXJub2lzZSIsDQogICJBcnJhcyAtIERvdWFpIiwNCiAgIkRvdWFpIC0gVmFsZW5jaWVubmVzIiwNCiAgIlZhbGVuY2llbm5lcyAtIENhbWJyYWkiLA0KICAiTGVucyAtIERvdWFpIiwNCiAgIlNlcnF1ZXV4IC0gR2lzb3JzIikgIyBJY2kgaidhaSBham91dMOpIGRldXggZXNhcGNlcyBlbnRyZSBsZSB0aXJldA0KYGBgDQoNCjxicj4NCg0KKmxpZ25lc19nYXJlc19uYW1lcyogZXN0IGNvbXBvc8OpIGR1IG5vbSBkZXMgbGlnbmVzIGRhbnMgbGVzIEhhdXRzLWRlLUZyYW5jZS4NCk5vdXMgY29uc3RhdG9ucyBxdSdpbCBleGlzdGUgZGVzIG5vbXMgZGUgbGlnbmVzIGRpc3Bvc2FudCBkZSBnYXJlcyBpbnRlcm3DqWRpYWlyZXMsIGV4ZW1wbGUgOiAiTGlsbGUgRmxhbmRyZXMgLSBEb24tU2FpbmdoaW4gLSBMZW5zIi4gTm91cyBkw6ljaWRvbnMgZGUgc3VwcHJpbWVyIGxlcyBnYXJlcyBpbnRlcm3DqWRpYWlyZXMgcG91ciBuZSBsYWlzc2VyIGFwcGFyYcOudHJlIHF1ZSBsZXMgZ2FyZXMgc2l0dcOpZXMgZW4gYm91dCBkZSBsaWduZS4gDQoNCjxicj4NCg0KYGBge3J9DQojIEluaXRpYWxpc2VyIHVuIHZlY3RldXIgdmlkZSBwb3VyIHN0b2NrZXIgbGVzIHLDqXN1bHRhdHMNCmxpZ25lc19zaW1wbGlmaWVlcyA8LSB2ZWN0b3IoImNoYXJhY3RlciIsIGxlbmd0aCA9IGxlbmd0aChsaWduZXNfZ2FyZXNfbmFtZXMpKQ0KDQojIEJvdWNsZSBwb3VyIHRyYWl0ZXIgY2hhcXVlIG5vbSBkZSBsaWduZQ0KZm9yKGkgaW4gc2VxX2Fsb25nKGxpZ25lc19nYXJlc19uYW1lcykpIHsNCiAgbm9tIDwtIGxpZ25lc19nYXJlc19uYW1lc1tpXQ0KICBwYXJ0aWVzIDwtIHN0cl9zcGxpdChub20sIHBhdHRlcm4gPSAiIC0gIiwgc2ltcGxpZnkgPSBGQUxTRSlbWzFdXQ0KICBkZWJ1dF9maW4gPC0gYyhwYXJ0aWVzWzFdLCB0YWlsKHBhcnRpZXMsIDEpKQ0KICBsaWduZXNfc2ltcGxpZmllZXNbaV0gPC0gc3RyX2MoZGVidXRfZmluLCBjb2xsYXBzZSA9ICIgLSAiKQ0KfQ0KDQojIEFmZmljaGVyIGxlcyByw6lzdWx0YXRzDQpwcmludChsaWduZXNfc2ltcGxpZmllZXMpDQpgYGANCg0KDQpgYGB7cn0NCiMgQ3LDqWF0aW9uIGR1IG5vdXZlYXUgdmVjdGV1ciBzaW1wbGlmacOpDQpsaWduZXNfc2ltcGxpZmllZXMgPC0gYygiTGlsbGUgRmxhbmRyZXMgLSBCw6l0aHVuZSIsDQogICAgICAgICAgICAgICAgICAgICAgICAiTGlsbGUgRmxhbmRyZXMgLSBMZW5zIiAgICwgICAgICAgICAgICAgICAgDQogICAgICAgICAgICAgICAgICAgICAgICAiTGlsbGUgRmxhbmRyZXMgLSBCw6l0aHVuZSIsICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICANCiAgICAgICAgICAgICAgICAgICAgICAgICJQQVJJUyAtIFNUIERJWklFUiIgICAgICAgICwgICAgICAgICAgICAgICAgICAgICAgICAgIA0KICAgICAgICAgICAgICAgICAgICAgICAgICJQQVJJUyAtIFNUUkFTQk9VUkciICAgICAgICAsICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIA0KICAgICAgICAgICAgICAgICAgICAgICAgICJDaMOidGVhdS1UaGllcnJ5IC0gUmVpbXMiICAgICwgICAgICAgICAgICAgICAgICAgICAgICAgICAgICANCiAgICAgICAgICAgICAgICAgICAgICAgICAiUkVJTVMgLSBMQU9OIiAgICAgICAgICAgICAgICAsICAgICAgICAgICAgICAgICAgICAgICAgICAgICANCiAgICAgICAgICAgICAgICAgICAgICAgICAiUGFyaXMgTm9yZCAtIExhb24iICAgICAgICAgICAgLCAgICAgICAgICAgICAgICAgICAgICAgICAgICANCiAgICAgICAgICAgICAgICAgICAgICAgICAiQ3LDqXB5LUVuLVZhbG9pcyAtIExhb24iICAgICAgICAsICAgICAgICAgICAgICAgICAgICAgICAgICAgDQogICAgICAgICAgICAgICAgICAgICAgICAgIkNvbXBpw6hnbmUgLSBQYXJpcyIsICAgICAgICAgICAgICAgICAgICAgICAgDQogICAgICAgICAgICAgICAgICAgICAgICAgIlBhcmlzIE5vcmQgLSBCZWF1dmFpcyIgICAsICAgICAgICAgICAgICAgICAgICAgICANCiAgICAgICAgICAgICAgICAgICAgICAgICAiU2FpbnQtUXVlbnRpbiAtIENvbXBpw6hnbmUiICAgICAgICwgICAgICAgICAgICAgICAgICAgICAgDQogICAgICAgICAgICAgICAgICAgICAgICAgIlBhcmlzIE5vcmQgLSBMaWxsZSBGbGFuZHJlcyIgICAgICAgICAsICAgICAgICAgICAgICAgICAgICAgDQogICAgICAgICAgICAgICAgICAgICAgICAgIlBhcmlzIE5vcmQgLSBBbWllbnMiICAgICAgICAgICAgICAgICAgLCAgICAgICAgICAgICAgICAgICAgDQogICAgICAgICAgICAgICAgICAgICAgICAgIlBhcmlzIE5vcmQgLSBTYWludC1RdWVudGluIiAgICAgICAgICAgICwgICAgICAgICAgICAgICAgICAgDQogICAgICAgICAgICAgICAgICAgICAgICAgIlBhcmlzIE5vcmQgLSBDb21wacOoZ25lIiAgICAgICAgICAgICAgICAgLCAgICAgICAgICAgICAgICAgIA0KICAgICAgICAgICAgICAgICAgICAgICAgICJQYXJpcyBOb3JkIC0gQ2FtYnJhaSIgICAgICAgICAgICAgICAgICAgICwgICAgICAgICAgICAgICAgIA0KICAgICAgICAgICAgICAgICAgICAgICAgICJQYXJpcyBOb3JkIC0gTG9uZ3VlYXUtQW1pZW5zIiwgIyBNb2RpZmljYXRpb24gbWFudWVsbGUNCiAgICAgICAgICAgICAgICAgICAgICAgICAiUGFyaXMgTm9yZCAtIFNhaW50LUp1c3QtRW4tQ2hhdXNzw6llIiAgICAgICwgICAgICAgICAgICAgICAgDQogICAgICAgICAgICAgICAgICAgICAgICAgIkJlYXV2YWlzIC0gQ3JlaWwiICAgICAgICAgICAgICAgICwgICAgICAgICAgICAgICAgICAgICAgICAgDQogICAgICAgICAgICAgICAgICAgICAgICAgIkFtaWVucyAtIENyZWlsIiAgICAgICAgICAgICAgICAgICwgICAgICAgICAgICAgICAgICAgICAgICAgDQogICAgICAgICAgICAgICAgICAgICAgICAgIlBhcmlzIE5vcmQgLSBDcmVpbCIgICAgICAgICAgICAgICAsICAgICAgICAgICAgICAgICAgICAgICAgDQogICAgICAgICAgICAgICAgICAgICAgICAgIkNhbGFpcyAtIFBhcmlzIiAgICAgICAsICAgICAgICAgICAgICAgICAgICAgICANCiAgICAgICAgICAgICAgICAgICAgICAgICAiQW1pZW5zIC0gQ29tcGnDqGduZSIgICAgICAgICAgICAgICAgICwgICAgICAgICAgICAgICAgICAgICAgDQogICAgICAgICAgICAgICAgICAgICAgICAgIkFycmFzIC0gRHVua2VycXVlIFRlciBFdCBUZ3YgQXV0b3Jpc8OpcyIsICAgICAgICAgICAgICAgICAgIA0KICAgICAgICAgICAgICAgICAgICAgICAgICJMaWxsZSBGbGFuZHJlcyAtIER1bmtlcnF1ZSIgICAgICAgICAgICwgICAgICAgICAgICAgICAgICAgIA0KICAgICAgICAgICAgICAgICAgICAgICAgICJEdW5rZXJxdWUgLSBIYXplYnJvdWNrIiAgICAgICAgICAgICAgICAgLCAgICAgICAgICAgICAgICAgIA0KICAgICAgICAgICAgICAgICAgICAgICAgICJDYWxhaXMgVmlsbGUgLSBEdW5rZXJxdWUiICAgICAgICAgICAgICAgICwgICAgICAgICAgICAgICAgIA0KICAgICAgICAgICAgICAgICAgICAgICAgICJMaWxsZSBGbGFuZHJlcyAtIENvdXJ0cmFpIiAgICAgICAgICAgICAgICAsICAgICAgICAgICAgICAgIA0KICAgICAgICAgICAgICAgICAgICAgICAgICJMaWxsZSBGbGFuZHJlcyAtIFRvdXJjb2luZyBZIENvbXByaXMgVGd2IEF1dG9yaXPDqXMiICAgICAgICwNCiAgICAgICAgICAgICAgICAgICAgICAgICAiQ2FsYWlzIFZpbGxlIC0gQW1pZW5zIiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgLA0KICAgICAgICAgICAgICAgICAgICAgICAgICJDYWxhaXMgVmlsbGUgLSBCZXJjayIgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgLA0KICAgICAgICAgICAgICAgICAgICAgICAgICJDYWxhaXMgVmlsbGUgLSBIYXplYnJvdWNrIiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgLA0KICAgICAgICAgICAgICAgICAgICAgICAgICJDYWxhaXMgVmlsbGUgLSBBcnJhcyIgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgLA0KICAgICAgICAgICAgICAgICAgICAgICAgICJMaWxsZSBGbGFuZHJlcyAtIENhbGFpcyBWaWxsZSIgICAgICAgICAgICAgICAgICAgICAgICAgICAgLA0KICAgICAgICAgICAgICAgICAgICAgICAgICJMaWxsZSBGbGFuZHJlcyAtIFN0IFF1ZW50aW4iICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgLA0KICAgICAgICAgICAgICAgICAgICAgICAgICJMaWxsZSBGbGFuZHJlcyAtIEhhemVicm91Y2siICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgLA0KICAgICAgICAgICAgICAgICAgICAgICAgICJSb3VlbiAtIExpbGxlIiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgLA0KICAgICAgICAgICAgICAgICAgICAgICAgICJDaGFybGV2aWxsZSAtIExpbGxlIiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgLA0KICAgICAgICAgICAgICAgICAgICAgICAgICJMaWxsZSBGbGFuZHJlcyAtIFZhbGVuY2llbm5lcyIgICAgICAgICAgICAgICAgICAgICAgICAgICAgLA0KICAgICAgICAgICAgICAgICAgICAgICAgICJMaWxsZSAtIChSb3VlbiBSaXZlLURyb2l0ZSkiICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgLA0KICAgICAgICAgICAgICAgICAgICAgICAgICJMaWxsZSBGbGFuZHJlcyAtIERvdWFpIiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgLA0KICAgICAgICAgICAgICAgICAgICAgICAgICJMaWxsZSBGbGFuZHJlcyAtIChDaGFybGV2aWxsZS0gTcOpemnDqHJlcykiICAgICAgICAgICAgICAgICAsDQogICAgICAgICAgICAgICAgICAgICAgICAgIkxpbGxlIEZsYW5kcmVzIC0gQW1pZW5zIiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAsDQogICAgICAgICAgICAgICAgICAgICAgICAgIkxpbGxlIEZsYW5kcmVzIC0gSmV1bW9udCIgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAsDQogICAgICAgICAgICAgICAgICAgICAgICAgIkxpbGxlIEZsYW5kcmVzIC0gTGVucyIgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAsDQogICAgICAgICAgICAgICAgICAgICAgICAgIkxpbGxlIEZsYW5kcmVzIC0gKFRvdXJuYWkpIiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAsDQogICAgICAgICAgICAgICAgICAgICAgICAgIkhhemVicm91Y2sgLSBBcnJhcyIgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAsDQogICAgICAgICAgICAgICAgICAgICAgICAgIlZhbGVuY2llbm5lcyAtIEpldW1vbnQiICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAsDQogICAgICAgICAgICAgICAgICAgICAgICAgIihOYW11cikgLSBNYXViZXVnZSIgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAsDQogICAgICAgICAgICAgICAgICAgICAgICAgIkF1bG5veWUgLSBTYWludC1RdWVudGluIiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAsDQogICAgICAgICAgICAgICAgICAgICAgICAgIkF1bG5veWUgLSBIaXJzb24iICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAsDQogICAgICAgICAgICAgICAgICAgICAgICAgIkFtaWVucyAtIFNhaW50LVF1ZW50aW4iICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAsDQogICAgICAgICAgICAgICAgICAgICAgICAgIkRvdWFpIC0gU2FpbnQtUXVlbnRpbiIgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAsDQogICAgICAgICAgICAgICAgICAgICAgICAgIkFtaWVucyAtIExhb24iICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAsDQogICAgICAgICAgICAgICAgICAgICAgICAgIkFtaWVucyAtIEFycmFzIiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAsDQogICAgICAgICAgICAgICAgICAgICAgICAgIkFiYmV2aWxsZSAtIEFsYmVydCIgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAsDQogICAgICAgICAgICAgICAgICAgICAgICAgIkFtaWVucyAtIChSb3VlbiBSaXZlLURyb2l0ZSkiICAgICAgICAgICAgICAgICAgICAgICAgICAgICAsDQogICAgICAgICAgICAgICAgICAgICAgICAgIkFtaWVucyAtIEFiYW5jb3VydCIgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAsDQogICAgICAgICAgICAgICAgICAgICAgICAgIlJvdWVuIC0gQW1pZW5zIiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAsDQogICAgICAgICAgICAgICAgICAgICAgICAgIkJlYXV2YWlzIC0gTGUgVHLDqXBvcnQiICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgLA0KICAgICAgICAgICAgICAgICAgICAgICAgICJBcnJhcy9Cw6l0aHVuZSAtIEV0YXBsZXMiICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICwNCiAgICAgICAgICAgICAgICAgICAgICAgICAiTGVucyAtIFN0LVBvbC1zdXIgVGVybm9pc2UiICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICwNCiAgICAgICAgICAgICAgICAgICAgICAgICAiQXJyYXMgLSBEb3VhaSIgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICwNCiAgICAgICAgICAgICAgICAgICAgICAgICAiRG91YWkgLSBWYWxlbmNpZW5uZXMiICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICwNCiAgICAgICAgICAgICAgICAgICAgICAgICAiVmFsZW5jaWVubmVzIC0gQ2FtYnJhaSIgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICwNCiAgICAgICAgICAgICAgICAgICAgICAgICAiTGVucyAtIERvdWFpIiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICwNCiAgICAgICAgICAgICAgICAgICAgICAgICAiU2VycXVldXggLSBHaXNvcnMiKSAjIG1vZGlmaWNhdGlvbiBtYW51ZWxsZQ0KDQojIENyw6llciB1biBkYXRhIGZyYW1lIMOgIHBhcnRpciBkdSB2ZWN0ZXVyIGVuIHPDqXBhcmFudCBsZXMgZ2FyZXMgZGUgZMOpcGFydCBldCBkJ2Fycml2w6llDQpkZl9nYXJlcyA8LSBkYXRhLmZyYW1lKA0KICBHYXJlRGVwYXJ0ID0gc2FwcGx5KGxpZ25lc19zaW1wbGlmaWVlcywgZnVuY3Rpb24oeCkgc3Ryc3BsaXQoeCwgIiAtICIpW1sxXV1bMV0pLA0KICBHYXJlQXJyaXZlZSA9IHNhcHBseShsaWduZXNfc2ltcGxpZmllZXMsIGZ1bmN0aW9uKHgpIHsNCiAgICBwYXJ0cyA8LSBzdHJzcGxpdCh4LCAiIC0gIilbWzFdXQ0KICAgIGlmKGxlbmd0aChwYXJ0cykgPj0gMikgcmV0dXJuKHRhaWwocGFydHMsIG4gPSAxKSkNCiAgICBlbHNlIHJldHVybihwYXJ0c1sxXSkNCiAgfSkNCikNCg0KIyBBZmZpY2hlciBsZSBkYXRhIGZyYW1lIHLDqXN1bHRhbnQNCnByaW50KGRmX2dhcmVzKQ0KDQpkZl9nYXJlc190ZXIgPC0gZGZfZ2FyZXMNCmBgYA0KDQojIyMgVEdWDQoNCkVuc3VpdGUsIGlsIGZhdXQgZXh0cmFpcmUgbGVzIGxpZ25lcyBUR1YNCg0KDQpgYGB7ciwgb3V0LndpZHRoPScxMDAlJ30NCnBhY21hbjo6cF9sb2FkKGRwbHlyLCBzZiwgc3RyaW5nciwgaWdyYXBoKQ0KIyBUR1YNCiMgw4l0YXBlIDEgOiBJZGVudGlmaWVyIGxhIEdhcmUgZCdJbnTDqXLDqnQNCnN0b3BzIDwtIHJlYWQuY3N2KCdkYXRhL0dURlMvZXhwb3J0X2d0ZnNfdm95YWdlcy9zdG9wcy50eHQnLCBoZWFkZXIgPSBUUlVFKQ0KbmFtZXMoc3RvcHMpDQpzdG9wcyA8LSBzdG9wcyAlPiUgDQogIGZpbHRlcihncmVwbCgnVEdWJywgc3RvcF9pZCkpDQojIGRpbShzdG9wcykNCnN0b3BzX3NmIDwtIHN0b3BzICU+JQ0KICBzZjo6c3RfYXNfc2YoY29vcmRzID0gYygic3RvcF9sb24iLCAic3RvcF9sYXQiKSwgY3JzID0gNDMyNikgJT4lIA0KICBzdF90cmFuc2Zvcm0oMjE1NCkNCm1hcHZpZXc6Om1hcHZpZXcoc3RvcHNfc2YpDQoNCnJlZ2lvbnMgPC0gc2Y6OnN0X3JlYWQoImRhdGEvUkVHSU9OLnNocCIpDQpIREYgPC0gcmVnaW9ucyAlPiUgDQogIGZpbHRlcihJTlNFRV9SRUcgPT0gMzIpICMgZmlsdHJlIHN1ciBsZXMgSGF1dHMtZGUtRnJhbmNlDQoNCnN0b3BzX3NmX2hkZiA8LSBzZjo6c3RfaW50ZXJzZWN0aW9uKHN0b3BzX3NmLCBIREYpDQptYXB2aWV3OjptYXB2aWV3KHN0b3BzX3NmX2hkZikNCnN0b3BzIDwtIHN0b3BzX3NmX2hkZiAlPiUgDQogIHN0X2Ryb3BfZ2VvbWV0cnkoKQ0Kbm9tX2lkIDwtIGxpc3QoKQ0KDQpmb3IoIGkgaW4gMTpucm93KHN0b3BzKSkgew0KZ2FyZV9ub20gPC0gc3RvcHNbaSwnc3RvcF9uYW1lJ10gDQpnYXJlX2lkIDwtIHN0b3BzICU+JSANCiAgZmlsdGVyKHN0b3BfbmFtZSA9PSBnYXJlX25vbSkgDQpub21faWRbaV0gPC0gZ2FyZV9pZCRzdG9wX2lkDQp9DQoNCm5vbV9pZCA8LSB1bmxpc3Qobm9tX2lkKQ0KIyBwcmludChub21faWQpDQpgYGANCg0KDQpgYGB7ciwgb3V0LndpZHRoPScxMDAlJywgY2FjaGUgPSBUfQ0KIyDDiXRhcGUgMiA6IFRyb3V2ZXIgbGVzIHRyYWpldHMgcGFzc2FudCBwYXIgbGVzIGdhcmVzDQpzdG9wX3RpbWVzIDwtIHJlYWQuY3N2KCdkYXRhL0dURlMvZXhwb3J0X2d0ZnNfdm95YWdlcy9zdG9wX3RpbWVzLnR4dCcsIGhlYWRlciA9IFRSVUUpDQp0cmlwcyA8LSByZWFkLmNzdignZGF0YS9HVEZTL2V4cG9ydF9ndGZzX3ZveWFnZXMvdHJpcHMudHh0JywgaGVhZGVyID0gVFJVRSkNCnJvdXRlcyA8LSByZWFkLmNzdignZGF0YS9HVEZTL2V4cG9ydF9ndGZzX3ZveWFnZXMvcm91dGVzLnR4dCcsIGhlYWRlciA9IFRSVUUpDQpgYGANCg0KKipFeHRyYWN0aW9uIGRlIHRvdXRlcyBsZXMgbGlnbmVzKioNCg0KYGBge3IsIG91dC53aWR0aD0nMTAwJSd9DQpsaWduZXNfZ2FyZXNfbmIgPC0gbGlzdCgpDQoNCmZvciggaSBpbiAxOm5yb3coc3RvcHMpKSB7DQp0cmFqZXRzX2dhcmUgPC0gc3RvcF90aW1lcyAlPiUgZmlsdGVyKHN0b3BfaWQgJWluJSBub21faWRbaV0pDQpoZWFkKHRyYWpldHNfZ2FyZSkNCmRpbSh0cmFqZXRzX2dhcmUpDQoNCnRyYWpldHNfZ2FyZSA8LSB0cmFqZXRzX2dhcmUkdHJpcF9pZA0KDQojIFRyaXBzDQp0cmlwc19nYXJlIDwtIHRyaXBzICU+JQ0KICBmaWx0ZXIodHJpcF9pZCAlaW4lIHRyYWpldHNfZ2FyZSkNCmhlYWQodHJhamV0c19nYXJlKQ0KZGltKHRyYWpldHNfZ2FyZSkNCnRyaXBzX2dhcmUgPC0gdHJpcHNfZ2FyZSRyb3V0ZV9pZA0KdHJpcHNfZ2FyZQ0KDQojIMOJdGFwZSAzIDogSWRlbnRpZmllciBsZXMgbGlnbmVzIGNvcnJlc3BvbmRhbnRlcw0KbmFtZXMocm91dGVzKQ0KDQpsaWduZXNfZ2FyZXMgPC0gcm91dGVzICU+JSANCiAgZmlsdGVyKHJvdXRlX2lkICVpbiUgdHJpcHNfZ2FyZSkNCmxpZ25lc19nYXJlcw0KbnJvdyhsaWduZXNfZ2FyZXMpDQoNCmxpZ25lc19nYXJlc19uYltpXSA8LSBucm93KGxpZ25lc19nYXJlcykNCn0NCmxpZ25lc19nYXJlc19uYl91bmxpc3QgPC0gdW5saXN0KGxpZ25lc19nYXJlc19uYikNCmxpZ25lc19nYXJlc19uYl91bmxpc3QNCg0KZ2FyZXNfc2VydmljZXMgPC0gZGF0YS5mcmFtZShzdG9wcyRzdG9wX25hbWUsIGxpZ25lc19nYXJlc19uYl91bmxpc3QpDQpgYGANCiAgDQpMZSB0YWJsZWF1IGNpLWRlc3NvdXMgY29tcHRlIGxlIG5vbWJyZSBkZSBsaWduZXMgcXVpIHBhc3NlbnQgcGFyIGNoYWN1bmUgZGVzIGdhcmVzIGRlcyBIYXV0cy1kZS1GcmFuY2UgOiAgIA0KICANCiAgICANCmBgYHtyLCBvdXQud2lkdGg9JzEwMCUnfQ0KZ2FyZXNfc2VydmljZXMNCg0KYGBgDQoNCioqRXh0cmFjdGlvbiBkZXMgZWRnZXMgcG91ciBpR3JhcGgqKg0KDQpgYGB7cn0NCiMgSW5pdGlhbGlzYXRpb24gZGUgbGEgbGlzdGUgcG91ciBsZXMgbm9tcyBkZSBsaWduZXMgZGVzIGdhcmVzDQpsaWduZXNfZ2FyZXNfbmFtZXMgPC0gbGlzdCgpDQoNCiMgQm91Y2xlIHN1ciBjaGFxdWUgaWRlbnRpZmlhbnQgZGUgZ2FyZQ0KZm9yKCBpIGluIDE6bnJvdyhzdG9wcykpIHsNCnRyYWpldHNfZ2FyZSA8LSBzdG9wX3RpbWVzICU+JSANCiAgZmlsdGVyKHN0b3BfaWQgJWluJSBub21faWRbaV0pDQpoZWFkKHRyYWpldHNfZ2FyZSkNCmRpbSh0cmFqZXRzX2dhcmUpDQoNCnRyYWpldHNfZ2FyZSA8LSB0cmFqZXRzX2dhcmUkdHJpcF9pZA0KDQojIEZpbHRyZXIgbGVzIHRyYWpldHMgcGFyIHRyaXBfaWQgcG91ciBvYnRlbmlyIGxlcyByb3V0ZV9pZCBjb3JyZXNwb25kYW50cw0Kcm91dGVzX2lkcyA8LSB0cmlwcyAlPiUNCiAgZmlsdGVyKHRyaXBfaWQgJWluJSB0cmFqZXRzX2dhcmUpDQpoZWFkKHRyYWpldHNfZ2FyZSkNCmRpbSh0cmFqZXRzX2dhcmUpDQpyb3V0ZXNfaWRzIDwtIHJvdXRlc19pZHMkcm91dGVfaWQNCnJvdXRlc19pZHMNCg0KICMgRmlsdHJlciBsZXMgbGlnbmVzL3JvdXRlcyBwYXIgcm91dGVfaWQNCm5hbWVzKHJvdXRlcykNCg0KbGlnbmVzX2dhcmVzIDwtIHJvdXRlcyAlPiUgDQogIGZpbHRlcihyb3V0ZV9pZCAlaW4lIHJvdXRlc19pZHMpDQpsaWduZXNfZ2FyZXMNCm5yb3cobGlnbmVzX2dhcmVzKQ0KDQojIEFqb3V0ZXIgbGVzIG5vbXMgZGVzIGxpZ25lcyDDoCBsYSBsaXN0ZSBkZXMgbm9tcyBkZXMgZ2FyZXMNCiAgbGlnbmVzX2dhcmVzX25hbWVzW1tpXV0gPC0gbGlnbmVzX2dhcmVzJHJvdXRlX2xvbmdfbmFtZQ0KfQ0KDQojIEFwbGF0aXIgbGEgbGlzdGUgZW4gdW4gdmVjdGV1ciB1bmlxdWUgZGUgbm9tcyBkZSBsaWduZXMNCmxpZ25lc19nYXJlc19uYW1lcyA8LSB1bmxpc3QobGlnbmVzX2dhcmVzX25hbWVzKQ0KbGlnbmVzX2dhcmVzX25hbWVzIDwtIHVuaXF1ZShsaWduZXNfZ2FyZXNfbmFtZXMpDQoNCiMgQWZmaWNoZXIgbGVzIG5vbXMgdW5pcXVlcyBkZXMgbGlnbmVzIGRlIGdhcmUNCnByaW50KGxpZ25lc19nYXJlc19uYW1lcykNCg0KZGZfZ2FyZXNfdGd2IDwtIGxpZ25lc19nYXJlc19uYW1lcw0KDQpkZl9nYXJlc190Z3YgPC0gYygiTGlsbGUgLSBBbHBlcyBUR1YiLCAiUGFyaXMgLSBEdW5rZXJxdWUgcGFyIEJhc3NpbiBNaW5pZXIiLA0KICAgICAgICAgICAgICAgICAgIk5vcmQgLSBCcmV0YWduZSBUR1YiLCAiU3RyYXNib3VyZyAtIEJydXhlbGxlcyIsDQogICAgICAgICAgICAgICAgICAiTGlsbGUgLSBMeW9uIFRHViIsICMgaWNpIGonYWkgYWpvdXTDqSB1biB0aXJldCBlbnRyZSBMaWxsZSBldCBMeW9uIFRHVg0KICAgICAgICAgICAgICAgICAgIlBhcmlzIC0gTGl0dG9yYWwiKQ0KDQojIFRyYW5zZm9ybWVyIGxlIHZlY3RldXIgZW4gZGF0YWZyYW1lIGF2ZWMgZGV1eCBjb2xvbm5lcyA6IEdhcmVEZXBhcnQgZXQgR2FyZUFycml2ZWUNCmRmX2dhcmVzX3Rndl9kZiA8LSBkYXRhLmZyYW1lKA0KICBHYXJlRGVwYXJ0ID0gc2FwcGx5KGRmX2dhcmVzX3RndiwgZnVuY3Rpb24oeCkgc3Ryc3BsaXQoeCwgIiAtICIpW1sxXV1bMV0pLA0KICBHYXJlQXJyaXZlZSA9IHNhcHBseShkZl9nYXJlc190Z3YsIGZ1bmN0aW9uKHgpIHsNCiAgICBzcGxpdCA8LSBzdHJzcGxpdCh4LCAiIC0gIikNCiAgICBpZiAobGVuZ3RoKHNwbGl0W1sxXV0pID4gMSkgcmV0dXJuKHNwbGl0W1sxXV1bMl0pDQogICAgZWxzZSByZXR1cm4oTkEpICMgUmV0b3VybmUgTkEgc2kgYXVjdW5lIGdhcmUgZCdhcnJpdsOpZSBuJ2VzdCB0cm91dsOpZQ0KICB9KQ0KKQ0KDQojIEFmZmljaGVyIGxlIHLDqXN1bHRhdA0KcHJpbnQoZGZfZ2FyZXNfdGd2X2RmKQ0Kcm93Lm5hbWVzKGRmX2dhcmVzX3Rndl9kZikgPC0gTlVMTA0KDQpgYGANCg0KIyMjIE9VSUdPDQoNCg0KRW5zdWl0ZSBpbCBmYXV0IGxlcyBsaWduZXMgT1VJR08gOg0KDQoNCmBgYHtyLCBvdXQud2lkdGg9JzEwMCUnfQ0KcGFjbWFuOjpwX2xvYWQoZHBseXIsIHNmLCBzdHJpbmdyLCBpZ3JhcGgpDQojIFRHVg0KIyDDiXRhcGUgMSA6IElkZW50aWZpZXIgbGEgR2FyZSBkJ0ludMOpcsOqdA0Kc3RvcHMgPC0gcmVhZC5jc3YoJ2RhdGEvR1RGUy9leHBvcnRfZ3Rmc192b3lhZ2VzL3N0b3BzLnR4dCcsIGhlYWRlciA9IFRSVUUpDQpuYW1lcyhzdG9wcykNCnN0b3BzIDwtIHN0b3BzICU+JSANCiAgZmlsdGVyKGdyZXBsKCdPVUlHTycsIHN0b3BfaWQpKQ0KZGltKHN0b3BzKQ0Kc3RvcHNfc2YgPC0gc3RvcHMgJT4lDQogIHNmOjpzdF9hc19zZihjb29yZHMgPSBjKCJzdG9wX2xvbiIsICJzdG9wX2xhdCIpLCBjcnMgPSA0MzI2KSAlPiUgDQogIHN0X3RyYW5zZm9ybSgyMTU0KQ0KbWFwdmlldzo6bWFwdmlldyhzdG9wc19zZikNCg0KcmVnaW9ucyA8LSBzZjo6c3RfcmVhZCgiZGF0YS9SRUdJT04uc2hwIikNCkhERiA8LSByZWdpb25zICU+JSANCiAgZmlsdGVyKElOU0VFX1JFRyA9PSAzMikgIyBmaWx0cmUgc3VyIGxlcyBIYXV0cy1kZS1GcmFuY2UNCg0Kc3RvcHNfc2ZfaGRmIDwtIHNmOjpzdF9pbnRlcnNlY3Rpb24oc3RvcHNfc2YsIEhERikNCm1hcHZpZXc6Om1hcHZpZXcoc3RvcHNfc2ZfaGRmKQ0Kc3RvcHMgPC0gc3RvcHNfc2ZfaGRmICU+JSANCiAgc3RfZHJvcF9nZW9tZXRyeSgpDQpub21faWQgPC0gbGlzdCgpDQoNCmZvciggaSBpbiAxOm5yb3coc3RvcHMpKSB7DQpnYXJlX25vbSA8LSBzdG9wc1tpLCdzdG9wX25hbWUnXSANCmdhcmVfaWQgPC0gc3RvcHMgJT4lIA0KICBmaWx0ZXIoc3RvcF9uYW1lID09IGdhcmVfbm9tKSANCm5vbV9pZFtpXSA8LSBnYXJlX2lkJHN0b3BfaWQNCn0NCg0Kbm9tX2lkIDwtIHVubGlzdChub21faWQpDQpwcmludChub21faWQpDQpgYGANCg0KDQpgYGB7ciwgb3V0LndpZHRoPScxMDAlJywgY2FjaGUgPSBUfQ0KIyDDiXRhcGUgMiA6IFRyb3V2ZXIgbGVzIHRyYWpldHMgcGFzc2FudCBwYXIgbGVzIGdhcmVzDQpzdG9wX3RpbWVzIDwtIHJlYWQuY3N2KCdkYXRhL0dURlMvZXhwb3J0X2d0ZnNfdm95YWdlcy9zdG9wX3RpbWVzLnR4dCcsIGhlYWRlciA9IFRSVUUpDQp0cmlwcyA8LSByZWFkLmNzdignZGF0YS9HVEZTL2V4cG9ydF9ndGZzX3ZveWFnZXMvdHJpcHMudHh0JywgaGVhZGVyID0gVFJVRSkNCnJvdXRlcyA8LSByZWFkLmNzdignZGF0YS9HVEZTL2V4cG9ydF9ndGZzX3ZveWFnZXMvcm91dGVzLnR4dCcsIGhlYWRlciA9IFRSVUUpDQpgYGANCg0KKipFeHRyYWN0aW9uIGRlIHRvdXRlcyBsZXMgbGlnbmVzKioNCg0KYGBge3IsIG91dC53aWR0aD0nMTAwJSd9DQpsaWduZXNfZ2FyZXNfbmIgPC0gbGlzdCgpDQoNCmZvciggaSBpbiAxOm5yb3coc3RvcHMpKSB7DQp0cmFqZXRzX2dhcmUgPC0gc3RvcF90aW1lcyAlPiUgZmlsdGVyKHN0b3BfaWQgJWluJSBub21faWRbaV0pDQpoZWFkKHRyYWpldHNfZ2FyZSkNCmRpbSh0cmFqZXRzX2dhcmUpDQoNCnRyYWpldHNfZ2FyZSA8LSB0cmFqZXRzX2dhcmUkdHJpcF9pZA0KDQojIFRyaXBzDQp0cmlwc19nYXJlIDwtIHRyaXBzICU+JQ0KICBmaWx0ZXIodHJpcF9pZCAlaW4lIHRyYWpldHNfZ2FyZSkNCmhlYWQodHJhamV0c19nYXJlKQ0KZGltKHRyYWpldHNfZ2FyZSkNCnRyaXBzX2dhcmUgPC0gdHJpcHNfZ2FyZSRyb3V0ZV9pZA0KdHJpcHNfZ2FyZQ0KDQojIMOJdGFwZSAzIDogSWRlbnRpZmllciBsZXMgbGlnbmVzIGNvcnJlc3BvbmRhbnRlcw0KbmFtZXMocm91dGVzKQ0KDQpsaWduZXNfZ2FyZXMgPC0gcm91dGVzICU+JSANCiAgZmlsdGVyKHJvdXRlX2lkICVpbiUgdHJpcHNfZ2FyZSkNCmxpZ25lc19nYXJlcw0KbnJvdyhsaWduZXNfZ2FyZXMpDQoNCmxpZ25lc19nYXJlc19uYltpXSA8LSBucm93KGxpZ25lc19nYXJlcykNCn0NCmxpZ25lc19nYXJlc19uYl91bmxpc3QgPC0gdW5saXN0KGxpZ25lc19nYXJlc19uYikNCmxpZ25lc19nYXJlc19uYl91bmxpc3QNCg0KZ2FyZXNfc2VydmljZXMgPC0gZGF0YS5mcmFtZShzdG9wcyRzdG9wX25hbWUsIGxpZ25lc19nYXJlc19uYl91bmxpc3QpDQpgYGANCiAgDQpMZSB0YWJsZWF1IGNpLWRlc3NvdXMgY29tcHRlIGxlIG5vbWJyZSBkZSBsaWduZXMgcXVpIHBhc3NlbnQgcGFyIGNoYWN1bmUgZGVzIGdhcmVzIGRlcyBIYXV0cy1kZS1GcmFuY2UgOiAgIA0KICANCiAgICANCmBgYHtyLCBvdXQud2lkdGg9JzEwMCUnfQ0KZ2FyZXNfc2VydmljZXMNCg0KYGBgDQoNCioqRXh0cmFjdGlvbiBkZXMgZWRnZXMgcG91ciBpR3JhcGgqKg0KDQpgYGB7cn0NCiMgSW5pdGlhbGlzYXRpb24gZGUgbGEgbGlzdGUgcG91ciBsZXMgbm9tcyBkZSBsaWduZXMgZGVzIGdhcmVzDQpsaWduZXNfZ2FyZXNfbmFtZXMgPC0gbGlzdCgpDQoNCiMgQm91Y2xlIHN1ciBjaGFxdWUgaWRlbnRpZmlhbnQgZGUgZ2FyZQ0KZm9yKCBpIGluIDE6bnJvdyhzdG9wcykpIHsNCnRyYWpldHNfZ2FyZSA8LSBzdG9wX3RpbWVzICU+JSANCiAgZmlsdGVyKHN0b3BfaWQgJWluJSBub21faWRbaV0pDQpoZWFkKHRyYWpldHNfZ2FyZSkNCmRpbSh0cmFqZXRzX2dhcmUpDQoNCnRyYWpldHNfZ2FyZSA8LSB0cmFqZXRzX2dhcmUkdHJpcF9pZA0KDQojIEZpbHRyZXIgbGVzIHRyYWpldHMgcGFyIHRyaXBfaWQgcG91ciBvYnRlbmlyIGxlcyByb3V0ZV9pZCBjb3JyZXNwb25kYW50cw0Kcm91dGVzX2lkcyA8LSB0cmlwcyAlPiUNCiAgZmlsdGVyKHRyaXBfaWQgJWluJSB0cmFqZXRzX2dhcmUpDQpoZWFkKHRyYWpldHNfZ2FyZSkNCmRpbSh0cmFqZXRzX2dhcmUpDQpyb3V0ZXNfaWRzIDwtIHJvdXRlc19pZHMkcm91dGVfaWQNCnJvdXRlc19pZHMNCg0KICMgRmlsdHJlciBsZXMgbGlnbmVzL3JvdXRlcyBwYXIgcm91dGVfaWQNCm5hbWVzKHJvdXRlcykNCg0KbGlnbmVzX2dhcmVzIDwtIHJvdXRlcyAlPiUgDQogIGZpbHRlcihyb3V0ZV9pZCAlaW4lIHJvdXRlc19pZHMpDQpsaWduZXNfZ2FyZXMNCm5yb3cobGlnbmVzX2dhcmVzKQ0KDQojIEFqb3V0ZXIgbGVzIG5vbXMgZGVzIGxpZ25lcyDDoCBsYSBsaXN0ZSBkZXMgbm9tcyBkZXMgZ2FyZXMNCiAgbGlnbmVzX2dhcmVzX25hbWVzW1tpXV0gPC0gbGlnbmVzX2dhcmVzJHJvdXRlX2xvbmdfbmFtZQ0KfQ0KDQojIEFwbGF0aXIgbGEgbGlzdGUgZW4gdW4gdmVjdGV1ciB1bmlxdWUgZGUgbm9tcyBkZSBsaWduZXMNCmxpZ25lc19nYXJlc19uYW1lcyA8LSB1bmxpc3QobGlnbmVzX2dhcmVzX25hbWVzKQ0KbGlnbmVzX2dhcmVzX25hbWVzIDwtIHVuaXF1ZShsaWduZXNfZ2FyZXNfbmFtZXMpDQoNCiMgQWZmaWNoZXIgbGVzIG5vbXMgdW5pcXVlcyBkZXMgbGlnbmVzIGRlIGdhcmUNCnByaW50KGxpZ25lc19nYXJlc19uYW1lcykNCmRmX2dhcmVzX291aWdvIDwtIGxpZ25lc19nYXJlc19uYW1lcw0KDQpkZl9nYXJlc19vdWlnbyA8LSBjKCdCb3JkZWF1eCAtIExpbGxlIFNFQSIgIklTIE5vcmQgU3VkJykNCg0KIyBUcmFuc2Zvcm1lciBsZSB2ZWN0ZXVyIGVuIGRhdGFmcmFtZSBhdmVjIGRldXggY29sb25uZXMgOiBHYXJlRGVwYXJ0IGV0IEdhcmVBcnJpdmVlDQpkZl9nYXJlc19vdWlnbyA8LSBkYXRhLmZyYW1lKA0KICBHYXJlRGVwYXJ0ID0gc2FwcGx5KGRmX2dhcmVzX291aWdvLCBmdW5jdGlvbih4KSBzdHJzcGxpdCh4LCAiIC0gIilbWzFdXVsxXSksDQogIEdhcmVBcnJpdmVlID0gc2FwcGx5KGRmX2dhcmVzX291aWdvLCBmdW5jdGlvbih4KSB7DQogICAgc3BsaXQgPC0gc3Ryc3BsaXQoeCwgIiAtICIpDQogICAgaWYgKGxlbmd0aChzcGxpdFtbMV1dKSA+IDEpIHJldHVybihzcGxpdFtbMV1dWzJdKQ0KICAgIGVsc2UgcmV0dXJuKE5BKSAjIFJldG91cm5lIE5BIHNpIGF1Y3VuZSBnYXJlIGQnYXJyaXbDqWUgbidlc3QgdHJvdXbDqWUNCiAgfSkNCikNCg0KIyBBZmZpY2hlciBsZSByw6lzdWx0YXQNCnByaW50KGRmX2dhcmVzX291aWdvKQ0Kcm93Lm5hbWVzKGRmX2dhcmVzX291aWdvKSA8LSBOVUxMDQpgYGANCg0KDQojIyMgQ29tcGlsYXRpb24gZGVzIGRvbm7DqWVzDQoNCmBgYHtyfQ0KZGZfZ2FyZXNfdGVyDQpkZl9nYXJlc190Z3ZfZGYNCmRmX2dhcmVzX291aWdvDQoNCmRmX2dhcmVzIDwtIHJiaW5kKGRmX2dhcmVzX3RlciwNCiAgICAgICAgICAgICAgICAgIGRmX2dhcmVzX3Rndl9kZiwNCiAgICAgICAgICAgICAgICAgIGRmX2dhcmVzX291aWdvKQ0KDQojIFN1cHByaW1lciBsZXMgbGlnbmVzIGF2ZWMgZGVzIHZhbGV1cnMgbWFucXVhbnRlcyBkYW5zIGxlcyBjb2xvbm5lcyBkZSBjb29yZG9ubsOpZXMNCmRmX2dhcmVzIDwtIGRmX2dhcmVzICU+JQ0KICBmaWx0ZXIoIWlzLm5hKEdhcmVEZXBhcnQpICYgIWlzLm5hKEdhcmVBcnJpdmVlKSkNCg0KcHJpbnQoZGZfZ2FyZXMpDQpgYGANCg0KDQojIyMgSUdSQVBIIDogbm9tYnJlIGRlIGNvbm5leGlvbnMgZGlyZWN0ZXMNCg0KRW5maW4gb24gY3LDqWUgdW4gb2JqZXQgcXVpIG1vbnRyZSBsZSBub21icmUgZGUgY29ubmV4aW9ucyBkaXJlY3RlcyBwb3VyIGNoYWN1bmUgZ2FyZXMgc2l0dcOpZXMgZW4gYm91dCBkZSBsaWduZS4gIA0KDQpgYGB7ciwgb3V0LndpZHRoPScxMDAlJ30NCiMgQ3LDqWF0aW9uIGR1IGdyYXBoZQ0KZyA8LSBpZ3JhcGg6OmdyYXBoX2Zyb21fZGF0YV9mcmFtZShkZl9nYXJlcywgDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGRpcmVjdGVkID0gRkFMU0UgIyBDZXQgYXJndW1lbnQgc3DDqWNpZmllIHF1ZSBsZSBncmFwaGUgY3LDqcOpIGVzdCBub24gZGlyaWfDqS4gRGFucyB1biBncmFwaGUgbm9uIGRpcmlnw6ksIGxlcyBhcsOqdGVzIG4nb250IHBhcyBkZSBkaXJlY3Rpb24sIGNlIHF1aSBzaWduaWZpZSBxdWUgbGEgcmVsYXRpb24gZW50cmUgZGV1eCBuxZN1ZHMgZXN0IGJpZGlyZWN0aW9ubmVsbGUuIA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICApDQpgYGANCiAgDQpPbiBwZXV0IGRvbmMgbGlyZSBxdWUgTGlsbGUgRmxhbmRyZXMgcG9zc8OoZGUgMTcgY29ubmV4aW9ucyBkaXJlY3RlcyBhdmVjIGQnYXV0cmVzIGdhcmVzIGRlIGxhIHLDqWdpb24gSGF1dHMtZGUtRnJhbmNlLiAgIA0KDQogIA0KYGBge3IsIG91dC53aWR0aD0nMTAwJSd9DQojIENhbGN1bCBkdSBub21icmUgZGUgbGllbnMgY29ubmVjdMOpcyDDoCBjaGFxdWUgbsWTdWQgKHN0YXRpb24pDQpzb3J0KGRlZ3JlZShnKSwgZGVjcmVhc2luZyA9IFQpDQpgYGANCg0KDQojIyMgSUdSQVBIIDogdW5pcXVlbWVudCBsJ2luZnJhc3RydWN0dXJlIChwcmVtaWVyIGpldCkNCg0KQ2Ugc2NyaXB0IGZvdXJuaXQgZG9uYyB1bmUgbcOpdGhvZG9sb2dpZSBwb3VyIGxlIHRyYWl0ZW1lbnQsIGxhIHZpc3VhbGlzYXRpb24gZXQgbCdhbmFseXNlIGRlIGRvbm7DqWVzIHNwYXRpYWxlcyBjb21wbGV4ZXMsIGVuIHNlIGNvbmNlbnRyYW50IHN1ciBsYSBjb21wcsOpaGVuc2lvbiBkZSBsYSBzdHJ1Y3R1cmUgZXQgZGUgbCdpbXBvcnRhbmNlIGRlcyBjb21wb3NhbnRlcyBkJ3VuIHLDqXNlYXUgZmVycm92aWFpcmUuICANCg0KKipPYmplY3RpZnMgR8OpbsOpcmF1eCBkdSBTY3JpcHQgOioqICANCiogQW5hbHlzZXIgbGEgc3RydWN0dXJlIGR1IHLDqXNlYXUgZmVycm92aWFpcmUgOiBFbiBmdXNpb25uYW50IGxlcyBzZWdtZW50cyBkZSBsaWduZSBldCBlbiBleHRyYXlhbnQgbGVzIHBvaW50cyB0ZXJtaW5hdXgsIGxlIHNjcmlwdCBzaW1wbGlmaWUgbGEgcmVwcsOpc2VudGF0aW9uIGR1IHLDqXNlYXUgcG91ciBmYWNpbGl0ZXIgbCdhbmFseXNlLiAgDQoqIFZpc3VhbGlzZXIgbGVzIGRvbm7DqWVzIHNwYXRpYWxlcyA6IG5vdXMgdXRpbGlzb25zICptYXB2aWV3KiBwb3VyIG1vbnRyZXIgbGVzIGxpZ25lcyBmZXJyb3ZpYWlyZXMsIGxldXJzIHBvaW50cyB0ZXJtaW5hdXgsIGV0IGxlcyBnYXJlcywgYWlkYW50IMOgIGNvbXByZW5kcmUgdmlzdWVsbGVtZW50IGxhIGRpc3Bvc2l0aW9uIHNwYXRpYWxlLiAgDQoqIMOJdmFsdWVyIGwnaW1wb3J0YW5jZSBkZXMgZ2FyZXMgOiBDYWxjdWxlIGxhIGNlbnRyYWxpdMOpIGRlIGRlZ3LDqSBwb3VyIGlkZW50aWZpZXIgbGVzIGdhcmVzIGNsw6lzIGRhbnMgbGUgcsOpc2VhdSwgYydlc3Qtw6AtZGlyZSBjZWxsZXMgYXlhbnQgbGUgcGx1cyBncmFuZCBub21icmUgZGUgY29ubmV4aW9ucyBkaXJlY3RlcyBhdmVjIGQnYXV0cmVzIGdhcmVzLiAgICANCg0KTm90ZSA6IEljaSwgamUgbmUgcHJlbmRzIHBhcyBlbiBjb21wdGUgbCdlbnNlbWJsZSBkZXMgbGlnbmVzIGVuIHNlcnZpY2Ugc3VyIGwnaW5mcmFzdHJ1Y3R1cmUgZmVycm92aWFpcmUuIEplIG1lIGNvbnRlbnRlIGRlIGNvbXB0ZXIgbGVzIHBvaW50cyB0ZXJtaW5hdXggZGUgY2hhcXVlIGxpZ25lIGQndW4gcG9pbnQgZGUgdnVlIGRlIGwnaW5mcmFzdHJ1Y3R1cmUuICANCiAgDQoNCmBgYHtyfQ0KaWYgKCFyZXF1aXJlTmFtZXNwYWNlKCJwYWNtYW4iLCBxdWlldGx5ID0gVFJVRSkpIGluc3RhbGwucGFja2FnZXMoInBhY21hbiIpDQpwYWNtYW46OnBfbG9hZChzZiwgaWdyYXBoLCBkcGx5ciwgbWFwdmlldykNCmBgYA0KDQoNCmBgYHtyLCBvdXQud2lkdGg9JzEwMCUnfQ0KIyBDaGFyZ2VtbmVudCBkZXMgZG9ubsOpZXMNCnJlc2VhdSA8LSBzZjo6c3RfcmVhZCgnZGF0YS9yZXNlYXVfZmVycm92aWFpcmUg4oCUIHJlc2VhdV9mZXJyb3ZpYWlyZV8xMzA2MjAyMl8xN2gwMCBfIEhERi5ncGtnJykNCm5hbWVzKHJlc2VhdSkNCm1hcHZpZXcocmVzZWF1KQ0KcmVzZWF1X3NlbGVjdCA8LSByZXNlYXUgJT4lIA0KICBmaWx0ZXIoY29kZV9saWduZSA9PSAyODkwMDApDQpoZWFkKHJlc2VhdV9zZWxlY3QpDQptYXB2aWV3KHJlc2VhdV9zZWxlY3QpDQpucm93KHJlc2VhdSkNCg0KIyBGdXNpb25uZXIgbGVzIHNlZ21lbnRzIGF5YW50IGxlIG3Dqm1lIGNvZGVfbGlnbmUNCmxpZ25lX2Z1c2lvbm5lZSA8LSByZXNlYXUgJT4lDQogIGdyb3VwX2J5KGNvZGVfbGlnbmUpICU+JQ0KICBzdW1tYXJpc2UoZ2VvbSA9IHN0X3VuaW9uKGdlb20pKSAlPiUNCiAgdW5ncm91cCgpDQpucm93KGxpZ25lX2Z1c2lvbm5lZSkNCnJlc2VhdV9zZWxlY3QgPC0gbGlnbmVfZnVzaW9ubmVlICU+JSANCiAgZmlsdGVyKGNvZGVfbGlnbmUgPT0gMjg5MDAwKQ0KaGVhZChyZXNlYXVfc2VsZWN0KQ0KbWFwdmlldyhyZXNlYXVfc2VsZWN0KQ0KDQojIEFmZmljaGFnZSBkZSBsYSBjYXJ0ZQ0KbWFwdmlldyhsaWduZV9mdXNpb25uZWUpDQpucm93KGxpZ25lX2Z1c2lvbm5lZSkNCmBgYA0KDQoNCmBgYHtyLCBvdXQud2lkdGg9JzEwMCUnfQ0KZ2FyZXMgPC0gc2Y6OnN0X3JlYWQoJ2RhdGEvZ2FyZXNfSERGXzIxNTQuZ3BrZycpDQpnYXJlc19zZWxlY3QgPC0gZ2FyZXMgJT4lIA0KICBzZWxlY3Qobm9tX2dhcmUpDQptYXB2aWV3KGdhcmVzKQ0KbnJvdyhnYXJlc19zZWxlY3QpDQpgYGANCg0KDQpgYGB7ciwgb3V0LndpZHRoPScxMDAlJywgd2FybmluZyA9IEZ9DQojID09PT09PT09PT09PT09PT09PT09PT09DQpsaWduZXMgPC0gbGlnbmVfZnVzaW9ubmVlICANCg0KIyBFeHRyYWlyZSBsYSBnw6lvbcOpdHJpZSBkZXMgbGlnbmVzDQpnZW9tZXRyaWVzIDwtIHN0X2dlb21ldHJ5KGxpZ25lcykNCg0KIyBVdGlsaXNlciBsYXBwbHkgcG91ciBhcHBsaXF1ZXIgdW5lIGZvbmN0aW9uIMOgIGNoYXF1ZSBnw6lvbcOpdHJpZSBkZSBsaWduZQ0KcG9pbnRzX3Rlcm1pbmF1eCA8LSBsYXBwbHkoZ2VvbWV0cmllcywgZnVuY3Rpb24obGluZV9nZW9tKSB7DQogICMgRXh0cmFpcmUgbGVzIGNvb3Jkb25uw6llcyBkZXMgcG9pbnRzIHRlcm1pbmF1eCBkZSBsYSBnw6lvbcOpdHJpZSBkZSBsaWduZQ0KICBjb29yZHMgPC0gc3RfY29vcmRpbmF0ZXMobGluZV9nZW9tKVtjKDEsIG5yb3coc3RfY29vcmRpbmF0ZXMobGluZV9nZW9tKSkpLCBdDQogICMgQ3LDqWVyIHVuIE1VTFRJUE9JTlQgw6AgcGFydGlyIGRlcyBjb29yZG9ubsOpZXMgZGVzIHRlcm1pbmF1eA0KICBzdF9tdWx0aXBvaW50KGFzLm1hdHJpeChjb29yZHMpLCBkaW0gPSAiWFkiKQ0KfSkNCg0KIyBDb21iaW5lciBsZXMgb2JqZXRzIE1VTFRJUE9JTlQgZW4gdW5lIHNldWxlIGNvbGxlY3Rpb24gZGUgZmVhdHVyZXMgZ8Opb23DqXRyaXF1ZXMgKHNmYykNCnBvaW50c190ZXJtaW5hdXhfc2ZjIDwtIGRvLmNhbGwoc3Rfc2ZjLCBhcmdzID0gbGlzdChwb2ludHNfdGVybWluYXV4LCBjcnMgPSBzdF9jcnMobGlnbmVzKSkpDQoNCiMgU3VwcHJpbWVyIGxlcyBkaW1lbnNpb25zIFogKGFsdGl0dWRlKSBldCBNIChlbnRpdMOpIGRlIHN0b2NrYWdlKQ0KcG9pbnRzX3Rlcm1pbmF1eF9zZmMgPC0gc3Rfem0ocG9pbnRzX3Rlcm1pbmF1eF9zZmMpDQoNCiMgQ29udmVydGlyIE1VTFRJUE9JTlQgZW4gUE9JTlQgcG91ciBhdm9pciBkZXMgcG9pbnRzIGluZGl2aWR1ZWxzDQpwb2ludHNfdGVybWluYXV4X3NmYyA8LSBzdF9jYXN0KHBvaW50c190ZXJtaW5hdXhfc2ZjLCAiUE9JTlQiKQ0KDQojIEFmZmljaGFnZSBkZSBsYSBjYXJ0ZQ0KbWFwdmlldyhzdF9hc19zZihwb2ludHNfdGVybWluYXV4X3NmYykpICsgbWFwdmlldyhsaWduZXMpDQpgYGANCg0KDQpgYGB7ciwgb3V0LndpZHRoPScxMDAlJywgbWVzc2FnZSA9IEZ9DQojIENyw6lhdGlvbiBkJ3VuIHNmIGRhdGFmcmFtZSBwb3VyIGxlcyBwb2ludHMgdGVybWluYXV4DQojIENlIGNvZGUgY3LDqWUgdW4gZGYgYXZlYyBwb3VyIGNoYXF1ZSBsaWduZSBsZXMgcG9pbnRzIHNpdHXDqXMgYXV4IGV4dHLDqW1pdMOpcw0KcG9pbnRzX3Rlcm1pbmF1eF9kZiA8LSBzdF9zZihnZW9tZXRyeSA9IHBvaW50c190ZXJtaW5hdXhfc2ZjLCANCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbGluZV9pZCA9IHJlcChsaWduZXMkY29kZV9saWduZSwgZWFjaCA9IDIpKSANCnN0X2Nycyhwb2ludHNfdGVybWluYXV4X2RmKSA8LSAnRVBTRzoyMTU0JyANCg0KIyBBZmZpY2hhZ2UgZGUgbGEgY2FydGUNCm1hcHZpZXcoc3RfYXNfc2YocG9pbnRzX3Rlcm1pbmF1eF9kZikpICsgbWFwdmlldyhsaWduZXMpICsgbWFwdmlldyhnYXJlc19zZWxlY3QsIGNvbC5yZWdpb24gPSAncmVkJykNCmBgYA0KDQoNCmBgYHtyLCBvdXQud2lkdGg9JzEwMCUnfQ0KIyBgZ2FyZXNfc2VsZWN0YCBlc3QgbCdvYmpldCBzZiBjb250ZW5hbnQgbGVzIGdhcmVzIGF2ZWMgbGV1cnMgaWRlbnRpZmlhbnRzIHVuaXF1ZXMNCiMgZXQgYHBvaW50c190ZXJtaW5hdXhfc2ZgIGVzdCB1biBvYmpldCBzZiBkZXMgcG9pbnRzIHRlcm1pbmF1eA0KDQojIENhbGN1bCBkZSBsYSBnYXJlIGxhIHBsdXMgcHJvY2hlIHBvdXIgY2hhcXVlIHBvaW50IHRlcm1pbmFsDQppbmRpY2VzX2dhcmVzX3Byb2NoZXMgPC0gc3RfbmVhcmVzdF9mZWF0dXJlKHBvaW50c190ZXJtaW5hdXhfZGYsIGdhcmVzX3NlbGVjdCkNCg0KIyBBam91dGVyIGwnaWRlbnRpZmlhbnQgZGUgbGEgZ2FyZSBsYSBwbHVzIHByb2NoZSDDoCBgcG9pbnRzX3Rlcm1pbmF1eF9kZmANCnBvaW50c190ZXJtaW5hdXhfZGYkZ2FyZV9pZCA8LSBnYXJlc19zZWxlY3Qkbm9tX2dhcmVbaW5kaWNlc19nYXJlc19wcm9jaGVzXQ0KDQpoZWFkKHBvaW50c190ZXJtaW5hdXhfZGYpDQoNCiMgQWZmaWNoYWdlIGRlIGxhIGNhcnRlDQptYXB2aWV3KHBvaW50c190ZXJtaW5hdXhfZGYpICsgbWFwdmlldyhsaWduZXMpDQoNCiMgVHJpZXIgcG9pbnRzX3Rlcm1pbmF1eF9kZiBwYXIgbGluZV9pZCANCnBvaW50c190ZXJtaW5hdXhfZGYgPC0gcG9pbnRzX3Rlcm1pbmF1eF9kZltvcmRlcihwb2ludHNfdGVybWluYXV4X2RmJGxpbmVfaWQpLF0NCg0KIyBDcsOpZXIgbGUgZGF0YWZyYW1lIGQnYXLDqnRlcyBlbiBhc3NvY2lhbnQgY2hhcXVlIHBhaXJlIGRlIGdhcmVzIHBhciBsaW5lX2lkDQplZGdlcyA8LSBkYXRhLmZyYW1lKA0KICBmcm9tID0gcG9pbnRzX3Rlcm1pbmF1eF9kZiRnYXJlX2lkW2MoVFJVRSwgRkFMU0UpXSwgICMgU8OpbGVjdGlvbiBkZXMgZ2FyZXMgZGUgZMOpcGFydA0KICB0byA9IHBvaW50c190ZXJtaW5hdXhfZGYkZ2FyZV9pZFtjKEZBTFNFLCBUUlVFKV0gICAgICMgU8OpbGVjdGlvbiBkZXMgZ2FyZXMgZCdhcnJpdsOpZQ0KKQ0KDQojIFN1cHByaW1lciBsZXMgw6l2ZW50dWVscyBkb3VibG9ucyBvdSBjb25uZXhpb25zIGRlIGdhcmUgw6AgZWxsZS1tw6ptZQ0KIyBQYXIgZXhlbXBsZSAzMDYwMDAgcXVpIHBhcnQgZCdBcnJhcyBldCBxdWkgeSBhcnJpdmUNCiMgQ2Ugc29udCBwcmluY2lwYWxlbWVudCBkZSBwZXRpdCB0cm9uw6dvbi4gMzA2MDAwIDogVm9pZSB1bmlxdWUgZnJldA0KZWRnZXMgPC0gdW5pcXVlKGVkZ2VzW2VkZ2VzJGZyb20gIT0gZWRnZXMkdG8sXSkNCmVkZ2VzDQpgYGANCg0KDQpgYGB7ciwgb3V0LndpZHRoPScxMDAlJ30NCiMgQ3LDqWF0aW9uIGR1IGdyYXBoZQ0KZyA8LSBpZ3JhcGg6OmdyYXBoX2Zyb21fZGF0YV9mcmFtZShlZGdlcywgZGlyZWN0ZWQgPSBGQUxTRSkNCg0KIyBDYWxjdWwgZHUgbm9tYnJlIGRlIGxpZW5zIGNvbm5lY3TDqXMgw6AgY2hhcXVlIG7Fk3VkIChzdGF0aW9uKQ0Kc29ydChkZWdyZWUoZyksIGRlY3JlYXNpbmcgPSBUKQ0KYGBgDQoNCioqQU5BTFlTRSoqICANCg0KKkNlbnRyYWxpdMOpIGRlIERlZ3LDqSBFbGV2w6llKiA6IExlcyBzdGF0aW9ucyBjb21tZSBWYWxlbmNpZW5uZXMsIEJvdWxvZ25lLCBMYW9uLCBTYWxsYXVtaW5lcywgRHVua2VycXVlLCBldCBTYWludC1Sb2NoIG9udCBsZXMgY2VudHJhbGl0w6lzIGRlIGRlZ3LDqSBsZXMgcGx1cyDDqWxldsOpZXMgZGFucyBsZSByw6lzZWF1LCBhdmVjIGRlcyB2YWxldXJzIGRlIDQgb3UgMy4gQ2VsYSBzaWduaWZpZSBxdWUgY2VzIHN0YXRpb25zIHNvbnQgY29ubmVjdMOpZXMgZGlyZWN0ZW1lbnQgw6AgNCBvdSAzIGF1dHJlcyBzdGF0aW9ucy4gRGFucyBsZSBjb250ZXh0ZSBkJ3VuIHLDqXNlYXUgZGUgdHJhbnNwb3J0LCB1bmUgY2VudHJhbGl0w6kgZGUgZGVncsOpIMOpbGV2w6llIHBvdXIgdW5lIHN0YXRpb24gaW5kaXF1ZSB1biByw7RsZSBwb3RlbnRpZWxsZW1lbnQgY3J1Y2lhbCBkYW5zIGxlIHLDqXNlYXUsIHNlcnZhbnQgY29tbWUgdW4gaHViIG91IHVuIHBvaW50IGRlIHRyYW5zZmVydCBtYWpldXIgb8O5IGRlIG5vbWJyZXVzZXMgbGlnbmVzIG91IHRyYWpldHMgc2UgY3JvaXNlbnQuICANCg0KKlN0YXRpb25zIGF2ZWMgTW9pbnMgZGUgQ29ubmV4aW9ucyogOiBMZXMgc3RhdGlvbnMgYXZlYyB1bmUgY2VudHJhbGl0w6kgZGUgZGVncsOpIGRlIDIgc29udCBjb25uZWN0w6llcyBkaXJlY3RlbWVudCDDoCBkZXV4IGF1dHJlcyBzdGF0aW9ucy4gQ2VsYSBzdWdnw6hyZSBxdSdlbGxlcyBzb250IG1vaW5zIGNlbnRyYWxlcyBxdWUgbGVzIHByZW1pw6hyZXMgbWFpcyBqb3VlbnQgdG91am91cnMgdW4gcsO0bGUgZGFucyBsYSBjb25uZWN0aXZpdMOpIGR1IHLDqXNlYXUuICANCg0KKlN0YXRpb25zIFDDqXJpcGjDqXJpcXVlcyogOiBMZXMgc3RhdGlvbnMgYXZlYyB1bmUgY2VudHJhbGl0w6kgZGUgZGVncsOpIGRlIDEsIGNvbW1lIE5vZ2VudCBsJ0FydGF1ZC1DaGFybHksIEFndWlsY291cnQtVmFyaXNjb3VydCwgZXQgQ3lzb2luZywgc29udCBjb25uZWN0w6llcyBkaXJlY3RlbWVudCDDoCB1bmUgc2V1bGUgYXV0cmUgc3RhdGlvbi4gQ2VzIHN0YXRpb25zIHBldXZlbnQgw6p0cmUgY29uc2lkw6lyw6llcyBjb21tZSBww6lyaXBow6lyaXF1ZXMgb3UgdGVybWluYWxlcyBkYW5zIGxlIHLDqXNlYXUsIMOpdGFudCDDoCBsJ2V4dHLDqW1pdMOpIGQndW5lIGxpZ25lIG91IGF5YW50IG1vaW5zIGQnaW1wb3J0YW5jZSBkYW5zIGxhIHN0cnVjdHVyZSBnbG9iYWxlIGR1IHLDqXNlYXUuICANCg0KDQoNCg0KDQojIyMgQmlibGlvZ3JhcGhpZSAgDQoNCjxicj4NCg0KUG91ciBwbHVzIGRlIGTDqXRhaWxzIHN1ciBsYSB0aMOpb3JpZSBkZXMgZ3JhcGhzIGV0IGxlICpkZWdyZWUgY2VudHJhbGl0eSogdm91cyBwb3V2ZXogY29uc3VsdGVyIGNlIA0KPGEgaHJlZj0iaHR0cHM6Ly90b3dhcmRzZGF0YXNjaWVuY2UuY29tL21hcHBpbmctdGhlLWphbXMtdHJhZmZpYy1hbmFseXNpcy11c2luZy1ncmFwaC10aGVvcnktYTM4NzEzNWVhNzQ4IiB0YXJnZXQ9Il9ibGFuayI+bGllbi4NCjwvYT4NCg0KPGJyPg0KDQojIyBDbG9zZW5lc3MgQ2VudHJhbGl0eQ0KDQpMYSBjbG9zZW5lc3MgY2VudHJhbGl0eSAob3UgY2VudHJhbGl0w6kgZGUgcHJveGltaXTDqSkgZXN0IHVuZSBtZXN1cmUgdXRpbGlzw6llIGVuIHRow6lvcmllIGRlcyBncmFwaGVzIGV0IGFuYWx5c2UgZGUgcsOpc2VhdXggcG91ciDDqXZhbHVlciBsJ2ltcG9ydGFuY2Ugb3UgbGEgY2VudHJhbGl0w6kgZCd1biBuxZN1ZCAocGFyIGV4ZW1wbGUsIHVuZSBnYXJlIGRhbnMgdW4gcsOpc2VhdSBkZSB0cmFuc3BvcnQpIHBhciByYXBwb3J0IMOgIHRvdXMgbGVzIGF1dHJlcyBuxZN1ZHMgZHUgcsOpc2VhdS4gRWxsZSBlc3QgZMOpZmluaWUgY29tbWUgbCdpbnZlcnNlIGRlIGxhIHNvbW1lIGRlcyBkaXN0YW5jZXMgbGVzIHBsdXMgY291cnRlcyBlbnRyZSBjZSBuxZN1ZCBldCB0b3VzIGxlcyBhdXRyZXMgbsWTdWRzIGRhbnMgbGUgcsOpc2VhdS4NCg0KRW4gZCdhdXRyZXMgdGVybWVzLCB1biBuxZN1ZCBhdmVjIHVuZSBoYXV0ZSAqY2xvc2VuZXNzIGNlbnRyYWxpdHkqIGVzdCB1biBuxZN1ZCBxdWksIGVuIG1veWVubmUsIGVzdCBwbHVzIHByb2NoZSBkZSB0b3VzIGxlcyBhdXRyZXMgbsWTdWRzIGRhbnMgbGUgcsOpc2VhdS4gQ2VsYSBzaWduaWZpZSBxdWUgZGVwdWlzIGNlIG7Fk3VkLCBvbiBwZXV0IGF0dGVpbmRyZSByYXBpZGVtZW50IHRvdXMgbGVzIGF1dHJlcyBuxZN1ZHMsIGNlIHF1aSBsZSByZW5kIHN0cmF0w6lnaXF1ZW1lbnQgaW1wb3J0YW50IHBvdXIgbGEgZGlmZnVzaW9uIHJhcGlkZSBkJ2luZm9ybWF0aW9ucywgbGEgcsOpZHVjdGlvbiBkZXMgdGVtcHMgZGUgdHJhamV0LCBvdSBsJ2VmZmljYWNpdMOpIGRlcyDDqWNoYW5nZXMgYXUgc2VpbiBkdSByw6lzZWF1LiAgDQoNClBvdXIgY2FsY3VsZXIgbGEgKmNsb3NlbmVzcyBjZW50cmFsaXR5KiBkZXMgZ2FyZXMgZGVzIEhhdXRzLWRlLUZyYW5jZSBub3VzIHV0aWxpc29ucyBkZXMgZG9ubsOpZXMgR1RGUy4gTGVzIGRvbm7DqWVzIHV0aWxpc8OpZXMgZGFucyBsZXMgc2NyaXB0cyBzdWl2YW50cyBwcm92aWVubmVudCBkdSBzaXRlIE9wZW4gRGF0YSBTTkNGIChjb25zdWx0w6kgbGUgMTkgZXQgbGUgMjAgZsOpdnJpZXIgMjAyNCkuICANCg0KKkNsb3NlbmVzcyBjZW50cmFsaXR5IGlzIGEgbWVhc3VyZSB1c2VkIGluIGdyYXBoIHRoZW9yeSBhbmQgbmV0d29yayBhbmFseXNpcyB0byBhc3Nlc3MgdGhlIGltcG9ydGFuY2Ugb3IgY2VudHJhbGl0eSBvZiBhIG5vZGUgKGZvciBleGFtcGxlLCBhIHN0YXRpb24gaW4gYSB0cmFuc3BvcnRhdGlvbiBuZXR3b3JrKSBpbiByZWxhdGlvbiB0byBhbGwgb3RoZXIgbm9kZXMgaW4gdGhlIG5ldHdvcmsuIEl0IGlzIGRlZmluZWQgYXMgdGhlIGludmVyc2Ugb2YgdGhlIHN1bSBvZiB0aGUgc2hvcnRlc3QgZGlzdGFuY2VzIGJldHdlZW4gdGhpcyBub2RlIGFuZCBhbGwgb3RoZXIgbm9kZXMgaW4gdGhlIG5ldHdvcmsuICAqDQoNCipJbiBvdGhlciB3b3JkcywgYSBub2RlIHdpdGggaGlnaCBjbG9zZW5lc3MgY2VudHJhbGl0eSBpcyBvbmUgdGhhdCwgb24gYXZlcmFnZSwgaXMgY2xvc2VyIHRvIGFsbCBvdGhlciBub2RlcyBpbiB0aGUgbmV0d29yay4gVGhpcyBtZWFucyB0aGF0IGZyb20gdGhpcyBub2RlLCBhbGwgb3RoZXIgbm9kZXMgY2FuIGJlIHJlYWNoZWQgcXVpY2tseSwgbWFraW5nIGl0IHN0cmF0ZWdpY2FsbHkgaW1wb3J0YW50IGZvciB0aGUgcmFwaWQgZGlzc2VtaW5hdGlvbiBvZiBpbmZvcm1hdGlvbiwgdGhlIHJlZHVjdGlvbiBvZiB0cmF2ZWwgdGltZXMsIG9yIHRoZSBlZmZpY2llbmN5IG9mIGV4Y2hhbmdlcyB3aXRoaW4gdGhlIG5ldHdvcmsuICAqDQoNCipUbyBjYWxjdWxhdGUgdGhlIGNsb3NlbmVzcyBjZW50cmFsaXR5IG9mIHN0YXRpb25zIGluIHRoZSBIYXV0cy1kZS1GcmFuY2UgcmVnaW9uLCB3ZSB1c2UgR1RGUyBkYXRhLiBUaGUgZGF0YSB1c2VkIGluIHRoZSBmb2xsb3dpbmcgc2NyaXB0cyBjb21lcyBmcm9tIHRoZSBTTkNGIE9wZW4gRGF0YSBzaXRlIChjb25zdWx0ZWQgb24gRmVicnVhcnkgMTkgYW5kIDIwLCAyMDI0KS4gKiANCg0KDQpgYGB7ciwgb3V0LndpZHRoPScxMDAlJ30NCg0KcGFjbWFuOjpwX2xvYWQoc2YsIGRwbHlyLCBtYXB2aWV3LCBnZW9zcGhlcmUsIGlncmFwaCwgZ2dwbG90MikNCg0Kc3RvcHNfdGVyIDwtIHJlYWQuY3N2KCdkYXRhL0dURlMvZXhwb3J0LXRlci1ndGZzLWxhc3Qvc3RvcHMudHh0JywgaGVhZGVyID0gVFJVRSkNCiMgbmFtZXMoc3RvcHNfdGVyKQ0Kc3RvcHNfdGVyX3NlbGVjdCA8LSBzdG9wc190ZXIgJT4lIA0KICBkcGx5cjo6c2VsZWN0KHN0b3BfaWQsIHN0b3BfbmFtZSwgc3RvcF9sYXQsIHN0b3BfbG9uKQ0Kcm0oc3RvcHNfdGVyKQ0Kc3RvcHNfdGd2IDwtIHJlYWQuY3N2KCdkYXRhL0dURlMvZXhwb3J0X2d0ZnNfdm95YWdlcy9zdG9wcy50eHQnLCBoZWFkZXIgPSBUUlVFKQ0Kc3RvcHNfdGd2X3NlbGVjdCA8LSBzdG9wc190Z3YgJT4lIA0KICBkcGx5cjo6c2VsZWN0KHN0b3BfaWQsIHN0b3BfbmFtZSwgc3RvcF9sYXQsIHN0b3BfbG9uKQ0Kcm0oc3RvcHNfdGd2KQ0Kc3RvcHNfb3VpZ28gPC0gcmVhZC5jc3YoJ2RhdGEvR1RGUy9leHBvcnRfZ3Rmc192b3lhZ2VzL3N0b3BzLnR4dCcsIGhlYWRlciA9IFRSVUUpDQpzdG9wc19vdWlnb19zZWxlY3QgPC0gc3RvcHNfb3VpZ28gJT4lIA0KICBkcGx5cjo6c2VsZWN0KHN0b3BfaWQsIHN0b3BfbmFtZSwgc3RvcF9sYXQsIHN0b3BfbG9uKQ0Kcm0oc3RvcHNfb3VpZ28pDQpzdG9wcyA8LSByYmluZCgNCiAgc3RvcHNfdGVyX3NlbGVjdCwgDQogIHN0b3BzX3Rndl9zZWxlY3QsDQogIHN0b3BzX291aWdvX3NlbGVjdA0KKQ0Kcm0oc3RvcHNfdGVyX3NlbGVjdCxzdG9wc190Z3Zfc2VsZWN0LCBzdG9wc19vdWlnb19zZWxlY3QpDQpzdG9wc19zZWxlY3QgPC0gc3RvcHMgJT4lIGZpbHRlcihncmVwbCgnVHJhaW4nLCBzdG9wX2lkKSkNCiMgbmFtZXMoc3RvcHNfc2VsZWN0KQ0Kcm0oc3RvcHMpDQpzdG9wc19zZWxlY3Rfc2YgPC0gc3RvcHNfc2VsZWN0ICU+JQ0KICBzZjo6c3RfYXNfc2YoY29vcmRzID0gYygic3RvcF9sb24iLCAic3RvcF9sYXQiKSwgY3JzID0gNDMyNikNCnN0b3BzX3NlbGVjdF9zZiA8LSBzdF90cmFuc2Zvcm0oc3RvcHNfc2VsZWN0X3NmLCAyMTU0KQ0KIyBtYXB2aWV3KHN0b3BzX3NlbGVjdF9zZikNCg0KIyBTw6lsZWN0aW9uIGRlcyBhcnLDqnRzIGRhbnMgbGVzIEhhdXRzLWRlLUZyYW5jZQ0KcmVnaW9ucyA8LSBzZjo6c3RfcmVhZCgiZGF0YS9SRUdJT04uc2hwIikNCkhERiA8LSByZWdpb25zICU+JSANCiAgZmlsdGVyKElOU0VFX1JFRyA9PSAzMikgIyBmaWx0cmUgc3VyIGxlcyBIYXV0cy1kZS1GcmFuY2UNCnJtKHJlZ2lvbnMpDQpzdG9wc19zZWxlY3Rfc2ZfaW50ZXIgPC0gc2Y6OnN0X2ludGVyc2VjdGlvbihzdG9wc19zZWxlY3Rfc2YsIEhERikNCnJtKEhERikNCm1hcHZpZXc6Om1hcHZpZXcoc3RvcHNfc2VsZWN0X3NmX2ludGVyKQ0KIyBuYW1lcyhzdG9wc19zZWxlY3Rfc2ZfaW50ZXIpDQpybShzdG9wc19zZWxlY3Rfc2YpDQoNCnN0b3BzX3NlbGVjdF9zZl9pbnRlcl9zZWxlY3QgPC0gc3RvcHNfc2VsZWN0X3NmX2ludGVyICU+JSANCiAgc2VsZWN0KHN0b3BfaWQsIHN0b3BfbmFtZSwgZ2VvbWV0cnkpDQpoZWFkKHN0b3BzX3NlbGVjdF9zZl9pbnRlcl9zZWxlY3QpDQpzdG9wc19zZWxlY3Rfc2ZfaW50ZXJfc2VsZWN0IDwtIHN0X3RyYW5zZm9ybShzdG9wc19zZWxlY3Rfc2ZfaW50ZXJfc2VsZWN0LCA0MzI2KQ0KbmFtZXMoc3RvcHNfc2VsZWN0X3NmX2ludGVyX3NlbGVjdCkNCnJtKHN0b3BzX3NlbGVjdF9zZl9pbnRlcikNCmNvb3JkcyA8LSBzdF9jb29yZGluYXRlcyhzdG9wc19zZWxlY3Rfc2ZfaW50ZXJfc2VsZWN0KQ0Kc3RvcHNfc2VsZWN0IDwtIGRhdGEuZnJhbWUoc3RvcF9pZCA9IHN0b3BzX3NlbGVjdF9zZl9pbnRlcl9zZWxlY3Qkc3RvcF9pZCwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgIHN0b3BfbmFtZSA9IHN0b3BzX3NlbGVjdF9zZl9pbnRlcl9zZWxlY3Qkc3RvcF9uYW1lLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgc3RvcF9sYXQgPSBjb29yZHNbLDJdLCAjIFkgZXN0IGfDqW7DqXJhbGVtZW50IGxhIGxhdGl0dWRlDQogICAgICAgICAgICAgICAgICAgICAgICAgICBzdG9wX2xvbiA9IGNvb3Jkc1ssMV0pICMgWCBlc3QgZ8OpbsOpcmFsZW1lbnQgbGEgbG9uZ2l0dWRlDQoNCiMgQ2hhcmdlbWVudCBkZXMgYXV0cmVzIGZpY2hpZXJzDQpzdG9wX3RpbWVzIDwtIHJlYWQuY3N2KCdkYXRhL0dURlMvZXhwb3J0LXRlci1ndGZzLWxhc3Qvc3RvcF90aW1lcy50eHQnLCBoZWFkZXIgPSBUUlVFKQ0KdHJpcHMgPC0gcmVhZC5jc3YoJ2RhdGEvR1RGUy9leHBvcnQtdGVyLWd0ZnMtbGFzdC90cmlwcy50eHQnLCBoZWFkZXIgPSBUUlVFKQ0Kcm91dGVzIDwtIHJlYWQuY3N2KCdkYXRhL0dURlMvZXhwb3J0LXRlci1ndGZzLWxhc3Qvcm91dGVzLnR4dCcsIGhlYWRlciA9IFRSVUUpDQoNCiMgSm9pbmRyZSBsZXMgdGFibGVzIHBvdXIgb2J0ZW5pciB1bmUgdnVlIGNvbXBsw6h0ZSBpbmNsdWFudCBsJ29yZHJlIGRlcyBnYXJlcywgbGV1cnMgY29vcmRvbm7DqWVzLCBldCBsYSBsaWduZSBjb3JyZXNwb25kYW50ZQ0Kc3RvcHNfb3JkZXJlZCA8LSBzdG9wX3RpbWVzICU+JQ0KICBzZWxlY3QodHJpcF9pZCwgc3RvcF9pZCwgc3RvcF9zZXF1ZW5jZSkgJT4lDQogIGxlZnRfam9pbih0cmlwcywgYnkgPSAidHJpcF9pZCIpICU+JQ0KICBsZWZ0X2pvaW4ocm91dGVzLCBieSA9ICJyb3V0ZV9pZCIpICU+JQ0KICBsZWZ0X2pvaW4oc3RvcHNfc2VsZWN0LCBieSA9ICJzdG9wX2lkIikgJT4lDQogIGFycmFuZ2Uocm91dGVfaWQsIHRyaXBfaWQsIHN0b3Bfc2VxdWVuY2UpDQoNCnJtKHN0b3BfdGltZXMsIHRyaXBzLCByb3V0ZXMpDQojIHN0b3BzX29yZGVyZWRfZmlsdGVyIDwtIHN0b3BzX29yZGVyZWQgJT4lIA0KIyAgIGZpbHRlcihyb3V0ZV9pZCA9PSAnRlI6TGluZTo6REJGM0YxMDQtOUZGMC00OEY2LTg0Q0UtRTQ5RkQ2RDdGNjMwOicpDQoNCiMgw4AgY2Ugc3RhZGUsIHN0b3BzX29yZGVyZWQgY29udGllbnQgbCdvcmRyZSBkZXMgYXJyw6p0cyBwb3VyIGNoYXF1ZSBsaWduZSBldCB0cmFqZXQsIGF2ZWMgbGVzIGNvb3Jkb25uw6llcyBnw6lvZ3JhcGhpcXVlcw0KDQpzdG9wc19vcmRlcmVkX2NsZWFuIDwtIHN0b3BzX29yZGVyZWQgJT4lDQogIGZpbHRlcighaXMubmEoc3RvcF9zZXF1ZW5jZSkgJiAhaXMubmEoc3RvcF9sYXQpICYgDQogICAgICAgICAgICFpcy5uYShzdG9wX2xvbikpICU+JSANCiAgZmlsdGVyKGRpcmVjdGlvbl9pZCA9PSAwKQ0Kcm0oc3RvcHNfb3JkZXJlZCkNCg0Kc3RvcHNfb3JkZXJlZF9jbGVhbl9zZWxlY3QgPC0gc3RvcHNfb3JkZXJlZF9jbGVhbiAlPiUgDQogIHNlbGVjdChyb3V0ZV9pZCwgcm91dGVfbG9uZ19uYW1lLHN0b3BfbmFtZSwgZGlyZWN0aW9uX2lkLCBzdG9wX3NlcXVlbmNlLCBzdG9wX2xhdCwgc3RvcF9sb24pDQpybShzdG9wc19vcmRlcmVkX2NsZWFuKQ0KDQpkaW0oc3RvcHNfb3JkZXJlZF9jbGVhbl9zZWxlY3QpDQpoZWFkKHN0b3BzX29yZGVyZWRfY2xlYW5fc2VsZWN0KQ0KDQojIEljaSBqZSB0cmllIHBhciByb3V0ZSBldCBzdG9wX3NlcXVlbmNlIHB1aXMgamUgY2FsY3VsZSBsYSBkaXN0YW5jZSBhdmVjIGxhIGdhcmUgc3VpdmFudGUNCnN0b3BzX29yZGVyZWRfY2xlYW5fc2VsZWN0X2Rpc3QgPC0gc3RvcHNfb3JkZXJlZF9jbGVhbl9zZWxlY3QgJT4lDQogIGFycmFuZ2Uocm91dGVfaWQsIHN0b3Bfc2VxdWVuY2UpICU+JSAjIFMnYXNzdXJlciBxdWUgbGVzIGRvbm7DqWVzIHNvbnQgdHJpw6llcw0KICBncm91cF9ieShyb3V0ZV9pZCkgJT4lDQogIG11dGF0ZSgNCiAgICAjIENhbGN1bGVyIGxhIGRpc3RhbmNlIGF2ZWMgbGEgZ2FyZSBzdWl2YW50ZSBkYW5zIGxhIHPDqXF1ZW5jZQ0KICAgIGRpc3RhbmNlX3RvX25leHQgPSBkaXN0SGF2ZXJzaW5lKGNiaW5kKHN0b3BfbG9uLCBzdG9wX2xhdCksIA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNiaW5kKGxlYWQoc3RvcF9sb24pLCANCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBsZWFkKHN0b3BfbGF0KSkpDQogICkgJT4lDQogIHVuZ3JvdXAoKSAjIETDqWdyb3VwZW1lbnQgcG91ciBtYW5pcHVsYXRpb25zIHVsdMOpcmlldXJlcw0Kcm0oc3RvcHNfb3JkZXJlZF9jbGVhbl9zZWxlY3QpDQoNCiMgQWZmaWNoZXIgbGUgcsOpc3VsdGF0DQpkaW0oc3RvcHNfb3JkZXJlZF9jbGVhbl9zZWxlY3RfZGlzdCkNCg0KDQojIFByw6lwYXJlciBsZSB0YWJsZWF1IGVkZ2VzIFYyDQplZGdlcyA8LSBzdG9wc19vcmRlcmVkX2NsZWFuX3NlbGVjdF9kaXN0ICU+JQ0KICBmaWx0ZXIoIWlzLm5hKGRpc3RhbmNlX3RvX25leHQpKSAlPiUgICAgICAgICAgIyBzdXBwcmVzc2lvbiBkZXMgdmFsZXVycyBtYW5xdWFudGVzDQogIGdyb3VwX2J5KHJvdXRlX2lkKSAlPiUgICAgICAgICAgICAgICAgICAgICAgICAjIEdyb3VwZXIgcGFyIGl0aW7DqXJhaXJlDQogIG11dGF0ZSh0YXJnZXRfc3RvcF9uYW1lID0gbGVhZChzdG9wX25hbWUpLA0KICAgICAgICAgdGFyZ2V0X3N0b3Bfc2VxdWVuY2UgPSBsZWFkKHN0b3Bfc2VxdWVuY2UpLA0KICAgICAgICAgdGFyZ2V0X3N0b3BfbGF0ID0gbGVhZChzdG9wX2xhdCksDQogICAgICAgICB0YXJnZXRfc3RvcF9sb24gPSBsZWFkKHN0b3BfbG9uKSkgJT4lDQogIHVuZ3JvdXAoKSAlPiUgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAjIEVubGV2ZXIgbGUgcmVncm91cGVtZW50DQogIHNlbGVjdChyb3V0ZV9pZCwgDQogICAgICAgICByb3V0ZV9sb25nX25hbWUsDQogICAgICAgICBzb3VyY2Vfc3RvcF9uYW1lID0gc3RvcF9uYW1lLCANCiAgICAgICAgIHRhcmdldF9zdG9wX25hbWUsIA0KICAgICAgICAgc291cmNlX3NlcXVlbmNlID0gc3RvcF9zZXF1ZW5jZSwgDQogICAgICAgICB0YXJnZXRfc2VxdWVuY2UgPSB0YXJnZXRfc3RvcF9zZXF1ZW5jZSwNCiAgICAgICAgIHNvdXJjZV9sYXQgPSBzdG9wX2xhdCwgc291cmNlX2xvbiA9IHN0b3BfbG9uLCANCiAgICAgICAgIHRhcmdldF9sYXQgPSB0YXJnZXRfc3RvcF9sYXQsIHRhcmdldF9sb24gPSB0YXJnZXRfc3RvcF9sb24sIA0KICAgICAgICAgd2VpZ2h0ID0gZGlzdGFuY2VfdG9fbmV4dCkgJT4lDQogIGZpbHRlcighaXMubmEodGFyZ2V0X3N0b3BfbmFtZSkpICAjIEZpbHRyZSBwb3VyIGVubGV2ZXIgbGEgZGVybmnDqHJlIGxpZ25lIGRlIGNoYXF1ZSBncm91cGUgcXVpIG4nYSBwYXMgZGUgJ3RhcmdldCcNCg0KZWRnZXNfY2xvc2VuZXNzIDwtIGVkZ2VzICU+JQ0KICBmaWx0ZXIoc291cmNlX3N0b3BfbmFtZSAhPSB0YXJnZXRfc3RvcF9uYW1lKQ0KDQojIEFmZmljaGVyIGxlIHLDqXN1bHRhdA0KZGltKGVkZ2VzX2Nsb3NlbmVzcykNCmhlYWQoZWRnZXNfY2xvc2VuZXNzKQ0KDQplZGdlc19ncmFwaF9kYXRhIDwtIGVkZ2VzX2Nsb3NlbmVzcyAlPiUNCiAgc2VsZWN0KHNvdXJjZSA9IHNvdXJjZV9zdG9wX25hbWUsIHRhcmdldCA9IHRhcmdldF9zdG9wX25hbWUsIHdlaWdodCkNCg0KZGltKGVkZ2VzX2dyYXBoX2RhdGEpDQpoZWFkKGVkZ2VzX2dyYXBoX2RhdGEpDQoNCiMgUHLDqXBhcmVyIGxlcyBkb25uw6llcyBwb3VyIGlHcmFwaA0KZyA8LSBncmFwaF9mcm9tX2RhdGFfZnJhbWUoZCA9IGVkZ2VzX2dyYXBoX2RhdGEpDQoNCiMgQ2FsY3VsIGR1IHNjb3JlIGNlbnRyYWxpdMOpIGRlIHByb3hpbWl0w6kNCmNsb3NlbmVzc19zY29yZXMgPC0gY2xvc2VuZXNzKGcsIG1vZGUgPSAiYWxsIiwgd2VpZ2h0cyA9IEUoZykkd2VpZ2h0KQ0KDQpjbG9zZW5lc3NfZGYgPC0gZGF0YS5mcmFtZSgNCiAgc3RvcF9uYW1lID0gVihnKSRuYW1lLA0KICBjbG9zZW5lc3MgPSBjbG9zZW5lc3Nfc2NvcmVzDQopDQoNCiMgQWZmaWNoZXIgbGVzIHLDqXN1bHRhdHMNCmhlYWQoY2xvc2VuZXNzX2RmLDIwKQ0KdGFpbChjbG9zZW5lc3NfZGYsMjApDQojIENvbWJpbmVyIGxlcyBkb25uw6llcyBkZSBjbG9zZW5lc3MgY2VudHJhbGl0eSBhdmVjIGxlcyBjb29yZG9ubsOpZXMgZGVzIGdhcmVzDQpnYXJlc19zZiA8LSBzdG9wc19zZWxlY3Rfc2ZfaW50ZXJfc2VsZWN0ICU+JQ0KICBsZWZ0X2pvaW4oY2xvc2VuZXNzX2RmLCBieSA9ICJzdG9wX25hbWUiKQ0KbmFtZXMoZ2FyZXNfc2YpDQojIENvbnZlcnRpciBnYXJlc19zZiBlbiB1biBvYmpldCBzZiA8DQpnYXJlc19zZiA8LSBzdF9hc19zZihnYXJlc19zZiwgY29vcmRzID0gYygic3RvcF9sb24iLCAic3RvcF9sYXQiKSwgY3JzID0gNDMyNikNCg0KIyBDcsOpYXRpb24gZGUgbGEgaGVhdG1hcCBhdmVjIGdncGxvdDINCmdncGxvdChkYXRhID0gZ2FyZXNfc2YpICsNCiAgZ2VvbV9zZihhZXMoY29sb3IgPSBjbG9zZW5lc3MpLCBzaXplID0gMykgKw0KICBzY2FsZV9jb2xvcl92aXJpZGlzX2Mob3B0aW9uID0gIkEiKSArDQogIGxhYnModGl0bGUgPSAiSGVhdG1hcCBkZSBsYSBDbG9zZW5lc3MgQ2VudHJhbGl0eSBkZXMgR2FyZXMiLA0KICAgICAgIHN1YnRpdGxlID0gIkNoYXF1ZSBnYXJlIGVzdCBjb2xvcsOpZSBzZWxvbiBzb24gc2NvcmUgZGUgY2xvc2VuZXNzIGNlbnRyYWxpdHkiKSArDQogIHRoZW1lX21pbmltYWwoKSArDQogIHRoZW1lKGxlZ2VuZC50aXRsZSA9IGVsZW1lbnRfdGV4dChzaXplID0gMTIpLA0KICAgICAgICBsZWdlbmQudGV4dCA9IGVsZW1lbnRfdGV4dChzaXplID0gMTApKQ0KDQpsb3VyY2hlcyA8LSBnYXJlc19zZiAlPiUgDQogIGZpbHRlcihzdG9wX25hbWUgPT0gJ0xvdXJjaGVzJykNCm1hcHZpZXcobG91cmNoZXMpDQpgYGANCg0KKioqDQpMaWxsZSBGbGFuZHJlcyBlc3QgbCdhcnLDqnQgYXZlYyBsZSBzY29yZSBkZSBwcm94aW1pdMOpIGxlIHBsdXMgw6lsZXbDqSwgY2UgcXVpIHN1Z2fDqHJlIHF1J2lsIGVzdCBsZSBwbHVzIGNlbnRyYWwgb3UgbGUgbWlldXggY29ubmVjdMOpIHBhcm1pIHRvdXMgbGVzIGFycsOqdHMgbGlzdMOpcy4gQydlc3QgZMO7IMOgIHNhIHBvc2l0aW9uIHN0cmF0w6lnaXF1ZSBkYW5zIGxlIHLDqXNlYXUgZGUgdHJhbnNwb3J0IGV0IGF1IG5vbWJyZSDDqWxldsOpIGRlIGNvbm5leGlvbnMgZGlzcG9uaWJsZXMgw6AgcGFydGlyIGRlIGNldCBhcnLDqnQuICANCg0KRW4gb2JzZXJ2YW50IGxlcyBzY29yZXMgZGUgcHJveGltaXTDqSBsZXMgcGx1cyBiYXMgKHBhciBleGVtcGxlLCBQcm91dnkgLSBUaGlhbnQsIERlbmFpbiBvdSBMb3VyY2hlcyksIG9uIG5vdGUgdW5lIGRpbWludXRpb24gc2lnbmlmaWNhdGl2ZSwgY2UgcXVpIHBvdXJyYWl0IGluZGlxdWVyIGRlcyBhcnLDqnRzIG1vaW5zIGNlbnRyYXV4IG91IHBsdXMgw6lsb2lnbsOpcyBkZXMgcHJpbmNpcGF1eCBheGVzIGRlIHRyYW5zcG9ydC4gIA0KDQojIyMgQmlibGlvZ3JhcGhpZSAgDQoNCjxicj4NCg0KUG91ciBwbHVzIGRlIGTDqXRhaWxzIHN1ciBsYSB0aMOpb3JpZSBkZXMgZ3JhcGhzIGV0IGxlICpjbG9zZW5lc3MgY2VudHJhbGl0eSogdm91cyBwb3V2ZXogY29uc3VsdGVyIGNlIA0KPGEgaHJlZj0iaHR0cHM6Ly90b3dhcmRzZGF0YXNjaWVuY2UuY29tL21hcHBpbmctdGhlLWphbXMtdHJhZmZpYy1hbmFseXNpcy11c2luZy1ncmFwaC10aGVvcnktYTM4NzEzNWVhNzQ4IiB0YXJnZXQ9Il9ibGFuayI+bGllbi4NCjwvYT4NCg0KPGJyPg0KDQoNCiAgDQoNCiAgDQogIA0KIyMgT1NSTQ0KDQojIyMgT1NSTSBJU09DSFJPTkVTDQoNCiogKipDcsOpYXRpb24gZCdpc29jaHJvbmVzIGF2ZWMgb3NybUlzb2Nocm9uZSoqICANCg0KYGBge3IsIGV2YWw9Rn0NCg0KcGFjbWFuOjpwX2xvYWQoc2YsIG9zcm0pDQoNCmdhcmVzX3Jlbm5lc19jaGF0ZWF1IDwtIHNmOjpzdF9yZWFkKCdDOi9Vc2Vycy9PdGhhdXJlYXUvRG9jdW1lbnRzL0JERC9HRU9GRVIvZ2FyZXNfcmVubmVzX2NoYXRlYXVicmlhbnQuZ3BrZycpDQpnYXJlc19yZW5uZXNfY2hhdGVhdSA8LSBnYXJlc19yZW5uZXNfY2hhdGVhdSAlPiUgDQogIHN0X3RyYW5zZm9ybSg0MzI2KQ0KDQojIEFzc3VyZXotdm91cyBxdWUgdm9zIHBvaW50cyBzb250IGF1IGZvcm1hdCBzZiBldCBlbiBXR1M4NA0KcG9pbnRzX3NmIDwtIHN0X2FzX3NmKGdhcmVzX3Jlbm5lc19jaGF0ZWF1LCBjb29yZHMgPSBjKCJsb24iLCAibGF0IiksIGNycyA9IDQzMjYpDQoNCiMgSW5pdGlhbGlzZXIgdW5lIGxpc3RlIHBvdXIgc3RvY2tlciBsZXMgcsOpc3VsdGF0cyBkZXMgaXNvY2hyb25lcw0KbGlzdF9pc29jaHJvbmVzIDwtIGxpc3QoKQ0KDQojIEJvdWNsZSBwb3VyIGNhbGN1bGVyIGxlcyBpc29jaHJvbmVzIHBvdXIgY2hhcXVlIHBvaW50DQpmb3IgKGkgaW4gMTpucm93KHBvaW50c19zZikpIHsNCiAgIyBFeHRyYWlyZSBsZSBwb2ludCBhY3R1ZWwNCiAgcG9pbnQgPC0gcG9pbnRzX3NmW2ksIF0NCiAgDQogICMgQ2FsY3VsZXIgbCdpc29jaHJvbmUgZGUgMTUgbWludXRlcyDDoCBwaWVkDQogIGlzb2Nocm9uZSA8LSBvc3JtOjpvc3JtSXNvY2hyb25lKGxvYyA9IHBvaW50LCANCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcmV0dXJuY2xhc3MgPSAic2YiLCANCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgYnJlYWtzID0gMTUsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG9zcm0ucHJvZmlsZSA9ICdmb290JykgIyB0aGUgcm91dGluZyBwcm9maWxlIHRvIHVzZSwgZS5nLiAiY2FyIiwgImJpa2UiIG9yICJmb290IiANCiAgDQogICMgU3RvY2tlciBsJ2lzb2Nocm9uZSBkYW5zIGxhIGxpc3RlDQogIGxpc3RfaXNvY2hyb25lc1tbaV1dIDwtIGlzb2Nocm9uZQ0KfQ0KDQojIz8/b3NybUlzb2Nocm9uZQ0KDQojIEZ1c2lvbm5lciB0b3VzIGxlcyBpc29jaHJvbmVzIGRhbnMgdW4gc2V1bCBvYmpldCBzZiBzaSBuw6ljZXNzYWlyZQ0KYWxsX2lzb2Nocm9uZXMgPC0gZG8uY2FsbChyYmluZCwgbGlzdF9pc29jaHJvbmVzKQ0KDQptYXB2aWV3OjptYXB2aWV3KGFsbF9pc29jaHJvbmVzKQ0KDQojIFZpc3VhbGlzYXRpb24gKG9wdGlvbm5lbCkNCiMgU2kgdm91cyB2b3VsZXogdmlzdWFsaXNlciBsZXMgaXNvY2hyb25lcywgdm91cyBwb3V2ZXogdXRpbGlzZXIgZ2dwbG90MiBvdSB1bmUgYXV0cmUgbGlicmFpcmllIGdyYXBoaXF1ZQ0KIyBsaWJyYXJ5KGdncGxvdDIpDQojIGdncGxvdCgpICsgDQojICAgZ2VvbV9zZihkYXRhID0gcG9pbnRzX3NmLCBjb2xvciA9ICdyZWQnKSArDQojICAgZ2VvbV9zZihkYXRhID0gYWxsX2lzb2Nocm9uZXMsIGZpbGwgPSAnYmx1ZScsIGFscGhhID0gMC41KQ0KDQojIEVucmVnaXN0cmVyIGxlIHLDqXN1bHRhdCBkYW5zIHVuIGZpY2hpZXIgR2VvSlNPTiwgcGFyIGV4ZW1wbGUNCmdldHdkKCkNCiMgc3Rfd3JpdGUoYWxsX2lzb2Nocm9uZXMsICJDOi9Vc2Vycy9PdGhhdXJlYXUvRG9jdW1lbnRzL0JERC9JU09DSFJPTkVTX09TUk0vaXNvY2hyb25lcy5nZW9qc29uIikNCg0KDQpgYGANCg0KDQoqKkNhcnRvIGRlIGwnaXNvY2hyb25lIGF2ZWMgbGUgbW9pbnMgYm9uIHNjb3JlKioNCg0KYGBge3IsIG91dC53aWR0aD0nMTAwJSd9DQpwa19hdmVjX2RhdGEgPC0gc2Y6OnN0X3JlYWQoJ3Byb2Nlc3NlZF9kYXRhL2FsbF9kYXRhX3N0YW5kYXJkaXplZF9zdHBvbF9mb290X3AxNV8yMDI0MDIyMi5ncGtnJykNCmlzb2Nocm9uZXNfcDE1bWluIDwtIHNmOjpzdF9yZWFkKCdDOi9Vc2Vycy9vdGhldXJlYXV4L0RvY3VtZW50cy9PVC82VC9SL2RhdGFiYXNlX3NmL3Byb2Nlc3NlZF9kYXRhL2lzb2Nocm9uZXNfc3Rwb2xfZm9vdF8xNW1pbl8yMDI0MDIwOC5nZW9qc29uJykNCmlzb2Nocm9uZXNfcDE1bWluIDwtIGlzb2Nocm9uZXNfcDE1bWluICU+JSANCiAgc3RfdHJhbnNmb3JtKDIxNTQpDQpwa19taW4gPC0gcGtfYXZlY19kYXRhICU+JSANCiAgZmlsdGVyKHRvdGFsX3N1bSA9PSBtaW4ocGtfYXZlY19kYXRhJHRvdGFsX3N1bSkpDQoNCmludGVyc2VjdGlvbnMgPC0gc2Y6OnN0X2ludGVyc2VjdHMoc3RfYnVmZmVyKHBrX21pbiwgMTApLCBpc29jaHJvbmVzX3AxNW1pbikNCg0KIyBFeHRyYWlyZSBsZXMgaXNvY2hyb25lcyBjb3JyZXNwb25kYW50cw0KaXNvX21pbiA8LSBpc29jaHJvbmVzX3AxNW1pblt1bmxpc3QoaW50ZXJzZWN0aW9ucyksIF0NCg0KaXNvX21pbg0KYGBgDQoNCg0KYGBge3IsIG91dC53aWR0aD0nMTAwJSd9DQpwYWNtYW46OnBfbG9hZCh0bWFwKQ0KdG1hcF9tb2RlKCd2aWV3JykNCnRtX2Jhc2VtYXAoYyhsZWFmbGV0Ojpwcm92aWRlcnMkRXNyaS5Xb3JsZFRvcG9NYXAsDQogICAgICAgICAgICAgbGVhZmxldDo6cHJvdmlkZXJzJE9wZW5TdHJlZXRNYXAsIA0KICAgICAgICAgICAgIGxlYWZsZXQ6OnByb3ZpZGVycyRFc3JpLldvcmxkSW1hZ2VyeWlkZXJzLCANCiAgICAgICAgICAgICBsZWFmbGV0Ojpwcm92aWRlcnMkR2VvcG9ydGFpbEZyYW5jZS5vcnRob3MpKSArIA0KICB0bV9zaGFwZShzdF91bmlvbihpc29fbWluKSkgKyB0bV9maWxsKGNvbCA9ICcjNzVhOTkzJywNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBhbHBoYSA9IDAuNSkgKw0KICB0bV9zaGFwZShwa19taW4pICsgdG1fZG90cygpDQpgYGANCg0KDQoqKkNhcnRvIGRlIGwnaXNvY2hyb25lIGF2ZWMgbGUgbWVpbGxldXIgc2NvcmUqKg0KDQpgYGB7ciwgb3V0LndpZHRoPScxMDAlJ30NCnBrX21heCA8LSBwa19hdmVjX2RhdGEgJT4lIA0KICBmaWx0ZXIodG90YWxfc3VtID09IG1heChwa19hdmVjX2RhdGEkdG90YWxfc3VtKSkNCg0KaW50ZXJzZWN0aW9ucyA8LSBzZjo6c3RfaW50ZXJzZWN0cyhwa19tYXgsIGlzb2Nocm9uZXNfcDE1bWluKQ0KDQojIEV4dHJhaXJlIGxlcyBpc29jaHJvbmVzIGNvcnJlc3BvbmRhbnRzDQppc29fbWF4IDwtIGlzb2Nocm9uZXNfcDE1bWluW3VubGlzdChpbnRlcnNlY3Rpb25zKSwgXQ0KDQppc29fbWF4DQpgYGANCg0KDQpgYGB7ciwgb3V0LndpZHRoPScxMDAlJ30NCnBhY21hbjo6cF9sb2FkKHRtYXApDQp0bV9iYXNlbWFwKGMobGVhZmxldDo6cHJvdmlkZXJzJEVzcmkuV29ybGRUb3BvTWFwLA0KICAgICAgICAgICAgIGxlYWZsZXQ6OnByb3ZpZGVycyRPcGVuU3RyZWV0TWFwLCANCiAgICAgICAgICAgICBsZWFmbGV0Ojpwcm92aWRlcnMkRXNyaS5Xb3JsZEltYWdlcnlpZGVycywgDQogICAgICAgICAgICAgbGVhZmxldDo6cHJvdmlkZXJzJEdlb3BvcnRhaWxGcmFuY2Uub3J0aG9zKSkgKyANCiAgdG1fc2hhcGUoc3RfdW5pb24oaXNvX21heCkpICsgdG1fZmlsbChjb2wgPSAnIzc1YTk5MycsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgYWxwaGEgPSAwLjUpICsNCiAgdG1fc2hhcGUocGtfbWF4KSArIHRtX2RvdHMoKQ0KYGBgDQoNCiMjIyBPU1JNIFJPVVRFDQoNCmBgYHtyLCBvdXQud2lkdGg9JzEwMCUnfQ0KIyBEb25uw6llcyBPU1JNUk9VVEUgMSAtLS0tDQpwYWNtYW46OnBfbG9hZChzZiwgZHBseXIsIG1hcHZpZXcsIGdlb3NwaGVyZSwgaWdyYXBoLCBnZ3Bsb3QyLCBvc3JtLCBnb29nbGVQb2x5bGluZXMpDQoNCmVkZ2VzXzEwMCA8LSBlZGdlc19jbG9zZW5lc3NbMToxMDAsXSANCg0KZGVwYXJ0X2dhcmUgPC0gZWRnZXNfMTAwICU+JSANCiAgc2VsZWN0KHNvdXJjZV9zdG9wX25hbWUsIHNvdXJjZV9sYXQsIHNvdXJjZV9sb24pDQpzb3VyY2VfZ2FyZV9zZiA8LSBzZjo6c3RfYXNfc2YoZGVwYXJ0X2dhcmUsIGNvb3Jkcz1jKCdzb3VyY2VfbG9uJywNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgJ3NvdXJjZV9sYXQnKSkNCnN0X2Nycyhzb3VyY2VfZ2FyZV9zZikgPC0gJ2Vwc2c6NDMyNicNCm1hcHZpZXcoc291cmNlX2dhcmVfc2YpDQpkZXBhcnRfbG9uIDwtIHN0X2Nvb3JkaW5hdGVzKHNvdXJjZV9nYXJlX3NmKVsyLCAxXQ0KZGVwYXJ0X2xhdCA8LSBzdF9jb29yZGluYXRlcyhzb3VyY2VfZ2FyZV9zZilbMiwgMl0NCg0KZGVzdGluYXRpb25fZ2FyZSA8LSBlZGdlc18xMDAgJT4lIA0KICBzZWxlY3QodGFyZ2V0X3N0b3BfbmFtZSwgdGFyZ2V0X2xhdCwgdGFyZ2V0X2xvbikNCmRlc3RpbmF0aW9uX2dhcmVfc2YgPC0gc2Y6OnN0X2FzX3NmKGRlc3RpbmF0aW9uX2dhcmUsIA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgY29vcmRzPWMoIA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgJ3RhcmdldF9sb24nLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgJ3RhcmdldF9sYXQnKSkNCnN0X2NycyhkZXN0aW5hdGlvbl9nYXJlX3NmKSA8LSAnZXBzZzo0MzI2Jw0KZGVzdGluYXRpb25fbGF0IDwtIHN0X2Nvb3JkaW5hdGVzKGRlc3RpbmF0aW9uX2dhcmVfc2YpWzIsIDFdDQpkZXN0aW5hdGlvbl9sb24gPC0gc3RfY29vcmRpbmF0ZXMoZGVzdGluYXRpb25fZ2FyZV9zZilbMiwgMl0NCg0KbWFwdmlldyhkZXN0aW5hdGlvbl9nYXJlX3NmKQ0KDQpsb2MgPC0gY2JpbmQoc291cmNlX2dhcmVfc2YsDQogICAgICAgICAgICAgZGVzdGluYXRpb25fZ2FyZV9zZikNCnN0X2Nycyhsb2MpIDwtICdlcHNnOjQzMjYnDQoNCiMgSW5pdGlhbGlzZXIgdW5lIGxpc3RlIHBvdXIgc3RvY2tlciBsZXMgcsOpc3VsdGF0cyBkZXMgcm91dGVzDQpsaXN0X3JvdXRlcyA8LSBsaXN0KCkNCg0KIyBCb3VjbGUgcG91ciBjYWxjdWxlciBsZXMgaXNvY2hyb25lcyBwb3VyIGNoYXF1ZSBwb2ludA0KZm9yIChpIGluIDE6bnJvdyhzb3VyY2VfZ2FyZV9zZikpIHsNCiAgZGVwYXJ0X2xvbiA8LSBzdF9jb29yZGluYXRlcyhzb3VyY2VfZ2FyZV9zZilbaSwgMV0NCiAgZGVwYXJ0X2xhdCA8LSBzdF9jb29yZGluYXRlcyhzb3VyY2VfZ2FyZV9zZilbaSwgMl0NCiAgZGVzdGluYXRpb25fbG9uIDwtIHN0X2Nvb3JkaW5hdGVzKGRlc3RpbmF0aW9uX2dhcmVfc2YpW2ksIDFdDQogIGRlc3RpbmF0aW9uX2xhdCA8LSBzdF9jb29yZGluYXRlcyhkZXN0aW5hdGlvbl9nYXJlX3NmKVtpLCAyXQ0Kcm91dGUgPC0gb3NybVJvdXRlKA0KICBzcmMgPSBjKGRlcGFydF9sb24sIGRlcGFydF9sYXQpLA0KICBkc3QgPSBjKGRlc3RpbmF0aW9uX2xvbiwgZGVzdGluYXRpb25fbGF0KSwNCiAgb3ZlcnZpZXcgPSAic2ltcGxpZmllZCIsDQogIG9zcm0uc2VydmVyID0gZ2V0T3B0aW9uKCJvc3JtLnNlcnZlciIpLCANCiAgICAgICAgICAgICAgICAgICAgICAgICAgI2RlZmF1bHQgPSAiaHR0cHM6Ly9yb3V0ZXIucHJvamVjdC1vc3JtLm9yZy8iKSwNCiAgb3NybS5wcm9maWxlID0gImZvb3QiKQ0KIyBTdG9ja2VyIGxhIHJvdXRlIGRhbnMgbGEgbGlzdGUNCiAgbGlzdF9yb3V0ZXNbW2ldXSA8LSByb3V0ZQ0KfQ0KIyBGdXNpb25uZXIgdG91cyBsZXMgcm91dGVzIGRhbnMgdW4gc2V1bCBvYmpldCBzZiBzaSBuw6ljZXNzYWlyZQ0KYWxsX3JvdXRlcyA8LSBkby5jYWxsKHJiaW5kLCBsaXN0X3JvdXRlcykNCg0KbWFwdmlldzo6bWFwdmlldyhhbGxfcm91dGVzKSArIA0KICBtYXB2aWV3KHNvdXJjZV9nYXJlX3NmKQ0KDQojIERvbm7DqWVzIE9TUk1ST1VURSAyIC0tLS0NCmRhdGFfT0RfREVQNTEgPC0gc2Y6OnN0X3JlYWQoJ2RhdGEvT0RfREVQNTEuY3N2JykNCm5hbWVzKGRhdGFfT0RfREVQNTEpDQoNCmNvbW11bmVzIDwtIHNmOjpzdF9yZWFkKCdDOi9Vc2Vycy9vdGhldXJlYXV4L0RvY3VtZW50cy9PVC9ERUNPVVBBR0VTX0ZSL0NPTU1VTkUuc2hwJykNCm5hbWVzKGNvbW11bmVzKQ0KDQojIHNvdXJjZQ0Kc291cmNlX2dhcmUgPC0gZGF0YS5mcmFtZShkYXRhX09EX0RFUDUxJENPTU1VTkUpDQpuYW1lcyhzb3VyY2VfZ2FyZSkNCnNvdXJjZV9nYXJlIDwtIHNvdXJjZV9nYXJlICU+JSANCiAgZHBseXI6OnJlbmFtZShJTlNFRV9DT00gPSBkYXRhX09EX0RFUDUxLkNPTU1VTkUpDQpuYW1lcyhzb3VyY2VfZ2FyZSkNCnNvdXJjZV9nYXJlX3NmIDwtIHJpZ2h0X2pvaW4oY29tbXVuZXMsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgIHNvdXJjZV9nYXJlLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICBieSA9ICdJTlNFRV9DT00nKSAlPiUgDQogIHN0X3RyYW5zZm9ybSg0MzI2KSAlPiUgDQogIHN0X2NlbnRyb2lkKCkNCm1hcHZpZXcoc291cmNlX2dhcmVfc2YpDQoNCiMgZGVzdGluYXRpb24NCmRlc3RpbmF0aW9uX2dhcmUgPC0gZGF0YS5mcmFtZShkYXRhX09EX0RFUDUxJERDTFQpDQpuYW1lcyhkZXN0aW5hdGlvbl9nYXJlKQ0KZGVzdGluYXRpb25fZ2FyZSA8LSBkZXN0aW5hdGlvbl9nYXJlICU+JSANCiAgZHBseXI6OnJlbmFtZShJTlNFRV9DT00gPSBkYXRhX09EX0RFUDUxLkRDTFQpDQpuYW1lcyhkZXN0aW5hdGlvbl9nYXJlKQ0KZGVzdGluYXRpb25fZ2FyZV9zZiA8LSByaWdodF9qb2luKGNvbW11bmVzLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICBkZXN0aW5hdGlvbl9nYXJlLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICBieSA9ICdJTlNFRV9DT00nKSU+JSANCiAgc3RfdHJhbnNmb3JtKDQzMjYpJT4lIA0KICBzdF9jZW50cm9pZCgpDQptYXB2aWV3KGRlc3RpbmF0aW9uX2dhcmVfc2YpDQoNCiMgSW5pdGlhbGlzZXIgdW5lIGxpc3RlIHBvdXIgc3RvY2tlciBsZXMgcsOpc3VsdGF0cyBkZXMgcm91dGVzDQpsaXN0X3JvdXRlcyA8LSBsaXN0KCkNCg0KIyBCb3VjbGUgcG91ciBjYWxjdWxlciBsZXMgcm91dGVzIHBvdXIgY2hhcXVlIHBvaW50DQojIGZvciAoaSBpbiAxOm5yb3coc291cmNlX2dhcmVfc2YpKQ0KZm9yIChpIGluIDE6MTApIHsNCiAgZGVwYXJ0X2xvbiA8LSBzdF9jb29yZGluYXRlcyhzb3VyY2VfZ2FyZV9zZilbaSwgMV0NCiAgZGVwYXJ0X2xhdCA8LSBzdF9jb29yZGluYXRlcyhzb3VyY2VfZ2FyZV9zZilbaSwgMl0NCiAgZGVzdGluYXRpb25fbG9uIDwtIHN0X2Nvb3JkaW5hdGVzKGRlc3RpbmF0aW9uX2dhcmVfc2YpW2ksIDFdDQogIGRlc3RpbmF0aW9uX2xhdCA8LSBzdF9jb29yZGluYXRlcyhkZXN0aW5hdGlvbl9nYXJlX3NmKVtpLCAyXQ0KICByb3V0ZSA8LSBvc3JtUm91dGUoDQogICAgc3JjID0gYyhkZXBhcnRfbG9uLCBkZXBhcnRfbGF0KSwNCiAgICBkc3QgPSBjKGRlc3RpbmF0aW9uX2xvbiwgZGVzdGluYXRpb25fbGF0KSwNCiAgICBvdmVydmlldyA9ICJzaW1wbGlmaWVkIiwNCiAgICBvc3JtLnNlcnZlciA9IGdldE9wdGlvbigib3NybS5zZXJ2ZXIiKSwgDQogICAgI2RlZmF1bHQgPSAiaHR0cHM6Ly9yb3V0ZXIucHJvamVjdC1vc3JtLm9yZy8iKSwNCiAgICBvc3JtLnByb2ZpbGUgPSAiY2FyIikNCiAgIyBTdG9ja2VyIGxhIHJvdXRlIGRhbnMgbGEgbGlzdGUNCiAgbGlzdF9yb3V0ZXNbW2ldXSA8LSByb3V0ZQ0KfQ0KIyBGdXNpb25uZXIgdG91cyBsZXMgcm91dGVzIGRhbnMgdW4gc2V1bCBvYmpldCBzZiBzaSBuw6ljZXNzYWlyZQ0KYWxsX3JvdXRlcyA8LSBkby5jYWxsKHJiaW5kLCBsaXN0X3JvdXRlcykNCg0KYWxsX3JvdXRlX2Rpc3RpbmN0IDwtIGFsbF9yb3V0ZXMgJT4lIA0KICBkaXN0aW5jdChkaXN0YW5jZSwgLmtlZXBfYWxsID0gVCkNCm1hcHZpZXc6Om1hcHZpZXcoYWxsX3JvdXRlcykgKyBtYXB2aWV3KHNvdXJjZV9nYXJlX3NmKQ0KDQojIEVucmVnaXN0cmVyIGxlcyBkb25uw6llcw0KIyBzZjo6c3Rfd3JpdGUoYWxsX3JvdXRlcywgDQojICAgICAgICAgICAgICAnQzovVXNlcnMvb3RoZXVyZWF1eC9Eb3dubG9hZHMvYWxsX3JvdXRlX3Rlc3QxLmdwa2cnKQ0KDQoNCmBgYA0KDQoNCiMjIHI1cg0KDQoqKkV0YXBlIDEgOiBJbnN0YWxsZXIgbGEgYm9ubmUgdmVyc2lvbiBkZSBKQVZBICh2ZXJzaW9uIDExKSoqDQoNCiogQXByw6hzIGF2b2lyIGluc3RhbGzDqSBKYXZhLCBhc3N1cmV6LXZvdXMgcXVlIGxlIGNoZW1pbiBkJ2luc3RhbGxhdGlvbiBkZSBKYXZhIGVzdCBjb3JyZWN0ZW1lbnQgZMOpZmluaS4gUG91ciBjZSBmYWlyZSBhbGxlciBkYW5zIFN5c3TDqG1lPkluZm9ybWF0aW9ucyBzeXN0w6htZT5QYXJhbcOodHJlcyBhdmFuY8OpcyBkdSBzeXN0w6htZT5WYXJpYWJsZXMgZCdlbnZpcm9ubmVtZW50PlZhcmlhYmxlcyBzeXN0w6htZXMuICANCg0KYSkgZGFucyBsJ2Vudmlyb25uZW1lbnQgKipWYXJpYWJsZXMgc3lzdMOobWVzKiogOiB2YXJpYWJsZSBkJ2Vudmlyb25uZW1lbnQgSkFWQV9IT01FIDogQzovUHJvZ3JhbSBGaWxlcy9KYXZhL2pkay0xMS8gKEFzc3VyZXotdm91cyBkZSBuZSBwYXMgaW5jbHVyZSBiaW4gw6AgbGEgZmluIGR1IGNoZW1pbiBKQVZBX0hPTUUpIEVUIFBBVEggOiBDOi9Qcm9ncmFtIEZpbGVzL0phdmEvamRrLTExL2JpbiAoY2hlbWluIGR1IGRvc3NpZXIgYmluIGRlIEphdmEpLiAgIA0KDQoNCmIpIGRhbnMgbCdlbnZpcm9ubmVtZW50ICoqVmFyaWFibGVzIHV0aWxpc2F0ZXVycyoqIDogdmFyaWFibGUgZCdlbnZpcm9ubmVtZW50IFBBVEggOiBDOi9Qcm9ncmFtIEZpbGVzL0phdmEvamRrLTExL2JpbiAoY2hlbWluIGR1IGRvc3NpZXIgYmluIGRlIEphdmEpLiAgIA0KDQoNCioqRXRhcGUgMiA6IExhIGRlcm5pw6hyZSB2ZXJzaW9uIGRlIFIgZG9pdCDDqnRyZSBpbnN0YWxsw6kqKiANCg0KUG91ciBjb25uYcOudHJlIHZvdHJlIHZlcnNpb24gZGUgUiA6IFIuaG9tZSgpKQ0KVmVyc2lvbiBuw6ljZXNzYWlyZSA6IFIgdmVyc2lvbiA0LjMuMiAoMjAyMy0xMC0zMSB1Y3J0KQ0KDQoqIENvbmZpZ3VyZXIgUl9IT01FICAgDQoNCiogSW5zdGFsbGVyIGxhIGRlcm5pw6hyZSB2ZXJzaW9uIGRlIFIgICAgDQoNCkFwcsOocyBhdm9pciBpbnN0YWxsw6kgUiwgYXNzdXJlei12b3VzIHF1ZSBsZSBjaGVtaW4gZCdpbnN0YWxsYXRpb24gZGUgUiBlc3QgY29ycmVjdGVtZW50IGTDqWZpbmkgZGFucyBsYSAqKnZhcmlhYmxlIGQnZW52aXJvbm5lbWVudCoqIFJfSE9NRS8gIA0KKiBTdXIgV2luZG93cywgUl9IT01FIGRvaXQgw6p0cmUgZMOpZmluaSBwb3VyIHBvaW50ZXIgdmVycyBsZSByw6lwZXJ0b2lyZSBkJ2luc3RhbGxhdGlvbiBkZSBSLCBjb21tZSBDOi9Qcm9ncmFtIEZpbGVzL1IvUi00LjMuMg0KUG91ciBjZSBmYWlyZSBhbGxlciBkYW5zIFN5c3TDqG1lPkluZm9ybWF0aW9ucyBzeXN0w6htZT5QYXJhbcOodHJlcyBhdmFuY8OpcyBkdSBzeXN0w6htZT5WYXJpYWJsZXMgZCdlbnZpcm9ubmVtZW50PlZhcmlhYmxlcyBzeXN0w6htZXMuDQpBcHLDqHMgYXZvaXIgZMOpZmluaSBSX0pBVkEsIHJlZMOpbWFycmV6IHZvdHJlIG9yZGluYXRldXIuIA0KDQpBcHLDqHMgYXZvaXIgZMOpZmluaSBKQVZBX0hPTUUgUl9IT01FIGV0IGxlcyBib25zIFBhdGgsIHJlZMOpbWFycmV6IHZvdHJlIG9yZGluYXRldXIuICANCg0KKiBDb25maWd1cmV6IFIgU3R1ZGlvICANCg0KKiBQb3VyIHV0aWxpc2VyIGxhIGRlcm5pw6hyZSB2ZXJzaW9uIGRlIFIgYXZlYyBSU3R1ZGlvIHN1ciBXaW5kb3dzIDoNCkxhbmNleiBSU3R1ZGlvLiBBY2PDqWRlciBhdXggT3B0aW9ucyBHbG9iYWxlcy4NCkFsbGV6IGRhbnMgVG9vbHMgKE91dGlscykgZGFucyBsYSBiYXJyZSBkZSBtZW51Lg0KU8OpbGVjdGlvbm5leiBHbG9iYWwgT3B0aW9ucyAoT3B0aW9ucyBHbG9iYWxlcykuDQpDaGFuZ2VyIGxhIFZlcnNpb24gZGUgUiA6IERhbnMgbGEgZmVuw6p0cmUgT3B0aW9ucywgY2xpcXVleiBzdXIgR2VuZXJhbCAoR8OpbsOpcmFsKSBkYW5zIGxhIGxpc3RlIGRlIGdhdWNoZS4gU291cyBSIHZlcnNpb24sIGNsaXF1ZXogc3VyIENoYW5nZSAoTW9kaWZpZXIpIHBvdXIgY2hvaXNpciB1bmUgdmVyc2lvbiBkaWZmw6lyZW50ZSBkZSBSLiBVbmUgbGlzdGUgZGVzIHZlcnNpb25zIGRlIFIgaW5zdGFsbMOpZXMgc3VyIHZvdHJlIHN5c3TDqG1lIGRldnJhaXQgYXBwYXJhw650cmUuIFPDqWxlY3Rpb25uZXogbGEgdmVyc2lvbiBxdWUgdm91cyB2ZW5leiBkJ2luc3RhbGxlci4NCkFwcGxpcXVlciBldCBSZWTDqW1hcnJlciBSU3R1ZGlvIDogQ2xpcXVleiBzdXIgT0sgcG91ciBlbnJlZ2lzdHJlciB2b3MgbW9kaWZpY2F0aW9ucy4NClZvdXMgZGV2cmV6IHBldXQtw6p0cmUgcmVkw6ltYXJyZXIgUlN0dWRpbyBwb3VyIHF1ZSBsZXMgY2hhbmdlbWVudHMgcHJlbm5lbnQgZWZmZXQuDQpWw6lyaWZpZXIgbGEgVmVyc2lvbiBkZSBSIGRhbnMgUlN0dWRpbyA6IEFwcsOocyBhdm9pciByZWTDqW1hcnLDqSBSU3R1ZGlvLCB2b3VzIHBvdXZleiB2w6lyaWZpZXIgcXVlIGxhIGJvbm5lIHZlcnNpb24gZGUgUiBlc3QgdXRpbGlzw6llIGVuIGV4w6ljdXRhbnQgUi52ZXJzaW9uLnN0cmluZyBvdSBzZXNzaW9uSW5mbygpIGRhbnMgbGEgY29uc29sZS4NCg0KDQoqKkV0YXBlIDMgOiBPdXZyZXogUlN0dWRpbyBldCBpbnN0YWxsZXogcjVyIGV0IHJKYXZhICoqDQoNClNjcmlwdCBkJ2V4ZW1wbGUgIDoNClNvdXJjZSA6IGh0dHBzOi8vaXBlYWdpdC5naXRodWIuaW8vaW50cm9fYWNjZXNzX2Jvb2svM19jYWxjdWxhbmRvX2FjZXNzby5lbi5odG1sDQoNCmBgYHtyLCBldmFsPUZ9DQoNCm9wdGlvbnMoamF2YS5wYXJhbWV0ZXJzID0gIi1YbXgyRyIpDQoNCnBhY21hbjo6cF9sb2FkKHI1ciwgckphdmEpDQpkYXRhX3BhdGggPC0gc3lzdGVtLmZpbGUoImV4dGRhdGEvcG9hIiwgcGFja2FnZSA9ICJyNXIiKQ0KZGF0YV9wYXRoDQpmczo6ZGlyX3RyZWUoZGF0YV9wYXRoKQ0KDQoNCnI1cl9jb3JlIDwtIHNldHVwX3I1KGRhdGFfcGF0aCwgdmVyYm9zZSA9IEZBTFNFKQ0KZnM6OmRpcl90cmVlKGRhdGFfcGF0aCkNCg0KIyByZWFkIGRhdGEuZnJhbWUgd2l0aCBncmlkIGNlbnRyb2lkcw0KcG9pbnRzIDwtIGRhdGEudGFibGU6OmZyZWFkKGZpbGUucGF0aChkYXRhX3BhdGgsICJwb2FfaGV4Z3JpZC5jc3YiKSkNCm1hcHZpZXc6Om1hcHZpZXcocG9pbnRzKQ0KDQp0dG0gPC0gdHJhdmVsX3RpbWVfbWF0cml4KA0KICByNXJfY29yZSwNCiAgb3JpZ2lucyA9IHBvaW50cywNCiAgZGVzdGluYXRpb25zID0gcG9pbnRzLA0KICBtb2RlID0gYygiV0FMSyIsICJUUkFOU0lUIiksDQogIGRlcGFydHVyZV9kYXRldGltZSA9IGFzLlBPU0lYY3QoDQogICAgIjEzLTA1LTIwMTkgMTQ6MDA6MDAiLA0KICAgIGZvcm1hdCA9ICIlZC0lbS0lWSAlSDolTTolUyINCiAgKSwNCiAgbWF4X3dhbGtfdGltZSA9IDMwLA0KICBtYXhfdHJpcF9kdXJhdGlvbiA9IDEyMCwNCiAgdmVyYm9zZSA9IEZBTFNFLA0KICBwcm9ncmVzcyA9IEZBTFNFDQopDQoNCmhlYWQodHRtKQ0KYGBgDQoNCioqKg0KDQoqKkNhbGN1bCBkJ2lzb2Nocm9uZXMgYXZlYyByNXIgc3VyIGxhIGxpZ25lIFNhaW50LVBvbC1zdXItVGVybm9pc2UqKg0KDQpgYGB7ciwgZXZhbD1GfQ0KIyBBdmFudCB0b3V0DQpvcHRpb25zKGphdmEucGFyYW1ldGVycyA9ICItWG14MkciKQ0KDQpwYWNtYW46OnBfbG9hZChyNXIsIHNmLCBkYXRhLnRhYmxlLCBnZ3Bsb3QyLCB0bWFwKQ0KDQojIFTDqWzDqWNoYXJnZXIgbGVzIGRvbm7DqWVzIGF1IGZvcm1hdCBwYmYgKFByb3RvY29sYnVmZmVyIEJpbmFyeSBGb3JtYXQpDQpVbiBmaWNoaWVyIFBCRiBlc3QgdW4gZm9ybWF0IGRlIGZpY2hpZXIgdXRpbGlzw6kgcG91ciBzdG9ja2VyIGRlcyBkb25uw6llcyBnw6lvZ3JhcGhpcXVlcywgbm90YW1tZW50IGNlbGxlcyBpc3N1ZXMgZCdPcGVuU3RyZWV0TWFwJy4gDQpJY2ksIHZvdXMgcG91dmV6IHRyb3V2ZXIgZGVzIGZpY2hpZXJzIHBiZiA6IGh0dHBzOi8vZG93bmxvYWQuZ2VvZmFicmlrLmRlLw0KDQojIENvbmZpZ3VyZXIgbGUgZG9zc2llciBvw7kgc2UgdHJvdXZlIGxlcyDDqWzDqW1lbnRzDQpyNXJfY29yZSA8LSBzZXR1cF9yNShkYXRhX3BhdGggPSAiQzovVXNlcnMvb3RoZXVyZWF1eC9Eb2N1bWVudHMvT1QvUkFJTEVOSVVNL0JERF9URUxMSS9PU01fUEJGIiwgdmVyYm9zZSA9IEZBTFNFKQ0KIyBkYXRhX3BhdGggZXN0IGxlIGNoZW1pbiB2ZXJzIGxlIGRvc3NpZXIgY29udGVuYW50IHZvcyBkb25uw6llcyBkZSByw6lzZWF1Lg0KDQojIENoYXJnZW1lbnQgZGUgbCdlbXByaXNlIGRlIGxhIHpvbmUgZCfDqXR1ZGUgLS0tLQ0Kem9uZV9ldHVkZSA8LSBzZjo6c3RfcmVhZCgnQzovVXNlcnMvb3RoZXVyZWF1eC9Eb2N1bWVudHMvT1QvUkFJTEVOSVVNL0JERF9URUxMSS9JU09DSFJPTkVTL2lzb2Nocm9uZXNfc3Rwb2xfY2FyXzIwbWluX2dyb3VwZS5ncGtnJykNCnpvbmVfZXR1ZGUgPC0gc2Y6OnN0X3RyYW5zZm9ybSh6b25lX2V0dWRlLCAyMTU0KQ0KDQojIENoYXJnZW1lbnQgZGUgbGEgY291Y2hlIGRlcyBwb2ludHMga2lsb23DqXRyaXF1ZXMgLS0tLQ0KcG9pbnRzX2ttcyA8LSBzZjo6c3RfcmVhZCgnQzovVXNlcnMvb3RoZXVyZWF1eC9Eb2N1bWVudHMvT1QvUkFJTEVOSVVNL0JERF9URUxMSS9QVF9LTVMvcGtfZXRvaWxlX2RlX3NhaW50cG9sLmdwa2cnKQ0KIyBwb2ludHNfa21zIDwtIHN0X3RyYW5zZm9ybShwb2ludHNfa21zLCAyMTU0KQ0KcG9pbnRzX2ttcyA8LSBwb2ludHNfa21zICU+JSBtdXRhdGUoSURfbnVtYmVyID0gcm93X251bWJlcigpKQ0KDQojIENvbnZlcnRpciBsZXMgdmFsZXVycyBkZSAncGsnIGVuIG51bcOpcmlxdWUNCnBrX251bSA8LSBhcy5udW1lcmljKHBvaW50c19rbXMkcGspDQoNCiMgQ3LDqWVyIHVuIHZlY3RldXIgbG9naXF1ZSBwb3VyIGZpbHRyZXIgbGVzIHBrIGVudGllcnMgZXQgY2V1eCBzZSB0ZXJtaW5hbnQgcGFyICw1DQpmaWx0ZXJfY29uZGl0aW9uIDwtIHBrX251bSAlJSAxID09IDANCg0KIyBGaWx0cmVyIGxhIGNvbGxlY3Rpb24gZGUgZm9uY3Rpb25zIHNpbXBsZXMgZW4gdXRpbGlzYW50IGxhIGNvbmRpdGlvbg0KZmlsdGVyZWRfcG9pbnRzX2ttcyA8LSBwb2ludHNfa21zW2ZpbHRlcl9jb25kaXRpb24sIF0NCg0KIyBEw6ljb3VwYWdlIGRlIGxhIGNvdWNoZSBkZXMgcG9pbnRzIGtpbG9tw6l0cmlxdWVzDQpwb2ludHNfa21zX2RlY291cCA8LSBzZjo6c3RfaW50ZXJzZWN0aW9uKGZpbHRlcmVkX3BvaW50c19rbXMsem9uZV9ldHVkZSkgDQoNCiMgQWpvdXRlciB1bmUgY29sb25uZSAnaWQnIMOgIHZvcyBwb2ludHMgZCdvcmlnaW5lDQpwb2ludHNfa21zX2RlY291cCRpZCA8LSAxOm5yb3cocG9pbnRzX2ttc19kZWNvdXApDQoNCiMgVsOpcmlmaWV6IGxlcyBwcmVtacOocmVzIGxpZ25lcyBwb3VyIGNvbmZpcm1lciBsJ2Fqb3V0IGRlICdpZCcNCmhlYWQocG9pbnRzX2ttc19kZWNvdXApDQoNCiMgVHJhbnNmb3JtYXRpb24gZW4gV0dTODQNCnBvaW50c19rbXNfZGVjb3VwIDwtIHN0X3RyYW5zZm9ybShwb2ludHNfa21zX2RlY291cCwgNDMyNikNCg0KIyBFeHRyYWlyZSBsZXMgY29vcmRvbm7DqWVzIGV0IGNyw6llciBkZXMgY29sb25uZXMgJ2xhdCcgZXQgJ2xvbicNCnBvaW50c19rbXNfZGVjb3VwJGxhdCA8LSBzdF9jb29yZGluYXRlcyhwb2ludHNfa21zX2RlY291cClbLCAiWSJdDQpwb2ludHNfa21zX2RlY291cCRsb24gPC0gc3RfY29vcmRpbmF0ZXMocG9pbnRzX2ttc19kZWNvdXApWywgIlgiXQ0KDQojIFbDqXJpZmllciBsZSByw6lzdWx0YXQNCmhlYWQocG9pbnRzX2ttc19kZWNvdXApDQoNCiMgc2Y6OnN0X3dyaXRlKHBvaW50c19rbXNfZGVjb3VwX2xpZ2h0X3dnczg0LCAncHJvY2Vzc2VkX2RhdGEvcG9pbnRzX2ttc19kZWNvdXBfbGlnaHRfd2dzODRfdjEuZ3BrZycpDQoNCiMgcm91dGluZyBpbnB1dHMNCm1vZGUgPC0gYygiV0FMSyIpDQptYXhfdHJpcF9kdXJhdGlvbiA8LSAxNSAgICAgICMgaW4gbWludXRlcw0KDQojIGNhbGN1bGF0ZSB0cmF2ZWwgdGltZSBtYXRyaXgNCmlzbzEgPC0gcjVyOjppc29jaHJvbmUocjVyX2NvcmUsDQogICAgICAgICAgICAgICAgICAgICAgIG9yaWdpbnMgPSBwb2ludHNfa21zX2RlY291cCwNCiAgICAgICAgICAgICAgICAgICAgICAgbW9kZSA9IG1vZGUsDQogICAgICAgICAgICAgICAgICAgICAgIHNhbXBsZV9zaXplID0gMSwNCiAgICAgICAgICAgICAgICAgICAgICAgbWF4X3RyaXBfZHVyYXRpb24gPSBtYXhfdHJpcF9kdXJhdGlvbiwNCiAgICAgICAgICAgICAgICAgICAgICAgd2Fsa19zcGVlZCA9IDUsDQogICAgICAgICAgICAgICAgICAgICAgIHByb2dyZXNzID0gRkFMU0UpDQoNCmlzbzFfZmlsdGVyIDwtIGlzbzEgJT4lIA0KICBmaWx0ZXIoaXNvY2hyb25lID09IDE1KQ0Kc2Y6OnN0X3dyaXRlKGlzbzFfZmlsdGVyLCAncHJvY2Vzc2VkX2RhdGEvcjVyX2lzb18xNW1pbl81a21fMjAyNDAxMTcuZ3BrZycpDQoNCg0KIyBleHRyYWN0IE9TTSBuZXR3b3JrDQpzdHJlZXRfbmV0IDwtIHN0cmVldF9uZXR3b3JrX3RvX3NmKHI1cl9jb3JlKQ0KbWFpbl9yb2FkcyA8LSBzdWJzZXQoc3RyZWV0X25ldCRlZGdlcywgc3RyZWV0X2NsYXNzICVsaWtlJSAnUFJJTUFSWXxTRUNPTkRBUlknKQ0KICANCmNvbG9ycyA8LSBjKCcjZmZlMGE1JywnI2ZmY2I2OScsJyNmZmE2MDAnLCcjZmY3YzQzJywnI2Y5NWQ2YScsDQogICAgICAgICAgICAnI2Q0NTA4NycsJyNhMDUxOTUnLCcjNjY1MTkxJywnIzJmNGI3YycsJyMwMDNmNWMnKQ0KDQpnZ3Bsb3QoKSArDQogIGdlb21fc2YoZGF0YSA9IGlzbzEsIGFlcyhmaWxsPWZhY3Rvcihpc29jaHJvbmUpKSwgY29sb3IgPSBOQSwgYWxwaGEgPSAuNykgKw0KICBnZW9tX3NmKGRhdGEgPSBtYWluX3JvYWRzLCBjb2xvciA9ICJncmF5NTUiLCBzaXplPTAuMDEsIGFscGhhID0gMC4yKSArDQogICAjIHNjYWxlX2ZpbGxfdmlyaWRpc19kKGRpcmVjdGlvbiA9IC0xLCBvcHRpb24gPSAnQicpICsNCiAgc2NhbGVfZmlsbF9tYW51YWwodmFsdWVzID0gcmV2KGNvbG9ycykgKSArDQogIHNjYWxlX2NvbG9yX21hbnVhbCh2YWx1ZXM9YygnQ2VudHJhbCBidXNcbnN0YXRpb24nPSdibGFjaycpKSArDQogIGxhYnMoZmlsbCA9ICJUcmF2ZWwgdGltZVxuKGluIG1pbnV0ZXMpIiwgY29sb3I9JycpICsNCiAgdGhlbWVfbWluaW1hbCgpICsNCiAgdGhlbWUoYXhpcy50aXRsZSA9IGVsZW1lbnRfYmxhbmsoKSkNCg0KDQoNCiMgQ3LDqWF0aW9uIGRlIGxhIGNhcnRlIGF2ZWMgdG1hcA0KdG1hcF9tb2RlKCd2aWV3JykNCnRtX3NoYXBlKGlzbzFfc2VsZWN0KSArDQogIHRtX3BvbHlnb25zKCJpc29jaHJvbmUiLCBpZCA9ICJpZCIsIHBhbGV0dGUgPSByZXYoY29sb3JzKSwgYm9yZGVyLmNvbCA9IE5BLCBhbHBoYSA9IC43KSArDQogIHRtX3NoYXBlKG1haW5fcm9hZHMpICsNCiAgdG1fbGluZXMoY29sID0gImdyYXk1NSIsIGx3ZCA9IDAuMSwgYWxwaGEgPSAwLjIpICsNCiAgdG1fbGF5b3V0KA0KICAgIGxlZ2VuZC50aXRsZS5zaXplID0gMC44LA0KICAgIGxlZ2VuZC50ZXh0LnNpemUgPSAwLjgsDQogICAgYmcuY29sb3IgPSAid2hpdGUiLA0KICAgIGZyYW1lID0gRkFMU0UsDQogICAgbGVnZW5kLnBvc2l0aW9uID0gYygibGVmdCIsICJib3R0b20iKQ0KICApDQoNCg0KYGBgDQoNCiMjIFFHSVNQUk9DRVNTDQoNCmBgYHtyLCBldmFsID0gRiwgZWNobyA9IFR9DQojIENoYXJnZW1lbnQgZGUgbGEgbGlicmFyeQ0KcGFjbWFuOjpwX2xvYWQocWdpc3Byb2Nlc3MpDQoNCiMgUGVybWV0IGRlIGNyw6llciB1biB2ZWN0ZXVyIGF2ZWMgdG91cyBsZXMgYWxnb3JpdGhtZXMNCnFnaXNfYWxnbyA8LSBxZ2lzX2FsZ29yaXRobXMoKQ0KDQojIFJlY2hlcmNoZXIgdW4gdHJhaXRlbWVudCBzcMOpY2lmaXF1ZQ0KZ3JlcCgiaW50ZXJzZWN0IiwgcWdpc19hbGdvJGFsZ29yaXRobSwgdmFsdWUgPSBUKQ0KZ3JlcCgiZ3Jhc3MiLCBxZ2lzX2FsZ28kYWxnb3JpdGhtLCB2YWx1ZSA9IFQpDQpxZ2lzX2FsZ28kcHJvdmlkZXJfdGl0bGUNCg0KcWdpc19jb25maWd1cmUoKQ0KDQojIE1vbnRyZXIgbCdhaWRlDQpxZ2lzX3Nob3dfaGVscCgibmF0aXZlOnNsb3BlIikNCg0KIyBFeGVtcGxlIGF2ZWMgbGUgY2FsY3VsIGRlIHBlbnRlIGRlIHBsdXNpZXVycyBpc29jaHJvbmVzDQpmb3IoaSBpbiAxOm5yb3coYWxsX2lzb2Nocm9uZXMpKSB7DQogIG1udF9pbnRlcnNlY3Rpb24gPC0gY3JvcChyYXMsIGFsbF9pc29jaHJvbmVzW2ksXSkNCiAgYSA8LSBtYXNrKG1udF9pbnRlcnNlY3Rpb24sIGFsbF9pc29jaHJvbmVzW2ksXSkNCiAgYiA8LSBxZ2lzX3J1bl9hbGdvcml0aG0oIm5hdGl2ZTpzbG9wZSIsIA0KICAgIElOUFVUID0gYSwgDQogICAgT1VUUFVUID0gcGFzdGUoJ0M6L1VzZXJzL290aGV1cmVhdXgvRG9jdW1lbnRzL09UL1JBSUxFTklVTS9ET05ORUVTX0NBUlRPL3Jhc3Rlci9tbnRfNjIvJywgaSwgIi50aWYiKSkNCiAgYl9yYXMgPC0gcmFzdGVyKHBhc3RlKCdDOi9Vc2Vycy9vdGhldXJlYXV4L0RvY3VtZW50cy9PVC9SQUlMRU5JVU0vRE9OTkVFU19DQVJUTy9yYXN0ZXIvbW50XzYyLycsIGksICIudGlmIikpDQogIG1lYW4gPC0gcmFzdGVyOjpjZWxsU3RhdHMoYl9yYXMsIHN0YXQgPSAibWVhbiIsIG5hLnJtID0gVFJVRSkNCiAgc3VtIDwtIGRhdGEuZnJhbWUocGVudGVfbW95ZW5uZSA9IG1lYW4sIA0KICAgICAgICAgICAgICAgICAgICBJRF9udW1iZXIgPSBhbGxfaXNvY2hyb25lcyRwb2ludC5JRF9udW1iZXJbaV0pDQogIGFsbF9zdW1bW2ldXSA8LSBzdW0NCiAgICB9ICANCmBgYA0KDQoNCiMjIEJQRQ0KDQpgYGB7ciwgZXZhbD1GLCBlY2hvID0gVH0NCnBhY21hbjo6cF9sb2FkKHNmKQ0KDQpCUEUgPC0gcmVhZC5jc3YyKCJDOi9Vc2Vycy9vdGhldXJlYXV4L0RvY3VtZW50cy9PVC9SQUlMRU5JVU0vQkREX1RFTExJL0JQRS9icGUyMV9lbnNlbWJsZV94eV9jc3YvYnBlMjFfZW5zZW1ibGVfeHkuY3N2IikNCkJQRV9WQVJNT0QgPC0gcmVhZC5jc3YyKCJDOi9Vc2Vycy9vdGhldXJlYXV4L0RvY3VtZW50cy9PVC9SQUlMRU5JVU0vQkREX1RFTExJL0JQRS9icGUyMV9lbnNlbWJsZV94eV9jc3YvVmFybW9kX2JwZTIxX2Vuc2VtYmxlX3h5LmNzdiIpDQpCUEVfVkFSTU9EX2NvbW1lcmNlcyA8LSBCUEVfVkFSTU9EICU+JSANCiAgZmlsdGVyKHN0cmluZ3I6OnN0cl9zdGFydHMoQ09EX01PRCwgJ0InKSkNCm5hbWVzKEJQRV9WQVJNT0RfY29tbWVyY2VzKQ0KKEJQRV9WQVJNT0RfY29tbWVyY2VzJExJQl9NT0QpDQoNCkJQRV9jbGVhbiA8LSBCUEUgJT4lDQogIGRwbHlyOjpmaWx0ZXIoTEFNQkVSVF9YICE9ICIiLCBMQU1CRVJUX1kgIT0gIiIsIA0KICAgICAgICAgICAgICAgICFpcy5uYShMQU1CRVJUX1gpLCAhaXMubmEoTEFNQkVSVF9ZKSkNCkJQRV9jbGVhbl8yNSA8LSBCUEVfY2xlYW4gJT4lIA0KICBmaWx0ZXIoREVQID09IDI1KQ0KDQpCUEVfY2xlYW5fMjVfcmVzdG8gPC0gQlBFX2NsZWFuXzI1ICU+JSANCiAgZmlsdGVyKHN0cmluZ3I6OnN0cl9zdGFydHMoVFlQRVFVLCAnQTUwNCcpKSAjIFJFU1RBVVJBTlQtIFJFU1RBVVJBVElPTiBSQVBJREUNCg0KQlBFX2NsZWFuXzI1X3Jlc3RvX3NmIDwtIEJQRV9jbGVhbl8yNV9yZXN0byAlPiUNCiAgc2Y6OnN0X2FzX3NmKGNvb3JkcyA9IGMoIkxBTUJFUlRfWCIsICJMQU1CRVJUX1kiKSwgY3JzID0gMjE1NCkNCkJQRV9jbGVhbl8yNV9yZXN0b19zZl9pbnRlcnNlY3Rpb24gPC0gc3RfaW50ZXJzZWN0aW9uKEJQRV9jbGVhbl8yNV9yZXN0b19zZiwgem9uZV9ldHVkZSkNCg0KbWFwdmlldyhCUEVfY2xlYW5fMjVfcmVzdG9fc2ZfaW50ZXJzZWN0aW9uKQ0KDQpCUEVfY2xlYW5fMjVfY29tbWVyY2VzIDwtIEJQRV9jbGVhbl8yNSAlPiUgDQogIGZpbHRlcihzdHJpbmdyOjpzdHJfc3RhcnRzKFRZUEVRVSwgJ0InKSkgDQojICAiQ29tbWVyY2VzIiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIkdyYW5kZXMgc3VyZmFjZXMiICAgICAgICAgICAgICAgICAgICAgICAgICAgDQojICAiQ29tbWVyY2VzIGFsaW1lbnRhaXJlcyIgICAgICAgICAgICAgICAgICAgICAgICAgIkNvbW1lcmNlcyBzcMOpY2lhbGlzw6lzIG5vbi1hbGltZW50YWlyZXMiICAgICANCiMgICJIWVBFUk1BUkNIw4kiICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIlNVUEVSTUFSQ0jDiSIgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIA0KIyAgIkdSQU5ERSBTVVJGQUNFIERFIEJSSUNPTEFHRSIgICAgICAgICAgICAgICAgICAgICJTVVDDiVJFVFRFIiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICANCiMgICLDiVBJQ0VSSUUiICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIkJPVUxBTkdFUklFIiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgDQojICAiQk9VQ0hFUklFIENIQVJDVVRFUklFIiAgICAgICAgICAgICAgICAgICAgICAgICAgIlBST0RVSVRTIFNVUkdFTMOJUyIgICAgICAgICAgICAgICAgICAgICAgICAgICANCiMgICJQT0lTU09OTkVSSUUiICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiTElCUkFJUklFLCBQQVBFVEVSSUUsIEpPVVJOQVVYIiAgICAgICAgICAgICANCiMgICJNQUdBU0lOIERFIFbDilRFTUVOVFMiICAgICAgICAgICAgICAgICAgICAgICAgICAgIk1BR0FTSU4gROKAmcOJUVVJUEVNRU5UUyBEVSBGT1lFUiIgICAgICAgICAgICAgDQojICAiTUFHQVNJTiBERSBDSEFVU1NVUkVTIiAgICAgICAgICAgICAgICAgICAgICAgICAgIk1BR0FTSU4gROKAmcOJTEVDVFJPTcOJTkFHRVIgRVQgREUgTUFULiBBVURJTy1WSURFTyINCiMgICJNQUdBU0lOIERFIE1FVUJMRVMiICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiTUFHQVNJTiBE4oCZQVJUSUNMRVMgREUgU1BPUlRTIEVUIERFIExPSVNJUlMiIA0KIyAgIk1BR0FTSU4gREUgUkVWw4pURU1FTlRTIE1VUlMgRVQgU09MUyIgICAgICAgICAgICAiRFJPR1VFUklFIFFVSU5DQUlMTEVSSUUgQlJJQ09MQUdFIiAgICAgICAgICAgDQojICAiUEFSRlVNRVJJRS1DT1NNw4lUSVFVRSIgICAgICAgICAgICAgICAgICAgICAgICAgICJIT1JMT0dFUklFLUJJSk9VVEVSSUUiICAgICAgICAgICAgICAgICAgICAgIA0KIyAgIkZMRVVSSVNURS1KQVJESU5FUklFLUFOSU1BTEVSSUUiICAgICAgICAgICAgICAgICJNQUdBU0lOIETigJlPUFRJUVVFIiAgICAgICAgICAgICAgICAgICAgICAgICAgDQojICAiTUFHQVNJTiBERSBNQVTDiVJJRUwgTcOJRElDQUwgRVQgT1JUSE9Qw4lESVFVRSAgICAgIlNUQVRJT04tU0VSVklDRSIgDQoNCm5hbWVzKEJQRV9jbGVhbl8yNV9jb21tZXJjZXMpDQp0YWJsZShCUEVfY2xlYW5fMjVfY29tbWVyY2VzJFRZUEVRVSkNCg0KbmFtZXMoQlBFX2NsZWFuXzI1X2NvbW1lcmNlcykNCnRhYmxlKEJQRV9jbGVhbl8yNV9jb21tZXJjZXMkVFlQRVFVKQ0KQlBFX2NsZWFuXzI1X2NvbW1lcmNlc19zZiA8LSBCUEVfY2xlYW5fMjVfY29tbWVyY2VzICU+JQ0KICBzZjo6c3RfYXNfc2YoY29vcmRzID0gYygiTEFNQkVSVF9YIiwgIkxBTUJFUlRfWSIpLCBjcnMgPSAyMTU0KQ0KDQptYXB2aWV3KEJQRV9jbGVhbl8yNV9jb21tZXJjZXNfc2YpDQpgYGANCg0KIyMgSUdOIFdGUw0KDQpgYGB7ciwgd2FybmluZz1GLCBtZXNzYWdlPUYsIGV2YWwgPSBGfQ0KIyMgUGFja2FnZXMNCnBhY21hbjo6cF9sb2FkKHNmLCBkcGx5ciwgaHR0ciwgcHVycnIpDQoNCiMjIENob2l4IGR1IGZsdXg6ICBSRUdJT04sIENPTU1VTkUsIEVQQ0ksIEJBVElNRU5ULCBST1VURVMsIFJJVklFUkUsIFZPSUUgRkVSUkUNCnVybF9iZCA8LSAiaHR0cHM6Ly93eHMuaWduLmZyL2Vzc2VudGllbHMvZ2VvcG9ydGFpbC93ZnM/VkVSU0lPTj0yLjAuMCINCg0KIyBERVBBUlRFTUVOVA0KIyB1cmxfYmQgPC0gImh0dHBzOi8vd3hzLmlnbi5mci90b3BvZ3JhcGhpZS9nZW9wb3J0YWlsL3dmcz9TRVJWSUNFPVdGUyZWRVJTSU9OPTIuMC4wJlJFUVVFU1Q9R2V0Q2FwYWJpbGl0aWVzIg0KDQojIElSSVMNCiMgdXJsX2JkIDwtICJodHRwczovL3d4cy5pZ24uZnIvY2FydG92ZWN0by9nZW9wb3J0YWlsL3dmcz9TRVJWSUNFPVdGUyZWRVJTSU9OPTIuMC4wJlJFUVVFU1Q9R2V0Q2FwYWJpbGl0aWVzIg0KDQojIyBJbnRlcnJvZ2VyIGxlIGNvbnRlbnUgZHUgbGllbiwgYWZmaWNoZXIgdG91dCBsZSBjb250ZW51DQppZ25fY2xpZW50IDwtIG93czRSOjpXRlNDbGllbnQkbmV3KHVybF9iZCwgc2VydmljZVZlcnNpb24gPSAiMi4wLjAiKSANCmlnbl9jbGllbnQkZ2V0RmVhdHVyZVR5cGVzKHByZXR0eSA9IFRSVUUpDQojIG9wdGlvbnMobWF4LnByaW50ID0gMTAwMCkNCmBgYA0KDQoNCmBgYHtyLCB3YXJuaW5nPUYsIG1lc3NhZ2U9RiwgZXZhbCA9IEZ9DQojIyBDaGVyY2hlciB1bmUgdmFyaWFibGUgcGFyIHNvbiBub20gDQpiZF92cmJnIDwtIGlnbl9jbGllbnQkZ2V0Q2FwYWJpbGl0aWVzKCkNCg0KYSA8LSBiZF92cmJnJGdldEZlYXR1cmVUeXBlcyhwcmV0dHkgPSBUUlVFKQ0KYiA8LSBhJT4lZmlsdGVyKGdyZXBsKHBhdHRlcm4gPSAiYmF0aW1lbnQiLCB4ID0gbmFtZSkpDQpzdHIoYikNCmBgYA0KDQoNCmBgYHtyLCB3YXJuaW5nPUYsIG1lc3NhZ2U9RiwgZXZhbCA9IEZ9DQojIyBSZWNoZXJjaGVyIGxlcyBtw6l0YWRvbm7DqWVzIGQndW5lIHZhcmlhYmxlDQppZ25fY2xpZW50JA0KICBnZXRDYXBhYmlsaXRpZXMoKSQNCiAgZmluZEZlYXR1cmVUeXBlQnlOYW1lKCJCRFRPUE9fVjM6dHJvbmNvbl9kZV9yb3V0ZSIpJA0KICBnZXREZXNjcmlwdGlvbigpICU+JQ0KICBwdXJycjo6bWFwX2NocihmdW5jdGlvbih4KXt4JGdldE5hbWUoKX0pDQpgYGANCg0KDQpgYGB7ciwgd2FybmluZz1GLCBtZXNzYWdlPUYsIGV2YWwgPSBGfQ0KIyMgaXNvbGVyIGRlcyBkb25uw6llcyBkZSBsYSBCRFRPUE8gYXZlYyB1bmUgYm91bmRpbmcgYm94DQpwYXJzZV91cmwgPC0gaHR0cjo6cGFyc2VfdXJsKHVybF9iZCkNCnBhcnNlX3VybCRxdWVyeSA8LSBsaXN0KHNlcnZpY2UgPSAiV0ZTIiwNCiAgICAgICAgICAgICAgICAgICAgICAgICN2ZXJzaW9uID0gIjIuMC4wIiwgIyBvcHRpb25hbA0KICAgICAgICAgICAgICAgICAgICAgICAgcmVxdWVzdCA9ICJHZXRGZWF0dXJlIiwNCiAgICAgICAgICAgICAgICAgICAgICAgIHR5cGVuYW1lID0gIkJEQ0FSVE9fQkREX1dMRF9XR1M4NEc6Y29tbXVuZSIsICMgdHlwZSBkaXNwb25pYmxlIGljaSA6IGlnbl9jbGllbnQkZ2V0RmVhdHVyZVR5cGVzKHByZXR0eSA9IFRSVUUpDQogICAgICAgICAgICAgICAgICAgICAgICBjcWxfZmlsdGVyID0gYygiaW5zZWVfY29tPSczNDE3MiciKSkgIyBKJ3V0aWxpc2UgaW5zZWVfY29tIHJlcMOpcsOpIMOgIGwnw6l0YXBlIHByw6ljw6lkZW50ZSBwb3VyIGNoYXJnZXIgbCdlbXByaXNlIGRlIE1vbnRwZWxsaWVyDQogICAgICAgICAgICAgICAgICAgICAgICAjIGNxbF9maWx0ZXIgPSBwYXN0ZSgiY29kZV9pbnNlZT0nNzczMTYnIiwgImNvZGVfaW5zZWU9Jzc3MTg2JyIsICJjb2RlX2luc2VlPSc3NzQ2MyciLCAiY29kZV9pbnNlZT0nNzc0MTknIiwgImNvZGVfaW5zZWU9Jzc3MDE0JyIsICJjb2RlX2luc2VlPSc3NzA3OSciLCBzZXAgPSAiIE9SICIpKQ0KICAgICAgICAgICAgICAgICAgICAgICAgIyBiYm94ID0gIjQ4Ljg4NDE3LDIuMzQ0NjYsNDguOTA5NTYsMi4zODMwOCIpICMgZXBzZzoyMTU0DQpyZXF1ZXN0IDwtIHN0X3JlYWQoYnVpbGRfdXJsKHBhcnNlX3VybCkpDQpgYGANCg0KIyMgVFJBTlNGT1JNQVRJT04NCg0KKiBMYSAqKnN0YW5kYXJkaXNhdGlvbiAoc2NvcmVzIHopKiogc2UgY29uY2VudHJlIHN1ciBsYSByZWNvbmZpZ3VyYXRpb24gZGVzIGRvbm7DqWVzIHBvdXIgcXUnZWxsZXMgYWllbnQgdW5lIG1veWVubmUgZGUgMCBldCB1biDDqWNhcnQtdHlwZSBkZSAxLCByZW5kYW50IGFpbnNpIGxhIGRpc3RyaWJ1dGlvbiBkZXMgZG9ubsOpZXMgc3RhbmRhcmRpc8OpZSBlbiB0ZXJtZXMgZCd1bml0w6lzIGQnw6ljYXJ0LXR5cGUuIEVsbGUgZXN0IHV0aWxlIHBvdXIgY29tcGFyZXIgZGVzIHNjb3JlcyBlbnRyZSBkaWZmw6lyZW50ZXMgw6ljaGVsbGVzIGV0IGVzdCBzb3V2ZW50IHByw6lmw6lyw6llIGRhbnMgbGVzIGFuYWx5c2VzIHF1aSBzdXBwb3NlbnQgdW5lIGRpc3RyaWJ1dGlvbiBub3JtYWxlIGRlcyBkb25uw6llcy4gICAgDQo8YnI+DQoqIExhICoqbm9ybWFsaXNhdGlvbiBNaW4tTWF4KiogcmVkaW1lbnNpb25uZSBsZXMgZG9ubsOpZXMgZGFucyB1biBpbnRlcnZhbGxlIGZpeGUsIHNvdXZlbnQgWzAsIDFdLCBlbiBhanVzdGFudCBjaGFxdWUgdmFsZXVyIHNlbG9uIGxlcyB2YWxldXJzIG1pbmltYWxlcyBldCBtYXhpbWFsZXMgZGUgbCdlbnNlbWJsZSBkZSBkb25uw6llcywgY2UgcXVpIGVzdCB1dGlsZSBwb3VyIGxlcyBtb2TDqGxlcyBzZW5zaWJsZXMgYXV4IHZhcmlhdGlvbnMgZCfDqWNoZWxsZSBlbnRyZSBsZXMgdmFyaWFibGVzLiBFbGxlIGVzdCBwYXJ0aWN1bGnDqHJlbWVudCB1dGlsZSBwb3VyIGxlcyBhbGdvcml0aG1lcyBzZW5zaWJsZXMgYXV4IMOpY2hlbGxlcyBkZXMgdmFyaWFibGVzLCBjb21tZSBsZXMgYWxnb3JpdGhtZXMgYmFzw6lzIHN1ciBsZXMgZGlzdGFuY2VzLiBMYSBub3JtYWxpc2F0aW9uIE1pbi1NYXggZXN0IHNvdXZlbnQgdXRpbGlzw6llIGRhbnMgbGVzIGNvbnRleHRlcyBvw7kgbCd1bmlmb3JtaXTDqSBkZSBsJ8OpY2hlbGxlIGVudHJlIGxlcyB2YXJpYWJsZXMgZXN0IGNydWNpYWxlLiAgIA0KPGJyPg0KKiBMYSAqKm5vcm1hbGlzYXRpb24gcGFyIGxhIHZhbGV1ciBtYXhpbWFsZSoqIGFqdXN0ZSBsZXMgZG9ubsOpZXMgc2Vsb24gbGEgcGx1cyBncmFuZGUgdmFsZXVyIGFic29sdWUgZGUgbCdlbnNlbWJsZSBkZSBkb25uw6llcywgcmVuZGFudCB0b3V0ZXMgbGVzIHZhbGV1cnMgcmVsYXRpdmVzIMOgIGNldHRlIHZhbGV1ciBtYXhpbWFsZSwgY2UgcXVpIGVzdCB1bmUgYXBwcm9jaGUgc2ltcGxpZmnDqWUgZGUgbWlzZSDDoCBsJ8OpY2hlbGxlIHBhciByYXBwb3J0IMOgIHVuZSB2YWxldXIgZGUgcsOpZsOpcmVuY2UgdW5pcXVlLiBFbGxlIGVzdCBzaW1pbGFpcmUgw6AgbGEgbm9ybWFsaXNhdGlvbiBNaW4tTWF4IG1haXMgc2UgY29uY2VudHJlIHVuaXF1ZW1lbnQgc3VyIGxlIHJlZGltZW5zaW9ubmVtZW50IHBhciByYXBwb3J0IMOgIGxhIHZhbGV1ciBtYXhpbWFsZSBkZSBjaGFxdWUgdmFyaWFibGUuIFNpIHRvdXRlcyBsZXMgdmFsZXVycyBzb250IHBvc2l0aXZlcywgbGVzIGRvbm7DqWVzIHZhcmllbnQgZW50cmUgMCBldCAxLiAgTGEgbm9ybWFsaXNhdGlvbiBwYXIgbGEgdmFsZXVyIG1heGltYWxlIHBldXQgw6p0cmUgcHLDqWbDqXLDqWUgcG91ciBzYSBzaW1wbGljaXTDqSBldCBsb3JzcXVlIGxhIGRvbWluYW5jZSBkZSBsYSB2YWxldXIgbWF4aW1hbGUgZXN0IHNpZ25pZmljYXRpdmUgcG91ciBsJ2FuYWx5c2UuICANCjxicj4NCg0KIyMgVkFOT0lTRQ0KDQoqKlRvdXIgZGUgbGEgVmFub2lzZSAyMDE0KiogIA0KDQpOMSA6IHJlZnVnZSBkZSBsYSBMZWlzc2UgIA0KTjIgOiByZWZ1Z2UgZHUgcGxhbiBzZWMgIA0KTjMgOiByZWZ1Z2UgZGUgbOKAmUFpZ3VpbGxlIERvcmFuICANCk40IDogcmVmdWdlIGRlcyBCYXJtZXR0ZXMgIA0KTjUgOiByZWZ1Z2UgZGUgVmFsbG9uYnJ1biAgDQpONiA6IHJlZnVnZSBkdSBmb25kIGRlcyBmb3VycyAgIA0KDQpgYGB7ciwgb3V0LndpZHRoPScxMDAlJywgZWNobz0gRn0NCnRtYXBfbW9kZSgndmlldycpDQpkZXBfYXJyaXZlZSA8LSBzZjo6c3RfcmVhZCgnZGF0YS9kZXBfYXJ2Lmdwa2cnKQ0KdmFub2lzZSA8LSBzZjo6c3RfcmVhZCgnZGF0YS90cmFjZV92YW5vaXNlLmdwa2cnKQ0KdG1fYmFzZW1hcChjKGxlYWZsZXQ6OnByb3ZpZGVycyRPcGVuU3RyZWV0TWFwLA0KICAgICAgICAgICAgIGxlYWZsZXQ6OnByb3ZpZGVycyRFc3JpLldvcmxkVG9wb01hcCwNCiAgICAgICAgICAgICBsZWFmbGV0Ojpwcm92aWRlcnMkR2VvcG9ydGFpbEZyYW5jZS5vcnRob3MpKSArIA0KICANCiAgdG1fc2hhcGUoc3RfbWFrZV92YWxpZCh2YW5vaXNlKSkgKyB0bV9saW5lcyhsd2QgPSAzLCBjb2wgPSAicmVkIikgKyANCiAgICB0bV9zaGFwZShzdF9tYWtlX3ZhbGlkKGRlcF9hcnJpdmVlKSkgKyB0bV9kb3RzKHNpemUgPSAwLjUsIA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgY29sID0gImJsYWNrIiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGxlZ2VuZC5zaG93ID0gVCkgDQoNCmBgYA0KDQoNCiMjIE5FVyAoKQ0KICANCg0KDQoNCg0KIyBDaXRhdGlvbg0KDQpQb3VyIGNpdGVyIGNlcyB0cmF2YXV4IDoNCg0KT2xpdmllciBUaGV1cmVhdXguIG4uZC4g4oCcVHV0b3JpZWxzIFIgZGFucyBsZSBjYWRyZSBkJ3VuZSBtaXNzaW9uIGRlIGfDqW9tYXRpY2llbiBhdSBzZWluIGR1IExWTVTigJ0=