#'  Kernel matrix construction for given datasets
#'
#' Constructs a kernel matrix \eqn{K \in \mathbb{R}^{n' \times n}} given two
#' datasets \eqn{X \in \mathbb{R}^{n \times d}} and \eqn{X' \in \mathbb{R}^{n' \times d}},
#' where \eqn{x_i \in \mathbb{R}^d} and \eqn{x'_j \in \mathbb{R}^d} denote the
#' i-th and j-th rows of \eqn{X} and \eqn{X'}, respectively, and
#' \eqn{K_{ji}=\mathcal{K}(x_i, x'_j)} for a user-specified kernel.
#' Implemented in C++ via RcppArmadillo.
#'
#' @param X Design matrix \eqn{X \in \mathbb{R}^{n \times d}} (rows  \eqn{x_i \in \mathbb{R}^d}).
#' @param X_new  Second matrix \eqn{X' \in \mathbb{R}^{n' \times d}} (rows  \eqn{x'_j \in \mathbb{R}^d}).
#' If omitted, \eqn{X' = X} and \eqn{n' = n}.
#' @param kernel Kernel type; one of \code{"gaussian"} or \code{"laplace"}.
#' @param rho Kernel width parameter (\eqn{\rho > 0}). Default is 1.
#' @param n_threads Number of parallel threads. If \code{NULL}, it defaults to
#'   half of the available system processors (\code{max_threads \%/\% 2}).
#'   For these parallelizable cases, if the system has 3 or fewer processors, it automatically
#'   falls back to \code{1} thread; otherwise, it is capped at \code{max_threads - 1}.
#'   For all other settings, it is restricted to \code{1} thread.
#'   Parallelization (implemented in C++) is one of the main advantages
#'   of this package and is applied only for \code{"laplace"} kernels.
#'
#' @details
#' Gaussian kernel: \deqn{K_{ji}=\mathcal{K}(x_i,x_j)=\exp\!\big(-\rho\|x_i-x_j\|_2^2\big)}
#' Laplace kernel:  \deqn{K_{ji}=\mathcal{K}(x_i,x_j)=\exp\!\big(-\rho\|x_i-x_j\|_1\big)}
#'
#' @return
#' The computed kernel matrix. If \code{X_new} is \code{NULL}, the result is a symmetric matrix
#' \eqn{K_{ij} = \mathcal{K}(x_i, x_j)}, with \eqn{K \in \mathbb{R}^{n \times n}}.
#' Otherwise, the result is a rectangular matrix
#' \eqn{K'_{ji} = \mathcal{K}(x_i, x'_j)}, with \eqn{K' \in \mathbb{R}^{n' \times n}}.
#'
#' @examples
#' # Data setting
#' set.seed(1)
#' d = 1
#' rho = 1
#' n = 100
#' X = matrix(runif(n*d, 0, 1), nrow = n, ncol = d)
#'
#' # New design matrix
#' new_n = 150
#' new_X = matrix(runif(new_n*d, 0, 1), nrow = new_n, ncol = d)
#'
#' # Make kernel : Gaussian kernel
#' K = make_kernel(X, kernel = "gaussian", rho = rho) ## symmetric matrix
#' new_K = make_kernel(X, new_X, kernel = "gaussian", rho = rho) ## rectangular matrix
#'
#' # Make kernel : Laplace kernel
#' K = make_kernel(X, kernel = "laplace", rho = rho, n_threads = 1) ## symmetric matrix
#' new_K = make_kernel(X, new_X, kernel = "laplace", rho = rho, n_threads = 1) ## rectangular matrix
#'
#' @name make_kernel
#' @usage make_kernel(X,
#'             X_new = NULL,
#'             kernel = "gaussian",
#'             rho = 1,
#'             n_threads = NULL)
#' @export make_kernel
#' @useDynLib FastKRR, .registration = TRUE
#' @importFrom Rcpp evalCpp
NULL
