建立模型 製作新變數 調整變數 挑選變數

Sys.setlocale("LC_ALL","C")
[1] "C"
library(dplyr)
library(ggplot2)
library(caTools)
library(lubridate)
library(rpart.plot)
library(corrplot)
library(ggplot2)

一、使用原始變數建立模型:glm

載入檔案
load(file='data/tf2.Rdata')
切割TR、TS
TR=subset(A,spl)
TS=subset(A,!spl)
is.na(TR) %>% colSums() #計算TR的NA數量
  cust      r      s      f      m    rev    raw    age   area amount 
     0      0      0      0      0      0      0      0      0  10739 
   buy 
     0 
建立模型(glm)
cx=c(2:9,11)
colnames(TR[,cx])
[1] "r"    "s"    "f"    "m"    "rev"  "raw"  "age"  "area" "buy" 
glm1 = glm(buy ~ ., TR[,cx], family=binomial()) 
summary(glm1)

Call:
glm(formula = buy ~ ., family = binomial(), data = TR[, cx])

Deviance Residuals: 
    Min       1Q   Median       3Q      Max  
-3.7931  -0.8733  -0.6991   1.0384   1.8735  

Coefficients:
              Estimate Std. Error z value Pr(>|z|)    
(Intercept) -1.259e+00  1.261e-01  -9.985  < 2e-16 ***
r           -1.227e-02  8.951e-04 -13.708  < 2e-16 ***
s            9.566e-03  9.101e-04  10.511  < 2e-16 ***
f            2.905e-01  1.593e-02  18.233  < 2e-16 ***
m           -3.028e-05  2.777e-05  -1.090  0.27559    
rev          4.086e-05  1.940e-05   2.106  0.03521 *  
raw         -2.306e-04  8.561e-05  -2.693  0.00708 ** 
ageB        -4.194e-02  8.666e-02  -0.484  0.62838    
ageC         1.772e-02  7.992e-02   0.222  0.82456    
ageD         7.705e-02  7.921e-02   0.973  0.33074    
ageE         8.699e-02  8.132e-02   1.070  0.28476    
ageF         1.928e-02  8.457e-02   0.228  0.81962    
ageG         1.745e-02  9.323e-02   0.187  0.85155    
ageH         1.752e-01  1.094e-01   1.602  0.10926    
ageI         6.177e-02  1.175e-01   0.526  0.59904    
ageJ         2.652e-01  1.047e-01   2.533  0.01131 *  
ageK        -1.419e-01  1.498e-01  -0.947  0.34347    
areaB       -4.105e-02  1.321e-01  -0.311  0.75603    
areaC       -2.075e-01  1.045e-01  -1.986  0.04703 *  
areaD        3.801e-02  1.111e-01   0.342  0.73214    
areaE        2.599e-01  9.682e-02   2.684  0.00727 ** 
areaF        1.817e-01  9.753e-02   1.863  0.06243 .  
areaG       -4.677e-02  1.045e-01  -0.448  0.65435    
areaH       -1.695e-01  1.232e-01  -1.375  0.16912    
---
Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1

(Dispersion parameter for binomial family taken to be 1)

    Null deviance: 27629  on 20007  degrees of freedom
Residual deviance: 23295  on 19984  degrees of freedom
AIC: 23343

Number of Fisher Scoring iterations: 5
pred =  predict(glm1, TS, type="response")
glm的Accuracy及AUC
cm = table(actual = TS$buy, predict = pred > 0.5); cm
       predict
actual  FALSE TRUE
  FALSE  3730  873
  TRUE   1700 2273
acc.ts = cm %>% {sum(diag(.))/sum(.)}; acc.ts       
[1] 0.6999767
colAUC(pred, TS$buy)   #0.7556038                                
                    [,1]
FALSE vs. TRUE 0.7556038
檢查共線性
cx=c(2:7,11)
colnames(TR[,cx])
[1] "r"   "s"   "f"   "m"   "rev" "raw" "buy"
cor(TR[,cx]) %>% corrplot.mixed()


二、製作新變數(1)

join_df=group_by(X, cust) %>% summarise(
 
  T11=(month(date)==11) %>% sum ,
  T12=(month(date)==12) %>% sum ,
  T1=(month(date)==1) %>% sum
  ) %>% data.frame    # 28584
join_df

note

T11、T12、T1:將顧客分別在11、12、1月來店次數的總和。


合併變數T11、T12、T1到A (Left join):
A = merge(A, join_df, by="cust", all.x=T)
A
tapply(A$buy, A$T11, mean) %>% barplot

tapply(A$buy, A$T12, mean) %>% barplot

tapply(A$buy, A$T1, mean) %>% barplot


製作新變數(2)
#顧客在11月的消費
Nov = filter(X, month(date)==11 ) %>% 
  group_by(cust) %>% 
  summarise(
    amount_nov = sum(total),#消費總額
    items_nov=sum(items),#交易件數
    pieces_nov=sum(pieces),#購買商品個數
    gross_nov=sum(gross)
  ) 
Nov
#顧客在12月的消費
Dec = filter(X, month(date)==12 ) %>%
  group_by(cust) %>% 
  summarise(
    amount_dec = sum(total),
    items_dec=sum(items),
    pieces_dec=sum(pieces),
    gross_dec=sum(gross)
  ) 
Dec
#顧客在1月的消費
Jan = filter(X, month(date)==1 ) %>% 
  group_by(cust) %>% 
  summarise(
    amount_m1 = sum(total),#消費總額
    items_m1=sum(items),#交易件數
    pieces_m1=sum(pieces),#購買商品個數
    gross_m1=sum(gross)
  ) 
Jan

note

  • 分別製作出顧客在11、12、1月的消費(total/items/pieces/gross),丟進模型排列組合過後發現amount_m1的效果是最顯著的

合併變數到A(Left Join)
A = merge(A, Nov, by="cust", all.x=T)
A = merge(A, Dec, by="cust", all.x=T)
A = merge(A, Jan, by="cust", all.x=T)
A

用平均值填補NA
for(i in 15:24){
  mean_col <- mean(A[, i], na.rm = T)  # mean of col ith
  na.rows <- is.na(A[, i])   #col ith na data
  A[na.rows, i] <- mean_col
}
圖片
Figure - 填補NA

Figure - 填補NA


製作新變數(3)
A$amount_total=A$amount_nov+A$amount_dec+A$amount_m1
A$gross_total=A$gross_nov+A$gross_dec+A$gross_m1
A$items_total=A$items_nov+A$items_dec+A$items_m1
A$pieces_total=A$pieces_nov+A$pieces_dec+A$pieces_m1

調整變數

A$f_itemtotal=A$f*A$items_total
A$f_amounttotal=A$f*A$amount_total
A$f2=A$f^4*A$m^4
A$f3=A$r^4
A$f4=A$s^4
切割TR與TS
TR=subset(A,spl)
TS=subset(A,!spl)

用新變數來建立模型(glm)
cx=c(2:9,11,14,23,27,29,31,32,33,34)
colnames(TR[,cx])
 [1] "r"             "s"             "f"             "m"            
 [5] "rev"           "raw"           "age"           "area"         
 [9] "buy"           "T1"            "amount_m1"     "amount_total" 
[13] "items_total"   "f_itemtotal"   "f_amounttotal" "f2"           
[17] "f3"           
glm1 = glm(buy ~ ., TR[,cx], family=binomial()) 
glm.fit: fitted probabilities numerically 0 or 1 occurred
#summary(glm1)
pred =  predict(glm1, TS, type="response")

glm1的Accuracy及AUC
cm = table(actual = TS$buy, predict = pred > 0.5); cm
       predict
actual  FALSE TRUE
  FALSE  3738  865
  TRUE   1692 2281
acc.ts = cm %>% {sum(diag(.))/sum(.)}; acc.ts   #0.7017257     
[1] 0.7018424
colAUC(pred, TS$buy)                                
                    [,1]
FALSE vs. TRUE 0.7578508
#0.7579886

用step自動挑選變數

glm1_step=step(glm1,direction = 'backward')
Start:  AIC=23259.64
buy ~ r + s + f + m + rev + raw + age + area + T1 + amount_m1 + 
    amount_total + items_total + f_itemtotal + f_amounttotal + 
    f2 + f3
glm.fit: fitted probabilities numerically 0 or 1 occurredglm.fit: fitted probabilities numerically 0 or 1 occurredglm.fit: fitted probabilities numerically 0 or 1 occurredglm.fit: fitted probabilities numerically 0 or 1 occurredglm.fit: fitted probabilities numerically 0 or 1 occurredglm.fit: fitted probabilities numerically 0 or 1 occurredglm.fit: fitted probabilities numerically 0 or 1 occurredglm.fit: fitted probabilities numerically 0 or 1 occurredglm.fit: fitted probabilities numerically 0 or 1 occurredglm.fit: fitted probabilities numerically 0 or 1 occurredglm.fit: fitted probabilities numerically 0 or 1 occurred
                Df Deviance   AIC
- age           10    23213 23257
- r              1    23196 23258
- amount_m1      1    23197 23259
<none>                23196 23260
- f_amounttotal  1    23199 23261
- f_itemtotal    1    23203 23265
- m              1    23205 23267
- f2             1    23208 23270
- raw            1    23210 23272
- f3             1    23214 23276
- amount_total   1    23214 23276
- s              1    23214 23276
- items_total    1    23217 23279
- T1             1    23223 23285
- rev            1    23233 23295
- area           7    23296 23346
- f              1    23355 23417
glm.fit: fitted probabilities numerically 0 or 1 occurred

Step:  AIC=23256.62
buy ~ r + s + f + m + rev + raw + area + T1 + amount_m1 + amount_total + 
    items_total + f_itemtotal + f_amounttotal + f2 + f3
glm.fit: fitted probabilities numerically 0 or 1 occurredglm.fit: fitted probabilities numerically 0 or 1 occurredglm.fit: fitted probabilities numerically 0 or 1 occurredglm.fit: fitted probabilities numerically 0 or 1 occurredglm.fit: fitted probabilities numerically 0 or 1 occurredglm.fit: fitted probabilities numerically 0 or 1 occurredglm.fit: fitted probabilities numerically 0 or 1 occurredglm.fit: fitted probabilities numerically 0 or 1 occurredglm.fit: fitted probabilities numerically 0 or 1 occurredglm.fit: fitted probabilities numerically 0 or 1 occurred
                Df Deviance   AIC
- r              1    23213 23255
- amount_m1      1    23214 23256
<none>                23213 23257
- f_amounttotal  1    23216 23258
- f_itemtotal    1    23220 23262
- m              1    23222 23264
- f2             1    23225 23267
- raw            1    23227 23269
- s              1    23231 23273
- f3             1    23232 23274
- amount_total   1    23232 23274
- items_total    1    23234 23276
- T1             1    23241 23283
- rev            1    23251 23293
- area           7    23315 23345
- f              1    23372 23414
glm.fit: fitted probabilities numerically 0 or 1 occurred

Step:  AIC=23254.77
buy ~ s + f + m + rev + raw + area + T1 + amount_m1 + amount_total + 
    items_total + f_itemtotal + f_amounttotal + f2 + f3
glm.fit: fitted probabilities numerically 0 or 1 occurredglm.fit: fitted probabilities numerically 0 or 1 occurredglm.fit: fitted probabilities numerically 0 or 1 occurredglm.fit: fitted probabilities numerically 0 or 1 occurredglm.fit: fitted probabilities numerically 0 or 1 occurredglm.fit: fitted probabilities numerically 0 or 1 occurredglm.fit: fitted probabilities numerically 0 or 1 occurredglm.fit: fitted probabilities numerically 0 or 1 occurredglm.fit: fitted probabilities numerically 0 or 1 occurred
                Df Deviance   AIC
- amount_m1      1    23214 23254
<none>                23213 23255
- f_amounttotal  1    23216 23256
- f_itemtotal    1    23220 23260
- m              1    23223 23263
- f2             1    23226 23266
- raw            1    23228 23268
- items_total    1    23234 23274
- amount_total   1    23235 23275
- s              1    23238 23278
- f3             1    23244 23284
- T1             1    23251 23291
- rev            1    23260 23300
- area           7    23315 23343
- f              1    23377 23417
glm.fit: fitted probabilities numerically 0 or 1 occurred

Step:  AIC=23253.94
buy ~ s + f + m + rev + raw + area + T1 + amount_total + items_total + 
    f_itemtotal + f_amounttotal + f2 + f3
glm.fit: fitted probabilities numerically 0 or 1 occurredglm.fit: fitted probabilities numerically 0 or 1 occurredglm.fit: fitted probabilities numerically 0 or 1 occurredglm.fit: fitted probabilities numerically 0 or 1 occurredglm.fit: fitted probabilities numerically 0 or 1 occurredglm.fit: fitted probabilities numerically 0 or 1 occurredglm.fit: fitted probabilities numerically 0 or 1 occurredglm.fit: fitted probabilities numerically 0 or 1 occurred
                Df Deviance   AIC
<none>                23214 23254
- f_amounttotal  1    23217 23255
- f_itemtotal    1    23221 23259
- m              1    23224 23262
- f2             1    23227 23265
- raw            1    23228 23266
- amount_total   1    23235 23273
- items_total    1    23235 23273
- s              1    23241 23279
- f3             1    23245 23283
- T1             1    23258 23296
- rev            1    23260 23298
- area           7    23316 23342
- f              1    23377 23415
pred =  predict(glm1_step, TS, type="response")
glm1_step的Accuracy及AUC
cm = table(actual = TS$buy, predict = pred > 0.5); cm
       predict
actual  FALSE TRUE
  FALSE  3741  862
  TRUE   1694 2279
acc.ts = cm %>% {sum(diag(.))/sum(.)}; acc.ts     #0.7028918     
[1] 0.701959
colAUC(pred, TS$buy)                                
                    [,1]
FALSE vs. TRUE 0.7578848
#0.7581489
CV: glm
cx=c(2:9,11,14,23,28,30:33)
colnames(TR[,cx])
ctrl$repeats = 2
t0 = Sys.time(); set.seed(2)
cv.glm = train(
  buy ~ ., data=TR[,cx], method="glm", 
  trControl=ctrl, metric="ROC")
Sys.time() - t0
cv.glm$results
##### glm(), Final Model
glm1 = b=glm(buy ~ ., TR, family=binomial)
predict(glm1, TS, type="response") %>% colAUC(TS$buy)









LS0tDQp0aXRsZTogIuacn+S4reWwj+e1hOertuizvSwgVGEtRmVuZyINCmF1dGhvcjogIuesrOS4gOe1hC3lionogrLpipjjgIHnjovmt6/kvbPjgIHpu4Pmn4/ono3jgIHkvZnmm5zlu7fjgIHmnpfkv57kvLbjgIHpmbPmraPorIAiDQpkYXRlOiAiYHIgU3lzLnRpbWUoKWAiDQpvdXRwdXQ6IGh0bWxfbm90ZWJvb2sNCi0tLQ0KDQorICoq5L2/55So5YmN5LiJ5YCL5pyI55qE6LOH5paZ77yM6aCQ5ris6aGn5a6i5Zyo56ys5Zub5YCL5pyI5pyD5LiN5pyD5L6G6LK3KioNCg0KDQo8ZGl2IGlkPSJteVNpZGVuYXYiIGNsYXNzPSJzaWRlbmF2Ij4NCiAgPGEgaHJlZj0iI2dsbSIgaWQ9ImFib3V0Ij7lu7rnq4vmqKHlnos8L2E+DQogIDxhIGhyZWY9IiNBZGRfeCIgaWQ9ImJsb2ciPuijveS9nOaWsOiuiuaVuDwvYT4NCiAgPGEgaHJlZj0iI0FkanVzdF94IiBpZD0icHJvamVjdHMiPuiqv+aVtOiuiuaVuDwvYT4NCiAgPGEgaHJlZj0iI3NlbGVjdF94IiBpZD0iY29udGFjdCI+5oyR6YG46K6K5pW4PC9hPg0KPC9kaXY+DQoNCg0KYGBge3IgZWNobz1ULCBtZXNzYWdlPUYsIGNhY2hlPUYsIHdhcm5pbmc9Rn0NClN5cy5zZXRsb2NhbGUoIkxDX0FMTCIsIkMiKQ0KbGlicmFyeShkcGx5cikNCmxpYnJhcnkoZ2dwbG90MikNCmxpYnJhcnkoY2FUb29scykNCmxpYnJhcnkobHVicmlkYXRlKQ0KbGlicmFyeShycGFydC5wbG90KQ0KbGlicmFyeShjb3JycGxvdCkNCmxpYnJhcnkoZ2dwbG90MikNCmBgYA0KDQoNCg0KDQoNCiMjIyA8c3BhbiBpZD0iZ2xtIj7kuIDjgIHkvb/nlKjljp/lp4vorormlbjlu7rnq4vmqKHlnos6Z2xtPC9zcGFuPg0KDQoNCiMjIyMjIOi8ieWFpeaqlOahiA0KYGBge3J9DQpsb2FkKGZpbGU9J2RhdGEvdGYyLlJkYXRhJykNCmBgYA0KDQojIyMjIyDliIflibJUUuOAgVRTDQpgYGB7cn0NClRSPXN1YnNldChBLHNwbCkNClRTPXN1YnNldChBLCFzcGwpDQoNCmBgYA0KDQoNCmBgYHtyfQ0KaXMubmEoVFIpICU+JSBjb2xTdW1zKCkgI+ioiOeul1RS55qETkHmlbjph48NCmBgYA0KDQoNCiMjIyMjIOW7uueri+aooeWeiyhnbG0pDQpgYGB7cn0NCmN4PWMoMjo5LDExKQ0KY29sbmFtZXMoVFJbLGN4XSkNCmdsbTEgPSBnbG0oYnV5IH4gLiwgVFJbLGN4XSwgZmFtaWx5PWJpbm9taWFsKCkpIA0Kc3VtbWFyeShnbG0xKQ0KcHJlZCA9ICBwcmVkaWN0KGdsbTEsIFRTLCB0eXBlPSJyZXNwb25zZSIpDQpgYGANCg0KDQojIyMjIyBnbG3nmoRBY2N1cmFjeeWPikFVQw0KYGBge3J9DQoNCmNtID0gdGFibGUoYWN0dWFsID0gVFMkYnV5LCBwcmVkaWN0ID0gcHJlZCA+IDAuNSk7IGNtDQphY2MudHMgPSBjbSAlPiUge3N1bShkaWFnKC4pKS9zdW0oLil9OyBhY2MudHMgICAgICAgDQpjb2xBVUMocHJlZCwgVFMkYnV5KSAgICMwLjc1NTYwMzggICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIA0KDQpgYGANCg0KIyMjIyMg5qqi5p+l5YWx57ea5oCnDQpgYGB7cn0NCg0KDQpjeD1jKDI6NywxMSkNCmNvbG5hbWVzKFRSWyxjeF0pDQpjb3IoVFJbLGN4XSkgJT4lIGNvcnJwbG90Lm1peGVkKCkNCg0KYGBgDQoNCg0KPCEtLSAjIyMjIyAyLiDkvb/nlKjljp/lp4vorormlbjlu7rnq4vmqKHlnos65rG6562W5qi5IC0tPg0KPCEtLSBgYGB7cn0gLS0+DQo8IS0tIGxpYnJhcnkocnBhcnQucGxvdCkgLS0+DQo8IS0tIGN4PWMoMiwzLDU6OSwxMSwxNCwxOCkgLS0+DQo8IS0tIGNvbG5hbWVzKFRSWyxjeF0pIC0tPg0KPCEtLSBjYXJ0MSA9IHJwYXJ0KGJ1eX4uLCBUUlssY3hdLCBtZXRob2Q9J2NsYXNzJykgLS0+DQo8IS0tIHBycChjYXJ0MSwgY2V4PTAuNzUpIC0tPg0KDQo8IS0tIGBgYCAtLT4NCg0KPCEtLSAjIyMjIyAyLjEg5rG6562W5qi555qEQWNjdXJhY3nlj4pBVUMgLS0+DQo8IS0tIGBgYHtyfSAtLT4NCjwhLS0gcC5jYXJ0ID0gcHJlZCA9IHByZWRpY3QoY2FydDEsIFRTKVssMl0gLS0+DQo8IS0tIHRhYmxlKFRTJGJ1eSwgcHJlZCA+IDAuNSkgLS0+DQo8IS0tIGNvbEFVQyggcC5jYXJ0LCBUUyRidXkgKSAtLT4NCjwhLS0gYGBgIC0tPg0KDQoNCg0KLSAtIC0NCg0KIyMjIDxzcGFuIGlkPSdBZGRfeCc+5LqM44CB6KO95L2c5paw6K6K5pW4KDEpPC9zcGFuPg0KYGBge3J9DQpqb2luX2RmPWdyb3VwX2J5KFgsIGN1c3QpICU+JSBzdW1tYXJpc2UoDQogDQogIFQxMT0obW9udGgoZGF0ZSk9PTExKSAlPiUgc3VtICwNCiAgVDEyPShtb250aChkYXRlKT09MTIpICU+JSBzdW0gLA0KICBUMT0obW9udGgoZGF0ZSk9PTEpICU+JSBzdW0NCiAgKSAlPiUgZGF0YS5mcmFtZSAgICAjIDI4NTg0DQoNCmpvaW5fZGYNCmBgYA0KXzxzcGFuIGlkPSdBMSc+IG5vdGUgPC9zcGFuPl8NCg0KVDEx44CBVDEy44CBVDE65bCH6aGn5a6i5YiG5Yil5ZyoMTHjgIExMuOAgTHmnIjkvoblupfmrKHmlbjnmoTnuL3lkozjgIINCg0KLSAtIC0gDQoNCg0KIyMjIyMg5ZCI5L216K6K5pW4VDEx44CBVDEy44CBVDHliLBBIChMZWZ0IGpvaW4pOg0KYGBge3J9DQpBID0gbWVyZ2UoQSwgam9pbl9kZiwgYnk9ImN1c3QiLCBhbGwueD1UKQ0KQQ0KYGBgDQoNCg0KYGBge3J9DQp0YXBwbHkoQSRidXksIEEkVDExLCBtZWFuKSAlPiUgYmFycGxvdA0KYGBgDQoNCmBgYHtyfQ0KdGFwcGx5KEEkYnV5LCBBJFQxMiwgbWVhbikgJT4lIGJhcnBsb3QNCmBgYA0KDQpgYGB7cn0NCnRhcHBseShBJGJ1eSwgQSRUMSwgbWVhbikgJT4lIGJhcnBsb3QNCmBgYA0KDQotIC0gLQ0KDQoNCiMjIyMjIOijveS9nOaWsOiuiuaVuCgyKQ0KYGBge3J9DQoj6aGn5a6i5ZyoMTHmnIjnmoTmtojosrsNCk5vdiA9IGZpbHRlcihYLCBtb250aChkYXRlKT09MTEgKSAlPiUgDQogIGdyb3VwX2J5KGN1c3QpICU+JSANCiAgc3VtbWFyaXNlKA0KICAgIGFtb3VudF9ub3YgPSBzdW0odG90YWwpLCPmtojosrvnuL3poY0NCiAgICBpdGVtc19ub3Y9c3VtKGl0ZW1zKSwj5Lqk5piT5Lu25pW4DQogICAgcGllY2VzX25vdj1zdW0ocGllY2VzKSwj6LO86LK35ZWG5ZOB5YCL5pW4DQogICAgZ3Jvc3Nfbm92PXN1bShncm9zcykNCiAgKSANCk5vdg0KYGBgDQoNCg0KYGBge3J9DQoj6aGn5a6i5ZyoMTLmnIjnmoTmtojosrsNCkRlYyA9IGZpbHRlcihYLCBtb250aChkYXRlKT09MTIgKSAlPiUNCiAgZ3JvdXBfYnkoY3VzdCkgJT4lIA0KICBzdW1tYXJpc2UoDQogICAgYW1vdW50X2RlYyA9IHN1bSh0b3RhbCksDQogICAgaXRlbXNfZGVjPXN1bShpdGVtcyksDQogICAgcGllY2VzX2RlYz1zdW0ocGllY2VzKSwNCiAgICBncm9zc19kZWM9c3VtKGdyb3NzKQ0KICApIA0KRGVjDQpgYGANCg0KDQoNCmBgYHtyfQ0KI+mhp+WuouWcqDHmnIjnmoTmtojosrsNCkphbiA9IGZpbHRlcihYLCBtb250aChkYXRlKT09MSApICU+JSANCiAgZ3JvdXBfYnkoY3VzdCkgJT4lIA0KICBzdW1tYXJpc2UoDQogICAgYW1vdW50X20xID0gc3VtKHRvdGFsKSwj5raI6LK757i96aGNDQogICAgaXRlbXNfbTE9c3VtKGl0ZW1zKSwj5Lqk5piT5Lu25pW4DQogICAgcGllY2VzX20xPXN1bShwaWVjZXMpLCPos7zosrfllYblk4HlgIvmlbgNCiAgICBncm9zc19tMT1zdW0oZ3Jvc3MpDQogICkgDQpKYW4NCmBgYA0KXzxzcGFuIGlkPSdBMSc+IG5vdGUgPC9zcGFuPl8gDQoNCisg5YiG5Yil6KO95L2c5Ye66aGn5a6i5ZyoMTHjgIExMuOAgTHmnIjnmoTmtojosrsodG90YWwvaXRlbXMvcGllY2VzL2dyb3NzKe+8jOS4n+mAsuaooeWei+aOkuWIl+e1hOWQiOmBjuW+jOeZvOePvmFtb3VudF9tMeeahOaViOaenOaYr+acgOmhr+iRl+eahA0KDQoNCi0gLSAtDQoNCg0KIyMjIyMg5ZCI5L216K6K5pW45YiwQShMZWZ0IEpvaW4pDQpgYGB7cn0NCg0KQSA9IG1lcmdlKEEsIE5vdiwgYnk9ImN1c3QiLCBhbGwueD1UKQ0KQSA9IG1lcmdlKEEsIERlYywgYnk9ImN1c3QiLCBhbGwueD1UKQ0KQSA9IG1lcmdlKEEsIEphbiwgYnk9ImN1c3QiLCBhbGwueD1UKQ0KQQ0KYGBgDQoNCi0gLSAtDQoNCiMjIyMjIOeUqOW5s+Wdh+WAvOWhq+ijnE5BDQpgYGB7cn0NCmZvcihpIGluIDE1OjI0KXsNCiAgbWVhbl9jb2wgPC0gbWVhbihBWywgaV0sIG5hLnJtID0gVCkgICMgbWVhbiBvZiBjb2wgaXRoDQogIG5hLnJvd3MgPC0gaXMubmEoQVssIGldKSAgICNjb2wgaXRoIG5hIGRhdGENCiAgQVtuYS5yb3dzLCBpXSA8LSBtZWFuX2NvbA0KfQ0KDQoNCmBgYA0KIyMjIyMgPHNwYW4gaWQ9J0ExJz4g5ZyW54mHIDwvc3Bhbj4gDQohW0ZpZ3VyZSAgLSDloavoo5xOQV0oZmlnL2NvbXBsZXRlX25hLnBuZykNCg0KDQoNCg0KLSAtIC0NCg0KIyMjIyMg6KO95L2c5paw6K6K5pW4KDMpDQoNCmBgYHtyfQ0KQSRhbW91bnRfdG90YWw9QSRhbW91bnRfbm92K0EkYW1vdW50X2RlYytBJGFtb3VudF9tMQ0KQSRncm9zc190b3RhbD1BJGdyb3NzX25vditBJGdyb3NzX2RlYytBJGdyb3NzX20xDQpBJGl0ZW1zX3RvdGFsPUEkaXRlbXNfbm92K0EkaXRlbXNfZGVjK0EkaXRlbXNfbTENCkEkcGllY2VzX3RvdGFsPUEkcGllY2VzX25vditBJHBpZWNlc19kZWMrQSRwaWVjZXNfbTENCmBgYA0KDQoNCg0KIyMjIDxzcGFuIGlkPSdBZGp1c3RfeCc+6Kq/5pW06K6K5pW4PC9zcGFuPg0KKyBQKHk9MSk9MS8xK2VeIC0oQjArQjFYMStCMlgyK0IzWDMrLi4uK0JrWGspDQohW0ZpZ3VyZSAgLSBBVUNdKGZpZy9BVUMucG5nKQ0KDQoNCg0KDQpgYGB7cn0NCg0KDQpBJGZfaXRlbXRvdGFsPUEkZipBJGl0ZW1zX3RvdGFsDQpBJGZfYW1vdW50dG90YWw9QSRmKkEkYW1vdW50X3RvdGFsDQpBJGYyPUEkZl40KkEkbV40DQpBJGYzPUEkcl40DQpBJGY0PUEkc140DQpgYGANCg0KIyMjIyMg5YiH5YmyVFLoiIdUUw0KYGBge3J9DQpUUj1zdWJzZXQoQSxzcGwpDQpUUz1zdWJzZXQoQSwhc3BsKQ0KDQpgYGANCg0KLSAtIC0NCg0KIyMjIyMg55So5paw6K6K5pW45L6G5bu656uL5qih5Z6LKGdsbSkNCmBgYHtyfQ0KY3g9YygyOjksMTEsMTQsMjMsMjcsMjksMzEsMzIsMzMsMzQpDQpjb2xuYW1lcyhUUlssY3hdKQ0KZ2xtMSA9IGdsbShidXkgfiAuLCBUUlssY3hdLCBmYW1pbHk9Ymlub21pYWwoKSkgDQojc3VtbWFyeShnbG0xKQ0KcHJlZCA9ICBwcmVkaWN0KGdsbTEsIFRTLCB0eXBlPSJyZXNwb25zZSIpDQpgYGANCg0KLSAtIC0NCg0KIyMjIyMgZ2xtMeeahEFjY3VyYWN55Y+KQVVDDQpgYGB7cn0NCg0KY20gPSB0YWJsZShhY3R1YWwgPSBUUyRidXksIHByZWRpY3QgPSBwcmVkID4gMC41KTsgY20NCmFjYy50cyA9IGNtICU+JSB7c3VtKGRpYWcoLikpL3N1bSguKX07IGFjYy50cyAgICMwLjcwMTcyNTcgICAgIA0KY29sQVVDKHByZWQsIFRTJGJ1eSkgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIA0KIzAuNzU3OTg4Ng0KYGBgDQoNCg0KDQotIC0gLQ0KDQoNCiMjIyA8c3BhbiBpZD0nc2VsZWN0X3gnPueUqHN0ZXDoh6rli5XmjJHpgbjorormlbg8L3NwYW4+DQpgYGB7cn0NCmdsbTFfc3RlcD1zdGVwKGdsbTEsZGlyZWN0aW9uID0gJ2JhY2t3YXJkJykNCnByZWQgPSAgcHJlZGljdChnbG0xX3N0ZXAsIFRTLCB0eXBlPSJyZXNwb25zZSIpDQpgYGANCg0KDQojIyMjIyBnbG0xX3N0ZXDnmoRBY2N1cmFjeeWPikFVQw0KYGBge3J9DQoNCmNtID0gdGFibGUoYWN0dWFsID0gVFMkYnV5LCBwcmVkaWN0ID0gcHJlZCA+IDAuNSk7IGNtDQphY2MudHMgPSBjbSAlPiUge3N1bShkaWFnKC4pKS9zdW0oLil9OyBhY2MudHMgICAgICMwLjcwMjg5MTggICAgIA0KY29sQVVDKHByZWQsIFRTJGJ1eSkgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIA0KIzAuNzU4MTQ4OQ0KYGBgDQoNCg0KDQoNCg0KIyMjIyMgQ1Y6IGdsbQ0KDQpgYGB7cn0NCmN4PWMoMjo5LDExLDE0LDIzLDI4LDMwOjMzKQ0KY29sbmFtZXMoVFJbLGN4XSkNCmN0cmwkcmVwZWF0cyA9IDINCnQwID0gU3lzLnRpbWUoKTsgc2V0LnNlZWQoMikNCmN2LmdsbSA9IHRyYWluKA0KICBidXkgfiAuLCBkYXRhPVRSWyxjeF0sIG1ldGhvZD0iZ2xtIiwgDQogIHRyQ29udHJvbD1jdHJsLCBtZXRyaWM9IlJPQyIpDQpTeXMudGltZSgpIC0gdDANCmN2LmdsbSRyZXN1bHRzDQojIyMjIyBnbG0oKSwgRmluYWwgTW9kZWwNCmdsbTEgPSBiPWdsbShidXkgfiAuLCBUUiwgZmFtaWx5PWJpbm9taWFsKQ0KcHJlZGljdChnbG0xLCBUUywgdHlwZT0icmVzcG9uc2UiKSAlPiUgY29sQVVDKFRTJGJ1eSkNCg0KYGBgDQoNCg0KDQoNCg0KDQoNCi0gLSAtDQoNCg0KDQoNCg0KDQoNCg0KDQoNCjxicj48YnI+PGJyPjxicj48aHI+PGJyPjxicj48YnI+DQoNCjxzdHlsZT4NCi5jYXB0aW9uIHsNCiAgY29sb3I6ICM3Nzc7DQogIG1hcmdpbi10b3A6IDEwcHg7DQp9DQpwIGNvZGUgew0KICB3aGl0ZS1zcGFjZTogaW5oZXJpdDsNCn0NCnByZSB7DQogIHdvcmQtYnJlYWs6IG5vcm1hbDsNCiAgd29yZC13cmFwOiBub3JtYWw7DQogIGxpbmUtaGVpZ2h0OiAxOw0KfQ0KcHJlIGNvZGUgew0KICB3aGl0ZS1zcGFjZTogaW5oZXJpdDsNCn0NCnAsbGkgew0KICBmb250LWZhbWlseTogIlRyZWJ1Y2hldCBNUyIsICLlvq7ou5/mraPpu5Hpq5QiLCAiTWljcm9zb2Z0IEpoZW5nSGVpIjsNCn0NCg0KLnJ7DQogIGxpbmUtaGVpZ2h0OiAxLjI7DQp9DQoNCnRpdGxlew0KICBjb2xvcjogI2NjMDAwMDsNCiAgZm9udC1mYW1pbHk6ICJUcmVidWNoZXQgTVMiLCAi5b6u6Luf5q2j6buR6auUIiwgIk1pY3Jvc29mdCBKaGVuZ0hlaSI7DQp9DQoNCmJvZHl7DQogIGZvbnQtZmFtaWx5OiAiVHJlYnVjaGV0IE1TIiwgIuW+rui7n+ato+m7kemrlCIsICJNaWNyb3NvZnQgSmhlbmdIZWkiOw0KfQ0KDQpoMSxoMixoMyxoNCxoNXsNCiAgY29sb3I6ICMwMDg4MDA7DQogIGZvbnQtZmFtaWx5OiAiVHJlYnVjaGV0IE1TIiwgIuW+rui7n+ato+m7kemrlCIsICJNaWNyb3NvZnQgSmhlbmdIZWkiOw0KfQ0KDQpoM3sNCiAgY29sb3I6ICNiMzZiMDA7DQogIGJhY2tncm91bmQ6ICNmZmUwYjM7DQogIGxpbmUtaGVpZ2h0OiAyOw0KICBmb250LXdlaWdodDogYm9sZDsNCn0NCg0KaDV7DQogIGNvbG9yOiAjMDA2MDAwOw0KICBiYWNrZ3JvdW5kOiAjZmZmZmUwOw0KICBsaW5lLWhlaWdodDogMjsNCiAgZm9udC13ZWlnaHQ6IGJvbGQ7DQp9DQpoNnsNCiAgY29sb3I6ICMwMDYwMDA7DQogIGJhY2tncm91bmQ6ICMwMGZmZmY7DQogIGxpbmUtaGVpZ2h0OiAyOw0KICBmb250LXdlaWdodDogYm9sZDsNCg0KfQ0KZW17DQogIGNvbG9yOiAjRkZFQTZDOw0KICBiYWNrZ3JvdW5kOiAjN0Q3RDdEOw0KICB9DQogIA0KdGFibGUsIHRoLCB0ZCB7DQogICAgYm9yZGVyOiAxcHggc29saWQgYmxhY2s7DQp9DQoNCg0KI215U2lkZW5hdiBhIHsNCiAgICBwb3NpdGlvbjogYWJzb2x1dGU7DQogICAgbGVmdDogLTE1MHB4Ow0KICAgIHRyYW5zaXRpb246IDAuM3M7DQogICAgcGFkZGluZzogMTVweDsNCiAgICB3aWR0aDogMTUwcHg7DQogICAgdGV4dC1kZWNvcmF0aW9uOiBub25lOw0KICAgIGZvbnQtc2l6ZTogMjBweDsNCiAgICBjb2xvcjogd2hpdGU7DQogICAgYm9yZGVyLXJhZGl1czogMCA1cHggNXB4IDA7DQp9DQojbXlTaWRlbmF2ew0KICAgIHRvcDotMTBweDsNCiAgICBwb3NpdGlvbjogZml4ZWQ7DQp9DQojbXlTaWRlbmF2IGE6aG92ZXIgew0KICAgIGxlZnQ6IC0yMHB4Ow0KfQ0KDQojYWJvdXQgew0KICAgIHRvcDogMjBweDsNCiAgICBiYWNrZ3JvdW5kLWNvbG9yOiAjNENBRjUwOw0KfQ0KDQojYmxvZyB7DQogICAgdG9wOiA4MHB4Ow0KICAgIGJhY2tncm91bmQtY29sb3I6ICMyMTk2RjM7DQp9DQoNCiNwcm9qZWN0cyB7DQogICAgdG9wOiAxNDBweDsNCiAgICBiYWNrZ3JvdW5kLWNvbG9yOiAjZjQ0MzM2Ow0KfQ0KDQojY29udGFjdCB7DQogICAgdG9wOiAyMDBweDsNCiAgICBiYWNrZ3JvdW5kLWNvbG9yOiAjNTU1DQp9DQoNCjwvc3R5bGU+