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 = [32mcol_double()[39m,
lat = [32mcol_double()[39m,
lon = [32mcol_double()[39m,
hospital = [31mcol_character()[39m
)
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