FAILURE MAP
← Case archive

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

Open-road gantry trip building: the trip gap is measured from the first read of the trip · case 01

Open-road gantry trip building returns a wrong result when the trip gap is measured from the first read of the trip.

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

ROOT CAUSE

Long multi-gantry trips are split because the gap is measured from trip start.

VERIFIED REPAIR

Restore the trip gap anchor rule so that the step reads `trips[-1]['t'] = t`.

Unsuccessful approach: Advancing the anchor only partially still splits long trips.

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 trips[-1]['d'] == d and t - trips[-1]['t'] <= x['max_gap']:
            trips[-1]['g'].append(g)
        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': [[38, 3, 'S'], [41, 3, 'S'], [48, 4, 'S'], [68, 2, 'S'], [78, 0, 'S']], 'dedupe': 3, 'max_gap': 30, 'price': [150, 110, 110, 110, 200, 75], 'trip_cap': 900}, [1, 570]), ({'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': [[8, 1, 'S']], 'dedupe': 3, 'max_gap': 30, 'price': [200, 75, 75, 150, 75, 150], 'trip_cap': 400}, [1, 75]), ({'reads': [[45, 4, 'S'], [48, 4, 'S'], [48, 3, 'S']], 'dedupe': 3, 'max_gap': 30, 'price': [110, 150, 200, 75, 200, 200], 'trip_cap': 900}, [1, 275]), ({'reads': [[39, 2, 'S'], [59, 0, 'S'], [104, 4, 'S'], [114, 3, 'S'], [145, 5, 'N']], 'dedupe': 3, 'max_gap': 30, 'price': [75, 75, 110, 110, 150, 110], 'trip_cap': 900}, [3, 555]), ({'reads': [[35, 2, 'S'], [32, 2, 'S'], [12, 5, 'S'], [4, 3, 'S'], [7, 4, 'S']], 'dedupe': 3, 'max_gap': 20, 'price': [110, 75, 110, 150, 150, 200], 'trip_cap': 600}, [1, 600]), ({'reads': [[32, 3, 'N'], [87, 5, 'N'], [107, 5, 'N'], [153, 3, 'S'], [2, 4, 'S'], [77, 1, 'N'], [152, 3, 'N'], [89, 5, 'N']], 'dedupe': 3, 'max_gap': 20, 'price': [110, 110, 150, 200, 75, 110], 'trip_cap': 600}, [4, 805]), ({'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': [[132, 4, 'N'], [82, 0, 'N'], [133, 5, 'N'], [72, 2, 'N'], [102, 1, 'N'], [22, 5, 'N'], [27, 3, 'N'], [17, 3, 'N'], [72, 2, 'N']], 'dedupe': 2, 'max_gap': 20, 'price': [110, 110, 75, 200, 110, 110], 'trip_cap': 600}, [3, 1025]), ({'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': [[8, 1, 'N'], [203, 3, 'S'], [128, 0, 'S'], [83, 0, 'S'], [175, 1, 'S'], [173, 1, 'S'], [53, 0, 'S']], 'dedupe': 2, 'max_gap': 20, 'price': [150, 75, 200, 200, 150, 150], 'trip_cap': 600}, [6, 800]), ({'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': [[38, 0, 'N'], [40, 0, 'N']], 'dedupe': 2, 'max_gap': 30, 'price': [200, 110, 200, 75, 150, 150], 'trip_cap': 600}, [1, 200]), ({'reads': [[62, 3, 'N'], [115, 3, 'N'], [64, 0, 'N'], [145, 4, 'N'], [42, 1, 'N'], [64, 3, 'N'], [37, 5, 'N'], [84, 4, 'N'], [114, 1, 'N'], [63, 5, 'N'], [116, 1, 'N']], 'dedupe': 3, 'max_gap': 30, 'price': [75, 75, 150, 150, 110, 110], 'trip_cap': 900}, [1, 900]), ({'reads': [[37, 5, 'S'], [67, 2, 'S'], [72, 0, 'S'], [75, 0, 'S'], [103, 1, 'S'], [123, 4, 'S'], [153, 5, 'S'], [184, 4, 'S'], [204, 3, 'S'], [204, 3, 'S']], 'dedupe': 3, 'max_gap': 20, 'price': [75, 200, 200, 75, 110, 150], 'trip_cap': 400}, [5, 1070]), ({'reads': [[16, 4, 'S'], [26, 4, 'S'], [123, 3, 'S'], [51, 4, 'S'], [87, 3, 'S'], [118, 4, 'S'], [46, 1, 'S'], [56, 2, 'S']], 'dedupe': 3, 'max_gap': 30, 'price': [75, 150, 150, 110, 200, 110], 'trip_cap': 400}, [3, 820])], [({'reads': [[31, 2, 'S'], [61, 0, 'S'], [64, 0, 'S']], 'dedupe': 3, 'max_gap': 20, 'price': [110, 150, 200, 200, 150, 110], 'trip_cap': 900}, [2, 310]), ({'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': [[0, 3, 'S'], [45, 2, 'S'], [50, 1, 'S'], [70, 2, 'S'], [100, 2, 'S'], [101, 2, 'S'], [131, 1, 'S'], [132, 1, 'S']], 'dedupe': 2, 'max_gap': 20, 'price': [150, 200, 150, 200, 150, 200], 'trip_cap': 900}, [4, 1050]), ({'reads': [[18, 3, 'N']], 'dedupe': 2, 'max_gap': 20, 'price': [75, 75, 150, 75, 200, 110], 'trip_cap': 900}, [1, 75]), ({'reads': [[33, 1, 'S'], [118, 1, 'S'], [141, 2, 'S'], [64, 5, 'S'], [84, 5, 'S'], [64, 5, 'S'], [115, 0, 'S'], [121, 5, 'S'], [124, 5, 'S']], 'dedupe': 2, 'max_gap': 20, 'price': [200, 110, 200, 200, 150, 75], 'trip_cap': 900}, [3, 920]), ({'reads': [[12, 5, 'S']], 'dedupe': 3, 'max_gap': 30, 'price': [150, 110, 110, 150, 75, 110], 'trip_cap': 900}, [1, 110]), ({'reads': [[35, 0, 'N'], [65, 3, 'S'], [68, 3, 'S'], [68, 1, 'S'], [68, 1, 'S'], [99, 4, 'S'], [102, 1, 'N'], [104, 1, 'N'], [122, 5, 'N'], [132, 2, 'S'], [135, 5, 'S'], [136, 4, 'S']], 'dedupe': 3, 'max_gap': 30, 'price': [110, 200, 75, 75, 75, 75], 'trip_cap': 400}, [5, 960]), ({'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': [[26, 2, 'N'], [112, 0, 'N'], [31, 2, 'N'], [28, 2, 'N'], [62, 0, 'N'], [44, 5, 'N'], [25, 5, 'N'], [157, 4, 'N'], [107, 3, 'N'], [41, 5, 'N'], [42, 5, 'N']], 'dedupe': 2, 'max_gap': 20, 'price': [200, 75, 150, 110, 110, 150], 'trip_cap': 400}, [3, 820]), ({'reads': [[30, 1, 'N'], [75, 3, 'N'], [78, 0, 'N'], [80, 0, 'N'], [79, 4, 'N']], 'dedupe': 2, 'max_gap': 20, 'price': [150, 110, 75, 150, 75, 150], 'trip_cap': 400}, [2, 485]), ({'reads': [[36, 5, 'S']], 'dedupe': 2, 'max_gap': 20, 'price': [110, 200, 75, 75, 200, 110], 'trip_cap': 600}, [1, 110]), ({'reads': [[2, 4, 'N'], [32, 3, 'S'], [77, 4, 'N'], [122, 4, 'S'], [124, 4, 'S']], 'dedupe': 3, 'max_gap': 30, 'price': [75, 200, 200, 150, 150, 150], 'trip_cap': 900}, [4, 600]), ({'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': [[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, 5, 'N'], [81, 4, 'N'], [84, 4, 'N'], [112, 1, 'N'], [115, 1, 'N'], [115, 2, 'N'], [120, 4, 'S'], [121, 4, 'S'], [122, 4, 'S']], 'dedupe': 2, 'max_gap': 30, 'price': [150, 75, 75, 150, 150, 75], 'trip_cap': 900}, [3, 750]), ({'reads': [[37, 5, 'N'], [42, 2, 'N'], [43, 3, 'N'], [46, 3, 'S'], [48, 3, 'S'], [49, 5, 'N']], 'dedupe': 3, 'max_gap': 20, 'price': [110, 150, 110, 110, 110, 150], 'trip_cap': 900}, [3, 630])], [({'reads': [[1, 5, 'N'], [3, 5, 'N'], [2, 2, 'N'], [5, 4, 'N'], [8, 4, 'N'], [10, 4, 'N'], [10, 4, 'N']], 'dedupe': 2, 'max_gap': 30, 'price': [150, 110, 75, 110, 200, 200], 'trip_cap': 600}, [1, 600]), ({'reads': [[23, 2, 'S'], [26, 1, 'S']], 'dedupe': 2, 'max_gap': 20, 'price': [150, 75, 150, 75, 200, 110], 'trip_cap': 900}, [1, 225]), ({'reads': [[24, 0, 'S']], 'dedupe': 2, 'max_gap': 20, 'price': [110, 200, 150, 200, 110, 110], 'trip_cap': 900}, [1, 110]), ({'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': [[47, 2, 'S'], [37, 4, 'S'], [7, 1, 'S'], [50, 2, 'S'], [8, 1, 'S'], [77, 4, 'N']], 'dedupe': 2, 'max_gap': 30, 'price': [110, 110, 200, 150, 150, 110], 'trip_cap': 900}, [2, 810]), ({'reads': [[94, 4, 'S'], [42, 1, 'S'], [55, 1, 'N'], [89, 1, 'S'], [124, 5, 'S'], [172, 2, 'N'], [45, 2, 'S'], [127, 1, 'S'], [86, 1, 'S']], 'dedupe': 3, 'max_gap': 30, 'price': [110, 200, 200, 75, 150, 110], 'trip_cap': 900}, [4, 1460]), ({'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': [[47, 0, 'N'], [48, 0, 'N'], [78, 2, 'N'], [83, 1, 'S'], [84, 4, 'N'], [85, 5, 'N'], [130, 1, 'N'], [161, 0, 'N']], 'dedupe': 3, 'max_gap': 20, 'price': [150, 75, 75, 110, 110, 75], 'trip_cap': 900}, [6, 710])]]
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, 570][1, 570]Failed
fee oracle 1[2, 750][1, 750]Failed
fee oracle 2[1, 75][1, 75]Passed
fee oracle 3[1, 275][1, 275]Passed
fee oracle 4[3, 555][3, 555]Passed
fee oracle 5[2, 610][1, 600]Failed
fee oracle 6[5, 805][4, 805]Failed
fee oracle 7[2, 350][2, 350]Passed

SHA-256 / 66106fc831ac5a54f5e10baf30c8bbfc0caac62dbe509be060bc1324e45ce35f

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 and t - trips[-1]['t'] <= x['max_gap']:
            trips[-1]['g'].append(g)
            trips[-1]['t'] = max(trips[-1]['t'], t - x['max_gap'] // 2)
        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': [[38, 3, 'S'], [41, 3, 'S'], [48, 4, 'S'], [68, 2, 'S'], [78, 0, 'S']], 'dedupe': 3, 'max_gap': 30, 'price': [150, 110, 110, 110, 200, 75], 'trip_cap': 900}, [1, 570]), ({'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': [[8, 1, 'S']], 'dedupe': 3, 'max_gap': 30, 'price': [200, 75, 75, 150, 75, 150], 'trip_cap': 400}, [1, 75]), ({'reads': [[45, 4, 'S'], [48, 4, 'S'], [48, 3, 'S']], 'dedupe': 3, 'max_gap': 30, 'price': [110, 150, 200, 75, 200, 200], 'trip_cap': 900}, [1, 275]), ({'reads': [[39, 2, 'S'], [59, 0, 'S'], [104, 4, 'S'], [114, 3, 'S'], [145, 5, 'N']], 'dedupe': 3, 'max_gap': 30, 'price': [75, 75, 110, 110, 150, 110], 'trip_cap': 900}, [3, 555]), ({'reads': [[35, 2, 'S'], [32, 2, 'S'], [12, 5, 'S'], [4, 3, 'S'], [7, 4, 'S']], 'dedupe': 3, 'max_gap': 20, 'price': [110, 75, 110, 150, 150, 200], 'trip_cap': 600}, [1, 600]), ({'reads': [[32, 3, 'N'], [87, 5, 'N'], [107, 5, 'N'], [153, 3, 'S'], [2, 4, 'S'], [77, 1, 'N'], [152, 3, 'N'], [89, 5, 'N']], 'dedupe': 3, 'max_gap': 20, 'price': [110, 110, 150, 200, 75, 110], 'trip_cap': 600}, [4, 805]), ({'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': [[132, 4, 'N'], [82, 0, 'N'], [133, 5, 'N'], [72, 2, 'N'], [102, 1, 'N'], [22, 5, 'N'], [27, 3, 'N'], [17, 3, 'N'], [72, 2, 'N']], 'dedupe': 2, 'max_gap': 20, 'price': [110, 110, 75, 200, 110, 110], 'trip_cap': 600}, [3, 1025]), ({'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': [[8, 1, 'N'], [203, 3, 'S'], [128, 0, 'S'], [83, 0, 'S'], [175, 1, 'S'], [173, 1, 'S'], [53, 0, 'S']], 'dedupe': 2, 'max_gap': 20, 'price': [150, 75, 200, 200, 150, 150], 'trip_cap': 600}, [6, 800]), ({'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': [[38, 0, 'N'], [40, 0, 'N']], 'dedupe': 2, 'max_gap': 30, 'price': [200, 110, 200, 75, 150, 150], 'trip_cap': 600}, [1, 200]), ({'reads': [[62, 3, 'N'], [115, 3, 'N'], [64, 0, 'N'], [145, 4, 'N'], [42, 1, 'N'], [64, 3, 'N'], [37, 5, 'N'], [84, 4, 'N'], [114, 1, 'N'], [63, 5, 'N'], [116, 1, 'N']], 'dedupe': 3, 'max_gap': 30, 'price': [75, 75, 150, 150, 110, 110], 'trip_cap': 900}, [1, 900]), ({'reads': [[37, 5, 'S'], [67, 2, 'S'], [72, 0, 'S'], [75, 0, 'S'], [103, 1, 'S'], [123, 4, 'S'], [153, 5, 'S'], [184, 4, 'S'], [204, 3, 'S'], [204, 3, 'S']], 'dedupe': 3, 'max_gap': 20, 'price': [75, 200, 200, 75, 110, 150], 'trip_cap': 400}, [5, 1070]), ({'reads': [[16, 4, 'S'], [26, 4, 'S'], [123, 3, 'S'], [51, 4, 'S'], [87, 3, 'S'], [118, 4, 'S'], [46, 1, 'S'], [56, 2, 'S']], 'dedupe': 3, 'max_gap': 30, 'price': [75, 150, 150, 110, 200, 110], 'trip_cap': 400}, [3, 820])], [({'reads': [[31, 2, 'S'], [61, 0, 'S'], [64, 0, 'S']], 'dedupe': 3, 'max_gap': 20, 'price': [110, 150, 200, 200, 150, 110], 'trip_cap': 900}, [2, 310]), ({'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': [[0, 3, 'S'], [45, 2, 'S'], [50, 1, 'S'], [70, 2, 'S'], [100, 2, 'S'], [101, 2, 'S'], [131, 1, 'S'], [132, 1, 'S']], 'dedupe': 2, 'max_gap': 20, 'price': [150, 200, 150, 200, 150, 200], 'trip_cap': 900}, [4, 1050]), ({'reads': [[18, 3, 'N']], 'dedupe': 2, 'max_gap': 20, 'price': [75, 75, 150, 75, 200, 110], 'trip_cap': 900}, [1, 75]), ({'reads': [[33, 1, 'S'], [118, 1, 'S'], [141, 2, 'S'], [64, 5, 'S'], [84, 5, 'S'], [64, 5, 'S'], [115, 0, 'S'], [121, 5, 'S'], [124, 5, 'S']], 'dedupe': 2, 'max_gap': 20, 'price': [200, 110, 200, 200, 150, 75], 'trip_cap': 900}, [3, 920]), ({'reads': [[12, 5, 'S']], 'dedupe': 3, 'max_gap': 30, 'price': [150, 110, 110, 150, 75, 110], 'trip_cap': 900}, [1, 110]), ({'reads': [[35, 0, 'N'], [65, 3, 'S'], [68, 3, 'S'], [68, 1, 'S'], [68, 1, 'S'], [99, 4, 'S'], [102, 1, 'N'], [104, 1, 'N'], [122, 5, 'N'], [132, 2, 'S'], [135, 5, 'S'], [136, 4, 'S']], 'dedupe': 3, 'max_gap': 30, 'price': [110, 200, 75, 75, 75, 75], 'trip_cap': 400}, [5, 960]), ({'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': [[26, 2, 'N'], [112, 0, 'N'], [31, 2, 'N'], [28, 2, 'N'], [62, 0, 'N'], [44, 5, 'N'], [25, 5, 'N'], [157, 4, 'N'], [107, 3, 'N'], [41, 5, 'N'], [42, 5, 'N']], 'dedupe': 2, 'max_gap': 20, 'price': [200, 75, 150, 110, 110, 150], 'trip_cap': 400}, [3, 820]), ({'reads': [[30, 1, 'N'], [75, 3, 'N'], [78, 0, 'N'], [80, 0, 'N'], [79, 4, 'N']], 'dedupe': 2, 'max_gap': 20, 'price': [150, 110, 75, 150, 75, 150], 'trip_cap': 400}, [2, 485]), ({'reads': [[36, 5, 'S']], 'dedupe': 2, 'max_gap': 20, 'price': [110, 200, 75, 75, 200, 110], 'trip_cap': 600}, [1, 110]), ({'reads': [[2, 4, 'N'], [32, 3, 'S'], [77, 4, 'N'], [122, 4, 'S'], [124, 4, 'S']], 'dedupe': 3, 'max_gap': 30, 'price': [75, 200, 200, 150, 150, 150], 'trip_cap': 900}, [4, 600]), ({'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': [[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, 5, 'N'], [81, 4, 'N'], [84, 4, 'N'], [112, 1, 'N'], [115, 1, 'N'], [115, 2, 'N'], [120, 4, 'S'], [121, 4, 'S'], [122, 4, 'S']], 'dedupe': 2, 'max_gap': 30, 'price': [150, 75, 75, 150, 150, 75], 'trip_cap': 900}, [3, 750]), ({'reads': [[37, 5, 'N'], [42, 2, 'N'], [43, 3, 'N'], [46, 3, 'S'], [48, 3, 'S'], [49, 5, 'N']], 'dedupe': 3, 'max_gap': 20, 'price': [110, 150, 110, 110, 110, 150], 'trip_cap': 900}, [3, 630])], [({'reads': [[1, 5, 'N'], [3, 5, 'N'], [2, 2, 'N'], [5, 4, 'N'], [8, 4, 'N'], [10, 4, 'N'], [10, 4, 'N']], 'dedupe': 2, 'max_gap': 30, 'price': [150, 110, 75, 110, 200, 200], 'trip_cap': 600}, [1, 600]), ({'reads': [[23, 2, 'S'], [26, 1, 'S']], 'dedupe': 2, 'max_gap': 20, 'price': [150, 75, 150, 75, 200, 110], 'trip_cap': 900}, [1, 225]), ({'reads': [[24, 0, 'S']], 'dedupe': 2, 'max_gap': 20, 'price': [110, 200, 150, 200, 110, 110], 'trip_cap': 900}, [1, 110]), ({'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': [[47, 2, 'S'], [37, 4, 'S'], [7, 1, 'S'], [50, 2, 'S'], [8, 1, 'S'], [77, 4, 'N']], 'dedupe': 2, 'max_gap': 30, 'price': [110, 110, 200, 150, 150, 110], 'trip_cap': 900}, [2, 810]), ({'reads': [[94, 4, 'S'], [42, 1, 'S'], [55, 1, 'N'], [89, 1, 'S'], [124, 5, 'S'], [172, 2, 'N'], [45, 2, 'S'], [127, 1, 'S'], [86, 1, 'S']], 'dedupe': 3, 'max_gap': 30, 'price': [110, 200, 200, 75, 150, 110], 'trip_cap': 900}, [4, 1460]), ({'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': [[47, 0, 'N'], [48, 0, 'N'], [78, 2, 'N'], [83, 1, 'S'], [84, 4, 'N'], [85, 5, 'N'], [130, 1, 'N'], [161, 0, 'N']], 'dedupe': 3, 'max_gap': 20, 'price': [150, 75, 75, 110, 110, 75], 'trip_cap': 900}, [6, 710])]]
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[1, 570][1, 570]Passed
fee oracle 1[2, 750][1, 750]Failed
fee oracle 2[1, 75][1, 75]Passed
fee oracle 3[1, 275][1, 275]Passed
fee oracle 4[3, 555][3, 555]Passed
fee oracle 5[2, 610][1, 600]Failed
fee oracle 6[5, 805][4, 805]Failed
fee oracle 7[2, 350][2, 350]Passed

SHA-256 / 949aeffefafb25ef808ccc317af32ffe977423b43f7fef0d47356c7cc227dd02

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': [[38, 3, 'S'], [41, 3, 'S'], [48, 4, 'S'], [68, 2, 'S'], [78, 0, 'S']], 'dedupe': 3, 'max_gap': 30, 'price': [150, 110, 110, 110, 200, 75], 'trip_cap': 900}, [1, 570]), ({'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': [[8, 1, 'S']], 'dedupe': 3, 'max_gap': 30, 'price': [200, 75, 75, 150, 75, 150], 'trip_cap': 400}, [1, 75]), ({'reads': [[45, 4, 'S'], [48, 4, 'S'], [48, 3, 'S']], 'dedupe': 3, 'max_gap': 30, 'price': [110, 150, 200, 75, 200, 200], 'trip_cap': 900}, [1, 275]), ({'reads': [[39, 2, 'S'], [59, 0, 'S'], [104, 4, 'S'], [114, 3, 'S'], [145, 5, 'N']], 'dedupe': 3, 'max_gap': 30, 'price': [75, 75, 110, 110, 150, 110], 'trip_cap': 900}, [3, 555]), ({'reads': [[35, 2, 'S'], [32, 2, 'S'], [12, 5, 'S'], [4, 3, 'S'], [7, 4, 'S']], 'dedupe': 3, 'max_gap': 20, 'price': [110, 75, 110, 150, 150, 200], 'trip_cap': 600}, [1, 600]), ({'reads': [[32, 3, 'N'], [87, 5, 'N'], [107, 5, 'N'], [153, 3, 'S'], [2, 4, 'S'], [77, 1, 'N'], [152, 3, 'N'], [89, 5, 'N']], 'dedupe': 3, 'max_gap': 20, 'price': [110, 110, 150, 200, 75, 110], 'trip_cap': 600}, [4, 805]), ({'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': [[132, 4, 'N'], [82, 0, 'N'], [133, 5, 'N'], [72, 2, 'N'], [102, 1, 'N'], [22, 5, 'N'], [27, 3, 'N'], [17, 3, 'N'], [72, 2, 'N']], 'dedupe': 2, 'max_gap': 20, 'price': [110, 110, 75, 200, 110, 110], 'trip_cap': 600}, [3, 1025]), ({'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': [[8, 1, 'N'], [203, 3, 'S'], [128, 0, 'S'], [83, 0, 'S'], [175, 1, 'S'], [173, 1, 'S'], [53, 0, 'S']], 'dedupe': 2, 'max_gap': 20, 'price': [150, 75, 200, 200, 150, 150], 'trip_cap': 600}, [6, 800]), ({'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': [[38, 0, 'N'], [40, 0, 'N']], 'dedupe': 2, 'max_gap': 30, 'price': [200, 110, 200, 75, 150, 150], 'trip_cap': 600}, [1, 200]), ({'reads': [[62, 3, 'N'], [115, 3, 'N'], [64, 0, 'N'], [145, 4, 'N'], [42, 1, 'N'], [64, 3, 'N'], [37, 5, 'N'], [84, 4, 'N'], [114, 1, 'N'], [63, 5, 'N'], [116, 1, 'N']], 'dedupe': 3, 'max_gap': 30, 'price': [75, 75, 150, 150, 110, 110], 'trip_cap': 900}, [1, 900]), ({'reads': [[37, 5, 'S'], [67, 2, 'S'], [72, 0, 'S'], [75, 0, 'S'], [103, 1, 'S'], [123, 4, 'S'], [153, 5, 'S'], [184, 4, 'S'], [204, 3, 'S'], [204, 3, 'S']], 'dedupe': 3, 'max_gap': 20, 'price': [75, 200, 200, 75, 110, 150], 'trip_cap': 400}, [5, 1070]), ({'reads': [[16, 4, 'S'], [26, 4, 'S'], [123, 3, 'S'], [51, 4, 'S'], [87, 3, 'S'], [118, 4, 'S'], [46, 1, 'S'], [56, 2, 'S']], 'dedupe': 3, 'max_gap': 30, 'price': [75, 150, 150, 110, 200, 110], 'trip_cap': 400}, [3, 820])], [({'reads': [[31, 2, 'S'], [61, 0, 'S'], [64, 0, 'S']], 'dedupe': 3, 'max_gap': 20, 'price': [110, 150, 200, 200, 150, 110], 'trip_cap': 900}, [2, 310]), ({'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': [[0, 3, 'S'], [45, 2, 'S'], [50, 1, 'S'], [70, 2, 'S'], [100, 2, 'S'], [101, 2, 'S'], [131, 1, 'S'], [132, 1, 'S']], 'dedupe': 2, 'max_gap': 20, 'price': [150, 200, 150, 200, 150, 200], 'trip_cap': 900}, [4, 1050]), ({'reads': [[18, 3, 'N']], 'dedupe': 2, 'max_gap': 20, 'price': [75, 75, 150, 75, 200, 110], 'trip_cap': 900}, [1, 75]), ({'reads': [[33, 1, 'S'], [118, 1, 'S'], [141, 2, 'S'], [64, 5, 'S'], [84, 5, 'S'], [64, 5, 'S'], [115, 0, 'S'], [121, 5, 'S'], [124, 5, 'S']], 'dedupe': 2, 'max_gap': 20, 'price': [200, 110, 200, 200, 150, 75], 'trip_cap': 900}, [3, 920]), ({'reads': [[12, 5, 'S']], 'dedupe': 3, 'max_gap': 30, 'price': [150, 110, 110, 150, 75, 110], 'trip_cap': 900}, [1, 110]), ({'reads': [[35, 0, 'N'], [65, 3, 'S'], [68, 3, 'S'], [68, 1, 'S'], [68, 1, 'S'], [99, 4, 'S'], [102, 1, 'N'], [104, 1, 'N'], [122, 5, 'N'], [132, 2, 'S'], [135, 5, 'S'], [136, 4, 'S']], 'dedupe': 3, 'max_gap': 30, 'price': [110, 200, 75, 75, 75, 75], 'trip_cap': 400}, [5, 960]), ({'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': [[26, 2, 'N'], [112, 0, 'N'], [31, 2, 'N'], [28, 2, 'N'], [62, 0, 'N'], [44, 5, 'N'], [25, 5, 'N'], [157, 4, 'N'], [107, 3, 'N'], [41, 5, 'N'], [42, 5, 'N']], 'dedupe': 2, 'max_gap': 20, 'price': [200, 75, 150, 110, 110, 150], 'trip_cap': 400}, [3, 820]), ({'reads': [[30, 1, 'N'], [75, 3, 'N'], [78, 0, 'N'], [80, 0, 'N'], [79, 4, 'N']], 'dedupe': 2, 'max_gap': 20, 'price': [150, 110, 75, 150, 75, 150], 'trip_cap': 400}, [2, 485]), ({'reads': [[36, 5, 'S']], 'dedupe': 2, 'max_gap': 20, 'price': [110, 200, 75, 75, 200, 110], 'trip_cap': 600}, [1, 110]), ({'reads': [[2, 4, 'N'], [32, 3, 'S'], [77, 4, 'N'], [122, 4, 'S'], [124, 4, 'S']], 'dedupe': 3, 'max_gap': 30, 'price': [75, 200, 200, 150, 150, 150], 'trip_cap': 900}, [4, 600]), ({'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': [[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, 5, 'N'], [81, 4, 'N'], [84, 4, 'N'], [112, 1, 'N'], [115, 1, 'N'], [115, 2, 'N'], [120, 4, 'S'], [121, 4, 'S'], [122, 4, 'S']], 'dedupe': 2, 'max_gap': 30, 'price': [150, 75, 75, 150, 150, 75], 'trip_cap': 900}, [3, 750]), ({'reads': [[37, 5, 'N'], [42, 2, 'N'], [43, 3, 'N'], [46, 3, 'S'], [48, 3, 'S'], [49, 5, 'N']], 'dedupe': 3, 'max_gap': 20, 'price': [110, 150, 110, 110, 110, 150], 'trip_cap': 900}, [3, 630])], [({'reads': [[1, 5, 'N'], [3, 5, 'N'], [2, 2, 'N'], [5, 4, 'N'], [8, 4, 'N'], [10, 4, 'N'], [10, 4, 'N']], 'dedupe': 2, 'max_gap': 30, 'price': [150, 110, 75, 110, 200, 200], 'trip_cap': 600}, [1, 600]), ({'reads': [[23, 2, 'S'], [26, 1, 'S']], 'dedupe': 2, 'max_gap': 20, 'price': [150, 75, 150, 75, 200, 110], 'trip_cap': 900}, [1, 225]), ({'reads': [[24, 0, 'S']], 'dedupe': 2, 'max_gap': 20, 'price': [110, 200, 150, 200, 110, 110], 'trip_cap': 900}, [1, 110]), ({'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': [[47, 2, 'S'], [37, 4, 'S'], [7, 1, 'S'], [50, 2, 'S'], [8, 1, 'S'], [77, 4, 'N']], 'dedupe': 2, 'max_gap': 30, 'price': [110, 110, 200, 150, 150, 110], 'trip_cap': 900}, [2, 810]), ({'reads': [[94, 4, 'S'], [42, 1, 'S'], [55, 1, 'N'], [89, 1, 'S'], [124, 5, 'S'], [172, 2, 'N'], [45, 2, 'S'], [127, 1, 'S'], [86, 1, 'S']], 'dedupe': 3, 'max_gap': 30, 'price': [110, 200, 200, 75, 150, 110], 'trip_cap': 900}, [4, 1460]), ({'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': [[47, 0, 'N'], [48, 0, 'N'], [78, 2, 'N'], [83, 1, 'S'], [84, 4, 'N'], [85, 5, 'N'], [130, 1, 'N'], [161, 0, 'N']], 'dedupe': 3, 'max_gap': 20, 'price': [150, 75, 75, 110, 110, 75], 'trip_cap': 900}, [6, 710])]]
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[1, 570][1, 570]Passed
fee oracle 1[1, 750][1, 750]Passed
fee oracle 2[1, 75][1, 75]Passed
fee oracle 3[1, 275][1, 275]Passed
fee oracle 4[3, 555][3, 555]Passed
fee oracle 5[1, 600][1, 600]Passed
fee oracle 6[4, 805][4, 805]Passed
fee oracle 7[2, 350][2, 350]Passed

SHA-256 / d6a1593d198e51115ff3b27ceb0796e08b7d1ce8b37e9336ab9150a2c55c5a52

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

Case digest / fb0e3f20f04a75c7f57ca8fb823633faccfa961330e1a726e8133351188f7c08