
Indice:
-
Obtención de datos de demanda eléctrica: Uso de api de Red Eléctrica.
-
Diagrama de Calor: Comparativa de la demanda por hora diaria del 1º Trimestre del periodo 2015-2018
-
Serie Temporal: Representación de la demanda durante el periodo 2014-2018 (hora de ejemplo: 22.00)
-
Diagrama Polar: Representación del rango de la demanda diaria durante el año 2017
El operador del sistema eléctrico en España Red Eléctrica,proporciona desde su página información de la demanda de energía eléctrica en el mercado español y además pone a disposición de terceros,de forma libre estos datos a traves de su api.La documentación para el consumo de ésta se encuentra disponible aquí.Para poder utilizarla es necesario tener un token personal que puede solicitarse por correo en la dirección consultasios@ree.es.
Mi interés es poder crear un modelo de predicción de la demanda agregada en España del consumo eléctrico por hora. Como primer paso, necesito recuperar un set de datos con la serie histórica de los últimos años que me sirva de entrenamiento y lo haré por medio de este api. He obtenido un token personal para su uso y me apoyaré en el paquete requests de python para realizar la gestión de la conexión http y la descarga de sus datos.
OBTENCIÓN DE DATOS DE DEMANDA DE ENERGIA ELECTRICA
Los pasos que seguiré son los siguientes:
-
Descargaré los identificadores de servicio.Haré una petición http a la direccion “https://api.esios.ree.es/indicators” para obtener la lista (json) de servicios - indicadores - con los que cuenta la api. Usaré el método get() del paquete requests para realizar la llamada donde por parámetro pasaré la dirección y el token personal.
-
Buscaré los identificadores de Demanda real y previsión mensual de la demanda eléctrica. Esta último me proporcionará un benchmark entre la predicción pública y la que pueda desarrollar personalmente.
-
Crearé las fechas del periodo de datos que quiero estudiar inicialmente.Cuatros años desde la fecha actual.
-
Haré una segunda llamada al api, esta vez para obtener los datos reales de consumo en el periodo y para ello compondré la dirección https://api.esios.ree.es/indicators/%s?start_date=%sT22:00:00Z&end_date=%sT21:00:00Z&time_trunc=hour con los parámetros id del servicio y la fecha de inicio y fin de la petición.
-
Por último compondré dos dataframe con la respuesta de cada uno de los indicadores.
A continuación el código en python desarrollado para obtener los datos:
Los identifcadores para nuestros servicios son:
-
Demanda real: 1293
-
Prevision mensual de la demanda eléctrica peninsular: 461
y la fecha de nuestro periodo de estudio:
-
fecha de inicio: 2014-07-29
-
fecha fin: 2018-07-27
Recuperada la información deseada,inicio una primer exploración de los datos. Para ello voy a utilizar el paquete highcharter en r que proporciona un wrapper muy sencillo de la librería highchart en javascript.
############### Datos de demanda real
library(dplyr)
library(highcharter)
library(htmltools)
library(xts)
demandaReal<-read.csv('Datos_Demanda_Real.csv',sep=';',stringsAsFactors = F)
diagramaPolar<-function(df,anno){
df<-df[substr(df$Fecha,1,4)==anno, c('Fecha','Valor')]
demandaReal<-df %>% group_by(Fecha) %>%summarise('Max'=max(Valor),'Min'=min(Valor),'Mean'=mean(Valor))
demandaReal$Fecha<-as.Date(demandaReal$Fecha)
demandaReal$Mean<-round(demandaReal$Mean)
x <- c("Min", "Mean", "Max")
y <- sprintf("{point.%s}", c("Min", "Mean", "Max"))
tltip <- tooltip_table(x, y)
maxL<-mean(demandaReal$Max)
p<-hchart(demandaReal, type = "columnrange",
hcaes(x = Fecha, low = Min, high = Max,
color = Mean)) %>%
hc_chart(polar = TRUE) %>%
hc_yAxis( max = maxL+10000, min =10000, labels = list(format = "{value} GWh"),
showFirstLabel = FALSE) %>%
hc_xAxis(
title = list(text = ''), gridLineWidth = 0.5,
labels = list(format = "{value: %b}")) %>%
hc_tooltip(useHTML = TRUE, pointFormat = tltip,
headerFormat = as.character(tags$small("{point.x:%d %B, %Y}"))) %>%
hc_title(text=paste0('Digrama anual de la demanda real de energía en España durante el ','Año: ','- ',anno,' -')) %>%
hc_subtitle(text='Valores Máximos ,Mínimos y Medios diarios')
return(p)
}
dp<-diagramaPolar(demandaReal,'2017')
################################
serieHora<-function(df,HORA){
demandaReal<-df[substr(df$Hora,1,2)==HORA,c('Fecha','Valor')]
demandaReal$Fecha<-as.Date(demandaReal$Fecha)
demandaReal<-xts(demandaReal$Valor,order.by =demandaReal$Fecha )
hc <- highchart(type = "stock") %>%
hc_title(text = "Diagrama de la demanda real de energía en España durante los años 2014-2018") %>%
hc_subtitle(text = paste0("La serie contiene la demanda agregada a las ",HORA,' horas')) %>%
hc_add_series(demandaReal)
return(hc)
}
ser<-serieHora(demandaReal,'22')
#############################
mapaCalor<-function(df,Trimestre, Anno){
demandaReal2017<-df[substr(df$Fecha,1,4)==Anno,c('Fecha','Hora','Valor')]
demandaReal2017$Hora<-substr(demandaReal2017$Hora,1,2)
demandaReal2017$Hora<-as.integer(demandaReal2017$Hora)
demandaReal2017$Dia<-as.POSIXlt(demandaReal2017$Fecha)$yday
M<-matrix(rep(0,24*365),nrow=24,ncol=365,byrow = T)
for(i in (0:23)){
for(j in (1:365)){
#print(paste0(i,' ',j))
M[i+1,j]=demandaReal2017[(demandaReal2017$Dia==j) & (demandaReal2017$Hora==i),'Valor'][1]
}
}
if(Trimestre==1){
ini=1
fin=90
}else if (Trimestre==2){
ini=91
fin=180
}else if (Trimestre==3){
ini=181
fin=20
}else{
ini=271
fin=365
}
s<-hchart(M[c(1:24),c(ini:fin)]) %>%
hc_colorAxis(stops = color_stops(colors = viridis::inferno(10))) %>%
hc_title(text = "Diagrama de calor de la demanda real agregada de energía en España") %>%
hc_subtitle(text = paste0(Trimestre,'º Tirmestre',' del ', Anno, '/ Columna: Hora, Fila= Dia secuencial del año'))
return(s)
}
c1<-mapaCalor(demandaReal,1,'2015')
c2<-mapaCalor(demandaReal,1,'2016')
c3<-mapaCalor(demandaReal,1,'2017')
c4<-mapaCalor(demandaReal,1,'2018')
lst <- list(
c1,
c2,
c3,
c4
)
s<-hw_grid(lst, rowheight = 600) %>% browsable()
s
ser
dp
Esta visualizacion forma parte del estudio y modelo de predicción de la demanda real eléctrica en el mercado español
LS0tCm91dHB1dDoKICBodG1sX25vdGVib29rOiBkZWZhdWx0CiAgaHRtbF9kb2N1bWVudDogZGVmYXVsdAotLS0KPGltZyBzcmM9J2NhYmVjZXJhLnBuZyc+Cgo8ZGl2IHN0eWxlPSJiYWNrZ3JvdW5kLWNvbG9yOiNGQkVGRjIiPgo8YnI+Cgo8YnI+CjxoMj48Y2VudGVyPkluZGljZTo8L2NlbnRlcj48L2gyPgo8YnI+Cgo8dWw+CjxsaT48Yj5PYnRlbmNpw7NuIGRlIGRhdG9zIGRlIGRlbWFuZGEgZWzDqWN0cmljYTo8L2I+IFVzbyBkZSBhcGkgZGUgUmVkIEVsw6ljdHJpY2EuPC9saT4KPGxpPjxiPkRpYWdyYW1hIGRlIENhbG9yPC9iPjogQ29tcGFyYXRpdmEgZGUgbGEgZGVtYW5kYSBwb3IgaG9yYSBkaWFyaWEgZGVsIDHCuiBUcmltZXN0cmUgZGVsIHBlcmlvZG8gMjAxNS0yMDE4IDwvbGk+CjxsaT48Yj5TZXJpZSBUZW1wb3JhbDwvYj46IFJlcHJlc2VudGFjacOzbiBkZSBsYSBkZW1hbmRhIGR1cmFudGUgZWwgcGVyaW9kbyAyMDE0LTIwMTggKGhvcmEgZGUgZWplbXBsbzogMjIuMDApPC9saT4KPGxpPjxiPkRpYWdyYW1hIFBvbGFyPC9iPjogUmVwcmVzZW50YWNpw7NuIGRlbCByYW5nbyBkZSBsYSBkZW1hbmRhIGRpYXJpYSBkdXJhbnRlIGVsIGHDsW8gMjAxNzwvbGk+CjwvdWw+Cgo8YnI+Cjxicj4KPC9kaXY+Cjxocj4KPGJyPgogIEVsIG9wZXJhZG9yIGRlbCBzaXN0ZW1hIGVsw6ljdHJpY28gZW4gRXNwYcOxYSA8Yj5SZWQgRWzDqWN0cmljYTwvYj4scHJvcG9yY2lvbmEgZGVzZGUgc3UgPGEgaHJlZj0iaHR0cHM6Ly93d3cuZXNpb3MucmVlLmVzL2VzIj5ww6FnaW5hPC9hPiBpbmZvcm1hY2nDs24gZGUgbGEgZGVtYW5kYSBkZSBlbmVyZ8OtYSBlbMOpY3RyaWNhIGVuIGVsIG1lcmNhZG8gZXNwYcOxb2wgeSBhZGVtw6FzIHBvbmUgYSBkaXNwb3NpY2nDs24gZGUgdGVyY2Vyb3MsZGUgZm9ybWEgbGlicmUgZXN0b3MgZGF0b3MgYSB0cmF2ZXMgZGUgc3UgYXBpLkxhIGRvY3VtZW50YWNpw7NuIHBhcmEgZWwgY29uc3VtbyBkZSDDqXN0YSBzZSBlbmN1ZW50cmEgZGlzcG9uaWJsZSA8YSBocmVmPSdodHRwczovL2FwaS5lc2lvcy5yZWUuZXMnPmFxdcOtLjwvYT5QYXJhIHBvZGVyIHV0aWxpemFybGEgZXMgbmVjZXNhcmlvIHRlbmVyIHVuIHRva2VuIHBlcnNvbmFsIHF1ZSBwdWVkZSBzb2xpY2l0YXJzZSBwb3IgY29ycmVvIGVuIGxhIGRpcmVjY2nDs24gY29uc3VsdGFzaW9zQHJlZS5lcy4KCiAgTWkgaW50ZXLDqXMgZXMgcG9kZXIgY3JlYXIgdW4gbW9kZWxvIGRlIHByZWRpY2Npw7NuIGRlIGxhIGRlbWFuZGEgYWdyZWdhZGEgZW4gRXNwYcOxYSBkZWwgY29uc3VtbyBlbMOpY3RyaWNvIHBvciBob3JhLiBDb21vIHByaW1lciBwYXNvLCBuZWNlc2l0byByZWN1cGVyYXIgdW4gc2V0IGRlIGRhdG9zIGNvbiBsYSBzZXJpZSBoaXN0w7NyaWNhIGRlIGxvcyDDumx0aW1vcyBhw7FvcyBxdWUgbWUgc2lydmEgZGUgZW50cmVuYW1pZW50byB5IGxvIGhhcsOpIHBvciBtZWRpbyBkZSBlc3RlIGFwaS4gSGUgb2J0ZW5pZG8gdW4gdG9rZW4gcGVyc29uYWwgcGFyYSBzdSB1c28geSBtZSBhcG95YXLDqSBlbiBlbCBwYXF1ZXRlIDxiPnJlcXVlc3RzPC9iPiBkZSA8Yj5weXRob248L2I+IHBhcmEgcmVhbGl6YXIgbGEgZ2VzdGnDs24gZGUgbGEgY29uZXhpw7NuIDxpPmh0dHA8L2k+IHkgbGEgZGVzY2FyZ2EgZGUgc3VzIGRhdG9zLgogIAo8YnI+Cjxicj4KPGRpdiBzdHlsZT0iYmFja2dyb3VuZC1jb2xvcjojRjhFQ0UwIj4KPGJyPgogIDxoMz48Y2VudGVyPk9CVEVOQ0nDk04gREUgREFUT1MgREUgREVNQU5EQSBERSBFTkVSR0lBIEVMRUNUUklDQTwvY2VudGVyPjwvaDM+Cjxicj4KCjwvZGl2PiAgCiAgCiAgTG9zIHBhc29zIHF1ZSBzZWd1aXLDqSBzb24gbG9zIHNpZ3VpZW50ZXM6CiAgPGJyPgogIDxicj4KICA8dWw+CiAgPGxpPkRlc2NhcmdhcsOpIGxvcyBpZGVudGlmaWNhZG9yZXMgZGUgc2VydmljaW8uSGFyw6kgdW5hIHBldGljacOzbiBodHRwICBhIGxhIGRpcmVjY2lvbiAiaHR0cHM6Ly9hcGkuZXNpb3MucmVlLmVzL2luZGljYXRvcnMiIHBhcmEgb2J0ZW5lciBsYSBsaXN0YSAoanNvbikgZGUgc2VydmljaW9zIC0gaW5kaWNhZG9yZXMgLSBjb24gbG9zIHF1ZSBjdWVudGEgbGEgYXBpLiBVc2Fyw6kgIGVsIG3DqXRvZG8gPGI+Z2V0KCk8L2I+IGRlbCBwYXF1ZXRlIDxiPnJlcXVlc3RzPC9iPiBwYXJhIHJlYWxpemFyIGxhIGxsYW1hZGEgZG9uZGUgcG9yIHBhcsOhbWV0cm8gcGFzYXLDqSBsYSBkaXJlY2Npw7NuIHkgZWwgdG9rZW4gcGVyc29uYWwuPC9saT4KICA8bGk+QnVzY2Fyw6kgbG9zIGlkZW50aWZpY2Fkb3JlcyBkZSA8Yj5EZW1hbmRhIHJlYWw8L2I+IHkgPGI+cHJldmlzacOzbiBtZW5zdWFsIGRlIGxhIGRlbWFuZGEgZWzDqWN0cmljYTwvYj4uIEVzdGEgw7psdGltbyBtZSBwcm9wb3JjaW9uYXLDoSAgdW4gYmVuY2htYXJrIGVudHJlIGxhIHByZWRpY2Npw7NuIHDDumJsaWNhIHkgbGEgcXVlIHB1ZWRhIGRlc2Fycm9sbGFyIHBlcnNvbmFsbWVudGUuPC9saT4KICA8bGk+Q3JlYXLDqSAgbGFzIGZlY2hhcyBkZWwgcGVyaW9kbyBkZSBkYXRvcyBxdWUgcXVpZXJvIGVzdHVkaWFyIGluaWNpYWxtZW50ZS5DdWF0cm9zIGHDsW9zIGRlc2RlIGxhIGZlY2hhIGFjdHVhbC48L2xpPgogIDxsaT5IYXLDqSAgdW5hIHNlZ3VuZGEgbGxhbWFkYSBhbCBhcGksIGVzdGEgdmV6IHBhcmEgb2J0ZW5lciBsb3MgZGF0b3MgcmVhbGVzIGRlIGNvbnN1bW8gZW4gZWwgcGVyaW9kbyB5IHBhcmEgZWxsbyBjb21wb25kcsOpICBsYSBkaXJlY2Npw7NuIGh0dHBzOi8vYXBpLmVzaW9zLnJlZS5lcy9pbmRpY2F0b3JzLyVzP3N0YXJ0X2RhdGU9JXNUMjI6MDA6MDBaJmVuZF9kYXRlPSVzVDIxOjAwOjAwWiZ0aW1lX3RydW5jPWhvdXIgY29uIGxvcyBwYXLDoW1ldHJvcyBpZCBkZWwgc2VydmljaW8geSBsYSBmZWNoYSBkZSBpbmljaW8geSBmaW4gZGUgbGEgcGV0aWNpw7NuLjwvbGk+CiAgPGxpPlBvciDDumx0aW1vIGNvbXBvbmRyw6kgIGRvcyA8aT5kYXRhZnJhbWU8L2k+IGNvbiBsYSByZXNwdWVzdGEgZGUgY2FkYSB1bm8gZGUgbG9zIGluZGljYWRvcmVzLjwvbGk+CiAgPC91bD4KCjxicj4KQSBjb250aW51YWNpw7NuIGVsIGPDs2RpZ28gZW4gcHl0aG9uIGRlc2Fycm9sbGFkbyBwYXJhIG9idGVuZXIgbG9zIGRhdG9zOgo8YnI+Cjxicj4KPGltZyBzcmM9J2NvZDEucG5nJz4KPGJyPgo8YnI+CkxvcyBpZGVudGlmY2Fkb3JlcyBwYXJhIG51ZXN0cm9zIHNlcnZpY2lvcyBzb246Cjx1bD4KPGxpPkRlbWFuZGEgcmVhbDogMTI5MzwvbGk+CjxsaT5QcmV2aXNpb24gbWVuc3VhbCBkZSBsYSBkZW1hbmRhIGVsw6ljdHJpY2EgcGVuaW5zdWxhcjogNDYxPC9saT4KPC91bD4KeSBsYSBmZWNoYSBkZSBudWVzdHJvIHBlcmlvZG8gZGUgZXN0dWRpbzoKPHVsPgo8bGk+ZmVjaGEgZGUgaW5pY2lvOiAyMDE0LTA3LTI5PC9saT4KPGxpPmZlY2hhIGZpbjogMjAxOC0wNy0yNzwvbGk+CjwvdWw+Cjxicj4KPGJyPgo8aW1nIHNyYz0nY29kMi5wbmcnPgo8YnI+Cjxicj4KUmVjdXBlcmFkYSBsYSBpbmZvcm1hY2nDs24gZGVzZWFkYSxpbmljaW8gdW5hIHByaW1lciBleHBsb3JhY2nDs24gZGUgbG9zIGRhdG9zLiBQYXJhIGVsbG8gdm95IGEgdXRpbGl6YXIgZWwgcGFxdWV0ZSA8Yj5oaWdoY2hhcnRlcjwvYj4gZW4gPGI+cjwvYj4gcXVlIHByb3BvcmNpb25hIHVuIDxpPndyYXBwZXI8L2k+IG11eSBzZW5jaWxsbyBkZSBsYSBsaWJyZXLDrWEgPGk+aGlnaGNoYXJ0PC9pPiBlbiBqYXZhc2NyaXB0Lgo8YnI+Cjxicj4KYGBge3J9CiMjIyMjIyMjIyMjIyMjIyBEYXRvcyBkZSBkZW1hbmRhIHJlYWwKbGlicmFyeShkcGx5cikKbGlicmFyeShoaWdoY2hhcnRlcikKbGlicmFyeShodG1sdG9vbHMpCmxpYnJhcnkoeHRzKQoKZGVtYW5kYVJlYWw8LXJlYWQuY3N2KCdEYXRvc19EZW1hbmRhX1JlYWwuY3N2JyxzZXA9JzsnLHN0cmluZ3NBc0ZhY3RvcnMgPSBGKQoKZGlhZ3JhbWFQb2xhcjwtZnVuY3Rpb24oZGYsYW5ubyl7CiAgZGY8LWRmW3N1YnN0cihkZiRGZWNoYSwxLDQpPT1hbm5vLCBjKCdGZWNoYScsJ1ZhbG9yJyldCiAgZGVtYW5kYVJlYWw8LWRmICU+JSBncm91cF9ieShGZWNoYSkgJT4lc3VtbWFyaXNlKCdNYXgnPW1heChWYWxvciksJ01pbic9bWluKFZhbG9yKSwnTWVhbic9bWVhbihWYWxvcikpCiAgZGVtYW5kYVJlYWwkRmVjaGE8LWFzLkRhdGUoZGVtYW5kYVJlYWwkRmVjaGEpCiAgZGVtYW5kYVJlYWwkTWVhbjwtcm91bmQoZGVtYW5kYVJlYWwkTWVhbikKICB4IDwtIGMoIk1pbiIsICJNZWFuIiwgIk1heCIpCiAgeSA8LSBzcHJpbnRmKCJ7cG9pbnQuJXN9IiwgYygiTWluIiwgIk1lYW4iLCAiTWF4IikpCiAgdGx0aXAgPC0gdG9vbHRpcF90YWJsZSh4LCB5KQoKICBtYXhMPC1tZWFuKGRlbWFuZGFSZWFsJE1heCkKICAKICBwPC1oY2hhcnQoZGVtYW5kYVJlYWwsIHR5cGUgPSAiY29sdW1ucmFuZ2UiLAogICAgICAgICAgICBoY2Flcyh4ID0gRmVjaGEsIGxvdyA9IE1pbiwgaGlnaCA9IE1heCwKICAgICAgICAgICAgICAgICAgY29sb3IgPSBNZWFuKSkgJT4lIAogICAgaGNfY2hhcnQocG9sYXIgPSBUUlVFKSAlPiUKICAgIGhjX3lBeGlzKCBtYXggPSBtYXhMKzEwMDAwLCBtaW4gPTEwMDAwLCBsYWJlbHMgPSBsaXN0KGZvcm1hdCA9ICJ7dmFsdWV9IEdXaCIpLAogICAgICAgICAgICAgIHNob3dGaXJzdExhYmVsID0gRkFMU0UpICU+JSAKICAgIGhjX3hBeGlzKAogICAgICB0aXRsZSA9IGxpc3QodGV4dCA9ICcnKSwgZ3JpZExpbmVXaWR0aCA9IDAuNSwKICAgICAgbGFiZWxzID0gbGlzdChmb3JtYXQgPSAie3ZhbHVlOiAlYn0iKSkgJT4lIAogICAgaGNfdG9vbHRpcCh1c2VIVE1MID0gVFJVRSwgcG9pbnRGb3JtYXQgPSB0bHRpcCwKICAgICAgICAgICAgICAgaGVhZGVyRm9ybWF0ID0gYXMuY2hhcmFjdGVyKHRhZ3Mkc21hbGwoIntwb2ludC54OiVkICVCLCAlWX0iKSkpICAlPiUKICAgIGhjX3RpdGxlKHRleHQ9cGFzdGUwKCdEaWdyYW1hIGFudWFsIGRlIGxhIGRlbWFuZGEgcmVhbCBkZSBlbmVyZ8OtYSBlbiBFc3Bhw7FhIGR1cmFudGUgZWwgJywnQcOxbzogJywnLSAnLGFubm8sJyAtJykpICAlPiUKICAgIGhjX3N1YnRpdGxlKHRleHQ9J1ZhbG9yZXMgTcOheGltb3MgLE3DrW5pbW9zIHkgTWVkaW9zIGRpYXJpb3MnKQogIHJldHVybihwKQogIAp9CgpkcDwtZGlhZ3JhbWFQb2xhcihkZW1hbmRhUmVhbCwnMjAxNycpCgojIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIwoKc2VyaWVIb3JhPC1mdW5jdGlvbihkZixIT1JBKXsKICBkZW1hbmRhUmVhbDwtZGZbc3Vic3RyKGRmJEhvcmEsMSwyKT09SE9SQSxjKCdGZWNoYScsJ1ZhbG9yJyldCiAgZGVtYW5kYVJlYWwkRmVjaGE8LWFzLkRhdGUoZGVtYW5kYVJlYWwkRmVjaGEpCiAgZGVtYW5kYVJlYWw8LXh0cyhkZW1hbmRhUmVhbCRWYWxvcixvcmRlci5ieSA9ZGVtYW5kYVJlYWwkRmVjaGEgKQoKICBoYyA8LSBoaWdoY2hhcnQodHlwZSA9ICJzdG9jayIpICU+JSAKICAgaGNfdGl0bGUodGV4dCA9ICJEaWFncmFtYSBkZSBsYSBkZW1hbmRhIHJlYWwgZGUgZW5lcmfDrWEgZW4gRXNwYcOxYSBkdXJhbnRlIGxvcyBhw7FvcyAyMDE0LTIwMTgiKSAlPiUgCiAgICBoY19zdWJ0aXRsZSh0ZXh0ID0gcGFzdGUwKCJMYSBzZXJpZSBjb250aWVuZSBsYSBkZW1hbmRhIGFncmVnYWRhIGEgbGFzICIsSE9SQSwnIGhvcmFzJykpICU+JSAKICAgIGhjX2FkZF9zZXJpZXMoZGVtYW5kYVJlYWwpIAoKICByZXR1cm4oaGMpCn0KCnNlcjwtc2VyaWVIb3JhKGRlbWFuZGFSZWFsLCcyMicpCgojIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIwoKbWFwYUNhbG9yPC1mdW5jdGlvbihkZixUcmltZXN0cmUsIEFubm8pewogIGRlbWFuZGFSZWFsMjAxNzwtZGZbc3Vic3RyKGRmJEZlY2hhLDEsNCk9PUFubm8sYygnRmVjaGEnLCdIb3JhJywnVmFsb3InKV0KICBkZW1hbmRhUmVhbDIwMTckSG9yYTwtc3Vic3RyKGRlbWFuZGFSZWFsMjAxNyRIb3JhLDEsMikKICBkZW1hbmRhUmVhbDIwMTckSG9yYTwtYXMuaW50ZWdlcihkZW1hbmRhUmVhbDIwMTckSG9yYSkKICBkZW1hbmRhUmVhbDIwMTckRGlhPC1hcy5QT1NJWGx0KGRlbWFuZGFSZWFsMjAxNyRGZWNoYSkkeWRheQogIAogIE08LW1hdHJpeChyZXAoMCwyNCozNjUpLG5yb3c9MjQsbmNvbD0zNjUsYnlyb3cgPSBUKQogIAogIGZvcihpIGluICgwOjIzKSl7CiAgICBmb3IoaiBpbiAoMTozNjUpKXsKICAgICAgI3ByaW50KHBhc3RlMChpLCcgJyxqKSkKICAgICAgTVtpKzEsal09ZGVtYW5kYVJlYWwyMDE3WyhkZW1hbmRhUmVhbDIwMTckRGlhPT1qKSAmIChkZW1hbmRhUmVhbDIwMTckSG9yYT09aSksJ1ZhbG9yJ11bMV0KICAgIH0KICB9CiAgaWYoVHJpbWVzdHJlPT0xKXsKICAgIGluaT0xCiAgICBmaW49OTAKICB9ZWxzZSBpZiAoVHJpbWVzdHJlPT0yKXsKICAgIGluaT05MQogICAgZmluPTE4MAogIH1lbHNlIGlmIChUcmltZXN0cmU9PTMpewogICAgaW5pPTE4MQogICAgZmluPTIwCiAgfWVsc2V7CiAgICBpbmk9MjcxCiAgICBmaW49MzY1CiAgfSAgCiAgICAKICAgIAogIHM8LWhjaGFydChNW2MoMToyNCksYyhpbmk6ZmluKV0pICU+JSAKICAgIGhjX2NvbG9yQXhpcyhzdG9wcyA9IGNvbG9yX3N0b3BzKGNvbG9ycyA9IHZpcmlkaXM6OmluZmVybm8oMTApKSkgJT4lCiAgICBoY190aXRsZSh0ZXh0ID0gIkRpYWdyYW1hIGRlIGNhbG9yIGRlIGxhIGRlbWFuZGEgcmVhbCBhZ3JlZ2FkYSBkZSBlbmVyZ8OtYSBlbiBFc3Bhw7FhIikgJT4lIAogICAgaGNfc3VidGl0bGUodGV4dCA9IHBhc3RlMChUcmltZXN0cmUsJ8K6IFRpcm1lc3RyZScsJyBkZWwgJywgQW5ubywgJy8gQ29sdW1uYTogSG9yYSwgRmlsYT0gRGlhIHNlY3VlbmNpYWwgZGVsIGHDsW8nKSkKICByZXR1cm4ocykKfQoKCmMxPC1tYXBhQ2Fsb3IoZGVtYW5kYVJlYWwsMSwnMjAxNScpCmMyPC1tYXBhQ2Fsb3IoZGVtYW5kYVJlYWwsMSwnMjAxNicpCmMzPC1tYXBhQ2Fsb3IoZGVtYW5kYVJlYWwsMSwnMjAxNycpCmM0PC1tYXBhQ2Fsb3IoZGVtYW5kYVJlYWwsMSwnMjAxOCcpCgoKbHN0IDwtIGxpc3QoCiAgYzEsCiAgYzIsCiAgYzMsCiAgYzQKKQoKczwtaHdfZ3JpZChsc3QsIHJvd2hlaWdodCA9IDYwMCkgICU+JSBicm93c2FibGUoKQpgYGAKCjxicj4KPGJyPgo8YnI+CjxkaXYgc3R5bGU9ImJhY2tncm91bmQtY29sb3I6I0Y4RUNFMCI+Cjxicj4KICA8aDM+PGNlbnRlcj5ESUFHUkFNQSBERSBDQUxPUjwvY2VudGVyPjwvaDM+Cjxicj4KCjwvZGl2PgoKCgoKYGBge3J9CnMKYGBgCgo8YnI+Cjxicj4KPGJyPgoKPGRpdiBzdHlsZT0iYmFja2dyb3VuZC1jb2xvcjojRjhFQ0UwIj4KPGJyPgogIDxoMz48Y2VudGVyPlNFUklFIFRFTVBPUkFMPC9jZW50ZXI+PC9oMz4KPGJyPgoKPC9kaXY+CgoKYGBge3J9CnNlcgpgYGAKPGJyPgo8YnI+Cjxicj4KCjxkaXYgc3R5bGU9ImJhY2tncm91bmQtY29sb3I6I0Y4RUNFMCI+Cjxicj4KICA8aDM+PGNlbnRlcj5ESUFHUkFNQSBQT0xBUjwvY2VudGVyPjwvaDM+Cjxicj4KCjwvZGl2PgpgYGB7cn0KZHAKYGBgCjxicj4KPGJyPgo8aHI+CjxkaXYgc3R5bGU9ImJhY2tncm91bmQtY29sb3I6I0Y4RUNFMCI+Cjxicj4KICA8cD48Y2VudGVyPkVzdGEgdmlzdWFsaXphY2lvbiBmb3JtYSBwYXJ0ZSBkZWwgZXN0dWRpbyB5IG1vZGVsbyBkZSBwcmVkaWNjacOzbiBkZSBsYSBkZW1hbmRhIHJlYWwgZWzDqWN0cmljYSBlbiBlbCBtZXJjYWRvIGVzcGHDsW9sPC9pPjwvY2VudGVyPjwvcD4KPGJyPgoKPC9kaXY+Cjxicj4KPGJyPgo=