FAILURE MAP
← Case archive

FA-58361 / Loan amortization schedules / Open access

Level payment with final adjustment: nearest tie rule · case 01

An exact half-cent payment is rounded down on even cents.

Verified by executionVariant 1 · 7 checks per implementationDownload source bundle ↓JSON ↗

ROOT CAUSE

Python round() applies banker rounding to half-cent ties.

VERIFIED REPAIR

Round half-up by flooring exact + 1/2.

Unsuccessful approach: Flooring rounds every fractional payment down.

Case contract

x = {'principal', 'rate_bp' (annual, monthly rate = bp/120000), 'months', 'round': 'nearest'|'up'}. The exact annuity payment P*r/(1-(1+r)^-n) (P/n at zero rate) is rounded half-up ('nearest') or up to the cent. Monthly interest is round_half_up(balance*bp/120000); months 1..n-1 pay the level payment and month n pays balance + interest. Return {'payment', 'final_payment', 'total_interest'}.

Why this case matters

Amortization engines drive borrower statements, payoff quotes and investor remittances; a misplaced rounding step, boundary or ordering rule compounds across hundreds of periods.

1 / The failure

Exit 1
"""Failure Map reference implementation. Python standard library only."""
import json
import math
from fractions import Fraction
N = 1
observations = []
def solve(x):
    P = x['principal']
    n = x['months']
    bp = x['rate_bp']
    if bp == 0:
        exact = Fraction(P, n)
    else:
        r = Fraction(bp, 120000)
        exact = P * r / (1 - (1 + r) ** -n)
    if x['round'] == 'up':
        pay = math.ceil(exact)
    else:
        pay = round(exact)
    bal = P
    total_int = 0
    last = 0
    for k in range(1, n + 1):
        q, rem = divmod(bal * bp, 120000)
        interest = q + (1 if 2 * rem >= 120000 else 0)
        total_int += interest
        if k == n:
            last = bal + interest
        else:
            bal = bal + interest - pay
    return {'payment': pay, 'final_payment': last, 'total_interest': total_int}
def check(label, actual, expected):
    observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
fixtures = [[['regression: interest-free half-cent tie', {'principal': 1001, 'rate_bp': 0, 'months': 2, 'round': 'nearest'}, {'payment': 501, 'final_payment': 500, 'total_interest': 0}], ['regression: nearest tie rule', {'principal': 500, 'rate_bp': 600, 'months': 1, 'round': 'nearest'}, {'payment': 503, 'final_payment': 503, 'total_interest': 3}], ['control 1', {'principal': 7, 'rate_bp': 725, 'months': 360, 'round': 'up'}, {'payment': 1, 'final_payment': -1123, 'total_interest': -771}], ['control 2', {'principal': 1000000, 'rate_bp': 13, 'months': 36, 'round': 'up'}, {'payment': 27834, 'final_payment': 27808, 'total_interest': 1998}], ['control 3', {'principal': 12345, 'rate_bp': 1200, 'months': 1, 'round': 'nearest'}, {'payment': 12468, 'final_payment': 12468, 'total_interest': 123}], ['control 4', {'principal': 999999, 'rate_bp': 0, 'months': 60, 'round': 'nearest'}, {'payment': 16667, 'final_payment': 16646, 'total_interest': 0}], ['control 5', {'principal': 30000000, 'rate_bp': 0, 'months': 36, 'round': 'up'}, {'payment': 833334, 'final_payment': 833310, 'total_interest': 0}]], [['regression: interest-free half-cent tie', {'principal': 1001, 'rate_bp': 0, 'months': 2, 'round': 'nearest'}, {'payment': 501, 'final_payment': 500, 'total_interest': 0}], ['regression: nearest tie rule', {'principal': 500, 'rate_bp': 600, 'months': 1, 'round': 'nearest'}, {'payment': 503, 'final_payment': 503, 'total_interest': 3}], ['control 1', {'principal': 500, 'rate_bp': 13, 'months': 3, 'round': 'up'}, {'payment': 167, 'final_payment': 166, 'total_interest': 0}], ['control 2', {'principal': 2500000, 'rate_bp': 1999, 'months': 3, 'round': 'nearest'}, {'payment': 861250, 'final_payment': 861251, 'total_interest': 83751}], ['control 3', {'principal': 999999, 'rate_bp': 725, 'months': 360, 'round': 'nearest'}, {'payment': 6822, 'final_payment': 6503, 'total_interest': 1455602}], ['control 4', {'principal': 7, 'rate_bp': 0, 'months': 60, 'round': 'nearest'}, {'payment': 0, 'final_payment': 7, 'total_interest': 0}], ['control 5', {'principal': 999999, 'rate_bp': 0, 'months': 3, 'round': 'nearest'}, {'payment': 333333, 'final_payment': 333333, 'total_interest': 0}]], [['regression: interest-free half-cent tie', {'principal': 1001, 'rate_bp': 0, 'months': 2, 'round': 'nearest'}, {'payment': 501, 'final_payment': 500, 'total_interest': 0}], ['control 1', {'principal': 2500000, 'rate_bp': 13, 'months': 1, 'round': 'nearest'}, {'payment': 2500271, 'final_payment': 2500271, 'total_interest': 271}], ['control 2', {'principal': 100000, 'rate_bp': 1999, 'months': 3, 'round': 'nearest'}, {'payment': 34450, 'final_payment': 34450, 'total_interest': 3350}], ['control 3', {'principal': 999999, 'rate_bp': 0, 'months': 12, 'round': 'nearest'}, {'payment': 83333, 'final_payment': 83336, 'total_interest': 0}], ['control 4', {'principal': 12345, 'rate_bp': 0, 'months': 12, 'round': 'up'}, {'payment': 1029, 'final_payment': 1026, 'total_interest': 0}], ['control 5', {'principal': 1000000, 'rate_bp': 0, 'months': 360, 'round': 'up'}, {'payment': 2778, 'final_payment': 2698, 'total_interest': 0}], ['control 6', {'principal': 999999, 'rate_bp': 1999, 'months': 3, 'round': 'up'}, {'payment': 344500, 'final_payment': 344499, 'total_interest': 33500}]], [['regression: interest-free half-cent tie', {'principal': 1001, 'rate_bp': 0, 'months': 2, 'round': 'nearest'}, {'payment': 501, 'final_payment': 500, 'total_interest': 0}], ['regression: nearest tie rule', {'principal': 12345, 'rate_bp': 0, 'months': 2, 'round': 'nearest'}, {'payment': 6173, 'final_payment': 6172, 'total_interest': 0}], ['control 1', {'principal': 7, 'rate_bp': 13, 'months': 12, 'round': 'up'}, {'payment': 1, 'final_payment': -4, 'total_interest': 0}], ['control 2', {'principal': 1000000, 'rate_bp': 13, 'months': 60, 'round': 'up'}, {'payment': 16722, 'final_payment': 16710, 'total_interest': 3308}], ['control 3', {'principal': 500, 'rate_bp': 725, 'months': 7, 'round': 'up'}, {'payment': 74, 'final_payment': 68, 'total_interest': 12}], ['control 4', {'principal': 7, 'rate_bp': 725, 'months': 7, 'round': 'up'}, {'payment': 2, 'final_payment': -5, 'total_interest': 0}], ['control 5', {'principal': 100000, 'rate_bp': 1999, 'months': 60, 'round': 'up'}, {'payment': 2649, 'final_payment': 2626, 'total_interest': 58917}]], [['regression: interest-free half-cent tie', {'principal': 1001, 'rate_bp': 0, 'months': 2, 'round': 'nearest'}, {'payment': 501, 'final_payment': 500, 'total_interest': 0}], ['regression: nearest tie rule', {'principal': 500, 'rate_bp': 600, 'months': 1, 'round': 'nearest'}, {'payment': 503, 'final_payment': 503, 'total_interest': 3}], ['control 1', {'principal': 30000000, 'rate_bp': 499, 'months': 2, 'round': 'nearest'}, {'payment': 15093627, 'final_payment': 15093627, 'total_interest': 187254}], ['control 2', {'principal': 999999, 'rate_bp': 0, 'months': 3, 'round': 'nearest'}, {'payment': 333333, 'final_payment': 333333, 'total_interest': 0}], ['control 3', {'principal': 500, 'rate_bp': 0, 'months': 2, 'round': 'up'}, {'payment': 250, 'final_payment': 250, 'total_interest': 0}], ['control 4', {'principal': 7, 'rate_bp': 600, 'months': 360, 'round': 'up'}, {'payment': 1, 'final_payment': -876, 'total_interest': -524}], ['control 5', {'principal': 100000, 'rate_bp': 499, 'months': 60, 'round': 'up'}, {'payment': 1887, 'final_payment': 1866, 'total_interest': 13199}]]]
for label, args, expected in fixtures[N-1]:
    try:
        actual = solve(args)
    except Exception as exc:
        actual = 'raised ' + type(exc).__name__
    check(label, actual, expected)
print(json.dumps({"observations": observations, "passed": all(x["passed"] for x in observations)}, ensure_ascii=False))
raise SystemExit(0 if all(x["passed"] for x in observations) else 1)
Boundary fixtureActualExpectedOutcome
regression: interest-free half-cent tie{'final_payment': 501, 'payment': 500, 'total_interest': 0}{'final_payment': 500, 'payment': 501, 'total_interest': 0}Failed
regression: nearest tie rule{'final_payment': 503, 'payment': 502, 'total_interest': 3}{'final_payment': 503, 'payment': 503, 'total_interest': 3}Failed
control 1{'final_payment': -1123, 'payment': 1, 'total_interest': -771}{'final_payment': -1123, 'payment': 1, 'total_interest': -771}Passed
control 2{'final_payment': 27808, 'payment': 27834, 'total_interest': 1998}{'final_payment': 27808, 'payment': 27834, 'total_interest': 1998}Passed
control 3{'final_payment': 12468, 'payment': 12468, 'total_interest': 123}{'final_payment': 12468, 'payment': 12468, 'total_interest': 123}Passed
control 4{'final_payment': 16646, 'payment': 16667, 'total_interest': 0}{'final_payment': 16646, 'payment': 16667, 'total_interest': 0}Passed
control 5{'final_payment': 833310, 'payment': 833334, 'total_interest': 0}{'final_payment': 833310, 'payment': 833334, 'total_interest': 0}Passed

SHA-256 / 5485f961a698906a44da2f7a0f26147d261b29be5fdfcff29f7b952a55a18338

2 / The unsuccessful fix

Exit 1
"""Failure Map reference implementation. Python standard library only."""
import json
import math
from fractions import Fraction
N = 1
observations = []
def solve(x):
    P = x['principal']
    n = x['months']
    bp = x['rate_bp']
    if bp == 0:
        exact = Fraction(P, n)
    else:
        r = Fraction(bp, 120000)
        exact = P * r / (1 - (1 + r) ** -n)
    if x['round'] == 'up':
        pay = math.ceil(exact)
    else:
        pay = math.floor(exact)
    bal = P
    total_int = 0
    last = 0
    for k in range(1, n + 1):
        q, rem = divmod(bal * bp, 120000)
        interest = q + (1 if 2 * rem >= 120000 else 0)
        total_int += interest
        if k == n:
            last = bal + interest
        else:
            bal = bal + interest - pay
    return {'payment': pay, 'final_payment': last, 'total_interest': total_int}
def check(label, actual, expected):
    observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
fixtures = [[['regression: interest-free half-cent tie', {'principal': 1001, 'rate_bp': 0, 'months': 2, 'round': 'nearest'}, {'payment': 501, 'final_payment': 500, 'total_interest': 0}], ['regression: nearest tie rule', {'principal': 500, 'rate_bp': 600, 'months': 1, 'round': 'nearest'}, {'payment': 503, 'final_payment': 503, 'total_interest': 3}], ['control 1', {'principal': 7, 'rate_bp': 725, 'months': 360, 'round': 'up'}, {'payment': 1, 'final_payment': -1123, 'total_interest': -771}], ['control 2', {'principal': 1000000, 'rate_bp': 13, 'months': 36, 'round': 'up'}, {'payment': 27834, 'final_payment': 27808, 'total_interest': 1998}], ['control 3', {'principal': 12345, 'rate_bp': 1200, 'months': 1, 'round': 'nearest'}, {'payment': 12468, 'final_payment': 12468, 'total_interest': 123}], ['control 4', {'principal': 999999, 'rate_bp': 0, 'months': 60, 'round': 'nearest'}, {'payment': 16667, 'final_payment': 16646, 'total_interest': 0}], ['control 5', {'principal': 30000000, 'rate_bp': 0, 'months': 36, 'round': 'up'}, {'payment': 833334, 'final_payment': 833310, 'total_interest': 0}]], [['regression: interest-free half-cent tie', {'principal': 1001, 'rate_bp': 0, 'months': 2, 'round': 'nearest'}, {'payment': 501, 'final_payment': 500, 'total_interest': 0}], ['regression: nearest tie rule', {'principal': 500, 'rate_bp': 600, 'months': 1, 'round': 'nearest'}, {'payment': 503, 'final_payment': 503, 'total_interest': 3}], ['control 1', {'principal': 500, 'rate_bp': 13, 'months': 3, 'round': 'up'}, {'payment': 167, 'final_payment': 166, 'total_interest': 0}], ['control 2', {'principal': 2500000, 'rate_bp': 1999, 'months': 3, 'round': 'nearest'}, {'payment': 861250, 'final_payment': 861251, 'total_interest': 83751}], ['control 3', {'principal': 999999, 'rate_bp': 725, 'months': 360, 'round': 'nearest'}, {'payment': 6822, 'final_payment': 6503, 'total_interest': 1455602}], ['control 4', {'principal': 7, 'rate_bp': 0, 'months': 60, 'round': 'nearest'}, {'payment': 0, 'final_payment': 7, 'total_interest': 0}], ['control 5', {'principal': 999999, 'rate_bp': 0, 'months': 3, 'round': 'nearest'}, {'payment': 333333, 'final_payment': 333333, 'total_interest': 0}]], [['regression: interest-free half-cent tie', {'principal': 1001, 'rate_bp': 0, 'months': 2, 'round': 'nearest'}, {'payment': 501, 'final_payment': 500, 'total_interest': 0}], ['control 1', {'principal': 2500000, 'rate_bp': 13, 'months': 1, 'round': 'nearest'}, {'payment': 2500271, 'final_payment': 2500271, 'total_interest': 271}], ['control 2', {'principal': 100000, 'rate_bp': 1999, 'months': 3, 'round': 'nearest'}, {'payment': 34450, 'final_payment': 34450, 'total_interest': 3350}], ['control 3', {'principal': 999999, 'rate_bp': 0, 'months': 12, 'round': 'nearest'}, {'payment': 83333, 'final_payment': 83336, 'total_interest': 0}], ['control 4', {'principal': 12345, 'rate_bp': 0, 'months': 12, 'round': 'up'}, {'payment': 1029, 'final_payment': 1026, 'total_interest': 0}], ['control 5', {'principal': 1000000, 'rate_bp': 0, 'months': 360, 'round': 'up'}, {'payment': 2778, 'final_payment': 2698, 'total_interest': 0}], ['control 6', {'principal': 999999, 'rate_bp': 1999, 'months': 3, 'round': 'up'}, {'payment': 344500, 'final_payment': 344499, 'total_interest': 33500}]], [['regression: interest-free half-cent tie', {'principal': 1001, 'rate_bp': 0, 'months': 2, 'round': 'nearest'}, {'payment': 501, 'final_payment': 500, 'total_interest': 0}], ['regression: nearest tie rule', {'principal': 12345, 'rate_bp': 0, 'months': 2, 'round': 'nearest'}, {'payment': 6173, 'final_payment': 6172, 'total_interest': 0}], ['control 1', {'principal': 7, 'rate_bp': 13, 'months': 12, 'round': 'up'}, {'payment': 1, 'final_payment': -4, 'total_interest': 0}], ['control 2', {'principal': 1000000, 'rate_bp': 13, 'months': 60, 'round': 'up'}, {'payment': 16722, 'final_payment': 16710, 'total_interest': 3308}], ['control 3', {'principal': 500, 'rate_bp': 725, 'months': 7, 'round': 'up'}, {'payment': 74, 'final_payment': 68, 'total_interest': 12}], ['control 4', {'principal': 7, 'rate_bp': 725, 'months': 7, 'round': 'up'}, {'payment': 2, 'final_payment': -5, 'total_interest': 0}], ['control 5', {'principal': 100000, 'rate_bp': 1999, 'months': 60, 'round': 'up'}, {'payment': 2649, 'final_payment': 2626, 'total_interest': 58917}]], [['regression: interest-free half-cent tie', {'principal': 1001, 'rate_bp': 0, 'months': 2, 'round': 'nearest'}, {'payment': 501, 'final_payment': 500, 'total_interest': 0}], ['regression: nearest tie rule', {'principal': 500, 'rate_bp': 600, 'months': 1, 'round': 'nearest'}, {'payment': 503, 'final_payment': 503, 'total_interest': 3}], ['control 1', {'principal': 30000000, 'rate_bp': 499, 'months': 2, 'round': 'nearest'}, {'payment': 15093627, 'final_payment': 15093627, 'total_interest': 187254}], ['control 2', {'principal': 999999, 'rate_bp': 0, 'months': 3, 'round': 'nearest'}, {'payment': 333333, 'final_payment': 333333, 'total_interest': 0}], ['control 3', {'principal': 500, 'rate_bp': 0, 'months': 2, 'round': 'up'}, {'payment': 250, 'final_payment': 250, 'total_interest': 0}], ['control 4', {'principal': 7, 'rate_bp': 600, 'months': 360, 'round': 'up'}, {'payment': 1, 'final_payment': -876, 'total_interest': -524}], ['control 5', {'principal': 100000, 'rate_bp': 499, 'months': 60, 'round': 'up'}, {'payment': 1887, 'final_payment': 1866, 'total_interest': 13199}]]]
for label, args, expected in fixtures[N-1]:
    try:
        actual = solve(args)
    except Exception as exc:
        actual = 'raised ' + type(exc).__name__
    check(label, actual, expected)
print(json.dumps({"observations": observations, "passed": all(x["passed"] for x in observations)}, ensure_ascii=False))
raise SystemExit(0 if all(x["passed"] for x in observations) else 1)
Boundary fixtureActualExpectedOutcome
regression: interest-free half-cent tie{'final_payment': 501, 'payment': 500, 'total_interest': 0}{'final_payment': 500, 'payment': 501, 'total_interest': 0}Failed
regression: nearest tie rule{'final_payment': 503, 'payment': 502, 'total_interest': 3}{'final_payment': 503, 'payment': 503, 'total_interest': 3}Failed
control 1{'final_payment': -1123, 'payment': 1, 'total_interest': -771}{'final_payment': -1123, 'payment': 1, 'total_interest': -771}Passed
control 2{'final_payment': 27808, 'payment': 27834, 'total_interest': 1998}{'final_payment': 27808, 'payment': 27834, 'total_interest': 1998}Passed
control 3{'final_payment': 12468, 'payment': 12468, 'total_interest': 123}{'final_payment': 12468, 'payment': 12468, 'total_interest': 123}Passed
control 4{'final_payment': 16705, 'payment': 16666, 'total_interest': 0}{'final_payment': 16646, 'payment': 16667, 'total_interest': 0}Failed
control 5{'final_payment': 833310, 'payment': 833334, 'total_interest': 0}{'final_payment': 833310, 'payment': 833334, 'total_interest': 0}Passed

SHA-256 / e63085a36b73be1954a0e893df217c3f952e25ccba9871701b06ce0a1146eed3

3 / The verified repair

Exit 0
"""Failure Map reference implementation. Python standard library only."""
import json
import math
from fractions import Fraction
N = 1
observations = []
def solve(x):
    P = x['principal']
    n = x['months']
    bp = x['rate_bp']
    if bp == 0:
        exact = Fraction(P, n)
    else:
        r = Fraction(bp, 120000)
        exact = P * r / (1 - (1 + r) ** -n)
    if x['round'] == 'up':
        pay = math.ceil(exact)
    else:
        pay = math.floor(exact + Fraction(1, 2))
    bal = P
    total_int = 0
    last = 0
    for k in range(1, n + 1):
        q, rem = divmod(bal * bp, 120000)
        interest = q + (1 if 2 * rem >= 120000 else 0)
        total_int += interest
        if k == n:
            last = bal + interest
        else:
            bal = bal + interest - pay
    return {'payment': pay, 'final_payment': last, 'total_interest': total_int}
def check(label, actual, expected):
    observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
fixtures = [[['regression: interest-free half-cent tie', {'principal': 1001, 'rate_bp': 0, 'months': 2, 'round': 'nearest'}, {'payment': 501, 'final_payment': 500, 'total_interest': 0}], ['regression: nearest tie rule', {'principal': 500, 'rate_bp': 600, 'months': 1, 'round': 'nearest'}, {'payment': 503, 'final_payment': 503, 'total_interest': 3}], ['control 1', {'principal': 7, 'rate_bp': 725, 'months': 360, 'round': 'up'}, {'payment': 1, 'final_payment': -1123, 'total_interest': -771}], ['control 2', {'principal': 1000000, 'rate_bp': 13, 'months': 36, 'round': 'up'}, {'payment': 27834, 'final_payment': 27808, 'total_interest': 1998}], ['control 3', {'principal': 12345, 'rate_bp': 1200, 'months': 1, 'round': 'nearest'}, {'payment': 12468, 'final_payment': 12468, 'total_interest': 123}], ['control 4', {'principal': 999999, 'rate_bp': 0, 'months': 60, 'round': 'nearest'}, {'payment': 16667, 'final_payment': 16646, 'total_interest': 0}], ['control 5', {'principal': 30000000, 'rate_bp': 0, 'months': 36, 'round': 'up'}, {'payment': 833334, 'final_payment': 833310, 'total_interest': 0}]], [['regression: interest-free half-cent tie', {'principal': 1001, 'rate_bp': 0, 'months': 2, 'round': 'nearest'}, {'payment': 501, 'final_payment': 500, 'total_interest': 0}], ['regression: nearest tie rule', {'principal': 500, 'rate_bp': 600, 'months': 1, 'round': 'nearest'}, {'payment': 503, 'final_payment': 503, 'total_interest': 3}], ['control 1', {'principal': 500, 'rate_bp': 13, 'months': 3, 'round': 'up'}, {'payment': 167, 'final_payment': 166, 'total_interest': 0}], ['control 2', {'principal': 2500000, 'rate_bp': 1999, 'months': 3, 'round': 'nearest'}, {'payment': 861250, 'final_payment': 861251, 'total_interest': 83751}], ['control 3', {'principal': 999999, 'rate_bp': 725, 'months': 360, 'round': 'nearest'}, {'payment': 6822, 'final_payment': 6503, 'total_interest': 1455602}], ['control 4', {'principal': 7, 'rate_bp': 0, 'months': 60, 'round': 'nearest'}, {'payment': 0, 'final_payment': 7, 'total_interest': 0}], ['control 5', {'principal': 999999, 'rate_bp': 0, 'months': 3, 'round': 'nearest'}, {'payment': 333333, 'final_payment': 333333, 'total_interest': 0}]], [['regression: interest-free half-cent tie', {'principal': 1001, 'rate_bp': 0, 'months': 2, 'round': 'nearest'}, {'payment': 501, 'final_payment': 500, 'total_interest': 0}], ['control 1', {'principal': 2500000, 'rate_bp': 13, 'months': 1, 'round': 'nearest'}, {'payment': 2500271, 'final_payment': 2500271, 'total_interest': 271}], ['control 2', {'principal': 100000, 'rate_bp': 1999, 'months': 3, 'round': 'nearest'}, {'payment': 34450, 'final_payment': 34450, 'total_interest': 3350}], ['control 3', {'principal': 999999, 'rate_bp': 0, 'months': 12, 'round': 'nearest'}, {'payment': 83333, 'final_payment': 83336, 'total_interest': 0}], ['control 4', {'principal': 12345, 'rate_bp': 0, 'months': 12, 'round': 'up'}, {'payment': 1029, 'final_payment': 1026, 'total_interest': 0}], ['control 5', {'principal': 1000000, 'rate_bp': 0, 'months': 360, 'round': 'up'}, {'payment': 2778, 'final_payment': 2698, 'total_interest': 0}], ['control 6', {'principal': 999999, 'rate_bp': 1999, 'months': 3, 'round': 'up'}, {'payment': 344500, 'final_payment': 344499, 'total_interest': 33500}]], [['regression: interest-free half-cent tie', {'principal': 1001, 'rate_bp': 0, 'months': 2, 'round': 'nearest'}, {'payment': 501, 'final_payment': 500, 'total_interest': 0}], ['regression: nearest tie rule', {'principal': 12345, 'rate_bp': 0, 'months': 2, 'round': 'nearest'}, {'payment': 6173, 'final_payment': 6172, 'total_interest': 0}], ['control 1', {'principal': 7, 'rate_bp': 13, 'months': 12, 'round': 'up'}, {'payment': 1, 'final_payment': -4, 'total_interest': 0}], ['control 2', {'principal': 1000000, 'rate_bp': 13, 'months': 60, 'round': 'up'}, {'payment': 16722, 'final_payment': 16710, 'total_interest': 3308}], ['control 3', {'principal': 500, 'rate_bp': 725, 'months': 7, 'round': 'up'}, {'payment': 74, 'final_payment': 68, 'total_interest': 12}], ['control 4', {'principal': 7, 'rate_bp': 725, 'months': 7, 'round': 'up'}, {'payment': 2, 'final_payment': -5, 'total_interest': 0}], ['control 5', {'principal': 100000, 'rate_bp': 1999, 'months': 60, 'round': 'up'}, {'payment': 2649, 'final_payment': 2626, 'total_interest': 58917}]], [['regression: interest-free half-cent tie', {'principal': 1001, 'rate_bp': 0, 'months': 2, 'round': 'nearest'}, {'payment': 501, 'final_payment': 500, 'total_interest': 0}], ['regression: nearest tie rule', {'principal': 500, 'rate_bp': 600, 'months': 1, 'round': 'nearest'}, {'payment': 503, 'final_payment': 503, 'total_interest': 3}], ['control 1', {'principal': 30000000, 'rate_bp': 499, 'months': 2, 'round': 'nearest'}, {'payment': 15093627, 'final_payment': 15093627, 'total_interest': 187254}], ['control 2', {'principal': 999999, 'rate_bp': 0, 'months': 3, 'round': 'nearest'}, {'payment': 333333, 'final_payment': 333333, 'total_interest': 0}], ['control 3', {'principal': 500, 'rate_bp': 0, 'months': 2, 'round': 'up'}, {'payment': 250, 'final_payment': 250, 'total_interest': 0}], ['control 4', {'principal': 7, 'rate_bp': 600, 'months': 360, 'round': 'up'}, {'payment': 1, 'final_payment': -876, 'total_interest': -524}], ['control 5', {'principal': 100000, 'rate_bp': 499, 'months': 60, 'round': 'up'}, {'payment': 1887, 'final_payment': 1866, 'total_interest': 13199}]]]
for label, args, expected in fixtures[N-1]:
    try:
        actual = solve(args)
    except Exception as exc:
        actual = 'raised ' + type(exc).__name__
    check(label, actual, expected)
print(json.dumps({"observations": observations, "passed": all(x["passed"] for x in observations)}, ensure_ascii=False))
raise SystemExit(0 if all(x["passed"] for x in observations) else 1)
Boundary fixtureActualExpectedOutcome
regression: interest-free half-cent tie{'final_payment': 500, 'payment': 501, 'total_interest': 0}{'final_payment': 500, 'payment': 501, 'total_interest': 0}Passed
regression: nearest tie rule{'final_payment': 503, 'payment': 503, 'total_interest': 3}{'final_payment': 503, 'payment': 503, 'total_interest': 3}Passed
control 1{'final_payment': -1123, 'payment': 1, 'total_interest': -771}{'final_payment': -1123, 'payment': 1, 'total_interest': -771}Passed
control 2{'final_payment': 27808, 'payment': 27834, 'total_interest': 1998}{'final_payment': 27808, 'payment': 27834, 'total_interest': 1998}Passed
control 3{'final_payment': 12468, 'payment': 12468, 'total_interest': 123}{'final_payment': 12468, 'payment': 12468, 'total_interest': 123}Passed
control 4{'final_payment': 16646, 'payment': 16667, 'total_interest': 0}{'final_payment': 16646, 'payment': 16667, 'total_interest': 0}Passed
control 5{'final_payment': 833310, 'payment': 833334, 'total_interest': 0}{'final_payment': 833310, 'payment': 833334, 'total_interest': 0}Passed

SHA-256 / 25cc622ee827720c132f56574a9714783714c4f09a0abe601c6ff6bd43e5fa23

Verification & scope

A deterministic bounded teaching model with stipulated toy lending rules stated in the contract; money is integer cents and rates are basis points; it makes no claim of conformance to any regulation, servicing standard or product. This reproducer isolates one failure mechanism. Results cover the supplied fixtures. Variants within a family share a test contract and should remain grouped when constructing evaluation splits. Related mechanisms with a shared evaluation_group must also remain together; these controlled models are not independent production incidents.

Observations recorded using Python 3.12.14 at 2026-09-29T14:46:26.008851+00:00.

Case digest / d6ca8521bf66ef8381770abb1681f764c3dd4df18d31e2268a32e37a3ef9af56