{"abstract":"Payoff quotes omit one remaining installment and its rebate.","category":"Loan amortization schedules","checks":7,"contract":"x = {'term' n, 'paid' k, 'finance_charge', 'acq_fee' (fully earned up front), 'payment', 'min_rebate'}. Rebatable charge = finance_charge - acq_fee; remaining r = n - k; rebate = round_half_up(rebatable * r(r+1) / (n(n+1))), set to 0 when below min_rebate. Payoff = payment*r - rebate; earned = finance_charge - rebate. Return {'rebate', 'payoff', 'earned'}.","evaluation_group":"w2-loan-amortization-schedules-rule-of-78-rebate","failed_approach":"Adding one counts an installment already paid.","family":"w2-loan-amortization-schedules-rule-of-78-rebate-remaining-installment-count","id":"FA-58636","implementations":{"attempt":{"sha256":"40e2dfde476059fbe327ed8ba6d23bcdcf4e4ade380bf2aaf5ae577d4290091f","source":"\"\"\"Failure Map reference implementation. Python standard library only.\"\"\"\nimport json\n\nN = 1\nobservations = []\ndef solve(x):\n    def rnd(n, d):\n        q, r = divmod(n, d)\n        return q + (1 if 2 * r >= d else 0)\n    n = x['term']\n    k = x['paid']\n    fc = x['finance_charge'] - x['acq_fee']\n    rem = n - k + 1\n    rebate = rnd(fc * rem * (rem + 1), n * (n + 1))\n    if rebate < x['min_rebate']:\n        rebate = 0\n    remaining = x['payment'] * rem\n    return {'rebate': rebate, 'payoff': remaining - rebate, 'earned': x['finance_charge'] - rebate}\ndef check(label, actual, expected):\n    observations.append({\"check\": label, \"actual\": actual, \"expected\": expected, \"passed\": actual == expected})\nfixtures = [[['regression: remaining installment count', {'term': 36, 'paid': 35, 'finance_charge': 120000, 'acq_fee': 0, 'payment': 10000, 'min_rebate': 100}, {'rebate': 180, 'payoff': 9820, 'earned': 119820}], ['sample 1', {'term': 36, 'paid': 35, 'finance_charge': 50000, 'acq_fee': 7500, 'payment': 25000, 'min_rebate': 100}, {'rebate': 0, 'payoff': 25000, 'earned': 50000}], ['sample 2', {'term': 24, 'paid': 23, 'finance_charge': 100, 'acq_fee': 0, 'payment': 25000, 'min_rebate': 100}, {'rebate': 0, 'payoff': 25000, 'earned': 100}], ['sample 3', {'term': 24, 'paid': 12, 'finance_charge': 50000, 'acq_fee': 0, 'payment': 25000, 'min_rebate': 100}, {'rebate': 13000, 'payoff': 287000, 'earned': 37000}], ['sample 4', {'term': 12, 'paid': 3, 'finance_charge': 100, 'acq_fee': 0, 'payment': 4321, 'min_rebate': 100}, {'rebate': 0, 'payoff': 38889, 'earned': 100}], ['sample 5', {'term': 6, 'paid': 3, 'finance_charge': 7777, 'acq_fee': 0, 'payment': 10000, 'min_rebate': 500}, {'rebate': 2222, 'payoff': 27778, 'earned': 5555}], ['sample 6', {'term': 24, 'paid': 1, 'finance_charge': 120000, 'acq_fee': 0, 'payment': 4321, 'min_rebate': 100}, {'rebate': 110400, 'payoff': -11017, 'earned': 9600}]], [['regression: remaining installment count', {'term': 36, 'paid': 3, 'finance_charge': 7777, 'acq_fee': 2500, 'payment': 10000, 'min_rebate': 500}, {'rebate': 4445, 'payoff': 325555, 'earned': 3332}], ['sample 1', {'term': 24, 'paid': 1, 'finance_charge': 120000, 'acq_fee': 0, 'payment': 25000, 'min_rebate': 500}, {'rebate': 110400, 'payoff': 464600, 'earned': 9600}], ['sample 2', {'term': 12, 'paid': 1, 'finance_charge': 100, 'acq_fee': 0, 'payment': 10000, 'min_rebate': 100}, {'rebate': 0, 'payoff': 110000, 'earned': 100}], ['sample 3', {'term': 24, 'paid': 24, 'finance_charge': 120000, 'acq_fee': 0, 'payment': 10000, 'min_rebate': 100}, {'rebate': 0, 'payoff': 0, 'earned': 120000}], ['sample 4', {'term': 36, 'paid': 0, 'finance_charge': 100, 'acq_fee': 7500, 'payment': 25000, 'min_rebate': 500}, {'rebate': 0, 'payoff': 900000, 'earned': 100}], ['sample 5', {'term': 24, 'paid': 12, 'finance_charge': 100, 'acq_fee': 2500, 'payment': 10000, 'min_rebate': 500}, {'rebate': 0, 'payoff': 120000, 'earned': 100}], ['sample 6', {'term': 6, 'paid': 0, 'finance_charge': 7777, 'acq_fee': 0, 'payment': 10000, 'min_rebate': 500}, {'rebate': 7777, 'payoff': 52223, 'earned': 0}]], [['regression: remaining installment count', {'term': 6, 'paid': 5, 'finance_charge': 100, 'acq_fee': 0, 'payment': 25000, 'min_rebate': 2000}, {'rebate': 0, 'payoff': 25000, 'earned': 100}], ['sample 1', {'term': 24, 'paid': 3, 'finance_charge': 120000, 'acq_fee': 0, 'payment': 10000, 'min_rebate': 2000}, {'rebate': 92400, 'payoff': 117600, 'earned': 27600}], ['sample 2', {'term': 12, 'paid': 3, 'finance_charge': 50000, 'acq_fee': 0, 'payment': 4321, 'min_rebate': 2000}, {'rebate': 28846, 'payoff': 10043, 'earned': 21154}], ['sample 3', {'term': 6, 'paid': 3, 'finance_charge': 7777, 'acq_fee': 7500, 'payment': 4321, 'min_rebate': 0}, {'rebate': 79, 'payoff': 12884, 'earned': 7698}], ['sample 4', {'term': 6, 'paid': 5, 'finance_charge': 120000, 'acq_fee': 0, 'payment': 10000, 'min_rebate': 0}, {'rebate': 5714, 'payoff': 4286, 'earned': 114286}], ['sample 5', {'term': 12, 'paid': 12, 'finance_charge': 7777, 'acq_fee': 7500, 'payment': 10000, 'min_rebate': 2000}, {'rebate': 0, 'payoff': 0, 'earned': 7777}], ['sample 6', {'term': 24, 'paid': 0, 'finance_charge': 7777, 'acq_fee': 2500, 'payment': 10000, 'min_rebate': 0}, {'rebate': 5277, 'payoff': 234723, 'earned': 2500}]], [['regression: remaining installment count', {'term': 36, 'paid': 0, 'finance_charge': 100, 'acq_fee': 7500, 'payment': 25000, 'min_rebate': 500}, {'rebate': 0, 'payoff': 900000, 'earned': 100}], ['sample 1', {'term': 12, 'paid': 12, 'finance_charge': 50000, 'acq_fee': 2500, 'payment': 10000, 'min_rebate': 0}, {'rebate': 0, 'payoff': 0, 'earned': 50000}], ['sample 2', {'term': 12, 'paid': 0, 'finance_charge': 7777, 'acq_fee': 0, 'payment': 4321, 'min_rebate': 2000}, {'rebate': 7777, 'payoff': 44075, 'earned': 0}], ['sample 3', {'term': 36, 'paid': 0, 'finance_charge': 100, 'acq_fee': 0, 'payment': 10000, 'min_rebate': 0}, {'rebate': 100, 'payoff': 359900, 'earned': 0}], ['sample 4', {'term': 6, 'paid': 3, 'finance_charge': 100, 'acq_fee': 0, 'payment': 25000, 'min_rebate': 2000}, {'rebate': 0, 'payoff': 75000, 'earned': 100}], ['sample 5', {'term': 24, 'paid': 12, 'finance_charge': 120000, 'acq_fee': 2500, 'payment': 4321, 'min_rebate': 100}, {'rebate': 30550, 'payoff': 21302, 'earned': 89450}], ['sample 6', {'term': 36, 'paid': 18, 'finance_charge': 50000, 'acq_fee': 0, 'payment': 4321, 'min_rebate': 0}, {'rebate': 12838, 'payoff': 64940, 'earned': 37162}]], [['regression: remaining installment count', {'term': 24, 'paid': 12, 'finance_charge': 120000, 'acq_fee': 7500, 'payment': 25000, 'min_rebate': 500}, {'rebate': 29250, 'payoff': 270750, 'earned': 90750}], ['sample 1', {'term': 24, 'paid': 12, 'finance_charge': 100, 'acq_fee': 0, 'payment': 4321, 'min_rebate': 500}, {'rebate': 0, 'payoff': 51852, 'earned': 100}], ['sample 2', {'term': 36, 'paid': 0, 'finance_charge': 7777, 'acq_fee': 7500, 'payment': 25000, 'min_rebate': 2000}, {'rebate': 0, 'payoff': 900000, 'earned': 7777}], ['sample 3', {'term': 6, 'paid': 5, 'finance_charge': 120000, 'acq_fee': 2500, 'payment': 10000, 'min_rebate': 100}, {'rebate': 5595, 'payoff': 4405, 'earned': 114405}], ['sample 4', {'term': 36, 'paid': 18, 'finance_charge': 7777, 'acq_fee': 2500, 'payment': 10000, 'min_rebate': 100}, {'rebate': 1355, 'payoff': 178645, 'earned': 6422}], ['sample 5', {'term': 36, 'paid': 36, 'finance_charge': 100, 'acq_fee': 7500, 'payment': 4321, 'min_rebate': 0}, {'rebate': 0, 'payoff': 0, 'earned': 100}], ['sample 6', {'term': 12, 'paid': 0, 'finance_charge': 100, 'acq_fee': 0, 'payment': 25000, 'min_rebate': 500}, {'rebate': 0, 'payoff': 300000, 'earned': 100}]]]\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":"300a207f5150bf9a5d9ba73e9f90984effd18bb880a9de71da5902335aca4234","source":"\"\"\"Failure Map reference implementation. Python standard library only.\"\"\"\nimport json\n\nN = 1\nobservations = []\ndef solve(x):\n    def rnd(n, d):\n        q, r = divmod(n, d)\n        return q + (1 if 2 * r >= d else 0)\n    n = x['term']\n    k = x['paid']\n    fc = x['finance_charge'] - x['acq_fee']\n    rem = n - k - 1\n    rebate = rnd(fc * rem * (rem + 1), n * (n + 1))\n    if rebate < x['min_rebate']:\n        rebate = 0\n    remaining = x['payment'] * rem\n    return {'rebate': rebate, 'payoff': remaining - rebate, 'earned': x['finance_charge'] - rebate}\ndef check(label, actual, expected):\n    observations.append({\"check\": label, \"actual\": actual, \"expected\": expected, \"passed\": actual == expected})\nfixtures = [[['regression: remaining installment count', {'term': 36, 'paid': 35, 'finance_charge': 120000, 'acq_fee': 0, 'payment': 10000, 'min_rebate': 100}, {'rebate': 180, 'payoff': 9820, 'earned': 119820}], ['sample 1', {'term': 36, 'paid': 35, 'finance_charge': 50000, 'acq_fee': 7500, 'payment': 25000, 'min_rebate': 100}, {'rebate': 0, 'payoff': 25000, 'earned': 50000}], ['sample 2', {'term': 24, 'paid': 23, 'finance_charge': 100, 'acq_fee': 0, 'payment': 25000, 'min_rebate': 100}, {'rebate': 0, 'payoff': 25000, 'earned': 100}], ['sample 3', {'term': 24, 'paid': 12, 'finance_charge': 50000, 'acq_fee': 0, 'payment': 25000, 'min_rebate': 100}, {'rebate': 13000, 'payoff': 287000, 'earned': 37000}], ['sample 4', {'term': 12, 'paid': 3, 'finance_charge': 100, 'acq_fee': 0, 'payment': 4321, 'min_rebate': 100}, {'rebate': 0, 'payoff': 38889, 'earned': 100}], ['sample 5', {'term': 6, 'paid': 3, 'finance_charge': 7777, 'acq_fee': 0, 'payment': 10000, 'min_rebate': 500}, {'rebate': 2222, 'payoff': 27778, 'earned': 5555}], ['sample 6', {'term': 24, 'paid': 1, 'finance_charge': 120000, 'acq_fee': 0, 'payment': 4321, 'min_rebate': 100}, {'rebate': 110400, 'payoff': -11017, 'earned': 9600}]], [['regression: remaining installment count', {'term': 36, 'paid': 3, 'finance_charge': 7777, 'acq_fee': 2500, 'payment': 10000, 'min_rebate': 500}, {'rebate': 4445, 'payoff': 325555, 'earned': 3332}], ['sample 1', {'term': 24, 'paid': 1, 'finance_charge': 120000, 'acq_fee': 0, 'payment': 25000, 'min_rebate': 500}, {'rebate': 110400, 'payoff': 464600, 'earned': 9600}], ['sample 2', {'term': 12, 'paid': 1, 'finance_charge': 100, 'acq_fee': 0, 'payment': 10000, 'min_rebate': 100}, {'rebate': 0, 'payoff': 110000, 'earned': 100}], ['sample 3', {'term': 24, 'paid': 24, 'finance_charge': 120000, 'acq_fee': 0, 'payment': 10000, 'min_rebate': 100}, {'rebate': 0, 'payoff': 0, 'earned': 120000}], ['sample 4', {'term': 36, 'paid': 0, 'finance_charge': 100, 'acq_fee': 7500, 'payment': 25000, 'min_rebate': 500}, {'rebate': 0, 'payoff': 900000, 'earned': 100}], ['sample 5', {'term': 24, 'paid': 12, 'finance_charge': 100, 'acq_fee': 2500, 'payment': 10000, 'min_rebate': 500}, {'rebate': 0, 'payoff': 120000, 'earned': 100}], ['sample 6', {'term': 6, 'paid': 0, 'finance_charge': 7777, 'acq_fee': 0, 'payment': 10000, 'min_rebate': 500}, {'rebate': 7777, 'payoff': 52223, 'earned': 0}]], [['regression: remaining installment count', {'term': 6, 'paid': 5, 'finance_charge': 100, 'acq_fee': 0, 'payment': 25000, 'min_rebate': 2000}, {'rebate': 0, 'payoff': 25000, 'earned': 100}], ['sample 1', {'term': 24, 'paid': 3, 'finance_charge': 120000, 'acq_fee': 0, 'payment': 10000, 'min_rebate': 2000}, {'rebate': 92400, 'payoff': 117600, 'earned': 27600}], ['sample 2', {'term': 12, 'paid': 3, 'finance_charge': 50000, 'acq_fee': 0, 'payment': 4321, 'min_rebate': 2000}, {'rebate': 28846, 'payoff': 10043, 'earned': 21154}], ['sample 3', {'term': 6, 'paid': 3, 'finance_charge': 7777, 'acq_fee': 7500, 'payment': 4321, 'min_rebate': 0}, {'rebate': 79, 'payoff': 12884, 'earned': 7698}], ['sample 4', {'term': 6, 'paid': 5, 'finance_charge': 120000, 'acq_fee': 0, 'payment': 10000, 'min_rebate': 0}, {'rebate': 5714, 'payoff': 4286, 'earned': 114286}], ['sample 5', {'term': 12, 'paid': 12, 'finance_charge': 7777, 'acq_fee': 7500, 'payment': 10000, 'min_rebate': 2000}, {'rebate': 0, 'payoff': 0, 'earned': 7777}], ['sample 6', {'term': 24, 'paid': 0, 'finance_charge': 7777, 'acq_fee': 2500, 'payment': 10000, 'min_rebate': 0}, {'rebate': 5277, 'payoff': 234723, 'earned': 2500}]], [['regression: remaining installment count', {'term': 36, 'paid': 0, 'finance_charge': 100, 'acq_fee': 7500, 'payment': 25000, 'min_rebate': 500}, {'rebate': 0, 'payoff': 900000, 'earned': 100}], ['sample 1', {'term': 12, 'paid': 12, 'finance_charge': 50000, 'acq_fee': 2500, 'payment': 10000, 'min_rebate': 0}, {'rebate': 0, 'payoff': 0, 'earned': 50000}], ['sample 2', {'term': 12, 'paid': 0, 'finance_charge': 7777, 'acq_fee': 0, 'payment': 4321, 'min_rebate': 2000}, {'rebate': 7777, 'payoff': 44075, 'earned': 0}], ['sample 3', {'term': 36, 'paid': 0, 'finance_charge': 100, 'acq_fee': 0, 'payment': 10000, 'min_rebate': 0}, {'rebate': 100, 'payoff': 359900, 'earned': 0}], ['sample 4', {'term': 6, 'paid': 3, 'finance_charge': 100, 'acq_fee': 0, 'payment': 25000, 'min_rebate': 2000}, {'rebate': 0, 'payoff': 75000, 'earned': 100}], ['sample 5', {'term': 24, 'paid': 12, 'finance_charge': 120000, 'acq_fee': 2500, 'payment': 4321, 'min_rebate': 100}, {'rebate': 30550, 'payoff': 21302, 'earned': 89450}], ['sample 6', {'term': 36, 'paid': 18, 'finance_charge': 50000, 'acq_fee': 0, 'payment': 4321, 'min_rebate': 0}, {'rebate': 12838, 'payoff': 64940, 'earned': 37162}]], [['regression: remaining installment count', {'term': 24, 'paid': 12, 'finance_charge': 120000, 'acq_fee': 7500, 'payment': 25000, 'min_rebate': 500}, {'rebate': 29250, 'payoff': 270750, 'earned': 90750}], ['sample 1', {'term': 24, 'paid': 12, 'finance_charge': 100, 'acq_fee': 0, 'payment': 4321, 'min_rebate': 500}, {'rebate': 0, 'payoff': 51852, 'earned': 100}], ['sample 2', {'term': 36, 'paid': 0, 'finance_charge': 7777, 'acq_fee': 7500, 'payment': 25000, 'min_rebate': 2000}, {'rebate': 0, 'payoff': 900000, 'earned': 7777}], ['sample 3', {'term': 6, 'paid': 5, 'finance_charge': 120000, 'acq_fee': 2500, 'payment': 10000, 'min_rebate': 100}, {'rebate': 5595, 'payoff': 4405, 'earned': 114405}], ['sample 4', {'term': 36, 'paid': 18, 'finance_charge': 7777, 'acq_fee': 2500, 'payment': 10000, 'min_rebate': 100}, {'rebate': 1355, 'payoff': 178645, 'earned': 6422}], ['sample 5', {'term': 36, 'paid': 36, 'finance_charge': 100, 'acq_fee': 7500, 'payment': 4321, 'min_rebate': 0}, {'rebate': 0, 'payoff': 0, 'earned': 100}], ['sample 6', {'term': 12, 'paid': 0, 'finance_charge': 100, 'acq_fee': 0, 'payment': 25000, 'min_rebate': 500}, {'rebate': 0, 'payoff': 300000, 'earned': 100}]]]\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":"518dcb65906dd75a949a59a9b08d05ae1f159cb0e95f37a02628d1904d481c2b","source":"\"\"\"Failure Map reference implementation. Python standard library only.\"\"\"\nimport json\n\nN = 1\nobservations = []\ndef solve(x):\n    def rnd(n, d):\n        q, r = divmod(n, d)\n        return q + (1 if 2 * r >= d else 0)\n    n = x['term']\n    k = x['paid']\n    fc = x['finance_charge'] - x['acq_fee']\n    rem = n - k\n    rebate = rnd(fc * rem * (rem + 1), n * (n + 1))\n    if rebate < x['min_rebate']:\n        rebate = 0\n    remaining = x['payment'] * rem\n    return {'rebate': rebate, 'payoff': remaining - rebate, 'earned': x['finance_charge'] - rebate}\ndef check(label, actual, expected):\n    observations.append({\"check\": label, \"actual\": actual, \"expected\": expected, \"passed\": actual == expected})\nfixtures = [[['regression: remaining installment count', {'term': 36, 'paid': 35, 'finance_charge': 120000, 'acq_fee': 0, 'payment': 10000, 'min_rebate': 100}, {'rebate': 180, 'payoff': 9820, 'earned': 119820}], ['sample 1', {'term': 36, 'paid': 35, 'finance_charge': 50000, 'acq_fee': 7500, 'payment': 25000, 'min_rebate': 100}, {'rebate': 0, 'payoff': 25000, 'earned': 50000}], ['sample 2', {'term': 24, 'paid': 23, 'finance_charge': 100, 'acq_fee': 0, 'payment': 25000, 'min_rebate': 100}, {'rebate': 0, 'payoff': 25000, 'earned': 100}], ['sample 3', {'term': 24, 'paid': 12, 'finance_charge': 50000, 'acq_fee': 0, 'payment': 25000, 'min_rebate': 100}, {'rebate': 13000, 'payoff': 287000, 'earned': 37000}], ['sample 4', {'term': 12, 'paid': 3, 'finance_charge': 100, 'acq_fee': 0, 'payment': 4321, 'min_rebate': 100}, {'rebate': 0, 'payoff': 38889, 'earned': 100}], ['sample 5', {'term': 6, 'paid': 3, 'finance_charge': 7777, 'acq_fee': 0, 'payment': 10000, 'min_rebate': 500}, {'rebate': 2222, 'payoff': 27778, 'earned': 5555}], ['sample 6', {'term': 24, 'paid': 1, 'finance_charge': 120000, 'acq_fee': 0, 'payment': 4321, 'min_rebate': 100}, {'rebate': 110400, 'payoff': -11017, 'earned': 9600}]], [['regression: remaining installment count', {'term': 36, 'paid': 3, 'finance_charge': 7777, 'acq_fee': 2500, 'payment': 10000, 'min_rebate': 500}, {'rebate': 4445, 'payoff': 325555, 'earned': 3332}], ['sample 1', {'term': 24, 'paid': 1, 'finance_charge': 120000, 'acq_fee': 0, 'payment': 25000, 'min_rebate': 500}, {'rebate': 110400, 'payoff': 464600, 'earned': 9600}], ['sample 2', {'term': 12, 'paid': 1, 'finance_charge': 100, 'acq_fee': 0, 'payment': 10000, 'min_rebate': 100}, {'rebate': 0, 'payoff': 110000, 'earned': 100}], ['sample 3', {'term': 24, 'paid': 24, 'finance_charge': 120000, 'acq_fee': 0, 'payment': 10000, 'min_rebate': 100}, {'rebate': 0, 'payoff': 0, 'earned': 120000}], ['sample 4', {'term': 36, 'paid': 0, 'finance_charge': 100, 'acq_fee': 7500, 'payment': 25000, 'min_rebate': 500}, {'rebate': 0, 'payoff': 900000, 'earned': 100}], ['sample 5', {'term': 24, 'paid': 12, 'finance_charge': 100, 'acq_fee': 2500, 'payment': 10000, 'min_rebate': 500}, {'rebate': 0, 'payoff': 120000, 'earned': 100}], ['sample 6', {'term': 6, 'paid': 0, 'finance_charge': 7777, 'acq_fee': 0, 'payment': 10000, 'min_rebate': 500}, {'rebate': 7777, 'payoff': 52223, 'earned': 0}]], [['regression: remaining installment count', {'term': 6, 'paid': 5, 'finance_charge': 100, 'acq_fee': 0, 'payment': 25000, 'min_rebate': 2000}, {'rebate': 0, 'payoff': 25000, 'earned': 100}], ['sample 1', {'term': 24, 'paid': 3, 'finance_charge': 120000, 'acq_fee': 0, 'payment': 10000, 'min_rebate': 2000}, {'rebate': 92400, 'payoff': 117600, 'earned': 27600}], ['sample 2', {'term': 12, 'paid': 3, 'finance_charge': 50000, 'acq_fee': 0, 'payment': 4321, 'min_rebate': 2000}, {'rebate': 28846, 'payoff': 10043, 'earned': 21154}], ['sample 3', {'term': 6, 'paid': 3, 'finance_charge': 7777, 'acq_fee': 7500, 'payment': 4321, 'min_rebate': 0}, {'rebate': 79, 'payoff': 12884, 'earned': 7698}], ['sample 4', {'term': 6, 'paid': 5, 'finance_charge': 120000, 'acq_fee': 0, 'payment': 10000, 'min_rebate': 0}, {'rebate': 5714, 'payoff': 4286, 'earned': 114286}], ['sample 5', {'term': 12, 'paid': 12, 'finance_charge': 7777, 'acq_fee': 7500, 'payment': 10000, 'min_rebate': 2000}, {'rebate': 0, 'payoff': 0, 'earned': 7777}], ['sample 6', {'term': 24, 'paid': 0, 'finance_charge': 7777, 'acq_fee': 2500, 'payment': 10000, 'min_rebate': 0}, {'rebate': 5277, 'payoff': 234723, 'earned': 2500}]], [['regression: remaining installment count', {'term': 36, 'paid': 0, 'finance_charge': 100, 'acq_fee': 7500, 'payment': 25000, 'min_rebate': 500}, {'rebate': 0, 'payoff': 900000, 'earned': 100}], ['sample 1', {'term': 12, 'paid': 12, 'finance_charge': 50000, 'acq_fee': 2500, 'payment': 10000, 'min_rebate': 0}, {'rebate': 0, 'payoff': 0, 'earned': 50000}], ['sample 2', {'term': 12, 'paid': 0, 'finance_charge': 7777, 'acq_fee': 0, 'payment': 4321, 'min_rebate': 2000}, {'rebate': 7777, 'payoff': 44075, 'earned': 0}], ['sample 3', {'term': 36, 'paid': 0, 'finance_charge': 100, 'acq_fee': 0, 'payment': 10000, 'min_rebate': 0}, {'rebate': 100, 'payoff': 359900, 'earned': 0}], ['sample 4', {'term': 6, 'paid': 3, 'finance_charge': 100, 'acq_fee': 0, 'payment': 25000, 'min_rebate': 2000}, {'rebate': 0, 'payoff': 75000, 'earned': 100}], ['sample 5', {'term': 24, 'paid': 12, 'finance_charge': 120000, 'acq_fee': 2500, 'payment': 4321, 'min_rebate': 100}, {'rebate': 30550, 'payoff': 21302, 'earned': 89450}], ['sample 6', {'term': 36, 'paid': 18, 'finance_charge': 50000, 'acq_fee': 0, 'payment': 4321, 'min_rebate': 0}, {'rebate': 12838, 'payoff': 64940, 'earned': 37162}]], [['regression: remaining installment count', {'term': 24, 'paid': 12, 'finance_charge': 120000, 'acq_fee': 7500, 'payment': 25000, 'min_rebate': 500}, {'rebate': 29250, 'payoff': 270750, 'earned': 90750}], ['sample 1', {'term': 24, 'paid': 12, 'finance_charge': 100, 'acq_fee': 0, 'payment': 4321, 'min_rebate': 500}, {'rebate': 0, 'payoff': 51852, 'earned': 100}], ['sample 2', {'term': 36, 'paid': 0, 'finance_charge': 7777, 'acq_fee': 7500, 'payment': 25000, 'min_rebate': 2000}, {'rebate': 0, 'payoff': 900000, 'earned': 7777}], ['sample 3', {'term': 6, 'paid': 5, 'finance_charge': 120000, 'acq_fee': 2500, 'payment': 10000, 'min_rebate': 100}, {'rebate': 5595, 'payoff': 4405, 'earned': 114405}], ['sample 4', {'term': 36, 'paid': 18, 'finance_charge': 7777, 'acq_fee': 2500, 'payment': 10000, 'min_rebate': 100}, {'rebate': 1355, 'payoff': 178645, 'earned': 6422}], ['sample 5', {'term': 36, 'paid': 36, 'finance_charge': 100, 'acq_fee': 7500, 'payment': 4321, 'min_rebate': 0}, {'rebate': 0, 'payoff': 0, 'earned': 100}], ['sample 6', {'term': 12, 'paid': 0, 'finance_charge': 100, 'acq_fee': 0, 'payment': 25000, 'min_rebate': 500}, {'rebate': 0, 'payoff': 300000, 'earned': 100}]]]\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-rule-of-78-rebate-remaining-installment-count","generated_at":"2026-09-29T14:46:28.652810+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":"Remaining installments are n - k.","root_cause":"The current installment is excluded from the remaining count.","sha256":"2f25cf5dedee502ae9e1ec40094ffb7827af1037d221e68c7f234c5518276f4b","title":"Rule of 78s early payoff rebate: remaining installment count · case 01","variant":1,"variant_policy":"Five numbered records share a model and may reuse boundary fixtures.","verification":{"attempt":{"elapsed_ms":39.142,"exit_code":1,"observations":[{"actual":{"earned":119459,"payoff":19459,"rebate":541},"check":"regression: remaining installment count","expected":{"earned":119820,"payoff":9820,"rebate":180},"passed":false},{"actual":{"earned":49809,"payoff":49809,"rebate":191},"check":"sample 1","expected":{"earned":50000,"payoff":25000,"rebate":0},"passed":false},{"actual":{"earned":100,"payoff":50000,"rebate":0},"check":"sample 2","expected":{"earned":100,"payoff":25000,"rebate":0},"passed":false},{"actual":{"earned":34833,"payoff":309833,"rebate":15167},"check":"sample 3","expected":{"earned":37000,"payoff":287000,"rebate":13000},"passed":false},{"actual":{"earned":100,"payoff":43210,"rebate":0},"check":"sample 4","expected":{"earned":100,"payoff":38889,"rebate":0},"passed":false},{"actual":{"earned":4074,"payoff":36297,"rebate":3703},"check":"sample 5","expected":{"earned":5555,"payoff":27778,"rebate":2222},"passed":false},{"actual":{"earned":0,"payoff":-16296,"rebate":120000},"check":"sample 6","expected":{"earned":9600,"payoff":-11017,"rebate":110400},"passed":false}],"passed":false,"stderr":"","stdout":"{\"observations\": [{\"check\": \"regression: remaining installment count\", \"actual\": {\"rebate\": 541, \"payoff\": 19459, \"earned\": 119459}, \"expected\": {\"rebate\": 180, \"payoff\": 9820, \"earned\": 119820}, \"passed\": false}, {\"check\": \"sample 1\", \"actual\": {\"rebate\": 191, \"payoff\": 49809, \"earned\": 49809}, \"expected\": {\"rebate\": 0, \"payoff\": 25000, \"earned\": 50000}, \"passed\": false}, {\"check\": \"sample 2\", \"actual\": {\"rebate\": 0, \"payoff\": 50000, \"earned\": 100}, \"expected\": {\"rebate\": 0, \"payoff\": 25000, \"earned\": 100}, \"passed\": false}, {\"check\": \"sample 3\", \"actual\": {\"rebate\": 15167, \"payoff\": 309833, \"earned\": 34833}, \"expected\": {\"rebate\": 13000, \"payoff\": 287000, \"earned\": 37000}, \"passed\": false}, {\"check\": \"sample 4\", \"actual\": {\"rebate\": 0, \"payoff\": 43210, \"earned\": 100}, \"expected\": {\"rebate\": 0, \"payoff\": 38889, \"earned\": 100}, \"passed\": false}, {\"check\": \"sample 5\", \"actual\": {\"rebate\": 3703, \"payoff\": 36297, \"earned\": 4074}, \"expected\": {\"rebate\": 2222, \"payoff\": 27778, \"earned\": 5555}, \"passed\": false}, {\"check\": \"sample 6\", \"actual\": {\"rebate\": 120000, \"payoff\": -16296, \"earned\": 0}, \"expected\": {\"rebate\": 110400, \"payoff\": -11017, \"earned\": 9600}, \"passed\": false}], \"passed\": false}\n"},"broken":{"elapsed_ms":40.44,"exit_code":1,"observations":[{"actual":{"earned":120000,"payoff":0,"rebate":0},"check":"regression: remaining installment count","expected":{"earned":119820,"payoff":9820,"rebate":180},"passed":false},{"actual":{"earned":50000,"payoff":0,"rebate":0},"check":"sample 1","expected":{"earned":50000,"payoff":25000,"rebate":0},"passed":false},{"actual":{"earned":100,"payoff":0,"rebate":0},"check":"sample 2","expected":{"earned":100,"payoff":25000,"rebate":0},"passed":false},{"actual":{"earned":39000,"payoff":264000,"rebate":11000},"check":"sample 3","expected":{"earned":37000,"payoff":287000,"rebate":13000},"passed":false},{"actual":{"earned":100,"payoff":34568,"rebate":0},"check":"sample 4","expected":{"earned":100,"payoff":38889,"rebate":0},"passed":false},{"actual":{"earned":6666,"payoff":18889,"rebate":1111},"check":"sample 5","expected":{"earned":5555,"payoff":27778,"rebate":2222},"passed":false},{"actual":{"earned":18800,"payoff":-6138,"rebate":101200},"check":"sample 6","expected":{"earned":9600,"payoff":-11017,"rebate":110400},"passed":false}],"passed":false,"stderr":"","stdout":"{\"observations\": [{\"check\": \"regression: remaining installment count\", \"actual\": {\"rebate\": 0, \"payoff\": 0, \"earned\": 120000}, \"expected\": {\"rebate\": 180, \"payoff\": 9820, \"earned\": 119820}, \"passed\": false}, {\"check\": \"sample 1\", \"actual\": {\"rebate\": 0, \"payoff\": 0, \"earned\": 50000}, \"expected\": {\"rebate\": 0, \"payoff\": 25000, \"earned\": 50000}, \"passed\": false}, {\"check\": \"sample 2\", \"actual\": {\"rebate\": 0, \"payoff\": 0, \"earned\": 100}, \"expected\": {\"rebate\": 0, \"payoff\": 25000, \"earned\": 100}, \"passed\": false}, {\"check\": \"sample 3\", \"actual\": {\"rebate\": 11000, \"payoff\": 264000, \"earned\": 39000}, \"expected\": {\"rebate\": 13000, \"payoff\": 287000, \"earned\": 37000}, \"passed\": false}, {\"check\": \"sample 4\", \"actual\": {\"rebate\": 0, \"payoff\": 34568, \"earned\": 100}, \"expected\": {\"rebate\": 0, \"payoff\": 38889, \"earned\": 100}, \"passed\": false}, {\"check\": \"sample 5\", \"actual\": {\"rebate\": 1111, \"payoff\": 18889, \"earned\": 6666}, \"expected\": {\"rebate\": 2222, \"payoff\": 27778, \"earned\": 5555}, \"passed\": false}, {\"check\": \"sample 6\", \"actual\": {\"rebate\": 101200, \"payoff\": -6138, \"earned\": 18800}, \"expected\": {\"rebate\": 110400, \"payoff\": -11017, \"earned\": 9600}, \"passed\": false}], \"passed\": false}\n"},"fixed":{"elapsed_ms":41.086,"exit_code":0,"observations":[{"actual":{"earned":119820,"payoff":9820,"rebate":180},"check":"regression: remaining installment count","expected":{"earned":119820,"payoff":9820,"rebate":180},"passed":true},{"actual":{"earned":50000,"payoff":25000,"rebate":0},"check":"sample 1","expected":{"earned":50000,"payoff":25000,"rebate":0},"passed":true},{"actual":{"earned":100,"payoff":25000,"rebate":0},"check":"sample 2","expected":{"earned":100,"payoff":25000,"rebate":0},"passed":true},{"actual":{"earned":37000,"payoff":287000,"rebate":13000},"check":"sample 3","expected":{"earned":37000,"payoff":287000,"rebate":13000},"passed":true},{"actual":{"earned":100,"payoff":38889,"rebate":0},"check":"sample 4","expected":{"earned":100,"payoff":38889,"rebate":0},"passed":true},{"actual":{"earned":5555,"payoff":27778,"rebate":2222},"check":"sample 5","expected":{"earned":5555,"payoff":27778,"rebate":2222},"passed":true},{"actual":{"earned":9600,"payoff":-11017,"rebate":110400},"check":"sample 6","expected":{"earned":9600,"payoff":-11017,"rebate":110400},"passed":true}],"passed":true,"stderr":"","stdout":"{\"observations\": [{\"check\": \"regression: remaining installment count\", \"actual\": {\"rebate\": 180, \"payoff\": 9820, \"earned\": 119820}, \"expected\": {\"rebate\": 180, \"payoff\": 9820, \"earned\": 119820}, \"passed\": true}, {\"check\": \"sample 1\", \"actual\": {\"rebate\": 0, \"payoff\": 25000, \"earned\": 50000}, \"expected\": {\"rebate\": 0, \"payoff\": 25000, \"earned\": 50000}, \"passed\": true}, {\"check\": \"sample 2\", \"actual\": {\"rebate\": 0, \"payoff\": 25000, \"earned\": 100}, \"expected\": {\"rebate\": 0, \"payoff\": 25000, \"earned\": 100}, \"passed\": true}, {\"check\": \"sample 3\", \"actual\": {\"rebate\": 13000, \"payoff\": 287000, \"earned\": 37000}, \"expected\": {\"rebate\": 13000, \"payoff\": 287000, \"earned\": 37000}, \"passed\": true}, {\"check\": \"sample 4\", \"actual\": {\"rebate\": 0, \"payoff\": 38889, \"earned\": 100}, \"expected\": {\"rebate\": 0, \"payoff\": 38889, \"earned\": 100}, \"passed\": true}, {\"check\": \"sample 5\", \"actual\": {\"rebate\": 2222, \"payoff\": 27778, \"earned\": 5555}, \"expected\": {\"rebate\": 2222, \"payoff\": 27778, \"earned\": 5555}, \"passed\": true}, {\"check\": \"sample 6\", \"actual\": {\"rebate\": 110400, \"payoff\": -11017, \"earned\": 9600}, \"expected\": {\"rebate\": 110400, \"payoff\": -11017, \"earned\": 9600}, \"passed\": true}], \"passed\": true}\n"}},"verified":true,"visibility":"public"}