/**
 * In-place reduction of a strided contact-candidate buffer to at most `maxKept`
 * points:
 *   1. keep the deepest candidate (penetration depth at lane `stride - 1`) in
 *      slot 0;
 *   2. then greedily pick the candidate whose MINIMUM squared distance to the
 *      already-kept set is largest (farthest-point sampling) — a behaviour-
 *      preserving max-spread approximation that avoids degenerate near-coincident
 *      manifolds.
 *
 * The contact position is lanes 0,1,2 of each `stride`-float record; the buffer
 * is reordered in place and the caller reads the first `maxKept` records.
 *
 * Shared by {@link box_box_manifold} and {@link convex_convex_manifold} (both
 * stride 4), which previously each carried a byte-identical copy. The stride-7
 * (box_triangle) and stride-10 (narrowphase_step) variants differ only in stride
 * and can adopt this too.
 *
 * @param {Float64Array} buf  strided candidate buffer
 * @param {number} n          input candidate count
 * @param {number} stride     floats per candidate (depth at lane stride-1)
 * @param {number} maxKept    maximum candidates to keep
 * @returns {number} kept count = min(n, maxKept)
 *
 * @author Alex Goldring
 * @copyright Company Named Limited (c) 2026
 */
export function reduce_manifold_contacts(buf, n, stride, maxKept) {
    if (n <= maxKept) return n;

    const depth_lane = stride - 1;

    // Deepest into slot 0.
    let deepest_idx = 0;
    let deepest_val = buf[depth_lane];
    for (let i = 1; i < n; i++) {
        const v = buf[i * stride + depth_lane];
        if (v > deepest_val) { deepest_val = v; deepest_idx = i; }
    }
    if (deepest_idx !== 0) swap(buf, stride, 0, deepest_idx);

    // Greedy farthest-point for the remaining slots.
    for (let k = 1; k < maxKept; k++) {
        let best_score = -1;
        let best_i = -1;
        for (let i = k; i < n; i++) {
            let min_d2 = Infinity;
            for (let j = 0; j < k; j++) {
                const dx = buf[i * stride] - buf[j * stride];
                const dy = buf[i * stride + 1] - buf[j * stride + 1];
                const dz = buf[i * stride + 2] - buf[j * stride + 2];
                const d2 = dx * dx + dy * dy + dz * dz;
                if (d2 < min_d2) min_d2 = d2;
            }
            if (min_d2 > best_score) { best_score = min_d2; best_i = i; }
        }
        if (best_i !== k) swap(buf, stride, k, best_i);
    }
    return maxKept;
}

function swap(buf, stride, a, b) {
    const ao = a * stride, bo = b * stride;
    for (let k = 0; k < stride; k++) {
        const t = buf[ao + k];
        buf[ao + k] = buf[bo + k];
        buf[bo + k] = t;
    }
}
