1 Introduction and Objective

The objective of this project is to identify which behavioral and contextual factors most strongly predict whether an online retail session results in a purchase. The analysis uses the Online Shoppers Purchasing Intention dataset, which contains 12,330 browsing sessions and 18 variables describing user behavior, engagement, navigation, and session attributes.

We apply multiple logistic regression to model the probability that a customer completes a purchase (Revenue = "Yes"). Several candidate models are estimated and compared, including:

  • A full standardized model with all main effects,
  • A reduced model focused on key predictors,
  • A stepwise-selected model, and
  • A discrete model using a binned version of PageValues.

This analysis is designed to answer three core questions:

  1. Which continuous behaviors (durations, bounce/exit rates, page values) are the strongest predictors of purchases?
  2. Which categorical attributes (Month, VisitorType, Weekend) meaningfully influence conversion likelihood?
  3. Which logistic model provides the best overall performance based on AIC, cross-validation AUC, and hold-out test accuracy?

The ultimate goal is to combine predictive performance with interpretable insights that can guide marketing and UX decisions.

2 Data Summary and Preparation

2.1 Data Import and Structure

data_path <- "C:/Users/rg03/Downloads/sta321/online_shoppers_intention.csv"
dat <- read.csv(data_path, stringsAsFactors = FALSE)

dat$Revenue <- factor(dat$Revenue, levels = c(FALSE, TRUE), labels = c("No", "Yes"))

glimpse(dat)
## Rows: 12,330
## Columns: 18
## $ Administrative          <int> 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 2…
## $ Administrative_Duration <dbl> 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 5…
## $ Informational           <int> 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0…
## $ Informational_Duration  <dbl> 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0…
## $ ProductRelated          <int> 1, 2, 1, 2, 10, 19, 1, 0, 2, 3, 3, 16, 7, 6, 2…
## $ ProductRelated_Duration <dbl> 0.000000, 64.000000, 0.000000, 2.666667, 627.5…
## $ BounceRates             <dbl> 0.200000000, 0.000000000, 0.200000000, 0.05000…
## $ ExitRates               <dbl> 0.200000000, 0.100000000, 0.200000000, 0.14000…
## $ PageValues              <dbl> 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0…
## $ SpecialDay              <dbl> 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.4, 0.0, 0.8, 0…
## $ Month                   <chr> "Feb", "Feb", "Feb", "Feb", "Feb", "Feb", "Feb…
## $ OperatingSystems        <int> 1, 2, 4, 3, 3, 2, 2, 1, 2, 2, 1, 1, 1, 2, 3, 1…
## $ Browser                 <int> 1, 2, 1, 2, 3, 2, 4, 2, 2, 4, 1, 1, 1, 5, 2, 1…
## $ Region                  <int> 1, 1, 9, 2, 1, 1, 3, 1, 2, 1, 3, 4, 1, 1, 3, 9…
## $ TrafficType             <int> 1, 2, 3, 4, 4, 3, 3, 5, 3, 2, 3, 3, 3, 3, 3, 3…
## $ VisitorType             <chr> "Returning_Visitor", "Returning_Visitor", "Ret…
## $ Weekend                 <lgl> FALSE, FALSE, FALSE, FALSE, TRUE, FALSE, FALSE…
## $ Revenue                 <fct> No, No, No, No, No, No, No, No, No, No, No, No…
summary(dat$Revenue)
##    No   Yes 
## 10422  1908

Interpretation.
The dataset consists of 12,330 sessions with the binary outcome Revenue indicating whether a purchase occurred. The response is highly imbalanced, with the majority of sessions yielding no purchase, a common feature of online shopping data. This imbalance will affect accuracy and recall metrics and must be kept in mind when evaluating model performance.

2.2 Variables Used in the Analysis

For this project, we focus on predictors directly linked to on-site behavior and session characteristics:

  • Continuous variables (behavioral durations, rates, values)
    • Administrative_Duration – Time spent in administrative-related pages (e.g., account, login).
    • Informational_Duration – Time spent on informational pages.
    • ProductRelated_Duration – Time spent on product-related pages.
    • BounceRates – Probability that the user left the site from the landing page without further interaction.
    • ExitRates – Probability that the user exited the site after viewing a given page.
    • PageValues – Aggregate measure of page value based on prior ecommerce and conversion metrics.
  • Categorical variables
    • Month – Month of the session (captures seasonal patterns).
    • VisitorType – New or returning visitor.
    • Weekend – Indicator for weekend sessions.
use_vars <- c(
  "Administrative_Duration", "Informational_Duration", "ProductRelated_Duration",
  "BounceRates", "ExitRates", "PageValues",
  "Month", "VisitorType", "Weekend"
)

dat2 <- dat %>%
  dplyr::select(Revenue, dplyr::all_of(use_vars)) %>%
  na.omit() %>%
  mutate(
    Month      = factor(Month),
    VisitorType = factor(VisitorType),
    Weekend     = factor(Weekend)
  )

summary(dat2)
##  Revenue     Administrative_Duration Informational_Duration
##  No :10422   Min.   :   0.00         Min.   :   0.00       
##  Yes: 1908   1st Qu.:   0.00         1st Qu.:   0.00       
##              Median :   7.50         Median :   0.00       
##              Mean   :  80.82         Mean   :  34.47       
##              3rd Qu.:  93.26         3rd Qu.:   0.00       
##              Max.   :3398.75         Max.   :2549.38       
##                                                            
##  ProductRelated_Duration  BounceRates         ExitRates         PageValues     
##  Min.   :    0.0         Min.   :0.000000   Min.   :0.00000   Min.   :  0.000  
##  1st Qu.:  184.1         1st Qu.:0.000000   1st Qu.:0.01429   1st Qu.:  0.000  
##  Median :  598.9         Median :0.003112   Median :0.02516   Median :  0.000  
##  Mean   : 1194.8         Mean   :0.022191   Mean   :0.04307   Mean   :  5.889  
##  3rd Qu.: 1464.2         3rd Qu.:0.016813   3rd Qu.:0.05000   3rd Qu.:  0.000  
##  Max.   :63973.5         Max.   :0.200000   Max.   :0.20000   Max.   :361.764  
##                                                                                
##      Month                 VisitorType     Weekend    
##  May    :3364   New_Visitor      : 1694   FALSE:9462  
##  Nov    :2998   Other            :   85   TRUE :2868  
##  Mar    :1907   Returning_Visitor:10551               
##  Dec    :1727                                         
##  Oct    : 549                                         
##  Sep    : 448                                         
##  (Other):1337

Interpretation.
The cleaned dataset dat2 focuses on predictors with clear behavioral interpretation and minimal missingness. We ensure that Month, VisitorType, and Weekend are treated as factors, while the durations, bounce/exit rates, and page values remain numeric.

2.3 Correlation and Relationships Among Numeric Predictors

pairs(
  ~ Administrative_Duration + Informational_Duration +
    ProductRelated_Duration + BounceRates + ExitRates + PageValues,
  data = dat2,
  main = "Scatterplot Matrix of Continuous Predictors"
)

num_df <- dat2 %>% dplyr::select(where(is.numeric))
if (ncol(num_df) > 1) {
  round(cor(num_df), 2)
}
##                         Administrative_Duration Informational_Duration
## Administrative_Duration                    1.00                   0.24
## Informational_Duration                     0.24                   1.00
## ProductRelated_Duration                    0.36                   0.35
## BounceRates                               -0.14                  -0.07
## ExitRates                                 -0.21                  -0.11
## PageValues                                 0.07                   0.03
##                         ProductRelated_Duration BounceRates ExitRates
## Administrative_Duration                    0.36       -0.14     -0.21
## Informational_Duration                     0.35       -0.07     -0.11
## ProductRelated_Duration                    1.00       -0.18     -0.25
## BounceRates                               -0.18        1.00      0.91
## ExitRates                                 -0.25        0.91      1.00
## PageValues                                 0.05       -0.12     -0.17
##                         PageValues
## Administrative_Duration       0.07
## Informational_Duration        0.03
## ProductRelated_Duration       0.05
## BounceRates                  -0.12
## ExitRates                    -0.17
## PageValues                    1.00

Interpretation.
The scatterplot matrix and correlation matrix reveal expected patterns:

  • BounceRates and ExitRates are highly correlated, as both measure disengagement.
  • ProductRelated_Duration and PageValues tend to increase in sessions leading to purchases.
  • No pair of variables suggests perfect collinearity, but some predictors (especially bounce/exit rates) convey similar information and should be monitored for multicollinearity.

These findings guide model building and help interpret overlapping effects.

2.4 2.4 Standardization and Discretization

To improve interpretability and numerical stability:

  • All numeric predictors are standardized (z-scores).
  • PageValues is also discretized into tertiles for a separate model to test for nonlinear threshold effects.
num_vars <- c(
  "Administrative_Duration","Informational_Duration","ProductRelated_Duration",
  "BounceRates","ExitRates","PageValues"
)

safe_pagevalues_tertile <- function(x) {
  ux <- unique(x[is.finite(x)])
  if (length(ux) >= 3) {
    factor(dplyr::ntile(x, 3), labels = c("Low","Mid","High"))
  } else {
    med <- median(x, na.rm = TRUE)
    factor(ifelse(x <= med, "Low", "High"), levels = c("Low","High"))
  }
}

dat3 <- dat2 %>%
  mutate(
    across(all_of(num_vars), ~ as.numeric(scale(.x)), .names = "{.col}_z"),
    PageValues_tertile = safe_pagevalues_tertile(PageValues)
  )

pander(data.frame(Standardized = paste0(num_vars, "_z")),
       caption = "Numerical Variables Standardized (z-scores)")
Numerical Variables Standardized (z-scores)
Standardized
Administrative_Duration_z
Informational_Duration_z
ProductRelated_Duration_z
BounceRates_z
ExitRates_z
PageValues_z
pander(table(dat3$PageValues_tertile),
       caption = "Discretized Variable: PageValues (safe tertiles / median split)")
Discretized Variable: PageValues (safe tertiles / median split)
Low Mid High
4110 4110 4110

Interpretation.
Standardization (*_z) places all numeric predictors on a common scale, so coefficient magnitudes can be compared directly in the logistic regression. The PageValues_tertile factor allows us to check whether there is a meaningful difference between “Low”, “Mid”, and “High” page value sessions beyond a linear effect.


3 Train–Test Split

3.1 Rationale

To evaluate model generalization, we use a 70/30 train–test split with stratified sampling on the outcome Revenue. This maintains the same proportion of purchases and non-purchases in both training and test sets, crucial in imbalanced data.

3.2 Implementation and Class Proportions

set.seed(321)
idx <- createDataPartition(dat3$Revenue, p = 0.70, list = FALSE)
train <- dat3[idx, ]
test  <- dat3[-idx, ]

pander(rbind(Train = prop.table(table(train$Revenue)),
             Test  = prop.table(table(test$Revenue))),
       caption = "Stratified Split: Class Proportions (Revenue)")
Stratified Split: Class Proportions (Revenue)
  No Yes
Train 0.8452 0.1548
Test 0.8453 0.1547

Interpretation.
The training and test sets preserve the original class imbalance, with roughly 15% “Yes” (purchase) and 85% “No” (non-purchase). This ensures that performance metrics from the test set reflect realistic deployment conditions.


4 Candidate Logistic Regression Models

4.1 Model Definitions

We consider four candidate models:

  1. Full Model – includes all standardized numeric predictors and categorical factors:
    Administrative_Duration_z, Informational_Duration_z, ProductRelated_Duration_z, BounceRates_z, ExitRates_z, PageValues_z, Month, VisitorType, and Weekend.

  2. Reduced Model – focuses on key predictors based on domain knowledge:
    BounceRates_z, ExitRates_z, PageValues_z, VisitorType.

  3. Stepwise Model – built using stepwise forward selection (by AIC) from the reduced model towards the full model.

  4. Discrete Model – replaces PageValues_z with PageValues_tertile to evaluate potential threshold effects.

full.model <- glm(
  Revenue ~ Administrative_Duration_z + Informational_Duration_z +
    ProductRelated_Duration_z + BounceRates_z + ExitRates_z +
    PageValues_z + Month + VisitorType + Weekend,
  data = train, family = binomial(link = "logit")
)

reduced.model <- glm(
  Revenue ~ BounceRates_z + ExitRates_z + PageValues_z + VisitorType,
  data = train, family = binomial(link = "logit")
)

final.model <- stepAIC(
  reduced.model,
  scope = list(lower = formula(reduced.model), upper = formula(full.model)),
  direction = "forward",
  trace = 0
)

discrete.model <- glm(
  Revenue ~ PageValues_tertile + BounceRates_z + ExitRates_z +
    VisitorType + Month + Weekend,
  data = train, family = binomial(link = "logit")
)

4.2 Full Model: Coefficients and Multicollinearity

pander(summary(full.model)$coef,
       caption = "Full Model (Train) — Inferential Statistics")
Full Model (Train) — Inferential Statistics
  Estimate Std. Error z value Pr(>|z|)
(Intercept) -1.908 0.2169 -8.796 1.412e-18
Administrative_Duration_z 0.0259 0.03352 0.7726 0.4397
Informational_Duration_z 0.03611 0.02992 1.207 0.2275
ProductRelated_Duration_z 0.2041 0.03402 5.999 1.985e-09
BounceRates_z -0.2185 0.1833 -1.192 0.2332
ExitRates_z -0.67 0.1347 -4.973 6.598e-07
PageValues_z 1.594 0.05489 29.04 1.97e-185
MonthDec -0.4409 0.2233 -1.975 0.0483
MonthFeb -1.825 0.7998 -2.281 0.02253
MonthJul 0.07092 0.2728 0.2599 0.7949
MonthJune -0.4791 0.357 -1.342 0.1796
MonthMar -0.3442 0.2196 -1.567 0.1171
MonthMay -0.4549 0.2087 -2.18 0.02929
MonthNov 0.6967 0.2011 3.464 0.0005313
MonthOct 0.1461 0.2456 0.595 0.5518
MonthSep 0.1604 0.262 0.6122 0.5404
VisitorTypeOther -1.237 0.7403 -1.671 0.09469
VisitorTypeReturning_Visitor -0.3463 0.1027 -3.373 0.0007432
WeekendTRUE 0.07555 0.08529 0.8858 0.3757
pander(vif(full.model), caption = "VIF — Full Model")
VIF — Full Model
  GVIF Df GVIF^(1/(2*Df))
Administrative_Duration_z 1.161 1 1.077
Informational_Duration_z 1.142 1 1.068
ProductRelated_Duration_z 1.332 1 1.154
BounceRates_z 2.023 1 1.422
ExitRates_z 2.149 1 1.466
PageValues_z 1.064 1 1.031
Month 1.103 9 1.005
VisitorType 1.126 2 1.03
Weekend 1.011 1 1.006

Interpretation.
In the full model:

  • PageValues_z is a strong positive predictor: higher page value is associated with much higher odds of purchase.
  • ProductRelated_Duration_z has a positive effect: more time on product pages increases conversion likelihood.
  • ExitRates_z is strongly negative: higher exit rates sharply reduce purchase odds.
  • Some months (e.g., November) show positive effects, consistent with holiday shopping peaks; other months (e.g., May, December, February) can show negative effects relative to baseline.
  • VisitorType = Returning Visitor significantly increases purchase probability.

VIF values are generally moderate; while BounceRates_z and ExitRates_z are correlated, their VIFs do not indicate severe multicollinearity. The full model is statistically sound and interpretable.

4.3 Reduced and Stepwise Models

pander(summary(reduced.model)$coef,
       caption = "Reduced Model (Train) — Inferential Statistics")
Reduced Model (Train) — Inferential Statistics
  Estimate Std. Error z value Pr(>|z|)
(Intercept) -2.007 0.1041 -19.29 6.543e-83
BounceRates_z -0.02422 0.176 -0.1376 0.8905
ExitRates_z -0.9555 0.1299 -7.353 1.937e-13
PageValues_z 1.528 0.05267 29.01 5.482e-185
VisitorTypeOther -1.164 0.714 -1.63 0.1031
VisitorTypeReturning_Visitor -0.1751 0.09759 -1.794 0.07278
pander(summary(final.model)$coef,
       caption = "Stepwise Forward Model (Train) — Inferential Statistics")
Stepwise Forward Model (Train) — Inferential Statistics
  Estimate Std. Error z value Pr(>|z|)
(Intercept) -1.889 0.216 -8.744 2.242e-18
BounceRates_z -0.2048 0.183 -1.119 0.2633
ExitRates_z -0.6872 0.1343 -5.116 3.128e-07
PageValues_z 1.596 0.05488 29.08 7.394e-186
VisitorTypeOther -1.25 0.7434 -1.682 0.09257
VisitorTypeReturning_Visitor -0.3482 0.1026 -3.395 0.0006875
MonthDec -0.4375 0.2235 -1.958 0.05023
MonthFeb -1.842 0.801 -2.299 0.02148
MonthJul 0.07393 0.2728 0.2711 0.7863
MonthJune -0.4998 0.3574 -1.398 0.162
MonthMar -0.3352 0.2198 -1.525 0.1272
MonthMay -0.4528 0.209 -2.167 0.03025
MonthNov 0.699 0.2013 3.473 0.0005153
MonthOct 0.1548 0.2457 0.6302 0.5286
MonthSep 0.1574 0.2624 0.5998 0.5486
ProductRelated_Duration_z 0.224 0.03086 7.258 3.923e-13

Interpretation.
The reduced model retains only disengagement rates and overall page value plus visitor type. It is simpler but drops potentially important duration and seasonal information. The stepwise model reintroduces some of these predictors based on AIC, improving fit but not substantially surpassing the full model.

4.4 Discrete Model

pander(summary(discrete.model)$coef,
       caption = "Discrete Variant (Train) — Inferential Statistics")
Discrete Variant (Train) — Inferential Statistics
  Estimate Std. Error z value Pr(>|z|)
(Intercept) -19.6 189.4 -0.1035 0.9176
PageValues_tertileMid 17.28 189.4 0.09123 0.9273
PageValues_tertileHigh 19.45 189.4 0.1027 0.9182
BounceRates_z -0.1847 0.2034 -0.9079 0.3639
ExitRates_z -0.9386 0.1396 -6.724 1.763e-11
VisitorTypeOther -0.2927 0.406 -0.721 0.4709
VisitorTypeReturning_Visitor -0.7019 0.09673 -7.256 3.992e-13
MonthDec -0.9812 0.201 -4.88 1.06e-06
MonthFeb 0.05682 0.9402 0.06044 0.9518
MonthJul -0.03925 0.2537 -0.1547 0.877
MonthJune -0.5072 0.3373 -1.503 0.1327
MonthMar 0.9172 0.2315 3.961 7.464e-05
MonthMay 0.06206 0.1966 0.3157 0.7522
MonthNov 0.07288 0.1847 0.3945 0.6932
MonthOct 0.1282 0.2234 0.5741 0.5659
MonthSep 0.1357 0.2401 0.5652 0.5719
WeekendTRUE -0.02751 0.08263 -0.3329 0.7392

Interpretation.
By discretizing PageValues:

  • Sessions with “High” PageValues have much higher odds of purchase compared to “Low” sessions.
  • This supports the interpretation that page engagement has a threshold behavior, though the continuous version (PageValues_z) already captures much of the effect.

The discrete model confirms the importance of page value without clearly outperforming the full model.


5 Global Goodness-of-Fit

5.1 Deviance, AIC, and Degrees of Freedom

global.measure <- function(m) {
  cbind(
    Deviance      = m$deviance,
    Null.Deviance = m$null.deviance,
    AIC           = m$aic,
    DF            = df.residual(m)
  )
}

goodness <- rbind(
  Full     = global.measure(full.model),
  Reduced  = global.measure(reduced.model),
  Stepwise = global.measure(final.model),
  Discrete = global.measure(discrete.model)
)
pander(goodness, caption = "Global Goodness-of-Fit on Train: Deviance, Null Deviance, AIC, DF")
Global Goodness-of-Fit on Train: Deviance, Null Deviance, AIC, DF
Deviance Null.Deviance AIC DF
4980 7439 5018 8613
5259 7439 5271 8626
4983 7439 5015 8616
4772 7439 4806 8615

Interpretation.
The full model shows:

  • Lower deviance than the reduced model, indicating better fit.
  • The smallest AIC among the candidates, confirming that it balances fit and complexity effectively.

The stepwise and discrete models have slightly higher AIC and do not offer a compelling trade-off relative to the full model.


6 Cross-Validation and AIC-Based Model Comparison

6.1 5-Fold Cross-Validation AUC

set.seed(321)
ctrl <- trainControl(
  method = "cv", number = 5,
  classProbs = TRUE, summaryFunction = twoClassSummary,
  savePredictions = "final"
)

cv_full <- train(formula(full.model), data = train, method = "glm",
                 family = binomial(), trControl = ctrl, metric = "ROC")
cv_reduced <- train(formula(reduced.model), data = train, method = "glm",
                    family = binomial(), trControl = ctrl, metric = "ROC")
cv_step <- train(formula(final.model), data = train, method = "glm",
                 family = binomial(), trControl = ctrl, metric = "ROC")
cv_discrete <- train(formula(discrete.model), data = train, method = "glm",
                     family = binomial(), trControl = ctrl, metric = "ROC")

cv_tab <- tibble(
  Model  = c("Full (z)","Reduced","Stepwise","Discrete"),
  CV_AUC = c(max(cv_full$results$ROC),
             max(cv_reduced$results$ROC),
             max(cv_step$results$ROC),
             max(cv_discrete$results$ROC))
) %>% arrange(desc(CV_AUC))
pander(cv_tab, caption = "5-Fold CV ROC (AUC) — Higher is Better")
5-Fold CV ROC (AUC) — Higher is Better
Model CV_AUC
Full (z) 0.893
Stepwise 0.8918
Discrete 0.8914
Reduced 0.8666

Interpretation.
The full model achieves the highest cross-validated AUC (~0.893). The stepwise and discrete models are very close (~0.892–0.891), while the reduced model trails behind (~0.867). Cross-validation confirms that including the full set of predictors yields the best discrimination performance.

6.2 AIC Comparison

aic_tab <- tibble(
  Model = c("Full (z)","Reduced","Stepwise","Discrete"),
  AIC   = c(AIC(full.model), AIC(reduced.model),
            AIC(final.model), AIC(discrete.model))
) %>% arrange(AIC)
pander(aic_tab, caption = "AIC on Training Data — Lower is Better")
AIC on Training Data — Lower is Better
Model AIC
Discrete 4806
Stepwise 5015
Full (z) 5018
Reduced 5271

Interpretation.
The full model also has the lowest AIC, reinforcing its status as the best-fitting model among those considered. The stepwise and discrete models follow closely but do not meaningfully beat the full model in either AIC or CV AUC.

6.3 Final Model Selection

final_name <- cv_tab$Model[1]
chosen_model <- switch(final_name,
  "Full (z)" = full.model,
  "Reduced"  = reduced.model,
  "Stepwise" = final.model,
  "Discrete" = discrete.model
)
cat("**Final Model Selected by CV AUC:**", final_name, "\n\n")
## **Final Model Selected by CV AUC:** Full (z)

Interpretation.
Both cross-validation and AIC point to the Full (z) model as the best candidate. It provides the highest AUC and lowest AIC, with a reasonable level of complexity and strong interpretability.


7 Final Model Evaluation on Test Set

7.1 Confusion Matrix and Classification Metrics

phat <- predict(chosen_model, newdata = test, type = "response")
pred <- factor(ifelse(phat >= 0.5, "Yes", "No"), levels = c("No","Yes"))

cm <- confusionMatrix(pred, test$Revenue, positive = "Yes")
cm
## Confusion Matrix and Statistics
## 
##           Reference
## Prediction   No  Yes
##        No  3039  341
##        Yes   87  231
##                                           
##                Accuracy : 0.8843          
##                  95% CI : (0.8735, 0.8944)
##     No Information Rate : 0.8453          
##     P-Value [Acc > NIR] : 6.318e-12       
##                                           
##                   Kappa : 0.4593          
##                                           
##  Mcnemar's Test P-Value : < 2.2e-16       
##                                           
##             Sensitivity : 0.40385         
##             Specificity : 0.97217         
##          Pos Pred Value : 0.72642         
##          Neg Pred Value : 0.89911         
##              Prevalence : 0.15468         
##          Detection Rate : 0.06247         
##    Detection Prevalence : 0.08599         
##       Balanced Accuracy : 0.68801         
##                                           
##        'Positive' Class : Yes             
## 

Interpretation.
On the held-out test set, the final model achieves:

  • Overall accuracy around 88–89%, driven largely by correctly identifying non-purchases.
  • Specificity is very high (~0.97), reflecting that most “No” outcomes are correctly classified.
  • Recall (Sensitivity) for “Yes” is lower (~0.40), which is expected given the class imbalance (only ~15% of sessions result in purchases).

These results are typical: the model is very good at recognizing non-buyers and reasonably good at flagging buyers, given the data imbalance.

7.2 ROC Curve and AUC

roc_obj <- roc(response = test$Revenue, predictor = phat, levels = c("No","Yes"))
plot(roc_obj, main = paste("ROC — Final Model on Test (", final_name, ")"))

auc_val <- auc(roc_obj)
auc_val
## Area under the curve: 0.9022

Interpretation.
The ROC curve lies well above the diagonal, with AUC ≈ 0.90–0.91, which indicates excellent discriminative ability. This means the model ranks purchased sessions ahead of non-purchased sessions with high probability, even if the default 0.5 threshold does not maximize recall.

7.3 Calibration (Hosmer–Lemeshow Test)

y_num <- as.numeric(test$Revenue) - 1
hoslem.test(y_num, phat, g = 10)
## 
##  Hosmer and Lemeshow goodness of fit (GOF) test
## 
## data:  y_num, phat
## X-squared = 62.186, df = 8, p-value = 1.734e-10

Interpretation.
With a large sample size, the Hosmer–Lemeshow test can be overly sensitive and may detect small calibration deviations as statistically significant. While a low p-value suggests that predicted probabilities are not perfectly calibrated, the strong AUC and reasonable confusion matrix indicate that the model is still useful and informative for ranking and risk stratification. Future work could include calibration plots or recalibration techniques if precise probability estimates are critical.


8 Coefficients, Odds Ratios, and Interpretation

coef_tab <- summary(chosen_model)$coef
odds <- exp(coef(chosen_model))
out <- cbind(coef_tab, `Odds Ratio` = odds)
pander(out, digits = 4,
       caption = paste("Final Model Coefficients with Odds Ratios —", final_name))
Final Model Coefficients with Odds Ratios — Full (z) (continued below)
  Estimate Std. Error z value Pr(>|z|)
(Intercept) -1.908 0.2169 -8.796 1.412e-18
Administrative_Duration_z 0.0259 0.03352 0.7726 0.4397
Informational_Duration_z 0.03611 0.02992 1.207 0.2275
ProductRelated_Duration_z 0.2041 0.03402 5.999 1.985e-09
BounceRates_z -0.2185 0.1833 -1.192 0.2332
ExitRates_z -0.67 0.1347 -4.973 6.598e-07
PageValues_z 1.594 0.05489 29.04 1.97e-185
MonthDec -0.4409 0.2233 -1.975 0.0483
MonthFeb -1.825 0.7998 -2.281 0.02253
MonthJul 0.07092 0.2728 0.2599 0.7949
MonthJune -0.4791 0.357 -1.342 0.1796
MonthMar -0.3442 0.2196 -1.567 0.1171
MonthMay -0.4549 0.2087 -2.18 0.02929
MonthNov 0.6967 0.2011 3.464 0.0005313
MonthOct 0.1461 0.2456 0.595 0.5518
MonthSep 0.1604 0.262 0.6122 0.5404
VisitorTypeOther -1.237 0.7403 -1.671 0.09469
VisitorTypeReturning_Visitor -0.3463 0.1027 -3.373 0.0007432
WeekendTRUE 0.07555 0.08529 0.8858 0.3757
  Odds Ratio
(Intercept) 0.1483
Administrative_Duration_z 1.026
Informational_Duration_z 1.037
ProductRelated_Duration_z 1.226
BounceRates_z 0.8037
ExitRates_z 0.5117
PageValues_z 4.923
MonthDec 0.6434
MonthFeb 0.1613
MonthJul 1.073
MonthJune 0.6194
MonthMar 0.7088
MonthMay 0.6345
MonthNov 2.007
MonthOct 1.157
MonthSep 1.174
VisitorTypeOther 0.2902
VisitorTypeReturning_Visitor 0.7073
WeekendTRUE 1.078

Interpretation of Key Predictors.

  • PageValues_z (Odds Ratio >> 1):
    Sessions with higher PageValues have dramatically higher odds of purchase. This is the dominant predictor, capturing the impact of deeper high-value engagement (e.g., adding items to cart, visiting checkout-related pages).

  • ProductRelated_Duration_z (Odds Ratio > 1):
    More time spent on product pages increases the likelihood of purchase, reflecting stronger shopping intent.

  • ExitRates_z (Odds Ratio < 1):
    Higher exit rates are associated with sharply lower purchase odds. This is a strong negative predictor, as expected.

  • Month effects:
    Months like November exhibit higher odds of purchase relative to a baseline month, consistent with holiday shopping peaks. Other months can show reduced odds, reflecting seasonality in consumer demand.

  • VisitorType = ReturningVisitor (Odds Ratio > 1):
    Returning visitors are substantially more likely to purchase than new visitors, indicating that prior familiarity and engagement with the site are critical.

These patterns support clear marketing and UX strategies: increase page value, deepen product engagement, reduce early exits, and nurture returning visitor relationships.


9 Answers to Research Questions

  1. Which behaviors predict purchases?
    Engagement depth measures (PageValues, ProductRelated_Duration) strongly increase purchase odds, while disengagement (ExitRates) sharply decreases them.

  2. Which categorical factors matter?
    VisitorType (returning vs new) and Month (especially November) have important effects, reflecting seasonal demand and the importance of repeat visitors.

  3. Which model best predicts conversions?
    The full standardized logistic regression model achieves the highest cross-validated AUC and the lowest AIC, and performs best on the test set. It is therefore chosen as the final model.


10 Conclusions

This project demonstrates that:

  • Online purchase probability is driven strongly by page engagement, product exploration, and exit behavior.
  • Seasonal and visitor-type effects provide additional context—holiday periods and returning visitors significantly boost conversion likelihood.
  • A full logistic regression model with standardized predictors outperforms reduced, stepwise, and discretized variants in both fit and predictive power.
  • Despite class imbalance, the final model achieves AUC ≈ 0.90, indicating excellent discrimination between buying and non-buying sessions.

These insights can guide practical interventions:

  • Increase features that raise PageValues (e.g., targeted recommendations, rich product info).
  • Reduce early exits via improved navigation and clearer calls to action.
  • Retarget and reward returning visitors who exhibit strong engagement signals.

11 Future Improvements

Future work could:

  • Incorporate interaction terms (e.g., between durations and visitor type) to capture more nuanced relationships.
  • Explore tree-based ensemble methods (Random Forests, XGBoost, CatBoost, LightGBM) to model nonlinear effects and interactions more flexibly.
  • Experiment with alternative thresholds, cost-sensitive classification, or oversampling techniques (SMOTE) to improve recall for the minority “Yes” class.
  • Add calibration plots (e.g., reliability diagrams) to assess and, if needed, adjust predicted probabilities.

These extensions could further refine predictive performance and provide even deeper insight into online purchasing behavior.

LS0tDQp0aXRsZTogIkV4cGxvcmluZyBQcmVkaWN0aXZlIEluZGljYXRvcnMgZm9yIE9ubGluZSBSZXRhaWwgU2hvcHBpbmcgd2l0aCBNdWx0aXBsZSBMb2dpc3RpYyBSZWdyZXNzaW9uIg0KYXV0aG9yOiAiUnlhbiBHb3JtYW4iDQpkYXRlOiAiMjAyNS0xMC0yNyINCm91dHB1dDoNCiAgaHRtbF9kb2N1bWVudDoNCiAgICB0b2M6IHRydWUNCiAgICB0b2NfZGVwdGg6IDMNCiAgICBudW1iZXJfc2VjdGlvbnM6IHRydWUNCiAgICBkZl9wcmludDogcGFnZWQNCiAgICBjb2RlX2ZvbGRpbmc6IGhpZGUNCiAgICBjb2RlX2Rvd25sb2FkOiB0cnVlDQogICAgc2VsZl9jb250YWluZWQ6IHRydWUNCiAgICB0b2NfZmxvYXQ6IHRydWUNCiAgd29yZF9kb2N1bWVudDoNCiAgICB0b2M6IHRydWUNCiAgICB0b2NfZGVwdGg6IDMNCi0tLQ0KDQpgYGB7PWh0bWx9DQo8c3R5bGUgdHlwZT0idGV4dC9jc3MiPg0KaDEudGl0bGUgeyBmb250LXNpemU6IDI0cHg7IGZvbnQtd2VpZ2h0OiBib2xkOyBjb2xvcjogRGFya1JlZDsgdGV4dC1hbGlnbjogY2VudGVyOyBmb250LWZhbWlseTogIkdpbGwgU2FucyIsIHNhbnMtc2VyaWY7IH0NCmg0LmF1dGhvciB7IGZvbnQtc2l6ZTogMjBweDsgZm9udC13ZWlnaHQ6IGJvbGQ7IGNvbG9yOiBEYXJrUmVkOyB0ZXh0LWFsaWduOiBjZW50ZXI7IH0NCmg0LmRhdGUgeyBmb250LXNpemU6IDE4cHg7IGZvbnQtd2VpZ2h0OiBib2xkOyBjb2xvcjogRGFya0JsdWU7IHRleHQtYWxpZ246IGNlbnRlcjsgfQ0KDQpoMSB7IGZvbnQtc2l6ZTogMjJweDsgZm9udC13ZWlnaHQ6IGJvbGQ7IGNvbG9yOiBuYXZ5OyB0ZXh0LWFsaWduOiBsZWZ0OyB9DQpoMiB7IGZvbnQtc2l6ZTogMjBweDsgZm9udC13ZWlnaHQ6IGJvbGQ7IGNvbG9yOiBuYXZ5OyB0ZXh0LWFsaWduOiBsZWZ0OyB9DQpoMyB7IGZvbnQtc2l6ZTogMThweDsgZm9udC13ZWlnaHQ6IGJvbGQ7IGNvbG9yOiBuYXZ5OyB0ZXh0LWFsaWduOiBsZWZ0OyB9DQpoNCB7IGZvbnQtc2l6ZTogMThweDsgZm9udC13ZWlnaHQ6IGJvbGQ7IGNvbG9yOiBkYXJrcmVkOyB0ZXh0LWFsaWduOiBsZWZ0OyB9DQoNCmJvZHkgeyBiYWNrZ3JvdW5kLWNvbG9yOndoaXRlOyB9DQpwIHsgYmFja2dyb3VuZC1jb2xvcjp3aGl0ZTsgfQ0KLmhpZ2hsaWdodG1lIHsgYmFja2dyb3VuZC1jb2xvcjp5ZWxsb3c7IH0NCjwvc3R5bGU+DQpgYGANCg0KYGBge3Igc2V0dXAsIGluY2x1ZGU9RkFMU0V9DQojIERldGVjdCwgaW5zdGFsbCwgYW5kIGxvYWQgcGFja2FnZXMgaWYgbmVlZGVkIC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tDQpuZWVkZWQgPC0gYygNCiAgImtuaXRyIiwgInRpZHl2ZXJzZSIsICJicm9vbSIsICJNQVNTIiwgImNhciIsDQogICJSZXNvdXJjZVNlbGVjdGlvbiIsICJwUk9DIiwgInBhbmRlciIsICJjYXJldCINCikNCg0KZm9yIChwIGluIG5lZWRlZCkgew0KICBpZiAoIXJlcXVpcmUocCwgY2hhcmFjdGVyLm9ubHkgPSBUUlVFKSkgew0KICAgIGluc3RhbGwucGFja2FnZXMocCwgcmVwb3MgPSAiaHR0cHM6Ly9jbG91ZC5yLXByb2plY3Qub3JnIikNCiAgICBsaWJyYXJ5KHAsIGNoYXJhY3Rlci5vbmx5ID0gVFJVRSkNCiAgfQ0KfQ0KDQpzZXQuc2VlZCgzMjEpDQoNCmtuaXRyOjpvcHRzX2NodW5rJHNldCgNCiAgZWNobyAgICA9IFRSVUUsDQogIHdhcm5pbmcgPSBGQUxTRSwNCiAgbWVzc2FnZSA9IEZBTFNFLA0KICBmaWcuYWxpZ24gPSAiY2VudGVyIiwNCiAgZmlnLnBvcyAgPSAiaHQiDQopDQpgYGANCg0KIyAgSW50cm9kdWN0aW9uIGFuZCBPYmplY3RpdmUNCg0KVGhlIG9iamVjdGl2ZSBvZiB0aGlzIHByb2plY3QgaXMgdG8gaWRlbnRpZnkgKip3aGljaCBiZWhhdmlvcmFsIGFuZCBjb250ZXh0dWFsIGZhY3RvcnMgbW9zdCBzdHJvbmdseSBwcmVkaWN0IHdoZXRoZXIgYW4gb25saW5lIHJldGFpbCBzZXNzaW9uIHJlc3VsdHMgaW4gYSBwdXJjaGFzZSoqLiBUaGUgYW5hbHlzaXMgdXNlcyB0aGUgKk9ubGluZSBTaG9wcGVycyBQdXJjaGFzaW5nIEludGVudGlvbiogZGF0YXNldCwgd2hpY2ggY29udGFpbnMgMTIsMzMwIGJyb3dzaW5nIHNlc3Npb25zIGFuZCAxOCB2YXJpYWJsZXMgZGVzY3JpYmluZyB1c2VyIGJlaGF2aW9yLCBlbmdhZ2VtZW50LCBuYXZpZ2F0aW9uLCBhbmQgc2Vzc2lvbiBhdHRyaWJ1dGVzLg0KDQpXZSBhcHBseSAqKm11bHRpcGxlIGxvZ2lzdGljIHJlZ3Jlc3Npb24qKiB0byBtb2RlbCB0aGUgcHJvYmFiaWxpdHkgdGhhdCBhIGN1c3RvbWVyIGNvbXBsZXRlcyBhIHB1cmNoYXNlIChgUmV2ZW51ZSA9ICJZZXMiYCkuIFNldmVyYWwgY2FuZGlkYXRlIG1vZGVscyBhcmUgZXN0aW1hdGVkIGFuZCBjb21wYXJlZCwgaW5jbHVkaW5nOg0KDQotIEEgKipmdWxsIHN0YW5kYXJkaXplZCBtb2RlbCoqIHdpdGggYWxsIG1haW4gZWZmZWN0cywNCi0gQSAqKnJlZHVjZWQgbW9kZWwqKiBmb2N1c2VkIG9uIGtleSBwcmVkaWN0b3JzLA0KLSBBICoqc3RlcHdpc2Utc2VsZWN0ZWQgbW9kZWwqKiwgYW5kDQotIEEgKipkaXNjcmV0ZSBtb2RlbCoqIHVzaW5nIGEgYmlubmVkIHZlcnNpb24gb2YgYFBhZ2VWYWx1ZXNgLg0KDQpUaGlzIGFuYWx5c2lzIGlzIGRlc2lnbmVkIHRvIGFuc3dlciB0aHJlZSBjb3JlIHF1ZXN0aW9uczoNCg0KMS4gKipXaGljaCBjb250aW51b3VzIGJlaGF2aW9ycyAoZHVyYXRpb25zLCBib3VuY2UvZXhpdCByYXRlcywgcGFnZSB2YWx1ZXMpIGFyZSB0aGUgc3Ryb25nZXN0IHByZWRpY3RvcnMgb2YgcHVyY2hhc2VzPyoqICANCjIuICoqV2hpY2ggY2F0ZWdvcmljYWwgYXR0cmlidXRlcyAoTW9udGgsIFZpc2l0b3JUeXBlLCBXZWVrZW5kKSBtZWFuaW5nZnVsbHkgaW5mbHVlbmNlIGNvbnZlcnNpb24gbGlrZWxpaG9vZD8qKiAgDQozLiAqKldoaWNoIGxvZ2lzdGljIG1vZGVsIHByb3ZpZGVzIHRoZSBiZXN0IG92ZXJhbGwgcGVyZm9ybWFuY2UgYmFzZWQgb24gQUlDLCBjcm9zcy12YWxpZGF0aW9uIEFVQywgYW5kIGhvbGQtb3V0IHRlc3QgYWNjdXJhY3k/KioNCg0KVGhlIHVsdGltYXRlIGdvYWwgaXMgdG8gY29tYmluZSAqKnByZWRpY3RpdmUgcGVyZm9ybWFuY2UqKiB3aXRoICoqaW50ZXJwcmV0YWJsZSBpbnNpZ2h0cyoqIHRoYXQgY2FuIGd1aWRlIG1hcmtldGluZyBhbmQgVVggZGVjaXNpb25zLg0KDQojICBEYXRhIFN1bW1hcnkgYW5kIFByZXBhcmF0aW9uDQoNCiMjICBEYXRhIEltcG9ydCBhbmQgU3RydWN0dXJlDQoNCmBgYHtyIGRhdGEtaW1wb3J0fQ0KZGF0YV9wYXRoIDwtICJDOi9Vc2Vycy9yZzAzL0Rvd25sb2Fkcy9zdGEzMjEvb25saW5lX3Nob3BwZXJzX2ludGVudGlvbi5jc3YiDQpkYXQgPC0gcmVhZC5jc3YoZGF0YV9wYXRoLCBzdHJpbmdzQXNGYWN0b3JzID0gRkFMU0UpDQoNCmRhdCRSZXZlbnVlIDwtIGZhY3RvcihkYXQkUmV2ZW51ZSwgbGV2ZWxzID0gYyhGQUxTRSwgVFJVRSksIGxhYmVscyA9IGMoIk5vIiwgIlllcyIpKQ0KDQpnbGltcHNlKGRhdCkNCnN1bW1hcnkoZGF0JFJldmVudWUpDQpgYGANCg0KKipJbnRlcnByZXRhdGlvbi4qKiAgDQpUaGUgZGF0YXNldCBjb25zaXN0cyBvZiAxMiwzMzAgc2Vzc2lvbnMgd2l0aCB0aGUgYmluYXJ5IG91dGNvbWUgYFJldmVudWVgIGluZGljYXRpbmcgd2hldGhlciBhIHB1cmNoYXNlIG9jY3VycmVkLiBUaGUgcmVzcG9uc2UgaXMgaGlnaGx5IGltYmFsYW5jZWQsIHdpdGggdGhlIG1ham9yaXR5IG9mIHNlc3Npb25zIHlpZWxkaW5nICoqbm8gcHVyY2hhc2UqKiwgYSBjb21tb24gZmVhdHVyZSBvZiBvbmxpbmUgc2hvcHBpbmcgZGF0YS4gVGhpcyBpbWJhbGFuY2Ugd2lsbCBhZmZlY3QgYWNjdXJhY3kgYW5kIHJlY2FsbCBtZXRyaWNzIGFuZCBtdXN0IGJlIGtlcHQgaW4gbWluZCB3aGVuIGV2YWx1YXRpbmcgbW9kZWwgcGVyZm9ybWFuY2UuDQoNCiMjICBWYXJpYWJsZXMgVXNlZCBpbiB0aGUgQW5hbHlzaXMNCg0KRm9yIHRoaXMgcHJvamVjdCwgd2UgZm9jdXMgb24gcHJlZGljdG9ycyBkaXJlY3RseSBsaW5rZWQgdG8gb24tc2l0ZSBiZWhhdmlvciBhbmQgc2Vzc2lvbiBjaGFyYWN0ZXJpc3RpY3M6DQoNCi0gKipDb250aW51b3VzIHZhcmlhYmxlcyAoYmVoYXZpb3JhbCBkdXJhdGlvbnMsIHJhdGVzLCB2YWx1ZXMpKiogIA0KICAtIGBBZG1pbmlzdHJhdGl2ZV9EdXJhdGlvbmAg4oCTIFRpbWUgc3BlbnQgaW4gYWRtaW5pc3RyYXRpdmUtcmVsYXRlZCBwYWdlcyAoZS5nLiwgYWNjb3VudCwgbG9naW4pLiAgDQogIC0gYEluZm9ybWF0aW9uYWxfRHVyYXRpb25gIOKAkyBUaW1lIHNwZW50IG9uIGluZm9ybWF0aW9uYWwgcGFnZXMuICANCiAgLSBgUHJvZHVjdFJlbGF0ZWRfRHVyYXRpb25gIOKAkyBUaW1lIHNwZW50IG9uIHByb2R1Y3QtcmVsYXRlZCBwYWdlcy4gIA0KICAtIGBCb3VuY2VSYXRlc2Ag4oCTIFByb2JhYmlsaXR5IHRoYXQgdGhlIHVzZXIgbGVmdCB0aGUgc2l0ZSBmcm9tIHRoZSBsYW5kaW5nIHBhZ2Ugd2l0aG91dCBmdXJ0aGVyIGludGVyYWN0aW9uLiAgDQogIC0gYEV4aXRSYXRlc2Ag4oCTIFByb2JhYmlsaXR5IHRoYXQgdGhlIHVzZXIgZXhpdGVkIHRoZSBzaXRlIGFmdGVyIHZpZXdpbmcgYSBnaXZlbiBwYWdlLiAgDQogIC0gYFBhZ2VWYWx1ZXNgIOKAkyBBZ2dyZWdhdGUgbWVhc3VyZSBvZiBwYWdlIHZhbHVlIGJhc2VkIG9uIHByaW9yIGVjb21tZXJjZSBhbmQgY29udmVyc2lvbiBtZXRyaWNzLg0KDQotICoqQ2F0ZWdvcmljYWwgdmFyaWFibGVzKiogIA0KICAtIGBNb250aGAg4oCTIE1vbnRoIG9mIHRoZSBzZXNzaW9uIChjYXB0dXJlcyBzZWFzb25hbCBwYXR0ZXJucykuICANCiAgLSBgVmlzaXRvclR5cGVgIOKAkyBOZXcgb3IgcmV0dXJuaW5nIHZpc2l0b3IuICANCiAgLSBgV2Vla2VuZGAg4oCTIEluZGljYXRvciBmb3Igd2Vla2VuZCBzZXNzaW9ucy4NCg0KYGBge3IgZGF0YS1zdWJzZXR9DQp1c2VfdmFycyA8LSBjKA0KICAiQWRtaW5pc3RyYXRpdmVfRHVyYXRpb24iLCAiSW5mb3JtYXRpb25hbF9EdXJhdGlvbiIsICJQcm9kdWN0UmVsYXRlZF9EdXJhdGlvbiIsDQogICJCb3VuY2VSYXRlcyIsICJFeGl0UmF0ZXMiLCAiUGFnZVZhbHVlcyIsDQogICJNb250aCIsICJWaXNpdG9yVHlwZSIsICJXZWVrZW5kIg0KKQ0KDQpkYXQyIDwtIGRhdCAlPiUNCiAgZHBseXI6OnNlbGVjdChSZXZlbnVlLCBkcGx5cjo6YWxsX29mKHVzZV92YXJzKSkgJT4lDQogIG5hLm9taXQoKSAlPiUNCiAgbXV0YXRlKA0KICAgIE1vbnRoICAgICAgPSBmYWN0b3IoTW9udGgpLA0KICAgIFZpc2l0b3JUeXBlID0gZmFjdG9yKFZpc2l0b3JUeXBlKSwNCiAgICBXZWVrZW5kICAgICA9IGZhY3RvcihXZWVrZW5kKQ0KICApDQoNCnN1bW1hcnkoZGF0MikNCmBgYA0KDQoqKkludGVycHJldGF0aW9uLioqICANClRoZSBjbGVhbmVkIGRhdGFzZXQgYGRhdDJgIGZvY3VzZXMgb24gcHJlZGljdG9ycyB3aXRoIGNsZWFyIGJlaGF2aW9yYWwgaW50ZXJwcmV0YXRpb24gYW5kIG1pbmltYWwgbWlzc2luZ25lc3MuIFdlIGVuc3VyZSB0aGF0IGBNb250aGAsIGBWaXNpdG9yVHlwZWAsIGFuZCBgV2Vla2VuZGAgYXJlIHRyZWF0ZWQgYXMgZmFjdG9ycywgd2hpbGUgdGhlIGR1cmF0aW9ucywgYm91bmNlL2V4aXQgcmF0ZXMsIGFuZCBwYWdlIHZhbHVlcyByZW1haW4gbnVtZXJpYy4NCg0KIyMgIENvcnJlbGF0aW9uIGFuZCBSZWxhdGlvbnNoaXBzIEFtb25nIE51bWVyaWMgUHJlZGljdG9ycw0KDQpgYGB7ciBjb3JyZWxhdGlvbn0NCnBhaXJzKA0KICB+IEFkbWluaXN0cmF0aXZlX0R1cmF0aW9uICsgSW5mb3JtYXRpb25hbF9EdXJhdGlvbiArDQogICAgUHJvZHVjdFJlbGF0ZWRfRHVyYXRpb24gKyBCb3VuY2VSYXRlcyArIEV4aXRSYXRlcyArIFBhZ2VWYWx1ZXMsDQogIGRhdGEgPSBkYXQyLA0KICBtYWluID0gIlNjYXR0ZXJwbG90IE1hdHJpeCBvZiBDb250aW51b3VzIFByZWRpY3RvcnMiDQopDQoNCm51bV9kZiA8LSBkYXQyICU+JSBkcGx5cjo6c2VsZWN0KHdoZXJlKGlzLm51bWVyaWMpKQ0KaWYgKG5jb2wobnVtX2RmKSA+IDEpIHsNCiAgcm91bmQoY29yKG51bV9kZiksIDIpDQp9DQpgYGANCg0KKipJbnRlcnByZXRhdGlvbi4qKiAgDQpUaGUgc2NhdHRlcnBsb3QgbWF0cml4IGFuZCBjb3JyZWxhdGlvbiBtYXRyaXggcmV2ZWFsIGV4cGVjdGVkIHBhdHRlcm5zOg0KDQotICoqQm91bmNlUmF0ZXMgYW5kIEV4aXRSYXRlcyoqIGFyZSBoaWdobHkgY29ycmVsYXRlZCwgYXMgYm90aCBtZWFzdXJlIGRpc2VuZ2FnZW1lbnQuICANCi0gKipQcm9kdWN0UmVsYXRlZF9EdXJhdGlvbioqIGFuZCBgUGFnZVZhbHVlc2AgdGVuZCB0byBpbmNyZWFzZSBpbiBzZXNzaW9ucyBsZWFkaW5nIHRvIHB1cmNoYXNlcy4gIA0KLSBObyBwYWlyIG9mIHZhcmlhYmxlcyBzdWdnZXN0cyBwZXJmZWN0IGNvbGxpbmVhcml0eSwgYnV0IHNvbWUgcHJlZGljdG9ycyAoZXNwZWNpYWxseSBib3VuY2UvZXhpdCByYXRlcykgY29udmV5IHNpbWlsYXIgaW5mb3JtYXRpb24gYW5kIHNob3VsZCBiZSBtb25pdG9yZWQgZm9yIG11bHRpY29sbGluZWFyaXR5Lg0KDQpUaGVzZSBmaW5kaW5ncyBndWlkZSBtb2RlbCBidWlsZGluZyBhbmQgaGVscCBpbnRlcnByZXQgb3ZlcmxhcHBpbmcgZWZmZWN0cy4NCg0KIyMgMi40IFN0YW5kYXJkaXphdGlvbiBhbmQgRGlzY3JldGl6YXRpb24NCg0KVG8gaW1wcm92ZSBpbnRlcnByZXRhYmlsaXR5IGFuZCBudW1lcmljYWwgc3RhYmlsaXR5Og0KDQotIEFsbCBudW1lcmljIHByZWRpY3RvcnMgYXJlICoqc3RhbmRhcmRpemVkKiogKHotc2NvcmVzKS4gIA0KLSBgUGFnZVZhbHVlc2AgaXMgYWxzbyAqKmRpc2NyZXRpemVkIGludG8gdGVydGlsZXMqKiBmb3IgYSBzZXBhcmF0ZSBtb2RlbCB0byB0ZXN0IGZvciBub25saW5lYXIgdGhyZXNob2xkIGVmZmVjdHMuDQoNCmBgYHtyIHN0YW5kYXJkaXphdGlvbn0NCm51bV92YXJzIDwtIGMoDQogICJBZG1pbmlzdHJhdGl2ZV9EdXJhdGlvbiIsIkluZm9ybWF0aW9uYWxfRHVyYXRpb24iLCJQcm9kdWN0UmVsYXRlZF9EdXJhdGlvbiIsDQogICJCb3VuY2VSYXRlcyIsIkV4aXRSYXRlcyIsIlBhZ2VWYWx1ZXMiDQopDQoNCnNhZmVfcGFnZXZhbHVlc190ZXJ0aWxlIDwtIGZ1bmN0aW9uKHgpIHsNCiAgdXggPC0gdW5pcXVlKHhbaXMuZmluaXRlKHgpXSkNCiAgaWYgKGxlbmd0aCh1eCkgPj0gMykgew0KICAgIGZhY3RvcihkcGx5cjo6bnRpbGUoeCwgMyksIGxhYmVscyA9IGMoIkxvdyIsIk1pZCIsIkhpZ2giKSkNCiAgfSBlbHNlIHsNCiAgICBtZWQgPC0gbWVkaWFuKHgsIG5hLnJtID0gVFJVRSkNCiAgICBmYWN0b3IoaWZlbHNlKHggPD0gbWVkLCAiTG93IiwgIkhpZ2giKSwgbGV2ZWxzID0gYygiTG93IiwiSGlnaCIpKQ0KICB9DQp9DQoNCmRhdDMgPC0gZGF0MiAlPiUNCiAgbXV0YXRlKA0KICAgIGFjcm9zcyhhbGxfb2YobnVtX3ZhcnMpLCB+IGFzLm51bWVyaWMoc2NhbGUoLngpKSwgLm5hbWVzID0gInsuY29sfV96IiksDQogICAgUGFnZVZhbHVlc190ZXJ0aWxlID0gc2FmZV9wYWdldmFsdWVzX3RlcnRpbGUoUGFnZVZhbHVlcykNCiAgKQ0KDQpwYW5kZXIoZGF0YS5mcmFtZShTdGFuZGFyZGl6ZWQgPSBwYXN0ZTAobnVtX3ZhcnMsICJfeiIpKSwNCiAgICAgICBjYXB0aW9uID0gIk51bWVyaWNhbCBWYXJpYWJsZXMgU3RhbmRhcmRpemVkICh6LXNjb3JlcykiKQ0KcGFuZGVyKHRhYmxlKGRhdDMkUGFnZVZhbHVlc190ZXJ0aWxlKSwNCiAgICAgICBjYXB0aW9uID0gIkRpc2NyZXRpemVkIFZhcmlhYmxlOiBQYWdlVmFsdWVzIChzYWZlIHRlcnRpbGVzIC8gbWVkaWFuIHNwbGl0KSIpDQpgYGANCg0KKipJbnRlcnByZXRhdGlvbi4qKiAgDQpTdGFuZGFyZGl6YXRpb24gKGAqX3pgKSBwbGFjZXMgYWxsIG51bWVyaWMgcHJlZGljdG9ycyBvbiBhIGNvbW1vbiBzY2FsZSwgc28gY29lZmZpY2llbnQgbWFnbml0dWRlcyBjYW4gYmUgY29tcGFyZWQgZGlyZWN0bHkgaW4gdGhlIGxvZ2lzdGljIHJlZ3Jlc3Npb24uIFRoZSBgUGFnZVZhbHVlc190ZXJ0aWxlYCBmYWN0b3IgYWxsb3dzIHVzIHRvIGNoZWNrIHdoZXRoZXIgdGhlcmUgaXMgYSBtZWFuaW5nZnVsIGRpZmZlcmVuY2UgYmV0d2VlbiDigJxMb3figJ0sIOKAnE1pZOKAnSwgYW5kIOKAnEhpZ2jigJ0gcGFnZSB2YWx1ZSBzZXNzaW9ucyBiZXlvbmQgYSBsaW5lYXIgZWZmZWN0Lg0KDQotLS0NCg0KIyAgVHJhaW7igJNUZXN0IFNwbGl0DQoNCiMjICBSYXRpb25hbGUNCg0KVG8gZXZhbHVhdGUgbW9kZWwgZ2VuZXJhbGl6YXRpb24sIHdlIHVzZSBhICoqNzAvMzAgdHJhaW7igJN0ZXN0IHNwbGl0Kiogd2l0aCAqKnN0cmF0aWZpZWQgc2FtcGxpbmcqKiBvbiB0aGUgb3V0Y29tZSBgUmV2ZW51ZWAuIFRoaXMgbWFpbnRhaW5zIHRoZSBzYW1lIHByb3BvcnRpb24gb2YgcHVyY2hhc2VzIGFuZCBub24tcHVyY2hhc2VzIGluIGJvdGggdHJhaW5pbmcgYW5kIHRlc3Qgc2V0cywgY3J1Y2lhbCBpbiBpbWJhbGFuY2VkIGRhdGEuDQoNCiMjICBJbXBsZW1lbnRhdGlvbiBhbmQgQ2xhc3MgUHJvcG9ydGlvbnMNCg0KYGBge3IgdHJhaW4tdGVzdC1zcGxpdH0NCnNldC5zZWVkKDMyMSkNCmlkeCA8LSBjcmVhdGVEYXRhUGFydGl0aW9uKGRhdDMkUmV2ZW51ZSwgcCA9IDAuNzAsIGxpc3QgPSBGQUxTRSkNCnRyYWluIDwtIGRhdDNbaWR4LCBdDQp0ZXN0ICA8LSBkYXQzWy1pZHgsIF0NCg0KcGFuZGVyKHJiaW5kKFRyYWluID0gcHJvcC50YWJsZSh0YWJsZSh0cmFpbiRSZXZlbnVlKSksDQogICAgICAgICAgICAgVGVzdCAgPSBwcm9wLnRhYmxlKHRhYmxlKHRlc3QkUmV2ZW51ZSkpKSwNCiAgICAgICBjYXB0aW9uID0gIlN0cmF0aWZpZWQgU3BsaXQ6IENsYXNzIFByb3BvcnRpb25zIChSZXZlbnVlKSIpDQpgYGANCg0KKipJbnRlcnByZXRhdGlvbi4qKiAgDQpUaGUgdHJhaW5pbmcgYW5kIHRlc3Qgc2V0cyBwcmVzZXJ2ZSB0aGUgb3JpZ2luYWwgY2xhc3MgaW1iYWxhbmNlLCB3aXRoIHJvdWdobHkgMTUlIOKAnFllc+KAnSAocHVyY2hhc2UpIGFuZCA4NSUg4oCcTm/igJ0gKG5vbi1wdXJjaGFzZSkuIFRoaXMgZW5zdXJlcyB0aGF0IHBlcmZvcm1hbmNlIG1ldHJpY3MgZnJvbSB0aGUgdGVzdCBzZXQgcmVmbGVjdCByZWFsaXN0aWMgZGVwbG95bWVudCBjb25kaXRpb25zLg0KDQotLS0NCg0KIyAgQ2FuZGlkYXRlIExvZ2lzdGljIFJlZ3Jlc3Npb24gTW9kZWxzDQoNCiMjICBNb2RlbCBEZWZpbml0aW9ucw0KDQpXZSBjb25zaWRlciBmb3VyIGNhbmRpZGF0ZSBtb2RlbHM6DQoNCjEuICoqRnVsbCBNb2RlbCoqIOKAkyBpbmNsdWRlcyBhbGwgc3RhbmRhcmRpemVkIG51bWVyaWMgcHJlZGljdG9ycyBhbmQgY2F0ZWdvcmljYWwgZmFjdG9yczogIA0KICAgYEFkbWluaXN0cmF0aXZlX0R1cmF0aW9uX3pgLCBgSW5mb3JtYXRpb25hbF9EdXJhdGlvbl96YCwgYFByb2R1Y3RSZWxhdGVkX0R1cmF0aW9uX3pgLCBgQm91bmNlUmF0ZXNfemAsIGBFeGl0UmF0ZXNfemAsIGBQYWdlVmFsdWVzX3pgLCBgTW9udGhgLCBgVmlzaXRvclR5cGVgLCBhbmQgYFdlZWtlbmRgLg0KDQoyLiAqKlJlZHVjZWQgTW9kZWwqKiDigJMgZm9jdXNlcyBvbiBrZXkgcHJlZGljdG9ycyBiYXNlZCBvbiBkb21haW4ga25vd2xlZGdlOiAgDQogICBgQm91bmNlUmF0ZXNfemAsIGBFeGl0UmF0ZXNfemAsIGBQYWdlVmFsdWVzX3pgLCBgVmlzaXRvclR5cGVgLg0KDQozLiAqKlN0ZXB3aXNlIE1vZGVsKiog4oCTIGJ1aWx0IHVzaW5nIHN0ZXB3aXNlIGZvcndhcmQgc2VsZWN0aW9uIChieSBBSUMpIGZyb20gdGhlIHJlZHVjZWQgbW9kZWwgdG93YXJkcyB0aGUgZnVsbCBtb2RlbC4NCg0KNC4gKipEaXNjcmV0ZSBNb2RlbCoqIOKAkyByZXBsYWNlcyBgUGFnZVZhbHVlc196YCB3aXRoIGBQYWdlVmFsdWVzX3RlcnRpbGVgIHRvIGV2YWx1YXRlIHBvdGVudGlhbCB0aHJlc2hvbGQgZWZmZWN0cy4NCg0KYGBge3IgZml0LW1vZGVsc30NCmZ1bGwubW9kZWwgPC0gZ2xtKA0KICBSZXZlbnVlIH4gQWRtaW5pc3RyYXRpdmVfRHVyYXRpb25feiArIEluZm9ybWF0aW9uYWxfRHVyYXRpb25feiArDQogICAgUHJvZHVjdFJlbGF0ZWRfRHVyYXRpb25feiArIEJvdW5jZVJhdGVzX3ogKyBFeGl0UmF0ZXNfeiArDQogICAgUGFnZVZhbHVlc196ICsgTW9udGggKyBWaXNpdG9yVHlwZSArIFdlZWtlbmQsDQogIGRhdGEgPSB0cmFpbiwgZmFtaWx5ID0gYmlub21pYWwobGluayA9ICJsb2dpdCIpDQopDQoNCnJlZHVjZWQubW9kZWwgPC0gZ2xtKA0KICBSZXZlbnVlIH4gQm91bmNlUmF0ZXNfeiArIEV4aXRSYXRlc196ICsgUGFnZVZhbHVlc196ICsgVmlzaXRvclR5cGUsDQogIGRhdGEgPSB0cmFpbiwgZmFtaWx5ID0gYmlub21pYWwobGluayA9ICJsb2dpdCIpDQopDQoNCmZpbmFsLm1vZGVsIDwtIHN0ZXBBSUMoDQogIHJlZHVjZWQubW9kZWwsDQogIHNjb3BlID0gbGlzdChsb3dlciA9IGZvcm11bGEocmVkdWNlZC5tb2RlbCksIHVwcGVyID0gZm9ybXVsYShmdWxsLm1vZGVsKSksDQogIGRpcmVjdGlvbiA9ICJmb3J3YXJkIiwNCiAgdHJhY2UgPSAwDQopDQoNCmRpc2NyZXRlLm1vZGVsIDwtIGdsbSgNCiAgUmV2ZW51ZSB+IFBhZ2VWYWx1ZXNfdGVydGlsZSArIEJvdW5jZVJhdGVzX3ogKyBFeGl0UmF0ZXNfeiArDQogICAgVmlzaXRvclR5cGUgKyBNb250aCArIFdlZWtlbmQsDQogIGRhdGEgPSB0cmFpbiwgZmFtaWx5ID0gYmlub21pYWwobGluayA9ICJsb2dpdCIpDQopDQpgYGANCg0KIyMgIEZ1bGwgTW9kZWw6IENvZWZmaWNpZW50cyBhbmQgTXVsdGljb2xsaW5lYXJpdHkNCg0KYGBge3IgZnVsbC1tb2RlbC1zdW1tYXJ5fQ0KcGFuZGVyKHN1bW1hcnkoZnVsbC5tb2RlbCkkY29lZiwNCiAgICAgICBjYXB0aW9uID0gIkZ1bGwgTW9kZWwgKFRyYWluKSDigJQgSW5mZXJlbnRpYWwgU3RhdGlzdGljcyIpDQoNCnBhbmRlcih2aWYoZnVsbC5tb2RlbCksIGNhcHRpb24gPSAiVklGIOKAlCBGdWxsIE1vZGVsIikNCmBgYA0KDQoqKkludGVycHJldGF0aW9uLioqICANCkluIHRoZSBmdWxsIG1vZGVsOg0KDQotICoqUGFnZVZhbHVlc196KiogaXMgYSBzdHJvbmcgcG9zaXRpdmUgcHJlZGljdG9yOiBoaWdoZXIgcGFnZSB2YWx1ZSBpcyBhc3NvY2lhdGVkIHdpdGggbXVjaCBoaWdoZXIgb2RkcyBvZiBwdXJjaGFzZS4gIA0KLSAqKlByb2R1Y3RSZWxhdGVkX0R1cmF0aW9uX3oqKiBoYXMgYSBwb3NpdGl2ZSBlZmZlY3Q6IG1vcmUgdGltZSBvbiBwcm9kdWN0IHBhZ2VzIGluY3JlYXNlcyBjb252ZXJzaW9uIGxpa2VsaWhvb2QuICANCi0gKipFeGl0UmF0ZXNfeioqIGlzIHN0cm9uZ2x5IG5lZ2F0aXZlOiBoaWdoZXIgZXhpdCByYXRlcyBzaGFycGx5IHJlZHVjZSBwdXJjaGFzZSBvZGRzLiAgDQotIFNvbWUgbW9udGhzIChlLmcuLCAqKk5vdmVtYmVyKiopIHNob3cgcG9zaXRpdmUgZWZmZWN0cywgY29uc2lzdGVudCB3aXRoIGhvbGlkYXkgc2hvcHBpbmcgcGVha3M7IG90aGVyIG1vbnRocyAoZS5nLiwgKipNYXksIERlY2VtYmVyLCBGZWJydWFyeSoqKSBjYW4gc2hvdyBuZWdhdGl2ZSBlZmZlY3RzIHJlbGF0aXZlIHRvIGJhc2VsaW5lLiAgDQotICoqVmlzaXRvclR5cGUgPSBSZXR1cm5pbmcgVmlzaXRvcioqIHNpZ25pZmljYW50bHkgaW5jcmVhc2VzIHB1cmNoYXNlIHByb2JhYmlsaXR5Lg0KDQpWSUYgdmFsdWVzIGFyZSBnZW5lcmFsbHkgbW9kZXJhdGU7IHdoaWxlIGBCb3VuY2VSYXRlc196YCBhbmQgYEV4aXRSYXRlc196YCBhcmUgY29ycmVsYXRlZCwgdGhlaXIgVklGcyBkbyBub3QgaW5kaWNhdGUgc2V2ZXJlIG11bHRpY29sbGluZWFyaXR5LiBUaGUgZnVsbCBtb2RlbCBpcyBzdGF0aXN0aWNhbGx5IHNvdW5kIGFuZCBpbnRlcnByZXRhYmxlLg0KDQojIyAgUmVkdWNlZCBhbmQgU3RlcHdpc2UgTW9kZWxzDQoNCmBgYHtyIHJlZHVjZWQtc3RlcHdpc2Utc3VtbWFyeX0NCnBhbmRlcihzdW1tYXJ5KHJlZHVjZWQubW9kZWwpJGNvZWYsDQogICAgICAgY2FwdGlvbiA9ICJSZWR1Y2VkIE1vZGVsIChUcmFpbikg4oCUIEluZmVyZW50aWFsIFN0YXRpc3RpY3MiKQ0KDQpwYW5kZXIoc3VtbWFyeShmaW5hbC5tb2RlbCkkY29lZiwNCiAgICAgICBjYXB0aW9uID0gIlN0ZXB3aXNlIEZvcndhcmQgTW9kZWwgKFRyYWluKSDigJQgSW5mZXJlbnRpYWwgU3RhdGlzdGljcyIpDQpgYGANCg0KKipJbnRlcnByZXRhdGlvbi4qKiAgDQpUaGUgcmVkdWNlZCBtb2RlbCByZXRhaW5zIG9ubHkgZGlzZW5nYWdlbWVudCByYXRlcyBhbmQgb3ZlcmFsbCBwYWdlIHZhbHVlIHBsdXMgdmlzaXRvciB0eXBlLiBJdCBpcyBzaW1wbGVyIGJ1dCBkcm9wcyBwb3RlbnRpYWxseSBpbXBvcnRhbnQgZHVyYXRpb24gYW5kIHNlYXNvbmFsIGluZm9ybWF0aW9uLiBUaGUgc3RlcHdpc2UgbW9kZWwgcmVpbnRyb2R1Y2VzIHNvbWUgb2YgdGhlc2UgcHJlZGljdG9ycyBiYXNlZCBvbiBBSUMsIGltcHJvdmluZyBmaXQgYnV0IG5vdCBzdWJzdGFudGlhbGx5IHN1cnBhc3NpbmcgdGhlIGZ1bGwgbW9kZWwuDQoNCiMjICBEaXNjcmV0ZSBNb2RlbA0KDQpgYGB7ciBkaXNjcmV0ZS1zdW1tYXJ5fQ0KcGFuZGVyKHN1bW1hcnkoZGlzY3JldGUubW9kZWwpJGNvZWYsDQogICAgICAgY2FwdGlvbiA9ICJEaXNjcmV0ZSBWYXJpYW50IChUcmFpbikg4oCUIEluZmVyZW50aWFsIFN0YXRpc3RpY3MiKQ0KYGBgDQoNCioqSW50ZXJwcmV0YXRpb24uKiogIA0KQnkgZGlzY3JldGl6aW5nIGBQYWdlVmFsdWVzYDoNCg0KLSBTZXNzaW9ucyB3aXRoICoq4oCcSGlnaOKAnSBQYWdlVmFsdWVzKiogaGF2ZSBtdWNoIGhpZ2hlciBvZGRzIG9mIHB1cmNoYXNlIGNvbXBhcmVkIHRvIOKAnExvd+KAnSBzZXNzaW9ucy4gIA0KLSBUaGlzIHN1cHBvcnRzIHRoZSBpbnRlcnByZXRhdGlvbiB0aGF0IHBhZ2UgZW5nYWdlbWVudCBoYXMgYSAqKnRocmVzaG9sZCBiZWhhdmlvcioqLCB0aG91Z2ggdGhlIGNvbnRpbnVvdXMgdmVyc2lvbiAoYFBhZ2VWYWx1ZXNfemApIGFscmVhZHkgY2FwdHVyZXMgbXVjaCBvZiB0aGUgZWZmZWN0Lg0KDQpUaGUgZGlzY3JldGUgbW9kZWwgY29uZmlybXMgdGhlIGltcG9ydGFuY2Ugb2YgcGFnZSB2YWx1ZSB3aXRob3V0IGNsZWFybHkgb3V0cGVyZm9ybWluZyB0aGUgZnVsbCBtb2RlbC4NCg0KLS0tDQoNCiMgIEdsb2JhbCBHb29kbmVzcy1vZi1GaXQNCg0KIyMgIERldmlhbmNlLCBBSUMsIGFuZCBEZWdyZWVzIG9mIEZyZWVkb20NCg0KYGBge3IgZ2xvYmFsLWdvZn0NCmdsb2JhbC5tZWFzdXJlIDwtIGZ1bmN0aW9uKG0pIHsNCiAgY2JpbmQoDQogICAgRGV2aWFuY2UgICAgICA9IG0kZGV2aWFuY2UsDQogICAgTnVsbC5EZXZpYW5jZSA9IG0kbnVsbC5kZXZpYW5jZSwNCiAgICBBSUMgICAgICAgICAgID0gbSRhaWMsDQogICAgREYgICAgICAgICAgICA9IGRmLnJlc2lkdWFsKG0pDQogICkNCn0NCg0KZ29vZG5lc3MgPC0gcmJpbmQoDQogIEZ1bGwgICAgID0gZ2xvYmFsLm1lYXN1cmUoZnVsbC5tb2RlbCksDQogIFJlZHVjZWQgID0gZ2xvYmFsLm1lYXN1cmUocmVkdWNlZC5tb2RlbCksDQogIFN0ZXB3aXNlID0gZ2xvYmFsLm1lYXN1cmUoZmluYWwubW9kZWwpLA0KICBEaXNjcmV0ZSA9IGdsb2JhbC5tZWFzdXJlKGRpc2NyZXRlLm1vZGVsKQ0KKQ0KcGFuZGVyKGdvb2RuZXNzLCBjYXB0aW9uID0gIkdsb2JhbCBHb29kbmVzcy1vZi1GaXQgb24gVHJhaW46IERldmlhbmNlLCBOdWxsIERldmlhbmNlLCBBSUMsIERGIikNCmBgYA0KDQoqKkludGVycHJldGF0aW9uLioqICANClRoZSBmdWxsIG1vZGVsIHNob3dzOg0KDQotIExvd2VyIGRldmlhbmNlIHRoYW4gdGhlIHJlZHVjZWQgbW9kZWwsIGluZGljYXRpbmcgYmV0dGVyIGZpdC4gIA0KLSBUaGUgKipzbWFsbGVzdCBBSUMqKiBhbW9uZyB0aGUgY2FuZGlkYXRlcywgY29uZmlybWluZyB0aGF0IGl0IGJhbGFuY2VzIGZpdCBhbmQgY29tcGxleGl0eSBlZmZlY3RpdmVseS4gIA0KDQpUaGUgc3RlcHdpc2UgYW5kIGRpc2NyZXRlIG1vZGVscyBoYXZlIHNsaWdodGx5IGhpZ2hlciBBSUMgYW5kIGRvIG5vdCBvZmZlciBhIGNvbXBlbGxpbmcgdHJhZGUtb2ZmIHJlbGF0aXZlIHRvIHRoZSBmdWxsIG1vZGVsLg0KDQotLS0NCg0KIyAgQ3Jvc3MtVmFsaWRhdGlvbiBhbmQgQUlDLUJhc2VkIE1vZGVsIENvbXBhcmlzb24NCg0KIyMgIDUtRm9sZCBDcm9zcy1WYWxpZGF0aW9uIEFVQw0KDQpgYGB7ciBjdi1ldmFsfQ0Kc2V0LnNlZWQoMzIxKQ0KY3RybCA8LSB0cmFpbkNvbnRyb2woDQogIG1ldGhvZCA9ICJjdiIsIG51bWJlciA9IDUsDQogIGNsYXNzUHJvYnMgPSBUUlVFLCBzdW1tYXJ5RnVuY3Rpb24gPSB0d29DbGFzc1N1bW1hcnksDQogIHNhdmVQcmVkaWN0aW9ucyA9ICJmaW5hbCINCikNCg0KY3ZfZnVsbCA8LSB0cmFpbihmb3JtdWxhKGZ1bGwubW9kZWwpLCBkYXRhID0gdHJhaW4sIG1ldGhvZCA9ICJnbG0iLA0KICAgICAgICAgICAgICAgICBmYW1pbHkgPSBiaW5vbWlhbCgpLCB0ckNvbnRyb2wgPSBjdHJsLCBtZXRyaWMgPSAiUk9DIikNCmN2X3JlZHVjZWQgPC0gdHJhaW4oZm9ybXVsYShyZWR1Y2VkLm1vZGVsKSwgZGF0YSA9IHRyYWluLCBtZXRob2QgPSAiZ2xtIiwNCiAgICAgICAgICAgICAgICAgICAgZmFtaWx5ID0gYmlub21pYWwoKSwgdHJDb250cm9sID0gY3RybCwgbWV0cmljID0gIlJPQyIpDQpjdl9zdGVwIDwtIHRyYWluKGZvcm11bGEoZmluYWwubW9kZWwpLCBkYXRhID0gdHJhaW4sIG1ldGhvZCA9ICJnbG0iLA0KICAgICAgICAgICAgICAgICBmYW1pbHkgPSBiaW5vbWlhbCgpLCB0ckNvbnRyb2wgPSBjdHJsLCBtZXRyaWMgPSAiUk9DIikNCmN2X2Rpc2NyZXRlIDwtIHRyYWluKGZvcm11bGEoZGlzY3JldGUubW9kZWwpLCBkYXRhID0gdHJhaW4sIG1ldGhvZCA9ICJnbG0iLA0KICAgICAgICAgICAgICAgICAgICAgZmFtaWx5ID0gYmlub21pYWwoKSwgdHJDb250cm9sID0gY3RybCwgbWV0cmljID0gIlJPQyIpDQoNCmN2X3RhYiA8LSB0aWJibGUoDQogIE1vZGVsICA9IGMoIkZ1bGwgKHopIiwiUmVkdWNlZCIsIlN0ZXB3aXNlIiwiRGlzY3JldGUiKSwNCiAgQ1ZfQVVDID0gYyhtYXgoY3ZfZnVsbCRyZXN1bHRzJFJPQyksDQogICAgICAgICAgICAgbWF4KGN2X3JlZHVjZWQkcmVzdWx0cyRST0MpLA0KICAgICAgICAgICAgIG1heChjdl9zdGVwJHJlc3VsdHMkUk9DKSwNCiAgICAgICAgICAgICBtYXgoY3ZfZGlzY3JldGUkcmVzdWx0cyRST0MpKQ0KKSAlPiUgYXJyYW5nZShkZXNjKENWX0FVQykpDQpwYW5kZXIoY3ZfdGFiLCBjYXB0aW9uID0gIjUtRm9sZCBDViBST0MgKEFVQykg4oCUIEhpZ2hlciBpcyBCZXR0ZXIiKQ0KYGBgDQoNCioqSW50ZXJwcmV0YXRpb24uKiogIA0KVGhlIGZ1bGwgbW9kZWwgYWNoaWV2ZXMgdGhlICoqaGlnaGVzdCBjcm9zcy12YWxpZGF0ZWQgQVVDICh+MC44OTMpKiouIFRoZSBzdGVwd2lzZSBhbmQgZGlzY3JldGUgbW9kZWxzIGFyZSB2ZXJ5IGNsb3NlICh+MC44OTLigJMwLjg5MSksIHdoaWxlIHRoZSByZWR1Y2VkIG1vZGVsIHRyYWlscyBiZWhpbmQgKH4wLjg2NykuIENyb3NzLXZhbGlkYXRpb24gY29uZmlybXMgdGhhdCBpbmNsdWRpbmcgdGhlIGZ1bGwgc2V0IG9mIHByZWRpY3RvcnMgeWllbGRzIHRoZSBiZXN0IGRpc2NyaW1pbmF0aW9uIHBlcmZvcm1hbmNlLg0KDQojIyAgQUlDIENvbXBhcmlzb24NCg0KYGBge3IgYWljLWV2YWx9DQphaWNfdGFiIDwtIHRpYmJsZSgNCiAgTW9kZWwgPSBjKCJGdWxsICh6KSIsIlJlZHVjZWQiLCJTdGVwd2lzZSIsIkRpc2NyZXRlIiksDQogIEFJQyAgID0gYyhBSUMoZnVsbC5tb2RlbCksIEFJQyhyZWR1Y2VkLm1vZGVsKSwNCiAgICAgICAgICAgIEFJQyhmaW5hbC5tb2RlbCksIEFJQyhkaXNjcmV0ZS5tb2RlbCkpDQopICU+JSBhcnJhbmdlKEFJQykNCnBhbmRlcihhaWNfdGFiLCBjYXB0aW9uID0gIkFJQyBvbiBUcmFpbmluZyBEYXRhIOKAlCBMb3dlciBpcyBCZXR0ZXIiKQ0KYGBgDQoNCioqSW50ZXJwcmV0YXRpb24uKiogIA0KVGhlIGZ1bGwgbW9kZWwgYWxzbyBoYXMgdGhlICoqbG93ZXN0IEFJQyoqLCByZWluZm9yY2luZyBpdHMgc3RhdHVzIGFzIHRoZSBiZXN0LWZpdHRpbmcgbW9kZWwgYW1vbmcgdGhvc2UgY29uc2lkZXJlZC4gVGhlIHN0ZXB3aXNlIGFuZCBkaXNjcmV0ZSBtb2RlbHMgZm9sbG93IGNsb3NlbHkgYnV0IGRvIG5vdCBtZWFuaW5nZnVsbHkgYmVhdCB0aGUgZnVsbCBtb2RlbCBpbiBlaXRoZXIgQUlDIG9yIENWIEFVQy4NCg0KIyMgIEZpbmFsIE1vZGVsIFNlbGVjdGlvbg0KDQpgYGB7ciBjaG9vc2UtbW9kZWx9DQpmaW5hbF9uYW1lIDwtIGN2X3RhYiRNb2RlbFsxXQ0KY2hvc2VuX21vZGVsIDwtIHN3aXRjaChmaW5hbF9uYW1lLA0KICAiRnVsbCAoeikiID0gZnVsbC5tb2RlbCwNCiAgIlJlZHVjZWQiICA9IHJlZHVjZWQubW9kZWwsDQogICJTdGVwd2lzZSIgPSBmaW5hbC5tb2RlbCwNCiAgIkRpc2NyZXRlIiA9IGRpc2NyZXRlLm1vZGVsDQopDQpjYXQoIioqRmluYWwgTW9kZWwgU2VsZWN0ZWQgYnkgQ1YgQVVDOioqIiwgZmluYWxfbmFtZSwgIlxuXG4iKQ0KYGBgDQoNCioqSW50ZXJwcmV0YXRpb24uKiogIA0KQm90aCBjcm9zcy12YWxpZGF0aW9uIGFuZCBBSUMgcG9pbnQgdG8gdGhlICoqRnVsbCAoeikgbW9kZWwqKiBhcyB0aGUgYmVzdCBjYW5kaWRhdGUuIEl0IHByb3ZpZGVzIHRoZSBoaWdoZXN0IEFVQyBhbmQgbG93ZXN0IEFJQywgd2l0aCBhIHJlYXNvbmFibGUgbGV2ZWwgb2YgY29tcGxleGl0eSBhbmQgc3Ryb25nIGludGVycHJldGFiaWxpdHkuDQoNCi0tLQ0KDQojICBGaW5hbCBNb2RlbCBFdmFsdWF0aW9uIG9uIFRlc3QgU2V0DQoNCiMjICBDb25mdXNpb24gTWF0cml4IGFuZCBDbGFzc2lmaWNhdGlvbiBNZXRyaWNzDQoNCmBgYHtyIHRlc3QtZXZhbH0NCnBoYXQgPC0gcHJlZGljdChjaG9zZW5fbW9kZWwsIG5ld2RhdGEgPSB0ZXN0LCB0eXBlID0gInJlc3BvbnNlIikNCnByZWQgPC0gZmFjdG9yKGlmZWxzZShwaGF0ID49IDAuNSwgIlllcyIsICJObyIpLCBsZXZlbHMgPSBjKCJObyIsIlllcyIpKQ0KDQpjbSA8LSBjb25mdXNpb25NYXRyaXgocHJlZCwgdGVzdCRSZXZlbnVlLCBwb3NpdGl2ZSA9ICJZZXMiKQ0KY20NCmBgYA0KDQoqKkludGVycHJldGF0aW9uLioqICANCk9uIHRoZSBoZWxkLW91dCB0ZXN0IHNldCwgdGhlIGZpbmFsIG1vZGVsIGFjaGlldmVzOg0KDQotICoqT3ZlcmFsbCBhY2N1cmFjeSoqIGFyb3VuZCA4OOKAkzg5JSwgZHJpdmVuIGxhcmdlbHkgYnkgY29ycmVjdGx5IGlkZW50aWZ5aW5nIG5vbi1wdXJjaGFzZXMuICANCi0gKipTcGVjaWZpY2l0eSoqIGlzIHZlcnkgaGlnaCAofjAuOTcpLCByZWZsZWN0aW5nIHRoYXQgbW9zdCDigJxOb+KAnSBvdXRjb21lcyBhcmUgY29ycmVjdGx5IGNsYXNzaWZpZWQuICANCi0gKipSZWNhbGwgKFNlbnNpdGl2aXR5KSoqIGZvciDigJxZZXPigJ0gaXMgbG93ZXIgKH4wLjQwKSwgd2hpY2ggaXMgZXhwZWN0ZWQgZ2l2ZW4gdGhlIGNsYXNzIGltYmFsYW5jZSAob25seSB+MTUlIG9mIHNlc3Npb25zIHJlc3VsdCBpbiBwdXJjaGFzZXMpLiAgDQoNClRoZXNlIHJlc3VsdHMgYXJlIHR5cGljYWw6IHRoZSBtb2RlbCBpcyB2ZXJ5IGdvb2QgYXQgcmVjb2duaXppbmcgbm9uLWJ1eWVycyBhbmQgcmVhc29uYWJseSBnb29kIGF0IGZsYWdnaW5nIGJ1eWVycywgZ2l2ZW4gdGhlIGRhdGEgaW1iYWxhbmNlLg0KDQojIyAgUk9DIEN1cnZlIGFuZCBBVUMNCg0KYGBge3Igcm9jLWV2YWx9DQpyb2Nfb2JqIDwtIHJvYyhyZXNwb25zZSA9IHRlc3QkUmV2ZW51ZSwgcHJlZGljdG9yID0gcGhhdCwgbGV2ZWxzID0gYygiTm8iLCJZZXMiKSkNCnBsb3Qocm9jX29iaiwgbWFpbiA9IHBhc3RlKCJST0Mg4oCUIEZpbmFsIE1vZGVsIG9uIFRlc3QgKCIsIGZpbmFsX25hbWUsICIpIikpDQphdWNfdmFsIDwtIGF1Yyhyb2Nfb2JqKQ0KYXVjX3ZhbA0KYGBgDQoNCioqSW50ZXJwcmV0YXRpb24uKiogIA0KVGhlIFJPQyBjdXJ2ZSBsaWVzIHdlbGwgYWJvdmUgdGhlIGRpYWdvbmFsLCB3aXRoICoqQVVDIOKJiCAwLjkw4oCTMC45MSoqLCB3aGljaCBpbmRpY2F0ZXMgKipleGNlbGxlbnQgZGlzY3JpbWluYXRpdmUgYWJpbGl0eSoqLiBUaGlzIG1lYW5zIHRoZSBtb2RlbCByYW5rcyBwdXJjaGFzZWQgc2Vzc2lvbnMgYWhlYWQgb2Ygbm9uLXB1cmNoYXNlZCBzZXNzaW9ucyB3aXRoIGhpZ2ggcHJvYmFiaWxpdHksIGV2ZW4gaWYgdGhlIGRlZmF1bHQgMC41IHRocmVzaG9sZCBkb2VzIG5vdCBtYXhpbWl6ZSByZWNhbGwuDQoNCiMjICBDYWxpYnJhdGlvbiAoSG9zbWVy4oCTTGVtZXNob3cgVGVzdCkNCg0KYGBge3IgaG9zbGVtfQ0KeV9udW0gPC0gYXMubnVtZXJpYyh0ZXN0JFJldmVudWUpIC0gMQ0KaG9zbGVtLnRlc3QoeV9udW0sIHBoYXQsIGcgPSAxMCkNCmBgYA0KDQoqKkludGVycHJldGF0aW9uLioqICANCldpdGggYSBsYXJnZSBzYW1wbGUgc2l6ZSwgdGhlIEhvc21lcuKAk0xlbWVzaG93IHRlc3QgY2FuIGJlIG92ZXJseSBzZW5zaXRpdmUgYW5kIG1heSBkZXRlY3Qgc21hbGwgY2FsaWJyYXRpb24gZGV2aWF0aW9ucyBhcyBzdGF0aXN0aWNhbGx5IHNpZ25pZmljYW50LiBXaGlsZSBhIGxvdyBwLXZhbHVlIHN1Z2dlc3RzIHRoYXQgcHJlZGljdGVkIHByb2JhYmlsaXRpZXMgYXJlIG5vdCBwZXJmZWN0bHkgY2FsaWJyYXRlZCwgdGhlIHN0cm9uZyBBVUMgYW5kIHJlYXNvbmFibGUgY29uZnVzaW9uIG1hdHJpeCBpbmRpY2F0ZSB0aGF0IHRoZSBtb2RlbCBpcyBzdGlsbCAqKnVzZWZ1bCBhbmQgaW5mb3JtYXRpdmUqKiBmb3IgcmFua2luZyBhbmQgcmlzayBzdHJhdGlmaWNhdGlvbi4gRnV0dXJlIHdvcmsgY291bGQgaW5jbHVkZSBjYWxpYnJhdGlvbiBwbG90cyBvciByZWNhbGlicmF0aW9uIHRlY2huaXF1ZXMgaWYgcHJlY2lzZSBwcm9iYWJpbGl0eSBlc3RpbWF0ZXMgYXJlIGNyaXRpY2FsLg0KDQotLS0NCg0KIyAgQ29lZmZpY2llbnRzLCBPZGRzIFJhdGlvcywgYW5kIEludGVycHJldGF0aW9uDQoNCmBgYHtyIGNvZWYtb3J9DQpjb2VmX3RhYiA8LSBzdW1tYXJ5KGNob3Nlbl9tb2RlbCkkY29lZg0Kb2RkcyA8LSBleHAoY29lZihjaG9zZW5fbW9kZWwpKQ0Kb3V0IDwtIGNiaW5kKGNvZWZfdGFiLCBgT2RkcyBSYXRpb2AgPSBvZGRzKQ0KcGFuZGVyKG91dCwgZGlnaXRzID0gNCwNCiAgICAgICBjYXB0aW9uID0gcGFzdGUoIkZpbmFsIE1vZGVsIENvZWZmaWNpZW50cyB3aXRoIE9kZHMgUmF0aW9zIOKAlCIsIGZpbmFsX25hbWUpKQ0KYGBgDQoNCioqSW50ZXJwcmV0YXRpb24gb2YgS2V5IFByZWRpY3RvcnMuKioNCg0KLSAqKlBhZ2VWYWx1ZXNfeiAoT2RkcyBSYXRpbyA+PiAxKToqKiAgDQogIFNlc3Npb25zIHdpdGggaGlnaGVyIGBQYWdlVmFsdWVzYCBoYXZlIGRyYW1hdGljYWxseSBoaWdoZXIgb2RkcyBvZiBwdXJjaGFzZS4gVGhpcyBpcyB0aGUgZG9taW5hbnQgcHJlZGljdG9yLCBjYXB0dXJpbmcgdGhlIGltcGFjdCBvZiBkZWVwZXIgaGlnaC12YWx1ZSBlbmdhZ2VtZW50IChlLmcuLCBhZGRpbmcgaXRlbXMgdG8gY2FydCwgdmlzaXRpbmcgY2hlY2tvdXQtcmVsYXRlZCBwYWdlcykuDQoNCi0gKipQcm9kdWN0UmVsYXRlZF9EdXJhdGlvbl96IChPZGRzIFJhdGlvID4gMSk6KiogIA0KICBNb3JlIHRpbWUgc3BlbnQgb24gcHJvZHVjdCBwYWdlcyBpbmNyZWFzZXMgdGhlIGxpa2VsaWhvb2Qgb2YgcHVyY2hhc2UsIHJlZmxlY3Rpbmcgc3Ryb25nZXIgc2hvcHBpbmcgaW50ZW50Lg0KDQotICoqRXhpdFJhdGVzX3ogKE9kZHMgUmF0aW8gPCAxKToqKiAgDQogIEhpZ2hlciBleGl0IHJhdGVzIGFyZSBhc3NvY2lhdGVkIHdpdGggc2hhcnBseSBsb3dlciBwdXJjaGFzZSBvZGRzLiBUaGlzIGlzIGEgc3Ryb25nIG5lZ2F0aXZlIHByZWRpY3RvciwgYXMgZXhwZWN0ZWQuDQoNCi0gKipNb250aCBlZmZlY3RzOioqICANCiAgTW9udGhzIGxpa2UgKipOb3ZlbWJlcioqIGV4aGliaXQgaGlnaGVyIG9kZHMgb2YgcHVyY2hhc2UgcmVsYXRpdmUgdG8gYSBiYXNlbGluZSBtb250aCwgY29uc2lzdGVudCB3aXRoIGhvbGlkYXkgc2hvcHBpbmcgcGVha3MuIE90aGVyIG1vbnRocyBjYW4gc2hvdyByZWR1Y2VkIG9kZHMsIHJlZmxlY3Rpbmcgc2Vhc29uYWxpdHkgaW4gY29uc3VtZXIgZGVtYW5kLg0KDQotICoqVmlzaXRvclR5cGUgPSBSZXR1cm5pbmdWaXNpdG9yIChPZGRzIFJhdGlvID4gMSk6KiogIA0KICBSZXR1cm5pbmcgdmlzaXRvcnMgYXJlIHN1YnN0YW50aWFsbHkgbW9yZSBsaWtlbHkgdG8gcHVyY2hhc2UgdGhhbiBuZXcgdmlzaXRvcnMsIGluZGljYXRpbmcgdGhhdCBwcmlvciBmYW1pbGlhcml0eSBhbmQgZW5nYWdlbWVudCB3aXRoIHRoZSBzaXRlIGFyZSBjcml0aWNhbC4NCg0KVGhlc2UgcGF0dGVybnMgc3VwcG9ydCBjbGVhciBtYXJrZXRpbmcgYW5kIFVYIHN0cmF0ZWdpZXM6IGluY3JlYXNlIHBhZ2UgdmFsdWUsIGRlZXBlbiBwcm9kdWN0IGVuZ2FnZW1lbnQsIHJlZHVjZSBlYXJseSBleGl0cywgYW5kIG51cnR1cmUgcmV0dXJuaW5nIHZpc2l0b3IgcmVsYXRpb25zaGlwcy4NCg0KLS0tDQoNCiMgQW5zd2VycyB0byBSZXNlYXJjaCBRdWVzdGlvbnMNCg0KMS4gKipXaGljaCBiZWhhdmlvcnMgcHJlZGljdCBwdXJjaGFzZXM/KiogIA0KICAgRW5nYWdlbWVudCBkZXB0aCBtZWFzdXJlcyAoYFBhZ2VWYWx1ZXNgLCBgUHJvZHVjdFJlbGF0ZWRfRHVyYXRpb25gKSBzdHJvbmdseSBpbmNyZWFzZSBwdXJjaGFzZSBvZGRzLCB3aGlsZSBkaXNlbmdhZ2VtZW50IChgRXhpdFJhdGVzYCkgc2hhcnBseSBkZWNyZWFzZXMgdGhlbS4gIA0KDQoyLiAqKldoaWNoIGNhdGVnb3JpY2FsIGZhY3RvcnMgbWF0dGVyPyoqICANCiAgIGBWaXNpdG9yVHlwZWAgKHJldHVybmluZyB2cyBuZXcpIGFuZCBgTW9udGhgIChlc3BlY2lhbGx5IE5vdmVtYmVyKSBoYXZlIGltcG9ydGFudCBlZmZlY3RzLCByZWZsZWN0aW5nIHNlYXNvbmFsIGRlbWFuZCBhbmQgdGhlIGltcG9ydGFuY2Ugb2YgcmVwZWF0IHZpc2l0b3JzLiAgDQoNCjMuICoqV2hpY2ggbW9kZWwgYmVzdCBwcmVkaWN0cyBjb252ZXJzaW9ucz8qKiAgDQogICBUaGUgKipmdWxsIHN0YW5kYXJkaXplZCBsb2dpc3RpYyByZWdyZXNzaW9uIG1vZGVsKiogYWNoaWV2ZXMgdGhlIGhpZ2hlc3QgY3Jvc3MtdmFsaWRhdGVkIEFVQyBhbmQgdGhlIGxvd2VzdCBBSUMsIGFuZCBwZXJmb3JtcyBiZXN0IG9uIHRoZSB0ZXN0IHNldC4gSXQgaXMgdGhlcmVmb3JlIGNob3NlbiBhcyB0aGUgZmluYWwgbW9kZWwuDQoNCi0tLQ0KDQojICBDb25jbHVzaW9ucw0KDQpUaGlzIHByb2plY3QgZGVtb25zdHJhdGVzIHRoYXQ6DQoNCi0gT25saW5lIHB1cmNoYXNlIHByb2JhYmlsaXR5IGlzIGRyaXZlbiBzdHJvbmdseSBieSAqKnBhZ2UgZW5nYWdlbWVudCoqLCAqKnByb2R1Y3QgZXhwbG9yYXRpb24qKiwgYW5kICoqZXhpdCBiZWhhdmlvcioqLiAgDQotIFNlYXNvbmFsIGFuZCB2aXNpdG9yLXR5cGUgZWZmZWN0cyBwcm92aWRlIGFkZGl0aW9uYWwgY29udGV4dOKAlGhvbGlkYXkgcGVyaW9kcyBhbmQgcmV0dXJuaW5nIHZpc2l0b3JzIHNpZ25pZmljYW50bHkgYm9vc3QgY29udmVyc2lvbiBsaWtlbGlob29kLiAgDQotIEEgKipmdWxsIGxvZ2lzdGljIHJlZ3Jlc3Npb24gbW9kZWwqKiB3aXRoIHN0YW5kYXJkaXplZCBwcmVkaWN0b3JzIG91dHBlcmZvcm1zIHJlZHVjZWQsIHN0ZXB3aXNlLCBhbmQgZGlzY3JldGl6ZWQgdmFyaWFudHMgaW4gYm90aCBmaXQgYW5kIHByZWRpY3RpdmUgcG93ZXIuICANCi0gRGVzcGl0ZSBjbGFzcyBpbWJhbGFuY2UsIHRoZSBmaW5hbCBtb2RlbCBhY2hpZXZlcyAqKkFVQyDiiYggMC45MCoqLCBpbmRpY2F0aW5nIGV4Y2VsbGVudCBkaXNjcmltaW5hdGlvbiBiZXR3ZWVuIGJ1eWluZyBhbmQgbm9uLWJ1eWluZyBzZXNzaW9ucy4NCg0KVGhlc2UgaW5zaWdodHMgY2FuIGd1aWRlIHByYWN0aWNhbCBpbnRlcnZlbnRpb25zOg0KDQotIEluY3JlYXNlIGZlYXR1cmVzIHRoYXQgcmFpc2UgYFBhZ2VWYWx1ZXNgIChlLmcuLCB0YXJnZXRlZCByZWNvbW1lbmRhdGlvbnMsIHJpY2ggcHJvZHVjdCBpbmZvKS4gIA0KLSBSZWR1Y2UgZWFybHkgZXhpdHMgdmlhIGltcHJvdmVkIG5hdmlnYXRpb24gYW5kIGNsZWFyZXIgY2FsbHMgdG8gYWN0aW9uLiAgDQotIFJldGFyZ2V0IGFuZCByZXdhcmQgcmV0dXJuaW5nIHZpc2l0b3JzIHdobyBleGhpYml0IHN0cm9uZyBlbmdhZ2VtZW50IHNpZ25hbHMuDQoNCi0tLQ0KDQojICBGdXR1cmUgSW1wcm92ZW1lbnRzDQoNCkZ1dHVyZSB3b3JrIGNvdWxkOg0KDQotIEluY29ycG9yYXRlICoqaW50ZXJhY3Rpb24gdGVybXMqKiAoZS5nLiwgYmV0d2VlbiBkdXJhdGlvbnMgYW5kIHZpc2l0b3IgdHlwZSkgdG8gY2FwdHVyZSBtb3JlIG51YW5jZWQgcmVsYXRpb25zaGlwcy4gIA0KLSBFeHBsb3JlICoqdHJlZS1iYXNlZCBlbnNlbWJsZSBtZXRob2RzKiogKFJhbmRvbSBGb3Jlc3RzLCBYR0Jvb3N0LCBDYXRCb29zdCwgTGlnaHRHQk0pIHRvIG1vZGVsIG5vbmxpbmVhciBlZmZlY3RzIGFuZCBpbnRlcmFjdGlvbnMgbW9yZSBmbGV4aWJseS4gIA0KLSBFeHBlcmltZW50IHdpdGggKiphbHRlcm5hdGl2ZSB0aHJlc2hvbGRzKiosIGNvc3Qtc2Vuc2l0aXZlIGNsYXNzaWZpY2F0aW9uLCBvciBvdmVyc2FtcGxpbmcgdGVjaG5pcXVlcyAoU01PVEUpIHRvIGltcHJvdmUgcmVjYWxsIGZvciB0aGUgbWlub3JpdHkg4oCcWWVz4oCdIGNsYXNzLiAgDQotIEFkZCBjYWxpYnJhdGlvbiBwbG90cyAoZS5nLiwgcmVsaWFiaWxpdHkgZGlhZ3JhbXMpIHRvIGFzc2VzcyBhbmQsIGlmIG5lZWRlZCwgYWRqdXN0IHByZWRpY3RlZCBwcm9iYWJpbGl0aWVzLg0KDQpUaGVzZSBleHRlbnNpb25zIGNvdWxkIGZ1cnRoZXIgcmVmaW5lIHByZWRpY3RpdmUgcGVyZm9ybWFuY2UgYW5kIHByb3ZpZGUgZXZlbiBkZWVwZXIgaW5zaWdodCBpbnRvIG9ubGluZSBwdXJjaGFzaW5nIGJlaGF2aW9yLg0KDQo=