#' @title Dimensionality reduction of multidimensional ordinal binary data
#'
#' @description Starting from a dataset with \eqn{n} statistical units, scored against \eqn{k}
#' ordinal 0/1-indicators and partially ordered component-wise into a Boolean lattice \eqn{B_k=(\{0,1\}^k,\leq_{cmp})},
#' it finds the bidimensional data representation that optimally preserves the input order relation.
#' The algorithm finding the best bidimensional representation is optimized by using a parallel C++ implementation.
#'
#' @param profile Boolean matrix of dimension \eqn{m\times k} of the unique \eqn{m\leq n} different observed profiles.
#' Each observed profile is a row of `profile`. Each observed profile is repeated only once in the matrix `profile`.
#'
#' @param weights real vector of length \eqn{m} with the frequencies/weights of each observed profiles. Element
#' of position \eqn{j} in vector `weights` is the frequency/weight of the profile in row \eqn{j} of `profile`.
#'
#' @param output_every_sec Integer specifying a time interval (in seconds).
#' By specifying this argument, during the execution of `OptimalBidimensionalEmbedding`, a message reporting the number of reversed pairs
#' of lexicographic linear extensions analyzed is printed on the R-Console, every `output_every_sec` seconds. Note that the number
#' of reversed pairs of lexicographic linear extensions to be analyzed is \eqn{k!/2}.
#'
#' @param thread_share real number in the interval \eqn{(0,1])} specifying the share of CPU threads to be involved
#' in the algorithm execution.
#'
#' @return a list of 5 elements named `allLoss`, `variablesPriority`, `bestLossVAlue`, `bestVariablePriority`, and `bestRepresentation`.
#'
#' `allLoss` is a vector of dimension \eqn{k!/2} reporting the value of the loss function \eqn{L(D^{out}|D^{inp},p)}
#' corresponding to the representation induced by each reversed pairs of lexicographic linear extensions. This loss function measures the global errors
#' made in approximating the order structure of the input Boolean Lattice \eqn{B_k} with its bidimensional representations.
#'
#' `variablesPriority` is a matrix with \eqn{k!/2} rows and \eqn{k} columns. Each row is an integer vector of dimension \eqn{k} containing a
#' permutation \eqn{i_1,...,i_k} of \eqn{1,...,k}.
#' This vector specifies the criterion to build the reversed pair of lexicographic linear extensions used to
#' approximate \eqn{B_k}. The first linear extension is built by ordering profiles first according to their
#' scores on \eqn{V_{i_1}}, then to the scores on \eqn{V_{i_{2}}} and so on, until \eqn{V_{i_{k}}}; the second linear extension
#' is built by ordering profiles first according to their scores on \eqn{V_{i_k}}, then to the scores on \eqn{V_{i_{k-1}}} and so on, until \eqn{V_{i_{1}}}.
#' The \eqn{j}-th row of `variablesPriority` identifies the reversed pair of lexicographic linear extensions inducing the bidimensional representation
#' associated to the \eqn{j}-th global loss in `allLoss`.
#'
#' `bestLossVAlue` real number indicating the minimum value of the global error \eqn{L(D^{out}|D^{inp},p)} among the \eqn{k!/2} global errors associated
#' to the different pairs of reversed lexicographic linear extensions.
#'
#' `bestVariablePriority` integer vector of dimension \eqn{k} containing the permutation of \eqn{1,...,k} inducing the best bidimensional representation,
#' i.e. the bidimensional representation with associated global error `bestLossVAlue`.
#'
#' `bestRepresentation`  a data frame with \eqn{m} values (one value for each observed profile) of 5 variables named `profiles`, `x`, `y`, `weights` and `error`.
#' `$profile` is an integer vector containing the base-10 representation of the \eqn{k}-dimensional Boolean vectors representing observed profiles.
#' `$x` is an integer vector containing the x-coordinates of points representing observed profiles in the optimal bidimensional representation.
#' `$y` is an integer vector containing the y-coordinates of points representing observed profiles in the optimal bidimensional representation.
#' `$weights` is a real vector with the frequencies/weights of each observed profile.
#' `$error` is a real vector with the values of the approximation errors \eqn{L(b|D^{inp}, p)} associated to each observed profile
#' in the optimal bidimensional representation.
#'
#' @examples
#' #SIMULATING OBSERVED BINARY DATA
#' #number of binary variables
#' k <- 6
#' #building observed profiles matrix
#' profiles <- sapply((0:(2^k-1)) ,function(x){ as.integer(intToBits(x))})
#' profiles <- t(profiles[1:k, ])
#' #building the vector of observation frequencies
#' weights <- sample.int(100, nrow(profiles), replace=TRUE)
#' #FINDING THE OPTIMAL BIDIMENSIONAL REPRESENTATION
#' result <- OptimalBidimensionalEmbedding(profiles, weights)
#'
#'
#' @name OptimalBidimensionalEmbedding
#' @export OptimalBidimensionalEmbedding
OptimalBidimensionalEmbedding <- function(profile, weights, output_every_sec=NULL, thread_share=1.0) {
  loss="LB"
  lossFunctions <- c("LB")
  if (!is.matrix(profile) || !all(profile %in% c(0,1))) {
    stop("profile must be a 0/1 matrix")
  }
  mode(profile) <- "integer"
  if (!is.vector(weights)) {
    stop("weights must be a numeric vector")
  }

  if (length(weights) != nrow(profile)) {
    stop("weights length must be equal to profile'rows")
  }
  mode(weights) <- "numeric"
  if (length(which(weights == 0)) > 0) {
    profile <- profile[-which(weights == 0), ]
    weights <- weights[-which(weights == 0)]
  }
  if (!(loss %in% lossFunctions)) {
    stop("loss TO_DO")
  }

  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(output_every_sec)) {
    output_every_sec <- as.integer(output_every_sec)
  }

  if (!is.null(thread_share) && (thread_share <= 0 || thread_share > 1.0)) {
    stop("thread_share must be in (0, 1]")
  }

  if (is.null(thread_share)) {
    thread_share <- 1.0
  }
  tryCatch({
    result <- .Call("_RunDimensionalityReduction", profile, weights, loss, output_every_sec, thread_share)
    result[["bestRepresentation"]] <- data.frame(result[["bestRepresentation"]])
    return (result)
  }, error = function(err) {
    err_split <- strsplit(err[[1]], split = ":")
    stop(err_split[[1]][length(err_split[[1]])])
  }) # END tryCatch
}
