sim.ARCHmX <- function(n, alpha, m, X, delta=2, shock.distr = "Normal", valinit=200) {
  # Validation
  # Note: We don't explicitly check whether m can be applied to X since
  #       we handle this in a tryCatch block later on in the function.
  
  if (!(shock.distr %in% c("Normal", "t5", "t7", "Laplace"))){
    stop("shock.distr must be Normal, t5, t7, or Laplace")
  } 
  if (length(alpha) == 0) {
    stop("alpha must contain at least a single parameter")
  }
  if (any(alpha < 0)) {
    stop("alpha must be nonnegative")
  }
  if (delta <= 0) {
    stop("delta must be a positive number.")
  }  
  if (!(is.matrix(X)) || (length(X) == 0)) {
    stop("X is not a valid data matrix.")
  }
  if (!is.function(m)){
    stop("m must be a function that accepts a matrix and returns a numeric vector.")
  }
  if(dim(X)[1] != (n +  valinit)){
    stop("Number of rows of X should be n + valinit")
  } 
  
  p <- length(alpha)
  epsilons <- rep(0, n + valinit)
  
  # Step 1: Initialization
  # ========================
  # We need an initial set of epsilons to actually 
  # build the iterative time series. 
  
  # for (i in 1:p) {
  #   epsilons[i] <- 0
  # }
  
  # Step 2: Burn-in period 
  # ========================
  # Here we first need to simulate the data matrix for 
  # our burn-in epsilons (we will actually create zero 
  # rows for the initial p-1 epsilons as well to keep  
  # the indices consistent later on)
  
  # covariate_means <- colMeans(X)
  # covariate_sds <- apply(X, 2, sd)
  # simulated_data <- matrix(0, nrow=p + burn_in_period, ncol=n_covariates)
  # for (i in p:(p + burn_in_period)) {
  #   for (j in 1:n_covariates) {
  #     simulated_data[i,j] <- rnorm(1, covariate_means[j], covariate_sds[j])
  #   }
  # }
  # 
  # # Then we will augment X with this simulated data
  # 
  # augmented_X <- rbind(simulated_data, X)
  
  # Next we compute the m function values:

  # tryCatch(
  #   {
  #     m_values <- m(X)
  #   }, error = function(msg){
  #     return(NA)
  #   })
  
  # assign m_values even on error so next validation block can cleanly exit if needed
  m_values <- tryCatch(
    m(X),
    error = function(e) NA
  )
  
  if (!is.numeric(m_values) || length(m_values) != n + valinit || any(!is.finite(m_values))) {
    stop("m(X) must return a finite numeric vector of length n + valinit.")
  }

  for (t in (p + 1):(n + valinit)) {
    sigma_to_deltath <- sum(alpha[1:p] * (epsilons^delta)[(t-1):(t-p)]) + m_values[t-1]
    if (!is.finite(sigma_to_deltath) || sigma_to_deltath <= 0) { 
      stop("non-positive or non-finite sigma^delta encountered during burn-in. check m and alpha.") 
    } # assumption in paper
    epsilons[t] <- ARCHmX_simulate_white_noise(shock.distr) * sigma_to_deltath^(1/delta)
  }
  
  # Step 3: Simulation
  # ========================  
  # Here we simulate the values to be returned
  
  # for (t in (p + burn_in_period + 1):(p + burn_in_period + n_samples)) {
  #   sigma_to_deltath <- sum(alpha[1:p] * (epsilons^delta)[(t-1):(t-p)]) + m_values[t-1]
  #   if (!is.finite(sigma_to_deltath) || sigma_to_deltath <= 0) { 
  #     stop("non-positive or non-finite sigma^delta encountered during burn-in. check m and alpha.") 
  #   } # assumption in paper
  #   epsilons[t] <- ARCHmX_simulate_white_noise(shock.distr) * sigma_to_deltath^(1/delta)
  # }  
  
  # And return them as a vector
  # return(epsilons[(p + burn_in_period + 1):(p + burn_in_period + n_samples)])
  i <- valinit + 1
  j <- valinit + n
  
  eps <- epsilons[i:j]
  X <- X[i:j, ]
  
  return(list(eps = eps, X = X))
}
