// Original teaching example, 2026-10-10. No external packages or network calls. // Contract: both inputs are JavaScript numbers, amountMinor is an integer // in [0, 1_000_000_000], and ratePercent is an integer in [0, 100]. // Result: round the final payable amount to the nearest minor unit, // with exact half units rounded up. This is a chosen teaching rule, // not a statement of any merchant's policy or jurisdiction's law. export const MAX_AMOUNT_MINOR = 1_000_000_000; function validate(amountMinor, ratePercent) { if (!Number.isSafeInteger(amountMinor)) { throw new TypeError('amountMinor must be an integer number'); } if (!Number.isSafeInteger(ratePercent)) { throw new TypeError('ratePercent must be an integer number'); } if (amountMinor < 0 || amountMinor > MAX_AMOUNT_MINOR) { throw new RangeError('amountMinor must be between 0 and 1000000000'); } if (ratePercent < 0 || ratePercent > 100) { throw new RangeError('ratePercent must be between 0 and 100'); } return [BigInt(amountMinor), BigInt(ratePercent)]; } export function payableCorrect(amountMinor, ratePercent) { const [amount, rate] = validate(amountMinor, ratePercent); // All multiplication, addition and division here are exact integer // operations. For nonnegative inputs, +50 before /100 is half-up. // Conversion back to Number is safe because the result <= the input cap. return Number((amount * (100n - rate) + 50n) / 100n); } export function payableFaulty(amountMinor, ratePercent) { const [amount, rate] = validate(amountMinor, ratePercent); // DELIBERATE FAULT relative to the contract: round the discount first. return Number(amount - (amount * rate + 50n) / 100n); }