FA-58376 / Loan amortization schedules / Open access
Schedule rows from a fixed payment: interest-only guard · case 01
A payment that exactly equals interest produces rows that never reduce principal.
ROOT CAUSE
The guard lets a payment equal to the interest through.
VERIFIED REPAIR
Reject payments that do not exceed the interest due.
Unsuccessful approach: Special-casing a zero payment still lets a payment equal to the interest through.
Case contract
x = {'principal', 'rate_bp', 'payment', 'max_months'}. For months 1..max_months while the balance is positive: interest = round_half_up(balance*bp/120000); if payment <= interest return {'error': 'negative_amortization', 'month': k}; principal paid = min(payment - interest, balance); append [k, interest, principal, balance after]. Return {'rows', 'balloon': remaining principal}.
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)
bal = x['principal']
rows = []
for k in range(1, x['max_months'] + 1):
if bal == 0:
break
interest = rnd(bal * x['rate_bp'], 120000)
pay = x['payment']
if pay < interest:
return {'error': 'negative_amortization', 'month': k}
princ = min(pay - interest, bal)
bal -= princ
rows.append([k, interest, princ, bal])
return {'rows': rows, 'balloon': bal}
def check(label, actual, expected):
observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
fixtures = [[['regression: interest-only guard', {'principal': 50000, 'rate_bp': 1200, 'payment': 500, 'max_months': 1}, {'error': 'negative_amortization', 'month': 1}], ['control 1', {'principal': 250000, 'rate_bp': 1999, 'payment': 5000, 'max_months': 24}, {'rows': [[1, 4165, 835, 249165], [2, 4151, 849, 248316], [3, 4137, 863, 247453], [4, 4122, 878, 246575], [5, 4108, 892, 245683], [6, 4093, 907, 244776], [7, 4078, 922, 243854], [8, 4062, 938, 242916], [9, 4047, 953, 241963], [10, 4031, 969, 240994], [11, 4015, 985, 240009], [12, 3998, 1002, 239007], [13, 3981, 1019, 237988], [14, 3964, 1036, 236952], [15, 3947, 1053, 235899], [16, 3930, 1070, 234829], [17, 3912, 1088, 233741], [18, 3894, 1106, 232635], [19, 3875, 1125, 231510], [20, 3857, 1143, 230367], [21, 3838, 1162, 229205], [22, 3818, 1182, 228023], [23, 3798, 1202, 226821], [24, 3778, 1222, 225599]], 'balloon': 225599}], ['control 2', {'principal': 250000, 'rate_bp': 0, 'payment': 500, 'max_months': 1}, {'rows': [[1, 0, 500, 249500]], 'balloon': 249500}], ['control 3', {'principal': 250000, 'rate_bp': 1999, 'payment': 500, 'max_months': 24}, {'error': 'negative_amortization', 'month': 1}], ['control 4', {'principal': 100000, 'rate_bp': 1999, 'payment': 1000, 'max_months': 0}, {'rows': [], 'balloon': 100000}], ['control 5', {'principal': 250000, 'rate_bp': 1200, 'payment': 5000, 'max_months': 24}, {'rows': [[1, 2500, 2500, 247500], [2, 2475, 2525, 244975], [3, 2450, 2550, 242425], [4, 2424, 2576, 239849], [5, 2398, 2602, 237247], [6, 2372, 2628, 234619], [7, 2346, 2654, 231965], [8, 2320, 2680, 229285], [9, 2293, 2707, 226578], [10, 2266, 2734, 223844], [11, 2238, 2762, 221082], [12, 2211, 2789, 218293], [13, 2183, 2817, 215476], [14, 2155, 2845, 212631], [15, 2126, 2874, 209757], [16, 2098, 2902, 206855], [17, 2069, 2931, 203924], [18, 2039, 2961, 200963], [19, 2010, 2990, 197973], [20, 1980, 3020, 194953], [21, 1950, 3050, 191903], [22, 1919, 3081, 188822], [23, 1888, 3112, 185710], [24, 1857, 3143, 182567]], 'balloon': 182567}], ['control 6', {'principal': 100000, 'rate_bp': 2400, 'payment': 20007, 'max_months': 12}, {'rows': [[1, 2000, 18007, 81993], [2, 1640, 18367, 63626], [3, 1273, 18734, 44892], [4, 898, 19109, 25783], [5, 516, 19491, 6292], [6, 126, 6292, 0]], 'balloon': 0}]], [['regression: interest-only guard', {'principal': 50000, 'rate_bp': 2400, 'payment': 1000, 'max_months': 6}, {'error': 'negative_amortization', 'month': 1}], ['control 1', {'principal': 100000, 'rate_bp': 600, 'payment': 20007, 'max_months': 1}, {'rows': [[1, 500, 19507, 80493]], 'balloon': 80493}], ['control 2', {'principal': 12345, 'rate_bp': 600, 'payment': 500, 'max_months': 24}, {'rows': [[1, 62, 438, 11907], [2, 60, 440, 11467], [3, 57, 443, 11024], [4, 55, 445, 10579], [5, 53, 447, 10132], [6, 51, 449, 9683], [7, 48, 452, 9231], [8, 46, 454, 8777], [9, 44, 456, 8321], [10, 42, 458, 7863], [11, 39, 461, 7402], [12, 37, 463, 6939], [13, 35, 465, 6474], [14, 32, 468, 6006], [15, 30, 470, 5536], [16, 28, 472, 5064], [17, 25, 475, 4589], [18, 23, 477, 4112], [19, 21, 479, 3633], [20, 18, 482, 3151], [21, 16, 484, 2667], [22, 13, 487, 2180], [23, 11, 489, 1691], [24, 8, 492, 1199]], 'balloon': 1199}], ['control 3', {'principal': 100000, 'rate_bp': 0, 'payment': 20007, 'max_months': 3}, {'rows': [[1, 0, 20007, 79993], [2, 0, 20007, 59986], [3, 0, 20007, 39979]], 'balloon': 39979}], ['control 4', {'principal': 100000, 'rate_bp': 1200, 'payment': 5000, 'max_months': 6}, {'rows': [[1, 1000, 4000, 96000], [2, 960, 4040, 91960], [3, 920, 4080, 87880], [4, 879, 4121, 83759], [5, 838, 4162, 79597], [6, 796, 4204, 75393]], 'balloon': 75393}], ['control 5', {'principal': 1000, 'rate_bp': 0, 'payment': 1000, 'max_months': 0}, {'rows': [], 'balloon': 1000}], ['control 6', {'principal': 50000, 'rate_bp': 0, 'payment': 1000, 'max_months': 3}, {'rows': [[1, 0, 1000, 49000], [2, 0, 1000, 48000], [3, 0, 1000, 47000]], 'balloon': 47000}]], [['regression: interest-only guard', {'principal': 250000, 'rate_bp': 2400, 'payment': 5000, 'max_months': 3}, {'error': 'negative_amortization', 'month': 1}], ['control 1', {'principal': 50000, 'rate_bp': 2400, 'payment': 5000, 'max_months': 3}, {'rows': [[1, 1000, 4000, 46000], [2, 920, 4080, 41920], [3, 838, 4162, 37758]], 'balloon': 37758}], ['control 2', {'principal': 12345, 'rate_bp': 0, 'payment': 4115, 'max_months': 24}, {'rows': [[1, 0, 4115, 8230], [2, 0, 4115, 4115], [3, 0, 4115, 0]], 'balloon': 0}], ['control 3', {'principal': 12345, 'rate_bp': 0, 'payment': 4115, 'max_months': 1}, {'rows': [[1, 0, 4115, 8230]], 'balloon': 8230}], ['control 4', {'principal': 50000, 'rate_bp': 1200, 'payment': 5000, 'max_months': 24}, {'rows': [[1, 500, 4500, 45500], [2, 455, 4545, 40955], [3, 410, 4590, 36365], [4, 364, 4636, 31729], [5, 317, 4683, 27046], [6, 270, 4730, 22316], [7, 223, 4777, 17539], [8, 175, 4825, 12714], [9, 127, 4873, 7841], [10, 78, 4922, 2919], [11, 29, 2919, 0]], 'balloon': 0}], ['control 5', {'principal': 250000, 'rate_bp': 600, 'payment': 20833, 'max_months': 1}, {'rows': [[1, 1250, 19583, 230417]], 'balloon': 230417}], ['control 6', {'principal': 1000, 'rate_bp': 2400, 'payment': 1050, 'max_months': 0}, {'rows': [], 'balloon': 1000}]], [['regression: interest-only guard', {'principal': 50000, 'rate_bp': 1200, 'payment': 500, 'max_months': 24}, {'error': 'negative_amortization', 'month': 1}], ['control 1', {'principal': 12345, 'rate_bp': 1200, 'payment': 5000, 'max_months': 3}, {'rows': [[1, 123, 4877, 7468], [2, 75, 4925, 2543], [3, 25, 2543, 0]], 'balloon': 0}], ['control 2', {'principal': 12345, 'rate_bp': 2400, 'payment': 500, 'max_months': 6}, {'rows': [[1, 247, 253, 12092], [2, 242, 258, 11834], [3, 237, 263, 11571], [4, 231, 269, 11302], [5, 226, 274, 11028], [6, 221, 279, 10749]], 'balloon': 10749}], ['control 3', {'principal': 12345, 'rate_bp': 0, 'payment': 5000, 'max_months': 6}, {'rows': [[1, 0, 5000, 7345], [2, 0, 5000, 2345], [3, 0, 2345, 0]], 'balloon': 0}], ['control 4', {'principal': 100000, 'rate_bp': 0, 'payment': 8333, 'max_months': 24}, {'rows': [[1, 0, 8333, 91667], [2, 0, 8333, 83334], [3, 0, 8333, 75001], [4, 0, 8333, 66668], [5, 0, 8333, 58335], [6, 0, 8333, 50002], [7, 0, 8333, 41669], [8, 0, 8333, 33336], [9, 0, 8333, 25003], [10, 0, 8333, 16670], [11, 0, 8333, 8337], [12, 0, 8333, 4], [13, 0, 4, 0]], 'balloon': 0}], ['control 5', {'principal': 1000, 'rate_bp': 1200, 'payment': 333, 'max_months': 6}, {'rows': [[1, 10, 323, 677], [2, 7, 326, 351], [3, 4, 329, 22], [4, 0, 22, 0]], 'balloon': 0}], ['control 6', {'principal': 1000, 'rate_bp': 1999, 'payment': 5000, 'max_months': 24}, {'rows': [[1, 17, 1000, 0]], 'balloon': 0}]], [['regression: interest-only guard', {'principal': 100000, 'rate_bp': 1200, 'payment': 1000, 'max_months': 6}, {'error': 'negative_amortization', 'month': 1}], ['control 1', {'principal': 1000, 'rate_bp': 2400, 'payment': 500, 'max_months': 3}, {'rows': [[1, 20, 480, 520], [2, 10, 490, 30], [3, 1, 30, 0]], 'balloon': 0}], ['control 2', {'principal': 50000, 'rate_bp': 0, 'payment': 10007, 'max_months': 24}, {'rows': [[1, 0, 10007, 39993], [2, 0, 10007, 29986], [3, 0, 10007, 19979], [4, 0, 10007, 9972], [5, 0, 9972, 0]], 'balloon': 0}], ['control 3', {'principal': 12345, 'rate_bp': 2400, 'payment': 2476, 'max_months': 0}, {'rows': [], 'balloon': 12345}], ['control 4', {'principal': 12345, 'rate_bp': 600, 'payment': 1200, 'max_months': 12}, {'rows': [[1, 62, 1138, 11207], [2, 56, 1144, 10063], [3, 50, 1150, 8913], [4, 45, 1155, 7758], [5, 39, 1161, 6597], [6, 33, 1167, 5430], [7, 27, 1173, 4257], [8, 21, 1179, 3078], [9, 15, 1185, 1893], [10, 9, 1191, 702], [11, 4, 702, 0]], 'balloon': 0}], ['control 5', {'principal': 250000, 'rate_bp': 2400, 'payment': 20833, 'max_months': 0}, {'rows': [], 'balloon': 250000}], ['control 6', {'principal': 50000, 'rate_bp': 600, 'payment': 1000, 'max_months': 0}, {'rows': [], 'balloon': 50000}]]]
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: interest-only guard | {'balloon': 50000, 'rows': [[1, 500, 0, 50000]]} | {'error': 'negative_amortization', 'month': 1} | Failed |
| control 1 | {'balloon': 225599, 'rows': [[1, 4165, 835, 249165], [2, 4151, 849, 248316], [3, 4137, 863, 247453], [4, 4122, 878, 246575], [5, 4108, 892, 245683], [6, 4093, 907, 244776], [7, 4078, 922, 243854], [8, 4062, 938, 242916], [9, 4047, 953, 241963], [10, 4031, 969, 240994], [11, 4015, 985, 240009], [12, 3998, 1002, 239007], [13, 3981, 1019, 237988], [14, 3964, 1036, 236952], [15, 3947, 1053, 235899], [16, 3930, 1070, 234829], [17, 3912, 1088, 233741], [18, 3894, 1106, 232635], [19, 3875, 1125, 231510], [20, 3857, 1143, 230367], [21, 3838, 1162, 229205], [22, 3818, 1182, 228023], [23, 3798, 1202, 226821], [24, 3778, 1222, 225599]]} | {'balloon': 225599, 'rows': [[1, 4165, 835, 249165], [2, 4151, 849, 248316], [3, 4137, 863, 247453], [4, 4122, 878, 246575], [5, 4108, 892, 245683], [6, 4093, 907, 244776], [7, 4078, 922, 243854], [8, 4062, 938, 242916], [9, 4047, 953, 241963], [10, 4031, 969, 240994], [11, 4015, 985, 240009], [12, 3998, 1002, 239007], [13, 3981, 1019, 237988], [14, 3964, 1036, 236952], [15, 3947, 1053, 235899], [16, 3930, 1070, 234829], [17, 3912, 1088, 233741], [18, 3894, 1106, 232635], [19, 3875, 1125, 231510], [20, 3857, 1143, 230367], [21, 3838, 1162, 229205], [22, 3818, 1182, 228023], [23, 3798, 1202, 226821], [24, 3778, 1222, 225599]]} | Passed |
| control 2 | {'balloon': 249500, 'rows': [[1, 0, 500, 249500]]} | {'balloon': 249500, 'rows': [[1, 0, 500, 249500]]} | Passed |
| control 3 | {'error': 'negative_amortization', 'month': 1} | {'error': 'negative_amortization', 'month': 1} | Passed |
| control 4 | {'balloon': 100000, 'rows': []} | {'balloon': 100000, 'rows': []} | Passed |
| control 5 | {'balloon': 182567, 'rows': [[1, 2500, 2500, 247500], [2, 2475, 2525, 244975], [3, 2450, 2550, 242425], [4, 2424, 2576, 239849], [5, 2398, 2602, 237247], [6, 2372, 2628, 234619], [7, 2346, 2654, 231965], [8, 2320, 2680, 229285], [9, 2293, 2707, 226578], [10, 2266, 2734, 223844], [11, 2238, 2762, 221082], [12, 2211, 2789, 218293], [13, 2183, 2817, 215476], [14, 2155, 2845, 212631], [15, 2126, 2874, 209757], [16, 2098, 2902, 206855], [17, 2069, 2931, 203924], [18, 2039, 2961, 200963], [19, 2010, 2990, 197973], [20, 1980, 3020, 194953], [21, 1950, 3050, 191903], [22, 1919, 3081, 188822], [23, 1888, 3112, 185710], [24, 1857, 3143, 182567]]} | {'balloon': 182567, 'rows': [[1, 2500, 2500, 247500], [2, 2475, 2525, 244975], [3, 2450, 2550, 242425], [4, 2424, 2576, 239849], [5, 2398, 2602, 237247], [6, 2372, 2628, 234619], [7, 2346, 2654, 231965], [8, 2320, 2680, 229285], [9, 2293, 2707, 226578], [10, 2266, 2734, 223844], [11, 2238, 2762, 221082], [12, 2211, 2789, 218293], [13, 2183, 2817, 215476], [14, 2155, 2845, 212631], [15, 2126, 2874, 209757], [16, 2098, 2902, 206855], [17, 2069, 2931, 203924], [18, 2039, 2961, 200963], [19, 2010, 2990, 197973], [20, 1980, 3020, 194953], [21, 1950, 3050, 191903], [22, 1919, 3081, 188822], [23, 1888, 3112, 185710], [24, 1857, 3143, 182567]]} | Passed |
| control 6 | {'balloon': 0, 'rows': [[1, 2000, 18007, 81993], [2, 1640, 18367, 63626], [3, 1273, 18734, 44892], [4, 898, 19109, 25783], [5, 516, 19491, 6292], [6, 126, 6292, 0]]} | {'balloon': 0, 'rows': [[1, 2000, 18007, 81993], [2, 1640, 18367, 63626], [3, 1273, 18734, 44892], [4, 898, 19109, 25783], [5, 516, 19491, 6292], [6, 126, 6292, 0]]} | Passed |
SHA-256 / 1ff3e5bd4a6aff5c4052150cfd402d860741538ca4688cbf7dc0c501ca9328f2
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)
bal = x['principal']
rows = []
for k in range(1, x['max_months'] + 1):
if bal == 0:
break
interest = rnd(bal * x['rate_bp'], 120000)
pay = x['payment']
if pay < interest or pay == 0:
return {'error': 'negative_amortization', 'month': k}
princ = min(pay - interest, bal)
bal -= princ
rows.append([k, interest, princ, bal])
return {'rows': rows, 'balloon': bal}
def check(label, actual, expected):
observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
fixtures = [[['regression: interest-only guard', {'principal': 50000, 'rate_bp': 1200, 'payment': 500, 'max_months': 1}, {'error': 'negative_amortization', 'month': 1}], ['control 1', {'principal': 250000, 'rate_bp': 1999, 'payment': 5000, 'max_months': 24}, {'rows': [[1, 4165, 835, 249165], [2, 4151, 849, 248316], [3, 4137, 863, 247453], [4, 4122, 878, 246575], [5, 4108, 892, 245683], [6, 4093, 907, 244776], [7, 4078, 922, 243854], [8, 4062, 938, 242916], [9, 4047, 953, 241963], [10, 4031, 969, 240994], [11, 4015, 985, 240009], [12, 3998, 1002, 239007], [13, 3981, 1019, 237988], [14, 3964, 1036, 236952], [15, 3947, 1053, 235899], [16, 3930, 1070, 234829], [17, 3912, 1088, 233741], [18, 3894, 1106, 232635], [19, 3875, 1125, 231510], [20, 3857, 1143, 230367], [21, 3838, 1162, 229205], [22, 3818, 1182, 228023], [23, 3798, 1202, 226821], [24, 3778, 1222, 225599]], 'balloon': 225599}], ['control 2', {'principal': 250000, 'rate_bp': 0, 'payment': 500, 'max_months': 1}, {'rows': [[1, 0, 500, 249500]], 'balloon': 249500}], ['control 3', {'principal': 250000, 'rate_bp': 1999, 'payment': 500, 'max_months': 24}, {'error': 'negative_amortization', 'month': 1}], ['control 4', {'principal': 100000, 'rate_bp': 1999, 'payment': 1000, 'max_months': 0}, {'rows': [], 'balloon': 100000}], ['control 5', {'principal': 250000, 'rate_bp': 1200, 'payment': 5000, 'max_months': 24}, {'rows': [[1, 2500, 2500, 247500], [2, 2475, 2525, 244975], [3, 2450, 2550, 242425], [4, 2424, 2576, 239849], [5, 2398, 2602, 237247], [6, 2372, 2628, 234619], [7, 2346, 2654, 231965], [8, 2320, 2680, 229285], [9, 2293, 2707, 226578], [10, 2266, 2734, 223844], [11, 2238, 2762, 221082], [12, 2211, 2789, 218293], [13, 2183, 2817, 215476], [14, 2155, 2845, 212631], [15, 2126, 2874, 209757], [16, 2098, 2902, 206855], [17, 2069, 2931, 203924], [18, 2039, 2961, 200963], [19, 2010, 2990, 197973], [20, 1980, 3020, 194953], [21, 1950, 3050, 191903], [22, 1919, 3081, 188822], [23, 1888, 3112, 185710], [24, 1857, 3143, 182567]], 'balloon': 182567}], ['control 6', {'principal': 100000, 'rate_bp': 2400, 'payment': 20007, 'max_months': 12}, {'rows': [[1, 2000, 18007, 81993], [2, 1640, 18367, 63626], [3, 1273, 18734, 44892], [4, 898, 19109, 25783], [5, 516, 19491, 6292], [6, 126, 6292, 0]], 'balloon': 0}]], [['regression: interest-only guard', {'principal': 50000, 'rate_bp': 2400, 'payment': 1000, 'max_months': 6}, {'error': 'negative_amortization', 'month': 1}], ['control 1', {'principal': 100000, 'rate_bp': 600, 'payment': 20007, 'max_months': 1}, {'rows': [[1, 500, 19507, 80493]], 'balloon': 80493}], ['control 2', {'principal': 12345, 'rate_bp': 600, 'payment': 500, 'max_months': 24}, {'rows': [[1, 62, 438, 11907], [2, 60, 440, 11467], [3, 57, 443, 11024], [4, 55, 445, 10579], [5, 53, 447, 10132], [6, 51, 449, 9683], [7, 48, 452, 9231], [8, 46, 454, 8777], [9, 44, 456, 8321], [10, 42, 458, 7863], [11, 39, 461, 7402], [12, 37, 463, 6939], [13, 35, 465, 6474], [14, 32, 468, 6006], [15, 30, 470, 5536], [16, 28, 472, 5064], [17, 25, 475, 4589], [18, 23, 477, 4112], [19, 21, 479, 3633], [20, 18, 482, 3151], [21, 16, 484, 2667], [22, 13, 487, 2180], [23, 11, 489, 1691], [24, 8, 492, 1199]], 'balloon': 1199}], ['control 3', {'principal': 100000, 'rate_bp': 0, 'payment': 20007, 'max_months': 3}, {'rows': [[1, 0, 20007, 79993], [2, 0, 20007, 59986], [3, 0, 20007, 39979]], 'balloon': 39979}], ['control 4', {'principal': 100000, 'rate_bp': 1200, 'payment': 5000, 'max_months': 6}, {'rows': [[1, 1000, 4000, 96000], [2, 960, 4040, 91960], [3, 920, 4080, 87880], [4, 879, 4121, 83759], [5, 838, 4162, 79597], [6, 796, 4204, 75393]], 'balloon': 75393}], ['control 5', {'principal': 1000, 'rate_bp': 0, 'payment': 1000, 'max_months': 0}, {'rows': [], 'balloon': 1000}], ['control 6', {'principal': 50000, 'rate_bp': 0, 'payment': 1000, 'max_months': 3}, {'rows': [[1, 0, 1000, 49000], [2, 0, 1000, 48000], [3, 0, 1000, 47000]], 'balloon': 47000}]], [['regression: interest-only guard', {'principal': 250000, 'rate_bp': 2400, 'payment': 5000, 'max_months': 3}, {'error': 'negative_amortization', 'month': 1}], ['control 1', {'principal': 50000, 'rate_bp': 2400, 'payment': 5000, 'max_months': 3}, {'rows': [[1, 1000, 4000, 46000], [2, 920, 4080, 41920], [3, 838, 4162, 37758]], 'balloon': 37758}], ['control 2', {'principal': 12345, 'rate_bp': 0, 'payment': 4115, 'max_months': 24}, {'rows': [[1, 0, 4115, 8230], [2, 0, 4115, 4115], [3, 0, 4115, 0]], 'balloon': 0}], ['control 3', {'principal': 12345, 'rate_bp': 0, 'payment': 4115, 'max_months': 1}, {'rows': [[1, 0, 4115, 8230]], 'balloon': 8230}], ['control 4', {'principal': 50000, 'rate_bp': 1200, 'payment': 5000, 'max_months': 24}, {'rows': [[1, 500, 4500, 45500], [2, 455, 4545, 40955], [3, 410, 4590, 36365], [4, 364, 4636, 31729], [5, 317, 4683, 27046], [6, 270, 4730, 22316], [7, 223, 4777, 17539], [8, 175, 4825, 12714], [9, 127, 4873, 7841], [10, 78, 4922, 2919], [11, 29, 2919, 0]], 'balloon': 0}], ['control 5', {'principal': 250000, 'rate_bp': 600, 'payment': 20833, 'max_months': 1}, {'rows': [[1, 1250, 19583, 230417]], 'balloon': 230417}], ['control 6', {'principal': 1000, 'rate_bp': 2400, 'payment': 1050, 'max_months': 0}, {'rows': [], 'balloon': 1000}]], [['regression: interest-only guard', {'principal': 50000, 'rate_bp': 1200, 'payment': 500, 'max_months': 24}, {'error': 'negative_amortization', 'month': 1}], ['control 1', {'principal': 12345, 'rate_bp': 1200, 'payment': 5000, 'max_months': 3}, {'rows': [[1, 123, 4877, 7468], [2, 75, 4925, 2543], [3, 25, 2543, 0]], 'balloon': 0}], ['control 2', {'principal': 12345, 'rate_bp': 2400, 'payment': 500, 'max_months': 6}, {'rows': [[1, 247, 253, 12092], [2, 242, 258, 11834], [3, 237, 263, 11571], [4, 231, 269, 11302], [5, 226, 274, 11028], [6, 221, 279, 10749]], 'balloon': 10749}], ['control 3', {'principal': 12345, 'rate_bp': 0, 'payment': 5000, 'max_months': 6}, {'rows': [[1, 0, 5000, 7345], [2, 0, 5000, 2345], [3, 0, 2345, 0]], 'balloon': 0}], ['control 4', {'principal': 100000, 'rate_bp': 0, 'payment': 8333, 'max_months': 24}, {'rows': [[1, 0, 8333, 91667], [2, 0, 8333, 83334], [3, 0, 8333, 75001], [4, 0, 8333, 66668], [5, 0, 8333, 58335], [6, 0, 8333, 50002], [7, 0, 8333, 41669], [8, 0, 8333, 33336], [9, 0, 8333, 25003], [10, 0, 8333, 16670], [11, 0, 8333, 8337], [12, 0, 8333, 4], [13, 0, 4, 0]], 'balloon': 0}], ['control 5', {'principal': 1000, 'rate_bp': 1200, 'payment': 333, 'max_months': 6}, {'rows': [[1, 10, 323, 677], [2, 7, 326, 351], [3, 4, 329, 22], [4, 0, 22, 0]], 'balloon': 0}], ['control 6', {'principal': 1000, 'rate_bp': 1999, 'payment': 5000, 'max_months': 24}, {'rows': [[1, 17, 1000, 0]], 'balloon': 0}]], [['regression: interest-only guard', {'principal': 100000, 'rate_bp': 1200, 'payment': 1000, 'max_months': 6}, {'error': 'negative_amortization', 'month': 1}], ['control 1', {'principal': 1000, 'rate_bp': 2400, 'payment': 500, 'max_months': 3}, {'rows': [[1, 20, 480, 520], [2, 10, 490, 30], [3, 1, 30, 0]], 'balloon': 0}], ['control 2', {'principal': 50000, 'rate_bp': 0, 'payment': 10007, 'max_months': 24}, {'rows': [[1, 0, 10007, 39993], [2, 0, 10007, 29986], [3, 0, 10007, 19979], [4, 0, 10007, 9972], [5, 0, 9972, 0]], 'balloon': 0}], ['control 3', {'principal': 12345, 'rate_bp': 2400, 'payment': 2476, 'max_months': 0}, {'rows': [], 'balloon': 12345}], ['control 4', {'principal': 12345, 'rate_bp': 600, 'payment': 1200, 'max_months': 12}, {'rows': [[1, 62, 1138, 11207], [2, 56, 1144, 10063], [3, 50, 1150, 8913], [4, 45, 1155, 7758], [5, 39, 1161, 6597], [6, 33, 1167, 5430], [7, 27, 1173, 4257], [8, 21, 1179, 3078], [9, 15, 1185, 1893], [10, 9, 1191, 702], [11, 4, 702, 0]], 'balloon': 0}], ['control 5', {'principal': 250000, 'rate_bp': 2400, 'payment': 20833, 'max_months': 0}, {'rows': [], 'balloon': 250000}], ['control 6', {'principal': 50000, 'rate_bp': 600, 'payment': 1000, 'max_months': 0}, {'rows': [], 'balloon': 50000}]]]
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: interest-only guard | {'balloon': 50000, 'rows': [[1, 500, 0, 50000]]} | {'error': 'negative_amortization', 'month': 1} | Failed |
| control 1 | {'balloon': 225599, 'rows': [[1, 4165, 835, 249165], [2, 4151, 849, 248316], [3, 4137, 863, 247453], [4, 4122, 878, 246575], [5, 4108, 892, 245683], [6, 4093, 907, 244776], [7, 4078, 922, 243854], [8, 4062, 938, 242916], [9, 4047, 953, 241963], [10, 4031, 969, 240994], [11, 4015, 985, 240009], [12, 3998, 1002, 239007], [13, 3981, 1019, 237988], [14, 3964, 1036, 236952], [15, 3947, 1053, 235899], [16, 3930, 1070, 234829], [17, 3912, 1088, 233741], [18, 3894, 1106, 232635], [19, 3875, 1125, 231510], [20, 3857, 1143, 230367], [21, 3838, 1162, 229205], [22, 3818, 1182, 228023], [23, 3798, 1202, 226821], [24, 3778, 1222, 225599]]} | {'balloon': 225599, 'rows': [[1, 4165, 835, 249165], [2, 4151, 849, 248316], [3, 4137, 863, 247453], [4, 4122, 878, 246575], [5, 4108, 892, 245683], [6, 4093, 907, 244776], [7, 4078, 922, 243854], [8, 4062, 938, 242916], [9, 4047, 953, 241963], [10, 4031, 969, 240994], [11, 4015, 985, 240009], [12, 3998, 1002, 239007], [13, 3981, 1019, 237988], [14, 3964, 1036, 236952], [15, 3947, 1053, 235899], [16, 3930, 1070, 234829], [17, 3912, 1088, 233741], [18, 3894, 1106, 232635], [19, 3875, 1125, 231510], [20, 3857, 1143, 230367], [21, 3838, 1162, 229205], [22, 3818, 1182, 228023], [23, 3798, 1202, 226821], [24, 3778, 1222, 225599]]} | Passed |
| control 2 | {'balloon': 249500, 'rows': [[1, 0, 500, 249500]]} | {'balloon': 249500, 'rows': [[1, 0, 500, 249500]]} | Passed |
| control 3 | {'error': 'negative_amortization', 'month': 1} | {'error': 'negative_amortization', 'month': 1} | Passed |
| control 4 | {'balloon': 100000, 'rows': []} | {'balloon': 100000, 'rows': []} | Passed |
| control 5 | {'balloon': 182567, 'rows': [[1, 2500, 2500, 247500], [2, 2475, 2525, 244975], [3, 2450, 2550, 242425], [4, 2424, 2576, 239849], [5, 2398, 2602, 237247], [6, 2372, 2628, 234619], [7, 2346, 2654, 231965], [8, 2320, 2680, 229285], [9, 2293, 2707, 226578], [10, 2266, 2734, 223844], [11, 2238, 2762, 221082], [12, 2211, 2789, 218293], [13, 2183, 2817, 215476], [14, 2155, 2845, 212631], [15, 2126, 2874, 209757], [16, 2098, 2902, 206855], [17, 2069, 2931, 203924], [18, 2039, 2961, 200963], [19, 2010, 2990, 197973], [20, 1980, 3020, 194953], [21, 1950, 3050, 191903], [22, 1919, 3081, 188822], [23, 1888, 3112, 185710], [24, 1857, 3143, 182567]]} | {'balloon': 182567, 'rows': [[1, 2500, 2500, 247500], [2, 2475, 2525, 244975], [3, 2450, 2550, 242425], [4, 2424, 2576, 239849], [5, 2398, 2602, 237247], [6, 2372, 2628, 234619], [7, 2346, 2654, 231965], [8, 2320, 2680, 229285], [9, 2293, 2707, 226578], [10, 2266, 2734, 223844], [11, 2238, 2762, 221082], [12, 2211, 2789, 218293], [13, 2183, 2817, 215476], [14, 2155, 2845, 212631], [15, 2126, 2874, 209757], [16, 2098, 2902, 206855], [17, 2069, 2931, 203924], [18, 2039, 2961, 200963], [19, 2010, 2990, 197973], [20, 1980, 3020, 194953], [21, 1950, 3050, 191903], [22, 1919, 3081, 188822], [23, 1888, 3112, 185710], [24, 1857, 3143, 182567]]} | Passed |
| control 6 | {'balloon': 0, 'rows': [[1, 2000, 18007, 81993], [2, 1640, 18367, 63626], [3, 1273, 18734, 44892], [4, 898, 19109, 25783], [5, 516, 19491, 6292], [6, 126, 6292, 0]]} | {'balloon': 0, 'rows': [[1, 2000, 18007, 81993], [2, 1640, 18367, 63626], [3, 1273, 18734, 44892], [4, 898, 19109, 25783], [5, 516, 19491, 6292], [6, 126, 6292, 0]]} | Passed |
SHA-256 / c75933a55b3d67f3df486795fb35e5cdea4f2f11c960876ae8101618c3948779
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)
bal = x['principal']
rows = []
for k in range(1, x['max_months'] + 1):
if bal == 0:
break
interest = rnd(bal * x['rate_bp'], 120000)
pay = x['payment']
if pay <= interest:
return {'error': 'negative_amortization', 'month': k}
princ = min(pay - interest, bal)
bal -= princ
rows.append([k, interest, princ, bal])
return {'rows': rows, 'balloon': bal}
def check(label, actual, expected):
observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
fixtures = [[['regression: interest-only guard', {'principal': 50000, 'rate_bp': 1200, 'payment': 500, 'max_months': 1}, {'error': 'negative_amortization', 'month': 1}], ['control 1', {'principal': 250000, 'rate_bp': 1999, 'payment': 5000, 'max_months': 24}, {'rows': [[1, 4165, 835, 249165], [2, 4151, 849, 248316], [3, 4137, 863, 247453], [4, 4122, 878, 246575], [5, 4108, 892, 245683], [6, 4093, 907, 244776], [7, 4078, 922, 243854], [8, 4062, 938, 242916], [9, 4047, 953, 241963], [10, 4031, 969, 240994], [11, 4015, 985, 240009], [12, 3998, 1002, 239007], [13, 3981, 1019, 237988], [14, 3964, 1036, 236952], [15, 3947, 1053, 235899], [16, 3930, 1070, 234829], [17, 3912, 1088, 233741], [18, 3894, 1106, 232635], [19, 3875, 1125, 231510], [20, 3857, 1143, 230367], [21, 3838, 1162, 229205], [22, 3818, 1182, 228023], [23, 3798, 1202, 226821], [24, 3778, 1222, 225599]], 'balloon': 225599}], ['control 2', {'principal': 250000, 'rate_bp': 0, 'payment': 500, 'max_months': 1}, {'rows': [[1, 0, 500, 249500]], 'balloon': 249500}], ['control 3', {'principal': 250000, 'rate_bp': 1999, 'payment': 500, 'max_months': 24}, {'error': 'negative_amortization', 'month': 1}], ['control 4', {'principal': 100000, 'rate_bp': 1999, 'payment': 1000, 'max_months': 0}, {'rows': [], 'balloon': 100000}], ['control 5', {'principal': 250000, 'rate_bp': 1200, 'payment': 5000, 'max_months': 24}, {'rows': [[1, 2500, 2500, 247500], [2, 2475, 2525, 244975], [3, 2450, 2550, 242425], [4, 2424, 2576, 239849], [5, 2398, 2602, 237247], [6, 2372, 2628, 234619], [7, 2346, 2654, 231965], [8, 2320, 2680, 229285], [9, 2293, 2707, 226578], [10, 2266, 2734, 223844], [11, 2238, 2762, 221082], [12, 2211, 2789, 218293], [13, 2183, 2817, 215476], [14, 2155, 2845, 212631], [15, 2126, 2874, 209757], [16, 2098, 2902, 206855], [17, 2069, 2931, 203924], [18, 2039, 2961, 200963], [19, 2010, 2990, 197973], [20, 1980, 3020, 194953], [21, 1950, 3050, 191903], [22, 1919, 3081, 188822], [23, 1888, 3112, 185710], [24, 1857, 3143, 182567]], 'balloon': 182567}], ['control 6', {'principal': 100000, 'rate_bp': 2400, 'payment': 20007, 'max_months': 12}, {'rows': [[1, 2000, 18007, 81993], [2, 1640, 18367, 63626], [3, 1273, 18734, 44892], [4, 898, 19109, 25783], [5, 516, 19491, 6292], [6, 126, 6292, 0]], 'balloon': 0}]], [['regression: interest-only guard', {'principal': 50000, 'rate_bp': 2400, 'payment': 1000, 'max_months': 6}, {'error': 'negative_amortization', 'month': 1}], ['control 1', {'principal': 100000, 'rate_bp': 600, 'payment': 20007, 'max_months': 1}, {'rows': [[1, 500, 19507, 80493]], 'balloon': 80493}], ['control 2', {'principal': 12345, 'rate_bp': 600, 'payment': 500, 'max_months': 24}, {'rows': [[1, 62, 438, 11907], [2, 60, 440, 11467], [3, 57, 443, 11024], [4, 55, 445, 10579], [5, 53, 447, 10132], [6, 51, 449, 9683], [7, 48, 452, 9231], [8, 46, 454, 8777], [9, 44, 456, 8321], [10, 42, 458, 7863], [11, 39, 461, 7402], [12, 37, 463, 6939], [13, 35, 465, 6474], [14, 32, 468, 6006], [15, 30, 470, 5536], [16, 28, 472, 5064], [17, 25, 475, 4589], [18, 23, 477, 4112], [19, 21, 479, 3633], [20, 18, 482, 3151], [21, 16, 484, 2667], [22, 13, 487, 2180], [23, 11, 489, 1691], [24, 8, 492, 1199]], 'balloon': 1199}], ['control 3', {'principal': 100000, 'rate_bp': 0, 'payment': 20007, 'max_months': 3}, {'rows': [[1, 0, 20007, 79993], [2, 0, 20007, 59986], [3, 0, 20007, 39979]], 'balloon': 39979}], ['control 4', {'principal': 100000, 'rate_bp': 1200, 'payment': 5000, 'max_months': 6}, {'rows': [[1, 1000, 4000, 96000], [2, 960, 4040, 91960], [3, 920, 4080, 87880], [4, 879, 4121, 83759], [5, 838, 4162, 79597], [6, 796, 4204, 75393]], 'balloon': 75393}], ['control 5', {'principal': 1000, 'rate_bp': 0, 'payment': 1000, 'max_months': 0}, {'rows': [], 'balloon': 1000}], ['control 6', {'principal': 50000, 'rate_bp': 0, 'payment': 1000, 'max_months': 3}, {'rows': [[1, 0, 1000, 49000], [2, 0, 1000, 48000], [3, 0, 1000, 47000]], 'balloon': 47000}]], [['regression: interest-only guard', {'principal': 250000, 'rate_bp': 2400, 'payment': 5000, 'max_months': 3}, {'error': 'negative_amortization', 'month': 1}], ['control 1', {'principal': 50000, 'rate_bp': 2400, 'payment': 5000, 'max_months': 3}, {'rows': [[1, 1000, 4000, 46000], [2, 920, 4080, 41920], [3, 838, 4162, 37758]], 'balloon': 37758}], ['control 2', {'principal': 12345, 'rate_bp': 0, 'payment': 4115, 'max_months': 24}, {'rows': [[1, 0, 4115, 8230], [2, 0, 4115, 4115], [3, 0, 4115, 0]], 'balloon': 0}], ['control 3', {'principal': 12345, 'rate_bp': 0, 'payment': 4115, 'max_months': 1}, {'rows': [[1, 0, 4115, 8230]], 'balloon': 8230}], ['control 4', {'principal': 50000, 'rate_bp': 1200, 'payment': 5000, 'max_months': 24}, {'rows': [[1, 500, 4500, 45500], [2, 455, 4545, 40955], [3, 410, 4590, 36365], [4, 364, 4636, 31729], [5, 317, 4683, 27046], [6, 270, 4730, 22316], [7, 223, 4777, 17539], [8, 175, 4825, 12714], [9, 127, 4873, 7841], [10, 78, 4922, 2919], [11, 29, 2919, 0]], 'balloon': 0}], ['control 5', {'principal': 250000, 'rate_bp': 600, 'payment': 20833, 'max_months': 1}, {'rows': [[1, 1250, 19583, 230417]], 'balloon': 230417}], ['control 6', {'principal': 1000, 'rate_bp': 2400, 'payment': 1050, 'max_months': 0}, {'rows': [], 'balloon': 1000}]], [['regression: interest-only guard', {'principal': 50000, 'rate_bp': 1200, 'payment': 500, 'max_months': 24}, {'error': 'negative_amortization', 'month': 1}], ['control 1', {'principal': 12345, 'rate_bp': 1200, 'payment': 5000, 'max_months': 3}, {'rows': [[1, 123, 4877, 7468], [2, 75, 4925, 2543], [3, 25, 2543, 0]], 'balloon': 0}], ['control 2', {'principal': 12345, 'rate_bp': 2400, 'payment': 500, 'max_months': 6}, {'rows': [[1, 247, 253, 12092], [2, 242, 258, 11834], [3, 237, 263, 11571], [4, 231, 269, 11302], [5, 226, 274, 11028], [6, 221, 279, 10749]], 'balloon': 10749}], ['control 3', {'principal': 12345, 'rate_bp': 0, 'payment': 5000, 'max_months': 6}, {'rows': [[1, 0, 5000, 7345], [2, 0, 5000, 2345], [3, 0, 2345, 0]], 'balloon': 0}], ['control 4', {'principal': 100000, 'rate_bp': 0, 'payment': 8333, 'max_months': 24}, {'rows': [[1, 0, 8333, 91667], [2, 0, 8333, 83334], [3, 0, 8333, 75001], [4, 0, 8333, 66668], [5, 0, 8333, 58335], [6, 0, 8333, 50002], [7, 0, 8333, 41669], [8, 0, 8333, 33336], [9, 0, 8333, 25003], [10, 0, 8333, 16670], [11, 0, 8333, 8337], [12, 0, 8333, 4], [13, 0, 4, 0]], 'balloon': 0}], ['control 5', {'principal': 1000, 'rate_bp': 1200, 'payment': 333, 'max_months': 6}, {'rows': [[1, 10, 323, 677], [2, 7, 326, 351], [3, 4, 329, 22], [4, 0, 22, 0]], 'balloon': 0}], ['control 6', {'principal': 1000, 'rate_bp': 1999, 'payment': 5000, 'max_months': 24}, {'rows': [[1, 17, 1000, 0]], 'balloon': 0}]], [['regression: interest-only guard', {'principal': 100000, 'rate_bp': 1200, 'payment': 1000, 'max_months': 6}, {'error': 'negative_amortization', 'month': 1}], ['control 1', {'principal': 1000, 'rate_bp': 2400, 'payment': 500, 'max_months': 3}, {'rows': [[1, 20, 480, 520], [2, 10, 490, 30], [3, 1, 30, 0]], 'balloon': 0}], ['control 2', {'principal': 50000, 'rate_bp': 0, 'payment': 10007, 'max_months': 24}, {'rows': [[1, 0, 10007, 39993], [2, 0, 10007, 29986], [3, 0, 10007, 19979], [4, 0, 10007, 9972], [5, 0, 9972, 0]], 'balloon': 0}], ['control 3', {'principal': 12345, 'rate_bp': 2400, 'payment': 2476, 'max_months': 0}, {'rows': [], 'balloon': 12345}], ['control 4', {'principal': 12345, 'rate_bp': 600, 'payment': 1200, 'max_months': 12}, {'rows': [[1, 62, 1138, 11207], [2, 56, 1144, 10063], [3, 50, 1150, 8913], [4, 45, 1155, 7758], [5, 39, 1161, 6597], [6, 33, 1167, 5430], [7, 27, 1173, 4257], [8, 21, 1179, 3078], [9, 15, 1185, 1893], [10, 9, 1191, 702], [11, 4, 702, 0]], 'balloon': 0}], ['control 5', {'principal': 250000, 'rate_bp': 2400, 'payment': 20833, 'max_months': 0}, {'rows': [], 'balloon': 250000}], ['control 6', {'principal': 50000, 'rate_bp': 600, 'payment': 1000, 'max_months': 0}, {'rows': [], 'balloon': 50000}]]]
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: interest-only guard | {'error': 'negative_amortization', 'month': 1} | {'error': 'negative_amortization', 'month': 1} | Passed |
| control 1 | {'balloon': 225599, 'rows': [[1, 4165, 835, 249165], [2, 4151, 849, 248316], [3, 4137, 863, 247453], [4, 4122, 878, 246575], [5, 4108, 892, 245683], [6, 4093, 907, 244776], [7, 4078, 922, 243854], [8, 4062, 938, 242916], [9, 4047, 953, 241963], [10, 4031, 969, 240994], [11, 4015, 985, 240009], [12, 3998, 1002, 239007], [13, 3981, 1019, 237988], [14, 3964, 1036, 236952], [15, 3947, 1053, 235899], [16, 3930, 1070, 234829], [17, 3912, 1088, 233741], [18, 3894, 1106, 232635], [19, 3875, 1125, 231510], [20, 3857, 1143, 230367], [21, 3838, 1162, 229205], [22, 3818, 1182, 228023], [23, 3798, 1202, 226821], [24, 3778, 1222, 225599]]} | {'balloon': 225599, 'rows': [[1, 4165, 835, 249165], [2, 4151, 849, 248316], [3, 4137, 863, 247453], [4, 4122, 878, 246575], [5, 4108, 892, 245683], [6, 4093, 907, 244776], [7, 4078, 922, 243854], [8, 4062, 938, 242916], [9, 4047, 953, 241963], [10, 4031, 969, 240994], [11, 4015, 985, 240009], [12, 3998, 1002, 239007], [13, 3981, 1019, 237988], [14, 3964, 1036, 236952], [15, 3947, 1053, 235899], [16, 3930, 1070, 234829], [17, 3912, 1088, 233741], [18, 3894, 1106, 232635], [19, 3875, 1125, 231510], [20, 3857, 1143, 230367], [21, 3838, 1162, 229205], [22, 3818, 1182, 228023], [23, 3798, 1202, 226821], [24, 3778, 1222, 225599]]} | Passed |
| control 2 | {'balloon': 249500, 'rows': [[1, 0, 500, 249500]]} | {'balloon': 249500, 'rows': [[1, 0, 500, 249500]]} | Passed |
| control 3 | {'error': 'negative_amortization', 'month': 1} | {'error': 'negative_amortization', 'month': 1} | Passed |
| control 4 | {'balloon': 100000, 'rows': []} | {'balloon': 100000, 'rows': []} | Passed |
| control 5 | {'balloon': 182567, 'rows': [[1, 2500, 2500, 247500], [2, 2475, 2525, 244975], [3, 2450, 2550, 242425], [4, 2424, 2576, 239849], [5, 2398, 2602, 237247], [6, 2372, 2628, 234619], [7, 2346, 2654, 231965], [8, 2320, 2680, 229285], [9, 2293, 2707, 226578], [10, 2266, 2734, 223844], [11, 2238, 2762, 221082], [12, 2211, 2789, 218293], [13, 2183, 2817, 215476], [14, 2155, 2845, 212631], [15, 2126, 2874, 209757], [16, 2098, 2902, 206855], [17, 2069, 2931, 203924], [18, 2039, 2961, 200963], [19, 2010, 2990, 197973], [20, 1980, 3020, 194953], [21, 1950, 3050, 191903], [22, 1919, 3081, 188822], [23, 1888, 3112, 185710], [24, 1857, 3143, 182567]]} | {'balloon': 182567, 'rows': [[1, 2500, 2500, 247500], [2, 2475, 2525, 244975], [3, 2450, 2550, 242425], [4, 2424, 2576, 239849], [5, 2398, 2602, 237247], [6, 2372, 2628, 234619], [7, 2346, 2654, 231965], [8, 2320, 2680, 229285], [9, 2293, 2707, 226578], [10, 2266, 2734, 223844], [11, 2238, 2762, 221082], [12, 2211, 2789, 218293], [13, 2183, 2817, 215476], [14, 2155, 2845, 212631], [15, 2126, 2874, 209757], [16, 2098, 2902, 206855], [17, 2069, 2931, 203924], [18, 2039, 2961, 200963], [19, 2010, 2990, 197973], [20, 1980, 3020, 194953], [21, 1950, 3050, 191903], [22, 1919, 3081, 188822], [23, 1888, 3112, 185710], [24, 1857, 3143, 182567]]} | Passed |
| control 6 | {'balloon': 0, 'rows': [[1, 2000, 18007, 81993], [2, 1640, 18367, 63626], [3, 1273, 18734, 44892], [4, 898, 19109, 25783], [5, 516, 19491, 6292], [6, 126, 6292, 0]]} | {'balloon': 0, 'rows': [[1, 2000, 18007, 81993], [2, 1640, 18367, 63626], [3, 1273, 18734, 44892], [4, 898, 19109, 25783], [5, 516, 19491, 6292], [6, 126, 6292, 0]]} | Passed |
SHA-256 / eea4610fe6cd45c2b8176aeff25c1763b0e8274fb288cddc0534233e83067888
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.103637+00:00.
Case digest / 2c95b1548515d1c644d78ad2725245275f22f3e9ceff804e20d73a77e1cf7f86