####  Utility Functions for lme4 Testing
####  ----------------------------------
if(FALSE) ### "Load" these by
    source(system.file("testdata", "lme-tst-funs.R", package="lme4", mustWork=TRUE))
## e.g. from ../../tests/glmmWeights.R

##' example originally from Gabor Grothendieck
##' https://stat.ethz.ch/pipermail/r-sig-mixed-models/2010q2/003726.html
##'
##' @title Simple LMM with 1 ran.eff., 1 fixed eff.
rSim.11 <- function(n, k, sigma=1, beta=2) {
    stopifnot(length(n) == 1, length(k) == 1, n > k, k >= 1, k == round(k))
    x <- 1:n
    fac <- gl(k, 1, n)
    fac.eff <- rnorm(k, 0, 4)[fac]
    e <- rnorm(n, sd=sigma)
    data.frame(x, fac,
               y = 1 + beta * x + fac.eff + e)
}

##' General GLMM simulation  for the model   y ~ x + (1|block)
##' i.e., in "2-way notation",
##'   g( E[Y_{ij}|*] ) =: \eta_{ij} = \beta_1 + \beta_2 x_{ij} + B_j,
##'                                   for  j = 1..nblk,  i = 1..nperblk.
##' @param nblk integer number of "blocks" (sometimes named \eqn{m}).
##' @param nperblk integer number of observations per block. ((may be as small as 1,
##'                e.g., for the binomial or poisson family
##' @param sigma_B standard deviation of random effect  B  (default = 1).
##' @param beta length-2 (intercept, slope) fixed eff. parameter  = c(4,3),
##' @param x  numeric vector of length \eqn{n}   (default = runif(n) (with sd = sqrt(1/12) = 0.2887)
##'          allow also x=c(0,1)   [length ?? FIXME !!]
##' @param shape numeric parameter for Gamma shape  (default = 2)
##' @param nbinom integer N (\code{size}) for binomial trials      (default = 10)
##' @param sd standard deviation ('sigma') for gaussian() family   (default = 1 )
##' @param dInitial an "initial" data frame alternatively to the previous arguments; must contain
##'                 valid columns
##' @param family a \code{\link{family}} object such as \code{Gamma()}, \code{binomial()},
##'               or \code{gaussian(link = log)}.
##'
##' @author Martin Maechler
gSim <- function(nblk   =  26,
                 nperblk= 100,
                 sigma_B = 1,
                 beta = c(4,3),
                 x = runif(n),
                 shape  = 2,  ## shape parameter for Gamma
                 nbinom = 10, ## N for binomial trials
                 sd     = 1,  ## standard deviation ('sigma') for gaussian family
                 dInitial = NULL,
                 family = Gamma())
{
  if(is.null(dInitial)) {
    stopifnot(1 <= nblk, nblk <= 1e7, 1 <= nperblk,
              nblk * nperblk <= 5e7) # some sanity ..
    ## ch.set := a large enough set of "letters", as level-labels for 'block':
    nc <- length(ch.set <- c(LETTERS, letters,
                             paste0(LETTERS,LETTERS), paste0(LETTERS,letters)))
    while(nblk > nc) nc <-
	length(ch.set <- c(paste0(ch.set, LETTERS),
			   paste0(ch.set, letters)))
    stopifnot(nblk <= length(ch.set))
    d <- expand.grid(block = ch.set[1:nblk],
                     rep= 1:nperblk, KEEP.OUT.ATTRS=FALSE)
    stopifnot(nblk == length(levels(d$block)),
              (n <- nrow(d)) == nblk * nperblk, length(x) == n, length(beta) == 2)
    d$ x <- x
    reff_f <- rnorm(nblk, sd = sigma_B)
    ## need intercept large enough to avoid negative values
    d$ eta0 <- beta[1] + beta[2]*x  ## fixed effects only
    d$ eta  <- d$eta0 + reff_f[d$block]
  } else { # use 'dInitial'
    stopifnot(is.data.frame(dInitial),
              is.character(nm.d <- c("block", "x", "eta0", "eta")), # "rep" not really required
              nm.d %in% names(dInitial),
              is.factor(dInitial[["block"]]),
              vapply(nm.d[-1], function(nm) is.numeric(dInitial[[nm]]), NA))
    d <- dInitial
    n <- nrow(d)
  }
    ## Only now  "family-specific" : ----------------------
    d$ mu   <- family$linkinv(d$eta)
    d$ y <- switch(family$family,
                   "Gamma"    = rgamma(n, scale = d$mu/shape, shape=shape),
                   "gaussian" = rnorm(n, d$mu, sd = sd),
                   "poisson"  = rpois(n, d$mu),
		   "binomial" = {
		       z <- rbinom(n, prob=d$mu, size=nbinom)
		       if (nbinom==1)
                           factor(z)
                       else
                           cbind(succ = z, fail = nbinom - z)
		   },
                   stop("Family ", family$family, " not supported here"))
    d
}

### For Model comparisons

## a version of getME() that can be used for objects, both from lme4.0 or lme4
gimME <- function(mod, nm, is.mer = is(mod,"mer"))
    if(is.mer) lme4.0::getME(mod,nm) else lme4::getME(mod,nm)

##' @title All Coefficients of fitted  (G)LMM model
##' @param mod a fitted (G)LMM model from pkg lme4.0 or lme4
##' @param incl.t logical, indicating if 'beta' should include std.errors and t-values
##' @return named vector of coefficients [ beta | theta | sigma ]
##' @author Martin Maechler
allcoefs <- function(mod, incl.t = FALSE) {
    iMer <- is(mod, "mer")
    sigmaF <- {
	if (iMer) lme4.0::sigma else if(getRversion() >= "3.3.0")
	    stats::sigma else lme4::sigma
    }
    ## incl.t: rather also the std.err., t-values:
    c(beta = if(incl.t) coef(summary(mod)) else gimME(mod, "beta", iMer),
      gimME(mod,"theta", iMer),# have their own names
      sigma = sigmaF(mod))
}

##' S3 method but also works for lme4.0:
all.equal.merMod <- function(target, current, incl.t=FALSE, ...) {
    all.equal(allcoefs(target,  incl.t),
              allcoefs(current, incl.t), ...)
}

##' some optimizer info
isOptimized <- function(mod) mod@optinfo[["conv"]][["opt"]] == 0
baseOpti <- function(mod) mod@optinfo[c("optimizer", "conv","feval")]

## use case:  all.equal(emptyCall(mod1), emptyCall(mod2))
emptyCall <- function(mod, arg = quote(`.....`)) {
    cl <- mod@call[1:2]
    names(cl) <- NULL
    cl[[2]] <- arg
    mod@call <- cl
    mod
}

## Matrix >= 1.4.1  keeps names in  diag(.)
Mge141 <- packageVersion("Matrix") >= "1.4.1"
uc <- if(Mge141) c else function(...) c(..., use.names = FALSE)
## uc(a = 1.234, b = 4...)  has names for Matrix >= 1.4.1
unn <- if(Mge141) identity else unname
## unn(vec)  # keeps names for Matrix >= 1.4.1
