library(s2dv)
library(testthat)
library(multiApply)
library(ClimProjDiags)


##############################################
# data
##############################################

# dat1
set.seed(1)
exp1 <- array(rnorm(60), dim = c(member = 3, sdate = 10, lat = 2))
set.seed(2)
obs1 <- array(rnorm(20), dim = c(sdate = 10, lat = 2))

# dat1_2
exp1_2 <-  exp1 + 1
obs1_2 <-  obs1 + 1

# dat1_3: NAs
exp1_3 <- exp1; exp1_3[1, 2, 1] <- NA
obs1_3 <- obs1; obs1_3[2, 1] <- NA

# dat2
set.seed(1)
exp2 <- array(rnorm(20), dim = c(member = 2, sdate = 10))
set.seed(2)
obs2 <- array(rnorm(10), dim = c(sdate = 10))
set.seed(2)
obs2_1 <- array(rnorm(10), dim = c(member = 1, sdate = 10))

# dat3
set.seed(1)
exp3 <- array(rnorm(60), dim = c(member = 2, sdate = 10, dataset = 3))
set.seed(2)
obs3 <- array(rnorm(20), dim = c(member = 1, sdate = 10, dataset = 2))

# dat3_2
set.seed(1)
exp3_2 <- array(rnorm(80), dim = c(member = 4, sdate = 5, dataset = 4))
set.seed(2)
obs3_2 <- array(rnorm(30), dim = c(member = 2, sdate = 5, dataset = 3))

# dat4
set.seed(1)
exp4 <- array(rnorm(60), dim = c(member = 2, sdate = 10, dataset = 3, lat = 2))
set.seed(2)
obs4 <- array(rnorm(20), dim = c(member = 1, sdate = 10, dataset = 2, lat = 2))

# dat4_2: NAs
exp4_2 <- exp4; exp4_2[1, 2, 1, 1] <- NA
obs4_2 <- obs4; obs4_2[1, 1:4, 1, 1] <- NA


##############################################
# tests
##############################################

test_that("1. Input checks", {

  # exp and obs (1)
  expect_error(
    SprErr(c(), obs1), 
    "Parameter 'exp' and 'obs' cannot be NULL." 
  )
  expect_error(
    SprErr(obs1, c()), 
    "Parameter 'exp' and 'obs' cannot be NULL." 
  )
  expect_error(
    SprErr("", obs1),
    "Parameter 'exp' and 'obs' must be a numeric array."
  )
  expect_error(
    SprErr(exp1, ""), 
    "Parameter 'exp' and 'obs' must be a numeric array."
  )
  expect_error(
    SprErr(1, obs1), 
    "Parameter 'exp' and 'obs' must be array with as least two dimensions memb_dim and time_dim."
  )
  expect_error(
    SprErr(exp1, 1), 
    "Parameter 'exp' and 'obs' must be array with as least two dimensions memb_dim and time_dim." 
  )
  expect_error(
    SprErr(array(1), obs1), 
    "Parameter 'exp' and 'obs' must have dimension names."
  )
  expect_error(
    SprErr(exp1, array(1)), 
    "Parameter 'exp' and 'obs' must have dimension names."
  )
  # dat_dim
  expect_error(
    SprErr(exp1, obs1, dat_dim = 1),
    "Parameter 'dat_dim' must be a character string."
  ) 
  expect_error(
    SprErr(exp1, obs1, dat_dim = 'dat'), 
    "Parameter 'dat_dim' is not found in 'exp' or 'obs' dimension."
  )  
  # memb_dim
  expect_error(
    SprErr(exp1, obs1, memb_dim = 1),
    "Parameter 'memb_dim' must be a character string."
  )
  expect_error(
    SprErr(exp1, obs1, memb_dim = 'check'), 
    "Parameter 'memb_dim' is not found in 'exp' dimensions. 'exp' must have the member dimension to compute the spread."
  )
  # time_dim
  expect_error(
    SprErr(exp1, obs1, time_dim = 1),
    "Parameter 'time_dim' must be a character string."
  )  
  expect_error(
    SprErr(exp1, obs1, time_dim = 'time'),
    "Parameter 'time_dim' is not found in 'exp' or 'obs' dimension."
  )
  # exp and obs (2)
  expect_error(
    SprErr(exp1, array(1:9, dim = c(sdate = 9))),
    "Parameter 'exp' and 'obs' must have same length of all the dimensions except 'dat_dim' and 'memb_dim'."
  )
  # pval
  expect_error(
    SprErr(exp1, obs1, pval = 1),
    "Parameter 'pval' must be one logical value."
  )  
  # sign
  expect_error(
    SprErr(exp1, obs1, sign = 1),
    "Parameter 'sign' must be one logical value."
  )  
  # alpha
  expect_error(
    SprErr(exp1, obs1, alpha = -0.05),
    "Parameter 'alpha' must be a numeric number between 0 and 1."
  )   
  # na.rm
  expect_error(
    SprErr(exp1, obs1, na.rm = ""), 
    "Parameter 'na.rm' must be TRUE or FALSE"
  )  
  # ncores
  expect_error(
    SprErr(exp2, obs2, ncores = 1.5),
    "Parameter 'ncores' must be a positive integer."
  )
})

##############################################


test_that("2. Output checks: dat1", {
  
  # element names
  expect_equal(
    names(SprErr(exp1, obs1)),
    c("ratio", "p.val")
  )
  expect_equal(
    names(SprErr(exp1, obs1, pval = FALSE, sign = TRUE)),
    c("ratio", "sign")
  )
  expect_equal(
    names(SprErr(exp1, obs1, pval = TRUE, sign = TRUE)),
    c("ratio", "p.val", "sign")
  )
  # dimensions
  expect_equal(
    dim(SprErr(exp1, obs1)$ratio),
    c(lat = 2)
  )
  expect_equal(
    dim(SprErr(exp1, obs1)$p.val),
    c(lat = 2)
  )
  expect_equal(
    dim(SprErr(exp1, obs1, pval = FALSE, sign = TRUE)$ratio), 
    c(lat = 2)
  )
  expect_equal(
    dim(SprErr(exp1, obs1, pval = FALSE, sign = TRUE)$sign), 
    c(lat = 2)
  )
  # values
  expect_equal(
    as.vector(SprErr(exp1, obs1)$ratio),
    c(1.0646692, 0.6657522),
    tolerance = 0.0001
  )
  expect_equal(
    as.vector(SprErr(exp1, obs1)$p.val),
    c(0.8549593, 0.2412730),
    tolerance = 0.0001
  )
  expect_equal(
    as.vector(SprErr(exp1, obs1, sign = TRUE)$sign),
    c(FALSE, FALSE)
  )
  # pval = FALSE
  expect_equal(
    as.vector(SprErr(exp1, obs1, pval = FALSE, sign = TRUE)$ratio),
    c(01.0646692, 0.6657522),  
    tolerance = 0.0001
  )
  expect_equal(
    as.vector(SprErr(exp1, obs1, pval = FALSE, sign = TRUE)$sign),
    c(FALSE, FALSE)  
  )
  # na.rm = TRUE
  expect_equal(
    as.vector(SprErr(exp1, obs1, na.rm = TRUE)$ratio),
    c(1.0646692, 0.6657522),  
    tolerance = 0.0001
  )
  expect_equal(
    as.vector(SprErr(exp1, obs1, na.rm = TRUE)$p.val),
    c(0.8549593, 0.2412730),  
    tolerance = 0.0001
  )
  expect_equal(
    as.vector(SprErr(exp1, obs1, sign = TRUE, na.rm = TRUE)$sign),
    c(FALSE, FALSE)
  )
  # alpha
  expect_equal(
    as.vector(SprErr(exp1, obs1, sign = TRUE, alpha = 0.5)$sign),
    c(FALSE, TRUE)
  )
  expect_equal(
    as.vector(SprErr(exp1, obs1, sign = TRUE, alpha = 0.99)$sign),
    c(TRUE, TRUE)
  )
  
  # dat1_2
  expect_equal(
    SprErr(exp1, obs1),
    SprErr(exp1_2, obs1_2)
  )
  expect_equal(
    SprErr(exp1, obs1, sign = TRUE)$ratio,
    SprErr(exp1_2, obs1_2)$ratio
  )
  expect_equal(
    SprErr(exp1, obs1, sign = TRUE)$p.val,
    SprErr(exp1_2, obs1_2)$p.val
  )
  
  # dat1_3
  expect_equal(
    as.vector(SprErr(exp1_3, obs1_3)$ratio),
    c(NA, 0.6657522),
    tolerance = 0.0001
  )
  expect_equal(
    as.vector(SprErr(exp1_3, obs1_3)$p.val),
    c(NA, 0.241273),
    tolerance = 0.0001
  )
  expect_equal(
    as.vector(SprErr(exp1_3, obs1_3, sign = TRUE)$sign),
    c(NA, FALSE)
  )
  expect_equal(
    as.vector(SprErr(exp1_3, obs1_3, na.rm = TRUE)$ratio),
    c(0.9656329, 0.6657522),
    tolerance = 0.0001
  )
  expect_equal(
    as.vector(SprErr(exp1_3, obs1_3, na.rm = TRUE)$p.val),
    c(0.896455, 0.241273),
    tolerance = 0.0001
  )
  expect_equal(
    as.vector(SprErr(exp1_3, obs1_3, sign = TRUE, na.rm = TRUE)$sign),
    c(FALSE, FALSE)
  )
  
})

##############################################


test_that("3. Output checks: dat2", {
  
  expect_equal(
    names(SprErr(exp2, obs2)),
    c("ratio", "p.val")
  )
  expect_equal(
    names(SprErr(exp2, obs2, sign = TRUE)),
    c("ratio", "p.val","sign")
  )
  expect_equal(
    dim(SprErr(exp2, obs2)$ratio),
    NULL
  )
  expect_equal(
    dim(SprErr(exp2, obs2)$p.val),
    NULL
  )
  expect_equal(
    dim(SprErr(exp2, obs2, sign = TRUE)$sign),
    NULL
  )
  # values
  expect_equal(
    as.vector(SprErr(exp2, obs2)$ratio),
    c(0.6866402),
    tolerance = 0.0001
  )
  expect_equal(
    as.vector(SprErr(exp2, obs2)$p.val),
    c(0.2779936),
    tolerance = 0.0001
  )
  expect_equal( 
    as.vector(SprErr(exp2, obs2, sign = TRUE)$sign), 
    FALSE 
  )
  # sign = TRUE
  expect_equal(
    as.vector(SprErr(exp2, obs2, sign = TRUE)$ratio),
    c(0.6866402),
    tolerance = 0.0001
  )
  expect_equal(
    as.vector(SprErr(exp2, obs2, sign = TRUE)$p.val),
    c(0.2779936),
    tolerance = 0.0001
  )
  # alpha
  expect_equal(
    as.vector(SprErr(exp2, obs2, sign = TRUE, alpha = 0.99)$sign),
    TRUE 
  )
  # na.rm = TRUE
  expect_equal(
    as.vector(SprErr(exp2, obs2, na.rm = TRUE)$ratio),
    c(0.6866402),
    tolerance = 0.0001
  )
  expect_equal(
    as.vector(SprErr(exp2, obs2, na.rm = TRUE)$p.val),
    c(0.2779936),
    tolerance = 0.0001
  )
  expect_equal(
    as.vector(SprErr(exp2, obs2, na.rm = TRUE, sign = TRUE)$sign), 
    FALSE 
  )
  # other
  expect_equal(
    SprErr(exp2, obs2),
    SprErr(exp2, obs2_1)
  )
  
})

##############################################


test_that("4. Output checks: dat3", {
  
  expect_equal(
    dim(SprErr(exp3, obs3, dat_dim = 'dataset')$ratio),
    c('nexp' = 3, 'nobs' = 2)
  )
  expect_equal(
    dim(SprErr(exp3, obs3, dat_dim = 'dataset')$p.val),
    c('nexp' = 3, 'nobs' = 2)
  )
  # values
  expect_equal(
    mean(SprErr(exp3, obs3, dat_dim = 'dataset')$ratio),
    c(0.5831841),
    tolerance = 0.0001
  )
  expect_equal(
    as.vector(SprErr(exp3, obs3, dat_dim = 'dataset')$ratio),
    c(0.7006396, 0.6277856, 0.4211269, 0.7006396, 0.6277856, 0.4211269),
    tolerance = 0.0001
  )
  expect_equal(
    as.vector(SprErr(exp3, obs3, dat_dim = 'dataset')$p.val)[1:3],
    c(0.30405979, 0.18162950, 0.01675207),
    tolerance = 0.0001
  )
  expect_equal(
    as.vector(SprErr(exp3, obs3, dat_dim = 'dataset', sign = TRUE)$sign)[1:3],
    c(FALSE, FALSE, TRUE)
  )
  expect_equal(
    mean(SprErr(exp3, obs3, dat_dim = 'dataset', sign = TRUE)$ratio),
    c(0.5831841),
    tolerance = 0.0001
  )
  expect_equal(
    as.vector(SprErr(exp3, obs3, dat_dim = 'dataset', sign = TRUE)$p.val)[1:3],
    c(0.30405979, 0.18162950, 0.01675207),
    tolerance = 0.0001
  )
  # alpha
  expect_equal(
    as.vector(SprErr(exp3, obs3, dat_dim = 'dataset', sign = TRUE, alpha = 0.99)$sign)[1:3],
    c(TRUE, TRUE, TRUE)
  )
  expect_equal(
    as.vector(SprErr(exp3, obs3, dat_dim = 'dataset', sign = TRUE, alpha = 0.20)$sign)[1:3],
    c(FALSE, TRUE, TRUE)
  )
  # na.rm = TRUE
  expect_equal(
    as.vector(SprErr(exp3, obs3, dat_dim = 'dataset', na.rm = TRUE)$ratio),
    c(0.7006396, 0.6277856, 0.4211269, 0.7006396, 0.6277856, 0.4211269),
    tolerance = 0.0001
  )
  expect_equal(
    as.vector(SprErr(exp3, obs3, dat_dim = 'dataset', na.rm = TRUE)$p.val)[1:3],
    c(0.30405979, 0.18162950, 0.01675207),
    tolerance = 0.0001
  )
  expect_equal( 
    as.vector(SprErr(exp3, obs3, dat_dim = 'dataset', na.rm = TRUE, sign = TRUE)$sign)[1:3], 
    c(FALSE, FALSE, TRUE) 
  )
  
  # dat3_2
  expect_equal(
    dim(SprErr(exp3_2, obs3_2, dat_dim = 'dataset')$ratio),
    c('nexp' = 4, 'nobs' = 3)
  )
  expect_equal(
    dim(SprErr(exp3_2, obs3_2, dat_dim = 'dataset')$p.val),
    c('nexp' = 4, 'nobs' = 3)
  )
  # values
  expect_equal(
    mean(SprErr(exp3_2, obs3_2, dat_dim = 'dataset')$ratio),
    c(1.25586),
    tolerance = 0.0001
  )
  expect_equal(
    as.vector(SprErr(exp3_2, obs3_2, dat_dim = 'dataset')$p.val)[1:4],
    c(0.6927309, 0.7390035, 0.8834023, 0.4421531),
    tolerance = 0.0001
  )
  expect_equal(
    as.vector(SprErr(exp3_2, obs3_2, dat_dim = 'dataset', sign = TRUE)$sign)[1:4],
    c(FALSE, FALSE, FALSE, FALSE)
  )
  expect_equal(
    mean(SprErr(exp3_2, obs3_2, dat_dim = 'dataset', sign = TRUE)$ratio),
    c(1.25586),
    tolerance = 0.0001
  )
  expect_equal(
    as.vector(SprErr(exp3_2, obs3_2, dat_dim = 'dataset', sign = TRUE)$p.val)[1:4],
    c(0.6927309, 0.7390035, 0.8834023, 0.4421531),
    tolerance = 0.0001
  )
  # alpha
  expect_equal(
    as.vector(SprErr(exp3_2, obs3_2, dat_dim = 'dataset', sign = TRUE, alpha = 0.99)$sign)[1:3],
    c(TRUE, TRUE, TRUE)
  )
  expect_equal(
    as.vector(SprErr(exp3_2, obs3_2, dat_dim = 'dataset', sign = TRUE, alpha = 0.70)$sign)[1:4],
    c(TRUE, FALSE, FALSE, TRUE)
  )
  # na.rm = TRUE
  expect_equal(
    mean(SprErr(exp3_2, obs3_2, dat_dim = 'dataset', na.rm = TRUE)$ratio),
    c(1.25586),
    tolerance = 0.0001
  )
  expect_equal(
    as.vector(SprErr(exp3_2, obs3_2, dat_dim = 'dataset', na.rm = TRUE)$p.val)[1:4],
    c(0.6927309, 0.7390035, 0.8834023, 0.4421531),
    tolerance = 0.0001
  )
  expect_equal( 
    as.vector(SprErr(exp3_2, obs3_2, dat_dim = 'dataset', na.rm = TRUE, 
                     alpha = 0.70, sign = TRUE)$sign)[1:4], 
    c(TRUE, FALSE, FALSE, TRUE) 
  )
  
})

############################################## 


test_that("5. Output checks: dat4", {
  
  expect_equal(
    dim(SprErr(exp4, obs4, dat_dim = 'dataset')$ratio),
    c('nexp' = 3, 'nobs' = 2, 'lat' = 2)
  )
  expect_equal(
    dim(SprErr(exp4, obs4, dat_dim = 'dataset', sign = TRUE)$p.val),
    c('nexp' = 3, 'nobs' = 2, 'lat' = 2)
  )
  # values
  expect_equal(
    mean(SprErr(exp4, obs4, dat_dim = 'dataset')$ratio),
    c(0.5831841),
    tolerance = 0.0001
  )
  expect_equal(
    mean(SprErr(exp4, obs4, dat_dim = 'dataset')$p.val),
    c(0.1674805),
    tolerance = 0.0001
  )
  expect_equal(
    as.vector(SprErr(exp4, obs4, dat_dim = 'dataset')$ratio[, , 2]),
    c(0.7006396, 0.6277856, 0.4211269, 0.7006396, 0.6277856, 0.4211269),
    tolerance = 0.0001
  )
  expect_equal(
    as.vector(SprErr(exp4, obs4, dat_dim = 'dataset')$p.val[, , 2]),
    c(0.30405979, 0.18162950, 0.01675207, 0.30405979, 0.18162950, 0.01675207),
    tolerance = 0.0001
  )
  expect_equal(
    SprErr(exp4, obs4, dat_dim = 'dataset')$ratio[, , 1],
    SprErr(exp3, obs3, dat_dim = 'dataset')$ratio
  )
  expect_equal(
    mean(SprErr(exp4, obs4, dat_dim = 'dataset', sign = TRUE)$ratio),
    c(0.5831841),
    tolerance = 0.0001
  )
  expect_equal(
    mean(SprErr(exp4, obs4, dat_dim = 'dataset', sign = TRUE)$p.val),
    c(0.1674805),
    tolerance = 0.0001
  )
  
  # dat4_2: NAs
  expect_equal(
    as.vector(SprErr(exp4_2, obs4_2, dat_dim = 'dataset')$ratio[, , 1]),
    c(NA_real_, NA_real_, NA_real_, NA_real_, NA_real_, NA_real_),
    tolerance = 0.0001
  )
  expect_equal(
    as.vector(SprErr(exp4_2, obs4_2, dat_dim = 'dataset')$p.val[, , 1]),
    c(NA_real_, NA_real_, NA_real_, NA_real_, NA_real_, NA_real_),
    tolerance = 0.0001
  )
  expect_equal(
    as.vector(SprErr(exp4_2, obs4_2, dat_dim = 'dataset', sign = TRUE)$sign[, , 1]),
    c(NA, NA, NA, NA, NA, NA),
    tolerance = 0.0001
  )
  expect_equal(
    as.vector(SprErr(exp4_2, obs4_2, dat_dim = 'dataset')$ratio[, , 2]),
    c(0.7006396, 0.6277856, 0.4211269, 0.7006396, 0.6277856, 0.4211269),
    tolerance = 0.0001
  )
  expect_equal(
    as.vector(SprErr(exp4_2, obs4_2, dat_dim = 'dataset')$p.val[, , 2]),
    c(0.30405979, 0.18162950, 0.01675207, 0.30405979, 0.18162950, 0.01675207),
    tolerance = 0.0001
  )
  expect_equal(
    which(is.na(SprErr(exp4_2, obs4_2, dat_dim = 'dataset')$ratio)),
    which(is.na(SprErr(exp4_2, obs4_2, dat_dim = 'dataset')$p.val))
  )
  expect_equal(
    as.vector(SprErr(exp4_2, obs4_2, dat_dim = 'dataset', na.rm = TRUE)$ratio[, , 1]),
    c(0.4648097, 0.6571888, 0.3975804, 0.4648097, 0.6571888, 0.3975804),
    tolerance = 0.0001
  )
  expect_equal(
    as.vector(SprErr(exp4_2, obs4_2, dat_dim = 'dataset', na.rm = TRUE)$p.val[, , 1]),
    c(0.04674814, 0.21983481, 0.01350139, 0.04351013, 0.22700305, 0.01124875),
    tolerance = 0.0001
  )
  expect_equal( 
    as.vector(SprErr(exp4_2, obs4_2, dat_dim = 'dataset', na.rm =TRUE, sign = TRUE)$sign[, , 1]),
    c(TRUE, FALSE, TRUE, TRUE, FALSE, TRUE),
    tolerance = 0.0001
  )
  expect_equal(
    which(is.na(SprErr(exp4_2, obs4_2, dat_dim = 'dataset', na.rm = TRUE)$ratio)),
    which(is.na(SprErr(exp4_2, obs4_2, dat_dim = 'dataset', na.rm = TRUE)$p.val))
  )
  expect_equal(
    which(is.na(SprErr(exp4_2, obs4_2, dat_dim = 'dataset', na.rm = TRUE)$ratio)),
    which(is.na(SprErr(exp4_2, obs4_2, dat_dim = 'dataset', na.rm = TRUE)$p.val))
  )
  
})
