R/model.R

## Copyright (C) 2013 Lars Simon Zehnder
#
# This file is part of finmix.
#
# finmix is free software: you can redistribute it and/or modify it
# under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# finmix is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with finmix. If not, see <http://www.gnu.org/licenses/>.

#' Finmix `model` class
#' 
#' @description 
#' This class specifies a finite mixture model. Entities are created from it by 
#' calling its constructor [model()].
#' 
#' @details 
#' A finite mixture model in the ` finmix` package is defined by its number of 
#' components `K`, the component distributions `dist`, the data dimension `r` 
#' and an indicator defining, if the model has fixed or unknown indicators. 
#' Finite mixture models for the following distributions can be constructed: 
#' 
#'  * Poisson,
#'  * Conditional Poisson,
#'  * Exponential,
#'  * Binomial,
#'  * Normal,
#'  * Multivariate Normal,
#'  * Student-t,
#'  * Multivariate Student-t.
#'  
#' Using the constructor [model()] a finite mixture model can be created, the 
#' default being a mixture model of Poisson distributions. 
#' 
#' ## Fully defined finite mixture models
#' A fully defined finite mixture model contains next to the distribution and 
#' the components also weights and parameters. The weights are defined in slot 
#' `weight` and must be of class ` matrix` with as many weights as there are 
#' components in the mixture model (dimension `Kx1`). Parameters are defined in 
#' a ` list` named `par`. The elements of this list depend on the chosen 
#' distribution in slot `dist`: 
#' 
#'  * Poisson: A `matrix` named `lambda` of dimension `Kx1` holding the rate 
#'    parameters.
#'  * Exponential: A `matrix` named `lambda` of dimension `Kx1` holding the rate 
#'    parameters.
#'  * Binomial: A `matrix` of dimension `Kx1` named `p` storing the 
#'    probabilities.
#'  
#' 
#'  
#' 
#' @slot dist A character, defining the distribution family. Possible choices
#' are binomial, exponential, normal, normult, poisson, student, and studmult.
#' @slot r An integer. Defines the vector dimension of a model. Is one for all
#' univariate distributions and larger than one for normult and studmult.
#' @slot K An integer, defining the number of components in the finite mixture.
#' @slot weight A matrix, containing the weights of the finite mixture model. 
#' The matrix must have dimension \code{1 x K} and weights must add to one
#' must all be larger or equal to zero. 
#' @slot par A list containing the parameter vectors for the finite mixture 
#' distribution. The list can contain more than one named parameter vector. 
#' @slot indicmod A character defining the indicator model. So far only 
#' multinomial indicator models are possible. 
#' @slot indicfix A logical. If \code{TRUE} the indicators are given and
#' therefore fixed. 
#' @slot T A matrix containing the repetitions in case of a \code{"binomial"} or 
#'  \code{"poisson"} model.
#' @exportClass model
#' @rdname model-class
#' 
#' @seealso 
#' * [mixturemcmc()] for performing MCMC sampling with a mixture model
#' * [modelmoments()] for compute theoretical moments of a finite mixture model
.model <- setClass("model",
  representation(
    dist = "character",
    r = "integer",
    K = "integer",
    weight = "matrix",
    par = "list",
    indicmod = "character",
    indicfix = "logical",
    T = "matrix"
  ),
  validity = function(object) {
    .init.valid.Model(object)
    ## else: OK ##
    TRUE
  },
  prototype(
    dist = character(),
    r = integer(),
    K = integer(),
    weight = matrix(),
    par = list(),
    indicmod = character(),
    indicfix = logical(),
    T = matrix()
  )
)

#' Constructor for the S4 model class
#' 
#' \code{model} creates a finite mixture model with given parameters. 
#' 
#' This is a constructor that creates a class object and guides the user in 
#' regard to the different parameters needed to define a finite mixture model.
#' 
#' @param dist A character, defining the distribution family. Possible choices
#' are \code{"binomial"}, \code{"exponential"}, \code{"normal"}, 
#' \code{"normult"}, \code{"poisson"}, \code{"student"}, and \code{"studmult"}.
#' @param r An integer. Defines the vector dimension of a model. Is one for all
#' univariate distributions and larger than one for \code{"normult"} and 
#' \code{"studmult"}.
#' @param K An integer, defining the number of components in the finite mixture. 
#' Must be larger or equal to one. 
#' @param weight A matrix, containing the weights of the finite mixture model. 
#' The matrix must have dimension \code{1 x K} and weights must add to one
#' and must all be larger or equal to zero. 
#' @param par A list containing the parameter vectors for the finite mixture 
#' distribution. The list can contain more than one named parameter vector. 
#' Depending on the distribution parameters must be defined in the list as 
#' follows: a \code{K}-dimensional vector of probabilities named \code{"p"} for 
#' a \code{"binomial"} model, a \code{K}-dimensional vector of positive rates 
#' named \code{"lambda"} for an \code{"exponential"} model, 
#' \code{K}-dimensional vectors of means named \code{"mu"} and variances named 
#' \code{sigma} for a \code{"normal"} model, a \code{r x K}-dimensional 
#' matrix of means named \code{"mu"} and a \code{K x r x r} dimensional
#' array of variance-covariance matrices named \code{"sigma"} for a 
#' \code{"normult"} model, a \code{K}-dimensional vector of rates named 
#' \code{"rates"} for a \code{"poisson"} model, \code{K}-dimensional vectors of 
#' means named \code{"mu"}, variances named \code{sigma}, and degrees of freedom
#'  named \code{"df"} for a \code{"student"} model, a 
#' \code{r x K}-dimensional matrix of means named \code{"mu"}, a 
#' \code{K x r x r} dimensional array of variance-covariance matrices 
#' named \code{"sigma"}, and a \code{K}-dimensional vector of degrees of freedom
#'  for a \code{"studmult"} model.
#' @param indicmod A character defining the indicator model used. For now only
#' \code{"multinomial"} is implemented.
#' @param indicfix A logical. If \code{TRUE} the indicators are given and
#' therefore fixed. 
#' @param T A matrix containing the repetitions in case of a \code{"binomial"} or 
#'  \code{"poisson"} model. Must be positive integers.
#' @return An S4 `model` object.
#' @export
#' 
#' @examples 
#' f_model <- model(dist = "poisson", K = 2, par = list(lambda = c(0.17, 0.2)))
#' 
#' @seealso 
#' * [model][model-class] for the class definition
"model" <- function(dist = "poisson", r, K,
                    weight = matrix(), par = list(),
                    indicmod = "multinomial",
                    indicfix = FALSE, T = matrix()) {
  if (missing(K)) {
    K <- .check.K.Model(weight)
  } else {
    K <- as.integer(K)
    if (K == 1 && dist == "cond.poisson") {
      dist <- "poisson"
    }
  }
  if (missing(r)) {
    r <- .check.r.Model(dist)
  } else {
    r <- as.integer(r)
  }
  if (missing(weight) && K > 1) {
    weight <- .check.weight.Model(K)
  } else {
    weight <- as.matrix(weight)
  }
  if (!missing(T)) {
    T <- .check.T.Model(T)
  } else {
    if (dist == "binomial") {
      T <- matrix(as.integer(1))
    }
  }

  .model(
    dist = dist, r = r, K = K, weight = weight,
    par = par, indicmod = indicmod,
    indicfix = indicfix, T = T
  )
}

#' Getter for weights
#'
#' \code{hasWeight} returns the weight matrix. 
#' 
#' @param model An S4 model object. 
#' @param verbose A logical indicating, if the function should give a print out.
#' @return Matrix of weights.
#' @exportMethod hasWeight
#' @keywords internal
#' 
#' @examples
#' \dontrun{
#' weight <- hasWeight(model)
#' }
setMethod(
  "hasWeight", "model",
  function(object, verbose = FALSE) {
    if (!all(is.na(object@weight))) {
      if (ncol(object@weight) == object@K) {
        return(TRUE)
      } else {
        if (verbose) {
          stop(paste("Wrong dimension of ",
            "slot 'weight' of ",
            "'model' object.",
            "Weights must be of ",
            "dimension 1 x K.",
            sep = ""
          ))
        } else {
          return(FALSE)
        }
      }
    } else {
      if (verbose) {
        stop(paste("Slot 'weight' of 'model' ",
          "object is empty.",
          sep = ""
        ))
      } else {
        return(FALSE)
      }
    }
  }
)

#' Checks for repetitions.
#' 
#' \code{hasT} chwecks if the model object possesses repetitions.
#' 
#' @param model An S4 model object.
#' @param verbose A logical indicating if the function should give a print out. 
#' @return A logical. \code{TRUE} if repetitions are existent in the model. If 
#' values of slot \code{T} are \code{NA} it returns \code{FALSE}.
#' @exportMethod hasT
#' @keywords internal
#' 
#' @examples
#' \dontrun{
#' if(hasT(model)) {cat('Has repetitions.')}
#' }
#' 
#' @seealso \code{model}
setMethod(
  "hasT", "model",
  function(object, verbose = FALSE) {
    if (!all(is.na(object@T))) {
      return(TRUE)
    } else {
      if (verbose) {
        stop(paste("Slot 'T' of 'model' ",
          "object is empty.",
          sep = ""
        ))
      } else {
        return(FALSE)
      }
    }
  }
)

#' Checks for parameters.
#' 
#' \code{hasPar} checks if the model has parameters defined. 
#' 
#' @param object An S4 model object.
#' @param verbose A logical indicating, if the function should give a print out. 
#' @return A matrix with repetitions. Can be empty, if no repetitions are set.
#' @exportMethod hasPar
#' @keywords internal
#' 
#' @examples 
#' \dontrun{
#' if(hasPar(model)) {simulate(model)}
#' }
#' 
#' @seealso 
#' * [model-class] for the class definition
setMethod(
  "hasPar", "model",
  function(object, verbose = FALSE) {
    .haspar.Model(object, verbose)
  }
)

#' Simulates data from a model. 
#' 
#' `simulate()` simulates values for a specified mixture model in an 
#' S4 `model` object.
#' 
#' @param model An S4 model object with specified parameters and weights.
#' @param N An integer specifying the number of values to be simulated. 
#' @param varargin An S4 fdata object with specified variable dimensions, `r` 
#'   and repetitions `T`. 
#' @param seed An integer specifying the seed for the RNG. 
#' @return An S4 fdata object holding the simulated values.
#' @exportMethod simulate
#' @keywords internal
#' 
#' @examples 
#' \dontrun{
#' f_data <- simulate(model, 100)
#' }
#' 
#' @seealso 
#' * [model-class] for the class definition
#' * [fdata-class] for the class defining `finmix` data objects
setMethod(
  "simulate", "model",
  function(model, N = 100, varargin, seed = 0) {
    ## TODO: Check model for parameters. Check varargin for dimension. Check
    ##      model and varargin for consistency.
    if (!missing(seed)) {
      set.seed(seed)
    } ## Implemented maybe finmixOptions with a state variable seed
    if (!hasWeight(model)) {
      model@weight <- matrix(1 / model@K, nrow = 1, ncol = model@K)
    }
    ## Start simulating the allocations
    S <- .simulate.indicators.Model(model, N)
    if (missing(varargin)) {
      varargin <- fdata(
        r = model@r, T = matrix(1, nrow = N),
        exp = matrix(1, nrow = N), S = S
      )
    } else {
      varargin@S <- S
    }
    if (hasPar(model, verbose = TRUE)) {
      .simulate.data.Model(model, N, varargin)
    }
  }
)

#' Plots a model.
#' 
#' \code{plot} plots the density or probabilities of a fully specified mixture 
#' model.
#' 
#' @param x An S4 model object. Must have specified parameters and weights.
#' @param y Unused.
#' @param dev A logical indicating, if the plot should be shown in a graphical 
#'   device. Set to \code{FALSE}, if plotted to a file. 
#' @param ...	 Arguments to be passed to methods, such as graphical parameters 
#'   (see par).
#' @return Density or barplot of the S4 model object. 
#' @exportMethod plot
#' @keywords internal
#' 
#' @examples \dontrun{
#' plot(f_model)
#' }
#' 
#' @seealso 
#' * [model-class] for the class definition
#' * [model()] for the class constructor
setMethod(
  "plot", "model",
  function(x, y, dev = TRUE, ...) {
    dist <- x@dist
    if (dist == "normal") {
      .plot.Normal.Model(x, dev, ...)
    } else if (dist == "normult") {
      .plot.Normult.Model(x, dev, ...)
    } else if (dist == "exponential") {
      .plot.Exponential.Model(x, dev, ...)
    } else if (dist == "student") {
      .plot.Student.Model(x, dev, ...)
    } else if (dist == "studmult") {
      .plot.Studmult.Model(x, dev, ...)
    } else if (dist %in% c("poisson", "cond.poisson")) {
      .plot.Poisson.Model(x, dev, ...)
    } else if (dist == "binomial") {
      if (abs(max(x@T) - min(x@T)) > 1e-6) {
        stop("Plotting a binomial distribution with varying
                           repetitions in slot 'T' is not possible.")
      }
      .plot.Binomial.Model(x, dev, ...)
    }
  }
)

#' Plots the point process of a finite model
#' 
#' \code{plotPointProc} plots the point process of an S4 model object that 
#' defines a finite mixture model. Only available for Poisson mixtures so far.
#' 
#' @param x An S4 model object with defined parameters and weights. 
#' @param dev A logical indicating, if the plot should be shown in a graphical 
#'   device. Set to \code{FALSE}, if plotted to a file. 
#' @param ...	 Arguments to be passed to methods, such as graphical parameters 
#'   (see [par]).
#' @return A scatter plot of weighted parameters.  
#' @exportMethod plotPointProc
#' @keywords internal
#' 
#' @examples 
#' \dontrun{
#' plotPointProc(f_model)
#' }
#' 
#' @seealso \code{model}
setMethod(
  "plotPointProc", signature(
    x = "model",
    dev = "ANY"
  ),
  function(x, dev = TRUE, ...) {
    hasPar(x, verbose = TRUE)
    hasWeight(x, verbose = TRUE)
    if (x@dist == "poisson") {
      .plotpointproc.Poisson(x, dev)
    }
  }
)

## Marginal Mixture ##
#' Returns the marginal distribution. 
#' 
#' \code{mixturemar} returns the marginal distribution of a multivariate 
#' mixture distribution. This can only be applied on S4 model objects with 
#' \code{dist="normult"} or \code{dist="studmult"}. 
#' 
#' @param object An S4 model object with a multivariate distribution.
#' @param J An integer specifying the dimension for which the marginal 
#' distribution should be returned.
#' @return An S4 model object with the marginal distribution for dimension 
#' \code{J}.
#' @exportMethod mixturemar
#' 
#' @examples
#' \dontrun{
#' mar_model <- mixturemar(f_model, 1)
#' }
#' 
#' @seealso \code{model}
setMethod(
  "mixturemar", "model",
  function(object, J) {
    .mixturemar.Model(object, J)
  }
)

#' Shows the model.
#' 
#' \code{show} prints model information to the console. 
#' 
#' @param object An S4 model object. 
#' @return A print out of model information about all slots. 
#' @exportMethod show
#' @keywords internal
#' 
#' @examples 
#' \dontrun{
#' show(f_model)
#' }
#' 
#' @seealso 
#' * [model-class] for the class definition
setMethod(
  "show", "model",
  function(object) {
    cat("Object 'model'\n")
    cat("     class       :", class(object), "\n")
    cat("     dist        :", object@dist, "\n")
    cat("     r           :", object@r, "\n")
    cat("     K           :", object@K, "\n")
    if (hasPar(object)) {
      cat(
        "     par         : List of",
        length(object@par), "\n"
      )
    }
    if (!object@indicfix) {
      cat(
        "     weight      :",
        paste(dim(object@weight), collapse = "x"),
        "\n"
      )
    }
    cat("     indicmod    :", object@indicmod, "\n")
    cat("     indicfix    :", object@indicfix, "\n")
    if (object@dist == "binomial" && !all(is.na(object@T))) {
      cat(
        "     T           :",
        paste(dim(object@T), collapse = "x"), "\n"
      )
    }
  }
)

## Getters ##

#' Getter method of `model` class.
#' 
#' Returns the `dist` slot.
#' 
#' @param object An `model` object.
#' @returns The `dist` slot of the `object`.
#' @exportMethod getDist
#' @keywords internal
#' 
#' @examples 
#' # Generate an exponential mixture model with two components.
#' f_model <- model("exponential", par = list(lambda = c(0.3, 0.7)), K = 2)
#' # Get the slot
#' getDist(f_model)
#' 
#' @seealso 
#' * [model-class] for the class definition
setMethod(
  "getDist", "model",
  function(object) {
    return(object@dist)
  }
)

#' Getter method of `model` class.
#' 
#' Returns the `r` slot.
#' 
#' @param object An `model` object.
#' @returns The `r` slot of the `object`.
#' @exportMethod getR
#' @keywords internal
#' 
#' @examples 
#' # Generate an exponential mixture model with two components.
#' f_model <- model("exponential", par = list(lambda = c(0.3, 0.7)), K = 2)
#' # Get the slot
#' getR(f_model)
#' 
#' @seealso 
#' * [model-class] for the class definition
setMethod(
  "getR", "model",
  function(object) {
    return(object@r)
  }
)

#' Getter method of `model` class.
#' 
#' Returns the `K` slot.
#' 
#' @param object An `model` object.
#' @returns The `K` slot of the `object`.
#' @exportMethod getK
#' @keywords internal
#' 
#' @examples 
#' # Generate an exponential mixture model with two components.
#' f_model <- model("exponential", par = list(lambda = c(0.3, 0.7)), K = 2)
#' # Get the slot
#' getK(f_model)
#' 
#' @seealso 
#' * [model-class] for the class definition
setMethod(
  "getK", "model",
  function(object) {
    return(object@K)
  }
)

#' Getter method of `model` class.
#' 
#' Returns the `weight` slot.
#' 
#' @param object An `model` object.
#' @returns The `weight` slot of the `object`.
#' @exportMethod getWeight
#' @keywords internal
#' 
#' @examples 
#' # Generate an exponential mixture model with two components.
#' f_model <- model("exponential", par = list(lambda = c(0.3, 0.7)), K = 2)
#' # Get the slot
#' getWeight(f_model)
#' 
#' @seealso 
#' * [model-class] for the class definition
setMethod(
  "getWeight", "model",
  function(object) {
    return(object@weight)
  }
)

#' Getter method of `model` class.
#' 
#' Returns the `par` slot.
#' 
#' @param object An `model` object.
#' @returns The `par` slot of the `object`.
#' @exportMethod getPar
#' @keywords internal
#' 
#' @examples 
#' # Generate an exponential mixture model with two components.
#' f_model <- model("exponential", par = list(lambda = c(0.3, 0.7)), K = 2)
#' # Get the slot
#' getPar(f_model)
#' 
#' @seealso 
#' * [model-class] for the class definition
setMethod(
  "getPar", "model",
  function(object) {
    return(object@par)
  }
)

#' Getter method of `model` class.
#' 
#' Returns the `indicmod` slot.
#' 
#' @param object An `model` object.
#' @returns The `indicmod` slot of the `object`.
#' @exportMethod getIndicmod
#' @keywords internal
#' 
#' @examples 
#' # Generate an exponential mixture model with two components.
#' f_model <- model("exponential", par = list(lambda = c(0.3, 0.7)), K = 2)
#' # Get the slot
#' getIndicmod(f_model)
#' 
#' @seealso 
#' * [model-class] for the class definition
setMethod(
  "getIndicmod", "model",
  function(object) {
    return(object@indicmod)
  }
)

#' Getter method of `model` class.
#' 
#' Returns the `indicfix` slot.
#' 
#' @param object An `model` object.
#' @returns The `indicfix` slot of the `object`.
#' @exportMethod getIndicfix
#' @keywords internal
#' 
#' @examples 
#' # Generate an exponential mixture model with two components.
#' f_model <- model("exponential", par = list(lambda = c(0.3, 0.7)), K = 2)
#' # Get the slot
#' getIndicfix(f_model)
#' 
#' @seealso 
#' * [model-class] for the class definition
setMethod(
  "getIndicfix", "model",
  function(object) {
    return(object@indicfix)
  }
)

#' Getter method of `model` class.
#' 
#' Returns the `T` slot.
#' 
#' @param object An `model` object.
#' @returns The `T` slot of the `object`.
#' @exportMethod getT
#' @keywords internal
#' 
#' @examples 
#' # Generate an exponential mixture model with two components.
#' f_model <- model("exponential", par = list(lambda = c(0.3, 0.7)), K = 2)
#' # Get the slot
#' getT(f_model)
#' 
#' @seealso 
#' * [model-class] for the class definition
setMethod(
  "getT", "model",
  function(object) {
    return(object@T)
  }
)

## Setters ##
#' Setter method of `model` class.
#' 
#' Sets a value for the `dist` slot.
#' 
#' @param object An `model` object.
#' @param value A character defining the distribution.
#' @returns The `model` object with slot `dist` set to `value`.
#' @exportMethod setDist<-
#' @keywords internal
#' 
#' @examples 
#' # Generate an default mixture model.
#' f_model <- model()
#' # Get the slot
#' setDist(f_model) <- "poisson"
#' 
#' @seealso 
#' * [model-class] for the class definition
setReplaceMethod(
  "setDist", "model",
  function(object, value) {
    object@dist <- value
    .valid.dist.Model(object)
    return(object)
  }
)

#' Setter method of `model` class.
#' 
#' Sets a value for the `r` slot.
#' 
#' @param object An `model` object.
#' @param value A character defining the distribution.
#' @returns The `model` object with slot `r` set to `value`.
#' @exportMethod setR<-
#' @keywords internal
#' 
#' @examples 
#' # Generate an default mixture model.
#' f_model <- model()
#' # Set the slot.
#' setR(f_model) <- 1
#' 
#' @seealso 
#' * [model-class] for the class definition
setReplaceMethod(
  "setR", "model",
  function(object, value) {
    object@r <- as.integer(value)
    validObject(object)
    return(object)
  }
)

#' Setter method of `model` class.
#' 
#' Sets a value for the `K` slot.
#' 
#' @param object An `model` object.
#' @param value An integer specifying the number of components.
#' @returns The `model` object with slot `K` set to `value`.
#' @exportMethod setK<-
#' @keywords internal
#' 
#' @examples 
#' # Generate an default mixture model.
#' f_model <- model()
#' # Set the slot.
#' setK(f_model) <- 2
#' 
#' @seealso 
#' * [model-class] for the class definition
setReplaceMethod(
  "setK", "model",
  function(object, value) {
    object@K <- as.integer(value)
    .valid.K.Model(object)
    if (object@K > 1) {
      object@weight <- .check.weight.Model(object@K)
    } else {
      weight <- matrix()
      storage.mode(weight) <- "numeric"
      object@weight <- weight
    }
    return(object)
  }
)

#' Setter method of `model` class.
#' 
#' Sets a value for the `weight` slot.
#' 
#' @param object An `model` object.
#' @param value An matrix specifying the weights.
#' @returns The `model` object with slot `weight` set to `value`.
#' @exportMethod setWeight<-
#' @keywords internal
#' 
#' @examples 
#' # Generate an default mixture model.
#' f_model <- model()
#' # Set the slot.
#' setWeight(f_model) <- matrix(c(0.4, 0.6), nrow = 1)
#' 
#' @seealso 
#' * [model-class] for the class definition
setReplaceMethod(
  "setWeight", "model",
  function(object, value) {
    object@weight <- as.matrix(value)
    object@K <- ncol(object@weight)
    .valid.weight.Model(object)
    return(object)
  }
)

#' Setter method of `model` class.
#' 
#' Sets a value for the `par` slot.
#' 
#' @param object An `model` object.
#' @param value A list specifying the component parameters.
#' @returns The `model` object with slot `par` set to `value`.
#' @exportMethod setPar<-
#' @keywords internal
#' 
#' @examples 
#' # Generate an default mixture model.
#' f_model <- model()
#' # Set the number of components to two.
#' setK(f_model) <- 2
#' # Set the slot.
#' setPar(f_model) <- list(lambda=c(0.2, 0.7))
#' 
#' @seealso 
#' * [model-class] for the class definition
setReplaceMethod(
  "setPar", "model",
  function(object, value) {
    object@par <- value
    .valid.par.Model(object)
    return(object)
  }
)

#' Setter method of `model` class.
#' 
#' Sets a value for the `indicmod` slot.
#' 
#' @param object An `model` object.
#' @param value An character specifying the indicator model. 
#' @returns The `model` object with slot `indicmod` set to `value`.
#' @exportMethod setK<-
#' @keywords internal
#' 
#' @examples 
#' # Generate an default mixture model.
#' f_model <- model()
#' # Set the slot.
#' setK(f_model) <- 2
#' 
#' @seealso 
#' * [model-class] for the class definition
setReplaceMethod(
  "setIndicmod", "model",
  function(object, value) {
    object@indicmod <- value
    return(object)
  }
)

#' Setter method of `model` class.
#' 
#' Sets a value for the `indicfix` slot.
#' 
#' @param object An `model` object.
#' @param value A logical specifying, if the model is one with fixed indicators.
#' @returns The `model` object with slot `indicfix` set to `value`.
#' @exportMethod setIndicfix<-
#' @keywords internal
#' 
#' @examples 
#' # Generate an default mixture model.
#' f_model <- model()
#' # Set the slot.
#' setIndicfix(f_model) <- TRUE
#' 
#' @seealso 
#' * [model-class] for the class definition
setReplaceMethod(
  "setIndicfix", "model",
  function(object, value) {
    object@indicfix <- value
    return(object)
  }
)

#' Setter method of `model` class.
#' 
#' Sets a value for the `T` slot.
#' 
#' @param object An `model` object.
#' @param value An integer specifying the number of components.
#' @returns The `model` object with slot `T` set to `value`.
#' @exportMethod setT<-
#' @keywords internal
#' 
#' @examples 
#' # Generate an default mixture model.
#' f_model <- model()
#' # Set the slot.
#' setT(f_model) <- as.integer(4)
#' 
#' @seealso 
#' * [model-class] for the class definition
setReplaceMethod(
  "setT", "model",
  function(object, value) {
    object@T <- matrix(value)
    .valid.T.Model(object)
    return(object)
  }
)

### Private functions
### These functions are not exported

### Checking.
### Checking is used for in the constructor.
### Arguments for the slots are checked for validity and
### if missing are given by default values. Altogether the
### constructor tries to construct a fully specified model
### object with consistent slots.

### Check K: If weights are provided by the user, the number
### of components is set to the number of columns of the weights.
### If argument 'weight' is missing from the call, the number of
### components is assumed to be one.

#' @noRd
".check.K.Model" <- function(weight) {
  if (!all(is.na(weight))) {
    return(NCOL(weight))
  } else {
    return(as.integer(1))
  }
}

### Check r: The dimension of the model is determined in regard to
### the defined distribution in argument 'dist' (if missing the
### default is 'poisson'). For univariate distributions it is set
### to one and for multivariate distribution as a default to two.
#' @noRd
".check.r.Model" <- function(dist) {
  univ <- .get.univ.Model()
  multiv <- .get.multiv.Model()
  if (dist %in% univ) {
    return(as.integer(1))
  } else if (dist %in% multiv) {
    return(as.integer(2))
  } else {
    stop(paste("Unknown distribution in slot ",
      "'dist' of 'model' object.",
      sep = ""
    ))
  }
}

### Check weight: If argument 'weight' is missing from the call
### equally balanced weights are given as a default.
#' @noRd
".check.weight.Model" <- function(K) {
  weight <- matrix(1 / K, nrow = 1, ncol = K)
  return(weight)
}

### Check T: If repetitions are given they are checked in regard
### to validity. In case of non-numeric objects an error is thrown.
### In case of objects of type 'numeric' it is implicitly converted
### to type 'integer'.
#' @noRd
".check.T.Model" <- function(T) {
  if (!all(is.na(T))) {
    if (!is.numeric(T)) {
      stop(paste("Wrong specification of slot 'T' in ",
        "'model' object. Repetitions must be of ",
        "type 'integer'.",
        sep = ""
      ))
    } else {
      storage.mode(T) <- "integer"
      return(T)
    }
  }
}

### Marginal model
#' @noRd
".mixturemar.Model" <- function(obj, J) {
  if (obj@dist == "normult") {
    .mixturemar.normult.Model(obj, J)
  } else if (obj@dist == "studmult") {
    .mixturemar.studmult.Model(obj, J)
  } else {
    stop("A marginal distribution can only be obtained from 
         multivariate distributions.")
  }
}

#' @noRd
".mixturemar.normult.Model" <- function(obj, J) {
  dist <- ifelse(length(J) == 1, "normal", "normult")
  r <- length(J)
  K <- obj@K
  weight <- obj@weight
  mu <- obj@par$mu[J, ]
  sigma <- obj@par$sigma[J, J, ]
  par <- list(mu = mu, sigma = sigma)
  indicmod <- "multinomial"
  indicfix <- TRUE
  margin.model <- .model(
    dist = dist, r = r, K = K,
    weight = weight, par = par,
    indicmod = indicmod,
    indicfix = indicfix
  )
  validObject(margin.model)
  return(margin.model)
}

#' @noRd
".mixturemar.studmult.Model" <- function(obj, J) {
  dist <- ifelse(length(J) == 1, "student", "studmult")
  r <- length(J)
  K <- obj@K
  weight <- obj@weight
  mu <- obj@par$mu[J, ]
  sigma <- obj@par$sigma[J, J, ]
  df <- obj@par$df
  par <- list(mu = mu, sigma = sigma, df = df)
  indicmod <- "multinomial"
  indicfix <- TRUE
  margin.model <- .model(
    dist = dist, r = r, K = K,
    weight = weight, par = par,
    indicmod = indicmod,
    indicfix = indicfix
  )
  validObject(margin.model)
  return(margin.model)
}

### ==============================================================
### Simulate
### --------------------------------------------------------------

### --------------------------------------------------------------
### .simulate.indicators.Model
### @description    Simulates the indicators.
### @par    obj an S4 object of class 'model'
### @par    N   an R 'integer' object
### @return         an R 'matrix' object with N simulated indi-
###                 cators.
### @details        indicators are simulated via the slot @weight
###                 the 'model' object
### @see    ?simulate
### @author Lars Simon Zehnder
### --------------------------------------------------------------

### TODO: Implement C++ function.
#' @noRd
".simulate.indicators.Model" <- function(obj, N) {
  K <- obj@K
  if (K == 1) {
    S <- matrix(as.integer(1), nrow = N, ncol = K)
  } else {
    ## if (model@indicmod = "") -> "Multinomial"
    ## if Markov else
    if (obj@indicmod == "multinomial") {
      rnd <- runif(N)
      rnd <- matrix(rnd, nrow = N, ncol = K)
      weightm <- matrix(obj@weight,
        nrow = N, ncol = K,
        byrow = TRUE
      )
      S <- apply((t(apply(weightm, 1, cumsum)) < rnd), 1, sum) + 1
      S <- matrix(S, nrow = N)
      storage.mode(S) <- "integer"
    }
  }
  return(S)
}

### --------------------------------------------------------------------
### .simulate.data.Model
### @description    Simulates the simulation functions for a specific model.
### @par    obj         an S4 'model' object
### @par    N           an R 'integer' object; number of simulated values
### @par    fdata.obj   an S4 'fdata' object
### @return         an S4 object of class 'fdata' with simulated values
### @see    ?fdata, ?simulate
### @author Lars Simon Zehnder
### ---------------------------------------------------------------------
#' @noRd
".simulate.data.Model" <- function(obj, N, fdata.obj) {
  dist <- obj@dist
  if (dist == "poisson" || dist == "cond.poisson") {
    .simulate.data.poisson.Model(obj, N, fdata.obj)
  } else if (dist == "binomial") {
    .simulate.data.binomial.Model(obj, N, fdata.obj)
  } else if (dist == "exponential") {
    .simulate.data.exponential.Model(obj, N, fdata.obj)
  } else if (dist == "normal") {
    .simulate.data.normal.Model(obj, N, fdata.obj)
  } else if (dist == "student") {
    .simulate.data.student.Model(obj, N, fdata.obj)
  } else if (dist == "normult") {
    .simulate.data.normult.Model(obj, N, fdata.obj)
  }
}

### ---------------------------------------------------------------------
### .simulate.data.poisson.Model
### @description    Simulates values from a Poisson mixture using pre-
###                 specified model and indicators
### @par    obj         an S4 object of class 'model'
### @par    N           an R 'integer' object; number of simulated values
### @par    fdata.obj   an S4 object of class 'fdata'
### @return         an S4 object of class 'fdata' with simulated values
### @see    ?simulate, model:::.simulate.data.Model, ?rpois
### @author Lars Simon Zehnder
### ---------------------------------------------------------------------
#' Simulate data from a Poisson finite mixture model
#' 
#' @description 
#' Simulates values from a Poisson mixture using pre-specified model and 
#' indicators.
#' 
#' @param obj A `model` object specifying the finite mixture model. 
#' @param N An integer specifying the sample size.
#' @param fdata.obj An `fdata` object to store the simulated data.
#' @return An `fdata` object with simulated data.
#' @importFrom stats rpois
#' @noRd
".simulate.data.poisson.Model" <- function(obj, N, fdata.obj) {
  fdata.obj@type <- "discrete"
  fdata.obj@sim <- TRUE
  fdata.obj@y <- matrix(rpois(N, fdata.obj@exp * obj@par$lambda[fdata.obj@S]))
  return(fdata.obj)
}

#' Simulate data from Binomial mixture model
#' 
#' @description 
#' Simulates values from a Binomial mixture using pre-specified model and 
#' indicators
#' @param obj An `model` object specifying the mixture model.
#' @param N An integer specifying the size of the simulated sample.
#' @param fdata.obj An `fdata` object to store the simulated sample. If the 
#'   `fdata` object contains repetitions in slot `@@T`, the repetitions are 
#'   used in sampling.
#' @return An `fdata` object containing the simulated values.
#' @importFrom stats rbinom
#' @noRd
#' 
#' @seealso 
#' [simulate()][model-class] for the calling function
".simulate.data.binomial.Model" <- function(obj, N, fdata.obj) {
  if (!hasT(fdata.obj)) {
    fdata.obj@T <- as.matrix(1)
  }
  fdata.obj@type <- "discrete"
  fdata.obj@sim <- TRUE
  fdata.obj@y <- matrix(rbinom(N, fdata.obj@T, obj@par$p[fdata.obj@S]))
  return(fdata.obj)
}

#' Simulate data from exponential mixture model
#' 
#' @description 
#' Simulates values from a exponential mixture using pre-specified model and 
#' indicators
#' @param obj An `model` object specifying the mixture model.
#' @param N An integer specifying the size of the simulated sample.
#' @param fdata.obj An `fdata` object to store the simulated sample. 
#' @return An `fdata` object containing the simulated values.
#' @importFrom stats rexp
#' @noRd
#' 
#' @seealso 
#' [simulate()][model-class] for the calling function
".simulate.data.exponential.Model" <- function(obj, N, fdata.obj) {
  fdata.obj@type <- "continuous"
  fdata.obj@sim <- TRUE
  fdata.obj@y <- matrix(rexp(N, obj@par$lambda[fdata.obj@S]))
  return(fdata.obj)
}

#' Simulate data from Normal mixture model
#' 
#' @description 
#' Simulates values from a Normal mixture using pre-specified model and 
#' indicators
#' @param obj An `model` object specifying the mixture model.
#' @param N An integer specifying the size of the simulated sample.
#' @param fdata.obj An `fdata` object to store the simulated sample. 
#' @return An `fdata` object containing the simulated values.
#' @noRd
#' 
#' @seealso 
#' [simulate()][model-class] for the calling function
".simulate.data.normal.Model" <- function(obj, N, fdata.obj) {
  fdata.obj@type <- "continuous"
  fdata.obj@sim <- TRUE
  fdata.obj@y <- matrix(rnorm(
    N, obj@par$mu[fdata.obj@S],
    obj@par$sigma[fdata.obj@S]
  ))
  return(fdata.obj)
}

#' Simulate data from Student-t mixture model
#' 
#' @description 
#' Simulates values from a Student-t mixture using pre-specified model and 
#' indicators
#' @param obj An `model` object specifying the mixture model.
#' @param N An integer specifying the size of the simulated sample.
#' @param fdata.obj An `fdata` object to store the simulated sample. If the 
#'   `fdata` object contains repetitions in slot `@@T`, the repetitions are 
#'   used in sampling.
#' @return An `fdata` object containing the simulated values.
#' @importFrom stats rgamma
#' @noRd
#' 
#' @seealso 
#' [simulate()][model-class] for the calling function
".simulate.data.student.Model" <- function(obj, N, fdata.obj) {
  fdata.obj@type <- "continuous"
  fdata.obj@sim <- TRUE
  omega <- rgamma(N, obj@par$df[fdata.obj@S] / 2,
    rate = 2 / obj@par$df[fdata.obj@S]
  )
  fdata.obj@y <- as.matrix(obj@par$mu[fdata.obj@S] +
    sqrt(obj@par$sigma[fdata.obj@S] / omega) *
      rnorm(N, 0.0, 1.0))
  return(fdata.obj)
}

#' Simulate data from a multivariate Normal mixture model
#' 
#' @description 
#' Simulates values from a multivariate Normal mixture using pre-specified 
#' model and indicators
#' @param obj An `model` object specifying the mixture model.
#' @param N An integer specifying the size of the simulated sample.
#' @param fdata.obj An `fdata` object to store the simulated sample. If the 
#'   `fdata` object contains repetitions in slot `@@T`, the repetitions are 
#'   used in sampling.
#' @return An `fdata` object containing the simulated values.
#' @importFrom mvtnorm rmvnorm
#' @noRd
#' 
#' @seealso 
#' [simulate()][model-class] for the calling function
".simulate.data.normult.Model" <- function(obj, N, fdata.obj) {
  fdata.obj@type <- "continuous"
  fdata.obj@sim <- TRUE
  fdata.obj@y <- matrix(numeric(), nrow = N, ncol = obj@r)
  fdata.obj@r <- obj@r
  for (i in 1:N) {
    fdata.obj@y[i, ] <- rmvnorm(1,
      mean = obj@par$mu[, fdata.obj@S[i]],
      sigma = obj@par$sigma[, , fdata.obj@S[i]],
      method = "chol"
    )
  }
  return(fdata.obj)
}

### Plotting
### Plot Poisson models: Poisson models are discrete
### models and a barplot is used.
### The range for the x-axis is determined via the
### quantiles of the largest and smallest Poisson model
### in the mixture.
#' @importFrom stats qpois dpois
#' @importFrom grDevices axisTicks
#' @noRd 
".plot.Poisson.Model" <- function(model.obj, dev, ...) {
  if (.check.grDevice() && dev) {
    dev.new(title = "Model plot")
  }
  lambda <- model.obj@par$lambda
  weight <- model.obj@weight
  xlim.up <- qpois(.9999, lambda = max(lambda))
  xlim.low <- qpois(.0001, lambda = min(lambda))
  x.grid <- seq(xlim.low, xlim.up, by = 1)
  y.grid <- sapply(x.grid, dpois, lambda = lambda)
  y.grid <- weight %*% y.grid
  main.title <- paste("Poisson Mixture K = ",
    model.obj@K,
    sep = ""
  )
  label.grid <- axisTicks(c(xlim.low, xlim.up),
    log = FALSE,
    nint = 10
  )
  bp <- barplot(y.grid,
    main = main.title, axes = F,
    col = "gray65", border = "gray65", ...
  )
  axis(side = 2, cex = .7, cex.axis = .7)
  axis(
    side = 1, tick = FALSE, at = bp[which(x.grid %in% label.grid)],
    labels = which(x.grid %in% label.grid), cex.axis = .7
  )
  mtext(side = 1, "x", cex = .7, cex.axis = .7, line = 3)
  mtext(side = 2, "P(x)", cex = .7, cex.axis = .7, line = 3)
}

### Plot Binomial models: Binomial models are discrete
### models and line model is used.
### The grid for the x-axis is determined by taking
### the
#' @importFrom stats dbinom
#' @noRd
".plot.Binomial.Model" <- function(model.obj, dev, ...) {
  if (.check.grDevice() && dev) {
    dev.new(title = "Model plot")
  }
  n <- model.obj@T[1]
  p <- model.obj@par$p
  weight <- model.obj@weight
  xlim <- max(n, na.rm = TRUE)
  x.grid <- seq(0, xlim, by = 1)
  y.grid <- sapply(x.grid, dbinom, size = n, p = p)
  y.grid <- weight %*% y.grid
  main.title <- paste("Binomial Mixture K = ",
    model.obj@K,
    sep = ""
  )
  plot(x.grid, y.grid,
    main = main.title, type = "h",
    xlab = "x", ylab = "P(x)", ...
  )
  points(x.grid, y.grid, pch = 20)
}

#' @importFrom stats qexp dexp
#' @noRd
".plot.Exponential.Model" <- function(model.obj, dev, ...) {
  if (.check.grDevice() && dev) {
    dev.new(title = "Model plot")
  }
  lambda <- model.obj@par$lambda
  weight <- model.obj@weight
  min.lambda <- min(lambda, na.rm = TRUE)
  xlim <- qexp(.9999, rate = min.lambda)
  x.grid <- seq(0, ceiling(xlim),
    length =
      as.integer(100 * lambda^(-2))
  )
  y.grid <- sapply(x.grid, dexp, rate = lambda)
  y.grid <- weight %*% y.grid
  main.title <- paste("Exponential Mixture K = ",
    model.obj@K,
    sep = ""
  )
  plot(x.grid, y.grid,
    main = main.title, type = "l",
    xlab = "x", ylab = "P(x)", ...
  )
}

#' @importFrom stats qt dt
#' @noRd
".plot.Student.Model" <- function(model.obj, dev, ...) {
  if (.check.grDevice() && dev) {
    dev.new(title = "Model plot")
  }
  mu <- model.obj@par$mu
  sigma <- model.obj@par$sigma
  df <- model.obj@par$df
  weight <- model.obj@weight
  max.mu <- max(mu, na.rm = TRUE)
  max.sigma <- max(sigma, na.rm = TRUE)
  min.df <- min(df, na.rm = TRUE)
  xlim <- max.mu + max.sigma * qt(.9999, min.df)
  x.grid <- seq(-xlim, xlim, length = 1000) + max.mu
  y.grid <- sapply(x.grid, "-", mu)
  y.grid <- apply(y.grid, 2, "/", sigma)
  y.grid <- apply(y.grid, 2, dt, df = df)
  y.grid <- apply(y.grid, 2, "/", sqrt(sigma))
  y.grid <- t(weight %*% y.grid)
  main.title <- paste("Student-t Mixture K = ",
    model.obj@K,
    sep = ""
  )
  plot(x.grid, y.grid,
    main = main.title, type = "l",
    xlab = "x", ylab = "P(x)", ...
  )
}

#' @importFrom stats qnorm dnorm
#' @noRd
".plot.Normal.Model" <- function(model.obj, dev, ...) {
  if (.check.grDevice() && dev) {
    dev.new(title = "Model Plot")
  }
  mu <- model.obj@par$mu
  sigma <- model.obj@par$sigma
  weight <- model.obj@weight
  max.mu <- max(mu, na.rm = TRUE)
  max.sigma <- max(mu, na.rm = TRUE)
  xlim <- qnorm(.9999,
    mean = max.mu,
    sd = max.sigma
  )
  x.grid <- seq(-xlim, xlim, length = 1000) + max.mu
  y.grid <- sapply(x.grid, dnorm,
    mean = mu,
    sd = sigma
  )
  y.grid <- weight %*% y.grid
  main.title <- paste("Normal Mixture K = ",
    model.obj@K,
    sep = ""
  )
  plot(x.grid, y.grid,
    main = main.title, type = "l",
    xlab = "x", ylab = "P(x)", ...
  )
}

#' @noRd
".plot.Normult.Model" <- function(model.obj, dev, ...) {
  K <- model.obj@K
  r <- model.obj@r
  if (r == 2) {
    if (.check.grDevice() && dev) {
      dev.new(title = "Model: Perspective plot")
    }
    xyz.grid <- .generate.Grid.Normal(model.obj)
    main.title <- paste("Multivariate Normal Mixture K = ",
      K,
      sep = ""
    )
    persp(xyz.grid$x, xyz.grid$y, xyz.grid$z,
      col = "gray65",
      border = "gray47", theta = 55, phi = 30, expand = 0.5,
      lphi = 180, ltheta = 90, r = 40, d = 0.1,
      ticktype = "detailed", zlab = "P(x)", xlab = "r = 1",
      ylab = "r = 2", cex = 0.7, cex.lab = 0.7, cex.axis = 0.7
    )
  } else if (r > 2 && r < 6) {
    if (.check.grDevice() && dev) {
      dev.new(title = "Model: Contour plots")
    }
    if (r == 3) {
      par(
        mfrow = c(1, r), mar = c(2, 2, 2, 2),
        oma = c(4, 5, 1, 5)
      )
    } else if (r == 4) {
      par(
        mfrow = c(2, 3), mar = c(2, 2, 2, 2),
        oma = c(4, 5, 1, 5)
      )
    } else {
      par(
        mfrow = c(2, 5), mar = c(2, 2, 2, 2),
        oma = c(4, 5, 1, 5)
      )
    }
    for (i in seq(1, r - 1)) {
      for (j in seq(1, r)) {
        marmodel <- mixturemar(model.obj, J = c(i, j))
        xyz.grid <- .generate.Grid.Normal(marmodel)
        contour(xyz.grid$x, xyz.grid$y, xyz.grid$z,
          col = "gray47", cex = 0.7, cex.axis = 0.7,
          xlab = paste("r = ", i, sep = ""),
          ylab = paste("r = ", j, sep = "")
        )
      }
    }
  } else {
    stop("Method 'plot' for 'model' objects is not implemented for
             model dimensions of r > 5.")
  }
}

#' @noRd
".plot.Studmult.Model" <- function(model.obj, dev, ...) {
  K <- model.obj@K
  r <- model.obj@r
  if (r == 2) {
    if (.check.grDevice() && dev) {
      dev.new(title = "Model: Perspective plot")
    }
    xyz.grid <- .generate.Grid.Student(model.obj)
    main.title <- paste("Multivariate Student-t Mixture K = ",
      K,
      sep = ""
    )
    persp(xyz.grid$x, xyz.grid$y, xyz.grid$z,
      col = "gray65",
      border = "gray47", theta = 55, phi = 30, expand = 0.5,
      lphi = 180, ltheta = 90, r = 40, d = 0.1,
      ticktype = "detailed", zlab = "P(x)", xlab = "r = 1",
      ylab = "r = 2", cex = 0.7, cex.lab = 0.7, cex.axis = 0.7
    )
  } else if (r > 2 && r < 6) {
    if (.check.grDevice() && dev) {
      dev.new(title = "Model: Contour plots")
    }
    if (r == 3) {
      par(
        mfrow = c(1, r), mar = c(2, 2, 2, 2),
        oma = c(4, 5, 1, 5)
      )
    } else if (r == 4) {
      par(
        mfrow = c(2, 3), mar = c(2, 2, 2, 2),
        oma = c(4, 5, 1, 5)
      )
    } else {
      par(
        mfrow = c(2, 5), mar = c(2, 2, 2, 2),
        oma = c(4, 5, 1, 5)
      )
    }
    for (i in seq(1, r - 1)) {
      for (j in seq(1, r)) {
        marmodel <- mixturemar(model.obj, J = c(i, j))
        xyz.grid <- .generate.Grid.Student(marmodel)
        contour(xyz.grid$x, xyz.grid$y, xyz.grid$z,
          col = "gray47", cex = 0.7, cex.axis = 0.7,
          xlab = paste("r = ", i, sep = ""),
          ylab = paste("r = ", j, sep = "")
        )
      }
    }
  } else {
    stop("Method 'plot' for 'model' objects is not implemented for
             model dimensions of r > 5.")
  }
}

#' @importFrom mvtnorm qmvnorm dmvnorm
#' @noRd
".generate.Grid.Normal" <- function(model.obj) {
  K <- model.obj@k
  mu <- model.obj@par$mu
  sigma <- model.obj@par$sigma
  weight <- model.obj@weight
  func <- function(s, t) {
    value <- 0
    for (k in seq(1, K)) {
      value <- value + weight[k] *
        dmvnorm(cbind(s, t),
          mean = mu[, k],
          sigma = sigma[, , k]
        )
    }
  }
  mu.norm <- apply(mu, 2, function(x) sqrt(sum(x^2)))
  max.mu.index <- tail(sort(mu.norm, index = TRUE)$ix, 1)
  max.mu <- mu[, max.mu.index]
  sigma.det <- apply(sigma, 3, det)
  max.sigma.index <- tail(sort(sigma.det, index = TRUE)$ix, 1)
  max.sigma <- sigma[, , max.sigma.index]
  xylim <- qmvnorm(.9999,
    mean = max.mu,
    sigma = max.sigma
  )$quantile
  x.grid <- seq(-xylim, xylim, length = 100)
  xy.grid <- cbind(x.grid, x.grid)
  xy.grid <- t(apply(xy.grid, 1, "+", max.mu))
  z.grid <- outer(xy.grid[, 1], xy.grid[, 2], func)
  grid.list <- list(
    x = xy.grid[, 1], y = xy.grid[, 2],
    z = z.grid
  )
  return(grid.list)
}

#' @importFrom mvtnorm qmvt dmvt
#' @noRd
".generate.Grid.Student" <- function(model.obj) {
  K <- model.obj@K
  mu <- model.obj@par$mu
  sigma <- model.obj@par$sigma
  df <- model.obj@par$df
  weight <- model.obj@weight
  func <- function(s, t) {
    value <- 0
    for (k in seq(1, K)) {
      value <- value + weight[k] *
        dmvt(cbind(s, t),
          delta = mu[, k],
          sigma = sigma[, , k], df = df[k]
        )
    }
  }
  mu.norm <- apply(mu, 2, function(x) sqrt(sum(x^2)))
  max.mu.index <- tail(sort(mu.norm, index = TRUE)$ix, 1)
  max.mu <- mu[, max.mu.index]
  sigma.det <- apply(sigma, 3, det)
  max.sigma.index <- tail(sort(sigma.det, index = TRUE)$ix, 1)
  max.sigma <- sigma[, , max.sigma.index]
  min.df <- min(df, na.rm = TRUE)
  xylim <- qmvt(.9999,
    delta = max.mu,
    sigma = max.sigma, df = min.df
  )$quantile
  x.grid <- seq(-xylim, xylim, length = 100)
  xy.grid <- cbind(x.grid, x.grid)
  xy.grid <- t(apply(xy.grid, 1, "+", max.mu))
  z.grid <- outer(xy.grid[, 1], xy.grid[, 2], func)
  grid.list <- list(
    x = xy.grid[, 1], y = xy.grid[, 2],
    z = z.grid
  )
  return(grid.list)
}

### plotPointProc
#' @noRd
".plotpointproc.Poisson" <- function(x, dev) {
  K <- x@K
  if (.check.grDevice() && dev) {
    dev.new(title = "Point Process Representation")
  }
  if (min(x@par$lambda) < 1) {
    lambda <- log(x@par$lambda)
  } else {
    lambda <- x@par$lambda
  }
  y.grid <- rep(0, K)
  size.grid <- as.vector(x@weight * 4)
  col.grid <- gray.colors(K,
    start = 0.2,
    end = 0.5
  )
  plot(lambda, y.grid,
    pch = 20, col = col.grid,
    cex = size.grid, cex.lab = .7, cex.axis = .7,
    main = "", ylab = "", xlab = ""
  )
  mtext(
    side = 1, bquote(lambda), cex = .7, cex.lab = .7,
    line = 3
  )
  legend.names <- list("", K)
  for (k in seq(1, K)) {
    legend.names[[k]] <- bquote(lambda[.(k)])
  }
  legend("topright",
    legend = do.call(expression, legend.names),
    col = col.grid, fill = col.grid
  )
}

### Has
### Checks if a 'model' object has specified parameters.
#' @noRd
".haspar.Model" <- function(obj, verbose) {
  if (length(obj@par) > 0) {
    dist <- obj@dist
    if (dist %in% c("poisson", "cond.poisson")) {
      .haspar.poisson.Model(obj, verbose)
    } else if (dist == "binomial") {
      .haspar.binomial.Model(obj, verbose)
    } else if (dist == "exponential") {
      .haspar.exponential.Model(obj, verbose)
    } else if (dist == "normal") {
      .haspar.normal.Model(obj, verbose)
    } else if (dist == "student") {
      .haspar.student.Model(obj, verbose)
    } else if (dist == "normult") {
      .haspar.normult.Model(obj, verbose)
    } else if (dist == "studmult") {
      .haspar.studmult.Model(obj, verbose)
    }
  } else {
    if (verbose) {
      stop(paste("Slot 'par' of 'model' object is ",
        "empty.",
        sep = ""
      ))
    } else {
      return(FALSE)
    }
  }
}

### -----------------------------------------------------------------
### .haspar.poisson.Mode
### @description    Checks if a Poisson model has fully specified
###                 parameters. If verbose is set to TRUE an error
###                 is thrown.
### @par    obj     an S4 object of class 'model'
### @par    verbose an object of class 'logical'
### @return         either TRUE or FALSE if parameters are fully
###                 specified or not. In case verbose == FALSE an
###                 error is thrown.
### -----------------------------------------------------------------
#' @noRd
".haspar.poisson.Model" <- function(obj, verbose) {
  if (length(obj@par) == 0) {
    if (verbose) {
      stop("Slot @par in 'model' object is empty.",
        call. = FALSE
      )
    } else {
      return(FALSE)
    }
  } else {
    if (!"lambda" %in% names(obj@par)) {
      if (verbose) {
        stop(paste("Wrong specification of slot @par ",
          "in 'model' object. Binomial models ",
          "need a parameter vector named 'lambda'.",
          sep = ""
        ), call. = FALSE)
      } else {
        return(FALSE)
      }
    } else {
      if (length(obj@par$lambda) != obj@K) {
        if (verbose) {
          stop(paste("Wrong specification of slot @par of ",
            "'model' object. Slot @K does not match ",
            "dimension of parameters in @par$lambda.",
            sep = ""
          ), call. = FALSE)
        } else {
          return(FALSE)
        }
      } else {
        return(TRUE)
      }
    }
  }
}
### -------------------------------------------------------------------
### .haspar.binomial.Model
### @description    Checks if a Binomial model has fully specified
###                 parameters. If verbose is set to TRUE an error is
###                 thrown.
### @par    obj     an S4 object of class 'model'
### @par    verbose an object of class 'logical'
### @return         either TRUE or FALSE if parameters are fully
###                 specified or not. In case verbose == TRUE an
###                 error is thrown.
### -------------------------------------------------------------------
#' @noRd
".haspar.binomial.Model" <- function(obj, verbose) {
  if (length(obj@par) == 0) {
    if (verbose) {
      stop("Slot @par in 'model' object is empty.",
        call. = FALSE
      )
    } else {
      return(FALSE)
    }
  } else {
    if (!"p" %in% names(obj@par)) {
      if (verbose) {
        stop(paste("Wring specification of slot @par ",
          "in 'model' object. Binomial models ",
          "need a parameter named 'p'.",
          sep = ""
        ),
        call. = FALSE
        )
      } else {
        return(FALSE)
      }
    } else {
      if (length(obj@par$p) != obj@K) {
        if (verbose) {
          stop(paste("Wrong specification of slot @par of ",
            "'model' object. Slot @K does not ",
            "match the dimension of parameters ",
            "in @par$p.",
            sep = ""
          ), call. = FALSE)
        } else {
          return(FALSE)
        }
      } else {
        return(TRUE)
      }
    }
  }
}

### ------------------------------------------------------------------
### .haspar.exponential.Model
### @description    Checks if an Exponential model has fully specified
###                 parameters. If verbose is set to TRUE an error is
###                 thrown.
### @param  obj     an S4 object of class 'model'
### @param  verbose an object of class 'logical'
### @return either TRUE or FALSE if parameters are fully specified or
###         nor. In case verbose == TRUE an error is thrown .
### ------------------------------------------------------------------
#' @noRd
".haspar.exponential.Model" <- function(obj, verbose) {
  if (length(obj@par) == 0) {
    if (verbose) {
      stop("Slot @par in 'model' object is empty",
        call. = FALSE
      )
    } else {
      return(FALSE)
    }
  } else {
    if (!"lambda" %in% names(obj@par)) {
      if (verbose) {
        stop(paste("Wrong specification of slot @par ",
          "in 'model' object. Exponential ",
          "models need a parameter named ",
          "'lambda'.",
          sep = ""
        ),
        call. = FALSE
        )
      } else {
        return(FALSE)
      }
    } else {
      if (length(obj@par$lambda) != obj@K) {
        if (verbose) {
          stop(paste("Wrong specification of slot @par in ",
            "'model' object. Number of Exponential ",
            "parameters in @par$lambda must match ",
            "number of components in slot @K.",
            sep = ""
          ), call. = FALSE)
        } else {
          return(FALSE)
        }
      } else {
        return(TRUE)
      }
    }
  }
}

### ------------------------------------------------------------------
### .haspar.normal.Model
### @description    Checks if a Normal model has fully specified
###                 parameters. If verbose is set to TRUE an error is
###                 thrown.
### @param  obj     an S4 object of class 'model'
### @param  verbose an object of class 'logical'
### @return either TRUE or FALSE if parameters are fully specified or
###         not. In case verbose == TRUE an error is thrown .
### ------------------------------------------------------------------
#' @noRd
".haspar.normal.Model" <- function(obj, verbose) {
  K <- obj@K
  if (length(obj@par) == 0) {
    if (verbose) {
      stop("Slot @par in 'model' object is empty.",
        call. = FALSE
      )
    } else {
      return(FALSE)
    }
  } else {
    if (!("mu" %in% names(obj@par))) {
      if (verbose) {
        stop(paste("Wrong specification of slot @par ",
          "in 'model' object. Normal models ",
          "need a mean vector named 'mu'.",
          sep = ""
        ), call. = FALSE)
      } else {
        return(FALSE)
      }
    } else {
      if (length(obj@par$mu) != K) {
        if (verbose) {
          stop(paste("Wrong specification of slot @par ",
            "in 'model' object. Slot @K does ",
            "not match dimension of parameter ",
            "@par$mu.",
            sep = ""
          ), call. = FALSE)
        } else {
          return(FALSE)
        }
      } else {
        if (!("sigma" %in% names(obj@par))) {
          if (verbose) {
            stop(paste("Wrong specification of slot @par ",
              "in 'model' object. Normal models ",
              "need a standard deviation vector ",
              "named 'sigma'.",
              sep = ""
            ),
            call. = FALSE
            )
          } else {
            return(FALSE)
          }
        } else {
          if (length(obj@par$sigma) != K) {
            if (verbose) {
              stop(paste("Wrong specification of slot @par ",
                "in 'model' object. Slot @K does ",
                "not match dimension of parameter ",
                "par@$sigma.",
                sep = ""
              ), call. = FALSE)
            }
          } else {
            return(TRUE)
          }
        }
      }
    }
  }
}

### ------------------------------------------------------------------
### .haspar.normal.Model
### @description    Checks if a Normal model has fully specified
###                 parameters. If verbose is set to TRUE an error is
###                 thrown.
### @param  obj     an S4 object of class 'model'
### @param  verbose an object of class 'logical'
### @return either TRUE or FALSE if parameters are fully specified or
###         not. In case verbose == TRUE an error is thrown .
### ------------------------------------------------------------------
#' @noRd
".haspar.normult.Model" <- function(obj, verbose) {
  K <- obj@K
  if (length(obj@par) == 0) {
    if (verbose) {
      stop("Slot @par in 'model' object is empty.",
        call. = FALSE
      )
    } else {
      return(FALSE)
    }
  } else {
    if (!("mu" %in% names(obj@par))) {
      if (verbose) {
        stop(paste("Wrong specification of slot @par ",
          "in 'model' object. Normal models ",
          "need a mean vector named 'mu'.",
          sep = ""
        ), call. = FALSE)
      } else {
        return(FALSE)
      }
    } else {
      if (ncol(obj@par$mu) != K) {
        if (verbose) {
          stop(paste("Wrong specification of slot @par ",
            "in 'model' object. Slot @K does ",
            "not match dimension of parameter ",
            "@par$mu.",
            sep = ""
          ), call. = FALSE)
        } else {
          return(FALSE)
        }
      } else {
        if (!("sigma" %in% names(obj@par))) {
          if (verbose) {
            stop(paste("Wrong specification of slot @par ",
              "in 'model' object. Normal models ",
              "need a standard deviation vector ",
              "named 'sigma'.",
              sep = ""
            ),
            call. = FALSE
            )
          } else {
            return(FALSE)
          }
        } else {
          if (dim(obj@par$sigma)[3] != K) {
            if (verbose) {
              stop(paste("Wrong specification of slot @par ",
                "in 'model' object. Slot @K does ",
                "not match dimension of parameter ",
                "par@$sigma.",
                sep = ""
              ), call. = FALSE)
            }
          } else {
            return(TRUE)
          }
        }
      }
    }
  }
}

### ------------------------------------------------------------------
### .haspar.student.Model
### @description    Checks if a Normal model has fully specified
###                 parameters. If verbose is set to TRUE an error is
###                 thrown.
### @param  obj     an S4 object of class 'model'
### @param  verbose an object of class 'logical'
### @return either TRUE or FALSE if parameters are fully specified or
###         not. In case verbose == TRUE an error is thrown .
### ------------------------------------------------------------------
#' @noRd
".haspar.student.Model" <- function(obj, verbose) {
  K <- obj@K
  if (length(obj@par) == 0) {
    if (verbose) {
      stop("Slot @par in 'model' object is empty.",
        call. = FALSE
      )
    } else {
      return(FALSE)
    }
  } else {
    if (!("mu" %in% names(obj@par))) {
      if (verbose) {
        stop(paste("Wrong specification of slot @par ",
          "in 'model' object. Student-t models ",
          "need a mean vector named 'mu'.",
          sep = ""
        ), call. = FALSE)
      } else {
        return(FALSE)
      }
    } else {
      if (length(obj@par$mu) != K) {
        if (verbose) {
          stop(paste("Wrong specification of slot @par ",
            "in 'model' object. Slot @K does ",
            "not match dimension of parameter ",
            "@par$mu.",
            sep = ""
          ), call. = FALSE)
        } else {
          return(FALSE)
        }
      } else {
        if (!("sigma" %in% names(obj@par))) {
          if (verbose) {
            stop(paste("Wrong specification of slot @par ",
              "in 'model' object. Student-t models ",
              "need a standard deviation vector ",
              "named 'sigma'.",
              sep = ""
            ),
            call. = FALSE
            )
          } else {
            return(FALSE)
          }
        } else {
          if (length(obj@par$sigma) != K) {
            if (verbose) {
              stop(paste("Wrong specification of slot @par ",
                "in 'model' object. Slot @K does ",
                "not match dimension of parameter ",
                "par@$sigma.",
                sep = ""
              ), call. = FALSE)
            }
          } else {
            if (!"df" %in% names(obj@par)) {
              if (verbose) {
                stop(paste("Wrong specification of slot @par ",
                  "in 'model' object. Student-t models ",
                  "need a vector with degrees of freedom ",
                  "named 'df'.",
                  sep = ""
                ), call. = FALSE)
              } else {
                return(FALSE)
              }
            } else {
              return(TRUE)
            }
          }
        }
      }
    }
  }
}

### ------------------------------------------------------------------
### .haspar.student.Model
### @description    Checks if a Normal model has fully specified
###                 parameters. If verbose is set to TRUE an error is
###                 thrown.
### @param  obj     an S4 object of class 'model'
### @param  verbose an object of class 'logical'
### @return either TRUE or FALSE if parameters are fully specified or
###         not. In case verbose == TRUE an error is thrown .
### ------------------------------------------------------------------
#' @noRd
".haspar.studmult.Model" <- function(obj, verbose) {
  K <- obj@K
  if (length(obj@par) == 0) {
    if (verbose) {
      stop("Slot @par in 'model' object is empty.",
        call. = FALSE
      )
    } else {
      return(FALSE)
    }
  } else {
    if (!("mu" %in% names(obj@par))) {
      if (verbose) {
        stop(paste("Wrong specification of slot @par ",
          "in 'model' object. Student-t models ",
          "need a mean vector named 'mu'.",
          sep = ""
        ), call. = FALSE)
      } else {
        return(FALSE)
      }
    } else {
      if (ncol(obj@par$mu) != K) {
        if (verbose) {
          stop(paste("Wrong specification of slot @par ",
            "in 'model' object. Slot @K does ",
            "not match dimension of parameter ",
            "@par$mu.",
            sep = ""
          ), call. = FALSE)
        } else {
          return(FALSE)
        }
      } else {
        if (!("sigma" %in% names(obj@par))) {
          if (verbose) {
            stop(paste("Wrong specification of slot @par ",
              "in 'model' object. Student-t models ",
              "need a standard deviation vector ",
              "named 'sigma'.",
              sep = ""
            ),
            call. = FALSE
            )
          } else {
            return(FALSE)
          }
        } else {
          if (dim(obj@par$sigma)[3] != K) {
            if (verbose) {
              stop(paste("Wrong specification of slot @par ",
                "in 'model' object. Slot @K does ",
                "not match dimension of parameter ",
                "par@$sigma.",
                sep = ""
              ), call. = FALSE)
            }
          } else {
            if (!"df" %in% names(obj@par)) {
              if (verbose) {
                stop(paste("Wrong specification of slot @par ",
                  "in 'model' object. Student-t models ",
                  "need a vector with degrees of freedom ",
                  "named 'df'.",
                  sep = ""
                ), call. = FALSE)
              } else {
                return(FALSE)
              }
            } else {
              return(TRUE)
            }
          }
        }
      }
    }
  }
}

### Validity
### Validity checking of model objects is implemented
### in two versions: an initializing version relying partly
### on warnings and amore restrictive version relying exclusively
### on errors.
### The less restrictive validity check is used in setters and
### and the fully restrictive version in the constructor and later
### usage of model object (e.g. see 'mcmcstart()')
### -----------------------------------------------------------------------------
### .init.valid.Model
### @description    Initial validity check for model object
### @par    obj     a model object
### @return         An error in case certain conditions are failed or there are
###                 inconsistencies.
### @see            ?model, ?vignette('finmix'), .init.valid.*, .valid.*
### @author         Lars Simon Zehnder
### -----------------------------------------------------------------------------
#' @noRd
".init.valid.Model" <- function(obj) {
  .valid.dist.Model(obj)
  .init.valid.K.Model(obj)
  .init.valid.r.Model(obj)
  .init.valid.par.Model(obj)
  .init.valid.weight.Model(obj)
  .init.valid.T.Model(obj)
}

### -----------------------------------------------------------------------------
### .init.Model
### @description    Validity check for model object
### @par    obj     a model object
### @return         An error in case certain conditions are failed or a warning
###                 if there are inconsistencies.
### @see            ?model, ?vignette('finmix'), .init.valid.*, .valid.*
### @author         Lars Simon Zehnder
### -----------------------------------------------------------------------------
#' @noRd
".valid.Model" <- function(obj) {
  .valid.dist.Model(obj)
  .valid.K.Model(obj)
  .valid.r.Model(obj)
  .valid.par.Model(obj)
  .valid.weight.Model(obj)
  .valid.T.Model(obj)
}

### ----------------------------------------------------------------------------
### .valid.dist.Model
### @description    Initial validity check for the distribution of a finite
###                 mixture model
### @par    obj     a model object
### @return         An error in case the distribution is unknown.
### @see            ?model, ?vignette('finmix')i
### ----------------------------------------------------------------------------
#' @noRd
".valid.dist.Model" <- function(obj) {
  dists <- c(
    "normal", "normult", "exponential",
    "student", "studmult", "poisson",
    "cond.poisson", "binomial"
  )
  indicmod.dists <- c("multinomial")
  if (length(obj@dist) > 0) {
    if (!(obj@dist %in% dists)) {
      stop(paste("Unknown distribution in slot 'dist' ",
        "of 'model' object.",
        sep = ""
      ),
      call. = FALSE
      )
    } else {
      if (!(obj@indicmod %in% indicmod.dists)) {
        stop(paste("Unknown indicator distribution in slot ",
          "'indicmod' of 'model' object.",
          sep = ""
        ),
        call. = FALSE
        )
      }
    }
  }
}

### ----------------------------------------------------------------------------
### .init.valid.K.Model
### @description    Initial validity check for the number of components K of
###                 a finite mixture model.
### @par    obj     a model object
### @return         An error if the number of components are not a positive
###                 integer
### @see            ?model, ?vignette('finmix')
### @author         Lars Simon Zehnder
### ----------------------------------------------------------------------------
#' @noRd
".init.valid.K.Model" <- function(obj) {
  if (obj@K < 1) {
    stop(paste("Wrong specification of slot 'K' of ",
      "'model' object. Number of components ",
      "must be a positive integer.",
      sep = ""
    ),
    call. = FALSE
    )
  } else {
    if (!all(is.na(obj@weight))) {
      if (obj@K != ncol(obj@weight)) {
        stop(paste("Dimension of slot 'weight' in ",
          "'model' object does not match ",
          "number of components in slot 'K'.",
          sep = ""
        ),
        call. = FALSE
        )
      }
    }
    .init.valid.par.Model(obj)
  }
}

### ----------------------------------------------------------------------------
### .valid.K.Model
### @description    Validity check for the number of components K of
###                 a finite mixture model.
### @par    obj     a model object
### @return         An error if the number of components are not a positive
###                 integer and a warning if the number of components do not
###                 match the dimension of the weights.
### @see            ?model, ?vignette('finmix')
### @author         Lars Simon Zehnder
### ----------------------------------------------------------------------------
#' @noRd
".valid.K.Model" <- function(obj) {
  if (obj@K < 1) {
    stop(paste("Wrong specification of slot 'K' of ",
      "'model' object. Number of components ",
      "must be a positive integer.",
      sep = ""
    ),
    call. = FALSE
    )
  } else {
    if (!all(is.na(obj@weight))) {
      if (obj@K != ncol(obj@weight)) {
        warning(paste("Dimension of slot 'weight' in ",
          "'model' object does not match ",
          "number of components in slot 'K'.",
          sep = ""
        ),
        call. = FALSE
        )
      }
    }
    .valid.par.Model(obj)
  }
}

### ----------------------------------------------------------------------------
### .init.valid.r.Model
### @description    Initial validity check for variable dimension r.
### @par    obj     a model object
### @return         An error in case the variable dimension r is not a positive
###                 integer or the dimension does not fit the distribution model.
### @see            ?model, ?vignette('finmix')
### @author         Lars Simon Zehnder
### ----------------------------------------------------------------------------
#' @noRd
".init.valid.r.Model" <- function(obj) {
  univ <- .get.univ.Model()
  multiv <- .get.multiv.Model()
  if (obj@r < 1) {
    stop(paste("Wrong specification of slot 'r' ",
      "in 'model' object. Dimension of ",
      "variables must be a positive integer.",
      sep = ""
    ),
    call. = FALSE
    )
  } else {
    if ((obj@dist %in% univ) && obj@r > 1) {
      stop(paste("Wrong specification of slot 'r' ",
        "in 'model' object. Univariate ",
        "distributions can only have one ",
        "dimension.",
        sep = ""
      ),
      call. = FALSE
      )
    } else if ((obj@dist %in% multiv) && obj@r < 2) {
      stop(paste("Wrong specification of slot 'r' ",
        "in 'model' object. Multivariate ",
        "distributions must have dimension ",
        "greater one.",
        sep = ""
      ),
      call. = FALSE
      )
    }
  }
}

### ----------------------------------------------------------------------------
### .init.valid.r.Model
### @description    Initial validity check for variable dimension r.
### @par    obj     a model object
### @return         An error in case the variable dimension r is not a positive
###                 integer or a warning if the dimension does not fit the
###                 distribution model.
### @see            ?model, ?vignette('finmix')
### @author         Lars Simon Zehnder
### ----------------------------------------------------------------------------
#' @noRd
".valid.r.Model" <- function(obj) {
  univ <- .get.univ.Model()
  multiv <- .get.multiv.Model()
  if (obj@r < 1) {
    stop(paste("Wrong specification of slot 'r' ",
      "in 'model' object. Dimension of ",
      "variables must be positive.",
      sep = ""
    ),
    call. = FALSE
    )
  } else {
    if ((obj@dist %in% univ) && obj@r > 1) {
      stop(paste("Wrong specification of slot 'r' ",
        "in 'model' object. Univariate ",
        "distributions can only have one ",
        "dimension.",
        sep = ""
      ),
      call. = FALSE
      )
    } else if ((obj@dist %in% multiv) && obj@r < 2) {
      stop(paste("Wrong specification of slot 'r' ",
        "in 'model' object. Multivariate ",
        "distributions must have dimension ",
        "greater one.",
        sep = ""
      ),
      call. = FALSE
      )
    }
  }
}

### ----------------------------------------------------------------------------
### .init.valid.weight.Model
### @description    Initial validity check for the weights of a finite mixture
###                 model.
### @par    obj     a model object
### @return         An error if the dimension of the weight vector does not fit
###                 the model or if the weights do not sum to 1, are negative or
###                 larger than one.
### @see            ?model, ?vignette('finmix')
### @author         Lars Simon Zehnder
### ----------------------------------------------------------------------------
#' @noRd
".init.valid.weight.Model" <- function(obj) {
  if (!all(is.na(obj@weight))) {
    if (nrow(obj@weight) > 1) {
      stop(paste("Wrong dimension of slot 'weight' in ",
        "'model' object. Dimension of slot ",
        "'weight' must be 1 x K.",
        sep = ""
      ),
      call. = FALSE
      )
    } else {
      if (ncol(obj@weight) != obj@K) {
        stop(paste("Wrong number of weights in slot 'weight' of ",
          "'model' object. Number of weights does not ",
          "match number of components in slot 'K'.",
          sep = ""
        ),
        call. = FALSE
        )
      } else {
        if (is.integer(obj@weight)) {
          stop(paste("Wrong specification of slot 'weight' of ",
            "'model' object. Weights must be of type ",
            "'numeric'.",
            sep = ""
          ),
          call. = FALSE
          )
        }
        if (!is.numeric(obj@weight)) {
          stop(paste("Wrong specification of slot 'weight' of ",
            "'model' object. Weights must be of type ",
            "'numeric'.",
            sep = ""
          ),
          call. = FALSE
          )
        }
        if (any(obj@weight <= 0) || any(obj@weight >= 1)) {
          stop(paste("Weights in slot 'weight' of 'model' ",
            "object must be positive.",
            sep = ""
          ),
          call. = FALSE
          )
        } else {
          if (round(sum(obj@weight)) != 1) {
            stop(paste("Weights in slot 'weight' of 'model' ",
              "object must sum to one.",
              sep = ""
            ),
            call. = FALSE
            )
          }
        }
      }
    }
  }
}

### ------------------------------------------------------------------------------------
### .valid.weight.Model
### @description    Validity check for the weights of a finite mixture model.
### @par    obj     a model object
### @return         An error if the weights are not of type 'numeric' and a warning
###                 if the weigths do not conform to the number of components K,
###                 do not sum to one or are not values between 0 and 1.
### @see            ?model, ?vignette('finmix')
### @author         Lars Simon Zehnder
### -------------------------------------------------------------------------------------
#' @noRd
".valid.weight.Model" <- function(obj) {
  if (!all(is.na(obj@weight))) {
    if (nrow(obj@weight) > 1) {
      warning(paste("Wrong dimension of slot 'weight' in ",
        "'model' object. Dimension of slot ",
        "'weight' must be 1 x K.",
        sep = ""
      ),
      call. = FALSE
      )
    } else {
      if (ncol(obj@weight) != obj@K) {
        warning(paste("Wrong number of weights in slot 'weight' of ",
          "'model' object. Number of weights does not ",
          "match number of components in slot 'K'.",
          sep = ""
        ),
        call. = FALSE
        )
      } else {
        if (is.integer(obj@weight)) {
          stop(paste("Wrong specification of slot 'weight' of ",
            "'model' object. Weights must be of type ",
            "'numeric'.",
            sep = ""
          ),
          call. = FALSE
          )
        }
        if (!is.numeric(obj@weight)) {
          stop(paste("Wrong specification of slot 'weight' of ",
            "'model' object. Weights must be of type ",
            "'numeric'.",
            sep = ""
          ),
          call. = FALSE
          )
        }
        if (any(obj@weight <= 0) || any(obj@weight >= 1)) {
          warning(paste("Weights in slot 'weight' of 'model' ",
            "object must be positive.",
            sep = ""
          ),
          call. = FALSE
          )
        } else {
          if (round(sum(obj@weight)) != 1) {
            warning(paste("Weights in slot 'weight' of 'model' ",
              "object must sum to one.",
              sep = ""
            ),
            call. = FALSE
            )
          }
        }
      }
    }
  }
}

### -------------------------------------------------------------------------------------
### .init.valid.T.Model
### @description    Initial validity check for the repetitions of a Binomial mixture.
### @par    obj     a model object
### @return         An error in case the reptitions are not of type integer, have
###                 the wrong dimension, or non-positive values.
### @see            ?model, ?vignette('finmix')
### --------------------------------------------------------------------------------------
#' @noRd
".init.valid.T.Model" <- function(obj) {
  if (!all(is.na(obj@T))) {
    if (!is.integer(obj@T)) {
      stop(paste("Wrong type of slot 'T' in 'model' object ",
        "Repetitions must be of type 'integer'.",
        sep = ""
      ),
      call. = FALSE
      )
    }
    if (nrow(obj@T) > 1 && ncol(obj@T) > 1) {
      stop(paste("Wrong dimension of slot 'T' in 'model' ",
        "object. Repetitions can only be ",
        "one-dimensional",
        sep = ""
      ),
      call. = FALSE
      )
    }
    if (any(obj@T < 1)) {
      stop(paste("Wrong specification of slot 'T' in 'model' ",
        "object. Repetitions must be positive integers ",
        "or NA.",
        sep = ""
      ),
      call. = FALSE
      )
    }
  }
}

### -------------------------------------------------------------------------------------
### .valid.T.Model
### @description    Validity check for the repetitions of a Binomial mixture.
### @par    obj     a model object
### @return         An error in case the reptitions are not of type integer, have
###                 the wrong dimension, or non-positive values.
### @see            ?model, ?vignette('finmix')
### --------------------------------------------------------------------------------------
#' @noRd
".valid.T.Model" <- function(obj) {
  if (!all(is.na(obj@T))) {
    if (!is.integer(obj@T)) {
      stop(paste("Wrong type of slot 'T' in 'model' object ",
        "Repetitions must be of type 'integer'.",
        sep = ""
      ),
      call. = FALSE
      )
    }
    if (nrow(obj@T) > 1 && ncol(obj@T) > 1) {
      stop(paste("Wrong dimension of slot 'T' in 'model' ",
        "object. Repetitions can only be ",
        "one-dimensional",
        sep = ""
      ),
      call. = FALSE
      )
    }
    if (any(obj@T < 1)) {
      stop(paste("Wrong specification of slot 'T' in 'model' ",
        "object. Repetitions must be positive integers ",
        "or NA.",
        sep = ""
      ),
      call. = FALSE
      )
    }
  }
}


### -------------------------------------------------------------------------------
### .init.valid.par.Model
### @description    Initial validity check of model parameters
### @par    obj     a model object
### @return         An error if parameters fail certain conditions
### @detail         This validity check is called in the S4 constructor
###                 'model()' and ensures that the user constructs an inherently
###                 consistent model object.
### @see            ?model, ?vignette('finmix')
### @author         Lars Simon Zehnder
### --------------------------------------------------------------------------------
#' @noRd
".init.valid.par.Model" <- function(obj) {
  dist <- obj@dist
  if (length(obj@par) > 0) {
    if (dist %in% c("poisson", "cond.poisson")) {
      .init.valid.Poisson.Model(obj)
    } else if (dist == "binomial") {
      .init.valid.Binomial.Model(obj)
    } else if (dist == "normal") {
      .init.valid.Normal.Model(obj)
    } else if (dist == "normult") {
      .init.valid.Normult.Model(obj)
    } else if (dist == "student") {
      .init.valid.Student.Model(obj)
    } else if (dist == "studmult") {
      .init.valid.Studmult.Model(obj)
    }
  }
}

### -------------------------------------------------------------------------------
### .valid.par.Model
### @description    Validity check of model parameters
### @par    obj     a model object
### @return         An error if parameters fail certain necessary conditions and
###                 a warning if parameters fail consistency.
### @detail         This validity check is called in the setters to ensure that
###                 slots can be changed without errors but help the user to
###                 end up with an inherently consistent model object.
### @see            ?model, ?vignette('finmix')
### @author         Lars Simon Zehnder
### --------------------------------------------------------------------------------
#' @noRd
".valid.par.Model" <- function(obj) {
  dist <- obj@dist
  if (length(obj@par) > 0) {
    if (dist %in% c("poisson", "cond.poisson")) {
      .valid.Poisson.Model(obj)
    } else if (dist == "binomial") {
      .valid.Binomial.Model(obj)
    } else if (dist == "exponential") {
      .valid.Exponential.Model(obj)
    } else if (dist == "normal") {
      .valid.Normal.Model(obj)
    } else if (dist == "normult") {
      .valid.Normult.Model(obj)
    } else if (dist == "student") {
      .valid.Student.Model(obj)
    } else if (dist == "studmult") {
      .valid.Studmult.Model(obj)
    }
  }
}

### -----------------------------------------------------------------------------
### .init.valid.Poisson.Model
### @description    Initial validity check for parameters of a Poisson mixture.
### @par    obj     a model object
### @return         An error if parameters fail certain conditions.
### @detail     This initial validity check is called in the S4 constructor
###             'model()' and ensures that the user constructs an inherently
###             consistent model object.
###             The parameter list must contain an element 'lambda' that is
###             an 1 x K array, vector or matrix with numeric or integer values
###             all positive.
### @see        ?model
### @author     Lars Simon Zehnder
### -------------------------------------------------------------------------------
#' @noRd
".init.valid.Poisson.Model" <- function(obj) {
  if (length(obj@par) > 0) {
    if ("lambda" %in% names(obj@par)) {
      if (!is.array(obj@par$lambda) && !is.vector(obj@par$lambda) &&
        !is.matrix(obj@par$lambda)) {
        stop(paste("Wrong specification of slot @par: ",
          "Poisson parameters must be either an ",
          "array, a vector or a matrix of dimension ",
          "1 x K.",
          sep = ""
        ),
        call. = FALSE
        )
      }
      obj@par$lambda <- as.vector(obj@par$lambda)
      if (!is.numeric(obj@par$lambda) && !is.integer(obj@par$lambda)) {
        stop(paste("Wrong specification in slot 'par' of 'model' object. ",
          "Parameters must be of type 'numeric' or 'integer'.",
          sep = ""
        ),
        call. = FALSE
        )
      }
      if (length(obj@par$lambda) != obj@K) {
        warning(paste("Wrong specification of slot @par: ",
          "lambda must be either an array, a vector ",
          "or a matrix of dimension 1 x K.",
          sep = ""
        ),
        call. = FALSE
        )
      } else {
        if (any(obj@par$lambda <= 0)) {
          stop(paste("Wrong specification of slot @par: ",
            "Poisson parameters ",
            "must be positive.",
            sep = ""
          ),
          call. = FALSE
          )
        }
      }
    } else {
      warning(paste("Wrong specification of slot 'par' in 'model' object. ",
        "Poisson parameters must be named 'lambda'.",
        sep = ""
      ),
      call. = FALSE
      )
    }
  }
}

### -----------------------------------------------------------------------------
### .valid.Poisson.Model
### @description    Validity check for parameters of a Poisson mixture.
### @par    obj     a model object
### @return         An error if parameters do fail certain necessary conditions.
###                 A warning if parameters do fail consistency.
### @detail     This validity check is called in the setters to ensure that
###             slots can be changed without errors but help the user to
###             get a inherently consistent model object.
###             The parameter list must contain an element 'lambda' that is
###             an 1 x K array, vector or matrix with numeric or integer values
###             all positive.
### @see        $model
### @author     Lars Simon Zehnder
### -----------------------------------------------------------------------------
#' @noRd
".valid.Poisson.Model" <- function(obj) {
  if (length(par) > 0) {
    if ("lambda" %in% names(obj@par)) {
      if (!is.array(obj@par$lambda) && !is.vector(obj@par$lambda) &&
        !is.matrix(obj@par$lambda)) {
        stop(paste("Wrong specification of slot @par: ",
          "Poisson parameters must be either an ",
          "array, a vector or a matrix of dimension ",
          "1 x K.",
          sep = ""
        ),
        call. = FALSE
        )
      }
      obj@par$lambda <- as.vector(obj@par$lambda)
      if (!is.numeric(obj@par$lambda) && !is.integer(obj@par$lambda)) {
        stop(paste("Wrong specification in slot 'par' of 'model' object. ",
          "Parameters must be of type 'numeric' or 'integer'.",
          sep = ""
        ),
        call. = FALSE
        )
      }
      if (length(obj@par$lambda) != obj@K) {
        warning(paste("Wrong specification of slot @par: ",
          "lambda must be either an array, a vector ",
          "or a matrix of dimension 1 x K.",
          sep = ""
        ),
        call. = FALSE
        )
      } else {
        if (any(obj@par$lambda <= 0)) {
          stop(paste("Wrong specification of slot @par: ",
            "Poisson parameters ",
            "must be positive.",
            sep = ""
          ),
          call. = FALSE
          )
        }
      }
    } else {
      stop(paste("Wrong specification of slot 'par' in 'model' object. ",
        "Poisson parameters must be named 'lambda'.",
        sep = ""
      ),
      call. = FALSE
      )
    }
  }
}

### ------------------------------------------------------------------------------
### .init.valid.Binomial.Model
### @description    Initial validity check for parameters of a Binomial mixture.
### @par    obj     a model object
### @return         An error if parameters fail certain conditions
### @detail         This initial validity check is called in the S4 constructor
###                 'model()' and ensures that the user constructs an inherently
###                 consistent model object.
###                 The parameter list must contain an 1 x K array, vector, or
###                 matrix with probabilities, all between 0 and 1.
### @see            ?model, ?vignette('finmix')
### @author         Lars Simon Zehnder
### ------------------------------------------------------------------------------
#' @noRd
".init.valid.Binomial.Model" <- function(obj) {
  if (length(obj@par)) {
    if (!"p" %in% names(obj@par)) {
      stop(paste("Wrong specification of slot @par: ",
        "Binomial mixtures need a ",
        "probability vector named 'p'.",
        sep = ""
      ),
      call. = FALSE
      )
    } else if (!is.array(obj@par$p) && !is.vector(obj@par$p) &&
      !is.matrix(obj@par$p)) {
      stop(paste("Wrong specification of slot @par: ",
        "p must be either an array, a vector ",
        "or a matrix of dimension 1 x K",
        sep = ""
      ),
      call. = FALSE
      )
    } else if (!all(is.numeric(obj@par$p) || is.integer(obj@par$p))) {
      stop(paste("Wrong specification of slot @par: ",
        "parameters must be either of type ,",
        "'numeric' or 'integer'.",
        sep = ""
      ),
      call. = FALSE
      )
    } else if (length(obj@par$p) != obj@K) {
      stop(paste("Wrong specification of slot @par: ",
        "p must be an array, a vector ",
        "or a matrix of dimension 1 x K",
        sep = ""
      ),
      call. = FALSE
      )
    } else if (!all(obj@par$p > 0) || !all(obj@par$p < 1)) {
      stop(paste("Wrong specification of slot @par: ",
        "Binomial parameters must be all ",
        "between 0 and 1.",
        sep = ""
      ),
      call. = FALSE
      )
    }
  }
  if (dim(obj@T)[1] > 1 && dim(obj@T)[2] > 1) {
    stop(paste(
      "Dimensions of repetitions 'T' for binomial mixture",
      "model do not match conditions. Only one-dimensional",
      "repetitions can be used in a binomial mixture model."
    ), sep = "")
  }
}

### ------------------------------------------------------------------------------
### .valid.Binomial.Model
### @description    Validity check for parameters of a Binomial mixture.
### @par    obj     a model object
### @return         An error if parameters fail certain necessary conditions and
###                 a warning if parameters fail consistency
### @detail         This validity check is called in the setters to ensure that
## ä                 slots can be changed without errors but help the user to
###                 end up with an inherently consistent model object.
###                 The parameter list must contain an 1 x K array, vector, or
###                 matrix with probabilities, all between 0 and 1.
### @see            ?model, ?vignette('finmix')
### @author         Lars Simon Zehnder
### ------------------------------------------------------------------------------
#' @noRd
".valid.Binomial.Model" <- function(obj) {
  if (length(obj@par)) {
    if (!"p" %in% names(obj@par)) {
      warning(paste("Wrong specification of slot @par: ",
        "Binomial mixtures need a ",
        "probability vector named 'p'.",
        sep = ""
      ),
      call. = FALSE
      )
    } else if (!is.array(obj@par$p) && !is.vector(obj@par$p) &&
      !is.matrix(obj@par$p)) {
      stop(paste("Wrong specification of slot @par: ",
        "p must be either an array, a vector ",
        "or a matrix of dimension 1 x K",
        sep = ""
      ),
      call. = FALSE
      )
    } else if (!all(is.numeric(obj@par$p) || is.integer(obj@par$p))) {
      stop(paste("Wrong specification of slot @par: ",
        "parameters must be either of type ,",
        "'numeric' or 'integer'.",
        sep = ""
      ),
      call. = FALSE
      )
    } else if (length(obj@par$p) != obj@K) {
      warning(paste("Wrong specification of slot @par: ",
        "p must be an array, a vector ",
        "or a matrix of dimension 1 x K",
        sep = ""
      ),
      call. = FALSE
      )
    } else if (!all(obj@par$p > 0 && obj@par$p < 1)) {
      stop(paste("Wrong specification of slot @par: ",
        "Binomial parameters must be all ",
        "between 0 and 1.",
        sep = ""
      ),
      call. = FALSE
      )
    }
  }
  if (dim(obj@T)[1] > 1 && dim(obj@T)[2] > 1) {
    stop(paste(
      "Dimensions of repetitions 'T' for binomial mixture",
      "model do not match conditions. Only one-dimensional",
      "repetitions can be used in a binomial mixture model."
    ), sep = "")
  }
}


### -----------------------------------------------------------------------------
### .init.valid.Exponential.Model
### @description    Initial validity check for parameters of a Exponential
###                 mixture.
### @par    obj     a model object
### @return         An error if parameters fail certain conditions.
### @detail     This initial validity check is called in the S4 constructor
###             'model()' and ensures that the user constructs an inherently
###             consistent model object.
###             The parameter list must contain an element 'lambda' that is
###             an 1 x K array, vector or matrix with numeric or integer values
###             all positive.
### @see        ?model
### @author     Lars Simon Zehnder
### -------------------------------------------------------------------------------
#' @noRd
".init.valid.Exponential.Model" <- function(obj) {
  if (length(obj@par) > 0) {
    if ("lambda" %in% names(obj@par)) {
      if (!is.array(obj@par$lambda) && !is.vector(obj@par$lambda) &&
        !is.matrix(obj@par$lambda)) {
        stop(paste("Wrong specification of slot @par: ",
          "Exponential parameters must be either an ",
          "array, a vector or a matrix of dimension ",
          "1 x K.",
          sep = ""
        ),
        call. = FALSE
        )
      }
      obj@par$lambda <- as.vector(obj@par$lambda)
      if (!is.numeric(obj@par$lambda) && !is.integer(obj@par$lambda)) {
        stop(paste("Wrong specification in slot 'par' of 'model' object. ",
          "Parameters must be of type 'numeric' or 'integer'.",
          sep = ""
        ),
        call. = FALSE
        )
      }
      if (length(obj@par$lambda) != obj@K) {
        warning(paste("Wrong specification of slot @par: ",
          "lambda must be either an array, a vector ",
          "or a matrix of dimension 1 x K.",
          sep = ""
        ),
        call. = FALSE
        )
      } else {
        if (any(obj@par$lambda <= 0)) {
          stop(paste("Wrong specification of slot @par: ",
            "Exponential parameters ",
            "must be positive.",
            sep = ""
          ),
          call. = FALSE
          )
        }
      }
    } else {
      warning(paste("Wrong specification of slot 'par' in 'model' object. ",
        "Exponential parameters must be named 'lambda'.",
        sep = ""
      ),
      call. = FALSE
      )
    }
  }
}

### -----------------------------------------------------------------------------
### .valid.Exponential.Model
### @description    Validity check for parameters of a Exponential mixture.
### @par    obj     a model object
### @return         An error if parameters do fail certain necessary conditions.
###                 A warning if parameters do fail consistency.
### @detail     This validity check is called in the setters to ensure that
###             slots can be changed without errors but help the user to
###             get a inherently consistent model object.
###             The parameter list must contain an element 'lambda' that is
###             an 1 x K array, vector or matrix with numeric or integer values
###             all positive.
### @see        $model
### @author     Lars Simon Zehnder
### -----------------------------------------------------------------------------
#' @noRd
".valid.Exponential.Model" <- function(obj) {
  if (length(par) > 0) {
    if ("lambda" %in% names(obj@par)) {
      if (!is.array(obj@par$lambda) && !is.vector(obj@par$lambda) &&
        !is.matrix(obj@par$lambda)) {
        stop(paste("Wrong specification of slot @par: ",
          "Exponential parameters must be either an ",
          "array, a vector or a matrix of dimension ",
          "1 x K.",
          sep = ""
        ),
        call. = FALSE
        )
      }
      obj@par$lambda <- as.vector(obj@par$lambda)
      if (!is.numeric(obj@par$lambda) && !is.integer(obj@par$lambda)) {
        stop(paste("Wrong specification in slot 'par' of 'model' object. ",
          "parameters must be of type 'numeric' or 'integer'.",
          sep = ""
        ),
        call. = FALSE
        )
      }
      if (length(obj@par$lambda) != obj@K) {
        warning(paste("Wrong specification of slot @par: ",
          "lambda must be either an array, a vector ",
          "or a matrix of dimension 1 x K.",
          sep = ""
        ),
        call. = FALSE
        )
      } else {
        if (any(obj@par$lambda <= 0)) {
          stop(paste("Wrong specification of slot @par: ",
            "Exponential parameters ",
            "must be positive.",
            sep = ""
          ),
          call. = FALSE
          )
        }
      }
    } else {
      stop(paste("Wrong specification of slot 'par' in 'model' object. ",
        "Exponential parameters must be named 'lambda'.",
        sep = ""
      ),
      call. = FALSE
      )
    }
  }
}

### ------------------------------------------------------------------------------
### .init.valid.Normal.Model
### @description    Initial validity check for parameters of a univariate
###                 Normal mixture.
### @par    obj     a model object
### @return         An error if parameters fail certain conditions
### @detail         This initial validity check is called in the S4 constructor
###                 'model()' and ensures that the user constructs an inherently
###                 consistent model object.
###                 The parameter list must contain the following elements:
###                     mu:     an 1 x K array, vector or matrix containing
###                             'numeric' or 'integer' values
###                     sigma:  an 1 x K array, vector or matrix containing
###                             'numeric' or 'integer' values, all positive
###                     df:     an 1 x K array, vector or matrix containing
###                             'numeric' or 'integer' values, all positive
### @see            ?model, ?vignette('finmix')
### @author         Lars Simon Zehnder
### -------------------------------------------------------------------------------
#' @noRd
".init.valid.Normal.Model" <- function(obj) {
  if (length(obj@par) > 0) {
    if (!"mu" %in% names(obj@par)) {
      stop(paste("Wrong specification of slot @par: ",
        "univariate Normal mixtures need ",
        "a mean matrix named 'mu'.",
        sep = ""
      ),
      call. = FALSE
      )
    } else if (!is.array(obj@par$mu) && !is.vector(obj@par$mu) &&
      !is.matrix(obj@par$mu)) {
      stop(paste("Wrong specification of slot @par: ",
        "mu must be either an array, a vector ",
        "or a matrix of dimension 1 x K. ",
        sep = ""
      ),
      call. = FALSE
      )
    } else if (!all(is.numeric(as.vector(obj@par$mu)) ||
      is.integer(as.vector(obj@par$mu)))) {
      stop(paste("Wrong specification of slot @par: ",
        "Parameters must be of type 'numeric' or ",
        "'integer'.",
        sep = ""
      ),
      call. = FALSE
      )
    } else if (length(obj@par$mu) != obj@K) {
      stop(paste("Wrong specification of slot @par: ",
        "mu must be a matrix of dimension 1 x K ",
        "or a vector of size K.",
        sep = ""
      ),
      call. = FALSE
      )
    }
    if (!"sigma" %in% names(obj@par)) {
      stop(paste("Wrong specification of slot @par: ",
        "univariate Normal mictures need ",
        "a variance vector named ",
        "'sigma'",
        sep = ""
      ),
      call. = FALSE
      )
    } else if (!all(is.numeric(as.vector(obj@par$sigma)) ||
      is.integer(as.vector(obj@par$sigma)))) {
      stop(paste("Wrong specification of slot @par: ",
        "Parameters must be of type 'numeric' or ",
        "'integer'.",
        sep = ""
      ),
      call. = FALSE
      )
    } else if (any(obj@par$sigma <= 0)) {
      stop(paste("Wrong specification of slot @par: ",
        "sigma must contain variances, all ",
        "positive.",
        sep = ""
      ),
      .call = FALSE
      )
    } else if (!is.array(obj@par$sigma) && !is.vector(obj@par$sigma) &&
      !is.matrix(obj@par$sigma)) {
      stop(paste("Wrong specification of slot @par: ",
        "sigma must be either an array, a vector, ",
        "or a matrix of dimension 1 x K.",
        sep = ""
      ),
      call. = FALSE
      )
    } else if (length(obj@par$sigma) != obj@K) {
      stop(paste("Wrong specification of slot @par: ",
        "sigma must be either an array, a vector, ",
        "or a matrix, ",
        "or a matrix of dimension ",
        "1 x K.",
        sep = ""
      ),
      call. = FALSE
      )
    }
  }
}

### ------------------------------------------------------------------------------
### .valid.Normal.Model
### @description    Validity check for parameters of a univariate Normal
###                 mixture.
### @par    obj     a model object
### @return         An error if parameters fail certain necessary conditions and
###                 a warning if parameters fail consistency.
### @detail         This validity check is called in the setters to ensure that
###                 slots can be changed without errors but help the user to
###                 end up with an inherently consistent model object.
###                 The parameter list must contain the following elements:
###                     mu:     an 1 x K array, vector or matrix containing
###                             'numeric' or 'integer' values
###                     sigma:  an 1 x K array, vector or matrix containing
###                             'numeric' or 'integer' values, all positive
### @see            ?model, ?vignette('finmix')
### @author         Lars Simon Zehnder
### -------------------------------------------------------------------------------
#' @noRd
".valid.Normal.Model" <- function(obj) {
  if (length(obj@par) > 0) {
    if (!"mu" %in% names(obj@par)) {
      warning(paste("Wrong specification of slot @par: ",
        "univariate Normal mixtures need ",
        "a mean matrix named 'mu'.",
        sep = ""
      ),
      call. = FALSE
      )
    } else if (!is.array(obj@par$mu) && !is.vector(obj@par$mu) &&
      !is.matrix(obj@par$mu)) {
      warning(paste("Wrong specification of slot @par: ",
        "mu must be either an array, a vector ",
        "or a matrix of dimension 1 x K. ",
        sep = ""
      ),
      call. = FALSE
      )
    } else if (!all(is.numeric(as.vector(obj@par$mu)) ||
      is.integer(as.vector(obj@par$mu)))) {
      stop(paste("Wrong specification of slot @par: ",
        "Parameters must be of type 'numeric' or ",
        "'integer'.",
        sep = ""
      ),
      call. = FALSE
      )
    } else if (length(obj@par$mu) != obj@K) {
      warning(paste("Wrong specification of slot @par: ",
        "mu must be a matrix of dimension 1 x K ",
        "or a vector of size K.",
        sep = ""
      ),
      call. = FALSE
      )
    }
    if (!"sigma" %in% names(obj@par)) {
      warning(paste("Wrong specification of slot @par: ",
        "univariate Normal mixtures need ",
        "a variance vector named ",
        "'sigma'",
        sep = ""
      ),
      call. = FALSE
      )
    } else if (!all(is.numeric(as.vector(obj@par$sigma)) ||
      is.integer(as.vector(obj@par$sigma)))) {
      stop(paste("Wrong specification of slot @par: ",
        "Parameters must be of type 'numeric' or ",
        "'integer'.",
        sep = ""
      ),
      call. = FALSE
      )
    } else if (any(obj@par$sigma <= 0)) {
      stop(paste("Wrong specification of slot @par: ",
        "sigma must contain variances, all ",
        "positive.",
        sep = ""
      ),
      .call = FALSE
      )
    } else if (!is.array(obj@par$sigma) && !is.matrix(obj@par$sigma) &&
      !is.matrix(obj@par$sigma)) {
      warning(paste("Wrong specification of slot @par: ",
        "sigma must be either an array, a vector, ",
        "or a matrix of dimension 1 x K.",
        sep = ""
      ),
      call. = FALSE
      )
    } else if (length(obj@par$sigma) != obj@K) {
      warning(paste("Wrong specification of slot @par: ",
        "sigma must be either an array, a vector, ",
        "or a matrix, ",
        "or a matrix of dimension ",
        "1 x K.",
        sep = ""
      ),
      call. = FALSE
      )
    }
  }
}

### ----------------------------------------------------------------------------
### .init.valid.Normult.Model
### @description    Initial validity check for parameters of a multivariate
###                 Normal mixture.
### @par    obj     a model object
### @return         An error if parameters fail certain conditions
### @detail         This initial validity check is called in the S4 constructor
###                 'model()' and ensures that the user constructs an inherently
###                 consistent model object.
###                 The parameter list must contain the foillowing elements:
###                     mu:     an r x K matrix containing 'numeric' or
###                             'integer' values
###                     sigma:  am r x r x K array containing 'numeric' or
###                             'integer' matrices, all symmetric/positive
###                             definite
### @see        ?model, ?vignette('finmix')
### @author     Lars Simon Zehnder
### ----------------------------------------------------------------------------
#' @noRd
".init.valid.Normult.Model" <- function(obj) {
  if (length(obj@par) > 0) {
    if (!"mu" %in% names(obj@par)) {
      stop(paste("Wrong specification of slot @par: ",
        "multivariate Normal mixtures need ",
        "a mean matrix named 'mu'.",
        sep = ""
      ),
      call. = FALSE
      )
    } else if (!is.matrix(obj@par$mu)) {
      stop(paste("Wrong specification of slot @par: ",
        "mu is not a matrix. ",
        sep = ""
      ),
      call. = FALSE
      )
    } else if (!all(is.numeric(obj@par$mu) || !is.numeric(obj@par$mu))) {
      stop(paste("Wrong specification of slot @par: ",
        "parameters must be of type 'numeric ",
        "or 'integer'.",
        sep = ""
      ),
      call. = FALSE
      )
    } else if (!identical(dim(obj@par$mu), c(obj@r, obj@K))) {
      stop(paste("Wrong specification of slot @par: ",
        "mu must be a matrix of dimension r x K.",
        sep = ""
      ),
      call. = FALSE
      )
    }
    if (!"sigma" %in% names(obj@par)) {
      stop(paste("Wrong specification of slot @par: ",
        "multivariate Normal mixtures need ",
        "a variance-covariance array named ",
        "'sigma'",
        sep = ""
      ),
      call. = FALSE
      )
    } else if (!(is.numeric(obj@par$sigma) || is.integer(obj@par$mu))) {
      stop(paste("Wrong specification of slot @par: ",
        "parameters must be of type 'numeric' ",
        "or 'integer'.",
        sep = ""
      ),
      call. = FALSE
      )
    } else if (!is.array(obj@par$sigma)) {
      stop(paste("Wrong specification of slot @par: ",
        "sigma is not an array.",
        sep = ""
      ),
      call. = FALSE
      )
    } else if (!all(apply(obj@par$sigma, 3, isSymmetric))) {
      stop(paste("Wrong specification of slot @par: ",
        "sigma must contain K symmetric ",
        "r x r matrices.",
        sep = ""
      ),
      call. = FALSE
      )
    } else if (!all(apply(obj@par$sigma, 3, function(x) {
      all(eigen(x)$values > 0)
    }))) {
      stop(paste("Wrong specification of slot @par: ",
        "sigma must contain K positive definite ",
        "r x r matrices.",
        sep = ""
      ),
      call. = FALSE
      )
    } else if (!identical(dim(obj@par$sigma), c(obj@r, obj@r, obj@K))) {
      stop(paste("Wrong specification of slot @par: ",
        "sigma must be an array of dimension ",
        "r x r x K.",
        sep = ""
      ),
      call. = FALSE
      )
    }
  }
}

### ----------------------------------------------------------------------------
### .valid.Normult.Model
### @description    Initial validity check for parameters of a multivariate
###                 Normal mixture.
### @par    obj     a model object
### @return         An error if parameters fail necessary conditions and
###                 a warning if parameters fail consistency
### @detail         This validity check is called in the setters to ensure that
###                 slots can be changed without errors but help the user to
###                 end up with an inherently consistent model object.
###                 The parameter list must contain the foillowing elements:
###                     mu:     an r x K matrix containing 'numeric' or
###                             'integer' values
###                     sigma:  am r x r x K array containing 'numeric' or
###                             'integer' matrices, all symmetric/positive
###                             definite
### @see        ?model, ?vignette('finmix')
### @author     Lars Simon Zehnder
### ----------------------------------------------------------------------------
#' @noRd
".valid.Normult.Model" <- function(obj) {
  if (length(obj@par) > 0) {
    if (!"mu" %in% names(obj@par)) {
      warning(paste("Wrong specification of slot @par: ",
        "multivariate Normal mixtures need ",
        "a mean matrix named 'mu'.",
        sep = ""
      ),
      call. = FALSE
      )
    } else if (!is.matrix(obj@par$mu)) {
      warning(paste("Wrong specification of slot @par: ",
        "mu is not a matrix. ",
        sep = ""
      ),
      call. = FALSE
      )
    } else if (!all(is.numeric(obj@par$mu) || is.numeric(obj@par$mu))) {
      stop(paste("Wrong specification of slot @par: ",
        "parameters must be of type 'numeric ",
        "or 'integer'.",
        sep = ""
      ),
      call. = FALSE
      )
    } else if (!identical(dim(obj@par$mu), c(obj@r, obj@K))) {
      warning(paste("Wrong specification of slot @par: ",
        "mu must be a matrix of dimension r x K.",
        sep = ""
      ),
      call. = FALSE
      )
    }
    if (!"sigma" %in% names(obj@par)) {
      warning(paste("Wrong specification of slot @par: ",
        "multivariate Normal mixtures need ",
        "a variance-covariance array named ",
        "'sigma'",
        sep = ""
      ),
      call. = FALSE
      )
    } else if (!(is.numeric(obj@par$sigma) || is.integer(obj@par$mu))) {
      stop(paste("Wrong specification of slot @par: ",
        "parameters must be of type 'numeric' ",
        "or 'integer'.",
        sep = ""
      ),
      call. = FALSE
      )
    } else if (!is.array(obj@par$sigma)) {
      warning(paste("Wrong specification of slot @par: ",
        "sigma is not an array.",
        sep = ""
      ),
      call. = FALSE
      )
    } else if (!all(apply(obj@par$sigma, 3, isSymmetric))) {
      warning(paste("Wrong specification of slot @par: ",
        "sigma must contain K symmetric ",
        "r x r matrices.",
        sep = ""
      ),
      call. = FALSE
      )
    } else if (!all(apply(obj@par$sigma, 3, function(x) {
      all(eigen(x)$values > 0)
    }))) {
      warning(paste("Wrong specification of slot @par: ",
        "sigma must contain K positive definite ",
        "r x r matrices.",
        sep = ""
      ),
      call. = FALSE
      )
    } else if (!identical(dim(obj@par$sigma), c(obj@r, obj@r, obj@K))) {
      warning(paste("Wrong specification of slot @par: ",
        "sigma must be an array of dimension ",
        "r x r x K.",
        sep = ""
      ),
      call. = FALSE
      )
    }
  }
}

### ------------------------------------------------------------------------------
### .init.valid.Student.Model
### @description    Initial validity check for parameters of a univariate
###                 Student-t mixture.
### @par    obj     a model object
### @return         An error if parameters fail certain conditions
### @detail         This initial validity check is called in the S4 constructor
###                 'model()' and ensures that the user constructs an inherently
###                 consistent model object.
###                 The parameter list must contain the following elements:
###                     mu:     an 1 x K array, vector or matrix containing
###                             'numeric' or 'integer' values
###                     sigma:  an 1 x K array, vector or matrix containing
###                             'numeric' or 'integer' values, all positive
###                     df:     an 1 x K array, vector or matrix containing
###                             'numeric' or 'integer' values, all positive
### @see            ?model, ?vignette('finmix')
### @author         Lars Simon Zehnder
### -------------------------------------------------------------------------------
#' @noRd
".init.valid.Student.Model" <- function(obj) {
  if (length(obj@par) > 0) {
    if (!"mu" %in% names(obj@par)) {
      stop(paste("Wrong specification of slot @par: ",
        "univariate Normal mixtures need ",
        "a mean matrix named 'mu'.",
        sep = ""
      ),
      call. = FALSE
      )
    } else if (!is.array(obj@par$mu) && !is.vector(obj@par$mu) &&
      !is.matrix(obj@par$mu)) {
      stop(paste("Wrong specification of slot @par: ",
        "mu must be either an array, a vector ",
        "or a matrix of dimension 1 x K. ",
        sep = ""
      ),
      call. = FALSE
      )
    } else if (!all(is.numeric(as.vector(obj@par$mu)) ||
      is.integer(as.vector(obj@par$mu)))) {
      stop(paste("Wrong specification of slot @par: ",
        "Parameters must be of type 'numeric' or ",
        "'integer'.",
        sep = ""
      ),
      call. = FALSE
      )
    } else if (length(obj@par$mu) != obj@K) {
      stop(paste("Wrong specification of slot @par: ",
        "mu must be a matrix of dimension 1 x K ",
        "or a vector of size K.",
        sep = ""
      ),
      call. = FALSE
      )
    }
    if (!"sigma" %in% names(obj@par)) {
      warning(paste("Wrong specification of slot @par: ",
        "univariate Student-t mixtures need ",
        "a variance vector named ",
        "'sigma'",
        sep = ""
      ),
      call. = FALSE
      )
    } else if (!all(is.numeric(as.vector(obj@par$sigma)) ||
      is.integer(as.vector(obj@par$sigma)))) {
      stop(paste("Wrong specification of slot @par: ",
        "Parameters must be of type 'numeric' or ",
        "'integer'.",
        sep = ""
      ),
      call. = FALSE
      )
    } else if (any(obj@par$sigma <= 0)) {
      stop(paste("Wrong specification of slot @par: ",
        "sigma must contain variances, all ",
        "positive.",
        sep = ""
      ),
      .call = FALSE
      )
    } else if (!is.array(obj@par$sigma) && is.vector(obj@par$sigma) &&
      is.matrix(obj@par$sigma)) {
      stop(paste("Wrong specification of slot @par: ",
        "sigma must be either an array, a vector, ",
        "or a matrix of dimension 1 x K.",
        sep = ""
      ),
      call. = FALSE
      )
    } else if (length(obj@par$sigma) != obj@K) {
      stop(paste("Wrong specification of slot @par: ",
        "sigma must be either an array, a vector, ",
        "or a matrix, ",
        "or a matrix of dimension ",
        "1 x K.",
        sep = ""
      ),
      call. = FALSE
      )
    }
    if (!"df" %in% names(obj@par)) {
      stop(paste("Wrong specification of slot @par: ",
        "Student-t mixtures need a degree of ",
        "freedom vector.",
        sep = ""
      ),
      call. = FALSE
      )
    } else if (!all(is.numeric(as.vector(obj@par$df)) ||
      is.integer(as.vector(obj@par$df)))) {
      stop(paste("Wrong specification of slot @par: ",
        "Parameters must be of type 'numeric' or ",
        "'integer'.",
        sep = ""
      ),
      call. = FALSE
      )
    } else if (any(obj@par$df <= 0)) {
      stop(paste("Wrong specification of slot @par: ",
        "Degrees of freedom must be all positive.",
        sep = ""
      ),
      call. = FALSE
      )
    } else if (length(obj@par$df) != obj@K) {
      stop(paste("Wrong specification of slot @par: ",
        "df must be a vector or matrix of ",
        "dimension 1 x K",
        sep = ""
      ),
      call. = FALSE
      )
    }
  }
}

### ------------------------------------------------------------------------------
### .valid.Student.Model
### @description    Validity check for parameters of a univariate Student-t
###                 mixture.
### @par    obj     a model object
### @return         An error if parameters fail certain necessary conditions and
###                 a warning if parameters fail consistency.
### @detail         This validity check is called in the setters to ensure that
###                 slots can be changed without errors but help the user to
###                 end up with an inherently consistent model object.
###                 The parameter list must contain the following elements:
###                     mu:     an 1 x K array, vector or matrix containing
###                             'numeric' or 'integer' values
###                     sigma:  an 1 x K array, vector or matrix containing
###                             'numeric' or 'integer' values, all positive
###                     df:     an 1 x K array, vector or matrix containing
###                             'numeric' or 'integer' values, all positive
### @see            ?model, ?vignette('finmix')
### @author         Lars Simon Zehnder
### -------------------------------------------------------------------------------
#' @noRd
".valid.Student.Model" <- function(obj) {
  if (length(obj@par) > 0) {
    if (!"mu" %in% names(obj@par)) {
      warning(paste("Wrong specification of slot @par: ",
        "univariate Normal mixtures need ",
        "a mean matrix named 'mu'.",
        sep = ""
      ),
      call. = FALSE
      )
    } else if (!is.array(obj@par$mu) && !is.vector(obj@par$mu) &&
      !is.matrix(obj@par$mu)) {
      warning(paste("Wrong specification of slot @par: ",
        "mu must be either an array, a vector ",
        "or a matrix of dimension 1 x K. ",
        sep = ""
      ),
      call. = FALSE
      )
    } else if (!all(is.numeric(as.vector(obj@par$mu)) ||
      is.integer(as.vector(obj@par$mu)))) {
      stop(paste("Wrong specification of slot @par: ",
        "Parameters must be of type 'numeric' or ",
        "'integer'.",
        sep = ""
      ),
      call. = FALSE
      )
    } else if (length(obj@par$mu) != obj@K) {
      warning(paste("Wrong specification of slot @par: ",
        "mu must be a matrix of dimension 1 x K ",
        "or a vector of size K.",
        sep = ""
      ),
      call. = FALSE
      )
    }
    if (!"sigma" %in% names(obj@par)) {
      warning(paste("Wrong specification of slot @par: ",
        "univariate Normal mictures need ",
        "a variance vector named ",
        "'sigma'",
        sep = ""
      ),
      call. = FALSE
      )
    } else if (!all(is.numeric(as.vector(obj@par$sigma)) ||
      is.integer(as.vector(obj@par$sigma)))) {
      stop(paste("Wrong specification of slot @par: ",
        "Parameters must be of type 'numeric' or ",
        "'integer'.",
        sep = ""
      ),
      call. = FALSE
      )
    } else if (any(obj@par$sigma <= 0)) {
      stop(paste("Wrong specification of slot @par: ",
        "sigma must contain variances, all ",
        "positive.",
        sep = ""
      ),
      .call = FALSE
      )
    } else if (is.array(obj@par$sigma) && is.vector(obj@par$sigma) &&
      is.matrix(obj@par$sigma)) {
      warning(paste("Wrong specification of slot @par: ",
        "sigma must be either an array, a vector, ",
        "or a matrix of dimension 1 x K.",
        sep = ""
      ),
      call. = FALSE
      )
    } else if (length(obj@par$sigma) != obj@K) {
      warning(paste("Wrong specification of slot @par: ",
        "sigma must be either an array, a vector, ",
        "or a matrix, ",
        "or a matrix of dimension ",
        "1 x K.",
        sep = ""
      ),
      call. = FALSE
      )
    }
    if (!"df" %in% names(obj@par)) {
      warning(paste("Wrong specification of slot @par: ",
        "Student-t mixtures need a degree of ",
        "freedom vector.",
        sep = ""
      ),
      call. = FALSE
      )
    } else if (!all(is.numeric(as.vector(obj@par$df)) ||
      is.integer(as.vector(obj@par$df)))) {
      stop(paste("Wrong specification of slot @par: ",
        "Parameters must be of type 'numeric' or ",
        "'integer'.",
        sep = ""
      ),
      call. = FALSE
      )
    } else if (any(obj@par$df <= 0)) {
      stop(paste("Wrong specification of slot @par: ",
        "Degrees of freedom must be all positive.",
        sep = ""
      ),
      call. = FALSE
      )
    } else if (length(obj@par$df) != obj@K) {
      warning(paste("Wrong specification of slot @par: ",
        "df must be a vector or matrix of ",
        "dimension 1 x K",
        sep = ""
      ),
      call. = FALSE
      )
    }
  }
}

### ----------------------------------------------------------------------------
### .init.valid.Studmult.Model
### @description    Initial validity check for parameters of a multivariate
###                 Student-t mixture.
### @par    obj     a model object
### @return         An error if parameters fail certain conditions
### @detail         This initial validity check is called in the S4 constructor
###                 'model()' and ensures that the user constructs an inherently
###                 consistent model object.
###                 The parameter list must contain the foillowing elements:
###                     mu:     an r x K matrix containing 'numeric' or
###                             'integer' values
###                     sigma:  an r x r x K array containing 'numeric' or
###                             'integer' matrices, all symmetric/positive
###                             definite
###                     df:     an 1 x K array, vector or matrix containing
###                             'numeric' or 'integer', all positive
### @see        ?model, ?vignette('finmix')
### @author     Lars Simon Zehnder
### ----------------------------------------------------------------------------
#' @noRd
".init.valid.Studmult.Model" <- function(obj) {
  if (length(obj@par) > 0) {
    if (!"mu" %in% names(obj@par)) {
      stop(paste("Wrong specification of slot @par: ",
        "multivariate Student-t mixtures need ",
        "a mean matrix named 'mu'.",
        sep = ""
      ),
      call. = FALSE
      )
    } else if (!is.matrix(obj@par$mu)) {
      stop(paste("Wrong specification of slot @par: ",
        "mu is not a matrix. ",
        sep = ""
      ),
      call. = FALSE
      )
    } else if (!all(is.numeric(obj@par$mu) || is.numeric(obj@par$mu))) {
      stop(paste("Wrong specification of slot @par: ",
        "parameters must be of type 'numeric ",
        "or 'integer'.",
        sep = ""
      ),
      call. = FALSE
      )
    } else if (!identical(dim(obj@par$mu), c(obj@r, obj@K))) {
      stop(paste("Wrong specification of slot @par: ",
        "mu must be a matrix of dimension r x K.",
        sep = ""
      ),
      call. = FALSE
      )
    }
    if (!"sigma" %in% names(obj@par)) {
      stop(paste("Wrong specification of slot @par: ",
        "multivariate Student-t mictures need ",
        "a variance-covariance array named ",
        "'sigma'",
        sep = ""
      ),
      call. = FALSE
      )
    } else if (!(is.numeric(obj@par$sigma) || is.integer(obj@par$mu))) {
      stop(paste("Wrong specification of slot @par: ",
        "parameters must be of type 'numeric' ",
        "or 'integer'.",
        sep = ""
      ),
      call. = FALSE
      )
    } else if (!is.array(obj@par$sigma)) {
      stop(paste("Wrong specification of slot @par: ",
        "sigma is not an array.",
        sep = ""
      ),
      call. = FALSE
      )
    } else if (!all(apply(obj@par$sigma, 3, isSymmetric))) {
      stop(paste("Wrong specification of slot @par: ",
        "sigma must contain K symmetric ",
        "r x r matrices.",
        sep = ""
      ),
      call. = FALSE
      )
    } else if (!all(apply(obj@par$sigma, 3, function(x) {
      all(eigen(x)$values > 0)
    }))) {
      stop(paste("Wrong specification of slot @par: ",
        "sigma must contain K positive definite ",
        "r x r matrices.",
        sep = ""
      ),
      call. = FALSE
      )
    } else if (!identical(dim(obj@par$sigma), c(obj@r, obj@r, obj@K))) {
      stop(paste("Wrong specification of slot @par: ",
        "sigma must be an array of dimension ",
        "r x r x K.",
        sep = ""
      ),
      call. = FALSE
      )
    }
    if (!"df" %in% names(obj@par)) {
      warning(paste("Wrong specification of slot @par: ",
        "Student-t mixtures need a degree of ",
        "freedom vector.",
        sep = ""
      ),
      call. = FALSE
      )
    } else if (!all(is.numeric(as.vector(obj@par$df)) ||
      is.integer(as.vector(obj@par$df)))) {
      stop(paste("Wrong specification of slot @par: ",
        "Parameters must be of type 'numeric' or ",
        "'integer'.",
        sep = ""
      ),
      call. = FALSE
      )
    } else if (any(obj@par$df <= 0)) {
      stop(paste("Wrong specification of slot @par: ",
        "Degrees of freedom must be all positive.",
        sep = ""
      ),
      call. = FALSE
      )
    } else if (length(obj@par$df) != obj@K) {
      warning(paste("Wrong specification of slot @par: ",
        "df must be a vector or matrix of ",
        "dimension 1 x K",
        sep = ""
      ),
      call. = FALSE
      )
    }
  }
}

### ----------------------------------------------------------------------------
### .valid.Studmult.Model
### @description    Initial validity check for parameters of a multivariate
###                 Student-t mixture.
### @par    obj     a model object
### @return         An error if parameters fail necessary conditions and
###                 a warning if parameters fail consistency
### @detail         This validity check is called in the setters to ensure that
###                 slots can be changed without errors but help the user to
###                 end up with an inherently consistent model object.
###                 The parameter list must contain the foillowing elements:
###                     mu:     an r x K matrix containing 'numeric' or
###                             'integer' values
###                     sigma:  am r x r x K array containing 'numeric' or
###                             'integer' matrices, all symmetric/positive
###                             definite
###                     df:     an 1 x K array, vector or matrix containing
###                             'numeric' or 'integer' values, all positive
### @see        ?model, ?vignette('finmix')
### @author     Lars Simon Zehnder
### ----------------------------------------------------------------------------
#' @noRd
".valid.Studmult.Model" <- function(obj) {
  if (length(obj@par) > 0) {
    if (!"mu" %in% names(obj@par)) {
      warning(paste("Wrong specification of slot @par: ",
        "multivariate Student-t mixtures need ",
        "a mean matrix named 'mu'.",
        sep = ""
      ),
      call. = FALSE
      )
    } else if (!is.matrix(obj@par$mu)) {
      warning(paste("Wrong specification of slot @par: ",
        "mu is not a matrix. ",
        sep = ""
      ),
      call. = FALSE
      )
    } else if (!all(is.numeric(obj@par$mu) || is.numeric(obj@par$mu))) {
      stop(paste("Wrong specification of slot @par: ",
        "parameters must be of type 'numeric ",
        "or 'integer'.",
        sep = ""
      ),
      call. = FALSE
      )
    } else if (!identical(dim(obj@par$mu), c(obj@r, obj@K))) {
      warning(paste("Wrong specification of slot @par: ",
        "mu must be a matrix of dimension r x K.",
        sep = ""
      ),
      call. = FALSE
      )
    }
    if (!"sigma" %in% names(obj@par)) {
      warning(paste("Wrong specification of slot @par: ",
        "multivariate Student-t mictures need ",
        "a variance-covariance array named ",
        "'sigma'",
        sep = ""
      ),
      call. = FALSE
      )
    } else if (!(is.numeric(obj@par$sigma) || is.integer(obj@par$mu))) {
      stop(paste("Wrong specification of slot @par: ",
        "parameters must be of type 'numeric' ",
        "or 'integer'.",
        sep = ""
      ),
      call. = FALSE
      )
    } else if (!is.array(obj@par$sigma)) {
      warning(paste("Wrong specification of slot @par: ",
        "sigma is not an array.",
        sep = ""
      ),
      call. = FALSE
      )
    } else if (!all(apply(obj@par$sigma, 3, isSymmetric))) {
      warning(paste("Wrong specification of slot @par: ",
        "sigma must contain K symmetric ",
        "r x r matrices.",
        sep = ""
      ),
      call. = FALSE
      )
    } else if (!all(apply(obj@par$sigma, 3, function(x) {
      all(eigen(x)$values > 0)
    }))) {
      warning(paste("Wrong specification of slot @par: ",
        "sigma must contain K positive definite ",
        "r x r matrices.",
        sep = ""
      ),
      call. = FALSE
      )
    } else if (!identical(dim(obj@par$sigma), c(obj@r, obj@r, obj@K))) {
      warning(paste("Wrong specification of slot @par: ",
        "sigma must be an array of dimension ",
        "r x r x K.",
        sep = ""
      ),
      call. = FALSE
      )
    }
    if (!"df" %in% names(obj@par)) {
      warning(paste("Wrong specification of slot @par: ",
        "Student-t mixtures need a degree of ",
        "freedom vector.",
        sep = ""
      ),
      call. = FALSE
      )
    } else if (!all(is.numeric(as.vector(obj@par$df)) ||
      is.integer(as.vector(obj@par$df)))) {
      stop(paste("Wrong specification of slot @par: ",
        "Parameters must be of type 'numeric' or ",
        "'integer'.",
        sep = ""
      ),
      call. = FALSE
      )
    } else if (any(obj@par$df <= 0)) {
      stop(paste("Wrong specification of slot @par: ",
        "Degrees of freedom must be all positive.",
        sep = ""
      ),
      call. = FALSE
      )
    } else if (length(obj@par$df) != obj@K) {
      warning(paste("Wrong specification of slot @par: ",
        "df must be a vector or matrix of ",
        "dimension 1 x K",
        sep = ""
      ),
      call. = FALSE
      )
    }
  }
}

### Additional functions
#' Returns all univariate distributions
#' 
#' @description 
#' For internal usage only. 
#' 
#' @return A character vector containing all univariate distributions.
#' @noRd
".get.univ.Model" <- function() {
  univ <- c(
    "poisson", "cond.poisson",
    "binomial", "exponential",
    "normal", "student"
  )
  return(univ)
}

#' Returns all multivariate distributions
#' 
#' @description 
#' For internal usage only. 
#' 
#' @return A character vector containing all multivariate distributions.
#' @noRd
".get.multiv.Model" <- function() {
  multiv <- c("normult", "studmult")
  return(multiv)
}
simonsays1980/finmix documentation built on Dec. 23, 2021, 2:25 a.m.