{"abstract":"A redemption is accepted against expired points and then only partly filled.","category":"Loyalty points ledgers","checks":8,"contract":"Events [\"earn\",day,pts,ttl] create a lot expiring at day+ttl (usable through that day); [\"redeem\",day,pts] draws only unexpired lots, soonest expiry first (ties by earn day), rejecting with \"insufficient\" when unexpired points are short; [\"sweep\",day] removes lots whose expiry is before day and reports the points removed. Emptied lots are dropped. Return {results, lots:[[expiry, earn_day, pts]]}.","evaluation_group":"w2-loyalty-points-ledgers-expiring-lots","failed_approach":"Checking only lots expiring strictly after today rejects redemptions that live lots can fill.","family":"w2-loyalty-points-ledgers-expiring-lots-availability-pool","id":"FA-60396","implementations":{"attempt":{"sha256":"d90e3c0c5c7b9cf1e97a8b85feafb9a87e66e860119b325f24e9ba304e85723e","source":"\"\"\"Failure Map reference implementation. Python standard library only.\"\"\"\nimport json\n\nN = 1\nobservations = []\ndef solve(x):\n    lots = []\n    out = []\n    for ev in x['events']:\n        kind, day = ev[0], ev[1]\n        if kind == 'earn':\n            exp = day + ev[3]\n            lots.append([exp, day, ev[2]])\n            out.append(exp)\n        elif kind == 'sweep':\n            gone = sum(l[2] for l in lots if l[0] < day)\n            lots = [l for l in lots if l[0] >= day]\n            out.append(gone)\n        else:\n            need = ev[2]\n            live = sorted([l for l in lots if l[0] >= day], key=lambda l: (l[0], l[1]))\n            if need > sum(l[2] for l in live if l[0] > day):\n                out.append('insufficient')\n                continue\n            used = []\n            for l in live:\n                t = min(need, l[2])\n                if t > 0:\n                    l[2] -= t\n                    need -= t\n                    used.append([l[0], t])\n            lots = [l for l in lots if l[2] > 0]\n            out.append(used)\n    return {'results': out, 'lots': lots}\ndef check(label, actual, expected):\n    observations.append({\"check\": label, \"actual\": actual, \"expected\": expected, \"passed\": actual == expected})\nfixtures = [[['soonest expiry first', {'events': [['earn', 1, 500, 365], ['earn', 10, 300, 31], ['redeem', 20, 400]]}, {'results': [366, 41, [[41, 300], [366, 100]]], 'lots': [[366, 1, 400]]}], ['use on expiry day', {'events': [['earn', 0, 200, 30], ['redeem', 30, 151]]}, {'results': [30, [[30, 151]]], 'lots': [[30, 0, 49]]}], ['expired unusable', {'events': [['earn', 0, 200, 30], ['earn', 5, 50, 90], ['redeem', 31, 101]]}, {'results': [30, 95, 'insufficient'], 'lots': [[30, 0, 200], [95, 5, 50]]}], ['sweep reports points', {'events': [['earn', 0, 120, 10], ['earn', 0, 81, 10], ['earn', 3, 60, 100], ['sweep', 11]]}, {'results': [10, 10, 103, 201], 'lots': [[103, 3, 60]]}], ['sweep boundary', {'events': [['earn', 0, 70, 11], ['sweep', 11], ['sweep', 12]]}, {'results': [11, 0, 70], 'lots': []}], ['partial lot cleanup', {'events': [['earn', 0, 100, 50], ['earn', 1, 100, 60], ['redeem', 2, 100], ['redeem', 3, 31]]}, {'results': [50, 61, [[50, 100]], [[61, 31]]], 'lots': [[61, 1, 69]]}], ['insufficient', {'events': [['earn', 0, 100, 50], ['redeem', 1, 151], ['redeem', 2, 100]]}, {'results': [50, 'insufficient', [[50, 100]]], 'lots': []}], ['ties by earn day', {'events': [['earn', 5, 40, 20], ['earn', 1, 40, 24], ['redeem', 6, 51]]}, {'results': [25, 25, [[25, 40], [25, 11]]], 'lots': [[25, 5, 29]]}]], [['soonest expiry first', {'events': [['earn', 1, 500, 365], ['earn', 10, 300, 32], ['redeem', 20, 400]]}, {'results': [366, 42, [[42, 300], [366, 100]]], 'lots': [[366, 1, 400]]}], ['use on expiry day', {'events': [['earn', 0, 200, 30], ['redeem', 30, 152]]}, {'results': [30, [[30, 152]]], 'lots': [[30, 0, 48]]}], ['expired unusable', {'events': [['earn', 0, 200, 30], ['earn', 5, 50, 90], ['redeem', 31, 102]]}, {'results': [30, 95, 'insufficient'], 'lots': [[30, 0, 200], [95, 5, 50]]}], ['sweep reports points', {'events': [['earn', 0, 120, 10], ['earn', 0, 82, 10], ['earn', 3, 60, 100], ['sweep', 11]]}, {'results': [10, 10, 103, 202], 'lots': [[103, 3, 60]]}], ['sweep boundary', {'events': [['earn', 0, 70, 12], ['sweep', 12], ['sweep', 13]]}, {'results': [12, 0, 70], 'lots': []}], ['partial lot cleanup', {'events': [['earn', 0, 100, 50], ['earn', 1, 100, 60], ['redeem', 2, 100], ['redeem', 3, 32]]}, {'results': [50, 61, [[50, 100]], [[61, 32]]], 'lots': [[61, 1, 68]]}], ['insufficient', {'events': [['earn', 0, 100, 50], ['redeem', 1, 152], ['redeem', 2, 100]]}, {'results': [50, 'insufficient', [[50, 100]]], 'lots': []}], ['ties by earn day', {'events': [['earn', 5, 40, 20], ['earn', 1, 40, 24], ['redeem', 6, 52]]}, {'results': [25, 25, [[25, 40], [25, 12]]], 'lots': [[25, 5, 28]]}]], [['soonest expiry first', {'events': [['earn', 1, 500, 365], ['earn', 10, 300, 33], ['redeem', 20, 400]]}, {'results': [366, 43, [[43, 300], [366, 100]]], 'lots': [[366, 1, 400]]}], ['use on expiry day', {'events': [['earn', 0, 200, 30], ['redeem', 30, 153]]}, {'results': [30, [[30, 153]]], 'lots': [[30, 0, 47]]}], ['expired unusable', {'events': [['earn', 0, 200, 30], ['earn', 5, 50, 90], ['redeem', 31, 103]]}, {'results': [30, 95, 'insufficient'], 'lots': [[30, 0, 200], [95, 5, 50]]}], ['sweep reports points', {'events': [['earn', 0, 120, 10], ['earn', 0, 83, 10], ['earn', 3, 60, 100], ['sweep', 11]]}, {'results': [10, 10, 103, 203], 'lots': [[103, 3, 60]]}], ['sweep boundary', {'events': [['earn', 0, 70, 13], ['sweep', 13], ['sweep', 14]]}, {'results': [13, 0, 70], 'lots': []}], ['partial lot cleanup', {'events': [['earn', 0, 100, 50], ['earn', 1, 100, 60], ['redeem', 2, 100], ['redeem', 3, 33]]}, {'results': [50, 61, [[50, 100]], [[61, 33]]], 'lots': [[61, 1, 67]]}], ['insufficient', {'events': [['earn', 0, 100, 50], ['redeem', 1, 153], ['redeem', 2, 100]]}, {'results': [50, 'insufficient', [[50, 100]]], 'lots': []}], ['ties by earn day', {'events': [['earn', 5, 40, 20], ['earn', 1, 40, 24], ['redeem', 6, 53]]}, {'results': [25, 25, [[25, 40], [25, 13]]], 'lots': [[25, 5, 27]]}]], [['soonest expiry first', {'events': [['earn', 1, 500, 365], ['earn', 10, 300, 34], ['redeem', 20, 400]]}, {'results': [366, 44, [[44, 300], [366, 100]]], 'lots': [[366, 1, 400]]}], ['use on expiry day', {'events': [['earn', 0, 200, 30], ['redeem', 30, 154]]}, {'results': [30, [[30, 154]]], 'lots': [[30, 0, 46]]}], ['expired unusable', {'events': [['earn', 0, 200, 30], ['earn', 5, 50, 90], ['redeem', 31, 104]]}, {'results': [30, 95, 'insufficient'], 'lots': [[30, 0, 200], [95, 5, 50]]}], ['sweep reports points', {'events': [['earn', 0, 120, 10], ['earn', 0, 84, 10], ['earn', 3, 60, 100], ['sweep', 11]]}, {'results': [10, 10, 103, 204], 'lots': [[103, 3, 60]]}], ['sweep boundary', {'events': [['earn', 0, 70, 14], ['sweep', 14], ['sweep', 15]]}, {'results': [14, 0, 70], 'lots': []}], ['partial lot cleanup', {'events': [['earn', 0, 100, 50], ['earn', 1, 100, 60], ['redeem', 2, 100], ['redeem', 3, 34]]}, {'results': [50, 61, [[50, 100]], [[61, 34]]], 'lots': [[61, 1, 66]]}], ['insufficient', {'events': [['earn', 0, 100, 50], ['redeem', 1, 154], ['redeem', 2, 100]]}, {'results': [50, 'insufficient', [[50, 100]]], 'lots': []}], ['ties by earn day', {'events': [['earn', 5, 40, 20], ['earn', 1, 40, 24], ['redeem', 6, 54]]}, {'results': [25, 25, [[25, 40], [25, 14]]], 'lots': [[25, 5, 26]]}]], [['soonest expiry first', {'events': [['earn', 1, 500, 365], ['earn', 10, 300, 35], ['redeem', 20, 400]]}, {'results': [366, 45, [[45, 300], [366, 100]]], 'lots': [[366, 1, 400]]}], ['use on expiry day', {'events': [['earn', 0, 200, 30], ['redeem', 30, 155]]}, {'results': [30, [[30, 155]]], 'lots': [[30, 0, 45]]}], ['expired unusable', {'events': [['earn', 0, 200, 30], ['earn', 5, 50, 90], ['redeem', 31, 105]]}, {'results': [30, 95, 'insufficient'], 'lots': [[30, 0, 200], [95, 5, 50]]}], ['sweep reports points', {'events': [['earn', 0, 120, 10], ['earn', 0, 85, 10], ['earn', 3, 60, 100], ['sweep', 11]]}, {'results': [10, 10, 103, 205], 'lots': [[103, 3, 60]]}], ['sweep boundary', {'events': [['earn', 0, 70, 15], ['sweep', 15], ['sweep', 16]]}, {'results': [15, 0, 70], 'lots': []}], ['partial lot cleanup', {'events': [['earn', 0, 100, 50], ['earn', 1, 100, 60], ['redeem', 2, 100], ['redeem', 3, 35]]}, {'results': [50, 61, [[50, 100]], [[61, 35]]], 'lots': [[61, 1, 65]]}], ['insufficient', {'events': [['earn', 0, 100, 50], ['redeem', 1, 155], ['redeem', 2, 100]]}, {'results': [50, 'insufficient', [[50, 100]]], 'lots': []}], ['ties by earn day', {'events': [['earn', 5, 40, 20], ['earn', 1, 40, 24], ['redeem', 6, 55]]}, {'results': [25, 25, [[25, 40], [25, 15]]], 'lots': [[25, 5, 25]]}]]]\nfor label, args, expected in fixtures[N-1]:\n    check(label, solve(args), expected)\nprint(json.dumps({\"observations\": observations, \"passed\": all(x[\"passed\"] for x in observations)}, ensure_ascii=False))\nraise SystemExit(0 if all(x[\"passed\"] for x in observations) else 1)\n"},"broken":{"sha256":"6da79937c384adce34392d8b542e7aa6115259f62d4b14949e25682873fe7bbc","source":"\"\"\"Failure Map reference implementation. Python standard library only.\"\"\"\nimport json\n\nN = 1\nobservations = []\ndef solve(x):\n    lots = []\n    out = []\n    for ev in x['events']:\n        kind, day = ev[0], ev[1]\n        if kind == 'earn':\n            exp = day + ev[3]\n            lots.append([exp, day, ev[2]])\n            out.append(exp)\n        elif kind == 'sweep':\n            gone = sum(l[2] for l in lots if l[0] < day)\n            lots = [l for l in lots if l[0] >= day]\n            out.append(gone)\n        else:\n            need = ev[2]\n            live = sorted([l for l in lots if l[0] >= day], key=lambda l: (l[0], l[1]))\n            if need > sum(l[2] for l in lots):\n                out.append('insufficient')\n                continue\n            used = []\n            for l in live:\n                t = min(need, l[2])\n                if t > 0:\n                    l[2] -= t\n                    need -= t\n                    used.append([l[0], t])\n            lots = [l for l in lots if l[2] > 0]\n            out.append(used)\n    return {'results': out, 'lots': lots}\ndef check(label, actual, expected):\n    observations.append({\"check\": label, \"actual\": actual, \"expected\": expected, \"passed\": actual == expected})\nfixtures = [[['soonest expiry first', {'events': [['earn', 1, 500, 365], ['earn', 10, 300, 31], ['redeem', 20, 400]]}, {'results': [366, 41, [[41, 300], [366, 100]]], 'lots': [[366, 1, 400]]}], ['use on expiry day', {'events': [['earn', 0, 200, 30], ['redeem', 30, 151]]}, {'results': [30, [[30, 151]]], 'lots': [[30, 0, 49]]}], ['expired unusable', {'events': [['earn', 0, 200, 30], ['earn', 5, 50, 90], ['redeem', 31, 101]]}, {'results': [30, 95, 'insufficient'], 'lots': [[30, 0, 200], [95, 5, 50]]}], ['sweep reports points', {'events': [['earn', 0, 120, 10], ['earn', 0, 81, 10], ['earn', 3, 60, 100], ['sweep', 11]]}, {'results': [10, 10, 103, 201], 'lots': [[103, 3, 60]]}], ['sweep boundary', {'events': [['earn', 0, 70, 11], ['sweep', 11], ['sweep', 12]]}, {'results': [11, 0, 70], 'lots': []}], ['partial lot cleanup', {'events': [['earn', 0, 100, 50], ['earn', 1, 100, 60], ['redeem', 2, 100], ['redeem', 3, 31]]}, {'results': [50, 61, [[50, 100]], [[61, 31]]], 'lots': [[61, 1, 69]]}], ['insufficient', {'events': [['earn', 0, 100, 50], ['redeem', 1, 151], ['redeem', 2, 100]]}, {'results': [50, 'insufficient', [[50, 100]]], 'lots': []}], ['ties by earn day', {'events': [['earn', 5, 40, 20], ['earn', 1, 40, 24], ['redeem', 6, 51]]}, {'results': [25, 25, [[25, 40], [25, 11]]], 'lots': [[25, 5, 29]]}]], [['soonest expiry first', {'events': [['earn', 1, 500, 365], ['earn', 10, 300, 32], ['redeem', 20, 400]]}, {'results': [366, 42, [[42, 300], [366, 100]]], 'lots': [[366, 1, 400]]}], ['use on expiry day', {'events': [['earn', 0, 200, 30], ['redeem', 30, 152]]}, {'results': [30, [[30, 152]]], 'lots': [[30, 0, 48]]}], ['expired unusable', {'events': [['earn', 0, 200, 30], ['earn', 5, 50, 90], ['redeem', 31, 102]]}, {'results': [30, 95, 'insufficient'], 'lots': [[30, 0, 200], [95, 5, 50]]}], ['sweep reports points', {'events': [['earn', 0, 120, 10], ['earn', 0, 82, 10], ['earn', 3, 60, 100], ['sweep', 11]]}, {'results': [10, 10, 103, 202], 'lots': [[103, 3, 60]]}], ['sweep boundary', {'events': [['earn', 0, 70, 12], ['sweep', 12], ['sweep', 13]]}, {'results': [12, 0, 70], 'lots': []}], ['partial lot cleanup', {'events': [['earn', 0, 100, 50], ['earn', 1, 100, 60], ['redeem', 2, 100], ['redeem', 3, 32]]}, {'results': [50, 61, [[50, 100]], [[61, 32]]], 'lots': [[61, 1, 68]]}], ['insufficient', {'events': [['earn', 0, 100, 50], ['redeem', 1, 152], ['redeem', 2, 100]]}, {'results': [50, 'insufficient', [[50, 100]]], 'lots': []}], ['ties by earn day', {'events': [['earn', 5, 40, 20], ['earn', 1, 40, 24], ['redeem', 6, 52]]}, {'results': [25, 25, [[25, 40], [25, 12]]], 'lots': [[25, 5, 28]]}]], [['soonest expiry first', {'events': [['earn', 1, 500, 365], ['earn', 10, 300, 33], ['redeem', 20, 400]]}, {'results': [366, 43, [[43, 300], [366, 100]]], 'lots': [[366, 1, 400]]}], ['use on expiry day', {'events': [['earn', 0, 200, 30], ['redeem', 30, 153]]}, {'results': [30, [[30, 153]]], 'lots': [[30, 0, 47]]}], ['expired unusable', {'events': [['earn', 0, 200, 30], ['earn', 5, 50, 90], ['redeem', 31, 103]]}, {'results': [30, 95, 'insufficient'], 'lots': [[30, 0, 200], [95, 5, 50]]}], ['sweep reports points', {'events': [['earn', 0, 120, 10], ['earn', 0, 83, 10], ['earn', 3, 60, 100], ['sweep', 11]]}, {'results': [10, 10, 103, 203], 'lots': [[103, 3, 60]]}], ['sweep boundary', {'events': [['earn', 0, 70, 13], ['sweep', 13], ['sweep', 14]]}, {'results': [13, 0, 70], 'lots': []}], ['partial lot cleanup', {'events': [['earn', 0, 100, 50], ['earn', 1, 100, 60], ['redeem', 2, 100], ['redeem', 3, 33]]}, {'results': [50, 61, [[50, 100]], [[61, 33]]], 'lots': [[61, 1, 67]]}], ['insufficient', {'events': [['earn', 0, 100, 50], ['redeem', 1, 153], ['redeem', 2, 100]]}, {'results': [50, 'insufficient', [[50, 100]]], 'lots': []}], ['ties by earn day', {'events': [['earn', 5, 40, 20], ['earn', 1, 40, 24], ['redeem', 6, 53]]}, {'results': [25, 25, [[25, 40], [25, 13]]], 'lots': [[25, 5, 27]]}]], [['soonest expiry first', {'events': [['earn', 1, 500, 365], ['earn', 10, 300, 34], ['redeem', 20, 400]]}, {'results': [366, 44, [[44, 300], [366, 100]]], 'lots': [[366, 1, 400]]}], ['use on expiry day', {'events': [['earn', 0, 200, 30], ['redeem', 30, 154]]}, {'results': [30, [[30, 154]]], 'lots': [[30, 0, 46]]}], ['expired unusable', {'events': [['earn', 0, 200, 30], ['earn', 5, 50, 90], ['redeem', 31, 104]]}, {'results': [30, 95, 'insufficient'], 'lots': [[30, 0, 200], [95, 5, 50]]}], ['sweep reports points', {'events': [['earn', 0, 120, 10], ['earn', 0, 84, 10], ['earn', 3, 60, 100], ['sweep', 11]]}, {'results': [10, 10, 103, 204], 'lots': [[103, 3, 60]]}], ['sweep boundary', {'events': [['earn', 0, 70, 14], ['sweep', 14], ['sweep', 15]]}, {'results': [14, 0, 70], 'lots': []}], ['partial lot cleanup', {'events': [['earn', 0, 100, 50], ['earn', 1, 100, 60], ['redeem', 2, 100], ['redeem', 3, 34]]}, {'results': [50, 61, [[50, 100]], [[61, 34]]], 'lots': [[61, 1, 66]]}], ['insufficient', {'events': [['earn', 0, 100, 50], ['redeem', 1, 154], ['redeem', 2, 100]]}, {'results': [50, 'insufficient', [[50, 100]]], 'lots': []}], ['ties by earn day', {'events': [['earn', 5, 40, 20], ['earn', 1, 40, 24], ['redeem', 6, 54]]}, {'results': [25, 25, [[25, 40], [25, 14]]], 'lots': [[25, 5, 26]]}]], [['soonest expiry first', {'events': [['earn', 1, 500, 365], ['earn', 10, 300, 35], ['redeem', 20, 400]]}, {'results': [366, 45, [[45, 300], [366, 100]]], 'lots': [[366, 1, 400]]}], ['use on expiry day', {'events': [['earn', 0, 200, 30], ['redeem', 30, 155]]}, {'results': [30, [[30, 155]]], 'lots': [[30, 0, 45]]}], ['expired unusable', {'events': [['earn', 0, 200, 30], ['earn', 5, 50, 90], ['redeem', 31, 105]]}, {'results': [30, 95, 'insufficient'], 'lots': [[30, 0, 200], [95, 5, 50]]}], ['sweep reports points', {'events': [['earn', 0, 120, 10], ['earn', 0, 85, 10], ['earn', 3, 60, 100], ['sweep', 11]]}, {'results': [10, 10, 103, 205], 'lots': [[103, 3, 60]]}], ['sweep boundary', {'events': [['earn', 0, 70, 15], ['sweep', 15], ['sweep', 16]]}, {'results': [15, 0, 70], 'lots': []}], ['partial lot cleanup', {'events': [['earn', 0, 100, 50], ['earn', 1, 100, 60], ['redeem', 2, 100], ['redeem', 3, 35]]}, {'results': [50, 61, [[50, 100]], [[61, 35]]], 'lots': [[61, 1, 65]]}], ['insufficient', {'events': [['earn', 0, 100, 50], ['redeem', 1, 155], ['redeem', 2, 100]]}, {'results': [50, 'insufficient', [[50, 100]]], 'lots': []}], ['ties by earn day', {'events': [['earn', 5, 40, 20], ['earn', 1, 40, 24], ['redeem', 6, 55]]}, {'results': [25, 25, [[25, 40], [25, 15]]], 'lots': [[25, 5, 25]]}]]]\nfor label, args, expected in fixtures[N-1]:\n    check(label, solve(args), expected)\nprint(json.dumps({\"observations\": observations, \"passed\": all(x[\"passed\"] for x in observations)}, ensure_ascii=False))\nraise SystemExit(0 if all(x[\"passed\"] for x in observations) else 1)\n"},"fixed":{"sha256":"5bc68b951670e50c9a94821866bb25d5150737754da5f6fdee108df25fde5473","source":"\"\"\"Failure Map reference implementation. Python standard library only.\"\"\"\nimport json\n\nN = 1\nobservations = []\ndef solve(x):\n    lots = []\n    out = []\n    for ev in x['events']:\n        kind, day = ev[0], ev[1]\n        if kind == 'earn':\n            exp = day + ev[3]\n            lots.append([exp, day, ev[2]])\n            out.append(exp)\n        elif kind == 'sweep':\n            gone = sum(l[2] for l in lots if l[0] < day)\n            lots = [l for l in lots if l[0] >= day]\n            out.append(gone)\n        else:\n            need = ev[2]\n            live = sorted([l for l in lots if l[0] >= day], key=lambda l: (l[0], l[1]))\n            if need > sum(l[2] for l in live):\n                out.append('insufficient')\n                continue\n            used = []\n            for l in live:\n                t = min(need, l[2])\n                if t > 0:\n                    l[2] -= t\n                    need -= t\n                    used.append([l[0], t])\n            lots = [l for l in lots if l[2] > 0]\n            out.append(used)\n    return {'results': out, 'lots': lots}\ndef check(label, actual, expected):\n    observations.append({\"check\": label, \"actual\": actual, \"expected\": expected, \"passed\": actual == expected})\nfixtures = [[['soonest expiry first', {'events': [['earn', 1, 500, 365], ['earn', 10, 300, 31], ['redeem', 20, 400]]}, {'results': [366, 41, [[41, 300], [366, 100]]], 'lots': [[366, 1, 400]]}], ['use on expiry day', {'events': [['earn', 0, 200, 30], ['redeem', 30, 151]]}, {'results': [30, [[30, 151]]], 'lots': [[30, 0, 49]]}], ['expired unusable', {'events': [['earn', 0, 200, 30], ['earn', 5, 50, 90], ['redeem', 31, 101]]}, {'results': [30, 95, 'insufficient'], 'lots': [[30, 0, 200], [95, 5, 50]]}], ['sweep reports points', {'events': [['earn', 0, 120, 10], ['earn', 0, 81, 10], ['earn', 3, 60, 100], ['sweep', 11]]}, {'results': [10, 10, 103, 201], 'lots': [[103, 3, 60]]}], ['sweep boundary', {'events': [['earn', 0, 70, 11], ['sweep', 11], ['sweep', 12]]}, {'results': [11, 0, 70], 'lots': []}], ['partial lot cleanup', {'events': [['earn', 0, 100, 50], ['earn', 1, 100, 60], ['redeem', 2, 100], ['redeem', 3, 31]]}, {'results': [50, 61, [[50, 100]], [[61, 31]]], 'lots': [[61, 1, 69]]}], ['insufficient', {'events': [['earn', 0, 100, 50], ['redeem', 1, 151], ['redeem', 2, 100]]}, {'results': [50, 'insufficient', [[50, 100]]], 'lots': []}], ['ties by earn day', {'events': [['earn', 5, 40, 20], ['earn', 1, 40, 24], ['redeem', 6, 51]]}, {'results': [25, 25, [[25, 40], [25, 11]]], 'lots': [[25, 5, 29]]}]], [['soonest expiry first', {'events': [['earn', 1, 500, 365], ['earn', 10, 300, 32], ['redeem', 20, 400]]}, {'results': [366, 42, [[42, 300], [366, 100]]], 'lots': [[366, 1, 400]]}], ['use on expiry day', {'events': [['earn', 0, 200, 30], ['redeem', 30, 152]]}, {'results': [30, [[30, 152]]], 'lots': [[30, 0, 48]]}], ['expired unusable', {'events': [['earn', 0, 200, 30], ['earn', 5, 50, 90], ['redeem', 31, 102]]}, {'results': [30, 95, 'insufficient'], 'lots': [[30, 0, 200], [95, 5, 50]]}], ['sweep reports points', {'events': [['earn', 0, 120, 10], ['earn', 0, 82, 10], ['earn', 3, 60, 100], ['sweep', 11]]}, {'results': [10, 10, 103, 202], 'lots': [[103, 3, 60]]}], ['sweep boundary', {'events': [['earn', 0, 70, 12], ['sweep', 12], ['sweep', 13]]}, {'results': [12, 0, 70], 'lots': []}], ['partial lot cleanup', {'events': [['earn', 0, 100, 50], ['earn', 1, 100, 60], ['redeem', 2, 100], ['redeem', 3, 32]]}, {'results': [50, 61, [[50, 100]], [[61, 32]]], 'lots': [[61, 1, 68]]}], ['insufficient', {'events': [['earn', 0, 100, 50], ['redeem', 1, 152], ['redeem', 2, 100]]}, {'results': [50, 'insufficient', [[50, 100]]], 'lots': []}], ['ties by earn day', {'events': [['earn', 5, 40, 20], ['earn', 1, 40, 24], ['redeem', 6, 52]]}, {'results': [25, 25, [[25, 40], [25, 12]]], 'lots': [[25, 5, 28]]}]], [['soonest expiry first', {'events': [['earn', 1, 500, 365], ['earn', 10, 300, 33], ['redeem', 20, 400]]}, {'results': [366, 43, [[43, 300], [366, 100]]], 'lots': [[366, 1, 400]]}], ['use on expiry day', {'events': [['earn', 0, 200, 30], ['redeem', 30, 153]]}, {'results': [30, [[30, 153]]], 'lots': [[30, 0, 47]]}], ['expired unusable', {'events': [['earn', 0, 200, 30], ['earn', 5, 50, 90], ['redeem', 31, 103]]}, {'results': [30, 95, 'insufficient'], 'lots': [[30, 0, 200], [95, 5, 50]]}], ['sweep reports points', {'events': [['earn', 0, 120, 10], ['earn', 0, 83, 10], ['earn', 3, 60, 100], ['sweep', 11]]}, {'results': [10, 10, 103, 203], 'lots': [[103, 3, 60]]}], ['sweep boundary', {'events': [['earn', 0, 70, 13], ['sweep', 13], ['sweep', 14]]}, {'results': [13, 0, 70], 'lots': []}], ['partial lot cleanup', {'events': [['earn', 0, 100, 50], ['earn', 1, 100, 60], ['redeem', 2, 100], ['redeem', 3, 33]]}, {'results': [50, 61, [[50, 100]], [[61, 33]]], 'lots': [[61, 1, 67]]}], ['insufficient', {'events': [['earn', 0, 100, 50], ['redeem', 1, 153], ['redeem', 2, 100]]}, {'results': [50, 'insufficient', [[50, 100]]], 'lots': []}], ['ties by earn day', {'events': [['earn', 5, 40, 20], ['earn', 1, 40, 24], ['redeem', 6, 53]]}, {'results': [25, 25, [[25, 40], [25, 13]]], 'lots': [[25, 5, 27]]}]], [['soonest expiry first', {'events': [['earn', 1, 500, 365], ['earn', 10, 300, 34], ['redeem', 20, 400]]}, {'results': [366, 44, [[44, 300], [366, 100]]], 'lots': [[366, 1, 400]]}], ['use on expiry day', {'events': [['earn', 0, 200, 30], ['redeem', 30, 154]]}, {'results': [30, [[30, 154]]], 'lots': [[30, 0, 46]]}], ['expired unusable', {'events': [['earn', 0, 200, 30], ['earn', 5, 50, 90], ['redeem', 31, 104]]}, {'results': [30, 95, 'insufficient'], 'lots': [[30, 0, 200], [95, 5, 50]]}], ['sweep reports points', {'events': [['earn', 0, 120, 10], ['earn', 0, 84, 10], ['earn', 3, 60, 100], ['sweep', 11]]}, {'results': [10, 10, 103, 204], 'lots': [[103, 3, 60]]}], ['sweep boundary', {'events': [['earn', 0, 70, 14], ['sweep', 14], ['sweep', 15]]}, {'results': [14, 0, 70], 'lots': []}], ['partial lot cleanup', {'events': [['earn', 0, 100, 50], ['earn', 1, 100, 60], ['redeem', 2, 100], ['redeem', 3, 34]]}, {'results': [50, 61, [[50, 100]], [[61, 34]]], 'lots': [[61, 1, 66]]}], ['insufficient', {'events': [['earn', 0, 100, 50], ['redeem', 1, 154], ['redeem', 2, 100]]}, {'results': [50, 'insufficient', [[50, 100]]], 'lots': []}], ['ties by earn day', {'events': [['earn', 5, 40, 20], ['earn', 1, 40, 24], ['redeem', 6, 54]]}, {'results': [25, 25, [[25, 40], [25, 14]]], 'lots': [[25, 5, 26]]}]], [['soonest expiry first', {'events': [['earn', 1, 500, 365], ['earn', 10, 300, 35], ['redeem', 20, 400]]}, {'results': [366, 45, [[45, 300], [366, 100]]], 'lots': [[366, 1, 400]]}], ['use on expiry day', {'events': [['earn', 0, 200, 30], ['redeem', 30, 155]]}, {'results': [30, [[30, 155]]], 'lots': [[30, 0, 45]]}], ['expired unusable', {'events': [['earn', 0, 200, 30], ['earn', 5, 50, 90], ['redeem', 31, 105]]}, {'results': [30, 95, 'insufficient'], 'lots': [[30, 0, 200], [95, 5, 50]]}], ['sweep reports points', {'events': [['earn', 0, 120, 10], ['earn', 0, 85, 10], ['earn', 3, 60, 100], ['sweep', 11]]}, {'results': [10, 10, 103, 205], 'lots': [[103, 3, 60]]}], ['sweep boundary', {'events': [['earn', 0, 70, 15], ['sweep', 15], ['sweep', 16]]}, {'results': [15, 0, 70], 'lots': []}], ['partial lot cleanup', {'events': [['earn', 0, 100, 50], ['earn', 1, 100, 60], ['redeem', 2, 100], ['redeem', 3, 35]]}, {'results': [50, 61, [[50, 100]], [[61, 35]]], 'lots': [[61, 1, 65]]}], ['insufficient', {'events': [['earn', 0, 100, 50], ['redeem', 1, 155], ['redeem', 2, 100]]}, {'results': [50, 'insufficient', [[50, 100]]], 'lots': []}], ['ties by earn day', {'events': [['earn', 5, 40, 20], ['earn', 1, 40, 24], ['redeem', 6, 55]]}, {'results': [25, 25, [[25, 40], [25, 15]]], 'lots': [[25, 5, 25]]}]]]\nfor label, args, expected in fixtures[N-1]:\n    check(label, solve(args), expected)\nprint(json.dumps({\"observations\": observations, \"passed\": all(x[\"passed\"] for x in observations)}, ensure_ascii=False))\nraise SystemExit(0 if all(x[\"passed\"] for x in observations) else 1)\n"}},"limitations":"Stipulated bounded loyalty-program model with explicit toy rules; not any real program terms. This reproducer isolates one failure mechanism. Results cover the supplied fixtures. Variants within a family share a test contract and should remain grouped when constructing evaluation splits. Related mechanisms with a shared evaluation_group must also remain together; these controlled models are not independent production incidents.","method":"Deterministic executable model with adversarial boundary fixtures.","provenance":{"created_by":"Failure Map","dependencies":"Python standard library","family":"w2-loyalty-points-ledgers-expiring-lots-availability-pool","generated_at":"2026-09-29T14:46:45.256093+00:00","license":"CC0-1.0","python":"3.12.14","seed":1,"split":"open-access"},"relevance":"Member point balances, statements and the program liability depend on this ledger rule.","repair":"Check sufficiency against live lots only.","root_cause":"The sufficiency check sums every lot, including expired ones.","sha256":"afd90667f28dedc09d8e31c03f9b6defae81fa0e32fbb76e066790aa77a7ddcb","title":"Expiring point lots: availability pool · case 01","variant":1,"variant_policy":"Five numbered records share a model and may reuse boundary fixtures.","verification":{"attempt":{"elapsed_ms":41.203,"exit_code":1,"observations":[{"actual":{"lots":[[366,1,400]],"results":[366,41,[[41,300],[366,100]]]},"check":"soonest expiry first","expected":{"lots":[[366,1,400]],"results":[366,41,[[41,300],[366,100]]]},"passed":true},{"actual":{"lots":[[30,0,200]],"results":[30,"insufficient"]},"check":"use on expiry day","expected":{"lots":[[30,0,49]],"results":[30,[[30,151]]]},"passed":false},{"actual":{"lots":[[30,0,200],[95,5,50]],"results":[30,95,"insufficient"]},"check":"expired unusable","expected":{"lots":[[30,0,200],[95,5,50]],"results":[30,95,"insufficient"]},"passed":true},{"actual":{"lots":[[103,3,60]],"results":[10,10,103,201]},"check":"sweep reports points","expected":{"lots":[[103,3,60]],"results":[10,10,103,201]},"passed":true},{"actual":{"lots":[],"results":[11,0,70]},"check":"sweep boundary","expected":{"lots":[],"results":[11,0,70]},"passed":true},{"actual":{"lots":[[61,1,69]],"results":[50,61,[[50,100]],[[61,31]]]},"check":"partial lot cleanup","expected":{"lots":[[61,1,69]],"results":[50,61,[[50,100]],[[61,31]]]},"passed":true},{"actual":{"lots":[],"results":[50,"insufficient",[[50,100]]]},"check":"insufficient","expected":{"lots":[],"results":[50,"insufficient",[[50,100]]]},"passed":true},{"actual":{"lots":[[25,5,29]],"results":[25,25,[[25,40],[25,11]]]},"check":"ties by earn day","expected":{"lots":[[25,5,29]],"results":[25,25,[[25,40],[25,11]]]},"passed":true}],"passed":false,"stderr":"","stdout":"{\"observations\": [{\"check\": \"soonest expiry first\", \"actual\": {\"results\": [366, 41, [[41, 300], [366, 100]]], \"lots\": [[366, 1, 400]]}, \"expected\": {\"results\": [366, 41, [[41, 300], [366, 100]]], \"lots\": [[366, 1, 400]]}, \"passed\": true}, {\"check\": \"use on expiry day\", \"actual\": {\"results\": [30, \"insufficient\"], \"lots\": [[30, 0, 200]]}, \"expected\": {\"results\": [30, [[30, 151]]], \"lots\": [[30, 0, 49]]}, \"passed\": false}, {\"check\": \"expired unusable\", \"actual\": {\"results\": [30, 95, \"insufficient\"], \"lots\": [[30, 0, 200], [95, 5, 50]]}, \"expected\": {\"results\": [30, 95, \"insufficient\"], \"lots\": [[30, 0, 200], [95, 5, 50]]}, \"passed\": true}, {\"check\": \"sweep reports points\", \"actual\": {\"results\": [10, 10, 103, 201], \"lots\": [[103, 3, 60]]}, \"expected\": {\"results\": [10, 10, 103, 201], \"lots\": [[103, 3, 60]]}, \"passed\": true}, {\"check\": \"sweep boundary\", \"actual\": {\"results\": [11, 0, 70], \"lots\": []}, \"expected\": {\"results\": [11, 0, 70], \"lots\": []}, \"passed\": true}, {\"check\": \"partial lot cleanup\", \"actual\": {\"results\": [50, 61, [[50, 100]], [[61, 31]]], \"lots\": [[61, 1, 69]]}, \"expected\": {\"results\": [50, 61, [[50, 100]], [[61, 31]]], \"lots\": [[61, 1, 69]]}, \"passed\": true}, {\"check\": \"insufficient\", \"actual\": {\"results\": [50, \"insufficient\", [[50, 100]]], \"lots\": []}, \"expected\": {\"results\": [50, \"insufficient\", [[50, 100]]], \"lots\": []}, \"passed\": true}, {\"check\": \"ties by earn day\", \"actual\": {\"results\": [25, 25, [[25, 40], [25, 11]]], \"lots\": [[25, 5, 29]]}, \"expected\": {\"results\": [25, 25, [[25, 40], [25, 11]]], \"lots\": [[25, 5, 29]]}, \"passed\": true}], \"passed\": false}\n"},"broken":{"elapsed_ms":41.704,"exit_code":1,"observations":[{"actual":{"lots":[[366,1,400]],"results":[366,41,[[41,300],[366,100]]]},"check":"soonest expiry first","expected":{"lots":[[366,1,400]],"results":[366,41,[[41,300],[366,100]]]},"passed":true},{"actual":{"lots":[[30,0,49]],"results":[30,[[30,151]]]},"check":"use on expiry day","expected":{"lots":[[30,0,49]],"results":[30,[[30,151]]]},"passed":true},{"actual":{"lots":[[30,0,200]],"results":[30,95,[[95,50]]]},"check":"expired unusable","expected":{"lots":[[30,0,200],[95,5,50]],"results":[30,95,"insufficient"]},"passed":false},{"actual":{"lots":[[103,3,60]],"results":[10,10,103,201]},"check":"sweep reports points","expected":{"lots":[[103,3,60]],"results":[10,10,103,201]},"passed":true},{"actual":{"lots":[],"results":[11,0,70]},"check":"sweep boundary","expected":{"lots":[],"results":[11,0,70]},"passed":true},{"actual":{"lots":[[61,1,69]],"results":[50,61,[[50,100]],[[61,31]]]},"check":"partial lot cleanup","expected":{"lots":[[61,1,69]],"results":[50,61,[[50,100]],[[61,31]]]},"passed":true},{"actual":{"lots":[],"results":[50,"insufficient",[[50,100]]]},"check":"insufficient","expected":{"lots":[],"results":[50,"insufficient",[[50,100]]]},"passed":true},{"actual":{"lots":[[25,5,29]],"results":[25,25,[[25,40],[25,11]]]},"check":"ties by earn day","expected":{"lots":[[25,5,29]],"results":[25,25,[[25,40],[25,11]]]},"passed":true}],"passed":false,"stderr":"","stdout":"{\"observations\": [{\"check\": \"soonest expiry first\", \"actual\": {\"results\": [366, 41, [[41, 300], [366, 100]]], \"lots\": [[366, 1, 400]]}, \"expected\": {\"results\": [366, 41, [[41, 300], [366, 100]]], \"lots\": [[366, 1, 400]]}, \"passed\": true}, {\"check\": \"use on expiry day\", \"actual\": {\"results\": [30, [[30, 151]]], \"lots\": [[30, 0, 49]]}, \"expected\": {\"results\": [30, [[30, 151]]], \"lots\": [[30, 0, 49]]}, \"passed\": true}, {\"check\": \"expired unusable\", \"actual\": {\"results\": [30, 95, [[95, 50]]], \"lots\": [[30, 0, 200]]}, \"expected\": {\"results\": [30, 95, \"insufficient\"], \"lots\": [[30, 0, 200], [95, 5, 50]]}, \"passed\": false}, {\"check\": \"sweep reports points\", \"actual\": {\"results\": [10, 10, 103, 201], \"lots\": [[103, 3, 60]]}, \"expected\": {\"results\": [10, 10, 103, 201], \"lots\": [[103, 3, 60]]}, \"passed\": true}, {\"check\": \"sweep boundary\", \"actual\": {\"results\": [11, 0, 70], \"lots\": []}, \"expected\": {\"results\": [11, 0, 70], \"lots\": []}, \"passed\": true}, {\"check\": \"partial lot cleanup\", \"actual\": {\"results\": [50, 61, [[50, 100]], [[61, 31]]], \"lots\": [[61, 1, 69]]}, \"expected\": {\"results\": [50, 61, [[50, 100]], [[61, 31]]], \"lots\": [[61, 1, 69]]}, \"passed\": true}, {\"check\": \"insufficient\", \"actual\": {\"results\": [50, \"insufficient\", [[50, 100]]], \"lots\": []}, \"expected\": {\"results\": [50, \"insufficient\", [[50, 100]]], \"lots\": []}, \"passed\": true}, {\"check\": \"ties by earn day\", \"actual\": {\"results\": [25, 25, [[25, 40], [25, 11]]], \"lots\": [[25, 5, 29]]}, \"expected\": {\"results\": [25, 25, [[25, 40], [25, 11]]], \"lots\": [[25, 5, 29]]}, \"passed\": true}], \"passed\": false}\n"},"fixed":{"elapsed_ms":40.8,"exit_code":0,"observations":[{"actual":{"lots":[[366,1,400]],"results":[366,41,[[41,300],[366,100]]]},"check":"soonest expiry first","expected":{"lots":[[366,1,400]],"results":[366,41,[[41,300],[366,100]]]},"passed":true},{"actual":{"lots":[[30,0,49]],"results":[30,[[30,151]]]},"check":"use on expiry day","expected":{"lots":[[30,0,49]],"results":[30,[[30,151]]]},"passed":true},{"actual":{"lots":[[30,0,200],[95,5,50]],"results":[30,95,"insufficient"]},"check":"expired unusable","expected":{"lots":[[30,0,200],[95,5,50]],"results":[30,95,"insufficient"]},"passed":true},{"actual":{"lots":[[103,3,60]],"results":[10,10,103,201]},"check":"sweep reports points","expected":{"lots":[[103,3,60]],"results":[10,10,103,201]},"passed":true},{"actual":{"lots":[],"results":[11,0,70]},"check":"sweep boundary","expected":{"lots":[],"results":[11,0,70]},"passed":true},{"actual":{"lots":[[61,1,69]],"results":[50,61,[[50,100]],[[61,31]]]},"check":"partial lot cleanup","expected":{"lots":[[61,1,69]],"results":[50,61,[[50,100]],[[61,31]]]},"passed":true},{"actual":{"lots":[],"results":[50,"insufficient",[[50,100]]]},"check":"insufficient","expected":{"lots":[],"results":[50,"insufficient",[[50,100]]]},"passed":true},{"actual":{"lots":[[25,5,29]],"results":[25,25,[[25,40],[25,11]]]},"check":"ties by earn day","expected":{"lots":[[25,5,29]],"results":[25,25,[[25,40],[25,11]]]},"passed":true}],"passed":true,"stderr":"","stdout":"{\"observations\": [{\"check\": \"soonest expiry first\", \"actual\": {\"results\": [366, 41, [[41, 300], [366, 100]]], \"lots\": [[366, 1, 400]]}, \"expected\": {\"results\": [366, 41, [[41, 300], [366, 100]]], \"lots\": [[366, 1, 400]]}, \"passed\": true}, {\"check\": \"use on expiry day\", \"actual\": {\"results\": [30, [[30, 151]]], \"lots\": [[30, 0, 49]]}, \"expected\": {\"results\": [30, [[30, 151]]], \"lots\": [[30, 0, 49]]}, \"passed\": true}, {\"check\": \"expired unusable\", \"actual\": {\"results\": [30, 95, \"insufficient\"], \"lots\": [[30, 0, 200], [95, 5, 50]]}, \"expected\": {\"results\": [30, 95, \"insufficient\"], \"lots\": [[30, 0, 200], [95, 5, 50]]}, \"passed\": true}, {\"check\": \"sweep reports points\", \"actual\": {\"results\": [10, 10, 103, 201], \"lots\": [[103, 3, 60]]}, \"expected\": {\"results\": [10, 10, 103, 201], \"lots\": [[103, 3, 60]]}, \"passed\": true}, {\"check\": \"sweep boundary\", \"actual\": {\"results\": [11, 0, 70], \"lots\": []}, \"expected\": {\"results\": [11, 0, 70], \"lots\": []}, \"passed\": true}, {\"check\": \"partial lot cleanup\", \"actual\": {\"results\": [50, 61, [[50, 100]], [[61, 31]]], \"lots\": [[61, 1, 69]]}, \"expected\": {\"results\": [50, 61, [[50, 100]], [[61, 31]]], \"lots\": [[61, 1, 69]]}, \"passed\": true}, {\"check\": \"insufficient\", \"actual\": {\"results\": [50, \"insufficient\", [[50, 100]]], \"lots\": []}, \"expected\": {\"results\": [50, \"insufficient\", [[50, 100]]], \"lots\": []}, \"passed\": true}, {\"check\": \"ties by earn day\", \"actual\": {\"results\": [25, 25, [[25, 40], [25, 11]]], \"lots\": [[25, 5, 29]]}, \"expected\": {\"results\": [25, 25, [[25, 40], [25, 11]]], \"lots\": [[25, 5, 29]]}, \"passed\": true}], \"passed\": true}\n"}},"verified":true,"visibility":"public"}