El paquete por excelencia para trabajar con fechas y horas es
{lubridate} que forma parte del mundo de tidyverse como (casi) todos los
paquetes que usamos hasta ahora. Si normalmente usas
library(tidyverse)
para cargar los paquetes, tené en cuenta
que {lubridate} no se carga automáticamente. Así que arranquemos por lo
primero.
library(lubridate)
library(dplyr)
library(readr)
Creando fechas y horas
R maneja fechas y fechas y horas y para esto tiene los tipos de datos
<date>
y <dttm>
y si bien
internamente esos datos se almacenan como números enteros (por ejemplo
cantidad de segundos desde el 1 de enero de 1970) con lubridate casi no
tenemos que preocuparnos de eso y podemos trabajar con fechas como seres
humanos.
Es posible que al leer bases de datos algunas columnas con este tipo
de información sea leída como cadena de caracteres, por ejemplo
“25-Oct-1990”. Esto se puede solucionar tanto en la lectura de los datos
como luego usando lubridate. Veamos lo segundo.
Vamos a trabajar con una base de datos de reportes de eventos severos
reportados por personas durante la campaña RELAMPAGO que se
llevó a cabo en Argentina entre octubre y diciembre de 2018.
Los reportes se recolectaron a través de un formulario de google que
entre otros datos pedía: el tipo de evento o eventos observados, el
lugar y horario de ocurrencia del evento.
reportes <- read_csv("https://raw.githubusercontent.com/paocorrales/r4ds-notas/master/examen/eventos_severos_RELAMPAGO.csv")
## Rows: 1421 Columns: 4
## ── Column specification ────────────────────────────────────────────────────────
## Delimiter: ","
## chr (2): fecha, evento
## dbl (2): lat, lon
##
## ℹ Use `spec()` to retrieve the full column specification for this data.
## ℹ Specify the column types or set `show_col_types = FALSE` to quiet this message.
glimpse(reportes) # ¿qué pinta tiene?
## Rows: 1,421
## Columns: 4
## $ fecha <chr> "10/29/2018 22:05:00", "10/30/2018 13:00:00", "10/30/2018 10:00…
## $ lat <dbl> -33.11956, -32.95214, -32.85000, -33.27861, -31.39905, -31.7598…
## $ lon <dbl> -64.32797, -60.76837, -61.25000, -65.61417, -64.33477, -65.0016…
## $ evento <chr> "Ráfagas y/o vientos intensos", "Rayos y/o truenos", "Granizo",…
Tip: las funciones read_*()
puede leer
bases de datos desde urls! en este caso es un archivo guardado en un
repositorio de GitHub.
La base de datos tiene 4 variables, la fecha (que R no reconoció como
tal), la ubicación en latitud y longitud y el tipo de evento.
Arreglemos la variable fecha. Para eso usaremos una familia de
funciones de lubridate que convierten una cadena de caracteres o números
en fechas y fechas y horas.
En este caso el formato de la fecha en la base de datos es “mes / día
/ año hora : minutos : segundos” y conociendo el orden de las
componentes podemos elegir la función necesaria. En este caso será
mdy_hms()
que viene justamente de month,
day, year, hour,
minute y second, las componentes en
inglés.
reportes <- reportes %>%
mutate(fecha_convertida = mdy_hms(fecha))
reportes %>%
pull(fecha_convertida) %>% # Necesitamos extraer el vector para ver la zona horaria.
.[1] # Extraemos el primer elemento
## [1] "2018-10-29 22:05:00 UTC"
Por defecto cualquiera de las funciones de la familia
ymd_hms()
le asigna la zona horaria UTC a la variable de
salida. Nos vamos a ocupar de esto más adelante pero es importante
conocer los metadatos de esta base de datos, ¿las horas estarán en hora
local o en UTC?
Desafío:
- Revisá la documentación de la familia de funciones
ymd_hms()
y las funciones ymd()
par ver que
combinaciones posibles existen.
- ¿Cual de todas las funciones usarías si tenés que convertir a fecha
y hora los siguientes elementos:
- “2018-11-22 12:00:00”
- 15092020
- “10/31/1998”
- 20181122150000
Si en vez de tener la información de la fecha u fecha y hora en una
sola columna, la tenemos en distintas columnas (el mes por un lado, el
día por el otro, ect.), existen un par de funciones que permiten armar
la variable: make_date()
y make_datetime()
. Si
quisiéramos armar la fecha 25 de octubre de 1990 tendríamos que usar la
siguiente línea de código:
make_date(year = 1990, month = 10, day = 25)
## [1] "1990-10-25"
O una fecha y hora, por ejemplo el 14 de agosto de 1988 a las 13
horas (por ahora UTC, ya nos meteremos en ese berenjenal al final de
este episodio).
make_datetime(year = 1988, month = 8, day = 14, hour = 13)
## [1] "1988-08-14 13:00:00 UTC"
Por supuesto podríamos guardar esas fechas en variables o usar las
funciones dentro de mutate()
para generar columnas nuevas
en un data frame.
Sus componentes
Ahora que sabemos como armar fechas, es posible que te estés
preguntando como desarmarlas o como extraer sus componentes. Algo de
esto ya hicimos al graficar temperaturas medías mensuales pero vamos a
revisarlo.
{lubridate} tiene toda una familia de funciones que permiten extraer
las componentes de una variable fecha u fecha y hora. Acá la clave es
recordar los nombres en inglés (no nos queda otra!), por ejemplo si
queremos el mes, la función será month()
.
Veamos como funciona.
reportes %>%
mutate(mes = month(fecha_convertida),
dia = day(fecha_convertida))
## # A tibble: 1,421 × 7
## fecha lat lon evento fecha_convertida mes dia
## <chr> <dbl> <dbl> <chr> <dttm> <dbl> <int>
## 1 10/29/2018 22:05:00 -33.1 -64.3 Ráfagas y/o … 2018-10-29 22:05:00 10 29
## 2 10/30/2018 13:00:00 -33.0 -60.8 Rayos y/o tr… 2018-10-30 13:00:00 10 30
## 3 10/30/2018 10:00:00 -32.8 -61.2 Granizo 2018-10-30 10:00:00 10 30
## 4 10/29/2018 19:00:00 -33.3 -65.6 Granizo 2018-10-29 19:00:00 10 29
## 5 10/30/2018 12:50:00 -31.4 -64.3 Granizo 2018-10-30 12:50:00 10 30
## 6 11/3/2018 10:35:00 -31.8 -65.0 Rayos y/o tr… 2018-11-03 10:35:00 11 3
## 7 10/29/2018 22:00:00 -33.1 -64.3 Ráfagas y/o … 2018-10-29 22:00:00 10 29
## 8 11/3/2018 13:45:00 -31.4 -64.6 Lluvias inte… 2018-11-03 13:45:00 11 3
## 9 11/3/2018 14:37:00 -31.1 -64.3 Rayos y/o tr… 2018-11-03 14:37:00 11 3
## 10 11/3/2018 14:10:00 -31.2 -64.5 Granizo 2018-11-03 14:10:00 11 3
## # … with 1,411 more rows
En particular la componente día tiene toda una subfamilia de
funciones para extraer el día del año, el día del mes o el día de la
semana!
Desafío
- Extraé el día de la semana generando una nueva columna en la base de
datos
reportes
que se llame dia_semana
. (Psss!
la función se llama wday()
).
- Ahora intentalo de nuevo pero cambiando el argumento label a
TRUE
. ¿Qué ocurre? ¿Cambia el tipo de datos?
Zonas horarias
Cuando trabajamos con datos temporales y específicamente aquellos que
tienen fecha y hora una de las consideraciones que tenemos que tener en
cuenta es la zona horaria. Por defecto {lubridate} y la ciencia en
general trabaja en hora UTC (o Coordinated Universal Time) pero
es posible que nuestros datos estén almacenados en una zona horaria
totalmente distinta.
En este caso, los reportes tienen la hora a la que ocurrió el evento
en Argentina. Por esto podemos inferir que la zona horaria es
-3. Esto significa 3 horas menos respecto de la hora
UTC. ¿Cómo le avisamos a R?
Todas las funciones de la familia ymd_hms()
tiene un
argumento tz
que permite cambiar la zona horaria. El truco
está en saber la nomenclatura de zonas horarias.
head(OlsonNames()) # Lista de las más de 600 zonas horarias disponibles
## [1] "Africa/Abidjan" "Africa/Accra" "Africa/Addis_Ababa"
## [4] "Africa/Algiers" "Africa/Asmara" "Africa/Asmera"
Sys.timezone() # ¿En qué zona horaria está mi computadora?
## [1] "America/Argentina/Buenos_Aires"
Podemos usar esa información en el argumento tz
.
reportes <- reportes %>%
mutate(fecha_convertida_tz = mdy_hms(fecha, tz = "America/Argentina/Buenos_Aires"))
reportes %>%
pull(fecha_convertida_tz) %>% # Necesitamos extraer el vector para ver la zona horaria.
.[1]
## [1] "2018-10-29 22:05:00 -03"
LS0tCnRpdGxlOiAiTWFuaXB1bGFuZG8gZmVjaGFzIgpvdXRwdXQ6IAogIGh0bWxfZG9jdW1lbnQ6CiAgICBjb2RlX2Rvd25sb2FkOiB0cnVlCiAgICB0b2M6IHRydWUKICAgIHRvY19mbG9hdDogdHJ1ZQogICAgaGlnaGxpZ2h0OiB0YW5nbyAKLS0tCgpgYGB7ciBzZXR1cCwgaW5jbHVkZT1GQUxTRX0Ka25pdHI6Om9wdHNfY2h1bmskc2V0KGVjaG8gPSBUUlVFKQpgYGAKCkVsIHBhcXVldGUgcG9yIGV4Y2VsZW5jaWEgcGFyYSB0cmFiYWphciBjb24gZmVjaGFzIHkgaG9yYXMgZXMge2x1YnJpZGF0ZX0gcXVlIGZvcm1hIHBhcnRlIGRlbCBtdW5kbyBkZSB0aWR5dmVyc2UgY29tbyAoY2FzaSkgdG9kb3MgbG9zIHBhcXVldGVzIHF1ZSB1c2Ftb3MgaGFzdGEgYWhvcmEuIFNpIG5vcm1hbG1lbnRlIHVzYXMgYGxpYnJhcnkodGlkeXZlcnNlKWAgcGFyYSBjYXJnYXIgbG9zIHBhcXVldGVzLCB0ZW7DqSBlbiBjdWVudGEgcXVlIHtsdWJyaWRhdGV9IG5vIHNlIGNhcmdhIGF1dG9tw6F0aWNhbWVudGUuIEFzw60gcXVlIGFycmFucXVlbW9zIHBvciBsbyBwcmltZXJvLgoKYGBge3IgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRX0KbGlicmFyeShsdWJyaWRhdGUpCmxpYnJhcnkoZHBseXIpCmxpYnJhcnkocmVhZHIpCmBgYAoKCiMjIENyZWFuZG8gZmVjaGFzIHkgaG9yYXMKClIgbWFuZWphIGZlY2hhcyB5IGZlY2hhcyB5IGhvcmFzIHkgcGFyYSBlc3RvIHRpZW5lIGxvcyB0aXBvcyBkZSBkYXRvcyBgPGRhdGU+YCB5IGA8ZHR0bT5gIHkgc2kgYmllbiBpbnRlcm5hbWVudGUgZXNvcyBkYXRvcyBzZSBhbG1hY2VuYW4gY29tbyBuw7ptZXJvcyBlbnRlcm9zIChwb3IgZWplbXBsbyBjYW50aWRhZCBkZSBzZWd1bmRvcyBkZXNkZSBlbCAxIGRlIGVuZXJvIGRlIDE5NzApIGNvbiBsdWJyaWRhdGUgY2FzaSBubyB0ZW5lbW9zIHF1ZSBwcmVvY3VwYXJub3MgZGUgZXNvIHkgcG9kZW1vcyB0cmFiYWphciBjb24gZmVjaGFzIGNvbW8gc2VyZXMgaHVtYW5vcy4gCgpFcyBwb3NpYmxlIHF1ZSBhbCBsZWVyIGJhc2VzIGRlIGRhdG9zIGFsZ3VuYXMgY29sdW1uYXMgY29uIGVzdGUgdGlwbyBkZSBpbmZvcm1hY2nDs24gc2VhIGxlw61kYSBjb21vIGNhZGVuYSBkZSBjYXJhY3RlcmVzLCBwb3IgZWplbXBsbyAiMjUtT2N0LTE5OTAiLiBFc3RvIHNlIHB1ZWRlIHNvbHVjaW9uYXIgdGFudG8gZW4gbGEgbGVjdHVyYSBkZSBsb3MgZGF0b3MgY29tbyBsdWVnbyB1c2FuZG8gbHVicmlkYXRlLiBWZWFtb3MgbG8gc2VndW5kby4KCgpWYW1vcyBhIHRyYWJhamFyIGNvbiB1bmEgYmFzZSBkZSBkYXRvcyBkZSByZXBvcnRlcyBkZSBldmVudG9zIHNldmVyb3MgcmVwb3J0YWRvcyBwb3IgcGVyc29uYXMgZHVyYW50ZSBsYSAgY2FtcGHDsWEgW1JFTEFNUEFHT10oaHR0cHM6Ly93d3cuYmJjLmNvbS9tdW5kby9ub3RpY2lhcy00NjEzMzMzMCkgcXVlIHNlIGxsZXbDsyBhIGNhYm8gZW4gQXJnZW50aW5hIGVudHJlIG9jdHVicmUgeSBkaWNpZW1icmUgZGUgMjAxOC4gCgpMb3MgcmVwb3J0ZXMgc2UgcmVjb2xlY3Rhcm9uIGEgdHJhdsOpcyBkZSB1biBmb3JtdWxhcmlvIGRlIGdvb2dsZSBxdWUgZW50cmUgb3Ryb3MgZGF0b3MgcGVkw61hOiBlbCB0aXBvIGRlIGV2ZW50byBvIGV2ZW50b3Mgb2JzZXJ2YWRvcywgZWwgbHVnYXIgeSBob3JhcmlvIGRlIG9jdXJyZW5jaWEgZGVsIGV2ZW50by4gCgpgYGB7ciBkYXRvc30KcmVwb3J0ZXMgPC0gcmVhZF9jc3YoImh0dHBzOi8vcmF3LmdpdGh1YnVzZXJjb250ZW50LmNvbS9wYW9jb3JyYWxlcy9yNGRzLW5vdGFzL21hc3Rlci9leGFtZW4vZXZlbnRvc19zZXZlcm9zX1JFTEFNUEFHTy5jc3YiKQoKZ2xpbXBzZShyZXBvcnRlcykgIyDCv3F1w6kgcGludGEgdGllbmU/CmBgYAoKOjo6ey5hbGVydCAuYWxlcnQtc3VjY2Vzc30KCioqVGlwOioqIGxhcyBmdW5jaW9uZXMgYHJlYWRfKigpYCBwdWVkZSBsZWVyIGJhc2VzIGRlIGRhdG9zIGRlc2RlIHVybHMhIGVuIGVzdGUgY2FzbyBlcyB1biBhcmNoaXZvIGd1YXJkYWRvIGVuIHVuIHJlcG9zaXRvcmlvIGRlIEdpdEh1Yi4KOjo6CgpMYSBiYXNlIGRlIGRhdG9zIHRpZW5lIDQgdmFyaWFibGVzLCBsYSBmZWNoYSAocXVlIFIgbm8gcmVjb25vY2nDsyBjb21vIHRhbCksIGxhIHViaWNhY2nDs24gZW4gbGF0aXR1ZCB5IGxvbmdpdHVkIHkgZWwgdGlwbyBkZSBldmVudG8uIAoKQXJyZWdsZW1vcyBsYSB2YXJpYWJsZSBmZWNoYS4gUGFyYSBlc28gdXNhcmVtb3MgdW5hIGZhbWlsaWEgZGUgZnVuY2lvbmVzIGRlIGx1YnJpZGF0ZSBxdWUgY29udmllcnRlbiB1bmEgY2FkZW5hIGRlIGNhcmFjdGVyZXMgbyBuw7ptZXJvcyBlbiBmZWNoYXMgeSBmZWNoYXMgeSBob3Jhcy4gCgpFbiBlc3RlIGNhc28gZWwgZm9ybWF0byBkZSBsYSBmZWNoYSBlbiBsYSBiYXNlIGRlIGRhdG9zIGVzICJtZXMgLyBkw61hIC8gYcOxbyBob3JhIDogbWludXRvcyA6IHNlZ3VuZG9zIiB5ICBjb25vY2llbmRvIGVsIG9yZGVuIGRlIGxhcyBjb21wb25lbnRlcyBwb2RlbW9zIGVsZWdpciBsYSBmdW5jacOzbiBuZWNlc2FyaWEuIEVuIGVzdGUgY2FzbyBzZXLDoSBgbWR5X2htcygpYCBxdWUgdmllbmUganVzdGFtZW50ZSBkZSAqKm0qKm9udGgsICoqZCoqYXksICoqeSoqZWFyLCAqKmgqKm91ciwgKiptKippbnV0ZSB5ICoqcyoqZWNvbmQsIGxhcyBjb21wb25lbnRlcyBlbiBpbmdsw6lzLiAKCmBgYHtyIHBhcnNlX2ZlY2hhfQpyZXBvcnRlcyA8LSByZXBvcnRlcyAlPiUgCiAgbXV0YXRlKGZlY2hhX2NvbnZlcnRpZGEgPSBtZHlfaG1zKGZlY2hhKSkgCgpyZXBvcnRlcyAlPiUgCiAgcHVsbChmZWNoYV9jb252ZXJ0aWRhKSAlPiUgIyBOZWNlc2l0YW1vcyBleHRyYWVyIGVsIHZlY3RvciBwYXJhIHZlciBsYSB6b25hIGhvcmFyaWEuCiAgLlsxXSAgICAgICAgICAgICAgICAgICAgICAgIyBFeHRyYWVtb3MgZWwgcHJpbWVyIGVsZW1lbnRvIApgYGAKClBvciBkZWZlY3RvIGN1YWxxdWllcmEgZGUgbGFzIGZ1bmNpb25lcyBkZSBsYSBmYW1pbGlhIGB5bWRfaG1zKClgIGxlIGFzaWduYSBsYSB6b25hIGhvcmFyaWEgVVRDIGEgbGEgdmFyaWFibGUgZGUgc2FsaWRhLiBOb3MgdmFtb3MgYSBvY3VwYXIgZGUgZXN0byBtw6FzIGFkZWxhbnRlIHBlcm8gZXMgaW1wb3J0YW50ZSBjb25vY2VyIGxvcyBtZXRhZGF0b3MgZGUgZXN0YSBiYXNlIGRlIGRhdG9zLCDCv2xhcyBob3JhcyBlc3RhcsOhbiBlbiBob3JhIGxvY2FsIG8gZW4gVVRDPwoKOjo6ey5hbGVydCAuYWxlcnQtaW5mb30KCioqRGVzYWbDrW86KioKCjEuIFJldmlzw6EgbGEgZG9jdW1lbnRhY2nDs24gZGUgbGEgZmFtaWxpYSBkZSBmdW5jaW9uZXMgYHltZF9obXMoKWAgeSBsYXMgZnVuY2lvbmVzIGB5bWQoKWAgcGFyIHZlciBxdWUgY29tYmluYWNpb25lcyBwb3NpYmxlcyBleGlzdGVuLgoyLiDCv0N1YWwgZGUgdG9kYXMgbGFzIGZ1bmNpb25lcyB1c2Fyw61hcyBzaSB0ZW7DqXMgcXVlIGNvbnZlcnRpciBhIGZlY2hhIHkgaG9yYSBsb3Mgc2lndWllbnRlcyBlbGVtZW50b3M6CiAgLSAiMjAxOC0xMS0yMiAxMjowMDowMCIKICAtIDE1MDkyMDIwCiAgLSAiMTAvMzEvMTk5OCIKICAtIDIwMTgxMTIyMTUwMDAwCjo6OgoKClNpIGVuIHZleiBkZSB0ZW5lciBsYSBpbmZvcm1hY2nDs24gZGUgbGEgZmVjaGEgdSBmZWNoYSB5IGhvcmEgZW4gdW5hIHNvbGEgY29sdW1uYSwgbGEgdGVuZW1vcyBlbiBkaXN0aW50YXMgY29sdW1uYXMgKGVsIG1lcyBwb3IgdW4gbGFkbywgZWwgZMOtYSBwb3IgZWwgb3RybywgZWN0LiksIGV4aXN0ZW4gdW4gcGFyIGRlIGZ1bmNpb25lcyBxdWUgcGVybWl0ZW4gYXJtYXIgbGEgdmFyaWFibGU6IGBtYWtlX2RhdGUoKWAgeSBgbWFrZV9kYXRldGltZSgpYC4gU2kgcXVpc2nDqXJhbW9zIGFybWFyIGxhIGZlY2hhIDI1IGRlIG9jdHVicmUgZGUgMTk5MCB0ZW5kcsOtYW1vcyBxdWUgdXNhciBsYSBzaWd1aWVudGUgbMOtbmVhIGRlIGPDs2RpZ286CgpgYGB7cn0KbWFrZV9kYXRlKHllYXIgPSAxOTkwLCBtb250aCA9IDEwLCBkYXkgPSAyNSkKYGBgCgpPIHVuYSBmZWNoYSB5IGhvcmEsIHBvciBlamVtcGxvIGVsIDE0IGRlIGFnb3N0byBkZSAxOTg4IGEgbGFzIDEzIGhvcmFzIChwb3IgYWhvcmEgVVRDLCB5YSBub3MgbWV0ZXJlbW9zIGVuIGVzZSBiZXJlbmplbmFsIGFsIGZpbmFsIGRlIGVzdGUgZXBpc29kaW8pLgoKYGBge3J9Cm1ha2VfZGF0ZXRpbWUoeWVhciA9IDE5ODgsIG1vbnRoID0gOCwgZGF5ID0gMTQsIGhvdXIgPSAxMykKYGBgCgoKUG9yIHN1cHVlc3RvIHBvZHLDrWFtb3MgZ3VhcmRhciBlc2FzIGZlY2hhcyBlbiB2YXJpYWJsZXMgbyB1c2FyIGxhcyBmdW5jaW9uZXMgZGVudHJvIGRlIGBtdXRhdGUoKWAgcGFyYSBnZW5lcmFyIGNvbHVtbmFzIG51ZXZhcyBlbiB1biBkYXRhIGZyYW1lLgoKIyMgU3VzIGNvbXBvbmVudGVzCgpBaG9yYSBxdWUgc2FiZW1vcyBjb21vIGFybWFyIGZlY2hhcywgZXMgcG9zaWJsZSBxdWUgdGUgZXN0w6lzIHByZWd1bnRhbmRvIGNvbW8gZGVzYXJtYXJsYXMgbyBjb21vIGV4dHJhZXIgc3VzIGNvbXBvbmVudGVzLiBBbGdvIGRlIGVzdG8geWEgaGljaW1vcyBhbCBncmFmaWNhciB0ZW1wZXJhdHVyYXMgbWVkw61hcyBtZW5zdWFsZXMgcGVybyB2YW1vcyBhIHJldmlzYXJsby4KCntsdWJyaWRhdGV9IHRpZW5lIHRvZGEgdW5hIGZhbWlsaWEgZGUgZnVuY2lvbmVzIHF1ZSBwZXJtaXRlbiBleHRyYWVyIGxhcyBjb21wb25lbnRlcyBkZSB1bmEgdmFyaWFibGUgZmVjaGEgdSBmZWNoYSB5IGhvcmEuIEFjw6EgbGEgY2xhdmUgZXMgcmVjb3JkYXIgbG9zIG5vbWJyZXMgZW4gaW5nbMOpcyAobm8gbm9zIHF1ZWRhIG90cmEhKSwgcG9yIGVqZW1wbG8gc2kgcXVlcmVtb3MgZWwgbWVzLCBsYSBmdW5jacOzbiBzZXLDoSBgbW9udGgoKWAuCgpWZWFtb3MgY29tbyBmdW5jaW9uYS4KCmBgYHtyfQpyZXBvcnRlcyAlPiUgCiAgbXV0YXRlKG1lcyA9IG1vbnRoKGZlY2hhX2NvbnZlcnRpZGEpLAogICAgICAgICBkaWEgPSBkYXkoZmVjaGFfY29udmVydGlkYSkpIApgYGAKCkVuIHBhcnRpY3VsYXIgbGEgY29tcG9uZW50ZSBkw61hIHRpZW5lIHRvZGEgdW5hIHN1YmZhbWlsaWEgZGUgZnVuY2lvbmVzIHBhcmEgZXh0cmFlciBlbCBkw61hIGRlbCBhw7FvLCBlbCBkw61hIGRlbCBtZXMgbyBlbCBkw61hIGRlIGxhIHNlbWFuYSEKCjo6OnsuYWxlcnQgLmFsZXJ0LWluZm99CgoqKkRlc2Fmw61vKioKCjEuIEV4dHJhw6kgZWwgZMOtYSBkZSBsYSBzZW1hbmEgZ2VuZXJhbmRvIHVuYSBudWV2YSBjb2x1bW5hIGVuIGxhIGJhc2UgZGUgZGF0b3MgYHJlcG9ydGVzYCBxdWUgc2UgbGxhbWUgYGRpYV9zZW1hbmFgLiAoUHNzcyEgbGEgZnVuY2nDs24gc2UgbGxhbWEgYHdkYXkoKWApLgoyLiBBaG9yYSBpbnRlbnRhbG8gZGUgbnVldm8gcGVybyBjYW1iaWFuZG8gZWwgYXJndW1lbnRvIGxhYmVsIGEgYFRSVUVgLiDCv1F1w6kgb2N1cnJlPyDCv0NhbWJpYSBlbCB0aXBvIGRlIGRhdG9zPyAKOjo6CgojIyBab25hcyBob3JhcmlhcwoKQ3VhbmRvIHRyYWJhamFtb3MgY29uIGRhdG9zIHRlbXBvcmFsZXMgeSBlc3BlY8OtZmljYW1lbnRlIGFxdWVsbG9zIHF1ZSB0aWVuZW4gZmVjaGEgeSBob3JhIHVuYSBkZSBsYXMgY29uc2lkZXJhY2lvbmVzIHF1ZSB0ZW5lbW9zIHF1ZSB0ZW5lciBlbiBjdWVudGEgZXMgbGEgem9uYSBob3JhcmlhLiBQb3IgZGVmZWN0byB7bHVicmlkYXRlfSB5IGxhIGNpZW5jaWEgZW4gZ2VuZXJhbCB0cmFiYWphIGVuICpob3JhIFVUQyogKG8gQ29vcmRpbmF0ZWQgVW5pdmVyc2FsIFRpbWUpIHBlcm8gZXMgcG9zaWJsZSBxdWUgbnVlc3Ryb3MgZGF0b3MgZXN0w6luIGFsbWFjZW5hZG9zIGVuIHVuYSB6b25hIGhvcmFyaWEgdG90YWxtZW50ZSBkaXN0aW50YS4KCkVuIGVzdGUgY2FzbywgbG9zIHJlcG9ydGVzIHRpZW5lbiBsYSBob3JhIGEgbGEgcXVlIG9jdXJyacOzIGVsIGV2ZW50byBlbiBBcmdlbnRpbmEuIFBvciBlc3RvIHBvZGVtb3MgaW5mZXJpciBxdWUgbGEgem9uYSBob3JhcmlhIGVzICotMyouIEVzdG8gc2lnbmlmaWNhICozIGhvcmFzIG1lbm9zIHJlc3BlY3RvIGRlIGxhIGhvcmEgVVRDKi4gIMK/Q8OzbW8gbGUgYXZpc2Ftb3MgYSBSPwoKVG9kYXMgbGFzIGZ1bmNpb25lcyBkZSBsYSBmYW1pbGlhIGB5bWRfaG1zKClgIHRpZW5lIHVuIGFyZ3VtZW50byBgdHpgIHF1ZSBwZXJtaXRlIGNhbWJpYXIgbGEgem9uYSBob3JhcmlhLiBFbCB0cnVjbyBlc3TDoSBlbiBzYWJlciBsYSBub21lbmNsYXR1cmEgZGUgem9uYXMgaG9yYXJpYXMuCgpgYGB7cn0KaGVhZChPbHNvbk5hbWVzKCkpICMgTGlzdGEgZGUgbGFzIG3DoXMgZGUgNjAwIHpvbmFzIGhvcmFyaWFzIGRpc3BvbmlibGVzClN5cy50aW1lem9uZSgpICAgICAjIMK/RW4gcXXDqSB6b25hIGhvcmFyaWEgZXN0w6EgbWkgY29tcHV0YWRvcmE/CmBgYAoKUG9kZW1vcyB1c2FyIGVzYSBpbmZvcm1hY2nDs24gZW4gZWwgYXJndW1lbnRvIGB0emAuCgpgYGB7cn0KcmVwb3J0ZXMgPC0gcmVwb3J0ZXMgJT4lIAogIG11dGF0ZShmZWNoYV9jb252ZXJ0aWRhX3R6ID0gbWR5X2htcyhmZWNoYSwgdHogPSAiQW1lcmljYS9BcmdlbnRpbmEvQnVlbm9zX0FpcmVzIikpIAoKcmVwb3J0ZXMgJT4lIAogICBwdWxsKGZlY2hhX2NvbnZlcnRpZGFfdHopICU+JSAjIE5lY2VzaXRhbW9zIGV4dHJhZXIgZWwgdmVjdG9yIHBhcmEgdmVyIGxhIHpvbmEgaG9yYXJpYS4KICAuWzFdCmBgYAoKIyMjIFRyYW5mb3JtYW5kbyB6b25hcyBob3JhcmlhcwoKQWhvcmEgc2ksIFIgaW50ZXJwcmV0YSBlc3RhcyBmZWNoYXMgeSBob3JhcyBlbiBsYSB6b25hIGhvcmFyaWEgY29ycmVjdGEuIFNpbiBlbWJhcmdvLCBlbiBtZXRlb3JvbG9nw61hIGVzdGFtb3MgYWNvc3R1bWJyYWRvcyBhIGNvbXVuaWNhciBudWVzdHJvcyByZXN1bHRhZG9zIGVuIGhvcmEgKlVUQyogKHUgKmhvcmEgWiosIHF1ZSBlcyBsbyBtaXNtbyksIMK/Y8OzbW8gY29udmVydGltb3MgbGFzIGZlY2hhcyB5IGhvcmFzIGEgb3RyYSB6b25hIGhvcmFyaWE/CiAKe2x1YnJpZGF0ZX0gdGllbmUgZG9zIGZ1bmNpb25lcyBlc3BlY8OtZmljYXMgcGFyYSB0cmFiYWphciBjb24gem9uYXMgaG9yYXJpYXMuIAogCiogYGZvcmNlX3R6KClgIG1hbnRpZW5lIGNvbnN0YW50ZSBsYSBob3JhIHF1ZSB2ZW1vcyBlbiBlbCByZWxvaiBwZXJvIGNhbWJpYSBsYSB6b25hIGhvcmFyaWEuICAKIApgYGB7cn0KcmVwb3J0ZXMgPC0gcmVwb3J0ZXMgJT4lIAogIG11dGF0ZShmZWNoYV9mb3JjZV90eiA9IGZvcmNlX3R6KGZlY2hhX2NvbnZlcnRpZGFfdHosIHR6b25lID0gIlVUQyIpKSAKCnJlcG9ydGVzICU+JSAKICAgcHVsbChmZWNoYV9mb3JjZV90eikgJT4lICMgTmVjZXNpdGFtb3MgZXh0cmFlciBlbCB2ZWN0b3IgcGFyYSB2ZXIgbGEgem9uYSBob3JhcmlhLgogIC5bMV0KYGBgCgpFc3RvIG5vIHJlc3VlbHZlIG51ZXN0cm8gcHJvYmxlbWEsIHNpIHF1ZXJlbW9zIGV4cHJlc2FyICIyMDE4LTEwLTMwIDEyOjUwOjAwIC0zIiBlbiBob3JhIFVUQyBoYXkgcXVlICpzdW1hcmxlKiAzIGhvcmFzLgogCiogYHdpdGhfdHooKWAgbm9zIHBlcm1pdGUgY2FtYmlhciBkZSB6b25hIGhvcmFyaWEgbWFudGVuaWVuZG8gZWwgbWlzbW8gaW5zdGFudGUgZW4gZWwgdGllbXBvLiBFbiBvdHJhcyBwYWxhYnJhcywgcG9kZW1vcyByZXNwb25kZXIgwr9RdcOpIGhvcmEgZXMgZW4gQXVzdHJhbGlhPyBFc3RvIGVzIGp1c3RhbWVudGUgbG8gcXVlIG5lY2VzaXRhbW9zLgogCmBgYHtyfQpyZXBvcnRlcyA8LSByZXBvcnRlcyAlPiUgCiAgbXV0YXRlKGZlY2hhX3dpdGhfdHogPSB3aXRoX3R6KGZlY2hhX2NvbnZlcnRpZGFfdHosIHR6b25lID0gIlVUQyIpKSAKCnJlcG9ydGVzICU+JSAKICAgcHVsbChmZWNoYV93aXRoX3R6KSAlPiUgIyBOZWNlc2l0YW1vcyBleHRyYWVyIGVsIHZlY3RvciBwYXJhIHZlciBsYSB6b25hIGhvcmFyaWEuCiAgLlsxXQpgYGAKIAoKCjo6OnsuYWxlcnQgLmFsZXJ0LWluZm99CgoqKkRlc2Fmw61vKioKCkVzdMOhcyB0cmFiYWphbmRvIGNvbiB1bmEgYmFzZSBkZSBkYXRvcyB0ZW1wZXJhdHVyYSBxdWUgY29udGllbmUgdW5hIHZhcmlhYmxlIGNvbiBpbmZvcm1hY2nDs24gZmVjaGFzIHkgaG9yYXMuIEFsIHJldmlzYXIgbGEgY2xhc2UgZGUgbGEgdmFyaWFibGUgdGllbXBvIG9ic2VydsOhcyBxdWUgYXBhcmVjZSBjb21vIGNhcmFjdGVyLiBBZGVtw6FzIHRlIGd1c3RhcsOtYSBwb2RlciBtb3N0cmFyIGVzdG9zIGRhdG9zIGVuIGhvcmEgbG9jYWwgKC0zKSBwYXJhIHF1ZSBlbCBww7pibGljbyBnZW5lcmFsIHB1ZWRhIGVudGVuZGVyIGbDoWNpbG1lbnRlIHR1cyByZXN1bHRhZG9zLiBPcmRlbsOhIGxhcyBzaWd1aWVudGVzIGzDrW5lYXMgZGUgY8OzZGlnbyBwYXJhIGNvcnJlZ2lyIGxhIHZhcmlhYmxlLiBBZ3JlZ8OhICU+JSBjdWFuZG8gbG8gY29uc2lkZXJlcy4KCmBgYAptdXRhdGUodGllbXBvID0geW1kX2htcyh0aWVtcG8sIHR6ID0g4oCcVVRD4oCdKSkKdGVtcGVyYXR1cmEgCm11dGF0ZSh0aWVtcG9fbG9jYWwgPSB3aXRoX3R6KHRpZW1wbywgdHpvbmUgPSDigJxBbWVyaWNhL0FyZ2VudGluYS9CdWVub3NfQWlyZXPigJ0pKQp0ZW1wZXJhdHVyYV9jb3JyZWdpZG8KYGBgCjo6OgoKOjo6ey5hbGVydCAuYWxlcnQtaW5mb30KKipFeHRyYSoqCgpFbiBlbCDigJxhbWJpZW50ZeKAnSBkZSBSIHRlbsOpcyB1bmEgdmFyaWFibGUgYG1pX2ZlY2hhYCBxdWUgZ3VhcmRhIHVuYSBmZWNoYS4KCmBgYAo+IG1pX2ZlY2hhClsxXSAiMjAxOC0xMS0yMiAyMTowMDowMCAtMDMiCj4gY2xhc3MobWlfZmVjaGEpClsxXSAiUE9TSVhjdCIgIlBPU0lYdCIKYGBgCgpTaSBjb3JyaWVyYXMgbGEgc2lndWllbnRlIGzDrW5lYSBkZSBjw7NkaWdvLCBjdcOhbCBzZXLDoSBlbCByZXN1bHRhZG8/IChwZW5zw6EgY3VhbCBzZXLDoSBlbCByZXN1bHRhZG8gc2luIGNvcnJlciBlbCBjw7NkaWdvIGVuIFIpLgoKYGBgCj4gd2l0aF90eihtaV9mZWNoYSwgdHpvbmUgPSAiVVRDIikKYGBgCgotICIyMDE4LTExLTIyIDIxOjAwOjAwIFVUQyIgZXMgZGVjaXIsIG1hbnRlbmllbmRvIGxhIG1pc21hIGhvcmEgZW4gZWwgcmVsb2ogeSBjYW1iaWFuZG8gbGEgem9uYSBob3JhcmlhLgotICIyMDE4LTExLTIzIDAwOjAwOjAwIFVUQyIgZXMgZGVjaXIsIGVsIG1pc21vIGluc3RhbnRlIGVuIGVsIHRpZW1wbyBleHByZXNhZG8gZW4gdW5hIHpvbmEgaG9yYXJpYSBkaXN0aW50YS4gCi0gIjIwMTgtMTEtMjIgMTg6MDA6MDAgVVRDIiBlcyBkZWNpciwgZWwgbWlzbW8gaW5zdGFudGUgZW4gZWwgdGllbXBvIGV4cHJlc2FkbyBlbiB1bmEgem9uYSBob3JhcmlhIGRpc3RpbnRhLiAKLSAiMjAxOC0xMS0yMiAwMDowMDowMCBVVEMiIGVzIGRlY2lyLCBlbCBtaXNtbyBpbnN0YW50ZSBlbiBlbCB0aWVtcG8gZXhwcmVzYWRvIGVuIHVuYSB6b25hIGhvcmFyaWEgZGlzdGludGEuIAoKOjo6Cgo8ZGl2IGNsYXNzPSJidG4tZ3JvdXAiIHJvbGU9Imdyb3VwIiBhcmlhLWxhYmVsPSJOYXZlZ2FjacOzbiI+CiAgPGEgaHJlZj0gIjA3LWdyYWZpY29zLUkuaHRtbCIgY2xhc3MgPSAiYnRuIGJ0bi1wcmltYXJ5Ij5BbnRlcmlvcjwvYT4KICA8YSBocmVmPSAiMDktZ3JhZmljb3MtSUkuaHRtbCIgY2xhc3MgPSAiYnRuIGJ0bi1wcmltYXJ5Ij5TaWd1aWVudGU8L2E+CjwvZGl2Pg==