context("Dissolution Diagnostics")

test_that("simulation diagnostics work as expected", {
  skip_on_cran()

  ## direct implementation of dissolution statistics based on tedgelist
  simulate_diss_stats <- function(est, nsteps, dyad_indexer) {
    nws1 <- simulate(est$formula,
                     coef = est$coef.form.crude,
                     basis = est$newnetwork,
                     dynamic = FALSE)

    nws2 <- simulate(~Form(est$formation) + Persist(est$coef.diss$dissolution),
                     coef = c(est$coef.form, est$coef.diss$coef.crude),
                     basis = nws1,
                     time.slices = nsteps,
                     dynamic = TRUE)

    dissolution <- est$coef.diss$dissolution
    durs <- est$coef.diss$duration

    sim.df <- as.data.frame(nws2)

    edgecounts <- matrix(0, nrow = nsteps, ncol = length(durs))
    edgeages <- matrix(0, nrow = nsteps, ncol = length(durs))
    edgediss <- matrix(0, nrow = nsteps, ncol = length(durs))

    dyad_types <- dyad_indexer(sim.df$tail, sim.df$head, nws1)
    for (i in seq_len(NROW(sim.df))) {
      onset <- max(sim.df$onset[i], 1)
      terminus <- sim.df$terminus[i]

      if (terminus > 1) {
        edgecounts[onset - 1 + seq_len(terminus - onset), dyad_types[i]] <-
          edgecounts[onset - 1 + seq_len(terminus - onset), dyad_types[i]] + 1
        edgeages[onset - 1 + seq_len(terminus - onset), dyad_types[i]] <-
          edgeages[onset - 1 + seq_len(terminus - onset), dyad_types[i]] +
          seq_len(terminus - onset) + (sim.df$onset[i] == 0)
      }

      if (terminus <= nsteps) {
        edgediss[terminus, dyad_types[i]] <- edgediss[terminus, dyad_types[i]] + 1
      }
    }

    init.edgecounts <- summary(dissolution, basis = nws2, at = 0)
    if (length(durs) > 1) {
      init.edgecounts[1] <- init.edgecounts[1] - sum(init.edgecounts[-1])
    }
    edgecounts <- rbind(init.edgecounts, edgecounts)

    edgeagesimputed <- edgeages

    wti <- which(sim.df$onset == 0 & sim.df$terminus > 1)
    for (index in wti) {
      edgeagesimputed[seq_len(sim.df$terminus[index] - 1), dyad_types[index]] <-
        edgeagesimputed[seq_len(sim.df$terminus[index] - 1), dyad_types[index]] +
        rgeom(1, 1/durs[dyad_types[index]])
    }

    pages <- array(edgeages/edgecounts[-1,,drop=FALSE],
                   dim = c(nsteps,length(durs),1))
    pages_imptd <- array(edgeagesimputed/edgecounts[-1,,drop=FALSE],
                         dim = c(nsteps,length(durs),1))
    prop.diss <- array(edgediss/edgecounts[-NROW(edgecounts),,drop=FALSE],
                       dim = c(nsteps,length(durs),1))

    list(pages = pages,
         pages_imptd = pages_imptd,
         prop.diss = prop.diss)
  }

  net_size <- 100
  nsteps <- 10
  nsims <- 2
  seed <- 0

  coefs.diss <- list(dissolution_coefs(~offset(edges), 10, 0),
                     dissolution_coefs(~offset(edges)+offset(nodemix("attr", levels = 2:3,
                                                                     levels2 = c(2,1))), c(8,5,7), 0),
                     dissolution_coefs(~offset(edges)+offset(nodematch("attr", diff = TRUE,
                                                                       levels = c(3,1,2))), c(31, 22, 3, 4), 0))

  dyad_indexers <- list(function(tails, heads, nw) rep(1, length(tails)),
                        function(tails, heads, nw) {
                          attr <- nw %v% "attr"
                          tailattr <- attr[tails]
                          headattr <- attr[heads]
                          indices <- rep(1, length(tails))
                          indices[(tailattr == 2 & headattr == 3) | (tailattr == 3 & headattr == 2)] <- 2
                          indices[tailattr == 2 & headattr == 2] <- 3
                          indices
                        },
                        function(tails, heads, nw) {
                          attr <- nw %v% "attr"
                          tailattr <- attr[tails]
                          headattr <- attr[heads]
                          indices <- rep(1, length(tails))
                          indices[tailattr == 3 & headattr == 3] <- 2
                          indices[tailattr == 1 & headattr == 1] <- 3
                          indices[tailattr == 2 & headattr == 2] <- 4
                          indices
                        })

  formations <- list(~edges,
                     ~edges + nodemix("attr", levels = 2:3, levels2 = c(2,1)),
                     ~edges + nodematch("attr", diff = TRUE, levels = c(3,1,2)))

  targets <- lapply(list(c(100),
                         c(100, 2*100/9, 2*100/9),
                         c(100, 100/9, 100/9, 100/9)),
                    as.integer)

  init.edges.list <- list(FALSE, FALSE, TRUE)
  nested.edapprox.list <- list(FALSE, TRUE, FALSE)
  keep.tnetwork.list <- list(TRUE, FALSE, FALSE)

  for (index in seq_along(coefs.diss)) {
    init.edges <- init.edges.list[[index]]
    nested.edapprox <- nested.edapprox.list[[index]]
    keep.tnetwork <- keep.tnetwork.list[[index]]
    coef.diss <- coefs.diss[[index]]
    dyad_indexer <- dyad_indexers[[index]]

    if (nested.edapprox == TRUE) {
      formation <- formations[[index]]
      target.stats <- targets[[index]]
    } else {
      formation <- ~edges
      target.stats <- c(100)
    }

    nw <- network.initialize(net_size, directed = FALSE)
    nw %v% "attr" <- rep(1:3, length.out = net_size)

    est <- netest(nw,
                  formation = formation,
                  coef.diss = coef.diss,
                  target.stats = target.stats,
                  nested.edapprox = nested.edapprox)

    if (init.edges == FALSE) {
      est$newnetwork[,] <- FALSE
    }

    set.seed(seed)
    dx <- netdx(est, nsims = nsims, nsteps = nsteps, keep.tnetwork = keep.tnetwork, verbose = FALSE)

    capture_output(
      print(dx)
    )
    plot(dx)

    set.seed(seed)
    ds <- list()
    for (i in seq_len(nsims)) {
      ds[[i]] <- simulate_diss_stats(est, nsteps, dyad_indexer)
    }

    durs <- coef.diss$duration

    pages <- array(unlist(lapply(ds, `[[`, "pages")),
                   dim = c(nsteps,length(durs),nsims))
    pages_imptd <- array(unlist(lapply(ds, `[[`, "pages_imptd")),
                         dim = c(nsteps,length(durs),nsims))
    prop.diss <- array(unlist(lapply(ds, `[[`, "prop.diss")),
                       dim = c(nsteps,length(durs),nsims))

    expect_equal(pages, dx$pages)
    expect_equal(pages_imptd, dx$pages_imptd)
    expect_equal(prop.diss, dx$prop.diss)
  }
})

test_that("netsim produces a networkDynamic with the expected data.frame when resimulate.network = FALSE", {
  skip_on_cran()
  nw <- network_initialize(n = 50)
  nw <- set_vertex_attribute(nw, "race", rbinom(50, 1, 0.5))
  est <- netest(nw, formation = ~edges + nodematch("race"),
                target.stats = c(25, 10),
                coef.diss = dissolution_coefs(~offset(edges), 10, 0),
                verbose = FALSE)
  param <- param.net(inf.prob = 0, act.rate = 0)
  init <- init.net(status.vector = rep("i", 50))
  control <- control.net(type = NULL,
                         nsims = 1,
                         nsteps = 5,
                         resimulate.network = FALSE,
                         set.control.ergm = control.simulate.formula(),
                         verbose = FALSE)

  set.seed(0)
  mod <- netsim(est, param, init, control)
  nwd <- get_network(mod)

  capture_output(
    print(mod)
  )
  plot(mod)

  set.seed(0)
  nws <- simulate(est$formula,
                  coef = est$coef.form.crude,
                  basis = est$newnetwork,
                  dynamic = FALSE)

  nws <- networkDynamic::as.networkDynamic(nws)
  nws <- networkDynamic::activate.vertices(nws, onset = 0, terminus = Inf)
  nws <- networkDynamic::activate.edges(nws, onset = 0, terminus = Inf)

  nws <- simulate(nws ~ Form(est$formation) + Persist(est$coef.diss$dissolution),
                  coef = c(est$coef.form, est$coef.diss$coef.crude),
                  time.start = 0,
                  time.slices = 5,
                  dynamic = TRUE)

  expect_identical(as.data.frame(nws), as.data.frame(nwd))
})

test_that("netsim produces a networkDynamic with the expected data.frame when resimulate.network = TRUE", {
  skip_on_cran()
  nw <- network_initialize(n = 50)
  nw <- set_vertex_attribute(nw, "race", rbinom(50, 1, 0.5))
  est <- netest(nw, formation = ~edges + nodematch("race"),
                target.stats = c(25, 10),
                coef.diss = dissolution_coefs(~offset(edges), 10, 0),
                verbose = FALSE)
  param <- param.net(inf.prob = 0, act.rate = 0)
  init <- init.net(status.vector = rep("i", 50))
  control <- control.net(type = NULL,
                         nsims = 1,
                         resimulate.network = TRUE,
                         nsteps = 5,
                         verbose = FALSE)

  set.seed(0)
  mod <- netsim(est, param, init, control)
  nwd <- get_network(mod)

  capture_output(
    print(mod)
  )
  plot(mod)

  set.seed(0)
  nws <- simulate(est$formula,
                  coef = est$coef.form.crude,
                  basis = est$newnetwork,
                  control = list(MCMC.burnin = 2e5),
                  dynamic = FALSE)

  nws <- networkDynamic::as.networkDynamic(nws)
  nws <- networkDynamic::activate.vertices(nws, onset = 0, terminus = Inf)
  nws <- networkDynamic::activate.edges(nws, onset = 0, terminus = Inf)

  nws %n% "net.obs.period" <- list(observations = list(c(0,1)),
                                   mode = "discrete",
                                   time.increment =  1,
                                   time.unit = "step")

  for (i in seq_len(5)) {
    nws <- suppressWarnings(simulate(nws ~ Form(est$formation) + Persist(est$coef.diss$dissolution),
                                     coef = c(est$coef.form, est$coef.diss$coef.crude),
                                     time.start = i,
                                     time.offset = 0,
                                     dynamic = TRUE))
  }

  expect_identical(as.data.frame(nws), as.data.frame(nwd))
})

test_that("tedgelist_to_toggles functions as expected", {
  skip_on_cran()
  logit <- function(p) log(p/(1-p))
  density <- 1/50
  D <- 10

  for (init.edges in list(FALSE, TRUE)) {
    nw <- network.initialize(100, directed = FALSE)
    if (init.edges == TRUE) {
      nw <- san(nw ~ edges, target.stats = c(100))
    }

    set.seed(0)
    nwd <- simulate(nw ~ Form(~edges) + Persist(~edges),
                    coef = c(logit(density) - log(D), log(D - 1)),
                    time.slices = 10,
                    output = "networkDynamic",
                    dynamic = TRUE)
    toggles <- tedgelist_to_toggles(as.data.frame(nwd))

    set.seed(0)
    changes <- simulate(nw ~ Form(~edges) + Persist(~edges),
                        coef = c(logit(density) - log(D), log(D - 1)),
                        time.slices = 10,
                        output = "changes",
                        dynamic = TRUE)

    if (init.edges == TRUE) {
      changes <- rbind(cbind(0L, as.edgelist(nw), 1L),
                       changes)
    }

    toggles2 <- changes[,-4L,drop=FALSE]

    toggles <- toggles[order(toggles[,1], toggles[,2], toggles[,3]),,drop=FALSE]
    toggles2 <- toggles2[order(toggles2[,1], toggles2[,2], toggles2[,3]),,drop=FALSE]
    expect_identical(unname(toggles), unname(toggles2))
  }
})
