# approach B: smooth residuals and then shift upward by subtracting the minimum so it’s nonnegative

ARCHmXAdditive_backfitting_lowes <- function(target_sum_nonpar, X, bandwidth = NULL,
                              max_iter = 50, tol = 1e-4, floor_eps = 1e-8) {
  # target_sum_nonpar: vector of length n with the "total" nonparametric component
  #                    i.e., sigma2_resid = eps^2 - arch_component   (can be < 0)
  # We fit m_ell >= 0 via m_ell(x) = g_ell(x) - min(g_ell(x)).
  
  n <- nrow(X) # number of samples
  d <- ncol(X) # number of covariates
  
  # Initialize m values strictly positive (tiny), and storage for functions
  m_values <- matrix(floor_eps, nrow = n, ncol = d)
  m_funcs  <- vector("list", d)
  
  # bandwidths
  if (is.null(bandwidth)) {
    bandwidth <- rep(NA_real_, d)
    for (ell in 1:d) {
      x_range <- diff(range(X[, ell]))
      bandwidth[ell] <- 0.5 * x_range * n^(-1/5)
    }
  }
  
  for (iter in 1:max_iter) {
    m_old <- m_values
    
    for (ell in 1:d) {
      # Partial residual for component ell
      partial_resid <- target_sum_nonpar - rowSums(m_values[, -ell, drop = FALSE])
      
      fit <- lowess(X[, ell], partial_resid, f=.3)
      fit$y = fit$y - min(fit$y) # shift curve to ensure nonnegative
      
      # Predict on observed X and exponentiate back
      ghat_at_x  <- approx(fit$x, fit$y, xout = X[, ell], rule = 2)$y
      m_values[, ell] <- ghat_at_x
      
      # Store the function on a grid
      m_funcs[[ell]] <- (function(xgrid, ygrid) {
        # returns m_ell(x) = exp(g_ell(x))
        f_lin <- approxfun(xgrid, ygrid, rule = 2)
        function(z) f_lin(z)
      })(fit$x, fit$y)
    }
    
    if (max(abs(m_values - m_old)) < tol) break
  }
  
  list(m_values = m_values, m_funcs = m_funcs, bandwidth = bandwidth)
}