FAILURE MAP
← Case archive

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

Open-road gantry trip building: direction changes do not start a new trip · case 01

Open-road gantry trip building returns a wrong result when direction changes do not start a new trip.

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

ROOT CAUSE

A U-turn inside the gap window is billed as part of the same trip and capped together.

VERIFIED REPAIR

Restore the direction continuity rule so that the step reads `trips[-1]['d'] == d and`.

Unsuccessful approach: Letting single-read trips change direction still merges an outbound read with the return trip.

Case contract

Input {reads: [[minute, gantry, direction]] unordered, dedupe, max_gap, price per gantry, trip_cap}. Reads are sorted; a read of the same gantry within dedupe minutes (inclusive) of the last kept read of that gantry is dropped. Kept reads join the current trip when the direction matches and the gap from the trip's latest read is <= max_gap, else start a new trip. Each trip pays min(trip_cap, sum of its gantry prices). Return [trips, total].

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):
    reads = sorted(x['reads'])
    kept = []
    lastg = {}
    for t, g, d in reads:
        if g in lastg and t - lastg[g] <= x['dedupe']:
            continue
        lastg[g] = t
        kept.append((t, g, d))
    trips = []
    for t, g, d in kept:
        if trips and t - trips[-1]['t'] <= x['max_gap']:
            trips[-1]['g'].append(g)
            trips[-1]['t'] = t
        else:
            trips.append({'d': d, 't': t, 'g': [g]})
    total = sum(min(x['trip_cap'], sum(x['price'][g] for g in tr['g'])) for tr in trips)
    return [len(trips), total]
def check(label, actual, expected):
    observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
fixtures = [[({'reads': [[153, 0, 'S'], [163, 5, 'S'], [148, 1, 'S'], [63, 1, 'S'], [53, 5, 'N'], [103, 4, 'S'], [33, 5, 'N'], [73, 3, 'S']], 'dedupe': 3, 'max_gap': 30, 'price': [75, 75, 110, 150, 200, 75], 'trip_cap': 900}, [3, 800]), ({'reads': [[134, 0, 'S'], [28, 5, 'N'], [187, 3, 'S'], [83, 2, 'S'], [113, 4, 'S'], [186, 5, 'S'], [113, 4, 'S'], [133, 0, 'S'], [136, 5, 'S'], [166, 4, 'S'], [38, 1, 'S'], [136, 5, 'S']], 'dedupe': 2, 'max_gap': 20, 'price': [200, 110, 200, 150, 200, 150], 'trip_cap': 900}, [5, 1510]), ({'reads': [[65, 1, 'N'], [138, 1, 'S'], [35, 1, 'N'], [148, 1, 'S'], [135, 4, 'N'], [91, 4, 'S'], [148, 1, 'S'], [71, 4, 'N'], [70, 4, 'N'], [65, 1, 'N'], [90, 4, 'S']], 'dedupe': 3, 'max_gap': 30, 'price': [150, 110, 110, 150, 75, 150], 'trip_cap': 600}, [4, 665]), ({'reads': [[0, 1, 'N'], [2, 1, 'N'], [4, 1, 'N'], [30, 2, 'N'], [55, 3, 'N']], 'dedupe': 2, 'max_gap': 30, 'price': [100, 150, 200, 250, 100, 100], 'trip_cap': 900}, [1, 750]), ({'reads': [[10, 0, 'S'], [5, 4, 'N'], [12, 1, 'S'], [11, 0, 'S']], 'dedupe': 3, 'max_gap': 20, 'price': [100, 150, 200, 250, 100, 100], 'trip_cap': 400}, [2, 350]), ({'reads': [[5, 2, 'N'], [8, 2, 'N'], [8, 5, 'N']], 'dedupe': 3, 'max_gap': 20, 'price': [150, 75, 75, 110, 75, 200], 'trip_cap': 900}, [1, 275]), ({'reads': [[17, 5, 'N'], [18, 5, 'N'], [20, 4, 'S'], [20, 4, 'S'], [23, 5, 'S'], [26, 4, 'N'], [28, 4, 'N'], [46, 4, 'S'], [46, 4, 'S']], 'dedupe': 2, 'max_gap': 30, 'price': [75, 200, 150, 110, 200, 150], 'trip_cap': 600}, [4, 900]), ({'reads': [[40, 3, 'N'], [43, 3, 'N'], [45, 5, 'N'], [47, 5, 'N'], [75, 4, 'N'], [77, 4, 'N'], [105, 2, 'N'], [136, 0, 'N'], [136, 0, 'N']], 'dedupe': 3, 'max_gap': 30, 'price': [75, 200, 110, 75, 150, 110], 'trip_cap': 600}, [2, 520])], [({'reads': [[36, 3, 'N'], [39, 3, 'N'], [46, 2, 'S'], [47, 4, 'S'], [92, 0, 'S'], [102, 1, 'N'], [122, 3, 'N'], [125, 2, 'N'], [135, 2, 'N']], 'dedupe': 2, 'max_gap': 20, 'price': [75, 110, 150, 75, 150, 110], 'trip_cap': 400}, [4, 925]), ({'reads': [[0, 1, 'N'], [2, 1, 'N'], [4, 1, 'N'], [30, 2, 'N'], [55, 3, 'N']], 'dedupe': 2, 'max_gap': 30, 'price': [100, 150, 200, 250, 100, 100], 'trip_cap': 900}, [1, 750]), ({'reads': [[10, 0, 'S'], [5, 4, 'N'], [12, 1, 'S'], [11, 0, 'S']], 'dedupe': 3, 'max_gap': 20, 'price': [100, 150, 200, 250, 100, 100], 'trip_cap': 400}, [2, 350]), ({'reads': [[16, 3, 'S'], [26, 4, 'S'], [29, 3, 'S'], [30, 3, 'S'], [32, 1, 'S'], [35, 3, 'N'], [55, 5, 'S'], [55, 5, 'S'], [56, 0, 'S'], [57, 0, 'S'], [86, 2, 'N'], [87, 2, 'N'], [131, 2, 'N']], 'dedupe': 3, 'max_gap': 30, 'price': [200, 150, 110, 75, 150, 200], 'trip_cap': 900}, [5, 1145]), ({'reads': [[24, 0, 'S'], [26, 0, 'S']], 'dedupe': 3, 'max_gap': 30, 'price': [150, 200, 150, 110, 200, 150], 'trip_cap': 600}, [1, 150]), ({'reads': [[40, 5, 'N'], [70, 1, 'N'], [73, 1, 'N'], [115, 0, 'N'], [116, 0, 'N'], [160, 3, 'S'], [165, 2, 'S'], [166, 1, 'S'], [171, 5, 'S'], [202, 3, 'S'], [207, 2, 'S']], 'dedupe': 3, 'max_gap': 20, 'price': [150, 200, 200, 75, 110, 75], 'trip_cap': 400}, [5, 1100]), ({'reads': [[4, 0, 'S'], [34, 3, 'S'], [35, 3, 'S'], [44, 2, 'S'], [49, 5, 'N']], 'dedupe': 3, 'max_gap': 30, 'price': [150, 150, 200, 200, 110, 110], 'trip_cap': 900}, [2, 660]), ({'reads': [[22, 3, 'S'], [121, 2, 'S'], [131, 5, 'S'], [76, 2, 'S'], [131, 5, 'S'], [25, 5, 'S'], [22, 3, 'S'], [142, 4, 'S'], [132, 2, 'S'], [56, 0, 'S'], [187, 5, 'N']], 'dedupe': 2, 'max_gap': 30, 'price': [200, 75, 75, 110, 200, 75], 'trip_cap': 400}, [4, 935])], [({'reads': [[34, 0, 'S'], [34, 0, 'S'], [64, 5, 'N'], [74, 1, 'S'], [75, 3, 'S'], [106, 0, 'S'], [106, 0, 'S'], [107, 5, 'S']], 'dedupe': 2, 'max_gap': 20, 'price': [110, 75, 200, 150, 110, 200], 'trip_cap': 900}, [4, 845]), ({'reads': [[0, 1, 'N'], [2, 1, 'N'], [4, 1, 'N'], [30, 2, 'N'], [55, 3, 'N']], 'dedupe': 2, 'max_gap': 30, 'price': [100, 150, 200, 250, 100, 100], 'trip_cap': 900}, [1, 750]), ({'reads': [[50, 2, 'N'], [51, 4, 'S'], [52, 4, 'S'], [96, 4, 'N'], [127, 1, 'S']], 'dedupe': 2, 'max_gap': 20, 'price': [150, 75, 75, 200, 200, 150], 'trip_cap': 400}, [4, 550]), ({'reads': [[97, 0, 'N'], [10, 2, 'S'], [94, 0, 'N'], [34, 5, 'N'], [64, 5, 'N'], [14, 4, 'S'], [11, 4, 'S'], [10, 2, 'S']], 'dedupe': 2, 'max_gap': 30, 'price': [75, 200, 110, 200, 150, 200], 'trip_cap': 900}, [2, 960]), ({'reads': [[21, 5, 'N'], [24, 5, 'N'], [12, 4, 'N'], [11, 4, 'N']], 'dedupe': 3, 'max_gap': 20, 'price': [75, 150, 150, 110, 150, 200], 'trip_cap': 400}, [1, 350]), ({'reads': [[24, 0, 'S'], [59, 2, 'S'], [29, 4, 'S'], [21, 0, 'S'], [60, 4, 'S'], [61, 2, 'S']], 'dedupe': 3, 'max_gap': 20, 'price': [75, 110, 150, 75, 200, 150], 'trip_cap': 900}, [2, 625]), ({'reads': [[13, 2, 'S'], [16, 2, 'S'], [44, 5, 'S'], [89, 0, 'S'], [134, 3, 'N'], [144, 0, 'S']], 'dedupe': 3, 'max_gap': 30, 'price': [110, 200, 110, 150, 110, 150], 'trip_cap': 400}, [5, 630]), ({'reads': [[10, 0, 'S'], [5, 4, 'N'], [12, 1, 'S'], [11, 0, 'S']], 'dedupe': 3, 'max_gap': 20, 'price': [100, 150, 200, 250, 100, 100], 'trip_cap': 400}, [2, 350])], [({'reads': [[38, 4, 'N'], [38, 4, 'N'], [44, 4, 'S'], [41, 1, 'S'], [74, 0, 'S'], [84, 3, 'S'], [77, 0, 'S']], 'dedupe': 2, 'max_gap': 20, 'price': [200, 110, 75, 150, 200, 110], 'trip_cap': 400}, [3, 910]), ({'reads': [[27, 1, 'N'], [57, 5, 'N'], [59, 5, 'N'], [88, 3, 'S'], [98, 1, 'S'], [118, 1, 'S'], [149, 4, 'S'], [152, 3, 'S'], [157, 2, 'S'], [188, 4, 'N']], 'dedupe': 3, 'max_gap': 20, 'price': [200, 200, 110, 110, 75, 200], 'trip_cap': 900}, [5, 1280]), ({'reads': [[13, 5, 'N'], [14, 5, 'N'], [33, 3, 'N'], [35, 3, 'N'], [63, 2, 'N'], [94, 0, 'N'], [99, 3, 'S'], [130, 5, 'S'], [132, 5, 'S'], [133, 1, 'N'], [153, 1, 'N']], 'dedupe': 2, 'max_gap': 20, 'price': [110, 150, 200, 75, 110, 75], 'trip_cap': 600}, [6, 910]), ({'reads': [[16, 2, 'N'], [26, 4, 'N'], [29, 4, 'N'], [29, 3, 'N'], [49, 2, 'N'], [79, 5, 'N']], 'dedupe': 3, 'max_gap': 30, 'price': [75, 75, 150, 150, 110, 150], 'trip_cap': 600}, [1, 600]), ({'reads': [[90, 0, 'N'], [83, 4, 'N'], [86, 5, 'N'], [94, 0, 'N'], [40, 1, 'S'], [89, 0, 'N'], [60, 3, 'N'], [114, 2, 'N'], [86, 5, 'N'], [80, 4, 'S']], 'dedupe': 3, 'max_gap': 30, 'price': [150, 150, 150, 110, 150, 110], 'trip_cap': 600}, [4, 970]), ({'reads': [[0, 1, 'N'], [2, 1, 'N'], [4, 1, 'N'], [30, 2, 'N'], [55, 3, 'N']], 'dedupe': 2, 'max_gap': 30, 'price': [100, 150, 200, 250, 100, 100], 'trip_cap': 900}, [1, 750]), ({'reads': [[13, 5, 'N'], [23, 2, 'N'], [43, 1, 'S'], [44, 1, 'S'], [73, 0, 'S'], [93, 5, 'S'], [94, 5, 'S']], 'dedupe': 3, 'max_gap': 30, 'price': [110, 75, 110, 110, 150, 110], 'trip_cap': 600}, [2, 515]), ({'reads': [[10, 0, 'S'], [5, 4, 'N'], [12, 1, 'S'], [11, 0, 'S']], 'dedupe': 3, 'max_gap': 20, 'price': [100, 150, 200, 250, 100, 100], 'trip_cap': 400}, [2, 350])], [({'reads': [[24, 5, 'S'], [24, 5, 'S']], 'dedupe': 3, 'max_gap': 30, 'price': [200, 75, 110, 200, 200, 75], 'trip_cap': 400}, [1, 75]), ({'reads': [[46, 4, 'S'], [49, 4, 'N'], [79, 0, 'N'], [84, 4, 'N'], [94, 0, 'S']], 'dedupe': 2, 'max_gap': 20, 'price': [75, 150, 200, 110, 75, 110], 'trip_cap': 400}, [4, 375]), ({'reads': [[10, 0, 'S'], [5, 4, 'N'], [12, 1, 'S'], [11, 0, 'S']], 'dedupe': 3, 'max_gap': 20, 'price': [100, 150, 200, 250, 100, 100], 'trip_cap': 400}, [2, 350]), ({'reads': [[6, 4, 'S'], [7, 4, 'S'], [37, 5, 'S'], [68, 1, 'S'], [113, 1, 'S'], [113, 1, 'S'], [143, 4, 'S'], [153, 0, 'S'], [156, 0, 'S']], 'dedupe': 2, 'max_gap': 20, 'price': [200, 110, 110, 75, 150, 110], 'trip_cap': 400}, [5, 880]), ({'reads': [[0, 1, 'N'], [2, 1, 'N'], [4, 1, 'N'], [30, 2, 'N'], [55, 3, 'N']], 'dedupe': 2, 'max_gap': 30, 'price': [100, 150, 200, 250, 100, 100], 'trip_cap': 900}, [1, 750]), ({'reads': [[27, 1, 'N'], [58, 0, 'N']], 'dedupe': 2, 'max_gap': 30, 'price': [200, 200, 200, 200, 110, 150], 'trip_cap': 400}, [2, 400]), ({'reads': [[33, 1, 'S'], [34, 1, 'S'], [34, 1, 'S'], [79, 2, 'S'], [80, 2, 'N'], [82, 2, 'N'], [125, 4, 'N'], [128, 4, 'N'], [148, 0, 'S']], 'dedupe': 2, 'max_gap': 30, 'price': [200, 75, 75, 75, 150, 150], 'trip_cap': 600}, [5, 725]), ({'reads': [[45, 4, 'S'], [65, 2, 'S'], [66, 2, 'S'], [75, 4, 'N'], [80, 0, 'N']], 'dedupe': 3, 'max_gap': 30, 'price': [150, 75, 200, 110, 150, 75], 'trip_cap': 400}, [2, 650])]]
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 0[2, 800][3, 800]Failed
fee oracle 1[4, 1510][5, 1510]Failed
fee oracle 2[2, 665][4, 665]Failed
fee oracle 3[1, 750][1, 750]Passed
fee oracle 4[1, 350][2, 350]Failed
fee oracle 5[1, 275][1, 275]Passed
fee oracle 6[1, 600][4, 900]Failed
fee oracle 7[2, 520][2, 520]Passed

SHA-256 / e520de71323f019942bb06807b6d42900ff6579474325b3e8df922f17ec45df1

2 / The unsuccessful fix

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

N = 1
observations = []
def solve(x):
    reads = sorted(x['reads'])
    kept = []
    lastg = {}
    for t, g, d in reads:
        if g in lastg and t - lastg[g] <= x['dedupe']:
            continue
        lastg[g] = t
        kept.append((t, g, d))
    trips = []
    for t, g, d in kept:
        if trips and (trips[-1]['d'] == d or len(trips[-1]['g']) == 1) and t - trips[-1]['t'] <= x['max_gap']:
            trips[-1]['g'].append(g)
            trips[-1]['t'] = t
        else:
            trips.append({'d': d, 't': t, 'g': [g]})
    total = sum(min(x['trip_cap'], sum(x['price'][g] for g in tr['g'])) for tr in trips)
    return [len(trips), total]
def check(label, actual, expected):
    observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
fixtures = [[({'reads': [[153, 0, 'S'], [163, 5, 'S'], [148, 1, 'S'], [63, 1, 'S'], [53, 5, 'N'], [103, 4, 'S'], [33, 5, 'N'], [73, 3, 'S']], 'dedupe': 3, 'max_gap': 30, 'price': [75, 75, 110, 150, 200, 75], 'trip_cap': 900}, [3, 800]), ({'reads': [[134, 0, 'S'], [28, 5, 'N'], [187, 3, 'S'], [83, 2, 'S'], [113, 4, 'S'], [186, 5, 'S'], [113, 4, 'S'], [133, 0, 'S'], [136, 5, 'S'], [166, 4, 'S'], [38, 1, 'S'], [136, 5, 'S']], 'dedupe': 2, 'max_gap': 20, 'price': [200, 110, 200, 150, 200, 150], 'trip_cap': 900}, [5, 1510]), ({'reads': [[65, 1, 'N'], [138, 1, 'S'], [35, 1, 'N'], [148, 1, 'S'], [135, 4, 'N'], [91, 4, 'S'], [148, 1, 'S'], [71, 4, 'N'], [70, 4, 'N'], [65, 1, 'N'], [90, 4, 'S']], 'dedupe': 3, 'max_gap': 30, 'price': [150, 110, 110, 150, 75, 150], 'trip_cap': 600}, [4, 665]), ({'reads': [[0, 1, 'N'], [2, 1, 'N'], [4, 1, 'N'], [30, 2, 'N'], [55, 3, 'N']], 'dedupe': 2, 'max_gap': 30, 'price': [100, 150, 200, 250, 100, 100], 'trip_cap': 900}, [1, 750]), ({'reads': [[10, 0, 'S'], [5, 4, 'N'], [12, 1, 'S'], [11, 0, 'S']], 'dedupe': 3, 'max_gap': 20, 'price': [100, 150, 200, 250, 100, 100], 'trip_cap': 400}, [2, 350]), ({'reads': [[5, 2, 'N'], [8, 2, 'N'], [8, 5, 'N']], 'dedupe': 3, 'max_gap': 20, 'price': [150, 75, 75, 110, 75, 200], 'trip_cap': 900}, [1, 275]), ({'reads': [[17, 5, 'N'], [18, 5, 'N'], [20, 4, 'S'], [20, 4, 'S'], [23, 5, 'S'], [26, 4, 'N'], [28, 4, 'N'], [46, 4, 'S'], [46, 4, 'S']], 'dedupe': 2, 'max_gap': 30, 'price': [75, 200, 150, 110, 200, 150], 'trip_cap': 600}, [4, 900]), ({'reads': [[40, 3, 'N'], [43, 3, 'N'], [45, 5, 'N'], [47, 5, 'N'], [75, 4, 'N'], [77, 4, 'N'], [105, 2, 'N'], [136, 0, 'N'], [136, 0, 'N']], 'dedupe': 3, 'max_gap': 30, 'price': [75, 200, 110, 75, 150, 110], 'trip_cap': 600}, [2, 520])], [({'reads': [[36, 3, 'N'], [39, 3, 'N'], [46, 2, 'S'], [47, 4, 'S'], [92, 0, 'S'], [102, 1, 'N'], [122, 3, 'N'], [125, 2, 'N'], [135, 2, 'N']], 'dedupe': 2, 'max_gap': 20, 'price': [75, 110, 150, 75, 150, 110], 'trip_cap': 400}, [4, 925]), ({'reads': [[0, 1, 'N'], [2, 1, 'N'], [4, 1, 'N'], [30, 2, 'N'], [55, 3, 'N']], 'dedupe': 2, 'max_gap': 30, 'price': [100, 150, 200, 250, 100, 100], 'trip_cap': 900}, [1, 750]), ({'reads': [[10, 0, 'S'], [5, 4, 'N'], [12, 1, 'S'], [11, 0, 'S']], 'dedupe': 3, 'max_gap': 20, 'price': [100, 150, 200, 250, 100, 100], 'trip_cap': 400}, [2, 350]), ({'reads': [[16, 3, 'S'], [26, 4, 'S'], [29, 3, 'S'], [30, 3, 'S'], [32, 1, 'S'], [35, 3, 'N'], [55, 5, 'S'], [55, 5, 'S'], [56, 0, 'S'], [57, 0, 'S'], [86, 2, 'N'], [87, 2, 'N'], [131, 2, 'N']], 'dedupe': 3, 'max_gap': 30, 'price': [200, 150, 110, 75, 150, 200], 'trip_cap': 900}, [5, 1145]), ({'reads': [[24, 0, 'S'], [26, 0, 'S']], 'dedupe': 3, 'max_gap': 30, 'price': [150, 200, 150, 110, 200, 150], 'trip_cap': 600}, [1, 150]), ({'reads': [[40, 5, 'N'], [70, 1, 'N'], [73, 1, 'N'], [115, 0, 'N'], [116, 0, 'N'], [160, 3, 'S'], [165, 2, 'S'], [166, 1, 'S'], [171, 5, 'S'], [202, 3, 'S'], [207, 2, 'S']], 'dedupe': 3, 'max_gap': 20, 'price': [150, 200, 200, 75, 110, 75], 'trip_cap': 400}, [5, 1100]), ({'reads': [[4, 0, 'S'], [34, 3, 'S'], [35, 3, 'S'], [44, 2, 'S'], [49, 5, 'N']], 'dedupe': 3, 'max_gap': 30, 'price': [150, 150, 200, 200, 110, 110], 'trip_cap': 900}, [2, 660]), ({'reads': [[22, 3, 'S'], [121, 2, 'S'], [131, 5, 'S'], [76, 2, 'S'], [131, 5, 'S'], [25, 5, 'S'], [22, 3, 'S'], [142, 4, 'S'], [132, 2, 'S'], [56, 0, 'S'], [187, 5, 'N']], 'dedupe': 2, 'max_gap': 30, 'price': [200, 75, 75, 110, 200, 75], 'trip_cap': 400}, [4, 935])], [({'reads': [[34, 0, 'S'], [34, 0, 'S'], [64, 5, 'N'], [74, 1, 'S'], [75, 3, 'S'], [106, 0, 'S'], [106, 0, 'S'], [107, 5, 'S']], 'dedupe': 2, 'max_gap': 20, 'price': [110, 75, 200, 150, 110, 200], 'trip_cap': 900}, [4, 845]), ({'reads': [[0, 1, 'N'], [2, 1, 'N'], [4, 1, 'N'], [30, 2, 'N'], [55, 3, 'N']], 'dedupe': 2, 'max_gap': 30, 'price': [100, 150, 200, 250, 100, 100], 'trip_cap': 900}, [1, 750]), ({'reads': [[50, 2, 'N'], [51, 4, 'S'], [52, 4, 'S'], [96, 4, 'N'], [127, 1, 'S']], 'dedupe': 2, 'max_gap': 20, 'price': [150, 75, 75, 200, 200, 150], 'trip_cap': 400}, [4, 550]), ({'reads': [[97, 0, 'N'], [10, 2, 'S'], [94, 0, 'N'], [34, 5, 'N'], [64, 5, 'N'], [14, 4, 'S'], [11, 4, 'S'], [10, 2, 'S']], 'dedupe': 2, 'max_gap': 30, 'price': [75, 200, 110, 200, 150, 200], 'trip_cap': 900}, [2, 960]), ({'reads': [[21, 5, 'N'], [24, 5, 'N'], [12, 4, 'N'], [11, 4, 'N']], 'dedupe': 3, 'max_gap': 20, 'price': [75, 150, 150, 110, 150, 200], 'trip_cap': 400}, [1, 350]), ({'reads': [[24, 0, 'S'], [59, 2, 'S'], [29, 4, 'S'], [21, 0, 'S'], [60, 4, 'S'], [61, 2, 'S']], 'dedupe': 3, 'max_gap': 20, 'price': [75, 110, 150, 75, 200, 150], 'trip_cap': 900}, [2, 625]), ({'reads': [[13, 2, 'S'], [16, 2, 'S'], [44, 5, 'S'], [89, 0, 'S'], [134, 3, 'N'], [144, 0, 'S']], 'dedupe': 3, 'max_gap': 30, 'price': [110, 200, 110, 150, 110, 150], 'trip_cap': 400}, [5, 630]), ({'reads': [[10, 0, 'S'], [5, 4, 'N'], [12, 1, 'S'], [11, 0, 'S']], 'dedupe': 3, 'max_gap': 20, 'price': [100, 150, 200, 250, 100, 100], 'trip_cap': 400}, [2, 350])], [({'reads': [[38, 4, 'N'], [38, 4, 'N'], [44, 4, 'S'], [41, 1, 'S'], [74, 0, 'S'], [84, 3, 'S'], [77, 0, 'S']], 'dedupe': 2, 'max_gap': 20, 'price': [200, 110, 75, 150, 200, 110], 'trip_cap': 400}, [3, 910]), ({'reads': [[27, 1, 'N'], [57, 5, 'N'], [59, 5, 'N'], [88, 3, 'S'], [98, 1, 'S'], [118, 1, 'S'], [149, 4, 'S'], [152, 3, 'S'], [157, 2, 'S'], [188, 4, 'N']], 'dedupe': 3, 'max_gap': 20, 'price': [200, 200, 110, 110, 75, 200], 'trip_cap': 900}, [5, 1280]), ({'reads': [[13, 5, 'N'], [14, 5, 'N'], [33, 3, 'N'], [35, 3, 'N'], [63, 2, 'N'], [94, 0, 'N'], [99, 3, 'S'], [130, 5, 'S'], [132, 5, 'S'], [133, 1, 'N'], [153, 1, 'N']], 'dedupe': 2, 'max_gap': 20, 'price': [110, 150, 200, 75, 110, 75], 'trip_cap': 600}, [6, 910]), ({'reads': [[16, 2, 'N'], [26, 4, 'N'], [29, 4, 'N'], [29, 3, 'N'], [49, 2, 'N'], [79, 5, 'N']], 'dedupe': 3, 'max_gap': 30, 'price': [75, 75, 150, 150, 110, 150], 'trip_cap': 600}, [1, 600]), ({'reads': [[90, 0, 'N'], [83, 4, 'N'], [86, 5, 'N'], [94, 0, 'N'], [40, 1, 'S'], [89, 0, 'N'], [60, 3, 'N'], [114, 2, 'N'], [86, 5, 'N'], [80, 4, 'S']], 'dedupe': 3, 'max_gap': 30, 'price': [150, 150, 150, 110, 150, 110], 'trip_cap': 600}, [4, 970]), ({'reads': [[0, 1, 'N'], [2, 1, 'N'], [4, 1, 'N'], [30, 2, 'N'], [55, 3, 'N']], 'dedupe': 2, 'max_gap': 30, 'price': [100, 150, 200, 250, 100, 100], 'trip_cap': 900}, [1, 750]), ({'reads': [[13, 5, 'N'], [23, 2, 'N'], [43, 1, 'S'], [44, 1, 'S'], [73, 0, 'S'], [93, 5, 'S'], [94, 5, 'S']], 'dedupe': 3, 'max_gap': 30, 'price': [110, 75, 110, 110, 150, 110], 'trip_cap': 600}, [2, 515]), ({'reads': [[10, 0, 'S'], [5, 4, 'N'], [12, 1, 'S'], [11, 0, 'S']], 'dedupe': 3, 'max_gap': 20, 'price': [100, 150, 200, 250, 100, 100], 'trip_cap': 400}, [2, 350])], [({'reads': [[24, 5, 'S'], [24, 5, 'S']], 'dedupe': 3, 'max_gap': 30, 'price': [200, 75, 110, 200, 200, 75], 'trip_cap': 400}, [1, 75]), ({'reads': [[46, 4, 'S'], [49, 4, 'N'], [79, 0, 'N'], [84, 4, 'N'], [94, 0, 'S']], 'dedupe': 2, 'max_gap': 20, 'price': [75, 150, 200, 110, 75, 110], 'trip_cap': 400}, [4, 375]), ({'reads': [[10, 0, 'S'], [5, 4, 'N'], [12, 1, 'S'], [11, 0, 'S']], 'dedupe': 3, 'max_gap': 20, 'price': [100, 150, 200, 250, 100, 100], 'trip_cap': 400}, [2, 350]), ({'reads': [[6, 4, 'S'], [7, 4, 'S'], [37, 5, 'S'], [68, 1, 'S'], [113, 1, 'S'], [113, 1, 'S'], [143, 4, 'S'], [153, 0, 'S'], [156, 0, 'S']], 'dedupe': 2, 'max_gap': 20, 'price': [200, 110, 110, 75, 150, 110], 'trip_cap': 400}, [5, 880]), ({'reads': [[0, 1, 'N'], [2, 1, 'N'], [4, 1, 'N'], [30, 2, 'N'], [55, 3, 'N']], 'dedupe': 2, 'max_gap': 30, 'price': [100, 150, 200, 250, 100, 100], 'trip_cap': 900}, [1, 750]), ({'reads': [[27, 1, 'N'], [58, 0, 'N']], 'dedupe': 2, 'max_gap': 30, 'price': [200, 200, 200, 200, 110, 150], 'trip_cap': 400}, [2, 400]), ({'reads': [[33, 1, 'S'], [34, 1, 'S'], [34, 1, 'S'], [79, 2, 'S'], [80, 2, 'N'], [82, 2, 'N'], [125, 4, 'N'], [128, 4, 'N'], [148, 0, 'S']], 'dedupe': 2, 'max_gap': 30, 'price': [200, 75, 75, 75, 150, 150], 'trip_cap': 600}, [5, 725]), ({'reads': [[45, 4, 'S'], [65, 2, 'S'], [66, 2, 'S'], [75, 4, 'N'], [80, 0, 'N']], 'dedupe': 3, 'max_gap': 30, 'price': [150, 75, 200, 110, 150, 75], 'trip_cap': 400}, [2, 650])]]
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 0[3, 800][3, 800]Passed
fee oracle 1[4, 1510][5, 1510]Failed
fee oracle 2[4, 665][4, 665]Passed
fee oracle 3[1, 750][1, 750]Passed
fee oracle 4[2, 350][2, 350]Passed
fee oracle 5[1, 275][1, 275]Passed
fee oracle 6[2, 900][4, 900]Failed
fee oracle 7[2, 520][2, 520]Passed

SHA-256 / 4e1742b9192c41c583483cdc53f880609134a59cc4364a2d1092ca50e7a849ab

3 / The verified repair

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

N = 1
observations = []
def solve(x):
    reads = sorted(x['reads'])
    kept = []
    lastg = {}
    for t, g, d in reads:
        if g in lastg and t - lastg[g] <= x['dedupe']:
            continue
        lastg[g] = t
        kept.append((t, g, d))
    trips = []
    for t, g, d in kept:
        if trips and trips[-1]['d'] == d and t - trips[-1]['t'] <= x['max_gap']:
            trips[-1]['g'].append(g)
            trips[-1]['t'] = t
        else:
            trips.append({'d': d, 't': t, 'g': [g]})
    total = sum(min(x['trip_cap'], sum(x['price'][g] for g in tr['g'])) for tr in trips)
    return [len(trips), total]
def check(label, actual, expected):
    observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
fixtures = [[({'reads': [[153, 0, 'S'], [163, 5, 'S'], [148, 1, 'S'], [63, 1, 'S'], [53, 5, 'N'], [103, 4, 'S'], [33, 5, 'N'], [73, 3, 'S']], 'dedupe': 3, 'max_gap': 30, 'price': [75, 75, 110, 150, 200, 75], 'trip_cap': 900}, [3, 800]), ({'reads': [[134, 0, 'S'], [28, 5, 'N'], [187, 3, 'S'], [83, 2, 'S'], [113, 4, 'S'], [186, 5, 'S'], [113, 4, 'S'], [133, 0, 'S'], [136, 5, 'S'], [166, 4, 'S'], [38, 1, 'S'], [136, 5, 'S']], 'dedupe': 2, 'max_gap': 20, 'price': [200, 110, 200, 150, 200, 150], 'trip_cap': 900}, [5, 1510]), ({'reads': [[65, 1, 'N'], [138, 1, 'S'], [35, 1, 'N'], [148, 1, 'S'], [135, 4, 'N'], [91, 4, 'S'], [148, 1, 'S'], [71, 4, 'N'], [70, 4, 'N'], [65, 1, 'N'], [90, 4, 'S']], 'dedupe': 3, 'max_gap': 30, 'price': [150, 110, 110, 150, 75, 150], 'trip_cap': 600}, [4, 665]), ({'reads': [[0, 1, 'N'], [2, 1, 'N'], [4, 1, 'N'], [30, 2, 'N'], [55, 3, 'N']], 'dedupe': 2, 'max_gap': 30, 'price': [100, 150, 200, 250, 100, 100], 'trip_cap': 900}, [1, 750]), ({'reads': [[10, 0, 'S'], [5, 4, 'N'], [12, 1, 'S'], [11, 0, 'S']], 'dedupe': 3, 'max_gap': 20, 'price': [100, 150, 200, 250, 100, 100], 'trip_cap': 400}, [2, 350]), ({'reads': [[5, 2, 'N'], [8, 2, 'N'], [8, 5, 'N']], 'dedupe': 3, 'max_gap': 20, 'price': [150, 75, 75, 110, 75, 200], 'trip_cap': 900}, [1, 275]), ({'reads': [[17, 5, 'N'], [18, 5, 'N'], [20, 4, 'S'], [20, 4, 'S'], [23, 5, 'S'], [26, 4, 'N'], [28, 4, 'N'], [46, 4, 'S'], [46, 4, 'S']], 'dedupe': 2, 'max_gap': 30, 'price': [75, 200, 150, 110, 200, 150], 'trip_cap': 600}, [4, 900]), ({'reads': [[40, 3, 'N'], [43, 3, 'N'], [45, 5, 'N'], [47, 5, 'N'], [75, 4, 'N'], [77, 4, 'N'], [105, 2, 'N'], [136, 0, 'N'], [136, 0, 'N']], 'dedupe': 3, 'max_gap': 30, 'price': [75, 200, 110, 75, 150, 110], 'trip_cap': 600}, [2, 520])], [({'reads': [[36, 3, 'N'], [39, 3, 'N'], [46, 2, 'S'], [47, 4, 'S'], [92, 0, 'S'], [102, 1, 'N'], [122, 3, 'N'], [125, 2, 'N'], [135, 2, 'N']], 'dedupe': 2, 'max_gap': 20, 'price': [75, 110, 150, 75, 150, 110], 'trip_cap': 400}, [4, 925]), ({'reads': [[0, 1, 'N'], [2, 1, 'N'], [4, 1, 'N'], [30, 2, 'N'], [55, 3, 'N']], 'dedupe': 2, 'max_gap': 30, 'price': [100, 150, 200, 250, 100, 100], 'trip_cap': 900}, [1, 750]), ({'reads': [[10, 0, 'S'], [5, 4, 'N'], [12, 1, 'S'], [11, 0, 'S']], 'dedupe': 3, 'max_gap': 20, 'price': [100, 150, 200, 250, 100, 100], 'trip_cap': 400}, [2, 350]), ({'reads': [[16, 3, 'S'], [26, 4, 'S'], [29, 3, 'S'], [30, 3, 'S'], [32, 1, 'S'], [35, 3, 'N'], [55, 5, 'S'], [55, 5, 'S'], [56, 0, 'S'], [57, 0, 'S'], [86, 2, 'N'], [87, 2, 'N'], [131, 2, 'N']], 'dedupe': 3, 'max_gap': 30, 'price': [200, 150, 110, 75, 150, 200], 'trip_cap': 900}, [5, 1145]), ({'reads': [[24, 0, 'S'], [26, 0, 'S']], 'dedupe': 3, 'max_gap': 30, 'price': [150, 200, 150, 110, 200, 150], 'trip_cap': 600}, [1, 150]), ({'reads': [[40, 5, 'N'], [70, 1, 'N'], [73, 1, 'N'], [115, 0, 'N'], [116, 0, 'N'], [160, 3, 'S'], [165, 2, 'S'], [166, 1, 'S'], [171, 5, 'S'], [202, 3, 'S'], [207, 2, 'S']], 'dedupe': 3, 'max_gap': 20, 'price': [150, 200, 200, 75, 110, 75], 'trip_cap': 400}, [5, 1100]), ({'reads': [[4, 0, 'S'], [34, 3, 'S'], [35, 3, 'S'], [44, 2, 'S'], [49, 5, 'N']], 'dedupe': 3, 'max_gap': 30, 'price': [150, 150, 200, 200, 110, 110], 'trip_cap': 900}, [2, 660]), ({'reads': [[22, 3, 'S'], [121, 2, 'S'], [131, 5, 'S'], [76, 2, 'S'], [131, 5, 'S'], [25, 5, 'S'], [22, 3, 'S'], [142, 4, 'S'], [132, 2, 'S'], [56, 0, 'S'], [187, 5, 'N']], 'dedupe': 2, 'max_gap': 30, 'price': [200, 75, 75, 110, 200, 75], 'trip_cap': 400}, [4, 935])], [({'reads': [[34, 0, 'S'], [34, 0, 'S'], [64, 5, 'N'], [74, 1, 'S'], [75, 3, 'S'], [106, 0, 'S'], [106, 0, 'S'], [107, 5, 'S']], 'dedupe': 2, 'max_gap': 20, 'price': [110, 75, 200, 150, 110, 200], 'trip_cap': 900}, [4, 845]), ({'reads': [[0, 1, 'N'], [2, 1, 'N'], [4, 1, 'N'], [30, 2, 'N'], [55, 3, 'N']], 'dedupe': 2, 'max_gap': 30, 'price': [100, 150, 200, 250, 100, 100], 'trip_cap': 900}, [1, 750]), ({'reads': [[50, 2, 'N'], [51, 4, 'S'], [52, 4, 'S'], [96, 4, 'N'], [127, 1, 'S']], 'dedupe': 2, 'max_gap': 20, 'price': [150, 75, 75, 200, 200, 150], 'trip_cap': 400}, [4, 550]), ({'reads': [[97, 0, 'N'], [10, 2, 'S'], [94, 0, 'N'], [34, 5, 'N'], [64, 5, 'N'], [14, 4, 'S'], [11, 4, 'S'], [10, 2, 'S']], 'dedupe': 2, 'max_gap': 30, 'price': [75, 200, 110, 200, 150, 200], 'trip_cap': 900}, [2, 960]), ({'reads': [[21, 5, 'N'], [24, 5, 'N'], [12, 4, 'N'], [11, 4, 'N']], 'dedupe': 3, 'max_gap': 20, 'price': [75, 150, 150, 110, 150, 200], 'trip_cap': 400}, [1, 350]), ({'reads': [[24, 0, 'S'], [59, 2, 'S'], [29, 4, 'S'], [21, 0, 'S'], [60, 4, 'S'], [61, 2, 'S']], 'dedupe': 3, 'max_gap': 20, 'price': [75, 110, 150, 75, 200, 150], 'trip_cap': 900}, [2, 625]), ({'reads': [[13, 2, 'S'], [16, 2, 'S'], [44, 5, 'S'], [89, 0, 'S'], [134, 3, 'N'], [144, 0, 'S']], 'dedupe': 3, 'max_gap': 30, 'price': [110, 200, 110, 150, 110, 150], 'trip_cap': 400}, [5, 630]), ({'reads': [[10, 0, 'S'], [5, 4, 'N'], [12, 1, 'S'], [11, 0, 'S']], 'dedupe': 3, 'max_gap': 20, 'price': [100, 150, 200, 250, 100, 100], 'trip_cap': 400}, [2, 350])], [({'reads': [[38, 4, 'N'], [38, 4, 'N'], [44, 4, 'S'], [41, 1, 'S'], [74, 0, 'S'], [84, 3, 'S'], [77, 0, 'S']], 'dedupe': 2, 'max_gap': 20, 'price': [200, 110, 75, 150, 200, 110], 'trip_cap': 400}, [3, 910]), ({'reads': [[27, 1, 'N'], [57, 5, 'N'], [59, 5, 'N'], [88, 3, 'S'], [98, 1, 'S'], [118, 1, 'S'], [149, 4, 'S'], [152, 3, 'S'], [157, 2, 'S'], [188, 4, 'N']], 'dedupe': 3, 'max_gap': 20, 'price': [200, 200, 110, 110, 75, 200], 'trip_cap': 900}, [5, 1280]), ({'reads': [[13, 5, 'N'], [14, 5, 'N'], [33, 3, 'N'], [35, 3, 'N'], [63, 2, 'N'], [94, 0, 'N'], [99, 3, 'S'], [130, 5, 'S'], [132, 5, 'S'], [133, 1, 'N'], [153, 1, 'N']], 'dedupe': 2, 'max_gap': 20, 'price': [110, 150, 200, 75, 110, 75], 'trip_cap': 600}, [6, 910]), ({'reads': [[16, 2, 'N'], [26, 4, 'N'], [29, 4, 'N'], [29, 3, 'N'], [49, 2, 'N'], [79, 5, 'N']], 'dedupe': 3, 'max_gap': 30, 'price': [75, 75, 150, 150, 110, 150], 'trip_cap': 600}, [1, 600]), ({'reads': [[90, 0, 'N'], [83, 4, 'N'], [86, 5, 'N'], [94, 0, 'N'], [40, 1, 'S'], [89, 0, 'N'], [60, 3, 'N'], [114, 2, 'N'], [86, 5, 'N'], [80, 4, 'S']], 'dedupe': 3, 'max_gap': 30, 'price': [150, 150, 150, 110, 150, 110], 'trip_cap': 600}, [4, 970]), ({'reads': [[0, 1, 'N'], [2, 1, 'N'], [4, 1, 'N'], [30, 2, 'N'], [55, 3, 'N']], 'dedupe': 2, 'max_gap': 30, 'price': [100, 150, 200, 250, 100, 100], 'trip_cap': 900}, [1, 750]), ({'reads': [[13, 5, 'N'], [23, 2, 'N'], [43, 1, 'S'], [44, 1, 'S'], [73, 0, 'S'], [93, 5, 'S'], [94, 5, 'S']], 'dedupe': 3, 'max_gap': 30, 'price': [110, 75, 110, 110, 150, 110], 'trip_cap': 600}, [2, 515]), ({'reads': [[10, 0, 'S'], [5, 4, 'N'], [12, 1, 'S'], [11, 0, 'S']], 'dedupe': 3, 'max_gap': 20, 'price': [100, 150, 200, 250, 100, 100], 'trip_cap': 400}, [2, 350])], [({'reads': [[24, 5, 'S'], [24, 5, 'S']], 'dedupe': 3, 'max_gap': 30, 'price': [200, 75, 110, 200, 200, 75], 'trip_cap': 400}, [1, 75]), ({'reads': [[46, 4, 'S'], [49, 4, 'N'], [79, 0, 'N'], [84, 4, 'N'], [94, 0, 'S']], 'dedupe': 2, 'max_gap': 20, 'price': [75, 150, 200, 110, 75, 110], 'trip_cap': 400}, [4, 375]), ({'reads': [[10, 0, 'S'], [5, 4, 'N'], [12, 1, 'S'], [11, 0, 'S']], 'dedupe': 3, 'max_gap': 20, 'price': [100, 150, 200, 250, 100, 100], 'trip_cap': 400}, [2, 350]), ({'reads': [[6, 4, 'S'], [7, 4, 'S'], [37, 5, 'S'], [68, 1, 'S'], [113, 1, 'S'], [113, 1, 'S'], [143, 4, 'S'], [153, 0, 'S'], [156, 0, 'S']], 'dedupe': 2, 'max_gap': 20, 'price': [200, 110, 110, 75, 150, 110], 'trip_cap': 400}, [5, 880]), ({'reads': [[0, 1, 'N'], [2, 1, 'N'], [4, 1, 'N'], [30, 2, 'N'], [55, 3, 'N']], 'dedupe': 2, 'max_gap': 30, 'price': [100, 150, 200, 250, 100, 100], 'trip_cap': 900}, [1, 750]), ({'reads': [[27, 1, 'N'], [58, 0, 'N']], 'dedupe': 2, 'max_gap': 30, 'price': [200, 200, 200, 200, 110, 150], 'trip_cap': 400}, [2, 400]), ({'reads': [[33, 1, 'S'], [34, 1, 'S'], [34, 1, 'S'], [79, 2, 'S'], [80, 2, 'N'], [82, 2, 'N'], [125, 4, 'N'], [128, 4, 'N'], [148, 0, 'S']], 'dedupe': 2, 'max_gap': 30, 'price': [200, 75, 75, 75, 150, 150], 'trip_cap': 600}, [5, 725]), ({'reads': [[45, 4, 'S'], [65, 2, 'S'], [66, 2, 'S'], [75, 4, 'N'], [80, 0, 'N']], 'dedupe': 3, 'max_gap': 30, 'price': [150, 75, 200, 110, 150, 75], 'trip_cap': 400}, [2, 650])]]
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 0[3, 800][3, 800]Passed
fee oracle 1[5, 1510][5, 1510]Passed
fee oracle 2[4, 665][4, 665]Passed
fee oracle 3[1, 750][1, 750]Passed
fee oracle 4[2, 350][2, 350]Passed
fee oracle 5[1, 275][1, 275]Passed
fee oracle 6[4, 900][4, 900]Passed
fee oracle 7[2, 520][2, 520]Passed

SHA-256 / 0d61a0caf597e486fa6047fa6d85ca04d11da68a4f30a0e06af00cc93159ddb2

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

Case digest / 248887bb56f73d7b2ab104a9f0a8d03b302c23e55d2edcfe7b87f8fa1aa1815c