Conjoint Analysis is a survey based technique to identify how customers value various attributes that make up an individual product. Products and services are bundles of features that customers consider jointly and while making a purchase decision they must make trade offs. Conjoint analysis helps companies and business owners determine importance of different features and their money value. This also helps them identify optimal price for their products and services.

Basic Setup In this example, we will discover how to design a survey to get the information necessary, analyze and quantify importance of various features in a product and how to interpret the results. But first , let’s begin by setting up R environment and loading required libraries.

library(tidyverse)
options(scipen = 999)
options(digits=3)
library(conjoint)
install.packages('pacman')
library(pacman)
pacman::p_load(conjoint, DoE.base, knitr, dplyr, kableExtra, ggplot2)

Experiment Design

I want to know what features / style in sneakers stand out more to fans


###Experiment Design for asking Conjoint Questions

#- identify number of questions required. Define level and factors
NumberOfQuestions = nrow(oa.design(nlevels=c(7,2,2)))
creating full factorial with 28 runs ...
# create dummy data
NBAstar_branded_sneaker = expand.grid(
                
mj_nostalgia = c('fashionable','performance','sleek','style','hightop','reputation','none_of_the_above'),
 lj_enhancements= c('performance_based',
                    'modern','technology','stability','durability','lowtop','none'))
                   
#combination to enquire
selectedComb = caFactorialDesign(
  data  = NBAstar_branded_sneaker,
type = 'fractional',
cards = NumberOfQuestions)

# print select combinations

kable(selectedComb) %>% 
  kable_styling(bootstrap_options = c('striped','hover','condensed','responsive'))
mj_nostalgia lj_enhancements
4 style performance_based
5 hightop performance_based
6 reputation performance_based
7 none_of_the_above performance_based
8 fashionable modern
9 performance modern
12 hightop modern
13 reputation modern
15 fashionable technology
17 sleek technology
18 style technology
21 none_of_the_above technology
23 performance stability
24 sleek stability
27 reputation stability
28 none_of_the_above stability
29 fashionable durability
31 sleek durability
32 style durability
34 reputation durability
37 performance lowtop
38 sleek lowtop
39 style lowtop
40 hightop lowtop
43 fashionable none
44 performance none
47 hightop none
49 none_of_the_above none

Predicting Responses for other combinations Using orthogonal factorial design, we identified 28 combinations to include in customer survey out of possible 49 combinations. It saves time, money and effort. Survey takers was asked if they like the combination or not and their reposes were noted for all 9 combinations. We will use these 9 responses to train out logistic regression model and use to predict rest of combinations.

# add response column
selectedComb$Response = 
  c(0,0,1,1,1,1,1,0,0,0,0,1,1,1,1,1,0,0,0,0,1,1,1,1,1,0,0,0)
                          
selectedComb$Response
 [1] 0 0 1 1 1 1 1 0 0 0 0 1 1 1 1 1 0 0 0 0 1 1 1 1 1 0 0 0
# logistic regression
logisticmodel=glm(Response ~ factor(mj_nostalgia) + factor(lj_enhancements),
            family=binomial(link='logit'), data=selectedComb)
logisticmodel

Call:  glm(formula = Response ~ factor(mj_nostalgia) + factor(lj_enhancements), 
    family = binomial(link = "logit"), data = selectedComb)

Coefficients:
                          (Intercept)        factor(mj_nostalgia)performance  
                 37.17375406612627131                  -18.66970866954126862  
            factor(mj_nostalgia)sleek              factor(mj_nostalgia)style  
                -24.18860483890588853                  -56.42542531667980654  
          factor(mj_nostalgia)hightop         factor(mj_nostalgia)reputation  
                -37.17375406612629263                  -37.17375406612627131  
factor(mj_nostalgia)none_of_the_above          factor(lj_enhancements)modern  
                  0.00000000000000367                    0.00000000000000819  
    factor(lj_enhancements)technology       factor(lj_enhancements)stability  
                -37.17375406612627131                   19.79288025159280195  
    factor(lj_enhancements)durability          factor(lj_enhancements)lowtop  
                -56.83058479194890822                   39.00537632897358975  
          factor(lj_enhancements)none  
                -37.17375406612627842  

Degrees of Freedom: 27 Total (i.e. Null);  15 Residual
Null Deviance:      38.7 
Residual Deviance: 11.1     AIC: 37.1
# predict
NBAstar_branded_sneaker$response = ifelse(predict(logisticmodel,NBAstar_branded_sneaker,type="response") > 0.5,1,0)
NBAstar_branded_sneaker$response 
 [1] 1 1 1 0 0 0 1 1 1 1 0 0 1 1 0 0 0 0 0 0 1 1 1 1 1 1 1 1 0 0 0 0 0 0 0 1 1 1 1 1 1 1
[43] 0 0 0 0 0 0 0

Conjoint Analysis

Now, that we have all the data we need, lets run a conjoint analysis and summarize importance of various features for customers.


Call:
lm(formula = frml)

Residuals:
    Min      1Q  Median      3Q     Max 
-0,5102 -0,0816 -0,0816  0,2041  0,6327 

Coefficients:
                           Estimate Std. Error t value          Pr(>|t|)    
(Intercept)                  0,4898     0,0405   12,08 0,000000000000031 ***
factor(x$mj_nostalgia)1      0,0816     0,0993    0,82            0,4164    
factor(x$mj_nostalgia)2      0,0816     0,0993    0,82            0,4164    
factor(x$mj_nostalgia)3      0,0816     0,0993    0,82            0,4164    
factor(x$mj_nostalgia)4     -0,2041     0,0993   -2,06            0,0471 *  
factor(x$mj_nostalgia)5     -0,2041     0,0993   -2,06            0,0471 *  
factor(x$mj_nostalgia)6     -0,0612     0,0993   -0,62            0,5413    
factor(x$lj_enhancements)1   0,0816     0,0993    0,82            0,4164    
factor(x$lj_enhancements)2   0,2245     0,0993    2,26            0,0299 *  
factor(x$lj_enhancements)3  -0,3469     0,0993   -3,49            0,0013 ** 
factor(x$lj_enhancements)4   0,5102     0,0993    5,14 0,000009820851537 ***
factor(x$lj_enhancements)5  -0,4898     0,0993   -4,93 0,000018414331608 ***
factor(x$lj_enhancements)6   0,5102     0,0993    5,14 0,000009820851537 ***
---
Signif. codes:  0 ‘***’ 0,001 ‘**’ 0,01 ‘*’ 0,05 ‘.’ 0,1 ‘ ’ 1

Residual standard error: 0,284 on 36 degrees of freedom
Multiple R-squared:  0,763, Adjusted R-squared:  0,684 
F-statistic: 9,68 on 12 and 36 DF,  p-value: 0,0000000514

[1] "Part worths (utilities) of levels (model parameters for whole sample):"
[1] "Average importance of factors (attributes):"
[1] 30 70
[1] Sum of average importance:  100
[1] "Chart of average factors importance"

Feature Importance
The Importance of features plot shows that buyers of sneakers (specifically Nike brand sneakers associated with NBA stars Michael Jordan | Lebron James) care primarily about enhancements while only a small minority of consumers care abut nostalgia.

# Feature Importance
Importance = data.frame(Feature = c('mj_nostalgia','lj_enhancements'),
                        Importance=caImportance(y=Survey,
   x=NBAstar_branded_sneaker[,1:2]))
Importance
ggplot(Importance,aes(reorder(Feature,-Importance),Importance))+
  geom_bar(stat='identity',width=.8,fill='purple3')+
  ggtitle('Importance of different features')+
  xlab('')+theme_classic()+coord_flip()





Feature Utilities

Now, for NBA player Endorsed sneakers, we understand that enhancement is the key factor, although nostalgia is also of some importance. But which aspect of enhancement is of the greatest reason for consumers wanting to buy the sneakers? We can get our answer by comparing the levels of each feature one at a time

#summarize utilities
util = data.frame(Utilities = t
        (data.frame(caPartUtilities(y=Survey,
          x=NBAstar_branded_sneaker[,1:2],z=levels))))


util$levels = row.names(util)

util$levels
 [1] "intercept"         "fashionable"       "performance"       "sleek"            
 [5] "style"             "hightop"           "reputation"        "none_of_the_above"
 [9] "performance_based" "modern"            "technology"        "stability"        
[13] "durability"        "lowtop"            "none"             
util %>% 
  arrange(desc(Utilities))
#  Type
ggplot(data = util[which(util$levels %in% c('performance_based',
                    'modern','technology','stability','durability','lowtop','none')),], aes(x = levels, y = Utilities,fill=Utilities)) +
  geom_bar(stat='identity') +
  ggtitle("Utilities of NBA Endorsed Sneaker 'Enhancements' aspects") + xlab("")+coord_flip()+
  geom_hline(yintercept = 0,lty=2,col='black',linewidth=.7)+scale_fill_gradient(high='pink2',low='black')+theme(plot.title =  element_text(size=14),vjust=1)+theme_classic()+theme(legend.position = 'bottom')



ggplot(data = util[which(util$levels %in% c('performance_based',
                    'modern','stability','lowtop')),], aes(x = levels, y = Utilities,fill=Utilities)) +
  geom_bar(stat='identity') +
  ggtitle("Utilities of NBA Endorsed Sneaker 'Enhancements' aspects") + xlab("")+coord_flip()+
  geom_hline(yintercept = 0,lty=2,col='black',linewidth=.7)+scale_fill_gradient(high='pink2',low='black')+theme(plot.title =  element_text(size=14),vjust=1)+theme_classic()+theme(legend.position = 'bottom')

NA
NA

In conclusion:

Findings
After comparing the levels of each feature in our utility summary it’s clear from the results of the survey that the majority of respondents choose to buy NBA endorsed sneakers based off of enhancements over nostalgia. In addition to this, we also discovered that the two most important aspects behind one’s decision to purchase such sneakers is its stability and low top design with both having 51% support and after that is modern at 22.4% alongside performance_based at 8%. While durability, none of the above and technology all seemed to be of absolutely no importance to potential customers.

Insights:

Now, in order to take advantage of the popularity and fan base behind these types of sneakers and the today’s stars that endorse them such as Lebron James the company should continue to focus heavily on the sneakers level of stability and low cut design and secondarily its modern look and performance_based fit ideally suited for game play.

LS0tDQp0aXRsZTogIkNvbmpvaW50IGFuYWx5c2lzIg0Kc3VidGl0bGU6ICAiQ2hvaWNlIG9mIEF0aGxldGljIFNuZWFrZXJzIGVuZG9yc2VkIGJ5IE5CQSBTdGFyIg0Kb3V0cHV0OiBodG1sX25vdGVib29rDQphdXRob3I6IGhhcnJpc29uDQotLS0NCjxicj4NCjxicj4NCjxicj4NCjxicj4NCkNvbmpvaW50IEFuYWx5c2lzIGlzIGEgc3VydmV5IGJhc2VkIHRlY2huaXF1ZSB0byBpZGVudGlmeSBob3cgY3VzdG9tZXJzIHZhbHVlIHZhcmlvdXMgYXR0cmlidXRlcyB0aGF0IG1ha2UgdXAgYW4gaW5kaXZpZHVhbCBwcm9kdWN0LiBQcm9kdWN0cyBhbmQgc2VydmljZXMgYXJlIGJ1bmRsZXMgb2YgZmVhdHVyZXMgdGhhdCBjdXN0b21lcnMgY29uc2lkZXIgam9pbnRseSBhbmQgd2hpbGUgbWFraW5nIGEgcHVyY2hhc2UgZGVjaXNpb24gdGhleSBtdXN0IG1ha2UgdHJhZGUgb2Zmcy4gQ29uam9pbnQgYW5hbHlzaXMgaGVscHMgY29tcGFuaWVzIGFuZCBidXNpbmVzcyBvd25lcnMgZGV0ZXJtaW5lIGltcG9ydGFuY2Ugb2YgZGlmZmVyZW50IGZlYXR1cmVzIGFuZCB0aGVpciBtb25leSB2YWx1ZS4gVGhpcyBhbHNvIGhlbHBzIHRoZW0gaWRlbnRpZnkgb3B0aW1hbCBwcmljZSBmb3IgdGhlaXIgcHJvZHVjdHMgYW5kIHNlcnZpY2VzLg0KDQpCYXNpYyBTZXR1cA0KSW4gdGhpcyBleGFtcGxlLCB3ZSB3aWxsIGRpc2NvdmVyIGhvdyB0byBkZXNpZ24gYSBzdXJ2ZXkgdG8gZ2V0IHRoZSBpbmZvcm1hdGlvbiBuZWNlc3NhcnksIGFuYWx5emUgYW5kIHF1YW50aWZ5IGltcG9ydGFuY2Ugb2YgdmFyaW91cyBmZWF0dXJlcyBpbiBhIHByb2R1Y3QgYW5kIGhvdyB0byBpbnRlcnByZXQgdGhlIHJlc3VsdHMuIEJ1dCBmaXJzdCAsIGxldOKAmXMgYmVnaW4gYnkgc2V0dGluZyB1cCBSIGVudmlyb25tZW50IGFuZCBsb2FkaW5nIHJlcXVpcmVkIGxpYnJhcmllcy4NCg0KYGBge3IgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRX0NCmxpYnJhcnkodGlkeXZlcnNlKQ0Kb3B0aW9ucyhzY2lwZW4gPSA5OTkpDQpvcHRpb25zKGRpZ2l0cz0zKQ0KbGlicmFyeShjb25qb2ludCkNCmluc3RhbGwucGFja2FnZXMoJ3BhY21hbicpDQpsaWJyYXJ5KHBhY21hbikNCnBhY21hbjo6cF9sb2FkKGNvbmpvaW50LCBEb0UuYmFzZSwga25pdHIsIGRwbHlyLCBrYWJsZUV4dHJhLCBnZ3Bsb3QyKQ0KYGBgDQoNCjxiPkV4cGVyaW1lbnQgRGVzaWduPC9iPg0KDQpJIHdhbnQgdG8ga25vdyB3aGF0IGZlYXR1cmVzIC8gc3R5bGUgaW4gc25lYWtlcnMgc3RhbmQgb3V0IG1vcmUgdG8gZmFucw0KDQoNCg0KYGBge3J9DQoNCiMjI0V4cGVyaW1lbnQgRGVzaWduIGZvciBhc2tpbmcgQ29uam9pbnQgUXVlc3Rpb25zDQoNCiMtIGlkZW50aWZ5IG51bWJlciBvZiBxdWVzdGlvbnMgcmVxdWlyZWQuIERlZmluZSBsZXZlbCBhbmQgZmFjdG9ycw0KTnVtYmVyT2ZRdWVzdGlvbnMgPSBucm93KG9hLmRlc2lnbihubGV2ZWxzPWMoNywyLDIpKSkNCg0KIyBjcmVhdGUgZHVtbXkgZGF0YQ0KTkJBc3Rhcl9icmFuZGVkX3NuZWFrZXIgPSBleHBhbmQuZ3JpZCgNCiAgICAgICAgICAgICAgICANCm1qX25vc3RhbGdpYSA9IGMoJ2Zhc2hpb25hYmxlJywncGVyZm9ybWFuY2UnLCdzbGVlaycsJ3N0eWxlJywnaGlnaHRvcCcsJ3JlcHV0YXRpb24nLCdub25lX29mX3RoZV9hYm92ZScpLA0KIGxqX2VuaGFuY2VtZW50cz0gYygncGVyZm9ybWFuY2VfYmFzZWQnLA0KICAgICAgICAgICAgICAgICAgICAnbW9kZXJuJywndGVjaG5vbG9neScsJ3N0YWJpbGl0eScsJ2R1cmFiaWxpdHknLCdsb3d0b3AnLCdub25lJykpDQogICAgICAgICAgICAgICAgICAgDQojY29tYmluYXRpb24gdG8gZW5xdWlyZQ0Kc2VsZWN0ZWRDb21iID0gY2FGYWN0b3JpYWxEZXNpZ24oDQogIGRhdGEgID0gTkJBc3Rhcl9icmFuZGVkX3NuZWFrZXIsDQp0eXBlID0gJ2ZyYWN0aW9uYWwnLA0KY2FyZHMgPSBOdW1iZXJPZlF1ZXN0aW9ucykNCg0KIyBwcmludCBzZWxlY3QgY29tYmluYXRpb25zDQoNCmthYmxlKHNlbGVjdGVkQ29tYikgJT4lIA0KICBrYWJsZV9zdHlsaW5nKGJvb3RzdHJhcF9vcHRpb25zID0gYygnc3RyaXBlZCcsJ2hvdmVyJywnY29uZGVuc2VkJywncmVzcG9uc2l2ZScpKQ0KYGBgDQoNCg0KYGBge3J9DQpgYGANCg0KDQpgYGB7cn0NCmBgYA0KDQpQcmVkaWN0aW5nIFJlc3BvbnNlcyBmb3Igb3RoZXIgY29tYmluYXRpb25zDQpVc2luZyBvcnRob2dvbmFsIGZhY3RvcmlhbCBkZXNpZ24sIHdlIGlkZW50aWZpZWQgMjggY29tYmluYXRpb25zIHRvIGluY2x1ZGUgaW4gY3VzdG9tZXIgc3VydmV5IG91dCBvZiBwb3NzaWJsZSA0OSBjb21iaW5hdGlvbnMuIEl0IHNhdmVzIHRpbWUsIG1vbmV5IGFuZCBlZmZvcnQuIFN1cnZleSB0YWtlcnMgd2FzIGFza2VkIGlmIHRoZXkgbGlrZSB0aGUgY29tYmluYXRpb24gb3Igbm90IGFuZCB0aGVpciByZXBvc2VzIHdlcmUgbm90ZWQgZm9yIGFsbCA5IGNvbWJpbmF0aW9ucy4gV2Ugd2lsbCB1c2UgdGhlc2UgOSByZXNwb25zZXMgdG8gdHJhaW4gb3V0IGxvZ2lzdGljIHJlZ3Jlc3Npb24gbW9kZWwgYW5kIHVzZSB0byBwcmVkaWN0IHJlc3Qgb2YgY29tYmluYXRpb25zLg0KDQpgYGB7cn0NCiMgYWRkIHJlc3BvbnNlIGNvbHVtbg0Kc2VsZWN0ZWRDb21iJFJlc3BvbnNlID0gDQogIGMoMCwwLDEsMSwxLDEsMSwwLDAsMCwwLDEsMSwxLDEsMSwwLDAsMCwwLDEsMSwxLDEsMSwwLDAsMCkNCiAgICAgICAgICAgICAgICAgICAgICAgICAgDQpzZWxlY3RlZENvbWIkUmVzcG9uc2UNCmBgYA0KDQoNCmBgYHtyIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0V9DQojIGxvZ2lzdGljIHJlZ3Jlc3Npb24NCmxvZ2lzdGljbW9kZWw9Z2xtKFJlc3BvbnNlIH4gZmFjdG9yKG1qX25vc3RhbGdpYSkgKyBmYWN0b3IobGpfZW5oYW5jZW1lbnRzKSwNCiAgICAgICAgICAgIGZhbWlseT1iaW5vbWlhbChsaW5rPSdsb2dpdCcpLCBkYXRhPXNlbGVjdGVkQ29tYikNCmxvZ2lzdGljbW9kZWwNCmBgYA0KDQoNCmBgYHtyfQ0KIyBwcmVkaWN0DQpOQkFzdGFyX2JyYW5kZWRfc25lYWtlciRyZXNwb25zZSA9IGlmZWxzZShwcmVkaWN0KGxvZ2lzdGljbW9kZWwsTkJBc3Rhcl9icmFuZGVkX3NuZWFrZXIsdHlwZT0icmVzcG9uc2UiKSA+IDAuNSwxLDApDQpOQkFzdGFyX2JyYW5kZWRfc25lYWtlciRyZXNwb25zZSANCmBgYA0KDQoNCmBgYHtyfQ0KYGBgDQoNCjxiPkNvbmpvaW50IEFuYWx5c2lzIDwvYj4NCg0KTm93LCB0aGF0IHdlIGhhdmUgYWxsIHRoZSBkYXRhIHdlIG5lZWQsIGxldHMgcnVuIGEgY29uam9pbnQgYW5hbHlzaXMgYW5kIHN1bW1hcml6ZSBpbXBvcnRhbmNlIG9mIHZhcmlvdXMgZmVhdHVyZXMgZm9yIGN1c3RvbWVycy4NCmBgYHtyLCBlcnJvcj1GQUxTRSwgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRSxlY2hvPUZBTFNFfQ0KDQojIFN1cnZleSBSZXNwb25zZQ0KU3VydmV5ID0gZGF0YS5mcmFtZShtYXRyaXgoTkJBc3Rhcl9icmFuZGVkX3NuZWFrZXIkcmVzcG9uc2UsDQogICAgICAgICAgICAgICAgICAgICAgICAgICBuY29sPTQ5LG5yb3c9MSkpDQoNCiMgc2F2ZSBsZXZlbHMgb2YgdGhlIHJlc3BvbnNlDQpsZXZlbHMgPSBjKCdmYXNoaW9uYWJsZScsJ3BlcmZvcm1hbmNlJywnc2xlZWsnLCdzdHlsZScsJ2hpZ2h0b3AnLCdyZXB1dGF0aW9uJywnbm9uZV9vZl90aGVfYWJvdmUnLA0KICAgICAgICAgICAncGVyZm9ybWFuY2VfYmFzZWQnLCdtb2Rlcm4nLCd0ZWNobm9sb2d5Jywnc3RhYmlsaXR5JywnZHVyYWJpbGl0eScsJ2xvd3RvcCcsJ25vbmUnKQ0KDQojQ29uam9pbnQNCkNvbmpvaW50KFN1cnZleSxOQkFzdGFyX2JyYW5kZWRfc25lYWtlclssMToyXSwgej1sZXZlbHMpDQpgYGANCg0KPGI+RmVhdHVyZSBJbXBvcnRhbmNlPC9iPg0KPGJyPg0KPGk+DQpUaGUgSW1wb3J0YW5jZSBvZiBmZWF0dXJlcyBwbG90IHNob3dzIHRoYXQgYnV5ZXJzIG9mIHNuZWFrZXJzIChzcGVjaWZpY2FsbHkgTmlrZSBicmFuZCBzbmVha2VycyBhc3NvY2lhdGVkIHdpdGggTkJBIHN0YXJzIE1pY2hhZWwgSm9yZGFuIHwgTGVicm9uIEphbWVzKSBjYXJlIHByaW1hcmlseSBhYm91dCBlbmhhbmNlbWVudHMgd2hpbGUgb25seSBhIHNtYWxsIG1pbm9yaXR5IG9mIGNvbnN1bWVycyBjYXJlIGFidXQgbm9zdGFsZ2lhLg0KPC9pPg0KDQpgYGB7cn0NCiMgRmVhdHVyZSBJbXBvcnRhbmNlDQpJbXBvcnRhbmNlID0gZGF0YS5mcmFtZShGZWF0dXJlID0gYygnbWpfbm9zdGFsZ2lhJywnbGpfZW5oYW5jZW1lbnRzJyksDQogICAgICAgICAgICAgICAgICAgICAgICBJbXBvcnRhbmNlPWNhSW1wb3J0YW5jZSh5PVN1cnZleSwNCiAgIHg9TkJBc3Rhcl9icmFuZGVkX3NuZWFrZXJbLDE6Ml0pKQ0KSW1wb3J0YW5jZQ0KYGBgDQoNCg0KYGBge3J9DQpnZ3Bsb3QoSW1wb3J0YW5jZSxhZXMocmVvcmRlcihGZWF0dXJlLC1JbXBvcnRhbmNlKSxJbXBvcnRhbmNlKSkrDQogIGdlb21fYmFyKHN0YXQ9J2lkZW50aXR5Jyx3aWR0aD0uOCxmaWxsPSdwdXJwbGUzJykrDQogIGdndGl0bGUoJ0ltcG9ydGFuY2Ugb2YgZGlmZmVyZW50IGZlYXR1cmVzJykrDQogIHhsYWIoJycpK3RoZW1lX2NsYXNzaWMoKStjb29yZF9mbGlwKCkNCmBgYA0KDQo8YnI+DQo8YnI+DQo8YnI+DQo8YnI+DQpgYGB7cn0NCmBgYA0KDQo8Yj5GZWF0dXJlIFV0aWxpdGllczwvYj4NCjxicj48YnI+DQo8aT4NCk5vdywgZm9yIE5CQSBwbGF5ZXIgRW5kb3JzZWQgc25lYWtlcnMsIHdlIHVuZGVyc3RhbmQgdGhhdCBlbmhhbmNlbWVudCBpcyB0aGUga2V5IGZhY3RvciwgYWx0aG91Z2ggIG5vc3RhbGdpYSBpcyBhbHNvIG9mIHNvbWUgaW1wb3J0YW5jZS4gQnV0IHdoaWNoIGFzcGVjdCBvZiBlbmhhbmNlbWVudCBpcyBvZiB0aGUgZ3JlYXRlc3QgcmVhc29uIGZvciBjb25zdW1lcnMgd2FudGluZyB0byBidXkgdGhlIHNuZWFrZXJzPyBXZSBjYW4gZ2V0IG91ciBhbnN3ZXIgYnkgY29tcGFyaW5nIHRoZSBsZXZlbHMgb2YgZWFjaCBmZWF0dXJlIG9uZSBhdCBhIHRpbWUNCjwvaT4NCg0KDQoNCmBgYHtyfQ0KI3N1bW1hcml6ZSB1dGlsaXRpZXMNCnV0aWwgPSBkYXRhLmZyYW1lKFV0aWxpdGllcyA9IHQNCiAgICAgICAgKGRhdGEuZnJhbWUoY2FQYXJ0VXRpbGl0aWVzKHk9U3VydmV5LA0KICAgICAgICAgIHg9TkJBc3Rhcl9icmFuZGVkX3NuZWFrZXJbLDE6Ml0sej1sZXZlbHMpKSkpDQoNCg0KdXRpbCRsZXZlbHMgPSByb3cubmFtZXModXRpbCkNCg0KdXRpbCRsZXZlbHMNCg0KdXRpbCAlPiUgDQogIGFycmFuZ2UoZGVzYyhVdGlsaXRpZXMpKQ0KYGBgDQoNCg0KYGBge3IgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRX0NCiMgIFR5cGUNCmdncGxvdChkYXRhID0gdXRpbFt3aGljaCh1dGlsJGxldmVscyAlaW4lIGMoJ3BlcmZvcm1hbmNlX2Jhc2VkJywNCiAgICAgICAgICAgICAgICAgICAgJ21vZGVybicsJ3RlY2hub2xvZ3knLCdzdGFiaWxpdHknLCdkdXJhYmlsaXR5JywnbG93dG9wJywnbm9uZScpKSxdLCBhZXMoeCA9IGxldmVscywgeSA9IFV0aWxpdGllcyxmaWxsPVV0aWxpdGllcykpICsNCiAgZ2VvbV9iYXIoc3RhdD0naWRlbnRpdHknKSArDQogIGdndGl0bGUoIlV0aWxpdGllcyBvZiBOQkEgRW5kb3JzZWQgU25lYWtlciAnRW5oYW5jZW1lbnRzJyBhc3BlY3RzIikgKyB4bGFiKCIiKStjb29yZF9mbGlwKCkrDQogIGdlb21faGxpbmUoeWludGVyY2VwdCA9IDAsbHR5PTIsY29sPSdibGFjaycsbGluZXdpZHRoPS43KStzY2FsZV9maWxsX2dyYWRpZW50KGhpZ2g9J3BpbmsyJyxsb3c9J2JsYWNrJykrdGhlbWUocGxvdC50aXRsZSA9ICBlbGVtZW50X3RleHQoc2l6ZT0xNCksdmp1c3Q9MSkrdGhlbWVfY2xhc3NpYygpK3RoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICdib3R0b20nKQ0KDQoNCmdncGxvdChkYXRhID0gdXRpbFt3aGljaCh1dGlsJGxldmVscyAlaW4lIGMoJ3BlcmZvcm1hbmNlX2Jhc2VkJywNCiAgICAgICAgICAgICAgICAgICAgJ21vZGVybicsJ3N0YWJpbGl0eScsJ2xvd3RvcCcpKSxdLCBhZXMoeCA9IGxldmVscywgeSA9IFV0aWxpdGllcyxmaWxsPVV0aWxpdGllcykpICsNCiAgZ2VvbV9iYXIoc3RhdD0naWRlbnRpdHknKSArDQogIGdndGl0bGUoIlV0aWxpdGllcyBvZiBOQkEgRW5kb3JzZWQgU25lYWtlciAnRW5oYW5jZW1lbnRzJyBhc3BlY3RzIikgKyB4bGFiKCIiKStjb29yZF9mbGlwKCkrDQogIGdlb21faGxpbmUoeWludGVyY2VwdCA9IDAsbHR5PTIsY29sPSdibGFjaycsbGluZXdpZHRoPS43KStzY2FsZV9maWxsX2dyYWRpZW50KGhpZ2g9J3BpbmsyJyxsb3c9J2JsYWNrJykrdGhlbWUocGxvdC50aXRsZSA9ICBlbGVtZW50X3RleHQoc2l6ZT0xNCksdmp1c3Q9MSkrdGhlbWVfY2xhc3NpYygpK3RoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICdib3R0b20nKQ0KDQoNCmBgYA0KPGg0PkluIGNvbmNsdXNpb246PC9oND4NCjxiPkZpbmRpbmdzPC9iPg0KPGJyPg0KPGk+DQpBZnRlciBjb21wYXJpbmcgdGhlIGxldmVscyBvZiBlYWNoIGZlYXR1cmUgaW4gb3VyIHV0aWxpdHkgc3VtbWFyeSBpdCdzIGNsZWFyIGZyb20gdGhlIHJlc3VsdHMgb2YgdGhlIHN1cnZleSB0aGF0IHRoZSBtYWpvcml0eSBvZiByZXNwb25kZW50cyBjaG9vc2UgdG8gYnV5IE5CQSBlbmRvcnNlZCBzbmVha2VycyBiYXNlZCBvZmYgb2YgZW5oYW5jZW1lbnRzIG92ZXIgbm9zdGFsZ2lhLiBJbiBhZGRpdGlvbiB0byB0aGlzLCB3ZSBhbHNvIGRpc2NvdmVyZWQgdGhhdCB0aGUgdHdvIG1vc3QgaW1wb3J0YW50IGFzcGVjdHMgYmVoaW5kIG9uZSdzIGRlY2lzaW9uIHRvIHB1cmNoYXNlIHN1Y2ggc25lYWtlcnMgaXMgaXRzIHN0YWJpbGl0eSBhbmQgbG93IHRvcCBkZXNpZ24gd2l0aCBib3RoIGhhdmluZyA1MSUgc3VwcG9ydCBhbmQgYWZ0ZXIgdGhhdCBpcyBtb2Rlcm4gYXQgMjIuNCUgYWxvbmdzaWRlIHBlcmZvcm1hbmNlX2Jhc2VkIGF0IDglLiBXaGlsZSBkdXJhYmlsaXR5LCBub25lIG9mIHRoZSBhYm92ZSBhbmQgdGVjaG5vbG9neSBhbGwgc2VlbWVkIHRvIGJlIG9mIGFic29sdXRlbHkgbm8gaW1wb3J0YW5jZSB0byBwb3RlbnRpYWwgY3VzdG9tZXJzLiAgICA8L2k+IA0KYGBge3J9DQoNCmBgYA0KDQpJbnNpZ2h0czoNCg0KTm93LCBpbiBvcmRlciB0byB0YWtlIGFkdmFudGFnZSBvZiB0aGUgcG9wdWxhcml0eSBhbmQgZmFuIGJhc2UgYmVoaW5kIHRoZXNlIHR5cGVzIG9mIHNuZWFrZXJzIGFuZCB0aGUgdG9kYXkncyBzdGFycyB0aGF0IGVuZG9yc2UgdGhlbSBzdWNoIGFzIExlYnJvbiBKYW1lcyB0aGUgY29tcGFueSBzaG91bGQgY29udGludWUgdG8gZm9jdXMgaGVhdmlseSBvbiB0aGUgc25lYWtlcnMgbGV2ZWwgb2Ygc3RhYmlsaXR5IGFuZCBsb3cgY3V0IGRlc2lnbiBhbmQgc2Vjb25kYXJpbHkgaXRzIG1vZGVybiBsb29rIGFuZCBwZXJmb3JtYW5jZV9iYXNlZCBmaXQgaWRlYWxseSBzdWl0ZWQgZm9yIGdhbWUgcGxheS4gIA0KDQpgYGB7cn0NCmBgYA0KDQoNCg0KDQpgYGB7cn0NCmBgYA0KDQoNCmBgYHtyfQ0KYGBgDQoNCg0KYGBge3J9DQpgYGANCg0KDQpgYGB7cn0NCmBgYA0KDQoNCmBgYHtyfQ0KYGBgDQoNCg0KYGBge3J9DQpgYGANCg0KDQpgYGB7cn0NCmBgYA0KDQoNCmBgYHtyfQ0KYGBgDQoNCg0KYGBge3J9DQpgYGANCg0K