{"abstract":"Fully indexed rates are always rounded up to the next increment.","category":"Loan amortization schedules","checks":7,"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.","evaluation_group":"w2-loan-amortization-schedules-adjustable-rate-reset-caps","failed_approach":"Strict comparison rounds exact ties down.","family":"w2-loan-amortization-schedules-adjustable-rate-reset-caps-rate-increment-rounding","id":"FA-58556","implementations":{"attempt":{"sha256":"35fcebe13b351dcd5c1e93fdcf352e0e24f2c97c2c59dd97cf7f5d15618a5ac1","source":"\"\"\"Failure Map reference implementation. Python standard library only.\"\"\"\nimport json\n\nN = 1\nobservations = []\ndef solve(x):\n    rate = x['initial_bp']\n    out = []\n    life_max = x['initial_bp'] + x['lifetime_cap_bp']\n    for i, idx in enumerate(x['index_path']):\n        fi = idx + x['margin_bp']\n        q, r = divmod(fi, x['round_to'])\n        fi = (q + (1 if 2 * r > x['round_to'] else 0)) * x['round_to']\n        cap = x['initial_cap_bp'] if i == 0 else x['periodic_cap_bp']\n        new = max(rate - cap, min(fi, rate + cap))\n        new = max(x['floor_bp'], min(new, life_max))\n        out.append(new)\n        rate = new\n    return out\ndef check(label, actual, expected):\n    observations.append({\"check\": label, \"actual\": actual, \"expected\": expected, \"passed\": actual == expected})\nfixtures = [[['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]]]]\nfor label, args, expected in fixtures[N-1]:\n    try:\n        actual = solve(args)\n    except Exception as exc:\n        actual = 'raised ' + type(exc).__name__\n    check(label, actual, expected)\nprint(json.dumps({\"observations\": observations, \"passed\": all(x[\"passed\"] for x in observations)}, ensure_ascii=False))\nraise SystemExit(0 if all(x[\"passed\"] for x in observations) else 1)\n"},"broken":{"sha256":"0cd8917d85761deee7a7e5c8eb4cc0fd003eec7b9c5743cc5952d0c6a23e1589","source":"\"\"\"Failure Map reference implementation. Python standard library only.\"\"\"\nimport json\n\nN = 1\nobservations = []\ndef solve(x):\n    rate = x['initial_bp']\n    out = []\n    life_max = x['initial_bp'] + x['lifetime_cap_bp']\n    for i, idx in enumerate(x['index_path']):\n        fi = idx + x['margin_bp']\n        q, r = divmod(fi, x['round_to'])\n        fi = (q + (1 if r else 0)) * x['round_to']\n        cap = x['initial_cap_bp'] if i == 0 else x['periodic_cap_bp']\n        new = max(rate - cap, min(fi, rate + cap))\n        new = max(x['floor_bp'], min(new, life_max))\n        out.append(new)\n        rate = new\n    return out\ndef check(label, actual, expected):\n    observations.append({\"check\": label, \"actual\": actual, \"expected\": expected, \"passed\": actual == expected})\nfixtures = [[['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]]]]\nfor label, args, expected in fixtures[N-1]:\n    try:\n        actual = solve(args)\n    except Exception as exc:\n        actual = 'raised ' + type(exc).__name__\n    check(label, actual, expected)\nprint(json.dumps({\"observations\": observations, \"passed\": all(x[\"passed\"] for x in observations)}, ensure_ascii=False))\nraise SystemExit(0 if all(x[\"passed\"] for x in observations) else 1)\n"},"fixed":{"sha256":"67b035ab24cffdc07f6e26e1dfccc837ebbd981d36d4bcd2e10d3b9588af2461","source":"\"\"\"Failure Map reference implementation. Python standard library only.\"\"\"\nimport json\n\nN = 1\nobservations = []\ndef solve(x):\n    rate = x['initial_bp']\n    out = []\n    life_max = x['initial_bp'] + x['lifetime_cap_bp']\n    for i, idx in enumerate(x['index_path']):\n        fi = idx + x['margin_bp']\n        q, r = divmod(fi, x['round_to'])\n        fi = (q + (1 if 2 * r >= x['round_to'] else 0)) * x['round_to']\n        cap = x['initial_cap_bp'] if i == 0 else x['periodic_cap_bp']\n        new = max(rate - cap, min(fi, rate + cap))\n        new = max(x['floor_bp'], min(new, life_max))\n        out.append(new)\n        rate = new\n    return out\ndef check(label, actual, expected):\n    observations.append({\"check\": label, \"actual\": actual, \"expected\": expected, \"passed\": actual == expected})\nfixtures = [[['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]]]]\nfor label, args, expected in fixtures[N-1]:\n    try:\n        actual = solve(args)\n    except Exception as exc:\n        actual = 'raised ' + type(exc).__name__\n    check(label, actual, expected)\nprint(json.dumps({\"observations\": observations, \"passed\": all(x[\"passed\"] for x in observations)}, ensure_ascii=False))\nraise SystemExit(0 if all(x[\"passed\"] for x in observations) else 1)\n"}},"limitations":"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.","method":"Deterministic executable model with adversarial boundary fixtures.","provenance":{"created_by":"Failure Map","dependencies":"Python standard library","family":"w2-loan-amortization-schedules-adjustable-rate-reset-caps-rate-increment-rounding","generated_at":"2026-09-29T14:46:27.868279+00:00","license":"CC0-1.0","python":"3.12.14","seed":1,"split":"open-access"},"relevance":"Amortization engines drive borrower statements, payoff quotes and investor remittances; a misplaced rounding step, boundary or ordering rule compounds across hundreds of periods.","repair":"Round to the nearest increment, ties up.","root_cause":"Any remainder rounds up instead of half-up.","sha256":"3e3fa6bf640f24335d81c1b3084d9597ef5f58e8b75e3285edf4228fdf803ac0","title":"Adjustable rate reset with caps: rate increment rounding · case 01","variant":1,"variant_policy":"Five numbered records share a model and may reuse boundary fixtures.","verification":{"attempt":{"elapsed_ms":40.916,"exit_code":1,"observations":[{"actual":[875,675,875,675,475],"check":"regression: rate increment rounding","expected":[875,675,875,675,475],"passed":true},{"actual":[350,300,500],"check":"regression: rate increment rounding, partial-repair probe","expected":[400,350,550],"passed":false},{"actual":[500,400,400,500,600],"check":"control 1","expected":[500,400,400,500,600],"passed":true},{"actual":[475,575,475,575,675],"check":"control 2","expected":[475,575,475,575,675],"passed":true},{"actual":[875],"check":"control 3","expected":[875],"passed":true},{"actual":[375,475,575,675],"check":"control 4","expected":[375,475,575,675],"passed":true},{"actual":[500,500,400],"check":"control 5","expected":[500,500,400],"passed":true}],"passed":false,"stderr":"","stdout":"{\"observations\": [{\"check\": \"regression: rate increment rounding\", \"actual\": [875, 675, 875, 675, 475], \"expected\": [875, 675, 875, 675, 475], \"passed\": true}, {\"check\": \"regression: rate increment rounding, partial-repair probe\", \"actual\": [350, 300, 500], \"expected\": [400, 350, 550], \"passed\": false}, {\"check\": \"control 1\", \"actual\": [500, 400, 400, 500, 600], \"expected\": [500, 400, 400, 500, 600], \"passed\": true}, {\"check\": \"control 2\", \"actual\": [475, 575, 475, 575, 675], \"expected\": [475, 575, 475, 575, 675], \"passed\": true}, {\"check\": \"control 3\", \"actual\": [875], \"expected\": [875], \"passed\": true}, {\"check\": \"control 4\", \"actual\": [375, 475, 575, 675], \"expected\": [375, 475, 575, 675], \"passed\": true}, {\"check\": \"control 5\", \"actual\": [500, 500, 400], \"expected\": [500, 500, 400], \"passed\": true}], \"passed\": false}\n"},"broken":{"elapsed_ms":39.977,"exit_code":1,"observations":[{"actual":[1000,800,1000,800,600],"check":"regression: rate increment rounding","expected":[875,675,875,675,475],"passed":false},{"actual":[400,350,550],"check":"regression: rate increment rounding, partial-repair probe","expected":[400,350,550],"passed":true},{"actual":[500,400,400,500,600],"check":"control 1","expected":[500,400,400,500,600],"passed":true},{"actual":[475,575,475,575,675],"check":"control 2","expected":[475,575,475,575,675],"passed":true},{"actual":[875],"check":"control 3","expected":[875],"passed":true},{"actual":[375,475,575,675],"check":"control 4","expected":[375,475,575,675],"passed":true},{"actual":[500,500,400],"check":"control 5","expected":[500,500,400],"passed":true}],"passed":false,"stderr":"","stdout":"{\"observations\": [{\"check\": \"regression: rate increment rounding\", \"actual\": [1000, 800, 1000, 800, 600], \"expected\": [875, 675, 875, 675, 475], \"passed\": false}, {\"check\": \"regression: rate increment rounding, partial-repair probe\", \"actual\": [400, 350, 550], \"expected\": [400, 350, 550], \"passed\": true}, {\"check\": \"control 1\", \"actual\": [500, 400, 400, 500, 600], \"expected\": [500, 400, 400, 500, 600], \"passed\": true}, {\"check\": \"control 2\", \"actual\": [475, 575, 475, 575, 675], \"expected\": [475, 575, 475, 575, 675], \"passed\": true}, {\"check\": \"control 3\", \"actual\": [875], \"expected\": [875], \"passed\": true}, {\"check\": \"control 4\", \"actual\": [375, 475, 575, 675], \"expected\": [375, 475, 575, 675], \"passed\": true}, {\"check\": \"control 5\", \"actual\": [500, 500, 400], \"expected\": [500, 500, 400], \"passed\": true}], \"passed\": false}\n"},"fixed":{"elapsed_ms":40.11,"exit_code":0,"observations":[{"actual":[875,675,875,675,475],"check":"regression: rate increment rounding","expected":[875,675,875,675,475],"passed":true},{"actual":[400,350,550],"check":"regression: rate increment rounding, partial-repair probe","expected":[400,350,550],"passed":true},{"actual":[500,400,400,500,600],"check":"control 1","expected":[500,400,400,500,600],"passed":true},{"actual":[475,575,475,575,675],"check":"control 2","expected":[475,575,475,575,675],"passed":true},{"actual":[875],"check":"control 3","expected":[875],"passed":true},{"actual":[375,475,575,675],"check":"control 4","expected":[375,475,575,675],"passed":true},{"actual":[500,500,400],"check":"control 5","expected":[500,500,400],"passed":true}],"passed":true,"stderr":"","stdout":"{\"observations\": [{\"check\": \"regression: rate increment rounding\", \"actual\": [875, 675, 875, 675, 475], \"expected\": [875, 675, 875, 675, 475], \"passed\": true}, {\"check\": \"regression: rate increment rounding, partial-repair probe\", \"actual\": [400, 350, 550], \"expected\": [400, 350, 550], \"passed\": true}, {\"check\": \"control 1\", \"actual\": [500, 400, 400, 500, 600], \"expected\": [500, 400, 400, 500, 600], \"passed\": true}, {\"check\": \"control 2\", \"actual\": [475, 575, 475, 575, 675], \"expected\": [475, 575, 475, 575, 675], \"passed\": true}, {\"check\": \"control 3\", \"actual\": [875], \"expected\": [875], \"passed\": true}, {\"check\": \"control 4\", \"actual\": [375, 475, 575, 675], \"expected\": [375, 475, 575, 675], \"passed\": true}, {\"check\": \"control 5\", \"actual\": [500, 500, 400], \"expected\": [500, 500, 400], \"passed\": true}], \"passed\": true}\n"}},"verified":true,"visibility":"public"}