FAILURE MAP
← Case archive

FA-66616 / Airline fare rule evaluation / Open access

Parties below the group minimum receive conductor tickets · case 01

A 12-person party on a 20-minimum group fare gets two free tickets.

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

ROOT CAUSE

The small-party branch computes the free-ticket entitlement too.

VERIFIED REPAIR

No free tickets below the group minimum.

Unsuccessful approach: Reporting free tickets while still charging everyone contradicts the ticket count.

Case contract

Input {'pax','min_group','free_per','fare','disc_pct'}. Below min_group (total travellers) no discount and no free tickets. Otherwise one free ticket per free_per paying travellers (free = pax // (free_per+1)); each paying fare = fare*(100-disc)/100 rounded to cents, then multiplied. Return [paying, free, total r2].

Why this case matters

Group fares apply a discount once a minimum size is reached and grant tour-conductor tickets that must not themselves count as paying passengers.

1 / The failure

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

N = 1
observations = []
def solve(x):
    n = x['pax']
    if n < x['min_group']:
        per = round(x['fare'], 2)
        free = n // (x['free_per'] + 1)
        return [n - free, free, round(per * (n - free), 2)]
    free = n // (x['free_per'] + 1)
    paying = n - free
    per = round(x['fare'] * (100 - x['disc_pct']) / 100, 2)
    return [paying, free, round(per * paying, 2)]
def check(label, actual, expected):
    observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
fixtures = [[['exactly minimum group', {'pax': 10, 'min_group': 10, 'free_per': 15, 'fare': 249.99, 'disc_pct': 13}, [10, 0, 2174.9]], ['sixteen travellers one free', {'pax': 16, 'min_group': 10, 'free_per': 15, 'fare': 199.95, 'disc_pct': 10}, [15, 1, 2699.4]], ['fifteen travellers none free', {'pax': 15, 'min_group': 10, 'free_per': 15, 'fare': 200.95, 'disc_pct': 10}, [15, 0, 2712.75]], ['small party', {'pax': 9, 'min_group': 10, 'free_per': 15, 'fare': 120.0, 'disc_pct': 15}, [9, 0, 1080.0]], ['large group two free', {'pax': 35, 'min_group': 10, 'free_per': 15, 'fare': 333.33, 'disc_pct': 7}, [33, 2, 10230.0]], ['generous free rule small party', {'pax': 12, 'min_group': 20, 'free_per': 5, 'fare': 101.0, 'disc_pct': 10}, [12, 0, 1212.0]], ['rounding per passenger', {'pax': 21, 'min_group': 20, 'free_per': 9, 'fare': 99.99, 'disc_pct': 15}, [19, 2, 1614.81]], ['sampled case 1', {'pax': 13, 'min_group': 20, 'free_per': 5, 'fare': 244.64, 'disc_pct': 12}, [13, 0, 3180.32]], ['sampled case 2', {'pax': 24, 'min_group': 20, 'free_per': 5, 'fare': 241.52, 'disc_pct': 20}, [20, 4, 3864.4]], ['sampled case 3', {'pax': 59, 'min_group': 20, 'free_per': 10, 'fare': 154.51, 'disc_pct': 15}, [54, 5, 7091.82]]], [['exactly minimum group', {'pax': 10, 'min_group': 10, 'free_per': 15, 'fare': 249.99, 'disc_pct': 14}, [10, 0, 2149.9]], ['sixteen travellers one free', {'pax': 16, 'min_group': 10, 'free_per': 15, 'fare': 199.95, 'disc_pct': 10}, [15, 1, 2699.4]], ['fifteen travellers none free', {'pax': 15, 'min_group': 10, 'free_per': 15, 'fare': 201.95, 'disc_pct': 10}, [15, 0, 2726.25]], ['small party', {'pax': 8, 'min_group': 10, 'free_per': 15, 'fare': 120.0, 'disc_pct': 15}, [8, 0, 960.0]], ['large group two free', {'pax': 36, 'min_group': 10, 'free_per': 15, 'fare': 333.33, 'disc_pct': 7}, [34, 2, 10540.0]], ['generous free rule small party', {'pax': 12, 'min_group': 20, 'free_per': 5, 'fare': 102.0, 'disc_pct': 10}, [12, 0, 1224.0]], ['rounding per passenger', {'pax': 21, 'min_group': 20, 'free_per': 9, 'fare': 99.99, 'disc_pct': 15}, [19, 2, 1614.81]], ['sampled case 1', {'pax': 24, 'min_group': 15, 'free_per': 5, 'fare': 165.48, 'disc_pct': 15}, [20, 4, 2813.2]], ['sampled case 2', {'pax': 50, 'min_group': 20, 'free_per': 15, 'fare': 118.08, 'disc_pct': 10}, [47, 3, 4994.69]], ['sampled case 3', {'pax': 24, 'min_group': 20, 'free_per': 9, 'fare': 198.91, 'disc_pct': 7}, [22, 2, 4069.78]]], [['exactly minimum group', {'pax': 10, 'min_group': 10, 'free_per': 15, 'fare': 249.99, 'disc_pct': 15}, [10, 0, 2124.9]], ['sixteen travellers one free', {'pax': 16, 'min_group': 10, 'free_per': 15, 'fare': 199.95, 'disc_pct': 10}, [15, 1, 2699.4]], ['fifteen travellers none free', {'pax': 15, 'min_group': 10, 'free_per': 15, 'fare': 202.95, 'disc_pct': 10}, [15, 0, 2739.9]], ['small party', {'pax': 9, 'min_group': 10, 'free_per': 15, 'fare': 120.0, 'disc_pct': 15}, [9, 0, 1080.0]], ['large group two free', {'pax': 37, 'min_group': 10, 'free_per': 15, 'fare': 333.33, 'disc_pct': 7}, [35, 2, 10850.0]], ['generous free rule small party', {'pax': 12, 'min_group': 20, 'free_per': 5, 'fare': 103.0, 'disc_pct': 10}, [12, 0, 1236.0]], ['rounding per passenger', {'pax': 21, 'min_group': 20, 'free_per': 9, 'fare': 99.99, 'disc_pct': 15}, [19, 2, 1614.81]], ['sampled case 1', {'pax': 28, 'min_group': 20, 'free_per': 9, 'fare': 148.43, 'disc_pct': 15}, [26, 2, 3280.42]], ['sampled case 2', {'pax': 19, 'min_group': 10, 'free_per': 10, 'fare': 244.1, 'disc_pct': 12}, [18, 1, 3866.58]], ['sampled case 3', {'pax': 37, 'min_group': 20, 'free_per': 15, 'fare': 79.12, 'disc_pct': 5}, [35, 2, 2630.6]]], [['exactly minimum group', {'pax': 10, 'min_group': 10, 'free_per': 15, 'fare': 249.99, 'disc_pct': 16}, [10, 0, 2099.9]], ['sixteen travellers one free', {'pax': 16, 'min_group': 10, 'free_per': 15, 'fare': 199.95, 'disc_pct': 10}, [15, 1, 2699.4]], ['fifteen travellers none free', {'pax': 15, 'min_group': 10, 'free_per': 15, 'fare': 203.95, 'disc_pct': 10}, [15, 0, 2753.4]], ['small party', {'pax': 8, 'min_group': 10, 'free_per': 15, 'fare': 120.0, 'disc_pct': 15}, [8, 0, 960.0]], ['large group two free', {'pax': 38, 'min_group': 10, 'free_per': 15, 'fare': 333.33, 'disc_pct': 7}, [36, 2, 11160.0]], ['generous free rule small party', {'pax': 12, 'min_group': 20, 'free_per': 5, 'fare': 104.0, 'disc_pct': 10}, [12, 0, 1248.0]], ['rounding per passenger', {'pax': 21, 'min_group': 20, 'free_per': 9, 'fare': 99.99, 'disc_pct': 15}, [19, 2, 1614.81]], ['sampled case 1', {'pax': 17, 'min_group': 20, 'free_per': 10, 'fare': 198.81, 'disc_pct': 7}, [17, 0, 3379.77]], ['sampled case 2', {'pax': 19, 'min_group': 15, 'free_per': 10, 'fare': 304.43, 'disc_pct': 5}, [18, 1, 5205.78]], ['sampled case 3', {'pax': 23, 'min_group': 15, 'free_per': 10, 'fare': 154.19, 'disc_pct': 7}, [21, 2, 3011.4]]], [['exactly minimum group', {'pax': 10, 'min_group': 10, 'free_per': 15, 'fare': 249.99, 'disc_pct': 17}, [10, 0, 2074.9]], ['sixteen travellers one free', {'pax': 16, 'min_group': 10, 'free_per': 15, 'fare': 199.95, 'disc_pct': 10}, [15, 1, 2699.4]], ['fifteen travellers none free', {'pax': 15, 'min_group': 10, 'free_per': 15, 'fare': 204.95, 'disc_pct': 10}, [15, 0, 2766.9]], ['small party', {'pax': 9, 'min_group': 10, 'free_per': 15, 'fare': 120.0, 'disc_pct': 15}, [9, 0, 1080.0]], ['large group two free', {'pax': 39, 'min_group': 10, 'free_per': 15, 'fare': 333.33, 'disc_pct': 7}, [37, 2, 11470.0]], ['generous free rule small party', {'pax': 12, 'min_group': 20, 'free_per': 5, 'fare': 105.0, 'disc_pct': 10}, [12, 0, 1260.0]], ['rounding per passenger', {'pax': 21, 'min_group': 20, 'free_per': 9, 'fare': 99.99, 'disc_pct': 15}, [19, 2, 1614.81]], ['sampled case 1', {'pax': 30, 'min_group': 15, 'free_per': 15, 'fare': 348.05, 'disc_pct': 12}, [29, 1, 8882.12]], ['sampled case 2', {'pax': 31, 'min_group': 15, 'free_per': 9, 'fare': 312.49, 'disc_pct': 12}, [28, 3, 7699.72]], ['sampled case 3', {'pax': 59, 'min_group': 20, 'free_per': 9, 'fare': 68.44, 'disc_pct': 10}, [54, 5, 3326.4]]]]
for label, args, expected in fixtures[N-1]:
    check(label, 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
exactly minimum group[10, 0, 2174.9][10, 0, 2174.9]Passed
sixteen travellers one free[15, 1, 2699.4][15, 1, 2699.4]Passed
fifteen travellers none free[15, 0, 2712.75][15, 0, 2712.75]Passed
small party[9, 0, 1080.0][9, 0, 1080.0]Passed
large group two free[33, 2, 10230.0][33, 2, 10230.0]Passed
generous free rule small party[10, 2, 1010.0][12, 0, 1212.0]Failed
rounding per passenger[19, 2, 1614.81][19, 2, 1614.81]Passed
sampled case 1[11, 2, 2691.04][13, 0, 3180.32]Failed
sampled case 2[20, 4, 3864.4][20, 4, 3864.4]Passed
sampled case 3[54, 5, 7091.82][54, 5, 7091.82]Passed

SHA-256 / 974e206fe02cfa3f2984cdacc5d99357facf78bb2d17033724fc0abdf3409103

2 / The unsuccessful fix

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

N = 1
observations = []
def solve(x):
    n = x['pax']
    if n < x['min_group']:
        per = round(x['fare'], 2)
        return [n, n // (x['free_per'] + 1), round(per * n, 2)]
    free = n // (x['free_per'] + 1)
    paying = n - free
    per = round(x['fare'] * (100 - x['disc_pct']) / 100, 2)
    return [paying, free, round(per * paying, 2)]
def check(label, actual, expected):
    observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
fixtures = [[['exactly minimum group', {'pax': 10, 'min_group': 10, 'free_per': 15, 'fare': 249.99, 'disc_pct': 13}, [10, 0, 2174.9]], ['sixteen travellers one free', {'pax': 16, 'min_group': 10, 'free_per': 15, 'fare': 199.95, 'disc_pct': 10}, [15, 1, 2699.4]], ['fifteen travellers none free', {'pax': 15, 'min_group': 10, 'free_per': 15, 'fare': 200.95, 'disc_pct': 10}, [15, 0, 2712.75]], ['small party', {'pax': 9, 'min_group': 10, 'free_per': 15, 'fare': 120.0, 'disc_pct': 15}, [9, 0, 1080.0]], ['large group two free', {'pax': 35, 'min_group': 10, 'free_per': 15, 'fare': 333.33, 'disc_pct': 7}, [33, 2, 10230.0]], ['generous free rule small party', {'pax': 12, 'min_group': 20, 'free_per': 5, 'fare': 101.0, 'disc_pct': 10}, [12, 0, 1212.0]], ['rounding per passenger', {'pax': 21, 'min_group': 20, 'free_per': 9, 'fare': 99.99, 'disc_pct': 15}, [19, 2, 1614.81]], ['sampled case 1', {'pax': 13, 'min_group': 20, 'free_per': 5, 'fare': 244.64, 'disc_pct': 12}, [13, 0, 3180.32]], ['sampled case 2', {'pax': 24, 'min_group': 20, 'free_per': 5, 'fare': 241.52, 'disc_pct': 20}, [20, 4, 3864.4]], ['sampled case 3', {'pax': 59, 'min_group': 20, 'free_per': 10, 'fare': 154.51, 'disc_pct': 15}, [54, 5, 7091.82]]], [['exactly minimum group', {'pax': 10, 'min_group': 10, 'free_per': 15, 'fare': 249.99, 'disc_pct': 14}, [10, 0, 2149.9]], ['sixteen travellers one free', {'pax': 16, 'min_group': 10, 'free_per': 15, 'fare': 199.95, 'disc_pct': 10}, [15, 1, 2699.4]], ['fifteen travellers none free', {'pax': 15, 'min_group': 10, 'free_per': 15, 'fare': 201.95, 'disc_pct': 10}, [15, 0, 2726.25]], ['small party', {'pax': 8, 'min_group': 10, 'free_per': 15, 'fare': 120.0, 'disc_pct': 15}, [8, 0, 960.0]], ['large group two free', {'pax': 36, 'min_group': 10, 'free_per': 15, 'fare': 333.33, 'disc_pct': 7}, [34, 2, 10540.0]], ['generous free rule small party', {'pax': 12, 'min_group': 20, 'free_per': 5, 'fare': 102.0, 'disc_pct': 10}, [12, 0, 1224.0]], ['rounding per passenger', {'pax': 21, 'min_group': 20, 'free_per': 9, 'fare': 99.99, 'disc_pct': 15}, [19, 2, 1614.81]], ['sampled case 1', {'pax': 24, 'min_group': 15, 'free_per': 5, 'fare': 165.48, 'disc_pct': 15}, [20, 4, 2813.2]], ['sampled case 2', {'pax': 50, 'min_group': 20, 'free_per': 15, 'fare': 118.08, 'disc_pct': 10}, [47, 3, 4994.69]], ['sampled case 3', {'pax': 24, 'min_group': 20, 'free_per': 9, 'fare': 198.91, 'disc_pct': 7}, [22, 2, 4069.78]]], [['exactly minimum group', {'pax': 10, 'min_group': 10, 'free_per': 15, 'fare': 249.99, 'disc_pct': 15}, [10, 0, 2124.9]], ['sixteen travellers one free', {'pax': 16, 'min_group': 10, 'free_per': 15, 'fare': 199.95, 'disc_pct': 10}, [15, 1, 2699.4]], ['fifteen travellers none free', {'pax': 15, 'min_group': 10, 'free_per': 15, 'fare': 202.95, 'disc_pct': 10}, [15, 0, 2739.9]], ['small party', {'pax': 9, 'min_group': 10, 'free_per': 15, 'fare': 120.0, 'disc_pct': 15}, [9, 0, 1080.0]], ['large group two free', {'pax': 37, 'min_group': 10, 'free_per': 15, 'fare': 333.33, 'disc_pct': 7}, [35, 2, 10850.0]], ['generous free rule small party', {'pax': 12, 'min_group': 20, 'free_per': 5, 'fare': 103.0, 'disc_pct': 10}, [12, 0, 1236.0]], ['rounding per passenger', {'pax': 21, 'min_group': 20, 'free_per': 9, 'fare': 99.99, 'disc_pct': 15}, [19, 2, 1614.81]], ['sampled case 1', {'pax': 28, 'min_group': 20, 'free_per': 9, 'fare': 148.43, 'disc_pct': 15}, [26, 2, 3280.42]], ['sampled case 2', {'pax': 19, 'min_group': 10, 'free_per': 10, 'fare': 244.1, 'disc_pct': 12}, [18, 1, 3866.58]], ['sampled case 3', {'pax': 37, 'min_group': 20, 'free_per': 15, 'fare': 79.12, 'disc_pct': 5}, [35, 2, 2630.6]]], [['exactly minimum group', {'pax': 10, 'min_group': 10, 'free_per': 15, 'fare': 249.99, 'disc_pct': 16}, [10, 0, 2099.9]], ['sixteen travellers one free', {'pax': 16, 'min_group': 10, 'free_per': 15, 'fare': 199.95, 'disc_pct': 10}, [15, 1, 2699.4]], ['fifteen travellers none free', {'pax': 15, 'min_group': 10, 'free_per': 15, 'fare': 203.95, 'disc_pct': 10}, [15, 0, 2753.4]], ['small party', {'pax': 8, 'min_group': 10, 'free_per': 15, 'fare': 120.0, 'disc_pct': 15}, [8, 0, 960.0]], ['large group two free', {'pax': 38, 'min_group': 10, 'free_per': 15, 'fare': 333.33, 'disc_pct': 7}, [36, 2, 11160.0]], ['generous free rule small party', {'pax': 12, 'min_group': 20, 'free_per': 5, 'fare': 104.0, 'disc_pct': 10}, [12, 0, 1248.0]], ['rounding per passenger', {'pax': 21, 'min_group': 20, 'free_per': 9, 'fare': 99.99, 'disc_pct': 15}, [19, 2, 1614.81]], ['sampled case 1', {'pax': 17, 'min_group': 20, 'free_per': 10, 'fare': 198.81, 'disc_pct': 7}, [17, 0, 3379.77]], ['sampled case 2', {'pax': 19, 'min_group': 15, 'free_per': 10, 'fare': 304.43, 'disc_pct': 5}, [18, 1, 5205.78]], ['sampled case 3', {'pax': 23, 'min_group': 15, 'free_per': 10, 'fare': 154.19, 'disc_pct': 7}, [21, 2, 3011.4]]], [['exactly minimum group', {'pax': 10, 'min_group': 10, 'free_per': 15, 'fare': 249.99, 'disc_pct': 17}, [10, 0, 2074.9]], ['sixteen travellers one free', {'pax': 16, 'min_group': 10, 'free_per': 15, 'fare': 199.95, 'disc_pct': 10}, [15, 1, 2699.4]], ['fifteen travellers none free', {'pax': 15, 'min_group': 10, 'free_per': 15, 'fare': 204.95, 'disc_pct': 10}, [15, 0, 2766.9]], ['small party', {'pax': 9, 'min_group': 10, 'free_per': 15, 'fare': 120.0, 'disc_pct': 15}, [9, 0, 1080.0]], ['large group two free', {'pax': 39, 'min_group': 10, 'free_per': 15, 'fare': 333.33, 'disc_pct': 7}, [37, 2, 11470.0]], ['generous free rule small party', {'pax': 12, 'min_group': 20, 'free_per': 5, 'fare': 105.0, 'disc_pct': 10}, [12, 0, 1260.0]], ['rounding per passenger', {'pax': 21, 'min_group': 20, 'free_per': 9, 'fare': 99.99, 'disc_pct': 15}, [19, 2, 1614.81]], ['sampled case 1', {'pax': 30, 'min_group': 15, 'free_per': 15, 'fare': 348.05, 'disc_pct': 12}, [29, 1, 8882.12]], ['sampled case 2', {'pax': 31, 'min_group': 15, 'free_per': 9, 'fare': 312.49, 'disc_pct': 12}, [28, 3, 7699.72]], ['sampled case 3', {'pax': 59, 'min_group': 20, 'free_per': 9, 'fare': 68.44, 'disc_pct': 10}, [54, 5, 3326.4]]]]
for label, args, expected in fixtures[N-1]:
    check(label, 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
exactly minimum group[10, 0, 2174.9][10, 0, 2174.9]Passed
sixteen travellers one free[15, 1, 2699.4][15, 1, 2699.4]Passed
fifteen travellers none free[15, 0, 2712.75][15, 0, 2712.75]Passed
small party[9, 0, 1080.0][9, 0, 1080.0]Passed
large group two free[33, 2, 10230.0][33, 2, 10230.0]Passed
generous free rule small party[12, 2, 1212.0][12, 0, 1212.0]Failed
rounding per passenger[19, 2, 1614.81][19, 2, 1614.81]Passed
sampled case 1[13, 2, 3180.32][13, 0, 3180.32]Failed
sampled case 2[20, 4, 3864.4][20, 4, 3864.4]Passed
sampled case 3[54, 5, 7091.82][54, 5, 7091.82]Passed

SHA-256 / ab1c7dcd144787b56c219c9580951f6ab626cfbd31c5646cbe3ebaeb48ff4053

3 / The verified repair

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

N = 1
observations = []
def solve(x):
    n = x['pax']
    if n < x['min_group']:
        per = round(x['fare'], 2)
        return [n, 0, round(per * n, 2)]
    free = n // (x['free_per'] + 1)
    paying = n - free
    per = round(x['fare'] * (100 - x['disc_pct']) / 100, 2)
    return [paying, free, round(per * paying, 2)]
def check(label, actual, expected):
    observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
fixtures = [[['exactly minimum group', {'pax': 10, 'min_group': 10, 'free_per': 15, 'fare': 249.99, 'disc_pct': 13}, [10, 0, 2174.9]], ['sixteen travellers one free', {'pax': 16, 'min_group': 10, 'free_per': 15, 'fare': 199.95, 'disc_pct': 10}, [15, 1, 2699.4]], ['fifteen travellers none free', {'pax': 15, 'min_group': 10, 'free_per': 15, 'fare': 200.95, 'disc_pct': 10}, [15, 0, 2712.75]], ['small party', {'pax': 9, 'min_group': 10, 'free_per': 15, 'fare': 120.0, 'disc_pct': 15}, [9, 0, 1080.0]], ['large group two free', {'pax': 35, 'min_group': 10, 'free_per': 15, 'fare': 333.33, 'disc_pct': 7}, [33, 2, 10230.0]], ['generous free rule small party', {'pax': 12, 'min_group': 20, 'free_per': 5, 'fare': 101.0, 'disc_pct': 10}, [12, 0, 1212.0]], ['rounding per passenger', {'pax': 21, 'min_group': 20, 'free_per': 9, 'fare': 99.99, 'disc_pct': 15}, [19, 2, 1614.81]], ['sampled case 1', {'pax': 13, 'min_group': 20, 'free_per': 5, 'fare': 244.64, 'disc_pct': 12}, [13, 0, 3180.32]], ['sampled case 2', {'pax': 24, 'min_group': 20, 'free_per': 5, 'fare': 241.52, 'disc_pct': 20}, [20, 4, 3864.4]], ['sampled case 3', {'pax': 59, 'min_group': 20, 'free_per': 10, 'fare': 154.51, 'disc_pct': 15}, [54, 5, 7091.82]]], [['exactly minimum group', {'pax': 10, 'min_group': 10, 'free_per': 15, 'fare': 249.99, 'disc_pct': 14}, [10, 0, 2149.9]], ['sixteen travellers one free', {'pax': 16, 'min_group': 10, 'free_per': 15, 'fare': 199.95, 'disc_pct': 10}, [15, 1, 2699.4]], ['fifteen travellers none free', {'pax': 15, 'min_group': 10, 'free_per': 15, 'fare': 201.95, 'disc_pct': 10}, [15, 0, 2726.25]], ['small party', {'pax': 8, 'min_group': 10, 'free_per': 15, 'fare': 120.0, 'disc_pct': 15}, [8, 0, 960.0]], ['large group two free', {'pax': 36, 'min_group': 10, 'free_per': 15, 'fare': 333.33, 'disc_pct': 7}, [34, 2, 10540.0]], ['generous free rule small party', {'pax': 12, 'min_group': 20, 'free_per': 5, 'fare': 102.0, 'disc_pct': 10}, [12, 0, 1224.0]], ['rounding per passenger', {'pax': 21, 'min_group': 20, 'free_per': 9, 'fare': 99.99, 'disc_pct': 15}, [19, 2, 1614.81]], ['sampled case 1', {'pax': 24, 'min_group': 15, 'free_per': 5, 'fare': 165.48, 'disc_pct': 15}, [20, 4, 2813.2]], ['sampled case 2', {'pax': 50, 'min_group': 20, 'free_per': 15, 'fare': 118.08, 'disc_pct': 10}, [47, 3, 4994.69]], ['sampled case 3', {'pax': 24, 'min_group': 20, 'free_per': 9, 'fare': 198.91, 'disc_pct': 7}, [22, 2, 4069.78]]], [['exactly minimum group', {'pax': 10, 'min_group': 10, 'free_per': 15, 'fare': 249.99, 'disc_pct': 15}, [10, 0, 2124.9]], ['sixteen travellers one free', {'pax': 16, 'min_group': 10, 'free_per': 15, 'fare': 199.95, 'disc_pct': 10}, [15, 1, 2699.4]], ['fifteen travellers none free', {'pax': 15, 'min_group': 10, 'free_per': 15, 'fare': 202.95, 'disc_pct': 10}, [15, 0, 2739.9]], ['small party', {'pax': 9, 'min_group': 10, 'free_per': 15, 'fare': 120.0, 'disc_pct': 15}, [9, 0, 1080.0]], ['large group two free', {'pax': 37, 'min_group': 10, 'free_per': 15, 'fare': 333.33, 'disc_pct': 7}, [35, 2, 10850.0]], ['generous free rule small party', {'pax': 12, 'min_group': 20, 'free_per': 5, 'fare': 103.0, 'disc_pct': 10}, [12, 0, 1236.0]], ['rounding per passenger', {'pax': 21, 'min_group': 20, 'free_per': 9, 'fare': 99.99, 'disc_pct': 15}, [19, 2, 1614.81]], ['sampled case 1', {'pax': 28, 'min_group': 20, 'free_per': 9, 'fare': 148.43, 'disc_pct': 15}, [26, 2, 3280.42]], ['sampled case 2', {'pax': 19, 'min_group': 10, 'free_per': 10, 'fare': 244.1, 'disc_pct': 12}, [18, 1, 3866.58]], ['sampled case 3', {'pax': 37, 'min_group': 20, 'free_per': 15, 'fare': 79.12, 'disc_pct': 5}, [35, 2, 2630.6]]], [['exactly minimum group', {'pax': 10, 'min_group': 10, 'free_per': 15, 'fare': 249.99, 'disc_pct': 16}, [10, 0, 2099.9]], ['sixteen travellers one free', {'pax': 16, 'min_group': 10, 'free_per': 15, 'fare': 199.95, 'disc_pct': 10}, [15, 1, 2699.4]], ['fifteen travellers none free', {'pax': 15, 'min_group': 10, 'free_per': 15, 'fare': 203.95, 'disc_pct': 10}, [15, 0, 2753.4]], ['small party', {'pax': 8, 'min_group': 10, 'free_per': 15, 'fare': 120.0, 'disc_pct': 15}, [8, 0, 960.0]], ['large group two free', {'pax': 38, 'min_group': 10, 'free_per': 15, 'fare': 333.33, 'disc_pct': 7}, [36, 2, 11160.0]], ['generous free rule small party', {'pax': 12, 'min_group': 20, 'free_per': 5, 'fare': 104.0, 'disc_pct': 10}, [12, 0, 1248.0]], ['rounding per passenger', {'pax': 21, 'min_group': 20, 'free_per': 9, 'fare': 99.99, 'disc_pct': 15}, [19, 2, 1614.81]], ['sampled case 1', {'pax': 17, 'min_group': 20, 'free_per': 10, 'fare': 198.81, 'disc_pct': 7}, [17, 0, 3379.77]], ['sampled case 2', {'pax': 19, 'min_group': 15, 'free_per': 10, 'fare': 304.43, 'disc_pct': 5}, [18, 1, 5205.78]], ['sampled case 3', {'pax': 23, 'min_group': 15, 'free_per': 10, 'fare': 154.19, 'disc_pct': 7}, [21, 2, 3011.4]]], [['exactly minimum group', {'pax': 10, 'min_group': 10, 'free_per': 15, 'fare': 249.99, 'disc_pct': 17}, [10, 0, 2074.9]], ['sixteen travellers one free', {'pax': 16, 'min_group': 10, 'free_per': 15, 'fare': 199.95, 'disc_pct': 10}, [15, 1, 2699.4]], ['fifteen travellers none free', {'pax': 15, 'min_group': 10, 'free_per': 15, 'fare': 204.95, 'disc_pct': 10}, [15, 0, 2766.9]], ['small party', {'pax': 9, 'min_group': 10, 'free_per': 15, 'fare': 120.0, 'disc_pct': 15}, [9, 0, 1080.0]], ['large group two free', {'pax': 39, 'min_group': 10, 'free_per': 15, 'fare': 333.33, 'disc_pct': 7}, [37, 2, 11470.0]], ['generous free rule small party', {'pax': 12, 'min_group': 20, 'free_per': 5, 'fare': 105.0, 'disc_pct': 10}, [12, 0, 1260.0]], ['rounding per passenger', {'pax': 21, 'min_group': 20, 'free_per': 9, 'fare': 99.99, 'disc_pct': 15}, [19, 2, 1614.81]], ['sampled case 1', {'pax': 30, 'min_group': 15, 'free_per': 15, 'fare': 348.05, 'disc_pct': 12}, [29, 1, 8882.12]], ['sampled case 2', {'pax': 31, 'min_group': 15, 'free_per': 9, 'fare': 312.49, 'disc_pct': 12}, [28, 3, 7699.72]], ['sampled case 3', {'pax': 59, 'min_group': 20, 'free_per': 9, 'fare': 68.44, 'disc_pct': 10}, [54, 5, 3326.4]]]]
for label, args, expected in fixtures[N-1]:
    check(label, 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
exactly minimum group[10, 0, 2174.9][10, 0, 2174.9]Passed
sixteen travellers one free[15, 1, 2699.4][15, 1, 2699.4]Passed
fifteen travellers none free[15, 0, 2712.75][15, 0, 2712.75]Passed
small party[9, 0, 1080.0][9, 0, 1080.0]Passed
large group two free[33, 2, 10230.0][33, 2, 10230.0]Passed
generous free rule small party[12, 0, 1212.0][12, 0, 1212.0]Passed
rounding per passenger[19, 2, 1614.81][19, 2, 1614.81]Passed
sampled case 1[13, 0, 3180.32][13, 0, 3180.32]Passed
sampled case 2[20, 4, 3864.4][20, 4, 3864.4]Passed
sampled case 3[54, 5, 7091.82][54, 5, 7091.82]Passed

SHA-256 / 8b5a33e301a9cc3dc4060b3f9df20c335457d34305c59862a019f9776b7832c1

Verification & scope

A stipulated toy fare-rule contract with invented constants; it is not an ATPCO or carrier tariff implementation and makes no claim of industry-standard conformance. 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:47:45.439996+00:00.

Case digest / 096da06585d1c5186095bf3782180bae9121fa4a1ee01a3eefa689bbc567c20a