FA-58821 / Loan amortization schedules / Open access
Graduated payment schedule: year boundary · case 01
Payments step up in the twelfth month of each year.
ROOT CAUSE
The year change is detected one month early.
VERIFIED REPAIR
Step at months 13, 25, 37 and so on.
Unsuccessful approach: Checking (m - 2) steps a month late.
Case contract
x = {'principal', 'rate_bp', 'months', 'initial_payment', 'step_bp', 'steps'}. Month m (1-based) is in year t = (m-1)//12. For t < steps+1 the year payment starts at initial_payment and each new year multiplies the previous year payment by (10000+step_bp)/10000, rounded half-up. At the start of year steps+1 (if the loan runs that long) the payment is re-leveled on the balance over the remaining months (exact annuity, half-up) and stays fixed. Interest round_half_up(balance*bp/120000); balance += interest - payment (it may grow). Return {'year_payments', 'peak_balance', 'end_balance'}.
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):
def rnd(n, d):
q, r = divmod(n, d)
return q + (1 if 2 * r >= d else 0)
def level(P, n):
if n <= 0 or P <= 0:
return 0
if bp == 0:
exact = Fraction(P, n)
else:
r = Fraction(bp, 120000)
exact = P * r / (1 - (1 + r) ** -n)
return math.floor(exact + Fraction(1, 2))
bp = x['rate_bp']
n = x['months']
bal = x['principal']
pay = x['initial_payment']
years = [pay]
peak = bal
for m in range(1, n + 1):
t = (m - 1) // 12
if m > 1 and m % 12 == 0:
if t <= x['steps']:
pay = rnd(pay * (10000 + x['step_bp']), 10000)
elif t == x['steps'] + 1:
pay = level(bal, n - m + 1)
years.append(pay)
interest = rnd(bal * bp, 120000)
bal += interest - pay
peak = max(peak, bal)
return {'year_payments': years, 'peak_balance': peak, 'end_balance': bal}
def check(label, actual, expected):
observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
fixtures = [[['regression: year boundary', {'principal': 250000, 'rate_bp': 600, 'months': 24, 'initial_payment': 3000, 'step_bp': 250, 'steps': 3}, {'year_payments': [3000, 3075], 'peak_balance': 250000, 'end_balance': 204568}], ['sample 1', {'principal': 250000, 'rate_bp': 900, 'months': 36, 'initial_payment': 8000, 'step_bp': 250, 'steps': 0}, {'year_payments': [8000, 7921, 7921], 'peak_balance': 250000, 'end_balance': 9}], ['sample 2', {'principal': 100000, 'rate_bp': 600, 'months': 24, 'initial_payment': 500, 'step_bp': 250, 'steps': 0}, {'year_payments': [500, 8607], 'peak_balance': 100000, 'end_balance': -4}], ['sample 3', {'principal': 250000, 'rate_bp': 900, 'months': 60, 'initial_payment': 8000, 'step_bp': 250, 'steps': 1}, {'year_payments': [8000, 8200, 2770, 2770, 2770], 'peak_balance': 250000, 'end_balance': -23}], ['sample 4', {'principal': 250000, 'rate_bp': 1200, 'months': 60, 'initial_payment': 20000, 'step_bp': 250, 'steps': 1}, {'year_payments': [20000, 20500, 0, 0, 0], 'peak_balance': 250000, 'end_balance': -326758}], ['sample 5', {'principal': 1000000, 'rate_bp': 600, 'months': 36, 'initial_payment': 3000, 'step_bp': 250, 'steps': 0}, {'year_payments': [3000, 45414, 45414], 'peak_balance': 1024671, 'end_balance': -2}], ['sample 6', {'principal': 100000, 'rate_bp': 600, 'months': 24, 'initial_payment': 500, 'step_bp': 1000, 'steps': 0}, {'year_payments': [500, 8607], 'peak_balance': 100000, 'end_balance': -4}]], [['regression: year boundary', {'principal': 250000, 'rate_bp': 1200, 'months': 24, 'initial_payment': 3000, 'step_bp': 250, 'steps': 2}, {'year_payments': [3000, 3075], 'peak_balance': 250000, 'end_balance': 235565}], ['sample 1', {'principal': 250000, 'rate_bp': 600, 'months': 24, 'initial_payment': 3000, 'step_bp': 1000, 'steps': 1}, {'year_payments': [3000, 3300], 'peak_balance': 250000, 'end_balance': 201795}], ['sample 2', {'principal': 100000, 'rate_bp': 900, 'months': 24, 'initial_payment': 500, 'step_bp': 1000, 'steps': 3}, {'year_payments': [500, 550], 'peak_balance': 105921, 'end_balance': 105921}], ['sample 3', {'principal': 1000000, 'rate_bp': 600, 'months': 60, 'initial_payment': 500, 'step_bp': 250, 'steps': 2}, {'year_payments': [500, 513, 526, 52144, 52144], 'peak_balance': 1176523, 'end_balance': 4}], ['sample 4', {'principal': 250000, 'rate_bp': 900, 'months': 60, 'initial_payment': 500, 'step_bp': 250, 'steps': 1}, {'year_payments': [500, 513, 9090, 9090, 9090], 'peak_balance': 285848, 'end_balance': -7}], ['sample 5', {'principal': 1000000, 'rate_bp': 900, 'months': 36, 'initial_payment': 3000, 'step_bp': 1000, 'steps': 3}, {'year_payments': [3000, 3300, 3630], 'peak_balance': 1173203, 'end_balance': 1173203}], ['sample 6', {'principal': 250000, 'rate_bp': 1200, 'months': 36, 'initial_payment': 8000, 'step_bp': 250, 'steps': 0}, {'year_payments': [8000, 8485, 8485], 'peak_balance': 250000, 'end_balance': -6}]], [['regression: year boundary', {'principal': 1000000, 'rate_bp': 900, 'months': 36, 'initial_payment': 3000, 'step_bp': 750, 'steps': 1}, {'year_payments': [3000, 3225, 97511], 'peak_balance': 1115035, 'end_balance': 6}], ['sample 1', {'principal': 250000, 'rate_bp': 900, 'months': 24, 'initial_payment': 3000, 'step_bp': 750, 'steps': 1}, {'year_payments': [3000, 3225], 'peak_balance': 250000, 'end_balance': 217726}], ['sample 2', {'principal': 1000000, 'rate_bp': 900, 'months': 36, 'initial_payment': 20000, 'step_bp': 1000, 'steps': 1}, {'year_payments': [20000, 22000, 56636], 'peak_balance': 1000000, 'end_balance': 0}], ['sample 3', {'principal': 100000, 'rate_bp': 900, 'months': 24, 'initial_payment': 3000, 'step_bp': 750, 'steps': 0}, {'year_payments': [3000, 6284], 'peak_balance': 100000, 'end_balance': -2}], ['sample 4', {'principal': 250000, 'rate_bp': 1200, 'months': 60, 'initial_payment': 20000, 'step_bp': 250, 'steps': 0}, {'year_payments': [20000, 739, 739, 739, 739], 'peak_balance': 250000, 'end_balance': -10}], ['sample 5', {'principal': 1000000, 'rate_bp': 1200, 'months': 60, 'initial_payment': 8000, 'step_bp': 250, 'steps': 2}, {'year_payments': [8000, 8200, 8405, 50753, 50753], 'peak_balance': 1078159, 'end_balance': -8}], ['sample 6', {'principal': 250000, 'rate_bp': 900, 'months': 24, 'initial_payment': 3000, 'step_bp': 750, 'steps': 2}, {'year_payments': [3000, 3225], 'peak_balance': 250000, 'end_balance': 217726}]], [['regression: year boundary', {'principal': 100000, 'rate_bp': 900, 'months': 24, 'initial_payment': 20000, 'step_bp': 750, 'steps': 3}, {'year_payments': [20000, 21500], 'peak_balance': 100000, 'end_balance': -422889}], ['sample 1', {'principal': 100000, 'rate_bp': 1200, 'months': 36, 'initial_payment': 3000, 'step_bp': 1000, 'steps': 2}, {'year_payments': [3000, 3300, 3630], 'peak_balance': 100000, 'end_balance': 1568}], ['sample 2', {'principal': 1000000, 'rate_bp': 900, 'months': 36, 'initial_payment': 8000, 'step_bp': 250, 'steps': 1}, {'year_payments': [8000, 8200, 86087], 'peak_balance': 1000000, 'end_balance': 5}], ['sample 3', {'principal': 1000000, 'rate_bp': 900, 'months': 60, 'initial_payment': 500, 'step_bp': 250, 'steps': 0}, {'year_payments': [500, 27064, 27064, 27064, 27064], 'peak_balance': 1087553, 'end_balance': -13}], ['sample 4', {'principal': 100000, 'rate_bp': 600, 'months': 36, 'initial_payment': 500, 'step_bp': 1000, 'steps': 3}, {'year_payments': [500, 550, 605], 'peak_balance': 100000, 'end_balance': 98050}], ['sample 5', {'principal': 250000, 'rate_bp': 900, 'months': 36, 'initial_payment': 20000, 'step_bp': 750, 'steps': 1}, {'year_payments': [20000, 21500, 0], 'peak_balance': 250000, 'end_balance': -266261}], ['sample 6', {'principal': 1000000, 'rate_bp': 600, 'months': 60, 'initial_payment': 8000, 'step_bp': 1000, 'steps': 0}, {'year_payments': [8000, 22616, 22616, 22616, 22616], 'peak_balance': 1000000, 'end_balance': 0}]], [['regression: year boundary', {'principal': 1000000, 'rate_bp': 600, 'months': 60, 'initial_payment': 20000, 'step_bp': 750, 'steps': 1}, {'year_payments': [20000, 21500, 18254, 18254, 18254], 'peak_balance': 1000000, 'end_balance': -11}], ['sample 1', {'principal': 100000, 'rate_bp': 600, 'months': 60, 'initial_payment': 500, 'step_bp': 1000, 'steps': 3}, {'year_payments': [500, 550, 605, 666, 8252], 'peak_balance': 100000, 'end_balance': 1}], ['sample 2', {'principal': 100000, 'rate_bp': 1200, 'months': 36, 'initial_payment': 500, 'step_bp': 250, 'steps': 2}, {'year_payments': [500, 513, 526], 'peak_balance': 121021, 'end_balance': 121021}], ['sample 3', {'principal': 1000000, 'rate_bp': 900, 'months': 60, 'initial_payment': 3000, 'step_bp': 1000, 'steps': 1}, {'year_payments': [3000, 3300, 35428, 35428, 35428], 'peak_balance': 1114096, 'end_balance': -5}], ['sample 4', {'principal': 250000, 'rate_bp': 1200, 'months': 24, 'initial_payment': 3000, 'step_bp': 250, 'steps': 1}, {'year_payments': [3000, 3075], 'peak_balance': 250000, 'end_balance': 235565}], ['sample 5', {'principal': 250000, 'rate_bp': 600, 'months': 60, 'initial_payment': 500, 'step_bp': 750, 'steps': 3}, {'year_payments': [500, 538, 578, 621, 24747], 'peak_balance': 287533, 'end_balance': 1}], ['sample 6', {'principal': 1000000, 'rate_bp': 600, 'months': 36, 'initial_payment': 3000, 'step_bp': 1000, 'steps': 1}, {'year_payments': [3000, 3300, 90126], 'peak_balance': 1047163, 'end_balance': -6}]]]
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 fixture | Actual | Expected | Outcome |
|---|---|---|---|
| regression: year boundary | {'end_balance': 204415, 'peak_balance': 250000, 'year_payments': [3000, 3075, 3152]} | {'end_balance': 204568, 'peak_balance': 250000, 'year_payments': [3000, 3075]} | Failed |
| sample 1 | {'end_balance': -3, 'peak_balance': 250000, 'year_payments': [8000, 8200, 7646, 7646]} | {'end_balance': 9, 'peak_balance': 250000, 'year_payments': [8000, 7921, 7921]} | Failed |
| sample 2 | {'end_balance': 0, 'peak_balance': 100000, 'year_payments': [500, 513, 100339]} | {'end_balance': -4, 'peak_balance': 100000, 'year_payments': [500, 8607]} | Failed |
| sample 3 | {'end_balance': -2298, 'peak_balance': 250000, 'year_payments': [8000, 8200, 8405, 0, 0, 0]} | {'end_balance': -23, 'peak_balance': 250000, 'year_payments': [8000, 8200, 2770, 2770, 2770]} | Failed |
| sample 4 | {'end_balance': -639994, 'peak_balance': 250000, 'year_payments': [20000, 20500, 21013, 0, 0, 0]} | {'end_balance': -326758, 'peak_balance': 250000, 'year_payments': [20000, 20500, 0, 0, 0]} | Failed |
| sample 5 | {'end_balance': -3, 'peak_balance': 1047696, 'year_payments': [3000, 3075, 83441, 83441]} | {'end_balance': -2, 'peak_balance': 1024671, 'year_payments': [3000, 45414, 45414]} | Failed |
| sample 6 | {'end_balance': 0, 'peak_balance': 100000, 'year_payments': [500, 550, 99880]} | {'end_balance': -4, 'peak_balance': 100000, 'year_payments': [500, 8607]} | Failed |
SHA-256 / 1dfdd3041e5d47833bed4955dfd920bb92d56e8ff3beb5d46ac8da2e6b19c95a
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):
def rnd(n, d):
q, r = divmod(n, d)
return q + (1 if 2 * r >= d else 0)
def level(P, n):
if n <= 0 or P <= 0:
return 0
if bp == 0:
exact = Fraction(P, n)
else:
r = Fraction(bp, 120000)
exact = P * r / (1 - (1 + r) ** -n)
return math.floor(exact + Fraction(1, 2))
bp = x['rate_bp']
n = x['months']
bal = x['principal']
pay = x['initial_payment']
years = [pay]
peak = bal
for m in range(1, n + 1):
t = (m - 1) // 12
if m > 12 and (m - 2) % 12 == 0:
if t <= x['steps']:
pay = rnd(pay * (10000 + x['step_bp']), 10000)
elif t == x['steps'] + 1:
pay = level(bal, n - m + 1)
years.append(pay)
interest = rnd(bal * bp, 120000)
bal += interest - pay
peak = max(peak, bal)
return {'year_payments': years, 'peak_balance': peak, 'end_balance': bal}
def check(label, actual, expected):
observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
fixtures = [[['regression: year boundary', {'principal': 250000, 'rate_bp': 600, 'months': 24, 'initial_payment': 3000, 'step_bp': 250, 'steps': 3}, {'year_payments': [3000, 3075], 'peak_balance': 250000, 'end_balance': 204568}], ['sample 1', {'principal': 250000, 'rate_bp': 900, 'months': 36, 'initial_payment': 8000, 'step_bp': 250, 'steps': 0}, {'year_payments': [8000, 7921, 7921], 'peak_balance': 250000, 'end_balance': 9}], ['sample 2', {'principal': 100000, 'rate_bp': 600, 'months': 24, 'initial_payment': 500, 'step_bp': 250, 'steps': 0}, {'year_payments': [500, 8607], 'peak_balance': 100000, 'end_balance': -4}], ['sample 3', {'principal': 250000, 'rate_bp': 900, 'months': 60, 'initial_payment': 8000, 'step_bp': 250, 'steps': 1}, {'year_payments': [8000, 8200, 2770, 2770, 2770], 'peak_balance': 250000, 'end_balance': -23}], ['sample 4', {'principal': 250000, 'rate_bp': 1200, 'months': 60, 'initial_payment': 20000, 'step_bp': 250, 'steps': 1}, {'year_payments': [20000, 20500, 0, 0, 0], 'peak_balance': 250000, 'end_balance': -326758}], ['sample 5', {'principal': 1000000, 'rate_bp': 600, 'months': 36, 'initial_payment': 3000, 'step_bp': 250, 'steps': 0}, {'year_payments': [3000, 45414, 45414], 'peak_balance': 1024671, 'end_balance': -2}], ['sample 6', {'principal': 100000, 'rate_bp': 600, 'months': 24, 'initial_payment': 500, 'step_bp': 1000, 'steps': 0}, {'year_payments': [500, 8607], 'peak_balance': 100000, 'end_balance': -4}]], [['regression: year boundary', {'principal': 250000, 'rate_bp': 1200, 'months': 24, 'initial_payment': 3000, 'step_bp': 250, 'steps': 2}, {'year_payments': [3000, 3075], 'peak_balance': 250000, 'end_balance': 235565}], ['sample 1', {'principal': 250000, 'rate_bp': 600, 'months': 24, 'initial_payment': 3000, 'step_bp': 1000, 'steps': 1}, {'year_payments': [3000, 3300], 'peak_balance': 250000, 'end_balance': 201795}], ['sample 2', {'principal': 100000, 'rate_bp': 900, 'months': 24, 'initial_payment': 500, 'step_bp': 1000, 'steps': 3}, {'year_payments': [500, 550], 'peak_balance': 105921, 'end_balance': 105921}], ['sample 3', {'principal': 1000000, 'rate_bp': 600, 'months': 60, 'initial_payment': 500, 'step_bp': 250, 'steps': 2}, {'year_payments': [500, 513, 526, 52144, 52144], 'peak_balance': 1176523, 'end_balance': 4}], ['sample 4', {'principal': 250000, 'rate_bp': 900, 'months': 60, 'initial_payment': 500, 'step_bp': 250, 'steps': 1}, {'year_payments': [500, 513, 9090, 9090, 9090], 'peak_balance': 285848, 'end_balance': -7}], ['sample 5', {'principal': 1000000, 'rate_bp': 900, 'months': 36, 'initial_payment': 3000, 'step_bp': 1000, 'steps': 3}, {'year_payments': [3000, 3300, 3630], 'peak_balance': 1173203, 'end_balance': 1173203}], ['sample 6', {'principal': 250000, 'rate_bp': 1200, 'months': 36, 'initial_payment': 8000, 'step_bp': 250, 'steps': 0}, {'year_payments': [8000, 8485, 8485], 'peak_balance': 250000, 'end_balance': -6}]], [['regression: year boundary', {'principal': 1000000, 'rate_bp': 900, 'months': 36, 'initial_payment': 3000, 'step_bp': 750, 'steps': 1}, {'year_payments': [3000, 3225, 97511], 'peak_balance': 1115035, 'end_balance': 6}], ['sample 1', {'principal': 250000, 'rate_bp': 900, 'months': 24, 'initial_payment': 3000, 'step_bp': 750, 'steps': 1}, {'year_payments': [3000, 3225], 'peak_balance': 250000, 'end_balance': 217726}], ['sample 2', {'principal': 1000000, 'rate_bp': 900, 'months': 36, 'initial_payment': 20000, 'step_bp': 1000, 'steps': 1}, {'year_payments': [20000, 22000, 56636], 'peak_balance': 1000000, 'end_balance': 0}], ['sample 3', {'principal': 100000, 'rate_bp': 900, 'months': 24, 'initial_payment': 3000, 'step_bp': 750, 'steps': 0}, {'year_payments': [3000, 6284], 'peak_balance': 100000, 'end_balance': -2}], ['sample 4', {'principal': 250000, 'rate_bp': 1200, 'months': 60, 'initial_payment': 20000, 'step_bp': 250, 'steps': 0}, {'year_payments': [20000, 739, 739, 739, 739], 'peak_balance': 250000, 'end_balance': -10}], ['sample 5', {'principal': 1000000, 'rate_bp': 1200, 'months': 60, 'initial_payment': 8000, 'step_bp': 250, 'steps': 2}, {'year_payments': [8000, 8200, 8405, 50753, 50753], 'peak_balance': 1078159, 'end_balance': -8}], ['sample 6', {'principal': 250000, 'rate_bp': 900, 'months': 24, 'initial_payment': 3000, 'step_bp': 750, 'steps': 2}, {'year_payments': [3000, 3225], 'peak_balance': 250000, 'end_balance': 217726}]], [['regression: year boundary', {'principal': 100000, 'rate_bp': 900, 'months': 24, 'initial_payment': 20000, 'step_bp': 750, 'steps': 3}, {'year_payments': [20000, 21500], 'peak_balance': 100000, 'end_balance': -422889}], ['sample 1', {'principal': 100000, 'rate_bp': 1200, 'months': 36, 'initial_payment': 3000, 'step_bp': 1000, 'steps': 2}, {'year_payments': [3000, 3300, 3630], 'peak_balance': 100000, 'end_balance': 1568}], ['sample 2', {'principal': 1000000, 'rate_bp': 900, 'months': 36, 'initial_payment': 8000, 'step_bp': 250, 'steps': 1}, {'year_payments': [8000, 8200, 86087], 'peak_balance': 1000000, 'end_balance': 5}], ['sample 3', {'principal': 1000000, 'rate_bp': 900, 'months': 60, 'initial_payment': 500, 'step_bp': 250, 'steps': 0}, {'year_payments': [500, 27064, 27064, 27064, 27064], 'peak_balance': 1087553, 'end_balance': -13}], ['sample 4', {'principal': 100000, 'rate_bp': 600, 'months': 36, 'initial_payment': 500, 'step_bp': 1000, 'steps': 3}, {'year_payments': [500, 550, 605], 'peak_balance': 100000, 'end_balance': 98050}], ['sample 5', {'principal': 250000, 'rate_bp': 900, 'months': 36, 'initial_payment': 20000, 'step_bp': 750, 'steps': 1}, {'year_payments': [20000, 21500, 0], 'peak_balance': 250000, 'end_balance': -266261}], ['sample 6', {'principal': 1000000, 'rate_bp': 600, 'months': 60, 'initial_payment': 8000, 'step_bp': 1000, 'steps': 0}, {'year_payments': [8000, 22616, 22616, 22616, 22616], 'peak_balance': 1000000, 'end_balance': 0}]], [['regression: year boundary', {'principal': 1000000, 'rate_bp': 600, 'months': 60, 'initial_payment': 20000, 'step_bp': 750, 'steps': 1}, {'year_payments': [20000, 21500, 18254, 18254, 18254], 'peak_balance': 1000000, 'end_balance': -11}], ['sample 1', {'principal': 100000, 'rate_bp': 600, 'months': 60, 'initial_payment': 500, 'step_bp': 1000, 'steps': 3}, {'year_payments': [500, 550, 605, 666, 8252], 'peak_balance': 100000, 'end_balance': 1}], ['sample 2', {'principal': 100000, 'rate_bp': 1200, 'months': 36, 'initial_payment': 500, 'step_bp': 250, 'steps': 2}, {'year_payments': [500, 513, 526], 'peak_balance': 121021, 'end_balance': 121021}], ['sample 3', {'principal': 1000000, 'rate_bp': 900, 'months': 60, 'initial_payment': 3000, 'step_bp': 1000, 'steps': 1}, {'year_payments': [3000, 3300, 35428, 35428, 35428], 'peak_balance': 1114096, 'end_balance': -5}], ['sample 4', {'principal': 250000, 'rate_bp': 1200, 'months': 24, 'initial_payment': 3000, 'step_bp': 250, 'steps': 1}, {'year_payments': [3000, 3075], 'peak_balance': 250000, 'end_balance': 235565}], ['sample 5', {'principal': 250000, 'rate_bp': 600, 'months': 60, 'initial_payment': 500, 'step_bp': 750, 'steps': 3}, {'year_payments': [500, 538, 578, 621, 24747], 'peak_balance': 287533, 'end_balance': 1}], ['sample 6', {'principal': 1000000, 'rate_bp': 600, 'months': 36, 'initial_payment': 3000, 'step_bp': 1000, 'steps': 1}, {'year_payments': [3000, 3300, 90126], 'peak_balance': 1047163, 'end_balance': -6}]]]
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 fixture | Actual | Expected | Outcome |
|---|---|---|---|
| regression: year boundary | {'end_balance': 204651, 'peak_balance': 250000, 'year_payments': [3000, 3075]} | {'end_balance': 204568, 'peak_balance': 250000, 'year_payments': [3000, 3075]} | Failed |
| sample 1 | {'end_balance': -14, 'peak_balance': 250000, 'year_payments': [8000, 7918, 7918]} | {'end_balance': 9, 'peak_balance': 250000, 'year_payments': [8000, 7921, 7921]} | Failed |
| sample 2 | {'end_balance': -1, 'peak_balance': 100000, 'year_payments': [500, 9366]} | {'end_balance': -4, 'peak_balance': 100000, 'year_payments': [500, 8607]} | Failed |
| sample 3 | {'end_balance': -11, 'peak_balance': 250000, 'year_payments': [8000, 8200, 2600, 2600, 2600]} | {'end_balance': -23, 'peak_balance': 250000, 'year_payments': [8000, 8200, 2770, 2770, 2770]} | Failed |
| sample 4 | {'end_balance': -354997, 'peak_balance': 250000, 'year_payments': [20000, 20500, 0, 0, 0]} | {'end_balance': -326758, 'peak_balance': 250000, 'year_payments': [20000, 20500, 0, 0, 0]} | Failed |
| sample 5 | {'end_balance': -3, 'peak_balance': 1026794, 'year_payments': [3000, 47371, 47371]} | {'end_balance': -2, 'peak_balance': 1024671, 'year_payments': [3000, 45414, 45414]} | Failed |
| sample 6 | {'end_balance': -1, 'peak_balance': 100000, 'year_payments': [500, 9366]} | {'end_balance': -4, 'peak_balance': 100000, 'year_payments': [500, 8607]} | Failed |
SHA-256 / 1c133265a4e1f7e2a17b83bffb63ce2174f12cbf615b4ff2e2e601c41407ca02
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):
def rnd(n, d):
q, r = divmod(n, d)
return q + (1 if 2 * r >= d else 0)
def level(P, n):
if n <= 0 or P <= 0:
return 0
if bp == 0:
exact = Fraction(P, n)
else:
r = Fraction(bp, 120000)
exact = P * r / (1 - (1 + r) ** -n)
return math.floor(exact + Fraction(1, 2))
bp = x['rate_bp']
n = x['months']
bal = x['principal']
pay = x['initial_payment']
years = [pay]
peak = bal
for m in range(1, n + 1):
t = (m - 1) // 12
if m > 1 and (m - 1) % 12 == 0:
if t <= x['steps']:
pay = rnd(pay * (10000 + x['step_bp']), 10000)
elif t == x['steps'] + 1:
pay = level(bal, n - m + 1)
years.append(pay)
interest = rnd(bal * bp, 120000)
bal += interest - pay
peak = max(peak, bal)
return {'year_payments': years, 'peak_balance': peak, 'end_balance': bal}
def check(label, actual, expected):
observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
fixtures = [[['regression: year boundary', {'principal': 250000, 'rate_bp': 600, 'months': 24, 'initial_payment': 3000, 'step_bp': 250, 'steps': 3}, {'year_payments': [3000, 3075], 'peak_balance': 250000, 'end_balance': 204568}], ['sample 1', {'principal': 250000, 'rate_bp': 900, 'months': 36, 'initial_payment': 8000, 'step_bp': 250, 'steps': 0}, {'year_payments': [8000, 7921, 7921], 'peak_balance': 250000, 'end_balance': 9}], ['sample 2', {'principal': 100000, 'rate_bp': 600, 'months': 24, 'initial_payment': 500, 'step_bp': 250, 'steps': 0}, {'year_payments': [500, 8607], 'peak_balance': 100000, 'end_balance': -4}], ['sample 3', {'principal': 250000, 'rate_bp': 900, 'months': 60, 'initial_payment': 8000, 'step_bp': 250, 'steps': 1}, {'year_payments': [8000, 8200, 2770, 2770, 2770], 'peak_balance': 250000, 'end_balance': -23}], ['sample 4', {'principal': 250000, 'rate_bp': 1200, 'months': 60, 'initial_payment': 20000, 'step_bp': 250, 'steps': 1}, {'year_payments': [20000, 20500, 0, 0, 0], 'peak_balance': 250000, 'end_balance': -326758}], ['sample 5', {'principal': 1000000, 'rate_bp': 600, 'months': 36, 'initial_payment': 3000, 'step_bp': 250, 'steps': 0}, {'year_payments': [3000, 45414, 45414], 'peak_balance': 1024671, 'end_balance': -2}], ['sample 6', {'principal': 100000, 'rate_bp': 600, 'months': 24, 'initial_payment': 500, 'step_bp': 1000, 'steps': 0}, {'year_payments': [500, 8607], 'peak_balance': 100000, 'end_balance': -4}]], [['regression: year boundary', {'principal': 250000, 'rate_bp': 1200, 'months': 24, 'initial_payment': 3000, 'step_bp': 250, 'steps': 2}, {'year_payments': [3000, 3075], 'peak_balance': 250000, 'end_balance': 235565}], ['sample 1', {'principal': 250000, 'rate_bp': 600, 'months': 24, 'initial_payment': 3000, 'step_bp': 1000, 'steps': 1}, {'year_payments': [3000, 3300], 'peak_balance': 250000, 'end_balance': 201795}], ['sample 2', {'principal': 100000, 'rate_bp': 900, 'months': 24, 'initial_payment': 500, 'step_bp': 1000, 'steps': 3}, {'year_payments': [500, 550], 'peak_balance': 105921, 'end_balance': 105921}], ['sample 3', {'principal': 1000000, 'rate_bp': 600, 'months': 60, 'initial_payment': 500, 'step_bp': 250, 'steps': 2}, {'year_payments': [500, 513, 526, 52144, 52144], 'peak_balance': 1176523, 'end_balance': 4}], ['sample 4', {'principal': 250000, 'rate_bp': 900, 'months': 60, 'initial_payment': 500, 'step_bp': 250, 'steps': 1}, {'year_payments': [500, 513, 9090, 9090, 9090], 'peak_balance': 285848, 'end_balance': -7}], ['sample 5', {'principal': 1000000, 'rate_bp': 900, 'months': 36, 'initial_payment': 3000, 'step_bp': 1000, 'steps': 3}, {'year_payments': [3000, 3300, 3630], 'peak_balance': 1173203, 'end_balance': 1173203}], ['sample 6', {'principal': 250000, 'rate_bp': 1200, 'months': 36, 'initial_payment': 8000, 'step_bp': 250, 'steps': 0}, {'year_payments': [8000, 8485, 8485], 'peak_balance': 250000, 'end_balance': -6}]], [['regression: year boundary', {'principal': 1000000, 'rate_bp': 900, 'months': 36, 'initial_payment': 3000, 'step_bp': 750, 'steps': 1}, {'year_payments': [3000, 3225, 97511], 'peak_balance': 1115035, 'end_balance': 6}], ['sample 1', {'principal': 250000, 'rate_bp': 900, 'months': 24, 'initial_payment': 3000, 'step_bp': 750, 'steps': 1}, {'year_payments': [3000, 3225], 'peak_balance': 250000, 'end_balance': 217726}], ['sample 2', {'principal': 1000000, 'rate_bp': 900, 'months': 36, 'initial_payment': 20000, 'step_bp': 1000, 'steps': 1}, {'year_payments': [20000, 22000, 56636], 'peak_balance': 1000000, 'end_balance': 0}], ['sample 3', {'principal': 100000, 'rate_bp': 900, 'months': 24, 'initial_payment': 3000, 'step_bp': 750, 'steps': 0}, {'year_payments': [3000, 6284], 'peak_balance': 100000, 'end_balance': -2}], ['sample 4', {'principal': 250000, 'rate_bp': 1200, 'months': 60, 'initial_payment': 20000, 'step_bp': 250, 'steps': 0}, {'year_payments': [20000, 739, 739, 739, 739], 'peak_balance': 250000, 'end_balance': -10}], ['sample 5', {'principal': 1000000, 'rate_bp': 1200, 'months': 60, 'initial_payment': 8000, 'step_bp': 250, 'steps': 2}, {'year_payments': [8000, 8200, 8405, 50753, 50753], 'peak_balance': 1078159, 'end_balance': -8}], ['sample 6', {'principal': 250000, 'rate_bp': 900, 'months': 24, 'initial_payment': 3000, 'step_bp': 750, 'steps': 2}, {'year_payments': [3000, 3225], 'peak_balance': 250000, 'end_balance': 217726}]], [['regression: year boundary', {'principal': 100000, 'rate_bp': 900, 'months': 24, 'initial_payment': 20000, 'step_bp': 750, 'steps': 3}, {'year_payments': [20000, 21500], 'peak_balance': 100000, 'end_balance': -422889}], ['sample 1', {'principal': 100000, 'rate_bp': 1200, 'months': 36, 'initial_payment': 3000, 'step_bp': 1000, 'steps': 2}, {'year_payments': [3000, 3300, 3630], 'peak_balance': 100000, 'end_balance': 1568}], ['sample 2', {'principal': 1000000, 'rate_bp': 900, 'months': 36, 'initial_payment': 8000, 'step_bp': 250, 'steps': 1}, {'year_payments': [8000, 8200, 86087], 'peak_balance': 1000000, 'end_balance': 5}], ['sample 3', {'principal': 1000000, 'rate_bp': 900, 'months': 60, 'initial_payment': 500, 'step_bp': 250, 'steps': 0}, {'year_payments': [500, 27064, 27064, 27064, 27064], 'peak_balance': 1087553, 'end_balance': -13}], ['sample 4', {'principal': 100000, 'rate_bp': 600, 'months': 36, 'initial_payment': 500, 'step_bp': 1000, 'steps': 3}, {'year_payments': [500, 550, 605], 'peak_balance': 100000, 'end_balance': 98050}], ['sample 5', {'principal': 250000, 'rate_bp': 900, 'months': 36, 'initial_payment': 20000, 'step_bp': 750, 'steps': 1}, {'year_payments': [20000, 21500, 0], 'peak_balance': 250000, 'end_balance': -266261}], ['sample 6', {'principal': 1000000, 'rate_bp': 600, 'months': 60, 'initial_payment': 8000, 'step_bp': 1000, 'steps': 0}, {'year_payments': [8000, 22616, 22616, 22616, 22616], 'peak_balance': 1000000, 'end_balance': 0}]], [['regression: year boundary', {'principal': 1000000, 'rate_bp': 600, 'months': 60, 'initial_payment': 20000, 'step_bp': 750, 'steps': 1}, {'year_payments': [20000, 21500, 18254, 18254, 18254], 'peak_balance': 1000000, 'end_balance': -11}], ['sample 1', {'principal': 100000, 'rate_bp': 600, 'months': 60, 'initial_payment': 500, 'step_bp': 1000, 'steps': 3}, {'year_payments': [500, 550, 605, 666, 8252], 'peak_balance': 100000, 'end_balance': 1}], ['sample 2', {'principal': 100000, 'rate_bp': 1200, 'months': 36, 'initial_payment': 500, 'step_bp': 250, 'steps': 2}, {'year_payments': [500, 513, 526], 'peak_balance': 121021, 'end_balance': 121021}], ['sample 3', {'principal': 1000000, 'rate_bp': 900, 'months': 60, 'initial_payment': 3000, 'step_bp': 1000, 'steps': 1}, {'year_payments': [3000, 3300, 35428, 35428, 35428], 'peak_balance': 1114096, 'end_balance': -5}], ['sample 4', {'principal': 250000, 'rate_bp': 1200, 'months': 24, 'initial_payment': 3000, 'step_bp': 250, 'steps': 1}, {'year_payments': [3000, 3075], 'peak_balance': 250000, 'end_balance': 235565}], ['sample 5', {'principal': 250000, 'rate_bp': 600, 'months': 60, 'initial_payment': 500, 'step_bp': 750, 'steps': 3}, {'year_payments': [500, 538, 578, 621, 24747], 'peak_balance': 287533, 'end_balance': 1}], ['sample 6', {'principal': 1000000, 'rate_bp': 600, 'months': 36, 'initial_payment': 3000, 'step_bp': 1000, 'steps': 1}, {'year_payments': [3000, 3300, 90126], 'peak_balance': 1047163, 'end_balance': -6}]]]
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 fixture | Actual | Expected | Outcome |
|---|---|---|---|
| regression: year boundary | {'end_balance': 204568, 'peak_balance': 250000, 'year_payments': [3000, 3075]} | {'end_balance': 204568, 'peak_balance': 250000, 'year_payments': [3000, 3075]} | Passed |
| sample 1 | {'end_balance': 9, 'peak_balance': 250000, 'year_payments': [8000, 7921, 7921]} | {'end_balance': 9, 'peak_balance': 250000, 'year_payments': [8000, 7921, 7921]} | Passed |
| sample 2 | {'end_balance': -4, 'peak_balance': 100000, 'year_payments': [500, 8607]} | {'end_balance': -4, 'peak_balance': 100000, 'year_payments': [500, 8607]} | Passed |
| sample 3 | {'end_balance': -23, 'peak_balance': 250000, 'year_payments': [8000, 8200, 2770, 2770, 2770]} | {'end_balance': -23, 'peak_balance': 250000, 'year_payments': [8000, 8200, 2770, 2770, 2770]} | Passed |
| sample 4 | {'end_balance': -326758, 'peak_balance': 250000, 'year_payments': [20000, 20500, 0, 0, 0]} | {'end_balance': -326758, 'peak_balance': 250000, 'year_payments': [20000, 20500, 0, 0, 0]} | Passed |
| sample 5 | {'end_balance': -2, 'peak_balance': 1024671, 'year_payments': [3000, 45414, 45414]} | {'end_balance': -2, 'peak_balance': 1024671, 'year_payments': [3000, 45414, 45414]} | Passed |
| sample 6 | {'end_balance': -4, 'peak_balance': 100000, 'year_payments': [500, 8607]} | {'end_balance': -4, 'peak_balance': 100000, 'year_payments': [500, 8607]} | Passed |
SHA-256 / ba1805a75cb2c8ec00300595888cec3fe8a5429a8a54ea5086d22c580f7df5f7
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:30.294234+00:00.
Case digest / 1c1307605f53a41facbb7aaeba849cc811e6fd683fb3155ceed446eb45a72a13