Packages

Make sure the following packages are installed before proceeding:

  1. xtable
  2. knitr
  3. mlogit
  4. caret
  5. e1071
library("xtable") # processing of regression output
Warning message:
In strsplit(x, "\n") : input string 1 is invalid in this locale
library("knitr") # used for report compilation and table display
library("ggplot2") # very popular plotting library ggplot2
library("ggthemes") # themes for ggplot2
suppressMessages(library("mlogit")) # multinomial logit
library("caret")
Loading required package: lattice

Multinomial logit

Multinomial logit, in contrast to simple binomial logisic regression, is used for modeling choices among multiple alternatives.

Once the choice model has been estimated, we can use the parameter estimates to assess relative importance of different attributes in predicting the probability of choice.

Data

You will work with provided trasportation_data.csv file.

data <- read.csv(file = "transportation_data.csv")
kable(head(data,8))
TRAVELER MODE TTME INVC INVT HINC
1 0 69 59 100 35
1 0 34 31 372 35
1 0 35 25 417 35
1 1 0 10 180 35
2 0 64 58 68 30
2 0 44 31 354 30
2 0 53 25 399 30
2 1 0 11 255 30

由以上表格為例,1st~4th row代表第一個旅客的選擇過程,5th~8th row為第二個旅客,以此類推。 第一個col代表旅客最終選擇的交通工具,以0,1的形式呈現,並皆以以下方式排序。 1 - 飛機 2 - 火車 3 - 巴士 4 - 自駕

以第一個旅客為例,他選擇了自駕。

第2~4欄位則分別代表此選擇的“商品特性”,包括TTME, INVC, INVT

最後一欄位則代表此旅客的特性,HINC為其收入,以千元為單位。

所有變數皆假定為連續而非離散。

Descriptive statistics

transp_dec<-rbind(
colSums(data[seq(1, nrow(data), 4), ])/210,
colSums(data[seq(2, nrow(data), 4), ])/210,
colSums(data[seq(3, nrow(data), 4), ])/210,
colSums(data[seq(4, nrow(data), 4), ])/210)
transp_dec<-transp_dec[,c(2:5)]
colnames(transp_dec) <- c('CHOICE SHARE','AVG. WAITING TTME', 'AVG. COST', 'AVG. TRAVEL TIME')
kable(transp_dec)
CHOICE SHARE AVG. WAITING TTME AVG. COST AVG. TRAVEL TIME
0.2761905 61.00952 85.25238 133.7095
0.3000000 35.69048 51.33810 608.2857
0.1428571 41.65714 33.45714 629.4619
0.2809524 0.00000 20.99524 573.2048
Household_Income <- data[seq(1, nrow(data), 4), 6]
summary(Household_Income)
   Min. 1st Qu.  Median    Mean 3rd Qu.    Max. 
   2.00   20.00   34.50   34.55   50.00   72.00 
hist(Household_Income)

MNL model estimation - product attributes only

接下來,我們將剛剛資料中的三個變數(TTME, INVC, INVT)去預測每個交通工具對個人的效用。

\[\begin{align*} V_j = & \beta_{0j}+\beta_{1}\text{TTME}_j + \beta_{2}\text{INVC}_j + \beta_{3}\text{INVT}_j \end{align*}\]

\(U_j = V_j + \text{error}\) 理論上,我們必須把迴歸分析出來的線性函數加上誤差項為準,但R語言中…?

\[p_j = \frac{\exp(V_j)}{\exp(V_1)+\exp(V_2)+\exp(V_3)+\exp(V_4)},\ \ j\in\{1,2,3,4\}\] 再透過MNL將效用轉化成選擇此交通工具的機率。

由上述的公式我們可以很輕易地看出個別機率相加等於1。 \(p_1+p_2+p_3+p_4=1\)

require('mlogit')
mdata <- mlogit.data(data=data,
                     choice='MODE', # variable that contains choice
                     shape='long', # tells mlogit how data is structured (every row is alternative)
                     varying=3:5, # only select variables that describe the alternatives
                     alt.levels = c("plane", "train", "bus", "car"), # levels of the alternatives
                     id.var='TRAVELER') # consumer id
head(mdata,6)
set.seed(999)
model <- mlogit(MODE~TTME+INVC+INVT,data=mdata)
summary(model)

Call:
mlogit(formula = MODE ~ TTME + INVC + INVT, data = mdata, method = "nr", 
    print.level = 0)

Frequencies of alternatives:
  plane   train     bus     car 
0.27619 0.30000 0.14286 0.28095 

nr method
5 iterations, 0h:0m:1s 
g'(-H)^-1g = 0.000192 
successive function values within tolerance limits 

Coefficients :
                     Estimate  Std. Error z-value  Pr(>|z|)    
train:(intercept) -0.78666667  0.60260733 -1.3054   0.19174    
bus:(intercept)   -1.43363372  0.68071345 -2.1061   0.03520 *  
car:(intercept)   -4.73985647  0.86753178 -5.4636 4.665e-08 ***
TTME              -0.09688675  0.01034202 -9.3683 < 2.2e-16 ***
INVC              -0.01391160  0.00665133 -2.0916   0.03648 *  
INVT              -0.00399468  0.00084915 -4.7043 2.547e-06 ***
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

Log-Likelihood: -192.89
McFadden R^2:  0.32024 
Likelihood ratio test : chisq = 181.74 (p.value = < 2.22e-16)
model.null <- mlogit(MODE~1,data=mdata)
lrtest(model,model.null)
Likelihood ratio test

Model 1: MODE ~ TTME + INVC + INVT
Model 2: MODE ~ 1
  #Df  LogLik Df  Chisq Pr(>Chisq)    
1   6 -192.89                         
2   3 -283.76 -3 181.74  < 2.2e-16 ***
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1
kable(head(predict(model,mdata),1))
plane train bus car
0.0483305 0.3255135 0.1405072 0.4856488
predicted_alternative <- apply(predict(model,mdata),1,which.max)
selected_alternative <- rep(1:4,210)[data$MODE>0]
predicted_alternative= as.factor(predicted_alternative)
selected_alternative = as.factor(selected_alternative)
confusionMatrix(predicted_alternative,selected_alternative)
Confusion Matrix and Statistics

          Reference
Prediction  1  2  3  4
         1 39  6  3  7
         2  4 49  3  8
         3  0  1 23  0
         4 15  7  1 44

Overall Statistics
                                          
               Accuracy : 0.7381          
                 95% CI : (0.6731, 0.7962)
    No Information Rate : 0.3             
    P-Value [Acc > NIR] : <2e-16          
                                          
                  Kappa : 0.6414          
 Mcnemar's Test P-Value : 0.2118          

Statistics by Class:

                     Class: 1 Class: 2 Class: 3 Class: 4
Sensitivity            0.6724   0.7778   0.7667   0.7458
Specificity            0.8947   0.8980   0.9944   0.8477
Pos Pred Value         0.7091   0.7656   0.9583   0.6567
Neg Pred Value         0.8774   0.9041   0.9624   0.8951
Prevalence             0.2762   0.3000   0.1429   0.2810
Detection Rate         0.1857   0.2333   0.1095   0.2095
Detection Prevalence   0.2619   0.3048   0.1143   0.3190
Balanced Accuracy      0.7836   0.8379   0.8806   0.7967

Model with demographics

Now we will estimate a model that also includes a demographic variable – household income. However, we cannot just include it as an ordinary alternative-specific variable – this is because demographics for one individual would be the same across all alternatives, and so would cancel out from the probability expression as follows (so we cannot estimate the parameter \(\beta_4\))

\[\begin{align*} p_{bus} &= \frac{\exp(\cdots_{bus} + \beta_4 \text{HINC})}{\exp(\cdots_{car} + \beta_4 \text{HINC}) + \cdots + \exp(\cdots_{plane} + \beta_4 \text{HINC})}\\ &= \frac{\exp(\cdots_{bus})\exp(\beta_4 \text{HINC})}{\exp(\cdots_{car})\exp(\beta_4 \text{HINC}) + \cdots + \exp(\cdots_{plane})\exp(\beta_4 \text{HINC})}\\ &= \frac{\exp(\cdots_{bus})}{\exp(\cdots_{car}) + \cdots + \exp(\cdots_{plane})} \end{align*}\]

To deal with this issue, we need to interact the demographic variable with a dummy code for each alternative and then estimate the model. Specifically, we are now estimating utility equation where

\[\begin{align*} V_j = & \alpha_{0j} + \alpha_{1j}HouseholdIncome +\beta_{1}\text{TTME}_j + \beta_{2}\text{INVC}_j + \beta_{3}\text{INVT}_j \end{align*}\]

with intercept terms for air normalized to zero: \(\alpha_{01}=\alpha_{11}=0\). \(\alpha_{0j}\) here has the same interpretation as an intercept term in no-demographics model – that is, inherent utility of a trasportation mode relative to travel by plane. And \(\alpha_{1j}\) now measures additional (dis)utility from a trasportation mode at higher income level (again, relative to the plane).

This is how we would estimate the model

model1 <- mlogit(MODE~TTME+INVC+INVT|HINC,data=mdata)
summary(model1)

Call:
mlogit(formula = MODE ~ TTME + INVC + INVT | HINC, data = mdata, 
    method = "nr", print.level = 0)

Frequencies of alternatives:
  plane   train     bus     car 
0.27619 0.30000 0.14286 0.28095 

nr method
5 iterations, 0h:0m:0s 
g'(-H)^-1g = 0.000546 
successive function values within tolerance limits 

Coefficients :
                     Estimate  Std. Error z-value  Pr(>|z|)    
train:(intercept)  1.24212398  0.81686459  1.5206  0.128360    
bus:(intercept)   -0.18436561  0.89664384 -0.2056  0.837090    
car:(intercept)   -4.24742503  1.00650942 -4.2200 2.444e-05 ***
TTME              -0.09528341  0.01035524 -9.2015 < 2.2e-16 ***
INVC              -0.00449878  0.00721124 -0.6239  0.532722    
INVT              -0.00366471  0.00086797 -4.2222 2.420e-05 ***
train:HINC        -0.05589505  0.01535704 -3.6397  0.000273 ***
bus:HINC          -0.02311070  0.01645639 -1.4044  0.160212    
car:HINC           0.00210282  0.01209542  0.1739  0.861982    
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

Log-Likelihood: -182.22
McFadden R^2:  0.35784 
Likelihood ratio test : chisq = 203.08 (p.value = < 2.22e-16)

And here is how we can use likelihood ratio test to test the second model against the first one.

lrtest(model1,model)
Likelihood ratio test

Model 1: MODE ~ TTME + INVC + INVT | HINC
Model 2: MODE ~ TTME + INVC + INVT
  #Df  LogLik Df Chisq Pr(>Chisq)    
1   9 -182.22                        
2   6 -192.89 -3 21.34  8.948e-05 ***
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

Let us look at the new confusion matrix.

predicted_alternative <- apply(predict(model1,mdata),1,which.max)
selected_alternative <- rep(1:4,210)[data$MODE>0]
predicted_alternative= as.factor(predicted_alternative)
selected_alternative = as.factor(selected_alternative)
confusionMatrix(predicted_alternative,selected_alternative)
Confusion Matrix and Statistics

          Reference
Prediction  1  2  3  4
         1 40  6  1  7
         2  5 50  3  9
         3  0  1 23  0
         4 13  6  3 43

Overall Statistics
                                          
               Accuracy : 0.7429          
                 95% CI : (0.6782, 0.8005)
    No Information Rate : 0.3             
    P-Value [Acc > NIR] : <2e-16          
                                          
                  Kappa : 0.6477          
 Mcnemar's Test P-Value : 0.2778          

Statistics by Class:

                     Class: 1 Class: 2 Class: 3 Class: 4
Sensitivity            0.6897   0.7937   0.7667   0.7288
Specificity            0.9079   0.8844   0.9944   0.8543
Pos Pred Value         0.7407   0.7463   0.9583   0.6615
Neg Pred Value         0.8846   0.9091   0.9624   0.8897
Prevalence             0.2762   0.3000   0.1429   0.2810
Detection Rate         0.1905   0.2381   0.1095   0.2048
Detection Prevalence   0.2571   0.3190   0.1143   0.3095
Balanced Accuracy      0.7988   0.8390   0.8806   0.7916

Finally, using this model with income, we can simulate how choice share of different modes of transport will change if we reduce in-vehicle time in train by 10% (multiply it by \(0.9\)). We observe that train share increases by 5%, while bus share is most negatively affected of all modes of transport.

mdata.new <- mdata
mdata.new[seq(2,840,4),"INVT"] <- 0.9*mdata.new[seq(2,840,4),"INVT"]
predicted_alternative_new <- apply(predict(model1,mdata.new),1,which.max)
table(predicted_alternative)/210 # probability under original data
predicted_alternative
        1         2         3         4 
0.2571429 0.3190476 0.1142857 0.3095238 
table(predicted_alternative_new)/210 # probability after decrease in train travel time
predicted_alternative_new
        1         2         3         4 
0.2523810 0.3380952 0.1095238 0.3000000 
(table(predicted_alternative_new) - table(predicted_alternative))/table(predicted_alternative)
predicted_alternative_new
          1           2           3           4 
-0.01851852  0.05970149 -0.04166667 -0.03076923 

Interaction effects

Finally, we can also interact a demographic variable with product attributes. Let us do it and see whether including corresponding terms contributes to the model’s quality.

model2 <- mlogit(MODE~TTME+INVC+INVT+TTME:HINC+INVC:HINC+INVT:HINC|HINC,data=mdata)
summary(model2)

Call:
mlogit(formula = MODE ~ TTME + INVC + INVT + TTME:HINC + INVC:HINC + 
    INVT:HINC | HINC, data = mdata, method = "nr", print.level = 0)

Frequencies of alternatives:
  plane   train     bus     car 
0.27619 0.30000 0.14286 0.28095 

nr method
5 iterations, 0h:0m:0s 
g'(-H)^-1g = 1.24E-07 
gradient close to zero 

Coefficients :
                     Estimate  Std. Error z-value  Pr(>|z|)    
train:(intercept)  2.7964e+00  1.3736e+00  2.0359 0.0417642 *  
bus:(intercept)    1.3521e+00  1.4690e+00  0.9204 0.3573409    
car:(intercept)   -2.2819e+00  1.7905e+00 -1.2745 0.2024902    
TTME              -7.6820e-02  1.9786e-02 -3.8825 0.0001034 ***
INVC              -1.2090e-02  1.6351e-02 -0.7394 0.4596505    
INVT              -6.4867e-03  1.8918e-03 -3.4288 0.0006063 ***
TTME:HINC         -6.1259e-04  5.6766e-04 -1.0791 0.2805255    
INVC:HINC          2.3632e-04  4.0999e-04  0.5764 0.5643403    
INVT:HINC          7.5168e-05  4.2332e-05  1.7757 0.0757833 .  
train:HINC        -9.9571e-02  3.2746e-02 -3.0407 0.0023604 ** 
bus:HINC          -6.4966e-02  3.4688e-02 -1.8729 0.0610848 .  
car:HINC          -5.4584e-02  4.5749e-02 -1.1931 0.2328209    
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

Log-Likelihood: -180.1
McFadden R^2:  0.36529 
Likelihood ratio test : chisq = 207.31 (p.value = < 2.22e-16)
lrtest(model2,model1)
Likelihood ratio test

Model 1: MODE ~ TTME + INVC + INVT + TTME:HINC + INVC:HINC + INVT:HINC | 
    HINC
Model 2: MODE ~ TTME + INVC + INVT | HINC
  #Df  LogLik Df  Chisq Pr(>Chisq)
1  12 -180.10                     
2   9 -182.22 -3 4.2295     0.2377

We find that adding such interaction terms does not improve model significantly.

LS0tDQp0aXRsZTogJ1IgTm90ZWJvb2s6IE11bHRpbm9taWFsIExvZ2l0Jw0Kb3V0cHV0Og0KICBodG1sX25vdGVib29rOiBkZWZhdWx0DQogIGh0bWxfZG9jdW1lbnQ6IGRlZmF1bHQNCmhlYWRlci1pbmNsdWRlczogXHVzZXBhY2thZ2V7YmJtfQ0KLS0tDQoNCisg5Zyo5LiK5YCL56ug56+A5Lit77yM5oiR5YCR5a245Yiw5LqG6aGn5a6i5Zyo6Z2i5bCN5Zau5LiA55Si5ZOB5pmC77yM6YG45pOH6LK35oiW5LiN6LK3KOWFqeeorumBuOaThynlj6/nlKhsb2dpc3RpYyByZWdyZXNzaW9u6aCQ5ris44CCDQorIOS4gOiIrOeUn+a0u+S4re+8jOmdouWwjeeahOmBuOaTh+mAmuW4uOS4jeWPquS4gOeorueUouWTgeOAguS+i+WmguWPo+a4tOaDs+WWnemjsuaWmeaZgu+8jOWwseacieekpuazieawtOOAgeeTtuijnemjsuaWmeOAgeaJi+aQluadr+etieWkmueorumBuOaTh+OAgg0KKyDpgJnnqK7lpJrpoZ7liKXnmoTpgbjmk4fvvIzpnIDopoHnlKjliLBNdWx0aW5vbWlhbCBMb2dpdOW5q+WKqeaIkeWAkeWBmumgkOa4rOOAgg0KKyDlpKflrrblj6/ku6Xlm57mg7PvvIzllq7kuIDnlKLlk4Hpgbjmk4foiIflpJrph43nlKLlk4Hpgbjmk4fmmYLvvIzoqIjnrpflvI/mnIPmnInku4DpurzkuI3lkIw/DQoNCiMjIFBhY2thZ2VzDQoNCk1ha2Ugc3VyZSB0aGUgZm9sbG93aW5nIHBhY2thZ2VzIGFyZSBpbnN0YWxsZWQgYmVmb3JlIHByb2NlZWRpbmc6DQoNCjEuIHh0YWJsZSANCjIuIGtuaXRyDQozLiBtbG9naXQNCjQuIGNhcmV0DQo1LiBlMTA3MQ0KDQpgYGB7cn0NCmxpYnJhcnkoInh0YWJsZSIpICMgcHJvY2Vzc2luZyBvZiByZWdyZXNzaW9uIG91dHB1dA0KbGlicmFyeSgia25pdHIiKSAjIHVzZWQgZm9yIHJlcG9ydCBjb21waWxhdGlvbiBhbmQgdGFibGUgZGlzcGxheQ0KbGlicmFyeSgiZ2dwbG90MiIpICMgdmVyeSBwb3B1bGFyIHBsb3R0aW5nIGxpYnJhcnkgZ2dwbG90Mg0KbGlicmFyeSgiZ2d0aGVtZXMiKSAjIHRoZW1lcyBmb3IgZ2dwbG90Mg0Kc3VwcHJlc3NNZXNzYWdlcyhsaWJyYXJ5KCJtbG9naXQiKSkgIyBtdWx0aW5vbWlhbCBsb2dpdA0KbGlicmFyeSgiY2FyZXQiKQ0KYGBgDQoNCiMjIE11bHRpbm9taWFsIGxvZ2l0DQoNCk11bHRpbm9taWFsIGxvZ2l0LCBpbiBjb250cmFzdCB0byBzaW1wbGUgYmlub21pYWwgbG9naXNpYyByZWdyZXNzaW9uLCBpcyB1c2VkIGZvciBtb2RlbGluZyBjaG9pY2VzIGFtb25nIG11bHRpcGxlIGFsdGVybmF0aXZlcy4NCg0KT25jZSB0aGUgY2hvaWNlIG1vZGVsIGhhcyBiZWVuIGVzdGltYXRlZCwgd2UgY2FuIHVzZSB0aGUgcGFyYW1ldGVyIGVzdGltYXRlcyB0byBhc3Nlc3MgcmVsYXRpdmUgaW1wb3J0YW5jZSBvZiBkaWZmZXJlbnQgYXR0cmlidXRlcyBpbiBwcmVkaWN0aW5nIHRoZSBwcm9iYWJpbGl0eSBvZiBjaG9pY2UuDQoNCiMjIERhdGENCg0KWW91IHdpbGwgd29yayB3aXRoIHByb3ZpZGVkICp0cmFzcG9ydGF0aW9uX2RhdGEuY3N2KiBmaWxlLiANCg0KYGBge3J9DQpkYXRhIDwtIHJlYWQuY3N2KGZpbGUgPSAidHJhbnNwb3J0YXRpb25fZGF0YS5jc3YiKQ0KYGBgDQoNCisg5pys56ug56+A5oiR5YCR5LulMjEw5L2N5peF5a6i5bCN5pa85Lqk6YCa5bel5YW36YG45pOH5L2c54K656+E5L6L77yM5YyF5ous5pCt5LmY6aOb5qmf44CB54Gr6LuK44CB5be05aOr5oiW5piv6Ieq6aeVNOeoruWPr+iDveOAgg0KKyDmr4/kuIDlgIvkuI3lkIznmoTpgbjmk4flj6/oppbngrrkuIDlgIvmlrDnmoRyb3fvvIzlm6DmraTpgJnku73os4fmlpnnuL3lhbHmnIPmnIk4NDAoMjEwKjQpIHJvd3PjgIINCg0KYGBge3J9DQprYWJsZShoZWFkKGRhdGEsOCkpDQpgYGANCg0K55Sx5Lul5LiK6KGo5qC854K65L6L77yMMXN0fjR0aCByb3fku6PooajnrKzkuIDlgIvml4XlrqLnmoTpgbjmk4fpgY7nqIvvvIw1dGh+OHRoIHJvd+eCuuesrOS6jOWAi+aXheWuou+8jOS7peatpOmhnuaOqOOAgg0K56ys5LiA5YCLY29s5Luj6KGo5peF5a6i5pyA57WC6YG45pOH55qE5Lqk6YCa5bel5YW377yM5LulMCwx55qE5b2i5byP5ZGI54++77yM5Lim55qG5Lul5Lul5LiL5pa55byP5o6S5bqP44CCDQoxIC0g6aOb5qmfDQoyIC0g54Gr6LuKDQozIC0g5be05aOrDQo0IC0g6Ieq6aeVDQoNCuS7peesrOS4gOWAi+aXheWuoueCuuS+i++8jOS7lumBuOaTh+S6huiHqumnleOAgg0KDQrnrKwyfjTmrITkvY3liYfliIbliKXku6PooajmraTpgbjmk4fnmoQi5ZWG5ZOB54m55oCnIu+8jOWMheaLrFRUTUUsIElOVkMsIElOVlQNCg0KLSBUVE1FID0g6YG45pOH5q2k5Lqk6YCa5bel5YW36KaB562J5b6F5aSa5LmF77yM5Lul5YiG6ZCY54K65Zau5L2N44CC5Zug5q2k6Ieq6aeV54K6MA0KLSBJTlZDID0g6YG45pOH5q2k5Lqk6YCa5bel5YW35LmL6Iqx6LK777yM5Lul5YWD54K65Zau5L2NDQotIElOVlQgPSDml4XooYzmmYLplpPvvIzku6XliIbpkJjngrrllq7kvY0NCg0K5pyA5b6M5LiA5qyE5L2N5YmH5Luj6KGo5q2k5peF5a6i55qE54m55oCn77yMSElOQ+eCuuWFtuaUtuWFpe+8jOS7peWNg+WFg+eCuuWWruS9jeOAgg0KDQrmiYDmnInorormlbjnmoblgYflrprngrrpgKPnuozogIzpnZ7pm6LmlaPjgIINCg0KIyMgRGVzY3JpcHRpdmUgc3RhdGlzdGljcw0KDQorIOawuOmBoOWIpeW/mOiomO+8jOWFiOeci+eci+aVmOi/sOe1seioiOS6huino+izh+aWmee1kOaniw0KDQpgYGB7cn0NCnRyYW5zcF9kZWM8LXJiaW5kKA0KY29sU3VtcyhkYXRhW3NlcSgxLCBucm93KGRhdGEpLCA0KSwgXSkvMjEwLA0KY29sU3VtcyhkYXRhW3NlcSgyLCBucm93KGRhdGEpLCA0KSwgXSkvMjEwLA0KY29sU3VtcyhkYXRhW3NlcSgzLCBucm93KGRhdGEpLCA0KSwgXSkvMjEwLA0KY29sU3VtcyhkYXRhW3NlcSg0LCBucm93KGRhdGEpLCA0KSwgXSkvMjEwKQ0KdHJhbnNwX2RlYzwtdHJhbnNwX2RlY1ssYygyOjUpXQ0KY29sbmFtZXModHJhbnNwX2RlYykgPC0gYygnQ0hPSUNFIFNIQVJFJywnQVZHLiBXQUlUSU5HIFRUTUUnLCAnQVZHLiBDT1NUJywgJ0FWRy4gVFJBVkVMIFRJTUUnKQ0Ka2FibGUodHJhbnNwX2RlYykNCmBgYA0KDQpgYGB7cn0NCkhvdXNlaG9sZF9JbmNvbWUgPC0gZGF0YVtzZXEoMSwgbnJvdyhkYXRhKSwgNCksIDZdDQpzdW1tYXJ5KEhvdXNlaG9sZF9JbmNvbWUpDQpoaXN0KEhvdXNlaG9sZF9JbmNvbWUpDQpgYGANCg0KIyMgTU5MIG1vZGVsIGVzdGltYXRpb24gLSBwcm9kdWN0IGF0dHJpYnV0ZXMgb25seQ0KDQrmjqXkuIvkvobvvIzmiJHlgJHlsIfliZvliZvos4fmlpnkuK3nmoTkuInlgIvorormlbgoVFRNRSwgSU5WQywgSU5WVCnljrvpoJDmuKzmr4/lgIvkuqTpgJrlt6XlhbflsI3lgIvkurrnmoTmlYjnlKjjgIINCg0KIFxiZWdpbnthbGlnbip9DQpWX2ogPSAmIFxiZXRhX3swan0rXGJldGFfezF9XHRleHR7VFRNRX1faiArIFxiZXRhX3syfVx0ZXh0e0lOVkN9X2ogKyBcYmV0YV97M31cdGV4dHtJTlZUfV9qDQpcZW5ke2FsaWduKn0NCg0KJFVfaiA9IFZfaiArIFx0ZXh0e2Vycm9yfSQNCueQhuirluS4iu+8jOaIkeWAkeW/hemgiOaKiui/tOatuOWIhuaekOWHuuS+hueahOe3muaAp+WHveaVuOWKoOS4iuiqpOW3rumgheeCuua6lu+8jOS9hlLoqp7oqIDkuK3igKbvvJ8gDQoNCiQkcF9qID0gXGZyYWN7XGV4cChWX2opfXtcZXhwKFZfMSkrXGV4cChWXzIpK1xleHAoVl8zKStcZXhwKFZfNCl9LFwgXCBqXGluXHsxLDIsMyw0XH0kJA0K5YaN6YCP6YGOTU5M5bCH5pWI55So6L2J5YyW5oiQ6YG45pOH5q2k5Lqk6YCa5bel5YW355qE5qmf546H44CCDQoNCueUseS4iui/sOeahOWFrOW8j+aIkeWAkeWPr+S7peW+iOi8leaYk+WcsOeci+WHuuWAi+WIpeapn+eOh+ebuOWKoOetieaWvDHjgIIgJHBfMStwXzIrcF8zK3BfND0xJA0KDQpgYGB7cn0NCnJlcXVpcmUoJ21sb2dpdCcpDQptZGF0YSA8LSBtbG9naXQuZGF0YShkYXRhPWRhdGEsDQogICAgICAgICAgICAgICAgICAgICBjaG9pY2U9J01PREUnLCAjIHZhcmlhYmxlIHRoYXQgY29udGFpbnMgY2hvaWNlDQogICAgICAgICAgICAgICAgICAgICBzaGFwZT0nbG9uZycsICMgdGVsbHMgbWxvZ2l0IGhvdyBkYXRhIGlzIHN0cnVjdHVyZWQgKGV2ZXJ5IHJvdyBpcyBhbHRlcm5hdGl2ZSkNCiAgICAgICAgICAgICAgICAgICAgIHZhcnlpbmc9Mzo1LCAjIG9ubHkgc2VsZWN0IHZhcmlhYmxlcyB0aGF0IGRlc2NyaWJlIHRoZSBhbHRlcm5hdGl2ZXMNCiAgICAgICAgICAgICAgICAgICAgIGFsdC5sZXZlbHMgPSBjKCJwbGFuZSIsICJ0cmFpbiIsICJidXMiLCAiY2FyIiksICMgbGV2ZWxzIG9mIHRoZSBhbHRlcm5hdGl2ZXMNCiAgICAgICAgICAgICAgICAgICAgIGlkLnZhcj0nVFJBVkVMRVInKSAjIGNvbnN1bWVyIGlkDQpoZWFkKG1kYXRhLDYpDQoNCnNldC5zZWVkKDk5OSkNCm1vZGVsIDwtIG1sb2dpdChNT0RFflRUTUUrSU5WQytJTlZULGRhdGE9bWRhdGEpDQpzdW1tYXJ5KG1vZGVsKQ0KYGBgDQorIOWcqOWft+ihjE1OTOaooeWei+mgkOa4rOWJje+8jOW/hemgiOmgkOWFiOaMh+WumuWlveaooeWei+S4reeahOiuiuaVuOOAgg0KKyDms6jmhI/vvJpNTkzmqKHlnovnhKHms5XoqIjnrpflh7rntZXlsI3nmoTmlYjnlKjvvIzlm6DmraTmiJHlgJHlj6/ku6Xms6jmhI/liLDmqKHlnovmiYDpoJDkvLDlh7rkvobnmoTmiKrot53poIXku6VwbGFuZeeCuuWfuua6luS+huWBmuavlOi8g+OAgg0KDQpgYGB7cn0NCm1vZGVsLm51bGwgPC0gbWxvZ2l0KE1PREV+MSxkYXRhPW1kYXRhKQ0KbHJ0ZXN0KG1vZGVsLG1vZGVsLm51bGwpDQpgYGANCg0KKyDlhbblr6bmiJHlgJHkvp3mnIDlpJrkurrpgbjmk4fnmoTkuqTpgJrlt6XlhbfkvobpoJDmuKzmiYDmnInkurrnmoTpgbjmk4fvvIzkvoblkozmiJHlgJHliZvliZvnlKhNTkzmiYDpoJDmuKznmoTntZDmnpzkvobnlKjljaHmlrnmqqLlrprkvobmr5TovIPjgIINCisg5Y+v5Lul55m854++5oiR5YCR5LulM+WAi+iHqueUseW6puS+hueNsuW+l+Wwh+i/kTEwMCBsb2cobGlrZWxpaG9vZCnnmoTmlLnlloTjgIINCisg5o+b5Y+l6Kmx6Kqq77yM55SoTU5M5qih5Z6L5omA6aCQ5ris55qE57WQ5p6c5q+UQkzmqKHlnovmiYDpoJDmuKznmoTmnInpoa/okZflt67nlbAo6YCy5q2lKeOAgg0KDQoNCmBgYHtyfQ0Ka2FibGUoaGVhZChwcmVkaWN0KG1vZGVsLG1kYXRhKSwxKSkNCmBgYA0KDQoNCg0KYGBge3J9DQpwcmVkaWN0ZWRfYWx0ZXJuYXRpdmUgPC0gYXBwbHkocHJlZGljdChtb2RlbCxtZGF0YSksMSx3aGljaC5tYXgpDQpzZWxlY3RlZF9hbHRlcm5hdGl2ZSA8LSByZXAoMTo0LDIxMClbZGF0YSRNT0RFPjBdDQoNCnByZWRpY3RlZF9hbHRlcm5hdGl2ZT0gYXMuZmFjdG9yKHByZWRpY3RlZF9hbHRlcm5hdGl2ZSkNCnNlbGVjdGVkX2FsdGVybmF0aXZlID0gYXMuZmFjdG9yKHNlbGVjdGVkX2FsdGVybmF0aXZlKQ0KDQpjb25mdXNpb25NYXRyaXgocHJlZGljdGVkX2FsdGVybmF0aXZlLHNlbGVjdGVkX2FsdGVybmF0aXZlKQ0KYGBgDQorIOacgOW+jO+8jOaIkeWAkeS+huaqouimlk1OTOaooeWei+aJgOmgkOa4rOeahOe1kOaenOWwjeecn+Wvpue1kOaenOeahOihqOePvijmt7fmt4bnn6npmaMp44CCDQoNCiAgICArIFNlbnNpdGl2aXR5OumgkOa4rOe1kOaenFlFU+S4lOecn+Wvpue1kOaenOS5n1lFU+eahOavlOeOh+OAgg0KICAgICsgU3BlY2lmaWNpdHk6IOmgkOa4rOe1kOaenE5P5LiU55yf5a+m57WQ5p6c5LmfTk/nmoTmr5TnjofjgIINCiAgICArIFBvcyBQcmVkIFZhbHVlOg0KICAgICsgTmVnIFByZWQgVmFsdWU6DQogICAgKyBQcmV2YWxlbmNlOg0KICAgICsgRGV0ZWN0aW9uIFJhdGU6DQogICAgKyBEZXRlY3Rpb24gUHJldmFsZW5jZToNCiAgICArIEJhbGFuY2VkIEFjY3VyYWN5Og0KDQojIyBNb2RlbCB3aXRoIGRlbW9ncmFwaGljcw0KDQpOb3cgd2Ugd2lsbCBlc3RpbWF0ZSBhIG1vZGVsIHRoYXQgYWxzbyBpbmNsdWRlcyBhIGRlbW9ncmFwaGljIHZhcmlhYmxlIC0tIGhvdXNlaG9sZCBpbmNvbWUuIEhvd2V2ZXIsIHdlIGNhbm5vdCBqdXN0IGluY2x1ZGUgaXQgYXMgYW4gb3JkaW5hcnkgYWx0ZXJuYXRpdmUtc3BlY2lmaWMgdmFyaWFibGUgLS0gdGhpcyBpcyBiZWNhdXNlIGRlbW9ncmFwaGljcyBmb3Igb25lIGluZGl2aWR1YWwgd291bGQgYmUgdGhlIHNhbWUgYWNyb3NzIGFsbCBhbHRlcm5hdGl2ZXMsIGFuZCBzbyB3b3VsZCBjYW5jZWwgb3V0IGZyb20gdGhlIHByb2JhYmlsaXR5IGV4cHJlc3Npb24gYXMgZm9sbG93cyAoc28gd2UgY2Fubm90IGVzdGltYXRlIHRoZSBwYXJhbWV0ZXIgJFxiZXRhXzQkKQ0KDQpcYmVnaW57YWxpZ24qfQ0KcF97YnVzfSAmPSAgXGZyYWN7XGV4cChcY2RvdHNfe2J1c30gKyBcYmV0YV80IFx0ZXh0e0hJTkN9KX17XGV4cChcY2RvdHNfe2Nhcn0gKyBcYmV0YV80IFx0ZXh0e0hJTkN9KSArIFxjZG90cyArIFxleHAoXGNkb3RzX3twbGFuZX0gKyBcYmV0YV80IFx0ZXh0e0hJTkN9KX1cXA0KJj0gXGZyYWN7XGV4cChcY2RvdHNfe2J1c30pXGV4cChcYmV0YV80IFx0ZXh0e0hJTkN9KX17XGV4cChcY2RvdHNfe2Nhcn0pXGV4cChcYmV0YV80IFx0ZXh0e0hJTkN9KSArIFxjZG90cyArIFxleHAoXGNkb3RzX3twbGFuZX0pXGV4cChcYmV0YV80IFx0ZXh0e0hJTkN9KX1cXA0KJj0gXGZyYWN7XGV4cChcY2RvdHNfe2J1c30pfXtcZXhwKFxjZG90c197Y2FyfSkgKyBcY2RvdHMgKyBcZXhwKFxjZG90c197cGxhbmV9KX0NClxlbmR7YWxpZ24qfQ0KDQpUbyBkZWFsIHdpdGggdGhpcyBpc3N1ZSwgd2UgbmVlZCB0byBpbnRlcmFjdCB0aGUgZGVtb2dyYXBoaWMgdmFyaWFibGUgd2l0aCBhIGR1bW15IGNvZGUgZm9yIGVhY2ggYWx0ZXJuYXRpdmUgYW5kIHRoZW4gZXN0aW1hdGUgdGhlIG1vZGVsLiBTcGVjaWZpY2FsbHksIHdlIGFyZSBub3cgZXN0aW1hdGluZyB1dGlsaXR5IGVxdWF0aW9uIHdoZXJlDQoNClxiZWdpbnthbGlnbip9DQpWX2ogPSAmIFxhbHBoYV97MGp9ICsgXGFscGhhX3sxan1Ib3VzZWhvbGRJbmNvbWUgK1xiZXRhX3sxfVx0ZXh0e1RUTUV9X2ogKyBcYmV0YV97Mn1cdGV4dHtJTlZDfV9qICsgXGJldGFfezN9XHRleHR7SU5WVH1fag0KXGVuZHthbGlnbip9DQoNCndpdGggaW50ZXJjZXB0IHRlcm1zIGZvciBhaXIgbm9ybWFsaXplZCB0byB6ZXJvOiAkXGFscGhhX3swMX09XGFscGhhX3sxMX09MCQuICRcYWxwaGFfezBqfSQgaGVyZSBoYXMgdGhlIHNhbWUgaW50ZXJwcmV0YXRpb24gYXMgYW4gaW50ZXJjZXB0IHRlcm0gaW4gbm8tZGVtb2dyYXBoaWNzIG1vZGVsIC0tIHRoYXQgaXMsIGluaGVyZW50IHV0aWxpdHkgb2YgYSB0cmFzcG9ydGF0aW9uIG1vZGUgcmVsYXRpdmUgdG8gdHJhdmVsIGJ5IHBsYW5lLiBBbmQgJFxhbHBoYV97MWp9JCBub3cgbWVhc3VyZXMgYWRkaXRpb25hbCAoZGlzKXV0aWxpdHkgZnJvbSBhIHRyYXNwb3J0YXRpb24gbW9kZSBhdCBoaWdoZXIgaW5jb21lIGxldmVsIChhZ2FpbiwgcmVsYXRpdmUgdG8gdGhlIHBsYW5lKS4NCg0KVGhpcyBpcyBob3cgd2Ugd291bGQgZXN0aW1hdGUgdGhlIG1vZGVsDQoNCmBgYHtyfQ0KbW9kZWwxIDwtIG1sb2dpdChNT0RFflRUTUUrSU5WQytJTlZUfEhJTkMsZGF0YT1tZGF0YSkNCnN1bW1hcnkobW9kZWwxKQ0KYGBgDQoNCkFuZCBoZXJlIGlzIGhvdyB3ZSBjYW4gdXNlIGxpa2VsaWhvb2QgcmF0aW8gdGVzdCB0byB0ZXN0IHRoZSBzZWNvbmQgbW9kZWwgYWdhaW5zdCB0aGUgZmlyc3Qgb25lLg0KDQpgYGB7cn0NCmxydGVzdChtb2RlbDEsbW9kZWwpDQpgYGANCg0KTGV0IHVzIGxvb2sgYXQgdGhlIG5ldyBjb25mdXNpb24gbWF0cml4Lg0KDQpgYGB7cn0NCnByZWRpY3RlZF9hbHRlcm5hdGl2ZSA8LSBhcHBseShwcmVkaWN0KG1vZGVsMSxtZGF0YSksMSx3aGljaC5tYXgpDQpzZWxlY3RlZF9hbHRlcm5hdGl2ZSA8LSByZXAoMTo0LDIxMClbZGF0YSRNT0RFPjBdDQoNCnByZWRpY3RlZF9hbHRlcm5hdGl2ZT0gYXMuZmFjdG9yKHByZWRpY3RlZF9hbHRlcm5hdGl2ZSkNCnNlbGVjdGVkX2FsdGVybmF0aXZlID0gYXMuZmFjdG9yKHNlbGVjdGVkX2FsdGVybmF0aXZlKQ0KDQpjb25mdXNpb25NYXRyaXgocHJlZGljdGVkX2FsdGVybmF0aXZlLHNlbGVjdGVkX2FsdGVybmF0aXZlKQ0KYGBgDQoNCkZpbmFsbHksIHVzaW5nIHRoaXMgbW9kZWwgd2l0aCBpbmNvbWUsIHdlIGNhbiBzaW11bGF0ZSBob3cgY2hvaWNlIHNoYXJlIG9mIGRpZmZlcmVudCBtb2RlcyBvZiB0cmFuc3BvcnQgd2lsbCBjaGFuZ2UgaWYgd2UgcmVkdWNlIGluLXZlaGljbGUgdGltZSBpbiB0cmFpbiBieSAxMCUgKG11bHRpcGx5IGl0IGJ5ICQwLjkkKS4gV2Ugb2JzZXJ2ZSB0aGF0IHRyYWluIHNoYXJlIGluY3JlYXNlcyBieSA1XCUsIHdoaWxlIGJ1cyBzaGFyZSBpcyBtb3N0IG5lZ2F0aXZlbHkgYWZmZWN0ZWQgb2YgYWxsIG1vZGVzIG9mIHRyYW5zcG9ydC4NCg0KYGBge3J9DQptZGF0YS5uZXcgPC0gbWRhdGENCm1kYXRhLm5ld1tzZXEoMiw4NDAsNCksIklOVlQiXSA8LSAwLjkqbWRhdGEubmV3W3NlcSgyLDg0MCw0KSwiSU5WVCJdDQpwcmVkaWN0ZWRfYWx0ZXJuYXRpdmVfbmV3IDwtIGFwcGx5KHByZWRpY3QobW9kZWwxLG1kYXRhLm5ldyksMSx3aGljaC5tYXgpDQoNCnRhYmxlKHByZWRpY3RlZF9hbHRlcm5hdGl2ZSkvMjEwICMgcHJvYmFiaWxpdHkgdW5kZXIgb3JpZ2luYWwgZGF0YQ0KdGFibGUocHJlZGljdGVkX2FsdGVybmF0aXZlX25ldykvMjEwICMgcHJvYmFiaWxpdHkgYWZ0ZXIgZGVjcmVhc2UgaW4gdHJhaW4gdHJhdmVsIHRpbWUNCg0KKHRhYmxlKHByZWRpY3RlZF9hbHRlcm5hdGl2ZV9uZXcpIC0gdGFibGUocHJlZGljdGVkX2FsdGVybmF0aXZlKSkvdGFibGUocHJlZGljdGVkX2FsdGVybmF0aXZlKQ0KYGBgDQoNCiMjIEludGVyYWN0aW9uIGVmZmVjdHMNCg0KRmluYWxseSwgd2UgY2FuIGFsc28gaW50ZXJhY3QgYSBkZW1vZ3JhcGhpYyB2YXJpYWJsZSB3aXRoIHByb2R1Y3QgYXR0cmlidXRlcy4gTGV0IHVzIGRvIGl0IGFuZCBzZWUgd2hldGhlciBpbmNsdWRpbmcgY29ycmVzcG9uZGluZyB0ZXJtcyBjb250cmlidXRlcyB0byB0aGUgbW9kZWwncyBxdWFsaXR5Lg0KDQpgYGB7cn0NCm1vZGVsMiA8LSBtbG9naXQoTU9ERX5UVE1FK0lOVkMrSU5WVCtUVE1FOkhJTkMrSU5WQzpISU5DK0lOVlQ6SElOQ3xISU5DLGRhdGE9bWRhdGEpDQpzdW1tYXJ5KG1vZGVsMikNCmxydGVzdChtb2RlbDIsbW9kZWwxKQ0KYGBgDQoNCldlIGZpbmQgdGhhdCBhZGRpbmcgc3VjaCBpbnRlcmFjdGlvbiB0ZXJtcyBkb2VzIG5vdCBpbXByb3ZlIG1vZGVsIHNpZ25pZmljYW50bHku