"use strict";
/**
 * Reciprocal Rank Fusion (Task 308).
 *
 * Replaces the weighted-sum fusion (`0.7 * vector + 0.3 * bm25_norm`) when
 * the `MAXY_GS_RRF` flag is on. RRF is robust to score-distribution drift
 * between the vector and BM25 indexes — it sums `1 / (k + rank_i)` for each
 * node across the ranked lists it appears in. Higher rank (lower index) →
 * larger contribution; the constant `k` (default 60, per the gbrain
 * default) smooths the long tail.
 *
 * The weighted-sum baseline stays as the fallback so flag flips are
 * reversible without code edits.
 */
Object.defineProperty(exports, "__esModule", { value: true });
exports.fuseRrf = fuseRrf;
/**
 * Fuse multiple ranked lists by Reciprocal Rank Fusion.
 *
 * Each input list is treated as already sorted (best first). For each
 * candidate, score = sum over lists of `1 / (k + rank)`. Returns the union
 * sorted by descending RRF score.
 *
 * Ties on RRF score are broken by the candidate's earliest appearance
 * across the input lists (first-seen wins), which is deterministic given
 * a deterministic input order.
 */
function fuseRrf(rankedLists, k = 60) {
    const scores = new Map();
    const representatives = new Map();
    const firstSeen = new Map();
    let order = 0;
    for (const list of rankedLists) {
        for (let rank = 0; rank < list.length; rank++) {
            const candidate = list[rank];
            const contribution = 1 / (k + rank);
            const prior = scores.get(candidate.nodeId) ?? 0;
            scores.set(candidate.nodeId, prior + contribution);
            if (!representatives.has(candidate.nodeId)) {
                representatives.set(candidate.nodeId, candidate);
                firstSeen.set(candidate.nodeId, order++);
            }
        }
    }
    const out = [];
    for (const [nodeId, rrfScore] of scores) {
        const rep = representatives.get(nodeId);
        out.push({ ...rep, rrfScore });
    }
    out.sort((a, b) => {
        if (b.rrfScore !== a.rrfScore)
            return b.rrfScore - a.rrfScore;
        return (firstSeen.get(a.nodeId) - firstSeen.get(b.nodeId));
    });
    return out;
}
//# sourceMappingURL=rrf-fusion.js.map