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.
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 fixture | Actual | Expected | Outcome |
|---|---|---|---|
| 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 fixture | Actual | Expected | Outcome |
|---|---|---|---|
| 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 fixture | Actual | Expected | Outcome |
|---|---|---|---|
| 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