# install.packages("devtools",repos = "http://cran.us.r-project.org")
library(devtools)

# install.packages("MLmetrics",repos = "http://cran.us.r-project.org")
library(MLmetrics)

#  Compare precision matrix estimation packages   #
#
# @param alg: "SQUIC" or "glasso" or "EQUAL" or "BigQUIC"
# @param data: input data in the form p (dimensions) x n (samples)
# @param lambda: scalar sparsity parameter
# @param tol: termination tolerance
# @param max_iter: maximum number of iterations
# @param X_star: true precision matrix
# @param M: graphical bias for the matrix tuning parameter
compare <- function(alg, data, lambda, tol=1e-3, max_iter=100, X_star=NULL, M=NULL){
    data_t     <- t(data)
    verbose    <- 1
    p          <- dim(data)[1]
    n          <- dim(data)[2]
    time_start <- as.numeric(Sys.time())


    if (alg == "squic") {
        print("#squic")
        # SQUIC
        out <- SQUIC(
            Y = data,
            lambda = lambda,
            max_iter = max_iter,
            tol = tol,
            tol_inv = 1e-4,
            verbose = verbose,
            M = M
        )

        X <- out$X

    } else if (alg == "glasso") {
        print("#glasso")
        # glasso
        out <- glasso(
            s = cov(data_t),
            rho = lambda,
            thr = tol,
            maxit = max_iter,
            penalize.diagonal = TRUE
        )
        
        X <- as(out$wi, "sparseMatrix")

    } else if (alg == "equal") {
        print("#equal")
        # EQUAL
        out <- EQUAL(
            X = data_t,
            type = TRUE,
            sdiag = TRUE,
            lambda = lambda,
            nlambda = 1
            #err = 1e-5, # keep defualt values, decrease either will result in bad results or high runtimes
            #maxIter = 1000,
        )

        X <- out$Omega[[1]]

    } else if (alg == "bigquic") {
        print("#bigquic")
        # BigQUIC
        out <- BigQuic(
            X = data_t,
            lambda = lambda,
            numthreads = detectCores()/2,
            maxit = max_iter,
            epsilon = tol,
            verbose = 0,
            use_ram = TRUE
        )

        X <- out$precision_matrices[[1]]

    } else {
        stop("Alg not found")
    }

    time_end <- as.numeric(Sys.time())

    print(paste("INFO::",alg,"::time=",time_end - time_start,"::nnz=",nnzero(X)/p))

    if (!is.null(X_star)) {
        # Convert matrix to labels
        print("#Computing F1-Score & Accuracy")

        X_star_dense_struc <- (as.vector(triu(X_star,1)) != 0) * 1
        X_dense_struc      <- (as.vector(triu(X,1))      != 0) * 1

        output <- list(
            "time" = time_end - time_start,
            "X"    = X,
            "fro"  = Matrix::norm(X_star - X, "f") / Matrix::norm(X_star, "f"),
            "f1"   = MLmetrics::F1_Score(X_star_dense_struc, X_dense_struc, positive = "1")
        )
    } else {
        # Convert matrix to labels
        output <- list(
            "X"    = X,
            "time" = time_end - time_start
        )
    }

    return(output)
}
