stopifnot(require("testthat"), require("lme4"))

## use old (<=3.5.2) sample() algorithm if necessary
if ("sample.kind" %in% names(formals(RNGkind))) {
    suppressWarnings(RNGkind("Mersenne-Twister", "Inversion", "Rounding"))
}

context("fitting lmer models")
## is "Nelder_Mead" default optimizer? -- no longer
(isNM <- formals(lmerControl)$optimizer == "Nelder_Mead")

test_that("lmer", {
    set.seed(101)
    d <- data.frame(z=rnorm(200),
                    f=factor(sample(1:10,200, replace=TRUE)))

    ## Using 'method=*' defunct in 2019-05 (after 6 years of deprecation)
    ## expect_warning(lmer(z~ 1|f, d, method="abc"),"Use the REML argument")
    ## expect_warning(lmer(z~ 1|f, d, method="Laplace"),"Use the REML argument")
    ##sp No '...' anymore
    ##sp expect_warning(lmer(z~ 1|f, d, sparseX=TRUE),"has no effect at present")
    expect_error(lmer(z~ 1|f, ddd), "bad 'data': object 'ddd' not found")
    expect_error(lmer(z~ 1|f), "object 'z' not found")
    expect_error(lmer(z~ 1|f, d[,1:1000]), "bad 'data': undefined columns selected")
    expect_is(fm1 <- lmer(Yield ~ 1|Batch, Dyestuff), "lmerMod")
    expect_is(fm1_noCD <- update(fm1,control=lmerControl(calc.derivs=FALSE)),
              "lmerMod")
    expect_equal(VarCorr(fm1),VarCorr(fm1_noCD))
    ## backward compatibility version {for optimizer="Nelder-Mead" only}:
    if(isNM) expect_is(fm1.old <- update(fm1,control=lmerControl(use.last.params=TRUE)),
                                "lmerMod")
    expect_is(fm1@resp,				"lmerResp")
    expect_is(fm1@pp, 				"merPredD")
    expect_that(fe1 <- fixef(fm1),                      is_equivalent_to(1527.5))
    expect_that(VarCorr(fm1)[[1]][1,1], ## "bobyqa" : 1764.050060
		equals(1764.0375195, tolerance = 1e-5))
    ## back-compatibility ...
    if(isNM) expect_that(VarCorr(fm1.old)[[1]][1,1], equals(1764.0726543))

    expect_that(isREML(fm1),                            equals(TRUE))
    expect_is(REMLfun <- as.function(fm1),	"function")
    expect_that(REMLfun(1),                             equals(319.792389042002))
    expect_that(REMLfun(0),                             equals(326.023232155879))
    expect_that(family(fm1),                            equals(gaussian()))
    expect_that(isREML(fm1ML <- refitML(fm1)),          equals(FALSE))
    expect_that(REMLcrit(fm1),                		equals(319.654276842342))
    expect_that(deviance(fm1ML),                        equals(327.327059881135))
    ##						"bobyqa":      49.51009984775
    expect_that(sigma(fm1),                             equals(49.5101272946856, tolerance=1e-6))
    if(isNM) expect_that(sigma(fm1.old),		equals(49.5100503990048))
    expect_that(sigma(fm1ML),                           equals(49.5100999308089))
    expect_that(extractAIC(fm1),                        equals(c(3, 333.327059881135)))
    expect_that(extractAIC(fm1ML),                      equals(c(3, 333.327059881135)))
    ##						"bobyqa":      375.71667627943
    expect_that(vcov(fm1)    [1,1],			equals(375.714676744, tolerance=1e-5))
    if(isNM) expect_that(vcov(fm1.old)[1,1],		equals(375.72027872986))
    expect_that(vcov(fm1ML)  [1,1],			equals(313.09721874266, tolerance=1e-7))
					#		   was 313.0972246957
    expect_is(fm2 <- refit(fm1, Dyestuff2$Yield), "lmerMod")
    expect_that(fixef(fm2),                             is_equivalent_to(5.6656))
    expect_that(VarCorr(fm2)[[1]][1,1],                 is_equivalent_to(0))
    expect_that(getME(fm2, "theta"),                    is_equivalent_to(0))
    expect_that(X  <- getME(fm1, "X"),                  is_equivalent_to(array(1, c(1, 30))))
    expect_is(Zt <- getME(fm1, "Zt"),		"dgCMatrix")
    expect_that(dim(Zt),                                equals(c(6L, 30L)))
    expect_that(Zt@x,                                   equals(rep.int(1, 30L)))
    expect_equal(dimnames(Zt),
                  list(levels(Dyestuff$Batch),
                       rownames(Dyestuff)))
    ##						"bobyqa":      0.8483237982
    expect_that(theta <- getME(fm1, "theta"),           equals(0.84832031, tolerance=6e-6, check.attributes=FALSE))
    if(isNM) expect_that(getME(fm1.old, "theta"),	is_equivalent_to(0.848330078))
    expect_is(Lambdat <- getME(fm1, "Lambdat"), "dgCMatrix")
    expect_that(as(Lambdat, "matrix"),                  is_equivalent_to(diag(theta, 6L, 6L)))
    expect_is(fm3 <- lmer(Reaction ~ Days + (1|Subject) + (0+Days|Subject), sleepstudy),
              					"lmerMod")
    expect_that(getME(fm3,"n_rtrms"),                   equals(2L))
    expect_that(getME(fm3,"n_rfacs"),                   equals(1L))
    expect_error(fm4 <- lmer(Reaction ~ Days + (1|Subject),
                            subset(sleepstudy,Subject==levels(Subject)[1])), "must have > 1")
    expect_warning(fm4 <- lFormula(Reaction ~ Days + (1|Subject),
                             subset(sleepstudy,Subject==levels(Subject)[1]),
                             control=lmerControl(check.nlev.gtr.1="warning")), "must have > 1")
    expect_warning(fm4 <- lmer(Reaction ~ Days + (1|Subject),
                            subset(sleepstudy,Subject %in% levels(Subject)[1:4]),
                               control=lmerControl(check.nlev.gtreq.5="warning")),
                   "< 5 sampled levels")
    sstudy9 <- subset(sleepstudy, Days == 1 | Days == 9)
    expect_error(lmer(Reaction ~ 1 + Days + (1 + Days | Subject),
                        data = sleepstudy, subset = (Days == 1 | Days == 9)),
                   "number of observations \\(=36\\) <= number of random effects \\(=36\\)")
    expect_error(lFormula(Reaction ~ 1 + Days + (1 + Days | Subject),
                           data = sleepstudy, subset = (Days == 1 | Days == 9)),
                 "number of observations \\(=36\\) <= number of random effects \\(=36\\)")
    ## with most recent Matrix (1.1-1), should *not* flag this
    ## for insufficient rank
    dat <- readRDS(system.file("testdata", "rankMatrix.rds", package="lme4"))
    expect_is(lFormula(y ~ (1|sample) + (1|day) + (1|day:sample) +
                           (1|operator) + (1|day:operator) + (1|sample:operator) +
                           (1|day:sample:operator),
                       data = dat,
                       control = lmerControl(check.nobs.vs.rankZ = "stop")),
                       "list")
    ## check scale
    ss <- within(sleepstudy, Days <- Days*1e6)
    expect_warning(lmer(Reaction ~ Days + (1|Subject), data=ss),
                 "predictor variables are on very different scales")

    ## Promote warning to error so that warnings or errors will stop the test:
    options(warn=2)
    expect_is(lmer(Yield ~ 1|Batch, Dyestuff, REML=TRUE), "lmerMod")
    expect_is(lmer(Yield ~ 1|Batch, Dyestuff, start=NULL), "lmerMod")
    expect_is(lmer(Yield ~ 1|Batch, Dyestuff, verbose=0L), "lmerMod")
    expect_is(lmer(Yield ~ 1|Batch, Dyestuff, subset=TRUE), "lmerMod")
    expect_is(lmer(Yield ~ 1|Batch, Dyestuff, weights=rep(1,nrow(Dyestuff))), "lmerMod")
    expect_is(lmer(Yield ~ 1|Batch, Dyestuff, na.action="na.exclude"), "lmerMod")
    expect_is(lmer(Yield ~ 1|Batch, Dyestuff, offset=rep(0,nrow(Dyestuff))), "lmerMod")
    expect_is(lmer(Yield ~ 1|Batch, Dyestuff, contrasts=NULL), "lmerMod")
    expect_is(lmer(Yield ~ 1|Batch, Dyestuff, devFunOnly=FALSE), "lmerMod")
    expect_is(lmer(Yield ~ 1|Batch, Dyestuff, control=lmerControl(optimizer="Nelder_Mead")), "lmerMod")
    expect_is(lmer(Yield ~ 1|Batch, Dyestuff, control=lmerControl()), "lmerMod")
    ## avoid _R_CHECK_LENGTH_1_LOGIC2_ errors ...
    if (getRversion() < "3.6.0" || packageVersion("optimx")>"2018.7.10") {
        expect_error(lmer(Yield ~ 1|Batch, Dyestuff, control=lmerControl(optimizer="optimx")),"must specify")
        expect_is(lmer(Yield ~ 1|Batch, Dyestuff,
                       control=lmerControl(optimizer="optimx",
                                           optCtrl=list(method="L-BFGS-B"))),
                  "lmerMod")
    }
    expect_error(lmer(Yield ~ 1|Batch, Dyestuff, control=lmerControl(optimizer="junk")), "couldn't find optimizer function")
    ## disable test ... should be no warning
    expect_is(lmer(Reaction ~ 1 + Days + (1 + Days | Subject),
                   data = sleepstudy, subset = (Days == 1 | Days == 9),
                   control=lmerControl(check.nobs.vs.rankZ="ignore",
                   check.nobs.vs.nRE="ignore",
                   check.conv.hess="ignore",
                   ## need to ignore relative gradient check too;
                   ## surface is flat so *relative* gradient gets large
                   check.conv.grad="ignore")),
              "merMod")
    expect_is(lmer(Reaction ~ 1 + Days + (1|obs),
                   data = transform(sleepstudy,obs=seq(nrow(sleepstudy))),
                   control=lmerControl(check.nobs.vs.nlev="ignore",
                   check.nobs.vs.nRE="ignore",
                   check.nobs.vs.rankZ="ignore")),
              "merMod")
    expect_error(lmer(Reaction ~ 1 + Days + (1|obs),
                      data = transform(sleepstudy,obs=seq(nrow(sleepstudy))),
                      "number of levels of each grouping factor"))

    ## check for errors with illegal input checking options
    flags <- lme4:::.get.checkingOpts(names(formals(lmerControl)))
    .t <- lapply(flags, function(OPT) {
	## set each to invalid string:
	## cat(OPT,"\n")
	expect_error(lFormula(Reaction~1+Days+(1|Subject), data = sleepstudy,
			      control = do.call(lmerControl,
				  ## Deliberate: fake typo
				  ##		       vvv
				  setNames(list("warnign"), OPT))),
		     "invalid control level")
    })
    ## disable warning via options
    options(lmerControl=list(check.nobs.vs.rankZ="ignore",check.nobs.vs.nRE="ignore"))
    expect_is(fm4 <- lmer(Reaction ~ Days + (1|Subject),
			  subset(sleepstudy,Subject %in% levels(Subject)[1:4])), "merMod")
    expect_is(lmer(Reaction ~ 1 + Days + (1 + Days | Subject),
                   data = sleepstudy, subset = (Days == 1 | Days == 9),
                   control=lmerControl(check.conv.hess="ignore",
                   check.conv.grad="ignore")),
              "merMod")
    options(lmerControl=NULL)
    ## check for when ignored options are set
    options(lmerControl=list(junk=1,check.conv.grad="ignore"))
    expect_warning(lmer(Reaction ~ Days + (1|Subject),sleepstudy),
                   "some options")
    options(lmerControl=NULL)
    options(warn=0)
    expect_error(lmer(Yield ~ 1|Batch, Dyestuff, junkArg=TRUE), "unused argument")
    expect_warning(lmer(Yield ~ 1|Batch, Dyestuff, control=list()),
                    "passing control as list is deprecated")
    if(FALSE) ## Hadley broke this
        expect_warning(lmer(Yield ~ 1|Batch, Dyestuff, control=glmerControl()),
                       "passing control as list is deprecated")

    ss <- transform(sleepstudy,obs=factor(seq(nrow(sleepstudy))))
    expect_warning(lmer(Reaction ~ 1 + (1|obs), data=ss,
         control=lmerControl(check.nobs.vs.nlev="warning",
                             check.nobs.vs.nRE="ignore")),
                   "number of levels of each grouping factor")

    ## test deparsing of very long terms inside mkReTrms
    set.seed(101)
    longNames <- sapply(letters[1:25],
                        function(x) paste(rep(x,8),collapse=""))
    tstdat <- data.frame(Y=rnorm(10),
                         F=factor(1:10),
                         matrix(runif(250),ncol=25,
                                dimnames=list(NULL,
                                longNames)))
    expect_is(lFormula(Y~1+(aaaaaaaa+bbbbbbbb+cccccccc+dddddddd+
                        eeeeeeee+ffffffff+gggggggg+hhhhhhhh+
                        iiiiiiii+jjjjjjjj+kkkkkkkk+llllllll|F),
                   data=tstdat,
                   control=lmerControl(check.nobs.vs.nlev="ignore",
                   check.nobs.vs.nRE="ignore",
                   check.nobs.vs.rankZ="ignore")),"list")

    ## do.call(new,...) bug
    new <- "foo"
    expect_is(refit(fm1),"merMod")
    rm("new")

    ## test subset-with-( printing from summary
    fm1 <- lmer(z~1|f,d,subset=(z<1e9))
    expect_equal(sum(grepl("Subset: \\(",capture.output(summary(fm1)))),1)

    ## test messed-up Hessian
    fm1 <- lmer(z~ as.numeric(f) + 1|f, d)
    fm1@optinfo$derivs$Hessian[2,2] <- NA
    expect_warning(lme4:::checkConv(fm1@optinfo$derivs,
                     coefs=c(1,1),
                     ctrl=lmerControl()$checkConv,lbound=0),
                   "Problem with Hessian check")

    ## test ordering of Ztlist names
    ## this is a silly model, just using it for a case
    ## where nlevs(RE term 1) < nlevs(RE term 2)x
    data(cbpp)
    cbpp <- transform(cbpp,obs=factor(1:nrow(cbpp)))
    fm0 <- lmer(incidence~1+(1|herd)+(1|obs),cbpp,
         control=lmerControl(check.nobs.vs.nlev="ignore",
                             check.nobs.vs.rankZ="ignore",
                             check.nobs.vs.nRE="ignore",
                             check.conv.grad="ignore",
                             check.conv.singular="ignore",
                             check.conv.hess="ignore"))
    fm0B <- update(fm0, .~1+(1|obs)+(1|herd))
    expect_equal(names(getME(fm0,"Ztlist")),
                 c("obs.(Intercept)", "herd.(Intercept)"))
    ## stable regardless of order in formula
    expect_equal(getME(fm0,"Ztlist"),getME(fm0B,"Ztlist"))
    ## no optimization  (GH #408)
    fm_noopt <- lmer(z~1|f,d,
                     control=lmerControl(optimizer=NULL))
    expect_equal(unname(unlist(getME(fm_noopt,c("theta","beta")))),
                 c(0.244179074357121, -0.0336616441209862))
    expect_error(lmer(z~1|f,d,
                     control=lmerControl(optimizer="none")),
                 "deprecated use")
    my_opt <- function(fn,par,lower,upper,control) {
        opt <- optim(fn=fn,par=par,lower=lower,
              upper=upper,control=control,,method="L-BFGS-B")
        return(list(par=opt$par,fval=opt$value,conv=opt$convergence))
    }
    expect_is(fm_noopt <- lmer(z~1|f,d,
                     control=lmerControl(optimizer=my_opt)),"merMod")

    ## test verbose option for nloptwrap
    cc <- capture.output(lmer(Reaction~1+(1|Subject),
         data=sleepstudy,
         control=lmerControl(optimizer="nloptwrap",
                 optCtrl=list(xtol_abs=1e-6, ftol_abs=1e-6)),
         verbose=5))
    expect_equal(sum(grepl("^iteration:",cc)),14)

}) ## test_that(..)

test_that("coef_lmer", {
    ## test coefficient extraction in the case where RE contain
    ## terms that are missing from the FE ...
    set.seed(101)
    d <- data.frame(resp=runif(100),
                    var1=factor(sample(1:5,size=100,replace=TRUE)),
                    var2=runif(100),
                    var3=factor(sample(1:5,size=100,replace=TRUE)))
    library(lme4)
    mix1 <- lmer(resp ~ 0 + var1 + var1:var2 + (1|var3), data=d)
    c1 <- coef(mix1)
    expect_is(c1, "coef.mer")
    cd1 <- c1$var3
    expect_is   (cd1, "data.frame")
    n1 <- paste0("var1", 1:5)
    nn <- c(n1, paste(n1, "var2", sep=":"))
    expect_identical(names(cd1), c("(Intercept)", nn))
    expect_equal(fixef(mix1),
                 setNames(c(0.2703951, 0.3832911, 0.451279, 0.6528842, 0.6109819,
                            0.4949802, 0.1222705, 0.08702069, -0.2856431, -0.01596725),
                          nn), tolerance= 6e-6)# 64-bit:  6.73e-9
})

test_that("getCall", {
    ## GH #535
    getClass <- function() "foo"
    expect_is(glmer(round(Reaction) ~ 1 + (1|Subject), sleepstudy,
        family=poisson), "glmerMod")
    rm(getClass)
})

test_that("better info about optimizer convergence",
{
    set.seed(14)
    cbpp$var <- rnorm(nrow(cbpp), 10, 10)

    suppressWarnings(gm2 <-
                         glmer(cbind(incidence, size - incidence) ~ period * var + (1 | herd),
                               data = cbpp, family = binomial,
                               control=glmerControl(optimizer=c("bobyqa","Nelder_Mead")))
                     )

    ## FIXME: with new update, suppressWarnings(update(gm2)) will give
    ## Error in as.list.environment(X[[i]], ...) : 
    ## promise already under evaluation: recursive default argument reference or earlier problems?
    op <- options(warn=-1)
    gm3 <- update(gm2,
                  control=glmerControl(optimizer="bobyqa",
                                       optCtrl=list(maxfun=2)))
    options(op)

    cc <-capture.output(print(summary(gm2)))
    expect_equal(tail(cc,3)[1],
                 "optimizer (Nelder_Mead) convergence code: 0 (OK)")
})

context("convergence warnings etc.")

fm1 <- lmer(Reaction~ Days + (Days|Subject), sleepstudy)
suppressMessages(fm0 <- lmer(Reaction~ Days + (Days|Subject), sleepstudy[1:20,]))

msg_in_output <- function(x, str) {
    cc <- capture.output(.prt.warn(x))
    any(grepl(str , cc))
}

test_that("convergence warnings from limited evals", {
    expect_warning(fm1B <- update(fm1, control=lmerControl(optCtrl=list(maxeval=3))),
                   "convergence code 5")
    expect_true(msg_in_output(fm1B@optinfo, "convergence code: 5"))
    expect_warning(fm1C <- update(fm1, control=lmerControl(optimizer="bobyqa",optCtrl=list(maxfun=3))),
                   "maximum number of function evaluations exceeded")
    expect_true(msg_in_output(fm1C@optinfo,
                              "maximum number of function evaluations exceeded"))
    ## one extra (spurious) warning here ...
    expect_warning(fm1D <- update(fm1, control=lmerControl(optimizer="Nelder_Mead",optCtrl=list(maxfun=3))),
                   "failure to converge in 3 evaluations")
    expect_true(msg_in_output(fm1D@optinfo,
                              "failure to converge in 3 evaluations"))
    expect_message(fm0D <- update(fm0, control=lmerControl(optimizer="Nelder_Mead")),
                   "boundary")
    expect_true(msg_in_output(fm0D@optinfo,
                              "(OK)"))
})

## GH 533
test_that("test for zero non-NA cases", {
    data_bad <- sleepstudy
    data_bad$Days <- NA_real_
    expect_error(lmer(Reaction ~ Days + (1| Subject), data_bad),
                 "0 \\(non-NA\\) cases")
})
