# Francq, C. and Thieu, L.Q.(2018). *QML Inference for Volatility Models with Covariates*.
GARCHX_variance <- function(fit, omega, alpha, beta, gamma, tol = (.Machine$double.eps)){
  # Extract eps, delta, r and exogenous covariates
  eps <- fit$eps
  delta <- fit$delta
  r <- fit$r
  X <- fit$selectedX 

  # Calculate length of time series
  n <- length(eps)

  # Calculate order of alpha and beta, and length of exogenous covariates
  d <- fit$d
  p <- fit$order[1]
  q <- fit$order[2]
  dim_1 <- 1+p+q+d

  # Initialize h, sigma^2, and eta vectors
  h <- rep(0, n)
  sigma2 <- rep(0, n)
  eta <- rep(0, n)

  # Initialize derivative of h, S, I, and J as zero matrices
  der.h <- matrix(0, nrow = dim_1, ncol = n)
  S <- matrix(0, nrow = dim_1, ncol = n)
  I <- matrix(0, nrow = dim_1, ncol = dim_1)
  J <- matrix(0, nrow = dim_1, ncol = dim_1)

  # Calculate initial value of sigma^delta
  sigma2.init <- mean(eps[1:r]^delta)
  h[1:(max(p, q))] <- sigma2.init^{delta/2}
  eps.delta <- eps^delta
  if(d == 0) {
    null <- TRUE
  } else {
    null = FALSE
  }
  # Calculate derivative of h
  if(null == FALSE)
  {

    for(t in (2+(max(p,q))-1):r){
      # Problem is at GARCHX_check_order with
      # eps.delta[(t-1):(t-p)]
      # If p = 2, we get eps.delta[1:0], which only returns one value
      # But we need it to return two values
      eps.entry <- GARCHX_check_order(p, t, eps.delta)

      # Check if q = 0
      h.entry <- GARCHX_check_order(q, t, h)

      # Check if d = 0
      X.entry <- GARCHX_check_gamma(d, t, X)

      # 1/27 per professor
      #if(q == 1 || q == 0){
      #  vol <- beta * h[(t-1):(t-q)]
      #  temp <- beta * der.h[, (t-1):(t-q)]
      #} else {
      #  vol <- beta %*% h[(t-1):(t-q)]
      #  temp <- der.h[, (t-1):(t-q)] %*% beta
      #}
      
      if (q == 0) 
      {
        vol <- 0
        temp <- 0
      }
      else if (q == 1)
      {
        vol <- beta * h[(t-1):(t-q)]
        temp <- beta * der.h[, (t-1):(t-q)]
      }
      else 
      {
        vol <- beta %*% h[(t-1):(t-q)]
        temp <- der.h[, (t-1):(t-q)] %*% beta
      }

      if (d == 0)
        h[t] <- omega + alpha %*% eps.delta[(t-1):(t-p)] + vol 
      else
        h[t] <- omega + alpha %*% eps.delta[(t-1):(t-p)] + vol + drop(X[t-1,] %*% gamma)
      # dim of der.h = 9 x 2
      # dim of beta = 2 x 1
      der.h[ ,t] <- c(1, eps.entry, h.entry, X.entry) + temp
    } # End for

    for (t in (r+1):n){
      eps.entry <- GARCHX_check_order(p, t, eps.delta)
      h.entry <- GARCHX_check_order(q, t, h)

      X.entry <- GARCHX_check_gamma(d, t, X)

      #if(q == 1 || q == 0){
      #  vol <- beta * h[(t-1):(t-q)]
      #  temp <- beta * der.h[, (t-1):(t-q)]
      #} else {
      #  vol <- beta %*% h[(t-1):(t-q)]
      #  temp <- der.h[, (t-1):(t-q)] %*% beta
      #}
      
      if (q == 0) 
      {
        vol <- 0
        temp <- 0
      }
      else if (q == 1)
      {
        vol <- beta * h[(t-1):(t-q)]
        temp <- beta * der.h[, (t-1):(t-q)]
      }
      else 
      {
        vol <- beta %*% h[(t-1):(t-q)]
        temp <- der.h[, (t-1):(t-q)] %*% beta
      }
      
      if (d == 0)
        h[t] <- omega + alpha %*% eps.delta[(t-1):(t-p)] + vol 
      else
        h[t] <- omega + alpha %*% eps.delta[(t-1):(t-p)] + vol + drop(X[t-1,] %*% gamma)
      der.h[ ,t] <- c(1, eps.entry, h.entry, X.entry) + temp
      # der.h[ ,t] <- c(1, eps.entry, h.entry, X.entry) + as.matrix(beta) %*% der.h[,(t-1):(t-q)]

      S[, t] <- der.h[,t]/h[t]

      sigma2[t] <- h[t]^(2/delta)

      eta[t] <- eps[t]/max(sqrt(sigma2[t]), tol)

      I <- I + (4/delta^2) * (eta[t]^4-1) * S[,t] %*% t(S[,t])/(n-r)

      J <- J + (4/delta^2) * S[,t] %*% t(S[,t])/n
    } # End for
  } else {

    for(t in (2+(max(p,q))-1):r){
      eps.entry <- GARCHX_check_order(p, t, eps.delta)
      h.entry <- GARCHX_check_order(q, t, h)

      #if(q == 1 || q == 0){
      #  vol <- beta * h[(t-1):(t-q)]
      #  temp <- beta * der.h[, (t-1):(t-q)]
      #} else {
      #  vol <- beta %*% h[(t-1):(t-q)]
      #  temp <- der.h[, (t-1):(t-q)] %*% beta
      #}
      
      if (q == 0) 
      {
        vol <- 0
        temp <- 0
      }
      else if (q == 1)
      {
        vol <- beta * h[(t-1):(t-q)]
        temp <- beta * der.h[, (t-1):(t-q)]
      }
      else 
      {
        vol <- beta %*% h[(t-1):(t-q)]
        temp <- der.h[, (t-1):(t-q)] %*% beta
      }

      h[t] <- omega + alpha %*% eps.delta[(t-1):(t-p)] + vol

      der.h[,t] <- c(1, eps.entry, h.entry) +  temp
    } # End for

    for(t in (r+1):n){
      eps.entry <- GARCHX_check_order(p, t, eps.delta)
      h.entry <- GARCHX_check_order(q, t, h)
      #if(q == 1 || q == 0){
      #  vol <- beta * h[(t-1):(t-q)]
      #  temp <- beta * der.h[, (t-1):(t-q)]
      #} else {
      #  vol <- beta %*% h[(t-1):(t-q)]
      #  temp <- der.h[, (t-1):(t-q)] %*% beta
      #}
      if (q == 0) 
      {
        vol <- 0
        temp <- 0
      }
      else if (q == 1)
      {
        vol <- beta * h[(t-1):(t-q)]
        temp <- beta * der.h[, (t-1):(t-q)]
      }
      else 
      {
        vol <- beta %*% h[(t-1):(t-q)]
        temp <- der.h[, (t-1):(t-q)] %*% beta
      }

      h[t] <- omega + alpha %*% eps.delta[(t-1):(t-p)] + vol

      der.h[,t] <- c(1, eps.entry, h.entry) +  temp

      S[,t] <- der.h[,t]/h[t]

      sigma2[t] <- h[t]^(2/delta)

      eta[t] <- eps[t]/max(sqrt(sigma2[t]), tol)

      I <- I + (4/delta^2) * (eta[t]^4-1) * S[,t] %*% t(S[,t])/(n-r)

      J <- J + (4/delta^2) * S[,t] %*% t(S[,t])/n
    } # End for
  } # End if-else

  mu4 <- mean(eta[(r+1):n]^4)
  mu2 <- mean(eta[(r+1):n]^2)

  Jinv <- GARCHX_Inv(J)

  Sigma.iid <- (mu4 - mu2) * Jinv

  Sigma <- Jinv %*% I %*% Jinv

  if(min(eigen(Sigma)$values) < tol){
    Sigma <- Sigma.iid
  } # End if

  sd <- sqrt(diag(Sigma/(n-r)))

  sd.iid <- sqrt(diag(Sigma.iid/n))

  t.ratio <- c(omega, alpha, beta, gamma)/sd

  t.ratio.iid <- c(omega, alpha, beta, gamma)/sd.iid

  pval <- (1 - pchisq(t.ratio^2, 1))/2

  pval.iid <- (1 -pchisq(t.ratio.iid^2, 1))/2

  list(sd = sd, pval = pval, sd.iid = sd.iid, pval.iid = pval.iid,
       t.ratio = t.ratio, t.ratio.iid = t.ratio.iid, Sigma = Sigma,
       Sigma.iid = Sigma.iid, I = I, J = J)
} # End variance.GARCHX
