#' Power Fuzzy Cluster-wise Regression
#'
#' Fits `K` cluster-specific linear models \eqn{Y = X B_k + \varepsilon},
#' selecting an internal solver based on the chosen distance and exponent.
#'
#' @param X An `n x dx` data frame or matrix of covariates.
#' @param Y An `n x dy` data frame or matrix of dependent variables.
#' @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 on successive coefficient updates.
#'   Default `0.01`.
#' @param max.iter Maximum number of iterations. Default `100`.
#'
#' @return A list with elements:
#' \describe{
#'   \item{B}{Array of regression coefficients.}
#'   \item{d}{Data frame of distances (`n x K`).}
#'   \item{p}{Data frame of membership degrees (`n x K`).}
#'   \item{JDF}{Vector of objective-function values per iteration.}
#'   \item{l}{Hard cluster labels (length `n`).}
#'   \item{rho, cov}{(Mahalanobis only) cluster proportions and covariance
#'     matrices.}
#' }
#'
#' @examples
#' \dontrun{
#' library(flexCWM)
#' data("students")
#' Y <- students[, 2:3]
#' X <- students[, 4]
#' res <- PFCR(X, Y, K = 2, distance = "Mahalanobis")
#' table(res$l, students[, 1])
#' }
#'
#' @export
PFCR <- function(X, 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(X)) != nrow(as.matrix(Y))) {
    stop("'X' and 'Y' must have the same number of rows.")
  }

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

  if (distance == "Minkowski") {
    if (pow == 1) {
      res <- PDVRegressionWithMinkowskiEqualToOne(
        X = X, Y = Y, K = K, a = a, b = b, threshold, max.iter)
    } else {
      res <- PDVRegressionWithMinkowskiMoreThanOne(
        X = X, 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)
}
