Viajes más Frecuentes

En esta parte, utilizaremos los datos de Origen y Destino y la frecuencia de de viaje de un lugar a otro, es decir la cantidad de veces que un Origen esta relacionado con un Destino.

Para esto, vamos a utilizar grafos, y los visualizaremos tanto con grafos dirigidos como grafos no dirigidos. Estos se ara con las librerias del paquete ggraph.

# Corro las librerias necesarias
library(ggraph)
library(ggplot2)
# library(ggthemes)
library(dplyr)
library(igraph)

# Leo el csv con los datos
df.vuelos <- read.csv("./csv/vuelos_2021.csv")

df.vuelos

El dataset ya esta ordenado por Origen y Destino.

Ahora, añadimos una columna contando la frecuencias de estos viajes.

frec.viajes <- df.vuelos %>% select(Origen, Destino) %>% group_by(Origen,Destino) %>% summarise(n=n())
`summarise()` has grouped output by 'Origen'. You can override using the `.groups` argument.
frec.viajes

Habiendo tomado las freciencias de los viajes segun origen y destino, ya tenemos la información necesaria para podes visualizar lo que pasa con algun tipo de grafo.

Grafo NO dirigido

Tomemos los datos y produscamos un grafo no dirigido donde el ancho del hilo de unión entre dos aeropuertos represente la cantidad de vuelos que hay entre estos dos aeropuertos.

# Tomo el dataframe del grafo y pido que no sea dirigido
grafo <- graph.data.frame(frec.viajes, directed = F)


plot.igraph( grafo, 
             vertex.frame.color = "Forestgreen",
             vertex.size=degree(grafo, mode = "out"),
             vertex.label.cex=c(2,2.5,3), 
             vertex.label.dist=0,
             edge.color="black",
             edge.curved = F, 
             edge.width = E(grafo)/30)

En el grafo se puede visualizar que la mayoria de los viajes salen o de EZE o de AER. Entonces, tendria sentido preguntarse si hay alguna diferencia entre los vuelos que salen de EZE (o llegan) y los de AER.

Hagamos el grafo, pero tomando un origen y viendo que pasa con cada uno de estos.

## La idea de este codigo es que cuando ustedes lo tengan a mano
## si oueden correrlo con , fig.show='animate' ouedan ver la 
## Secuencia de como esta cada uno relacionado con todos.
## A mi me falto tener instalado ffmep en windows cosa 
## que se me complico instalar antes de la entrega
grafica.grafo.origen <- function(df, filtro){
  # Filtra el dataset  
  dt <- df %>% filter(Origen == filtro)
  # crea el grafo
  gf <- graph.data.frame(dt, directed = F)
  
  # Dibuja el grafo
  plot.igraph( gf, 
             vertex.frame.color = "Forestgreen",
             vertex.label.cex=c(1.5,1,0.5), 
             vertex.label.dist=0,
             edge.color="black",
             edge.curved = F, 
             edge.width = E(gf)/30)
  }

nombres <- names(table(frec.viajes$Origen))

for (i in (1:4)){
  grafica.grafo.origen(frec.viajes, nombres[i])  
}

NA

¿Como montar en un mapa un grafo?

Necesitaria agregar las coordenadas de origenes y destino.

coord.origen <- df.vuelos %>% select(Origen, long.origen, lat.origen)
orig  <- table(coord.origen$Origen)
longs <- table(coord.origen$long.origen)

dim(orig)
[1] 35
dim(longs)
[1] 36
orig

 AER  BAR  BCA  CAT  CBA  CHP  CRR  CRV  DOZ  DRY  ECA  ESQ  EZE  GAL  GRA  IGU  JUA  JUJ  MDP  NEU  OSA  PAR 
5458 1645  149   21 1504  138  115  348 1022   11  486   80 4693  226   83  519  187  311  338  659   51   50 
 POS  ROS  SAL  SDE  SIS  SRA  SVO  TRC  TRE  TUC  UIS  USU  VIE 
 210  352  675  151  174   68   56   52  268  513   68  681   65 
longs

-72.0530451840514 -71.1593078789764 -71.1406808520164  -71.136651782164 -69.3079189026267 -68.7938610497928 
              486              1638               138                79               226              1021 
-68.4187752541459 -68.4031998682355 -68.3081049861187 -68.1544361990665 -67.7530829770762 -67.4655327946096 
              183                71               684               673                83               347 
-66.7923488935793 -66.3562805328339 -65.7527107900176 -65.4843872446955 -65.2686989064158 -65.1034976908493 
                2                70                21               677               269                11 
-65.1022544666745 -65.0991094243988 -64.3100211158383 -64.2748450739449 -64.2692882582764 -64.2082032341871 
              512               315               150                51                53              1482 
-62.9992672145635 -62.1537984793454 -60.8118014885171 -60.7852315547043  -60.480481255287 -59.0572367358005 
               71               147                58               352                52               180 
-58.7620000607308 -58.5424618655614        -58.413428 -57.5740449979357 -55.9712220853889 -54.4758314486937 
              117              4697              5435               340               211               525 

Vemos que, al parecer, tenemos algunos problemas con las coordenadas, pues no tenemos iguales longitudes de origenes con longitudes de origenes (Deberiamos ver porque pasa esto).

Tomemos entonces el archivo viejo sobre coordenadas en ANAC para unirlo con el dataset de frecuencias.

data.coordenadas <- read.csv("./csv/sna_abril_2021_fixed_encoding.csv", encoding = 'UTF-8')
data.coordenadas <- data.coordenadas %>%
  select(fna, ana, x, y) %>%
  rename(
    aeropuerto  = fna,
    codigo.ANAC = ana,
    longitud = x,
    latitud  = y
    )

df.origen <- merge( x=frec.viajes, y=data.coordenadas, 
                    by.x='Origen', by.y='codigo.ANAC')

df.destino <- merge(x=frec.viajes, y=data.coordenadas,
                    by.x='Destino', by.y='codigo.ANAC')

df.destino <- df.destino %>% arrange(Origen, n )
df.origen  <- df.origen  %>% arrange(Origen, n )

new.df.frec.viajes <- df.origen %>% 
  mutate(
    lat.des  = df.destino$latitud,
    long.des = df.destino$longitud
  ) %>%
  rename(
    lat.or  = latitud,
    long.or = longitud,
  )

new.df.frec.viajes

Ahora si tenemos todo como queriamos. Sabiendo que tenemos \(35\) Origenes posibles, intentemos armar los grafos para cada aeropuerto representandolo en un mapa. Esto nos permitira visualizar de una mejor manera lo que pasa entre los enlaces de aeropuetos.

colors()[1]
[1] "white"

library(ggmap)
library(maps)
library(mapdata)
library(geosphere)

# Tomo datos coordenados de argentina, segun el dataframe dado por map_data
arg <- map_data("world", region = "Argentina")

# Limites coordenados aproximados de Argentina
# los calculo como el maximo y el minimo de las coordenadas en el dataframe
xlim <- c(min(arg$long), max(arg$long))
ylim <- c(min(arg$lat), max(arg$lat))

# Origen (string) identificara el origen segun el dataframe que tenemos
# El color (string) sera un color que represente a este origen
ploteo <- function(origen, color) {
  
  df.Origen <- new.df.frec.viajes %>% 
  filter(
    Origen == origen
    ) %>%
  select( long.or, lat.or, long.des, lat.des, n)

  for (i in (1:length(df.Origen$long.or))){
    
    inter <- gcIntermediate(c(df.Origen$long.or[i], df.Origen$lat.or[i]), 
                            c(df.Origen$long.des[i], df.Origen$lat.des[i]), 
                            n = df.Origen$n[i]*3, 
                            addStartEnd=TRUE,
                            breakAtDateLine=T
                          )
           
    lines(inter, col=color, lwd=df.Origen$n[i]/100)  
  }
}

# Creo un mapa para la Argentina
map( 'world', col="#cfcfcf", fill=TRUE, bg="white", lwd=0.05, xlim=xlim, ylim=ylim)

# Creo un vector con los origenes segun la variable orig usada mas arriba
vec.origenes <- names(orig)

for (i in (1:length(vec.origenes))){
  ploteo(vec.origenes[i], colors()[i+7])
  cat("Origen: ", vec.origenes[i], "color identificador : ", colors()[i+10], "\n")
  }
Origen:  AER color identificador :  aquamarine3 
Origen:  BAR color identificador :  aquamarine4 
Origen:  BCA color identificador :  azure 
Origen:  CAT color identificador :  azure1 
Origen:  CBA color identificador :  azure2 
Origen:  CHP color identificador :  azure3 
Origen:  CRR color identificador :  azure4 
Origen:  CRV color identificador :  beige 
Origen:  DOZ color identificador :  bisque 
Origen:  DRY color identificador :  bisque1 
Origen:  ECA color identificador :  bisque2 
Origen:  ESQ color identificador :  bisque3 
Origen:  EZE color identificador :  bisque4 
Origen:  GAL color identificador :  black 
Origen:  GRA color identificador :  blanchedalmond 
Origen:  IGU color identificador :  blue 
Origen:  JUA color identificador :  blue1 
Origen:  JUJ color identificador :  blue2 
Origen:  MDP color identificador :  blue3 
Origen:  NEU color identificador :  blue4 
Origen:  OSA color identificador :  blueviolet 
Origen:  PAR color identificador :  brown 
Origen:  POS color identificador :  brown1 
Origen:  ROS color identificador :  brown2 
Origen:  SAL color identificador :  brown3 
Origen:  SDE color identificador :  brown4 
Origen:  SIS color identificador :  burlywood 
Origen:  SRA color identificador :  burlywood1 
Origen:  SVO color identificador :  burlywood2 
Origen:  TRC color identificador :  burlywood3 
Origen:  TRE color identificador :  burlywood4 
Origen:  TUC color identificador :  cadetblue 
Origen:  UIS color identificador :  cadetblue1 
Origen:  USU color identificador :  cadetblue2 
Origen:  VIE color identificador :  cadetblue3 
points(x=new.df.frec.viajes$long.des, 
       y=new.df.frec.viajes$lat.des, 
       col="slateblue", cex=2, pch=20)

text(new.df.frec.viajes$Destino, 
     x=new.df.frec.viajes$long.des, 
     y=new.df.frec.viajes$lat.des,  
     col="#ff8142", cex=1, pos=4)

Faltaria emprolijar.

LS0tDQp0aXRsZTogIlRQIC0gIEFlcm9wdWVydG9zIg0KYXV0aG9yOiAiR3J1cG8gODogIENvc2FyaW5za3kgTWF0aWFzLCBSZW11cyBFemVxdWllbCwgUmV5ZXMgQW5kcmVzICINCmRhdGU6ICIiDQpvdXRwdXQ6DQogIGh0bWxfZG9jdW1lbnQ6DQogICAgZGZfcHJpbnQ6IHBhZ2VkDQogICAgdG9jOiB5ZXMNCiAgaHRtbF9ub3RlYm9vazoNCiAgICB0aGVtZTogam91cm5hbA0KICAgIHRvYzogeWVzDQogICAgdG9jX2Zsb2F0OiB5ZXMNCiAgcGRmX2RvY3VtZW50Og0KICAgIHRvYzogeWVzDQpzdWJ0aXRsZTogTGFib3JhdG9yaW8gZGUgRGF0b3MNCi0tLQ0KPHN0eWxlIHR5cGU9InRleHQvY3NzIj4NCi50aXRsZSB7DQogIGRpc3BsYXk6IG5vbmU7DQp9DQoNCiNnZXR0aW5nLXN0YXJ0ZWQgaW1nIHsNCiAgbWFyZ2luLXJpZ2h0OiAxMHB4Ow0KfQ0KDQo8L3N0eWxlPg0KDQpgYGB7ciBzZXR1cCwgaW5jbHVkZT1GQUxTRX0NCmtuaXRyOjpvcHRzX2NodW5rJHNldChjb2xsYXBzZSA9IFRSVUUpDQpgYGANCiMgVmlhamVzIG3DoXMgRnJlY3VlbnRlcw0KDQpFbiBlc3RhIHBhcnRlLCB1dGlsaXphcmVtb3MgbG9zIGRhdG9zIGRlIGBPcmlnZW5gIHkgYERlc3Rpbm9gIHkgbGEgZnJlY3VlbmNpYSBkZSBkZSB2aWFqZSANCmRlIHVuIGx1Z2FyIGEgb3RybywgZXMgZGVjaXIgbGEgY2FudGlkYWQgZGUgdmVjZXMgcXVlIHVuICBgT3JpZ2VuYCBlc3RhIHJlbGFjaW9uYWRvIGNvbiB1biBgRGVzdGlub2AuDQoNClBhcmEgZXN0bywgdmFtb3MgYSB1dGlsaXphciBncmFmb3MsIHkgbG9zIHZpc3VhbGl6YXJlbW9zIHRhbnRvIGNvbiBncmFmb3MgZGlyaWdpZG9zIGNvbW8gZ3JhZm9zIG5vIGRpcmlnaWRvcy4gRXN0b3Mgc2UgYXJhIGNvbiBsYXMgbGlicmVyaWFzIGRlbCBwYXF1ZXRlIGBnZ3JhcGhgLg0KDQoNCmBgYHtyfQ0KIyBDb3JybyBsYXMgbGlicmVyaWFzIG5lY2VzYXJpYXMNCmxpYnJhcnkoZ2dyYXBoKQ0KbGlicmFyeShnZ3Bsb3QyKQ0KIyBsaWJyYXJ5KGdndGhlbWVzKQ0KbGlicmFyeShkcGx5cikNCmxpYnJhcnkoaWdyYXBoKQ0KDQojIExlbyBlbCBjc3YgY29uIGxvcyBkYXRvcw0KZGYudnVlbG9zIDwtIHJlYWQuY3N2KCIuL2Nzdi92dWVsb3NfMjAyMS5jc3YiKQ0KDQpkZi52dWVsb3MNCmBgYA0KDQpFbCBgZGF0YXNldGAgeWEgZXN0YSBvcmRlbmFkbyBwb3IgKk9yaWdlbiogeSAqRGVzdGlubyouDQoNCkFob3JhLCBhw7FhZGltb3MgdW5hIGNvbHVtbmEgY29udGFuZG8gbGEgZnJlY3VlbmNpYXMgZGUgZXN0b3MgdmlhamVzLg0KDQpgYGB7cn0NCmZyZWMudmlhamVzIDwtIGRmLnZ1ZWxvcyAlPiUgc2VsZWN0KE9yaWdlbiwgRGVzdGlubykgJT4lIGdyb3VwX2J5KE9yaWdlbixEZXN0aW5vKSAlPiUgc3VtbWFyaXNlKG49bigpKQ0KZnJlYy52aWFqZXMNCmBgYA0KDQpIYWJpZW5kbyB0b21hZG8gbGFzIGZyZWNpZW5jaWFzIGRlIGxvcyB2aWFqZXMgc2VndW4gb3JpZ2VuIHkgZGVzdGlubywgeWEgdGVuZW1vcyBsYSBpbmZvcm1hY2nDs24gbmVjZXNhcmlhIHBhcmEgcG9kZXMgdmlzdWFsaXphciBsbyBxdWUgcGFzYSBjb24gYWxndW4gdGlwbyBkZSBncmFmby4NCg0KIyMgR3JhZm8gTk8gZGlyaWdpZG8NCg0KVG9tZW1vcyBsb3MgZGF0b3MgeSBwcm9kdXNjYW1vcyB1biBncmFmbyBubyBkaXJpZ2lkbyBkb25kZSBlbCBhbmNobyBkZWwgaGlsbyBkZSB1bmnDs24gZW50cmUNCmRvcyBhZXJvcHVlcnRvcyByZXByZXNlbnRlIGxhIGNhbnRpZGFkIGRlIHZ1ZWxvcyBxdWUgaGF5IGVudHJlIGVzdG9zIGRvcyBhZXJvcHVlcnRvcy4NCg0KYGBge3IgIEZpZzEsIGVjaG89VFJVRSwgZmlnLmhlaWdodD0zMCwgZmlnLndpZHRoPTMwfQ0KIyBUb21vIGVsIGRhdGFmcmFtZSBkZWwgZ3JhZm8geSBwaWRvIHF1ZSBubyBzZWEgZGlyaWdpZG8NCmdyYWZvIDwtIGdyYXBoLmRhdGEuZnJhbWUoZnJlYy52aWFqZXMsIGRpcmVjdGVkID0gRikNCg0KDQpwbG90LmlncmFwaCggZ3JhZm8sIA0KICAgICAgICAgICAgIHZlcnRleC5mcmFtZS5jb2xvciA9ICJGb3Jlc3RncmVlbiIsDQogICAgICAgICAgICAgdmVydGV4LnNpemU9ZGVncmVlKGdyYWZvLCBtb2RlID0gIm91dCIpLA0KICAgICAgICAgICAgIHZlcnRleC5sYWJlbC5jZXg9YygyLDIuNSwzKSwgDQogICAgICAgICAgICAgdmVydGV4LmxhYmVsLmRpc3Q9MCwNCiAgICAgICAgICAgICBlZGdlLmNvbG9yPSJibGFjayIsDQogICAgICAgICAgICAgZWRnZS5jdXJ2ZWQgPSBGLCANCiAgICAgICAgICAgICBlZGdlLndpZHRoID0gRShncmFmbykvMzApDQoNCmBgYA0KDQpFbiBlbCBncmFmbyBzZSBwdWVkZSB2aXN1YWxpemFyIHF1ZSBsYSBtYXlvcmlhIGRlIGxvcyB2aWFqZXMgc2FsZW4gbyBkZSBgRVpFYCBvIGRlIGBBRVJgLiBFbnRvbmNlcywgdGVuZHJpYSBzZW50aWRvIHByZWd1bnRhcnNlIHNpIGhheSBhbGd1bmEgZGlmZXJlbmNpYSBlbnRyZSBsb3MgdnVlbG9zIHF1ZSBzYWxlbiBkZSBgRVpFYCAobyBsbGVnYW4pIHkgbG9zIGRlIGBBRVJgLg0KDQoNCkhhZ2Ftb3MgZWwgZ3JhZm8sIHBlcm8gdG9tYW5kbyB1biBvcmlnZW4geSB2aWVuZG8gcXVlIHBhc2EgY29uIGNhZGEgdW5vIGRlIGVzdG9zLg0KYGBge3J9DQojIyBMYSBpZGVhIGRlIGVzdGUgY29kaWdvIGVzIHF1ZSBjdWFuZG8gdXN0ZWRlcyBsbyB0ZW5nYW4gYSBtYW5vDQojIyBzaSBvdWVkZW4gY29ycmVybG8gY29uICwgZmlnLnNob3c9J2FuaW1hdGUnIG91ZWRhbiB2ZXIgbGEgDQojIyBTZWN1ZW5jaWEgZGUgY29tbyBlc3RhIGNhZGEgdW5vIHJlbGFjaW9uYWRvIGNvbiB0b2Rvcy4NCiMjIEEgbWkgbWUgZmFsdG8gdGVuZXIgaW5zdGFsYWRvIGZmbWVwIGVuIHdpbmRvd3MgY29zYSANCiMjIHF1ZSBzZSBtZSBjb21wbGljbyBpbnN0YWxhciBhbnRlcyBkZSBsYSBlbnRyZWdhDQpncmFmaWNhLmdyYWZvLm9yaWdlbiA8LSBmdW5jdGlvbihkZiwgZmlsdHJvKXsNCiAgIyBGaWx0cmEgZWwgZGF0YXNldCAgDQogIGR0IDwtIGRmICU+JSBmaWx0ZXIoT3JpZ2VuID09IGZpbHRybykNCiAgIyBjcmVhIGVsIGdyYWZvDQogIGdmIDwtIGdyYXBoLmRhdGEuZnJhbWUoZHQsIGRpcmVjdGVkID0gRikNCiAgDQogICMgRGlidWphIGVsIGdyYWZvDQogIHBsb3QuaWdyYXBoKCBnZiwgDQogICAgICAgICAgICAgdmVydGV4LmZyYW1lLmNvbG9yID0gIkZvcmVzdGdyZWVuIiwNCiAgICAgICAgICAgICB2ZXJ0ZXgubGFiZWwuY2V4PWMoMS41LDEsMC41KSwgDQogICAgICAgICAgICAgdmVydGV4LmxhYmVsLmRpc3Q9MCwNCiAgICAgICAgICAgICBlZGdlLmNvbG9yPSJibGFjayIsDQogICAgICAgICAgICAgZWRnZS5jdXJ2ZWQgPSBGLCANCiAgICAgICAgICAgICBlZGdlLndpZHRoID0gRShnZikvMzApDQogIH0NCg0Kbm9tYnJlcyA8LSBuYW1lcyh0YWJsZShmcmVjLnZpYWplcyRPcmlnZW4pKQ0KDQpmb3IgKGkgaW4gKDE6NCkpew0KICBncmFmaWNhLmdyYWZvLm9yaWdlbihmcmVjLnZpYWplcywgbm9tYnJlc1tpXSkgIA0KfQ0KDQpgYGANCg0KIyMgwr9Db21vIG1vbnRhciBlbiB1biBtYXBhIHVuIGdyYWZvPw0KDQpOZWNlc2l0YXJpYSBhZ3JlZ2FyIGxhcyBjb29yZGVuYWRhcyBkZSBvcmlnZW5lcyB5IGRlc3Rpbm8uIA0KDQpgYGB7cn0NCmNvb3JkLm9yaWdlbiA8LSBkZi52dWVsb3MgJT4lIHNlbGVjdChPcmlnZW4sIGxvbmcub3JpZ2VuLCBsYXQub3JpZ2VuKQ0Kb3JpZyAgPC0gdGFibGUoY29vcmQub3JpZ2VuJE9yaWdlbikNCmxvbmdzIDwtIHRhYmxlKGNvb3JkLm9yaWdlbiRsb25nLm9yaWdlbikNCg0KZGltKG9yaWcpDQpkaW0obG9uZ3MpDQpvcmlnDQpsb25ncw0KYGBgDQoNClZlbW9zIHF1ZSwgYWwgcGFyZWNlciwgdGVuZW1vcyBhbGd1bm9zIHByb2JsZW1hcyBjb24gbGFzIGNvb3JkZW5hZGFzLCBwdWVzIG5vIHRlbmVtb3MgaWd1YWxlcyBsb25naXR1ZGVzIGRlIG9yaWdlbmVzIGNvbiBsb25naXR1ZGVzIGRlIG9yaWdlbmVzIChEZWJlcmlhbW9zIHZlciBwb3JxdWUgcGFzYSBlc3RvKS4gDQoNClRvbWVtb3MgZW50b25jZXMgZWwgYXJjaGl2byB2aWVqbyBzb2JyZSBjb29yZGVuYWRhcyBlbiBBTkFDIHBhcmEgdW5pcmxvIGNvbiBlbCBkYXRhc2V0IGRlIGZyZWN1ZW5jaWFzLg0KDQpgYGB7cn0NCmRhdGEuY29vcmRlbmFkYXMgPC0gcmVhZC5jc3YoIi4vY3N2L3NuYV9hYnJpbF8yMDIxX2ZpeGVkX2VuY29kaW5nLmNzdiIsIGVuY29kaW5nID0gJ1VURi04JykNCmRhdGEuY29vcmRlbmFkYXMgPC0gZGF0YS5jb29yZGVuYWRhcyAlPiUNCiAgc2VsZWN0KGZuYSwgYW5hLCB4LCB5KSAlPiUNCiAgcmVuYW1lKA0KICAgIGFlcm9wdWVydG8gID0gZm5hLA0KICAgIGNvZGlnby5BTkFDID0gYW5hLA0KICAgIGxvbmdpdHVkID0geCwNCiAgICBsYXRpdHVkICA9IHkNCiAgICApDQoNCmRmLm9yaWdlbiA8LSBtZXJnZSggeD1mcmVjLnZpYWplcywgeT1kYXRhLmNvb3JkZW5hZGFzLCANCiAgICAgICAgICAgICAgICAgICAgYnkueD0nT3JpZ2VuJywgYnkueT0nY29kaWdvLkFOQUMnKQ0KDQpkZi5kZXN0aW5vIDwtIG1lcmdlKHg9ZnJlYy52aWFqZXMsIHk9ZGF0YS5jb29yZGVuYWRhcywNCiAgICAgICAgICAgICAgICAgICAgYnkueD0nRGVzdGlubycsIGJ5Lnk9J2NvZGlnby5BTkFDJykNCg0KZGYuZGVzdGlubyA8LSBkZi5kZXN0aW5vICU+JSBhcnJhbmdlKE9yaWdlbiwgbiApDQpkZi5vcmlnZW4gIDwtIGRmLm9yaWdlbiAgJT4lIGFycmFuZ2UoT3JpZ2VuLCBuICkNCg0KbmV3LmRmLmZyZWMudmlhamVzIDwtIGRmLm9yaWdlbiAlPiUgDQogIG11dGF0ZSgNCiAgICBsYXQuZGVzICA9IGRmLmRlc3Rpbm8kbGF0aXR1ZCwNCiAgICBsb25nLmRlcyA9IGRmLmRlc3Rpbm8kbG9uZ2l0dWQNCiAgKSAlPiUNCiAgcmVuYW1lKA0KICAgIGxhdC5vciAgPSBsYXRpdHVkLA0KICAgIGxvbmcub3IgPSBsb25naXR1ZCwNCiAgKQ0KDQpuZXcuZGYuZnJlYy52aWFqZXMNCmBgYA0KDQpBaG9yYSBzaSB0ZW5lbW9zIHRvZG8gY29tbyBxdWVyaWFtb3MuIFNhYmllbmRvIHF1ZSB0ZW5lbW9zICQzNSQgT3JpZ2VuZXMgcG9zaWJsZXMsIGludGVudGVtb3MNCmFybWFyIGxvcyBncmFmb3MgcGFyYSBjYWRhIGFlcm9wdWVydG8gcmVwcmVzZW50YW5kb2xvIGVuIHVuIG1hcGEuIEVzdG8gbm9zIHBlcm1pdGlyYSB2aXN1YWxpemFyIGRlIHVuYSBtZWpvciANCm1hbmVyYSBsbyBxdWUgcGFzYSBlbnRyZSBsb3MgZW5sYWNlcyBkZSBhZXJvcHVldG9zLg0KDQpgYGB7cn0NCmNvbG9ycygpWzFdDQpgYGANCg0KYGBge3IsIGZpZy5oZWlnaHQ9MjAsIGZpZy53aWR0aD0xNSB9DQoNCmxpYnJhcnkoZ2dtYXApDQpsaWJyYXJ5KG1hcHMpDQpsaWJyYXJ5KG1hcGRhdGEpDQpsaWJyYXJ5KGdlb3NwaGVyZSkNCg0KIyBUb21vIGRhdG9zIGNvb3JkZW5hZG9zIGRlIGFyZ2VudGluYSwgc2VndW4gZWwgZGF0YWZyYW1lIGRhZG8gcG9yIG1hcF9kYXRhDQphcmcgPC0gbWFwX2RhdGEoIndvcmxkIiwgcmVnaW9uID0gIkFyZ2VudGluYSIpDQoNCiMgTGltaXRlcyBjb29yZGVuYWRvcyBhcHJveGltYWRvcyBkZSBBcmdlbnRpbmENCiMgbG9zIGNhbGN1bG8gY29tbyBlbCBtYXhpbW8geSBlbCBtaW5pbW8gZGUgbGFzIGNvb3JkZW5hZGFzIGVuIGVsIGRhdGFmcmFtZQ0KeGxpbSA8LSBjKG1pbihhcmckbG9uZyksIG1heChhcmckbG9uZykpDQp5bGltIDwtIGMobWluKGFyZyRsYXQpLCBtYXgoYXJnJGxhdCkpDQoNCiMgT3JpZ2VuIChzdHJpbmcpIGlkZW50aWZpY2FyYSBlbCBvcmlnZW4gc2VndW4gZWwgZGF0YWZyYW1lIHF1ZSB0ZW5lbW9zDQojIEVsIGNvbG9yIChzdHJpbmcpIHNlcmEgdW4gY29sb3IgcXVlIHJlcHJlc2VudGUgYSBlc3RlIG9yaWdlbg0KcGxvdGVvIDwtIGZ1bmN0aW9uKG9yaWdlbiwgY29sb3IpIHsNCiAgDQogIGRmLk9yaWdlbiA8LSBuZXcuZGYuZnJlYy52aWFqZXMgJT4lIA0KICBmaWx0ZXIoDQogICAgT3JpZ2VuID09IG9yaWdlbg0KICAgICkgJT4lDQogIHNlbGVjdCggbG9uZy5vciwgbGF0Lm9yLCBsb25nLmRlcywgbGF0LmRlcywgbikNCg0KICBmb3IgKGkgaW4gKDE6bGVuZ3RoKGRmLk9yaWdlbiRsb25nLm9yKSkpew0KICAgIA0KICAgIGludGVyIDwtIGdjSW50ZXJtZWRpYXRlKGMoZGYuT3JpZ2VuJGxvbmcub3JbaV0sIGRmLk9yaWdlbiRsYXQub3JbaV0pLCANCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBjKGRmLk9yaWdlbiRsb25nLmRlc1tpXSwgZGYuT3JpZ2VuJGxhdC5kZXNbaV0pLCANCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBuID0gZGYuT3JpZ2VuJG5baV0qMywgDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgYWRkU3RhcnRFbmQ9VFJVRSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBicmVha0F0RGF0ZUxpbmU9VA0KICAgICAgICAgICAgICAgICAgICAgICAgICApDQogICAgICAgICAgIA0KICAgIGxpbmVzKGludGVyLCBjb2w9Y29sb3IsIGx3ZD1kZi5PcmlnZW4kbltpXS8xMDApICANCiAgfQ0KfQ0KDQojIENyZW8gdW4gbWFwYSBwYXJhIGxhIEFyZ2VudGluYQ0KbWFwKCAnd29ybGQnLCBjb2w9IiNjZmNmY2YiLCBmaWxsPVRSVUUsIGJnPSJ3aGl0ZSIsIGx3ZD0wLjA1LCB4bGltPXhsaW0sIHlsaW09eWxpbSkNCg0KIyBDcmVvIHVuIHZlY3RvciBjb24gbG9zIG9yaWdlbmVzIHNlZ3VuIGxhIHZhcmlhYmxlIG9yaWcgdXNhZGEgbWFzIGFycmliYQ0KdmVjLm9yaWdlbmVzIDwtIG5hbWVzKG9yaWcpDQoNCmZvciAoaSBpbiAoMTpsZW5ndGgodmVjLm9yaWdlbmVzKSkpew0KICBwbG90ZW8odmVjLm9yaWdlbmVzW2ldLCBjb2xvcnMoKVtpKzddKQ0KICBjYXQoIk9yaWdlbjogIiwgdmVjLm9yaWdlbmVzW2ldLCAiY29sb3IgaWRlbnRpZmljYWRvciA6ICIsIGNvbG9ycygpW2krMTBdLCAiXG4iKQ0KICB9DQoNCnBvaW50cyh4PW5ldy5kZi5mcmVjLnZpYWplcyRsb25nLmRlcywgDQogICAgICAgeT1uZXcuZGYuZnJlYy52aWFqZXMkbGF0LmRlcywgDQogICAgICAgY29sPSJzbGF0ZWJsdWUiLCBjZXg9MiwgcGNoPTIwKQ0KDQp0ZXh0KG5ldy5kZi5mcmVjLnZpYWplcyREZXN0aW5vLCANCiAgICAgeD1uZXcuZGYuZnJlYy52aWFqZXMkbG9uZy5kZXMsIA0KICAgICB5PW5ldy5kZi5mcmVjLnZpYWplcyRsYXQuZGVzLCAgDQogICAgIGNvbD0iI2ZmODE0MiIsIGNleD0xLCBwb3M9NCkNCmBgYA0KDQoNCg0KRmFsdGFyaWEgZW1wcm9saWphci4NCg0KDQoNCg0K