#ifndef HEADERS_ALGORITHMS_PAM_HPP_
#define HEADERS_ALGORITHMS_PAM_HPP_

#include <omp.h>
#include <armadillo>
#include <vector>
#include <fstream>
#include <iostream>

#include "kmedoids_algorithm.hpp"


namespace km {
/**
 * @brief Contains all necessary PAM functions
 */
class PAM : public km::KMedoids {
 public:
  /**
  * @brief Runs PAM to identify a dataset's medoids.
  *
  * @param inputData Input data to cluster
  */
  void fitPAM(
  const arma::fmat& inputData,
    std::optional<std::reference_wrapper<const arma::fmat>> distMat);

  /**
  * @brief Performs the BUILD step of PAM.
  *
  * Loops over all datapoint and checks each's distance to every other
  * datapoint in the dataset, then adds the point with the lowest overall
  * loss to the set of medoids.
  *
  * @param data Transposed input data to cluster
  * @param medoidIndices Array of medoids that is modified in place
  * as medoids are identified
  */
  void buildPAM(
    const arma::fmat& data,
    std::optional<std::reference_wrapper<const arma::fmat>> distMat,
    arma::urowvec* medoidIndices);

  /**
  * @brief Performs the SWAP steps of BanditPAM.
  *
  * Loops over all (medoid, non-medoid) pairs and computes the change in loss
  * when the points are swapped in and out of the medoid set. Then updates
  * the list of medoids by performing the swap that would lower the overall
  * loss the most, provided at least one such swap would reduce the loss.
  *
  * @param data Transposed input data to cluster
  * @param medoidIndices Array of medoid indices created from the BUILD step
  * that is modified in place as better medoids are identified
  * @param assignments Array of containing the medoid each point is closest to
  */
  void swapPAM(
    const arma::fmat& data,
    std::optional<std::reference_wrapper<const arma::fmat>> distMat,
    arma::urowvec* medoidIndices,
    arma::urowvec* assignments);
};
}  // namespace km
#endif  // HEADERS_ALGORITHMS_PAM_HPP_
