#  File src/library/stats/R/confint.R
#  Part of the R package, https://www.R-project.org

# Portions copied from MASS/R/confint.R
#
#  Copyright (C) 2003-2025 The R Core Team
#  Copyright (C) 1994-2006 W. N. Venables and B. D. Ripley
#
#  This program is free software; you can redistribute it and/or modify
#  it under the terms of the GNU General Public License as published by
#  the Free Software Foundation; either version 2 of the License, or
#  (at your option) any later version.
#
#  This program is distributed in the hope that it will be useful,
#  but WITHOUT ANY WARRANTY; without even the implied warranty of
#  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
#  GNU General Public License for more details.
#
#  A copy of the GNU General Public License is available at
#  https://www.R-project.org/Licenses/

confint <- function(object, parm, level = 0.95, ...) UseMethod("confint")

## *different* and simpler than  format_perc()  in ./quantile.R for quantile.default()
.format_perc <- function(probs, digits)
    paste(format(100 * probs, trim = TRUE, scientific = FALSE, digits = digits),
	  "%")

confint.lm <- function(object, parm, level = 0.95, ...)
{
    cf <- coef(object)
    ses <- sqrt(diag(vcov(object))) # gives NA for aliased parms
    pnames <- names(ses) # ok for "mlm", too
    if(is.matrix(cf)) cf <- setNames(as.vector(cf), pnames) # for "mlm"
    if(missing(parm)) parm <- pnames
    else if(is.numeric(parm)) parm <- pnames[parm]
    ## else 'parm' must contain parameter names matching those in 'pnames'
    a <- (1 - level)/2
    a <- c(a, 1 - a)
    fac <- qt(a, object$df.residual) # difference from default method
    pct <- .format_perc(a, 3)
    ci <- array(NA_real_,
                dim = c(length(parm), 2L), dimnames = list(parm, pct))
    ci[] <- cf[parm] + ses[parm] %o% fac
    ci
}

##Copied from MASS, modified for score tests ("Rao") by pd July 2023
confint.glm <- function(object, parm, level = 0.95, trace = FALSE, test = c("LRT", "Rao"), ...)
{
    test <- match.arg(test)
    pnames <- names(coef(object))
    if(missing(parm)) parm <- seq_along(pnames)
    else if(is.character(parm))  parm <- match(parm, pnames, nomatch = 0L)
    message("Waiting for profiling to be done...")
    utils::flush.console()
    object <- profile(object, which = parm, alpha = (1. - level)/4.,
                      trace = trace, test = test)
    confint(object, parm=parm, level=level, trace = trace, test = test, ...)
}

confint.profile.glm <- function(object, parm = seq_along(pnames), level = 0.95,
                                trace = FALSE, test = attr(object, "test"), ...)
{
    if (is.null(test)) test <- "LRT" # For backwards compatibility

    of <- attr(object, "original.fit")
    pnames <- names(coef(of))
    if(is.character(parm))  parm <- match(parm, pnames, nomatch = 0L)

    if (is.null(attr(object,"test"))) # For backwards compatibility
        attr(object,"test") <- "LRT" 
    if (test != attr(object, "test")) {
        message("Reprofiling for ", test, " statistic. Waiting...")
        utils::flush.console()
        object <- profile(of, which = parm, alpha = (1. - level)/4.,
                          trace = trace, test = test)
    }
 
    a <- (1-level)/2
    a <- c(a, 1-a)
    pct <- paste(round(100*a, 1), "%")
    ci <- array(NA, dim = c(length(parm), 2L),
                dimnames = list(pnames[parm], pct))
    ## Hmm: This could have a df correction if there's an estimated dispersion parameter
    ## Leave for now. -pd
    cutoff <- qnorm(a)
    for(pm in parm) {
        pro <- object[[ pnames[pm] ]]
        ## skip aliased params
        if(is.null(pro)) next
        if(length(pnames) > 1L)
            sp <- spline(x = pro[, "par.vals"][, pm], y = pro[, 1])
        else sp <- spline(x = pro[, "par.vals"], y = pro[, 1])
        ci[pnames[pm], ] <- approx(sp$y, sp$x, xout = cutoff)$y
    }
    drop(ci)
    ## This could be nice, but maybe also disruptive...
    # attr(ci, "test") <- "test" 
}

## loading the MASS namespace will overwrite these in the registry.
## stub is a specialized version of MASS:::confint.xxx with specific message

## Disabled for glm -pd
#confint.glm <- function(object, parm, level = 0.95, ...)
#{
#    if(!requireNamespace("MASS", quietly = TRUE))
#        stop("package 'MASS' must be installed")
#    asNamespace("MASS")$confint.glm(object, parm, level, ...)
#}

#confint.nls <- function(object, parm, level = 0.95, ...)
#{
#    if(!requireNamespace("MASS", quietly = TRUE))
#        stop("package 'MASS' must be installed")
#    asNamespace("MASS")$confint.nls(object, parm, level, ...)
#}

confint.nls <-
  function(object, parm, level = 0.95, ...)
{
  pnames <- names(coef(object))
  if(missing(parm)) parm <- seq_along(pnames)
  if(is.numeric(parm))  parm <- pnames[parm]
  message("Waiting for profiling to be done...")
  utils::flush.console()
  object <- profile(object, which = parm, alphamax = (1. - level)/4.)
  confint(object, parm=parm, level=level, ...)
}

confint.profile.nls <-
  function(object, parm = seq_along(pnames), level = 0.95, ...)
{
  pnames <- names(object) # non-linear pars only
  ncoefs <- length(coef(attr(object, "original.fit")))
  of <- attr(object, "original.fit")
  if(is.numeric(parm))  parm <- pnames[parm]
  ## drop any plinear parameters
  parm <- parm[parm %in% pnames]
  n <- length(fitted(of)) - length(of$m$getPars())
  a <- (1-level)/2
  a <- c(a, 1-a)
  pct <- paste(round(100*a, 1), "%", sep = "")
  ci <- array(NA, dim = c(length(parm), 2L), dimnames = list(parm, pct))
  cutoff <- qt(a, n)
  for(pm in parm) {
    pro <- object[[pm]]
    sp <- if(ncoefs > 1) spline(x = pro[, "par.vals"][, pm], y = pro$tau)
    else spline(x = pro[, "par.vals"], y = pro$tau)
    ci[pm, ] <- approx(sp$y, sp$x, xout = cutoff)$y
  }
  drop(ci)
}


confint.default <- function (object, parm, level = 0.95, ...)
{
    if(isS4(object)) {
        coef <- stats4::coef
        vcov <- stats4::vcov
    }
    cf <- coef(object)
    pnames <- names(cf)
    if(missing(parm)) parm <- pnames
    else if(is.numeric(parm)) parm <- pnames[parm]
    V <- vcov(object)
    ### Fixup for cases where V is not named
    ### Will break if dimensions don't match between cf and V,
    ### but without names, you can't really tell how things line up
    if (is.null(dimnames(V))) dimnames(V) <- list(pnames,pnames)
    a <- (1 - level)/2
    a <- c(a, 1 - a)
    pct <- .format_perc(a, 3)
    fac <- qnorm(a)
    ci <- array(NA, dim = c(length(parm), 2L),
		dimnames = list(parm, pct))
    ses <- sqrt(diag(V))[parm]
    ci[] <- cf[parm] + ses %o% fac
    ci
}

