## ----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("doppler_example", package = "rLifting")
n = nrow(doppler_example)

## ----plot-input, echo=FALSE, fig.cap="Figure 1: Doppler signal. The dashed black line shows the underlying signal; the grey line shows the same signal with additive Gaussian noise (sd = 0.5)."----
ggplot(doppler_example, aes(x = index)) +
  geom_line(aes(y = noisy), color = "grey60", alpha = 0.8, linewidth = 0.3) +
  geom_line(
    aes(y = original),
    color = "black",
    linewidth = 0.6,
    linetype = "dashed"
  ) +
  theme_minimal() +
  labs(
    title = "Doppler signal: original (dashed) and noisy input",
    x = "Sample index", y = "Amplitude"
  )

## ----wavelets, eval=FALSE-----------------------------------------------------
# lifting_scheme("haar") # Fast; 1 vanishing moment; step-like signals
# lifting_scheme("cdf53") # Linear prediction; 2 VM; JPEG 2000 lossless
# lifting_scheme("dd4") # Deslauriers-Dubuc (1989) 4-point cubic; 4 VM
# lifting_scheme("cdf97") # JPEG 2000 lossy; 4 VM; smoothest reconstruction on smooth signals
# lifting_scheme("db2") # Orthogonal; 2 vanishing moments
# lifting_scheme("lazy") # Identity split only; for diagnostic use

## ----offline------------------------------------------------------------------
scheme = lifting_scheme("cdf53")
# Pick a level depth that suits both the full signal (offline) and the
# sliding window (causal/stream); keeps the comparison in Section 8 fair.
levels = 4

offline_clean = denoise_signal_offline(
  doppler_example$noisy,
  scheme,
  levels = levels,
  shrinkage = "semisoft", # hard | soft | semisoft (default) | scad
  threshold_method = "universal", # universal | sure
  extension = "symmetric", # symmetric | periodic | zero | local_linear | one_sided
  alpha = 0.3, # threshold decay across levels (universal only)
  beta = 1.2 # threshold scale factor (universal only)
)

## ----causal-------------------------------------------------------------------
window_size = 255 # must be odd; forced odd automatically if even is supplied
causal_levels = levels # same depth as offline for a fair comparison (Section 8)

causal_clean = denoise_signal_causal(
  doppler_example$noisy,
  scheme,
  window_size = window_size,
  levels = causal_levels,
  shrinkage = "semisoft",
  update_freq = 1  # recompute threshold every sample (maximum adaptivity)
)

## ----stream-------------------------------------------------------------------
processor = new_wavelet_stream(
  scheme,
  window_size = window_size,
  levels = causal_levels,
  shrinkage = "semisoft",
  update_freq = 1
)

stream_clean = numeric(n)
for (i in seq_len(n)) {
  stream_clean[i] = processor(doppler_example$noisy[i])
}

## ----irregular, eval=FALSE----------------------------------------------------
# set.seed(42)
# n_irr = 512
# pure_irr = rLifting:::.generate_signal("doppler", n = n_irr)
# x_irr = pure_irr + rnorm(n_irr, sd = 0.2)
# 
# # Non-uniform grid: log-normal spacing
# t_irr = cumsum(c(0, abs(rnorm(n_irr - 1, mean = 1, sd = 0.4))))
# 
# # Interpolating wavelets adapt predict steps to physical positions
# sch_irr = lifting_scheme("cdf53")
# 
# res_irr = denoise_signal_offline(
#   x_irr, sch_irr,
#   levels = 4, t = t_irr
# )

## ----stream-irregular, eval=FALSE---------------------------------------------
# proc_irr = new_wavelet_stream(
#   sch_irr, window_size = 127,
#   levels = 3, irregular = TRUE
# )
# 
# output_irr = numeric(n_irr)
# for (i in seq_len(n_irr)) {
#   output_irr[i] = proc_irr(x_irr[i], t_val = t_irr[i])
# }

## ----comparison, fig.cap="Figure 2: Output of the three modes on the noisy Doppler signal, zoomed to samples 400–800 (post warm-up). All modes use CDF 5/3, semisoft shrinkage and 4 decomposition levels. The grey line is the noisy input."----
df_plot = data.frame(
  index = rep(doppler_example$index, 5),
  value = c(
    doppler_example$noisy,
    doppler_example$original,
    offline_clean,
    causal_clean,
    stream_clean
  ),
  Signal = factor(
    rep(c("Noisy input", "Original", "Offline", "Causal", "Stream"), each = n),
    levels = c("Noisy input", "Original", "Offline", "Causal", "Stream")
  )
)

ggplot(
  df_plot,
  aes(
    x = index, y = value, colour = Signal,
    linewidth = Signal, alpha = Signal
  )
) +
  geom_line() +
  scale_colour_manual(
    values = c(
      "Noisy input" = "grey70",
      "Original" = "black",
      "Offline" = "#0072B2",
      "Causal" = "#D55E00",
      "Stream" = "#009E73"
    )
  ) +
  scale_linewidth_manual(
    values = c(
      "Noisy input" = 0.3, "Original" = 0.5,
      "Offline" = 0.8, "Causal" = 0.8, "Stream" = 0.8
    )
  ) +
  scale_alpha_manual(
    values = c(
      "Noisy input" = 0.4, "Original" = 1,
      "Offline" = 1, "Causal" = 1, "Stream" = 1
    )
  ) +
  coord_cartesian(xlim = c(400, 800)) +
  theme_minimal() +
  labs(
    title = "Three modes: offline, causal, stream (CDF 5/3, semisoft, 4 levels)",
    subtitle = "Zoom: samples 400–800 (post warm-up). Grey: noisy input.",
    x = "Sample index", y = "Amplitude"
  )

