knitr::opts_chunk$set(cache = TRUE, dev = c("png", "pdf"))
library(here)
source(here("src", "model_linear_validation_grampians.R"))
library(PRROC)
Examining the use of area under the precision-recall curve (AUC-PR). This ignores true negatives and instead compares precision, TP/(TP + FP), and recall (or sensitivity), TP/(TP+FN), (the fraction of the presences that are correctly recalled). Data to predict to.
df <- rbind(
mds, # All southeast
mdg[names(mds)] # All grampians
)
Predict for each taxa at each location
df$pred <-
predict(model_grampians, df, type = "response", allow.new.levels = TRUE)
Split the data (including preds) by taxa and subregion.
split_df <-
split(df[c("pred", "y")], df[c("taxon", "ibra_subregion")], drop = TRUE)
Extract a single taxa and a single region
acc_bat <- split_df$`Angophora costata subsp. costata.Bateman`
egn_hnf <- split_df$`Eucalyptus (goniocalyx|nortonii).Highlands-Northern Fall`
Prevalence
mean(acc_bat$y)
[1] 0.02681992
mean(egn_hnf$y)
[1] 0.0911188
Histogram of predictions.
hist(acc_bat$pred)

pr_acc_bat <- pr.curve(
scores.class0 = acc_bat$pred, weights.class0 = acc_bat$y, curve = TRUE,
max.compute = TRUE, min.compute = TRUE, rand.compute = TRUE
)
pr_acc_bat
Precision-recall curve
Area under curve (Integral):
0.02306758
Relative area under curve (Integral):
0.009666909
Area under curve (Davis & Goadrich):
0.02306224
Relative area under curves (Davis & Goadrich):
0.009662118
Curve for scores from 1.963065e-05 to 0.9333909
( can be plotted with plot(x) )
Maximum AUC:
1 1
Minimum AUC:
0.01353148 0.01353086
AUC of a random classifier:
0.02681992 0.02681992
plot(
pr_acc_bat, max.plot = TRUE, min.plot = TRUE, rand.plot = TRUE,
fill.area = TRUE
)

roc_acc_bat <- roc.curve(
scores.class0 = acc_bat$pred, weights.class0 = acc_bat$y, curve = TRUE,
max.compute = TRUE, min.compute = TRUE, rand.compute = TRUE
)
roc_acc_bat
ROC curve
Area under curve:
0.46991
Relative area under curve:
0.46991
Curve for scores from 1.963065e-05 to 0.9333909
( can be plotted with plot(x) )
Maximum AUC:
1
Minimum AUC:
0
AUC of a random classifier:
0.5
plot(
roc_acc_bat, max.plot = TRUE, min.plot = TRUE, rand.plot = TRUE,
fill.area = TRUE
)

pr_egn_hnf <- pr.curve(
scores.class0 = egn_hnf$pred, weights.class0 = egn_hnf$y, curve = TRUE,
max.compute = TRUE, min.compute = TRUE, rand.compute = TRUE
)
plot(
pr_egn_hnf , max.plot = TRUE, min.plot = TRUE, rand.plot = TRUE,
fill.area = TRUE
)

roc_egn_hnf <- roc.curve(
scores.class0 = egn_hnf$pred, weights.class0 = egn_hnf$y, curve = TRUE,
max.compute = TRUE, min.compute = TRUE, rand.compute = TRUE
)
plot(
roc_egn_hnf, max.plot = TRUE, min.plot = TRUE, rand.plot = TRUE,
fill.area = TRUE
)

auprc <- function(x) {
AUCPR <- pr.curve(
scores.class0 = x[,1], weights.class0 = x[,2], max.compute = TRUE,
min.compute = TRUE, rand.compute = TRUE
)
AUCPR$aucpr.rel <- (AUCPR$auc.integral - AUCPR$min$auc.integral) /
(AUCPR$max$auc.integral - AUCPR$min$auc.integral)
c(
aucpr = AUCPR$auc.integral, aucpr.min = AUCPR$min$auc.integral,
aucpr.rnd = AUCPR$rand$auc.integral, aucpr.rel = AUCPR$aucpr.rel
)
}
auprc(acc_bat)
aucpr aucpr.min aucpr.rnd aucpr.rel
0.023067582 0.013531480 0.026819923 0.009666909
aucpr <- t(sapply(split_df, auprc))
prev <- sapply(split_df, function(x) c(prev = mean(x[, 2])))
perf2 <- cbind(subset(perf, !random_effect), cbind(prev, aucpr))
# should have used aa left join or merge...
# left_join(aucpr,prev)
# pairs(perf2[, 4:8])
prdist <- data.frame(
perf2[
c(
"taxon", "ibra_subregion", "aucpr", "aucpr.min", "aucpr.rnd", "aucpr.rel",
"prev"
)
],
`Geographic (km)`= dists[as.character(perf2$ibra_subregion)],
Environmental = kldists[as.character(perf2$ibra_subregion)],
Community = community_dist[as.character(perf2$ibra_subregion)],
"Dummy" = "Dummy",
check.names = FALSE
)
prdist <- subset(prdist, ibra_subregion != "Greater Grampians" )
# & !random_effect
prdist <- gather(
prdist, "distance", "value", `Geographic (km)`, Environmental, Community
)
prdist <- na.omit(prdist)
# prdistmean <- summarise(
# group_by(prdist, ibra_subregion, distance),
# meanpr = mean(aucpr), value = mean(value)
# )
relprdistmedian <- summarise(
group_by(prdist, ibra_subregion, distance),
medianpr = median(aucpr), value = median(value)
)
prdistplot <-
ggplot(prdist) +
aes(aucpr, value) +
# geom_vline(xintercept = .5, color = "grey", size = 1.5) +
geom_point(alpha=.1) +
geom_point(
aes(medianpr), data = relprdistmedian, size = 2, shape = 21, fill = "white"
) +
ylab("Distance") +
facet_grid(. ~ distance, scales = "free_x") +
coord_flip() +
scale_x_sqrt(limits = c(0, .75)) +
theme_bw() +
theme(
axis.title.y = element_blank(),
axis.text.y = element_blank(),
axis.ticks.y = element_blank(),
strip.background = element_blank()
)
aucprbw <-
ggplot(prdist) +
aes(x = Dummy, y = aucpr) +
geom_boxplot(fill = "grey") +
facet_grid(. ~ Dummy) +
scale_x_discrete(labels = "0.95") +
xlab("Dummy") +
theme_bw() +
scale_y_sqrt(limits = c(0, .75)) +
theme(
axis.title.x = element_text(color = "transparent"),
axis.text.x = element_text(color = "transparent"),
axis.ticks.x = element_line(color = "transparent"),
strip.background = element_blank(),
strip.text.x = element_text(color = "transparent"),
plot.margin = margin(5.5, 0, 5.5, 5.5)
)
grid.arrange(aucprbw, prdistplot, nrow = 1, widths = c(.14, .86))

Relative AUC-PR version
relprdistmedian <- summarise(
group_by(prdist, ibra_subregion, distance),
medianpr = median(aucpr.rel), value = median(value)
)
relprdistplot <-
ggplot(prdist) +
aes(aucpr.rel, value) +
# geom_vline(xintercept = .5, color = "grey", size = 1.5) +
geom_point(alpha=.1) +
geom_point(
aes(medianpr), data = relprdistmedian, size = 2, shape = 21, fill = "white"
) +
ylab("Distance") +
facet_grid(. ~ distance, scales = "free_x") +
coord_flip() +
scale_x_sqrt(limits = c(0, .75)) +
theme_bw() +
theme(
axis.title.y = element_blank(),
axis.text.y = element_blank(),
axis.ticks.y = element_blank(),
strip.background = element_blank()
)
aucpr.relbw <-
ggplot(prdist) +
aes(x = Dummy, y = aucpr.rel) +
geom_boxplot(fill = "grey") +
facet_grid(. ~ Dummy) +
scale_x_discrete(labels = "0.95") +
ylab("Relative AUCPR") +
xlab("Dummy") +
theme_bw() +
scale_y_sqrt(limits = c(.0, .75)) +
theme(
axis.title.x = element_text(color = "transparent"),
axis.text.x = element_text(color = "transparent"),
axis.ticks.x = element_line(color = "transparent"),
strip.background = element_blank(),
strip.text.x = element_text(color = "transparent"),
plot.margin = margin(5.5, 0, 5.5, 5.5)
)
grid.arrange(aucpr.relbw, relprdistplot, nrow = 1, widths = c(.14, .86))

# a <- subset(
# prdist,
# ibra_subregion != "Greater Grampians" & distance == "Geographic (km)"
# )
# a <- subset(
# prdist, ibra_subregion != "Greater Grampians" & distance == "Community"
# )
a <- subset(
prdist,
ibra_subregion != "Greater Grampians" & distance == "Environmental"
)
perf3 <- merge(
subset(perf2, ibra_subregion != "Greater Grampians" & !random_effect), a
)
Plot relations between AUC-PR and prevalence.
ggplot(data = perf3) +
geom_point(mapping = aes(x = prev, y = aucpr), color = "blue", alpha = 1 / 4) +
scale_x_log10()+
scale_y_log10() +
geom_line(
aes(x = prev, y = 1+ ((1-prev)*log(1-prev) ) / prev), color = "red",
linetype = "dotted"
)

Plot for relative AUC-PR.
ggplot(data = perf3) +
geom_point(
mapping = aes(x = aucpr.rnd, y = aucpr.rel), color = "blue", alpha = 1 / 4
) +
scale_y_sqrt() +
scale_x_sqrt()

# geom_abline(slope=1, color = "red", linetype = "dotted")
Plot AUC-ROC
ggplot(data = perf3) +
geom_point(mapping = aes(x = prev, y = AUC), color = "blue", alpha = 1 / 4) +
scale_x_log10()

ggplot(data = perf3) +
geom_point(
mapping = aes(x = aucpr.rel, y = AUC), color = "blue", alpha = 1 / 4
) +
scale_y_sqrt() +
scale_x_sqrt()

# library(nlme)
model_prE <- lmer(
log(aucpr) ~
1 + scale(value) + scale(log10(prev)) + (1 | ibra_subregion) + (1 | taxon),
data = perf3
)
plot(model_prE)

summary(model_prE)
Linear mixed model fit by REML ['lmerMod']
Formula:
log(aucpr) ~ 1 + scale(value) + scale(log10(prev)) + (1 | ibra_subregion) +
(1 | taxon)
Data: perf3
REML criterion at convergence: 1324
Scaled residuals:
Min 1Q Median 3Q Max
-2.5747 -0.5790 -0.1312 0.3951 5.1032
Random effects:
Groups Name Variance Std.Dev.
taxon (Intercept) 0.16250 0.4031
ibra_subregion (Intercept) 0.03419 0.1849
Residual 0.50661 0.7118
Number of obs: 560, groups: taxon, 82; ibra_subregion, 18
Fixed effects:
Estimate Std. Error t value
(Intercept) -3.55059 0.07218 -49.192
scale(value) 0.08307 0.05220 1.591
scale(log10(prev)) 1.74619 0.03564 48.996
Correlation of Fixed Effects:
(Intr) scl(v)
scale(valu) -0.004
scl(lg10()) 0.034 0.093
Strong effects of prevalence on each, but no effects of any distance, point estimates were posistive in each case and < 0.1 and SE > 0.5 * effect. Scaled effects of prevalence were ~1.7 +/- 0.04, so large and certain effects.
model_aucrocG <- lmer(
AUC ~
1 + scale(value) + scale(log10(prev)) + (1 | ibra_subregion) + (1 | taxon),
data =perf3
)
plot(model_aucrocG)

summary(model_aucrocG)
Linear mixed model fit by REML ['lmerMod']
Formula: AUC ~ 1 + scale(value) + scale(log10(prev)) + (1 | ibra_subregion) +
(1 | taxon)
Data: perf3
REML criterion at convergence: -759.7
Scaled residuals:
Min 1Q Median 3Q Max
-2.99551 -0.71856 -0.06671 0.72832 2.21426
Random effects:
Groups Name Variance Std.Dev.
taxon (Intercept) 0.0009645 0.03106
ibra_subregion (Intercept) 0.0010214 0.03196
Residual 0.0132363 0.11505
Number of obs: 560, groups: taxon, 82; ibra_subregion, 18
Fixed effects:
Estimate Std. Error t value
(Intercept) 0.6759597 0.0098977 68.295
scale(value) 0.0006611 0.0087494 0.076
scale(log10(prev)) -0.0467494 0.0054271 -8.614
Correlation of Fixed Effects:
(Intr) scl(v)
scale(valu) -0.010
scl(lg10()) 0.008 0.082
Prevalence has a clearly negative effect on AUC-ROC, larger than taxon or subregion REs. but prevalence effect (-0.05 +/-0.005) smaller than residual.
# STAN model of AUCPR
# model_pr <- stan_glmer(
# aucpr ~ value + log10(prev) + (1 | ibra_subregion/taxon),
# family = mgcv::betar,
# iter = 1000, chains = 3,
# data = prdist,
# subset = ibra_subregion != "Greater Grampians" &
# distance == "Geographic (km)"
#)
# summary(model_pr)
# summary(model_pr, regex_pars = "^[^b]", probs = c(.025, .975))
IycgLS0tCiMnIHRpdGxlOiAiRXhhbWluaW5nIHByZWNpc2lvbiByZWNhbGwiCiMnIGF1dGhvcjogIlBldGVyIEEuIFZlc2sgJiBXaWxsaWFtIEsuIE1vcnJpcyIKIycgZGF0ZTogImByIFN5cy5EYXRlKClgIgojJyBvdXRwdXQ6CiMnICAgcm1hcmtkb3duOjpodG1sX25vdGVib29rOgojJyAgICAgY29kZV9mb2xkaW5nOiBoaWRlCiMnIC0tLQoKIysgc2V0dXAsIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0UsIGZpZy5rZWVwPSJub25lIiwgZmlnLnNob3c9ImhpZGUiCmtuaXRyOjpvcHRzX2NodW5rJHNldChjYWNoZSA9IFRSVUUsIGRldiA9IGMoInBuZyIsICJwZGYiKSkKbGlicmFyeShoZXJlKQpzb3VyY2UoaGVyZSgic3JjIiwgIm1vZGVsX2xpbmVhcl92YWxpZGF0aW9uX2dyYW1waWFucy5SIikpCmxpYnJhcnkoUFJST0MpCgojJyBFeGFtaW5pbmcgdGhlIHVzZSBvZiBhcmVhIHVuZGVyIHRoZSBwcmVjaXNpb24tcmVjYWxsIGN1cnZlIChBVUMtUFIpLgojJyBUaGlzIGlnbm9yZXMgdHJ1ZSBuZWdhdGl2ZXMgYW5kIGluc3RlYWQgY29tcGFyZXMgcHJlY2lzaW9uLCBUUC8oVFAgKyBGUCksCiMnIGFuZCByZWNhbGwgKG9yIHNlbnNpdGl2aXR5KSwgVFAvKFRQK0ZOKSwgKHRoZSBmcmFjdGlvbiBvZiB0aGUgcHJlc2VuY2VzIHRoYXQKIycgYXJlIGNvcnJlY3RseSByZWNhbGxlZCkuCgojJyBEYXRhIHRvIHByZWRpY3QgdG8uCiMrIGRmCmRmIDwtIHJiaW5kKAogIG1kcywgICAgICAgICAgICAjIEFsbCBzb3V0aGVhc3QKICBtZGdbbmFtZXMobWRzKV0gIyBBbGwgZ3JhbXBpYW5zCikKCiMnIFByZWRpY3QgZm9yIGVhY2ggdGF4YSBhdCBlYWNoIGxvY2F0aW9uCiMrIHByZWRpY3QKZGYkcHJlZCA8LQogIHByZWRpY3QobW9kZWxfZ3JhbXBpYW5zLCBkZiwgdHlwZSA9ICJyZXNwb25zZSIsIGFsbG93Lm5ldy5sZXZlbHMgPSBUUlVFKQoKIycgU3BsaXQgdGhlIGRhdGEgKGluY2x1ZGluZyBwcmVkcykgYnkgdGF4YSBhbmQgc3VicmVnaW9uLgojKyBzcGxpdApzcGxpdF9kZiA8LQogIHNwbGl0KGRmW2MoInByZWQiLCAieSIpXSwgZGZbYygidGF4b24iLCAiaWJyYV9zdWJyZWdpb24iKV0sIGRyb3AgPSBUUlVFKQoKIycgRXh0cmFjdCBhIHNpbmdsZSB0YXhhIGFuZCBhIHNpbmdsZSByZWdpb24KIysgZXh0cmFjdAphY2NfYmF0IDwtIHNwbGl0X2RmJGBBbmdvcGhvcmEgY29zdGF0YSBzdWJzcC4gY29zdGF0YS5CYXRlbWFuYAplZ25faG5mIDwtIHNwbGl0X2RmJGBFdWNhbHlwdHVzIChnb25pb2NhbHl4fG5vcnRvbmlpKS5IaWdobGFuZHMtTm9ydGhlcm4gRmFsbGAKCiMnICMjIFByZXZhbGVuY2UKIysgcHJldmFsZW5jZQptZWFuKGFjY19iYXQkeSkKbWVhbihlZ25faG5mJHkpCgojJyAjIyBIaXN0b2dyYW0gb2YgcHJlZGljdGlvbnMuCiMrIGhpc3QKaGlzdChhY2NfYmF0JHByZWQpCgojKyBwcmVjaXNpb24tcmVjYWxsLWN1cnZlCnByX2FjY19iYXQgPC0gcHIuY3VydmUoCiAgc2NvcmVzLmNsYXNzMCA9IGFjY19iYXQkcHJlZCwgd2VpZ2h0cy5jbGFzczAgPSBhY2NfYmF0JHksIGN1cnZlID0gVFJVRSwKICBtYXguY29tcHV0ZSA9IFRSVUUsIG1pbi5jb21wdXRlID0gVFJVRSwgcmFuZC5jb21wdXRlID0gVFJVRQopCnByX2FjY19iYXQKCiMrIHBsb3QtcHJlY2lzaW9uLXJlY2FsbApwbG90KAogIHByX2FjY19iYXQsIG1heC5wbG90ID0gVFJVRSwgbWluLnBsb3QgPSBUUlVFLCByYW5kLnBsb3QgPSBUUlVFLAogIGZpbGwuYXJlYSA9IFRSVUUKKQoKIysgcmVjZWl2ZXItb3BlcmF0b3ItY3VydmUKcm9jX2FjY19iYXQgPC0gcm9jLmN1cnZlKAogIHNjb3Jlcy5jbGFzczAgPSBhY2NfYmF0JHByZWQsIHdlaWdodHMuY2xhc3MwID0gYWNjX2JhdCR5LCBjdXJ2ZSA9IFRSVUUsCiAgbWF4LmNvbXB1dGUgPSBUUlVFLCBtaW4uY29tcHV0ZSA9IFRSVUUsIHJhbmQuY29tcHV0ZSA9IFRSVUUKKQpyb2NfYWNjX2JhdAoKIysgcGxvdC1yZWNlaXZlci1vcGVyYXRvcgpwbG90KAogIHJvY19hY2NfYmF0LCBtYXgucGxvdCA9IFRSVUUsIG1pbi5wbG90ID0gVFJVRSwgcmFuZC5wbG90ID0gVFJVRSwKICBmaWxsLmFyZWEgPSBUUlVFCikKCiMrIHBsb3QtcHJlY2lzaW9uLXJlY2FsbC0yCnByX2Vnbl9obmYgPC0gcHIuY3VydmUoCiAgc2NvcmVzLmNsYXNzMCA9IGVnbl9obmYkcHJlZCwgd2VpZ2h0cy5jbGFzczAgPSBlZ25faG5mJHksIGN1cnZlID0gVFJVRSwKICBtYXguY29tcHV0ZSA9IFRSVUUsIG1pbi5jb21wdXRlID0gVFJVRSwgcmFuZC5jb21wdXRlID0gVFJVRQopCnBsb3QoCiAgcHJfZWduX2huZiAsIG1heC5wbG90ID0gVFJVRSwgbWluLnBsb3QgPSBUUlVFLCByYW5kLnBsb3QgPSBUUlVFLAogIGZpbGwuYXJlYSA9IFRSVUUKKQoKIysgcGxvdC1yZWNlaXZlci1vcGVyYXRvci0yCnJvY19lZ25faG5mIDwtIHJvYy5jdXJ2ZSgKICBzY29yZXMuY2xhc3MwID0gZWduX2huZiRwcmVkLCB3ZWlnaHRzLmNsYXNzMCA9IGVnbl9obmYkeSwgY3VydmUgPSBUUlVFLAogIG1heC5jb21wdXRlID0gVFJVRSwgbWluLmNvbXB1dGUgPSBUUlVFLCByYW5kLmNvbXB1dGUgPSBUUlVFCikKcGxvdCgKICByb2NfZWduX2huZiwgbWF4LnBsb3QgPSBUUlVFLCBtaW4ucGxvdCA9IFRSVUUsIHJhbmQucGxvdCA9IFRSVUUsCiAgZmlsbC5hcmVhID0gVFJVRQopCgojKyBhdXByYwphdXByYyA8LSBmdW5jdGlvbih4KSAgewogIEFVQ1BSIDwtIHByLmN1cnZlKAogICAgc2NvcmVzLmNsYXNzMCA9IHhbLDFdLCB3ZWlnaHRzLmNsYXNzMCA9IHhbLDJdLCBtYXguY29tcHV0ZSA9IFRSVUUsCiAgICBtaW4uY29tcHV0ZSA9IFRSVUUsIHJhbmQuY29tcHV0ZSA9IFRSVUUKICApCiAgQVVDUFIkYXVjcHIucmVsIDwtIChBVUNQUiRhdWMuaW50ZWdyYWwgLSBBVUNQUiRtaW4kYXVjLmludGVncmFsKSAvCiAgICAoQVVDUFIkbWF4JGF1Yy5pbnRlZ3JhbCAtIEFVQ1BSJG1pbiRhdWMuaW50ZWdyYWwpCiAgYygKICAgIGF1Y3ByID0gQVVDUFIkYXVjLmludGVncmFsLCBhdWNwci5taW4gPSBBVUNQUiRtaW4kYXVjLmludGVncmFsLAogICAgYXVjcHIucm5kID0gQVVDUFIkcmFuZCRhdWMuaW50ZWdyYWwsIGF1Y3ByLnJlbCA9IEFVQ1BSJGF1Y3ByLnJlbAogICkKfQoKYXVwcmMoYWNjX2JhdCkKCiMrIGRpc3QtcGxvdAphdWNwciA8LSB0KHNhcHBseShzcGxpdF9kZiwgYXVwcmMpKQpwcmV2IDwtIHNhcHBseShzcGxpdF9kZiwgZnVuY3Rpb24oeCkgYyhwcmV2ID0gbWVhbih4WywgMl0pKSkKCnBlcmYyIDwtIGNiaW5kKHN1YnNldChwZXJmLCAhcmFuZG9tX2VmZmVjdCksIGNiaW5kKHByZXYsIGF1Y3ByKSkKIyBzaG91bGQgaGF2ZSB1c2VkIGFhIGxlZnQgam9pbiBvciBtZXJnZS4uLgojIGxlZnRfam9pbihhdWNwcixwcmV2KQojIHBhaXJzKHBlcmYyWywgNDo4XSkKCnByZGlzdCA8LSBkYXRhLmZyYW1lKAogIHBlcmYyWwogICAgYygKICAgICAgInRheG9uIiwgImlicmFfc3VicmVnaW9uIiwgImF1Y3ByIiwgImF1Y3ByLm1pbiIsICJhdWNwci5ybmQiLCAiYXVjcHIucmVsIiwKICAgICAgInByZXYiCiAgICApCiAgXSwKICBgR2VvZ3JhcGhpYyAoa20pYD0gZGlzdHNbYXMuY2hhcmFjdGVyKHBlcmYyJGlicmFfc3VicmVnaW9uKV0sCiAgRW52aXJvbm1lbnRhbCA9IGtsZGlzdHNbYXMuY2hhcmFjdGVyKHBlcmYyJGlicmFfc3VicmVnaW9uKV0sCiAgQ29tbXVuaXR5ID0gY29tbXVuaXR5X2Rpc3RbYXMuY2hhcmFjdGVyKHBlcmYyJGlicmFfc3VicmVnaW9uKV0sCiAgIkR1bW15IiA9ICJEdW1teSIsCiAgY2hlY2submFtZXMgPSBGQUxTRQopCgpwcmRpc3QgPC0gc3Vic2V0KHByZGlzdCwgaWJyYV9zdWJyZWdpb24gIT0gIkdyZWF0ZXIgR3JhbXBpYW5zIiApCiMgJiAhcmFuZG9tX2VmZmVjdAoKcHJkaXN0IDwtIGdhdGhlcigKICBwcmRpc3QsICJkaXN0YW5jZSIsICJ2YWx1ZSIsIGBHZW9ncmFwaGljIChrbSlgLCBFbnZpcm9ubWVudGFsLCBDb21tdW5pdHkKKQoKcHJkaXN0IDwtIG5hLm9taXQocHJkaXN0KQoKIyBwcmRpc3RtZWFuIDwtIHN1bW1hcmlzZSgKIyAgIGdyb3VwX2J5KHByZGlzdCwgaWJyYV9zdWJyZWdpb24sIGRpc3RhbmNlKSwKIyAgIG1lYW5wciA9IG1lYW4oYXVjcHIpLCB2YWx1ZSA9IG1lYW4odmFsdWUpCiMgKQoKcmVscHJkaXN0bWVkaWFuIDwtIHN1bW1hcmlzZSgKICBncm91cF9ieShwcmRpc3QsIGlicmFfc3VicmVnaW9uLCBkaXN0YW5jZSksCiAgbWVkaWFucHIgPSBtZWRpYW4oYXVjcHIpLCB2YWx1ZSA9IG1lZGlhbih2YWx1ZSkKKQoKcHJkaXN0cGxvdCA8LQogIGdncGxvdChwcmRpc3QpICsKICBhZXMoYXVjcHIsIHZhbHVlKSArCiAgIyBnZW9tX3ZsaW5lKHhpbnRlcmNlcHQgPSAuNSwgY29sb3IgPSAiZ3JleSIsIHNpemUgPSAxLjUpICsKICBnZW9tX3BvaW50KGFscGhhPS4xKSArCiAgZ2VvbV9wb2ludCgKICAgIGFlcyhtZWRpYW5wciksIGRhdGEgPSByZWxwcmRpc3RtZWRpYW4sIHNpemUgPSAyLCBzaGFwZSA9IDIxLCBmaWxsID0gIndoaXRlIgogICkgKwogIHlsYWIoIkRpc3RhbmNlIikgKwogIGZhY2V0X2dyaWQoLiB+IGRpc3RhbmNlLCBzY2FsZXMgPSAiZnJlZV94IikgKwogIGNvb3JkX2ZsaXAoKSArCiAgc2NhbGVfeF9zcXJ0KGxpbWl0cyA9IGMoMCwgLjc1KSkgKwogIHRoZW1lX2J3KCkgKwogIHRoZW1lKAogICAgYXhpcy50aXRsZS55ID0gZWxlbWVudF9ibGFuaygpLAogICAgYXhpcy50ZXh0LnkgPSBlbGVtZW50X2JsYW5rKCksCiAgICBheGlzLnRpY2tzLnkgPSBlbGVtZW50X2JsYW5rKCksCiAgICBzdHJpcC5iYWNrZ3JvdW5kID0gZWxlbWVudF9ibGFuaygpCiAgKQoKYXVjcHJidyA8LQogIGdncGxvdChwcmRpc3QpICsKICBhZXMoeCA9IER1bW15LCB5ID0gYXVjcHIpICsKICBnZW9tX2JveHBsb3QoZmlsbCA9ICJncmV5IikgKwogIGZhY2V0X2dyaWQoLiB+IER1bW15KSArCiAgc2NhbGVfeF9kaXNjcmV0ZShsYWJlbHMgPSAiMC45NSIpICsKICB4bGFiKCJEdW1teSIpICsKICB0aGVtZV9idygpICsKICBzY2FsZV95X3NxcnQobGltaXRzID0gYygwLCAuNzUpKSArCiAgdGhlbWUoCiAgICBheGlzLnRpdGxlLnggPSBlbGVtZW50X3RleHQoY29sb3IgPSAidHJhbnNwYXJlbnQiKSwKICAgIGF4aXMudGV4dC54ICA9IGVsZW1lbnRfdGV4dChjb2xvciA9ICJ0cmFuc3BhcmVudCIpLAogICAgYXhpcy50aWNrcy54ID0gZWxlbWVudF9saW5lKGNvbG9yID0gInRyYW5zcGFyZW50IiksCiAgICBzdHJpcC5iYWNrZ3JvdW5kID0gZWxlbWVudF9ibGFuaygpLAogICAgc3RyaXAudGV4dC54ID0gZWxlbWVudF90ZXh0KGNvbG9yID0gInRyYW5zcGFyZW50IiksCiAgICBwbG90Lm1hcmdpbiA9IG1hcmdpbig1LjUsIDAsIDUuNSwgNS41KQogICkKCmdyaWQuYXJyYW5nZShhdWNwcmJ3LCBwcmRpc3RwbG90LCBucm93ID0gMSwgd2lkdGhzID0gYyguMTQsIC44NikpCgojJyAjIyBSZWxhdGl2ZSBBVUMtUFIgdmVyc2lvbgojKyBkaXN0LXBsb3QtcmVscHIsIG1lc3NhZ2UgPSBGQUxTRSwgd2FybmluZyA9IEZBTFNFLCBmaWcud2lkdGggPSA3LjUsIGZpZy5oZWlnaHQgPSAzLjUKcmVscHJkaXN0bWVkaWFuIDwtIHN1bW1hcmlzZSgKICBncm91cF9ieShwcmRpc3QsIGlicmFfc3VicmVnaW9uLCBkaXN0YW5jZSksCiAgbWVkaWFucHIgPSBtZWRpYW4oYXVjcHIucmVsKSwgdmFsdWUgPSBtZWRpYW4odmFsdWUpCikKCnJlbHByZGlzdHBsb3QgPC0KICBnZ3Bsb3QocHJkaXN0KSArCiAgYWVzKGF1Y3ByLnJlbCwgdmFsdWUpICsKICAjIGdlb21fdmxpbmUoeGludGVyY2VwdCA9IC41LCBjb2xvciA9ICJncmV5Iiwgc2l6ZSA9IDEuNSkgKwogIGdlb21fcG9pbnQoYWxwaGE9LjEpICsKICBnZW9tX3BvaW50KAogICAgYWVzKG1lZGlhbnByKSwgZGF0YSA9IHJlbHByZGlzdG1lZGlhbiwgc2l6ZSA9IDIsIHNoYXBlID0gMjEsIGZpbGwgPSAid2hpdGUiCiAgKSArCiAgeWxhYigiRGlzdGFuY2UiKSArCiAgZmFjZXRfZ3JpZCguIH4gZGlzdGFuY2UsIHNjYWxlcyA9ICJmcmVlX3giKSArCiAgY29vcmRfZmxpcCgpICsKICBzY2FsZV94X3NxcnQobGltaXRzID0gYygwLCAuNzUpKSArCiAgdGhlbWVfYncoKSArCiAgdGhlbWUoCiAgICBheGlzLnRpdGxlLnkgPSBlbGVtZW50X2JsYW5rKCksCiAgICBheGlzLnRleHQueSA9IGVsZW1lbnRfYmxhbmsoKSwKICAgIGF4aXMudGlja3MueSA9IGVsZW1lbnRfYmxhbmsoKSwKICAgIHN0cmlwLmJhY2tncm91bmQgPSBlbGVtZW50X2JsYW5rKCkKICApCgphdWNwci5yZWxidyA8LQogIGdncGxvdChwcmRpc3QpICsKICBhZXMoeCA9IER1bW15LCB5ID0gYXVjcHIucmVsKSArCiAgZ2VvbV9ib3hwbG90KGZpbGwgPSAiZ3JleSIpICsKICBmYWNldF9ncmlkKC4gfiBEdW1teSkgKwogIHNjYWxlX3hfZGlzY3JldGUobGFiZWxzID0gIjAuOTUiKSArCiAgeWxhYigiUmVsYXRpdmUgQVVDUFIiKSArCiAgeGxhYigiRHVtbXkiKSArCiAgdGhlbWVfYncoKSArCiAgc2NhbGVfeV9zcXJ0KGxpbWl0cyA9IGMoLjAsIC43NSkpICsKICB0aGVtZSgKICAgIGF4aXMudGl0bGUueCA9IGVsZW1lbnRfdGV4dChjb2xvciA9ICJ0cmFuc3BhcmVudCIpLAogICAgYXhpcy50ZXh0LnggID0gZWxlbWVudF90ZXh0KGNvbG9yID0gInRyYW5zcGFyZW50IiksCiAgICBheGlzLnRpY2tzLnggPSBlbGVtZW50X2xpbmUoY29sb3IgPSAidHJhbnNwYXJlbnQiKSwKICAgIHN0cmlwLmJhY2tncm91bmQgPSBlbGVtZW50X2JsYW5rKCksCiAgICBzdHJpcC50ZXh0LnggPSBlbGVtZW50X3RleHQoY29sb3IgPSAidHJhbnNwYXJlbnQiKSwKICAgIHBsb3QubWFyZ2luID0gbWFyZ2luKDUuNSwgMCwgNS41LCA1LjUpCiAgKQoKZ3JpZC5hcnJhbmdlKGF1Y3ByLnJlbGJ3LCByZWxwcmRpc3RwbG90LCBucm93ID0gMSwgd2lkdGhzID0gYyguMTQsIC44NikpCgojKyBtb2RlbHMKIyBhIDwtIHN1YnNldCgKIyAgIHByZGlzdCwKIyAgIGlicmFfc3VicmVnaW9uICE9ICJHcmVhdGVyIEdyYW1waWFucyIgJiBkaXN0YW5jZSA9PSAiR2VvZ3JhcGhpYyAoa20pIgojICkKIyBhIDwtIHN1YnNldCgKIyAgIHByZGlzdCwgaWJyYV9zdWJyZWdpb24gIT0gIkdyZWF0ZXIgR3JhbXBpYW5zIiAmIGRpc3RhbmNlID09ICJDb21tdW5pdHkiCiMgKQphIDwtIHN1YnNldCgKICBwcmRpc3QsCiAgaWJyYV9zdWJyZWdpb24gIT0gIkdyZWF0ZXIgR3JhbXBpYW5zIiAmIGRpc3RhbmNlID09ICJFbnZpcm9ubWVudGFsIgopCgpwZXJmMyA8LSBtZXJnZSgKICBzdWJzZXQocGVyZjIsIGlicmFfc3VicmVnaW9uICE9ICJHcmVhdGVyIEdyYW1waWFucyIgJiAhcmFuZG9tX2VmZmVjdCksIGEKKQoKIycgUGxvdCByZWxhdGlvbnMgYmV0d2VlbiBBVUMtUFIgYW5kIHByZXZhbGVuY2UuCiMrIHBsb3QtcGVyZjMKZ2dwbG90KGRhdGEgPSBwZXJmMykgKwpnZW9tX3BvaW50KG1hcHBpbmcgPSBhZXMoeCA9IHByZXYsIHkgPSBhdWNwciksIGNvbG9yID0gImJsdWUiLCBhbHBoYSA9IDEgLyA0KSArCnNjYWxlX3hfbG9nMTAoKSsKc2NhbGVfeV9sb2cxMCgpICsKZ2VvbV9saW5lKAogIGFlcyh4ID0gcHJldiwgeSA9IDErICgoMS1wcmV2KSpsb2coMS1wcmV2KSApIC8gcHJldiksIGNvbG9yID0gInJlZCIsCiAgbGluZXR5cGUgPSAiZG90dGVkIgopCgojJyBQbG90IGZvciByZWxhdGl2ZSBBVUMtUFIuCiMrIHBsb3QtcmVsLXBlcmYzCmdncGxvdChkYXRhID0gcGVyZjMpICsKZ2VvbV9wb2ludCgKICBtYXBwaW5nID0gYWVzKHggPSBhdWNwci5ybmQsIHkgPSBhdWNwci5yZWwpLCBjb2xvciA9ICJibHVlIiwgYWxwaGEgPSAxIC8gNAopICsKc2NhbGVfeV9zcXJ0KCkgKwpzY2FsZV94X3NxcnQoKQojIGdlb21fYWJsaW5lKHNsb3BlPTEsIGNvbG9yID0gInJlZCIsIGxpbmV0eXBlID0gImRvdHRlZCIpCgojJyBQbG90IEFVQy1ST0MKZ2dwbG90KGRhdGEgPSBwZXJmMykgKwpnZW9tX3BvaW50KG1hcHBpbmcgPSBhZXMoeCA9IHByZXYsIHkgPSBBVUMpLCBjb2xvciA9ICJibHVlIiwgYWxwaGEgPSAxIC8gNCkgKwpzY2FsZV94X2xvZzEwKCkKCmdncGxvdChkYXRhID0gcGVyZjMpICsKZ2VvbV9wb2ludCgKICBtYXBwaW5nID0gYWVzKHggPSBhdWNwci5yZWwsIHkgPSBBVUMpLCBjb2xvciA9ICJibHVlIiwgYWxwaGEgPSAxIC8gNAopICsKc2NhbGVfeV9zcXJ0KCkgKwpzY2FsZV94X3NxcnQoKQoKIysgbW9kZWwtYXVjcHIKIyBsaWJyYXJ5KG5sbWUpCm1vZGVsX3ByRSA8LSBsbWVyKAogIGxvZyhhdWNwcikgfgogICAgMSArIHNjYWxlKHZhbHVlKSArIHNjYWxlKGxvZzEwKHByZXYpKSArICgxIHwgaWJyYV9zdWJyZWdpb24pICsgKDEgfCB0YXhvbiksCiAgZGF0YSA9IHBlcmYzCikKCnBsb3QobW9kZWxfcHJFKQpzdW1tYXJ5KG1vZGVsX3ByRSkKIycgU3Ryb25nIGVmZmVjdHMgb2YgcHJldmFsZW5jZSBvbiBlYWNoLCBidXQgbm8gZWZmZWN0cyBvZiBhbnkgZGlzdGFuY2UsIHBvaW50CiMnIGVzdGltYXRlcyB3ZXJlIHBvc2lzdGl2ZSBpbiBlYWNoIGNhc2UgYW5kIDwgMC4xIGFuZCBTRSA+IDAuNSAqIGVmZmVjdC4KIycgU2NhbGVkIGVmZmVjdHMgb2YgcHJldmFsZW5jZSB3ZXJlIH4xLjcgKy8tIDAuMDQsIHNvIGxhcmdlIGFuZCBjZXJ0YWluCiMnIGVmZmVjdHMuCgojKyBtb2RlbC1hdWNybwptb2RlbF9hdWNyb2NHIDwtIGxtZXIoCiAgQVVDIH4KICAgIDEgKyBzY2FsZSh2YWx1ZSkgKyBzY2FsZShsb2cxMChwcmV2KSkgKyAoMSB8IGlicmFfc3VicmVnaW9uKSArICgxIHwgdGF4b24pLAogIGRhdGEgPXBlcmYzCiAgKQpwbG90KG1vZGVsX2F1Y3JvY0cpCnN1bW1hcnkobW9kZWxfYXVjcm9jRykKIycgUHJldmFsZW5jZSBoYXMgYSBjbGVhcmx5IG5lZ2F0aXZlIGVmZmVjdCBvbiBBVUMtUk9DLCBsYXJnZXIgdGhhbiB0YXhvbiBvcgojJyBzdWJyZWdpb24gUkVzLiBidXQgcHJldmFsZW5jZSBlZmZlY3QgKC0wLjA1ICsvLTAuMDA1KSBzbWFsbGVyIHRoYW4gcmVzaWR1YWwuCgoKIyBTVEFOIG1vZGVsIG9mIEFVQ1BSCiMrIG1vZGVscHIsIHJlc3VsdHMgPSAiaGlkZSIsIG1lc3NhZ2UgPSBGQUxTRQojIG1vZGVsX3ByIDwtIHN0YW5fZ2xtZXIoCiMgIGF1Y3ByIH4gdmFsdWUgKyBsb2cxMChwcmV2KSArICgxIHwgaWJyYV9zdWJyZWdpb24vdGF4b24pLAojICBmYW1pbHkgPSBtZ2N2OjpiZXRhciwKIyAgaXRlciA9IDEwMDAsIGNoYWlucyA9IDMsCiMgIGRhdGEgPSBwcmRpc3QsCiMgIHN1YnNldCA9IGlicmFfc3VicmVnaW9uICE9ICJHcmVhdGVyIEdyYW1waWFucyIgJgojICAgIGRpc3RhbmNlID09ICJHZW9ncmFwaGljIChrbSkiCiMpCgojIHN1bW1hcnkobW9kZWxfcHIpCgojIHN1bW1hcnkobW9kZWxfcHIsIHJlZ2V4X3BhcnMgPSAiXlteYl0iLCBwcm9icyA9IGMoLjAyNSwgLjk3NSkpCg==