## ----include = FALSE----------------------------------------------------------
knitr::opts_chunk$set(
  collapse = TRUE,
  comment = "#>",
  fig.width = 6,
  fig.height = 4,
  dpi = 96
)

## ----setup--------------------------------------------------------------------
library(freqTLS)

## ----profile-shrimp-----------------------------------------------------------
data(shrimp_lethal)
shrimp_std <- standardize_data(
  shrimp_lethal,
  temp = "Temperature_assay", duration = "Duration_exposure_hours",
  n_total = "N_individuals_after_trial", mortality = "Mortality_after_trial",
  duration_unit = "hours"
)
shrimp_fit <- fit_4pl(shrimp_std, t_ref = 1, family = "beta_binomial", quiet = TRUE)
tls(shrimp_fit, method = "profile")$summary

## ----profile-zebrafish--------------------------------------------------------
data(zebrafish_lethal)
zebra_std <- standardize_data(
  zebrafish_lethal,
  temp = "assay_temp", duration = "duration_h",
  n_total = "n_total", n_surv = "n_surv", duration_unit = "hours"
)
zebra_fit <- suppressWarnings(fit_4pl(zebra_std, by = "life_stage",
                                      t_ref = 1, family = "beta_binomial", quiet = TRUE))
# per-stage CTmax and z with profile intervals
tls(zebra_fit, method = "profile")$summary

## ----three-way, echo = FALSE--------------------------------------------------
cache_path <- system.file("extdata", "bayesTLS_benchmark_cache.rds", package = "freqTLS")
if (!nzchar(cache_path) || !file.exists(cache_path)) {
  stop("The shipped bayesTLS benchmark cache is missing; reinstall freqTLS from a complete source tarball.")
}
cache <- readRDS(cache_path)

fmt <- function(est, lo, hi) {
  ifelse(is.na(est), "--", sprintf("%.2f [%.2f, %.2f]", est, lo, hi))
}

pro <- confint(shrimp_fit, c("CTmax", "z"), method = "profile")  # confint() delegates to the engine fit

pick <- function(df, parm, cols) {
  r <- df[df$dataset == "shrimp" & df$parameter == parm, ]
  stats::setNames(as.numeric(r[cols]), c("est", "lo", "hi"))
}
ts_ct <- pick(cache$two_stage, "CTmax", c("estimate", "lower", "upper"))
ts_z  <- pick(cache$two_stage, "z",     c("estimate", "lower", "upper"))
by_ct <- pick(cache$bayesian,  "CTmax", c("median",   "lower", "upper"))
by_z  <- pick(cache$bayesian,  "z",     c("median",   "lower", "upper"))
pr_ct <- pro[pro$parameter == "CTmax", ]
pr_z  <- pro[pro$parameter == "z", ]

knitr::kable(data.frame(
  Quantity = c("CTmax (°C)", "z (°C / decade)"),
  `Two-stage (delta CI)`    = c(fmt(ts_ct["est"], ts_ct["lo"], ts_ct["hi"]),
                                fmt(ts_z["est"],  ts_z["lo"],  ts_z["hi"])),
  `bayesTLS (95% CrI)`      = c(fmt(by_ct["est"], by_ct["lo"], by_ct["hi"]),
                                fmt(by_z["est"],  by_z["lo"],  by_z["hi"])),
  `freqTLS (profile CI)` = c(fmt(pr_ct$estimate, pr_ct$conf.low, pr_ct$conf.high),
                                fmt(pr_z$estimate,  pr_z$conf.low,  pr_z$conf.high)),
  check.names = FALSE
), caption = "Shrimp CTmax and z shown side by side: the two model fits use the relative midpoint; the classical estimator uses absolute LT50.")

## ----benchmark-provenance, echo = FALSE---------------------------------------
provenance <- data.frame(
  Field = c("bayesTLS version", "bayesTLS source commit", "CmdStan version",
            "Cache build date", "Seed", "Model threshold", "Shape design"),
  Value = c(
    cache$meta$bayesTLS_version,
    cache$meta$git_sha,
    cache$meta$cmdstan_version,
    cache$meta$date_built,
    cache$meta$seed,
    cache$meta$config$target_surv,
    cache$meta$config$temp_effects
  )
)
knitr::kable(provenance, caption = "Version-stamped benchmark-cache provenance.")

## ----perf-load, echo = FALSE--------------------------------------------------
perf_path <- system.file("extdata", "performance_results.rds", package = "freqTLS")
has_perf  <- nzchar(perf_path) && file.exists(perf_path)
if (has_perf) perf <- readRDS(perf_path)

## ----perf-speed, echo = FALSE-------------------------------------------------
if (has_perf) {
  knitr::kable(
    perf$speed[, c("family", "design", "n_obs", "median_fit_ms", "median_profile_ms")],
    caption = "Median wall-clock (ms): one fit, and one CTmax profile CI."
  )
}

## ----speed-head-to-head, echo = FALSE-----------------------------------------
data("shrimp_lethal", package = "freqTLS", envir = environment())
fmt_t <- function(s) if (is.na(s)) "--" else if (s < 1) sprintf("%.0f ms", s * 1000) else sprintf("%.1f s", s)

sp_std <- standardize_data(shrimp_lethal, temp = "Temperature_assay",
                           duration = "Duration_exposure_hours",
                           n_total = "N_individuals_after_trial",
                           mortality = "Mortality_after_trial", duration_unit = "hours")
t_fit <- system.time(
  sp_fit <- fit_4pl(sp_std, t_ref = 1, family = "beta_binomial", quiet = TRUE)
)[["elapsed"]]
t_wald <- system.time(invisible(confint(sp_fit, c("CTmax", "z"), method = "wald")))[["elapsed"]]
t_prof <- system.time(invisible(confint(sp_fit, c("CTmax", "z"), method = "profile")))[["elapsed"]]

speed <- data.frame(
  Estimator = c("freqTLS", "freqTLS", "freqTLS", "classical two-stage", "bayesTLS"),
  Task = c("fit (ML)", "fit + Wald CTmax & z", "fit + profile CTmax & z",
           "fit + delta CI", "fit (4 chains x 4000 MCMC)"),
  `Wall-clock` = c(fmt_t(t_fit), fmt_t(t_fit + t_wald), fmt_t(t_fit + t_prof), "--", "--"),
  Source = c("live", "live", "live", "cached", "cached"),
  check.names = FALSE, stringsAsFactors = FALSE
)
tpath <- system.file("extdata", "timing_results.rds", package = "freqTLS")
if (nzchar(tpath) && file.exists(tpath)) {
  tm <- readRDS(tpath)$timing
  speed$`Wall-clock`[speed$Estimator == "classical two-stage"] <- fmt_t(tm$seconds[tm$method == "two_stage"])
  speed$`Wall-clock`[speed$Estimator == "bayesTLS"] <- fmt_t(tm$seconds[tm$method == "bayesTLS"])
}
knitr::kable(speed, caption = "Wall-clock on the shrimp benchmark. freqTLS is timed live as this page renders; the two-stage and bayesTLS times are cached from the repository-only maintainer script data-raw/timing-study.R (not installed with the package). The bayesTLS time is post-compile sampling plus overhead, and a first fit also pays a one-time Stan compilation.")

## ----perf-acc, echo = FALSE---------------------------------------------------
if (has_perf) {
  knitr::kable(
    perf$accuracy[, c("family", "truth_setting", "parameter", "bias", "rmse", "n_converged")],
    caption = sprintf("Near-unbiased recovery (nsim = %d).", perf$meta$nsim_accuracy)
  )
}

## ----perf-cov, echo = FALSE---------------------------------------------------
if (has_perf) {
  knitr::kable(
    perf$coverage[, c("family", "method", "parameter", "coverage", "median_width", "nominal")],
    caption = sprintf("Profile and Wald 95%% interval coverage (nsim = %d).",
                      perf$meta$nsim_coverage)
  )
}

## ----bb-phi, echo = FALSE-----------------------------------------------------
bbp_path <- system.file("extdata", "beta_binomial_phi_results.rds", package = "freqTLS")
if (nzchar(bbp_path) && file.exists(bbp_path)) {
  bbp <- readRDS(bbp_path)
  z <- bbp$coverage[bbp$coverage$parameter == "z", ]
  phis <- sort(unique(z$phi_true))
  lab <- c("5" = "strong", "50" = "mild", "200" = "very mild")[as.character(phis)]
  pick <- function(m) z$coverage[match(paste(phis, m), paste(z$phi_true, z$method))]
  tab <- data.frame(
    `phi (overdispersion)` = sprintf("%g (%s)", phis, ifelse(is.na(lab), "", lab)),
    profile = pick("profile"), Wald = pick("wald"), bootstrap = pick("bootstrap"),
    check.names = FALSE)
  knitr::kable(tab, digits = 3, caption = sprintf(
    paste("Empirical 95%% coverage of z by method as overdispersion weakens (phi",
          "up = nearer binomial, phi weakly identified; %d sims/cell). The profile",
          "collapses; Wald holds; bootstrap is intermediate."),
    bbp$meta$nsim))
}

## ----bootstrap-demo, eval = FALSE---------------------------------------------
# # A deliberately sparse design where the CTmax profile does not close.
# sparse <- simulate_tls(family = "binomial", temps = c(35, 36), times = c(1, 2),
#                        reps = 2, n = 10, CTmax = 36, z = 4, seed = 9)
# sfit_sparse <- suppressWarnings(fit_tls(sparse, y = survived, n = total,
#                                         time = duration, temp = temp,
#                                         family = "binomial", tref = 1))
# # Strict profile: NA on an open side. Default: a parametric bootstrap interval.
# strict <- suppressWarnings(
#   confint(sfit_sparse, "CTmax", method = "profile", fallback = FALSE))
# boot <- suppressWarnings(
#   confint(sfit_sparse, "CTmax", method = "profile", nboot = 1000, boot_seed = 1))
# data.frame(
#   setting   = c("strict profile (fallback = FALSE)", "default (bootstrap fallback)"),
#   conf.low  = c(strict$conf.low,  boot$conf.low),
#   conf.high = c(strict$conf.high, boot$conf.high),
#   method    = c(strict$method,    boot$method)
# )

## ----eye, fig.alt = "Confidence Eye for the shrimp CTmax and z: pale confidence lenses with hollow point estimates, the freqTLS uncertainty display."----
plot_confidence_eye(shrimp_fit, parm = c("CTmax", "z"), method = "profile")

## ----v02-stage-shape----------------------------------------------------------
stage_shape <- suppressWarnings(fit_4pl(
  zebra_std, by = "life_stage",
  low = ~ life_stage, up = ~ life_stage, k = ~ life_stage,
  t_ref = 1, family = "beta_binomial", quiet = TRUE
))
# zebra_fit (above) is the shared-shape fit; stage_shape lets low / up / k vary.
c(shared_shape_AIC = round(AIC(zebra_fit), 1),
  stage_shape_AIC  = round(AIC(stage_shape), 1))

## ----v02-up-------------------------------------------------------------------
stage_est <- stage_shape$fit$estimates
stage_est[grepl("^up:", stage_est$parameter), c("parameter", "estimate", "std.error")]

## ----v02-derive---------------------------------------------------------------
c(
  CTmax_50pct_1h = round(derive_ctmax(shrimp_fit$fit, surv = 0.5, duration = 1), 2),
  surv_32C_4h    = round(tail(predict_heat_injury(shrimp_fit$fit,
                     data.frame(time = seq(0, 4, by = 0.1), temp = 32))$survival, 1), 3)
)

