#ifndef agglomerative_HPP
#define agglomerative_HPP


#include <vector>

// C++ standalone verion of fastcluster by Daniel Muellner
//
// Copyright: Daniel Muellner, 2011
//            Christoph Dalitz, 2020
// License:   BSD style license
//            (see the file LICENSE for details)
//
// from https://github.com/cdalitz/hclust-cpp/blob/master/fastcluster.h


// Assigns cluster labels (0, ..., nclust-1) to the n points such
// that the cluster result is split into nclust clusters.
//
// Input arguments:
//   n      = number of observables
//   merge  = clustering result in R format
//   nclust = number of clusters
// Output arguments:
//   labels = allocated integer array of size n for result
//
void cutree_k(long long n, const long long* merge, long long nclust, long long* labels);

//
// Assigns cluster labels (0, ..., nclust-1) to the n points such
// that the hierarchical clsutering is stopped at cluster distance cdist
//
// Input arguments:
//   n      = number of observables
//   merge  = clustering result in R format
//   height = cluster distance at each merge step
//   cdist  = cutoff cluster distance
// Output arguments:
//   labels = allocated integer array of size n for result
//
void cutree_cdist(long long n, const long long* merge, double* height, double cdist, long long* labels);

//
// Hierarchical clustering with one of Daniel Muellner's fast algorithms
//
// Input arguments:
//   n       = number of observables
//   distmat = condensed distance matrix, i.e. an n*(n-1)/2 array representing
//             the upper triangle (without diagonal elements) of the distance
//             matrix, e.g. for n=4:
//               d00 d01 d02 d03
//               d10 d11 d12 d13   ->  d01 d02 d03 d12 d13 d23
//               d20 d21 d22 d23
//               d30 d31 d32 d33
//   method  = cluster metric (see enum hclust_fast_methods)
// Output arguments:
//   merge   = allocated (n-1)x2 matrix (2*(n-1) array) for storing result.
//             Result follows R hclust convention:
//              - observabe indices start with one
//              - merge[i][] contains the merged nodes in step i
//              - merge[i][j] is negative when the node is an atom
//   height  = allocated (n-1) array with distances at each merge step
// Return code:
//   0 = ok
//   1 = invalid method
//
long long hclust_fast(long long n, double* distmat, long long method, long long* merge, double* height);
enum hclust_fast_methods {
    // single link with the minimum spanning tree algorithm (Rohlf, 1973)
    HCLUST_METHOD_SINGLE = 0,
    // complete link with the nearest-neighbor-chain algorithm (Murtagh, 1984)
    HCLUST_METHOD_COMPLETE = 1,
    // unweighted average link with the nearest-neighbor-chain algorithm (Murtagh, 1984)
    HCLUST_METHOD_AVERAGE = 2,
    // median link with the generic algorithm (Müllner, 2011)
    // requires euclidean distances as distance data
    HCLUST_METHOD_MEDIAN = 3
};

std::vector<long long> agglomerative_clustering(std::vector<std::vector<double>>& points, long long k, long long method);


#endif