knitr::opts_chunk$set(cache = TRUE, dev = c("png", "pdf"))
library(here)
source(here("src", "model_quadratic_grampians.R"))
source(here("src", "environmental_distance_metrics.R"))
library(dplyr)
library(ggplot2)
library(gridExtra)
library(rstanarm)
library(tidyr)
The following is a validation of the predictions/inference from a trait-environment model of the Grampians Bioregion ~20 taxa dataset.
The following steps are undertaken:
- Fit a model to Grampians data.
- Calculate Performance Metrics:
- Predict probability of occurrence
- Predict within Grampians training set:
- Predict for each species including species random effects.
- Predict for each species without species random effects.
- Predict to the South-east validation set:
- Predict to each IBRA subregion within the South-east:
- Predict for each species common to Grampians and South-east subregion:
- Predict including species random effects.
- Predict without species random effects.
- Predict for each species not found in the Grampians training set.
- For each prediction type (within-sample/out-of-sample, with-re/without-re, and modelled-species/unmodelled-species), bioregion, and species (and their combinations) calculate performance metrics
mds <- local({
nms1 <- c(
"mean_moisture_index_of_low_qtr", "topographic_wetness_index_3sec",
"relief_1000m_radius", "total_nitrogen"
)
nms2 <- c("sla_mm2_per_mg", "seed_mass_mg", "max_height_m")
vars <- lapply(nms1, function(x) poly(log(grampians[[x]]), 2))
coefs <- lapply(vars, function(x) attr(x, "coefs"))
vars <- do.call(cbind, vars)
vars <- cbind(
vars, do.call(cbind, lapply(nms2, function(x) log(grampians[[x]])))
)
vars <- scale(vars)
cntrs <- attr(vars, "scaled:center")
scales <- attr(vars, "scaled:scale")
vars_new_raw <- na.omit(southeast)
vars_new <- lapply(
seq_along(nms1), function(x) {
poly(log(vars_new_raw[[nms1[x]]]), degree = 2, coefs = coefs[[x]])
}
)
vars_new <- do.call(cbind, vars_new)
vars_new <- cbind(
vars_new, do.call(cbind, lapply(nms2, function(x) log(vars_new_raw[[x]])))
)
vars_new <- scale(vars_new, center = cntrs, scale = scales)
setNames(
data.frame(
vars_new_raw[, c("occupancy", "taxon", "ibra_subregion")], vars_new
),
c(
"y", "taxon", "ibra_subregion", "mlq", "mlq2","twi", "twi2", "r1k",
"r1k2", "tn", "tn2", "sla", "sm", "mh"
)
)
})
perf <- local({
perf <- mapply(
function(f, re.form) {
f(
model_grampians, y, list(taxon, ibra_subregion),
rbind(mds, mdg[names(mds)]), re.form
)
},
f = rep(list(auroc, pred_inf, dev_explained), each = 2),
re.form = list(NULL, NA)
)
taxon <- sub("(.*)\\..*", "\\1", rownames(perf))
ibra_subregion <- sub(".*\\.", "", rownames(perf))
dim(perf) <- dim(perf) * c(2, .5)
colnames(perf) <- c("AUC", "mean_predictive_info", "deviance_explained")
cbind(
taxon = rep(taxon, 2),
ibra_subregion = rep(ibra_subregion, 2),
random_effect = rep(c(TRUE, FALSE), each = nrow(perf) / 2),
as.data.frame(perf)
)
})
aucdist <- data.frame(
perf[c("taxon", "ibra_subregion", "AUC")],
`Geographic (km)`= dists[as.character(perf$ibra_subregion)],
Environmental = kldists[as.character(perf$ibra_subregion)],
Community = community_dist[as.character(perf$ibra_subregion)],
"Dummy" = "Dummy",
check.names = FALSE
)
aucdist <- gather(
aucdist, "distance", "value", `Geographic (km)`, Environmental, Community
)
aucdist <- na.omit(aucdist)
aucdistmean <- summarise(
group_by(aucdist, ibra_subregion, distance),
meanauc = mean(AUC), value = mean(value)
)
aucdistplot <- ggplot(aucdist) +
aes(AUC, value) +
geom_vline(xintercept = .5, color = "grey", size = 1.5) +
geom_point(alpha=.1) +
geom_point(
aes(meanauc), data = aucdistmean, size = 2, shape = 21, fill = "white"
) +
xlim(0, 1) +
ylab("Distance") +
facet_grid(distance ~ ., scales = "free_y") +
theme_bw() +
theme(
axis.title.x = element_blank(),
axis.text.x = element_blank(),
axis.ticks.x = element_blank(),
strip.background = element_blank()
)
aucbw <- ggplot(aucdist) +
aes(x = Dummy, y = AUC) +
geom_boxplot(fill = "grey") +
coord_flip() +
facet_grid(Dummy ~ .) +
ylim(0, 1) +
scale_x_discrete(labels = "0.95") +
xlab("Dummy") +
theme_bw() +
theme(
axis.title.y = element_text(color = "transparent"),
axis.text.y = element_text(color = "transparent"),
axis.ticks.y = element_line(color = "transparent"),
strip.background = element_blank(),
strip.text.y = element_text(color = "transparent"),
plot.margin = margin(0, 5.5, 5.5, 5.5)
)
grid.arrange(aucdistplot, aucbw, ncol = 1, heights = c(.86, .14))

ggplot(
transform(
subset(perf, ibra_subregion != "Greater Grampians" & !random_effect),
ibra_subregion = factor(
ibra_subregion, names(sort(tapply(AUC, ibra_subregion, median)))
)
)
) +
aes(AUC) + geom_histogram() + facet_wrap(~ibra_subregion, 3) +
xlab("AUC") +
ylab("No. of taxa") +
ggtitle(
paste(
"Area Under Receiver Operator Curve for taxa within IBRA subregions of",
"Southeast Australia"
)
)

ggplot(
transform(
subset(perf, ibra_subregion != "Greater Grampians" & !random_effect),
ibra_subregion = factor(
ibra_subregion,
names(sort(tapply(mean_predictive_info, ibra_subregion, median)))
)
)
) +
aes(mean_predictive_info) + geom_histogram() + facet_wrap(~ibra_subregion, 3) +
xlab("Mean Predictive Information") +
ylab("No. of taxa") +
ggtitle(
paste(
"Mean Predictive Information for taxa within IBRA subregions of",
"Southeast Australia"
)
)

ggplot(
transform(
subset(perf, taxon %in% mdg$taxon & random_effect),
taxon = factor(
taxon,
names(
sort(
tapply(
AUC[ibra_subregion == "Greater Grampians"],
taxon[ibra_subregion == "Greater Grampians"],
median
)
)
)
)
)
) +
aes(
AUC,
mean_predictive_info,
col = ifelse(
ibra_subregion == "Greater Grampians",
"Grampians",
"Other Region"
)
) +
geom_point() +
facet_wrap(~taxon, labeller = label_wrap_gen(40)) +
labs(col = NULL) +
xlab("AUC") +
ylab("Mean Predictive Information") +
ggtitle(
paste(
""
)
) +
theme(legend.position = "bottom")

model_auc <- stan_glmer(
(AUC - .001) ~ value + (1 | taxon) + (1 | ibra_subregion),
family = mgcv::betar,
iter = 1000, chains = 3,
data = aucdist,
subset = ibra_subregion != "Greater Grampians" & distance == "Geographic (km)"
)
summary(model_auc, regex_pars = "^[^b]", probs = c(.025, .975))
Model Info:
function: stan_glmer
family: beta [logit, link.phi=log]
formula: (AUC - 0.001) ~ value + (1 | taxon) + (1 | ibra_subregion)
algorithm: sampling
sample: 1500 (posterior sample size)
priors: see help('prior_summary')
observations: 1120
groups: taxon (82), ibra_subregion (18)
Estimates:
mean sd 2.5% 97.5%
(Intercept) 0.9 0.2 0.5 1.4
value 0.0 0.0 0.0 0.0
(phi) 9.9 0.4 9.1 10.8
Sigma[taxon:(Intercept),(Intercept)] 0.2 0.0 0.1 0.2
Sigma[ibra_subregion:(Intercept),(Intercept)] 0.1 0.0 0.0 0.1
Fit Diagnostics:
mean sd 2.5% 97.5%
mean_PPD 0.7 0.0 0.7 0.7
The mean_ppd is the sample average posterior predictive distribution of the outcome variable (for details see help('summary.stanreg')).
MCMC diagnostics
mcse Rhat n_eff
(Intercept) 0.0 1.0 751
value 0.0 1.0 723
(phi) 0.0 1.0 2248
Sigma[taxon:(Intercept),(Intercept)] 0.0 1.0 429
Sigma[ibra_subregion:(Intercept),(Intercept)] 0.0 1.0 723
mean_PPD 0.0 1.0 1485
log-posterior 0.5 1.0 375
For each parameter, mcse is Monte Carlo standard error, n_eff is a crude measure of effective sample size, and Rhat is the potential scale reduction factor on split chains (at convergence Rhat=1).
IycgLS0tCiMnIHRpdGxlOiAiVmFsaWRhdGlvbiBvZiBxdWFkcmF0aWMgbW9kZWwiCiMnIGF1dGhvcjogIldpbGxpYW0gSy4gTW9ycmlzIgojJyBkYXRlOiAiYHIgU3lzLkRhdGUoKWAiCiMnIG91dHB1dDoKIycgICBybWFya2Rvd246Omh0bWxfbm90ZWJvb2s6CiMnICAgICBjb2RlX2ZvbGRpbmc6IGhpZGUKIycgLS0tCgojKyBzZXR1cCwgbWVzc2FnZT1GQUxTRSwgZmlnLmtlZXA9Im5vbmUiLCBmaWcuc2hvdz0iaGlkZSIKa25pdHI6Om9wdHNfY2h1bmskc2V0KGNhY2hlID0gVFJVRSwgZGV2ID0gYygicG5nIiwgInBkZiIpKQpsaWJyYXJ5KGhlcmUpCnNvdXJjZShoZXJlKCJzcmMiLCAibW9kZWxfcXVhZHJhdGljX2dyYW1waWFucy5SIikpCnNvdXJjZShoZXJlKCJzcmMiLCAiZW52aXJvbm1lbnRhbF9kaXN0YW5jZV9tZXRyaWNzLlIiKSkKbGlicmFyeShkcGx5cikKbGlicmFyeShnZ3Bsb3QyKQpsaWJyYXJ5KGdyaWRFeHRyYSkKbGlicmFyeShyc3RhbmFybSkKbGlicmFyeSh0aWR5cikKCiMnIFRoZSBmb2xsb3dpbmcgaXMgYSB2YWxpZGF0aW9uIG9mIHRoZSBwcmVkaWN0aW9ucy9pbmZlcmVuY2UgZnJvbSBhCiMnIHRyYWl0LWVudmlyb25tZW50IG1vZGVsIG9mIHRoZSBHcmFtcGlhbnMgQmlvcmVnaW9uIH4yMCB0YXhhIGRhdGFzZXQuCiMnCiMnIFRoZSBmb2xsb3dpbmcgc3RlcHMgYXJlIHVuZGVydGFrZW46CiMnCiMnICogRml0IGEgbW9kZWwgdG8gR3JhbXBpYW5zIGRhdGEuCiMnICogQ2FsY3VsYXRlIFBlcmZvcm1hbmNlIE1ldHJpY3M6CiMnICAgICAqIFByZWRpY3QgcHJvYmFiaWxpdHkgb2Ygb2NjdXJyZW5jZQojJyAgICAgICAgICogUHJlZGljdCB3aXRoaW4gR3JhbXBpYW5zIHRyYWluaW5nIHNldDoKIycgICAgICAgICAgICAgKiBQcmVkaWN0IGZvciBlYWNoIHNwZWNpZXMgaW5jbHVkaW5nIHNwZWNpZXMgcmFuZG9tIGVmZmVjdHMuCiMnICAgICAgICAgICAgICogUHJlZGljdCBmb3IgZWFjaCBzcGVjaWVzIHdpdGhvdXQgc3BlY2llcyByYW5kb20gZWZmZWN0cy4KIycgICAgICAgICAqIFByZWRpY3QgdG8gdGhlIFNvdXRoLWVhc3QgdmFsaWRhdGlvbiBzZXQ6CiMnICAgICAgICAgICAgICogUHJlZGljdCB0byBlYWNoIElCUkEgc3VicmVnaW9uIHdpdGhpbiB0aGUgU291dGgtZWFzdDoKIycgICAgICAgICAgICAgICAgICogUHJlZGljdCBmb3IgZWFjaCBzcGVjaWVzIGNvbW1vbiB0byBHcmFtcGlhbnMgYW5kIFNvdXRoLWVhc3QKIycgICAgICAgICAgICAgICAgICAgc3VicmVnaW9uOgojJyAgICAgICAgICAgICAgICAgICAgICogUHJlZGljdCBpbmNsdWRpbmcgc3BlY2llcyByYW5kb20gZWZmZWN0cy4KIycgICAgICAgICAgICAgICAgICAgICAqIFByZWRpY3Qgd2l0aG91dCBzcGVjaWVzIHJhbmRvbSBlZmZlY3RzLgojJyAgICAgICAgICAgICAgICAgKiBQcmVkaWN0IGZvciBlYWNoIHNwZWNpZXMgbm90IGZvdW5kIGluIHRoZSBHcmFtcGlhbnMKIycgICAgICAgICAgICAgICAgICAgdHJhaW5pbmcgc2V0LgojJyAgICAgKiBGb3IgZWFjaCBwcmVkaWN0aW9uIHR5cGUgKHdpdGhpbi1zYW1wbGUvb3V0LW9mLXNhbXBsZSwKIycgICAgICAgd2l0aC1yZS93aXRob3V0LXJlLCBhbmQgbW9kZWxsZWQtc3BlY2llcy91bm1vZGVsbGVkLXNwZWNpZXMpLAojJyAgICAgICBiaW9yZWdpb24sIGFuZCBzcGVjaWVzIChhbmQgdGhlaXIgY29tYmluYXRpb25zKSBjYWxjdWxhdGUgcGVyZm9ybWFuY2UKIycgICAgICAgbWV0cmljcwoKIysgbWRzCm1kcyA8LSBsb2NhbCh7CiAgbm1zMSA8LSAgIGMoCiAgICAibWVhbl9tb2lzdHVyZV9pbmRleF9vZl9sb3dfcXRyIiwgInRvcG9ncmFwaGljX3dldG5lc3NfaW5kZXhfM3NlYyIsCiAgICAicmVsaWVmXzEwMDBtX3JhZGl1cyIsICJ0b3RhbF9uaXRyb2dlbiIKICApCiAgbm1zMiA8LSBjKCJzbGFfbW0yX3Blcl9tZyIsICJzZWVkX21hc3NfbWciLCAibWF4X2hlaWdodF9tIikKICB2YXJzIDwtIGxhcHBseShubXMxLCBmdW5jdGlvbih4KSBwb2x5KGxvZyhncmFtcGlhbnNbW3hdXSksIDIpKQogIGNvZWZzIDwtIGxhcHBseSh2YXJzLCBmdW5jdGlvbih4KSBhdHRyKHgsICJjb2VmcyIpKQogIHZhcnMgPC0gZG8uY2FsbChjYmluZCwgdmFycykKICB2YXJzIDwtIGNiaW5kKAogICAgdmFycywgZG8uY2FsbChjYmluZCwgbGFwcGx5KG5tczIsIGZ1bmN0aW9uKHgpIGxvZyhncmFtcGlhbnNbW3hdXSkpKQogICkKICB2YXJzIDwtIHNjYWxlKHZhcnMpCiAgY250cnMgPC0gYXR0cih2YXJzLCAic2NhbGVkOmNlbnRlciIpCiAgc2NhbGVzIDwtIGF0dHIodmFycywgInNjYWxlZDpzY2FsZSIpCgogIHZhcnNfbmV3X3JhdyA8LSBuYS5vbWl0KHNvdXRoZWFzdCkKICB2YXJzX25ldyA8LSBsYXBwbHkoCiAgICBzZXFfYWxvbmcobm1zMSksIGZ1bmN0aW9uKHgpIHsKICAgICAgcG9seShsb2codmFyc19uZXdfcmF3W1tubXMxW3hdXV0pLCBkZWdyZWUgPSAyLCBjb2VmcyA9IGNvZWZzW1t4XV0pCiAgICB9CiAgKQogIHZhcnNfbmV3IDwtIGRvLmNhbGwoY2JpbmQsIHZhcnNfbmV3KQogIHZhcnNfbmV3IDwtIGNiaW5kKAogICAgdmFyc19uZXcsIGRvLmNhbGwoY2JpbmQsIGxhcHBseShubXMyLCBmdW5jdGlvbih4KSBsb2codmFyc19uZXdfcmF3W1t4XV0pKSkKICApCiAgdmFyc19uZXcgPC0gc2NhbGUodmFyc19uZXcsIGNlbnRlciA9IGNudHJzLCBzY2FsZSA9IHNjYWxlcykKCgogIHNldE5hbWVzKAogICAgZGF0YS5mcmFtZSgKICAgICAgdmFyc19uZXdfcmF3WywgYygib2NjdXBhbmN5IiwgInRheG9uIiwgImlicmFfc3VicmVnaW9uIildLCB2YXJzX25ldwogICAgKSwKICAgIGMoCiAgICAgICJ5IiwgInRheG9uIiwgImlicmFfc3VicmVnaW9uIiwgIm1scSIsICJtbHEyIiwidHdpIiwgInR3aTIiLCAicjFrIiwKICAgICAgInIxazIiLCAidG4iLCAidG4yIiwgInNsYSIsICJzbSIsICJtaCIKICAgICkKICApCn0pCgojKyBwZXJmCnBlcmYgPC0gbG9jYWwoewogIHBlcmYgPC0gbWFwcGx5KAogICAgZnVuY3Rpb24oZiwgcmUuZm9ybSkgewogICAgICBmKAogICAgICAgIG1vZGVsX2dyYW1waWFucywgeSwgbGlzdCh0YXhvbiwgaWJyYV9zdWJyZWdpb24pLAogICAgICAgIHJiaW5kKG1kcywgbWRnW25hbWVzKG1kcyldKSwgcmUuZm9ybQogICAgICApCiAgICB9LAogICAgZiA9IHJlcChsaXN0KGF1cm9jLCBwcmVkX2luZiwgZGV2X2V4cGxhaW5lZCksIGVhY2ggPSAyKSwKICAgIHJlLmZvcm0gPSBsaXN0KE5VTEwsICBOQSkKICApCiAgdGF4b24gPC0gc3ViKCIoLiopXFwuLioiLCAiXFwxIiwgcm93bmFtZXMocGVyZikpCiAgaWJyYV9zdWJyZWdpb24gPC0gc3ViKCIuKlxcLiIsICIiLCByb3duYW1lcyhwZXJmKSkKICBkaW0ocGVyZikgPC0gZGltKHBlcmYpICogYygyLCAuNSkKICBjb2xuYW1lcyhwZXJmKSA8LSBjKCJBVUMiLCAibWVhbl9wcmVkaWN0aXZlX2luZm8iLCAiZGV2aWFuY2VfZXhwbGFpbmVkIikKICBjYmluZCgKICAgIHRheG9uID0gcmVwKHRheG9uLCAyKSwKICAgIGlicmFfc3VicmVnaW9uID0gcmVwKGlicmFfc3VicmVnaW9uLCAyKSwKICAgIHJhbmRvbV9lZmZlY3QgPSByZXAoYyhUUlVFLCBGQUxTRSksIGVhY2ggPSBucm93KHBlcmYpIC8gMiksCiAgICBhcy5kYXRhLmZyYW1lKHBlcmYpCiAgKQp9KQoKIysgcGxvdC1hdWMtZGlzdCwgbWVzc2FnZSA9IEZBTFNFLCB3YXJuaW5nID0gRkFMU0UsIGZpZy53aWR0aCA9IDMuNSwgZmlnLmhlaWdodCA9IDcKYXVjZGlzdCA8LSBkYXRhLmZyYW1lKAogIHBlcmZbYygidGF4b24iLCAiaWJyYV9zdWJyZWdpb24iLCAiQVVDIildLAogIGBHZW9ncmFwaGljIChrbSlgPSBkaXN0c1thcy5jaGFyYWN0ZXIocGVyZiRpYnJhX3N1YnJlZ2lvbildLAogIEVudmlyb25tZW50YWwgPSBrbGRpc3RzW2FzLmNoYXJhY3RlcihwZXJmJGlicmFfc3VicmVnaW9uKV0sCiAgQ29tbXVuaXR5ID0gY29tbXVuaXR5X2Rpc3RbYXMuY2hhcmFjdGVyKHBlcmYkaWJyYV9zdWJyZWdpb24pXSwKICAiRHVtbXkiID0gIkR1bW15IiwKICBjaGVjay5uYW1lcyA9IEZBTFNFCikKCmF1Y2Rpc3QgPC0gZ2F0aGVyKAogIGF1Y2Rpc3QsICJkaXN0YW5jZSIsICJ2YWx1ZSIsIGBHZW9ncmFwaGljIChrbSlgLCBFbnZpcm9ubWVudGFsLCBDb21tdW5pdHkKKQoKYXVjZGlzdCA8LSBuYS5vbWl0KGF1Y2Rpc3QpCgphdWNkaXN0bWVhbiA8LSBzdW1tYXJpc2UoCiAgZ3JvdXBfYnkoYXVjZGlzdCwgaWJyYV9zdWJyZWdpb24sIGRpc3RhbmNlKSwKICBtZWFuYXVjID0gbWVhbihBVUMpLCB2YWx1ZSA9IG1lYW4odmFsdWUpCikKCmF1Y2Rpc3RwbG90IDwtIGdncGxvdChhdWNkaXN0KSArCiAgYWVzKEFVQywgdmFsdWUpICsKICBnZW9tX3ZsaW5lKHhpbnRlcmNlcHQgPSAuNSwgY29sb3IgPSAiZ3JleSIsIHNpemUgPSAxLjUpICsKICBnZW9tX3BvaW50KGFscGhhPS4xKSArCiAgZ2VvbV9wb2ludCgKICAgIGFlcyhtZWFuYXVjKSwgZGF0YSA9IGF1Y2Rpc3RtZWFuLCBzaXplID0gMiwgc2hhcGUgPSAyMSwgZmlsbCA9ICJ3aGl0ZSIKICApICsKICB4bGltKDAsIDEpICsKICB5bGFiKCJEaXN0YW5jZSIpICsKICBmYWNldF9ncmlkKGRpc3RhbmNlIH4gLiwgc2NhbGVzID0gImZyZWVfeSIpICsKICB0aGVtZV9idygpICsKICB0aGVtZSgKICAgIGF4aXMudGl0bGUueCA9IGVsZW1lbnRfYmxhbmsoKSwKICAgIGF4aXMudGV4dC54ID0gZWxlbWVudF9ibGFuaygpLAogICAgYXhpcy50aWNrcy54ID0gZWxlbWVudF9ibGFuaygpLAogICAgc3RyaXAuYmFja2dyb3VuZCA9IGVsZW1lbnRfYmxhbmsoKQogICkKCmF1Y2J3IDwtIGdncGxvdChhdWNkaXN0KSArCiAgYWVzKHggPSBEdW1teSwgeSA9IEFVQykgKwogIGdlb21fYm94cGxvdChmaWxsID0gImdyZXkiKSArCiAgY29vcmRfZmxpcCgpICsKICBmYWNldF9ncmlkKER1bW15IH4gLikgKwogIHlsaW0oMCwgMSkgKwogIHNjYWxlX3hfZGlzY3JldGUobGFiZWxzID0gIjAuOTUiKSArCiAgeGxhYigiRHVtbXkiKSArCiAgdGhlbWVfYncoKSArCiAgdGhlbWUoCiAgICBheGlzLnRpdGxlLnkgPSBlbGVtZW50X3RleHQoY29sb3IgPSAidHJhbnNwYXJlbnQiKSwKICAgIGF4aXMudGV4dC55ICA9IGVsZW1lbnRfdGV4dChjb2xvciA9ICJ0cmFuc3BhcmVudCIpLAogICAgYXhpcy50aWNrcy55ID0gZWxlbWVudF9saW5lKGNvbG9yID0gInRyYW5zcGFyZW50IiksCiAgICBzdHJpcC5iYWNrZ3JvdW5kID0gZWxlbWVudF9ibGFuaygpLAogICAgc3RyaXAudGV4dC55ID0gZWxlbWVudF90ZXh0KGNvbG9yID0gInRyYW5zcGFyZW50IiksCiAgICBwbG90Lm1hcmdpbiA9IG1hcmdpbigwLCA1LjUsIDUuNSwgNS41KQogICkKCmdyaWQuYXJyYW5nZShhdWNkaXN0cGxvdCwgYXVjYncsIG5jb2wgPSAxLCBoZWlnaHRzID0gYyguODYsIC4xNCkpCgojKyBwbG90LWF1YywgbWVzc2FnZSA9IEZBTFNFLCB3YXJuaW5nID0gRkFMU0UsIGZpZy53aWR0aCA9IDExCmdncGxvdCgKICB0cmFuc2Zvcm0oCiAgICBzdWJzZXQocGVyZiwgaWJyYV9zdWJyZWdpb24gIT0gIkdyZWF0ZXIgR3JhbXBpYW5zIiAmICFyYW5kb21fZWZmZWN0KSwKICAgIGlicmFfc3VicmVnaW9uID0gZmFjdG9yKAogICAgICBpYnJhX3N1YnJlZ2lvbiwgbmFtZXMoc29ydCh0YXBwbHkoQVVDLCBpYnJhX3N1YnJlZ2lvbiwgbWVkaWFuKSkpCiAgICApCiAgKQopICsKYWVzKEFVQykgKyBnZW9tX2hpc3RvZ3JhbSgpICsgZmFjZXRfd3JhcCh+aWJyYV9zdWJyZWdpb24sIDMpICsKeGxhYigiQVVDIikgKwp5bGFiKCJOby4gb2YgdGF4YSIpICsKZ2d0aXRsZSgKICBwYXN0ZSgKICAgICJBcmVhIFVuZGVyIFJlY2VpdmVyIE9wZXJhdG9yIEN1cnZlIGZvciB0YXhhIHdpdGhpbiBJQlJBIHN1YnJlZ2lvbnMgb2YiLAogICAgIlNvdXRoZWFzdCBBdXN0cmFsaWEiCiAgKQopCgojKyBwbG90LXByZWQtaW5mbywgbWVzc2FnZSA9IEZBTFNFLCB3YXJuaW5nID0gRkFMU0UsIGZpZy53aWR0aCA9IDExCmdncGxvdCgKICB0cmFuc2Zvcm0oCiAgICBzdWJzZXQocGVyZiwgaWJyYV9zdWJyZWdpb24gIT0gIkdyZWF0ZXIgR3JhbXBpYW5zIiAmICFyYW5kb21fZWZmZWN0KSwKICAgIGlicmFfc3VicmVnaW9uID0gZmFjdG9yKAogICAgICBpYnJhX3N1YnJlZ2lvbiwKICAgICAgbmFtZXMoc29ydCh0YXBwbHkobWVhbl9wcmVkaWN0aXZlX2luZm8sIGlicmFfc3VicmVnaW9uLCBtZWRpYW4pKSkKICAgICkKICApCikgKwphZXMobWVhbl9wcmVkaWN0aXZlX2luZm8pICsgZ2VvbV9oaXN0b2dyYW0oKSArIGZhY2V0X3dyYXAofmlicmFfc3VicmVnaW9uLCAzKSArCnhsYWIoIk1lYW4gUHJlZGljdGl2ZSBJbmZvcm1hdGlvbiIpICsKeWxhYigiTm8uIG9mIHRheGEiKSArCmdndGl0bGUoCiAgcGFzdGUoCiAgICAiTWVhbiBQcmVkaWN0aXZlIEluZm9ybWF0aW9uIGZvciB0YXhhIHdpdGhpbiBJQlJBIHN1YnJlZ2lvbnMgb2YiLAogICAgIlNvdXRoZWFzdCBBdXN0cmFsaWEiCiAgKQopCgojKyBwbG90LWdyYW1waWFucy10YXhhLCBtZXNzYWdlID0gRkFMU0UsIHdhcm5pbmcgPSBGQUxTRSwgZmlnLndpZHRoID0gMTEKZ2dwbG90KAogIHRyYW5zZm9ybSgKICAgIHN1YnNldChwZXJmLCB0YXhvbiAlaW4lIG1kZyR0YXhvbiAmIHJhbmRvbV9lZmZlY3QpLAogICAgdGF4b24gPSBmYWN0b3IoCiAgICAgIHRheG9uLAogICAgICBuYW1lcygKICAgICAgICBzb3J0KAogICAgICAgICAgdGFwcGx5KAogICAgICAgICAgICBBVUNbaWJyYV9zdWJyZWdpb24gPT0gIkdyZWF0ZXIgR3JhbXBpYW5zIl0sCiAgICAgICAgICAgIHRheG9uW2licmFfc3VicmVnaW9uID09ICJHcmVhdGVyIEdyYW1waWFucyJdLAogICAgICAgICAgICBtZWRpYW4KICAgICAgICAgICkKICAgICAgICApCiAgICAgICkKICAgICkKICApCikgKwphZXMoCiAgQVVDLAogIG1lYW5fcHJlZGljdGl2ZV9pbmZvLAogIGNvbCA9IGlmZWxzZSgKICAgIGlicmFfc3VicmVnaW9uID09ICJHcmVhdGVyIEdyYW1waWFucyIsCiAgICAiR3JhbXBpYW5zIiwKICAgICJPdGhlciBSZWdpb24iCiAgKQopICsKZ2VvbV9wb2ludCgpICsKZmFjZXRfd3JhcCh+dGF4b24sIGxhYmVsbGVyID0gbGFiZWxfd3JhcF9nZW4oNDApKSArCmxhYnMoY29sID0gTlVMTCkgKwp4bGFiKCJBVUMiKSArCnlsYWIoIk1lYW4gUHJlZGljdGl2ZSBJbmZvcm1hdGlvbiIpICsKZ2d0aXRsZSgKICBwYXN0ZSgKICAgICIiCiAgKQopICsKdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gImJvdHRvbSIpCgojKyBtb2RlbGF1YywgcmVzdWx0cyA9ICJoaWRlIiwgbWVzc2FnZSA9IEZBTFNFCm1vZGVsX2F1YyA8LSBzdGFuX2dsbWVyKAogIChBVUMgLSAuMDAxKSB+IHZhbHVlICsgKDEgfCB0YXhvbikgKyAoMSB8IGlicmFfc3VicmVnaW9uKSwKICBmYW1pbHkgPSBtZ2N2OjpiZXRhciwKICBpdGVyID0gMTAwMCwgY2hhaW5zID0gMywKICBkYXRhID0gYXVjZGlzdCwKICBzdWJzZXQgPSBpYnJhX3N1YnJlZ2lvbiAhPSAiR3JlYXRlciBHcmFtcGlhbnMiICYgZGlzdGFuY2UgPT0gIkdlb2dyYXBoaWMgKGttKSIKKQoKIysgc3VtbWFyeV9hdWMKc3VtbWFyeShtb2RlbF9hdWMsIHJlZ2V4X3BhcnMgPSAiXlteYl0iLCBwcm9icyA9IGMoLjAyNSwgLjk3NSkpCg==