library(ggplot2)
library(survival)
library(corrplot)
library(car)

Load Data

# read data
df <- read.csv("acquisition_and_defection_data.csv")
# show the structure of the data
str(df)
'data.frame':   500 obs. of  14 variables:
 $ Customer      : int  1 2 3 4 5 6 7 8 9 10 ...
 $ Acquisition   : int  1 0 0 1 1 0 0 0 1 1 ...
 $ Duration      : int  384 0 0 730 579 0 0 0 730 730 ...
 $ Retention     : int  0 0 0 1 0 0 0 0 1 1 ...
 $ First_Purchase: int  434 0 0 226 363 0 0 0 599 271 ...
 $ Acq_Expense   : int  760 148 253 610 672 436 363 884 452 787 ...
 $ Ret_Expense   : int  2310 0 0 2193 801 0 0 0 1341 2266 ...
 $ Future_CLV    : int  0 0 0 5732 0 0 0 0 6916 6084 ...
 $ Industry      : int  1 1 1 1 1 0 0 0 1 1 ...
 $ Revenue       : num  30.2 39.8 54.9 45.8 69 ...
 $ Employees     : int  1240 166 1016 122 313 359 902 264 1782 539 ...
 $ Breadth       : int  5 0 0 2 4 0 0 0 1 1 ...
 $ Frequency     : int  2 0 0 12 7 0 0 0 11 14 ...
 $ X             : logi  NA NA NA NA NA NA ...
# convert variables to factors
cols <- c("Acquisition", "Retention", "Industry")
df[cols] <- lapply(df[cols], factor)
# create variable names
levels(df$Acquisition) <- make.names(c("No", "Yes"))
levels(df$Retention) <- make.names(c("No", "Yes"))
levels(df$Industry) <- make.names(c("No", "Yes"))
# delete X column from df
df$X <- NULL
# show sample of the data
head(df)

ACQUISTION MODELING

Understanding Acquisition: In this first analysis, develop an acquisition model (acquired or not) based upon the following variables: a) acquisition expense, b) Industry, c) Firm revenue and d) Firm employees. Give a basic assessment of the predictive ability of your model and then provide a brief overview of the factors influencing successful acquisition. Since the number of predictor variables is limited, be sure to explore non-linear and/or interaction/moderating effects where possible.

Logistic Model

# fit inital logstic regression model to predict acquisition
acq_glm_fit <- glm(Acquisition ~ Acq_Expense + Industry + Revenue + Employees,
                   data = df, 
                   family = binomial(link = "logit"))
# fit just the intercept model
int_fit <- glm(Acquisition ~ 1,
               data = df, 
               family = binomial(link = "logit"))
# show the results of the model
summary(acq_glm_fit)

Call:
glm(formula = Acquisition ~ Acq_Expense + Industry + Revenue + 
    Employees, family = binomial(link = "logit"), data = df)

Deviance Residuals: 
    Min       1Q   Median       3Q      Max  
-3.7653  -0.1760   0.0224   0.2127   1.9177  

Coefficients:
              Estimate Std. Error z value Pr(>|z|)    
(Intercept) -1.352e+01  1.497e+00  -9.033  < 2e-16 ***
Acq_Expense  2.148e-02  2.219e-03   9.677  < 2e-16 ***
IndustryYes  1.126e-01  3.640e-01   0.309   0.7570    
Revenue      2.741e-02  1.110e-02   2.469   0.0135 *  
Employees    3.813e-03  5.477e-04   6.961 3.38e-12 ***
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

(Dispersion parameter for binomial family taken to be 1)

    Null deviance: 678.97  on 499  degrees of freedom
Residual deviance: 217.66  on 495  degrees of freedom
AIC: 227.66

Number of Fisher Scoring iterations: 7

Check Scatterplot Correlations

# subset dataframe to only continuous variables
cont_vars <- df[c("Acq_Expense", "Revenue", "Employees")]
# create function to provide correlations
panel.cor <- function(x, y, digits = 2, cex.cor, ...)
{
  usr <- par("usr"); on.exit(par(usr))
  par(usr = c(0, 1, 0, 1))
  # correlation coefficient
  r <- cor(x, y)
  txt <- format(c(r, 0.123456789), digits = digits)[1]
  txt <- paste("r= ", txt, sep = "")
  text(0.5, 0.6, txt)
  # p-value calculation
  p <- cor.test(x, y)$p.value
  txt2 <- format(c(p, 0.123456789), digits = digits)[1]
  txt2 <- paste("p= ", txt2, sep = "")
  if(p<0.01) txt2 <- paste("p= ", "<0.01", sep = "")
  text(0.5, 0.4, txt2)
}
# create scatterplots and correlations
pairs(cont_vars, upper.panel = panel.cor)

Inspect Model Fit

# show VIF for model
vif(acq_glm_fit)
Acq_Expense    Industry     Revenue   Employees 
   1.924360    1.032087    1.098079    1.852277 
# check LR test
anova(int_fit,acq_glm_fit,test="LRT")
Analysis of Deviance Table

Model 1: Acquisition ~ 1
Model 2: Acquisition ~ Acq_Expense + Industry + Revenue + Employees
  Resid. Df Resid. Dev Df Deviance  Pr(>Chi)    
1       499     678.97                          
2       495     217.66  4   461.31 < 2.2e-16 ***
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

CHURN MODELING

Modeling Defection: The next step is to model the defection/retention process for those 292 customers which were acquired. In doing so, utilize two different techniques: logistic regression and survival analysis. You may employ any of the variables you feel appropriate in either or both models. Again, alternative expressions of the variables may be useful. Also, can information from the acquisition model be incorporated

Number of Acquired Customers

# number of potential customers vs acquired
table(df$Acquisition)

 No Yes 
208 292 

Customer Lifetime Value vs. Rentention

# subset dataset into acquired customers
cust_acq <- df[df$Acquisition == "Yes", ]
# sort df by Future_CLV
cust_acq <- cust_acq[order(cust_acq$Future_CLV),]
# plot FutureCLV by Retention
ggplot(data = cust_acq) +
  geom_point(mapping = aes(x = seq(1, length(cust_acq$Future_CLV)), y = Future_CLV, color = Retention)) + 
  ggtitle("Future CLV vs. Rention for Acquired Customers") +
  labs(x = "Rank",y = "Future CLV") +
  theme(plot.title = element_text(lineheight = 0.8, face = "bold", hjust = 0.5)) +
  theme(legend.position = c(0.1, 0.85))

Duration vs. Retention

# sort df by Duration
cust_acq <- cust_acq[order(cust_acq$Duration),]
# plot Duration by Retention
ggplot(data = cust_acq) +
  geom_point(mapping = aes(x = seq(1, length(cust_acq$Duration)), y = Duration, color = Retention)) + 
  ggtitle("Duration vs. Rention for Acquired Customers") +
  labs(x = "Rank",y = "Duration") +
  theme(plot.title = element_text(lineheight = 0.8, face = "bold", hjust = 0.5)) +
  theme(legend.position = c(0.1, 0.85))

Logistic Model

# fit inital logstic regression model to predict churn without Future_CLV or Duration
churn_glm_fit <- glm(Retention ~ First_Purchase + Acq_Expense + Ret_Expense + Industry + Revenue + Employees + Breadth + Frequency,
                     data = cust_acq, 
                     family = binomial(link = "logit"))
glm.fit: fitted probabilities numerically 0 or 1 occurred
# show the results of the model
summary(churn_glm_fit)

Call:
glm(formula = Retention ~ First_Purchase + Acq_Expense + Ret_Expense + 
    Industry + Revenue + Employees + Breadth + Frequency, family = binomial(link = "logit"), 
    data = cust_acq)

Deviance Residuals: 
     Min        1Q    Median        3Q       Max  
-2.31948  -0.02187   0.00000   0.01035   2.52118  

Coefficients:
                Estimate Std. Error z value Pr(>|z|)    
(Intercept)     2.720040   3.719032   0.731 0.464545    
First_Purchase  0.014186   0.010309   1.376 0.168780    
Acq_Expense    -0.062551   0.012759  -4.903 9.45e-07 ***
Ret_Expense     0.009469   0.001786   5.301 1.15e-07 ***
IndustryYes     6.456693   1.439686   4.485 7.30e-06 ***
Revenue         0.060758   0.037490   1.621 0.105096    
Employees      -0.002217   0.002530  -0.876 0.380835    
Breadth         0.973277   0.290250   3.353 0.000799 ***
Frequency       1.084277   0.212643   5.099 3.41e-07 ***
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

(Dispersion parameter for binomial family taken to be 1)

    Null deviance: 403.14  on 291  degrees of freedom
Residual deviance:  59.85  on 283  degrees of freedom
AIC: 77.85

Number of Fisher Scoring iterations: 9
# show VIF for model
vif(churn_glm_fit)
First_Purchase    Acq_Expense    Ret_Expense       Industry        Revenue      Employees        Breadth      Frequency 
     16.085131      18.066321       8.440694       4.475781       4.039566      10.831953       2.303818      11.646520 

Check Scatterplot Correlations

# subset dataframe to only continuous variables
cont_vars <- cust_acq[c("First_Purchase", "Acq_Expense", "Ret_Expense", "Revenue", "Employees", "Breadth", "Frequency")]
# create scatterplots and correlations
pairs(cont_vars, upper.panel = panel.cor)

# fit inital logstic regression model to predict churn without Acq_Expense
churn_glm_fit1 <- glm(Retention ~ First_Purchase + Ret_Expense + Industry + Revenue + Employees + Breadth + Frequency,
                      data = cust_acq, 
                      family = binomial(link = "logit"))
# show the results of the model
summary(churn_glm_fit1)

Call:
glm(formula = Retention ~ First_Purchase + Ret_Expense + Industry + 
    Revenue + Employees + Breadth + Frequency, family = binomial(link = "logit"), 
    data = cust_acq)

Deviance Residuals: 
     Min        1Q    Median        3Q       Max  
-2.46126  -0.34800  -0.02215   0.31653   2.60652  

Coefficients:
                 Estimate Std. Error z value Pr(>|z|)    
(Intercept)    -1.724e+01  2.236e+00  -7.711 1.25e-14 ***
First_Purchase  5.203e-02  6.836e-03   7.612 2.70e-14 ***
Ret_Expense     3.628e-03  5.562e-04   6.522 6.92e-11 ***
IndustryYes     2.258e+00  4.868e-01   4.638 3.52e-06 ***
Revenue        -1.231e-01  2.220e-02  -5.545 2.95e-08 ***
Employees      -1.164e-02  1.632e-03  -7.135 9.68e-13 ***
Breadth         2.130e-01  1.231e-01   1.731   0.0835 .  
Frequency       4.115e-01  6.245e-02   6.588 4.45e-11 ***
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

(Dispersion parameter for binomial family taken to be 1)

    Null deviance: 403.14  on 291  degrees of freedom
Residual deviance: 157.63  on 284  degrees of freedom
AIC: 173.63

Number of Fisher Scoring iterations: 7
# show VIF for model
vif(churn_glm_fit1)
First_Purchase    Ret_Expense       Industry        Revenue      Employees        Breadth      Frequency 
     19.305937       1.924286       1.336773       3.495110      13.721545       1.104398       2.089035 

Check Scatterplot Correlations

# subset dataframe to only continuous variables
cont_vars <- cust_acq[c("First_Purchase", "Ret_Expense", "Revenue", "Employees", "Breadth", "Frequency")]
# create scatterplots and correlations
pairs(cont_vars, upper.panel = panel.cor)

# fit inital logstic regression model to predict churn without First_Purchase
churn_glm_fit2 <- glm(Retention ~ Ret_Expense + Industry + Revenue + Employees + Breadth + Frequency,
                      data = cust_acq, 
                      family = binomial(link = "logit"))
# show the results of the model
summary(churn_glm_fit2)

Call:
glm(formula = Retention ~ Ret_Expense + Industry + Revenue + 
    Employees + Breadth + Frequency, family = binomial(link = "logit"), 
    data = cust_acq)

Deviance Residuals: 
    Min       1Q   Median       3Q      Max  
-2.6710  -0.8412  -0.2798   0.9119   1.9508  

Coefficients:
              Estimate Std. Error z value Pr(>|z|)    
(Intercept) -7.3965059  1.0031115  -7.374 1.66e-13 ***
Ret_Expense  0.0017371  0.0002934   5.921 3.19e-09 ***
IndustryYes  1.2532274  0.3084229   4.063 4.84e-05 ***
Revenue      0.0281038  0.0087820   3.200  0.00137 ** 
Employees    0.0009735  0.0003055   3.187  0.00144 ** 
Breadth      0.0572668  0.0843417   0.679  0.49715    
Frequency    0.1939535  0.0341566   5.678 1.36e-08 ***
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

(Dispersion parameter for binomial family taken to be 1)

    Null deviance: 403.14  on 291  degrees of freedom
Residual deviance: 305.40  on 285  degrees of freedom
AIC: 319.4

Number of Fisher Scoring iterations: 5
# show VIF for model
vif(churn_glm_fit2)
Ret_Expense    Industry     Revenue   Employees     Breadth   Frequency 
   1.174202    1.139574    1.082365    1.127100    1.021835    1.258904 

Check Scatterplot Correlations

# subset dataframe to only continuous variables
cont_vars <- cust_acq[c("Ret_Expense", "Revenue", "Employees", "Breadth", "Frequency")]
# create scatterplots and correlations
pairs(cont_vars, upper.panel = panel.cor)

Survival Analysis

# create a "survival object" for each observation, using time and churn data.
cust_acq$survival <- Surv(cust_acq$Duration, cust_acq$Retention == "No")
# fit a basic survival curve using the data
fit <- survfit(survival ~ 1, data = cust_acq)
# plot the survival curve and add a title!
plot(fit, lty = 1, mark.time = FALSE, ylim=c(.75,1), xlab = 'Days since Subscribing', ylab = 'Percent Surviving')

# fit Cox Proportional Hazard Model
churn_coxph <- coxph(survival ~ Ret_Expense + Industry + Revenue + Employees + Breadth + Frequency,
                     data = cust_acq)
# show the results of the model
summary(churn_coxph)
Call:
coxph(formula = survival ~ Ret_Expense + Industry + Revenue + 
    Employees + Breadth + Frequency, data = cust_acq)

  n= 292, number of events= 157 

                  coef  exp(coef)   se(coef)      z Pr(>|z|)    
Ret_Expense -0.0011142  0.9988864  0.0001553 -7.173 7.36e-13 ***
IndustryYes -0.7286930  0.4825393  0.1644832 -4.430 9.41e-06 ***
Revenue     -0.0187239  0.9814503  0.0049961 -3.748 0.000178 ***
Employees   -0.0004553  0.9995448  0.0001767 -2.576 0.009994 ** 
Breadth     -0.0772937  0.9256180  0.0474889 -1.628 0.103606    
Frequency   -0.1115135  0.8944794  0.0182321 -6.116 9.58e-10 ***
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

            exp(coef) exp(-coef) lower .95 upper .95
Ret_Expense    0.9989      1.001    0.9986    0.9992
IndustryYes    0.4825      2.072    0.3496    0.6661
Revenue        0.9815      1.019    0.9719    0.9911
Employees      0.9995      1.000    0.9992    0.9999
Breadth        0.9256      1.080    0.8434    1.0159
Frequency      0.8945      1.118    0.8631    0.9270

Concordance= 0.722  (se = 0.024 )
Rsquare= 0.306   (max possible= 0.997 )
Likelihood ratio test= 106.5  on 6 df,   p=0
Wald test            = 99.41  on 6 df,   p=0
Score (logrank) test = 101.1  on 6 df,   p=0
LS0tCnRpdGxlOiAiQ3VzdG9tZXIgQWNxdWlzaXRpb24gdnMuIEN1c3RvbWVyIFJldGVudGlvbiIKYXV0aG9yOiAiQ3VydGlzIEwuIEhhbXB0b24iCm91dHB1dDogaHRtbF9ub3RlYm9vawotLS0KCmBgYHtyLCB3YXJuaW5nPUZBTFNFfQpsaWJyYXJ5KGdncGxvdDIpCmxpYnJhcnkoc3Vydml2YWwpCmxpYnJhcnkoY29ycnBsb3QpCmxpYnJhcnkoY2FyKQpgYGAKCiMgTG9hZCBEYXRhCmBgYHtyfQojIHJlYWQgZGF0YQpkZiA8LSByZWFkLmNzdigiYWNxdWlzaXRpb25fYW5kX2RlZmVjdGlvbl9kYXRhLmNzdiIpCmBgYAoKYGBge3J9CiMgc2hvdyB0aGUgc3RydWN0dXJlIG9mIHRoZSBkYXRhCnN0cihkZikKYGBgCgpgYGB7cn0KIyBjb252ZXJ0IHZhcmlhYmxlcyB0byBmYWN0b3JzCmNvbHMgPC0gYygiQWNxdWlzaXRpb24iLCAiUmV0ZW50aW9uIiwgIkluZHVzdHJ5IikKZGZbY29sc10gPC0gbGFwcGx5KGRmW2NvbHNdLCBmYWN0b3IpCmBgYAoKYGBge3J9CiMgY3JlYXRlIHZhcmlhYmxlIG5hbWVzCmxldmVscyhkZiRBY3F1aXNpdGlvbikgPC0gbWFrZS5uYW1lcyhjKCJObyIsICJZZXMiKSkKbGV2ZWxzKGRmJFJldGVudGlvbikgPC0gbWFrZS5uYW1lcyhjKCJObyIsICJZZXMiKSkKbGV2ZWxzKGRmJEluZHVzdHJ5KSA8LSBtYWtlLm5hbWVzKGMoIk5vIiwgIlllcyIpKQpgYGAKCmBgYHtyfQojIGRlbGV0ZSBYIGNvbHVtbiBmcm9tIGRmCmRmJFggPC0gTlVMTApgYGAKCmBgYHtyfQojIHNob3cgc2FtcGxlIG9mIHRoZSBkYXRhCmhlYWQoZGYpCmBgYAoKIyBBQ1FVSVNUSU9OIE1PREVMSU5HClVuZGVyc3RhbmRpbmcgQWNxdWlzaXRpb246IEluIHRoaXMgZmlyc3QgYW5hbHlzaXMsIGRldmVsb3AgYW4gYWNxdWlzaXRpb24gbW9kZWwgKGFjcXVpcmVkIG9yIG5vdCkgYmFzZWQgdXBvbiB0aGUgZm9sbG93aW5nIHZhcmlhYmxlczogYSkgYWNxdWlzaXRpb24gZXhwZW5zZSwgYikgSW5kdXN0cnksIGMpIEZpcm0gcmV2ZW51ZSBhbmQgZCkgRmlybSBlbXBsb3llZXMuIEdpdmUgYSBiYXNpYyBhc3Nlc3NtZW50IG9mIHRoZSBwcmVkaWN0aXZlIGFiaWxpdHkgb2YgeW91ciBtb2RlbCBhbmQgdGhlbiBwcm92aWRlIGEgYnJpZWYgb3ZlcnZpZXcgb2YgdGhlIGZhY3RvcnMgaW5mbHVlbmNpbmcgc3VjY2Vzc2Z1bCBhY3F1aXNpdGlvbi4gU2luY2UgdGhlIG51bWJlciBvZiBwcmVkaWN0b3IgdmFyaWFibGVzIGlzIGxpbWl0ZWQsIGJlIHN1cmUgdG8gZXhwbG9yZSBub24tbGluZWFyIGFuZC9vciBpbnRlcmFjdGlvbi9tb2RlcmF0aW5nIGVmZmVjdHMgd2hlcmUgcG9zc2libGUuCgojIyBMb2dpc3RpYyBNb2RlbApgYGB7cn0KIyBmaXQgaW5pdGFsIGxvZ3N0aWMgcmVncmVzc2lvbiBtb2RlbCB0byBwcmVkaWN0IGFjcXVpc2l0aW9uCmFjcV9nbG1fZml0IDwtIGdsbShBY3F1aXNpdGlvbiB+IEFjcV9FeHBlbnNlICsgSW5kdXN0cnkgKyBSZXZlbnVlICsgRW1wbG95ZWVzLAogICAgICAgICAgICAgICAgICAgZGF0YSA9IGRmLCAKICAgICAgICAgICAgICAgICAgIGZhbWlseSA9IGJpbm9taWFsKGxpbmsgPSAibG9naXQiKSkKYGBgCgpgYGB7cn0KIyBmaXQganVzdCB0aGUgaW50ZXJjZXB0IG1vZGVsCmludF9maXQgPC0gZ2xtKEFjcXVpc2l0aW9uIH4gMSwKICAgICAgICAgICAgICAgZGF0YSA9IGRmLCAKICAgICAgICAgICAgICAgZmFtaWx5ID0gYmlub21pYWwobGluayA9ICJsb2dpdCIpKQpgYGAKCmBgYHtyfQojIHNob3cgdGhlIHJlc3VsdHMgb2YgdGhlIG1vZGVsCnN1bW1hcnkoYWNxX2dsbV9maXQpCmBgYAoKIyMgQ2hlY2sgU2NhdHRlcnBsb3QgQ29ycmVsYXRpb25zCmBgYHtyfQojIHN1YnNldCBkYXRhZnJhbWUgdG8gb25seSBjb250aW51b3VzIHZhcmlhYmxlcwpjb250X3ZhcnMgPC0gZGZbYygiQWNxX0V4cGVuc2UiLCAiUmV2ZW51ZSIsICJFbXBsb3llZXMiKV0KYGBgCgpgYGB7cn0KIyBjcmVhdGUgZnVuY3Rpb24gdG8gcHJvdmlkZSBjb3JyZWxhdGlvbnMKcGFuZWwuY29yIDwtIGZ1bmN0aW9uKHgsIHksIGRpZ2l0cyA9IDIsIGNleC5jb3IsIC4uLikKewogIHVzciA8LSBwYXIoInVzciIpOyBvbi5leGl0KHBhcih1c3IpKQogIHBhcih1c3IgPSBjKDAsIDEsIDAsIDEpKQogICMgY29ycmVsYXRpb24gY29lZmZpY2llbnQKICByIDwtIGNvcih4LCB5KQogIHR4dCA8LSBmb3JtYXQoYyhyLCAwLjEyMzQ1Njc4OSksIGRpZ2l0cyA9IGRpZ2l0cylbMV0KICB0eHQgPC0gcGFzdGUoInI9ICIsIHR4dCwgc2VwID0gIiIpCiAgdGV4dCgwLjUsIDAuNiwgdHh0KQoKICAjIHAtdmFsdWUgY2FsY3VsYXRpb24KICBwIDwtIGNvci50ZXN0KHgsIHkpJHAudmFsdWUKICB0eHQyIDwtIGZvcm1hdChjKHAsIDAuMTIzNDU2Nzg5KSwgZGlnaXRzID0gZGlnaXRzKVsxXQogIHR4dDIgPC0gcGFzdGUoInA9ICIsIHR4dDIsIHNlcCA9ICIiKQogIGlmKHA8MC4wMSkgdHh0MiA8LSBwYXN0ZSgicD0gIiwgIjwwLjAxIiwgc2VwID0gIiIpCiAgdGV4dCgwLjUsIDAuNCwgdHh0MikKfQpgYGAKCmBgYHtyfQojIGNyZWF0ZSBzY2F0dGVycGxvdHMgYW5kIGNvcnJlbGF0aW9ucwpwYWlycyhjb250X3ZhcnMsIHVwcGVyLnBhbmVsID0gcGFuZWwuY29yKQpgYGAKCiMjIEluc3BlY3QgTW9kZWwgRml0CmBgYHtyfQojIHNob3cgVklGIGZvciBtb2RlbAp2aWYoYWNxX2dsbV9maXQpCmBgYAoKYGBge3J9CiMgY2hlY2sgTFIgdGVzdAphbm92YShpbnRfZml0LGFjcV9nbG1fZml0LHRlc3Q9IkxSVCIpCmBgYAoKIyBDSFVSTiBNT0RFTElORwpNb2RlbGluZyBEZWZlY3Rpb246IFRoZSBuZXh0IHN0ZXAgaXMgdG8gbW9kZWwgdGhlIGRlZmVjdGlvbi9yZXRlbnRpb24gcHJvY2VzcyBmb3IgdGhvc2UgMjkyIGN1c3RvbWVycyB3aGljaCB3ZXJlIGFjcXVpcmVkLiBJbiBkb2luZyBzbywgdXRpbGl6ZSB0d28gZGlmZmVyZW50IHRlY2huaXF1ZXM6IGxvZ2lzdGljIHJlZ3Jlc3Npb24gYW5kIHN1cnZpdmFsIGFuYWx5c2lzLiBZb3UgbWF5IGVtcGxveSBhbnkgb2YgdGhlIHZhcmlhYmxlcyB5b3UgZmVlbCBhcHByb3ByaWF0ZSBpbiBlaXRoZXIgb3IgYm90aCBtb2RlbHMuIEFnYWluLCBhbHRlcm5hdGl2ZSBleHByZXNzaW9ucyBvZiB0aGUgdmFyaWFibGVzIG1heSBiZSB1c2VmdWwuIEFsc28sIGNhbiBpbmZvcm1hdGlvbiBmcm9tIHRoZSBhY3F1aXNpdGlvbiBtb2RlbCBiZSBpbmNvcnBvcmF0ZWQKCiMjIE51bWJlciBvZiBBY3F1aXJlZCBDdXN0b21lcnMKYGBge3J9CiMgbnVtYmVyIG9mIHBvdGVudGlhbCBjdXN0b21lcnMgdnMgYWNxdWlyZWQKdGFibGUoZGYkQWNxdWlzaXRpb24pCmBgYAoKIyMgQ3VzdG9tZXIgTGlmZXRpbWUgVmFsdWUgdnMuIFJlbnRlbnRpb24KYGBge3J9CiMgc3Vic2V0IGRhdGFzZXQgaW50byBhY3F1aXJlZCBjdXN0b21lcnMKY3VzdF9hY3EgPC0gZGZbZGYkQWNxdWlzaXRpb24gPT0gIlllcyIsIF0KYGBgCgpgYGB7cn0KIyBzb3J0IGRmIGJ5IEZ1dHVyZV9DTFYKY3VzdF9hY3EgPC0gY3VzdF9hY3Fbb3JkZXIoY3VzdF9hY3EkRnV0dXJlX0NMViksXQpgYGAKCmBgYHtyfQojIHBsb3QgRnV0dXJlQ0xWIGJ5IFJldGVudGlvbgpnZ3Bsb3QoZGF0YSA9IGN1c3RfYWNxKSArCiAgZ2VvbV9wb2ludChtYXBwaW5nID0gYWVzKHggPSBzZXEoMSwgbGVuZ3RoKGN1c3RfYWNxJEZ1dHVyZV9DTFYpKSwgeSA9IEZ1dHVyZV9DTFYsIGNvbG9yID0gUmV0ZW50aW9uKSkgKyAKICBnZ3RpdGxlKCJGdXR1cmUgQ0xWIHZzLiBSZW50aW9uIGZvciBBY3F1aXJlZCBDdXN0b21lcnMiKSArCiAgbGFicyh4ID0gIlJhbmsiLHkgPSAiRnV0dXJlIENMViIpICsKICB0aGVtZShwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KGxpbmVoZWlnaHQgPSAwLjgsIGZhY2UgPSAiYm9sZCIsIGhqdXN0ID0gMC41KSkgKwogIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9IGMoMC4xLCAwLjg1KSkKYGBgCgojIyBEdXJhdGlvbiB2cy4gUmV0ZW50aW9uCmBgYHtyfQojIHNvcnQgZGYgYnkgRHVyYXRpb24KY3VzdF9hY3EgPC0gY3VzdF9hY3Fbb3JkZXIoY3VzdF9hY3EkRHVyYXRpb24pLF0KYGBgCgpgYGB7cn0KIyBwbG90IER1cmF0aW9uIGJ5IFJldGVudGlvbgpnZ3Bsb3QoZGF0YSA9IGN1c3RfYWNxKSArCiAgZ2VvbV9wb2ludChtYXBwaW5nID0gYWVzKHggPSBzZXEoMSwgbGVuZ3RoKGN1c3RfYWNxJER1cmF0aW9uKSksIHkgPSBEdXJhdGlvbiwgY29sb3IgPSBSZXRlbnRpb24pKSArIAogIGdndGl0bGUoIkR1cmF0aW9uIHZzLiBSZW50aW9uIGZvciBBY3F1aXJlZCBDdXN0b21lcnMiKSArCiAgbGFicyh4ID0gIlJhbmsiLHkgPSAiRHVyYXRpb24iKSArCiAgdGhlbWUocGxvdC50aXRsZSA9IGVsZW1lbnRfdGV4dChsaW5laGVpZ2h0ID0gMC44LCBmYWNlID0gImJvbGQiLCBoanVzdCA9IDAuNSkpICsKICB0aGVtZShsZWdlbmQucG9zaXRpb24gPSBjKDAuMSwgMC44NSkpCmBgYAoKIyMgTG9naXN0aWMgTW9kZWwKYGBge3J9CiMgZml0IGluaXRhbCBsb2dzdGljIHJlZ3Jlc3Npb24gbW9kZWwgdG8gcHJlZGljdCBjaHVybiB3aXRob3V0IEZ1dHVyZV9DTFYgb3IgRHVyYXRpb24KY2h1cm5fZ2xtX2ZpdCA8LSBnbG0oUmV0ZW50aW9uIH4gRmlyc3RfUHVyY2hhc2UgKyBBY3FfRXhwZW5zZSArIFJldF9FeHBlbnNlICsgSW5kdXN0cnkgKyBSZXZlbnVlICsgRW1wbG95ZWVzICsgQnJlYWR0aCArIEZyZXF1ZW5jeSwKICAgICAgICAgICAgICAgICAgICAgZGF0YSA9IGN1c3RfYWNxLCAKICAgICAgICAgICAgICAgICAgICAgZmFtaWx5ID0gYmlub21pYWwobGluayA9ICJsb2dpdCIpKQpgYGAKCmBgYHtyfQojIHNob3cgdGhlIHJlc3VsdHMgb2YgdGhlIG1vZGVsCnN1bW1hcnkoY2h1cm5fZ2xtX2ZpdCkKYGBgCgpgYGB7cn0KIyBzaG93IFZJRiBmb3IgbW9kZWwKdmlmKGNodXJuX2dsbV9maXQpCmBgYAoKIyMgQ2hlY2sgU2NhdHRlcnBsb3QgQ29ycmVsYXRpb25zCmBgYHtyfQojIHN1YnNldCBkYXRhZnJhbWUgdG8gb25seSBjb250aW51b3VzIHZhcmlhYmxlcwpjb250X3ZhcnMgPC0gY3VzdF9hY3FbYygiRmlyc3RfUHVyY2hhc2UiLCAiQWNxX0V4cGVuc2UiLCAiUmV0X0V4cGVuc2UiLCAiUmV2ZW51ZSIsICJFbXBsb3llZXMiLCAiQnJlYWR0aCIsICJGcmVxdWVuY3kiKV0KYGBgCgpgYGB7cn0KIyBjcmVhdGUgc2NhdHRlcnBsb3RzIGFuZCBjb3JyZWxhdGlvbnMKcGFpcnMoY29udF92YXJzLCB1cHBlci5wYW5lbCA9IHBhbmVsLmNvcikKYGBgCgpgYGB7cn0KIyBmaXQgaW5pdGFsIGxvZ3N0aWMgcmVncmVzc2lvbiBtb2RlbCB0byBwcmVkaWN0IGNodXJuIHdpdGhvdXQgQWNxX0V4cGVuc2UKY2h1cm5fZ2xtX2ZpdDEgPC0gZ2xtKFJldGVudGlvbiB+IEZpcnN0X1B1cmNoYXNlICsgUmV0X0V4cGVuc2UgKyBJbmR1c3RyeSArIFJldmVudWUgKyBFbXBsb3llZXMgKyBCcmVhZHRoICsgRnJlcXVlbmN5LAogICAgICAgICAgICAgICAgICAgICAgZGF0YSA9IGN1c3RfYWNxLCAKICAgICAgICAgICAgICAgICAgICAgIGZhbWlseSA9IGJpbm9taWFsKGxpbmsgPSAibG9naXQiKSkKYGBgCgpgYGB7cn0KIyBzaG93IHRoZSByZXN1bHRzIG9mIHRoZSBtb2RlbApzdW1tYXJ5KGNodXJuX2dsbV9maXQxKQpgYGAKCmBgYHtyfQojIHNob3cgVklGIGZvciBtb2RlbAp2aWYoY2h1cm5fZ2xtX2ZpdDEpCmBgYAoKIyMjIENoZWNrIFNjYXR0ZXJwbG90IENvcnJlbGF0aW9ucwpgYGB7cn0KIyBzdWJzZXQgZGF0YWZyYW1lIHRvIG9ubHkgY29udGludW91cyB2YXJpYWJsZXMKY29udF92YXJzIDwtIGN1c3RfYWNxW2MoIkZpcnN0X1B1cmNoYXNlIiwgIlJldF9FeHBlbnNlIiwgIlJldmVudWUiLCAiRW1wbG95ZWVzIiwgIkJyZWFkdGgiLCAiRnJlcXVlbmN5IildCmBgYAoKYGBge3J9CiMgY3JlYXRlIHNjYXR0ZXJwbG90cyBhbmQgY29ycmVsYXRpb25zCnBhaXJzKGNvbnRfdmFycywgdXBwZXIucGFuZWwgPSBwYW5lbC5jb3IpCmBgYAoKYGBge3J9CiMgZml0IGluaXRhbCBsb2dzdGljIHJlZ3Jlc3Npb24gbW9kZWwgdG8gcHJlZGljdCBjaHVybiB3aXRob3V0IEZpcnN0X1B1cmNoYXNlCmNodXJuX2dsbV9maXQyIDwtIGdsbShSZXRlbnRpb24gfiBSZXRfRXhwZW5zZSArIEluZHVzdHJ5ICsgUmV2ZW51ZSArIEVtcGxveWVlcyArIEJyZWFkdGggKyBGcmVxdWVuY3ksCiAgICAgICAgICAgICAgICAgICAgICBkYXRhID0gY3VzdF9hY3EsIAogICAgICAgICAgICAgICAgICAgICAgZmFtaWx5ID0gYmlub21pYWwobGluayA9ICJsb2dpdCIpKQpgYGAKCmBgYHtyfQojIHNob3cgdGhlIHJlc3VsdHMgb2YgdGhlIG1vZGVsCnN1bW1hcnkoY2h1cm5fZ2xtX2ZpdDIpCmBgYAoKYGBge3J9CiMgc2hvdyBWSUYgZm9yIG1vZGVsCnZpZihjaHVybl9nbG1fZml0MikKYGBgCgojIyBDaGVjayBTY2F0dGVycGxvdCBDb3JyZWxhdGlvbnMKYGBge3J9CiMgc3Vic2V0IGRhdGFmcmFtZSB0byBvbmx5IGNvbnRpbnVvdXMgdmFyaWFibGVzCmNvbnRfdmFycyA8LSBjdXN0X2FjcVtjKCJSZXRfRXhwZW5zZSIsICJSZXZlbnVlIiwgIkVtcGxveWVlcyIsICJCcmVhZHRoIiwgIkZyZXF1ZW5jeSIpXQpgYGAKCmBgYHtyfQojIGNyZWF0ZSBzY2F0dGVycGxvdHMgYW5kIGNvcnJlbGF0aW9ucwpwYWlycyhjb250X3ZhcnMsIHVwcGVyLnBhbmVsID0gcGFuZWwuY29yKQpgYGAKCiMgU3Vydml2YWwgQW5hbHlzaXMKYGBge3J9CiMgY3JlYXRlIGEgInN1cnZpdmFsIG9iamVjdCIgZm9yIGVhY2ggb2JzZXJ2YXRpb24sIHVzaW5nIHRpbWUgYW5kIGNodXJuIGRhdGEuCmN1c3RfYWNxJHN1cnZpdmFsIDwtIFN1cnYoY3VzdF9hY3EkRHVyYXRpb24sIGN1c3RfYWNxJFJldGVudGlvbiA9PSAiTm8iKQpgYGAKCmBgYHtyfQojIGZpdCBhIGJhc2ljIHN1cnZpdmFsIGN1cnZlIHVzaW5nIHRoZSBkYXRhCmZpdCA8LSBzdXJ2Zml0KHN1cnZpdmFsIH4gMSwgZGF0YSA9IGN1c3RfYWNxKQpgYGAKCmBgYHtyfQojIHBsb3QgdGhlIHN1cnZpdmFsIGN1cnZlIGFuZCBhZGQgYSB0aXRsZSEKcGxvdChmaXQsIGx0eSA9IDEsIG1hcmsudGltZSA9IEZBTFNFLCB5bGltPWMoLjc1LDEpLCB4bGFiID0gJ0RheXMgc2luY2UgU3Vic2NyaWJpbmcnLCB5bGFiID0gJ1BlcmNlbnQgU3Vydml2aW5nJykKYGBgCgpgYGB7cn0KIyBmaXQgQ294IFByb3BvcnRpb25hbCBIYXphcmQgTW9kZWwKY2h1cm5fY294cGggPC0gY294cGgoc3Vydml2YWwgfiBSZXRfRXhwZW5zZSArIEluZHVzdHJ5ICsgUmV2ZW51ZSArIEVtcGxveWVlcyArIEJyZWFkdGggKyBGcmVxdWVuY3ksCiAgICAgICAgICAgICAgICAgICAgIGRhdGEgPSBjdXN0X2FjcSkKYGBgCgpgYGB7cn0KIyBzaG93IHRoZSByZXN1bHRzIG9mIHRoZSBtb2RlbApzdW1tYXJ5KGNodXJuX2NveHBoKQpgYGA=