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?
#675
【1.2 底線機率】How many of the parolees in the dataset violated the terms of their parole?
table(parole$violator)
0 1
597 78
2 整理資料框 Creating Our Prediction Model
【2.1 類別變數】Which variables in this dataset are unordered factors with at least three levels?
table(parole$state)
1 2 3 4
143 120 82 330
#state crime
【2.2 資料框摘要】How does the output of summary() change for a factor variable as compared to a numerical variable?
summary(parole$age)
Min. 1st Qu. Median Mean 3rd Qu. Max.
18.4 25.4 33.7 34.5 42.5 67.0
as.factor(parole$crime)
[1] 4 3 3 1 1 4 3 1 3 2 1 1 3 3 3 1 3 1 3 1 1 3 3 3 1 1 3 1 2 1 1 1 1 1 3 1 1
[38] 1 3 1 2 1 3 2 1 4 1 4 4 4 1 1 4 1 1 1 1 1 1 1 1 1 2 1 1 3 1 1 1 3 2 1 2 1
[75] 3 1 1 3 3 3 3 3 1 1 3 2 3 3 4 1 1 3 3 3 3 3 3 3 3 1 3 1 3 1 3 1 3 1 3 3 1
[112] 1 1 3 3 3 2 1 3 3 3 3 3 3 3 1 1 3 3 1 3 3 3 2 1 3 2 2 2 2 2 3 3 1 3 3 1 1
[149] 1 1 3 3 3 1 3 1 1 1 1 3 3 3 3 1 1 3 3 3 3 3 3 3 1 1 1 1 4 4 4 1 1 1 3 3 3
[186] 3 3 1 1 2 4 3 1 1 1 4 2 3 3 3 1 1 3 3 1 1 1 1 1 1 1 3 1 3 3 1 2 1 1 1 1 1
[223] 4 1 1 2 1 2 2 1 3 2 1 1 2 2 3 2 2 3 1 3 3 3 2 2 3 1 1 2 1 1 3 3 1 1 1 1 1
[260] 2 4 1 4 1 1 3 1 3 1 1 1 3 3 1 3 1 1 3 3 1 4 1 1 2 1 1 1 3 3 1 1 1 3 4 1 4
[297] 1 1 1 1 3 3 1 1 1 2 3 1 1 4 3 4 2 3 4 4 4 4 4 4 4 4 4 1 1 4 4 3 2 4 4 4 3
[334] 4 4 1 4 4 4 4 4 1 1 1 3 1 1 1 1 1 1 2 2 1 1 2 1 1 1 3 3 2 2 4 4 1 1 3 2 1
[371] 2 2 4 4 1 2 2 3 4 1 2 1 3 1 1 4 4 2 3 4 2 2 4 1 1 1 2 1 4 1 2 1 1 4 1 1 1
[408] 1 2 1 1 1 4 2 3 2 3 2 1 1 4 4 1 4 1 1 1 3 2 1 2 4 1 4 2 2 2 1 3 4 2 1 1 1
[445] 1 1 1 2 3 3 1 4 2 1 2 4 4 1 1 1 2 1 3 1 2 1 4 2 1 1 4 4 2 4 3 1 1 1 1 1 4
[482] 1 1 3 2 4 1 1 3 2 2 1 2 1 1 2 2 3 4 1 4 2 1 1 2 1 4 4 2 3 1 1 1 1 1 2 1 1
[519] 3 1 1 3 2 1 1 1 4 4 3 4 1 2 1 4 1 1 1 3 4 3 4 1 4 1 3 1 2 4 3 4 4 1 2 2 2
[556] 1 4 3 1 1 1 4 1 2 1 1 3 4 2 2 2 3 1 1 1 3 2 1 1 4 2 2 3 2 1 1 1 1 2 1 1 4
[593] 1 4 1 2 1 4 1 1 1 3 1 1 1 1 3 1 1 2 1 4 2 1 2 4 1 1 1 1 2 2 1 1 1 1 1 4 1
[630] 1 1 3 1 2 1 1 4 1 1 1 2 1 1 4 1 2 2 1 1 4 1 1 1 1 4 2 3 1 4 1 1 1 4 2 1 2
[667] 1 4 2 1 1 3 3 1 4
Levels: 1 2 3 4
table(parole$state)
1 2 3 4
143 120 82 330
summary(parole$state)
Min. 1st Qu. Median Mean 3rd Qu. Max.
1.00 2.00 3.00 2.89 4.00 4.00
#can't transform into factor by as.factor?
3 資料分割 Splitting into a Training and Testing Set
【3.1 指定隨機種子、依比例分割資料】Roughly what proportion of parolees have been allocated to the training and testing sets?
set.seed(144)
library(caTools)
split = sample.split(parole$violator, SplitRatio = 0.7)
train = subset(parole, split == TRUE)
test = subset(parole, split == FALSE)
#70% of data allocated to training data, 30% to testing
【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?
split = sample.split(parole$violator, SplitRatio = 0.7)
train = subset(parole, split == TRUE)
test = subset(parole, split == FALSE)
#[1]-[5] exact same result
#not the same as we can see in the "split value" for question 2&3
4 建立模型 Building a Logistic Regression Model
【4.1 顯著性】What variables are significant in this model?
model1 = glm(violator~., data=train, family="binomial")
summary(model1)
Call:
glm(formula = violator ~ ., family = "binomial", data = train)
Deviance Residuals:
Min 1Q Median 3Q Max
-1.393 -0.495 -0.354 -0.250 2.677
Coefficients:
Estimate Std. Error z value Pr(>|z|)
(Intercept) -0.58813 1.16445 -0.51 0.6135
male 0.59110 0.47233 1.25 0.2108
race 0.85326 0.35384 2.41 0.0159 *
age 0.00598 0.01492 0.40 0.6888
state -0.72208 0.15540 -4.65 0.0000034 ***
time.served -0.24854 0.11148 -2.23 0.0258 *
max.sentence -0.09382 0.04519 -2.08 0.0379 *
multiple.offenses 1.38955 0.37390 3.72 0.0002 ***
crime -0.06962 0.13947 -0.50 0.6177
---
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: 290.43 on 464 degrees of freedom
AIC: 308.4
Number of Fisher Scoring iterations: 5
【4.2 從回歸係數估計邊際效用】What can we say based on the coefficient of the multiple.offenses variable?
#a e^1.661 times higher of odds
【4.3 從預測值估計勝率和機率】According to the model, what are the odds this individual is a violator? What is the probability this individual is a violator?
#log(odds) = -4.2411574 + 0.3869904*male + 0.8867192*race - 0.0001756*age + 0.4433007*state2 + 0.8349797*state3 - 3.3967878*state4 - 0.1238867*time.served + 0.0802954*max.sentence + 1.6119919*multiple.offenses + 0.6837143*crime2 - 0.2781054*crime3 - 0.0117627*crime4
1/(1+exp(1.700629))
[1] 0.1544
5 驗證模型 Evaluating the Model on the Testing Set
【5.1 從測試資料預測機率】What is the maximum predicted probability of a violation?
predictions = predict(model1, newdata=test, type="response")
summary(predictions)
Min. 1st Qu. Median Mean 3rd Qu. Max.
0.0071 0.0456 0.0692 0.0976 0.1249 0.5269
【5.2 從混淆矩陣計算敏感性、明確性、正確率】What is the model’s sensitivity, specificity, accuracy?
table(test$violator, as.numeric(predictions >= 0.5))
0 1
0 179 0
1 22 1
#sensitivity= 12/(11+12) = 0.522
#specificity= 167/(167+12) = 0.933
#accuracy=(167+12)/202 = 0.886
【5.3 底線機率】What is the accuracy of a simple model that predicts that every parolee is a non-violator?
【5.4 根據報償矩陣調整臨界機率】Which of the following most likely describes their preferences and best course of action?
#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?
#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?
pred = prediction(predictions, test$violator)
as.numeric(performance(pred, "auc")@y.values)
[1] 0.7352
【5.7 辨識率的定義】Describe the meaning of AUC in this context.
6 抽樣偏差 Identifying Bias in Observational Data
【6.1 如何避免、診斷、修正抽樣偏差】How could we improve our dataset to best address selection bias?
#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.
LS0tDQp0aXRsZTogIkFTMy0yIFByZWRpY3RpbmcgUGFyb2xlIFZpb2xhdG9ycyINCmF1dGhvcjogIkdyb3VwIDY6IOmZs+aMr+WYiSBNMDY0MDIwMDAyLCDlvLXlj6Hlk7IgTTA2NDExMTAzMywg6Zmz5YGJ5paHIE0wNjQwMjAwMTAs6a2P5ZiJ5aakIE0wNTQxMTEwNTAsIOaliuaYjuS/riBNMDY0MTExMDMwLCDmtKrnrbHmtrUgTTA2NDExMTAwMyINCm91dHB1dDogaHRtbF9ub3RlYm9vaw0KLS0tDQoNCmBgYHtyIGVjaG89VCwgbWVzc2FnZT1GLCBjYWNoZT1GLCB3YXJuaW5nPUZ9DQoNCmBgYA0KDQotIC0gLQ0KDQojIyMgSW50cm9kdWN0aW9uDQoNCioq6K2w6aGM77ya55So55Sz6KuL5YGH6YeL6ICF55qE5bGs5oCn77yM6aCQ5ris5LuW5pyD5LiN5pyD6YGV5Y+N5YGH6YeL6KaP5a6aKioNCg0KKirlrbjnv5Lph43pu57vvJoqKg0KDQorIOioreWumumaqOapn+eoruWtkHNldC5zZWVkKCnvvIzkvp3mr5TkvovliIblibLos4fmlpkNCisg5b6e6YKP6Lyv5byP5Zue5q2455qE5L+C5pW45o6o566X6Ieq6K6K5pW455qE6YKK6Zqb5pWI5p6cDQorIOWLneeOh+WSjOapn+eOh+OAgeWLneeOh+avlOWSjOapn+eOh+W3riDkuYvplpPnmoTmj5vnrpcgDQorIOiHqOeVjOapn+eOh+Wwjea3t+a3huefqemZoyjmnJ/mnJvloLHphawp55qE5b2x6Z+/cGF5b2ZmID0gbWF0cml4KGMoMCwtMTAwLC0xMCwtNTApLDIsMik7IHBheW9mZg0KKyBBVUPnmoTlr6bos6rmhI/nvqlwYXlvZmYgPSBtYXRyaXgoYygxMDAsIC04MCwgLTIwLCAxMDApLDIsMik7IHBheW9mZg0KKyDlpoLkvZUo5b6e5aCx6YWs55+p6ZmjKeaxuuWumuiHqOeVjOapn+eOhyANCisg5LuA6bq85piv5oq95qij5YGP5beuLOWmguS9lemBv+WFjeOAgeWmguS9leS/ruatow0KDQo8YnI+DQoNCi0gLSAtDQoNCiMjIyMgMSDos4fmlpnomZXnkIYgTG9hZGluZyB0aGUgRGF0YXNldA0KDQrjgJAqKjEuMSDoroDpgLLos4fmlpkqKuOAkUhvdyBtYW55IHBhcm9sZWVzIGFyZSBjb250YWluZWQgaW4gdGhlIGRhdGFzZXQ/DQpgYGB7cn0NCiM2NzUNCmBgYA0KDQrjgJAqKjEuMiDlupXnt5rmqZ/njocqKuOAkUhvdyBtYW55IG9mIHRoZSBwYXJvbGVlcyBpbiB0aGUgZGF0YXNldCB2aW9sYXRlZCB0aGUgdGVybXMgb2YgdGhlaXIgcGFyb2xlPw0KYGBge3J9DQp0YWJsZShwYXJvbGUkdmlvbGF0b3IpDQpgYGANCjxicj4NCg0KLSAtIC0NCg0KIyMjIyAyIOaVtOeQhuizh+aWmeahhiBDcmVhdGluZyBPdXIgUHJlZGljdGlvbiBNb2RlbA0KDQrjgJAqKjIuMSDpoZ7liKXorormlbgqKuOAkVdoaWNoIHZhcmlhYmxlcyBpbiB0aGlzIGRhdGFzZXQgYXJlIHVub3JkZXJlZCBmYWN0b3JzIHdpdGggYXQgbGVhc3QgdGhyZWUgbGV2ZWxzPyANCmBgYHtyfQ0KdGFibGUocGFyb2xlJHN0YXRlKQ0KI3N0YXRlIGNyaW1lDQpgYGANCg0K44CQKioyLjIg6LOH5paZ5qGG5pGY6KaBKirjgJFIb3cgZG9lcyB0aGUgb3V0cHV0IG9mIGBzdW1tYXJ5KClgIGNoYW5nZSBmb3IgYSBmYWN0b3IgdmFyaWFibGUgYXMgY29tcGFyZWQgdG8gYSBudW1lcmljYWwgdmFyaWFibGU/IA0KYGBge3J9DQpzdW1tYXJ5KHBhcm9sZSRhZ2UpDQphcy5mYWN0b3IocGFyb2xlJGNyaW1lKQ0KdGFibGUocGFyb2xlJHN0YXRlKQ0Kc3VtbWFyeShwYXJvbGUkc3RhdGUpDQojY2FuJ3QgdHJhbnNmb3JtIGludG8gZmFjdG9yIGJ5IGFzLmZhY3Rvcj8NCmBgYA0KPGJyPg0KDQotIC0gLQ0KDQojIyMjIDMg6LOH5paZ5YiG5YmyIFNwbGl0dGluZyBpbnRvIGEgVHJhaW5pbmcgYW5kIFRlc3RpbmcgU2V0DQoNCuOAkCoqMy4xIOaMh+WumumaqOapn+eoruWtkOOAgeS+neavlOS+i+WIhuWJsuizh+aWmSoq44CRUm91Z2hseSB3aGF0IHByb3BvcnRpb24gb2YgcGFyb2xlZXMgaGF2ZSBiZWVuIGFsbG9jYXRlZCB0byB0aGUgdHJhaW5pbmcgYW5kIHRlc3Rpbmcgc2V0cz8NCmBgYHtyfQ0KaW5zdGFsbC5wYWNrYWdlcygiY2FUb29scyIpDQpzZXQuc2VlZCgxNDQpDQpsaWJyYXJ5KGNhVG9vbHMpDQpzcGxpdCA9IHNhbXBsZS5zcGxpdChwYXJvbGUkdmlvbGF0b3IsIFNwbGl0UmF0aW8gPSAwLjcpDQp0cmFpbiA9IHN1YnNldChwYXJvbGUsIHNwbGl0ID09IFRSVUUpDQp0ZXN0ID0gc3Vic2V0KHBhcm9sZSwgc3BsaXQgPT0gRkFMU0UpDQojNzAlIG9mIGRhdGEgYWxsb2NhdGVkIHRvIHRyYWluaW5nIGRhdGEsIDMwJSB0byB0ZXN0aW5nDQpgYGANCg0K44CQKiozLjIg6Zqo5qmf56iu5a2Q55qE5Yqf55SoKirjgJFOb3csIHN1cHBvc2UgeW91IHJlLXJhbiBsaW5lcyBbMV0tWzVdIG9mIFByb2JsZW0gMy4xLiBXaGF0IHdvdWxkIHlvdSBleHBlY3Q/IElmIHlvdSBpbnN0ZWFkIE9OTFkgcmUtcmFuIGxpbmVzIFszXS1bNV0sIHdoYXQgd291bGQgeW91IGV4cGVjdD8gSWYgeW91IGluc3RlYWQgY2FsbGVkIHNldC5zZWVkKCkgd2l0aCBhIGRpZmZlcmVudCBudW1iZXIgYW5kIHRoZW4gcmUtcmFuIGxpbmVzIFszXS1bNV0gb2YgUHJvYmxlbSAzLjEsIHdoYXQgd291bGQgeW91IGV4cGVjdD8NCmBgYHtyfQ0Kc3BsaXQgPSBzYW1wbGUuc3BsaXQocGFyb2xlJHZpb2xhdG9yLCBTcGxpdFJhdGlvID0gMC43KQ0KdHJhaW4gPSBzdWJzZXQocGFyb2xlLCBzcGxpdCA9PSBUUlVFKQ0KdGVzdCA9IHN1YnNldChwYXJvbGUsIHNwbGl0ID09IEZBTFNFKQ0KI1sxXS1bNV0gZXhhY3Qgc2FtZSByZXN1bHQNCiNub3QgdGhlIHNhbWUgYXMgd2UgY2FuIHNlZSBpbiB0aGUgInNwbGl0IHZhbHVlIiBmb3IgcXVlc3Rpb24gMiYzDQpgYGANCjxicj4NCg0KLSAtIC0NCg0KIyMjIyA0IOW7uueri+aooeWeiyBCdWlsZGluZyBhIExvZ2lzdGljIFJlZ3Jlc3Npb24gTW9kZWwNCg0K44CQKio0LjEg6aGv6JGX5oCnKirjgJFXaGF0IHZhcmlhYmxlcyBhcmUgc2lnbmlmaWNhbnQgaW4gdGhpcyBtb2RlbD8NCmBgYHtyfQ0KbW9kZWwxID0gZ2xtKHZpb2xhdG9yfi4sIGRhdGE9dHJhaW4sIGZhbWlseT0iYmlub21pYWwiKQ0Kc3VtbWFyeShtb2RlbDEpDQpgYGANCg0K44CQKio0LjIg5b6e5Zue5q245L+C5pW45Lyw6KiI6YKK6Zqb5pWI55SoKirjgJFXaGF0IGNhbiB3ZSBzYXkgYmFzZWQgb24gdGhlIGNvZWZmaWNpZW50IG9mIHRoZSBgbXVsdGlwbGUub2ZmZW5zZXNgIHZhcmlhYmxlPw0KYGBge3J9DQojYSBlXjEuNjYxIHRpbWVzIGhpZ2hlciBvZiBvZGRzDQpgYGANCg0K44CQKio0LjMg5b6e6aCQ5ris5YC85Lyw6KiI5Yud546H5ZKM5qmf546HKirjgJFBY2NvcmRpbmcgdG8gdGhlIG1vZGVsLCB3aGF0IGFyZSB0aGUgb2RkcyB0aGlzIGluZGl2aWR1YWwgaXMgYSB2aW9sYXRvcj8gIFdoYXQgaXMgdGhlIHByb2JhYmlsaXR5IHRoaXMgaW5kaXZpZHVhbCBpcyBhIHZpb2xhdG9yPw0KYGBge3J9DQojbG9nKG9kZHMpID0gLTQuMjQxMTU3NCArIDAuMzg2OTkwNCptYWxlICsgMC44ODY3MTkyKnJhY2UgLSAwLjAwMDE3NTYqYWdlICsgMC40NDMzMDA3KnN0YXRlMiArIDAuODM0OTc5NypzdGF0ZTMgLSAzLjM5Njc4Nzgqc3RhdGU0IC0gMC4xMjM4ODY3KnRpbWUuc2VydmVkICsgMC4wODAyOTU0Km1heC5zZW50ZW5jZSArIDEuNjExOTkxOSptdWx0aXBsZS5vZmZlbnNlcyArIDAuNjgzNzE0MypjcmltZTIgLSAwLjI3ODEwNTQqY3JpbWUzIC0gMC4wMTE3NjI3KmNyaW1lNA0KIDEvKDErZXhwKDEuNzAwNjI5KSkNCmBgYA0KPGJyPg0KDQotIC0gLQ0KDQojIyMjIDUg6amX6K2J5qih5Z6LIEV2YWx1YXRpbmcgdGhlIE1vZGVsIG9uIHRoZSBUZXN0aW5nIFNldA0KDQrjgJAqKjUuMSDlvp7muKzoqabos4fmlpnpoJDmuKzmqZ/njocqKuOAkVdoYXQgaXMgdGhlIG1heGltdW0gcHJlZGljdGVkIHByb2JhYmlsaXR5IG9mIGEgdmlvbGF0aW9uPw0KYGBge3J9DQpwcmVkaWN0aW9ucyA9IHByZWRpY3QobW9kZWwxLCBuZXdkYXRhPXRlc3QsIHR5cGU9InJlc3BvbnNlIikNCnN1bW1hcnkocHJlZGljdGlvbnMpDQpgYGANCg0K44CQKio1LjIg5b6e5re35reG55+p6Zmj6KiI566X5pWP5oSf5oCn44CB5piO56K65oCn44CB5q2j56K6546HKirjgJFXaGF0IGlzIHRoZSBtb2RlbCdzIGBzZW5zaXRpdml0eWAsIGBzcGVjaWZpY2l0eWAsIGBhY2N1cmFjeWA/DQpgYGB7cn0NCnRhYmxlKHRlc3QkdmlvbGF0b3IsIGFzLm51bWVyaWMocHJlZGljdGlvbnMgPj0gMC41KSkNCiNzZW5zaXRpdml0eT0gMTIvKDExKzEyKSA9IDAuNTIyDQojc3BlY2lmaWNpdHk9IDE2Ny8oMTY3KzEyKSA9IDAuOTMzDQojYWNjdXJhY3k9KDE2NysxMikvMjAyID0gMC44ODYNCmBgYA0KDQrjgJAqKjUuMyDlupXnt5rmqZ/njocqKuOAkVdoYXQgaXMgdGhlIGFjY3VyYWN5IG9mIGEgc2ltcGxlIG1vZGVsIHRoYXQgcHJlZGljdHMgdGhhdCBldmVyeSBwYXJvbGVlIGlzIGEgbm9uLXZpb2xhdG9yPw0KYGBge3J9DQojYWNjdXJhY3k9KDE2NysxMikvMjAyID0gMC44ODYNCmBgYA0KDQrjgJAqKjUuNCDmoLnmk5rloLHlhJ/nn6npmaPoqr/mlbToh6jnlYzmqZ/njocqKuOAkVdoaWNoIG9mIHRoZSBmb2xsb3dpbmcgbW9zdCBsaWtlbHkgZGVzY3JpYmVzIHRoZWlyIHByZWZlcmVuY2VzIGFuZCBiZXN0IGNvdXJzZSBvZiBhY3Rpb24/DQpgYGB7cn0NCiNUaGUgYm9hcmQgYXNzaWducyBtb3JlIGNvc3QgdG8gYSBmYWxzZSBuZWdhdGl2ZSB0aGFuIGEgZmFsc2UgcG9zaXRpdmUsIGFuZCBzaG91bGQgdGhlcmVmb3JlIHVzZSBhIGxvZ2lzdGljIHJlZ3Jlc3Npb24gY3V0b2ZmIGxlc3MgdGhhbiAwLjUNCmBgYA0KDQrjgJAqKjUuNSDmraPnorrnjocgdnMg6L6o6K2Y546HKirjgJFXaGljaCBvZiB0aGUgZm9sbG93aW5nIGlzIHRoZSBtb3N0IGFjY3VyYXRlIGFzc2Vzc21lbnQgb2YgdGhlIHZhbHVlIG9mIHRoZSBsb2dpc3RpYyByZWdyZXNzaW9uIG1vZGVsIHdpdGggYSBjdXRvZmYgMC41IHRvIGEgcGFyb2xlIGJvYXJkLCBiYXNlZCBvbiB0aGUgbW9kZWwncyBhY2N1cmFjeSBhcyBjb21wYXJlZCB0byB0aGUgc2ltcGxlIGJhc2VsaW5lIG1vZGVsPw0KYGBge3J9DQojVGhlIG1vZGVsIGlzIGxpa2VseSBvZiB2YWx1ZSB0byB0aGUgYm9hcmQsIGFuZCB1c2luZyBhIGRpZmZlcmVudCBsb2dpc3RpYyByZWdyZXNzaW9uIGN1dG9mZiBpcyBsaWtlbHkgdG8gaW1wcm92ZSB0aGUgbW9kZWwncyB2YWx1ZQ0KYGBgDQoNCuOAkCoqNS42IOioiOeul+i+qOitmOeOhyoq44CRVXNpbmcgdGhlIGBST0NSYCBwYWNrYWdlLCB3aGF0IGlzIHRoZSBBVUMgdmFsdWUgZm9yIHRoZSBtb2RlbD8NCmBgYHtyfQ0KcHJlZCA9IHByZWRpY3Rpb24ocHJlZGljdGlvbnMsIHRlc3QkdmlvbGF0b3IpDQphcy5udW1lcmljKHBlcmZvcm1hbmNlKHByZWQsICJhdWMiKUB5LnZhbHVlcykNCmBgYA0KDQrjgJAqKjUuNyDovqjorZjnjofnmoTlrprnvqkqKuOAkURlc2NyaWJlIHRoZSBtZWFuaW5nIG9mIEFVQyBpbiB0aGlzIGNvbnRleHQuDQpgYGB7cn0NCiPpmqjmqZ/pgbjlj5blhanpoZ7liKXkuK3lkITkuIDpu57mmYLvvIzmqKHlnovlj6/mraPnorrliIbovqjlhanpoZ7nmoTmqZ/njocNCmBgYA0KPGJyPg0KDQotIC0gLQ0KDQojIyMjIDYg5oq95qij5YGP5beuIElkZW50aWZ5aW5nIEJpYXMgaW4gT2JzZXJ2YXRpb25hbCBEYXRhDQoNCuOAkCoqNi4xIOWmguS9lemBv+WFjeOAgeiouuaWt+OAgeS/ruato+aKveaoo+WBj+W3rioq44CRSG93IGNvdWxkIHdlIGltcHJvdmUgb3VyIGRhdGFzZXQgdG8gYmVzdCBhZGRyZXNzIHNlbGVjdGlvbiBiaWFzPw0KYGBge3J9DQojV2Ugc2hvdWxkIHVzZSBhIGRhdGFzZXQgdHJhY2tpbmcgYSBncm91cCBvZiBwYXJvbGVlcyBmcm9tIHRoZSBzdGFydCBvZiB0aGVpciBwYXJvbGUgdW50aWwgZWl0aGVyIHRoZXkgdmlvbGF0ZWQgcGFyb2xlIG9yIHRoZXkgY29tcGxldGVkIHRoZWlyIHRlcm0uDQpgYGANCjxicj4NCg0KLSAtIC0NCg0KPGJyPjxicj48YnI+DQo=