predict.ARCHmXAdditive <- function(object, newdata, ...){
  # object is the fitted value returned by ARCHmXAdditive
  # newdata is the future covariate matrix (row = future time steps)
  
  # Validation
  if (is.null(object$covariates)) {
    stop("predict requires an ARCHmXAdditive object with a data component.")
  } else if (object$p == 0) {
    stop("predict requires p > 0.")
  } else if (is.null(object$m_funcs)) {
    stop("predict requires an ARCHmXAdditive object with m_funcs.")
  }
  
  # strip newdata to only the columns used in object
  newdata <- matrix(newdata[, object$covariates], nrow = nrow(newdata))
  
  # retrieve the necessary attributes from the model object
  eps <- object$eps # training residuals
  p <- object$p # order of ARCH
  alphas <- matrix(object$alphas, ncol = 1) # fitted ARCH coefficients
  T_ <- object$T_
  train <- matrix(object$X[, object$covariates], nrow = nrow(object$X))[(p+1):T_, , drop = FALSE]
  delta <- object$delta
  m_funcs <- object$m_funcs
  
  newdata <- rbind(matrix(train[nrow(train), ], nrow = 1), matrix(newdata[(1:(nrow(newdata)-1)), ], ncol = ncol(newdata)))
  
  m_hats <- rep(0, nrow(newdata))
  for (ell in 1:length(m_funcs)) {
    m_hats <- m_hats + m_funcs[[ell]](newdata[, ell])
  }
  # Ensure nonnegative
  m_hats <- pmax(m_hats, 0)
  
  # Expand eps^delta and generate the new predicted values
  eps_delta <- (abs(eps))^delta
  eps_delta <- c(eps_delta, rep(0, nrow(newdata)))
  
  # prediction
  for (i in 1:length(m_hats)) {
    eps_delta[T_ + i] <- eps_delta[(T_ + i - 1):(T_ + i - p)] %*% alphas + m_hats[i]
  }
  
  # return the new predicted volatilities
  return(eps_delta[(T_+1):(T_+length(m_hats))]^(2/delta))
}

