/**
 * Disjoint-set-union (union-find) primitives on a flat `Uint32Array` parent
 * table. Stateless helpers — callers own the buffer.
 *
 * Design choices:
 *  - **Path halving** rather than full path compression. Halving rewires
 *    `parent[x]` to `parent[parent[x]]` as it walks, which is `O(α(n))`
 *    amortised (same as full compression) but has a simpler control flow and
 *    is deterministic regardless of call order. Full compression's iterative
 *    second pass would observe a different mutation pattern depending on
 *    which subtree was queried first — fine for correctness, awkward for
 *    bit-exact same-runtime determinism.
 *  - **Union by min-index** rather than by rank or size. The smaller index
 *    always becomes the root. This makes the canonical root of a component
 *    a deterministic function of the bodies in it (the smallest body index
 *    of any member), which gives us a natural stable ordering when sorting
 *    islands at the end. Slightly worse worst-case tree height than
 *    union-by-rank but path halving keeps the amortised cost the same in
 *    practice for the body counts we care about (≤ millions).
 *
 * Used by {@link IslandBuilder} to partition awake bodies + contact pairs
 * into connected components (islands) so the solver can iterate each
 * independently and so whole islands can sleep atomically.
 *
 * @author Alex Goldring
 * @copyright Company Named Limited (c) 2026
 */

/**
 * Initialise `parent` so every node `i ∈ [0, n)` is its own root.
 * @param {Uint32Array} parent
 * @param {number} n
 */
export function uf_init(parent, n) {
    for (let i = 0; i < n; i++) parent[i] = i;
}

/**
 * Find the root of `x` with path halving. Mutates `parent` to compress the
 * walked path by one level per hop.
 * @param {Uint32Array} parent
 * @param {number} x
 * @returns {number} root index
 */
export function uf_find(parent, x) {
    let p = parent[x];
    while (p !== x) {
        // Path-halve: point x at its grandparent.
        const gp = parent[p];
        parent[x] = gp;
        x = gp;
        p = parent[x];
    }
    return x;
}

/**
 * Union the components containing `x` and `y`. The smaller-indexed root wins,
 * keeping the canonical root deterministic regardless of call order.
 * @param {Uint32Array} parent
 * @param {number} x
 * @param {number} y
 * @returns {number} the root of the unified component
 */
export function uf_union(parent, x, y) {
    const rx = uf_find(parent, x);
    const ry = uf_find(parent, y);
    if (rx === ry) return rx;
    if (rx < ry) {
        parent[ry] = rx;
        return rx;
    } else {
        parent[rx] = ry;
        return ry;
    }
}
