#Read in all libraries
library(readr)
library(dplyr)
library(ggplot2)
dim(pp_cust_data)
[1] 2172    3
dim(sub_data)
[1] 679   5
summary(full_data)
 email_address       active_send     active_receive       pp_ind                  industry    relationship_length  site_visits         sub_ind    
 Length:2716        Min.   :0.0000   Min.   :0.0000   Min.   :1                       : 103   Min.   : 1.000      Min.   :    0.0   Min.   :1     
 Class :character   1st Qu.:1.0000   1st Qu.:0.0000   1st Qu.:1     home and garden   :  44   1st Qu.: 2.000      1st Qu.:   28.0   1st Qu.:1     
 Mode  :character   Median :1.0000   Median :0.0000   Median :1     outdoor           :  44   Median : 5.000      Median :   97.0   Median :1     
                    Mean   :0.7813   Mean   :0.4848   Mean   :1     landscape engineer:  39   Mean   : 8.931      Mean   :  434.2   Mean   :1     
                    3rd Qu.:1.0000   3rd Qu.:1.0000   3rd Qu.:1     nursery           :  39   3rd Qu.:13.000      3rd Qu.:  301.0   3rd Qu.:1     
                    Max.   :1.0000   Max.   :1.0000   Max.   :1     (Other)           : 410   Max.   :30.000      Max.   :16551.0   Max.   :1     
                    NA's   :544      NA's   :544      NA's   :544   NA's              :2037   NA's   :2037        NA's   :2037      NA's   :2037  
summary(common_data)
 email_address       active_send     active_receive       pp_ind                industry  relationship_length  site_visits         sub_ind 
 Length:135         Min.   :0.0000   Min.   :0.0000   Min.   :1                     :19   Min.   : 1.000      Min.   :    0.0   Min.   :1  
 Class :character   1st Qu.:0.0000   1st Qu.:0.0000   1st Qu.:1   garden            :12   1st Qu.: 2.000      1st Qu.:   43.5   1st Qu.:1  
 Mode  :character   Median :0.0000   Median :0.0000   Median :1   outdoor           :11   Median : 6.000      Median :  145.0   Median :1  
                    Mean   :0.3481   Mean   :0.3037   Mean   :1   landscape designer: 8   Mean   : 8.837      Mean   :  764.7   Mean   :1  
                    3rd Qu.:1.0000   3rd Qu.:1.0000   3rd Qu.:1   landscape engineer: 8   3rd Qu.:13.000      3rd Qu.:  520.0   3rd Qu.:1  
                    Max.   :1.0000   Max.   :1.0000   Max.   :1   gardening         : 7   Max.   :30.000      Max.   :16551.0   Max.   :1  
                                                                  (Other)           :70                                                    
ggplot(data = common_data, aes(x = industry, fill = industry)) +
    geom_bar(width=0.7, fill="steelblue", angle = 45) + theme(axis.text.x = element_text(angle = 90, vjust = 0.5, hjust=1))
Ignoring unknown parameters: angle

# compute unique levels in data frame
lvls <- unique(unlist(common_data$industry))
  
# apply the summation per value 
freq <- sapply(common_data,
               function(x) table(factor(x, levels = lvls, 
                                        ordered = TRUE)))
freq 
                    email_address active_send active_receive pp_ind industry relationship_length site_visits sub_ind
                                0           0              0      0       19                   0           0       0
supply                          0           0              0      0        7                   0           0       0
designer                        0           0              0      0        3                   0           0       0
outdoor                         0           0              0      0       11                   0           0       0
outdoor living                  0           0              0      0        7                   0           0       0
orchard                         0           0              0      0        5                   0           0       0
plants                          0           0              0      0        7                   0           0       0
grower                          0           0              0      0        7                   0           0       0
nursery                         0           0              0      0        7                   0           0       0
landscape designer              0           0              0      0        8                   0           0       0
garden                          0           0              0      0       12                   0           0       0
landscaper                      0           0              0      0        2                   0           0       0
vineyard                        0           0              0      0        4                   0           0       0
landscape engineer              0           0              0      0        8                   0           0       0
gardening                       0           0              0      0        7                   0           0       0
landscape architect             0           0              0      0        6                   0           0       0
architect                       0           0              0      0        4                   0           0       0
landscaping                     0           0              0      0        4                   0           0       0
home and garden                 0           0              0      0        5                   0           0       0
hg                              0           0              0      0        2                   0           0       0
#Active Transactions in the last year
#Average relationship length of these active transactors is 6 years
#Most of these individuals are also frequent site visitors (24 visits/year, 2 site visits a month)
active <- common_data %>% filter(active_send==1 | active_receive ==1)
summary(active)
 email_address       active_send     active_receive       pp_ind                industry  relationship_length  site_visits         sub_ind 
 Length:80          Min.   :0.0000   Min.   :0.0000   Min.   :1                     :10   Min.   : 1.000      Min.   :    0.0   Min.   :1  
 Class :character   1st Qu.:0.0000   1st Qu.:0.0000   1st Qu.:1   garden            : 8   1st Qu.: 2.000      1st Qu.:   51.5   1st Qu.:1  
 Mode  :character   Median :1.0000   Median :1.0000   Median :1   landscape designer: 7   Median : 6.000      Median :  145.5   Median :1  
                    Mean   :0.5875   Mean   :0.5125   Mean   :1   outdoor           : 6   Mean   : 8.488      Mean   :  623.9   Mean   :1  
                    3rd Qu.:1.0000   3rd Qu.:1.0000   3rd Qu.:1   grower            : 5   3rd Qu.:12.000      3rd Qu.:  446.5   3rd Qu.:1  
                    Max.   :1.0000   Max.   :1.0000   Max.   :1   landscape engineer: 5   Max.   :30.000      Max.   :16551.0   Max.   :1  
                                                                  (Other)           :39                                                    
lvls <- unique(unlist(full_data$industry))
  
# apply the summation per value 
freq <- sapply(full_data,
               function(x) table(factor(x, levels = lvls, 
                                        ordered = TRUE)))
freq 
                    email_address active_send active_receive pp_ind industry relationship_length site_visits sub_ind
                                0           0              0      0      103                   0           0       0
supply                          0           0              0      0       36                   0           0       0
designer                        0           0              0      0       28                   0           0       0
outdoor                         0           0              0      0       44                   0           0       0
outdoor living                  0           0              0      0       38                   0           0       0
orchard                         0           0              0      0       22                   0           0       0
plants                          0           0              0      0       25                   0           0       0
grower                          0           0              0      0       29                   0           0       0
nursery                         0           0              0      0       39                   0           0       0
landscape designer              0           0              0      0       28                   0           0       0
garden                          0           0              0      0       34                   0           0       0
landscaper                      0           0              0      0       15                   0           0       0
vineyard                        0           0              0      0       24                   0           0       0
landscape engineer              0           0              0      0       39                   0           0       0
gardening                       0           0              0      0       31                   0           0       0
landscape architect             0           0              0      0       29                   0           0       0
architect                       0           0              0      0       27                   0           0       0
landscaping                     0           0              0      0       17                   0           0       0
home and garden                 0           0              0      0       44                   0           0       0
hg                              0           0              0      0       27                   0           0       0
#Select only needed columns
final <- full_data %>% select(relationship_length, site_visits, inboth, industry_ind, sv_yr, active)
final <- as.matrix(final)
final <- prop.table(final, margin = 2) 
final_set <- bind_cols(as.data.frame(full_data[,1]), as.data.frame(final))
final_set
# Running the elbow method
#Code Source: https://uc-r.github.io/kmeans_clustering
library(cluster) # Needed for silhouette function
require(purrr)
kmeansDat <- final_set[,-(1)]  # Extract only customer columns
kmeansDat.t <- t(kmeansDat)  # Get customers in rows and products in columns
set.seed(123)
wss <- function(k) {
  kmeans(kmeansDat, k, nstart = 10 )$tot.withinss
}
# Compute and plot wss for k = 1 to k = 15
k.values <- 1:15
# extract wss for 2-15 clusters
wss_values <- map_dbl(k.values, wss)
plot(k.values, wss_values,
       type="b", pch = 19, frame = FALSE, 
       xlab="Number of clusters K",
       ylab="Total within-clusters sum of squares")

#Run Silhoute Method in Conjunction
#Code Source: https://uc-r.github.io/kmeans_clustering
#2 clusters is the winner
avg_sil <- function(k) {
  km.res <- kmeans(kmeansDat, centers = k, nstart = 25)
  ss <- silhouette(km.res$cluster, dist(kmeansDat))
  mean(ss[, 3])
}
# Compute and plot wss for k = 2 to k = 15
k.values <- 2:15
# extract avg silhouette for 2-15 clusters
avg_sil_values <- map_dbl(k.values, avg_sil)
plot(k.values, avg_sil_values,
       type = "b", pch = 19, frame = FALSE, 
       xlab = "Number of clusters K",
       ylab = "Average Silhouettes")

#Final
set.seed(123)
final <- kmeans(kmeansDat, 4, nstart = 25)
print(final)
K-means clustering with 4 clusters of sizes 131, 2379, 199, 7

Cluster means:
  relationship_length  site_visits      inboth industry_ind        sv_yr       active
1        1.389756e-03 1.339122e-03 0.007407407 0.0014843087 2.029560e-03 3.144178e-04
2        9.884755e-05 2.684723e-05 0.000000000 0.0002087128 8.491371e-05 4.028121e-04
3        2.780227e-03 2.179866e-03 0.000000000 0.0014918342 1.936575e-03 0.000000e+00
4        4.216924e-03 4.670173e-02 0.004232804 0.0017361111 2.096279e-02 7.448235e-05

Clustering vector:
   [1] 2 2 2 1 2 1 2 2 2 2 2 2 2 2 2 2 1 2 2 1 2 2 1 1 1 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 4 2 2 1 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 1 1 2 2 2 2 2 2 2 2 1 2 2 2 2 2 2 2 2 2 2
  [88] 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 1 1 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 1 2 2 2 2 2 2 2 2 2 2 2 2 2 1 2 2 2 1 2 1 2 1 2 2 2 2 2 2 2
 [175] 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 1 2 2 2 1 2 2 2 2 2 2 2 1 2 2 2 2 2 1 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 1 2 2 2 1 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2
 [262] 1 2 2 2 2 2 2 2 2 2 1 2 2 2 2 2 2 2 2 1 1 2 2 2 1 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 1 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 1 2 2 2 2 1 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2
 [349] 2 2 2 2 2 2 2 2 2 1 2 2 2 1 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 1 2 2 2 2 2 2 2 2 1 2 2 1 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2
 [436] 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2
 [523] 2 2 2 2 2 2 2 2 2 2 2 2 1 2 1 2 2 2 2 1 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 1 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 4 2 2 2 2
 [610] 2 2 2 2 2 2 2 2 2 2 2 2 2 1 2 2 2 2 2 2 2 2 1 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 1 2 2 2 2 2 2 2 2 2 2 2 2 2 2 1 1 2 2 2 2 2 2
 [697] 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 1 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 1 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2
 [784] 2 2 2 2 2 2 2 2 2 2 2 2 1 2 2 2 2 2 2 2 2 2 2 2 2 1 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 1 2 2 2 2 2 2 2 1 2 2 2 2 2 2 2 2 1 2 2 2 2 2 2 2
 [871] 2 2 2 2 2 1 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 1 2 2 2 2 2 2 2 2 2 2 2 2 2 1 2 2 2 2 1 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2
 [958] 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 1 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 1 2 2
 [ reached getOption("max.print") -- omitted 1716 entries ]

Within cluster sum of squares by cluster:
[1] 0.002729883 0.001347098 0.002742412 0.000925972
 (between_SS / total_SS =  79.0 %)

Available components:

[1] "cluster"      "centers"      "totss"        "withinss"     "tot.withinss" "betweenss"    "size"         "iter"         "ifault"      
#Visually able to see data is segmented, and I think one of these clusters would be our appropriate demographic
fviz_cluster(final, data = kmeansDat)

full_data %>%
  mutate(Cluster = final$cluster) %>%
  group_by(Cluster) %>%
  summarise_all("mean")
argument is not numeric or logical: returning NAargument is not numeric or logical: returning NAargument is not numeric or logical: returning NAargument is not numeric or logical: returning NAargument is not numeric or logical: returning NAargument is not numeric or logical: returning NAargument is not numeric or logical: returning NAargument is not numeric or logical: returning NA
out <- cbind(full_data, clusterNum = final$cluster)
head(out)
#Cluster 1: 131 Customers = Can't Target Existing Customers!
#Bad: Current customers 
#Okay: 8 year relationship length, active rate strong
#Good:  Industry indicator strong, 53 site visits per year, 1/week

out %>% filter(clusterNum == 1)
out %>% filter(clusterNum == 2) %>% filter(sub_ind == 1) %>% group_by(clusterNum) %>%
  summarise_all("mean")
argument is not numeric or logical: returning NAargument is not numeric or logical: returning NA
out %>% filter(clusterNum == 3) %>% filter(sub_ind == 1) %>% group_by(clusterNum) %>%
  summarise_all("mean")
argument is not numeric or logical: returning NAargument is not numeric or logical: returning NA
out %>% filter(inboth ==1) %>% group_by(clusterNum) %>%
  summarise_all("mean")
argument is not numeric or logical: returning NAargument is not numeric or logical: returning NAargument is not numeric or logical: returning NAargument is not numeric or logical: returning NA

Conclusion:

As the sample of 679 records from an active subscriber base of 30,000. Of those 679 records. We also received a PayPal data set of 2172 records.

My strategy was to first learn a bit about the data, build out new features (industry indicator, active indicator, in both data sets indicator, site visits/year ratio). Once I built out these features, I chose to run a K-Means clustering algorithm. While the silhouette and elbow method provided the optimal clusters to be 2, I chose to run a variety of numbers and settled on 4 as they were the best at telling a story.

Each cluster identifies a specific type of consumer, where Cluster 2 is the most interesting:

Cluster 1: TLDR: Can’t Target Existing Customers! n = 131 Customers Bad: Current customers Okay: 8 year relationship length, active rate strong Good: Industry indicator strong, 53 site visits per year, 1/week

Cluster 2: TLDR: Majority PayPal Customers, but Net New Customers would be a WIN! n = 2379 Customers 342 customers Net New subscriber base customers with 83% industry indicator Mostly current customers, but once we filter into Net New customers we see a different story Okay: O site visits (about ~1/month), 4 year relationship length

If we assume the 679:30,000 ratio holds, the 342 Net New customers from cluster 2 that have a strong industry indicator could be considered a strong lead, bringing in potentiall 15,000 new customers

Cluster 3: Also, Net New Customers! n = 199 Customers - Bad: 16 year relationship and 0 transactions Okay: 1 site view per week Good: Industry relevant

Cluster 4: Too small to consider

From our final analysis, we want to confirm that current Paypal customers who are subscribers to this magazine have a relatively high level of active transaction rates. We see above that Clusters 1 and 4 have strong industry indicators. Cluster 1 has an activity rate of 0.6 and is most similar to the subset of the Net New 342 customers found in Cluster 2. They are younger customers (4 years vs 8 years) and have less site visits, but with the potential of 15,000 new customers and 0.6 active rate in existing customers with a similar industry indicator, I would argue that this vendor can provide 1000 strong leads.

LS0tCnRpdGxlOiAiUiBOb3RlYm9vayIKb3V0cHV0OiBodG1sX25vdGVib29rCi0tLQoKYGBge3J9CiNSZWFkIGluIGFsbCBsaWJyYXJpZXMKbGlicmFyeShyZWFkcikKbGlicmFyeShkcGx5cikKbGlicmFyeShnZ3Bsb3QyKQpgYGAKCmBgYHtyfQojUmVhZCBpbiBQUCBDdXN0IERhdGEKcHBfY3VzdF9kYXRhIDwtIHJlYWQuY3N2KCIvVXNlcnMva2FqYWxjaG9rc2hpL0Rvd25sb2Fkcy9wcF9jdXN0X2RhdGEuY3N2IikKCiNMb29rIGF0IGNvbHVtbnMgb2YgcHBfY3VzdF9kYXRhCmhlYWQocHBfY3VzdF9kYXRhKQoKI1B1bGwgc3VtbWFyeSBvZiBjdXN0b21lciBkYXRhCiNOb3RlczogTWVhbiBvZiBhY3RpdmUgc2VuZCBpcyAwLjc4MyBhbmQgYWN0aXZlIHJlY2VpdmUgaXMgMC40ODQ4LCBhbmQgaWYgeW91IG5lZWQgdG8gYmUgYWN0aXZlIGluIHNlbmRpbmcgYW5kIHJlY2VpdmluZywgdGhlbiB5b3UgbXVzdCBoYXZlIGhhZCBhIHRyYW5zYWN0aW9uIGluIHRoZSBsYXN0IHllYXIuIApzdW1tYXJ5KHBwX2N1c3RfZGF0YSkKYGBgCgpgYGB7cn0KI1JlYWQgaW4gU3Vic2NyaWJlciBTYW1wbGUgRGF0YQpzdWJfZGF0YSA8LSByZWFkLmNzdigiL1VzZXJzL2thamFsY2hva3NoaS9Eb3dubG9hZHMvc3Vic2NyaWJlcl9kYXRhX3NhbXBsZS5jc3YiKQoKI0xvb2sgYXQgY29sdW1ucyBvZiBTdWJzY3JpYmVyIGRhdGEKaGVhZChzdWJfZGF0YSkKCiNQdWxsIHN1bW1hcnkgb2YgY3VzdG9tZXIgZGF0YQojTm90ZXM6IEludGVyc3RpbmcgdG8gc2VlIHRoZSBpbmR1c3RyaWVzLCBmYWlybHkgZXF1YWxseSBzcGxpdCBiZXR3ZWVuIGFsbCBpbmR1c3RyaWVzIGV4cGNlcHQgZm9yIG90aGVyCiNSZWxhdGlvbnNoaXAgbGVuZ3RoIG1lYW4gaXMgOC45LCBzbyBtb3N0IHN1YnNjcmliZXJzIGhhdmUgYSBsb25nIHJlbGF0aW9uc2hpcC4gVGhlIG1lZGlhbiBpcyA1IHRob3VnaCBzbyBhbiBvdXRsaWVyIGlzIGxpa2VseSBpbmZsYXRpbmcgdGhlIG1lYW4KI1NpdGUgdmlzaXRzIGJ5IHRoZSBtZWFuIGFyZSBhYm91dCA0MzQgYnV0IHRoaXMgc2VlbXMgaW5mbGF0ZWQsIHRoZSBtZWRpYW4gaXMgb25seSA5NyBidXQgd2UgZG8gc2VlIHRoZSBtYXggc2l0ZSB2aXNpdHMgaXMgMTY1NTEuIAojMzAgeWVhciByZWxhdGlvbnNoaXAgZG9lcyBub3Qgc2VlbSBvdXQgb2YgdGhlIG9yZGluYXJ5CnN1bW1hcnkoc3ViX2RhdGEpCmBgYAoKYGBge3J9CiNKb2luIFRhYmxlcyB0byBidWlsZCBGdWxsIERhdGEgCnBwX2N1c3RfZGF0YSRwcF9pbmQgPC0gMQpzdWJfZGF0YSRzdWJfaW5kIDwtIDEKZnVsbF9kYXRhIDwtIGZ1bGxfam9pbihwcF9jdXN0X2RhdGEsIHN1Yl9kYXRhLCBieSA9ICdlbWFpbF9hZGRyZXNzJykKc3VtbWFyeShmdWxsX2RhdGEpCgojVmVyaWZ5IHRoZSBoaWdoIHNpdGUgdmlzaXRzIGlzIHJlbGF0ZWQgdG8gbG9uZ2VyIHJlbGF0aW9uc2hpcHMKZnVsbF9kYXRhICU+JSBmaWx0ZXIoc2l0ZV92aXNpdHMgPiAxMDAwMCkKYGBgCmBgYHtyfQojSm9pbiBUYWJsZXMgdG8gYnVpbGQgSW5uZXIgSm9pbmVkIERhdGEgKGxpc3Qgb2YgZW1haWxzIGZvdW5kIGluIGJvdGgpCmNvbW1vbl9kYXRhIDwtIGlubmVyX2pvaW4ocHBfY3VzdF9kYXRhLCBzdWJfZGF0YSwgYnkgPSAnZW1haWxfYWRkcmVzcycpCmNvbW1vbl9kYXRhCnN1bW1hcnkoY29tbW9uX2RhdGEpCmBgYAoKYGBge3J9CiMgQmFyIENoYXJ0IG9mIEluZHVzdHJpZXMKZ2dwbG90KGRhdGEgPSBjb21tb25fZGF0YSwgYWVzKHggPSBpbmR1c3RyeSwgZmlsbCA9IGluZHVzdHJ5KSkgKwogICAgZ2VvbV9iYXIod2lkdGg9MC43LCBmaWxsPSJzdGVlbGJsdWUiLCBhbmdsZSA9IDQ1KSArIHRoZW1lKGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KGFuZ2xlID0gOTAsIHZqdXN0ID0gMC41LCBoanVzdD0xKSkKYGBgCgpgYGB7cn0KIyBjb21wdXRlIHVuaXF1ZSBsZXZlbHMgaW4gZGF0YSBmcmFtZQpsdmxzIDwtIHVuaXF1ZSh1bmxpc3QoY29tbW9uX2RhdGEkaW5kdXN0cnkpKQogIAojIGFwcGx5IHRoZSBzdW1tYXRpb24gcGVyIHZhbHVlIApmcmVxIDwtIHNhcHBseShjb21tb25fZGF0YSwKICAgICAgICAgICAgICAgZnVuY3Rpb24oeCkgdGFibGUoZmFjdG9yKHgsIGxldmVscyA9IGx2bHMsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgb3JkZXJlZCA9IFRSVUUpKSkKZnJlcSAKYGBgCgoKYGBge3J9CiNBY3RpdmUgVHJhbnNhY3Rpb25zIGluIHRoZSBsYXN0IHllYXIKI0F2ZXJhZ2UgcmVsYXRpb25zaGlwIGxlbmd0aCBvZiB0aGVzZSBhY3RpdmUgdHJhbnNhY3RvcnMgaXMgNiB5ZWFycwojTW9zdCBvZiB0aGVzZSBpbmRpdmlkdWFscyBhcmUgYWxzbyBmcmVxdWVudCBzaXRlIHZpc2l0b3JzICgyNCB2aXNpdHMveWVhciwgMiBzaXRlIHZpc2l0cyBhIG1vbnRoKQphY3RpdmUgPC0gY29tbW9uX2RhdGEgJT4lIGZpbHRlcihhY3RpdmVfc2VuZD09MSB8IGFjdGl2ZV9yZWNlaXZlID09MSkKc3VtbWFyeShhY3RpdmUpCmBgYAoKYGBge3J9Cmx2bHMgPC0gdW5pcXVlKHVubGlzdChmdWxsX2RhdGEkaW5kdXN0cnkpKQogIAojIGFwcGx5IHRoZSBzdW1tYXRpb24gcGVyIHZhbHVlIApmcmVxIDwtIHNhcHBseShmdWxsX2RhdGEsCiAgICAgICAgICAgICAgIGZ1bmN0aW9uKHgpIHRhYmxlKGZhY3Rvcih4LCBsZXZlbHMgPSBsdmxzLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG9yZGVyZWQgPSBUUlVFKSkpCmZyZXEgCmBgYAoKYGBge3J9CiNDcmVhdGluZyBOZXcgRmVhdHVyZXMKCmZ1bGxfZGF0YQoKI0luZHVzdHJ5IEluZGljYXRvciAtIElkZW50aWZ5IGlmIHRoZSBpbmRpdmlkdWFsIGlzIHBhcnQgb2YgdGhlIHJlcXVpcmVkIGluZHVzdHJ5CmZ1bGxfZGF0YSRpbmR1c3RyeV9pbmQgPC0gaWZlbHNlKGZ1bGxfZGF0YSRpbmR1c3RyeSA9PSAiIiwgMCwgMSkgCgojU2l0ZSBWaXNpdC9ZZWFyIFJhdGlvIC0gSWRlbnRpZnkgdGhlIG51bWJlciBvZiBzaXRlIHZpc2l0cyBwZXIgeWVhcgpmdWxsX2RhdGEkc3ZfeXIgPC0gZnVsbF9kYXRhJHNpdGVfdmlzaXRzL2Z1bGxfZGF0YSRyZWxhdGlvbnNoaXBfbGVuZ3RoCgojQWN0aXZlIFN0YXR1cyBJbmRpY2F0b3IgLSBJZGVudGlmeSBpZiBlbWFpbCBoYXMgYmVlbiBhY3RpdmUgaW4gdGhlIGxhc3QgeWVhcgpmdWxsX2RhdGEkYWN0aXZlIDwtIGFzLm51bWVyaWMoaWZlbHNlKGZ1bGxfZGF0YSRhY3RpdmVfc2VuZCA9PSAxIHwgZnVsbF9kYXRhJGFjdGl2ZV9yZWNlaXZlID09MSwgIjEiLCAiMCIpKQoKI0luIEJvdGggSW5kaWNhdG9yCmZ1bGxfZGF0YSRpbmJvdGggPC0gYXMubnVtZXJpYyhpZmVsc2UoZnVsbF9kYXRhJHBwX2luZCA9PSAgMSAmIGZ1bGxfZGF0YSRzdWJfaW5kID09IDEsICIxIiwgIjAiKSkKCiNSZXBsYWNlIGVtcHRpZXMgd2l0aCAwCmZ1bGxfZGF0YVtpcy5uYShmdWxsX2RhdGEpXSA8LSAwCmZ1bGxfZGF0YQoKCmBgYAoKYGBge3J9CiNTZWxlY3Qgb25seSBuZWVkZWQgY29sdW1ucwpmaW5hbCA8LSBmdWxsX2RhdGEgJT4lIHNlbGVjdChyZWxhdGlvbnNoaXBfbGVuZ3RoLCBzaXRlX3Zpc2l0cywgaW5ib3RoLCBpbmR1c3RyeV9pbmQsIHN2X3lyLCBhY3RpdmUpCmZpbmFsIDwtIGFzLm1hdHJpeChmaW5hbCkKZmluYWwgPC0gcHJvcC50YWJsZShmaW5hbCwgbWFyZ2luID0gMikgCmZpbmFsX3NldCA8LSBiaW5kX2NvbHMoYXMuZGF0YS5mcmFtZShmdWxsX2RhdGFbLDFdKSwgYXMuZGF0YS5mcmFtZShmaW5hbCkpCmZpbmFsX3NldApgYGAKCmBgYHtyfQojIFJ1bm5pbmcgdGhlIGVsYm93IG1ldGhvZAojQ29kZSBTb3VyY2U6IGh0dHBzOi8vdWMtci5naXRodWIuaW8va21lYW5zX2NsdXN0ZXJpbmcKbGlicmFyeShjbHVzdGVyKSAjIE5lZWRlZCBmb3Igc2lsaG91ZXR0ZSBmdW5jdGlvbgpyZXF1aXJlKHB1cnJyKQoKa21lYW5zRGF0IDwtIGZpbmFsX3NldFssLSgxKV0gICMgRXh0cmFjdCBvbmx5IGN1c3RvbWVyIGNvbHVtbnMKa21lYW5zRGF0LnQgPC0gdChrbWVhbnNEYXQpICAjIEdldCBjdXN0b21lcnMgaW4gcm93cyBhbmQgcHJvZHVjdHMgaW4gY29sdW1ucwoKc2V0LnNlZWQoMTIzKQoKd3NzIDwtIGZ1bmN0aW9uKGspIHsKICBrbWVhbnMoa21lYW5zRGF0LCBrLCBuc3RhcnQgPSAxMCApJHRvdC53aXRoaW5zcwp9CgojIENvbXB1dGUgYW5kIHBsb3Qgd3NzIGZvciBrID0gMSB0byBrID0gMTUKay52YWx1ZXMgPC0gMToxNQoKIyBleHRyYWN0IHdzcyBmb3IgMi0xNSBjbHVzdGVycwp3c3NfdmFsdWVzIDwtIG1hcF9kYmwoay52YWx1ZXMsIHdzcykKCnBsb3Qoay52YWx1ZXMsIHdzc192YWx1ZXMsCiAgICAgICB0eXBlPSJiIiwgcGNoID0gMTksIGZyYW1lID0gRkFMU0UsIAogICAgICAgeGxhYj0iTnVtYmVyIG9mIGNsdXN0ZXJzIEsiLAogICAgICAgeWxhYj0iVG90YWwgd2l0aGluLWNsdXN0ZXJzIHN1bSBvZiBzcXVhcmVzIikKCmBgYAoKCmBgYHtyfQojUnVuIFNpbGhvdXRlIE1ldGhvZCBpbiBDb25qdW5jdGlvbgojQ29kZSBTb3VyY2U6IGh0dHBzOi8vdWMtci5naXRodWIuaW8va21lYW5zX2NsdXN0ZXJpbmcKIzIgY2x1c3RlcnMgaXMgdGhlIHdpbm5lcgphdmdfc2lsIDwtIGZ1bmN0aW9uKGspIHsKICBrbS5yZXMgPC0ga21lYW5zKGttZWFuc0RhdCwgY2VudGVycyA9IGssIG5zdGFydCA9IDI1KQogIHNzIDwtIHNpbGhvdWV0dGUoa20ucmVzJGNsdXN0ZXIsIGRpc3Qoa21lYW5zRGF0KSkKICBtZWFuKHNzWywgM10pCn0KCiMgQ29tcHV0ZSBhbmQgcGxvdCB3c3MgZm9yIGsgPSAyIHRvIGsgPSAxNQprLnZhbHVlcyA8LSAyOjE1CgojIGV4dHJhY3QgYXZnIHNpbGhvdWV0dGUgZm9yIDItMTUgY2x1c3RlcnMKYXZnX3NpbF92YWx1ZXMgPC0gbWFwX2RibChrLnZhbHVlcywgYXZnX3NpbCkKCnBsb3Qoay52YWx1ZXMsIGF2Z19zaWxfdmFsdWVzLAogICAgICAgdHlwZSA9ICJiIiwgcGNoID0gMTksIGZyYW1lID0gRkFMU0UsIAogICAgICAgeGxhYiA9ICJOdW1iZXIgb2YgY2x1c3RlcnMgSyIsCiAgICAgICB5bGFiID0gIkF2ZXJhZ2UgU2lsaG91ZXR0ZXMiKQpgYGAKYGBge3J9CiNGaW5hbApzZXQuc2VlZCgxMjMpCmZpbmFsIDwtIGttZWFucyhrbWVhbnNEYXQsIDQsIG5zdGFydCA9IDI1KQpwcmludChmaW5hbCkKYGBgCmBgYHtyfQojVmlzdWFsbHkgYWJsZSB0byBzZWUgZGF0YSBpcyBzZWdtZW50ZWQsIGFuZCBJIHRoaW5rIG9uZSBvZiB0aGVzZSBjbHVzdGVycyB3b3VsZCBiZSBvdXIgYXBwcm9wcmlhdGUgZGVtb2dyYXBoaWMKZnZpel9jbHVzdGVyKGZpbmFsLCBkYXRhID0ga21lYW5zRGF0KQpgYGAKCmBgYHtyfQpmdWxsX2RhdGEgJT4lCiAgbXV0YXRlKENsdXN0ZXIgPSBmaW5hbCRjbHVzdGVyKSAlPiUKICBncm91cF9ieShDbHVzdGVyKSAlPiUKICBzdW1tYXJpc2VfYWxsKCJtZWFuIikKYGBgCgpgYGB7cn0Kb3V0IDwtIGNiaW5kKGZ1bGxfZGF0YSwgY2x1c3Rlck51bSA9IGZpbmFsJGNsdXN0ZXIpCmhlYWQob3V0KQpgYGAKCmBgYHtyfQojQ2x1c3RlciAxOiAxMzEgQ3VzdG9tZXJzID0gQ2FuJ3QgVGFyZ2V0IEV4aXN0aW5nIEN1c3RvbWVycyEKI0JhZDogQ3VycmVudCBjdXN0b21lcnMgCiNPa2F5OiA4IHllYXIgcmVsYXRpb25zaGlwIGxlbmd0aCwgYWN0aXZlIHJhdGUgc3Ryb25nCiNHb29kOiAgSW5kdXN0cnkgaW5kaWNhdG9yIHN0cm9uZywgNTMgc2l0ZSB2aXNpdHMgcGVyIHllYXIsIDEvd2VlawoKb3V0ICU+JSBmaWx0ZXIoY2x1c3Rlck51bSA9PSAxKQpgYGAKCmBgYHtyfQojQ2x1c3RlciAyOiAyMzc5IEN1c3RvbWVycyAtIE1ham9yaXR5IFBheVBhbCBDdXN0b21lcnMsIGJ1dCBzdHJvbmcgYWN0aXZpdHkgcmF0ZQojMzQyIGN1c3RvbWVycyBuZXQgbmV3IGN1c3RvbWVycywgODMlIGluIGluZHVzdHJ5CiNCYWQ6IE1vc3RseSBjdXJyZW50IGN1c3RvbWVycywgbG93IHNpdGUgdmlzaXRzIChsZXNzIHRoYW4gMS9tb250aCkKI09rYXk6IFNob3J0IHJlbGF0aW9uc2hpcCBsZW5ndGgKI0dvb2Q6IEhpZ2ggYWN0aXZlIHJhdGUgKDAuNzcgaGF2ZSBhIHRyYW5zYWN0aW9uIG9uY2UgYSB5ZWFyKQoKb3V0ICU+JSBmaWx0ZXIoY2x1c3Rlck51bSA9PSAyKQoKb3V0ICU+JSBmaWx0ZXIoY2x1c3Rlck51bSA9PSAyKSAlPiUgZmlsdGVyKHN1Yl9pbmQgPT0gMSkgJT4lIGdyb3VwX2J5KGNsdXN0ZXJOdW0pICU+JQogIHN1bW1hcmlzZV9hbGwoIm1lYW4iKQpgYGAKCmBgYHtyfQojQ2x1c3RlciAzOiAxOTkgQ3VzdG9tZXJzIC0gTmV0IE5ldyBDdXN0b21lcnMhCiNCYWQ6IDE2IHllYXIgcmVsYXRpb25zaGlwIGFuZCAwIHRyYW5zYWN0aW9ucwojT2theTogMSBzaXRlIHZpZXcgcGVyIHdlZWsKI0dvb2Q6IEluZHVzdHJ5IHJlbGV2YW50CgpvdXQgJT4lIGZpbHRlcihjbHVzdGVyTnVtID09IDMpCm91dCAlPiUgZmlsdGVyKGNsdXN0ZXJOdW0gPT0gMykgJT4lIGZpbHRlcihzdWJfaW5kID09IDEpICU+JSBncm91cF9ieShjbHVzdGVyTnVtKSAlPiUKICBzdW1tYXJpc2VfYWxsKCJtZWFuIikKYGBgCmBgYHtyfQojQ2x1c3RlciA0OiAxMyBDdXN0b21lcnMgLSBPbGRlc3QgQ3VzdG9tZXJzIQojVG9vIHNtYWxsIHRvIGNvbnNpZGVyIHRoaXMgY2x1c3RlciwgYnV0IHRoZXkgaGF2ZSBoaWdoIHNpdGUgdmlzaXRzIGFuZCBsb3cgYWN0aXZpdHkgcmF0ZXMKb3V0ICU+JSBmaWx0ZXIoY2x1c3Rlck51bSA9PSA0KQpgYGAKCmBgYHtyfQojRmluYWwgQW5hbHlzaXMgLSBJZGVudGlmeSBvZiBvdXIgY3VycmVudCBjdXN0b21lcnMgYW5kIHN1YnNjcmliZXJzLCB3aGF0IGlzIHRoZSBhY3RpdmUgcmF0ZQpvdXQgJT4lIGZpbHRlcihpbmJvdGggPT0gMSkgJT4lIGdyb3VwX2J5KGNsdXN0ZXJOdW0pICU+JQogIHN1bW1hcmlzZV9hbGwoIm1lYW4iKQpgYGAKCgpDb25jbHVzaW9uOiAKCkFzIHRoZSBzYW1wbGUgb2YgNjc5IHJlY29yZHMgZnJvbSBhbiBhY3RpdmUgc3Vic2NyaWJlciBiYXNlIG9mIDMwLDAwMC4gT2YgdGhvc2UgNjc5IHJlY29yZHMuIFdlIGFsc28gcmVjZWl2ZWQgYSBQYXlQYWwgZGF0YSBzZXQgb2YgMjE3MiByZWNvcmRzLiAKCk15IHN0cmF0ZWd5IHdhcyB0byBmaXJzdCBsZWFybiBhIGJpdCBhYm91dCB0aGUgZGF0YSwgYnVpbGQgb3V0IG5ldyBmZWF0dXJlcyAoaW5kdXN0cnkgaW5kaWNhdG9yLCBhY3RpdmUgaW5kaWNhdG9yLCBpbiBib3RoIGRhdGEgc2V0cyBpbmRpY2F0b3IsIHNpdGUgdmlzaXRzL3llYXIgcmF0aW8pLiBPbmNlIEkgYnVpbHQgb3V0IHRoZXNlIGZlYXR1cmVzLCBJIGNob3NlIHRvIHJ1biBhIEstTWVhbnMgY2x1c3RlcmluZyBhbGdvcml0aG0uIFdoaWxlIHRoZSBzaWxob3VldHRlIGFuZCBlbGJvdyBtZXRob2QgcHJvdmlkZWQgdGhlIG9wdGltYWwgY2x1c3RlcnMgdG8gYmUgMiwgSSBjaG9zZSB0byBydW4gYSB2YXJpZXR5IG9mIG51bWJlcnMgYW5kIHNldHRsZWQgb24gNCBhcyB0aGV5IHdlcmUgdGhlIGJlc3QgYXQgdGVsbGluZyBhIHN0b3J5LgoKRWFjaCBjbHVzdGVyIGlkZW50aWZpZXMgYSBzcGVjaWZpYyB0eXBlIG9mIGNvbnN1bWVyLCB3aGVyZSBDbHVzdGVyIDIgaXMgdGhlIG1vc3QgaW50ZXJlc3Rpbmc6IAoKQ2x1c3RlciAxOiBUTERSOiBDYW4ndCBUYXJnZXQgRXhpc3RpbmcgQ3VzdG9tZXJzIQpuID0gMTMxIEN1c3RvbWVycyAKQmFkOiBDdXJyZW50IGN1c3RvbWVycyAKT2theTogOCB5ZWFyIHJlbGF0aW9uc2hpcCBsZW5ndGgsIGFjdGl2ZSByYXRlIHN0cm9uZwpHb29kOiAgSW5kdXN0cnkgaW5kaWNhdG9yIHN0cm9uZywgNTMgc2l0ZSB2aXNpdHMgcGVyIHllYXIsIDEvd2VlawoKQ2x1c3RlciAyOiBUTERSOiBNYWpvcml0eSBQYXlQYWwgQ3VzdG9tZXJzLCBidXQgTmV0IE5ldyBDdXN0b21lcnMgd291bGQgYmUgYSBXSU4hCm4gPSAyMzc5IEN1c3RvbWVycyAKMzQyIGN1c3RvbWVycyBOZXQgTmV3IHN1YnNjcmliZXIgYmFzZSBjdXN0b21lcnMgd2l0aCA4MyUgaW5kdXN0cnkgaW5kaWNhdG9yCk1vc3RseSBjdXJyZW50IGN1c3RvbWVycywgYnV0IG9uY2Ugd2UgZmlsdGVyIGludG8gTmV0IE5ldyBjdXN0b21lcnMgd2Ugc2VlIGEgZGlmZmVyZW50IHN0b3J5IApPa2F5OiBPIHNpdGUgdmlzaXRzIChhYm91dCB+MS9tb250aCksIDQgeWVhciByZWxhdGlvbnNoaXAgbGVuZ3RoIAoKSWYgd2UgYXNzdW1lIHRoZSA2Nzk6MzAsMDAwIHJhdGlvIGhvbGRzLCB0aGUgMzQyIE5ldCBOZXcgY3VzdG9tZXJzIGZyb20gY2x1c3RlciAyIHRoYXQgaGF2ZSBhIHN0cm9uZyBpbmR1c3RyeSBpbmRpY2F0b3IgY291bGQgYmUgY29uc2lkZXJlZCBhIHN0cm9uZyBsZWFkLCBicmluZ2luZyBpbiBwb3RlbnRpYWxsIDE1LDAwMCBuZXcgY3VzdG9tZXJzCgpDbHVzdGVyIDM6IEFsc28sIE5ldCBOZXcgQ3VzdG9tZXJzIQpuID0gMTk5IEN1c3RvbWVycyAtIApCYWQ6IDE2IHllYXIgcmVsYXRpb25zaGlwIGFuZCAwIHRyYW5zYWN0aW9ucwpPa2F5OiAxIHNpdGUgdmlldyBwZXIgd2VlawpHb29kOiBJbmR1c3RyeSByZWxldmFudAoKQ2x1c3RlciA0OiBUb28gc21hbGwgdG8gY29uc2lkZXIKCkZyb20gb3VyIGZpbmFsIGFuYWx5c2lzLCB3ZSB3YW50IHRvIGNvbmZpcm0gdGhhdCBjdXJyZW50IFBheXBhbCBjdXN0b21lcnMgd2hvIGFyZSBzdWJzY3JpYmVycyB0byB0aGlzIG1hZ2F6aW5lIGhhdmUgYSByZWxhdGl2ZWx5IGhpZ2ggbGV2ZWwgb2YgYWN0aXZlIHRyYW5zYWN0aW9uIHJhdGVzLiBXZSBzZWUgYWJvdmUgdGhhdCBDbHVzdGVycyAxIGFuZCA0IGhhdmUgc3Ryb25nIGluZHVzdHJ5IGluZGljYXRvcnMuIENsdXN0ZXIgMSBoYXMgYW4gYWN0aXZpdHkgcmF0ZSBvZiAwLjYgYW5kIGlzIG1vc3Qgc2ltaWxhciB0byB0aGUgc3Vic2V0IG9mIHRoZSBOZXQgTmV3IDM0MiBjdXN0b21lcnMgZm91bmQgaW4gQ2x1c3RlciAyLiBUaGV5IGFyZSB5b3VuZ2VyIGN1c3RvbWVycyAoNCB5ZWFycyB2cyA4IHllYXJzKSBhbmQgaGF2ZSBsZXNzIHNpdGUgdmlzaXRzLCBidXQgd2l0aCB0aGUgcG90ZW50aWFsIG9mIDE1LDAwMCBuZXcgY3VzdG9tZXJzIGFuZCAwLjYgYWN0aXZlIHJhdGUgaW4gZXhpc3RpbmcgY3VzdG9tZXJzIHdpdGggYSBzaW1pbGFyIGluZHVzdHJ5IGluZGljYXRvciwgSSB3b3VsZCBhcmd1ZSB0aGF0IHRoaXMgdmVuZG9yIGNhbiBwcm92aWRlIDEwMDAgc3Ryb25nIGxlYWRzLiAKCgo=