// Find a 1:1 mapping from rows to columns of the specified square matrix such that the total cost is minimized. Returns a
// vector V such that V[i] = j maps rows i to columns j.
static std::vector<long>
findMinimumAssignment(const DistanceMatrix &matrix) {
#ifdef ROSE_HAVE_DLIB
    ASSERT_forbid(matrix.size() == 0);
    ASSERT_require(matrix.nr() == matrix.nc());

    // We can avoid the O(n^3) Kuhn-Munkres algorithm if all values of the matrix are the same.
    double minValue, maxValue;
    dlib::find_min_and_max(matrix, minValue /*out*/, maxValue /*out*/);
    if (minValue == maxValue) {
        std::vector<long> ident;
        ident.reserve(matrix.nr());
        for (long i=0; i<matrix.nr(); ++i)
            ident.push_back(i);
        return ident;
    }

    // Dlib's Kuhn-Munkres finds the *maximum* mapping over *integers*, so we negate everything to find the minumum, and we map
    // the doubles onto a reasonably large interval of integers. The interval should be large enough to have some precision,
    // but not so large that things might overflow.
    const int iGreatest = 1000000;                      // arbitrary upper bound for integer interval
    dlib::matrix<long> intMatrix(matrix.nr(), matrix.nc());
    for (long i=0; i<matrix.nr(); ++i) {
        for (long j=0; j<matrix.nc(); ++j)
            intMatrix(i, j) = round(-iGreatest * (matrix(i, j) - minValue) / (maxValue - minValue));
    }
    return dlib::max_cost_assignment(intMatrix);
#else
    throw FunctionSimilarity::Exception("dlib support is necessary for FunctionSimilarity analysis"
                                        "; see ROSE installation instructions");
#endif
}
// Given a square matrix and a 1:1 mapping from rows to columns, return the total cost of the mapping.
static double
totalAssignmentCost(const DistanceMatrix &matrix, const std::vector<long> assignment) {
    double sum = 0.0;
    ASSERT_require(matrix.nr() == matrix.nc());
    ASSERT_require((size_t)matrix.nr() == assignment.size());
    for (long i=0; i<matrix.nr(); ++i) {
        ASSERT_require(assignment[i] < matrix.nc());
        sum += matrix(i, assignment[i]);
    }
    return sum;
}