#' Power Fuzzy Clustering
#'
#' Clusters the rows of `Y` into `K` groups using Minkowski or adaptive
#' regularised Mahalanobis distance.
#'
#' @param Y An `n x dy` data frame or matrix of observations.
#' @param K Number of clusters (positive integer).
#' @param m Fuzzifier, must be strictly greater than 1. Default `2`.
#' @param q Distance exponent, must be strictly greater than 0. Default `2`.
#' @param distance Either `"Minkowski"` or `"Mahalanobis"`.
#' @param p Minkowski exponent (`>= 1`). Ignored when
#'   `distance = "Mahalanobis"`. Default `2`.
#' @param alpha Regularisation weight for the Mahalanobis covariance.
#'   Default `0.5`.
#' @param beta Eigenvalue ratio bound for the Mahalanobis covariance.
#'   Default `1e15`.
#' @param threshold Convergence tolerance. Default `0.01`.
#' @param max.iter Maximum number of iterations. Default `100`.
#'
#' @return A list with elements `B` (or `C`) for cluster centres, `d`
#'   (distances), `p` (memberships), `JDF` (objective history), and `l`
#'   (hard labels). For Mahalanobis, also `rho` and `cov`.
#'
#' @examples
#' res <- PFC(iris[, 1:4], K = 3)
#' table(res$l, iris[, 5])
#'
#' @export
PFC <- function(Y, K, m = 2, q = 2, distance = "Minkowski", p = 2,
                alpha = 0.5, beta = 10^15, threshold = 0.01, max.iter = 100) {

  if (distance != "Minkowski" & distance != "Mahalanobis") {
    stop("'distance' must be either 'Mahalanobis' or 'Minkowski'.")
  }
  if (!is.numeric(K) || length(K) != 1 || K < 1 || K != round(K)) {
    stop("'K' must be a positive integer.")
  }
  if (m <= 1)  stop("'m' (fuzzifier) must be strictly greater than 1.")
  if (q <= 0)  stop("'q' (distance exponent) must be strictly greater than 0.")
  if (p < 1)   stop("'p' (Minkowski exponent) must be >= 1.")
  if (nrow(as.matrix(Y)) < K) {
    stop("Number of observations must be >= K.")
  }

  a   <- m
  b   <- q
  pow <- p
  X   <- NULL

  if (distance == "Minkowski") {
    if (pow == 1) {
      res <- PDVWithMinkowskiEqualToOne(
        Y = Y, K = K, a = a, b = b, threshold, max.iter)
    } else {
      res <- PDVWithMinkowskiMoreThanOne(
        Y = Y, K = K, a = a, b = b, pow = pow, threshold, max.iter)
    }
  } else {
    res <- PDVRegressionWithCovar(
      X = X, Y = Y, K = K, a = a, b = b, alpha, beta, threshold, max.iter)
  }

  return(res)
}
