## ----include = FALSE----------------------------------------------------------
knitr::opts_chunk$set(collapse = TRUE, comment = "#>")
has_penguins <- requireNamespace("palmerpenguins", quietly = TRUE)

## ----penguin-data, eval = has_penguins----------------------------------------
penguins <- subset(
  palmerpenguins::penguins,
  complete.cases(species, sex, year, flipper_length_mm, body_mass_g) &
    flipper_length_mm > 0 & body_mass_g > 0
)
penguins$year_c <- penguins$year - mean(penguins$year)
nrow(penguins)

## ----penguin-raw-scale, eval = has_penguins, fig.width = 6, fig.height = 4, fig.cap = "Raw log-scale flipper length and body mass for analysed complete rows. This descriptive relationship is not the fitted residual association."----
plot(
  log(body_mass_g) ~ log(flipper_length_mm), data = penguins,
  pch = 16, col = grDevices::adjustcolor("#087f8c", alpha.f = 0.45),
  xlab = "log flipper length (mm)", ylab = "log body mass (g)"
)
abline(stats::lm(log(body_mass_g) ~ log(flipper_length_mm), data = penguins),
  col = "#14344a", lwd = 2
)

## ----penguin-direct-lognormal, eval = has_penguins, warning = FALSE, message = FALSE----
fit_log <- drmTMB::drmTMB(
  drmTMB::bf(mu1 = flipper_length_mm ~ species + sex + year_c,
             mu2 = body_mass_g ~ species + sex + year_c,
             sigma1 = ~ 1, sigma2 = ~ 1, rho12 = ~ 1),
  family = drmTMB::biv_lognormal(), data = penguins
)

drmTMB::rho12(fit_log)[1]
drmTMB::check_drm(fit_log)

# All three target the log-residual rho12, not a raw-scale correlation.
ci_wald <- confint(fit_log, parm = "rho12", method = "wald")
ci_profile <- confint(
  fit_log, parm = "rho12", method = "profile", profile_engine = "endpoint"
)
ci_bootstrap <- confint(
  fit_log, parm = "rho12", method = "bootstrap", R = 99, seed = 20260724
)
interval_row <- function(x, label) {
  data.frame(
    label = label,
    estimate = unname(drmTMB::rho12(fit_log)[1]),
    lower = x$lower,
    upper = x$upper,
    status = x$conf.status,
    note = x$profile.message,
    stringsAsFactors = FALSE
  )
}
intervals <- rbind(
  interval_row(ci_wald, "Wald"),
  interval_row(ci_profile, "Profile"),
  interval_row(
    ci_bootstrap,
    sprintf(
      "Bootstrap (%d/%d retained)",
      ci_bootstrap$bootstrap.n,
      ci_bootstrap$bootstrap.n + ci_bootstrap$bootstrap.failed
    )
  )
)
bootstrap_total <- ci_bootstrap$bootstrap.n + ci_bootstrap$bootstrap.failed
cat(sprintf(
  "Bootstrap diagnostic: %d/%d full refits retained. Failed refits remain a diagnostic, not hidden precision.\n",
  ci_bootstrap$bootstrap.n, bootstrap_total
))

## ----penguin-intervals, eval = has_penguins, echo = FALSE, fig.width = 7, fig.height = 3.8, fig.cap = "Reported 95% confidence intervals for the fitted direct log-residual association in the penguin model. Each eye spans the reported interval and is centred on its estimate; the taper is a visual interval cue, not a likelihood, sampling density, or posterior distribution. The retained bootstrap count is a diagnostic, not hidden precision.", fig.alt = "Three 95 percent confidence intervals for the direct log-residual correlation rho12. Wald, profile, and bootstrap estimates are all about 0.35. The bootstrap interval is annotated with its retained full-refit count."----
at <- rev(seq_len(nrow(intervals)))
old_par <- par(no.readonly = TRUE)
par(mar = c(4.2, 7.4, 0.8, 2.6), bg = "white")
x_limits <- range(c(0, intervals$lower, intervals$upper)) + c(-0.08, 0.18)
plot(
  NA, xlim = x_limits, ylim = c(0.5, 3.5), yaxt = "n", bty = "n",
  xlab = expression(rho[12]), ylab = ""
)
abline(v = 0, lty = 2, lwd = 1, col = "grey65")

draw_interval_eye <- function(lower, estimate, upper, y) {
  x <- seq(lower, upper, length.out = 101)
  left_width <- max(estimate - lower, .Machine$double.eps)
  right_width <- max(upper - estimate, .Machine$double.eps)
  taper <- ifelse(
    x <= estimate,
    (x - lower) / left_width,
    (upper - x) / right_width
  )
  half_height <- 0.18 * sqrt(pmax(taper, 0))
  polygon(
    c(x, rev(x)), c(y + half_height, rev(y - half_height)),
    col = grDevices::adjustcolor("#087f8c", alpha.f = 0.28), border = NA
  )
  points(
    estimate, y,
    pch = 21, cex = 1.50, lwd = 1.80,
    col = "#087f8c", bg = "white"
  )
}

for (i in seq_len(nrow(intervals))) {
  draw_interval_eye(intervals$lower[i], intervals$estimate[i], intervals$upper[i], at[i])
}
axis(2, at = at, labels = c("Wald", "Profile", "Bootstrap"), las = 1, tick = FALSE)
text(
  intervals$upper[3] + 0.025, at[3],
  labels = sprintf("%d/%d retained", ci_bootstrap$bootstrap.n, bootstrap_total),
  adj = c(0, 0.5), cex = 0.82, col = "#4b5563"
)
par(old_par)

## ----student-example, eval = FALSE--------------------------------------------
# fit_t <- drmTMB(
#   bf(mu1 = activity ~ habitat, mu2 = boldness ~ habitat,
#      sigma1 = ~ 1, sigma2 = ~ 1, nu = ~ 1, rho12 = ~ 1),
#   family = biv_student(), data = behaviour
# )
# 
# rho12(fit_t)

## ----staged-example, eval = FALSE---------------------------------------------
# paired_data <- na.omit(data.frame(
#   bred, offspring, habitat, habitat_score, season
# ))
# 
# assoc <- biv_associate(
#   bf(mu = bred ~ habitat),
#   bf(mu = offspring ~ habitat, sigma = ~ season),
#   family = list(binomial(), nbinom2()),
#   data = paired_data,
#   association = ~ 1
# )
# 
# association(assoc)
# vcov(assoc)
# confint(assoc)
# confint(assoc, type = "eta")

