Introduction
議題:用申請假釋者的屬性,預測他會不會違反假釋規定
學習重點:
- 設定隨機種子set.seed(),依比例分割資料
- 從邏輯式回歸的係數推算自變數的邊際效果
- 勝率和機率、勝率比和機率差 之間的換算
- 臨界機率對混淆矩陣(期望報酬)的影響payoff = matrix(c(0,-100,-10,-50),2,2); payoff
- AUC的實質意義payoff = matrix(c(100, -80, -20, 100),2,2); payoff
- 如何(從報酬矩陣)決定臨界機率
- 什麼是抽樣偏差,如何避免、如何修正
1 資料處理 Loading the Dataset
【1.1 讀進資料】How many parolees are contained in the dataset?
parole=read.csv("data/parole.csv")
nrow(parole)
[1] 675
【1.2 底線機率】How many of the parolees in the dataset violated the terms of their parole?
sum(parole$violator==1)
[1] 78
2 整理資料框 Creating Our Prediction Model
【2.1 類別變數】Which variables in this dataset are unordered factors with at least three levels?
parole$state = factor(parole$state)
parole$crime = factor(parole$crime)
summary(parole)
male race age state time.served max.sentence multiple.offenses crime
Min. :0.000 Min. :1.00 Min. :18.4 1:143 Min. :0.00 Min. : 1.0 Min. :0.000 1:315
1st Qu.:1.000 1st Qu.:1.00 1st Qu.:25.4 2:120 1st Qu.:3.25 1st Qu.:12.0 1st Qu.:0.000 2:106
Median :1.000 Median :1.00 Median :33.7 3: 82 Median :4.40 Median :12.0 Median :1.000 3:153
Mean :0.807 Mean :1.42 Mean :34.5 4:330 Mean :4.20 Mean :13.1 Mean :0.536 4:101
3rd Qu.:1.000 3rd Qu.:2.00 3rd Qu.:42.5 3rd Qu.:5.20 3rd Qu.:15.0 3rd Qu.:1.000
Max. :1.000 Max. :2.00 Max. :67.0 Max. :6.00 Max. :18.0 Max. :1.000
violator
Min. :0.000
1st Qu.:0.000
Median :0.000
Mean :0.116
3rd Qu.:0.000
Max. :1.000
【2.2 資料框摘要】How does the output of summary() change for a factor variable as compared to a numerical variable?
set.seed(144)
library(caTools)
split = sample.split(parole$violator, SplitRatio = 0.7)
train = subset(parole, split == TRUE)
test = subset(parole, split == FALSE)
"0.7 training 0.3 testing"
[1] "0.7 training 0.3 testing"
3 資料分割 Splitting into a Training and Testing Set
【3.1 指定隨機種子、依比例分割資料】Roughly what proportion of parolees have been allocated to the training and testing sets?
paroleglm=glm(violator~.,train,family="binomial")
summary(paroleglm)
Call:
glm(formula = violator ~ ., family = "binomial", data = train)
Deviance Residuals:
Min 1Q Median 3Q Max
-1.704 -0.424 -0.272 -0.169 2.837
Coefficients:
Estimate Std. Error z value Pr(>|z|)
(Intercept) -4.241157 1.293885 -3.28 0.001 **
male 0.386990 0.437961 0.88 0.377
race 0.886719 0.395066 2.24 0.025 *
age -0.000176 0.016085 -0.01 0.991
state2 0.443301 0.481662 0.92 0.357
state3 0.834980 0.556270 1.50 0.133
state4 -3.396788 0.611586 -5.55 0.000000028 ***
time.served -0.123887 0.120423 -1.03 0.304
max.sentence 0.080295 0.055375 1.45 0.147
multiple.offenses 1.611992 0.385305 4.18 0.000028683 ***
crime2 0.683714 0.500355 1.37 0.172
crime3 -0.278105 0.432836 -0.64 0.521
crime4 -0.011763 0.571304 -0.02 0.984
---
Signif. codes: 0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1
(Dispersion parameter for binomial family taken to be 1)
Null deviance: 340.04 on 472 degrees of freedom
Residual deviance: 251.48 on 460 degrees of freedom
AIC: 277.5
Number of Fisher Scoring iterations: 6
"race state4 multiple.offenses are significant"
[1] "race state4 multiple.offenses are significant"
【3.2 隨機種子的功用】Now, suppose you re-ran lines [1]-[5] of Problem 3.1. What would you expect? If you instead ONLY re-ran lines [3]-[5], what would you expect? If you instead called set.seed() with a different number and then re-ran lines [3]-[5] of Problem 3.1, what would you expect?
odd=-4.241157+0.386990+0.886719+50*(-0.000176)+3*(-0.123887)+12*0.080295+0.683714
exp(odd)
[1] 0.1826
1/(1+exp(-odd))
[1] 0.1544
4 建立模型 Building a Logistic Regression Model
【4.1 顯著性】What variables are significant in this model?
x=table(actual = test$violator, predict = predictmd>=0.5)
x
predict
actual FALSE TRUE
0 167 12
1 11 12
sensitivity=12/(12+11)
specificity=167/(167+12)
accuracy=(12+167)/(12+167+12+11)
sensitivity
[1] 0.5217
specificity
[1] 0.933
accuracy
[1] 0.8861
【4.2 從回歸係數估計邊際效用】What can we say based on the coefficient of the multiple.offenses variable?
table(test$violator)
0 1
179 23
179/(179+23)
[1] 0.8861
【4.3 從預測值估計勝率和機率】According to the model, what are the odds this individual is a violator? What is the probability this individual is a violator?
library(caTools)
colAUC(predictmd, test$violator)
[,1]
0 vs. 1 0.8946
5 驗證模型 Evaluating the Model on the Testing Set
【5.1 從測試資料預測機率】What is the maximum predicted probability of a violation?
max(predict(model1,newdata = test, type = "response"))
[1] 0.9073
【5.2 從混淆矩陣計算敏感性、明確性、正確率】What is the model’s sensitivity, specificity, accuracy?
preResult <- predict(model1,newdata = test, type = "response")
table(test$violator, as.numeric(preResult >= 0.5))
0 1
0 167 12
1 11 12
12/(11+12)
[1] 0.5217
167/(167+12)
[1] 0.933
(167+12)/(167+12+11+12)
[1] 0.8861
# 利用 table() 計算出 confusion matrix
# 並利用 confusion matrix 計算 Sensitivity、Specificity 以及 Accuracy
【5.3 底線機率】What is the accuracy of a simple model that predicts that every parolee is a non-violator?
table(test$violator)
0 1
179 23
179/(179+23)
[1] 0.8861
# 底線機率單純看其 violater 變數項是否為 1
【5.4 根據報償矩陣調整臨界機率】Which of the following most likely describes their preferences and best course of action?
print("The board assigns more cost to a false negative than a false positive, and should therefore use a logistic regression cutoff less than 0.5. ", quote = FALSE)
[1] The board assigns more cost to a false negative than a false positive, and should therefore use a logistic regression cutoff less than 0.5.
# 放錯人比關錯人的成本要大得多,因此評估該假釋犯是否會違反規定的標準應該要嚴格一些
【5.5 正確率 vs 辨識率】Which of the following is the most accurate assessment of the value of the logistic regression model with a cutoff 0.5 to a parole board, based on the model’s accuracy as compared to the simple baseline model?
print("The model is likely of value to the board, and using a different logistic regression cutoff is likely to improve the model's value. ", quote = FALSE)
[1] The model is likely of value to the board, and using a different logistic regression cutoff is likely to improve the model's value.
# 可以將低放錯人的次數
【5.6 計算辨識率】Using the ROCR package, what is the AUC value for the model?
library(ROCR)
pred <- prediction(preResult, test$violator)
as.numeric(performance(pred, "auc")@y.values)
[1] 0.8946
【5.7 辨識率的定義】Describe the meaning of AUC in this context.
print("The probability the model can correctly differentiate between a randomly selected parole violator and a randomly selected parole non-violator.", quote = FALSE)
[1] The probability the model can correctly differentiate between a randomly selected parole violator and a randomly selected parole non-violator.
# 模型能夠猜對假釋犯不會違反規定與會違反規定的概率
6 抽樣偏差 Identifying Bias in Observational Data
【6.1 如何避免、診斷、修正抽樣偏差】How could we improve our dataset to best address selection bias?
print("We should use a dataset tracking a group of parolees from the start of their parole until either they violated parole or they completed their term.", quote = FALSE)
[1] We should use a dataset tracking a group of parolees from the start of their parole until either they violated parole or they completed their term.
# 面對缺漏值
# 若是將該假釋犯的 violator 自動補為 0
# 會造成模型偏向放大不會犯罪的可能性
# 若是將該假釋犯的 violator 自動補為 NA
# R 會自動在建立模型的時候剔除這些觀測值
# 最好的方式就是去追蹤這些假釋犯至其假釋期間結束
LS0tDQp0aXRsZTogIkFTMy0yIFByZWRpY3RpbmcgUGFyb2xlIFZpb2xhdG9ycyINCmF1dGhvcjogIkdyb3VwIDIiDQpvdXRwdXQ6IGh0bWxfbm90ZWJvb2sNCi0tLQ0KDQpgYGB7ciBlY2hvPVQsIG1lc3NhZ2U9RiwgY2FjaGU9Riwgd2FybmluZz1GfQ0Kcm0obGlzdD1scyhhbGw9VCkpDQpvcHRpb25zKGRpZ2l0cz00LCBzY2lwZW49MTIpDQpsaWJyYXJ5KG1hZ3JpdHRyKQ0KYGBgDQoNCi0gLSAtDQoNCiMjIyBJbnRyb2R1Y3Rpb24NCg0KKirorbDpoYzvvJrnlKjnlLPoq4vlgYfph4vogIXnmoTlsazmgKfvvIzpoJDmuKzku5bmnIPkuI3mnIPpgZXlj43lgYfph4vopo/lrpoqKg0KDQoqKuWtuOe/kumHjem7nu+8mioqDQoNCisg6Kit5a6a6Zqo5qmf56iu5a2Qc2V0LnNlZWQoKe+8jOS+neavlOS+i+WIhuWJsuizh+aWmQ0KKyDlvp7pgo/ovK/lvI/lm57mrbjnmoTkv4Lmlbjmjqjnrpfoh6rorormlbjnmoTpgorpmpvmlYjmnpwNCisg5Yud546H5ZKM5qmf546H44CB5Yud546H5q+U5ZKM5qmf546H5beuIOS5i+mWk+eahOaPm+eulyANCisg6Ieo55WM5qmf546H5bCN5re35reG55+p6ZmjKOacn+acm+WgsemFrCnnmoTlvbHpn79wYXlvZmYgPSBtYXRyaXgoYygwLC0xMDAsLTEwLC01MCksMiwyKTsgcGF5b2ZmDQorIEFVQ+eahOWvpuizquaEj+e+qXBheW9mZiA9IG1hdHJpeChjKDEwMCwgLTgwLCAtMjAsIDEwMCksMiwyKTsgcGF5b2ZmDQorIOWmguS9lSjlvp7loLHphaznn6npmaMp5rG65a6a6Ieo55WM5qmf546HIA0KKyDku4DpurzmmK/mir3mqKPlgY/lt64s5aaC5L2V6YG/5YWN44CB5aaC5L2V5L+u5q2jDQoNCjxicj4NCg0KLSAtIC0NCg0KIyMjIyAxIOizh+aWmeiZleeQhiBMb2FkaW5nIHRoZSBEYXRhc2V0DQoNCuOAkCoqMS4xIOiugOmAsuizh+aWmSoq44CRSG93IG1hbnkgcGFyb2xlZXMgYXJlIGNvbnRhaW5lZCBpbiB0aGUgZGF0YXNldD8NCmBgYHtyfQ0KDQpwYXJvbGUgPC0gcmVhZC5jc3YoImRhdGEvcGFyb2xlLmNzdiIpDQpucm93KHBhcm9sZSkNCg0KYGBgDQoNCuOAkCoqMS4yIOW6lee3muapn+eOhyoq44CRSG93IG1hbnkgb2YgdGhlIHBhcm9sZWVzIGluIHRoZSBkYXRhc2V0IHZpb2xhdGVkIHRoZSB0ZXJtcyBvZiB0aGVpciBwYXJvbGU/DQpgYGB7cn0NCg0KbnJvdyhwYXJvbGVbcGFyb2xlJHZpb2xhdG9yID09IDEsXSkNCg0KYGBgDQo8YnI+DQoNCi0gLSAtDQoNCiMjIyMgMiDmlbTnkIbos4fmlpnmoYYgQ3JlYXRpbmcgT3VyIFByZWRpY3Rpb24gTW9kZWwNCg0K44CQKioyLjEg6aGe5Yil6K6K5pW4KirjgJFXaGljaCB2YXJpYWJsZXMgaW4gdGhpcyBkYXRhc2V0IGFyZSB1bm9yZGVyZWQgZmFjdG9ycyB3aXRoIGF0IGxlYXN0IHRocmVlIGxldmVscz8gDQpgYGB7cn0NCg0KcGFyb2xlMiA8LSBkYXRhLmZyYW1lKHNhcHBseShwYXJvbGUsYXMuZmFjdG9yKSkNCnN1bW1hcnkocGFyb2xlMikNCnByaW50KCJzdGF0ZSIscXVvdGUgPSBGQUxTRSkNCnByaW50KCJjcmltZSIscXVvdGUgPSBGQUxTRSkNCg0KcGFyb2xlJG1hbGUgPC0gcGFyb2xlMiRtYWxlDQpwYXJvbGUkcmFjZSA8LSBwYXJvbGUyJHJhY2UNCnBhcm9sZSRzdGF0ZSA8LSBwYXJvbGUyJHN0YXRlDQpwYXJvbGUkbXVsdGlwbGUub2ZmZW5zZXMgPC0gcGFyb2xlMiRtdWx0aXBsZS5vZmZlbnNlcw0KcGFyb2xlJGNyaW1lIDwtIHBhcm9sZTIkY3JpbWUNCnBhcm9sZSR2aW9sYXRvciA8LSBwYXJvbGUyJHZpb2xhdG9yDQoNCiMg5bCH5Y6f5aeL6LOH5paZ5YWI5YWo6YOo6L2J5oiQIGZhY3RvciDlrZjliLDlj6bkuIDlgIsgZGF0YS5mcmFtZQ0KIyDop4Dlr5/pgJnlgIsgZGF0YS5mcmFtZSBzdW1tYXJ5KCkg55qE57WQ5p6cDQojIOaMkeWHuuaYr+mhnuWIpeeahOiuiuaVuOS4puabtOaWsOWOn+WniyBkYXRhLmZyYW1lIOS4remCo+W5vuWAi+iuiuaVuOeCuiBmYWN0b3Ig6LOH5paZDQoNCmBgYA0KDQrjgJAqKjIuMiDos4fmlpnmoYbmkZjopoEqKuOAkUhvdyBkb2VzIHRoZSBvdXRwdXQgb2YgYHN1bW1hcnkoKWAgY2hhbmdlIGZvciBhIGZhY3RvciB2YXJpYWJsZSBhcyBjb21wYXJlZCB0byBhIG51bWVyaWNhbCB2YXJpYWJsZT8gDQpgYGB7cn0NCg0KcHJpbnQoIlRoZSBvdXRwdXQgYmVjb21lcyBzaW1pbGFyIHRvIHRoYXQgb2YgdGhlIHRhYmxlKCkgZnVuY3Rpb24gYXBwbGllZCB0byB0aGF0IHZhcmlhYmxlIC4iLHF1b3RlID0gRkFMU0UpDQoNCiMgZmFjdG9yIOiuiuaVuOeahCBzdW1tYXJ5IOacg+aYr+WQhOWAi+mhnuWIpeeahOWHuuePvuasoeaVuA0KDQpgYGANCjxicj4NCg0KLSAtIC0NCg0KIyMjIyAzIOizh+aWmeWIhuWJsiBTcGxpdHRpbmcgaW50byBhIFRyYWluaW5nIGFuZCBUZXN0aW5nIFNldA0KDQrjgJAqKjMuMSDmjIflrprpmqjmqZ/nqK7lrZDjgIHkvp3mr5TkvovliIblibLos4fmlpkqKuOAkVJvdWdobHkgd2hhdCBwcm9wb3J0aW9uIG9mIHBhcm9sZWVzIGhhdmUgYmVlbiBhbGxvY2F0ZWQgdG8gdGhlIHRyYWluaW5nIGFuZCB0ZXN0aW5nIHNldHM/DQpgYGB7cn0NCg0Kc2V0LnNlZWQoMTQ0KQ0KbGlicmFyeShjYVRvb2xzKQ0Kc3BsaXQgPC0gc2FtcGxlLnNwbGl0KHBhcm9sZSR2aW9sYXRvciwgU3BsaXRSYXRpbyA9IDAuNykNCnRyYWluIDwtIHN1YnNldChwYXJvbGUsIHNwbGl0ID09IFRSVUUpDQp0ZXN0IDwtIHN1YnNldChwYXJvbGUsIHNwbGl0ID09IEZBTFNFKQ0KbnJvdyh0cmFpbikvKG5yb3codHJhaW4pK25yb3codGVzdCkpDQpwcmludCgiNzAlIHRvIHRoZSB0cmFpbmluZyBzZXQsIDMwJSB0byB0aGUgdGVzdGluZyBzZXQgLiAiLHF1b3RlID0gRkFMU0UpDQoNCiMg5oyH5a6a6Zqo5qmf56iu5a2Q5Lim5oyH5a6a5q+U5L6L5L6G5YiG5Ymy6LOH5paZDQojIOS4g+aIkOeCuiB0cmFpbmluZyBkYXRh44CB5LiJ5oiQ54K6IHRlc3RpbmcgZGF0YQ0KDQpgYGANCg0K44CQKiozLjIg6Zqo5qmf56iu5a2Q55qE5Yqf55SoKirjgJFOb3csIHN1cHBvc2UgeW91IHJlLXJhbiBsaW5lcyBbMV0tWzVdIG9mIFByb2JsZW0gMy4xLiBXaGF0IHdvdWxkIHlvdSBleHBlY3Q/IElmIHlvdSBpbnN0ZWFkIE9OTFkgcmUtcmFuIGxpbmVzIFszXS1bNV0sIHdoYXQgd291bGQgeW91IGV4cGVjdD8gSWYgeW91IGluc3RlYWQgY2FsbGVkIHNldC5zZWVkKCkgd2l0aCBhIGRpZmZlcmVudCBudW1iZXIgYW5kIHRoZW4gcmUtcmFuIGxpbmVzIFszXS1bNV0gb2YgUHJvYmxlbSAzLjEsIHdoYXQgd291bGQgeW91IGV4cGVjdD8NCmBgYHtyfQ0KDQpwcmludCgiVGhlIGV4YWN0IHNhbWUgdHJhaW5pbmcvdGVzdGluZyBzZXQgc3BsaXQgYXMgdGhlIGZpcnN0IGV4ZWN1dGlvbiBvZiBbMV0tWzVdIC4iLHF1b3RlID0gRkFMU0UpDQpwcmludCgiQSBkaWZmZXJlbnQgdHJhaW5pbmcvdGVzdGluZyBzZXQgc3BsaXQgZnJvbSB0aGUgZmlyc3QgZXhlY3V0aW9uIG9mIFsxXS1bNV0gLiAiLHF1b3RlID0gRkFMU0UpDQpwcmludCgiQSBkaWZmZXJlbnQgdHJhaW5pbmcvdGVzdGluZyBzZXQgc3BsaXQgZnJvbSB0aGUgZmlyc3QgZXhlY3V0aW9uIG9mIFsxXS1bNV0gLiAiLHF1b3RlID0gRkFMU0UpDQoNCiMg6Iul5piv5pyJ5oyH5a6a5aW96Zqo5qmf56iu5a2QDQojIOaOpeS4i+S+huWcqOS9v+eUqOebuOmXnOWHveW8j+eahOaZguWAmemDveacg+W+l+WIsOebuOWQjOeahOe1kOaenA0KIyDlm6DmrKHoi6XmmK/mspLmnInoqK3lrprpmqjmqZ/nqK7lrZDnmoTmlbjlrZfjgIHmiJbmmK/mjIflrprkuobkuI3lkIznmoTnqK7lrZANCiMg5ZCM5qij55qE5Ye95byP5bCH5pyD5pyJ5LiN5ZCM55qE55Si5Ye6DQoNCmBgYA0KPGJyPg0KDQotIC0gLQ0KDQojIyMjIDQg5bu656uL5qih5Z6LIEJ1aWxkaW5nIGEgTG9naXN0aWMgUmVncmVzc2lvbiBNb2RlbA0KDQrjgJAqKjQuMSDpoa/okZfmgKcqKuOAkVdoYXQgdmFyaWFibGVzIGFyZSBzaWduaWZpY2FudCBpbiB0aGlzIG1vZGVsPw0KYGBge3J9DQoNCm1vZGVsMSA8LSBnbG0odmlvbGF0b3IgfiAuICwgZGF0YSA9IHRyYWluICwgZmFtaWx5ID0gImJpbm9taWFsIikNCnN1bW1hcnkobW9kZWwxKQ0KcHJpbnQoInJhY2UgLCBzdGF0ZTQgLCBtdWx0aXBsZS5vZmZlbnNlcyIscXVvdGUgPSBGQUxTRSkNCg0KIyDliKnnlKggZ2xtIOWHveW8j+aQremFjSBmYW1pbHkg5oyH5a6a54K6IGJpbm9taWFsIOS+huW7uueri+mCj+i8r+aWr+WbnuatuOaooeWeiw0KIyDpgJnpgorlu7rnq4vnmoTmqKHlnovngrogdmlvbGF0b3Ig54K65oeJ6K6K5pW4DQojIOWFtuS7liBkYXRhLmZyYW1lIOS4reeahOiuiuaVuOmAmumAmueCuuiHquiuiuaVuA0KDQpgYGANCg0K44CQKio0LjIg5b6e5Zue5q245L+C5pW45Lyw6KiI6YKK6Zqb5pWI55SoKirjgJFXaGF0IGNhbiB3ZSBzYXkgYmFzZWQgb24gdGhlIGNvZWZmaWNpZW50IG9mIHRoZSBgbXVsdGlwbGUub2ZmZW5zZXNgIHZhcmlhYmxlPw0KYGBge3J9DQoNCmV4cCgxLjYxMTk5MikNCnByaW50KCJPdXIgbW9kZWwgcHJlZGljdHMgdGhhdCBhIHBhcm9sZWUgd2hvIGNvbW1pdHRlZCBtdWx0aXBsZSBvZmZlbnNlcyBoYXMgNS4wMSB0aW1lcyBoaWdoZXIgb2RkcyBvZiBiZWluZyBhIHZpb2xhdG9yIHRoYW4gYSBwYXJvbGVlIHdobyBkaWQgbm90IGNvbW1pdCBtdWx0aXBsZSBvZmZlbnNlcyBidXQgaXMgb3RoZXJ3aXNlIGlkZW50aWNhbC4gIiwgcXVvdGUgPSBGQUxTRSkNCg0KIyDmk4HmnIkgbXVsdGlwbGUgb2ZmZW5zZXMg57SA6YyE55qE5YGH6YeL6ICFDQojIOaciSA1LjAxIOWAjeeahOapn+eOh+mBleWPjeWBh+mHi+acn+mWk+eahOimj+Wumg0KDQpgYGANCg0K44CQKio0LjMg5b6e6aCQ5ris5YC85Lyw6KiI5Yud546H5ZKM5qmf546HKirjgJFBY2NvcmRpbmcgdG8gdGhlIG1vZGVsLCB3aGF0IGFyZSB0aGUgb2RkcyB0aGlzIGluZGl2aWR1YWwgaXMgYSB2aW9sYXRvcj8gIFdoYXQgaXMgdGhlIHByb2JhYmlsaXR5IHRoaXMgaW5kaXZpZHVhbCBpcyBhIHZpb2xhdG9yPw0KYGBge3J9DQoNCmxvZ2l0IDwtIHByZWRpY3QobW9kZWwxLG5ld2RhdGEgPSBkYXRhLmZyYW1lKG1hbGUgPSAiMSIscmFjZSA9ICIxIixhZ2UgPSA1MCwgc3RhdGUgPSAiMSIsIHRpbWUuc2VydmVkID0gMyAsIG1heC5zZW50ZW5jZSA9IDEyLCBtdWx0aXBsZS5vZmZlbnNlcyA9ICIwIixjcmltZSA9ICIyIikpDQpuYW1lcyhsb2dpdCkgPC0gTlVMTA0KZXhwKGxvZ2l0KQ0KMS8oMStleHAoLWxvZ2l0KSkNCg0KIyDlsIfoqbLlgYfph4vogIXnmoTos4foqIrkuJ/lhaXmqKHlnovpoJDmuKzlvowNCiMg5oqK57WQ5p6c5LmLIGxvZ2l0IOioiOeul+aIkCBvZGRzIOS7peWPiiBwcm9iYWJpbGl0eQ0KDQpgYGANCjxicj4NCg0KLSAtIC0NCg0KIyMjIyA1IOmpl+itieaooeWeiyBFdmFsdWF0aW5nIHRoZSBNb2RlbCBvbiB0aGUgVGVzdGluZyBTZXQNCg0K44CQKio1LjEg5b6e5ris6Kmm6LOH5paZ6aCQ5ris5qmf546HKirjgJFXaGF0IGlzIHRoZSBtYXhpbXVtIHByZWRpY3RlZCBwcm9iYWJpbGl0eSBvZiBhIHZpb2xhdGlvbj8NCmBgYHtyfQ0KDQptYXgocHJlZGljdChtb2RlbDEsbmV3ZGF0YSA9IHRlc3QsIHR5cGUgPSAicmVzcG9uc2UiKSkNCg0KYGBgDQoNCuOAkCoqNS4yIOW+nua3t+a3huefqemZo+ioiOeul+aVj+aEn+aAp+OAgeaYjueiuuaAp+OAgeato+eiuueOhyoq44CRV2hhdCBpcyB0aGUgbW9kZWwncyBgc2Vuc2l0aXZpdHlgLCBgc3BlY2lmaWNpdHlgLCBgYWNjdXJhY3lgPw0KYGBge3J9DQoNCnByZVJlc3VsdCA8LSBwcmVkaWN0KG1vZGVsMSxuZXdkYXRhID0gdGVzdCwgdHlwZSA9ICJyZXNwb25zZSIpDQp0YWJsZSh0ZXN0JHZpb2xhdG9yLCBhcy5udW1lcmljKHByZVJlc3VsdCA+PSAwLjUpKQ0KMTIvKDExKzEyKQ0KMTY3LygxNjcrMTIpDQooMTY3KzEyKS8oMTY3KzEyKzExKzEyKQ0KDQojIOWIqeeUqCB0YWJsZSgpIOioiOeul+WHuiBjb25mdXNpb24gbWF0cml4DQojIOS4puWIqeeUqCBjb25mdXNpb24gbWF0cml4IOioiOeulyBTZW5zaXRpdml0eeOAgVNwZWNpZmljaXR5IOS7peWPiiBBY2N1cmFjeQ0KDQpgYGANCg0K44CQKio1LjMg5bqV57ea5qmf546HKirjgJFXaGF0IGlzIHRoZSBhY2N1cmFjeSBvZiBhIHNpbXBsZSBtb2RlbCB0aGF0IHByZWRpY3RzIHRoYXQgZXZlcnkgcGFyb2xlZSBpcyBhIG5vbi12aW9sYXRvcj8NCmBgYHtyfQ0KDQp0YWJsZSh0ZXN0JHZpb2xhdG9yKQ0KMTc5LygxNzkrMjMpDQoNCiMg5bqV57ea5qmf546H5Zau57SU55yL5YW2IHZpb2xhdGVyIOiuiuaVuOmgheaYr+WQpueCuiAxDQoNCmBgYA0KDQrjgJAqKjUuNCDmoLnmk5rloLHlhJ/nn6npmaPoqr/mlbToh6jnlYzmqZ/njocqKuOAkVdoaWNoIG9mIHRoZSBmb2xsb3dpbmcgbW9zdCBsaWtlbHkgZGVzY3JpYmVzIHRoZWlyIHByZWZlcmVuY2VzIGFuZCBiZXN0IGNvdXJzZSBvZiBhY3Rpb24/DQpgYGB7cn0NCg0KcHJpbnQoIlRoZSBib2FyZCBhc3NpZ25zIG1vcmUgY29zdCB0byBhIGZhbHNlIG5lZ2F0aXZlIHRoYW4gYSBmYWxzZSBwb3NpdGl2ZSwgYW5kIHNob3VsZCB0aGVyZWZvcmUgdXNlIGEgbG9naXN0aWMgcmVncmVzc2lvbiBjdXRvZmYgbGVzcyB0aGFuIDAuNS4gIiwgcXVvdGUgPSBGQUxTRSkNCg0KIyDmlL7pjK/kurrmr5Tpl5zpjK/kurrnmoTmiJDmnKzopoHlpKflvpflpJrvvIzlm6DmraToqZXkvLDoqbLlgYfph4vniq/mmK/lkKbmnIPpgZXlj43opo/lrprnmoTmqJnmupbmh4noqbLopoHlmrTmoLzkuIDkupsNCg0KYGBgDQoNCuOAkCoqNS41IOato+eiuueOhyB2cyDovqjorZjnjocqKuOAkVdoaWNoIG9mIHRoZSBmb2xsb3dpbmcgaXMgdGhlIG1vc3QgYWNjdXJhdGUgYXNzZXNzbWVudCBvZiB0aGUgdmFsdWUgb2YgdGhlIGxvZ2lzdGljIHJlZ3Jlc3Npb24gbW9kZWwgd2l0aCBhIGN1dG9mZiAwLjUgdG8gYSBwYXJvbGUgYm9hcmQsIGJhc2VkIG9uIHRoZSBtb2RlbCdzIGFjY3VyYWN5IGFzIGNvbXBhcmVkIHRvIHRoZSBzaW1wbGUgYmFzZWxpbmUgbW9kZWw/DQpgYGB7cn0NCg0KcHJpbnQoIlRoZSBtb2RlbCBpcyBsaWtlbHkgb2YgdmFsdWUgdG8gdGhlIGJvYXJkLCBhbmQgdXNpbmcgYSBkaWZmZXJlbnQgbG9naXN0aWMgcmVncmVzc2lvbiBjdXRvZmYgaXMgbGlrZWx5IHRvIGltcHJvdmUgdGhlIG1vZGVsJ3MgdmFsdWUuICIsIHF1b3RlID0gRkFMU0UpDQoNCiMg5Y+v5Lul5bCH5L2O5pS+6Yyv5Lq655qE5qyh5pW4DQoNCmBgYA0KDQrjgJAqKjUuNiDoqIjnrpfovqjorZjnjocqKuOAkVVzaW5nIHRoZSBgUk9DUmAgcGFja2FnZSwgd2hhdCBpcyB0aGUgQVVDIHZhbHVlIGZvciB0aGUgbW9kZWw/DQpgYGB7cn0NCg0KbGlicmFyeShST0NSKQ0KcHJlZCA8LSBwcmVkaWN0aW9uKHByZVJlc3VsdCwgdGVzdCR2aW9sYXRvcikNCmFzLm51bWVyaWMocGVyZm9ybWFuY2UocHJlZCwgImF1YyIpQHkudmFsdWVzKQ0KDQpgYGANCg0K44CQKio1Ljcg6L6o6K2Y546H55qE5a6a576pKirjgJFEZXNjcmliZSB0aGUgbWVhbmluZyBvZiBBVUMgaW4gdGhpcyBjb250ZXh0Lg0KYGBge3J9DQoNCnByaW50KCJUaGUgcHJvYmFiaWxpdHkgdGhlIG1vZGVsIGNhbiBjb3JyZWN0bHkgZGlmZmVyZW50aWF0ZSBiZXR3ZWVuIGEgcmFuZG9tbHkgc2VsZWN0ZWQgcGFyb2xlIHZpb2xhdG9yIGFuZCBhIHJhbmRvbWx5IHNlbGVjdGVkIHBhcm9sZSBub24tdmlvbGF0b3IuIiwgcXVvdGUgPSBGQUxTRSkNCg0KIyDmqKHlnovog73lpKDnjJzlsI3lgYfph4vniq/kuI3mnIPpgZXlj43opo/lrproiIfmnIPpgZXlj43opo/lrprnmoTmpoLnjocNCg0KYGBgDQo8YnI+DQoNCi0gLSAtDQoNCiMjIyMgNiDmir3mqKPlgY/lt64gSWRlbnRpZnlpbmcgQmlhcyBpbiBPYnNlcnZhdGlvbmFsIERhdGENCg0K44CQKio2LjEg5aaC5L2V6YG/5YWN44CB6Ki65pa344CB5L+u5q2j5oq95qij5YGP5beuKirjgJFIb3cgY291bGQgd2UgaW1wcm92ZSBvdXIgZGF0YXNldCB0byBiZXN0IGFkZHJlc3Mgc2VsZWN0aW9uIGJpYXM/DQpgYGB7cn0NCg0KcHJpbnQoIldlIHNob3VsZCB1c2UgYSBkYXRhc2V0IHRyYWNraW5nIGEgZ3JvdXAgb2YgcGFyb2xlZXMgZnJvbSB0aGUgc3RhcnQgb2YgdGhlaXIgcGFyb2xlIHVudGlsIGVpdGhlciB0aGV5IHZpb2xhdGVkIHBhcm9sZSBvciB0aGV5IGNvbXBsZXRlZCB0aGVpciB0ZXJtLiIsIHF1b3RlID0gRkFMU0UpDQoNCiMg6Z2i5bCN57y65ryP5YC8DQojIOiLpeaYr+Wwh+ipsuWBh+mHi+eKr+eahCB2aW9sYXRvciDoh6rli5Xoo5zngrogMA0KIyDmnIPpgKDmiJDmqKHlnovlgY/lkJHmlL7lpKfkuI3mnIPniq/nvarnmoTlj6/og73mgKcNCiMg6Iul5piv5bCH6Kmy5YGH6YeL54qv55qEIHZpb2xhdG9yIOiHquWLleijnOeCuiBOQQ0KIyBSIOacg+iHquWLleWcqOW7uueri+aooeWei+eahOaZguWAmeWJlOmZpOmAmeS6m+ingOa4rOWAvA0KDQojIOacgOWlveeahOaWueW8j+WwseaYr+WOu+i/vei5pOmAmeS6m+WBh+mHi+eKr+iHs+WFtuWBh+mHi+acn+mWk+e1kOadnw0KDQpgYGANCjxicj4NCg0KLSAtIC0NCg0KPGJyPjxicj48YnI+DQo=