FAILURE MAP
← Case archive

FA-68416 / Toll and parking fee computation / Open access

Early bird flat rate: the early bird is compared with the uncapped hourly fee · case 01

Early bird flat rate returns a wrong result when the early bird is compared with the uncapped hourly fee.

Verified by executionVariant 1 · 8 checks per implementationDownload source bundle ↓JSON ↗

ROOT CAUSE

Qualifying customers are charged the flat rate even when the regular fee is lower.

VERIFIED REPAIR

Restore the regular fee comparison rule so that the step reads `return min(regular, x['early'])`.

Unsuccessful approach: Comparing against the uncapped hourly fee ignores a daily maximum below the early bird price.

Case contract

Input {entry: [day, minute], exit: [day, minute], hourly, daily_max, early}; day % 7 == 0 is Monday. Regular fee = min(started hours*hourly, started 24h periods (at least 1)*daily_max). Early bird applies on weekdays when entry is 06:00-09:00 inclusive and exit is the same day 15:00-19:00 inclusive; the customer pays min(regular, early). Negative durations are 'invalid'.

Why this case matters

Fee engines bill customers in integer cents; a wrong boundary, rounding stage or cap scope silently over- or under-charges.

1 / The failure

Exit 1
"""Failure Map reference implementation. Python standard library only."""
import json

N = 1
observations = []
def solve(x):
    ed, em = x['entry']
    xd, xm = x['exit']
    dur = (xd - ed) * 1440 + xm - em
    if dur < 0:
        return 'invalid'
    hours = -(-dur // 60)
    days = max(1, -(-dur // 1440))
    regular = min(hours * x['hourly'], days * x['daily_max'])
    eligible = ed % 7 < 5 and 360 <= em <= 540 and xd == ed and 900 <= xm <= 1140
    if eligible:
        return x['early']
    return regular
def check(label, actual, expected):
    observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
fixtures = [[({'entry': [8, 360], 'exit': [8, 1140], 'hourly': 200, 'daily_max': 1200, 'early': 1000}, 1000), ({'entry': [8, 360], 'exit': [8, 900], 'hourly': 200, 'daily_max': 1200, 'early': 1400}, 1200), ({'entry': [1, 541], 'exit': [2, 899], 'hourly': 400, 'daily_max': 2500, 'early': 1000}, 5000), ({'entry': [0, 480], 'exit': [0, 1300], 'hourly': 300, 'daily_max': 2500, 'early': 1000}, 2500), ({'entry': [1, 360], 'exit': [1, 1140], 'hourly': 400, 'daily_max': 1800, 'early': 1000}, 1000), ({'entry': [3, 420], 'exit': [4, 960], 'hourly': 300, 'daily_max': 1800, 'early': 900}, 3600), ({'entry': [3, 540], 'exit': [3, 1140], 'hourly': 400, 'daily_max': 1200, 'early': 1400}, 1200), ({'entry': [0, 540], 'exit': [0, 1140], 'hourly': 200, 'daily_max': 1200, 'early': 1400}, 1200)], [({'entry': [11, 810], 'exit': [11, 913], 'hourly': 300, 'daily_max': 1800, 'early': 1400}, 600), ({'entry': [5, 400], 'exit': [5, 1000], 'hourly': 300, 'daily_max': 2500, 'early': 1000}, 2500), ({'entry': [1, 0], 'exit': [2, 0], 'hourly': 300, 'daily_max': 1800, 'early': 1000}, 1800), ({'entry': [11, 540], 'exit': [11, 1140], 'hourly': 400, 'daily_max': 1200, 'early': 1400}, 1200), ({'entry': [2, 930], 'exit': [2, 1141], 'hourly': 300, 'daily_max': 2500, 'early': 1000}, 1200), ({'entry': [8, 360], 'exit': [8, 900], 'hourly': 400, 'daily_max': 1200, 'early': 1400}, 1200), ({'entry': [2, 540], 'exit': [2, 1000], 'hourly': 300, 'daily_max': 2500, 'early': 1000}, 1000), ({'entry': [4, 360], 'exit': [4, 900], 'hourly': 300, 'daily_max': 1200, 'early': 1400}, 1200)], [({'entry': [2, 540], 'exit': [2, 1000], 'hourly': 300, 'daily_max': 2500, 'early': 1000}, 1000), ({'entry': [7, 360], 'exit': [7, 900], 'hourly': 300, 'daily_max': 1200, 'early': 1400}, 1200), ({'entry': [0, 540], 'exit': [0, 921], 'hourly': 300, 'daily_max': 1800, 'early': 1000}, 1000), ({'entry': [1, 0], 'exit': [2, 0], 'hourly': 300, 'daily_max': 1800, 'early': 1000}, 1800), ({'entry': [8, 360], 'exit': [8, 1140], 'hourly': 200, 'daily_max': 1200, 'early': 1400}, 1200), ({'entry': [3, 420], 'exit': [4, 960], 'hourly': 300, 'daily_max': 1800, 'early': 900}, 3600), ({'entry': [0, 360], 'exit': [0, 900], 'hourly': 300, 'daily_max': 1200, 'early': 1400}, 1200), ({'entry': [2, 540], 'exit': [2, 899], 'hourly': 300, 'daily_max': 2500, 'early': 900}, 1800)], [({'entry': [9, 540], 'exit': [9, 900], 'hourly': 200, 'daily_max': 1200, 'early': 1400}, 1200), ({'entry': [2, 540], 'exit': [2, 1000], 'hourly': 300, 'daily_max': 2500, 'early': 1000}, 1000), ({'entry': [2, 540], 'exit': [2, 900], 'hourly': 200, 'daily_max': 1800, 'early': 1400}, 1200), ({'entry': [5, 400], 'exit': [5, 1000], 'hourly': 300, 'daily_max': 2500, 'early': 1000}, 2500), ({'entry': [0, 360], 'exit': [0, 1140], 'hourly': 200, 'daily_max': 1200, 'early': 1400}, 1200), ({'entry': [1, 0], 'exit': [2, 0], 'hourly': 300, 'daily_max': 1800, 'early': 1000}, 1800), ({'entry': [8, 541], 'exit': [8, 1232], 'hourly': 400, 'daily_max': 2500, 'early': 1400}, 2500), ({'entry': [3, 400], 'exit': [3, 1000], 'hourly': 300, 'daily_max': 800, 'early': 1000}, 800)], [({'entry': [3, 400], 'exit': [3, 1000], 'hourly': 300, 'daily_max': 800, 'early': 1000}, 800), ({'entry': [3, 420], 'exit': [4, 960], 'hourly': 300, 'daily_max': 1800, 'early': 900}, 3600), ({'entry': [1, 540], 'exit': [1, 899], 'hourly': 300, 'daily_max': 1200, 'early': 1400}, 1200), ({'entry': [3, 540], 'exit': [5, 1218], 'hourly': 400, 'daily_max': 1800, 'early': 900}, 5400), ({'entry': [2, 470], 'exit': [2, 1140], 'hourly': 200, 'daily_max': 1200, 'early': 1400}, 1200), ({'entry': [8, 541], 'exit': [8, 996], 'hourly': 200, 'daily_max': 1200, 'early': 1000}, 1200), ({'entry': [3, 450], 'exit': [3, 930], 'hourly': 400, 'daily_max': 1200, 'early': 1400}, 1200), ({'entry': [1, 360], 'exit': [1, 1140], 'hourly': 400, 'daily_max': 1800, 'early': 1000}, 1000)]]
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)
Boundary fixtureActualExpectedOutcome
fee oracle 010001000Passed
fee oracle 114001200Failed
fee oracle 250005000Passed
fee oracle 325002500Passed
fee oracle 410001000Passed
fee oracle 536003600Passed
fee oracle 614001200Failed
fee oracle 714001200Failed

SHA-256 / d5ab0a1d6a3f92ad9a539d1f81dbf0200f6c8807f72c17fe0c2de6bc1d82b8ef

2 / The unsuccessful fix

Exit 1
"""Failure Map reference implementation. Python standard library only."""
import json

N = 1
observations = []
def solve(x):
    ed, em = x['entry']
    xd, xm = x['exit']
    dur = (xd - ed) * 1440 + xm - em
    if dur < 0:
        return 'invalid'
    hours = -(-dur // 60)
    days = max(1, -(-dur // 1440))
    regular = min(hours * x['hourly'], days * x['daily_max'])
    eligible = ed % 7 < 5 and 360 <= em <= 540 and xd == ed and 900 <= xm <= 1140
    if eligible:
        return min(hours * x['hourly'], x['early'])
    return regular
def check(label, actual, expected):
    observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
fixtures = [[({'entry': [8, 360], 'exit': [8, 1140], 'hourly': 200, 'daily_max': 1200, 'early': 1000}, 1000), ({'entry': [8, 360], 'exit': [8, 900], 'hourly': 200, 'daily_max': 1200, 'early': 1400}, 1200), ({'entry': [1, 541], 'exit': [2, 899], 'hourly': 400, 'daily_max': 2500, 'early': 1000}, 5000), ({'entry': [0, 480], 'exit': [0, 1300], 'hourly': 300, 'daily_max': 2500, 'early': 1000}, 2500), ({'entry': [1, 360], 'exit': [1, 1140], 'hourly': 400, 'daily_max': 1800, 'early': 1000}, 1000), ({'entry': [3, 420], 'exit': [4, 960], 'hourly': 300, 'daily_max': 1800, 'early': 900}, 3600), ({'entry': [3, 540], 'exit': [3, 1140], 'hourly': 400, 'daily_max': 1200, 'early': 1400}, 1200), ({'entry': [0, 540], 'exit': [0, 1140], 'hourly': 200, 'daily_max': 1200, 'early': 1400}, 1200)], [({'entry': [11, 810], 'exit': [11, 913], 'hourly': 300, 'daily_max': 1800, 'early': 1400}, 600), ({'entry': [5, 400], 'exit': [5, 1000], 'hourly': 300, 'daily_max': 2500, 'early': 1000}, 2500), ({'entry': [1, 0], 'exit': [2, 0], 'hourly': 300, 'daily_max': 1800, 'early': 1000}, 1800), ({'entry': [11, 540], 'exit': [11, 1140], 'hourly': 400, 'daily_max': 1200, 'early': 1400}, 1200), ({'entry': [2, 930], 'exit': [2, 1141], 'hourly': 300, 'daily_max': 2500, 'early': 1000}, 1200), ({'entry': [8, 360], 'exit': [8, 900], 'hourly': 400, 'daily_max': 1200, 'early': 1400}, 1200), ({'entry': [2, 540], 'exit': [2, 1000], 'hourly': 300, 'daily_max': 2500, 'early': 1000}, 1000), ({'entry': [4, 360], 'exit': [4, 900], 'hourly': 300, 'daily_max': 1200, 'early': 1400}, 1200)], [({'entry': [2, 540], 'exit': [2, 1000], 'hourly': 300, 'daily_max': 2500, 'early': 1000}, 1000), ({'entry': [7, 360], 'exit': [7, 900], 'hourly': 300, 'daily_max': 1200, 'early': 1400}, 1200), ({'entry': [0, 540], 'exit': [0, 921], 'hourly': 300, 'daily_max': 1800, 'early': 1000}, 1000), ({'entry': [1, 0], 'exit': [2, 0], 'hourly': 300, 'daily_max': 1800, 'early': 1000}, 1800), ({'entry': [8, 360], 'exit': [8, 1140], 'hourly': 200, 'daily_max': 1200, 'early': 1400}, 1200), ({'entry': [3, 420], 'exit': [4, 960], 'hourly': 300, 'daily_max': 1800, 'early': 900}, 3600), ({'entry': [0, 360], 'exit': [0, 900], 'hourly': 300, 'daily_max': 1200, 'early': 1400}, 1200), ({'entry': [2, 540], 'exit': [2, 899], 'hourly': 300, 'daily_max': 2500, 'early': 900}, 1800)], [({'entry': [9, 540], 'exit': [9, 900], 'hourly': 200, 'daily_max': 1200, 'early': 1400}, 1200), ({'entry': [2, 540], 'exit': [2, 1000], 'hourly': 300, 'daily_max': 2500, 'early': 1000}, 1000), ({'entry': [2, 540], 'exit': [2, 900], 'hourly': 200, 'daily_max': 1800, 'early': 1400}, 1200), ({'entry': [5, 400], 'exit': [5, 1000], 'hourly': 300, 'daily_max': 2500, 'early': 1000}, 2500), ({'entry': [0, 360], 'exit': [0, 1140], 'hourly': 200, 'daily_max': 1200, 'early': 1400}, 1200), ({'entry': [1, 0], 'exit': [2, 0], 'hourly': 300, 'daily_max': 1800, 'early': 1000}, 1800), ({'entry': [8, 541], 'exit': [8, 1232], 'hourly': 400, 'daily_max': 2500, 'early': 1400}, 2500), ({'entry': [3, 400], 'exit': [3, 1000], 'hourly': 300, 'daily_max': 800, 'early': 1000}, 800)], [({'entry': [3, 400], 'exit': [3, 1000], 'hourly': 300, 'daily_max': 800, 'early': 1000}, 800), ({'entry': [3, 420], 'exit': [4, 960], 'hourly': 300, 'daily_max': 1800, 'early': 900}, 3600), ({'entry': [1, 540], 'exit': [1, 899], 'hourly': 300, 'daily_max': 1200, 'early': 1400}, 1200), ({'entry': [3, 540], 'exit': [5, 1218], 'hourly': 400, 'daily_max': 1800, 'early': 900}, 5400), ({'entry': [2, 470], 'exit': [2, 1140], 'hourly': 200, 'daily_max': 1200, 'early': 1400}, 1200), ({'entry': [8, 541], 'exit': [8, 996], 'hourly': 200, 'daily_max': 1200, 'early': 1000}, 1200), ({'entry': [3, 450], 'exit': [3, 930], 'hourly': 400, 'daily_max': 1200, 'early': 1400}, 1200), ({'entry': [1, 360], 'exit': [1, 1140], 'hourly': 400, 'daily_max': 1800, 'early': 1000}, 1000)]]
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)
Boundary fixtureActualExpectedOutcome
fee oracle 010001000Passed
fee oracle 114001200Failed
fee oracle 250005000Passed
fee oracle 325002500Passed
fee oracle 410001000Passed
fee oracle 536003600Passed
fee oracle 614001200Failed
fee oracle 714001200Failed

SHA-256 / 4c178ec59dccb7137151c741038ecf8f1d5e5e6d5f0d529926765abbb21c3f28

3 / The verified repair

Exit 0
"""Failure Map reference implementation. Python standard library only."""
import json

N = 1
observations = []
def solve(x):
    ed, em = x['entry']
    xd, xm = x['exit']
    dur = (xd - ed) * 1440 + xm - em
    if dur < 0:
        return 'invalid'
    hours = -(-dur // 60)
    days = max(1, -(-dur // 1440))
    regular = min(hours * x['hourly'], days * x['daily_max'])
    eligible = ed % 7 < 5 and 360 <= em <= 540 and xd == ed and 900 <= xm <= 1140
    if eligible:
        return min(regular, x['early'])
    return regular
def check(label, actual, expected):
    observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
fixtures = [[({'entry': [8, 360], 'exit': [8, 1140], 'hourly': 200, 'daily_max': 1200, 'early': 1000}, 1000), ({'entry': [8, 360], 'exit': [8, 900], 'hourly': 200, 'daily_max': 1200, 'early': 1400}, 1200), ({'entry': [1, 541], 'exit': [2, 899], 'hourly': 400, 'daily_max': 2500, 'early': 1000}, 5000), ({'entry': [0, 480], 'exit': [0, 1300], 'hourly': 300, 'daily_max': 2500, 'early': 1000}, 2500), ({'entry': [1, 360], 'exit': [1, 1140], 'hourly': 400, 'daily_max': 1800, 'early': 1000}, 1000), ({'entry': [3, 420], 'exit': [4, 960], 'hourly': 300, 'daily_max': 1800, 'early': 900}, 3600), ({'entry': [3, 540], 'exit': [3, 1140], 'hourly': 400, 'daily_max': 1200, 'early': 1400}, 1200), ({'entry': [0, 540], 'exit': [0, 1140], 'hourly': 200, 'daily_max': 1200, 'early': 1400}, 1200)], [({'entry': [11, 810], 'exit': [11, 913], 'hourly': 300, 'daily_max': 1800, 'early': 1400}, 600), ({'entry': [5, 400], 'exit': [5, 1000], 'hourly': 300, 'daily_max': 2500, 'early': 1000}, 2500), ({'entry': [1, 0], 'exit': [2, 0], 'hourly': 300, 'daily_max': 1800, 'early': 1000}, 1800), ({'entry': [11, 540], 'exit': [11, 1140], 'hourly': 400, 'daily_max': 1200, 'early': 1400}, 1200), ({'entry': [2, 930], 'exit': [2, 1141], 'hourly': 300, 'daily_max': 2500, 'early': 1000}, 1200), ({'entry': [8, 360], 'exit': [8, 900], 'hourly': 400, 'daily_max': 1200, 'early': 1400}, 1200), ({'entry': [2, 540], 'exit': [2, 1000], 'hourly': 300, 'daily_max': 2500, 'early': 1000}, 1000), ({'entry': [4, 360], 'exit': [4, 900], 'hourly': 300, 'daily_max': 1200, 'early': 1400}, 1200)], [({'entry': [2, 540], 'exit': [2, 1000], 'hourly': 300, 'daily_max': 2500, 'early': 1000}, 1000), ({'entry': [7, 360], 'exit': [7, 900], 'hourly': 300, 'daily_max': 1200, 'early': 1400}, 1200), ({'entry': [0, 540], 'exit': [0, 921], 'hourly': 300, 'daily_max': 1800, 'early': 1000}, 1000), ({'entry': [1, 0], 'exit': [2, 0], 'hourly': 300, 'daily_max': 1800, 'early': 1000}, 1800), ({'entry': [8, 360], 'exit': [8, 1140], 'hourly': 200, 'daily_max': 1200, 'early': 1400}, 1200), ({'entry': [3, 420], 'exit': [4, 960], 'hourly': 300, 'daily_max': 1800, 'early': 900}, 3600), ({'entry': [0, 360], 'exit': [0, 900], 'hourly': 300, 'daily_max': 1200, 'early': 1400}, 1200), ({'entry': [2, 540], 'exit': [2, 899], 'hourly': 300, 'daily_max': 2500, 'early': 900}, 1800)], [({'entry': [9, 540], 'exit': [9, 900], 'hourly': 200, 'daily_max': 1200, 'early': 1400}, 1200), ({'entry': [2, 540], 'exit': [2, 1000], 'hourly': 300, 'daily_max': 2500, 'early': 1000}, 1000), ({'entry': [2, 540], 'exit': [2, 900], 'hourly': 200, 'daily_max': 1800, 'early': 1400}, 1200), ({'entry': [5, 400], 'exit': [5, 1000], 'hourly': 300, 'daily_max': 2500, 'early': 1000}, 2500), ({'entry': [0, 360], 'exit': [0, 1140], 'hourly': 200, 'daily_max': 1200, 'early': 1400}, 1200), ({'entry': [1, 0], 'exit': [2, 0], 'hourly': 300, 'daily_max': 1800, 'early': 1000}, 1800), ({'entry': [8, 541], 'exit': [8, 1232], 'hourly': 400, 'daily_max': 2500, 'early': 1400}, 2500), ({'entry': [3, 400], 'exit': [3, 1000], 'hourly': 300, 'daily_max': 800, 'early': 1000}, 800)], [({'entry': [3, 400], 'exit': [3, 1000], 'hourly': 300, 'daily_max': 800, 'early': 1000}, 800), ({'entry': [3, 420], 'exit': [4, 960], 'hourly': 300, 'daily_max': 1800, 'early': 900}, 3600), ({'entry': [1, 540], 'exit': [1, 899], 'hourly': 300, 'daily_max': 1200, 'early': 1400}, 1200), ({'entry': [3, 540], 'exit': [5, 1218], 'hourly': 400, 'daily_max': 1800, 'early': 900}, 5400), ({'entry': [2, 470], 'exit': [2, 1140], 'hourly': 200, 'daily_max': 1200, 'early': 1400}, 1200), ({'entry': [8, 541], 'exit': [8, 996], 'hourly': 200, 'daily_max': 1200, 'early': 1000}, 1200), ({'entry': [3, 450], 'exit': [3, 930], 'hourly': 400, 'daily_max': 1200, 'early': 1400}, 1200), ({'entry': [1, 360], 'exit': [1, 1140], 'hourly': 400, 'daily_max': 1800, 'early': 1000}, 1000)]]
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)
Boundary fixtureActualExpectedOutcome
fee oracle 010001000Passed
fee oracle 112001200Passed
fee oracle 250005000Passed
fee oracle 325002500Passed
fee oracle 410001000Passed
fee oracle 536003600Passed
fee oracle 612001200Passed
fee oracle 712001200Passed

SHA-256 / 2048c85ae412f3b42f65ddbf102230ed7581200532b54eac7f30fad61c6309a7

Verification & scope

A deterministic, bounded toy model with a stipulated contract; it makes no claim of conformance to any agency manual or standard. This reproducer isolates one failure mechanism. Results cover the supplied fixtures. Variants within a family share a test contract and should remain grouped when constructing evaluation splits. Related mechanisms with a shared evaluation_group must also remain together; these controlled models are not independent production incidents.

Observations recorded using Python 3.12.14 at 2026-09-29T14:48:02.021508+00:00.

Case digest / c5e1a8ceffd636a611f46f1f03069bc19b20084fe5958cca78df9559b665e11b