new_ARCHmX <- function(eps, X, p, delta=2, covariates="all", hypothesisTest=TRUE, bandwidth = "plugin", k.choice = "plugin") {
  

  #################################################
  # A: Fit the model
  #################################################
  
  # 10/27: Rewritten to use professor's matrix routines from simulation5covariatesNormal
  
  if ((!identical(covariates,"all")) || (is.null(X))) {
    if ((((covariates[1] == 0) || is.null(covariates))) || (is.null(X))) {
      covariates = NULL
      #return(ARCH_internal(eps=eps, p=p, delta=delta))
      return(GARCHX(eps=eps, X = NULL, order = c(p,0), delta=delta))
    } else {
      X <- as.matrix(X[,covariates])
    }
  } else { #covariates = "all" so change to a vector
    covariates <- 1:ncol(X)
  }
  
  get_W <- function(X, optimal_h) {
    Kernel_matrix  = ARCHmX_kernel_matrix_iso(X,optimal_h)
    row_normalize <- function(K) {
      K / pmax(rowSums(K), .Machine$double.eps)
    }
    W <- row_normalize(Kernel_matrix)
    W = W[(p):(N-1),(p):(N-1)]
    return(W)
  }
  
  get_epsilon_matrix <- function(epsilon_delta) {
    Epsilon_matrix = matrix(0,N-p,p)
    for (j in 1:p) {
      Epsilon_matrix[,j] = epsilon_delta[(p-j+1):(N-j)] 
    }
    return(Epsilon_matrix)
  }
  
  get_X_tilde <- function(W, Epsilon_matrix) {
    X_tilde = matrix(0,N-p,p)
    X_tilde = Epsilon_matrix - W%*%Epsilon_matrix
    return(X_tilde)
  }
  
  N <- length(eps)
  epsilon_delta <- eps^delta
  if (bandwidth == "plugin") {
    s_x <- sd(X[,1], na.rm=TRUE)
    optimal_h <- s_x * N^(-1/5)
  } else{
    h_grid = seq(0.01, (range(X[,1])/4)[2], length.out = 10)
    #optimal_h <- ARCHmX_nw_cv_bandwidth(matrix(X[p:(N-1),], nrow=N-p), epsilon_delta[(p+1):N], h_grid = h_grid, nfolds = 10)$best_h
    optimal_h <- ARCHmX_nw_cv_bandwidth(matrix(X, nrow=nrow(X)), epsilon_delta, h_grid = h_grid, nfolds = 10)$best_h
  }
  Epsilon_matrix <- get_epsilon_matrix(epsilon_delta)
  W <- get_W(X, optimal_h)
  Y_tilde = epsilon_delta[(p+1):N] - as.vector(W %*% epsilon_delta[(p+1):N])  
  X_tilde <- get_X_tilde(W, Epsilon_matrix) 
  estim_alpha = nnls(X_tilde, Y_tilde)$x
  unexplained_by_ARCH  <- epsilon_delta[(p+1):N] - as.vector(Epsilon_matrix %*% estim_alpha)   # {p+1} through T, length T-p
  m_hat <- as.vector(W %*% unexplained_by_ARCH)                                                 # M(X_{p}) through M(X_{T-1}), length T-p
  # originally v_hat was computed here as unexplained_by_ARCH - m_hat per paperEJS but
  # this has been modified (here and below in the hypothesis test section) to match professor's code
  
  #################################################
  # B: Get Performance Metrics 
  #################################################
  
  get_sigma_tilde_2 <- function(estim_alpha, epsilon_delta, m_hat) {
    m_hat = c(rep(0,p),m_hat)
    sigma_tilde_d <- rep(0, N)
    sigma_tilde_d.init <- mean(epsilon_delta[1:10])    # r hardcoded to 10 based on line 665 simulation5covariatesNormal.r
    sigma_tilde_d[1:p]<-sigma_tilde_d.init 
    for (t in (p+1):N) 
    {
      # original code just omits epsilons prior to p+1, m_hat index aligned with sigma_tilde (seems 
      # right based off the documentation but Professor believes there is an indexing issue here)
      #  sigma_tilde_d[i] <- sum(estim_alpha * epsilon_delta[(p+i-1):i]) + max(0,m_hat[i])
      sigma_tilde_d[t] <- estim_alpha%*%(epsilon_delta[(t-1):(t-p)]) + max(0,m_hat[t-1])
    }    
    return(sigma_tilde_d^(2/delta))
  }
  
  sigma_tilde_2 <- get_sigma_tilde_2(estim_alpha, epsilon_delta, m_hat)
  sigma_tilde_2[which(sigma_tilde_2 == 0)] = mean(sigma_tilde_2)
  #v_hat <- (eps^2)[(p+1):N] - sigma_tilde_2
  v_hat <- (eps^2) - sigma_tilde_2
  #v_hat[which(is.nan(v_hat))] = 0
  v_hat[!is.finite(v_hat) | is.na(v_hat)] = 0 
  residual_deviation <- sqrt(mean(v_hat^2))
  
  #log_QL <- ( log(2 * pi) * -(N-p)/2 ) - sum(log(sigma_tilde_2) / 2) - sum( (eps^2)[(p+1):N] / sigma_tilde_2 ) / 2
  log_QL <- ( log(2 * pi) * -(N-p)/2 ) - sum(log(sigma_tilde_2) / 2) - sum( (eps^2) / sigma_tilde_2 ) / 2
  k <- sum(diag(W))
  
  df <- k + p
  AIC <- 2 * df - 2 * log_QL
  BIC <- df * log(N-p) - 2 * log_QL
  
  #################################################
  # C: Hypothesis testing for variable significance  
  #################################################
  
  alpha_p.values <- rep(NA_real_, p) # TBD!!
  covariate_p.values <- NULL
  p.values <- NULL
  
  if (hypothesisTest) {
    # Step B1: Get the v_hats
    
    # get the v_hats in a matrix, each row corresponds to the residuals for a given covariate
    # the ij'th entry corresponds to (p+j-1)th entry of the i'th covariate. In other words, 
    # v_hats_{ij} goes with X_{p+j-1, i}
    
    n_vars <- ncol(X)
    v_hats <- c()
    for (i in 1:n_vars) 
    {
      X_prime <- matrix(X[, -i], nrow=nrow(X))
      if (n_vars > 1) #(length(X_prime) > 0)    ## It means that there is only one covariate and when removing there are no covariates left
      {
        if (bandwidth == "plugin") {
          s_x_prime <- sd(X_prime[,1], na.rm=TRUE)
          optimal_h_prime <- s_x_prime * N^(-1/5)
        } else{
          h_grid_prime = seq(0.01, (range(X_prime[,1])/4)[2], length.out = 10)
          #optimal_h_prime <- ARCHmX_nw_cv_bandwidth(matrix(X_prime[p:(N-1),], nrow=N-p), epsilon_delta[(p+1):N], h_grid = h_grid_prime, nfolds = 10)$best_h
          optimal_h_prime <- ARCHmX_nw_cv_bandwidth(matrix(X_prime, nrow=nrow(X_prime)), epsilon_delta, h_grid = h_grid_prime, nfolds = 10)$best_h
        }
        
        W_prime <- get_W(X_prime, optimal_h_prime)
        Y_tilde_prime = epsilon_delta[(p+1):N] - as.vector(W_prime %*% epsilon_delta[(p+1):N])  
        X_tilde_prime <- get_X_tilde(W_prime, Epsilon_matrix)
        estim_alpha_prime = nnls(X_tilde_prime, Y_tilde_prime)$x
        unexplained_by_ARCH_prime <- epsilon_delta[(p+1):N] - as.vector(Epsilon_matrix %*% estim_alpha_prime) 
        m_hat_prime <- as.vector(W_prime %*% unexplained_by_ARCH_prime)  
        #v_hat_prime <- unexplained_by_ARCH_prime - m_hat_prime # commented 12/14 - see below
        #v_hat_prime <- (eps^2)[(p+1):N] - sigma_tilde_2_prime
      }
      else  # no covariates
      { 
        #empty_model <- ARCH_internal(eps, p, delta = delta)
        empty_model <- GARCHX(eps=eps, X = NULL, order = c(p,0), delta=delta)
        estim_alpha_prime = empty_model$alpha
        m_hat_prime = rep(0,length(epsilon_delta))
      }
      sigma_tilde_2_prime <- get_sigma_tilde_2(estim_alpha_prime, epsilon_delta, m_hat_prime)
      v_hat_prime <- (eps^2) - sigma_tilde_2_prime
      v_hat_prime[which(is.nan(v_hat_prime))] = 0
      v_hats <- c(v_hats, v_hat_prime)
    }
    
    # Step B2: Perform the tests
    B <- 200
    alpha <- 0.05
    covariate_p.values <- rep(0, n_vars)
    #n <- ncol(v_hats) # in paper_EJS, n is the number of observation-residual pairs for the given covariate
    
    next_odd <- function(n) {
      n <- floor(n)                 # floor each element
      ifelse(n %% 2 == 0, n + 1, n) # if even → next odd; if odd → keep
    }
    range <- unique(next_odd(seq(next_odd(N^(1/3)/2), next_odd(N^(1/3)), length = 5)))
    
    for (i in 1:n_vars)
    {
      #X_values <- X[,i][p:(N - 1)]
      #Y_values <- v_hats[i,]
      # Y_values <- v_hats[i,][2:N]
      #k_n <- ARCHmX_choosek(X_values, Y_values, range, B, alpha)
      
      X_values <- X[,i][1:(N - 1)]
      offset <- N * (i-1)
      Y_values <- v_hats[(offset + 2):(offset + N)]
      if (k.choice == "plugin")
        k_n = next_odd(N^(29/90))
      else
        k_n <- ARCHmX_choosek(X_values, Y_values, range = range, B = B, alpha = 0.05)
      
      covariate_p.values[i] <- ARCHmX_anova_test_univariate(X_values, Y_values, k_n)
    }
  }
  
  
  if (is.null(covariate_p.values)) {
    covariate_p.values <- numeric(0)
  }
  
  p.values <- c(alpha_p.values, covariate_p.values)

  #################################################
  # D: Set model parameters 
  #################################################

  structure(
    list(
      h = optimal_h,
      N = N,
      covariates = covariates,
      delta = delta, #
      df = df,
      log_QL = log_QL,
      epsilon_delta = epsilon_delta,
      p = p,
      q = 0,
      X = X, #
      eps = eps,
      alphas = c(estim_alpha), #
      p.values = c(p.values),
      covariate_p.values = c(covariate_p.values),
      m_hat = m_hat, #
      v_hat = v_hat,
      residual_deviation = residual_deviation,
      AIC = AIC,
      BIC = BIC,
      hypothesisTest = hypothesisTest,
      bandwidth = bandwidth,
      k.choice = k.choice
    ),
    class = "ARCHmX"
  )
}