FA-58556 / Loan amortization schedules / Open access
Adjustable rate reset with caps: rate increment rounding · case 01
Fully indexed rates are always rounded up to the next increment.
ROOT CAUSE
Any remainder rounds up instead of half-up.
VERIFIED REPAIR
Round to the nearest increment, ties up.
Unsuccessful approach: Strict comparison rounds exact ties down.
Case contract
x = {'initial_bp', 'index_path': index at each reset, 'margin_bp', 'round_to', 'initial_cap_bp', 'periodic_cap_bp', 'lifetime_cap_bp' (above initial), 'floor_bp'}. At each reset the fully indexed rate index + margin is rounded half-up to a multiple of round_to, limited to within the cap of the previous note rate (initial cap at the first reset, periodic afterwards, both directions), then limited to [floor_bp, initial + lifetime cap]. Return the list of note rates.
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):
rate = x['initial_bp']
out = []
life_max = x['initial_bp'] + x['lifetime_cap_bp']
for i, idx in enumerate(x['index_path']):
fi = idx + x['margin_bp']
q, r = divmod(fi, x['round_to'])
fi = (q + (1 if r else 0)) * x['round_to']
cap = x['initial_cap_bp'] if i == 0 else x['periodic_cap_bp']
new = max(rate - cap, min(fi, rate + cap))
new = max(x['floor_bp'], min(new, life_max))
out.append(new)
rate = new
return out
def check(label, actual, expected):
observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
fixtures = [[['regression: rate increment rounding', {'initial_bp': 500, 'index_path': [700, 50, 700, 50, 100], 'margin_bp': 225, 'round_to': 125, 'initial_cap_bp': 500, 'periodic_cap_bp': 200, 'lifetime_cap_bp': 500, 'floor_bp': 300}, [875, 675, 875, 675, 475]], ['regression: rate increment rounding, partial-repair probe', {'initial_bp': 300, 'index_path': [100, 50, 250], 'margin_bp': 275, 'round_to': 50, 'initial_cap_bp': 500, 'periodic_cap_bp': 200, 'lifetime_cap_bp': 600, 'floor_bp': 300}, [400, 350, 550]], ['control 1', {'initial_bp': 500, 'index_path': [212, 100, 50, 250, 700], 'margin_bp': 275, 'round_to': 125, 'initial_cap_bp': 200, 'periodic_cap_bp': 100, 'lifetime_cap_bp': 500, 'floor_bp': 400}, [500, 400, 400, 500, 600]], ['control 2', {'initial_bp': 450, 'index_path': [250, 650, 100, 650, 650], 'margin_bp': 225, 'round_to': 25, 'initial_cap_bp': 200, 'periodic_cap_bp': 100, 'lifetime_cap_bp': 600, 'floor_bp': 225}, [475, 575, 475, 575, 675]], ['control 3', {'initial_bp': 500, 'index_path': [650], 'margin_bp': 225, 'round_to': 25, 'initial_cap_bp': 500, 'periodic_cap_bp': 200, 'lifetime_cap_bp': 600, 'floor_bp': 225}, [875]], ['control 4', {'initial_bp': 450, 'index_path': [50, 537, 400, 650], 'margin_bp': 275, 'round_to': 125, 'initial_cap_bp': 500, 'periodic_cap_bp': 100, 'lifetime_cap_bp': 600, 'floor_bp': 300}, [375, 475, 575, 675]], ['control 5', {'initial_bp': 300, 'index_path': [650, 212, 100], 'margin_bp': 275, 'round_to': 50, 'initial_cap_bp': 200, 'periodic_cap_bp': 100, 'lifetime_cap_bp': 600, 'floor_bp': 400}, [500, 500, 400]]], [['regression: rate increment rounding', {'initial_bp': 500, 'index_path': [537, 650, 250, 400, 537], 'margin_bp': 225, 'round_to': 25, 'initial_cap_bp': 200, 'periodic_cap_bp': 200, 'lifetime_cap_bp': 600, 'floor_bp': 400}, [700, 875, 675, 625, 750]], ['regression: rate increment rounding, partial-repair probe', {'initial_bp': 500, 'index_path': [50], 'margin_bp': 275, 'round_to': 50, 'initial_cap_bp': 500, 'periodic_cap_bp': 100, 'lifetime_cap_bp': 600, 'floor_bp': 225}, [350]], ['control 1', {'initial_bp': 450, 'index_path': [650, 50, 212], 'margin_bp': 275, 'round_to': 125, 'initial_cap_bp': 200, 'periodic_cap_bp': 100, 'lifetime_cap_bp': 600, 'floor_bp': 225}, [650, 550, 500]], ['control 2', {'initial_bp': 300, 'index_path': [650, 50, 700], 'margin_bp': 225, 'round_to': 125, 'initial_cap_bp': 500, 'periodic_cap_bp': 200, 'lifetime_cap_bp': 600, 'floor_bp': 400}, [800, 600, 800]], ['control 3', {'initial_bp': 450, 'index_path': [250, 100, 700], 'margin_bp': 225, 'round_to': 25, 'initial_cap_bp': 200, 'periodic_cap_bp': 200, 'lifetime_cap_bp': 600, 'floor_bp': 225}, [475, 325, 525]], ['control 4', {'initial_bp': 500, 'index_path': [250, 400, 50, 700, 400], 'margin_bp': 225, 'round_to': 25, 'initial_cap_bp': 500, 'periodic_cap_bp': 100, 'lifetime_cap_bp': 600, 'floor_bp': 225}, [475, 575, 475, 575, 625]], ['control 5', {'initial_bp': 300, 'index_path': [250], 'margin_bp': 275, 'round_to': 25, 'initial_cap_bp': 500, 'periodic_cap_bp': 200, 'lifetime_cap_bp': 500, 'floor_bp': 400}, [525]]], [['regression: rate increment rounding', {'initial_bp': 300, 'index_path': [537, 700, 250], 'margin_bp': 250, 'round_to': 125, 'initial_cap_bp': 500, 'periodic_cap_bp': 100, 'lifetime_cap_bp': 600, 'floor_bp': 225}, [750, 850, 750]], ['regression: rate increment rounding, partial-repair probe', {'initial_bp': 450, 'index_path': [250, 250, 212], 'margin_bp': 225, 'round_to': 50, 'initial_cap_bp': 500, 'periodic_cap_bp': 200, 'lifetime_cap_bp': 600, 'floor_bp': 225}, [500, 500, 450]], ['control 1', {'initial_bp': 450, 'index_path': [212, 650, 50, 650], 'margin_bp': 250, 'round_to': 125, 'initial_cap_bp': 200, 'periodic_cap_bp': 200, 'lifetime_cap_bp': 500, 'floor_bp': 300}, [500, 700, 500, 700]], ['control 2', {'initial_bp': 450, 'index_path': [537, 50, 537], 'margin_bp': 275, 'round_to': 125, 'initial_cap_bp': 200, 'periodic_cap_bp': 100, 'lifetime_cap_bp': 500, 'floor_bp': 300}, [650, 550, 650]], ['control 3', {'initial_bp': 450, 'index_path': [100], 'margin_bp': 225, 'round_to': 25, 'initial_cap_bp': 500, 'periodic_cap_bp': 200, 'lifetime_cap_bp': 500, 'floor_bp': 400}, [400]], ['control 4', {'initial_bp': 500, 'index_path': [700, 250], 'margin_bp': 275, 'round_to': 125, 'initial_cap_bp': 500, 'periodic_cap_bp': 100, 'lifetime_cap_bp': 500, 'floor_bp': 400}, [1000, 900]], ['control 5', {'initial_bp': 500, 'index_path': [212, 537], 'margin_bp': 275, 'round_to': 50, 'initial_cap_bp': 500, 'periodic_cap_bp': 200, 'lifetime_cap_bp': 500, 'floor_bp': 400}, [500, 700]]], [['regression: rate increment rounding', {'initial_bp': 300, 'index_path': [650, 250, 50, 537, 50], 'margin_bp': 275, 'round_to': 125, 'initial_cap_bp': 200, 'periodic_cap_bp': 200, 'lifetime_cap_bp': 600, 'floor_bp': 225}, [500, 500, 375, 575, 375]], ['regression: rate increment rounding, partial-repair probe', {'initial_bp': 450, 'index_path': [650, 212, 537, 50], 'margin_bp': 225, 'round_to': 50, 'initial_cap_bp': 500, 'periodic_cap_bp': 100, 'lifetime_cap_bp': 600, 'floor_bp': 400}, [900, 800, 750, 650]], ['control 1', {'initial_bp': 300, 'index_path': [400, 400, 537], 'margin_bp': 275, 'round_to': 25, 'initial_cap_bp': 200, 'periodic_cap_bp': 200, 'lifetime_cap_bp': 500, 'floor_bp': 400}, [500, 675, 800]], ['control 2', {'initial_bp': 500, 'index_path': [537], 'margin_bp': 250, 'round_to': 25, 'initial_cap_bp': 200, 'periodic_cap_bp': 200, 'lifetime_cap_bp': 600, 'floor_bp': 225}, [700]], ['control 3', {'initial_bp': 300, 'index_path': [250, 250, 250], 'margin_bp': 250, 'round_to': 50, 'initial_cap_bp': 500, 'periodic_cap_bp': 100, 'lifetime_cap_bp': 600, 'floor_bp': 300}, [500, 500, 500]], ['control 4', {'initial_bp': 300, 'index_path': [400, 100, 400, 250, 50], 'margin_bp': 275, 'round_to': 25, 'initial_cap_bp': 200, 'periodic_cap_bp': 100, 'lifetime_cap_bp': 600, 'floor_bp': 400}, [500, 400, 500, 525, 425]], ['control 5', {'initial_bp': 300, 'index_path': [700, 212], 'margin_bp': 275, 'round_to': 25, 'initial_cap_bp': 500, 'periodic_cap_bp': 100, 'lifetime_cap_bp': 500, 'floor_bp': 225}, [800, 700]]], [['regression: rate increment rounding', {'initial_bp': 450, 'index_path': [650], 'margin_bp': 275, 'round_to': 125, 'initial_cap_bp': 500, 'periodic_cap_bp': 200, 'lifetime_cap_bp': 500, 'floor_bp': 300}, [875]], ['regression: rate increment rounding, partial-repair probe', {'initial_bp': 500, 'index_path': [250, 537], 'margin_bp': 225, 'round_to': 50, 'initial_cap_bp': 200, 'periodic_cap_bp': 100, 'lifetime_cap_bp': 600, 'floor_bp': 300}, [500, 600]], ['control 1', {'initial_bp': 300, 'index_path': [400, 537, 100], 'margin_bp': 275, 'round_to': 125, 'initial_cap_bp': 200, 'periodic_cap_bp': 200, 'lifetime_cap_bp': 600, 'floor_bp': 400}, [500, 700, 500]], ['control 2', {'initial_bp': 500, 'index_path': [400, 212], 'margin_bp': 250, 'round_to': 25, 'initial_cap_bp': 500, 'periodic_cap_bp': 100, 'lifetime_cap_bp': 600, 'floor_bp': 300}, [650, 550]], ['control 3', {'initial_bp': 450, 'index_path': [250, 700, 212, 700, 650], 'margin_bp': 250, 'round_to': 25, 'initial_cap_bp': 200, 'periodic_cap_bp': 100, 'lifetime_cap_bp': 500, 'floor_bp': 225}, [500, 600, 500, 600, 700]], ['control 4', {'initial_bp': 300, 'index_path': [250], 'margin_bp': 225, 'round_to': 125, 'initial_cap_bp': 200, 'periodic_cap_bp': 200, 'lifetime_cap_bp': 600, 'floor_bp': 300}, [500]], ['control 5', {'initial_bp': 300, 'index_path': [50, 537, 250, 100], 'margin_bp': 225, 'round_to': 25, 'initial_cap_bp': 500, 'periodic_cap_bp': 100, 'lifetime_cap_bp': 600, 'floor_bp': 300}, [300, 400, 475, 375]]]]
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: rate increment rounding | [1000, 800, 1000, 800, 600] | [875, 675, 875, 675, 475] | Failed |
| regression: rate increment rounding, partial-repair probe | [400, 350, 550] | [400, 350, 550] | Passed |
| control 1 | [500, 400, 400, 500, 600] | [500, 400, 400, 500, 600] | Passed |
| control 2 | [475, 575, 475, 575, 675] | [475, 575, 475, 575, 675] | Passed |
| control 3 | [875] | [875] | Passed |
| control 4 | [375, 475, 575, 675] | [375, 475, 575, 675] | Passed |
| control 5 | [500, 500, 400] | [500, 500, 400] | Passed |
SHA-256 / 0cd8917d85761deee7a7e5c8eb4cc0fd003eec7b9c5743cc5952d0c6a23e1589
2 / The unsuccessful fix
Exit 1"""Failure Map reference implementation. Python standard library only."""
import json
N = 1
observations = []
def solve(x):
rate = x['initial_bp']
out = []
life_max = x['initial_bp'] + x['lifetime_cap_bp']
for i, idx in enumerate(x['index_path']):
fi = idx + x['margin_bp']
q, r = divmod(fi, x['round_to'])
fi = (q + (1 if 2 * r > x['round_to'] else 0)) * x['round_to']
cap = x['initial_cap_bp'] if i == 0 else x['periodic_cap_bp']
new = max(rate - cap, min(fi, rate + cap))
new = max(x['floor_bp'], min(new, life_max))
out.append(new)
rate = new
return out
def check(label, actual, expected):
observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
fixtures = [[['regression: rate increment rounding', {'initial_bp': 500, 'index_path': [700, 50, 700, 50, 100], 'margin_bp': 225, 'round_to': 125, 'initial_cap_bp': 500, 'periodic_cap_bp': 200, 'lifetime_cap_bp': 500, 'floor_bp': 300}, [875, 675, 875, 675, 475]], ['regression: rate increment rounding, partial-repair probe', {'initial_bp': 300, 'index_path': [100, 50, 250], 'margin_bp': 275, 'round_to': 50, 'initial_cap_bp': 500, 'periodic_cap_bp': 200, 'lifetime_cap_bp': 600, 'floor_bp': 300}, [400, 350, 550]], ['control 1', {'initial_bp': 500, 'index_path': [212, 100, 50, 250, 700], 'margin_bp': 275, 'round_to': 125, 'initial_cap_bp': 200, 'periodic_cap_bp': 100, 'lifetime_cap_bp': 500, 'floor_bp': 400}, [500, 400, 400, 500, 600]], ['control 2', {'initial_bp': 450, 'index_path': [250, 650, 100, 650, 650], 'margin_bp': 225, 'round_to': 25, 'initial_cap_bp': 200, 'periodic_cap_bp': 100, 'lifetime_cap_bp': 600, 'floor_bp': 225}, [475, 575, 475, 575, 675]], ['control 3', {'initial_bp': 500, 'index_path': [650], 'margin_bp': 225, 'round_to': 25, 'initial_cap_bp': 500, 'periodic_cap_bp': 200, 'lifetime_cap_bp': 600, 'floor_bp': 225}, [875]], ['control 4', {'initial_bp': 450, 'index_path': [50, 537, 400, 650], 'margin_bp': 275, 'round_to': 125, 'initial_cap_bp': 500, 'periodic_cap_bp': 100, 'lifetime_cap_bp': 600, 'floor_bp': 300}, [375, 475, 575, 675]], ['control 5', {'initial_bp': 300, 'index_path': [650, 212, 100], 'margin_bp': 275, 'round_to': 50, 'initial_cap_bp': 200, 'periodic_cap_bp': 100, 'lifetime_cap_bp': 600, 'floor_bp': 400}, [500, 500, 400]]], [['regression: rate increment rounding', {'initial_bp': 500, 'index_path': [537, 650, 250, 400, 537], 'margin_bp': 225, 'round_to': 25, 'initial_cap_bp': 200, 'periodic_cap_bp': 200, 'lifetime_cap_bp': 600, 'floor_bp': 400}, [700, 875, 675, 625, 750]], ['regression: rate increment rounding, partial-repair probe', {'initial_bp': 500, 'index_path': [50], 'margin_bp': 275, 'round_to': 50, 'initial_cap_bp': 500, 'periodic_cap_bp': 100, 'lifetime_cap_bp': 600, 'floor_bp': 225}, [350]], ['control 1', {'initial_bp': 450, 'index_path': [650, 50, 212], 'margin_bp': 275, 'round_to': 125, 'initial_cap_bp': 200, 'periodic_cap_bp': 100, 'lifetime_cap_bp': 600, 'floor_bp': 225}, [650, 550, 500]], ['control 2', {'initial_bp': 300, 'index_path': [650, 50, 700], 'margin_bp': 225, 'round_to': 125, 'initial_cap_bp': 500, 'periodic_cap_bp': 200, 'lifetime_cap_bp': 600, 'floor_bp': 400}, [800, 600, 800]], ['control 3', {'initial_bp': 450, 'index_path': [250, 100, 700], 'margin_bp': 225, 'round_to': 25, 'initial_cap_bp': 200, 'periodic_cap_bp': 200, 'lifetime_cap_bp': 600, 'floor_bp': 225}, [475, 325, 525]], ['control 4', {'initial_bp': 500, 'index_path': [250, 400, 50, 700, 400], 'margin_bp': 225, 'round_to': 25, 'initial_cap_bp': 500, 'periodic_cap_bp': 100, 'lifetime_cap_bp': 600, 'floor_bp': 225}, [475, 575, 475, 575, 625]], ['control 5', {'initial_bp': 300, 'index_path': [250], 'margin_bp': 275, 'round_to': 25, 'initial_cap_bp': 500, 'periodic_cap_bp': 200, 'lifetime_cap_bp': 500, 'floor_bp': 400}, [525]]], [['regression: rate increment rounding', {'initial_bp': 300, 'index_path': [537, 700, 250], 'margin_bp': 250, 'round_to': 125, 'initial_cap_bp': 500, 'periodic_cap_bp': 100, 'lifetime_cap_bp': 600, 'floor_bp': 225}, [750, 850, 750]], ['regression: rate increment rounding, partial-repair probe', {'initial_bp': 450, 'index_path': [250, 250, 212], 'margin_bp': 225, 'round_to': 50, 'initial_cap_bp': 500, 'periodic_cap_bp': 200, 'lifetime_cap_bp': 600, 'floor_bp': 225}, [500, 500, 450]], ['control 1', {'initial_bp': 450, 'index_path': [212, 650, 50, 650], 'margin_bp': 250, 'round_to': 125, 'initial_cap_bp': 200, 'periodic_cap_bp': 200, 'lifetime_cap_bp': 500, 'floor_bp': 300}, [500, 700, 500, 700]], ['control 2', {'initial_bp': 450, 'index_path': [537, 50, 537], 'margin_bp': 275, 'round_to': 125, 'initial_cap_bp': 200, 'periodic_cap_bp': 100, 'lifetime_cap_bp': 500, 'floor_bp': 300}, [650, 550, 650]], ['control 3', {'initial_bp': 450, 'index_path': [100], 'margin_bp': 225, 'round_to': 25, 'initial_cap_bp': 500, 'periodic_cap_bp': 200, 'lifetime_cap_bp': 500, 'floor_bp': 400}, [400]], ['control 4', {'initial_bp': 500, 'index_path': [700, 250], 'margin_bp': 275, 'round_to': 125, 'initial_cap_bp': 500, 'periodic_cap_bp': 100, 'lifetime_cap_bp': 500, 'floor_bp': 400}, [1000, 900]], ['control 5', {'initial_bp': 500, 'index_path': [212, 537], 'margin_bp': 275, 'round_to': 50, 'initial_cap_bp': 500, 'periodic_cap_bp': 200, 'lifetime_cap_bp': 500, 'floor_bp': 400}, [500, 700]]], [['regression: rate increment rounding', {'initial_bp': 300, 'index_path': [650, 250, 50, 537, 50], 'margin_bp': 275, 'round_to': 125, 'initial_cap_bp': 200, 'periodic_cap_bp': 200, 'lifetime_cap_bp': 600, 'floor_bp': 225}, [500, 500, 375, 575, 375]], ['regression: rate increment rounding, partial-repair probe', {'initial_bp': 450, 'index_path': [650, 212, 537, 50], 'margin_bp': 225, 'round_to': 50, 'initial_cap_bp': 500, 'periodic_cap_bp': 100, 'lifetime_cap_bp': 600, 'floor_bp': 400}, [900, 800, 750, 650]], ['control 1', {'initial_bp': 300, 'index_path': [400, 400, 537], 'margin_bp': 275, 'round_to': 25, 'initial_cap_bp': 200, 'periodic_cap_bp': 200, 'lifetime_cap_bp': 500, 'floor_bp': 400}, [500, 675, 800]], ['control 2', {'initial_bp': 500, 'index_path': [537], 'margin_bp': 250, 'round_to': 25, 'initial_cap_bp': 200, 'periodic_cap_bp': 200, 'lifetime_cap_bp': 600, 'floor_bp': 225}, [700]], ['control 3', {'initial_bp': 300, 'index_path': [250, 250, 250], 'margin_bp': 250, 'round_to': 50, 'initial_cap_bp': 500, 'periodic_cap_bp': 100, 'lifetime_cap_bp': 600, 'floor_bp': 300}, [500, 500, 500]], ['control 4', {'initial_bp': 300, 'index_path': [400, 100, 400, 250, 50], 'margin_bp': 275, 'round_to': 25, 'initial_cap_bp': 200, 'periodic_cap_bp': 100, 'lifetime_cap_bp': 600, 'floor_bp': 400}, [500, 400, 500, 525, 425]], ['control 5', {'initial_bp': 300, 'index_path': [700, 212], 'margin_bp': 275, 'round_to': 25, 'initial_cap_bp': 500, 'periodic_cap_bp': 100, 'lifetime_cap_bp': 500, 'floor_bp': 225}, [800, 700]]], [['regression: rate increment rounding', {'initial_bp': 450, 'index_path': [650], 'margin_bp': 275, 'round_to': 125, 'initial_cap_bp': 500, 'periodic_cap_bp': 200, 'lifetime_cap_bp': 500, 'floor_bp': 300}, [875]], ['regression: rate increment rounding, partial-repair probe', {'initial_bp': 500, 'index_path': [250, 537], 'margin_bp': 225, 'round_to': 50, 'initial_cap_bp': 200, 'periodic_cap_bp': 100, 'lifetime_cap_bp': 600, 'floor_bp': 300}, [500, 600]], ['control 1', {'initial_bp': 300, 'index_path': [400, 537, 100], 'margin_bp': 275, 'round_to': 125, 'initial_cap_bp': 200, 'periodic_cap_bp': 200, 'lifetime_cap_bp': 600, 'floor_bp': 400}, [500, 700, 500]], ['control 2', {'initial_bp': 500, 'index_path': [400, 212], 'margin_bp': 250, 'round_to': 25, 'initial_cap_bp': 500, 'periodic_cap_bp': 100, 'lifetime_cap_bp': 600, 'floor_bp': 300}, [650, 550]], ['control 3', {'initial_bp': 450, 'index_path': [250, 700, 212, 700, 650], 'margin_bp': 250, 'round_to': 25, 'initial_cap_bp': 200, 'periodic_cap_bp': 100, 'lifetime_cap_bp': 500, 'floor_bp': 225}, [500, 600, 500, 600, 700]], ['control 4', {'initial_bp': 300, 'index_path': [250], 'margin_bp': 225, 'round_to': 125, 'initial_cap_bp': 200, 'periodic_cap_bp': 200, 'lifetime_cap_bp': 600, 'floor_bp': 300}, [500]], ['control 5', {'initial_bp': 300, 'index_path': [50, 537, 250, 100], 'margin_bp': 225, 'round_to': 25, 'initial_cap_bp': 500, 'periodic_cap_bp': 100, 'lifetime_cap_bp': 600, 'floor_bp': 300}, [300, 400, 475, 375]]]]
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: rate increment rounding | [875, 675, 875, 675, 475] | [875, 675, 875, 675, 475] | Passed |
| regression: rate increment rounding, partial-repair probe | [350, 300, 500] | [400, 350, 550] | Failed |
| control 1 | [500, 400, 400, 500, 600] | [500, 400, 400, 500, 600] | Passed |
| control 2 | [475, 575, 475, 575, 675] | [475, 575, 475, 575, 675] | Passed |
| control 3 | [875] | [875] | Passed |
| control 4 | [375, 475, 575, 675] | [375, 475, 575, 675] | Passed |
| control 5 | [500, 500, 400] | [500, 500, 400] | Passed |
SHA-256 / 35fcebe13b351dcd5c1e93fdcf352e0e24f2c97c2c59dd97cf7f5d15618a5ac1
3 / The verified repair
Exit 0"""Failure Map reference implementation. Python standard library only."""
import json
N = 1
observations = []
def solve(x):
rate = x['initial_bp']
out = []
life_max = x['initial_bp'] + x['lifetime_cap_bp']
for i, idx in enumerate(x['index_path']):
fi = idx + x['margin_bp']
q, r = divmod(fi, x['round_to'])
fi = (q + (1 if 2 * r >= x['round_to'] else 0)) * x['round_to']
cap = x['initial_cap_bp'] if i == 0 else x['periodic_cap_bp']
new = max(rate - cap, min(fi, rate + cap))
new = max(x['floor_bp'], min(new, life_max))
out.append(new)
rate = new
return out
def check(label, actual, expected):
observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
fixtures = [[['regression: rate increment rounding', {'initial_bp': 500, 'index_path': [700, 50, 700, 50, 100], 'margin_bp': 225, 'round_to': 125, 'initial_cap_bp': 500, 'periodic_cap_bp': 200, 'lifetime_cap_bp': 500, 'floor_bp': 300}, [875, 675, 875, 675, 475]], ['regression: rate increment rounding, partial-repair probe', {'initial_bp': 300, 'index_path': [100, 50, 250], 'margin_bp': 275, 'round_to': 50, 'initial_cap_bp': 500, 'periodic_cap_bp': 200, 'lifetime_cap_bp': 600, 'floor_bp': 300}, [400, 350, 550]], ['control 1', {'initial_bp': 500, 'index_path': [212, 100, 50, 250, 700], 'margin_bp': 275, 'round_to': 125, 'initial_cap_bp': 200, 'periodic_cap_bp': 100, 'lifetime_cap_bp': 500, 'floor_bp': 400}, [500, 400, 400, 500, 600]], ['control 2', {'initial_bp': 450, 'index_path': [250, 650, 100, 650, 650], 'margin_bp': 225, 'round_to': 25, 'initial_cap_bp': 200, 'periodic_cap_bp': 100, 'lifetime_cap_bp': 600, 'floor_bp': 225}, [475, 575, 475, 575, 675]], ['control 3', {'initial_bp': 500, 'index_path': [650], 'margin_bp': 225, 'round_to': 25, 'initial_cap_bp': 500, 'periodic_cap_bp': 200, 'lifetime_cap_bp': 600, 'floor_bp': 225}, [875]], ['control 4', {'initial_bp': 450, 'index_path': [50, 537, 400, 650], 'margin_bp': 275, 'round_to': 125, 'initial_cap_bp': 500, 'periodic_cap_bp': 100, 'lifetime_cap_bp': 600, 'floor_bp': 300}, [375, 475, 575, 675]], ['control 5', {'initial_bp': 300, 'index_path': [650, 212, 100], 'margin_bp': 275, 'round_to': 50, 'initial_cap_bp': 200, 'periodic_cap_bp': 100, 'lifetime_cap_bp': 600, 'floor_bp': 400}, [500, 500, 400]]], [['regression: rate increment rounding', {'initial_bp': 500, 'index_path': [537, 650, 250, 400, 537], 'margin_bp': 225, 'round_to': 25, 'initial_cap_bp': 200, 'periodic_cap_bp': 200, 'lifetime_cap_bp': 600, 'floor_bp': 400}, [700, 875, 675, 625, 750]], ['regression: rate increment rounding, partial-repair probe', {'initial_bp': 500, 'index_path': [50], 'margin_bp': 275, 'round_to': 50, 'initial_cap_bp': 500, 'periodic_cap_bp': 100, 'lifetime_cap_bp': 600, 'floor_bp': 225}, [350]], ['control 1', {'initial_bp': 450, 'index_path': [650, 50, 212], 'margin_bp': 275, 'round_to': 125, 'initial_cap_bp': 200, 'periodic_cap_bp': 100, 'lifetime_cap_bp': 600, 'floor_bp': 225}, [650, 550, 500]], ['control 2', {'initial_bp': 300, 'index_path': [650, 50, 700], 'margin_bp': 225, 'round_to': 125, 'initial_cap_bp': 500, 'periodic_cap_bp': 200, 'lifetime_cap_bp': 600, 'floor_bp': 400}, [800, 600, 800]], ['control 3', {'initial_bp': 450, 'index_path': [250, 100, 700], 'margin_bp': 225, 'round_to': 25, 'initial_cap_bp': 200, 'periodic_cap_bp': 200, 'lifetime_cap_bp': 600, 'floor_bp': 225}, [475, 325, 525]], ['control 4', {'initial_bp': 500, 'index_path': [250, 400, 50, 700, 400], 'margin_bp': 225, 'round_to': 25, 'initial_cap_bp': 500, 'periodic_cap_bp': 100, 'lifetime_cap_bp': 600, 'floor_bp': 225}, [475, 575, 475, 575, 625]], ['control 5', {'initial_bp': 300, 'index_path': [250], 'margin_bp': 275, 'round_to': 25, 'initial_cap_bp': 500, 'periodic_cap_bp': 200, 'lifetime_cap_bp': 500, 'floor_bp': 400}, [525]]], [['regression: rate increment rounding', {'initial_bp': 300, 'index_path': [537, 700, 250], 'margin_bp': 250, 'round_to': 125, 'initial_cap_bp': 500, 'periodic_cap_bp': 100, 'lifetime_cap_bp': 600, 'floor_bp': 225}, [750, 850, 750]], ['regression: rate increment rounding, partial-repair probe', {'initial_bp': 450, 'index_path': [250, 250, 212], 'margin_bp': 225, 'round_to': 50, 'initial_cap_bp': 500, 'periodic_cap_bp': 200, 'lifetime_cap_bp': 600, 'floor_bp': 225}, [500, 500, 450]], ['control 1', {'initial_bp': 450, 'index_path': [212, 650, 50, 650], 'margin_bp': 250, 'round_to': 125, 'initial_cap_bp': 200, 'periodic_cap_bp': 200, 'lifetime_cap_bp': 500, 'floor_bp': 300}, [500, 700, 500, 700]], ['control 2', {'initial_bp': 450, 'index_path': [537, 50, 537], 'margin_bp': 275, 'round_to': 125, 'initial_cap_bp': 200, 'periodic_cap_bp': 100, 'lifetime_cap_bp': 500, 'floor_bp': 300}, [650, 550, 650]], ['control 3', {'initial_bp': 450, 'index_path': [100], 'margin_bp': 225, 'round_to': 25, 'initial_cap_bp': 500, 'periodic_cap_bp': 200, 'lifetime_cap_bp': 500, 'floor_bp': 400}, [400]], ['control 4', {'initial_bp': 500, 'index_path': [700, 250], 'margin_bp': 275, 'round_to': 125, 'initial_cap_bp': 500, 'periodic_cap_bp': 100, 'lifetime_cap_bp': 500, 'floor_bp': 400}, [1000, 900]], ['control 5', {'initial_bp': 500, 'index_path': [212, 537], 'margin_bp': 275, 'round_to': 50, 'initial_cap_bp': 500, 'periodic_cap_bp': 200, 'lifetime_cap_bp': 500, 'floor_bp': 400}, [500, 700]]], [['regression: rate increment rounding', {'initial_bp': 300, 'index_path': [650, 250, 50, 537, 50], 'margin_bp': 275, 'round_to': 125, 'initial_cap_bp': 200, 'periodic_cap_bp': 200, 'lifetime_cap_bp': 600, 'floor_bp': 225}, [500, 500, 375, 575, 375]], ['regression: rate increment rounding, partial-repair probe', {'initial_bp': 450, 'index_path': [650, 212, 537, 50], 'margin_bp': 225, 'round_to': 50, 'initial_cap_bp': 500, 'periodic_cap_bp': 100, 'lifetime_cap_bp': 600, 'floor_bp': 400}, [900, 800, 750, 650]], ['control 1', {'initial_bp': 300, 'index_path': [400, 400, 537], 'margin_bp': 275, 'round_to': 25, 'initial_cap_bp': 200, 'periodic_cap_bp': 200, 'lifetime_cap_bp': 500, 'floor_bp': 400}, [500, 675, 800]], ['control 2', {'initial_bp': 500, 'index_path': [537], 'margin_bp': 250, 'round_to': 25, 'initial_cap_bp': 200, 'periodic_cap_bp': 200, 'lifetime_cap_bp': 600, 'floor_bp': 225}, [700]], ['control 3', {'initial_bp': 300, 'index_path': [250, 250, 250], 'margin_bp': 250, 'round_to': 50, 'initial_cap_bp': 500, 'periodic_cap_bp': 100, 'lifetime_cap_bp': 600, 'floor_bp': 300}, [500, 500, 500]], ['control 4', {'initial_bp': 300, 'index_path': [400, 100, 400, 250, 50], 'margin_bp': 275, 'round_to': 25, 'initial_cap_bp': 200, 'periodic_cap_bp': 100, 'lifetime_cap_bp': 600, 'floor_bp': 400}, [500, 400, 500, 525, 425]], ['control 5', {'initial_bp': 300, 'index_path': [700, 212], 'margin_bp': 275, 'round_to': 25, 'initial_cap_bp': 500, 'periodic_cap_bp': 100, 'lifetime_cap_bp': 500, 'floor_bp': 225}, [800, 700]]], [['regression: rate increment rounding', {'initial_bp': 450, 'index_path': [650], 'margin_bp': 275, 'round_to': 125, 'initial_cap_bp': 500, 'periodic_cap_bp': 200, 'lifetime_cap_bp': 500, 'floor_bp': 300}, [875]], ['regression: rate increment rounding, partial-repair probe', {'initial_bp': 500, 'index_path': [250, 537], 'margin_bp': 225, 'round_to': 50, 'initial_cap_bp': 200, 'periodic_cap_bp': 100, 'lifetime_cap_bp': 600, 'floor_bp': 300}, [500, 600]], ['control 1', {'initial_bp': 300, 'index_path': [400, 537, 100], 'margin_bp': 275, 'round_to': 125, 'initial_cap_bp': 200, 'periodic_cap_bp': 200, 'lifetime_cap_bp': 600, 'floor_bp': 400}, [500, 700, 500]], ['control 2', {'initial_bp': 500, 'index_path': [400, 212], 'margin_bp': 250, 'round_to': 25, 'initial_cap_bp': 500, 'periodic_cap_bp': 100, 'lifetime_cap_bp': 600, 'floor_bp': 300}, [650, 550]], ['control 3', {'initial_bp': 450, 'index_path': [250, 700, 212, 700, 650], 'margin_bp': 250, 'round_to': 25, 'initial_cap_bp': 200, 'periodic_cap_bp': 100, 'lifetime_cap_bp': 500, 'floor_bp': 225}, [500, 600, 500, 600, 700]], ['control 4', {'initial_bp': 300, 'index_path': [250], 'margin_bp': 225, 'round_to': 125, 'initial_cap_bp': 200, 'periodic_cap_bp': 200, 'lifetime_cap_bp': 600, 'floor_bp': 300}, [500]], ['control 5', {'initial_bp': 300, 'index_path': [50, 537, 250, 100], 'margin_bp': 225, 'round_to': 25, 'initial_cap_bp': 500, 'periodic_cap_bp': 100, 'lifetime_cap_bp': 600, 'floor_bp': 300}, [300, 400, 475, 375]]]]
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: rate increment rounding | [875, 675, 875, 675, 475] | [875, 675, 875, 675, 475] | Passed |
| regression: rate increment rounding, partial-repair probe | [400, 350, 550] | [400, 350, 550] | Passed |
| control 1 | [500, 400, 400, 500, 600] | [500, 400, 400, 500, 600] | Passed |
| control 2 | [475, 575, 475, 575, 675] | [475, 575, 475, 575, 675] | Passed |
| control 3 | [875] | [875] | Passed |
| control 4 | [375, 475, 575, 675] | [375, 475, 575, 675] | Passed |
| control 5 | [500, 500, 400] | [500, 500, 400] | Passed |
SHA-256 / 67b035ab24cffdc07f6e26e1dfccc837ebbd981d36d4bcd2e10d3b9588af2461
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:27.868279+00:00.
Case digest / 3e3fa6bf640f24335d81c1b3084d9597ef5f58e8b75e3285edf4228fdf803ac0