knitr::opts_chunk$set(cache = TRUE, dev = c("png", "pdf"))
library(here)
source(here("src", "model_linear_grampians_internal_validation.R"))
library(arm)
library(forcats)
library(ggplot2)
library(gridExtra)
library(tidyr)
vars <- c("mlq", "twi", "r1k", "tn")
taxa <- unique(mds$taxon)
taxa_x_region <- unique(mds[, c("taxon", "ibra_subregion")])
traits <- sapply(
taxa,
function(x) unlist(unique(mds[mds$taxon == x, c("sla", "sm", "mh")]))
)
Calculate the responses of each species in each region using single-species/single-region glms.
observed_responses <- mapply(
function(taxoni, region) {
df <- subset(mds, taxon == taxoni & ibra_subregion == region)
model <- bayesglm(
formula(sprintf("y ~ %s", paste(vars, collapse = " + "))),
binomial, df, prior.scale = 1, prior.df = Inf
)
data.frame(
Taxon = taxoni, Region = region, Var = vars,
summary(model)$coefficients[vars, 1:2]
)
},
taxa_x_region[, 1], taxa_x_region[, 2],
SIMPLIFY = FALSE
)
observed_responses <- do.call(rbind, observed_responses)
Calculate the predicted responses of each species in each region based on the Grampians model and their trait values.
nsims <- 1000
sims <- fixef(sim(model_grampians, nsims))
predicted_responses <- function(taxon) {
ans <- vapply(
vars, function(x) {
rowSums(
sweep(sims[, grep(x, colnames(sims))], 2, c(1, traits[, taxon]), "*")
)
},
numeric(nsims)
)
t(
apply(
ans, 2, function(x) c(Prediction = mean(x), "Predicted..Error" = sd(x))
)
)
}
predicted_responses <- do.call(
rbind, sapply(taxa, predicted_responses, simplify = FALSE)[taxa_x_region[, 1]]
)
responses <- cbind(observed_responses, predicted_responses)
responses <- merge(
responses, subset(perf, !random_effect),
by.x = c("Taxon", "Region"), by.y = c("taxon", "ibra_subregion")
)
plot_regions <- c(
"Snowy Mountains", "Jervis", "Victorian Alps", "Strzelecki Ranges",
"Greater Grampians"
)
responses <- rbind(responses, grampians_responses)
responses$Region <- forcats::fct_relevel(responses$Region, plot_regions)
taxon_region_prevalence <- summarise(
group_by(rbind(mds, mdg[names(mds)]), taxon, ibra_subregion),
prevalence = mean(y)
)
responses <- left_join(
responses, taxon_region_prevalence,
by = c("Taxon" = "taxon", "Region" = "ibra_subregion")
)
perf$ibra_subregion <- forcats::fct_relevel(perf$ibra_subregion, plot_regions)
perf <- left_join(perf, taxon_region_prevalence)
Plot of the observed vs. predicted responses for each covariate grey-coded by region (AUPRC/Prevalence).
scatters <- ggplot(
subset(
responses[order(responses$`AUC-PR` / responses$prevalence), ],
Var %in% c("mlq", "twi") & Region %in% plot_regions
)
) +
xlim(-3, 3) +
ylim(-3, 3) +
geom_hline(yintercept = 0) +
geom_vline(xintercept = 0) +
geom_abline(slope = 1, intercept = 0, lty = 2) +
aes(Prediction, Estimate) +
geom_point(aes(color = `AUC-PR` / prevalence), size = 2) +
scale_color_gradient(
name = "AUPRC /\nPrevalence", low = "white", high = "black",
trans = "log2"
) +
facet_grid(
Region ~ Var,
labeller = labeller(
Var = as_labeller(
c(
mlq = "Moisture Lowest Quarter",
twi = "Topographic Wetness"
)
)
)
) +
xlab("Trait-SDM prediction") +
ylab("Taxon regression estimate") +
theme_bw() +
theme(
legend.position = c(0.11, .94),
legend.key.width = unit(.07, "inches"),
legend.key.height = unit(.09, "inches"),
legend.background = element_rect(fill = "transparent"),
legend.title = element_text(size = 8),
legend.text = element_text(size = 6),
strip.background = element_blank(),
strip.text.y = element_blank()
)
hist_data <- subset(perf, ibra_subregion %in% plot_regions & !random_effect)
med_data <- with(
hist_data, tapply(`AUC-PR` / prevalence, ibra_subregion, median)
)
med_data <- as.data.frame(med_data)
med_data <- na.omit(med_data)
med_data$ibra_subregion <- row.names(med_data)
med_data$ibra_subregion <- fct_relevel(med_data$ibra_subregion, plot_regions)
hists <- ggplot(hist_data) +
aes(`AUC-PR` / prevalence) +
geom_histogram() +
scale_x_continuous(trans = "log2") +
facet_grid(ibra_subregion ~ random_effect) +
geom_vline(aes(xintercept = med_data), med_data, col = "grey", size = 2) +
xlab("AUPRC / Prevalence") +
ylab("No. of taxa") +
theme_bw() +
theme(
strip.background = element_blank(),
strip.text.x = element_text(color = "transparent")
)
grid.arrange(scatters, hists, ncol = 2, widths = c(.45, .55))

Plot of the observed vs. predicted responses for each covariate grey-coded by region (AUROC).
scatters_roc <- ggplot(
subset(
responses[order(responses$AUC), ],
Var %in% c("mlq", "twi") & Region %in% plot_regions
)
) +
xlim(-3, 3) +
ylim(-3, 3) +
geom_hline(yintercept = 0) +
geom_vline(xintercept = 0) +
geom_abline(slope = 1, intercept = 0, lty = 2) +
aes(Prediction, Estimate) +
geom_point(aes(color = AUC), size = 2) +
scale_color_gradient(
name = "AUROC", low = "white", high = "black"
) +
facet_grid(
Region ~ Var,
labeller = labeller(
Var = as_labeller(
c(
mlq = "Moisture Lowest Quarter",
twi = "Topographic Wetness"
)
)
)
) +
xlab("Trait-SDM prediction") +
ylab("Taxon regression estimate") +
theme_bw() +
theme(
legend.position = c(0.09, .95),
legend.key.width = unit(.07, "inches"),
legend.key.height = unit(.09, "inches"),
legend.background = element_rect(fill = "transparent"),
legend.title = element_text(size = 8),
legend.text = element_text(size = 6),
strip.background = element_blank(),
strip.text.y = element_blank()
)
hist_data_roc <- subset(perf, ibra_subregion %in% plot_regions & !random_effect)
med_data_roc <- with(
hist_data_roc, tapply(AUC, ibra_subregion, median)
)
med_data_roc <- as.data.frame(med_data_roc)
med_data_roc <- na.omit(med_data_roc)
med_data_roc$ibra_subregion <- row.names(med_data_roc)
med_data_roc$ibra_subregion <- fct_relevel(med_data_roc$ibra_subregion, plot_regions)
hists_roc <- ggplot(hist_data_roc) +
aes(AUC) +
geom_histogram() +
facet_grid(ibra_subregion ~ random_effect) +
geom_vline(aes(xintercept = med_data_roc), med_data_roc, col = "grey", size = 2) +
xlab("AUROC") +
ylab("No. of taxa") +
theme_bw() +
theme(
strip.background = element_blank(),
strip.text.x = element_text(color = "transparent")
)
grid.arrange(scatters_roc, hists_roc, ncol = 2, widths = c(.45, .55))

Plot of the observed vs. predicted responses to TWI grey-coded by trait values.
ggplot(
merge(
subset(
responses,
Var == "twi" & Region %in% plot_regions[c(1, 3, 5)]
),
gather(
unique(
rbind(
mdg[c("taxon", "ibra_subregion", "sla", "sm")],
mds[c("taxon", "ibra_subregion", "sla", "sm")]
)
),
"trait",
"value",
sla, sm
),
by.x = c("Taxon", "Region"), by.y = c("taxon", "ibra_subregion")
)
) +
xlim(-3, 3) +
ylim(-3, 3) +
geom_hline(yintercept = 0) +
geom_vline(xintercept = 0) +
geom_abline(slope = 1, intercept = 0, lty = 2) +
aes(Prediction, Estimate) +
geom_point(aes(color = value), size = 2) +
scale_color_gradient(
name = "SD", low = "white", high = "black"
) +
facet_grid(
Region ~ trait,
labeller = labeller(
trait = as_labeller(
c(
sla = "Specific Leaf Area",
sm = "Seed Mass"
)
)
)
) +
theme_bw() +
theme(
legend.position = c(0.07, .91),
legend.key.width = unit(.07, "inches"),
legend.key.height = unit(.09, "inches"),
legend.background = element_rect(fill = "transparent"),
legend.title = element_text(size = 8),
legend.text = element_text(size = 6),
strip.background = element_blank()
)

Plot of the observed vs. predicted responses for each covariate
ggplot(responses) +
aes(
Prediction, Estimate,
fill = Region == "Greater Grampians",
alpha = Region == "Greater Grampians"
) +
geom_abline(slope = 1, intercept = 0, col = "grey") +
geom_point(col = "black", pch = 21, size = 2) +
scale_fill_grey(start = 0, end = 1) +
scale_alpha_discrete(range = c(0.3, 1)) +
facet_wrap(
~Var, 2,
labeller = labeller(
Var = as_labeller(
c(
mlq = "Moisture Lowest Quarter",
twi = "Topographic Wetness",
r1k = "Relief 1000m Radius",
tn = "Total Nitrogen"
)
)
)
) +
xlab("Trait-SDM prediction") +
ylab("Taxon regression estimate") +
lims(x = c(-4, 4), y = c(-4, 4)) +
geom_text(
data = cbind(
summarise(group_by(responses, Var), r = cor(Prediction, Estimate)),
Region = ""
),
mapping = aes(
x = -Inf, y = Inf,
label = sprintf("italic('r') == %s", round(r, digits = 2))
),
hjust = -0.1,
vjust = 1.5,
parse = TRUE
) +
theme_bw() +
theme(
legend.position = "none",
strip.background = element_blank()
)

Plot of the miscalibration for each covariate vs. performance
ggplot(responses) +
aes(
Prediction - Estimate, `AUC-PR`/prevalence,
fill = Region == "Greater Grampians",
alpha = Region == "Greater Grampians"
) +
geom_point(col = "black", pch = 21, size = 2) +
scale_y_log10() +
scale_fill_grey(start = 0, end = 1) +
scale_alpha_discrete(range = c(0.3, 1)) +
facet_wrap(
~Var, 2,
labeller = labeller(
Var = as_labeller(
c(
mlq = "Moisture Lowest Quarter",
twi = "Topographic Wetness",
r1k = "Relief 1000m Radius",
tn = "Total Nitrogen"
)
)
)
) +
xlim(-5, 5) +
ylab("AUPRC / Prevalence") +
xlab("Miscalibration (Prediction - Estimate)") +
theme_bw() +
theme(
legend.position = "none",
strip.background = element_blank()
)

# Calculate the correlation between predicted vs observed response coefficients
# for each covariate among all taxa within a region.
# calccor
#correlations <- lapply(
# vars,
# function(var) {
# ans <- sapply(
# rgn_names,
# function(region) {
# ss <- subset(responses, Region == region & Var == var)
# cor(ss$Estimate, ss$Prediction)
# }
# )
# data.frame(
# var = var, region = names(ans), cor = ans, stringsAsFactors = FALSE,
# row.names = NULL
# )
# }
#)
#correlations <- do.call(rbind, correlations)
#correlations <- data.frame(
# correlations,
# KLD = kldists[correlations$region],
# GD = dists[correlations$region],
# stringsAsFactors = FALSE
#)
#correlations <- merge(correlations, kldists_univariate)
# Plot the correlations vs. the KL distance of the regions environment from the
# Grampians.
# plotcors1, fig.width=11
#ggplot(correlations) +
#aes(KLD, cor) +
#geom_point() +
#geom_smooth(method = "lm") +
#xlim(0, 35) +
#ylim(-1, 1) +
#facet_wrap("var")
# Plot the correlations vs. the univariate KL distance of the regions
# environment from the Grampians.
# plotcors2, fig.width=11
#ggplot(correlations) +
#aes(kldist_var, cor) +
#geom_point() +
#geom_smooth(method = "lm") +
#ylim(-1, 1) +
#facet_wrap("var", scales = "free_x")
# Plot the correlations vs. the geographic (centroid) distance of the region
# to the Grampians.
# plotcors3, fig.width=11
#ggplot(correlations) +
#aes(GD, cor) +
#geom_point() +
#geom_smooth(method = "lm") +
#xlim(0, 1000) +
#ylim(-1, 1) +
#facet_wrap("var")
IycgLS0tCiMnIHRpdGxlOiAiVmFsaWRhdGluZyB0aGUgc3BlY2llcyByZXNwb25zZXMgZm9yIGdyYW1waWFucyB0cmFpdC1lbnZpcm9ubWVudAojJyAgIG1vZGVsIHdpdGggbGluZWFyIHJlbGF0aW9uc2hpcHMiCiMnIGF1dGhvcjogIldpbGxpYW0gSy4gTW9ycmlzIgojJyBkYXRlOiAiYHIgU3lzLkRhdGUoKWAiCiMnIG91dHB1dDoKIycgICBybWFya2Rvd246Omh0bWxfbm90ZWJvb2s6CiMnICAgICBjb2RlX2ZvbGRpbmc6IGhpZGUKIycgLS0tCgojKyBzZXR1cCwgbWVzc2FnZT1GQUxTRSwgd2FybmluZyA9IEZBTFNFLCBmaWcua2VlcD0ibm9uZSIsIGZpZy5zaG93PSJoaWRlIgprbml0cjo6b3B0c19jaHVuayRzZXQoY2FjaGUgPSBUUlVFLCBkZXYgPSBjKCJwbmciLCAicGRmIikpCmxpYnJhcnkoaGVyZSkKc291cmNlKGhlcmUoInNyYyIsICJtb2RlbF9saW5lYXJfZ3JhbXBpYW5zX2ludGVybmFsX3ZhbGlkYXRpb24uUiIpKQpsaWJyYXJ5KGFybSkKbGlicmFyeShmb3JjYXRzKQpsaWJyYXJ5KGdncGxvdDIpCmxpYnJhcnkoZ3JpZEV4dHJhKQpsaWJyYXJ5KHRpZHlyKQoKdmFycyA8LSBjKCJtbHEiLCAidHdpIiwgInIxayIsICJ0biIpCnRheGEgPC0gdW5pcXVlKG1kcyR0YXhvbikKdGF4YV94X3JlZ2lvbiA8LSB1bmlxdWUobWRzWywgYygidGF4b24iLCAiaWJyYV9zdWJyZWdpb24iKV0pCnRyYWl0cyA8LSBzYXBwbHkoCiAgdGF4YSwKICBmdW5jdGlvbih4KSB1bmxpc3QodW5pcXVlKG1kc1ttZHMkdGF4b24gPT0geCwgYygic2xhIiwgInNtIiwgIm1oIildKSkKKQoKIycgQ2FsY3VsYXRlIHRoZSByZXNwb25zZXMgb2YgZWFjaCBzcGVjaWVzIGluIGVhY2ggcmVnaW9uIHVzaW5nCiMnIHNpbmdsZS1zcGVjaWVzL3NpbmdsZS1yZWdpb24gZ2xtcy4KIysgb2JzcmVzcCwgd2FybmluZyA9IEZBTFNFCm9ic2VydmVkX3Jlc3BvbnNlcyA8LSBtYXBwbHkoCiAgZnVuY3Rpb24odGF4b25pLCByZWdpb24pIHsKICAgIGRmIDwtIHN1YnNldChtZHMsIHRheG9uID09IHRheG9uaSAmIGlicmFfc3VicmVnaW9uID09IHJlZ2lvbikKICAgIG1vZGVsIDwtIGJheWVzZ2xtKAogICAgICBmb3JtdWxhKHNwcmludGYoInkgfiAlcyIsIHBhc3RlKHZhcnMsIGNvbGxhcHNlID0gIiArICIpKSksCiAgICAgIGJpbm9taWFsLCBkZiwgcHJpb3Iuc2NhbGUgPSAxLCBwcmlvci5kZiA9IEluZgogICAgKQogICAgZGF0YS5mcmFtZSgKICAgICAgVGF4b24gPSB0YXhvbmksIFJlZ2lvbiA9IHJlZ2lvbiwgVmFyID0gdmFycywKICAgICAgc3VtbWFyeShtb2RlbCkkY29lZmZpY2llbnRzW3ZhcnMsIDE6Ml0KICAgICkKICB9LAogIHRheGFfeF9yZWdpb25bLCAxXSwgdGF4YV94X3JlZ2lvblssIDJdLAogIFNJTVBMSUZZID0gRkFMU0UKKQoKb2JzZXJ2ZWRfcmVzcG9uc2VzIDwtIGRvLmNhbGwocmJpbmQsIG9ic2VydmVkX3Jlc3BvbnNlcykKCiMnIENhbGN1bGF0ZSB0aGUgcHJlZGljdGVkIHJlc3BvbnNlcyBvZiBlYWNoIHNwZWNpZXMgaW4gZWFjaCByZWdpb24gYmFzZWQgb24gdGhlCiMnIEdyYW1waWFucyBtb2RlbCBhbmQgdGhlaXIgdHJhaXQgdmFsdWVzLgojKyBwcmVkcmVzcCwgd2FybmluZyA9IEZBTFNFLCBtZXNzYWdlID0gRkFMU0UKbnNpbXMgPC0gMTAwMApzaW1zIDwtIGZpeGVmKHNpbShtb2RlbF9ncmFtcGlhbnMsIG5zaW1zKSkKCnByZWRpY3RlZF9yZXNwb25zZXMgPC0gZnVuY3Rpb24odGF4b24pIHsKICBhbnMgPC0gdmFwcGx5KAogICAgdmFycywgZnVuY3Rpb24oeCkgewogICAgICByb3dTdW1zKAogICAgICAgIHN3ZWVwKHNpbXNbLCBncmVwKHgsIGNvbG5hbWVzKHNpbXMpKV0sIDIsIGMoMSwgdHJhaXRzWywgdGF4b25dKSwgIioiKQogICAgICApCiAgICB9LAogICAgbnVtZXJpYyhuc2ltcykKICApCiAgdCgKICAgIGFwcGx5KAogICAgICBhbnMsIDIsIGZ1bmN0aW9uKHgpIGMoUHJlZGljdGlvbiA9IG1lYW4oeCksICJQcmVkaWN0ZWQuLkVycm9yIiA9IHNkKHgpKQogICAgKQogICkKfQoKcHJlZGljdGVkX3Jlc3BvbnNlcyA8LSBkby5jYWxsKAogIHJiaW5kLCBzYXBwbHkodGF4YSwgcHJlZGljdGVkX3Jlc3BvbnNlcywgc2ltcGxpZnkgPSBGQUxTRSlbdGF4YV94X3JlZ2lvblssIDFdXQopCgpyZXNwb25zZXMgPC0gY2JpbmQob2JzZXJ2ZWRfcmVzcG9uc2VzLCBwcmVkaWN0ZWRfcmVzcG9uc2VzKQoKcmVzcG9uc2VzIDwtIG1lcmdlKAogIHJlc3BvbnNlcywgc3Vic2V0KHBlcmYsICFyYW5kb21fZWZmZWN0KSwKICBieS54ID0gYygiVGF4b24iLCAiUmVnaW9uIiksIGJ5LnkgPSBjKCJ0YXhvbiIsICJpYnJhX3N1YnJlZ2lvbiIpCikKCnBsb3RfcmVnaW9ucyA8LSBjKAogICJTbm93eSBNb3VudGFpbnMiLCAiSmVydmlzIiwgIlZpY3RvcmlhbiBBbHBzIiwgIlN0cnplbGVja2kgUmFuZ2VzIiwKICAiR3JlYXRlciBHcmFtcGlhbnMiCikKCnJlc3BvbnNlcyA8LSByYmluZChyZXNwb25zZXMsIGdyYW1waWFuc19yZXNwb25zZXMpCgpyZXNwb25zZXMkUmVnaW9uIDwtIGZvcmNhdHM6OmZjdF9yZWxldmVsKHJlc3BvbnNlcyRSZWdpb24sIHBsb3RfcmVnaW9ucykKCnRheG9uX3JlZ2lvbl9wcmV2YWxlbmNlIDwtIHN1bW1hcmlzZSgKICBncm91cF9ieShyYmluZChtZHMsIG1kZ1tuYW1lcyhtZHMpXSksIHRheG9uLCBpYnJhX3N1YnJlZ2lvbiksCiAgcHJldmFsZW5jZSA9IG1lYW4oeSkKKQoKcmVzcG9uc2VzIDwtIGxlZnRfam9pbigKICByZXNwb25zZXMsIHRheG9uX3JlZ2lvbl9wcmV2YWxlbmNlLAogIGJ5ID0gYygiVGF4b24iID0gInRheG9uIiwgICJSZWdpb24iID0gImlicmFfc3VicmVnaW9uIikKKQoKcGVyZiRpYnJhX3N1YnJlZ2lvbiA8LSBmb3JjYXRzOjpmY3RfcmVsZXZlbChwZXJmJGlicmFfc3VicmVnaW9uLCBwbG90X3JlZ2lvbnMpCgpwZXJmIDwtIGxlZnRfam9pbihwZXJmLCB0YXhvbl9yZWdpb25fcHJldmFsZW5jZSkKCiMnIFBsb3Qgb2YgdGhlIG9ic2VydmVkIHZzLiBwcmVkaWN0ZWQgcmVzcG9uc2VzIGZvciBlYWNoIGNvdmFyaWF0ZSBncmV5LWNvZGVkCiMnIGJ5IHJlZ2lvbiAoQVVQUkMvUHJldmFsZW5jZSkuCiMrIHBsb3RyZXNwb25zZSwgZmlnLndpZHRoID0gNy41LCBmaWcuaGVpZ2h0ID0gNy4xLCB3YXJuaW5nID0gRkFMU0UsIG1lc3NhZ2UgPSBGQUxTRQpzY2F0dGVycyA8LSBnZ3Bsb3QoCiAgICBzdWJzZXQoCiAgICAgIHJlc3BvbnNlc1tvcmRlcihyZXNwb25zZXMkYEFVQy1QUmAgLyByZXNwb25zZXMkcHJldmFsZW5jZSksIF0sCiAgICAgIFZhciAlaW4lIGMoIm1scSIsICJ0d2kiKSAmIFJlZ2lvbiAlaW4lIHBsb3RfcmVnaW9ucwogICAgKQogICkgKwogIHhsaW0oLTMsIDMpICsKICB5bGltKC0zLCAzKSArCiAgZ2VvbV9obGluZSh5aW50ZXJjZXB0ID0gMCkgKwogIGdlb21fdmxpbmUoeGludGVyY2VwdCA9IDApICsKICBnZW9tX2FibGluZShzbG9wZSA9IDEsIGludGVyY2VwdCA9IDAsIGx0eSA9IDIpICsKICBhZXMoUHJlZGljdGlvbiwgRXN0aW1hdGUpICsKICBnZW9tX3BvaW50KGFlcyhjb2xvciA9IGBBVUMtUFJgIC8gcHJldmFsZW5jZSksIHNpemUgPSAyKSArCiAgc2NhbGVfY29sb3JfZ3JhZGllbnQoCiAgICBuYW1lID0gIkFVUFJDIC9cblByZXZhbGVuY2UiLCBsb3cgPSAid2hpdGUiLCBoaWdoID0gImJsYWNrIiwKICAgIHRyYW5zID0gImxvZzIiCiAgKSArCiAgZmFjZXRfZ3JpZCgKICAgIFJlZ2lvbiB+IFZhciwKICAgIGxhYmVsbGVyID0gbGFiZWxsZXIoCiAgICAgIFZhciA9IGFzX2xhYmVsbGVyKAogICAgICAgIGMoCiAgICAgICAgICBtbHEgID0gIk1vaXN0dXJlIExvd2VzdCBRdWFydGVyIiwKICAgICAgICAgIHR3aSAgPSAiVG9wb2dyYXBoaWMgV2V0bmVzcyIKICAgICAgICApCiAgICAgICkKICAgICkKICApICsKICB4bGFiKCJUcmFpdC1TRE0gcHJlZGljdGlvbiIpICsKICB5bGFiKCJUYXhvbiByZWdyZXNzaW9uIGVzdGltYXRlIikgKwogIHRoZW1lX2J3KCkgKwogIHRoZW1lKAogICAgbGVnZW5kLnBvc2l0aW9uID0gYygwLjExLCAuOTQpLAogICAgbGVnZW5kLmtleS53aWR0aCA9IHVuaXQoLjA3LCAiaW5jaGVzIiksCiAgICBsZWdlbmQua2V5LmhlaWdodCA9IHVuaXQoLjA5LCAiaW5jaGVzIiksCiAgICBsZWdlbmQuYmFja2dyb3VuZCA9IGVsZW1lbnRfcmVjdChmaWxsID0gInRyYW5zcGFyZW50IiksCiAgICBsZWdlbmQudGl0bGUgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDgpLAogICAgbGVnZW5kLnRleHQgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDYpLAogICAgc3RyaXAuYmFja2dyb3VuZCA9IGVsZW1lbnRfYmxhbmsoKSwKICAgIHN0cmlwLnRleHQueSA9IGVsZW1lbnRfYmxhbmsoKQogICkKCmhpc3RfZGF0YSA8LSBzdWJzZXQocGVyZiwgaWJyYV9zdWJyZWdpb24gJWluJSBwbG90X3JlZ2lvbnMgJiAhcmFuZG9tX2VmZmVjdCkKbWVkX2RhdGEgPC0gd2l0aCgKICBoaXN0X2RhdGEsIHRhcHBseShgQVVDLVBSYCAvIHByZXZhbGVuY2UsIGlicmFfc3VicmVnaW9uLCBtZWRpYW4pCikKbWVkX2RhdGEgPC0gYXMuZGF0YS5mcmFtZShtZWRfZGF0YSkKbWVkX2RhdGEgPC0gbmEub21pdChtZWRfZGF0YSkKbWVkX2RhdGEkaWJyYV9zdWJyZWdpb24gPC0gcm93Lm5hbWVzKG1lZF9kYXRhKQptZWRfZGF0YSRpYnJhX3N1YnJlZ2lvbiA8LSBmY3RfcmVsZXZlbChtZWRfZGF0YSRpYnJhX3N1YnJlZ2lvbiwgcGxvdF9yZWdpb25zKQoKaGlzdHMgPC0gZ2dwbG90KGhpc3RfZGF0YSkgKwogIGFlcyhgQVVDLVBSYCAvIHByZXZhbGVuY2UpICsKICBnZW9tX2hpc3RvZ3JhbSgpICsKICBzY2FsZV94X2NvbnRpbnVvdXModHJhbnMgPSAibG9nMiIpICsKICBmYWNldF9ncmlkKGlicmFfc3VicmVnaW9uIH4gcmFuZG9tX2VmZmVjdCkgKwogIGdlb21fdmxpbmUoYWVzKHhpbnRlcmNlcHQgPSBtZWRfZGF0YSksIG1lZF9kYXRhLCBjb2wgPSAiZ3JleSIsIHNpemUgPSAyKSArCiAgeGxhYigiQVVQUkMgLyBQcmV2YWxlbmNlIikgKwogIHlsYWIoIk5vLiBvZiB0YXhhIikgKwogIHRoZW1lX2J3KCkgKwogIHRoZW1lKAogICAgc3RyaXAuYmFja2dyb3VuZCA9IGVsZW1lbnRfYmxhbmsoKSwKICAgIHN0cmlwLnRleHQueCA9IGVsZW1lbnRfdGV4dChjb2xvciA9ICJ0cmFuc3BhcmVudCIpCiAgKQoKZ3JpZC5hcnJhbmdlKHNjYXR0ZXJzLCBoaXN0cywgbmNvbCA9IDIsIHdpZHRocyA9IGMoLjQ1LCAuNTUpKQoKIycgUGxvdCBvZiB0aGUgb2JzZXJ2ZWQgdnMuIHByZWRpY3RlZCByZXNwb25zZXMgZm9yIGVhY2ggY292YXJpYXRlIGdyZXktY29kZWQKIycgYnkgcmVnaW9uIChBVVJPQykuCiMrIHBsb3RyZXNwb25zZWF1cm9jLCBmaWcud2lkdGggPSA3LjUsIGZpZy5oZWlnaHQgPSA3LjEsIHdhcm5pbmcgPSBGQUxTRSwgbWVzc2FnZSA9IEZBTFNFCnNjYXR0ZXJzX3JvYyA8LSBnZ3Bsb3QoCiAgICBzdWJzZXQoCiAgICAgIHJlc3BvbnNlc1tvcmRlcihyZXNwb25zZXMkQVVDKSwgXSwKICAgICAgVmFyICVpbiUgYygibWxxIiwgInR3aSIpICYgUmVnaW9uICVpbiUgcGxvdF9yZWdpb25zCiAgICApCiAgKSArCiAgeGxpbSgtMywgMykgKwogIHlsaW0oLTMsIDMpICsKICBnZW9tX2hsaW5lKHlpbnRlcmNlcHQgPSAwKSArCiAgZ2VvbV92bGluZSh4aW50ZXJjZXB0ID0gMCkgKwogIGdlb21fYWJsaW5lKHNsb3BlID0gMSwgaW50ZXJjZXB0ID0gMCwgbHR5ID0gMikgKwogIGFlcyhQcmVkaWN0aW9uLCBFc3RpbWF0ZSkgKwogIGdlb21fcG9pbnQoYWVzKGNvbG9yID0gQVVDKSwgc2l6ZSA9IDIpICsKICBzY2FsZV9jb2xvcl9ncmFkaWVudCgKICAgIG5hbWUgPSAiQVVST0MiLCBsb3cgPSAid2hpdGUiLCBoaWdoID0gImJsYWNrIgogICkgKwogIGZhY2V0X2dyaWQoCiAgICBSZWdpb24gfiBWYXIsCiAgICBsYWJlbGxlciA9IGxhYmVsbGVyKAogICAgICBWYXIgPSBhc19sYWJlbGxlcigKICAgICAgICBjKAogICAgICAgICAgbWxxICA9ICJNb2lzdHVyZSBMb3dlc3QgUXVhcnRlciIsCiAgICAgICAgICB0d2kgID0gIlRvcG9ncmFwaGljIFdldG5lc3MiCiAgICAgICAgKQogICAgICApCiAgICApCiAgKSArCiAgeGxhYigiVHJhaXQtU0RNIHByZWRpY3Rpb24iKSArCiAgeWxhYigiVGF4b24gcmVncmVzc2lvbiBlc3RpbWF0ZSIpICsKICB0aGVtZV9idygpICsKICB0aGVtZSgKICAgIGxlZ2VuZC5wb3NpdGlvbiA9IGMoMC4wOSwgLjk1KSwKICAgIGxlZ2VuZC5rZXkud2lkdGggPSB1bml0KC4wNywgImluY2hlcyIpLAogICAgbGVnZW5kLmtleS5oZWlnaHQgPSB1bml0KC4wOSwgImluY2hlcyIpLAogICAgbGVnZW5kLmJhY2tncm91bmQgPSBlbGVtZW50X3JlY3QoZmlsbCA9ICJ0cmFuc3BhcmVudCIpLAogICAgbGVnZW5kLnRpdGxlID0gZWxlbWVudF90ZXh0KHNpemUgPSA4KSwKICAgIGxlZ2VuZC50ZXh0ID0gZWxlbWVudF90ZXh0KHNpemUgPSA2KSwKICAgIHN0cmlwLmJhY2tncm91bmQgPSBlbGVtZW50X2JsYW5rKCksCiAgICBzdHJpcC50ZXh0LnkgPSBlbGVtZW50X2JsYW5rKCkKICApCgpoaXN0X2RhdGFfcm9jIDwtIHN1YnNldChwZXJmLCBpYnJhX3N1YnJlZ2lvbiAlaW4lIHBsb3RfcmVnaW9ucyAmICFyYW5kb21fZWZmZWN0KQptZWRfZGF0YV9yb2MgPC0gd2l0aCgKICBoaXN0X2RhdGFfcm9jLCB0YXBwbHkoQVVDLCBpYnJhX3N1YnJlZ2lvbiwgbWVkaWFuKQopCm1lZF9kYXRhX3JvYyA8LSBhcy5kYXRhLmZyYW1lKG1lZF9kYXRhX3JvYykKbWVkX2RhdGFfcm9jIDwtIG5hLm9taXQobWVkX2RhdGFfcm9jKQptZWRfZGF0YV9yb2MkaWJyYV9zdWJyZWdpb24gPC0gcm93Lm5hbWVzKG1lZF9kYXRhX3JvYykKbWVkX2RhdGFfcm9jJGlicmFfc3VicmVnaW9uIDwtIGZjdF9yZWxldmVsKG1lZF9kYXRhX3JvYyRpYnJhX3N1YnJlZ2lvbiwgcGxvdF9yZWdpb25zKQoKaGlzdHNfcm9jIDwtIGdncGxvdChoaXN0X2RhdGFfcm9jKSArCiAgYWVzKEFVQykgKwogIGdlb21faGlzdG9ncmFtKCkgKwogIGZhY2V0X2dyaWQoaWJyYV9zdWJyZWdpb24gfiByYW5kb21fZWZmZWN0KSArCiAgZ2VvbV92bGluZShhZXMoeGludGVyY2VwdCA9IG1lZF9kYXRhX3JvYyksIG1lZF9kYXRhX3JvYywgY29sID0gImdyZXkiLCBzaXplID0gMikgKwogIHhsYWIoIkFVUk9DIikgKwogIHlsYWIoIk5vLiBvZiB0YXhhIikgKwogIHRoZW1lX2J3KCkgKwogIHRoZW1lKAogICAgc3RyaXAuYmFja2dyb3VuZCA9IGVsZW1lbnRfYmxhbmsoKSwKICAgIHN0cmlwLnRleHQueCA9IGVsZW1lbnRfdGV4dChjb2xvciA9ICJ0cmFuc3BhcmVudCIpCiAgKQoKZ3JpZC5hcnJhbmdlKHNjYXR0ZXJzX3JvYywgaGlzdHNfcm9jLCBuY29sID0gMiwgd2lkdGhzID0gYyguNDUsIC41NSkpCgojJyBQbG90IG9mIHRoZSBvYnNlcnZlZCB2cy4gcHJlZGljdGVkIHJlc3BvbnNlcyB0byBUV0kgZ3JleS1jb2RlZAojJyBieSB0cmFpdCB2YWx1ZXMuCiMrIHBsb3RyZXNwb25zZXRyYWl0cywgZmlnLndpZHRoID0gMy41LCBmaWcuaGVpZ2h0ID0gNC41LCB3YXJuaW5nID0gRkFMU0UsIG1lc3NhZ2UgPSBGQUxTRQpnZ3Bsb3QoCiAgbWVyZ2UoCiAgICBzdWJzZXQoCiAgICAgIHJlc3BvbnNlcywKICAgICAgVmFyID09ICJ0d2kiICYgUmVnaW9uICVpbiUgcGxvdF9yZWdpb25zW2MoMSwgMywgNSldCiAgICApLAogICAgZ2F0aGVyKAogICAgICB1bmlxdWUoCiAgICAgICAgcmJpbmQoCiAgICAgICAgICBtZGdbYygidGF4b24iLCAiaWJyYV9zdWJyZWdpb24iLCAic2xhIiwgInNtIildLAogICAgICAgICAgbWRzW2MoInRheG9uIiwgImlicmFfc3VicmVnaW9uIiwgInNsYSIsICJzbSIpXQogICAgICAgICkKICAgICAgKSwKICAgICAgInRyYWl0IiwKICAgICAgInZhbHVlIiwKICAgICAgc2xhLCBzbQogICAgKSwKICAgIGJ5LnggPSBjKCJUYXhvbiIsICJSZWdpb24iKSwgYnkueSA9IGMoInRheG9uIiwgImlicmFfc3VicmVnaW9uIikKICApCikgKwp4bGltKC0zLCAzKSArCnlsaW0oLTMsIDMpICsKZ2VvbV9obGluZSh5aW50ZXJjZXB0ID0gMCkgKwpnZW9tX3ZsaW5lKHhpbnRlcmNlcHQgPSAwKSArCmdlb21fYWJsaW5lKHNsb3BlID0gMSwgaW50ZXJjZXB0ID0gMCwgbHR5ID0gMikgKwphZXMoUHJlZGljdGlvbiwgRXN0aW1hdGUpICsKZ2VvbV9wb2ludChhZXMoY29sb3IgPSB2YWx1ZSksIHNpemUgPSAyKSArCnNjYWxlX2NvbG9yX2dyYWRpZW50KAogIG5hbWUgPSAiU0QiLCBsb3cgPSAid2hpdGUiLCBoaWdoID0gImJsYWNrIgopICsKZmFjZXRfZ3JpZCgKICBSZWdpb24gfiB0cmFpdCwKICBsYWJlbGxlciA9IGxhYmVsbGVyKAogICAgdHJhaXQgPSBhc19sYWJlbGxlcigKICAgICAgYygKICAgICAgICBzbGEgPSAiU3BlY2lmaWMgTGVhZiBBcmVhIiwKICAgICAgICBzbSAgPSAiU2VlZCBNYXNzIgogICAgICApCiAgICApCiAgKQopICsKdGhlbWVfYncoKSArCnRoZW1lKAogIGxlZ2VuZC5wb3NpdGlvbiA9IGMoMC4wNywgLjkxKSwKICBsZWdlbmQua2V5LndpZHRoID0gdW5pdCguMDcsICJpbmNoZXMiKSwKICBsZWdlbmQua2V5LmhlaWdodCA9IHVuaXQoLjA5LCAiaW5jaGVzIiksCiAgbGVnZW5kLmJhY2tncm91bmQgPSBlbGVtZW50X3JlY3QoZmlsbCA9ICJ0cmFuc3BhcmVudCIpLAogIGxlZ2VuZC50aXRsZSA9IGVsZW1lbnRfdGV4dChzaXplID0gOCksCiAgbGVnZW5kLnRleHQgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDYpLAogIHN0cmlwLmJhY2tncm91bmQgPSBlbGVtZW50X2JsYW5rKCkKKQoKIycgUGxvdCBvZiB0aGUgb2JzZXJ2ZWQgdnMuIHByZWRpY3RlZCByZXNwb25zZXMgZm9yIGVhY2ggY292YXJpYXRlCiMrIHBsb3RyZXNwb25zZXNhbGxjb3ZzLCBmaWcud2lkdGggPSA1LjEsIGZpZy5oZWlnaHQgPSA0LjUsIHdhcm5pbmcgPSBGQUxTRSwgbWVzc2FnZSA9IEZBTFNFCmdncGxvdChyZXNwb25zZXMpICsKYWVzKAogIFByZWRpY3Rpb24sIEVzdGltYXRlLAogIGZpbGwgPSBSZWdpb24gPT0gIkdyZWF0ZXIgR3JhbXBpYW5zIiwKICBhbHBoYSA9ICBSZWdpb24gPT0gIkdyZWF0ZXIgR3JhbXBpYW5zIgopICsKZ2VvbV9hYmxpbmUoc2xvcGUgPSAxLCBpbnRlcmNlcHQgPSAwLCBjb2wgPSAiZ3JleSIpICsKZ2VvbV9wb2ludChjb2wgPSAiYmxhY2siLCBwY2ggPSAyMSwgc2l6ZSA9IDIpICsKc2NhbGVfZmlsbF9ncmV5KHN0YXJ0ID0gMCwgZW5kID0gMSkgKwpzY2FsZV9hbHBoYV9kaXNjcmV0ZShyYW5nZSA9IGMoMC4zLCAxKSkgKwpmYWNldF93cmFwKAogIH5WYXIsIDIsCiAgbGFiZWxsZXIgPSBsYWJlbGxlcigKICAgIFZhciA9IGFzX2xhYmVsbGVyKAogICAgICBjKAogICAgICAgIG1scSA9ICJNb2lzdHVyZSBMb3dlc3QgUXVhcnRlciIsCiAgICAgICAgdHdpID0gIlRvcG9ncmFwaGljIFdldG5lc3MiLAogICAgICAgIHIxayA9ICJSZWxpZWYgMTAwMG0gUmFkaXVzIiwKICAgICAgICB0biAgPSAiVG90YWwgTml0cm9nZW4iCiAgICAgICkKICAgICkKICApCikgKwp4bGFiKCJUcmFpdC1TRE0gcHJlZGljdGlvbiIpICsKeWxhYigiVGF4b24gcmVncmVzc2lvbiBlc3RpbWF0ZSIpICsKbGltcyh4ID0gYygtNCwgNCksIHkgPSBjKC00LCA0KSkgKwpnZW9tX3RleHQoCiAgZGF0YSAgICA9IGNiaW5kKAogICAgc3VtbWFyaXNlKGdyb3VwX2J5KHJlc3BvbnNlcywgVmFyKSwgciA9IGNvcihQcmVkaWN0aW9uLCBFc3RpbWF0ZSkpLAogICAgUmVnaW9uID0gIiIKICApLAogIG1hcHBpbmcgPSBhZXMoCiAgICB4ID0gLUluZiwgeSA9IEluZiwKICAgIGxhYmVsID0gc3ByaW50ZigiaXRhbGljKCdyJykgPT0gJXMiLCByb3VuZChyLCBkaWdpdHMgPSAyKSkKICApLAogIGhqdXN0ICAgPSAtMC4xLAogIHZqdXN0ICAgPSAxLjUsCiAgcGFyc2UgPSBUUlVFCikgKwp0aGVtZV9idygpICsKdGhlbWUoCiAgbGVnZW5kLnBvc2l0aW9uID0gIm5vbmUiLAogIHN0cmlwLmJhY2tncm91bmQgPSBlbGVtZW50X2JsYW5rKCkKKQoKIycgUGxvdCBvZiB0aGUgbWlzY2FsaWJyYXRpb24gZm9yIGVhY2ggY292YXJpYXRlIHZzLiBwZXJmb3JtYW5jZQojKyBwbG90bWlzY2FsLCBmaWcud2lkdGggPSA1LjEsIGZpZy5oZWlnaHQgPSA0LjUsIHdhcm5pbmcgPSBGQUxTRSwgbWVzc2FnZSA9IEZBTFNFCmdncGxvdChyZXNwb25zZXMpICsKYWVzKAogIFByZWRpY3Rpb24gLSBFc3RpbWF0ZSwgYEFVQy1QUmAvcHJldmFsZW5jZSwKICBmaWxsID0gUmVnaW9uID09ICJHcmVhdGVyIEdyYW1waWFucyIsCiAgYWxwaGEgPSAgUmVnaW9uID09ICJHcmVhdGVyIEdyYW1waWFucyIKKSArCmdlb21fcG9pbnQoY29sID0gImJsYWNrIiwgcGNoID0gMjEsIHNpemUgPSAyKSArCnNjYWxlX3lfbG9nMTAoKSArCnNjYWxlX2ZpbGxfZ3JleShzdGFydCA9IDAsIGVuZCA9IDEpICsKc2NhbGVfYWxwaGFfZGlzY3JldGUocmFuZ2UgPSBjKDAuMywgMSkpICsKZmFjZXRfd3JhcCgKICB+VmFyLCAyLAogIGxhYmVsbGVyID0gbGFiZWxsZXIoCiAgICBWYXIgPSBhc19sYWJlbGxlcigKICAgICAgYygKICAgICAgICBtbHEgPSAiTW9pc3R1cmUgTG93ZXN0IFF1YXJ0ZXIiLAogICAgICAgIHR3aSA9ICJUb3BvZ3JhcGhpYyBXZXRuZXNzIiwKICAgICAgICByMWsgPSAiUmVsaWVmIDEwMDBtIFJhZGl1cyIsCiAgICAgICAgdG4gID0gIlRvdGFsIE5pdHJvZ2VuIgogICAgICApCiAgICApCiAgKQopICsKeGxpbSgtNSwgNSkgKwp5bGFiKCJBVVBSQyAvIFByZXZhbGVuY2UiKSArCnhsYWIoIk1pc2NhbGlicmF0aW9uIChQcmVkaWN0aW9uIC0gRXN0aW1hdGUpIikgKwp0aGVtZV9idygpICsKdGhlbWUoCiAgbGVnZW5kLnBvc2l0aW9uID0gIm5vbmUiLAogIHN0cmlwLmJhY2tncm91bmQgPSBlbGVtZW50X2JsYW5rKCkKKQoKIyBDYWxjdWxhdGUgdGhlIGNvcnJlbGF0aW9uIGJldHdlZW4gcHJlZGljdGVkIHZzIG9ic2VydmVkIHJlc3BvbnNlIGNvZWZmaWNpZW50cwojIGZvciBlYWNoIGNvdmFyaWF0ZSBhbW9uZyBhbGwgdGF4YSB3aXRoaW4gYSByZWdpb24uCiMgY2FsY2NvcgojY29ycmVsYXRpb25zIDwtIGxhcHBseSgKIyAgdmFycywKIyAgZnVuY3Rpb24odmFyKSB7CiMgICAgYW5zIDwtIHNhcHBseSgKIyAgICAgIHJnbl9uYW1lcywKIyAgICAgIGZ1bmN0aW9uKHJlZ2lvbikgewojICAgICAgICBzcyA8LSBzdWJzZXQocmVzcG9uc2VzLCBSZWdpb24gPT0gcmVnaW9uICYgVmFyID09IHZhcikKIyAgICAgICAgY29yKHNzJEVzdGltYXRlLCBzcyRQcmVkaWN0aW9uKQojICAgICAgfQojICAgICkKIyAgICBkYXRhLmZyYW1lKAojICAgICAgdmFyID0gdmFyLCByZWdpb24gPSBuYW1lcyhhbnMpLCBjb3IgPSBhbnMsIHN0cmluZ3NBc0ZhY3RvcnMgPSBGQUxTRSwKIyAgICAgIHJvdy5uYW1lcyA9IE5VTEwKIyAgICApCiMgIH0KIykKCiNjb3JyZWxhdGlvbnMgPC0gZG8uY2FsbChyYmluZCwgY29ycmVsYXRpb25zKQoKI2NvcnJlbGF0aW9ucyA8LSBkYXRhLmZyYW1lKAojICBjb3JyZWxhdGlvbnMsCiMgIEtMRCA9IGtsZGlzdHNbY29ycmVsYXRpb25zJHJlZ2lvbl0sCiMgIEdEID0gZGlzdHNbY29ycmVsYXRpb25zJHJlZ2lvbl0sCiMgIHN0cmluZ3NBc0ZhY3RvcnMgPSBGQUxTRQojKQoKI2NvcnJlbGF0aW9ucyA8LSBtZXJnZShjb3JyZWxhdGlvbnMsIGtsZGlzdHNfdW5pdmFyaWF0ZSkKCiMgUGxvdCB0aGUgY29ycmVsYXRpb25zIHZzLiB0aGUgS0wgZGlzdGFuY2Ugb2YgdGhlIHJlZ2lvbnMgZW52aXJvbm1lbnQgZnJvbSB0aGUKIyBHcmFtcGlhbnMuCiMgcGxvdGNvcnMxLCBmaWcud2lkdGg9MTEKI2dncGxvdChjb3JyZWxhdGlvbnMpICsKI2FlcyhLTEQsIGNvcikgKwojZ2VvbV9wb2ludCgpICsKI2dlb21fc21vb3RoKG1ldGhvZCA9ICJsbSIpICsKI3hsaW0oMCwgMzUpICsKI3lsaW0oLTEsIDEpICsKI2ZhY2V0X3dyYXAoInZhciIpCgojIFBsb3QgdGhlIGNvcnJlbGF0aW9ucyB2cy4gdGhlIHVuaXZhcmlhdGUgS0wgZGlzdGFuY2Ugb2YgdGhlIHJlZ2lvbnMKIyBlbnZpcm9ubWVudCBmcm9tIHRoZSBHcmFtcGlhbnMuCiMgcGxvdGNvcnMyLCBmaWcud2lkdGg9MTEKI2dncGxvdChjb3JyZWxhdGlvbnMpICsKI2FlcyhrbGRpc3RfdmFyLCBjb3IpICsKI2dlb21fcG9pbnQoKSArCiNnZW9tX3Ntb290aChtZXRob2QgPSAibG0iKSArCiN5bGltKC0xLCAxKSArCiNmYWNldF93cmFwKCJ2YXIiLCBzY2FsZXMgPSAiZnJlZV94IikKCiMgUGxvdCB0aGUgY29ycmVsYXRpb25zIHZzLiB0aGUgZ2VvZ3JhcGhpYyAoY2VudHJvaWQpIGRpc3RhbmNlIG9mIHRoZSByZWdpb24KIyB0byB0aGUgR3JhbXBpYW5zLgojIHBsb3Rjb3JzMywgZmlnLndpZHRoPTExCiNnZ3Bsb3QoY29ycmVsYXRpb25zKSArCiNhZXMoR0QsIGNvcikgKwojZ2VvbV9wb2ludCgpICsKI2dlb21fc21vb3RoKG1ldGhvZCA9ICJsbSIpICsKI3hsaW0oMCwgMTAwMCkgKwojeWxpbSgtMSwgMSkgKwojZmFjZXRfd3JhcCgidmFyIikKCg==