Questions
a.
dat_new <- dat %>%
filter(dataset == "Cleveland") %>%
select(-id, -dataset) %>%
mutate(num = ifelse(num == 0, "No", "Yes"))
b.
colSums(is.na(dat_new))
age sex cp trestbps chol fbs restecg thalch
0 0 0 0 0 0 0 0
exang oldpeak slope ca thal num
0 0 0 5 0 0
c.
colSums(is.na(dat_new) | dat_new == "")
age sex cp trestbps chol fbs restecg thalch
0 0 0 0 0 0 0 0
exang oldpeak slope ca thal num
0 0 1 5 3 0
dat_new$slope <- na_if(dat_new$slope, "")
dat_new$thal <- na_if(dat_new$thal, "")
d.
dat_new <- dat_new %>%
mutate(
across(where(is.character), as.factor),
across(where(is.logical), as.factor)
)
e.
mice_impute <- mice(dat_new, seed = 123)
dat_imp <- complete(mice_impute, 1)
f.Â
Age and ca have the strongest positive correlation (0.37), and ca and
oldpeak have the second strongest (0.30). age and thalch have the
strongest negative correlation (-.40), and thalch and oldpeak have the
second strongest (-0.35).
data_numeric <- dat_imp %>%
select(where(is.numeric))
cor_mat <- cor(data_numeric)
kable(round(cor_mat, 2), format = "html") %>%
kable_styling(full_width = F, font_size = 10)
|
|
age
|
trestbps
|
chol
|
thalch
|
oldpeak
|
ca
|
|
age
|
1.00
|
0.28
|
0.23
|
-0.40
|
0.21
|
0.37
|
|
trestbps
|
0.28
|
1.00
|
0.13
|
-0.05
|
0.19
|
0.10
|
|
chol
|
0.23
|
0.13
|
1.00
|
-0.01
|
0.05
|
0.13
|
|
thalch
|
-0.40
|
-0.05
|
-0.01
|
1.00
|
-0.35
|
-0.27
|
|
oldpeak
|
0.21
|
0.19
|
0.05
|
-0.35
|
1.00
|
0.30
|
|
ca
|
0.37
|
0.10
|
0.13
|
-0.27
|
0.30
|
1.00
|
g.
The first PC alone explains 74.98% of the variation in the original
data set. The first and second PC explain 89.92% of the variation in the
original data set.
pca <- prcomp(data_numeric)
names(pca)
[1] "sdev" "rotation" "center" "scale" "x"
summary(pca)
Importance of components:
PC1 PC2 PC3 PC4 PC5 PC6
Standard deviation 52.2102 23.3079 17.48752 7.66560 1.10732 0.80797
Proportion of Variance 0.7498 0.1494 0.08412 0.01616 0.00034 0.00018
Cumulative Proportion 0.7498 0.8992 0.98332 0.99948 0.99982 1.00000
h.
Chol dominated PC1, thalch dominated PC2, and trestbps dominated PC3.
Due to their larger scales, these three variables had the highest
variance, resulting in them carrying more weight in PCA. You should also
scale variables before running PCA.
pca$rotation[ , 1:3]
PC1 PC2 PC3
age -0.041751366 -0.18400118 -0.126715579
trestbps -0.049911822 -0.10117012 -0.982293036
chol -0.997827582 0.02252561 0.053193289
thalch 0.009920235 0.97721420 -0.126981652
oldpeak -0.001303992 -0.01794537 -0.008982926
ca -0.002374603 -0.01145152 -0.002999127
apply(data_numeric, 2, var)
age trestbps chol thalch oldpeak ca
83.7271908 308.7382317 2715.2728852 525.6593712 1.3472012 0.8716237
i.
sum(apply(data_numeric, 2, var))
[1] 3635.617
sum(apply(pca$x, 2, var))
[1] 3635.617
j.
cor_mat <- cor(pca$x)
kable(round(cor_mat, 2), format = "html") %>%
kable_styling(full_width = F, font_size = 10)
|
|
PC1
|
PC2
|
PC3
|
PC4
|
PC5
|
PC6
|
|
PC1
|
1
|
0
|
0
|
0
|
0
|
0
|
|
PC2
|
0
|
1
|
0
|
0
|
0
|
0
|
|
PC3
|
0
|
0
|
1
|
0
|
0
|
0
|
|
PC4
|
0
|
0
|
0
|
1
|
0
|
0
|
|
PC5
|
0
|
0
|
0
|
0
|
1
|
0
|
|
PC6
|
0
|
0
|
0
|
0
|
0
|
1
|
k.
The first PC explains 34.94% of the variation in the original data
set. The first and second PC explain 53.11% of the variation in the
original data set. There is a large difference after scaling because the
variation of the original features are now more equal, meaning a couple
features can no longer have significantly larger variances than the
others.
pca_sc <- prcomp(data_numeric, scale = TRUE)
summary(pca_sc)
Importance of components:
PC1 PC2 PC3 PC4 PC5 PC6
Standard deviation 1.4479 1.0442 0.9492 0.8716 0.8427 0.66528
Proportion of Variance 0.3494 0.1817 0.1502 0.1266 0.1184 0.07377
Cumulative Proportion 0.3494 0.5311 0.6813 0.8079 0.9262 1.00000
l.
The angle between age and ca, and the angle between ca and oldpeak
are both small due to the larger positive correlation between the
variables. The angle between thalch and age, and thalch and oldpeak are
closer to 180 degrees due to the larger negative correlation between the
variables.The angle between thalch and chol is almost 90 degrees because
the variables share no correlation.
fviz_pca_var(pca_sc, col.var = "cos2",
gradient.cols = c("#00AFBB", "#E7B800", "#FC4E07"),
repel = TRUE)

m.
set.seed(23)
fviz_pca_ind(pca_sc, col.ind = pca_sc$x[,3],
select.ind = list(name = sample(1:nrow(dat_imp), 50)),
gradient.cols = c("#00AFBB", "#E7B800", "#FC4E07"))

n.Â
Yes, they are close to each other.
scale(data_numeric)[c(51, 241), ]
age trestbps chol thalch oldpeak ca
[1,] -1.459191 -1.518655 -0.9272190 0.7971434 -0.8927312 0.3629102
[2,] -1.459191 -1.234094 -0.2171591 0.1429004 -0.8927312 -0.7082033
o.
Observation 3 and 51 have very different age, thalch, and oldpeak
scores, and since PC1 or dim1 is heavily influenced by those variables,
the observations are very far apart in dim1 of the biplot. Observation 3
is on the negative side of dim1, since its scores are the opposite sign
of the weights for those variables (positive/neg score for a
negative/pos weight). Observation 51 is on the positive side since its
signs are the same (negative/pos score for a negative/pos weight).
Observation 3 and 51 have similar chol and trestbps scores, and since
PC2 or dim2 is heavily influenced by those variables, the observations
aren’t super far apart in dim2 of the biplot. They’re both on the
positive side since signs match.
scale(data_numeric)[c(3, 51), ]
age trestbps chol thalch oldpeak ca
[1,] 1.382259 -0.6649732 -0.332304 -0.9038883 1.3473157 1.4340237
[2,] -1.459191 -1.5186551 -0.927219 0.7971434 -0.8927312 0.3629102
pca_sc$rotation
PC1 PC2 PC3 PC4 PC5 PC6
age -0.5201885 -0.1508345 -0.1061464 -0.52689267 0.08619213 0.6405785
trestbps -0.2931992 -0.4929706 0.7427019 -0.09308449 0.09143817 -0.3199725
chol -0.2211667 -0.6739897 -0.5153347 0.28452697 -0.36110251 -0.1410768
thalch 0.4519486 -0.4287149 0.1060637 0.35833169 0.45216554 0.5175338
oldpeak -0.4284800 0.2840087 0.2906678 0.66220769 -0.29817699 0.3518915
ca -0.4546105 0.1245371 -0.2753536 0.25659925 0.74863326 -0.2751458
p.
Observation 51 and 174 are on the opposite side of both dims in the
biplot. This is due to them having very different scores across all
variables, with the exception of thalch. Observation 51 is on the
positive side in both dims, and 174 is on the negative, due to the same
logic I used in question o.
scale(data_numeric)[c(51, 174), ]
age trestbps chol thalch oldpeak ca
[1,] -1.4591912 -1.5186551 -0.927219 0.7971434 -0.8927312 0.3629102
[2,] 0.8358265 0.4732692 2.834179 0.3173652 0.1411366 -0.7082033
q.
data_cat <- dat_imp %>%
select(where(is.factor))
he_cont <- xtabs( ~ thal + num, data = data_cat)
kable(he_cont)
| fixed defect |
6 |
12 |
| normal |
131 |
37 |
| reversable defect |
28 |
90 |
r.
Reversable defect has the strongest positive relationship with having
heart disease, and normal has the strongest negative relationship.
Normal has the strongest positive relationship with no getting heart
disease, and reversable defect has the strongest negative
relationship.
chi_test <- chisq.test(he_cont)
chi_test$residuals
num
thal No Yes
fixed defect -1.206062 1.314027
normal 4.169611 -4.542868
reversable defect -4.504136 4.907339
s.
The angle between normal and no is extremely small since they have a
strong positive relationship, the same can be said for yes and
reversable defect. The angle between the opposites (normal and yes,
reversable and no) are near 180 degress since those relationships are
strong and negative.
he_ca <- MCA(data_cat[ , 7:8], graph = FALSE)
fviz_mca_var(he_ca, col.var = "cos2",
geom.var = c("arrow", "text"),
gradient.cols = c("#00AFBB", "#E7B800", "#FC4E07"))

t.
Fixed defect is closer to reversable defect since its also positively
associated with yes, and negatively associated with no, however the
association isn’t as strong.
u.
The variables oldpeak and thalch seem the most associated with num.
There also could be some association with ca, slope, exang, cp, and
thal.
dat_famd <- FAMD(dat_imp, graph = FALSE)
fviz_famd_var(dat_famd, col.var = "cos2",
gradient.cols = c("#00AFBB", "#E7B800", "#FC4E07"),
repel = TRUE)

v.
The really red points are observations with large positive scores on
the third dimension, meaning they align strongly with the variables that
load positively on Dim3. The really blue points have large negative Dim3
scores, meaning they align with the opposite pattern (variables with
negative loadings on Dim3).
fviz_pca_ind(dat_famd, col.ind = dat_famd$ind$coord[,3],
gradient.cols = c("#00AFBB", "#E7B800", "#FC4E07"))

LS0tCnRpdGxlOiAiSG9tZXdvcmsgMiIKYXV0aG9yOiAiQ2hhcmxpZSBNb3JnYW4iCmRhdGU6ICIgRHVlOiAwMy8wMS8yNiIKb3V0cHV0OgogIGh0bWxfZG9jdW1lbnQ6IAogICAgdG9jOiB5ZXMKICAgIHRvY19kZXB0aDogNAogICAgdG9jX2Zsb2F0OiB5ZXMKICAgIG51bWJlcl9zZWN0aW9uczogbm8KICAgIHRvY19jb2xsYXBzZWQ6IHllcwogICAgY29kZV9mb2xkaW5nOiBoaWRlCiAgICBjb2RlX2Rvd25sb2FkOiB5ZXMKICAgIHNtb290aF9zY3JvbGw6IHllcwogICAgdGhlbWU6IGx1bWVuCiAgcGRmX2RvY3VtZW50OiAKICAgIHRvYzogeWVzCiAgICB0b2NfZGVwdGg6IDQKICAgIGZpZ19jYXB0aW9uOiB5ZXMKICAgIG51bWJlcl9zZWN0aW9uczogeWVzCiAgICBmaWdfd2lkdGg6IDMKICAgIGZpZ19oZWlnaHQ6IDMKICB3b3JkX2RvY3VtZW50OiAKICAgIHRvYzogeWVzCiAgICB0b2NfZGVwdGg6IDQKICAgIGZpZ19jYXB0aW9uOiB5ZXMKICAgIGtlZXBfbWQ6IHllcwplZGl0b3Jfb3B0aW9uczogCiAgY2h1bmtfb3V0cHV0X3R5cGU6IGlubGluZQotLS0KCmBgYHtjc3MsIGVjaG8gPSBGQUxTRX0KI1RPQzo6YmVmb3JlIHsKICBjb250ZW50OiAiVGFibGUgb2YgQ29udGVudHMiOwogIGZvbnQtd2VpZ2h0OiBib2xkOwogIGZvbnQtc2l6ZTogMS4yZW07CiAgZGlzcGxheTogYmxvY2s7CiAgY29sb3I6IG5hdnk7CiAgbWFyZ2luLWJvdHRvbTogMTBweDsKfQoKCmRpdiNUT0MgbGkgeyAgICAgLyogdGFibGUgb2YgY29udGVudCAgKi8KICAgIGxpc3Qtc3R5bGU6dXBwZXItcm9tYW47CiAgICBiYWNrZ3JvdW5kLWltYWdlOm5vbmU7CiAgICBiYWNrZ3JvdW5kLXJlcGVhdDpub25lOwogICAgYmFja2dyb3VuZC1wb3NpdGlvbjowOwp9CgpoMS50aXRsZSB7ICAgIC8qIGxldmVsIDEgaGVhZGVyIG9mIHRpdGxlICAqLwogIGZvbnQtc2l6ZTogMjJweDsKICBmb250LXdlaWdodDogYm9sZDsKICBjb2xvcjogRGFya1JlZDsKICB0ZXh0LWFsaWduOiBjZW50ZXI7CiAgZm9udC1mYW1pbHk6ICJHaWxsIFNhbnMiLCBzYW5zLXNlcmlmOwp9CgpoNC5hdXRob3IgeyAvKiBIZWFkZXIgNCAtIGFuZCB0aGUgYXV0aG9yIGFuZCBkYXRhIGhlYWRlcnMgdXNlIHRoaXMgdG9vICAqLwogIGZvbnQtc2l6ZTogMTVweDsKICBmb250LXdlaWdodDogYm9sZDsKICBmb250LWZhbWlseTogc3lzdGVtLXVpOwogIGNvbG9yOiBuYXZ5OwogIHRleHQtYWxpZ246IGNlbnRlcjsKfQoKaDQuZGF0ZSB7IC8qIEhlYWRlciA0IC0gYW5kIHRoZSBhdXRob3IgYW5kIGRhdGEgaGVhZGVycyB1c2UgdGhpcyB0b28gICovCiAgZm9udC1zaXplOiAxOHB4OwogIGZvbnQtd2VpZ2h0OiBib2xkOwogIGZvbnQtZmFtaWx5OiAiR2lsbCBTYW5zIiwgc2Fucy1zZXJpZjsKICBjb2xvcjogRGFya0JsdWU7CiAgdGV4dC1hbGlnbjogY2VudGVyOwp9CgpoMSB7IC8qIEhlYWRlciAxIC0gYW5kIHRoZSBhdXRob3IgYW5kIGRhdGEgaGVhZGVycyB1c2UgdGhpcyB0b28gICovCiAgICBmb250LXNpemU6IDIwcHg7CiAgICBmb250LXdlaWdodDogYm9sZDsKICAgIGZvbnQtZmFtaWx5OiAiVGltZXMgTmV3IFJvbWFuIiwgVGltZXMsIHNlcmlmOwogICAgY29sb3I6IGRhcmtyZWQ7CiAgICB0ZXh0LWFsaWduOiBjZW50ZXI7Cn0KCmgyIHsgLyogSGVhZGVyIDIgLSBhbmQgdGhlIGF1dGhvciBhbmQgZGF0YSBoZWFkZXJzIHVzZSB0aGlzIHRvbyAgKi8KICAgIGZvbnQtc2l6ZTogMThweDsKICAgIGZvbnQtd2VpZ2h0OiBib2xkOwogICAgZm9udC1mYW1pbHk6ICJUaW1lcyBOZXcgUm9tYW4iLCBUaW1lcywgc2VyaWY7CiAgICBjb2xvcjogbmF2eTsKICAgIHRleHQtYWxpZ246IGxlZnQ7Cn0KCmgzIHsgLyogSGVhZGVyIDMgLSBhbmQgdGhlIGF1dGhvciBhbmQgZGF0YSBoZWFkZXJzIHVzZSB0aGlzIHRvbyAgKi8KICAgIGZvbnQtc2l6ZTogMTZweDsKICAgIGZvbnQtd2VpZ2h0OiBib2xkOwogICAgZm9udC1mYW1pbHk6ICJUaW1lcyBOZXcgUm9tYW4iLCBUaW1lcywgc2VyaWY7CiAgICBjb2xvcjogbmF2eTsKICAgIHRleHQtYWxpZ246IGxlZnQ7Cn0KCmg0IHsgLyogSGVhZGVyIDQgLSBhbmQgdGhlIGF1dGhvciBhbmQgZGF0YSBoZWFkZXJzIHVzZSB0aGlzIHRvbyAgKi8KICAgIGZvbnQtc2l6ZTogMTRweDsKICBmb250LXdlaWdodDogYm9sZDsKICAgIGZvbnQtZmFtaWx5OiAiVGltZXMgTmV3IFJvbWFuIiwgVGltZXMsIHNlcmlmOwogICAgY29sb3I6IGRhcmtyZWQ7CiAgICB0ZXh0LWFsaWduOiBsZWZ0Owp9CgovKiBBZGQgZG90cyBhZnRlciBudW1iZXJlZCBoZWFkZXJzICovCi5oZWFkZXItc2VjdGlvbi1udW1iZXI6OmFmdGVyIHsKICBjb250ZW50OiAiLiI7Cgpib2R5IHsgYmFja2dyb3VuZC1jb2xvcjp3aGl0ZTsgfQoKLmhpZ2hsaWdodG1lIHsgYmFja2dyb3VuZC1jb2xvcjp5ZWxsb3c7IH0KCnAgeyBiYWNrZ3JvdW5kLWNvbG9yOndoaXRlOyB9Cgp9CmBgYAoKYGBge3Igc2V0dXAsIGluY2x1ZGU9RkFMU0V9CiMgY29kZSBjaHVuayBzcGVjaWZpZXMgd2hldGhlciB0aGUgUiBjb2RlLCB3YXJuaW5ncywgYW5kIG91dHB1dCAKIyB3aWxsIGJlIGluY2x1ZGVkIGluIHRoZSBvdXRwdXQgZmlsZXMuCmlmICghcmVxdWlyZSgia25pdHIiKSkgewogICBpbnN0YWxsLnBhY2thZ2VzKCJrbml0ciIpCiAgIGxpYnJhcnkoa25pdHIpCn0KaWYgKCFyZXF1aXJlKCJmYWN0b2V4dHJhIikpIHsKICBpbnN0YWxsLnBhY2thZ2VzKCJmYWN0b2V4dHJhIikKICBsaWJyYXJ5KGZhY3RvZXh0cmEpCn0KCmlmICghcmVxdWlyZSgidGlkeXZlcnNlIikpIHsKICBpbnN0YWxsLnBhY2thZ2VzKCJ0aWR5dmVyc2UiKQogIGxpYnJhcnkodGlkeXZlcnNlKQp9CgppZiAoIXJlcXVpcmUoIkZhY3RvTWluZVIiKSkgewogIGluc3RhbGwucGFja2FnZXMoIkZhY3RvTWluZVIiKQogIGxpYnJhcnkoRmFjdG9NaW5lUikKfQoKaWYgKCFyZXF1aXJlKCJjb3JycGxvdCIpKSB7CiAgaW5zdGFsbC5wYWNrYWdlcygiY29ycnBsb3QiKQogIGxpYnJhcnkoY29ycnBsb3QpCn0KCmlmICghcmVxdWlyZSgibWljZSIpKSB7CiAgaW5zdGFsbC5wYWNrYWdlcygibWljZSIpCiAgbGlicmFyeShtaWNlKQp9CgppZiAoIXJlcXVpcmUoImthYmxlRXh0cmEiKSkgewogIGluc3RhbGwucGFja2FnZXMoImthYmxlRXh0cmEiKQogIGxpYnJhcnkoa2FibGVFeHRyYSkKfQojIyMjCmtuaXRyOjpvcHRzX2NodW5rJHNldChlY2hvID0gVFJVRSwgICAgICAgIyBpbmNsdWRlIGNvZGUgY2h1bmsgaW4gdGhlIG91dHB1dCBmaWxlCiAgICAgICAgICAgICAgICAgICAgICB3YXJuaW5nID0gRkFMU0UsICAgIyBzb21ldGltZXMsIHlvdSBjb2RlIG1heSBwcm9kdWNlIHdhcm5pbmcgbWVzc2FnZXMsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIyB5b3UgY2FuIGNob29zZSB0byBpbmNsdWRlIHRoZSB3YXJuaW5nIG1lc3NhZ2VzIGluCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIyB0aGUgb3V0cHV0IGZpbGUuIAogICAgICAgICAgICAgICAgICAgICAgcmVzdWx0cyA9IFRSVUUsICAgICMgeW91IGNhbiBhbHNvIGRlY2lkZSB3aGV0aGVyIHRvIGluY2x1ZGUgdGhlIG91dHB1dAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICMgaW4gdGhlIG91dHB1dCBmaWxlLgogICAgICAgICAgICAgICAgICAgICAgbWVzc2FnZSA9IEZBTFNFLAogICAgICAgICAgICAgICAgICAgICAgY29tbWVudCA9IE5BCiAgICAgICAgICAgICAgICAgICAgICApICAKCmBgYAoKYGBge3J9CmRhdCA8LSByZWFkLmNzdigiV2VlazJEYXRhLmNzdiIpCmBgYAoKIyBRdWVzdGlvbnMKIyMgYS4KYGBge3J9CmRhdF9uZXcgPC0gZGF0ICU+JQpmaWx0ZXIoZGF0YXNldCA9PSAiQ2xldmVsYW5kIikgJT4lCnNlbGVjdCgtaWQsIC1kYXRhc2V0KSAlPiUKbXV0YXRlKG51bSA9IGlmZWxzZShudW0gPT0gMCwgIk5vIiwgIlllcyIpKQpgYGAKCiMjIGIuCmBgYHtyfQpjb2xTdW1zKGlzLm5hKGRhdF9uZXcpKQpgYGAKCiMjIGMuCmBgYHtyfQpjb2xTdW1zKGlzLm5hKGRhdF9uZXcpIHwgZGF0X25ldyA9PSAiIikKZGF0X25ldyRzbG9wZSA8LSBuYV9pZihkYXRfbmV3JHNsb3BlLCAiIikKZGF0X25ldyR0aGFsIDwtIG5hX2lmKGRhdF9uZXckdGhhbCwgIiIpCmBgYAoKIyMgZC4KYGBge3J9CmRhdF9uZXcgPC0gZGF0X25ldyAlPiUKbXV0YXRlKAphY3Jvc3Mod2hlcmUoaXMuY2hhcmFjdGVyKSwgYXMuZmFjdG9yKSwKYWNyb3NzKHdoZXJlKGlzLmxvZ2ljYWwpLCBhcy5mYWN0b3IpCikKYGBgCgojIyBlLgpgYGB7ciwgcmVzdWx0cyA9ICdoaWRlJ30KbWljZV9pbXB1dGUgPC0gbWljZShkYXRfbmV3LCBzZWVkID0gMTIzKQpkYXRfaW1wIDwtIGNvbXBsZXRlKG1pY2VfaW1wdXRlLCAxKQpgYGAKCiMjIGYuIApBZ2UgYW5kIGNhIGhhdmUgdGhlIHN0cm9uZ2VzdCBwb3NpdGl2ZSBjb3JyZWxhdGlvbiAoMC4zNyksIGFuZCBjYSBhbmQgb2xkcGVhayBoYXZlIHRoZSBzZWNvbmQgc3Ryb25nZXN0ICgwLjMwKS4gYWdlIGFuZCB0aGFsY2ggaGF2ZSB0aGUgc3Ryb25nZXN0IG5lZ2F0aXZlIGNvcnJlbGF0aW9uICgtLjQwKSwgYW5kIHRoYWxjaCBhbmQgb2xkcGVhayBoYXZlIHRoZSBzZWNvbmQgc3Ryb25nZXN0ICgtMC4zNSkuCmBgYHtyfQpkYXRhX251bWVyaWMgPC0gZGF0X2ltcCAlPiUKICBzZWxlY3Qod2hlcmUoaXMubnVtZXJpYykpCmNvcl9tYXQgPC0gY29yKGRhdGFfbnVtZXJpYykKa2FibGUocm91bmQoY29yX21hdCwgMiksIGZvcm1hdCA9ICJodG1sIikgJT4lCiAga2FibGVfc3R5bGluZyhmdWxsX3dpZHRoID0gRiwgZm9udF9zaXplID0gMTApCmBgYAoKIyMgZy4gClRoZSBmaXJzdCBQQyBhbG9uZSBleHBsYWlucyA3NC45OCUgIG9mIHRoZSB2YXJpYXRpb24gaW4gdGhlIG9yaWdpbmFsIGRhdGEgc2V0LiBUaGUgZmlyc3QgYW5kIHNlY29uZCBQQyBleHBsYWluIDg5LjkyJSBvZiB0aGUgdmFyaWF0aW9uIGluIHRoZSBvcmlnaW5hbCBkYXRhIHNldC4KYGBge3J9CnBjYSA8LSBwcmNvbXAoZGF0YV9udW1lcmljKQogIG5hbWVzKHBjYSkKICBzdW1tYXJ5KHBjYSkKYGBgCgojIyBoLiAKQ2hvbCBkb21pbmF0ZWQgUEMxLCB0aGFsY2ggZG9taW5hdGVkIFBDMiwgYW5kIHRyZXN0YnBzIGRvbWluYXRlZCBQQzMuIER1ZSB0byB0aGVpciBsYXJnZXIgc2NhbGVzLCB0aGVzZSB0aHJlZSB2YXJpYWJsZXMgaGFkIHRoZSBoaWdoZXN0IHZhcmlhbmNlLCByZXN1bHRpbmcgaW4gdGhlbSBjYXJyeWluZyBtb3JlIHdlaWdodCBpbiBQQ0EuIFlvdSBzaG91bGQgYWxzbyBzY2FsZSB2YXJpYWJsZXMgYmVmb3JlIHJ1bm5pbmcgUENBLgpgYGB7cn0KcGNhJHJvdGF0aW9uWyAsIDE6M10KYGBgCgpgYGB7cn0KYXBwbHkoZGF0YV9udW1lcmljLCAyLCB2YXIpCmBgYAoKIyMgaS4KYGBge3J9CnN1bShhcHBseShkYXRhX251bWVyaWMsIDIsIHZhcikpCnN1bShhcHBseShwY2EkeCwgMiwgdmFyKSkKYGBgCgojIyBqLgpgYGB7cn0KY29yX21hdCA8LSBjb3IocGNhJHgpCmthYmxlKHJvdW5kKGNvcl9tYXQsIDIpLCBmb3JtYXQgPSAiaHRtbCIpICU+JQogIGthYmxlX3N0eWxpbmcoZnVsbF93aWR0aCA9IEYsIGZvbnRfc2l6ZSA9IDEwKQpgYGAKCiMjIGsuIApUaGUgZmlyc3QgUEMgZXhwbGFpbnMgMzQuOTQlIG9mIHRoZSB2YXJpYXRpb24gaW4gdGhlIG9yaWdpbmFsIGRhdGEgc2V0LiBUaGUgZmlyc3QgYW5kIHNlY29uZCBQQyBleHBsYWluIDUzLjExJSBvZiB0aGUgdmFyaWF0aW9uIGluIHRoZSBvcmlnaW5hbCBkYXRhIHNldC4gVGhlcmUgaXMgYSBsYXJnZSBkaWZmZXJlbmNlIGFmdGVyIHNjYWxpbmcgYmVjYXVzZSB0aGUgdmFyaWF0aW9uIG9mIHRoZSBvcmlnaW5hbCBmZWF0dXJlcyBhcmUgbm93IG1vcmUgZXF1YWwsIG1lYW5pbmcgYSBjb3VwbGUgZmVhdHVyZXMgY2FuIG5vIGxvbmdlciBoYXZlIHNpZ25pZmljYW50bHkgbGFyZ2VyIHZhcmlhbmNlcyB0aGFuIHRoZSBvdGhlcnMuCmBgYHtyfQpwY2Ffc2MgPC0gcHJjb21wKGRhdGFfbnVtZXJpYywgc2NhbGUgPSBUUlVFKQogIHN1bW1hcnkocGNhX3NjKQpgYGAKCiMjIGwuIApUaGUgYW5nbGUgYmV0d2VlbiBhZ2UgYW5kIGNhLCBhbmQgdGhlIGFuZ2xlIGJldHdlZW4gY2EgYW5kIG9sZHBlYWsgYXJlIGJvdGggc21hbGwgZHVlIHRvIHRoZSBsYXJnZXIgcG9zaXRpdmUgY29ycmVsYXRpb24gYmV0d2VlbiB0aGUgdmFyaWFibGVzLiBUaGUgYW5nbGUgYmV0d2VlbiB0aGFsY2ggYW5kIGFnZSwgYW5kIHRoYWxjaCBhbmQgb2xkcGVhayBhcmUgY2xvc2VyIHRvIDE4MCBkZWdyZWVzIGR1ZSB0byB0aGUgbGFyZ2VyIG5lZ2F0aXZlIGNvcnJlbGF0aW9uIGJldHdlZW4gdGhlIHZhcmlhYmxlcy5UaGUgYW5nbGUgYmV0d2VlbiB0aGFsY2ggYW5kIGNob2wgaXMgYWxtb3N0IDkwIGRlZ3JlZXMgYmVjYXVzZSB0aGUgdmFyaWFibGVzIHNoYXJlIG5vIGNvcnJlbGF0aW9uLgpgYGB7ciwgbWVzc2FnZSA9IEZBTFNFfQpmdml6X3BjYV92YXIocGNhX3NjLCBjb2wudmFyID0gImNvczIiLAogICAgICAgICAgICAgICBncmFkaWVudC5jb2xzID0gYygiIzAwQUZCQiIsICIjRTdCODAwIiwgIiNGQzRFMDciKSwgCiAgICAgICAgICAgICAgIHJlcGVsID0gVFJVRSkKYGBgCgojIyBtLgpgYGB7cn0Kc2V0LnNlZWQoMjMpCmZ2aXpfcGNhX2luZChwY2Ffc2MsIGNvbC5pbmQgPSBwY2Ffc2MkeFssM10sIAogICAgICAgICAgICAgc2VsZWN0LmluZCA9IGxpc3QobmFtZSA9IHNhbXBsZSgxOm5yb3coZGF0X2ltcCksIDUwKSksIAogICAgICAgICAgICAgZ3JhZGllbnQuY29scyA9IGMoIiMwMEFGQkIiLCAiI0U3QjgwMCIsICIjRkM0RTA3IikpCmBgYAoKIyMgbi4gClllcywgdGhleSBhcmUgY2xvc2UgdG8gZWFjaCBvdGhlci4KYGBge3J9CnNjYWxlKGRhdGFfbnVtZXJpYylbYyg1MSwgMjQxKSwgXQpgYGAKCiMjIG8uIApPYnNlcnZhdGlvbiAzIGFuZCA1MSBoYXZlIHZlcnkgZGlmZmVyZW50IGFnZSwgdGhhbGNoLCBhbmQgb2xkcGVhayBzY29yZXMsIGFuZCBzaW5jZSBQQzEgb3IgZGltMSBpcyBoZWF2aWx5IGluZmx1ZW5jZWQgYnkgdGhvc2UgdmFyaWFibGVzLCB0aGUgb2JzZXJ2YXRpb25zIGFyZSB2ZXJ5IGZhciBhcGFydCBpbiBkaW0xIG9mIHRoZSBiaXBsb3QuIE9ic2VydmF0aW9uIDMgaXMgb24gdGhlIG5lZ2F0aXZlIHNpZGUgb2YgZGltMSwgc2luY2UgaXRzIHNjb3JlcyBhcmUgdGhlIG9wcG9zaXRlIHNpZ24gb2YgdGhlIHdlaWdodHMgZm9yIHRob3NlIHZhcmlhYmxlcyAocG9zaXRpdmUvbmVnIHNjb3JlIGZvciBhIG5lZ2F0aXZlL3BvcyB3ZWlnaHQpLiBPYnNlcnZhdGlvbiA1MSBpcyBvbiB0aGUgcG9zaXRpdmUgc2lkZSBzaW5jZSBpdHMgc2lnbnMgYXJlIHRoZSBzYW1lIChuZWdhdGl2ZS9wb3Mgc2NvcmUgZm9yIGEgbmVnYXRpdmUvcG9zIHdlaWdodCkuIE9ic2VydmF0aW9uIDMgYW5kIDUxIGhhdmUgc2ltaWxhciBjaG9sIGFuZCB0cmVzdGJwcyBzY29yZXMsIGFuZCBzaW5jZSBQQzIgb3IgZGltMiBpcyBoZWF2aWx5IGluZmx1ZW5jZWQgYnkgdGhvc2UgdmFyaWFibGVzLCB0aGUgb2JzZXJ2YXRpb25zIGFyZW4ndCBzdXBlciBmYXIgYXBhcnQgaW4gZGltMiBvZiB0aGUgYmlwbG90LiBUaGV5J3JlIGJvdGggb24gdGhlIHBvc2l0aXZlIHNpZGUgc2luY2Ugc2lnbnMgbWF0Y2guCmBgYHtyfQpzY2FsZShkYXRhX251bWVyaWMpW2MoMywgNTEpLCBdCnBjYV9zYyRyb3RhdGlvbgpgYGAKCiMjIHAuCk9ic2VydmF0aW9uIDUxIGFuZCAxNzQgYXJlIG9uIHRoZSBvcHBvc2l0ZSBzaWRlIG9mIGJvdGggZGltcyBpbiB0aGUgYmlwbG90LiBUaGlzIGlzIGR1ZSB0byB0aGVtIGhhdmluZyB2ZXJ5IGRpZmZlcmVudCBzY29yZXMgYWNyb3NzIGFsbCB2YXJpYWJsZXMsIHdpdGggdGhlIGV4Y2VwdGlvbiBvZiB0aGFsY2guIE9ic2VydmF0aW9uIDUxIGlzIG9uIHRoZSBwb3NpdGl2ZSBzaWRlIGluIGJvdGggZGltcywgYW5kIDE3NCBpcyBvbiB0aGUgbmVnYXRpdmUsIGR1ZSB0byB0aGUgc2FtZSBsb2dpYyBJIHVzZWQgaW4gcXVlc3Rpb24gby4KYGBge3J9CnNjYWxlKGRhdGFfbnVtZXJpYylbYyg1MSwgMTc0KSwgXQpgYGAKCiMjIHEuCmBgYHtyfQpkYXRhX2NhdCA8LSBkYXRfaW1wICU+JQogIHNlbGVjdCh3aGVyZShpcy5mYWN0b3IpKQoKaGVfY29udCA8LSB4dGFicyggfiB0aGFsICsgbnVtLCBkYXRhID0gZGF0YV9jYXQpICAgCmthYmxlKGhlX2NvbnQpCmBgYAoKIyMgci4KUmV2ZXJzYWJsZSBkZWZlY3QgaGFzIHRoZSBzdHJvbmdlc3QgcG9zaXRpdmUgcmVsYXRpb25zaGlwIHdpdGggaGF2aW5nIGhlYXJ0IGRpc2Vhc2UsIGFuZCBub3JtYWwgaGFzIHRoZSBzdHJvbmdlc3QgbmVnYXRpdmUgcmVsYXRpb25zaGlwLiBOb3JtYWwgaGFzIHRoZSBzdHJvbmdlc3QgcG9zaXRpdmUgcmVsYXRpb25zaGlwIHdpdGggbm8gZ2V0dGluZyBoZWFydCBkaXNlYXNlLCBhbmQgcmV2ZXJzYWJsZSBkZWZlY3QgaGFzIHRoZSBzdHJvbmdlc3QgbmVnYXRpdmUgcmVsYXRpb25zaGlwLgpgYGB7cn0KY2hpX3Rlc3QgPC0gY2hpc3EudGVzdChoZV9jb250KQogIGNoaV90ZXN0JHJlc2lkdWFscwpgYGAKCiMjIHMuIApUaGUgYW5nbGUgYmV0d2VlbiBub3JtYWwgYW5kIG5vIGlzIGV4dHJlbWVseSBzbWFsbCBzaW5jZSB0aGV5IGhhdmUgYSBzdHJvbmcgcG9zaXRpdmUgcmVsYXRpb25zaGlwLCB0aGUgc2FtZSBjYW4gYmUgc2FpZCBmb3IgeWVzIGFuZCByZXZlcnNhYmxlIGRlZmVjdC4gVGhlIGFuZ2xlIGJldHdlZW4gdGhlIG9wcG9zaXRlcyAobm9ybWFsIGFuZCB5ZXMsIHJldmVyc2FibGUgYW5kIG5vKSBhcmUgbmVhciAxODAgZGVncmVzcyBzaW5jZSB0aG9zZSByZWxhdGlvbnNoaXBzIGFyZSBzdHJvbmcgYW5kIG5lZ2F0aXZlLgpgYGB7cn0KaGVfY2EgPC0gTUNBKGRhdGFfY2F0WyAsIDc6OF0sIGdyYXBoID0gRkFMU0UpIApmdml6X21jYV92YXIoaGVfY2EsIGNvbC52YXIgPSAiY29zMiIsCiAgICAgICAgICAgICBnZW9tLnZhciA9IGMoImFycm93IiwgInRleHQiKSwKICAgICAgICAgICAgIGdyYWRpZW50LmNvbHMgPSBjKCIjMDBBRkJCIiwgIiNFN0I4MDAiLCAiI0ZDNEUwNyIpKQpgYGAKCiMjIHQuIApGaXhlZCBkZWZlY3QgaXMgY2xvc2VyIHRvIHJldmVyc2FibGUgZGVmZWN0IHNpbmNlIGl0cyBhbHNvIHBvc2l0aXZlbHkgYXNzb2NpYXRlZCB3aXRoIHllcywgYW5kIG5lZ2F0aXZlbHkgYXNzb2NpYXRlZCB3aXRoIG5vLCBob3dldmVyIHRoZSBhc3NvY2lhdGlvbiBpc24ndCBhcyBzdHJvbmcuCgojIyB1LiAKVGhlIHZhcmlhYmxlcyBvbGRwZWFrIGFuZCB0aGFsY2ggc2VlbSB0aGUgbW9zdCBhc3NvY2lhdGVkIHdpdGggbnVtLiBUaGVyZSBhbHNvIGNvdWxkIGJlIHNvbWUgYXNzb2NpYXRpb24gd2l0aCBjYSwgc2xvcGUsIGV4YW5nLCBjcCwgYW5kIHRoYWwuCmBgYHtyfQpkYXRfZmFtZCA8LSBGQU1EKGRhdF9pbXAsIGdyYXBoID0gRkFMU0UpCmZ2aXpfZmFtZF92YXIoZGF0X2ZhbWQsIGNvbC52YXIgPSAiY29zMiIsCiAgICAgICAgICAgICBncmFkaWVudC5jb2xzID0gYygiIzAwQUZCQiIsICIjRTdCODAwIiwgIiNGQzRFMDciKSwKICAgICAgICAgICAgIHJlcGVsID0gVFJVRSkKYGBgCgojIyB2LgpUaGUgcmVhbGx5IHJlZCBwb2ludHMgYXJlIG9ic2VydmF0aW9ucyB3aXRoIGxhcmdlIHBvc2l0aXZlIHNjb3JlcyBvbiB0aGUgdGhpcmQgZGltZW5zaW9uLCBtZWFuaW5nIHRoZXkgYWxpZ24gc3Ryb25nbHkgd2l0aCB0aGUgdmFyaWFibGVzIHRoYXQgbG9hZCBwb3NpdGl2ZWx5IG9uIERpbTMuIFRoZSByZWFsbHkgYmx1ZSBwb2ludHMgaGF2ZSBsYXJnZSBuZWdhdGl2ZSBEaW0zIHNjb3JlcywgbWVhbmluZyB0aGV5IGFsaWduIHdpdGggdGhlIG9wcG9zaXRlIHBhdHRlcm4gKHZhcmlhYmxlcyB3aXRoIG5lZ2F0aXZlIGxvYWRpbmdzIG9uIERpbTMpLgpgYGB7cn0KZnZpel9wY2FfaW5kKGRhdF9mYW1kLCBjb2wuaW5kID0gZGF0X2ZhbWQkaW5kJGNvb3JkWywzXSwKZ3JhZGllbnQuY29scyA9IGMoIiMwMEFGQkIiLCAiI0U3QjgwMCIsICIjRkM0RTA3IikpCmBgYA==