Actividades

  1. Utilizando la API como (De preferencia utilice esta) OMDb API o cualquiera de su preferencia (Yelp, Google, etc) realice un procedimiento de análisis exploratorio de datos (gráficos y tablas comparativas) con las fuentes obtenidas. Es recomendable utilizar el paquete httr.
  2. Utilizando el paquete rvest realice un procedimiento de web scraping sobre una pagina de su preferencia que contenga tablas de datos que puedan ser extraídas y visualizadas en R (Por ejemplo best places) de igual forma que en el anterior se piden estadísticas de resumen y análisis exploratorio de datos

Análisis Exploratorio de Datos

La API football-data.org, que se utilizará para este ejercicio, proporciona datos acerca del fútbol soccer; como esta API se conforma de distintos conjuntos de datos, utilizaremos uno cuyo autor lo denomina “Competition” que posee una lista de todas los competencias que se realizan en el fútbol y ciertos resultados por cada año. Utilizando la función fromJSON() del paquete jsonlite, se podrá descargar la información deseada; en este caso, solo se tomará las columnas:

  • Liga: Liga de futbol abreviada. Ej: Premier League = PL
  • Año: Año en el cual se realizó el torneo.
  • N_Fechas: Número de fechas jugadas.
  • N_equipos: Número de equipos participante
  • N_juegos: Número de partidos jugados.

La idea no es solo descargar los datos relacionados con un solo año, sino varios años, pero una url solo proporciona los datos de un solo año; entonces para descargar la informción de varios años y de forma automática, se realizará un ciclo for; en este caso se hará del año 2015 al 2017:

library(jsonlite)
futdata<-list()
for(j in 1:3){
  dfut<-fromJSON(paste0("http://api.football-data.org/v1/competitions/?season=",2014+j))
  message("Cargando pagina ", 2014+j)
  futdata[[j]]<-data.frame(dfut$league,dfut$year,dfut$currentMatchday,dfut$numberOfTeams,dfut$numberOfGames)
}
Cargando pagina 2015
Cargando pagina 2016
Cargando pagina 2017

Con la función rbind_pages(), una lista de data frame se comvertirá en un data frame:

dfutbol<-rbind_pages(futdata)
colnames(dfutbol)<-c("Liga","Año","N_Fechas","N_equipos","N_juegos")
head(dfutbol);tail(dfutbol)

Ahora que se tiene los datos limpios y organizados, procederemos con el análisis exploratorio de datos:

dplyr::glimpse(dfutbol)
Observations: 48
Variables: 5
$ Liga      <fctr> BL1, BL2, FL1, FL2, PL, PD, SD, SA, PPL, BL3, DED, C...
$ Año       <fctr> 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015...
$ N_Fechas  <int> 34, 34, 38, 38, 38, 38, 42, 38, 34, 38, 34, 10, 16, 7...
$ N_equipos <int> 18, 18, 20, 20, 20, 20, 22, 20, 18, 20, 18, 32, 24, 2...
$ N_juegos  <int> 306, 306, 380, 380, 380, 380, 462, 380, 306, 380, 306...
summary(dfutbol)
      Liga      Año        N_Fechas       N_equipos       N_juegos    
 BL1    : 3   2015:13   Min.   : 1.00   Min.   :18.0   Min.   : 48.0  
 BL2    : 3   2016:19   1st Qu.: 5.00   1st Qu.:19.5   1st Qu.:306.0  
 CL     : 3   2017:16   Median :34.00   Median :20.0   Median :380.0  
 DED    : 3             Mean   :23.73   Mean   :23.0   Mean   :355.4  
 EL1    : 3             3rd Qu.:38.00   3rd Qu.:24.0   3rd Qu.:400.5  
 FL1    : 3             Max.   :46.00   Max.   :64.0   Max.   :552.0  
 (Other):30                                                           
rbind(tapply(dfutbol$N_Fechas,dfutbol$Año,mean),
tapply(dfutbol$N_equipos,dfutbol$Año,mean),
tapply(dfutbol$N_juegos,dfutbol$Año,mean))
          2015      2016    2017
[1,]  33.23077  32.89474   5.125
[2,]  20.76923  23.78947  23.875
[3,] 357.15385 350.05263 360.375
colMeans(dfutbol[,3:5])
 N_Fechas N_equipos  N_juegos 
 23.72917  23.00000 355.41667 

Se observan los promedios para las fechas, los equipos y juegos por cada año. En promedio, se han realizado aproximadamente 24 fechas entre todos los torneos durante estos últimos tres años, han jugado 23 equipos y aproximadamente 355 juegos.

rbind(tapply(dfutbol$N_Fechas,dfutbol$Año,median),
tapply(dfutbol$N_equipos,dfutbol$Año,median),
tapply(dfutbol$N_juegos,dfutbol$Año,median))
     2015 2016  2017
[1,]   38   38   4.5
[2,]   20   20  20.0
[3,]  380  380 380.0
apply(dfutbol[,3:5],2,median)
 N_Fechas N_equipos  N_juegos 
       34        20       380 
rbind(tapply(dfutbol$N_Fechas,dfutbol$Año,var),
tapply(dfutbol$N_equipos,dfutbol$Año,var),
tapply(dfutbol$N_juegos,dfutbol$Año,var))
           2015       2016        2017
[1,]   87.69231   195.8772    24.91667
[2,]   14.35897   106.1754   127.18333
[3,] 9704.30769 25587.7193 20139.85000
apply(dfutbol[,3:5],2,var)
   N_Fechas   N_equipos    N_juegos 
  282.11658    86.80851 18725.69504 

Varianza para las 3 variables por cada año. Hay una gran variación entre los números de juegos realizados entre los distintos torneos.

cor(dfutbol[,3:5])
            N_Fechas  N_equipos   N_juegos
N_Fechas   1.0000000 -0.3049551  0.3908054
N_equipos -0.3049551  1.0000000 -0.4659933
N_juegos   0.3908054 -0.4659933  1.0000000

Las variables no están altamente correlacionadas.

library(ggplot2)
ggplot(dfutbol,aes(Año,N_Fechas,fill=Liga))+geom_bar(stat = "identity",position = "dodge",colour="black")+ggtitle("N. de Fechas por Año según la Liga")

Hay mucha diferencia entre los tres graficos, sobre todo en el 2017, y es entendible debido a que no se han jugado todas las fechas. Aunque la liga BSA es la que lleva más fechas cumplidas; si se comparan respecto a los dos años anteriores, al finalizar este año es posible que otras ligas tengan un mayor número de fechas.

ggplot(dfutbol,aes(Año,N_equipos,fill=Liga))+geom_bar(stat = "identity",position = "dodge",colour="black")+ggtitle("N. de Equipos por Año según la Liga")

Según el gráfico, el número de equipos ha sido similar durante los tres años, la mayoria de las ligas poseen el mísmo número de equipos; la DFB-poka (DFB), es la liga con el mayor número de equipos.

ggplot(dfutbol,aes(Año,N_juegos,fill=Liga))+geom_bar(stat = "identity",position = "dodge",colour="black")+ggtitle("N. de Juegos por Año según la Liga")

El número de partidos jugados por cada año, es similar; nótese que no hay la misma cantidad de ligas en cada año.

Web Scraping

A continuación se realiza un procedimiento web scraping a la pagina http://www.sobrefutbol.com/torneos/torneo_colombiano.htm la cual contiene información sobre los campeones del Fútbol Colombiano de primera división.

Utilizaremos el paquete rvest creado por Hadley Wickham y la función read_html para leer una página web. Esta función es proporcionada por el paquete xml2, que se cargó automáticamente cuando cargamos rvest

library(rvest)
Loading required package: xml2
url <- 'http://www.sobrefutbol.com/torneos/torneo_colombiano.htm'
webpage <- read_html(url)

A continuación, utilizamos las funciones html_nodes y html_table (de rvest) para extraer el elemento de tabla HTML y convertirlo en un data frame.

data <- html_nodes(webpage, 'table')
datos <- html_table(data)[[1]]
head(datos)

Hacemos uso de la libreria ggplot2 para realizar distintos gráficos

library(ggplot2)

Torneo Colombia

El torneo colombia se realizó entre el año 1948 al 2001.

Extraemos las columnas de Campeón y Subcampeón del Torneo Colombia

TC <- datos[c(36:47,49:89), 2:3]
names(TC) <- c("Campeón", "Subcampeón")
head(TC)

Realizamos un gráfico de barras de los equipos campeones del torneo colombia

TCc <- as.data.frame(TC[,1])
names(TCc) <- c("Campeón")
ggplot(TCc, aes(x = Campeón))+ geom_bar()+
  scale_y_continuous(name = "Frecuencia")+theme(axis.text.x = element_text(angle = 90, hjust = 1))

Realizamos un gráfico de barras de los equipos subcampeones del torneo colombia

TCs <- as.data.frame(TC[,2])
names(TCs) <- c("Subcampeón")
ggplot(TCs, aes(x = Subcampeón))+ geom_bar()+
  scale_y_continuous(name = "Frecuencia")+theme(axis.text.x = element_text(angle = 90, hjust = 1))

Torneo Apertura/Finalización

El torneo apertura/finalización se lleva a cabo desde el año 2002 hasta la fecha.

Extraemos las columnas de Campeón y Subcampeón del Torneo Apertura/Finalización

TAF <- datos[3:33, 2:3]
names(TAF) <- c("Campeón", "Subcampeón")
head(TAF)

Realizamos un gráfico de los equipos campeones del Torneo Apertura/Finalización

TAFc <- as.data.frame(TAF[,1])
names(TAFc) <- c("Campeón")
ggplot(TAFc, aes(x = Campeón))+ geom_bar()+
  scale_y_continuous(name = "Frecuencia")+theme(axis.text.x = element_text(angle = 90, hjust = 1))

Realizamos un gráfico de los equipos subcampeones del Torneo Apertura/Finalización

TAFs <- as.data.frame(TAF[,2])
names(TAFs) <- c("Subcampeón")
ggplot(TAFs, aes(x = Subcampeón))+ geom_bar()+
  scale_y_continuous(name = "Frecuencia")+theme(axis.text.x = element_text(angle = 90, hjust = 1))

LS0tDQp0aXRsZTogIlRhbGxlciBOby4gMSBkZSBSIEF2YW56YWRvIg0KYXV0aG9yOiAiSG9yYWNpbyBNLiBNYXJydWdvIFAuICYgSnVsaW8gQy4gRXNxdWl2ZWwgUi4iDQpvdXRwdXQ6IGh0bWxfbm90ZWJvb2sNCi0tLQ0KDQojIyBBY3RpdmlkYWRlcw0KMS4gVXRpbGl6YW5kbyBsYSBfXypBUEkqX18gY29tbyAoRGUgcHJlZmVyZW5jaWEgdXRpbGljZSBlc3RhKSBPTURiIEFQSSBvIGN1YWxxdWllcmEgZGUgc3UgcHJlZmVyZW5jaWEgKCpZZWxwLCBHb29nbGUsIGV0YyopIHJlYWxpY2UgdW4gcHJvY2VkaW1pZW50byBkZSBhbuFsaXNpcyBleHBsb3JhdG9yaW8gZGUgZGF0b3MgKGdy4WZpY29zIHkgdGFibGFzIGNvbXBhcmF0aXZhcykgY29uIGxhcyBmdWVudGVzIG9idGVuaWRhcy4gRXMgcmVjb21lbmRhYmxlIHV0aWxpemFyIGVsIHBhcXVldGUgKipgaHR0cmAqKi4NCjIuIFV0aWxpemFuZG8gZWwgcGFxdWV0ZSAqKmBydmVzdGAqKiByZWFsaWNlIHVuIHByb2NlZGltaWVudG8gZGUgKndlYiBzY3JhcGluZyogc29icmUgdW5hIHBhZ2luYSBkZSBzdSBwcmVmZXJlbmNpYSBxdWUgY29udGVuZ2EgdGFibGFzIGRlIGRhdG9zIHF1ZSBwdWVkYW4gc2VyIGV4dHJh7WRhcyB5IHZpc3VhbGl6YWRhcyBlbiAqKmBSYCoqIChQb3IgZWplbXBsbyBiZXN0IHBsYWNlcykgZGUgaWd1YWwgZm9ybWEgcXVlIGVuIGVsIGFudGVyaW9yIHNlIHBpZGVuIGVzdGFk7XN0aWNhcyBkZSByZXN1bWVuIHkgYW7hbGlzaXMgZXhwbG9yYXRvcmlvIGRlIGRhdG9zDQoNCiMjIyBBbuFsaXNpcyBFeHBsb3JhdG9yaW8gZGUgRGF0b3MNCg0KTGEgQVBJICoqZm9vdGJhbGwtZGF0YS5vcmcqKiwgcXVlIHNlIHV0aWxpemFy4SBwYXJhIGVzdGUgZWplcmNpY2lvLCBwcm9wb3JjaW9uYSBkYXRvcyBhY2VyY2EgZGVsIGb6dGJvbCBzb2NjZXI7IGNvbW8gZXN0YSBBUEkgc2UgY29uZm9ybWEgZGUgZGlzdGludG9zIGNvbmp1bnRvcyBkZSBkYXRvcywgdXRpbGl6YXJlbW9zIHVubyBjdXlvIGF1dG9yIGxvIGRlbm9taW5hICJDb21wZXRpdGlvbiIgcXVlIHBvc2VlIHVuYSBsaXN0YSBkZSB0b2RhcyBsb3MgY29tcGV0ZW5jaWFzIHF1ZSBzZSByZWFsaXphbiBlbiBlbCBm+nRib2wgeSBjaWVydG9zIHJlc3VsdGFkb3MgcG9yIGNhZGEgYfFvLiBVdGlsaXphbmRvIGxhIGZ1bmNp824gYGZyb21KU09OKClgIGRlbCBwYXF1ZXRlIGBqc29ubGl0ZWAsIHNlIHBvZHLhIGRlc2NhcmdhciBsYSBpbmZvcm1hY2nzbiBkZXNlYWRhOyBlbiBlc3RlIGNhc28sIHNvbG8gc2UgdG9tYXLhIGxhcyBjb2x1bW5hczoNCg0KKiBMaWdhOiBMaWdhIGRlIGZ1dGJvbCBhYnJldmlhZGEuIEVqOiAqUHJlbWllciBMZWFndWUgPSBQTCoNCiogKipB8W86KiogQfFvIGVuIGVsIGN1YWwgc2UgcmVhbGl68yBlbCB0b3JuZW8uDQoqICoqTl9GZWNoYXM6KiogTvptZXJvIGRlIGZlY2hhcyBqdWdhZGFzLg0KKiAqKk5fZXF1aXBvczoqKiBO+m1lcm8gZGUgZXF1aXBvcyBwYXJ0aWNpcGFudGUNCiogKipOX2p1ZWdvczoqKiBO+m1lcm8gZGUgcGFydGlkb3MganVnYWRvcy4NCg0KTGEgaWRlYSBubyBlcyBzb2xvIGRlc2NhcmdhciBsb3MgZGF0b3MgcmVsYWNpb25hZG9zIGNvbiB1biBzb2xvIGHxbywgc2lubyB2YXJpb3MgYfFvcywgcGVybyB1bmEgdXJsIHNvbG8gcHJvcG9yY2lvbmEgbG9zIGRhdG9zIGRlIHVuIHNvbG8gYfFvOyBlbnRvbmNlcyBwYXJhIGRlc2NhcmdhciBsYSBpbmZvcm1jafNuIGRlIHZhcmlvcyBh8W9zIHkgZGUgZm9ybWEgYXV0b23hdGljYSwgc2UgcmVhbGl6YXLhIHVuIGNpY2xvIGBmb3JgOyBlbiBlc3RlIGNhc28gc2UgaGFy4SBkZWwgYfFvIDIwMTUgYWwgMjAxNzogDQoNCmBgYHtyfQ0KbGlicmFyeShqc29ubGl0ZSkNCmZ1dGRhdGE8LWxpc3QoKQ0KZm9yKGogaW4gMTozKXsNCiAgZGZ1dDwtZnJvbUpTT04ocGFzdGUwKCJodHRwOi8vYXBpLmZvb3RiYWxsLWRhdGEub3JnL3YxL2NvbXBldGl0aW9ucy8/c2Vhc29uPSIsMjAxNCtqKSkNCiAgbWVzc2FnZSgiQ2FyZ2FuZG8gcGFnaW5hICIsIDIwMTQraikNCiAgZnV0ZGF0YVtbal1dPC1kYXRhLmZyYW1lKGRmdXQkbGVhZ3VlLGRmdXQkeWVhcixkZnV0JGN1cnJlbnRNYXRjaGRheSxkZnV0JG51bWJlck9mVGVhbXMsZGZ1dCRudW1iZXJPZkdhbWVzKQ0KfQ0KDQpgYGANCkNvbiBsYSBmdW5jafNuIGByYmluZF9wYWdlcygpYCwgdW5hIGxpc3RhIGRlIGRhdGEgZnJhbWUgc2UgY29tdmVydGly4SBlbiB1biBkYXRhIGZyYW1lOg0KDQpgYGB7cn0NCmRmdXRib2w8LXJiaW5kX3BhZ2VzKGZ1dGRhdGEpDQpjb2xuYW1lcyhkZnV0Ym9sKTwtYygiTGlnYSIsIkHxbyIsIk5fRmVjaGFzIiwiTl9lcXVpcG9zIiwiTl9qdWVnb3MiKQ0KaGVhZChkZnV0Ym9sKTt0YWlsKGRmdXRib2wpDQoNCmBgYA0KDQpBaG9yYSBxdWUgc2UgdGllbmUgbG9zIGRhdG9zIGxpbXBpb3MgeSBvcmdhbml6YWRvcywgcHJvY2VkZXJlbW9zIGNvbiBlbCAgYW7hbGlzaXMgZXhwbG9yYXRvcmlvIGRlIGRhdG9zOg0KYGBge3J9DQpkcGx5cjo6Z2xpbXBzZShkZnV0Ym9sKQ0KDQpgYGANCmBgYHtyfQ0Kc3VtbWFyeShkZnV0Ym9sKQ0KYGBgDQpgYGB7cn0NCnJiaW5kKHRhcHBseShkZnV0Ym9sJE5fRmVjaGFzLGRmdXRib2wkQfFvLG1lYW4pLA0KdGFwcGx5KGRmdXRib2wkTl9lcXVpcG9zLGRmdXRib2wkQfFvLG1lYW4pLA0KdGFwcGx5KGRmdXRib2wkTl9qdWVnb3MsZGZ1dGJvbCRB8W8sbWVhbikpDQpjb2xNZWFucyhkZnV0Ym9sWywzOjVdKQ0KYGBgDQpTZSBvYnNlcnZhbiBsb3MgcHJvbWVkaW9zIHBhcmEgbGFzIGZlY2hhcywgbG9zIGVxdWlwb3MgeSBqdWVnb3MgcG9yIGNhZGEgYfFvLiBFbiBwcm9tZWRpbywgc2UgaGFuIHJlYWxpemFkbyBhcHJveGltYWRhbWVudGUgMjQgZmVjaGFzIGVudHJlIHRvZG9zIGxvcyB0b3JuZW9zIGR1cmFudGUgZXN0b3Mg+mx0aW1vcyB0cmVzIGHxb3MsIGhhbiBqdWdhZG8gMjMgZXF1aXBvcyB5IGFwcm94aW1hZGFtZW50ZSAzNTUganVlZ29zLg0KDQpgYGB7cn0NCnJiaW5kKHRhcHBseShkZnV0Ym9sJE5fRmVjaGFzLGRmdXRib2wkQfFvLG1lZGlhbiksDQp0YXBwbHkoZGZ1dGJvbCROX2VxdWlwb3MsZGZ1dGJvbCRB8W8sbWVkaWFuKSwNCnRhcHBseShkZnV0Ym9sJE5fanVlZ29zLGRmdXRib2wkQfFvLG1lZGlhbikpDQphcHBseShkZnV0Ym9sWywzOjVdLDIsbWVkaWFuKQ0KYGBgDQpgYGB7cn0NCnJiaW5kKHRhcHBseShkZnV0Ym9sJE5fRmVjaGFzLGRmdXRib2wkQfFvLHZhciksDQp0YXBwbHkoZGZ1dGJvbCROX2VxdWlwb3MsZGZ1dGJvbCRB8W8sdmFyKSwNCnRhcHBseShkZnV0Ym9sJE5fanVlZ29zLGRmdXRib2wkQfFvLHZhcikpDQphcHBseShkZnV0Ym9sWywzOjVdLDIsdmFyKQ0KYGBgDQpWYXJpYW56YSBwYXJhIGxhcyAzIHZhcmlhYmxlcyBwb3IgY2FkYSBh8W8uIEhheSB1bmEgZ3JhbiB2YXJpYWNp824gZW50cmUgbG9zIG76bWVyb3MgZGUganVlZ29zIHJlYWxpemFkb3MgZW50cmUgbG9zIGRpc3RpbnRvcyB0b3JuZW9zLg0KYGBge3J9DQpjb3IoZGZ1dGJvbFssMzo1XSkNCmBgYA0KTGFzIHZhcmlhYmxlcyBubyBlc3ThbiBhbHRhbWVudGUgY29ycmVsYWNpb25hZGFzLg0KDQpgYGB7ciAsZmlnLmFsaWduPSdjZW50ZXInfQ0KbGlicmFyeShnZ3Bsb3QyKQ0KZ2dwbG90KGRmdXRib2wsYWVzKEHxbyxOX0ZlY2hhcyxmaWxsPUxpZ2EpKStnZW9tX2JhcihzdGF0ID0gImlkZW50aXR5Iixwb3NpdGlvbiA9ICJkb2RnZSIsY29sb3VyPSJibGFjayIpK2dndGl0bGUoIk4uIGRlIEZlY2hhcyBwb3IgQfFvIHNlZ/puIGxhIExpZ2EiKQ0KYGBgDQpIYXkgbXVjaGEgZGlmZXJlbmNpYSBlbnRyZSBsb3MgdHJlcyBncmFmaWNvcywgc29icmUgdG9kbyBlbiBlbCAyMDE3LCB5IGVzIGVudGVuZGlibGUgZGViaWRvIGEgcXVlIG5vIHNlIGhhbiBqdWdhZG8gdG9kYXMgbGFzIGZlY2hhcy4gQXVucXVlIGxhIGxpZ2EgQlNBIGVzIGxhIHF1ZSBsbGV2YSBt4XMgZmVjaGFzIGN1bXBsaWRhczsgc2kgc2UgY29tcGFyYW4gcmVzcGVjdG8gYSBsb3MgZG9zIGHxb3MgYW50ZXJpb3JlcywgYWwgZmluYWxpemFyIGVzdGUgYfFvIGVzIHBvc2libGUgcXVlIG90cmFzIGxpZ2FzIHRlbmdhbiB1biBtYXlvciBu+m1lcm8gZGUgZmVjaGFzLg0KDQpgYGB7ciAsZmlnLmFsaWduPSdjZW50ZXInfQ0KZ2dwbG90KGRmdXRib2wsYWVzKEHxbyxOX2VxdWlwb3MsZmlsbD1MaWdhKSkrZ2VvbV9iYXIoc3RhdCA9ICJpZGVudGl0eSIscG9zaXRpb24gPSAiZG9kZ2UiLGNvbG91cj0iYmxhY2siKStnZ3RpdGxlKCJOLiBkZSBFcXVpcG9zIHBvciBB8W8gc2Vn+m4gbGEgTGlnYSIpDQpgYGANClNlZ/puIGVsIGdy4WZpY28sIGVsIG76bWVybyBkZSBlcXVpcG9zIGhhIHNpZG8gc2ltaWxhciAgZHVyYW50ZSBsb3MgdHJlcyBh8W9zLCBsYSBtYXlvcmlhIGRlIGxhcyBsaWdhcyBwb3NlZW4gZWwgbe1zbW8gbvptZXJvIGRlIGVxdWlwb3M7IGxhIERGQi1wb2thIChERkIpLCBlcyBsYSBsaWdhIGNvbiBlbCBtYXlvciBu+m1lcm8gZGUgZXF1aXBvcy4NCg0KYGBge3IgLGZpZy5hbGlnbj0nY2VudGVyJ30NCmdncGxvdChkZnV0Ym9sLGFlcyhB8W8sTl9qdWVnb3MsZmlsbD1MaWdhKSkrZ2VvbV9iYXIoc3RhdCA9ICJpZGVudGl0eSIscG9zaXRpb24gPSAiZG9kZ2UiLGNvbG91cj0iYmxhY2siKStnZ3RpdGxlKCJOLiBkZSBKdWVnb3MgcG9yIEHxbyBzZWf6biBsYSBMaWdhIikNCmBgYA0KRWwgbvptZXJvIGRlIHBhcnRpZG9zIGp1Z2Fkb3MgcG9yIGNhZGEgYfFvLCBlcyBzaW1pbGFyOyBu83Rlc2UgcXVlIG5vIGhheSBsYSBtaXNtYSBjYW50aWRhZCBkZSBsaWdhcyBlbiBjYWRhIGHxby4NCg0KIyMjIFdlYiBTY3JhcGluZw0KDQpBIGNvbnRpbnVhY2nzbiBzZSByZWFsaXphIHVuIHByb2NlZGltaWVudG8gKndlYiBzY3JhcGluZyogYSBsYSBwYWdpbmEgPGh0dHA6Ly93d3cuc29icmVmdXRib2wuY29tL3Rvcm5lb3MvdG9ybmVvX2NvbG9tYmlhbm8uaHRtPiBsYSBjdWFsIGNvbnRpZW5lIGluZm9ybWFjafNuIHNvYnJlIGxvcyBjYW1wZW9uZXMgZGVsIEb6dGJvbCBDb2xvbWJpYW5vIGRlIHByaW1lcmEgZGl2aXNp824uIA0KDQoNClV0aWxpemFyZW1vcyBlbCBwYXF1ZXRlICoqcnZlc3QqKiBjcmVhZG8gcG9yIEhhZGxleSBXaWNraGFtIHkgbGEgZnVuY2nzbiByZWFkX2h0bWwgcGFyYSBsZWVyIHVuYSBw4WdpbmEgd2ViLiBFc3RhIGZ1bmNp824gZXMgcHJvcG9yY2lvbmFkYSBwb3IgZWwgcGFxdWV0ZSB4bWwyLCBxdWUgc2UgY2FyZ/MgYXV0b23hdGljYW1lbnRlIGN1YW5kbyBjYXJnYW1vcyBydmVzdA0KDQpgYGB7cn0NCmxpYnJhcnkocnZlc3QpDQp1cmwgPC0gJ2h0dHA6Ly93d3cuc29icmVmdXRib2wuY29tL3Rvcm5lb3MvdG9ybmVvX2NvbG9tYmlhbm8uaHRtJw0Kd2VicGFnZSA8LSByZWFkX2h0bWwodXJsKQ0KYGBgDQoNCkEgY29udGludWFjafNuLCB1dGlsaXphbW9zIGxhcyBmdW5jaW9uZXMgaHRtbF9ub2RlcyB5IGh0bWxfdGFibGUgKGRlIHJ2ZXN0KSBwYXJhIGV4dHJhZXIgZWwgZWxlbWVudG8gZGUgdGFibGEgSFRNTCB5IGNvbnZlcnRpcmxvIGVuIHVuIGRhdGEgZnJhbWUuDQoNCmBgYHtyfQ0KZGF0YSA8LSBodG1sX25vZGVzKHdlYnBhZ2UsICd0YWJsZScpDQpkYXRvcyA8LSBodG1sX3RhYmxlKGRhdGEpW1sxXV0NCmhlYWQoZGF0b3MpDQpgYGANCg0KSGFjZW1vcyB1c28gZGUgbGEgbGlicmVyaWEgKmdncGxvdDIqIHBhcmEgcmVhbGl6YXIgZGlzdGludG9zIGdy4WZpY29zDQpgYGB7cn0NCmxpYnJhcnkoZ2dwbG90MikNCmBgYA0KDQoNCiNUb3JuZW8gQ29sb21iaWENCkVsIHRvcm5lbyBjb2xvbWJpYSBzZSByZWFsaXrzIGVudHJlIGVsIGHxbyAxOTQ4IGFsIDIwMDEuDQoNCg0KRXh0cmFlbW9zIGxhcyBjb2x1bW5hcyBkZSBDYW1wZfNuIHkgU3ViY2FtcGXzbiBkZWwgVG9ybmVvIENvbG9tYmlhDQpgYGB7cn0NClRDIDwtIGRhdG9zW2MoMzY6NDcsNDk6ODkpLCAyOjNdDQpuYW1lcyhUQykgPC0gYygiQ2FtcGXzbiIsICJTdWJjYW1wZfNuIikNCmhlYWQoVEMpDQpgYGANCg0KUmVhbGl6YW1vcyB1biBncuFmaWNvIGRlIGJhcnJhcyBkZSBsb3MgZXF1aXBvcyBjYW1wZW9uZXMgZGVsIHRvcm5lbyBjb2xvbWJpYSANCg0KYGBge3J9DQpUQ2MgPC0gYXMuZGF0YS5mcmFtZShUQ1ssMV0pDQpuYW1lcyhUQ2MpIDwtIGMoIkNhbXBl824iKQ0KZ2dwbG90KFRDYywgYWVzKHggPSBDYW1wZfNuKSkrIGdlb21fYmFyKCkrDQogIHNjYWxlX3lfY29udGludW91cyhuYW1lID0gIkZyZWN1ZW5jaWEiKSt0aGVtZShheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChhbmdsZSA9IDkwLCBoanVzdCA9IDEpKQ0KYGBgDQoNCg0KUmVhbGl6YW1vcyB1biBncuFmaWNvIGRlIGJhcnJhcyBkZSBsb3MgZXF1aXBvcyBzdWJjYW1wZW9uZXMgZGVsIHRvcm5lbyBjb2xvbWJpYSANCg0KYGBge3J9DQpUQ3MgPC0gYXMuZGF0YS5mcmFtZShUQ1ssMl0pDQpuYW1lcyhUQ3MpIDwtIGMoIlN1YmNhbXBl824iKQ0KZ2dwbG90KFRDcywgYWVzKHggPSBTdWJjYW1wZfNuKSkrIGdlb21fYmFyKCkrDQogIHNjYWxlX3lfY29udGludW91cyhuYW1lID0gIkZyZWN1ZW5jaWEiKSt0aGVtZShheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChhbmdsZSA9IDkwLCBoanVzdCA9IDEpKQ0KYGBgDQoNCg0KI1Rvcm5lbyBBcGVydHVyYS9GaW5hbGl6YWNp824NCkVsIHRvcm5lbyBhcGVydHVyYS9maW5hbGl6YWNp824gc2UgbGxldmEgYSBjYWJvIGRlc2RlIGVsIGHxbyAyMDAyIGhhc3RhIGxhIGZlY2hhLg0KDQpFeHRyYWVtb3MgbGFzIGNvbHVtbmFzIGRlIENhbXBl824geSBTdWJjYW1wZfNuIGRlbCBUb3JuZW8gQXBlcnR1cmEvRmluYWxpemFjafNuIA0KDQpgYGB7cn0NClRBRiA8LSBkYXRvc1szOjMzLCAyOjNdDQpuYW1lcyhUQUYpIDwtIGMoIkNhbXBl824iLCAiU3ViY2FtcGXzbiIpDQpoZWFkKFRBRikNCmBgYA0KDQoNClJlYWxpemFtb3MgdW4gZ3LhZmljbyBkZSBsb3MgZXF1aXBvcyBjYW1wZW9uZXMgZGVsIFRvcm5lbyBBcGVydHVyYS9GaW5hbGl6YWNp824gDQoNCg0KYGBge3J9DQpUQUZjIDwtIGFzLmRhdGEuZnJhbWUoVEFGWywxXSkNCm5hbWVzKFRBRmMpIDwtIGMoIkNhbXBl824iKQ0KZ2dwbG90KFRBRmMsIGFlcyh4ID0gQ2FtcGXzbikpKyBnZW9tX2JhcigpKw0KICBzY2FsZV95X2NvbnRpbnVvdXMobmFtZSA9ICJGcmVjdWVuY2lhIikrdGhlbWUoYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoYW5nbGUgPSA5MCwgaGp1c3QgPSAxKSkNCmBgYA0KDQpSZWFsaXphbW9zIHVuIGdy4WZpY28gZGUgbG9zIGVxdWlwb3Mgc3ViY2FtcGVvbmVzIGRlbCBUb3JuZW8gQXBlcnR1cmEvRmluYWxpemFjafNuDQoNCmBgYHtyfQ0KVEFGcyA8LSBhcy5kYXRhLmZyYW1lKFRBRlssMl0pDQpuYW1lcyhUQUZzKSA8LSBjKCJTdWJjYW1wZfNuIikNCmdncGxvdChUQUZzLCBhZXMoeCA9IFN1YmNhbXBl824pKSsgZ2VvbV9iYXIoKSsNCiAgc2NhbGVfeV9jb250aW51b3VzKG5hbWUgPSAiRnJlY3VlbmNpYSIpK3RoZW1lKGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KGFuZ2xlID0gOTAsIGhqdXN0ID0gMSkpDQoNCmBgYA0KDQoNCg0K