import { SPECIALIZATIONS } from '../data/specializations'; const FALLBACK_RANK = SPECIALIZATIONS.length - 1; const MAX_RANK_WEIGHT = SPECIALIZATIONS.length; export function priorityScore(specs: string[], ranking: string[]): number { const rankIndex = new Map(ranking.map((id, i) => [id, i])); return specs.reduce( (sum, id) => sum + (MAX_RANK_WEIGHT - (rankIndex.get(id) ?? FALLBACK_RANK)), 0, ); } export function makePriorityScorer(ranking: string[]): (specs: string[]) => number { const rankIndex = new Map(ranking.map((id, i) => [id, i])); return (specs) => specs.reduce( (sum, id) => sum + (MAX_RANK_WEIGHT - (rankIndex.get(id) ?? FALLBACK_RANK)), 0, ); } /** * Lexicographic comparison weight: each spec encoded as a bit, top-ranked * spec = highest bit. Higher rankWeight means a strictly preferred plan * under priority order (a single top-ranked spec outweighs any combination * of lower-ranked specs). Used by comparators; not for display. */ export function priorityRankWeight(specs: string[], ranking: string[]): number { const rankIndex = new Map(ranking.map((id, i) => [id, i])); const N = ranking.length; let w = 0; for (const id of specs) { const r = rankIndex.get(id); if (r === undefined) continue; w += 1 << (N - 1 - r); } return w; } export function makePriorityRankWeight(ranking: string[]): (specs: string[]) => number { const rankIndex = new Map(ranking.map((id, i) => [id, i])); const N = ranking.length; return (specs) => { let w = 0; for (const id of specs) { const r = rankIndex.get(id); if (r === undefined) continue; w += 1 << (N - 1 - r); } return w; }; }