FAILURE MAP
← Case archive

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

Tiered parking rate table: tiers are scanned in reverse order · case 01

Tiered parking rate table returns a wrong result when tiers are scanned in reverse order.

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

ROOT CAUSE

An unsorted rate table matches the first listed tier that fits rather than the smallest.

VERIFIED REPAIR

Restore the tier ordering rule so that the step reads `tiers = sorted(x['tiers'])`.

Unsuccessful approach: Descending order always matches the largest tier.

Case contract

Input {minutes, tiers: [[upto minutes, cumulative price]] in any order, extra_per: [block minutes, cents], max}. A stay of 0 minutes is free. Tiers are applied in ascending upto order and a stay of exactly upto minutes belongs to that tier (price capped at max). Beyond the last tier each started block after its upto adds the block price; the whole fee is capped at max.

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):
    m = x['minutes']
    tiers = x['tiers']
    if m <= 0:
        return 0
    for upto, price in tiers:
        if m <= upto:
            return min(price, x['max'])
    last_upto, last_price = tiers[-1]
    blk, add = x['extra_per']
    extra = -(-(m - last_upto) // blk)
    return min(last_price + extra * add, x['max'])
def check(label, actual, expected):
    observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
fixtures = [[({'minutes': 1000, 'tiers': [[90, 400], [180, 700]], 'extra_per': [45, 150], 'max': 2500}, 2500), ({'minutes': 330, 'tiers': [[210, 250], [330, 900]], 'extra_per': [15, 150], 'max': 4000}, 900), ({'minutes': 361, 'tiers': [[360, 950], [240, 450], [180, 200]], 'extra_per': [60, 100], 'max': 4000}, 1050), ({'minutes': 240, 'tiers': [[300, 850], [240, 550], [330, 900], [360, 1600]], 'extra_per': [15, 50], 'max': 4000}, 550), ({'minutes': 60, 'tiers': [[60, 300], [120, 500], [240, 800]], 'extra_per': [60, 200], 'max': 2000}, 300), ({'minutes': 1, 'tiers': [[30, 150], [60, 300]], 'extra_per': [30, 100], 'max': 1500}, 150), ({'minutes': 602, 'tiers': [[120, 850], [360, 1900], [270, 1700], [240, 1200]], 'extra_per': [20, 150], 'max': 4000}, 3850), ({'minutes': 121, 'tiers': [[120, 900]], 'extra_per': [30, 50], 'max': 2500}, 950)], [({'minutes': 1000, 'tiers': [[90, 400], [180, 700]], 'extra_per': [45, 150], 'max': 2500}, 2500), ({'minutes': 330, 'tiers': [[330, 1600]], 'extra_per': [20, 50], 'max': 4000}, 1600), ({'minutes': 250, 'tiers': [[240, 800], [60, 300], [120, 500]], 'extra_per': [60, 200], 'max': 2000}, 1000), ({'minutes': 157, 'tiers': [[60, 1250], [150, 1700]], 'extra_per': [30, 150], 'max': 1500}, 1500), ({'minutes': 30, 'tiers': [[180, 450], [30, 200], [300, 1600]], 'extra_per': [20, 100], 'max': 4000}, 200), ({'minutes': 352, 'tiers': [[330, 150]], 'extra_per': [60, 150], 'max': 2500}, 300), ({'minutes': 375, 'tiers': [[360, 1950], [60, 750], [180, 1150], [30, 700]], 'extra_per': [60, 50], 'max': 4000}, 2000), ({'minutes': 1, 'tiers': [[30, 150], [60, 300]], 'extra_per': [30, 100], 'max': 1500}, 150)], [({'minutes': 60, 'tiers': [[60, 300], [120, 500], [240, 800]], 'extra_per': [60, 200], 'max': 2000}, 300), ({'minutes': 91, 'tiers': [[90, 550], [330, 1800], [300, 1450]], 'extra_per': [60, 100], 'max': 4000}, 1450), ({'minutes': 0, 'tiers': [[60, 300]], 'extra_per': [30, 100], 'max': 1500}, 0), ({'minutes': 1, 'tiers': [[30, 150], [60, 300]], 'extra_per': [30, 100], 'max': 1500}, 150), ({'minutes': 91, 'tiers': [[240, 2000], [90, 1100], [120, 1150]], 'extra_per': [20, 50], 'max': 4000}, 1150), ({'minutes': 517, 'tiers': [[240, 2000]], 'extra_per': [15, 100], 'max': 1500}, 1500), ({'minutes': 60, 'tiers': [[120, 1600], [90, 1050], [60, 250], [300, 1900]], 'extra_per': [15, 100], 'max': 4000}, 250), ({'minutes': 288, 'tiers': [[90, 200], [210, 800]], 'extra_per': [15, 150], 'max': 1500}, 1500)], [({'minutes': 544, 'tiers': [[270, 200], [360, 1600]], 'extra_per': [60, 100], 'max': 1500}, 1500), ({'minutes': 885, 'tiers': [[210, 600], [180, 100]], 'extra_per': [20, 50], 'max': 4000}, 2300), ({'minutes': 1000, 'tiers': [[90, 400], [180, 700]], 'extra_per': [45, 150], 'max': 2500}, 2500), ({'minutes': 30, 'tiers': [[300, 1950], [30, 300], [210, 550]], 'extra_per': [30, 50], 'max': 1500}, 300), ({'minutes': 0, 'tiers': [[60, 300]], 'extra_per': [30, 100], 'max': 1500}, 0), ({'minutes': 211, 'tiers': [[360, 1600], [300, 1250], [60, 800], [210, 1000]], 'extra_per': [20, 50], 'max': 2500}, 1250), ({'minutes': 267, 'tiers': [[90, 1250], [180, 1350], [210, 1500], [270, 1900]], 'extra_per': [60, 150], 'max': 4000}, 1900), ({'minutes': 1, 'tiers': [[30, 150], [60, 300]], 'extra_per': [30, 100], 'max': 1500}, 150)], [({'minutes': 0, 'tiers': [[60, 300]], 'extra_per': [30, 100], 'max': 1500}, 0), ({'minutes': 47, 'tiers': [[300, 1650], [60, 1350]], 'extra_per': [30, 100], 'max': 4000}, 1350), ({'minutes': 301, 'tiers': [[300, 1550]], 'extra_per': [30, 150], 'max': 2500}, 1700), ({'minutes': 250, 'tiers': [[240, 800], [60, 300], [120, 500]], 'extra_per': [60, 200], 'max': 2000}, 1000), ({'minutes': 210, 'tiers': [[210, 100]], 'extra_per': [20, 150], 'max': 2500}, 100), ({'minutes': 125, 'tiers': [[300, 1300], [180, 300], [240, 1050], [210, 650]], 'extra_per': [20, 100], 'max': 1500}, 300), ({'minutes': 1000, 'tiers': [[90, 400], [180, 700]], 'extra_per': [45, 150], 'max': 2500}, 2500), ({'minutes': 900, 'tiers': [[120, 200], [150, 950], [300, 1450]], 'extra_per': [15, 50], 'max': 4000}, 3450)]]
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 025002500Passed
fee oracle 1900900Passed
fee oracle 26001050Failed
fee oracle 3850550Failed
fee oracle 4300300Passed
fee oracle 5150150Passed
fee oracle 640003850Failed
fee oracle 7950950Passed

SHA-256 / 7df0417a48b7c41834771d0dfc5883857ee37f9b6013578a95705be81b131839

2 / The unsuccessful fix

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

N = 1
observations = []
def solve(x):
    m = x['minutes']
    tiers = sorted(x['tiers'], reverse=True)
    if m <= 0:
        return 0
    for upto, price in tiers:
        if m <= upto:
            return min(price, x['max'])
    last_upto, last_price = tiers[-1]
    blk, add = x['extra_per']
    extra = -(-(m - last_upto) // blk)
    return min(last_price + extra * add, x['max'])
def check(label, actual, expected):
    observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
fixtures = [[({'minutes': 1000, 'tiers': [[90, 400], [180, 700]], 'extra_per': [45, 150], 'max': 2500}, 2500), ({'minutes': 330, 'tiers': [[210, 250], [330, 900]], 'extra_per': [15, 150], 'max': 4000}, 900), ({'minutes': 361, 'tiers': [[360, 950], [240, 450], [180, 200]], 'extra_per': [60, 100], 'max': 4000}, 1050), ({'minutes': 240, 'tiers': [[300, 850], [240, 550], [330, 900], [360, 1600]], 'extra_per': [15, 50], 'max': 4000}, 550), ({'minutes': 60, 'tiers': [[60, 300], [120, 500], [240, 800]], 'extra_per': [60, 200], 'max': 2000}, 300), ({'minutes': 1, 'tiers': [[30, 150], [60, 300]], 'extra_per': [30, 100], 'max': 1500}, 150), ({'minutes': 602, 'tiers': [[120, 850], [360, 1900], [270, 1700], [240, 1200]], 'extra_per': [20, 150], 'max': 4000}, 3850), ({'minutes': 121, 'tiers': [[120, 900]], 'extra_per': [30, 50], 'max': 2500}, 950)], [({'minutes': 1000, 'tiers': [[90, 400], [180, 700]], 'extra_per': [45, 150], 'max': 2500}, 2500), ({'minutes': 330, 'tiers': [[330, 1600]], 'extra_per': [20, 50], 'max': 4000}, 1600), ({'minutes': 250, 'tiers': [[240, 800], [60, 300], [120, 500]], 'extra_per': [60, 200], 'max': 2000}, 1000), ({'minutes': 157, 'tiers': [[60, 1250], [150, 1700]], 'extra_per': [30, 150], 'max': 1500}, 1500), ({'minutes': 30, 'tiers': [[180, 450], [30, 200], [300, 1600]], 'extra_per': [20, 100], 'max': 4000}, 200), ({'minutes': 352, 'tiers': [[330, 150]], 'extra_per': [60, 150], 'max': 2500}, 300), ({'minutes': 375, 'tiers': [[360, 1950], [60, 750], [180, 1150], [30, 700]], 'extra_per': [60, 50], 'max': 4000}, 2000), ({'minutes': 1, 'tiers': [[30, 150], [60, 300]], 'extra_per': [30, 100], 'max': 1500}, 150)], [({'minutes': 60, 'tiers': [[60, 300], [120, 500], [240, 800]], 'extra_per': [60, 200], 'max': 2000}, 300), ({'minutes': 91, 'tiers': [[90, 550], [330, 1800], [300, 1450]], 'extra_per': [60, 100], 'max': 4000}, 1450), ({'minutes': 0, 'tiers': [[60, 300]], 'extra_per': [30, 100], 'max': 1500}, 0), ({'minutes': 1, 'tiers': [[30, 150], [60, 300]], 'extra_per': [30, 100], 'max': 1500}, 150), ({'minutes': 91, 'tiers': [[240, 2000], [90, 1100], [120, 1150]], 'extra_per': [20, 50], 'max': 4000}, 1150), ({'minutes': 517, 'tiers': [[240, 2000]], 'extra_per': [15, 100], 'max': 1500}, 1500), ({'minutes': 60, 'tiers': [[120, 1600], [90, 1050], [60, 250], [300, 1900]], 'extra_per': [15, 100], 'max': 4000}, 250), ({'minutes': 288, 'tiers': [[90, 200], [210, 800]], 'extra_per': [15, 150], 'max': 1500}, 1500)], [({'minutes': 544, 'tiers': [[270, 200], [360, 1600]], 'extra_per': [60, 100], 'max': 1500}, 1500), ({'minutes': 885, 'tiers': [[210, 600], [180, 100]], 'extra_per': [20, 50], 'max': 4000}, 2300), ({'minutes': 1000, 'tiers': [[90, 400], [180, 700]], 'extra_per': [45, 150], 'max': 2500}, 2500), ({'minutes': 30, 'tiers': [[300, 1950], [30, 300], [210, 550]], 'extra_per': [30, 50], 'max': 1500}, 300), ({'minutes': 0, 'tiers': [[60, 300]], 'extra_per': [30, 100], 'max': 1500}, 0), ({'minutes': 211, 'tiers': [[360, 1600], [300, 1250], [60, 800], [210, 1000]], 'extra_per': [20, 50], 'max': 2500}, 1250), ({'minutes': 267, 'tiers': [[90, 1250], [180, 1350], [210, 1500], [270, 1900]], 'extra_per': [60, 150], 'max': 4000}, 1900), ({'minutes': 1, 'tiers': [[30, 150], [60, 300]], 'extra_per': [30, 100], 'max': 1500}, 150)], [({'minutes': 0, 'tiers': [[60, 300]], 'extra_per': [30, 100], 'max': 1500}, 0), ({'minutes': 47, 'tiers': [[300, 1650], [60, 1350]], 'extra_per': [30, 100], 'max': 4000}, 1350), ({'minutes': 301, 'tiers': [[300, 1550]], 'extra_per': [30, 150], 'max': 2500}, 1700), ({'minutes': 250, 'tiers': [[240, 800], [60, 300], [120, 500]], 'extra_per': [60, 200], 'max': 2000}, 1000), ({'minutes': 210, 'tiers': [[210, 100]], 'extra_per': [20, 150], 'max': 2500}, 100), ({'minutes': 125, 'tiers': [[300, 1300], [180, 300], [240, 1050], [210, 650]], 'extra_per': [20, 100], 'max': 1500}, 300), ({'minutes': 1000, 'tiers': [[90, 400], [180, 700]], 'extra_per': [45, 150], 'max': 2500}, 2500), ({'minutes': 900, 'tiers': [[120, 200], [150, 950], [300, 1450]], 'extra_per': [15, 50], 'max': 4000}, 3450)]]
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 025002500Passed
fee oracle 1900900Passed
fee oracle 26001050Failed
fee oracle 31600550Failed
fee oracle 4800300Failed
fee oracle 5300150Failed
fee oracle 640003850Failed
fee oracle 7950950Passed

SHA-256 / 28a464d8290e8f6223854f119bd6bd7a98d1590aae10829633d71c00a4368cb1

3 / The verified repair

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

N = 1
observations = []
def solve(x):
    m = x['minutes']
    tiers = sorted(x['tiers'])
    if m <= 0:
        return 0
    for upto, price in tiers:
        if m <= upto:
            return min(price, x['max'])
    last_upto, last_price = tiers[-1]
    blk, add = x['extra_per']
    extra = -(-(m - last_upto) // blk)
    return min(last_price + extra * add, x['max'])
def check(label, actual, expected):
    observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
fixtures = [[({'minutes': 1000, 'tiers': [[90, 400], [180, 700]], 'extra_per': [45, 150], 'max': 2500}, 2500), ({'minutes': 330, 'tiers': [[210, 250], [330, 900]], 'extra_per': [15, 150], 'max': 4000}, 900), ({'minutes': 361, 'tiers': [[360, 950], [240, 450], [180, 200]], 'extra_per': [60, 100], 'max': 4000}, 1050), ({'minutes': 240, 'tiers': [[300, 850], [240, 550], [330, 900], [360, 1600]], 'extra_per': [15, 50], 'max': 4000}, 550), ({'minutes': 60, 'tiers': [[60, 300], [120, 500], [240, 800]], 'extra_per': [60, 200], 'max': 2000}, 300), ({'minutes': 1, 'tiers': [[30, 150], [60, 300]], 'extra_per': [30, 100], 'max': 1500}, 150), ({'minutes': 602, 'tiers': [[120, 850], [360, 1900], [270, 1700], [240, 1200]], 'extra_per': [20, 150], 'max': 4000}, 3850), ({'minutes': 121, 'tiers': [[120, 900]], 'extra_per': [30, 50], 'max': 2500}, 950)], [({'minutes': 1000, 'tiers': [[90, 400], [180, 700]], 'extra_per': [45, 150], 'max': 2500}, 2500), ({'minutes': 330, 'tiers': [[330, 1600]], 'extra_per': [20, 50], 'max': 4000}, 1600), ({'minutes': 250, 'tiers': [[240, 800], [60, 300], [120, 500]], 'extra_per': [60, 200], 'max': 2000}, 1000), ({'minutes': 157, 'tiers': [[60, 1250], [150, 1700]], 'extra_per': [30, 150], 'max': 1500}, 1500), ({'minutes': 30, 'tiers': [[180, 450], [30, 200], [300, 1600]], 'extra_per': [20, 100], 'max': 4000}, 200), ({'minutes': 352, 'tiers': [[330, 150]], 'extra_per': [60, 150], 'max': 2500}, 300), ({'minutes': 375, 'tiers': [[360, 1950], [60, 750], [180, 1150], [30, 700]], 'extra_per': [60, 50], 'max': 4000}, 2000), ({'minutes': 1, 'tiers': [[30, 150], [60, 300]], 'extra_per': [30, 100], 'max': 1500}, 150)], [({'minutes': 60, 'tiers': [[60, 300], [120, 500], [240, 800]], 'extra_per': [60, 200], 'max': 2000}, 300), ({'minutes': 91, 'tiers': [[90, 550], [330, 1800], [300, 1450]], 'extra_per': [60, 100], 'max': 4000}, 1450), ({'minutes': 0, 'tiers': [[60, 300]], 'extra_per': [30, 100], 'max': 1500}, 0), ({'minutes': 1, 'tiers': [[30, 150], [60, 300]], 'extra_per': [30, 100], 'max': 1500}, 150), ({'minutes': 91, 'tiers': [[240, 2000], [90, 1100], [120, 1150]], 'extra_per': [20, 50], 'max': 4000}, 1150), ({'minutes': 517, 'tiers': [[240, 2000]], 'extra_per': [15, 100], 'max': 1500}, 1500), ({'minutes': 60, 'tiers': [[120, 1600], [90, 1050], [60, 250], [300, 1900]], 'extra_per': [15, 100], 'max': 4000}, 250), ({'minutes': 288, 'tiers': [[90, 200], [210, 800]], 'extra_per': [15, 150], 'max': 1500}, 1500)], [({'minutes': 544, 'tiers': [[270, 200], [360, 1600]], 'extra_per': [60, 100], 'max': 1500}, 1500), ({'minutes': 885, 'tiers': [[210, 600], [180, 100]], 'extra_per': [20, 50], 'max': 4000}, 2300), ({'minutes': 1000, 'tiers': [[90, 400], [180, 700]], 'extra_per': [45, 150], 'max': 2500}, 2500), ({'minutes': 30, 'tiers': [[300, 1950], [30, 300], [210, 550]], 'extra_per': [30, 50], 'max': 1500}, 300), ({'minutes': 0, 'tiers': [[60, 300]], 'extra_per': [30, 100], 'max': 1500}, 0), ({'minutes': 211, 'tiers': [[360, 1600], [300, 1250], [60, 800], [210, 1000]], 'extra_per': [20, 50], 'max': 2500}, 1250), ({'minutes': 267, 'tiers': [[90, 1250], [180, 1350], [210, 1500], [270, 1900]], 'extra_per': [60, 150], 'max': 4000}, 1900), ({'minutes': 1, 'tiers': [[30, 150], [60, 300]], 'extra_per': [30, 100], 'max': 1500}, 150)], [({'minutes': 0, 'tiers': [[60, 300]], 'extra_per': [30, 100], 'max': 1500}, 0), ({'minutes': 47, 'tiers': [[300, 1650], [60, 1350]], 'extra_per': [30, 100], 'max': 4000}, 1350), ({'minutes': 301, 'tiers': [[300, 1550]], 'extra_per': [30, 150], 'max': 2500}, 1700), ({'minutes': 250, 'tiers': [[240, 800], [60, 300], [120, 500]], 'extra_per': [60, 200], 'max': 2000}, 1000), ({'minutes': 210, 'tiers': [[210, 100]], 'extra_per': [20, 150], 'max': 2500}, 100), ({'minutes': 125, 'tiers': [[300, 1300], [180, 300], [240, 1050], [210, 650]], 'extra_per': [20, 100], 'max': 1500}, 300), ({'minutes': 1000, 'tiers': [[90, 400], [180, 700]], 'extra_per': [45, 150], 'max': 2500}, 2500), ({'minutes': 900, 'tiers': [[120, 200], [150, 950], [300, 1450]], 'extra_per': [15, 50], 'max': 4000}, 3450)]]
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 025002500Passed
fee oracle 1900900Passed
fee oracle 210501050Passed
fee oracle 3550550Passed
fee oracle 4300300Passed
fee oracle 5150150Passed
fee oracle 638503850Passed
fee oracle 7950950Passed

SHA-256 / 71f023823c1493466298990671c396dc3074d2caa4f1e2155f95cbed39e608f6

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.599023+00:00.

Case digest / f200e01559fcfa96d78618524145796eb4a865260bd1607026f0e362c5a69a98