Paula Cazali

14000060

Cargamos las librerias necesarias para el proyecto:

library(readr)
library(tidyverse)
library(httr)
library(jsonlite)
library(ggmap)
library(gepaf)
library(dplyr)

Se usara un API key y y el API de google para obtener las direcciones desde cada punto.

api_key <- "api_key_goes_here"
register_google(key=api_key)

api_key <- "&key=api_key_goes_here"
url_part_1 <- "https://maps.googleapis.com/maps/api/distancematrix/json?units=metric&origins="
url_part_2 <- "&destinations="

Cargamos la informacion de los hospitales con un .csv y creamos un dataset con las coordenadas unidas

# hosp_coordinates <- read_csv("Universidad Galileo/Maestria/Simulacion 1/Proyecto Final/Proyecto Final/hosp_coordinates.csv")
hosp_coordinates <- read_csv("hosp_coordinates.csv")
Parsed with column specification:
cols(
  id = col_double(),
  lat = col_double(),
  lon = col_double(),
  hospital = col_character()
)
hosp_coordinates <- hosp_coordinates %>%
  unite(coordinate, c(lat,lon), sep = ",")
hosp_coordinates

Funcion que calcula la distancia desde un origen a multiples destinos. Devuelve un vector con las distancias

get_distance <- function(origin_id, coordinates, distance = T){
  destination <- coordinates[!coordinates$id == origin_id,]
  origin <- coordinates[coordinates$id == origin_id,]
  url <- paste0(url_part_1,
                origin$coordinate,
                url_part_2,
                paste0(destination$coordinate,collapse = "|"),
                api_key,collapse = "")
  
  response <- GET(url)
  resp_json <- fromJSON(content(response, as = "text"))
  resp_df <- resp_json[[3]][[1]][[1]]
  if(distance) return(resp_df[[1]][2]$value)
  else return(resp_df[[2]][2]$value)
}

En este for se hara una matriz que tiene las distancias desde cada ciudad hasta cada ciudad, se usa la funcion get_distance()

dist_matrix <- c()
for(hosp in 1:nrow(hosp_coordinates)){
  hosp_coordinates$id
  distance_vec <- get_distance(hosp, hosp_coordinates)
  distance_vec <- append(distance_vec, 0, after = hosp - 1)
  print(distance_vec)
  dist_matrix <- c(dist_matrix, distance_vec)
  #print(hosp)
}
 [1]     0  5543  7458  9543  9776  9682  4553  4553  3344  4908 11895  7853 10778
 [1]  5211     0  4661  6188  6421  5502  5237  5237  4711  5593 11143  8564  7069
 [1] 5777 4213    0 2311 2544 5488 7660 7660 4901 8016 9013 8384 5399
 [1]  7022  5275  2374     0   233  4016  8906  8906  6147  9261  9532 12396  3915
 [1]  7549  5056  2043  1017     0  3783  9432  9432  6674  9788  9727 12591  3791
 [1]  8936  5016  4412  4405  4638     0  9012  9012  7863  9368 13736 11651  3323
 [1]  5102  4259  7085  9164  9398  8398     0     0  4602   721 11516  9512  9493
 [1]  5102  4259  7085  9164  9398  8398     0     0  4602   721 11516  9512  9493
 [1]  3081  5555  5785  7087 13063  9129  6151  6151     0  6507 10439  4772 15958
 [1]  4816  3973  6799  8878  9111  8112  1198  1198  4316     0 11230  9226  9207
 [1]  9098 10742  9007  9606  9512 12983 10981 10981  7604 11336     0  9235 12407
 [1]  7101 10738  8965 13441 13347 12940 10977 10977  5710 11333 10723     0 16242
 [1]  9563  5625  5039  4265  4498  3137  9185  9185  8490  9541 12150 15014     0
dist_matrix <- matrix(dist_matrix, ncol = nrow(hosp_coordinates), byrow = TRUE)

Algoritmo Genetico para obtener la ruta mas corta

Definicion de funciones

# distancia total de toda la ruta
distance_route <- function(route,distance){
  sum_distance<-0
  for(i in 1:length(route[-1])){
    out <- distance[route[i],route[i+1] ]
    sum_distance <- sum_distance + out
  }
  return(sum_distance)
}

# genera una ruta aleatoria
sample_route <- function(..., cities){
  c(1,sample(2:cities),1) %>% return()
}

# Selecciona los padres en base a la distancia (roulette)
select_mating_parents <- function(...,pop_size, roullete, population){
  sum_fit_p <-
    sample(1:sum(roullete$rank), size = 1 )
  pindex <-
    roullete %>% 
    filter(cumsum_rank < sum_fit_p) %>%
    nrow()
  p1 <- roullete[pindex + 1,] %>% pull(parent)
  sum_fit_p <-
    sample(1:sum(roullete$rank),size = 1 )
  pindex <-
    roullete %>% 
    filter(cumsum_rank < sum_fit_p) %>%
    nrow()
  p2 <- roullete[pindex + 1,] %>% pull(parent)
  return(population[c(p1,p2)])
}

# Funcion para hacer el crossover con dos padres, se usa material genetico de uno y del otro.
position_based_crossover <- function(parents, n = 3){
  p1 <- parents[[1]]
  p2 <- parents[[2]]
  length_parent <- length(p1)
  p1 <- p1[2:(length_parent-1)]
  p2 <- p2[2:(length_parent-1)]
  index <- sample(1:(length_parent-2), size = n, replace = FALSE)
  child1 <- p1
  fill_values <- setdiff(p2, p1[index])
  child1[-index] <- fill_values
  return(c(1, child1, 1)) 
}

#Cambia de posicion dos ciudades, una por la otra.
swap <- function(vec, i, j){
  out <- vec
  out[i] <- vec[j]
  out[j] <- vec[i]
  return(out)
}

# Funcion para mutar los hijos
mutate_child <- function(child, rate = 0.03){
  if(runif(1) < rate){
    index <- 2:(length(child) - 1)
    i <- sample(index, size = 1)
    index <- setdiff(index,i)
    j <- sample(index, size = 1)
    child <- swap(child, i, j)
    #print(1)
  }
  return(child)
}

Obteniendo la ruta mas corta

population <- lapply(1:100, sample_route, cities = nrow(hosp_coordinates))

# FOR PARA OBTENER LA RUTA MAS CORTA.
for(i in 1:100){
  pop_fitness <- lapply(population, distance_route, distance = dist_matrix)
  
  roullete <-
    tibble(parent = 1:100, fitness = pop_fitness %>% unlist()) %>%
    arrange(desc(fitness))
  
  roullete$rank <- 1:nrow(roullete)
  
  roullete <-
    roullete %>% 
    mutate(cumsum_rank = cumsum(rank))
  
  mating_parents <-
    lapply(1:100, select_mating_parents,
           pop_size = 100,
           roullete = roullete, 
           population = population)
  
  children <- lapply(mating_parents, position_based_crossover, n = 5)
  
  population <- lapply(children, mutate_child, rate = 0.1)
  #print(pop_fitness[[which.min(pop_fitness %>% unlist())]])
}

min_route <- population[which.min(pop_fitness %>% unlist())]
min_route[[1]]
 [1]  1 12 11  3  4  5 13  6  2  8  7 10  9  1

Graficar la ruta mas corta

url_route1 <- "https://maps.googleapis.com/maps/api/directions/json?origin="
url_route2 <- "&destination="

# OBTENER LOS POLYLINES
poly_lines <- data.frame()

for(hosp in 1:length(min_route[[1]])){
  if(hosp == length(min_route[[1]])){
    origin <- hosp_coordinates[min_route[[1]][hosp],]
    destination <- hosp_coordinates[min_route[[1]][1],]
    #print(hosp)
  }else{
    #print(hosp)
    origin <- hosp_coordinates[min_route[[1]][hosp],]
    destination <- hosp_coordinates[min_route[[1]][hosp+1],]
  }
  url <- paste0(url_route1,
                origin$coordinate,
                url_route2,
                paste0(destination$coordinate),
                api_key,collapse = "")
  
  response <- GET(url)
  resp_json <- fromJSON(content(response, as = "text"))
  ruta <- resp_json$routes$overview_polyline$points
  ruta <- decodePolyline(ruta)
  aux_df <- ruta
  poly_lines <- rbind(poly_lines, aux_df)
}

Graficar la ruta mas corta

qmap('Estacion Tivoli',zoom = 13, maptype="roadmap") +
  geom_point(aes(x = lon, y = lat), 
             data = poly_lines, 
             colour = "red" )
Source : https://maps.googleapis.com/maps/api/staticmap?center=Estacion%20Tivoli&zoom=13&size=640x640&scale=2&maptype=roadmap&language=en-EN&key=xxx
Source : https://maps.googleapis.com/maps/api/geocode/json?address=Estacion+Tivoli&key=xxx

Algoritmo Simulated Annealing

# Generamos una ruta al azar 
simulated_route <- sample_route(cities = nrow(hosp_coordinates))

# Distancia que hay que recorrer en esa ruta
dist_actual <- distance_route(simulated_route, dist_matrix)

temperature <- 20
delta_temp <- -0.01

for (i in seq(from = temperature, to = 0, by = delta_temp)){
  dist_anterior <- dist_actual
  # quitaremos el punto que esta fijo en la ruta: el Aeropuerto (punto de inicio y fin)
  simulated_route <- simulated_route[2:(length(simulated_route)-1)]
  #cambio en las ciudades
  swap_index <- sample(1:(length(simulated_route)), 2, replace = FALSE)
  simulated_route_2 <- c(1,swap(simulated_route, swap_index[1],swap_index[2]),1)
  dist_actual <- distance_route(simulated_route_2, dist_matrix)
  
  delta_dist <- dist_anterior - dist_actual
  if(delta_dist > 0){
    simulated_route <- simulated_route_2
    #print("cambio")
  } else if(exp(delta_dist/temperature) > runif(1, 0, 1)){
    simulated_route <- simulated_route_2
    #print("probabilidad")
  }else {
    simulated_route <- c(1, simulated_route, 1)
    #print("No cambio")
  } 
}

distancia_obtenida <- distance_route(simulated_route, dist_matrix)
distancia_obtenida
[1] 66264

Obtener los polylines

poly_lines_SA <- data.frame()

for(hosp in 1:length(simulated_route)){
  if(hosp == length(simulated_route)){
    origin <- hosp_coordinates[simulated_route[hosp],]
    destination <- hosp_coordinates[simulated_route[1],]
    #print(hosp)
  }else{
    #print(hosp)
    origin <- hosp_coordinates[simulated_route[hosp],]
    destination <- hosp_coordinates[simulated_route[hosp+1],]
  }
  url <- paste0(url_route1,
                origin$coordinate,
                url_route2,
                paste0(destination$coordinate),
                api_key,collapse = "")
  
  response <- GET(url)
  resp_json <- fromJSON(content(response, as = "text"))
  ruta_SA <- resp_json$routes$overview_polyline$points
  ruta_SA <- decodePolyline(ruta_SA)
  aux_df <- ruta_SA
  poly_lines_SA <- rbind(poly_lines_SA, aux_df)
}

Graficar la ruta encontrada por el algoritmo Simulated Annealing

qmap('Estacion Tivoli',zoom = 13, maptype="roadmap") +
  geom_point(aes(x = lon, y = lat), 
             data = poly_lines_SA, 
             colour = "blue" )
Source : https://maps.googleapis.com/maps/api/staticmap?center=Estacion%20Tivoli&zoom=13&size=640x640&scale=2&maptype=roadmap&language=en-EN&key=xxx
Source : https://maps.googleapis.com/maps/api/geocode/json?address=Estacion+Tivoli&key=xxx

LS0tDQp0aXRsZTogIlByb3llY3RvIEZpbmFsOiBTaW11bGFjaW9uIDEiDQpvdXRwdXQ6IGh0bWxfbm90ZWJvb2sNCi0tLQ0KDQojIyBQYXVsYSBDYXphbGkNCiMjIDE0MDAwMDYwDQoNCg0KQ2FyZ2Ftb3MgbGFzIGxpYnJlcmlhcyBuZWNlc2FyaWFzIHBhcmEgZWwgcHJveWVjdG86DQpgYGB7cn0NCmxpYnJhcnkocmVhZHIpDQpsaWJyYXJ5KHRpZHl2ZXJzZSkNCmxpYnJhcnkoaHR0cikNCmxpYnJhcnkoanNvbmxpdGUpDQpsaWJyYXJ5KGdnbWFwKQ0KbGlicmFyeShnZXBhZikNCmxpYnJhcnkoZHBseXIpDQpgYGANCg0KDQpTZSB1c2FyYSB1biBBUEkga2V5IHkgeSBlbCBBUEkgZGUgZ29vZ2xlIHBhcmEgb2J0ZW5lciBsYXMgZGlyZWNjaW9uZXMgZGVzZGUgY2FkYSBwdW50by4NCmBgYHtyfQ0KYXBpX2tleSA8LSAiYXBpX2tleV9nb2VzX2hlcmUiDQpyZWdpc3Rlcl9nb29nbGUoa2V5PWFwaV9rZXkpDQoNCmFwaV9rZXkgPC0gIiZrZXk9YXBpX2tleV9nb2VzX2hlcmUiDQp1cmxfcGFydF8xIDwtICJodHRwczovL21hcHMuZ29vZ2xlYXBpcy5jb20vbWFwcy9hcGkvZGlzdGFuY2VtYXRyaXgvanNvbj91bml0cz1tZXRyaWMmb3JpZ2lucz0iDQp1cmxfcGFydF8yIDwtICImZGVzdGluYXRpb25zPSINCmBgYA0KDQoNCkNhcmdhbW9zIGxhIGluZm9ybWFjaW9uIGRlIGxvcyBob3NwaXRhbGVzIGNvbiB1biAuY3N2IHkgY3JlYW1vcyB1biBkYXRhc2V0IGNvbiBsYXMgY29vcmRlbmFkYXMgdW5pZGFzDQpgYGB7cn0NCiMgaG9zcF9jb29yZGluYXRlcyA8LSByZWFkX2NzdigiVW5pdmVyc2lkYWQgR2FsaWxlby9NYWVzdHJpYS9TaW11bGFjaW9uIDEvUHJveWVjdG8gRmluYWwvUHJveWVjdG8gRmluYWwvaG9zcF9jb29yZGluYXRlcy5jc3YiKQ0KaG9zcF9jb29yZGluYXRlcyA8LSByZWFkX2NzdigiaG9zcF9jb29yZGluYXRlcy5jc3YiKQ0KaG9zcF9jb29yZGluYXRlcyA8LSBob3NwX2Nvb3JkaW5hdGVzICU+JQ0KICB1bml0ZShjb29yZGluYXRlLCBjKGxhdCxsb24pLCBzZXAgPSAiLCIpDQpob3NwX2Nvb3JkaW5hdGVzDQpgYGANCg0KDQpGdW5jaW9uIHF1ZSBjYWxjdWxhIGxhIGRpc3RhbmNpYSBkZXNkZSB1biBvcmlnZW4gYSBtdWx0aXBsZXMgZGVzdGlub3MuIERldnVlbHZlIHVuIHZlY3RvciBjb24gbGFzIGRpc3RhbmNpYXMNCmBgYHtyfQ0KZ2V0X2Rpc3RhbmNlIDwtIGZ1bmN0aW9uKG9yaWdpbl9pZCwgY29vcmRpbmF0ZXMsIGRpc3RhbmNlID0gVCl7DQogIGRlc3RpbmF0aW9uIDwtIGNvb3JkaW5hdGVzWyFjb29yZGluYXRlcyRpZCA9PSBvcmlnaW5faWQsXQ0KICBvcmlnaW4gPC0gY29vcmRpbmF0ZXNbY29vcmRpbmF0ZXMkaWQgPT0gb3JpZ2luX2lkLF0NCiAgdXJsIDwtIHBhc3RlMCh1cmxfcGFydF8xLA0KICAgICAgICAgICAgICAgIG9yaWdpbiRjb29yZGluYXRlLA0KICAgICAgICAgICAgICAgIHVybF9wYXJ0XzIsDQogICAgICAgICAgICAgICAgcGFzdGUwKGRlc3RpbmF0aW9uJGNvb3JkaW5hdGUsY29sbGFwc2UgPSAifCIpLA0KICAgICAgICAgICAgICAgIGFwaV9rZXksY29sbGFwc2UgPSAiIikNCiAgDQogIHJlc3BvbnNlIDwtIEdFVCh1cmwpDQogIHJlc3BfanNvbiA8LSBmcm9tSlNPTihjb250ZW50KHJlc3BvbnNlLCBhcyA9ICJ0ZXh0IikpDQogIHJlc3BfZGYgPC0gcmVzcF9qc29uW1szXV1bWzFdXVtbMV1dDQogIGlmKGRpc3RhbmNlKSByZXR1cm4ocmVzcF9kZltbMV1dWzJdJHZhbHVlKQ0KICBlbHNlIHJldHVybihyZXNwX2RmW1syXV1bMl0kdmFsdWUpDQp9DQpgYGANCg0KDQpFbiBlc3RlIGZvciBzZSBoYXJhIHVuYSBtYXRyaXogcXVlIHRpZW5lIGxhcyBkaXN0YW5jaWFzIGRlc2RlIGNhZGEgY2l1ZGFkIGhhc3RhIGNhZGEgY2l1ZGFkLCBzZSB1c2EgbGEgZnVuY2lvbiBnZXRfZGlzdGFuY2UoKQ0KYGBge3J9DQpkaXN0X21hdHJpeCA8LSBjKCkNCmZvcihob3NwIGluIDE6bnJvdyhob3NwX2Nvb3JkaW5hdGVzKSl7DQogIGhvc3BfY29vcmRpbmF0ZXMkaWQNCiAgZGlzdGFuY2VfdmVjIDwtIGdldF9kaXN0YW5jZShob3NwLCBob3NwX2Nvb3JkaW5hdGVzKQ0KICBkaXN0YW5jZV92ZWMgPC0gYXBwZW5kKGRpc3RhbmNlX3ZlYywgMCwgYWZ0ZXIgPSBob3NwIC0gMSkNCiAgcHJpbnQoZGlzdGFuY2VfdmVjKQ0KICBkaXN0X21hdHJpeCA8LSBjKGRpc3RfbWF0cml4LCBkaXN0YW5jZV92ZWMpDQogICNwcmludChob3NwKQ0KfQ0KZGlzdF9tYXRyaXggPC0gbWF0cml4KGRpc3RfbWF0cml4LCBuY29sID0gbnJvdyhob3NwX2Nvb3JkaW5hdGVzKSwgYnlyb3cgPSBUUlVFKQ0KYGBgDQoNCiMjIyBBbGdvcml0bW8gR2VuZXRpY28gcGFyYSBvYnRlbmVyIGxhIHJ1dGEgbWFzIGNvcnRhDQoNCiMjIyMgRGVmaW5pY2lvbiBkZSBmdW5jaW9uZXMNCmBgYHtyfQ0KIyBkaXN0YW5jaWEgdG90YWwgZGUgdG9kYSBsYSBydXRhDQpkaXN0YW5jZV9yb3V0ZSA8LSBmdW5jdGlvbihyb3V0ZSxkaXN0YW5jZSl7DQogIHN1bV9kaXN0YW5jZTwtMA0KICBmb3IoaSBpbiAxOmxlbmd0aChyb3V0ZVstMV0pKXsNCiAgICBvdXQgPC0gZGlzdGFuY2Vbcm91dGVbaV0scm91dGVbaSsxXSBdDQogICAgc3VtX2Rpc3RhbmNlIDwtIHN1bV9kaXN0YW5jZSArIG91dA0KICB9DQogIHJldHVybihzdW1fZGlzdGFuY2UpDQp9DQoNCiMgZ2VuZXJhIHVuYSBydXRhIGFsZWF0b3JpYQ0Kc2FtcGxlX3JvdXRlIDwtIGZ1bmN0aW9uKC4uLiwgY2l0aWVzKXsNCiAgYygxLHNhbXBsZSgyOmNpdGllcyksMSkgJT4lIHJldHVybigpDQp9DQoNCiMgU2VsZWNjaW9uYSBsb3MgcGFkcmVzIGVuIGJhc2UgYSBsYSBkaXN0YW5jaWEgKHJvdWxldHRlKQ0Kc2VsZWN0X21hdGluZ19wYXJlbnRzIDwtIGZ1bmN0aW9uKC4uLixwb3Bfc2l6ZSwgcm91bGxldGUsIHBvcHVsYXRpb24pew0KICBzdW1fZml0X3AgPC0NCiAgICBzYW1wbGUoMTpzdW0ocm91bGxldGUkcmFuayksIHNpemUgPSAxICkNCiAgcGluZGV4IDwtDQogICAgcm91bGxldGUgJT4lIA0KICAgIGZpbHRlcihjdW1zdW1fcmFuayA8IHN1bV9maXRfcCkgJT4lDQogICAgbnJvdygpDQogIHAxIDwtIHJvdWxsZXRlW3BpbmRleCArIDEsXSAlPiUgcHVsbChwYXJlbnQpDQogIHN1bV9maXRfcCA8LQ0KICAgIHNhbXBsZSgxOnN1bShyb3VsbGV0ZSRyYW5rKSxzaXplID0gMSApDQogIHBpbmRleCA8LQ0KICAgIHJvdWxsZXRlICU+JSANCiAgICBmaWx0ZXIoY3Vtc3VtX3JhbmsgPCBzdW1fZml0X3ApICU+JQ0KICAgIG5yb3coKQ0KICBwMiA8LSByb3VsbGV0ZVtwaW5kZXggKyAxLF0gJT4lIHB1bGwocGFyZW50KQ0KICByZXR1cm4ocG9wdWxhdGlvbltjKHAxLHAyKV0pDQp9DQoNCiMgRnVuY2lvbiBwYXJhIGhhY2VyIGVsIGNyb3Nzb3ZlciBjb24gZG9zIHBhZHJlcywgc2UgdXNhIG1hdGVyaWFsIGdlbmV0aWNvIGRlIHVubyB5IGRlbCBvdHJvLg0KcG9zaXRpb25fYmFzZWRfY3Jvc3NvdmVyIDwtIGZ1bmN0aW9uKHBhcmVudHMsIG4gPSAzKXsNCiAgcDEgPC0gcGFyZW50c1tbMV1dDQogIHAyIDwtIHBhcmVudHNbWzJdXQ0KICBsZW5ndGhfcGFyZW50IDwtIGxlbmd0aChwMSkNCiAgcDEgPC0gcDFbMjoobGVuZ3RoX3BhcmVudC0xKV0NCiAgcDIgPC0gcDJbMjoobGVuZ3RoX3BhcmVudC0xKV0NCiAgaW5kZXggPC0gc2FtcGxlKDE6KGxlbmd0aF9wYXJlbnQtMiksIHNpemUgPSBuLCByZXBsYWNlID0gRkFMU0UpDQogIGNoaWxkMSA8LSBwMQ0KICBmaWxsX3ZhbHVlcyA8LSBzZXRkaWZmKHAyLCBwMVtpbmRleF0pDQogIGNoaWxkMVstaW5kZXhdIDwtIGZpbGxfdmFsdWVzDQogIHJldHVybihjKDEsIGNoaWxkMSwgMSkpIA0KfQ0KDQojQ2FtYmlhIGRlIHBvc2ljaW9uIGRvcyBjaXVkYWRlcywgdW5hIHBvciBsYSBvdHJhLg0Kc3dhcCA8LSBmdW5jdGlvbih2ZWMsIGksIGopew0KICBvdXQgPC0gdmVjDQogIG91dFtpXSA8LSB2ZWNbal0NCiAgb3V0W2pdIDwtIHZlY1tpXQ0KICByZXR1cm4ob3V0KQ0KfQ0KDQojIEZ1bmNpb24gcGFyYSBtdXRhciBsb3MgaGlqb3MNCm11dGF0ZV9jaGlsZCA8LSBmdW5jdGlvbihjaGlsZCwgcmF0ZSA9IDAuMDMpew0KICBpZihydW5pZigxKSA8IHJhdGUpew0KICAgIGluZGV4IDwtIDI6KGxlbmd0aChjaGlsZCkgLSAxKQ0KICAgIGkgPC0gc2FtcGxlKGluZGV4LCBzaXplID0gMSkNCiAgICBpbmRleCA8LSBzZXRkaWZmKGluZGV4LGkpDQogICAgaiA8LSBzYW1wbGUoaW5kZXgsIHNpemUgPSAxKQ0KICAgIGNoaWxkIDwtIHN3YXAoY2hpbGQsIGksIGopDQogICAgI3ByaW50KDEpDQogIH0NCiAgcmV0dXJuKGNoaWxkKQ0KfQ0KYGBgDQoNCg0KT2J0ZW5pZW5kbyBsYSBydXRhIG1hcyBjb3J0YQ0KYGBge3J9DQpwb3B1bGF0aW9uIDwtIGxhcHBseSgxOjEwMCwgc2FtcGxlX3JvdXRlLCBjaXRpZXMgPSBucm93KGhvc3BfY29vcmRpbmF0ZXMpKQ0KDQojIEZPUiBQQVJBIE9CVEVORVIgTEEgUlVUQSBNQVMgQ09SVEEuDQpmb3IoaSBpbiAxOjEwMCl7DQogIHBvcF9maXRuZXNzIDwtIGxhcHBseShwb3B1bGF0aW9uLCBkaXN0YW5jZV9yb3V0ZSwgZGlzdGFuY2UgPSBkaXN0X21hdHJpeCkNCiAgDQogIHJvdWxsZXRlIDwtDQogICAgdGliYmxlKHBhcmVudCA9IDE6MTAwLCBmaXRuZXNzID0gcG9wX2ZpdG5lc3MgJT4lIHVubGlzdCgpKSAlPiUNCiAgICBhcnJhbmdlKGRlc2MoZml0bmVzcykpDQogIA0KICByb3VsbGV0ZSRyYW5rIDwtIDE6bnJvdyhyb3VsbGV0ZSkNCiAgDQogIHJvdWxsZXRlIDwtDQogICAgcm91bGxldGUgJT4lIA0KICAgIG11dGF0ZShjdW1zdW1fcmFuayA9IGN1bXN1bShyYW5rKSkNCiAgDQogIG1hdGluZ19wYXJlbnRzIDwtDQogICAgbGFwcGx5KDE6MTAwLCBzZWxlY3RfbWF0aW5nX3BhcmVudHMsDQogICAgICAgICAgIHBvcF9zaXplID0gMTAwLA0KICAgICAgICAgICByb3VsbGV0ZSA9IHJvdWxsZXRlLCANCiAgICAgICAgICAgcG9wdWxhdGlvbiA9IHBvcHVsYXRpb24pDQogIA0KICBjaGlsZHJlbiA8LSBsYXBwbHkobWF0aW5nX3BhcmVudHMsIHBvc2l0aW9uX2Jhc2VkX2Nyb3Nzb3ZlciwgbiA9IDUpDQogIA0KICBwb3B1bGF0aW9uIDwtIGxhcHBseShjaGlsZHJlbiwgbXV0YXRlX2NoaWxkLCByYXRlID0gMC4xKQ0KICAjcHJpbnQocG9wX2ZpdG5lc3NbW3doaWNoLm1pbihwb3BfZml0bmVzcyAlPiUgdW5saXN0KCkpXV0pDQp9DQoNCm1pbl9yb3V0ZSA8LSBwb3B1bGF0aW9uW3doaWNoLm1pbihwb3BfZml0bmVzcyAlPiUgdW5saXN0KCkpXQ0KbWluX3JvdXRlW1sxXV0NCmBgYA0KDQojIyMgR3JhZmljYXIgbGEgcnV0YSBtYXMgY29ydGENCmBgYHtyfQ0KdXJsX3JvdXRlMSA8LSAiaHR0cHM6Ly9tYXBzLmdvb2dsZWFwaXMuY29tL21hcHMvYXBpL2RpcmVjdGlvbnMvanNvbj9vcmlnaW49Ig0KdXJsX3JvdXRlMiA8LSAiJmRlc3RpbmF0aW9uPSINCg0KIyBPQlRFTkVSIExPUyBQT0xZTElORVMNCnBvbHlfbGluZXMgPC0gZGF0YS5mcmFtZSgpDQoNCmZvcihob3NwIGluIDE6bGVuZ3RoKG1pbl9yb3V0ZVtbMV1dKSl7DQogIGlmKGhvc3AgPT0gbGVuZ3RoKG1pbl9yb3V0ZVtbMV1dKSl7DQogICAgb3JpZ2luIDwtIGhvc3BfY29vcmRpbmF0ZXNbbWluX3JvdXRlW1sxXV1baG9zcF0sXQ0KICAgIGRlc3RpbmF0aW9uIDwtIGhvc3BfY29vcmRpbmF0ZXNbbWluX3JvdXRlW1sxXV1bMV0sXQ0KICAgICNwcmludChob3NwKQ0KICB9ZWxzZXsNCiAgICAjcHJpbnQoaG9zcCkNCiAgICBvcmlnaW4gPC0gaG9zcF9jb29yZGluYXRlc1ttaW5fcm91dGVbWzFdXVtob3NwXSxdDQogICAgZGVzdGluYXRpb24gPC0gaG9zcF9jb29yZGluYXRlc1ttaW5fcm91dGVbWzFdXVtob3NwKzFdLF0NCiAgfQ0KICB1cmwgPC0gcGFzdGUwKHVybF9yb3V0ZTEsDQogICAgICAgICAgICAgICAgb3JpZ2luJGNvb3JkaW5hdGUsDQogICAgICAgICAgICAgICAgdXJsX3JvdXRlMiwNCiAgICAgICAgICAgICAgICBwYXN0ZTAoZGVzdGluYXRpb24kY29vcmRpbmF0ZSksDQogICAgICAgICAgICAgICAgYXBpX2tleSxjb2xsYXBzZSA9ICIiKQ0KICANCiAgcmVzcG9uc2UgPC0gR0VUKHVybCkNCiAgcmVzcF9qc29uIDwtIGZyb21KU09OKGNvbnRlbnQocmVzcG9uc2UsIGFzID0gInRleHQiKSkNCiAgcnV0YSA8LSByZXNwX2pzb24kcm91dGVzJG92ZXJ2aWV3X3BvbHlsaW5lJHBvaW50cw0KICBydXRhIDwtIGRlY29kZVBvbHlsaW5lKHJ1dGEpDQogIGF1eF9kZiA8LSBydXRhDQogIHBvbHlfbGluZXMgPC0gcmJpbmQocG9seV9saW5lcywgYXV4X2RmKQ0KfQ0KYGBgDQoNCiMjIyBHcmFmaWNhciBsYSBydXRhIG1hcyBjb3J0YQ0KYGBge3J9DQpxbWFwKCdFc3RhY2lvbiBUaXZvbGknLHpvb20gPSAxMywgbWFwdHlwZT0icm9hZG1hcCIpICsNCiAgZ2VvbV9wb2ludChhZXMoeCA9IGxvbiwgeSA9IGxhdCksIA0KICAgICAgICAgICAgIGRhdGEgPSBwb2x5X2xpbmVzLCANCiAgICAgICAgICAgICBjb2xvdXIgPSAicmVkIiApDQoNCmBgYA0KDQojIyMgQWxnb3JpdG1vIFNpbXVsYXRlZCBBbm5lYWxpbmcNCmBgYHtyfQ0KIyBHZW5lcmFtb3MgdW5hIHJ1dGEgYWwgYXphciANCnNpbXVsYXRlZF9yb3V0ZSA8LSBzYW1wbGVfcm91dGUoY2l0aWVzID0gbnJvdyhob3NwX2Nvb3JkaW5hdGVzKSkNCg0KIyBEaXN0YW5jaWEgcXVlIGhheSBxdWUgcmVjb3JyZXIgZW4gZXNhIHJ1dGENCmRpc3RfYWN0dWFsIDwtIGRpc3RhbmNlX3JvdXRlKHNpbXVsYXRlZF9yb3V0ZSwgZGlzdF9tYXRyaXgpDQoNCnRlbXBlcmF0dXJlIDwtIDIwDQpkZWx0YV90ZW1wIDwtIC0wLjAxDQoNCmZvciAoaSBpbiBzZXEoZnJvbSA9IHRlbXBlcmF0dXJlLCB0byA9IDAsIGJ5ID0gZGVsdGFfdGVtcCkpew0KICBkaXN0X2FudGVyaW9yIDwtIGRpc3RfYWN0dWFsDQogICMgcXVpdGFyZW1vcyBlbCBwdW50byBxdWUgZXN0YSBmaWpvIGVuIGxhIHJ1dGE6IGVsIEFlcm9wdWVydG8gKHB1bnRvIGRlIGluaWNpbyB5IGZpbikNCiAgc2ltdWxhdGVkX3JvdXRlIDwtIHNpbXVsYXRlZF9yb3V0ZVsyOihsZW5ndGgoc2ltdWxhdGVkX3JvdXRlKS0xKV0NCiAgI2NhbWJpbyBlbiBsYXMgY2l1ZGFkZXMNCiAgc3dhcF9pbmRleCA8LSBzYW1wbGUoMToobGVuZ3RoKHNpbXVsYXRlZF9yb3V0ZSkpLCAyLCByZXBsYWNlID0gRkFMU0UpDQogIHNpbXVsYXRlZF9yb3V0ZV8yIDwtIGMoMSxzd2FwKHNpbXVsYXRlZF9yb3V0ZSwgc3dhcF9pbmRleFsxXSxzd2FwX2luZGV4WzJdKSwxKQ0KICBkaXN0X2FjdHVhbCA8LSBkaXN0YW5jZV9yb3V0ZShzaW11bGF0ZWRfcm91dGVfMiwgZGlzdF9tYXRyaXgpDQogIA0KICBkZWx0YV9kaXN0IDwtIGRpc3RfYW50ZXJpb3IgLSBkaXN0X2FjdHVhbA0KICBpZihkZWx0YV9kaXN0ID4gMCl7DQogICAgc2ltdWxhdGVkX3JvdXRlIDwtIHNpbXVsYXRlZF9yb3V0ZV8yDQogICAgI3ByaW50KCJjYW1iaW8iKQ0KICB9IGVsc2UgaWYoZXhwKGRlbHRhX2Rpc3QvdGVtcGVyYXR1cmUpID4gcnVuaWYoMSwgMCwgMSkpew0KICAgIHNpbXVsYXRlZF9yb3V0ZSA8LSBzaW11bGF0ZWRfcm91dGVfMg0KICAgICNwcmludCgicHJvYmFiaWxpZGFkIikNCiAgfWVsc2Ugew0KICAgIHNpbXVsYXRlZF9yb3V0ZSA8LSBjKDEsIHNpbXVsYXRlZF9yb3V0ZSwgMSkNCiAgICAjcHJpbnQoIk5vIGNhbWJpbyIpDQogIH0gDQp9DQoNCmRpc3RhbmNpYV9vYnRlbmlkYSA8LSBkaXN0YW5jZV9yb3V0ZShzaW11bGF0ZWRfcm91dGUsIGRpc3RfbWF0cml4KQ0KZGlzdGFuY2lhX29idGVuaWRhDQpgYGANCg0KIyMjIE9idGVuZXIgbG9zIHBvbHlsaW5lcw0KYGBge3J9DQpwb2x5X2xpbmVzX1NBIDwtIGRhdGEuZnJhbWUoKQ0KDQpmb3IoaG9zcCBpbiAxOmxlbmd0aChzaW11bGF0ZWRfcm91dGUpKXsNCiAgaWYoaG9zcCA9PSBsZW5ndGgoc2ltdWxhdGVkX3JvdXRlKSl7DQogICAgb3JpZ2luIDwtIGhvc3BfY29vcmRpbmF0ZXNbc2ltdWxhdGVkX3JvdXRlW2hvc3BdLF0NCiAgICBkZXN0aW5hdGlvbiA8LSBob3NwX2Nvb3JkaW5hdGVzW3NpbXVsYXRlZF9yb3V0ZVsxXSxdDQogICAgI3ByaW50KGhvc3ApDQogIH1lbHNlew0KICAgICNwcmludChob3NwKQ0KICAgIG9yaWdpbiA8LSBob3NwX2Nvb3JkaW5hdGVzW3NpbXVsYXRlZF9yb3V0ZVtob3NwXSxdDQogICAgZGVzdGluYXRpb24gPC0gaG9zcF9jb29yZGluYXRlc1tzaW11bGF0ZWRfcm91dGVbaG9zcCsxXSxdDQogIH0NCiAgdXJsIDwtIHBhc3RlMCh1cmxfcm91dGUxLA0KICAgICAgICAgICAgICAgIG9yaWdpbiRjb29yZGluYXRlLA0KICAgICAgICAgICAgICAgIHVybF9yb3V0ZTIsDQogICAgICAgICAgICAgICAgcGFzdGUwKGRlc3RpbmF0aW9uJGNvb3JkaW5hdGUpLA0KICAgICAgICAgICAgICAgIGFwaV9rZXksY29sbGFwc2UgPSAiIikNCiAgDQogIHJlc3BvbnNlIDwtIEdFVCh1cmwpDQogIHJlc3BfanNvbiA8LSBmcm9tSlNPTihjb250ZW50KHJlc3BvbnNlLCBhcyA9ICJ0ZXh0IikpDQogIHJ1dGFfU0EgPC0gcmVzcF9qc29uJHJvdXRlcyRvdmVydmlld19wb2x5bGluZSRwb2ludHMNCiAgcnV0YV9TQSA8LSBkZWNvZGVQb2x5bGluZShydXRhX1NBKQ0KICBhdXhfZGYgPC0gcnV0YV9TQQ0KICBwb2x5X2xpbmVzX1NBIDwtIHJiaW5kKHBvbHlfbGluZXNfU0EsIGF1eF9kZikNCn0NCmBgYA0KDQogR3JhZmljYXIgbGEgcnV0YSBlbmNvbnRyYWRhIHBvciBlbCBhbGdvcml0bW8gU2ltdWxhdGVkIEFubmVhbGluZw0KYGBge3J9DQpxbWFwKCdFc3RhY2lvbiBUaXZvbGknLHpvb20gPSAxMywgbWFwdHlwZT0icm9hZG1hcCIpICsNCiAgZ2VvbV9wb2ludChhZXMoeCA9IGxvbiwgeSA9IGxhdCksIA0KICAgICAgICAgICAgIGRhdGEgPSBwb2x5X2xpbmVzX1NBLCANCiAgICAgICAgICAgICBjb2xvdXIgPSAiYmx1ZSIgKQ0KYGBgDQoNCg0K