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:

  1. Revisá la documentación de la familia de funciones ymd_hms() y las funciones ymd() par ver que combinaciones posibles existen.
  2. ¿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

  1. 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()).
  2. 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"

Tranformando zonas horarias

Ahora si, R interpreta estas fechas y horas en la zona horaria correcta. Sin embargo, en meteorología estamos acostumbrados a comunicar nuestros resultados en hora UTC (u hora Z, que es lo mismo), ¿cómo convertimos las fechas y horas a otra zona horaria?

{lubridate} tiene dos funciones específicas para trabajar con zonas horarias.

  • force_tz() mantiene constante la hora que vemos en el reloj pero cambia la zona horaria.
reportes <- reportes %>% 
  mutate(fecha_force_tz = force_tz(fecha_convertida_tz, tzone = "UTC")) 

reportes %>% 
   pull(fecha_force_tz) %>% # Necesitamos extraer el vector para ver la zona horaria.
  .[1]
## [1] "2018-10-29 22:05:00 UTC"

Esto no resuelve nuestro problema, si queremos expresar “2018-10-30 12:50:00 -3” en hora UTC hay que sumarle 3 horas.

  • with_tz() nos permite cambiar de zona horaria manteniendo el mismo instante en el tiempo. En otras palabras, podemos responder ¿Qué hora es en Australia? Esto es justamente lo que necesitamos.
reportes <- reportes %>% 
  mutate(fecha_with_tz = with_tz(fecha_convertida_tz, tzone = "UTC")) 

reportes %>% 
   pull(fecha_with_tz) %>% # Necesitamos extraer el vector para ver la zona horaria.
  .[1]
## [1] "2018-10-30 01:05:00 UTC"

Desafío

Estás trabajando con una base de datos temperatura que contiene una variable con información fechas y horas. Al revisar la clase de la variable tiempo observás que aparece como caracter. Además te gustaría poder mostrar estos datos en hora local (-3) para que el público general pueda entender fácilmente tus resultados. Ordená las siguientes líneas de código para corregir la variable. Agregá %>% cuando lo consideres.

mutate(tiempo = ymd_hms(tiempo, tz = “UTC”))
temperatura 
mutate(tiempo_local = with_tz(tiempo, tzone = “America/Argentina/Buenos_Aires”))
temperatura_corregido

Extra

En el “ambiente” de R tenés una variable mi_fecha que guarda una fecha.

> mi_fecha
[1] "2018-11-22 21:00:00 -03"
> class(mi_fecha)
[1] "POSIXct" "POSIXt"

Si corrieras la siguiente línea de código, cuál será el resultado? (pensá cual será el resultado sin correr el código en R).

> with_tz(mi_fecha, tzone = "UTC")
  • “2018-11-22 21:00:00 UTC” es decir, manteniendo la misma hora en el reloj y cambiando la zona horaria.
  • “2018-11-23 00:00:00 UTC” es decir, el mismo instante en el tiempo expresado en una zona horaria distinta.
  • “2018-11-22 18:00:00 UTC” es decir, el mismo instante en el tiempo expresado en una zona horaria distinta.
  • “2018-11-22 00:00:00 UTC” es decir, el mismo instante en el tiempo expresado en una zona horaria distinta.
LS0tCnRpdGxlOiAiTWFuaXB1bGFuZG8gZmVjaGFzIgpvdXRwdXQ6IAogIGh0bWxfZG9jdW1lbnQ6CiAgICBjb2RlX2Rvd25sb2FkOiB0cnVlCiAgICB0b2M6IHRydWUKICAgIHRvY19mbG9hdDogdHJ1ZQogICAgaGlnaGxpZ2h0OiB0YW5nbyAKLS0tCgpgYGB7ciBzZXR1cCwgaW5jbHVkZT1GQUxTRX0Ka25pdHI6Om9wdHNfY2h1bmskc2V0KGVjaG8gPSBUUlVFKQpgYGAKCkVsIHBhcXVldGUgcG9yIGV4Y2VsZW5jaWEgcGFyYSB0cmFiYWphciBjb24gZmVjaGFzIHkgaG9yYXMgZXMge2x1YnJpZGF0ZX0gcXVlIGZvcm1hIHBhcnRlIGRlbCBtdW5kbyBkZSB0aWR5dmVyc2UgY29tbyAoY2FzaSkgdG9kb3MgbG9zIHBhcXVldGVzIHF1ZSB1c2Ftb3MgaGFzdGEgYWhvcmEuIFNpIG5vcm1hbG1lbnRlIHVzYXMgYGxpYnJhcnkodGlkeXZlcnNlKWAgcGFyYSBjYXJnYXIgbG9zIHBhcXVldGVzLCB0ZW7DqSBlbiBjdWVudGEgcXVlIHtsdWJyaWRhdGV9IG5vIHNlIGNhcmdhIGF1dG9tw6F0aWNhbWVudGUuIEFzw60gcXVlIGFycmFucXVlbW9zIHBvciBsbyBwcmltZXJvLgoKYGBge3IgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRX0KbGlicmFyeShsdWJyaWRhdGUpCmxpYnJhcnkoZHBseXIpCmxpYnJhcnkocmVhZHIpCmBgYAoKCiMjIENyZWFuZG8gZmVjaGFzIHkgaG9yYXMKClIgbWFuZWphIGZlY2hhcyB5IGZlY2hhcyB5IGhvcmFzIHkgcGFyYSBlc3RvIHRpZW5lIGxvcyB0aXBvcyBkZSBkYXRvcyBgPGRhdGU+YCB5IGA8ZHR0bT5gIHkgc2kgYmllbiBpbnRlcm5hbWVudGUgZXNvcyBkYXRvcyBzZSBhbG1hY2VuYW4gY29tbyBuw7ptZXJvcyBlbnRlcm9zIChwb3IgZWplbXBsbyBjYW50aWRhZCBkZSBzZWd1bmRvcyBkZXNkZSBlbCAxIGRlIGVuZXJvIGRlIDE5NzApIGNvbiBsdWJyaWRhdGUgY2FzaSBubyB0ZW5lbW9zIHF1ZSBwcmVvY3VwYXJub3MgZGUgZXNvIHkgcG9kZW1vcyB0cmFiYWphciBjb24gZmVjaGFzIGNvbW8gc2VyZXMgaHVtYW5vcy4gCgpFcyBwb3NpYmxlIHF1ZSBhbCBsZWVyIGJhc2VzIGRlIGRhdG9zIGFsZ3VuYXMgY29sdW1uYXMgY29uIGVzdGUgdGlwbyBkZSBpbmZvcm1hY2nDs24gc2VhIGxlw61kYSBjb21vIGNhZGVuYSBkZSBjYXJhY3RlcmVzLCBwb3IgZWplbXBsbyAiMjUtT2N0LTE5OTAiLiBFc3RvIHNlIHB1ZWRlIHNvbHVjaW9uYXIgdGFudG8gZW4gbGEgbGVjdHVyYSBkZSBsb3MgZGF0b3MgY29tbyBsdWVnbyB1c2FuZG8gbHVicmlkYXRlLiBWZWFtb3MgbG8gc2VndW5kby4KCgpWYW1vcyBhIHRyYWJhamFyIGNvbiB1bmEgYmFzZSBkZSBkYXRvcyBkZSByZXBvcnRlcyBkZSBldmVudG9zIHNldmVyb3MgcmVwb3J0YWRvcyBwb3IgcGVyc29uYXMgZHVyYW50ZSBsYSAgY2FtcGHDsWEgW1JFTEFNUEFHT10oaHR0cHM6Ly93d3cuYmJjLmNvbS9tdW5kby9ub3RpY2lhcy00NjEzMzMzMCkgcXVlIHNlIGxsZXbDsyBhIGNhYm8gZW4gQXJnZW50aW5hIGVudHJlIG9jdHVicmUgeSBkaWNpZW1icmUgZGUgMjAxOC4gCgpMb3MgcmVwb3J0ZXMgc2UgcmVjb2xlY3Rhcm9uIGEgdHJhdsOpcyBkZSB1biBmb3JtdWxhcmlvIGRlIGdvb2dsZSBxdWUgZW50cmUgb3Ryb3MgZGF0b3MgcGVkw61hOiBlbCB0aXBvIGRlIGV2ZW50byBvIGV2ZW50b3Mgb2JzZXJ2YWRvcywgZWwgbHVnYXIgeSBob3JhcmlvIGRlIG9jdXJyZW5jaWEgZGVsIGV2ZW50by4gCgpgYGB7ciBkYXRvc30KcmVwb3J0ZXMgPC0gcmVhZF9jc3YoImh0dHBzOi8vcmF3LmdpdGh1YnVzZXJjb250ZW50LmNvbS9wYW9jb3JyYWxlcy9yNGRzLW5vdGFzL21hc3Rlci9leGFtZW4vZXZlbnRvc19zZXZlcm9zX1JFTEFNUEFHTy5jc3YiKQoKZ2xpbXBzZShyZXBvcnRlcykgIyDCv3F1w6kgcGludGEgdGllbmU/CmBgYAoKOjo6ey5hbGVydCAuYWxlcnQtc3VjY2Vzc30KCioqVGlwOioqIGxhcyBmdW5jaW9uZXMgYHJlYWRfKigpYCBwdWVkZSBsZWVyIGJhc2VzIGRlIGRhdG9zIGRlc2RlIHVybHMhIGVuIGVzdGUgY2FzbyBlcyB1biBhcmNoaXZvIGd1YXJkYWRvIGVuIHVuIHJlcG9zaXRvcmlvIGRlIEdpdEh1Yi4KOjo6CgpMYSBiYXNlIGRlIGRhdG9zIHRpZW5lIDQgdmFyaWFibGVzLCBsYSBmZWNoYSAocXVlIFIgbm8gcmVjb25vY2nDsyBjb21vIHRhbCksIGxhIHViaWNhY2nDs24gZW4gbGF0aXR1ZCB5IGxvbmdpdHVkIHkgZWwgdGlwbyBkZSBldmVudG8uIAoKQXJyZWdsZW1vcyBsYSB2YXJpYWJsZSBmZWNoYS4gUGFyYSBlc28gdXNhcmVtb3MgdW5hIGZhbWlsaWEgZGUgZnVuY2lvbmVzIGRlIGx1YnJpZGF0ZSBxdWUgY29udmllcnRlbiB1bmEgY2FkZW5hIGRlIGNhcmFjdGVyZXMgbyBuw7ptZXJvcyBlbiBmZWNoYXMgeSBmZWNoYXMgeSBob3Jhcy4gCgpFbiBlc3RlIGNhc28gZWwgZm9ybWF0byBkZSBsYSBmZWNoYSBlbiBsYSBiYXNlIGRlIGRhdG9zIGVzICJtZXMgLyBkw61hIC8gYcOxbyBob3JhIDogbWludXRvcyA6IHNlZ3VuZG9zIiB5ICBjb25vY2llbmRvIGVsIG9yZGVuIGRlIGxhcyBjb21wb25lbnRlcyBwb2RlbW9zIGVsZWdpciBsYSBmdW5jacOzbiBuZWNlc2FyaWEuIEVuIGVzdGUgY2FzbyBzZXLDoSBgbWR5X2htcygpYCBxdWUgdmllbmUganVzdGFtZW50ZSBkZSAqKm0qKm9udGgsICoqZCoqYXksICoqeSoqZWFyLCAqKmgqKm91ciwgKiptKippbnV0ZSB5ICoqcyoqZWNvbmQsIGxhcyBjb21wb25lbnRlcyBlbiBpbmdsw6lzLiAKCmBgYHtyIHBhcnNlX2ZlY2hhfQpyZXBvcnRlcyA8LSByZXBvcnRlcyAlPiUgCiAgbXV0YXRlKGZlY2hhX2NvbnZlcnRpZGEgPSBtZHlfaG1zKGZlY2hhKSkgCgpyZXBvcnRlcyAlPiUgCiAgcHVsbChmZWNoYV9jb252ZXJ0aWRhKSAlPiUgIyBOZWNlc2l0YW1vcyBleHRyYWVyIGVsIHZlY3RvciBwYXJhIHZlciBsYSB6b25hIGhvcmFyaWEuCiAgLlsxXSAgICAgICAgICAgICAgICAgICAgICAgIyBFeHRyYWVtb3MgZWwgcHJpbWVyIGVsZW1lbnRvIApgYGAKClBvciBkZWZlY3RvIGN1YWxxdWllcmEgZGUgbGFzIGZ1bmNpb25lcyBkZSBsYSBmYW1pbGlhIGB5bWRfaG1zKClgIGxlIGFzaWduYSBsYSB6b25hIGhvcmFyaWEgVVRDIGEgbGEgdmFyaWFibGUgZGUgc2FsaWRhLiBOb3MgdmFtb3MgYSBvY3VwYXIgZGUgZXN0byBtw6FzIGFkZWxhbnRlIHBlcm8gZXMgaW1wb3J0YW50ZSBjb25vY2VyIGxvcyBtZXRhZGF0b3MgZGUgZXN0YSBiYXNlIGRlIGRhdG9zLCDCv2xhcyBob3JhcyBlc3RhcsOhbiBlbiBob3JhIGxvY2FsIG8gZW4gVVRDPwoKOjo6ey5hbGVydCAuYWxlcnQtaW5mb30KCioqRGVzYWbDrW86KioKCjEuIFJldmlzw6EgbGEgZG9jdW1lbnRhY2nDs24gZGUgbGEgZmFtaWxpYSBkZSBmdW5jaW9uZXMgYHltZF9obXMoKWAgeSBsYXMgZnVuY2lvbmVzIGB5bWQoKWAgcGFyIHZlciBxdWUgY29tYmluYWNpb25lcyBwb3NpYmxlcyBleGlzdGVuLgoyLiDCv0N1YWwgZGUgdG9kYXMgbGFzIGZ1bmNpb25lcyB1c2Fyw61hcyBzaSB0ZW7DqXMgcXVlIGNvbnZlcnRpciBhIGZlY2hhIHkgaG9yYSBsb3Mgc2lndWllbnRlcyBlbGVtZW50b3M6CiAgLSAiMjAxOC0xMS0yMiAxMjowMDowMCIKICAtIDE1MDkyMDIwCiAgLSAiMTAvMzEvMTk5OCIKICAtIDIwMTgxMTIyMTUwMDAwCjo6OgoKClNpIGVuIHZleiBkZSB0ZW5lciBsYSBpbmZvcm1hY2nDs24gZGUgbGEgZmVjaGEgdSBmZWNoYSB5IGhvcmEgZW4gdW5hIHNvbGEgY29sdW1uYSwgbGEgdGVuZW1vcyBlbiBkaXN0aW50YXMgY29sdW1uYXMgKGVsIG1lcyBwb3IgdW4gbGFkbywgZWwgZMOtYSBwb3IgZWwgb3RybywgZWN0LiksIGV4aXN0ZW4gdW4gcGFyIGRlIGZ1bmNpb25lcyBxdWUgcGVybWl0ZW4gYXJtYXIgbGEgdmFyaWFibGU6IGBtYWtlX2RhdGUoKWAgeSBgbWFrZV9kYXRldGltZSgpYC4gU2kgcXVpc2nDqXJhbW9zIGFybWFyIGxhIGZlY2hhIDI1IGRlIG9jdHVicmUgZGUgMTk5MCB0ZW5kcsOtYW1vcyBxdWUgdXNhciBsYSBzaWd1aWVudGUgbMOtbmVhIGRlIGPDs2RpZ286CgpgYGB7cn0KbWFrZV9kYXRlKHllYXIgPSAxOTkwLCBtb250aCA9IDEwLCBkYXkgPSAyNSkKYGBgCgpPIHVuYSBmZWNoYSB5IGhvcmEsIHBvciBlamVtcGxvIGVsIDE0IGRlIGFnb3N0byBkZSAxOTg4IGEgbGFzIDEzIGhvcmFzIChwb3IgYWhvcmEgVVRDLCB5YSBub3MgbWV0ZXJlbW9zIGVuIGVzZSBiZXJlbmplbmFsIGFsIGZpbmFsIGRlIGVzdGUgZXBpc29kaW8pLgoKYGBge3J9Cm1ha2VfZGF0ZXRpbWUoeWVhciA9IDE5ODgsIG1vbnRoID0gOCwgZGF5ID0gMTQsIGhvdXIgPSAxMykKYGBgCgoKUG9yIHN1cHVlc3RvIHBvZHLDrWFtb3MgZ3VhcmRhciBlc2FzIGZlY2hhcyBlbiB2YXJpYWJsZXMgbyB1c2FyIGxhcyBmdW5jaW9uZXMgZGVudHJvIGRlIGBtdXRhdGUoKWAgcGFyYSBnZW5lcmFyIGNvbHVtbmFzIG51ZXZhcyBlbiB1biBkYXRhIGZyYW1lLgoKIyMgU3VzIGNvbXBvbmVudGVzCgpBaG9yYSBxdWUgc2FiZW1vcyBjb21vIGFybWFyIGZlY2hhcywgZXMgcG9zaWJsZSBxdWUgdGUgZXN0w6lzIHByZWd1bnRhbmRvIGNvbW8gZGVzYXJtYXJsYXMgbyBjb21vIGV4dHJhZXIgc3VzIGNvbXBvbmVudGVzLiBBbGdvIGRlIGVzdG8geWEgaGljaW1vcyBhbCBncmFmaWNhciB0ZW1wZXJhdHVyYXMgbWVkw61hcyBtZW5zdWFsZXMgcGVybyB2YW1vcyBhIHJldmlzYXJsby4KCntsdWJyaWRhdGV9IHRpZW5lIHRvZGEgdW5hIGZhbWlsaWEgZGUgZnVuY2lvbmVzIHF1ZSBwZXJtaXRlbiBleHRyYWVyIGxhcyBjb21wb25lbnRlcyBkZSB1bmEgdmFyaWFibGUgZmVjaGEgdSBmZWNoYSB5IGhvcmEuIEFjw6EgbGEgY2xhdmUgZXMgcmVjb3JkYXIgbG9zIG5vbWJyZXMgZW4gaW5nbMOpcyAobm8gbm9zIHF1ZWRhIG90cmEhKSwgcG9yIGVqZW1wbG8gc2kgcXVlcmVtb3MgZWwgbWVzLCBsYSBmdW5jacOzbiBzZXLDoSBgbW9udGgoKWAuCgpWZWFtb3MgY29tbyBmdW5jaW9uYS4KCmBgYHtyfQpyZXBvcnRlcyAlPiUgCiAgbXV0YXRlKG1lcyA9IG1vbnRoKGZlY2hhX2NvbnZlcnRpZGEpLAogICAgICAgICBkaWEgPSBkYXkoZmVjaGFfY29udmVydGlkYSkpIApgYGAKCkVuIHBhcnRpY3VsYXIgbGEgY29tcG9uZW50ZSBkw61hIHRpZW5lIHRvZGEgdW5hIHN1YmZhbWlsaWEgZGUgZnVuY2lvbmVzIHBhcmEgZXh0cmFlciBlbCBkw61hIGRlbCBhw7FvLCBlbCBkw61hIGRlbCBtZXMgbyBlbCBkw61hIGRlIGxhIHNlbWFuYSEKCjo6OnsuYWxlcnQgLmFsZXJ0LWluZm99CgoqKkRlc2Fmw61vKioKCjEuIEV4dHJhw6kgZWwgZMOtYSBkZSBsYSBzZW1hbmEgZ2VuZXJhbmRvIHVuYSBudWV2YSBjb2x1bW5hIGVuIGxhIGJhc2UgZGUgZGF0b3MgYHJlcG9ydGVzYCBxdWUgc2UgbGxhbWUgYGRpYV9zZW1hbmFgLiAoUHNzcyEgbGEgZnVuY2nDs24gc2UgbGxhbWEgYHdkYXkoKWApLgoyLiBBaG9yYSBpbnRlbnRhbG8gZGUgbnVldm8gcGVybyBjYW1iaWFuZG8gZWwgYXJndW1lbnRvIGxhYmVsIGEgYFRSVUVgLiDCv1F1w6kgb2N1cnJlPyDCv0NhbWJpYSBlbCB0aXBvIGRlIGRhdG9zPyAKOjo6CgojIyBab25hcyBob3JhcmlhcwoKQ3VhbmRvIHRyYWJhamFtb3MgY29uIGRhdG9zIHRlbXBvcmFsZXMgeSBlc3BlY8OtZmljYW1lbnRlIGFxdWVsbG9zIHF1ZSB0aWVuZW4gZmVjaGEgeSBob3JhIHVuYSBkZSBsYXMgY29uc2lkZXJhY2lvbmVzIHF1ZSB0ZW5lbW9zIHF1ZSB0ZW5lciBlbiBjdWVudGEgZXMgbGEgem9uYSBob3JhcmlhLiBQb3IgZGVmZWN0byB7bHVicmlkYXRlfSB5IGxhIGNpZW5jaWEgZW4gZ2VuZXJhbCB0cmFiYWphIGVuICpob3JhIFVUQyogKG8gQ29vcmRpbmF0ZWQgVW5pdmVyc2FsIFRpbWUpIHBlcm8gZXMgcG9zaWJsZSBxdWUgbnVlc3Ryb3MgZGF0b3MgZXN0w6luIGFsbWFjZW5hZG9zIGVuIHVuYSB6b25hIGhvcmFyaWEgdG90YWxtZW50ZSBkaXN0aW50YS4KCkVuIGVzdGUgY2FzbywgbG9zIHJlcG9ydGVzIHRpZW5lbiBsYSBob3JhIGEgbGEgcXVlIG9jdXJyacOzIGVsIGV2ZW50byBlbiBBcmdlbnRpbmEuIFBvciBlc3RvIHBvZGVtb3MgaW5mZXJpciBxdWUgbGEgem9uYSBob3JhcmlhIGVzICotMyouIEVzdG8gc2lnbmlmaWNhICozIGhvcmFzIG1lbm9zIHJlc3BlY3RvIGRlIGxhIGhvcmEgVVRDKi4gIMK/Q8OzbW8gbGUgYXZpc2Ftb3MgYSBSPwoKVG9kYXMgbGFzIGZ1bmNpb25lcyBkZSBsYSBmYW1pbGlhIGB5bWRfaG1zKClgIHRpZW5lIHVuIGFyZ3VtZW50byBgdHpgIHF1ZSBwZXJtaXRlIGNhbWJpYXIgbGEgem9uYSBob3JhcmlhLiBFbCB0cnVjbyBlc3TDoSBlbiBzYWJlciBsYSBub21lbmNsYXR1cmEgZGUgem9uYXMgaG9yYXJpYXMuCgpgYGB7cn0KaGVhZChPbHNvbk5hbWVzKCkpICMgTGlzdGEgZGUgbGFzIG3DoXMgZGUgNjAwIHpvbmFzIGhvcmFyaWFzIGRpc3BvbmlibGVzClN5cy50aW1lem9uZSgpICAgICAjIMK/RW4gcXXDqSB6b25hIGhvcmFyaWEgZXN0w6EgbWkgY29tcHV0YWRvcmE/CmBgYAoKUG9kZW1vcyB1c2FyIGVzYSBpbmZvcm1hY2nDs24gZW4gZWwgYXJndW1lbnRvIGB0emAuCgpgYGB7cn0KcmVwb3J0ZXMgPC0gcmVwb3J0ZXMgJT4lIAogIG11dGF0ZShmZWNoYV9jb252ZXJ0aWRhX3R6ID0gbWR5X2htcyhmZWNoYSwgdHogPSAiQW1lcmljYS9BcmdlbnRpbmEvQnVlbm9zX0FpcmVzIikpIAoKcmVwb3J0ZXMgJT4lIAogICBwdWxsKGZlY2hhX2NvbnZlcnRpZGFfdHopICU+JSAjIE5lY2VzaXRhbW9zIGV4dHJhZXIgZWwgdmVjdG9yIHBhcmEgdmVyIGxhIHpvbmEgaG9yYXJpYS4KICAuWzFdCmBgYAoKIyMjIFRyYW5mb3JtYW5kbyB6b25hcyBob3JhcmlhcwoKQWhvcmEgc2ksIFIgaW50ZXJwcmV0YSBlc3RhcyBmZWNoYXMgeSBob3JhcyBlbiBsYSB6b25hIGhvcmFyaWEgY29ycmVjdGEuIFNpbiBlbWJhcmdvLCBlbiBtZXRlb3JvbG9nw61hIGVzdGFtb3MgYWNvc3R1bWJyYWRvcyBhIGNvbXVuaWNhciBudWVzdHJvcyByZXN1bHRhZG9zIGVuIGhvcmEgKlVUQyogKHUgKmhvcmEgWiosIHF1ZSBlcyBsbyBtaXNtbyksIMK/Y8OzbW8gY29udmVydGltb3MgbGFzIGZlY2hhcyB5IGhvcmFzIGEgb3RyYSB6b25hIGhvcmFyaWE/CiAKe2x1YnJpZGF0ZX0gdGllbmUgZG9zIGZ1bmNpb25lcyBlc3BlY8OtZmljYXMgcGFyYSB0cmFiYWphciBjb24gem9uYXMgaG9yYXJpYXMuIAogCiogYGZvcmNlX3R6KClgIG1hbnRpZW5lIGNvbnN0YW50ZSBsYSBob3JhIHF1ZSB2ZW1vcyBlbiBlbCByZWxvaiBwZXJvIGNhbWJpYSBsYSB6b25hIGhvcmFyaWEuICAKIApgYGB7cn0KcmVwb3J0ZXMgPC0gcmVwb3J0ZXMgJT4lIAogIG11dGF0ZShmZWNoYV9mb3JjZV90eiA9IGZvcmNlX3R6KGZlY2hhX2NvbnZlcnRpZGFfdHosIHR6b25lID0gIlVUQyIpKSAKCnJlcG9ydGVzICU+JSAKICAgcHVsbChmZWNoYV9mb3JjZV90eikgJT4lICMgTmVjZXNpdGFtb3MgZXh0cmFlciBlbCB2ZWN0b3IgcGFyYSB2ZXIgbGEgem9uYSBob3JhcmlhLgogIC5bMV0KYGBgCgpFc3RvIG5vIHJlc3VlbHZlIG51ZXN0cm8gcHJvYmxlbWEsIHNpIHF1ZXJlbW9zIGV4cHJlc2FyICIyMDE4LTEwLTMwIDEyOjUwOjAwIC0zIiBlbiBob3JhIFVUQyBoYXkgcXVlICpzdW1hcmxlKiAzIGhvcmFzLgogCiogYHdpdGhfdHooKWAgbm9zIHBlcm1pdGUgY2FtYmlhciBkZSB6b25hIGhvcmFyaWEgbWFudGVuaWVuZG8gZWwgbWlzbW8gaW5zdGFudGUgZW4gZWwgdGllbXBvLiBFbiBvdHJhcyBwYWxhYnJhcywgcG9kZW1vcyByZXNwb25kZXIgwr9RdcOpIGhvcmEgZXMgZW4gQXVzdHJhbGlhPyBFc3RvIGVzIGp1c3RhbWVudGUgbG8gcXVlIG5lY2VzaXRhbW9zLgogCmBgYHtyfQpyZXBvcnRlcyA8LSByZXBvcnRlcyAlPiUgCiAgbXV0YXRlKGZlY2hhX3dpdGhfdHogPSB3aXRoX3R6KGZlY2hhX2NvbnZlcnRpZGFfdHosIHR6b25lID0gIlVUQyIpKSAKCnJlcG9ydGVzICU+JSAKICAgcHVsbChmZWNoYV93aXRoX3R6KSAlPiUgIyBOZWNlc2l0YW1vcyBleHRyYWVyIGVsIHZlY3RvciBwYXJhIHZlciBsYSB6b25hIGhvcmFyaWEuCiAgLlsxXQpgYGAKIAoKCjo6OnsuYWxlcnQgLmFsZXJ0LWluZm99CgoqKkRlc2Fmw61vKioKCkVzdMOhcyB0cmFiYWphbmRvIGNvbiB1bmEgYmFzZSBkZSBkYXRvcyB0ZW1wZXJhdHVyYSBxdWUgY29udGllbmUgdW5hIHZhcmlhYmxlIGNvbiBpbmZvcm1hY2nDs24gZmVjaGFzIHkgaG9yYXMuIEFsIHJldmlzYXIgbGEgY2xhc2UgZGUgbGEgdmFyaWFibGUgdGllbXBvIG9ic2VydsOhcyBxdWUgYXBhcmVjZSBjb21vIGNhcmFjdGVyLiBBZGVtw6FzIHRlIGd1c3RhcsOtYSBwb2RlciBtb3N0cmFyIGVzdG9zIGRhdG9zIGVuIGhvcmEgbG9jYWwgKC0zKSBwYXJhIHF1ZSBlbCBww7pibGljbyBnZW5lcmFsIHB1ZWRhIGVudGVuZGVyIGbDoWNpbG1lbnRlIHR1cyByZXN1bHRhZG9zLiBPcmRlbsOhIGxhcyBzaWd1aWVudGVzIGzDrW5lYXMgZGUgY8OzZGlnbyBwYXJhIGNvcnJlZ2lyIGxhIHZhcmlhYmxlLiBBZ3JlZ8OhICU+JSBjdWFuZG8gbG8gY29uc2lkZXJlcy4KCmBgYAptdXRhdGUodGllbXBvID0geW1kX2htcyh0aWVtcG8sIHR6ID0g4oCcVVRD4oCdKSkKdGVtcGVyYXR1cmEgCm11dGF0ZSh0aWVtcG9fbG9jYWwgPSB3aXRoX3R6KHRpZW1wbywgdHpvbmUgPSDigJxBbWVyaWNhL0FyZ2VudGluYS9CdWVub3NfQWlyZXPigJ0pKQp0ZW1wZXJhdHVyYV9jb3JyZWdpZG8KYGBgCjo6OgoKOjo6ey5hbGVydCAuYWxlcnQtaW5mb30KKipFeHRyYSoqCgpFbiBlbCDigJxhbWJpZW50ZeKAnSBkZSBSIHRlbsOpcyB1bmEgdmFyaWFibGUgYG1pX2ZlY2hhYCBxdWUgZ3VhcmRhIHVuYSBmZWNoYS4KCmBgYAo+IG1pX2ZlY2hhClsxXSAiMjAxOC0xMS0yMiAyMTowMDowMCAtMDMiCj4gY2xhc3MobWlfZmVjaGEpClsxXSAiUE9TSVhjdCIgIlBPU0lYdCIKYGBgCgpTaSBjb3JyaWVyYXMgbGEgc2lndWllbnRlIGzDrW5lYSBkZSBjw7NkaWdvLCBjdcOhbCBzZXLDoSBlbCByZXN1bHRhZG8/IChwZW5zw6EgY3VhbCBzZXLDoSBlbCByZXN1bHRhZG8gc2luIGNvcnJlciBlbCBjw7NkaWdvIGVuIFIpLgoKYGBgCj4gd2l0aF90eihtaV9mZWNoYSwgdHpvbmUgPSAiVVRDIikKYGBgCgotICIyMDE4LTExLTIyIDIxOjAwOjAwIFVUQyIgZXMgZGVjaXIsIG1hbnRlbmllbmRvIGxhIG1pc21hIGhvcmEgZW4gZWwgcmVsb2ogeSBjYW1iaWFuZG8gbGEgem9uYSBob3JhcmlhLgotICIyMDE4LTExLTIzIDAwOjAwOjAwIFVUQyIgZXMgZGVjaXIsIGVsIG1pc21vIGluc3RhbnRlIGVuIGVsIHRpZW1wbyBleHByZXNhZG8gZW4gdW5hIHpvbmEgaG9yYXJpYSBkaXN0aW50YS4gCi0gIjIwMTgtMTEtMjIgMTg6MDA6MDAgVVRDIiBlcyBkZWNpciwgZWwgbWlzbW8gaW5zdGFudGUgZW4gZWwgdGllbXBvIGV4cHJlc2FkbyBlbiB1bmEgem9uYSBob3JhcmlhIGRpc3RpbnRhLiAKLSAiMjAxOC0xMS0yMiAwMDowMDowMCBVVEMiIGVzIGRlY2lyLCBlbCBtaXNtbyBpbnN0YW50ZSBlbiBlbCB0aWVtcG8gZXhwcmVzYWRvIGVuIHVuYSB6b25hIGhvcmFyaWEgZGlzdGludGEuIAoKOjo6Cgo8ZGl2IGNsYXNzPSJidG4tZ3JvdXAiIHJvbGU9Imdyb3VwIiBhcmlhLWxhYmVsPSJOYXZlZ2FjacOzbiI+CiAgPGEgaHJlZj0gIjA3LWdyYWZpY29zLUkuaHRtbCIgY2xhc3MgPSAiYnRuIGJ0bi1wcmltYXJ5Ij5BbnRlcmlvcjwvYT4KICA8YSBocmVmPSAiMDktZ3JhZmljb3MtSUkuaHRtbCIgY2xhc3MgPSAiYnRuIGJ0bi1wcmltYXJ5Ij5TaWd1aWVudGU8L2E+CjwvZGl2Pg==