Data Wrangling

Contexto

El paquete nycflights13 contiene información sobre todos los vuelos que partieron desde Nueva York (EWR, JFK y LGA) a destinos en los Estados Unidos en 2013. Fueron 336,776 vuelos en total. Para ayudar a comprender las causas de los retrasos, también incluye otros de datos útiles. Responder a la pregunta ¿Por qué los retrasos?

Este paquete incluye las siguientes tablas:

  • flights = todos los vuelos que salieron de Nueva York en 2013
  • weather = datos metereológicos por hora de cada aeropuerto
  • planes = información de construcción de cada avión
  • airports = nombres y ubicaciones de aeropuertos
  • airlines = relación entre nombres y códigos de las aerolíneas

Fuente

Instalar paquetes y llamar librerías

# install.packages("nycflights13")
library(nycflights13)
# install.packages("tidyverse")
library(tidyverse)
## Warning: package 'tidyverse' was built under R version 4.3.3
## Warning: package 'ggplot2' was built under R version 4.3.3
## ── Attaching core tidyverse packages ──────────────────────── tidyverse 2.0.0 ──
## ✔ dplyr     1.1.4     ✔ readr     2.1.5
## ✔ forcats   1.0.0     ✔ stringr   1.5.1
## ✔ ggplot2   3.5.0     ✔ tibble    3.2.1
## ✔ lubridate 1.9.3     ✔ tidyr     1.3.1
## ✔ purrr     1.0.2     
## ── Conflicts ────────────────────────────────────────── tidyverse_conflicts() ──
## ✖ dplyr::filter() masks stats::filter()
## ✖ dplyr::lag()    masks stats::lag()
## ℹ Use the conflicted package (<http://conflicted.r-lib.org/>) to force all conflicts to become errors
library(dplyr)
library(scales)
## 
## Attaching package: 'scales'
## 
## The following object is masked from 'package:purrr':
## 
##     discard
## 
## The following object is masked from 'package:readr':
## 
##     col_factor
library(ggplot2)
library(corrplot)
## corrplot 0.92 loaded
library(lubridate)

Guardar bases de datos

flights <- flights
weather <- weather
planes <- planes
airports <- airports
airlines <- airlines 

Relacion entre base de datos

Funciones Básicas de Manejo de Datos

Select

La función select sirve para seleccionar columnas de una tabla (data frame).

df1 <- flights %>% select(carrier, flight) # Selección de columnas específicas
df2 <- flights %>% select(carrier:distance) # Selección de rango de columnas 
df3 <- flights %>% select(-carrier, -flight) # Eliminar columnas específicas 
df4 <- flights %>% select(-carrier:-distance) # Eliminar rango de columnas 
df5 <- flights %>% select(aerolinea = carrier) # Selecciona una columna y le cambia el nombre 
df6 <- flights %>% rename(aerolinea =carrier) # Cambia el nombre a una columna 

Filter

La función filter sirve para seleccionar renglones de una tabla (data frame). La función slice sirve para seleccionar filas en especifico (data frame).

df7 <- flights %>% filter(dep_delay >=500) # Extrae renglones que cumplan condición 

# Igual == , Desigual =!= , Mayor que > , Mayor o igual que >= , Menor que < , Menor o igual que <= 
# Operadores lógicos: AND &, OR | , NOT ! 


df8 <- flights %>% filter(dep_delay >=500,dep_delay<600) # Extrae renglones que cumplan dos condiciones 
df9 <- flights %>% slice(1000:1099) # Extrae los números de los renglones indicados, sin importar sus valores 

Distinct

La función distinct sirve para eliminar renglones duplicados.

df10 <- distinct(flights) # Dejar sólo los renglones diferentes, borra todos los repetidos 

Merge

La función merge sirve para juntar bases de datos.

bdgrande <- merge(flights, airlines, by = "carrier") # carrier es la columna llave
bdgrande0 <- left_join(flights, weather, by = c("origin", "time_hour"))
bdgrande1 <- left_join(bdgrande, planes, by = "tailnum") # toda tabla bdgrande y solo coincidencias de planes
bdgrande2 <- mutate(bdgrande1, dist_km = distance*1.609)
bdgrande3 <- left_join(bdgrande2, weather, by = c("origin", "time_hour"))

Ejercicios

  1. Encuentra todos los vuelos que tuvieron un atraso en llegada de 2 horas o más
ejercicio1 <- bdgrande2 %>% filter(arr_delay>=120)
  1. Encuentra todos los vuelos que llegaron a Houston (IAH o HOU)
ejercicio2 <- bdgrande2 %>% filter(dest == "IAH" | dest == "HOU")

3.Encuentra todos los vuelos operados por United, American o Delta

ejercicio3 <- bdgrande2 %>% filter(carrier %in% c("UA" , "AA" , "DL"))

Ejercicio 4. Encuentra todos los vuelos que despegaron en Julio, Agosto o Septiembre.

ejercicio4a <- bdgrande2 %>% filter(month == 7 | month == 8 | month == 9)

ejercicio4b <- bdgrande2 %>% filter(month %in% c(7:9))

Ejercicio 5. Encuentra todos los vuelos que arrivaron más de 2 horas tarde, pero no despegaron tarde.

ejercicio5 <- bdgrande2 %>% filter(arr_delay>120 & dep_delay<=0)

Ejercicio 6. Encuentra todos los vuelos que se retrasaron al menos 1 hora, pero que llegaron antes 30 minutos o más.

ejercicio6 <- bdgrande2 %>% filter(dep_delay>=60 & arr_delay<=-30)

Ejercicio 7. Encuentra todos los vuelos que salieron entre la medianoche y las 6 a.m.

ejercicio7 <- bdgrande2 %>% filter(dep_time == 2400 | dep_time <= 600)

Arrange

Similar a filter() pero en lugar de seleccionar renglones, los ordena de menor a mayor.

df11 <- arrange(bdgrande2,year.x,month,day,)

para acomodar de mayor a menor.

df12 <- arrange(bdgrande2,year.x,desc(month),day)

Summarize

Colapsa una tabla a un sólo renglón

# Obtén el retraso promedio de despegue de todos los vuelos 
summarize(bdgrande2,mean(dep_delay, na.rm=TRUE))
##   mean(dep_delay, na.rm = TRUE)
## 1                      12.63907

Group by

Agrupa tabla basado en algunas columnas

# Obtener el retraso promedio de despegue por día 
por_dia <- group_by(bdgrande2, year.x,month,day)
summarize(por_dia,mean(dep_delay, na.rm=TRUE))
## `summarise()` has grouped output by 'year.x', 'month'. You can override using
## the `.groups` argument.
## # A tibble: 365 × 4
## # Groups:   year.x, month [12]
##    year.x month   day `mean(dep_delay, na.rm = TRUE)`
##     <int> <int> <int>                           <dbl>
##  1   2013     1     1                           11.5 
##  2   2013     1     2                           13.9 
##  3   2013     1     3                           11.0 
##  4   2013     1     4                            8.95
##  5   2013     1     5                            5.73
##  6   2013     1     6                            7.15
##  7   2013     1     7                            5.42
##  8   2013     1     8                            2.55
##  9   2013     1     9                            2.28
## 10   2013     1    10                            2.84
## # ℹ 355 more rows

Avance 1

Guardar bases de datos

flights <- flights
weather <- weather
planes <- planes
airports <- airports
airlines <- airlines

Cargar en memoria la tabla flights y mostrar su contenido

Observamos que la tabla flights nos dan las variables:

  • year, month, day, hour, minute y time_hour para la fecha del vuelo.
  • sched_dep_time y sched_arr_time para la hora y minuto agendadas de salida y llegada.
  • dep_time y arr_time para la hora y minuto de salida y llegada.
  • dep_delay y arr_delay dando la diferencia entre la hora agendada y la real.
  • carrier, flight y tailnum que es la aerolínea, el vuelo, y número de cola.
  • origin y dest ofrecen en categorías ciudades de origen y destino.
  • air_time da las horas y minutos que tardo el vuelo.
  • distance da la distancia en millas.
flights <- flights
head(flights)
## # A tibble: 6 × 19
##    year month   day dep_time sched_dep_time dep_delay arr_time sched_arr_time
##   <int> <int> <int>    <int>          <int>     <dbl>    <int>          <int>
## 1  2013     1     1      517            515         2      830            819
## 2  2013     1     1      533            529         4      850            830
## 3  2013     1     1      542            540         2      923            850
## 4  2013     1     1      544            545        -1     1004           1022
## 5  2013     1     1      554            600        -6      812            837
## 6  2013     1     1      554            558        -4      740            728
## # ℹ 11 more variables: arr_delay <dbl>, carrier <chr>, flight <int>,
## #   tailnum <chr>, origin <chr>, dest <chr>, air_time <dbl>, distance <dbl>,
## #   hour <dbl>, minute <dbl>, time_hour <dttm>
tail(flights)
## # A tibble: 6 × 19
##    year month   day dep_time sched_dep_time dep_delay arr_time sched_arr_time
##   <int> <int> <int>    <int>          <int>     <dbl>    <int>          <int>
## 1  2013     9    30       NA           1842        NA       NA           2019
## 2  2013     9    30       NA           1455        NA       NA           1634
## 3  2013     9    30       NA           2200        NA       NA           2312
## 4  2013     9    30       NA           1210        NA       NA           1330
## 5  2013     9    30       NA           1159        NA       NA           1344
## 6  2013     9    30       NA            840        NA       NA           1020
## # ℹ 11 more variables: arr_delay <dbl>, carrier <chr>, flight <int>,
## #   tailnum <chr>, origin <chr>, dest <chr>, air_time <dbl>, distance <dbl>,
## #   hour <dbl>, minute <dbl>, time_hour <dttm>

Consulta la estructura de flights

Tipos de variables

Observamos que en la estructura de flights las variables pueden dividirse en:

  • Númericos: hay variables tipo integer (enteros) y númericos (en negativo y decimales)
  • Categóricos: hay variables tipo character
  • Temporales: existe una variables tipo POSIXct con fecha y hora
str(flights)
## tibble [336,776 × 19] (S3: tbl_df/tbl/data.frame)
##  $ year          : int [1:336776] 2013 2013 2013 2013 2013 2013 2013 2013 2013 2013 ...
##  $ month         : int [1:336776] 1 1 1 1 1 1 1 1 1 1 ...
##  $ day           : int [1:336776] 1 1 1 1 1 1 1 1 1 1 ...
##  $ dep_time      : int [1:336776] 517 533 542 544 554 554 555 557 557 558 ...
##  $ sched_dep_time: int [1:336776] 515 529 540 545 600 558 600 600 600 600 ...
##  $ dep_delay     : num [1:336776] 2 4 2 -1 -6 -4 -5 -3 -3 -2 ...
##  $ arr_time      : int [1:336776] 830 850 923 1004 812 740 913 709 838 753 ...
##  $ sched_arr_time: int [1:336776] 819 830 850 1022 837 728 854 723 846 745 ...
##  $ arr_delay     : num [1:336776] 11 20 33 -18 -25 12 19 -14 -8 8 ...
##  $ carrier       : chr [1:336776] "UA" "UA" "AA" "B6" ...
##  $ flight        : int [1:336776] 1545 1714 1141 725 461 1696 507 5708 79 301 ...
##  $ tailnum       : chr [1:336776] "N14228" "N24211" "N619AA" "N804JB" ...
##  $ origin        : chr [1:336776] "EWR" "LGA" "JFK" "JFK" ...
##  $ dest          : chr [1:336776] "IAH" "IAH" "MIA" "BQN" ...
##  $ air_time      : num [1:336776] 227 227 160 183 116 150 158 53 140 138 ...
##  $ distance      : num [1:336776] 1400 1416 1089 1576 762 ...
##  $ hour          : num [1:336776] 5 5 5 5 6 5 6 6 6 6 ...
##  $ minute        : num [1:336776] 15 29 40 45 0 58 0 0 0 0 ...
##  $ time_hour     : POSIXct[1:336776], format: "2013-01-01 05:00:00" "2013-01-01 05:00:00" ...

Clase de Flights

Podemos identificar que flights (y, por consecuente, las otros 3 objetos en la librería nycflights13) es un tipo de objeto data frame

class(flights)
## [1] "tbl_df"     "tbl"        "data.frame"

Dimensiones de Flights

Tiene un total de 336776 renglones con 19 variables.

dim(flights)
## [1] 336776     19

Resultados descriptivos de Flights

Summary nos arroja los promedios, máximos y mínimos por variables. Estas son nuestras observaciones:

  • Las variables que nos ofrecen las horas y minutos de llegadas, salidas y transcurso de los vuelos tienen NAs.
  • Los datos son solo del año 2013, desde 1ero de enero hasta 31 de diciembre.
  • dep_delay y arr_delay tienen números negativos. Llegaron o salieron más temprano.
  • El IQR de air_time y distance es alto. Los vuelos varían en transcurso y distancia mucho.
summary(flights)
##       year          month             day           dep_time    sched_dep_time
##  Min.   :2013   Min.   : 1.000   Min.   : 1.00   Min.   :   1   Min.   : 106  
##  1st Qu.:2013   1st Qu.: 4.000   1st Qu.: 8.00   1st Qu.: 907   1st Qu.: 906  
##  Median :2013   Median : 7.000   Median :16.00   Median :1401   Median :1359  
##  Mean   :2013   Mean   : 6.549   Mean   :15.71   Mean   :1349   Mean   :1344  
##  3rd Qu.:2013   3rd Qu.:10.000   3rd Qu.:23.00   3rd Qu.:1744   3rd Qu.:1729  
##  Max.   :2013   Max.   :12.000   Max.   :31.00   Max.   :2400   Max.   :2359  
##                                                  NA's   :8255                 
##    dep_delay          arr_time    sched_arr_time   arr_delay       
##  Min.   : -43.00   Min.   :   1   Min.   :   1   Min.   : -86.000  
##  1st Qu.:  -5.00   1st Qu.:1104   1st Qu.:1124   1st Qu.: -17.000  
##  Median :  -2.00   Median :1535   Median :1556   Median :  -5.000  
##  Mean   :  12.64   Mean   :1502   Mean   :1536   Mean   :   6.895  
##  3rd Qu.:  11.00   3rd Qu.:1940   3rd Qu.:1945   3rd Qu.:  14.000  
##  Max.   :1301.00   Max.   :2400   Max.   :2359   Max.   :1272.000  
##  NA's   :8255      NA's   :8713                  NA's   :9430      
##    carrier              flight       tailnum             origin         
##  Length:336776      Min.   :   1   Length:336776      Length:336776     
##  Class :character   1st Qu.: 553   Class :character   Class :character  
##  Mode  :character   Median :1496   Mode  :character   Mode  :character  
##                     Mean   :1972                                        
##                     3rd Qu.:3465                                        
##                     Max.   :8500                                        
##                                                                         
##      dest              air_time        distance         hour      
##  Length:336776      Min.   : 20.0   Min.   :  17   Min.   : 1.00  
##  Class :character   1st Qu.: 82.0   1st Qu.: 502   1st Qu.: 9.00  
##  Mode  :character   Median :129.0   Median : 872   Median :13.00  
##                     Mean   :150.7   Mean   :1040   Mean   :13.18  
##                     3rd Qu.:192.0   3rd Qu.:1389   3rd Qu.:17.00  
##                     Max.   :695.0   Max.   :4983   Max.   :23.00  
##                     NA's   :9430                                  
##      minute        time_hour                     
##  Min.   : 0.00   Min.   :2013-01-01 05:00:00.00  
##  1st Qu.: 8.00   1st Qu.:2013-04-04 13:00:00.00  
##  Median :29.00   Median :2013-07-03 10:00:00.00  
##  Mean   :26.23   Mean   :2013-07-03 05:22:54.64  
##  3rd Qu.:44.00   3rd Qu.:2013-10-01 07:00:00.00  
##  Max.   :59.00   Max.   :2013-12-31 23:00:00.00  
## 

Avance 2

En este avance se tocaran diversos temas y funciones para poder llegar al resultado que se quiere obtener, las funciones que se van a usar son los siguentes:

  • Filter
  • Arrange
  • Select
  • Rename +Mutate
  • Group_by +Summarize
  • if/Else
  • Subset

Paso 1. De la anterior base de datos flights se tiene que conocer y observar el contenido

head(flights)
## # A tibble: 6 × 19
##    year month   day dep_time sched_dep_time dep_delay arr_time sched_arr_time
##   <int> <int> <int>    <int>          <int>     <dbl>    <int>          <int>
## 1  2013     1     1      517            515         2      830            819
## 2  2013     1     1      533            529         4      850            830
## 3  2013     1     1      542            540         2      923            850
## 4  2013     1     1      544            545        -1     1004           1022
## 5  2013     1     1      554            600        -6      812            837
## 6  2013     1     1      554            558        -4      740            728
## # ℹ 11 more variables: arr_delay <dbl>, carrier <chr>, flight <int>,
## #   tailnum <chr>, origin <chr>, dest <chr>, air_time <dbl>, distance <dbl>,
## #   hour <dbl>, minute <dbl>, time_hour <dttm>

Paso 2. Encuentra los datos descriptivos del data frame flights. Identifica la media de las distancias recorridas en millas.

summary(flights)
##       year          month             day           dep_time    sched_dep_time
##  Min.   :2013   Min.   : 1.000   Min.   : 1.00   Min.   :   1   Min.   : 106  
##  1st Qu.:2013   1st Qu.: 4.000   1st Qu.: 8.00   1st Qu.: 907   1st Qu.: 906  
##  Median :2013   Median : 7.000   Median :16.00   Median :1401   Median :1359  
##  Mean   :2013   Mean   : 6.549   Mean   :15.71   Mean   :1349   Mean   :1344  
##  3rd Qu.:2013   3rd Qu.:10.000   3rd Qu.:23.00   3rd Qu.:1744   3rd Qu.:1729  
##  Max.   :2013   Max.   :12.000   Max.   :31.00   Max.   :2400   Max.   :2359  
##                                                  NA's   :8255                 
##    dep_delay          arr_time    sched_arr_time   arr_delay       
##  Min.   : -43.00   Min.   :   1   Min.   :   1   Min.   : -86.000  
##  1st Qu.:  -5.00   1st Qu.:1104   1st Qu.:1124   1st Qu.: -17.000  
##  Median :  -2.00   Median :1535   Median :1556   Median :  -5.000  
##  Mean   :  12.64   Mean   :1502   Mean   :1536   Mean   :   6.895  
##  3rd Qu.:  11.00   3rd Qu.:1940   3rd Qu.:1945   3rd Qu.:  14.000  
##  Max.   :1301.00   Max.   :2400   Max.   :2359   Max.   :1272.000  
##  NA's   :8255      NA's   :8713                  NA's   :9430      
##    carrier              flight       tailnum             origin         
##  Length:336776      Min.   :   1   Length:336776      Length:336776     
##  Class :character   1st Qu.: 553   Class :character   Class :character  
##  Mode  :character   Median :1496   Mode  :character   Mode  :character  
##                     Mean   :1972                                        
##                     3rd Qu.:3465                                        
##                     Max.   :8500                                        
##                                                                         
##      dest              air_time        distance         hour      
##  Length:336776      Min.   : 20.0   Min.   :  17   Min.   : 1.00  
##  Class :character   1st Qu.: 82.0   1st Qu.: 502   1st Qu.: 9.00  
##  Mode  :character   Median :129.0   Median : 872   Median :13.00  
##                     Mean   :150.7   Mean   :1040   Mean   :13.18  
##                     3rd Qu.:192.0   3rd Qu.:1389   3rd Qu.:17.00  
##                     Max.   :695.0   Max.   :4983   Max.   :23.00  
##                     NA's   :9430                                  
##      minute        time_hour                     
##  Min.   : 0.00   Min.   :2013-01-01 05:00:00.00  
##  1st Qu.: 8.00   1st Qu.:2013-04-04 13:00:00.00  
##  Median :29.00   Median :2013-07-03 10:00:00.00  
##  Mean   :26.23   Mean   :2013-07-03 05:22:54.64  
##  3rd Qu.:44.00   3rd Qu.:2013-10-01 07:00:00.00  
##  Max.   :59.00   Max.   :2013-12-31 23:00:00.00  
## 

Paso 3. Define un criterio para encontrar las aerolíneas que han recorrido más distancia y crea un nuevo data frame

#selecciona las variables carrier, distance, origin, dest de la tabla
df1_avance2 <- select(flights, carrier, distance, origin, dest)

#se filtra las aerolineas con millas recorridas superiores a la media - 1040 
df2_avance2 <- filter(df1_avance2, distance >1040) 

#ordena en forma descendente por distancia recorrida 
df3_avance2 <- arrange(df2_avance2, desc(distance))

Paso 4. Encontrar la suma y la media de las distancias recorridas por carrier

#Agrupar los datos por carrier,origin, dest y obtener la suma acumulada y la media de las distancias recorridas por carrier eliminando los NA's

df4_avance2 <- df3_avance2 %>% group_by(carrier,origin,dest) %>%  
  summarize(sumdistance=sum(distance, na.rm=TRUE), meandistance=mean(distance, na.rm=TRUE)) 
## `summarise()` has grouped output by 'carrier', 'origin'. You can override using
## the `.groups` argument.

Paso 5. Ordena en forma descendente por distancia recorrida

df5_avance2 <- arrange(df4_avance2,carrier,sumdistance)

Paso 6. Identifica si las aerolíneas líderes son las mismas en los tres aeropuertos cuyo origen es Nueva York

JFK = df5_avance2 %>%  
  filter(origin == "JFK")  %>%
  arrange(carrier, desc(sumdistance))
LGA = df5_avance2 %>%  
  filter(origin == "LGA")  %>%
  arrange(carrier, desc(sumdistance))
EWR = df5_avance2 %>%  
  filter(origin == "EWR")  %>%
  arrange(carrier, desc(sumdistance))

Avance 3

Paso 1. Consulta las tablas planes y weather para que conozcas su contenido.

head(planes)
## # A tibble: 6 × 9
##   tailnum  year type               manufacturer model engines seats speed engine
##   <chr>   <int> <chr>              <chr>        <chr>   <int> <int> <int> <chr> 
## 1 N10156   2004 Fixed wing multi … EMBRAER      EMB-…       2    55    NA Turbo…
## 2 N102UW   1998 Fixed wing multi … AIRBUS INDU… A320…       2   182    NA Turbo…
## 3 N103US   1999 Fixed wing multi … AIRBUS INDU… A320…       2   182    NA Turbo…
## 4 N104UW   1999 Fixed wing multi … AIRBUS INDU… A320…       2   182    NA Turbo…
## 5 N10575   2002 Fixed wing multi … EMBRAER      EMB-…       2    55    NA Turbo…
## 6 N105UW   1999 Fixed wing multi … AIRBUS INDU… A320…       2   182    NA Turbo…
head(weather)
## # A tibble: 6 × 15
##   origin  year month   day  hour  temp  dewp humid wind_dir wind_speed wind_gust
##   <chr>  <int> <int> <int> <int> <dbl> <dbl> <dbl>    <dbl>      <dbl>     <dbl>
## 1 EWR     2013     1     1     1  39.0  26.1  59.4      270      10.4         NA
## 2 EWR     2013     1     1     2  39.0  27.0  61.6      250       8.06        NA
## 3 EWR     2013     1     1     3  39.0  28.0  64.4      240      11.5         NA
## 4 EWR     2013     1     1     4  39.9  28.0  62.2      250      12.7         NA
## 5 EWR     2013     1     1     5  39.0  28.0  64.4      260      12.7         NA
## 6 EWR     2013     1     1     6  37.9  28.0  67.2      240      11.5         NA
## # ℹ 4 more variables: precip <dbl>, pressure <dbl>, visib <dbl>,
## #   time_hour <dttm>
  • En la tabla “planes” podemos observar los distintos factores que conforman la composición de los aviones de la base de datos “nyflights13”, como por ejemplo : su número de asientos, el número de engines en cada avión, etc.

  • En la tabla “weather” se nos muestra información que nos indica por un lado las condiciones meteorológicas en las que operan los aviones y por otro lado se nos proporciona datos para poder comprender mejor cuándo y de donde salieron los aviones de la base de datos “nyflights13”.

Paso 2. Se necesita saber de cada vuelo, la aerolinea, el aeropuerto de origen y el aeropuerto destino.

Aquí estamos creando una variable llamada “aerolineas” que nos muestra tres variables : aerolíneas, puerto de salida y puerto de llegada de todos los aviones de la base de datos nyflights13.

aerolineas <- flights %>% 
  select(carrier,origin,dest)

aerolineas
## # A tibble: 336,776 × 3
##    carrier origin dest 
##    <chr>   <chr>  <chr>
##  1 UA      EWR    IAH  
##  2 UA      LGA    IAH  
##  3 AA      JFK    MIA  
##  4 B6      JFK    BQN  
##  5 DL      LGA    ATL  
##  6 UA      EWR    ORD  
##  7 B6      EWR    FLL  
##  8 EV      LGA    IAD  
##  9 B6      JFK    MCO  
## 10 AA      LGA    ORD  
## # ℹ 336,766 more rows

Paso 3. En la consulta anterior se necesita conocer el nombre de la aerolínea.

Aquí estamos creando una nueva variable llamada “aerolineas_nombre” que le añade una columna al dataframe “aerolíneas” llamada name que muestra el nombre de todas las aerolíneas de la base de datos “nyflights13”.

aerolineas_nombre <- aerolineas %>%
  left_join(airlines, by = "carrier")

aerolineas_nombre
## # A tibble: 336,776 × 4
##    carrier origin dest  name                    
##    <chr>   <chr>  <chr> <chr>                   
##  1 UA      EWR    IAH   United Air Lines Inc.   
##  2 UA      LGA    IAH   United Air Lines Inc.   
##  3 AA      JFK    MIA   American Airlines Inc.  
##  4 B6      JFK    BQN   JetBlue Airways         
##  5 DL      LGA    ATL   Delta Air Lines Inc.    
##  6 UA      EWR    ORD   United Air Lines Inc.   
##  7 B6      EWR    FLL   JetBlue Airways         
##  8 EV      LGA    IAD   ExpressJet Airlines Inc.
##  9 B6      JFK    MCO   JetBlue Airways         
## 10 AA      LGA    ORD   American Airlines Inc.  
## # ℹ 336,766 more rows

Paso 4. Se necesita saber la cantidad de vuelos por cada destino para identificar cuáles son los destinos más buscados.

Aquí creamos una nueva variable llamada “cant_vuelos”, en la que utilizamos la función count para analizar la columna de datos carrier perteneciente al conjunto de datos flights.

cant_vuelos <- flights %>% 
  select(carrier, dest) %>% 
  count(carrier)

cant_vuelos  
## # A tibble: 16 × 2
##    carrier     n
##    <chr>   <int>
##  1 9E      18460
##  2 AA      32729
##  3 AS        714
##  4 B6      54635
##  5 DL      48110
##  6 EV      54173
##  7 F9        685
##  8 FL       3260
##  9 HA        342
## 10 MQ      26397
## 11 OO         32
## 12 UA      58665
## 13 US      20536
## 14 VX       5162
## 15 WN      12275
## 16 YV        601
  • Los destinos más buscados son : UA , EV , B6

Paso 5. Agregar el nombre de la aerolínea a la tabla anterior

Aquí agarramos la tabla “cant_vuelos” (que contiene la cantidad de vuelos por cada aerolínea) y la juntamos con la tabla airlines por la columna común carrier utilizando la función de unión left_join, todo esto con el fin de crear una nueva tabla llamada “cant_vuelos_nombre” que contendrá todas las columnas de la tabla original “cant_vuelos” (aerolínea y cantidad de vuelos) y además contendrá una columna adicional de la tabla airlines, que muestra el nombre de la aerolíneas.

cant_vuelos_nombre <- cant_vuelos %>%
  left_join(airlines, by = "carrier")

cant_vuelos_nombre
## # A tibble: 16 × 3
##    carrier     n name                       
##    <chr>   <int> <chr>                      
##  1 9E      18460 Endeavor Air Inc.          
##  2 AA      32729 American Airlines Inc.     
##  3 AS        714 Alaska Airlines Inc.       
##  4 B6      54635 JetBlue Airways            
##  5 DL      48110 Delta Air Lines Inc.       
##  6 EV      54173 ExpressJet Airlines Inc.   
##  7 F9        685 Frontier Airlines Inc.     
##  8 FL       3260 AirTran Airways Corporation
##  9 HA        342 Hawaiian Airlines Inc.     
## 10 MQ      26397 Envoy Air                  
## 11 OO         32 SkyWest Airlines Inc.      
## 12 UA      58665 United Air Lines Inc.      
## 13 US      20536 US Airways Inc.            
## 14 VX       5162 Virgin America             
## 15 WN      12275 Southwest Airlines Co.     
## 16 YV        601 Mesa Airlines Inc.

Paso 6. Se necesita conocer las aerolíneas (clave y nombre) y destinos que vuelan por la Mañana: de 6 a 12, Tarde: de 12 a 19 , Noche: de 19 a 24 y Madrugada de 24 a 6. Agrega un nuevo campo a la tabla con el nombre de clas_horario y agrega, mañana, tarde, noche y madrugada según sea el caso.

Aquí lo primero que hicimos fue una nueva variable llamada “carrier_horario” en la que seleccionamos las columnas carrier, dest y sched_dep_time del conjunto de datos flights, y utilizamos la función (left_join()) para unir esto con la tabla airlines utilizando la columna común “carrier”. Esto agrega el nombre de la aerolínea a la tabla de vuelos. Después creamos una nueva variable llamada “clasxhora” que muestra la misma información que la variable “carrier_horario” pero con una nueva columna que se creó gracias a la función mutate y esta nueva columna nos muestra una etiqueta de horario (Mañana, Tarde, Noche o Madrugada) según la hora programada de salida (sched_dep_time) y siguiendo las especificaciones detalladas con la función ifelse para cada horario en específico.

carrier_horario <- flights %>%
select(carrier, dest, sched_dep_time) %>%
  left_join(airlines, by = "carrier")

clasxhora<- mutate(carrier_horario, clas_horario = ifelse(sched_dep_time %in% 600:1159,"Mañana",
                                               ifelse(sched_dep_time %in% 1200:1859,"Tarde",
                                               ifelse(sched_dep_time %in% 1900:2400,"Noche", "Madrugada"))))

clasxhora
## # A tibble: 336,776 × 5
##    carrier dest  sched_dep_time name                     clas_horario
##    <chr>   <chr>          <int> <chr>                    <chr>       
##  1 UA      IAH              515 United Air Lines Inc.    Madrugada   
##  2 UA      IAH              529 United Air Lines Inc.    Madrugada   
##  3 AA      MIA              540 American Airlines Inc.   Madrugada   
##  4 B6      BQN              545 JetBlue Airways          Madrugada   
##  5 DL      ATL              600 Delta Air Lines Inc.     Mañana      
##  6 UA      ORD              558 United Air Lines Inc.    Madrugada   
##  7 B6      FLL              600 JetBlue Airways          Mañana      
##  8 EV      IAD              600 ExpressJet Airlines Inc. Mañana      
##  9 B6      MCO              600 JetBlue Airways          Mañana      
## 10 AA      ORD              600 American Airlines Inc.   Mañana      
## # ℹ 336,766 more rows

Paso 7.Se necesita saber la cantidad de vuelos por aerolínea y destino que hay por la Mañana, Tarde, Noche y Madrugada

Aquí estamos creando una nueva variable llamada “cant_clasxhora” en la que estamos agrupando los datos de las columnas carrier, dest y clas_horario de la tabla “clasxhora” por medio de la función group_by. Después utilizamos la función count() para contar la cantidad de observaciones en cada grupo.

cant_clasxhora <- clasxhora %>% 
             group_by(carrier, dest,clas_horario) %>% count()

cant_clasxhora
## # A tibble: 741 × 4
## # Groups:   carrier, dest, clas_horario [741]
##    carrier dest  clas_horario     n
##    <chr>   <chr> <chr>        <int>
##  1 9E      ATL   Mañana          59
##  2 9E      AUS   Tarde            2
##  3 9E      AVL   Mañana          10
##  4 9E      BGR   Noche            1
##  5 9E      BNA   Mañana           6
##  6 9E      BNA   Noche            1
##  7 9E      BNA   Tarde          467
##  8 9E      BOS   Mañana         271
##  9 9E      BOS   Noche           94
## 10 9E      BOS   Tarde          549
## # ℹ 731 more rows
  • La tabla indica que la mayor cantidad de vuelos son de la aerolínea “DL” con destino a “ATL” por la tarde.

Paso 8. Se necesita saber a qué destinos vuela la aerolínea American Airlines Inc.-AA durante la madrugada

Aquí estamos creando una variable llamada “destinos_aa” en la que estamos seleccionando con la funcion select las columnas carrier,name,dest y clas_horario de la variable “clasxhora”. Después usamos la función filter para obtener únicamente los resultados AA y Madrugada de la columnas carrier y clas_horario. Finalmente utilizamos la función group_by para que la nueva tabla nos agrupara los datos en este orden : (carrier,name, dest,clas_horario).

destinos_aa <- clasxhora %>% 
  select(carrier,name, dest,clas_horario) %>% 
  filter(carrier == "AA" & clas_horario == "Madrugada") %>% 
  group_by(carrier,name, dest,clas_horario)

destinos_aa
## # A tibble: 365 × 4
## # Groups:   carrier, name, dest, clas_horario [1]
##    carrier name                   dest  clas_horario
##    <chr>   <chr>                  <chr> <chr>       
##  1 AA      American Airlines Inc. MIA   Madrugada   
##  2 AA      American Airlines Inc. MIA   Madrugada   
##  3 AA      American Airlines Inc. MIA   Madrugada   
##  4 AA      American Airlines Inc. MIA   Madrugada   
##  5 AA      American Airlines Inc. MIA   Madrugada   
##  6 AA      American Airlines Inc. MIA   Madrugada   
##  7 AA      American Airlines Inc. MIA   Madrugada   
##  8 AA      American Airlines Inc. MIA   Madrugada   
##  9 AA      American Airlines Inc. MIA   Madrugada   
## 10 AA      American Airlines Inc. MIA   Madrugada   
## # ℹ 355 more rows
  • La tabla indica que la aerolínea American Airlines Inc.-AA solo hace viajes a Miami durante la madrugada.

Paso 9. ¿Qué aviones utiliza la aerolínea AA? aerolínea, tipo, motor y número de asientos y ¿Cuántos vuelos se han realizado con cada uno? elimina los NA’s

Aquí estamos creando una variable llamada “avion_aa” en la que estamos uniendo por medio de la función left_join el conjunto de datos flights y el conjunto de datos planes basados en la columna común tailnum. Después seleccionamos únicamente las columnas de carrier, type, engine, seats con la función select. Con la función filter hicimos que por un lado la tabla solo mostrará los resultados de la aerolínea “AA” y que por otro lado se eliminara todos los NA ’s en la columna type. Más adelante utilizamos la función group_by para agrupar y asignar el orden de las columnas de la siguiente manera (carrier,type,engine,seats). Finalmente se utilizó la función count para saber cuántos vuelos se han realizado con cada uno.

avion_aa <- flights %>%
  left_join(planes, by = "tailnum") %>%
  select(carrier, type, engine, seats) %>%
  filter(carrier == "AA", !is.na(type)) %>%
  group_by(carrier,type,engine,seats) %>% 
  count()

avion_aa 
## # A tibble: 22 × 5
## # Groups:   carrier, type, engine, seats [22]
##    carrier type                    engine        seats     n
##    <chr>   <chr>                   <chr>         <int> <int>
##  1 AA      Fixed wing multi engine Reciprocating     6    22
##  2 AA      Fixed wing multi engine Reciprocating     8    92
##  3 AA      Fixed wing multi engine Reciprocating   102    22
##  4 AA      Fixed wing multi engine Turbo-fan         8    38
##  5 AA      Fixed wing multi engine Turbo-fan        11    19
##  6 AA      Fixed wing multi engine Turbo-fan        22    13
##  7 AA      Fixed wing multi engine Turbo-fan       172  3857
##  8 AA      Fixed wing multi engine Turbo-fan       178   501
##  9 AA      Fixed wing multi engine Turbo-fan       255  4257
## 10 AA      Fixed wing multi engine Turbo-fan       330   450
## # ℹ 12 more rows
  • La tabla indica que la aerolínea AA utiliza principalmente aviones de tipo “Fixed wing multi engine”.
  • La tabla indica que la aerolínea AA utiliza principalmente motores “turbo fan” en sus aviones.
  • La tabla indica que la aerolínea AA utiliza aviones que tienen desde 2 asientos hasta 330 asientos.

¿Qué influye en el retraso de los vuelos?

Ya habiendo analizado de manera descriptiva las tablas de nycflights13, es tiempo de llegar a distintos hallazgos para arribar a una respuesta hacía la cuestión presentada. Para poder hacerlo, realizaremos nuevas databases, dataframes y regresiones lineales.

Variables categóricas

Haremos grupos de las variables categóricas más relevantes del dataframe flights, viendo las frecuencias y los promedios por categoría.

Salidas tardías por aerolínea

Según la tabla proporcionada, parece que el factor “carrier” sí tiene un efecto en la variable “dep_delay”. Aquí hay algunas observaciones:

  1. F9 tiene el retraso promedio más alto de aproximadamente 20.22 minutos.
  2. HA tiene el retraso promedio más bajo de aproximadamente 4.9 minutos.
  3. Portadores como WN y FL tienen altos retrasos promedio de 17.71 y 18.73 minutos respectivamente, y también altos proporción de vuelos retrasados (53.43% y 50.74% respectivamente).
  4. MQ y OO tienen bajos retrasos promedio (10.55 y 12.58 minutos respectivamente) pero sus porcentajes de vuelos retrasados son de los más bajos (30.42% y 28.12% respectivamente).
summary_delay <- bdgrande3 %>%
  group_by(carrier) %>%
  summarise(avg_delay = mean(dep_delay, na.rm = TRUE),
            total_flights = n(),
            total_delayed_flights = sum(dep_delay > 0, na.rm = TRUE),
            perc_delayed_flights = total_delayed_flights / total_flights * 100) %>%
  arrange(desc(perc_delayed_flights))

summary_delay
## # A tibble: 16 × 5
##    carrier avg_delay total_flights total_delayed_flights perc_delayed_flights
##    <chr>       <dbl>         <int>                 <int>                <dbl>
##  1 WN          17.7          12275                  6558                 53.4
##  2 FL          18.7           3260                  1654                 50.7
##  3 F9          20.2            685                   341                 49.8
##  4 UA          12.1          58665                 27261                 46.5
##  5 VX          12.9           5162                  2225                 43.1
##  6 EV          20.0          54173                 23139                 42.7
##  7 B6          13.0          54635                 21445                 39.3
##  8 YV          19.0            601                   233                 38.8
##  9 9E          16.7          18460                  7063                 38.3
## 10 DL           9.26         48110                 15241                 31.7
## 11 AS           5.80           714                   226                 31.7
## 12 AA           8.59         32729                 10162                 31.0
## 13 MQ          10.6          26397                  8031                 30.4
## 14 OO          12.6             32                     9                 28.1
## 15 US           3.78         20536                  4775                 23.3
## 16 HA           4.90           342                    69                 20.2

Salidas tardías por aeropuerto

Según la tabla proporcionada, parece que el factor “origin” sí tiene un efecto en la variable “dep_delay”. Aquí hay algunas observaciones:

  1. EWR tiene el retraso promedio más alto de aproximadamente 15.11 minutos y el porcentaje más alto de vuelos retrasados (43.62%).
  2. JFK tiene un retraso promedio de aproximadamente 12.11 minutos y un 37.77% de sus vuelos están retrasados.
  3. LGA tiene el retraso promedio más bajo de aproximadamente 10.35 minutos y el porcentaje más bajo de vuelos retrasados (32.19%).
summary_delay <- bdgrande3 %>%
  group_by(origin) %>%
  summarise(avg_delay = mean(dep_delay, na.rm = TRUE),
            total_flights = n(),
            total_delayed_flights = sum(dep_delay > 0, na.rm = TRUE),
            perc_delayed_flights = total_delayed_flights / total_flights * 100) %>%
  arrange(desc(perc_delayed_flights))

summary_delay
## # A tibble: 3 × 5
##   origin avg_delay total_flights total_delayed_flights perc_delayed_flights
##   <chr>      <dbl>         <int>                 <int>                <dbl>
## 1 EWR         15.1        120835                 52711                 43.6
## 2 JFK         12.1        111279                 42031                 37.8
## 3 LGA         10.3        104662                 33690                 32.2

Salidas tardías por hora

Según la tabla en la imagen, como la hora. x (que representa diferentes horas del día) aumenta, tanto el avg_delay y perc_delayed_flights generalmente también aumentan. Esto sugiere que los vuelos tienden a retrasarse más tarde en el día, lo que podemos observar en los siguientes data points:

  1. A la hora 5, el retraso promedioes de 0.68 minutos y el porcentaje de vuelos retrasados es del 25.03%.
  2. A la hora 6, el retraso promedio aumenta a 1.64 minutos y el porcentaje de vuelos retrasados aumenta al 20.92%.
  3. A la hora 7, el retraso promedio aumenta aún más a 1.91 minutos y el porcentaje de vuelos retrasados es del 21.74%.
  4. A la hora 8, el retraso promedio aumenta significativamente a 4.12 minutos y el porcentaje de vuelos retrasados aumenta al 24.92%.
  5. A la hora 9, el retraso promedio aumenta a 4.58 minutos y el porcentaje de vuelos retrasados aumenta al 26.54%.
summary_delay <- bdgrande3 %>%
  group_by(hour.x) %>%
  summarise(avg_delay = mean(dep_delay, na.rm = TRUE),
            total_flights = n(),
            total_delayed_flights = sum(dep_delay > 0, na.rm = TRUE),
            perc_delayed_flights = total_delayed_flights / total_flights * 100)

summary_delay
## # A tibble: 20 × 5
##    hour.x avg_delay total_flights total_delayed_flights perc_delayed_flights
##     <dbl>     <dbl>         <int>                 <int>                <dbl>
##  1      1   NaN                 1                     0                  0  
##  2      5     0.688          1953                   489                 25.0
##  3      6     1.64          25951                  5430                 20.9
##  4      7     1.91          22821                  4963                 21.7
##  5      8     4.13          27242                  6790                 24.9
##  6      9     4.58          20312                  5392                 26.5
##  7     10     6.50          16708                  4942                 29.6
##  8     11     7.19          16033                  5034                 31.4
##  9     12     8.61          18181                  6408                 35.2
## 10     13    11.4           19956                  8183                 41.0
## 11     14    13.8           21706                  9257                 42.6
## 12     15    16.9           23888                 11364                 47.6
## 13     16    18.8           23002                 10699                 46.5
## 14     17    21.1           24426                 12132                 49.7
## 15     18    21.1           21783                 10636                 48.8
## 16     19    24.8           21441                 10839                 50.6
## 17     20    24.3           16739                  8633                 51.6
## 18     21    24.2           10933                  5596                 51.2
## 19     22    18.8            2639                  1184                 44.9
## 20     23    14.0            1061                   461                 43.4

Variables numéricas

Identificamos que hay variables númericas en la misma tabla de flights y en la tabla de weather que pueden tener una correlación estadísticamente significativa para predecir las salidas tardías de los vuelos.

Rango intercuartílico en Outliers

Tomaremos como outliers todos aquellos valores antes del primer cuartil y después del tercer cuartil.

retraso <- bdgrande3 %>% select(dep_delay)
summary(retraso)
##    dep_delay      
##  Min.   : -43.00  
##  1st Qu.:  -5.00  
##  Median :  -2.00  
##  Mean   :  12.64  
##  3rd Qu.:  11.00  
##  Max.   :1301.00  
##  NA's   :8255

Según ambos summaries, aquí están algunas diferencias significativas entre los outliers positivos y negativos en relación con las variables climáticas:

Temperatura (temp):.

  • Outliers Positivos: El rango intercuartil (IQR) es de aproximadamente 31.12 (73.92 - 42.80).
  • Outliers Negativos: El rango intercuartil (IQR) es de aproximadamente 29.12 (68.09 - 38.97).

Dirección del viento (wind_dir):

  • Outliers Positivos: El rango intercuartil (IQR) es de aproximadamente 150 (280 - 130).
  • Outliers Negativos: El rango intercuartil (IQR) es de aproximadamente 110 (220 - 110).

Velocidad del viento (wind_speed):

  • Outliers Positivos: El rango intercuartil (IQR) es de aproximadamente 6.5 (14.56 - 8.056).
  • Outliers Negativos: El rango intercuartil (IQR) es de aproximadamente 4 (10 - 6).

Estas diferencias en los rangos intercuartiles entre los outliers positivos y negativos podrían sugerir que las condiciones climáticas más extremas (temperaturas más altas, direcciones de viento más variadas y velocidades de viento más altas) están asociadas con mayores retrasos en los vuelos.

bd_outliers <- filter(bdgrande0, dep_delay > 11)
bd_outliers <- bd_outliers %>% select(-year.x:-hour.y)

bd_mejores <- filter(bdgrande0, dep_delay < -5)
bd_mejores <- bd_mejores %>% select(-year.x:-hour.y)

summary(bd_outliers)
##       temp             dewp           humid           wind_dir    
##  Min.   : 10.94   Min.   :-9.04   Min.   : 12.74   Min.   :  0.0  
##  1st Qu.: 42.80   1st Qu.:28.04   1st Qu.: 45.60   1st Qu.:130.0  
##  Median : 60.08   Median :46.94   Median : 62.05   Median :210.0  
##  Mean   : 58.59   Mean   :44.28   Mean   : 62.47   Mean   :200.5  
##  3rd Qu.: 75.02   3rd Qu.:62.06   3rd Qu.: 80.78   3rd Qu.:280.0  
##  Max.   :100.04   Max.   :78.08   Max.   :100.00   Max.   :360.0  
##  NA's   :415      NA's   :415     NA's   :415      NA's   :2358   
##    wind_speed       wind_gust         precip          pressure    
##  Min.   : 0.000   Min.   :16.11   Min.   :0.0000   Min.   : 985   
##  1st Qu.: 8.056   1st Qu.:20.71   1st Qu.:0.0000   1st Qu.:1011   
##  Median :11.508   Median :24.17   Median :0.0000   Median :1016   
##  Mean   :11.633   Mean   :25.56   Mean   :0.0083   Mean   :1016   
##  3rd Qu.:14.960   3rd Qu.:28.77   3rd Qu.:0.0000   3rd Qu.:1021   
##  Max.   :42.579   Max.   :66.75   Max.   :1.2100   Max.   :1042   
##  NA's   :415      NA's   :59719   NA's   :405      NA's   :12962  
##      visib       
##  Min.   : 0.000  
##  1st Qu.:10.000  
##  Median :10.000  
##  Mean   : 9.008  
##  3rd Qu.:10.000  
##  Max.   :10.000  
##  NA's   :405
summary(bd_mejores)
##       temp            dewp           humid           wind_dir    
##  Min.   :10.94   Min.   :-9.94   Min.   : 12.74   Min.   :  0.0  
##  1st Qu.:42.98   1st Qu.:26.06   1st Qu.: 42.82   1st Qu.:110.0  
##  Median :55.94   Median :41.00   Median : 54.97   Median :220.0  
##  Mean   :56.32   Mean   :39.96   Mean   : 57.00   Mean   :200.7  
##  3rd Qu.:69.98   3rd Qu.:55.04   3rd Qu.: 70.42   3rd Qu.:300.0  
##  Max.   :98.96   Max.   :78.08   Max.   :100.00   Max.   :360.0  
##  NA's   :283     NA's   :283     NA's   :283      NA's   :1987   
##    wind_speed       wind_gust         precip           pressure     
##  Min.   : 0.000   Min.   :16.11   Min.   :0.00000   Min.   : 983.8  
##  1st Qu.: 6.905   1st Qu.:20.71   1st Qu.:0.00000   1st Qu.:1013.7  
##  Median :10.357   Median :24.17   Median :0.00000   Median :1018.5  
##  Mean   :10.671   Mean   :24.79   Mean   :0.00196   Mean   :1018.7  
##  3rd Qu.:13.809   3rd Qu.:27.62   3rd Qu.:0.00000   3rd Qu.:1023.7  
##  Max.   :42.579   Max.   :58.69   Max.   :0.82000   Max.   :1042.1  
##  NA's   :290      NA's   :53685   NA's   :281       NA's   :5665    
##      visib       
##  Min.   : 0.000  
##  1st Qu.:10.000  
##  Median :10.000  
##  Mean   : 9.462  
##  3rd Qu.:10.000  
##  Max.   :10.000  
##  NA's   :281

Regresión Lineal con Weather

Creamos un modelo de regresión lineal con todas las variables del clima de la tabla weather.

flights_weather <- flights %>% left_join(weather, by = c("year", "month", "day", "hour", "origin"))

regresion_weather <- lm(dep_delay ~ temp + dewp + humid + wind_dir + wind_gust + pressure + visib, data =  flights_weather)
summary(regresion_weather)
## 
## Call:
## lm(formula = dep_delay ~ temp + dewp + humid + wind_dir + wind_gust + 
##     pressure + visib, data = flights_weather)
## 
## Residuals:
##    Min     1Q Median     3Q    Max 
## -58.34 -17.08 -11.69   0.02 754.06 
## 
## Coefficients:
##               Estimate Std. Error t value Pr(>|t|)    
## (Intercept) 235.624884  24.153820   9.755  < 2e-16 ***
## temp          0.403213   0.084383   4.778 1.77e-06 ***
## dewp         -0.323405   0.092573  -3.493 0.000477 ***
## humid         0.362956   0.052040   6.975 3.09e-12 ***
## wind_dir     -0.010832   0.002002  -5.410 6.31e-08 ***
## wind_gust     0.232139   0.027706   8.379  < 2e-16 ***
## pressure     -0.234661   0.022344 -10.502  < 2e-16 ***
## visib        -1.637206   0.162541 -10.073  < 2e-16 ***
## ---
## Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
## 
## Residual standard error: 37.82 on 72999 degrees of freedom
##   (263769 observations deleted due to missingness)
## Multiple R-squared:  0.0248, Adjusted R-squared:  0.02471 
## F-statistic: 265.2 on 7 and 72999 DF,  p-value: < 2.2e-16

Matríz de Correlación

flights_weather <- flights %>% left_join(weather, by = c("year", "month", "day", "hour","origin"))
df15 <- flights_weather %>% select(temp,dewp,humid,wind_dir,wind_gust,pressure,visib,dep_delay)
df15 <- replace(df15, is.na(df15), 0)
data(df15)
## Warning in data(df15): data set 'df15' not found
str(df15)
## tibble [336,776 × 8] (S3: tbl_df/tbl/data.frame)
##  $ temp     : num [1:336776] 39 39.9 39 39 39.9 ...
##  $ dewp     : num [1:336776] 28 25 27 27 25 ...
##  $ humid    : num [1:336776] 64.4 54.8 61.6 61.6 54.8 ...
##  $ wind_dir : num [1:336776] 260 250 260 260 260 260 240 260 260 260 ...
##  $ wind_gust: num [1:336776] 0 21.9 0 0 23 ...
##  $ pressure : num [1:336776] 1012 1011 1012 1012 1012 ...
##  $ visib    : num [1:336776] 10 10 10 10 10 10 10 10 10 10 ...
##  $ dep_delay: num [1:336776] 2 4 2 -1 -6 -4 -5 -3 -3 -2 ...
M = cor(df15)

corrplot(M)

El coeficiente r^2 ajustado es de 0.02 para el modelo regresion_weather, lo que significa que las variables climáticas no son estadísticamente significativas para predecir los retrasos en la salida de los vuelos. Podemos probar esto con dos gráficos superpuestos: el negro es el valor real, mientras que el rojo es el predicho por el modelo. No son convergentes en absoluto, ya que el modelo solo puede predecir en un rango menor que el de los valores reales, lo que representa la falta de relación.
A pesar de la inclusión de variables climáticas en un modelo de regresión lineal, encontramos que estas no son estadísticamente significativas para predecir los retrasos en las salidas de vuelos. Esto sugiere que otros factores pueden estar influyendo en los retrasos de los vuelos, como los problemas operativos internos de las aerolíneas o los aeropuertos.

predicted_values <- predict(regresion_weather, newdata = flights_weather)

comparison <- data.frame(Actual = flights_weather$dep_delay, Predicted = predicted_values)

registro <- 1:nrow(comparison) # Crear un vector de números de registro

comparison$Registro <- registro # Agregar el vector de números de registro al dataframe comparison

# Crear un gráfico de líneas para los valores reales
p1 <- ggplot(comparison, aes(x = Registro, y = Actual)) +
  geom_line(color = "black") +
  labs(x = "Número de registro", y = "Retraso (minutos)", title = "Retraso real vs predicho") +
  theme_minimal()

# Crear un gráfico de líneas para los valores predichos
p2 <- ggplot(comparison, aes(x = Registro, y = Predicted)) +
  geom_line(color = "red") +
  theme_minimal()

# Superponer los dos gráficos
final_plot <- p1 + geom_line(data = p2$data, aes(x = Registro, y = Predicted), color = "red")
final_plot

Serie de tiempo con Flights

Descompusimos por mes una serie de tiempo entre date y el promedio de dep_delay, por lo que se sacó el efecto estacional mensual. Con este gráfico podemos observar un componente estacional en el que se producen mayores retrasos de salida en los meses de:

  • Abril
  • Julio y Agosto
  • Noviembre y Diciembre
# Promedio de dep_delay por fecha
flights1 <- flights %>%
  mutate(date = make_date(year, month, day)) %>%
  group_by(date) %>%
  summarise(dep_delay = mean(dep_delay, na.rm = TRUE))

flights1 <- na.omit(flights1)

# Objeto de serie de tiempo con frecuencia mensual
ts_flights <- ts(flights1$dep_delay, start = c(2013, 1), frequency = 12)

# Descomponer la serie de tiempo de manera periódica
decomposed <- stl(ts_flights, s.window="periodic")

# Crear un data frame con las temporadas y fechas
seasonal_component <- decomposed$time.series[, "seasonal"]
dates <- seq(as.Date("2013-01-01"), by = "month", length.out = length(seasonal_component))

df_seasonal <- data.frame(Date = dates, Seasonal = seasonal_component)
df_seasonal$Month <- format(df_seasonal$Date, "%B")

# Calcular el efecto estacional medio para cada mes
avg_seasonal <- aggregate(Seasonal ~ Month, df_seasonal, mean)

avg_seasonal$Month <- factor(avg_seasonal$Month, levels = month.name)

# Gráficar
ggplot(avg_seasonal, aes(x = Month, y = Seasonal)) +
  geom_bar(stat = "identity") +
  labs(x = "Mes", y = "Componente estacional medio", 
       title = "Efecto estacional promedio por mes")

Meses

Al ver ya visto que con las variables climáticas no hay una relación significativa, y vemos que si hay un efecto estacional, podemos potencialmente atribuir los cambios a un cambio de demanda de vuelos.
Los meses con mayor demanda julio y agosto coinciden con el efecto estacional en retrasos que tienen. No obstante, octubre y marzo son tercero y cuarto en demanda y esto no se ve reflejado en el efecto estacional.
Esto nos lleva a concluir que hay otros factores, fuera de la demanda, que ocasionan este efecto estacional de retrasos.
No obstante, los meses con mayor proporción de retrasos son los meses vacacionales junio, julio, diciembre.

summary_delay <- bdgrande3 %>%
  group_by(month.x) %>%
  summarise(avg_delay = mean(dep_delay, na.rm = TRUE),
            total_flights = n(),
            total_delayed_flights = sum(dep_delay > 0, na.rm = TRUE),
            perc_delayed_flights = total_delayed_flights / total_flights * 100) %>%
  arrange(desc(total_flights))

summary_delay
## # A tibble: 12 × 5
##    month.x avg_delay total_flights total_delayed_flights perc_delayed_flights
##      <int>     <dbl>         <int>                 <int>                <dbl>
##  1       7     21.7          29425                 13909                 47.3
##  2       8     12.6          29327                 11713                 39.9
##  3      10      6.24         28889                  8722                 30.2
##  4       3     13.2          28834                 11209                 38.9
##  5       5     13.0          28796                 11291                 39.2
##  6       4     13.9          28330                 10543                 37.2
##  7       6     20.8          28243                 12655                 44.8
##  8      12     16.6          28135                 13550                 48.2
##  9       9      6.72         27574                  7815                 28.3
## 10      11      5.44         27268                  8239                 30.2
## 11       1     10.0          27004                  9662                 35.8
## 12       2     10.8          24951                  9124                 36.6
flights_filtered <- flights %>%
  mutate(delayed = ifelse(dep_delay > 0, "Delayed", "Not Delayed")) %>%
  group_by(month, delayed) %>%
  summarise(count = n()) %>%
  mutate(percent = count / sum(count) * 100)
## `summarise()` has grouped output by 'month'. You can override using the
## `.groups` argument.
ggplot(flights_filtered, aes(x = factor(month), y = count, fill = delayed)) +
  geom_bar(stat = "identity", position = "stack") +
  scale_fill_manual(values = c("red", "green")) +
  labs(title = "Número de vuelos por mes con y sin retraso",
       x = "Mes",
       y = "Número de vuelos",
       fill = "Retraso") +
  theme_minimal()

Conclusión General

  • A medida que aumenta la hora del día, tanto el retraso medio como el porcentaje de vuelos retrasados suelen aumentar. Esto podría explicarse por la acumulación de retrasos a lo largo del día, las condiciones climáticas cambiantes o la mayor congestión del tráfico aéreo en las horas pico. Un ejemplo es que a las 5 am, el retraso promedio es de 0.68 minutos y el porcentaje de 25.03%; a las 5 pm el retraso es de 21.1 minutos y el porcentaje es de 49.6%.

  • Los vuelos que salen en meses de alta demanda tienen más retrasos porque al subir más demanda podría haber complejidad en las operaciones y mayor tráfico. Al analizar los datos se encontró que en julio, el 47% de los vuelos tienen retraso y, a su vez, es el mes con más alta demanda; en febrero, solamente el 36% de los vuelos son retrasados.

  • Las aerolíneas tienen un impacto significativo en los retrasos en los vuelos ya que la disponibilidad de personal y la demanda de viajes varían por cada una. Esto se puede ver como en Southwest Airlines Co. se retrasó el 53.4% de los vuelos (teniendo 12 mil vuelos totales) mientras que Hawaiian Airlines Inc. solo se retrasó el 20.2% (con 342 vuelos).

  • En cuestión de aeropuertos, entre mayor cantidad de vuelos totales salgan del aeropuerto, habrá una proporción más alta de retrasos ya que la capacidad de gestión del tráfico aéreo es dependiente de la demanda. Se puede presenciar este fénomeno en como el Aeropuerto Internacional de Newark (EWR) , el de mayor vuelos totales, tiene una proporción de 43.6% de retrasos; Aeropuerto Internacional de La Guardia (LGA), el de menos vuelos toales, solo tiene 32.2% de retrasos.

  • En la noche se presenta la mayor proporción de retrasos en comparación con la mañana debido a que hay menos personal disponible (la demanda no esta relacionada a esto). Se observa en cómo la proporción de retrasos llega a un punto caliente entre las horas 7 a 9 pm con 50.5% y 51.2% de retrasos respectivamente.

Compromiso ético y ciudadano (Adrián)

Para mi la integridad es actuar de forma responsable, respetuosa y honesta en todo momento con cualquier persona o entidad con la que interactuemos. Al momento de hacer negocios con otras personas siempre debemos actuar de forma íntegra con los medios que la otra parte nos está brindando ya que el mal uso por nuestra parte de estos datos o información se puede prestar a futuras discusiones o demandas en caso de que estos datos sean muy importantes/confidenciales. Al momento de tener datos de otras empresas, debemos comprometernos a mantenerlos privados en caso de que se nos solicite hacer esto y sólo utilizarlos de forma íntegra para realizar nuestros análisis deseados.

Compromiso ético y ciudadano (Anna)

En resumen, tener acceso a la información de una empresa conlleva una gran responsabilidad que debe manejarse con integridad y ética profesional. En el contexto del análisis de datos de vuelos, esto implica utilizar la información de manera responsable, garantizando la privacidad y confidencialidad de los datos, así como asegurando que los análisis y conclusiones se basen en metodologías sólidas y éticas.
Además, significa mantener un código ético en la manipulación y presentación de los resultados, asegurando que la información se utilice para mejorar la toma de decisiones y generar valor para la empresa y sus grupos de interés, en lugar de para fines cuestionables o perjudiciales.

Compromiso ético y ciudadano (Dario)

El tener integridad y honestidad a la hora de manejar datos en una empresa es de suma importancia y responsabilidad, los datos son algo que actualmente y en el futuro son bien cotizados y que se le está dando reconocimiento debido a que las empresas logran satisfactoriamente sus objetivos, pero para tener mayor base y ser profesionales al trabajar siempre hay que conducirse con integridad, honestidad y ética

LS0tDQp0aXRsZTogIkV2aWRlbmNpYXMgMSB5IDIiDQphdXRob3I6ICJBbm5hIER1csOhbiBBMDEyODU2NzQgfCBTYW50aWFnbyBHYWxsYXJkbyBBMDEyODU3MzYgfCBBZHJpw6FuIEdhcnphIEEwMTI4NTQxMiINCmRhdGU6ICIyMDI0LTAyLTI2Ig0Kb3V0cHV0Og0KICBodG1sX2RvY3VtZW50Og0KICAgIHRvYzogdHJ1ZQ0KICAgIHRvY19mbG9hdDogdHJ1ZQ0KICAgIGNvZGVfZG93bmxvYWQ6IHRydWUNCiAgICB0aGVtZTogY29zbW8NCi0tLQ0KIVtdKC9Vc2Vycy9hbm5hZHVyYW4vTGlicmFyeS9DbG91ZFN0b3JhZ2UvT25lRHJpdmUtSW5zdGl0dXRvVGVjbm9sb2dpY295ZGVFc3R1ZGlvc1N1cGVyaW9yZXNkZU1vbnRlcnJleS80VE8gU0VNL01hbmlwdWxhY2nDs24gZGUgZGF0b3MvUnN0dWRpby9wbGFuZS5naWYpDQoNCg0KIyA8c3BhbiBzdHlsZSA9ICJjb2xvcjogQmx1ZTsiPkRhdGEgV3JhbmdsaW5nPC9zcGFuPg0KDQojIyA8c3BhbiBzdHlsZSA9ICJjb2xvcjogZ3JlZW47Ij5Db250ZXh0bzwvc3Bhbj4NCg0KRWwgcGFxdWV0ZSAqKm55Y2ZsaWdodHMxMyoqIGNvbnRpZW5lIGluZm9ybWFjacOzbiBzb2JyZSB0b2RvcyBsb3MgdnVlbG9zIHF1ZSBwYXJ0aWVyb24gZGVzZGUgTnVldmEgWW9yayAoRVdSLCBKRksgeSBMR0EpIGEgZGVzdGlub3MgZW4gbG9zIEVzdGFkb3MgVW5pZG9zIGVuIDIwMTMuIEZ1ZXJvbiAzMzYsNzc2IHZ1ZWxvcyBlbiB0b3RhbC4gUGFyYSBheXVkYXIgYSBjb21wcmVuZGVyIGxhcyBjYXVzYXMgZGUgbG9zIHJldHJhc29zLCB0YW1iacOpbiBpbmNsdXllIG90cm9zIGRlIGRhdG9zIMO6dGlsZXMuIFJlc3BvbmRlciBhIGxhIHByZWd1bnRhIMK/UG9yIHF1w6kgbG9zIHJldHJhc29zPyAgDQoNCkVzdGUgcGFxdWV0ZSBpbmNsdXllIGxhcyBzaWd1aWVudGVzIHRhYmxhczogIA0KDQorIGZsaWdodHMgPSB0b2RvcyBsb3MgdnVlbG9zIHF1ZSBzYWxpZXJvbiBkZSBOdWV2YSBZb3JrIGVuIDIwMTMgIA0KKyB3ZWF0aGVyID0gZGF0b3MgbWV0ZXJlb2zDs2dpY29zIHBvciBob3JhIGRlIGNhZGEgYWVyb3B1ZXJ0byAgDQorIHBsYW5lcyA9IGluZm9ybWFjacOzbiBkZSBjb25zdHJ1Y2Npw7NuIGRlIGNhZGEgYXZpw7NuICANCisgYWlycG9ydHMgPSBub21icmVzIHkgdWJpY2FjaW9uZXMgZGUgYWVyb3B1ZXJ0b3MgIA0KKyBhaXJsaW5lcyA9IHJlbGFjacOzbiBlbnRyZSBub21icmVzIHkgY8OzZGlnb3MgZGUgbGFzIGFlcm9sw61uZWFzICANCg0KW0Z1ZW50ZV0oaHR0cHM6Ly9jcmFuLnItcHJvamVjdC5vcmcvd2ViL3BhY2thZ2VzL255Y2ZsaWdodHMxMy9pbmRleC5odG1sKQ0KDQojIyA8c3BhbiBzdHlsZT0iY29sb3I6IGdyZWVuOyI+SW5zdGFsYXIgcGFxdWV0ZXMgeSBsbGFtYXIgbGlicmVyw61hczwvc3Bhbj4NCmBgYHtyfQ0KIyBpbnN0YWxsLnBhY2thZ2VzKCJueWNmbGlnaHRzMTMiKQ0KbGlicmFyeShueWNmbGlnaHRzMTMpDQojIGluc3RhbGwucGFja2FnZXMoInRpZHl2ZXJzZSIpDQpsaWJyYXJ5KHRpZHl2ZXJzZSkNCmxpYnJhcnkoZHBseXIpDQpsaWJyYXJ5KHNjYWxlcykNCmxpYnJhcnkoZ2dwbG90MikNCmxpYnJhcnkoY29ycnBsb3QpDQpsaWJyYXJ5KGx1YnJpZGF0ZSkNCmBgYA0KDQojIyA8c3BhbiBzdHlsZT0iY29sb3I6IGdyZWVuOyI+R3VhcmRhciBiYXNlcyBkZSBkYXRvczwvc3Bhbj4NCmBgYHtyfQ0KZmxpZ2h0cyA8LSBmbGlnaHRzDQp3ZWF0aGVyIDwtIHdlYXRoZXINCnBsYW5lcyA8LSBwbGFuZXMNCmFpcnBvcnRzIDwtIGFpcnBvcnRzDQphaXJsaW5lcyA8LSBhaXJsaW5lcyANCmBgYA0KDQojIyA8c3BhbiBzdHlsZT0iY29sb3I6IGdyZWVuOyI+UmVsYWNpb24gZW50cmUgYmFzZSBkZSBkYXRvczwvc3Bhbj4NCg0KIVtdKC9Vc2Vycy9hbm5hZHVyYW4vTGlicmFyeS9DbG91ZFN0b3JhZ2UvT25lRHJpdmUtSW5zdGl0dXRvVGVjbm9sb2dpY295ZGVFc3R1ZGlvc1N1cGVyaW9yZXNkZU1vbnRlcnJleS80VE8gU0VNL01hbmlwdWxhY2nDs24gZGUgZGF0b3MvUnN0dWRpby9yZWxhdGlvbmFsLW55Y2ZsaWdodHMucG5nKQ0KDQojIyA8c3BhbiBzdHlsZT0iY29sb3I6IGdyZWVuOyI+RnVuY2lvbmVzIELDoXNpY2FzIGRlIE1hbmVqbyBkZSBEYXRvczwvc3Bhbj4NCg0KIyMjIDxzcGFuIHN0eWxlPSJjb2xvcjogZGFya3Zpb2xldDsiPlNlbGVjdDwvc3Bhbj4NCkxhIGZ1bmNpw7NuICpzZWxlY3QqIHNpcnZlIHBhcmEgc2VsZWNjaW9uYXIgY29sdW1uYXMgZGUgdW5hIHRhYmxhICgqZGF0YSBmcmFtZSopLg0KYGBge3J9DQpkZjEgPC0gZmxpZ2h0cyAlPiUgc2VsZWN0KGNhcnJpZXIsIGZsaWdodCkgIyBTZWxlY2Npw7NuIGRlIGNvbHVtbmFzIGVzcGVjw61maWNhcw0KZGYyIDwtIGZsaWdodHMgJT4lIHNlbGVjdChjYXJyaWVyOmRpc3RhbmNlKSAjIFNlbGVjY2nDs24gZGUgcmFuZ28gZGUgY29sdW1uYXMgDQpkZjMgPC0gZmxpZ2h0cyAlPiUgc2VsZWN0KC1jYXJyaWVyLCAtZmxpZ2h0KSAjIEVsaW1pbmFyIGNvbHVtbmFzIGVzcGVjw61maWNhcyANCmRmNCA8LSBmbGlnaHRzICU+JSBzZWxlY3QoLWNhcnJpZXI6LWRpc3RhbmNlKSAjIEVsaW1pbmFyIHJhbmdvIGRlIGNvbHVtbmFzIA0KZGY1IDwtIGZsaWdodHMgJT4lIHNlbGVjdChhZXJvbGluZWEgPSBjYXJyaWVyKSAjIFNlbGVjY2lvbmEgdW5hIGNvbHVtbmEgeSBsZSBjYW1iaWEgZWwgbm9tYnJlIA0KZGY2IDwtIGZsaWdodHMgJT4lIHJlbmFtZShhZXJvbGluZWEgPWNhcnJpZXIpICMgQ2FtYmlhIGVsIG5vbWJyZSBhIHVuYSBjb2x1bW5hIA0KYGBgDQoNCiMjIyA8c3BhbiBzdHlsZT0iY29sb3I6IGRhcmt2aW9sZXQ7Ij5GaWx0ZXI8L3NwYW4+DQpMYSBmdW5jacOzbiAqZmlsdGVyKiBzaXJ2ZSBwYXJhIHNlbGVjY2lvbmFyIHJlbmdsb25lcyBkZSB1bmEgdGFibGEgKCpkYXRhIGZyYW1lKikuDQpMYSBmdW5jacOzbiAqc2xpY2UqIHNpcnZlIHBhcmEgc2VsZWNjaW9uYXIgZmlsYXMgZW4gZXNwZWNpZmljbyAoKmRhdGEgZnJhbWUqKS4NCmBgYHtyfQ0KZGY3IDwtIGZsaWdodHMgJT4lIGZpbHRlcihkZXBfZGVsYXkgPj01MDApICMgRXh0cmFlIHJlbmdsb25lcyBxdWUgY3VtcGxhbiBjb25kaWNpw7NuIA0KDQojIElndWFsID09ICwgRGVzaWd1YWwgPSE9ICwgTWF5b3IgcXVlID4gLCBNYXlvciBvIGlndWFsIHF1ZSA+PSAsIE1lbm9yIHF1ZSA8ICwgTWVub3IgbyBpZ3VhbCBxdWUgPD0gDQojIE9wZXJhZG9yZXMgbMOzZ2ljb3M6IEFORCAmLCBPUiB8ICwgTk9UICEgDQoNCg0KZGY4IDwtIGZsaWdodHMgJT4lIGZpbHRlcihkZXBfZGVsYXkgPj01MDAsZGVwX2RlbGF5PDYwMCkgIyBFeHRyYWUgcmVuZ2xvbmVzIHF1ZSBjdW1wbGFuIGRvcyBjb25kaWNpb25lcyANCmRmOSA8LSBmbGlnaHRzICU+JSBzbGljZSgxMDAwOjEwOTkpICMgRXh0cmFlIGxvcyBuw7ptZXJvcyBkZSBsb3MgcmVuZ2xvbmVzIGluZGljYWRvcywgc2luIGltcG9ydGFyIHN1cyB2YWxvcmVzIA0KYGBgDQoNCiMjIyA8c3BhbiBzdHlsZT0iY29sb3I6IGRhcmt2aW9sZXQ7Ij5EaXN0aW5jdDwvc3Bhbj4NCkxhIGZ1bmNpw7NuICpkaXN0aW5jdCogc2lydmUgcGFyYSBlbGltaW5hciByZW5nbG9uZXMgZHVwbGljYWRvcy4NCmBgYHtyfQ0KZGYxMCA8LSBkaXN0aW5jdChmbGlnaHRzKSAjIERlamFyIHPDs2xvIGxvcyByZW5nbG9uZXMgZGlmZXJlbnRlcywgYm9ycmEgdG9kb3MgbG9zIHJlcGV0aWRvcyANCmBgYA0KDQojIyMgPHNwYW4gc3R5bGU9ImNvbG9yOiBkYXJrdmlvbGV0OyI+TWVyZ2U8L3NwYW4+DQpMYSBmdW5jacOzbiAqbWVyZ2UqIHNpcnZlIHBhcmEganVudGFyIGJhc2VzIGRlIGRhdG9zLg0KYGBge3J9DQpiZGdyYW5kZSA8LSBtZXJnZShmbGlnaHRzLCBhaXJsaW5lcywgYnkgPSAiY2FycmllciIpICMgY2FycmllciBlcyBsYSBjb2x1bW5hIGxsYXZlDQpiZGdyYW5kZTAgPC0gbGVmdF9qb2luKGZsaWdodHMsIHdlYXRoZXIsIGJ5ID0gYygib3JpZ2luIiwgInRpbWVfaG91ciIpKQ0KYmRncmFuZGUxIDwtIGxlZnRfam9pbihiZGdyYW5kZSwgcGxhbmVzLCBieSA9ICJ0YWlsbnVtIikgIyB0b2RhIHRhYmxhIGJkZ3JhbmRlIHkgc29sbyBjb2luY2lkZW5jaWFzIGRlIHBsYW5lcw0KYmRncmFuZGUyIDwtIG11dGF0ZShiZGdyYW5kZTEsIGRpc3Rfa20gPSBkaXN0YW5jZSoxLjYwOSkNCmJkZ3JhbmRlMyA8LSBsZWZ0X2pvaW4oYmRncmFuZGUyLCB3ZWF0aGVyLCBieSA9IGMoIm9yaWdpbiIsICJ0aW1lX2hvdXIiKSkNCmBgYA0KDQojIyMgPHNwYW4gc3R5bGU9ImNvbG9yOiBkYXJrdmlvbGV0OyI+RWplcmNpY2lvczwvc3Bhbj4NCjEuIEVuY3VlbnRyYSB0b2RvcyBsb3MgdnVlbG9zIHF1ZSB0dXZpZXJvbiB1biBhdHJhc28gZW4gbGxlZ2FkYSBkZSAyIGhvcmFzIG8gbcOhcyANCmBgYHtyfQ0KZWplcmNpY2lvMSA8LSBiZGdyYW5kZTIgJT4lIGZpbHRlcihhcnJfZGVsYXk+PTEyMCkNCmBgYA0KDQoyLiBFbmN1ZW50cmEgdG9kb3MgbG9zIHZ1ZWxvcyBxdWUgbGxlZ2Fyb24gYSBIb3VzdG9uIChJQUggbyBIT1UpDQpgYGB7cn0NCmVqZXJjaWNpbzIgPC0gYmRncmFuZGUyICU+JSBmaWx0ZXIoZGVzdCA9PSAiSUFIIiB8IGRlc3QgPT0gIkhPVSIpDQpgYGANCg0KMy5FbmN1ZW50cmEgdG9kb3MgbG9zIHZ1ZWxvcyBvcGVyYWRvcyBwb3IgVW5pdGVkLCBBbWVyaWNhbiBvIERlbHRhDQpgYGB7cn0NCmVqZXJjaWNpbzMgPC0gYmRncmFuZGUyICU+JSBmaWx0ZXIoY2FycmllciAlaW4lIGMoIlVBIiAsICJBQSIgLCAiREwiKSkNCmBgYA0KDQpFamVyY2ljaW8gNC4gRW5jdWVudHJhIHRvZG9zIGxvcyB2dWVsb3MgcXVlIGRlc3BlZ2Fyb24gZW4gSnVsaW8sIEFnb3N0byBvIFNlcHRpZW1icmUuDQpgYGB7cn0NCmVqZXJjaWNpbzRhIDwtIGJkZ3JhbmRlMiAlPiUgZmlsdGVyKG1vbnRoID09IDcgfCBtb250aCA9PSA4IHwgbW9udGggPT0gOSkNCg0KZWplcmNpY2lvNGIgPC0gYmRncmFuZGUyICU+JSBmaWx0ZXIobW9udGggJWluJSBjKDc6OSkpDQpgYGANCg0KRWplcmNpY2lvIDUuIEVuY3VlbnRyYSB0b2RvcyBsb3MgdnVlbG9zIHF1ZSBhcnJpdmFyb24gbcOhcyBkZSAyIGhvcmFzIHRhcmRlLCBwZXJvIG5vIGRlc3BlZ2Fyb24gdGFyZGUuDQpgYGB7cn0NCmVqZXJjaWNpbzUgPC0gYmRncmFuZGUyICU+JSBmaWx0ZXIoYXJyX2RlbGF5PjEyMCAmIGRlcF9kZWxheTw9MCkNCmBgYA0KDQpFamVyY2ljaW8gNi4gRW5jdWVudHJhIHRvZG9zIGxvcyB2dWVsb3MgcXVlIHNlIHJldHJhc2Fyb24gYWwgbWVub3MgMSBob3JhLCBwZXJvIHF1ZSBsbGVnYXJvbiBhbnRlcyAzMCBtaW51dG9zIG8gbcOhcy4NCmBgYHtyfQ0KZWplcmNpY2lvNiA8LSBiZGdyYW5kZTIgJT4lIGZpbHRlcihkZXBfZGVsYXk+PTYwICYgYXJyX2RlbGF5PD0tMzApDQpgYGANCg0KRWplcmNpY2lvIDcuIEVuY3VlbnRyYSB0b2RvcyBsb3MgdnVlbG9zIHF1ZSBzYWxpZXJvbiBlbnRyZSBsYSBtZWRpYW5vY2hlIHkgbGFzIDYgYS5tLiANCmBgYHtyfQ0KZWplcmNpY2lvNyA8LSBiZGdyYW5kZTIgJT4lIGZpbHRlcihkZXBfdGltZSA9PSAyNDAwIHwgZGVwX3RpbWUgPD0gNjAwKQ0KYGBgDQoNCiMjIyA8c3BhbiBzdHlsZT0iY29sb3I6IGRhcmt2aW9sZXQ7Ij5BcnJhbmdlPC9zcGFuPg0KU2ltaWxhciBhIGZpbHRlcigpIHBlcm8gZW4gbHVnYXIgZGUgc2VsZWNjaW9uYXIgcmVuZ2xvbmVzLCBsb3Mgb3JkZW5hIGRlIG1lbm9yIGEgbWF5b3IuDQpgYGB7cn0NCmRmMTEgPC0gYXJyYW5nZShiZGdyYW5kZTIseWVhci54LG1vbnRoLGRheSwpDQpgYGANCg0KcGFyYSBhY29tb2RhciBkZSBtYXlvciBhIG1lbm9yLiANCmBgYHtyfQ0KZGYxMiA8LSBhcnJhbmdlKGJkZ3JhbmRlMix5ZWFyLngsZGVzYyhtb250aCksZGF5KQ0KYGBgDQoNCiMjIyA8c3BhbiBzdHlsZT0iY29sb3I6IGRhcmt2aW9sZXQ7Ij5TdW1tYXJpemU8L3NwYW4+DQpDb2xhcHNhIHVuYSB0YWJsYSBhIHVuIHPDs2xvIHJlbmdsw7NuIA0KYGBge3J9DQojIE9idMOpbiBlbCByZXRyYXNvIHByb21lZGlvIGRlIGRlc3BlZ3VlIGRlIHRvZG9zIGxvcyB2dWVsb3MgDQpzdW1tYXJpemUoYmRncmFuZGUyLG1lYW4oZGVwX2RlbGF5LCBuYS5ybT1UUlVFKSkNCmBgYA0KDQojIyMgPHNwYW4gc3R5bGU9ImNvbG9yOiBkYXJrdmlvbGV0OyI+R3JvdXAgYnk8L3NwYW4+DQpBZ3J1cGEgdGFibGEgYmFzYWRvIGVuIGFsZ3VuYXMgY29sdW1uYXMNCmBgYHtyfQ0KIyBPYnRlbmVyIGVsIHJldHJhc28gcHJvbWVkaW8gZGUgZGVzcGVndWUgcG9yIGTDrWEgDQpwb3JfZGlhIDwtIGdyb3VwX2J5KGJkZ3JhbmRlMiwgeWVhci54LG1vbnRoLGRheSkNCnN1bW1hcml6ZShwb3JfZGlhLG1lYW4oZGVwX2RlbGF5LCBuYS5ybT1UUlVFKSkNCmBgYA0KDQojIDxzcGFuIHN0eWxlID0gImNvbG9yOiBibHVlOyI+QXZhbmNlIDE8L3NwYW4+DQoNCiMjIDxzcGFuIHN0eWxlID0gImNvbG9yOiBncmVlbjsiPkd1YXJkYXIgYmFzZXMgZGUgZGF0b3M8L3NwYW4+DQpgYGB7cn0NCmZsaWdodHMgPC0gZmxpZ2h0cw0Kd2VhdGhlciA8LSB3ZWF0aGVyDQpwbGFuZXMgPC0gcGxhbmVzDQphaXJwb3J0cyA8LSBhaXJwb3J0cw0KYWlybGluZXMgPC0gYWlybGluZXMNCmBgYA0KDQojIyA8c3BhbiBzdHlsZSA9ICJjb2xvcjogZ3JlZW47Ij5DYXJnYXIgZW4gbWVtb3JpYSBsYSB0YWJsYSBmbGlnaHRzIHkgbW9zdHJhciBzdSBjb250ZW5pZG88L3NwYW4+DQpPYnNlcnZhbW9zIHF1ZSBsYSB0YWJsYSAqKmZsaWdodHMqKiBub3MgZGFuIGxhcyB2YXJpYWJsZXM6ICANCg0KKyAqKnllYXIqKiwgKiptb250aCoqLCAqKmRheSoqLCAqKmhvdXIqKiwgKiptaW51dGUqKiB5ICoqdGltZV9ob3VyKiogcGFyYSBsYSBmZWNoYSBkZWwgdnVlbG8uIA0KKyAqKnNjaGVkX2RlcF90aW1lKiogeSAqKnNjaGVkX2Fycl90aW1lKiogcGFyYSBsYSBob3JhIHkgbWludXRvIGFnZW5kYWRhcyBkZSBzYWxpZGEgeSBsbGVnYWRhLiAgDQorICoqZGVwX3RpbWUqKiB5ICoqYXJyX3RpbWUqKiBwYXJhIGxhIGhvcmEgeSBtaW51dG8gZGUgc2FsaWRhIHkgbGxlZ2FkYS4gIA0KKyAqKmRlcF9kZWxheSoqIHkgKiphcnJfZGVsYXkqKiBkYW5kbyBsYSBkaWZlcmVuY2lhIGVudHJlIGxhIGhvcmEgYWdlbmRhZGEgeSBsYSByZWFsLiAgDQorICoqY2FycmllcioqLCAqKmZsaWdodCoqIHkgKip0YWlsbnVtKiogcXVlIGVzIGxhIGFlcm9sw61uZWEsIGVsIHZ1ZWxvLCB5IG7Dum1lcm8gZGUgY29sYS4gIA0KKyAqKm9yaWdpbioqIHkgKipkZXN0Kiogb2ZyZWNlbiBlbiBjYXRlZ29yw61hcyBjaXVkYWRlcyBkZSBvcmlnZW4geSBkZXN0aW5vLiAgDQorICoqYWlyX3RpbWUqKiBkYSBsYXMgaG9yYXMgeSBtaW51dG9zIHF1ZSB0YXJkbyBlbCB2dWVsby4gIA0KKyAqKmRpc3RhbmNlKiogZGEgbGEgZGlzdGFuY2lhIGVuIG1pbGxhcy4NCmBgYHtyfQ0KZmxpZ2h0cyA8LSBmbGlnaHRzDQpoZWFkKGZsaWdodHMpDQp0YWlsKGZsaWdodHMpDQpgYGANCg0KIyMgPHNwYW4gc3R5bGUgPSAiY29sb3I6IGdyZWVuOyI+Q29uc3VsdGEgbGEgZXN0cnVjdHVyYSBkZSBmbGlnaHRzPC9zcGFuPg0KDQojIyMgPHNwYW4gc3R5bGUgPSAiY29sb3I6IGRhcmt2aW9sZXQ7Ij5UaXBvcyBkZSB2YXJpYWJsZXM8L3NwYW4+DQpPYnNlcnZhbW9zIHF1ZSBlbiBsYSBlc3RydWN0dXJhIGRlICoqZmxpZ2h0cyoqIGxhcyB2YXJpYWJsZXMgcHVlZGVuIGRpdmlkaXJzZSBlbjogIA0KDQorICoqTsO6bWVyaWNvcyoqOiBoYXkgdmFyaWFibGVzIHRpcG8gKmludGVnZXIqIChlbnRlcm9zKSB5ICpuw7ptZXJpY29zKiAoZW4gbmVnYXRpdm8geSBkZWNpbWFsZXMpDQorICoqQ2F0ZWfDs3JpY29zKio6IGhheSB2YXJpYWJsZXMgdGlwbyAqY2hhcmFjdGVyKiAgDQorICoqVGVtcG9yYWxlcyoqOiBleGlzdGUgdW5hIHZhcmlhYmxlcyB0aXBvICpQT1NJWGN0KiBjb24gZmVjaGEgeSBob3JhDQpgYGB7cn0NCnN0cihmbGlnaHRzKQ0KYGBgDQojIyMgPHNwYW4gc3R5bGUgPSAiY29sb3I6IGRhcmt2aW9sZXQ7Ij5DbGFzZSBkZSBGbGlnaHRzPC9zcGFuPg0KUG9kZW1vcyBpZGVudGlmaWNhciBxdWUgKipmbGlnaHRzKiogKHksIHBvciBjb25zZWN1ZW50ZSwgbGFzIG90cm9zIDMgb2JqZXRvcyBlbiBsYSBsaWJyZXLDrWEgKipueWNmbGlnaHRzMTMqKikgZXMgdW4gdGlwbyBkZSBvYmpldG8gKmRhdGEgZnJhbWUqDQpgYGB7cn0NCmNsYXNzKGZsaWdodHMpDQpgYGANCg0KIyMjIDxzcGFuIHN0eWxlID0gImNvbG9yOiBkYXJrdmlvbGV0OyI+RGltZW5zaW9uZXMgZGUgRmxpZ2h0czwvc3Bhbj4NClRpZW5lIHVuIHRvdGFsIGRlICoqMzM2Nzc2IHJlbmdsb25lcyoqIGNvbiAqKjE5IHZhcmlhYmxlcyoqLg0KYGBge3J9DQpkaW0oZmxpZ2h0cykNCmBgYA0KDQojIyMgPHNwYW4gc3R5bGUgPSAiY29sb3I6IGRhcmt2aW9sZXQ7Ij5SZXN1bHRhZG9zIGRlc2NyaXB0aXZvcyBkZSBGbGlnaHRzPC9zcGFuPg0KKipTdW1tYXJ5Kiogbm9zIGFycm9qYSBsb3MgKipwcm9tZWRpb3MqKiwgKiptw6F4aW1vcyoqIHkgKiptw61uaW1vcyoqIHBvciB2YXJpYWJsZXMuIEVzdGFzIHNvbiBudWVzdHJhcyBvYnNlcnZhY2lvbmVzOiAgDQoNCisgTGFzIHZhcmlhYmxlcyBxdWUgbm9zIG9mcmVjZW4gbGFzIGhvcmFzIHkgbWludXRvcyBkZSBsbGVnYWRhcywgc2FsaWRhcyB5IHRyYW5zY3Vyc28gZGUgbG9zIHZ1ZWxvcyB0aWVuZW4gTkFzLiANCisgTG9zIGRhdG9zIHNvbiBzb2xvIGRlbCBhw7FvICoqMjAxMyoqLCBkZXNkZSAxZXJvIGRlIGVuZXJvIGhhc3RhIDMxIGRlIGRpY2llbWJyZS4gIA0KKyAqKmRlcF9kZWxheSoqIHkgKiphcnJfZGVsYXkqKiB0aWVuZW4gbsO6bWVyb3MgbmVnYXRpdm9zLiBMbGVnYXJvbiBvIHNhbGllcm9uIG3DoXMgdGVtcHJhbm8uICANCisgRWwgKklRUiogZGUgKiphaXJfdGltZSoqIHkgKipkaXN0YW5jZSoqIGVzIGFsdG8uIExvcyB2dWVsb3MgdmFyw61hbiBlbiB0cmFuc2N1cnNvIHkgZGlzdGFuY2lhIG11Y2hvLg0KYGBge3J9DQpzdW1tYXJ5KGZsaWdodHMpDQpgYGANCiMgPHNwYW4gc3R5bGUgPSAiY29sb3I6IGJsdWU7Ij5BdmFuY2UgMjwvc3Bhbj4NCg0KRW4gZXN0ZSBhdmFuY2Ugc2UgdG9jYXJhbiBkaXZlcnNvcyB0ZW1hcyB5IGZ1bmNpb25lcyBwYXJhIHBvZGVyIGxsZWdhciBhbCByZXN1bHRhZG8gcXVlIHNlIHF1aWVyZSBvYnRlbmVyLCBsYXMgZnVuY2lvbmVzIHF1ZSBzZSB2YW4gYSB1c2FyIHNvbiBsb3Mgc2lndWVudGVzOg0KDQorICoqRmlsdGVyKioNCisgKipBcnJhbmdlKioNCisgKipTZWxlY3QqKiANCisgKipSZW5hbWUqKg0KKyoqTXV0YXRlKioNCisgKipHcm91cF9ieSoqDQorKipTdW1tYXJpemUqKg0KKyAqKmlmL0Vsc2UqKg0KKyAqKlN1YnNldCoqDQoNCiMjIDxzcGFuIHN0eWxlID0gImNvbG9yOiBncmVlbjsiPlBhc28gMS4gRGUgbGEgYW50ZXJpb3IgYmFzZSBkZSBkYXRvcyBmbGlnaHRzIHNlIHRpZW5lIHF1ZSBjb25vY2VyIHkgb2JzZXJ2YXIgZWwgY29udGVuaWRvPC9zcGFuPg0KDQpgYGB7cn0NCmhlYWQoZmxpZ2h0cykNCmBgYA0KDQoNCiMjIDxzcGFuIHN0eWxlID0gImNvbG9yOiBncmVlbjsiPlBhc28gMi4JRW5jdWVudHJhIGxvcyBkYXRvcyBkZXNjcmlwdGl2b3MgZGVsIGRhdGEgZnJhbWUgZmxpZ2h0cy4gSWRlbnRpZmljYSBsYSBtZWRpYSBkZSBsYXMgZGlzdGFuY2lhcyByZWNvcnJpZGFzIGVuIG1pbGxhcy48L3NwYW4+DQoNCmBgYHtyfQ0Kc3VtbWFyeShmbGlnaHRzKQ0KYGBgDQoNCg0KIyMgPHNwYW4gc3R5bGUgPSAiY29sb3I6IGdyZWVuOyI+UGFzbyAzLglEZWZpbmUgdW4gY3JpdGVyaW8gcGFyYSBlbmNvbnRyYXIgbGFzIGFlcm9sw61uZWFzIHF1ZSBoYW4gcmVjb3JyaWRvIG3DoXMgZGlzdGFuY2lhIHkgY3JlYSB1biBudWV2byBkYXRhIGZyYW1lPC9zcGFuPg0KDQpgYGB7cn0NCiNzZWxlY2Npb25hIGxhcyB2YXJpYWJsZXMgY2FycmllciwgZGlzdGFuY2UsIG9yaWdpbiwgZGVzdCBkZSBsYSB0YWJsYQ0KZGYxX2F2YW5jZTIgPC0gc2VsZWN0KGZsaWdodHMsIGNhcnJpZXIsIGRpc3RhbmNlLCBvcmlnaW4sIGRlc3QpDQoNCiNzZSBmaWx0cmEgbGFzIGFlcm9saW5lYXMgY29uIG1pbGxhcyByZWNvcnJpZGFzIHN1cGVyaW9yZXMgYSBsYSBtZWRpYSAtIDEwNDAgDQpkZjJfYXZhbmNlMiA8LSBmaWx0ZXIoZGYxX2F2YW5jZTIsIGRpc3RhbmNlID4xMDQwKSANCg0KI29yZGVuYSBlbiBmb3JtYSBkZXNjZW5kZW50ZSBwb3IgZGlzdGFuY2lhIHJlY29ycmlkYSANCmRmM19hdmFuY2UyIDwtIGFycmFuZ2UoZGYyX2F2YW5jZTIsIGRlc2MoZGlzdGFuY2UpKQ0KYGBgDQoNCiMjIDxzcGFuIHN0eWxlID0gImNvbG9yOiBncmVlbjsiPlBhc28gNC4gRW5jb250cmFyIGxhIHN1bWEgeSBsYSBtZWRpYSBkZSBsYXMgZGlzdGFuY2lhcyByZWNvcnJpZGFzIHBvciAqKmNhcnJpZXIqKjwvc3Bhbj4NCg0KYGBge3J9DQojQWdydXBhciBsb3MgZGF0b3MgcG9yIGNhcnJpZXIsb3JpZ2luLCBkZXN0IHkgb2J0ZW5lciBsYSBzdW1hIGFjdW11bGFkYSB5IGxhIG1lZGlhIGRlIGxhcyBkaXN0YW5jaWFzIHJlY29ycmlkYXMgcG9yIGNhcnJpZXIgZWxpbWluYW5kbyBsb3MgTkEncw0KDQpkZjRfYXZhbmNlMiA8LSBkZjNfYXZhbmNlMiAlPiUgZ3JvdXBfYnkoY2FycmllcixvcmlnaW4sZGVzdCkgJT4lICANCiAgc3VtbWFyaXplKHN1bWRpc3RhbmNlPXN1bShkaXN0YW5jZSwgbmEucm09VFJVRSksIG1lYW5kaXN0YW5jZT1tZWFuKGRpc3RhbmNlLCBuYS5ybT1UUlVFKSkgDQoNCmBgYA0KIyMgPHNwYW4gc3R5bGUgPSAiY29sb3I6IGdyZWVuOyI+UGFzbyA1LiBPcmRlbmEgZW4gZm9ybWEgZGVzY2VuZGVudGUgcG9yIGRpc3RhbmNpYSByZWNvcnJpZGE8L3NwYW4+DQpgYGB7cn0NCmRmNV9hdmFuY2UyIDwtIGFycmFuZ2UoZGY0X2F2YW5jZTIsY2FycmllcixzdW1kaXN0YW5jZSkNCmBgYA0KDQojIyA8c3BhbiBzdHlsZSA9ICJjb2xvcjogZ3JlZW47Ij5QYXNvIDYuICBJZGVudGlmaWNhIHNpIGxhcyBhZXJvbMOtbmVhcyBsw61kZXJlcyBzb24gbGFzIG1pc21hcyBlbiBsb3MgdHJlcyBhZXJvcHVlcnRvcyBjdXlvIG9yaWdlbiBlcyBOdWV2YSBZb3JrPC9zcGFuPg0KDQpgYGB7cn0NCkpGSyA9IGRmNV9hdmFuY2UyICU+JSAgDQogIGZpbHRlcihvcmlnaW4gPT0gIkpGSyIpICAlPiUNCiAgYXJyYW5nZShjYXJyaWVyLCBkZXNjKHN1bWRpc3RhbmNlKSkNCkxHQSA9IGRmNV9hdmFuY2UyICU+JSAgDQogIGZpbHRlcihvcmlnaW4gPT0gIkxHQSIpICAlPiUNCiAgYXJyYW5nZShjYXJyaWVyLCBkZXNjKHN1bWRpc3RhbmNlKSkNCkVXUiA9IGRmNV9hdmFuY2UyICU+JSAgDQogIGZpbHRlcihvcmlnaW4gPT0gIkVXUiIpICAlPiUNCiAgYXJyYW5nZShjYXJyaWVyLCBkZXNjKHN1bWRpc3RhbmNlKSkNCmBgYA0KDQojIDxzcGFuIHN0eWxlID0gImNvbG9yOiBibHVlOyI+QXZhbmNlIDM8L3NwYW4+DQoNCmBgYHtyIHNldHVwLCBpbmNsdWRlPUZBTFNFfQ0Ka25pdHI6Om9wdHNfY2h1bmskc2V0KGVjaG8gPSBUUlVFKQ0KYGBgDQoNCiMjIDxzcGFuIHN0eWxlID0gImNvbG9yOiBncmVlbjsiPlBhc28gMS4JQ29uc3VsdGEgbGFzIHRhYmxhcyBwbGFuZXMgeSB3ZWF0aGVyIHBhcmEgcXVlIGNvbm96Y2FzIHN1IGNvbnRlbmlkby48L3NwYW4+DQpgYGB7cn0NCmhlYWQocGxhbmVzKQ0KaGVhZCh3ZWF0aGVyKQ0KYGBgDQoNCisgRW4gbGEgdGFibGEgInBsYW5lcyIgcG9kZW1vcyBvYnNlcnZhciBsb3MgZGlzdGludG9zIGZhY3RvcmVzIHF1ZSBjb25mb3JtYW4gbGEgY29tcG9zaWNpw7NuIGRlIGxvcyBhdmlvbmVzIGRlIGxhIGJhc2UgZGUgZGF0b3MgIm55ZmxpZ2h0czEzIiwgY29tbyBwb3IgZWplbXBsbyA6IHN1IG7Dum1lcm8gZGUgYXNpZW50b3MsIGVsIG7Dum1lcm8gZGUgZW5naW5lcyBlbiBjYWRhIGF2acOzbiwgZXRjLiANCg0KKyBFbiBsYSB0YWJsYSAid2VhdGhlciIgc2Ugbm9zIG11ZXN0cmEgaW5mb3JtYWNpw7NuIHF1ZSBub3MgaW5kaWNhIHBvciB1biBsYWRvIGxhcyBjb25kaWNpb25lcyBtZXRlb3JvbMOzZ2ljYXMgZW4gbGFzIHF1ZSBvcGVyYW4gbG9zIGF2aW9uZXMgeSBwb3Igb3RybyBsYWRvIHNlIG5vcyBwcm9wb3JjaW9uYSBkYXRvcyBwYXJhIHBvZGVyIGNvbXByZW5kZXIgbWVqb3IgY3XDoW5kbyB5IGRlIGRvbmRlIHNhbGllcm9uIGxvcyBhdmlvbmVzIGRlIGxhIGJhc2UgZGUgZGF0b3MgIm55ZmxpZ2h0czEzIi4NCg0KDQojIyA8c3BhbiBzdHlsZSA9ICJjb2xvcjogZ3JlZW47Ij5QYXNvIDIuIFNlIG5lY2VzaXRhIHNhYmVyIGRlIGNhZGEgdnVlbG8sIGxhIGFlcm9saW5lYSwgZWwgYWVyb3B1ZXJ0byBkZSBvcmlnZW4geSBlbCBhZXJvcHVlcnRvIGRlc3Rpbm8uPC9zcGFuPg0KDQoNCkFxdcOtIGVzdGFtb3MgY3JlYW5kbyB1bmEgKip2YXJpYWJsZSoqIGxsYW1hZGEgImFlcm9saW5lYXMiIHF1ZSBub3MgbXVlc3RyYSB0cmVzIHZhcmlhYmxlcyA6IGFlcm9sw61uZWFzLCBwdWVydG8gZGUgc2FsaWRhIHkgcHVlcnRvIGRlIGxsZWdhZGEgZGUgdG9kb3MgbG9zIGF2aW9uZXMgZGUgbGEgYmFzZSBkZSBkYXRvcyAqbnlmbGlnaHRzMTMqLg0KDQpgYGB7cn0NCmFlcm9saW5lYXMgPC0gZmxpZ2h0cyAlPiUgDQogIHNlbGVjdChjYXJyaWVyLG9yaWdpbixkZXN0KQ0KDQphZXJvbGluZWFzDQpgYGANCg0KIyMgPHNwYW4gc3R5bGUgPSAiY29sb3I6IGdyZWVuOyI+UGFzbyAzLglFbiBsYSBjb25zdWx0YSBhbnRlcmlvciBzZSBuZWNlc2l0YSBjb25vY2VyIGVsIG5vbWJyZSBkZSBsYSBhZXJvbMOtbmVhLjwvc3Bhbj4NCg0KQXF1w60gZXN0YW1vcyBjcmVhbmRvIHVuYSBudWV2YSB2YXJpYWJsZSBsbGFtYWRhICJhZXJvbGluZWFzX25vbWJyZSIgcXVlIGxlIGHDsWFkZSB1bmEgY29sdW1uYSBhbCBkYXRhZnJhbWUgImFlcm9sw61uZWFzIiBsbGFtYWRhICoqbmFtZSoqIHF1ZSBtdWVzdHJhIGVsIG5vbWJyZSBkZSB0b2RhcyBsYXMgYWVyb2zDrW5lYXMgZGUgbGEgYmFzZSBkZSBkYXRvcyAibnlmbGlnaHRzMTMiLg0KDQpgYGB7cn0NCmFlcm9saW5lYXNfbm9tYnJlIDwtIGFlcm9saW5lYXMgJT4lDQogIGxlZnRfam9pbihhaXJsaW5lcywgYnkgPSAiY2FycmllciIpDQoNCmFlcm9saW5lYXNfbm9tYnJlDQpgYGANCg0KIyMgPHNwYW4gc3R5bGUgPSAiY29sb3I6IGdyZWVuOyI+UGFzbyA0LiBTZSBuZWNlc2l0YSBzYWJlciBsYSBjYW50aWRhZCBkZSB2dWVsb3MgcG9yIGNhZGEgZGVzdGlubyBwYXJhIGlkZW50aWZpY2FyIGN1w6FsZXMgc29uIGxvcyBkZXN0aW5vcyBtw6FzIGJ1c2NhZG9zLjwvc3Bhbj4NCg0KQXF1w60gY3JlYW1vcyB1bmEgbnVldmEgdmFyaWFibGUgbGxhbWFkYSDigJxjYW50X3Z1ZWxvc+KAnSwgZW4gbGEgcXVlIHV0aWxpemFtb3MgbGEgZnVuY2nDs24gKipjb3VudCoqIHBhcmEgYW5hbGl6YXIgbGEgY29sdW1uYSBkZSBkYXRvcyBjYXJyaWVyIHBlcnRlbmVjaWVudGUgYWwgY29uanVudG8gZGUgZGF0b3MgZmxpZ2h0cy4NCg0KYGBge3J9DQpjYW50X3Z1ZWxvcyA8LSBmbGlnaHRzICU+JSANCiAgc2VsZWN0KGNhcnJpZXIsIGRlc3QpICU+JSANCiAgY291bnQoY2FycmllcikNCg0KY2FudF92dWVsb3MgIA0KYGBgDQoNCisgTG9zIGRlc3Rpbm9zIG3DoXMgYnVzY2Fkb3Mgc29uIDogVUEgICwgIEVWICAsICBCNiANCg0KIyMgPHNwYW4gc3R5bGUgPSAiY29sb3I6IGdyZWVuOyI+UGFzbyA1LiBBZ3JlZ2FyIGVsIG5vbWJyZSBkZSBsYSBhZXJvbMOtbmVhIGEgbGEgdGFibGEgYW50ZXJpb3I8L3NwYW4+DQoNCkFxdcOtIGFnYXJyYW1vcyBsYSB0YWJsYSAiY2FudF92dWVsb3MiIChxdWUgY29udGllbmUgbGEgY2FudGlkYWQgZGUgdnVlbG9zIHBvciBjYWRhIGFlcm9sw61uZWEpIHkgbGEganVudGFtb3MgY29uIGxhIHRhYmxhICphaXJsaW5lcyogcG9yIGxhIGNvbHVtbmEgY29tw7puICpjYXJyaWVyKiB1dGlsaXphbmRvIGxhIGZ1bmNpw7NuIGRlIHVuacOzbiAqKmxlZnRfam9pbioqLCB0b2RvIGVzdG8gY29uIGVsIGZpbiBkZSBjcmVhciB1bmEgbnVldmEgdGFibGEgbGxhbWFkYSAiY2FudF92dWVsb3Nfbm9tYnJlIiBxdWUgIGNvbnRlbmRyw6EgdG9kYXMgbGFzIGNvbHVtbmFzIGRlIGxhIHRhYmxhIG9yaWdpbmFsICJjYW50X3Z1ZWxvcyIgKGFlcm9sw61uZWEgeSBjYW50aWRhZCBkZSB2dWVsb3MpIHkgYWRlbcOhcyBjb250ZW5kcsOhIHVuYSBjb2x1bW5hIGFkaWNpb25hbCBkZSBsYSB0YWJsYSBhaXJsaW5lcywgcXVlIG11ZXN0cmEgZWwgbm9tYnJlIGRlIGxhIGFlcm9sw61uZWFzLg0KDQpgYGB7cn0NCmNhbnRfdnVlbG9zX25vbWJyZSA8LSBjYW50X3Z1ZWxvcyAlPiUNCiAgbGVmdF9qb2luKGFpcmxpbmVzLCBieSA9ICJjYXJyaWVyIikNCg0KY2FudF92dWVsb3Nfbm9tYnJlDQpgYGANCg0KIyMgPHNwYW4gc3R5bGUgPSAiY29sb3I6IGdyZWVuOyI+UGFzbyA2LiBTZSBuZWNlc2l0YSBjb25vY2VyIGxhcyBhZXJvbMOtbmVhcyAoY2xhdmUgeSBub21icmUpIHkgZGVzdGlub3MgcXVlIHZ1ZWxhbiBwb3IgbGEgTWHDsWFuYTogZGUgNiBhIDEyLCBUYXJkZTogZGUgMTIgYSAxOSAsIE5vY2hlOiBkZSAxOSBhIDI0IHkgTWFkcnVnYWRhIGRlIDI0IGEgNi4gQWdyZWdhIHVuIG51ZXZvIGNhbXBvIGEgbGEgdGFibGEgY29uIGVsIG5vbWJyZSBkZSBjbGFzX2hvcmFyaW8geSBhZ3JlZ2EsIG1hw7FhbmEsIHRhcmRlLCBub2NoZSB5IG1hZHJ1Z2FkYSBzZWfDum4gc2VhIGVsIGNhc28uPC9zcGFuPg0KDQpBcXXDrSBsbyBwcmltZXJvIHF1ZSBoaWNpbW9zIGZ1ZSB1bmEgbnVldmEgdmFyaWFibGUgbGxhbWFkYSAiY2Fycmllcl9ob3JhcmlvIiBlbiBsYSBxdWUgc2VsZWNjaW9uYW1vcyBsYXMgY29sdW1uYXMgY2FycmllciwgZGVzdCB5IHNjaGVkX2RlcF90aW1lIGRlbCBjb25qdW50byBkZSBkYXRvcyBmbGlnaHRzLCB5IHV0aWxpemFtb3MgbGEgZnVuY2nDs24gKGxlZnRfam9pbigpKSBwYXJhIHVuaXIgZXN0byBjb24gbGEgdGFibGEgYWlybGluZXMgdXRpbGl6YW5kbyBsYSBjb2x1bW5hIGNvbcO6biAiY2FycmllciIuIEVzdG8gYWdyZWdhIGVsIG5vbWJyZSBkZSBsYSBhZXJvbMOtbmVhIGEgbGEgdGFibGEgZGUgdnVlbG9zLiBEZXNwdcOpcyBjcmVhbW9zIHVuYSBudWV2YSB2YXJpYWJsZSBsbGFtYWRhICJjbGFzeGhvcmEiIHF1ZSBtdWVzdHJhIGxhIG1pc21hIGluZm9ybWFjacOzbiBxdWUgbGEgdmFyaWFibGUgImNhcnJpZXJfaG9yYXJpbyIgcGVybyBjb24gdW5hIG51ZXZhIGNvbHVtbmEgcXVlIHNlIGNyZcOzIGdyYWNpYXMgYSBsYSBmdW5jacOzbiAqKm11dGF0ZSoqIHkgZXN0YSBudWV2YSBjb2x1bW5hIG5vcyBtdWVzdHJhIHVuYSBldGlxdWV0YSBkZSBob3JhcmlvIChNYcOxYW5hLCBUYXJkZSwgTm9jaGUgbyBNYWRydWdhZGEpIHNlZ8O6biBsYSBob3JhIHByb2dyYW1hZGEgZGUgc2FsaWRhIChzY2hlZF9kZXBfdGltZSkgeSBzaWd1aWVuZG8gbGFzIGVzcGVjaWZpY2FjaW9uZXMgZGV0YWxsYWRhcyBjb24gbGEgZnVuY2nDs24gKippZmVsc2UqKiBwYXJhIGNhZGEgaG9yYXJpbyBlbiBlc3BlY8OtZmljby4NCg0KYGBge3J9DQpjYXJyaWVyX2hvcmFyaW8gPC0gZmxpZ2h0cyAlPiUNCnNlbGVjdChjYXJyaWVyLCBkZXN0LCBzY2hlZF9kZXBfdGltZSkgJT4lDQogIGxlZnRfam9pbihhaXJsaW5lcywgYnkgPSAiY2FycmllciIpDQoNCmNsYXN4aG9yYTwtIG11dGF0ZShjYXJyaWVyX2hvcmFyaW8sIGNsYXNfaG9yYXJpbyA9IGlmZWxzZShzY2hlZF9kZXBfdGltZSAlaW4lIDYwMDoxMTU5LCJNYcOxYW5hIiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgaWZlbHNlKHNjaGVkX2RlcF90aW1lICVpbiUgMTIwMDoxODU5LCJUYXJkZSIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGlmZWxzZShzY2hlZF9kZXBfdGltZSAlaW4lIDE5MDA6MjQwMCwiTm9jaGUiLCAiTWFkcnVnYWRhIikpKSkNCg0KY2xhc3hob3JhDQpgYGANCg0KIyMgPHNwYW4gc3R5bGUgPSAiY29sb3I6IGdyZWVuOyI+UGFzbyA3LlNlIG5lY2VzaXRhIHNhYmVyIGxhIGNhbnRpZGFkIGRlIHZ1ZWxvcyBwb3IgYWVyb2zDrW5lYSB5IGRlc3Rpbm8gcXVlIGhheSBwb3IgbGEgTWHDsWFuYSwgVGFyZGUsIE5vY2hlIHkgTWFkcnVnYWRhPC9zcGFuPg0KDQogQXF1w60gZXN0YW1vcyBjcmVhbmRvIHVuYSBudWV2YSB2YXJpYWJsZSBsbGFtYWRhICJjYW50X2NsYXN4aG9yYSIgZW4gbGEgcXVlIGVzdGFtb3MgYWdydXBhbmRvIGxvcyBkYXRvcyBkZSBsYXMgY29sdW1uYXMgY2FycmllciwgZGVzdCB5IGNsYXNfaG9yYXJpbyBkZSBsYSB0YWJsYSAiY2xhc3hob3JhIiBwb3IgbWVkaW8gZGUgbGEgZnVuY2nDs24gZ3JvdXBfYnkuIERlc3B1w6lzIHV0aWxpemFtb3MgbGEgZnVuY2nDs24gKipjb3VudCgpKiogcGFyYSBjb250YXIgbGEgY2FudGlkYWQgZGUgb2JzZXJ2YWNpb25lcyBlbiBjYWRhIGdydXBvLg0KIA0KYGBge3J9DQpjYW50X2NsYXN4aG9yYSA8LSBjbGFzeGhvcmEgJT4lIA0KICAgICAgICAgICAgIGdyb3VwX2J5KGNhcnJpZXIsIGRlc3QsY2xhc19ob3JhcmlvKSAlPiUgY291bnQoKQ0KDQpjYW50X2NsYXN4aG9yYQ0KYGBgICAgICAgICAgICAgICAgICAgICAgICANCg0KKyBMYSB0YWJsYSBpbmRpY2EgcXVlIGxhIG1heW9yIGNhbnRpZGFkIGRlIHZ1ZWxvcyBzb24gZGUgbGEgYWVyb2zDrW5lYSAiREwiIGNvbiBkZXN0aW5vIGEgIkFUTCIgcG9yIGxhIHRhcmRlLg0KDQojIyA8c3BhbiBzdHlsZSA9ICJjb2xvcjogZ3JlZW47Ij5QYXNvIDguIFNlIG5lY2VzaXRhIHNhYmVyIGEgcXXDqSBkZXN0aW5vcyB2dWVsYSBsYSBhZXJvbMOtbmVhIEFtZXJpY2FuIEFpcmxpbmVzIEluYy4tQUEgIGR1cmFudGUgbGEgbWFkcnVnYWRhPC9zcGFuPg0KDQpBcXXDrSBlc3RhbW9zIGNyZWFuZG8gdW5hIHZhcmlhYmxlIGxsYW1hZGEgImRlc3Rpbm9zX2FhIiBlbiBsYSBxdWUgZXN0YW1vcyBzZWxlY2Npb25hbmRvIGNvbiBsYSBmdW5jaW9uICoqc2VsZWN0KiogbGFzIGNvbHVtbmFzIGNhcnJpZXIsbmFtZSxkZXN0IHkgY2xhc19ob3JhcmlvIGRlIGxhIHZhcmlhYmxlICJjbGFzeGhvcmEiLiBEZXNwdcOpcyB1c2Ftb3MgbGEgZnVuY2nDs24gKipmaWx0ZXIqKiBwYXJhIG9idGVuZXIgw7puaWNhbWVudGUgbG9zIHJlc3VsdGFkb3MgKkFBKiB5ICpNYWRydWdhZGEqIGRlIGxhIGNvbHVtbmFzIGNhcnJpZXIgeSBjbGFzX2hvcmFyaW8uIEZpbmFsbWVudGUgdXRpbGl6YW1vcyBsYSBmdW5jacOzbiAqZ3JvdXBfYnkqIHBhcmEgcXVlIGxhIG51ZXZhIHRhYmxhIG5vcyBhZ3J1cGFyYSBsb3MgZGF0b3MgZW4gZXN0ZSBvcmRlbiA6IChjYXJyaWVyLG5hbWUsIGRlc3QsY2xhc19ob3JhcmlvKS4NCg0KYGBge3J9DQpkZXN0aW5vc19hYSA8LSBjbGFzeGhvcmEgJT4lIA0KICBzZWxlY3QoY2FycmllcixuYW1lLCBkZXN0LGNsYXNfaG9yYXJpbykgJT4lIA0KICBmaWx0ZXIoY2FycmllciA9PSAiQUEiICYgY2xhc19ob3JhcmlvID09ICJNYWRydWdhZGEiKSAlPiUgDQogIGdyb3VwX2J5KGNhcnJpZXIsbmFtZSwgZGVzdCxjbGFzX2hvcmFyaW8pDQoNCmRlc3Rpbm9zX2FhDQpgYGANCg0KKyBMYSB0YWJsYSBpbmRpY2EgcXVlIGxhIGFlcm9sw61uZWEgQW1lcmljYW4gQWlybGluZXMgSW5jLi1BQSBzb2xvIGhhY2UgdmlhamVzIGEgTWlhbWkgZHVyYW50ZSBsYSBtYWRydWdhZGEuDQoNCiMjIDxzcGFuIHN0eWxlID0gImNvbG9yOiBncmVlbjsiPlBhc28gOS4gwr9RdcOpIGF2aW9uZXMgdXRpbGl6YSBsYSBhZXJvbMOtbmVhIEFBPyBhZXJvbMOtbmVhLCB0aXBvLCBtb3RvciB5IG7Dum1lcm8gZGUgYXNpZW50b3MgeSDCv0N1w6FudG9zIHZ1ZWxvcyBzZSBoYW4gcmVhbGl6YWRvIGNvbiBjYWRhIHVubz8gZWxpbWluYSBsb3MgTkEnczwvc3Bhbj4NCg0KQXF1w60gZXN0YW1vcyBjcmVhbmRvIHVuYSB2YXJpYWJsZSBsbGFtYWRhICJhdmlvbl9hYSIgZW4gbGEgcXVlIGVzdGFtb3MgdW5pZW5kbyBwb3IgbWVkaW8gZGUgbGEgZnVuY2nDs24gKipsZWZ0X2pvaW4qKiBlbCBjb25qdW50byBkZSBkYXRvcyAqZmxpZ2h0cyogeSBlbCBjb25qdW50byBkZSBkYXRvcyAqcGxhbmVzKiAgYmFzYWRvcyBlbiBsYSBjb2x1bW5hIGNvbcO6biAqdGFpbG51bSouIERlc3B1w6lzIHNlbGVjY2lvbmFtb3Mgw7puaWNhbWVudGUgbGFzIGNvbHVtbmFzIGRlIGNhcnJpZXIsIHR5cGUsIGVuZ2luZSwgc2VhdHMgY29uIGxhIGZ1bmNpw7NuICoqc2VsZWN0KiouIENvbiBsYSBmdW5jacOzbiAqKmZpbHRlcioqIGhpY2ltb3MgcXVlIHBvciB1biBsYWRvIGxhIHRhYmxhIHNvbG8gbW9zdHJhcsOhIGxvcyByZXN1bHRhZG9zIGRlIGxhIGFlcm9sw61uZWEgIkFBIiB5IHF1ZSBwb3Igb3RybyBsYWRvIHNlIGVsaW1pbmFyYSB0b2RvcyBsb3MgTkEgJ3MgZW4gbGEgY29sdW1uYSB0eXBlLiBNw6FzIGFkZWxhbnRlIHV0aWxpemFtb3MgbGEgZnVuY2nDs24gKipncm91cF9ieSoqIHBhcmEgYWdydXBhciB5IGFzaWduYXIgZWwgb3JkZW4gZGUgbGFzIGNvbHVtbmFzIGRlIGxhIHNpZ3VpZW50ZSBtYW5lcmEgKGNhcnJpZXIsdHlwZSxlbmdpbmUsc2VhdHMpLiBGaW5hbG1lbnRlIHNlIHV0aWxpesOzIGxhIGZ1bmNpw7NuICoqY291bnQqKiBwYXJhIHNhYmVyIGN1w6FudG9zIHZ1ZWxvcyBzZSBoYW4gcmVhbGl6YWRvIGNvbiBjYWRhIHVuby4gDQoNCmBgYHtyfQ0KYXZpb25fYWEgPC0gZmxpZ2h0cyAlPiUNCiAgbGVmdF9qb2luKHBsYW5lcywgYnkgPSAidGFpbG51bSIpICU+JQ0KICBzZWxlY3QoY2FycmllciwgdHlwZSwgZW5naW5lLCBzZWF0cykgJT4lDQogIGZpbHRlcihjYXJyaWVyID09ICJBQSIsICFpcy5uYSh0eXBlKSkgJT4lDQogIGdyb3VwX2J5KGNhcnJpZXIsdHlwZSxlbmdpbmUsc2VhdHMpICU+JSANCiAgY291bnQoKQ0KDQphdmlvbl9hYSANCmBgYA0KDQorIExhIHRhYmxhIGluZGljYSBxdWUgbGEgYWVyb2zDrW5lYSBBQSB1dGlsaXphIHByaW5jaXBhbG1lbnRlIGF2aW9uZXMgZGUgdGlwbyAiRml4ZWQgd2luZyBtdWx0aSBlbmdpbmUiLg0KKyBMYSB0YWJsYSBpbmRpY2EgcXVlIGxhIGFlcm9sw61uZWEgQUEgdXRpbGl6YSBwcmluY2lwYWxtZW50ZSBtb3RvcmVzICJ0dXJibyBmYW4iIGVuIHN1cyBhdmlvbmVzLg0KKyBMYSB0YWJsYSBpbmRpY2EgcXVlIGxhIGFlcm9sw61uZWEgQUEgdXRpbGl6YSBhdmlvbmVzIHF1ZSB0aWVuZW4gZGVzZGUgMiBhc2llbnRvcyBoYXN0YSAzMzAgYXNpZW50b3MuDQoNCiMgPHNwYW4gc3R5bGUgPSAiY29sb3I6IGJsdWU7Ij7Cv1F1w6kgaW5mbHV5ZSBlbiBlbCByZXRyYXNvIGRlIGxvcyB2dWVsb3M/PC9zcGFuPg0KWWEgaGFiaWVuZG8gYW5hbGl6YWRvIGRlIG1hbmVyYSBkZXNjcmlwdGl2YSBsYXMgdGFibGFzIGRlICpueWNmbGlnaHRzMTMqLCBlcyB0aWVtcG8gZGUgbGxlZ2FyIGEgZGlzdGludG9zICoqaGFsbGF6Z29zKiogcGFyYSBhcnJpYmFyIGEgdW5hIHJlc3B1ZXN0YSBoYWPDrWEgbGEgY3Vlc3Rpw7NuIHByZXNlbnRhZGEuIFBhcmEgcG9kZXIgaGFjZXJsbywgcmVhbGl6YXJlbW9zIG51ZXZhcyAqZGF0YWJhc2VzKiwgKmRhdGFmcmFtZXMqIHkgcmVncmVzaW9uZXMgbGluZWFsZXMuDQoNCiMjIDxzcGFuIHN0eWxlID0gImNvbG9yOiBncmVlbjsiPlZhcmlhYmxlcyBjYXRlZ8OzcmljYXM8L3NwYW4+DQpIYXJlbW9zIGdydXBvcyBkZSBsYXMgdmFyaWFibGVzIGNhdGVnw7NyaWNhcyBtw6FzIHJlbGV2YW50ZXMgZGVsICpkYXRhZnJhbWUqICoqZmxpZ2h0cyoqLCB2aWVuZG8gbGFzICoqZnJlY3VlbmNpYXMqKiB5IGxvcyAqKnByb21lZGlvcyoqIHBvciBjYXRlZ29yw61hLg0KDQojIyMgPHNwYW4gc3R5bGUgPSAiY29sb3I6IGRhcmt2aW9sZXQ7Ij5TYWxpZGFzIHRhcmTDrWFzIHBvciBhZXJvbMOtbmVhPC9zcGFuPg0KU2Vnw7puIGxhIHRhYmxhIHByb3BvcmNpb25hZGEsIHBhcmVjZSBxdWUgZWwgZmFjdG9yICJjYXJyaWVyIiBzw60gdGllbmUgdW4gZWZlY3RvIGVuIGxhIHZhcmlhYmxlICJkZXBfZGVsYXkiLiBBcXXDrSBoYXkgYWxndW5hcyBvYnNlcnZhY2lvbmVzOiAgDQoNCjEuICoqRjkqKiB0aWVuZSBlbCByZXRyYXNvIHByb21lZGlvIG3DoXMgYWx0byBkZSBhcHJveGltYWRhbWVudGUgKioyMC4yMiBtaW51dG9zKiouICANCjIuICoqSEEqKiB0aWVuZSBlbCByZXRyYXNvIHByb21lZGlvIG3DoXMgYmFqbyBkZSBhcHJveGltYWRhbWVudGUgKio0LjkgbWludXRvcyoqLiAgDQozLiBQb3J0YWRvcmVzIGNvbW8gKipXTioqIHkgKipGTCoqIHRpZW5lbiBhbHRvcyByZXRyYXNvcyBwcm9tZWRpbyBkZSAqKjE3LjcxKiogeSAqKjE4LjczIG1pbnV0b3MqKiByZXNwZWN0aXZhbWVudGUsIHkgdGFtYmnDqW4gYWx0b3MgcHJvcG9yY2nDs24gZGUgdnVlbG9zIHJldHJhc2Fkb3MgKCoqNTMuNDMlKiogeSAqKjUwLjc0JSoqIHJlc3BlY3RpdmFtZW50ZSkuICANCjQuICoqTVEqKiB5ICoqT08qKiB0aWVuZW4gYmFqb3MgcmV0cmFzb3MgcHJvbWVkaW8gKCoqMTAuNTUqKiB5ICoqMTIuNTggbWludXRvcyoqIHJlc3BlY3RpdmFtZW50ZSkgcGVybyBzdXMgcG9yY2VudGFqZXMgZGUgdnVlbG9zIHJldHJhc2Fkb3Mgc29uIGRlIGxvcyBtw6FzIGJham9zICgqKjMwLjQyJSoqIHkgKioyOC4xMiUqKiByZXNwZWN0aXZhbWVudGUpLg0KYGBge3J9DQpzdW1tYXJ5X2RlbGF5IDwtIGJkZ3JhbmRlMyAlPiUNCiAgZ3JvdXBfYnkoY2FycmllcikgJT4lDQogIHN1bW1hcmlzZShhdmdfZGVsYXkgPSBtZWFuKGRlcF9kZWxheSwgbmEucm0gPSBUUlVFKSwNCiAgICAgICAgICAgIHRvdGFsX2ZsaWdodHMgPSBuKCksDQogICAgICAgICAgICB0b3RhbF9kZWxheWVkX2ZsaWdodHMgPSBzdW0oZGVwX2RlbGF5ID4gMCwgbmEucm0gPSBUUlVFKSwNCiAgICAgICAgICAgIHBlcmNfZGVsYXllZF9mbGlnaHRzID0gdG90YWxfZGVsYXllZF9mbGlnaHRzIC8gdG90YWxfZmxpZ2h0cyAqIDEwMCkgJT4lDQogIGFycmFuZ2UoZGVzYyhwZXJjX2RlbGF5ZWRfZmxpZ2h0cykpDQoNCnN1bW1hcnlfZGVsYXkNCmBgYA0KDQojIyMgPHNwYW4gc3R5bGUgPSAiY29sb3I6IGRhcmt2aW9sZXQ7Ij5TYWxpZGFzIHRhcmTDrWFzIHBvciBhZXJvcHVlcnRvPC9zcGFuPg0KU2Vnw7puIGxhIHRhYmxhIHByb3BvcmNpb25hZGEsIHBhcmVjZSBxdWUgZWwgZmFjdG9yICJvcmlnaW4iIHPDrSB0aWVuZSB1biBlZmVjdG8gZW4gbGEgdmFyaWFibGUgImRlcF9kZWxheSIuIEFxdcOtIGhheSBhbGd1bmFzIG9ic2VydmFjaW9uZXM6ICANCg0KMS4gKipFV1IqKiB0aWVuZSBlbCByZXRyYXNvIHByb21lZGlvIG3DoXMgYWx0byBkZSBhcHJveGltYWRhbWVudGUgKioxNS4xMSBtaW51dG9zKiogeSBlbCBwb3JjZW50YWplIG3DoXMgYWx0byBkZSB2dWVsb3MgcmV0cmFzYWRvcyAoKio0My42MiUqKikuICANCjIuICoqSkZLKiogdGllbmUgdW4gcmV0cmFzbyBwcm9tZWRpbyBkZSBhcHJveGltYWRhbWVudGUgKioxMi4xMSBtaW51dG9zKiogeSB1biAqKjM3Ljc3JSoqIGRlIHN1cyB2dWVsb3MgZXN0w6FuIHJldHJhc2Fkb3MuICANCjMuICoqTEdBKiogdGllbmUgZWwgcmV0cmFzbyBwcm9tZWRpbyBtw6FzIGJham8gZGUgYXByb3hpbWFkYW1lbnRlICoqMTAuMzUgbWludXRvcyoqIHkgZWwgcG9yY2VudGFqZSBtw6FzIGJham8gZGUgdnVlbG9zIHJldHJhc2Fkb3MgKCoqMzIuMTklKiopLg0KYGBge3J9DQpzdW1tYXJ5X2RlbGF5IDwtIGJkZ3JhbmRlMyAlPiUNCiAgZ3JvdXBfYnkob3JpZ2luKSAlPiUNCiAgc3VtbWFyaXNlKGF2Z19kZWxheSA9IG1lYW4oZGVwX2RlbGF5LCBuYS5ybSA9IFRSVUUpLA0KICAgICAgICAgICAgdG90YWxfZmxpZ2h0cyA9IG4oKSwNCiAgICAgICAgICAgIHRvdGFsX2RlbGF5ZWRfZmxpZ2h0cyA9IHN1bShkZXBfZGVsYXkgPiAwLCBuYS5ybSA9IFRSVUUpLA0KICAgICAgICAgICAgcGVyY19kZWxheWVkX2ZsaWdodHMgPSB0b3RhbF9kZWxheWVkX2ZsaWdodHMgLyB0b3RhbF9mbGlnaHRzICogMTAwKSAlPiUNCiAgYXJyYW5nZShkZXNjKHBlcmNfZGVsYXllZF9mbGlnaHRzKSkNCg0Kc3VtbWFyeV9kZWxheQ0KYGBgDQoNCiMjIyA8c3BhbiBzdHlsZSA9ICJjb2xvcjogZGFya3Zpb2xldDsiPlNhbGlkYXMgdGFyZMOtYXMgcG9yIGhvcmE8L3NwYW4+DQpTZWfDum4gbGEgdGFibGEgZW4gbGEgaW1hZ2VuLCBjb21vIGxhICpob3JhLiB4KiAocXVlIHJlcHJlc2VudGEgZGlmZXJlbnRlcyBob3JhcyBkZWwgZMOtYSkgKiphdW1lbnRhKiosIHRhbnRvIGVsICphdmdfZGVsYXkqIHkgKnBlcmNfZGVsYXllZF9mbGlnaHRzKiBnZW5lcmFsbWVudGUgKip0YW1iacOpbiBhdW1lbnRhbioqLiBFc3RvIHN1Z2llcmUgcXVlIGxvcyB2dWVsb3MgdGllbmRlbiBhIHJldHJhc2Fyc2UgbcOhcyB0YXJkZSBlbiBlbCBkw61hLCBsbyBxdWUgcG9kZW1vcyBvYnNlcnZhciBlbiBsb3Mgc2lndWllbnRlcyAqZGF0YSBwb2ludHMqOiAgICANCg0KMS4gQSBsYSAqKmhvcmEgNSoqLCBlbCByZXRyYXNvIHByb21lZGlvZXMgZGUgMC42OCBtaW51dG9zIHkgZWwgcG9yY2VudGFqZSBkZSB2dWVsb3MgcmV0cmFzYWRvcyBlcyBkZWwgMjUuMDMlLiAgDQoyLiBBIGxhICoqaG9yYSA2KiosIGVsIHJldHJhc28gcHJvbWVkaW8gYXVtZW50YSBhIDEuNjQgbWludXRvcyB5IGVsIHBvcmNlbnRhamUgZGUgdnVlbG9zIHJldHJhc2Fkb3MgYXVtZW50YSBhbCAyMC45MiUuICANCjMuIEEgbGEgKipob3JhIDcqKiwgZWwgcmV0cmFzbyBwcm9tZWRpbyBhdW1lbnRhIGHDum4gbcOhcyBhIDEuOTEgbWludXRvcyB5IGVsIHBvcmNlbnRhamUgZGUgdnVlbG9zIHJldHJhc2Fkb3MgZXMgZGVsIDIxLjc0JS4gIA0KNC4gQSBsYSAqKmhvcmEgOCoqLCBlbCByZXRyYXNvIHByb21lZGlvIGF1bWVudGEgc2lnbmlmaWNhdGl2YW1lbnRlIGEgNC4xMiBtaW51dG9zIHkgZWwgcG9yY2VudGFqZSBkZSB2dWVsb3MgcmV0cmFzYWRvcyBhdW1lbnRhIGFsIDI0LjkyJS4gIA0KNS4gQSBsYSAqKmhvcmEgOSoqLCBlbCByZXRyYXNvIHByb21lZGlvIGF1bWVudGEgYSA0LjU4IG1pbnV0b3MgeSBlbCBwb3JjZW50YWplIGRlIHZ1ZWxvcyByZXRyYXNhZG9zIGF1bWVudGEgYWwgMjYuNTQlLg0KYGBge3J9DQpzdW1tYXJ5X2RlbGF5IDwtIGJkZ3JhbmRlMyAlPiUNCiAgZ3JvdXBfYnkoaG91ci54KSAlPiUNCiAgc3VtbWFyaXNlKGF2Z19kZWxheSA9IG1lYW4oZGVwX2RlbGF5LCBuYS5ybSA9IFRSVUUpLA0KICAgICAgICAgICAgdG90YWxfZmxpZ2h0cyA9IG4oKSwNCiAgICAgICAgICAgIHRvdGFsX2RlbGF5ZWRfZmxpZ2h0cyA9IHN1bShkZXBfZGVsYXkgPiAwLCBuYS5ybSA9IFRSVUUpLA0KICAgICAgICAgICAgcGVyY19kZWxheWVkX2ZsaWdodHMgPSB0b3RhbF9kZWxheWVkX2ZsaWdodHMgLyB0b3RhbF9mbGlnaHRzICogMTAwKQ0KDQpzdW1tYXJ5X2RlbGF5DQpgYGANCg0KIyMgPHNwYW4gc3R5bGUgPSAiY29sb3I6IGdyZWVuOyI+VmFyaWFibGVzIG51bcOpcmljYXM8L3NwYW4+DQpJZGVudGlmaWNhbW9zIHF1ZSBoYXkgdmFyaWFibGVzIG7Dum1lcmljYXMgZW4gbGEgbWlzbWEgdGFibGEgZGUgKipmbGlnaHRzKiogeSBlbiBsYSB0YWJsYSBkZSAqKndlYXRoZXIqKiBxdWUgcHVlZGVuIHRlbmVyIHVuYSBjb3JyZWxhY2nDs24gZXN0YWTDrXN0aWNhbWVudGUgc2lnbmlmaWNhdGl2YSBwYXJhIHByZWRlY2lyIGxhcyBzYWxpZGFzIHRhcmTDrWFzIGRlIGxvcyB2dWVsb3MuIA0KDQojIyMgPHNwYW4gc3R5bGUgPSAiY29sb3I6IGRhcmt2aW9sZXQ7Ij5SYW5nbyBpbnRlcmN1YXJ0w61saWNvIGVuIE91dGxpZXJzPC9zcGFuPg0KVG9tYXJlbW9zIGNvbW8gKm91dGxpZXJzKiB0b2RvcyBhcXVlbGxvcyB2YWxvcmVzIGFudGVzIGRlbCBwcmltZXIgY3VhcnRpbCB5IGRlc3B1w6lzIGRlbCB0ZXJjZXIgY3VhcnRpbC4NCmBgYHtyfQ0KcmV0cmFzbyA8LSBiZGdyYW5kZTMgJT4lIHNlbGVjdChkZXBfZGVsYXkpDQpzdW1tYXJ5KHJldHJhc28pDQpgYGANCg0KU2Vnw7puIGFtYm9zICpzdW1tYXJpZXMqLCBhcXXDrSBlc3TDoW4gYWxndW5hcyBkaWZlcmVuY2lhcyBzaWduaWZpY2F0aXZhcyBlbnRyZSBsb3Mgb3V0bGllcnMgcG9zaXRpdm9zIHkgbmVnYXRpdm9zIGVuIHJlbGFjacOzbiBjb24gbGFzIHZhcmlhYmxlcyBjbGltw6F0aWNhczogIA0KDQoqKlRlbXBlcmF0dXJhICh0ZW1wKToqKi4NCg0KKiBPdXRsaWVycyBQb3NpdGl2b3M6IEVsIHJhbmdvIGludGVyY3VhcnRpbCAoSVFSKSBlcyBkZSBhcHJveGltYWRhbWVudGUgMzEuMTIgKDczLjkyIC0gNDIuODApLiAgDQoqIE91dGxpZXJzIE5lZ2F0aXZvczogRWwgcmFuZ28gaW50ZXJjdWFydGlsIChJUVIpIGVzIGRlIGFwcm94aW1hZGFtZW50ZSAyOS4xMiAoNjguMDkgLSAzOC45NykuICANCg0KKipEaXJlY2Npw7NuIGRlbCB2aWVudG8gKHdpbmRfZGlyKToqKg0KDQoqIE91dGxpZXJzIFBvc2l0aXZvczogRWwgcmFuZ28gaW50ZXJjdWFydGlsIChJUVIpIGVzIGRlIGFwcm94aW1hZGFtZW50ZSAxNTAgKDI4MCAtIDEzMCkuICANCiogT3V0bGllcnMgTmVnYXRpdm9zOiBFbCByYW5nbyBpbnRlcmN1YXJ0aWwgKElRUikgZXMgZGUgYXByb3hpbWFkYW1lbnRlIDExMCAoMjIwIC0gMTEwKS4gIA0KDQoqKlZlbG9jaWRhZCBkZWwgdmllbnRvICh3aW5kX3NwZWVkKToqKg0KDQoqIE91dGxpZXJzIFBvc2l0aXZvczogRWwgcmFuZ28gaW50ZXJjdWFydGlsIChJUVIpIGVzIGRlIGFwcm94aW1hZGFtZW50ZSA2LjUgKDE0LjU2IC0gOC4wNTYpLiAgDQoqIE91dGxpZXJzIE5lZ2F0aXZvczogRWwgcmFuZ28gaW50ZXJjdWFydGlsIChJUVIpIGVzIGRlIGFwcm94aW1hZGFtZW50ZSA0ICgxMCAtIDYpLiAgDQoNCkVzdGFzIGRpZmVyZW5jaWFzIGVuIGxvcyByYW5nb3MgaW50ZXJjdWFydGlsZXMgZW50cmUgbG9zIG91dGxpZXJzIHBvc2l0aXZvcyB5IG5lZ2F0aXZvcyBwb2Ryw61hbiBzdWdlcmlyIHF1ZSBsYXMgY29uZGljaW9uZXMgY2xpbcOhdGljYXMgbcOhcyBleHRyZW1hcyAodGVtcGVyYXR1cmFzIG3DoXMgYWx0YXMsIGRpcmVjY2lvbmVzIGRlIHZpZW50byBtw6FzIHZhcmlhZGFzIHkgdmVsb2NpZGFkZXMgZGUgdmllbnRvIG3DoXMgYWx0YXMpIGVzdMOhbiBhc29jaWFkYXMgY29uIG1heW9yZXMgcmV0cmFzb3MgZW4gbG9zIHZ1ZWxvcy4NCmBgYHtyfQ0KYmRfb3V0bGllcnMgPC0gZmlsdGVyKGJkZ3JhbmRlMCwgZGVwX2RlbGF5ID4gMTEpDQpiZF9vdXRsaWVycyA8LSBiZF9vdXRsaWVycyAlPiUgc2VsZWN0KC15ZWFyLng6LWhvdXIueSkNCg0KYmRfbWVqb3JlcyA8LSBmaWx0ZXIoYmRncmFuZGUwLCBkZXBfZGVsYXkgPCAtNSkNCmJkX21lam9yZXMgPC0gYmRfbWVqb3JlcyAlPiUgc2VsZWN0KC15ZWFyLng6LWhvdXIueSkNCg0Kc3VtbWFyeShiZF9vdXRsaWVycykNCnN1bW1hcnkoYmRfbWVqb3JlcykNCmBgYA0KDQojIyMgPHNwYW4gc3R5bGUgPSAiY29sb3I6IGRhcmt2aW9sZXQ7Ij5SZWdyZXNpw7NuIExpbmVhbCBjb24gV2VhdGhlcjwvc3Bhbj4NCkNyZWFtb3MgdW4gbW9kZWxvIGRlIHJlZ3Jlc2nDs24gbGluZWFsIGNvbiB0b2RhcyBsYXMgdmFyaWFibGVzIGRlbCBjbGltYSBkZSBsYSB0YWJsYSAqKndlYXRoZXIqKi4NCmBgYHtyfQ0KZmxpZ2h0c193ZWF0aGVyIDwtIGZsaWdodHMgJT4lIGxlZnRfam9pbih3ZWF0aGVyLCBieSA9IGMoInllYXIiLCAibW9udGgiLCAiZGF5IiwgImhvdXIiLCAib3JpZ2luIikpDQoNCnJlZ3Jlc2lvbl93ZWF0aGVyIDwtIGxtKGRlcF9kZWxheSB+IHRlbXAgKyBkZXdwICsgaHVtaWQgKyB3aW5kX2RpciArIHdpbmRfZ3VzdCArIHByZXNzdXJlICsgdmlzaWIsIGRhdGEgPSAgZmxpZ2h0c193ZWF0aGVyKQ0Kc3VtbWFyeShyZWdyZXNpb25fd2VhdGhlcikNCmBgYA0KDQojIyMgPHNwYW4gc3R5bGUgPSAiY29sb3I6IGRhcmt2aW9sZXQ7Ij5NYXRyw616IGRlIENvcnJlbGFjacOzbjwvc3Bhbj4NCmBgYHtyfQ0KZmxpZ2h0c193ZWF0aGVyIDwtIGZsaWdodHMgJT4lIGxlZnRfam9pbih3ZWF0aGVyLCBieSA9IGMoInllYXIiLCAibW9udGgiLCAiZGF5IiwgImhvdXIiLCJvcmlnaW4iKSkNCmRmMTUgPC0gZmxpZ2h0c193ZWF0aGVyICU+JSBzZWxlY3QodGVtcCxkZXdwLGh1bWlkLHdpbmRfZGlyLHdpbmRfZ3VzdCxwcmVzc3VyZSx2aXNpYixkZXBfZGVsYXkpDQpkZjE1IDwtIHJlcGxhY2UoZGYxNSwgaXMubmEoZGYxNSksIDApDQpkYXRhKGRmMTUpDQpzdHIoZGYxNSkNCg0KTSA9IGNvcihkZjE1KQ0KDQpjb3JycGxvdChNKQ0KYGBgDQoNCg0KRWwgKipjb2VmaWNpZW50ZSByXjIgYWp1c3RhZG8qKiBlcyBkZSAqKjAuMDIqKiBwYXJhIGVsIG1vZGVsbyAqKnJlZ3Jlc2lvbl93ZWF0aGVyKiosIGxvIHF1ZSBzaWduaWZpY2EgcXVlIGxhcyB2YXJpYWJsZXMgY2xpbcOhdGljYXMgbm8gc29uIGVzdGFkw61zdGljYW1lbnRlIHNpZ25pZmljYXRpdmFzIHBhcmEgcHJlZGVjaXIgbG9zIHJldHJhc29zIGVuIGxhIHNhbGlkYSBkZSBsb3MgdnVlbG9zLiBQb2RlbW9zIHByb2JhciBlc3RvIGNvbiBkb3MgZ3LDoWZpY29zIHN1cGVycHVlc3RvczogZWwgKipuZWdybyoqIGVzIGVsIHZhbG9yICpyZWFsKiwgbWllbnRyYXMgcXVlIGVsICoqcm9qbyoqIGVzIGVsICpwcmVkaWNobyogcG9yIGVsIG1vZGVsby4gTm8gc29uIGNvbnZlcmdlbnRlcyBlbiBhYnNvbHV0bywgeWEgcXVlIGVsIG1vZGVsbyBzb2xvIHB1ZWRlIHByZWRlY2lyIGVuIHVuIHJhbmdvIG1lbm9yIHF1ZSBlbCBkZSBsb3MgdmFsb3JlcyByZWFsZXMsIGxvIHF1ZSByZXByZXNlbnRhIGxhIGZhbHRhIGRlIHJlbGFjacOzbi4gIA0KQSBwZXNhciBkZSBsYSBpbmNsdXNpw7NuIGRlICp2YXJpYWJsZXMgY2xpbcOhdGljYXMqIGVuIHVuIG1vZGVsbyBkZSByZWdyZXNpw7NuIGxpbmVhbCwgZW5jb250cmFtb3MgcXVlIGVzdGFzICoqbm8gc29uIGVzdGFkw61zdGljYW1lbnRlIHNpZ25pZmljYXRpdmFzKiogcGFyYSBwcmVkZWNpciBsb3MgcmV0cmFzb3MgZW4gbGFzIHNhbGlkYXMgZGUgdnVlbG9zLiBFc3RvIHN1Z2llcmUgcXVlIG90cm9zIGZhY3RvcmVzIHB1ZWRlbiBlc3RhciBpbmZsdXllbmRvIGVuIGxvcyByZXRyYXNvcyBkZSBsb3MgdnVlbG9zLCBjb21vIGxvcyBwcm9ibGVtYXMgb3BlcmF0aXZvcyBpbnRlcm5vcyBkZSBsYXMgYWVyb2zDrW5lYXMgbyBsb3MgYWVyb3B1ZXJ0b3MuDQoNCmBgYHtyIHdhcm5pbmc9RkFMU0V9DQpwcmVkaWN0ZWRfdmFsdWVzIDwtIHByZWRpY3QocmVncmVzaW9uX3dlYXRoZXIsIG5ld2RhdGEgPSBmbGlnaHRzX3dlYXRoZXIpDQoNCmNvbXBhcmlzb24gPC0gZGF0YS5mcmFtZShBY3R1YWwgPSBmbGlnaHRzX3dlYXRoZXIkZGVwX2RlbGF5LCBQcmVkaWN0ZWQgPSBwcmVkaWN0ZWRfdmFsdWVzKQ0KDQpyZWdpc3RybyA8LSAxOm5yb3coY29tcGFyaXNvbikgIyBDcmVhciB1biB2ZWN0b3IgZGUgbsO6bWVyb3MgZGUgcmVnaXN0cm8NCg0KY29tcGFyaXNvbiRSZWdpc3RybyA8LSByZWdpc3RybyAjIEFncmVnYXIgZWwgdmVjdG9yIGRlIG7Dum1lcm9zIGRlIHJlZ2lzdHJvIGFsIGRhdGFmcmFtZSBjb21wYXJpc29uDQoNCiMgQ3JlYXIgdW4gZ3LDoWZpY28gZGUgbMOtbmVhcyBwYXJhIGxvcyB2YWxvcmVzIHJlYWxlcw0KcDEgPC0gZ2dwbG90KGNvbXBhcmlzb24sIGFlcyh4ID0gUmVnaXN0cm8sIHkgPSBBY3R1YWwpKSArDQogIGdlb21fbGluZShjb2xvciA9ICJibGFjayIpICsNCiAgbGFicyh4ID0gIk7Dum1lcm8gZGUgcmVnaXN0cm8iLCB5ID0gIlJldHJhc28gKG1pbnV0b3MpIiwgdGl0bGUgPSAiUmV0cmFzbyByZWFsIHZzIHByZWRpY2hvIikgKw0KICB0aGVtZV9taW5pbWFsKCkNCg0KIyBDcmVhciB1biBncsOhZmljbyBkZSBsw61uZWFzIHBhcmEgbG9zIHZhbG9yZXMgcHJlZGljaG9zDQpwMiA8LSBnZ3Bsb3QoY29tcGFyaXNvbiwgYWVzKHggPSBSZWdpc3RybywgeSA9IFByZWRpY3RlZCkpICsNCiAgZ2VvbV9saW5lKGNvbG9yID0gInJlZCIpICsNCiAgdGhlbWVfbWluaW1hbCgpDQoNCiMgU3VwZXJwb25lciBsb3MgZG9zIGdyw6FmaWNvcw0KZmluYWxfcGxvdCA8LSBwMSArIGdlb21fbGluZShkYXRhID0gcDIkZGF0YSwgYWVzKHggPSBSZWdpc3RybywgeSA9IFByZWRpY3RlZCksIGNvbG9yID0gInJlZCIpDQpmaW5hbF9wbG90DQpgYGANCg0KIyMjIDxzcGFuIHN0eWxlID0gImNvbG9yOiBkYXJrdmlvbGV0OyI+U2VyaWUgZGUgdGllbXBvIGNvbiBGbGlnaHRzPC9zcGFuPg0KRGVzY29tcHVzaW1vcyBwb3IgbWVzIHVuYSBzZXJpZSBkZSB0aWVtcG8gZW50cmUgKipkYXRlKiogeSBlbCBwcm9tZWRpbyBkZSAqKmRlcF9kZWxheSoqLCBwb3IgbG8gcXVlIHNlIHNhY8OzIGVsIGVmZWN0byBlc3RhY2lvbmFsIG1lbnN1YWwuIENvbiBlc3RlIGdyw6FmaWNvIHBvZGVtb3Mgb2JzZXJ2YXIgdW4gY29tcG9uZW50ZSBlc3RhY2lvbmFsIGVuIGVsIHF1ZSBzZSBwcm9kdWNlbiBtYXlvcmVzIHJldHJhc29zIGRlIHNhbGlkYSBlbiBsb3MgbWVzZXMgZGU6IA0KDQorICoqQWJyaWwqKiAgDQorICoqSnVsaW8qKiB5ICoqQWdvc3RvKioNCisgKipOb3ZpZW1icmUqKiB5ICoqRGljaWVtYnJlKiogIA0KYGBge3J9DQojIFByb21lZGlvIGRlIGRlcF9kZWxheSBwb3IgZmVjaGENCmZsaWdodHMxIDwtIGZsaWdodHMgJT4lDQogIG11dGF0ZShkYXRlID0gbWFrZV9kYXRlKHllYXIsIG1vbnRoLCBkYXkpKSAlPiUNCiAgZ3JvdXBfYnkoZGF0ZSkgJT4lDQogIHN1bW1hcmlzZShkZXBfZGVsYXkgPSBtZWFuKGRlcF9kZWxheSwgbmEucm0gPSBUUlVFKSkNCg0KZmxpZ2h0czEgPC0gbmEub21pdChmbGlnaHRzMSkNCg0KIyBPYmpldG8gZGUgc2VyaWUgZGUgdGllbXBvIGNvbiBmcmVjdWVuY2lhIG1lbnN1YWwNCnRzX2ZsaWdodHMgPC0gdHMoZmxpZ2h0czEkZGVwX2RlbGF5LCBzdGFydCA9IGMoMjAxMywgMSksIGZyZXF1ZW5jeSA9IDEyKQ0KDQojIERlc2NvbXBvbmVyIGxhIHNlcmllIGRlIHRpZW1wbyBkZSBtYW5lcmEgcGVyacOzZGljYQ0KZGVjb21wb3NlZCA8LSBzdGwodHNfZmxpZ2h0cywgcy53aW5kb3c9InBlcmlvZGljIikNCg0KIyBDcmVhciB1biBkYXRhIGZyYW1lIGNvbiBsYXMgdGVtcG9yYWRhcyB5IGZlY2hhcw0Kc2Vhc29uYWxfY29tcG9uZW50IDwtIGRlY29tcG9zZWQkdGltZS5zZXJpZXNbLCAic2Vhc29uYWwiXQ0KZGF0ZXMgPC0gc2VxKGFzLkRhdGUoIjIwMTMtMDEtMDEiKSwgYnkgPSAibW9udGgiLCBsZW5ndGgub3V0ID0gbGVuZ3RoKHNlYXNvbmFsX2NvbXBvbmVudCkpDQoNCmRmX3NlYXNvbmFsIDwtIGRhdGEuZnJhbWUoRGF0ZSA9IGRhdGVzLCBTZWFzb25hbCA9IHNlYXNvbmFsX2NvbXBvbmVudCkNCmRmX3NlYXNvbmFsJE1vbnRoIDwtIGZvcm1hdChkZl9zZWFzb25hbCREYXRlLCAiJUIiKQ0KDQojIENhbGN1bGFyIGVsIGVmZWN0byBlc3RhY2lvbmFsIG1lZGlvIHBhcmEgY2FkYSBtZXMNCmF2Z19zZWFzb25hbCA8LSBhZ2dyZWdhdGUoU2Vhc29uYWwgfiBNb250aCwgZGZfc2Vhc29uYWwsIG1lYW4pDQoNCmF2Z19zZWFzb25hbCRNb250aCA8LSBmYWN0b3IoYXZnX3NlYXNvbmFsJE1vbnRoLCBsZXZlbHMgPSBtb250aC5uYW1lKQ0KDQojIEdyw6FmaWNhcg0KZ2dwbG90KGF2Z19zZWFzb25hbCwgYWVzKHggPSBNb250aCwgeSA9IFNlYXNvbmFsKSkgKw0KICBnZW9tX2JhcihzdGF0ID0gImlkZW50aXR5IikgKw0KICBsYWJzKHggPSAiTWVzIiwgeSA9ICJDb21wb25lbnRlIGVzdGFjaW9uYWwgbWVkaW8iLCANCiAgICAgICB0aXRsZSA9ICJFZmVjdG8gZXN0YWNpb25hbCBwcm9tZWRpbyBwb3IgbWVzIikNCg0KYGBgDQoNCiMjIyA8c3BhbiBzdHlsZSA9ICJjb2xvcjogZGFya3Zpb2xldDsiPk1lc2VzPC9zcGFuPg0KQWwgdmVyIHlhIHZpc3RvIHF1ZSBjb24gbGFzIHZhcmlhYmxlcyBjbGltw6F0aWNhcyBubyBoYXkgdW5hIHJlbGFjacOzbiBzaWduaWZpY2F0aXZhLCB5IHZlbW9zIHF1ZSAqc2kqIGhheSB1biBlZmVjdG8gZXN0YWNpb25hbCwgcG9kZW1vcyBwb3RlbmNpYWxtZW50ZSBhdHJpYnVpciBsb3MgY2FtYmlvcyBhIHVuIGNhbWJpbyBkZSBkZW1hbmRhIGRlIHZ1ZWxvcy4gIA0KTG9zIG1lc2VzIGNvbiBtYXlvciBkZW1hbmRhICoqanVsaW8qKiB5ICoqYWdvc3RvKiogY29pbmNpZGVuIGNvbiBlbCBlZmVjdG8gZXN0YWNpb25hbCBlbiByZXRyYXNvcyBxdWUgdGllbmVuLiBObyBvYnN0YW50ZSwgKipvY3R1YnJlKiogeSAqKm1hcnpvKiogc29uIHRlcmNlcm8geSBjdWFydG8gZW4gZGVtYW5kYSB5IGVzdG8gbm8gc2UgdmUgcmVmbGVqYWRvIGVuIGVsIGVmZWN0byBlc3RhY2lvbmFsLiAgDQpFc3RvIG5vcyBsbGV2YSBhIGNvbmNsdWlyIHF1ZSBoYXkgb3Ryb3MgZmFjdG9yZXMsIGZ1ZXJhIGRlIGxhIGRlbWFuZGEsIHF1ZSBvY2FzaW9uYW4gZXN0ZSBlZmVjdG8gZXN0YWNpb25hbCBkZSByZXRyYXNvcy4gIA0KTm8gb2JzdGFudGUsIGxvcyBtZXNlcyBjb24gbWF5b3IgcHJvcG9yY2nDs24gZGUgcmV0cmFzb3Mgc29uIGxvcyBtZXNlcyB2YWNhY2lvbmFsZXMgKipqdW5pbyoqLCAqKmp1bGlvKiosICoqZGljaWVtYnJlKiouDQpgYGB7cn0NCnN1bW1hcnlfZGVsYXkgPC0gYmRncmFuZGUzICU+JQ0KICBncm91cF9ieShtb250aC54KSAlPiUNCiAgc3VtbWFyaXNlKGF2Z19kZWxheSA9IG1lYW4oZGVwX2RlbGF5LCBuYS5ybSA9IFRSVUUpLA0KICAgICAgICAgICAgdG90YWxfZmxpZ2h0cyA9IG4oKSwNCiAgICAgICAgICAgIHRvdGFsX2RlbGF5ZWRfZmxpZ2h0cyA9IHN1bShkZXBfZGVsYXkgPiAwLCBuYS5ybSA9IFRSVUUpLA0KICAgICAgICAgICAgcGVyY19kZWxheWVkX2ZsaWdodHMgPSB0b3RhbF9kZWxheWVkX2ZsaWdodHMgLyB0b3RhbF9mbGlnaHRzICogMTAwKSAlPiUNCiAgYXJyYW5nZShkZXNjKHRvdGFsX2ZsaWdodHMpKQ0KDQpzdW1tYXJ5X2RlbGF5DQpgYGANCg0KDQpgYGB7ciB3YXJuaW5nPUZBTFNFfQ0KZmxpZ2h0c19maWx0ZXJlZCA8LSBmbGlnaHRzICU+JQ0KICBtdXRhdGUoZGVsYXllZCA9IGlmZWxzZShkZXBfZGVsYXkgPiAwLCAiRGVsYXllZCIsICJOb3QgRGVsYXllZCIpKSAlPiUNCiAgZ3JvdXBfYnkobW9udGgsIGRlbGF5ZWQpICU+JQ0KICBzdW1tYXJpc2UoY291bnQgPSBuKCkpICU+JQ0KICBtdXRhdGUocGVyY2VudCA9IGNvdW50IC8gc3VtKGNvdW50KSAqIDEwMCkNCg0KZ2dwbG90KGZsaWdodHNfZmlsdGVyZWQsIGFlcyh4ID0gZmFjdG9yKG1vbnRoKSwgeSA9IGNvdW50LCBmaWxsID0gZGVsYXllZCkpICsNCiAgZ2VvbV9iYXIoc3RhdCA9ICJpZGVudGl0eSIsIHBvc2l0aW9uID0gInN0YWNrIikgKw0KICBzY2FsZV9maWxsX21hbnVhbCh2YWx1ZXMgPSBjKCJyZWQiLCAiZ3JlZW4iKSkgKw0KICBsYWJzKHRpdGxlID0gIk7Dum1lcm8gZGUgdnVlbG9zIHBvciBtZXMgY29uIHkgc2luIHJldHJhc28iLA0KICAgICAgIHggPSAiTWVzIiwNCiAgICAgICB5ID0gIk7Dum1lcm8gZGUgdnVlbG9zIiwNCiAgICAgICBmaWxsID0gIlJldHJhc28iKSArDQogIHRoZW1lX21pbmltYWwoKQ0KYGBgDQoNCiMgPHNwYW4gc3R5bGUgPSAiY29sb3I6IGJsdWU7Ij4gQ29uY2x1c2nDs24gR2VuZXJhbDwvc3Bhbj4NCg0KKiBBIG1lZGlkYSBxdWUgYXVtZW50YSBsYSBob3JhIGRlbCBkw61hLCB0YW50byBlbCByZXRyYXNvIG1lZGlvIGNvbW8gZWwgcG9yY2VudGFqZSBkZSB2dWVsb3MgcmV0cmFzYWRvcyBzdWVsZW4gYXVtZW50YXIuIEVzdG8gcG9kcsOtYSBleHBsaWNhcnNlIHBvciBsYSBhY3VtdWxhY2nDs24gZGUgcmV0cmFzb3MgYSBsbyBsYXJnbyBkZWwgZMOtYSwgbGFzIGNvbmRpY2lvbmVzIGNsaW3DoXRpY2FzIGNhbWJpYW50ZXMgbyBsYSBtYXlvciBjb25nZXN0acOzbiBkZWwgdHLDoWZpY28gYcOpcmVvIGVuIGxhcyBob3JhcyBwaWNvLiBVbiBlamVtcGxvIGVzIHF1ZSBhIGxhcyAqKjUgYW0qKiwgZWwgcmV0cmFzbyBwcm9tZWRpbyBlcyBkZSAqMC42OCBtaW51dG9zKiB5IGVsIHBvcmNlbnRhamUgZGUgKjI1LjAzJSo7IGEgbGFzICoqNSBwbSoqIGVsIHJldHJhc28gZXMgZGUgKjIxLjEgbWludXRvcyogeSBlbCBwb3JjZW50YWplIGVzIGRlICo0OS42JSouICANCg0KKiBMb3MgdnVlbG9zIHF1ZSBzYWxlbiBlbiBtZXNlcyBkZSBhbHRhIGRlbWFuZGEgdGllbmVuIG3DoXMgcmV0cmFzb3MgcG9ycXVlIGFsIHN1YmlyIG3DoXMgZGVtYW5kYSBwb2Ryw61hIGhhYmVyIGNvbXBsZWppZGFkIGVuIGxhcyBvcGVyYWNpb25lcyB5IG1heW9yIHRyw6FmaWNvLiBBbCBhbmFsaXphciBsb3MgZGF0b3Mgc2UgZW5jb250csOzIHF1ZSBlbiBqdWxpbywgZWwgKio0NyUqKiBkZSBsb3MgdnVlbG9zIHRpZW5lbiByZXRyYXNvIHksIGEgc3UgdmV6LCBlcyBlbCBtZXMgY29uIG3DoXMgYWx0YSBkZW1hbmRhOyBlbiBmZWJyZXJvLCBzb2xhbWVudGUgZWwgKiozNiUqKiBkZSBsb3MgdnVlbG9zIHNvbiByZXRyYXNhZG9zLiAgDQoNCiogTGFzIGFlcm9sw61uZWFzIHRpZW5lbiB1biBpbXBhY3RvIHNpZ25pZmljYXRpdm8gZW4gbG9zIHJldHJhc29zIGVuIGxvcyB2dWVsb3MgeWEgcXVlIGxhIGRpc3BvbmliaWxpZGFkIGRlIHBlcnNvbmFsIHkgbGEgZGVtYW5kYSBkZSB2aWFqZXMgdmFyw61hbiBwb3IgY2FkYSB1bmEuIEVzdG8gc2UgcHVlZGUgdmVyIGNvbW8gZW4gKlNvdXRod2VzdCBBaXJsaW5lcyBDby4qIHNlIHJldHJhc8OzIGVsICoqNTMuNCUqKiBkZSBsb3MgdnVlbG9zICh0ZW5pZW5kbyAxMiBtaWwgdnVlbG9zIHRvdGFsZXMpIG1pZW50cmFzIHF1ZSAqSGF3YWlpYW4gQWlybGluZXMgSW5jLiogc29sbyBzZSByZXRyYXPDsyBlbCAqKjIwLjIlKiogKGNvbiAzNDIgdnVlbG9zKS4gIA0KDQoqIEVuIGN1ZXN0acOzbiBkZSBhZXJvcHVlcnRvcywgZW50cmUgbWF5b3IgY2FudGlkYWQgZGUgdnVlbG9zIHRvdGFsZXMgc2FsZ2FuIGRlbCBhZXJvcHVlcnRvLCBoYWJyw6EgdW5hIHByb3BvcmNpw7NuIG3DoXMgYWx0YSBkZSByZXRyYXNvcyB5YSBxdWUgbGEgY2FwYWNpZGFkIGRlIGdlc3Rpw7NuIGRlbCB0csOhZmljbyBhw6lyZW8gZXMgZGVwZW5kaWVudGUgZGUgbGEgZGVtYW5kYS4gU2UgcHVlZGUgcHJlc2VuY2lhciBlc3RlIGbDqW5vbWVubyBlbiBjb21vIGVsICpBZXJvcHVlcnRvIEludGVybmFjaW9uYWwgZGUgTmV3YXJrIChFV1IpKiAsIGVsIGRlIG1heW9yIHZ1ZWxvcyB0b3RhbGVzLCB0aWVuZSB1bmEgcHJvcG9yY2nDs24gZGUgKio0My42JSoqIGRlIHJldHJhc29zOyAqQWVyb3B1ZXJ0byBJbnRlcm5hY2lvbmFsIGRlIExhIEd1YXJkaWEgKExHQSkqLCBlbCBkZSBtZW5vcyB2dWVsb3MgdG9hbGVzLCBzb2xvIHRpZW5lICoqMzIuMiUqKiBkZSByZXRyYXNvcy4gIA0KDQoqIEVuIGxhIG5vY2hlIHNlIHByZXNlbnRhIGxhIG1heW9yIHByb3BvcmNpw7NuIGRlIHJldHJhc29zIGVuIGNvbXBhcmFjacOzbiBjb24gbGEgbWHDsWFuYSBkZWJpZG8gYSBxdWUgaGF5IG1lbm9zIHBlcnNvbmFsIGRpc3BvbmlibGUgKGxhIGRlbWFuZGEgbm8gZXN0YSByZWxhY2lvbmFkYSBhIGVzdG8pLiBTZSBvYnNlcnZhIGVuIGPDs21vIGxhIHByb3BvcmNpw7NuIGRlIHJldHJhc29zIGxsZWdhIGEgdW4gcHVudG8gY2FsaWVudGUgZW50cmUgbGFzIGhvcmFzICoqNyBhIDkgcG0qKiBjb24gKio1MC41JSoqIHkgKio1MS4yJSoqIGRlIHJldHJhc29zIHJlc3BlY3RpdmFtZW50ZS4NCg0KIyA8c3BhbiBzdHlsZSA9ICJjb2xvcjogYmx1ZTsiPiBDb21wcm9taXNvIMOpdGljbyB5IGNpdWRhZGFubyAoQWRyacOhbik8L3NwYW4+DQpQYXJhIG1pIGxhIGludGVncmlkYWQgZXMgYWN0dWFyIGRlIGZvcm1hIHJlc3BvbnNhYmxlLCByZXNwZXR1b3NhIHkgaG9uZXN0YSBlbiB0b2RvIG1vbWVudG8gY29uIGN1YWxxdWllciBwZXJzb25hIG8gZW50aWRhZCBjb24gbGEgcXVlIGludGVyYWN0dWVtb3MuIEFsIG1vbWVudG8gZGUgaGFjZXIgbmVnb2Npb3MgY29uIG90cmFzIHBlcnNvbmFzIHNpZW1wcmUgZGViZW1vcyBhY3R1YXIgZGUgZm9ybWEgw61udGVncmEgY29uIGxvcyBtZWRpb3MgcXVlIGxhIG90cmEgcGFydGUgbm9zIGVzdMOhIGJyaW5kYW5kbyB5YSBxdWUgZWwgbWFsIHVzbyBwb3IgbnVlc3RyYSBwYXJ0ZSBkZSBlc3RvcyBkYXRvcyBvIGluZm9ybWFjacOzbiBzZSBwdWVkZSBwcmVzdGFyIGEgZnV0dXJhcyBkaXNjdXNpb25lcyBvIGRlbWFuZGFzIGVuIGNhc28gZGUgcXVlIGVzdG9zIGRhdG9zIHNlYW4gbXV5IGltcG9ydGFudGVzL2NvbmZpZGVuY2lhbGVzLiBBbCBtb21lbnRvIGRlIHRlbmVyIGRhdG9zIGRlIG90cmFzIGVtcHJlc2FzLCBkZWJlbW9zIGNvbXByb21ldGVybm9zIGEgbWFudGVuZXJsb3MgcHJpdmFkb3MgZW4gY2FzbyBkZSBxdWUgc2Ugbm9zIHNvbGljaXRlIGhhY2VyIGVzdG8geSBzw7NsbyB1dGlsaXphcmxvcyBkZSBmb3JtYSDDrW50ZWdyYSBwYXJhIHJlYWxpemFyIG51ZXN0cm9zIGFuw6FsaXNpcyBkZXNlYWRvcy4NCg0KIyA8c3BhbiBzdHlsZSA9ICJjb2xvcjogYmx1ZTsiPiBDb21wcm9taXNvIMOpdGljbyB5IGNpdWRhZGFubyAoQW5uYSk8L3NwYW4+DQpFbiByZXN1bWVuLCAqKnRlbmVyIGFjY2VzbyBhIGxhIGluZm9ybWFjacOzbiBkZSB1bmEgZW1wcmVzYSBjb25sbGV2YSB1bmEgZ3JhbiByZXNwb25zYWJpbGlkYWQqKiBxdWUgZGViZSBtYW5lamFyc2UgY29uIGludGVncmlkYWQgeSDDqXRpY2EgcHJvZmVzaW9uYWwuIEVuIGVsIGNvbnRleHRvIGRlbCBhbsOhbGlzaXMgZGUgZGF0b3MgZGUgdnVlbG9zLCBlc3RvIGltcGxpY2EgdXRpbGl6YXIgbGEgaW5mb3JtYWNpw7NuIGRlIG1hbmVyYSByZXNwb25zYWJsZSwgZ2FyYW50aXphbmRvIGxhIHByaXZhY2lkYWQgeSBjb25maWRlbmNpYWxpZGFkIGRlIGxvcyBkYXRvcywgYXPDrSBjb21vICoqYXNlZ3VyYW5kbyBxdWUgbG9zIGFuw6FsaXNpcyB5IGNvbmNsdXNpb25lcyBzZSBiYXNlbiBlbiBtZXRvZG9sb2fDrWFzIHPDs2xpZGFzIHkgw6l0aWNhcyoqLiAgDQpBZGVtw6FzLCBzaWduaWZpY2EgbWFudGVuZXIgdW4gY8OzZGlnbyDDqXRpY28gZW4gbGEgbWFuaXB1bGFjacOzbiB5IHByZXNlbnRhY2nDs24gZGUgbG9zIHJlc3VsdGFkb3MsIGFzZWd1cmFuZG8gcXVlIGxhIGluZm9ybWFjacOzbiBzZSB1dGlsaWNlIHBhcmEgKiptZWpvcmFyIGxhIHRvbWEgZGUgZGVjaXNpb25lcyB5IGdlbmVyYXIgdmFsb3IgcGFyYSBsYSBlbXByZXNhIHkgc3VzIGdydXBvcyBkZSBpbnRlcsOpcyoqLCBlbiBsdWdhciBkZSBwYXJhIGZpbmVzIGN1ZXN0aW9uYWJsZXMgbyBwZXJqdWRpY2lhbGVzLg0KDQojIDxzcGFuIHN0eWxlID0gImNvbG9yOiBibHVlOyI+IENvbXByb21pc28gw6l0aWNvIHkgY2l1ZGFkYW5vIChEYXJpbyk8L3NwYW4+DQpFbCB0ZW5lciBpbnRlZ3JpZGFkIHkgaG9uZXN0aWRhZCBhIGxhIGhvcmEgZGUgbWFuZWphciBkYXRvcyBlbiB1bmEgZW1wcmVzYSBlcyBkZSBzdW1hIGltcG9ydGFuY2lhIHkgcmVzcG9uc2FiaWxpZGFkLCBsb3MgZGF0b3Mgc29uIGFsZ28gcXVlIGFjdHVhbG1lbnRlIHkgZW4gZWwgZnV0dXJvIHNvbiBiaWVuIGNvdGl6YWRvcyB5IHF1ZSBzZSBsZSBlc3TDoSBkYW5kbyByZWNvbm9jaW1pZW50byBkZWJpZG8gYSBxdWUgbGFzIGVtcHJlc2FzIGxvZ3JhbiBzYXRpc2ZhY3RvcmlhbWVudGUgc3VzIG9iamV0aXZvcywgcGVybyBwYXJhIHRlbmVyIG1heW9yIGJhc2UgeSBzZXIgcHJvZmVzaW9uYWxlcyBhbCB0cmFiYWphciBzaWVtcHJlIGhheSBxdWUgY29uZHVjaXJzZSBjb24gaW50ZWdyaWRhZCwgaG9uZXN0aWRhZCB5IMOpdGljYQ==