#' Fit a generalized linear model with an L0 penalty
#' @description Fit a sparse generalized linear model by approximating the
#'   L0-penalized objective with an adaptive ridge algorithm.
#' @usage  l0ara(x, y, family, lam, standardize, maxit, eps)
#' @param x Design matrix. A numeric matrix is used as-is; other inputs must be
#'   coercible to a model matrix. Rows correspond to observations and columns to
#'   predictors.
#' @param y Response vector. For accurate use, supply numeric outcomes for
#'   \code{family = "gaussian"}, positive numeric outcomes for
#'   \code{family = "gamma"} and \code{family = "inv.gaussian"}, binary outcomes
#'   coded as 0/1 for \code{family = "logit"}, and non-negative counts for
#'   \code{family = "poisson"}.
#' @param family Model family.
#' @param lam A single user-supplied penalty value. If you have a candidate
#'   sequence of values, use \code{cv.l0ara()} to choose \code{lam.min} and then
#'   refit with \code{l0ara()}. Setting \code{lam = 2} mimics AIC-style model
#'   selection, and \code{lam = log(n)} mimics BIC-style model selection.
#' @param standardize Logical flag indicating whether to standardize the columns
#'   of \code{x} before fitting. The original \code{x} is stored in the returned
#'   object.
#' @param maxit Maximum number of iterations passed to the fitting routine.
#' @param eps Convergence threshold. Default value is \code{1e-4}.
#' @details The objective function is
#'   \deqn{-(\log\mbox{-}\mathrm{likelihood}) + (\lambda / 2)|\beta|_0,}
#'   where \eqn{|\beta|_0} is the number of non-zero elements of \eqn{\beta}.
#'   The adaptive ridge algorithm provides an efficient approximation to the
#'   corresponding L0-penalized generalized linear model.
#' @return An object with S3 class "l0ara" containing:
#'  \item{beta}{A vector of fitted coefficients.}
#'  \item{df}{Number of non-zero coefficients.}
#'  \item{iter}{Number of iterations used by the fitting routine.}
#'  \item{lam}{The supplied penalty value.}
#'  \item{lambda}{A copy of \code{lam} for compatibility with downstream
#'    methods.}
#'  \item{family}{The fitted model family.}
#'  \item{x}{The original design matrix supplied to the function.}
#'  \item{y}{The response vector supplied to the function.}
#' @author
#' Wenchuan Guo <wguo1017@gmail.com>, Shujie Ma <shujie.ma@ucr.edu>, Zhenqiu Liu <Zhenqiu.Liu@cshs.org>
#' @seealso \code{\link{cv.l0ara}}, \code{\link{predict.l0ara}},
#'   \code{\link{coef.l0ara}}, \code{\link{plot.l0ara}}.
#' @useDynLib l0ara
#' @importFrom Rcpp evalCpp
#' @import stats
#' @import graphics
#' @examples
#' # Linear regression
#' # Generate design matrix and response variable
#' n <- 100
#' p <- 40
#' x <- matrix(rnorm(n*p), n, p)
#' beta <- c(1,0,2,3,rep(0,p-4))
#' noise <- rnorm(n)
#' y <- x%*%beta+noise
#' # fit sparse linear regression using BIC
#' res.gaussian <- l0ara(x, y, family="gaussian", log(n))
#'
#' # predict for new observations
#' print(res.gaussian)
#' predict(res.gaussian, newx=matrix(rnorm(3,p),3,p))
#' coef(res.gaussian)
#'
#' # Logistic regression
#' # Generate design matrix and response variable
#' n <- 100
#' p <- 40
#' x <- matrix(rnorm(n*p), n, p)
#' beta <- c(1,0,2,3,rep(0,p-4))
#' prob <- exp(x%*%beta)/(1+exp(x%*%beta))
#' y <- rbinom(n, rep(1, n), prob)
#' # fit sparse logistic regression
#' res.logit <- l0ara(x, y, family="logit", 0.7)
#'
#' # predict for new observations
#' print(res.logit)
#' predict(res.logit, newx=matrix(rnorm(3,p),3,p))
#' coef(res.logit)
#'
#' # Poisson regression
#' # Generate design matrix and response variable
#' n <- 100
#' p <- 40
#' x <- matrix(rnorm(n*p), n, p)
#' beta <- c(1,0,0.5,0.3,rep(0,p-4))
#' mu <- exp(x%*%beta)
#' y <- rpois(n, mu)
#' # fit sparse Poisson regression using AIC
#' res.pois <- l0ara(x, y, family="poisson", 2)
#'
#' # predict for new observations
#' print(res.pois)
#' predict(res.pois, newx=matrix(rnorm(3,p),3,p))
#' coef(res.pois)

#' @export
l0ara <- function(x, y, family = c("gaussian", "logit", "gamma", "poisson", "inv.gaussian"), lam, standardize = TRUE, maxit = 10^3, eps = 1e-04){
  x <- .l0ara_as_matrix(x)
  y <- .l0ara_as_numeric(y)

  family <- match.arg(family)
  if (missing(lam)) {
    stop("'lam' was not found(must be a postiive number)", call. = FALSE)
  }
  if (length(lam) > 1L) {
    stop("Require lenght >=1 for 'lam'", call. = FALSE)
  }
  if (lam < 0) {
    warning("lambda < 0; set to 0")
    lam <- 0
  }
  np <- dim(x)
  if (is.null(np) || np[2] <= 1L) {
    stop("x should be a matrix with 2 or more columns", call. = FALSE)
  }
  if (length(y) != np[1]) {
    stop("length of y not equal to the number of rows of x", call. = FALSE)
  }

  res <- .l0ara_fit_raw(
    x = x,
    y = y,
    family = family,
    lam = lam,
    maxit = maxit,
    eps = eps,
    standardize = standardize
  )
  class(res) <- "l0ara"
  res
}
