Analisis sobre el timeline

1- Completamos la autorización para twitter

Creamos un token para entrar a twitter y usar la API, y las librerias Rtweet y TwitteR

twitter_token <- create_token(
  app = appname,
  consumer_key = consumer_key,
  consumer_secret = consumer_secret,
  access_token = access_token, 
  access_secret = access_secret)

2- Descargamos los tweets

Descargamos los tweets de cualquier usuario con la funcion “get_timeline” de rtweet User = @ del usuario n= Cantidad de tweets a bajar includeRts = F para eliminar los posibles RT de la cuenta excludeReplies = F vamos a descargar la sección “Tweets y respuestas”

Tweets_BocaJrs <- get_timeline(user = "BocaJrsOficial", n = 3200, includeRts = F, excludeReplies = F)
Tweets_RiverPlate <- get_timeline(user = "RiverPlate", n = 3200, includeRts = F, excludeReplies = F)
Tweets_SanLorenzo <- get_timeline(user = "SanLorenzo", n = 3200, includeRts = F, excludeReplies = F)
Tweets_Racing <- get_timeline(user = "RacingClub", n = 3200, includeRts = F, excludeReplies = F)
Tweets_Independiente <- get_timeline(user = "Independiente", n = 3200, includeRts = F, excludeReplies = F)

3- Unimos los dataframes

Vamos a usar un solo Dataframe que llamaremos Tweets_Club y lo vamos a hacer con la funcion rbind de R base

El nuevo Dataframe va a contar con 3200*5 filas = 16000 y contiene 3200 tweets de cada una de las cuentas

Tweets_Club <- rbind(Tweets_BocaJrs, Tweets_Racing, Tweets_SanLorenzo, Tweets_RiverPlate, Tweets_Independiente)

4- Comenzamos el proceso de limpieza

Vamos a crear algunas columnas extras para obtener mas información y también vamos a filtrar datos, para quedarnos solo con los que nos interesa

Tweets_Club <- Tweets_Club%>%
  tbl_df %>%
  mutate(text = gsub("[^[:graph:]]", " ", text)) %>% #Sin graficos
  mutate(text = tolower(text)) %>% #Todo el texto a minuscula
  mutate(created_at = with_tz(created_at, "America/Argentina/Buenos_Aires"))%>% #Cambiamos a la zona horaria correspondiente
  separate(created_at, into = c("date", "hour"), sep = " ")%>% #Separamos en dia y hora el campo created_at
    separate(hour, into = c("hour", "minutes","seconds"), sep = ":")%>% #Separamos la hora en hora,minutos y segundos
   rename(Club = screen_name) %>% #Cambiamos la columna con el nombre del club
mutate(periodo = year(date), 
         mes = month(date, label = T, abbr = F),
         dia = as.numeric(day(date)),
         dia_sem = wday(date, label = T, abbr = F, week_start = 1),
         dia_per = yday(date),
         date = as.Date(date) #Creamos una columna con el numero de año, mes, dia, nombre de dia y de mes.
  )%>%
  filter(periodo == 2020) #Solo vamos a utilizar info  de 2020

El nuevo DataFrame sera de 9660 observaciones

5- Creamos ciertos temas graficos

Para tener una mejor visualizacion, precargamos algunos temas y le damos el color a cada club

tema_plot <-
  theme(text = element_text(family = "sans", size = 10),
        panel.border = element_rect(color = "#cccccc", fill = NA),
        panel.background = element_rect(fill = "white"),
        panel.grid.major =  element_line(color = "#dddddd"),
        panel.grid.minor =  element_line(color = "#eeeeee"),
        axis.ticks = element_line(colour = "#cccccc"),
        strip.background = element_rect(color = "#cccccc", fill = "#eeeeee"),
        legend.position = "top")

tema_graf <- theme_minimal() +
  theme(text = element_text(family = "serif"),
        panel.grid.minor = element_blank(),
        strip.background = element_rect(fill = "#EBEBEB", colour = NA),
        legend.position = "none",
        legend.box.background = element_rect(fill = "#EBEBEB", colour = NA))

colours <- c(
  "#1746f8", # BocaJrsOficial
  "#cd0000", # Independiente
  "#13e4fc", # RacingClub
  "#9b5b53", # River Plate
  "#001256"  # SanLorenzo
)

6- Cantidad de tweets en el año

Vemos que el club que mas twitteo en lo que va del año es Racing con casi 2500, parecido a lo que hizo San Lorenzo y también a River.

Boca e Independiente estan en un nivel más bajo, cerca de los 1500 tweets en lo que va del año


Tweets_Club %>% 
  group_by(Club)%>%
  count(Club) %>%
ggplot()+
  aes(x=reorder(Club, n), y= n, fill= Club) +
  geom_col() +
    coord_flip() +
    labs(title = "Cantidad Tweets", x = "Club", Y = "Cantidad") +
   scale_fill_manual(values= colours) +
    tema_graf

7- Cantidad de tweets por mes

Tweets_Club %>% 
Warning message:
In readChar(file, size, TRUE) : truncating string with embedded nuls
  group_by(Club, mes)%>%
  count(Club) %>%
ggplot()+
  aes(x=reorder(Club, n), y= n, fill= Club) +
  geom_col() +
    coord_flip() +
    labs(title = "Cantidad Tweets", x = "Club", Y = "Cantidad") +
  facet_wrap(~mes, scales = "free", nrow = 3)+
   scale_fill_manual(values= colours) +
    tema_graf

8- Tweets por hora

Vemos como es la distribución de los tweets por horario.

Boca es el club con mayor actividad a la medianoche, a la madrugada la actividad es casi nula y comienza nuevamente a las 8am.

Tambíen podemos notar que la mayor diferencia en los tweets se dan alrededor del mediodia y a media tarde, volviendo a twittear de manera parecida durante la noche.

Tweets_Club %>% 
  group_by(Club, hour)%>%
  summarise(n = n()) %>%
ggplot (aes(x= hour, y = n, group= Club, color = Club)) +
  geom_line()+
    labs(title = "Tweets por hora", x = "Hora", y = "Cantidad") +
  tema_plot
`summarise()` regrouping output by 'Club' (override with `.groups` argument)

9- Cantidad de interacciones

Creamos un pequeño DF para sumarizar todas las iteracciones que tienen los distintos clubes, y le agregamos su cantidad de followers (dividido 1000)

Interacciones = Tweets_Club %>% 
  group_by(Club)%>%
  summarise(PromedioRT = mean(retweet_count), 
            TotalRT =  sum(retweet_count),
            PromedioFAV = mean(favorite_count), 
            TotalFAV =  sum(favorite_count),
            TotalInteracciones = TotalFAV + TotalRT,
            PromedioInteracciones = PromedioRT + PromedioFAV
  ) %>%
    mutate(followers = ifelse (Club == "BocaJrsOficial", 3800,
                               ifelse (Club == "Independiente", 454.4,
                                      ifelse (Club == "RacingClub", 330.5,
                                              ifelse (Club == "RiverPlate", 3400,
                                                      753.4
                                                      )
                                              )
                               )
    )
  )
`summarise()` ungrouping output (override with `.groups` argument)
print(Interacciones)

10- Retweets a la cuenta

Medimos los RT que tuvo cada uno de los clubes, tanto en cantidad total que va de la mano de las publicaciones, así como tambien el promedio.

Se nota una clara diferencia entre River y Boca con los otros 3 clubes a la hora de que sus tweets sean retwitteados.

Interacciones %>% 
Warning messages:
1: In readChar(file, size, TRUE) : truncating string with embedded nuls
2: In readChar(file, size, TRUE) : truncating string with embedded nuls
3: In readChar(file, size, TRUE) : truncating string with embedded nuls
4: In readChar(file, size, TRUE) : truncating string with embedded nuls
5: In readChar(file, size, TRUE) : truncating string with embedded nuls
6: In readChar(file, size, TRUE) : truncating string with embedded nuls
ggplot()+
  aes(x=reorder(Club, TotalRT), y= TotalRT, fill= Club) +
  geom_col() +
    coord_flip() +
    labs(title = "Cantidad total de RT", x = "Club", y = "Cantidad") +
   scale_fill_manual(values= colours) +
    tema_graf


Interacciones %>% 
ggplot()+
  aes(x=reorder(Club, PromedioRT), y= PromedioRT, fill= Club) +
  geom_col() +
    coord_flip() +
    labs(title = "Cantidad promedio de RT", x = "Club", y = "Cantidad") +
   scale_fill_manual(values= colours) +
    tema_plot

11- Favoritos a la cuenta

Medimos los FAV que tuvo cada uno de los clubes, tanto en cantidad total que va de la mano de las publicaciones, así como tambien el promedio.

Se nota una clara diferencia entre River, que ademas se asentua contra Boca, mientras que los otros clubes están muy por debajo en cuanto a FAVs recibidos

Interacciones %>% 
Warning messages:
1: In readChar(file, size, TRUE) : truncating string with embedded nuls
2: In readChar(file, size, TRUE) : truncating string with embedded nuls
3: In readChar(file, size, TRUE) : truncating string with embedded nuls
4: In readChar(file, size, TRUE) : truncating string with embedded nuls
5: In readChar(file, size, TRUE) : truncating string with embedded nuls
ggplot()+
  aes(x=reorder(Club, -TotalFAV), y= TotalFAV, fill= Club) +
  geom_col() +
  labs(title = "Cantidad total de FAV", x = "Club", y = "Cantidad") +
   scale_fill_manual(values= colours) +
    tema_graf


Interacciones %>% 
ggplot()+
  aes(x=reorder(Club, -PromedioFAV), y= PromedioFAV, fill= Club) +
  geom_col() +
    labs(title = "Cantidad promedio de FAV", x = "Club", y = "Cantidad") +
   scale_fill_manual(values= colours) +
    tema_plot

12- Cantidad promedio de interacciones

Sumamos lo visto anteriormente, tomando un solo campo “Interaccion” como la suma de los promedios de FAVs y de RTs, las diferencias que se notan son las mismas que en los anteriores gráficos.

13- Cantidad promedio de interacciones

Normalizamos la cantidad de interacciones que tuvieron por cada 1000 seguidores, ahí se nota que Racing e Independiente si bien sus interacciones son menor, se debe a la cantidad de followers que tiene cada uno, cuando normalizamos quedan en un primer nivel junto a River.

Mientras que Boca no logra llegar a 1 interacción por cada mil followers, estando en un segundo pelotón, mientras que San Lorenzo es el que menos interacciones recibe con su gente.

Interacciones %>% 
ggplot()+
  aes(x=reorder(Club, (PromedioInteracciones/followers)), y= (PromedioInteracciones/followers), color= Club) +
  geom_point(shape=23, fill=colours, color=colours, size=5) +
    labs(title = "Cantidad promedio de interacciones x 1000 followers", x = "Club", y = "Cantidad") +
   scale_fill_manual(values= colours) +
    tema_plot

14- Media de interacciones

A traves de un boxplot buscamos las medias, y los outliers que tuvieron las interacciones a los distintos tweets de los clubes, siempre y cuando los tweets hayan tenido al menos 1000 interacciones.

Si bien es un gráfico que no nos dice mucho, sirve para identificar 2 outliers claves, que son el día que Boca salio campeon y tuvo casi 100k de interacciones y el dia que Racing propuso llegar a 100 mil favoritos, que lo supero ampliamente.

Tweets_Club %>% 
  mutate(interacciones = favorite_count + retweet_count)%>%
  filter(interacciones > 1000) %>%
ggplot()+
  aes(x= Club, y= interacciones, color= Club) +
  geom_boxplot () +
    labs(title = "Boxplot de interacciones", x = "Club", y = "Cantidad") +
   scale_fill_manual(values= colours) +
    tema_plot

15- Largo del tweet

Con el campo “display_text_width” que mide la cantidad de caracteres que tiene un tweet sacamos la media del largo que tiene cada tweet.

San Lorenzo es el que produce los tweets con más texto, luego el resto de los clubes están en una mediania bastante parecida.

Tweets_Club %>% 
ggplot()+
  aes(x= Club, y= display_text_width, color= Club) +
  geom_boxplot () +
    labs(title = "Cantidad promedio de interacciones x 1000 followers", x = "Club", y = "Largo promedio tweet") +
   scale_fill_manual(values= colours) +
  coord_flip() +
    tema_plot

16- Tweets sobre covid

El covid es el tema más importante del año 2020, con la funcion grepl buscamos tweets que contengan palabras que se relacionan con el virus, y contabilizamos cuales son los clubes que más hablaron sobre el mismo

#Buscamos tweets con la palabra covid
Palabras_covid <- "covid|covid-19|covid19|coronavirus|#covid|#covid-19|#covid19|#coronavirus|test|testeo|testeos|pcr|serologico|hisopado"
Tweets_Club$Covid <- grepl(Palabras_covid, Tweets_Club$text, ignore.case ="True")

Tweets_Club %>% 
  group_by(Club, Covid)%>%
  filter(Covid == T) %>%
  count(Club) %>%
ggplot()+
  aes(x=reorder(Club, n), y= n, fill= Club) +
  geom_col() +
    coord_flip() +
    labs(title = "Cantidad Tweets sobre Coronavirus", x = "Club", y = "Cantidad") +
   scale_fill_manual(values= colours) +
    tema_graf

Analisis sobre followers

1 - Descargamos los followers

Con la funcion “get_followers” del paquete “rtweet” podemos descargar el ID de todos los seguidores de una cuenta y luego con la “funcion lookup_users” obtenemos la información más precisa de los seguidores.

n = “all” es el máximo de cantidad de seguidores que podes descargar de una cuenta, en este caso es 75 mil usuarios, que se tomara como Muestra aleatoria simple

2 - Agregamos una identificación de cada club

Le ponemos una identificación a cada usuario, de que club sigue, y luego unimos nuevamente con la función Rbind para tener un DataFrame único

for(i in 1:nrow(Followers_Independiente)) {
Followers_Independiente$Sigue  <- c ("Independiente")
}

for(i in 1:nrow(Followers_BocaJrs)) {
Followers_BocaJrs$Sigue  <- c ("Boca")
}

for(i in 1:nrow(Followers_SanLorenzo)) {
Followers_SanLorenzo$Sigue  <- c ("San Lorenzo")
}

for(i in 1:nrow(Followers_RacingClub)) {
Followers_RacingClub$Sigue  <- c ("Racing")
}

for(i in 1:nrow(Followers_RiverPlate)) {
Followers_RiverPlate$Sigue  <- c ("River Plate")
}

Followers_Club <- rbind(Followers_BocaJrs, Followers_RacingClub, Followers_SanLorenzo, Followers_RiverPlate, Followers_Independiente)

3- Cantidad seguidores

El club con mas seguidores es Boca, que cuenta con casi 3.8 MM, en un segundo nivel River con casi 3.5 MM de seguidores, y luego el resto de los clubes en torno a los 500K de seguidores

Interacciones %>% 
ggplot()+
  aes(x=reorder(Club, -followers), y= (followers*1000), fill= Club) +
  geom_col() +
    labs(title = "Cantidad followers", x = "Club", y = "Cantidad") +
   scale_fill_manual(values= colours) +
    tema_plot

4- Desde que interfaz lo siguen

El comportamiento desde donde lo siguen los usuarios es parecida en todos los clubes, predonima Android, luego Iphone y web en un nivel parecido, y un grupo pequeño desde Instagram y la Web Client de twitter.

Se filtra en un mínimo de 300 los seguidores que usen esa plataforma, para evitar que sea infinita la cantidad de variables.

5- Idioma de los followers

Se saca proporcionalmente la cantidad de idiomas que son hablados por los seguidores que tiene cada una de las cuentas.

Como corresponde, la mayoria habla en español, la cantidad de seguidores en ingles es parecida entre todas las cuentas.

El portugues es el otro idioma importante entre los clubes.

Followers_Club %>%
  group_by(Sigue) %>%
  count(lang) %>%
  filter(n > 300) %>%
  filter(!is.na(lang)) %>%
    filter(lang != "und") %>%
   filter(lang != "ar")%>%
  mutate(Proporcion = n / sum(n)) %>%
  ggplot() +
      aes(Sigue, Proporcion, fill = lang) +
      geom_col() +
    scale_y_continuous(labels = percent_format()) +
      tema_plot

6- Extranjeros que lo siguen

Buscamos nada mas que los extranjeros que siguen a la cuenta, el país que más se destaca es Brasil, seguido por Colombia y Uruguay.

Followers_Club %>%
  group_by(Sigue) %>%
  count(country_code) %>%
  filter(country_code != "") %>%
  filter(country_code != "AR")%>%
  filter(!is.na(country_code)) %>%
  filter(n > 10) %>%
  mutate(Proporcion = n / sum(n)) %>%
  ggplot() +
      aes(Sigue, Proporcion, fill = country_code) +
      geom_col() +
    scale_y_continuous(labels = percent_format()) +
      tema_plot

7- Actividad de los seguidores

Primero creamos los campos para la fecha created_at como hicimos con el DF de tweets, y adjuntamos un campo de actividad, para saber cual es la relación del usuario con Twitter.

Los clubes con menos seguidores son los que tienen seguidores con más tiempo sin actividad, mientras que los siguen a clubes grandes estan mayormente en actividad.

LS0tDQp0aXRsZTogIkFuYWxpc2lzIDUgZ3JhbmRlcyBlbiBUd2l0dGVyIg0Kb3V0cHV0OiBodG1sX25vdGVib29rDQotLS0NCg0KIyBBbmFsaXNpcyBzb2JyZSBlbCB0aW1lbGluZQ0KDQojIyAxLSBDb21wbGV0YW1vcyBsYSBhdXRvcml6YWNpw7NuIHBhcmEgdHdpdHRlcg0KQ3JlYW1vcyB1biB0b2tlbiBwYXJhIGVudHJhciBhIHR3aXR0ZXIgeSB1c2FyIGxhIEFQSSwgeSBsYXMgbGlicmVyaWFzIFJ0d2VldCB5IFR3aXR0ZVINCmBgYHtyfQ0KdHdpdHRlcl90b2tlbiA8LSBjcmVhdGVfdG9rZW4oDQogIGFwcCA9IGFwcG5hbWUsDQogIGNvbnN1bWVyX2tleSA9IGNvbnN1bWVyX2tleSwNCiAgY29uc3VtZXJfc2VjcmV0ID0gY29uc3VtZXJfc2VjcmV0LA0KICBhY2Nlc3NfdG9rZW4gPSBhY2Nlc3NfdG9rZW4sIA0KICBhY2Nlc3Nfc2VjcmV0ID0gYWNjZXNzX3NlY3JldCkNCmBgYA0KDQojIyAyLSBEZXNjYXJnYW1vcyBsb3MgdHdlZXRzDQpEZXNjYXJnYW1vcyBsb3MgdHdlZXRzIGRlIGN1YWxxdWllciB1c3VhcmlvIGNvbiBsYSBmdW5jaW9uICJnZXRfdGltZWxpbmUiIGRlIHJ0d2VldA0KVXNlciA9IEAgZGVsIHVzdWFyaW8NCm49IENhbnRpZGFkIGRlIHR3ZWV0cyBhIGJhamFyDQppbmNsdWRlUnRzID0gRiBwYXJhIGVsaW1pbmFyIGxvcyBwb3NpYmxlcyBSVCBkZSBsYSBjdWVudGENCmV4Y2x1ZGVSZXBsaWVzID0gRiB2YW1vcyBhIGRlc2NhcmdhciBsYSBzZWNjacOzbiAiVHdlZXRzIHkgcmVzcHVlc3RhcyINCg0KYGBge3J9DQpUd2VldHNfQm9jYUpycyA8LSBnZXRfdGltZWxpbmUodXNlciA9ICJCb2NhSnJzT2ZpY2lhbCIsIG4gPSAzMjAwLCBpbmNsdWRlUnRzID0gRiwgZXhjbHVkZVJlcGxpZXMgPSBGKQ0KVHdlZXRzX1JpdmVyUGxhdGUgPC0gZ2V0X3RpbWVsaW5lKHVzZXIgPSAiUml2ZXJQbGF0ZSIsIG4gPSAzMjAwLCBpbmNsdWRlUnRzID0gRiwgZXhjbHVkZVJlcGxpZXMgPSBGKQ0KVHdlZXRzX1NhbkxvcmVuem8gPC0gZ2V0X3RpbWVsaW5lKHVzZXIgPSAiU2FuTG9yZW56byIsIG4gPSAzMjAwLCBpbmNsdWRlUnRzID0gRiwgZXhjbHVkZVJlcGxpZXMgPSBGKQ0KVHdlZXRzX1JhY2luZyA8LSBnZXRfdGltZWxpbmUodXNlciA9ICJSYWNpbmdDbHViIiwgbiA9IDMyMDAsIGluY2x1ZGVSdHMgPSBGLCBleGNsdWRlUmVwbGllcyA9IEYpDQpUd2VldHNfSW5kZXBlbmRpZW50ZSA8LSBnZXRfdGltZWxpbmUodXNlciA9ICJJbmRlcGVuZGllbnRlIiwgbiA9IDMyMDAsIGluY2x1ZGVSdHMgPSBGLCBleGNsdWRlUmVwbGllcyA9IEYpDQpgYGANCg0KIyMgMy0gVW5pbW9zIGxvcyBkYXRhZnJhbWVzDQpWYW1vcyBhIHVzYXIgdW4gc29sbyBEYXRhZnJhbWUgcXVlIGxsYW1hcmVtb3MgVHdlZXRzX0NsdWIgeSBsbyB2YW1vcyBhIGhhY2VyIGNvbiBsYSBmdW5jaW9uIHJiaW5kIGRlIFIgYmFzZQ0KDQpFbCBudWV2byBEYXRhZnJhbWUgdmEgYSBjb250YXIgY29uIDMyMDAqNSBmaWxhcyA9IDE2MDAwIHkgY29udGllbmUgMzIwMCB0d2VldHMgZGUgY2FkYSB1bmEgZGUgbGFzIGN1ZW50YXMNCmBgYHtyfQ0KVHdlZXRzX0NsdWIgPC0gcmJpbmQoVHdlZXRzX0JvY2FKcnMsIFR3ZWV0c19SYWNpbmcsIFR3ZWV0c19TYW5Mb3JlbnpvLCBUd2VldHNfUml2ZXJQbGF0ZSwgVHdlZXRzX0luZGVwZW5kaWVudGUpDQpgYGANCg0KIyMgNC0gQ29tZW56YW1vcyBlbCBwcm9jZXNvIGRlIGxpbXBpZXphDQpWYW1vcyBhIGNyZWFyIGFsZ3VuYXMgY29sdW1uYXMgZXh0cmFzIHBhcmEgb2J0ZW5lciBtYXMgaW5mb3JtYWNpw7NuIHkgdGFtYmnDqW4gdmFtb3MgYSBmaWx0cmFyIGRhdG9zLCBwYXJhIHF1ZWRhcm5vcyBzb2xvIGNvbiBsb3MgcXVlIG5vcyBpbnRlcmVzYQ0KYGBge3J9DQpUd2VldHNfQ2x1YiA8LSBUd2VldHNfQ2x1YiU+JQ0KICB0YmxfZGYgJT4lDQogIG11dGF0ZSh0ZXh0ID0gZ3N1YigiW15bOmdyYXBoOl1dIiwgIiAiLCB0ZXh0KSkgJT4lICNTaW4gZ3JhZmljb3MNCiAgbXV0YXRlKHRleHQgPSB0b2xvd2VyKHRleHQpKSAlPiUgI1RvZG8gZWwgdGV4dG8gYSBtaW51c2N1bGENCiAgbXV0YXRlKGNyZWF0ZWRfYXQgPSB3aXRoX3R6KGNyZWF0ZWRfYXQsICJBbWVyaWNhL0FyZ2VudGluYS9CdWVub3NfQWlyZXMiKSklPiUgI0NhbWJpYW1vcyBhIGxhIHpvbmEgaG9yYXJpYSBjb3JyZXNwb25kaWVudGUNCiAgc2VwYXJhdGUoY3JlYXRlZF9hdCwgaW50byA9IGMoImRhdGUiLCAiaG91ciIpLCBzZXAgPSAiICIpJT4lICNTZXBhcmFtb3MgZW4gZGlhIHkgaG9yYSBlbCBjYW1wbyBjcmVhdGVkX2F0DQogICAgc2VwYXJhdGUoaG91ciwgaW50byA9IGMoImhvdXIiLCAibWludXRlcyIsInNlY29uZHMiKSwgc2VwID0gIjoiKSU+JSAjU2VwYXJhbW9zIGxhIGhvcmEgZW4gaG9yYSxtaW51dG9zIHkgc2VndW5kb3MNCiAgIHJlbmFtZShDbHViID0gc2NyZWVuX25hbWUpICU+JSAjQ2FtYmlhbW9zIGxhIGNvbHVtbmEgY29uIGVsIG5vbWJyZSBkZWwgY2x1Yg0KbXV0YXRlKHBlcmlvZG8gPSB5ZWFyKGRhdGUpLCANCiAgICAgICAgIG1lcyA9IG1vbnRoKGRhdGUsIGxhYmVsID0gVCwgYWJiciA9IEYpLA0KICAgICAgICAgZGlhID0gYXMubnVtZXJpYyhkYXkoZGF0ZSkpLA0KICAgICAgICAgZGlhX3NlbSA9IHdkYXkoZGF0ZSwgbGFiZWwgPSBULCBhYmJyID0gRiwgd2Vla19zdGFydCA9IDEpLA0KICAgICAgICAgZGlhX3BlciA9IHlkYXkoZGF0ZSksDQogICAgICAgICBkYXRlID0gYXMuRGF0ZShkYXRlKSAjQ3JlYW1vcyB1bmEgY29sdW1uYSBjb24gZWwgbnVtZXJvIGRlIGHDsW8sIG1lcywgZGlhLCBub21icmUgZGUgZGlhIHkgZGUgbWVzLg0KICApJT4lDQogIGZpbHRlcihwZXJpb2RvID09IDIwMjApICNTb2xvIHZhbW9zIGEgdXRpbGl6YXIgaW5mbyAgZGUgMjAyMA0KYGBgDQpFbCBudWV2byBEYXRhRnJhbWUgc2VyYSBkZSA5NjYwIG9ic2VydmFjaW9uZXMNCg0KDQojIyA1LSBDcmVhbW9zIGNpZXJ0b3MgdGVtYXMgZ3JhZmljb3MNCg0KUGFyYSB0ZW5lciB1bmEgbWVqb3IgdmlzdWFsaXphY2lvbiwgcHJlY2FyZ2Ftb3MgYWxndW5vcyB0ZW1hcyB5IGxlIGRhbW9zIGVsIGNvbG9yIGEgY2FkYSBjbHViDQpgYGB7cn0NCnRlbWFfcGxvdCA8LQ0KICB0aGVtZSh0ZXh0ID0gZWxlbWVudF90ZXh0KGZhbWlseSA9ICJzYW5zIiwgc2l6ZSA9IDEwKSwNCiAgICAgICAgcGFuZWwuYm9yZGVyID0gZWxlbWVudF9yZWN0KGNvbG9yID0gIiNjY2NjY2MiLCBmaWxsID0gTkEpLA0KICAgICAgICBwYW5lbC5iYWNrZ3JvdW5kID0gZWxlbWVudF9yZWN0KGZpbGwgPSAid2hpdGUiKSwNCiAgICAgICAgcGFuZWwuZ3JpZC5tYWpvciA9ICBlbGVtZW50X2xpbmUoY29sb3IgPSAiI2RkZGRkZCIpLA0KICAgICAgICBwYW5lbC5ncmlkLm1pbm9yID0gIGVsZW1lbnRfbGluZShjb2xvciA9ICIjZWVlZWVlIiksDQogICAgICAgIGF4aXMudGlja3MgPSBlbGVtZW50X2xpbmUoY29sb3VyID0gIiNjY2NjY2MiKSwNCiAgICAgICAgc3RyaXAuYmFja2dyb3VuZCA9IGVsZW1lbnRfcmVjdChjb2xvciA9ICIjY2NjY2NjIiwgZmlsbCA9ICIjZWVlZWVlIiksDQogICAgICAgIGxlZ2VuZC5wb3NpdGlvbiA9ICJ0b3AiKQ0KDQp0ZW1hX2dyYWYgPC0gdGhlbWVfbWluaW1hbCgpICsNCiAgdGhlbWUodGV4dCA9IGVsZW1lbnRfdGV4dChmYW1pbHkgPSAic2VyaWYiKSwNCiAgICAgICAgcGFuZWwuZ3JpZC5taW5vciA9IGVsZW1lbnRfYmxhbmsoKSwNCiAgICAgICAgc3RyaXAuYmFja2dyb3VuZCA9IGVsZW1lbnRfcmVjdChmaWxsID0gIiNFQkVCRUIiLCBjb2xvdXIgPSBOQSksDQogICAgICAgIGxlZ2VuZC5wb3NpdGlvbiA9ICJub25lIiwNCiAgICAgICAgbGVnZW5kLmJveC5iYWNrZ3JvdW5kID0gZWxlbWVudF9yZWN0KGZpbGwgPSAiI0VCRUJFQiIsIGNvbG91ciA9IE5BKSkNCg0KY29sb3VycyA8LSBjKA0KICAiIzE3NDZmOCIsICMgQm9jYUpyc09maWNpYWwNCiAgIiNjZDAwMDAiLCAjIEluZGVwZW5kaWVudGUNCiAgIiMxM2U0ZmMiLCAjIFJhY2luZ0NsdWINCiAgIiM5YjViNTMiLCAjIFJpdmVyIFBsYXRlDQogICIjMDAxMjU2IiAgIyBTYW5Mb3JlbnpvDQopDQpgYGANCg0KIyMgNi0gQ2FudGlkYWQgZGUgdHdlZXRzIGVuIGVsIGHDsW8NCg0KVmVtb3MgcXVlIGVsIGNsdWIgcXVlIG1hcyB0d2l0dGVvIGVuIGxvIHF1ZSB2YSBkZWwgYcOxbyBlcyBSYWNpbmcgY29uIGNhc2kgMjUwMCwgcGFyZWNpZG8gYSBsbyBxdWUgaGl6byBTYW4gTG9yZW56byB5IHRhbWJpw6luIGEgUml2ZXIuIA0KDQpCb2NhIGUgSW5kZXBlbmRpZW50ZSBlc3RhbiBlbiB1biBuaXZlbCBtw6FzIGJham8sIGNlcmNhIGRlIGxvcyAxNTAwIHR3ZWV0cyBlbiBsbyBxdWUgdmEgZGVsIGHDsW8NCmBgYHtyfQ0KDQpUd2VldHNfQ2x1YiAlPiUgDQogIGdyb3VwX2J5KENsdWIpJT4lDQogIGNvdW50KENsdWIpICU+JQ0KZ2dwbG90KCkrDQogIGFlcyh4PXJlb3JkZXIoQ2x1YiwgbiksIHk9IG4sIGZpbGw9IENsdWIpICsNCiAgZ2VvbV9jb2woKSArDQogICAgY29vcmRfZmxpcCgpICsNCiAgICBsYWJzKHRpdGxlID0gIkNhbnRpZGFkIFR3ZWV0cyIsIHggPSAiQ2x1YiIsIFkgPSAiQ2FudGlkYWQiKSArDQogICBzY2FsZV9maWxsX21hbnVhbCh2YWx1ZXM9IGNvbG91cnMpICsNCiAgICB0ZW1hX2dyYWYNCmBgYA0KDQojIyA3LSBDYW50aWRhZCBkZSB0d2VldHMgcG9yIG1lcw0KYGBge3J9DQpUd2VldHNfQ2x1YiAlPiUgDQogIGdyb3VwX2J5KENsdWIsIG1lcyklPiUNCiAgY291bnQoQ2x1YikgJT4lDQpnZ3Bsb3QoKSsNCiAgYWVzKHg9cmVvcmRlcihDbHViLCBuKSwgeT0gbiwgZmlsbD0gQ2x1YikgKw0KICBnZW9tX2NvbCgpICsNCiAgICBjb29yZF9mbGlwKCkgKw0KICAgIGxhYnModGl0bGUgPSAiQ2FudGlkYWQgVHdlZXRzIiwgeCA9ICJDbHViIiwgWSA9ICJDYW50aWRhZCIpICsNCiAgZmFjZXRfd3JhcCh+bWVzLCBzY2FsZXMgPSAiZnJlZSIsIG5yb3cgPSAzKSsNCiAgIHNjYWxlX2ZpbGxfbWFudWFsKHZhbHVlcz0gY29sb3VycykgKw0KICAgIHRlbWFfZ3JhZg0KYGBgDQoNCg0KIyMgOC0gVHdlZXRzIHBvciBob3JhDQoNClZlbW9zIGNvbW8gZXMgbGEgZGlzdHJpYnVjacOzbiBkZSBsb3MgdHdlZXRzIHBvciBob3JhcmlvLiANCg0KQm9jYSBlcyBlbCBjbHViIGNvbiBtYXlvciBhY3RpdmlkYWQgYSBsYSBtZWRpYW5vY2hlLCBhIGxhIG1hZHJ1Z2FkYSBsYSBhY3RpdmlkYWQgZXMgY2FzaSBudWxhIHkgY29taWVuemEgbnVldmFtZW50ZSBhIGxhcyA4YW0uDQoNClRhbWLDrWVuIHBvZGVtb3Mgbm90YXIgcXVlIGxhIG1heW9yIGRpZmVyZW5jaWEgZW4gbG9zIHR3ZWV0cyBzZSBkYW4gYWxyZWRlZG9yIGRlbCBtZWRpb2RpYSB5IGEgbWVkaWEgdGFyZGUsIHZvbHZpZW5kbyBhIHR3aXR0ZWFyIGRlIG1hbmVyYSBwYXJlY2lkYSBkdXJhbnRlIGxhIG5vY2hlLg0KDQpgYGB7cn0NClR3ZWV0c19DbHViICU+JSANCiAgZ3JvdXBfYnkoQ2x1YiwgaG91ciklPiUNCiAgc3VtbWFyaXNlKG4gPSBuKCkpICU+JQ0KZ2dwbG90IChhZXMoeD0gaG91ciwgeSA9IG4sIGdyb3VwPSBDbHViLCBjb2xvciA9IENsdWIpKSArDQogIGdlb21fbGluZSgpKw0KICAgIGxhYnModGl0bGUgPSAiVHdlZXRzIHBvciBob3JhIiwgeCA9ICJIb3JhIiwgeSA9ICJDYW50aWRhZCIpICsNCiAgdGVtYV9wbG90DQpgYGANCg0KIyMgOS0gQ2FudGlkYWQgZGUgaW50ZXJhY2Npb25lcw0KDQpDcmVhbW9zIHVuIHBlcXVlw7FvIERGIHBhcmEgc3VtYXJpemFyIHRvZGFzIGxhcyBpdGVyYWNjaW9uZXMgcXVlIHRpZW5lbiBsb3MgZGlzdGludG9zIGNsdWJlcywgeSBsZSBhZ3JlZ2Ftb3Mgc3UgY2FudGlkYWQgZGUgZm9sbG93ZXJzIChkaXZpZGlkbyAxMDAwKQ0KYGBge3J9DQpJbnRlcmFjY2lvbmVzID0gVHdlZXRzX0NsdWIgJT4lIA0KICBncm91cF9ieShDbHViKSU+JQ0KICBzdW1tYXJpc2UoUHJvbWVkaW9SVCA9IG1lYW4ocmV0d2VldF9jb3VudCksIA0KICAgICAgICAgICAgVG90YWxSVCA9ICBzdW0ocmV0d2VldF9jb3VudCksDQogICAgICAgICAgICBQcm9tZWRpb0ZBViA9IG1lYW4oZmF2b3JpdGVfY291bnQpLCANCiAgICAgICAgICAgIFRvdGFsRkFWID0gIHN1bShmYXZvcml0ZV9jb3VudCksDQogICAgICAgICAgICBUb3RhbEludGVyYWNjaW9uZXMgPSBUb3RhbEZBViArIFRvdGFsUlQsDQogICAgICAgICAgICBQcm9tZWRpb0ludGVyYWNjaW9uZXMgPSBQcm9tZWRpb1JUICsgUHJvbWVkaW9GQVYNCiAgKSAlPiUNCiAgICBtdXRhdGUoZm9sbG93ZXJzID0gaWZlbHNlIChDbHViID09ICJCb2NhSnJzT2ZpY2lhbCIsIDM4MDAsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgaWZlbHNlIChDbHViID09ICJJbmRlcGVuZGllbnRlIiwgNDU0LjQsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGlmZWxzZSAoQ2x1YiA9PSAiUmFjaW5nQ2x1YiIsIDMzMC41LA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGlmZWxzZSAoQ2x1YiA9PSAiUml2ZXJQbGF0ZSIsIDM0MDAsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA3NTMuNA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgKQ0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICkNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICApDQogICAgKQ0KICApDQoNCnByaW50KEludGVyYWNjaW9uZXMpDQpgYGANCg0KIyMgMTAtIFJldHdlZXRzIGEgbGEgY3VlbnRhDQoNCk1lZGltb3MgbG9zIFJUIHF1ZSB0dXZvIGNhZGEgdW5vIGRlIGxvcyBjbHViZXMsIHRhbnRvIGVuIGNhbnRpZGFkIHRvdGFsIHF1ZSB2YSBkZSBsYSBtYW5vIGRlIGxhcyBwdWJsaWNhY2lvbmVzLCBhc8OtIGNvbW8gdGFtYmllbiBlbCBwcm9tZWRpby4NCg0KU2Ugbm90YSB1bmEgY2xhcmEgZGlmZXJlbmNpYSBlbnRyZSBSaXZlciB5IEJvY2EgY29uIGxvcyBvdHJvcyAzIGNsdWJlcyBhIGxhIGhvcmEgZGUgcXVlIHN1cyB0d2VldHMgc2VhbiByZXR3aXR0ZWFkb3MuDQpgYGB7cn0NCkludGVyYWNjaW9uZXMgJT4lIA0KZ2dwbG90KCkrDQogIGFlcyh4PXJlb3JkZXIoQ2x1YiwgVG90YWxSVCksIHk9IFRvdGFsUlQsIGZpbGw9IENsdWIpICsNCiAgZ2VvbV9jb2woKSArDQogICAgY29vcmRfZmxpcCgpICsNCiAgICBsYWJzKHRpdGxlID0gIkNhbnRpZGFkIHRvdGFsIGRlIFJUIiwgeCA9ICJDbHViIiwgeSA9ICJDYW50aWRhZCIpICsNCiAgIHNjYWxlX2ZpbGxfbWFudWFsKHZhbHVlcz0gY29sb3VycykgKw0KICAgIHRlbWFfZ3JhZg0KDQpJbnRlcmFjY2lvbmVzICU+JSANCmdncGxvdCgpKw0KICBhZXMoeD1yZW9yZGVyKENsdWIsIFByb21lZGlvUlQpLCB5PSBQcm9tZWRpb1JULCBmaWxsPSBDbHViKSArDQogIGdlb21fY29sKCkgKw0KICAgIGNvb3JkX2ZsaXAoKSArDQogICAgbGFicyh0aXRsZSA9ICJDYW50aWRhZCBwcm9tZWRpbyBkZSBSVCIsIHggPSAiQ2x1YiIsIHkgPSAiQ2FudGlkYWQiKSArDQogICBzY2FsZV9maWxsX21hbnVhbCh2YWx1ZXM9IGNvbG91cnMpICsNCiAgICB0ZW1hX3Bsb3QNCmBgYA0KDQojIyAxMS0gRmF2b3JpdG9zIGEgbGEgY3VlbnRhDQoNCk1lZGltb3MgbG9zIEZBViBxdWUgdHV2byBjYWRhIHVubyBkZSBsb3MgY2x1YmVzLCB0YW50byBlbiBjYW50aWRhZCB0b3RhbCBxdWUgdmEgZGUgbGEgbWFubyBkZSBsYXMgcHVibGljYWNpb25lcywgYXPDrSBjb21vIHRhbWJpZW4gZWwgcHJvbWVkaW8uDQoNClNlIG5vdGEgdW5hIGNsYXJhIGRpZmVyZW5jaWEgZW50cmUgUml2ZXIsIHF1ZSBhZGVtYXMgc2UgYXNlbnR1YSBjb250cmEgQm9jYSwgbWllbnRyYXMgcXVlIGxvcyBvdHJvcyBjbHViZXMgZXN0w6FuIG11eSBwb3IgZGViYWpvIGVuIGN1YW50byBhIEZBVnMgcmVjaWJpZG9zDQoNCmBgYHtyfQ0KSW50ZXJhY2Npb25lcyAlPiUgDQpnZ3Bsb3QoKSsNCiAgYWVzKHg9cmVvcmRlcihDbHViLCAtVG90YWxGQVYpLCB5PSBUb3RhbEZBViwgZmlsbD0gQ2x1YikgKw0KICBnZW9tX2NvbCgpICsNCiAgbGFicyh0aXRsZSA9ICJDYW50aWRhZCB0b3RhbCBkZSBGQVYiLCB4ID0gIkNsdWIiLCB5ID0gIkNhbnRpZGFkIikgKw0KICAgc2NhbGVfZmlsbF9tYW51YWwodmFsdWVzPSBjb2xvdXJzKSArDQogICAgdGVtYV9ncmFmDQoNCkludGVyYWNjaW9uZXMgJT4lIA0KZ2dwbG90KCkrDQogIGFlcyh4PXJlb3JkZXIoQ2x1YiwgLVByb21lZGlvRkFWKSwgeT0gUHJvbWVkaW9GQVYsIGZpbGw9IENsdWIpICsNCiAgZ2VvbV9jb2woKSArDQogICAgbGFicyh0aXRsZSA9ICJDYW50aWRhZCBwcm9tZWRpbyBkZSBGQVYiLCB4ID0gIkNsdWIiLCB5ID0gIkNhbnRpZGFkIikgKw0KICAgc2NhbGVfZmlsbF9tYW51YWwodmFsdWVzPSBjb2xvdXJzKSArDQogICAgdGVtYV9wbG90DQpgYGANCg0KDQojIyAxMi0gQ2FudGlkYWQgcHJvbWVkaW8gZGUgaW50ZXJhY2Npb25lcw0KDQpTdW1hbW9zIGxvIHZpc3RvIGFudGVyaW9ybWVudGUsIHRvbWFuZG8gdW4gc29sbyBjYW1wbyAiSW50ZXJhY2Npb24iIGNvbW8gbGEgc3VtYSBkZSBsb3MgcHJvbWVkaW9zIGRlIEZBVnMgeSBkZSBSVHMsIGxhcyBkaWZlcmVuY2lhcyBxdWUgc2Ugbm90YW4gc29uIGxhcyBtaXNtYXMgcXVlIGVuIGxvcyBhbnRlcmlvcmVzIGdyw6FmaWNvcy4NCg0KYGBge3J9DQpJbnRlcmFjY2lvbmVzICU+JSANCmdncGxvdCgpKw0KICBhZXMoeD1yZW9yZGVyKENsdWIsIFByb21lZGlvSW50ZXJhY2Npb25lcyksIHk9IFByb21lZGlvSW50ZXJhY2Npb25lcywgY29sb3I9IENsdWIpICsNCiAgZ2VvbV9wb2ludChzaGFwZT0yMywgZmlsbD1jb2xvdXJzLCBjb2xvcj1jb2xvdXJzLCBzaXplPTUpICsNCiAgICBsYWJzKHRpdGxlID0gIkNhbnRpZGFkIHByb21lZGlvIGRlIGludGVyYWNjaW9uZXMiLCB4ID0gIkNsdWIiLCB5ID0gIkNhbnRpZGFkIikgKw0KICAgc2NhbGVfZmlsbF9tYW51YWwodmFsdWVzPSBjb2xvdXJzKSArDQogICAgdGVtYV9wbG90DQpgYGANCg0KIyMgMTMtIENhbnRpZGFkIHByb21lZGlvIGRlIGludGVyYWNjaW9uZXMNCg0KTm9ybWFsaXphbW9zIGxhIGNhbnRpZGFkIGRlIGludGVyYWNjaW9uZXMgcXVlIHR1dmllcm9uIHBvciBjYWRhIDEwMDAgc2VndWlkb3JlcywgYWjDrSBzZSBub3RhIHF1ZSBSYWNpbmcgZSBJbmRlcGVuZGllbnRlIHNpIGJpZW4gc3VzIGludGVyYWNjaW9uZXMgc29uIG1lbm9yLCBzZSBkZWJlIGEgbGEgY2FudGlkYWQgZGUgZm9sbG93ZXJzIHF1ZSB0aWVuZSBjYWRhIHVubywgY3VhbmRvIG5vcm1hbGl6YW1vcyBxdWVkYW4gZW4gdW4gcHJpbWVyIG5pdmVsIGp1bnRvIGEgUml2ZXIuDQoNCk1pZW50cmFzIHF1ZSBCb2NhIG5vIGxvZ3JhIGxsZWdhciBhIDEgaW50ZXJhY2Npw7NuIHBvciBjYWRhIG1pbCBmb2xsb3dlcnMsIGVzdGFuZG8gZW4gdW4gc2VndW5kbyBwZWxvdMOzbiwgbWllbnRyYXMgcXVlIFNhbiBMb3JlbnpvIGVzIGVsIHF1ZSBtZW5vcyBpbnRlcmFjY2lvbmVzIHJlY2liZSBjb24gc3UgZ2VudGUuDQoNCmBgYHtyfQ0KSW50ZXJhY2Npb25lcyAlPiUgDQpnZ3Bsb3QoKSsNCiAgYWVzKHg9cmVvcmRlcihDbHViLCAoUHJvbWVkaW9JbnRlcmFjY2lvbmVzL2ZvbGxvd2VycykpLCB5PSAoUHJvbWVkaW9JbnRlcmFjY2lvbmVzL2ZvbGxvd2VycyksIGNvbG9yPSBDbHViKSArDQogIGdlb21fcG9pbnQoc2hhcGU9MjMsIGZpbGw9Y29sb3VycywgY29sb3I9Y29sb3Vycywgc2l6ZT01KSArDQogICAgbGFicyh0aXRsZSA9ICJDYW50aWRhZCBwcm9tZWRpbyBkZSBpbnRlcmFjY2lvbmVzIHggMTAwMCBmb2xsb3dlcnMiLCB4ID0gIkNsdWIiLCB5ID0gIkNhbnRpZGFkIikgKw0KICAgc2NhbGVfZmlsbF9tYW51YWwodmFsdWVzPSBjb2xvdXJzKSArDQogICAgdGVtYV9wbG90DQpgYGANCg0KIyMgMTQtIE1lZGlhIGRlIGludGVyYWNjaW9uZXMNCg0KQSB0cmF2ZXMgZGUgdW4gYm94cGxvdCBidXNjYW1vcyBsYXMgbWVkaWFzLCB5IGxvcyBvdXRsaWVycyBxdWUgdHV2aWVyb24gbGFzIGludGVyYWNjaW9uZXMgYSBsb3MgZGlzdGludG9zIHR3ZWV0cyBkZSBsb3MgY2x1YmVzLCBzaWVtcHJlIHkgY3VhbmRvIGxvcyB0d2VldHMgaGF5YW4gdGVuaWRvIGFsIG1lbm9zIDEwMDAgaW50ZXJhY2Npb25lcy4NCg0KU2kgYmllbiBlcyB1biBncsOhZmljbyBxdWUgbm8gbm9zIGRpY2UgbXVjaG8sIHNpcnZlIHBhcmEgaWRlbnRpZmljYXIgMiBvdXRsaWVycyBjbGF2ZXMsIHF1ZSBzb24gZWwgZMOtYSBxdWUgQm9jYSBzYWxpbyBjYW1wZW9uIHkgdHV2byBjYXNpIDEwMGsgZGUgaW50ZXJhY2Npb25lcyB5IGVsIGRpYSBxdWUgUmFjaW5nIHByb3B1c28gbGxlZ2FyIGEgMTAwIG1pbCBmYXZvcml0b3MsIHF1ZSBsbyBzdXBlcm8gYW1wbGlhbWVudGUuDQoNCmBgYHtyfQ0KVHdlZXRzX0NsdWIgJT4lIA0KICBtdXRhdGUoaW50ZXJhY2Npb25lcyA9IGZhdm9yaXRlX2NvdW50ICsgcmV0d2VldF9jb3VudCklPiUNCiAgZmlsdGVyKGludGVyYWNjaW9uZXMgPiAxMDAwKSAlPiUNCmdncGxvdCgpKw0KICBhZXMoeD0gQ2x1YiwgeT0gaW50ZXJhY2Npb25lcywgY29sb3I9IENsdWIpICsNCiAgZ2VvbV9ib3hwbG90ICgpICsNCiAgICBsYWJzKHRpdGxlID0gIkJveHBsb3QgZGUgaW50ZXJhY2Npb25lcyIsIHggPSAiQ2x1YiIsIHkgPSAiQ2FudGlkYWQiKSArDQogICBzY2FsZV9maWxsX21hbnVhbCh2YWx1ZXM9IGNvbG91cnMpICsNCiAgICB0ZW1hX3Bsb3QNCmBgYA0KDQojIyAxNS0gTGFyZ28gZGVsIHR3ZWV0DQoNCkNvbiBlbCBjYW1wbyAiZGlzcGxheV90ZXh0X3dpZHRoIiBxdWUgbWlkZSBsYSBjYW50aWRhZCBkZSBjYXJhY3RlcmVzIHF1ZSB0aWVuZSB1biB0d2VldCBzYWNhbW9zIGxhIG1lZGlhIGRlbCBsYXJnbyBxdWUgdGllbmUgY2FkYSB0d2VldC4NCg0KU2FuIExvcmVuem8gZXMgZWwgcXVlIHByb2R1Y2UgbG9zIHR3ZWV0cyBjb24gbcOhcyB0ZXh0bywgbHVlZ28gZWwgcmVzdG8gZGUgbG9zIGNsdWJlcyBlc3TDoW4gZW4gdW5hIG1lZGlhbmlhIGJhc3RhbnRlIHBhcmVjaWRhLg0KDQpgYGB7cn0NClR3ZWV0c19DbHViICU+JSANCmdncGxvdCgpKw0KICBhZXMoeD0gQ2x1YiwgeT0gZGlzcGxheV90ZXh0X3dpZHRoLCBjb2xvcj0gQ2x1YikgKw0KICBnZW9tX2JveHBsb3QgKCkgKw0KICAgIGxhYnModGl0bGUgPSAiQ2FudGlkYWQgcHJvbWVkaW8gZGUgaW50ZXJhY2Npb25lcyB4IDEwMDAgZm9sbG93ZXJzIiwgeCA9ICJDbHViIiwgeSA9ICJMYXJnbyBwcm9tZWRpbyB0d2VldCIpICsNCiAgIHNjYWxlX2ZpbGxfbWFudWFsKHZhbHVlcz0gY29sb3VycykgKw0KICBjb29yZF9mbGlwKCkgKw0KICAgIHRlbWFfcGxvdA0KYGBgDQoNCiMjIDE2LSBUd2VldHMgc29icmUgY292aWQNCg0KRWwgY292aWQgZXMgZWwgdGVtYSBtw6FzIGltcG9ydGFudGUgZGVsIGHDsW8gMjAyMCwgY29uIGxhIGZ1bmNpb24gZ3JlcGwgYnVzY2Ftb3MgdHdlZXRzIHF1ZSBjb250ZW5nYW4gcGFsYWJyYXMgcXVlIHNlIHJlbGFjaW9uYW4gY29uIGVsIHZpcnVzLCB5IGNvbnRhYmlsaXphbW9zIGN1YWxlcyBzb24gbG9zIGNsdWJlcyBxdWUgbcOhcyBoYWJsYXJvbiBzb2JyZSBlbCBtaXNtbw0KYGBge3J9DQojQnVzY2Ftb3MgdHdlZXRzIGNvbiBsYSBwYWxhYnJhIGNvdmlkDQpQYWxhYnJhc19jb3ZpZCA8LSAiY292aWR8Y292aWQtMTl8Y292aWQxOXxjb3JvbmF2aXJ1c3wjY292aWR8I2NvdmlkLTE5fCNjb3ZpZDE5fCNjb3JvbmF2aXJ1c3x0ZXN0fHRlc3Rlb3x0ZXN0ZW9zfHBjcnxzZXJvbG9naWNvfGhpc29wYWRvIg0KVHdlZXRzX0NsdWIkQ292aWQgPC0gZ3JlcGwoUGFsYWJyYXNfY292aWQsIFR3ZWV0c19DbHViJHRleHQsIGlnbm9yZS5jYXNlID0iVHJ1ZSIpDQoNClR3ZWV0c19DbHViICU+JSANCiAgZ3JvdXBfYnkoQ2x1YiwgQ292aWQpJT4lDQogIGZpbHRlcihDb3ZpZCA9PSBUKSAlPiUNCiAgY291bnQoQ2x1YikgJT4lDQpnZ3Bsb3QoKSsNCiAgYWVzKHg9cmVvcmRlcihDbHViLCBuKSwgeT0gbiwgZmlsbD0gQ2x1YikgKw0KICBnZW9tX2NvbCgpICsNCiAgICBjb29yZF9mbGlwKCkgKw0KICAgIGxhYnModGl0bGUgPSAiQ2FudGlkYWQgVHdlZXRzIHNvYnJlIENvcm9uYXZpcnVzIiwgeCA9ICJDbHViIiwgeSA9ICJDYW50aWRhZCIpICsNCiAgIHNjYWxlX2ZpbGxfbWFudWFsKHZhbHVlcz0gY29sb3VycykgKw0KICAgIHRlbWFfZ3JhZg0KYGBgDQoNCg0KDQojIEFuYWxpc2lzIHNvYnJlIGZvbGxvd2Vycw0KDQojIyAxIC0gRGVzY2FyZ2Ftb3MgbG9zIGZvbGxvd2Vycw0KDQpDb24gbGEgZnVuY2lvbiAiZ2V0X2ZvbGxvd2VycyIgZGVsIHBhcXVldGUgInJ0d2VldCIgcG9kZW1vcyBkZXNjYXJnYXIgZWwgSUQgZGUgdG9kb3MgbG9zIHNlZ3VpZG9yZXMgZGUgdW5hIGN1ZW50YSB5IGx1ZWdvIGNvbiBsYSAiZnVuY2lvbiBsb29rdXBfdXNlcnMiIG9idGVuZW1vcyBsYSBpbmZvcm1hY2nDs24gbcOhcyBwcmVjaXNhIGRlIGxvcyBzZWd1aWRvcmVzLg0KDQpuID0gImFsbCIgZXMgZWwgbcOheGltbyBkZSBjYW50aWRhZCBkZSBzZWd1aWRvcmVzIHF1ZSBwb2RlcyBkZXNjYXJnYXIgZGUgdW5hIGN1ZW50YSwgZW4gZXN0ZSBjYXNvIGVzIDc1IG1pbCB1c3VhcmlvcywgcXVlIHNlIHRvbWFyYSBjb21vIE11ZXN0cmEgYWxlYXRvcmlhIHNpbXBsZQ0KDQpgYGB7cn0NCkZvbGxvd2Vyc19Cb2NhSnJzX2lkIDwtIGdldF9mb2xsb3dlcnMoIkJvY2FKcnNPZmljaWFsIiwgbiA9ICJhbGwiKSAjRGVzY2FyZ2FyIGZvbGxvd2VycyBJZA0KRm9sbG93ZXJzX0JvY2FKcnMgPC0gbG9va3VwX3VzZXJzKEZvbGxvd2Vyc19Cb2NhSnJzX2lkJHVzZXJfaWQpICNEZXNjYXJnYXIgdGhlIGZvbGxvd2VycyBpbmZvDQoNCiNSZXBldGlyIHBhcmEgdG9kb3MgbG9zIHVzdWFyaW9zDQoNCkZvbGxvd2Vyc19SaXZlclBsYXRlX2lkIDwtIGdldF9mb2xsb3dlcnMoIlJpdmVyUGxhdGUiLCBuID0gImFsbCIpDQpGb2xsb3dlcnNfUml2ZXJQbGF0ZSA8LSBsb29rdXBfdXNlcnMoRm9sbG93ZXJzX1JpdmVyUGxhdGVfaWQkdXNlcl9pZCkNCg0KRm9sbG93ZXJzX1NhbkxvcmVuem9faWQgPC0gZ2V0X2ZvbGxvd2VycygiU2FuTG9yZW56byIsIG4gPSAiYWxsIikNCkZvbGxvd2Vyc19TYW5Mb3JlbnpvIDwtIGxvb2t1cF91c2VycyhGb2xsb3dlcnNfU2FuTG9yZW56b19pZCR1c2VyX2lkKQ0KDQpGb2xsb3dlcnNfUmFjaW5nQ2x1Yl9pZCA8LSBnZXRfZm9sbG93ZXJzKCJSYWNpbmdDbHViIiwgbiA9ICJhbGwiKQ0KRm9sbG93ZXJzX1JhY2luZ0NsdWIgPC0gbG9va3VwX3VzZXJzKEZvbGxvd2Vyc19SYWNpbmdDbHViX2lkJHVzZXJfaWQpDQoNCkZvbGxvd2Vyc19JbmRlcGVuZGllbnRlX2lkIDwtIGdldF9mb2xsb3dlcnMoIkluZGVwZW5kaWVudGUiLCBuID0gImFsbCIpDQpGb2xsb3dlcnNfSW5kZXBlbmRpZW50ZSA8LSBsb29rdXBfdXNlcnMoRm9sbG93ZXJzX0luZGVwZW5kaWVudGVfaWQkdXNlcl9pZCkNCg0KcHJpbnQoRm9sbG93ZXJzX1JhY2luZ0NsdWIpDQpgYGANCg0KIyMgMiAtIEFncmVnYW1vcyB1bmEgaWRlbnRpZmljYWNpw7NuIGRlIGNhZGEgY2x1YiANCg0KTGUgcG9uZW1vcyB1bmEgaWRlbnRpZmljYWNpw7NuIGEgY2FkYSB1c3VhcmlvLCBkZSBxdWUgY2x1YiBzaWd1ZSwgeSBsdWVnbyB1bmltb3MgbnVldmFtZW50ZSBjb24gbGEgZnVuY2nDs24gUmJpbmQgcGFyYSB0ZW5lciB1biBEYXRhRnJhbWUgw7puaWNvDQoNCmBgYHtyfQ0KZm9yKGkgaW4gMTpucm93KEZvbGxvd2Vyc19JbmRlcGVuZGllbnRlKSkgew0KRm9sbG93ZXJzX0luZGVwZW5kaWVudGUkU2lndWUgIDwtIGMgKCJJbmRlcGVuZGllbnRlIikNCn0NCg0KZm9yKGkgaW4gMTpucm93KEZvbGxvd2Vyc19Cb2NhSnJzKSkgew0KRm9sbG93ZXJzX0JvY2FKcnMkU2lndWUgIDwtIGMgKCJCb2NhIikNCn0NCg0KZm9yKGkgaW4gMTpucm93KEZvbGxvd2Vyc19TYW5Mb3JlbnpvKSkgew0KRm9sbG93ZXJzX1NhbkxvcmVuem8kU2lndWUgIDwtIGMgKCJTYW4gTG9yZW56byIpDQp9DQoNCmZvcihpIGluIDE6bnJvdyhGb2xsb3dlcnNfUmFjaW5nQ2x1YikpIHsNCkZvbGxvd2Vyc19SYWNpbmdDbHViJFNpZ3VlICA8LSBjICgiUmFjaW5nIikNCn0NCg0KZm9yKGkgaW4gMTpucm93KEZvbGxvd2Vyc19SaXZlclBsYXRlKSkgew0KRm9sbG93ZXJzX1JpdmVyUGxhdGUkU2lndWUgIDwtIGMgKCJSaXZlciBQbGF0ZSIpDQp9DQoNCkZvbGxvd2Vyc19DbHViIDwtIHJiaW5kKEZvbGxvd2Vyc19Cb2NhSnJzLCBGb2xsb3dlcnNfUmFjaW5nQ2x1YiwgRm9sbG93ZXJzX1NhbkxvcmVuem8sIEZvbGxvd2Vyc19SaXZlclBsYXRlLCBGb2xsb3dlcnNfSW5kZXBlbmRpZW50ZSkNCg0KYGBgDQoNCiMjIDMtIENhbnRpZGFkIHNlZ3VpZG9yZXMNCg0KRWwgY2x1YiBjb24gbWFzIHNlZ3VpZG9yZXMgZXMgQm9jYSwgcXVlIGN1ZW50YSBjb24gY2FzaSAzLjggTU0sIGVuIHVuIHNlZ3VuZG8gbml2ZWwgUml2ZXIgY29uIGNhc2kgMy41IE1NIGRlIHNlZ3VpZG9yZXMsIHkgbHVlZ28gZWwgcmVzdG8gZGUgbG9zIGNsdWJlcyBlbiB0b3JubyBhIGxvcyA1MDBLIGRlIHNlZ3VpZG9yZXMNCmBgYHtyfQ0KSW50ZXJhY2Npb25lcyAlPiUgDQpnZ3Bsb3QoKSsNCiAgYWVzKHg9cmVvcmRlcihDbHViLCAtZm9sbG93ZXJzKSwgeT0gKGZvbGxvd2VycyoxMDAwKSwgZmlsbD0gQ2x1YikgKw0KICBnZW9tX2NvbCgpICsNCiAgICBsYWJzKHRpdGxlID0gIkNhbnRpZGFkIGZvbGxvd2VycyIsIHggPSAiQ2x1YiIsIHkgPSAiQ2FudGlkYWQiKSArDQogICBzY2FsZV9maWxsX21hbnVhbCh2YWx1ZXM9IGNvbG91cnMpICsNCiAgICB0ZW1hX3Bsb3QNCmBgYA0KDQoNCiMjIDQtIERlc2RlIHF1ZSBpbnRlcmZheiBsbyBzaWd1ZW4NCg0KRWwgY29tcG9ydGFtaWVudG8gZGVzZGUgZG9uZGUgbG8gc2lndWVuIGxvcyB1c3VhcmlvcyBlcyBwYXJlY2lkYSBlbiB0b2RvcyBsb3MgY2x1YmVzLCBwcmVkb25pbWEgQW5kcm9pZCwgbHVlZ28gSXBob25lIHkgd2ViIGVuIHVuIG5pdmVsIHBhcmVjaWRvLCB5IHVuIGdydXBvIHBlcXVlw7FvIGRlc2RlIEluc3RhZ3JhbSB5IGxhIFdlYiBDbGllbnQgZGUgdHdpdHRlci4NCg0KU2UgZmlsdHJhIGVuIHVuIG3DrW5pbW8gZGUgMzAwIGxvcyBzZWd1aWRvcmVzIHF1ZSB1c2VuIGVzYSBwbGF0YWZvcm1hLCBwYXJhIGV2aXRhciBxdWUgc2VhIGluZmluaXRhIGxhIGNhbnRpZGFkIGRlIHZhcmlhYmxlcy4NCmBgYHtyfQ0KRm9sbG93ZXJzX0NsdWIgJT4lDQogIGdyb3VwX2J5KFNpZ3VlKSAlPiUNCiAgY291bnQoc291cmNlKSAlPiUNCiAgZmlsdGVyKG4gPiAzMDApICU+JQ0KICBmaWx0ZXIoIWlzLm5hKHNvdXJjZSkpICU+JQ0KICBtdXRhdGUoUHJvcG9yY2lvbiA9IG4gLyBzdW0obikpICU+JQ0KICBnZ3Bsb3QoKSArDQogICAgICBhZXMoU2lndWUsIFByb3BvcmNpb24sIGZpbGwgPSBzb3VyY2UpICsNCiAgICAgIGdlb21fY29sKCkgKw0KICAgIHNjYWxlX3lfY29udGludW91cyhsYWJlbHMgPSBwZXJjZW50X2Zvcm1hdCgpKSArDQogIHNjYWxlX2ZpbGxfbWFudWFsKHZhbHVlcz0gY29sb3VycykgKw0KICAgICAgdGVtYV9wbG90DQpgYGANCg0KIyMgNS0gSWRpb21hIGRlIGxvcyBmb2xsb3dlcnMgDQoNClNlIHNhY2EgcHJvcG9yY2lvbmFsbWVudGUgbGEgY2FudGlkYWQgZGUgaWRpb21hcyBxdWUgc29uIGhhYmxhZG9zIHBvciBsb3Mgc2VndWlkb3JlcyBxdWUgdGllbmUgY2FkYSB1bmEgZGUgbGFzIGN1ZW50YXMuDQoNCkNvbW8gY29ycmVzcG9uZGUsIGxhIG1heW9yaWEgaGFibGEgZW4gZXNwYcOxb2wsIGxhIGNhbnRpZGFkIGRlIHNlZ3VpZG9yZXMgZW4gaW5nbGVzIGVzIHBhcmVjaWRhIGVudHJlIHRvZGFzIGxhcyBjdWVudGFzLiANCg0KRWwgcG9ydHVndWVzIGVzIGVsIG90cm8gaWRpb21hIGltcG9ydGFudGUgZW50cmUgbG9zIGNsdWJlcy4NCmBgYHtyfQ0KRm9sbG93ZXJzX0NsdWIgJT4lDQogIGdyb3VwX2J5KFNpZ3VlKSAlPiUNCiAgY291bnQobGFuZykgJT4lDQogIGZpbHRlcihuID4gMzAwKSAlPiUNCiAgZmlsdGVyKCFpcy5uYShsYW5nKSkgJT4lDQogICAgZmlsdGVyKGxhbmcgIT0gInVuZCIpICU+JQ0KICAgZmlsdGVyKGxhbmcgIT0gImFyIiklPiUNCiAgbXV0YXRlKFByb3BvcmNpb24gPSBuIC8gc3VtKG4pKSAlPiUNCiAgZ2dwbG90KCkgKw0KICAgICAgYWVzKFNpZ3VlLCBQcm9wb3JjaW9uLCBmaWxsID0gbGFuZykgKw0KICAgICAgZ2VvbV9jb2woKSArDQogICAgc2NhbGVfeV9jb250aW51b3VzKGxhYmVscyA9IHBlcmNlbnRfZm9ybWF0KCkpICsNCiAgICAgIHRlbWFfcGxvdA0KYGBgDQoNCiMjIDYtIEV4dHJhbmplcm9zIHF1ZSBsbyBzaWd1ZW4NCg0KQnVzY2Ftb3MgbmFkYSBtYXMgcXVlIGxvcyBleHRyYW5qZXJvcyBxdWUgc2lndWVuIGEgbGEgY3VlbnRhLCBlbCBwYcOtcyBxdWUgbcOhcyBzZSBkZXN0YWNhIGVzIEJyYXNpbCwgc2VndWlkbyBwb3IgQ29sb21iaWEgeSBVcnVndWF5Lg0KDQpgYGB7cn0NCkZvbGxvd2Vyc19DbHViICU+JQ0KICBncm91cF9ieShTaWd1ZSkgJT4lDQogIGNvdW50KGNvdW50cnlfY29kZSkgJT4lDQogIGZpbHRlcihjb3VudHJ5X2NvZGUgIT0gIiIpICU+JQ0KICBmaWx0ZXIoY291bnRyeV9jb2RlICE9ICJBUiIpJT4lDQogIGZpbHRlcighaXMubmEoY291bnRyeV9jb2RlKSkgJT4lDQogIGZpbHRlcihuID4gMTApICU+JQ0KICBtdXRhdGUoUHJvcG9yY2lvbiA9IG4gLyBzdW0obikpICU+JQ0KICBnZ3Bsb3QoKSArDQogICAgICBhZXMoU2lndWUsIFByb3BvcmNpb24sIGZpbGwgPSBjb3VudHJ5X2NvZGUpICsNCiAgICAgIGdlb21fY29sKCkgKw0KICAgIHNjYWxlX3lfY29udGludW91cyhsYWJlbHMgPSBwZXJjZW50X2Zvcm1hdCgpKSArDQogICAgICB0ZW1hX3Bsb3QNCmBgYA0KDQoNCiMjIDctIEFjdGl2aWRhZCBkZSBsb3Mgc2VndWlkb3Jlcw0KDQpQcmltZXJvIGNyZWFtb3MgbG9zIGNhbXBvcyBwYXJhIGxhIGZlY2hhIGNyZWF0ZWRfYXQgY29tbyBoaWNpbW9zIGNvbiBlbCBERiBkZSB0d2VldHMsIHkgYWRqdW50YW1vcyB1biBjYW1wbyBkZSBhY3RpdmlkYWQsIHBhcmEgc2FiZXIgY3VhbCBlcyBsYSByZWxhY2nDs24gZGVsIHVzdWFyaW8gY29uIFR3aXR0ZXIuDQoNCkxvcyBjbHViZXMgY29uIG1lbm9zIHNlZ3VpZG9yZXMgc29uIGxvcyBxdWUgdGllbmVuIHNlZ3VpZG9yZXMgY29uIG3DoXMgdGllbXBvIHNpbiBhY3RpdmlkYWQsIG1pZW50cmFzIHF1ZSBsb3Mgc2lndWVuIGEgY2x1YmVzIGdyYW5kZXMgZXN0YW4gbWF5b3JtZW50ZSBlbiBhY3RpdmlkYWQuDQoNCmBgYHtyfQ0KRm9sbG93ZXJzX0NsdWIgPC0gRm9sbG93ZXJzX0NsdWIlPiUNCiAgbXV0YXRlKGNyZWF0ZWRfYXQgPSB3aXRoX3R6KGNyZWF0ZWRfYXQsICJBbWVyaWNhL0FyZ2VudGluYS9CdWVub3NfQWlyZXMiKSklPiUgI0NoYW5nZSB0aGUgdGltZV96b25lDQogIHNlcGFyYXRlKGNyZWF0ZWRfYXQsIGludG8gPSBjKCJkYXRlIiwgImhvdXIiKSwgc2VwID0gIiAiKSU+JSAjU2VwYXJhdGVkIHRoZSBkYXRlIGluIGRhdGUgYW5kIGhvdXINCiAgICBzZXBhcmF0ZShob3VyLCBpbnRvID0gYygiaG91ciIsICJtaW51dGVzIiwic2Vjb25kcyIpLCBzZXAgPSAiOiIpJT4lICNTZXBhcmF0ZWQgdGhlIGhvdXIgaW50byBob3VyLCBtaW51dGVzIGFuZCANCm11dGF0ZShwZXJpb2RvID0geWVhcihkYXRlKSwgDQogICAgICAgICBtZXMgPSBtb250aChkYXRlLCBsYWJlbCA9IFQsIGFiYnIgPSBGKSwNCiAgICAgICAgIGRpYSA9IGFzLm51bWVyaWMoZGF5KGRhdGUpKSwNCiAgICAgICAgIGRpYV9zZW0gPSB3ZGF5KGRhdGUsIGxhYmVsID0gVCwgYWJiciA9IEYsIHdlZWtfc3RhcnQgPSAxKSwNCiAgICAgICAgIGRpYV9wZXIgPSB5ZGF5KGRhdGUpLA0KICAgICAgICAgZGF0ZSA9IGFzLkRhdGUoZGF0ZSkgI0NyZWF0ZWQgY29sdW1ucyB3aXRoIG51bWJlciBvZiB5ZWFyLCBtb250aCwgZGF5LCBuYW1lIG9mIGRheS4NCiAgKSU+JQ0KbXV0YXRlKEFjdGl2aWRhZCA9IGlmZWxzZShwZXJpb2RvIDwgMjAxNiwgIjUrIGHDsW9zIHNpbiB0d2l0dGVhciIsDQogICAgICAgICAgICAgICAgICAgaWZlbHNlKHBlcmlvZG8gPCAyMDE4LCAiMyBhIDUgYcOxb3Mgc2luIHR3aXR0ZWFyIiwNCiAgICAgICAgICAgICAgICAgICBpZmVsc2UocGVyaW9kbyA8IDIwMjAsICIxIGEgMiBhw7FvcyBzaW4gdHdpdHRlYXIiLCAgDQogICAgICAgICAgICAgICAgICAgICAgICAgICJUd2l0dGVvIGVuIDIwMjAiKQ0KICAgICAgICAgICAgICAgICAgICkNCiAgICAgICkNCikNCg0KRm9sbG93ZXJzX0NsdWIgJT4lDQogIGdyb3VwX2J5KFNpZ3VlKSAlPiUNCiAgY291bnQoQWN0aXZpZGFkKSAlPiUNCiAgZmlsdGVyKCFpcy5uYShBY3RpdmlkYWQpKSAlPiUNCiAgbXV0YXRlKFByb3BvcmNpb24gPSBuIC8gc3VtKG4pKSAlPiUNCiAgZ2dwbG90KCkgKw0KICAgICAgYWVzKFNpZ3VlLCBQcm9wb3JjaW9uLCBmaWxsID0gQWN0aXZpZGFkKSArDQogICAgICBnZW9tX2NvbCgpICsNCiAgICBzY2FsZV95X2NvbnRpbnVvdXMobGFiZWxzID0gcGVyY2VudF9mb3JtYXQoKSkgKw0KICAgICAgdGVtYV9wbG90DQpgYGANCg==