broken_source:
"""Failure Map reference implementation. Python standard library only."""
import json

N = 1
observations = []
def solve(x):
    free = min(x['max_free_min'], sum(v for k, v in x['validations'] if k == 'free_min'))
    bill = max(0, x['minutes'] - free)
    fee = min(x['daily_max'], -(-bill // 60) * x['hourly'])
    pcts = [v for k, v in x['validations'] if k == 'pct']
    if pcts:
        fee -= fee * max(pcts) // 100
    flat = sum(v for k, v in x['validations'] if k == 'flat')
    return fee - flat
def check(label, actual, expected):
    observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
fixtures = [[({'minutes': 150, 'validations': [['free_min', 60], ['free_min', 60], ['free_min', 60]], 'hourly': 300, 'daily_max': 2400, 'max_free_min': 120}, 300), ({'minutes': 794, 'validations': [], 'hourly': 350, 'daily_max': 2400, 'max_free_min': 60}, 2400), ({'minutes': 754, 'validations': [['free_min', 30], ['free_min', 120], ['free_min', 30], ['pct', 25]], 'hourly': 300, 'daily_max': 1800, 'max_free_min': 180}, 1350), ({'minutes': 7, 'validations': [['flat', 250], ['free_min', 15], ['free_min', 60]], 'hourly': 350, 'daily_max': 1800, 'max_free_min': 120}, 0), ({'minutes': 27, 'validations': [['free_min', 90], ['flat', 500], ['free_min', 15]], 'hourly': 300, 'daily_max': 2400, 'max_free_min': 120}, 0), ({'minutes': 95, 'validations': [['free_min', 30]], 'hourly': 350, 'daily_max': 2400, 'max_free_min': 120}, 700), ({'minutes': 42, 'validations': [['pct', 10], ['flat', 500], ['flat', 500]], 'hourly': 200, 'daily_max': 2400, 'max_free_min': 120}, 0), ({'minutes': 200, 'validations': [['pct', 25], ['pct', 10], ['flat', 100], ['flat', 250]], 'hourly': 300, 'daily_max': 2400, 'max_free_min': 120}, 550)], [({'minutes': 27, 'validations': [['free_min', 90], ['free_min', 60], ['flat', 100], ['flat', 100]], 'hourly': 200, 'daily_max': 1800, 'max_free_min': 180}, 0), ({'minutes': 95, 'validations': [['free_min', 30]], 'hourly': 350, 'daily_max': 2400, 'max_free_min': 120}, 700), ({'minutes': 150, 'validations': [['free_min', 60], ['free_min', 60], ['free_min', 60]], 'hourly': 300, 'daily_max': 2400, 'max_free_min': 120}, 300), ({'minutes': 1012, 'validations': [['pct', 50], ['flat', 250], ['free_min', 30], ['pct', 50]], 'hourly': 300, 'daily_max': 1800, 'max_free_min': 120}, 650), ({'minutes': 42, 'validations': [['free_min', 60], ['flat', 500]], 'hourly': 200, 'daily_max': 2400, 'max_free_min': 60}, 0), ({'minutes': 1400, 'validations': [['free_min', 30], ['flat', 250], ['free_min', 90], ['free_min', 120]], 'hourly': 200, 'daily_max': 3000, 'max_free_min': 180}, 2750), ({'minutes': 200, 'validations': [['pct', 25], ['pct', 10], ['flat', 100], ['flat', 250]], 'hourly': 300, 'daily_max': 2400, 'max_free_min': 120}, 550), ({'minutes': 29, 'validations': [['flat', 250], ['free_min', 90], ['free_min', 30]], 'hourly': 350, 'daily_max': 3000, 'max_free_min': 60}, 0)], [({'minutes': 150, 'validations': [['free_min', 60], ['free_min', 60], ['free_min', 60]], 'hourly': 300, 'daily_max': 2400, 'max_free_min': 120}, 300), ({'minutes': 200, 'validations': [['pct', 25], ['pct', 10], ['flat', 100], ['flat', 250]], 'hourly': 300, 'daily_max': 2400, 'max_free_min': 120}, 550), ({'minutes': 259, 'validations': [['flat', 250], ['free_min', 60], ['free_min', 120], ['free_min', 120]], 'hourly': 350, 'daily_max': 3000, 'max_free_min': 180}, 450), ({'minutes': 420, 'validations': [['free_min', 120], ['free_min', 90], ['free_min', 90]], 'hourly': 300, 'daily_max': 1800, 'max_free_min': 180}, 1200), ({'minutes': 135, 'validations': [['free_min', 120], ['flat', 500], ['free_min', 30]], 'hourly': 200, 'daily_max': 3000, 'max_free_min': 180}, 0), ({'minutes': 95, 'validations': [['free_min', 30]], 'hourly': 350, 'daily_max': 2400, 'max_free_min': 120}, 700), ({'minutes': 94, 'validations': [['free_min', 60], ['flat', 500], ['pct', 25]], 'hourly': 300, 'daily_max': 1800, 'max_free_min': 120}, 0), ({'minutes': 87, 'validations': [['flat', 500], ['free_min', 120], ['flat', 250], ['free_min', 120]], 'hourly': 200, 'daily_max': 2400, 'max_free_min': 180}, 0)], [({'minutes': 209, 'validations': [['pct', 25], ['free_min', 30], ['flat', 500], ['pct', 50]], 'hourly': 300, 'daily_max': 1800, 'max_free_min': 60}, 0), ({'minutes': 19, 'validations': [['pct', 10], ['flat', 100], ['free_min', 120]], 'hourly': 200, 'daily_max': 2400, 'max_free_min': 180}, 0), ({'minutes': 95, 'validations': [['free_min', 30]], 'hourly': 350, 'daily_max': 2400, 'max_free_min': 120}, 700), ({'minutes': 568, 'validations': [['free_min', 60], ['free_min', 60], ['free_min', 30], ['free_min', 120]], 'hourly': 200, 'daily_max': 2400, 'max_free_min': 60}, 1800), ({'minutes': 200, 'validations': [['pct', 25], ['pct', 10], ['flat', 100], ['flat', 250]], 'hourly': 300, 'daily_max': 2400, 'max_free_min': 120}, 550), ({'minutes': 806, 'validations': [['pct', 50], ['free_min', 60], ['free_min', 60], ['flat', 500]], 'hourly': 300, 'daily_max': 2400, 'max_free_min': 120}, 700), ({'minutes': 56, 'validations': [['flat', 100], ['flat', 100], ['pct', 10], ['free_min', 90]], 'hourly': 350, 'daily_max': 3000, 'max_free_min': 60}, 0), ({'minutes': 150, 'validations': [['free_min', 60], ['free_min', 60], ['free_min', 60]], 'hourly': 300, 'daily_max': 2400, 'max_free_min': 120}, 300)], [({'minutes': 150, 'validations': [['free_min', 60], ['free_min', 60], ['free_min', 60]], 'hourly': 300, 'daily_max': 2400, 'max_free_min': 120}, 300), ({'minutes': 200, 'validations': [['pct', 25], ['pct', 10], ['flat', 100], ['flat', 250]], 'hourly': 300, 'daily_max': 2400, 'max_free_min': 120}, 550), ({'minutes': 95, 'validations': [['free_min', 30]], 'hourly': 350, 'daily_max': 2400, 'max_free_min': 120}, 700), ({'minutes': 485, 'validations': [['pct', 10], ['free_min', 60]], 'hourly': 350, 'daily_max': 3000, 'max_free_min': 120}, 2520), ({'minutes': 37, 'validations': [['free_min', 90], ['pct', 50], ['flat', 500]], 'hourly': 200, 'daily_max': 1800, 'max_free_min': 60}, 0), ({'minutes': 12, 'validations': [['flat', 500], ['flat', 500]], 'hourly': 350, 'daily_max': 3000, 'max_free_min': 180}, 0), ({'minutes': 1247, 'validations': [['flat', 250]], 'hourly': 300, 'daily_max': 2400, 'max_free_min': 180}, 2150), ({'minutes': 161, 'validations': [['free_min', 60], ['flat', 500], ['flat', 250]], 'hourly': 300, 'daily_max': 1800, 'max_free_min': 120}, 0)]]
for i, (args, expected) in enumerate(fixtures[N-1]):
    check('fee oracle' + ' %d' % i, solve(args), 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

N = 1
observations = []
def solve(x):
    free = min(x['max_free_min'], sum(v for k, v in x['validations'] if k == 'free_min'))
    bill = max(0, x['minutes'] - free)
    fee = min(x['daily_max'], -(-bill // 60) * x['hourly'])
    pcts = [v for k, v in x['validations'] if k == 'pct']
    if pcts:
        fee -= fee * max(pcts) // 100
    flat = sum(v for k, v in x['validations'] if k == 'flat')
    return max(0, fee) - flat
def check(label, actual, expected):
    observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
fixtures = [[({'minutes': 150, 'validations': [['free_min', 60], ['free_min', 60], ['free_min', 60]], 'hourly': 300, 'daily_max': 2400, 'max_free_min': 120}, 300), ({'minutes': 794, 'validations': [], 'hourly': 350, 'daily_max': 2400, 'max_free_min': 60}, 2400), ({'minutes': 754, 'validations': [['free_min', 30], ['free_min', 120], ['free_min', 30], ['pct', 25]], 'hourly': 300, 'daily_max': 1800, 'max_free_min': 180}, 1350), ({'minutes': 7, 'validations': [['flat', 250], ['free_min', 15], ['free_min', 60]], 'hourly': 350, 'daily_max': 1800, 'max_free_min': 120}, 0), ({'minutes': 27, 'validations': [['free_min', 90], ['flat', 500], ['free_min', 15]], 'hourly': 300, 'daily_max': 2400, 'max_free_min': 120}, 0), ({'minutes': 95, 'validations': [['free_min', 30]], 'hourly': 350, 'daily_max': 2400, 'max_free_min': 120}, 700), ({'minutes': 42, 'validations': [['pct', 10], ['flat', 500], ['flat', 500]], 'hourly': 200, 'daily_max': 2400, 'max_free_min': 120}, 0), ({'minutes': 200, 'validations': [['pct', 25], ['pct', 10], ['flat', 100], ['flat', 250]], 'hourly': 300, 'daily_max': 2400, 'max_free_min': 120}, 550)], [({'minutes': 27, 'validations': [['free_min', 90], ['free_min', 60], ['flat', 100], ['flat', 100]], 'hourly': 200, 'daily_max': 1800, 'max_free_min': 180}, 0), ({'minutes': 95, 'validations': [['free_min', 30]], 'hourly': 350, 'daily_max': 2400, 'max_free_min': 120}, 700), ({'minutes': 150, 'validations': [['free_min', 60], ['free_min', 60], ['free_min', 60]], 'hourly': 300, 'daily_max': 2400, 'max_free_min': 120}, 300), ({'minutes': 1012, 'validations': [['pct', 50], ['flat', 250], ['free_min', 30], ['pct', 50]], 'hourly': 300, 'daily_max': 1800, 'max_free_min': 120}, 650), ({'minutes': 42, 'validations': [['free_min', 60], ['flat', 500]], 'hourly': 200, 'daily_max': 2400, 'max_free_min': 60}, 0), ({'minutes': 1400, 'validations': [['free_min', 30], ['flat', 250], ['free_min', 90], ['free_min', 120]], 'hourly': 200, 'daily_max': 3000, 'max_free_min': 180}, 2750), ({'minutes': 200, 'validations': [['pct', 25], ['pct', 10], ['flat', 100], ['flat', 250]], 'hourly': 300, 'daily_max': 2400, 'max_free_min': 120}, 550), ({'minutes': 29, 'validations': [['flat', 250], ['free_min', 90], ['free_min', 30]], 'hourly': 350, 'daily_max': 3000, 'max_free_min': 60}, 0)], [({'minutes': 150, 'validations': [['free_min', 60], ['free_min', 60], ['free_min', 60]], 'hourly': 300, 'daily_max': 2400, 'max_free_min': 120}, 300), ({'minutes': 200, 'validations': [['pct', 25], ['pct', 10], ['flat', 100], ['flat', 250]], 'hourly': 300, 'daily_max': 2400, 'max_free_min': 120}, 550), ({'minutes': 259, 'validations': [['flat', 250], ['free_min', 60], ['free_min', 120], ['free_min', 120]], 'hourly': 350, 'daily_max': 3000, 'max_free_min': 180}, 450), ({'minutes': 420, 'validations': [['free_min', 120], ['free_min', 90], ['free_min', 90]], 'hourly': 300, 'daily_max': 1800, 'max_free_min': 180}, 1200), ({'minutes': 135, 'validations': [['free_min', 120], ['flat', 500], ['free_min', 30]], 'hourly': 200, 'daily_max': 3000, 'max_free_min': 180}, 0), ({'minutes': 95, 'validations': [['free_min', 30]], 'hourly': 350, 'daily_max': 2400, 'max_free_min': 120}, 700), ({'minutes': 94, 'validations': [['free_min', 60], ['flat', 500], ['pct', 25]], 'hourly': 300, 'daily_max': 1800, 'max_free_min': 120}, 0), ({'minutes': 87, 'validations': [['flat', 500], ['free_min', 120], ['flat', 250], ['free_min', 120]], 'hourly': 200, 'daily_max': 2400, 'max_free_min': 180}, 0)], [({'minutes': 209, 'validations': [['pct', 25], ['free_min', 30], ['flat', 500], ['pct', 50]], 'hourly': 300, 'daily_max': 1800, 'max_free_min': 60}, 0), ({'minutes': 19, 'validations': [['pct', 10], ['flat', 100], ['free_min', 120]], 'hourly': 200, 'daily_max': 2400, 'max_free_min': 180}, 0), ({'minutes': 95, 'validations': [['free_min', 30]], 'hourly': 350, 'daily_max': 2400, 'max_free_min': 120}, 700), ({'minutes': 568, 'validations': [['free_min', 60], ['free_min', 60], ['free_min', 30], ['free_min', 120]], 'hourly': 200, 'daily_max': 2400, 'max_free_min': 60}, 1800), ({'minutes': 200, 'validations': [['pct', 25], ['pct', 10], ['flat', 100], ['flat', 250]], 'hourly': 300, 'daily_max': 2400, 'max_free_min': 120}, 550), ({'minutes': 806, 'validations': [['pct', 50], ['free_min', 60], ['free_min', 60], ['flat', 500]], 'hourly': 300, 'daily_max': 2400, 'max_free_min': 120}, 700), ({'minutes': 56, 'validations': [['flat', 100], ['flat', 100], ['pct', 10], ['free_min', 90]], 'hourly': 350, 'daily_max': 3000, 'max_free_min': 60}, 0), ({'minutes': 150, 'validations': [['free_min', 60], ['free_min', 60], ['free_min', 60]], 'hourly': 300, 'daily_max': 2400, 'max_free_min': 120}, 300)], [({'minutes': 150, 'validations': [['free_min', 60], ['free_min', 60], ['free_min', 60]], 'hourly': 300, 'daily_max': 2400, 'max_free_min': 120}, 300), ({'minutes': 200, 'validations': [['pct', 25], ['pct', 10], ['flat', 100], ['flat', 250]], 'hourly': 300, 'daily_max': 2400, 'max_free_min': 120}, 550), ({'minutes': 95, 'validations': [['free_min', 30]], 'hourly': 350, 'daily_max': 2400, 'max_free_min': 120}, 700), ({'minutes': 485, 'validations': [['pct', 10], ['free_min', 60]], 'hourly': 350, 'daily_max': 3000, 'max_free_min': 120}, 2520), ({'minutes': 37, 'validations': [['free_min', 90], ['pct', 50], ['flat', 500]], 'hourly': 200, 'daily_max': 1800, 'max_free_min': 60}, 0), ({'minutes': 12, 'validations': [['flat', 500], ['flat', 500]], 'hourly': 350, 'daily_max': 3000, 'max_free_min': 180}, 0), ({'minutes': 1247, 'validations': [['flat', 250]], 'hourly': 300, 'daily_max': 2400, 'max_free_min': 180}, 2150), ({'minutes': 161, 'validations': [['free_min', 60], ['flat', 500], ['flat', 250]], 'hourly': 300, 'daily_max': 1800, 'max_free_min': 120}, 0)]]
for i, (args, expected) in enumerate(fixtures[N-1]):
    check('fee oracle' + ' %d' % i, solve(args), 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:
Merchant parking validations returns a wrong result when flat validations can drive the fee negative.

Input {minutes (single day), validations: [[free_min|pct|flat, value]], hourly, daily_max, max_free_min}. Free minutes from all validations are summed, capped at max_free_min and deducted from the stay before rating. The fee is started hours * hourly capped at daily_max. Only the largest percentage validation applies (discount floored to cents), then all flat validations are subtracted, never below zero.

split: open-access


broken_source:
"""Failure Map reference implementation. Python standard library only."""
import json

N = 1
observations = []
def solve(x):
    free = min(x['max_free_min'], sum(v for k, v in x['validations'] if k == 'free_min'))
    bill = max(0, x['minutes'] - free)
    fee = min(x['daily_max'], -(-bill // 60) * x['hourly'])
    pcts = [v for k, v in x['validations'] if k == 'pct']
    if pcts:
        fee -= fee * max(pcts) // 100
    flat = max([0] + [v for k, v in x['validations'] if k == 'flat'])
    return max(0, fee - flat)
def check(label, actual, expected):
    observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
fixtures = [[({'minutes': 552, 'validations': [['flat', 250], ['flat', 500], ['free_min', 15], ['free_min', 15]], 'hourly': 350, 'daily_max': 3000, 'max_free_min': 120}, 2250), ({'minutes': 839, 'validations': [['flat', 250], ['free_min', 60], ['flat', 500]], 'hourly': 200, 'daily_max': 1800, 'max_free_min': 120}, 1050), ({'minutes': 95, 'validations': [['free_min', 30]], 'hourly': 350, 'daily_max': 2400, 'max_free_min': 120}, 700), ({'minutes': 977, 'validations': [['flat', 100]], 'hourly': 300, 'daily_max': 2400, 'max_free_min': 180}, 2300), ({'minutes': 150, 'validations': [['free_min', 60], ['free_min', 60], ['free_min', 60]], 'hourly': 300, 'daily_max': 2400, 'max_free_min': 120}, 300), ({'minutes': 1179, 'validations': [['pct', 10], ['flat', 100]], 'hourly': 200, 'daily_max': 3000, 'max_free_min': 180}, 2600), ({'minutes': 226, 'validations': [], 'hourly': 200, 'daily_max': 1800, 'max_free_min': 180}, 800), ({'minutes': 200, 'validations': [['pct', 25], ['pct', 10], ['flat', 100], ['flat', 250]], 'hourly': 300, 'daily_max': 2400, 'max_free_min': 120}, 550)], [({'minutes': 200, 'validations': [['pct', 25], ['pct', 10], ['flat', 100], ['flat', 250]], 'hourly': 300, 'daily_max': 2400, 'max_free_min': 120}, 550), ({'minutes': 753, 'validations': [['flat', 500], ['free_min', 90], ['free_min', 30], ['flat', 250]], 'hourly': 300, 'daily_max': 1800, 'max_free_min': 60}, 1050), ({'minutes': 780, 'validations': [], 'hourly': 350, 'daily_max': 2400, 'max_free_min': 180}, 2400), ({'minutes': 150, 'validations': [['free_min', 60], ['free_min', 60], ['free_min', 60]], 'hourly': 300, 'daily_max': 2400, 'max_free_min': 120}, 300), ({'minutes': 945, 'validations': [['free_min', 60], ['pct', 50], ['free_min', 30]], 'hourly': 200, 'daily_max': 2400, 'max_free_min': 60}, 1200), ({'minutes': 590, 'validations': [['pct', 10], ['flat', 250], ['flat', 500], ['pct', 50]], 'hourly': 200, 'daily_max': 3000, 'max_free_min': 120}, 250), ({'minutes': 738, 'validations': [['free_min', 30], ['free_min', 120], ['flat', 100], ['free_min', 120]], 'hourly': 350, 'daily_max': 2400, 'max_free_min': 120}, 2300), ({'minutes': 95, 'validations': [['free_min', 30]], 'hourly': 350, 'daily_max': 2400, 'max_free_min': 120}, 700)], [({'minutes': 761, 'validations': [['free_min', 90], ['flat', 100], ['free_min', 30], ['flat', 500]], 'hourly': 300, 'daily_max': 2400, 'max_free_min': 120}, 1800), ({'minutes': 745, 'validations': [['flat', 100], ['free_min', 90], ['flat', 250], ['pct', 25]], 'hourly': 350, 'daily_max': 2400, 'max_free_min': 60}, 1450), ({'minutes': 200, 'validations': [['pct', 25], ['pct', 10], ['flat', 100], ['flat', 250]], 'hourly': 300, 'daily_max': 2400, 'max_free_min': 120}, 550), ({'minutes': 1258, 'validations': [], 'hourly': 350, 'daily_max': 3000, 'max_free_min': 60}, 3000), ({'minutes': 1149, 'validations': [], 'hourly': 300, 'daily_max': 2400, 'max_free_min': 60}, 2400), ({'minutes': 1112, 'validations': [['flat', 100], ['pct', 10]], 'hourly': 350, 'daily_max': 3000, 'max_free_min': 60}, 2600), ({'minutes': 150, 'validations': [['free_min', 60], ['free_min', 60], ['free_min', 60]], 'hourly': 300, 'daily_max': 2400, 'max_free_min': 120}, 300), ({'minutes': 95, 'validations': [['free_min', 30]], 'hourly': 350, 'daily_max': 2400, 'max_free_min': 120}, 700)], [({'minutes': 150, 'validations': [['free_min', 60], ['free_min', 60], ['free_min', 60]], 'hourly': 300, 'daily_max': 2400, 'max_free_min': 120}, 300), ({'minutes': 1372, 'validations': [['pct', 10], ['flat', 250], ['flat', 500], ['free_min', 60]], 'hourly': 350, 'daily_max': 3000, 'max_free_min': 60}, 1950), ({'minutes': 95, 'validations': [['free_min', 30]], 'hourly': 350, 'daily_max': 2400, 'max_free_min': 120}, 700), ({'minutes': 814, 'validations': [['free_min', 90], ['pct', 50]], 'hourly': 300, 'daily_max': 1800, 'max_free_min': 120}, 900), ({'minutes': 200, 'validations': [['pct', 25], ['pct', 10], ['flat', 100], ['flat', 250]], 'hourly': 300, 'daily_max': 2400, 'max_free_min': 120}, 550), ({'minutes': 1395, 'validations': [['flat', 250], ['free_min', 90], ['free_min', 90], ['flat', 500]], 'hourly': 300, 'daily_max': 2400, 'max_free_min': 120}, 1650), ({'minutes': 158, 'validations': [['flat', 250], ['free_min', 90], ['free_min', 30], ['pct', 10]], 'hourly': 200, 'daily_max': 3000, 'max_free_min': 120}, 0), ({'minutes': 592, 'validations': [['free_min', 15], ['flat', 250]], 'hourly': 350, 'daily_max': 3000, 'max_free_min': 180}, 2750)], [({'minutes': 542, 'validations': [['free_min', 30], ['pct', 25], ['flat', 100], ['pct', 10]], 'hourly': 350, 'daily_max': 2400, 'max_free_min': 120}, 1700), ({'minutes': 1169, 'validations': [['flat', 250], ['flat', 100]], 'hourly': 300, 'daily_max': 3000, 'max_free_min': 180}, 2650), ({'minutes': 150, 'validations': [['free_min', 60], ['free_min', 60], ['free_min', 60]], 'hourly': 300, 'daily_max': 2400, 'max_free_min': 120}, 300), ({'minutes': 1262, 'validations': [['flat', 100], ['free_min', 90], ['flat', 250]], 'hourly': 200, 'daily_max': 2400, 'max_free_min': 180}, 2050), ({'minutes': 200, 'validations': [['pct', 25], ['pct', 10], ['flat', 100], ['flat', 250]], 'hourly': 300, 'daily_max': 2400, 'max_free_min': 120}, 550), ({'minutes': 593, 'validations': [['flat', 100]], 'hourly': 350, 'daily_max': 2400, 'max_free_min': 180}, 2300), ({'minutes': 95, 'validations': [['free_min', 30]], 'hourly': 350, 'daily_max': 2400, 'max_free_min': 120}, 700), ({'minutes': 1364, 'validations': [['free_min', 60], ['pct', 10]], 'hourly': 200, 'daily_max': 3000, 'max_free_min': 120}, 2700)]]
for i, (args, expected) in enumerate(fixtures[N-1]):
    check('fee oracle' + ' %d' % i, solve(args), 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

N = 1
observations = []
def solve(x):
    free = min(x['max_free_min'], sum(v for k, v in x['validations'] if k == 'free_min'))
    bill = max(0, x['minutes'] - free)
    fee = min(x['daily_max'], -(-bill // 60) * x['hourly'])
    pcts = [v for k, v in x['validations'] if k == 'pct']
    if pcts:
        fee -= fee * max(pcts) // 100
    flat = sum(v for k, v in x['validations'] if k == 'flat') if not pcts else 0
    return max(0, fee - flat)
def check(label, actual, expected):
    observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
fixtures = [[({'minutes': 552, 'validations': [['flat', 250], ['flat', 500], ['free_min', 15], ['free_min', 15]], 'hourly': 350, 'daily_max': 3000, 'max_free_min': 120}, 2250), ({'minutes': 839, 'validations': [['flat', 250], ['free_min', 60], ['flat', 500]], 'hourly': 200, 'daily_max': 1800, 'max_free_min': 120}, 1050), ({'minutes': 95, 'validations': [['free_min', 30]], 'hourly': 350, 'daily_max': 2400, 'max_free_min': 120}, 700), ({'minutes': 977, 'validations': [['flat', 100]], 'hourly': 300, 'daily_max': 2400, 'max_free_min': 180}, 2300), ({'minutes': 150, 'validations': [['free_min', 60], ['free_min', 60], ['free_min', 60]], 'hourly': 300, 'daily_max': 2400, 'max_free_min': 120}, 300), ({'minutes': 1179, 'validations': [['pct', 10], ['flat', 100]], 'hourly': 200, 'daily_max': 3000, 'max_free_min': 180}, 2600), ({'minutes': 226, 'validations': [], 'hourly': 200, 'daily_max': 1800, 'max_free_min': 180}, 800), ({'minutes': 200, 'validations': [['pct', 25], ['pct', 10], ['flat', 100], ['flat', 250]], 'hourly': 300, 'daily_max': 2400, 'max_free_min': 120}, 550)], [({'minutes': 200, 'validations': [['pct', 25], ['pct', 10], ['flat', 100], ['flat', 250]], 'hourly': 300, 'daily_max': 2400, 'max_free_min': 120}, 550), ({'minutes': 753, 'validations': [['flat', 500], ['free_min', 90], ['free_min', 30], ['flat', 250]], 'hourly': 300, 'daily_max': 1800, 'max_free_min': 60}, 1050), ({'minutes': 780, 'validations': [], 'hourly': 350, 'daily_max': 2400, 'max_free_min': 180}, 2400), ({'minutes': 150, 'validations': [['free_min', 60], ['free_min', 60], ['free_min', 60]], 'hourly': 300, 'daily_max': 2400, 'max_free_min': 120}, 300), ({'minutes': 945, 'validations': [['free_min', 60], ['pct', 50], ['free_min', 30]], 'hourly': 200, 'daily_max': 2400, 'max_free_min': 60}, 1200), ({'minutes': 590, 'validations': [['pct', 10], ['flat', 250], ['flat', 500], ['pct', 50]], 'hourly': 200, 'daily_max': 3000, 'max_free_min': 120}, 250), ({'minutes': 738, 'validations': [['free_min', 30], ['free_min', 120], ['flat', 100], ['free_min', 120]], 'hourly': 350, 'daily_max': 2400, 'max_free_min': 120}, 2300), ({'minutes': 95, 'validations': [['free_min', 30]], 'hourly': 350, 'daily_max': 2400, 'max_free_min': 120}, 700)], [({'minutes': 761, 'validations': [['free_min', 90], ['flat', 100], ['free_min', 30], ['flat', 500]], 'hourly': 300, 'daily_max': 2400, 'max_free_min': 120}, 1800), ({'minutes': 745, 'validations': [['flat', 100], ['free_min', 90], ['flat', 250], ['pct', 25]], 'hourly': 350, 'daily_max': 2400, 'max_free_min': 60}, 1450), ({'minutes': 200, 'validations': [['pct', 25], ['pct', 10], ['flat', 100], ['flat', 250]], 'hourly': 300, 'daily_max': 2400, 'max_free_min': 120}, 550), ({'minutes': 1258, 'validations': [], 'hourly': 350, 'daily_max': 3000, 'max_free_min': 60}, 3000), ({'minutes': 1149, 'validations': [], 'hourly': 300, 'daily_max': 2400, 'max_free_min': 60}, 2400), ({'minutes': 1112, 'validations': [['flat', 100], ['pct', 10]], 'hourly': 350, 'daily_max': 3000, 'max_free_min': 60}, 2600), ({'minutes': 150, 'validations': [['free_min', 60], ['free_min', 60], ['free_min', 60]], 'hourly': 300, 'daily_max': 2400, 'max_free_min': 120}, 300), ({'minutes': 95, 'validations': [['free_min', 30]], 'hourly': 350, 'daily_max': 2400, 'max_free_min': 120}, 700)], [({'minutes': 150, 'validations': [['free_min', 60], ['free_min', 60], ['free_min', 60]], 'hourly': 300, 'daily_max': 2400, 'max_free_min': 120}, 300), ({'minutes': 1372, 'validations': [['pct', 10], ['flat', 250], ['flat', 500], ['free_min', 60]], 'hourly': 350, 'daily_max': 3000, 'max_free_min': 60}, 1950), ({'minutes': 95, 'validations': [['free_min', 30]], 'hourly': 350, 'daily_max': 2400, 'max_free_min': 120}, 700), ({'minutes': 814, 'validations': [['free_min', 90], ['pct', 50]], 'hourly': 300, 'daily_max': 1800, 'max_free_min': 120}, 900), ({'minutes': 200, 'validations': [['pct', 25], ['pct', 10], ['flat', 100], ['flat', 250]], 'hourly': 300, 'daily_max': 2400, 'max_free_min': 120}, 550), ({'minutes': 1395, 'validations': [['flat', 250], ['free_min', 90], ['free_min', 90], ['flat', 500]], 'hourly': 300, 'daily_max': 2400, 'max_free_min': 120}, 1650), ({'minutes': 158, 'validations': [['flat', 250], ['free_min', 90], ['free_min', 30], ['pct', 10]], 'hourly': 200, 'daily_max': 3000, 'max_free_min': 120}, 0), ({'minutes': 592, 'validations': [['free_min', 15], ['flat', 250]], 'hourly': 350, 'daily_max': 3000, 'max_free_min': 180}, 2750)], [({'minutes': 542, 'validations': [['free_min', 30], ['pct', 25], ['flat', 100], ['pct', 10]], 'hourly': 350, 'daily_max': 2400, 'max_free_min': 120}, 1700), ({'minutes': 1169, 'validations': [['flat', 250], ['flat', 100]], 'hourly': 300, 'daily_max': 3000, 'max_free_min': 180}, 2650), ({'minutes': 150, 'validations': [['free_min', 60], ['free_min', 60], ['free_min', 60]], 'hourly': 300, 'daily_max': 2400, 'max_free_min': 120}, 300), ({'minutes': 1262, 'validations': [['flat', 100], ['free_min', 90], ['flat', 250]], 'hourly': 200, 'daily_max': 2400, 'max_free_min': 180}, 2050), ({'minutes': 200, 'validations': [['pct', 25], ['pct', 10], ['flat', 100], ['flat', 250]], 'hourly': 300, 'daily_max': 2400, 'max_free_min': 120}, 550), ({'minutes': 593, 'validations': [['flat', 100]], 'hourly': 350, 'daily_max': 2400, 'max_free_min': 180}, 2300), ({'minutes': 95, 'validations': [['free_min', 30]], 'hourly': 350, 'daily_max': 2400, 'max_free_min': 120}, 700), ({'minutes': 1364, 'validations': [['free_min', 60], ['pct', 10]], 'hourly': 200, 'daily_max': 3000, 'max_free_min': 120}, 2700)]]
for i, (args, expected) in enumerate(fixtures[N-1]):
    check('fee oracle' + ' %d' % i, solve(args), 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:
Merchant parking validations returns a wrong result when only the largest flat validation applies.

Input {minutes (single day), validations: [[free_min|pct|flat, value]], hourly, daily_max, max_free_min}. Free minutes from all validations are summed, capped at max_free_min and deducted from the stay before rating. The fee is started hours * hourly capped at daily_max. Only the largest percentage validation applies (discount floored to cents), then all flat validations are subtracted, never below zero.

split: open-access


broken_source:
"""Failure Map reference implementation. Python standard library only."""
import json

N = 1
observations = []
def solve(x):
    energy = x['wh'] // 1000 * x['price_per_kwh']
    idle = max(0, x['unplug'] - x['charge_end'] - x['idle_grace'])
    idle_fee = min(x['idle_cap'], -(-idle // 10) * x['idle_rate'])
    total = x['session_fee'] + energy + idle_fee
    return {'energy': energy, 'idle': idle_fee, 'total': max(x['min_fee'], total)}
def check(label, actual, expected):
    observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
fixtures = [[({'plug_in': 351, 'charge_end': 512, 'unplug': 522, 'wh': 7509, 'price_per_kwh': 35, 'idle_grace': 15, 'idle_rate': 40, 'idle_cap': 1000, 'session_fee': 150, 'min_fee': 500}, {'energy': 263, 'idle': 0, 'total': 500}), ({'plug_in': 30, 'charge_end': 45, 'unplug': 400, 'wh': 800, 'price_per_kwh': 29, 'idle_grace': 10, 'idle_rate': 100, 'idle_cap': 1000, 'session_fee': 0, 'min_fee': 500}, {'energy': 24, 'idle': 1000, 'total': 1024}), ({'plug_in': 21, 'charge_end': 121, 'unplug': 136, 'wh': 56596, 'price_per_kwh': 45, 'idle_grace': 10, 'idle_rate': 40, 'idle_cap': 1000, 'session_fee': 100, 'min_fee': 200}, {'energy': 2547, 'idle': 40, 'total': 2687}), ({'plug_in': 145, 'charge_end': 298, 'unplug': 303, 'wh': 58365, 'price_per_kwh': 45, 'idle_grace': 10, 'idle_rate': 40, 'idle_cap': 3000, 'session_fee': 150, 'min_fee': 300}, {'energy': 2627, 'idle': 0, 'total': 2777}), ({'plug_in': 0, 'charge_end': 90, 'unplug': 106, 'wh': 12500, 'price_per_kwh': 35, 'idle_grace': 15, 'idle_rate': 50, 'idle_cap': 2000, 'session_fee': 100, 'min_fee': 300}, {'energy': 438, 'idle': 50, 'total': 588}), ({'plug_in': 186, 'charge_end': 330, 'unplug': 355, 'wh': 23566, 'price_per_kwh': 29, 'idle_grace': 15, 'idle_rate': 100, 'idle_cap': 3000, 'session_fee': 150, 'min_fee': 500}, {'energy': 684, 'idle': 100, 'total': 934}), ({'plug_in': 195, 'charge_end': 466, 'unplug': 471, 'wh': 49517, 'price_per_kwh': 29, 'idle_grace': 10, 'idle_rate': 100, 'idle_cap': 2000, 'session_fee': 100, 'min_fee': 300}, {'energy': 1436, 'idle': 0, 'total': 1536}), ({'plug_in': 33, 'charge_end': 60, 'unplug': 85, 'wh': 12076, 'price_per_kwh': 45, 'idle_grace': 10, 'idle_rate': 50, 'idle_cap': 1000, 'session_fee': 150, 'min_fee': 200}, {'energy': 544, 'idle': 100, 'total': 794})], [({'plug_in': 559, 'charge_end': 572, 'unplug': 772, 'wh': 45833, 'price_per_kwh': 52, 'idle_grace': 15, 'idle_rate': 50, 'idle_cap': 1000, 'session_fee': 100, 'min_fee': 200}, {'energy': 2384, 'idle': 950, 'total': 3434}), ({'plug_in': 159, 'charge_end': 336, 'unplug': 352, 'wh': 25795, 'price_per_kwh': 35, 'idle_grace': 10, 'idle_rate': 100, 'idle_cap': 1000, 'session_fee': 100, 'min_fee': 500}, {'energy': 903, 'idle': 100, 'total': 1103}), ({'plug_in': 194, 'charge_end': 435, 'unplug': 440, 'wh': 59915, 'price_per_kwh': 29, 'idle_grace': 10, 'idle_rate': 50, 'idle_cap': 2000, 'session_fee': 0, 'min_fee': 500}, {'energy': 1738, 'idle': 0, 'total': 1738}), ({'plug_in': 528, 'charge_end': 654, 'unplug': 669, 'wh': 12153, 'price_per_kwh': 29, 'idle_grace': 15, 'idle_rate': 40, 'idle_cap': 3000, 'session_fee': 0, 'min_fee': 200}, {'energy': 353, 'idle': 0, 'total': 353}), ({'plug_in': 431, 'charge_end': 619, 'unplug': 624, 'wh': 55493, 'price_per_kwh': 52, 'idle_grace': 15, 'idle_rate': 40, 'idle_cap': 3000, 'session_fee': 150, 'min_fee': 200}, {'energy': 2886, 'idle': 0, 'total': 3036}), ({'plug_in': 81, 'charge_end': 343, 'unplug': 543, 'wh': 5132, 'price_per_kwh': 29, 'idle_grace': 10, 'idle_rate': 100, 'idle_cap': 1000, 'session_fee': 100, 'min_fee': 500}, {'energy': 149, 'idle': 1000, 'total': 1249}), ({'plug_in': 30, 'charge_end': 45, 'unplug': 400, 'wh': 800, 'price_per_kwh': 29, 'idle_grace': 10, 'idle_rate': 100, 'idle_cap': 1000, 'session_fee': 0, 'min_fee': 500}, {'energy': 24, 'idle': 1000, 'total': 1024}), ({'plug_in': 0, 'charge_end': 90, 'unplug': 106, 'wh': 12500, 'price_per_kwh': 35, 'idle_grace': 15, 'idle_rate': 50, 'idle_cap': 2000, 'session_fee': 100, 'min_fee': 300}, {'energy': 438, 'idle': 50, 'total': 588})], [({'plug_in': 304, 'charge_end': 396, 'unplug': 411, 'wh': 5781, 'price_per_kwh': 29, 'idle_grace': 15, 'idle_rate': 50, 'idle_cap': 1000, 'session_fee': 150, 'min_fee': 300}, {'energy': 168, 'idle': 0, 'total': 318}), ({'plug_in': 185, 'charge_end': 272, 'unplug': 277, 'wh': 54298, 'price_per_kwh': 35, 'idle_grace': 15, 'idle_rate': 100, 'idle_cap': 2000, 'session_fee': 0, 'min_fee': 300}, {'energy': 1901, 'idle': 0, 'total': 1901}), ({'plug_in': 517, 'charge_end': 530, 'unplug': 545, 'wh': 32951, 'price_per_kwh': 29, 'idle_grace': 10, 'idle_rate': 100, 'idle_cap': 1000, 'session_fee': 100, 'min_fee': 200}, {'energy': 956, 'idle': 100, 'total': 1156}), ({'plug_in': 516, 'charge_end': 713, 'unplug': 713, 'wh': 4148, 'price_per_kwh': 35, 'idle_grace': 10, 'idle_rate': 40, 'idle_cap': 3000, 'session_fee': 0, 'min_fee': 200}, {'energy': 146, 'idle': 0, 'total': 200}), ({'plug_in': 0, 'charge_end': 90, 'unplug': 106, 'wh': 12500, 'price_per_kwh': 35, 'idle_grace': 15, 'idle_rate': 50, 'idle_cap': 2000, 'session_fee': 100, 'min_fee': 300}, {'energy': 438, 'idle': 50, 'total': 588}), ({'plug_in': 368, 'charge_end': 458, 'unplug': 468, 'wh': 47000, 'price_per_kwh': 45, 'idle_grace': 15, 'idle_rate': 50, 'idle_cap': 1000, 'session_fee': 0, 'min_fee': 200}, {'energy': 2115, 'idle': 0, 'total': 2115}), ({'plug_in': 372, 'charge_end': 436, 'unplug': 446, 'wh': 50026, 'price_per_kwh': 29, 'idle_grace': 10, 'idle_rate': 50, 'idle_cap': 1000, 'session_fee': 100, 'min_fee': 300}, {'energy': 1451, 'idle': 0, 'total': 1551}), ({'plug_in': 30, 'charge_end': 45, 'unplug': 400, 'wh': 800, 'price_per_kwh': 29, 'idle_grace': 10, 'idle_rate': 100, 'idle_cap': 1000, 'session_fee': 0, 'min_fee': 500}, {'energy': 24, 'idle': 1000, 'total': 1024})], [({'plug_in': 0, 'charge_end': 90, 'unplug': 106, 'wh': 12500, 'price_per_kwh': 35, 'idle_grace': 15, 'idle_rate': 50, 'idle_cap': 2000, 'session_fee': 100, 'min_fee': 300}, {'energy': 438, 'idle': 50, 'total': 588}), ({'plug_in': 30, 'charge_end': 45, 'unplug': 400, 'wh': 800, 'price_per_kwh': 29, 'idle_grace': 10, 'idle_rate': 100, 'idle_cap': 1000, 'session_fee': 0, 'min_fee': 500}, {'energy': 24, 'idle': 1000, 'total': 1024}), ({'plug_in': 225, 'charge_end': 273, 'unplug': 278, 'wh': 11802, 'price_per_kwh': 29, 'idle_grace': 15, 'idle_rate': 100, 'idle_cap': 3000, 'session_fee': 100, 'min_fee': 300}, {'energy': 343, 'idle': 0, 'total': 443}), ({'plug_in': 205, 'charge_end': 441, 'unplug': 501, 'wh': 10080, 'price_per_kwh': 45, 'idle_grace': 10, 'idle_rate': 50, 'idle_cap': 3000, 'session_fee': 0, 'min_fee': 200}, {'energy': 454, 'idle': 250, 'total': 704}), ({'plug_in': 287, 'charge_end': 319, 'unplug': 329, 'wh': 40679, 'price_per_kwh': 29, 'idle_grace': 15, 'idle_rate': 40, 'idle_cap': 1000, 'session_fee': 100, 'min_fee': 200}, {'energy': 1180, 'idle': 0, 'total': 1280}), ({'plug_in': 188, 'charge_end': 300, 'unplug': 700, 'wh': 27871, 'price_per_kwh': 35, 'idle_grace': 15, 'idle_rate': 40, 'idle_cap': 1000, 'session_fee': 0, 'min_fee': 300}, {'energy': 976, 'idle': 1000, 'total': 1976}), ({'plug_in': 295, 'charge_end': 451, 'unplug': 466, 'wh': 26332, 'price_per_kwh': 35, 'idle_grace': 15, 'idle_rate': 50, 'idle_cap': 1000, 'session_fee': 150, 'min_fee': 300}, {'energy': 922, 'idle': 0, 'total': 1072}), ({'plug_in': 51, 'charge_end': 100, 'unplug': 110, 'wh': 10852, 'price_per_kwh': 52, 'idle_grace': 15, 'idle_rate': 100, 'idle_cap': 1000, 'session_fee': 100, 'min_fee': 300}, {'energy': 565, 'idle': 0, 'total': 665})], [({'plug_in': 581, 'charge_end': 789, 'unplug': 805, 'wh': 25000, 'price_per_kwh': 35, 'idle_grace': 15, 'idle_rate': 50, 'idle_cap': 1000, 'session_fee': 100, 'min_fee': 200}, {'energy': 875, 'idle': 50, 'total': 1025}), ({'plug_in': 375, 'charge_end': 498, 'unplug': 514, 'wh': 1169, 'price_per_kwh': 29, 'idle_grace': 10, 'idle_rate': 50, 'idle_cap': 1000, 'session_fee': 0, 'min_fee': 500}, {'energy': 34, 'idle': 50, 'total': 500}), ({'plug_in': 159, 'charge_end': 387, 'unplug': 412, 'wh': 39938, 'price_per_kwh': 35, 'idle_grace': 10, 'idle_rate': 50, 'idle_cap': 3000, 'session_fee': 100, 'min_fee': 500}, {'energy': 1398, 'idle': 100, 'total': 1598}), ({'plug_in': 30, 'charge_end': 45, 'unplug': 400, 'wh': 800, 'price_per_kwh': 29, 'idle_grace': 10, 'idle_rate': 100, 'idle_cap': 1000, 'session_fee': 0, 'min_fee': 500}, {'energy': 24, 'idle': 1000, 'total': 1024}), ({'plug_in': 124, 'charge_end': 356, 'unplug': 372, 'wh': 55526, 'price_per_kwh': 52, 'idle_grace': 10, 'idle_rate': 100, 'idle_cap': 1000, 'session_fee': 100, 'min_fee': 200}, {'energy': 2888, 'idle': 100, 'total': 3088}), ({'plug_in': 0, 'charge_end': 90, 'unplug': 106, 'wh': 12500, 'price_per_kwh': 35, 'idle_grace': 15, 'idle_rate': 50, 'idle_cap': 2000, 'session_fee': 100, 'min_fee': 300}, {'energy': 438, 'idle': 50, 'total': 588}), ({'plug_in': 72, 'charge_end': 247, 'unplug': 263, 'wh': 14417, 'price_per_kwh': 35, 'idle_grace': 10, 'idle_rate': 50, 'idle_cap': 3000, 'session_fee': 0, 'min_fee': 500}, {'energy': 505, 'idle': 50, 'total': 555}), ({'plug_in': 292, 'charge_end': 503, 'unplug': 503, 'wh': 48283, 'price_per_kwh': 29, 'idle_grace': 15, 'idle_rate': 100, 'idle_cap': 1000, 'session_fee': 100, 'min_fee': 200}, {'energy': 1401, 'idle': 0, 'total': 1501})]]
for i, (args, expected) in enumerate(fixtures[N-1]):
    check('fee oracle' + ' %d' % i, solve(args), 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

N = 1
observations = []
def solve(x):
    energy = round(x['wh'] / 1000) * x['price_per_kwh']
    idle = max(0, x['unplug'] - x['charge_end'] - x['idle_grace'])
    idle_fee = min(x['idle_cap'], -(-idle // 10) * x['idle_rate'])
    total = x['session_fee'] + energy + idle_fee
    return {'energy': energy, 'idle': idle_fee, 'total': max(x['min_fee'], total)}
def check(label, actual, expected):
    observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
fixtures = [[({'plug_in': 351, 'charge_end': 512, 'unplug': 522, 'wh': 7509, 'price_per_kwh': 35, 'idle_grace': 15, 'idle_rate': 40, 'idle_cap': 1000, 'session_fee': 150, 'min_fee': 500}, {'energy': 263, 'idle': 0, 'total': 500}), ({'plug_in': 30, 'charge_end': 45, 'unplug': 400, 'wh': 800, 'price_per_kwh': 29, 'idle_grace': 10, 'idle_rate': 100, 'idle_cap': 1000, 'session_fee': 0, 'min_fee': 500}, {'energy': 24, 'idle': 1000, 'total': 1024}), ({'plug_in': 21, 'charge_end': 121, 'unplug': 136, 'wh': 56596, 'price_per_kwh': 45, 'idle_grace': 10, 'idle_rate': 40, 'idle_cap': 1000, 'session_fee': 100, 'min_fee': 200}, {'energy': 2547, 'idle': 40, 'total': 2687}), ({'plug_in': 145, 'charge_end': 298, 'unplug': 303, 'wh': 58365, 'price_per_kwh': 45, 'idle_grace': 10, 'idle_rate': 40, 'idle_cap': 3000, 'session_fee': 150, 'min_fee': 300}, {'energy': 2627, 'idle': 0, 'total': 2777}), ({'plug_in': 0, 'charge_end': 90, 'unplug': 106, 'wh': 12500, 'price_per_kwh': 35, 'idle_grace': 15, 'idle_rate': 50, 'idle_cap': 2000, 'session_fee': 100, 'min_fee': 300}, {'energy': 438, 'idle': 50, 'total': 588}), ({'plug_in': 186, 'charge_end': 330, 'unplug': 355, 'wh': 23566, 'price_per_kwh': 29, 'idle_grace': 15, 'idle_rate': 100, 'idle_cap': 3000, 'session_fee': 150, 'min_fee': 500}, {'energy': 684, 'idle': 100, 'total': 934}), ({'plug_in': 195, 'charge_end': 466, 'unplug': 471, 'wh': 49517, 'price_per_kwh': 29, 'idle_grace': 10, 'idle_rate': 100, 'idle_cap': 2000, 'session_fee': 100, 'min_fee': 300}, {'energy': 1436, 'idle': 0, 'total': 1536}), ({'plug_in': 33, 'charge_end': 60, 'unplug': 85, 'wh': 12076, 'price_per_kwh': 45, 'idle_grace': 10, 'idle_rate': 50, 'idle_cap': 1000, 'session_fee': 150, 'min_fee': 200}, {'energy': 544, 'idle': 100, 'total': 794})], [({'plug_in': 559, 'charge_end': 572, 'unplug': 772, 'wh': 45833, 'price_per_kwh': 52, 'idle_grace': 15, 'idle_rate': 50, 'idle_cap': 1000, 'session_fee': 100, 'min_fee': 200}, {'energy': 2384, 'idle': 950, 'total': 3434}), ({'plug_in': 159, 'charge_end': 336, 'unplug': 352, 'wh': 25795, 'price_per_kwh': 35, 'idle_grace': 10, 'idle_rate': 100, 'idle_cap': 1000, 'session_fee': 100, 'min_fee': 500}, {'energy': 903, 'idle': 100, 'total': 1103}), ({'plug_in': 194, 'charge_end': 435, 'unplug': 440, 'wh': 59915, 'price_per_kwh': 29, 'idle_grace': 10, 'idle_rate': 50, 'idle_cap': 2000, 'session_fee': 0, 'min_fee': 500}, {'energy': 1738, 'idle': 0, 'total': 1738}), ({'plug_in': 528, 'charge_end': 654, 'unplug': 669, 'wh': 12153, 'price_per_kwh': 29, 'idle_grace': 15, 'idle_rate': 40, 'idle_cap': 3000, 'session_fee': 0, 'min_fee': 200}, {'energy': 353, 'idle': 0, 'total': 353}), ({'plug_in': 431, 'charge_end': 619, 'unplug': 624, 'wh': 55493, 'price_per_kwh': 52, 'idle_grace': 15, 'idle_rate': 40, 'idle_cap': 3000, 'session_fee': 150, 'min_fee': 200}, {'energy': 2886, 'idle': 0, 'total': 3036}), ({'plug_in': 81, 'charge_end': 343, 'unplug': 543, 'wh': 5132, 'price_per_kwh': 29, 'idle_grace': 10, 'idle_rate': 100, 'idle_cap': 1000, 'session_fee': 100, 'min_fee': 500}, {'energy': 149, 'idle': 1000, 'total': 1249}), ({'plug_in': 30, 'charge_end': 45, 'unplug': 400, 'wh': 800, 'price_per_kwh': 29, 'idle_grace': 10, 'idle_rate': 100, 'idle_cap': 1000, 'session_fee': 0, 'min_fee': 500}, {'energy': 24, 'idle': 1000, 'total': 1024}), ({'plug_in': 0, 'charge_end': 90, 'unplug': 106, 'wh': 12500, 'price_per_kwh': 35, 'idle_grace': 15, 'idle_rate': 50, 'idle_cap': 2000, 'session_fee': 100, 'min_fee': 300}, {'energy': 438, 'idle': 50, 'total': 588})], [({'plug_in': 304, 'charge_end': 396, 'unplug': 411, 'wh': 5781, 'price_per_kwh': 29, 'idle_grace': 15, 'idle_rate': 50, 'idle_cap': 1000, 'session_fee': 150, 'min_fee': 300}, {'energy': 168, 'idle': 0, 'total': 318}), ({'plug_in': 185, 'charge_end': 272, 'unplug': 277, 'wh': 54298, 'price_per_kwh': 35, 'idle_grace': 15, 'idle_rate': 100, 'idle_cap': 2000, 'session_fee': 0, 'min_fee': 300}, {'energy': 1901, 'idle': 0, 'total': 1901}), ({'plug_in': 517, 'charge_end': 530, 'unplug': 545, 'wh': 32951, 'price_per_kwh': 29, 'idle_grace': 10, 'idle_rate': 100, 'idle_cap': 1000, 'session_fee': 100, 'min_fee': 200}, {'energy': 956, 'idle': 100, 'total': 1156}), ({'plug_in': 516, 'charge_end': 713, 'unplug': 713, 'wh': 4148, 'price_per_kwh': 35, 'idle_grace': 10, 'idle_rate': 40, 'idle_cap': 3000, 'session_fee': 0, 'min_fee': 200}, {'energy': 146, 'idle': 0, 'total': 200}), ({'plug_in': 0, 'charge_end': 90, 'unplug': 106, 'wh': 12500, 'price_per_kwh': 35, 'idle_grace': 15, 'idle_rate': 50, 'idle_cap': 2000, 'session_fee': 100, 'min_fee': 300}, {'energy': 438, 'idle': 50, 'total': 588}), ({'plug_in': 368, 'charge_end': 458, 'unplug': 468, 'wh': 47000, 'price_per_kwh': 45, 'idle_grace': 15, 'idle_rate': 50, 'idle_cap': 1000, 'session_fee': 0, 'min_fee': 200}, {'energy': 2115, 'idle': 0, 'total': 2115}), ({'plug_in': 372, 'charge_end': 436, 'unplug': 446, 'wh': 50026, 'price_per_kwh': 29, 'idle_grace': 10, 'idle_rate': 50, 'idle_cap': 1000, 'session_fee': 100, 'min_fee': 300}, {'energy': 1451, 'idle': 0, 'total': 1551}), ({'plug_in': 30, 'charge_end': 45, 'unplug': 400, 'wh': 800, 'price_per_kwh': 29, 'idle_grace': 10, 'idle_rate': 100, 'idle_cap': 1000, 'session_fee': 0, 'min_fee': 500}, {'energy': 24, 'idle': 1000, 'total': 1024})], [({'plug_in': 0, 'charge_end': 90, 'unplug': 106, 'wh': 12500, 'price_per_kwh': 35, 'idle_grace': 15, 'idle_rate': 50, 'idle_cap': 2000, 'session_fee': 100, 'min_fee': 300}, {'energy': 438, 'idle': 50, 'total': 588}), ({'plug_in': 30, 'charge_end': 45, 'unplug': 400, 'wh': 800, 'price_per_kwh': 29, 'idle_grace': 10, 'idle_rate': 100, 'idle_cap': 1000, 'session_fee': 0, 'min_fee': 500}, {'energy': 24, 'idle': 1000, 'total': 1024}), ({'plug_in': 225, 'charge_end': 273, 'unplug': 278, 'wh': 11802, 'price_per_kwh': 29, 'idle_grace': 15, 'idle_rate': 100, 'idle_cap': 3000, 'session_fee': 100, 'min_fee': 300}, {'energy': 343, 'idle': 0, 'total': 443}), ({'plug_in': 205, 'charge_end': 441, 'unplug': 501, 'wh': 10080, 'price_per_kwh': 45, 'idle_grace': 10, 'idle_rate': 50, 'idle_cap': 3000, 'session_fee': 0, 'min_fee': 200}, {'energy': 454, 'idle': 250, 'total': 704}), ({'plug_in': 287, 'charge_end': 319, 'unplug': 329, 'wh': 40679, 'price_per_kwh': 29, 'idle_grace': 15, 'idle_rate': 40, 'idle_cap': 1000, 'session_fee': 100, 'min_fee': 200}, {'energy': 1180, 'idle': 0, 'total': 1280}), ({'plug_in': 188, 'charge_end': 300, 'unplug': 700, 'wh': 27871, 'price_per_kwh': 35, 'idle_grace': 15, 'idle_rate': 40, 'idle_cap': 1000, 'session_fee': 0, 'min_fee': 300}, {'energy': 976, 'idle': 1000, 'total': 1976}), ({'plug_in': 295, 'charge_end': 451, 'unplug': 466, 'wh': 26332, 'price_per_kwh': 35, 'idle_grace': 15, 'idle_rate': 50, 'idle_cap': 1000, 'session_fee': 150, 'min_fee': 300}, {'energy': 922, 'idle': 0, 'total': 1072}), ({'plug_in': 51, 'charge_end': 100, 'unplug': 110, 'wh': 10852, 'price_per_kwh': 52, 'idle_grace': 15, 'idle_rate': 100, 'idle_cap': 1000, 'session_fee': 100, 'min_fee': 300}, {'energy': 565, 'idle': 0, 'total': 665})], [({'plug_in': 581, 'charge_end': 789, 'unplug': 805, 'wh': 25000, 'price_per_kwh': 35, 'idle_grace': 15, 'idle_rate': 50, 'idle_cap': 1000, 'session_fee': 100, 'min_fee': 200}, {'energy': 875, 'idle': 50, 'total': 1025}), ({'plug_in': 375, 'charge_end': 498, 'unplug': 514, 'wh': 1169, 'price_per_kwh': 29, 'idle_grace': 10, 'idle_rate': 50, 'idle_cap': 1000, 'session_fee': 0, 'min_fee': 500}, {'energy': 34, 'idle': 50, 'total': 500}), ({'plug_in': 159, 'charge_end': 387, 'unplug': 412, 'wh': 39938, 'price_per_kwh': 35, 'idle_grace': 10, 'idle_rate': 50, 'idle_cap': 3000, 'session_fee': 100, 'min_fee': 500}, {'energy': 1398, 'idle': 100, 'total': 1598}), ({'plug_in': 30, 'charge_end': 45, 'unplug': 400, 'wh': 800, 'price_per_kwh': 29, 'idle_grace': 10, 'idle_rate': 100, 'idle_cap': 1000, 'session_fee': 0, 'min_fee': 500}, {'energy': 24, 'idle': 1000, 'total': 1024}), ({'plug_in': 124, 'charge_end': 356, 'unplug': 372, 'wh': 55526, 'price_per_kwh': 52, 'idle_grace': 10, 'idle_rate': 100, 'idle_cap': 1000, 'session_fee': 100, 'min_fee': 200}, {'energy': 2888, 'idle': 100, 'total': 3088}), ({'plug_in': 0, 'charge_end': 90, 'unplug': 106, 'wh': 12500, 'price_per_kwh': 35, 'idle_grace': 15, 'idle_rate': 50, 'idle_cap': 2000, 'session_fee': 100, 'min_fee': 300}, {'energy': 438, 'idle': 50, 'total': 588}), ({'plug_in': 72, 'charge_end': 247, 'unplug': 263, 'wh': 14417, 'price_per_kwh': 35, 'idle_grace': 10, 'idle_rate': 50, 'idle_cap': 3000, 'session_fee': 0, 'min_fee': 500}, {'energy': 505, 'idle': 50, 'total': 555}), ({'plug_in': 292, 'charge_end': 503, 'unplug': 503, 'wh': 48283, 'price_per_kwh': 29, 'idle_grace': 15, 'idle_rate': 100, 'idle_cap': 1000, 'session_fee': 100, 'min_fee': 200}, {'energy': 1401, 'idle': 0, 'total': 1501})]]
for i, (args, expected) in enumerate(fixtures[N-1]):
    check('fee oracle' + ' %d' % i, solve(args), 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:
EV charging session and idle fee returns a wrong result when energy is billed in whole kWh.

Input {plug_in, charge_end, unplug (minutes), wh, price_per_kwh, idle_grace, idle_rate (per started 10 min), idle_cap, session_fee, min_fee}. Energy = ceil(wh*price/1000). Idle minutes = max(0, unplug - charge_end - grace); idle fee = min(idle_cap, started 10-minute blocks * idle_rate). Total = max(min_fee, session_fee + energy + idle). Return {energy, idle, total}.

split: open-access


broken_source:
"""Failure Map reference implementation. Python standard library only."""
import json

N = 1
observations = []
def solve(x):
    energy = -(-x['wh'] * x['price_per_kwh'] // 1000)
    idle = max(0, x['unplug'] - x['plug_in'] - x['idle_grace'])
    idle_fee = min(x['idle_cap'], -(-idle // 10) * x['idle_rate'])
    total = x['session_fee'] + energy + idle_fee
    return {'energy': energy, 'idle': idle_fee, 'total': max(x['min_fee'], total)}
def check(label, actual, expected):
    observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
fixtures = [[({'plug_in': 198, 'charge_end': 364, 'unplug': 379, 'wh': 11108, 'price_per_kwh': 45, 'idle_grace': 10, 'idle_rate': 50, 'idle_cap': 3000, 'session_fee': 0, 'min_fee': 300}, {'energy': 500, 'idle': 50, 'total': 550}), ({'plug_in': 83, 'charge_end': 221, 'unplug': 221, 'wh': 32363, 'price_per_kwh': 29, 'idle_grace': 10, 'idle_rate': 50, 'idle_cap': 3000, 'session_fee': 150, 'min_fee': 300}, {'energy': 939, 'idle': 0, 'total': 1089}), ({'plug_in': 0, 'charge_end': 90, 'unplug': 106, 'wh': 12500, 'price_per_kwh': 35, 'idle_grace': 15, 'idle_rate': 50, 'idle_cap': 2000, 'session_fee': 100, 'min_fee': 300}, {'energy': 438, 'idle': 50, 'total': 588}), ({'plug_in': 463, 'charge_end': 667, 'unplug': 1067, 'wh': 38632, 'price_per_kwh': 35, 'idle_grace': 15, 'idle_rate': 100, 'idle_cap': 2000, 'session_fee': 150, 'min_fee': 200}, {'energy': 1353, 'idle': 2000, 'total': 3503}), ({'plug_in': 300, 'charge_end': 327, 'unplug': 352, 'wh': 2749, 'price_per_kwh': 45, 'idle_grace': 15, 'idle_rate': 100, 'idle_cap': 3000, 'session_fee': 0, 'min_fee': 300}, {'energy': 124, 'idle': 100, 'total': 300}), ({'plug_in': 30, 'charge_end': 45, 'unplug': 400, 'wh': 800, 'price_per_kwh': 29, 'idle_grace': 10, 'idle_rate': 100, 'idle_cap': 1000, 'session_fee': 0, 'min_fee': 500}, {'energy': 24, 'idle': 1000, 'total': 1024}), ({'plug_in': 571, 'charge_end': 611, 'unplug': 671, 'wh': 58013, 'price_per_kwh': 35, 'idle_grace': 10, 'idle_rate': 100, 'idle_cap': 1000, 'session_fee': 150, 'min_fee': 500}, {'energy': 2031, 'idle': 500, 'total': 2681}), ({'plug_in': 585, 'charge_end': 651, 'unplug': 1051, 'wh': 11487, 'price_per_kwh': 45, 'idle_grace': 10, 'idle_rate': 100, 'idle_cap': 2000, 'session_fee': 100, 'min_fee': 300}, {'energy': 517, 'idle': 2000, 'total': 2617})], [({'plug_in': 276, 'charge_end': 449, 'unplug': 849, 'wh': 11301, 'price_per_kwh': 52, 'idle_grace': 10, 'idle_rate': 100, 'idle_cap': 1000, 'session_fee': 150, 'min_fee': 200}, {'energy': 588, 'idle': 1000, 'total': 1738}), ({'plug_in': 30, 'charge_end': 45, 'unplug': 400, 'wh': 800, 'price_per_kwh': 29, 'idle_grace': 10, 'idle_rate': 100, 'idle_cap': 1000, 'session_fee': 0, 'min_fee': 500}, {'energy': 24, 'idle': 1000, 'total': 1024}), ({'plug_in': 0, 'charge_end': 90, 'unplug': 106, 'wh': 12500, 'price_per_kwh': 35, 'idle_grace': 15, 'idle_rate': 50, 'idle_cap': 2000, 'session_fee': 100, 'min_fee': 300}, {'energy': 438, 'idle': 50, 'total': 588}), ({'plug_in': 55, 'charge_end': 192, 'unplug': 592, 'wh': 56671, 'price_per_kwh': 45, 'idle_grace': 10, 'idle_rate': 100, 'idle_cap': 2000, 'session_fee': 150, 'min_fee': 200}, {'energy': 2551, 'idle': 2000, 'total': 4701}), ({'plug_in': 119, 'charge_end': 141, 'unplug': 166, 'wh': 50337, 'price_per_kwh': 29, 'idle_grace': 15, 'idle_rate': 100, 'idle_cap': 1000, 'session_fee': 150, 'min_fee': 500}, {'energy': 1460, 'idle': 100, 'total': 1710}), ({'plug_in': 133, 'charge_end': 348, 'unplug': 364, 'wh': 39429, 'price_per_kwh': 35, 'idle_grace': 10, 'idle_rate': 40, 'idle_cap': 2000, 'session_fee': 100, 'min_fee': 500}, {'energy': 1381, 'idle': 40, 'total': 1521}), ({'plug_in': 367, 'charge_end': 452, 'unplug': 457, 'wh': 33621, 'price_per_kwh': 35, 'idle_grace': 15, 'idle_rate': 40, 'idle_cap': 3000, 'session_fee': 150, 'min_fee': 200}, {'energy': 1177, 'idle': 0, 'total': 1327}), ({'plug_in': 86, 'charge_end': 100, 'unplug': 500, 'wh': 33981, 'price_per_kwh': 29, 'idle_grace': 10, 'idle_rate': 40, 'idle_cap': 2000, 'session_fee': 100, 'min_fee': 300}, {'energy': 986, 'idle': 1560, 'total': 2646})], [({'plug_in': 484, 'charge_end': 691, 'unplug': 696, 'wh': 43716, 'price_per_kwh': 35, 'idle_grace': 15, 'idle_rate': 50, 'idle_cap': 2000, 'session_fee': 0, 'min_fee': 500}, {'energy': 1531, 'idle': 0, 'total': 1531}), ({'plug_in': 94, 'charge_end': 104, 'unplug': 504, 'wh': 29791, 'price_per_kwh': 35, 'idle_grace': 15, 'idle_rate': 40, 'idle_cap': 2000, 'session_fee': 0, 'min_fee': 300}, {'energy': 1043, 'idle': 1560, 'total': 2603}), ({'plug_in': 30, 'charge_end': 45, 'unplug': 400, 'wh': 800, 'price_per_kwh': 29, 'idle_grace': 10, 'idle_rate': 100, 'idle_cap': 1000, 'session_fee': 0, 'min_fee': 500}, {'energy': 24, 'idle': 1000, 'total': 1024}), ({'plug_in': 298, 'charge_end': 412, 'unplug': 422, 'wh': 44300, 'price_per_kwh': 29, 'idle_grace': 15, 'idle_rate': 40, 'idle_cap': 2000, 'session_fee': 150, 'min_fee': 500}, {'energy': 1285, 'idle': 0, 'total': 1435}), ({'plug_in': 490, 'charge_end': 604, 'unplug': 1004, 'wh': 19203, 'price_per_kwh': 35, 'idle_grace': 10, 'idle_rate': 40, 'idle_cap': 1000, 'session_fee': 100, 'min_fee': 500}, {'energy': 673, 'idle': 1000, 'total': 1773}), ({'plug_in': 35, 'charge_end': 237, 'unplug': 637, 'wh': 21103, 'price_per_kwh': 29, 'idle_grace': 15, 'idle_rate': 100, 'idle_cap': 2000, 'session_fee': 150, 'min_fee': 200}, {'energy': 612, 'idle': 2000, 'total': 2762}), ({'plug_in': 435, 'charge_end': 452, 'unplug': 477, 'wh': 8379, 'price_per_kwh': 52, 'idle_grace': 15, 'idle_rate': 50, 'idle_cap': 1000, 'session_fee': 100, 'min_fee': 200}, {'energy': 436, 'idle': 50, 'total': 586}), ({'plug_in': 0, 'charge_end': 90, 'unplug': 106, 'wh': 12500, 'price_per_kwh': 35, 'idle_grace': 15, 'idle_rate': 50, 'idle_cap': 2000, 'session_fee': 100, 'min_fee': 300}, {'energy': 438, 'idle': 50, 'total': 588})], [({'plug_in': 196, 'charge_end': 237, 'unplug': 437, 'wh': 1583, 'price_per_kwh': 35, 'idle_grace': 15, 'idle_rate': 40, 'idle_cap': 3000, 'session_fee': 100, 'min_fee': 500}, {'energy': 56, 'idle': 760, 'total': 916}), ({'plug_in': 189, 'charge_end': 408, 'unplug': 608, 'wh': 14406, 'price_per_kwh': 45, 'idle_grace': 15, 'idle_rate': 100, 'idle_cap': 1000, 'session_fee': 150, 'min_fee': 500}, {'energy': 649, 'idle': 1000, 'total': 1799}), ({'plug_in': 485, 'charge_end': 673, 'unplug': 678, 'wh': 49217, 'price_per_kwh': 35, 'idle_grace': 15, 'idle_rate': 100, 'idle_cap': 1000, 'session_fee': 100, 'min_fee': 500}, {'energy': 1723, 'idle': 0, 'total': 1823}), ({'plug_in': 30, 'charge_end': 45, 'unplug': 400, 'wh': 800, 'price_per_kwh': 29, 'idle_grace': 10, 'idle_rate': 100, 'idle_cap': 1000, 'session_fee': 0, 'min_fee': 500}, {'energy': 24, 'idle': 1000, 'total': 1024}), ({'plug_in': 579, 'charge_end': 616, 'unplug': 816, 'wh': 38092, 'price_per_kwh': 29, 'idle_grace': 10, 'idle_rate': 40, 'idle_cap': 3000, 'session_fee': 0, 'min_fee': 300}, {'energy': 1105, 'idle': 760, 'total': 1865}), ({'plug_in': 434, 'charge_end': 669, 'unplug': 1069, 'wh': 52657, 'price_per_kwh': 29, 'idle_grace': 15, 'idle_rate': 50, 'idle_cap': 1000, 'session_fee': 0, 'min_fee': 500}, {'energy': 1528, 'idle': 1000, 'total': 2528}), ({'plug_in': 0, 'charge_end': 90, 'unplug': 106, 'wh': 12500, 'price_per_kwh': 35, 'idle_grace': 15, 'idle_rate': 50, 'idle_cap': 2000, 'session_fee': 100, 'min_fee': 300}, {'energy': 438, 'idle': 50, 'total': 588}), ({'plug_in': 423, 'charge_end': 679, 'unplug': 879, 'wh': 7379, 'price_per_kwh': 29, 'idle_grace': 10, 'idle_rate': 100, 'idle_cap': 2000, 'session_fee': 0, 'min_fee': 200}, {'energy': 214, 'idle': 1900, 'total': 2114})], [({'plug_in': 443, 'charge_end': 636, 'unplug': 1036, 'wh': 37050, 'price_per_kwh': 45, 'idle_grace': 10, 'idle_rate': 100, 'idle_cap': 3000, 'session_fee': 150, 'min_fee': 300}, {'energy': 1668, 'idle': 3000, 'total': 4818}), ({'plug_in': 154, 'charge_end': 307, 'unplug': 367, 'wh': 13001, 'price_per_kwh': 52, 'idle_grace': 10, 'idle_rate': 100, 'idle_cap': 2000, 'session_fee': 0, 'min_fee': 200}, {'energy': 677, 'idle': 500, 'total': 1177}), ({'plug_in': 429, 'charge_end': 659, 'unplug': 1059, 'wh': 6145, 'price_per_kwh': 35, 'idle_grace': 10, 'idle_rate': 50, 'idle_cap': 1000, 'session_fee': 150, 'min_fee': 500}, {'energy': 216, 'idle': 1000, 'total': 1366}), ({'plug_in': 505, 'charge_end': 552, 'unplug': 612, 'wh': 43134, 'price_per_kwh': 29, 'idle_grace': 10, 'idle_rate': 40, 'idle_cap': 3000, 'session_fee': 0, 'min_fee': 200}, {'energy': 1251, 'idle': 200, 'total': 1451}), ({'plug_in': 598, 'charge_end': 637, 'unplug': 653, 'wh': 32840, 'price_per_kwh': 29, 'idle_grace': 10, 'idle_rate': 50, 'idle_cap': 2000, 'session_fee': 0, 'min_fee': 500}, {'energy': 953, 'idle': 50, 'total': 1003}), ({'plug_in': 194, 'charge_end': 420, 'unplug': 480, 'wh': 11163, 'price_per_kwh': 29, 'idle_grace': 10, 'idle_rate': 50, 'idle_cap': 1000, 'session_fee': 150, 'min_fee': 300}, {'energy': 324, 'idle': 250, 'total': 724}), ({'plug_in': 30, 'charge_end': 45, 'unplug': 400, 'wh': 800, 'price_per_kwh': 29, 'idle_grace': 10, 'idle_rate': 100, 'idle_cap': 1000, 'session_fee': 0, 'min_fee': 500}, {'energy': 24, 'idle': 1000, 'total': 1024}), ({'plug_in': 0, 'charge_end': 90, 'unplug': 106, 'wh': 12500, 'price_per_kwh': 35, 'idle_grace': 15, 'idle_rate': 50, 'idle_cap': 2000, 'session_fee': 100, 'min_fee': 300}, {'energy': 438, 'idle': 50, 'total': 588})]]
for i, (args, expected) in enumerate(fixtures[N-1]):
    check('fee oracle' + ' %d' % i, solve(args), 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

N = 1
observations = []
def solve(x):
    energy = -(-x['wh'] * x['price_per_kwh'] // 1000)
    idle = max(0, x['unplug'] - max(x['charge_end'], x['plug_in'] + 60) - x['idle_grace'])
    idle_fee = min(x['idle_cap'], -(-idle // 10) * x['idle_rate'])
    total = x['session_fee'] + energy + idle_fee
    return {'energy': energy, 'idle': idle_fee, 'total': max(x['min_fee'], total)}
def check(label, actual, expected):
    observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
fixtures = [[({'plug_in': 198, 'charge_end': 364, 'unplug': 379, 'wh': 11108, 'price_per_kwh': 45, 'idle_grace': 10, 'idle_rate': 50, 'idle_cap': 3000, 'session_fee': 0, 'min_fee': 300}, {'energy': 500, 'idle': 50, 'total': 550}), ({'plug_in': 83, 'charge_end': 221, 'unplug': 221, 'wh': 32363, 'price_per_kwh': 29, 'idle_grace': 10, 'idle_rate': 50, 'idle_cap': 3000, 'session_fee': 150, 'min_fee': 300}, {'energy': 939, 'idle': 0, 'total': 1089}), ({'plug_in': 0, 'charge_end': 90, 'unplug': 106, 'wh': 12500, 'price_per_kwh': 35, 'idle_grace': 15, 'idle_rate': 50, 'idle_cap': 2000, 'session_fee': 100, 'min_fee': 300}, {'energy': 438, 'idle': 50, 'total': 588}), ({'plug_in': 463, 'charge_end': 667, 'unplug': 1067, 'wh': 38632, 'price_per_kwh': 35, 'idle_grace': 15, 'idle_rate': 100, 'idle_cap': 2000, 'session_fee': 150, 'min_fee': 200}, {'energy': 1353, 'idle': 2000, 'total': 3503}), ({'plug_in': 300, 'charge_end': 327, 'unplug': 352, 'wh': 2749, 'price_per_kwh': 45, 'idle_grace': 15, 'idle_rate': 100, 'idle_cap': 3000, 'session_fee': 0, 'min_fee': 300}, {'energy': 124, 'idle': 100, 'total': 300}), ({'plug_in': 30, 'charge_end': 45, 'unplug': 400, 'wh': 800, 'price_per_kwh': 29, 'idle_grace': 10, 'idle_rate': 100, 'idle_cap': 1000, 'session_fee': 0, 'min_fee': 500}, {'energy': 24, 'idle': 1000, 'total': 1024}), ({'plug_in': 571, 'charge_end': 611, 'unplug': 671, 'wh': 58013, 'price_per_kwh': 35, 'idle_grace': 10, 'idle_rate': 100, 'idle_cap': 1000, 'session_fee': 150, 'min_fee': 500}, {'energy': 2031, 'idle': 500, 'total': 2681}), ({'plug_in': 585, 'charge_end': 651, 'unplug': 1051, 'wh': 11487, 'price_per_kwh': 45, 'idle_grace': 10, 'idle_rate': 100, 'idle_cap': 2000, 'session_fee': 100, 'min_fee': 300}, {'energy': 517, 'idle': 2000, 'total': 2617})], [({'plug_in': 276, 'charge_end': 449, 'unplug': 849, 'wh': 11301, 'price_per_kwh': 52, 'idle_grace': 10, 'idle_rate': 100, 'idle_cap': 1000, 'session_fee': 150, 'min_fee': 200}, {'energy': 588, 'idle': 1000, 'total': 1738}), ({'plug_in': 30, 'charge_end': 45, 'unplug': 400, 'wh': 800, 'price_per_kwh': 29, 'idle_grace': 10, 'idle_rate': 100, 'idle_cap': 1000, 'session_fee': 0, 'min_fee': 500}, {'energy': 24, 'idle': 1000, 'total': 1024}), ({'plug_in': 0, 'charge_end': 90, 'unplug': 106, 'wh': 12500, 'price_per_kwh': 35, 'idle_grace': 15, 'idle_rate': 50, 'idle_cap': 2000, 'session_fee': 100, 'min_fee': 300}, {'energy': 438, 'idle': 50, 'total': 588}), ({'plug_in': 55, 'charge_end': 192, 'unplug': 592, 'wh': 56671, 'price_per_kwh': 45, 'idle_grace': 10, 'idle_rate': 100, 'idle_cap': 2000, 'session_fee': 150, 'min_fee': 200}, {'energy': 2551, 'idle': 2000, 'total': 4701}), ({'plug_in': 119, 'charge_end': 141, 'unplug': 166, 'wh': 50337, 'price_per_kwh': 29, 'idle_grace': 15, 'idle_rate': 100, 'idle_cap': 1000, 'session_fee': 150, 'min_fee': 500}, {'energy': 1460, 'idle': 100, 'total': 1710}), ({'plug_in': 133, 'charge_end': 348, 'unplug': 364, 'wh': 39429, 'price_per_kwh': 35, 'idle_grace': 10, 'idle_rate': 40, 'idle_cap': 2000, 'session_fee': 100, 'min_fee': 500}, {'energy': 1381, 'idle': 40, 'total': 1521}), ({'plug_in': 367, 'charge_end': 452, 'unplug': 457, 'wh': 33621, 'price_per_kwh': 35, 'idle_grace': 15, 'idle_rate': 40, 'idle_cap': 3000, 'session_fee': 150, 'min_fee': 200}, {'energy': 1177, 'idle': 0, 'total': 1327}), ({'plug_in': 86, 'charge_end': 100, 'unplug': 500, 'wh': 33981, 'price_per_kwh': 29, 'idle_grace': 10, 'idle_rate': 40, 'idle_cap': 2000, 'session_fee': 100, 'min_fee': 300}, {'energy': 986, 'idle': 1560, 'total': 2646})], [({'plug_in': 484, 'charge_end': 691, 'unplug': 696, 'wh': 43716, 'price_per_kwh': 35, 'idle_grace': 15, 'idle_rate': 50, 'idle_cap': 2000, 'session_fee': 0, 'min_fee': 500}, {'energy': 1531, 'idle': 0, 'total': 1531}), ({'plug_in': 94, 'charge_end': 104, 'unplug': 504, 'wh': 29791, 'price_per_kwh': 35, 'idle_grace': 15, 'idle_rate': 40, 'idle_cap': 2000, 'session_fee': 0, 'min_fee': 300}, {'energy': 1043, 'idle': 1560, 'total': 2603}), ({'plug_in': 30, 'charge_end': 45, 'unplug': 400, 'wh': 800, 'price_per_kwh': 29, 'idle_grace': 10, 'idle_rate': 100, 'idle_cap': 1000, 'session_fee': 0, 'min_fee': 500}, {'energy': 24, 'idle': 1000, 'total': 1024}), ({'plug_in': 298, 'charge_end': 412, 'unplug': 422, 'wh': 44300, 'price_per_kwh': 29, 'idle_grace': 15, 'idle_rate': 40, 'idle_cap': 2000, 'session_fee': 150, 'min_fee': 500}, {'energy': 1285, 'idle': 0, 'total': 1435}), ({'plug_in': 490, 'charge_end': 604, 'unplug': 1004, 'wh': 19203, 'price_per_kwh': 35, 'idle_grace': 10, 'idle_rate': 40, 'idle_cap': 1000, 'session_fee': 100, 'min_fee': 500}, {'energy': 673, 'idle': 1000, 'total': 1773}), ({'plug_in': 35, 'charge_end': 237, 'unplug': 637, 'wh': 21103, 'price_per_kwh': 29, 'idle_grace': 15, 'idle_rate': 100, 'idle_cap': 2000, 'session_fee': 150, 'min_fee': 200}, {'energy': 612, 'idle': 2000, 'total': 2762}), ({'plug_in': 435, 'charge_end': 452, 'unplug': 477, 'wh': 8379, 'price_per_kwh': 52, 'idle_grace': 15, 'idle_rate': 50, 'idle_cap': 1000, 'session_fee': 100, 'min_fee': 200}, {'energy': 436, 'idle': 50, 'total': 586}), ({'plug_in': 0, 'charge_end': 90, 'unplug': 106, 'wh': 12500, 'price_per_kwh': 35, 'idle_grace': 15, 'idle_rate': 50, 'idle_cap': 2000, 'session_fee': 100, 'min_fee': 300}, {'energy': 438, 'idle': 50, 'total': 588})], [({'plug_in': 196, 'charge_end': 237, 'unplug': 437, 'wh': 1583, 'price_per_kwh': 35, 'idle_grace': 15, 'idle_rate': 40, 'idle_cap': 3000, 'session_fee': 100, 'min_fee': 500}, {'energy': 56, 'idle': 760, 'total': 916}), ({'plug_in': 189, 'charge_end': 408, 'unplug': 608, 'wh': 14406, 'price_per_kwh': 45, 'idle_grace': 15, 'idle_rate': 100, 'idle_cap': 1000, 'session_fee': 150, 'min_fee': 500}, {'energy': 649, 'idle': 1000, 'total': 1799}), ({'plug_in': 485, 'charge_end': 673, 'unplug': 678, 'wh': 49217, 'price_per_kwh': 35, 'idle_grace': 15, 'idle_rate': 100, 'idle_cap': 1000, 'session_fee': 100, 'min_fee': 500}, {'energy': 1723, 'idle': 0, 'total': 1823}), ({'plug_in': 30, 'charge_end': 45, 'unplug': 400, 'wh': 800, 'price_per_kwh': 29, 'idle_grace': 10, 'idle_rate': 100, 'idle_cap': 1000, 'session_fee': 0, 'min_fee': 500}, {'energy': 24, 'idle': 1000, 'total': 1024}), ({'plug_in': 579, 'charge_end': 616, 'unplug': 816, 'wh': 38092, 'price_per_kwh': 29, 'idle_grace': 10, 'idle_rate': 40, 'idle_cap': 3000, 'session_fee': 0, 'min_fee': 300}, {'energy': 1105, 'idle': 760, 'total': 1865}), ({'plug_in': 434, 'charge_end': 669, 'unplug': 1069, 'wh': 52657, 'price_per_kwh': 29, 'idle_grace': 15, 'idle_rate': 50, 'idle_cap': 1000, 'session_fee': 0, 'min_fee': 500}, {'energy': 1528, 'idle': 1000, 'total': 2528}), ({'plug_in': 0, 'charge_end': 90, 'unplug': 106, 'wh': 12500, 'price_per_kwh': 35, 'idle_grace': 15, 'idle_rate': 50, 'idle_cap': 2000, 'session_fee': 100, 'min_fee': 300}, {'energy': 438, 'idle': 50, 'total': 588}), ({'plug_in': 423, 'charge_end': 679, 'unplug': 879, 'wh': 7379, 'price_per_kwh': 29, 'idle_grace': 10, 'idle_rate': 100, 'idle_cap': 2000, 'session_fee': 0, 'min_fee': 200}, {'energy': 214, 'idle': 1900, 'total': 2114})], [({'plug_in': 443, 'charge_end': 636, 'unplug': 1036, 'wh': 37050, 'price_per_kwh': 45, 'idle_grace': 10, 'idle_rate': 100, 'idle_cap': 3000, 'session_fee': 150, 'min_fee': 300}, {'energy': 1668, 'idle': 3000, 'total': 4818}), ({'plug_in': 154, 'charge_end': 307, 'unplug': 367, 'wh': 13001, 'price_per_kwh': 52, 'idle_grace': 10, 'idle_rate': 100, 'idle_cap': 2000, 'session_fee': 0, 'min_fee': 200}, {'energy': 677, 'idle': 500, 'total': 1177}), ({'plug_in': 429, 'charge_end': 659, 'unplug': 1059, 'wh': 6145, 'price_per_kwh': 35, 'idle_grace': 10, 'idle_rate': 50, 'idle_cap': 1000, 'session_fee': 150, 'min_fee': 500}, {'energy': 216, 'idle': 1000, 'total': 1366}), ({'plug_in': 505, 'charge_end': 552, 'unplug': 612, 'wh': 43134, 'price_per_kwh': 29, 'idle_grace': 10, 'idle_rate': 40, 'idle_cap': 3000, 'session_fee': 0, 'min_fee': 200}, {'energy': 1251, 'idle': 200, 'total': 1451}), ({'plug_in': 598, 'charge_end': 637, 'unplug': 653, 'wh': 32840, 'price_per_kwh': 29, 'idle_grace': 10, 'idle_rate': 50, 'idle_cap': 2000, 'session_fee': 0, 'min_fee': 500}, {'energy': 953, 'idle': 50, 'total': 1003}), ({'plug_in': 194, 'charge_end': 420, 'unplug': 480, 'wh': 11163, 'price_per_kwh': 29, 'idle_grace': 10, 'idle_rate': 50, 'idle_cap': 1000, 'session_fee': 150, 'min_fee': 300}, {'energy': 324, 'idle': 250, 'total': 724}), ({'plug_in': 30, 'charge_end': 45, 'unplug': 400, 'wh': 800, 'price_per_kwh': 29, 'idle_grace': 10, 'idle_rate': 100, 'idle_cap': 1000, 'session_fee': 0, 'min_fee': 500}, {'energy': 24, 'idle': 1000, 'total': 1024}), ({'plug_in': 0, 'charge_end': 90, 'unplug': 106, 'wh': 12500, 'price_per_kwh': 35, 'idle_grace': 15, 'idle_rate': 50, 'idle_cap': 2000, 'session_fee': 100, 'min_fee': 300}, {'energy': 438, 'idle': 50, 'total': 588})]]
for i, (args, expected) in enumerate(fixtures[N-1]):
    check('fee oracle' + ' %d' % i, solve(args), 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:
EV charging session and idle fee returns a wrong result when idle time is measured from plug-in.

Input {plug_in, charge_end, unplug (minutes), wh, price_per_kwh, idle_grace, idle_rate (per started 10 min), idle_cap, session_fee, min_fee}. Energy = ceil(wh*price/1000). Idle minutes = max(0, unplug - charge_end - grace); idle fee = min(idle_cap, started 10-minute blocks * idle_rate). Total = max(min_fee, session_fee + energy + idle). Return {energy, idle, total}.

split: open-access


broken_source:
"""Failure Map reference implementation. Python standard library only."""
import json

N = 1
observations = []
def solve(x):
    energy = -(-x['wh'] * x['price_per_kwh'] // 1000)
    idle = max(0, x['unplug'] - x['charge_end'])
    idle_fee = min(x['idle_cap'], -(-idle // 10) * x['idle_rate'])
    total = x['session_fee'] + energy + idle_fee
    return {'energy': energy, 'idle': idle_fee, 'total': max(x['min_fee'], total)}
def check(label, actual, expected):
    observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
fixtures = [[({'plug_in': 400, 'charge_end': 430, 'unplug': 440, 'wh': 26712, 'price_per_kwh': 35, 'idle_grace': 10, 'idle_rate': 40, 'idle_cap': 1000, 'session_fee': 150, 'min_fee': 200}, {'energy': 935, 'idle': 0, 'total': 1085}), ({'plug_in': 443, 'charge_end': 621, 'unplug': 1021, 'wh': 32003, 'price_per_kwh': 35, 'idle_grace': 10, 'idle_rate': 100, 'idle_cap': 3000, 'session_fee': 100, 'min_fee': 500}, {'energy': 1121, 'idle': 3000, 'total': 4221}), ({'plug_in': 178, 'charge_end': 431, 'unplug': 831, 'wh': 23652, 'price_per_kwh': 35, 'idle_grace': 15, 'idle_rate': 50, 'idle_cap': 1000, 'session_fee': 100, 'min_fee': 200}, {'energy': 828, 'idle': 1000, 'total': 1928}), ({'plug_in': 0, 'charge_end': 90, 'unplug': 106, 'wh': 12500, 'price_per_kwh': 35, 'idle_grace': 15, 'idle_rate': 50, 'idle_cap': 2000, 'session_fee': 100, 'min_fee': 300}, {'energy': 438, 'idle': 50, 'total': 588}), ({'plug_in': 24, 'charge_end': 82, 'unplug': 82, 'wh': 29231, 'price_per_kwh': 45, 'idle_grace': 15, 'idle_rate': 100, 'idle_cap': 3000, 'session_fee': 100, 'min_fee': 300}, {'energy': 1316, 'idle': 0, 'total': 1416}), ({'plug_in': 30, 'charge_end': 45, 'unplug': 400, 'wh': 800, 'price_per_kwh': 29, 'idle_grace': 10, 'idle_rate': 100, 'idle_cap': 1000, 'session_fee': 0, 'min_fee': 500}, {'energy': 24, 'idle': 1000, 'total': 1024}), ({'plug_in': 321, 'charge_end': 549, 'unplug': 949, 'wh': 16186, 'price_per_kwh': 29, 'idle_grace': 10, 'idle_rate': 50, 'idle_cap': 3000, 'session_fee': 0, 'min_fee': 300}, {'energy': 470, 'idle': 1950, 'total': 2420}), ({'plug_in': 445, 'charge_end': 719, 'unplug': 744, 'wh': 13785, 'price_per_kwh': 29, 'idle_grace': 10, 'idle_rate': 100, 'idle_cap': 2000, 'session_fee': 0, 'min_fee': 500}, {'energy': 400, 'idle': 200, 'total': 600})], [({'plug_in': 143, 'charge_end': 442, 'unplug': 842, 'wh': 33904, 'price_per_kwh': 29, 'idle_grace': 15, 'idle_rate': 40, 'idle_cap': 1000, 'session_fee': 150, 'min_fee': 200}, {'energy': 984, 'idle': 1000, 'total': 2134}), ({'plug_in': 468, 'charge_end': 648, 'unplug': 648, 'wh': 29211, 'price_per_kwh': 45, 'idle_grace': 15, 'idle_rate': 100, 'idle_cap': 2000, 'session_fee': 150, 'min_fee': 500}, {'energy': 1315, 'idle': 0, 'total': 1465}), ({'plug_in': 244, 'charge_end': 536, 'unplug': 736, 'wh': 50241, 'price_per_kwh': 29, 'idle_grace': 15, 'idle_rate': 100, 'idle_cap': 1000, 'session_fee': 150, 'min_fee': 200}, {'energy': 1457, 'idle': 1000, 'total': 2607}), ({'plug_in': 217, 'charge_end': 487, 'unplug': 687, 'wh': 40392, 'price_per_kwh': 52, 'idle_grace': 10, 'idle_rate': 50, 'idle_cap': 3000, 'session_fee': 0, 'min_fee': 300}, {'energy': 2101, 'idle': 950, 'total': 3051}), ({'plug_in': 30, 'charge_end': 45, 'unplug': 400, 'wh': 800, 'price_per_kwh': 29, 'idle_grace': 10, 'idle_rate': 100, 'idle_cap': 1000, 'session_fee': 0, 'min_fee': 500}, {'energy': 24, 'idle': 1000, 'total': 1024}), ({'plug_in': 0, 'charge_end': 90, 'unplug': 106, 'wh': 12500, 'price_per_kwh': 35, 'idle_grace': 15, 'idle_rate': 50, 'idle_cap': 2000, 'session_fee': 100, 'min_fee': 300}, {'energy': 438, 'idle': 50, 'total': 588}), ({'plug_in': 417, 'charge_end': 599, 'unplug': 604, 'wh': 38888, 'price_per_kwh': 29, 'idle_grace': 10, 'idle_rate': 40, 'idle_cap': 3000, 'session_fee': 150, 'min_fee': 300}, {'energy': 1128, 'idle': 0, 'total': 1278}), ({'plug_in': 468, 'charge_end': 517, 'unplug': 542, 'wh': 35685, 'price_per_kwh': 45, 'idle_grace': 15, 'idle_rate': 100, 'idle_cap': 1000, 'session_fee': 0, 'min_fee': 500}, {'energy': 1606, 'idle': 100, 'total': 1706})], [({'plug_in': 533, 'charge_end': 668, 'unplug': 684, 'wh': 20000, 'price_per_kwh': 52, 'idle_grace': 15, 'idle_rate': 50, 'idle_cap': 2000, 'session_fee': 150, 'min_fee': 500}, {'energy': 1040, 'idle': 50, 'total': 1240}), ({'plug_in': 0, 'charge_end': 90, 'unplug': 106, 'wh': 12500, 'price_per_kwh': 35, 'idle_grace': 15, 'idle_rate': 50, 'idle_cap': 2000, 'session_fee': 100, 'min_fee': 300}, {'energy': 438, 'idle': 50, 'total': 588}), ({'plug_in': 548, 'charge_end': 679, 'unplug': 679, 'wh': 46438, 'price_per_kwh': 52, 'idle_grace': 10, 'idle_rate': 40, 'idle_cap': 2000, 'session_fee': 150, 'min_fee': 200}, {'energy': 2415, 'idle': 0, 'total': 2565}), ({'plug_in': 295, 'charge_end': 514, 'unplug': 539, 'wh': 58607, 'price_per_kwh': 35, 'idle_grace': 10, 'idle_rate': 50, 'idle_cap': 3000, 'session_fee': 100, 'min_fee': 200}, {'energy': 2052, 'idle': 100, 'total': 2252}), ({'plug_in': 270, 'charge_end': 560, 'unplug': 620, 'wh': 13483, 'price_per_kwh': 29, 'idle_grace': 10, 'idle_rate': 50, 'idle_cap': 3000, 'session_fee': 0, 'min_fee': 300}, {'energy': 392, 'idle': 250, 'total': 642}), ({'plug_in': 503, 'charge_end': 781, 'unplug': 1181, 'wh': 23844, 'price_per_kwh': 35, 'idle_grace': 15, 'idle_rate': 100, 'idle_cap': 1000, 'session_fee': 100, 'min_fee': 200}, {'energy': 835, 'idle': 1000, 'total': 1935}), ({'plug_in': 30, 'charge_end': 45, 'unplug': 400, 'wh': 800, 'price_per_kwh': 29, 'idle_grace': 10, 'idle_rate': 100, 'idle_cap': 1000, 'session_fee': 0, 'min_fee': 500}, {'energy': 24, 'idle': 1000, 'total': 1024}), ({'plug_in': 422, 'charge_end': 604, 'unplug': 1004, 'wh': 58741, 'price_per_kwh': 45, 'idle_grace': 10, 'idle_rate': 40, 'idle_cap': 1000, 'session_fee': 0, 'min_fee': 300}, {'energy': 2644, 'idle': 1000, 'total': 3644})], [({'plug_in': 141, 'charge_end': 394, 'unplug': 410, 'wh': 26923, 'price_per_kwh': 52, 'idle_grace': 10, 'idle_rate': 100, 'idle_cap': 2000, 'session_fee': 0, 'min_fee': 500}, {'energy': 1400, 'idle': 100, 'total': 1500}), ({'plug_in': 87, 'charge_end': 194, 'unplug': 194, 'wh': 20119, 'price_per_kwh': 45, 'idle_grace': 10, 'idle_rate': 100, 'idle_cap': 1000, 'session_fee': 100, 'min_fee': 500}, {'energy': 906, 'idle': 0, 'total': 1006}), ({'plug_in': 30, 'charge_end': 45, 'unplug': 400, 'wh': 800, 'price_per_kwh': 29, 'idle_grace': 10, 'idle_rate': 100, 'idle_cap': 1000, 'session_fee': 0, 'min_fee': 500}, {'energy': 24, 'idle': 1000, 'total': 1024}), ({'plug_in': 121, 'charge_end': 230, 'unplug': 230, 'wh': 2855, 'price_per_kwh': 45, 'idle_grace': 10, 'idle_rate': 40, 'idle_cap': 3000, 'session_fee': 100, 'min_fee': 500}, {'energy': 129, 'idle': 0, 'total': 500}), ({'plug_in': 116, 'charge_end': 342, 'unplug': 357, 'wh': 45320, 'price_per_kwh': 45, 'idle_grace': 10, 'idle_rate': 100, 'idle_cap': 1000, 'session_fee': 100, 'min_fee': 200}, {'energy': 2040, 'idle': 100, 'total': 2240}), ({'plug_in': 0, 'charge_end': 90, 'unplug': 106, 'wh': 12500, 'price_per_kwh': 35, 'idle_grace': 15, 'idle_rate': 50, 'idle_cap': 2000, 'session_fee': 100, 'min_fee': 300}, {'energy': 438, 'idle': 50, 'total': 588}), ({'plug_in': 304, 'charge_end': 530, 'unplug': 555, 'wh': 5327, 'price_per_kwh': 45, 'idle_grace': 15, 'idle_rate': 40, 'idle_cap': 2000, 'session_fee': 150, 'min_fee': 500}, {'energy': 240, 'idle': 40, 'total': 500}), ({'plug_in': 338, 'charge_end': 429, 'unplug': 429, 'wh': 18347, 'price_per_kwh': 29, 'idle_grace': 15, 'idle_rate': 40, 'idle_cap': 1000, 'session_fee': 150, 'min_fee': 200}, {'energy': 533, 'idle': 0, 'total': 683})], [({'plug_in': 391, 'charge_end': 417, 'unplug': 432, 'wh': 50407, 'price_per_kwh': 35, 'idle_grace': 10, 'idle_rate': 50, 'idle_cap': 2000, 'session_fee': 0, 'min_fee': 500}, {'energy': 1765, 'idle': 50, 'total': 1815}), ({'plug_in': 30, 'charge_end': 45, 'unplug': 400, 'wh': 800, 'price_per_kwh': 29, 'idle_grace': 10, 'idle_rate': 100, 'idle_cap': 1000, 'session_fee': 0, 'min_fee': 500}, {'energy': 24, 'idle': 1000, 'total': 1024}), ({'plug_in': 547, 'charge_end': 782, 'unplug': 782, 'wh': 23444, 'price_per_kwh': 45, 'idle_grace': 15, 'idle_rate': 50, 'idle_cap': 2000, 'session_fee': 150, 'min_fee': 300}, {'energy': 1055, 'idle': 0, 'total': 1205}), ({'plug_in': 424, 'charge_end': 695, 'unplug': 1095, 'wh': 19996, 'price_per_kwh': 29, 'idle_grace': 15, 'idle_rate': 100, 'idle_cap': 2000, 'session_fee': 150, 'min_fee': 200}, {'energy': 580, 'idle': 2000, 'total': 2730}), ({'plug_in': 596, 'charge_end': 767, 'unplug': 967, 'wh': 39532, 'price_per_kwh': 45, 'idle_grace': 15, 'idle_rate': 100, 'idle_cap': 1000, 'session_fee': 150, 'min_fee': 200}, {'energy': 1779, 'idle': 1000, 'total': 2929}), ({'plug_in': 445, 'charge_end': 682, 'unplug': 698, 'wh': 57925, 'price_per_kwh': 29, 'idle_grace': 10, 'idle_rate': 40, 'idle_cap': 1000, 'session_fee': 150, 'min_fee': 200}, {'energy': 1680, 'idle': 40, 'total': 1870}), ({'plug_in': 555, 'charge_end': 609, 'unplug': 634, 'wh': 10270, 'price_per_kwh': 35, 'idle_grace': 10, 'idle_rate': 100, 'idle_cap': 2000, 'session_fee': 100, 'min_fee': 300}, {'energy': 360, 'idle': 200, 'total': 660}), ({'plug_in': 0, 'charge_end': 90, 'unplug': 106, 'wh': 12500, 'price_per_kwh': 35, 'idle_grace': 15, 'idle_rate': 50, 'idle_cap': 2000, 'session_fee': 100, 'min_fee': 300}, {'energy': 438, 'idle': 50, 'total': 588})]]
for i, (args, expected) in enumerate(fixtures[N-1]):
    check('fee oracle' + ' %d' % i, solve(args), 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

N = 1
observations = []
def solve(x):
    energy = -(-x['wh'] * x['price_per_kwh'] // 1000)
    idle = (x['unplug'] - x['charge_end'] if x['unplug'] - x['charge_end'] > x['idle_grace'] else 0)
    idle_fee = min(x['idle_cap'], -(-idle // 10) * x['idle_rate'])
    total = x['session_fee'] + energy + idle_fee
    return {'energy': energy, 'idle': idle_fee, 'total': max(x['min_fee'], total)}
def check(label, actual, expected):
    observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
fixtures = [[({'plug_in': 400, 'charge_end': 430, 'unplug': 440, 'wh': 26712, 'price_per_kwh': 35, 'idle_grace': 10, 'idle_rate': 40, 'idle_cap': 1000, 'session_fee': 150, 'min_fee': 200}, {'energy': 935, 'idle': 0, 'total': 1085}), ({'plug_in': 443, 'charge_end': 621, 'unplug': 1021, 'wh': 32003, 'price_per_kwh': 35, 'idle_grace': 10, 'idle_rate': 100, 'idle_cap': 3000, 'session_fee': 100, 'min_fee': 500}, {'energy': 1121, 'idle': 3000, 'total': 4221}), ({'plug_in': 178, 'charge_end': 431, 'unplug': 831, 'wh': 23652, 'price_per_kwh': 35, 'idle_grace': 15, 'idle_rate': 50, 'idle_cap': 1000, 'session_fee': 100, 'min_fee': 200}, {'energy': 828, 'idle': 1000, 'total': 1928}), ({'plug_in': 0, 'charge_end': 90, 'unplug': 106, 'wh': 12500, 'price_per_kwh': 35, 'idle_grace': 15, 'idle_rate': 50, 'idle_cap': 2000, 'session_fee': 100, 'min_fee': 300}, {'energy': 438, 'idle': 50, 'total': 588}), ({'plug_in': 24, 'charge_end': 82, 'unplug': 82, 'wh': 29231, 'price_per_kwh': 45, 'idle_grace': 15, 'idle_rate': 100, 'idle_cap': 3000, 'session_fee': 100, 'min_fee': 300}, {'energy': 1316, 'idle': 0, 'total': 1416}), ({'plug_in': 30, 'charge_end': 45, 'unplug': 400, 'wh': 800, 'price_per_kwh': 29, 'idle_grace': 10, 'idle_rate': 100, 'idle_cap': 1000, 'session_fee': 0, 'min_fee': 500}, {'energy': 24, 'idle': 1000, 'total': 1024}), ({'plug_in': 321, 'charge_end': 549, 'unplug': 949, 'wh': 16186, 'price_per_kwh': 29, 'idle_grace': 10, 'idle_rate': 50, 'idle_cap': 3000, 'session_fee': 0, 'min_fee': 300}, {'energy': 470, 'idle': 1950, 'total': 2420}), ({'plug_in': 445, 'charge_end': 719, 'unplug': 744, 'wh': 13785, 'price_per_kwh': 29, 'idle_grace': 10, 'idle_rate': 100, 'idle_cap': 2000, 'session_fee': 0, 'min_fee': 500}, {'energy': 400, 'idle': 200, 'total': 600})], [({'plug_in': 143, 'charge_end': 442, 'unplug': 842, 'wh': 33904, 'price_per_kwh': 29, 'idle_grace': 15, 'idle_rate': 40, 'idle_cap': 1000, 'session_fee': 150, 'min_fee': 200}, {'energy': 984, 'idle': 1000, 'total': 2134}), ({'plug_in': 468, 'charge_end': 648, 'unplug': 648, 'wh': 29211, 'price_per_kwh': 45, 'idle_grace': 15, 'idle_rate': 100, 'idle_cap': 2000, 'session_fee': 150, 'min_fee': 500}, {'energy': 1315, 'idle': 0, 'total': 1465}), ({'plug_in': 244, 'charge_end': 536, 'unplug': 736, 'wh': 50241, 'price_per_kwh': 29, 'idle_grace': 15, 'idle_rate': 100, 'idle_cap': 1000, 'session_fee': 150, 'min_fee': 200}, {'energy': 1457, 'idle': 1000, 'total': 2607}), ({'plug_in': 217, 'charge_end': 487, 'unplug': 687, 'wh': 40392, 'price_per_kwh': 52, 'idle_grace': 10, 'idle_rate': 50, 'idle_cap': 3000, 'session_fee': 0, 'min_fee': 300}, {'energy': 2101, 'idle': 950, 'total': 3051}), ({'plug_in': 30, 'charge_end': 45, 'unplug': 400, 'wh': 800, 'price_per_kwh': 29, 'idle_grace': 10, 'idle_rate': 100, 'idle_cap': 1000, 'session_fee': 0, 'min_fee': 500}, {'energy': 24, 'idle': 1000, 'total': 1024}), ({'plug_in': 0, 'charge_end': 90, 'unplug': 106, 'wh': 12500, 'price_per_kwh': 35, 'idle_grace': 15, 'idle_rate': 50, 'idle_cap': 2000, 'session_fee': 100, 'min_fee': 300}, {'energy': 438, 'idle': 50, 'total': 588}), ({'plug_in': 417, 'charge_end': 599, 'unplug': 604, 'wh': 38888, 'price_per_kwh': 29, 'idle_grace': 10, 'idle_rate': 40, 'idle_cap': 3000, 'session_fee': 150, 'min_fee': 300}, {'energy': 1128, 'idle': 0, 'total': 1278}), ({'plug_in': 468, 'charge_end': 517, 'unplug': 542, 'wh': 35685, 'price_per_kwh': 45, 'idle_grace': 15, 'idle_rate': 100, 'idle_cap': 1000, 'session_fee': 0, 'min_fee': 500}, {'energy': 1606, 'idle': 100, 'total': 1706})], [({'plug_in': 533, 'charge_end': 668, 'unplug': 684, 'wh': 20000, 'price_per_kwh': 52, 'idle_grace': 15, 'idle_rate': 50, 'idle_cap': 2000, 'session_fee': 150, 'min_fee': 500}, {'energy': 1040, 'idle': 50, 'total': 1240}), ({'plug_in': 0, 'charge_end': 90, 'unplug': 106, 'wh': 12500, 'price_per_kwh': 35, 'idle_grace': 15, 'idle_rate': 50, 'idle_cap': 2000, 'session_fee': 100, 'min_fee': 300}, {'energy': 438, 'idle': 50, 'total': 588}), ({'plug_in': 548, 'charge_end': 679, 'unplug': 679, 'wh': 46438, 'price_per_kwh': 52, 'idle_grace': 10, 'idle_rate': 40, 'idle_cap': 2000, 'session_fee': 150, 'min_fee': 200}, {'energy': 2415, 'idle': 0, 'total': 2565}), ({'plug_in': 295, 'charge_end': 514, 'unplug': 539, 'wh': 58607, 'price_per_kwh': 35, 'idle_grace': 10, 'idle_rate': 50, 'idle_cap': 3000, 'session_fee': 100, 'min_fee': 200}, {'energy': 2052, 'idle': 100, 'total': 2252}), ({'plug_in': 270, 'charge_end': 560, 'unplug': 620, 'wh': 13483, 'price_per_kwh': 29, 'idle_grace': 10, 'idle_rate': 50, 'idle_cap': 3000, 'session_fee': 0, 'min_fee': 300}, {'energy': 392, 'idle': 250, 'total': 642}), ({'plug_in': 503, 'charge_end': 781, 'unplug': 1181, 'wh': 23844, 'price_per_kwh': 35, 'idle_grace': 15, 'idle_rate': 100, 'idle_cap': 1000, 'session_fee': 100, 'min_fee': 200}, {'energy': 835, 'idle': 1000, 'total': 1935}), ({'plug_in': 30, 'charge_end': 45, 'unplug': 400, 'wh': 800, 'price_per_kwh': 29, 'idle_grace': 10, 'idle_rate': 100, 'idle_cap': 1000, 'session_fee': 0, 'min_fee': 500}, {'energy': 24, 'idle': 1000, 'total': 1024}), ({'plug_in': 422, 'charge_end': 604, 'unplug': 1004, 'wh': 58741, 'price_per_kwh': 45, 'idle_grace': 10, 'idle_rate': 40, 'idle_cap': 1000, 'session_fee': 0, 'min_fee': 300}, {'energy': 2644, 'idle': 1000, 'total': 3644})], [({'plug_in': 141, 'charge_end': 394, 'unplug': 410, 'wh': 26923, 'price_per_kwh': 52, 'idle_grace': 10, 'idle_rate': 100, 'idle_cap': 2000, 'session_fee': 0, 'min_fee': 500}, {'energy': 1400, 'idle': 100, 'total': 1500}), ({'plug_in': 87, 'charge_end': 194, 'unplug': 194, 'wh': 20119, 'price_per_kwh': 45, 'idle_grace': 10, 'idle_rate': 100, 'idle_cap': 1000, 'session_fee': 100, 'min_fee': 500}, {'energy': 906, 'idle': 0, 'total': 1006}), ({'plug_in': 30, 'charge_end': 45, 'unplug': 400, 'wh': 800, 'price_per_kwh': 29, 'idle_grace': 10, 'idle_rate': 100, 'idle_cap': 1000, 'session_fee': 0, 'min_fee': 500}, {'energy': 24, 'idle': 1000, 'total': 1024}), ({'plug_in': 121, 'charge_end': 230, 'unplug': 230, 'wh': 2855, 'price_per_kwh': 45, 'idle_grace': 10, 'idle_rate': 40, 'idle_cap': 3000, 'session_fee': 100, 'min_fee': 500}, {'energy': 129, 'idle': 0, 'total': 500}), ({'plug_in': 116, 'charge_end': 342, 'unplug': 357, 'wh': 45320, 'price_per_kwh': 45, 'idle_grace': 10, 'idle_rate': 100, 'idle_cap': 1000, 'session_fee': 100, 'min_fee': 200}, {'energy': 2040, 'idle': 100, 'total': 2240}), ({'plug_in': 0, 'charge_end': 90, 'unplug': 106, 'wh': 12500, 'price_per_kwh': 35, 'idle_grace': 15, 'idle_rate': 50, 'idle_cap': 2000, 'session_fee': 100, 'min_fee': 300}, {'energy': 438, 'idle': 50, 'total': 588}), ({'plug_in': 304, 'charge_end': 530, 'unplug': 555, 'wh': 5327, 'price_per_kwh': 45, 'idle_grace': 15, 'idle_rate': 40, 'idle_cap': 2000, 'session_fee': 150, 'min_fee': 500}, {'energy': 240, 'idle': 40, 'total': 500}), ({'plug_in': 338, 'charge_end': 429, 'unplug': 429, 'wh': 18347, 'price_per_kwh': 29, 'idle_grace': 15, 'idle_rate': 40, 'idle_cap': 1000, 'session_fee': 150, 'min_fee': 200}, {'energy': 533, 'idle': 0, 'total': 683})], [({'plug_in': 391, 'charge_end': 417, 'unplug': 432, 'wh': 50407, 'price_per_kwh': 35, 'idle_grace': 10, 'idle_rate': 50, 'idle_cap': 2000, 'session_fee': 0, 'min_fee': 500}, {'energy': 1765, 'idle': 50, 'total': 1815}), ({'plug_in': 30, 'charge_end': 45, 'unplug': 400, 'wh': 800, 'price_per_kwh': 29, 'idle_grace': 10, 'idle_rate': 100, 'idle_cap': 1000, 'session_fee': 0, 'min_fee': 500}, {'energy': 24, 'idle': 1000, 'total': 1024}), ({'plug_in': 547, 'charge_end': 782, 'unplug': 782, 'wh': 23444, 'price_per_kwh': 45, 'idle_grace': 15, 'idle_rate': 50, 'idle_cap': 2000, 'session_fee': 150, 'min_fee': 300}, {'energy': 1055, 'idle': 0, 'total': 1205}), ({'plug_in': 424, 'charge_end': 695, 'unplug': 1095, 'wh': 19996, 'price_per_kwh': 29, 'idle_grace': 15, 'idle_rate': 100, 'idle_cap': 2000, 'session_fee': 150, 'min_fee': 200}, {'energy': 580, 'idle': 2000, 'total': 2730}), ({'plug_in': 596, 'charge_end': 767, 'unplug': 967, 'wh': 39532, 'price_per_kwh': 45, 'idle_grace': 15, 'idle_rate': 100, 'idle_cap': 1000, 'session_fee': 150, 'min_fee': 200}, {'energy': 1779, 'idle': 1000, 'total': 2929}), ({'plug_in': 445, 'charge_end': 682, 'unplug': 698, 'wh': 57925, 'price_per_kwh': 29, 'idle_grace': 10, 'idle_rate': 40, 'idle_cap': 1000, 'session_fee': 150, 'min_fee': 200}, {'energy': 1680, 'idle': 40, 'total': 1870}), ({'plug_in': 555, 'charge_end': 609, 'unplug': 634, 'wh': 10270, 'price_per_kwh': 35, 'idle_grace': 10, 'idle_rate': 100, 'idle_cap': 2000, 'session_fee': 100, 'min_fee': 300}, {'energy': 360, 'idle': 200, 'total': 660}), ({'plug_in': 0, 'charge_end': 90, 'unplug': 106, 'wh': 12500, 'price_per_kwh': 35, 'idle_grace': 15, 'idle_rate': 50, 'idle_cap': 2000, 'session_fee': 100, 'min_fee': 300}, {'energy': 438, 'idle': 50, 'total': 588})]]
for i, (args, expected) in enumerate(fixtures[N-1]):
    check('fee oracle' + ' %d' % i, solve(args), 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:
EV charging session and idle fee returns a wrong result when the grace period is only a threshold.

Input {plug_in, charge_end, unplug (minutes), wh, price_per_kwh, idle_grace, idle_rate (per started 10 min), idle_cap, session_fee, min_fee}. Energy = ceil(wh*price/1000). Idle minutes = max(0, unplug - charge_end - grace); idle fee = min(idle_cap, started 10-minute blocks * idle_rate). Total = max(min_fee, session_fee + energy + idle). Return {energy, idle, total}.

split: open-access