Trabajo elaborado para la asignatura “Programación y manejo de datos en la era del Big Data” de la Universitat de València durante el curso 2020-2021. El repo del trabajo está aquí. La página web de la asignatura y los trabajos de mis compañeros pueden verse aquí.


1. Introducción

El trabajo consiste en la obtención y manipulación de datos obtenidos de una de las principales fuentes de alojamiento de videos: Youtube, así como, la creación de diferentes gráficos y tablas ilustrativas a partir de los datos que obtengamos de esta. Los datos a manipular no son datos cualesquiera; no son datos de estadísticas generales, si no, datos de un canal concreto, el canal de música de una artista que me gusta mucho: Bad Gyal. Quería hacer un trabajo más ameno, sobre algo con lo que estuviera cómodo trabajando, pues, es un trabajo de bastantes horas-días y mejor si resulta entretenido. Es por esto la elección del tema.

¿Quién es: ?

Alba Farelo, más conocida como Bad Gyal es una artista de la escena musical urbana española, nacida en Vilassar de Mar (Catalunya) en 1997.

Alba comenzaría su andadura musical en la escena urbana de nuestro país en 2016, en un momento donde el género “trap” estaba al alza y los pocos artistas que explotaban este género estaban cosechando grandes éxitos. La popularidad de la artista comenzó a aumentar tras subir a Youtube una cover casera en catalán del mítico “Work” de Rihanna. Tras este primer tema que la colocaría de lleno dentro de la escena, le sucederían otros como temas “Indapanden”, “Leiriss” o “No pierdo Nada”, pero, no sería hasta la publicación de su primera mixtape “Slow Wine” el 9 de noviembre de 2016 de la mano de Fake Guido cuando comenzaría su verdadera andadura por los éxitos de nuestro país. El sencillo Fiebre dentro de la primera mixtape de Alba cosecharía un gran éxito, siendo esta canción uno de los temás más reconocidos a día de hoy de la artista. A partir de este momento, le sucederían grandes proyectos como su segunda mixtape “Worldwide Angel” en 2018 y giras por festivales y salas de España y de el resto del mundo (incluyendo ciudades como Tokio, Hong Kong, EEUU, México…).

En 2019 firmaría con el sello Interscope Records, siendo esto un punto de inflexión para Alba, pues, el éxito que había cosechado hasta el momento comenzaría a multiplicarse, hasta el punto de, obtener su primer Disco de Oro con su single Santa María.

Su música ha sido catalogada como una fusión entre reggaeton, dancehall y ritmos urbanos como trap. Durante su carrera, ha recibido constantes críticas de ciertos sectores de público por el uso de auto-tune en sus conciertos o canciones. Alba ha sabido como aprovechar esta herramienta de voz, convertirla en uno de sus sellos de identidad.

Bad Gyal a día de hoy en nuestro país está cosechando grandes éxitos y es que, para el público joven con gustos musicales que tienden a la música urbana, las canciones de Alba, son canciones pegadizas y que atraen, ya sea por el ritmo que emanan o por el carácter explícito y “pegadizo” de sus letras.

2. Procedencia y explicación

En este apartado y los que vengan a continuación voy a centrarme en explicar la procedencia de los datos, los paquetes que he ido utilizando, así como la limpieza de las variables no tan necesarias y los problemas que me han ido surgiendo a lo largo del trabajo.

Los datos proceden mayoritariamente de Youtube, tanto del canal principal de Bad Gyal, como su canal de VEVO. Los datos de los videos de ambos canales los he obtenido gracias al paquete tuber, mediante este he podido obtener datos acerca de las visitas, likes, dislikes y comentarios de cada video.

Comenzar a utilizar este paquete no me ha resultado nada sencillo, pues, había que habilitar unas API’s, las cuales, hasta ese momento no sabía muy bien lo que eran. Para poder activar el paquete y las API, he seguido este tutorial.

A continuación, siguiendo el tutorial citado había que crear un OAuth client y autentificarlo. Adjunto el código base que he utilizado para abrir el OAuth 1 y autentificarlo: procedimiento requerido para poder obtener los datos de cualquier video o canal procedentes de Youtube mediante el paquete tuber.

library(tuber)
client_id <- "932129872577-1bga8vv47tb6ongavh0gjvf7i41ojd4b.apps.googleusercontent.com"
client_secret <- "fgRz-atI5Huuy3X3fsU-vCs7"

yt_oauth(client_id, client_secret)

Obteniendo las estadísticas y datos principales

Una vez abierta el OAuth y con los credenciales autenticados, he procedido a descargar los stats por canal. En mi caso, Bad Gyal tiene 2 canales: VEVO y personal, de forma que, he tenido que hacer el procedimiento 2 veces.

Primero, descargamos los stats de los videos del canal principal mediante la función yt_search(), incluida en el paquete.

bgcanal1<- yt_search(type="video", term="", channel_id = "UC2ypBaYnDvnlbzyAH8w2jsw")
bgcanal1<- bgcanal1 %>% mutate(date = as.Date(publishedAt)) %>% select(video_id, date, title)

Estos stats los arreglamos un poco, ya que, el dataframe resultante es un dataframe que nos da la fecha de publicación con la hora exacta, la cual, no la necesitamos y también nos da datos acerca de la descripción de cada video, los cuales tampoco necesitaremos usar. De esta forma, tendremos un dataframe con el id, la fecha (modificada para que no aparezca la hora) y el título.

Si queremos más detalles acerca de los videos tenemos que utilizar la función get_stats() dentro del paquete. Como queremos hacerlo de todos los videos del canal y tenemos la id en el dataframe anterior, podemos utilizar un bucle para obtener los stats de todos directamente en vez de ir video a video. Para coger los stats de todos los videos utilizaremos un lapply(), esta función nos pide el dataframe y luego la función que utilizaremos, en clase habíamos utilizado una función ya existente: “mean” para la media. En nuestro caso, crearemos primero una función para que coja los stats de todas las variables y no tengamos que introducir la id de los videos nosotros manualmente una por una, pues, !Ojo, hay 32 videos! y con esta función ya creada, el lapply será más facil.

#Creamos la función que meteremos en el lapply. Esta función hace que por a cada id del dataframe anterior se aplique el get_stats(), para así obtener detalles más concretos de cada video del canal.
funcion_stats_videos <- function(x) {
  get_stats(video_id = x)
}

bgstatlista <- lapply(bgcanal1$video_id, funcion_stats_videos)

De esta forma, el lapply nos devolverá una lista, la cual, para transformarla en dataframe utilizaremos el siguiente código:

bg_stats_videos<- do.call(rbind.data.frame, bgstatlista) 

Como vemos, ya tenemos 2 dataframes, y solo nos quedará juntarlos para tener todos estos datos-stats en uno solo:

#Ahora hacemos un full join para ambos dataframes.
bg_stats_full<- full_join(bgcanal1, bg_stats_videos, by=c("video_id"="id"))

Ahora, repetimos las mismas acciones para el 2º canal, el VEVO.

#Los primeros stats del canal VEVO
bgcanal2<- yt_search(type="video", term="", channel_id = "UCOmtuk6Mp66DRFcg81kr3eQ")
bgcanal2<- bgcanal2 %>% mutate(date = as.Date(publishedAt)) %>% select(video_id, date, title)

#Bucle para obtener los segundos stats 
bgstatlista2 <- lapply(bgcanal2$video_id, funcion_stats_videos)

#Para transformar la lista en dataframe, utilizamos la función
bg_stats_videos2<- do.call(rbind.data.frame, bgstatlista2)

#Ahora hacemos un full join para ambos dataframes.
bg_stats_full2<- full_join(bgcanal2, bg_stats_videos2, by=c("video_id"="id"))

Así, finalmente agrupamos los dataframes de los 2 canales en un solo dataframe, y además lo arreglamos un poco: cambiamos los nombres de las columnas, seleccionamos las variables que queremos que aparezcan y pasamos de character a numeric las columnas tanto de likes, comentarios y visitas.

#Unimos
bg_stats_ambos_canales<- full_join(bg_stats_full, bg_stats_full2)

#Arreglamos un poco el dataframe
bg_stats_ambos_canales<- bg_stats_ambos_canales %>%
  select(title, date, viewCount, likeCount, commentCount) %>%
  rename(titulo=title, fecha=date, visitas=viewCount, likes=likeCount, comentarios=commentCount)%>%
  arrange(fecha)

bg_stats_ambos_canales1<- transform(bg_stats_ambos_canales, visitas=as.numeric(as.character(visitas)),
  likes=as.numeric(as.character(likes)),
  comentarios=as.numeric(as.character(comentarios)))


#- Para finalizar, limpiamos el Global Env.
objetos_no_borrar <- c("bg_stats_ambos_canales1")
rm(list = ls()[!ls() %in% objetos_no_borrar])

El dataframe resultante será de esta forma:

Tabla: 3 videos más vistos

A partir del dataframe anterior, voy a mostrar los 3 videos más vistos de los canales de Bad Gyal, en este caso, utilizaré el paquete gt para la tabla, en vez del paquete que ya he usado antes: reactable.

Primero, modificamos el dataframe de forma que nos muestre los títulos ordenados de más visitas a menos, y a su vez, que nos muestre también cuantos likes y comentarios tienen, para, hacer un escalado de color desde el que más tenga al que menos tenga. A partir de aquí, generaremos una tabla que muestre los 3 primeros títulos.

#Modificamos el dataframe
bg_stats_3_primeros <- bg_stats_ambos_canales1 %>% slice_max(visitas, n=3)

#Hacemos la tabla a nuestro gusto: colores con escala, desde una paleta verde para los valores más altos a una paleta de rojos para los valores más bajos.
tabla_gt<- bg_stats_3_primeros %>% 
  gt() %>% 
  tab_header(title= md("**3 videos más vistos**")) %>% 
  tab_options(heading.background.color = "#91ede4", column_labels.font.weight =  "bold")%>%
  cols_align(align="center") %>%
  tab_style(cell_borders(sides="all", color="black"), cells_body())%>%
  data_color( 
    columns = vars(visitas), 
    colors = scales::col_numeric( 
      palette = c(
        "#db535f","#ffe16b", "#64de68"), # 
      domain = c(max(visitas), min(visitas)) )
    ) %>% 
  data_color( 
    columns = vars(likes), 
    colors = scales::col_numeric( 
      palette = c(
        "#db535f","#ffe16b", "#64de68"), # 
      domain = c(max(likes), min(likes)) )
    ) %>%
  data_color( 
    columns = vars(comentarios), 
    colors = scales::col_numeric( 
      palette = c(
        "#db535f","#ffe16b","#64de68"), # 
      domain = c(max(comentarios), min(comentarios)) )
    ) 

#La mostramos
tabla_gt
3 videos más vistos
titulo fecha visitas likes comentarios
BAD GYAL - FIEBRE PROD KING DOUDOU slow wine 2016-11-21 40696362 344967 7508
Bad Gyal - Zorra 2019-12-12 24447615 234120 10789
Kafu Banton, Bad Gyal - Tu Eres Un Bom Bom (Remix) 2020-04-09 20765727 277773 7658

Gráfico de tarta: videos con más likes

En este apartado he querido realizar un gráfico de quesitos o “pie chart” a partir de los 6 videos con más likes. El gráfico lo he hecho a partir del porcentaje de visitas de cada video sobre el total, variable que he tenido que crear, de forma que, se creará un gráfico de trozos de tarta con 6 títulos más una variable que engloba a los videos no seleccionados, porcentaje de la cual será el 100% menos el porcentaje de la suma de los títulos seleccionados.

Para este, nos hemos basado en el modelo básico propuesto en The R Graph Gallery

#Conseguimos en vector el número total de likes (sumando los likes de todos los videos)
suma_total_likes<- bg_stats_ambos_canales1 %>% summarise(suma=sum(likes))
suma_total_likes<- suma_total_likes[1,1]

#Creamos una nueva variable "% likes" para saber el porcentaje de likes que le corresponde a cada video
bg_stats_pie<- bg_stats_ambos_canales1 %>% mutate(`% likes`= likes / suma_total_likes *100) %>% select(titulo, `% likes`)

#Hacemos un slice para aquellos videos con mayor porcentaje de likes y sumamos los likes de todos para ver cuanto %de likes sobre el total(100) hay en estos 6 videos.
bg_6stats_pie<- bg_stats_pie %>% slice_max(`% likes`, n=6) %>% mutate(suma=sum(`% likes`))

#Calculamos la diferencia entre el porcentaje que suman los 6 videos con más likes (el porcentaje anterior) y el total.
dif_6stats<- 100- bg_6stats_pie$suma[1]

#Añadimos esa diferencia como una columna más para nuestro dataframe y la llamamos "otros"; esta columna incluirá los porcentajes de los demás videos-títulos que no han sido incluido en los 6 videos del slice_max.
bg_6stats_pie<- bg_6stats_pie%>%select(titulo, `% likes`) %>% add_row(titulo="Otros", `% likes`=dif_6stats)

#Hacemos el pie chart. Sería como un ggplot + geom_bar() normal, solo que, hacemos un giro de coordenadas con coord_polar para darle forma redonda, el resto de cosas que he ido poniendo son añadidos extra.
bgpie<- ggplot(bg_6stats_pie, aes(x="", y=`% likes`, fill=titulo))+
  geom_bar(stat="identity",width=1, color="white")+
  coord_polar("y", start=0)+
  geom_text(aes(label=paste0(round(`% likes`), "%")), position=position_stack(vjust=0.5))+
  labs(title="Gráfico de tarta: likes")+
   theme_void()

3. Obteniendo más datos: dataframe secundario

El dataframe anterior que agrupa los videos de ambos canales no nos ofrece información sobre las visitas diarias en los videos desde su publicación, por lo que, para hacer diferentes transformaciones más adelante lo que voy a hacer es conseguir de forma “vaga” estas visitas diarias. Para esto, he decidido filtrar del dataframe principal anterior los videos con menos de 1.000.000 visitas y con menos de 1000 comentarios y operar a partir de ahí.

El procedimiento que he seguido ha sido algo laborioso, pues, en este caso no sabía como introduccir un loop para hacerlo más automático y he tenido que ir fila a fila (video a video, de los 17 resultantes por el filtrado que he comentado). Este procedimiento lo detallo a continuación y también en el código que le sigue:

  • Almacenamos en 17 variables-values las fechas a partir del dataframe que hemos filtrado y otra más para la fecha actual, mediante la función today() del paquete lubridate.
  • Almacenamos las visitas de cada video en variables diferentes (de nuevo 17 variables, una por cada video).
  • Calculamos la diferencia en días, es decir, los días que han pasado desde la publicación de los videos y la fecha actual, y almacenamos estos datos de nuevo en 17 variables.
  • Calculamos la media diaria de las visitas de cada video, con la diferencia de días anterior y las visitas totales.
  • Creamos un dataframe para cada video, el cual, se compone de una columna llamada fecha, la cual está construida previamente con la función seq() y otra columna con la media diaria de cada video calculada. Esta primera columna estará formada por los días que han pasado desde la publicación del video hasta la fecha actual, es decir, si un video estuviera publicado 01/01/2020, las filas de la columna serían: 01/01/20, 02/01/20, 03/01/20… así hasta la fecha a día de hoy.
  • Uniremos mediante full_join() cada uno de los dataframes creados, de forma que, tendremos un dataframe en formato wider donde habrá una columna común (fecha) y una columna por cada media de visitas calculada de los videos (17).
#Videos >1.000.000 views y >1000 comentarios
bg_mayor_1<- bg_stats_ambos_canales1 %>% filter(visitas>=1000000, comentarios>=1000)

#Fechas
fechahoy <- today()
fechaPAI<-bg_mayor_1$fecha[1]
fechaINDAPAN<-bg_mayor_1$fecha[2]
fechaMERCA<- bg_mayor_1$fecha[3]
fechaFIEBRE<- bg_mayor_1$fecha[4]
fechaJACA<- bg_mayor_1$fecha[5]
fechaNICE<- bg_mayor_1$fecha[6]
fechaCAND <- bg_mayor_1$fecha[7]
fechaINT<- bg_mayor_1$fecha[8]
fechaMAS<- bg_mayor_1$fecha[9]
fechaOPEN<- bg_mayor_1$fecha[10]
fechaIUAL<- bg_mayor_1$fecha[11]
fechaMARI<- bg_mayor_1$fecha[12]
fechaHOOKAH<- bg_mayor_1$fecha[13]
fechaZORR<- bg_mayor_1$fecha[14]
fechaBOM<- bg_mayor_1$fecha[15]
fechaSEX<- bg_mayor_1$fecha[16]
fechaBLIN<- bg_mayor_1$fecha[17]

#Visitas
numPAI<- bg_mayor_1$visitas[1]
numINDAPAN<- bg_mayor_1$visitas[2]
numMERCA<- bg_mayor_1$visitas[3]
numFIEBRE<- bg_mayor_1$visitas[4]
numJACA<- bg_mayor_1$visitas[5]
numNICE<- bg_mayor_1$visitas[6]
numCAND<- bg_mayor_1$visitas[7]
numINT<- bg_mayor_1$visitas[8]
numMAS<- bg_mayor_1$visitas[9]
numOPEN<- bg_mayor_1$visitas[10]
numIUAL<- bg_mayor_1$visitas[11]
numMARI<- bg_mayor_1$visitas[12]
numHOOKAH<- bg_mayor_1$visitas[13]
numZORR<- bg_mayor_1$visitas[14]
numBOM<- bg_mayor_1$visitas[15]
numSEX<- bg_mayor_1$visitas[16]
numBLIN<- bg_mayor_1$visitas[17]

#Días que han pasado desde fecha video a hoy
difPAI<- difftime(fechahoy, fechaPAI, units="days") %>% as.numeric()
difINDAPAN<- difftime(fechahoy, fechaINDAPAN, units="days") %>% as.numeric()
difMERCA<- difftime(fechahoy, fechaMERCA, units="days") %>% as.numeric()
difFIEBRE<- difftime(fechahoy, fechaFIEBRE, units="days") %>% as.numeric()
difJACA<- difftime(fechahoy, fechaJACA, units="days") %>% as.numeric()
difNICE<- difftime(fechahoy, fechaNICE, units="days") %>% as.numeric()
difCAND<- difftime(fechahoy, fechaCAND, units="days") %>% as.numeric()
difINT<- difftime(fechahoy, fechaINT, units="days") %>% as.numeric()
difMAS<- difftime(fechahoy, fechaMAS, units="days") %>% as.numeric()
difOPEN<- difftime(fechahoy, fechaOPEN, units="days") %>% as.numeric()
difIUAL<- difftime(fechahoy, fechaIUAL, units="days") %>% as.numeric()
difMARI<- difftime(fechahoy, fechaMARI, units="days") %>% as.numeric()
difHOOKAH<- difftime(fechahoy, fechaHOOKAH, units="days") %>% as.numeric()
difZORR<- difftime(fechahoy, fechaZORR, units="days") %>% as.numeric()
difBOM<- difftime(fechahoy, fechaBOM, units="days") %>% as.numeric()
difSEX<- difftime(fechahoy, fechaSEX, units="days") %>% as.numeric()
difBLIN<- difftime(fechahoy, fechaBLIN, units="days") %>% as.numeric()

#Media diaria de visitas (con los días que han pasado desde fecha video a hoy)
mediaPAI<- numPAI / difPAI
mediaINDA<- numINDAPAN / difINDAPAN
mediaMERCA<- numMERCA / difMERCA
mediaFIEBRE<- numFIEBRE /difFIEBRE
mediaJACA <- numJACA /difJACA
mediaNICE<- numNICE /difNICE
mediaCAND <- numCAND/ difCAND
mediaINT<- numINT/difINT
mediaMAS<- numMAS/difMAS
mediaOPEN<- numOPEN/difOPEN
mediaIUAL<- numIUAL/difIUAL
mediaMARI<- numMARI/difMARI
mediaHOOKAH<- numHOOKAH/difHOOKAH
mediaZORR<- numZORR/difZORR
mediaBOM<- numBOM/difBOM
mediaSEX<- numSEX/difSEX
mediaBLIN<- numBLIN/difBLIN


#Creamos dataframes
fecha=seq(fechaPAI,length=difPAI,by="day")
df1<- data.frame(fecha,mediaPAI)

fecha=seq(fechaINDAPAN,length=difINDAPAN,by="day")
df2<- data.frame(fecha,mediaINDA)

fecha=seq(fechaMERCA,length=difMERCA,by="day")
df3<- data.frame(fecha,mediaMERCA)

fecha=seq(fechaFIEBRE,length=difFIEBRE,by="day")
df4<- data.frame(fecha,mediaFIEBRE)

fecha=seq(fechaJACA,length=difJACA,by="day")
df5<- data.frame(fecha,mediaJACA)

fecha=seq(fechaNICE,length=difNICE,by="day")
df6<- data.frame(fecha,mediaNICE)

fecha=seq(fechaCAND,length=difCAND,by="day")
df7<- data.frame(fecha,mediaCAND)

fecha=seq(fechaINT,length=difINT,by="day")
df8<- data.frame(fecha,mediaINT)

fecha=seq(fechaMAS,length=difMAS,by="day")
df9<- data.frame(fecha,mediaMAS)

fecha=seq(fechaOPEN,length=difOPEN,by="day")
df10<- data.frame(fecha,mediaOPEN)

fecha=seq(fechaIUAL,length=difIUAL,by="day")
df11<- data.frame(fecha,mediaIUAL)

fecha=seq(fechaMARI,length=difMARI,by="day")
df12<- data.frame(fecha,mediaMARI)

fecha=seq(fechaHOOKAH,length=difHOOKAH,by="day")
df13<- data.frame(fecha,mediaHOOKAH)

fecha=seq(fechaZORR,length=difZORR,by="day")
df14<- data.frame(fecha,mediaZORR)

fecha=seq(fechaBOM,length=difBOM,by="day")
df15<- data.frame(fecha,mediaBOM)

fecha=seq(fechaSEX,length=difSEX,by="day")
df16<- data.frame(fecha,mediaSEX)

fecha=seq(fechaBLIN,length=difBLIN,by="day")
df17<- data.frame(fecha,mediaBLIN)

#Unimos dataframes
df_visitas1<- full_join(df1,df2)
df_visitas2<- full_join(df_visitas1, df3)
df_v3<- full_join(df_visitas2,df4)
df_v4<- full_join(df_v3,df5)
df_v5<- full_join(df_v4,df6)
df_v6<- full_join(df_v5,df7)
df_v7<- full_join(df_v6,df8)
df_v8<- full_join(df_v7,df9)
df_v9<- full_join(df_v8,df10)
df_v10<- full_join(df_v9,df11)
df_v11<- full_join(df_v10,df12)
df_v12<- full_join(df_v11,df13)
df_v13<- full_join(df_v12,df14)
df_v14<- full_join(df_v13,df15)
df_v15<- full_join(df_v14,df16)
df_completo<- full_join(df_v15,df17)


#- Limpiamos el Global Env.
objetos_no_borrar <- c("bg_stats_ambos_canales1","df_completo","bg_mayor_1")
rm(list = ls()[!ls() %in% objetos_no_borrar])

Race bar chart

A partir del dataframe final que he creado (df_completo), he querido realizar un gráfico animado de carrera de barras basado en las visualizaciones diarias de cada título. De esta forma, el dataframe anterior, formado por la fecha en días secuencial desde 2016 a la actualidad y los títulos de los videos, tendrá que modificarse, de forma que, tendrá que pasarse a formato longer ,de manera que, las diferentes “medias” que aparecen estarán todas ahora en una columna llamada título y los valores de estas estarán en una columna llamada visitas. Además, mediante un drop_na() eliminaremos todas aquellas filas que no tuviesen datos, de forma que, si para una fecha determinada solo existía un título, ya no nos aparecerán los demás títulos con NA.

A continuación, agruparemos por título y crearemos una nueva variable, que es la suma acumulada de visitas, es decir, si a 1/X/20 se crea el video y su media de visualizaciones diarias es de 2000, la suma acumulada en ese momento será de 2000. Al día siguiente, 2/X/20, su media visualizaciones será la misma (2000), pero, su suma acumulada será de 4000, y así sucesivamente.

Por otro lado, agruparemos por fecha y a partir de esta crearemos una nueva variable ranking mediante la función dense_rank() , siendo la variable por la cual queremos que haga un ranking: la suma acumulada anterior. De esta forma, si por ejemplo a 25/X/20 hay 2 videos, y uno tiene mayor acumulada que el otro, en la columna ranking del primero de ellos aparecerá como 1 y el segundo con mayor acumulada como 2.

Así, mediante este dataframe ya modificado, podremos crear un gráfico animado de carrera de barras, que nos muestra la evolución de las visita diarias (conseguida a través de la media, como hemos visto en el punto anterior) de los títulos desde su fecha de creación hasta la actualidad.

La dificultad de este gráfico, aunque está inspirado en uno ya existente, ha sido la creación desde cero de un dataframe complejo, cuyos datos principales he tenido que calcularlos a partir de los pocos que ya tenía gracias al paquete “tuber”.


#GRAFICO PLOT

df_completo1<-df_completo %>% rename(Pai=mediaPAI, Indapanden=mediaINDA, Mercadona=mediaMERCA, Fiebre=mediaFIEBRE, Jacaranda=mediaJACA, `Nicest Cocky`=mediaNICE, Candela=mediaCAND, Internationally=mediaINT, `Más Raro`=mediaMAS, `Open The Door`=mediaOPEN, `Yo Sigo Iual`=mediaIUAL, `Santa María`=mediaMARI, Hookah=mediaHOOKAH, Zorra=mediaZORR, `Bom Bom`=mediaBOM, `Aprendiendo El Sexo`=mediaSEX, `Blin Blin`=mediaBLIN )

#modificamos
df_plot <- df_completo1 %>% pivot_longer(cols=2:18, names_to="titulo",values_to="visitas") %>% drop_na()
df_plot2 <- df_plot %>% group_by(titulo)%>% mutate(acumulada=cumsum(visitas))
df_plot3 <- df_plot2 %>% group_by(fecha) %>% mutate(ranking= dense_rank(desc(acumulada)))

#PLOT
animacion <- df_plot3 %>%
  ggplot() +
  geom_col(aes(ranking, acumulada, fill = titulo)) +
  geom_text(aes(ranking, acumulada, label = acumulada), hjust=-0.1) +
  geom_text(aes(ranking, y=0 , label = titulo), hjust=1.1) +
  geom_text(aes(x=15, y=max(acumulada) , label = as.factor(fecha)), vjust = 0.2, alpha = 0.5,  col = "gray", size = 20) +
  coord_flip(clip = "off", expand = FALSE) + scale_x_reverse() +
  theme_minimal() + theme(
    panel.grid = element_blank(),
    legend.position = "none",
    axis.ticks.y = element_blank(),
    axis.title.y = element_blank(),
    axis.text.y = element_blank(),
    plot.margin = margin(1, 4, 1, 3, "cm")
  ) +
  transition_states(fecha, state_length = 0, transition_length = 2) +
  enter_fade() +
  exit_fade() +
  ease_aes('quadratic-in-out')
Visualizaciones por día de cada título

Visualizaciones por día de cada título

4. Obteniendo datos sobre los comentarios

En este apartado, nos vamos a basar en la obtención de datos provenientes de los comentarios de los videos, usando principalmente la función get_all_comments() del paquete “tuber”.

Nube de comentarios

Aquí lo que queremos mostrar son los comentarios que más repite la gente acerca de uno de uno de los últimos videos de Bad Gyal: “Aprendiendo el sexo”. Para ello, nos descargaremos todos los comentarios, y mediante la función stri_extract_all_words() del paquete stringi extraeremos todas las palabras que aparecen en el dataframe de comentarios descargados. Por otro lado, usaré el paquete tm, el cual nos proporciona una serie de palabras comunes que luego utilizaré para poder eliminar y que no salgan en la wordcloud.

A partir de estas palabras extraidas, creamos un nuevo dataframe y en este haremos un group de las palabras y una vez agrupadas crearemos una nueva variable con la función count() para ver cuantas veces aparecen. Así, pues, mediante las palabras comunes que queremos quitar, proporcionadas por el paquete tm, haremos un filtrado para poder eliminarlas de nuestro dataframe y además las ordenaremos de más frecuentes a menos, para así, finalmente, crear la nube de palabras.

# WORDCLOUD
comentarios<- get_all_comments(video_id= "MBm0iCu_FBI")

comentarios_modif<- comentarios %>% select(textOriginal)

library(tidytext)
library(wordcloud2)
library(stringi)
library(tm)

comentarios_modif_2<- comentarios_modif %>% mutate(texto= stri_trans_general(tolower(textOriginal),"Latin-ASCII")) %>% select(texto)

remove = stopwords("spanish")

Palabras<- c(stri_extract_all_words(comentarios_modif_2$texto))
df<-data.frame(Palabras = unlist(Palabras))

df_palabras<- df %>% group_by(Palabras) %>% count() %>% arrange(desc(n)) %>% filter(nchar(Palabras) >=3) %>% filter(n>10 & Palabras %in% remove==FALSE)

#wordcloud2(df_palabras, color="random-light", shape="circle")

#- Para finalizar, limpiamos el Global Env.
objetos_no_borrar <- c("df_palabras" , "comentarios")
rm(list = ls()[!ls() %in% objetos_no_borrar])

Gráficos de línea: vídeos de 2020

En este apartado lo que he querido hacer es mostrar la evolución del número de comentarios por fecha de las 3 canciones que ha sacado Bad Gyal durante el 2020: Bom Bom (remix), Aprendiendo El Sexo y Blin Blin. Para ello, he descargado los comentarios de los 3 videos (aunque para el de AP he utilizado el dataframe del subpunto anterior) y a partir de los datos descargados he pasado la fecha al formato estándar de fecha con un as.Date() y agrupado por esta variable(fecha) para conseguir los comentarios publicados cada día.

A partir de estas modificaciones realizadas en los dataframe, los he juntado y he hecho un gráfico de línea con “facetting”, para mostrar los 3 títulos por separado. A su vez, también he hecho otro gráfico para mostrar el número de comentarios totales en cada título.


#aprendiendo el sexo
comentarios_sex<- comentarios %>% mutate(fecha = as.Date(publishedAt)) %>% select(textOriginal, fecha)

comentarios_sex2<- comentarios_sex %>% group_by(fecha) %>% count() %>% arrange(desc(n)) %>% rename(`número de comentarios`=n)

#blin

comentariosblin<- get_all_comments(video_id = "cFX4WR3g-kA")

comentarios_blin2<- comentariosblin %>% mutate(fecha = as.Date(publishedAt)) %>% select(textOriginal, fecha)

comentarios_blin3<- comentarios_blin2 %>% group_by(fecha) %>% count() %>% arrange(desc(n)) %>% rename(`número de comentarios`=n)


#bom bom

comentariosbom<- get_all_comments(video_id = "qOkURCVtlwU")

comentarios_statsbom<- comentariosbom %>% mutate(fecha = as.Date(publishedAt)) %>% select(textOriginal, fecha)

comentarios_statsbom2<- comentarios_statsbom %>% group_by(fecha) %>% count() %>% arrange(desc(n)) %>% rename(`número de comentarios`=n)


#Añadimos la variable título
comentarios_stats_asex<- comentarios_sex2 %>% mutate(titulo="Aprendiendo El Sexo")
comentarios_stats_blin<- comentarios_blin3 %>% mutate(titulo="Blin Blin")
comentarios_stats_bom<- comentarios_statsbom2 %>% mutate(titulo="Tu Eres Un Bom Bom (remix)")

#Los juntamos
comentarios_juntos<- full_join(comentarios_stats_blin, comentarios_stats_asex)
comentarios_juntos_full<- full_join(comentarios_juntos, comentarios_stats_bom)

#Modificamos el dataframe de comentarios_juntos_full para obtener el número total de comentarios por título
comentarios_juntos_full_modif<- comentarios_juntos_full%>% group_by(titulo)%>%summarise(`Número total de comentarios` = sum(`número de comentarios`))

#- Limpiamos el Global Env.
objetos_no_borrar <- c("comentarios_juntos_full_modif", "comentarios_juntos_full")
rm(list = ls()[!ls() %in% objetos_no_borrar])

#Plots
plot_total_coment<- ggplot(comentarios_juntos_full_modif, aes(x=titulo, y=`Número total de comentarios`, fill=titulo)) + geom_bar(stat="identity") + coord_flip() + theme(legend.position="none") + theme_bw()

plot_lineas<- ggplot(comentarios_juntos_full, aes(x=fecha, y=`número de comentarios`,color=titulo))+geom_line()+ facet_wrap(vars(titulo), nrow=2, ncol=2) + theme(legend.position = "none") + theme_bw()

Grafico líneas

Gráfico comentarios

5. Datos sobre popularidad: mapa

Mediante el paquete gtrendsR he querido mostrar la popularidad-tendencia en el buscador de Google de Bad Gyal. Para este apartado me he basado en el RScript de la sesión 11: ej_gtrends_NV. El paquete gtrendsR mediante la función gtrends() te devuelve una lista con diferentes resultados, dependiendo de las variables que metas dentro de la función (estas son: la palabra o palabras que quieres que busque y analize de Google, la zona o país a analizar, la franja temporal…).

Mediante el dataframe obtenido, nos quedaremos con las variables referentes a las CCAA en las que destaca la busqueda de Bad Gyal y así, con estas y las geometrías mostraremos en un mapa con color, las comunidades autónomas donde Bad Gyal levanta más interés (donde, valor 100 representa el máximo interés-hits.).

library(gtrendsR)
library(sf)
library(ggrepel)
library(ggspatial)

#Seleccionamos los que queremos que nos busque y los términos.
search_terms <- c("Bad Gyal")
output_results <- gtrends(keyword = search_terms, geo = c("ES"), time = "today 12-m") 

#- De la lista que nos devuelve el output_results seleccionamos el slot nº3: interés por región.
BG_interes <- output_results[[3]]

#Modificamos nuestro dataframe para que coincida con los nombres de las geometrías que vamos a cargar
BG_interes_2<- BG_interes %>% select(location, hits) %>% pivot_wider(names_from= location, values_from=hits)

BG_interes_3 <- BG_interes_2 %>% rename("Cataluña" = "Catalonia", "Comunidad Foral de Navarra"="Navarre", "Castilla y León"="Castile and León", "País Vasco"="Basque Country", "Comunidad Valenciana"="Valencian Community", "Comunidad de Madrid"="Community of Madrid", "Principado de Asturias"="Asturias", "Castilla-La Mancha"="Castile-La Mancha", "Andalucía"="Andalusia", "Aragón"="Aragon", "Illes Balears"="Balearic Islands", "Región de Murcia"="Region of Murcia","Canarias"="Canary Islands")

BG_interes_3<- BG_interes_3 %>% pivot_longer(1:19, names_to="CCAA", values_to="hits") 

BG_interes_3<- BG_interes_3 %>% drop_na()



# CCAA
#Cargamos
load("./datos/geometria_ccaa.RData")

#Arreglamos el dataframe
CCAA_2<- CCAA %>% slice(c(1:17))
CCAA_2 <- CCAA_2 %>% select(NombreCCAA, geometry)
rmapshaper::ms_simplify(CCAA_2$geometry)
#> Geometry set for 17 features 
#> geometry type:  MULTIPOLYGON
#> dimension:      XY
#> bbox:           xmin: -18.16073 ymin: 27.63773 xmax: 4.327739 ymax: 43.78987
#> geographic CRS: ETRS89
#> First 5 geometries:

df_stats_mapa<- full_join(CCAA_2, BG_interes_3, by=c("NombreCCAA" = "CCAA"))


#Simplificamos las geometrías para que el tiempo de carga sea menor
df_stats_mapa2<- df_stats_mapa %>% rmapshaper::ms_simplify()


#Plot
MAPA1<- ggplot(df_stats_mapa2, aes(geometry = geometry)) + geom_sf(aes(fill= hits))  + scale_fill_viridis_c(option = "inferno", trans = "sqrt", direction = -1, alpha= .6) + coord_sf(xlim = c(-20,10)) + labs (title = "Mapa 1: Distribución de busquedas de Bad Gyal",
       subtitle = "Por CCAA",
       caption = "Datos provenientes del paquete gtrendsR") + theme_minimal()

6. Trabajos en los que me he basado

Los trabajos en los que me he ido inspirando para hacer el mío han sido varios:


7. Bibliografía

A parte de los ya citados en los trabajos en los que me he inspirado, he ido sacando información e ideas en diferentes páginas:


¡Espero que hayais aprendido un poquito más!



Chunk para incluir la session info:

sessioninfo::session_info() %>% details::details(summary = 'current session info') 

current session info


─ Session info ───────────────────────────────────────────────────────────────
 setting  value                       
 version  R version 4.0.2 (2020-06-22)
 os       macOS Catalina 10.15.7      
 system   x86_64, darwin17.0          
 ui       X11                         
 language (EN)                        
 collate  es_ES.UTF-8                 
 ctype    es_ES.UTF-8                 
 tz       Europe/Madrid               
 date     2021-01-15                  

─ Packages ───────────────────────────────────────────────────────────────────
 package      * version    date       lib source                        
 assertthat     0.2.1      2019-03-21 [1] CRAN (R 4.0.2)                
 backports      1.1.10     2020-09-15 [1] CRAN (R 4.0.2)                
 base64enc      0.1-3      2015-07-28 [1] CRAN (R 4.0.2)                
 blob           1.2.1      2020-01-20 [1] CRAN (R 4.0.2)                
 broom          0.7.0      2020-07-09 [1] CRAN (R 4.0.2)                
 cellranger     1.1.0      2016-07-27 [1] CRAN (R 4.0.2)                
 checkmate      2.0.0      2020-02-06 [1] CRAN (R 4.0.2)                
 class          7.3-17     2020-04-26 [1] CRAN (R 4.0.2)                
 classInt       0.4-3      2020-04-07 [1] CRAN (R 4.0.2)                
 cli            2.0.2      2020-02-28 [1] CRAN (R 4.0.2)                
 colorspace     1.4-1      2019-03-18 [1] CRAN (R 4.0.2)                
 commonmark     1.7        2018-12-01 [1] CRAN (R 4.0.2)                
 crayon         1.3.4      2017-09-16 [1] CRAN (R 4.0.2)                
 crosstalk      1.1.0.1    2020-03-13 [1] CRAN (R 4.0.2)                
 crul           1.0.0      2020-07-30 [1] CRAN (R 4.0.2)                
 curl           4.3        2019-12-02 [1] CRAN (R 4.0.1)                
 data.table   * 1.13.0     2020-07-24 [1] CRAN (R 4.0.2)                
 DBI            1.1.0      2019-12-15 [1] CRAN (R 4.0.2)                
 dbplyr         1.4.4      2020-05-27 [1] CRAN (R 4.0.2)                
 digest         0.6.25     2020-02-23 [1] CRAN (R 4.0.2)                
 dplyr        * 1.0.2      2020-08-18 [1] CRAN (R 4.0.2)                
 e1071          1.7-3      2019-11-26 [1] CRAN (R 4.0.2)                
 ellipsis       0.3.1      2020-05-15 [1] CRAN (R 4.0.2)                
 evaluate       0.14       2019-05-28 [1] CRAN (R 4.0.1)                
 fansi          0.4.1      2020-01-08 [1] CRAN (R 4.0.2)                
 farver         2.0.3      2020-01-16 [1] CRAN (R 4.0.2)                
 forcats      * 0.5.0      2020-03-01 [1] CRAN (R 4.0.2)                
 foreign        0.8-80     2020-05-24 [1] CRAN (R 4.0.2)                
 fs             1.5.0      2020-07-31 [1] CRAN (R 4.0.2)                
 generics       0.1.0      2020-10-31 [1] CRAN (R 4.0.2)                
 geojson        0.3.4      2020-06-23 [1] CRAN (R 4.0.2)                
 geojsonio      0.9.4      2021-01-13 [1] CRAN (R 4.0.2)                
 geojsonlint    0.4.0      2020-02-13 [1] CRAN (R 4.0.2)                
 geojsonsf      2.0.1      2020-10-02 [1] CRAN (R 4.0.2)                
 gganimate    * 1.0.7      2020-10-15 [1] CRAN (R 4.0.2)                
 ggplot2      * 3.3.2      2020-06-19 [1] CRAN (R 4.0.2)                
 ggrepel      * 0.8.2      2020-03-08 [1] CRAN (R 4.0.2)                
 ggspatial    * 1.1.4      2020-07-12 [1] CRAN (R 4.0.2)                
 gifski         0.8.6      2018-09-28 [1] CRAN (R 4.0.2)                
 glue           1.4.2      2020-08-27 [1] CRAN (R 4.0.2)                
 gt           * 0.2.2      2020-08-05 [1] CRAN (R 4.0.2)                
 gtable         0.3.0      2019-03-25 [1] CRAN (R 4.0.2)                
 gtrendsR     * 1.4.7      2020-09-12 [1] CRAN (R 4.0.2)                
 haven          2.3.1      2020-06-01 [1] CRAN (R 4.0.2)                
 highr          0.8        2019-03-20 [1] CRAN (R 4.0.2)                
 hms            0.5.3      2020-01-08 [1] CRAN (R 4.0.2)                
 htmltools      0.5.0      2020-06-16 [1] CRAN (R 4.0.2)                
 htmlwidgets    1.5.1      2019-10-08 [1] CRAN (R 4.0.2)                
 httpcode       0.3.0      2020-04-10 [1] CRAN (R 4.0.2)                
 httr           1.4.2      2020-07-20 [1] CRAN (R 4.0.2)                
 janeaustenr    0.1.5      2017-06-10 [1] CRAN (R 4.0.2)                
 jqr            1.2.0      2020-11-13 [1] CRAN (R 4.0.2)                
 jsonlite       1.7.1      2020-09-07 [1] CRAN (R 4.0.2)                
 jsonvalidate   1.1.0      2019-06-25 [1] CRAN (R 4.0.2)                
 KernSmooth     2.23-17    2020-04-26 [1] CRAN (R 4.0.2)                
 klippy       * 0.0.0.9500 2020-11-17 [1] Github (rlesur/klippy@378c247)
 knitr        * 1.29       2020-06-23 [1] CRAN (R 4.0.2)                
 labeling       0.3        2014-08-23 [1] CRAN (R 4.0.2)                
 lattice        0.20-41    2020-04-02 [1] CRAN (R 4.0.2)                
 lazyeval       0.2.2      2019-03-15 [1] CRAN (R 4.0.2)                
 lifecycle      0.2.0      2020-03-06 [1] CRAN (R 4.0.2)                
 lubridate    * 1.7.9      2020-06-08 [1] CRAN (R 4.0.2)                
 magrittr       1.5        2014-11-22 [1] CRAN (R 4.0.2)                
 maptools       1.0-2      2020-08-24 [1] CRAN (R 4.0.2)                
 Matrix         1.2-18     2019-11-27 [1] CRAN (R 4.0.2)                
 modelr         0.1.8      2020-05-19 [1] CRAN (R 4.0.2)                
 munsell        0.5.0      2018-06-12 [1] CRAN (R 4.0.2)                
 NLP          * 0.2-1      2020-10-14 [1] CRAN (R 4.0.2)                
 pillar         1.4.6      2020-07-10 [1] CRAN (R 4.0.2)                
 pkgconfig      2.0.3      2019-09-22 [1] CRAN (R 4.0.2)                
 plotly       * 4.9.2.1    2020-04-04 [1] CRAN (R 4.0.2)                
 plyr           1.8.6      2020-03-03 [1] CRAN (R 4.0.2)                
 prettyunits    1.1.1      2020-01-24 [1] CRAN (R 4.0.2)                
 progress       1.2.2      2019-05-16 [1] CRAN (R 4.0.2)                
 purrr        * 0.3.4      2020-04-17 [1] CRAN (R 4.0.2)                
 R6             2.4.1      2019-11-12 [1] CRAN (R 4.0.2)                
 Rcpp           1.0.5      2020-07-06 [1] CRAN (R 4.0.2)                
 reactable    * 0.2.3      2020-10-04 [1] CRAN (R 4.0.2)                
 reactR         0.4.3      2020-07-12 [1] CRAN (R 4.0.2)                
 readr        * 1.3.1      2018-12-21 [1] CRAN (R 4.0.2)                
 readxl         1.3.1      2019-03-13 [1] CRAN (R 4.0.2)                
 reprex         0.3.0      2019-05-16 [1] CRAN (R 4.0.2)                
 rgeos          0.5-5      2020-09-07 [1] CRAN (R 4.0.2)                
 rlang          0.4.7      2020-07-09 [1] CRAN (R 4.0.2)                
 rmapshaper     0.4.4      2020-04-01 [1] CRAN (R 4.0.2)                
 rmarkdown      2.3        2020-06-18 [1] CRAN (R 4.0.2)                
 rstudioapi     0.11       2020-02-07 [1] CRAN (R 4.0.2)                
 rvest          0.3.6      2020-07-25 [1] CRAN (R 4.0.2)                
 sass           0.2.0      2020-03-18 [1] CRAN (R 4.0.2)                
 scales         1.1.1      2020-05-11 [1] CRAN (R 4.0.2)                
 sessioninfo    1.1.1      2018-11-05 [1] CRAN (R 4.0.2)                
 sf           * 0.9-6      2020-09-13 [1] CRAN (R 4.0.2)                
 slam           0.1-48     2020-12-03 [1] CRAN (R 4.0.2)                
 SnowballC      0.7.0      2020-04-01 [1] CRAN (R 4.0.2)                
 sp             1.4-2      2020-05-20 [1] CRAN (R 4.0.2)                
 stringi      * 1.5.3      2020-09-09 [1] CRAN (R 4.0.2)                
 stringr      * 1.4.0      2019-02-10 [1] CRAN (R 4.0.2)                
 tibble       * 3.0.3      2020-07-10 [1] CRAN (R 4.0.2)                
 tidyr        * 1.1.2      2020-08-27 [1] CRAN (R 4.0.2)                
 tidyselect     1.1.0      2020-05-11 [1] CRAN (R 4.0.2)                
 tidytext     * 0.3.0      2021-01-06 [1] CRAN (R 4.0.2)                
 tidyverse    * 1.3.0      2019-11-21 [1] CRAN (R 4.0.2)                
 tm           * 0.7-8      2020-11-18 [1] CRAN (R 4.0.2)                
 tokenizers     0.2.1      2018-03-29 [1] CRAN (R 4.0.2)                
 tuber        * 0.9.9      2020-06-11 [1] CRAN (R 4.0.2)                
 tweenr         1.0.1      2018-12-14 [1] CRAN (R 4.0.2)                
 units          0.6-7      2020-06-13 [1] CRAN (R 4.0.2)                
 V8             3.4.0      2020-11-04 [1] CRAN (R 4.0.2)                
 vctrs          0.3.4      2020-08-29 [1] CRAN (R 4.0.2)                
 viridisLite    0.3.0      2018-02-01 [1] CRAN (R 4.0.1)                
 withr          2.2.0      2020-04-20 [1] CRAN (R 4.0.2)                
 wordcloud2   * 0.2.1      2018-01-03 [1] CRAN (R 4.0.2)                
 xfun           0.17       2020-09-09 [1] CRAN (R 4.0.2)                
 xml2           1.3.2      2020-04-23 [1] CRAN (R 4.0.2)                
 yaml           2.2.1      2020-02-01 [1] CRAN (R 4.0.2)                

[1] /Library/Frameworks/R.framework/Versions/4.0/Resources/library



LS0tCnRpdGxlOiA8Rk9OVCBDT0xPUj0gIjg4ZGJkMyI+PGZvbnQgZmFjZT0gIkltcGFjdCI+PGNlbnRlcj5EYXRvcyB5IGVzdGFkw61zdGljYXMgZGUgWW91dHViZTsgQmFkIEd5YWw8L2ZvbnQ+PC9jZW50ZXI+PC9GT05UPgpzdWJ0aXRsZTogIkRhbmllbCBDYXNpbm8gQmFsbGVzdGVyb3MgKGRhY2FiYTRAYWx1bW5pLnV2LmVzKSIKYXV0aG9yOiAiVW5pdmVyc2l0YXQgZGUgVmFsw6huY2lhIgpkYXRlOiAiRGljaWVtYnJlIGRlIDIwMjAgKGFjdHVhbGl6YWRvIGVsIGByIGZvcm1hdChTeXMudGltZSgpLCAnJWQtJW0tJVknKWApIgpvdXRwdXQ6CiAgaHRtbF9kb2N1bWVudDoKICAgIHRoZW1lOiBmbGF0bHkKICAgIGhpZ2hsaWdodDogdGV4dG1hdGUgCiAgICB0b2M6IHRydWUKICAgIHRvY19kZXB0aDogMyAKICAgIHRvY19mbG9hdDogCiAgICAgIGNvbGxhcHNlZDogdHJ1ZQogICAgICBzbW9vdGhfc2Nyb2xsOiB0cnVlCiAgICBzZWxmX2NvbnRhaW5lZDogdHJ1ZQogICAgbnVtYmVyX3NlY3Rpb25zOiBmYWxzZQogICAgZGZfcHJpbnQ6IGthYmxlCiAgICBjb2RlX2Rvd25sb2FkOiB0cnVlCiAgICBjb2RlX2ZvbGRpbmc6IHNob3cKZWRpdG9yX29wdGlvbnM6IAogIGNodW5rX291dHB1dF90eXBlOiBjb25zb2xlCi0tLQoKYGBge3IgcGFja2FnZXMtc2V0dXAsIGluY2x1ZGUgPSBGQUxTRX0KbGlicmFyeSh0aWR5dmVyc2UpCmxpYnJhcnkoa2xpcHB5KSAgIy0gcmVtb3Rlczo6aW5zdGFsbF9naXRodWIoInJsZXN1ci9rbGlwcHkiKQpsaWJyYXJ5KGtuaXRyKQpsaWJyYXJ5KHRpZHl2ZXJzZSkKbGlicmFyeShsdWJyaWRhdGUpCmxpYnJhcnkocmVhY3RhYmxlKQpsaWJyYXJ5KGdncGxvdDIpCmxpYnJhcnkoZ2dhbmltYXRlKQpsaWJyYXJ5KGRhdGEudGFibGUpCmxpYnJhcnkoZ3QpCmxpYnJhcnkocGxvdGx5KQojIGRldnRvb2xzOjppbnN0YWxsX2dpdGh1YigiaGFkbGV5L2VtbyIpCgpgYGAKCmBgYHtyIGNodW5rLXNldHVwLCBpbmNsdWRlID0gRkFMU0V9CmtuaXRyOjpvcHRzX2NodW5rJHNldChlY2hvID0gVFJVRSwgZXZhbCA9IFRSVUUsIG1lc3NhZ2UgPSBGQUxTRSwgd2FybmluZyA9IEZBTFNFLCAKICAgICAgICAgICAgICAgICAgICAgICNyZXN1bHRzID0gImhvbGQiLAogICAgICAgICAgICAgICAgICAgICAgY2FjaGUgPSBGQUxTRSwgY2FjaGUucGF0aCA9ICIvY2FjaGVzLyIsIGNvbW1lbnQgPSAiIz4iLAogICAgICAgICAgICAgICAgICAgICAgI2ZpZy53aWR0aCA9IDcsICNmaWcuaGVpZ2h0PSA3LCAgIAogICAgICAgICAgICAgICAgICAgICAgI291dC53aWR0aCA9IDcsIG91dC5oZWlnaHQgPSA3LAogICAgICAgICAgICAgICAgICAgICAgY29sbGFwc2UgPSBUUlVFLCAgZmlnLnNob3cgPSAiaG9sZCIsCiAgICAgICAgICAgICAgICAgICAgICBmaWcuYXNwID0gNy85LCBvdXQud2lkdGggPSAiNjAlIiwgZmlnLmFsaWduID0gImNlbnRlciIpCmtuaXRyOjpvcHRzX2NodW5rJHNldChkZXYgPSAicG5nIiwgZGV2LmFyZ3MgPSBsaXN0KHR5cGUgPSAiY2Fpcm8tcG5nIikpCmBgYAoKYGBge3Igb3B0aW9ucy1zZXR1cCwgaW5jbHVkZSA9IEZBTFNFfQpvcHRpb25zKHNjaXBlbiA9IDk5OSkgIy0gcGFyYSBxdWl0YXIgbGEgbm90YWNpw7NuIGNpZW50w61maWNhCm9wdGlvbnMoInlhbWwuZXZhbC5leHByIiA9IFRSVUUpIApgYGAKCgpgYGB7ciBrbGlwcHksIGVjaG8gPSBGQUxTRX0Ka2xpcHB5OjprbGlwcHkocG9zaXRpb24gPSBjKCJ0b3AiLCAicmlnaHQiKSkgIy0gcmVtb3Rlczo6aW5zdGFsbF9naXRodWIoInJsZXN1ci9rbGlwcHkiKQpgYGAKCmBgYCB7Y3NzLCBlY2hvPUZBTFNFfQpib2R5IHsKICBiYWNrZ3JvdW5kLWltYWdlOiB1cmwoJy4vaW1hZ2VuZXMvaGVhZHBob25lcy5qcGcnKTsKICBiYWNrZ3JvdW5kLXJlcGVhdDogcmVwZWF0OwogIGJhY2tncm91bmQtc2l6ZTogMTAwJTsKfQoKZGl2IHsKICBiYWNrZ3JvdW5kLWNvbG9yOiByZ2JhKDI1NSwgMjU1LCAyNTUsIDAuNTUpICAgLyogNTUlIG9wYXF1ZSB3aGl0ZSAqLzsKICBwYWRkaW5nOiAwLjEwZW07Cn0KCmBgYAoKCgo8ZGl2IHN0eWxlPSJ0ZXh0LWFsaWduOiBqdXN0aWZ5Ij48ZGl2Lz4KCjxociBjbGFzcz0ibGluZWEtYmxhY2siPgoKVHJhYmFqbyBlbGFib3JhZG8gcGFyYSBsYSBhc2lnbmF0dXJhICJQcm9ncmFtYWNpw7NuIHkgbWFuZWpvIGRlIGRhdG9zIGVuIGxhIGVyYSBkZWwgQmlnIERhdGEiIGRlIGxhIFVuaXZlcnNpdGF0IGRlIFZhbMOobmNpYSBkdXJhbnRlIGVsIGN1cnNvIDIwMjAtMjAyMS4gRWwgcmVwbyBkZWwgdHJhYmFqbyBlc3TDoSBbYXF1w61dKGh0dHBzOi8vZ2l0aHViLmNvbS9kYXNkZmcwOTgvdHJhYmFqb19CaWdEYXRhKXt0YXJnZXQ9Il9ibGFuayJ9LiBMYSBww6FnaW5hIHdlYiBkZSBsYSBhc2lnbmF0dXJhIHkgbG9zIHRyYWJham9zIGRlIG1pcyBjb21wYcOxZXJvcyBwdWVkZW4gdmVyc2UgW2FxdcOtXShodHRwczovL3BlcmV6cDQ0LmdpdGh1Yi5pby9pbnRyby1kcy0yMC0yMS13ZWIvMDctdHJhYmFqb3MuaHRtbCl7dGFyZ2V0PSJfYmxhbmsifS4KCi0tLS0tLS0tLS0tLQoKIyA8Rk9OVCBDT0xPUj0gImViODhjZCI+KioxLiBJbnRyb2R1Y2Npw7NuKio8L0ZPTlQ+CkVsIHRyYWJham8gY29uc2lzdGUgZW4gbGEgb2J0ZW5jacOzbiB5IG1hbmlwdWxhY2nDs24gZGUgZGF0b3Mgb2J0ZW5pZG9zIGRlIHVuYSBkZSBsYXMgcHJpbmNpcGFsZXMgZnVlbnRlcyBkZSBhbG9qYW1pZW50byBkZSB2aWRlb3M6ICoqWW91dHViZSoqLCBhc8OtIGNvbW8sIGxhIGNyZWFjacOzbiBkZSBkaWZlcmVudGVzIGdyw6FmaWNvcyB5IHRhYmxhcyBpbHVzdHJhdGl2YXMgYSBwYXJ0aXIgZGUgbG9zIGRhdG9zIHF1ZSBvYnRlbmdhbW9zIGRlIGVzdGEuIExvcyBkYXRvcyBhIG1hbmlwdWxhciBubyBzb24gZGF0b3MgY3VhbGVzcXVpZXJhOyBubyBzb24gZGF0b3MgZGUgZXN0YWTDrXN0aWNhcyBnZW5lcmFsZXMsIHNpIG5vLCBkYXRvcyBkZSB1biBjYW5hbCBjb25jcmV0bywgZWwgY2FuYWwgZGUgbcO6c2ljYSBkZSB1bmEgYXJ0aXN0YSBxdWUgbWUgZ3VzdGEgbXVjaG86ICoqQmFkIEd5YWwqKi4gUXVlcsOtYSBoYWNlciB1biB0cmFiYWpvIG3DoXMgYW1lbm8sIHNvYnJlIGFsZ28gY29uIGxvIHF1ZSBlc3R1dmllcmEgY8OzbW9kbyB0cmFiYWphbmRvLCBwdWVzLCBlcyB1biB0cmFiYWpvIGRlIGJhc3RhbnRlcyBob3Jhcy1kw61hcyB5IG1lam9yIHNpIHJlc3VsdGEgZW50cmV0ZW5pZG8uIEVzIHBvciBlc3RvIGxhIGVsZWNjacOzbiBkZWwgdGVtYS4gCgojIyA8Rk9OVCBDT0xPUj0iZWI4OGNkIj4qKsK/UXVpw6luIGVzOioqPC9GT05UPiAhW10oaW1hZ2VuZXMvbG9nb2JhZGd5YWwucG5nKXt3aWR0aD0iMTUlIn08Rk9OVCBDT0xPUj0iZWI4OGNkIj4qKj8qKjwvRk9OVD4KQWxiYSBGYXJlbG8sIG3DoXMgY29ub2NpZGEgY29tbyAqKkJhZCBHeWFsKiogZXMgdW5hIGFydGlzdGEgZGUgbGEgZXNjZW5hIG11c2ljYWwgdXJiYW5hIGVzcGHDsW9sYSwgbmFjaWRhIGVuICpWaWxhc3NhciBkZSBNYXIqIChDYXRhbHVueWEpIGVuIDE5OTcuCgpBbGJhIGNvbWVuemFyw61hIHN1IGFuZGFkdXJhIG11c2ljYWwgZW4gbGEgZXNjZW5hIHVyYmFuYSBkZSBudWVzdHJvIHBhw61zIGVuIDIwMTYsIGVuIHVuIG1vbWVudG8gZG9uZGUgZWwgZ8OpbmVybyAidHJhcCIgZXN0YWJhIGFsIGFsemEgeSBsb3MgcG9jb3MgYXJ0aXN0YXMgcXVlIGV4cGxvdGFiYW4gZXN0ZSBnw6luZXJvIGVzdGFiYW4gY29zZWNoYW5kbyBncmFuZGVzIMOpeGl0b3MuCkxhIHBvcHVsYXJpZGFkIGRlIGxhIGFydGlzdGEgY29tZW56w7MgYSBhdW1lbnRhciB0cmFzIHN1YmlyIGEgKllvdXR1YmUqIHVuYSBjb3ZlciBjYXNlcmEgZW4gY2F0YWzDoW4gZGVsIG3DrXRpY28gIldvcmsiIGRlIFJpaGFubmEuIFRyYXMgZXN0ZSBwcmltZXIgdGVtYSBxdWUgbGEgY29sb2NhcsOtYSBkZSBsbGVubyBkZW50cm8gZGUgbGEgZXNjZW5hLCBsZSBzdWNlZGVyw61hbiBvdHJvcyBjb21vIHRlbWFzICJJbmRhcGFuZGVuIiwgIkxlaXJpc3MiIG8gIk5vIHBpZXJkbyBOYWRhIiwgcGVybywgbm8gc2Vyw61hIGhhc3RhIGxhIHB1YmxpY2FjacOzbiBkZSBzdSBwcmltZXJhIG1peHRhcGUgKioiU2xvdyBXaW5lIioqIGVsIDkgZGUgbm92aWVtYnJlIGRlIDIwMTYgZGUgbGEgbWFubyBkZSAqKkZha2UgR3VpZG8qKiBjdWFuZG8gY29tZW56YXLDrWEgc3UgdmVyZGFkZXJhIGFuZGFkdXJhIHBvciBsb3Mgw6l4aXRvcyBkZSBudWVzdHJvIHBhw61zLgpFbCBzZW5jaWxsbyAqKkZpZWJyZSoqIGRlbnRybyBkZSBsYSBwcmltZXJhIG1peHRhcGUgZGUgQWxiYSBjb3NlY2hhcsOtYSB1biBncmFuIMOpeGl0bywgc2llbmRvIGVzdGEgY2FuY2nDs24gdW5vIGRlIGxvcyB0ZW3DoXMgbcOhcyByZWNvbm9jaWRvcyBhIGTDrWEgZGUgaG95IGRlIGxhIGFydGlzdGEuCkEgcGFydGlyIGRlIGVzdGUgbW9tZW50bywgbGUgc3VjZWRlcsOtYW4gZ3JhbmRlcyBwcm95ZWN0b3MgY29tbyBzdSBzZWd1bmRhIG1peHRhcGUgKioiV29ybGR3aWRlIEFuZ2VsIioqIGVuIDIwMTggeSAgZ2lyYXMgcG9yIGZlc3RpdmFsZXMgeSBzYWxhcyBkZSBFc3Bhw7FhIHkgZGUgZWwgcmVzdG8gZGVsIG11bmRvIChpbmNsdXllbmRvIGNpdWRhZGVzIGNvbW8gVG9raW8sIEhvbmcgS29uZywgRUVVVSwgTcOpeGljby4uLikuIAoKRW4gMjAxOSBmaXJtYXLDrWEgY29uIGVsIHNlbGxvICoqSW50ZXJzY29wZSBSZWNvcmRzKiosIHNpZW5kbyBlc3RvIHVuIHB1bnRvIGRlIGluZmxleGnDs24gcGFyYSBBbGJhLCBwdWVzLCBlbCDDqXhpdG8gcXVlIGhhYsOtYSBjb3NlY2hhZG8gaGFzdGEgZWwgbW9tZW50byBjb21lbnphcsOtYSBhIG11bHRpcGxpY2Fyc2UsIGhhc3RhIGVsIHB1bnRvIGRlLCBvYnRlbmVyIHN1IHByaW1lciBEaXNjbyBkZSBPcm8gY29uIHN1ICpzaW5nbGUqICoqU2FudGEgTWFyw61hKiouCgpTdSBtw7pzaWNhIGhhIHNpZG8gY2F0YWxvZ2FkYSBjb21vIHVuYSBmdXNpw7NuIGVudHJlICpyZWdnYWV0b24qLCAqZGFuY2VoYWxsKiB5IHJpdG1vcyB1cmJhbm9zIGNvbW8gKnRyYXAqLiBEdXJhbnRlIHN1IGNhcnJlcmEsIGhhIHJlY2liaWRvIGNvbnN0YW50ZXMgY3LDrXRpY2FzIGRlIGNpZXJ0b3Mgc2VjdG9yZXMgZGUgcMO6YmxpY28gcG9yIGVsIHVzbyBkZSAqYXV0by10dW5lKiBlbiBzdXMgY29uY2llcnRvcyBvIGNhbmNpb25lcy4gQWxiYSBoYSBzYWJpZG8gY29tbyBhcHJvdmVjaGFyIGVzdGEgaGVycmFtaWVudGEgZGUgdm96LCBjb252ZXJ0aXJsYSBlbiB1bm8gZGUgc3VzIHNlbGxvcyBkZSBpZGVudGlkYWQuCgoqKkJhZCBHeWFsKiogYSBkw61hIGRlIGhveSBlbiBudWVzdHJvIHBhw61zIGVzdMOhIGNvc2VjaGFuZG8gZ3JhbmRlcyDDqXhpdG9zIHkgZXMgcXVlLCBwYXJhIGVsIHDDumJsaWNvIGpvdmVuIGNvbiBndXN0b3MgbXVzaWNhbGVzIHF1ZSB0aWVuZGVuIGEgbGEgbcO6c2ljYSB1cmJhbmEsIGxhcyBjYW5jaW9uZXMgZGUgQWxiYSwgc29uIGNhbmNpb25lcyBwZWdhZGl6YXMgeSBxdWUgYXRyYWVuLCB5YSBzZWEgcG9yIGVsIHJpdG1vIHF1ZSBlbWFuYW4gbyBwb3IgZWwgY2Fyw6FjdGVyIGV4cGzDrWNpdG8geSAicGVnYWRpem8iIGRlIHN1cyBsZXRyYXMuCgo8aWZyYW1lIHdpZHRoPSI1NjAiIGhlaWdodD0iMzE1IiBzcmM9Imh0dHBzOi8vd3d3LnlvdXR1YmUuY29tL2VtYmVkL0k2ZElYMTVSamV3IiBmcmFtZWJvcmRlcj0iMCIgYWxsb3c9ImFjY2VsZXJvbWV0ZXI7IGF1dG9wbGF5OyBjbGlwYm9hcmQtd3JpdGU7IGVuY3J5cHRlZC1tZWRpYTsgZ3lyb3Njb3BlOyBwaWN0dXJlLWluLXBpY3R1cmUiIGFsbG93ZnVsbHNjcmVlbj48L2lmcmFtZT4KCgojIDxGT05UIENPTE9SPSJlYjg4Y2QiPioqMi4gUHJvY2VkZW5jaWEgeSBleHBsaWNhY2nDs24qKjwvRk9OVD4KRW4gZXN0ZSBhcGFydGFkbyB5IGxvcyBxdWUgdmVuZ2FuIGEgY29udGludWFjacOzbiB2b3kgYSBjZW50cmFybWUgZW4gZXhwbGljYXIgbGEgcHJvY2VkZW5jaWEgZGUgbG9zIGRhdG9zLCBsb3MgcGFxdWV0ZXMgcXVlIGhlIGlkbyB1dGlsaXphbmRvLCBhc8OtIGNvbW8gbGEgbGltcGllemEgZGUgbGFzIHZhcmlhYmxlcyBubyB0YW4gbmVjZXNhcmlhcyB5IGxvcyBwcm9ibGVtYXMgcXVlIG1lIGhhbiBpZG8gc3VyZ2llbmRvIGEgbG8gbGFyZ28gZGVsIHRyYWJham8uCgpMb3MgZGF0b3MgcHJvY2VkZW4gbWF5b3JpdGFyaWFtZW50ZSBkZSBZb3V0dWJlLCB0YW50byBkZWwgW2NhbmFsIHByaW5jaXBhbCBkZSBCYWQgR3lhbF0oaHR0cHM6Ly93d3cueW91dHViZS5jb20vY2hhbm5lbC9VQzJ5cEJhWW5Edm5sYnp5QUg4dzJqc3cpLCBjb21vIHN1IFtjYW5hbCBkZSBWRVZPXShodHRwczovL3d3dy55b3V0dWJlLmNvbS9jaGFubmVsL1VDT210dWs2TXA2NkRSRmNnODFrcjNlUSkuIExvcyBkYXRvcyBkZSBsb3MgdmlkZW9zIGRlIGFtYm9zIGNhbmFsZXMgbG9zIGhlIG9idGVuaWRvIGdyYWNpYXMgYWwgcGFxdWV0ZSAqKnR1YmVyKiosIG1lZGlhbnRlIGVzdGUgaGUgcG9kaWRvIG9idGVuZXIgZGF0b3MgYWNlcmNhIGRlIGxhcyB2aXNpdGFzLCAqbGlrZXMqLCAqZGlzbGlrZXMqIHkgY29tZW50YXJpb3MgZGUgY2FkYSB2aWRlby4KCkNvbWVuemFyIGEgdXRpbGl6YXIgZXN0ZSBwYXF1ZXRlIG5vIG1lIGhhIHJlc3VsdGFkbyBuYWRhIHNlbmNpbGxvLCBwdWVzLCBoYWLDrWEgcXVlIGhhYmlsaXRhciB1bmFzIEFQSSdzLCBsYXMgY3VhbGVzLCBoYXN0YSBlc2UgbW9tZW50byBubyBzYWLDrWEgbXV5IGJpZW4gbG8gcXVlIGVyYW4uIFBhcmEgcG9kZXIgYWN0aXZhciBlbCBwYXF1ZXRlIHkgbGFzIEFQSSwgaGUgc2VndWlkbyBbZXN0ZSB0dXRvcmlhbF0oaHR0cHM6Ly93d3cuc3RvcnliZW5jaC5vcmcvaG93LXRvLWRvd25sb2FkLXlvdXR1YmUtZGF0YS1pbi1yLXVzaW5nLXR1YmVyLWFuZC1wdXJyci8pLiAKCkEgY29udGludWFjacOzbiwgc2lndWllbmRvIGVsIHR1dG9yaWFsIGNpdGFkbyBoYWLDrWEgcXVlIGNyZWFyIHVuICpPQXV0aCBjbGllbnQqIHkgYXV0ZW50aWZpY2FybG8uIEFkanVudG8gZWwgY8OzZGlnbyBiYXNlIHF1ZSBoZSB1dGlsaXphZG8gcGFyYSBhYnJpciBlbCBPQXV0aCBbXjFdIHkgYXV0ZW50aWZpY2FybG86IHByb2NlZGltaWVudG8gcmVxdWVyaWRvIHBhcmEgcG9kZXIgb2J0ZW5lciBsb3MgZGF0b3MgZGUgY3VhbHF1aWVyIHZpZGVvIG8gY2FuYWwgcHJvY2VkZW50ZXMgZGUgWW91dHViZSBtZWRpYW50ZSBlbCBwYXF1ZXRlIHR1YmVyLgpgYGB7ciwgZXZhbCA9IFRSVUUsIGVjaG89VFJVRX0KbGlicmFyeSh0dWJlcikKY2xpZW50X2lkIDwtICI5MzIxMjk4NzI1NzctMWJnYTh2djQ3dGI2b25nYXZoMGdqdmY3aTQxb2pkNGIuYXBwcy5nb29nbGV1c2VyY29udGVudC5jb20iCmNsaWVudF9zZWNyZXQgPC0gImZnUnotYXRJNUh1dXkzWDNmc1UtdkNzNyIKCnl0X29hdXRoKGNsaWVudF9pZCwgY2xpZW50X3NlY3JldCkKCmBgYAoKIyMgPEZPTlQgQ09MT1I9ImViODhjZCI+T2J0ZW5pZW5kbyBsYXMgZXN0YWTDrXN0aWNhcyB5IGRhdG9zIHByaW5jaXBhbGVzPC9GT05UPgpVbmEgdmV6IGFiaWVydGEgZWwgT0F1dGggeSBjb24gbG9zIGNyZWRlbmNpYWxlcyBhdXRlbnRpY2Fkb3MsIGhlIHByb2NlZGlkbyBhIGRlc2NhcmdhciBsb3Mgc3RhdHMgcG9yIGNhbmFsLgpFbiBtaSBjYXNvLCBCYWQgR3lhbCB0aWVuZSAyIGNhbmFsZXM6IFZFVk8geSBwZXJzb25hbCwgZGUgZm9ybWEgcXVlLCBoZSB0ZW5pZG8gcXVlIGhhY2VyIGVsIHByb2NlZGltaWVudG8gMiB2ZWNlcy4KClByaW1lcm8sIGRlc2NhcmdhbW9zIGxvcyBzdGF0cyBkZSBsb3MgdmlkZW9zIGRlbCBjYW5hbCBwcmluY2lwYWwgbWVkaWFudGUgbGEgZnVuY2nDs24gKnl0X3NlYXJjaCgpKiwgaW5jbHVpZGEgZW4gZWwgcGFxdWV0ZS4KYGBge3IsIGV2YWwgPSBUUlVFLCBlY2hvPVRSVUV9CmJnY2FuYWwxPC0geXRfc2VhcmNoKHR5cGU9InZpZGVvIiwgdGVybT0iIiwgY2hhbm5lbF9pZCA9ICJVQzJ5cEJhWW5Edm5sYnp5QUg4dzJqc3ciKQpiZ2NhbmFsMTwtIGJnY2FuYWwxICU+JSBtdXRhdGUoZGF0ZSA9IGFzLkRhdGUocHVibGlzaGVkQXQpKSAlPiUgc2VsZWN0KHZpZGVvX2lkLCBkYXRlLCB0aXRsZSkKYGBgCgpFc3RvcyBzdGF0cyBsb3MgYXJyZWdsYW1vcyB1biBwb2NvLCB5YSBxdWUsIGVsIGRhdGFmcmFtZSByZXN1bHRhbnRlIGVzIHVuIGRhdGFmcmFtZSBxdWUgbm9zIGRhIGxhIGZlY2hhIGRlIHB1YmxpY2FjacOzbiBjb24gbGEgaG9yYSBleGFjdGEsIGxhIGN1YWwsIG5vIGxhIG5lY2VzaXRhbW9zIHkgdGFtYmnDqW4gbm9zIGRhIGRhdG9zIGFjZXJjYSBkZSBsYSBkZXNjcmlwY2nDs24gZGUgY2FkYSB2aWRlbywgbG9zIGN1YWxlcyB0YW1wb2NvIG5lY2VzaXRhcmVtb3MgdXNhci4KRGUgZXN0YSBmb3JtYSwgdGVuZHJlbW9zIHVuIGRhdGFmcmFtZSBjb24gZWwgaWQsIGxhIGZlY2hhIChtb2RpZmljYWRhIHBhcmEgcXVlIG5vIGFwYXJlemNhIGxhIGhvcmEpIHkgZWwgdMOtdHVsby4KClNpIHF1ZXJlbW9zIG3DoXMgZGV0YWxsZXMgYWNlcmNhIGRlIGxvcyB2aWRlb3MgdGVuZW1vcyBxdWUgdXRpbGl6YXIgbGEgZnVuY2nDs24gKmdldF9zdGF0cygpKiBkZW50cm8gZGVsIHBhcXVldGUuIENvbW8gcXVlcmVtb3MgaGFjZXJsbyBkZSB0b2RvcyBsb3MgdmlkZW9zIGRlbCBjYW5hbCB5IHRlbmVtb3MgbGEgaWQgZW4gZWwgZGF0YWZyYW1lIGFudGVyaW9yLCBwb2RlbW9zIHV0aWxpemFyIHVuIGJ1Y2xlIHBhcmEgb2J0ZW5lciBsb3Mgc3RhdHMgZGUgdG9kb3MgZGlyZWN0YW1lbnRlIGVuIHZleiBkZSBpciB2aWRlbyBhIHZpZGVvLgpQYXJhIGNvZ2VyIGxvcyBzdGF0cyBkZSB0b2RvcyBsb3MgdmlkZW9zIHV0aWxpemFyZW1vcyB1biAqKmxhcHBseSgpKiosIGVzdGEgZnVuY2nDs24gbm9zIHBpZGUgZWwgZGF0YWZyYW1lIHkgbHVlZ28gbGEgZnVuY2nDs24gcXVlIHV0aWxpemFyZW1vcywgZW4gY2xhc2UgaGFiw61hbW9zIHV0aWxpemFkbyB1bmEgZnVuY2nDs24geWEgZXhpc3RlbnRlOiAibWVhbiIgcGFyYSBsYSBtZWRpYS4gRW4gbnVlc3RybyBjYXNvLCBjcmVhcmVtb3MgcHJpbWVybyB1bmEgZnVuY2nDs24gcGFyYSBxdWUgY29qYSBsb3Mgc3RhdHMgZGUgdG9kYXMgbGFzIHZhcmlhYmxlcyB5IG5vIHRlbmdhbW9zIHF1ZSBpbnRyb2R1Y2lyIGxhIGlkIGRlIGxvcyB2aWRlb3Mgbm9zb3Ryb3MgbWFudWFsbWVudGUgdW5hIHBvciB1bmEsIHB1ZXMsICFPam8sIGhheSAzMiB2aWRlb3MhIHkgY29uIGVzdGEgZnVuY2nDs24geWEgY3JlYWRhLCBlbCAqKmxhcHBseSoqIHNlcsOhIG3DoXMgZmFjaWwuCgpgYGB7ciwgZXZhbD1UUlVFLCBlY2hvPVRSVUV9CiNDcmVhbW9zIGxhIGZ1bmNpw7NuIHF1ZSBtZXRlcmVtb3MgZW4gZWwgbGFwcGx5LiBFc3RhIGZ1bmNpw7NuIGhhY2UgcXVlIHBvciBhIGNhZGEgaWQgZGVsIGRhdGFmcmFtZSBhbnRlcmlvciBzZSBhcGxpcXVlIGVsIGdldF9zdGF0cygpLCBwYXJhIGFzw60gb2J0ZW5lciBkZXRhbGxlcyBtw6FzIGNvbmNyZXRvcyBkZSBjYWRhIHZpZGVvIGRlbCBjYW5hbC4KZnVuY2lvbl9zdGF0c192aWRlb3MgPC0gZnVuY3Rpb24oeCkgewogIGdldF9zdGF0cyh2aWRlb19pZCA9IHgpCn0KCmJnc3RhdGxpc3RhIDwtIGxhcHBseShiZ2NhbmFsMSR2aWRlb19pZCwgZnVuY2lvbl9zdGF0c192aWRlb3MpCmBgYAoKRGUgZXN0YSBmb3JtYSwgZWwgbGFwcGx5IG5vcyBkZXZvbHZlcsOhIHVuYSBsaXN0YSwgbGEgY3VhbCwgcGFyYSB0cmFuc2Zvcm1hcmxhIGVuIGRhdGFmcmFtZSB1dGlsaXphcmVtb3MgZWwgc2lndWllbnRlIGPDs2RpZ286CmBgYHtyLCBldmFsPVRSVUUsIGVjaG89VFJVRX0KYmdfc3RhdHNfdmlkZW9zPC0gZG8uY2FsbChyYmluZC5kYXRhLmZyYW1lLCBiZ3N0YXRsaXN0YSkgCmBgYAoKQ29tbyB2ZW1vcywgeWEgdGVuZW1vcyAyIGRhdGFmcmFtZXMsIHkgc29sbyBub3MgcXVlZGFyw6EganVudGFybG9zIHBhcmEgdGVuZXIgdG9kb3MgZXN0b3MgKmRhdG9zLXN0YXRzKiBlbiB1bm8gc29sbzoKYGBge3IsIGV2YWw9VFJVRSwgZWNobz1UUlVFfQojQWhvcmEgaGFjZW1vcyB1biBmdWxsIGpvaW4gcGFyYSBhbWJvcyBkYXRhZnJhbWVzLgpiZ19zdGF0c19mdWxsPC0gZnVsbF9qb2luKGJnY2FuYWwxLCBiZ19zdGF0c192aWRlb3MsIGJ5PWMoInZpZGVvX2lkIj0iaWQiKSkKYGBgCgpBaG9yYSwgcmVwZXRpbW9zIGxhcyBtaXNtYXMgYWNjaW9uZXMgcGFyYSBlbCAywrogY2FuYWwsIGVsIFZFVk8uCmBgYHtyLCBldmFsPVRSVUUsIGVjaG89VFJVRX0KI0xvcyBwcmltZXJvcyBzdGF0cyBkZWwgY2FuYWwgVkVWTwpiZ2NhbmFsMjwtIHl0X3NlYXJjaCh0eXBlPSJ2aWRlbyIsIHRlcm09IiIsIGNoYW5uZWxfaWQgPSAiVUNPbXR1azZNcDY2RFJGY2c4MWtyM2VRIikKYmdjYW5hbDI8LSBiZ2NhbmFsMiAlPiUgbXV0YXRlKGRhdGUgPSBhcy5EYXRlKHB1Ymxpc2hlZEF0KSkgJT4lIHNlbGVjdCh2aWRlb19pZCwgZGF0ZSwgdGl0bGUpCgojQnVjbGUgcGFyYSBvYnRlbmVyIGxvcyBzZWd1bmRvcyBzdGF0cyAKYmdzdGF0bGlzdGEyIDwtIGxhcHBseShiZ2NhbmFsMiR2aWRlb19pZCwgZnVuY2lvbl9zdGF0c192aWRlb3MpCgojUGFyYSB0cmFuc2Zvcm1hciBsYSBsaXN0YSBlbiBkYXRhZnJhbWUsIHV0aWxpemFtb3MgbGEgZnVuY2nDs24KYmdfc3RhdHNfdmlkZW9zMjwtIGRvLmNhbGwocmJpbmQuZGF0YS5mcmFtZSwgYmdzdGF0bGlzdGEyKQoKI0Fob3JhIGhhY2Vtb3MgdW4gZnVsbCBqb2luIHBhcmEgYW1ib3MgZGF0YWZyYW1lcy4KYmdfc3RhdHNfZnVsbDI8LSBmdWxsX2pvaW4oYmdjYW5hbDIsIGJnX3N0YXRzX3ZpZGVvczIsIGJ5PWMoInZpZGVvX2lkIj0iaWQiKSkKCmBgYAoKQXPDrSwgZmluYWxtZW50ZSBhZ3J1cGFtb3MgbG9zIGRhdGFmcmFtZXMgZGUgbG9zIDIgY2FuYWxlcyBlbiB1biBzb2xvIGRhdGFmcmFtZSwgeSBhZGVtw6FzIGxvIGFycmVnbGFtb3MgdW4gcG9jbzogY2FtYmlhbW9zIGxvcyBub21icmVzIGRlIGxhcyBjb2x1bW5hcywgc2VsZWNjaW9uYW1vcyBsYXMgdmFyaWFibGVzIHF1ZSBxdWVyZW1vcyBxdWUgYXBhcmV6Y2FuIHkgcGFzYW1vcyBkZSAqY2hhcmFjdGVyKiBhICpudW1lcmljKiBsYXMgY29sdW1uYXMgdGFudG8gZGUgKmxpa2VzKiwgY29tZW50YXJpb3MgeSB2aXNpdGFzLgpgYGB7ciwgZXZhbD1UUlVFLCBlY2hvPVRSVUV9CiNVbmltb3MKYmdfc3RhdHNfYW1ib3NfY2FuYWxlczwtIGZ1bGxfam9pbihiZ19zdGF0c19mdWxsLCBiZ19zdGF0c19mdWxsMikKCiNBcnJlZ2xhbW9zIHVuIHBvY28gZWwgZGF0YWZyYW1lCmJnX3N0YXRzX2FtYm9zX2NhbmFsZXM8LSBiZ19zdGF0c19hbWJvc19jYW5hbGVzICU+JQogIHNlbGVjdCh0aXRsZSwgZGF0ZSwgdmlld0NvdW50LCBsaWtlQ291bnQsIGNvbW1lbnRDb3VudCkgJT4lCiAgcmVuYW1lKHRpdHVsbz10aXRsZSwgZmVjaGE9ZGF0ZSwgdmlzaXRhcz12aWV3Q291bnQsIGxpa2VzPWxpa2VDb3VudCwgY29tZW50YXJpb3M9Y29tbWVudENvdW50KSU+JQogIGFycmFuZ2UoZmVjaGEpCgpiZ19zdGF0c19hbWJvc19jYW5hbGVzMTwtIHRyYW5zZm9ybShiZ19zdGF0c19hbWJvc19jYW5hbGVzLCB2aXNpdGFzPWFzLm51bWVyaWMoYXMuY2hhcmFjdGVyKHZpc2l0YXMpKSwKICBsaWtlcz1hcy5udW1lcmljKGFzLmNoYXJhY3RlcihsaWtlcykpLAogIGNvbWVudGFyaW9zPWFzLm51bWVyaWMoYXMuY2hhcmFjdGVyKGNvbWVudGFyaW9zKSkpCgoKIy0gUGFyYSBmaW5hbGl6YXIsIGxpbXBpYW1vcyBlbCBHbG9iYWwgRW52LgpvYmpldG9zX25vX2JvcnJhciA8LSBjKCJiZ19zdGF0c19hbWJvc19jYW5hbGVzMSIpCnJtKGxpc3QgPSBscygpWyFscygpICVpbiUgb2JqZXRvc19ub19ib3JyYXJdKQpgYGAKCkVsIGRhdGFmcmFtZSByZXN1bHRhbnRlIHNlcsOhIGRlIGVzdGEgZm9ybWE6CmBgYHtyLCBlY2hvPUZBTFNFLCBldmFsPVRSVUV9CnJlYWN0YWJsZSgKICBiZ19zdGF0c19hbWJvc19jYW5hbGVzMSwKICBkZWZhdWx0Q29sRGVmID0gY29sRGVmKAogICAgaGVhZGVyID0gZnVuY3Rpb24odmFsdWUpIGdzdWIoIi4iLCAiICIsIHZhbHVlLCBmaXhlZCA9IFRSVUUpLAogICAgY2VsbCA9IGZ1bmN0aW9uKHZhbHVlKSBmb3JtYXQodmFsdWUsIG5zbWFsbCA9IDEpLAogICAgYWxpZ24gPSAiY2VudGVyIiwKICAgIG1pbldpZHRoID0gNzAsCiAgICBoZWFkZXJTdHlsZSA9IGxpc3QoYmFja2dyb3VuZCA9ICIjZjdmN2Y4IikKICApLAogIGNvbHVtbnMgPSBsaXN0KAogICAgdGl0dWxvID0gY29sRGVmKHN0eWxlPWZ1bmN0aW9uKHZhbHVlKXsKICAgICAgbGlzdChtaW5XaWR0aCA9IDE0MCwgZm9udFdlaWdodD0iYm9sZCIpCiAgICB9CiAgKSksCiAgYm9yZGVyZWQgPSBUUlVFLAogIGhpZ2hsaWdodCA9IFRSVUUsCiAgZGVmYXVsdFBhZ2VTaXplPTUsCikKYGBgCgojIyA8Rk9OVCBDT0xPUj0iZWI4OGNkIj5UYWJsYTogMyB2aWRlb3MgbcOhcyB2aXN0b3M8L0ZPTlQ+CkEgcGFydGlyIGRlbCBkYXRhZnJhbWUgYW50ZXJpb3IsIHZveSBhIG1vc3RyYXIgbG9zIDMgdmlkZW9zIG3DoXMgdmlzdG9zIGRlIGxvcyBjYW5hbGVzIGRlICoqQmFkIEd5YWwqKiwgZW4gZXN0ZSBjYXNvLCB1dGlsaXphcsOpIGVsIHBhcXVldGUgKipndCoqIHBhcmEgbGEgdGFibGEsIGVuIHZleiBkZWwgcGFxdWV0ZSBxdWUgeWEgaGUgdXNhZG8gYW50ZXM6ICoqcmVhY3RhYmxlKiouCgpQcmltZXJvLCBtb2RpZmljYW1vcyBlbCBkYXRhZnJhbWUgZGUgZm9ybWEgcXVlIG5vcyBtdWVzdHJlIGxvcyB0w610dWxvcyBvcmRlbmFkb3MgZGUgbcOhcyB2aXNpdGFzIGEgbWVub3MsIHkgYSBzdSB2ZXosIHF1ZSBub3MgbXVlc3RyZSB0YW1iacOpbiBjdWFudG9zIGxpa2VzIHkgY29tZW50YXJpb3MgdGllbmVuLCBwYXJhLCBoYWNlciB1biBlc2NhbGFkbyBkZSBjb2xvciBkZXNkZSBlbCBxdWUgbcOhcyB0ZW5nYSBhbCBxdWUgbWVub3MgdGVuZ2EuCkEgcGFydGlyIGRlIGFxdcOtLCBnZW5lcmFyZW1vcyB1bmEgdGFibGEgcXVlIG11ZXN0cmUgbG9zIDMgcHJpbWVyb3MgdMOtdHVsb3MuCmBgYHtyLCBlY2hvPVRSVUUsIGV2YWw9VFJVRX0KI01vZGlmaWNhbW9zIGVsIGRhdGFmcmFtZQpiZ19zdGF0c18zX3ByaW1lcm9zIDwtIGJnX3N0YXRzX2FtYm9zX2NhbmFsZXMxICU+JSBzbGljZV9tYXgodmlzaXRhcywgbj0zKQoKI0hhY2Vtb3MgbGEgdGFibGEgYSBudWVzdHJvIGd1c3RvOiBjb2xvcmVzIGNvbiBlc2NhbGEsIGRlc2RlIHVuYSBwYWxldGEgdmVyZGUgcGFyYSBsb3MgdmFsb3JlcyBtw6FzIGFsdG9zIGEgdW5hIHBhbGV0YSBkZSByb2pvcyBwYXJhIGxvcyB2YWxvcmVzIG3DoXMgYmFqb3MuCnRhYmxhX2d0PC0gYmdfc3RhdHNfM19wcmltZXJvcyAlPiUgCiAgZ3QoKSAlPiUgCiAgdGFiX2hlYWRlcih0aXRsZT0gbWQoIioqMyB2aWRlb3MgbcOhcyB2aXN0b3MqKiIpKSAlPiUgCiAgdGFiX29wdGlvbnMoaGVhZGluZy5iYWNrZ3JvdW5kLmNvbG9yID0gIiM5MWVkZTQiLCBjb2x1bW5fbGFiZWxzLmZvbnQud2VpZ2h0ID0gICJib2xkIiklPiUKICBjb2xzX2FsaWduKGFsaWduPSJjZW50ZXIiKSAlPiUKICB0YWJfc3R5bGUoY2VsbF9ib3JkZXJzKHNpZGVzPSJhbGwiLCBjb2xvcj0iYmxhY2siKSwgY2VsbHNfYm9keSgpKSU+JQogIGRhdGFfY29sb3IoIAogICAgY29sdW1ucyA9IHZhcnModmlzaXRhcyksIAogICAgY29sb3JzID0gc2NhbGVzOjpjb2xfbnVtZXJpYyggCiAgICAgIHBhbGV0dGUgPSBjKAogICAgICAgICIjZGI1MzVmIiwiI2ZmZTE2YiIsICIjNjRkZTY4IiksICMgCiAgICAgIGRvbWFpbiA9IGMobWF4KHZpc2l0YXMpLCBtaW4odmlzaXRhcykpICkKICAgICkgJT4lIAogIGRhdGFfY29sb3IoIAogICAgY29sdW1ucyA9IHZhcnMobGlrZXMpLCAKICAgIGNvbG9ycyA9IHNjYWxlczo6Y29sX251bWVyaWMoIAogICAgICBwYWxldHRlID0gYygKICAgICAgICAiI2RiNTM1ZiIsIiNmZmUxNmIiLCAiIzY0ZGU2OCIpLCAjIAogICAgICBkb21haW4gPSBjKG1heChsaWtlcyksIG1pbihsaWtlcykpICkKICAgICkgJT4lCiAgZGF0YV9jb2xvciggCiAgICBjb2x1bW5zID0gdmFycyhjb21lbnRhcmlvcyksIAogICAgY29sb3JzID0gc2NhbGVzOjpjb2xfbnVtZXJpYyggCiAgICAgIHBhbGV0dGUgPSBjKAogICAgICAgICIjZGI1MzVmIiwiI2ZmZTE2YiIsIiM2NGRlNjgiKSwgIyAKICAgICAgZG9tYWluID0gYyhtYXgoY29tZW50YXJpb3MpLCBtaW4oY29tZW50YXJpb3MpKSApCiAgICApIAoKI0xhIG1vc3RyYW1vcwp0YWJsYV9ndAoKCmBgYAoKCiMjIDxGT05UIENPTE9SPSJlYjg4Y2QiPkdyw6FmaWNvIGRlIHRhcnRhOiB2aWRlb3MgY29uIG3DoXMgbGlrZXM8L0ZPTlQ+CkVuIGVzdGUgYXBhcnRhZG8gaGUgcXVlcmlkbyByZWFsaXphciB1biBncsOhZmljbyBkZSBxdWVzaXRvcyBvICoqInBpZSBjaGFydCIqKiBhIHBhcnRpciBkZSBsb3MgNiB2aWRlb3MgY29uIG3DoXMgbGlrZXMuCkVsIGdyw6FmaWNvIGxvIGhlIGhlY2hvIGEgcGFydGlyIGRlbCBwb3JjZW50YWplIGRlIHZpc2l0YXMgZGUgY2FkYSB2aWRlbyBzb2JyZSBlbCB0b3RhbCwgdmFyaWFibGUgcXVlIGhlIHRlbmlkbyBxdWUgY3JlYXIsIGRlIGZvcm1hIHF1ZSwgc2UgY3JlYXLDoSB1biBncsOhZmljbyBkZSB0cm96b3MgZGUgdGFydGEgY29uIDYgdMOtdHVsb3MgbcOhcyB1bmEgdmFyaWFibGUgcXVlIGVuZ2xvYmEgYSBsb3MgdmlkZW9zIG5vIHNlbGVjY2lvbmFkb3MsIHBvcmNlbnRhamUgZGUgbGEgY3VhbCBzZXLDoSBlbCAxMDAlIG1lbm9zIGVsIHBvcmNlbnRhamUgZGUgbGEgc3VtYSBkZSBsb3MgdMOtdHVsb3Mgc2VsZWNjaW9uYWRvcy4KClBhcmEgZXN0ZSwgbm9zIGhlbW9zIGJhc2FkbyBlbiBlbCBtb2RlbG8gYsOhc2ljbyBwcm9wdWVzdG8gZW4gW1RoZSBSIEdyYXBoIEdhbGxlcnldKGh0dHBzOi8vd3d3LnItZ3JhcGgtZ2FsbGVyeS5jb20vcGllY2hhcnQtZ2dwbG90Mi5odG1sKQoKYGBge3IsIGVjaG89VFJVRSwgZXZhbD1UUlVFfQojQ29uc2VndWltb3MgZW4gdmVjdG9yIGVsIG7Dum1lcm8gdG90YWwgZGUgbGlrZXMgKHN1bWFuZG8gbG9zIGxpa2VzIGRlIHRvZG9zIGxvcyB2aWRlb3MpCnN1bWFfdG90YWxfbGlrZXM8LSBiZ19zdGF0c19hbWJvc19jYW5hbGVzMSAlPiUgc3VtbWFyaXNlKHN1bWE9c3VtKGxpa2VzKSkKc3VtYV90b3RhbF9saWtlczwtIHN1bWFfdG90YWxfbGlrZXNbMSwxXQoKI0NyZWFtb3MgdW5hIG51ZXZhIHZhcmlhYmxlICIlIGxpa2VzIiBwYXJhIHNhYmVyIGVsIHBvcmNlbnRhamUgZGUgbGlrZXMgcXVlIGxlIGNvcnJlc3BvbmRlIGEgY2FkYSB2aWRlbwpiZ19zdGF0c19waWU8LSBiZ19zdGF0c19hbWJvc19jYW5hbGVzMSAlPiUgbXV0YXRlKGAlIGxpa2VzYD0gbGlrZXMgLyBzdW1hX3RvdGFsX2xpa2VzICoxMDApICU+JSBzZWxlY3QodGl0dWxvLCBgJSBsaWtlc2ApCgojSGFjZW1vcyB1biBzbGljZSBwYXJhIGFxdWVsbG9zIHZpZGVvcyBjb24gbWF5b3IgcG9yY2VudGFqZSBkZSBsaWtlcyB5IHN1bWFtb3MgbG9zIGxpa2VzIGRlIHRvZG9zIHBhcmEgdmVyIGN1YW50byAlZGUgbGlrZXMgc29icmUgZWwgdG90YWwoMTAwKSBoYXkgZW4gZXN0b3MgNiB2aWRlb3MuCmJnXzZzdGF0c19waWU8LSBiZ19zdGF0c19waWUgJT4lIHNsaWNlX21heChgJSBsaWtlc2AsIG49NikgJT4lIG11dGF0ZShzdW1hPXN1bShgJSBsaWtlc2ApKQoKI0NhbGN1bGFtb3MgbGEgZGlmZXJlbmNpYSBlbnRyZSBlbCBwb3JjZW50YWplIHF1ZSBzdW1hbiBsb3MgNiB2aWRlb3MgY29uIG3DoXMgbGlrZXMgKGVsIHBvcmNlbnRhamUgYW50ZXJpb3IpIHkgZWwgdG90YWwuCmRpZl82c3RhdHM8LSAxMDAtIGJnXzZzdGF0c19waWUkc3VtYVsxXQoKI0HDsWFkaW1vcyBlc2EgZGlmZXJlbmNpYSBjb21vIHVuYSBjb2x1bW5hIG3DoXMgcGFyYSBudWVzdHJvIGRhdGFmcmFtZSB5IGxhIGxsYW1hbW9zICJvdHJvcyI7IGVzdGEgY29sdW1uYSBpbmNsdWlyw6EgbG9zIHBvcmNlbnRhamVzIGRlIGxvcyBkZW3DoXMgdmlkZW9zLXTDrXR1bG9zIHF1ZSBubyBoYW4gc2lkbyBpbmNsdWlkbyBlbiBsb3MgNiB2aWRlb3MgZGVsIHNsaWNlX21heC4KYmdfNnN0YXRzX3BpZTwtIGJnXzZzdGF0c19waWUlPiVzZWxlY3QodGl0dWxvLCBgJSBsaWtlc2ApICU+JSBhZGRfcm93KHRpdHVsbz0iT3Ryb3MiLCBgJSBsaWtlc2A9ZGlmXzZzdGF0cykKCiNIYWNlbW9zIGVsIHBpZSBjaGFydC4gU2Vyw61hIGNvbW8gdW4gZ2dwbG90ICsgZ2VvbV9iYXIoKSBub3JtYWwsIHNvbG8gcXVlLCBoYWNlbW9zIHVuIGdpcm8gZGUgY29vcmRlbmFkYXMgY29uIGNvb3JkX3BvbGFyIHBhcmEgZGFybGUgZm9ybWEgcmVkb25kYSwgZWwgcmVzdG8gZGUgY29zYXMgcXVlIGhlIGlkbyBwb25pZW5kbyBzb24gYcOxYWRpZG9zIGV4dHJhLgpiZ3BpZTwtIGdncGxvdChiZ182c3RhdHNfcGllLCBhZXMoeD0iIiwgeT1gJSBsaWtlc2AsIGZpbGw9dGl0dWxvKSkrCiAgZ2VvbV9iYXIoc3RhdD0iaWRlbnRpdHkiLHdpZHRoPTEsIGNvbG9yPSJ3aGl0ZSIpKwogIGNvb3JkX3BvbGFyKCJ5Iiwgc3RhcnQ9MCkrCiAgZ2VvbV90ZXh0KGFlcyhsYWJlbD1wYXN0ZTAocm91bmQoYCUgbGlrZXNgKSwgIiUiKSksIHBvc2l0aW9uPXBvc2l0aW9uX3N0YWNrKHZqdXN0PTAuNSkpKwogIGxhYnModGl0bGU9Ikdyw6FmaWNvIGRlIHRhcnRhOiBsaWtlcyIpKwogICB0aGVtZV92b2lkKCkKYGBgCgpgYGB7ciBvdXQud2lkdGg9IjgwJSIsIGVjaG89RkFMU0UsIGV2YWw9VFJVRX0KYmdwaWUKYGBgCgoKIyA8Rk9OVCBDT0xPUj0iZWI4OGNkIj4qKjMuIE9idGVuaWVuZG8gbcOhcyBkYXRvczogZGF0YWZyYW1lIHNlY3VuZGFyaW8qKjwvRk9OVD4KRWwgZGF0YWZyYW1lIGFudGVyaW9yIHF1ZSBhZ3J1cGEgbG9zIHZpZGVvcyBkZSBhbWJvcyBjYW5hbGVzIG5vIG5vcyBvZnJlY2UgaW5mb3JtYWNpw7NuIHNvYnJlIGxhcyB2aXNpdGFzIGRpYXJpYXMgZW4gbG9zIHZpZGVvcyBkZXNkZSBzdSBwdWJsaWNhY2nDs24sIHBvciBsbyBxdWUsIHBhcmEgaGFjZXIgZGlmZXJlbnRlcyB0cmFuc2Zvcm1hY2lvbmVzIG3DoXMgYWRlbGFudGUgbG8gcXVlIHZveSBhIGhhY2VyIGVzIGNvbnNlZ3VpciBkZSBmb3JtYSAidmFnYSIgZXN0YXMgdmlzaXRhcyBkaWFyaWFzLgpQYXJhIGVzdG8sIGhlIGRlY2lkaWRvIGZpbHRyYXIgZGVsIGRhdGFmcmFtZSBwcmluY2lwYWwgYW50ZXJpb3IgbG9zIHZpZGVvcyBjb24gbWVub3MgZGUgMS4wMDAuMDAwIHZpc2l0YXMgeSBjb24gbWVub3MgZGUgMTAwMCBjb21lbnRhcmlvcyB5IG9wZXJhciBhIHBhcnRpciBkZSBhaMOtLgoKRWwgcHJvY2VkaW1pZW50byBxdWUgaGUgc2VndWlkbyBoYSBzaWRvIGFsZ28gbGFib3Jpb3NvLCBwdWVzLCBlbiBlc3RlIGNhc28gbm8gc2Fiw61hIGNvbW8gaW50cm9kdWNjaXIgdW4gbG9vcCBwYXJhIGhhY2VybG8gbcOhcyBhdXRvbcOhdGljbyB5IGhlIHRlbmlkbyBxdWUgaXIgZmlsYSBhIGZpbGEgKHZpZGVvIGEgdmlkZW8sIGRlIGxvcyAxNyByZXN1bHRhbnRlcyBwb3IgZWwgZmlsdHJhZG8gcXVlIGhlIGNvbWVudGFkbykuCkVzdGUgcHJvY2VkaW1pZW50byBsbyBkZXRhbGxvIGEgY29udGludWFjacOzbiB5IHRhbWJpw6luIGVuIGVsIGPDs2RpZ28gcXVlIGxlIHNpZ3VlOgoKKiBBbG1hY2VuYW1vcyBlbiAxNyAqdmFyaWFibGVzLXZhbHVlcyogbGFzIGZlY2hhcyBhIHBhcnRpciBkZWwgZGF0YWZyYW1lIHF1ZSBoZW1vcyBmaWx0cmFkbyB5IG90cmEgbcOhcyBwYXJhIGxhIGZlY2hhIGFjdHVhbCwgbWVkaWFudGUgbGEgZnVuY2nDs24gdG9kYXkoKSBkZWwgcGFxdWV0ZSAqKmx1YnJpZGF0ZSoqLgoqIEFsbWFjZW5hbW9zIGxhcyB2aXNpdGFzIGRlIGNhZGEgdmlkZW8gZW4gdmFyaWFibGVzIGRpZmVyZW50ZXMgKGRlIG51ZXZvIDE3IHZhcmlhYmxlcywgdW5hIHBvciBjYWRhIHZpZGVvKS4KKiBDYWxjdWxhbW9zIGxhIGRpZmVyZW5jaWEgZW4gZMOtYXMsIGVzIGRlY2lyLCBsb3MgZMOtYXMgcXVlIGhhbiBwYXNhZG8gZGVzZGUgbGEgcHVibGljYWNpw7NuIGRlIGxvcyB2aWRlb3MgeSBsYSBmZWNoYSBhY3R1YWwsIHkgYWxtYWNlbmFtb3MgZXN0b3MgZGF0b3MgZGUgbnVldm8gZW4gMTcgdmFyaWFibGVzLgoqIENhbGN1bGFtb3MgbGEgbWVkaWEgZGlhcmlhIGRlIGxhcyB2aXNpdGFzIGRlIGNhZGEgdmlkZW8sIGNvbiBsYSBkaWZlcmVuY2lhIGRlIGTDrWFzIGFudGVyaW9yIHkgbGFzIHZpc2l0YXMgdG90YWxlcy4KKiBDcmVhbW9zIHVuIGRhdGFmcmFtZSBwYXJhIGNhZGEgdmlkZW8sIGVsIGN1YWwsIHNlIGNvbXBvbmUgZGUgdW5hIGNvbHVtbmEgbGxhbWFkYSBmZWNoYSwgbGEgY3VhbCBlc3TDoSBjb25zdHJ1aWRhIHByZXZpYW1lbnRlIGNvbiBsYSBmdW5jacOzbiAqKnNlcSgpKiogeSBvdHJhIGNvbHVtbmEgY29uIGxhIG1lZGlhIGRpYXJpYSBkZSBjYWRhIHZpZGVvIGNhbGN1bGFkYS4gCkVzdGEgcHJpbWVyYSBjb2x1bW5hIGVzdGFyw6EgZm9ybWFkYSBwb3IgbG9zIGTDrWFzIHF1ZSBoYW4gcGFzYWRvIGRlc2RlIGxhIHB1YmxpY2FjacOzbiBkZWwgdmlkZW8gaGFzdGEgbGEgZmVjaGEgYWN0dWFsLCBlcyBkZWNpciwgc2kgdW4gdmlkZW8gZXN0dXZpZXJhIHB1YmxpY2FkbyAwMS8wMS8yMDIwLCBsYXMgZmlsYXMgZGUgbGEgY29sdW1uYSBzZXLDrWFuOiAwMS8wMS8yMCwgMDIvMDEvMjAsIDAzLzAxLzIwLi4uIGFzw60gaGFzdGEgbGEgZmVjaGEgYSBkw61hIGRlIGhveS4KKiBVbmlyZW1vcyBtZWRpYW50ZSBmdWxsX2pvaW4oKSBjYWRhIHVubyBkZSBsb3MgZGF0YWZyYW1lcyBjcmVhZG9zLCBkZSBmb3JtYSBxdWUsIHRlbmRyZW1vcyB1biBkYXRhZnJhbWUgZW4gZm9ybWF0byAqd2lkZXIqIGRvbmRlIGhhYnLDoSB1bmEgY29sdW1uYSBjb23Dum4gKGZlY2hhKSB5IHVuYSBjb2x1bW5hIHBvciBjYWRhIG1lZGlhIGRlIHZpc2l0YXMgY2FsY3VsYWRhIGRlIGxvcyB2aWRlb3MgKDE3KS4KCgpgYGB7ciwgZXZhbD1UUlVFLCBlY2hvPVRSVUV9CiNWaWRlb3MgPjEuMDAwLjAwMCB2aWV3cyB5ID4xMDAwIGNvbWVudGFyaW9zCmJnX21heW9yXzE8LSBiZ19zdGF0c19hbWJvc19jYW5hbGVzMSAlPiUgZmlsdGVyKHZpc2l0YXM+PTEwMDAwMDAsIGNvbWVudGFyaW9zPj0xMDAwKQoKI0ZlY2hhcwpmZWNoYWhveSA8LSB0b2RheSgpCmZlY2hhUEFJPC1iZ19tYXlvcl8xJGZlY2hhWzFdCmZlY2hhSU5EQVBBTjwtYmdfbWF5b3JfMSRmZWNoYVsyXQpmZWNoYU1FUkNBPC0gYmdfbWF5b3JfMSRmZWNoYVszXQpmZWNoYUZJRUJSRTwtIGJnX21heW9yXzEkZmVjaGFbNF0KZmVjaGFKQUNBPC0gYmdfbWF5b3JfMSRmZWNoYVs1XQpmZWNoYU5JQ0U8LSBiZ19tYXlvcl8xJGZlY2hhWzZdCmZlY2hhQ0FORCA8LSBiZ19tYXlvcl8xJGZlY2hhWzddCmZlY2hhSU5UPC0gYmdfbWF5b3JfMSRmZWNoYVs4XQpmZWNoYU1BUzwtIGJnX21heW9yXzEkZmVjaGFbOV0KZmVjaGFPUEVOPC0gYmdfbWF5b3JfMSRmZWNoYVsxMF0KZmVjaGFJVUFMPC0gYmdfbWF5b3JfMSRmZWNoYVsxMV0KZmVjaGFNQVJJPC0gYmdfbWF5b3JfMSRmZWNoYVsxMl0KZmVjaGFIT09LQUg8LSBiZ19tYXlvcl8xJGZlY2hhWzEzXQpmZWNoYVpPUlI8LSBiZ19tYXlvcl8xJGZlY2hhWzE0XQpmZWNoYUJPTTwtIGJnX21heW9yXzEkZmVjaGFbMTVdCmZlY2hhU0VYPC0gYmdfbWF5b3JfMSRmZWNoYVsxNl0KZmVjaGFCTElOPC0gYmdfbWF5b3JfMSRmZWNoYVsxN10KCiNWaXNpdGFzCm51bVBBSTwtIGJnX21heW9yXzEkdmlzaXRhc1sxXQpudW1JTkRBUEFOPC0gYmdfbWF5b3JfMSR2aXNpdGFzWzJdCm51bU1FUkNBPC0gYmdfbWF5b3JfMSR2aXNpdGFzWzNdCm51bUZJRUJSRTwtIGJnX21heW9yXzEkdmlzaXRhc1s0XQpudW1KQUNBPC0gYmdfbWF5b3JfMSR2aXNpdGFzWzVdCm51bU5JQ0U8LSBiZ19tYXlvcl8xJHZpc2l0YXNbNl0KbnVtQ0FORDwtIGJnX21heW9yXzEkdmlzaXRhc1s3XQpudW1JTlQ8LSBiZ19tYXlvcl8xJHZpc2l0YXNbOF0KbnVtTUFTPC0gYmdfbWF5b3JfMSR2aXNpdGFzWzldCm51bU9QRU48LSBiZ19tYXlvcl8xJHZpc2l0YXNbMTBdCm51bUlVQUw8LSBiZ19tYXlvcl8xJHZpc2l0YXNbMTFdCm51bU1BUkk8LSBiZ19tYXlvcl8xJHZpc2l0YXNbMTJdCm51bUhPT0tBSDwtIGJnX21heW9yXzEkdmlzaXRhc1sxM10KbnVtWk9SUjwtIGJnX21heW9yXzEkdmlzaXRhc1sxNF0KbnVtQk9NPC0gYmdfbWF5b3JfMSR2aXNpdGFzWzE1XQpudW1TRVg8LSBiZ19tYXlvcl8xJHZpc2l0YXNbMTZdCm51bUJMSU48LSBiZ19tYXlvcl8xJHZpc2l0YXNbMTddCgojRMOtYXMgcXVlIGhhbiBwYXNhZG8gZGVzZGUgZmVjaGEgdmlkZW8gYSBob3kKZGlmUEFJPC0gZGlmZnRpbWUoZmVjaGFob3ksIGZlY2hhUEFJLCB1bml0cz0iZGF5cyIpICU+JSBhcy5udW1lcmljKCkKZGlmSU5EQVBBTjwtIGRpZmZ0aW1lKGZlY2hhaG95LCBmZWNoYUlOREFQQU4sIHVuaXRzPSJkYXlzIikgJT4lIGFzLm51bWVyaWMoKQpkaWZNRVJDQTwtIGRpZmZ0aW1lKGZlY2hhaG95LCBmZWNoYU1FUkNBLCB1bml0cz0iZGF5cyIpICU+JSBhcy5udW1lcmljKCkKZGlmRklFQlJFPC0gZGlmZnRpbWUoZmVjaGFob3ksIGZlY2hhRklFQlJFLCB1bml0cz0iZGF5cyIpICU+JSBhcy5udW1lcmljKCkKZGlmSkFDQTwtIGRpZmZ0aW1lKGZlY2hhaG95LCBmZWNoYUpBQ0EsIHVuaXRzPSJkYXlzIikgJT4lIGFzLm51bWVyaWMoKQpkaWZOSUNFPC0gZGlmZnRpbWUoZmVjaGFob3ksIGZlY2hhTklDRSwgdW5pdHM9ImRheXMiKSAlPiUgYXMubnVtZXJpYygpCmRpZkNBTkQ8LSBkaWZmdGltZShmZWNoYWhveSwgZmVjaGFDQU5ELCB1bml0cz0iZGF5cyIpICU+JSBhcy5udW1lcmljKCkKZGlmSU5UPC0gZGlmZnRpbWUoZmVjaGFob3ksIGZlY2hhSU5ULCB1bml0cz0iZGF5cyIpICU+JSBhcy5udW1lcmljKCkKZGlmTUFTPC0gZGlmZnRpbWUoZmVjaGFob3ksIGZlY2hhTUFTLCB1bml0cz0iZGF5cyIpICU+JSBhcy5udW1lcmljKCkKZGlmT1BFTjwtIGRpZmZ0aW1lKGZlY2hhaG95LCBmZWNoYU9QRU4sIHVuaXRzPSJkYXlzIikgJT4lIGFzLm51bWVyaWMoKQpkaWZJVUFMPC0gZGlmZnRpbWUoZmVjaGFob3ksIGZlY2hhSVVBTCwgdW5pdHM9ImRheXMiKSAlPiUgYXMubnVtZXJpYygpCmRpZk1BUkk8LSBkaWZmdGltZShmZWNoYWhveSwgZmVjaGFNQVJJLCB1bml0cz0iZGF5cyIpICU+JSBhcy5udW1lcmljKCkKZGlmSE9PS0FIPC0gZGlmZnRpbWUoZmVjaGFob3ksIGZlY2hhSE9PS0FILCB1bml0cz0iZGF5cyIpICU+JSBhcy5udW1lcmljKCkKZGlmWk9SUjwtIGRpZmZ0aW1lKGZlY2hhaG95LCBmZWNoYVpPUlIsIHVuaXRzPSJkYXlzIikgJT4lIGFzLm51bWVyaWMoKQpkaWZCT008LSBkaWZmdGltZShmZWNoYWhveSwgZmVjaGFCT00sIHVuaXRzPSJkYXlzIikgJT4lIGFzLm51bWVyaWMoKQpkaWZTRVg8LSBkaWZmdGltZShmZWNoYWhveSwgZmVjaGFTRVgsIHVuaXRzPSJkYXlzIikgJT4lIGFzLm51bWVyaWMoKQpkaWZCTElOPC0gZGlmZnRpbWUoZmVjaGFob3ksIGZlY2hhQkxJTiwgdW5pdHM9ImRheXMiKSAlPiUgYXMubnVtZXJpYygpCgojTWVkaWEgZGlhcmlhIGRlIHZpc2l0YXMgKGNvbiBsb3MgZMOtYXMgcXVlIGhhbiBwYXNhZG8gZGVzZGUgZmVjaGEgdmlkZW8gYSBob3kpCm1lZGlhUEFJPC0gbnVtUEFJIC8gZGlmUEFJCm1lZGlhSU5EQTwtIG51bUlOREFQQU4gLyBkaWZJTkRBUEFOCm1lZGlhTUVSQ0E8LSBudW1NRVJDQSAvIGRpZk1FUkNBCm1lZGlhRklFQlJFPC0gbnVtRklFQlJFIC9kaWZGSUVCUkUKbWVkaWFKQUNBIDwtIG51bUpBQ0EgL2RpZkpBQ0EKbWVkaWFOSUNFPC0gbnVtTklDRSAvZGlmTklDRQptZWRpYUNBTkQgPC0gbnVtQ0FORC8gZGlmQ0FORAptZWRpYUlOVDwtIG51bUlOVC9kaWZJTlQKbWVkaWFNQVM8LSBudW1NQVMvZGlmTUFTCm1lZGlhT1BFTjwtIG51bU9QRU4vZGlmT1BFTgptZWRpYUlVQUw8LSBudW1JVUFML2RpZklVQUwKbWVkaWFNQVJJPC0gbnVtTUFSSS9kaWZNQVJJCm1lZGlhSE9PS0FIPC0gbnVtSE9PS0FIL2RpZkhPT0tBSAptZWRpYVpPUlI8LSBudW1aT1JSL2RpZlpPUlIKbWVkaWFCT008LSBudW1CT00vZGlmQk9NCm1lZGlhU0VYPC0gbnVtU0VYL2RpZlNFWAptZWRpYUJMSU48LSBudW1CTElOL2RpZkJMSU4KCgojQ3JlYW1vcyBkYXRhZnJhbWVzCmZlY2hhPXNlcShmZWNoYVBBSSxsZW5ndGg9ZGlmUEFJLGJ5PSJkYXkiKQpkZjE8LSBkYXRhLmZyYW1lKGZlY2hhLG1lZGlhUEFJKQoKZmVjaGE9c2VxKGZlY2hhSU5EQVBBTixsZW5ndGg9ZGlmSU5EQVBBTixieT0iZGF5IikKZGYyPC0gZGF0YS5mcmFtZShmZWNoYSxtZWRpYUlOREEpCgpmZWNoYT1zZXEoZmVjaGFNRVJDQSxsZW5ndGg9ZGlmTUVSQ0EsYnk9ImRheSIpCmRmMzwtIGRhdGEuZnJhbWUoZmVjaGEsbWVkaWFNRVJDQSkKCmZlY2hhPXNlcShmZWNoYUZJRUJSRSxsZW5ndGg9ZGlmRklFQlJFLGJ5PSJkYXkiKQpkZjQ8LSBkYXRhLmZyYW1lKGZlY2hhLG1lZGlhRklFQlJFKQoKZmVjaGE9c2VxKGZlY2hhSkFDQSxsZW5ndGg9ZGlmSkFDQSxieT0iZGF5IikKZGY1PC0gZGF0YS5mcmFtZShmZWNoYSxtZWRpYUpBQ0EpCgpmZWNoYT1zZXEoZmVjaGFOSUNFLGxlbmd0aD1kaWZOSUNFLGJ5PSJkYXkiKQpkZjY8LSBkYXRhLmZyYW1lKGZlY2hhLG1lZGlhTklDRSkKCmZlY2hhPXNlcShmZWNoYUNBTkQsbGVuZ3RoPWRpZkNBTkQsYnk9ImRheSIpCmRmNzwtIGRhdGEuZnJhbWUoZmVjaGEsbWVkaWFDQU5EKQoKZmVjaGE9c2VxKGZlY2hhSU5ULGxlbmd0aD1kaWZJTlQsYnk9ImRheSIpCmRmODwtIGRhdGEuZnJhbWUoZmVjaGEsbWVkaWFJTlQpCgpmZWNoYT1zZXEoZmVjaGFNQVMsbGVuZ3RoPWRpZk1BUyxieT0iZGF5IikKZGY5PC0gZGF0YS5mcmFtZShmZWNoYSxtZWRpYU1BUykKCmZlY2hhPXNlcShmZWNoYU9QRU4sbGVuZ3RoPWRpZk9QRU4sYnk9ImRheSIpCmRmMTA8LSBkYXRhLmZyYW1lKGZlY2hhLG1lZGlhT1BFTikKCmZlY2hhPXNlcShmZWNoYUlVQUwsbGVuZ3RoPWRpZklVQUwsYnk9ImRheSIpCmRmMTE8LSBkYXRhLmZyYW1lKGZlY2hhLG1lZGlhSVVBTCkKCmZlY2hhPXNlcShmZWNoYU1BUkksbGVuZ3RoPWRpZk1BUkksYnk9ImRheSIpCmRmMTI8LSBkYXRhLmZyYW1lKGZlY2hhLG1lZGlhTUFSSSkKCmZlY2hhPXNlcShmZWNoYUhPT0tBSCxsZW5ndGg9ZGlmSE9PS0FILGJ5PSJkYXkiKQpkZjEzPC0gZGF0YS5mcmFtZShmZWNoYSxtZWRpYUhPT0tBSCkKCmZlY2hhPXNlcShmZWNoYVpPUlIsbGVuZ3RoPWRpZlpPUlIsYnk9ImRheSIpCmRmMTQ8LSBkYXRhLmZyYW1lKGZlY2hhLG1lZGlhWk9SUikKCmZlY2hhPXNlcShmZWNoYUJPTSxsZW5ndGg9ZGlmQk9NLGJ5PSJkYXkiKQpkZjE1PC0gZGF0YS5mcmFtZShmZWNoYSxtZWRpYUJPTSkKCmZlY2hhPXNlcShmZWNoYVNFWCxsZW5ndGg9ZGlmU0VYLGJ5PSJkYXkiKQpkZjE2PC0gZGF0YS5mcmFtZShmZWNoYSxtZWRpYVNFWCkKCmZlY2hhPXNlcShmZWNoYUJMSU4sbGVuZ3RoPWRpZkJMSU4sYnk9ImRheSIpCmRmMTc8LSBkYXRhLmZyYW1lKGZlY2hhLG1lZGlhQkxJTikKCiNVbmltb3MgZGF0YWZyYW1lcwpkZl92aXNpdGFzMTwtIGZ1bGxfam9pbihkZjEsZGYyKQpkZl92aXNpdGFzMjwtIGZ1bGxfam9pbihkZl92aXNpdGFzMSwgZGYzKQpkZl92MzwtIGZ1bGxfam9pbihkZl92aXNpdGFzMixkZjQpCmRmX3Y0PC0gZnVsbF9qb2luKGRmX3YzLGRmNSkKZGZfdjU8LSBmdWxsX2pvaW4oZGZfdjQsZGY2KQpkZl92NjwtIGZ1bGxfam9pbihkZl92NSxkZjcpCmRmX3Y3PC0gZnVsbF9qb2luKGRmX3Y2LGRmOCkKZGZfdjg8LSBmdWxsX2pvaW4oZGZfdjcsZGY5KQpkZl92OTwtIGZ1bGxfam9pbihkZl92OCxkZjEwKQpkZl92MTA8LSBmdWxsX2pvaW4oZGZfdjksZGYxMSkKZGZfdjExPC0gZnVsbF9qb2luKGRmX3YxMCxkZjEyKQpkZl92MTI8LSBmdWxsX2pvaW4oZGZfdjExLGRmMTMpCmRmX3YxMzwtIGZ1bGxfam9pbihkZl92MTIsZGYxNCkKZGZfdjE0PC0gZnVsbF9qb2luKGRmX3YxMyxkZjE1KQpkZl92MTU8LSBmdWxsX2pvaW4oZGZfdjE0LGRmMTYpCmRmX2NvbXBsZXRvPC0gZnVsbF9qb2luKGRmX3YxNSxkZjE3KQoKCiMtIExpbXBpYW1vcyBlbCBHbG9iYWwgRW52LgpvYmpldG9zX25vX2JvcnJhciA8LSBjKCJiZ19zdGF0c19hbWJvc19jYW5hbGVzMSIsImRmX2NvbXBsZXRvIiwiYmdfbWF5b3JfMSIpCnJtKGxpc3QgPSBscygpWyFscygpICVpbiUgb2JqZXRvc19ub19ib3JyYXJdKQpgYGAKCiMjIDxGT05UIENPTE9SPSJlYjg4Y2QiPlJhY2UgYmFyIGNoYXJ0PC9GT05UPgpBIHBhcnRpciBkZWwgZGF0YWZyYW1lIGZpbmFsIHF1ZSBoZSBjcmVhZG8gKCoqZGZfY29tcGxldG8qKiksIGhlIHF1ZXJpZG8gcmVhbGl6YXIgdW4gZ3LDoWZpY28gYW5pbWFkbyBkZSBjYXJyZXJhIGRlIGJhcnJhcyBiYXNhZG8gZW4gbGFzIHZpc3VhbGl6YWNpb25lcyBkaWFyaWFzIGRlIGNhZGEgdMOtdHVsby4gRGUgZXN0YSBmb3JtYSwgIGVsIGRhdGFmcmFtZSBhbnRlcmlvciwgZm9ybWFkbyBwb3IgbGEgZmVjaGEgZW4gZMOtYXMgc2VjdWVuY2lhbCBkZXNkZSAyMDE2IGEgbGEgYWN0dWFsaWRhZCB5IGxvcyB0w610dWxvcyBkZSBsb3MgdmlkZW9zLCB0ZW5kcsOhIHF1ZSBtb2RpZmljYXJzZSwgZGUgZm9ybWEgcXVlLCB0ZW5kcsOhIHF1ZSBwYXNhcnNlIGEgZm9ybWF0byAqbG9uZ2VyKiAsZGUgbWFuZXJhIHF1ZSwgbGFzIGRpZmVyZW50ZXMgIm1lZGlhcyIgcXVlIGFwYXJlY2VuIGVzdGFyw6FuIHRvZGFzIGFob3JhIGVuIHVuYSBjb2x1bW5hIGxsYW1hZGEgKip0w610dWxvKiogeSBsb3MgdmFsb3JlcyBkZSBlc3RhcyBlc3RhcsOhbiBlbiB1bmEgY29sdW1uYSBsbGFtYWRhICoqdmlzaXRhcyoqLiBBZGVtw6FzLCBtZWRpYW50ZSB1biBkcm9wX25hKCkgZWxpbWluYXJlbW9zIHRvZGFzIGFxdWVsbGFzIGZpbGFzIHF1ZSBubyB0dXZpZXNlbiBkYXRvcywgZGUgZm9ybWEgcXVlLCBzaSBwYXJhIHVuYSBmZWNoYSBkZXRlcm1pbmFkYSBzb2xvIGV4aXN0w61hIHVuIHTDrXR1bG8sIHlhIG5vIG5vcyBhcGFyZWNlcsOhbiBsb3MgZGVtw6FzIHTDrXR1bG9zIGNvbiBOQS4KCkEgY29udGludWFjacOzbiwgYWdydXBhcmVtb3MgcG9yIHTDrXR1bG8geSBjcmVhcmVtb3MgdW5hIG51ZXZhIHZhcmlhYmxlLCBxdWUgZXMgbGEgc3VtYSBhY3VtdWxhZGEgZGUgdmlzaXRhcywgZXMgZGVjaXIsIHNpIGEgMS9YLzIwIHNlIGNyZWEgZWwgdmlkZW8geSBzdSBtZWRpYSBkZSB2aXN1YWxpemFjaW9uZXMgZGlhcmlhcyBlcyBkZSAyMDAwLCBsYSBzdW1hIGFjdW11bGFkYSBlbiBlc2UgbW9tZW50byBzZXLDoSBkZSAyMDAwLiBBbCBkw61hIHNpZ3VpZW50ZSwgMi9YLzIwLCBzdSBtZWRpYSB2aXN1YWxpemFjaW9uZXMgc2Vyw6EgbGEgbWlzbWEgKDIwMDApLCBwZXJvLCBzdSBzdW1hIGFjdW11bGFkYSBzZXLDoSBkZSA0MDAwLCB5IGFzw60gc3VjZXNpdmFtZW50ZS4KClBvciBvdHJvIGxhZG8sIGFncnVwYXJlbW9zIHBvciBmZWNoYSB5IGEgcGFydGlyIGRlIGVzdGEgY3JlYXJlbW9zIHVuYSBudWV2YSB2YXJpYWJsZSByYW5raW5nIG1lZGlhbnRlIGxhIGZ1bmNpw7NuICpkZW5zZV9yYW5rKCkqICwgc2llbmRvIGxhIHZhcmlhYmxlIHBvciBsYSBjdWFsIHF1ZXJlbW9zIHF1ZSBoYWdhIHVuIHJhbmtpbmc6IGxhIHN1bWEgYWN1bXVsYWRhIGFudGVyaW9yLiBEZSBlc3RhIGZvcm1hLCBzaSBwb3IgZWplbXBsbyBhIDI1L1gvMjAgaGF5IDIgdmlkZW9zLCB5IHVubyB0aWVuZSBtYXlvciBhY3VtdWxhZGEgcXVlIGVsIG90cm8sIGVuIGxhIGNvbHVtbmEgcmFua2luZyBkZWwgcHJpbWVybyBkZSBlbGxvcyBhcGFyZWNlcsOhIGNvbW8gKioxKiogeSBlbCBzZWd1bmRvIGNvbiBtYXlvciBhY3VtdWxhZGEgY29tbyAqKjIqKi4KCkFzw60sIG1lZGlhbnRlIGVzdGUgZGF0YWZyYW1lIHlhIG1vZGlmaWNhZG8sIHBvZHJlbW9zIGNyZWFyIHVuIGdyw6FmaWNvIGFuaW1hZG8gZGUgY2FycmVyYSBkZSBiYXJyYXMsIHF1ZSBub3MgbXVlc3RyYSBsYSBldm9sdWNpw7NuIGRlIGxhcyB2aXNpdGEgZGlhcmlhcyAoY29uc2VndWlkYSBhIHRyYXbDqXMgZGUgbGEgbWVkaWEsIGNvbW8gaGVtb3MgdmlzdG8gZW4gZWwgcHVudG8gYW50ZXJpb3IpIGRlIGxvcyB0w610dWxvcyBkZXNkZSBzdSBmZWNoYSBkZSBjcmVhY2nDs24gaGFzdGEgbGEgYWN0dWFsaWRhZC4KCkxhIGRpZmljdWx0YWQgZGUgZXN0ZSBncsOhZmljbywgW2F1bnF1ZSBlc3TDoSBpbnNwaXJhZG8gZW4gdW5vIHlhIGV4aXN0ZW50ZV0oaHR0cHM6Ly9hbmRlcmZlcm5hbmRlei5jb20vYmxvZy9jb21vLWNyZWFyLWFuaW1hY2lvbmVzLWVuLXItY29uLWdnYW5pbWF0ZS8pLCBoYSBzaWRvIGxhIGNyZWFjacOzbiBkZXNkZSBjZXJvIGRlIHVuIGRhdGFmcmFtZSBjb21wbGVqbywgY3V5b3MgZGF0b3MgcHJpbmNpcGFsZXMgaGUgdGVuaWRvIHF1ZSBjYWxjdWxhcmxvcyBhIHBhcnRpciBkZSBsb3MgcG9jb3MgcXVlIHlhIHRlbsOtYSBncmFjaWFzIGFsIHBhcXVldGUgInR1YmVyIi4KCmBgYHtyLCBlY2hvPVRSVUUsIGV2YWw9VFJVRX0KCiNHUkFGSUNPIFBMT1QKCmRmX2NvbXBsZXRvMTwtZGZfY29tcGxldG8gJT4lIHJlbmFtZShQYWk9bWVkaWFQQUksIEluZGFwYW5kZW49bWVkaWFJTkRBLCBNZXJjYWRvbmE9bWVkaWFNRVJDQSwgRmllYnJlPW1lZGlhRklFQlJFLCBKYWNhcmFuZGE9bWVkaWFKQUNBLCBgTmljZXN0IENvY2t5YD1tZWRpYU5JQ0UsIENhbmRlbGE9bWVkaWFDQU5ELCBJbnRlcm5hdGlvbmFsbHk9bWVkaWFJTlQsIGBNw6FzIFJhcm9gPW1lZGlhTUFTLCBgT3BlbiBUaGUgRG9vcmA9bWVkaWFPUEVOLCBgWW8gU2lnbyBJdWFsYD1tZWRpYUlVQUwsIGBTYW50YSBNYXLDrWFgPW1lZGlhTUFSSSwgSG9va2FoPW1lZGlhSE9PS0FILCBab3JyYT1tZWRpYVpPUlIsIGBCb20gQm9tYD1tZWRpYUJPTSwgYEFwcmVuZGllbmRvIEVsIFNleG9gPW1lZGlhU0VYLCBgQmxpbiBCbGluYD1tZWRpYUJMSU4gKQoKI21vZGlmaWNhbW9zCmRmX3Bsb3QgPC0gZGZfY29tcGxldG8xICU+JSBwaXZvdF9sb25nZXIoY29scz0yOjE4LCBuYW1lc190bz0idGl0dWxvIix2YWx1ZXNfdG89InZpc2l0YXMiKSAlPiUgZHJvcF9uYSgpCmRmX3Bsb3QyIDwtIGRmX3Bsb3QgJT4lIGdyb3VwX2J5KHRpdHVsbyklPiUgbXV0YXRlKGFjdW11bGFkYT1jdW1zdW0odmlzaXRhcykpCmRmX3Bsb3QzIDwtIGRmX3Bsb3QyICU+JSBncm91cF9ieShmZWNoYSkgJT4lIG11dGF0ZShyYW5raW5nPSBkZW5zZV9yYW5rKGRlc2MoYWN1bXVsYWRhKSkpCgojUExPVAphbmltYWNpb24gPC0gZGZfcGxvdDMgJT4lCiAgZ2dwbG90KCkgKwogIGdlb21fY29sKGFlcyhyYW5raW5nLCBhY3VtdWxhZGEsIGZpbGwgPSB0aXR1bG8pKSArCiAgZ2VvbV90ZXh0KGFlcyhyYW5raW5nLCBhY3VtdWxhZGEsIGxhYmVsID0gYWN1bXVsYWRhKSwgaGp1c3Q9LTAuMSkgKwogIGdlb21fdGV4dChhZXMocmFua2luZywgeT0wICwgbGFiZWwgPSB0aXR1bG8pLCBoanVzdD0xLjEpICsKICBnZW9tX3RleHQoYWVzKHg9MTUsIHk9bWF4KGFjdW11bGFkYSkgLCBsYWJlbCA9IGFzLmZhY3RvcihmZWNoYSkpLCB2anVzdCA9IDAuMiwgYWxwaGEgPSAwLjUsICBjb2wgPSAiZ3JheSIsIHNpemUgPSAyMCkgKwogIGNvb3JkX2ZsaXAoY2xpcCA9ICJvZmYiLCBleHBhbmQgPSBGQUxTRSkgKyBzY2FsZV94X3JldmVyc2UoKSArCiAgdGhlbWVfbWluaW1hbCgpICsgdGhlbWUoCiAgICBwYW5lbC5ncmlkID0gZWxlbWVudF9ibGFuaygpLAogICAgbGVnZW5kLnBvc2l0aW9uID0gIm5vbmUiLAogICAgYXhpcy50aWNrcy55ID0gZWxlbWVudF9ibGFuaygpLAogICAgYXhpcy50aXRsZS55ID0gZWxlbWVudF9ibGFuaygpLAogICAgYXhpcy50ZXh0LnkgPSBlbGVtZW50X2JsYW5rKCksCiAgICBwbG90Lm1hcmdpbiA9IG1hcmdpbigxLCA0LCAxLCAzLCAiY20iKQogICkgKwogIHRyYW5zaXRpb25fc3RhdGVzKGZlY2hhLCBzdGF0ZV9sZW5ndGggPSAwLCB0cmFuc2l0aW9uX2xlbmd0aCA9IDIpICsKICBlbnRlcl9mYWRlKCkgKwogIGV4aXRfZmFkZSgpICsKICBlYXNlX2FlcygncXVhZHJhdGljLWluLW91dCcpCgoKCmBgYAoKYGBge3Igb3V0LndpZHRoPSI1MCUiLCBlY2hvPUZBTFNFLCBldmFsPUZBTFNFfQphbmltYXRlKGFuaW1hY2lvbiwgd2lkdGggPSA3MDAsIGhlaWdodCA9IDQzMiwgZnBzID0gMzAsIGR1cmF0aW9uID0gNzAsIGVuZF9wYXVzZSA9IDEwMCwgcmV3aW5kID0gRkFMU0UpCgpgYGAKCmBgYHtyIG91dC53aWR0aD0iODAlIiwgZmlnLmNhcD0iVmlzdWFsaXphY2lvbmVzIHBvciBkw61hIGRlIGNhZGEgdMOtdHVsbyIsIGVjaG89RkFMU0UsIGV2YWw9VFJVRX0Ka25pdHI6OmluY2x1ZGVfZ3JhcGhpY3MoIi4vaW1hZ2VuZXMvYW5pbTMuZ2lmIikKCmBgYAoKIyA8Rk9OVCBDT0xPUj0iZWI4OGNkIj4gKio0LiBPYnRlbmllbmRvIGRhdG9zIHNvYnJlIGxvcyBjb21lbnRhcmlvcyoqIDwvRk9OVD4KRW4gZXN0ZSBhcGFydGFkbywgbm9zIHZhbW9zIGEgYmFzYXIgZW4gbGEgb2J0ZW5jacOzbiBkZSBkYXRvcyBwcm92ZW5pZW50ZXMgZGUgbG9zIGNvbWVudGFyaW9zIGRlIGxvcyB2aWRlb3MsIHVzYW5kbyBwcmluY2lwYWxtZW50ZSBsYSBmdW5jacOzbiAqKmdldF9hbGxfY29tbWVudHMoKSoqIGRlbCBwYXF1ZXRlICJ0dWJlciIuIAoKIyMgPEZPTlQgQ09MT1I9ImViODhjZCI+TnViZSBkZSBjb21lbnRhcmlvczwvRk9OVCBDT0xPUj4KQXF1w60gbG8gcXVlIHF1ZXJlbW9zIG1vc3RyYXIgc29uIGxvcyBjb21lbnRhcmlvcyBxdWUgbcOhcyByZXBpdGUgbGEgZ2VudGUgYWNlcmNhIGRlIHVubyBkZSB1bm8gZGUgbG9zIMO6bHRpbW9zIHZpZGVvcyBkZSBCYWQgR3lhbDogKiJBcHJlbmRpZW5kbyBlbCBzZXhvIiouIFBhcmEgZWxsbywgbm9zIGRlc2NhcmdhcmVtb3MgdG9kb3MgbG9zIGNvbWVudGFyaW9zLCB5IG1lZGlhbnRlIGxhIGZ1bmNpw7NuICoqc3RyaV9leHRyYWN0X2FsbF93b3JkcygpKiogZGVsIHBhcXVldGUgKipzdHJpbmdpKiogZXh0cmFlcmVtb3MgdG9kYXMgbGFzIHBhbGFicmFzIHF1ZSBhcGFyZWNlbiBlbiBlbCBkYXRhZnJhbWUgZGUgY29tZW50YXJpb3MgZGVzY2FyZ2Fkb3MuIApQb3Igb3RybyBsYWRvLCB1c2Fyw6kgZWwgcGFxdWV0ZSAqKnRtKiosIGVsIGN1YWwgbm9zIHByb3BvcmNpb25hIHVuYSBzZXJpZSBkZSBwYWxhYnJhcyBjb211bmVzIHF1ZSBsdWVnbyB1dGlsaXphcsOpIHBhcmEgcG9kZXIgZWxpbWluYXIgeSBxdWUgbm8gc2FsZ2FuIGVuIGxhIHdvcmRjbG91ZC4gCgpBIHBhcnRpciBkZSBlc3RhcyBwYWxhYnJhcyBleHRyYWlkYXMsIGNyZWFtb3MgdW4gbnVldm8gZGF0YWZyYW1lIHkgZW4gZXN0ZSBoYXJlbW9zIHVuICpncm91cCogZGUgbGFzIHBhbGFicmFzIHkgdW5hIHZleiBhZ3J1cGFkYXMgY3JlYXJlbW9zIHVuYSBudWV2YSB2YXJpYWJsZSBjb24gbGEgZnVuY2nDs24gKipjb3VudCgpKiogcGFyYSB2ZXIgY3VhbnRhcyB2ZWNlcyBhcGFyZWNlbi4gQXPDrSwgcHVlcywgbWVkaWFudGUgbGFzIHBhbGFicmFzIGNvbXVuZXMgcXVlIHF1ZXJlbW9zIHF1aXRhciwgcHJvcG9yY2lvbmFkYXMgcG9yIGVsIHBhcXVldGUgKip0bSoqLCBoYXJlbW9zIHVuIGZpbHRyYWRvIHBhcmEgcG9kZXIgZWxpbWluYXJsYXMgZGUgbnVlc3RybyBkYXRhZnJhbWUgeSBhZGVtw6FzIGxhcyBvcmRlbmFyZW1vcyBkZSBtw6FzIGZyZWN1ZW50ZXMgYSBtZW5vcywgcGFyYSBhc8OtLCBmaW5hbG1lbnRlLCBjcmVhciBsYSAqKm51YmUgZGUgcGFsYWJyYXMqKi4KCmBgYHtyIGVjaG89VFJVRSwgZXZhbD1UUlVFLCBmaWcuYWxpZ249ImNlbnRlciJ9CiMgV09SRENMT1VECmNvbWVudGFyaW9zPC0gZ2V0X2FsbF9jb21tZW50cyh2aWRlb19pZD0gIk1CbTBpQ3VfRkJJIikKCmNvbWVudGFyaW9zX21vZGlmPC0gY29tZW50YXJpb3MgJT4lIHNlbGVjdCh0ZXh0T3JpZ2luYWwpCgpsaWJyYXJ5KHRpZHl0ZXh0KQpsaWJyYXJ5KHdvcmRjbG91ZDIpCmxpYnJhcnkoc3RyaW5naSkKbGlicmFyeSh0bSkKCmNvbWVudGFyaW9zX21vZGlmXzI8LSBjb21lbnRhcmlvc19tb2RpZiAlPiUgbXV0YXRlKHRleHRvPSBzdHJpX3RyYW5zX2dlbmVyYWwodG9sb3dlcih0ZXh0T3JpZ2luYWwpLCJMYXRpbi1BU0NJSSIpKSAlPiUgc2VsZWN0KHRleHRvKQoKcmVtb3ZlID0gc3RvcHdvcmRzKCJzcGFuaXNoIikKClBhbGFicmFzPC0gYyhzdHJpX2V4dHJhY3RfYWxsX3dvcmRzKGNvbWVudGFyaW9zX21vZGlmXzIkdGV4dG8pKQpkZjwtZGF0YS5mcmFtZShQYWxhYnJhcyA9IHVubGlzdChQYWxhYnJhcykpCgpkZl9wYWxhYnJhczwtIGRmICU+JSBncm91cF9ieShQYWxhYnJhcykgJT4lIGNvdW50KCkgJT4lIGFycmFuZ2UoZGVzYyhuKSkgJT4lIGZpbHRlcihuY2hhcihQYWxhYnJhcykgPj0zKSAlPiUgZmlsdGVyKG4+MTAgJiBQYWxhYnJhcyAlaW4lIHJlbW92ZT09RkFMU0UpCgojd29yZGNsb3VkMihkZl9wYWxhYnJhcywgY29sb3I9InJhbmRvbS1saWdodCIsIHNoYXBlPSJjaXJjbGUiKQoKIy0gUGFyYSBmaW5hbGl6YXIsIGxpbXBpYW1vcyBlbCBHbG9iYWwgRW52LgpvYmpldG9zX25vX2JvcnJhciA8LSBjKCJkZl9wYWxhYnJhcyIgLCAiY29tZW50YXJpb3MiKQpybShsaXN0ID0gbHMoKVshbHMoKSAlaW4lIG9iamV0b3Nfbm9fYm9ycmFyXSkKYGBgCgpgYGB7ciBlY2hvPUZBTFNFLCBldmFsPVRSVUUsIGZpZy5hbGlnbj0iY2VudGVyIn0Kd29yZGNsb3VkMihkZl9wYWxhYnJhcywgY29sb3I9InJhbmRvbS1saWdodCIsIHNoYXBlPSJjaXJjbGUiKQpgYGAKCgotLS0tLS0gCgojIyA8Rk9OVCBDT0xPUj0iZWI4OGNkIj5HcsOhZmljb3MgZGUgbMOtbmVhOiB2w61kZW9zIGRlIDIwMjAgPC9GT05UPiB7LnRhYnNldCAudGFic2V0LWZhZGV9CkVuIGVzdGUgYXBhcnRhZG8gbG8gcXVlIGhlIHF1ZXJpZG8gaGFjZXIgZXMgbW9zdHJhciBsYSBldm9sdWNpw7NuIGRlbCBuw7ptZXJvIGRlIGNvbWVudGFyaW9zIHBvciBmZWNoYSBkZSBsYXMgMyBjYW5jaW9uZXMgcXVlIGhhIHNhY2FkbyAqKkJhZCBHeWFsKiogZHVyYW50ZSBlbCAyMDIwOiAqQm9tIEJvbSAocmVtaXgpKiwgKkFwcmVuZGllbmRvIEVsIFNleG8qIHkgKkJsaW4gQmxpbiouClBhcmEgZWxsbywgaGUgZGVzY2FyZ2FkbyBsb3MgY29tZW50YXJpb3MgZGUgbG9zIDMgdmlkZW9zIChhdW5xdWUgcGFyYSBlbCBkZSBBUCBoZSB1dGlsaXphZG8gZWwgZGF0YWZyYW1lIGRlbCBzdWJwdW50byBhbnRlcmlvcikgeSBhIHBhcnRpciBkZSBsb3MgZGF0b3MgZGVzY2FyZ2Fkb3MgaGUgcGFzYWRvIGxhIGZlY2hhIGFsIGZvcm1hdG8gZXN0w6FuZGFyIGRlIGZlY2hhIGNvbiB1biBhcy5EYXRlKCkgeSBhZ3J1cGFkbyBwb3IgZXN0YSB2YXJpYWJsZShmZWNoYSkgcGFyYSBjb25zZWd1aXIgbG9zIGNvbWVudGFyaW9zIHB1YmxpY2Fkb3MgY2FkYSBkw61hLgoKQSBwYXJ0aXIgZGUgZXN0YXMgbW9kaWZpY2FjaW9uZXMgcmVhbGl6YWRhcyBlbiBsb3MgZGF0YWZyYW1lLCBsb3MgaGUganVudGFkbyB5IGhlIGhlY2hvIHVuIGdyw6FmaWNvIGRlIGzDrW5lYSBjb24gKiJmYWNldHRpbmciKiwgcGFyYSBtb3N0cmFyIGxvcyAzIHTDrXR1bG9zIHBvciBzZXBhcmFkby4gQSBzdSB2ZXosIHRhbWJpw6luIGhlIGhlY2hvIG90cm8gZ3LDoWZpY28gcGFyYSBtb3N0cmFyIGVsIG7Dum1lcm8gZGUgY29tZW50YXJpb3MgdG90YWxlcyBlbiBjYWRhIHTDrXR1bG8uCgpgYGB7ciwgZWNobz1UUlVFLCBldmFsPVRSVUV9CgojYXByZW5kaWVuZG8gZWwgc2V4bwpjb21lbnRhcmlvc19zZXg8LSBjb21lbnRhcmlvcyAlPiUgbXV0YXRlKGZlY2hhID0gYXMuRGF0ZShwdWJsaXNoZWRBdCkpICU+JSBzZWxlY3QodGV4dE9yaWdpbmFsLCBmZWNoYSkKCmNvbWVudGFyaW9zX3NleDI8LSBjb21lbnRhcmlvc19zZXggJT4lIGdyb3VwX2J5KGZlY2hhKSAlPiUgY291bnQoKSAlPiUgYXJyYW5nZShkZXNjKG4pKSAlPiUgcmVuYW1lKGBuw7ptZXJvIGRlIGNvbWVudGFyaW9zYD1uKQoKI2JsaW4KCmNvbWVudGFyaW9zYmxpbjwtIGdldF9hbGxfY29tbWVudHModmlkZW9faWQgPSAiY0ZYNFdSM2cta0EiKQoKY29tZW50YXJpb3NfYmxpbjI8LSBjb21lbnRhcmlvc2JsaW4gJT4lIG11dGF0ZShmZWNoYSA9IGFzLkRhdGUocHVibGlzaGVkQXQpKSAlPiUgc2VsZWN0KHRleHRPcmlnaW5hbCwgZmVjaGEpCgpjb21lbnRhcmlvc19ibGluMzwtIGNvbWVudGFyaW9zX2JsaW4yICU+JSBncm91cF9ieShmZWNoYSkgJT4lIGNvdW50KCkgJT4lIGFycmFuZ2UoZGVzYyhuKSkgJT4lIHJlbmFtZShgbsO6bWVybyBkZSBjb21lbnRhcmlvc2A9bikKCgojYm9tIGJvbQoKY29tZW50YXJpb3Nib208LSBnZXRfYWxsX2NvbW1lbnRzKHZpZGVvX2lkID0gInFPa1VSQ1Z0bHdVIikKCmNvbWVudGFyaW9zX3N0YXRzYm9tPC0gY29tZW50YXJpb3Nib20gJT4lIG11dGF0ZShmZWNoYSA9IGFzLkRhdGUocHVibGlzaGVkQXQpKSAlPiUgc2VsZWN0KHRleHRPcmlnaW5hbCwgZmVjaGEpCgpjb21lbnRhcmlvc19zdGF0c2JvbTI8LSBjb21lbnRhcmlvc19zdGF0c2JvbSAlPiUgZ3JvdXBfYnkoZmVjaGEpICU+JSBjb3VudCgpICU+JSBhcnJhbmdlKGRlc2MobikpICU+JSByZW5hbWUoYG7Dum1lcm8gZGUgY29tZW50YXJpb3NgPW4pCgoKI0HDsWFkaW1vcyBsYSB2YXJpYWJsZSB0w610dWxvCmNvbWVudGFyaW9zX3N0YXRzX2FzZXg8LSBjb21lbnRhcmlvc19zZXgyICU+JSBtdXRhdGUodGl0dWxvPSJBcHJlbmRpZW5kbyBFbCBTZXhvIikKY29tZW50YXJpb3Nfc3RhdHNfYmxpbjwtIGNvbWVudGFyaW9zX2JsaW4zICU+JSBtdXRhdGUodGl0dWxvPSJCbGluIEJsaW4iKQpjb21lbnRhcmlvc19zdGF0c19ib208LSBjb21lbnRhcmlvc19zdGF0c2JvbTIgJT4lIG11dGF0ZSh0aXR1bG89IlR1IEVyZXMgVW4gQm9tIEJvbSAocmVtaXgpIikKCiNMb3MganVudGFtb3MKY29tZW50YXJpb3NfanVudG9zPC0gZnVsbF9qb2luKGNvbWVudGFyaW9zX3N0YXRzX2JsaW4sIGNvbWVudGFyaW9zX3N0YXRzX2FzZXgpCmNvbWVudGFyaW9zX2p1bnRvc19mdWxsPC0gZnVsbF9qb2luKGNvbWVudGFyaW9zX2p1bnRvcywgY29tZW50YXJpb3Nfc3RhdHNfYm9tKQoKI01vZGlmaWNhbW9zIGVsIGRhdGFmcmFtZSBkZSBjb21lbnRhcmlvc19qdW50b3NfZnVsbCBwYXJhIG9idGVuZXIgZWwgbsO6bWVybyB0b3RhbCBkZSBjb21lbnRhcmlvcyBwb3IgdMOtdHVsbwpjb21lbnRhcmlvc19qdW50b3NfZnVsbF9tb2RpZjwtIGNvbWVudGFyaW9zX2p1bnRvc19mdWxsJT4lIGdyb3VwX2J5KHRpdHVsbyklPiVzdW1tYXJpc2UoYE7Dum1lcm8gdG90YWwgZGUgY29tZW50YXJpb3NgID0gc3VtKGBuw7ptZXJvIGRlIGNvbWVudGFyaW9zYCkpCgojLSBMaW1waWFtb3MgZWwgR2xvYmFsIEVudi4Kb2JqZXRvc19ub19ib3JyYXIgPC0gYygiY29tZW50YXJpb3NfanVudG9zX2Z1bGxfbW9kaWYiLCAiY29tZW50YXJpb3NfanVudG9zX2Z1bGwiKQpybShsaXN0ID0gbHMoKVshbHMoKSAlaW4lIG9iamV0b3Nfbm9fYm9ycmFyXSkKCiNQbG90cwpwbG90X3RvdGFsX2NvbWVudDwtIGdncGxvdChjb21lbnRhcmlvc19qdW50b3NfZnVsbF9tb2RpZiwgYWVzKHg9dGl0dWxvLCB5PWBOw7ptZXJvIHRvdGFsIGRlIGNvbWVudGFyaW9zYCwgZmlsbD10aXR1bG8pKSArIGdlb21fYmFyKHN0YXQ9ImlkZW50aXR5IikgKyBjb29yZF9mbGlwKCkgKyB0aGVtZShsZWdlbmQucG9zaXRpb249Im5vbmUiKSArIHRoZW1lX2J3KCkKCnBsb3RfbGluZWFzPC0gZ2dwbG90KGNvbWVudGFyaW9zX2p1bnRvc19mdWxsLCBhZXMoeD1mZWNoYSwgeT1gbsO6bWVybyBkZSBjb21lbnRhcmlvc2AsY29sb3I9dGl0dWxvKSkrZ2VvbV9saW5lKCkrIGZhY2V0X3dyYXAodmFycyh0aXR1bG8pLCBucm93PTIsIG5jb2w9MikgKyB0aGVtZShsZWdlbmQucG9zaXRpb24gPSAibm9uZSIpICsgdGhlbWVfYncoKQoKCmBgYAoKIyMjIEdyYWZpY28gbMOtbmVhcwpgYGB7ciwgZWNobz1GQUxTRSwgZXZhbD1UUlVFLCBvdXQud2lkdGg9IjgwJSJ9CnBsb3RfbGluZWFzCmBgYAoKIyMjIEdyw6FmaWNvIGNvbWVudGFyaW9zCmBgYHtyLCBlY2hvPUZBTFNFLCBldmFsPVRSVUUsIG91dC53aWR0aD0iNjAlIn0KcGxvdF90b3RhbF9jb21lbnQKYGBgCgo8L2Rpdj4KCiMgPEZPTlQgQ09MT1I9ImViODhjZCI+Kio1LiBEYXRvcyBzb2JyZSBwb3B1bGFyaWRhZDogbWFwYSoqPC9GT05UPgpNZWRpYW50ZSBlbCBwYXF1ZXRlICoqZ3RyZW5kc1IqKiBoZSBxdWVyaWRvIG1vc3RyYXIgbGEgcG9wdWxhcmlkYWQtdGVuZGVuY2lhIGVuIGVsIGJ1c2NhZG9yIGRlICpHb29nbGUqIGRlICoqQmFkIEd5YWwqKi4gUGFyYSBlc3RlIGFwYXJ0YWRvIG1lIGhlIGJhc2FkbyBlbiBlbCBSU2NyaXB0IGRlIGxhIHNlc2nDs24gMTE6IGVqX2d0cmVuZHNfTlYuIApFbCBwYXF1ZXRlIGd0cmVuZHNSIG1lZGlhbnRlIGxhIGZ1bmNpw7NuIGd0cmVuZHMoKSB0ZSBkZXZ1ZWx2ZSB1bmEgbGlzdGEgY29uIGRpZmVyZW50ZXMgcmVzdWx0YWRvcywgZGVwZW5kaWVuZG8gZGUgbGFzIHZhcmlhYmxlcyBxdWUgbWV0YXMgZGVudHJvIGRlIGxhIGZ1bmNpw7NuIChlc3RhcyBzb246IGxhIHBhbGFicmEgbyBwYWxhYnJhcyBxdWUgcXVpZXJlcyBxdWUgYnVzcXVlIHkgYW5hbGl6ZSBkZSBHb29nbGUsIGxhIHpvbmEgbyBwYcOtcyBhIGFuYWxpemFyLCBsYSBmcmFuamEgdGVtcG9yYWwuLi4pLiAKCk1lZGlhbnRlIGVsIGRhdGFmcmFtZSBvYnRlbmlkbywgbm9zIHF1ZWRhcmVtb3MgY29uIGxhcyB2YXJpYWJsZXMgcmVmZXJlbnRlcyBhIGxhcyBDQ0FBIGVuIGxhcyBxdWUgZGVzdGFjYSBsYSBidXNxdWVkYSBkZSAqKkJhZCBHeWFsKiogeSBhc8OtLCBjb24gZXN0YXMgeSBsYXMgZ2VvbWV0csOtYXMgbW9zdHJhcmVtb3MgZW4gdW4gbWFwYSBjb24gY29sb3IsIGxhcyBjb211bmlkYWRlcyBhdXTDs25vbWFzIGRvbmRlICoqQmFkIEd5YWwqKiBsZXZhbnRhIG3DoXMgaW50ZXLDqXMgKGRvbmRlLCB2YWxvciAxMDAgcmVwcmVzZW50YSBlbCBtw6F4aW1vIGludGVyw6lzLWhpdHMuKS4KCmBgYHtyIGVjaG89VFJVRSwgZXZhbD1UUlVFfQpsaWJyYXJ5KGd0cmVuZHNSKQpsaWJyYXJ5KHNmKQpsaWJyYXJ5KGdncmVwZWwpCmxpYnJhcnkoZ2dzcGF0aWFsKQoKI1NlbGVjY2lvbmFtb3MgbG9zIHF1ZSBxdWVyZW1vcyBxdWUgbm9zIGJ1c3F1ZSB5IGxvcyB0w6lybWlub3MuCnNlYXJjaF90ZXJtcyA8LSBjKCJCYWQgR3lhbCIpCm91dHB1dF9yZXN1bHRzIDwtIGd0cmVuZHMoa2V5d29yZCA9IHNlYXJjaF90ZXJtcywgZ2VvID0gYygiRVMiKSwgdGltZSA9ICJ0b2RheSAxMi1tIikgCgojLSBEZSBsYSBsaXN0YSBxdWUgbm9zIGRldnVlbHZlIGVsIG91dHB1dF9yZXN1bHRzIHNlbGVjY2lvbmFtb3MgZWwgc2xvdCBuwrozOiBpbnRlcsOpcyBwb3IgcmVnacOzbi4KQkdfaW50ZXJlcyA8LSBvdXRwdXRfcmVzdWx0c1tbM11dCgojTW9kaWZpY2Ftb3MgbnVlc3RybyBkYXRhZnJhbWUgcGFyYSBxdWUgY29pbmNpZGEgY29uIGxvcyBub21icmVzIGRlIGxhcyBnZW9tZXRyw61hcyBxdWUgdmFtb3MgYSBjYXJnYXIKQkdfaW50ZXJlc18yPC0gQkdfaW50ZXJlcyAlPiUgc2VsZWN0KGxvY2F0aW9uLCBoaXRzKSAlPiUgcGl2b3Rfd2lkZXIobmFtZXNfZnJvbT0gbG9jYXRpb24sIHZhbHVlc19mcm9tPWhpdHMpCgpCR19pbnRlcmVzXzMgPC0gQkdfaW50ZXJlc18yICU+JSByZW5hbWUoIkNhdGFsdcOxYSIgPSAiQ2F0YWxvbmlhIiwgIkNvbXVuaWRhZCBGb3JhbCBkZSBOYXZhcnJhIj0iTmF2YXJyZSIsICJDYXN0aWxsYSB5IExlw7NuIj0iQ2FzdGlsZSBhbmQgTGXDs24iLCAiUGHDrXMgVmFzY28iPSJCYXNxdWUgQ291bnRyeSIsICJDb211bmlkYWQgVmFsZW5jaWFuYSI9IlZhbGVuY2lhbiBDb21tdW5pdHkiLCAiQ29tdW5pZGFkIGRlIE1hZHJpZCI9IkNvbW11bml0eSBvZiBNYWRyaWQiLCAiUHJpbmNpcGFkbyBkZSBBc3R1cmlhcyI9IkFzdHVyaWFzIiwgIkNhc3RpbGxhLUxhIE1hbmNoYSI9IkNhc3RpbGUtTGEgTWFuY2hhIiwgIkFuZGFsdWPDrWEiPSJBbmRhbHVzaWEiLCAiQXJhZ8OzbiI9IkFyYWdvbiIsICJJbGxlcyBCYWxlYXJzIj0iQmFsZWFyaWMgSXNsYW5kcyIsICJSZWdpw7NuIGRlIE11cmNpYSI9IlJlZ2lvbiBvZiBNdXJjaWEiLCJDYW5hcmlhcyI9IkNhbmFyeSBJc2xhbmRzIikKCkJHX2ludGVyZXNfMzwtIEJHX2ludGVyZXNfMyAlPiUgcGl2b3RfbG9uZ2VyKDE6MTksIG5hbWVzX3RvPSJDQ0FBIiwgdmFsdWVzX3RvPSJoaXRzIikgCgpCR19pbnRlcmVzXzM8LSBCR19pbnRlcmVzXzMgJT4lIGRyb3BfbmEoKQoKCgojIENDQUEKI0NhcmdhbW9zCmxvYWQoIi4vZGF0b3MvZ2VvbWV0cmlhX2NjYWEuUkRhdGEiKQoKI0FycmVnbGFtb3MgZWwgZGF0YWZyYW1lCkNDQUFfMjwtIENDQUEgJT4lIHNsaWNlKGMoMToxNykpCkNDQUFfMiA8LSBDQ0FBXzIgJT4lIHNlbGVjdChOb21icmVDQ0FBLCBnZW9tZXRyeSkKcm1hcHNoYXBlcjo6bXNfc2ltcGxpZnkoQ0NBQV8yJGdlb21ldHJ5KQoKZGZfc3RhdHNfbWFwYTwtIGZ1bGxfam9pbihDQ0FBXzIsIEJHX2ludGVyZXNfMywgYnk9YygiTm9tYnJlQ0NBQSIgPSAiQ0NBQSIpKQoKCiNTaW1wbGlmaWNhbW9zIGxhcyBnZW9tZXRyw61hcyBwYXJhIHF1ZSBlbCB0aWVtcG8gZGUgY2FyZ2Egc2VhIG1lbm9yCmRmX3N0YXRzX21hcGEyPC0gZGZfc3RhdHNfbWFwYSAlPiUgcm1hcHNoYXBlcjo6bXNfc2ltcGxpZnkoKQoKCiNQbG90Ck1BUEExPC0gZ2dwbG90KGRmX3N0YXRzX21hcGEyLCBhZXMoZ2VvbWV0cnkgPSBnZW9tZXRyeSkpICsgZ2VvbV9zZihhZXMoZmlsbD0gaGl0cykpICArIHNjYWxlX2ZpbGxfdmlyaWRpc19jKG9wdGlvbiA9ICJpbmZlcm5vIiwgdHJhbnMgPSAic3FydCIsIGRpcmVjdGlvbiA9IC0xLCBhbHBoYT0gLjYpICsgY29vcmRfc2YoeGxpbSA9IGMoLTIwLDEwKSkgKyBsYWJzICh0aXRsZSA9ICJNYXBhIDE6IERpc3RyaWJ1Y2nDs24gZGUgYnVzcXVlZGFzIGRlIEJhZCBHeWFsIiwKICAgICAgIHN1YnRpdGxlID0gIlBvciBDQ0FBIiwKICAgICAgIGNhcHRpb24gPSAiRGF0b3MgcHJvdmVuaWVudGVzIGRlbCBwYXF1ZXRlIGd0cmVuZHNSIikgKyB0aGVtZV9taW5pbWFsKCkKCmBgYAoKYGBge3IgZWNobz1GQUxTRSwgZXZhbD1UUlVFLCBvdXQud2lkdGg9IjkwJSJ9Ck1BUEExCmBgYAoKCiMgPEZPTlQgQ09MT1I9ImViODhjZCI+Kio2LiBUcmFiYWpvcyBlbiBsb3MgcXVlIG1lIGhlIGJhc2FkbyoqPC9GT05UPgoKTG9zIHRyYWJham9zIGVuIGxvcyBxdWUgbWUgaGUgaWRvIGluc3BpcmFuZG8gcGFyYSBoYWNlciBlbCBtw61vIGhhbiBzaWRvIHZhcmlvczoKCiogW1RyYW5zZm9ybWFjaW9uZXMgY29uIGVsIHBhcXVldGUgdHViZXJdKGh0dHBzOi8vd3d3LnItYmxvZ2dlcnMuY29tLzIwMTcvMTIvdXNpbmctdGhlLXR1YmVyLXBhY2thZ2UtdG8tYW5hbHlzZS1hLXlvdXR1YmUtY2hhbm5lbC8pCiogW0NvbnN0cnVjY2nDs24gZGUgZ3LDoWZpY28gZGUgY2FycmVyYXMgZGUgYmFycmFzXShodHRwczovL2FuZGVyZmVybmFuZGV6LmNvbS9ibG9nL2NvbW8tY3JlYXItYW5pbWFjaW9uZXMtZW4tci1jb24tZ2dhbmltYXRlLykKKiBbV29yZGNsb3VkXShodHRwczovL3JzdHVkaW8tcHVicy1zdGF0aWMuczMuYW1hem9uYXdzLmNvbS80NjA1NjNfNDQ0MDNhM2IwODdhNDE1Nzk3ZGU3NWJhM2IyMWQxN2IuaHRtbCkKCi0tLS0tLS0tLS0tIAoKIyA8Rk9OVCBDT0xPUj0iZWI4OGNkIj4qKjcuIEJpYmxpb2dyYWbDrWEqKjwvRk9OVD4KCkEgcGFydGUgZGUgbG9zIHlhIGNpdGFkb3MgZW4gbG9zIHRyYWJham9zIGVuIGxvcyBxdWUgbWUgaGUgaW5zcGlyYWRvLCBoZSBpZG8gc2FjYW5kbyBpbmZvcm1hY2nDs24gZSBpZGVhcyBlbiBkaWZlcmVudGVzIHDDoWdpbmFzOgoKKiBbQXB1bnRlcy10dXRvcmlhbGVzOiBEYXRhIG11bmdpbmddKGh0dHBzOi8vcGVyZXpwNDQuZ2l0aHViLmlvL2ludHJvLWRzLTIwLTIxLXdlYi90dXRvcmlhbGVzL3R0XzA1X2RhdGEtbXVuZ2luZy5odG1sKQoqIFtBcHVudGVzLXR1dG9yaWFsZXM6IEdHUExPVF0oaHR0cHM6Ly9wZXJlenA0NC5naXRodWIuaW8vaW50cm8tZHMtMjAtMjEtd2ViL3R1dG9yaWFsZXMvdHRfMDZfZ2dwbG90Mi5odG1sKQoqIFtDdXN0b21pemFuZG8gdGFibGFzIGNvbiBHVF0oaHR0cHM6Ly93d3cuYWxsaXNvbmhvcnN0LmNvbS9wb3N0LzIwMjAtMDMtMDItZ3QtdGFibGVzLWV4YW1wbGVzLykKKiBbSW1hZ2VuIGRlIGZvbmRvXShodHRwczovL3JwdWJzLmNvbS90aGF1ZmFzLzU1NTE1NykKKiBbQ3JlYWNpw7NuIGRlIGRhdGFmcmFtZSAxXShodHRwczovL3JwdWJzLmNvbS9DZXNhcl9BSE4vZGF0YV9mcmFtZV9jcmVhcl9zZWxlY2Npb25fZGVfZWxlbWVudG9zX3ZhcmlhYmxlc19jb2x1bW5hc19maWxhc19hZ3JlZ2FyKQoqIFtDcmVhY2nDs24gZGUgZGF0YWZyYW1lIDJdKGh0dHBzOi8vd3d3LmMtc2hhcnBjb3JuZXIuY29tL2FydGljbGUvci1kYXRhLWZyYW1lLW9wZXJhdGlvbnMtYWRkaW5nLXJvd3MtcmVtb3Zpbmctcm93cy1hbmQtbWVyZ2luZy10d28tZGF0YS1mcmFtZS8pCiogW0NyZWFjacOzbiBkZSBkYXRhZnJhbWUgM10oaHR0cHM6Ly9zdGFja292ZXJmbG93LmNvbS9xdWVzdGlvbnMvNTA5MjAxNTMvZWxlZ2FudC13YXktb2YtYWRkaW5nLWNvbHVtbnMtb24tYS1zcGVjaWZpYy1wb3NpdGlvbi1pbi1hLWRhdGEtZnJhbWUpCiogW1RyYWJhamFuZG8gY29uIGZlY2hhc10oaHR0cHM6Ly9lc3RhZGlzdGljYW1lbnRlLmNvbS90cmFiYWphbmRvLWNvbi1mZWNoYXMtZW4tci8pCiogW0FuYWxpemFuZG8gZGF0b3MgeSBhYnJpZW5kbyB0dWJlcl0oaHR0cHM6Ly9ycHVicy5jb20vR01penVuby90dWJlUikKKiBbQ29naWVuZG8gZGF0b3MgZGUgVHViZXJdKGh0dHBzOi8vd3d3LnJwdWJzLmNvbS9zdGF0c2NvbC95b3V0dWJlX2RhdGFfaW5fcikKKiBbQcOxYWRpZW5kbyBjb2x1bW5hIHJhbmtpbmddKGh0dHBzOi8vc3RhY2tvdmVyZmxvdy5jb20vcXVlc3Rpb25zLzI0OTM4MTcyL2FkZGluZy1hLXJhbmtpbmctY29sdW1uLXRvLWEtZGF0YWZyYW1lKQoqIFtHcsOhZmljbyBkZSB0YXJ0YV0oaHR0cHM6Ly93d3cuci1ncmFwaC1nYWxsZXJ5LmNvbS9waWVjaGFydC1nZ3Bsb3QyLmh0bWwpCiogW03DoXMgc29icmUgd29yZGNsb3VkXShodHRwczovL2FuYWxpc2lzeWRlY2lzaW9uLmVzL2Nsb3VkLXdvcmRzLWNvbi1yLXRyYWJhamFyLWNvbi1sYS1hcGktZGVsLWV1cm9wZS1wbWMtY29uLXIvKQoqIFtFamVtcGxvcyByZWFjdGFibGVdKGh0dHBzOi8vZ2xpbi5naXRodWIuaW8vcmVhY3RhYmxlL2FydGljbGVzL2V4YW1wbGVzLmh0bWwpCiogR2VvbWV0csOtYXMgZGUgY2xhc2UKKiBSU2NyaXB0IGNsYXNlIDExLCBndHJlbmRzUgoKLS0tLS0tLS0tLS0tLS0tCgo8Y2VudGVyPjxGT05UIENPTE9SPSJlYjg4Y2QiPgo8Zm9udCBzaXplPSIxMCI+wqFFc3Blcm8gcXVlIGhheWFpcyBhcHJlbmRpZG8gdW4gcG9xdWl0byBtw6FzITwvRk9OVD48L2ZvbnQ+PC9jZW50ZXI+Cgo8Y2VudGVyPgohW10oaW1hZ2VuZXMvYmFkZ3lhbGdpZi5naWYpe3dpZHRoPSIzNSUifTwvY2VudGVyPgoKCi0tLS0tLS0tLS0tLS0tLS0KCjxicj48YnI+CgpDaHVuayBwYXJhIGluY2x1aXIgbGEgYHNlc3Npb24gaW5mb2A6CmBgYHtyfQpzZXNzaW9uaW5mbzo6c2Vzc2lvbl9pbmZvKCkgJT4lIGRldGFpbHM6OmRldGFpbHMoc3VtbWFyeSA9ICdjdXJyZW50IHNlc3Npb24gaW5mbycpIApgYGAKCi0tLS0tLS0tLS0tLS0tLS0KClteMV06IFF1w6kgZXMgT0F1dGg6IGh0dHBzOi8vZXMud2lraXBlZGlhLm9yZy93aWtpL09BdXRoCg==