1// UQO Éval — Métré : arithmétique monétaire sûre (entiers de cents). Module PUR.2/**3 * Toutes les sommes du moteur d'estimation sont accumulées en CENTS entiers4 * (bigint-safe en pratique : < 2^53) et arrondies explicitement (arrondi5 * commercial, demi vers le haut) — jamais de flottants additionnés en cascade.6 */78export type Cents = number;910/** $ (flottant) → cents (entier), arrondi demi vers le haut. */11export function toCents(amount: number): Cents {12 if (!Number.isFinite(amount)) return 0;13 return roundHalfUp(amount * 100);14}1516/** cents → $ avec deux décimales exactes. */17export function fromCents(c: Cents): number {18 return Math.round(c) / 100;19}2021/** Arrondi demi vers le haut, symétrique pour les négatifs (−1,5 → −2). */22export function roundHalfUp(x: number): number {23 const s = Math.sign(x) || 1;24 return s * Math.floor(Math.abs(x) + 0.5 + 1e-9);25}2627/** cents × facteur (flottant) → cents arrondis. */28export function mulCents(c: Cents, factor: number): Cents {29 return roundHalfUp(c * factor);30}3132/** pourcentage d'un montant : cents × pct/100 → cents arrondis. */33export function pctOf(c: Cents, pct: number): Cents {34 return roundHalfUp((c * pct) / 100);35}3637export function sumCents(xs: Cents[]): Cents {38 let s = 0;39 for (const x of xs) s += Math.round(x);40 return s;41}4243/** Arrondit des cents au multiple demandé (1 = cent, 100 = dollar, 10000 = 100 $). */44export function roundCentsTo(c: Cents, step: number): Cents {45 if (!(step > 1)) return Math.round(c);46 return roundHalfUp(c / step) * step;47}4849/** Quantité × prix unitaire ($) → cents (le produit est arrondi une seule fois). */50export function lineCents(quantity: number, unitPrice: number): Cents {51 if (!Number.isFinite(quantity) || !Number.isFinite(unitPrice)) return 0;52 return roundHalfUp(quantity * unitPrice * 100);53}54