broken_source:
"""Failure Map reference implementation. Python standard library only."""
import json
import math
from fractions import Fraction
N = 1
observations = []
def solve(x):
    def rnd(n, d):
        q, r = divmod(n, d)
        return q + (1 if 2 * r >= d else 0)
    def level(P, n):
        if n <= 0 or P <= 0:
            return 0
        if bp == 0:
            exact = Fraction(P, n)
        else:
            r = Fraction(bp, 120000)
            exact = P * r / (1 - (1 + r) ** -n)
        return math.floor(exact + Fraction(1, 2))
    bp = x['rate_bp']
    P = x['principal']
    io_bills = []
    total_int = 0
    for m in range(1, x['io_months']):
        days = 31 - x['fund_day'] if m == 0 else 30
        interest = rnd(P * bp * days, 120000 * 30)
        io_bills.append(interest)
        total_int += interest
    pay = level(P, x['amort_months'])
    bal = P
    final = 0
    for k in range(1, x['amort_months'] + 1):
        interest = rnd(bal * bp, 120000)
        total_int += interest
        if k == x['amort_months']:
            final = bal + interest
        else:
            bal -= pay - interest
    return {'io_bills': io_bills, 'payment': pay, 'final_payment': final, 'total_interest': total_int}
def check(label, actual, expected):
    observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
fixtures = [[['regression: interest-only month count', {'principal': 3000000, 'rate_bp': 500, 'io_months': 3, 'amort_months': 60, 'fund_day': 1}, {'io_bills': [12500, 12500, 12500], 'payment': 56614, 'final_payment': 56596, 'total_interest': 434322}], ['sample 1', {'principal': 12345, 'rate_bp': 725, 'io_months': 12, 'amort_months': 12, 'fund_day': 15}, {'io_bills': [40, 75, 75, 75, 75, 75, 75, 75, 75, 75, 75, 75], 'payment': 1070, 'final_payment': 1065, 'total_interest': 1355}], ['sample 2', {'principal': 250000, 'rate_bp': 725, 'io_months': 3, 'amort_months': 12, 'fund_day': 29}, {'io_bills': [101, 1510, 1510], 'payment': 21661, 'final_payment': 21655, 'total_interest': 13047}], ['sample 3', {'principal': 250000, 'rate_bp': 1200, 'io_months': 3, 'amort_months': 24, 'fund_day': 29}, {'io_bills': [167, 2500, 2500], 'payment': 11768, 'final_payment': 11778, 'total_interest': 37609}], ['sample 4', {'principal': 3000000, 'rate_bp': 1200, 'io_months': 3, 'amort_months': 1, 'fund_day': 2}, {'io_bills': [29000, 30000, 30000], 'payment': 3030000, 'final_payment': 3030000, 'total_interest': 119000}], ['sample 5', {'principal': 12345, 'rate_bp': 0, 'io_months': 1, 'amort_months': 24, 'fund_day': 2}, {'io_bills': [0], 'payment': 514, 'final_payment': 523, 'total_interest': 0}], ['sample 6', {'principal': 12345, 'rate_bp': 500, 'io_months': 3, 'amort_months': 12, 'fund_day': 15}, {'io_bills': [27, 51, 51], 'payment': 1057, 'final_payment': 1054, 'total_interest': 465}]], [['regression: interest-only month count', {'principal': 12345, 'rate_bp': 0, 'io_months': 3, 'amort_months': 12, 'fund_day': 1}, {'io_bills': [0, 0, 0], 'payment': 1029, 'final_payment': 1026, 'total_interest': 0}], ['sample 1', {'principal': 100000, 'rate_bp': 725, 'io_months': 2, 'amort_months': 60, 'fund_day': 1}, {'io_bills': [604, 604], 'payment': 1992, 'final_payment': 1987, 'total_interest': 20723}], ['sample 2', {'principal': 250000, 'rate_bp': 500, 'io_months': 2, 'amort_months': 60, 'fund_day': 30}, {'io_bills': [35, 1042], 'payment': 4718, 'final_payment': 4703, 'total_interest': 34142}], ['sample 3', {'principal': 3000000, 'rate_bp': 0, 'io_months': 12, 'amort_months': 60, 'fund_day': 2}, {'io_bills': [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], 'payment': 50000, 'final_payment': 50000, 'total_interest': 0}], ['sample 4', {'principal': 100000, 'rate_bp': 1200, 'io_months': 3, 'amort_months': 24, 'fund_day': 30}, {'io_bills': [33, 1000, 1000], 'payment': 4707, 'final_payment': 4718, 'total_interest': 15012}], ['sample 5', {'principal': 12345, 'rate_bp': 725, 'io_months': 3, 'amort_months': 60, 'fund_day': 15}, {'io_bills': [40, 75, 75], 'payment': 246, 'final_payment': 239, 'total_interest': 2598}], ['sample 6', {'principal': 3000000, 'rate_bp': 500, 'io_months': 3, 'amort_months': 12, 'fund_day': 1}, {'io_bills': [12500, 12500, 12500], 'payment': 256822, 'final_payment': 256827, 'total_interest': 119369}]], [['regression: interest-only month count', {'principal': 3000000, 'rate_bp': 500, 'io_months': 2, 'amort_months': 24, 'fund_day': 29}, {'io_bills': [833, 12500], 'payment': 131614, 'final_payment': 131618, 'total_interest': 172073}], ['sample 1', {'principal': 3000000, 'rate_bp': 500, 'io_months': 3, 'amort_months': 24, 'fund_day': 1}, {'io_bills': [12500, 12500, 12500], 'payment': 131614, 'final_payment': 131618, 'total_interest': 196240}], ['sample 2', {'principal': 12345, 'rate_bp': 500, 'io_months': 2, 'amort_months': 12, 'fund_day': 15}, {'io_bills': [27, 51], 'payment': 1057, 'final_payment': 1054, 'total_interest': 414}], ['sample 3', {'principal': 100000, 'rate_bp': 0, 'io_months': 12, 'amort_months': 24, 'fund_day': 29}, {'io_bills': [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], 'payment': 4167, 'final_payment': 4159, 'total_interest': 0}], ['sample 4', {'principal': 100000, 'rate_bp': 500, 'io_months': 12, 'amort_months': 24, 'fund_day': 15}, {'io_bills': [222, 417, 417, 417, 417, 417, 417, 417, 417, 417, 417, 417], 'payment': 4387, 'final_payment': 4389, 'total_interest': 10099}], ['sample 5', {'principal': 100000, 'rate_bp': 500, 'io_months': 12, 'amort_months': 12, 'fund_day': 29}, {'io_bills': [28, 417, 417, 417, 417, 417, 417, 417, 417, 417, 417, 417], 'payment': 8561, 'final_payment': 8559, 'total_interest': 7345}], ['sample 6', {'principal': 12345, 'rate_bp': 0, 'io_months': 2, 'amort_months': 24, 'fund_day': 30}, {'io_bills': [0, 0], 'payment': 514, 'final_payment': 523, 'total_interest': 0}]], [['regression: interest-only month count', {'principal': 250000, 'rate_bp': 500, 'io_months': 12, 'amort_months': 60, 'fund_day': 15}, {'io_bills': [556, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042], 'payment': 4718, 'final_payment': 4703, 'total_interest': 45083}], ['sample 1', {'principal': 100000, 'rate_bp': 0, 'io_months': 12, 'amort_months': 60, 'fund_day': 29}, {'io_bills': [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], 'payment': 1667, 'final_payment': 1647, 'total_interest': 0}], ['sample 2', {'principal': 250000, 'rate_bp': 0, 'io_months': 3, 'amort_months': 1, 'fund_day': 2}, {'io_bills': [0, 0, 0], 'payment': 250000, 'final_payment': 250000, 'total_interest': 0}], ['sample 3', {'principal': 250000, 'rate_bp': 1200, 'io_months': 12, 'amort_months': 24, 'fund_day': 15}, {'io_bills': [1333, 2500, 2500, 2500, 2500, 2500, 2500, 2500, 2500, 2500, 2500, 2500], 'payment': 11768, 'final_payment': 11778, 'total_interest': 61275}], ['sample 4', {'principal': 100000, 'rate_bp': 725, 'io_months': 12, 'amort_months': 24, 'fund_day': 15}, {'io_bills': [322, 604, 604, 604, 604, 604, 604, 604, 604, 604, 604, 604], 'payment': 4489, 'final_payment': 4477, 'total_interest': 14690}], ['sample 5', {'principal': 12345, 'rate_bp': 1200, 'io_months': 2, 'amort_months': 24, 'fund_day': 30}, {'io_bills': [4, 123], 'payment': 581, 'final_payment': 584, 'total_interest': 1729}], ['sample 6', {'principal': 100000, 'rate_bp': 500, 'io_months': 1, 'amort_months': 12, 'fund_day': 30}, {'io_bills': [14], 'payment': 8561, 'final_payment': 8559, 'total_interest': 2744}]], [['regression: interest-only month count', {'principal': 12345, 'rate_bp': 725, 'io_months': 1, 'amort_months': 12, 'fund_day': 2}, {'io_bills': [72], 'payment': 1070, 'final_payment': 1065, 'total_interest': 562}], ['sample 1', {'principal': 3000000, 'rate_bp': 725, 'io_months': 1, 'amort_months': 60, 'fund_day': 29}, {'io_bills': [1208], 'payment': 59758, 'final_payment': 59764, 'total_interest': 586694}], ['sample 2', {'principal': 100000, 'rate_bp': 500, 'io_months': 3, 'amort_months': 60, 'fund_day': 2}, {'io_bills': [403, 417, 417], 'payment': 1887, 'final_payment': 1894, 'total_interest': 14464}], ['sample 3', {'principal': 250000, 'rate_bp': 725, 'io_months': 1, 'amort_months': 60, 'fund_day': 15}, {'io_bills': [806], 'payment': 4980, 'final_payment': 4967, 'total_interest': 49593}], ['sample 4', {'principal': 100000, 'rate_bp': 0, 'io_months': 12, 'amort_months': 24, 'fund_day': 30}, {'io_bills': [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], 'payment': 4167, 'final_payment': 4159, 'total_interest': 0}], ['sample 5', {'principal': 12345, 'rate_bp': 725, 'io_months': 2, 'amort_months': 24, 'fund_day': 15}, {'io_bills': [40, 75], 'payment': 554, 'final_payment': 559, 'total_interest': 1071}], ['sample 6', {'principal': 3000000, 'rate_bp': 0, 'io_months': 12, 'amort_months': 24, 'fund_day': 15}, {'io_bills': [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], 'payment': 125000, 'final_payment': 125000, 'total_interest': 0}]]]
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)

source:
"""Failure Map reference implementation. Python standard library only."""
import json
import math
from fractions import Fraction
N = 1
observations = []
def solve(x):
    def rnd(n, d):
        q, r = divmod(n, d)
        return q + (1 if 2 * r >= d else 0)
    def level(P, n):
        if n <= 0 or P <= 0:
            return 0
        if bp == 0:
            exact = Fraction(P, n)
        else:
            r = Fraction(bp, 120000)
            exact = P * r / (1 - (1 + r) ** -n)
        return math.floor(exact + Fraction(1, 2))
    bp = x['rate_bp']
    P = x['principal']
    io_bills = []
    total_int = 0
    for m in range(x['io_months'] + 1):
        days = 31 - x['fund_day'] if m == 0 else 30
        interest = rnd(P * bp * days, 120000 * 30)
        io_bills.append(interest)
        total_int += interest
    pay = level(P, x['amort_months'])
    bal = P
    final = 0
    for k in range(1, x['amort_months'] + 1):
        interest = rnd(bal * bp, 120000)
        total_int += interest
        if k == x['amort_months']:
            final = bal + interest
        else:
            bal -= pay - interest
    return {'io_bills': io_bills, 'payment': pay, 'final_payment': final, 'total_interest': total_int}
def check(label, actual, expected):
    observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
fixtures = [[['regression: interest-only month count', {'principal': 3000000, 'rate_bp': 500, 'io_months': 3, 'amort_months': 60, 'fund_day': 1}, {'io_bills': [12500, 12500, 12500], 'payment': 56614, 'final_payment': 56596, 'total_interest': 434322}], ['sample 1', {'principal': 12345, 'rate_bp': 725, 'io_months': 12, 'amort_months': 12, 'fund_day': 15}, {'io_bills': [40, 75, 75, 75, 75, 75, 75, 75, 75, 75, 75, 75], 'payment': 1070, 'final_payment': 1065, 'total_interest': 1355}], ['sample 2', {'principal': 250000, 'rate_bp': 725, 'io_months': 3, 'amort_months': 12, 'fund_day': 29}, {'io_bills': [101, 1510, 1510], 'payment': 21661, 'final_payment': 21655, 'total_interest': 13047}], ['sample 3', {'principal': 250000, 'rate_bp': 1200, 'io_months': 3, 'amort_months': 24, 'fund_day': 29}, {'io_bills': [167, 2500, 2500], 'payment': 11768, 'final_payment': 11778, 'total_interest': 37609}], ['sample 4', {'principal': 3000000, 'rate_bp': 1200, 'io_months': 3, 'amort_months': 1, 'fund_day': 2}, {'io_bills': [29000, 30000, 30000], 'payment': 3030000, 'final_payment': 3030000, 'total_interest': 119000}], ['sample 5', {'principal': 12345, 'rate_bp': 0, 'io_months': 1, 'amort_months': 24, 'fund_day': 2}, {'io_bills': [0], 'payment': 514, 'final_payment': 523, 'total_interest': 0}], ['sample 6', {'principal': 12345, 'rate_bp': 500, 'io_months': 3, 'amort_months': 12, 'fund_day': 15}, {'io_bills': [27, 51, 51], 'payment': 1057, 'final_payment': 1054, 'total_interest': 465}]], [['regression: interest-only month count', {'principal': 12345, 'rate_bp': 0, 'io_months': 3, 'amort_months': 12, 'fund_day': 1}, {'io_bills': [0, 0, 0], 'payment': 1029, 'final_payment': 1026, 'total_interest': 0}], ['sample 1', {'principal': 100000, 'rate_bp': 725, 'io_months': 2, 'amort_months': 60, 'fund_day': 1}, {'io_bills': [604, 604], 'payment': 1992, 'final_payment': 1987, 'total_interest': 20723}], ['sample 2', {'principal': 250000, 'rate_bp': 500, 'io_months': 2, 'amort_months': 60, 'fund_day': 30}, {'io_bills': [35, 1042], 'payment': 4718, 'final_payment': 4703, 'total_interest': 34142}], ['sample 3', {'principal': 3000000, 'rate_bp': 0, 'io_months': 12, 'amort_months': 60, 'fund_day': 2}, {'io_bills': [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], 'payment': 50000, 'final_payment': 50000, 'total_interest': 0}], ['sample 4', {'principal': 100000, 'rate_bp': 1200, 'io_months': 3, 'amort_months': 24, 'fund_day': 30}, {'io_bills': [33, 1000, 1000], 'payment': 4707, 'final_payment': 4718, 'total_interest': 15012}], ['sample 5', {'principal': 12345, 'rate_bp': 725, 'io_months': 3, 'amort_months': 60, 'fund_day': 15}, {'io_bills': [40, 75, 75], 'payment': 246, 'final_payment': 239, 'total_interest': 2598}], ['sample 6', {'principal': 3000000, 'rate_bp': 500, 'io_months': 3, 'amort_months': 12, 'fund_day': 1}, {'io_bills': [12500, 12500, 12500], 'payment': 256822, 'final_payment': 256827, 'total_interest': 119369}]], [['regression: interest-only month count', {'principal': 3000000, 'rate_bp': 500, 'io_months': 2, 'amort_months': 24, 'fund_day': 29}, {'io_bills': [833, 12500], 'payment': 131614, 'final_payment': 131618, 'total_interest': 172073}], ['sample 1', {'principal': 3000000, 'rate_bp': 500, 'io_months': 3, 'amort_months': 24, 'fund_day': 1}, {'io_bills': [12500, 12500, 12500], 'payment': 131614, 'final_payment': 131618, 'total_interest': 196240}], ['sample 2', {'principal': 12345, 'rate_bp': 500, 'io_months': 2, 'amort_months': 12, 'fund_day': 15}, {'io_bills': [27, 51], 'payment': 1057, 'final_payment': 1054, 'total_interest': 414}], ['sample 3', {'principal': 100000, 'rate_bp': 0, 'io_months': 12, 'amort_months': 24, 'fund_day': 29}, {'io_bills': [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], 'payment': 4167, 'final_payment': 4159, 'total_interest': 0}], ['sample 4', {'principal': 100000, 'rate_bp': 500, 'io_months': 12, 'amort_months': 24, 'fund_day': 15}, {'io_bills': [222, 417, 417, 417, 417, 417, 417, 417, 417, 417, 417, 417], 'payment': 4387, 'final_payment': 4389, 'total_interest': 10099}], ['sample 5', {'principal': 100000, 'rate_bp': 500, 'io_months': 12, 'amort_months': 12, 'fund_day': 29}, {'io_bills': [28, 417, 417, 417, 417, 417, 417, 417, 417, 417, 417, 417], 'payment': 8561, 'final_payment': 8559, 'total_interest': 7345}], ['sample 6', {'principal': 12345, 'rate_bp': 0, 'io_months': 2, 'amort_months': 24, 'fund_day': 30}, {'io_bills': [0, 0], 'payment': 514, 'final_payment': 523, 'total_interest': 0}]], [['regression: interest-only month count', {'principal': 250000, 'rate_bp': 500, 'io_months': 12, 'amort_months': 60, 'fund_day': 15}, {'io_bills': [556, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042], 'payment': 4718, 'final_payment': 4703, 'total_interest': 45083}], ['sample 1', {'principal': 100000, 'rate_bp': 0, 'io_months': 12, 'amort_months': 60, 'fund_day': 29}, {'io_bills': [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], 'payment': 1667, 'final_payment': 1647, 'total_interest': 0}], ['sample 2', {'principal': 250000, 'rate_bp': 0, 'io_months': 3, 'amort_months': 1, 'fund_day': 2}, {'io_bills': [0, 0, 0], 'payment': 250000, 'final_payment': 250000, 'total_interest': 0}], ['sample 3', {'principal': 250000, 'rate_bp': 1200, 'io_months': 12, 'amort_months': 24, 'fund_day': 15}, {'io_bills': [1333, 2500, 2500, 2500, 2500, 2500, 2500, 2500, 2500, 2500, 2500, 2500], 'payment': 11768, 'final_payment': 11778, 'total_interest': 61275}], ['sample 4', {'principal': 100000, 'rate_bp': 725, 'io_months': 12, 'amort_months': 24, 'fund_day': 15}, {'io_bills': [322, 604, 604, 604, 604, 604, 604, 604, 604, 604, 604, 604], 'payment': 4489, 'final_payment': 4477, 'total_interest': 14690}], ['sample 5', {'principal': 12345, 'rate_bp': 1200, 'io_months': 2, 'amort_months': 24, 'fund_day': 30}, {'io_bills': [4, 123], 'payment': 581, 'final_payment': 584, 'total_interest': 1729}], ['sample 6', {'principal': 100000, 'rate_bp': 500, 'io_months': 1, 'amort_months': 12, 'fund_day': 30}, {'io_bills': [14], 'payment': 8561, 'final_payment': 8559, 'total_interest': 2744}]], [['regression: interest-only month count', {'principal': 12345, 'rate_bp': 725, 'io_months': 1, 'amort_months': 12, 'fund_day': 2}, {'io_bills': [72], 'payment': 1070, 'final_payment': 1065, 'total_interest': 562}], ['sample 1', {'principal': 3000000, 'rate_bp': 725, 'io_months': 1, 'amort_months': 60, 'fund_day': 29}, {'io_bills': [1208], 'payment': 59758, 'final_payment': 59764, 'total_interest': 586694}], ['sample 2', {'principal': 100000, 'rate_bp': 500, 'io_months': 3, 'amort_months': 60, 'fund_day': 2}, {'io_bills': [403, 417, 417], 'payment': 1887, 'final_payment': 1894, 'total_interest': 14464}], ['sample 3', {'principal': 250000, 'rate_bp': 725, 'io_months': 1, 'amort_months': 60, 'fund_day': 15}, {'io_bills': [806], 'payment': 4980, 'final_payment': 4967, 'total_interest': 49593}], ['sample 4', {'principal': 100000, 'rate_bp': 0, 'io_months': 12, 'amort_months': 24, 'fund_day': 30}, {'io_bills': [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], 'payment': 4167, 'final_payment': 4159, 'total_interest': 0}], ['sample 5', {'principal': 12345, 'rate_bp': 725, 'io_months': 2, 'amort_months': 24, 'fund_day': 15}, {'io_bills': [40, 75], 'payment': 554, 'final_payment': 559, 'total_interest': 1071}], ['sample 6', {'principal': 3000000, 'rate_bp': 0, 'io_months': 12, 'amort_months': 24, 'fund_day': 15}, {'io_bills': [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], 'payment': 125000, 'final_payment': 125000, 'total_interest': 0}]]]
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)

prompt:
The interest-only period bills one month too few.

x = {'principal', 'rate_bp', 'io_months' >= 1, 'amort_months' >= 1, 'fund_day' 1..30}. IO month m pays interest round_half_up(P*bp*days/(120000*30)) with days = 31 - fund_day for the first IO month and 30 otherwise. Then the level payment on the full principal over amort_months (exact annuity, half-up) amortizes with monthly interest round_half_up(balance*bp/120000); the last month pays balance + interest. Return {'io_bills': list of IO payments, 'payment', 'final_payment', 'total_interest'}.

split: open-access


broken_source:
"""Failure Map reference implementation. Python standard library only."""
import json
import math
from fractions import Fraction
N = 1
observations = []
def solve(x):
    def rnd(n, d):
        q, r = divmod(n, d)
        return q + (1 if 2 * r >= d else 0)
    def level(P, n):
        if n <= 0 or P <= 0:
            return 0
        if bp == 0:
            exact = Fraction(P, n)
        else:
            r = Fraction(bp, 120000)
            exact = P * r / (1 - (1 + r) ** -n)
        return math.floor(exact + Fraction(1, 2))
    bp = x['rate_bp']
    P = x['principal']
    io_bills = []
    total_int = 0
    for m in range(x['io_months']):
        days = 30 - x['fund_day'] if m == 0 else 30
        interest = rnd(P * bp * days, 120000 * 30)
        io_bills.append(interest)
        total_int += interest
    pay = level(P, x['amort_months'])
    bal = P
    final = 0
    for k in range(1, x['amort_months'] + 1):
        interest = rnd(bal * bp, 120000)
        total_int += interest
        if k == x['amort_months']:
            final = bal + interest
        else:
            bal -= pay - interest
    return {'io_bills': io_bills, 'payment': pay, 'final_payment': final, 'total_interest': total_int}
def check(label, actual, expected):
    observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
fixtures = [[['regression: stub day count', {'principal': 3000000, 'rate_bp': 500, 'io_months': 3, 'amort_months': 60, 'fund_day': 1}, {'io_bills': [12500, 12500, 12500], 'payment': 56614, 'final_payment': 56596, 'total_interest': 434322}], ['control 1', {'principal': 12345, 'rate_bp': 0, 'io_months': 1, 'amort_months': 24, 'fund_day': 2}, {'io_bills': [0], 'payment': 514, 'final_payment': 523, 'total_interest': 0}], ['control 2', {'principal': 12345, 'rate_bp': 0, 'io_months': 1, 'amort_months': 1, 'fund_day': 2}, {'io_bills': [0], 'payment': 12345, 'final_payment': 12345, 'total_interest': 0}], ['control 3', {'principal': 250000, 'rate_bp': 0, 'io_months': 2, 'amort_months': 60, 'fund_day': 15}, {'io_bills': [0, 0], 'payment': 4167, 'final_payment': 4147, 'total_interest': 0}], ['control 4', {'principal': 100000, 'rate_bp': 0, 'io_months': 3, 'amort_months': 60, 'fund_day': 15}, {'io_bills': [0, 0, 0], 'payment': 1667, 'final_payment': 1647, 'total_interest': 0}], ['control 5', {'principal': 100000, 'rate_bp': 0, 'io_months': 1, 'amort_months': 12, 'fund_day': 29}, {'io_bills': [0], 'payment': 8333, 'final_payment': 8337, 'total_interest': 0}], ['control 6', {'principal': 100000, 'rate_bp': 0, 'io_months': 3, 'amort_months': 1, 'fund_day': 30}, {'io_bills': [0, 0, 0], 'payment': 100000, 'final_payment': 100000, 'total_interest': 0}]], [['regression: stub day count', {'principal': 100000, 'rate_bp': 725, 'io_months': 2, 'amort_months': 60, 'fund_day': 1}, {'io_bills': [604, 604], 'payment': 1992, 'final_payment': 1987, 'total_interest': 20723}], ['control 1', {'principal': 12345, 'rate_bp': 0, 'io_months': 3, 'amort_months': 12, 'fund_day': 1}, {'io_bills': [0, 0, 0], 'payment': 1029, 'final_payment': 1026, 'total_interest': 0}], ['control 2', {'principal': 3000000, 'rate_bp': 0, 'io_months': 12, 'amort_months': 60, 'fund_day': 2}, {'io_bills': [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], 'payment': 50000, 'final_payment': 50000, 'total_interest': 0}], ['control 3', {'principal': 250000, 'rate_bp': 0, 'io_months': 2, 'amort_months': 24, 'fund_day': 15}, {'io_bills': [0, 0], 'payment': 10417, 'final_payment': 10409, 'total_interest': 0}], ['control 4', {'principal': 3000000, 'rate_bp': 0, 'io_months': 2, 'amort_months': 1, 'fund_day': 2}, {'io_bills': [0, 0], 'payment': 3000000, 'final_payment': 3000000, 'total_interest': 0}], ['control 5', {'principal': 12345, 'rate_bp': 0, 'io_months': 2, 'amort_months': 60, 'fund_day': 2}, {'io_bills': [0, 0], 'payment': 206, 'final_payment': 191, 'total_interest': 0}], ['control 6', {'principal': 250000, 'rate_bp': 0, 'io_months': 3, 'amort_months': 12, 'fund_day': 1}, {'io_bills': [0, 0, 0], 'payment': 20833, 'final_payment': 20837, 'total_interest': 0}]], [['regression: stub day count', {'principal': 3000000, 'rate_bp': 500, 'io_months': 2, 'amort_months': 24, 'fund_day': 29}, {'io_bills': [833, 12500], 'payment': 131614, 'final_payment': 131618, 'total_interest': 172073}], ['control 1', {'principal': 100000, 'rate_bp': 0, 'io_months': 12, 'amort_months': 24, 'fund_day': 29}, {'io_bills': [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], 'payment': 4167, 'final_payment': 4159, 'total_interest': 0}], ['control 2', {'principal': 12345, 'rate_bp': 0, 'io_months': 2, 'amort_months': 24, 'fund_day': 30}, {'io_bills': [0, 0], 'payment': 514, 'final_payment': 523, 'total_interest': 0}], ['control 3', {'principal': 250000, 'rate_bp': 0, 'io_months': 1, 'amort_months': 1, 'fund_day': 30}, {'io_bills': [0], 'payment': 250000, 'final_payment': 250000, 'total_interest': 0}], ['control 4', {'principal': 3000000, 'rate_bp': 0, 'io_months': 1, 'amort_months': 1, 'fund_day': 29}, {'io_bills': [0], 'payment': 3000000, 'final_payment': 3000000, 'total_interest': 0}], ['control 5', {'principal': 12345, 'rate_bp': 0, 'io_months': 2, 'amort_months': 60, 'fund_day': 15}, {'io_bills': [0, 0], 'payment': 206, 'final_payment': 191, 'total_interest': 0}], ['control 6', {'principal': 3000000, 'rate_bp': 0, 'io_months': 1, 'amort_months': 1, 'fund_day': 1}, {'io_bills': [0], 'payment': 3000000, 'final_payment': 3000000, 'total_interest': 0}]], [['regression: stub day count', {'principal': 250000, 'rate_bp': 500, 'io_months': 12, 'amort_months': 60, 'fund_day': 15}, {'io_bills': [556, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042], 'payment': 4718, 'final_payment': 4703, 'total_interest': 45083}], ['control 1', {'principal': 100000, 'rate_bp': 0, 'io_months': 12, 'amort_months': 60, 'fund_day': 29}, {'io_bills': [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], 'payment': 1667, 'final_payment': 1647, 'total_interest': 0}], ['control 2', {'principal': 250000, 'rate_bp': 0, 'io_months': 3, 'amort_months': 1, 'fund_day': 2}, {'io_bills': [0, 0, 0], 'payment': 250000, 'final_payment': 250000, 'total_interest': 0}], ['control 3', {'principal': 12345, 'rate_bp': 0, 'io_months': 12, 'amort_months': 60, 'fund_day': 15}, {'io_bills': [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], 'payment': 206, 'final_payment': 191, 'total_interest': 0}], ['control 4', {'principal': 12345, 'rate_bp': 0, 'io_months': 2, 'amort_months': 24, 'fund_day': 15}, {'io_bills': [0, 0], 'payment': 514, 'final_payment': 523, 'total_interest': 0}], ['control 5', {'principal': 3000000, 'rate_bp': 0, 'io_months': 1, 'amort_months': 1, 'fund_day': 29}, {'io_bills': [0], 'payment': 3000000, 'final_payment': 3000000, 'total_interest': 0}], ['control 6', {'principal': 250000, 'rate_bp': 0, 'io_months': 12, 'amort_months': 60, 'fund_day': 30}, {'io_bills': [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], 'payment': 4167, 'final_payment': 4147, 'total_interest': 0}]], [['regression: stub day count', {'principal': 12345, 'rate_bp': 725, 'io_months': 1, 'amort_months': 12, 'fund_day': 2}, {'io_bills': [72], 'payment': 1070, 'final_payment': 1065, 'total_interest': 562}], ['control 1', {'principal': 100000, 'rate_bp': 0, 'io_months': 12, 'amort_months': 24, 'fund_day': 30}, {'io_bills': [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], 'payment': 4167, 'final_payment': 4159, 'total_interest': 0}], ['control 2', {'principal': 3000000, 'rate_bp': 0, 'io_months': 12, 'amort_months': 24, 'fund_day': 15}, {'io_bills': [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], 'payment': 125000, 'final_payment': 125000, 'total_interest': 0}], ['control 3', {'principal': 12345, 'rate_bp': 0, 'io_months': 3, 'amort_months': 24, 'fund_day': 15}, {'io_bills': [0, 0, 0], 'payment': 514, 'final_payment': 523, 'total_interest': 0}], ['control 4', {'principal': 3000000, 'rate_bp': 0, 'io_months': 2, 'amort_months': 1, 'fund_day': 15}, {'io_bills': [0, 0], 'payment': 3000000, 'final_payment': 3000000, 'total_interest': 0}], ['control 5', {'principal': 12345, 'rate_bp': 0, 'io_months': 2, 'amort_months': 12, 'fund_day': 30}, {'io_bills': [0, 0], 'payment': 1029, 'final_payment': 1026, 'total_interest': 0}], ['control 6', {'principal': 250000, 'rate_bp': 0, 'io_months': 1, 'amort_months': 60, 'fund_day': 1}, {'io_bills': [0], 'payment': 4167, 'final_payment': 4147, 'total_interest': 0}]]]
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)

meaning: Fixture-count band, not measured model difficulty

deps: stdlib

source:
"""Failure Map reference implementation. Python standard library only."""
import json
import math
from fractions import Fraction
N = 1
observations = []
def solve(x):
    def rnd(n, d):
        q, r = divmod(n, d)
        return q + (1 if 2 * r >= d else 0)
    def level(P, n):
        if n <= 0 or P <= 0:
            return 0
        if bp == 0:
            exact = Fraction(P, n)
        else:
            r = Fraction(bp, 120000)
            exact = P * r / (1 - (1 + r) ** -n)
        return math.floor(exact + Fraction(1, 2))
    bp = x['rate_bp']
    P = x['principal']
    io_bills = []
    total_int = 0
    for m in range(x['io_months']):
        days = x['fund_day'] if m == 0 else 30
        interest = rnd(P * bp * days, 120000 * 30)
        io_bills.append(interest)
        total_int += interest
    pay = level(P, x['amort_months'])
    bal = P
    final = 0
    for k in range(1, x['amort_months'] + 1):
        interest = rnd(bal * bp, 120000)
        total_int += interest
        if k == x['amort_months']:
            final = bal + interest
        else:
            bal -= pay - interest
    return {'io_bills': io_bills, 'payment': pay, 'final_payment': final, 'total_interest': total_int}
def check(label, actual, expected):
    observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
fixtures = [[['regression: stub day count', {'principal': 3000000, 'rate_bp': 500, 'io_months': 3, 'amort_months': 60, 'fund_day': 1}, {'io_bills': [12500, 12500, 12500], 'payment': 56614, 'final_payment': 56596, 'total_interest': 434322}], ['control 1', {'principal': 12345, 'rate_bp': 0, 'io_months': 1, 'amort_months': 24, 'fund_day': 2}, {'io_bills': [0], 'payment': 514, 'final_payment': 523, 'total_interest': 0}], ['control 2', {'principal': 12345, 'rate_bp': 0, 'io_months': 1, 'amort_months': 1, 'fund_day': 2}, {'io_bills': [0], 'payment': 12345, 'final_payment': 12345, 'total_interest': 0}], ['control 3', {'principal': 250000, 'rate_bp': 0, 'io_months': 2, 'amort_months': 60, 'fund_day': 15}, {'io_bills': [0, 0], 'payment': 4167, 'final_payment': 4147, 'total_interest': 0}], ['control 4', {'principal': 100000, 'rate_bp': 0, 'io_months': 3, 'amort_months': 60, 'fund_day': 15}, {'io_bills': [0, 0, 0], 'payment': 1667, 'final_payment': 1647, 'total_interest': 0}], ['control 5', {'principal': 100000, 'rate_bp': 0, 'io_months': 1, 'amort_months': 12, 'fund_day': 29}, {'io_bills': [0], 'payment': 8333, 'final_payment': 8337, 'total_interest': 0}], ['control 6', {'principal': 100000, 'rate_bp': 0, 'io_months': 3, 'amort_months': 1, 'fund_day': 30}, {'io_bills': [0, 0, 0], 'payment': 100000, 'final_payment': 100000, 'total_interest': 0}]], [['regression: stub day count', {'principal': 100000, 'rate_bp': 725, 'io_months': 2, 'amort_months': 60, 'fund_day': 1}, {'io_bills': [604, 604], 'payment': 1992, 'final_payment': 1987, 'total_interest': 20723}], ['control 1', {'principal': 12345, 'rate_bp': 0, 'io_months': 3, 'amort_months': 12, 'fund_day': 1}, {'io_bills': [0, 0, 0], 'payment': 1029, 'final_payment': 1026, 'total_interest': 0}], ['control 2', {'principal': 3000000, 'rate_bp': 0, 'io_months': 12, 'amort_months': 60, 'fund_day': 2}, {'io_bills': [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], 'payment': 50000, 'final_payment': 50000, 'total_interest': 0}], ['control 3', {'principal': 250000, 'rate_bp': 0, 'io_months': 2, 'amort_months': 24, 'fund_day': 15}, {'io_bills': [0, 0], 'payment': 10417, 'final_payment': 10409, 'total_interest': 0}], ['control 4', {'principal': 3000000, 'rate_bp': 0, 'io_months': 2, 'amort_months': 1, 'fund_day': 2}, {'io_bills': [0, 0], 'payment': 3000000, 'final_payment': 3000000, 'total_interest': 0}], ['control 5', {'principal': 12345, 'rate_bp': 0, 'io_months': 2, 'amort_months': 60, 'fund_day': 2}, {'io_bills': [0, 0], 'payment': 206, 'final_payment': 191, 'total_interest': 0}], ['control 6', {'principal': 250000, 'rate_bp': 0, 'io_months': 3, 'amort_months': 12, 'fund_day': 1}, {'io_bills': [0, 0, 0], 'payment': 20833, 'final_payment': 20837, 'total_interest': 0}]], [['regression: stub day count', {'principal': 3000000, 'rate_bp': 500, 'io_months': 2, 'amort_months': 24, 'fund_day': 29}, {'io_bills': [833, 12500], 'payment': 131614, 'final_payment': 131618, 'total_interest': 172073}], ['control 1', {'principal': 100000, 'rate_bp': 0, 'io_months': 12, 'amort_months': 24, 'fund_day': 29}, {'io_bills': [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], 'payment': 4167, 'final_payment': 4159, 'total_interest': 0}], ['control 2', {'principal': 12345, 'rate_bp': 0, 'io_months': 2, 'amort_months': 24, 'fund_day': 30}, {'io_bills': [0, 0], 'payment': 514, 'final_payment': 523, 'total_interest': 0}], ['control 3', {'principal': 250000, 'rate_bp': 0, 'io_months': 1, 'amort_months': 1, 'fund_day': 30}, {'io_bills': [0], 'payment': 250000, 'final_payment': 250000, 'total_interest': 0}], ['control 4', {'principal': 3000000, 'rate_bp': 0, 'io_months': 1, 'amort_months': 1, 'fund_day': 29}, {'io_bills': [0], 'payment': 3000000, 'final_payment': 3000000, 'total_interest': 0}], ['control 5', {'principal': 12345, 'rate_bp': 0, 'io_months': 2, 'amort_months': 60, 'fund_day': 15}, {'io_bills': [0, 0], 'payment': 206, 'final_payment': 191, 'total_interest': 0}], ['control 6', {'principal': 3000000, 'rate_bp': 0, 'io_months': 1, 'amort_months': 1, 'fund_day': 1}, {'io_bills': [0], 'payment': 3000000, 'final_payment': 3000000, 'total_interest': 0}]], [['regression: stub day count', {'principal': 250000, 'rate_bp': 500, 'io_months': 12, 'amort_months': 60, 'fund_day': 15}, {'io_bills': [556, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042], 'payment': 4718, 'final_payment': 4703, 'total_interest': 45083}], ['control 1', {'principal': 100000, 'rate_bp': 0, 'io_months': 12, 'amort_months': 60, 'fund_day': 29}, {'io_bills': [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], 'payment': 1667, 'final_payment': 1647, 'total_interest': 0}], ['control 2', {'principal': 250000, 'rate_bp': 0, 'io_months': 3, 'amort_months': 1, 'fund_day': 2}, {'io_bills': [0, 0, 0], 'payment': 250000, 'final_payment': 250000, 'total_interest': 0}], ['control 3', {'principal': 12345, 'rate_bp': 0, 'io_months': 12, 'amort_months': 60, 'fund_day': 15}, {'io_bills': [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], 'payment': 206, 'final_payment': 191, 'total_interest': 0}], ['control 4', {'principal': 12345, 'rate_bp': 0, 'io_months': 2, 'amort_months': 24, 'fund_day': 15}, {'io_bills': [0, 0], 'payment': 514, 'final_payment': 523, 'total_interest': 0}], ['control 5', {'principal': 3000000, 'rate_bp': 0, 'io_months': 1, 'amort_months': 1, 'fund_day': 29}, {'io_bills': [0], 'payment': 3000000, 'final_payment': 3000000, 'total_interest': 0}], ['control 6', {'principal': 250000, 'rate_bp': 0, 'io_months': 12, 'amort_months': 60, 'fund_day': 30}, {'io_bills': [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], 'payment': 4167, 'final_payment': 4147, 'total_interest': 0}]], [['regression: stub day count', {'principal': 12345, 'rate_bp': 725, 'io_months': 1, 'amort_months': 12, 'fund_day': 2}, {'io_bills': [72], 'payment': 1070, 'final_payment': 1065, 'total_interest': 562}], ['control 1', {'principal': 100000, 'rate_bp': 0, 'io_months': 12, 'amort_months': 24, 'fund_day': 30}, {'io_bills': [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], 'payment': 4167, 'final_payment': 4159, 'total_interest': 0}], ['control 2', {'principal': 3000000, 'rate_bp': 0, 'io_months': 12, 'amort_months': 24, 'fund_day': 15}, {'io_bills': [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], 'payment': 125000, 'final_payment': 125000, 'total_interest': 0}], ['control 3', {'principal': 12345, 'rate_bp': 0, 'io_months': 3, 'amort_months': 24, 'fund_day': 15}, {'io_bills': [0, 0, 0], 'payment': 514, 'final_payment': 523, 'total_interest': 0}], ['control 4', {'principal': 3000000, 'rate_bp': 0, 'io_months': 2, 'amort_months': 1, 'fund_day': 15}, {'io_bills': [0, 0], 'payment': 3000000, 'final_payment': 3000000, 'total_interest': 0}], ['control 5', {'principal': 12345, 'rate_bp': 0, 'io_months': 2, 'amort_months': 12, 'fund_day': 30}, {'io_bills': [0, 0], 'payment': 1029, 'final_payment': 1026, 'total_interest': 0}], ['control 6', {'principal': 250000, 'rate_bp': 0, 'io_months': 1, 'amort_months': 60, 'fund_day': 1}, {'io_bills': [0], 'payment': 4167, 'final_payment': 4147, 'total_interest': 0}]]]
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)

prompt:
The first interest-only bill misses the funding day.

x = {'principal', 'rate_bp', 'io_months' >= 1, 'amort_months' >= 1, 'fund_day' 1..30}. IO month m pays interest round_half_up(P*bp*days/(120000*30)) with days = 31 - fund_day for the first IO month and 30 otherwise. Then the level payment on the full principal over amort_months (exact annuity, half-up) amortizes with monthly interest round_half_up(balance*bp/120000); the last month pays balance + interest. Return {'io_bills': list of IO payments, 'payment', 'final_payment', 'total_interest'}.

split: open-access


broken_source:
"""Failure Map reference implementation. Python standard library only."""
import json
import math
from fractions import Fraction
N = 1
observations = []
def solve(x):
    def rnd(n, d):
        q, r = divmod(n, d)
        return q + (1 if 2 * r >= d else 0)
    def level(P, n):
        if n <= 0 or P <= 0:
            return 0
        if bp == 0:
            exact = Fraction(P, n)
        else:
            r = Fraction(bp, 120000)
            exact = P * r / (1 - (1 + r) ** -n)
        return math.floor(exact + Fraction(1, 2))
    bp = x['rate_bp']
    P = x['principal']
    io_bills = []
    total_int = 0
    for m in range(x['io_months']):
        days = 31 - x['fund_day']
        interest = rnd(P * bp * days, 120000 * 30)
        io_bills.append(interest)
        total_int += interest
    pay = level(P, x['amort_months'])
    bal = P
    final = 0
    for k in range(1, x['amort_months'] + 1):
        interest = rnd(bal * bp, 120000)
        total_int += interest
        if k == x['amort_months']:
            final = bal + interest
        else:
            bal -= pay - interest
    return {'io_bills': io_bills, 'payment': pay, 'final_payment': final, 'total_interest': total_int}
def check(label, actual, expected):
    observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
fixtures = [[['regression: stub month selection', {'principal': 12345, 'rate_bp': 725, 'io_months': 12, 'amort_months': 12, 'fund_day': 15}, {'io_bills': [40, 75, 75, 75, 75, 75, 75, 75, 75, 75, 75, 75], 'payment': 1070, 'final_payment': 1065, 'total_interest': 1355}], ['control 1', {'principal': 3000000, 'rate_bp': 500, 'io_months': 3, 'amort_months': 60, 'fund_day': 1}, {'io_bills': [12500, 12500, 12500], 'payment': 56614, 'final_payment': 56596, 'total_interest': 434322}], ['control 2', {'principal': 12345, 'rate_bp': 0, 'io_months': 1, 'amort_months': 24, 'fund_day': 2}, {'io_bills': [0], 'payment': 514, 'final_payment': 523, 'total_interest': 0}], ['control 3', {'principal': 3000000, 'rate_bp': 725, 'io_months': 2, 'amort_months': 1, 'fund_day': 1}, {'io_bills': [18125, 18125], 'payment': 3018125, 'final_payment': 3018125, 'total_interest': 54375}], ['control 4', {'principal': 12345, 'rate_bp': 0, 'io_months': 1, 'amort_months': 1, 'fund_day': 2}, {'io_bills': [0], 'payment': 12345, 'final_payment': 12345, 'total_interest': 0}], ['control 5', {'principal': 12345, 'rate_bp': 725, 'io_months': 1, 'amort_months': 60, 'fund_day': 2}, {'io_bills': [72], 'payment': 246, 'final_payment': 239, 'total_interest': 2480}], ['control 6', {'principal': 250000, 'rate_bp': 0, 'io_months': 2, 'amort_months': 60, 'fund_day': 15}, {'io_bills': [0, 0], 'payment': 4167, 'final_payment': 4147, 'total_interest': 0}]], [['regression: stub month selection', {'principal': 250000, 'rate_bp': 500, 'io_months': 2, 'amort_months': 60, 'fund_day': 30}, {'io_bills': [35, 1042], 'payment': 4718, 'final_payment': 4703, 'total_interest': 34142}], ['control 1', {'principal': 12345, 'rate_bp': 0, 'io_months': 3, 'amort_months': 12, 'fund_day': 1}, {'io_bills': [0, 0, 0], 'payment': 1029, 'final_payment': 1026, 'total_interest': 0}], ['control 2', {'principal': 100000, 'rate_bp': 725, 'io_months': 2, 'amort_months': 60, 'fund_day': 1}, {'io_bills': [604, 604], 'payment': 1992, 'final_payment': 1987, 'total_interest': 20723}], ['control 3', {'principal': 3000000, 'rate_bp': 0, 'io_months': 12, 'amort_months': 60, 'fund_day': 2}, {'io_bills': [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], 'payment': 50000, 'final_payment': 50000, 'total_interest': 0}], ['control 4', {'principal': 3000000, 'rate_bp': 500, 'io_months': 3, 'amort_months': 12, 'fund_day': 1}, {'io_bills': [12500, 12500, 12500], 'payment': 256822, 'final_payment': 256827, 'total_interest': 119369}], ['control 5', {'principal': 250000, 'rate_bp': 0, 'io_months': 2, 'amort_months': 24, 'fund_day': 15}, {'io_bills': [0, 0], 'payment': 10417, 'final_payment': 10409, 'total_interest': 0}], ['control 6', {'principal': 3000000, 'rate_bp': 0, 'io_months': 2, 'amort_months': 1, 'fund_day': 2}, {'io_bills': [0, 0], 'payment': 3000000, 'final_payment': 3000000, 'total_interest': 0}]], [['regression: stub month selection', {'principal': 3000000, 'rate_bp': 500, 'io_months': 2, 'amort_months': 24, 'fund_day': 29}, {'io_bills': [833, 12500], 'payment': 131614, 'final_payment': 131618, 'total_interest': 172073}], ['control 1', {'principal': 3000000, 'rate_bp': 500, 'io_months': 3, 'amort_months': 24, 'fund_day': 1}, {'io_bills': [12500, 12500, 12500], 'payment': 131614, 'final_payment': 131618, 'total_interest': 196240}], ['control 2', {'principal': 100000, 'rate_bp': 0, 'io_months': 12, 'amort_months': 24, 'fund_day': 29}, {'io_bills': [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], 'payment': 4167, 'final_payment': 4159, 'total_interest': 0}], ['control 3', {'principal': 12345, 'rate_bp': 0, 'io_months': 2, 'amort_months': 24, 'fund_day': 30}, {'io_bills': [0, 0], 'payment': 514, 'final_payment': 523, 'total_interest': 0}], ['control 4', {'principal': 250000, 'rate_bp': 0, 'io_months': 1, 'amort_months': 1, 'fund_day': 30}, {'io_bills': [0], 'payment': 250000, 'final_payment': 250000, 'total_interest': 0}], ['control 5', {'principal': 250000, 'rate_bp': 500, 'io_months': 2, 'amort_months': 12, 'fund_day': 1}, {'io_bills': [1042, 1042], 'payment': 21402, 'final_payment': 21400, 'total_interest': 8906}], ['control 6', {'principal': 250000, 'rate_bp': 1200, 'io_months': 1, 'amort_months': 1, 'fund_day': 15}, {'io_bills': [1333], 'payment': 252500, 'final_payment': 252500, 'total_interest': 3833}]], [['regression: stub month selection', {'principal': 250000, 'rate_bp': 500, 'io_months': 12, 'amort_months': 60, 'fund_day': 15}, {'io_bills': [556, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042], 'payment': 4718, 'final_payment': 4703, 'total_interest': 45083}], ['control 1', {'principal': 100000, 'rate_bp': 0, 'io_months': 12, 'amort_months': 60, 'fund_day': 29}, {'io_bills': [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], 'payment': 1667, 'final_payment': 1647, 'total_interest': 0}], ['control 2', {'principal': 250000, 'rate_bp': 0, 'io_months': 3, 'amort_months': 1, 'fund_day': 2}, {'io_bills': [0, 0, 0], 'payment': 250000, 'final_payment': 250000, 'total_interest': 0}], ['control 3', {'principal': 100000, 'rate_bp': 500, 'io_months': 1, 'amort_months': 12, 'fund_day': 30}, {'io_bills': [14], 'payment': 8561, 'final_payment': 8559, 'total_interest': 2744}], ['control 4', {'principal': 12345, 'rate_bp': 0, 'io_months': 12, 'amort_months': 60, 'fund_day': 15}, {'io_bills': [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], 'payment': 206, 'final_payment': 191, 'total_interest': 0}], ['control 5', {'principal': 100000, 'rate_bp': 725, 'io_months': 3, 'amort_months': 12, 'fund_day': 1}, {'io_bills': [604, 604, 604], 'payment': 8664, 'final_payment': 8666, 'total_interest': 5782}], ['control 6', {'principal': 12345, 'rate_bp': 0, 'io_months': 2, 'amort_months': 24, 'fund_day': 15}, {'io_bills': [0, 0], 'payment': 514, 'final_payment': 523, 'total_interest': 0}]], [['regression: stub month selection', {'principal': 100000, 'rate_bp': 500, 'io_months': 3, 'amort_months': 60, 'fund_day': 2}, {'io_bills': [403, 417, 417], 'payment': 1887, 'final_payment': 1894, 'total_interest': 14464}], ['control 1', {'principal': 12345, 'rate_bp': 725, 'io_months': 1, 'amort_months': 12, 'fund_day': 2}, {'io_bills': [72], 'payment': 1070, 'final_payment': 1065, 'total_interest': 562}], ['control 2', {'principal': 3000000, 'rate_bp': 725, 'io_months': 1, 'amort_months': 60, 'fund_day': 29}, {'io_bills': [1208], 'payment': 59758, 'final_payment': 59764, 'total_interest': 586694}], ['control 3', {'principal': 250000, 'rate_bp': 725, 'io_months': 1, 'amort_months': 60, 'fund_day': 15}, {'io_bills': [806], 'payment': 4980, 'final_payment': 4967, 'total_interest': 49593}], ['control 4', {'principal': 100000, 'rate_bp': 0, 'io_months': 12, 'amort_months': 24, 'fund_day': 30}, {'io_bills': [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], 'payment': 4167, 'final_payment': 4159, 'total_interest': 0}], ['control 5', {'principal': 3000000, 'rate_bp': 0, 'io_months': 12, 'amort_months': 24, 'fund_day': 15}, {'io_bills': [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], 'payment': 125000, 'final_payment': 125000, 'total_interest': 0}], ['control 6', {'principal': 3000000, 'rate_bp': 500, 'io_months': 1, 'amort_months': 12, 'fund_day': 30}, {'io_bills': [417], 'payment': 256822, 'final_payment': 256827, 'total_interest': 82286}]]]
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)

source:
"""Failure Map reference implementation. Python standard library only."""
import json
import math
from fractions import Fraction
N = 1
observations = []
def solve(x):
    def rnd(n, d):
        q, r = divmod(n, d)
        return q + (1 if 2 * r >= d else 0)
    def level(P, n):
        if n <= 0 or P <= 0:
            return 0
        if bp == 0:
            exact = Fraction(P, n)
        else:
            r = Fraction(bp, 120000)
            exact = P * r / (1 - (1 + r) ** -n)
        return math.floor(exact + Fraction(1, 2))
    bp = x['rate_bp']
    P = x['principal']
    io_bills = []
    total_int = 0
    for m in range(x['io_months']):
        days = 31 - x['fund_day'] if m == x['io_months'] - 1 else 30
        interest = rnd(P * bp * days, 120000 * 30)
        io_bills.append(interest)
        total_int += interest
    pay = level(P, x['amort_months'])
    bal = P
    final = 0
    for k in range(1, x['amort_months'] + 1):
        interest = rnd(bal * bp, 120000)
        total_int += interest
        if k == x['amort_months']:
            final = bal + interest
        else:
            bal -= pay - interest
    return {'io_bills': io_bills, 'payment': pay, 'final_payment': final, 'total_interest': total_int}
def check(label, actual, expected):
    observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
fixtures = [[['regression: stub month selection', {'principal': 12345, 'rate_bp': 725, 'io_months': 12, 'amort_months': 12, 'fund_day': 15}, {'io_bills': [40, 75, 75, 75, 75, 75, 75, 75, 75, 75, 75, 75], 'payment': 1070, 'final_payment': 1065, 'total_interest': 1355}], ['control 1', {'principal': 3000000, 'rate_bp': 500, 'io_months': 3, 'amort_months': 60, 'fund_day': 1}, {'io_bills': [12500, 12500, 12500], 'payment': 56614, 'final_payment': 56596, 'total_interest': 434322}], ['control 2', {'principal': 12345, 'rate_bp': 0, 'io_months': 1, 'amort_months': 24, 'fund_day': 2}, {'io_bills': [0], 'payment': 514, 'final_payment': 523, 'total_interest': 0}], ['control 3', {'principal': 3000000, 'rate_bp': 725, 'io_months': 2, 'amort_months': 1, 'fund_day': 1}, {'io_bills': [18125, 18125], 'payment': 3018125, 'final_payment': 3018125, 'total_interest': 54375}], ['control 4', {'principal': 12345, 'rate_bp': 0, 'io_months': 1, 'amort_months': 1, 'fund_day': 2}, {'io_bills': [0], 'payment': 12345, 'final_payment': 12345, 'total_interest': 0}], ['control 5', {'principal': 12345, 'rate_bp': 725, 'io_months': 1, 'amort_months': 60, 'fund_day': 2}, {'io_bills': [72], 'payment': 246, 'final_payment': 239, 'total_interest': 2480}], ['control 6', {'principal': 250000, 'rate_bp': 0, 'io_months': 2, 'amort_months': 60, 'fund_day': 15}, {'io_bills': [0, 0], 'payment': 4167, 'final_payment': 4147, 'total_interest': 0}]], [['regression: stub month selection', {'principal': 250000, 'rate_bp': 500, 'io_months': 2, 'amort_months': 60, 'fund_day': 30}, {'io_bills': [35, 1042], 'payment': 4718, 'final_payment': 4703, 'total_interest': 34142}], ['control 1', {'principal': 12345, 'rate_bp': 0, 'io_months': 3, 'amort_months': 12, 'fund_day': 1}, {'io_bills': [0, 0, 0], 'payment': 1029, 'final_payment': 1026, 'total_interest': 0}], ['control 2', {'principal': 100000, 'rate_bp': 725, 'io_months': 2, 'amort_months': 60, 'fund_day': 1}, {'io_bills': [604, 604], 'payment': 1992, 'final_payment': 1987, 'total_interest': 20723}], ['control 3', {'principal': 3000000, 'rate_bp': 0, 'io_months': 12, 'amort_months': 60, 'fund_day': 2}, {'io_bills': [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], 'payment': 50000, 'final_payment': 50000, 'total_interest': 0}], ['control 4', {'principal': 3000000, 'rate_bp': 500, 'io_months': 3, 'amort_months': 12, 'fund_day': 1}, {'io_bills': [12500, 12500, 12500], 'payment': 256822, 'final_payment': 256827, 'total_interest': 119369}], ['control 5', {'principal': 250000, 'rate_bp': 0, 'io_months': 2, 'amort_months': 24, 'fund_day': 15}, {'io_bills': [0, 0], 'payment': 10417, 'final_payment': 10409, 'total_interest': 0}], ['control 6', {'principal': 3000000, 'rate_bp': 0, 'io_months': 2, 'amort_months': 1, 'fund_day': 2}, {'io_bills': [0, 0], 'payment': 3000000, 'final_payment': 3000000, 'total_interest': 0}]], [['regression: stub month selection', {'principal': 3000000, 'rate_bp': 500, 'io_months': 2, 'amort_months': 24, 'fund_day': 29}, {'io_bills': [833, 12500], 'payment': 131614, 'final_payment': 131618, 'total_interest': 172073}], ['control 1', {'principal': 3000000, 'rate_bp': 500, 'io_months': 3, 'amort_months': 24, 'fund_day': 1}, {'io_bills': [12500, 12500, 12500], 'payment': 131614, 'final_payment': 131618, 'total_interest': 196240}], ['control 2', {'principal': 100000, 'rate_bp': 0, 'io_months': 12, 'amort_months': 24, 'fund_day': 29}, {'io_bills': [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], 'payment': 4167, 'final_payment': 4159, 'total_interest': 0}], ['control 3', {'principal': 12345, 'rate_bp': 0, 'io_months': 2, 'amort_months': 24, 'fund_day': 30}, {'io_bills': [0, 0], 'payment': 514, 'final_payment': 523, 'total_interest': 0}], ['control 4', {'principal': 250000, 'rate_bp': 0, 'io_months': 1, 'amort_months': 1, 'fund_day': 30}, {'io_bills': [0], 'payment': 250000, 'final_payment': 250000, 'total_interest': 0}], ['control 5', {'principal': 250000, 'rate_bp': 500, 'io_months': 2, 'amort_months': 12, 'fund_day': 1}, {'io_bills': [1042, 1042], 'payment': 21402, 'final_payment': 21400, 'total_interest': 8906}], ['control 6', {'principal': 250000, 'rate_bp': 1200, 'io_months': 1, 'amort_months': 1, 'fund_day': 15}, {'io_bills': [1333], 'payment': 252500, 'final_payment': 252500, 'total_interest': 3833}]], [['regression: stub month selection', {'principal': 250000, 'rate_bp': 500, 'io_months': 12, 'amort_months': 60, 'fund_day': 15}, {'io_bills': [556, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042], 'payment': 4718, 'final_payment': 4703, 'total_interest': 45083}], ['control 1', {'principal': 100000, 'rate_bp': 0, 'io_months': 12, 'amort_months': 60, 'fund_day': 29}, {'io_bills': [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], 'payment': 1667, 'final_payment': 1647, 'total_interest': 0}], ['control 2', {'principal': 250000, 'rate_bp': 0, 'io_months': 3, 'amort_months': 1, 'fund_day': 2}, {'io_bills': [0, 0, 0], 'payment': 250000, 'final_payment': 250000, 'total_interest': 0}], ['control 3', {'principal': 100000, 'rate_bp': 500, 'io_months': 1, 'amort_months': 12, 'fund_day': 30}, {'io_bills': [14], 'payment': 8561, 'final_payment': 8559, 'total_interest': 2744}], ['control 4', {'principal': 12345, 'rate_bp': 0, 'io_months': 12, 'amort_months': 60, 'fund_day': 15}, {'io_bills': [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], 'payment': 206, 'final_payment': 191, 'total_interest': 0}], ['control 5', {'principal': 100000, 'rate_bp': 725, 'io_months': 3, 'amort_months': 12, 'fund_day': 1}, {'io_bills': [604, 604, 604], 'payment': 8664, 'final_payment': 8666, 'total_interest': 5782}], ['control 6', {'principal': 12345, 'rate_bp': 0, 'io_months': 2, 'amort_months': 24, 'fund_day': 15}, {'io_bills': [0, 0], 'payment': 514, 'final_payment': 523, 'total_interest': 0}]], [['regression: stub month selection', {'principal': 100000, 'rate_bp': 500, 'io_months': 3, 'amort_months': 60, 'fund_day': 2}, {'io_bills': [403, 417, 417], 'payment': 1887, 'final_payment': 1894, 'total_interest': 14464}], ['control 1', {'principal': 12345, 'rate_bp': 725, 'io_months': 1, 'amort_months': 12, 'fund_day': 2}, {'io_bills': [72], 'payment': 1070, 'final_payment': 1065, 'total_interest': 562}], ['control 2', {'principal': 3000000, 'rate_bp': 725, 'io_months': 1, 'amort_months': 60, 'fund_day': 29}, {'io_bills': [1208], 'payment': 59758, 'final_payment': 59764, 'total_interest': 586694}], ['control 3', {'principal': 250000, 'rate_bp': 725, 'io_months': 1, 'amort_months': 60, 'fund_day': 15}, {'io_bills': [806], 'payment': 4980, 'final_payment': 4967, 'total_interest': 49593}], ['control 4', {'principal': 100000, 'rate_bp': 0, 'io_months': 12, 'amort_months': 24, 'fund_day': 30}, {'io_bills': [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], 'payment': 4167, 'final_payment': 4159, 'total_interest': 0}], ['control 5', {'principal': 3000000, 'rate_bp': 0, 'io_months': 12, 'amort_months': 24, 'fund_day': 15}, {'io_bills': [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], 'payment': 125000, 'final_payment': 125000, 'total_interest': 0}], ['control 6', {'principal': 3000000, 'rate_bp': 500, 'io_months': 1, 'amort_months': 12, 'fund_day': 30}, {'io_bills': [417], 'payment': 256822, 'final_payment': 256827, 'total_interest': 82286}]]]
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)

prompt:
Every interest-only bill is prorated like the funding month.

x = {'principal', 'rate_bp', 'io_months' >= 1, 'amort_months' >= 1, 'fund_day' 1..30}. IO month m pays interest round_half_up(P*bp*days/(120000*30)) with days = 31 - fund_day for the first IO month and 30 otherwise. Then the level payment on the full principal over amort_months (exact annuity, half-up) amortizes with monthly interest round_half_up(balance*bp/120000); the last month pays balance + interest. Return {'io_bills': list of IO payments, 'payment', 'final_payment', 'total_interest'}.

split: open-access


broken_source:
"""Failure Map reference implementation. Python standard library only."""
import json
import math
from fractions import Fraction
N = 1
observations = []
def solve(x):
    def rnd(n, d):
        q, r = divmod(n, d)
        return q + (1 if 2 * r >= d else 0)
    def level(P, n):
        if n <= 0 or P <= 0:
            return 0
        if bp == 0:
            exact = Fraction(P, n)
        else:
            r = Fraction(bp, 120000)
            exact = P * r / (1 - (1 + r) ** -n)
        return math.floor(exact + Fraction(1, 2))
    bp = x['rate_bp']
    P = x['principal']
    io_bills = []
    total_int = 0
    for m in range(x['io_months']):
        days = 31 - x['fund_day'] if m == 0 else 30
        interest = rnd(P * bp * days, 120000 * 30)
        io_bills.append(interest)
    pay = level(P, x['amort_months'])
    bal = P
    final = 0
    for k in range(1, x['amort_months'] + 1):
        interest = rnd(bal * bp, 120000)
        total_int += interest
        if k == x['amort_months']:
            final = bal + interest
        else:
            bal -= pay - interest
    return {'io_bills': io_bills, 'payment': pay, 'final_payment': final, 'total_interest': total_int}
def check(label, actual, expected):
    observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
fixtures = [[['regression: total interest coverage', {'principal': 3000000, 'rate_bp': 500, 'io_months': 3, 'amort_months': 60, 'fund_day': 1}, {'io_bills': [12500, 12500, 12500], 'payment': 56614, 'final_payment': 56596, 'total_interest': 434322}], ['control 1', {'principal': 12345, 'rate_bp': 0, 'io_months': 1, 'amort_months': 24, 'fund_day': 2}, {'io_bills': [0], 'payment': 514, 'final_payment': 523, 'total_interest': 0}], ['control 2', {'principal': 12345, 'rate_bp': 0, 'io_months': 1, 'amort_months': 1, 'fund_day': 2}, {'io_bills': [0], 'payment': 12345, 'final_payment': 12345, 'total_interest': 0}], ['control 3', {'principal': 250000, 'rate_bp': 0, 'io_months': 2, 'amort_months': 60, 'fund_day': 15}, {'io_bills': [0, 0], 'payment': 4167, 'final_payment': 4147, 'total_interest': 0}], ['control 4', {'principal': 100000, 'rate_bp': 0, 'io_months': 3, 'amort_months': 60, 'fund_day': 15}, {'io_bills': [0, 0, 0], 'payment': 1667, 'final_payment': 1647, 'total_interest': 0}], ['control 5', {'principal': 100000, 'rate_bp': 0, 'io_months': 1, 'amort_months': 12, 'fund_day': 29}, {'io_bills': [0], 'payment': 8333, 'final_payment': 8337, 'total_interest': 0}], ['control 6', {'principal': 100000, 'rate_bp': 0, 'io_months': 3, 'amort_months': 1, 'fund_day': 30}, {'io_bills': [0, 0, 0], 'payment': 100000, 'final_payment': 100000, 'total_interest': 0}]], [['regression: total interest coverage', {'principal': 100000, 'rate_bp': 725, 'io_months': 2, 'amort_months': 60, 'fund_day': 1}, {'io_bills': [604, 604], 'payment': 1992, 'final_payment': 1987, 'total_interest': 20723}], ['control 1', {'principal': 12345, 'rate_bp': 0, 'io_months': 3, 'amort_months': 12, 'fund_day': 1}, {'io_bills': [0, 0, 0], 'payment': 1029, 'final_payment': 1026, 'total_interest': 0}], ['control 2', {'principal': 3000000, 'rate_bp': 0, 'io_months': 12, 'amort_months': 60, 'fund_day': 2}, {'io_bills': [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], 'payment': 50000, 'final_payment': 50000, 'total_interest': 0}], ['control 3', {'principal': 250000, 'rate_bp': 0, 'io_months': 2, 'amort_months': 24, 'fund_day': 15}, {'io_bills': [0, 0], 'payment': 10417, 'final_payment': 10409, 'total_interest': 0}], ['control 4', {'principal': 3000000, 'rate_bp': 0, 'io_months': 2, 'amort_months': 1, 'fund_day': 2}, {'io_bills': [0, 0], 'payment': 3000000, 'final_payment': 3000000, 'total_interest': 0}], ['control 5', {'principal': 12345, 'rate_bp': 0, 'io_months': 2, 'amort_months': 60, 'fund_day': 2}, {'io_bills': [0, 0], 'payment': 206, 'final_payment': 191, 'total_interest': 0}], ['control 6', {'principal': 250000, 'rate_bp': 0, 'io_months': 3, 'amort_months': 12, 'fund_day': 1}, {'io_bills': [0, 0, 0], 'payment': 20833, 'final_payment': 20837, 'total_interest': 0}]], [['regression: total interest coverage', {'principal': 3000000, 'rate_bp': 500, 'io_months': 2, 'amort_months': 24, 'fund_day': 29}, {'io_bills': [833, 12500], 'payment': 131614, 'final_payment': 131618, 'total_interest': 172073}], ['control 1', {'principal': 100000, 'rate_bp': 0, 'io_months': 12, 'amort_months': 24, 'fund_day': 29}, {'io_bills': [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], 'payment': 4167, 'final_payment': 4159, 'total_interest': 0}], ['control 2', {'principal': 12345, 'rate_bp': 0, 'io_months': 2, 'amort_months': 24, 'fund_day': 30}, {'io_bills': [0, 0], 'payment': 514, 'final_payment': 523, 'total_interest': 0}], ['control 3', {'principal': 250000, 'rate_bp': 0, 'io_months': 1, 'amort_months': 1, 'fund_day': 30}, {'io_bills': [0], 'payment': 250000, 'final_payment': 250000, 'total_interest': 0}], ['control 4', {'principal': 3000000, 'rate_bp': 0, 'io_months': 1, 'amort_months': 1, 'fund_day': 29}, {'io_bills': [0], 'payment': 3000000, 'final_payment': 3000000, 'total_interest': 0}], ['control 5', {'principal': 12345, 'rate_bp': 0, 'io_months': 2, 'amort_months': 60, 'fund_day': 15}, {'io_bills': [0, 0], 'payment': 206, 'final_payment': 191, 'total_interest': 0}], ['control 6', {'principal': 3000000, 'rate_bp': 0, 'io_months': 1, 'amort_months': 1, 'fund_day': 1}, {'io_bills': [0], 'payment': 3000000, 'final_payment': 3000000, 'total_interest': 0}]], [['regression: total interest coverage', {'principal': 250000, 'rate_bp': 500, 'io_months': 12, 'amort_months': 60, 'fund_day': 15}, {'io_bills': [556, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042], 'payment': 4718, 'final_payment': 4703, 'total_interest': 45083}], ['control 1', {'principal': 100000, 'rate_bp': 0, 'io_months': 12, 'amort_months': 60, 'fund_day': 29}, {'io_bills': [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], 'payment': 1667, 'final_payment': 1647, 'total_interest': 0}], ['control 2', {'principal': 250000, 'rate_bp': 0, 'io_months': 3, 'amort_months': 1, 'fund_day': 2}, {'io_bills': [0, 0, 0], 'payment': 250000, 'final_payment': 250000, 'total_interest': 0}], ['control 3', {'principal': 12345, 'rate_bp': 0, 'io_months': 12, 'amort_months': 60, 'fund_day': 15}, {'io_bills': [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], 'payment': 206, 'final_payment': 191, 'total_interest': 0}], ['control 4', {'principal': 12345, 'rate_bp': 0, 'io_months': 2, 'amort_months': 24, 'fund_day': 15}, {'io_bills': [0, 0], 'payment': 514, 'final_payment': 523, 'total_interest': 0}], ['control 5', {'principal': 3000000, 'rate_bp': 0, 'io_months': 1, 'amort_months': 1, 'fund_day': 29}, {'io_bills': [0], 'payment': 3000000, 'final_payment': 3000000, 'total_interest': 0}], ['control 6', {'principal': 250000, 'rate_bp': 0, 'io_months': 12, 'amort_months': 60, 'fund_day': 30}, {'io_bills': [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], 'payment': 4167, 'final_payment': 4147, 'total_interest': 0}]], [['regression: total interest coverage', {'principal': 12345, 'rate_bp': 725, 'io_months': 1, 'amort_months': 12, 'fund_day': 2}, {'io_bills': [72], 'payment': 1070, 'final_payment': 1065, 'total_interest': 562}], ['control 1', {'principal': 100000, 'rate_bp': 0, 'io_months': 12, 'amort_months': 24, 'fund_day': 30}, {'io_bills': [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], 'payment': 4167, 'final_payment': 4159, 'total_interest': 0}], ['control 2', {'principal': 3000000, 'rate_bp': 0, 'io_months': 12, 'amort_months': 24, 'fund_day': 15}, {'io_bills': [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], 'payment': 125000, 'final_payment': 125000, 'total_interest': 0}], ['control 3', {'principal': 12345, 'rate_bp': 0, 'io_months': 3, 'amort_months': 24, 'fund_day': 15}, {'io_bills': [0, 0, 0], 'payment': 514, 'final_payment': 523, 'total_interest': 0}], ['control 4', {'principal': 3000000, 'rate_bp': 0, 'io_months': 2, 'amort_months': 1, 'fund_day': 15}, {'io_bills': [0, 0], 'payment': 3000000, 'final_payment': 3000000, 'total_interest': 0}], ['control 5', {'principal': 12345, 'rate_bp': 0, 'io_months': 2, 'amort_months': 12, 'fund_day': 30}, {'io_bills': [0, 0], 'payment': 1029, 'final_payment': 1026, 'total_interest': 0}], ['control 6', {'principal': 250000, 'rate_bp': 0, 'io_months': 1, 'amort_months': 60, 'fund_day': 1}, {'io_bills': [0], 'payment': 4167, 'final_payment': 4147, 'total_interest': 0}]]]
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)

source:
"""Failure Map reference implementation. Python standard library only."""
import json
import math
from fractions import Fraction
N = 1
observations = []
def solve(x):
    def rnd(n, d):
        q, r = divmod(n, d)
        return q + (1 if 2 * r >= d else 0)
    def level(P, n):
        if n <= 0 or P <= 0:
            return 0
        if bp == 0:
            exact = Fraction(P, n)
        else:
            r = Fraction(bp, 120000)
            exact = P * r / (1 - (1 + r) ** -n)
        return math.floor(exact + Fraction(1, 2))
    bp = x['rate_bp']
    P = x['principal']
    io_bills = []
    total_int = 0
    for m in range(x['io_months']):
        days = 31 - x['fund_day'] if m == 0 else 30
        interest = rnd(P * bp * days, 120000 * 30)
        io_bills.append(interest)
        total_int += interest if m else 0
    pay = level(P, x['amort_months'])
    bal = P
    final = 0
    for k in range(1, x['amort_months'] + 1):
        interest = rnd(bal * bp, 120000)
        total_int += interest
        if k == x['amort_months']:
            final = bal + interest
        else:
            bal -= pay - interest
    return {'io_bills': io_bills, 'payment': pay, 'final_payment': final, 'total_interest': total_int}
def check(label, actual, expected):
    observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
fixtures = [[['regression: total interest coverage', {'principal': 3000000, 'rate_bp': 500, 'io_months': 3, 'amort_months': 60, 'fund_day': 1}, {'io_bills': [12500, 12500, 12500], 'payment': 56614, 'final_payment': 56596, 'total_interest': 434322}], ['control 1', {'principal': 12345, 'rate_bp': 0, 'io_months': 1, 'amort_months': 24, 'fund_day': 2}, {'io_bills': [0], 'payment': 514, 'final_payment': 523, 'total_interest': 0}], ['control 2', {'principal': 12345, 'rate_bp': 0, 'io_months': 1, 'amort_months': 1, 'fund_day': 2}, {'io_bills': [0], 'payment': 12345, 'final_payment': 12345, 'total_interest': 0}], ['control 3', {'principal': 250000, 'rate_bp': 0, 'io_months': 2, 'amort_months': 60, 'fund_day': 15}, {'io_bills': [0, 0], 'payment': 4167, 'final_payment': 4147, 'total_interest': 0}], ['control 4', {'principal': 100000, 'rate_bp': 0, 'io_months': 3, 'amort_months': 60, 'fund_day': 15}, {'io_bills': [0, 0, 0], 'payment': 1667, 'final_payment': 1647, 'total_interest': 0}], ['control 5', {'principal': 100000, 'rate_bp': 0, 'io_months': 1, 'amort_months': 12, 'fund_day': 29}, {'io_bills': [0], 'payment': 8333, 'final_payment': 8337, 'total_interest': 0}], ['control 6', {'principal': 100000, 'rate_bp': 0, 'io_months': 3, 'amort_months': 1, 'fund_day': 30}, {'io_bills': [0, 0, 0], 'payment': 100000, 'final_payment': 100000, 'total_interest': 0}]], [['regression: total interest coverage', {'principal': 100000, 'rate_bp': 725, 'io_months': 2, 'amort_months': 60, 'fund_day': 1}, {'io_bills': [604, 604], 'payment': 1992, 'final_payment': 1987, 'total_interest': 20723}], ['control 1', {'principal': 12345, 'rate_bp': 0, 'io_months': 3, 'amort_months': 12, 'fund_day': 1}, {'io_bills': [0, 0, 0], 'payment': 1029, 'final_payment': 1026, 'total_interest': 0}], ['control 2', {'principal': 3000000, 'rate_bp': 0, 'io_months': 12, 'amort_months': 60, 'fund_day': 2}, {'io_bills': [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], 'payment': 50000, 'final_payment': 50000, 'total_interest': 0}], ['control 3', {'principal': 250000, 'rate_bp': 0, 'io_months': 2, 'amort_months': 24, 'fund_day': 15}, {'io_bills': [0, 0], 'payment': 10417, 'final_payment': 10409, 'total_interest': 0}], ['control 4', {'principal': 3000000, 'rate_bp': 0, 'io_months': 2, 'amort_months': 1, 'fund_day': 2}, {'io_bills': [0, 0], 'payment': 3000000, 'final_payment': 3000000, 'total_interest': 0}], ['control 5', {'principal': 12345, 'rate_bp': 0, 'io_months': 2, 'amort_months': 60, 'fund_day': 2}, {'io_bills': [0, 0], 'payment': 206, 'final_payment': 191, 'total_interest': 0}], ['control 6', {'principal': 250000, 'rate_bp': 0, 'io_months': 3, 'amort_months': 12, 'fund_day': 1}, {'io_bills': [0, 0, 0], 'payment': 20833, 'final_payment': 20837, 'total_interest': 0}]], [['regression: total interest coverage', {'principal': 3000000, 'rate_bp': 500, 'io_months': 2, 'amort_months': 24, 'fund_day': 29}, {'io_bills': [833, 12500], 'payment': 131614, 'final_payment': 131618, 'total_interest': 172073}], ['control 1', {'principal': 100000, 'rate_bp': 0, 'io_months': 12, 'amort_months': 24, 'fund_day': 29}, {'io_bills': [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], 'payment': 4167, 'final_payment': 4159, 'total_interest': 0}], ['control 2', {'principal': 12345, 'rate_bp': 0, 'io_months': 2, 'amort_months': 24, 'fund_day': 30}, {'io_bills': [0, 0], 'payment': 514, 'final_payment': 523, 'total_interest': 0}], ['control 3', {'principal': 250000, 'rate_bp': 0, 'io_months': 1, 'amort_months': 1, 'fund_day': 30}, {'io_bills': [0], 'payment': 250000, 'final_payment': 250000, 'total_interest': 0}], ['control 4', {'principal': 3000000, 'rate_bp': 0, 'io_months': 1, 'amort_months': 1, 'fund_day': 29}, {'io_bills': [0], 'payment': 3000000, 'final_payment': 3000000, 'total_interest': 0}], ['control 5', {'principal': 12345, 'rate_bp': 0, 'io_months': 2, 'amort_months': 60, 'fund_day': 15}, {'io_bills': [0, 0], 'payment': 206, 'final_payment': 191, 'total_interest': 0}], ['control 6', {'principal': 3000000, 'rate_bp': 0, 'io_months': 1, 'amort_months': 1, 'fund_day': 1}, {'io_bills': [0], 'payment': 3000000, 'final_payment': 3000000, 'total_interest': 0}]], [['regression: total interest coverage', {'principal': 250000, 'rate_bp': 500, 'io_months': 12, 'amort_months': 60, 'fund_day': 15}, {'io_bills': [556, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042], 'payment': 4718, 'final_payment': 4703, 'total_interest': 45083}], ['control 1', {'principal': 100000, 'rate_bp': 0, 'io_months': 12, 'amort_months': 60, 'fund_day': 29}, {'io_bills': [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], 'payment': 1667, 'final_payment': 1647, 'total_interest': 0}], ['control 2', {'principal': 250000, 'rate_bp': 0, 'io_months': 3, 'amort_months': 1, 'fund_day': 2}, {'io_bills': [0, 0, 0], 'payment': 250000, 'final_payment': 250000, 'total_interest': 0}], ['control 3', {'principal': 12345, 'rate_bp': 0, 'io_months': 12, 'amort_months': 60, 'fund_day': 15}, {'io_bills': [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], 'payment': 206, 'final_payment': 191, 'total_interest': 0}], ['control 4', {'principal': 12345, 'rate_bp': 0, 'io_months': 2, 'amort_months': 24, 'fund_day': 15}, {'io_bills': [0, 0], 'payment': 514, 'final_payment': 523, 'total_interest': 0}], ['control 5', {'principal': 3000000, 'rate_bp': 0, 'io_months': 1, 'amort_months': 1, 'fund_day': 29}, {'io_bills': [0], 'payment': 3000000, 'final_payment': 3000000, 'total_interest': 0}], ['control 6', {'principal': 250000, 'rate_bp': 0, 'io_months': 12, 'amort_months': 60, 'fund_day': 30}, {'io_bills': [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], 'payment': 4167, 'final_payment': 4147, 'total_interest': 0}]], [['regression: total interest coverage', {'principal': 12345, 'rate_bp': 725, 'io_months': 1, 'amort_months': 12, 'fund_day': 2}, {'io_bills': [72], 'payment': 1070, 'final_payment': 1065, 'total_interest': 562}], ['control 1', {'principal': 100000, 'rate_bp': 0, 'io_months': 12, 'amort_months': 24, 'fund_day': 30}, {'io_bills': [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], 'payment': 4167, 'final_payment': 4159, 'total_interest': 0}], ['control 2', {'principal': 3000000, 'rate_bp': 0, 'io_months': 12, 'amort_months': 24, 'fund_day': 15}, {'io_bills': [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], 'payment': 125000, 'final_payment': 125000, 'total_interest': 0}], ['control 3', {'principal': 12345, 'rate_bp': 0, 'io_months': 3, 'amort_months': 24, 'fund_day': 15}, {'io_bills': [0, 0, 0], 'payment': 514, 'final_payment': 523, 'total_interest': 0}], ['control 4', {'principal': 3000000, 'rate_bp': 0, 'io_months': 2, 'amort_months': 1, 'fund_day': 15}, {'io_bills': [0, 0], 'payment': 3000000, 'final_payment': 3000000, 'total_interest': 0}], ['control 5', {'principal': 12345, 'rate_bp': 0, 'io_months': 2, 'amort_months': 12, 'fund_day': 30}, {'io_bills': [0, 0], 'payment': 1029, 'final_payment': 1026, 'total_interest': 0}], ['control 6', {'principal': 250000, 'rate_bp': 0, 'io_months': 1, 'amort_months': 60, 'fund_day': 1}, {'io_bills': [0], 'payment': 4167, 'final_payment': 4147, 'total_interest': 0}]]]
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)

prompt:
Lifetime interest disclosures omit the interest-only period.

x = {'principal', 'rate_bp', 'io_months' >= 1, 'amort_months' >= 1, 'fund_day' 1..30}. IO month m pays interest round_half_up(P*bp*days/(120000*30)) with days = 31 - fund_day for the first IO month and 30 otherwise. Then the level payment on the full principal over amort_months (exact annuity, half-up) amortizes with monthly interest round_half_up(balance*bp/120000); the last month pays balance + interest. Return {'io_bills': list of IO payments, 'payment', 'final_payment', 'total_interest'}.

split: open-access


broken_source:
"""Failure Map reference implementation. Python standard library only."""
import json
import datetime
N = 1
observations = []
def solve(x):
    def rnd(n, d):
        q, r = divmod(n, d)
        return q + (1 if 2 * r >= d else 0)
    y1, m1, d1 = x['start']
    y2, m2, d2 = x['first_due']
    conv = x['convention']
    if conv == '30/360':
        if d1 == 31:
            d1 = 30
        if d2 == 31:
            d2 = 30
        days = 360 * (y2 - y1) + 30 * (m2 - m1) + (d2 - d1)
        basis = 360
    else:
        days = (datetime.date(y2, m2, d2) - datetime.date(y1, m1, d1)).days
        basis = 365 if conv == 'act/365' else 360
    if days <= 0:
        return {'error': 'due_not_after_start'}
    interest = rnd(x['principal'] * x['rate_bp'] * days, 10000 * basis)
    return {'days': days, 'basis': basis, 'interest': interest}
def check(label, actual, expected):
    observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
fixtures = [[['regression: due day 31 adjustment', {'principal': 36500, 'rate_bp': 1200, 'convention': '30/360', 'start': [2023, 3, 29], 'first_due': [2023, 3, 31]}, {'days': 2, 'basis': 360, 'interest': 24}], ['regression: due day 31 adjustment, partial-repair probe', {'principal': 1000000, 'rate_bp': 600, 'convention': '30/360', 'start': [2024, 8, 30], 'first_due': [2025, 7, 31]}, {'days': 330, 'basis': 360, 'interest': 55000}], ['control 1', {'principal': 12345, 'rate_bp': 365, 'convention': 'act/360', 'start': [2023, 3, 15], 'first_due': [2024, 3, 1]}, {'days': 352, 'basis': 360, 'interest': 441}], ['control 2', {'principal': 12345, 'rate_bp': 500, 'convention': 'act/360', 'start': [2023, 7, 28], 'first_due': [2024, 7, 28]}, {'days': 366, 'basis': 360, 'interest': 628}], ['control 3', {'principal': 12345, 'rate_bp': 600, 'convention': 'act/360', 'start': [2024, 10, 30], 'first_due': [2025, 10, 30]}, {'days': 365, 'basis': 360, 'interest': 751}], ['control 4', {'principal': 100000, 'rate_bp': 1200, 'convention': 'act/360', 'start': [2023, 1, 1], 'first_due': [2023, 2, 1]}, {'days': 31, 'basis': 360, 'interest': 1033}], ['control 5', {'principal': 12345, 'rate_bp': 1200, 'convention': '30/360', 'start': [2024, 7, 15], 'first_due': [2024, 9, 30]}, {'days': 75, 'basis': 360, 'interest': 309}]], [['regression: due day 31 adjustment', {'principal': 1000000, 'rate_bp': 600, 'convention': '30/360', 'start': [2024, 7, 15], 'first_due': [2024, 8, 31]}, {'days': 46, 'basis': 360, 'interest': 7667}], ['regression: due day 31 adjustment, partial-repair probe', {'principal': 100000, 'rate_bp': 500, 'convention': '30/360', 'start': [2024, 11, 30], 'first_due': [2024, 12, 31]}, {'days': 30, 'basis': 360, 'interest': 417}], ['control 1', {'principal': 12345, 'rate_bp': 600, 'convention': '30/360', 'start': [2023, 7, 28], 'first_due': [2023, 8, 28]}, {'days': 30, 'basis': 360, 'interest': 62}], ['control 2', {'principal': 12345, 'rate_bp': 500, 'convention': 'act/360', 'start': [2023, 3, 15], 'first_due': [2024, 2, 1]}, {'days': 323, 'basis': 360, 'interest': 554}], ['control 3', {'principal': 1000000, 'rate_bp': 600, 'convention': 'act/365', 'start': [2023, 4, 30], 'first_due': [2024, 4, 30]}, {'days': 366, 'basis': 365, 'interest': 60164}], ['control 4', {'principal': 36500, 'rate_bp': 600, 'convention': '30/360', 'start': [2023, 6, 30], 'first_due': [2023, 8, 30]}, {'days': 60, 'basis': 360, 'interest': 365}], ['control 5', {'principal': 12345, 'rate_bp': 500, 'convention': 'act/365', 'start': [2024, 7, 30], 'first_due': [2024, 8, 28]}, {'days': 29, 'basis': 365, 'interest': 49}]], [['regression: due day 31 adjustment', {'principal': 100000, 'rate_bp': 600, 'convention': '30/360', 'start': [2024, 10, 15], 'first_due': [2024, 12, 31]}, {'days': 76, 'basis': 360, 'interest': 1267}], ['regression: due day 31 adjustment, partial-repair probe', {'principal': 1000000, 'rate_bp': 600, 'convention': '30/360', 'start': [2023, 1, 31], 'first_due': [2023, 3, 31]}, {'days': 60, 'basis': 360, 'interest': 10000}], ['control 1', {'principal': 12345, 'rate_bp': 500, 'convention': 'act/360', 'start': [2023, 11, 28], 'first_due': [2024, 10, 31]}, {'days': 338, 'basis': 360, 'interest': 580}], ['control 2', {'principal': 12345, 'rate_bp': 500, 'convention': '30/360', 'start': [2024, 1, 31], 'first_due': [2024, 1, 28]}, {'error': 'due_not_after_start'}], ['control 3', {'principal': 36500, 'rate_bp': 1200, 'convention': 'act/360', 'start': [2024, 3, 29], 'first_due': [2025, 2, 1]}, {'days': 309, 'basis': 360, 'interest': 3760}], ['control 4', {'principal': 12345, 'rate_bp': 500, 'convention': '30/360', 'start': [2023, 4, 30], 'first_due': [2023, 5, 28]}, {'days': 28, 'basis': 360, 'interest': 48}], ['control 5', {'principal': 1000000, 'rate_bp': 365, 'convention': 'act/365', 'start': [2023, 12, 1], 'first_due': [2023, 12, 15]}, {'days': 14, 'basis': 365, 'interest': 1400}]], [['regression: due day 31 adjustment', {'principal': 36500, 'rate_bp': 600, 'convention': '30/360', 'start': [2023, 8, 29], 'first_due': [2023, 8, 31]}, {'days': 2, 'basis': 360, 'interest': 12}], ['regression: due day 31 adjustment, partial-repair probe', {'principal': 36500, 'rate_bp': 1200, 'convention': '30/360', 'start': [2024, 1, 31], 'first_due': [2025, 1, 31]}, {'days': 360, 'basis': 360, 'interest': 4380}], ['control 1', {'principal': 100000, 'rate_bp': 1200, 'convention': '30/360', 'start': [2023, 4, 1], 'first_due': [2024, 3, 1]}, {'days': 330, 'basis': 360, 'interest': 11000}], ['control 2', {'principal': 36500, 'rate_bp': 500, 'convention': 'act/365', 'start': [2023, 4, 28], 'first_due': [2023, 6, 28]}, {'days': 61, 'basis': 365, 'interest': 305}], ['control 3', {'principal': 12345, 'rate_bp': 365, 'convention': 'act/360', 'start': [2024, 4, 30], 'first_due': [2024, 5, 1]}, {'days': 1, 'basis': 360, 'interest': 1}], ['control 4', {'principal': 36500, 'rate_bp': 1200, 'convention': 'act/365', 'start': [2023, 4, 30], 'first_due': [2023, 4, 30]}, {'error': 'due_not_after_start'}], ['control 5', {'principal': 12345, 'rate_bp': 1200, 'convention': 'act/365', 'start': [2024, 2, 29], 'first_due': [2024, 4, 29]}, {'days': 60, 'basis': 365, 'interest': 244}]], [['regression: due day 31 adjustment', {'principal': 36500, 'rate_bp': 600, 'convention': '30/360', 'start': [2023, 5, 29], 'first_due': [2023, 5, 31]}, {'days': 2, 'basis': 360, 'interest': 12}], ['regression: due day 31 adjustment, partial-repair probe', {'principal': 12345, 'rate_bp': 1200, 'convention': '30/360', 'start': [2024, 11, 30], 'first_due': [2024, 12, 31]}, {'days': 30, 'basis': 360, 'interest': 123}], ['control 1', {'principal': 1000000, 'rate_bp': 600, 'convention': 'act/360', 'start': [2024, 6, 28], 'first_due': [2024, 7, 30]}, {'days': 32, 'basis': 360, 'interest': 5333}], ['control 2', {'principal': 100000, 'rate_bp': 600, 'convention': 'act/360', 'start': [2024, 10, 1], 'first_due': [2024, 10, 31]}, {'days': 30, 'basis': 360, 'interest': 500}], ['control 3', {'principal': 1000000, 'rate_bp': 500, 'convention': 'act/360', 'start': [2024, 1, 29], 'first_due': [2024, 1, 30]}, {'days': 1, 'basis': 360, 'interest': 139}], ['control 4', {'principal': 12345, 'rate_bp': 365, 'convention': 'act/360', 'start': [2024, 2, 29], 'first_due': [2024, 4, 28]}, {'days': 59, 'basis': 360, 'interest': 74}], ['control 5', {'principal': 36500, 'rate_bp': 500, 'convention': 'act/365', 'start': [2024, 2, 29], 'first_due': [2024, 3, 29]}, {'days': 29, 'basis': 365, 'interest': 145}]]]
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)

source:
"""Failure Map reference implementation. Python standard library only."""
import json
import datetime
N = 1
observations = []
def solve(x):
    def rnd(n, d):
        q, r = divmod(n, d)
        return q + (1 if 2 * r >= d else 0)
    y1, m1, d1 = x['start']
    y2, m2, d2 = x['first_due']
    conv = x['convention']
    if conv == '30/360':
        if d1 == 31:
            d1 = 30
        if d2 == 31 and d1 == 31:
            d2 = 30
        days = 360 * (y2 - y1) + 30 * (m2 - m1) + (d2 - d1)
        basis = 360
    else:
        days = (datetime.date(y2, m2, d2) - datetime.date(y1, m1, d1)).days
        basis = 365 if conv == 'act/365' else 360
    if days <= 0:
        return {'error': 'due_not_after_start'}
    interest = rnd(x['principal'] * x['rate_bp'] * days, 10000 * basis)
    return {'days': days, 'basis': basis, 'interest': interest}
def check(label, actual, expected):
    observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
fixtures = [[['regression: due day 31 adjustment', {'principal': 36500, 'rate_bp': 1200, 'convention': '30/360', 'start': [2023, 3, 29], 'first_due': [2023, 3, 31]}, {'days': 2, 'basis': 360, 'interest': 24}], ['regression: due day 31 adjustment, partial-repair probe', {'principal': 1000000, 'rate_bp': 600, 'convention': '30/360', 'start': [2024, 8, 30], 'first_due': [2025, 7, 31]}, {'days': 330, 'basis': 360, 'interest': 55000}], ['control 1', {'principal': 12345, 'rate_bp': 365, 'convention': 'act/360', 'start': [2023, 3, 15], 'first_due': [2024, 3, 1]}, {'days': 352, 'basis': 360, 'interest': 441}], ['control 2', {'principal': 12345, 'rate_bp': 500, 'convention': 'act/360', 'start': [2023, 7, 28], 'first_due': [2024, 7, 28]}, {'days': 366, 'basis': 360, 'interest': 628}], ['control 3', {'principal': 12345, 'rate_bp': 600, 'convention': 'act/360', 'start': [2024, 10, 30], 'first_due': [2025, 10, 30]}, {'days': 365, 'basis': 360, 'interest': 751}], ['control 4', {'principal': 100000, 'rate_bp': 1200, 'convention': 'act/360', 'start': [2023, 1, 1], 'first_due': [2023, 2, 1]}, {'days': 31, 'basis': 360, 'interest': 1033}], ['control 5', {'principal': 12345, 'rate_bp': 1200, 'convention': '30/360', 'start': [2024, 7, 15], 'first_due': [2024, 9, 30]}, {'days': 75, 'basis': 360, 'interest': 309}]], [['regression: due day 31 adjustment', {'principal': 1000000, 'rate_bp': 600, 'convention': '30/360', 'start': [2024, 7, 15], 'first_due': [2024, 8, 31]}, {'days': 46, 'basis': 360, 'interest': 7667}], ['regression: due day 31 adjustment, partial-repair probe', {'principal': 100000, 'rate_bp': 500, 'convention': '30/360', 'start': [2024, 11, 30], 'first_due': [2024, 12, 31]}, {'days': 30, 'basis': 360, 'interest': 417}], ['control 1', {'principal': 12345, 'rate_bp': 600, 'convention': '30/360', 'start': [2023, 7, 28], 'first_due': [2023, 8, 28]}, {'days': 30, 'basis': 360, 'interest': 62}], ['control 2', {'principal': 12345, 'rate_bp': 500, 'convention': 'act/360', 'start': [2023, 3, 15], 'first_due': [2024, 2, 1]}, {'days': 323, 'basis': 360, 'interest': 554}], ['control 3', {'principal': 1000000, 'rate_bp': 600, 'convention': 'act/365', 'start': [2023, 4, 30], 'first_due': [2024, 4, 30]}, {'days': 366, 'basis': 365, 'interest': 60164}], ['control 4', {'principal': 36500, 'rate_bp': 600, 'convention': '30/360', 'start': [2023, 6, 30], 'first_due': [2023, 8, 30]}, {'days': 60, 'basis': 360, 'interest': 365}], ['control 5', {'principal': 12345, 'rate_bp': 500, 'convention': 'act/365', 'start': [2024, 7, 30], 'first_due': [2024, 8, 28]}, {'days': 29, 'basis': 365, 'interest': 49}]], [['regression: due day 31 adjustment', {'principal': 100000, 'rate_bp': 600, 'convention': '30/360', 'start': [2024, 10, 15], 'first_due': [2024, 12, 31]}, {'days': 76, 'basis': 360, 'interest': 1267}], ['regression: due day 31 adjustment, partial-repair probe', {'principal': 1000000, 'rate_bp': 600, 'convention': '30/360', 'start': [2023, 1, 31], 'first_due': [2023, 3, 31]}, {'days': 60, 'basis': 360, 'interest': 10000}], ['control 1', {'principal': 12345, 'rate_bp': 500, 'convention': 'act/360', 'start': [2023, 11, 28], 'first_due': [2024, 10, 31]}, {'days': 338, 'basis': 360, 'interest': 580}], ['control 2', {'principal': 12345, 'rate_bp': 500, 'convention': '30/360', 'start': [2024, 1, 31], 'first_due': [2024, 1, 28]}, {'error': 'due_not_after_start'}], ['control 3', {'principal': 36500, 'rate_bp': 1200, 'convention': 'act/360', 'start': [2024, 3, 29], 'first_due': [2025, 2, 1]}, {'days': 309, 'basis': 360, 'interest': 3760}], ['control 4', {'principal': 12345, 'rate_bp': 500, 'convention': '30/360', 'start': [2023, 4, 30], 'first_due': [2023, 5, 28]}, {'days': 28, 'basis': 360, 'interest': 48}], ['control 5', {'principal': 1000000, 'rate_bp': 365, 'convention': 'act/365', 'start': [2023, 12, 1], 'first_due': [2023, 12, 15]}, {'days': 14, 'basis': 365, 'interest': 1400}]], [['regression: due day 31 adjustment', {'principal': 36500, 'rate_bp': 600, 'convention': '30/360', 'start': [2023, 8, 29], 'first_due': [2023, 8, 31]}, {'days': 2, 'basis': 360, 'interest': 12}], ['regression: due day 31 adjustment, partial-repair probe', {'principal': 36500, 'rate_bp': 1200, 'convention': '30/360', 'start': [2024, 1, 31], 'first_due': [2025, 1, 31]}, {'days': 360, 'basis': 360, 'interest': 4380}], ['control 1', {'principal': 100000, 'rate_bp': 1200, 'convention': '30/360', 'start': [2023, 4, 1], 'first_due': [2024, 3, 1]}, {'days': 330, 'basis': 360, 'interest': 11000}], ['control 2', {'principal': 36500, 'rate_bp': 500, 'convention': 'act/365', 'start': [2023, 4, 28], 'first_due': [2023, 6, 28]}, {'days': 61, 'basis': 365, 'interest': 305}], ['control 3', {'principal': 12345, 'rate_bp': 365, 'convention': 'act/360', 'start': [2024, 4, 30], 'first_due': [2024, 5, 1]}, {'days': 1, 'basis': 360, 'interest': 1}], ['control 4', {'principal': 36500, 'rate_bp': 1200, 'convention': 'act/365', 'start': [2023, 4, 30], 'first_due': [2023, 4, 30]}, {'error': 'due_not_after_start'}], ['control 5', {'principal': 12345, 'rate_bp': 1200, 'convention': 'act/365', 'start': [2024, 2, 29], 'first_due': [2024, 4, 29]}, {'days': 60, 'basis': 365, 'interest': 244}]], [['regression: due day 31 adjustment', {'principal': 36500, 'rate_bp': 600, 'convention': '30/360', 'start': [2023, 5, 29], 'first_due': [2023, 5, 31]}, {'days': 2, 'basis': 360, 'interest': 12}], ['regression: due day 31 adjustment, partial-repair probe', {'principal': 12345, 'rate_bp': 1200, 'convention': '30/360', 'start': [2024, 11, 30], 'first_due': [2024, 12, 31]}, {'days': 30, 'basis': 360, 'interest': 123}], ['control 1', {'principal': 1000000, 'rate_bp': 600, 'convention': 'act/360', 'start': [2024, 6, 28], 'first_due': [2024, 7, 30]}, {'days': 32, 'basis': 360, 'interest': 5333}], ['control 2', {'principal': 100000, 'rate_bp': 600, 'convention': 'act/360', 'start': [2024, 10, 1], 'first_due': [2024, 10, 31]}, {'days': 30, 'basis': 360, 'interest': 500}], ['control 3', {'principal': 1000000, 'rate_bp': 500, 'convention': 'act/360', 'start': [2024, 1, 29], 'first_due': [2024, 1, 30]}, {'days': 1, 'basis': 360, 'interest': 139}], ['control 4', {'principal': 12345, 'rate_bp': 365, 'convention': 'act/360', 'start': [2024, 2, 29], 'first_due': [2024, 4, 28]}, {'days': 59, 'basis': 360, 'interest': 74}], ['control 5', {'principal': 36500, 'rate_bp': 500, 'convention': 'act/365', 'start': [2024, 2, 29], 'first_due': [2024, 3, 29]}, {'days': 29, 'basis': 365, 'interest': 145}]]]
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)

prompt:
Loans funded mid-month lose a day of interest when the first due date is the 31st.

x = {'principal', 'rate_bp', 'convention': '30/360'|'act/365'|'act/360', 'start': [y,m,d], 'first_due': [y,m,d]}. 30/360: a start day of 31 becomes 30; then a due day of 31 becomes 30 when the (adjusted) start day is 30; days = 360*dy + 30*dm + dd. Actual conventions count calendar days, basis 365 or 360 (365 even in leap years). If days <= 0 return {'error': 'due_not_after_start'}. Interest = round_half_up(P*bp*days/(10000*basis)). Return {'days', 'basis', 'interest'}.

split: open-access