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==