FA-68721 / Toll and parking fee computation / Open access
Open-road gantry trip building: duplicates are only detected between consecutive reads · case 01
Open-road gantry trip building returns a wrong result when duplicates are only detected between consecutive reads.
ROOT CAUSE
A duplicate exactly dedupe minutes later is billed as a second passage.
VERIFIED REPAIR
Restore the duplicate read window rule so that the step reads `if g in lastg and t - lastg[g] <= x['dedupe']: continue`.
Unsuccessful approach: Comparing only against the previous kept read misses duplicates interleaved with other gantries.
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)
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': [[2, 5, 'S'], [7, 1, 'S']], 'dedupe': 3, 'max_gap': 30, 'price': [110, 150, 110, 75, 75, 110], 'trip_cap': 900}, [1, 260]), ({'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': [[17, 1, 'S'], [48, 3, 'S'], [51, 3, 'S'], [49, 5, 'S'], [50, 1, 'S'], [51, 1, 'S'], [70, 1, 'S'], [80, 5, 'S'], [111, 5, 'S'], [113, 5, 'S'], [121, 5, 'N'], [152, 4, 'N']], 'dedupe': 2, 'max_gap': 20, 'price': [75, 150, 75, 110, 200, 110], 'trip_cap': 900}, [5, 1310]), ({'reads': [[55, 4, 'S'], [100, 1, 'N'], [14, 1, 'N'], [46, 5, 'N'], [44, 5, 'N'], [103, 1, 'N'], [45, 3, 'S']], 'dedupe': 3, 'max_gap': 20, 'price': [150, 75, 150, 75, 110, 200], 'trip_cap': 900}, [4, 535]), ({'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, 'S'], [89, 0, 'N'], [70, 0, 'S'], [92, 0, 'N'], [68, 0, 'S'], [7, 0, 'S'], [37, 4, 'S'], [69, 4, 'N']], 'dedupe': 2, 'max_gap': 30, 'price': [75, 110, 75, 200, 75, 150], 'trip_cap': 400}, [3, 450]), ({'reads': [[16, 0, 'N'], [61, 1, 'S'], [64, 1, 'S'], [66, 1, 'S']], 'dedupe': 2, 'max_gap': 20, 'price': [75, 200, 150, 75, 75, 110], 'trip_cap': 900}, [2, 475]), ({'reads': [[17, 3, 'N'], [62, 3, 'N'], [63, 3, 'N'], [72, 4, 'N'], [77, 3, 'N'], [82, 5, 'N'], [92, 2, 'N'], [123, 2, 'S']], 'dedupe': 3, 'max_gap': 20, 'price': [75, 75, 200, 200, 150, 75], 'trip_cap': 400}, [3, 800])], [({'reads': [[76, 0, 'N'], [102, 2, 'S'], [30, 4, 'N'], [103, 2, 'S'], [77, 3, 'N'], [73, 2, 'N'], [148, 3, 'S'], [74, 0, 'N'], [80, 0, 'S'], [28, 4, 'N'], [149, 5, 'S'], [100, 2, 'S'], [77, 3, 'N']], 'dedupe': 3, 'max_gap': 30, 'price': [110, 75, 200, 200, 110, 150], 'trip_cap': 400}, [4, 1170]), ({'reads': [[13, 0, 'N'], [58, 5, 'N'], [61, 5, 'N']], 'dedupe': 2, 'max_gap': 20, 'price': [200, 150, 110, 150, 110, 75], 'trip_cap': 600}, [2, 350]), ({'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': [[21, 4, 'N'], [20, 4, 'N'], [17, 5, 'N'], [20, 5, 'N'], [40, 2, 'N']], 'dedupe': 2, 'max_gap': 30, 'price': [110, 110, 75, 200, 75, 75], 'trip_cap': 900}, [1, 300]), ({'reads': [[55, 5, 'S'], [181, 1, 'S'], [24, 4, 'S'], [103, 5, 'S'], [178, 1, 'S'], [4, 5, 'S'], [198, 4, 'S'], [148, 3, 'S'], [4, 5, 'S'], [100, 4, 'S']], 'dedupe': 2, 'max_gap': 30, 'price': [150, 75, 200, 110, 200, 150], 'trip_cap': 900}, [4, 1310]), ({'reads': [[43, 3, 'S'], [46, 3, 'S'], [53, 4, 'S'], [84, 5, 'S'], [115, 3, 'S'], [116, 1, 'N'], [119, 1, 'N'], [117, 2, 'N'], [137, 4, 'S'], [182, 3, 'S'], [185, 3, 'S']], 'dedupe': 2, 'max_gap': 20, 'price': [75, 75, 110, 150, 150, 200], 'trip_cap': 600}, [6, 1510]), ({'reads': [[79, 1, 'S'], [130, 2, 'S'], [80, 1, 'S'], [35, 3, 'S'], [34, 2, 'S'], [100, 0, 'S'], [33, 3, 'S']], 'dedupe': 2, 'max_gap': 20, 'price': [75, 75, 110, 150, 150, 75], 'trip_cap': 600}, [4, 520])], [({'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': [[7, 3, 'S']], 'dedupe': 3, 'max_gap': 30, 'price': [200, 200, 75, 75, 200, 150], 'trip_cap': 900}, [1, 75]), ({'reads': [[31, 0, 'S'], [167, 3, 'S'], [26, 4, 'S'], [76, 2, 'S'], [124, 4, 'N'], [203, 5, 'S'], [77, 2, 'S'], [172, 4, 'S'], [206, 5, 'S'], [25, 5, 'S'], [122, 4, 'N'], [77, 1, 'N'], [26, 5, 'S']], 'dedupe': 3, 'max_gap': 20, 'price': [200, 110, 110, 200, 150, 110], 'trip_cap': 600}, [6, 1290]), ({'reads': [[45, 0, 'N']], 'dedupe': 3, 'max_gap': 30, 'price': [200, 110, 75, 150, 200, 150], 'trip_cap': 600}, [1, 200]), ({'reads': [[68, 1, 'N'], [69, 2, 'N'], [67, 2, 'N'], [22, 5, 'N']], 'dedupe': 3, 'max_gap': 20, 'price': [110, 75, 200, 200, 150, 75], 'trip_cap': 600}, [2, 350]), ({'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': [[46, 0, 'N'], [47, 0, 'N'], [91, 5, 'N'], [94, 0, 'S'], [124, 4, 'S'], [124, 4, 'S'], [129, 4, 'S'], [131, 4, 'S'], [159, 2, 'S'], [161, 2, 'S']], 'dedupe': 2, 'max_gap': 20, 'price': [75, 200, 200, 150, 200, 110], 'trip_cap': 900}, [5, 860]), ({'reads': [[27, 0, 'S'], [27, 0, 'S'], [37, 0, 'S'], [39, 0, 'S'], [68, 0, 'N']], 'dedupe': 2, 'max_gap': 20, 'price': [110, 150, 150, 200, 75, 200], 'trip_cap': 400}, [2, 330])], [({'reads': [[3, 1, 'S'], [13, 0, 'S'], [15, 0, 'S'], [23, 3, 'S'], [25, 3, 'S'], [68, 1, 'S'], [99, 0, 'S'], [100, 0, 'S'], [102, 5, 'S'], [103, 5, 'S'], [105, 1, 'S'], [106, 0, 'N'], [126, 4, 'N']], 'dedupe': 3, 'max_gap': 30, 'price': [75, 150, 200, 110, 200, 150], 'trip_cap': 600}, [4, 1135]), ({'reads': [[36, 0, 'N'], [35, 5, 'N'], [96, 4, 'S'], [61, 2, 'S'], [97, 4, 'S'], [66, 5, 'S'], [64, 2, 'S'], [69, 5, 'S'], [5, 2, 'N'], [56, 0, 'S'], [37, 5, 'N']], 'dedupe': 3, 'max_gap': 20, 'price': [110, 110, 110, 110, 110, 200], 'trip_cap': 900}, [4, 950]), ({'reads': [[63, 2, 'N'], [31, 4, 'S'], [62, 2, 'N']], 'dedupe': 3, 'max_gap': 30, 'price': [75, 150, 150, 200, 150, 110], 'trip_cap': 600}, [2, 300]), ({'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, 5, 'S'], [39, 2, 'S'], [41, 2, 'S'], [40, 5, 'S'], [85, 3, 'S'], [87, 3, 'S'], [86, 5, 'S'], [87, 3, 'S'], [132, 3, 'S'], [132, 3, 'S'], [152, 2, 'N'], [153, 4, 'S']], 'dedupe': 3, 'max_gap': 20, 'price': [200, 150, 200, 75, 110, 150], 'trip_cap': 900}, [6, 1110]), ({'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, 4, 'S'], [26, 4, 'S'], [57, 0, 'S'], [77, 0, 'N'], [80, 0, 'N'], [87, 0, 'S']], 'dedupe': 3, 'max_gap': 30, 'price': [75, 150, 75, 110, 200, 75], 'trip_cap': 400}, [4, 425]), ({'reads': [[24, 4, 'S'], [25, 1, 'S'], [26, 2, 'N'], [57, 3, 'N'], [102, 3, 'N'], [122, 2, 'S'], [123, 2, 'S'], [152, 4, 'S'], [154, 4, 'S'], [183, 3, 'N'], [185, 3, 'N']], 'dedupe': 2, 'max_gap': 30, 'price': [200, 75, 200, 75, 150, 110], 'trip_cap': 600}, [6, 1000])], [({'reads': [[34, 1, 'N']], 'dedupe': 2, 'max_gap': 20, 'price': [200, 110, 110, 75, 75, 110], 'trip_cap': 900}, [1, 110]), ({'reads': [[19, 1, 'N'], [12, 5, 'N'], [15, 5, 'N'], [18, 1, 'N'], [32, 3, 'N'], [7, 2, 'N'], [6, 1, 'S'], [4, 0, 'S'], [49, 4, 'S'], [3, 1, 'S'], [29, 3, 'N']], 'dedupe': 3, 'max_gap': 30, 'price': [200, 75, 75, 110, 200, 150], 'trip_cap': 900}, [3, 885]), ({'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': [[14, 0, 'N'], [24, 4, 'N'], [24, 4, 'N'], [34, 1, 'N'], [54, 2, 'S'], [55, 5, 'S'], [56, 5, 'S'], [60, 3, 'S'], [60, 3, 'S'], [70, 2, 'N'], [73, 2, 'N']], 'dedupe': 2, 'max_gap': 30, 'price': [200, 200, 150, 200, 110, 200], 'trip_cap': 600}, [3, 1360]), ({'reads': [[9, 4, 'S'], [19, 2, 'S'], [22, 2, 'S'], [64, 0, 'S'], [66, 0, 'S'], [94, 1, 'S'], [96, 1, 'S'], [139, 1, 'S'], [169, 1, 'S'], [171, 1, 'S']], 'dedupe': 2, 'max_gap': 30, 'price': [150, 150, 75, 150, 200, 75], 'trip_cap': 900}, [3, 950]), ({'reads': [[23, 5, 'N'], [24, 1, 'S'], [25, 1, 'S'], [25, 5, 'S'], [35, 2, 'S'], [45, 5, 'S']], 'dedupe': 2, 'max_gap': 20, 'price': [150, 75, 75, 110, 110, 75], 'trip_cap': 900}, [2, 300]), ({'reads': [[44, 3, 'N'], [45, 3, 'N'], [75, 5, 'N'], [78, 5, 'N'], [78, 5, 'N'], [81, 3, 'S']], 'dedupe': 3, 'max_gap': 20, 'price': [75, 75, 150, 150, 200, 150], 'trip_cap': 600}, [3, 450]), ({'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])]]
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 | [1, 260] | [1, 260] | Passed |
| fee oracle 1 | [1, 900] | [1, 750] | Failed |
| fee oracle 2 | [5, 1420] | [5, 1310] | Failed |
| fee oracle 3 | [4, 610] | [4, 535] | Failed |
| fee oracle 4 | [2, 350] | [2, 350] | Passed |
| fee oracle 5 | [5, 525] | [3, 450] | Failed |
| fee oracle 6 | [2, 675] | [2, 475] | Failed |
| fee oracle 7 | [3, 800] | [3, 800] | Passed |
SHA-256 / ad1d91121902f65fbc61c24f85b70da9426f375c2d03d11d77485e2bfdf4ff87
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 kept and kept[-1][1] == g and t - kept[-1][0] <= 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': [[2, 5, 'S'], [7, 1, 'S']], 'dedupe': 3, 'max_gap': 30, 'price': [110, 150, 110, 75, 75, 110], 'trip_cap': 900}, [1, 260]), ({'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': [[17, 1, 'S'], [48, 3, 'S'], [51, 3, 'S'], [49, 5, 'S'], [50, 1, 'S'], [51, 1, 'S'], [70, 1, 'S'], [80, 5, 'S'], [111, 5, 'S'], [113, 5, 'S'], [121, 5, 'N'], [152, 4, 'N']], 'dedupe': 2, 'max_gap': 20, 'price': [75, 150, 75, 110, 200, 110], 'trip_cap': 900}, [5, 1310]), ({'reads': [[55, 4, 'S'], [100, 1, 'N'], [14, 1, 'N'], [46, 5, 'N'], [44, 5, 'N'], [103, 1, 'N'], [45, 3, 'S']], 'dedupe': 3, 'max_gap': 20, 'price': [150, 75, 150, 75, 110, 200], 'trip_cap': 900}, [4, 535]), ({'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, 'S'], [89, 0, 'N'], [70, 0, 'S'], [92, 0, 'N'], [68, 0, 'S'], [7, 0, 'S'], [37, 4, 'S'], [69, 4, 'N']], 'dedupe': 2, 'max_gap': 30, 'price': [75, 110, 75, 200, 75, 150], 'trip_cap': 400}, [3, 450]), ({'reads': [[16, 0, 'N'], [61, 1, 'S'], [64, 1, 'S'], [66, 1, 'S']], 'dedupe': 2, 'max_gap': 20, 'price': [75, 200, 150, 75, 75, 110], 'trip_cap': 900}, [2, 475]), ({'reads': [[17, 3, 'N'], [62, 3, 'N'], [63, 3, 'N'], [72, 4, 'N'], [77, 3, 'N'], [82, 5, 'N'], [92, 2, 'N'], [123, 2, 'S']], 'dedupe': 3, 'max_gap': 20, 'price': [75, 75, 200, 200, 150, 75], 'trip_cap': 400}, [3, 800])], [({'reads': [[76, 0, 'N'], [102, 2, 'S'], [30, 4, 'N'], [103, 2, 'S'], [77, 3, 'N'], [73, 2, 'N'], [148, 3, 'S'], [74, 0, 'N'], [80, 0, 'S'], [28, 4, 'N'], [149, 5, 'S'], [100, 2, 'S'], [77, 3, 'N']], 'dedupe': 3, 'max_gap': 30, 'price': [110, 75, 200, 200, 110, 150], 'trip_cap': 400}, [4, 1170]), ({'reads': [[13, 0, 'N'], [58, 5, 'N'], [61, 5, 'N']], 'dedupe': 2, 'max_gap': 20, 'price': [200, 150, 110, 150, 110, 75], 'trip_cap': 600}, [2, 350]), ({'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': [[21, 4, 'N'], [20, 4, 'N'], [17, 5, 'N'], [20, 5, 'N'], [40, 2, 'N']], 'dedupe': 2, 'max_gap': 30, 'price': [110, 110, 75, 200, 75, 75], 'trip_cap': 900}, [1, 300]), ({'reads': [[55, 5, 'S'], [181, 1, 'S'], [24, 4, 'S'], [103, 5, 'S'], [178, 1, 'S'], [4, 5, 'S'], [198, 4, 'S'], [148, 3, 'S'], [4, 5, 'S'], [100, 4, 'S']], 'dedupe': 2, 'max_gap': 30, 'price': [150, 75, 200, 110, 200, 150], 'trip_cap': 900}, [4, 1310]), ({'reads': [[43, 3, 'S'], [46, 3, 'S'], [53, 4, 'S'], [84, 5, 'S'], [115, 3, 'S'], [116, 1, 'N'], [119, 1, 'N'], [117, 2, 'N'], [137, 4, 'S'], [182, 3, 'S'], [185, 3, 'S']], 'dedupe': 2, 'max_gap': 20, 'price': [75, 75, 110, 150, 150, 200], 'trip_cap': 600}, [6, 1510]), ({'reads': [[79, 1, 'S'], [130, 2, 'S'], [80, 1, 'S'], [35, 3, 'S'], [34, 2, 'S'], [100, 0, 'S'], [33, 3, 'S']], 'dedupe': 2, 'max_gap': 20, 'price': [75, 75, 110, 150, 150, 75], 'trip_cap': 600}, [4, 520])], [({'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': [[7, 3, 'S']], 'dedupe': 3, 'max_gap': 30, 'price': [200, 200, 75, 75, 200, 150], 'trip_cap': 900}, [1, 75]), ({'reads': [[31, 0, 'S'], [167, 3, 'S'], [26, 4, 'S'], [76, 2, 'S'], [124, 4, 'N'], [203, 5, 'S'], [77, 2, 'S'], [172, 4, 'S'], [206, 5, 'S'], [25, 5, 'S'], [122, 4, 'N'], [77, 1, 'N'], [26, 5, 'S']], 'dedupe': 3, 'max_gap': 20, 'price': [200, 110, 110, 200, 150, 110], 'trip_cap': 600}, [6, 1290]), ({'reads': [[45, 0, 'N']], 'dedupe': 3, 'max_gap': 30, 'price': [200, 110, 75, 150, 200, 150], 'trip_cap': 600}, [1, 200]), ({'reads': [[68, 1, 'N'], [69, 2, 'N'], [67, 2, 'N'], [22, 5, 'N']], 'dedupe': 3, 'max_gap': 20, 'price': [110, 75, 200, 200, 150, 75], 'trip_cap': 600}, [2, 350]), ({'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': [[46, 0, 'N'], [47, 0, 'N'], [91, 5, 'N'], [94, 0, 'S'], [124, 4, 'S'], [124, 4, 'S'], [129, 4, 'S'], [131, 4, 'S'], [159, 2, 'S'], [161, 2, 'S']], 'dedupe': 2, 'max_gap': 20, 'price': [75, 200, 200, 150, 200, 110], 'trip_cap': 900}, [5, 860]), ({'reads': [[27, 0, 'S'], [27, 0, 'S'], [37, 0, 'S'], [39, 0, 'S'], [68, 0, 'N']], 'dedupe': 2, 'max_gap': 20, 'price': [110, 150, 150, 200, 75, 200], 'trip_cap': 400}, [2, 330])], [({'reads': [[3, 1, 'S'], [13, 0, 'S'], [15, 0, 'S'], [23, 3, 'S'], [25, 3, 'S'], [68, 1, 'S'], [99, 0, 'S'], [100, 0, 'S'], [102, 5, 'S'], [103, 5, 'S'], [105, 1, 'S'], [106, 0, 'N'], [126, 4, 'N']], 'dedupe': 3, 'max_gap': 30, 'price': [75, 150, 200, 110, 200, 150], 'trip_cap': 600}, [4, 1135]), ({'reads': [[36, 0, 'N'], [35, 5, 'N'], [96, 4, 'S'], [61, 2, 'S'], [97, 4, 'S'], [66, 5, 'S'], [64, 2, 'S'], [69, 5, 'S'], [5, 2, 'N'], [56, 0, 'S'], [37, 5, 'N']], 'dedupe': 3, 'max_gap': 20, 'price': [110, 110, 110, 110, 110, 200], 'trip_cap': 900}, [4, 950]), ({'reads': [[63, 2, 'N'], [31, 4, 'S'], [62, 2, 'N']], 'dedupe': 3, 'max_gap': 30, 'price': [75, 150, 150, 200, 150, 110], 'trip_cap': 600}, [2, 300]), ({'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, 5, 'S'], [39, 2, 'S'], [41, 2, 'S'], [40, 5, 'S'], [85, 3, 'S'], [87, 3, 'S'], [86, 5, 'S'], [87, 3, 'S'], [132, 3, 'S'], [132, 3, 'S'], [152, 2, 'N'], [153, 4, 'S']], 'dedupe': 3, 'max_gap': 20, 'price': [200, 150, 200, 75, 110, 150], 'trip_cap': 900}, [6, 1110]), ({'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, 4, 'S'], [26, 4, 'S'], [57, 0, 'S'], [77, 0, 'N'], [80, 0, 'N'], [87, 0, 'S']], 'dedupe': 3, 'max_gap': 30, 'price': [75, 150, 75, 110, 200, 75], 'trip_cap': 400}, [4, 425]), ({'reads': [[24, 4, 'S'], [25, 1, 'S'], [26, 2, 'N'], [57, 3, 'N'], [102, 3, 'N'], [122, 2, 'S'], [123, 2, 'S'], [152, 4, 'S'], [154, 4, 'S'], [183, 3, 'N'], [185, 3, 'N']], 'dedupe': 2, 'max_gap': 30, 'price': [200, 75, 200, 75, 150, 110], 'trip_cap': 600}, [6, 1000])], [({'reads': [[34, 1, 'N']], 'dedupe': 2, 'max_gap': 20, 'price': [200, 110, 110, 75, 75, 110], 'trip_cap': 900}, [1, 110]), ({'reads': [[19, 1, 'N'], [12, 5, 'N'], [15, 5, 'N'], [18, 1, 'N'], [32, 3, 'N'], [7, 2, 'N'], [6, 1, 'S'], [4, 0, 'S'], [49, 4, 'S'], [3, 1, 'S'], [29, 3, 'N']], 'dedupe': 3, 'max_gap': 30, 'price': [200, 75, 75, 110, 200, 150], 'trip_cap': 900}, [3, 885]), ({'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': [[14, 0, 'N'], [24, 4, 'N'], [24, 4, 'N'], [34, 1, 'N'], [54, 2, 'S'], [55, 5, 'S'], [56, 5, 'S'], [60, 3, 'S'], [60, 3, 'S'], [70, 2, 'N'], [73, 2, 'N']], 'dedupe': 2, 'max_gap': 30, 'price': [200, 200, 150, 200, 110, 200], 'trip_cap': 600}, [3, 1360]), ({'reads': [[9, 4, 'S'], [19, 2, 'S'], [22, 2, 'S'], [64, 0, 'S'], [66, 0, 'S'], [94, 1, 'S'], [96, 1, 'S'], [139, 1, 'S'], [169, 1, 'S'], [171, 1, 'S']], 'dedupe': 2, 'max_gap': 30, 'price': [150, 150, 75, 150, 200, 75], 'trip_cap': 900}, [3, 950]), ({'reads': [[23, 5, 'N'], [24, 1, 'S'], [25, 1, 'S'], [25, 5, 'S'], [35, 2, 'S'], [45, 5, 'S']], 'dedupe': 2, 'max_gap': 20, 'price': [150, 75, 75, 110, 110, 75], 'trip_cap': 900}, [2, 300]), ({'reads': [[44, 3, 'N'], [45, 3, 'N'], [75, 5, 'N'], [78, 5, 'N'], [78, 5, 'N'], [81, 3, 'S']], 'dedupe': 3, 'max_gap': 20, 'price': [75, 75, 150, 150, 200, 150], 'trip_cap': 600}, [3, 450]), ({'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])]]
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 | [1, 260] | [1, 260] | Passed |
| fee oracle 1 | [1, 750] | [1, 750] | Passed |
| fee oracle 2 | [5, 1310] | [5, 1310] | Passed |
| fee oracle 3 | [6, 735] | [4, 535] | Failed |
| fee oracle 4 | [2, 350] | [2, 350] | Passed |
| fee oracle 5 | [5, 525] | [3, 450] | Failed |
| fee oracle 6 | [2, 475] | [2, 475] | Passed |
| fee oracle 7 | [3, 800] | [3, 800] | Passed |
SHA-256 / 311978b1cfa6924c13a0cc9d9cfcad3d22b1d2a8e915df8f25f25140951da672
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': [[2, 5, 'S'], [7, 1, 'S']], 'dedupe': 3, 'max_gap': 30, 'price': [110, 150, 110, 75, 75, 110], 'trip_cap': 900}, [1, 260]), ({'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': [[17, 1, 'S'], [48, 3, 'S'], [51, 3, 'S'], [49, 5, 'S'], [50, 1, 'S'], [51, 1, 'S'], [70, 1, 'S'], [80, 5, 'S'], [111, 5, 'S'], [113, 5, 'S'], [121, 5, 'N'], [152, 4, 'N']], 'dedupe': 2, 'max_gap': 20, 'price': [75, 150, 75, 110, 200, 110], 'trip_cap': 900}, [5, 1310]), ({'reads': [[55, 4, 'S'], [100, 1, 'N'], [14, 1, 'N'], [46, 5, 'N'], [44, 5, 'N'], [103, 1, 'N'], [45, 3, 'S']], 'dedupe': 3, 'max_gap': 20, 'price': [150, 75, 150, 75, 110, 200], 'trip_cap': 900}, [4, 535]), ({'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, 'S'], [89, 0, 'N'], [70, 0, 'S'], [92, 0, 'N'], [68, 0, 'S'], [7, 0, 'S'], [37, 4, 'S'], [69, 4, 'N']], 'dedupe': 2, 'max_gap': 30, 'price': [75, 110, 75, 200, 75, 150], 'trip_cap': 400}, [3, 450]), ({'reads': [[16, 0, 'N'], [61, 1, 'S'], [64, 1, 'S'], [66, 1, 'S']], 'dedupe': 2, 'max_gap': 20, 'price': [75, 200, 150, 75, 75, 110], 'trip_cap': 900}, [2, 475]), ({'reads': [[17, 3, 'N'], [62, 3, 'N'], [63, 3, 'N'], [72, 4, 'N'], [77, 3, 'N'], [82, 5, 'N'], [92, 2, 'N'], [123, 2, 'S']], 'dedupe': 3, 'max_gap': 20, 'price': [75, 75, 200, 200, 150, 75], 'trip_cap': 400}, [3, 800])], [({'reads': [[76, 0, 'N'], [102, 2, 'S'], [30, 4, 'N'], [103, 2, 'S'], [77, 3, 'N'], [73, 2, 'N'], [148, 3, 'S'], [74, 0, 'N'], [80, 0, 'S'], [28, 4, 'N'], [149, 5, 'S'], [100, 2, 'S'], [77, 3, 'N']], 'dedupe': 3, 'max_gap': 30, 'price': [110, 75, 200, 200, 110, 150], 'trip_cap': 400}, [4, 1170]), ({'reads': [[13, 0, 'N'], [58, 5, 'N'], [61, 5, 'N']], 'dedupe': 2, 'max_gap': 20, 'price': [200, 150, 110, 150, 110, 75], 'trip_cap': 600}, [2, 350]), ({'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': [[21, 4, 'N'], [20, 4, 'N'], [17, 5, 'N'], [20, 5, 'N'], [40, 2, 'N']], 'dedupe': 2, 'max_gap': 30, 'price': [110, 110, 75, 200, 75, 75], 'trip_cap': 900}, [1, 300]), ({'reads': [[55, 5, 'S'], [181, 1, 'S'], [24, 4, 'S'], [103, 5, 'S'], [178, 1, 'S'], [4, 5, 'S'], [198, 4, 'S'], [148, 3, 'S'], [4, 5, 'S'], [100, 4, 'S']], 'dedupe': 2, 'max_gap': 30, 'price': [150, 75, 200, 110, 200, 150], 'trip_cap': 900}, [4, 1310]), ({'reads': [[43, 3, 'S'], [46, 3, 'S'], [53, 4, 'S'], [84, 5, 'S'], [115, 3, 'S'], [116, 1, 'N'], [119, 1, 'N'], [117, 2, 'N'], [137, 4, 'S'], [182, 3, 'S'], [185, 3, 'S']], 'dedupe': 2, 'max_gap': 20, 'price': [75, 75, 110, 150, 150, 200], 'trip_cap': 600}, [6, 1510]), ({'reads': [[79, 1, 'S'], [130, 2, 'S'], [80, 1, 'S'], [35, 3, 'S'], [34, 2, 'S'], [100, 0, 'S'], [33, 3, 'S']], 'dedupe': 2, 'max_gap': 20, 'price': [75, 75, 110, 150, 150, 75], 'trip_cap': 600}, [4, 520])], [({'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': [[7, 3, 'S']], 'dedupe': 3, 'max_gap': 30, 'price': [200, 200, 75, 75, 200, 150], 'trip_cap': 900}, [1, 75]), ({'reads': [[31, 0, 'S'], [167, 3, 'S'], [26, 4, 'S'], [76, 2, 'S'], [124, 4, 'N'], [203, 5, 'S'], [77, 2, 'S'], [172, 4, 'S'], [206, 5, 'S'], [25, 5, 'S'], [122, 4, 'N'], [77, 1, 'N'], [26, 5, 'S']], 'dedupe': 3, 'max_gap': 20, 'price': [200, 110, 110, 200, 150, 110], 'trip_cap': 600}, [6, 1290]), ({'reads': [[45, 0, 'N']], 'dedupe': 3, 'max_gap': 30, 'price': [200, 110, 75, 150, 200, 150], 'trip_cap': 600}, [1, 200]), ({'reads': [[68, 1, 'N'], [69, 2, 'N'], [67, 2, 'N'], [22, 5, 'N']], 'dedupe': 3, 'max_gap': 20, 'price': [110, 75, 200, 200, 150, 75], 'trip_cap': 600}, [2, 350]), ({'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': [[46, 0, 'N'], [47, 0, 'N'], [91, 5, 'N'], [94, 0, 'S'], [124, 4, 'S'], [124, 4, 'S'], [129, 4, 'S'], [131, 4, 'S'], [159, 2, 'S'], [161, 2, 'S']], 'dedupe': 2, 'max_gap': 20, 'price': [75, 200, 200, 150, 200, 110], 'trip_cap': 900}, [5, 860]), ({'reads': [[27, 0, 'S'], [27, 0, 'S'], [37, 0, 'S'], [39, 0, 'S'], [68, 0, 'N']], 'dedupe': 2, 'max_gap': 20, 'price': [110, 150, 150, 200, 75, 200], 'trip_cap': 400}, [2, 330])], [({'reads': [[3, 1, 'S'], [13, 0, 'S'], [15, 0, 'S'], [23, 3, 'S'], [25, 3, 'S'], [68, 1, 'S'], [99, 0, 'S'], [100, 0, 'S'], [102, 5, 'S'], [103, 5, 'S'], [105, 1, 'S'], [106, 0, 'N'], [126, 4, 'N']], 'dedupe': 3, 'max_gap': 30, 'price': [75, 150, 200, 110, 200, 150], 'trip_cap': 600}, [4, 1135]), ({'reads': [[36, 0, 'N'], [35, 5, 'N'], [96, 4, 'S'], [61, 2, 'S'], [97, 4, 'S'], [66, 5, 'S'], [64, 2, 'S'], [69, 5, 'S'], [5, 2, 'N'], [56, 0, 'S'], [37, 5, 'N']], 'dedupe': 3, 'max_gap': 20, 'price': [110, 110, 110, 110, 110, 200], 'trip_cap': 900}, [4, 950]), ({'reads': [[63, 2, 'N'], [31, 4, 'S'], [62, 2, 'N']], 'dedupe': 3, 'max_gap': 30, 'price': [75, 150, 150, 200, 150, 110], 'trip_cap': 600}, [2, 300]), ({'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, 5, 'S'], [39, 2, 'S'], [41, 2, 'S'], [40, 5, 'S'], [85, 3, 'S'], [87, 3, 'S'], [86, 5, 'S'], [87, 3, 'S'], [132, 3, 'S'], [132, 3, 'S'], [152, 2, 'N'], [153, 4, 'S']], 'dedupe': 3, 'max_gap': 20, 'price': [200, 150, 200, 75, 110, 150], 'trip_cap': 900}, [6, 1110]), ({'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, 4, 'S'], [26, 4, 'S'], [57, 0, 'S'], [77, 0, 'N'], [80, 0, 'N'], [87, 0, 'S']], 'dedupe': 3, 'max_gap': 30, 'price': [75, 150, 75, 110, 200, 75], 'trip_cap': 400}, [4, 425]), ({'reads': [[24, 4, 'S'], [25, 1, 'S'], [26, 2, 'N'], [57, 3, 'N'], [102, 3, 'N'], [122, 2, 'S'], [123, 2, 'S'], [152, 4, 'S'], [154, 4, 'S'], [183, 3, 'N'], [185, 3, 'N']], 'dedupe': 2, 'max_gap': 30, 'price': [200, 75, 200, 75, 150, 110], 'trip_cap': 600}, [6, 1000])], [({'reads': [[34, 1, 'N']], 'dedupe': 2, 'max_gap': 20, 'price': [200, 110, 110, 75, 75, 110], 'trip_cap': 900}, [1, 110]), ({'reads': [[19, 1, 'N'], [12, 5, 'N'], [15, 5, 'N'], [18, 1, 'N'], [32, 3, 'N'], [7, 2, 'N'], [6, 1, 'S'], [4, 0, 'S'], [49, 4, 'S'], [3, 1, 'S'], [29, 3, 'N']], 'dedupe': 3, 'max_gap': 30, 'price': [200, 75, 75, 110, 200, 150], 'trip_cap': 900}, [3, 885]), ({'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': [[14, 0, 'N'], [24, 4, 'N'], [24, 4, 'N'], [34, 1, 'N'], [54, 2, 'S'], [55, 5, 'S'], [56, 5, 'S'], [60, 3, 'S'], [60, 3, 'S'], [70, 2, 'N'], [73, 2, 'N']], 'dedupe': 2, 'max_gap': 30, 'price': [200, 200, 150, 200, 110, 200], 'trip_cap': 600}, [3, 1360]), ({'reads': [[9, 4, 'S'], [19, 2, 'S'], [22, 2, 'S'], [64, 0, 'S'], [66, 0, 'S'], [94, 1, 'S'], [96, 1, 'S'], [139, 1, 'S'], [169, 1, 'S'], [171, 1, 'S']], 'dedupe': 2, 'max_gap': 30, 'price': [150, 150, 75, 150, 200, 75], 'trip_cap': 900}, [3, 950]), ({'reads': [[23, 5, 'N'], [24, 1, 'S'], [25, 1, 'S'], [25, 5, 'S'], [35, 2, 'S'], [45, 5, 'S']], 'dedupe': 2, 'max_gap': 20, 'price': [150, 75, 75, 110, 110, 75], 'trip_cap': 900}, [2, 300]), ({'reads': [[44, 3, 'N'], [45, 3, 'N'], [75, 5, 'N'], [78, 5, 'N'], [78, 5, 'N'], [81, 3, 'S']], 'dedupe': 3, 'max_gap': 20, 'price': [75, 75, 150, 150, 200, 150], 'trip_cap': 600}, [3, 450]), ({'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])]]
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 | [1, 260] | [1, 260] | Passed |
| fee oracle 1 | [1, 750] | [1, 750] | Passed |
| fee oracle 2 | [5, 1310] | [5, 1310] | Passed |
| fee oracle 3 | [4, 535] | [4, 535] | Passed |
| fee oracle 4 | [2, 350] | [2, 350] | Passed |
| fee oracle 5 | [3, 450] | [3, 450] | Passed |
| fee oracle 6 | [2, 475] | [2, 475] | Passed |
| fee oracle 7 | [3, 800] | [3, 800] | Passed |
SHA-256 / a592f8c28bad1ec6461ed2512a556568a8767d628f7a75f0152fa75782d20383
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.844655+00:00.
Case digest / 79f45dd0c78c54c500059ca83a7c0ec32884f2c0986e29b0b9bbbbc8d85441e0