context("ampute")

# make objects for testfunctions
sigma <- matrix(data = c(1, 0.2, 0.2, 0.2, 1, 0.2, 0.2, 0.2, 1), nrow = 3)
complete.data <- MASS::mvrnorm(n = 100, mu = c(5, 5, 5), Sigma = sigma)

test_that("all examples work", {
  compl_boys <- cc(boys)[1:3]

  expect_error(ampute(data = compl_boys), NA)

  mads_boys <- ampute(data = compl_boys)

  my_patterns <- mads_boys$patterns
  my_patterns[1:3, 2] <- 0

  my_weights <- mads_boys$weights
  my_weights[2, 1] <- 2
  my_weights[3, 1] <- 0.5

  expect_error(ampute(
    data = compl_boys, patterns = my_patterns,
    freq = c(0.3, 0.3, 0.4), weights = my_weights,
    type = c("RIGHT", "TAIL", "LEFT")
  ), NA)
})

test_that("all arguments work", {
  set.seed(123)
  # empty run
  expect_error(ampute(data = complete.data, run = FALSE), NA)
  # missingness by cells
  expect_error(ampute(data = complete.data, prop = 0.1, bycases = FALSE), NA)
  # prop with 3 dec, weigths with negative values, unequal odds matrix
  expect_error(ampute(
    data = complete.data, prop = 0.314,
    freq = c(0.25, 0.4, 0.35),
    patterns = matrix(
      data = c(
        1, 0, 1,
        0, 1, 0,
        0, 1, 1
      ),
      nrow = 3, byrow = TRUE
    ),
    weights = matrix(
      data = c(
        -1, 1, 0,
        -4, -4, 1,
        0, 0, -1
      ),
      nrow = 3, byrow = TRUE
    ),
    odds = matrix(
      data = c(
        1, 4, NA, NA,
        0, 3, 3, NA,
        4, 1, 1, 4
      ),
      nrow = 3, byrow = TRUE
    ),
    cont = FALSE
  ), NA)
  # 1 pattern with vector for patterns and weights
  expect_error(ampute(
    data = complete.data, freq = 1, patterns = c(1, 0, 1),
    weights = c(3, 3, 0)
  ), NA)
  # multiple patterns given in vectors
  expect_error(ampute(
    data = complete.data, patterns = c(1, 0, 1, 1, 0, 0),
    cont = TRUE, weights = c(1, 4, -2, 0, 1, 2),
    type = c("LEFT", "TAIL")
  ), NA)
  # one pattern with odds vector
  expect_error(ampute(
    data = complete.data, patterns = c(1, 0, 1),
    weights = c(4, 1, 0), odds = c(2, 1), cont = FALSE
  ), NA)
  # argument standardized
  expect_error(ampute(data = complete.data, std = FALSE), NA)

  # sum scores cannot be NaN
  dich.data <- matrix(c(
    0, 0, 0, 1, 0, 0, 0, 0, 0,
    1, 0, 0, 0, 0, 0, 0, 0, 0
  ), ncol = 2, byrow = FALSE)
  wss <- expect_warning(ampute(data = dich.data, mech = "MNAR")$scores)
  check_na <- function(x) {
    return(any(is.na(x)))
  }
  expect_false(any(unlist(lapply(wss, check_na))))
})

test_that("function works around unusual arguments", {
  # data
  nasty.data <- complete.data
  nasty.data[, 1] <- rep(c("one", "two"), 50)
  # when data is categorical and mech != mcar, warning is expected
  expect_warning(
    ampute(data = nasty.data),
    "Data is made numeric because the calculation of weights requires numeric data"
  )
  # when data is categorical and mech = mcar, function can continue
  expect_warning(ampute(data = nasty.data, mech = "MCAR"), NA)

  # patterns
  expect_error(ampute(data = complete.data, patterns = c(0, 0, 0), mech = "MCAR"), NA)
  expect_error(ampute(data = complete.data, patterns = c(0, 0, 1, 0, 0, 0), mech = "MNAR"), NA)
  expect_warning(ampute(data = complete.data, patterns = c(1, 1, 1, 0, 1, 0)))

  # freq
  expect_warning(ampute(data = complete.data, freq = c(0.8, 0.4)))

  # prop
  expect_warning(ampute(data = complete.data, prop = 1))
  expect_error(ampute(data = complete.data, prop = 48.5), NA)

  # mech, type and weights
  expect_warning(
    ampute(data = complete.data, mech = c("MCAR", "MAR")),
    "Mechanism should contain merely MCAR, MAR or MNAR. First element is used"
  )
  expect_warning(
    ampute(data = complete.data, type = c("LEFT", "RIGHT")),
    "Type should either have length 1 or length equal to #patterns, first element is used for all patterns"
  )
  expect_warning(
    ampute(
      data = complete.data, mech = "MCAR",
      odds = matrix(
        data = c(
          1, 4, NA, NA,
          0, 3, 3, NA,
          4, 1, 1, 4
        ),
        nrow = 3, byrow = TRUE
      ), cont = FALSE
    ),
    "Odds matrix is not used when mechanism is MCAR"
  )
  expect_warning(
    ampute(
      data = complete.data, mech = "MCAR",
      weights = c(1, 3, 4)
    ),
    "Weights matrix is not used when mechanism is MCAR"
  )
  expect_warning(ampute(data = complete.data, odds = matrix(
    data = c(
      1, 4, NA, NA,
      0, 3, 3, NA,
      4, 1, 1, 4
    ),
    nrow = 3, byrow = TRUE
  )))
  expect_warning(ampute(data = complete.data, cont = FALSE, type = "LEFT"))
})

test_that("error messages work properly", {
  # data
  expect_error(
    ampute(data = as.list(complete.data)),
    "Data should be a matrix or data frame"
  )

  nasty.data <- complete.data
  nasty.data[1:10, 1] <- NA

  expect_error(ampute(data = nasty.data), "Data cannot contain NAs")
  expect_error(
    ampute(data = as.data.frame(complete.data[, 1])),
    "Data should contain at least two columns"
  )

  # prop
  expect_error(ampute(data = complete.data, prop = 104))
  expect_error(
    ampute(data = complete.data, prop = 0.9, bycases = FALSE),
    "Proportion of missing cells is too large in combination with the desired number of missing variables"
  )

  # patterns
  expect_error(
    ampute(data = complete.data, patterns = c(1, 1, 1)),
    "One pattern with merely ones results to no amputation at all, the procedure is therefore stopped"
  )
  expect_error(
    ampute(data = complete.data, patterns = c(0, 0, 0), mech = "MAR"),
    "Patterns object contains merely zeros and this kind of pattern is not possible when mechanism is MAR"
  )
  expect_error(
    ampute(data = complete.data, patterns = c(1, 0, 1, 1)),
    "Length of pattern vector does not match #variables"
  )
  expect_error(
    ampute(data = complete.data, patterns = c(1, 0, 2)),
    "Argument patterns can only contain 0 and 1, pattern 1 contains another element"
  )
  expect_error(
    ampute(data = complete.data, mech = "MAR", patterns = c(0, 0, 1, 0, 0, 0)),
    "Patterns object contains merely zeros and this kind of pattern is not possible when mechanism is MAR"
  )

  # mech, type, weights and odds
  expect_error(
    ampute(data = complete.data, mech = "MAAR"),
    "Mechanism should be either MCAR, MAR or MNAR"
  )
  expect_error(
    ampute(data = complete.data, type = "MARLEFT"),
    "Type should contain LEFT, MID, TAIL or RIGHT"
  )
  expect_error(
    ampute(data = complete.data, weights = c(1, 2, 1, 4)),
    "Length of weight vector does not match #variables"
  )
  expect_error(ampute(
    data = complete.data,
    odds = matrix(c(1, 4, -3, 2, 1, 1), nrow = 3),
    cont = FALSE
  ), "Odds matrix can only have positive values")
  expect_error(
    ampute(
      data = complete.data,
      patterns = matrix(
        data = c(
          1, 0, 1,
          0, 1, 0,
          0, 1, 1
        ),
        nrow = 3, byrow = TRUE
      ),
      weights = matrix(
        data = c(
          -1, 1, 0,
          -4, -4, 1,
          0, 0, -1,
          1, 1, 0
        ),
        nrow = 4, byrow = TRUE
      )
    ),
    "The objects patterns and weights are not matching"
  )
  expect_error(
    ampute(
      data = complete.data,
      patterns = matrix(
        data = c(
          1, 0, 1,
          0, 1, 0,
          0, 1, 1
        ),
        nrow = 3, byrow = TRUE
      ),
      odds = matrix(
        data = c(
          1, 4, NA, NA,
          0, 3, 3, 0
        ),
        nrow = 2, byrow = TRUE
      ), cont = FALSE
    ),
    "The objects patterns and odds are not matching"
  )
})

# The following tests were created to evaluate the patterns and weights matrices in case of a pattern with only 1's (#449)

test_that("patterns and weights matrices have right dimensions", {
  suppressWarnings(
    expect_true(all(
      ampute(data = complete.data, patterns = c(1, 1, 1, 0, 1, 0))$patterns == c(0, 1, 0)
    ))
  )

  suppressWarnings(
    expect_true(all(
      ampute(data = complete.data, patterns = c(0, 1, 0, 1, 1, 1))$patterns == c(0, 1, 0)
    ))
  )

  suppressWarnings(
    expect_true(all(
      ampute(data = complete.data, patterns = c(1, 1, 1, 0, 1, 0, 1, 1, 1))$patterns == c(0, 1, 0)
    ))
  )

  suppressWarnings(
    expect_true(all(
      ampute(
        data = complete.data, patterns = c(1, 1, 1, 0, 1, 0),
        weights = c(1, 0, 0, 0, 1, 0)
      )$weights == c(0, 1, 0)
    ))
  )

  suppressWarnings(
    expect_true(all(
      ampute(
        data = complete.data, patterns = c(0, 1, 0, 1, 1, 1),
        weights = c(1, 0, 0, 0, 1, 0)
      )$weights == c(1, 0, 0)
    ))
  )

  suppressWarnings(
    expect_true(all(
      ampute(
        data = complete.data, patterns = c(0, 1, 0, 1, 1, 1),
        weights = c(1, 0, 0)
      )$weights == c(1, 0, 0)
    ))
  )

  suppressWarnings(
    expect_true(all(
      ampute(
        data = complete.data, patterns = c(1, 1, 1, 0, 1, 0, 1, 1, 1),
        weights = c(1, 0, 0)
      )$weights == c(1, 0, 0)
    ))
  )

  suppressWarnings(
    expect_true(all(
      ampute(
        data = complete.data, patterns = c(1, 1, 1, 0, 1, 0, 1, 1, 1),
        weights = c(1, 0, 0, 0, 1, 0, 0, 0, 1)
      )$weights == c(0, 1, 0)
    ))
  )
})

test_that("prop and freq are properly adjusted when patterns contain only 1's", {
  suppressWarnings(
    expect_equal(ampute(data = complete.data, patterns = c(1, 1, 1, 0, 1, 0))$prop, 0.25)
  )

  suppressWarnings(
    expect_equal(ampute(data = complete.data, patterns = c(1, 1, 1, 0, 1, 0))$freq, 1)
  )

  suppressWarnings(
    expect_equal(
      ampute(data = complete.data, patterns = c(1, 1, 1, 0, 1, 0, 0, 1, 0))$prop, 1 / 3
    )
  )

  suppressWarnings(
    expect_true(all(
      ampute(data = complete.data, patterns = c(1, 1, 1, 0, 1, 0, 0, 1, 0))$freq == c(0.5, 0.5)
    ))
  )

  suppressWarnings(
    expect_equal(
      ampute(data = complete.data, patterns = c(1, 1, 1, 0, 1, 0, 1, 1, 1))$prop, 1 / 3 * 0.5
    )
  )

  suppressWarnings(
    expect_equal(
      ampute(data = complete.data, patterns = c(1, 1, 1, 0, 1, 0, 1, 1, 1))$freq, 1
    )
  )
})

# The following test was created to evaluate warnings when not all patterns can be generated (#317)

test_that("warnings appear when not all patterns can be generated", {
  set.seed(12032021)
  binary.data <- lapply(
    runif(10, 0.05, 0.15),
    function(p, n) rbinom(n, 1, p),
    n = 10
  ) %>%
    do.call(what = "data.frame") %>%
    rlang::set_names(paste0("type", LETTERS[1:ncol(.)]))
  expect_warning(
    ampute(
      data = binary.data
    )
  )

  df <- matrix(c(runif(1000, 0.5, 1), rep(0, 1000)), nrow = 1000, byrow = FALSE)
  expect_warning(
    ampute(df, pattern = c(0, 1)),
    "The weighted sum scores of all candidates in pattern 1 are the same, they will be amputed with probability 0.5"
  )
})


# The following test was contributed by Shangzhi-hong (#216) Dec 2019
context("ampute robust version")

set.seed(1)

# Set-up
# Dataset
NUM_OBS_DF <- 25
NUM_VAR_DF <- 10

data <- replicate(
  n = NUM_VAR_DF,
  expr = {
    rnorm(n = NUM_OBS_DF, mean = 1, sd = 1)
  },
  simplify = "matrix"
)

# Ampute pattern
covNum <- NUM_VAR_DF - 1
misPatCov1 <- t(combn(
  x = covNum, m = 1,
  FUN = function(x) replace(rep(1, covNum), x, 0)
))
misPat1 <- cbind(rep(1, choose(covNum, 1)), misPatCov1)
misPatCov2 <- t(combn(
  x = covNum, m = 2,
  FUN = function(x) replace(rep(1, covNum), x, 0)
))
misPat2 <- cbind(rep(1, choose(covNum, 2)), misPatCov2)
patterns <- rbind(misPat1, misPat2)
weights <- matrix(0, nrow = nrow(patterns), ncol = ncol(patterns))
weights[, 1] <- 1

prop <- 0.5
mech <- "MAR"
type <- "RIGHT"
bycases <- TRUE
# Other params
# freq <- NULL
# std <- TRUE
# cont <- TRUE
# type <- NULL
# odds <- NULL
# run <- TRUE

test_that("ampute() works under extreme condition", {
  expect_warning(
    ampDf <- ampute(
      data = data,
      prop = prop,
      mech = mech,
      type = type,
      bycases = bycases,
      patterns = patterns,
      weights = weights
    )$amp
  )
  outProp <- sum(complete.cases(ampDf)) / NUM_OBS_DF
  expect_true(outProp > 0.3 & outProp < 0.7)
})

# --- end test Shangzhi-hong (#216) Dec 2019
