broken_source:
"""Failure Map reference implementation. Python standard library only."""
import json

N = 1
observations = []
def solve(x):
    if x['exit'] <= x['entry']:
        return 'invalid'
    dur = x['exit'] - x['entry']
    if dur <= x['grace']:
        fee = 0
    else:
        days, rem = divmod(dur, 1440)
        fee = days * x['daily_max'] + min(x['daily_max'], -(-rem // 60) * x['hourly'])
    if x['lost_ticket']:
        fee = max(fee, x['lost_fee'])
    return fee
def check(label, actual, expected):
    observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
fixtures = [[({'entry': 631, 'exit': 638, 'grace': 20, 'hourly': 300, 'daily_max': 2000, 'lost_ticket': True, 'lost_fee': 3000}, 3000), ({'entry': 2581, 'exit': 2580, 'grace': 10, 'hourly': 300, 'daily_max': 1500, 'lost_ticket': False, 'lost_fee': 3000}, 'invalid'), ({'entry': 100, 'exit': 115, 'grace': 15, 'hourly': 300, 'daily_max': 2000, 'lost_ticket': False, 'lost_fee': 2500}, 0), ({'entry': 0, 'exit': 1560, 'grace': 10, 'hourly': 300, 'daily_max': 2000, 'lost_ticket': False, 'lost_fee': 2500}, 2600), ({'entry': 1676, 'exit': 1676, 'grace': 15, 'hourly': 450, 'daily_max': 1500, 'lost_ticket': True, 'lost_fee': 2500}, 2500), ({'entry': 500, 'exit': 500, 'grace': 0, 'hourly': 300, 'daily_max': 2000, 'lost_ticket': True, 'lost_fee': 2500}, 2500), ({'entry': 2473, 'exit': 2473, 'grace': 15, 'hourly': 200, 'daily_max': 2000, 'lost_ticket': False, 'lost_fee': 2500}, 0), ({'entry': 360, 'exit': 346, 'grace': 15, 'hourly': 300, 'daily_max': 1500, 'lost_ticket': False, 'lost_fee': 2500}, 'invalid')], [({'entry': 500, 'exit': 500, 'grace': 0, 'hourly': 300, 'daily_max': 2000, 'lost_ticket': True, 'lost_fee': 2500}, 2500), ({'entry': 0, 'exit': 1560, 'grace': 10, 'hourly': 300, 'daily_max': 2000, 'lost_ticket': False, 'lost_fee': 2500}, 2600), ({'entry': 1465, 'exit': 1462, 'grace': 10, 'hourly': 300, 'daily_max': 2000, 'lost_ticket': False, 'lost_fee': 2500}, 'invalid'), ({'entry': 100, 'exit': 115, 'grace': 15, 'hourly': 300, 'daily_max': 2000, 'lost_ticket': False, 'lost_fee': 2500}, 0), ({'entry': 406, 'exit': 561, 'grace': 15, 'hourly': 300, 'daily_max': 2000, 'lost_ticket': False, 'lost_fee': 3000}, 900), ({'entry': 87, 'exit': 85, 'grace': 20, 'hourly': 200, 'daily_max': 2400, 'lost_ticket': False, 'lost_fee': 3000}, 'invalid'), ({'entry': 1952, 'exit': 1952, 'grace': 0, 'hourly': 300, 'daily_max': 2400, 'lost_ticket': False, 'lost_fee': 2500}, 0), ({'entry': 89, 'exit': 365, 'grace': 10, 'hourly': 300, 'daily_max': 1500, 'lost_ticket': False, 'lost_fee': 2500}, 1500)], [({'entry': 717, 'exit': 699, 'grace': 20, 'hourly': 200, 'daily_max': 2000, 'lost_ticket': False, 'lost_fee': 3000}, 'invalid'), ({'entry': 2276, 'exit': 2276, 'grace': 0, 'hourly': 300, 'daily_max': 1500, 'lost_ticket': False, 'lost_fee': 3000}, 0), ({'entry': 0, 'exit': 1560, 'grace': 10, 'hourly': 300, 'daily_max': 2000, 'lost_ticket': False, 'lost_fee': 2500}, 2600), ({'entry': 500, 'exit': 500, 'grace': 0, 'hourly': 300, 'daily_max': 2000, 'lost_ticket': True, 'lost_fee': 2500}, 2500), ({'entry': 60, 'exit': 240, 'grace': 15, 'hourly': 450, 'daily_max': 2400, 'lost_ticket': False, 'lost_fee': 3000}, 1350), ({'entry': 5, 'exit': 5, 'grace': 0, 'hourly': 450, 'daily_max': 2400, 'lost_ticket': False, 'lost_fee': 3000}, 0), ({'entry': 100, 'exit': 115, 'grace': 15, 'hourly': 300, 'daily_max': 2000, 'lost_ticket': False, 'lost_fee': 2500}, 0), ({'entry': 1064, 'exit': 1058, 'grace': 10, 'hourly': 300, 'daily_max': 2000, 'lost_ticket': False, 'lost_fee': 2500}, 'invalid')], [({'entry': 60, 'exit': 240, 'grace': 15, 'hourly': 450, 'daily_max': 2400, 'lost_ticket': False, 'lost_fee': 3000}, 1350), ({'entry': 2683, 'exit': 5878, 'grace': 15, 'hourly': 450, 'daily_max': 2000, 'lost_ticket': False, 'lost_fee': 3000}, 6000), ({'entry': 169, 'exit': 161, 'grace': 15, 'hourly': 200, 'daily_max': 1500, 'lost_ticket': False, 'lost_fee': 2500}, 'invalid'), ({'entry': 2896, 'exit': 7216, 'grace': 10, 'hourly': 200, 'daily_max': 1500, 'lost_ticket': False, 'lost_fee': 3000}, 4500), ({'entry': 2250, 'exit': 2230, 'grace': 20, 'hourly': 200, 'daily_max': 1500, 'lost_ticket': False, 'lost_fee': 3000}, 'invalid'), ({'entry': 1281, 'exit': 1281, 'grace': 20, 'hourly': 450, 'daily_max': 2000, 'lost_ticket': True, 'lost_fee': 3000}, 3000), ({'entry': 500, 'exit': 500, 'grace': 0, 'hourly': 300, 'daily_max': 2000, 'lost_ticket': True, 'lost_fee': 2500}, 2500), ({'entry': 100, 'exit': 115, 'grace': 15, 'hourly': 300, 'daily_max': 2000, 'lost_ticket': False, 'lost_fee': 2500}, 0)], [({'entry': 100, 'exit': 115, 'grace': 15, 'hourly': 300, 'daily_max': 2000, 'lost_ticket': False, 'lost_fee': 2500}, 0), ({'entry': 500, 'exit': 500, 'grace': 0, 'hourly': 300, 'daily_max': 2000, 'lost_ticket': True, 'lost_fee': 2500}, 2500), ({'entry': 1799, 'exit': 1789, 'grace': 20, 'hourly': 300, 'daily_max': 2000, 'lost_ticket': False, 'lost_fee': 3000}, 'invalid'), ({'entry': 1549, 'exit': 4829, 'grace': 10, 'hourly': 450, 'daily_max': 1500, 'lost_ticket': False, 'lost_fee': 3000}, 4500), ({'entry': 1393, 'exit': 1446, 'grace': 0, 'hourly': 300, 'daily_max': 1500, 'lost_ticket': False, 'lost_fee': 2500}, 300), ({'entry': 426, 'exit': 438, 'grace': 15, 'hourly': 300, 'daily_max': 2000, 'lost_ticket': False, 'lost_fee': 2500}, 0), ({'entry': 2821, 'exit': 2816, 'grace': 10, 'hourly': 200, 'daily_max': 2400, 'lost_ticket': True, 'lost_fee': 2500}, 'invalid'), ({'entry': 0, 'exit': 1560, 'grace': 10, 'hourly': 300, 'daily_max': 2000, 'lost_ticket': False, 'lost_fee': 2500}, 2600)]]
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)

source:
"""Failure Map reference implementation. Python standard library only."""
import json

N = 1
observations = []
def solve(x):
    if x['exit'] < x['entry'] - x['grace']:
        return 'invalid'
    dur = x['exit'] - x['entry']
    if dur <= x['grace']:
        fee = 0
    else:
        days, rem = divmod(dur, 1440)
        fee = days * x['daily_max'] + min(x['daily_max'], -(-rem // 60) * x['hourly'])
    if x['lost_ticket']:
        fee = max(fee, x['lost_fee'])
    return fee
def check(label, actual, expected):
    observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
fixtures = [[({'entry': 631, 'exit': 638, 'grace': 20, 'hourly': 300, 'daily_max': 2000, 'lost_ticket': True, 'lost_fee': 3000}, 3000), ({'entry': 2581, 'exit': 2580, 'grace': 10, 'hourly': 300, 'daily_max': 1500, 'lost_ticket': False, 'lost_fee': 3000}, 'invalid'), ({'entry': 100, 'exit': 115, 'grace': 15, 'hourly': 300, 'daily_max': 2000, 'lost_ticket': False, 'lost_fee': 2500}, 0), ({'entry': 0, 'exit': 1560, 'grace': 10, 'hourly': 300, 'daily_max': 2000, 'lost_ticket': False, 'lost_fee': 2500}, 2600), ({'entry': 1676, 'exit': 1676, 'grace': 15, 'hourly': 450, 'daily_max': 1500, 'lost_ticket': True, 'lost_fee': 2500}, 2500), ({'entry': 500, 'exit': 500, 'grace': 0, 'hourly': 300, 'daily_max': 2000, 'lost_ticket': True, 'lost_fee': 2500}, 2500), ({'entry': 2473, 'exit': 2473, 'grace': 15, 'hourly': 200, 'daily_max': 2000, 'lost_ticket': False, 'lost_fee': 2500}, 0), ({'entry': 360, 'exit': 346, 'grace': 15, 'hourly': 300, 'daily_max': 1500, 'lost_ticket': False, 'lost_fee': 2500}, 'invalid')], [({'entry': 500, 'exit': 500, 'grace': 0, 'hourly': 300, 'daily_max': 2000, 'lost_ticket': True, 'lost_fee': 2500}, 2500), ({'entry': 0, 'exit': 1560, 'grace': 10, 'hourly': 300, 'daily_max': 2000, 'lost_ticket': False, 'lost_fee': 2500}, 2600), ({'entry': 1465, 'exit': 1462, 'grace': 10, 'hourly': 300, 'daily_max': 2000, 'lost_ticket': False, 'lost_fee': 2500}, 'invalid'), ({'entry': 100, 'exit': 115, 'grace': 15, 'hourly': 300, 'daily_max': 2000, 'lost_ticket': False, 'lost_fee': 2500}, 0), ({'entry': 406, 'exit': 561, 'grace': 15, 'hourly': 300, 'daily_max': 2000, 'lost_ticket': False, 'lost_fee': 3000}, 900), ({'entry': 87, 'exit': 85, 'grace': 20, 'hourly': 200, 'daily_max': 2400, 'lost_ticket': False, 'lost_fee': 3000}, 'invalid'), ({'entry': 1952, 'exit': 1952, 'grace': 0, 'hourly': 300, 'daily_max': 2400, 'lost_ticket': False, 'lost_fee': 2500}, 0), ({'entry': 89, 'exit': 365, 'grace': 10, 'hourly': 300, 'daily_max': 1500, 'lost_ticket': False, 'lost_fee': 2500}, 1500)], [({'entry': 717, 'exit': 699, 'grace': 20, 'hourly': 200, 'daily_max': 2000, 'lost_ticket': False, 'lost_fee': 3000}, 'invalid'), ({'entry': 2276, 'exit': 2276, 'grace': 0, 'hourly': 300, 'daily_max': 1500, 'lost_ticket': False, 'lost_fee': 3000}, 0), ({'entry': 0, 'exit': 1560, 'grace': 10, 'hourly': 300, 'daily_max': 2000, 'lost_ticket': False, 'lost_fee': 2500}, 2600), ({'entry': 500, 'exit': 500, 'grace': 0, 'hourly': 300, 'daily_max': 2000, 'lost_ticket': True, 'lost_fee': 2500}, 2500), ({'entry': 60, 'exit': 240, 'grace': 15, 'hourly': 450, 'daily_max': 2400, 'lost_ticket': False, 'lost_fee': 3000}, 1350), ({'entry': 5, 'exit': 5, 'grace': 0, 'hourly': 450, 'daily_max': 2400, 'lost_ticket': False, 'lost_fee': 3000}, 0), ({'entry': 100, 'exit': 115, 'grace': 15, 'hourly': 300, 'daily_max': 2000, 'lost_ticket': False, 'lost_fee': 2500}, 0), ({'entry': 1064, 'exit': 1058, 'grace': 10, 'hourly': 300, 'daily_max': 2000, 'lost_ticket': False, 'lost_fee': 2500}, 'invalid')], [({'entry': 60, 'exit': 240, 'grace': 15, 'hourly': 450, 'daily_max': 2400, 'lost_ticket': False, 'lost_fee': 3000}, 1350), ({'entry': 2683, 'exit': 5878, 'grace': 15, 'hourly': 450, 'daily_max': 2000, 'lost_ticket': False, 'lost_fee': 3000}, 6000), ({'entry': 169, 'exit': 161, 'grace': 15, 'hourly': 200, 'daily_max': 1500, 'lost_ticket': False, 'lost_fee': 2500}, 'invalid'), ({'entry': 2896, 'exit': 7216, 'grace': 10, 'hourly': 200, 'daily_max': 1500, 'lost_ticket': False, 'lost_fee': 3000}, 4500), ({'entry': 2250, 'exit': 2230, 'grace': 20, 'hourly': 200, 'daily_max': 1500, 'lost_ticket': False, 'lost_fee': 3000}, 'invalid'), ({'entry': 1281, 'exit': 1281, 'grace': 20, 'hourly': 450, 'daily_max': 2000, 'lost_ticket': True, 'lost_fee': 3000}, 3000), ({'entry': 500, 'exit': 500, 'grace': 0, 'hourly': 300, 'daily_max': 2000, 'lost_ticket': True, 'lost_fee': 2500}, 2500), ({'entry': 100, 'exit': 115, 'grace': 15, 'hourly': 300, 'daily_max': 2000, 'lost_ticket': False, 'lost_fee': 2500}, 0)], [({'entry': 100, 'exit': 115, 'grace': 15, 'hourly': 300, 'daily_max': 2000, 'lost_ticket': False, 'lost_fee': 2500}, 0), ({'entry': 500, 'exit': 500, 'grace': 0, 'hourly': 300, 'daily_max': 2000, 'lost_ticket': True, 'lost_fee': 2500}, 2500), ({'entry': 1799, 'exit': 1789, 'grace': 20, 'hourly': 300, 'daily_max': 2000, 'lost_ticket': False, 'lost_fee': 3000}, 'invalid'), ({'entry': 1549, 'exit': 4829, 'grace': 10, 'hourly': 450, 'daily_max': 1500, 'lost_ticket': False, 'lost_fee': 3000}, 4500), ({'entry': 1393, 'exit': 1446, 'grace': 0, 'hourly': 300, 'daily_max': 1500, 'lost_ticket': False, 'lost_fee': 2500}, 300), ({'entry': 426, 'exit': 438, 'grace': 15, 'hourly': 300, 'daily_max': 2000, 'lost_ticket': False, 'lost_fee': 2500}, 0), ({'entry': 2821, 'exit': 2816, 'grace': 10, 'hourly': 200, 'daily_max': 2400, 'lost_ticket': True, 'lost_fee': 2500}, 'invalid'), ({'entry': 0, 'exit': 1560, 'grace': 10, 'hourly': 300, 'daily_max': 2000, 'lost_ticket': False, 'lost_fee': 2500}, 2600)]]
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)

prompt:
Garage hourly fee with grace and daily maximum returns a wrong result when zero-length stays are rejected as invalid.

Input {entry, exit (absolute minutes), grace, hourly, daily_max, lost_ticket, lost_fee}; cents. Exit before entry is 'invalid'. A stay no longer than grace is free (grace is not deducted from longer stays). Otherwise each full 1440-minute block costs daily_max and the remainder costs hourly per started hour, capped at daily_max. A lost ticket pays the greater of the computed fee and lost_fee.

split: open-access


meaning: Fixture-count band, not measured model difficulty

deps: stdlib

prompt:
Tiered parking rate table returns a wrong result when tiers are matched at hour granularity.

Input {minutes, tiers: [[upto minutes, cumulative price]] in any order, extra_per: [block minutes, cents], max}. A stay of 0 minutes is free. Tiers are applied in ascending upto order and a stay of exactly upto minutes belongs to that tier (price capped at max). Beyond the last tier each started block after its upto adds the block price; the whole fee is capped at max.

split: open-access


prompt:
Tiered parking rate table returns a wrong result when tiers are scanned in reverse order.

Input {minutes, tiers: [[upto minutes, cumulative price]] in any order, extra_per: [block minutes, cents], max}. A stay of 0 minutes is free. Tiers are applied in ascending upto order and a stay of exactly upto minutes belongs to that tier (price capped at max). Beyond the last tier each started block after its upto adds the block price; the whole fee is capped at max.

split: open-access


prompt:
Tiered parking rate table returns a wrong result when additional blocks are counted from time zero.

Input {minutes, tiers: [[upto minutes, cumulative price]] in any order, extra_per: [block minutes, cents], max}. A stay of 0 minutes is free. Tiers are applied in ascending upto order and a stay of exactly upto minutes belongs to that tier (price capped at max). Beyond the last tier each started block after its upto adds the block price; the whole fee is capped at max.

split: open-access


prompt:
Tiered parking rate table returns a wrong result when a zero-minute stay pays the first tier.

Input {minutes, tiers: [[upto minutes, cumulative price]] in any order, extra_per: [block minutes, cents], max}. A stay of 0 minutes is free. Tiers are applied in ascending upto order and a stay of exactly upto minutes belongs to that tier (price capped at max). Beyond the last tier each started block after its upto adds the block price; the whole fee is capped at max.

split: open-access


prompt:
Tiered parking rate table returns a wrong result when the maximum caps only the additional blocks.

Input {minutes, tiers: [[upto minutes, cumulative price]] in any order, extra_per: [block minutes, cents], max}. A stay of 0 minutes is free. Tiers are applied in ascending upto order and a stay of exactly upto minutes belongs to that tier (price capped at max). Beyond the last tier each started block after its upto adds the block price; the whole fee is capped at max.

split: open-access


prompt:
Early bird flat rate returns a wrong result when saturday is treated as a weekday.

Input {entry: [day, minute], exit: [day, minute], hourly, daily_max, early}; day % 7 == 0 is Monday. Regular fee = min(started hours*hourly, started 24h periods (at least 1)*daily_max). Early bird applies on weekdays when entry is 06:00-09:00 inclusive and exit is the same day 15:00-19:00 inclusive; the customer pays min(regular, early). Negative durations are 'invalid'.

split: open-access


prompt:
Early bird flat rate returns a wrong result when an entry at exactly 09:00 misses the early bird.

Input {entry: [day, minute], exit: [day, minute], hourly, daily_max, early}; day % 7 == 0 is Monday. Regular fee = min(started hours*hourly, started 24h periods (at least 1)*daily_max). Early bird applies on weekdays when entry is 06:00-09:00 inclusive and exit is the same day 15:00-19:00 inclusive; the customer pays min(regular, early). Negative durations are 'invalid'.

split: open-access


prompt:
Early bird flat rate returns a wrong result when overnight stays qualify for the early bird.

Input {entry: [day, minute], exit: [day, minute], hourly, daily_max, early}; day % 7 == 0 is Monday. Regular fee = min(started hours*hourly, started 24h periods (at least 1)*daily_max). Early bird applies on weekdays when entry is 06:00-09:00 inclusive and exit is the same day 15:00-19:00 inclusive; the customer pays min(regular, early). Negative durations are 'invalid'.

split: open-access


prompt:
Early bird flat rate returns a wrong result when late evening exits keep the early bird.

Input {entry: [day, minute], exit: [day, minute], hourly, daily_max, early}; day % 7 == 0 is Monday. Regular fee = min(started hours*hourly, started 24h periods (at least 1)*daily_max). Early bird applies on weekdays when entry is 06:00-09:00 inclusive and exit is the same day 15:00-19:00 inclusive; the customer pays min(regular, early). Negative durations are 'invalid'.

split: open-access


prompt:
Early bird flat rate returns a wrong result when the early bird is compared with the uncapped hourly fee.

Input {entry: [day, minute], exit: [day, minute], hourly, daily_max, early}; day % 7 == 0 is Monday. Regular fee = min(started hours*hourly, started 24h periods (at least 1)*daily_max). Early bird applies on weekdays when entry is 06:00-09:00 inclusive and exit is the same day 15:00-19:00 inclusive; the customer pays min(regular, early). Negative durations are 'invalid'.

split: open-access


prompt:
Early bird flat rate returns a wrong result when an exact 24 hour stay counts as two days.

Input {entry: [day, minute], exit: [day, minute], hourly, daily_max, early}; day % 7 == 0 is Monday. Regular fee = min(started hours*hourly, started 24h periods (at least 1)*daily_max). Early bird applies on weekdays when entry is 06:00-09:00 inclusive and exit is the same day 15:00-19:00 inclusive; the customer pays min(regular, early). Negative durations are 'invalid'.

split: open-access


prompt:
Time-band rate with calendar day cap returns a wrong result when each band segment is rounded up separately.

Input {bands: [[start minute of day, cents per quarter hour]] sorted with first start 0, entry, exit (absolute minutes), daily_max}. Minutes of the stay are totalled per calendar day and band; each total is rounded up to whole quarter hours and priced at its band rate. Each calendar day's charge is capped at daily_max.

split: open-access


prompt:
Time-band rate with calendar day cap returns a wrong result when band minutes are rounded down to quarters.

Input {bands: [[start minute of day, cents per quarter hour]] sorted with first start 0, entry, exit (absolute minutes), daily_max}. Minutes of the stay are totalled per calendar day and band; each total is rounded up to whole quarter hours and priced at its band rate. Each calendar day's charge is capped at daily_max.

split: open-access


prompt:
Time-band rate with calendar day cap returns a wrong result when the last segment runs to the band boundary.

Input {bands: [[start minute of day, cents per quarter hour]] sorted with first start 0, entry, exit (absolute minutes), daily_max}. Minutes of the stay are totalled per calendar day and band; each total is rounded up to whole quarter hours and priced at its band rate. Each calendar day's charge is capped at daily_max.

split: open-access


prompt:
Time-band rate with calendar day cap returns a wrong result when the daily maximum caps the whole stay once.

Input {bands: [[start minute of day, cents per quarter hour]] sorted with first start 0, entry, exit (absolute minutes), daily_max}. Minutes of the stay are totalled per calendar day and band; each total is rounded up to whole quarter hours and priced at its band rate. Each calendar day's charge is capped at daily_max.

split: open-access


prompt:
Time-band rate with calendar day cap returns a wrong result when days are counted as 24 hour periods from entry.

Input {bands: [[start minute of day, cents per quarter hour]] sorted with first start 0, entry, exit (absolute minutes), daily_max}. Minutes of the stay are totalled per calendar day and band; each total is rounded up to whole quarter hours and priced at its band rate. Each calendar day's charge is capped at daily_max.

split: open-access


broken_source:
"""Failure Map reference implementation. Python standard library only."""
import json

N = 1
observations = []
def solve(x):
    inc = x['increment']
    c = x['paid_cents']
    now = x['now']
    if now >= x['enf_end']:
        return [None, 0]
    base = max(now, x['enf_start'])
    start = base if x['expiry'] is None else max(base, x['expiry'])
    mins = c * 60 // x['rate_per_hour']
    end = min(start + mins, base + x['max_minutes'], x['enf_end'])
    used = max(0, end - start)
    cost = min(c, -(-used * x['rate_per_hour'] // 60))
    return [max(end, start), cost]
def check(label, actual, expected):
    observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
fixtures = [[({'now': 1080, 'expiry': 1117, 'paid_cents': 337, 'rate_per_hour': 100, 'max_minutes': 240, 'enf_start': 480, 'enf_end': 1080, 'increment': 5}, [None, 0]), ({'now': 420, 'expiry': None, 'paid_cents': 200, 'rate_per_hour': 200, 'max_minutes': 120, 'enf_start': 480, 'enf_end': 1080, 'increment': 25}, [540, 200]), ({'now': 662, 'expiry': 661, 'paid_cents': 587, 'rate_per_hour': 200, 'max_minutes': 240, 'enf_start': 480, 'enf_end': 1080, 'increment': 5}, [837, 584]), ({'now': 480, 'expiry': 471, 'paid_cents': 576, 'rate_per_hour': 200, 'max_minutes': 60, 'enf_start': 480, 'enf_end': 1080, 'increment': 5}, [540, 200]), ({'now': 1040, 'expiry': None, 'paid_cents': 333, 'rate_per_hour': 150, 'max_minutes': 120, 'enf_start': 480, 'enf_end': 1080, 'increment': 25}, [1080, 100]), ({'now': 768, 'expiry': 755, 'paid_cents': 387, 'rate_per_hour': 150, 'max_minutes': 240, 'enf_start': 480, 'enf_end': 1080, 'increment': 25}, [918, 375]), ({'now': 480, 'expiry': 461, 'paid_cents': 174, 'rate_per_hour': 200, 'max_minutes': 240, 'enf_start': 480, 'enf_end': 1080, 'increment': 5}, [531, 170]), ({'now': 600, 'expiry': 650, 'paid_cents': 400, 'rate_per_hour': 200, 'max_minutes': 120, 'enf_start': 480, 'enf_end': 1080, 'increment': 5}, [720, 234])], [({'now': 420, 'expiry': None, 'paid_cents': 200, 'rate_per_hour': 200, 'max_minutes': 120, 'enf_start': 480, 'enf_end': 1080, 'increment': 25}, [540, 200]), ({'now': 634, 'expiry': None, 'paid_cents': 224, 'rate_per_hour': 150, 'max_minutes': 240, 'enf_start': 480, 'enf_end': 1080, 'increment': 25}, [714, 200]), ({'now': 1103, 'expiry': 1153, 'paid_cents': 441, 'rate_per_hour': 150, 'max_minutes': 120, 'enf_start': 480, 'enf_end': 1080, 'increment': 25}, [None, 0]), ({'now': 472, 'expiry': None, 'paid_cents': 54, 'rate_per_hour': 200, 'max_minutes': 60, 'enf_start': 480, 'enf_end': 1080, 'increment': 5}, [495, 50]), ({'now': 737, 'expiry': None, 'paid_cents': 305, 'rate_per_hour': 100, 'max_minutes': 240, 'enf_start': 480, 'enf_end': 1080, 'increment': 25}, [917, 300]), ({'now': 1080, 'expiry': 1100, 'paid_cents': 100, 'rate_per_hour': 200, 'max_minutes': 120, 'enf_start': 480, 'enf_end': 1080, 'increment': 25}, [None, 0]), ({'now': 1040, 'expiry': None, 'paid_cents': 333, 'rate_per_hour': 150, 'max_minutes': 120, 'enf_start': 480, 'enf_end': 1080, 'increment': 25}, [1080, 100]), ({'now': 1080, 'expiry': 1075, 'paid_cents': 372, 'rate_per_hour': 200, 'max_minutes': 120, 'enf_start': 480, 'enf_end': 1080, 'increment': 25}, [None, 0])], [({'now': 420, 'expiry': None, 'paid_cents': 200, 'rate_per_hour': 200, 'max_minutes': 120, 'enf_start': 480, 'enf_end': 1080, 'increment': 25}, [540, 200]), ({'now': 480, 'expiry': 540, 'paid_cents': 241, 'rate_per_hour': 150, 'max_minutes': 60, 'enf_start': 480, 'enf_end': 1080, 'increment': 25}, [540, 0]), ({'now': 1040, 'expiry': None, 'paid_cents': 333, 'rate_per_hour': 150, 'max_minutes': 120, 'enf_start': 480, 'enf_end': 1080, 'increment': 25}, [1080, 100]), ({'now': 480, 'expiry': None, 'paid_cents': 195, 'rate_per_hour': 100, 'max_minutes': 120, 'enf_start': 480, 'enf_end': 1080, 'increment': 25}, [585, 175]), ({'now': 1192, 'expiry': None, 'paid_cents': 253, 'rate_per_hour': 250, 'max_minutes': 240, 'enf_start': 480, 'enf_end': 1080, 'increment': 25}, [None, 0]), ({'now': 480, 'expiry': 518, 'paid_cents': 159, 'rate_per_hour': 250, 'max_minutes': 120, 'enf_start': 480, 'enf_end': 1080, 'increment': 25}, [554, 150]), ({'now': 1080, 'expiry': 1100, 'paid_cents': 100, 'rate_per_hour': 200, 'max_minutes': 120, 'enf_start': 480, 'enf_end': 1080, 'increment': 25}, [None, 0]), ({'now': 430, 'expiry': None, 'paid_cents': 294, 'rate_per_hour': 100, 'max_minutes': 240, 'enf_start': 480, 'enf_end': 1080, 'increment': 25}, [645, 275])], [({'now': 1080, 'expiry': 1100, 'paid_cents': 100, 'rate_per_hour': 200, 'max_minutes': 120, 'enf_start': 480, 'enf_end': 1080, 'increment': 25}, [None, 0]), ({'now': 641, 'expiry': None, 'paid_cents': 104, 'rate_per_hour': 250, 'max_minutes': 240, 'enf_start': 480, 'enf_end': 1080, 'increment': 25}, [665, 100]), ({'now': 480, 'expiry': 462, 'paid_cents': 171, 'rate_per_hour': 150, 'max_minutes': 120, 'enf_start': 480, 'enf_end': 1080, 'increment': 25}, [540, 150]), ({'now': 1040, 'expiry': None, 'paid_cents': 333, 'rate_per_hour': 150, 'max_minutes': 120, 'enf_start': 480, 'enf_end': 1080, 'increment': 25}, [1080, 100]), ({'now': 480, 'expiry': 540, 'paid_cents': 124, 'rate_per_hour': 150, 'max_minutes': 240, 'enf_start': 480, 'enf_end': 1080, 'increment': 5}, [588, 120]), ({'now': 1098, 'expiry': None, 'paid_cents': 140, 'rate_per_hour': 100, 'max_minutes': 240, 'enf_start': 480, 'enf_end': 1080, 'increment': 5}, [None, 0]), ({'now': 480, 'expiry': None, 'paid_cents': 104, 'rate_per_hour': 150, 'max_minutes': 60, 'enf_start': 480, 'enf_end': 1080, 'increment': 25}, [520, 100]), ({'now': 600, 'expiry': 650, 'paid_cents': 400, 'rate_per_hour': 200, 'max_minutes': 120, 'enf_start': 480, 'enf_end': 1080, 'increment': 5}, [720, 234])], [({'now': 1040, 'expiry': None, 'paid_cents': 333, 'rate_per_hour': 150, 'max_minutes': 120, 'enf_start': 480, 'enf_end': 1080, 'increment': 25}, [1080, 100]), ({'now': 913, 'expiry': 880, 'paid_cents': 89, 'rate_per_hour': 100, 'max_minutes': 120, 'enf_start': 480, 'enf_end': 1080, 'increment': 25}, [958, 75]), ({'now': 850, 'expiry': 827, 'paid_cents': 491, 'rate_per_hour': 100, 'max_minutes': 120, 'enf_start': 480, 'enf_end': 1080, 'increment': 5}, [970, 200]), ({'now': 318, 'expiry': 269, 'paid_cents': 362, 'rate_per_hour': 200, 'max_minutes': 240, 'enf_start': 480, 'enf_end': 1080, 'increment': 25}, [585, 350]), ({'now': 420, 'expiry': None, 'paid_cents': 200, 'rate_per_hour': 200, 'max_minutes': 120, 'enf_start': 480, 'enf_end': 1080, 'increment': 25}, [540, 200]), ({'now': 956, 'expiry': 942, 'paid_cents': 122, 'rate_per_hour': 100, 'max_minutes': 120, 'enf_start': 480, 'enf_end': 1080, 'increment': 5}, [1028, 120]), ({'now': 40, 'expiry': 116, 'paid_cents': 331, 'rate_per_hour': 250, 'max_minutes': 60, 'enf_start': 480, 'enf_end': 1080, 'increment': 25}, [540, 250]), ({'now': 1080, 'expiry': 1100, 'paid_cents': 100, 'rate_per_hour': 200, 'max_minutes': 120, 'enf_start': 480, 'enf_end': 1080, 'increment': 25}, [None, 0])]]
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)

source:
"""Failure Map reference implementation. Python standard library only."""
import json

N = 1
observations = []
def solve(x):
    inc = x['increment']
    c = -(-x['paid_cents'] // inc) * inc
    now = x['now']
    if now >= x['enf_end']:
        return [None, 0]
    base = max(now, x['enf_start'])
    start = base if x['expiry'] is None else max(base, x['expiry'])
    mins = c * 60 // x['rate_per_hour']
    end = min(start + mins, base + x['max_minutes'], x['enf_end'])
    used = max(0, end - start)
    cost = min(c, -(-used * x['rate_per_hour'] // 60))
    return [max(end, start), cost]
def check(label, actual, expected):
    observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
fixtures = [[({'now': 1080, 'expiry': 1117, 'paid_cents': 337, 'rate_per_hour': 100, 'max_minutes': 240, 'enf_start': 480, 'enf_end': 1080, 'increment': 5}, [None, 0]), ({'now': 420, 'expiry': None, 'paid_cents': 200, 'rate_per_hour': 200, 'max_minutes': 120, 'enf_start': 480, 'enf_end': 1080, 'increment': 25}, [540, 200]), ({'now': 662, 'expiry': 661, 'paid_cents': 587, 'rate_per_hour': 200, 'max_minutes': 240, 'enf_start': 480, 'enf_end': 1080, 'increment': 5}, [837, 584]), ({'now': 480, 'expiry': 471, 'paid_cents': 576, 'rate_per_hour': 200, 'max_minutes': 60, 'enf_start': 480, 'enf_end': 1080, 'increment': 5}, [540, 200]), ({'now': 1040, 'expiry': None, 'paid_cents': 333, 'rate_per_hour': 150, 'max_minutes': 120, 'enf_start': 480, 'enf_end': 1080, 'increment': 25}, [1080, 100]), ({'now': 768, 'expiry': 755, 'paid_cents': 387, 'rate_per_hour': 150, 'max_minutes': 240, 'enf_start': 480, 'enf_end': 1080, 'increment': 25}, [918, 375]), ({'now': 480, 'expiry': 461, 'paid_cents': 174, 'rate_per_hour': 200, 'max_minutes': 240, 'enf_start': 480, 'enf_end': 1080, 'increment': 5}, [531, 170]), ({'now': 600, 'expiry': 650, 'paid_cents': 400, 'rate_per_hour': 200, 'max_minutes': 120, 'enf_start': 480, 'enf_end': 1080, 'increment': 5}, [720, 234])], [({'now': 420, 'expiry': None, 'paid_cents': 200, 'rate_per_hour': 200, 'max_minutes': 120, 'enf_start': 480, 'enf_end': 1080, 'increment': 25}, [540, 200]), ({'now': 634, 'expiry': None, 'paid_cents': 224, 'rate_per_hour': 150, 'max_minutes': 240, 'enf_start': 480, 'enf_end': 1080, 'increment': 25}, [714, 200]), ({'now': 1103, 'expiry': 1153, 'paid_cents': 441, 'rate_per_hour': 150, 'max_minutes': 120, 'enf_start': 480, 'enf_end': 1080, 'increment': 25}, [None, 0]), ({'now': 472, 'expiry': None, 'paid_cents': 54, 'rate_per_hour': 200, 'max_minutes': 60, 'enf_start': 480, 'enf_end': 1080, 'increment': 5}, [495, 50]), ({'now': 737, 'expiry': None, 'paid_cents': 305, 'rate_per_hour': 100, 'max_minutes': 240, 'enf_start': 480, 'enf_end': 1080, 'increment': 25}, [917, 300]), ({'now': 1080, 'expiry': 1100, 'paid_cents': 100, 'rate_per_hour': 200, 'max_minutes': 120, 'enf_start': 480, 'enf_end': 1080, 'increment': 25}, [None, 0]), ({'now': 1040, 'expiry': None, 'paid_cents': 333, 'rate_per_hour': 150, 'max_minutes': 120, 'enf_start': 480, 'enf_end': 1080, 'increment': 25}, [1080, 100]), ({'now': 1080, 'expiry': 1075, 'paid_cents': 372, 'rate_per_hour': 200, 'max_minutes': 120, 'enf_start': 480, 'enf_end': 1080, 'increment': 25}, [None, 0])], [({'now': 420, 'expiry': None, 'paid_cents': 200, 'rate_per_hour': 200, 'max_minutes': 120, 'enf_start': 480, 'enf_end': 1080, 'increment': 25}, [540, 200]), ({'now': 480, 'expiry': 540, 'paid_cents': 241, 'rate_per_hour': 150, 'max_minutes': 60, 'enf_start': 480, 'enf_end': 1080, 'increment': 25}, [540, 0]), ({'now': 1040, 'expiry': None, 'paid_cents': 333, 'rate_per_hour': 150, 'max_minutes': 120, 'enf_start': 480, 'enf_end': 1080, 'increment': 25}, [1080, 100]), ({'now': 480, 'expiry': None, 'paid_cents': 195, 'rate_per_hour': 100, 'max_minutes': 120, 'enf_start': 480, 'enf_end': 1080, 'increment': 25}, [585, 175]), ({'now': 1192, 'expiry': None, 'paid_cents': 253, 'rate_per_hour': 250, 'max_minutes': 240, 'enf_start': 480, 'enf_end': 1080, 'increment': 25}, [None, 0]), ({'now': 480, 'expiry': 518, 'paid_cents': 159, 'rate_per_hour': 250, 'max_minutes': 120, 'enf_start': 480, 'enf_end': 1080, 'increment': 25}, [554, 150]), ({'now': 1080, 'expiry': 1100, 'paid_cents': 100, 'rate_per_hour': 200, 'max_minutes': 120, 'enf_start': 480, 'enf_end': 1080, 'increment': 25}, [None, 0]), ({'now': 430, 'expiry': None, 'paid_cents': 294, 'rate_per_hour': 100, 'max_minutes': 240, 'enf_start': 480, 'enf_end': 1080, 'increment': 25}, [645, 275])], [({'now': 1080, 'expiry': 1100, 'paid_cents': 100, 'rate_per_hour': 200, 'max_minutes': 120, 'enf_start': 480, 'enf_end': 1080, 'increment': 25}, [None, 0]), ({'now': 641, 'expiry': None, 'paid_cents': 104, 'rate_per_hour': 250, 'max_minutes': 240, 'enf_start': 480, 'enf_end': 1080, 'increment': 25}, [665, 100]), ({'now': 480, 'expiry': 462, 'paid_cents': 171, 'rate_per_hour': 150, 'max_minutes': 120, 'enf_start': 480, 'enf_end': 1080, 'increment': 25}, [540, 150]), ({'now': 1040, 'expiry': None, 'paid_cents': 333, 'rate_per_hour': 150, 'max_minutes': 120, 'enf_start': 480, 'enf_end': 1080, 'increment': 25}, [1080, 100]), ({'now': 480, 'expiry': 540, 'paid_cents': 124, 'rate_per_hour': 150, 'max_minutes': 240, 'enf_start': 480, 'enf_end': 1080, 'increment': 5}, [588, 120]), ({'now': 1098, 'expiry': None, 'paid_cents': 140, 'rate_per_hour': 100, 'max_minutes': 240, 'enf_start': 480, 'enf_end': 1080, 'increment': 5}, [None, 0]), ({'now': 480, 'expiry': None, 'paid_cents': 104, 'rate_per_hour': 150, 'max_minutes': 60, 'enf_start': 480, 'enf_end': 1080, 'increment': 25}, [520, 100]), ({'now': 600, 'expiry': 650, 'paid_cents': 400, 'rate_per_hour': 200, 'max_minutes': 120, 'enf_start': 480, 'enf_end': 1080, 'increment': 5}, [720, 234])], [({'now': 1040, 'expiry': None, 'paid_cents': 333, 'rate_per_hour': 150, 'max_minutes': 120, 'enf_start': 480, 'enf_end': 1080, 'increment': 25}, [1080, 100]), ({'now': 913, 'expiry': 880, 'paid_cents': 89, 'rate_per_hour': 100, 'max_minutes': 120, 'enf_start': 480, 'enf_end': 1080, 'increment': 25}, [958, 75]), ({'now': 850, 'expiry': 827, 'paid_cents': 491, 'rate_per_hour': 100, 'max_minutes': 120, 'enf_start': 480, 'enf_end': 1080, 'increment': 5}, [970, 200]), ({'now': 318, 'expiry': 269, 'paid_cents': 362, 'rate_per_hour': 200, 'max_minutes': 240, 'enf_start': 480, 'enf_end': 1080, 'increment': 25}, [585, 350]), ({'now': 420, 'expiry': None, 'paid_cents': 200, 'rate_per_hour': 200, 'max_minutes': 120, 'enf_start': 480, 'enf_end': 1080, 'increment': 25}, [540, 200]), ({'now': 956, 'expiry': 942, 'paid_cents': 122, 'rate_per_hour': 100, 'max_minutes': 120, 'enf_start': 480, 'enf_end': 1080, 'increment': 5}, [1028, 120]), ({'now': 40, 'expiry': 116, 'paid_cents': 331, 'rate_per_hour': 250, 'max_minutes': 60, 'enf_start': 480, 'enf_end': 1080, 'increment': 25}, [540, 250]), ({'now': 1080, 'expiry': 1100, 'paid_cents': 100, 'rate_per_hour': 200, 'max_minutes': 120, 'enf_start': 480, 'enf_end': 1080, 'increment': 25}, [None, 0])]]
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)

prompt:
Street meter time purchase returns a wrong result when partial increments are credited.

Input {now, expiry (or null), paid_cents, rate_per_hour, max_minutes, enf_start, enf_end, increment}. Only whole increments are credited. At or after enforcement end nothing is sold: [null, 0]. Paid time starts at the later of now, enforcement start and any unexpired expiry; it ends at the earliest of start + purchased minutes (floor), max(now, enf_start) + max_minutes, and enf_end. Accepted cents = min(credited, ceil(used minutes * rate/60)). Return [new expiry, accepted cents].

split: open-access


broken_source:
"""Failure Map reference implementation. Python standard library only."""
import json

N = 1
observations = []
def solve(x):
    inc = x['increment']
    c = x['paid_cents'] - x['paid_cents'] % inc
    now = x['now']
    if now >= x['enf_end']:
        return [None, 0]
    base = now
    start = base if x['expiry'] is None else max(base, x['expiry'])
    mins = c * 60 // x['rate_per_hour']
    end = min(start + mins, base + x['max_minutes'], x['enf_end'])
    used = max(0, end - start)
    cost = min(c, -(-used * x['rate_per_hour'] // 60))
    return [max(end, start), cost]
def check(label, actual, expected):
    observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
fixtures = [[({'now': 459, 'expiry': None, 'paid_cents': 517, 'rate_per_hour': 100, 'max_minutes': 240, 'enf_start': 480, 'enf_end': 1080, 'increment': 5}, [720, 400]), ({'now': 1080, 'expiry': 1100, 'paid_cents': 100, 'rate_per_hour': 200, 'max_minutes': 120, 'enf_start': 480, 'enf_end': 1080, 'increment': 25}, [None, 0]), ({'now': 600, 'expiry': 650, 'paid_cents': 400, 'rate_per_hour': 200, 'max_minutes': 120, 'enf_start': 480, 'enf_end': 1080, 'increment': 5}, [720, 234]), ({'now': 1040, 'expiry': None, 'paid_cents': 333, 'rate_per_hour': 150, 'max_minutes': 120, 'enf_start': 480, 'enf_end': 1080, 'increment': 25}, [1080, 100]), ({'now': 479, 'expiry': None, 'paid_cents': 114, 'rate_per_hour': 150, 'max_minutes': 120, 'enf_start': 480, 'enf_end': 1080, 'increment': 5}, [524, 110]), ({'now': 427, 'expiry': 476, 'paid_cents': 417, 'rate_per_hour': 150, 'max_minutes': 120, 'enf_start': 480, 'enf_end': 1080, 'increment': 5}, [600, 300]), ({'now': 1004, 'expiry': None, 'paid_cents': 562, 'rate_per_hour': 100, 'max_minutes': 120, 'enf_start': 480, 'enf_end': 1080, 'increment': 5}, [1080, 127]), ({'now': 414, 'expiry': 388, 'paid_cents': 65, 'rate_per_hour': 200, 'max_minutes': 240, 'enf_start': 480, 'enf_end': 1080, 'increment': 25}, [495, 50])], [({'now': 1080, 'expiry': 1100, 'paid_cents': 100, 'rate_per_hour': 200, 'max_minutes': 120, 'enf_start': 480, 'enf_end': 1080, 'increment': 25}, [None, 0]), ({'now': 600, 'expiry': 650, 'paid_cents': 400, 'rate_per_hour': 200, 'max_minutes': 120, 'enf_start': 480, 'enf_end': 1080, 'increment': 5}, [720, 234]), ({'now': 81, 'expiry': 101, 'paid_cents': 151, 'rate_per_hour': 250, 'max_minutes': 240, 'enf_start': 480, 'enf_end': 1080, 'increment': 5}, [516, 150]), ({'now': 480, 'expiry': None, 'paid_cents': 99, 'rate_per_hour': 250, 'max_minutes': 120, 'enf_start': 480, 'enf_end': 1080, 'increment': 25}, [498, 75]), ({'now': 420, 'expiry': None, 'paid_cents': 200, 'rate_per_hour': 200, 'max_minutes': 120, 'enf_start': 480, 'enf_end': 1080, 'increment': 25}, [540, 200]), ({'now': 1080, 'expiry': 1061, 'paid_cents': 149, 'rate_per_hour': 100, 'max_minutes': 60, 'enf_start': 480, 'enf_end': 1080, 'increment': 5}, [None, 0]), ({'now': 65, 'expiry': 152, 'paid_cents': 24, 'rate_per_hour': 100, 'max_minutes': 240, 'enf_start': 480, 'enf_end': 1080, 'increment': 25}, [480, 0]), ({'now': 480, 'expiry': 502, 'paid_cents': 468, 'rate_per_hour': 200, 'max_minutes': 120, 'enf_start': 480, 'enf_end': 1080, 'increment': 5}, [600, 327])], [({'now': 1040, 'expiry': None, 'paid_cents': 333, 'rate_per_hour': 150, 'max_minutes': 120, 'enf_start': 480, 'enf_end': 1080, 'increment': 25}, [1080, 100]), ({'now': 449, 'expiry': None, 'paid_cents': 279, 'rate_per_hour': 250, 'max_minutes': 240, 'enf_start': 480, 'enf_end': 1080, 'increment': 25}, [546, 275]), ({'now': 420, 'expiry': None, 'paid_cents': 200, 'rate_per_hour': 200, 'max_minutes': 120, 'enf_start': 480, 'enf_end': 1080, 'increment': 25}, [540, 200]), ({'now': 1080, 'expiry': 1056, 'paid_cents': 124, 'rate_per_hour': 150, 'max_minutes': 60, 'enf_start': 480, 'enf_end': 1080, 'increment': 5}, [None, 0]), ({'now': 441, 'expiry': 437, 'paid_cents': 475, 'rate_per_hour': 100, 'max_minutes': 240, 'enf_start': 480, 'enf_end': 1080, 'increment': 25}, [720, 400]), ({'now': 600, 'expiry': 650, 'paid_cents': 400, 'rate_per_hour': 200, 'max_minutes': 120, 'enf_start': 480, 'enf_end': 1080, 'increment': 5}, [720, 234]), ({'now': 1177, 'expiry': None, 'paid_cents': 418, 'rate_per_hour': 100, 'max_minutes': 240, 'enf_start': 480, 'enf_end': 1080, 'increment': 5}, [None, 0]), ({'now': 327, 'expiry': 289, 'paid_cents': 186, 'rate_per_hour': 200, 'max_minutes': 240, 'enf_start': 480, 'enf_end': 1080, 'increment': 25}, [532, 174])], [({'now': 751, 'expiry': None, 'paid_cents': 37, 'rate_per_hour': 250, 'max_minutes': 60, 'enf_start': 480, 'enf_end': 1080, 'increment': 5}, [759, 34]), ({'now': 420, 'expiry': None, 'paid_cents': 200, 'rate_per_hour': 200, 'max_minutes': 120, 'enf_start': 480, 'enf_end': 1080, 'increment': 25}, [540, 200]), ({'now': 420, 'expiry': 426, 'paid_cents': 444, 'rate_per_hour': 250, 'max_minutes': 240, 'enf_start': 480, 'enf_end': 1080, 'increment': 25}, [582, 425]), ({'now': 1080, 'expiry': 1101, 'paid_cents': 444, 'rate_per_hour': 150, 'max_minutes': 120, 'enf_start': 480, 'enf_end': 1080, 'increment': 25}, [None, 0]), ({'now': 1040, 'expiry': None, 'paid_cents': 333, 'rate_per_hour': 150, 'max_minutes': 120, 'enf_start': 480, 'enf_end': 1080, 'increment': 25}, [1080, 100]), ({'now': 402, 'expiry': 435, 'paid_cents': 29, 'rate_per_hour': 150, 'max_minutes': 60, 'enf_start': 480, 'enf_end': 1080, 'increment': 25}, [490, 25]), ({'now': 1001, 'expiry': 1047, 'paid_cents': 331, 'rate_per_hour': 250, 'max_minutes': 60, 'enf_start': 480, 'enf_end': 1080, 'increment': 25}, [1061, 59]), ({'now': 1080, 'expiry': 1100, 'paid_cents': 100, 'rate_per_hour': 200, 'max_minutes': 120, 'enf_start': 480, 'enf_end': 1080, 'increment': 25}, [None, 0])], [({'now': 1060, 'expiry': 1107, 'paid_cents': 414, 'rate_per_hour': 100, 'max_minutes': 120, 'enf_start': 480, 'enf_end': 1080, 'increment': 25}, [1107, 0]), ({'now': 47, 'expiry': 84, 'paid_cents': 192, 'rate_per_hour': 200, 'max_minutes': 60, 'enf_start': 480, 'enf_end': 1080, 'increment': 5}, [537, 190]), ({'now': 1040, 'expiry': None, 'paid_cents': 333, 'rate_per_hour': 150, 'max_minutes': 120, 'enf_start': 480, 'enf_end': 1080, 'increment': 25}, [1080, 100]), ({'now': 147, 'expiry': 118, 'paid_cents': 14, 'rate_per_hour': 250, 'max_minutes': 120, 'enf_start': 480, 'enf_end': 1080, 'increment': 25}, [480, 0]), ({'now': 862, 'expiry': 851, 'paid_cents': 76, 'rate_per_hour': 200, 'max_minutes': 60, 'enf_start': 480, 'enf_end': 1080, 'increment': 25}, [884, 74]), ({'now': 420, 'expiry': None, 'paid_cents': 200, 'rate_per_hour': 200, 'max_minutes': 120, 'enf_start': 480, 'enf_end': 1080, 'increment': 25}, [540, 200]), ({'now': 600, 'expiry': 650, 'paid_cents': 400, 'rate_per_hour': 200, 'max_minutes': 120, 'enf_start': 480, 'enf_end': 1080, 'increment': 5}, [720, 234]), ({'now': 45, 'expiry': None, 'paid_cents': 260, 'rate_per_hour': 250, 'max_minutes': 240, 'enf_start': 480, 'enf_end': 1080, 'increment': 5}, [542, 259])]]
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)

source:
"""Failure Map reference implementation. Python standard library only."""
import json

N = 1
observations = []
def solve(x):
    inc = x['increment']
    c = x['paid_cents'] - x['paid_cents'] % inc
    now = x['now']
    if now >= x['enf_end']:
        return [None, 0]
    base = max(now, x['enf_start']) if x['expiry'] is None else now
    start = base if x['expiry'] is None else max(base, x['expiry'])
    mins = c * 60 // x['rate_per_hour']
    end = min(start + mins, base + x['max_minutes'], x['enf_end'])
    used = max(0, end - start)
    cost = min(c, -(-used * x['rate_per_hour'] // 60))
    return [max(end, start), cost]
def check(label, actual, expected):
    observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
fixtures = [[({'now': 459, 'expiry': None, 'paid_cents': 517, 'rate_per_hour': 100, 'max_minutes': 240, 'enf_start': 480, 'enf_end': 1080, 'increment': 5}, [720, 400]), ({'now': 1080, 'expiry': 1100, 'paid_cents': 100, 'rate_per_hour': 200, 'max_minutes': 120, 'enf_start': 480, 'enf_end': 1080, 'increment': 25}, [None, 0]), ({'now': 600, 'expiry': 650, 'paid_cents': 400, 'rate_per_hour': 200, 'max_minutes': 120, 'enf_start': 480, 'enf_end': 1080, 'increment': 5}, [720, 234]), ({'now': 1040, 'expiry': None, 'paid_cents': 333, 'rate_per_hour': 150, 'max_minutes': 120, 'enf_start': 480, 'enf_end': 1080, 'increment': 25}, [1080, 100]), ({'now': 479, 'expiry': None, 'paid_cents': 114, 'rate_per_hour': 150, 'max_minutes': 120, 'enf_start': 480, 'enf_end': 1080, 'increment': 5}, [524, 110]), ({'now': 427, 'expiry': 476, 'paid_cents': 417, 'rate_per_hour': 150, 'max_minutes': 120, 'enf_start': 480, 'enf_end': 1080, 'increment': 5}, [600, 300]), ({'now': 1004, 'expiry': None, 'paid_cents': 562, 'rate_per_hour': 100, 'max_minutes': 120, 'enf_start': 480, 'enf_end': 1080, 'increment': 5}, [1080, 127]), ({'now': 414, 'expiry': 388, 'paid_cents': 65, 'rate_per_hour': 200, 'max_minutes': 240, 'enf_start': 480, 'enf_end': 1080, 'increment': 25}, [495, 50])], [({'now': 1080, 'expiry': 1100, 'paid_cents': 100, 'rate_per_hour': 200, 'max_minutes': 120, 'enf_start': 480, 'enf_end': 1080, 'increment': 25}, [None, 0]), ({'now': 600, 'expiry': 650, 'paid_cents': 400, 'rate_per_hour': 200, 'max_minutes': 120, 'enf_start': 480, 'enf_end': 1080, 'increment': 5}, [720, 234]), ({'now': 81, 'expiry': 101, 'paid_cents': 151, 'rate_per_hour': 250, 'max_minutes': 240, 'enf_start': 480, 'enf_end': 1080, 'increment': 5}, [516, 150]), ({'now': 480, 'expiry': None, 'paid_cents': 99, 'rate_per_hour': 250, 'max_minutes': 120, 'enf_start': 480, 'enf_end': 1080, 'increment': 25}, [498, 75]), ({'now': 420, 'expiry': None, 'paid_cents': 200, 'rate_per_hour': 200, 'max_minutes': 120, 'enf_start': 480, 'enf_end': 1080, 'increment': 25}, [540, 200]), ({'now': 1080, 'expiry': 1061, 'paid_cents': 149, 'rate_per_hour': 100, 'max_minutes': 60, 'enf_start': 480, 'enf_end': 1080, 'increment': 5}, [None, 0]), ({'now': 65, 'expiry': 152, 'paid_cents': 24, 'rate_per_hour': 100, 'max_minutes': 240, 'enf_start': 480, 'enf_end': 1080, 'increment': 25}, [480, 0]), ({'now': 480, 'expiry': 502, 'paid_cents': 468, 'rate_per_hour': 200, 'max_minutes': 120, 'enf_start': 480, 'enf_end': 1080, 'increment': 5}, [600, 327])], [({'now': 1040, 'expiry': None, 'paid_cents': 333, 'rate_per_hour': 150, 'max_minutes': 120, 'enf_start': 480, 'enf_end': 1080, 'increment': 25}, [1080, 100]), ({'now': 449, 'expiry': None, 'paid_cents': 279, 'rate_per_hour': 250, 'max_minutes': 240, 'enf_start': 480, 'enf_end': 1080, 'increment': 25}, [546, 275]), ({'now': 420, 'expiry': None, 'paid_cents': 200, 'rate_per_hour': 200, 'max_minutes': 120, 'enf_start': 480, 'enf_end': 1080, 'increment': 25}, [540, 200]), ({'now': 1080, 'expiry': 1056, 'paid_cents': 124, 'rate_per_hour': 150, 'max_minutes': 60, 'enf_start': 480, 'enf_end': 1080, 'increment': 5}, [None, 0]), ({'now': 441, 'expiry': 437, 'paid_cents': 475, 'rate_per_hour': 100, 'max_minutes': 240, 'enf_start': 480, 'enf_end': 1080, 'increment': 25}, [720, 400]), ({'now': 600, 'expiry': 650, 'paid_cents': 400, 'rate_per_hour': 200, 'max_minutes': 120, 'enf_start': 480, 'enf_end': 1080, 'increment': 5}, [720, 234]), ({'now': 1177, 'expiry': None, 'paid_cents': 418, 'rate_per_hour': 100, 'max_minutes': 240, 'enf_start': 480, 'enf_end': 1080, 'increment': 5}, [None, 0]), ({'now': 327, 'expiry': 289, 'paid_cents': 186, 'rate_per_hour': 200, 'max_minutes': 240, 'enf_start': 480, 'enf_end': 1080, 'increment': 25}, [532, 174])], [({'now': 751, 'expiry': None, 'paid_cents': 37, 'rate_per_hour': 250, 'max_minutes': 60, 'enf_start': 480, 'enf_end': 1080, 'increment': 5}, [759, 34]), ({'now': 420, 'expiry': None, 'paid_cents': 200, 'rate_per_hour': 200, 'max_minutes': 120, 'enf_start': 480, 'enf_end': 1080, 'increment': 25}, [540, 200]), ({'now': 420, 'expiry': 426, 'paid_cents': 444, 'rate_per_hour': 250, 'max_minutes': 240, 'enf_start': 480, 'enf_end': 1080, 'increment': 25}, [582, 425]), ({'now': 1080, 'expiry': 1101, 'paid_cents': 444, 'rate_per_hour': 150, 'max_minutes': 120, 'enf_start': 480, 'enf_end': 1080, 'increment': 25}, [None, 0]), ({'now': 1040, 'expiry': None, 'paid_cents': 333, 'rate_per_hour': 150, 'max_minutes': 120, 'enf_start': 480, 'enf_end': 1080, 'increment': 25}, [1080, 100]), ({'now': 402, 'expiry': 435, 'paid_cents': 29, 'rate_per_hour': 150, 'max_minutes': 60, 'enf_start': 480, 'enf_end': 1080, 'increment': 25}, [490, 25]), ({'now': 1001, 'expiry': 1047, 'paid_cents': 331, 'rate_per_hour': 250, 'max_minutes': 60, 'enf_start': 480, 'enf_end': 1080, 'increment': 25}, [1061, 59]), ({'now': 1080, 'expiry': 1100, 'paid_cents': 100, 'rate_per_hour': 200, 'max_minutes': 120, 'enf_start': 480, 'enf_end': 1080, 'increment': 25}, [None, 0])], [({'now': 1060, 'expiry': 1107, 'paid_cents': 414, 'rate_per_hour': 100, 'max_minutes': 120, 'enf_start': 480, 'enf_end': 1080, 'increment': 25}, [1107, 0]), ({'now': 47, 'expiry': 84, 'paid_cents': 192, 'rate_per_hour': 200, 'max_minutes': 60, 'enf_start': 480, 'enf_end': 1080, 'increment': 5}, [537, 190]), ({'now': 1040, 'expiry': None, 'paid_cents': 333, 'rate_per_hour': 150, 'max_minutes': 120, 'enf_start': 480, 'enf_end': 1080, 'increment': 25}, [1080, 100]), ({'now': 147, 'expiry': 118, 'paid_cents': 14, 'rate_per_hour': 250, 'max_minutes': 120, 'enf_start': 480, 'enf_end': 1080, 'increment': 25}, [480, 0]), ({'now': 862, 'expiry': 851, 'paid_cents': 76, 'rate_per_hour': 200, 'max_minutes': 60, 'enf_start': 480, 'enf_end': 1080, 'increment': 25}, [884, 74]), ({'now': 420, 'expiry': None, 'paid_cents': 200, 'rate_per_hour': 200, 'max_minutes': 120, 'enf_start': 480, 'enf_end': 1080, 'increment': 25}, [540, 200]), ({'now': 600, 'expiry': 650, 'paid_cents': 400, 'rate_per_hour': 200, 'max_minutes': 120, 'enf_start': 480, 'enf_end': 1080, 'increment': 5}, [720, 234]), ({'now': 45, 'expiry': None, 'paid_cents': 260, 'rate_per_hour': 250, 'max_minutes': 240, 'enf_start': 480, 'enf_end': 1080, 'increment': 5}, [542, 259])]]
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)

prompt:
Street meter time purchase returns a wrong result when time bought before enforcement starts immediately.

Input {now, expiry (or null), paid_cents, rate_per_hour, max_minutes, enf_start, enf_end, increment}. Only whole increments are credited. At or after enforcement end nothing is sold: [null, 0]. Paid time starts at the later of now, enforcement start and any unexpired expiry; it ends at the earliest of start + purchased minutes (floor), max(now, enf_start) + max_minutes, and enf_end. Accepted cents = min(credited, ceil(used minutes * rate/60)). Return [new expiry, accepted cents].

split: open-access


broken_source:
"""Failure Map reference implementation. Python standard library only."""
import json

N = 1
observations = []
def solve(x):
    inc = x['increment']
    c = x['paid_cents'] - x['paid_cents'] % inc
    now = x['now']
    if now >= x['enf_end']:
        return [None, 0]
    base = max(now, x['enf_start'])
    start = base if x['expiry'] is None else base
    mins = c * 60 // x['rate_per_hour']
    end = min(start + mins, base + x['max_minutes'], x['enf_end'])
    used = max(0, end - start)
    cost = min(c, -(-used * x['rate_per_hour'] // 60))
    return [max(end, start), cost]
def check(label, actual, expected):
    observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
fixtures = [[({'now': 480, 'expiry': 507, 'paid_cents': 386, 'rate_per_hour': 150, 'max_minutes': 240, 'enf_start': 480, 'enf_end': 1080, 'increment': 25}, [657, 375]), ({'now': 1080, 'expiry': 1100, 'paid_cents': 100, 'rate_per_hour': 200, 'max_minutes': 120, 'enf_start': 480, 'enf_end': 1080, 'increment': 25}, [None, 0]), ({'now': 974, 'expiry': 1004, 'paid_cents': 53, 'rate_per_hour': 150, 'max_minutes': 240, 'enf_start': 480, 'enf_end': 1080, 'increment': 25}, [1024, 50]), ({'now': 1080, 'expiry': None, 'paid_cents': 482, 'rate_per_hour': 150, 'max_minutes': 240, 'enf_start': 480, 'enf_end': 1080, 'increment': 25}, [None, 0]), ({'now': 502, 'expiry': 497, 'paid_cents': 193, 'rate_per_hour': 200, 'max_minutes': 120, 'enf_start': 480, 'enf_end': 1080, 'increment': 25}, [554, 174]), ({'now': 9, 'expiry': 5, 'paid_cents': 311, 'rate_per_hour': 100, 'max_minutes': 120, 'enf_start': 480, 'enf_end': 1080, 'increment': 25}, [600, 200]), ({'now': 600, 'expiry': 650, 'paid_cents': 400, 'rate_per_hour': 200, 'max_minutes': 120, 'enf_start': 480, 'enf_end': 1080, 'increment': 5}, [720, 234]), ({'now': 420, 'expiry': None, 'paid_cents': 200, 'rate_per_hour': 200, 'max_minutes': 120, 'enf_start': 480, 'enf_end': 1080, 'increment': 25}, [540, 200])], [({'now': 495, 'expiry': 548, 'paid_cents': 54, 'rate_per_hour': 250, 'max_minutes': 60, 'enf_start': 480, 'enf_end': 1080, 'increment': 5}, [555, 30]), ({'now': 983, 'expiry': 1008, 'paid_cents': 521, 'rate_per_hour': 200, 'max_minutes': 120, 'enf_start': 480, 'enf_end': 1080, 'increment': 5}, [1080, 240]), ({'now': 1080, 'expiry': 1100, 'paid_cents': 100, 'rate_per_hour': 200, 'max_minutes': 120, 'enf_start': 480, 'enf_end': 1080, 'increment': 25}, [None, 0]), ({'now': 480, 'expiry': None, 'paid_cents': 223, 'rate_per_hour': 150, 'max_minutes': 240, 'enf_start': 480, 'enf_end': 1080, 'increment': 25}, [560, 200]), ({'now': 1008, 'expiry': 949, 'paid_cents': 513, 'rate_per_hour': 200, 'max_minutes': 240, 'enf_start': 480, 'enf_end': 1080, 'increment': 5}, [1080, 240]), ({'now': 600, 'expiry': 650, 'paid_cents': 400, 'rate_per_hour': 200, 'max_minutes': 120, 'enf_start': 480, 'enf_end': 1080, 'increment': 5}, [720, 234]), ({'now': 480, 'expiry': 459, 'paid_cents': 336, 'rate_per_hour': 250, 'max_minutes': 60, 'enf_start': 480, 'enf_end': 1080, 'increment': 5}, [540, 250]), ({'now': 420, 'expiry': None, 'paid_cents': 200, 'rate_per_hour': 200, 'max_minutes': 120, 'enf_start': 480, 'enf_end': 1080, 'increment': 25}, [540, 200])], [({'now': 1080, 'expiry': 1031, 'paid_cents': 14, 'rate_per_hour': 150, 'max_minutes': 120, 'enf_start': 480, 'enf_end': 1080, 'increment': 25}, [None, 0]), ({'now': 420, 'expiry': None, 'paid_cents': 200, 'rate_per_hour': 200, 'max_minutes': 120, 'enf_start': 480, 'enf_end': 1080, 'increment': 25}, [540, 200]), ({'now': 761, 'expiry': 757, 'paid_cents': 208, 'rate_per_hour': 150, 'max_minutes': 60, 'enf_start': 480, 'enf_end': 1080, 'increment': 25}, [821, 150]), ({'now': 1080, 'expiry': 1100, 'paid_cents': 100, 'rate_per_hour': 200, 'max_minutes': 120, 'enf_start': 480, 'enf_end': 1080, 'increment': 25}, [None, 0]), ({'now': 645, 'expiry': 664, 'paid_cents': 14, 'rate_per_hour': 100, 'max_minutes': 60, 'enf_start': 480, 'enf_end': 1080, 'increment': 5}, [670, 10]), ({'now': 597, 'expiry': 543, 'paid_cents': 14, 'rate_per_hour': 150, 'max_minutes': 240, 'enf_start': 480, 'enf_end': 1080, 'increment': 5}, [601, 10]), ({'now': 812, 'expiry': 824, 'paid_cents': 149, 'rate_per_hour': 150, 'max_minutes': 60, 'enf_start': 480, 'enf_end': 1080, 'increment': 25}, [872, 120]), ({'now': 600, 'expiry': 650, 'paid_cents': 400, 'rate_per_hour': 200, 'max_minutes': 120, 'enf_start': 480, 'enf_end': 1080, 'increment': 5}, [720, 234])], [({'now': 650, 'expiry': 663, 'paid_cents': 527, 'rate_per_hour': 100, 'max_minutes': 60, 'enf_start': 480, 'enf_end': 1080, 'increment': 25}, [710, 79]), ({'now': 1040, 'expiry': None, 'paid_cents': 333, 'rate_per_hour': 150, 'max_minutes': 120, 'enf_start': 480, 'enf_end': 1080, 'increment': 25}, [1080, 100]), ({'now': 392, 'expiry': 350, 'paid_cents': 319, 'rate_per_hour': 150, 'max_minutes': 240, 'enf_start': 480, 'enf_end': 1080, 'increment': 25}, [600, 300]), ({'now': 600, 'expiry': 650, 'paid_cents': 400, 'rate_per_hour': 200, 'max_minutes': 120, 'enf_start': 480, 'enf_end': 1080, 'increment': 5}, [720, 234]), ({'now': 61, 'expiry': 125, 'paid_cents': 66, 'rate_per_hour': 250, 'max_minutes': 240, 'enf_start': 480, 'enf_end': 1080, 'increment': 25}, [492, 50]), ({'now': 480, 'expiry': 510, 'paid_cents': 581, 'rate_per_hour': 100, 'max_minutes': 240, 'enf_start': 480, 'enf_end': 1080, 'increment': 25}, [720, 350]), ({'now': 420, 'expiry': None, 'paid_cents': 200, 'rate_per_hour': 200, 'max_minutes': 120, 'enf_start': 480, 'enf_end': 1080, 'increment': 25}, [540, 200]), ({'now': 1080, 'expiry': 1072, 'paid_cents': 138, 'rate_per_hour': 100, 'max_minutes': 60, 'enf_start': 480, 'enf_end': 1080, 'increment': 25}, [None, 0])], [({'now': 1040, 'expiry': None, 'paid_cents': 333, 'rate_per_hour': 150, 'max_minutes': 120, 'enf_start': 480, 'enf_end': 1080, 'increment': 25}, [1080, 100]), ({'now': 583, 'expiry': 530, 'paid_cents': 342, 'rate_per_hour': 100, 'max_minutes': 60, 'enf_start': 480, 'enf_end': 1080, 'increment': 25}, [643, 100]), ({'now': 1080, 'expiry': 1100, 'paid_cents': 100, 'rate_per_hour': 200, 'max_minutes': 120, 'enf_start': 480, 'enf_end': 1080, 'increment': 25}, [None, 0]), ({'now': 480, 'expiry': 552, 'paid_cents': 342, 'rate_per_hour': 200, 'max_minutes': 60, 'enf_start': 480, 'enf_end': 1080, 'increment': 25}, [552, 0]), ({'now': 480, 'expiry': 428, 'paid_cents': 263, 'rate_per_hour': 200, 'max_minutes': 60, 'enf_start': 480, 'enf_end': 1080, 'increment': 25}, [540, 200]), ({'now': 420, 'expiry': None, 'paid_cents': 200, 'rate_per_hour': 200, 'max_minutes': 120, 'enf_start': 480, 'enf_end': 1080, 'increment': 25}, [540, 200]), ({'now': 496, 'expiry': 502, 'paid_cents': 61, 'rate_per_hour': 250, 'max_minutes': 120, 'enf_start': 480, 'enf_end': 1080, 'increment': 5}, [516, 59]), ({'now': 934, 'expiry': 971, 'paid_cents': 344, 'rate_per_hour': 200, 'max_minutes': 60, 'enf_start': 480, 'enf_end': 1080, 'increment': 5}, [994, 77])]]
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)

source:
"""Failure Map reference implementation. Python standard library only."""
import json

N = 1
observations = []
def solve(x):
    inc = x['increment']
    c = x['paid_cents'] - x['paid_cents'] % inc
    now = x['now']
    if now >= x['enf_end']:
        return [None, 0]
    base = max(now, x['enf_start'])
    start = base if x['expiry'] is None else x['expiry']
    mins = c * 60 // x['rate_per_hour']
    end = min(start + mins, base + x['max_minutes'], x['enf_end'])
    used = max(0, end - start)
    cost = min(c, -(-used * x['rate_per_hour'] // 60))
    return [max(end, start), cost]
def check(label, actual, expected):
    observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
fixtures = [[({'now': 480, 'expiry': 507, 'paid_cents': 386, 'rate_per_hour': 150, 'max_minutes': 240, 'enf_start': 480, 'enf_end': 1080, 'increment': 25}, [657, 375]), ({'now': 1080, 'expiry': 1100, 'paid_cents': 100, 'rate_per_hour': 200, 'max_minutes': 120, 'enf_start': 480, 'enf_end': 1080, 'increment': 25}, [None, 0]), ({'now': 974, 'expiry': 1004, 'paid_cents': 53, 'rate_per_hour': 150, 'max_minutes': 240, 'enf_start': 480, 'enf_end': 1080, 'increment': 25}, [1024, 50]), ({'now': 1080, 'expiry': None, 'paid_cents': 482, 'rate_per_hour': 150, 'max_minutes': 240, 'enf_start': 480, 'enf_end': 1080, 'increment': 25}, [None, 0]), ({'now': 502, 'expiry': 497, 'paid_cents': 193, 'rate_per_hour': 200, 'max_minutes': 120, 'enf_start': 480, 'enf_end': 1080, 'increment': 25}, [554, 174]), ({'now': 9, 'expiry': 5, 'paid_cents': 311, 'rate_per_hour': 100, 'max_minutes': 120, 'enf_start': 480, 'enf_end': 1080, 'increment': 25}, [600, 200]), ({'now': 600, 'expiry': 650, 'paid_cents': 400, 'rate_per_hour': 200, 'max_minutes': 120, 'enf_start': 480, 'enf_end': 1080, 'increment': 5}, [720, 234]), ({'now': 420, 'expiry': None, 'paid_cents': 200, 'rate_per_hour': 200, 'max_minutes': 120, 'enf_start': 480, 'enf_end': 1080, 'increment': 25}, [540, 200])], [({'now': 495, 'expiry': 548, 'paid_cents': 54, 'rate_per_hour': 250, 'max_minutes': 60, 'enf_start': 480, 'enf_end': 1080, 'increment': 5}, [555, 30]), ({'now': 983, 'expiry': 1008, 'paid_cents': 521, 'rate_per_hour': 200, 'max_minutes': 120, 'enf_start': 480, 'enf_end': 1080, 'increment': 5}, [1080, 240]), ({'now': 1080, 'expiry': 1100, 'paid_cents': 100, 'rate_per_hour': 200, 'max_minutes': 120, 'enf_start': 480, 'enf_end': 1080, 'increment': 25}, [None, 0]), ({'now': 480, 'expiry': None, 'paid_cents': 223, 'rate_per_hour': 150, 'max_minutes': 240, 'enf_start': 480, 'enf_end': 1080, 'increment': 25}, [560, 200]), ({'now': 1008, 'expiry': 949, 'paid_cents': 513, 'rate_per_hour': 200, 'max_minutes': 240, 'enf_start': 480, 'enf_end': 1080, 'increment': 5}, [1080, 240]), ({'now': 600, 'expiry': 650, 'paid_cents': 400, 'rate_per_hour': 200, 'max_minutes': 120, 'enf_start': 480, 'enf_end': 1080, 'increment': 5}, [720, 234]), ({'now': 480, 'expiry': 459, 'paid_cents': 336, 'rate_per_hour': 250, 'max_minutes': 60, 'enf_start': 480, 'enf_end': 1080, 'increment': 5}, [540, 250]), ({'now': 420, 'expiry': None, 'paid_cents': 200, 'rate_per_hour': 200, 'max_minutes': 120, 'enf_start': 480, 'enf_end': 1080, 'increment': 25}, [540, 200])], [({'now': 1080, 'expiry': 1031, 'paid_cents': 14, 'rate_per_hour': 150, 'max_minutes': 120, 'enf_start': 480, 'enf_end': 1080, 'increment': 25}, [None, 0]), ({'now': 420, 'expiry': None, 'paid_cents': 200, 'rate_per_hour': 200, 'max_minutes': 120, 'enf_start': 480, 'enf_end': 1080, 'increment': 25}, [540, 200]), ({'now': 761, 'expiry': 757, 'paid_cents': 208, 'rate_per_hour': 150, 'max_minutes': 60, 'enf_start': 480, 'enf_end': 1080, 'increment': 25}, [821, 150]), ({'now': 1080, 'expiry': 1100, 'paid_cents': 100, 'rate_per_hour': 200, 'max_minutes': 120, 'enf_start': 480, 'enf_end': 1080, 'increment': 25}, [None, 0]), ({'now': 645, 'expiry': 664, 'paid_cents': 14, 'rate_per_hour': 100, 'max_minutes': 60, 'enf_start': 480, 'enf_end': 1080, 'increment': 5}, [670, 10]), ({'now': 597, 'expiry': 543, 'paid_cents': 14, 'rate_per_hour': 150, 'max_minutes': 240, 'enf_start': 480, 'enf_end': 1080, 'increment': 5}, [601, 10]), ({'now': 812, 'expiry': 824, 'paid_cents': 149, 'rate_per_hour': 150, 'max_minutes': 60, 'enf_start': 480, 'enf_end': 1080, 'increment': 25}, [872, 120]), ({'now': 600, 'expiry': 650, 'paid_cents': 400, 'rate_per_hour': 200, 'max_minutes': 120, 'enf_start': 480, 'enf_end': 1080, 'increment': 5}, [720, 234])], [({'now': 650, 'expiry': 663, 'paid_cents': 527, 'rate_per_hour': 100, 'max_minutes': 60, 'enf_start': 480, 'enf_end': 1080, 'increment': 25}, [710, 79]), ({'now': 1040, 'expiry': None, 'paid_cents': 333, 'rate_per_hour': 150, 'max_minutes': 120, 'enf_start': 480, 'enf_end': 1080, 'increment': 25}, [1080, 100]), ({'now': 392, 'expiry': 350, 'paid_cents': 319, 'rate_per_hour': 150, 'max_minutes': 240, 'enf_start': 480, 'enf_end': 1080, 'increment': 25}, [600, 300]), ({'now': 600, 'expiry': 650, 'paid_cents': 400, 'rate_per_hour': 200, 'max_minutes': 120, 'enf_start': 480, 'enf_end': 1080, 'increment': 5}, [720, 234]), ({'now': 61, 'expiry': 125, 'paid_cents': 66, 'rate_per_hour': 250, 'max_minutes': 240, 'enf_start': 480, 'enf_end': 1080, 'increment': 25}, [492, 50]), ({'now': 480, 'expiry': 510, 'paid_cents': 581, 'rate_per_hour': 100, 'max_minutes': 240, 'enf_start': 480, 'enf_end': 1080, 'increment': 25}, [720, 350]), ({'now': 420, 'expiry': None, 'paid_cents': 200, 'rate_per_hour': 200, 'max_minutes': 120, 'enf_start': 480, 'enf_end': 1080, 'increment': 25}, [540, 200]), ({'now': 1080, 'expiry': 1072, 'paid_cents': 138, 'rate_per_hour': 100, 'max_minutes': 60, 'enf_start': 480, 'enf_end': 1080, 'increment': 25}, [None, 0])], [({'now': 1040, 'expiry': None, 'paid_cents': 333, 'rate_per_hour': 150, 'max_minutes': 120, 'enf_start': 480, 'enf_end': 1080, 'increment': 25}, [1080, 100]), ({'now': 583, 'expiry': 530, 'paid_cents': 342, 'rate_per_hour': 100, 'max_minutes': 60, 'enf_start': 480, 'enf_end': 1080, 'increment': 25}, [643, 100]), ({'now': 1080, 'expiry': 1100, 'paid_cents': 100, 'rate_per_hour': 200, 'max_minutes': 120, 'enf_start': 480, 'enf_end': 1080, 'increment': 25}, [None, 0]), ({'now': 480, 'expiry': 552, 'paid_cents': 342, 'rate_per_hour': 200, 'max_minutes': 60, 'enf_start': 480, 'enf_end': 1080, 'increment': 25}, [552, 0]), ({'now': 480, 'expiry': 428, 'paid_cents': 263, 'rate_per_hour': 200, 'max_minutes': 60, 'enf_start': 480, 'enf_end': 1080, 'increment': 25}, [540, 200]), ({'now': 420, 'expiry': None, 'paid_cents': 200, 'rate_per_hour': 200, 'max_minutes': 120, 'enf_start': 480, 'enf_end': 1080, 'increment': 25}, [540, 200]), ({'now': 496, 'expiry': 502, 'paid_cents': 61, 'rate_per_hour': 250, 'max_minutes': 120, 'enf_start': 480, 'enf_end': 1080, 'increment': 5}, [516, 59]), ({'now': 934, 'expiry': 971, 'paid_cents': 344, 'rate_per_hour': 200, 'max_minutes': 60, 'enf_start': 480, 'enf_end': 1080, 'increment': 5}, [994, 77])]]
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)

prompt:
Street meter time purchase returns a wrong result when a new purchase ignores the remaining paid time.

Input {now, expiry (or null), paid_cents, rate_per_hour, max_minutes, enf_start, enf_end, increment}. Only whole increments are credited. At or after enforcement end nothing is sold: [null, 0]. Paid time starts at the later of now, enforcement start and any unexpired expiry; it ends at the earliest of start + purchased minutes (floor), max(now, enf_start) + max_minutes, and enf_end. Accepted cents = min(credited, ceil(used minutes * rate/60)). Return [new expiry, accepted cents].

split: open-access


broken_source:
"""Failure Map reference implementation. Python standard library only."""
import json

N = 1
observations = []
def solve(x):
    inc = x['increment']
    c = x['paid_cents'] - x['paid_cents'] % inc
    now = x['now']
    if now >= x['enf_end']:
        return [None, 0]
    base = max(now, x['enf_start'])
    start = base if x['expiry'] is None else max(base, x['expiry'])
    mins = c * 60 // x['rate_per_hour']
    end = min(start + mins, start + x['max_minutes'], x['enf_end'])
    used = max(0, end - start)
    cost = min(c, -(-used * x['rate_per_hour'] // 60))
    return [max(end, start), cost]
def check(label, actual, expected):
    observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
fixtures = [[({'now': 480, 'expiry': 532, 'paid_cents': 270, 'rate_per_hour': 100, 'max_minutes': 120, 'enf_start': 480, 'enf_end': 1080, 'increment': 5}, [600, 114]), ({'now': 646, 'expiry': 691, 'paid_cents': 468, 'rate_per_hour': 150, 'max_minutes': 120, 'enf_start': 480, 'enf_end': 1080, 'increment': 5}, [766, 188]), ({'now': 1080, 'expiry': 1100, 'paid_cents': 100, 'rate_per_hour': 200, 'max_minutes': 120, 'enf_start': 480, 'enf_end': 1080, 'increment': 25}, [None, 0]), ({'now': 420, 'expiry': None, 'paid_cents': 200, 'rate_per_hour': 200, 'max_minutes': 120, 'enf_start': 480, 'enf_end': 1080, 'increment': 25}, [540, 200]), ({'now': 1040, 'expiry': None, 'paid_cents': 333, 'rate_per_hour': 150, 'max_minutes': 120, 'enf_start': 480, 'enf_end': 1080, 'increment': 25}, [1080, 100]), ({'now': 182, 'expiry': 191, 'paid_cents': 258, 'rate_per_hour': 250, 'max_minutes': 60, 'enf_start': 480, 'enf_end': 1080, 'increment': 5}, [540, 250]), ({'now': 42, 'expiry': None, 'paid_cents': 361, 'rate_per_hour': 200, 'max_minutes': 60, 'enf_start': 480, 'enf_end': 1080, 'increment': 25}, [540, 200]), ({'now': 591, 'expiry': 603, 'paid_cents': 339, 'rate_per_hour': 100, 'max_minutes': 60, 'enf_start': 480, 'enf_end': 1080, 'increment': 5}, [651, 80])], [({'now': 798, 'expiry': 823, 'paid_cents': 558, 'rate_per_hour': 150, 'max_minutes': 60, 'enf_start': 480, 'enf_end': 1080, 'increment': 5}, [858, 88]), ({'now': 1040, 'expiry': None, 'paid_cents': 333, 'rate_per_hour': 150, 'max_minutes': 120, 'enf_start': 480, 'enf_end': 1080, 'increment': 25}, [1080, 100]), ({'now': 480, 'expiry': None, 'paid_cents': 257, 'rate_per_hour': 150, 'max_minutes': 240, 'enf_start': 480, 'enf_end': 1080, 'increment': 5}, [582, 255]), ({'now': 180, 'expiry': None, 'paid_cents': 342, 'rate_per_hour': 250, 'max_minutes': 60, 'enf_start': 480, 'enf_end': 1080, 'increment': 5}, [540, 250]), ({'now': 600, 'expiry': 650, 'paid_cents': 400, 'rate_per_hour': 200, 'max_minutes': 120, 'enf_start': 480, 'enf_end': 1080, 'increment': 5}, [720, 234]), ({'now': 436, 'expiry': 444, 'paid_cents': 152, 'rate_per_hour': 100, 'max_minutes': 60, 'enf_start': 480, 'enf_end': 1080, 'increment': 5}, [540, 100]), ({'now': 420, 'expiry': None, 'paid_cents': 200, 'rate_per_hour': 200, 'max_minutes': 120, 'enf_start': 480, 'enf_end': 1080, 'increment': 25}, [540, 200]), ({'now': 480, 'expiry': 564, 'paid_cents': 165, 'rate_per_hour': 200, 'max_minutes': 60, 'enf_start': 480, 'enf_end': 1080, 'increment': 25}, [564, 0])], [({'now': 420, 'expiry': None, 'paid_cents': 200, 'rate_per_hour': 200, 'max_minutes': 120, 'enf_start': 480, 'enf_end': 1080, 'increment': 25}, [540, 200]), ({'now': 453, 'expiry': None, 'paid_cents': 545, 'rate_per_hour': 100, 'max_minutes': 240, 'enf_start': 480, 'enf_end': 1080, 'increment': 25}, [720, 400]), ({'now': 480, 'expiry': 548, 'paid_cents': 563, 'rate_per_hour': 150, 'max_minutes': 120, 'enf_start': 480, 'enf_end': 1080, 'increment': 5}, [600, 130]), ({'now': 396, 'expiry': None, 'paid_cents': 250, 'rate_per_hour': 150, 'max_minutes': 60, 'enf_start': 480, 'enf_end': 1080, 'increment': 5}, [540, 150]), ({'now': 480, 'expiry': 429, 'paid_cents': 333, 'rate_per_hour': 200, 'max_minutes': 120, 'enf_start': 480, 'enf_end': 1080, 'increment': 5}, [579, 330]), ({'now': 600, 'expiry': 650, 'paid_cents': 400, 'rate_per_hour': 200, 'max_minutes': 120, 'enf_start': 480, 'enf_end': 1080, 'increment': 5}, [720, 234]), ({'now': 1040, 'expiry': None, 'paid_cents': 333, 'rate_per_hour': 150, 'max_minutes': 120, 'enf_start': 480, 'enf_end': 1080, 'increment': 25}, [1080, 100]), ({'now': 480, 'expiry': 527, 'paid_cents': 369, 'rate_per_hour': 200, 'max_minutes': 60, 'enf_start': 480, 'enf_end': 1080, 'increment': 5}, [540, 44])], [({'now': 1040, 'expiry': None, 'paid_cents': 333, 'rate_per_hour': 150, 'max_minutes': 120, 'enf_start': 480, 'enf_end': 1080, 'increment': 25}, [1080, 100]), ({'now': 480, 'expiry': 514, 'paid_cents': 358, 'rate_per_hour': 150, 'max_minutes': 60, 'enf_start': 480, 'enf_end': 1080, 'increment': 5}, [540, 65]), ({'now': 480, 'expiry': 556, 'paid_cents': 47, 'rate_per_hour': 150, 'max_minutes': 60, 'enf_start': 480, 'enf_end': 1080, 'increment': 5}, [556, 0]), ({'now': 600, 'expiry': 650, 'paid_cents': 400, 'rate_per_hour': 200, 'max_minutes': 120, 'enf_start': 480, 'enf_end': 1080, 'increment': 5}, [720, 234]), ({'now': 1000, 'expiry': 966, 'paid_cents': 599, 'rate_per_hour': 200, 'max_minutes': 240, 'enf_start': 480, 'enf_end': 1080, 'increment': 5}, [1080, 267]), ({'now': 228, 'expiry': 243, 'paid_cents': 594, 'rate_per_hour': 250, 'max_minutes': 60, 'enf_start': 480, 'enf_end': 1080, 'increment': 5}, [540, 250]), ({'now': 1080, 'expiry': 1100, 'paid_cents': 100, 'rate_per_hour': 200, 'max_minutes': 120, 'enf_start': 480, 'enf_end': 1080, 'increment': 25}, [None, 0]), ({'now': 16, 'expiry': None, 'paid_cents': 295, 'rate_per_hour': 200, 'max_minutes': 240, 'enf_start': 480, 'enf_end': 1080, 'increment': 5}, [568, 294])], [({'now': 1040, 'expiry': None, 'paid_cents': 333, 'rate_per_hour': 150, 'max_minutes': 120, 'enf_start': 480, 'enf_end': 1080, 'increment': 25}, [1080, 100]), ({'now': 914, 'expiry': 980, 'paid_cents': 538, 'rate_per_hour': 250, 'max_minutes': 60, 'enf_start': 480, 'enf_end': 1080, 'increment': 25}, [980, 0]), ({'now': 758, 'expiry': 791, 'paid_cents': 478, 'rate_per_hour': 150, 'max_minutes': 240, 'enf_start': 480, 'enf_end': 1080, 'increment': 25}, [981, 475]), ({'now': 413, 'expiry': None, 'paid_cents': 368, 'rate_per_hour': 200, 'max_minutes': 120, 'enf_start': 480, 'enf_end': 1080, 'increment': 25}, [585, 350]), ({'now': 701, 'expiry': 740, 'paid_cents': 590, 'rate_per_hour': 100, 'max_minutes': 120, 'enf_start': 480, 'enf_end': 1080, 'increment': 25}, [821, 135]), ({'now': 600, 'expiry': 650, 'paid_cents': 400, 'rate_per_hour': 200, 'max_minutes': 120, 'enf_start': 480, 'enf_end': 1080, 'increment': 5}, [720, 234]), ({'now': 420, 'expiry': None, 'paid_cents': 200, 'rate_per_hour': 200, 'max_minutes': 120, 'enf_start': 480, 'enf_end': 1080, 'increment': 25}, [540, 200]), ({'now': 62, 'expiry': None, 'paid_cents': 358, 'rate_per_hour': 250, 'max_minutes': 120, 'enf_start': 480, 'enf_end': 1080, 'increment': 25}, [564, 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)

source:
"""Failure Map reference implementation. Python standard library only."""
import json

N = 1
observations = []
def solve(x):
    inc = x['increment']
    c = x['paid_cents'] - x['paid_cents'] % inc
    now = x['now']
    if now >= x['enf_end']:
        return [None, 0]
    base = max(now, x['enf_start'])
    start = base if x['expiry'] is None else max(base, x['expiry'])
    mins = c * 60 // x['rate_per_hour']
    end = min(start + mins, now + x['max_minutes'], x['enf_end'])
    used = max(0, end - start)
    cost = min(c, -(-used * x['rate_per_hour'] // 60))
    return [max(end, start), cost]
def check(label, actual, expected):
    observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
fixtures = [[({'now': 480, 'expiry': 532, 'paid_cents': 270, 'rate_per_hour': 100, 'max_minutes': 120, 'enf_start': 480, 'enf_end': 1080, 'increment': 5}, [600, 114]), ({'now': 646, 'expiry': 691, 'paid_cents': 468, 'rate_per_hour': 150, 'max_minutes': 120, 'enf_start': 480, 'enf_end': 1080, 'increment': 5}, [766, 188]), ({'now': 1080, 'expiry': 1100, 'paid_cents': 100, 'rate_per_hour': 200, 'max_minutes': 120, 'enf_start': 480, 'enf_end': 1080, 'increment': 25}, [None, 0]), ({'now': 420, 'expiry': None, 'paid_cents': 200, 'rate_per_hour': 200, 'max_minutes': 120, 'enf_start': 480, 'enf_end': 1080, 'increment': 25}, [540, 200]), ({'now': 1040, 'expiry': None, 'paid_cents': 333, 'rate_per_hour': 150, 'max_minutes': 120, 'enf_start': 480, 'enf_end': 1080, 'increment': 25}, [1080, 100]), ({'now': 182, 'expiry': 191, 'paid_cents': 258, 'rate_per_hour': 250, 'max_minutes': 60, 'enf_start': 480, 'enf_end': 1080, 'increment': 5}, [540, 250]), ({'now': 42, 'expiry': None, 'paid_cents': 361, 'rate_per_hour': 200, 'max_minutes': 60, 'enf_start': 480, 'enf_end': 1080, 'increment': 25}, [540, 200]), ({'now': 591, 'expiry': 603, 'paid_cents': 339, 'rate_per_hour': 100, 'max_minutes': 60, 'enf_start': 480, 'enf_end': 1080, 'increment': 5}, [651, 80])], [({'now': 798, 'expiry': 823, 'paid_cents': 558, 'rate_per_hour': 150, 'max_minutes': 60, 'enf_start': 480, 'enf_end': 1080, 'increment': 5}, [858, 88]), ({'now': 1040, 'expiry': None, 'paid_cents': 333, 'rate_per_hour': 150, 'max_minutes': 120, 'enf_start': 480, 'enf_end': 1080, 'increment': 25}, [1080, 100]), ({'now': 480, 'expiry': None, 'paid_cents': 257, 'rate_per_hour': 150, 'max_minutes': 240, 'enf_start': 480, 'enf_end': 1080, 'increment': 5}, [582, 255]), ({'now': 180, 'expiry': None, 'paid_cents': 342, 'rate_per_hour': 250, 'max_minutes': 60, 'enf_start': 480, 'enf_end': 1080, 'increment': 5}, [540, 250]), ({'now': 600, 'expiry': 650, 'paid_cents': 400, 'rate_per_hour': 200, 'max_minutes': 120, 'enf_start': 480, 'enf_end': 1080, 'increment': 5}, [720, 234]), ({'now': 436, 'expiry': 444, 'paid_cents': 152, 'rate_per_hour': 100, 'max_minutes': 60, 'enf_start': 480, 'enf_end': 1080, 'increment': 5}, [540, 100]), ({'now': 420, 'expiry': None, 'paid_cents': 200, 'rate_per_hour': 200, 'max_minutes': 120, 'enf_start': 480, 'enf_end': 1080, 'increment': 25}, [540, 200]), ({'now': 480, 'expiry': 564, 'paid_cents': 165, 'rate_per_hour': 200, 'max_minutes': 60, 'enf_start': 480, 'enf_end': 1080, 'increment': 25}, [564, 0])], [({'now': 420, 'expiry': None, 'paid_cents': 200, 'rate_per_hour': 200, 'max_minutes': 120, 'enf_start': 480, 'enf_end': 1080, 'increment': 25}, [540, 200]), ({'now': 453, 'expiry': None, 'paid_cents': 545, 'rate_per_hour': 100, 'max_minutes': 240, 'enf_start': 480, 'enf_end': 1080, 'increment': 25}, [720, 400]), ({'now': 480, 'expiry': 548, 'paid_cents': 563, 'rate_per_hour': 150, 'max_minutes': 120, 'enf_start': 480, 'enf_end': 1080, 'increment': 5}, [600, 130]), ({'now': 396, 'expiry': None, 'paid_cents': 250, 'rate_per_hour': 150, 'max_minutes': 60, 'enf_start': 480, 'enf_end': 1080, 'increment': 5}, [540, 150]), ({'now': 480, 'expiry': 429, 'paid_cents': 333, 'rate_per_hour': 200, 'max_minutes': 120, 'enf_start': 480, 'enf_end': 1080, 'increment': 5}, [579, 330]), ({'now': 600, 'expiry': 650, 'paid_cents': 400, 'rate_per_hour': 200, 'max_minutes': 120, 'enf_start': 480, 'enf_end': 1080, 'increment': 5}, [720, 234]), ({'now': 1040, 'expiry': None, 'paid_cents': 333, 'rate_per_hour': 150, 'max_minutes': 120, 'enf_start': 480, 'enf_end': 1080, 'increment': 25}, [1080, 100]), ({'now': 480, 'expiry': 527, 'paid_cents': 369, 'rate_per_hour': 200, 'max_minutes': 60, 'enf_start': 480, 'enf_end': 1080, 'increment': 5}, [540, 44])], [({'now': 1040, 'expiry': None, 'paid_cents': 333, 'rate_per_hour': 150, 'max_minutes': 120, 'enf_start': 480, 'enf_end': 1080, 'increment': 25}, [1080, 100]), ({'now': 480, 'expiry': 514, 'paid_cents': 358, 'rate_per_hour': 150, 'max_minutes': 60, 'enf_start': 480, 'enf_end': 1080, 'increment': 5}, [540, 65]), ({'now': 480, 'expiry': 556, 'paid_cents': 47, 'rate_per_hour': 150, 'max_minutes': 60, 'enf_start': 480, 'enf_end': 1080, 'increment': 5}, [556, 0]), ({'now': 600, 'expiry': 650, 'paid_cents': 400, 'rate_per_hour': 200, 'max_minutes': 120, 'enf_start': 480, 'enf_end': 1080, 'increment': 5}, [720, 234]), ({'now': 1000, 'expiry': 966, 'paid_cents': 599, 'rate_per_hour': 200, 'max_minutes': 240, 'enf_start': 480, 'enf_end': 1080, 'increment': 5}, [1080, 267]), ({'now': 228, 'expiry': 243, 'paid_cents': 594, 'rate_per_hour': 250, 'max_minutes': 60, 'enf_start': 480, 'enf_end': 1080, 'increment': 5}, [540, 250]), ({'now': 1080, 'expiry': 1100, 'paid_cents': 100, 'rate_per_hour': 200, 'max_minutes': 120, 'enf_start': 480, 'enf_end': 1080, 'increment': 25}, [None, 0]), ({'now': 16, 'expiry': None, 'paid_cents': 295, 'rate_per_hour': 200, 'max_minutes': 240, 'enf_start': 480, 'enf_end': 1080, 'increment': 5}, [568, 294])], [({'now': 1040, 'expiry': None, 'paid_cents': 333, 'rate_per_hour': 150, 'max_minutes': 120, 'enf_start': 480, 'enf_end': 1080, 'increment': 25}, [1080, 100]), ({'now': 914, 'expiry': 980, 'paid_cents': 538, 'rate_per_hour': 250, 'max_minutes': 60, 'enf_start': 480, 'enf_end': 1080, 'increment': 25}, [980, 0]), ({'now': 758, 'expiry': 791, 'paid_cents': 478, 'rate_per_hour': 150, 'max_minutes': 240, 'enf_start': 480, 'enf_end': 1080, 'increment': 25}, [981, 475]), ({'now': 413, 'expiry': None, 'paid_cents': 368, 'rate_per_hour': 200, 'max_minutes': 120, 'enf_start': 480, 'enf_end': 1080, 'increment': 25}, [585, 350]), ({'now': 701, 'expiry': 740, 'paid_cents': 590, 'rate_per_hour': 100, 'max_minutes': 120, 'enf_start': 480, 'enf_end': 1080, 'increment': 25}, [821, 135]), ({'now': 600, 'expiry': 650, 'paid_cents': 400, 'rate_per_hour': 200, 'max_minutes': 120, 'enf_start': 480, 'enf_end': 1080, 'increment': 5}, [720, 234]), ({'now': 420, 'expiry': None, 'paid_cents': 200, 'rate_per_hour': 200, 'max_minutes': 120, 'enf_start': 480, 'enf_end': 1080, 'increment': 25}, [540, 200]), ({'now': 62, 'expiry': None, 'paid_cents': 358, 'rate_per_hour': 250, 'max_minutes': 120, 'enf_start': 480, 'enf_end': 1080, 'increment': 25}, [564, 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)

prompt:
Street meter time purchase returns a wrong result when the time limit is measured from the extension start.

Input {now, expiry (or null), paid_cents, rate_per_hour, max_minutes, enf_start, enf_end, increment}. Only whole increments are credited. At or after enforcement end nothing is sold: [null, 0]. Paid time starts at the later of now, enforcement start and any unexpired expiry; it ends at the earliest of start + purchased minutes (floor), max(now, enf_start) + max_minutes, and enf_end. Accepted cents = min(credited, ceil(used minutes * rate/60)). Return [new expiry, accepted cents].

split: open-access


broken_source:
"""Failure Map reference implementation. Python standard library only."""
import json

N = 1
observations = []
def solve(x):
    inc = x['increment']
    c = x['paid_cents'] - x['paid_cents'] % inc
    now = x['now']
    if now >= x['enf_end']:
        return [None, 0]
    base = max(now, x['enf_start'])
    start = base if x['expiry'] is None else max(base, x['expiry'])
    mins = c * 60 // x['rate_per_hour']
    end = min(start + mins, base + x['max_minutes'])
    used = max(0, end - start)
    cost = min(c, -(-used * x['rate_per_hour'] // 60))
    return [max(end, start), cost]
def check(label, actual, expected):
    observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
fixtures = [[({'now': 1044, 'expiry': None, 'paid_cents': 169, 'rate_per_hour': 100, 'max_minutes': 60, 'enf_start': 480, 'enf_end': 1080, 'increment': 5}, [1080, 60]), ({'now': 600, 'expiry': 650, 'paid_cents': 400, 'rate_per_hour': 200, 'max_minutes': 120, 'enf_start': 480, 'enf_end': 1080, 'increment': 5}, [720, 234]), ({'now': 1080, 'expiry': 1100, 'paid_cents': 100, 'rate_per_hour': 200, 'max_minutes': 120, 'enf_start': 480, 'enf_end': 1080, 'increment': 25}, [None, 0]), ({'now': 1023, 'expiry': 1036, 'paid_cents': 13, 'rate_per_hour': 100, 'max_minutes': 120, 'enf_start': 480, 'enf_end': 1080, 'increment': 25}, [1036, 0]), ({'now': 420, 'expiry': None, 'paid_cents': 200, 'rate_per_hour': 200, 'max_minutes': 120, 'enf_start': 480, 'enf_end': 1080, 'increment': 25}, [540, 200]), ({'now': 480, 'expiry': None, 'paid_cents': 6, 'rate_per_hour': 250, 'max_minutes': 240, 'enf_start': 480, 'enf_end': 1080, 'increment': 25}, [480, 0]), ({'now': 1031, 'expiry': None, 'paid_cents': 459, 'rate_per_hour': 250, 'max_minutes': 240, 'enf_start': 480, 'enf_end': 1080, 'increment': 25}, [1080, 205]), ({'now': 1046, 'expiry': 1039, 'paid_cents': 578, 'rate_per_hour': 100, 'max_minutes': 60, 'enf_start': 480, 'enf_end': 1080, 'increment': 5}, [1080, 57])], [({'now': 1080, 'expiry': 1051, 'paid_cents': 305, 'rate_per_hour': 100, 'max_minutes': 120, 'enf_start': 480, 'enf_end': 1080, 'increment': 5}, [None, 0]), ({'now': 943, 'expiry': 889, 'paid_cents': 449, 'rate_per_hour': 250, 'max_minutes': 240, 'enf_start': 480, 'enf_end': 1080, 'increment': 25}, [1045, 425]), ({'now': 1040, 'expiry': None, 'paid_cents': 333, 'rate_per_hour': 150, 'max_minutes': 120, 'enf_start': 480, 'enf_end': 1080, 'increment': 25}, [1080, 100]), ({'now': 420, 'expiry': None, 'paid_cents': 200, 'rate_per_hour': 200, 'max_minutes': 120, 'enf_start': 480, 'enf_end': 1080, 'increment': 25}, [540, 200]), ({'now': 1080, 'expiry': 1060, 'paid_cents': 69, 'rate_per_hour': 100, 'max_minutes': 120, 'enf_start': 480, 'enf_end': 1080, 'increment': 5}, [None, 0]), ({'now': 1080, 'expiry': 1100, 'paid_cents': 100, 'rate_per_hour': 200, 'max_minutes': 120, 'enf_start': 480, 'enf_end': 1080, 'increment': 25}, [None, 0]), ({'now': 946, 'expiry': 902, 'paid_cents': 565, 'rate_per_hour': 100, 'max_minutes': 240, 'enf_start': 480, 'enf_end': 1080, 'increment': 5}, [1080, 224]), ({'now': 1023, 'expiry': 1102, 'paid_cents': 159, 'rate_per_hour': 150, 'max_minutes': 240, 'enf_start': 480, 'enf_end': 1080, 'increment': 5}, [1102, 0])], [({'now': 878, 'expiry': 862, 'paid_cents': 455, 'rate_per_hour': 100, 'max_minutes': 120, 'enf_start': 480, 'enf_end': 1080, 'increment': 25}, [998, 200]), ({'now': 991, 'expiry': 997, 'paid_cents': 386, 'rate_per_hour': 200, 'max_minutes': 120, 'enf_start': 480, 'enf_end': 1080, 'increment': 25}, [1080, 277]), ({'now': 1040, 'expiry': None, 'paid_cents': 333, 'rate_per_hour': 150, 'max_minutes': 120, 'enf_start': 480, 'enf_end': 1080, 'increment': 25}, [1080, 100]), ({'now': 600, 'expiry': 650, 'paid_cents': 400, 'rate_per_hour': 200, 'max_minutes': 120, 'enf_start': 480, 'enf_end': 1080, 'increment': 5}, [720, 234]), ({'now': 1080, 'expiry': 1158, 'paid_cents': 156, 'rate_per_hour': 150, 'max_minutes': 240, 'enf_start': 480, 'enf_end': 1080, 'increment': 25}, [None, 0]), ({'now': 331, 'expiry': 299, 'paid_cents': 450, 'rate_per_hour': 150, 'max_minutes': 120, 'enf_start': 480, 'enf_end': 1080, 'increment': 25}, [600, 300]), ({'now': 1080, 'expiry': 1100, 'paid_cents': 100, 'rate_per_hour': 200, 'max_minutes': 120, 'enf_start': 480, 'enf_end': 1080, 'increment': 25}, [None, 0]), ({'now': 1055, 'expiry': 1058, 'paid_cents': 559, 'rate_per_hour': 150, 'max_minutes': 60, 'enf_start': 480, 'enf_end': 1080, 'increment': 5}, [1080, 55])], [({'now': 420, 'expiry': None, 'paid_cents': 200, 'rate_per_hour': 200, 'max_minutes': 120, 'enf_start': 480, 'enf_end': 1080, 'increment': 25}, [540, 200]), ({'now': 1080, 'expiry': 1151, 'paid_cents': 314, 'rate_per_hour': 200, 'max_minutes': 240, 'enf_start': 480, 'enf_end': 1080, 'increment': 5}, [None, 0]), ({'now': 1040, 'expiry': None, 'paid_cents': 333, 'rate_per_hour': 150, 'max_minutes': 120, 'enf_start': 480, 'enf_end': 1080, 'increment': 25}, [1080, 100]), ({'now': 1080, 'expiry': 1100, 'paid_cents': 100, 'rate_per_hour': 200, 'max_minutes': 120, 'enf_start': 480, 'enf_end': 1080, 'increment': 25}, [None, 0]), ({'now': 480, 'expiry': 478, 'paid_cents': 452, 'rate_per_hour': 100, 'max_minutes': 60, 'enf_start': 480, 'enf_end': 1080, 'increment': 25}, [540, 100]), ({'now': 972, 'expiry': None, 'paid_cents': 526, 'rate_per_hour': 150, 'max_minutes': 120, 'enf_start': 480, 'enf_end': 1080, 'increment': 25}, [1080, 270]), ({'now': 1018, 'expiry': 1010, 'paid_cents': 189, 'rate_per_hour': 100, 'max_minutes': 120, 'enf_start': 480, 'enf_end': 1080, 'increment': 5}, [1080, 104]), ({'now': 1080, 'expiry': None, 'paid_cents': 266, 'rate_per_hour': 200, 'max_minutes': 60, 'enf_start': 480, 'enf_end': 1080, 'increment': 25}, [None, 0])], [({'now': 1415, 'expiry': 1420, 'paid_cents': 119, 'rate_per_hour': 200, 'max_minutes': 60, 'enf_start': 480, 'enf_end': 1080, 'increment': 5}, [None, 0]), ({'now': 1080, 'expiry': 1140, 'paid_cents': 5, 'rate_per_hour': 100, 'max_minutes': 120, 'enf_start': 480, 'enf_end': 1080, 'increment': 5}, [None, 0]), ({'now': 420, 'expiry': None, 'paid_cents': 200, 'rate_per_hour': 200, 'max_minutes': 120, 'enf_start': 480, 'enf_end': 1080, 'increment': 25}, [540, 200]), ({'now': 1040, 'expiry': None, 'paid_cents': 333, 'rate_per_hour': 150, 'max_minutes': 120, 'enf_start': 480, 'enf_end': 1080, 'increment': 25}, [1080, 100]), ({'now': 1080, 'expiry': 1100, 'paid_cents': 100, 'rate_per_hour': 200, 'max_minutes': 120, 'enf_start': 480, 'enf_end': 1080, 'increment': 25}, [None, 0]), ({'now': 1080, 'expiry': 1030, 'paid_cents': 119, 'rate_per_hour': 150, 'max_minutes': 120, 'enf_start': 480, 'enf_end': 1080, 'increment': 25}, [None, 0]), ({'now': 1075, 'expiry': 1017, 'paid_cents': 282, 'rate_per_hour': 250, 'max_minutes': 60, 'enf_start': 480, 'enf_end': 1080, 'increment': 5}, [1080, 21]), ({'now': 1011, 'expiry': 1080, 'paid_cents': 532, 'rate_per_hour': 200, 'max_minutes': 240, 'enf_start': 480, 'enf_end': 1080, 'increment': 5}, [1080, 0])]]
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)

source:
"""Failure Map reference implementation. Python standard library only."""
import json

N = 1
observations = []
def solve(x):
    inc = x['increment']
    c = x['paid_cents'] - x['paid_cents'] % inc
    now = x['now']
    if now >= x['enf_end']:
        return [None, 0]
    base = max(now, x['enf_start'])
    start = base if x['expiry'] is None else max(base, x['expiry'])
    mins = c * 60 // x['rate_per_hour']
    end = min(start + mins, base + x['max_minutes'], 1440)
    used = max(0, end - start)
    cost = min(c, -(-used * x['rate_per_hour'] // 60))
    return [max(end, start), cost]
def check(label, actual, expected):
    observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
fixtures = [[({'now': 1044, 'expiry': None, 'paid_cents': 169, 'rate_per_hour': 100, 'max_minutes': 60, 'enf_start': 480, 'enf_end': 1080, 'increment': 5}, [1080, 60]), ({'now': 600, 'expiry': 650, 'paid_cents': 400, 'rate_per_hour': 200, 'max_minutes': 120, 'enf_start': 480, 'enf_end': 1080, 'increment': 5}, [720, 234]), ({'now': 1080, 'expiry': 1100, 'paid_cents': 100, 'rate_per_hour': 200, 'max_minutes': 120, 'enf_start': 480, 'enf_end': 1080, 'increment': 25}, [None, 0]), ({'now': 1023, 'expiry': 1036, 'paid_cents': 13, 'rate_per_hour': 100, 'max_minutes': 120, 'enf_start': 480, 'enf_end': 1080, 'increment': 25}, [1036, 0]), ({'now': 420, 'expiry': None, 'paid_cents': 200, 'rate_per_hour': 200, 'max_minutes': 120, 'enf_start': 480, 'enf_end': 1080, 'increment': 25}, [540, 200]), ({'now': 480, 'expiry': None, 'paid_cents': 6, 'rate_per_hour': 250, 'max_minutes': 240, 'enf_start': 480, 'enf_end': 1080, 'increment': 25}, [480, 0]), ({'now': 1031, 'expiry': None, 'paid_cents': 459, 'rate_per_hour': 250, 'max_minutes': 240, 'enf_start': 480, 'enf_end': 1080, 'increment': 25}, [1080, 205]), ({'now': 1046, 'expiry': 1039, 'paid_cents': 578, 'rate_per_hour': 100, 'max_minutes': 60, 'enf_start': 480, 'enf_end': 1080, 'increment': 5}, [1080, 57])], [({'now': 1080, 'expiry': 1051, 'paid_cents': 305, 'rate_per_hour': 100, 'max_minutes': 120, 'enf_start': 480, 'enf_end': 1080, 'increment': 5}, [None, 0]), ({'now': 943, 'expiry': 889, 'paid_cents': 449, 'rate_per_hour': 250, 'max_minutes': 240, 'enf_start': 480, 'enf_end': 1080, 'increment': 25}, [1045, 425]), ({'now': 1040, 'expiry': None, 'paid_cents': 333, 'rate_per_hour': 150, 'max_minutes': 120, 'enf_start': 480, 'enf_end': 1080, 'increment': 25}, [1080, 100]), ({'now': 420, 'expiry': None, 'paid_cents': 200, 'rate_per_hour': 200, 'max_minutes': 120, 'enf_start': 480, 'enf_end': 1080, 'increment': 25}, [540, 200]), ({'now': 1080, 'expiry': 1060, 'paid_cents': 69, 'rate_per_hour': 100, 'max_minutes': 120, 'enf_start': 480, 'enf_end': 1080, 'increment': 5}, [None, 0]), ({'now': 1080, 'expiry': 1100, 'paid_cents': 100, 'rate_per_hour': 200, 'max_minutes': 120, 'enf_start': 480, 'enf_end': 1080, 'increment': 25}, [None, 0]), ({'now': 946, 'expiry': 902, 'paid_cents': 565, 'rate_per_hour': 100, 'max_minutes': 240, 'enf_start': 480, 'enf_end': 1080, 'increment': 5}, [1080, 224]), ({'now': 1023, 'expiry': 1102, 'paid_cents': 159, 'rate_per_hour': 150, 'max_minutes': 240, 'enf_start': 480, 'enf_end': 1080, 'increment': 5}, [1102, 0])], [({'now': 878, 'expiry': 862, 'paid_cents': 455, 'rate_per_hour': 100, 'max_minutes': 120, 'enf_start': 480, 'enf_end': 1080, 'increment': 25}, [998, 200]), ({'now': 991, 'expiry': 997, 'paid_cents': 386, 'rate_per_hour': 200, 'max_minutes': 120, 'enf_start': 480, 'enf_end': 1080, 'increment': 25}, [1080, 277]), ({'now': 1040, 'expiry': None, 'paid_cents': 333, 'rate_per_hour': 150, 'max_minutes': 120, 'enf_start': 480, 'enf_end': 1080, 'increment': 25}, [1080, 100]), ({'now': 600, 'expiry': 650, 'paid_cents': 400, 'rate_per_hour': 200, 'max_minutes': 120, 'enf_start': 480, 'enf_end': 1080, 'increment': 5}, [720, 234]), ({'now': 1080, 'expiry': 1158, 'paid_cents': 156, 'rate_per_hour': 150, 'max_minutes': 240, 'enf_start': 480, 'enf_end': 1080, 'increment': 25}, [None, 0]), ({'now': 331, 'expiry': 299, 'paid_cents': 450, 'rate_per_hour': 150, 'max_minutes': 120, 'enf_start': 480, 'enf_end': 1080, 'increment': 25}, [600, 300]), ({'now': 1080, 'expiry': 1100, 'paid_cents': 100, 'rate_per_hour': 200, 'max_minutes': 120, 'enf_start': 480, 'enf_end': 1080, 'increment': 25}, [None, 0]), ({'now': 1055, 'expiry': 1058, 'paid_cents': 559, 'rate_per_hour': 150, 'max_minutes': 60, 'enf_start': 480, 'enf_end': 1080, 'increment': 5}, [1080, 55])], [({'now': 420, 'expiry': None, 'paid_cents': 200, 'rate_per_hour': 200, 'max_minutes': 120, 'enf_start': 480, 'enf_end': 1080, 'increment': 25}, [540, 200]), ({'now': 1080, 'expiry': 1151, 'paid_cents': 314, 'rate_per_hour': 200, 'max_minutes': 240, 'enf_start': 480, 'enf_end': 1080, 'increment': 5}, [None, 0]), ({'now': 1040, 'expiry': None, 'paid_cents': 333, 'rate_per_hour': 150, 'max_minutes': 120, 'enf_start': 480, 'enf_end': 1080, 'increment': 25}, [1080, 100]), ({'now': 1080, 'expiry': 1100, 'paid_cents': 100, 'rate_per_hour': 200, 'max_minutes': 120, 'enf_start': 480, 'enf_end': 1080, 'increment': 25}, [None, 0]), ({'now': 480, 'expiry': 478, 'paid_cents': 452, 'rate_per_hour': 100, 'max_minutes': 60, 'enf_start': 480, 'enf_end': 1080, 'increment': 25}, [540, 100]), ({'now': 972, 'expiry': None, 'paid_cents': 526, 'rate_per_hour': 150, 'max_minutes': 120, 'enf_start': 480, 'enf_end': 1080, 'increment': 25}, [1080, 270]), ({'now': 1018, 'expiry': 1010, 'paid_cents': 189, 'rate_per_hour': 100, 'max_minutes': 120, 'enf_start': 480, 'enf_end': 1080, 'increment': 5}, [1080, 104]), ({'now': 1080, 'expiry': None, 'paid_cents': 266, 'rate_per_hour': 200, 'max_minutes': 60, 'enf_start': 480, 'enf_end': 1080, 'increment': 25}, [None, 0])], [({'now': 1415, 'expiry': 1420, 'paid_cents': 119, 'rate_per_hour': 200, 'max_minutes': 60, 'enf_start': 480, 'enf_end': 1080, 'increment': 5}, [None, 0]), ({'now': 1080, 'expiry': 1140, 'paid_cents': 5, 'rate_per_hour': 100, 'max_minutes': 120, 'enf_start': 480, 'enf_end': 1080, 'increment': 5}, [None, 0]), ({'now': 420, 'expiry': None, 'paid_cents': 200, 'rate_per_hour': 200, 'max_minutes': 120, 'enf_start': 480, 'enf_end': 1080, 'increment': 25}, [540, 200]), ({'now': 1040, 'expiry': None, 'paid_cents': 333, 'rate_per_hour': 150, 'max_minutes': 120, 'enf_start': 480, 'enf_end': 1080, 'increment': 25}, [1080, 100]), ({'now': 1080, 'expiry': 1100, 'paid_cents': 100, 'rate_per_hour': 200, 'max_minutes': 120, 'enf_start': 480, 'enf_end': 1080, 'increment': 25}, [None, 0]), ({'now': 1080, 'expiry': 1030, 'paid_cents': 119, 'rate_per_hour': 150, 'max_minutes': 120, 'enf_start': 480, 'enf_end': 1080, 'increment': 25}, [None, 0]), ({'now': 1075, 'expiry': 1017, 'paid_cents': 282, 'rate_per_hour': 250, 'max_minutes': 60, 'enf_start': 480, 'enf_end': 1080, 'increment': 5}, [1080, 21]), ({'now': 1011, 'expiry': 1080, 'paid_cents': 532, 'rate_per_hour': 200, 'max_minutes': 240, 'enf_start': 480, 'enf_end': 1080, 'increment': 5}, [1080, 0])]]
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)

prompt:
Street meter time purchase returns a wrong result when purchased time runs past enforcement end.

Input {now, expiry (or null), paid_cents, rate_per_hour, max_minutes, enf_start, enf_end, increment}. Only whole increments are credited. At or after enforcement end nothing is sold: [null, 0]. Paid time starts at the later of now, enforcement start and any unexpired expiry; it ends at the earliest of start + purchased minutes (floor), max(now, enf_start) + max_minutes, and enf_end. Accepted cents = min(credited, ceil(used minutes * rate/60)). Return [new expiry, accepted cents].

split: open-access