FA-58791 / Loan amortization schedules / Open access
Credit line average daily balance: event effective day · case 01
Draws accrue no interest on the day they are taken.
ROOT CAUSE
The day is accrued before applying that day transactions.
VERIFIED REPAIR
Apply same-day transactions before accruing the day.
Unsuccessful approach: Shifting the day range adds a phantom day zero and drops the last day transactions.
Case contract
x = {'rate_bp', 'basis', 'start_balance', 'cycle_days', 'events': [[day 1..cycle_days, signed amount]], 'min_bp', 'min_floor'}. Events take effect at the start of their day. Each day adds max(balance, 0) to the accumulator; interest = round_half_up(acc*bp/(10000*basis)) once per cycle. owed = end balance + interest. Minimum = 0 if owed <= 0 else min(owed, max(min_floor, interest + round_half_up(max(end balance, 0)*min_bp/10000))). Return {'interest', 'balance': owed, 'minimum', 'adb': acc // cycle_days}.
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
N = 1
observations = []
def solve(x):
def rnd(n, d):
q, r = divmod(n, d)
return q + (1 if 2 * r >= d else 0)
bal = x['start_balance']
ev = sorted(x['events'])
i = 0
acc = 0
for day in range(1, x['cycle_days'] + 1):
acc += max(bal, 0)
while i < len(ev) and ev[i][0] == day:
bal += ev[i][1]
i += 1
interest = rnd(acc * x['rate_bp'], 10000 * x['basis'])
owed = bal + interest
if owed <= 0:
minimum = 0
else:
minimum = min(owed, max(x['min_floor'], interest + rnd(max(bal, 0) * x['min_bp'], 10000)))
return {'interest': interest, 'balance': owed, 'minimum': minimum, 'adb': acc // x['cycle_days']}
def check(label, actual, expected):
observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
fixtures = [[['regression: event effective day', {'rate_bp': 600, 'basis': 365, 'start_balance': 1000000, 'cycle_days': 31, 'events': [[4, 5000], [24, -20000], [10, 10000]], 'min_bp': 100, 'min_floor': 2500}, {'interest': 5129, 'balance': 1000129, 'minimum': 15079, 'adb': 1006451}], ['control 1', {'rate_bp': 1200, 'basis': 365, 'start_balance': 0, 'cycle_days': 30, 'events': [[6, -80000], [30, -80000], [3, 10000]], 'min_bp': 100, 'min_floor': 2500}, {'interest': 10, 'balance': -149990, 'minimum': 0, 'adb': 1000}], ['control 2', {'rate_bp': 850, 'basis': 365, 'start_balance': 0, 'cycle_days': 28, 'events': [], 'min_bp': 150, 'min_floor': 2500}, {'interest': 0, 'balance': 0, 'minimum': 0, 'adb': 0}], ['control 3', {'rate_bp': 1200, 'basis': 365, 'start_balance': 0, 'cycle_days': 28, 'events': [[9, 10000], [16, 10000], [22, -80000], [9, 10000]], 'min_bp': 100, 'min_floor': 10000}, {'interest': 105, 'balance': -49895, 'minimum': 0, 'adb': 11428}], ['control 4', {'rate_bp': 1200, 'basis': 365, 'start_balance': 1000000, 'cycle_days': 31, 'events': [], 'min_bp': 150, 'min_floor': 10000}, {'interest': 10192, 'balance': 1010192, 'minimum': 25192, 'adb': 1000000}], ['control 5', {'rate_bp': 600, 'basis': 365, 'start_balance': 0, 'cycle_days': 30, 'events': [[23, -20000], [29, 10000]], 'min_bp': 150, 'min_floor': 2500}, {'interest': 0, 'balance': -10000, 'minimum': 0, 'adb': 0}], ['control 6', {'rate_bp': 600, 'basis': 365, 'start_balance': 30000, 'cycle_days': 28, 'events': [], 'min_bp': 150, 'min_floor': 10000}, {'interest': 138, 'balance': 30138, 'minimum': 10000, 'adb': 30000}]], [['regression: event effective day', {'rate_bp': 1200, 'basis': 365, 'start_balance': 100000, 'cycle_days': 28, 'events': [[1, 50000], [6, 5000], [24, 10000], [18, -80000]], 'min_bp': 150, 'min_floor': 2500}, {'interest': 1146, 'balance': 86146, 'minimum': 2500, 'adb': 124464}], ['control 1', {'rate_bp': 850, 'basis': 365, 'start_balance': 0, 'cycle_days': 30, 'events': [[25, 10000], [30, -20000]], 'min_bp': 150, 'min_floor': 10000}, {'interest': 12, 'balance': -9988, 'minimum': 0, 'adb': 1666}], ['control 2', {'rate_bp': 850, 'basis': 365, 'start_balance': 100000, 'cycle_days': 31, 'events': [], 'min_bp': 150, 'min_floor': 10000}, {'interest': 722, 'balance': 100722, 'minimum': 10000, 'adb': 100000}], ['control 3', {'rate_bp': 850, 'basis': 365, 'start_balance': 0, 'cycle_days': 31, 'events': [[9, -80000], [20, -20000], [4, 5000]], 'min_bp': 150, 'min_floor': 2500}, {'interest': 6, 'balance': -94994, 'minimum': 0, 'adb': 806}], ['control 4', {'rate_bp': 600, 'basis': 365, 'start_balance': 0, 'cycle_days': 28, 'events': [[23, 5000], [9, -20000]], 'min_bp': 150, 'min_floor': 10000}, {'interest': 0, 'balance': -15000, 'minimum': 0, 'adb': 0}], ['control 5', {'rate_bp': 600, 'basis': 365, 'start_balance': 0, 'cycle_days': 28, 'events': [[19, 5000], [4, 10000], [12, -80000], [5, -20000]], 'min_bp': 150, 'min_floor': 2500}, {'interest': 2, 'balance': -84998, 'minimum': 0, 'adb': 357}], ['control 6', {'rate_bp': 1200, 'basis': 365, 'start_balance': 0, 'cycle_days': 28, 'events': [], 'min_bp': 150, 'min_floor': 10000}, {'interest': 0, 'balance': 0, 'minimum': 0, 'adb': 0}]], [['regression: event effective day', {'rate_bp': 600, 'basis': 365, 'start_balance': 1000000, 'cycle_days': 28, 'events': [[8, 5000]], 'min_bp': 100, 'min_floor': 2500}, {'interest': 4620, 'balance': 1009620, 'minimum': 14670, 'adb': 1003750}], ['control 1', {'rate_bp': 1200, 'basis': 365, 'start_balance': 0, 'cycle_days': 28, 'events': [[3, 5000], [20, 5000], [25, -20000]], 'min_bp': 150, 'min_floor': 10000}, {'interest': 44, 'balance': -9956, 'minimum': 0, 'adb': 4821}], ['control 2', {'rate_bp': 850, 'basis': 365, 'start_balance': 0, 'cycle_days': 28, 'events': [[5, -80000]], 'min_bp': 100, 'min_floor': 2500}, {'interest': 0, 'balance': -80000, 'minimum': 0, 'adb': 0}], ['control 3', {'rate_bp': 600, 'basis': 365, 'start_balance': 0, 'cycle_days': 28, 'events': [], 'min_bp': 100, 'min_floor': 2500}, {'interest': 0, 'balance': 0, 'minimum': 0, 'adb': 0}], ['control 4', {'rate_bp': 850, 'basis': 365, 'start_balance': 0, 'cycle_days': 30, 'events': [], 'min_bp': 150, 'min_floor': 10000}, {'interest': 0, 'balance': 0, 'minimum': 0, 'adb': 0}], ['control 5', {'rate_bp': 1200, 'basis': 365, 'start_balance': 100000, 'cycle_days': 28, 'events': [], 'min_bp': 150, 'min_floor': 10000}, {'interest': 921, 'balance': 100921, 'minimum': 10000, 'adb': 100000}], ['control 6', {'rate_bp': 1200, 'basis': 365, 'start_balance': 0, 'cycle_days': 31, 'events': [[20, 5000], [23, -80000]], 'min_bp': 150, 'min_floor': 2500}, {'interest': 5, 'balance': -74995, 'minimum': 0, 'adb': 483}]], [['regression: event effective day', {'rate_bp': 850, 'basis': 365, 'start_balance': 100000, 'cycle_days': 30, 'events': [[20, 50000], [29, -80000], [30, -80000]], 'min_bp': 100, 'min_floor': 10000}, {'interest': 773, 'balance': -9227, 'minimum': 0, 'adb': 110666}], ['control 1', {'rate_bp': 600, 'basis': 365, 'start_balance': 30000, 'cycle_days': 28, 'events': [], 'min_bp': 150, 'min_floor': 2500}, {'interest': 138, 'balance': 30138, 'minimum': 2500, 'adb': 30000}], ['control 2', {'rate_bp': 1200, 'basis': 365, 'start_balance': 1000000, 'cycle_days': 31, 'events': [], 'min_bp': 100, 'min_floor': 10000}, {'interest': 10192, 'balance': 1010192, 'minimum': 20192, 'adb': 1000000}], ['control 3', {'rate_bp': 1200, 'basis': 365, 'start_balance': 30000, 'cycle_days': 28, 'events': [], 'min_bp': 100, 'min_floor': 10000}, {'interest': 276, 'balance': 30276, 'minimum': 10000, 'adb': 30000}], ['control 4', {'rate_bp': 850, 'basis': 365, 'start_balance': 0, 'cycle_days': 31, 'events': [[30, 10000], [22, -20000], [23, 5000]], 'min_bp': 100, 'min_floor': 10000}, {'interest': 0, 'balance': -5000, 'minimum': 0, 'adb': 0}], ['control 5', {'rate_bp': 1200, 'basis': 365, 'start_balance': 30000, 'cycle_days': 30, 'events': [], 'min_bp': 100, 'min_floor': 10000}, {'interest': 296, 'balance': 30296, 'minimum': 10000, 'adb': 30000}], ['control 6', {'rate_bp': 1200, 'basis': 365, 'start_balance': 0, 'cycle_days': 28, 'events': [[2, -20000], [14, 10000], [20, -20000], [27, 10000]], 'min_bp': 150, 'min_floor': 10000}, {'interest': 0, 'balance': -20000, 'minimum': 0, 'adb': 0}]], [['regression: event effective day', {'rate_bp': 600, 'basis': 365, 'start_balance': 1000000, 'cycle_days': 28, 'events': [[21, 10000], [25, -80000]], 'min_bp': 100, 'min_floor': 2500}, {'interest': 4563, 'balance': 934563, 'minimum': 13863, 'adb': 991428}], ['control 1', {'rate_bp': 600, 'basis': 365, 'start_balance': 0, 'cycle_days': 31, 'events': [[15, 50000], [8, -80000], [21, 5000], [31, -20000]], 'min_bp': 150, 'min_floor': 10000}, {'interest': 0, 'balance': -45000, 'minimum': 0, 'adb': 0}], ['control 2', {'rate_bp': 1200, 'basis': 365, 'start_balance': 0, 'cycle_days': 28, 'events': [[5, -80000], [22, 5000]], 'min_bp': 100, 'min_floor': 10000}, {'interest': 0, 'balance': -75000, 'minimum': 0, 'adb': 0}], ['control 3', {'rate_bp': 600, 'basis': 365, 'start_balance': 0, 'cycle_days': 31, 'events': [[8, 5000], [3, 5000], [27, 10000], [28, -80000]], 'min_bp': 100, 'min_floor': 2500}, {'interest': 39, 'balance': -59961, 'minimum': 0, 'adb': 7580}], ['control 4', {'rate_bp': 600, 'basis': 365, 'start_balance': 30000, 'cycle_days': 31, 'events': [[13, 50000], [16, -20000], [7, -80000], [18, 50000]], 'min_bp': 100, 'min_floor': 2500}, {'interest': 99, 'balance': 30099, 'minimum': 2500, 'adb': 19354}], ['control 5', {'rate_bp': 1200, 'basis': 365, 'start_balance': 0, 'cycle_days': 28, 'events': [[4, -20000], [25, 5000]], 'min_bp': 100, 'min_floor': 2500}, {'interest': 0, 'balance': -15000, 'minimum': 0, 'adb': 0}], ['control 6', {'rate_bp': 600, 'basis': 365, 'start_balance': 1000000, 'cycle_days': 31, 'events': [], 'min_bp': 100, 'min_floor': 10000}, {'interest': 5096, 'balance': 1005096, 'minimum': 15096, 'adb': 1000000}]]]
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: event effective day | {'adb': 1006612, 'balance': 1000130, 'interest': 5130, 'minimum': 15080} | {'adb': 1006451, 'balance': 1000129, 'interest': 5129, 'minimum': 15079} | Failed |
| control 1 | {'adb': 1000, 'balance': -149990, 'interest': 10, 'minimum': 0} | {'adb': 1000, 'balance': -149990, 'interest': 10, 'minimum': 0} | Passed |
| control 2 | {'adb': 0, 'balance': 0, 'interest': 0, 'minimum': 0} | {'adb': 0, 'balance': 0, 'interest': 0, 'minimum': 0} | Passed |
| control 3 | {'adb': 11428, 'balance': -49895, 'interest': 105, 'minimum': 0} | {'adb': 11428, 'balance': -49895, 'interest': 105, 'minimum': 0} | Passed |
| control 4 | {'adb': 1000000, 'balance': 1010192, 'interest': 10192, 'minimum': 25192} | {'adb': 1000000, 'balance': 1010192, 'interest': 10192, 'minimum': 25192} | Passed |
| control 5 | {'adb': 0, 'balance': -10000, 'interest': 0, 'minimum': 0} | {'adb': 0, 'balance': -10000, 'interest': 0, 'minimum': 0} | Passed |
| control 6 | {'adb': 30000, 'balance': 30138, 'interest': 138, 'minimum': 10000} | {'adb': 30000, 'balance': 30138, 'interest': 138, 'minimum': 10000} | Passed |
SHA-256 / 63d7ebf39598605f8197a7539adf77a4b55f3c898dfaa804bcc8ae5aebb8e215
2 / The unsuccessful fix
Exit 1"""Failure Map reference implementation. Python standard library only."""
import json
N = 1
observations = []
def solve(x):
def rnd(n, d):
q, r = divmod(n, d)
return q + (1 if 2 * r >= d else 0)
bal = x['start_balance']
ev = sorted(x['events'])
i = 0
acc = 0
for day in range(0, x['cycle_days']):
acc += max(bal, 0)
while i < len(ev) and ev[i][0] == day:
bal += ev[i][1]
i += 1
interest = rnd(acc * x['rate_bp'], 10000 * x['basis'])
owed = bal + interest
if owed <= 0:
minimum = 0
else:
minimum = min(owed, max(x['min_floor'], interest + rnd(max(bal, 0) * x['min_bp'], 10000)))
return {'interest': interest, 'balance': owed, 'minimum': minimum, 'adb': acc // x['cycle_days']}
def check(label, actual, expected):
observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
fixtures = [[['regression: event effective day', {'rate_bp': 600, 'basis': 365, 'start_balance': 1000000, 'cycle_days': 31, 'events': [[4, 5000], [24, -20000], [10, 10000]], 'min_bp': 100, 'min_floor': 2500}, {'interest': 5129, 'balance': 1000129, 'minimum': 15079, 'adb': 1006451}], ['control 1', {'rate_bp': 1200, 'basis': 365, 'start_balance': 0, 'cycle_days': 30, 'events': [[6, -80000], [30, -80000], [3, 10000]], 'min_bp': 100, 'min_floor': 2500}, {'interest': 10, 'balance': -149990, 'minimum': 0, 'adb': 1000}], ['control 2', {'rate_bp': 850, 'basis': 365, 'start_balance': 0, 'cycle_days': 28, 'events': [], 'min_bp': 150, 'min_floor': 2500}, {'interest': 0, 'balance': 0, 'minimum': 0, 'adb': 0}], ['control 3', {'rate_bp': 1200, 'basis': 365, 'start_balance': 0, 'cycle_days': 28, 'events': [[9, 10000], [16, 10000], [22, -80000], [9, 10000]], 'min_bp': 100, 'min_floor': 10000}, {'interest': 105, 'balance': -49895, 'minimum': 0, 'adb': 11428}], ['control 4', {'rate_bp': 1200, 'basis': 365, 'start_balance': 1000000, 'cycle_days': 31, 'events': [], 'min_bp': 150, 'min_floor': 10000}, {'interest': 10192, 'balance': 1010192, 'minimum': 25192, 'adb': 1000000}], ['control 5', {'rate_bp': 600, 'basis': 365, 'start_balance': 0, 'cycle_days': 30, 'events': [[23, -20000], [29, 10000]], 'min_bp': 150, 'min_floor': 2500}, {'interest': 0, 'balance': -10000, 'minimum': 0, 'adb': 0}], ['control 6', {'rate_bp': 600, 'basis': 365, 'start_balance': 30000, 'cycle_days': 28, 'events': [], 'min_bp': 150, 'min_floor': 10000}, {'interest': 138, 'balance': 30138, 'minimum': 10000, 'adb': 30000}]], [['regression: event effective day', {'rate_bp': 1200, 'basis': 365, 'start_balance': 100000, 'cycle_days': 28, 'events': [[1, 50000], [6, 5000], [24, 10000], [18, -80000]], 'min_bp': 150, 'min_floor': 2500}, {'interest': 1146, 'balance': 86146, 'minimum': 2500, 'adb': 124464}], ['control 1', {'rate_bp': 850, 'basis': 365, 'start_balance': 0, 'cycle_days': 30, 'events': [[25, 10000], [30, -20000]], 'min_bp': 150, 'min_floor': 10000}, {'interest': 12, 'balance': -9988, 'minimum': 0, 'adb': 1666}], ['control 2', {'rate_bp': 850, 'basis': 365, 'start_balance': 100000, 'cycle_days': 31, 'events': [], 'min_bp': 150, 'min_floor': 10000}, {'interest': 722, 'balance': 100722, 'minimum': 10000, 'adb': 100000}], ['control 3', {'rate_bp': 850, 'basis': 365, 'start_balance': 0, 'cycle_days': 31, 'events': [[9, -80000], [20, -20000], [4, 5000]], 'min_bp': 150, 'min_floor': 2500}, {'interest': 6, 'balance': -94994, 'minimum': 0, 'adb': 806}], ['control 4', {'rate_bp': 600, 'basis': 365, 'start_balance': 0, 'cycle_days': 28, 'events': [[23, 5000], [9, -20000]], 'min_bp': 150, 'min_floor': 10000}, {'interest': 0, 'balance': -15000, 'minimum': 0, 'adb': 0}], ['control 5', {'rate_bp': 600, 'basis': 365, 'start_balance': 0, 'cycle_days': 28, 'events': [[19, 5000], [4, 10000], [12, -80000], [5, -20000]], 'min_bp': 150, 'min_floor': 2500}, {'interest': 2, 'balance': -84998, 'minimum': 0, 'adb': 357}], ['control 6', {'rate_bp': 1200, 'basis': 365, 'start_balance': 0, 'cycle_days': 28, 'events': [], 'min_bp': 150, 'min_floor': 10000}, {'interest': 0, 'balance': 0, 'minimum': 0, 'adb': 0}]], [['regression: event effective day', {'rate_bp': 600, 'basis': 365, 'start_balance': 1000000, 'cycle_days': 28, 'events': [[8, 5000]], 'min_bp': 100, 'min_floor': 2500}, {'interest': 4620, 'balance': 1009620, 'minimum': 14670, 'adb': 1003750}], ['control 1', {'rate_bp': 1200, 'basis': 365, 'start_balance': 0, 'cycle_days': 28, 'events': [[3, 5000], [20, 5000], [25, -20000]], 'min_bp': 150, 'min_floor': 10000}, {'interest': 44, 'balance': -9956, 'minimum': 0, 'adb': 4821}], ['control 2', {'rate_bp': 850, 'basis': 365, 'start_balance': 0, 'cycle_days': 28, 'events': [[5, -80000]], 'min_bp': 100, 'min_floor': 2500}, {'interest': 0, 'balance': -80000, 'minimum': 0, 'adb': 0}], ['control 3', {'rate_bp': 600, 'basis': 365, 'start_balance': 0, 'cycle_days': 28, 'events': [], 'min_bp': 100, 'min_floor': 2500}, {'interest': 0, 'balance': 0, 'minimum': 0, 'adb': 0}], ['control 4', {'rate_bp': 850, 'basis': 365, 'start_balance': 0, 'cycle_days': 30, 'events': [], 'min_bp': 150, 'min_floor': 10000}, {'interest': 0, 'balance': 0, 'minimum': 0, 'adb': 0}], ['control 5', {'rate_bp': 1200, 'basis': 365, 'start_balance': 100000, 'cycle_days': 28, 'events': [], 'min_bp': 150, 'min_floor': 10000}, {'interest': 921, 'balance': 100921, 'minimum': 10000, 'adb': 100000}], ['control 6', {'rate_bp': 1200, 'basis': 365, 'start_balance': 0, 'cycle_days': 31, 'events': [[20, 5000], [23, -80000]], 'min_bp': 150, 'min_floor': 2500}, {'interest': 5, 'balance': -74995, 'minimum': 0, 'adb': 483}]], [['regression: event effective day', {'rate_bp': 850, 'basis': 365, 'start_balance': 100000, 'cycle_days': 30, 'events': [[20, 50000], [29, -80000], [30, -80000]], 'min_bp': 100, 'min_floor': 10000}, {'interest': 773, 'balance': -9227, 'minimum': 0, 'adb': 110666}], ['control 1', {'rate_bp': 600, 'basis': 365, 'start_balance': 30000, 'cycle_days': 28, 'events': [], 'min_bp': 150, 'min_floor': 2500}, {'interest': 138, 'balance': 30138, 'minimum': 2500, 'adb': 30000}], ['control 2', {'rate_bp': 1200, 'basis': 365, 'start_balance': 1000000, 'cycle_days': 31, 'events': [], 'min_bp': 100, 'min_floor': 10000}, {'interest': 10192, 'balance': 1010192, 'minimum': 20192, 'adb': 1000000}], ['control 3', {'rate_bp': 1200, 'basis': 365, 'start_balance': 30000, 'cycle_days': 28, 'events': [], 'min_bp': 100, 'min_floor': 10000}, {'interest': 276, 'balance': 30276, 'minimum': 10000, 'adb': 30000}], ['control 4', {'rate_bp': 850, 'basis': 365, 'start_balance': 0, 'cycle_days': 31, 'events': [[30, 10000], [22, -20000], [23, 5000]], 'min_bp': 100, 'min_floor': 10000}, {'interest': 0, 'balance': -5000, 'minimum': 0, 'adb': 0}], ['control 5', {'rate_bp': 1200, 'basis': 365, 'start_balance': 30000, 'cycle_days': 30, 'events': [], 'min_bp': 100, 'min_floor': 10000}, {'interest': 296, 'balance': 30296, 'minimum': 10000, 'adb': 30000}], ['control 6', {'rate_bp': 1200, 'basis': 365, 'start_balance': 0, 'cycle_days': 28, 'events': [[2, -20000], [14, 10000], [20, -20000], [27, 10000]], 'min_bp': 150, 'min_floor': 10000}, {'interest': 0, 'balance': -20000, 'minimum': 0, 'adb': 0}]], [['regression: event effective day', {'rate_bp': 600, 'basis': 365, 'start_balance': 1000000, 'cycle_days': 28, 'events': [[21, 10000], [25, -80000]], 'min_bp': 100, 'min_floor': 2500}, {'interest': 4563, 'balance': 934563, 'minimum': 13863, 'adb': 991428}], ['control 1', {'rate_bp': 600, 'basis': 365, 'start_balance': 0, 'cycle_days': 31, 'events': [[15, 50000], [8, -80000], [21, 5000], [31, -20000]], 'min_bp': 150, 'min_floor': 10000}, {'interest': 0, 'balance': -45000, 'minimum': 0, 'adb': 0}], ['control 2', {'rate_bp': 1200, 'basis': 365, 'start_balance': 0, 'cycle_days': 28, 'events': [[5, -80000], [22, 5000]], 'min_bp': 100, 'min_floor': 10000}, {'interest': 0, 'balance': -75000, 'minimum': 0, 'adb': 0}], ['control 3', {'rate_bp': 600, 'basis': 365, 'start_balance': 0, 'cycle_days': 31, 'events': [[8, 5000], [3, 5000], [27, 10000], [28, -80000]], 'min_bp': 100, 'min_floor': 2500}, {'interest': 39, 'balance': -59961, 'minimum': 0, 'adb': 7580}], ['control 4', {'rate_bp': 600, 'basis': 365, 'start_balance': 30000, 'cycle_days': 31, 'events': [[13, 50000], [16, -20000], [7, -80000], [18, 50000]], 'min_bp': 100, 'min_floor': 2500}, {'interest': 99, 'balance': 30099, 'minimum': 2500, 'adb': 19354}], ['control 5', {'rate_bp': 1200, 'basis': 365, 'start_balance': 0, 'cycle_days': 28, 'events': [[4, -20000], [25, 5000]], 'min_bp': 100, 'min_floor': 2500}, {'interest': 0, 'balance': -15000, 'minimum': 0, 'adb': 0}], ['control 6', {'rate_bp': 600, 'basis': 365, 'start_balance': 1000000, 'cycle_days': 31, 'events': [], 'min_bp': 100, 'min_floor': 10000}, {'interest': 5096, 'balance': 1005096, 'minimum': 15096, 'adb': 1000000}]]]
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: event effective day | {'adb': 1006774, 'balance': 1000130, 'interest': 5130, 'minimum': 15080} | {'adb': 1006451, 'balance': 1000129, 'interest': 5129, 'minimum': 15079} | Failed |
| control 1 | {'adb': 1000, 'balance': -69990, 'interest': 10, 'minimum': 0} | {'adb': 1000, 'balance': -149990, 'interest': 10, 'minimum': 0} | Failed |
| control 2 | {'adb': 0, 'balance': 0, 'interest': 0, 'minimum': 0} | {'adb': 0, 'balance': 0, 'interest': 0, 'minimum': 0} | Passed |
| control 3 | {'adb': 11428, 'balance': -49895, 'interest': 105, 'minimum': 0} | {'adb': 11428, 'balance': -49895, 'interest': 105, 'minimum': 0} | Passed |
| control 4 | {'adb': 1000000, 'balance': 1010192, 'interest': 10192, 'minimum': 25192} | {'adb': 1000000, 'balance': 1010192, 'interest': 10192, 'minimum': 25192} | Passed |
| control 5 | {'adb': 0, 'balance': -10000, 'interest': 0, 'minimum': 0} | {'adb': 0, 'balance': -10000, 'interest': 0, 'minimum': 0} | Passed |
| control 6 | {'adb': 30000, 'balance': 30138, 'interest': 138, 'minimum': 10000} | {'adb': 30000, 'balance': 30138, 'interest': 138, 'minimum': 10000} | Passed |
SHA-256 / ddd4c16c24c4aa274e8df73355ff6a7ae1944eea431b165a3374f45db5f53a22
3 / The verified repair
Exit 0"""Failure Map reference implementation. Python standard library only."""
import json
N = 1
observations = []
def solve(x):
def rnd(n, d):
q, r = divmod(n, d)
return q + (1 if 2 * r >= d else 0)
bal = x['start_balance']
ev = sorted(x['events'])
i = 0
acc = 0
for day in range(1, x['cycle_days'] + 1):
while i < len(ev) and ev[i][0] == day:
bal += ev[i][1]
i += 1
acc += max(bal, 0)
interest = rnd(acc * x['rate_bp'], 10000 * x['basis'])
owed = bal + interest
if owed <= 0:
minimum = 0
else:
minimum = min(owed, max(x['min_floor'], interest + rnd(max(bal, 0) * x['min_bp'], 10000)))
return {'interest': interest, 'balance': owed, 'minimum': minimum, 'adb': acc // x['cycle_days']}
def check(label, actual, expected):
observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
fixtures = [[['regression: event effective day', {'rate_bp': 600, 'basis': 365, 'start_balance': 1000000, 'cycle_days': 31, 'events': [[4, 5000], [24, -20000], [10, 10000]], 'min_bp': 100, 'min_floor': 2500}, {'interest': 5129, 'balance': 1000129, 'minimum': 15079, 'adb': 1006451}], ['control 1', {'rate_bp': 1200, 'basis': 365, 'start_balance': 0, 'cycle_days': 30, 'events': [[6, -80000], [30, -80000], [3, 10000]], 'min_bp': 100, 'min_floor': 2500}, {'interest': 10, 'balance': -149990, 'minimum': 0, 'adb': 1000}], ['control 2', {'rate_bp': 850, 'basis': 365, 'start_balance': 0, 'cycle_days': 28, 'events': [], 'min_bp': 150, 'min_floor': 2500}, {'interest': 0, 'balance': 0, 'minimum': 0, 'adb': 0}], ['control 3', {'rate_bp': 1200, 'basis': 365, 'start_balance': 0, 'cycle_days': 28, 'events': [[9, 10000], [16, 10000], [22, -80000], [9, 10000]], 'min_bp': 100, 'min_floor': 10000}, {'interest': 105, 'balance': -49895, 'minimum': 0, 'adb': 11428}], ['control 4', {'rate_bp': 1200, 'basis': 365, 'start_balance': 1000000, 'cycle_days': 31, 'events': [], 'min_bp': 150, 'min_floor': 10000}, {'interest': 10192, 'balance': 1010192, 'minimum': 25192, 'adb': 1000000}], ['control 5', {'rate_bp': 600, 'basis': 365, 'start_balance': 0, 'cycle_days': 30, 'events': [[23, -20000], [29, 10000]], 'min_bp': 150, 'min_floor': 2500}, {'interest': 0, 'balance': -10000, 'minimum': 0, 'adb': 0}], ['control 6', {'rate_bp': 600, 'basis': 365, 'start_balance': 30000, 'cycle_days': 28, 'events': [], 'min_bp': 150, 'min_floor': 10000}, {'interest': 138, 'balance': 30138, 'minimum': 10000, 'adb': 30000}]], [['regression: event effective day', {'rate_bp': 1200, 'basis': 365, 'start_balance': 100000, 'cycle_days': 28, 'events': [[1, 50000], [6, 5000], [24, 10000], [18, -80000]], 'min_bp': 150, 'min_floor': 2500}, {'interest': 1146, 'balance': 86146, 'minimum': 2500, 'adb': 124464}], ['control 1', {'rate_bp': 850, 'basis': 365, 'start_balance': 0, 'cycle_days': 30, 'events': [[25, 10000], [30, -20000]], 'min_bp': 150, 'min_floor': 10000}, {'interest': 12, 'balance': -9988, 'minimum': 0, 'adb': 1666}], ['control 2', {'rate_bp': 850, 'basis': 365, 'start_balance': 100000, 'cycle_days': 31, 'events': [], 'min_bp': 150, 'min_floor': 10000}, {'interest': 722, 'balance': 100722, 'minimum': 10000, 'adb': 100000}], ['control 3', {'rate_bp': 850, 'basis': 365, 'start_balance': 0, 'cycle_days': 31, 'events': [[9, -80000], [20, -20000], [4, 5000]], 'min_bp': 150, 'min_floor': 2500}, {'interest': 6, 'balance': -94994, 'minimum': 0, 'adb': 806}], ['control 4', {'rate_bp': 600, 'basis': 365, 'start_balance': 0, 'cycle_days': 28, 'events': [[23, 5000], [9, -20000]], 'min_bp': 150, 'min_floor': 10000}, {'interest': 0, 'balance': -15000, 'minimum': 0, 'adb': 0}], ['control 5', {'rate_bp': 600, 'basis': 365, 'start_balance': 0, 'cycle_days': 28, 'events': [[19, 5000], [4, 10000], [12, -80000], [5, -20000]], 'min_bp': 150, 'min_floor': 2500}, {'interest': 2, 'balance': -84998, 'minimum': 0, 'adb': 357}], ['control 6', {'rate_bp': 1200, 'basis': 365, 'start_balance': 0, 'cycle_days': 28, 'events': [], 'min_bp': 150, 'min_floor': 10000}, {'interest': 0, 'balance': 0, 'minimum': 0, 'adb': 0}]], [['regression: event effective day', {'rate_bp': 600, 'basis': 365, 'start_balance': 1000000, 'cycle_days': 28, 'events': [[8, 5000]], 'min_bp': 100, 'min_floor': 2500}, {'interest': 4620, 'balance': 1009620, 'minimum': 14670, 'adb': 1003750}], ['control 1', {'rate_bp': 1200, 'basis': 365, 'start_balance': 0, 'cycle_days': 28, 'events': [[3, 5000], [20, 5000], [25, -20000]], 'min_bp': 150, 'min_floor': 10000}, {'interest': 44, 'balance': -9956, 'minimum': 0, 'adb': 4821}], ['control 2', {'rate_bp': 850, 'basis': 365, 'start_balance': 0, 'cycle_days': 28, 'events': [[5, -80000]], 'min_bp': 100, 'min_floor': 2500}, {'interest': 0, 'balance': -80000, 'minimum': 0, 'adb': 0}], ['control 3', {'rate_bp': 600, 'basis': 365, 'start_balance': 0, 'cycle_days': 28, 'events': [], 'min_bp': 100, 'min_floor': 2500}, {'interest': 0, 'balance': 0, 'minimum': 0, 'adb': 0}], ['control 4', {'rate_bp': 850, 'basis': 365, 'start_balance': 0, 'cycle_days': 30, 'events': [], 'min_bp': 150, 'min_floor': 10000}, {'interest': 0, 'balance': 0, 'minimum': 0, 'adb': 0}], ['control 5', {'rate_bp': 1200, 'basis': 365, 'start_balance': 100000, 'cycle_days': 28, 'events': [], 'min_bp': 150, 'min_floor': 10000}, {'interest': 921, 'balance': 100921, 'minimum': 10000, 'adb': 100000}], ['control 6', {'rate_bp': 1200, 'basis': 365, 'start_balance': 0, 'cycle_days': 31, 'events': [[20, 5000], [23, -80000]], 'min_bp': 150, 'min_floor': 2500}, {'interest': 5, 'balance': -74995, 'minimum': 0, 'adb': 483}]], [['regression: event effective day', {'rate_bp': 850, 'basis': 365, 'start_balance': 100000, 'cycle_days': 30, 'events': [[20, 50000], [29, -80000], [30, -80000]], 'min_bp': 100, 'min_floor': 10000}, {'interest': 773, 'balance': -9227, 'minimum': 0, 'adb': 110666}], ['control 1', {'rate_bp': 600, 'basis': 365, 'start_balance': 30000, 'cycle_days': 28, 'events': [], 'min_bp': 150, 'min_floor': 2500}, {'interest': 138, 'balance': 30138, 'minimum': 2500, 'adb': 30000}], ['control 2', {'rate_bp': 1200, 'basis': 365, 'start_balance': 1000000, 'cycle_days': 31, 'events': [], 'min_bp': 100, 'min_floor': 10000}, {'interest': 10192, 'balance': 1010192, 'minimum': 20192, 'adb': 1000000}], ['control 3', {'rate_bp': 1200, 'basis': 365, 'start_balance': 30000, 'cycle_days': 28, 'events': [], 'min_bp': 100, 'min_floor': 10000}, {'interest': 276, 'balance': 30276, 'minimum': 10000, 'adb': 30000}], ['control 4', {'rate_bp': 850, 'basis': 365, 'start_balance': 0, 'cycle_days': 31, 'events': [[30, 10000], [22, -20000], [23, 5000]], 'min_bp': 100, 'min_floor': 10000}, {'interest': 0, 'balance': -5000, 'minimum': 0, 'adb': 0}], ['control 5', {'rate_bp': 1200, 'basis': 365, 'start_balance': 30000, 'cycle_days': 30, 'events': [], 'min_bp': 100, 'min_floor': 10000}, {'interest': 296, 'balance': 30296, 'minimum': 10000, 'adb': 30000}], ['control 6', {'rate_bp': 1200, 'basis': 365, 'start_balance': 0, 'cycle_days': 28, 'events': [[2, -20000], [14, 10000], [20, -20000], [27, 10000]], 'min_bp': 150, 'min_floor': 10000}, {'interest': 0, 'balance': -20000, 'minimum': 0, 'adb': 0}]], [['regression: event effective day', {'rate_bp': 600, 'basis': 365, 'start_balance': 1000000, 'cycle_days': 28, 'events': [[21, 10000], [25, -80000]], 'min_bp': 100, 'min_floor': 2500}, {'interest': 4563, 'balance': 934563, 'minimum': 13863, 'adb': 991428}], ['control 1', {'rate_bp': 600, 'basis': 365, 'start_balance': 0, 'cycle_days': 31, 'events': [[15, 50000], [8, -80000], [21, 5000], [31, -20000]], 'min_bp': 150, 'min_floor': 10000}, {'interest': 0, 'balance': -45000, 'minimum': 0, 'adb': 0}], ['control 2', {'rate_bp': 1200, 'basis': 365, 'start_balance': 0, 'cycle_days': 28, 'events': [[5, -80000], [22, 5000]], 'min_bp': 100, 'min_floor': 10000}, {'interest': 0, 'balance': -75000, 'minimum': 0, 'adb': 0}], ['control 3', {'rate_bp': 600, 'basis': 365, 'start_balance': 0, 'cycle_days': 31, 'events': [[8, 5000], [3, 5000], [27, 10000], [28, -80000]], 'min_bp': 100, 'min_floor': 2500}, {'interest': 39, 'balance': -59961, 'minimum': 0, 'adb': 7580}], ['control 4', {'rate_bp': 600, 'basis': 365, 'start_balance': 30000, 'cycle_days': 31, 'events': [[13, 50000], [16, -20000], [7, -80000], [18, 50000]], 'min_bp': 100, 'min_floor': 2500}, {'interest': 99, 'balance': 30099, 'minimum': 2500, 'adb': 19354}], ['control 5', {'rate_bp': 1200, 'basis': 365, 'start_balance': 0, 'cycle_days': 28, 'events': [[4, -20000], [25, 5000]], 'min_bp': 100, 'min_floor': 2500}, {'interest': 0, 'balance': -15000, 'minimum': 0, 'adb': 0}], ['control 6', {'rate_bp': 600, 'basis': 365, 'start_balance': 1000000, 'cycle_days': 31, 'events': [], 'min_bp': 100, 'min_floor': 10000}, {'interest': 5096, 'balance': 1005096, 'minimum': 15096, 'adb': 1000000}]]]
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: event effective day | {'adb': 1006451, 'balance': 1000129, 'interest': 5129, 'minimum': 15079} | {'adb': 1006451, 'balance': 1000129, 'interest': 5129, 'minimum': 15079} | Passed |
| control 1 | {'adb': 1000, 'balance': -149990, 'interest': 10, 'minimum': 0} | {'adb': 1000, 'balance': -149990, 'interest': 10, 'minimum': 0} | Passed |
| control 2 | {'adb': 0, 'balance': 0, 'interest': 0, 'minimum': 0} | {'adb': 0, 'balance': 0, 'interest': 0, 'minimum': 0} | Passed |
| control 3 | {'adb': 11428, 'balance': -49895, 'interest': 105, 'minimum': 0} | {'adb': 11428, 'balance': -49895, 'interest': 105, 'minimum': 0} | Passed |
| control 4 | {'adb': 1000000, 'balance': 1010192, 'interest': 10192, 'minimum': 25192} | {'adb': 1000000, 'balance': 1010192, 'interest': 10192, 'minimum': 25192} | Passed |
| control 5 | {'adb': 0, 'balance': -10000, 'interest': 0, 'minimum': 0} | {'adb': 0, 'balance': -10000, 'interest': 0, 'minimum': 0} | Passed |
| control 6 | {'adb': 30000, 'balance': 30138, 'interest': 138, 'minimum': 10000} | {'adb': 30000, 'balance': 30138, 'interest': 138, 'minimum': 10000} | Passed |
SHA-256 / 25d3585aefb8a4deef143001c0c69145ef264baf6615e762ee1380ae02166935
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.084375+00:00.
Case digest / e69efb3f10362cd27806a282478401089158931224bd620ea3dc9c6853952465