Teoria

El market basket analysis es una tecnica en el ambito de analisis y mineria de datos en el campo del comercio. Su objetivo principal es descubrir patrones de asociacion entre producstos que suelen ser comprados juntos por los clientes.

las 2 metricas principales para evaluar reglas de asociacion son:

  • Confidence (Confianza): Probabilidad de comprar B sabiendo que se compro A. Ej. Pan –> Mantequilla 0.8 de cada 100 clientes que compraron.

  • Lift (Elevación): Cuanto mas probable es comprar B cuando se compra A en comparación de la probabilidad de comprar B sin saber si se compro A. EJ. LIft –> 1 compra A impulsa B. Lift = 1 No tiene relacion de compra. Lift < 1 A reduce la compra de B.

  • Support (Soporte): Popularidad del producto dentro de las transacciones. Ej. Pan y Mantequilla 0.05 El 5% de todas las transacciones compraron estos dos productos juntos.

Contexto

Una cadena de tiendas de conveniencia tiene 5 tiendas ubicadas en distintas ciudades de mexico. La base de datos “abarrotes”Contiene1mes de transacciones, ppero presenta errores de calidad que impiden realizar analisis confiables. El objetivo es limpiar la base de datos de forma estrategia y posteriormente aplicar MBA para descubrir patrones de compra y diseñar promociones que aumenten ventas.

Instalar paquetes y llamar librerias

#install.packages("tidyverse") # Paquete globl para manipulacion y analisis de datos

library(tidyverse)
## ── Attaching core tidyverse packages ──────────────────────── tidyverse 2.0.0 ──
## ✔ dplyr     1.2.1     ✔ readr     2.2.0
## ✔ forcats   1.0.1     ✔ stringr   1.6.0
## ✔ ggplot2   4.0.3     ✔ tibble    3.3.1
## ✔ lubridate 1.9.5     ✔ tidyr     1.3.2
## ✔ purrr     1.2.2     
## ── Conflicts ────────────────────────────────────────── tidyverse_conflicts() ──
## ✖ dplyr::filter() masks stats::filter()
## ✖ dplyr::lag()    masks stats::lag()
## ℹ Use the conflicted package (<http://conflicted.r-lib.org/>) to force all conflicts to become errors
#install.packages("janitor") #Examinar y limpiar bases de datos sucias
library(janitor)
## 
## Adjuntando el paquete: 'janitor'
## 
## The following objects are masked from 'package:stats':
## 
##     chisq.test, fisher.test
#install.packages("Matrix") #Para trabajar matrices
library(Matrix)
## 
## Adjuntando el paquete: 'Matrix'
## 
## The following objects are masked from 'package:tidyr':
## 
##     expand, pack, unpack
#install.packages("arules") #Genera reglas de asociacion
library(arules)
## 
## Adjuntando el paquete: 'arules'
## 
## The following object is masked from 'package:dplyr':
## 
##     recode
## 
## The following objects are masked from 'package:base':
## 
##     abbreviate, write
#install.packages("arulesViz") # visualizar reglas de asociacion
library(arulesViz)
#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)
## ------------------------------------------------------------------------------
## 
## Adjuntando el paquete: 'plyr'
## 
## The following objects are masked from 'package:dplyr':
## 
##     arrange, count, desc, mutate, rename, summarise, summarize
## 
## The following object is masked from 'package:purrr':
## 
##     compact

Importar la base de datos

#file.choose()
df <- read.csv("C:\\Users\\usuario1\\Downloads\\abarrotes.csv")
summary(df)
##     ClaveTienda          DescGiro      Codigo.Barras            PLU        
##  Length   :200625   Length   :200625   Min.   :8.347e+05   Min.   : 1.000  
##  N.unique :     5   N.unique :     3   1st Qu.:7.501e+12   1st Qu.: 1.000  
##  N.blank  :     0   N.blank  :     0   Median :7.501e+12   Median : 1.000  
##  Min.nchar:     5   Min.nchar:     8   Mean   :5.950e+12   Mean   : 2.112  
##  Max.nchar:     5   Max.nchar:    10   3rd Qu.:7.501e+12   3rd Qu.: 1.000  
##                                        Max.   :1.750e+13   Max.   :30.000  
##                                                            NAs    :199188  
##        Fecha               Hora              Marca            Fabricante    
##  Length   :200625   Length   :200625   Length   :200625   Length   :200625  
##  N.unique :   195   N.unique : 52145   N.unique :   540   N.unique :   241  
##  N.blank  :     0   N.blank  :     0   N.blank  :     0   N.blank  :     0  
##  Min.nchar:    10   Min.nchar:     8   Min.nchar:     3   Min.nchar:     3  
##  Max.nchar:    10   Max.nchar:     8   Max.nchar:    30   Max.nchar:    40  
##                                                                             
##                                                                             
##       Producto          Precio          Ult.Costo         Unidades     
##  Length   :200625   Min.   :-147.00   Min.   :  0.38   Min.   : 0.200  
##  N.unique :  3406   1st Qu.:  11.00   1st Qu.:  8.46   1st Qu.: 1.000  
##  N.blank  :     0   Median :  16.00   Median : 12.31   Median : 1.000  
##  Min.nchar:     4   Mean   :  19.42   Mean   : 15.31   Mean   : 1.262  
##  Max.nchar:    40   3rd Qu.:  25.00   3rd Qu.: 19.23   3rd Qu.: 1.000  
##                     Max.   :1000.00   Max.   :769.23   Max.   :96.000  
##                                                                        
##     F.Ticket      NombreDepartamento   NombreFamilia     NombreCategoria  
##  Min.   :     1   Length   :200625   Length   :200625   Length   :200625  
##  1st Qu.: 33964   N.unique :     9   N.unique :    51   N.unique :   174  
##  Median :105993   N.blank  :     0   N.blank  :     0   N.blank  :     0  
##  Mean   :193990   Min.nchar:     6   Min.nchar:     3   Min.nchar:     2  
##  3rd Qu.:383005   Max.nchar:    20   Max.nchar:    25   Max.nchar:    37  
##  Max.   :450040                                                           
##                                                                           
##        Estado           Mts.2        Tipo.ubicación          Giro       
##  Length   :200625   Min.   :47.0   Length   :200625   Length   :200625  
##  N.unique :     5   1st Qu.:53.0   N.unique :     3   N.unique :     2  
##  N.blank  :     0   Median :60.0   N.blank  :     0   N.blank  :     0  
##  Min.nchar:     7   Mean   :56.6   Min.nchar:     7   Min.nchar:     9  
##  Max.nchar:    12   3rd Qu.:60.0   Max.nchar:    12   Max.nchar:    10  
##                     Max.   :62.0                                        
##                                                                         
##     Hora.inicio        Hora.cierre    
##  Length   :200625   Length   :200625  
##  N.unique :     3   N.unique :     3  
##  N.blank  :     0   N.blank  :     0  
##  Min.nchar:     5   Min.nchar:     5  
##  Max.nchar:     5   Max.nchar:     5  
##                                       
## 
str(df)
## 'data.frame':    200625 obs. of  22 variables:
##  $ ClaveTienda       : chr  "MX001" "MX001" "MX001" "MX001" ...
##  $ DescGiro          : chr  "Abarrotes" "Abarrotes" "Abarrotes" "Abarrotes" ...
##  $ Codigo.Barras     : num  7.5e+12 7.5e+12 7.5e+12 7.5e+12 7.5e+12 ...
##  $ PLU               : int  NA NA NA NA NA NA NA NA NA NA ...
##  $ Fecha             : chr  "19/06/2020" "19/06/2020" "19/06/2020" "19/06/2020" ...
##  $ Hora              : chr  "08:16:21" "08:23:33" "08:24:33" "08:24:33" ...
##  $ Marca             : chr  "NUTRI LECHE" "DAN UP" "BIMBO" "PEPSI" ...
##  $ Fabricante        : chr  "MEXILAC" "DANONE DE MEXICO" "GRUPO BIMBO" "PEPSI-COLA MEXICANA" ...
##  $ Producto          : chr  "Nutri Leche 1 Litro" "DANUP STRAWBERRY P/BEBER 350GR NAL" "Rebanadas Bimbo 2Pz" "Pepsi N.R. 400Ml" ...
##  $ Precio            : num  16 14 5 8 19.5 16 14 5 8 19.5 ...
##  $ Ult.Costo         : num  12.3 14 5 8 15 ...
##  $ Unidades          : num  1 1 1 1 1 1 1 1 1 1 ...
##  $ F.Ticket          : int  1 2 3 3 4 1 2 3 3 4 ...
##  $ NombreDepartamento: chr  "Abarrotes" "Abarrotes" "Abarrotes" "Abarrotes" ...
##  $ NombreFamilia     : chr  "Lacteos y Refrigerados" "Lacteos y Refrigerados" "Pan y Tortilla" "Bebidas" ...
##  $ NombreCategoria   : chr  "Leche" "Yogurt" "Pan Dulce Empaquetado" "Refrescos Plástico (N.R.)" ...
##  $ Estado            : chr  "Nuevo León" "Nuevo León" "Nuevo León" "Nuevo León" ...
##  $ Mts.2             : int  60 60 60 60 60 60 60 60 60 60 ...
##  $ Tipo.ubicación    : chr  "Esquina" "Esquina" "Esquina" "Esquina" ...
##  $ Giro              : chr  "Abarrotes" "Abarrotes" "Abarrotes" "Abarrotes" ...
##  $ Hora.inicio       : chr  "08:00" "08:00" "08:00" "08:00" ...
##  $ Hora.cierre       : chr  "22:00" "22:00" "22:00" "22:00" ...
 # count(df, ClaveTienda, sort =TRUE)
 # count(df, DescGiro, sort =TRUE)
 # count(df, Fecha, sort =TRUE)
 # count(df, Hora, sort =TRUE)
 # count(df, Marca, sort =TRUE)
 # count(df, Producto, sort =TRUE)
 # count(df, NombreDepartamento, sort =TRUE)
 # count(df, NombreFamilia, sort =TRUE)
 # count(df, NombreCategoria, sort =TRUE)
 # count(df, Tipo.ubicación, sort=TRUE ) 
 # count(df, Giro, sort=TRUE ) 
 # count(df, Fabricante, sort =TRUE)
 # count(df, Estado, sort =TRUE)
 # count(df, Hora.cierre, sort =TRUE)
 # count(df, Hora.inicio, sort =TRUE)
head(df, 10)
##    ClaveTienda  DescGiro Codigo.Barras PLU      Fecha     Hora
## 1        MX001 Abarrotes  7.501021e+12  NA 19/06/2020 08:16:21
## 2        MX001 Abarrotes  7.501032e+12  NA 19/06/2020 08:23:33
## 3        MX001 Abarrotes  7.501000e+12  NA 19/06/2020 08:24:33
## 4        MX001 Abarrotes  7.501031e+12  NA 19/06/2020 08:24:33
## 5        MX001 Abarrotes  7.501026e+12  NA 19/06/2020 08:26:28
## 6        MX001 Abarrotes  7.501021e+12  NA 19/06/2020 08:16:21
## 7        MX001 Abarrotes  7.501032e+12  NA 19/06/2020 08:23:33
## 8        MX001 Abarrotes  7.501000e+12  NA 19/06/2020 08:24:33
## 9        MX001 Abarrotes  7.501031e+12  NA 19/06/2020 08:24:33
## 10       MX001 Abarrotes  7.501026e+12  NA 19/06/2020 08:26:28
##                         Marca                 Fabricante
## 1                 NUTRI LECHE                    MEXILAC
## 2                      DAN UP           DANONE DE MEXICO
## 3                       BIMBO                GRUPO BIMBO
## 4                       PEPSI        PEPSI-COLA MEXICANA
## 5  BLANCA NIEVES (DETERGENTE) FABRICA DE JABON LA CORONA
## 6                 NUTRI LECHE                    MEXILAC
## 7                      DAN UP           DANONE DE MEXICO
## 8                       BIMBO                GRUPO BIMBO
## 9                       PEPSI        PEPSI-COLA MEXICANA
## 10 BLANCA NIEVES (DETERGENTE) FABRICA DE JABON LA CORONA
##                              Producto Precio Ult.Costo Unidades F.Ticket
## 1                 Nutri Leche 1 Litro   16.0     12.31        1        1
## 2  DANUP STRAWBERRY P/BEBER 350GR NAL   14.0     14.00        1        2
## 3                 Rebanadas Bimbo 2Pz    5.0      5.00        1        3
## 4                    Pepsi N.R. 400Ml    8.0      8.00        1        3
## 5       Detergente Blanca Nieves 500G   19.5     15.00        1        4
## 6                 Nutri Leche 1 Litro   16.0     12.31        1        1
## 7  DANUP STRAWBERRY P/BEBER 350GR NAL   14.0     14.00        1        2
## 8                 Rebanadas Bimbo 2Pz    5.0      5.00        1        3
## 9                    Pepsi N.R. 400Ml    8.0      8.00        1        3
## 10      Detergente Blanca Nieves 500G   19.5     15.00        1        4
##    NombreDepartamento          NombreFamilia           NombreCategoria
## 1           Abarrotes Lacteos y Refrigerados                     Leche
## 2           Abarrotes Lacteos y Refrigerados                    Yogurt
## 3           Abarrotes         Pan y Tortilla     Pan Dulce Empaquetado
## 4           Abarrotes                Bebidas Refrescos Plástico (N.R.)
## 5           Abarrotes     Limpieza del Hogar                Lavandería
## 6           Abarrotes Lacteos y Refrigerados                     Leche
## 7           Abarrotes Lacteos y Refrigerados                    Yogurt
## 8           Abarrotes         Pan y Tortilla     Pan Dulce Empaquetado
## 9           Abarrotes                Bebidas Refrescos Plástico (N.R.)
## 10          Abarrotes     Limpieza del Hogar                Lavandería
##        Estado Mts.2 Tipo.ubicación      Giro Hora.inicio Hora.cierre
## 1  Nuevo León    60        Esquina Abarrotes       08:00       22:00
## 2  Nuevo León    60        Esquina Abarrotes       08:00       22:00
## 3  Nuevo León    60        Esquina Abarrotes       08:00       22:00
## 4  Nuevo León    60        Esquina Abarrotes       08:00       22:00
## 5  Nuevo León    60        Esquina Abarrotes       08:00       22:00
## 6  Nuevo León    60        Esquina Abarrotes       08:00       22:00
## 7  Nuevo León    60        Esquina Abarrotes       08:00       22:00
## 8  Nuevo León    60        Esquina Abarrotes       08:00       22:00
## 9  Nuevo León    60        Esquina Abarrotes       08:00       22:00
## 10 Nuevo León    60        Esquina Abarrotes       08:00       22:00
tail(df, 10)
##        ClaveTienda DescGiro Codigo.Barras PLU      Fecha     Hora
## 200616       MX005 Depósito   7.62221e+12  NA 07/08/2020 19:30:13
## 200617       MX005 Depósito   7.62221e+12  NA 25/07/2020 18:42:24
## 200618       MX005 Depósito   7.62221e+12  NA 18/07/2020 22:45:58
## 200619       MX005 Depósito   7.62221e+12  NA 12/07/2020 00:36:34
## 200620       MX005 Depósito   7.62221e+12  NA 12/07/2020 01:08:25
## 200621       MX005 Depósito   7.62221e+12  NA 23/10/2020 22:17:37
## 200622       MX005 Depósito   7.62221e+12  NA 10/10/2020 20:30:20
## 200623       MX005 Depósito   7.62221e+12  NA 10/10/2020 22:40:43
## 200624       MX005 Depósito   7.62221e+12  NA 27/06/2020 22:30:19
## 200625       MX005 Depósito   7.62221e+12  NA 26/06/2020 23:43:34
##                    Marca    Fabricante                          Producto Precio
## 200616 TRIDENT XTRA CARE CADBURY ADAMS Trident Xtracare Freshmint 16.32G      9
## 200617 TRIDENT XTRA CARE CADBURY ADAMS Trident Xtracare Freshmint 16.32G      9
## 200618 TRIDENT XTRA CARE CADBURY ADAMS Trident Xtracare Freshmint 16.32G      9
## 200619 TRIDENT XTRA CARE CADBURY ADAMS Trident Xtracare Freshmint 16.32G      9
## 200620 TRIDENT XTRA CARE CADBURY ADAMS Trident Xtracare Freshmint 16.32G      9
## 200621 TRIDENT XTRA CARE CADBURY ADAMS Trident Xtracare Freshmint 16.32G      9
## 200622 TRIDENT XTRA CARE CADBURY ADAMS Trident Xtracare Freshmint 16.32G      9
## 200623 TRIDENT XTRA CARE CADBURY ADAMS Trident Xtracare Freshmint 16.32G      9
## 200624 TRIDENT XTRA CARE CADBURY ADAMS Trident Xtracare Freshmint 16.32G      9
## 200625 TRIDENT XTRA CARE CADBURY ADAMS Trident Xtracare Freshmint 16.32G      9
##        Ult.Costo Unidades F.Ticket NombreDepartamento NombreFamilia
## 200616      6.92        1   106411          Abarrotes      Dulcería
## 200617      6.92        1   104693          Abarrotes      Dulcería
## 200618      6.92        1   103856          Abarrotes      Dulcería
## 200619      6.92        1   103087          Abarrotes      Dulcería
## 200620      6.92        1   103100          Abarrotes      Dulcería
## 200621      6.92        1   116598          Abarrotes      Dulcería
## 200622      6.92        1   114886          Abarrotes      Dulcería
## 200623      6.92        1   114955          Abarrotes      Dulcería
## 200624      6.92        1   101121          Abarrotes      Dulcería
## 200625      6.92        1   100879          Abarrotes      Dulcería
##        NombreCategoria       Estado Mts.2 Tipo.ubicación       Giro Hora.inicio
## 200616 Gomas de Mazcar Quintana Roo    58        Esquina Mini súper       08:00
## 200617 Gomas de Mazcar Quintana Roo    58        Esquina Mini súper       08:00
## 200618 Gomas de Mazcar Quintana Roo    58        Esquina Mini súper       08:00
## 200619 Gomas de Mazcar Quintana Roo    58        Esquina Mini súper       08:00
## 200620 Gomas de Mazcar Quintana Roo    58        Esquina Mini súper       08:00
## 200621 Gomas de Mazcar Quintana Roo    58        Esquina Mini súper       08:00
## 200622 Gomas de Mazcar Quintana Roo    58        Esquina Mini súper       08:00
## 200623 Gomas de Mazcar Quintana Roo    58        Esquina Mini súper       08:00
## 200624 Gomas de Mazcar Quintana Roo    58        Esquina Mini súper       08:00
## 200625 Gomas de Mazcar Quintana Roo    58        Esquina Mini súper       08:00
##        Hora.cierre
## 200616       21:00
## 200617       21:00
## 200618       21:00
## 200619       21:00
## 200620       21:00
## 200621       21:00
## 200622       21:00
## 200623       21:00
## 200624       21:00
## 200625       21:00
 # Tabla de tienda y Departamento
tabyl(df, ClaveTienda, NombreDepartamento)
##  ClaveTienda Abarrotes Bebes e Infantiles Carnes Farmacia Ferretería Mercería
##        MX001     95415                515      1      147        245       28
##        MX002      6590                 21      0        4         10        0
##        MX003      4026                 15      0        2          8        0
##        MX004     82234                932      0      102        114       16
##        MX005     10014                  0      0        0          0        0
##  Papelería Productos a Eliminar Vinos y Licores
##         35                    3              80
##          0                    0               4
##          0                    0               0
##         32                    5              20
##          7                    0               0
 # Tabla de estado y hora de inicio 
tabyl(df, Estado, Hora.inicio)
##        Estado 07:00 08:00 09:00
##       Chiapas  4051     0     0
##       Jalisco     0     0  6629
##    Nuevo León     0 96469     0
##  Quintana Roo     0 10021     0
##       Sinaloa 83455     0     0

Limpiar la base de datos

Tecnica #1 . Eliminar vaores irrelevantes

#Eliminar columnas
df <- subset(df, select=-c(PLU))


# Eliminar Rengalones

df <- df[df$Precio>0, ]

Tecnica #2 . Eliminar valores repetidos

#Eliminar columnas
df <- distinct(df)

Tecnica #3 . Corregir errores tipograficos y similares

df$Unidades <- ceiling(df$Unidades)

Tecnica #4 . Convertir tipos de datos

df$Fecha <- as.Date(df$Fecha, format="%d/%m/%Y")

Tecnica #5 . Tratar valores faltantes

 # Borrar todos los NA's
 # df <- na.omit(df)


 # reemplazar los Na´s con CEROS
 # df[is.na(df)] <- 0

 # Reemplazar los Na's con el Promedio
 # df$Altura[is.na(df$Altura)] <- mean(df$altura, na.rn=TRUE)

Tecnica #6 . Herramientas estadisticas

boxplot(df$Precio, horizontal = TRUE)

boxplot(df$Unidades, Horizontal = TRUE)

generar Basket

 # Ordenar de menor a mayor lacolumna Ticket
df <- df[order(df$F.Ticket), ]

 # generar Basket
basket <- ddply(df, c("F.Ticket"), function(df)paste(df$Marca, collapse = ","))

 # Eliminar numero de ticket
basket$F.Ticket <- NULL

 # Cambiar el titulo de la columna V1 por Marca
colnames(basket) <- c("Marca")

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

Market Basket Analysis

# file.choose() 
tr <- read.transactions("C:\\Users\\usuario1\\Documents\\basket.csv")
## Warning in asMethod(object): removing duplicated items in transactions
reglas.asociacion <- apriori(tr, parameter=list(supp=0.001, conf=0.2, maxlen=10))
## Apriori
## 
## Parameter specification:
##  confidence minval smax arem  aval originalSupport maxtime support minlen
##         0.2    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: 115 
## 
## set item appearances ...[0 item(s)] done [0.00s].
## set transactions ...[27292 item(s), 115031 transaction(s)] done [0.09s].
## sorting and recoding items ... [191 item(s)] done [0.00s].
## creating transaction tree ... done [0.02s].
## checking subsets of size 1 2 3 4 done [0.00s].
## writing ... [139 rule(s)] done [0.00s].
## creating S4 object  ... done [0.00s].
 # summary (reglas.asociacion)
 # summary(reglas.asociacion)

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

top10reglas <- head(reglas.asociacion, n=10, by="confidence")
plot(top10reglas, method="graph", engine="htmlwidget")
LS0tDQp0aXRsZTogIk1hcmtldCBCYXNrZXQgQW5hbHlzaXMiDQphdXRob3I6ICJLYW1pbCBNIg0KZGF0ZTogIjIwMjYtMDgtMTgiDQpvdXRwdXQ6IA0KICBodG1sX2RvY3VtZW50Og0KICAgIHRvYzogVFJVRQ0KICAgIHRvY19mbG9hdDogVFJVRQ0KICAgIGNvZGVfZG93bmxvYWQ6IFRSVUUNCiAgICB0aGVtZTogY29zbW8NCi0tLQ0KIVtdKGh0dHBzOi8vd3d3Lm1pbGxkZXNrLmNvbS93cC1jb250ZW50L3VwbG9hZHMvMjAxOC8wNS9NaWxsZGVzay10cmF2b2x0YS1hdC1zdXBlcm1hcmtldC1hbmltYXRlZC1naWYtZ2lmLmdpZikNCg0KDQojIDxzcGFuIHN0eWxlPSJjb2xvcjogcmVkIj5UZW9yaWE8L3NwYW4+DQpFbCAqKm1hcmtldCBiYXNrZXQgYW5hbHlzaXMqKiBlcyB1bmEgdGVjbmljYSBlbiBlbCBhbWJpdG8gZGUgYW5hbGlzaXMgeSBtaW5lcmlhIGRlIGRhdG9zIGVuIGVsIGNhbXBvIGRlbCBjb21lcmNpby4gU3Ugb2JqZXRpdm8gcHJpbmNpcGFsIGVzIGRlc2N1YnJpciBwYXRyb25lcyBkZSBhc29jaWFjaW9uIGVudHJlIHByb2R1Y3N0b3MgcXVlIHN1ZWxlbiBzZXIgY29tcHJhZG9zIGp1bnRvcyBwb3IgbG9zIGNsaWVudGVzLiAgDQoNCmxhcyAyIG1ldHJpY2FzIHByaW5jaXBhbGVzIHBhcmEgZXZhbHVhciByZWdsYXMgZGUgYXNvY2lhY2lvbiBzb246ICAgDQoNCiogKkNvbmZpZGVuY2UqIChDb25maWFuemEpOiBQcm9iYWJpbGlkYWQgZGUgY29tcHJhciBCIHNhYmllbmRvIHF1ZSBzZSBjb21wcm8gQS4gRWouIFBhbiAtLT4gTWFudGVxdWlsbGEgMC44IGRlIGNhZGEgMTAwIGNsaWVudGVzIHF1ZSBjb21wcmFyb24uICANCg0KKiAqTGlmdCogKEVsZXZhY2nDs24pOiBDdWFudG8gbWFzIHByb2JhYmxlIGVzIGNvbXByYXIgQiBjdWFuZG8gc2UgY29tcHJhIEEgZW4gY29tcGFyYWNpw7NuIGRlIGxhIHByb2JhYmlsaWRhZCBkZSBjb21wcmFyIEIgc2luIHNhYmVyIHNpIHNlIGNvbXBybyBBLiBFSi4gTElmdCAtLT4gMSBjb21wcmEgQSBpbXB1bHNhIEIuIExpZnQgPSAxIE5vIHRpZW5lIHJlbGFjaW9uIGRlIGNvbXByYS4gTGlmdCA8IDEgQSByZWR1Y2UgbGEgY29tcHJhIGRlIEIuDQoNCiogKlN1cHBvcnQqIChTb3BvcnRlKTogUG9wdWxhcmlkYWQgZGVsIHByb2R1Y3RvIGRlbnRybyBkZSBsYXMgdHJhbnNhY2Npb25lcy4gRWouIFBhbiB5IE1hbnRlcXVpbGxhIDAuMDUgRWwgNSUgZGUgdG9kYXMgbGFzIHRyYW5zYWNjaW9uZXMgY29tcHJhcm9uIGVzdG9zIGRvcyBwcm9kdWN0b3MganVudG9zLiAgDQoNCiFbXShodHRwczovL2VuY3J5cHRlZC10Ym4wLmdzdGF0aWMuY29tL2ltYWdlcz9xPXRibjpBTmQ5R2NTWU5QZUNDVkpBTTV0Y1VxM0h4OEdoUTJVaHRlRWZRYlZjVUg3UlphYmdNRzJHVFhvRUwxSFM1Nmsmcz0xMCkNCg0KIyA8c3BhbiBzdHlsZT0iY29sb3I6IHJlZCI+Q29udGV4dG88L3NwYW4+DQpVbmEgY2FkZW5hIGRlIHRpZW5kYXMgZGUgY29udmVuaWVuY2lhIHRpZW5lIDUgdGllbmRhcyB1YmljYWRhcyBlbiBkaXN0aW50YXMgY2l1ZGFkZXMgZGUgbWV4aWNvLiBMYSBiYXNlIGRlIGRhdG9zICJhYmFycm90ZXMiQ29udGllbmUxbWVzIGRlIHRyYW5zYWNjaW9uZXMsIHBwZXJvIHByZXNlbnRhIGVycm9yZXMgZGUgY2FsaWRhZCBxdWUgaW1waWRlbiByZWFsaXphciBhbmFsaXNpcyBjb25maWFibGVzLiBFbCBvYmpldGl2byBlcyBsaW1waWFyIGxhIGJhc2UgZGUgZGF0b3MgZGUgZm9ybWEgZXN0cmF0ZWdpYSB5IHBvc3Rlcmlvcm1lbnRlIGFwbGljYXIgTUJBIHBhcmEgZGVzY3VicmlyIHBhdHJvbmVzIGRlIGNvbXByYSB5IGRpc2XDsWFyIHByb21vY2lvbmVzIHF1ZSBhdW1lbnRlbiB2ZW50YXMuICANCg0KIyA8c3BhbiBzdHlsZT0iY29sb3I6IHJlZCI+SW5zdGFsYXIgcGFxdWV0ZXMgeSBsbGFtYXIgbGlicmVyaWFzPC9zcGFuPg0KYGBge3Igd2FybmluZz1GQUxTRX0NCiNpbnN0YWxsLnBhY2thZ2VzKCJ0aWR5dmVyc2UiKSAjIFBhcXVldGUgZ2xvYmwgcGFyYSBtYW5pcHVsYWNpb24geSBhbmFsaXNpcyBkZSBkYXRvcw0KDQpsaWJyYXJ5KHRpZHl2ZXJzZSkNCiNpbnN0YWxsLnBhY2thZ2VzKCJqYW5pdG9yIikgI0V4YW1pbmFyIHkgbGltcGlhciBiYXNlcyBkZSBkYXRvcyBzdWNpYXMNCmxpYnJhcnkoamFuaXRvcikNCiNpbnN0YWxsLnBhY2thZ2VzKCJNYXRyaXgiKSAjUGFyYSB0cmFiYWphciBtYXRyaWNlcw0KbGlicmFyeShNYXRyaXgpDQojaW5zdGFsbC5wYWNrYWdlcygiYXJ1bGVzIikgI0dlbmVyYSByZWdsYXMgZGUgYXNvY2lhY2lvbg0KbGlicmFyeShhcnVsZXMpDQojaW5zdGFsbC5wYWNrYWdlcygiYXJ1bGVzVml6IikgIyB2aXN1YWxpemFyIHJlZ2xhcyBkZSBhc29jaWFjaW9uDQpsaWJyYXJ5KGFydWxlc1ZpeikNCiNpbnN0YWxsLnBhY2thZ2VzKCJwbHlyIikNCmxpYnJhcnkocGx5cikNCg0KYGBgDQoNCiMgPHNwYW4gc3R5bGU9ImNvbG9yOiByZWQiPkltcG9ydGFyIGxhIGJhc2UgZGUgZGF0b3M8L3NwYW4+DQpgYGB7cn0NCiNmaWxlLmNob29zZSgpDQpkZiA8LSByZWFkLmNzdigiQzpcXFVzZXJzXFx1c3VhcmlvMVxcRG93bmxvYWRzXFxhYmFycm90ZXMuY3N2IikNCg0KYGBgDQoNCmBgYHtyfQ0Kc3VtbWFyeShkZikNCnN0cihkZikNCiAjIGNvdW50KGRmLCBDbGF2ZVRpZW5kYSwgc29ydCA9VFJVRSkNCiAjIGNvdW50KGRmLCBEZXNjR2lybywgc29ydCA9VFJVRSkNCiAjIGNvdW50KGRmLCBGZWNoYSwgc29ydCA9VFJVRSkNCiAjIGNvdW50KGRmLCBIb3JhLCBzb3J0ID1UUlVFKQ0KICMgY291bnQoZGYsIE1hcmNhLCBzb3J0ID1UUlVFKQ0KICMgY291bnQoZGYsIFByb2R1Y3RvLCBzb3J0ID1UUlVFKQ0KICMgY291bnQoZGYsIE5vbWJyZURlcGFydGFtZW50bywgc29ydCA9VFJVRSkNCiAjIGNvdW50KGRmLCBOb21icmVGYW1pbGlhLCBzb3J0ID1UUlVFKQ0KICMgY291bnQoZGYsIE5vbWJyZUNhdGVnb3JpYSwgc29ydCA9VFJVRSkNCiAjIGNvdW50KGRmLCBUaXBvLnViaWNhY2nDs24sIHNvcnQ9VFJVRSApIA0KICMgY291bnQoZGYsIEdpcm8sIHNvcnQ9VFJVRSApIA0KICMgY291bnQoZGYsIEZhYnJpY2FudGUsIHNvcnQgPVRSVUUpDQogIyBjb3VudChkZiwgRXN0YWRvLCBzb3J0ID1UUlVFKQ0KICMgY291bnQoZGYsIEhvcmEuY2llcnJlLCBzb3J0ID1UUlVFKQ0KICMgY291bnQoZGYsIEhvcmEuaW5pY2lvLCBzb3J0ID1UUlVFKQ0KaGVhZChkZiwgMTApDQp0YWlsKGRmLCAxMCkNCg0KICMgVGFibGEgZGUgdGllbmRhIHkgRGVwYXJ0YW1lbnRvDQp0YWJ5bChkZiwgQ2xhdmVUaWVuZGEsIE5vbWJyZURlcGFydGFtZW50bykNCg0KICMgVGFibGEgZGUgZXN0YWRvIHkgaG9yYSBkZSBpbmljaW8gDQp0YWJ5bChkZiwgRXN0YWRvLCBIb3JhLmluaWNpbykNCg0KYGBgDQojIDxzcGFuIHN0eWxlPSJjb2xvcjogcmVkIj5MaW1waWFyIGxhIGJhc2UgZGUgZGF0b3M8L3NwYW4+DQoNCiMjIDxzcGFuIHN0eWxlPSJjb2xvcjogcmVkIj5UZWNuaWNhICMxIC4gRWxpbWluYXIgdmFvcmVzIGlycmVsZXZhbnRlczwvc3Bhbj4NCmBgYHtyfQ0KI0VsaW1pbmFyIGNvbHVtbmFzDQpkZiA8LSBzdWJzZXQoZGYsIHNlbGVjdD0tYyhQTFUpKQ0KDQoNCiMgRWxpbWluYXIgUmVuZ2Fsb25lcw0KDQpkZiA8LSBkZltkZiRQcmVjaW8+MCwgXQ0KDQoNCmBgYA0KDQojIyA8c3BhbiBzdHlsZT0iY29sb3I6IHJlZCI+VGVjbmljYSAjMiAuIEVsaW1pbmFyIHZhbG9yZXMgcmVwZXRpZG9zPC9zcGFuPg0KYGBge3J9DQojRWxpbWluYXIgY29sdW1uYXMNCmRmIDwtIGRpc3RpbmN0KGRmKQ0KDQpgYGANCiAgDQojIyA8c3BhbiBzdHlsZT0iY29sb3I6IHJlZCI+VGVjbmljYSAjMyAuIENvcnJlZ2lyIGVycm9yZXMgdGlwb2dyYWZpY29zIHkgc2ltaWxhcmVzPC9zcGFuPg0KYGBge3J9DQpkZiRVbmlkYWRlcyA8LSBjZWlsaW5nKGRmJFVuaWRhZGVzKQ0KDQpgYGANCg0KIyMgPHNwYW4gc3R5bGU9ImNvbG9yOiByZWQiPlRlY25pY2EgIzQgLiBDb252ZXJ0aXIgdGlwb3MgZGUgZGF0b3M8L3NwYW4+DQpgYGB7cn0NCmRmJEZlY2hhIDwtIGFzLkRhdGUoZGYkRmVjaGEsIGZvcm1hdD0iJWQvJW0vJVkiKQ0KDQpgYGANCg0KIyMgPHNwYW4gc3R5bGU9ImNvbG9yOiByZWQiPlRlY25pY2EgIzUgLiBUcmF0YXIgdmFsb3JlcyBmYWx0YW50ZXM8L3NwYW4+DQpgYGB7cn0NCiAjIEJvcnJhciB0b2RvcyBsb3MgTkEncw0KICMgZGYgPC0gbmEub21pdChkZikNCg0KDQogIyByZWVtcGxhemFyIGxvcyBOYcK0cyBjb24gQ0VST1MNCiAjIGRmW2lzLm5hKGRmKV0gPC0gMA0KDQogIyBSZWVtcGxhemFyIGxvcyBOYSdzIGNvbiBlbCBQcm9tZWRpbw0KICMgZGYkQWx0dXJhW2lzLm5hKGRmJEFsdHVyYSldIDwtIG1lYW4oZGYkYWx0dXJhLCBuYS5ybj1UUlVFKQ0KDQpgYGANCg0KIyMgPHNwYW4gc3R5bGU9ImNvbG9yOiByZWQiPlRlY25pY2EgIzYgLiBIZXJyYW1pZW50YXMgZXN0YWRpc3RpY2FzPC9zcGFuPg0KYGBge3J9DQpib3hwbG90KGRmJFByZWNpbywgaG9yaXpvbnRhbCA9IFRSVUUpDQoNCmJveHBsb3QoZGYkVW5pZGFkZXMsIEhvcml6b250YWwgPSBUUlVFKQ0KDQpgYGANCg0KIyA8c3BhbiBzdHlsZT0iY29sb3I6IHJlZCI+Z2VuZXJhciBCYXNrZXQ8L3NwYW4+DQpgYGB7cn0NCiAjIE9yZGVuYXIgZGUgbWVub3IgYSBtYXlvciBsYWNvbHVtbmEgVGlja2V0DQpkZiA8LSBkZltvcmRlcihkZiRGLlRpY2tldCksIF0NCg0KICMgZ2VuZXJhciBCYXNrZXQNCmJhc2tldCA8LSBkZHBseShkZiwgYygiRi5UaWNrZXQiKSwgZnVuY3Rpb24oZGYpcGFzdGUoZGYkTWFyY2EsIGNvbGxhcHNlID0gIiwiKSkNCg0KICMgRWxpbWluYXIgbnVtZXJvIGRlIHRpY2tldA0KYmFza2V0JEYuVGlja2V0IDwtIE5VTEwNCg0KICMgQ2FtYmlhciBlbCB0aXR1bG8gZGUgbGEgY29sdW1uYSBWMSBwb3IgTWFyY2ENCmNvbG5hbWVzKGJhc2tldCkgPC0gYygiTWFyY2EiKQ0KDQogIyBFeHBvcnRhciBiYXNrZXQNCndyaXRlLmNzdihiYXNrZXQsICJiYXNrZXQuY3N2Iiwgcm93Lm5hbWVzID0gRkFMU0UsIHF1b3RlPUZBTFNFKQ0KYGBgDQoNCiMgPHNwYW4gc3R5bGU9ImNvbG9yOiByZWQiPk1hcmtldCBCYXNrZXQgQW5hbHlzaXM8L3NwYW4+DQpgYGB7cn0NCiMgZmlsZS5jaG9vc2UoKSANCnRyIDwtIHJlYWQudHJhbnNhY3Rpb25zKCJDOlxcVXNlcnNcXHVzdWFyaW8xXFxEb2N1bWVudHNcXGJhc2tldC5jc3YiKQ0KDQpyZWdsYXMuYXNvY2lhY2lvbiA8LSBhcHJpb3JpKHRyLCBwYXJhbWV0ZXI9bGlzdChzdXBwPTAuMDAxLCBjb25mPTAuMiwgbWF4bGVuPTEwKSkNCg0KICMgc3VtbWFyeSAocmVnbGFzLmFzb2NpYWNpb24pDQogIyBzdW1tYXJ5KHJlZ2xhcy5hc29jaWFjaW9uKQ0KDQpyZWdsYXMuYXNvY2lhY2lvbiA8LSBzb3J0KHJlZ2xhcy5hc29jaWFjaW9uLCBieT0iY29uZmlkZW5jZSIsIGRlY3JlYXNpbmc9VFJVRSkNCiAjIG1hcnkocmVnbGFzLmFzb2NpYWNpb24pDQogIyBwZWN0KHJlZ2xhcy5hc29jaWFjaW9uKQ0KDQp0b3AxMHJlZ2xhcyA8LSBoZWFkKHJlZ2xhcy5hc29jaWFjaW9uLCBuPTEwLCBieT0iY29uZmlkZW5jZSIpDQpwbG90KHRvcDEwcmVnbGFzLCBtZXRob2Q9ImdyYXBoIiwgZW5naW5lPSJodG1sd2lkZ2V0IikNCg0KDQpgYGANCg0KDQpgYGB7cn0NCg==