// UQO Éval — Métré : arithmétique monétaire sûre (entiers de cents). Module PUR. /** * Toutes les sommes du moteur d'estimation sont accumulées en CENTS entiers * (bigint-safe en pratique : < 2^53) et arrondies explicitement (arrondi * commercial, demi vers le haut) — jamais de flottants additionnés en cascade. */ export type Cents = number; /** $ (flottant) → cents (entier), arrondi demi vers le haut. */ export function toCents(amount: number): Cents { if (!Number.isFinite(amount)) return 0; return roundHalfUp(amount * 100); } /** cents → $ avec deux décimales exactes. */ export function fromCents(c: Cents): number { return Math.round(c) / 100; } /** Arrondi demi vers le haut, symétrique pour les négatifs (−1,5 → −2). */ export function roundHalfUp(x: number): number { const s = Math.sign(x) || 1; return s * Math.floor(Math.abs(x) + 0.5 + 1e-9); } /** cents × facteur (flottant) → cents arrondis. */ export function mulCents(c: Cents, factor: number): Cents { return roundHalfUp(c * factor); } /** pourcentage d'un montant : cents × pct/100 → cents arrondis. */ export function pctOf(c: Cents, pct: number): Cents { return roundHalfUp((c * pct) / 100); } export function sumCents(xs: Cents[]): Cents { let s = 0; for (const x of xs) s += Math.round(x); return s; } /** Arrondit des cents au multiple demandé (1 = cent, 100 = dollar, 10000 = 100 $). */ export function roundCentsTo(c: Cents, step: number): Cents { if (!(step > 1)) return Math.round(c); return roundHalfUp(c / step) * step; } /** Quantité × prix unitaire ($) → cents (le produit est arrondi une seule fois). */ export function lineCents(quantity: number, unitPrice: number): Cents { if (!Number.isFinite(quantity) || !Number.isFinite(unitPrice)) return 0; return roundHalfUp(quantity * unitPrice * 100); }