context("check minimize()")



# preliminaries
order <- 5L

null        <- PointMassPrior(.0, 1)
alternative <- PointMassPrior(.4, 1)
datadist    <- Normal(two_armed = FALSE)

ess   <- ExpectedSampleSize(datadist, alternative)
ess_0 <- ExpectedSampleSize(datadist, null)
cp    <- ConditionalPower(datadist, alternative)
pow   <- expected(cp, datadist, alternative)
toer  <- Power(datadist, null)

alpha <- 0.05
beta  <- 0.2

opt_os <- minimize(
    ess,
    subject_to(
        pow  >= 1 - beta,
        toer <= alpha
    ),
    initial_design = get_initial_design(.4, alpha, beta, "one-stage", dist = datadist, order = order),
    opts         =  list(
      algorithm   = "NLOPT_LN_COBYLA",
      xtol_rel    = 1e-7,
      maxeval     = 1e6
    )
)

opt_gs <- minimize(
    ess,
    subject_to(
        pow  >= 1 - beta,
        toer <= alpha
    ),
    initial_design = get_initial_design(.4, alpha, beta, "group-sequential", dist = datadist, order = order)
)

initial_design <- get_initial_design(.4, alpha, beta, "two-stage", dist = datadist, order = order)

opt_ts <- minimize(
    ess,
    subject_to(
        pow  >= 1 - beta,
        toer <= alpha
    ),
    initial_design
)



test_that("nloptr maxiter warning correctly passed", {

    expect_warning(

        minimize(
            ess,
            subject_to(
                pow  >= 1 - beta,
                toer <= alpha
            ),
            initial_design,
            opts = list(
                algorithm   = "NLOPT_LN_COBYLA",
                xtol_rel    = 1e-5,
                maxeval     = 10
            )
        )
    )

})



test_that("nloptr invalid initial values error works", {

    lb_design    <- update(initial_design, c(5, -1, 2, numeric(order), numeric(order) - 3))
    lb_design@n1 <- initial_design@n1 + 1

    expect_error(

        minimize(
            ess,
            subject_to(
                pow  >= 1 - beta,
                toer <= alpha
            ),
            initial_design,
            lower_boundary_design = lb_design,
            opts = list(
                algorithm   = "NLOPT_LN_COBYLA",
                xtol_rel    = 1e-5,
                maxeval     = 10
            )
        )

    )

})


test_that("Optimal one-stage design can be computed", {

    expect_equal(
        opt_os$design@c1f,
        qnorm(1 - alpha),
        tolerance = sqrt(.Machine$double.eps), scale = 1)

    expect_equal(
        opt_os$design@n1,
        ((qnorm(1 - beta) + qnorm(1 - alpha)) / 0.4)^2,
        tolerance = sqrt(.Machine$double.eps), scale = 1)

}) # end 'optimal one-stage design can be computed'



test_that("Optimal group-sequential design is computable", {

    expect_equal(
        round(evaluate(pow, opt_gs$design), 1),
        0.8,
        tolerance = sqrt(.Machine$double.eps), scale = 1)

    expect_equal(
        round(evaluate(toer, opt_gs$design), 2),
        0.05,
        tolerance = sqrt(.Machine$double.eps), scale = 1)


    # Check if n2 is equal at boundaries
    expect_equal(
        n2(opt_gs$design, opt_gs$design@c1f),
        n2(opt_gs$design, opt_gs$design@c1e))

    expect_equal(
        opt_gs$nloptr_return$solution[1],
        opt_gs$design@n1)

}) # end 'optimal group-sequential design is computable'



test_that("Optimal group-sequential design is superior to standard gs design", {

    # Create design from rpact
    design_rp <- rpact::getDesignInverseNormal(
        kMax = 2,
        alpha = alpha,
        beta = beta,
        futilityBounds = 0,
        typeOfDesign = "P")
    res <- rpact::getSampleSizeMeans(
        design_rp, normalApproximation = TRUE, alternative = .4 * sqrt(2))
    c2_fun <- function(z){
        w1 <- 1 / sqrt(2)
        w2 <- sqrt(1 - w1^2)
        out <- (design_rp$criticalValues[2] - w1 * z) / w2
        return(out)
    }
    c1f <- qnorm(
        rpact::getDesignCharacteristics(design_rp)$futilityProbabilities
    ) + sqrt(res$numberOfSubjects1[1]) * .4
    rpact_design <- GroupSequentialDesign(
            ceiling(res$numberOfSubjects1[1,]),
        c1f,
        design_rp$criticalValues[1],
        ceiling(res$numberOfSubjects1[2,]),
        rep(2.0, 100),
        100L
    )
    rpact_design@c2_pivots <- sapply(adoptr:::scaled_integration_pivots(rpact_design), c2_fun)

    # use opt_gs from above
    testthat::expect_lte(
        evaluate(ess, opt_gs$design),
        evaluate(ess, rpact_design))

})



test_that("base-case satisfies constraints", {

    # compute summaries
    out <- summary(opt_ts$design, "power" = pow, "toer" = toer, "CP" = cp, rounded = FALSE)

    out2 <- summary(opt_ts$design, "power" = pow, "toer" = toer, rounded = FALSE)

    out3 <- summary(opt_ts$design,  "CP" = cp, rounded = FALSE)

    out4 <- summary(opt_ts$design, rounded = TRUE)

    expect_equal(
        as.numeric(out$uncond_scores["power"]),
        0.8,
        tolerance = 1e-3, scale = 1)

    expect_equal(
        as.numeric(out$uncond_scores["power"]),
        as.numeric(out2$uncond_scores["power"]),
        tolerance = 1e-3, scale = 1)

    expect_equal(
        as.numeric(out$uncond_scores["toer"]),
        0.05,
        tolerance = 1e-3, scale = 1)


    expect_equal(
        as.numeric(out$uncond_scores["toer"]),
        as.numeric(out2$uncond_scores["toer"]),
        tolerance = 1e-3, scale = 1)


}) # end base-case respects constraints



test_that("base-case results are consistent - no post processing", {

    # optimal two-stage design better than optimal group-sequential design
    expect_lt(
        evaluate(ess, opt_ts$design),
        evaluate(ess, opt_gs$design))

    # optimal group-sequential design better than optimal one-stage design
    expect_lt(
        evaluate(ess, opt_gs$design),
        evaluate(ess, opt_os$design))


    # simulate on boundary of null
    sim_null <- simulate(
        opt_ts$design, nsim = 10^6, dist = datadist, theta = .0, seed = 54)

    # check type one error rate on boundary of null
    expect_equal(
        mean(sim_null$reject),
        alpha,
        tolerance = 1e-3, scale = 1)

    # expected sample size on boundary of null
    expect_equal(
        mean(sim_null$n2 + sim_null$n1),
        evaluate(ess_0, opt_ts$design),
        tolerance = 1e-2, scale = 1)

    # simulate under alternative
    sim_alt  <- simulate(
        opt_ts$design, nsim = 10^6, dist = datadist, theta = .4, seed = 54)

    # check power constraint
    expect_equal(
        mean(sim_alt$reject),
        1 - beta,
        tolerance = 1e-2, scale = 1)

    # check expected sample size under alternative
    expect_equal(
        mean(sim_alt$n2 + sim_alt$n1),
        evaluate(ess, opt_ts$design),
        tolerance = .5, scale = 1)

    # maximum sample size of adaptive design is larger than of one-stage design
    mss <- MaximumSampleSize()
    expect_lte(
        evaluate(mss, opt_os$design),
        evaluate(mss, opt_ts$design)
    )

}) # end 'base-case results are consistent'




test_that("conditional constraints work", {

    opt_ts <- suppressWarnings(

        minimize( # ignore: initial design is infeasible
            ess,
            subject_to(
                pow  >= 1 - beta,
                toer <= alpha,
                cp   >= 0.75,
                cp   <= 0.95
            ),
            initial_design,
            opts = list(
                algorithm   = "NLOPT_LN_COBYLA",
                xtol_rel    = 1e-4,
                maxeval     = 10000
            )
        )
    )

    tol <- .005
    # check lower boundary on conditional power
    expect_gte(
        evaluate(cp, opt_ts$design, opt_ts$design@c1f),
        .75 - tol)

    # check lower boundary on conditional power
    expect_lte(
        evaluate(cp, opt_ts$design, opt_ts$design@c1e),
        .95 + tol)

    # test that c2 is monotonously increasing
    expect_true(all(
        sign(diff(opt_ts$design@c2_pivots)) == -1))

}) # end 'conditional constraints work'



test_that("conditional constraints work", {

    expect_equal(
        capture.output(print(opt_os)),
        "OneStageDesign<optimized;n=39;c=1.64> "
    )


}) # end 'conditional constraints work'


test_that("constraint checks are working", {
  init <- TwoStageDesign(25, 0.2, 2.5,
                         c(80, 77, 70, 61, 50, 36, 25),
                         c(2.2, 2.1, 1.8, 1.4, 0.9, 0.3, -0.1), order = 7L)
  
  expect_warning(
    opt_design <- minimize(ess, subject_to(toer <= 0.01, pow >= 0.95, cp >= 0.95,
                                           MaximumSampleSize() <= 70),
                           initial_design = init, check_constraints = TRUE,
                           opts = list(algorithm = "NLOPT_LN_COBYLA", xtol_rel = 1e-05, maxeval = 1))
    
  )
  
  expect_warning(
    opt_design <- minimize(ess,subject_to(toer <= 0.025, pow >= 0.8, MaximumSampleSize() <= pow,
                                          cp >= ConditionalSampleSize()),
                           initial_design = init, check_constraints = TRUE,
                           opts = list(algorithm = "NLOPT_LN_COBYLA", xtol_rel = 1e-05, maxeval = 1))
  )
}) # end 'constraint checks are working'


test_that("c2_decreasing is respected", {
  opt_gs_2 <- minimize(
    ess,
    subject_to(
      pow  >= 1 - beta,
      toer <= alpha
    ),
    initial_design = get_initial_design(.4, alpha, beta, "group-sequential", dist = datadist, order = 3L),
    c2_decreasing = TRUE
  )$design
  
  expect_true(all(diff(opt_gs_2@c2_pivots) <= 0))
  
}) # end 'c2_decreasing is respected'


test_that("initial design works", {
  
  # test that errors are thrown when giving false input
  expect_error(
    get_initial_design(.4, .025, .2, "adaptive", dist = Normal(), order = 6L)
  )
  
  expect_error(
    get_initial_design(.4, 1.025, .2, "two-stage", dist = Normal(), order = 6L)
  )
  
  expect_error(
    get_initial_design(0.4, 0.025, 0.2, dist = Survival(0.8))
  )
  
  expect_error(
    get_initial_design(-.4, 0.025, .2, "two-stage", dist = Normal(), order = 6L)
  )
  
  # test that correct types of designs are returned under different scenarios
  expect_true(
    is(get_initial_design(.4, .025, .2, "two-stage", dist = Normal(F), order = 6L), "TwoStageDesign")
  )
  
  expect_true(
    is(get_initial_design(0.2, .025, .2, "two-stage", type_n2 = "linear_decreasing", 
                          dist = Binomial(0.4), order = 6L, slope = -10),
       "TwoStageDesign")
  )
  
  expect_true(
    is(get_initial_design(0.2, .025, .2, "two-stage", dist = Binomial(0.7), order = 6L),
       "TwoStageDesign")
  )
  
  expect_true(
    is(get_initial_design(0.2, .025, .2, "two-stage", type_n2 = "linear_increasing", 
                          dist = Binomial(0.4), order = 6L, slope = 10),
       "TwoStageDesign")
  )
  
  expect_true(
    is(get_initial_design(.4, .025, .2, "group-sequential", dist = Normal(), order = 6L), 
       "GroupSequentialDesign")
  )
  
  expect_true(
    is(get_initial_design(.4, .025, .2, "group-sequential", dist=Normal(), type_c2 = "constant",
                          order = 6L), "GroupSequentialDesign")
  )
  
  expect_true(
    is(get_initial_design(.4, .025, .2, "one-stage", dist = Normal(), order = 6L),
       "OneStageDesign")
  )
  
  expect_true(
    is(get_initial_design(0.04, 0.05, 0.2, "group-sequential", dist = ANOVA(2)),
       "GroupSequentialDesign")
  )
  
  expect_true(
    is(get_initial_design(1.4, .025, .2, "two-stage", type_n2 = "linear_decreasing", 
                          dist = Survival(0.7), order = 6L, slope = -10),
       "TwoStageDesignSurvival")
  )
  
  expect_true(
    is(get_initial_design(1.4, .025, .2, "two-stage", dist = Survival(0.7), order = 6L),
       "TwoStageDesignSurvival")
  )
  
  expect_true(
    is(get_initial_design(1.4, .025, .2, "two-stage", type_n2 = "linear_increasing", 
                          dist = Survival(0.7), order = 6L, slope = 10),
       "TwoStageDesignSurvival")
  )
  
  expect_true(
    is(get_initial_design(1.4, .025, .2, "group-sequential", dist = Survival(0.7), order = 6L),
       "GroupSequentialDesignSurvival")
  )
  
  expect_true(
    is(get_initial_design(1.4, .025, .2, "one-stage", dist = Survival(0.7)),
       "OneStageDesignSurvival")
  )
  
  expect_error(
    get_initial_design(0.4, 0.2, 0.8, ce = 1, cf = 2)
  )
  
  
  # compute example initial design and check its properties
  init <- get_initial_design(.4, .025, .2, "two-stage", dist = Normal(F), cf = 0.5,
                             type_c2 = "constant", ce = 2.5)
  
  #check values futility boundary
  expect_equal(
    init@c1f, 0.5
  )
  
  # check that c2 is constant
  expect_true(
    all(init@c2_pivots - init@c2_pivots[1] == rep(0, 7))
  )
  
  #check efficacy boundary
  expect_equal(
    init@c1e, 2.5
  )
  
  init2 <- get_initial_design(.4, .025, .2, "two-stage", dist = Normal(T),
                              type_n2 = "linear_increasing", slope = 20)
  
  #check that n2 is increasing
  expect_false(
    is.unsorted(init2@n2_pivots)
  )
  
  init2new <- get_initial_design(.4, .025, .2, "two-stage", dist = Normal(T),
                                 type_n2 = "linear_increasing", info_ratio = 0.3)
  
  #check that n2 is increasing
  expect_false(
    is.unsorted(init2new@n2_pivots)
  )
  #check the slope
  expect_equal(
    n2(init2, 1.5) - n2(init2, 0.5), 20
  )
  
  
  init3 <- get_initial_design(.4, .025, .2, "two-stage", dist = Normal(T),
                              type_c2 = "constant", info_ratio = 0.2, ce = 2)
  #check information ratio
  expect_equal(
    4 * rep(init3@n1, 7), init3@n2_pivots
  )
  
  #check that student distribution uses normal distribution
  init4 <- get_initial_design(0.4, 0.025, 0.1, "one-stage", dist = Student())
  expect_equal(
    init4@c1e,
    1.96,
    tolerance = 1e-3
  )
  
  #check warnings are raised when unnecessarily specifying n2 or c2 type
  expect_warning(
    get_initial_design(0.4, 0.025, 0.2, "one-stage", type_c2 = "constant")
  )
  expect_warning(
    get_initial_design(0.4, 0.025, 0.2, "one-stage", type_n2 = "constant")
  )
  
  #check warning when ce is chosen too small
  expect_warning(
    get_initial_design(0.4, 0.025, 0.2, "two-stage", type_c2 = "constant", ce = 1)
  )
  expect_warning(
    get_initial_design(0.4, 0.025, 0.2, "two-stage", type_c2 = "linear_decreasing", ce = 1)
  )
  
  #check that ce can be chosen
  expect_equal(
    get_initial_design(0.3, 0.025, 0.2, type_c2 = "linear_decreasing", ce = 3)@c1e,
    3
  )
  
  init5 <- get_initial_design(.4, .025, .2, "two-stage", dist = Normal(F), cf = 0.5, type_n2 = "constant",
                              type_c2 = "constant", ce = 2.5)
  
  #check that c2 is constant
  expect_true(
    all(init@c2_pivots-init@c2_pivots[1] == rep(0,7))
  )
  
  #check that c2 is linear decreasing
  init6 <- get_initial_design(0.4, .025, .2, "two-stage", dist = Normal(), 
                              cf = 0.5, type_n2 = "constant",
                              type_c2 = "linear_decreasing")
  
  expect_false(
    is.unsorted(rev(init6@c2_pivots))
  )
  
  #check linear decreasing n2 design
  init7 <- get_initial_design(0.4, .025, .2, "two-stage", cf = 0.5,
                              type_n2 = "linear_decreasing")
  
  expect_false(
    is.unsorted(rev(init7@n2_pivots))
  )
  
  expect_error(
    get_initial_design(0.4, .025, .2, "two-stage", cf = 0.5,
                       type_n2 = "linear_decreasing", slope = 10)
  )
  
  expect_error(
    get_initial_design(0.4, .025, .2, "two-stage", cf = 0.5,
                       type_n2 = "linear_increasing", slope = -10)
  )
  
  
  #check that slope is automatically adjusted
  expect_warning(
    get_initial_design(0.4, .025, .2, "two-stage", cf = 0.5,
                       type_n2 = "linear_decreasing",slope = -100))
  
  expect_warning(
    get_initial_design(0.4, .025, .2, "two-stage", cf = 0.5,
                       type_n2 = "linear_increasing",slope = 100))
  
  
  
}) # end 'initial design works'
