## run the example from predict.gMAP
source_example("predict_gMAP.R")

## check that we got for each input data item a prediction
test_that("correct # of predictions are generated", {
  expect_equal(nrow(map$data), ncol(samp))
})

## check that the predictive distribution has a variance which is
## larger in accordance to the betwee-trial heterogeniety (needs to be
## done on the link scale)

test_that("variances have correct ordering", {
  pred_cov_link <- predict(map, type = "link")
  within_var <- (summary(pred_cov_link)[, "sd"])^2

  pred_cov_link_pred <- predict(map, trans_cov, type = "link")
  pred_var_pred <- summary(pred_cov_link_pred)[, "sd"]
  tau_est <- summary(map)$tau[, "mean"]

  ## the predictive must include between and within; as such it is
  ## larger than within
  expect_true(all(pred_var_pred > tau_est))

  ## ensure that predictive has larger variance than the model estimate
  expect_true(all(
    summary(pred_cov_link_pred)[, "sd"] > summary(pred_cov_link)[, "sd"]
  ))
})


## new prediction was done for a single data item
test_that("correct # of new predictions are generated", {
  expect_equal(ncol(pred_new), 1)
})

## must have larger sd than between-trial alone (on link scale)
test_that("predictive variances have correct ordering", {
  pred_new_link <- predict(
    map,
    data.frame(country = "CH", study = 11),
    type = "link"
  )
  tau_est <- summary(map)$tau[, "mean"]
  expect_true(summary(pred_new_link)[, "sd"] > tau_est)
})

## whenever the same study/covariate combination is requested, then
## the MAP must be numerically exactly the same. This ensures that per
## study the random effect is sampled just once in each iteration.
test_that("predictive distributions for the same study & covariate must match exactly", {
  trans_cov_new <- data.frame(
    study = "new",
    n = 50,
    r = 0,
    country = levels(trans_cov$country)[c(1, 1)]
  )
  post_trans <- as.matrix(predict(map, newdata = trans_cov_new))
  expect_equal(post_trans[, 1], post_trans[, 2])
})

test_that("automixfit attempts K=4 different models and returns best fitting", {
  auto_map <- automixfit(map, Nc = 1:4, k = 6)
  models <- attr(auto_map, "models")
  expect_equal(length(models), 4)
  perf <- sapply(models, AIC, k = 6)
  ## ensure that performance is decreasing
  expect_true(all(diff(perf) > 0))
  expect_true("betaMix" %in% class(auto_map))
})


test_that("mixfit for prediction handles response and link scale", {
  pred_map <- mixfit(pred_new, Nc = 2)

  expect_true(is.list(pred_map))
  expect_true("betaMix" %in% class(pred_map[[1]]))
  expect_equal(ncol(pred_map[[1]]), 2)

  pred_new_link <- predict(
    map,
    data.frame(country = "CH", study = 11),
    type = "link"
  )
  pred_map_link <- mixfit(pred_new_link, Nc = 2)

  expect_true(is.list(pred_map_link))
  expect_true("normMix" %in% class(pred_map_link[[1]]))
  expect_equal(ncol(pred_map_link[[1]]), 2)
})


source_example("mixcombine.R")

test_that("combination of mixtures", {
  m1 <- mixcombine(bm, unif, weight = c(9, 1))
  m2 <- mixcombine(bm, unif, unif, weight = c(8, 1, 1))
  expect_equal(m1[1, ], c(bm[1, ] - 0.1 / 2, 0.1), ignore_attr = TRUE)
  expect_equal(m1[2:3, 1:2], bm[2:3, 1:2], ignore_attr = TRUE)
  expect_equal(m2[2:3, 1:2], bm[2:3, 1:2], ignore_attr = TRUE)
})

test_that("throws an error if more weights than mixtures given", {
  ## giving 3 weights but only 2 mixtures must not work
  expect_error(
    mixcombine(bm, unif, weight = c(8, 1, 1)),
    "length(weight) not equal to length(comp)",
    fixed = TRUE
  )
})

test_that("combination of normal mixtures without default sigma works", {
  norm_ui <- mixnorm(c(1, 0, 2))
  norm_ui_mix <- mixcombine(norm_ui, norm_ui, weight = c(0.5, 0.5))
  expect_true(ncol(norm_ui_mix) == 2)
})

source_example("robustify.R")

test_that("beta mixture is robustified with Beta(1,1)", {
  expect_equal(ncol(bmix) + 1, ncol(rbmix))
  expect_equal(rbmix[, ncol(rbmix)], c(0.1, 1, 1), ignore_attr = TRUE)
})

test_that("beta mixture is robustified with Beta(0.5,0.5)", {
  rbmix2 <- robustify(bmix, w = 0.1, n = 0, mean = 0.5)
  expect_equal(ncol(bmix) + 1, ncol(rbmix2))
  expect_equal(rbmix2[, ncol(rbmix2)], c(0.1, 0.5, 0.5), ignore_attr = TRUE)
})

test_that("gamma mixture is robustified with n=1 equivalent prior", {
  m <- summary(gmnMix)["mean"]
  nr <- ncol(rgmnMix)
  expect_equal(
    rgmnMix[[nr, rescale = TRUE]],
    mixgamma(c(1, m, 1), param = "mn"),
    ignore_attr = TRUE
  )
  expect_equal(rgmnMix[1, nr], 0.1)
})

test_that("gamma mixture is robustified with n=5 equivalent prior", {
  m <- summary(gmnMix)["mean"]
  rgmnMix2 <- robustify(gmnMix, w = 0.1, n = 5, mean = 2)
  nr <- ncol(rgmnMix2)
  expect_equal(
    rgmnMix2[[nr, rescale = TRUE]],
    mixgamma(c(1, m, 5), param = "mn"),
    ignore_attr = TRUE
  )
  expect_equal(rgmnMix2[1, nr], 0.1)
})

test_that("normal mixture is robustified with n=1 equivalent prior", {
  nr <- ncol(rnMix)
  expect_equal(
    rnMix[[nr, rescale = TRUE]],
    mixnorm(c(1, 0, 1), param = "mn", sigma = sigma(nm)),
    ignore_attr = TRUE
  )
  expect_equal(rnMix[1, nr], 0.1)
})

test_that("normal mixture is robustified with n=5 equivalent prior", {
  rnMix2 <- robustify(nm, w = 0.1, mean = 0, n = 5, sigma = sigma(nm))
  nr <- ncol(rnMix2)
  expect_equal(
    rnMix2[[nr, rescale = TRUE]],
    mixnorm(c(1, 0, 5), param = "mn", sigma = sigma(nm)),
    ignore_attr = TRUE
  )
  expect_equal(rnMix2[1, nr], 0.1)
})

test_that("plotting of normal mixtures without default sigma works", {
  norm_ui <- mixnorm(c(1, 0, 2))
  norm_mix_ui <- mixcombine(norm_ui, norm_ui, weight = c(0.5, 0.5))
  pl <- plot(norm_mix_ui)
  expect_true(inherits(pl, "ggplot"))
})

source_example("ess.R")

test_that("conjugate beta case matches canonical formula", {
  expect_equal(a + b, ess(prior, "moment"))
  expect_equal(a + b, round(ess(prior, "morita")))
  expect_equal(a + b, ess(prior, "elir"))
})

test_that("ess elir for beta mixtures gives a warning for a<1 & b<1 densities", {
  unconstrain1 <- mixbeta(c(0.95, 10, 5), c(0.05, 0.9, 2))
  unconstrain2 <- mixbeta(c(0.95, 10, 5), c(0.05, 2, 0.9))

  expect_error(
    ess(unconstrain1, "elir"),
    "At least one parameter of the beta mixtures is less than 1"
  )
  expect_error(
    ess(unconstrain2, "elir"),
    "At least one parameter of the beta mixtures is less than 1"
  )

  ## this one can trigger errors if the integration is not setup properly
  constrained <- mixbeta(c(0.48, 1, 11), c(0.34, 6.9, 173), c(0.18, 1.0, 1.13))
  expect_numeric(
    ess(constrained, "elir"),
    lower = 0,
    finite = TRUE,
    any.missing = FALSE,
    len = 1
  )
})

test_that("conjugate normal case matches canonical formula", {
  s <- 2
  sigma_data <- 4
  nprior <- mixnorm(c(1, -1, s), sigma = sigma_data)
  nprior_ess <- sigma_data^2 / s^2
  expect_equal(ess(nprior, "moment", sigma = sigma_data), nprior_ess)
  expect_equal(ess(nprior, "morita", sigma = sigma_data, s = Inf), nprior_ess)
  expect_equal(ess(nprior, "elir", sigma = sigma_data), nprior_ess)
})

test_that("ess elir for normal mixtures returns correct values", {
  mix <- mixnorm(
    inf1 = c(0.5026, -191.1869, 127.4207),
    inf2 = c(0.2647, -187.5895, 31.6130),
    inf3 = c(0.2326, -184.7445, 345.3849),
    sigma = 270.4877
  )
  expect_gt(ess(mix, sigma = 270.4877), 0)
})

test_that("moment matching for beta mixtures is correct", {
  expect_equal(ess(bmix, method = "moment"), sum(ab_matched))
})

test_that("beta mix ess works when run through sapply", {
  expect_numeric(sapply(X = list(bmix), FUN = ess))
})

test_that("normal mixtures have reference scale used correctly", {
  nmix_sigma_small <- nmix
  nmix_sigma_large <- nmix
  sigma_large <- 2 * summary(nmix_sigma_large)["sd"]
  sigma(nmix_sigma_large) <- sigma_large
  sigma(nmix_sigma_small) <- sigma_large / sqrt(2)
  suppressMessages(e1m <- ess(nmix_sigma_large, "moment"))
  suppressMessages(e2m <- ess(nmix_sigma_small, "moment"))
  expect_gt(e1m, e2m)
  expect_equal(floor(abs(e2m - e1m / 2)), 0)

  suppressMessages(e1b <- ess(nmix_sigma_large, "morita"))
  suppressMessages(e2b <- ess(nmix_sigma_small, "morita"))
  expect_gt(e1b, e2b)
  expect_equal(floor(abs(e2b - e1b / 2)), 0)

  suppressMessages(e1r <- ess(nmix_sigma_large, "elir"))
  suppressMessages(e2r <- ess(nmix_sigma_small, "elir"))
  expect_gt(e1r, e2r)
  expect_equal(floor(abs(e2r - e1r / 2)), 0)
})

test_that("gamma mixtures have likelihood property respected", {
  gmix1 <- gmix
  likelihood(gmix1) <- "poisson"
  gmix2 <- gmix
  likelihood(gmix2) <- "exp"
  e1m <- ess(gmix1, "moment")
  e2m <- ess(gmix2, "moment")
  expect_true(e1m != e2m)

  e1b <- ess(gmix1, "morita")
  e2b <- ess(gmix2, "morita")
  expect_true(e1b != e2b)

  e1r <- ess(gmix1, "morita")
  e2r <- ess(gmix2, "morita")
  expect_true(e1r != e2r)
})

test_that("gamma mix ess works when run through sapply", {
  expect_numeric(sapply(X = list(gmix), FUN = ess))
})

test_that("gamma 1-component density gives canonical results", {
  guni1 <- gmix[[1, rescale = TRUE]]
  likelihood(guni1) <- "poisson"
  guni2 <- gmix[[1, rescale = TRUE]]
  likelihood(guni2) <- "exp"

  e1m <- ess(guni1, "moment")
  e2m <- ess(guni2, "moment")
  expect_true(e1m != e2m)
  expect_equal(guni1[3, 1], e1m)
  expect_equal(guni2[2, 1], e2m)

  e1b <- round(ess(guni1, "morita"))
  e2b <- round(ess(guni2, "morita"))
  expect_true(e1b != e2b)
  expect_equal(guni1[3, 1], e1b)
  expect_equal(guni2[2, 1], e2b)

  e1r <- ess(guni1, "elir")
  e2r <- ess(guni2, "elir")
  expect_true(e1r != e2r)
  expect_true(abs(guni1[3, 1] - e1r) < 1E-4)
  ## ELIR gives a-1 as ESS
  expect_true(abs(guni2[2, 1] - (e2r + 1)) < 1E-4)
})

## check predictive consistency of ELIR
elir_predictive_consistent <- function(dens, m, Nsim, seed, stat, ...) {
  ## simulated from predictve which is m events equivalent to
  suppressMessages(pdens <- preddist(dens, n = m))
  set.seed(seed)
  psamp <- rmix(pdens, Nsim)

  if (inherits(dens, "gammaMix")) {
    psamp <- psamp / m
  }

  posterior_ess <- function(mix, method, stat, ...) {
    args <- c(list(priormix = mix, stat = 0), list(...))
    names(args)[2] <- stat
    fn <- function(x) {
      args[[stat]] <- x
      suppressMessages(res <- ess(do.call(postmix, args), method = method))
      res
    }
    Vectorize(fn)
  }

  ## obtain ess of each posterior
  pred_ess <- posterior_ess(dens, "elir", stat, ...)
  ess_psamp <- pred_ess(psamp)

  suppressMessages(elir_prior <- ess(dens, "elir"))
  ## the average over the predicitve of the posterior ESS must match
  ## the the elir value taken directly (when m is subtracted, of
  ## course)
  elir_pred <- mean(ess_psamp) - m

  expect_true(abs(elir_prior - elir_pred) < 0.75)
}


test_that("ESS elir is predictively consistent for normal mixtures", {
  skip_on_cran()
  nmix <- mixnorm(rob = c(0.5, 0, 2), inf = c(0.5, 3, 4), sigma = 10)
  elir_predictive_consistent(
    nmix,
    m = 3E2,
    Nsim = 1E3,
    seed = 3435,
    stat = "m",
    se = 10 / sqrt(3E2)
  )
})

test_that("ESS elir is predictively consistent for beta mixtures", {
  skip_on_cran()
  bmix <- mixbeta(rob = c(0.2, 1, 1), inf = c(0.8, 10, 2))
  elir_predictive_consistent(
    bmix,
    m = 1E2,
    Nsim = 1E3,
    seed = 355435,
    stat = "r",
    n = 1E2
  )
})

test_that("ESS elir is predictively consistent for gamma mixtures (Poisson likelihood)", {
  skip_on_cran()
  gmixP <- mixgamma(
    rob = c(0.3, 20, 4),
    inf = c(0.7, 50, 10),
    likelihood = "poisson"
  )
  elir_predictive_consistent(
    gmixP,
    m = 1E2,
    Nsim = 1E3,
    seed = 355435,
    stat = "m",
    n = 1E2
  )
})

test_that("ess elir for problematic beta mixtures gives correct result 1", {
  ## by user reported beta mixture density which triggers this erros
  ## with RBesT 1.7.2 & 1.7.3 (others not tested):
  ## Error in if (all(dgl < 0) || all(dgl > 0)) { :
  ##   missing value where TRUE/FALSE needed

  mixmat <- matrix(
    c(
      0.06429517,
      0.03301215,
      0.00269268,
      0.90000000,
      437.32302999,
      64.04211307,
      5.92543558,
      1.00000000,
      10.71709277,
      2.14157953,
      1.00000001,
      1.00000000
    ),
    byrow = TRUE,
    ncol = 4
  )

  mixb <- do.call(mixbeta, apply(mixmat, 2, c, simplify = FALSE))

  expect_double(
    ess(mixb),
    lower = 0,
    finite = TRUE,
    any.missing = FALSE,
    len = 1
  )
})

test_that("ess elir for problematic beta mixtures gives correct result 2", {
  mixmat <- matrix(
    c(
      0.7237396,
      0.1665037,
      0.1097567,
      53.3721902,
      44.3894573,
      9.8097062,
      1.4301638,
      4.3842200,
      1.8492197
    ),
    byrow = TRUE,
    ncol = 3
  )

  mixb <- do.call(mixbeta, apply(mixmat, 2, c, simplify = FALSE))

  expect_double(
    ess(robustify(mixb, 0.05, 0.5)),
    lower = 0,
    finite = TRUE,
    any.missing = FALSE,
    len = 1
  )
  expect_double(
    ess(robustify(mixb, 0.95, 0.5)),
    lower = 0,
    finite = TRUE,
    any.missing = FALSE,
    len = 1
  )
})


test_that("ess elir for problematic beta mixtures gives warning", {
  mixmat1 <- matrix(
    c(
      0.6092774,
      0.2337629,
      0.1569597,
      1.0000000,
      1.2672179,
      3.3856153,
      11.8465288,
      1.2389927,
      7.0191159
    ),
    byrow = TRUE,
    ncol = 3
  )

  mixb1 <- do.call(mixbeta, apply(mixmat1, 2, c, simplify = FALSE))

  ## in case one of the coefficients of a and b is 1, then we can
  ## get negative results... which are unreliable to the user hopefully
  expect_warning(ess(mixb1))
  expect_double(
    suppressWarnings(ess(mixb1)),
    finite = TRUE,
    any.missing = FALSE,
    len = 1
  )

  mixmat2 <- matrix(
    c(
      0.6051804,
      0.2324492,
      0.1623704,
      1.0210697,
      1.1955047,
      3.1342298,
      11.5485831,
      1.0831573,
      6.7636286
    ),
    byrow = TRUE,
    ncol = 3
  )

  mixb2 <- do.call(mixbeta, apply(mixmat2, 2, c, simplify = FALSE))

  expect_double(
    ess(mixb2),
    lower = 0,
    finite = TRUE,
    any.missing = FALSE,
    len = 1
  )
})

test_that("BinaryExactCI has correct boundary behavior", {
  expect_equal(unname(BinaryExactCI(0, 10, 0.05)[1]), 0)
  expect_equal(unname(BinaryExactCI(10, 10, 0.05)[2]), 1)
})


test_that("ess for a normal density with binomial family under a logit link gives correct results", {
  ## the ess elir for a normal density prior with mean m and
  ## standard deviation s given to a logit transformed response rate
  ## is: i(p(eta)) = 1/s^2 and i_F(eta) = exp(eta) / (1 +
  ## exp(eta))^2 => r(eta) = i(p(eta)) / i_F(eta) the ess ELIR
  ## integral then involves terms as integral exp(eta) p(eta|m,s)
  ## d(eta) = exp(m + s^2/2) and integral exp(-eta) p(eta|m,s) d(eta)
  ## = exp(-m + s^2/2). The analyical result is then
  ## ess_elir = 1/s^2 * [ 2 + exp(-m + s^/2) + exp(m + s^/2) ]

  ## since the information of a normal is just 1/s^2 and thus a
  ## constant, the moment based approach gives the same result as
  ## the elir method. The morita method differs though as it
  ## evaluates at the mode of the prior.

  ess_elir_binomial_logit <- function(m, s) {
    s2 <- s * s
    (2 + exp(-m + s2 / 2) + exp(m + s2 / 2)) / s2
  }

  fisher_binomial_logit <- function(l) {
    exp(l) / (1 + exp(l))^2
  }

  ## Pennello and Thomson ESS for a binomial logit case corresponds
  ## to the Morita ESS whenever the scale s for the flattened prior
  ## is Infinity
  pe_ess_binomial_logit <- function(m, s) {
    1 / s^2 / fisher_binomial_logit(m)
  }

  m1 <- 0
  s1 <- 2 / sqrt(10)
  prior_norm1 <- mixnorm(c(1, m1, s1), sigma = 2)
  expect_equal(
    ess(prior_norm1, "elir", family = binomial, sigma = 2),
    ess_elir_binomial_logit(m1, s1),
    tolerance = 1E-4
  )
  expect_equal(
    ess(prior_norm1, "moment", family = binomial, sigma = 2),
    ess_elir_binomial_logit(m1, s1),
    tolerance = 1E-4
  )
  expect_equal(
    ess(prior_norm1, "morita", family = binomial, sigma = 2, s = Inf),
    pe_ess_binomial_logit(m1, s1),
    tolerance = 1E-4
  )

  m2 <- 2
  s2 <- 2 / sqrt(100)
  prior_norm2a <- mixnorm(c(1, m2, s2), sigma = 2)
  expect_equal(
    ess(prior_norm2a, "elir", family = binomial, sigma = 2),
    ess_elir_binomial_logit(m2, s2),
    tolerance = 1E-4
  )
  expect_equal(
    ess(prior_norm2a, "moment", family = binomial, sigma = 2),
    ess_elir_binomial_logit(m2, s2),
    tolerance = 1E-4
  )
  expect_equal(
    ess(prior_norm2a, "morita", family = binomial, sigma = 2, s = Inf),
    pe_ess_binomial_logit(m2, s2),
    tolerance = 1E-4
  )

  ## sigma does not play a role here
  prior_norm2b <- mixnorm(c(1, m2, s2), sigma = 4)
  expect_equal(
    ess(prior_norm2b, "elir", family = binomial, sigma = 4),
    ess_elir_binomial_logit(m2, s2),
    tolerance = 1E-4
  )
  expect_equal(
    ess(prior_norm2b, "moment", family = binomial, sigma = 4),
    ess_elir_binomial_logit(m2, s2),
    tolerance = 1E-4
  )
  expect_equal(
    ess(prior_norm2b, "morita", family = binomial, sigma = 4, s = Inf),
    pe_ess_binomial_logit(m2, s2),
    tolerance = 1E-4
  )
})


test_that("ess for a normal density with poisson family under a log link gives correct results", {
  ## the ess elir for a normal density prior with mean m and
  ## standard deviation s given to a log transformed count rate
  ## is: i(p(eta)) = 1/s^2 and i_F(eta) = exp(eta)  => r(eta) = i(p(eta)) / i_F(eta) the ess ELIR
  ## integral then involves terms as integral exp(-eta) p(eta|m,s)
  ## d(eta) = exp(-m + s^2/2). The analyical result is then
  ## ess_elir = 1/s^2 * exp(-m + s^/2)

  ## since the information of a normal is just 1/s^2 and thus a
  ## constant, the moment based approach gives the same result as
  ## the elir method. The morita method differs though as it
  ## evaluates at the mode of the prior.

  ess_elir_poisson_log <- function(m, s) {
    s2 <- s * s
    exp(-m + s2 / 2) / s2
  }

  fisher_poisson_log <- function(l) {
    exp(l)
  }

  ## Pennello and Thomson ESS for a binomial logit case corresponds
  ## to the Morita ESS whenever the scale s for the flattened prior
  ## is Infinity
  pe_ess_poisson_log <- function(m, s) {
    1 / s^2 / fisher_poisson_log(m)
  }

  m1 <- 0
  s1 <- 2 / sqrt(10)
  prior_norm1 <- mixnorm(c(1, m1, s1), sigma = 2)
  expect_equal(
    ess(prior_norm1, "elir", family = poisson, sigma = 2),
    ess_elir_poisson_log(m1, s1),
    tolerance = 1E-4
  )
  expect_equal(
    ess(prior_norm1, "moment", family = poisson, sigma = 2),
    ess_elir_poisson_log(m1, s1),
    tolerance = 1E-4
  )
  expect_equal(
    ess(prior_norm1, "morita", family = poisson, sigma = 2, s = Inf),
    pe_ess_poisson_log(m1, s1),
    tolerance = 1E-4
  )

  m2 <- 2
  s2 <- 2 / sqrt(100)
  prior_norm2a <- mixnorm(c(1, m2, s2), sigma = 2)
  expect_equal(
    ess(prior_norm2a, "elir", family = poisson, sigma = 2),
    ess_elir_poisson_log(m2, s2),
    tolerance = 1E-4
  )
  expect_equal(
    ess(prior_norm2a, "moment", family = poisson, sigma = 2),
    ess_elir_poisson_log(m2, s2),
    tolerance = 1E-4
  )
  expect_equal(
    ess(prior_norm2a, "morita", family = poisson, sigma = 2, s = Inf),
    pe_ess_poisson_log(m2, s2),
    tolerance = 1E-4
  )

  ## sigma does not play a role here
  prior_norm2b <- mixnorm(c(1, m2, s2), sigma = 4)
  expect_equal(
    ess(prior_norm2b, "elir", family = poisson, sigma = 4),
    ess_elir_poisson_log(m2, s2),
    tolerance = 1E-4
  )
  expect_equal(
    ess(prior_norm2b, "moment", family = poisson, sigma = 4),
    ess_elir_poisson_log(m2, s2),
    tolerance = 1E-4
  )
  expect_equal(
    ess(prior_norm2b, "morita", family = poisson, sigma = 4, s = Inf),
    pe_ess_poisson_log(m2, s2),
    tolerance = 1E-4
  )
})

test_that("ess for a beta mixture errors if a family is specified", {
  expect_error(ess(mixbeta(c(1, 5, 15)), family = binomial))
})

test_that("ess for a gamma mixture errors if a family is specified", {
  expect_error(ess(
    mixgamma(rob = c(0.3, 20, 4), inf = c(0.7, 50, 10)),
    family = poisson
  ))
})
