FA-68401 / Toll and parking fee computation / Open access
Early bird flat rate: an entry at exactly 09:00 misses the early bird · case 01
Early bird flat rate returns a wrong result when an entry at exactly 09:00 misses the early bird.
ROOT CAUSE
The upper entry bound is exclusive although the posted window includes 09:00.
VERIFIED REPAIR
Restore the entry window rule so that the step reads `360 <= em <= 540`.
Unsuccessful approach: Excluding 06:00 moves the defect to the opening edge of the window.
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 min(regular, x['early'])
return regular
def check(label, actual, expected):
observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
fixtures = [[({'entry': [3, 420], 'exit': [4, 960], 'hourly': 300, 'daily_max': 1800, 'early': 900}, 3600), ({'entry': [7, 351], 'exit': [7, 1140], 'hourly': 200, 'daily_max': 1800, 'early': 900}, 1800), ({'entry': [1, 540], 'exit': [1, 900], 'hourly': 200, 'daily_max': 2500, 'early': 1000}, 1000), ({'entry': [4, 360], 'exit': [4, 900], 'hourly': 300, 'daily_max': 1800, 'early': 1400}, 1400), ({'entry': [3, 540], 'exit': [3, 900], 'hourly': 400, 'daily_max': 1200, 'early': 1000}, 1000), ({'entry': [1, 540], 'exit': [1, 970], 'hourly': 400, 'daily_max': 2500, 'early': 1400}, 1400), ({'entry': [1, 0], 'exit': [2, 0], 'hourly': 300, 'daily_max': 1800, 'early': 1000}, 1800), ({'entry': [1, 360], 'exit': [1, 1140], 'hourly': 400, 'daily_max': 1800, 'early': 1000}, 1000)], [({'entry': [0, 480], 'exit': [0, 1300], 'hourly': 300, 'daily_max': 2500, 'early': 1000}, 2500), ({'entry': [8, 540], 'exit': [8, 1037], 'hourly': 400, 'daily_max': 1200, 'early': 900}, 900), ({'entry': [0, 540], 'exit': [0, 927], 'hourly': 400, 'daily_max': 1800, 'early': 1400}, 1400), ({'entry': [1, 360], 'exit': [1, 1140], 'hourly': 400, 'daily_max': 1800, 'early': 1000}, 1000), ({'entry': [10, 1185], 'exit': [10, 1380], 'hourly': 400, 'daily_max': 2500, 'early': 1000}, 1600), ({'entry': [5, 400], 'exit': [5, 1000], 'hourly': 300, 'daily_max': 2500, 'early': 1000}, 2500), ({'entry': [3, 540], 'exit': [3, 900], 'hourly': 200, 'daily_max': 1800, 'early': 900}, 900), ({'entry': [7, 360], 'exit': [7, 995], 'hourly': 300, 'daily_max': 1800, 'early': 1000}, 1000)], [({'entry': [10, 540], 'exit': [10, 900], 'hourly': 300, 'daily_max': 1800, 'early': 1000}, 1000), ({'entry': [7, 540], 'exit': [7, 992], 'hourly': 200, 'daily_max': 2500, 'early': 1400}, 1400), ({'entry': [3, 400], 'exit': [3, 1000], 'hourly': 300, 'daily_max': 800, 'early': 1000}, 800), ({'entry': [8, 360], 'exit': [8, 900], 'hourly': 400, 'daily_max': 2500, 'early': 1000}, 1000), ({'entry': [11, 540], 'exit': [11, 1140], 'hourly': 400, 'daily_max': 2500, 'early': 1400}, 1400), ({'entry': [3, 540], 'exit': [5, 900], 'hourly': 300, 'daily_max': 2500, 'early': 1400}, 7500), ({'entry': [1, 0], 'exit': [2, 0], 'hourly': 300, 'daily_max': 1800, 'early': 1000}, 1800), ({'entry': [1, 360], 'exit': [1, 1140], 'hourly': 400, 'daily_max': 1800, 'early': 1000}, 1000)], [({'entry': [1, 360], 'exit': [1, 935], 'hourly': 200, 'daily_max': 1800, 'early': 900}, 900), ({'entry': [3, 540], 'exit': [3, 1140], 'hourly': 300, 'daily_max': 1800, 'early': 1400}, 1400), ({'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': [8, 540], 'exit': [8, 1140], 'hourly': 300, 'daily_max': 2500, 'early': 1400}, 1400), ({'entry': [0, 480], 'exit': [0, 1300], 'hourly': 300, 'daily_max': 2500, 'early': 1000}, 2500), ({'entry': [2, 360], 'exit': [2, 900], 'hourly': 300, 'daily_max': 2500, 'early': 900}, 900), ({'entry': [11, 540], 'exit': [11, 1140], 'hourly': 300, 'daily_max': 2500, 'early': 900}, 900)], [({'entry': [7, 540], 'exit': [9, 971], 'hourly': 300, 'daily_max': 1200, 'early': 1400}, 3600), ({'entry': [3, 540], 'exit': [3, 1140], 'hourly': 200, 'daily_max': 1800, 'early': 1000}, 1000), ({'entry': [1, 360], 'exit': [1, 1140], 'hourly': 400, 'daily_max': 1800, 'early': 1000}, 1000), ({'entry': [10, 360], 'exit': [10, 933], 'hourly': 300, 'daily_max': 1800, 'early': 1000}, 1000), ({'entry': [3, 420], 'exit': [4, 960], 'hourly': 300, 'daily_max': 1800, 'early': 900}, 3600), ({'entry': [7, 540], 'exit': [7, 1140], 'hourly': 400, 'daily_max': 2500, 'early': 1000}, 1000), ({'entry': [3, 400], 'exit': [3, 1000], 'hourly': 300, 'daily_max': 800, 'early': 1000}, 800), ({'entry': [8, 540], 'exit': [8, 900], 'hourly': 200, 'daily_max': 1800, 'early': 900}, 900)]]
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 fixture | Actual | Expected | Outcome |
|---|---|---|---|
| fee oracle 0 | 3600 | 3600 | Passed |
| fee oracle 1 | 1800 | 1800 | Passed |
| fee oracle 2 | 1200 | 1000 | Failed |
| fee oracle 3 | 1400 | 1400 | Passed |
| fee oracle 4 | 1200 | 1000 | Failed |
| fee oracle 5 | 2500 | 1400 | Failed |
| fee oracle 6 | 1800 | 1800 | Passed |
| fee oracle 7 | 1000 | 1000 | Passed |
SHA-256 / 6e34cf5c91b1d0fb135abf6395a68bfda497a222118c5ded472a09c5c0e0c25b
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(regular, x['early'])
return regular
def check(label, actual, expected):
observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
fixtures = [[({'entry': [3, 420], 'exit': [4, 960], 'hourly': 300, 'daily_max': 1800, 'early': 900}, 3600), ({'entry': [7, 351], 'exit': [7, 1140], 'hourly': 200, 'daily_max': 1800, 'early': 900}, 1800), ({'entry': [1, 540], 'exit': [1, 900], 'hourly': 200, 'daily_max': 2500, 'early': 1000}, 1000), ({'entry': [4, 360], 'exit': [4, 900], 'hourly': 300, 'daily_max': 1800, 'early': 1400}, 1400), ({'entry': [3, 540], 'exit': [3, 900], 'hourly': 400, 'daily_max': 1200, 'early': 1000}, 1000), ({'entry': [1, 540], 'exit': [1, 970], 'hourly': 400, 'daily_max': 2500, 'early': 1400}, 1400), ({'entry': [1, 0], 'exit': [2, 0], 'hourly': 300, 'daily_max': 1800, 'early': 1000}, 1800), ({'entry': [1, 360], 'exit': [1, 1140], 'hourly': 400, 'daily_max': 1800, 'early': 1000}, 1000)], [({'entry': [0, 480], 'exit': [0, 1300], 'hourly': 300, 'daily_max': 2500, 'early': 1000}, 2500), ({'entry': [8, 540], 'exit': [8, 1037], 'hourly': 400, 'daily_max': 1200, 'early': 900}, 900), ({'entry': [0, 540], 'exit': [0, 927], 'hourly': 400, 'daily_max': 1800, 'early': 1400}, 1400), ({'entry': [1, 360], 'exit': [1, 1140], 'hourly': 400, 'daily_max': 1800, 'early': 1000}, 1000), ({'entry': [10, 1185], 'exit': [10, 1380], 'hourly': 400, 'daily_max': 2500, 'early': 1000}, 1600), ({'entry': [5, 400], 'exit': [5, 1000], 'hourly': 300, 'daily_max': 2500, 'early': 1000}, 2500), ({'entry': [3, 540], 'exit': [3, 900], 'hourly': 200, 'daily_max': 1800, 'early': 900}, 900), ({'entry': [7, 360], 'exit': [7, 995], 'hourly': 300, 'daily_max': 1800, 'early': 1000}, 1000)], [({'entry': [10, 540], 'exit': [10, 900], 'hourly': 300, 'daily_max': 1800, 'early': 1000}, 1000), ({'entry': [7, 540], 'exit': [7, 992], 'hourly': 200, 'daily_max': 2500, 'early': 1400}, 1400), ({'entry': [3, 400], 'exit': [3, 1000], 'hourly': 300, 'daily_max': 800, 'early': 1000}, 800), ({'entry': [8, 360], 'exit': [8, 900], 'hourly': 400, 'daily_max': 2500, 'early': 1000}, 1000), ({'entry': [11, 540], 'exit': [11, 1140], 'hourly': 400, 'daily_max': 2500, 'early': 1400}, 1400), ({'entry': [3, 540], 'exit': [5, 900], 'hourly': 300, 'daily_max': 2500, 'early': 1400}, 7500), ({'entry': [1, 0], 'exit': [2, 0], 'hourly': 300, 'daily_max': 1800, 'early': 1000}, 1800), ({'entry': [1, 360], 'exit': [1, 1140], 'hourly': 400, 'daily_max': 1800, 'early': 1000}, 1000)], [({'entry': [1, 360], 'exit': [1, 935], 'hourly': 200, 'daily_max': 1800, 'early': 900}, 900), ({'entry': [3, 540], 'exit': [3, 1140], 'hourly': 300, 'daily_max': 1800, 'early': 1400}, 1400), ({'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': [8, 540], 'exit': [8, 1140], 'hourly': 300, 'daily_max': 2500, 'early': 1400}, 1400), ({'entry': [0, 480], 'exit': [0, 1300], 'hourly': 300, 'daily_max': 2500, 'early': 1000}, 2500), ({'entry': [2, 360], 'exit': [2, 900], 'hourly': 300, 'daily_max': 2500, 'early': 900}, 900), ({'entry': [11, 540], 'exit': [11, 1140], 'hourly': 300, 'daily_max': 2500, 'early': 900}, 900)], [({'entry': [7, 540], 'exit': [9, 971], 'hourly': 300, 'daily_max': 1200, 'early': 1400}, 3600), ({'entry': [3, 540], 'exit': [3, 1140], 'hourly': 200, 'daily_max': 1800, 'early': 1000}, 1000), ({'entry': [1, 360], 'exit': [1, 1140], 'hourly': 400, 'daily_max': 1800, 'early': 1000}, 1000), ({'entry': [10, 360], 'exit': [10, 933], 'hourly': 300, 'daily_max': 1800, 'early': 1000}, 1000), ({'entry': [3, 420], 'exit': [4, 960], 'hourly': 300, 'daily_max': 1800, 'early': 900}, 3600), ({'entry': [7, 540], 'exit': [7, 1140], 'hourly': 400, 'daily_max': 2500, 'early': 1000}, 1000), ({'entry': [3, 400], 'exit': [3, 1000], 'hourly': 300, 'daily_max': 800, 'early': 1000}, 800), ({'entry': [8, 540], 'exit': [8, 900], 'hourly': 200, 'daily_max': 1800, 'early': 900}, 900)]]
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 fixture | Actual | Expected | Outcome |
|---|---|---|---|
| fee oracle 0 | 3600 | 3600 | Passed |
| fee oracle 1 | 1800 | 1800 | Passed |
| fee oracle 2 | 1000 | 1000 | Passed |
| fee oracle 3 | 1800 | 1400 | Failed |
| fee oracle 4 | 1000 | 1000 | Passed |
| fee oracle 5 | 1400 | 1400 | Passed |
| fee oracle 6 | 1800 | 1800 | Passed |
| fee oracle 7 | 1800 | 1000 | Failed |
SHA-256 / d0bd56ce09d12a7423985b81f7bad65cd24e871fe481e0d4171879c26471b9a1
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': [3, 420], 'exit': [4, 960], 'hourly': 300, 'daily_max': 1800, 'early': 900}, 3600), ({'entry': [7, 351], 'exit': [7, 1140], 'hourly': 200, 'daily_max': 1800, 'early': 900}, 1800), ({'entry': [1, 540], 'exit': [1, 900], 'hourly': 200, 'daily_max': 2500, 'early': 1000}, 1000), ({'entry': [4, 360], 'exit': [4, 900], 'hourly': 300, 'daily_max': 1800, 'early': 1400}, 1400), ({'entry': [3, 540], 'exit': [3, 900], 'hourly': 400, 'daily_max': 1200, 'early': 1000}, 1000), ({'entry': [1, 540], 'exit': [1, 970], 'hourly': 400, 'daily_max': 2500, 'early': 1400}, 1400), ({'entry': [1, 0], 'exit': [2, 0], 'hourly': 300, 'daily_max': 1800, 'early': 1000}, 1800), ({'entry': [1, 360], 'exit': [1, 1140], 'hourly': 400, 'daily_max': 1800, 'early': 1000}, 1000)], [({'entry': [0, 480], 'exit': [0, 1300], 'hourly': 300, 'daily_max': 2500, 'early': 1000}, 2500), ({'entry': [8, 540], 'exit': [8, 1037], 'hourly': 400, 'daily_max': 1200, 'early': 900}, 900), ({'entry': [0, 540], 'exit': [0, 927], 'hourly': 400, 'daily_max': 1800, 'early': 1400}, 1400), ({'entry': [1, 360], 'exit': [1, 1140], 'hourly': 400, 'daily_max': 1800, 'early': 1000}, 1000), ({'entry': [10, 1185], 'exit': [10, 1380], 'hourly': 400, 'daily_max': 2500, 'early': 1000}, 1600), ({'entry': [5, 400], 'exit': [5, 1000], 'hourly': 300, 'daily_max': 2500, 'early': 1000}, 2500), ({'entry': [3, 540], 'exit': [3, 900], 'hourly': 200, 'daily_max': 1800, 'early': 900}, 900), ({'entry': [7, 360], 'exit': [7, 995], 'hourly': 300, 'daily_max': 1800, 'early': 1000}, 1000)], [({'entry': [10, 540], 'exit': [10, 900], 'hourly': 300, 'daily_max': 1800, 'early': 1000}, 1000), ({'entry': [7, 540], 'exit': [7, 992], 'hourly': 200, 'daily_max': 2500, 'early': 1400}, 1400), ({'entry': [3, 400], 'exit': [3, 1000], 'hourly': 300, 'daily_max': 800, 'early': 1000}, 800), ({'entry': [8, 360], 'exit': [8, 900], 'hourly': 400, 'daily_max': 2500, 'early': 1000}, 1000), ({'entry': [11, 540], 'exit': [11, 1140], 'hourly': 400, 'daily_max': 2500, 'early': 1400}, 1400), ({'entry': [3, 540], 'exit': [5, 900], 'hourly': 300, 'daily_max': 2500, 'early': 1400}, 7500), ({'entry': [1, 0], 'exit': [2, 0], 'hourly': 300, 'daily_max': 1800, 'early': 1000}, 1800), ({'entry': [1, 360], 'exit': [1, 1140], 'hourly': 400, 'daily_max': 1800, 'early': 1000}, 1000)], [({'entry': [1, 360], 'exit': [1, 935], 'hourly': 200, 'daily_max': 1800, 'early': 900}, 900), ({'entry': [3, 540], 'exit': [3, 1140], 'hourly': 300, 'daily_max': 1800, 'early': 1400}, 1400), ({'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': [8, 540], 'exit': [8, 1140], 'hourly': 300, 'daily_max': 2500, 'early': 1400}, 1400), ({'entry': [0, 480], 'exit': [0, 1300], 'hourly': 300, 'daily_max': 2500, 'early': 1000}, 2500), ({'entry': [2, 360], 'exit': [2, 900], 'hourly': 300, 'daily_max': 2500, 'early': 900}, 900), ({'entry': [11, 540], 'exit': [11, 1140], 'hourly': 300, 'daily_max': 2500, 'early': 900}, 900)], [({'entry': [7, 540], 'exit': [9, 971], 'hourly': 300, 'daily_max': 1200, 'early': 1400}, 3600), ({'entry': [3, 540], 'exit': [3, 1140], 'hourly': 200, 'daily_max': 1800, 'early': 1000}, 1000), ({'entry': [1, 360], 'exit': [1, 1140], 'hourly': 400, 'daily_max': 1800, 'early': 1000}, 1000), ({'entry': [10, 360], 'exit': [10, 933], 'hourly': 300, 'daily_max': 1800, 'early': 1000}, 1000), ({'entry': [3, 420], 'exit': [4, 960], 'hourly': 300, 'daily_max': 1800, 'early': 900}, 3600), ({'entry': [7, 540], 'exit': [7, 1140], 'hourly': 400, 'daily_max': 2500, 'early': 1000}, 1000), ({'entry': [3, 400], 'exit': [3, 1000], 'hourly': 300, 'daily_max': 800, 'early': 1000}, 800), ({'entry': [8, 540], 'exit': [8, 900], 'hourly': 200, 'daily_max': 1800, 'early': 900}, 900)]]
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 fixture | Actual | Expected | Outcome |
|---|---|---|---|
| fee oracle 0 | 3600 | 3600 | Passed |
| fee oracle 1 | 1800 | 1800 | Passed |
| fee oracle 2 | 1000 | 1000 | Passed |
| fee oracle 3 | 1400 | 1400 | Passed |
| fee oracle 4 | 1000 | 1000 | Passed |
| fee oracle 5 | 1400 | 1400 | Passed |
| fee oracle 6 | 1800 | 1800 | Passed |
| fee oracle 7 | 1000 | 1000 | Passed |
SHA-256 / 96af1d9c21da091c0d68a401e701801edfc8020533c17a36d4f1ee85c7c2182b
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:01.886505+00:00.
Case digest / a4b9625ff0f994d2e7dc26fda424ba7ddec8e16417a0ee21fd158564b3344118