Example 1: Synthetic Example with Two Features
set.seed(1)
v0 <- rnorm(n=50, 0, 1.2)
v1 <- rnorm(n=50, 0, 0.4)
df1 <- data.frame(x1 = v0 + v1 + 5, x2 = 0.8*v0 - v1 + 4)
mean1 = mean(df1$x1)
mean2 = mean(df1$x2)
plot(df1, xlim=c(2,8), ylim=c(1,7), pch=21, bg='cornflowerblue', col='black', cex=1.2)
points(mean1, mean2, pch=21, bg='orange', col='black', cex=2)

Transformed Features
pca1$x[1:10,]
PC1 PC2
[1,] -1.09883375 0.1626135
[2,] 0.08051408 -0.4104127
[3,] -1.42397846 0.1319798
[4,] 2.21625250 -0.7105089
[5,] 0.43739205 0.7378345
[6,] -1.29429260 1.0530729
[7,] 0.56324011 -0.2748628
[8,] 0.90486898 -0.6569212
[9,] 0.75982131 0.2510644
[10,] -0.64004302 -0.1391293
pc1 <- pca1$rotation[,1]
pc2 <- pca1$rotation[,2]
par(mfrow=c(1,2))
plot(df1, xlim=c(2,8), ylim=c(1,7), pch=21, bg='cornflowerblue', col='black', cex=1.2,
main="Original Features")
Arrows(mean1, mean2, mean1 + pc1[1], mean2 + pc1[2], lwd=5, arr.type='triangle')
Arrows(mean1, mean2, mean1 + pc1[1], mean2 + pc1[2], lwd=2, arr.type='triangle', col='plum')
Arrows(mean1, mean2, mean1 + pc2[1], mean2 + pc2[2], lwd=5, arr.type='triangle')
Arrows(mean1, mean2, mean1 + pc2[1], mean2 + pc2[2], lwd=2, arr.type='triangle', col='cyan')
points(mean1, mean2, pch=21, bg='orange', col='black', cex=2)
plot(pca1$x, xlim=c(-4,4), ylim=c(-4,4), pch=21, bg='cornflowerblue', col='black', cex=1.2,
xlab='z1', ylab='z2', main='Transformed Features')
Arrows(0, 0, 1, 0, lwd=5, arr.type='triangle')
Arrows(0, 0, 1, 0, lwd=2, arr.type='triangle', col='plum')
Arrows(0, 0, 0, 1, lwd=5, arr.type='triangle')
Arrows(0, 0, 0, 1, lwd=2, arr.type='triangle', col='cyan')
points(0, 0, pch=21, bg='orange', col='black', cex=2)
par(mfrow=c(1,1))

Proportion of Variance Explained
std <- pca1$sdev
var <- pca1$sdev^2
var_ex <- var / sum(var)
cat('Std Dev of New Features: ', round(std, 5),
'\nVariances of New Features: ', round(var, 5),
'\nProp of Variance Explained:', round(var_ex, 5), sep=' ')
Std Dev of New Features: 1.27672 0.54468
Variances of New Features: 1.63002 0.29667
Prop of Variance Explained: 0.84602 0.15398
Example 2: Wine Cultivars Dataset
wc <- read.table("data/wine_cultivars.txt", header = TRUE, sep="\t")
wc$Cultivar <- factor(wc$Cultivar)
summary(wc)
Cultivar Alcohol Malic_Acid Ash Alcalinity Magnesium
1:59 Min. :11.03 Min. :0.740 Min. :1.360 Min. :10.60 Min. : 70.00
2:71 1st Qu.:12.36 1st Qu.:1.603 1st Qu.:2.210 1st Qu.:17.20 1st Qu.: 88.00
3:48 Median :13.05 Median :1.865 Median :2.360 Median :19.50 Median : 98.00
Mean :13.00 Mean :2.336 Mean :2.367 Mean :19.49 Mean : 99.74
3rd Qu.:13.68 3rd Qu.:3.083 3rd Qu.:2.558 3rd Qu.:21.50 3rd Qu.:107.00
Max. :14.83 Max. :5.800 Max. :3.230 Max. :30.00 Max. :162.00
Total_Phenols Flavanoids NonFlavanoid_Phenols Proanthocyanins Color
Min. :0.980 Min. :0.340 Min. :0.1300 Min. :0.410 Min. : 1.280
1st Qu.:1.742 1st Qu.:1.205 1st Qu.:0.2700 1st Qu.:1.250 1st Qu.: 3.220
Median :2.355 Median :2.135 Median :0.3400 Median :1.555 Median : 4.690
Mean :2.295 Mean :2.029 Mean :0.3619 Mean :1.591 Mean : 5.058
3rd Qu.:2.800 3rd Qu.:2.875 3rd Qu.:0.4375 3rd Qu.:1.950 3rd Qu.: 6.200
Max. :3.880 Max. :5.080 Max. :0.6600 Max. :3.580 Max. :13.000
Hue OD280_OD315 Proline
Min. :0.4800 Min. :1.270 Min. : 278.0
1st Qu.:0.7825 1st Qu.:1.938 1st Qu.: 500.5
Median :0.9650 Median :2.780 Median : 673.5
Mean :0.9574 Mean :2.612 Mean : 746.9
3rd Qu.:1.1200 3rd Qu.:3.170 3rd Qu.: 985.0
Max. :1.7100 Max. :4.000 Max. :1680.0
Explained Variance
ev <- wc_pca$sdev**2 / sum(wc_pca$sdev**2)
ev
[1] 0.361988481 0.192074903 0.111236305 0.070690302 0.065632937 0.049358233 0.042386793
[8] 0.026807489 0.022221534 0.019300191 0.017368357 0.012982326 0.007952149
cum_ev <- cumsum(ev)
cum_ev
[1] 0.3619885 0.5540634 0.6652997 0.7359900 0.8016229 0.8509812 0.8933680 0.9201754
[9] 0.9423970 0.9616972 0.9790655 0.9920479 1.0000000
plot(1:13, cum_ev, cex=1.4, pch=21, col='black', bg='steelblue',
xlab='Number of Principal Components', ylab='Cumulative Variance Explained')
abline(v=8, lty=3, lwd=2, col='steelblue')
abline(h=cum_ev[8], lty=3, lwd=2, col='steelblue')

Example 3: Wisconsin Breast Cancer Dataset
bc <- read.table("data/breast_cancer.csv", header=TRUE, sep=",")
bc$id <- NULL
summary(bc)
diagnosis radius_mean texture_mean perimeter_mean area_mean
B:357 Min. : 6.981 Min. : 9.71 Min. : 43.79 Min. : 143.5
M:212 1st Qu.:11.700 1st Qu.:16.17 1st Qu.: 75.17 1st Qu.: 420.3
Median :13.370 Median :18.84 Median : 86.24 Median : 551.1
Mean :14.127 Mean :19.29 Mean : 91.97 Mean : 654.9
3rd Qu.:15.780 3rd Qu.:21.80 3rd Qu.:104.10 3rd Qu.: 782.7
Max. :28.110 Max. :39.28 Max. :188.50 Max. :2501.0
smoothness_mean compactness_mean concavity_mean concave.points_mean
Min. :0.05263 Min. :0.01938 Min. :0.00000 Min. :0.00000
1st Qu.:0.08637 1st Qu.:0.06492 1st Qu.:0.02956 1st Qu.:0.02031
Median :0.09587 Median :0.09263 Median :0.06154 Median :0.03350
Mean :0.09636 Mean :0.10434 Mean :0.08880 Mean :0.04892
3rd Qu.:0.10530 3rd Qu.:0.13040 3rd Qu.:0.13070 3rd Qu.:0.07400
Max. :0.16340 Max. :0.34540 Max. :0.42680 Max. :0.20120
symmetry_mean fractal_dimension_mean radius_se texture_se
Min. :0.1060 Min. :0.04996 Min. :0.1115 Min. :0.3602
1st Qu.:0.1619 1st Qu.:0.05770 1st Qu.:0.2324 1st Qu.:0.8339
Median :0.1792 Median :0.06154 Median :0.3242 Median :1.1080
Mean :0.1812 Mean :0.06280 Mean :0.4052 Mean :1.2169
3rd Qu.:0.1957 3rd Qu.:0.06612 3rd Qu.:0.4789 3rd Qu.:1.4740
Max. :0.3040 Max. :0.09744 Max. :2.8730 Max. :4.8850
perimeter_se area_se smoothness_se compactness_se
Min. : 0.757 Min. : 6.802 Min. :0.001713 Min. :0.002252
1st Qu.: 1.606 1st Qu.: 17.850 1st Qu.:0.005169 1st Qu.:0.013080
Median : 2.287 Median : 24.530 Median :0.006380 Median :0.020450
Mean : 2.866 Mean : 40.337 Mean :0.007041 Mean :0.025478
3rd Qu.: 3.357 3rd Qu.: 45.190 3rd Qu.:0.008146 3rd Qu.:0.032450
Max. :21.980 Max. :542.200 Max. :0.031130 Max. :0.135400
concavity_se concave.points_se symmetry_se fractal_dimension_se
Min. :0.00000 Min. :0.000000 Min. :0.007882 Min. :0.0008948
1st Qu.:0.01509 1st Qu.:0.007638 1st Qu.:0.015160 1st Qu.:0.0022480
Median :0.02589 Median :0.010930 Median :0.018730 Median :0.0031870
Mean :0.03189 Mean :0.011796 Mean :0.020542 Mean :0.0037949
3rd Qu.:0.04205 3rd Qu.:0.014710 3rd Qu.:0.023480 3rd Qu.:0.0045580
Max. :0.39600 Max. :0.052790 Max. :0.078950 Max. :0.0298400
radius_worst texture_worst perimeter_worst area_worst smoothness_worst
Min. : 7.93 Min. :12.02 Min. : 50.41 Min. : 185.2 Min. :0.07117
1st Qu.:13.01 1st Qu.:21.08 1st Qu.: 84.11 1st Qu.: 515.3 1st Qu.:0.11660
Median :14.97 Median :25.41 Median : 97.66 Median : 686.5 Median :0.13130
Mean :16.27 Mean :25.68 Mean :107.26 Mean : 880.6 Mean :0.13237
3rd Qu.:18.79 3rd Qu.:29.72 3rd Qu.:125.40 3rd Qu.:1084.0 3rd Qu.:0.14600
Max. :36.04 Max. :49.54 Max. :251.20 Max. :4254.0 Max. :0.22260
compactness_worst concavity_worst concave.points_worst symmetry_worst
Min. :0.02729 Min. :0.0000 Min. :0.00000 Min. :0.1565
1st Qu.:0.14720 1st Qu.:0.1145 1st Qu.:0.06493 1st Qu.:0.2504
Median :0.21190 Median :0.2267 Median :0.09993 Median :0.2822
Mean :0.25427 Mean :0.2722 Mean :0.11461 Mean :0.2901
3rd Qu.:0.33910 3rd Qu.:0.3829 3rd Qu.:0.16140 3rd Qu.:0.3179
Max. :1.05800 Max. :1.2520 Max. :0.29100 Max. :0.6638
fractal_dimension_worst
Min. :0.05504
1st Qu.:0.07146
Median :0.08004
Mean :0.08395
3rd Qu.:0.09208
Max. :0.20750
Create Elasticnet Model
set.seed(1)
bc_mod <- train(bc_pca$x[,1:10], bc$diagnosis, method='glmnet', metric='Accuracy',
trControl = trainControl(method='cv', number=10),
tuneGrid = expand.grid(alpha=seq(0, 1, by=0.2),
lambda=seq(-0.2, 0.2, length=50)))
best_ix = which.max(bc_mod$results$Accuracy)
bc_mod$results[best_ix, ]
plot(bc_mod, pch='')

coef(bc_mod$finalModel,
bc_mod$finalModel$lambdaOpt)
11 x 1 sparse Matrix of class "dgCMatrix"
1
(Intercept) -0.5090167
PC1 -1.8296826
PC2 0.9360084
PC3 -0.3481095
PC4 -0.4372119
PC5 0.6865393
PC6 -0.1271122
PC7 .
PC8 -0.4762884
PC9 0.7597011
PC10 -0.1502363
Example 4: Student Success Data
ss <- read.table('data/student_success_data.csv', header=TRUE, sep=',')
ss <- na.omit(ss)
ss <- ss[(ss$G3 <= 20) & (ss$G3 >= 0), ]
ss$Passed <- factor(ifelse(ss$G3 < 10, 0, 1))
ss$G1 <- NULL
ss$G2 <- NULL
ss$G3 <- NULL
ss$absences <- NULL
summary(ss)
school sex age address famsize Pstatus Medu
GP :462 F:297 Min. :15.00 R:155 GT3:393 A: 45 Min. :1.000
MHS:101 M:266 1st Qu.:16.00 U:408 LE3:170 T:518 1st Qu.:2.000
Median :16.00 Median :3.000
Mean :16.61 Mean :2.874
3rd Qu.:18.00 3rd Qu.:4.000
Max. :22.00 Max. :4.000
Fedu Mjob Fjob reason guardian
Min. :1.000 at_home : 66 at_home : 31 course :215 father:129
1st Qu.:2.000 health : 39 health : 25 home :158 mother:398
Median :3.000 other :184 other :275 other : 48 other : 36
Mean :2.686 services:182 services:173 reputation:142
3rd Qu.:4.000 teacher : 92 teacher : 59
Max. :4.000
traveltime studytime failures schoolsup famsup paid activities
Min. :1.000 Min. :1.000 Min. :0.0000 no :507 no :254 no :332 no :262
1st Qu.:1.000 1st Qu.:1.000 1st Qu.:0.0000 yes: 56 yes:309 yes:231 yes:301
Median :1.000 Median :2.000 Median :0.0000
Mean :1.481 Mean :1.986 Mean :0.2842
3rd Qu.:2.000 3rd Qu.:2.000 3rd Qu.:0.0000
Max. :4.000 Max. :4.000 Max. :3.0000
nursery higher internet romantic famrel freetime goout
no :102 no : 22 no : 93 no :341 Min. :1.000 Min. :1.000 Min. :1.000
yes:461 yes:541 yes:470 yes:222 1st Qu.:4.000 1st Qu.:3.000 1st Qu.:2.000
Median :4.000 Median :3.000 Median :3.000
Mean :3.938 Mean :3.213 Mean :3.021
3rd Qu.:5.000 3rd Qu.:4.000 3rd Qu.:4.000
Max. :5.000 Max. :5.000 Max. :5.000
Dalc Walc health Passed
Min. :1.000 Min. :1.000 Min. :1.00 0:200
1st Qu.:1.000 1st Qu.:1.000 1st Qu.:3.00 1:363
Median :1.000 Median :2.000 Median :4.00
Mean :1.401 Mean :2.176 Mean :3.67
3rd Qu.:2.000 3rd Qu.:3.000 3rd Qu.:5.00
Max. :5.000 Max. :5.000 Max. :5.00
Feature Encoding
encoder <- dummyVars("~ .", ss[,1:29], fullRank = FALSE)
enc <- predict(encoder, ss)
ncol(enc)
[1] 55
Explained Variance
ev <- ss_pca$sdev**2 / sum(ss_pca$sdev**2)
cum_ev <- cumsum(ev)
plot(1:55, cum_ev, cex=1, pch=21, col='black', bg='steelblue',
xlab='Number of Principal Components', ylab='Cumulative Variance Explained')
abline(v=28, lty=3, lwd=2, col='steelblue')
abline(h=cum_ev[28], lty=3, lwd=2, col='steelblue')

set.seed(1)
ss_mod <- train(ss_pca$x[,1:30], ss$Passed, method='glmnet', metric='Accuracy',
trControl = trainControl(method='cv', number=10),
tuneGrid = expand.grid(alpha=seq(0, 1, by=0.2),
lambda=seq(-0.5, 0.5, length=50)))
best_ix = which.max(ss_mod$results$Accuracy)
ss_mod$results[best_ix, ]
plot(ss_mod, pch='')

LS0tDQp0aXRsZTogIkxlc3NvbiAxMC4xIC0gUHJpbmNpcGFsIENvbXBvbmVudCBBbmFseXNpcyINCmF1dGhvcjogIlJvYmJpZSBCZWFuZSINCm91dHB1dDoNCiAgaHRtbF9ub3RlYm9vazoNCiAgICB0aGVtZTogZmxhdGx5DQogICAgdG9jOiB5ZXMNCiAgICB0b2NfZGVwdGg6IDQNCi0tLQ0KDQojIyMgKipMb2FkIFBhY2thZ2VzKioNCg0KYGBge3IsIG1lc3NhZ2U9RkFMU0V9DQpsaWJyYXJ5KGNhcmV0KQ0KbGlicmFyeShzaGFwZSkNCmxpYnJhcnkoZ2dwbG90MikNCmBgYA0KDQoNCiMjIyAqKkV4YW1wbGUgMTogU3ludGhldGljIEV4YW1wbGUgd2l0aCBUd28gRmVhdHVyZXMqKg0KDQpgYGB7ciwgZmlnLmhlaWdodD02LCBmaWcud2lkdGg9Nn0NCnNldC5zZWVkKDEpDQp2MCA8LSBybm9ybShuPTUwLCAwLCAxLjIpDQp2MSA8LSBybm9ybShuPTUwLCAwLCAwLjQpDQpkZjEgPC0gZGF0YS5mcmFtZSh4MSA9IHYwICsgdjEgKyA1LCB4MiA9IDAuOCp2MCAtIHYxICsgNCkNCg0KbWVhbjEgPSBtZWFuKGRmMSR4MSkNCm1lYW4yID0gbWVhbihkZjEkeDIpDQoNCnBsb3QoZGYxLCB4bGltPWMoMiw4KSwgeWxpbT1jKDEsNyksIHBjaD0yMSwgYmc9J2Nvcm5mbG93ZXJibHVlJywgY29sPSdibGFjaycsIGNleD0xLjIpDQpwb2ludHMobWVhbjEsIG1lYW4yLCBwY2g9MjEsIGJnPSdvcmFuZ2UnLCBjb2w9J2JsYWNrJywgY2V4PTIpDQpgYGANCg0KIyMjIyAqKlBlcmZvcm0gUENBKioNCg0KDQpgYGB7cn0NCnBjYTEgPC0gcHJjb21wKGRmMSkNCnBjYTEkcm90YXRpb24NCmBgYA0KDQoNCmBgYHtyLCBmaWcuaGVpZ2h0PTYsIGZpZy53aWR0aD02fQ0KcGMxIDwtIHBjYTEkcm90YXRpb25bLDFdDQpwYzIgPC0gcGNhMSRyb3RhdGlvblssMl0NCg0KcGxvdChkZjEsIHhsaW09YygyLDgpLCB5bGltPWMoMSw3KSwgcGNoPTIxLCBiZz0nY29ybmZsb3dlcmJsdWUnLCBjb2w9J2JsYWNrJywgY2V4PTEuMikNCkFycm93cyhtZWFuMSwgbWVhbjIsIG1lYW4xICsgcGMxWzFdLCBtZWFuMiArIHBjMVsyXSwgbHdkPTMsIGFyci50eXBlPSd0cmlhbmdsZScpDQpBcnJvd3MobWVhbjEsIG1lYW4yLCBtZWFuMSArIHBjMlsxXSwgbWVhbjIgKyBwYzJbMl0sIGx3ZD0zLCBhcnIudHlwZT0ndHJpYW5nbGUnKQ0KcG9pbnRzKG1lYW4xLCBtZWFuMiwgcGNoPTIxLCBiZz0nb3JhbmdlJywgY29sPSdibGFjaycsIGNleD0yKQ0KYGBgDQoNCiMjIyMgKipUcmFuc2Zvcm1lZCBGZWF0dXJlcyoqDQoNCmBgYHtyfQ0KcGNhMSR4WzE6MTAsXQ0KYGBgDQoNCg0KYGBge3IsIGZpZy5oZWlnaHQ9NSwgZmlnLndpZHRoPTEwfQ0KcGMxIDwtIHBjYTEkcm90YXRpb25bLDFdDQpwYzIgPC0gcGNhMSRyb3RhdGlvblssMl0NCg0KcGFyKG1mcm93PWMoMSwyKSkNCg0KcGxvdChkZjEsIHhsaW09YygyLDgpLCB5bGltPWMoMSw3KSwgcGNoPTIxLCBiZz0nY29ybmZsb3dlcmJsdWUnLCBjb2w9J2JsYWNrJywgY2V4PTEuMiwNCiAgICAgbWFpbj0iT3JpZ2luYWwgRmVhdHVyZXMiKQ0KQXJyb3dzKG1lYW4xLCBtZWFuMiwgbWVhbjEgKyBwYzFbMV0sIG1lYW4yICsgcGMxWzJdLCBsd2Q9NSwgYXJyLnR5cGU9J3RyaWFuZ2xlJykNCkFycm93cyhtZWFuMSwgbWVhbjIsIG1lYW4xICsgcGMxWzFdLCBtZWFuMiArIHBjMVsyXSwgbHdkPTIsIGFyci50eXBlPSd0cmlhbmdsZScsIGNvbD0ncGx1bScpDQpBcnJvd3MobWVhbjEsIG1lYW4yLCBtZWFuMSArIHBjMlsxXSwgbWVhbjIgKyBwYzJbMl0sIGx3ZD01LCBhcnIudHlwZT0ndHJpYW5nbGUnKQ0KQXJyb3dzKG1lYW4xLCBtZWFuMiwgbWVhbjEgKyBwYzJbMV0sIG1lYW4yICsgcGMyWzJdLCBsd2Q9MiwgYXJyLnR5cGU9J3RyaWFuZ2xlJywgY29sPSdjeWFuJykNCnBvaW50cyhtZWFuMSwgbWVhbjIsIHBjaD0yMSwgYmc9J29yYW5nZScsIGNvbD0nYmxhY2snLCBjZXg9MikNCg0KcGxvdChwY2ExJHgsIHhsaW09YygtNCw0KSwgeWxpbT1jKC00LDQpLCBwY2g9MjEsIGJnPSdjb3JuZmxvd2VyYmx1ZScsIGNvbD0nYmxhY2snLCBjZXg9MS4yLCANCiAgICAgeGxhYj0nejEnLCB5bGFiPSd6MicsIG1haW49J1RyYW5zZm9ybWVkIEZlYXR1cmVzJykNCkFycm93cygwLCAwLCAxLCAwLCBsd2Q9NSwgYXJyLnR5cGU9J3RyaWFuZ2xlJykNCkFycm93cygwLCAwLCAxLCAwLCBsd2Q9MiwgYXJyLnR5cGU9J3RyaWFuZ2xlJywgY29sPSdwbHVtJykNCkFycm93cygwLCAwLCAwLCAxLCBsd2Q9NSwgYXJyLnR5cGU9J3RyaWFuZ2xlJykNCkFycm93cygwLCAwLCAwLCAxLCBsd2Q9MiwgYXJyLnR5cGU9J3RyaWFuZ2xlJywgY29sPSdjeWFuJykNCnBvaW50cygwLCAwLCBwY2g9MjEsIGJnPSdvcmFuZ2UnLCBjb2w9J2JsYWNrJywgY2V4PTIpDQoNCg0KcGFyKG1mcm93PWMoMSwxKSkNCmBgYA0KDQojIyMjICoqUHJvcG9ydGlvbiBvZiBWYXJpYW5jZSBFeHBsYWluZWQqKg0KDQpgYGB7cn0NCnN0ZCA8LSBwY2ExJHNkZXYNCnZhciA8LSBwY2ExJHNkZXZeMg0KdmFyX2V4IDwtIHZhciAvIHN1bSh2YXIpDQoNCg0KY2F0KCdTdGQgRGV2IG9mIE5ldyBGZWF0dXJlczogICAnLCByb3VuZChzdGQsIDUpLCANCiAgICAnXG5WYXJpYW5jZXMgb2YgTmV3IEZlYXR1cmVzOiAnLCByb3VuZCh2YXIsIDUpLCANCiAgICAnXG5Qcm9wIG9mIFZhcmlhbmNlIEV4cGxhaW5lZDonLCByb3VuZCh2YXJfZXgsIDUpLCBzZXA9JyAnKQ0KYGBgDQoNCiMjIyAqKkV4YW1wbGUgMjogV2luZSBDdWx0aXZhcnMgRGF0YXNldCoqDQoNCmBgYHtyfQ0Kd2MgPC0gcmVhZC50YWJsZSgiZGF0YS93aW5lX2N1bHRpdmFycy50eHQiLCBoZWFkZXIgPSBUUlVFLCBzZXA9Ilx0IikNCndjJEN1bHRpdmFyIDwtIGZhY3Rvcih3YyRDdWx0aXZhcikNCnN1bW1hcnkod2MpDQpgYGANCg0KIyMjIyAqKlBlcmZvcm0gUENBKioNCg0KYGBge3J9DQp3Y19wY2EgPC0gcHJjb21wKHNjYWxlKHdjWywtMV0pKQ0Kd2NfcGNhJHJvdGF0aW9uDQpgYGANCg0KIyMjIyAqKkV4cGxhaW5lZCBWYXJpYW5jZSoqDQoNCmBgYHtyfQ0KZXYgPC0gd2NfcGNhJHNkZXYqKjIgLyBzdW0od2NfcGNhJHNkZXYqKjIpDQpldg0KYGBgDQoNCmBgYHtyfQ0KY3VtX2V2IDwtIGN1bXN1bShldikNCmN1bV9ldg0KYGBgDQoNCmBgYHtyfQ0KcGxvdCgxOjEzLCBjdW1fZXYsIGNleD0xLjQsIHBjaD0yMSwgY29sPSdibGFjaycsIGJnPSdzdGVlbGJsdWUnLA0KICAgICB4bGFiPSdOdW1iZXIgb2YgUHJpbmNpcGFsIENvbXBvbmVudHMnLCB5bGFiPSdDdW11bGF0aXZlIFZhcmlhbmNlIEV4cGxhaW5lZCcpDQphYmxpbmUodj04LCBsdHk9MywgbHdkPTIsIGNvbD0nc3RlZWxibHVlJykNCmFibGluZShoPWN1bV9ldls4XSwgbHR5PTMsIGx3ZD0yLCBjb2w9J3N0ZWVsYmx1ZScpDQpgYGANCg0KIyMjIyAqKlBsb3R0aW5nIEZpcnN0IHR3byBUcmFuc2Zvcm1lZCBGZWF0dXJlcyoqDQoNCmBgYHtyLCBmaWcuaGVpZ2h0PTYsIGZpZy53aWR0aD02fQ0KcGxvdCh3Y19wY2EkeFssMToyXSwgcGNoPTIxLCAgY29sPSdibGFjaycsIGNleD0xLjIsIA0KICAgICBiZz1jKCdzYWxtb24nLCAnc3RlZWxibHVlJywgJ2dvbGQyJylbd2MkQ3VsdGl2YXJdLA0KICAgICBtYWluPSdGaXJzdCB0d28gVHJhbnNmb3JtZWQgRmVhdHVyZXMnKQ0KYGBgDQoNCg0KIyMjICoqRXhhbXBsZSAzOiBXaXNjb25zaW4gQnJlYXN0IENhbmNlciBEYXRhc2V0KioNCg0KYGBge3J9DQpiYyA8LSByZWFkLnRhYmxlKCJkYXRhL2JyZWFzdF9jYW5jZXIuY3N2IiwgaGVhZGVyPVRSVUUsIHNlcD0iLCIpDQpiYyRpZCA8LSBOVUxMDQpzdW1tYXJ5KGJjKQ0KYGBgDQoNCiMjIyMgKipQZXJmb3JtIFBDQSoqDQoNCmBgYHtyfQ0KYmNfcGNhIDwtIHByY29tcChzY2FsZShiY1ssLTFdKSkNCg0KZXYgPC0gYmNfcGNhJHNkZXYqKjIgLyBzdW0oYmNfcGNhJHNkZXYqKjIpDQoNCmN1bV9ldiA8LSBjdW1zdW0oZXYpDQoNCnBsb3QoMTozMCwgY3VtX2V2LCBjZXg9MS40LCBwY2g9MjEsIGNvbD0nYmxhY2snLCBiZz0nc3RlZWxibHVlJywNCiAgICAgeGxhYj0nTnVtYmVyIG9mIFByaW5jaXBhbCBDb21wb25lbnRzJywgeWxhYj0nQ3VtdWxhdGl2ZSBWYXJpYW5jZSBFeHBsYWluZWQnKQ0KDQphYmxpbmUodj03LCBsdHk9MywgbHdkPTIsIGNvbD0nc3RlZWxibHVlJykNCmFibGluZShoPWN1bV9ldls3XSwgbHR5PTMsIGx3ZD0yLCBjb2w9J3N0ZWVsYmx1ZScpDQpgYGANCg0KIyMjIyAqKlBsb3QgRmlyc3QgdHdvIFRyYW5zZm9ybWVkIEZlYXR1cmVzKioNCg0KYGBge3IsIGZpZy5oZWlnaHQ9NiwgZmlnLndpZHRoPTZ9DQpwbG90KGJjX3BjYSR4WywxOjJdLCBwY2g9MjEsICBjb2w9J2JsYWNrJywgY2V4PTEsIA0KICAgICBiZz1jKCdzYWxtb24nLCAnc3RlZWxibHVlJylbYmMkZGlhZ25vc2lzXSwNCiAgICAgbWFpbj0nRmlyc3QgVHdvIFRyYW5zZm9ybWVkIEZlYXR1cmVzJykNCmBgYA0KDQojIyMjICoqQ3JlYXRlIEVsYXN0aWNuZXQgTW9kZWwqKg0KDQpgYGB7cn0NCnNldC5zZWVkKDEpDQpiY19tb2QgPC0gdHJhaW4oYmNfcGNhJHhbLDE6MTBdLCBiYyRkaWFnbm9zaXMsIG1ldGhvZD0nZ2xtbmV0JywgbWV0cmljPSdBY2N1cmFjeScsIA0KICAgICAgICAgICAgICAgIHRyQ29udHJvbCA9IHRyYWluQ29udHJvbChtZXRob2Q9J2N2JywgbnVtYmVyPTEwKSwNCiAgICAgICAgICAgICAgICB0dW5lR3JpZCA9IGV4cGFuZC5ncmlkKGFscGhhPXNlcSgwLCAxLCBieT0wLjIpLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbGFtYmRhPXNlcSgtMC4yLCAwLjIsIGxlbmd0aD01MCkpKSANCg0KYmVzdF9peCA9IHdoaWNoLm1heChiY19tb2QkcmVzdWx0cyRBY2N1cmFjeSkNCmJjX21vZCRyZXN1bHRzW2Jlc3RfaXgsIF0NCmBgYA0KDQpgYGB7cn0NCnBsb3QoYmNfbW9kLCBwY2g9JycpDQpgYGANCg0KYGBge3J9DQpjb2VmKGJjX21vZCRmaW5hbE1vZGVsLCANCiAgICAgYmNfbW9kJGZpbmFsTW9kZWwkbGFtYmRhT3B0KQ0KYGBgDQoNCg0KIyMjICoqRXhhbXBsZSA0OiBTdHVkZW50IFN1Y2Nlc3MgRGF0YSoqDQoNCmBgYHtyfQ0Kc3MgPC0gcmVhZC50YWJsZSgnZGF0YS9zdHVkZW50X3N1Y2Nlc3NfZGF0YS5jc3YnLCBoZWFkZXI9VFJVRSwgc2VwPScsJykNCnNzIDwtIG5hLm9taXQoc3MpDQpzcyA8LSBzc1soc3MkRzMgPD0gMjApICYgKHNzJEczID49IDApLCBdDQpzcyRQYXNzZWQgPC0gZmFjdG9yKGlmZWxzZShzcyRHMyA8IDEwLCAwLCAxKSkNCnNzJEcxIDwtIE5VTEwNCnNzJEcyIDwtIE5VTEwNCnNzJEczIDwtIE5VTEwNCnNzJGFic2VuY2VzIDwtIE5VTEwNCnN1bW1hcnkoc3MpDQpgYGANCg0KIyMjIyAqKkZlYXR1cmUgRW5jb2RpbmcqKg0KDQpgYGB7cn0NCmVuY29kZXIgPC0gZHVtbXlWYXJzKCJ+IC4iLCBzc1ssMToyOV0sIGZ1bGxSYW5rID0gRkFMU0UpDQplbmMgPC0gcHJlZGljdChlbmNvZGVyLCBzcykNCm5jb2woZW5jKQ0KYGBgDQoNCiMjIyMgKipQZXJmb3JtIFBDQSoqDQoNCmBgYHtyfQ0Kc3NfcGNhIDwtIHByY29tcChlbmMsIGNlbnRlcj1UUlVFLCBzY2FsZT1UUlVFKQ0KZGltKHNzX3BjYSRyb3RhdGlvbikNCmBgYA0KDQoNCmBgYHtyfQ0KZGltKHNzX3BjYSR4KQ0KYGBgDQoNCiMjIyMgKipQbG90IEZpcnN0IFR3byBUcmFuc2Zvcm1lZCBGZWF0dXJlcyoqDQoNCmBgYHtyfQ0KcGxvdChzc19wY2EkeFssMToyXSwgcGNoPTIxLCBiZyA9IGMoJ3NhbG1vbicsICdzdGVlbGJsdWUnKVtzcyRQYXNzZWRdLCBjb2w9J2JsYWNrJykNCmBgYA0KDQojIyMjICoqRXhwbGFpbmVkIFZhcmlhbmNlKioNCg0KYGBge3J9DQpldiA8LSBzc19wY2Ekc2RldioqMiAvIHN1bShzc19wY2Ekc2RldioqMikNCmN1bV9ldiA8LSBjdW1zdW0oZXYpDQoNCnBsb3QoMTo1NSwgY3VtX2V2LCBjZXg9MSwgcGNoPTIxLCBjb2w9J2JsYWNrJywgYmc9J3N0ZWVsYmx1ZScsDQogICAgIHhsYWI9J051bWJlciBvZiBQcmluY2lwYWwgQ29tcG9uZW50cycsIHlsYWI9J0N1bXVsYXRpdmUgVmFyaWFuY2UgRXhwbGFpbmVkJykNCmFibGluZSh2PTI4LCBsdHk9MywgbHdkPTIsIGNvbD0nc3RlZWxibHVlJykNCmFibGluZShoPWN1bV9ldlsyOF0sIGx0eT0zLCBsd2Q9MiwgY29sPSdzdGVlbGJsdWUnKQ0KYGBgDQoNCg0KYGBge3J9DQpzZXQuc2VlZCgxKQ0Kc3NfbW9kIDwtIHRyYWluKHNzX3BjYSR4WywxOjMwXSwgc3MkUGFzc2VkLCBtZXRob2Q9J2dsbW5ldCcsIG1ldHJpYz0nQWNjdXJhY3knLCANCiAgICAgICAgICAgICAgICB0ckNvbnRyb2wgPSB0cmFpbkNvbnRyb2wobWV0aG9kPSdjdicsIG51bWJlcj0xMCksDQogICAgICAgICAgICAgICAgdHVuZUdyaWQgPSBleHBhbmQuZ3JpZChhbHBoYT1zZXEoMCwgMSwgYnk9MC4yKSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGxhbWJkYT1zZXEoLTAuNSwgMC41LCBsZW5ndGg9NTApKSkgDQpiZXN0X2l4ID0gd2hpY2gubWF4KHNzX21vZCRyZXN1bHRzJEFjY3VyYWN5KQ0Kc3NfbW9kJHJlc3VsdHNbYmVzdF9peCwgXQ0KYGBgDQoNCmBgYHtyfQ0KcGxvdChzc19tb2QsIHBjaD0nJykNCmBgYA0KDQo=