rm(list=ls(all=T))
options(digits=3, scipen=40)
library(caTools)
library(magrittr)



1. 價量關係

\[ \sigma = \sqrt{\frac{s^2 \pi^2}{3}} \quad ;\quad s = \sqrt{\frac{3\sigma^2}{\pi^2}}\]

par(cex=0.7, mfcol=c(2,2), mar=c(4,4,4,2))
mu=100; sigma=10; s = sqrt(3 * sigma^2 / pi^2)
curve(dlogis(x,mu,s),60,140,col='blue',lwd=2,xlab="",ylab="",
      main="Logistic Dist. over WTP")
curve(1-plogis(x,mu,s),60,140,col='cyan',lwd=2,xlab="",ylab="",
      main="Logistic Price Response Function") 
curve(dnorm(x,mu,sigma),60,140,col='blue',lwd=2,xlab="",ylab="",
      main="Normal Dist. over WTP")
curve(1-pnorm(x,mu,sigma),60,140,col='cyan',lwd=2,xlab="",ylab="",
      main="Probit Price Response Function")


2. 顧客價值與顧客選擇

\(value = utility - price = V\)

\(V \sim \mathit{logistic} \rightarrow Pr[buy] \sim \mathit{Logistic}\)

\(Pr[buy] = \frac{1}{1+exp(-V)}\)

par(cex=0.8, mar=c(4,4,4,2))
curve(plogis(x,0,4),-30,30,col='cyan',lwd=3,xlab="Value",ylab="Prob[buy]",
      main="Logistic Function")
abline(v=seq(-30,30,5), h=seq(0,1,0.1), col='lightgray',lty=3)
points(0,0.5,pch=20,cex=2.5,col='orange')


2.1 Where does the Value come from?

Value as a function of product attribures - \(V(x_1, x_2, ..., price)\) where \(x\)’s are product option items - - -

3. 顧客選擇模型

1.將資料中要加入的變數先做線性迴歸模型,得出linear function: \(f(x)=y=b_0+b_1 x_1+⋯+b_k x_k\) 這裡的概念是透過一些屬性\((x_1,x_2,x_3…,x_k)\)來得出顧客對這次訂單的效用(y)

2.由剛剛線性迴歸得出的效用(y)代入logistic function可轉換出「購買機率(p)」 p= 1/(1+Exp(-f(X)))

接下來以一份顧客資料來實際操作模型。

bd <- read.csv("RawShort1.csv")
head(bd)

3.1 Modeling

mod1 <- glm(Order ~ Time+Quantity+PricePerLb, data=bd, family=binomial)
summary(mod1)

Call:
glm(formula = Order ~ Time + Quantity + PricePerLb, family = binomial, 
    data = bd)

Deviance Residuals: 
   Min      1Q  Median      3Q     Max  
-1.332  -1.083  -0.805   1.221   3.175  

Coefficients:
            Estimate Std. Error z value            Pr(>|z|)    
(Intercept)  0.64640    0.05509    11.7 <0.0000000000000002 ***
Time        -0.03106    0.00259   -12.0 <0.0000000000000002 ***
Quantity    -0.48567    0.02522   -19.3 <0.0000000000000002 ***
PricePerLb  -0.19365    0.01514   -12.8 <0.0000000000000002 ***
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

(Dispersion parameter for binomial family taken to be 1)

    Null deviance: 21532  on 15886  degrees of freedom
Residual deviance: 20702  on 15883  degrees of freedom
AIC: 20710

Number of Fisher Scoring iterations: 5

3.2 Regression Coefficient 迴歸係數

coef(mod1)
(Intercept)        Time    Quantity  PricePerLb 
     0.6464     -0.0311     -0.4857     -0.1936 


\[V(x) = 0.646 - 0.031 Time - 0.4857 Qty - 0.1937 Price\] \[\text{Logit (log odd):} \quad V(x) = b_0 + b_1 x_1 + ... + b_k x_k = logit = log(odd) = log(\frac{p}{1-p})\]

\[\text{Prob. of Buying:} \quad p = \mathfrak{L}(logit) = \frac{1}{1+exp(-V)}\]

  • 觀念複習:迴歸結果的係數會如何影Prob. Logit與Odd呢?

3.3 Prediction

bd$logOdd = predict(mod1, bd)
bd$ProbBuy = predict(mod1, bd, type='response')
head(bd)
  • 若在指令中未鍵入”type=’response’”,R會計算出顧客價值(V)。
  • 反之,若在指令中鍵入”type=’response’”則會轉化成購買機率。
1/(1+exp(-bd$logOdd[1:6]))
[1] 0.149 0.367 0.472 0.362 0.505 0.387
  • Q: 這行的目的?

3.4 Marginal Effect of Predictors

從迴歸係數估計預測變數對購買機率的邊際效果

\[ \text{odd} = e^{b_0+b_1x_1} \\ e^{b_0+b_1(x_1+1)} = e^{b_0+b_1x_1+b_1} = e^{b_0+b_1x_1} \times e^{b_1}\]

迴歸係數可以透過指數函數轉換為勝率比

  • 結論:logit的邊際效果不用代入指數函數,以變數前的係數即可衡量;odd的邊際效果則要代入指數函數。
coef(mod1) %>% exp
(Intercept)        Time    Quantity  PricePerLb 
      1.909       0.969       0.615       0.824 

注意:勝率和機率之間沒有固定的比率關係

\[ o = \frac{p}{1-p} \quad ; \quad p = \frac{o}{1+o} \]

par(mfcol= c(1,2), cex=0.8)
curve(x/(1-x), 0.01, 0.99, xlab="prob", ylab="odd")
curve(x/(1+x), 0.01, 100, xlab="odd", ylab="prob")

k = 2
p = seq(0.01, 0.99, 0.01)
o = p/(1-p)
pko = (k*o)/(1+k*o)
plot(p, pko - p, type='l', xlab="prob", ylab="delta prob", lwd=2, col="cyan")

par(cex=0.8)
plot(0.5,0,pch=20,col='gray',xlim=c(0,1),ylim=c(-0.8,0.8),
     xlab="base prob.",ylab="prob. increment",
     main = "Odd Ratio = 50, 10, 5, 2, 1, 1/2, 1/5, 1/10, 1/50")
abline(h=seq(-0.8,0.8,0.1), v=seq(0,1,0.05), col='lightgray', lty=3 )
p = seq(0.01, 0.99, 0.01)
for(k in c(1/50, 1/10, 1/5, 1/2, 1, 2, 5, 10, 50)) {
  o = p/(1-p)
  pko = (k*o)/(1+k*o)
  lines(p, pko - p, lwd=2)  
}

  • 給定每個不同機率p,在odd比值的改變下,所影響p的效果也會不一樣。
par(cex=0.8)
plot(0.5,0.5,pch=20,col='gray',xlim=c(0,1),ylim=c(0,1),
     xlab="base prob.",ylab="resultant prob.",
     main = "Odd Ratio = 50, 10, 5, 2, 1, 1/2, 1/5, 1/10, 1/50")
abline(h=seq(0,1,0.1), v=seq(0,1,0.1), col='lightgray', lty=3 )
p = seq(0.01, 0.99, 0.01)
for(k in c(1/50, 1/10, 1/5, 1/2, 1, 2, 5, 10, 50)) {
  o = p/(1-p)
  pko = (k*o)/(1+k*o)
  lines(p, pko, lwd=2)  
}


4. 客製化報價

expProfit =  function(price, mod, data) {
  data$PricePerLb = price
  (price - data$CostPerLb) * predict(mod, data, type="response")
}
expProfit(4.5, mod1, bd[1,])
   1 
0.29 
mean(bd$PricePerLb)
[1] 3.15
optim(4, expProfit, method="BFGS", control=list(fnscale=-1), 
      mod=mod1, data=bd[1,] )
$`par`
[1] 7.09

$value
[1] 0.345

$counts
function gradient 
       8        7 

$convergence
[1] 0

$message
NULL
bd[1,]
price = seq(4, 10, 0.2)
prob = predict(mod1, data.frame(Time=9, Quantity=3.5, PricePerLb=price), type="response")
expReturn = prob * (price - 1.58)
plot(price, expReturn , type='l')

5. 行為經濟模型(是否刪除?)

5.1 Modeling

bd$gain = (bd$LagPrice - bd$PricePerLb)*(bd$LagPrice > bd$PricePerLb)
bd$loss = (bd$PricePerLb - bd$LagPrice)*(bd$PricePerLb > bd$LagPrice)
mod2 = glm(Order ~ Time+Quantity+gain+loss+Quantity:gain+Quantity:loss, 
            data=bd, family=binomial)
summary(mod2)

Call:
glm(formula = Order ~ Time + Quantity + gain + loss + Quantity:gain + 
    Quantity:loss, family = binomial, data = bd)

Deviance Residuals: 
   Min      1Q  Median      3Q     Max  
-1.544  -1.081  -0.751   1.206   3.176  

Coefficients:
              Estimate Std. Error z value             Pr(>|z|)    
(Intercept)    0.13223    0.02789    4.74           0.00000213 ***
Time          -0.03248    0.00261  -12.43 < 0.0000000000000002 ***
Quantity      -0.50230    0.03079  -16.31 < 0.0000000000000002 ***
gain           0.10646    0.02124    5.01           0.00000054 ***
loss          -0.32350    0.02513  -12.87 < 0.0000000000000002 ***
Quantity:gain  0.05545    0.01818    3.05               0.0023 ** 
Quantity:loss -0.13806    0.07437   -1.86               0.0634 .  
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

(Dispersion parameter for binomial family taken to be 1)

    Null deviance: 21532  on 15886  degrees of freedom
Residual deviance: 20475  on 15880  degrees of freedom
AIC: 20489

Number of Fisher Scoring iterations: 5

5.2 Model Comparison

par(cex=0.8)
data.frame(
  model1 = predict(mod1, bd, type="response"),
  model2 = predict(mod2, bd, type="response")
  ) %>% colAUC(bd$Order, plotROC=TRUE)
        model1 model2
0 vs. 1  0.614  0.633

5.3 Optimization/Customization

expProfit2 =  function(price, mod, data) {
  data$gain = (price - data$PricePerLb)*(data$LagPrice > price)
  data$loss = (price - data$LagPrice)*(price > data$LagPrice)
  (price - data$CostPerLb) * predict(mod, data, type="response")
}
expProfit2(4.5, mod2, bd[1,])
     1 
0.0655 
optim(4, expProfit2, method="BFGS", control=list(fnscale=-1), 
      mod=mod2, data=bd[1,] )
$`par`
[1] 2.93

$value
[1] 0.101

$counts
function gradient 
      10        7 

$convergence
[1] 0

$message
NULL





LS0tDQp0aXRsZTogIumhp+WuouWDueWAvOOAgemhp+WuoumBuOaTh+OAgeWuouijveWMluWgseWDuSINCmF1dGhvcjogInRvbnljaHVvQG1haWwubnN5c3UuZWR1LnR3Ig0KZGF0ZTogImByIFN5cy50aW1lKClgIg0Kb3V0cHV0OiBodG1sX25vdGVib29rDQotLS0NCisg5bCN5pa86aGn5a6i6aGY5LuY5YO55qC85LmLcGRm77yM54K65L2V5Zyobm9ybWFsIGRpc3Qu6IiHbG9naXN0aWMgZGlzdC7kuK3pgbjmk4flvozogIXlkaI/DQorIOWJjeaDheaPkOimgTog5LiK5qyh5oiR5YCR6Yed5bCN5biC5aC05LmL5YO55qC85Y+N5oeJ5Ye95pW477yM5o6o5Lyw5Ye65pyA6auY5Yip5r2k5LmL5a6a5YO577yM5L2G6YCZ5piv5bCN5biC5aC05omA5pyJ5raI6LK76ICF57Wx5LiA5a6a5YO544CCDQorIOWmguaenOWPr+S7peagueaTmuavj+WAi+a2iOiyu+iAheaJgOaEn+imuueahCLlg7nlgLwi77yM5bCN5LuW5YCR5Yi25a6a5LiN5ZCM5YO55qC877yM55u45bCN5pa857Wx5LiA5YO55qC85pyJ5pu06auY5Yip55uK44CCDQoNCjxicj4NCmBgYHtyIHNldC1vcHRpb25zLCBlY2hvPUZBTFNFLCBjYWNoZT1GQUxTRX0NCmxpYnJhcnkoa25pdHIpDQpvcHRpb25zKHdpZHRoPTEwMCkNCm9wdHNfY2h1bmskc2V0KGNvbW1lbnQgPSBOQSkNCmBgYA0KDQpgYGB7ciAgd2FybmluZz1GLCBtZXNzYWdlPUYsIGNhY2hlPUYsIGVycm9yPUZ9DQpybShsaXN0PWxzKGFsbD1UKSkNCm9wdGlvbnMoZGlnaXRzPTMsIHNjaXBlbj00MCkNCmxpYnJhcnkoY2FUb29scykNCmxpYnJhcnkobWFncml0dHIpDQpgYGANCjxicj4NCg0KLSAtIC0NCg0KIyMjIDEuIOWDuemHj+mXnOS/gg0KDQokJCBcc2lnbWEgPSBcc3FydHtcZnJhY3tzXjIgXHBpXjJ9ezN9fSBccXVhZCA7XHF1YWQgDQpzID0gXHNxcnR7XGZyYWN7M1xzaWdtYV4yfXtccGleMn19JCQNCg0KYGBge3IgZmlnLndpZHRoPTcsIGZpZy5oZWlnaHQ9Nn0NCnBhcihjZXg9MC43LCBtZmNvbD1jKDIsMiksIG1hcj1jKDQsNCw0LDIpKQ0KbXU9MTAwOyBzaWdtYT0xMDsgcyA9IHNxcnQoMyAqIHNpZ21hXjIgLyBwaV4yKQ0KY3VydmUoZGxvZ2lzKHgsbXUscyksNjAsMTQwLGNvbD0nYmx1ZScsbHdkPTIseGxhYj0iIix5bGFiPSIiLA0KICAgICAgbWFpbj0iTG9naXN0aWMgRGlzdC4gb3ZlciBXVFAiKQ0KY3VydmUoMS1wbG9naXMoeCxtdSxzKSw2MCwxNDAsY29sPSdjeWFuJyxsd2Q9Mix4bGFiPSIiLHlsYWI9IiIsDQogICAgICBtYWluPSJMb2dpc3RpYyBQcmljZSBSZXNwb25zZSBGdW5jdGlvbiIpIA0KY3VydmUoZG5vcm0oeCxtdSxzaWdtYSksNjAsMTQwLGNvbD0nYmx1ZScsbHdkPTIseGxhYj0iIix5bGFiPSIiLA0KICAgICAgbWFpbj0iTm9ybWFsIERpc3QuIG92ZXIgV1RQIikNCmN1cnZlKDEtcG5vcm0oeCxtdSxzaWdtYSksNjAsMTQwLGNvbD0nY3lhbicsbHdkPTIseGxhYj0iIix5bGFiPSIiLA0KICAgICAgbWFpbj0iUHJvYml0IFByaWNlIFJlc3BvbnNlIEZ1bmN0aW9uIikNCmBgYA0KDQotIC0gLQ0KDQojIyMgMi4g6aGn5a6i5YO55YC86IiH6aGn5a6i6YG45pOHDQoNCisg5LuA6bq85piv6aGn5a6i5YO55YC8KFZhbHVlKT8g5q+P5YCL6aGn5a6i5YO55YC855qE5YiG5biD5pyD6ZW35LuA6bq85qij5a2QPw0KKyDmiJHlgJHmnInovqbms5Xnn6XpgZPmr4/lgIvpoaflrqLlg7nlgLzll44/DQorIOS9v+eUqOmCj+i8r+W8j+i/tOatuOWPr+S+neaTmumhp+WuouizvOiyt+S5i+apn+eOhyhQcm9iYWJpbGl0eSnvvIzpoJDmuKzliLDmnIDlvozpoaflrqLmnInmspLmnInosrcNCg0KJHZhbHVlID0gdXRpbGl0eSAtIHByaWNlID0gViQNCg0KJFYgXHNpbSBcbWF0aGl0e2xvZ2lzdGljfSBccmlnaHRhcnJvdyBQcltidXldIFxzaW0gXG1hdGhpdHtMb2dpc3RpY30kDQoNCiRQcltidXldID0gXGZyYWN7MX17MStleHAoLVYpfSQNCg0KYGBge3J9DQpwYXIoY2V4PTAuOCwgbWFyPWMoNCw0LDQsMikpDQpjdXJ2ZShwbG9naXMoeCwwLDQpLC0zMCwzMCxjb2w9J2N5YW4nLGx3ZD0zLHhsYWI9IlZhbHVlIix5bGFiPSJQcm9iW2J1eV0iLA0KICAgICAgbWFpbj0iTG9naXN0aWMgRnVuY3Rpb24iKQ0KYWJsaW5lKHY9c2VxKC0zMCwzMCw1KSwgaD1zZXEoMCwxLDAuMSksIGNvbD0nbGlnaHRncmF5JyxsdHk9MykNCnBvaW50cygwLDAuNSxwY2g9MjAsY2V4PTIuNSxjb2w9J29yYW5nZScpDQpgYGANCjxicj4NCg0KKyDlr6bli5nkuIrvvIzlm6DngrrnhKHms5Xlj5blvpfpoaflrqLlg7nlgLwoVmFsdWUp55qE5YiG5biD77yM5L2G5oiR5YCR5Y+v5Lul55+l6YGT55W25LiA5YCL55Si5ZOB55qE5YO55qC85ZKM5pWI55So5LiA5qij55qE5pmC5YCZ77yM6aGn5a6i6YG45pOH6LO86LK355qE5qmf546H5q2j5pivMC4144CCKOWcluS4reeahOapmOm7nikNCisg6ICM5bCH6aGn5a6i5YO55YC86IiH6LO86LK35qmf546H55qE6Zec5L+C5piv6YCP6YGOTG9naXN0aWMgZnVuY3Rpb27ovYnmj5vlvpfnn6XvvIzogIzmraTlnJbliYfmmK9Mb2dpc3RpYyBmdW5jdGlvbueahOWHveaVuOWcluW9ouOAgg0KDQoNCiMjIyMgMi4xIFdoZXJlIGRvZXMgdGhlIFZhbHVlIGNvbWUgZnJvbT8NClZhbHVlIGFzIGEgZnVuY3Rpb24gb2YgcHJvZHVjdCBhdHRyaWJ1cmVzIC0gJFYoeF8xLCB4XzIsIC4uLiwgcHJpY2UpJA0Kd2hlcmUgJHgkJ3MgYXJlIHByb2R1Y3Qgb3B0aW9uIGl0ZW1zDQotIC0gLQ0KDQojIyMgMy4g6aGn5a6i6YG45pOH5qih5Z6LDQoNCjEu5bCH6LOH5paZ5Lit6KaB5Yqg5YWl55qE6K6K5pW45YWI5YGa57ea5oCn6L+05q245qih5Z6L77yM5b6X5Ye6bGluZWFyIGZ1bmN0aW9uOg0KICRmKHgpPXk9Yl8wK2JfMSB4XzEr4ouvK2JfayB4X2skDQrpgJnoo6HnmoTmpoLlv7XmmK/pgI/pgY7kuIDkupvlsazmgKckKHhfMSx4XzIseF8z4oCmLHhfaykk5L6G5b6X5Ye66aGn5a6i5bCN6YCZ5qyh6KiC5Zau55qE5pWI55SoKHkpDQoNCjIu55Sx5Ymb5Ymb57ea5oCn6L+05q245b6X5Ye655qE5pWI55SoKHkp5Luj5YWlbG9naXN0aWMgZnVuY3Rpb27lj6/ovYnmj5vlh7rjgIzos7zosrfmqZ/njococCnjgI0NCnA9IDEvKDErRXhwKC1mKFgpKSkNCg0KKyDoo5zlhYXoqqrmmI7vvJoNCiAgICArCXDngrros7zosrfmqZ/njofvvJtvZGTngrrli53nrpcob2RkPXAvKDEtcCkp77ybTG9naXTmmK/li53nrpflj5Zsb2fnmoTlgLzjgIIo5omA5Lul6YKP6Lyv5byP6L+05q246Lef6YKP6Lyv5a6M5YWo54Sh6Zec77yBKQ0KICAgICsg6YCZ5LiJ6ICFKHAsb2RkLGxvZ2l0KeS6i+WvpuS4iueahuaYr+apn+eOh+eahOamguW/te+8jOS9huaJgOimgeihqOmBlOeahOacrOizquS4jeWQjOOAgg0KICAgICsg5pWI55SodmFsdWUoeSnlhbblr6bliZvlpb3mmK9sbmEob2RkKeeahOe1kOaenOOAgg0KDQrmjqXkuIvkvobku6XkuIDku73poaflrqLos4fmlpnkvoblr6bpmpvmk43kvZzmqKHlnovjgIINCg0KDQpgYGB7cn0NCmJkIDwtIHJlYWQuY3N2KCJSYXdTaG9ydDEuY3N2IikNCmhlYWQoYmQpDQpgYGANCg0KDQoNCiMjIyMgMy4xIE1vZGVsaW5nDQpgYGB7cn0NCm1vZDEgPC0gZ2xtKE9yZGVyIH4gVGltZStRdWFudGl0eStQcmljZVBlckxiLCBkYXRhPWJkLCBmYW1pbHk9Ymlub21pYWwpDQpzdW1tYXJ5KG1vZDEpDQpgYGANCg0KIyMjIyAzLjIgUmVncmVzc2lvbiBDb2VmZmljaWVudCDov7Tmrbjkv4LmlbgNCg0KYGBge3J9DQpjb2VmKG1vZDEpDQpgYGANCjxicj4NCg0KJCRWKHgpID0gMC42NDYgLSAwLjAzMSBUaW1lIC0gMC40ODU3IFF0eSAtIDAuMTkzNyBQcmljZSQkDQokJFx0ZXh0e0xvZ2l0IChsb2cgb2RkKTp9IFxxdWFkIA0KVih4KSA9IGJfMCArIGJfMSB4XzEgKyAuLi4gKyBiX2sgeF9rID0gbG9naXQgPSBsb2cob2RkKSA9IGxvZyhcZnJhY3twfXsxLXB9KSQkDQoNCiQkXHRleHR7UHJvYi4gb2YgQnV5aW5nOn0gXHF1YWQgcCA9IFxtYXRoZnJha3tMfShsb2dpdCkgPSBcZnJhY3sxfXsxK2V4cCgtVil9JCQNCg0KKyDop4Dlv7XopIfnv5I66L+05q2457WQ5p6c55qE5L+C5pW45pyD5aaC5L2V5b2xUHJvYi4gTG9naXToiIdPZGTlkaI/DQoNCiMjIyMgMy4zIFByZWRpY3Rpb24NCmBgYHtyfQ0KYmQkbG9nT2RkID0gcHJlZGljdChtb2QxLCBiZCkNCmJkJFByb2JCdXkgPSBwcmVkaWN0KG1vZDEsIGJkLCB0eXBlPSdyZXNwb25zZScpDQpoZWFkKGJkKQ0KYGBgDQorIOiLpeWcqOaMh+S7pOS4reacqumNteWFpeKAnXR5cGU94oCZcmVzcG9uc2XigJnigJ3vvIxS5pyD6KiI566X5Ye66aGn5a6i5YO55YC8KFYp44CCDQorIOWPjeS5i++8jOiLpeWcqOaMh+S7pOS4remNteWFpeKAnXR5cGU94oCZcmVzcG9uc2XigJnigJ3liYfmnIPovYnljJbmiJDos7zosrfmqZ/njofjgIINCg0KYGBge3J9DQoxLygxK2V4cCgtYmQkbG9nT2RkWzE6Nl0pKQ0KYGBgDQorIFE6IOmAmeihjOeahOebrueahO+8nw0KDQojIyMjIDMuNCBNYXJnaW5hbCBFZmZlY3Qgb2YgUHJlZGljdG9ycyANCg0K5b6e6L+05q245L+C5pW45Lyw6KiI6aCQ5ris6K6K5pW45bCN6LO86LK35qmf546H55qE6YKK6Zqb5pWI5p6cDQoNCiQkIFx0ZXh0e29kZH0gPSBlXntiXzArYl8xeF8xfSBcXA0KZV57Yl8wK2JfMSh4XzErMSl9ID0gZV57Yl8wK2JfMXhfMStiXzF9ID0gZV57Yl8wK2JfMXhfMX0gXHRpbWVzIGVee2JfMX0kJA0KDQrov7Tmrbjkv4Lmlbjlj6/ku6XpgI/pgY7mjIfmlbjlh73mlbjovYnmj5vngrrli53njofmr5QNCg0KKyDntZDoq5bvvJpsb2dpdOeahOmCiumam+aViOaenOS4jeeUqOS7o+WFpeaMh+aVuOWHveaVuO+8jOS7peiuiuaVuOWJjeeahOS/guaVuOWNs+WPr+ihoemHj++8m29kZOeahOmCiumam+aViOaenOWJh+imgeS7o+WFpeaMh+aVuOWHveaVuOOAgg0KDQpgYGB7cn0NCmNvZWYobW9kMSkgJT4lIGV4cA0KYGBgDQoNCuazqOaEj++8muWLneeOh+WSjOapn+eOh+S5i+mWk+aykuacieWbuuWumueahOavlOeOh+mXnOS/gg0KDQokJCBvID0gXGZyYWN7cH17MS1wfSBccXVhZCA7IFxxdWFkIHAgPSBcZnJhY3tvfXsxK299ICQkIA0KDQpgYGB7ciBmaWcuaGVpZ2h0PTMsIGZpZy53aWR0aD03fQ0KcGFyKG1mY29sPSBjKDEsMiksIGNleD0wLjgpDQpjdXJ2ZSh4LygxLXgpLCAwLjAxLCAwLjk5LCB4bGFiPSJwcm9iIiwgeWxhYj0ib2RkIikNCmN1cnZlKHgvKDEreCksIDAuMDEsIDEwMCwgeGxhYj0ib2RkIiwgeWxhYj0icHJvYiIpDQpgYGANCg0KYGBge3J9DQprID0gMg0KcCA9IHNlcSgwLjAxLCAwLjk5LCAwLjAxKQ0KbyA9IHAvKDEtcCkNCnBrbyA9IChrKm8pLygxK2sqbykNCnBsb3QocCwgcGtvIC0gcCwgdHlwZT0nbCcsIHhsYWI9InByb2IiLCB5bGFiPSJkZWx0YSBwcm9iIiwgbHdkPTIsIGNvbD0iY3lhbiIpDQpgYGANCg0KYGBge3IgZmlnLmhlaWdodD00LjUsIGZpZy53aWR0aD02fQ0KcGFyKGNleD0wLjgpDQpwbG90KDAuNSwwLHBjaD0yMCxjb2w9J2dyYXknLHhsaW09YygwLDEpLHlsaW09YygtMC44LDAuOCksDQogICAgIHhsYWI9ImJhc2UgcHJvYi4iLHlsYWI9InByb2IuIGluY3JlbWVudCIsDQogICAgIG1haW4gPSAiT2RkIFJhdGlvID0gNTAsIDEwLCA1LCAyLCAxLCAxLzIsIDEvNSwgMS8xMCwgMS81MCIpDQphYmxpbmUoaD1zZXEoLTAuOCwwLjgsMC4xKSwgdj1zZXEoMCwxLDAuMDUpLCBjb2w9J2xpZ2h0Z3JheScsIGx0eT0zICkNCnAgPSBzZXEoMC4wMSwgMC45OSwgMC4wMSkNCmZvcihrIGluIGMoMS81MCwgMS8xMCwgMS81LCAxLzIsIDEsIDIsIDUsIDEwLCA1MCkpIHsNCiAgbyA9IHAvKDEtcCkNCiAgcGtvID0gKGsqbykvKDEraypvKQ0KICBsaW5lcyhwLCBwa28gLSBwLCBsd2Q9MikgIA0KfQ0KYGBgDQorIOe1puWumuavj+WAi+S4jeWQjOapn+eOh3DvvIzlnKhvZGTmr5TlgLznmoTmlLnororkuIvvvIzmiYDlvbHpn79w55qE5pWI5p6c5Lmf5pyD5LiN5LiA5qij44CCDQoNCmBgYHtyIGZpZy5oZWlnaHQ9NC41LCBmaWcud2lkdGg9Nn0NCnBhcihjZXg9MC44KQ0KcGxvdCgwLjUsMC41LHBjaD0yMCxjb2w9J2dyYXknLHhsaW09YygwLDEpLHlsaW09YygwLDEpLA0KICAgICB4bGFiPSJiYXNlIHByb2IuIix5bGFiPSJyZXN1bHRhbnQgcHJvYi4iLA0KICAgICBtYWluID0gIk9kZCBSYXRpbyA9IDUwLCAxMCwgNSwgMiwgMSwgMS8yLCAxLzUsIDEvMTAsIDEvNTAiKQ0KYWJsaW5lKGg9c2VxKDAsMSwwLjEpLCB2PXNlcSgwLDEsMC4xKSwgY29sPSdsaWdodGdyYXknLCBsdHk9MyApDQpwID0gc2VxKDAuMDEsIDAuOTksIDAuMDEpDQpmb3IoayBpbiBjKDEvNTAsIDEvMTAsIDEvNSwgMS8yLCAxLCAyLCA1LCAxMCwgNTApKSB7DQogIG8gPSBwLygxLXApDQogIHBrbyA9IChrKm8pLygxK2sqbykNCiAgbGluZXMocCwgcGtvLCBsd2Q9MikgIA0KfQ0KYGBgDQoNCi0gLSAtDQoNCiMjIyA0LiDlrqLoo73ljJbloLHlg7kNCg0KYGBge3J9DQpleHBQcm9maXQgPSAgZnVuY3Rpb24ocHJpY2UsIG1vZCwgZGF0YSkgew0KICBkYXRhJFByaWNlUGVyTGIgPSBwcmljZQ0KICAocHJpY2UgLSBkYXRhJENvc3RQZXJMYikgKiBwcmVkaWN0KG1vZCwgZGF0YSwgdHlwZT0icmVzcG9uc2UiKQ0KfQ0KZXhwUHJvZml0KDQuNSwgbW9kMSwgYmRbMSxdKQ0KYGBgDQorIOWFiOeUqOesrOS4gOethumhp+WuouS+huWBmmRlbW/igKYNCg0KYGBge3J9DQptZWFuKGJkJFByaWNlUGVyTGIpDQpvcHRpbSg0LCBleHBQcm9maXQsIG1ldGhvZD0iQkZHUyIsIGNvbnRyb2w9bGlzdChmbnNjYWxlPS0xKSwgDQogICAgICBtb2Q9bW9kMSwgZGF0YT1iZFsxLF0gKQ0KYGBgDQorIG9wdGlt55qE5Y+D5pW46KaB5YWI57Wm5LiA5YCL5Yid5aeL5YC877yM6YCZ6YKK5Lul6LOH5paZcHJpY2XlubPlnYflgLzpmYTov5HkuYvlgLzngrrlj4PogIPjgIINCisg5b6e57WQ5p6c5b6X55+l5bCN56ys5LiA5YCL6aGn5a6i55qE5pyA6YGp5YO55qC854K6Ny4wOeWFg++8jOeNsuWIqeeCujAuMzQ177yM5L2G6Zmk5LqG5pW45pOa57Wm5oiR5YCR55qE6LOH6KiK5Lul5aSW77yM5oiR5YCR6YKE5piv5b+F6aCI5Y+D6ICD5YW25LuW5pW45pOa5oiW5YCL5Lq655qE5Li76KeA55+l6K2Y5L6G5YGa5a6a5YO544CCDQpgYGB7cn0NCmJkWzEsXQ0KYGBgDQoNCg0KYGBge3J9DQpwcmljZSA9IHNlcSg0LCAxMCwgMC4yKQ0KcHJvYiA9IHByZWRpY3QobW9kMSwgZGF0YS5mcmFtZShUaW1lPTksIFF1YW50aXR5PTMuNSwgUHJpY2VQZXJMYj1wcmljZSksIHR5cGU9InJlc3BvbnNlIikNCmV4cFJldHVybiA9IHByb2IgKiAocHJpY2UgLSAxLjU4KQ0KcGxvdChwcmljZSwgZXhwUmV0dXJuICwgdHlwZT0nbCcpDQpgYGANCg0KKyDov7TmrbjmqKHlnovluavliqnmiJHlgJHkuobop6NY6IiHWeS5i+mXnOiBrw0KKyBvcHRpbWl6YXRpb27luavliqnmiJHlgJHnlLHov7TmrbjntZDmnpzmib7lh7rmnIDpgalZDQotIC0gLQ0KDQojIyMgNS4g6KGM54K657aT5r+f5qih5Z6LKOaYr+WQpuWIqumZpD8pDQoNCiMjIyMgNS4xIE1vZGVsaW5nDQpgYGB7cn0NCmJkJGdhaW4gPSAoYmQkTGFnUHJpY2UgLSBiZCRQcmljZVBlckxiKSooYmQkTGFnUHJpY2UgPiBiZCRQcmljZVBlckxiKQ0KYmQkbG9zcyA9IChiZCRQcmljZVBlckxiIC0gYmQkTGFnUHJpY2UpKihiZCRQcmljZVBlckxiID4gYmQkTGFnUHJpY2UpDQptb2QyID0gZ2xtKE9yZGVyIH4gVGltZStRdWFudGl0eStnYWluK2xvc3MrUXVhbnRpdHk6Z2FpbitRdWFudGl0eTpsb3NzLCANCiAgICAgICAgICAgIGRhdGE9YmQsIGZhbWlseT1iaW5vbWlhbCkNCnN1bW1hcnkobW9kMikNCmBgYA0KDQojIyMjIDUuMiBNb2RlbCBDb21wYXJpc29uDQpgYGB7ciBmaWcuaGVpZ2h0PTQsIGZpZy53aWR0aD00fQ0KcGFyKGNleD0wLjgpDQpkYXRhLmZyYW1lKA0KICBtb2RlbDEgPSBwcmVkaWN0KG1vZDEsIGJkLCB0eXBlPSJyZXNwb25zZSIpLA0KICBtb2RlbDIgPSBwcmVkaWN0KG1vZDIsIGJkLCB0eXBlPSJyZXNwb25zZSIpDQogICkgJT4lIGNvbEFVQyhiZCRPcmRlciwgcGxvdFJPQz1UUlVFKQ0KYGBgDQoNCiMjIyMgNS4zIE9wdGltaXphdGlvbi9DdXN0b21pemF0aW9uDQpgYGB7cn0NCmV4cFByb2ZpdDIgPSAgZnVuY3Rpb24ocHJpY2UsIG1vZCwgZGF0YSkgew0KICBkYXRhJGdhaW4gPSAocHJpY2UgLSBkYXRhJFByaWNlUGVyTGIpKihkYXRhJExhZ1ByaWNlID4gcHJpY2UpDQogIGRhdGEkbG9zcyA9IChwcmljZSAtIGRhdGEkTGFnUHJpY2UpKihwcmljZSA+IGRhdGEkTGFnUHJpY2UpDQogIChwcmljZSAtIGRhdGEkQ29zdFBlckxiKSAqIHByZWRpY3QobW9kLCBkYXRhLCB0eXBlPSJyZXNwb25zZSIpDQp9DQpleHBQcm9maXQyKDQuNSwgbW9kMiwgYmRbMSxdKQ0KYGBgDQoNCg0KYGBge3J9DQpvcHRpbSg0LCBleHBQcm9maXQyLCBtZXRob2Q9IkJGR1MiLCBjb250cm9sPWxpc3QoZm5zY2FsZT0tMSksIA0KICAgICAgbW9kPW1vZDIsIGRhdGE9YmRbMSxdICkNCmBgYA0KDQotIC0gLQ0KDQoNCg0KPGJyPjxicj48YnI+PGJyPg0KDQo8c3R5bGU+DQouY2FwdGlvbiB7DQogIGNvbG9yOiAjNzc3Ow0KICBtYXJnaW4tdG9wOiAxMHB4Ow0KfQ0KcCBjb2RlIHsNCiAgd2hpdGUtc3BhY2U6IGluaGVyaXQ7DQp9DQpwcmUgew0KICB3b3JkLWJyZWFrOiBub3JtYWw7DQogIHdvcmQtd3JhcDogbm9ybWFsOw0KICBsaW5lLWhlaWdodDogMTsNCn0NCnByZSBjb2RlIHsNCiAgd2hpdGUtc3BhY2U6IGluaGVyaXQ7DQp9DQoNCnAsbGkgew0KICBmb250LWZhbWlseTogIlRyZWJ1Y2hldCBNUyIsICLlvq7ou5/mraPpu5Hpq5QiLCAiTWljcm9zb2Z0IEpoZW5nSGVpIjsNCn0NCg0KLnJ7DQogIGxpbmUtaGVpZ2h0OiAxLjI7DQp9DQoNCi5xaXogew0KICBsaW5lLWhlaWdodDogMS43NTsNCiAgYmFja2dyb3VuZDogI2YwZjBmMDsNCiAgYm9yZGVyLWxlZnQ6IDEycHggc29saWQgI2NjZmZjYzsNCiAgcGFkZGluZzogNHB4Ow0KICBwYWRkaW5nLWxlZnQ6IDEwcHg7DQogIGNvbG9yOiAjMDA5OTAwOw0KfQ0KDQp0aXRsZXsNCiAgY29sb3I6ICNjYzAwMDA7DQogIGZvbnQtZmFtaWx5OiAiVHJlYnVjaGV0IE1TIiwgIuW+rui7n+ato+m7kemrlCIsICJNaWNyb3NvZnQgSmhlbmdIZWkiOw0KfQ0KDQpib2R5ew0KICBmb250LWZhbWlseTogIlRyZWJ1Y2hldCBNUyIsICLlvq7ou5/mraPpu5Hpq5QiLCAiTWljcm9zb2Z0IEpoZW5nSGVpIjsNCn0NCg0KaDEsaDIsaDMsaDQsaDV7DQogIGNvbG9yOiAjMDA2NmZmOw0KICBmb250LWZhbWlseTogIlRyZWJ1Y2hldCBNUyIsICLlvq7ou5/mraPpu5Hpq5QiLCAiTWljcm9zb2Z0IEpoZW5nSGVpIjsNCn0NCg0KDQpoM3sNCiAgY29sb3I6ICMwMDg4MDA7DQogIGJhY2tncm91bmQ6ICNlNmZmZTY7DQogIGxpbmUtaGVpZ2h0OiAyOw0KICBmb250LXdlaWdodDogYm9sZDsNCn0NCg0KaDV7DQogIGNvbG9yOiAjMDA2MDAwOw0KICBiYWNrZ3JvdW5kOiAjZjhmOGY4Ow0KICBsaW5lLWhlaWdodDogMS41Ow0KICBmb250LXdlaWdodDogYm9sZDsNCn0NCjwvc3R5bGU+DQo=