rm(list=ls(all=T))
options(digits=4, scipen=12)
library(dplyr); library(ggplot2)
getwd()
[1] "/Users/katy/Desktop/rdata"
【A】 Definitions
機率、勝率(Odd)、Logit
par(cex=0.8, mfcol=c(1,2))
curve(x/(1-x), 0.02, 0.98, col='cyan',lwd=2, main='odd')
abline(v=seq(0,1,0.1), h=seq(0,50,5), col='lightgray', lty=3)
curve(log(x/(1-x)), 0.005, 0.995, lwd=2, col='purple', main="logit")
abline(v=seq(0,1,0.1), h=seq(-5,5,1), col='lightgray', lty=3)

Logistic Function & Logistic Regression
Linear Model: \(y = f(x) = b_0 + b_1x_1 + b_2x_2 + ...\)
General Linear Model(GLM): \(y = Link(f(x))\)
Logistic Regression: \(logit(y) = log(\frac{p}{1-p}) = f(x) \text{ where } p = prob[y=1]\)
Logistic Function: \(Logistic(F_x) = \frac{1}{1+Exp(-F_x)} = \frac{Exp(F_x)}{1+Exp(F_x)}\)
par(cex=0.8)
curve(1/(1+exp(-x)), -5, 5, col='blue', lwd=2,main="Logistic Function",
xlab="f(x): the logit of y = 1", ylab="the probability of y = 1")
abline(v=-5:5, h=seq(0,1,0.1), col='lightgray', lty=2)
abline(v=0,h=0.5,col='pink')
points(0,0.5,pch=20,cex=1.5,col='red')

【Q】What are the definiion of logit & logistic function? What is the relationship between them?
#logit:一種機率的算法,先算出odd再取log #logistic function:把logit轉成機率
【B】glm(, family=binomial)
glm()的功能:在 \(\{x\}\) 的空間之中,找出區隔 \(y\) 的(類別)界線
#Q %>% head()
Q=quality
glm1 = glm(PoorCare~OfficeVisits+Narcotics,Q, family=binomial)
summary(glm1)
Call:
glm(formula = PoorCare ~ OfficeVisits + Narcotics, family = binomial,
data = Q)
Deviance Residuals:
Min 1Q Median 3Q Max
-2.377 -0.627 -0.510 -0.154 2.119
Coefficients:
Estimate Std. Error z value Pr(>|z|)
(Intercept) -2.5402 0.4500 -5.64 0.000000017 ***
OfficeVisits 0.0627 0.0240 2.62 0.00892 **
Narcotics 0.1099 0.0326 3.37 0.00076 ***
---
Signif. codes: 0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1
(Dispersion parameter for binomial family taken to be 1)
Null deviance: 147.88 on 130 degrees of freedom
Residual deviance: 116.45 on 128 degrees of freedom
AIC: 122.4
Number of Fisher Scoring iterations: 5
b = coef(glm1); b # extract the regression coef
(Intercept) OfficeVisits Narcotics
-2.54021 0.06273 0.10990
Given OfficeVisits=3, Narcotics=4, what are the predicted logit, odd and probability?
logit = sum(b * c(1, 3, 4))
odd = exp(logit)
prob = odd/(1+odd)
c(logit=logit, odd=odd, prob=prob)
logit odd prob
-1.9124 0.1477 0.1287
【Q】What if OfficeVisits=2, Narcotics=3?
#
logit = sum(b * c(1, 2, 3))
odd = exp(logit)
prob = odd/(1+odd)
c(logit=logit, odd=odd, prob=prob)
logit odd prob
-2.0851 0.1243 0.1106
#
We can plot the line of logit = 0 or prob = 0.5 on the plane of \(X\)
par(cex=0.8)
plot(Q$OfficeVisits, Q$Narcotics, col=1+Q$PoorCare,pch=20)
abline(-b[1]/b[3], -b[2]/b[3])

Furthermore, we can translate probability, logit and coefficents to intercept & slope …
\[f(x) = b_1 + b_2 x_2 + b_3 x_3 = g \Rightarrow x_3 = \frac{g - b_1}{b_3} - \frac{b_2}{b_3}x_2\]
p = seq(0.1,0.9,0.1)
logit = log(p/(1-p))
data.frame(prob = p, logit)
then mark the contours of proabilities into the scatter plot
par(cex=0.7)
plot(Q$OfficeVisits, Q$Narcotics, col=1+Q$PoorCare,
pch=20, cex=1.3, xlab='X2', ylab='X3')
for(g in logit) {
abline((g-b[1])/b[3], -b[2]/b[3], col=ifelse(g==0,'blue','cyan')) }

【Q】What do the blue/cyan lines means?
等機率線(0.1~0.9) 【Q】Given any point in the figure above, how can you tell its (predicted) probability approximately? 看觀察點最接近的等機率線為何
【D】The Distribution of Predicted Probability (DPP)
Confusion matrix is not fixed. It changes by Threshold …
library(caTools)
DPP2 = function(pred,class,tvalue,breaks=0.01) {
mx = table(class == tvalue, pred > 0.5)
tn = sum(class != tvalue & pred <= 0.5)
fn = sum(class == tvalue & pred <= 0.5)
fp = sum(class != tvalue & pred > 0.5)
tp = sum(class == tvalue & pred > 0.5)
acc = (tn + tp)/length(pred)
sens = tp/(fn+tp)
spec = tn/(tn+fp)
auc = colAUC(pred,class)
data.frame(pred,class) %>%
ggplot(aes(x=pred, fill=class)) +
geom_histogram(col='gray',alpha=0.5,breaks=seq(0,1,breaks)) +
xlim(0,1) + theme_bw() + xlab("predicted probability") +
ggtitle(
sprintf("Distribution of Prob[class = \'%s\']", tvalue),
sprintf("AUC=%.3f, Acc=%.3f, Sens=%.3f, Spec=%.3f",
auc, acc, sens, spec) )
}
#
N1 = 300; N2 = 100 #平均值#標準差
DPP2(pred = c(rnorm(N1,0.125,0.03), rnorm(N2,0.375,0.03)),
class = c(rep('B',N1), rep('A',N2)),
tvalue = 'A')

【Q】Is it possible to have AUC = ACC = SENS = SPEC = 1? Can you modify the code to make it happen?
#
N1 = 300; N2 = 100 #平均值#標準差
DPP2(pred = c(rnorm(N1,0.125,0.03), rnorm(N2,0.625,0.03)),
class = c(rep('B',N1), rep('A',N2)),
tvalue = 'A')

#
【Q】Is it possible to have AUC = ACC = SENS = SPEC = 0? Can you modify the code to make that happen?
#
N1 = 300; N2 = 100 #平均值#標準差
DPP2(pred = c(rnorm(N1,0.625,0.03), rnorm(N2,0.125,0.03)),
class = c(rep('B',N1), rep('A',N2)),
tvalue = 'A')

#
【E】Modeling Expert
E1: Random Split
set.seed(88)
split = sample.split(Q$PoorCare, SplitRatio = 0.75)
table(split) %>% prop.table()
split
FALSE TRUE
0.2443 0.7557
table(y = Q$PoorCare, split) %>% prop.table(2)
split
y FALSE TRUE
0 0.7500 0.7475
1 0.2500 0.2525
TR = subset(Q, split == TRUE)
TS = subset(Q, split == FALSE)
E2: Build Model
glm1 = glm(PoorCare ~ OfficeVisits + Narcotics, TR, family=binomial)
summary(glm1)
Call:
glm(formula = PoorCare ~ OfficeVisits + Narcotics, family = binomial,
data = TR)
Deviance Residuals:
Min 1Q Median 3Q Max
-2.0630 -0.6316 -0.5050 -0.0969 2.1669
Coefficients:
Estimate Std. Error z value Pr(>|z|)
(Intercept) -2.6461 0.5236 -5.05 0.00000043 ***
OfficeVisits 0.0821 0.0305 2.69 0.0072 **
Narcotics 0.0763 0.0321 2.38 0.0173 *
---
Signif. codes: 0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1
(Dispersion parameter for binomial family taken to be 1)
Null deviance: 111.888 on 98 degrees of freedom
Residual deviance: 89.127 on 96 degrees of freedom
AIC: 95.13
Number of Fisher Scoring iterations: 4
E3: Prediction & Evaluation
pred = predict(glm1, type='response')
mx = table(TR$PoorCare, pred > 0.5); mx
FALSE TRUE
0 70 4
1 15 10
c(accuracy = sum(diag(mx))/sum(mx),
sensitivity = mx[2,2]/sum(mx[2,]),
specificity = mx[1,1]/sum(mx[1,]))
accuracy sensitivity specificity
0.8081 0.4000 0.9459
E4: ROC & AUC
library(ROCR)
Loading required package: gplots
Attaching package: ‘gplots’
The following object is masked from ‘package:stats’:
lowess
ROCRpred = prediction(pred, TR$PoorCare)
ROCRperf = performance(ROCRpred, "tpr", "fpr")
par(cex=0.8)
plot(ROCRperf, colorize=TRUE, print.cutoffs.at=seq(0,1,0.1))

as.numeric(performance(ROCRpred, "auc")@y.values)
[1] 0.7746
caTools::colAUC(pred, TR$PoorCare)
[,1]
0 vs. 1 0.7746
【F】Framingham Heart Study
getwd()
[1] "/Users/katy/Desktop/rdata"
source("DPP.R")
無法開啟檔案 'DPP.R' :No such file or directoryError in file(filename, "r", encoding = encoding) : 無法開啟連結
F1: Reading & Splitting
F =framingham
set.seed(1000)
split = sample.split(F$TenYearCHD, SplitRatio = 0.65)
TR = subset(F, split==TRUE)
TS = subset(F, split==FALSE)
F2: Logistic Regression Model
glm2 = glm(TenYearCHD ~ ., TR, family=binomial)
summary(glm2)
Call:
glm(formula = TenYearCHD ~ ., family = binomial, data = TR)
Deviance Residuals:
Min 1Q Median 3Q Max
-1.849 -0.601 -0.426 -0.284 2.837
Coefficients:
Estimate Std. Error z value Pr(>|z|)
(Intercept) -7.88657 0.89073 -8.85 < 2e-16 ***
male 0.52846 0.13544 3.90 0.0000955212349 ***
age 0.06206 0.00834 7.44 0.0000000000001 ***
education -0.05892 0.06243 -0.94 0.3453
currentSmoker 0.09324 0.19401 0.48 0.6308
cigsPerDay 0.01501 0.00783 1.92 0.0551 .
BPMeds 0.31122 0.28741 1.08 0.2789
prevalentStroke 1.16579 0.57121 2.04 0.0413 *
prevalentHyp 0.31582 0.17176 1.84 0.0660 .
diabetes -0.42149 0.40799 -1.03 0.3016
totChol 0.00384 0.00138 2.79 0.0053 **
sysBP 0.01134 0.00457 2.48 0.0130 *
diaBP -0.00474 0.00800 -0.59 0.5535
BMI 0.01072 0.01616 0.66 0.5069
heartRate -0.00810 0.00531 -1.52 0.1274
glucose 0.00893 0.00284 3.15 0.0016 **
---
Signif. codes: 0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1
(Dispersion parameter for binomial family taken to be 1)
Null deviance: 2020.7 on 2384 degrees of freedom
Residual deviance: 1792.3 on 2369 degrees of freedom
(371 observations deleted due to missingness)
AIC: 1824
Number of Fisher Scoring iterations: 5
F3: Prediction & Evaluation
pred = predict(glm2, TS, type="response")
y = TS$TenYearCHD[!is.na(pred)] # remove NA
pred = pred[!is.na(pred)]
mx = table(y, pred > 0.5); mx
y FALSE TRUE
0 1069 6
1 187 11
c(accuracy = sum(diag(mx))/sum(mx),
sensitivity = mx[2,2]/sum(mx[2,]),
specificity = mx[1,1]/sum(mx[1,]))
accuracy sensitivity specificity
0.84839 0.05556 0.99442
F4: AUC & DPP
par(cex=0.7)
auc = DPP(pred, y, 1, b=seq(0,1,0.02)) # 0.74211
Error in DPP(pred, y, 1, b = seq(0, 1, 0.02)) : 沒有這個函數 "DPP"
F5: Expected Result & Optimization
#2*2矩陣
payoff = matrix(c(0,-100,-10,-60),2,2)
cutoff = seq(0.02, 0.64, 0.01)
result = sapply(cutoff, function(p) sum(table(y,pred>p)*payoff) )
i = which.max(result)
par(cex=0.7)
plot(cutoff, result, type='l', col='cyan', lwd=2, main=sprintf(
"Optomal Expected Result: $%d @ %.2f",result[i],cutoff[i]))
abline(v=seq(0,1,0.05),h=seq(-23000,-17000,500),col='lightgray',lty=3)
abline(v=cutoff[i],col='red')

【Q】如果什麼都不做,期望報酬是多少? 19700 【Q】如果每位病人都做呢? 22600 【Q】以上哪一種做法期望報酬比較高? 什麼都不做 【Q】在所有的商務情境都是這種狀況嗎? 否 【Q】你可以模擬出「全做」比「全不做」還要好的狀況、並舉出一個會發生這種狀況的商務情境嗎? (0,-200,-100,500) 預測顧客偏好,-200我們預測該顧客不喜歡此產品,但實際上該顧客喜歡,因此我們會損失沒有該市場的營收。-100是我們針對錯誤的目標市場去做行銷活動的損失成本。500是我們正確預測顧客喜好並對他們做行銷活動。所以在此情況之下,對所有人做行銷活動會比完全不做還好。
#
#
#
F6: Simulation
library(manipulate)
library(ROCR)
p0 = par(mfrow=c(2,1),cex=0.8)
# manipulate({
# Y0 = -22000; Y1 = -12000
# mx = matrix(c(true_neg, false_neg, false_pos, true_pos),2,2)
# cx = seq(0.02, 0.64, 0.01)
# rx = sapply(cx, function(p) sum(table(y, pred>p)*mx) )
# i = which.max(rx)
# plot(cx, rx, type='l',col='cyan',lwd=2,main=sprintf(
# "Optomal Expected Result: $%d @ %.2f, T:%d",rx[i],cx[i],sum(pred>cx[i])),
# ylim=c(Y0,Y1))
# abline(v=cx[i],col='red')
# abline(v=seq(0,1,0.1),h=seq(Y0,Y1,2000),col='lightgray',lty=3)
# DPP(pred, y, 1, b=seq(0,1,0.02))
# abline(v=cx[i],col='red')l
# },
# true_neg = slider(-100,100,0,step=5),
# false_neg = slider(-100,100,-100,step=5),
# false_pos = slider(-100,100,-10,step=5),
# true_pos = slider(-100,100,-60,step=5)
# )
par(p0)
【Q】有五種成本分別為 $5, $10, $15, $20, $30 的藥,它們分別可以將風險成本從 $100 降低到 $70, $60, $50, $40, $25,哪一種藥的期望效益是最大的呢?
#$5,$10,$15,$20,$30
#-$17415,-$17500,-$17450,-$17400,-$17655
#$20的藥期望效益最大
LS0tCnRpdGxlOiAiQVMzLTAgR3JvdXAtMCIKYXV0aG9yOiAi5Y2T6ZuN54S2IEQ5OTQwMTAwMDEsIC4uLiIKb3V0cHV0OiBodG1sX25vdGVib29rCgotLS0KCmBgYHtyIGVjaG89VCwgbWVzc2FnZT1GLCBjYWNoZT1GLCB3YXJuaW5nPUZ9CnJtKGxpc3Q9bHMoYWxsPVQpKQpvcHRpb25zKGRpZ2l0cz00LCBzY2lwZW49MTIpCmxpYnJhcnkoZHBseXIpOyBsaWJyYXJ5KGdncGxvdDIpCmdldHdkKCkKCgoKYGBgCgoKLSAtIC0KCiMjIyDjgJBB44CRIERlZmluaXRpb25zCgojIyMjIOapn+eOh+OAgeWLneeOhyhPZGQp44CBTG9naXQKCisgT2RkID0gICRwLygxLXApJAoKKyBMb2dpdCA9ICRsb2cob2RkKSQgPSAkbG9nKFxmcmFje3B9ezE9cH0pJAoKKyAkbyA9IHAvKDEtcCkkIDsgJHAgPSBvLygxK28pJCA7ICAkbG9naXQgPSBsb2cobykkCgpgYGB7ciBmaWcuaGVpZ2h0PTMuNiwgZmlnLndpZHRoPTd9CnBhcihjZXg9MC44LCBtZmNvbD1jKDEsMikpCmN1cnZlKHgvKDEteCksIDAuMDIsIDAuOTgsIGNvbD0nY3lhbicsbHdkPTIsIG1haW49J29kZCcpCmFibGluZSh2PXNlcSgwLDEsMC4xKSwgaD1zZXEoMCw1MCw1KSwgY29sPSdsaWdodGdyYXknLCBsdHk9MykKY3VydmUobG9nKHgvKDEteCkpLCAwLjAwNSwgMC45OTUsIGx3ZD0yLCBjb2w9J3B1cnBsZScsIG1haW49ImxvZ2l0IikKYWJsaW5lKHY9c2VxKDAsMSwwLjEpLCBoPXNlcSgtNSw1LDEpLCBjb2w9J2xpZ2h0Z3JheScsIGx0eT0zKQoKYGBgCgojIyMjIExvZ2lzdGljIEZ1bmN0aW9uICYgTG9naXN0aWMgUmVncmVzc2lvbgoKKyBMaW5lYXIgTW9kZWw6ICR5ID0gZih4KSA9IGJfMCArIGJfMXhfMSArIGJfMnhfMiArIC4uLiQKCisgR2VuZXJhbCBMaW5lYXIgTW9kZWwoR0xNKTogJHkgPSBMaW5rKGYoeCkpJCAKCisgTG9naXN0aWMgUmVncmVzc2lvbjogJGxvZ2l0KHkpID0gbG9nKFxmcmFje3B9ezEtcH0pID0gZih4KSBcdGV4dHsgd2hlcmUgfSBwID0gcHJvYlt5PTFdJCAKCisgTG9naXN0aWMgRnVuY3Rpb246ICRMb2dpc3RpYyhGX3gpID0gXGZyYWN7MX17MStFeHAoLUZfeCl9ID0gXGZyYWN7RXhwKEZfeCl9ezErRXhwKEZfeCl9JAoKYGBge3IgIGZpZy53aWR0aD00LCBmaWcuaGVpZ2h0PTR9CnBhcihjZXg9MC44KQpjdXJ2ZSgxLygxK2V4cCgteCkpLCAtNSwgNSwgY29sPSdibHVlJywgbHdkPTIsbWFpbj0iTG9naXN0aWMgRnVuY3Rpb24iLAogICAgICB4bGFiPSJmKHgpOiB0aGUgbG9naXQgb2YgeSA9IDEiLCB5bGFiPSJ0aGUgcHJvYmFiaWxpdHkgb2YgeSA9IDEiKQphYmxpbmUodj0tNTo1LCBoPXNlcSgwLDEsMC4xKSwgY29sPSdsaWdodGdyYXknLCBsdHk9MikKYWJsaW5lKHY9MCxoPTAuNSxjb2w9J3BpbmsnKQpwb2ludHMoMCwwLjUscGNoPTIwLGNleD0xLjUsY29sPSdyZWQnKQpgYGAKCuOAkCoqUSoq44CRV2hhdCBhcmUgdGhlIGRlZmluaWlvbiBvZiBgbG9naXRgICYgYGxvZ2lzdGljIGZ1bmN0aW9uYD8gV2hhdCBpcyB0aGUgcmVsYXRpb25zaGlwIGJldHdlZW4gdGhlbT8KCu+8g2xvZ2l077ya5LiA56iu5qmf546H55qE566X5rOV77yM5YWI566X5Ye6b2Rk5YaN5Y+WbG9nCu+8g2xvZ2lzdGljIGZ1bmN0aW9u77ya5oqKbG9naXTovYnmiJDmqZ/njocKPGJyPgoKLSAtIC0KCiMjIyDjgJBC44CRYGdsbSgsIGZhbWlseT1iaW5vbWlhbClgCgpgZ2xtKClg55qE5Yqf6IO977ya5ZyoICRce3hcfSQg55qE56m66ZaT5LmL5Lit77yM5om+5Ye65Y2A6ZqUICR5JCDnmoQo6aGe5YilKeeVjOe3mgoKYGBge3J9CgojUSAlPiUgaGVhZCgpClE9cXVhbGl0eQpnbG0xID0gZ2xtKFBvb3JDYXJlfk9mZmljZVZpc2l0cytOYXJjb3RpY3MsUSwgZmFtaWx5PWJpbm9taWFsKQpzdW1tYXJ5KGdsbTEpCmBgYAoKYGBge3J9CmIgPSBjb2VmKGdsbTEpOyBiICAgIyBleHRyYWN0IHRoZSByZWdyZXNzaW9uIGNvZWYKYGBgCgpHaXZlbiBgT2ZmaWNlVmlzaXRzPTMsIE5hcmNvdGljcz00YCwgd2hhdCBhcmUgdGhlIHByZWRpY3RlZCBsb2dpdCwgb2RkIGFuZCBwcm9iYWJpbGl0eT8KYGBge3J9CmxvZ2l0ID0gc3VtKGIgKiBjKDEsIDMsIDQpKQpvZGQgPSBleHAobG9naXQpCnByb2IgPSBvZGQvKDErb2RkKQpjKGxvZ2l0PWxvZ2l0LCBvZGQ9b2RkLCBwcm9iPXByb2IpCmBgYAoK44CQKipRKirjgJFXaGF0IGlmIGBPZmZpY2VWaXNpdHM9MiwgTmFyY290aWNzPTNgPwpgYGB7cn0KIwpsb2dpdCA9IHN1bShiICogYygxLCAyLCAzKSkKb2RkID0gZXhwKGxvZ2l0KQpwcm9iID0gb2RkLygxK29kZCkKYyhsb2dpdD1sb2dpdCwgb2RkPW9kZCwgcHJvYj1wcm9iKQojCmBgYAoKV2UgY2FuIHBsb3QgdGhlIGxpbmUgb2YgYGxvZ2l0ID0gMGAgb3IgYHByb2IgPSAwLjVgIG9uIHRoZSBwbGFuZSBvZiAkWCQKYGBge3IgZmlnLndpZHRoPTMuNiwgZmlnLmhlaWdodD0zLjZ9CnBhcihjZXg9MC44KQpwbG90KFEkT2ZmaWNlVmlzaXRzLCBRJE5hcmNvdGljcywgY29sPTErUSRQb29yQ2FyZSxwY2g9MjApCmFibGluZSgtYlsxXS9iWzNdLCAtYlsyXS9iWzNdKQpgYGAKCkZ1cnRoZXJtb3JlLCB3ZSBjYW4gdHJhbnNsYXRlIHByb2JhYmlsaXR5LCBsb2dpdCBhbmQgY29lZmZpY2VudHMgdG8gaW50ZXJjZXB0ICYgc2xvcGUgLi4uCgokJGYoeCkgPSBiXzEgKyBiXzIgeF8yICsgYl8zIHhfMyA9IGcgXFJpZ2h0YXJyb3cgIHhfMyA9IFxmcmFje2cgLSBiXzF9e2JfM30gLSBcZnJhY3tiXzJ9e2JfM314XzIkJAoKCmBgYHtyICBmaWcud2lkdGg9My42LCBmaWcuaGVpZ2h0PTMuNn0KcCA9IHNlcSgwLjEsMC45LDAuMSkKbG9naXQgPSBsb2cocC8oMS1wKSkKZGF0YS5mcmFtZShwcm9iID0gcCwgbG9naXQpCmBgYAoKdGhlbiBtYXJrIHRoZSBjb250b3VycyBvZiBwcm9hYmlsaXRpZXMgaW50byB0aGUgc2NhdHRlciBwbG90IApgYGB7ciAgZmlnLndpZHRoPTMuNiwgZmlnLmhlaWdodD0zLjZ9CnBhcihjZXg9MC43KQpwbG90KFEkT2ZmaWNlVmlzaXRzLCBRJE5hcmNvdGljcywgY29sPTErUSRQb29yQ2FyZSwKICAgICBwY2g9MjAsIGNleD0xLjMsIHhsYWI9J1gyJywgeWxhYj0nWDMnKQpmb3IoZyBpbiBsb2dpdCkgewogIGFibGluZSgoZy1iWzFdKS9iWzNdLCAtYlsyXS9iWzNdLCBjb2w9aWZlbHNlKGc9PTAsJ2JsdWUnLCdjeWFuJykpIH0KYGBgCgrjgJAqKlEqKuOAkVdoYXQgZG8gdGhlIGJsdWUvY3lhbiBsaW5lcyBtZWFucz8KCuetieapn+eOh+e3mu+8iDAuMX4wLjkpCuOAkCoqUSoq44CRR2l2ZW4gYW55IHBvaW50IGluIHRoZSBmaWd1cmUgYWJvdmUsIGhvdyBjYW4geW91IHRlbGwgaXRzIChwcmVkaWN0ZWQpIHByb2JhYmlsaXR5IGFwcHJveGltYXRlbHk/Cueci+ingOWvn+m7nuacgOaOpei/keeahOetieapn+eOh+e3mueCuuS9lQoKPGJyPgoKLSAtIC0KCiMjIyDjgJBD44CRVGhlIENvbmZ1c2lvbiBNYXRyaXgKCgohW0ZpZ3VyZSAxIC0gQ29uZnVzaW9uIE1hdHJpeF0ocmVzL2NvbmZ1c2lvbl9tYXRyaXguanBnKQoKCjxicj4KCi0gLSAtCiMjIyDjgJBE44CRVGhlIERpc3RyaWJ1dGlvbiBvZiBQcmVkaWN0ZWQgUHJvYmFiaWxpdHkgKERQUCkKCkNvbmZ1c2lvbiBtYXRyaXggaXMgbm90IGZpeGVkLiBJdCBjaGFuZ2VzIGJ5IGBUaHJlc2hvbGRgIC4uLgoKIVtGaWd1cmUgMiAtIERpc3QuIFByZWRpZWN0ZWQgUHJvYi5dKHJlcy9kcHAuanBnKQoKCgpgYGB7cn0KbGlicmFyeShjYVRvb2xzKQpEUFAyID0gZnVuY3Rpb24ocHJlZCxjbGFzcyx0dmFsdWUsYnJlYWtzPTAuMDEpIHsKICBteCA9IHRhYmxlKGNsYXNzID09IHR2YWx1ZSwgcHJlZCA+IDAuNSkgCiAgdG4gPSBzdW0oY2xhc3MgIT0gdHZhbHVlICYgcHJlZCA8PSAwLjUpCiAgZm4gPSBzdW0oY2xhc3MgPT0gdHZhbHVlICYgcHJlZCA8PSAwLjUpCiAgZnAgPSBzdW0oY2xhc3MgIT0gdHZhbHVlICYgcHJlZCA+IDAuNSkKICB0cCA9IHN1bShjbGFzcyA9PSB0dmFsdWUgJiBwcmVkID4gMC41KQogIGFjYyA9ICh0biArIHRwKS9sZW5ndGgocHJlZCkKICBzZW5zID0gdHAvKGZuK3RwKQogIHNwZWMgPSB0bi8odG4rZnApCiAgYXVjID0gY29sQVVDKHByZWQsY2xhc3MpCiAgZGF0YS5mcmFtZShwcmVkLGNsYXNzKSAlPiUgCiAgICBnZ3Bsb3QoYWVzKHg9cHJlZCwgZmlsbD1jbGFzcykpICsKICAgIGdlb21faGlzdG9ncmFtKGNvbD0nZ3JheScsYWxwaGE9MC41LGJyZWFrcz1zZXEoMCwxLGJyZWFrcykpICsKICAgIHhsaW0oMCwxKSArIHRoZW1lX2J3KCkgKyB4bGFiKCJwcmVkaWN0ZWQgcHJvYmFiaWxpdHkiKSArIAogICAgZ2d0aXRsZSgKICAgICAgc3ByaW50ZigiRGlzdHJpYnV0aW9uIG9mIFByb2JbY2xhc3MgPSBcJyVzXCddIiwgdHZhbHVlKSwKICAgICAgc3ByaW50ZigiQVVDPSUuM2YsIEFjYz0lLjNmLCBTZW5zPSUuM2YsIFNwZWM9JS4zZiIsCiAgICAgICAgICAgICAgYXVjLCBhY2MsIHNlbnMsIHNwZWMpICkgCiAgfQoKYGBgCu+8gwpgYGB7ciBmaWcud2lkdGg9OCwgZmlnLmhlaWdodD0yLjV9Ck4xID0gMzAwOyBOMiA9IDEwMCAgICAgICPlubPlnYflgLwj5qiZ5rqW5beuCkRQUDIocHJlZCA9IGMocm5vcm0oTjEsMC4xMjUsMC4wMyksIHJub3JtKE4yLDAuMzc1LDAuMDMpKSwKICAgICBjbGFzcyA9IGMocmVwKCdCJyxOMSksIHJlcCgnQScsTjIpKSwgCiAgICAgdHZhbHVlID0gJ0EnKQpgYGAKCuOAkCoqUSoq44CRSXMgaXQgcG9zc2libGUgdG8gaGF2ZSBgQVVDID0gQUNDID0gU0VOUyA9IFNQRUMgPSAxYD8gQ2FuIHlvdSBtb2RpZnkgdGhlIGNvZGUgdG8gbWFrZSBpdCBoYXBwZW4/CgpgYGB7ciBmaWcud2lkdGg9OCwgZmlnLmhlaWdodD0yLjV9CiMgCk4xID0gMzAwOyBOMiA9IDEwMCAgICAgICPlubPlnYflgLwj5qiZ5rqW5beuCkRQUDIocHJlZCA9IGMocm5vcm0oTjEsMC4xMjUsMC4wMyksIHJub3JtKE4yLDAuNjI1LDAuMDMpKSwKICAgICBjbGFzcyA9IGMocmVwKCdCJyxOMSksIHJlcCgnQScsTjIpKSwgCiAgICAgdHZhbHVlID0gJ0EnKQojIApgYGAKCgrjgJAqKlEqKuOAkUlzIGl0IHBvc3NpYmxlIHRvIGhhdmUgYEFVQyA9IEFDQyA9IFNFTlMgPSBTUEVDID0gMGA/IENhbiB5b3UgbW9kaWZ5IHRoZSBjb2RlIHRvIG1ha2UgdGhhdCBoYXBwZW4/CgpgYGB7ciBmaWcud2lkdGg9OCwgZmlnLmhlaWdodD0yLjV9CiMgCk4xID0gMzAwOyBOMiA9IDEwMCAgICAgICPlubPlnYflgLwj5qiZ5rqW5beuCkRQUDIocHJlZCA9IGMocm5vcm0oTjEsMC42MjUsMC4wMyksIHJub3JtKE4yLDAuMTI1LDAuMDMpKSwKICAgICBjbGFzcyA9IGMocmVwKCdCJyxOMSksIHJlcCgnQScsTjIpKSwgCiAgICAgdHZhbHVlID0gJ0EnKQojIApgYGAKPGJyPgoKLSAtIC0KIyMjIOOAkEXjgJFNb2RlbGluZyBFeHBlcnQKCiMjIyMgRTE6IFJhbmRvbSBTcGxpdApgYGB7cn0KCnNldC5zZWVkKDg4KQpzcGxpdCA9IHNhbXBsZS5zcGxpdChRJFBvb3JDYXJlLCBTcGxpdFJhdGlvID0gMC43NSkKdGFibGUoc3BsaXQpICU+JSBwcm9wLnRhYmxlKCkKdGFibGUoeSA9IFEkUG9vckNhcmUsIHNwbGl0KSAlPiUgcHJvcC50YWJsZSgyKQpgYGAKCmBgYHtyfQpUUiA9IHN1YnNldChRLCBzcGxpdCA9PSBUUlVFKQpUUyA9IHN1YnNldChRLCBzcGxpdCA9PSBGQUxTRSkKYGBgCgojIyMjIEUyOiBCdWlsZCBNb2RlbApgYGB7cn0KZ2xtMSA9IGdsbShQb29yQ2FyZSB+IE9mZmljZVZpc2l0cyArIE5hcmNvdGljcywgVFIsIGZhbWlseT1iaW5vbWlhbCkKc3VtbWFyeShnbG0xKQpgYGAKCiMjIyMgRTM6IFByZWRpY3Rpb24gJiBFdmFsdWF0aW9uCmBgYHtyfQpwcmVkID0gcHJlZGljdChnbG0xLCB0eXBlPSdyZXNwb25zZScpCm14ID0gdGFibGUoVFIkUG9vckNhcmUsIHByZWQgPiAwLjUpOyBteApjKGFjY3VyYWN5ID0gc3VtKGRpYWcobXgpKS9zdW0obXgpLAogIHNlbnNpdGl2aXR5ID0gbXhbMiwyXS9zdW0obXhbMixdKSwKICBzcGVjaWZpY2l0eSA9IG14WzEsMV0vc3VtKG14WzEsXSkpCmBgYAoKIyMjIyBFNDogUk9DICYgQVVDCmBgYHtyIGZpZy53aWR0aD01LCBmaWcuaGVpZ2h0PTV9CmxpYnJhcnkoUk9DUikKUk9DUnByZWQgPSBwcmVkaWN0aW9uKHByZWQsIFRSJFBvb3JDYXJlKQpST0NScGVyZiA9IHBlcmZvcm1hbmNlKFJPQ1JwcmVkLCAidHByIiwgImZwciIpCnBhcihjZXg9MC44KQpwbG90KFJPQ1JwZXJmLCBjb2xvcml6ZT1UUlVFLCBwcmludC5jdXRvZmZzLmF0PXNlcSgwLDEsMC4xKSkKYGBgCgpgYGB7cn0KYXMubnVtZXJpYyhwZXJmb3JtYW5jZShST0NScHJlZCwgImF1YyIpQHkudmFsdWVzKQpjYVRvb2xzOjpjb2xBVUMocHJlZCwgVFIkUG9vckNhcmUpCmBgYAoKPGJyPgoKLSAtIC0KIyMjIOOAkEbjgJFGcmFtaW5naGFtIEhlYXJ0IFN0dWR5CgpgYGB7cn0KZ2V0d2QoKQpzb3VyY2UoIkRQUC5SIikKYGBgCgojIyMjIEYxOiBSZWFkaW5nICYgU3BsaXR0aW5nCmBgYHtyfQpGID1mcmFtaW5naGFtCnNldC5zZWVkKDEwMDApCnNwbGl0ID0gc2FtcGxlLnNwbGl0KEYkVGVuWWVhckNIRCwgU3BsaXRSYXRpbyA9IDAuNjUpClRSID0gc3Vic2V0KEYsIHNwbGl0PT1UUlVFKQpUUyA9IHN1YnNldChGLCBzcGxpdD09RkFMU0UpCmBgYAoKIyMjIyBGMjogTG9naXN0aWMgUmVncmVzc2lvbiBNb2RlbApgYGB7cn0KZ2xtMiA9IGdsbShUZW5ZZWFyQ0hEIH4gLiwgVFIsIGZhbWlseT1iaW5vbWlhbCkKc3VtbWFyeShnbG0yKQpgYGAKCiMjIyMgRjM6IFByZWRpY3Rpb24gJiBFdmFsdWF0aW9uCmBgYHtyfQpwcmVkID0gcHJlZGljdChnbG0yLCBUUywgdHlwZT0icmVzcG9uc2UiKQp5ID0gVFMkVGVuWWVhckNIRFshaXMubmEocHJlZCldICAgICAgICAgICAgICMgcmVtb3ZlIE5BCnByZWQgPSBwcmVkWyFpcy5uYShwcmVkKV0KCm14ID0gdGFibGUoeSwgcHJlZCA+IDAuNSk7IG14CmMoYWNjdXJhY3kgPSBzdW0oZGlhZyhteCkpL3N1bShteCksCiAgc2Vuc2l0aXZpdHkgPSBteFsyLDJdL3N1bShteFsyLF0pLAogIHNwZWNpZmljaXR5ID0gbXhbMSwxXS9zdW0obXhbMSxdKSkKYGBgCgojIyMjIEY0OiBBVUMgJiBEUFAKYGBge3IgZmlnLndpZHRoPTcsIGZpZy5oZWlnaHQ9Mi40fQpwYXIoY2V4PTAuNykKYXVjID0gRFBQKHByZWQsIHksIDEsIGI9c2VxKDAsMSwwLjAyKSkgICMgMC43NDIxMQpgYGAKCiMjIyMgRjU6IEV4cGVjdGVkIFJlc3VsdCAmIE9wdGltaXphdGlvbgoKCgoKCmBgYHtyIGZpZy53aWR0aD01LCBmaWcuaGVpZ2h0PTR9CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIzIqMuefqemZowpwYXlvZmYgPSBtYXRyaXgoYygwLC0xMDAsLTEwLC02MCksMiwyKSAKY3V0b2ZmID0gc2VxKDAuMDIsIDAuNjQsIDAuMDEpCnJlc3VsdCA9IHNhcHBseShjdXRvZmYsIGZ1bmN0aW9uKHApIHN1bSh0YWJsZSh5LHByZWQ+cCkqcGF5b2ZmKSApCmkgPSB3aGljaC5tYXgocmVzdWx0KQpwYXIoY2V4PTAuNykKcGxvdChjdXRvZmYsIHJlc3VsdCwgdHlwZT0nbCcsIGNvbD0nY3lhbicsIGx3ZD0yLCBtYWluPXNwcmludGYoCiAgIk9wdG9tYWwgRXhwZWN0ZWQgUmVzdWx0OiAkJWQgQCAlLjJmIixyZXN1bHRbaV0sY3V0b2ZmW2ldKSkKYWJsaW5lKHY9c2VxKDAsMSwwLjA1KSxoPXNlcSgtMjMwMDAsLTE3MDAwLDUwMCksY29sPSdsaWdodGdyYXknLGx0eT0zKQphYmxpbmUodj1jdXRvZmZbaV0sY29sPSdyZWQnKQpgYGAKCuOAkCoqUSoq44CR5aaC5p6c5LuA6bq86YO95LiN5YGa77yM5pyf5pyb5aCx6YWs5piv5aSa5bCR77yfCjE5NzAwCuOAkCoqUSoq44CR5aaC5p6c5q+P5L2N55eF5Lq66YO95YGa5ZGi77yfCjIyNjAwCuOAkCoqUSoq44CR5Lul5LiK5ZOq5LiA56iu5YGa5rOV5pyf5pyb5aCx6YWs5q+U6LyD6auY77yfCuS7gOm6vOmDveS4jeWBmgrjgJAqKlEqKuOAkeWcqOaJgOacieeahOWVhuWLmeaDheWig+mDveaYr+mAmeeorueLgOazgeWXju+8nwrlkKYK44CQKipRKirjgJHkvaDlj6/ku6XmqKHmk6zlh7rjgIzlhajlgZrjgI3mr5TjgIzlhajkuI3lgZrjgI3pgoTopoHlpb3nmoTni4Dms4HjgIHkuKboiInlh7rkuIDlgIvmnIPnmbznlJ/pgJnnqK7ni4Dms4HnmoTllYbli5nmg4XlooPll47vvJ8KKDAsLTIwMCwtMTAwLDUwMCkg6aCQ5ris6aGn5a6i5YGP5aW977yMLTIwMOaIkeWAkemgkOa4rOipsumhp+WuouS4jeWWnOatoeatpOeUouWTge+8jOS9huWvpumam+S4iuipsumhp+WuouWWnOatoe+8jOWboOatpOaIkeWAkeacg+aQjeWkseaykuacieipsuW4guWgtOeahOeHn+aUtuOAgi0xMDDmmK/miJHlgJHph53lsI3pjK/oqqTnmoTnm67mqJnluILloLTljrvlgZrooYzpirfmtLvli5XnmoTmkI3lpLHmiJDmnKzjgII1MDDmmK/miJHlgJHmraPnorrpoJDmuKzpoaflrqLllpzlpb3kuKblsI3ku5blgJHlgZrooYzpirfmtLvli5XjgILmiYDku6XlnKjmraTmg4Xms4HkuYvkuIvvvIzlsI3miYDmnInkurrlgZrooYzpirfmtLvli5XmnIPmr5TlrozlhajkuI3lgZrpgoTlpb3jgIIKCmBgYHtyfQojCiMKIwpgYGAKCjxicj4KCiMjIyMgRjY6IFNpbXVsYXRpb24KYGBge3IgZmlnLndpZHRoPTYsIGZpZy5oZWlnaHQ9Nn0KCmxpYnJhcnkobWFuaXB1bGF0ZSkKbGlicmFyeShST0NSKQpwMCA9IHBhcihtZnJvdz1jKDIsMSksY2V4PTAuOCkKIyBtYW5pcHVsYXRlKHsKIyAgIFkwID0gLTIyMDAwOyBZMSA9IC0xMjAwMAojICAgbXggPSBtYXRyaXgoYyh0cnVlX25lZywgZmFsc2VfbmVnLCBmYWxzZV9wb3MsIHRydWVfcG9zKSwyLDIpIAojICAgY3ggPSBzZXEoMC4wMiwgMC42NCwgMC4wMSkKIyAgIHJ4ID0gc2FwcGx5KGN4LCBmdW5jdGlvbihwKSBzdW0odGFibGUoeSwgcHJlZD5wKSpteCkgKQojICAgaSA9IHdoaWNoLm1heChyeCkKIyAgIHBsb3QoY3gsIHJ4LCB0eXBlPSdsJyxjb2w9J2N5YW4nLGx3ZD0yLG1haW49c3ByaW50ZigKIyAgICAgIk9wdG9tYWwgRXhwZWN0ZWQgUmVzdWx0OiAkJWQgQCAlLjJmLCBUOiVkIixyeFtpXSxjeFtpXSxzdW0ocHJlZD5jeFtpXSkpLAojICAgICB5bGltPWMoWTAsWTEpKQojICAgYWJsaW5lKHY9Y3hbaV0sY29sPSdyZWQnKQojICAgYWJsaW5lKHY9c2VxKDAsMSwwLjEpLGg9c2VxKFkwLFkxLDIwMDApLGNvbD0nbGlnaHRncmF5JyxsdHk9MykKIyAgIERQUChwcmVkLCB5LCAxLCBiPXNlcSgwLDEsMC4wMikpCiMgICBhYmxpbmUodj1jeFtpXSxjb2w9J3JlZCcpbAojICAgfSwKIyAgIHRydWVfbmVnICA9IHNsaWRlcigtMTAwLDEwMCwwLHN0ZXA9NSksCiMgICBmYWxzZV9uZWcgPSBzbGlkZXIoLTEwMCwxMDAsLTEwMCxzdGVwPTUpLAojICAgZmFsc2VfcG9zID0gc2xpZGVyKC0xMDAsMTAwLC0xMCxzdGVwPTUpLAojICAgdHJ1ZV9wb3MgID0gc2xpZGVyKC0xMDAsMTAwLC02MCxzdGVwPTUpCiMgICApIApwYXIocDApCmBgYAoKCuOAkCoqUSoq44CR5pyJ5LqU56iu5oiQ5pys5YiG5Yil54K6IGAkNSwgJDEwLCAkMTUsICQyMCwgJDMwYCDnmoTol6XvvIzlroPlgJHliIbliKXlj6/ku6XlsIfpoqjpmqrmiJDmnKzlvp4gYCQxMDBgIOmZjeS9juWIsCBgJDcwLCAkNjAsICQ1MCwgJDQwLCAkMjVg77yM5ZOq5LiA56iu6Jel55qE5pyf5pyb5pWI55uK5piv5pyA5aSn55qE5ZGi77yfCgoKYGBge3J9CiMkNSwkMTAsJDE1LCQyMCwkMzAKIy0kMTc0MTUsLSQxNzUwMCwtJDE3NDUwLC0kMTc0MDAsLSQxNzY1NQojJDIw55qE6Jel5pyf5pyb5pWI55uK5pyA5aSnCmBgYAoKPGJyPgoKLSAtIC0KIyMjIOOAkEfjgJHliIbmnpDmtYHnqIvvvJros4fmlpnjgIHmqKHlnovjgIHpoJDmuKzjgIHmsbrnrZYKCgoKCgo8YnI+PGJyPjxicj48YnI+PGJyPgoKCgoKCgoKCgo=