FA-68421 / Toll and parking fee computation / Open access
Early bird flat rate: an exact 24 hour stay counts as two days · case 01
Early bird flat rate returns a wrong result when an exact 24 hour stay counts as two days.
ROOT CAUSE
Exactly 1440 minutes is billed as two started days.
VERIFIED REPAIR
Restore the started day count rule so that the step reads `days = max(1, -(-dur // 1440))`.
Unsuccessful approach: Flooring the days undercounts a stay of a day and a few minutes.
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 = dur // 1440 + 1
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, 1141], 'hourly': 300, 'daily_max': 1800, 'early': 900}, 1800), ({'entry': [13, 540], 'exit': [13, 1140], 'hourly': 200, 'daily_max': 1200, 'early': 1400}, 1200), ({'entry': [9, 541], 'exit': [10, 1174], 'hourly': 200, 'daily_max': 1200, 'early': 1000}, 2400), ({'entry': [1, 0], 'exit': [2, 0], 'hourly': 300, 'daily_max': 1800, 'early': 1000}, 1800), ({'entry': [3, 420], 'exit': [4, 960], 'hourly': 300, 'daily_max': 1800, 'early': 900}, 3600), ({'entry': [3, 400], 'exit': [3, 1000], 'hourly': 300, 'daily_max': 800, 'early': 1000}, 800), ({'entry': [5, 400], 'exit': [5, 1000], 'hourly': 300, 'daily_max': 2500, 'early': 1000}, 2500), ({'entry': [3, 540], 'exit': [3, 1141], 'hourly': 400, 'daily_max': 1200, 'early': 900}, 1200)], [({'entry': [12, 360], 'exit': [12, 523], 'hourly': 300, 'daily_max': 2500, 'early': 900}, 900), ({'entry': [3, 420], 'exit': [4, 960], 'hourly': 300, 'daily_max': 1800, 'early': 900}, 3600), ({'entry': [5, 400], 'exit': [5, 1000], 'hourly': 300, 'daily_max': 2500, 'early': 1000}, 2500), ({'entry': [0, 359], 'exit': [0, 1256], 'hourly': 200, 'daily_max': 2500, 'early': 1400}, 2500), ({'entry': [1, 0], 'exit': [2, 0], 'hourly': 300, 'daily_max': 1800, 'early': 1000}, 1800), ({'entry': [4, 960], 'exit': [4, 1140], 'hourly': 200, 'daily_max': 1200, 'early': 1000}, 600), ({'entry': [3, 540], 'exit': [4, 1140], 'hourly': 400, 'daily_max': 1200, 'early': 900}, 2400), ({'entry': [2, 540], 'exit': [2, 1000], 'hourly': 300, 'daily_max': 2500, 'early': 1000}, 1000)], [({'entry': [1, 0], 'exit': [2, 0], 'hourly': 300, 'daily_max': 1800, 'early': 1000}, 1800), ({'entry': [7, 540], 'exit': [7, 900], 'hourly': 300, 'daily_max': 1200, 'early': 1000}, 1000), ({'entry': [2, 500], 'exit': [4, 1141], 'hourly': 400, 'daily_max': 2500, 'early': 900}, 7500), ({'entry': [0, 480], 'exit': [0, 1300], 'hourly': 300, 'daily_max': 2500, 'early': 1000}, 2500), ({'entry': [5, 467], 'exit': [6, 534], 'hourly': 200, 'daily_max': 1200, 'early': 900}, 2400), ({'entry': [5, 400], 'exit': [5, 1000], 'hourly': 300, 'daily_max': 2500, 'early': 1000}, 2500), ({'entry': [11, 540], 'exit': [11, 900], 'hourly': 300, 'daily_max': 2500, 'early': 1000}, 1000), ({'entry': [3, 400], 'exit': [3, 1000], 'hourly': 300, 'daily_max': 800, 'early': 1000}, 800)], [({'entry': [11, 540], 'exit': [12, 1140], 'hourly': 300, 'daily_max': 2500, 'early': 1400}, 5000), ({'entry': [1, 0], 'exit': [2, 0], 'hourly': 300, 'daily_max': 1800, 'early': 1000}, 1800), ({'entry': [13, 540], 'exit': [13, 899], 'hourly': 400, 'daily_max': 1200, 'early': 900}, 1200), ({'entry': [1, 360], 'exit': [1, 1140], 'hourly': 400, 'daily_max': 1800, 'early': 1000}, 1000), ({'entry': [3, 400], 'exit': [3, 1000], 'hourly': 300, 'daily_max': 800, 'early': 1000}, 800), ({'entry': [12, 530], 'exit': [14, 900], 'hourly': 200, 'daily_max': 2500, 'early': 1400}, 7500), ({'entry': [2, 540], 'exit': [2, 1000], 'hourly': 300, 'daily_max': 2500, 'early': 1000}, 1000), ({'entry': [3, 415], 'exit': [3, 900], 'hourly': 300, 'daily_max': 1800, 'early': 1400}, 1400)], [({'entry': [8, 540], 'exit': [8, 1141], 'hourly': 300, 'daily_max': 1800, 'early': 900}, 1800), ({'entry': [2, 540], 'exit': [2, 1000], 'hourly': 300, 'daily_max': 2500, 'early': 1000}, 1000), ({'entry': [3, 420], 'exit': [4, 960], 'hourly': 300, 'daily_max': 1800, 'early': 900}, 3600), ({'entry': [8, 540], 'exit': [10, 900], 'hourly': 200, 'daily_max': 2500, 'early': 1400}, 7500), ({'entry': [8, 551], 'exit': [10, 241], 'hourly': 200, 'daily_max': 1200, 'early': 1000}, 2400), ({'entry': [1, 0], 'exit': [2, 0], 'hourly': 300, 'daily_max': 1800, 'early': 1000}, 1800), ({'entry': [0, 480], 'exit': [0, 1300], 'hourly': 300, 'daily_max': 2500, 'early': 1000}, 2500), ({'entry': [6, 360], 'exit': [6, 1029], 'hourly': 200, 'daily_max': 1800, 'early': 1000}, 1800)]]
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 | 1800 | 1800 | Passed |
| fee oracle 1 | 1200 | 1200 | Passed |
| fee oracle 2 | 2400 | 2400 | Passed |
| fee oracle 3 | 3600 | 1800 | Failed |
| fee oracle 4 | 3600 | 3600 | Passed |
| fee oracle 5 | 800 | 800 | Passed |
| fee oracle 6 | 2500 | 2500 | Passed |
| fee oracle 7 | 1200 | 1200 | Passed |
SHA-256 / 3a4474df8e89874ebedcced0a4f7ad67c15b0b072ad9a129b9dfcad10daa015b
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': [8, 360], 'exit': [8, 1141], 'hourly': 300, 'daily_max': 1800, 'early': 900}, 1800), ({'entry': [13, 540], 'exit': [13, 1140], 'hourly': 200, 'daily_max': 1200, 'early': 1400}, 1200), ({'entry': [9, 541], 'exit': [10, 1174], 'hourly': 200, 'daily_max': 1200, 'early': 1000}, 2400), ({'entry': [1, 0], 'exit': [2, 0], 'hourly': 300, 'daily_max': 1800, 'early': 1000}, 1800), ({'entry': [3, 420], 'exit': [4, 960], 'hourly': 300, 'daily_max': 1800, 'early': 900}, 3600), ({'entry': [3, 400], 'exit': [3, 1000], 'hourly': 300, 'daily_max': 800, 'early': 1000}, 800), ({'entry': [5, 400], 'exit': [5, 1000], 'hourly': 300, 'daily_max': 2500, 'early': 1000}, 2500), ({'entry': [3, 540], 'exit': [3, 1141], 'hourly': 400, 'daily_max': 1200, 'early': 900}, 1200)], [({'entry': [12, 360], 'exit': [12, 523], 'hourly': 300, 'daily_max': 2500, 'early': 900}, 900), ({'entry': [3, 420], 'exit': [4, 960], 'hourly': 300, 'daily_max': 1800, 'early': 900}, 3600), ({'entry': [5, 400], 'exit': [5, 1000], 'hourly': 300, 'daily_max': 2500, 'early': 1000}, 2500), ({'entry': [0, 359], 'exit': [0, 1256], 'hourly': 200, 'daily_max': 2500, 'early': 1400}, 2500), ({'entry': [1, 0], 'exit': [2, 0], 'hourly': 300, 'daily_max': 1800, 'early': 1000}, 1800), ({'entry': [4, 960], 'exit': [4, 1140], 'hourly': 200, 'daily_max': 1200, 'early': 1000}, 600), ({'entry': [3, 540], 'exit': [4, 1140], 'hourly': 400, 'daily_max': 1200, 'early': 900}, 2400), ({'entry': [2, 540], 'exit': [2, 1000], 'hourly': 300, 'daily_max': 2500, 'early': 1000}, 1000)], [({'entry': [1, 0], 'exit': [2, 0], 'hourly': 300, 'daily_max': 1800, 'early': 1000}, 1800), ({'entry': [7, 540], 'exit': [7, 900], 'hourly': 300, 'daily_max': 1200, 'early': 1000}, 1000), ({'entry': [2, 500], 'exit': [4, 1141], 'hourly': 400, 'daily_max': 2500, 'early': 900}, 7500), ({'entry': [0, 480], 'exit': [0, 1300], 'hourly': 300, 'daily_max': 2500, 'early': 1000}, 2500), ({'entry': [5, 467], 'exit': [6, 534], 'hourly': 200, 'daily_max': 1200, 'early': 900}, 2400), ({'entry': [5, 400], 'exit': [5, 1000], 'hourly': 300, 'daily_max': 2500, 'early': 1000}, 2500), ({'entry': [11, 540], 'exit': [11, 900], 'hourly': 300, 'daily_max': 2500, 'early': 1000}, 1000), ({'entry': [3, 400], 'exit': [3, 1000], 'hourly': 300, 'daily_max': 800, 'early': 1000}, 800)], [({'entry': [11, 540], 'exit': [12, 1140], 'hourly': 300, 'daily_max': 2500, 'early': 1400}, 5000), ({'entry': [1, 0], 'exit': [2, 0], 'hourly': 300, 'daily_max': 1800, 'early': 1000}, 1800), ({'entry': [13, 540], 'exit': [13, 899], 'hourly': 400, 'daily_max': 1200, 'early': 900}, 1200), ({'entry': [1, 360], 'exit': [1, 1140], 'hourly': 400, 'daily_max': 1800, 'early': 1000}, 1000), ({'entry': [3, 400], 'exit': [3, 1000], 'hourly': 300, 'daily_max': 800, 'early': 1000}, 800), ({'entry': [12, 530], 'exit': [14, 900], 'hourly': 200, 'daily_max': 2500, 'early': 1400}, 7500), ({'entry': [2, 540], 'exit': [2, 1000], 'hourly': 300, 'daily_max': 2500, 'early': 1000}, 1000), ({'entry': [3, 415], 'exit': [3, 900], 'hourly': 300, 'daily_max': 1800, 'early': 1400}, 1400)], [({'entry': [8, 540], 'exit': [8, 1141], 'hourly': 300, 'daily_max': 1800, 'early': 900}, 1800), ({'entry': [2, 540], 'exit': [2, 1000], 'hourly': 300, 'daily_max': 2500, 'early': 1000}, 1000), ({'entry': [3, 420], 'exit': [4, 960], 'hourly': 300, 'daily_max': 1800, 'early': 900}, 3600), ({'entry': [8, 540], 'exit': [10, 900], 'hourly': 200, 'daily_max': 2500, 'early': 1400}, 7500), ({'entry': [8, 551], 'exit': [10, 241], 'hourly': 200, 'daily_max': 1200, 'early': 1000}, 2400), ({'entry': [1, 0], 'exit': [2, 0], 'hourly': 300, 'daily_max': 1800, 'early': 1000}, 1800), ({'entry': [0, 480], 'exit': [0, 1300], 'hourly': 300, 'daily_max': 2500, 'early': 1000}, 2500), ({'entry': [6, 360], 'exit': [6, 1029], 'hourly': 200, 'daily_max': 1800, 'early': 1000}, 1800)]]
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 | 1800 | 1800 | Passed |
| fee oracle 1 | 1200 | 1200 | Passed |
| fee oracle 2 | 1200 | 2400 | Failed |
| fee oracle 3 | 1800 | 1800 | Passed |
| fee oracle 4 | 1800 | 3600 | Failed |
| fee oracle 5 | 800 | 800 | Passed |
| fee oracle 6 | 2500 | 2500 | Passed |
| fee oracle 7 | 1200 | 1200 | Passed |
SHA-256 / a5d92d61dd2b98f46d5d3555c742c880a14c9edbeeb75ed34617964e8f2c70da
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, 1141], 'hourly': 300, 'daily_max': 1800, 'early': 900}, 1800), ({'entry': [13, 540], 'exit': [13, 1140], 'hourly': 200, 'daily_max': 1200, 'early': 1400}, 1200), ({'entry': [9, 541], 'exit': [10, 1174], 'hourly': 200, 'daily_max': 1200, 'early': 1000}, 2400), ({'entry': [1, 0], 'exit': [2, 0], 'hourly': 300, 'daily_max': 1800, 'early': 1000}, 1800), ({'entry': [3, 420], 'exit': [4, 960], 'hourly': 300, 'daily_max': 1800, 'early': 900}, 3600), ({'entry': [3, 400], 'exit': [3, 1000], 'hourly': 300, 'daily_max': 800, 'early': 1000}, 800), ({'entry': [5, 400], 'exit': [5, 1000], 'hourly': 300, 'daily_max': 2500, 'early': 1000}, 2500), ({'entry': [3, 540], 'exit': [3, 1141], 'hourly': 400, 'daily_max': 1200, 'early': 900}, 1200)], [({'entry': [12, 360], 'exit': [12, 523], 'hourly': 300, 'daily_max': 2500, 'early': 900}, 900), ({'entry': [3, 420], 'exit': [4, 960], 'hourly': 300, 'daily_max': 1800, 'early': 900}, 3600), ({'entry': [5, 400], 'exit': [5, 1000], 'hourly': 300, 'daily_max': 2500, 'early': 1000}, 2500), ({'entry': [0, 359], 'exit': [0, 1256], 'hourly': 200, 'daily_max': 2500, 'early': 1400}, 2500), ({'entry': [1, 0], 'exit': [2, 0], 'hourly': 300, 'daily_max': 1800, 'early': 1000}, 1800), ({'entry': [4, 960], 'exit': [4, 1140], 'hourly': 200, 'daily_max': 1200, 'early': 1000}, 600), ({'entry': [3, 540], 'exit': [4, 1140], 'hourly': 400, 'daily_max': 1200, 'early': 900}, 2400), ({'entry': [2, 540], 'exit': [2, 1000], 'hourly': 300, 'daily_max': 2500, 'early': 1000}, 1000)], [({'entry': [1, 0], 'exit': [2, 0], 'hourly': 300, 'daily_max': 1800, 'early': 1000}, 1800), ({'entry': [7, 540], 'exit': [7, 900], 'hourly': 300, 'daily_max': 1200, 'early': 1000}, 1000), ({'entry': [2, 500], 'exit': [4, 1141], 'hourly': 400, 'daily_max': 2500, 'early': 900}, 7500), ({'entry': [0, 480], 'exit': [0, 1300], 'hourly': 300, 'daily_max': 2500, 'early': 1000}, 2500), ({'entry': [5, 467], 'exit': [6, 534], 'hourly': 200, 'daily_max': 1200, 'early': 900}, 2400), ({'entry': [5, 400], 'exit': [5, 1000], 'hourly': 300, 'daily_max': 2500, 'early': 1000}, 2500), ({'entry': [11, 540], 'exit': [11, 900], 'hourly': 300, 'daily_max': 2500, 'early': 1000}, 1000), ({'entry': [3, 400], 'exit': [3, 1000], 'hourly': 300, 'daily_max': 800, 'early': 1000}, 800)], [({'entry': [11, 540], 'exit': [12, 1140], 'hourly': 300, 'daily_max': 2500, 'early': 1400}, 5000), ({'entry': [1, 0], 'exit': [2, 0], 'hourly': 300, 'daily_max': 1800, 'early': 1000}, 1800), ({'entry': [13, 540], 'exit': [13, 899], 'hourly': 400, 'daily_max': 1200, 'early': 900}, 1200), ({'entry': [1, 360], 'exit': [1, 1140], 'hourly': 400, 'daily_max': 1800, 'early': 1000}, 1000), ({'entry': [3, 400], 'exit': [3, 1000], 'hourly': 300, 'daily_max': 800, 'early': 1000}, 800), ({'entry': [12, 530], 'exit': [14, 900], 'hourly': 200, 'daily_max': 2500, 'early': 1400}, 7500), ({'entry': [2, 540], 'exit': [2, 1000], 'hourly': 300, 'daily_max': 2500, 'early': 1000}, 1000), ({'entry': [3, 415], 'exit': [3, 900], 'hourly': 300, 'daily_max': 1800, 'early': 1400}, 1400)], [({'entry': [8, 540], 'exit': [8, 1141], 'hourly': 300, 'daily_max': 1800, 'early': 900}, 1800), ({'entry': [2, 540], 'exit': [2, 1000], 'hourly': 300, 'daily_max': 2500, 'early': 1000}, 1000), ({'entry': [3, 420], 'exit': [4, 960], 'hourly': 300, 'daily_max': 1800, 'early': 900}, 3600), ({'entry': [8, 540], 'exit': [10, 900], 'hourly': 200, 'daily_max': 2500, 'early': 1400}, 7500), ({'entry': [8, 551], 'exit': [10, 241], 'hourly': 200, 'daily_max': 1200, 'early': 1000}, 2400), ({'entry': [1, 0], 'exit': [2, 0], 'hourly': 300, 'daily_max': 1800, 'early': 1000}, 1800), ({'entry': [0, 480], 'exit': [0, 1300], 'hourly': 300, 'daily_max': 2500, 'early': 1000}, 2500), ({'entry': [6, 360], 'exit': [6, 1029], 'hourly': 200, 'daily_max': 1800, 'early': 1000}, 1800)]]
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 | 1800 | 1800 | Passed |
| fee oracle 1 | 1200 | 1200 | Passed |
| fee oracle 2 | 2400 | 2400 | Passed |
| fee oracle 3 | 1800 | 1800 | Passed |
| fee oracle 4 | 3600 | 3600 | Passed |
| fee oracle 5 | 800 | 800 | Passed |
| fee oracle 6 | 2500 | 2500 | Passed |
| fee oracle 7 | 1200 | 1200 | Passed |
SHA-256 / c993eeb8afae4491955d716c870d04037469b7c7ae6b76d92150c30207b83825
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.132521+00:00.
Case digest / 00447df62fec53094680fb469f72ddf3d1108843b6da43896754fd8a77c25446