0.Overview

En este trabajo vamos a analizar el dataset de netflix, este tiene datos de las peliculas y las clasificaciones a las distintas peliculas hechas por los usuarios. A partir de un dato de muestras de dicho dataset, vamos a generar las reglas de asociacion mas importantes teniendo en cuenta las clasificaciones hechas por cada pelicula de cada uno de los clientes como si fueran transacciones. Esperamos analizar las mas importantes para buscar algun tipo de logica las cuales las expliquen y lograr buscar una propuesta para un sistema de recomendacion, ofertas o de toma de decisiones. Usaremos clusters para poder separar las peliculas e identificar las mas recomendadas. Por ultimo, veremos de intentar analizar las tendencias de los clientes usando word cloud.

1. Librerias

library(readr)
library(dplyr)
library(tidyverse)
library(plyr)
library(arules)
library(arulesViz)
library(RColorBrewer)
library(ggplot2)
library(factoextra)
library(tm)
library(wordcloud)
library(SnowballC)
library(textdata)

2. Analisis Estadistico Descriptivo

2.1 Dataset que contiene titulos de las peliculas

d_titulo <- read_csv("movie_titles.csv", col_names = c("IdPelicula","date","name"))
Parsed with column specification:
cols(
  IdPelicula = col_double(),
  date = col_double(),
  name = col_character()
)
343 parsing failures.
row col  expected    actual               file
 72  -- 3 columns 4 columns 'movie_titles.csv'
264  -- 3 columns 5 columns 'movie_titles.csv'
350  -- 3 columns 4 columns 'movie_titles.csv'
366  -- 3 columns 4 columns 'movie_titles.csv'
394  -- 3 columns 4 columns 'movie_titles.csv'
... ... ......... ......... ..................
See problems(...) for more details.
head(d_titulo)

2.2 Dataset que contiene los rating de los usuarios

d_rating <- read_csv("bq-results-20191202-190110-wfdzmx1qblfr.csv")
Parsed with column specification:
cols(
  IdPelicula = col_double(),
  IdCliente = col_double(),
  Rating = col_double(),
  Fecha = col_date(format = "")
)
head(d_rating)

2.3 Dataset que contiene los overview de las peliculas

d_overview <- read_csv("movies_metadata.csv")
Parsed with column specification:
cols(
  .default = col_character(),
  adult = col_logical(),
  budget = col_double(),
  id = col_double(),
  popularity = col_double(),
  release_date = col_date(format = ""),
  revenue = col_double(),
  runtime = col_double(),
  video = col_logical(),
  vote_average = col_double(),
  vote_count = col_double()
)
See spec(...) for full column specifications.
19 parsing failures.
  row          col               expected                           actual                  file
19730 NA           24 columns             10 columns                       'movies_metadata.csv'
19731 adult        1/0/T/F/TRUE/FALSE     - Written by Ørnås             'movies_metadata.csv'
19731 budget       a double               /ff9qCepilowshEtG2GYWwzt2bs4.jpg 'movies_metadata.csv'
19731 id           no trailing characters -08-20                           'movies_metadata.csv'
19731 release_date date like              1                                'movies_metadata.csv'
..... ............ ...................... ................................ .....................
See problems(...) for more details.
head(d_overview)

2.4 Dataset que contiene los generos de las peliculas

d_generos <- read_csv("title.basics.csv")
Missing column names filled in: 'X1' [1]Parsed with column specification:
cols(
  X1 = col_double(),
  tconst = col_character(),
  titleType = col_character(),
  primaryTitle = col_character(),
  originalTitle = col_character(),
  isAdult = col_double(),
  startYear = col_double(),
  endYear = col_character(),
  runtimeMinutes = col_character(),
  genres = col_character()
)
292789 parsing failures.
  row       col expected actual               file
65794 startYear a double    \N 'title.basics.csv'
69738 startYear a double    \N 'title.basics.csv'
83851 startYear a double    \N 'title.basics.csv'
84415 startYear a double    \N 'title.basics.csv'
90986 startYear a double    \N 'title.basics.csv'
..... ......... ........ ...... ..................
See problems(...) for more details.
head(d_generos)

2.5 Incluir Generos


genres <- c("")
peliculas <- d_titulo
peliculas <- cbind(peliculas,genres)

nm <- c("genres")

peliculas[nm] <- lapply(nm, function(x) d_generos[[x]] [match(peliculas$name, d_generos$primaryTitle)] )

2.6 Incluir overview


resumen <- c("")
peliculas <- cbind(peliculas,resumen)

ov <- c("overview")

peliculas[ov] <- lapply(ov, function(x) d_overview[[x]] [match(peliculas$name, d_overview$title)])

2.7 Borrar NA

sapply(peliculas, function(x) sum(is.na(x)))
IdPelicula       date       name     genres    resumen   overview 
         0          7          0       5898          0       9064 
peliculas2 <- na.omit(peliculas)
peliculas2$resumen <- NULL

2.8 Obtener promedio de rating de cada pelicula


#prom_rat <- group_by(d_rating, d_rating$IdPelicula)

#prom_rat <- summarise(prom_rat, mean(d_rating$IdPelicula), mean(d_rating$Rating))

by_id <- d_rating %>%
group_by(IdPelicula) %>%
dplyr::summarise(promrating = mean(Rating))

peliculas3 <- merge(peliculas2, by_id,by="IdPelicula", all.x = TRUE)
names(peliculas3)[names(peliculas3) == 'mean(Rating)'] <- 'promrating'

2.9 Eliminar Duplicados

peliculas4 <- peliculas3

peliculas4 <- peliculas4 %>% distinct(name, .keep_all = TRUE)

2.10 Exploracion de los datos


head(peliculas4)
tail(peliculas4)
colnames(peliculas4)
[1] "IdPelicula" "date"       "name"       "genres"     "overview"  
[6] "promrating"
dim(peliculas4)
[1] 8110    6
summary(peliculas4)
   IdPelicula         date          name              genres         
 Min.   :    3   Min.   :1914   Length:8110        Length:8110       
 1st Qu.: 4246   1st Qu.:1980   Class :character   Class :character  
 Median : 8639   Median :1994   Mode  :character   Mode  :character  
 Mean   : 8728   Mean   :1987                                        
 3rd Qu.:13197   3rd Qu.:2000                                        
 Max.   :17770   Max.   :2005                                        
                                                                     
   overview           promrating   
 Length:8110        Min.   :1.200  
 Class :character   1st Qu.:2.930  
 Mode  :character   Median :3.268  
                    Mean   :3.227  
                    3rd Qu.:3.577  
                    Max.   :4.750  
                    NA's   :2167   
  • Se visualizan NA en promrating

2.11 Quitar NA´S

sapply(peliculas4, function(x) sum(is.na(x)))
IdPelicula       date       name     genres   overview promrating 
         0          0          0          0          0       2167 
peliculas4 <- na.omit(peliculas4)

2.12 Date

summary(peliculas4$date)
   Min. 1st Qu.  Median    Mean 3rd Qu.    Max. 
   1914    1979    1994    1987    2000    2005 
sd(peliculas4$date)
[1] 17.64498
boxplot(peliculas4$date)

hist(peliculas4$date)

  • La mayor concentracion de peliculas fueron publicadas a partir de 1980

2.13 promrating

summary(peliculas4$promrating)
   Min. 1st Qu.  Median    Mean 3rd Qu.    Max. 
  1.200   2.930   3.268   3.227   3.577   4.750 

2.14 Reemplazando a NA


peliculas4$genres[peliculas4$genres == '\\N'] <- NA
sapply(peliculas4, function(x) sum(is.na(x)))
IdPelicula       date       name     genres   overview promrating 
         0          0          0         70          0          0 
peliculas4 <- na.omit(peliculas4)

2.15 Peliculas con mejores notas


head(peliculas4[order(peliculas4$promrating, decreasing = TRUE),c(3,6,2)],20)
NA
  • Entre las 10 peliculas con mejores notas se encuentran algunas conocidas, tales como La lista de Schindler, El padrino, Indiana Jones, Toy Story, etc.

peliculas4$genres <- as.factor(peliculas4$genres)

2.16 Generos mas Rrecurrentes

t <- table(peliculas4$genres)
barplot( sort(t, decreasing=TRUE)[1:5], space = 0.5, cex.names = 0.70)

sort(t, decreasing = TRUE)[1:10]

               Drama               Comedy        Drama,Romance 
                 501                  285                  242 
Comedy,Drama,Romance         Comedy,Drama       Comedy,Romance 
                 228                  195                  164 
  Action,Crime,Drama          Crime,Drama          Documentary 
                 111                   95                   95 
Crime,Drama,Thriller 
                  92 
  • El genero mas recurrente es Drama

2.17 Peliculas con mejor nota 70’s

p70 <- filter(peliculas4, (peliculas4$date >= 1970 & peliculas4$date< 1980))

head(p70[order(p70$promrating, decreasing = TRUE),c(3,6,2)],20)
NA
  • La pelicula con mejores notas de los 70´s es “El Padrino”

2.18 Peliculas con mejor nota 80’s

p80 <- filter(peliculas4, (peliculas4$date >= 1980 & peliculas4$date< 1990))

head(p80[order(p80$promrating, decreasing = TRUE),c(3,6,2)],20)
NA
  • Las peliculas con mejores notas de los 80´s son dos de la saga Indiana Jones
  • Otras peliculas interesantes son “El resplandor” y “Volver al Futuro”

2.19 Peliculas con mejor nota 90’s

p90 <- filter(peliculas4, (peliculas4$date >= 1990 & peliculas4$date< 2000))

head(p90[order(p90$promrating, decreasing = TRUE),c(3,6,2)],20)
NA
  • La pelicula con mejor nota de los 90´s es “La Lista de Schinder”
  • Otras interesantes son “Toy Story”,“Forrest Gump”.

2.20 Peliculas con mejor nota 00’s

p00 <- filter(peliculas4, (peliculas4$date >= 2000))

head(p00[order(p70$promrating, decreasing = TRUE),c(3,6,2)],20)
NA
  • Se puede observar un claro descenso en los promedios de notas de esta decada, hay que dejar en claro que son peliculas estrenadas hasta 2005.

2.21 Generos mas Rrecurrentes 70’s

t70 <- table(p70$genres)
barplot( sort(t70, decreasing=TRUE)[1:5], space = 0.5, cex.names = 0.70)

sort(t70, decreasing = TRUE)[1:10]

                Drama                Horror                Comedy 
                   43                    25                    20 
   Action,Crime,Drama         Drama,Romance          Comedy,Drama 
                   19                    17                    16 
 Comedy,Drama,Romance         Horror,Sci-Fi Action,Crime,Thriller 
                   14                    12                    11 
          Crime,Drama 
                   10 
  • El Drama es el genero mas popular o recurrente en los 70´s
  • El Horror es el segundo genero mas recurrente.

2.22 Generos mas Rrecurrentes 80’s

t80 <- table(p80$genres)
barplot( sort(t80, decreasing=TRUE)[1:5], space = 0.5, cex.names = 0.70)

sort(t80, decreasing = TRUE)[1:10]

              Comedy                Drama        Drama,Romance 
                  58                   57                   32 
        Comedy,Drama Comedy,Drama,Romance               Horror 
                  30                   30                   22 
     Horror,Thriller  Action,Comedy,Crime   Action,Crime,Drama 
                  17                   16                   16 
      Comedy,Romance 
                  15 
  • Comedia, que era el tercer genero en los 70´s se posiciona como primero en los 80´s
  • Desaparece Horror del top5 de generos.

2.23 Generos mas Rrecurrentes 90’s

t90 <- table(p90$genres)
barplot( sort(t90, decreasing=TRUE)[1:5], space = 0.5, cex.names = 0.70)

sort(t90, decreasing = TRUE)[1:10]

               Drama               Comedy Comedy,Drama,Romance 
                 160                   99                   78 
       Drama,Romance         Comedy,Drama       Comedy,Romance 
                  75                   74                   54 
  Action,Crime,Drama Crime,Drama,Thriller       Drama,Thriller 
                  39                   38                   28 
 Action,Comedy,Crime 
                  27 
  • Drama y Comedia siguen siendo los dos generos mas recurrentes en los 90´s

2.24 Generos mas Rrecurrentes 00’s

t00 <- table(p00$genres)
barplot( sort(t00, decreasing=TRUE)[1:5], space = 0.5, cex.names = 0.70)

sort(t00, decreasing = TRUE)[1:10]

               Drama               Comedy Comedy,Drama,Romance 
                 177                   77                   76 
       Drama,Romance          Documentary         Comedy,Drama 
                  66                   63                   57 
      Comedy,Romance          Crime,Drama   Action,Crime,Drama 
                  54                   33                   31 
Crime,Drama,Thriller 
                  27 
  • Drama y comedia siguen siendo los generos mas recurrentes en los 2000
  • Aparece en el top5 el genero documental

3. Cargar datos

3.1 Cargar los datasets de clasificacion(10% del dataset)

Aca cargamos el muestreo del dataset que el completo tiene 100 millones de observaciones, este muestre consta el del 10% del original. Quedando asi con 10 millones de observaciones.


dataset.raw <- read_csv("bq-results-20191202-190110-wfdzmx1qblfr.csv")
Parsed with column specification:
cols(
  IdPelicula = col_double(),
  IdCliente = col_double(),
  Rating = col_double(),
  Fecha = col_date(format = "")
)

Summary

El dataset de clasificaciones tiene 4 atributos los cuales son:

IdPelicula: el id de la pelicula cual clasifico el cliente.

IdCliente: el id del cliente que clasifico la pelicula.

Rating: la puntuacion que le puso que va desde el 1 al 5.

Fecha: la fecha que se la realizo, que es desde finales del año 1999 a finales de 2005

summary(dataset.raw)
   IdPelicula      IdCliente           Rating          Fecha           
 Min.   :    1   Min.   :      6   Min.   :1.000   Min.   :1999-11-11  
 1st Qu.: 2371   1st Qu.: 661712   1st Qu.:3.000   1st Qu.:2004-05-03  
 Median : 9368   Median :1318976   Median :4.000   Median :2005-01-20  
 Mean   : 8045   Mean   :1322632   Mean   :3.607   Mean   :2004-10-10  
 3rd Qu.:13763   3rd Qu.:1984819   3rd Qu.:4.000   3rd Qu.:2005-07-07  
 Max.   :17770   Max.   :2649429   Max.   :5.000   Max.   :2005-12-31  
head(dataset.raw)

Cantidad de ocurrencias por rating

Aca podemos ver como se distribuyen las votaciones como se puede ver hay mas votos positivos que negativos, osea mayores o igual a 3.

mt_mean <- dataset.raw %>% 
  group_by(Rating) %>% 
  dplyr::summarise(avg_count = dplyr::n())  

head(mt_mean)
barplot(mt_mean$avg_count,names.arg = c("1","2","3","4","5"))

Estructura

La estructura de de lso datos, en este caso se podria decir que tenemos 4 factores, pero como necesitamos ponerlos como factores en la utilizacion de las reglas de asociacion no lo tranformaremos, y tambien por temas de tecnicos de R que dan problemas con la cantidad de niveles de factores en la columna cliente y fecha por ejemplo.

str(dataset.raw)
Classes ‘spec_tbl_df’, ‘tbl_df’, ‘tbl’ and 'data.frame':    9758667 obs. of  4 variables:
 $ IdPelicula: num  11366 11376 11378 11405 11427 ...
 $ IdCliente : num  2594704 2283211 582829 174991 2170201 ...
 $ Rating    : num  1 1 1 1 1 1 1 1 1 1 ...
 $ Fecha     : Date, format: "2003-06-14" "2004-05-25" ...
 - attr(*, "spec")=
  .. cols(
  ..   IdPelicula = col_double(),
  ..   IdCliente = col_double(),
  ..   Rating = col_double(),
  ..   Fecha = col_date(format = "")
  .. )

3.2 Cargar dataset de peliculas

Bueno aca vamos a cargar el otro dataset que tenemos que son de las peliculas.

dataset.movies <- read_csv("movie_titles.csv",col_names = c("IdPelicula","date","original_title"))
Parsed with column specification:
cols(
  IdPelicula = col_double(),
  date = col_double(),
  original_title = col_character()
)
343 parsing failures.
row col  expected    actual               file
 72  -- 3 columns 4 columns 'movie_titles.csv'
264  -- 3 columns 5 columns 'movie_titles.csv'
350  -- 3 columns 4 columns 'movie_titles.csv'
366  -- 3 columns 4 columns 'movie_titles.csv'
394  -- 3 columns 4 columns 'movie_titles.csv'
... ... ......... ......... ..................
See problems(...) for more details.

3.3 Dataset externo

Bueno aca sacamos otro dataset con peliculas que tiene otros datos aparte de los que ya tenemos, lo trataremos mas tarde. Pero tiene estos daots

dataset.movies.metadata <- read_csv("movies_metadata.csv")
Parsed with column specification:
cols(
  .default = col_character(),
  adult = col_logical(),
  budget = col_double(),
  id = col_double(),
  popularity = col_double(),
  release_date = col_date(format = ""),
  revenue = col_double(),
  runtime = col_double(),
  video = col_logical(),
  vote_average = col_double(),
  vote_count = col_double()
)
See spec(...) for full column specifications.
19 parsing failures.
  row          col               expected                           actual                  file
19730 NA           24 columns             10 columns                       'movies_metadata.csv'
19731 adult        1/0/T/F/TRUE/FALSE     - Written by Ørnås             'movies_metadata.csv'
19731 budget       a double               /ff9qCepilowshEtG2GYWwzt2bs4.jpg 'movies_metadata.csv'
19731 id           no trailing characters -08-20                           'movies_metadata.csv'
19731 release_date date like              1                                'movies_metadata.csv'
..... ............ ...................... ................................ .....................
See problems(...) for more details.
head(dataset.movies.metadata)
summary(dataset.movies.metadata)
   adult         belongs_to_collection     budget         
 Mode :logical   Length:45466          Min.   :        0  
 FALSE:45454     Class :character      1st Qu.:        0  
 TRUE :9         Mode  :character      Median :        0  
 NA's :3                               Mean   :  4224579  
                                       3rd Qu.:        0  
                                       Max.   :380000000  
                                       NA's   :3          
    genres            homepage               id        
 Length:45466       Length:45466       Min.   :     2  
 Class :character   Class :character   1st Qu.: 26450  
 Mode  :character   Mode  :character   Median : 60003  
                                       Mean   :108360  
                                       3rd Qu.:157328  
                                       Max.   :469172  
                                       NA's   :3       
   imdb_id          original_language  original_title    
 Length:45466       Length:45466       Length:45466      
 Class :character   Class :character   Class :character  
 Mode  :character   Mode  :character   Mode  :character  
                                                         
                                                         
                                                         
                                                         
   overview           popularity       poster_path       
 Length:45466       Min.   :  0.0000   Length:45466      
 Class :character   1st Qu.:  0.3859   Class :character  
 Mode  :character   Median :  1.1277   Mode  :character  
                    Mean   :  2.9215                     
                    3rd Qu.:  3.6789                     
                    Max.   :547.4883                     
                    NA's   :6                            
 production_companies production_countries  release_date       
 Length:45466         Length:45466         Min.   :1874-12-09  
 Class :character     Class :character     1st Qu.:1978-10-06  
 Mode  :character     Mode  :character     Median :2001-08-30  
                                           Mean   :1992-05-15  
                                           3rd Qu.:2010-12-17  
                                           Max.   :2020-12-16  
                                           NA's   :90          
    revenue             runtime        spoken_languages  
 Min.   :0.000e+00   Min.   :   0.00   Length:45466      
 1st Qu.:0.000e+00   1st Qu.:  85.00   Class :character  
 Median :0.000e+00   Median :  95.00   Mode  :character  
 Mean   :1.121e+07   Mean   :  94.13                     
 3rd Qu.:0.000e+00   3rd Qu.: 107.00                     
 Max.   :2.788e+09   Max.   :1256.00                     
 NA's   :6           NA's   :263                         
    status            tagline             title          
 Length:45466       Length:45466       Length:45466      
 Class :character   Class :character   Class :character  
 Mode  :character   Mode  :character   Mode  :character  
                                                         
                                                         
                                                         
                                                         
   video          vote_average      vote_count     
 Mode :logical   Min.   : 0.000   Min.   :    0.0  
 FALSE:45367     1st Qu.: 5.000   1st Qu.:    3.0  
 TRUE :93        Median : 6.000   Median :   10.0  
 NA's :6         Mean   : 5.618   Mean   :  109.9  
                 3rd Qu.: 6.800   3rd Qu.:   34.0  
                 Max.   :10.000   Max.   :14075.0  
                 NA's   :6        NA's   :6        

4. Reglas de asociacion

4.1 Juntar dataset

Aquí juntamos los dataset usando a la columna IdPelicula que tiene ambos, luego los filtramos dejando solo los que son igual o mayor a 3, ya que consideramos que a partir del rating 3 se podría decir que es una película que mínimamente nos agrada. Por último, solo dejamos el idCliente y el título.

data.345<-merge(x=dataset.raw,y=dataset.movies,by="IdPelicula")
data.345<- subset(data.345,data.345$Rating>=3)
data.345<-select(data.345,IdCliente,original_title)

4.2 Datos: Summary y str

Como podemos ver asi nos quedarian los dos atributos.

summary(data.345)
   IdCliente       original_title    
 Min.   :      6   Length:8331005    
 1st Qu.: 662352   Class :character  
 Median :1318652   Mode  :character  
 Mean   :1322416                     
 3rd Qu.:1984503                     
 Max.   :2649429                     
str(data.345)
'data.frame':   8331005 obs. of  2 variables:
 $ IdCliente     : num  433945 722591 885013 1877347 1168571 ...
 $ original_title: chr  "Dinosaur Planet" "Dinosaur Planet" "Dinosaur Planet" "Dinosaur Planet" ...

4.3 Inspeccion

Como vemos no hay nulos.

sapply(data.345, function(x){ sum(is.na(x)) })
     IdCliente original_title 
             0              0 
head(data.345)

4.4. Creando las transacciones

En este momento para realizar las reglas de asociaciones tenemos que juntar las películas clasificadas según el IdCliente, ósea, por cada cliente tendremos un listado de las películas clasificadas.

clasificacion.usuario <- ddply(data.345, c("IdCliente"),
function(x) paste(x$original_title, collapse = ","))

Aca se puede ver lo que dijimos anteriormente.

head(clasificacion.usuario)

4.5 Exportar transacciones.

Ahora sacamos el id del cliente del dataset y lo escribimos en un csv.


clasificacion.usuario$IdCliente<- NULL

write.csv(clasificacion.usuario, "clasificacion.csv", quote = FALSE,
row.names = FALSE)

4.6 Cargarlo en formato transacciones.

Con la función read.transactions podemos cargar el dataset en una estructura diferente la que nos permitirá realizar el análisis de las asociaciones.

dataset.itemset <- read.transactions("clasificacion.csv", format = "basket",
sep = ",",quote = "")
removing duplicated items in transactions

4.7 Graficos de frecuencia.

En estos gráficos se ve las películas que aparecen más frecuentes, para ser exacto las primeras 20. En una se muestra la frecuencia absoluta y la otra la relativa.

itemFrequencyPlot(dataset.itemset, topN = 20, type = "absolute",col = brewer.pal(8,"Pastel2"),
main = "Absolute Item Frequency Plot")

itemFrequencyPlot(dataset.itemset,topN=20,type="relative",col=brewer.pal(8,"Pastel2"),main="Relative Item Frequency Plot")

4.8 Busqueda de las reglas de asociacion.

En este momento, lo que se hace es buscar las reglas de asociacion que tengan un supply de al menos 0.00002 y un confidence de mas de 0.8 y hasta una longitud de 3 items.

association.rules <- apriori(dataset.itemset, parameter = list(supp=0.00002, conf=0.8,maxlen=3))
Apriori

Parameter specification:

Algorithmic control:

Absolute minimum support count: 8 

set item appearances ...[0 item(s)] done [0.01s].
set transactions ...[12779 item(s), 449236 transaction(s)] done [6.16s].
sorting and recoding items ... [11285 item(s)] done [0.18s].
creating transaction tree ... done [2.02s].
checking subsets of size 1 2
Mining stopped (time limit reached). Only patterns up to a length of 2 returned!
 done [6.58s].
writing ... [0 rule(s)] done [0.24s].
creating S4 object  ... done [0.31s].
summary(association.rules)
set of 0 rules

4.9 Reglas

Todas las reglas.

Aca podemos vizualizar las reglas obtenidas que nos dio un total de 28.

arules::inspect(association.rules)

Godfather

filtrado.reglas.GF <- subset(x = association.rules,
                          subset = rhs %ain% c("The Godfather"))
arules::inspect(filtrado.reglas.GF)

Acá tenemos varias reglas relacionadas con “The godfather” 1972: Es una película bastante famosa y catalogada como clásica, que es de drama y gánster implicando algunas escenas de violencia, thriller y suspenso.

1- Harol and maude: es una película de drama y thriller, de los 1971. Love and Death on Long Island: otra película de drama la cual es de 1997.

Acá vemos un claro patrón en el cual predomina el drama, ósea esta regla esta más enfocado a las personas que disfruta de este género.

2- Undertow: parece una de acción y policiaco de 2004.

weird Science: la típica película americana de adolescentes y una linda chica que aparece en poca ropa, del año 1985.

Lo que puedo pensar acá es que se vería reflejada el caso de las familias que tienen adolescentes, o un hombre adulto sin familia que vive solo. También teniendo en cuentas las fechas, es posiblemente que refleje la edad en que las vio teniendo en cuenta, que la segunda es de 1985 probablemente las veas entre 5 y 10 años después luego la primera maso menos en la fecha de estreno en conjunto a la del padrino la cual por su fama es posible que aún le llamara la atención en esos años. 3-

Alien Nation: película de ciencia ficción

The Adventures of Milo and Otis: película de perros para niño.

Puede ocurrir acá que es una regla de asociación donde se refleja una familia con niños de corta edad y bueno el padre que disfrutaría de las otras. Algo parecido como se verá luego.

4- Farscape: pelicula de ciencia ficcion.

Hamlet: película de drama sobre la obra de shakespeare.

Esta regla de asociación veo como orientado un poco a las personas que gozas de un buen guion o historia.

5- Husbands and Wives:pelicula de drama/romance.

Ricochet: pelicula de accion policiaco.

En esta regla de asociación podría predominar una pareja, por un lado el tema de acción y otro romance drama, y bueno el padrino que quizás pueda ser disfrutado por los dos. Es más capas que es la típica de que toca elegir por turno lo que van a ver y por se ve esta relación.

Pirates of the Caribbean: The Curse of the Black Pearl

filtrado.reglas.Piratas <- subset(x = association.rules,
                          subset = rhs %ain% c("Pirates of the Caribbean: The Curse of the Black Pearl"))
arules::inspect(filtrado.reglas.Piratas)

La de piratas del caribe (2003) es una película de aventura, fantasía, alguna que otras escenas de acción pero que por lo general destaca un poco también el humor. 1-

Allan Quatermain and the Lost City of Gold (1986): esta es una película de aventura y acción, medio parecida a la de indiana jones de búsquedas de tesoros y pirámides, etc. Probablemente algo de humor, pero sutil.

Fried Green Tomatoes (1991): es una película más orientada a comedio sobre un asilo y quizás un poco de drama, típica película para ver en familia.

En estas películas vemos no tienen realmente mucho parecido lo que si noto es que son peliculas las que son aptas para todo público, lo que me hace pensar que son las películas que se veían en familia. Además, se reflejarías las personas que le gusta las de aventura y humor, con algo de fantasía.

2- *Babylon 5: The River of Souls (1998): una película muy de ciencia ficción visto a primera vista.

*Harold and Kumar Go to White Castle(2004): típica película tonta que predomina las escenas de humor entre dos actores.

De acá lo único que puedo pensar teniendo en cuenta la amplia diferencia entre las películas o son de distintas personas de una misma familia. Aunque por otro lado también se podría decir que la ciencia ficción en esa época era usualmente más popular entre los adolescentes, y la segunda era la típica de humor que era también orientado a adolescentes. Por consiguiente, acá el patrón que quizás estamos viendo en esa etapa también apoyándonos en las fechas de estreno que son muy cercanas entre sí.

3-

*Batman Beyond: The Movie(1999): películas de superhéroes y acción, se podría decir que de suspenso muy popular usualmente entre los jóvenes y adolescentes.

*Simone(2002): película de fantasía sobre una chica de computadora que hizo un chico y que luego adquiere vida. Típica para adolescente.

Por ultimo, algo que nos dimos cuenta es que de estas de estas reglas podemos ver que estaría basado en los clientes los cuales sean una familia con adolescentes o los mismos adolescentes.

The Mummy

filtrado.reglas.mummy <- subset(x = association.rules,
                          subset = rhs %ain% c("The Mummy"))
arules::inspect(filtrado.reglas.mummy)

The Mummy (1999): una película de Fantasía/acción sobre una maldición de una momia, que revive entre los muertos y quiere vengarse y además tiene poderes. Bástate entretenida para ver. 1- Tremors 3- Back to Perfection(2001): Parece una película de acción y ciencia ficción, sobre alienígenas/ criaturas que atacan a los humanos y que tienen que defenderse. Waterworld(1995): ciencia ficción, acción y aventura, una película sobre un mundo lleno de agua en la que un hombre que viaja solo protege a una mujer y su hijo de piratas.

Bueno acá noto una relación muy lineal respecto a la temática refriéndonos a la fantasía y al aventura con un poco de acción, además predomina el tema de pelear con criaturas(en distinto tipo pero en si se podría catalogar en lo mismo), lo cual me hace pensar que es una regla de asociación con bastante sentido, aunque la primera a mi parecer no creo que sea tan buena película cumple con lo primero, también teniendo en cuenta que los datos usados abarcan las películas clasificadas entre 3 y 5.

2- *American Ninja(1985): se trata según la descripción de un soldado América que al parecer aprende a usar una espada y lucha con traficantes. Por lo visto una tendencia clara en acción y violencia.

*Falling Down(1993): sobre un ciudadano normal que empieza a recurrir a la violencia y hacer al parecer varios crímenes a causa del estrés o algo parecido. Mucha acción y violencia.

Como la anterior, tendríamos algo más lineal y con sentido, acá lo que predominaría seria las personas con gusto respecto a la violencia y la acción la cual quizás en la momia no sea demasiada, pero tiene lo justo de esto y tampoco es tan mala. Respecto a las fechas note que están en un rango de 14 años, a mi parecer no es significativo.

Bueno podemos concluir que acá, tenemos dos casos muy marcados por un lado la parte de la acción y violencia, y por otro la de ciencia ficción y aventuras.

While You Were Sleeping

filtrado.reglas.WYWS <- subset(x = association.rules,
                          subset = rhs %ain% c("While You Were Sleeping"))
arules::inspect(filtrado.reglas.WYWS)

while you were sleeping pelicula(1995): película de drama y romance.

Three’s Company(1977): seria de dos chicas que encuentran un compañero de cuarto. Muy probable implicando drama y romance Baby boom (1987): película sobre una ejecutiva que tiene que cuidar al bebe de su primo. Drama y romance.

Bueno esta es una regla de asociación bastante fácil de interpretar, ósea tenemos un claro patrón de drama y romance, lo que hace pensar que serían los casos de las amas de casa las cuales serían muy de su estilo este tipo de películas y series.

Guarding Tess

filtrado.reglas.GT <- subset(x = association.rules,
                          subset = rhs %ain% c("Guarding Tess"))
arules::inspect(filtrado.reglas.GT)

En este caso son peliculas que desconosco bastante pero viendo la sipnapsis que dan en google que son estas:

Guarding Tess (1994): “Una ex primera dama de los Estados Unidos quiere que un agente del Servicio Secreto en particular encabece los detalles de su guardaespaldas, a pesar de que no puede soportarla.”

Fletch Lives(1989):“Después de recibir una herencia en Louisiana, el periodista de Los Ángeles Irvin Fletcher se dirige a la plantación de Belle Isle, donde se mete en problemas hilarantes y se topa con asesinato.”

Mad City(1997):“Cuando el museo en el que trabaja el guardia de seguridad Sam Baily le despide, éste se desespera. Dispuesto a todo para recuperar su trabajo, Baily asalta el museo con un arma y toma como rehenes a un grupo de niños y a la directora del centro. Max Brackett, un periodista caído en desgracia, ve en el suceso la gran oportunidad de realizar el reportaje de su vida. Así, convertirá la reivindicación laboral de Sam en un circo mediático de consecuencias imprevisibles.”

Analisando un poco las sipnapsis y los generos, llegamos a la conclucion de que estas peliculas comparten caracteristicas en comun del tipo que tienen las 3 toques de humor y estan ambientadas a lo que se refiere a los crimenes o mejor dicho en la parte policial de una formu u otra, visto desde distintos puntos en cada una pero llega a lo mismo. Ademas los años de estrenos son muy proximos.

Shrek 2

filtrado.reglas.S2 <- subset(x = association.rules,
                          subset = rhs %ain% c("Shrek 2"))
arules::inspect(filtrado.reglas.S2)

Shrek 2(2004): película popular de disney sobre fantasía y aventura.

The Andy Griffith Show(1960): un show sobre un alguacil de pueblo que vive con su hijo y su tia. Típico show para la familia.

Something’s Gotta Give(2003): es una película romántica sobre un hombre de unos 60 y pico que suele salir con mujeres jóvenes y en una ocasión se enamorada de la madre de una de sus novias.

Bueno acá tenemos cosas muy distintas entre sí, pero algo a destacar es que es el típico patrón que presentaría una familia, por un lado, la romántica que le gustaría a la madre o las mujeres de la familia. Un show que usualmente se lo vería en familia ósea que podría ser apreciado por todos. Y bueno shrek 2 que es también factible que se lo vea en familia porque también puede gustar a los adultos, pero mucho más enfocados a los hijos.

The Sixth Sense

filtrado.reglas.TSS <- subset(x = association.rules,
                          subset = rhs %ain% c("The Sixth Sense"))
arules::inspect(filtrado.reglas.TSS)

The sixth Sense: es una pelicula medio se podria decir suspenso y terror.

Airplane!: es una parodia comica.

The A-Team: es una serie famosa que tiene toques de humor con accion.

Aca puede ser que este el tipico comportamiento que puede pasar, de buscar peliculas de humor luego de peliculas de miedo como para relajarse y distraerse para dormir.

Finding Nemo (Widescreen)

filtrado.reglas.FN <- subset(x = association.rules,
                          subset = rhs %ain% c("Finding Nemo (Widescreen)"))
arules::inspect(filtrado.reglas.FN)

Finding Nemo (2003): pelicula de disney muy conocida.

The Rundown(2003): pelicula de accion y suspenso, donde actua “la roca”.

The Bourne Identity(2002): Pelicula de accion y suspenso, de un hombre que tiene amnesia y lo quieren matar.

Bueno de aca podemos sacar que las dos otras por primera vista no tendria sentido, pero si lo podemos ver por parte que son 2 peliculas de accion las cuales suelen ser vistas en su mayoria por hombre, que pueden tener hijos eso ya tiene mas sentido. Osea por una parte estarian lo que vieron los padres y despues nemo orientado a sus hijos.

Freaky Friday

filtrado.reglas.FF <- subset(x = association.rules,
                          subset = rhs %ain% c("Freaky Friday"))
arules::inspect(filtrado.reglas.FF)

Freaky Friday(2003): es una pelicula sobre una madre y una hija que se peliaban mucho, y por algo de una maldicion o algo asi cambian de cuerpo y viven la vida de la otra. Es una pelicula que personalmente vi en mi adolecencia y me gusto.

Superstar(1999):“Mary Gallagher, estudiante de colegio católico, sueña con ganar un concurso de talento y besar a un futbolista famoso.” Tipica pelicula para adolecente. Ellen DeGeneres: The Beginning(2000): Bueno aca hay un tipo show de Ellen que es una mujer segun lo que vi por internet y tambien otras peliculas es una mujer popular en estados unidos para gente de distinta edad.

Lo que se puede notar de estas peliculas es que son enfocadas a los adolecentes, ademas estan muy cerca en los rangos de estrenos osea de la misma epoca. Tambien cabe la posibilidad de que este relacionada con las chicas que tienen problemas con descubrir quienes son o mas digamos a la parte de homosexualidad o de querer ser otra persona. Es decir de gente que no se siente comoda consigo mismo y se siente reconfortadas por los de temas de cambio de personalidad, ser otra persona y ellen que es un celebridad homosexual que actualmente esta casada con una mujer.

The Silence of the Lambs

filtrado.reglas.TSofL <- subset(x = association.rules,
                          subset = rhs %ain% c("The Silence of the Lambs"))
arules::inspect(filtrado.reglas.TSofL)

Mr. Show: serie de comedia.

Zataoichi: parece una pelicula de accion y samurai.

The Silence of the lamps: pelicula de suspenso y no se si terror pero debe tener un poco.

Esta asociacion me hace acordar mucho de el sexto sentido, la cual tiene peliculas similares. Esto hace pensar que debe haber personas que quizas no sea como se dijo antes de ver comedia luego de la de terror. Sino quizas sean unos generos muy asociados a algunas tipos de personas en particular la cual disfrutan las pelicuals de suspeso, series de comedia y accion. Podria ser o queremos creer que debe ser a personas masculinas de edad media.

The Amityville Horror

filtrado.reglas.AH <- subset(x = association.rules,
                          subset = rhs %ain% c("The Amityville Horror"))
arules::inspect(filtrado.reglas.AH)

The Amityville Horror(2005): es una pelicula de terror y al parecer sangrientas por que se trata de asesinatos.

Candyman(1996): esta parece una pelicula de terror medio parecida a freddy krueger o algo asi, que al parecer es un hombre que es un asesino basado en crimenes.

Daylight(1992): es la una pelicula de suspenso en la que actual stallone, en la cual se trata de unas personas que se quedan atrapadas dentro de un tunel luego de unas explosiones e intenar salir. Y por las circunstancias hay escenas de suspensos, tension y a veces sangrientas pero mas por las personas que se lastiman.

El patron que se describe aca es horientada a las escenas con sangre y tension, quizas mas a tension. Son las personas que les gusta esa sensacion y disfrutan de este tipo de peliculas.

Love Actually

filtrado.reglas.LA <- subset(x = association.rules,
                          subset = rhs %ain% c("Love Actually"))
arules::inspect(filtrado.reglas.LA)

Bridget Jones’s Diary

filtrado.reglas.BJD <- subset(x = association.rules,
                          subset = rhs %ain% c("Bridget Jones's Diary"))
arules::inspect(filtrado.reglas.BJD)

Esta es muy parecida al análisis, While You Were Sleeping, porque tiene mucho tema de romance, aunque también podríamos ver otra similitud respecto a la protagonistas principales que las 3 son como chicas que buscan a una pareja quizás medio soñada o algo así.

The Wizard of Oz: Collector’s Edition

filtrado.reglas.Oz <- subset(x = association.rules,
                          subset = rhs %ain% c("The Wizard of Oz: Collector's Edition"))
arules::inspect(filtrado.reglas.Oz)

The Wizard of Oz: Película clásica de fantasía y aventura

A Nightmare on Elm Street 3: Dream Warriors: clásico de terror y miedo donde aparece Freddy Krueger. Madeline: aventura y comedia, muy tranquila al parecer.

En este caso podemos ver un comportamiento mejor descripto de lo hablado anteriormente sobre las películas de terror y luego algo más tranquilo para pasar el susto. Teniendo en cuenta que tenemos a izquierda una de terror, que creo recordar que daba bastante miedo y otra tranquila. Y como la parte derecha el mago de oz, podemos ver que se muestra de forma más clara el hecho que de ver una película de terror siendo una persona la cual disfruta de películas como madeline, va a ser muy probable que veas mago de oz ya que es popular y tranquila. Ósea podríamos identificar a las personas que no están acostumbradas a ver películas de terror o la sienten demasiado.

Sleepless in Seattle

filtrado.reglas.SinS <- subset(x = association.rules,
                          subset = rhs %ain% c("Sleepless in Seattle"))
arules::inspect(filtrado.reglas.SinS)

Lethal Weapon

filtrado.reglas.LW <- subset(x = association.rules,
                          subset = rhs %ain% c("Lethal Weapon"))
arules::inspect(filtrado.reglas.LW)

No se ve un comportamiento interesante ya que son peliculas de accion deporte etc. La cual estaria enfocado a hombre de mediana edad. ### American Beauty

filtrado.reglas.AB <- subset(x = association.rules,
                          subset = rhs %ain% c("American Beauty"))
arules::inspect(filtrado.reglas.AB)

Barbershop

filtrado.reglas.barber <- subset(x = association.rules,
                          subset = rhs %ain% c("Barbershop"))
arules::inspect(filtrado.reglas.barber)

Barbershop: comedia.

The cookout: comedia.

The hitcher: accion y policiaco.

Acá hay algo interesante que puede ser algo que tenga ver con la gente de color, teniendo en cuenta que empezó en EEUU. Barbershop es una película de comedia donde aparece la típica familia de color, the cookout más de lo mismo. Pero por otro lado the hitcher es como al parecer muy orientado a los “blancos”. Lo que me hace pensar teniendo en cuenta que por lo general los adolescentes de color son más liberales con temas de películas, música o parece respecto al tema racial (Según lo que veo en películas, no sé si será así verdaderamente, pero parece que siempre son más necios los adultos respecto a estos algo de verdad debe tener).

Moonstruck

filtrado.reglas.Moon <- subset(x = association.rules,
                          subset = rhs %ain% c("Moonstruck"))
arules::inspect(filtrado.reglas.Moon)

Películas orientados a adolescentes más especialmente al género femenino.

The Recruit

filtrado.reglas.recruit <- subset(x = association.rules,
                          subset = rhs %ain% c("The Recruit"))
arules::inspect(filtrado.reglas.recruit)

Acá hay unas reglas extrañas primero tenemos Hart´s War de guerra acción y The Recruit más de los mismo. Mientras que Sex and the city es poco más orientado a mujeres, lo que creo que puede ser que se vea el tema de las parejas jóvenes.

4.10 Propuesta de utilizacion

Estos datos los podriamos utilizar de varias formas, primero pensando en esa epoca era de alquiler de peliculas no como ahora de streaming, lo que se podria utilizar seria poner combos de descuento por comprar/alquilar 2 peliculas mas las cuales aparescan en las que tengan una regla de asociacion para asi potenciar el negocio y las ventas/alquiler como lo hacer amazon y otras paginas de venta.

Viendolo desde la pelicula actual de netflix como plataforma de streaming la utilizadad o la aplicacion de esta quizas ya es algo que hace netflix como la recomendacion por ver 1 o 2 peliculas te recomiendan otras mas, en una lista de varias peliculas. Tambien a la finalizacion de una pelicula/serie, la recomendaciones esas que te aparecen 2 o 3 peliculas varias series que te dice que te pueden gustar tambien.

Aunque, la otra idea que se me ocurrio es algo que en realidad no se si aplicara netflix, la que seria el tema de gestion de la disponibilidad de peliculas por paises, bueno teniendo en cuenta que tenes en nuestros casos 3 peliculas relacionadas por la regla de la asociacion podrias como “predecir” o mas bien dicho tomar decisiones de si meter o sacar peliculas, por ejemplo, que tengas en x pais dos peliculas de la parte izquierda la cual tenga mucho recibimiento por ese pais y mucha vizualizaciones, clasificiaciones positivas,etc. Podrias tomar la decision si es que no estaria disponible ya la pelicula de la mano derecha, agregarla a ese pais ya que teniendo en cuenta q la mayoria de las peliculas tienen una confianza del 80% entonces de ante manos sabrias que con esa pelicula pasaria algo parecido.

5. Cluster: Peliculas populares

5.1 Preparacion de los datos.

Primero vamos a realizar el calculo del rating promedio de las peliculas y la cantidad de clasificaciones que se le hicieron una pelicula para poder realizar el analisis de cuales serian las peliculas populares.

mt_mean <- dataset.raw %>% 
  group_by(IdPelicula) %>% 
  dplyr::summarise(rating_mean = mean(Rating))  
mt_count <- dataset.raw %>% 
  group_by(IdPelicula) %>% 
  dplyr::summarise(rating_count = dplyr::n())  

mt_completo<- merge(mt_mean,mt_count,by= "IdPelicula")

5.2 Escalado de datos.

Escalamos los datos excepto el de IdPeliculas.

dataset.scaled <- scale(mt_completo[,-1])

5.3 Elegir tamaños de cluster.

Graficamos un plot que nos dice el numero optimo según el cluster por número de cluster. De la cual elegiremos el tamaño 6.

fviz_nbclust(dataset.scaled, kmeans,method = "wss")

set.seed(1243)
k.means.fit <- kmeans(dataset.scaled, centers = 6, nstart = 100)
print(k.means.fit)
K-means clustering with 6 clusters of sizes 1421, 4328, 123, 566, 3021, 3597

Cluster means:
  rating_mean rating_count
1  -1.8891367   -0.2919799
2   0.2354406   -0.1633820
3   0.9697605    7.6325974
4   0.7045602    2.4728662
5   1.1709116   -0.1125407
6  -0.6644173   -0.2436601

Clustering vector:
   [1] 5 5 5 6 5 2 1 2 6 6 6 2 5 6 2 6 6 5 2 2 2 1 2 6 5 6 2 4 2 3 2 5 5
  [34] 6 6 6 5 1 1 6 1 1 1 5 6 5 2 5 5 2 5 5 1 2 6 5 5 5 6 1 6 2 2 6 1 6
  [67] 6 5 2 6 5 2 5 6 6 5 6 6 5 6 2 2 4 2 5 1 6 5 6 2 5 2 6 2 6 5 5 5 6
 [100] 6 2 6 2 5 6 5 2 4 6 2 4 2 5 2 5 2 2 4 6 1 5 6 1 1 2 2 6 2 1 1 6 2
 [133] 5 1 5 6 6 5 1 1 6 6 4 6 2 1 1 4 6 6 1 5 6 6 5 2 2 6 5 6 5 6 6 6 5
 [166] 2 5 2 2 2 5 2 2 2 3 2 6 2 6 5 6 6 6 2 6 2 4 6 2 1 3 6 2 6 2 2 3 6
 [199] 4 6 2 2 2 6 2 5 2 2 5 2 2 6 2 6 5 2 6 2 1 2 2 1 4 5 6 1 2 6 5 6 6
 [232] 2 6 6 6 1 2 5 5 5 4 5 2 5 1 2 2 5 2 2 5 4 5 1 2 2 4 2 2 6 5 5 5 2
 [265] 2 6 2 5 4 4 2 6 4 6 5 1 2 1 2 2 5 5 5 1 2 6 5 2 1 4 1 5 5 2 4 6 6
 [298] 6 3 1 6 6 6 5 5 2 2 6 6 2 4 3 3 2 2 5 6 1 2 6 6 6 1 6 5 2 2 6 3 4
 [331] 4 1 5 4 1 2 6 5 5 6 6 5 2 5 5 6 1 2 1 5 2 6 4 1 1 2 3 2 5 6 4 1 6
 [364] 2 2 2 2 2 6 2 5 1 6 2 6 2 1 6 6 2 2 2 6 5 5 6 2 1 6 6 2 6 2 2 5 2
 [397] 5 6 1 5 2 2 2 1 5 4 6 2 2 5 6 6 4 6 1 6 5 6 5 5 5 2 6 2 5 2 6 6 2
 [430] 5 2 5 2 2 6 5 5 6 2 6 5 4 4 5 2 6 2 2 6 2 5 6 2 5 5 5 3 2 2 1 2 5
 [463] 5 6 6 5 2 4 6 6 5 2 4 2 6 5 5 2 5 6 2 4 3 2 5 5 2 5 2 6 2 6 6 4 2
 [496] 5 2 5 6 5 5 5 2 5 2 6 5 6 6 2 6 6 1 5 1 4 2 5 5 5 6 2 6 2 1 6 6 4
 [529] 2 1 2 2 2 2 2 6 2 6 5 2 6 1 2 5 2 6 6 6 6 4 2 6 2 2 5 6 6 2 5 5 5
 [562] 6 2 2 6 6 6 6 2 6 3 2 2 6 5 5 5 1 5 6 6 6 2 2 5 2 5 2 1 2 6 1 6 1
 [595] 5 6 1 2 2 2 2 6 5 2 6 2 3 1 5 6 1 6 2 6 2 6 5 1 5 2 5 6 2 2 1 2 6
 [628] 2 2 6 5 6 2 5 6 2 5 2 2 2 5 5 2 6 5 5 2 2 1 6 6 2 6 6 2 1 5 4 6 4
 [661] 2 2 5 1 6 6 5 5 1 2 2 2 5 2 6 2 5 2 1 6 2 5 6 6 2 2 2 2 2 5 6 4 6
 [694] 5 6 2 2 6 5 5 1 6 5 6 4 2 2 3 2 5 5 5 6 1 2 6 6 2 6 4 1 5 6 5 2 1
 [727] 6 2 1 5 6 2 2 5 6 1 2 6 6 6 2 6 2 6 1 4 2 5 1 2 2 5 5 2 2 6 6 3 2
 [760] 5 6 5 2 5 1 6 2 2 2 2 2 6 6 5 2 6 5 6 6 5 2 1 2 2 2 2 1 4 4 6 2 6
 [793] 2 2 2 6 6 3 5 6 6 6 1 5 5 2 2 2 2 6 6 2 2 2 6 2 2 2 2 1 1 5 6 5 5
 [826] 2 6 6 2 6 4 5 2 1 2 2 5 6 5 5 6 2 5 6 5 6 2 2 2 2 4 2 6 1 1 6 5 6
 [859] 2 6 5 4 5 6 5 2 6 2 2 1 5 4 6 6 6 2 2 1 6 5 2 1 2 6 2 3 6 6 5 2 5
 [892] 5 5 6 5 4 2 6 2 5 5 1 2 1 1 2 5 5 6 6 6 2 2 2 1 2 5 5 2 1 2 6 2 1
 [925] 5 1 2 6 2 6 6 1 5 2 5 2 4 1 5 4 1 6 5 6 6 6 5 6 2 6 2 5 2 2 2 6 5
 [958] 5 6 2 2 4 4 5 2 6 2 6 1 2 5 2 5 5 1 5 6 2 6 5 6 5 1 2 3 2 2 5 2 5
 [991] 5 5 6 2 1 5 2 2 6 2
 [ reached getOption("max.print") -- omitted 12056 entries ]

Within cluster sum of squares by cluster:
[1] 383.4311 605.2349 684.6475 812.2432 660.5269 411.0842
 (between_SS / total_SS =  86.4 %)

Available components:

[1] "cluster"      "centers"      "totss"        "withinss"    
[5] "tot.withinss" "betweenss"    "size"         "iter"        
[9] "ifault"      

5.4 Pesos de los atributos segun los clusters.

El tercer cluster es el que tiene los valores del rating_mean como el de rating_count tanto positivo, altos y equilibrados(no mucho de uno y poco del otro) por lo cual sería el cluster según los datos el que nos interesaría.

k.means.fit$centers
  rating_mean rating_count
1  -1.8891367   -0.2919799
2   0.2354406   -0.1633820
3   0.9697605    7.6325974
4   0.7045602    2.4728662
5   1.1709116   -0.1125407
6  -0.6644173   -0.2436601

5.5 Tamaño de los clusters.

Aca se puede ver que el cluster 3 es el que tiene menos peliculas en el. Segun venimos viendo esto quizas no haga mas facil el filtrado.

k.means.fit$size
[1] 1421 4328  123  566 3021 3597

5.6 Vizualizacion de los clusters

En este plot podemos notar que el cluster gris es el de las películas “populares”, mientras que el rojo son las películas promedias ni mala ni buenas. Luego las que quedan es un conjunto maso menos quizás de las que menos se ven o quizás las que apenas salen. La celeste claro por ejemplo debe ser las películas nuevas pero que está teniendo una buena puntuación mientras la celeste fuertes las que nadie ve y a nadie gusta. La azul la que son medianamente malas pero que las ven con más frecuencias. Y la amarilla la que no son ni malas ni buena, pero tampoco son muy vistas.

fviz_cluster(object = k.means.fit, data = dataset.scaled, ellipse.type = "norm", geom = "point", main = "Cluster pelciulas ", stand = FALSE, palette = "jco") + theme_bw() + theme(legend.position = "none")

5.7 Validacion del cluster con datos simulados

Realizamos la simulación de los datos, y podemos ver que en las primeras dos componentes representan el 100% de los datos por lo tanto nuestros datos y por consiguiente los clusters deberían servirnos.

data.simulated <- purrr::map_df(mt_completo[,-1], .f = function(x){ runif(n = length(x), min = min(x), max = max(x)) })


 data.simulated.scaled <- scale(data.simulated)
k.means.simulados <- kmeans(x = data.simulated.scaled, centers = 6)

fviz_cluster(object = k.means.simulados, data = data.simulated.scaled, ellipse.type = "norm", geom = "point", main = "Datos ", stand = FALSE, palette = "jco") + theme_bw() + theme(legend.position = "none")

5.8 Filtrado de los datos y lista de recomendacions.

Bueno luego de tomar el cluster 3, lo volvemos a asociar con nuestro dataset original el cual tiene los títulos de las películas, y ordenamos de forma descendente por el rating_mean, entonces tomamos los primeros 20 películas, y se puede ver que esa serían las películas populares de nexflix, como se puede ver a simple vista son películas que muchos de nosotros nos gusta y sabemos que son famosas y buenas películas.

plot_data <- cbind(mt_completo, k.means.fit$cluster)
recomendadas<- subset(plot_data, k.means.fit$cluster==3)
recomendadas<-merge(x=recomendadas,y=dataset.movies,by="IdPelicula")
recomendadas = recomendadas[order(recomendadas$rating_mean,decreasing = TRUE),]
head(recomendadas,n=20)

6. Word Cloud

6.1 Juntar los dataset de peliculas

peliculas.cloud<- merge(dataset.movies,d_overview,by="original_title")
peliculas.cloud.select<-select(peliculas.cloud,original_title,IdPelicula,overview)
peliculas.cloud.select<- merge(peliculas.cloud.select,d_generos,by.x="original_title", by.y="originalTitle")
peliculas.cloud.select<-select(peliculas.cloud.select,original_title,IdPelicula,overview,genres)

6.2 Limpiar datos que nulos

sapply(peliculas.cloud.select, function(x) sum(is.na(x)))
original_title     IdPelicula       overview         genres 
             0              0            658              0 
peliculas.cloud.select <- na.omit(peliculas.cloud.select)

6.2 Limpiar datos que duplicados

peliculas.cloud.clean <- peliculas.cloud.select %>% distinct(original_title, .keep_all = TRUE)

6.3 Tamaño del dataset limpio

dim(peliculas.cloud.clean)
[1] 7195    4

6.4 Buscar los clientes que realizo las mayores clasificaciones positivas.

cliente_count <- dataset.raw %>% 
 filter(Rating>=3)  %>%
  group_by(IdCliente) %>% 
  dplyr::summarise(peliculas_count = dplyr::n()) 

cliente_count=cliente_count[order(cliente_count$peliculas_count,decreasing = TRUE),]

head(cliente_count)
  • El cliente que puntuo más veces lo hizo en 1558 oportunidades.

6.5 Word Cloud por cliente

Cliente 1664010

Word cloud por titulo.

Vemos las palabras mas frecuentes, y tenemos que quitar las que consideremos que no deberian ir.

cliente<-dataset.raw %>% 
  filter(IdCliente==1664010)  %>% 
 filter(Rating>=3)  

cliente<- merge(cliente,peliculas.cloud.clean,by="IdPelicula")


cliente <- cliente %>% select(IdPelicula,original_title,overview,IdCliente,Rating,Fecha)
names(cliente)[names(cliente) == 'IdPelicula'] <- 'doc_id'
names(cliente)[names(cliente) == 'original_title'] <- 'text'



movies_source <- DataframeSource(cliente)
movies_corpus <- VCorpus(movies_source)


movies_corpus <- movies_corpus %>% 
                tm_map(removePunctuation) %>%
                tm_map(content_transformer(tolower)) %>%
                tm_map(removeWords, c(stopwords("en"),"dont",
                       "can","just", "cant", "get", "got","one","two"))
                # tm_map(content_transformer(replace_number)) %>%
                # tm_map(content_transformer(bracketX)) %>%
                # tm_map(stripWhitespace) 
                
#Stem document
clean_corpus <- tm_map(movies_corpus, stemDocument)
#Create Term Document Matrix
clean_tdm    <- TermDocumentMatrix(clean_corpus)
#Converting TDM to matrix for Analysis
clean_m      <- as.matrix(clean_tdm)



freqwords <- findFreqTerms(clean_tdm, lowfreq = 10)

# calculate the frequency of words
v <- sort(rowSums(clean_m), decreasing=TRUE)
myNames <- names(v)
k <- which(names(v)=="miners")
myNames[k] <- "mining"
d <- data.frame(word=myNames, freq=v)
wordcloud(d$word, d$freq, min.freq=3,max.words=30, random.order=FALSE,rot.per=0.35, colors=brewer.pal(8, "Dark2"))

Word cloud por Overview.

cliente<-dataset.raw %>% 
  filter(IdCliente==1664010)  %>% 
 filter(Rating>=3)  

cliente<- merge(cliente,peliculas.cloud.clean,by="IdPelicula")


cliente <- cliente %>% select(IdPelicula,overview,original_title,IdCliente,Rating,Fecha)
names(cliente)[names(cliente) == 'IdPelicula'] <- 'doc_id'
names(cliente)[names(cliente) == 'overview'] <- 'text'



movies_source <- DataframeSource(cliente)
movies_corpus <- VCorpus(movies_source)


movies_corpus <- movies_corpus %>% 
                tm_map(removePunctuation) %>%
                tm_map(content_transformer(tolower)) %>%
                tm_map(removeWords, c(stopwords("en"),"dont",
                       "can","just", "cant", "get", "got","one","two"))
                # tm_map(content_transformer(replace_number)) %>%
                # tm_map(content_transformer(bracketX)) %>%
                # tm_map(stripWhitespace) 
                
#Stem document
clean_corpus <- tm_map(movies_corpus, stemDocument)
#Create Term Document Matrix
clean_tdm    <- TermDocumentMatrix(clean_corpus)
#Converting TDM to matrix for Analysis
clean_m      <- as.matrix(clean_tdm)



freqwords <- findFreqTerms(clean_tdm, lowfreq = 10)

# calculate the frequency of words
v <- sort(rowSums(clean_m), decreasing=TRUE)
myNames <- names(v)
k <- which(names(v)=="miners")
myNames[k] <- "mining"
d <- data.frame(word=myNames, freq=v)
wordcloud(d$word, d$freq, min.freq=3,max.words=30, random.order=FALSE,rot.per=0.35, colors=brewer.pal(8, "Dark2"))

Word cloud por Genres.

cliente<-dataset.raw %>% 
  filter(IdCliente==1664010)  %>% 
 filter(Rating>=3)  

cliente<- merge(cliente,peliculas.cloud.clean,by="IdPelicula")


cliente <- cliente %>% select(IdPelicula,genres,original_title,overview,IdCliente,Rating,Fecha)
names(cliente)[names(cliente) == 'IdPelicula'] <- 'doc_id'
names(cliente)[names(cliente) == 'genres'] <- 'text'



movies_source <- DataframeSource(cliente)
movies_corpus <- VCorpus(movies_source)


movies_corpus <- movies_corpus %>% 
                tm_map(removePunctuation) %>%
                tm_map(content_transformer(tolower)) %>%
                tm_map(removeWords, c(stopwords("en"),"dont",
                       "can","just", "cant", "get", "got","one","two","name","878"))
                # tm_map(content_transformer(replace_number)) %>%
                # tm_map(content_transformer(bracketX)) %>%
                # tm_map(stripWhitespace) 
                
#Stem document
clean_corpus <- tm_map(movies_corpus, stemDocument)
#Create Term Document Matrix
clean_tdm    <- TermDocumentMatrix(clean_corpus)
#Converting TDM to matrix for Analysis
clean_m      <- as.matrix(clean_tdm)



freqwords <- findFreqTerms(clean_tdm, lowfreq = 10)

# calculate the frequency of words
v <- sort(rowSums(clean_m), decreasing=TRUE)
myNames <- names(v)
k <- which(names(v)=="miners")
myNames[k] <- "mining"
d <- data.frame(word=myNames, freq=v)
wordcloud(d$word, d$freq, min.freq=3,max.words=30, random.order=FALSE,rot.per=0.35, colors=brewer.pal(8, "Dark2"))

  • Por titulo: Man, Day, Boy, Big, Girl, (Fear, Run, Die, Blood)
  • Por Overview: Find, Man, Love, Friend, Life, Young, (Murder, Town, Kill)
  • Por Genero: Comedy, Drama, Documentary

  • Basado en los terminos mas frecuentes al usuario se le podria recomendar peliculas casuales de comedia, drama o comedias romanticas. Sin embargo aparecen terminos que podrian indicar cierto gusto o inclinacion del usuario hacia las peliculas y documentales sobre Asesinos debido a las palabras como “miedo”, “correr”, “muerte”, “sangre”, etc.

Cliente 716173

Word cloud por titulo.

Vemos las palabras mas frecuentes, y tenemos que quitar las que consideremos que no deberian ir.

cliente<-dataset.raw %>% 
  filter(IdCliente==716173)  %>% 
 filter(Rating>=3)  

cliente<- merge(cliente,peliculas.cloud.clean,by="IdPelicula")


cliente <- cliente %>% select(IdPelicula,original_title,overview,IdCliente,Rating,Fecha)
names(cliente)[names(cliente) == 'IdPelicula'] <- 'doc_id'
names(cliente)[names(cliente) == 'original_title'] <- 'text'



movies_source <- DataframeSource(cliente)
movies_corpus <- VCorpus(movies_source)


movies_corpus <- movies_corpus %>% 
                tm_map(removePunctuation) %>%
                tm_map(content_transformer(tolower)) %>%
                tm_map(removeWords, c(stopwords("en"),"dont",
                       "can","just", "cant", "get", "got","one","two"))
                # tm_map(content_transformer(replace_number)) %>%
                # tm_map(content_transformer(bracketX)) %>%
                # tm_map(stripWhitespace) 
                
#Stem document
clean_corpus <- tm_map(movies_corpus, stemDocument)
#Create Term Document Matrix
clean_tdm    <- TermDocumentMatrix(clean_corpus)
#Converting TDM to matrix for Analysis
clean_m      <- as.matrix(clean_tdm)



freqwords <- findFreqTerms(clean_tdm, lowfreq = 10)

# calculate the frequency of words
v <- sort(rowSums(clean_m), decreasing=TRUE)
myNames <- names(v)
k <- which(names(v)=="miners")
myNames[k] <- "mining"
d <- data.frame(word=myNames, freq=v)
wordcloud(d$word, d$freq, min.freq=3,max.words=30, random.order=FALSE,rot.per=0.35, colors=brewer.pal(8, "Dark2"))

Word cloud por Overview.

cliente<-dataset.raw %>% 
  filter(IdCliente==716173)  %>% 
 filter(Rating>=3)  

cliente<- merge(cliente,peliculas.cloud.clean,by="IdPelicula")


cliente <- cliente %>% select(IdPelicula,overview,original_title,IdCliente,Rating,Fecha)
names(cliente)[names(cliente) == 'IdPelicula'] <- 'doc_id'
names(cliente)[names(cliente) == 'overview'] <- 'text'



movies_source <- DataframeSource(cliente)
movies_corpus <- VCorpus(movies_source)


movies_corpus <- movies_corpus %>% 
                tm_map(removePunctuation) %>%
                tm_map(content_transformer(tolower)) %>%
                tm_map(removeWords, c(stopwords("en"),"dont",
                       "can","just", "cant", "get", "got","one","two"))
                # tm_map(content_transformer(replace_number)) %>%
                # tm_map(content_transformer(bracketX)) %>%
                # tm_map(stripWhitespace) 
                
#Stem document
clean_corpus <- tm_map(movies_corpus, stemDocument)
#Create Term Document Matrix
clean_tdm    <- TermDocumentMatrix(clean_corpus)
#Converting TDM to matrix for Analysis
clean_m      <- as.matrix(clean_tdm)



freqwords <- findFreqTerms(clean_tdm, lowfreq = 10)

# calculate the frequency of words
v <- sort(rowSums(clean_m), decreasing=TRUE)
myNames <- names(v)
k <- which(names(v)=="miners")
myNames[k] <- "mining"
d <- data.frame(word=myNames, freq=v)
wordcloud(d$word, d$freq, min.freq=3,max.words=30, random.order=FALSE,rot.per=0.35, colors=brewer.pal(8, "Dark2"))

Word cloud por Genres.

cliente<-dataset.raw %>% 
  filter(IdCliente==716173)  %>% 
 filter(Rating>=3)  

cliente<- merge(cliente,peliculas.cloud.clean,by="IdPelicula")


cliente <- cliente %>% select(IdPelicula,genres,original_title,overview,IdCliente,Rating,Fecha)
names(cliente)[names(cliente) == 'IdPelicula'] <- 'doc_id'
names(cliente)[names(cliente) == 'genres'] <- 'text'



movies_source <- DataframeSource(cliente)
movies_corpus <- VCorpus(movies_source)


movies_corpus <- movies_corpus %>% 
                tm_map(removePunctuation) %>%
                tm_map(content_transformer(tolower)) %>%
                tm_map(removeWords, c(stopwords("en"),"dont",
                       "can","just", "cant", "get", "got","one","two","name"))
                # tm_map(content_transformer(replace_number)) %>%
                # tm_map(content_transformer(bracketX)) %>%
                # tm_map(stripWhitespace) 
                
#Stem document
clean_corpus <- tm_map(movies_corpus, stemDocument)
#Create Term Document Matrix
clean_tdm    <- TermDocumentMatrix(clean_corpus)
#Converting TDM to matrix for Analysis
clean_m      <- as.matrix(clean_tdm)



freqwords <- findFreqTerms(clean_tdm, lowfreq = 10)

# calculate the frequency of words
v <- sort(rowSums(clean_m), decreasing=TRUE)
myNames <- names(v)
k <- which(names(v)=="miners")
myNames[k] <- "mining"
d <- data.frame(word=myNames, freq=v)
wordcloud(d$word, d$freq, min.freq=3,max.words=30, random.order=FALSE,rot.per=0.35, colors=brewer.pal(8, "Dark2"))

  • Por Titulo: Girl, Man, Soldier, Time, Day, Cowboy (Legend, American, Universe)
  • Por Overview: Find, Life, New,Man, Year, World, (Murder, American, School)
  • Por Genero: Comedy, Drama, Documentary.

  • Tambien a este usuario se le podria recomendar peliculas casuales de comedia, drama, comedias romanticas, etc. Palabras claves que pueden indicar cierto gusto por las peliculas de guerra, cowboys, o enfrentamientos. Se podria recomendar documentales acerca de Leyendas o heroes, personas americanas famosas y documentales sobre el Universo. Ademas documentales o peliculas acerca de Asesinos o masacres en Escuelas.

Cliente 1314869

Word cloud por titulo.

Vemos las palabras mas frecuentes, y tenemos que quitar las que consideremos que no deberian ir.

cliente<-dataset.raw %>% 
  filter(IdCliente==1314869)  %>% 
 filter(Rating>=3)  

cliente<- merge(cliente,peliculas.cloud.clean,by="IdPelicula")


cliente <- cliente %>% select(IdPelicula,original_title,overview,IdCliente,Rating,Fecha)
names(cliente)[names(cliente) == 'IdPelicula'] <- 'doc_id'
names(cliente)[names(cliente) == 'original_title'] <- 'text'



movies_source <- DataframeSource(cliente)
movies_corpus <- VCorpus(movies_source)


movies_corpus <- movies_corpus %>% 
                tm_map(removePunctuation) %>%
                tm_map(content_transformer(tolower)) %>%
                tm_map(removeWords, c(stopwords("en"),"dont",
                       "can","just", "cant", "get", "got","one","two"))
                # tm_map(content_transformer(replace_number)) %>%
                # tm_map(content_transformer(bracketX)) %>%
                # tm_map(stripWhitespace) 
                
#Stem document
clean_corpus <- tm_map(movies_corpus, stemDocument)
#Create Term Document Matrix
clean_tdm    <- TermDocumentMatrix(clean_corpus)
#Converting TDM to matrix for Analysis
clean_m      <- as.matrix(clean_tdm)



freqwords <- findFreqTerms(clean_tdm, lowfreq = 10)

# calculate the frequency of words
v <- sort(rowSums(clean_m), decreasing=TRUE)
myNames <- names(v)
k <- which(names(v)=="miners")
myNames[k] <- "mining"
d <- data.frame(word=myNames, freq=v)
wordcloud(d$word, d$freq, min.freq=3,max.words=30, random.order=FALSE,rot.per=0.35, colors=brewer.pal(8, "Dark2"))

Word cloud por Overview.

cliente<-dataset.raw %>% 
  filter(IdCliente==1314869)  %>% 
 filter(Rating>=3)  

cliente<- merge(cliente,peliculas.cloud.clean,by="IdPelicula")


cliente <- cliente %>% select(IdPelicula,overview,original_title,IdCliente,Rating,Fecha)
names(cliente)[names(cliente) == 'IdPelicula'] <- 'doc_id'
names(cliente)[names(cliente) == 'overview'] <- 'text'



movies_source <- DataframeSource(cliente)
movies_corpus <- VCorpus(movies_source)


movies_corpus <- movies_corpus %>% 
                tm_map(removePunctuation) %>%
                tm_map(content_transformer(tolower)) %>%
                tm_map(removeWords, c(stopwords("en"),"dont",
                       "can","just", "cant", "get", "got","one","two"))
                # tm_map(content_transformer(replace_number)) %>%
                # tm_map(content_transformer(bracketX)) %>%
                # tm_map(stripWhitespace) 
                
#Stem document
clean_corpus <- tm_map(movies_corpus, stemDocument)
#Create Term Document Matrix
clean_tdm    <- TermDocumentMatrix(clean_corpus)
#Converting TDM to matrix for Analysis
clean_m      <- as.matrix(clean_tdm)



freqwords <- findFreqTerms(clean_tdm, lowfreq = 10)

# calculate the frequency of words
v <- sort(rowSums(clean_m), decreasing=TRUE)
myNames <- names(v)
k <- which(names(v)=="miners")
myNames[k] <- "mining"
d <- data.frame(word=myNames, freq=v)
wordcloud(d$word, d$freq, min.freq=3,max.words=30, random.order=FALSE,rot.per=0.35, colors=brewer.pal(8, "Dark2"))

Word cloud por Genres.

cliente<-dataset.raw %>% 
  filter(IdCliente==1314869)  %>% 
 filter(Rating>=3)  

cliente<- merge(cliente,peliculas.cloud.clean,by="IdPelicula")


cliente <- cliente %>% select(IdPelicula,genres,original_title,overview,IdCliente,Rating,Fecha)
names(cliente)[names(cliente) == 'IdPelicula'] <- 'doc_id'
names(cliente)[names(cliente) == 'genres'] <- 'text'



movies_source <- DataframeSource(cliente)
movies_corpus <- VCorpus(movies_source)


movies_corpus <- movies_corpus %>% 
                tm_map(removePunctuation) %>%
                tm_map(content_transformer(tolower)) %>%
                tm_map(removeWords, c(stopwords("en"),"dont",
                       "can","just", "cant", "get", "got","one","two","name"))
                # tm_map(content_transformer(replace_number)) %>%
                # tm_map(content_transformer(bracketX)) %>%
                # tm_map(stripWhitespace) 
                
#Stem document
clean_corpus <- tm_map(movies_corpus, stemDocument)
#Create Term Document Matrix
clean_tdm    <- TermDocumentMatrix(clean_corpus)
#Converting TDM to matrix for Analysis
clean_m      <- as.matrix(clean_tdm)



freqwords <- findFreqTerms(clean_tdm, lowfreq = 10)

# calculate the frequency of words
v <- sort(rowSums(clean_m), decreasing=TRUE)
myNames <- names(v)
k <- which(names(v)=="miners")
myNames[k] <- "mining"
d <- data.frame(word=myNames, freq=v)
wordcloud(d$word, d$freq, min.freq=3,max.words=30, random.order=FALSE,rot.per=0.35, colors=brewer.pal(8, "Dark2"))

  • Por Titulo: Man, Little, Black, Moon, Soldier, (Monster, Blood, Mad, Night)
  • Por Overview: Find, Life, Friend, Love, Man, (Death, Kill)
  • Por Genero: Comedy, Drama, Documentary, Horror

  • Al usuario se le puede recomendar como a todos peliculas casuales de comedia, drama, etc. Se puede observar predisposicion al genero horror y palabras como “Monster”, “Blood” o “Night”, por lo que se le podria recomendar peliculas de mounstruos, zombies, etc tanto de comedia(parodias) como de horror. Además tambien peliculas sobre guerras.

7. Conclusión

Reglas de asociación:

Respecto a las reglas de asociación conseguidas del dataset se vio reglas que describen cosas interesantes que pueden tener alguna explicación muy profunda sobre el comportamiento, el gusto de las personas y quizás los pensamientos. Algunas muy simples, un poco más complicadas y quizás a simple vista simples pero que esconde explicaciones más profundas. También, se nota que puede haber existencia de ruido teniendo en cuenta que no estamos hablando de un cliente en sí, habrá ocasiones que ese cliente no represente una persona sino una familia, pareja o hasta quizás grupos de amigos.

Cluster-Recomendacion: Respecto al cluster creo que se obtuvo buenos resultados, quizás unos grupos difíciles de describir, pero se obtuvo el que interesaba. Además, el resultado de las 20 películas que se muestras tiene mucho sentido por lo cual podemos concluir que es una técnica bastante eficiente en este caso para poder separar las películas.

Word Cloud: La utilizacion del word cloud nos mostro que nos puede ser de utilidad para recomendacion de tipos de peliculas ya que nos da tendencias personalizadas teniendo en cuenta las palabras mas frecuentes en el titulo, resumen y generos. Juntando los 3 nos brinda bastante informacion.

Conclusión general:

Todo lo hecho anteriormente, pensamos que en cierto modo son análisis bastante interesante pero también tendríamos que tener en cuenta que se está trabajando con el 10% de los datos, aunque este sea representado se debe ser consciente que lo mismo habrá un sesgo o ruido que se tenga.

LS0tDQp0aXRsZTogIlBhcmNpYWwgMiBkZSBEYXRhbWluaWctIE5ldGZsaXgiDQphdXRob3I6ICJBbHZhcmV6IElnbmFjaW8gLSBCcnVndWVyYSBBZ3VzdGluIC0gTWFydGluZXogRGFuaWVsIg0KZGF0ZTogIjMvMTEvMjAxOSINCm91dHB1dDoNCiAgICBodG1sX25vdGVib29rOg0KICAgICAgZGZfcHJpbnQ6IHBhZ2VkDQogICAgICBmaWc6aGVpZ2h0OiA0DQogICAgICBmaWc6d2lkdGg6IDYNCiAgICAgIHRvYzogeWVzDQogICAgICB0b2NfZmxvYXQ6IHllcw0KICAgIGh0bWxfZG9jdW1lbnQ6DQogICAgICBkZl9wcmludDogcGFnZWQNCiAgICAgIHRvYzogeWVzDQotLS0NCg0KDQojIDAuT3ZlcnZpZXcNCg0KRW4gZXN0ZSB0cmFiYWpvIHZhbW9zIGEgYW5hbGl6YXIgZWwgZGF0YXNldCBkZSBuZXRmbGl4LCBlc3RlIHRpZW5lIGRhdG9zIGRlIGxhcyBwZWxpY3VsYXMgeSBsYXMgY2xhc2lmaWNhY2lvbmVzIGEgbGFzIGRpc3RpbnRhcyBwZWxpY3VsYXMgaGVjaGFzIHBvciBsb3MgdXN1YXJpb3MuIEEgcGFydGlyIGRlIHVuIGRhdG8gZGUgbXVlc3RyYXMgZGUgZGljaG8gZGF0YXNldCwgdmFtb3MgYSBnZW5lcmFyIGxhcyByZWdsYXMgZGUgYXNvY2lhY2lvbiBtYXMgaW1wb3J0YW50ZXMgdGVuaWVuZG8gZW4gY3VlbnRhIGxhcyBjbGFzaWZpY2FjaW9uZXMgaGVjaGFzIHBvciBjYWRhIHBlbGljdWxhIGRlIGNhZGEgdW5vIGRlIGxvcyBjbGllbnRlcyBjb21vIHNpIGZ1ZXJhbiB0cmFuc2FjY2lvbmVzLiBFc3BlcmFtb3MgYW5hbGl6YXIgbGFzIG1hcyBpbXBvcnRhbnRlcyBwYXJhIGJ1c2NhciBhbGd1biB0aXBvIGRlIGxvZ2ljYSBsYXMgY3VhbGVzIGxhcyBleHBsaXF1ZW4geSBsb2dyYXIgYnVzY2FyIHVuYSBwcm9wdWVzdGEgcGFyYSB1biBzaXN0ZW1hIGRlIHJlY29tZW5kYWNpb24sIG9mZXJ0YXMgbyBkZSB0b21hIGRlIGRlY2lzaW9uZXMuDQpVc2FyZW1vcyBjbHVzdGVycyBwYXJhIHBvZGVyIHNlcGFyYXIgbGFzIHBlbGljdWxhcyBlIGlkZW50aWZpY2FyIGxhcyBtYXMgcmVjb21lbmRhZGFzLg0KUG9yIHVsdGltbywgdmVyZW1vcyBkZSBpbnRlbnRhciBhbmFsaXphciBsYXMgdGVuZGVuY2lhcyBkZSBsb3MgY2xpZW50ZXMgdXNhbmRvIHdvcmQgY2xvdWQuDQoNCiMgMS4gTGlicmVyaWFzDQoNCmBgYHtyfQ0KbGlicmFyeShyZWFkcikNCmxpYnJhcnkoZHBseXIpDQpsaWJyYXJ5KHRpZHl2ZXJzZSkNCmxpYnJhcnkocGx5cikNCmxpYnJhcnkoYXJ1bGVzKQ0KbGlicmFyeShhcnVsZXNWaXopDQpsaWJyYXJ5KFJDb2xvckJyZXdlcikNCmxpYnJhcnkoZ2dwbG90MikNCmxpYnJhcnkoZmFjdG9leHRyYSkNCmxpYnJhcnkodG0pDQpsaWJyYXJ5KHdvcmRjbG91ZCkNCmxpYnJhcnkoU25vd2JhbGxDKQ0KbGlicmFyeSh0ZXh0ZGF0YSkNCmBgYA0KDQojIDIuIEFuYWxpc2lzIEVzdGFkaXN0aWNvIERlc2NyaXB0aXZvDQoNCiMjIDIuMSBEYXRhc2V0IHF1ZSBjb250aWVuZSB0aXR1bG9zIGRlIGxhcyBwZWxpY3VsYXMNCmBgYHtyIH0NCmRfdGl0dWxvIDwtIHJlYWRfY3N2KCJtb3ZpZV90aXRsZXMuY3N2IiwgY29sX25hbWVzID0gYygiSWRQZWxpY3VsYSIsImRhdGUiLCJuYW1lIikpDQpgYGANCg0KYGBge3J9DQpoZWFkKGRfdGl0dWxvKQ0KYGBgDQoNCg0KIyMgMi4yIERhdGFzZXQgcXVlIGNvbnRpZW5lIGxvcyByYXRpbmcgZGUgbG9zIHVzdWFyaW9zDQpgYGB7ciB9DQpkX3JhdGluZyA8LSByZWFkX2NzdigiYnEtcmVzdWx0cy0yMDE5MTIwMi0xOTAxMTAtd2Zkem14MXFibGZyLmNzdiIpDQpgYGANCg0KYGBge3J9DQpoZWFkKGRfcmF0aW5nKQ0KYGBgDQoNCg0KIyMgMi4zIERhdGFzZXQgcXVlIGNvbnRpZW5lIGxvcyBvdmVydmlldyBkZSBsYXMgcGVsaWN1bGFzDQpgYGB7cn0NCmRfb3ZlcnZpZXcgPC0gcmVhZF9jc3YoIm1vdmllc19tZXRhZGF0YS5jc3YiKQ0KYGBgDQoNCmBgYHtyfQ0KaGVhZChkX292ZXJ2aWV3KQ0KYGBgDQoNCg0KIyMgMi40IERhdGFzZXQgcXVlIGNvbnRpZW5lIGxvcyBnZW5lcm9zIGRlIGxhcyBwZWxpY3VsYXMNCmBgYHtyfQ0KZF9nZW5lcm9zIDwtIHJlYWRfY3N2KCJ0aXRsZS5iYXNpY3MuY3N2IikNCmBgYA0KDQpgYGB7cn0NCmhlYWQoZF9nZW5lcm9zKQ0KYGBgDQoNCg0KIyMgMi41IEluY2x1aXIgR2VuZXJvcw0KYGBge3J9DQoNCmdlbnJlcyA8LSBjKCIiKQ0KcGVsaWN1bGFzIDwtIGRfdGl0dWxvDQpwZWxpY3VsYXMgPC0gY2JpbmQocGVsaWN1bGFzLGdlbnJlcykNCg0Kbm0gPC0gYygiZ2VucmVzIikNCg0KcGVsaWN1bGFzW25tXSA8LSBsYXBwbHkobm0sIGZ1bmN0aW9uKHgpIGRfZ2VuZXJvc1tbeF1dIFttYXRjaChwZWxpY3VsYXMkbmFtZSwgZF9nZW5lcm9zJHByaW1hcnlUaXRsZSldICkNCg0KYGBgDQoNCiMjIDIuNiBJbmNsdWlyIG92ZXJ2aWV3DQpgYGB7cn0NCg0KcmVzdW1lbiA8LSBjKCIiKQ0KcGVsaWN1bGFzIDwtIGNiaW5kKHBlbGljdWxhcyxyZXN1bWVuKQ0KDQpvdiA8LSBjKCJvdmVydmlldyIpDQoNCnBlbGljdWxhc1tvdl0gPC0gbGFwcGx5KG92LCBmdW5jdGlvbih4KSBkX292ZXJ2aWV3W1t4XV0gW21hdGNoKHBlbGljdWxhcyRuYW1lLCBkX292ZXJ2aWV3JHRpdGxlKV0pDQoNCmBgYA0KDQoNCiMjIDIuNyBCb3JyYXIgTkENCmBgYHtyfQ0Kc2FwcGx5KHBlbGljdWxhcywgZnVuY3Rpb24oeCkgc3VtKGlzLm5hKHgpKSkNCmBgYA0KDQpgYGB7cn0NCnBlbGljdWxhczIgPC0gbmEub21pdChwZWxpY3VsYXMpDQpgYGANCg0KYGBge3J9DQpwZWxpY3VsYXMyJHJlc3VtZW4gPC0gTlVMTA0KYGBgDQoNCiMjIDIuOCBPYnRlbmVyIHByb21lZGlvIGRlIHJhdGluZyBkZSBjYWRhIHBlbGljdWxhDQpgYGB7cn0NCg0KI3Byb21fcmF0IDwtIGdyb3VwX2J5KGRfcmF0aW5nLCBkX3JhdGluZyRJZFBlbGljdWxhKQ0KDQojcHJvbV9yYXQgPC0gc3VtbWFyaXNlKHByb21fcmF0LCBtZWFuKGRfcmF0aW5nJElkUGVsaWN1bGEpLCBtZWFuKGRfcmF0aW5nJFJhdGluZykpDQoNCmJ5X2lkIDwtIGRfcmF0aW5nICU+JQ0KZ3JvdXBfYnkoSWRQZWxpY3VsYSkgJT4lDQpkcGx5cjo6c3VtbWFyaXNlKHByb21yYXRpbmcgPSBtZWFuKFJhdGluZykpDQoNCmBgYA0KDQpgYGB7cn0NCg0KcGVsaWN1bGFzMyA8LSBtZXJnZShwZWxpY3VsYXMyLCBieV9pZCxieT0iSWRQZWxpY3VsYSIsIGFsbC54ID0gVFJVRSkNCg0KYGBgDQoNCmBgYHtyfQ0KbmFtZXMocGVsaWN1bGFzMylbbmFtZXMocGVsaWN1bGFzMykgPT0gJ21lYW4oUmF0aW5nKSddIDwtICdwcm9tcmF0aW5nJw0KYGBgDQoNCiMjIDIuOSBFbGltaW5hciBEdXBsaWNhZG9zDQpgYGB7cn0NCnBlbGljdWxhczQgPC0gcGVsaWN1bGFzMw0KDQpwZWxpY3VsYXM0IDwtIHBlbGljdWxhczQgJT4lIGRpc3RpbmN0KG5hbWUsIC5rZWVwX2FsbCA9IFRSVUUpDQpgYGANCiAgDQoNCiMjIDIuMTAgRXhwbG9yYWNpb24gZGUgbG9zIGRhdG9zIA0KYGBge3J9DQoNCmhlYWQocGVsaWN1bGFzNCkNCmBgYA0KDQoNCmBgYHtyfQ0KdGFpbChwZWxpY3VsYXM0KQ0KYGBgDQoNCmBgYHtyfQ0KY29sbmFtZXMocGVsaWN1bGFzNCkNCmBgYA0KDQpgYGB7cn0NCmRpbShwZWxpY3VsYXM0KQ0KYGBgDQoNCmBgYHtyfQ0Kc3VtbWFyeShwZWxpY3VsYXM0KQ0KYGBgDQotIFNlIHZpc3VhbGl6YW4gTkEgZW4gcHJvbXJhdGluZw0KDQojIyAyLjExIFF1aXRhciBOQcK0UyANCmBgYHtyfQ0Kc2FwcGx5KHBlbGljdWxhczQsIGZ1bmN0aW9uKHgpIHN1bShpcy5uYSh4KSkpDQpgYGANCg0KYGBge3J9DQpwZWxpY3VsYXM0IDwtIG5hLm9taXQocGVsaWN1bGFzNCkNCmBgYA0KDQojIyAyLjEyIERhdGUNCmBgYHtyfQ0Kc3VtbWFyeShwZWxpY3VsYXM0JGRhdGUpDQpgYGANCg0KYGBge3J9DQpzZChwZWxpY3VsYXM0JGRhdGUpDQpgYGANCg0KYGBge3J9DQpib3hwbG90KHBlbGljdWxhczQkZGF0ZSkNCmBgYA0KDQpgYGB7cn0NCmhpc3QocGVsaWN1bGFzNCRkYXRlKQ0KYGBgDQoNCi0gTGEgbWF5b3IgY29uY2VudHJhY2lvbiBkZSBwZWxpY3VsYXMgZnVlcm9uIHB1YmxpY2FkYXMgYSBwYXJ0aXIgZGUgMTk4MA0KDQojIyAyLjEzIHByb21yYXRpbmcNCg0KYGBge3J9DQpzdW1tYXJ5KHBlbGljdWxhczQkcHJvbXJhdGluZykNCmBgYA0KDQoNCiMjIDIuMTQgUmVlbXBsYXphbmRvIFxOIGEgTkENCmBgYHtyfQ0KDQpwZWxpY3VsYXM0JGdlbnJlc1twZWxpY3VsYXM0JGdlbnJlcyA9PSAnXFxOJ10gPC0gTkENCg0KYGBgDQoNCmBgYHtyfQ0Kc2FwcGx5KHBlbGljdWxhczQsIGZ1bmN0aW9uKHgpIHN1bShpcy5uYSh4KSkpDQpgYGANCg0KYGBge3J9DQpwZWxpY3VsYXM0IDwtIG5hLm9taXQocGVsaWN1bGFzNCkNCmBgYA0KDQojIyAyLjE1IFBlbGljdWxhcyBjb24gbWVqb3JlcyBub3Rhcw0KYGBge3J9DQoNCmhlYWQocGVsaWN1bGFzNFtvcmRlcihwZWxpY3VsYXM0JHByb21yYXRpbmcsIGRlY3JlYXNpbmcgPSBUUlVFKSxjKDMsNiwyKV0sMjApDQoNCmBgYA0KLSBFbnRyZSBsYXMgMTAgcGVsaWN1bGFzIGNvbiBtZWpvcmVzIG5vdGFzIHNlIGVuY3VlbnRyYW4gYWxndW5hcyBjb25vY2lkYXMsIHRhbGVzIGNvbW8gTGEgbGlzdGEgZGUgU2NoaW5kbGVyLCBFbCBwYWRyaW5vLCBJbmRpYW5hIEpvbmVzLCBUb3kgU3RvcnksIGV0Yy4NCg0KYGBge3J9DQoNCnBlbGljdWxhczQkZ2VucmVzIDwtIGFzLmZhY3RvcihwZWxpY3VsYXM0JGdlbnJlcykNCg0KYGBgDQoNCg0KIyMgMi4xNiBHZW5lcm9zIG1hcyBScmVjdXJyZW50ZXMNCmBgYHtyfQ0KdCA8LSB0YWJsZShwZWxpY3VsYXM0JGdlbnJlcykNCmJhcnBsb3QoIHNvcnQodCwgZGVjcmVhc2luZz1UUlVFKVsxOjVdLCBzcGFjZSA9IDAuNSwgY2V4Lm5hbWVzID0gMC43MCkNCnNvcnQodCwgZGVjcmVhc2luZyA9IFRSVUUpWzE6MTBdDQpgYGANCi0gRWwgZ2VuZXJvIG1hcyByZWN1cnJlbnRlIGVzIERyYW1hDQoNCiMjIDIuMTcgUGVsaWN1bGFzIGNvbiBtZWpvciBub3RhIDcwJ3MNCg0KYGBge3J9DQpwNzAgPC0gZmlsdGVyKHBlbGljdWxhczQsIChwZWxpY3VsYXM0JGRhdGUgPj0gMTk3MCAmIHBlbGljdWxhczQkZGF0ZTwgMTk4MCkpDQoNCmhlYWQocDcwW29yZGVyKHA3MCRwcm9tcmF0aW5nLCBkZWNyZWFzaW5nID0gVFJVRSksYygzLDYsMildLDIwKQ0KDQpgYGANCi0gTGEgcGVsaWN1bGEgY29uIG1lam9yZXMgbm90YXMgZGUgbG9zIDcwwrRzIGVzICJFbCBQYWRyaW5vIg0KDQojIyAyLjE4IFBlbGljdWxhcyBjb24gbWVqb3Igbm90YSA4MCdzDQoNCmBgYHtyfQ0KcDgwIDwtIGZpbHRlcihwZWxpY3VsYXM0LCAocGVsaWN1bGFzNCRkYXRlID49IDE5ODAgJiBwZWxpY3VsYXM0JGRhdGU8IDE5OTApKQ0KDQpoZWFkKHA4MFtvcmRlcihwODAkcHJvbXJhdGluZywgZGVjcmVhc2luZyA9IFRSVUUpLGMoMyw2LDIpXSwyMCkNCg0KYGBgDQotIExhcyBwZWxpY3VsYXMgY29uIG1lam9yZXMgbm90YXMgZGUgbG9zIDgwwrRzIHNvbiBkb3MgZGUgbGEgc2FnYSBJbmRpYW5hIEpvbmVzDQotIE90cmFzIHBlbGljdWxhcyBpbnRlcmVzYW50ZXMgc29uICJFbCByZXNwbGFuZG9yIiB5ICJWb2x2ZXIgYWwgRnV0dXJvIg0KDQojIyAyLjE5IFBlbGljdWxhcyBjb24gbWVqb3Igbm90YSA5MCdzDQoNCmBgYHtyfQ0KcDkwIDwtIGZpbHRlcihwZWxpY3VsYXM0LCAocGVsaWN1bGFzNCRkYXRlID49IDE5OTAgJiBwZWxpY3VsYXM0JGRhdGU8IDIwMDApKQ0KDQpoZWFkKHA5MFtvcmRlcihwOTAkcHJvbXJhdGluZywgZGVjcmVhc2luZyA9IFRSVUUpLGMoMyw2LDIpXSwyMCkNCg0KYGBgDQotIExhIHBlbGljdWxhIGNvbiBtZWpvciBub3RhIGRlIGxvcyA5MMK0cyBlcyAiTGEgTGlzdGEgZGUgU2NoaW5kZXIiDQotIE90cmFzIGludGVyZXNhbnRlcyBzb24gIlRveSBTdG9yeSIsIkZvcnJlc3QgR3VtcCIuDQoNCiMjIDIuMjAgUGVsaWN1bGFzIGNvbiBtZWpvciBub3RhIDAwJ3MNCg0KYGBge3J9DQpwMDAgPC0gZmlsdGVyKHBlbGljdWxhczQsIChwZWxpY3VsYXM0JGRhdGUgPj0gMjAwMCkpDQoNCmhlYWQocDAwW29yZGVyKHA3MCRwcm9tcmF0aW5nLCBkZWNyZWFzaW5nID0gVFJVRSksYygzLDYsMildLDIwKQ0KDQpgYGANCi0gU2UgcHVlZGUgb2JzZXJ2YXIgdW4gY2xhcm8gZGVzY2Vuc28gZW4gbG9zIHByb21lZGlvcyBkZSBub3RhcyBkZSBlc3RhIGRlY2FkYSwgaGF5IHF1ZSBkZWphciBlbiBjbGFybyBxdWUgc29uIHBlbGljdWxhcyBlc3RyZW5hZGFzIGhhc3RhIDIwMDUuDQoNCiMjIDIuMjEgR2VuZXJvcyBtYXMgUnJlY3VycmVudGVzIDcwJ3MNCmBgYHtyfQ0KdDcwIDwtIHRhYmxlKHA3MCRnZW5yZXMpDQpiYXJwbG90KCBzb3J0KHQ3MCwgZGVjcmVhc2luZz1UUlVFKVsxOjVdLCBzcGFjZSA9IDAuNSwgY2V4Lm5hbWVzID0gMC43MCkNCnNvcnQodDcwLCBkZWNyZWFzaW5nID0gVFJVRSlbMToxMF0NCmBgYA0KDQotIEVsIERyYW1hIGVzIGVsIGdlbmVybyBtYXMgcG9wdWxhciBvIHJlY3VycmVudGUgZW4gbG9zIDcwwrRzDQotIEVsIEhvcnJvciBlcyBlbCBzZWd1bmRvIGdlbmVybyBtYXMgcmVjdXJyZW50ZS4NCg0KIyMgMi4yMiBHZW5lcm9zIG1hcyBScmVjdXJyZW50ZXMgODAncw0KYGBge3J9DQp0ODAgPC0gdGFibGUocDgwJGdlbnJlcykNCmJhcnBsb3QoIHNvcnQodDgwLCBkZWNyZWFzaW5nPVRSVUUpWzE6NV0sIHNwYWNlID0gMC41LCBjZXgubmFtZXMgPSAwLjcwKQ0Kc29ydCh0ODAsIGRlY3JlYXNpbmcgPSBUUlVFKVsxOjEwXQ0KYGBgDQoNCi0gQ29tZWRpYSwgcXVlIGVyYSBlbCB0ZXJjZXIgZ2VuZXJvIGVuIGxvcyA3MMK0cyAgc2UgcG9zaWNpb25hIGNvbW8gcHJpbWVybyBlbiBsb3MgODDCtHMNCi0gRGVzYXBhcmVjZSBIb3Jyb3IgZGVsIHRvcDUgZGUgZ2VuZXJvcy4NCg0KIyMgMi4yMyBHZW5lcm9zIG1hcyBScmVjdXJyZW50ZXMgOTAncw0KYGBge3J9DQp0OTAgPC0gdGFibGUocDkwJGdlbnJlcykNCmJhcnBsb3QoIHNvcnQodDkwLCBkZWNyZWFzaW5nPVRSVUUpWzE6NV0sIHNwYWNlID0gMC41LCBjZXgubmFtZXMgPSAwLjcwKQ0Kc29ydCh0OTAsIGRlY3JlYXNpbmcgPSBUUlVFKVsxOjEwXQ0KYGBgDQoNCi0gRHJhbWEgeSBDb21lZGlhIHNpZ3VlbiBzaWVuZG8gbG9zIGRvcyBnZW5lcm9zIG1hcyByZWN1cnJlbnRlcyBlbiBsb3MgOTDCtHMNCg0KIyMgMi4yNCBHZW5lcm9zIG1hcyBScmVjdXJyZW50ZXMgMDAncw0KYGBge3J9DQp0MDAgPC0gdGFibGUocDAwJGdlbnJlcykNCmJhcnBsb3QoIHNvcnQodDAwLCBkZWNyZWFzaW5nPVRSVUUpWzE6NV0sIHNwYWNlID0gMC41LCBjZXgubmFtZXMgPSAwLjcwKQ0Kc29ydCh0MDAsIGRlY3JlYXNpbmcgPSBUUlVFKVsxOjEwXQ0KYGBgDQoNCi0gRHJhbWEgeSBjb21lZGlhIHNpZ3VlbiBzaWVuZG8gbG9zIGdlbmVyb3MgbWFzIHJlY3VycmVudGVzIGVuIGxvcyAyMDAwDQotIEFwYXJlY2UgZW4gZWwgdG9wNSBlbCBnZW5lcm8gZG9jdW1lbnRhbA0KDQoNCiMgMy4gQ2FyZ2FyIGRhdG9zDQojIyAzLjEgQ2FyZ2FyIGxvcyBkYXRhc2V0cyBkZSBjbGFzaWZpY2FjaW9uKDEwJSBkZWwgZGF0YXNldCkNCkFjYSBjYXJnYW1vcyBlbCBtdWVzdHJlbyBkZWwgZGF0YXNldCBxdWUgZWwgY29tcGxldG8gdGllbmUgMTAwIG1pbGxvbmVzIGRlIG9ic2VydmFjaW9uZXMsIGVzdGUgbXVlc3RyZSBjb25zdGEgZWwgZGVsIDEwJSBkZWwgb3JpZ2luYWwuIFF1ZWRhbmRvIGFzaSBjb24gMTAgbWlsbG9uZXMgZGUgb2JzZXJ2YWNpb25lcy4gDQpgYGB7ciBldmFsPWZhbHNlfQ0KDQpkYXRhc2V0LnJhdyA8LSByZWFkX2NzdigiYnEtcmVzdWx0cy0yMDE5MTIwMi0xOTAxMTAtd2Zkem14MXFibGZyLmNzdiIpDQoNCmBgYA0KDQojIyMgU3VtbWFyeSANCkVsIGRhdGFzZXQgZGUgY2xhc2lmaWNhY2lvbmVzIHRpZW5lIDQgYXRyaWJ1dG9zIGxvcyBjdWFsZXMgc29uOg0KDQpJZFBlbGljdWxhOiBlbCBpZCBkZSBsYSBwZWxpY3VsYSBjdWFsIGNsYXNpZmljbyBlbCBjbGllbnRlLg0KDQpJZENsaWVudGU6IGVsIGlkIGRlbCBjbGllbnRlIHF1ZSBjbGFzaWZpY28gbGEgcGVsaWN1bGEuDQoNClJhdGluZzogbGEgcHVudHVhY2lvbiBxdWUgbGUgcHVzbyBxdWUgdmEgZGVzZGUgZWwgMSBhbCA1Lg0KDQpGZWNoYTogbGEgZmVjaGEgcXVlIHNlIGxhIHJlYWxpem8sIHF1ZSBlcyBkZXNkZSBmaW5hbGVzIGRlbCBhw7FvIDE5OTkgYSBmaW5hbGVzIGRlIDIwMDUNCmBgYHtyfQ0Kc3VtbWFyeShkYXRhc2V0LnJhdykNCmhlYWQoZGF0YXNldC5yYXcpDQpgYGANCg0KIyMjIENhbnRpZGFkIGRlIG9jdXJyZW5jaWFzIHBvciByYXRpbmcNCg0KQWNhIHBvZGVtb3MgdmVyIGNvbW8gc2UgZGlzdHJpYnV5ZW4gbGFzIHZvdGFjaW9uZXMgY29tbyBzZSBwdWVkZSB2ZXIgaGF5IG1hcyB2b3RvcyBwb3NpdGl2b3MgcXVlIG5lZ2F0aXZvcywgb3NlYSBtYXlvcmVzIG8gaWd1YWwgYSAzLg0KYGBge3J9DQptdF9tZWFuIDwtIGRhdGFzZXQucmF3ICU+JSANCiAgZ3JvdXBfYnkoUmF0aW5nKSAlPiUgDQogIGRwbHlyOjpzdW1tYXJpc2UoYXZnX2NvdW50ID0gZHBseXI6Om4oKSkgIA0KDQpoZWFkKG10X21lYW4pDQpiYXJwbG90KG10X21lYW4kYXZnX2NvdW50LG5hbWVzLmFyZyA9IGMoIjEiLCIyIiwiMyIsIjQiLCI1IikpDQpgYGANCg0KIyMjIEVzdHJ1Y3R1cmENCkxhIGVzdHJ1Y3R1cmEgZGUgZGUgbHNvIGRhdG9zLCBlbiBlc3RlIGNhc28gc2UgcG9kcmlhIGRlY2lyIHF1ZSB0ZW5lbW9zIDQgZmFjdG9yZXMsIHBlcm8gY29tbyBuZWNlc2l0YW1vcyBwb25lcmxvcyBjb21vIGZhY3RvcmVzIGVuIGxhIHV0aWxpemFjaW9uIGRlIGxhcyByZWdsYXMgZGUgYXNvY2lhY2lvbiBubyBsbyB0cmFuZm9ybWFyZW1vcywgeSB0YW1iaWVuIHBvciB0ZW1hcyBkZSB0ZWNuaWNvcyBkZSBSIHF1ZSBkYW4gcHJvYmxlbWFzIGNvbiBsYSBjYW50aWRhZCBkZSBuaXZlbGVzIGRlIGZhY3RvcmVzIGVuIGxhIGNvbHVtbmEgY2xpZW50ZSB5IGZlY2hhIHBvciBlamVtcGxvLg0KYGBge3J9DQpzdHIoZGF0YXNldC5yYXcpDQpgYGANCg0KDQojIyAzLjIgQ2FyZ2FyIGRhdGFzZXQgZGUgcGVsaWN1bGFzDQpCdWVubyBhY2EgdmFtb3MgYSBjYXJnYXIgZWwgb3RybyBkYXRhc2V0IHF1ZSB0ZW5lbW9zIHF1ZSBzb24gZGUgbGFzIHBlbGljdWxhcy4NCmBgYHtyfQ0KZGF0YXNldC5tb3ZpZXMgPC0gcmVhZF9jc3YoIm1vdmllX3RpdGxlcy5jc3YiLGNvbF9uYW1lcyA9IGMoIklkUGVsaWN1bGEiLCJkYXRlIiwib3JpZ2luYWxfdGl0bGUiKSkNCg0KYGBgDQoNCg0KIyMgMy4zIERhdGFzZXQgZXh0ZXJubw0KQnVlbm8gYWNhIHNhY2Ftb3Mgb3RybyBkYXRhc2V0IGNvbiBwZWxpY3VsYXMgcXVlIHRpZW5lIG90cm9zIGRhdG9zIGFwYXJ0ZSBkZSBsb3MgcXVlIHlhIHRlbmVtb3MsIGxvIHRyYXRhcmVtb3MgbWFzIHRhcmRlLiBQZXJvIHRpZW5lIGVzdG9zIGRhb3RzDQpgYGB7cn0NCmRhdGFzZXQubW92aWVzLm1ldGFkYXRhIDwtIHJlYWRfY3N2KCJtb3ZpZXNfbWV0YWRhdGEuY3N2IikNCmBgYA0KDQpgYGB7cn0NCmhlYWQoZGF0YXNldC5tb3ZpZXMubWV0YWRhdGEpDQpgYGANCg0KYGBge3J9DQpzdW1tYXJ5KGRhdGFzZXQubW92aWVzLm1ldGFkYXRhKQ0KYGBgDQoNCiMgNC4gUmVnbGFzIGRlIGFzb2NpYWNpb24gDQojIyA0LjEgSnVudGFyIGRhdGFzZXQNCkFxdcOtIGp1bnRhbW9zIGxvcyBkYXRhc2V0IHVzYW5kbyBhIGxhIGNvbHVtbmEgSWRQZWxpY3VsYSBxdWUgdGllbmUgYW1ib3MsIGx1ZWdvIGxvcyBmaWx0cmFtb3MgZGVqYW5kbyBzb2xvIGxvcyBxdWUgc29uIGlndWFsIG8gbWF5b3IgYSAzLCB5YSBxdWUgY29uc2lkZXJhbW9zIHF1ZSBhIHBhcnRpciBkZWwgcmF0aW5nIDMgc2UgcG9kcsOtYSBkZWNpciBxdWUgZXMgdW5hIHBlbMOtY3VsYSBxdWUgbcOtbmltYW1lbnRlIG5vcyBhZ3JhZGEuIFBvciDDumx0aW1vLCBzb2xvIGRlamFtb3MgZWwgaWRDbGllbnRlIHkgZWwgdMOtdHVsby4NCmBgYHtyfQ0KZGF0YS4zNDU8LW1lcmdlKHg9ZGF0YXNldC5yYXcseT1kYXRhc2V0Lm1vdmllcyxieT0iSWRQZWxpY3VsYSIpDQpkYXRhLjM0NTwtIHN1YnNldChkYXRhLjM0NSxkYXRhLjM0NSRSYXRpbmc+PTMpDQpkYXRhLjM0NTwtc2VsZWN0KGRhdGEuMzQ1LElkQ2xpZW50ZSxvcmlnaW5hbF90aXRsZSkNCmBgYA0KDQoNCiMjIDQuMiBEYXRvczogU3VtbWFyeSB5IHN0cg0KQ29tbyBwb2RlbW9zIHZlciBhc2kgbm9zIHF1ZWRhcmlhbiBsb3MgZG9zIGF0cmlidXRvcy4NCg0KYGBge3J9DQpzdW1tYXJ5KGRhdGEuMzQ1KQ0KDQpzdHIoZGF0YS4zNDUpDQpgYGANCg0KIyMgNC4zIEluc3BlY2Npb24NCkNvbW8gdmVtb3Mgbm8gaGF5IG51bG9zLg0KYGBge3J9DQpzYXBwbHkoZGF0YS4zNDUsIGZ1bmN0aW9uKHgpeyBzdW0oaXMubmEoeCkpIH0pDQoNCmBgYA0KYGBge3J9DQpoZWFkKGRhdGEuMzQ1KQ0KYGBgDQojIyA0LjQuIENyZWFuZG8gbGFzIHRyYW5zYWNjaW9uZXMNCkVuIGVzdGUgbW9tZW50byBwYXJhIHJlYWxpemFyIGxhcyByZWdsYXMgZGUgYXNvY2lhY2lvbmVzIHRlbmVtb3MgcXVlIGp1bnRhciBsYXMgcGVsw61jdWxhcyBjbGFzaWZpY2FkYXMgc2Vnw7puIGVsIElkQ2xpZW50ZSwgw7NzZWEsIHBvciBjYWRhIGNsaWVudGUgdGVuZHJlbW9zIHVuIGxpc3RhZG8gZGUgbGFzIHBlbMOtY3VsYXMgY2xhc2lmaWNhZGFzLg0KDQpgYGB7cn0NCmNsYXNpZmljYWNpb24udXN1YXJpbyA8LSBkZHBseShkYXRhLjM0NSwgYygiSWRDbGllbnRlIiksDQpmdW5jdGlvbih4KSBwYXN0ZSh4JG9yaWdpbmFsX3RpdGxlLCBjb2xsYXBzZSA9ICIsIikpDQpgYGANCg0KDQpBY2Egc2UgcHVlZGUgdmVyIGxvIHF1ZSBkaWppbW9zIGFudGVyaW9ybWVudGUuDQoNCmBgYHtyfQ0KaGVhZChjbGFzaWZpY2FjaW9uLnVzdWFyaW8pDQpgYGANCg0KIyMgNC41IEV4cG9ydGFyIHRyYW5zYWNjaW9uZXMuDQpBaG9yYSBzYWNhbW9zIGVsIGlkIGRlbCBjbGllbnRlIGRlbCBkYXRhc2V0IHkgbG8gZXNjcmliaW1vcyBlbiB1biBjc3YuDQpgYGB7cn0NCg0KY2xhc2lmaWNhY2lvbi51c3VhcmlvJElkQ2xpZW50ZTwtIE5VTEwNCg0Kd3JpdGUuY3N2KGNsYXNpZmljYWNpb24udXN1YXJpbywgImNsYXNpZmljYWNpb24uY3N2IiwgcXVvdGUgPSBGQUxTRSwNCnJvdy5uYW1lcyA9IEZBTFNFKQ0KYGBgDQoNCiMjIDQuNiBDYXJnYXJsbyBlbiBmb3JtYXRvIHRyYW5zYWNjaW9uZXMuDQpDb24gbGEgZnVuY2nDs24gcmVhZC50cmFuc2FjdGlvbnMgcG9kZW1vcyBjYXJnYXIgZWwgZGF0YXNldCBlbiB1bmEgZXN0cnVjdHVyYSBkaWZlcmVudGUgbGEgcXVlIG5vcyBwZXJtaXRpcsOhIHJlYWxpemFyIGVsIGFuw6FsaXNpcyBkZSBsYXMgYXNvY2lhY2lvbmVzLg0KYGBge3J9DQpkYXRhc2V0Lml0ZW1zZXQgPC0gcmVhZC50cmFuc2FjdGlvbnMoImNsYXNpZmljYWNpb24uY3N2IiwgZm9ybWF0ID0gImJhc2tldCIsDQpzZXAgPSAiLCIscXVvdGUgPSAiIikNCmBgYA0KDQoNCiMjIDQuNyBHcmFmaWNvcyBkZSBmcmVjdWVuY2lhLg0KDQpFbiBlc3RvcyBncsOhZmljb3Mgc2UgdmUgbGFzIHBlbMOtY3VsYXMgcXVlIGFwYXJlY2VuIG3DoXMgZnJlY3VlbnRlcywgcGFyYSBzZXIgZXhhY3RvIGxhcyBwcmltZXJhcyAyMC4gRW4gdW5hIHNlIG11ZXN0cmEgbGEgZnJlY3VlbmNpYSBhYnNvbHV0YSB5IGxhIG90cmEgbGEgcmVsYXRpdmEuDQpgYGB7cn0NCml0ZW1GcmVxdWVuY3lQbG90KGRhdGFzZXQuaXRlbXNldCwgdG9wTiA9IDIwLCB0eXBlID0gImFic29sdXRlIixjb2wgPSBicmV3ZXIucGFsKDgsIlBhc3RlbDIiKSwNCm1haW4gPSAiQWJzb2x1dGUgSXRlbSBGcmVxdWVuY3kgUGxvdCIpDQpgYGANCg0KYGBge3J9DQppdGVtRnJlcXVlbmN5UGxvdChkYXRhc2V0Lml0ZW1zZXQsdG9wTj0yMCx0eXBlPSJyZWxhdGl2ZSIsY29sPWJyZXdlci5wYWwoOCwiUGFzdGVsMiIpLG1haW49IlJlbGF0aXZlIEl0ZW0gRnJlcXVlbmN5IFBsb3QiKQ0KYGBgDQojIyA0LjggQnVzcXVlZGEgZGUgbGFzIHJlZ2xhcyBkZSBhc29jaWFjaW9uLg0KRW4gZXN0ZSBtb21lbnRvLCBsbyBxdWUgc2UgaGFjZSBlcyBidXNjYXIgbGFzIHJlZ2xhcyBkZSBhc29jaWFjaW9uIHF1ZSB0ZW5nYW4gdW4gc3VwcGx5IGRlIGFsIG1lbm9zIDAuMDAwMDIgeSB1biBjb25maWRlbmNlIGRlIG1hcyBkZSAwLjggeSBoYXN0YSB1bmEgbG9uZ2l0dWQgZGUgMyBpdGVtcy4NCg0KYGBge3J9DQphc3NvY2lhdGlvbi5ydWxlcyA8LSBhcHJpb3JpKGRhdGFzZXQuaXRlbXNldCwgcGFyYW1ldGVyID0gbGlzdChzdXBwPTAuMDAwMDIsIGNvbmY9MC44LG1heGxlbj0zKSkNCmBgYA0KYGBge3J9DQpzdW1tYXJ5KGFzc29jaWF0aW9uLnJ1bGVzKQ0KYGBgDQoNCiMjIDQuOSBSZWdsYXMNCg0KIyMjIFRvZGFzIGxhcyByZWdsYXMuDQpBY2EgcG9kZW1vcyB2aXp1YWxpemFyIGxhcyByZWdsYXMgb2J0ZW5pZGFzIHF1ZSBub3MgZGlvIHVuIHRvdGFsIGRlIDI4Lg0KYGBge3J9DQphcnVsZXM6Omluc3BlY3QoYXNzb2NpYXRpb24ucnVsZXMpDQpgYGANCg0KIyMjIEdvZGZhdGhlcg0KYGBge3J9DQpmaWx0cmFkby5yZWdsYXMuR0YgPC0gc3Vic2V0KHggPSBhc3NvY2lhdGlvbi5ydWxlcywNCiAgICAgICAgICAgICAgICAgICAgICAgICAgc3Vic2V0ID0gcmhzICVhaW4lIGMoIlRoZSBHb2RmYXRoZXIiKSkNCmFydWxlczo6aW5zcGVjdChmaWx0cmFkby5yZWdsYXMuR0YpDQpgYGANCkFjw6EgdGVuZW1vcyB2YXJpYXMgcmVnbGFzIHJlbGFjaW9uYWRhcyBjb24gIlRoZSBnb2RmYXRoZXIiIDE5NzI6DQpFcyB1bmEgcGVsw61jdWxhIGJhc3RhbnRlIGZhbW9zYSB5IGNhdGFsb2dhZGEgY29tbyBjbMOhc2ljYSwgcXVlIGVzIGRlIGRyYW1hIHkgZ8OhbnN0ZXIgaW1wbGljYW5kbyBhbGd1bmFzIGVzY2VuYXMgZGUgdmlvbGVuY2lhLCB0aHJpbGxlciB5IHN1c3BlbnNvLg0KDQoxLSANCkhhcm9sIGFuZCBtYXVkZTogZXMgdW5hIHBlbMOtY3VsYSBkZSBkcmFtYSB5IHRocmlsbGVyLCBkZSBsb3MgMTk3MS4NCkxvdmUgYW5kIERlYXRoIG9uIExvbmcgSXNsYW5kOiBvdHJhIHBlbMOtY3VsYSBkZSBkcmFtYSBsYSBjdWFsIGVzIGRlIDE5OTcuDQoNCiAgQWPDoSB2ZW1vcyB1biBjbGFybyBwYXRyw7NuIGVuIGVsIGN1YWwgcHJlZG9taW5hIGVsIGRyYW1hLCDDs3NlYSBlc3RhIHJlZ2xhIGVzdGEgbcOhcyBlbmZvY2FkbyBhIGxhcyBwZXJzb25hcyBxdWUgZGlzZnJ1dGEgZGUgZXN0ZSBnw6luZXJvLg0KICANCjItDQpVbmRlcnRvdzogcGFyZWNlIHVuYSBkZSBhY2Npw7NuIHkgcG9saWNpYWNvIGRlIDIwMDQuDQoNCndlaXJkIFNjaWVuY2U6IGxhIHTDrXBpY2EgcGVsw61jdWxhIGFtZXJpY2FuYSBkZSBhZG9sZXNjZW50ZXMgeSB1bmEgbGluZGEgY2hpY2EgcXVlIGFwYXJlY2UgZW4gcG9jYSByb3BhLCBkZWwgYcOxbyAxOTg1Lg0KDQogIExvIHF1ZSBwdWVkbyBwZW5zYXIgYWPDoSBlcyBxdWUgc2UgdmVyw61hIHJlZmxlamFkYSBlbCBjYXNvIGRlIGxhcyBmYW1pbGlhcyBxdWUgdGllbmVuIGFkb2xlc2NlbnRlcywgbyB1biBob21icmUgYWR1bHRvIHNpbiBmYW1pbGlhIHF1ZSB2aXZlIHNvbG8uIFRhbWJpw6luIHRlbmllbmRvIGVuIGN1ZW50YXMgbGFzIGZlY2hhcywgZXMgcG9zaWJsZW1lbnRlIHF1ZSByZWZsZWplIGxhIGVkYWQgZW4gcXVlIGxhcyB2aW8gdGVuaWVuZG8gZW4gY3VlbnRhLCBxdWUgbGEgc2VndW5kYSBlcyBkZSAxOTg1IHByb2JhYmxlbWVudGUgbGFzIHZlYXMgZW50cmUgNSB5IDEwIGHDsW9zIGRlc3B1w6lzIGx1ZWdvIGxhIHByaW1lcmEgbWFzbyBtZW5vcyBlbiBsYSBmZWNoYSBkZSBlc3RyZW5vIGVuIGNvbmp1bnRvIGEgbGEgZGVsIHBhZHJpbm8gbGEgY3VhbCBwb3Igc3UgZmFtYSBlcyBwb3NpYmxlIHF1ZSBhw7puIGxlIGxsYW1hcmEgbGEgYXRlbmNpw7NuIGVuIGVzb3MgYcOxb3MuDQozLQ0KDQpBbGllbiBOYXRpb246IHBlbMOtY3VsYSBkZSBjaWVuY2lhIGZpY2Npw7NuDQoNClRoZSBBZHZlbnR1cmVzIG9mIE1pbG8gYW5kIE90aXM6IHBlbMOtY3VsYSBkZSBwZXJyb3MgcGFyYSBuacOxby4NCg0KDQpQdWVkZSBvY3VycmlyIGFjw6EgcXVlIGVzIHVuYSByZWdsYSBkZSBhc29jaWFjacOzbiBkb25kZSBzZSByZWZsZWphIHVuYSBmYW1pbGlhIGNvbiBuacOxb3MgZGUgY29ydGEgZWRhZCB5IGJ1ZW5vIGVsIHBhZHJlIHF1ZSBkaXNmcnV0YXLDrWEgZGUgbGFzIG90cmFzLiBBbGdvIHBhcmVjaWRvIGNvbW8gc2UgdmVyw6EgbHVlZ28uDQoNCg0KDQo0LQ0KRmFyc2NhcGU6IHBlbGljdWxhIGRlIGNpZW5jaWEgZmljY2lvbi4NCg0KSGFtbGV0OiBwZWzDrWN1bGEgZGUgZHJhbWEgc29icmUgbGEgb2JyYSBkZSBzaGFrZXNwZWFyZS4NCg0KRXN0YSByZWdsYSBkZSBhc29jaWFjacOzbiB2ZW8gY29tbyBvcmllbnRhZG8gdW4gcG9jbyBhIGxhcyBwZXJzb25hcyBxdWUgZ296YXMgZGUgdW4gYnVlbiBndWlvbiBvIGhpc3RvcmlhLg0KDQo1LQ0KSHVzYmFuZHMgYW5kIFdpdmVzOnBlbGljdWxhIGRlIGRyYW1hL3JvbWFuY2UuDQoNClJpY29jaGV0OiBwZWxpY3VsYSBkZSBhY2Npb24gcG9saWNpYWNvLg0KDQpFbiBlc3RhIHJlZ2xhIGRlIGFzb2NpYWNpw7NuIHBvZHLDrWEgcHJlZG9taW5hciB1bmEgcGFyZWphLCBwb3IgdW4gbGFkbyBlbCB0ZW1hIGRlIGFjY2nDs24geSBvdHJvIHJvbWFuY2UgZHJhbWEsIHkgYnVlbm8gZWwgcGFkcmlubyBxdWUgcXVpesOhcyBwdWVkYSBzZXIgZGlzZnJ1dGFkbyBwb3IgbG9zIGRvcy4gRXMgbcOhcyBjYXBhcyBxdWUgZXMgbGEgdMOtcGljYSBkZSBxdWUgdG9jYSBlbGVnaXIgcG9yIHR1cm5vIGxvIHF1ZSB2YW4gYSB2ZXIgeSBwb3Igc2UgdmUgZXN0YSByZWxhY2nDs24uDQoNCg0KIyMjIFBpcmF0ZXMgb2YgdGhlIENhcmliYmVhbjogVGhlIEN1cnNlIG9mIHRoZSBCbGFjayBQZWFybA0KDQpgYGB7cn0NCmZpbHRyYWRvLnJlZ2xhcy5QaXJhdGFzIDwtIHN1YnNldCh4ID0gYXNzb2NpYXRpb24ucnVsZXMsDQogICAgICAgICAgICAgICAgICAgICAgICAgIHN1YnNldCA9IHJocyAlYWluJSBjKCJQaXJhdGVzIG9mIHRoZSBDYXJpYmJlYW46IFRoZSBDdXJzZSBvZiB0aGUgQmxhY2sgUGVhcmwiKSkNCmFydWxlczo6aW5zcGVjdChmaWx0cmFkby5yZWdsYXMuUGlyYXRhcykNCmBgYA0KTGEgZGUgcGlyYXRhcyBkZWwgY2FyaWJlICgyMDAzKSBlcyB1bmEgcGVsw61jdWxhIGRlIGF2ZW50dXJhLCBmYW50YXPDrWEsIGFsZ3VuYSBxdWUgb3RyYXMgZXNjZW5hcyBkZSBhY2Npw7NuIHBlcm8gcXVlIHBvciBsbyBnZW5lcmFsIGRlc3RhY2EgdW4gcG9jbyB0YW1iacOpbiBlbCBodW1vci4NCjEtDQoNCkFsbGFuIFF1YXRlcm1haW4gYW5kIHRoZSBMb3N0IENpdHkgb2YgR29sZCAoMTk4Nik6IGVzdGEgZXMgdW5hIHBlbMOtY3VsYSBkZSBhdmVudHVyYSB5IGFjY2nDs24sIG1lZGlvIHBhcmVjaWRhIGEgbGEgZGUgaW5kaWFuYSBqb25lcyBkZSBiw7pzcXVlZGFzIGRlIHRlc29yb3MgeSBwaXLDoW1pZGVzLCBldGMuIFByb2JhYmxlbWVudGUgYWxnbyBkZSBodW1vciwgcGVybyBzdXRpbC4NCg0KRnJpZWQgR3JlZW4gVG9tYXRvZXMgKDE5OTEpOiBlcyB1bmEgcGVsw61jdWxhIG3DoXMgb3JpZW50YWRhIGEgY29tZWRpbyBzb2JyZSB1biBhc2lsbyB5IHF1aXrDoXMgdW4gcG9jbyBkZSBkcmFtYSwgdMOtcGljYSBwZWzDrWN1bGEgcGFyYSB2ZXIgZW4gZmFtaWxpYS4NCiANCiAgRW4gZXN0YXMgcGVsw61jdWxhcyB2ZW1vcyBubyB0aWVuZW4gcmVhbG1lbnRlIG11Y2hvIHBhcmVjaWRvIGxvIHF1ZSBzaSBub3RvIGVzIHF1ZSBzb24gcGVsaWN1bGFzIGxhcyBxdWUgc29uIGFwdGFzIHBhcmEgdG9kbyBww7pibGljbywgbG8gcXVlIG1lIGhhY2UgcGVuc2FyIHF1ZSBzb24gbGFzIHBlbMOtY3VsYXMgcXVlIHNlIHZlw61hbiBlbiBmYW1pbGlhLiBBZGVtw6FzLCBzZSByZWZsZWphcsOtYXMgbGFzIHBlcnNvbmFzIHF1ZSBsZSBndXN0YSBsYXMgZGUgYXZlbnR1cmEgeSBodW1vciwgY29uIGFsZ28gZGUgZmFudGFzw61hLg0KICANCjItDQoqQmFieWxvbiA1OiBUaGUgUml2ZXIgb2YgU291bHMgKDE5OTgpOiB1bmEgcGVsw61jdWxhIG11eSBkZSBjaWVuY2lhIGZpY2Npw7NuIHZpc3RvIGEgcHJpbWVyYSB2aXN0YS4NCg0KKkhhcm9sZCBhbmQgS3VtYXIgR28gdG8gV2hpdGUgQ2FzdGxlKDIwMDQpOiB0w61waWNhIHBlbMOtY3VsYSB0b250YSBxdWUgcHJlZG9taW5hIGxhcyBlc2NlbmFzIGRlIGh1bW9yIGVudHJlIGRvcyBhY3RvcmVzLg0KDQpEZSBhY8OhIGxvIMO6bmljbyBxdWUgcHVlZG8gcGVuc2FyIHRlbmllbmRvIGVuIGN1ZW50YSBsYSBhbXBsaWEgZGlmZXJlbmNpYSBlbnRyZSBsYXMgcGVsw61jdWxhcyBvIHNvbiBkZSBkaXN0aW50YXMgcGVyc29uYXMgZGUgdW5hIG1pc21hIGZhbWlsaWEuIEF1bnF1ZSBwb3Igb3RybyBsYWRvIHRhbWJpw6luIHNlIHBvZHLDrWEgZGVjaXIgcXVlIGxhIGNpZW5jaWEgZmljY2nDs24gZW4gZXNhIMOpcG9jYSBlcmEgdXN1YWxtZW50ZSBtw6FzIHBvcHVsYXIgZW50cmUgbG9zIGFkb2xlc2NlbnRlcywgeSBsYSBzZWd1bmRhIGVyYSBsYSB0w61waWNhIGRlIGh1bW9yIHF1ZSBlcmEgdGFtYmnDqW4gb3JpZW50YWRvIGEgYWRvbGVzY2VudGVzLiBQb3IgY29uc2lndWllbnRlLCBhY8OhIGVsIHBhdHLDs24gcXVlIHF1aXrDoXMgZXN0YW1vcyAgdmllbmRvIGVuIGVzYSBldGFwYSB0YW1iacOpbiBhcG95w6FuZG9ub3MgZW4gbGFzIGZlY2hhcyBkZSBlc3RyZW5vIHF1ZSBzb24gbXV5IGNlcmNhbmFzIGVudHJlIHPDrS4NCg0KMy0NCg0KKkJhdG1hbiBCZXlvbmQ6IFRoZSBNb3ZpZSgxOTk5KTogcGVsw61jdWxhcyBkZSBzdXBlcmjDqXJvZXMgeSBhY2Npw7NuLCBzZSBwb2Ryw61hIGRlY2lyIHF1ZSBkZSBzdXNwZW5zbyBtdXkgcG9wdWxhciB1c3VhbG1lbnRlIGVudHJlIGxvcyBqw7N2ZW5lcyB5IGFkb2xlc2NlbnRlcy4NCg0KKlNpbW9uZSgyMDAyKTogcGVsw61jdWxhIGRlIGZhbnRhc8OtYSBzb2JyZSB1bmEgY2hpY2EgZGUgY29tcHV0YWRvcmEgcXVlIGhpem8gdW4gY2hpY28geSBxdWUgbHVlZ28gYWRxdWllcmUgdmlkYS4gVMOtcGljYSBwYXJhIGFkb2xlc2NlbnRlLg0KDQoNClBvciB1bHRpbW8sIGFsZ28gcXVlIG5vcyBkaW1vcyBjdWVudGEgZXMgcXVlIGRlIGVzdGFzIGRlIGVzdGFzIHJlZ2xhcyBwb2RlbW9zIHZlciBxdWUgZXN0YXLDrWEgYmFzYWRvIGVuIGxvcyBjbGllbnRlcyBsb3MgY3VhbGVzIHNlYW4gdW5hIGZhbWlsaWEgY29uIGFkb2xlc2NlbnRlcyBvIGxvcyBtaXNtb3MgYWRvbGVzY2VudGVzLg0KDQoNCg0KIyMjIFRoZSBNdW1teQ0KDQpgYGB7cn0NCmZpbHRyYWRvLnJlZ2xhcy5tdW1teSA8LSBzdWJzZXQoeCA9IGFzc29jaWF0aW9uLnJ1bGVzLA0KICAgICAgICAgICAgICAgICAgICAgICAgICBzdWJzZXQgPSByaHMgJWFpbiUgYygiVGhlIE11bW15IikpDQphcnVsZXM6Omluc3BlY3QoZmlsdHJhZG8ucmVnbGFzLm11bW15KQ0KYGBgDQoNClRoZSBNdW1teSAoMTk5OSk6IHVuYSBwZWzDrWN1bGEgZGUgRmFudGFzw61hL2FjY2nDs24gc29icmUgdW5hIG1hbGRpY2nDs24gZGUgdW5hIG1vbWlhLCBxdWUgcmV2aXZlIGVudHJlIGxvcyBtdWVydG9zIHkgcXVpZXJlIHZlbmdhcnNlIHkgYWRlbcOhcyB0aWVuZSBwb2RlcmVzLiBCw6FzdGF0ZSBlbnRyZXRlbmlkYSBwYXJhIHZlci4NCjEtDQoqVHJlbW9ycyAzLSBCYWNrIHRvIFBlcmZlY3Rpb24oMjAwMSk6IFBhcmVjZSB1bmEgcGVsw61jdWxhIGRlIGFjY2nDs24geSBjaWVuY2lhIGZpY2Npw7NuLCBzb2JyZSBhbGllbsOtZ2VuYXMvIGNyaWF0dXJhcyBxdWUgYXRhY2FuIGEgbG9zIGh1bWFub3MgeSBxdWUgdGllbmVuIHF1ZSBkZWZlbmRlcnNlLg0KKldhdGVyd29ybGQoMTk5NSk6IGNpZW5jaWEgZmljY2nDs24sIGFjY2nDs24gIHkgYXZlbnR1cmEsIHVuYSBwZWzDrWN1bGEgc29icmUgdW4gbXVuZG8gbGxlbm8gZGUgYWd1YSBlbiBsYSBxdWUgdW4gaG9tYnJlIHF1ZSB2aWFqYSBzb2xvIHByb3RlZ2UgYSB1bmEgbXVqZXIgeSBzdSBoaWpvIGRlIHBpcmF0YXMuDQoNCkJ1ZW5vIGFjw6Egbm90byB1bmEgcmVsYWNpw7NuIG11eSBsaW5lYWwgcmVzcGVjdG8gYSBsYSB0ZW3DoXRpY2EgcmVmcmnDqW5kb25vcyBhIGxhIGZhbnRhc8OtYSB5IGFsIGF2ZW50dXJhIGNvbiB1biBwb2NvIGRlIGFjY2nDs24sIGFkZW3DoXMgcHJlZG9taW5hIGVsIHRlbWEgZGUgcGVsZWFyIGNvbiBjcmlhdHVyYXMoZW4gZGlzdGludG8gdGlwbyBwZXJvIGVuIHNpIHNlIHBvZHLDrWEgY2F0YWxvZ2FyIGVuIGxvIG1pc21vKSwgbG8gY3VhbCBtZSBoYWNlIHBlbnNhciBxdWUgZXMgdW5hIHJlZ2xhIGRlIGFzb2NpYWNpw7NuIGNvbiBiYXN0YW50ZSBzZW50aWRvLCBhdW5xdWUgbGEgcHJpbWVyYSBhIG1pIHBhcmVjZXIgbm8gY3JlbyBxdWUgc2VhIHRhbiBidWVuYSBwZWzDrWN1bGEgY3VtcGxlIGNvbiBsbyBwcmltZXJvLCB0YW1iacOpbiB0ZW5pZW5kbyBlbiBjdWVudGEgcXVlIGxvcyBkYXRvcyB1c2Fkb3MgYWJhcmNhbiBsYXMgcGVsw61jdWxhcyBjbGFzaWZpY2FkYXMgZW50cmUgMyB5IDUuIA0KDQoyLQ0KKkFtZXJpY2FuIE5pbmphKDE5ODUpOiBzZSB0cmF0YSBzZWfDum4gbGEgZGVzY3JpcGNpw7NuIGRlIHVuIHNvbGRhZG8gQW3DqXJpY2EgcXVlIGFsIHBhcmVjZXIgYXByZW5kZSBhIHVzYXIgdW5hIGVzcGFkYSB5IGx1Y2hhIGNvbiB0cmFmaWNhbnRlcy4gUG9yIGxvIHZpc3RvIHVuYSB0ZW5kZW5jaWEgY2xhcmEgZW4gYWNjacOzbiB5IHZpb2xlbmNpYS4NCg0KKkZhbGxpbmcgRG93bigxOTkzKTogc29icmUgdW4gY2l1ZGFkYW5vIG5vcm1hbCBxdWUgZW1waWV6YSBhIHJlY3VycmlyIGEgbGEgdmlvbGVuY2lhIHkgaGFjZXIgYWwgcGFyZWNlciB2YXJpb3MgY3LDrW1lbmVzIGEgY2F1c2EgZGVsIGVzdHLDqXMgbyBhbGdvIHBhcmVjaWRvLiBNdWNoYSBhY2Npw7NuIHkgdmlvbGVuY2lhLg0KDQpDb21vIGxhIGFudGVyaW9yLCB0ZW5kcsOtYW1vcyBhbGdvIG3DoXMgbGluZWFsIHkgY29uIHNlbnRpZG8sIGFjw6EgbG8gcXVlIHByZWRvbWluYXLDrWEgc2VyaWEgbGFzIHBlcnNvbmFzIGNvbiBndXN0byByZXNwZWN0byBhIGxhIHZpb2xlbmNpYSB5IGxhIGFjY2nDs24gbGEgY3VhbCBxdWl6w6FzIGVuIGxhIG1vbWlhIG5vIHNlYSBkZW1hc2lhZGEsIHBlcm8gdGllbmUgbG8ganVzdG8gZGUgZXN0byB5IHRhbXBvY28gZXMgdGFuIG1hbGEuIFJlc3BlY3RvIGEgbGFzIGZlY2hhcyBub3RlIHF1ZSBlc3TDoW4gZW4gdW4gcmFuZ28gZGUgMTQgYcOxb3MsIGEgbWkgcGFyZWNlciBubyBlcyBzaWduaWZpY2F0aXZvLg0KDQoNCkJ1ZW5vIHBvZGVtb3MgY29uY2x1aXIgcXVlIGFjw6EsIHRlbmVtb3MgZG9zIGNhc29zIG11eSBtYXJjYWRvcyBwb3IgdW4gbGFkbyBsYSBwYXJ0ZSBkZSBsYSBhY2Npw7NuIHkgdmlvbGVuY2lhLCB5IHBvciBvdHJvIGxhIGRlIGNpZW5jaWEgZmljY2nDs24geSBhdmVudHVyYXMuDQoNCg0KIyMjIFdoaWxlIFlvdSBXZXJlIFNsZWVwaW5nDQoNCmBgYHtyfQ0KZmlsdHJhZG8ucmVnbGFzLldZV1MgPC0gc3Vic2V0KHggPSBhc3NvY2lhdGlvbi5ydWxlcywNCiAgICAgICAgICAgICAgICAgICAgICAgICAgc3Vic2V0ID0gcmhzICVhaW4lIGMoIldoaWxlIFlvdSBXZXJlIFNsZWVwaW5nIikpDQphcnVsZXM6Omluc3BlY3QoZmlsdHJhZG8ucmVnbGFzLldZV1MpDQpgYGANCndoaWxlIHlvdSB3ZXJlIHNsZWVwaW5nIHBlbGljdWxhKDE5OTUpOiBwZWzDrWN1bGEgZGUgZHJhbWEgeSByb21hbmNlLg0KDQpUaHJlZSdzIENvbXBhbnkoMTk3Nyk6IHNlcmlhIGRlIGRvcyBjaGljYXMgcXVlIGVuY3VlbnRyYW4gdW4gY29tcGHDsWVybyBkZSBjdWFydG8uIE11eSBwcm9iYWJsZSBpbXBsaWNhbmRvIGRyYW1hIHkgcm9tYW5jZSANCkJhYnkgYm9vbSAoMTk4Nyk6IHBlbMOtY3VsYSBzb2JyZSB1bmEgZWplY3V0aXZhIHF1ZSB0aWVuZSBxdWUgY3VpZGFyIGFsIGJlYmUgZGUgc3UgcHJpbW8uIERyYW1hIHkgcm9tYW5jZS4NCg0KQnVlbm8gZXN0YSBlcyB1bmEgcmVnbGEgZGUgYXNvY2lhY2nDs24gYmFzdGFudGUgZsOhY2lsIGRlIGludGVycHJldGFyLCDDs3NlYSB0ZW5lbW9zIHVuIGNsYXJvIHBhdHLDs24gZGUgZHJhbWEgeSByb21hbmNlLCBsbyBxdWUgaGFjZSBwZW5zYXIgcXVlIHNlcsOtYW4gbG9zIGNhc29zIGRlIGxhcyBhbWFzIGRlIGNhc2EgbGFzIGN1YWxlcyBzZXLDrWFuIG11eSBkZSBzdSBlc3RpbG8gZXN0ZSB0aXBvIGRlIHBlbMOtY3VsYXMgeSBzZXJpZXMuDQoNCiMjIyBHdWFyZGluZyBUZXNzDQoNCg0KYGBge3J9DQpmaWx0cmFkby5yZWdsYXMuR1QgPC0gc3Vic2V0KHggPSBhc3NvY2lhdGlvbi5ydWxlcywNCiAgICAgICAgICAgICAgICAgICAgICAgICAgc3Vic2V0ID0gcmhzICVhaW4lIGMoIkd1YXJkaW5nIFRlc3MiKSkNCmFydWxlczo6aW5zcGVjdChmaWx0cmFkby5yZWdsYXMuR1QpDQpgYGANCkVuIGVzdGUgY2FzbyBzb24gcGVsaWN1bGFzIHF1ZSBkZXNjb25vc2NvIGJhc3RhbnRlIHBlcm8gdmllbmRvIGxhIHNpcG5hcHNpcyBxdWUgZGFuIGVuIGdvb2dsZSBxdWUgc29uIGVzdGFzOg0KDQpHdWFyZGluZyBUZXNzICgxOTk0KTogIlVuYSBleCBwcmltZXJhIGRhbWEgZGUgbG9zIEVzdGFkb3MgVW5pZG9zIHF1aWVyZSBxdWUgdW4gYWdlbnRlIGRlbCBTZXJ2aWNpbyBTZWNyZXRvIGVuIHBhcnRpY3VsYXIgZW5jYWJlY2UgbG9zIGRldGFsbGVzIGRlIHN1IGd1YXJkYWVzcGFsZGFzLCBhIHBlc2FyIGRlIHF1ZSBubyBwdWVkZSBzb3BvcnRhcmxhLiINCg0KDQpGbGV0Y2ggTGl2ZXMoMTk4OSk6IkRlc3B1w6lzIGRlIHJlY2liaXIgdW5hIGhlcmVuY2lhIGVuIExvdWlzaWFuYSwgZWwgcGVyaW9kaXN0YSBkZSBMb3Mgw4FuZ2VsZXMgSXJ2aW4gRmxldGNoZXIgc2UgZGlyaWdlIGEgbGEgcGxhbnRhY2nDs24gZGUgQmVsbGUgSXNsZSwgZG9uZGUgc2UgbWV0ZSBlbiBwcm9ibGVtYXMgaGlsYXJhbnRlcyB5IHNlIHRvcGEgY29uIGFzZXNpbmF0by4iDQoNCg0KTWFkIENpdHkoMTk5Nyk6IkN1YW5kbyBlbCBtdXNlbyBlbiBlbCBxdWUgdHJhYmFqYSBlbCBndWFyZGlhIGRlIHNlZ3VyaWRhZCBTYW0gQmFpbHkgbGUgZGVzcGlkZSwgw6lzdGUgc2UgZGVzZXNwZXJhLiBEaXNwdWVzdG8gYSB0b2RvIHBhcmEgcmVjdXBlcmFyIHN1IHRyYWJham8sIEJhaWx5IGFzYWx0YSBlbCBtdXNlbyBjb24gdW4gYXJtYSB5IHRvbWEgY29tbyByZWhlbmVzIGEgdW4gZ3J1cG8gZGUgbmnDsW9zIHkgYSBsYSBkaXJlY3RvcmEgZGVsIGNlbnRyby4gTWF4IEJyYWNrZXR0LCB1biBwZXJpb2Rpc3RhIGNhw61kbyBlbiBkZXNncmFjaWEsIHZlIGVuIGVsIHN1Y2VzbyBsYSBncmFuIG9wb3J0dW5pZGFkIGRlIHJlYWxpemFyIGVsIHJlcG9ydGFqZSBkZSBzdSB2aWRhLiBBc8OtLCBjb252ZXJ0aXLDoSBsYSByZWl2aW5kaWNhY2nDs24gbGFib3JhbCBkZSBTYW0gZW4gdW4gY2lyY28gbWVkacOhdGljbyBkZSBjb25zZWN1ZW5jaWFzIGltcHJldmlzaWJsZXMuIg0KDQoNCkFuYWxpc2FuZG8gdW4gcG9jbyBsYXMgc2lwbmFwc2lzIHkgbG9zIGdlbmVyb3MsIGxsZWdhbW9zIGEgbGEgY29uY2x1Y2lvbiBkZSBxdWUgZXN0YXMgcGVsaWN1bGFzIGNvbXBhcnRlbiBjYXJhY3RlcmlzdGljYXMgZW4gY29tdW4gZGVsIHRpcG8gcXVlIHRpZW5lbiBsYXMgMyB0b3F1ZXMgZGUgaHVtb3IgeSBlc3RhbiBhbWJpZW50YWRhcyBhIGxvIHF1ZSBzZSByZWZpZXJlIGEgbG9zIGNyaW1lbmVzIG8gbWVqb3IgZGljaG8gZW4gbGEgcGFydGUgcG9saWNpYWwgZGUgdW5hIGZvcm11IHUgb3RyYSwgdmlzdG8gZGVzZGUgZGlzdGludG9zIHB1bnRvcyBlbiBjYWRhIHVuYSBwZXJvIGxsZWdhIGEgbG8gbWlzbW8uIEFkZW1hcyBsb3MgYcOxb3MgZGUgZXN0cmVub3Mgc29uIG11eSBwcm94aW1vcy4gDQoNCiMjIyBTaHJlayAyDQpgYGB7cn0NCmZpbHRyYWRvLnJlZ2xhcy5TMiA8LSBzdWJzZXQoeCA9IGFzc29jaWF0aW9uLnJ1bGVzLA0KICAgICAgICAgICAgICAgICAgICAgICAgICBzdWJzZXQgPSByaHMgJWFpbiUgYygiU2hyZWsgMiIpKQ0KYXJ1bGVzOjppbnNwZWN0KGZpbHRyYWRvLnJlZ2xhcy5TMikNCmBgYA0KDQpTaHJlayAyKDIwMDQpOiBwZWzDrWN1bGEgcG9wdWxhciBkZSBkaXNuZXkgc29icmUgZmFudGFzw61hIHkgYXZlbnR1cmEuDQogIA0KVGhlIEFuZHkgR3JpZmZpdGggU2hvdygxOTYwKTogdW4gc2hvdyBzb2JyZSB1biBhbGd1YWNpbCBkZSBwdWVibG8gcXVlIHZpdmUgY29uIHN1IGhpam8geSBzdSB0aWEuIFTDrXBpY28gc2hvdyBwYXJhIGxhIGZhbWlsaWEuIA0KDQpTb21ldGhpbmcncyBHb3R0YSBHaXZlKDIwMDMpOiBlcyB1bmEgcGVsw61jdWxhIHJvbcOhbnRpY2Egc29icmUgdW4gaG9tYnJlIGRlIHVub3MgNjAgeSBwaWNvIHF1ZSBzdWVsZSBzYWxpciBjb24gbXVqZXJlcyBqw7N2ZW5lcyB5IGVuIHVuYSBvY2FzacOzbiBzZSBlbmFtb3JhZGEgZGUgbGEgbWFkcmUgZGUgdW5hIGRlIHN1cyBub3ZpYXMuDQoNCkJ1ZW5vIGFjw6EgdGVuZW1vcyBjb3NhcyBtdXkgZGlzdGludGFzIGVudHJlIHPDrSwgcGVybyBhbGdvIGEgZGVzdGFjYXIgZXMgcXVlIGVzIGVsIHTDrXBpY28gcGF0csOzbiBxdWUgcHJlc2VudGFyw61hIHVuYSBmYW1pbGlhLCBwb3IgdW4gbGFkbywgbGEgcm9tw6FudGljYSBxdWUgbGUgZ3VzdGFyw61hIGEgbGEgbWFkcmUgbyBsYXMgbXVqZXJlcyBkZSBsYSBmYW1pbGlhLiBVbiBzaG93IHF1ZSB1c3VhbG1lbnRlIHNlIGxvIHZlcsOtYSBlbiBmYW1pbGlhIMOzc2VhIHF1ZSBwb2Ryw61hIHNlciBhcHJlY2lhZG8gcG9yIHRvZG9zLiBZIGJ1ZW5vIHNocmVrIDIgcXVlIGVzIHRhbWJpw6luIGZhY3RpYmxlIHF1ZSBzZSBsbyB2ZWEgZW4gZmFtaWxpYSBwb3JxdWUgdGFtYmnDqW4gcHVlZGUgZ3VzdGFyIGEgbG9zIGFkdWx0b3MsIHBlcm8gbXVjaG8gbcOhcyBlbmZvY2Fkb3MgYSBsb3MgaGlqb3MuDQoNCiMjIyBUaGUgU2l4dGggU2Vuc2UNCmBgYHtyfQ0KZmlsdHJhZG8ucmVnbGFzLlRTUyA8LSBzdWJzZXQoeCA9IGFzc29jaWF0aW9uLnJ1bGVzLA0KICAgICAgICAgICAgICAgICAgICAgICAgICBzdWJzZXQgPSByaHMgJWFpbiUgYygiVGhlIFNpeHRoIFNlbnNlIikpDQphcnVsZXM6Omluc3BlY3QoZmlsdHJhZG8ucmVnbGFzLlRTUykNCmBgYA0KVGhlIHNpeHRoIFNlbnNlOiBlcyB1bmEgcGVsaWN1bGEgbWVkaW8gc2UgcG9kcmlhIGRlY2lyIHN1c3BlbnNvIHkgdGVycm9yLiANCg0KQWlycGxhbmUhOiBlcyB1bmEgcGFyb2RpYSBjb21pY2EuDQoNClRoZSBBLVRlYW06IGVzIHVuYSBzZXJpZSBmYW1vc2EgcXVlIHRpZW5lIHRvcXVlcyBkZSBodW1vciBjb24gYWNjaW9uLg0KDQpBY2EgcHVlZGUgc2VyIHF1ZSBlc3RlIGVsIHRpcGljbyBjb21wb3J0YW1pZW50byBxdWUgcHVlZGUgcGFzYXIsIGRlIGJ1c2NhciBwZWxpY3VsYXMgZGUgaHVtb3IgbHVlZ28gZGUgcGVsaWN1bGFzIGRlIG1pZWRvIGNvbW8gcGFyYSByZWxhamFyc2UgeSBkaXN0cmFlcnNlIHBhcmEgZG9ybWlyLg0KDQoNCiMjIyBGaW5kaW5nIE5lbW8gKFdpZGVzY3JlZW4pIA0KYGBge3J9DQpmaWx0cmFkby5yZWdsYXMuRk4gPC0gc3Vic2V0KHggPSBhc3NvY2lhdGlvbi5ydWxlcywNCiAgICAgICAgICAgICAgICAgICAgICAgICAgc3Vic2V0ID0gcmhzICVhaW4lIGMoIkZpbmRpbmcgTmVtbyAoV2lkZXNjcmVlbikiKSkNCmFydWxlczo6aW5zcGVjdChmaWx0cmFkby5yZWdsYXMuRk4pDQpgYGANCkZpbmRpbmcgTmVtbyAoMjAwMyk6IHBlbGljdWxhIGRlIGRpc25leSBtdXkgY29ub2NpZGEuDQoNClRoZSBSdW5kb3duKDIwMDMpOiBwZWxpY3VsYSBkZSBhY2Npb24geSBzdXNwZW5zbywgZG9uZGUgYWN0dWEgImxhIHJvY2EiLg0KDQpUaGUgQm91cm5lIElkZW50aXR5KDIwMDIpOiBQZWxpY3VsYSBkZSBhY2Npb24geSBzdXNwZW5zbywgZGUgdW4gaG9tYnJlIHF1ZSB0aWVuZSBhbW5lc2lhIHkgbG8gcXVpZXJlbiBtYXRhci4NCg0KQnVlbm8gZGUgYWNhIHBvZGVtb3Mgc2FjYXIgcXVlIGxhcyBkb3Mgb3RyYXMgcG9yIHByaW1lcmEgdmlzdGEgbm8gdGVuZHJpYSBzZW50aWRvLCBwZXJvIHNpIGxvIHBvZGVtb3MgdmVyIHBvciBwYXJ0ZSBxdWUgc29uIDIgcGVsaWN1bGFzIGRlIGFjY2lvbiBsYXMgY3VhbGVzIHN1ZWxlbiBzZXIgdmlzdGFzIGVuIHN1IG1heW9yaWEgcG9yIGhvbWJyZSwgcXVlIHB1ZWRlbiB0ZW5lciBoaWpvcyBlc28geWEgdGllbmUgbWFzIHNlbnRpZG8uIE9zZWEgcG9yIHVuYSBwYXJ0ZSBlc3RhcmlhbiBsbyBxdWUgdmllcm9uIGxvcyBwYWRyZXMgeSBkZXNwdWVzIG5lbW8gb3JpZW50YWRvIGEgc3VzIGhpam9zLg0KDQojIyMgRnJlYWt5IEZyaWRheQ0KYGBge3J9DQpmaWx0cmFkby5yZWdsYXMuRkYgPC0gc3Vic2V0KHggPSBhc3NvY2lhdGlvbi5ydWxlcywNCiAgICAgICAgICAgICAgICAgICAgICAgICAgc3Vic2V0ID0gcmhzICVhaW4lIGMoIkZyZWFreSBGcmlkYXkiKSkNCmFydWxlczo6aW5zcGVjdChmaWx0cmFkby5yZWdsYXMuRkYpDQpgYGANCkZyZWFreSBGcmlkYXkoMjAwMyk6IGVzIHVuYSBwZWxpY3VsYSBzb2JyZSB1bmEgbWFkcmUgeSB1bmEgaGlqYSBxdWUgc2UgcGVsaWFiYW4gbXVjaG8sIHkgcG9yIGFsZ28gZGUgdW5hIG1hbGRpY2lvbiBvIGFsZ28gYXNpIGNhbWJpYW4gZGUgY3VlcnBvIHkgdml2ZW4gbGEgdmlkYSBkZSBsYSBvdHJhLiBFcyB1bmEgcGVsaWN1bGEgcXVlIHBlcnNvbmFsbWVudGUgdmkgZW4gbWkgYWRvbGVjZW5jaWEgeSBtZSBndXN0by4NCg0KU3VwZXJzdGFyKDE5OTkpOiJNYXJ5IEdhbGxhZ2hlciwgZXN0dWRpYW50ZSBkZSBjb2xlZ2lvIGNhdMOzbGljbywgc3Vlw7FhIGNvbiBnYW5hciB1biBjb25jdXJzbyBkZSB0YWxlbnRvIHkgYmVzYXIgYSB1biBmdXRib2xpc3RhIGZhbW9zby4iIFRpcGljYSBwZWxpY3VsYSBwYXJhIGFkb2xlY2VudGUuDQpFbGxlbiBEZUdlbmVyZXM6IFRoZSBCZWdpbm5pbmcoMjAwMCk6IEJ1ZW5vIGFjYSBoYXkgdW4gdGlwbyBzaG93IGRlIEVsbGVuIHF1ZSBlcyB1bmEgbXVqZXIgc2VndW4gbG8gcXVlIHZpIHBvciBpbnRlcm5ldCB5IHRhbWJpZW4gb3RyYXMgcGVsaWN1bGFzIGVzIHVuYSBtdWplciBwb3B1bGFyIGVuIGVzdGFkb3MgdW5pZG9zIHBhcmEgZ2VudGUgZGUgZGlzdGludGEgZWRhZC4gIA0KDQpMbyBxdWUgc2UgcHVlZGUgbm90YXIgZGUgZXN0YXMgcGVsaWN1bGFzIGVzIHF1ZSBzb24gZW5mb2NhZGFzIGEgbG9zIGFkb2xlY2VudGVzLCBhZGVtYXMgZXN0YW4gbXV5IGNlcmNhIGVuIGxvcyByYW5nb3MgZGUgZXN0cmVub3Mgb3NlYSBkZSBsYSBtaXNtYSBlcG9jYS4gVGFtYmllbiBjYWJlIGxhIHBvc2liaWxpZGFkIGRlIHF1ZSBlc3RlIHJlbGFjaW9uYWRhIGNvbiBsYXMgY2hpY2FzIHF1ZSB0aWVuZW4gcHJvYmxlbWFzIGNvbiBkZXNjdWJyaXIgcXVpZW5lcyBzb24gbyBtYXMgZGlnYW1vcyBhIGxhIHBhcnRlIGRlIGhvbW9zZXh1YWxpZGFkIG8gZGUgcXVlcmVyIHNlciBvdHJhIHBlcnNvbmEuIEVzIGRlY2lyIGRlIGdlbnRlIHF1ZSBubyBzZSBzaWVudGUgY29tb2RhIGNvbnNpZ28gbWlzbW8geSBzZSBzaWVudGUgcmVjb25mb3J0YWRhcyBwb3IgbG9zIGRlIHRlbWFzIGRlIGNhbWJpbyBkZSBwZXJzb25hbGlkYWQsIHNlciBvdHJhIHBlcnNvbmEgeSBlbGxlbiBxdWUgZXMgdW4gY2VsZWJyaWRhZCBob21vc2V4dWFsIHF1ZSBhY3R1YWxtZW50ZSBlc3RhIGNhc2FkYSBjb24gdW5hIG11amVyLg0KDQojIyMgVGhlIFNpbGVuY2Ugb2YgdGhlIExhbWJzDQpgYGB7cn0NCmZpbHRyYWRvLnJlZ2xhcy5UU29mTCA8LSBzdWJzZXQoeCA9IGFzc29jaWF0aW9uLnJ1bGVzLA0KICAgICAgICAgICAgICAgICAgICAgICAgICBzdWJzZXQgPSByaHMgJWFpbiUgYygiVGhlIFNpbGVuY2Ugb2YgdGhlIExhbWJzIikpDQphcnVsZXM6Omluc3BlY3QoZmlsdHJhZG8ucmVnbGFzLlRTb2ZMKQ0KYGBgDQoNCk1yLiBTaG93OiBzZXJpZSBkZSBjb21lZGlhLg0KDQpaYXRhb2ljaGk6IHBhcmVjZSB1bmEgcGVsaWN1bGEgZGUgYWNjaW9uIHkgc2FtdXJhaS4NCg0KVGhlIFNpbGVuY2Ugb2YgdGhlIGxhbXBzOiBwZWxpY3VsYSBkZSBzdXNwZW5zbyB5IG5vIHNlIHNpIHRlcnJvciBwZXJvIGRlYmUgdGVuZXIgdW4gcG9jby4NCg0KRXN0YSBhc29jaWFjaW9uIG1lIGhhY2UgYWNvcmRhciBtdWNobyBkZSBlbCBzZXh0byBzZW50aWRvLCBsYSBjdWFsIHRpZW5lIHBlbGljdWxhcyBzaW1pbGFyZXMuIEVzdG8gaGFjZSBwZW5zYXIgcXVlIGRlYmUgaGFiZXIgcGVyc29uYXMgcXVlIHF1aXphcyBubyBzZWEgY29tbyBzZSBkaWpvIGFudGVzIGRlIHZlciBjb21lZGlhIGx1ZWdvIGRlIGxhIGRlIHRlcnJvci4gU2lubyBxdWl6YXMgc2VhbiB1bm9zIGdlbmVyb3MgbXV5IGFzb2NpYWRvcyBhIGFsZ3VuYXMgdGlwb3MgZGUgcGVyc29uYXMgZW4gcGFydGljdWxhciBsYSBjdWFsIGRpc2ZydXRhbiBsYXMgcGVsaWN1YWxzIGRlIHN1c3Blc28sIHNlcmllcyBkZSBjb21lZGlhIHkgYWNjaW9uLiBQb2RyaWEgc2VyIG8gcXVlcmVtb3MgY3JlZXIgcXVlIGRlYmUgc2VyIGEgcGVyc29uYXMgbWFzY3VsaW5hcyBkZSBlZGFkIG1lZGlhLg0KDQoNCiMjIyBUaGUgQW1pdHl2aWxsZSBIb3Jyb3INCg0KYGBge3J9DQpmaWx0cmFkby5yZWdsYXMuQUggPC0gc3Vic2V0KHggPSBhc3NvY2lhdGlvbi5ydWxlcywNCiAgICAgICAgICAgICAgICAgICAgICAgICAgc3Vic2V0ID0gcmhzICVhaW4lIGMoIlRoZSBBbWl0eXZpbGxlIEhvcnJvciIpKQ0KYXJ1bGVzOjppbnNwZWN0KGZpbHRyYWRvLnJlZ2xhcy5BSCkNCmBgYA0KVGhlIEFtaXR5dmlsbGUgSG9ycm9yKDIwMDUpOiBlcyB1bmEgcGVsaWN1bGEgZGUgdGVycm9yIHkgYWwgcGFyZWNlciBzYW5ncmllbnRhcyBwb3IgcXVlIHNlIHRyYXRhIGRlIGFzZXNpbmF0b3MuDQoNCkNhbmR5bWFuKDE5OTYpOiBlc3RhIHBhcmVjZSB1bmEgcGVsaWN1bGEgZGUgdGVycm9yIG1lZGlvIHBhcmVjaWRhIGEgZnJlZGR5IGtydWVnZXIgbyBhbGdvIGFzaSwgcXVlIGFsIHBhcmVjZXIgZXMgdW4gaG9tYnJlIHF1ZSBlcyB1biBhc2VzaW5vIGJhc2FkbyBlbiBjcmltZW5lcy4NCg0KRGF5bGlnaHQoMTk5Mik6IGVzIGxhIHVuYSBwZWxpY3VsYSBkZSBzdXNwZW5zbyBlbiBsYSBxdWUgYWN0dWFsIHN0YWxsb25lLCBlbiBsYSBjdWFsIHNlIHRyYXRhIGRlIHVuYXMgcGVyc29uYXMgcXVlIHNlIHF1ZWRhbiBhdHJhcGFkYXMgZGVudHJvIGRlIHVuIHR1bmVsIGx1ZWdvIGRlIHVuYXMgZXhwbG9zaW9uZXMgZSBpbnRlbmFyIHNhbGlyLiBZIHBvciBsYXMgY2lyY3Vuc3RhbmNpYXMgaGF5IGVzY2VuYXMgZGUgc3VzcGVuc29zLCB0ZW5zaW9uIHkgYSB2ZWNlcyBzYW5ncmllbnRhcyBwZXJvIG1hcyBwb3IgbGFzIHBlcnNvbmFzIHF1ZSBzZSBsYXN0aW1hbi4NCg0KRWwgcGF0cm9uIHF1ZSBzZSBkZXNjcmliZSBhY2EgZXMgaG9yaWVudGFkYSBhIGxhcyBlc2NlbmFzIGNvbiBzYW5ncmUgeSB0ZW5zaW9uLCBxdWl6YXMgbWFzIGEgdGVuc2lvbi4gU29uIGxhcyBwZXJzb25hcyBxdWUgbGVzIGd1c3RhIGVzYSBzZW5zYWNpb24geSBkaXNmcnV0YW4gZGUgZXN0ZSB0aXBvIGRlIHBlbGljdWxhcy4NCg0KDQojIyMgTG92ZSBBY3R1YWxseQ0KYGBge3J9DQpmaWx0cmFkby5yZWdsYXMuTEEgPC0gc3Vic2V0KHggPSBhc3NvY2lhdGlvbi5ydWxlcywNCiAgICAgICAgICAgICAgICAgICAgICAgICAgc3Vic2V0ID0gcmhzICVhaW4lIGMoIkxvdmUgQWN0dWFsbHkiKSkNCmFydWxlczo6aW5zcGVjdChmaWx0cmFkby5yZWdsYXMuTEEpDQpgYGANCg0KIyMjIEJyaWRnZXQgSm9uZXMncyBEaWFyeQ0KYGBge3J9DQpmaWx0cmFkby5yZWdsYXMuQkpEIDwtIHN1YnNldCh4ID0gYXNzb2NpYXRpb24ucnVsZXMsDQogICAgICAgICAgICAgICAgICAgICAgICAgIHN1YnNldCA9IHJocyAlYWluJSBjKCJCcmlkZ2V0IEpvbmVzJ3MgRGlhcnkiKSkNCmFydWxlczo6aW5zcGVjdChmaWx0cmFkby5yZWdsYXMuQkpEKQ0KYGBgDQpFc3RhIGVzIG11eSBwYXJlY2lkYSBhbCBhbsOhbGlzaXMsIFdoaWxlIFlvdSBXZXJlIFNsZWVwaW5nLCBwb3JxdWUgdGllbmUgbXVjaG8gdGVtYSBkZSByb21hbmNlLCBhdW5xdWUgdGFtYmnDqW4gcG9kcsOtYW1vcyB2ZXIgb3RyYSBzaW1pbGl0dWQgcmVzcGVjdG8gYSBsYSBwcm90YWdvbmlzdGFzIHByaW5jaXBhbGVzIHF1ZSBsYXMgMyBzb24gY29tbyBjaGljYXMgcXVlIGJ1c2NhbiBhIHVuYSBwYXJlamEgcXVpesOhcyBtZWRpbyBzb8OxYWRhIG8gYWxnbyBhc8OtLg0KDQoNCiMjIyBUaGUgV2l6YXJkIG9mIE96OiBDb2xsZWN0b3IncyBFZGl0aW9uDQpgYGB7cn0NCmZpbHRyYWRvLnJlZ2xhcy5PeiA8LSBzdWJzZXQoeCA9IGFzc29jaWF0aW9uLnJ1bGVzLA0KICAgICAgICAgICAgICAgICAgICAgICAgICBzdWJzZXQgPSByaHMgJWFpbiUgYygiVGhlIFdpemFyZCBvZiBPejogQ29sbGVjdG9yJ3MgRWRpdGlvbiIpKQ0KYXJ1bGVzOjppbnNwZWN0KGZpbHRyYWRvLnJlZ2xhcy5PeikNCmBgYA0KVGhlIFdpemFyZCBvZiBPejogUGVsw61jdWxhIGNsw6FzaWNhIGRlIGZhbnRhc8OtYSB5IGF2ZW50dXJhDQoNCkEgTmlnaHRtYXJlIG9uIEVsbSBTdHJlZXQgMzogRHJlYW0gV2FycmlvcnM6IGNsw6FzaWNvIGRlIHRlcnJvciB5IG1pZWRvIGRvbmRlIGFwYXJlY2UgRnJlZGR5IEtydWVnZXIuDQpNYWRlbGluZTogYXZlbnR1cmEgeSBjb21lZGlhLCBtdXkgdHJhbnF1aWxhIGFsIHBhcmVjZXIuDQoNCg0KRW4gZXN0ZSBjYXNvIHBvZGVtb3MgdmVyIHVuIGNvbXBvcnRhbWllbnRvIG1lam9yIGRlc2NyaXB0byBkZSBsbyBoYWJsYWRvIGFudGVyaW9ybWVudGUgc29icmUgbGFzIHBlbMOtY3VsYXMgZGUgdGVycm9yIHkgbHVlZ28gYWxnbyBtw6FzIHRyYW5xdWlsbyBwYXJhIHBhc2FyIGVsIHN1c3RvLiBUZW5pZW5kbyBlbiBjdWVudGEgcXVlIHRlbmVtb3MgYSBpenF1aWVyZGEgdW5hIGRlIHRlcnJvciwgcXVlIGNyZW8gcmVjb3JkYXIgcXVlIGRhYmEgYmFzdGFudGUgbWllZG8geSBvdHJhIHRyYW5xdWlsYS4gWSBjb21vIGxhIHBhcnRlIGRlcmVjaGEgZWwgbWFnbyBkZSBveiwgcG9kZW1vcyB2ZXIgcXVlIHNlIG11ZXN0cmEgZGUgZm9ybWEgbcOhcyBjbGFyYSBlbCBoZWNobyBxdWUgZGUgdmVyIHVuYSBwZWzDrWN1bGEgZGUgdGVycm9yIHNpZW5kbyB1bmEgcGVyc29uYSBsYSBjdWFsIGRpc2ZydXRhIGRlIHBlbMOtY3VsYXMgY29tbyBtYWRlbGluZSwgdmEgYSBzZXIgbXV5IHByb2JhYmxlIHF1ZSB2ZWFzIG1hZ28gZGUgb3ogeWEgcXVlIGVzIHBvcHVsYXIgeSB0cmFucXVpbGEuIMOTc2VhIHBvZHLDrWFtb3MgaWRlbnRpZmljYXIgYSBsYXMgcGVyc29uYXMgcXVlIG5vIGVzdMOhbiBhY29zdHVtYnJhZGFzIGEgdmVyIHBlbMOtY3VsYXMgZGUgdGVycm9yIG8gbGEgc2llbnRlbiBkZW1hc2lhZG8uDQoNCiMjIyBTbGVlcGxlc3MgaW4gU2VhdHRsZQ0KYGBge3J9DQpmaWx0cmFkby5yZWdsYXMuU2luUyA8LSBzdWJzZXQoeCA9IGFzc29jaWF0aW9uLnJ1bGVzLA0KICAgICAgICAgICAgICAgICAgICAgICAgICBzdWJzZXQgPSByaHMgJWFpbiUgYygiU2xlZXBsZXNzIGluIFNlYXR0bGUiKSkNCmFydWxlczo6aW5zcGVjdChmaWx0cmFkby5yZWdsYXMuU2luUykNCmBgYA0KDQojIyMgTGV0aGFsIFdlYXBvbg0KYGBge3J9DQpmaWx0cmFkby5yZWdsYXMuTFcgPC0gc3Vic2V0KHggPSBhc3NvY2lhdGlvbi5ydWxlcywNCiAgICAgICAgICAgICAgICAgICAgICAgICAgc3Vic2V0ID0gcmhzICVhaW4lIGMoIkxldGhhbCBXZWFwb24iKSkNCmFydWxlczo6aW5zcGVjdChmaWx0cmFkby5yZWdsYXMuTFcpDQpgYGANCk5vIHNlIHZlIHVuIGNvbXBvcnRhbWllbnRvIGludGVyZXNhbnRlIHlhIHF1ZSBzb24gcGVsaWN1bGFzIGRlIGFjY2lvbiBkZXBvcnRlIGV0Yy4gTGEgY3VhbCBlc3RhcmlhIGVuZm9jYWRvIGEgaG9tYnJlIGRlIG1lZGlhbmEgZWRhZC4NCiMjIyBBbWVyaWNhbiBCZWF1dHkNCmBgYHtyfQ0KZmlsdHJhZG8ucmVnbGFzLkFCIDwtIHN1YnNldCh4ID0gYXNzb2NpYXRpb24ucnVsZXMsDQogICAgICAgICAgICAgICAgICAgICAgICAgIHN1YnNldCA9IHJocyAlYWluJSBjKCJBbWVyaWNhbiBCZWF1dHkiKSkNCmFydWxlczo6aW5zcGVjdChmaWx0cmFkby5yZWdsYXMuQUIpDQpgYGANCg0KIyMjIEJhcmJlcnNob3ANCmBgYHtyfQ0KZmlsdHJhZG8ucmVnbGFzLmJhcmJlciA8LSBzdWJzZXQoeCA9IGFzc29jaWF0aW9uLnJ1bGVzLA0KICAgICAgICAgICAgICAgICAgICAgICAgICBzdWJzZXQgPSByaHMgJWFpbiUgYygiQmFyYmVyc2hvcCIpKQ0KYXJ1bGVzOjppbnNwZWN0KGZpbHRyYWRvLnJlZ2xhcy5iYXJiZXIpDQpgYGANCg0KQmFyYmVyc2hvcDogY29tZWRpYS4NCg0KVGhlIGNvb2tvdXQ6IGNvbWVkaWEuDQoNClRoZSBoaXRjaGVyOiBhY2Npb24geSBwb2xpY2lhY28uDQoNCg0KQWPDoSBoYXkgYWxnbyBpbnRlcmVzYW50ZSBxdWUgcHVlZGUgc2VyIGFsZ28gcXVlIHRlbmdhIHZlciBjb24gbGEgZ2VudGUgZGUgY29sb3IsIHRlbmllbmRvIGVuIGN1ZW50YSBxdWUgZW1wZXrDsyBlbiBFRVVVLiBCYXJiZXJzaG9wIGVzIHVuYSBwZWzDrWN1bGEgZGUgY29tZWRpYSBkb25kZSBhcGFyZWNlIGxhIHTDrXBpY2EgZmFtaWxpYSBkZSBjb2xvciwgdGhlIGNvb2tvdXQgbcOhcyBkZSBsbyBtaXNtby4gUGVybyBwb3Igb3RybyBsYWRvIHRoZSBoaXRjaGVyIGVzIGNvbW8gYWwgcGFyZWNlciBtdXkgb3JpZW50YWRvIGEgbG9zICJibGFuY29zIi4gTG8gcXVlIG1lIGhhY2UgcGVuc2FyIHRlbmllbmRvIGVuIGN1ZW50YSBxdWUgcG9yIGxvIGdlbmVyYWwgbG9zIGFkb2xlc2NlbnRlcyBkZSBjb2xvciBzb24gbcOhcyBsaWJlcmFsZXMgY29uIHRlbWFzIGRlIHBlbMOtY3VsYXMsIG3DunNpY2EgbyBwYXJlY2UgcmVzcGVjdG8gYWwgdGVtYSByYWNpYWwgKFNlZ8O6biBsbyBxdWUgdmVvIGVuIHBlbMOtY3VsYXMsIG5vIHPDqSBzaSBzZXLDoSBhc8OtIHZlcmRhZGVyYW1lbnRlLCBwZXJvIHBhcmVjZSBxdWUgc2llbXByZSBzb24gbcOhcyBuZWNpb3MgbG9zIGFkdWx0b3MgcmVzcGVjdG8gYSBlc3RvcyBhbGdvIGRlIHZlcmRhZCBkZWJlIHRlbmVyKS4NCg0KDQojIyMgTW9vbnN0cnVjaw0KYGBge3J9DQpmaWx0cmFkby5yZWdsYXMuTW9vbiA8LSBzdWJzZXQoeCA9IGFzc29jaWF0aW9uLnJ1bGVzLA0KICAgICAgICAgICAgICAgICAgICAgICAgICBzdWJzZXQgPSByaHMgJWFpbiUgYygiTW9vbnN0cnVjayIpKQ0KYXJ1bGVzOjppbnNwZWN0KGZpbHRyYWRvLnJlZ2xhcy5Nb29uKQ0KYGBgDQpQZWzDrWN1bGFzIG9yaWVudGFkb3MgYSBhZG9sZXNjZW50ZXMgbcOhcyBlc3BlY2lhbG1lbnRlIGFsIGfDqW5lcm8gZmVtZW5pbm8uDQoNCiMjIyBUaGUgUmVjcnVpdA0KYGBge3J9DQpmaWx0cmFkby5yZWdsYXMucmVjcnVpdCA8LSBzdWJzZXQoeCA9IGFzc29jaWF0aW9uLnJ1bGVzLA0KICAgICAgICAgICAgICAgICAgICAgICAgICBzdWJzZXQgPSByaHMgJWFpbiUgYygiVGhlIFJlY3J1aXQiKSkNCmFydWxlczo6aW5zcGVjdChmaWx0cmFkby5yZWdsYXMucmVjcnVpdCkNCmBgYA0KQWPDoSBoYXkgdW5hcyByZWdsYXMgZXh0cmHDsWFzIHByaW1lcm8gdGVuZW1vcyBIYXJ0wrRzIFdhciBkZSBndWVycmEgYWNjacOzbiB5IFRoZSBSZWNydWl0IG3DoXMgZGUgbG9zIG1pc21vLiBNaWVudHJhcyBxdWUgU2V4IGFuZCB0aGUgY2l0eSBlcyBwb2NvIG3DoXMgb3JpZW50YWRvIGEgbXVqZXJlcywgbG8gcXVlIGNyZW8gcXVlIHB1ZWRlIHNlciBxdWUgc2UgdmVhIGVsIHRlbWEgZGUgbGFzIHBhcmVqYXMgasOzdmVuZXMuDQoNCiMjIDQuMTAgUHJvcHVlc3RhIGRlIHV0aWxpemFjaW9uDQoNCkVzdG9zIGRhdG9zIGxvcyBwb2RyaWFtb3MgdXRpbGl6YXIgZGUgdmFyaWFzIGZvcm1hcywgcHJpbWVybyBwZW5zYW5kbyBlbiBlc2EgZXBvY2EgZXJhIGRlIGFscXVpbGVyIGRlIHBlbGljdWxhcyBubyBjb21vIGFob3JhIGRlIHN0cmVhbWluZywgbG8gcXVlIHNlIHBvZHJpYSB1dGlsaXphciBzZXJpYSBwb25lciBjb21ib3MgZGUgZGVzY3VlbnRvIHBvciBjb21wcmFyL2FscXVpbGFyIDIgcGVsaWN1bGFzIG1hcyBsYXMgY3VhbGVzIGFwYXJlc2NhbiBlbiBsYXMgcXVlIHRlbmdhbiB1bmEgcmVnbGEgZGUgYXNvY2lhY2lvbiBwYXJhIGFzaSBwb3RlbmNpYXIgZWwgbmVnb2NpbyB5IGxhcyB2ZW50YXMvYWxxdWlsZXIgY29tbyBsbyBoYWNlciBhbWF6b24geSBvdHJhcyBwYWdpbmFzIGRlIHZlbnRhLiANCg0KVmllbmRvbG8gZGVzZGUgbGEgcGVsaWN1bGEgYWN0dWFsIGRlIG5ldGZsaXggY29tbyBwbGF0YWZvcm1hIGRlIHN0cmVhbWluZyBsYSB1dGlsaXphZGFkIG8gbGEgYXBsaWNhY2lvbiBkZSBlc3RhIHF1aXphcyB5YSBlcyBhbGdvIHF1ZSBoYWNlIG5ldGZsaXggY29tbyBsYSByZWNvbWVuZGFjaW9uIHBvciB2ZXIgMSBvIDIgcGVsaWN1bGFzIHRlIHJlY29taWVuZGFuIG90cmFzIG1hcywgZW4gdW5hIGxpc3RhIGRlIHZhcmlhcyBwZWxpY3VsYXMuIFRhbWJpZW4gYSBsYSBmaW5hbGl6YWNpb24gZGUgdW5hIHBlbGljdWxhL3NlcmllLCBsYSByZWNvbWVuZGFjaW9uZXMgZXNhcyBxdWUgdGUgYXBhcmVjZW4gMiBvIDMgcGVsaWN1bGFzIHZhcmlhcyBzZXJpZXMgcXVlIHRlIGRpY2UgcXVlIHRlIHB1ZWRlbiBndXN0YXIgdGFtYmllbi4gDQoNCkF1bnF1ZSwgbGEgb3RyYSBpZGVhIHF1ZSBzZSBtZSBvY3VycmlvIGVzIGFsZ28gcXVlIGVuIHJlYWxpZGFkIG5vIHNlIHNpIGFwbGljYXJhIG5ldGZsaXgsIGxhIHF1ZSBzZXJpYSBlbCB0ZW1hIGRlIGdlc3Rpb24gZGUgbGEgZGlzcG9uaWJpbGlkYWQgZGUgcGVsaWN1bGFzIHBvciBwYWlzZXMsIGJ1ZW5vIHRlbmllbmRvIGVuIGN1ZW50YSBxdWUgdGVuZXMgZW4gbnVlc3Ryb3MgY2Fzb3MgMyBwZWxpY3VsYXMgcmVsYWNpb25hZGFzIHBvciBsYSByZWdsYSBkZSBsYSBhc29jaWFjaW9uIHBvZHJpYXMgY29tbyAicHJlZGVjaXIiIG8gbWFzIGJpZW4gZGljaG8gdG9tYXIgZGVjaXNpb25lcyBkZSBzaSBtZXRlciBvIHNhY2FyIHBlbGljdWxhcywgcG9yIGVqZW1wbG8sIHF1ZSB0ZW5nYXMgZW4geCBwYWlzIGRvcyBwZWxpY3VsYXMgZGUgbGEgcGFydGUgaXpxdWllcmRhIGxhIGN1YWwgdGVuZ2EgbXVjaG8gcmVjaWJpbWllbnRvIHBvciBlc2UgcGFpcyB5IG11Y2hhIHZpenVhbGl6YWNpb25lcywgY2xhc2lmaWNpYWNpb25lcyBwb3NpdGl2YXMsZXRjLiBQb2RyaWFzIHRvbWFyIGxhIGRlY2lzaW9uIHNpIGVzIHF1ZSBubyBlc3RhcmlhIGRpc3BvbmlibGUgeWEgbGEgcGVsaWN1bGEgZGUgbGEgbWFubyBkZXJlY2hhLCBhZ3JlZ2FybGEgYSBlc2UgcGFpcyB5YSBxdWUgdGVuaWVuZG8gZW4gY3VlbnRhIHEgbGEgbWF5b3JpYSBkZSBsYXMgcGVsaWN1bGFzIHRpZW5lbiB1bmEgY29uZmlhbnphIGRlbCA4MCUgZW50b25jZXMgZGUgYW50ZSBtYW5vcyBzYWJyaWFzIHF1ZSBjb24gZXNhIHBlbGljdWxhIHBhc2FyaWEgYWxnbyBwYXJlY2lkby4gDQoNCg0KIyA1LiBDbHVzdGVyOiBQZWxpY3VsYXMgcG9wdWxhcmVzDQojIyA1LjEgUHJlcGFyYWNpb24gZGUgbG9zIGRhdG9zLg0KUHJpbWVybyB2YW1vcyBhIHJlYWxpemFyIGVsIGNhbGN1bG8gZGVsIHJhdGluZyBwcm9tZWRpbyBkZSBsYXMgcGVsaWN1bGFzIHkgbGEgY2FudGlkYWQgZGUgY2xhc2lmaWNhY2lvbmVzIHF1ZSBzZSBsZSBoaWNpZXJvbiB1bmEgcGVsaWN1bGEgcGFyYSBwb2RlciByZWFsaXphciBlbCBhbmFsaXNpcyBkZSBjdWFsZXMgc2VyaWFuIGxhcyBwZWxpY3VsYXMgcG9wdWxhcmVzLg0KYGBge3J9DQptdF9tZWFuIDwtIGRhdGFzZXQucmF3ICU+JSANCiAgZ3JvdXBfYnkoSWRQZWxpY3VsYSkgJT4lIA0KICBkcGx5cjo6c3VtbWFyaXNlKHJhdGluZ19tZWFuID0gbWVhbihSYXRpbmcpKSAgDQptdF9jb3VudCA8LSBkYXRhc2V0LnJhdyAlPiUgDQogIGdyb3VwX2J5KElkUGVsaWN1bGEpICU+JSANCiAgZHBseXI6OnN1bW1hcmlzZShyYXRpbmdfY291bnQgPSBkcGx5cjo6bigpKSAgDQoNCm10X2NvbXBsZXRvPC0gbWVyZ2UobXRfbWVhbixtdF9jb3VudCxieT0gIklkUGVsaWN1bGEiKQ0KYGBgDQojIyA1LjIgRXNjYWxhZG8gZGUgZGF0b3MuDQpFc2NhbGFtb3MgbG9zIGRhdG9zIGV4Y2VwdG8gZWwgZGUgSWRQZWxpY3VsYXMuDQpgYGB7cn0NCmRhdGFzZXQuc2NhbGVkIDwtIHNjYWxlKG10X2NvbXBsZXRvWywtMV0pDQpgYGANCg0KIyMgNS4zIEVsZWdpciB0YW1hw7FvcyBkZSBjbHVzdGVyLg0KR3JhZmljYW1vcyB1biBwbG90IHF1ZSBub3MgZGljZSBlbCBudW1lcm8gb3B0aW1vIHNlZ8O6biBlbCBjbHVzdGVyIHBvciBuw7ptZXJvIGRlIGNsdXN0ZXIuIERlIGxhIGN1YWwgZWxlZ2lyZW1vcyBlbCB0YW1hw7FvIDYuDQpgYGB7cn0NCmZ2aXpfbmJjbHVzdChkYXRhc2V0LnNjYWxlZCwga21lYW5zLG1ldGhvZCA9ICJ3c3MiKQ0KYGBgDQoNCmBgYHtyfQ0Kc2V0LnNlZWQoMTI0MykNCmsubWVhbnMuZml0IDwtIGttZWFucyhkYXRhc2V0LnNjYWxlZCwgY2VudGVycyA9IDYsIG5zdGFydCA9IDEwMCkNCnByaW50KGsubWVhbnMuZml0KQ0KYGBgDQojIyMgNS40IFBlc29zIGRlIGxvcyBhdHJpYnV0b3Mgc2VndW4gbG9zIGNsdXN0ZXJzLg0KRWwgdGVyY2VyIGNsdXN0ZXIgZXMgZWwgcXVlIHRpZW5lIGxvcyB2YWxvcmVzIGRlbCByYXRpbmdfbWVhbiBjb21vIGVsIGRlIHJhdGluZ19jb3VudCB0YW50byBwb3NpdGl2bywgYWx0b3MgeSBlcXVpbGlicmFkb3Mobm8gbXVjaG8gZGUgdW5vIHkgcG9jbyBkZWwgb3RybykgcG9yIGxvIGN1YWwgc2Vyw61hIGVsIGNsdXN0ZXIgc2Vnw7puIGxvcyBkYXRvcyBlbCBxdWUgbm9zIGludGVyZXNhcsOtYS4NCmBgYHtyfQ0Kay5tZWFucy5maXQkY2VudGVycw0KYGBgDQojIyMgNS41IFRhbWHDsW8gZGUgbG9zIGNsdXN0ZXJzLg0KQWNhIHNlIHB1ZWRlIHZlciBxdWUgZWwgY2x1c3RlciAzIGVzIGVsICBxdWUgdGllbmUgbWVub3MgcGVsaWN1bGFzIGVuIGVsLiBTZWd1biB2ZW5pbW9zIHZpZW5kbyBlc3RvIHF1aXphcyBubyBoYWdhIG1hcyBmYWNpbCBlbCBmaWx0cmFkby4NCmBgYHtyfQ0Kay5tZWFucy5maXQkc2l6ZQ0KYGBgDQojIyMgNS42IFZpenVhbGl6YWNpb24gZGUgbG9zIGNsdXN0ZXJzDQpFbiBlc3RlIHBsb3QgcG9kZW1vcyBub3RhciBxdWUgZWwgY2x1c3RlciBncmlzIGVzIGVsIGRlIGxhcyBwZWzDrWN1bGFzICJwb3B1bGFyZXMiLCBtaWVudHJhcyBxdWUgZWwgcm9qbyBzb24gbGFzIHBlbMOtY3VsYXMgcHJvbWVkaWFzIG5pIG1hbGEgbmkgYnVlbmFzLiBMdWVnbyBsYXMgcXVlIHF1ZWRhbiBlcyB1biBjb25qdW50byBtYXNvIG1lbm9zIHF1aXrDoXMgZGUgbGFzIHF1ZSBtZW5vcyBzZSB2ZW4gbyBxdWl6w6FzIGxhcyBxdWUgYXBlbmFzIHNhbGVuLiBMYSBjZWxlc3RlIGNsYXJvIHBvciBlamVtcGxvIGRlYmUgc2VyIGxhcyBwZWzDrWN1bGFzIG51ZXZhcyBwZXJvIHF1ZSBlc3TDoSB0ZW5pZW5kbyB1bmEgYnVlbmEgcHVudHVhY2nDs24gbWllbnRyYXMgbGEgY2VsZXN0ZSBmdWVydGVzIGxhcyBxdWUgbmFkaWUgdmUgeSBhIG5hZGllIGd1c3RhLiBMYSBhenVsIGxhIHF1ZSBzb24gbWVkaWFuYW1lbnRlIG1hbGFzIHBlcm8gcXVlIGxhcyB2ZW4gY29uIG3DoXMgZnJlY3VlbmNpYXMuIFkgbGEgYW1hcmlsbGEgbGEgcXVlIG5vIHNvbiBuaSBtYWxhcyBuaSBidWVuYSwgcGVybyB0YW1wb2NvIHNvbiBtdXkgdmlzdGFzLg0KYGBge3J9DQpmdml6X2NsdXN0ZXIob2JqZWN0ID0gay5tZWFucy5maXQsIGRhdGEgPSBkYXRhc2V0LnNjYWxlZCwgZWxsaXBzZS50eXBlID0gIm5vcm0iLCBnZW9tID0gInBvaW50IiwgbWFpbiA9ICJDbHVzdGVyIHBlbGNpdWxhcyAiLCBzdGFuZCA9IEZBTFNFLCBwYWxldHRlID0gImpjbyIpICsgdGhlbWVfYncoKSArIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJub25lIikNCmBgYA0KDQojIyMgNS43IFZhbGlkYWNpb24gZGVsIGNsdXN0ZXIgY29uIGRhdG9zIHNpbXVsYWRvcyANClJlYWxpemFtb3MgbGEgc2ltdWxhY2nDs24gZGUgbG9zIGRhdG9zLCB5IHBvZGVtb3MgdmVyIHF1ZSBlbiBsYXMgcHJpbWVyYXMgZG9zIGNvbXBvbmVudGVzIHJlcHJlc2VudGFuIGVsIDEwMCUgZGUgbG9zIGRhdG9zIHBvciBsbyB0YW50byBudWVzdHJvcyBkYXRvcyB5IHBvciBjb25zaWd1aWVudGUgbG9zIGNsdXN0ZXJzIGRlYmVyw61hbiBzZXJ2aXJub3MuDQpgYGB7cn0NCmRhdGEuc2ltdWxhdGVkIDwtIHB1cnJyOjptYXBfZGYobXRfY29tcGxldG9bLC0xXSwgLmYgPSBmdW5jdGlvbih4KXsgcnVuaWYobiA9IGxlbmd0aCh4KSwgbWluID0gbWluKHgpLCBtYXggPSBtYXgoeCkpIH0pDQoNCg0KIGRhdGEuc2ltdWxhdGVkLnNjYWxlZCA8LSBzY2FsZShkYXRhLnNpbXVsYXRlZCkNCmBgYA0KDQpgYGB7cn0NCmsubWVhbnMuc2ltdWxhZG9zIDwtIGttZWFucyh4ID0gZGF0YS5zaW11bGF0ZWQuc2NhbGVkLCBjZW50ZXJzID0gNikNCg0KZnZpel9jbHVzdGVyKG9iamVjdCA9IGsubWVhbnMuc2ltdWxhZG9zLCBkYXRhID0gZGF0YS5zaW11bGF0ZWQuc2NhbGVkLCBlbGxpcHNlLnR5cGUgPSAibm9ybSIsIGdlb20gPSAicG9pbnQiLCBtYWluID0gIkRhdG9zICIsIHN0YW5kID0gRkFMU0UsIHBhbGV0dGUgPSAiamNvIikgKyB0aGVtZV9idygpICsgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gIm5vbmUiKQ0KDQpgYGANCg0KDQojIyA1LjggRmlsdHJhZG8gZGUgbG9zIGRhdG9zIHkgbGlzdGEgZGUgcmVjb21lbmRhY2lvbnMuDQoNCkJ1ZW5vIGx1ZWdvIGRlIHRvbWFyIGVsIGNsdXN0ZXIgMywgbG8gdm9sdmVtb3MgYSBhc29jaWFyIGNvbiBudWVzdHJvIGRhdGFzZXQgb3JpZ2luYWwgZWwgY3VhbCB0aWVuZSBsb3MgdMOtdHVsb3MgZGUgbGFzIHBlbMOtY3VsYXMsIHkgb3JkZW5hbW9zIGRlIGZvcm1hIGRlc2NlbmRlbnRlIHBvciBlbCByYXRpbmdfbWVhbiwgZW50b25jZXMgdG9tYW1vcyBsb3MgcHJpbWVyb3MgMjAgcGVsw61jdWxhcywgeSBzZSBwdWVkZSB2ZXIgcXVlIGVzYSBzZXLDrWFuIGxhcyBwZWzDrWN1bGFzIHBvcHVsYXJlcyBkZSBuZXhmbGl4LCBjb21vIHNlIHB1ZWRlIHZlciBhIHNpbXBsZSB2aXN0YSBzb24gcGVsw61jdWxhcyBxdWUgbXVjaG9zIGRlIG5vc290cm9zIG5vcyBndXN0YSB5IHNhYmVtb3MgcXVlIHNvbiBmYW1vc2FzIHkgYnVlbmFzIHBlbMOtY3VsYXMuDQpgYGB7cn0NCnBsb3RfZGF0YSA8LSBjYmluZChtdF9jb21wbGV0bywgay5tZWFucy5maXQkY2x1c3RlcikNCmBgYA0KDQpgYGB7cn0NCnJlY29tZW5kYWRhczwtIHN1YnNldChwbG90X2RhdGEsIGsubWVhbnMuZml0JGNsdXN0ZXI9PTMpDQpyZWNvbWVuZGFkYXM8LW1lcmdlKHg9cmVjb21lbmRhZGFzLHk9ZGF0YXNldC5tb3ZpZXMsYnk9IklkUGVsaWN1bGEiKQ0KcmVjb21lbmRhZGFzID0gcmVjb21lbmRhZGFzW29yZGVyKHJlY29tZW5kYWRhcyRyYXRpbmdfbWVhbixkZWNyZWFzaW5nID0gVFJVRSksXQ0KaGVhZChyZWNvbWVuZGFkYXMsbj0yMCkNCmBgYA0KDQoNCg0KDQojIDYuIFdvcmQgQ2xvdWQNCiMjIDYuMSBKdW50YXIgbG9zIGRhdGFzZXQgZGUgcGVsaWN1bGFzDQpgYGB7cn0NCnBlbGljdWxhcy5jbG91ZDwtIG1lcmdlKGRhdGFzZXQubW92aWVzLGRfb3ZlcnZpZXcsYnk9Im9yaWdpbmFsX3RpdGxlIikNCmBgYA0KDQoNCmBgYHtyfQ0KcGVsaWN1bGFzLmNsb3VkLnNlbGVjdDwtc2VsZWN0KHBlbGljdWxhcy5jbG91ZCxvcmlnaW5hbF90aXRsZSxJZFBlbGljdWxhLG92ZXJ2aWV3KQ0KYGBgDQoNCmBgYHtyfQ0KcGVsaWN1bGFzLmNsb3VkLnNlbGVjdDwtIG1lcmdlKHBlbGljdWxhcy5jbG91ZC5zZWxlY3QsZF9nZW5lcm9zLGJ5Lng9Im9yaWdpbmFsX3RpdGxlIiwgYnkueT0ib3JpZ2luYWxUaXRsZSIpDQoNCmBgYA0KDQpgYGB7cn0NCnBlbGljdWxhcy5jbG91ZC5zZWxlY3Q8LXNlbGVjdChwZWxpY3VsYXMuY2xvdWQuc2VsZWN0LG9yaWdpbmFsX3RpdGxlLElkUGVsaWN1bGEsb3ZlcnZpZXcsZ2VucmVzKQ0KYGBgDQoNCiMjIDYuMiBMaW1waWFyIGRhdG9zIHF1ZSBudWxvcyANCg0KYGBge3J9DQpzYXBwbHkocGVsaWN1bGFzLmNsb3VkLnNlbGVjdCwgZnVuY3Rpb24oeCkgc3VtKGlzLm5hKHgpKSkNCmBgYA0KDQoNCmBgYHtyfQ0KcGVsaWN1bGFzLmNsb3VkLnNlbGVjdCA8LSBuYS5vbWl0KHBlbGljdWxhcy5jbG91ZC5zZWxlY3QpDQpgYGANCg0KDQoNCg0KIyMgNi4yIExpbXBpYXIgZGF0b3MgcXVlIGR1cGxpY2Fkb3MgDQoNCmBgYHtyfQ0KcGVsaWN1bGFzLmNsb3VkLmNsZWFuIDwtIHBlbGljdWxhcy5jbG91ZC5zZWxlY3QgJT4lIGRpc3RpbmN0KG9yaWdpbmFsX3RpdGxlLCAua2VlcF9hbGwgPSBUUlVFKQ0KDQpgYGANCg0KIyMgNi4zIFRhbWHDsW8gZGVsIGRhdGFzZXQgbGltcGlvDQoNCmBgYHtyfQ0KZGltKHBlbGljdWxhcy5jbG91ZC5jbGVhbikNCmBgYA0KDQojIyA2LjQgQnVzY2FyIGxvcyBjbGllbnRlcyBxdWUgcmVhbGl6byBsYXMgbWF5b3JlcyBjbGFzaWZpY2FjaW9uZXMgcG9zaXRpdmFzLg0KDQpgYGB7cn0NCmNsaWVudGVfY291bnQgPC0gZGF0YXNldC5yYXcgJT4lIA0KIGZpbHRlcihSYXRpbmc+PTMpICAlPiUNCiAgZ3JvdXBfYnkoSWRDbGllbnRlKSAlPiUgDQogIGRwbHlyOjpzdW1tYXJpc2UocGVsaWN1bGFzX2NvdW50ID0gZHBseXI6Om4oKSkgDQoNCmNsaWVudGVfY291bnQ9Y2xpZW50ZV9jb3VudFtvcmRlcihjbGllbnRlX2NvdW50JHBlbGljdWxhc19jb3VudCxkZWNyZWFzaW5nID0gVFJVRSksXQ0KDQpoZWFkKGNsaWVudGVfY291bnQpDQpgYGANCg0KLSBFbCBjbGllbnRlIHF1ZSBwdW50dW8gbcOhcyB2ZWNlcyBsbyBoaXpvIGVuIDE1NTggb3BvcnR1bmlkYWRlcy4NCg0KIyMgNi41IFdvcmQgQ2xvdWQgcG9yIGNsaWVudGUNCiMjIyBDbGllbnRlIDE2NjQwMTANCg0KIyMjIyBXb3JkIGNsb3VkIHBvciB0aXR1bG8uDQpWZW1vcyBsYXMgcGFsYWJyYXMgbWFzIGZyZWN1ZW50ZXMsIHkgdGVuZW1vcyBxdWUgcXVpdGFyIGxhcyBxdWUgY29uc2lkZXJlbW9zIHF1ZSBubyBkZWJlcmlhbiBpci4NCmBgYHtyfQ0KY2xpZW50ZTwtZGF0YXNldC5yYXcgJT4lIA0KICBmaWx0ZXIoSWRDbGllbnRlPT0xNjY0MDEwKSAgJT4lIA0KIGZpbHRlcihSYXRpbmc+PTMpICANCg0KY2xpZW50ZTwtIG1lcmdlKGNsaWVudGUscGVsaWN1bGFzLmNsb3VkLmNsZWFuLGJ5PSJJZFBlbGljdWxhIikNCg0KDQpjbGllbnRlIDwtIGNsaWVudGUgJT4lIHNlbGVjdChJZFBlbGljdWxhLG9yaWdpbmFsX3RpdGxlLG92ZXJ2aWV3LElkQ2xpZW50ZSxSYXRpbmcsRmVjaGEpDQpuYW1lcyhjbGllbnRlKVtuYW1lcyhjbGllbnRlKSA9PSAnSWRQZWxpY3VsYSddIDwtICdkb2NfaWQnDQpuYW1lcyhjbGllbnRlKVtuYW1lcyhjbGllbnRlKSA9PSAnb3JpZ2luYWxfdGl0bGUnXSA8LSAndGV4dCcNCg0KDQoNCm1vdmllc19zb3VyY2UgPC0gRGF0YWZyYW1lU291cmNlKGNsaWVudGUpDQptb3ZpZXNfY29ycHVzIDwtIFZDb3JwdXMobW92aWVzX3NvdXJjZSkNCg0KDQptb3ZpZXNfY29ycHVzIDwtIG1vdmllc19jb3JwdXMgJT4lIA0KICAgICAgICAgICAgICAgIHRtX21hcChyZW1vdmVQdW5jdHVhdGlvbikgJT4lDQogICAgICAgICAgICAgICAgdG1fbWFwKGNvbnRlbnRfdHJhbnNmb3JtZXIodG9sb3dlcikpICU+JQ0KICAgICAgICAgICAgICAgIHRtX21hcChyZW1vdmVXb3JkcywgYyhzdG9wd29yZHMoImVuIiksImRvbnQiLA0KICAgICAgICAgICAgICAgICAgICAgICAiY2FuIiwianVzdCIsICJjYW50IiwgImdldCIsICJnb3QiLCJvbmUiLCJ0d28iKSkNCiAgICAgICAgICAgICAgICAjIHRtX21hcChjb250ZW50X3RyYW5zZm9ybWVyKHJlcGxhY2VfbnVtYmVyKSkgJT4lDQogICAgICAgICAgICAgICAgIyB0bV9tYXAoY29udGVudF90cmFuc2Zvcm1lcihicmFja2V0WCkpICU+JQ0KICAgICAgICAgICAgICAgICMgdG1fbWFwKHN0cmlwV2hpdGVzcGFjZSkgDQogICAgICAgICAgICAgICAgDQojU3RlbSBkb2N1bWVudA0KY2xlYW5fY29ycHVzIDwtIHRtX21hcChtb3ZpZXNfY29ycHVzLCBzdGVtRG9jdW1lbnQpDQojQ3JlYXRlIFRlcm0gRG9jdW1lbnQgTWF0cml4DQpjbGVhbl90ZG0gICAgPC0gVGVybURvY3VtZW50TWF0cml4KGNsZWFuX2NvcnB1cykNCiNDb252ZXJ0aW5nIFRETSB0byBtYXRyaXggZm9yIEFuYWx5c2lzDQpjbGVhbl9tICAgICAgPC0gYXMubWF0cml4KGNsZWFuX3RkbSkNCg0KDQoNCmZyZXF3b3JkcyA8LSBmaW5kRnJlcVRlcm1zKGNsZWFuX3RkbSwgbG93ZnJlcSA9IDEwKQ0KDQojIGNhbGN1bGF0ZSB0aGUgZnJlcXVlbmN5IG9mIHdvcmRzDQp2IDwtIHNvcnQocm93U3VtcyhjbGVhbl9tKSwgZGVjcmVhc2luZz1UUlVFKQ0KbXlOYW1lcyA8LSBuYW1lcyh2KQ0KayA8LSB3aGljaChuYW1lcyh2KT09Im1pbmVycyIpDQpteU5hbWVzW2tdIDwtICJtaW5pbmciDQpkIDwtIGRhdGEuZnJhbWUod29yZD1teU5hbWVzLCBmcmVxPXYpDQp3b3JkY2xvdWQoZCR3b3JkLCBkJGZyZXEsIG1pbi5mcmVxPTMsbWF4LndvcmRzPTMwLCByYW5kb20ub3JkZXI9RkFMU0Uscm90LnBlcj0wLjM1LCBjb2xvcnM9YnJld2VyLnBhbCg4LCAiRGFyazIiKSkNCmBgYA0KIyMjIyBXb3JkIGNsb3VkIHBvciBPdmVydmlldy4NCmBgYHtyfQ0KY2xpZW50ZTwtZGF0YXNldC5yYXcgJT4lIA0KICBmaWx0ZXIoSWRDbGllbnRlPT0xNjY0MDEwKSAgJT4lIA0KIGZpbHRlcihSYXRpbmc+PTMpICANCg0KY2xpZW50ZTwtIG1lcmdlKGNsaWVudGUscGVsaWN1bGFzLmNsb3VkLmNsZWFuLGJ5PSJJZFBlbGljdWxhIikNCg0KDQpjbGllbnRlIDwtIGNsaWVudGUgJT4lIHNlbGVjdChJZFBlbGljdWxhLG92ZXJ2aWV3LG9yaWdpbmFsX3RpdGxlLElkQ2xpZW50ZSxSYXRpbmcsRmVjaGEpDQpuYW1lcyhjbGllbnRlKVtuYW1lcyhjbGllbnRlKSA9PSAnSWRQZWxpY3VsYSddIDwtICdkb2NfaWQnDQpuYW1lcyhjbGllbnRlKVtuYW1lcyhjbGllbnRlKSA9PSAnb3ZlcnZpZXcnXSA8LSAndGV4dCcNCg0KDQoNCm1vdmllc19zb3VyY2UgPC0gRGF0YWZyYW1lU291cmNlKGNsaWVudGUpDQptb3ZpZXNfY29ycHVzIDwtIFZDb3JwdXMobW92aWVzX3NvdXJjZSkNCg0KDQptb3ZpZXNfY29ycHVzIDwtIG1vdmllc19jb3JwdXMgJT4lIA0KICAgICAgICAgICAgICAgIHRtX21hcChyZW1vdmVQdW5jdHVhdGlvbikgJT4lDQogICAgICAgICAgICAgICAgdG1fbWFwKGNvbnRlbnRfdHJhbnNmb3JtZXIodG9sb3dlcikpICU+JQ0KICAgICAgICAgICAgICAgIHRtX21hcChyZW1vdmVXb3JkcywgYyhzdG9wd29yZHMoImVuIiksImRvbnQiLA0KICAgICAgICAgICAgICAgICAgICAgICAiY2FuIiwianVzdCIsICJjYW50IiwgImdldCIsICJnb3QiLCJvbmUiLCJ0d28iKSkNCiAgICAgICAgICAgICAgICAjIHRtX21hcChjb250ZW50X3RyYW5zZm9ybWVyKHJlcGxhY2VfbnVtYmVyKSkgJT4lDQogICAgICAgICAgICAgICAgIyB0bV9tYXAoY29udGVudF90cmFuc2Zvcm1lcihicmFja2V0WCkpICU+JQ0KICAgICAgICAgICAgICAgICMgdG1fbWFwKHN0cmlwV2hpdGVzcGFjZSkgDQogICAgICAgICAgICAgICAgDQojU3RlbSBkb2N1bWVudA0KY2xlYW5fY29ycHVzIDwtIHRtX21hcChtb3ZpZXNfY29ycHVzLCBzdGVtRG9jdW1lbnQpDQojQ3JlYXRlIFRlcm0gRG9jdW1lbnQgTWF0cml4DQpjbGVhbl90ZG0gICAgPC0gVGVybURvY3VtZW50TWF0cml4KGNsZWFuX2NvcnB1cykNCiNDb252ZXJ0aW5nIFRETSB0byBtYXRyaXggZm9yIEFuYWx5c2lzDQpjbGVhbl9tICAgICAgPC0gYXMubWF0cml4KGNsZWFuX3RkbSkNCg0KDQoNCmZyZXF3b3JkcyA8LSBmaW5kRnJlcVRlcm1zKGNsZWFuX3RkbSwgbG93ZnJlcSA9IDEwKQ0KDQojIGNhbGN1bGF0ZSB0aGUgZnJlcXVlbmN5IG9mIHdvcmRzDQp2IDwtIHNvcnQocm93U3VtcyhjbGVhbl9tKSwgZGVjcmVhc2luZz1UUlVFKQ0KbXlOYW1lcyA8LSBuYW1lcyh2KQ0KayA8LSB3aGljaChuYW1lcyh2KT09Im1pbmVycyIpDQpteU5hbWVzW2tdIDwtICJtaW5pbmciDQpkIDwtIGRhdGEuZnJhbWUod29yZD1teU5hbWVzLCBmcmVxPXYpDQp3b3JkY2xvdWQoZCR3b3JkLCBkJGZyZXEsIG1pbi5mcmVxPTMsbWF4LndvcmRzPTMwLCByYW5kb20ub3JkZXI9RkFMU0Uscm90LnBlcj0wLjM1LCBjb2xvcnM9YnJld2VyLnBhbCg4LCAiRGFyazIiKSkNCmBgYA0KIyMjIyBXb3JkIGNsb3VkIHBvciBHZW5yZXMuDQpgYGB7cn0NCmNsaWVudGU8LWRhdGFzZXQucmF3ICU+JSANCiAgZmlsdGVyKElkQ2xpZW50ZT09MTY2NDAxMCkgICU+JSANCiBmaWx0ZXIoUmF0aW5nPj0zKSAgDQoNCmNsaWVudGU8LSBtZXJnZShjbGllbnRlLHBlbGljdWxhcy5jbG91ZC5jbGVhbixieT0iSWRQZWxpY3VsYSIpDQoNCg0KY2xpZW50ZSA8LSBjbGllbnRlICU+JSBzZWxlY3QoSWRQZWxpY3VsYSxnZW5yZXMsb3JpZ2luYWxfdGl0bGUsb3ZlcnZpZXcsSWRDbGllbnRlLFJhdGluZyxGZWNoYSkNCm5hbWVzKGNsaWVudGUpW25hbWVzKGNsaWVudGUpID09ICdJZFBlbGljdWxhJ10gPC0gJ2RvY19pZCcNCm5hbWVzKGNsaWVudGUpW25hbWVzKGNsaWVudGUpID09ICdnZW5yZXMnXSA8LSAndGV4dCcNCg0KDQoNCm1vdmllc19zb3VyY2UgPC0gRGF0YWZyYW1lU291cmNlKGNsaWVudGUpDQptb3ZpZXNfY29ycHVzIDwtIFZDb3JwdXMobW92aWVzX3NvdXJjZSkNCg0KDQptb3ZpZXNfY29ycHVzIDwtIG1vdmllc19jb3JwdXMgJT4lIA0KICAgICAgICAgICAgICAgIHRtX21hcChyZW1vdmVQdW5jdHVhdGlvbikgJT4lDQogICAgICAgICAgICAgICAgdG1fbWFwKGNvbnRlbnRfdHJhbnNmb3JtZXIodG9sb3dlcikpICU+JQ0KICAgICAgICAgICAgICAgIHRtX21hcChyZW1vdmVXb3JkcywgYyhzdG9wd29yZHMoImVuIiksImRvbnQiLA0KICAgICAgICAgICAgICAgICAgICAgICAiY2FuIiwianVzdCIsICJjYW50IiwgImdldCIsICJnb3QiLCJvbmUiLCJ0d28iLCJuYW1lIiwiODc4IikpDQogICAgICAgICAgICAgICAgIyB0bV9tYXAoY29udGVudF90cmFuc2Zvcm1lcihyZXBsYWNlX251bWJlcikpICU+JQ0KICAgICAgICAgICAgICAgICMgdG1fbWFwKGNvbnRlbnRfdHJhbnNmb3JtZXIoYnJhY2tldFgpKSAlPiUNCiAgICAgICAgICAgICAgICAjIHRtX21hcChzdHJpcFdoaXRlc3BhY2UpIA0KICAgICAgICAgICAgICAgIA0KI1N0ZW0gZG9jdW1lbnQNCmNsZWFuX2NvcnB1cyA8LSB0bV9tYXAobW92aWVzX2NvcnB1cywgc3RlbURvY3VtZW50KQ0KI0NyZWF0ZSBUZXJtIERvY3VtZW50IE1hdHJpeA0KY2xlYW5fdGRtICAgIDwtIFRlcm1Eb2N1bWVudE1hdHJpeChjbGVhbl9jb3JwdXMpDQojQ29udmVydGluZyBURE0gdG8gbWF0cml4IGZvciBBbmFseXNpcw0KY2xlYW5fbSAgICAgIDwtIGFzLm1hdHJpeChjbGVhbl90ZG0pDQoNCg0KDQpmcmVxd29yZHMgPC0gZmluZEZyZXFUZXJtcyhjbGVhbl90ZG0sIGxvd2ZyZXEgPSAxMCkNCg0KIyBjYWxjdWxhdGUgdGhlIGZyZXF1ZW5jeSBvZiB3b3Jkcw0KdiA8LSBzb3J0KHJvd1N1bXMoY2xlYW5fbSksIGRlY3JlYXNpbmc9VFJVRSkNCm15TmFtZXMgPC0gbmFtZXModikNCmsgPC0gd2hpY2gobmFtZXModik9PSJtaW5lcnMiKQ0KbXlOYW1lc1trXSA8LSAibWluaW5nIg0KZCA8LSBkYXRhLmZyYW1lKHdvcmQ9bXlOYW1lcywgZnJlcT12KQ0Kd29yZGNsb3VkKGQkd29yZCwgZCRmcmVxLCBtaW4uZnJlcT0zLG1heC53b3Jkcz0zMCwgcmFuZG9tLm9yZGVyPUZBTFNFLHJvdC5wZXI9MC4zNSwgY29sb3JzPWJyZXdlci5wYWwoOCwgIkRhcmsyIikpDQpgYGANCg0KLSBQb3IgdGl0dWxvOiBNYW4sIERheSwgQm95LCBCaWcsIEdpcmwsIChGZWFyLCBSdW4sIERpZSwgQmxvb2QpDQotIFBvciBPdmVydmlldzogRmluZCwgTWFuLCBMb3ZlLCBGcmllbmQsIExpZmUsIFlvdW5nLCAoTXVyZGVyLCBUb3duLCBLaWxsKQ0KLSBQb3IgR2VuZXJvOiBDb21lZHksIERyYW1hLCBEb2N1bWVudGFyeQ0KDQotIEJhc2FkbyBlbiBsb3MgdGVybWlub3MgbWFzIGZyZWN1ZW50ZXMgYWwgdXN1YXJpbyBzZSBsZSBwb2RyaWEgcmVjb21lbmRhciBwZWxpY3VsYXMgY2FzdWFsZXMgZGUgY29tZWRpYSwgZHJhbWEgbyBjb21lZGlhcyByb21hbnRpY2FzLiBTaW4gZW1iYXJnbyBhcGFyZWNlbiB0ZXJtaW5vcyBxdWUgcG9kcmlhbiBpbmRpY2FyIGNpZXJ0byBndXN0byBvIGluY2xpbmFjaW9uIGRlbCB1c3VhcmlvIGhhY2lhIGxhcyBwZWxpY3VsYXMgeSBkb2N1bWVudGFsZXMgc29icmUgQXNlc2lub3MgZGViaWRvIGEgbGFzIHBhbGFicmFzIGNvbW8gIm1pZWRvIiwgImNvcnJlciIsICJtdWVydGUiLCAic2FuZ3JlIiwgZXRjLiAgDQoNCiMjIyBDbGllbnRlIDcxNjE3Mw0KDQojIyMjIFdvcmQgY2xvdWQgcG9yIHRpdHVsby4NClZlbW9zIGxhcyBwYWxhYnJhcyBtYXMgZnJlY3VlbnRlcywgeSB0ZW5lbW9zIHF1ZSBxdWl0YXIgbGFzIHF1ZSBjb25zaWRlcmVtb3MgcXVlIG5vIGRlYmVyaWFuIGlyLg0KYGBge3J9DQpjbGllbnRlPC1kYXRhc2V0LnJhdyAlPiUgDQogIGZpbHRlcihJZENsaWVudGU9PTcxNjE3MykgICU+JSANCiBmaWx0ZXIoUmF0aW5nPj0zKSAgDQoNCmNsaWVudGU8LSBtZXJnZShjbGllbnRlLHBlbGljdWxhcy5jbG91ZC5jbGVhbixieT0iSWRQZWxpY3VsYSIpDQoNCg0KY2xpZW50ZSA8LSBjbGllbnRlICU+JSBzZWxlY3QoSWRQZWxpY3VsYSxvcmlnaW5hbF90aXRsZSxvdmVydmlldyxJZENsaWVudGUsUmF0aW5nLEZlY2hhKQ0KbmFtZXMoY2xpZW50ZSlbbmFtZXMoY2xpZW50ZSkgPT0gJ0lkUGVsaWN1bGEnXSA8LSAnZG9jX2lkJw0KbmFtZXMoY2xpZW50ZSlbbmFtZXMoY2xpZW50ZSkgPT0gJ29yaWdpbmFsX3RpdGxlJ10gPC0gJ3RleHQnDQoNCg0KDQptb3ZpZXNfc291cmNlIDwtIERhdGFmcmFtZVNvdXJjZShjbGllbnRlKQ0KbW92aWVzX2NvcnB1cyA8LSBWQ29ycHVzKG1vdmllc19zb3VyY2UpDQoNCg0KbW92aWVzX2NvcnB1cyA8LSBtb3ZpZXNfY29ycHVzICU+JSANCiAgICAgICAgICAgICAgICB0bV9tYXAocmVtb3ZlUHVuY3R1YXRpb24pICU+JQ0KICAgICAgICAgICAgICAgIHRtX21hcChjb250ZW50X3RyYW5zZm9ybWVyKHRvbG93ZXIpKSAlPiUNCiAgICAgICAgICAgICAgICB0bV9tYXAocmVtb3ZlV29yZHMsIGMoc3RvcHdvcmRzKCJlbiIpLCJkb250IiwNCiAgICAgICAgICAgICAgICAgICAgICAgImNhbiIsImp1c3QiLCAiY2FudCIsICJnZXQiLCAiZ290Iiwib25lIiwidHdvIikpDQogICAgICAgICAgICAgICAgIyB0bV9tYXAoY29udGVudF90cmFuc2Zvcm1lcihyZXBsYWNlX251bWJlcikpICU+JQ0KICAgICAgICAgICAgICAgICMgdG1fbWFwKGNvbnRlbnRfdHJhbnNmb3JtZXIoYnJhY2tldFgpKSAlPiUNCiAgICAgICAgICAgICAgICAjIHRtX21hcChzdHJpcFdoaXRlc3BhY2UpIA0KICAgICAgICAgICAgICAgIA0KI1N0ZW0gZG9jdW1lbnQNCmNsZWFuX2NvcnB1cyA8LSB0bV9tYXAobW92aWVzX2NvcnB1cywgc3RlbURvY3VtZW50KQ0KI0NyZWF0ZSBUZXJtIERvY3VtZW50IE1hdHJpeA0KY2xlYW5fdGRtICAgIDwtIFRlcm1Eb2N1bWVudE1hdHJpeChjbGVhbl9jb3JwdXMpDQojQ29udmVydGluZyBURE0gdG8gbWF0cml4IGZvciBBbmFseXNpcw0KY2xlYW5fbSAgICAgIDwtIGFzLm1hdHJpeChjbGVhbl90ZG0pDQoNCg0KDQpmcmVxd29yZHMgPC0gZmluZEZyZXFUZXJtcyhjbGVhbl90ZG0sIGxvd2ZyZXEgPSAxMCkNCg0KIyBjYWxjdWxhdGUgdGhlIGZyZXF1ZW5jeSBvZiB3b3Jkcw0KdiA8LSBzb3J0KHJvd1N1bXMoY2xlYW5fbSksIGRlY3JlYXNpbmc9VFJVRSkNCm15TmFtZXMgPC0gbmFtZXModikNCmsgPC0gd2hpY2gobmFtZXModik9PSJtaW5lcnMiKQ0KbXlOYW1lc1trXSA8LSAibWluaW5nIg0KZCA8LSBkYXRhLmZyYW1lKHdvcmQ9bXlOYW1lcywgZnJlcT12KQ0Kd29yZGNsb3VkKGQkd29yZCwgZCRmcmVxLCBtaW4uZnJlcT0zLG1heC53b3Jkcz0zMCwgcmFuZG9tLm9yZGVyPUZBTFNFLHJvdC5wZXI9MC4zNSwgY29sb3JzPWJyZXdlci5wYWwoOCwgIkRhcmsyIikpDQpgYGANCiMjIyMgV29yZCBjbG91ZCBwb3IgT3ZlcnZpZXcuDQpgYGB7cn0NCmNsaWVudGU8LWRhdGFzZXQucmF3ICU+JSANCiAgZmlsdGVyKElkQ2xpZW50ZT09NzE2MTczKSAgJT4lIA0KIGZpbHRlcihSYXRpbmc+PTMpICANCg0KY2xpZW50ZTwtIG1lcmdlKGNsaWVudGUscGVsaWN1bGFzLmNsb3VkLmNsZWFuLGJ5PSJJZFBlbGljdWxhIikNCg0KDQpjbGllbnRlIDwtIGNsaWVudGUgJT4lIHNlbGVjdChJZFBlbGljdWxhLG92ZXJ2aWV3LG9yaWdpbmFsX3RpdGxlLElkQ2xpZW50ZSxSYXRpbmcsRmVjaGEpDQpuYW1lcyhjbGllbnRlKVtuYW1lcyhjbGllbnRlKSA9PSAnSWRQZWxpY3VsYSddIDwtICdkb2NfaWQnDQpuYW1lcyhjbGllbnRlKVtuYW1lcyhjbGllbnRlKSA9PSAnb3ZlcnZpZXcnXSA8LSAndGV4dCcNCg0KDQoNCm1vdmllc19zb3VyY2UgPC0gRGF0YWZyYW1lU291cmNlKGNsaWVudGUpDQptb3ZpZXNfY29ycHVzIDwtIFZDb3JwdXMobW92aWVzX3NvdXJjZSkNCg0KDQptb3ZpZXNfY29ycHVzIDwtIG1vdmllc19jb3JwdXMgJT4lIA0KICAgICAgICAgICAgICAgIHRtX21hcChyZW1vdmVQdW5jdHVhdGlvbikgJT4lDQogICAgICAgICAgICAgICAgdG1fbWFwKGNvbnRlbnRfdHJhbnNmb3JtZXIodG9sb3dlcikpICU+JQ0KICAgICAgICAgICAgICAgIHRtX21hcChyZW1vdmVXb3JkcywgYyhzdG9wd29yZHMoImVuIiksImRvbnQiLA0KICAgICAgICAgICAgICAgICAgICAgICAiY2FuIiwianVzdCIsICJjYW50IiwgImdldCIsICJnb3QiLCJvbmUiLCJ0d28iKSkNCiAgICAgICAgICAgICAgICAjIHRtX21hcChjb250ZW50X3RyYW5zZm9ybWVyKHJlcGxhY2VfbnVtYmVyKSkgJT4lDQogICAgICAgICAgICAgICAgIyB0bV9tYXAoY29udGVudF90cmFuc2Zvcm1lcihicmFja2V0WCkpICU+JQ0KICAgICAgICAgICAgICAgICMgdG1fbWFwKHN0cmlwV2hpdGVzcGFjZSkgDQogICAgICAgICAgICAgICAgDQojU3RlbSBkb2N1bWVudA0KY2xlYW5fY29ycHVzIDwtIHRtX21hcChtb3ZpZXNfY29ycHVzLCBzdGVtRG9jdW1lbnQpDQojQ3JlYXRlIFRlcm0gRG9jdW1lbnQgTWF0cml4DQpjbGVhbl90ZG0gICAgPC0gVGVybURvY3VtZW50TWF0cml4KGNsZWFuX2NvcnB1cykNCiNDb252ZXJ0aW5nIFRETSB0byBtYXRyaXggZm9yIEFuYWx5c2lzDQpjbGVhbl9tICAgICAgPC0gYXMubWF0cml4KGNsZWFuX3RkbSkNCg0KDQoNCmZyZXF3b3JkcyA8LSBmaW5kRnJlcVRlcm1zKGNsZWFuX3RkbSwgbG93ZnJlcSA9IDEwKQ0KDQojIGNhbGN1bGF0ZSB0aGUgZnJlcXVlbmN5IG9mIHdvcmRzDQp2IDwtIHNvcnQocm93U3VtcyhjbGVhbl9tKSwgZGVjcmVhc2luZz1UUlVFKQ0KbXlOYW1lcyA8LSBuYW1lcyh2KQ0KayA8LSB3aGljaChuYW1lcyh2KT09Im1pbmVycyIpDQpteU5hbWVzW2tdIDwtICJtaW5pbmciDQpkIDwtIGRhdGEuZnJhbWUod29yZD1teU5hbWVzLCBmcmVxPXYpDQp3b3JkY2xvdWQoZCR3b3JkLCBkJGZyZXEsIG1pbi5mcmVxPTMsbWF4LndvcmRzPTMwLCByYW5kb20ub3JkZXI9RkFMU0Uscm90LnBlcj0wLjM1LCBjb2xvcnM9YnJld2VyLnBhbCg4LCAiRGFyazIiKSkNCmBgYA0KIyMjIyBXb3JkIGNsb3VkIHBvciBHZW5yZXMuDQpgYGB7cn0NCmNsaWVudGU8LWRhdGFzZXQucmF3ICU+JSANCiAgZmlsdGVyKElkQ2xpZW50ZT09NzE2MTczKSAgJT4lIA0KIGZpbHRlcihSYXRpbmc+PTMpICANCg0KY2xpZW50ZTwtIG1lcmdlKGNsaWVudGUscGVsaWN1bGFzLmNsb3VkLmNsZWFuLGJ5PSJJZFBlbGljdWxhIikNCg0KDQpjbGllbnRlIDwtIGNsaWVudGUgJT4lIHNlbGVjdChJZFBlbGljdWxhLGdlbnJlcyxvcmlnaW5hbF90aXRsZSxvdmVydmlldyxJZENsaWVudGUsUmF0aW5nLEZlY2hhKQ0KbmFtZXMoY2xpZW50ZSlbbmFtZXMoY2xpZW50ZSkgPT0gJ0lkUGVsaWN1bGEnXSA8LSAnZG9jX2lkJw0KbmFtZXMoY2xpZW50ZSlbbmFtZXMoY2xpZW50ZSkgPT0gJ2dlbnJlcyddIDwtICd0ZXh0Jw0KDQoNCg0KbW92aWVzX3NvdXJjZSA8LSBEYXRhZnJhbWVTb3VyY2UoY2xpZW50ZSkNCm1vdmllc19jb3JwdXMgPC0gVkNvcnB1cyhtb3ZpZXNfc291cmNlKQ0KDQoNCm1vdmllc19jb3JwdXMgPC0gbW92aWVzX2NvcnB1cyAlPiUgDQogICAgICAgICAgICAgICAgdG1fbWFwKHJlbW92ZVB1bmN0dWF0aW9uKSAlPiUNCiAgICAgICAgICAgICAgICB0bV9tYXAoY29udGVudF90cmFuc2Zvcm1lcih0b2xvd2VyKSkgJT4lDQogICAgICAgICAgICAgICAgdG1fbWFwKHJlbW92ZVdvcmRzLCBjKHN0b3B3b3JkcygiZW4iKSwiZG9udCIsDQogICAgICAgICAgICAgICAgICAgICAgICJjYW4iLCJqdXN0IiwgImNhbnQiLCAiZ2V0IiwgImdvdCIsIm9uZSIsInR3byIsIm5hbWUiKSkNCiAgICAgICAgICAgICAgICAjIHRtX21hcChjb250ZW50X3RyYW5zZm9ybWVyKHJlcGxhY2VfbnVtYmVyKSkgJT4lDQogICAgICAgICAgICAgICAgIyB0bV9tYXAoY29udGVudF90cmFuc2Zvcm1lcihicmFja2V0WCkpICU+JQ0KICAgICAgICAgICAgICAgICMgdG1fbWFwKHN0cmlwV2hpdGVzcGFjZSkgDQogICAgICAgICAgICAgICAgDQojU3RlbSBkb2N1bWVudA0KY2xlYW5fY29ycHVzIDwtIHRtX21hcChtb3ZpZXNfY29ycHVzLCBzdGVtRG9jdW1lbnQpDQojQ3JlYXRlIFRlcm0gRG9jdW1lbnQgTWF0cml4DQpjbGVhbl90ZG0gICAgPC0gVGVybURvY3VtZW50TWF0cml4KGNsZWFuX2NvcnB1cykNCiNDb252ZXJ0aW5nIFRETSB0byBtYXRyaXggZm9yIEFuYWx5c2lzDQpjbGVhbl9tICAgICAgPC0gYXMubWF0cml4KGNsZWFuX3RkbSkNCg0KDQoNCmZyZXF3b3JkcyA8LSBmaW5kRnJlcVRlcm1zKGNsZWFuX3RkbSwgbG93ZnJlcSA9IDEwKQ0KDQojIGNhbGN1bGF0ZSB0aGUgZnJlcXVlbmN5IG9mIHdvcmRzDQp2IDwtIHNvcnQocm93U3VtcyhjbGVhbl9tKSwgZGVjcmVhc2luZz1UUlVFKQ0KbXlOYW1lcyA8LSBuYW1lcyh2KQ0KayA8LSB3aGljaChuYW1lcyh2KT09Im1pbmVycyIpDQpteU5hbWVzW2tdIDwtICJtaW5pbmciDQpkIDwtIGRhdGEuZnJhbWUod29yZD1teU5hbWVzLCBmcmVxPXYpDQp3b3JkY2xvdWQoZCR3b3JkLCBkJGZyZXEsIG1pbi5mcmVxPTMsbWF4LndvcmRzPTMwLCByYW5kb20ub3JkZXI9RkFMU0Uscm90LnBlcj0wLjM1LCBjb2xvcnM9YnJld2VyLnBhbCg4LCAiRGFyazIiKSkNCmBgYA0KDQotIFBvciBUaXR1bG86IEdpcmwsIE1hbiwgU29sZGllciwgVGltZSwgRGF5LCBDb3dib3kgKExlZ2VuZCwgQW1lcmljYW4sIFVuaXZlcnNlKQ0KLSBQb3IgT3ZlcnZpZXc6IEZpbmQsIExpZmUsIE5ldyxNYW4sIFllYXIsIFdvcmxkLCAoTXVyZGVyLCBBbWVyaWNhbiwgU2Nob29sKQ0KLSBQb3IgR2VuZXJvOiBDb21lZHksIERyYW1hLCBEb2N1bWVudGFyeS4NCg0KLSBUYW1iaWVuIGEgZXN0ZSB1c3VhcmlvIHNlIGxlIHBvZHJpYSByZWNvbWVuZGFyIHBlbGljdWxhcyBjYXN1YWxlcyBkZSBjb21lZGlhLCBkcmFtYSwgY29tZWRpYXMgcm9tYW50aWNhcywgZXRjLiBQYWxhYnJhcyBjbGF2ZXMgcXVlIHB1ZWRlbiBpbmRpY2FyIGNpZXJ0byBndXN0byBwb3IgbGFzIHBlbGljdWxhcyBkZSBndWVycmEsIGNvd2JveXMsIG8gZW5mcmVudGFtaWVudG9zLiBTZSBwb2RyaWEgcmVjb21lbmRhciBkb2N1bWVudGFsZXMgYWNlcmNhIGRlIExleWVuZGFzIG8gaGVyb2VzLCBwZXJzb25hcyBhbWVyaWNhbmFzIGZhbW9zYXMgeSBkb2N1bWVudGFsZXMgc29icmUgZWwgVW5pdmVyc28uIEFkZW1hcyBkb2N1bWVudGFsZXMgbyBwZWxpY3VsYXMgYWNlcmNhIGRlIEFzZXNpbm9zIG8gbWFzYWNyZXMgZW4gRXNjdWVsYXMuDQoNCg0KIyMjIENsaWVudGUgMTMxNDg2OQkNCg0KIyMjIyBXb3JkIGNsb3VkIHBvciB0aXR1bG8uDQpWZW1vcyBsYXMgcGFsYWJyYXMgbWFzIGZyZWN1ZW50ZXMsIHkgdGVuZW1vcyBxdWUgcXVpdGFyIGxhcyBxdWUgY29uc2lkZXJlbW9zIHF1ZSBubyBkZWJlcmlhbiBpci4NCmBgYHtyfQ0KY2xpZW50ZTwtZGF0YXNldC5yYXcgJT4lIA0KICBmaWx0ZXIoSWRDbGllbnRlPT0xMzE0ODY5KSAgJT4lIA0KIGZpbHRlcihSYXRpbmc+PTMpICANCg0KY2xpZW50ZTwtIG1lcmdlKGNsaWVudGUscGVsaWN1bGFzLmNsb3VkLmNsZWFuLGJ5PSJJZFBlbGljdWxhIikNCg0KDQpjbGllbnRlIDwtIGNsaWVudGUgJT4lIHNlbGVjdChJZFBlbGljdWxhLG9yaWdpbmFsX3RpdGxlLG92ZXJ2aWV3LElkQ2xpZW50ZSxSYXRpbmcsRmVjaGEpDQpuYW1lcyhjbGllbnRlKVtuYW1lcyhjbGllbnRlKSA9PSAnSWRQZWxpY3VsYSddIDwtICdkb2NfaWQnDQpuYW1lcyhjbGllbnRlKVtuYW1lcyhjbGllbnRlKSA9PSAnb3JpZ2luYWxfdGl0bGUnXSA8LSAndGV4dCcNCg0KDQoNCm1vdmllc19zb3VyY2UgPC0gRGF0YWZyYW1lU291cmNlKGNsaWVudGUpDQptb3ZpZXNfY29ycHVzIDwtIFZDb3JwdXMobW92aWVzX3NvdXJjZSkNCg0KDQptb3ZpZXNfY29ycHVzIDwtIG1vdmllc19jb3JwdXMgJT4lIA0KICAgICAgICAgICAgICAgIHRtX21hcChyZW1vdmVQdW5jdHVhdGlvbikgJT4lDQogICAgICAgICAgICAgICAgdG1fbWFwKGNvbnRlbnRfdHJhbnNmb3JtZXIodG9sb3dlcikpICU+JQ0KICAgICAgICAgICAgICAgIHRtX21hcChyZW1vdmVXb3JkcywgYyhzdG9wd29yZHMoImVuIiksImRvbnQiLA0KICAgICAgICAgICAgICAgICAgICAgICAiY2FuIiwianVzdCIsICJjYW50IiwgImdldCIsICJnb3QiLCJvbmUiLCJ0d28iKSkNCiAgICAgICAgICAgICAgICAjIHRtX21hcChjb250ZW50X3RyYW5zZm9ybWVyKHJlcGxhY2VfbnVtYmVyKSkgJT4lDQogICAgICAgICAgICAgICAgIyB0bV9tYXAoY29udGVudF90cmFuc2Zvcm1lcihicmFja2V0WCkpICU+JQ0KICAgICAgICAgICAgICAgICMgdG1fbWFwKHN0cmlwV2hpdGVzcGFjZSkgDQogICAgICAgICAgICAgICAgDQojU3RlbSBkb2N1bWVudA0KY2xlYW5fY29ycHVzIDwtIHRtX21hcChtb3ZpZXNfY29ycHVzLCBzdGVtRG9jdW1lbnQpDQojQ3JlYXRlIFRlcm0gRG9jdW1lbnQgTWF0cml4DQpjbGVhbl90ZG0gICAgPC0gVGVybURvY3VtZW50TWF0cml4KGNsZWFuX2NvcnB1cykNCiNDb252ZXJ0aW5nIFRETSB0byBtYXRyaXggZm9yIEFuYWx5c2lzDQpjbGVhbl9tICAgICAgPC0gYXMubWF0cml4KGNsZWFuX3RkbSkNCg0KDQoNCmZyZXF3b3JkcyA8LSBmaW5kRnJlcVRlcm1zKGNsZWFuX3RkbSwgbG93ZnJlcSA9IDEwKQ0KDQojIGNhbGN1bGF0ZSB0aGUgZnJlcXVlbmN5IG9mIHdvcmRzDQp2IDwtIHNvcnQocm93U3VtcyhjbGVhbl9tKSwgZGVjcmVhc2luZz1UUlVFKQ0KbXlOYW1lcyA8LSBuYW1lcyh2KQ0KayA8LSB3aGljaChuYW1lcyh2KT09Im1pbmVycyIpDQpteU5hbWVzW2tdIDwtICJtaW5pbmciDQpkIDwtIGRhdGEuZnJhbWUod29yZD1teU5hbWVzLCBmcmVxPXYpDQp3b3JkY2xvdWQoZCR3b3JkLCBkJGZyZXEsIG1pbi5mcmVxPTMsbWF4LndvcmRzPTMwLCByYW5kb20ub3JkZXI9RkFMU0Uscm90LnBlcj0wLjM1LCBjb2xvcnM9YnJld2VyLnBhbCg4LCAiRGFyazIiKSkNCmBgYA0KIyMjIyBXb3JkIGNsb3VkIHBvciBPdmVydmlldy4NCmBgYHtyfQ0KY2xpZW50ZTwtZGF0YXNldC5yYXcgJT4lIA0KICBmaWx0ZXIoSWRDbGllbnRlPT0xMzE0ODY5KSAgJT4lIA0KIGZpbHRlcihSYXRpbmc+PTMpICANCg0KY2xpZW50ZTwtIG1lcmdlKGNsaWVudGUscGVsaWN1bGFzLmNsb3VkLmNsZWFuLGJ5PSJJZFBlbGljdWxhIikNCg0KDQpjbGllbnRlIDwtIGNsaWVudGUgJT4lIHNlbGVjdChJZFBlbGljdWxhLG92ZXJ2aWV3LG9yaWdpbmFsX3RpdGxlLElkQ2xpZW50ZSxSYXRpbmcsRmVjaGEpDQpuYW1lcyhjbGllbnRlKVtuYW1lcyhjbGllbnRlKSA9PSAnSWRQZWxpY3VsYSddIDwtICdkb2NfaWQnDQpuYW1lcyhjbGllbnRlKVtuYW1lcyhjbGllbnRlKSA9PSAnb3ZlcnZpZXcnXSA8LSAndGV4dCcNCg0KDQoNCm1vdmllc19zb3VyY2UgPC0gRGF0YWZyYW1lU291cmNlKGNsaWVudGUpDQptb3ZpZXNfY29ycHVzIDwtIFZDb3JwdXMobW92aWVzX3NvdXJjZSkNCg0KDQptb3ZpZXNfY29ycHVzIDwtIG1vdmllc19jb3JwdXMgJT4lIA0KICAgICAgICAgICAgICAgIHRtX21hcChyZW1vdmVQdW5jdHVhdGlvbikgJT4lDQogICAgICAgICAgICAgICAgdG1fbWFwKGNvbnRlbnRfdHJhbnNmb3JtZXIodG9sb3dlcikpICU+JQ0KICAgICAgICAgICAgICAgIHRtX21hcChyZW1vdmVXb3JkcywgYyhzdG9wd29yZHMoImVuIiksImRvbnQiLA0KICAgICAgICAgICAgICAgICAgICAgICAiY2FuIiwianVzdCIsICJjYW50IiwgImdldCIsICJnb3QiLCJvbmUiLCJ0d28iKSkNCiAgICAgICAgICAgICAgICAjIHRtX21hcChjb250ZW50X3RyYW5zZm9ybWVyKHJlcGxhY2VfbnVtYmVyKSkgJT4lDQogICAgICAgICAgICAgICAgIyB0bV9tYXAoY29udGVudF90cmFuc2Zvcm1lcihicmFja2V0WCkpICU+JQ0KICAgICAgICAgICAgICAgICMgdG1fbWFwKHN0cmlwV2hpdGVzcGFjZSkgDQogICAgICAgICAgICAgICAgDQojU3RlbSBkb2N1bWVudA0KY2xlYW5fY29ycHVzIDwtIHRtX21hcChtb3ZpZXNfY29ycHVzLCBzdGVtRG9jdW1lbnQpDQojQ3JlYXRlIFRlcm0gRG9jdW1lbnQgTWF0cml4DQpjbGVhbl90ZG0gICAgPC0gVGVybURvY3VtZW50TWF0cml4KGNsZWFuX2NvcnB1cykNCiNDb252ZXJ0aW5nIFRETSB0byBtYXRyaXggZm9yIEFuYWx5c2lzDQpjbGVhbl9tICAgICAgPC0gYXMubWF0cml4KGNsZWFuX3RkbSkNCg0KDQoNCmZyZXF3b3JkcyA8LSBmaW5kRnJlcVRlcm1zKGNsZWFuX3RkbSwgbG93ZnJlcSA9IDEwKQ0KDQojIGNhbGN1bGF0ZSB0aGUgZnJlcXVlbmN5IG9mIHdvcmRzDQp2IDwtIHNvcnQocm93U3VtcyhjbGVhbl9tKSwgZGVjcmVhc2luZz1UUlVFKQ0KbXlOYW1lcyA8LSBuYW1lcyh2KQ0KayA8LSB3aGljaChuYW1lcyh2KT09Im1pbmVycyIpDQpteU5hbWVzW2tdIDwtICJtaW5pbmciDQpkIDwtIGRhdGEuZnJhbWUod29yZD1teU5hbWVzLCBmcmVxPXYpDQp3b3JkY2xvdWQoZCR3b3JkLCBkJGZyZXEsIG1pbi5mcmVxPTMsbWF4LndvcmRzPTMwLCByYW5kb20ub3JkZXI9RkFMU0Uscm90LnBlcj0wLjM1LCBjb2xvcnM9YnJld2VyLnBhbCg4LCAiRGFyazIiKSkNCmBgYA0KIyMjIyBXb3JkIGNsb3VkIHBvciBHZW5yZXMuDQpgYGB7cn0NCmNsaWVudGU8LWRhdGFzZXQucmF3ICU+JSANCiAgZmlsdGVyKElkQ2xpZW50ZT09MTMxNDg2OSkgICU+JSANCiBmaWx0ZXIoUmF0aW5nPj0zKSAgDQoNCmNsaWVudGU8LSBtZXJnZShjbGllbnRlLHBlbGljdWxhcy5jbG91ZC5jbGVhbixieT0iSWRQZWxpY3VsYSIpDQoNCg0KY2xpZW50ZSA8LSBjbGllbnRlICU+JSBzZWxlY3QoSWRQZWxpY3VsYSxnZW5yZXMsb3JpZ2luYWxfdGl0bGUsb3ZlcnZpZXcsSWRDbGllbnRlLFJhdGluZyxGZWNoYSkNCm5hbWVzKGNsaWVudGUpW25hbWVzKGNsaWVudGUpID09ICdJZFBlbGljdWxhJ10gPC0gJ2RvY19pZCcNCm5hbWVzKGNsaWVudGUpW25hbWVzKGNsaWVudGUpID09ICdnZW5yZXMnXSA8LSAndGV4dCcNCg0KDQoNCm1vdmllc19zb3VyY2UgPC0gRGF0YWZyYW1lU291cmNlKGNsaWVudGUpDQptb3ZpZXNfY29ycHVzIDwtIFZDb3JwdXMobW92aWVzX3NvdXJjZSkNCg0KDQptb3ZpZXNfY29ycHVzIDwtIG1vdmllc19jb3JwdXMgJT4lIA0KICAgICAgICAgICAgICAgIHRtX21hcChyZW1vdmVQdW5jdHVhdGlvbikgJT4lDQogICAgICAgICAgICAgICAgdG1fbWFwKGNvbnRlbnRfdHJhbnNmb3JtZXIodG9sb3dlcikpICU+JQ0KICAgICAgICAgICAgICAgIHRtX21hcChyZW1vdmVXb3JkcywgYyhzdG9wd29yZHMoImVuIiksImRvbnQiLA0KICAgICAgICAgICAgICAgICAgICAgICAiY2FuIiwianVzdCIsICJjYW50IiwgImdldCIsICJnb3QiLCJvbmUiLCJ0d28iLCJuYW1lIikpDQogICAgICAgICAgICAgICAgIyB0bV9tYXAoY29udGVudF90cmFuc2Zvcm1lcihyZXBsYWNlX251bWJlcikpICU+JQ0KICAgICAgICAgICAgICAgICMgdG1fbWFwKGNvbnRlbnRfdHJhbnNmb3JtZXIoYnJhY2tldFgpKSAlPiUNCiAgICAgICAgICAgICAgICAjIHRtX21hcChzdHJpcFdoaXRlc3BhY2UpIA0KICAgICAgICAgICAgICAgIA0KI1N0ZW0gZG9jdW1lbnQNCmNsZWFuX2NvcnB1cyA8LSB0bV9tYXAobW92aWVzX2NvcnB1cywgc3RlbURvY3VtZW50KQ0KI0NyZWF0ZSBUZXJtIERvY3VtZW50IE1hdHJpeA0KY2xlYW5fdGRtICAgIDwtIFRlcm1Eb2N1bWVudE1hdHJpeChjbGVhbl9jb3JwdXMpDQojQ29udmVydGluZyBURE0gdG8gbWF0cml4IGZvciBBbmFseXNpcw0KY2xlYW5fbSAgICAgIDwtIGFzLm1hdHJpeChjbGVhbl90ZG0pDQoNCg0KDQpmcmVxd29yZHMgPC0gZmluZEZyZXFUZXJtcyhjbGVhbl90ZG0sIGxvd2ZyZXEgPSAxMCkNCg0KIyBjYWxjdWxhdGUgdGhlIGZyZXF1ZW5jeSBvZiB3b3Jkcw0KdiA8LSBzb3J0KHJvd1N1bXMoY2xlYW5fbSksIGRlY3JlYXNpbmc9VFJVRSkNCm15TmFtZXMgPC0gbmFtZXModikNCmsgPC0gd2hpY2gobmFtZXModik9PSJtaW5lcnMiKQ0KbXlOYW1lc1trXSA8LSAibWluaW5nIg0KZCA8LSBkYXRhLmZyYW1lKHdvcmQ9bXlOYW1lcywgZnJlcT12KQ0Kd29yZGNsb3VkKGQkd29yZCwgZCRmcmVxLCBtaW4uZnJlcT0zLG1heC53b3Jkcz0zMCwgcmFuZG9tLm9yZGVyPUZBTFNFLHJvdC5wZXI9MC4zNSwgY29sb3JzPWJyZXdlci5wYWwoOCwgIkRhcmsyIikpDQpgYGANCg0KLSBQb3IgVGl0dWxvOiBNYW4sIExpdHRsZSwgQmxhY2ssIE1vb24sIFNvbGRpZXIsIChNb25zdGVyLCBCbG9vZCwgTWFkLCBOaWdodCkNCi0gUG9yIE92ZXJ2aWV3OiBGaW5kLCBMaWZlLCBGcmllbmQsIExvdmUsIE1hbiwgKERlYXRoLCBLaWxsKQ0KLSBQb3IgR2VuZXJvOiBDb21lZHksIERyYW1hLCBEb2N1bWVudGFyeSwgSG9ycm9yDQoNCi0gQWwgdXN1YXJpbyBzZSBsZSBwdWVkZSByZWNvbWVuZGFyIGNvbW8gYSB0b2RvcyBwZWxpY3VsYXMgY2FzdWFsZXMgZGUgY29tZWRpYSwgZHJhbWEsIGV0Yy4gU2UgcHVlZGUgb2JzZXJ2YXIgcHJlZGlzcG9zaWNpb24gYWwgZ2VuZXJvIGhvcnJvciB5IHBhbGFicmFzIGNvbW8gIk1vbnN0ZXIiLCAiQmxvb2QiIG8gIk5pZ2h0IiwgcG9yIGxvIHF1ZSBzZSBsZSBwb2RyaWEgcmVjb21lbmRhciBwZWxpY3VsYXMgZGUgbW91bnN0cnVvcywgem9tYmllcywgZXRjIHRhbnRvIGRlIGNvbWVkaWEocGFyb2RpYXMpIGNvbW8gZGUgaG9ycm9yLiBBZGVtw6FzIHRhbWJpZW4gcGVsaWN1bGFzIHNvYnJlIGd1ZXJyYXMuDQoNCiMgNy4gQ29uY2x1c2nDs24gDQoNClJlZ2xhcyBkZSBhc29jaWFjacOzbjoNCg0KUmVzcGVjdG8gYSBsYXMgcmVnbGFzIGRlIGFzb2NpYWNpw7NuIGNvbnNlZ3VpZGFzIGRlbCBkYXRhc2V0IHNlIHZpbyByZWdsYXMgcXVlIGRlc2NyaWJlbiBjb3NhcyBpbnRlcmVzYW50ZXMgcXVlIHB1ZWRlbiB0ZW5lciBhbGd1bmEgZXhwbGljYWNpw7NuIG11eSBwcm9mdW5kYSBzb2JyZSBlbCBjb21wb3J0YW1pZW50bywgZWwgZ3VzdG8gZGUgbGFzIHBlcnNvbmFzIHkgcXVpesOhcyBsb3MgcGVuc2FtaWVudG9zLiBBbGd1bmFzIG11eSBzaW1wbGVzLCB1biBwb2NvIG3DoXMgY29tcGxpY2FkYXMgeSBxdWl6w6FzIGEgc2ltcGxlIHZpc3RhIHNpbXBsZXMgcGVybyBxdWUgZXNjb25kZSBleHBsaWNhY2lvbmVzIG3DoXMgcHJvZnVuZGFzLiBUYW1iacOpbiwgc2Ugbm90YSBxdWUgcHVlZGUgaGFiZXIgZXhpc3RlbmNpYSBkZSBydWlkbyB0ZW5pZW5kbyBlbiBjdWVudGEgcXVlIG5vIGVzdGFtb3MgaGFibGFuZG8gZGUgdW4gY2xpZW50ZSBlbiBzw60sIGhhYnLDoSBvY2FzaW9uZXMgcXVlIGVzZSBjbGllbnRlIG5vIHJlcHJlc2VudGUgdW5hIHBlcnNvbmEgc2lubyB1bmEgZmFtaWxpYSwgcGFyZWphIG8gaGFzdGEgcXVpesOhcyBncnVwb3MgZGUgYW1pZ29zLg0KDQpDbHVzdGVyLVJlY29tZW5kYWNpb246DQpSZXNwZWN0byBhbCBjbHVzdGVyIGNyZW8gcXVlIHNlIG9idHV2byBidWVub3MgcmVzdWx0YWRvcywgcXVpesOhcyB1bm9zIGdydXBvcyBkaWbDrWNpbGVzIGRlIGRlc2NyaWJpciwgcGVybyBzZSBvYnR1dm8gZWwgcXVlIGludGVyZXNhYmEuIEFkZW3DoXMsIGVsIHJlc3VsdGFkbyBkZSBsYXMgMjAgcGVsw61jdWxhcyBxdWUgc2UgbXVlc3RyYXMgdGllbmUgbXVjaG8gc2VudGlkbyBwb3IgbG8gY3VhbCBwb2RlbW9zIGNvbmNsdWlyIHF1ZSBlcyB1bmEgdMOpY25pY2EgYmFzdGFudGUgZWZpY2llbnRlIGVuIGVzdGUgY2FzbyBwYXJhIHBvZGVyIHNlcGFyYXIgbGFzIHBlbMOtY3VsYXMuDQoNCldvcmQgQ2xvdWQ6DQpMYSB1dGlsaXphY2lvbiBkZWwgd29yZCBjbG91ZCBub3MgbW9zdHJvIHF1ZSBub3MgcHVlZGUgc2VyIGRlIHV0aWxpZGFkIHBhcmEgcmVjb21lbmRhY2lvbiBkZSB0aXBvcyBkZSBwZWxpY3VsYXMgeWEgcXVlIG5vcyBkYSB0ZW5kZW5jaWFzIHBlcnNvbmFsaXphZGFzIHRlbmllbmRvIGVuIGN1ZW50YSBsYXMgcGFsYWJyYXMgbWFzIGZyZWN1ZW50ZXMgZW4gZWwgdGl0dWxvLCByZXN1bWVuIHkgZ2VuZXJvcy4gSnVudGFuZG8gbG9zIDMgbm9zIGJyaW5kYSBiYXN0YW50ZSBpbmZvcm1hY2lvbi4NCg0KQ29uY2x1c2nDs24gZ2VuZXJhbDoNCg0KVG9kbyBsbyBoZWNobyBhbnRlcmlvcm1lbnRlLCBwZW5zYW1vcyBxdWUgZW4gY2llcnRvIG1vZG8gc29uIGFuw6FsaXNpcyBiYXN0YW50ZSBpbnRlcmVzYW50ZSBwZXJvIHRhbWJpw6luIHRlbmRyw61hbW9zIHF1ZSB0ZW5lciBlbiBjdWVudGEgcXVlIHNlIGVzdMOhIHRyYWJhamFuZG8gY29uIGVsIDEwJSBkZSBsb3MgZGF0b3MsIGF1bnF1ZSBlc3RlIHNlYSByZXByZXNlbnRhZG8gc2UgZGViZSBzZXIgY29uc2NpZW50ZSBxdWUgbG8gbWlzbW8gaGFicsOhIHVuIHNlc2dvIG8gcnVpZG8gcXVlIHNlIHRlbmdhLg0K