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

## ----setup--------------------------------------------------------------------
library(rLifting)

if (!requireNamespace("ggplot2", quietly = TRUE)) {
  knitr::opts_chunk$set(eval = FALSE)
  message("'ggplot2' is required to render plots. Vignette code will not run.")
} else {
  library(ggplot2)
}

data("benchmark_rlifting", package = "rLifting")
set.seed(20260522)

## ----extensions-schematic, echo=FALSE, fig.height=4.5, fig.cap="Figure 1: The five boundary modes applied to a synthetic signal. The original 10 samples are shown in black; the virtual extensions (4 samples on each side) are shown in colour. symmetric mirrors the signal; periodic wraps; zero pads with zero; local_linear extrapolates a line fit through ll_k = 4 boundary samples; one_sided does not extend the signal — instead it renormalises the filter at boundary positions (shown here as 'no extension' for illustration)."----
x = c(1.0, 0.8, 0.6, 0.5, 0.7, 0.9, 1.1, 1.4, 1.5, 1.3)
n = length(x)
idx_orig = 1:n

# Build virtual extensions to display
make_ext = function(mode) {
  left = numeric(4); right = numeric(4)
  if (mode == "symmetric") {
    left = rev(x[1:4]); right = rev(x[(n-3):n])
  } else if (mode == "periodic") {
    left = x[(n-3):n]; right = x[1:4]
  } else if (mode == "zero") {
    left = rep(0, 4); right = rep(0, 4)
  } else if (mode == "local_linear") {
    bL = (sum((0:3) * x[1:4]) - mean(0:3) * sum(x[1:4])) /
         sum(((0:3) - mean(0:3))^2)
    aL = mean(x[1:4]) - bL * mean(0:3)
    left = aL + bL * (-4:-1)
    bR = (sum((0:3) * x[(n-3):n]) - mean(0:3) * sum(x[(n-3):n])) /
         sum(((0:3) - mean(0:3))^2)
    aR = mean(x[(n-3):n]) - bR * mean(0:3)
    right = aR + bR * (4:7)
  } else if (mode == "one_sided") {
    left = rep(NA, 4); right = rep(NA, 4)
  }
  data.frame(
    index = c((-3):0, idx_orig, (n + 1):(n + 4)),
    value = c(left, x, right),
    region = c(rep("ext", 4), rep("orig", n), rep("ext", 4))
  )
}

modes = c("symmetric", "periodic", "zero", "local_linear", "one_sided")
df_ext = do.call(rbind, lapply(modes, function(m) {
  d = make_ext(m); d$mode = m; d
}))
df_ext$mode = factor(df_ext$mode, levels = modes)

ggplot(df_ext, aes(x = index, y = value)) +
  geom_line(
    data = subset(df_ext, region == "orig"),
    colour = "black", linewidth = 0.8
  ) +
  geom_point(
    data = subset(df_ext, region == "orig"),
    colour = "black", size = 1.5
  ) +
  geom_line(
    data = subset(df_ext, region == "ext"),
    aes(colour = mode), linewidth = 0.8, na.rm = TRUE
  ) +
  geom_point(
    data = subset(df_ext, region == "ext"),
    aes(colour = mode), size = 1.5, na.rm = TRUE
  ) +
  geom_vline(
    xintercept = c(0.5, n + 0.5),
    linetype = "dashed", colour = "grey50"
  ) +
  facet_wrap(~ mode, ncol = 2) +
  theme_minimal() +
  theme(legend.position = "none") +
  labs(
    title = "Boundary extensions on a small synthetic signal",
    subtitle = "Black: original 10 samples. Coloured: virtual extension (4 on each side).",
    x = "Sample index", y = "Amplitude"
  )

## ----offline-boundary-spread--------------------------------------------------
sub_off = subset(
  benchmark_rlifting,
  Mode == "offline" & Wavelet == "cdf53" & ThresholdMethod == "universal" &
    !grepl("tuned", Method) & Shrinkage == "semisoft"
)
agg_off = aggregate(MSE_median ~ Signal + Boundary, data = sub_off, FUN = mean)
wide_off = reshape(
  agg_off, idvar = "Signal", timevar = "Boundary",
  direction = "wide"
)

names(wide_off) = sub("MSE_median\\.", "", names(wide_off))
modes_cols = c("symmetric", "periodic", "zero", "local_linear", "one_sided")
wide_off$ratio_max_min = round(
  apply(wide_off[, modes_cols], 1, max) /
    apply(wide_off[, modes_cols], 1, min), 3
)
wide_off[, c("Signal", modes_cols, "ratio_max_min")]

## ----causal-boundary-haar-----------------------------------------------------
sub_haar = subset(
  benchmark_rlifting,
  Mode == "causal" & Wavelet == "haar" & ThresholdMethod == "universal" &
    !grepl("tuned", Method) & Shrinkage == "semisoft"
)
agg_haar = aggregate(
  MSE_settled_median ~ Signal + Boundary, 
  data = sub_haar, FUN = mean
)

wide_haar = reshape(
  agg_haar, idvar = "Signal", 
  timevar = "Boundary", direction = "wide"
)

names(wide_haar) = sub("MSE_settled_median\\.", "", names(wide_haar))
wide_haar$ratio_max_min = round(
  apply(wide_haar[, modes_cols], 1, max) /
    apply(wide_haar[, modes_cols], 1, min), 3
)
wide_haar[, c("Signal", modes_cols, "ratio_max_min")]

## ----causal-boundary-haar-plot, echo=FALSE, fig.height=4, fig.cap="Figure 2: Causal-mode MSE by boundary across the four DJ signals (haar wavelet, universal threshold, semisoft shrinkage, ll_k = 4). With a single-tap predict filter, one_sided and zero both refuse to invent data at the right boundary and dominate every signal; symmetric pays a 10–50% penalty by mirroring the most recent sample inward."----
df_long_haar = do.call(
  rbind, 
  lapply(
    modes_cols, 
    function(b) {
      data.frame(
        Signal = wide_haar$Signal, Boundary = b,
        MSE = wide_haar[[b]]
      )
    }
  )
)

df_long_haar$Boundary = factor(df_long_haar$Boundary, levels = modes_cols)

ggplot(df_long_haar, aes(x = Boundary, y = MSE, fill = Boundary)) +
  geom_col() +
  facet_wrap(~ Signal, scales = "free_y", ncol = 4) +
  scale_fill_brewer(palette = "Set2") +
  theme_minimal() +
  theme(
    legend.position = "none",
    axis.text.x = element_text(angle = 45, hjust = 1)
  ) +
  labs(
    title = "Causal-mode MSE by boundary (haar, universal/semisoft)",
    x = NULL, y = "MSE (settled)"
  )

## ----causal-boundary-cdf53----------------------------------------------------
sub_cdf = subset(
  benchmark_rlifting,
  Mode == "causal" & Wavelet == "cdf53" & ThresholdMethod == "universal" &
    !grepl("tuned", Method) & Shrinkage == "semisoft"
)
agg_cdf = aggregate(
  MSE_settled_median ~ Signal + Boundary, 
  data = sub_cdf, FUN = mean
)

wide_cdf = reshape(
  agg_cdf, idvar = "Signal", 
  timevar = "Boundary", direction = "wide"
)

names(wide_cdf) = sub("MSE_settled_median\\.", "", names(wide_cdf))
wide_cdf$best = modes_cols[apply(wide_cdf[, modes_cols], 1, which.min)]
wide_cdf[, c("Signal", modes_cols, "best")]

