Paso 1. Importar base de datos

#1 Ajustes a la base de datos se reemplazaron las comas por espacios.  
#2 Se separó la fecha en día y hora con sus respectivos formatos.
#file.choose()
bd1<-read.csv("C:\\Users\\danyc\\OneDrive - Instituto Tecnologico y de Estudios Superiores de Monterrey\\Desktop\\Excel y CSV\\ventasfin1.csv")

Paso 2. Entender base de datos

summary(bd1)
##     BillNo            Itemname            Quantity            Date          
##  Length:522064      Length:522064      Min.   :-9600.00   Length:522064     
##  Class :character   Class :character   1st Qu.:    1.00   Class :character  
##  Mode  :character   Mode  :character   Median :    3.00   Mode  :character  
##                                        Mean   :   10.09                     
##                                        3rd Qu.:   10.00                     
##                                        Max.   :80995.00                     
##                                                                             
##      Time               Price              CustomerID       Country         
##  Length:522064      Min.   :-11062.060   Min.   :12346    Length:522064     
##  Class :character   1st Qu.:     1.250   1st Qu.:13950    Class :character  
##  Mode  :character   Median :     2.080   Median :15265    Mode  :character  
##                     Mean   :     3.827   Mean   :15317                      
##                     3rd Qu.:     4.130   3rd Qu.:16837                      
##                     Max.   : 13541.330   Max.   :18287                      
##                                          NA's   :134041
str(bd1)
## 'data.frame':    522064 obs. of  8 variables:
##  $ BillNo    : chr  "536365" "536365" "536365" "536365" ...
##  $ Itemname  : chr  "WHITE HANGING HEART T-LIGHT HOLDER" "WHITE METAL LANTERN" "CREAM CUPID HEARTS COAT HANGER" "KNITTED UNION FLAG HOT WATER BOTTLE" ...
##  $ Quantity  : int  6 6 8 6 6 2 6 6 6 32 ...
##  $ Date      : chr  "1/12/2010" "1/12/2010" "1/12/2010" "1/12/2010" ...
##  $ Time      : chr  "8:26:00" "8:26:00" "8:26:00" "8:26:00" ...
##  $ Price     : num  2.55 3.39 2.75 3.39 3.39 7.65 4.25 1.85 1.85 1.69 ...
##  $ CustomerID: int  17850 17850 17850 17850 17850 17850 17850 17850 17850 13047 ...
##  $ Country   : chr  "United Kingdom" "United Kingdom" "United Kingdom" "United Kingdom" ...
#install.packages("dplyr")
library(dplyr)
## 
## Attaching package: 'dplyr'
## The following objects are masked from 'package:stats':
## 
##     filter, lag
## The following objects are masked from 'package:base':
## 
##     intersect, setdiff, setequal, union
count(bd1, Country, sort = TRUE)
##                 Country      n
## 1        United Kingdom 487622
## 2               Germany   9042
## 3                France   8408
## 4                 Spain   2485
## 5           Netherlands   2363
## 6               Belgium   2031
## 7           Switzerland   1967
## 8              Portugal   1501
## 9             Australia   1185
## 10               Norway   1072
## 11                Italy    758
## 12               Sweden    451
## 13          Unspecified    446
## 14              Austria    398
## 15               Poland    330
## 16                Japan    321
## 17               Israel    295
## 18            Hong Kong    284
## 19            Singapore    222
## 20              Iceland    182
## 21                  USA    179
## 22               Greece    145
## 23                Malta    112
## 24 United Arab Emirates     68
## 25                  RSA     58
## 26              Lebanon     45
## 27            Lithuania     35
## 28               Brazil     32
## 29              Bahrain     18
## 30         Saudi Arabia      9

Observaciones:

#1. Hay 3 tickets que inician con “A”. R:Ya #2. Hay productos con “?”. R:Ya #3. Hay cantidades negativas. R:YA #4. Fecha esta como carácter. R:Ya #5. Hora esta como carácter. R:Ya #6. Hay precios negativos. R:Ya #7. Faltan ID de clientes. R:Ya

Herramienta “El Generador de Valor de Datos”

Paso 1. Definir el área del negocio que buscamos impactar o mejorar y su KPI

El área serían directamente las ventas de nuestros productos.

Los KPI´s a impactar son:

a. Ventas de productos
b. Tamaño de las compras (Basket size)

Paso 2. Seleccionar plantilla(-s) para crear valor a partir de los datos de los clientes.

Visión / Segmentación / Personalización / Contextualización

Paso 3. Generar ideas o conceptos específicos.

Generar un analisis de datos que permitan dar entrada a la generación de ventas cruzadas dentro del mercado.

Paso 4. Reunir los datos requeridos.

Se necesita una base de datos limpia de ventas de los productos en el Supermercado que ya tenemos para depurar en este caso.

Paso 5. Plan de ejecución.

Departamento de Marketing
Generación de insights y limpieza de bases de datos para generar estratégias de cross selling channels. (Data Minning)

Departamento de Compras
Tener en stock los productos para lo que se hará con las promociones.

Departamento de gerentes de tienda
Asegurarse del posicionamiento de los productos en la tienda.

Paso 3. Limpieza de base de datos

¿Cuántos NA tengo en la base de datos?

sum(is.na(bd1))
## [1] 134041

¿Cuántos NA tengo por variable?

sapply(bd1, function(x) sum(is.na(x)))
##     BillNo   Itemname   Quantity       Date       Time      Price CustomerID 
##          0          0          0          0          0          0     134041 
##    Country 
##          0

Eliminar renglones negativos

bd2<-bd1
bd2 <- bd2[bd2$Price> 0.001, ]

summary(bd2)
##     BillNo            Itemname            Quantity           Date          
##  Length:519547      Length:519547      Min.   :    1.0   Length:519547     
##  Class :character   Class :character   1st Qu.:    1.0   Class :character  
##  Mode  :character   Mode  :character   Median :    3.0   Mode  :character  
##                                        Mean   :   10.4                     
##                                        3rd Qu.:   10.0                     
##                                        Max.   :80995.0                     
##                                                                            
##      Time               Price             CustomerID       Country         
##  Length:519547      Min.   :    0.040   Min.   :12346    Length:519547     
##  Class :character   1st Qu.:    1.250   1st Qu.:13950    Class :character  
##  Mode  :character   Median :    2.080   Median :15265    Mode  :character  
##                     Mean   :    3.888   Mean   :15317                      
##                     3rd Qu.:    4.130   3rd Qu.:16837                      
##                     Max.   :13541.330   Max.   :18287                      
##                                         NA's   :131566

Convertir de carácter a fecha

bd3<-bd2
bd3$Date <- as.Date(bd3$Date, format= "%d/%m%y")
tibble(bd3)
## # A tibble: 519,547 × 8
##    BillNo Itemname                Quant…¹ Date   Time    Price Custome…² Country
##    <chr>  <chr>                     <int> <date> <chr>   <dbl>     <int> <chr>  
##  1 536365 "WHITE HANGING HEART T…       6 NA     8:26:00  2.55     17850 United…
##  2 536365 "WHITE METAL LANTERN"         6 NA     8:26:00  3.39     17850 United…
##  3 536365 "CREAM CUPID HEARTS CO…       8 NA     8:26:00  2.75     17850 United…
##  4 536365 "KNITTED UNION FLAG HO…       6 NA     8:26:00  3.39     17850 United…
##  5 536365 "RED WOOLLY HOTTIE WHI…       6 NA     8:26:00  3.39     17850 United…
##  6 536365 "SET 7 BABUSHKA NESTIN…       2 NA     8:26:00  7.65     17850 United…
##  7 536365 "GLASS STAR FROSTED T-…       6 NA     8:26:00  4.25     17850 United…
##  8 536366 "HAND WARMER UNION JAC…       6 NA     8:28:00  1.85     17850 United…
##  9 536366 "HAND WARMER RED POLKA…       6 NA     8:28:00  1.85     17850 United…
## 10 536367 "ASSORTED COLOUR BIRD …      32 NA     8:34:00  1.69     13047 United…
## # … with 519,537 more rows, and abbreviated variable names ¹​Quantity,
## #   ²​CustomerID

Convertir de caracteres a enteros

bd4<-bd3
bd4$Date.1 <- substr(bd4$Date, start = 1, stop=2)
tibble(bd4)
## # A tibble: 519,547 × 9
##    BillNo Itemname         Quant…¹ Date   Time    Price Custome…² Country Date.1
##    <chr>  <chr>              <int> <date> <chr>   <dbl>     <int> <chr>   <chr> 
##  1 536365 "WHITE HANGING …       6 NA     8:26:00  2.55     17850 United… <NA>  
##  2 536365 "WHITE METAL LA…       6 NA     8:26:00  3.39     17850 United… <NA>  
##  3 536365 "CREAM CUPID HE…       8 NA     8:26:00  2.75     17850 United… <NA>  
##  4 536365 "KNITTED UNION …       6 NA     8:26:00  3.39     17850 United… <NA>  
##  5 536365 "RED WOOLLY HOT…       6 NA     8:26:00  3.39     17850 United… <NA>  
##  6 536365 "SET 7 BABUSHKA…       2 NA     8:26:00  7.65     17850 United… <NA>  
##  7 536365 "GLASS STAR FRO…       6 NA     8:26:00  4.25     17850 United… <NA>  
##  8 536366 "HAND WARMER UN…       6 NA     8:28:00  1.85     17850 United… <NA>  
##  9 536366 "HAND WARMER RE…       6 NA     8:28:00  1.85     17850 United… <NA>  
## 10 536367 "ASSORTED COLOU…      32 NA     8:34:00  1.69     13047 United… <NA>  
## # … with 519,537 more rows, and abbreviated variable names ¹​Quantity,
## #   ²​CustomerID
bd4$Date.1<- as.integer(bd4$Date.1)
tibble(bd4)
## # A tibble: 519,547 × 9
##    BillNo Itemname         Quant…¹ Date   Time    Price Custome…² Country Date.1
##    <chr>  <chr>              <int> <date> <chr>   <dbl>     <int> <chr>    <int>
##  1 536365 "WHITE HANGING …       6 NA     8:26:00  2.55     17850 United…     NA
##  2 536365 "WHITE METAL LA…       6 NA     8:26:00  3.39     17850 United…     NA
##  3 536365 "CREAM CUPID HE…       8 NA     8:26:00  2.75     17850 United…     NA
##  4 536365 "KNITTED UNION …       6 NA     8:26:00  3.39     17850 United…     NA
##  5 536365 "RED WOOLLY HOT…       6 NA     8:26:00  3.39     17850 United…     NA
##  6 536365 "SET 7 BABUSHKA…       2 NA     8:26:00  7.65     17850 United…     NA
##  7 536365 "GLASS STAR FRO…       6 NA     8:26:00  4.25     17850 United…     NA
##  8 536366 "HAND WARMER UN…       6 NA     8:28:00  1.85     17850 United…     NA
##  9 536366 "HAND WARMER RE…       6 NA     8:28:00  1.85     17850 United…     NA
## 10 536367 "ASSORTED COLOU…      32 NA     8:34:00  1.69     13047 United…     NA
## # … with 519,537 more rows, and abbreviated variable names ¹​Quantity,
## #   ²​CustomerID

Ordenar de menor a mayor los tickets

bd5<-bd4[order(bd4$BillNo),]
tail(bd5)
##         BillNo                        Itemname Quantity Date     Time    Price
## 522060  581587     PACK OF 20 SPACEBOY NAPKINS       12 <NA> 12:50:00     0.85
## 522061  581587     CHILDREN'S APRON DOLLY GIRL        6 <NA> 12:50:00     2.10
## 522062  581587    CHILDRENS CUTLERY DOLLY GIRL        4 <NA> 12:50:00     4.15
## 522063  581587 CHILDRENS CUTLERY CIRCUS PARADE        4 <NA> 12:50:00     4.15
## 522064  581587    BAKING SET 9 PIECE RETROSPOT        3 <NA> 12:50:00     4.95
## 288773 A563185                 Adjust bad debt        1 <NA> 14:50:00 11062.06
##        CustomerID        Country Date.1
## 522060      12680         France     NA
## 522061      12680         France     NA
## 522062      12680         France     NA
## 522063      12680         France     NA
## 522064      12680         France     NA
## 288773         NA United Kingdom     NA

Eliminar renglones

bd6<-bd5
bd6<-bd6[bd6$BillNo < 581588,]
summary(bd1)
##     BillNo            Itemname            Quantity            Date          
##  Length:522064      Length:522064      Min.   :-9600.00   Length:522064     
##  Class :character   Class :character   1st Qu.:    1.00   Class :character  
##  Mode  :character   Mode  :character   Median :    3.00   Mode  :character  
##                                        Mean   :   10.09                     
##                                        3rd Qu.:   10.00                     
##                                        Max.   :80995.00                     
##                                                                             
##      Time               Price              CustomerID       Country         
##  Length:522064      Min.   :-11062.060   Min.   :12346    Length:522064     
##  Class :character   1st Qu.:     1.250   1st Qu.:13950    Class :character  
##  Mode  :character   Median :     2.080   Median :15265    Mode  :character  
##                     Mean   :     3.827   Mean   :15317                      
##                     3rd Qu.:     4.130   3rd Qu.:16837                      
##                     Max.   : 13541.330   Max.   :18287                      
##                                          NA's   :134041
tail(bd6)
##        BillNo                        Itemname Quantity Date     Time Price
## 522059 581587      CHILDRENS CUTLERY SPACEBOY        4 <NA> 12:50:00  4.15
## 522060 581587     PACK OF 20 SPACEBOY NAPKINS       12 <NA> 12:50:00  0.85
## 522061 581587     CHILDREN'S APRON DOLLY GIRL        6 <NA> 12:50:00  2.10
## 522062 581587    CHILDRENS CUTLERY DOLLY GIRL        4 <NA> 12:50:00  4.15
## 522063 581587 CHILDRENS CUTLERY CIRCUS PARADE        4 <NA> 12:50:00  4.15
## 522064 581587    BAKING SET 9 PIECE RETROSPOT        3 <NA> 12:50:00  4.95
##        CustomerID Country Date.1
## 522059      12680  France     NA
## 522060      12680  France     NA
## 522061      12680  France     NA
## 522062      12680  France     NA
## 522063      12680  France     NA
## 522064      12680  France     NA

Convertir tickets de caracter a entero

bd7<- bd6
bd7$BillNo <- as.integer(bd7$BillNo)
tibble(bd7)
## # A tibble: 519,546 × 9
##    BillNo Itemname         Quant…¹ Date   Time    Price Custome…² Country Date.1
##     <int> <chr>              <int> <date> <chr>   <dbl>     <int> <chr>    <int>
##  1 536365 "WHITE HANGING …       6 NA     8:26:00  2.55     17850 United…     NA
##  2 536365 "WHITE METAL LA…       6 NA     8:26:00  3.39     17850 United…     NA
##  3 536365 "CREAM CUPID HE…       8 NA     8:26:00  2.75     17850 United…     NA
##  4 536365 "KNITTED UNION …       6 NA     8:26:00  3.39     17850 United…     NA
##  5 536365 "RED WOOLLY HOT…       6 NA     8:26:00  3.39     17850 United…     NA
##  6 536365 "SET 7 BABUSHKA…       2 NA     8:26:00  7.65     17850 United…     NA
##  7 536365 "GLASS STAR FRO…       6 NA     8:26:00  4.25     17850 United…     NA
##  8 536366 "HAND WARMER UN…       6 NA     8:28:00  1.85     17850 United…     NA
##  9 536366 "HAND WARMER RE…       6 NA     8:28:00  1.85     17850 United…     NA
## 10 536367 "ASSORTED COLOU…      32 NA     8:34:00  1.69     13047 United…     NA
## # … with 519,536 more rows, and abbreviated variable names ¹​Quantity,
## #   ²​CustomerID
str(bd7)
## 'data.frame':    519546 obs. of  9 variables:
##  $ BillNo    : int  536365 536365 536365 536365 536365 536365 536365 536366 536366 536367 ...
##  $ Itemname  : chr  "WHITE HANGING HEART T-LIGHT HOLDER" "WHITE METAL LANTERN" "CREAM CUPID HEARTS COAT HANGER" "KNITTED UNION FLAG HOT WATER BOTTLE" ...
##  $ Quantity  : int  6 6 8 6 6 2 6 6 6 32 ...
##  $ Date      : Date, format: NA NA ...
##  $ Time      : chr  "8:26:00" "8:26:00" "8:26:00" "8:26:00" ...
##  $ Price     : num  2.55 3.39 2.75 3.39 3.39 7.65 4.25 1.85 1.85 1.69 ...
##  $ CustomerID: int  17850 17850 17850 17850 17850 17850 17850 17850 17850 13047 ...
##  $ Country   : chr  "United Kingdom" "United Kingdom" "United Kingdom" "United Kingdom" ...
##  $ Date.1    : int  NA NA NA NA NA NA NA NA NA NA ...

Eliminar comas y puntos de los artículos

#bd8<-bd7
#bd8$Itemname<- sub(","," ", bd8$Itemname, fixed=TRUE)
#bd8$Itemname<- sub("."," ", bd8$Itemname, fixed=TRUE)
# No se usó porque los artículos tienen comas entre texto y esas no se eliminaron con este fx.

Paso 4. Generar Basket

Generar función de basket

#install.packages("plyr")
library(plyr)
## ------------------------------------------------------------------------------
## You have loaded plyr after dplyr - this is likely to cause problems.
## If you need functions from both plyr and dplyr, please load plyr first, then dplyr:
## library(plyr); library(dplyr)
## ------------------------------------------------------------------------------
## 
## Attaching package: 'plyr'
## The following objects are masked from 'package:dplyr':
## 
##     arrange, count, desc, failwith, id, mutate, rename, summarise,
##     summarize
bd_limpia<-bd7
basket <- ddply(bd_limpia, c("BillNo"), function(bd_limpia)paste(bd_limpia$Itemname, collapse = ","))

Eliminar Número de Ticket

basket$BillNo<-NULL

Renombrar el nombre de la columna

colnames(basket)<- c("Articulo")

Exportar preparación de basket

#write.csv(basket, "basket.csv" , quote = FALSE, row.names = FALSE)

Importar transacciones

#install.packages("arules")
#install.packages("arulesViz")
#library(arules)
#library(arulesViz)
#library(Matrix)
#file.choose()
#tr<-read.transactions ( "C:\\Users\\danyc\\OneDrive - Instituto Tecnologico y de Estudios Superiores de Monterrey\\Desktop\\basket.csv",format = "basket", sep = ",")

Generar asociaciónes

reglas.asociacion <- apriori(tr, parameter = list(supp=0.001, conf=0.8, maxlen=10))
## Apriori
## 
## Parameter specification:
##  confidence minval smax arem  aval originalSupport maxtime support minlen
##         0.8    0.1    1 none FALSE            TRUE       5   0.001      1
##  maxlen target  ext
##      10  rules TRUE
## 
## Algorithmic control:
##  filter tree heap memopt load sort verbose
##     0.1 TRUE TRUE  FALSE TRUE    2    TRUE
## 
## Absolute minimum support count: 19 
## 
## set item appearances ...[0 item(s)] done [0.00s].
## set transactions ...[8583 item(s), 19559 transaction(s)] done [0.54s].
## sorting and recoding items ... [2646 item(s)] done [0.03s].
## creating transaction tree ... done [0.02s].
## checking subsets of size 1 2 3 4 5 6 7
## Warning in apriori(tr, parameter = list(supp = 0.001, conf = 0.8, maxlen = 10)):
## Mining stopped (time limit reached). Only patterns up to a length of 7 returned!
##  done [5.64s].
## writing ... [3309481 rule(s)] done [3.17s].
## creating S4 object  ... done [5.65s].
#summary(reglas.asociacion)
#inspect(reglas.asociacion)

reglas.asociacion <- sort(reglas.asociacion, by="confidence", decreasing = TRUE)
#summary(reglas.asociacion)
#inspect(reglas.asociacion)

Grafíca Market Basket

top10reglas <- head(reglas.asociacion, n = 10, by= "confidence")
inspect(top10reglas)
##      lhs                                    rhs                                  support confidence    coverage     lift count
## [1]  {SILVER MINI TAPE MEASURE}          => {JUMBO BAG PINK VINTAGE PAISLEY} 0.001124802          1 0.001124802 27.24095    22
## [2]  {SILVER MINI TAPE MEASURE}          => {STRAWBERRY CHARLOTTE BAG}       0.001124802          1 0.001124802 32.32893    22
## [3]  {SILVER MINI TAPE MEASURE}          => {LUNCH BAG CARS BLUE}            0.001124802          1 0.001124802 20.24741    22
## [4]  {SILVER MINI TAPE MEASURE}          => {WOODLAND CHARLOTTE BAG}         0.001124802          1 0.001124802 28.84808    22
## [5]  {SILVER MINI TAPE MEASURE}          => {RED RETROSPOT CHARLOTTE BAG}    0.001124802          1 0.001124802 22.98355    22
## [6]  {PINK POLKADOT BOWL,                                                                                                     
##       SET/20 FRUIT SALAD PAPER NAPKINS}  => {STRAWBERRY CHARLOTTE BAG}       0.001022547          1 0.001022547 32.32893    20
## [7]  {PINK POLKADOT BOWL,                                                                                                     
##       SET/20 FRUIT SALAD PAPER NAPKINS}  => {LUNCH BAG CARS BLUE}            0.001022547          1 0.001022547 20.24741    20
## [8]  {PINK POLKADOT BOWL,                                                                                                     
##       SET/20 FRUIT SALAD PAPER NAPKINS}  => {WOODLAND CHARLOTTE BAG}         0.001022547          1 0.001022547 28.84808    20
## [9]  {PINK POLKADOT BOWL,                                                                                                     
##       SET/20 FRUIT SALAD PAPER NAPKINS}  => {RED RETROSPOT CHARLOTTE BAG}    0.001022547          1 0.001022547 22.98355    20
## [10] {SET/20 FRUIT SALAD PAPER NAPKINS,                                                                                       
##       STRAWBERRY CHARLOTTE BAG}          => {LUNCH BAG CARS BLUE}            0.001073675          1 0.001073675 20.24741    21
plot(top10reglas,method = "graph", engine ="htmlwidget")

Conclusión y Aprendizaje

Dentro de esta actividad de generación de market basket podemos definir que este es un análisis matemático que encuentra diversos tipos de patrones, en este caso en específico se analizó un supermercado y el market basket nos ayudó a ver cómo los clientes se comportan con los productos y de esta manera crear insights para la generación de promociones.

En la gráfica de nuestro Market Basket podemos observar dos fuertes asociaciones que se marcan en colores rojo fuerte (regla 2 y regla 6) lo cual nos indica que si se llegará a poner una promoción del producto correlacionado que es el “Strawberry Charlotte Bag” lo más probable es que se mueva mucho más rápido de lo normal debido a que ya es algo que está ocurriendo.

Esto se puede utilizar como gancho para comprar estos en promocion dentro del comercio en linea del super o hacer una promocion de tercias regalando un producto similar a los que se muestran en el market basket y asi aumentar tambien las ventas de dicho producto de otra marca.

Las demás relaciones también pudieran funcionar de la misma manera pero se denota una fuerza menor en las comprar teniendo como significado una correlación, en efecto pero no tan marcada como la antes mencionada.

La estrategia para este súper sería crear este market basket en general de todos los productos para ver si hay algo interesante y después adentrarse por departamentos justamente para sugerir una promoción o bien un cambio “product placement” en los estantes y juntar las reglas más marcadas para darle al cliente ese touchpoint de exposición del producto.

LS0tDQp0aXRsZTogPHNwYW4gc3R5bGU9IkNvbG9yOnJlZCI+ICJNYXJrZXQgQmFza2V0IFN1cGVybWVyY2Fkb3MiPC9zcGFuPg0KYXV0aG9yOiAiRGFuaWVsYSBDw6FyZGVuYXMgWiAvLyBBMDE3MjA1MzUiDQpkYXRlOiAiMjAyMi0wOS0yMCINCm91dHB1dDogDQogICAgICBodG1sX2RvY3VtZW50Og0KICAgICAgICB0b2M6IHRydWUNCiAgICAgICAgdG9jX2Zsb2F0OiB0cnVlDQogICAgICAgIGNvZGVfZm9sZGluZzogImhpZGUiDQogICAgICAgIGNvZGVfZG93bmxvYWQ6IHRydWUNCi0tLQ0KKioqDQoNCiMgPGltZyBzcmM9ICJDOlxcVXNlcnNcXGRhbnljXFxEb3dubG9hZHNcXHN1cGVyLmdpZiIgLz4NCg0KIyA8c3BhbiBzdHlsZT0iQ29sb3I6I0U4QkMwMCI+IFBhc28gMS4gSW1wb3J0YXIgYmFzZSBkZSBkYXRvcyA8L3NwYW4+DQpgYGB7cn0NCiMxIEFqdXN0ZXMgYSBsYSBiYXNlIGRlIGRhdG9zIHNlIHJlZW1wbGF6YXJvbiBsYXMgY29tYXMgcG9yIGVzcGFjaW9zLiAgDQojMiBTZSBzZXBhcsOzIGxhIGZlY2hhIGVuIGTDrWEgeSBob3JhIGNvbiBzdXMgcmVzcGVjdGl2b3MgZm9ybWF0b3MuDQpgYGANCg0KYGBge3J9DQojZmlsZS5jaG9vc2UoKQ0KYmQxPC1yZWFkLmNzdigiQzpcXFVzZXJzXFxkYW55Y1xcT25lRHJpdmUgLSBJbnN0aXR1dG8gVGVjbm9sb2dpY28geSBkZSBFc3R1ZGlvcyBTdXBlcmlvcmVzIGRlIE1vbnRlcnJleVxcRGVza3RvcFxcRXhjZWwgeSBDU1ZcXHZlbnRhc2ZpbjEuY3N2IikNCg0KYGBgDQoNCg0KIyA8c3BhbiBzdHlsZT0iQ29sb3I6I0U4QkMwMCI+UGFzbyAyLiBFbnRlbmRlciBiYXNlIGRlIGRhdG9zIDwvc3Bhbj4NCmBgYHtyfQ0Kc3VtbWFyeShiZDEpDQpzdHIoYmQxKQ0KDQojaW5zdGFsbC5wYWNrYWdlcygiZHBseXIiKQ0KbGlicmFyeShkcGx5cikNCmNvdW50KGJkMSwgQ291bnRyeSwgc29ydCA9IFRSVUUpDQoNCmBgYA0KDQoNCiMjIyBPYnNlcnZhY2lvbmVzOiANCg0KICAjMS4gSGF5IDMgdGlja2V0cyBxdWUgaW5pY2lhbiBjb24gIkEiLiBSOllhDQogICMyLiBIYXkgcHJvZHVjdG9zIGNvbiAiPyIuIFI6WWENCiAgIzMuIEhheSBjYW50aWRhZGVzIG5lZ2F0aXZhcy4gUjpZQQ0KICAjNC4gRmVjaGEgZXN0YSBjb21vIGNhcsOhY3Rlci4gUjpZYQ0KICAjNS4gSG9yYSBlc3RhIGNvbW8gY2Fyw6FjdGVyLiBSOllhDQogICM2LiBIYXkgcHJlY2lvcyBuZWdhdGl2b3MuIFI6WWENCiAgIzcuIEZhbHRhbiBJRCBkZSBjbGllbnRlcy4gUjpZYQ0KICANCiMgPHNwYW4gc3R5bGU9IkNvbG9yOiNFODhCMDAiPiBIZXJyYW1pZW50YSDigJxFbCBHZW5lcmFkb3IgZGUgVmFsb3IgZGUgRGF0b3PigJ0gPC9zcGFuPg0KPHN0eWxlPg0KZGl2LmJsdWUgeyBiYWNrZ3JvdW5kLWNvbG9yOiM1ODk4MWY7IGJvcmRlci1yYWRpdXM6IDVweDsgcGFkZGluZzogMjBweDt9DQo8L3N0eWxlPg0KPGRpdiBjbGFzcyA9ICJibHVlIj4NCg0KKlBhc28gMS4qIERlZmluaXIgZWwgw6FyZWEgZGVsIG5lZ29jaW8gcXVlIGJ1c2NhbW9zIGltcGFjdGFyIG8gbWVqb3JhciB5IHN1IEtQSSAgDQoNCkVsIMOhcmVhIHNlcsOtYW4gZGlyZWN0YW1lbnRlIGxhcyB2ZW50YXMgZGUgbnVlc3Ryb3MgcHJvZHVjdG9zLiAgDQoNCkxvcyBLUEnCtHMgYSBpbXBhY3RhciBzb246ICAgDQoNCioqYS4gVmVudGFzIGRlIHByb2R1Y3RvcyAgICANCmIuIFRhbWHDsW8gZGUgbGFzIGNvbXByYXMgKEJhc2tldCBzaXplKSoqIA0KDQoqUGFzbyAyLiogU2VsZWNjaW9uYXIgcGxhbnRpbGxhKC1zKSBwYXJhIGNyZWFyIHZhbG9yIGEgcGFydGlyIGRlIGxvcyBkYXRvcyBkZSBsb3MgY2xpZW50ZXMuICAgICAgICANCg0KVmlzacOzbiAvICoqU2VnbWVudGFjacOzbioqIC8gUGVyc29uYWxpemFjacOzbiAvIENvbnRleHR1YWxpemFjacOzbiAgICAgICAgDQoNCipQYXNvIDMuKiBHZW5lcmFyIGlkZWFzIG8gY29uY2VwdG9zIGVzcGVjw61maWNvcy4gICANCg0KR2VuZXJhciB1biBhbmFsaXNpcyBkZSBkYXRvcyBxdWUgcGVybWl0YW4gZGFyIGVudHJhZGEgYSBsYSBnZW5lcmFjacOzbiBkZSB2ZW50YXMgY3J1emFkYXMgZGVudHJvIGRlbCBtZXJjYWRvLiAgIA0KDQoqUGFzbyA0LiogUmV1bmlyIGxvcyBkYXRvcyByZXF1ZXJpZG9zLiAgICANCg0KU2UgbmVjZXNpdGEgdW5hIGJhc2UgZGUgZGF0b3MgbGltcGlhIGRlIHZlbnRhcyBkZSBsb3MgcHJvZHVjdG9zIGVuIGVsIFN1cGVybWVyY2FkbyBxdWUgeWEgdGVuZW1vcyBwYXJhIGRlcHVyYXIgZW4gZXN0ZSBjYXNvLiANCg0KKlBhc28gNS4qIFBsYW4gZGUgZWplY3VjacOzbi4gIA0KDQoqKkRlcGFydGFtZW50byBkZSBNYXJrZXRpbmcqKiAgIA0KR2VuZXJhY2nDs24gZGUgaW5zaWdodHMgeSBsaW1waWV6YSBkZSBiYXNlcyBkZSBkYXRvcyBwYXJhIGdlbmVyYXIgZXN0cmF0w6lnaWFzIGRlIGNyb3NzIHNlbGxpbmcgY2hhbm5lbHMuIChEYXRhIE1pbm5pbmcpIA0KDQoqKkRlcGFydGFtZW50byBkZSBDb21wcmFzKiogICAgDQpUZW5lciBlbiBzdG9jayBsb3MgcHJvZHVjdG9zIHBhcmEgbG8gcXVlIHNlIGhhcsOhIGNvbiBsYXMgcHJvbW9jaW9uZXMuIA0KDQoqKkRlcGFydGFtZW50byBkZSBnZXJlbnRlcyBkZSB0aWVuZGEqKiAgIA0KQXNlZ3VyYXJzZSBkZWwgcG9zaWNpb25hbWllbnRvIGRlIGxvcyBwcm9kdWN0b3MgZW4gbGEgdGllbmRhLiANCg0KPC9kaXY+IA0KDQojIDxzcGFuIHN0eWxlPSJDb2xvcjojRThCQzAwIj5QYXNvIDMuIExpbXBpZXphIGRlIGJhc2UgZGUgZGF0b3M8L3NwYW4+DQoNCiMjIDxzcGFuIHN0eWxlPSJDb2xvcjojRTg4QjAwIj4gwr9DdcOhbnRvcyBOQSB0ZW5nbyBlbiBsYSBiYXNlIGRlIGRhdG9zPyA8L3NwYW4+IA0KDQpgYGB7cn0NCnN1bShpcy5uYShiZDEpKQ0KYGBgDQoNCiMjIDxzcGFuIHN0eWxlPSJDb2xvcjojRTg4QjAwIj4gwr9DdcOhbnRvcyBOQSB0ZW5nbyBwb3IgdmFyaWFibGU/IDwvc3Bhbj4gDQpgYGB7cn0NCnNhcHBseShiZDEsIGZ1bmN0aW9uKHgpIHN1bShpcy5uYSh4KSkpDQpgYGANCg0KDQojIyA8c3BhbiBzdHlsZT0iQ29sb3I6I0U4OEIwMCI+RWxpbWluYXIgcmVuZ2xvbmVzIG5lZ2F0aXZvcyA8L3NwYW4+IA0KYGBge3J9DQpiZDI8LWJkMQ0KYmQyIDwtIGJkMltiZDIkUHJpY2U+IDAuMDAxLCBdDQoNCnN1bW1hcnkoYmQyKQ0KYGBgDQoNCg0KDQojIyA8c3BhbiBzdHlsZT0iQ29sb3I6I0U4OEIwMCI+Q29udmVydGlyIGRlIGNhcsOhY3RlciBhIGZlY2hhIDwvc3Bhbj4gDQpgYGB7cn0NCmJkMzwtYmQyDQpiZDMkRGF0ZSA8LSBhcy5EYXRlKGJkMyREYXRlLCBmb3JtYXQ9ICIlZC8lbSV5IikNCnRpYmJsZShiZDMpDQpgYGANCg0KDQojIyA8c3BhbiBzdHlsZT0iQ29sb3I6I0U4OEIwMCI+Q29udmVydGlyIGRlIGNhcmFjdGVyZXMgYSBlbnRlcm9zIDwvc3Bhbj4gDQpgYGB7cn0NCmJkNDwtYmQzDQpiZDQkRGF0ZS4xIDwtIHN1YnN0cihiZDQkRGF0ZSwgc3RhcnQgPSAxLCBzdG9wPTIpDQp0aWJibGUoYmQ0KQ0KDQpiZDQkRGF0ZS4xPC0gYXMuaW50ZWdlcihiZDQkRGF0ZS4xKQ0KdGliYmxlKGJkNCkNCmBgYA0KDQoNCg0KIyMgPHNwYW4gc3R5bGU9IkNvbG9yOiNFODhCMDAiPk9yZGVuYXIgZGUgbWVub3IgYSBtYXlvciBsb3MgdGlja2V0cyA8L3NwYW4+IA0KYGBge3J9DQpiZDU8LWJkNFtvcmRlcihiZDQkQmlsbE5vKSxdDQp0YWlsKGJkNSkNCmBgYA0KDQoNCg0KIyMgPHNwYW4gc3R5bGU9IkNvbG9yOiNFODhCMDAiPkVsaW1pbmFyIHJlbmdsb25lcyA8L3NwYW4+IA0KYGBge3J9DQpiZDY8LWJkNQ0KYmQ2PC1iZDZbYmQ2JEJpbGxObyA8IDU4MTU4OCxdDQpzdW1tYXJ5KGJkMSkNCnRhaWwoYmQ2KQ0KYGBgDQoNCg0KDQojIyA8c3BhbiBzdHlsZT0iQ29sb3I6I0U4OEIwMCI+Q29udmVydGlyIHRpY2tldHMgZGUgY2FyYWN0ZXIgYSBlbnRlcm8gPC9zcGFuPiANCmBgYHtyfQ0KYmQ3PC0gYmQ2DQpiZDckQmlsbE5vIDwtIGFzLmludGVnZXIoYmQ3JEJpbGxObykNCnRpYmJsZShiZDcpDQpzdHIoYmQ3KQ0KYGBgDQoNCg0KDQojIyA8c3BhbiBzdHlsZT0iQ29sb3I6I0U4OEIwMCI+RWxpbWluYXIgY29tYXMgeSBwdW50b3MgZGUgbG9zIGFydMOtY3Vsb3M8L3NwYW4+ICANCmBgYHtyfQ0KI2JkODwtYmQ3DQojYmQ4JEl0ZW1uYW1lPC0gc3ViKCIsIiwiICIsIGJkOCRJdGVtbmFtZSwgZml4ZWQ9VFJVRSkNCiNiZDgkSXRlbW5hbWU8LSBzdWIoIi4iLCIgIiwgYmQ4JEl0ZW1uYW1lLCBmaXhlZD1UUlVFKQ0KIyBObyBzZSB1c8OzIHBvcnF1ZSBsb3MgYXJ0w61jdWxvcyB0aWVuZW4gY29tYXMgZW50cmUgdGV4dG8geSBlc2FzIG5vIHNlIGVsaW1pbmFyb24gY29uIGVzdGUgZnguDQpgYGANCg0KDQoNCiMgPHNwYW4gc3R5bGU9IkNvbG9yOiNFOEJDMDAiPiBQYXNvIDQuIEdlbmVyYXIgQmFza2V0PC9zcGFuPg0KDQojIyA8c3BhbiBzdHlsZT0iQ29sb3I6I0U4OEIwMCI+R2VuZXJhciBmdW5jacOzbiBkZSBiYXNrZXQgPC9zcGFuPiANCmBgYHtyfQ0KI2luc3RhbGwucGFja2FnZXMoInBseXIiKQ0KbGlicmFyeShwbHlyKQ0KYmRfbGltcGlhPC1iZDcNCmJhc2tldCA8LSBkZHBseShiZF9saW1waWEsIGMoIkJpbGxObyIpLCBmdW5jdGlvbihiZF9saW1waWEpcGFzdGUoYmRfbGltcGlhJEl0ZW1uYW1lLCBjb2xsYXBzZSA9ICIsIikpDQoNCmBgYA0KDQojIyA8c3BhbiBzdHlsZT0iQ29sb3I6I0U4OEIwMCI+RWxpbWluYXIgTsO6bWVybyBkZSBUaWNrZXQgPC9zcGFuPiANCmBgYHtyfQ0KYmFza2V0JEJpbGxObzwtTlVMTA0KYGBgDQoNCg0KDQojIyA8c3BhbiBzdHlsZT0iQ29sb3I6I0U4OEIwMCI+UmVub21icmFyIGVsIG5vbWJyZSBkZSBsYSBjb2x1bW5hIDwvc3Bhbj4gDQpgYGB7cn0NCmNvbG5hbWVzKGJhc2tldCk8LSBjKCJBcnRpY3VsbyIpDQoNCmBgYA0KDQoNCiMjIDxzcGFuIHN0eWxlPSJDb2xvcjojRTg4QjAwIj5FeHBvcnRhciBwcmVwYXJhY2nDs24gZGUgYmFza2V0IDwvc3Bhbj4gDQpgYGB7cn0NCiN3cml0ZS5jc3YoYmFza2V0LCAiYmFza2V0LmNzdiIgLCBxdW90ZSA9IEZBTFNFLCByb3cubmFtZXMgPSBGQUxTRSkNCg0KYGBgDQoNCg0KDQojIyA8c3BhbiBzdHlsZT0iQ29sb3I6I0U4OEIwMCI+SW1wb3J0YXIgdHJhbnNhY2Npb25lcyA8L3NwYW4+IA0KYGBge3J9DQojaW5zdGFsbC5wYWNrYWdlcygiYXJ1bGVzIikNCiNpbnN0YWxsLnBhY2thZ2VzKCJhcnVsZXNWaXoiKQ0KI2xpYnJhcnkoYXJ1bGVzKQ0KI2xpYnJhcnkoYXJ1bGVzVml6KQ0KI2xpYnJhcnkoTWF0cml4KQ0KI2ZpbGUuY2hvb3NlKCkNCiN0cjwtcmVhZC50cmFuc2FjdGlvbnMgKCAiQzpcXFVzZXJzXFxkYW55Y1xcT25lRHJpdmUgLSBJbnN0aXR1dG8gVGVjbm9sb2dpY28geSBkZSBFc3R1ZGlvcyBTdXBlcmlvcmVzIGRlIE1vbnRlcnJleVxcRGVza3RvcFxcYmFza2V0LmNzdiIsZm9ybWF0ID0gImJhc2tldCIsIHNlcCA9ICIsIikNCg0KYGBgDQoNCmBgYHtyLCBpbmNsdWRlPUZBTFNFfQ0KI2luc3RhbGwucGFja2FnZXMoImFydWxlcyIpDQojaW5zdGFsbC5wYWNrYWdlcygiYXJ1bGVzVml6IikNCmxpYnJhcnkoYXJ1bGVzKQ0KbGlicmFyeShhcnVsZXNWaXopDQpsaWJyYXJ5KE1hdHJpeCkNCiNmaWxlLmNob29zZSgpDQp0cjwtcmVhZC50cmFuc2FjdGlvbnMgKCAiQzpcXFVzZXJzXFxkYW55Y1xcT25lRHJpdmUgLSBJbnN0aXR1dG8gVGVjbm9sb2dpY28geSBkZSBFc3R1ZGlvcyBTdXBlcmlvcmVzIGRlIE1vbnRlcnJleVxcRGVza3RvcFxcYmFza2V0LmNzdiIsZm9ybWF0ID0gImJhc2tldCIsIHNlcCA9ICIsIikNCg0KYGBgDQoNCiMjIDxzcGFuIHN0eWxlPSJDb2xvcjojRTg4QjAwIj5HZW5lcmFyIGFzb2NpYWNpw7NuZXMgPC9zcGFuPiANCmBgYHtyfQ0KcmVnbGFzLmFzb2NpYWNpb24gPC0gYXByaW9yaSh0ciwgcGFyYW1ldGVyID0gbGlzdChzdXBwPTAuMDAxLCBjb25mPTAuOCwgbWF4bGVuPTEwKSkNCiNzdW1tYXJ5KHJlZ2xhcy5hc29jaWFjaW9uKQ0KI2luc3BlY3QocmVnbGFzLmFzb2NpYWNpb24pDQoNCnJlZ2xhcy5hc29jaWFjaW9uIDwtIHNvcnQocmVnbGFzLmFzb2NpYWNpb24sIGJ5PSJjb25maWRlbmNlIiwgZGVjcmVhc2luZyA9IFRSVUUpDQojc3VtbWFyeShyZWdsYXMuYXNvY2lhY2lvbikNCiNpbnNwZWN0KHJlZ2xhcy5hc29jaWFjaW9uKQ0KYGBgDQoNCg0KIyMgPHNwYW4gc3R5bGU9IkNvbG9yOiNFODhCMDAiPkdyYWbDrWNhIE1hcmtldCBCYXNrZXQgPC9zcGFuPiANCmBgYHtyfQ0KDQp0b3AxMHJlZ2xhcyA8LSBoZWFkKHJlZ2xhcy5hc29jaWFjaW9uLCBuID0gMTAsIGJ5PSAiY29uZmlkZW5jZSIpDQppbnNwZWN0KHRvcDEwcmVnbGFzKQ0KcGxvdCh0b3AxMHJlZ2xhcyxtZXRob2QgPSAiZ3JhcGgiLCBlbmdpbmUgPSJodG1sd2lkZ2V0IikNCmBgYA0KDQojIDxzcGFuIHN0eWxlPSJDb2xvcjojRThCQzAwIj4gQ29uY2x1c2nDs24geSBBcHJlbmRpemFqZSA8L3NwYW4+DQoNCjxzdHlsZT4NCmRpdi5ibHVlIHsgYmFja2dyb3VuZC1jb2xvcjpiZWlnZTsgYm9yZGVyLXJhZGl1czogNXB4OyBwYWRkaW5nOiAyMHB4O30NCjwvc3R5bGU+DQo8ZGl2IGNsYXNzID0gImJsdWUiPg0KRGVudHJvIGRlIGVzdGEgYWN0aXZpZGFkIGRlIGdlbmVyYWNpw7NuIGRlIG1hcmtldCBiYXNrZXQgcG9kZW1vcyBkZWZpbmlyIHF1ZSBlc3RlIGVzIHVuIGFuw6FsaXNpcyBtYXRlbcOhdGljbyBxdWUgZW5jdWVudHJhIGRpdmVyc29zIHRpcG9zIGRlIHBhdHJvbmVzLCBlbiBlc3RlIGNhc28gZW4gZXNwZWPDrWZpY28gc2UgYW5hbGl6w7MgdW4gc3VwZXJtZXJjYWRvIHkgZWwgbWFya2V0IGJhc2tldCBub3MgYXl1ZMOzIGEgdmVyIGPDs21vIGxvcyBjbGllbnRlcyBzZSBjb21wb3J0YW4gY29uIGxvcyBwcm9kdWN0b3MgeSBkZSBlc3RhIG1hbmVyYSBjcmVhciBpbnNpZ2h0cyBwYXJhIGxhIGdlbmVyYWNpw7NuIGRlIHByb21vY2lvbmVzLg0KDQpFbiBsYSBncsOhZmljYSBkZSBudWVzdHJvIE1hcmtldCBCYXNrZXQgcG9kZW1vcyBvYnNlcnZhciBkb3MgZnVlcnRlcyBhc29jaWFjaW9uZXMgcXVlIHNlIG1hcmNhbiBlbiBjb2xvcmVzICoqcm9qbyBmdWVydGUqKiAqKHJlZ2xhIDIgeSByZWdsYSA2KSogbG8gY3VhbCBub3MgaW5kaWNhIHF1ZSBzaSBzZSBsbGVnYXLDoSBhIHBvbmVyIHVuYSBwcm9tb2Npw7NuIGRlbCBwcm9kdWN0byBjb3JyZWxhY2lvbmFkbyBxdWUgZXMgZWwg4oCcU3RyYXdiZXJyeSBDaGFybG90dGUgQmFn4oCdIGxvIG3DoXMgcHJvYmFibGUgZXMgcXVlIHNlIG11ZXZhIG11Y2hvIG3DoXMgcsOhcGlkbyBkZSBsbyBub3JtYWwgZGViaWRvIGEgcXVlIHlhIGVzIGFsZ28gcXVlIGVzdMOhIG9jdXJyaWVuZG8uDQoNCkVzdG8gc2UgcHVlZGUgdXRpbGl6YXIgY29tbyBnYW5jaG8gcGFyYSBjb21wcmFyIGVzdG9zIGVuIHByb21vY2lvbiBkZW50cm8gZGVsIGNvbWVyY2lvIGVuIGxpbmVhIGRlbCBzdXBlciBvIGhhY2VyIHVuYSBwcm9tb2Npb24gZGUgdGVyY2lhcyByZWdhbGFuZG8gdW4gcHJvZHVjdG8gc2ltaWxhciBhIGxvcyBxdWUgc2UgbXVlc3RyYW4gZW4gZWwgbWFya2V0IGJhc2tldCB5IGFzaSBhdW1lbnRhciB0YW1iaWVuIGxhcyB2ZW50YXMgZGUgZGljaG8gcHJvZHVjdG8gZGUgb3RyYSBtYXJjYS4NCg0KTGFzIGRlbcOhcyByZWxhY2lvbmVzIHRhbWJpw6luIHB1ZGllcmFuIGZ1bmNpb25hciBkZSBsYSBtaXNtYSBtYW5lcmEgcGVybyBzZSBkZW5vdGEgdW5hIGZ1ZXJ6YSBtZW5vciBlbiBsYXMgY29tcHJhciB0ZW5pZW5kbyBjb21vIHNpZ25pZmljYWRvIHVuYSBjb3JyZWxhY2nDs24sIGVuIGVmZWN0byBwZXJvIG5vIHRhbiBtYXJjYWRhIGNvbW8gbGEgYW50ZXMgbWVuY2lvbmFkYS4NCg0KTGEgZXN0cmF0ZWdpYSBwYXJhIGVzdGUgc8O6cGVyIHNlcsOtYSBjcmVhciBlc3RlIG1hcmtldCBiYXNrZXQgZW4gZ2VuZXJhbCBkZSB0b2RvcyBsb3MgcHJvZHVjdG9zIHBhcmEgdmVyIHNpIGhheSBhbGdvIGludGVyZXNhbnRlIHkgZGVzcHXDqXMgYWRlbnRyYXJzZSBwb3IgZGVwYXJ0YW1lbnRvcyBqdXN0YW1lbnRlIHBhcmEgc3VnZXJpciB1bmEgcHJvbW9jacOzbiBvIGJpZW4gdW4gY2FtYmlvIOKAnHByb2R1Y3QgcGxhY2VtZW504oCdIGVuIGxvcyBlc3RhbnRlcyB5IGp1bnRhciBsYXMgcmVnbGFzIG3DoXMgbWFyY2FkYXMgcGFyYSBkYXJsZSBhbCBjbGllbnRlIGVzZSB0b3VjaHBvaW50IGRlIGV4cG9zaWNpw7NuIGRlbCBwcm9kdWN0by4NCg0KPC9kaXY+DQogIA0K