########################################################################################################################################
########################################################################################################################################
# BubleyDyerMRP
########################################################################################################################################
########################################################################################################################################

#' @title
#' Approximating MRP matrix computation, using the Bubley-Dyer procedure.
#'
#' @description
#' Computes an approximated MRP matrix, starting from a set of linear extensions
#' sampled according to the Bubley-Dyer procedure.
#'
#' @param generator The approximated MRP matrix generator created by function [BubleyDyerMRPGenerator()].
#'
#' @param n Number of linear extensions generated to compute the approximated MRP matrix.
#' See `Details` for further information on this argument.
#'
#' @param error A real number in \eqn{(0,1)}, representing the "distance" from uniformity of the
#' sampling distribution of the linear extensions.
#' This parameter is used to determine the number of linear extensions to be sampled, in order to achieve the desired "distance".
#' According to Bubley and Dyer (1999), if `error`\eqn{=\epsilon} and \eqn{E} is the number of elements in the poset,
#' then the number \eqn{n_\epsilon} of sampled linear extensions is given by
#'
#' \eqn{n_\epsilon=E^4(\ln(E))^2+E^3\ln(E)\ln(\epsilon^{-1})}.
#'
#' If both arguments `n` and `error` are specified by the user, the number of linear extensions actually generated
#' is `n`.
#'
#' @param output_every_sec Integer specifying a time interval (in seconds).
#' By specifying this argument, during the execution of `BubleyDyerMRP`, a message reporting the number of linear extensions
#' progressively generated is printed on the R-Console, every `output_every_sec` seconds.
#'
#' @return
#' A list of two elements: 1) the matrix of approximated MRP and 2) the number of linear extensions generated to compute it.
#'
#' @details The function `BubleyDyerMRP` allows the user to update a previously computed approximated
#' MRP matrix, so as to improve estimation accuracy.
#' Specifically, the argument `generator` internally stores the approximated MRP matrix
#' computed at each execution of `BubleyDyerMRP`.
#' At the subsequent call (with the same `generator` argument), the previously computed MRP
#' are updated, based on the newly sampled linear extensions.
#' In this case, the number of additional linear extensions is determined either directly, by parameter `n`,
#' or indirectly, by specifying parameter `error`, which sets the desired
#' "distance" from uniformity of the sampling distribution of linear extensions, in the Bubley-Dyer procedure.
#' In the latter case, the number of additional linear extensions is computed as
#' \eqn{n_\epsilon-n_a}, where \eqn{n_\epsilon} is the number of linear extensions necessary to achive the desired "distance"
#' and \eqn{n_a} is the total number of linear extensions generated in the previous calls of `BubleyDyerEvaluation`.
#' If \eqn{n_\epsilon-n_a\leq 0}, no further linear extensions are generated and a warning message is displayed.
#'
#' In case a newly computed approximated MRP matrix is desired, run `BubleyDyerMRP` with a `generator` argument
#' newly generated by function [BubleyDyerMRPGenerator()].

#'
#' @references Bubley, R., Dyer, M. (1999). Faster random generation of linear extensions.
#' Discrete Mathematics, 201, 81-88. https://doi.org/10.1016/S0012-365X(98)00333-1
#'
#'
#' @examples
#'\donttest{
#' el1 <- c("a", "b", "c")
#' el2 <- c("x", "y", "z")
#' el3 <- c("h", "k")
#' dom <- matrix(c(
#'   "a", "b",
#'   "c", "b"
#' ), ncol = 2, byrow = TRUE)
#'
#' pos1 <- POSet(elements = el1, dom = dom)
#'
#' pos2 <- LinearPOSet(elements = el2)
#'
#' pos3 <- LinearPOSet(elements = el3)
#'
#' pos <- ProductPOSet(pos1, pos2, pos3)
#'
#' BDgen <- BubleyDyerMRPGenerator(pos)
#' #MRP computation with parameter n
#' res <- BubleyDyerMRP(BDgen, n=700000, output_every_sec=1)
#' #MRP refinement with parameter n
#' res <- BubleyDyerMRP(BDgen, n=100000, output_every_sec=1)
#' #MRP refinement with parameter error
#' res <- BubleyDyerMRP(BDgen, error=0.05, output_every_sec=1)
#' #Attempt to further refine MRP with parameter `error=0.05` does not modify previous result
#' res <- BubleyDyerMRP(BDgen, error=0.05, output_every_sec=1)
#'
#' #new MRP computation with parameter error
#' BDgen <- BubleyDyerMRPGenerator(pos)
#' res <- BubleyDyerMRP(BDgen, error=0.05, output_every_sec=1)
#'}
#'
#' @name BubleyDyerMRP
#' @export BubleyDyerMRP
BubleyDyerMRP <- function(generator, n=NULL, error=NULL, output_every_sec=NULL) {
  if (!methods::is(generator, "BubleyDyerMRPGenerator")) {
    stop("generator must be of class BubleyDyerMRPGenerator")
  }
  if (!is.null(n) && (n < 0 || n != round(n))) {
    stop("n must be a positive integer")
  }
  if (!is.null(error) && (!is.numeric(error) || length(error) != 1 || (error <= 0.0) || (error >= 1.0))) {
    stop("error must be in (0, 1)")
  }
  if (!is.null(output_every_sec) && (output_every_sec < 0 || output_every_sec != round(output_every_sec))) {
    stop("output_every_sec must be a positive integer")
  }
  if (!is.null(n)) {
    n <- as.integer(n)
  }
  if (!is.null(output_every_sec)) {
    output_every_sec <- as.integer(output_every_sec)
  }

  tryCatch({
    if (is.null(n) && is.null(error)) {
      stop("parameter n or parameter error must be provided.")
    }
    m <- .Call("_BubleyDyerMRP", generator@ptr, n, error, output_every_sec)
    return (m)
  }, error = function(err) {
    err_split <- strsplit(err[[1]], split = ":")
    stop(err_split[[1]][length(err_split[[1]])])
  }) # END tryCatch
}
