FA-63351 / Actuarial life tables / Open access
Life table survivors from mortality rates: Tables with a later non-terminal rate are accepted as closed · case 01
An open table produces survivors past the last age.
ROOT CAUSE
The closure test on the final rate is missing.
VERIFIED REPAIR
Require the last rate to be exactly one.
Unsuccessful approach: Checking for any rate of one accepts tables that continue after it.
Case contract
Input q (per 100000, last must be 100000 else 'open table'; any outside [0,100000] -> 'invalid q') and radix (<=0 -> 'invalid radix'). l_0 = radix; l_{k+1} = l_k*(1-q_k) rounded half-up at every age; d_k = l_k - l_{k+1}. Return [l, d].
Why this case matters
Life-table arithmetic compounds across ages, so one misplaced index or assumption shifts every downstream value.
1 / The failure
Exit 1"""Failure Map reference implementation. Python standard library only."""
import json
import math
from fractions import Fraction
N = 1
observations = []
def solve(x):
qs=[Fraction(q,100000) for q in x['q']]
if x['radix']<=0: return 'invalid radix'
if any(q<0 or q>1 for q in qs): return 'invalid q'
if not qs: return 'open table'
l=[x['radix']]
for q in qs:
l.append(math.floor(l[-1]*(1-q)+Fraction(1,2)))
d=[a-b for a,b in zip(l,l[1:])]
return [l,d]
def check(label, actual, expected):
observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
fixtures = [[['closed table 0', {'q': [1033, 1165, 1336, 1514, 1709, 100000], 'radix': 69457}, [[69457, 68740, 67939, 67031, 66016, 64888, 0], [717, 801, 908, 1015, 1128, 64888]]], ['closed table 1', {'q': [593, 714, 824, 930, 1064, 1208, 1363, 100000], 'radix': 81539}, [[81539, 81055, 80476, 79813, 79071, 78230, 77285, 76232, 0], [484, 579, 663, 742, 841, 945, 1053, 76232]]], ['closed table 2', {'q': [515, 615, 705, 814, 949, 1107, 1282, 100000], 'radix': 23619}, [[23619, 23497, 23352, 23187, 22998, 22780, 22528, 22239, 0], [122, 145, 165, 189, 218, 252, 289, 22239]]], ['zero mortality ages', {'q': [0, 0, 796, 100000], 'radix': 100000}, [[100000, 100000, 100000, 99204, 0], [0, 0, 796, 99204]]], ['open table rejected', {'q': [555, 636, 741, 860, 978], 'radix': 10000}, 'open table'], ['early certain death then open', {'q': [223, 100000, 50000], 'radix': 10000}, 'open table'], ['negative rate rejected', {'q': [100, -5, 100000], 'radix': 100000}, 'invalid q'], ['zero radix rejected', {'q': [100, 100000], 'radix': 0}, 'invalid radix'], ['negative radix rejected', {'q': [100, 100000], 'radix': -1000}, 'invalid radix'], ['half-death rounding tie', {'q': [50000, 100000], 'radix': 3}, [[3, 2, 0], [1, 2]]], ['odd radix deaths reconcile', {'q': [15657, 22915, 37291, 19541, 14273, 100000], 'radix': 361}, [[361, 304, 234, 147, 118, 101, 0], [57, 70, 87, 29, 17, 101]]], ['small radix rounding', {'q': [33333, 33333, 50000, 100000], 'radix': 8}, [[8, 5, 3, 2, 0], [3, 2, 1, 2]]]], [['closed table 0', {'q': [1224, 1411, 1580, 1785, 2044, 2304, 2625, 2976, 3345, 100000], 'radix': 73425}, [[73425, 72526, 71503, 70373, 69117, 67704, 66144, 64408, 62491, 60401, 0], [899, 1023, 1130, 1256, 1413, 1560, 1736, 1917, 2090, 60401]]], ['closed table 1', {'q': [1483, 1675, 1889, 2148, 2445, 2784, 3127, 100000], 'radix': 95565}, [[95565, 94148, 92571, 90822, 88871, 86698, 84284, 81648, 0], [1417, 1577, 1749, 1951, 2173, 2414, 2636, 81648]]], ['closed table 2', {'q': [947, 1104, 1253, 1408, 1590, 1811, 2059, 2344, 2661, 100000], 'radix': 84753}, [[84753, 83950, 83023, 81983, 80829, 79544, 78103, 76495, 74702, 72714, 0], [803, 927, 1040, 1154, 1285, 1441, 1608, 1793, 1988, 72714]]], ['zero mortality ages', {'q': [0, 0, 577, 100000], 'radix': 100000}, [[100000, 100000, 100000, 99423, 0], [0, 0, 577, 99423]]], ['open table rejected', {'q': [401, 494, 603, 675, 758], 'radix': 10000}, 'open table'], ['early certain death then open', {'q': [861, 100000, 50000], 'radix': 1000}, 'open table'], ['negative rate rejected', {'q': [100, -5, 100000], 'radix': 100000}, 'invalid q'], ['zero radix rejected', {'q': [100, 100000], 'radix': 0}, 'invalid radix'], ['negative radix rejected', {'q': [100, 100000], 'radix': -1000}, 'invalid radix'], ['half-death rounding tie', {'q': [50000, 100000], 'radix': 5}, [[5, 3, 0], [2, 3]]], ['odd radix deaths reconcile', {'q': [34607, 36625, 31203, 24229, 24851, 100000], 'radix': 951}, [[951, 622, 394, 271, 205, 154, 0], [329, 228, 123, 66, 51, 154]]], ['small radix rounding', {'q': [33333, 33333, 50000, 100000], 'radix': 9}, [[9, 6, 4, 2, 0], [3, 2, 2, 2]]]], [['closed table 0', {'q': [843, 986, 1140, 1296, 1486, 1699, 1937, 2195, 100000], 'radix': 30585}, [[30585, 30327, 30028, 29686, 29301, 28866, 28376, 27826, 27215, 0], [258, 299, 342, 385, 435, 490, 550, 611, 27215]]], ['closed table 1', {'q': [1278, 1465, 1677, 1884, 2120, 100000], 'radix': 34567}, [[34567, 34125, 33625, 33061, 32438, 31750, 0], [442, 500, 564, 623, 688, 31750]]], ['closed table 2', {'q': [699, 786, 882, 1036, 1165, 1332, 1497, 1680, 100000], 'radix': 78197}, [[78197, 77650, 77040, 76361, 75570, 74690, 73695, 72592, 71372, 0], [547, 610, 679, 791, 880, 995, 1103, 1220, 71372]]], ['zero mortality ages', {'q': [0, 0, 651, 100000], 'radix': 10000}, [[10000, 10000, 10000, 9935, 0], [0, 0, 65, 9935]]], ['open table rejected', {'q': [293, 338, 418, 503, 565], 'radix': 1000}, 'open table'], ['early certain death then open', {'q': [246, 100000, 50000], 'radix': 10000}, 'open table'], ['negative rate rejected', {'q': [100, -5, 100000], 'radix': 1000}, 'invalid q'], ['zero radix rejected', {'q': [100, 100000], 'radix': 0}, 'invalid radix'], ['negative radix rejected', {'q': [100, 100000], 'radix': -1000}, 'invalid radix'], ['half-death rounding tie', {'q': [50000, 100000], 'radix': 7}, [[7, 4, 0], [3, 4]]], ['odd radix deaths reconcile', {'q': [24577, 26111, 12881, 35469, 14789, 100000], 'radix': 563}, [[563, 425, 314, 274, 177, 151, 0], [138, 111, 40, 97, 26, 151]]], ['small radix rounding', {'q': [33333, 33333, 50000, 100000], 'radix': 10}, [[10, 7, 5, 3, 0], [3, 2, 2, 3]]]], [['closed table 0', {'q': [1676, 1888, 2164, 2470, 2790, 3140, 3538, 3970, 100000], 'radix': 15185}, [[15185, 14930, 14648, 14331, 13977, 13587, 13160, 12694, 12190, 0], [255, 282, 317, 354, 390, 427, 466, 504, 12190]]], ['closed table 1', {'q': [2017, 2262, 2573, 2928, 3329, 3764, 4215, 4722, 5308, 100000], 'radix': 18031}, [[18031, 17667, 17267, 16823, 16330, 15786, 15192, 14552, 13865, 13129, 0], [364, 400, 444, 493, 544, 594, 640, 687, 736, 13129]]], ['closed table 2', {'q': [1538, 1732, 1939, 2177, 2450, 2788, 3129, 3535, 100000], 'radix': 93691}, [[93691, 92250, 90652, 88894, 86959, 84829, 82464, 79884, 77060, 0], [1441, 1598, 1758, 1935, 2130, 2365, 2580, 2824, 77060]]], ['zero mortality ages', {'q': [0, 0, 490, 100000], 'radix': 1000}, [[1000, 1000, 1000, 995, 0], [0, 0, 5, 995]]], ['open table rejected', {'q': [226, 259, 292, 356, 432], 'radix': 100000}, 'open table'], ['early certain death then open', {'q': [598, 100000, 50000], 'radix': 1000}, 'open table'], ['negative rate rejected', {'q': [100, -5, 100000], 'radix': 10000}, 'invalid q'], ['zero radix rejected', {'q': [100, 100000], 'radix': 0}, 'invalid radix'], ['negative radix rejected', {'q': [100, 100000], 'radix': -1000}, 'invalid radix'], ['half-death rounding tie', {'q': [50000, 100000], 'radix': 9}, [[9, 5, 0], [4, 5]]], ['odd radix deaths reconcile', {'q': [33513, 18981, 38831, 14099, 33629, 100000], 'radix': 617}, [[617, 410, 332, 203, 174, 115, 0], [207, 78, 129, 29, 59, 115]]], ['small radix rounding', {'q': [33333, 33333, 50000, 100000], 'radix': 11}, [[11, 7, 5, 3, 0], [4, 2, 2, 3]]]], [['closed table 0', {'q': [992, 1146, 1306, 1489, 1674, 100000], 'radix': 62363}, [[62363, 61744, 61036, 60239, 59342, 58349, 0], [619, 708, 797, 897, 993, 58349]]], ['closed table 1', {'q': [2141, 2429, 2766, 3097, 3484, 3938, 4441, 4974, 5590, 100000], 'radix': 50071}, [[50071, 48999, 47809, 46487, 45047, 43478, 41766, 39911, 37926, 35806, 0], [1072, 1190, 1322, 1440, 1569, 1712, 1855, 1985, 2120, 35806]]], ['closed table 2', {'q': [1626, 1851, 2115, 2406, 2724, 3092, 3463, 3919, 100000], 'radix': 12425}, [[12425, 12223, 11997, 11743, 11460, 11148, 10803, 10429, 10020, 0], [202, 226, 254, 283, 312, 345, 374, 409, 10020]]], ['zero mortality ages', {'q': [0, 0, 245, 100000], 'radix': 1000}, [[1000, 1000, 1000, 998, 0], [0, 0, 2, 998]]], ['open table rejected', {'q': [345, 413, 466, 555, 658], 'radix': 1000}, 'open table'], ['early certain death then open', {'q': [601, 100000, 50000], 'radix': 100000}, 'open table'], ['negative rate rejected', {'q': [100, -5, 100000], 'radix': 1000}, 'invalid q'], ['zero radix rejected', {'q': [100, 100000], 'radix': 0}, 'invalid radix'], ['negative radix rejected', {'q': [100, 100000], 'radix': -1000}, 'invalid radix'], ['half-death rounding tie', {'q': [50000, 100000], 'radix': 11}, [[11, 6, 0], [5, 6]]], ['odd radix deaths reconcile', {'q': [23365, 16837, 21555, 13823, 21221, 100000], 'radix': 421}, [[421, 323, 269, 211, 182, 143, 0], [98, 54, 58, 29, 39, 143]]], ['small radix rounding', {'q': [33333, 33333, 50000, 100000], 'radix': 12}, [[12, 8, 5, 3, 0], [4, 3, 2, 3]]]]]
for label, args, expected in fixtures[N-1]:
check(label, solve(args), expected)
print(json.dumps({"observations": observations, "passed": all(x["passed"] for x in observations)}, ensure_ascii=False))
raise SystemExit(0 if all(x["passed"] for x in observations) else 1)
| Boundary fixture | Actual | Expected | Outcome |
|---|---|---|---|
| closed table 0 | [[69457, 68740, 67939, 67031, 66016, 64888, 0], [717, 801, 908, 1015, 1128, 64888]] | [[69457, 68740, 67939, 67031, 66016, 64888, 0], [717, 801, 908, 1015, 1128, 64888]] | Passed |
| closed table 1 | [[81539, 81055, 80476, 79813, 79071, 78230, 77285, 76232, 0], [484, 579, 663, 742, 841, 945, 1053, 76232]] | [[81539, 81055, 80476, 79813, 79071, 78230, 77285, 76232, 0], [484, 579, 663, 742, 841, 945, 1053, 76232]] | Passed |
| closed table 2 | [[23619, 23497, 23352, 23187, 22998, 22780, 22528, 22239, 0], [122, 145, 165, 189, 218, 252, 289, 22239]] | [[23619, 23497, 23352, 23187, 22998, 22780, 22528, 22239, 0], [122, 145, 165, 189, 218, 252, 289, 22239]] | Passed |
| zero mortality ages | [[100000, 100000, 100000, 99204, 0], [0, 0, 796, 99204]] | [[100000, 100000, 100000, 99204, 0], [0, 0, 796, 99204]] | Passed |
| open table rejected | [[10000, 9945, 9882, 9809, 9725, 9630], [55, 63, 73, 84, 95]] | open table | Failed |
| early certain death then open | [[10000, 9978, 0, 0], [22, 9978, 0]] | open table | Failed |
| negative rate rejected | invalid q | invalid q | Passed |
| zero radix rejected | invalid radix | invalid radix | Passed |
| negative radix rejected | invalid radix | invalid radix | Passed |
| half-death rounding tie | [[3, 2, 0], [1, 2]] | [[3, 2, 0], [1, 2]] | Passed |
| odd radix deaths reconcile | [[361, 304, 234, 147, 118, 101, 0], [57, 70, 87, 29, 17, 101]] | [[361, 304, 234, 147, 118, 101, 0], [57, 70, 87, 29, 17, 101]] | Passed |
| small radix rounding | [[8, 5, 3, 2, 0], [3, 2, 1, 2]] | [[8, 5, 3, 2, 0], [3, 2, 1, 2]] | Passed |
SHA-256 / 5332df0f3778e5363fec1cdeeb91cd94d6cd31c78311fbbd5dabc904ee0e9d30
2 / The unsuccessful fix
Exit 1"""Failure Map reference implementation. Python standard library only."""
import json
import math
from fractions import Fraction
N = 1
observations = []
def solve(x):
qs=[Fraction(q,100000) for q in x['q']]
if x['radix']<=0: return 'invalid radix'
if any(q<0 or q>1 for q in qs): return 'invalid q'
if not qs or max(qs)!=1: return 'open table'
l=[x['radix']]
for q in qs:
l.append(math.floor(l[-1]*(1-q)+Fraction(1,2)))
d=[a-b for a,b in zip(l,l[1:])]
return [l,d]
def check(label, actual, expected):
observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
fixtures = [[['closed table 0', {'q': [1033, 1165, 1336, 1514, 1709, 100000], 'radix': 69457}, [[69457, 68740, 67939, 67031, 66016, 64888, 0], [717, 801, 908, 1015, 1128, 64888]]], ['closed table 1', {'q': [593, 714, 824, 930, 1064, 1208, 1363, 100000], 'radix': 81539}, [[81539, 81055, 80476, 79813, 79071, 78230, 77285, 76232, 0], [484, 579, 663, 742, 841, 945, 1053, 76232]]], ['closed table 2', {'q': [515, 615, 705, 814, 949, 1107, 1282, 100000], 'radix': 23619}, [[23619, 23497, 23352, 23187, 22998, 22780, 22528, 22239, 0], [122, 145, 165, 189, 218, 252, 289, 22239]]], ['zero mortality ages', {'q': [0, 0, 796, 100000], 'radix': 100000}, [[100000, 100000, 100000, 99204, 0], [0, 0, 796, 99204]]], ['open table rejected', {'q': [555, 636, 741, 860, 978], 'radix': 10000}, 'open table'], ['early certain death then open', {'q': [223, 100000, 50000], 'radix': 10000}, 'open table'], ['negative rate rejected', {'q': [100, -5, 100000], 'radix': 100000}, 'invalid q'], ['zero radix rejected', {'q': [100, 100000], 'radix': 0}, 'invalid radix'], ['negative radix rejected', {'q': [100, 100000], 'radix': -1000}, 'invalid radix'], ['half-death rounding tie', {'q': [50000, 100000], 'radix': 3}, [[3, 2, 0], [1, 2]]], ['odd radix deaths reconcile', {'q': [15657, 22915, 37291, 19541, 14273, 100000], 'radix': 361}, [[361, 304, 234, 147, 118, 101, 0], [57, 70, 87, 29, 17, 101]]], ['small radix rounding', {'q': [33333, 33333, 50000, 100000], 'radix': 8}, [[8, 5, 3, 2, 0], [3, 2, 1, 2]]]], [['closed table 0', {'q': [1224, 1411, 1580, 1785, 2044, 2304, 2625, 2976, 3345, 100000], 'radix': 73425}, [[73425, 72526, 71503, 70373, 69117, 67704, 66144, 64408, 62491, 60401, 0], [899, 1023, 1130, 1256, 1413, 1560, 1736, 1917, 2090, 60401]]], ['closed table 1', {'q': [1483, 1675, 1889, 2148, 2445, 2784, 3127, 100000], 'radix': 95565}, [[95565, 94148, 92571, 90822, 88871, 86698, 84284, 81648, 0], [1417, 1577, 1749, 1951, 2173, 2414, 2636, 81648]]], ['closed table 2', {'q': [947, 1104, 1253, 1408, 1590, 1811, 2059, 2344, 2661, 100000], 'radix': 84753}, [[84753, 83950, 83023, 81983, 80829, 79544, 78103, 76495, 74702, 72714, 0], [803, 927, 1040, 1154, 1285, 1441, 1608, 1793, 1988, 72714]]], ['zero mortality ages', {'q': [0, 0, 577, 100000], 'radix': 100000}, [[100000, 100000, 100000, 99423, 0], [0, 0, 577, 99423]]], ['open table rejected', {'q': [401, 494, 603, 675, 758], 'radix': 10000}, 'open table'], ['early certain death then open', {'q': [861, 100000, 50000], 'radix': 1000}, 'open table'], ['negative rate rejected', {'q': [100, -5, 100000], 'radix': 100000}, 'invalid q'], ['zero radix rejected', {'q': [100, 100000], 'radix': 0}, 'invalid radix'], ['negative radix rejected', {'q': [100, 100000], 'radix': -1000}, 'invalid radix'], ['half-death rounding tie', {'q': [50000, 100000], 'radix': 5}, [[5, 3, 0], [2, 3]]], ['odd radix deaths reconcile', {'q': [34607, 36625, 31203, 24229, 24851, 100000], 'radix': 951}, [[951, 622, 394, 271, 205, 154, 0], [329, 228, 123, 66, 51, 154]]], ['small radix rounding', {'q': [33333, 33333, 50000, 100000], 'radix': 9}, [[9, 6, 4, 2, 0], [3, 2, 2, 2]]]], [['closed table 0', {'q': [843, 986, 1140, 1296, 1486, 1699, 1937, 2195, 100000], 'radix': 30585}, [[30585, 30327, 30028, 29686, 29301, 28866, 28376, 27826, 27215, 0], [258, 299, 342, 385, 435, 490, 550, 611, 27215]]], ['closed table 1', {'q': [1278, 1465, 1677, 1884, 2120, 100000], 'radix': 34567}, [[34567, 34125, 33625, 33061, 32438, 31750, 0], [442, 500, 564, 623, 688, 31750]]], ['closed table 2', {'q': [699, 786, 882, 1036, 1165, 1332, 1497, 1680, 100000], 'radix': 78197}, [[78197, 77650, 77040, 76361, 75570, 74690, 73695, 72592, 71372, 0], [547, 610, 679, 791, 880, 995, 1103, 1220, 71372]]], ['zero mortality ages', {'q': [0, 0, 651, 100000], 'radix': 10000}, [[10000, 10000, 10000, 9935, 0], [0, 0, 65, 9935]]], ['open table rejected', {'q': [293, 338, 418, 503, 565], 'radix': 1000}, 'open table'], ['early certain death then open', {'q': [246, 100000, 50000], 'radix': 10000}, 'open table'], ['negative rate rejected', {'q': [100, -5, 100000], 'radix': 1000}, 'invalid q'], ['zero radix rejected', {'q': [100, 100000], 'radix': 0}, 'invalid radix'], ['negative radix rejected', {'q': [100, 100000], 'radix': -1000}, 'invalid radix'], ['half-death rounding tie', {'q': [50000, 100000], 'radix': 7}, [[7, 4, 0], [3, 4]]], ['odd radix deaths reconcile', {'q': [24577, 26111, 12881, 35469, 14789, 100000], 'radix': 563}, [[563, 425, 314, 274, 177, 151, 0], [138, 111, 40, 97, 26, 151]]], ['small radix rounding', {'q': [33333, 33333, 50000, 100000], 'radix': 10}, [[10, 7, 5, 3, 0], [3, 2, 2, 3]]]], [['closed table 0', {'q': [1676, 1888, 2164, 2470, 2790, 3140, 3538, 3970, 100000], 'radix': 15185}, [[15185, 14930, 14648, 14331, 13977, 13587, 13160, 12694, 12190, 0], [255, 282, 317, 354, 390, 427, 466, 504, 12190]]], ['closed table 1', {'q': [2017, 2262, 2573, 2928, 3329, 3764, 4215, 4722, 5308, 100000], 'radix': 18031}, [[18031, 17667, 17267, 16823, 16330, 15786, 15192, 14552, 13865, 13129, 0], [364, 400, 444, 493, 544, 594, 640, 687, 736, 13129]]], ['closed table 2', {'q': [1538, 1732, 1939, 2177, 2450, 2788, 3129, 3535, 100000], 'radix': 93691}, [[93691, 92250, 90652, 88894, 86959, 84829, 82464, 79884, 77060, 0], [1441, 1598, 1758, 1935, 2130, 2365, 2580, 2824, 77060]]], ['zero mortality ages', {'q': [0, 0, 490, 100000], 'radix': 1000}, [[1000, 1000, 1000, 995, 0], [0, 0, 5, 995]]], ['open table rejected', {'q': [226, 259, 292, 356, 432], 'radix': 100000}, 'open table'], ['early certain death then open', {'q': [598, 100000, 50000], 'radix': 1000}, 'open table'], ['negative rate rejected', {'q': [100, -5, 100000], 'radix': 10000}, 'invalid q'], ['zero radix rejected', {'q': [100, 100000], 'radix': 0}, 'invalid radix'], ['negative radix rejected', {'q': [100, 100000], 'radix': -1000}, 'invalid radix'], ['half-death rounding tie', {'q': [50000, 100000], 'radix': 9}, [[9, 5, 0], [4, 5]]], ['odd radix deaths reconcile', {'q': [33513, 18981, 38831, 14099, 33629, 100000], 'radix': 617}, [[617, 410, 332, 203, 174, 115, 0], [207, 78, 129, 29, 59, 115]]], ['small radix rounding', {'q': [33333, 33333, 50000, 100000], 'radix': 11}, [[11, 7, 5, 3, 0], [4, 2, 2, 3]]]], [['closed table 0', {'q': [992, 1146, 1306, 1489, 1674, 100000], 'radix': 62363}, [[62363, 61744, 61036, 60239, 59342, 58349, 0], [619, 708, 797, 897, 993, 58349]]], ['closed table 1', {'q': [2141, 2429, 2766, 3097, 3484, 3938, 4441, 4974, 5590, 100000], 'radix': 50071}, [[50071, 48999, 47809, 46487, 45047, 43478, 41766, 39911, 37926, 35806, 0], [1072, 1190, 1322, 1440, 1569, 1712, 1855, 1985, 2120, 35806]]], ['closed table 2', {'q': [1626, 1851, 2115, 2406, 2724, 3092, 3463, 3919, 100000], 'radix': 12425}, [[12425, 12223, 11997, 11743, 11460, 11148, 10803, 10429, 10020, 0], [202, 226, 254, 283, 312, 345, 374, 409, 10020]]], ['zero mortality ages', {'q': [0, 0, 245, 100000], 'radix': 1000}, [[1000, 1000, 1000, 998, 0], [0, 0, 2, 998]]], ['open table rejected', {'q': [345, 413, 466, 555, 658], 'radix': 1000}, 'open table'], ['early certain death then open', {'q': [601, 100000, 50000], 'radix': 100000}, 'open table'], ['negative rate rejected', {'q': [100, -5, 100000], 'radix': 1000}, 'invalid q'], ['zero radix rejected', {'q': [100, 100000], 'radix': 0}, 'invalid radix'], ['negative radix rejected', {'q': [100, 100000], 'radix': -1000}, 'invalid radix'], ['half-death rounding tie', {'q': [50000, 100000], 'radix': 11}, [[11, 6, 0], [5, 6]]], ['odd radix deaths reconcile', {'q': [23365, 16837, 21555, 13823, 21221, 100000], 'radix': 421}, [[421, 323, 269, 211, 182, 143, 0], [98, 54, 58, 29, 39, 143]]], ['small radix rounding', {'q': [33333, 33333, 50000, 100000], 'radix': 12}, [[12, 8, 5, 3, 0], [4, 3, 2, 3]]]]]
for label, args, expected in fixtures[N-1]:
check(label, solve(args), expected)
print(json.dumps({"observations": observations, "passed": all(x["passed"] for x in observations)}, ensure_ascii=False))
raise SystemExit(0 if all(x["passed"] for x in observations) else 1)
| Boundary fixture | Actual | Expected | Outcome |
|---|---|---|---|
| closed table 0 | [[69457, 68740, 67939, 67031, 66016, 64888, 0], [717, 801, 908, 1015, 1128, 64888]] | [[69457, 68740, 67939, 67031, 66016, 64888, 0], [717, 801, 908, 1015, 1128, 64888]] | Passed |
| closed table 1 | [[81539, 81055, 80476, 79813, 79071, 78230, 77285, 76232, 0], [484, 579, 663, 742, 841, 945, 1053, 76232]] | [[81539, 81055, 80476, 79813, 79071, 78230, 77285, 76232, 0], [484, 579, 663, 742, 841, 945, 1053, 76232]] | Passed |
| closed table 2 | [[23619, 23497, 23352, 23187, 22998, 22780, 22528, 22239, 0], [122, 145, 165, 189, 218, 252, 289, 22239]] | [[23619, 23497, 23352, 23187, 22998, 22780, 22528, 22239, 0], [122, 145, 165, 189, 218, 252, 289, 22239]] | Passed |
| zero mortality ages | [[100000, 100000, 100000, 99204, 0], [0, 0, 796, 99204]] | [[100000, 100000, 100000, 99204, 0], [0, 0, 796, 99204]] | Passed |
| open table rejected | open table | open table | Passed |
| early certain death then open | [[10000, 9978, 0, 0], [22, 9978, 0]] | open table | Failed |
| negative rate rejected | invalid q | invalid q | Passed |
| zero radix rejected | invalid radix | invalid radix | Passed |
| negative radix rejected | invalid radix | invalid radix | Passed |
| half-death rounding tie | [[3, 2, 0], [1, 2]] | [[3, 2, 0], [1, 2]] | Passed |
| odd radix deaths reconcile | [[361, 304, 234, 147, 118, 101, 0], [57, 70, 87, 29, 17, 101]] | [[361, 304, 234, 147, 118, 101, 0], [57, 70, 87, 29, 17, 101]] | Passed |
| small radix rounding | [[8, 5, 3, 2, 0], [3, 2, 1, 2]] | [[8, 5, 3, 2, 0], [3, 2, 1, 2]] | Passed |
SHA-256 / 11243743fa749e914f134b89e3f4f4d2e4bae341a836982eabf5c9cfc12f81bb
3 / The verified repair
Exit 0"""Failure Map reference implementation. Python standard library only."""
import json
import math
from fractions import Fraction
N = 1
observations = []
def solve(x):
qs=[Fraction(q,100000) for q in x['q']]
if x['radix']<=0: return 'invalid radix'
if any(q<0 or q>1 for q in qs): return 'invalid q'
if not qs or qs[-1]!=1: return 'open table'
l=[x['radix']]
for q in qs:
l.append(math.floor(l[-1]*(1-q)+Fraction(1,2)))
d=[a-b for a,b in zip(l,l[1:])]
return [l,d]
def check(label, actual, expected):
observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
fixtures = [[['closed table 0', {'q': [1033, 1165, 1336, 1514, 1709, 100000], 'radix': 69457}, [[69457, 68740, 67939, 67031, 66016, 64888, 0], [717, 801, 908, 1015, 1128, 64888]]], ['closed table 1', {'q': [593, 714, 824, 930, 1064, 1208, 1363, 100000], 'radix': 81539}, [[81539, 81055, 80476, 79813, 79071, 78230, 77285, 76232, 0], [484, 579, 663, 742, 841, 945, 1053, 76232]]], ['closed table 2', {'q': [515, 615, 705, 814, 949, 1107, 1282, 100000], 'radix': 23619}, [[23619, 23497, 23352, 23187, 22998, 22780, 22528, 22239, 0], [122, 145, 165, 189, 218, 252, 289, 22239]]], ['zero mortality ages', {'q': [0, 0, 796, 100000], 'radix': 100000}, [[100000, 100000, 100000, 99204, 0], [0, 0, 796, 99204]]], ['open table rejected', {'q': [555, 636, 741, 860, 978], 'radix': 10000}, 'open table'], ['early certain death then open', {'q': [223, 100000, 50000], 'radix': 10000}, 'open table'], ['negative rate rejected', {'q': [100, -5, 100000], 'radix': 100000}, 'invalid q'], ['zero radix rejected', {'q': [100, 100000], 'radix': 0}, 'invalid radix'], ['negative radix rejected', {'q': [100, 100000], 'radix': -1000}, 'invalid radix'], ['half-death rounding tie', {'q': [50000, 100000], 'radix': 3}, [[3, 2, 0], [1, 2]]], ['odd radix deaths reconcile', {'q': [15657, 22915, 37291, 19541, 14273, 100000], 'radix': 361}, [[361, 304, 234, 147, 118, 101, 0], [57, 70, 87, 29, 17, 101]]], ['small radix rounding', {'q': [33333, 33333, 50000, 100000], 'radix': 8}, [[8, 5, 3, 2, 0], [3, 2, 1, 2]]]], [['closed table 0', {'q': [1224, 1411, 1580, 1785, 2044, 2304, 2625, 2976, 3345, 100000], 'radix': 73425}, [[73425, 72526, 71503, 70373, 69117, 67704, 66144, 64408, 62491, 60401, 0], [899, 1023, 1130, 1256, 1413, 1560, 1736, 1917, 2090, 60401]]], ['closed table 1', {'q': [1483, 1675, 1889, 2148, 2445, 2784, 3127, 100000], 'radix': 95565}, [[95565, 94148, 92571, 90822, 88871, 86698, 84284, 81648, 0], [1417, 1577, 1749, 1951, 2173, 2414, 2636, 81648]]], ['closed table 2', {'q': [947, 1104, 1253, 1408, 1590, 1811, 2059, 2344, 2661, 100000], 'radix': 84753}, [[84753, 83950, 83023, 81983, 80829, 79544, 78103, 76495, 74702, 72714, 0], [803, 927, 1040, 1154, 1285, 1441, 1608, 1793, 1988, 72714]]], ['zero mortality ages', {'q': [0, 0, 577, 100000], 'radix': 100000}, [[100000, 100000, 100000, 99423, 0], [0, 0, 577, 99423]]], ['open table rejected', {'q': [401, 494, 603, 675, 758], 'radix': 10000}, 'open table'], ['early certain death then open', {'q': [861, 100000, 50000], 'radix': 1000}, 'open table'], ['negative rate rejected', {'q': [100, -5, 100000], 'radix': 100000}, 'invalid q'], ['zero radix rejected', {'q': [100, 100000], 'radix': 0}, 'invalid radix'], ['negative radix rejected', {'q': [100, 100000], 'radix': -1000}, 'invalid radix'], ['half-death rounding tie', {'q': [50000, 100000], 'radix': 5}, [[5, 3, 0], [2, 3]]], ['odd radix deaths reconcile', {'q': [34607, 36625, 31203, 24229, 24851, 100000], 'radix': 951}, [[951, 622, 394, 271, 205, 154, 0], [329, 228, 123, 66, 51, 154]]], ['small radix rounding', {'q': [33333, 33333, 50000, 100000], 'radix': 9}, [[9, 6, 4, 2, 0], [3, 2, 2, 2]]]], [['closed table 0', {'q': [843, 986, 1140, 1296, 1486, 1699, 1937, 2195, 100000], 'radix': 30585}, [[30585, 30327, 30028, 29686, 29301, 28866, 28376, 27826, 27215, 0], [258, 299, 342, 385, 435, 490, 550, 611, 27215]]], ['closed table 1', {'q': [1278, 1465, 1677, 1884, 2120, 100000], 'radix': 34567}, [[34567, 34125, 33625, 33061, 32438, 31750, 0], [442, 500, 564, 623, 688, 31750]]], ['closed table 2', {'q': [699, 786, 882, 1036, 1165, 1332, 1497, 1680, 100000], 'radix': 78197}, [[78197, 77650, 77040, 76361, 75570, 74690, 73695, 72592, 71372, 0], [547, 610, 679, 791, 880, 995, 1103, 1220, 71372]]], ['zero mortality ages', {'q': [0, 0, 651, 100000], 'radix': 10000}, [[10000, 10000, 10000, 9935, 0], [0, 0, 65, 9935]]], ['open table rejected', {'q': [293, 338, 418, 503, 565], 'radix': 1000}, 'open table'], ['early certain death then open', {'q': [246, 100000, 50000], 'radix': 10000}, 'open table'], ['negative rate rejected', {'q': [100, -5, 100000], 'radix': 1000}, 'invalid q'], ['zero radix rejected', {'q': [100, 100000], 'radix': 0}, 'invalid radix'], ['negative radix rejected', {'q': [100, 100000], 'radix': -1000}, 'invalid radix'], ['half-death rounding tie', {'q': [50000, 100000], 'radix': 7}, [[7, 4, 0], [3, 4]]], ['odd radix deaths reconcile', {'q': [24577, 26111, 12881, 35469, 14789, 100000], 'radix': 563}, [[563, 425, 314, 274, 177, 151, 0], [138, 111, 40, 97, 26, 151]]], ['small radix rounding', {'q': [33333, 33333, 50000, 100000], 'radix': 10}, [[10, 7, 5, 3, 0], [3, 2, 2, 3]]]], [['closed table 0', {'q': [1676, 1888, 2164, 2470, 2790, 3140, 3538, 3970, 100000], 'radix': 15185}, [[15185, 14930, 14648, 14331, 13977, 13587, 13160, 12694, 12190, 0], [255, 282, 317, 354, 390, 427, 466, 504, 12190]]], ['closed table 1', {'q': [2017, 2262, 2573, 2928, 3329, 3764, 4215, 4722, 5308, 100000], 'radix': 18031}, [[18031, 17667, 17267, 16823, 16330, 15786, 15192, 14552, 13865, 13129, 0], [364, 400, 444, 493, 544, 594, 640, 687, 736, 13129]]], ['closed table 2', {'q': [1538, 1732, 1939, 2177, 2450, 2788, 3129, 3535, 100000], 'radix': 93691}, [[93691, 92250, 90652, 88894, 86959, 84829, 82464, 79884, 77060, 0], [1441, 1598, 1758, 1935, 2130, 2365, 2580, 2824, 77060]]], ['zero mortality ages', {'q': [0, 0, 490, 100000], 'radix': 1000}, [[1000, 1000, 1000, 995, 0], [0, 0, 5, 995]]], ['open table rejected', {'q': [226, 259, 292, 356, 432], 'radix': 100000}, 'open table'], ['early certain death then open', {'q': [598, 100000, 50000], 'radix': 1000}, 'open table'], ['negative rate rejected', {'q': [100, -5, 100000], 'radix': 10000}, 'invalid q'], ['zero radix rejected', {'q': [100, 100000], 'radix': 0}, 'invalid radix'], ['negative radix rejected', {'q': [100, 100000], 'radix': -1000}, 'invalid radix'], ['half-death rounding tie', {'q': [50000, 100000], 'radix': 9}, [[9, 5, 0], [4, 5]]], ['odd radix deaths reconcile', {'q': [33513, 18981, 38831, 14099, 33629, 100000], 'radix': 617}, [[617, 410, 332, 203, 174, 115, 0], [207, 78, 129, 29, 59, 115]]], ['small radix rounding', {'q': [33333, 33333, 50000, 100000], 'radix': 11}, [[11, 7, 5, 3, 0], [4, 2, 2, 3]]]], [['closed table 0', {'q': [992, 1146, 1306, 1489, 1674, 100000], 'radix': 62363}, [[62363, 61744, 61036, 60239, 59342, 58349, 0], [619, 708, 797, 897, 993, 58349]]], ['closed table 1', {'q': [2141, 2429, 2766, 3097, 3484, 3938, 4441, 4974, 5590, 100000], 'radix': 50071}, [[50071, 48999, 47809, 46487, 45047, 43478, 41766, 39911, 37926, 35806, 0], [1072, 1190, 1322, 1440, 1569, 1712, 1855, 1985, 2120, 35806]]], ['closed table 2', {'q': [1626, 1851, 2115, 2406, 2724, 3092, 3463, 3919, 100000], 'radix': 12425}, [[12425, 12223, 11997, 11743, 11460, 11148, 10803, 10429, 10020, 0], [202, 226, 254, 283, 312, 345, 374, 409, 10020]]], ['zero mortality ages', {'q': [0, 0, 245, 100000], 'radix': 1000}, [[1000, 1000, 1000, 998, 0], [0, 0, 2, 998]]], ['open table rejected', {'q': [345, 413, 466, 555, 658], 'radix': 1000}, 'open table'], ['early certain death then open', {'q': [601, 100000, 50000], 'radix': 100000}, 'open table'], ['negative rate rejected', {'q': [100, -5, 100000], 'radix': 1000}, 'invalid q'], ['zero radix rejected', {'q': [100, 100000], 'radix': 0}, 'invalid radix'], ['negative radix rejected', {'q': [100, 100000], 'radix': -1000}, 'invalid radix'], ['half-death rounding tie', {'q': [50000, 100000], 'radix': 11}, [[11, 6, 0], [5, 6]]], ['odd radix deaths reconcile', {'q': [23365, 16837, 21555, 13823, 21221, 100000], 'radix': 421}, [[421, 323, 269, 211, 182, 143, 0], [98, 54, 58, 29, 39, 143]]], ['small radix rounding', {'q': [33333, 33333, 50000, 100000], 'radix': 12}, [[12, 8, 5, 3, 0], [4, 3, 2, 3]]]]]
for label, args, expected in fixtures[N-1]:
check(label, solve(args), expected)
print(json.dumps({"observations": observations, "passed": all(x["passed"] for x in observations)}, ensure_ascii=False))
raise SystemExit(0 if all(x["passed"] for x in observations) else 1)
| Boundary fixture | Actual | Expected | Outcome |
|---|---|---|---|
| closed table 0 | [[69457, 68740, 67939, 67031, 66016, 64888, 0], [717, 801, 908, 1015, 1128, 64888]] | [[69457, 68740, 67939, 67031, 66016, 64888, 0], [717, 801, 908, 1015, 1128, 64888]] | Passed |
| closed table 1 | [[81539, 81055, 80476, 79813, 79071, 78230, 77285, 76232, 0], [484, 579, 663, 742, 841, 945, 1053, 76232]] | [[81539, 81055, 80476, 79813, 79071, 78230, 77285, 76232, 0], [484, 579, 663, 742, 841, 945, 1053, 76232]] | Passed |
| closed table 2 | [[23619, 23497, 23352, 23187, 22998, 22780, 22528, 22239, 0], [122, 145, 165, 189, 218, 252, 289, 22239]] | [[23619, 23497, 23352, 23187, 22998, 22780, 22528, 22239, 0], [122, 145, 165, 189, 218, 252, 289, 22239]] | Passed |
| zero mortality ages | [[100000, 100000, 100000, 99204, 0], [0, 0, 796, 99204]] | [[100000, 100000, 100000, 99204, 0], [0, 0, 796, 99204]] | Passed |
| open table rejected | open table | open table | Passed |
| early certain death then open | open table | open table | Passed |
| negative rate rejected | invalid q | invalid q | Passed |
| zero radix rejected | invalid radix | invalid radix | Passed |
| negative radix rejected | invalid radix | invalid radix | Passed |
| half-death rounding tie | [[3, 2, 0], [1, 2]] | [[3, 2, 0], [1, 2]] | Passed |
| odd radix deaths reconcile | [[361, 304, 234, 147, 118, 101, 0], [57, 70, 87, 29, 17, 101]] | [[361, 304, 234, 147, 118, 101, 0], [57, 70, 87, 29, 17, 101]] | Passed |
| small radix rounding | [[8, 5, 3, 2, 0], [3, 2, 1, 2]] | [[8, 5, 3, 2, 0], [3, 2, 1, 2]] | Passed |
SHA-256 / e0672c3742c5c9804549451fce3aa8625b62ebaede73fabd296667c722f6959f
Verification & scope
A deterministic bounded teaching model of a stipulated toy actuarial contract. Rates are small synthetic tables, exact rationals are used where practical and floats are rounded at the output; it makes no claim of conformance with any published table, standard of practice or regulation. This reproducer isolates one failure mechanism. Results cover the supplied fixtures. Variants within a family share a test contract and should remain grouped when constructing evaluation splits. Related mechanisms with a shared evaluation_group must also remain together; these controlled models are not independent production incidents.
Observations recorded using Python 3.12.14 at 2026-09-29T14:47:13.597308+00:00.
Case digest / 15ea5a9c4f90b6410dad3f971edb55cd0a914c3c2f3d43f135dbbd32f4239d71