library("dbscan")
library("testthat")

context("FOSC")

data("iris")

## FOSC expects an hclust object
expect_error(extractFOSC(iris))

x <- iris[, 1:4]
x_sl <- hclust(dist(x), "single")

## Should return augmented hclust object and cluster assignments
expect_length(extractFOSC(x_sl), 2)
res <- extractFOSC(x_sl)
expect_equal(res$hc$method, "single (w/ stability-based extraction)")

## Constraint-checking
expect_error(extractFOSC(x_sl, constraints = c("1" = 2)))

## Matrix inputs must be nxn
expect_error(extractFOSC(x_sl, constraints = matrix(c(1, 2), nrow=1)))

## Matrix or vector constraints must be in c(-1, 0, 1)
expect_error(extractFOSC(x_sl, constraints = matrix(-2, nrow=nrow(x), ncol=nrow(x))))

## Valid constraints
expect_warning(extractFOSC(x_sl, constraints = matrix(1, nrow=nrow(x), ncol=nrow(x))))
expect_silent(extractFOSC(x_sl, constraints = list("1" = 2, "2" = 1)))
expect_silent(extractFOSC(x_sl, constraints = ifelse(dist(x) > 2, -1, 1)))

## Constraints should be symmetric, but symmetry test is only done if specified. Asymmetric
## constraints through warning, but proceeds with manual warning
expect_warning(extractFOSC(x_sl, constraints = list("1" = 2), validate_constraints = T))

## Make sure that's whats returned
res <- extractFOSC(x_sl)
expect_type(res$cluster, "integer")
expect_s3_class(res$hc, "hclust")

## Test 'Optimal' Clustering using only positive constraints
set <- which(iris$Species == "setosa")
ver <- which(iris$Species == "versicolor")
vir <- which(iris$Species == "virginica")
il_constraints <- structure(list(set[-1], ver[-1], vir[-1]), names = as.character(c(set[1], ver[1], vir[1])))
res <- extractFOSC(x_sl, il_constraints)

## Positive-only constraints should link to best unsupervised solution
expect_equivalent(table(res$cluster), as.table(c("1" = 50L, "2" = 100L)))
expect_equal(res$hc$method, "single (w/ constraint-based extraction)")

## Test negative constraints
set2 <- c(il_constraints[[as.character(set[1])]], -unlist(il_constraints[as.character(c(ver[1], vir[1]))], use.names = F))
ver2 <- c(il_constraints[[as.character(ver[1])]], -unlist(il_constraints[as.character(c(set[1], vir[1]))], use.names = F))
vir2 <- c(il_constraints[[as.character(vir[1])]], -unlist(il_constraints[as.character(c(set[1], ver[1]))], use.names = F))
il_constraints2 <- structure(list(set2, ver2, vir2), names = as.character(c(set[1], ver[1], vir[1])))
res2 <- extractFOSC(x_sl, constraints = il_constraints2)

## Positive and Negative should produce a different solution
expect_false(all(res$cluster == res2$cluster))
expect_equal(res2$hc$method, "single (w/ constraint-based extraction)")

## Test minPts parameters
expect_error(extractFOSC(x_sl, constraints = il_constraints2, minPts = 1))
expect_silent(extractFOSC(x_sl, constraints = il_constraints2, minPts = 5))

## Test alpha parameter
expect_silent(extractFOSC(x_sl, constraints = il_constraints2, alpha = 0.5))
expect_error(extractFOSC(x_sl, constraints = il_constraints2, alpha = 1.5))
res3 <- extractFOSC(x_sl, constraints = il_constraints2, alpha = 0.5)
expect_equal(res3$hc$method, "single (w/ mixed-objective extraction)")

## Test unstable pruning
expect_silent(extractFOSC(x_sl, constraints = il_constraints2, prune_unstable = T))

