#include <R.h>
#include <Rinternals.h>
#include <R_ext/Rdynload.h>

// extern SEXP FastPOI_C_sub(SEXP in_B, SEXP in_Delta,
//     SEXP in_lambda, SEXP in_maxit, SEXP in_tol
// );

/* Tensor Times Matrix a.k.a. Mode Product */
extern SEXP R_ttm(SEXP A, SEXP X, SEXP mode, SEXP op);

/* Multi Linear Multiplication (iterated mode products) */
extern SEXP R_mlm(SEXP A, SEXP Bs, SEXP modes, SEXP ops);

/* Matrix Times Vectorized Kronecker product `A vec(B_1 %x% ... %x% B_r)` */
extern SEXP mtvk(SEXP A, SEXP Bs);

/* Tensor Mode Crossproduct `A_(m) B_(m)^T` */
extern SEXP R_mcrossprod(SEXP A, SEXP B, SEXP mode);
/* Symmetric Tensor Mode Crossproduct `A_(m) A_(m)^T` */
extern SEXP R_mcrossprod_sym(SEXP A, SEXP mode);

/* Merge matrix-matrix multiplication over 3rd axis of 3D arrays */
extern SEXP R_merge_matmul(SEXP A, SEXP B);

// /* Higher Order PCA */
// extern SEXP hopca(SEXP X);

/* Singular Value Decomposition */
extern SEXP R_svd(SEXP A);

// /* Iterative Cyclic Updating for the Tensor Normal Distribution */
// extern SEXP R_icu_tensor_normal(SEXP X, SEXP Fy, SEXP max_iter);

// /* Generalized tensor normal using NAGD */
// extern SEXP R_gmlm_tensor_normal(
//     SEXP X, SEXP Fy,
//     SEXP eta_bar, SEXP alphas, SEXP Omegas,
//     SEXP max_iter, SEXP max_line_iter
// );

/* Solve linear equation system A X = B */
extern SEXP R_solve(SEXP A, SEXP B);

/* Determinant of a matrix */
extern SEXP R_det(SEXP A);

// /* Unscaled PMF of the Ising model with natural parameters `_params` */
// extern SEXP R_unscaled_prob(SEXP _y, SEXP _params);

// /* Exact computation of the partition function of the Ising model with natural
//    parameters `_params` */
// extern SEXP R_ising_partition_func_exact(SEXP _params);

// /* Estimated partition function of the Ising model with natural parameters `_params` */
// extern SEXP R_ising_partition_func_MC(SEXP _params);

// /* Exact computation of the partition function of the Ising model with natural
//    parameters `_params` */
// extern SEXP R_ising_m2_exact(SEXP _params);

// // extern SEXP R_ising_m2_MC64(SEXP _params);
// extern SEXP R_ising_m2_MC(SEXP _params, SEXP _nr_samples, SEXP _warmup);
// extern SEXP R_ising_m2_MC_thrd(SEXP _params, SEXP _nr_threads);

/* Sample from the Ising model */
extern SEXP R_ising_sample(SEXP, SEXP, SEXP);

// Interface to the Ising second moment function
extern SEXP R_ising_m2(SEXP, SEXP, SEXP, SEXP, SEXP);

/* List of registered routines (a.k.a. C entry points) */
static const R_CallMethodDef CallEntries[] = {
    {"C_ttm",                         (DL_FUNC) &R_ttm,                         4},
    {"C_mlm",                         (DL_FUNC) &R_mlm,                         4},
    {"C_mtvk",                        (DL_FUNC) &mtvk,                          2},
    {"C_mcrossprod",                  (DL_FUNC) &R_mcrossprod,                  3},
    {"C_mcrossprod_sym",              (DL_FUNC) &R_mcrossprod_sym,              2},
    {"C_merge_matmul",                (DL_FUNC) &R_merge_matmul,                2},
    {"C_svd",                         (DL_FUNC) &R_svd,                         1},
    {"C_solve",                       (DL_FUNC) &R_solve,                       2},
    {"C_det",                         (DL_FUNC) &R_det,                         1},
    {"C_ising_sample",                (DL_FUNC) &R_ising_sample,                3},
    {"C_ising_m2",                    (DL_FUNC) &R_ising_m2,                    5},
    // {"C_unscaled_prob",               (DL_FUNC) &R_unscaled_prob,               2},
    // {"C_ising_partition_func_MC",     (DL_FUNC) &R_ising_partition_func_MC,     1},
    // {"C_ising_partition_func_exact",  (DL_FUNC) &R_ising_partition_func_exact,  1},
    // {"C_ising_m2_exact",   (DL_FUNC) &R_ising_m2_exact,   1},
    // {"C_ising_m2_MC",      (DL_FUNC) &R_ising_m2_MC,      3},
    // {"C_ising_m2_MC_thrd", (DL_FUNC) &R_ising_m2_MC_thrd, 2},
    // {"C_ising_m2_MC64",    (DL_FUNC) &R_ising_m2_MC64,    1},
    // {"FastPOI_C_sub",          (DL_FUNC) &FastPOI_C_sub,         5},  // NOT USED
    // {"C_hopca",                      (DL_FUNC) &hopca,                  1},
    // {"C_icu_tensor_normal",          (DL_FUNC) &R_icu_tensor_normal,    3},  // NOT USED / IN DEVELOPMENT
    // {"C_gmlm_tensor_normal",         (DL_FUNC) &R_gmlm_tensor_normal,   7},  // NOT USED / IN DEVELOPMENT
    {NULL, NULL, 0}
};

/**
 * Restrict C entry points to registered routines.
 *
 * NOTE: Naming convention: `R_init_<PACKAGE-NAME>`
 */
void R_init_tensorPredictors(DllInfo *dll) {
    R_registerRoutines(dll, NULL, CallEntries, NULL, NULL);
    R_useDynamicSymbols(dll, FALSE);
}
