FA-62996 / Insurance premium rating / Open access
Loss development and trend to rating period: Tail factor is dropped · case 01
Ultimate losses omit development beyond the last factor.
ROOT CAUSE
The cumulative factor starts at 1 instead of the tail.
VERIFIED REPAIR
Initialise the cumulative factor with the tail factor.
Unsuccessful approach: Dropping the tail only for mature years still leaves those years under-developed.
Case contract
Input losses, age (months, positive multiple of 12 else 'invalid'), ldfs (age-to-age *1000, first is 12-24), tail *1000, trend (annual per mille), exp_start and pol_start (months). CDF = tail * product of ldfs from index age/12-1. Trend span = (pol_start+12) - (exp_start+6) months; factor = (1+t)^years * (1+t*rem/12). Return floor(losses*CDF*factor+1/2).
Why this case matters
Projected ultimate trended losses drive the indicated rate level for the prospective policy period.
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):
age=x['age']
if age<=0 or age%12: return 'invalid'
k=age//12-1
cdf=Fraction(1)
for f in x['ldfs'][k:]:
cdf*=Fraction(f,1000)
gap=(x['pol_start']+12)-(x['exp_start']+6)
t=Fraction(x['trend'],1000)
yrs,rem=divmod(gap,12)
tf=(1+t)**yrs*(1+t*rem/12)
return math.floor(x['losses']*cdf*tf+Fraction(1,2))
def check(label, actual, expected):
observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
fixtures = [[['immature year', {'losses': 6101262, 'age': 12, 'ldfs': [1610, 1250, 1101, 1040, 1015], 'tail': 1010, 'trend': 45, 'exp_start': 41, 'pol_start': 76}, 16756377], ['second evaluation', {'losses': 5233772, 'age': 24, 'ldfs': [1610, 1250, 1101, 1040, 1015], 'tail': 1010, 'trend': 50, 'exp_start': 8, 'pol_start': 48}, 9260396], ['mid development', {'losses': 6787850, 'age': 48, 'ldfs': [1610, 1250, 1101, 1040, 1015], 'tail': 1010, 'trend': 45, 'exp_start': 36, 'pol_start': 60}, 8080698], ['fully developed except tail', {'losses': 4698103, 'age': 72, 'ldfs': [1610, 1250, 1101, 1040, 1015], 'tail': 1020, 'trend': 30, 'exp_start': 28, 'pol_start': 58}, 5236419], ['beyond last factor', {'losses': 4308538, 'age': 84, 'ldfs': [1610, 1250, 1101, 1040, 1015], 'tail': 1030, 'trend': 50, 'exp_start': 31, 'pol_start': 68}, 5287139], ['trend span whole years', {'losses': 6440858, 'age': 36, 'ldfs': [1610, 1250, 1101, 1040, 1015], 'tail': 1010, 'trend': 50, 'exp_start': 25, 'pol_start': 37}, 8137012], ['trend span with remainder', {'losses': 8332360, 'age': 36, 'ldfs': [1610, 1250, 1101, 1040, 1015], 'tail': 1010, 'trend': 30, 'exp_start': 9, 'pol_start': 28}, 10402433], ['non-annual age rejected', {'losses': 7240281, 'age': 18, 'ldfs': [1610, 1250, 1101, 1040, 1015], 'tail': 1010, 'trend': 50, 'exp_start': 19, 'pol_start': 53}, 'invalid'], ['zero age rejected', {'losses': 1186186, 'age': 0, 'ldfs': [1610, 1250, 1101, 1040, 1015], 'tail': 1010, 'trend': 30, 'exp_start': 24, 'pol_start': 50}, 'invalid']], [['immature year', {'losses': 8928412, 'age': 12, 'ldfs': [1620, 1250, 1102, 1040, 1015], 'tail': 1010, 'trend': 30, 'exp_start': 21, 'pol_start': 50}, 23155698], ['second evaluation', {'losses': 8550609, 'age': 24, 'ldfs': [1620, 1250, 1102, 1040, 1015], 'tail': 1010, 'trend': 30, 'exp_start': 37, 'pol_start': 74}, 13962253], ['mid development', {'losses': 5731401, 'age': 48, 'ldfs': [1620, 1250, 1102, 1040, 1015], 'tail': 1010, 'trend': 50, 'exp_start': 3, 'pol_start': 21}, 6736901], ['fully developed except tail', {'losses': 2333827, 'age': 72, 'ldfs': [1620, 1250, 1102, 1040, 1015], 'tail': 1020, 'trend': 45, 'exp_start': 54, 'pol_start': 89}, 2767485], ['beyond last factor', {'losses': 3119424, 'age': 84, 'ldfs': [1620, 1250, 1102, 1040, 1015], 'tail': 1030, 'trend': 30, 'exp_start': 3, 'pol_start': 30}, 3485374], ['trend span whole years', {'losses': 3522316, 'age': 36, 'ldfs': [1620, 1250, 1102, 1040, 1015], 'tail': 1010, 'trend': 30, 'exp_start': 2, 'pol_start': 14}, 4326472], ['trend span with remainder', {'losses': 8948897, 'age': 36, 'ldfs': [1620, 1250, 1102, 1040, 1015], 'tail': 1010, 'trend': 50, 'exp_start': 5, 'pol_start': 24}, 11640088], ['non-annual age rejected', {'losses': 3221043, 'age': 18, 'ldfs': [1620, 1250, 1102, 1040, 1015], 'tail': 1010, 'trend': 45, 'exp_start': 39, 'pol_start': 74}, 'invalid'], ['zero age rejected', {'losses': 8655065, 'age': 0, 'ldfs': [1620, 1250, 1102, 1040, 1015], 'tail': 1010, 'trend': 50, 'exp_start': 37, 'pol_start': 56}, 'invalid']], [['immature year', {'losses': 2645801, 'age': 12, 'ldfs': [1630, 1250, 1103, 1040, 1015], 'tail': 1010, 'trend': 45, 'exp_start': 29, 'pol_start': 65}, 7397130], ['second evaluation', {'losses': 6201911, 'age': 24, 'ldfs': [1630, 1250, 1103, 1040, 1015], 'tail': 1010, 'trend': 45, 'exp_start': 20, 'pol_start': 39}, 9992863], ['mid development', {'losses': 4311093, 'age': 48, 'ldfs': [1630, 1250, 1103, 1040, 1015], 'tail': 1010, 'trend': 30, 'exp_start': 52, 'pol_start': 71}, 4888403], ['fully developed except tail', {'losses': 6201749, 'age': 72, 'ldfs': [1630, 1250, 1103, 1040, 1015], 'tail': 1020, 'trend': 50, 'exp_start': 31, 'pol_start': 67}, 7505958], ['beyond last factor', {'losses': 6770980, 'age': 84, 'ldfs': [1630, 1250, 1103, 1040, 1015], 'tail': 1030, 'trend': 30, 'exp_start': 32, 'pol_start': 52}, 7435827], ['trend span whole years', {'losses': 5004070, 'age': 36, 'ldfs': [1630, 1250, 1103, 1040, 1015], 'tail': 1010, 'trend': 50, 'exp_start': 32, 'pol_start': 44}, 6333340], ['trend span with remainder', {'losses': 7309289, 'age': 36, 'ldfs': [1630, 1250, 1103, 1040, 1015], 'tail': 1010, 'trend': 50, 'exp_start': 1, 'pol_start': 20}, 9516030], ['non-annual age rejected', {'losses': 6899078, 'age': 18, 'ldfs': [1630, 1250, 1103, 1040, 1015], 'tail': 1010, 'trend': 30, 'exp_start': 13, 'pol_start': 37}, 'invalid'], ['zero age rejected', {'losses': 6441743, 'age': 0, 'ldfs': [1630, 1250, 1103, 1040, 1015], 'tail': 1010, 'trend': 50, 'exp_start': 39, 'pol_start': 71}, 'invalid']], [['immature year', {'losses': 6749109, 'age': 12, 'ldfs': [1640, 1250, 1104, 1040, 1015], 'tail': 1010, 'trend': 30, 'exp_start': 58, 'pol_start': 77}, 17320043], ['second evaluation', {'losses': 2964503, 'age': 24, 'ldfs': [1640, 1250, 1104, 1040, 1015], 'tail': 1010, 'trend': 50, 'exp_start': 23, 'pol_start': 52}, 5029129], ['mid development', {'losses': 1958599, 'age': 48, 'ldfs': [1640, 1250, 1104, 1040, 1015], 'tail': 1010, 'trend': 45, 'exp_start': 8, 'pol_start': 45}, 2445504], ['fully developed except tail', {'losses': 7303981, 'age': 72, 'ldfs': [1640, 1250, 1104, 1040, 1015], 'tail': 1020, 'trend': 50, 'exp_start': 46, 'pol_start': 76}, 8624376], ['beyond last factor', {'losses': 5438017, 'age': 84, 'ldfs': [1640, 1250, 1104, 1040, 1015], 'tail': 1030, 'trend': 45, 'exp_start': 33, 'pol_start': 56}, 6231290], ['trend span whole years', {'losses': 3936734, 'age': 36, 'ldfs': [1640, 1250, 1104, 1040, 1015], 'tail': 1010, 'trend': 45, 'exp_start': 18, 'pol_start': 30}, 4951143], ['trend span with remainder', {'losses': 3870348, 'age': 36, 'ldfs': [1640, 1250, 1104, 1040, 1015], 'tail': 1010, 'trend': 30, 'exp_start': 36, 'pol_start': 53}, 4821242], ['non-annual age rejected', {'losses': 8705484, 'age': 18, 'ldfs': [1640, 1250, 1104, 1040, 1015], 'tail': 1010, 'trend': 50, 'exp_start': 56, 'pol_start': 83}, 'invalid'], ['zero age rejected', {'losses': 8329864, 'age': 0, 'ldfs': [1640, 1250, 1104, 1040, 1015], 'tail': 1010, 'trend': 45, 'exp_start': 0, 'pol_start': 30}, 'invalid']], [['immature year', {'losses': 2993216, 'age': 12, 'ldfs': [1650, 1250, 1105, 1040, 1015], 'tail': 1010, 'trend': 50, 'exp_start': 42, 'pol_start': 72}, 8419435], ['second evaluation', {'losses': 8759921, 'age': 24, 'ldfs': [1650, 1250, 1105, 1040, 1015], 'tail': 1010, 'trend': 45, 'exp_start': 24, 'pol_start': 53}, 14668335], ['mid development', {'losses': 7728493, 'age': 48, 'ldfs': [1650, 1250, 1105, 1040, 1015], 'tail': 1010, 'trend': 45, 'exp_start': 21, 'pol_start': 45}, 9200501], ['fully developed except tail', {'losses': 8451860, 'age': 72, 'ldfs': [1650, 1250, 1105, 1040, 1015], 'tail': 1020, 'trend': 45, 'exp_start': 16, 'pol_start': 46}, 9837876], ['beyond last factor', {'losses': 5895078, 'age': 84, 'ldfs': [1650, 1250, 1105, 1040, 1015], 'tail': 1030, 'trend': 30, 'exp_start': 17, 'pol_start': 56}, 6784249], ['trend span whole years', {'losses': 4683619, 'age': 36, 'ldfs': [1650, 1250, 1105, 1040, 1015], 'tail': 1010, 'trend': 45, 'exp_start': 55, 'pol_start': 67}, 5895820], ['trend span with remainder', {'losses': 5208228, 'age': 36, 'ldfs': [1650, 1250, 1105, 1040, 1015], 'tail': 1010, 'trend': 30, 'exp_start': 58, 'pol_start': 77}, 6525771], ['non-annual age rejected', {'losses': 2071597, 'age': 18, 'ldfs': [1650, 1250, 1105, 1040, 1015], 'tail': 1010, 'trend': 50, 'exp_start': 44, 'pol_start': 71}, 'invalid'], ['zero age rejected', {'losses': 2191426, 'age': 0, 'ldfs': [1650, 1250, 1105, 1040, 1015], 'tail': 1010, 'trend': 30, 'exp_start': 13, 'pol_start': 49}, 'invalid']]]
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 |
|---|---|---|---|
| immature year | 16590473 | 16756377 | Failed |
| second evaluation | 9168709 | 9260396 | Failed |
| mid development | 8000691 | 8080698 | Failed |
| fully developed except tail | 5133744 | 5236419 | Failed |
| beyond last factor | 5133145 | 5287139 | Failed |
| trend span whole years | 8056448 | 8137012 | Failed |
| trend span with remainder | 10299439 | 10402433 | Failed |
| non-annual age rejected | invalid | invalid | Passed |
| zero age rejected | invalid | invalid | Passed |
SHA-256 / ca78b58944dd10002da94b6ad065641e09d30cc57a7d7e841617122e93f907dd
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):
age=x['age']
if age<=0 or age%12: return 'invalid'
k=age//12-1
cdf=Fraction(x['tail'],1000) if k<len(x['ldfs']) else Fraction(1)
for f in x['ldfs'][k:]:
cdf*=Fraction(f,1000)
gap=(x['pol_start']+12)-(x['exp_start']+6)
t=Fraction(x['trend'],1000)
yrs,rem=divmod(gap,12)
tf=(1+t)**yrs*(1+t*rem/12)
return math.floor(x['losses']*cdf*tf+Fraction(1,2))
def check(label, actual, expected):
observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
fixtures = [[['immature year', {'losses': 6101262, 'age': 12, 'ldfs': [1610, 1250, 1101, 1040, 1015], 'tail': 1010, 'trend': 45, 'exp_start': 41, 'pol_start': 76}, 16756377], ['second evaluation', {'losses': 5233772, 'age': 24, 'ldfs': [1610, 1250, 1101, 1040, 1015], 'tail': 1010, 'trend': 50, 'exp_start': 8, 'pol_start': 48}, 9260396], ['mid development', {'losses': 6787850, 'age': 48, 'ldfs': [1610, 1250, 1101, 1040, 1015], 'tail': 1010, 'trend': 45, 'exp_start': 36, 'pol_start': 60}, 8080698], ['fully developed except tail', {'losses': 4698103, 'age': 72, 'ldfs': [1610, 1250, 1101, 1040, 1015], 'tail': 1020, 'trend': 30, 'exp_start': 28, 'pol_start': 58}, 5236419], ['beyond last factor', {'losses': 4308538, 'age': 84, 'ldfs': [1610, 1250, 1101, 1040, 1015], 'tail': 1030, 'trend': 50, 'exp_start': 31, 'pol_start': 68}, 5287139], ['trend span whole years', {'losses': 6440858, 'age': 36, 'ldfs': [1610, 1250, 1101, 1040, 1015], 'tail': 1010, 'trend': 50, 'exp_start': 25, 'pol_start': 37}, 8137012], ['trend span with remainder', {'losses': 8332360, 'age': 36, 'ldfs': [1610, 1250, 1101, 1040, 1015], 'tail': 1010, 'trend': 30, 'exp_start': 9, 'pol_start': 28}, 10402433], ['non-annual age rejected', {'losses': 7240281, 'age': 18, 'ldfs': [1610, 1250, 1101, 1040, 1015], 'tail': 1010, 'trend': 50, 'exp_start': 19, 'pol_start': 53}, 'invalid'], ['zero age rejected', {'losses': 1186186, 'age': 0, 'ldfs': [1610, 1250, 1101, 1040, 1015], 'tail': 1010, 'trend': 30, 'exp_start': 24, 'pol_start': 50}, 'invalid']], [['immature year', {'losses': 8928412, 'age': 12, 'ldfs': [1620, 1250, 1102, 1040, 1015], 'tail': 1010, 'trend': 30, 'exp_start': 21, 'pol_start': 50}, 23155698], ['second evaluation', {'losses': 8550609, 'age': 24, 'ldfs': [1620, 1250, 1102, 1040, 1015], 'tail': 1010, 'trend': 30, 'exp_start': 37, 'pol_start': 74}, 13962253], ['mid development', {'losses': 5731401, 'age': 48, 'ldfs': [1620, 1250, 1102, 1040, 1015], 'tail': 1010, 'trend': 50, 'exp_start': 3, 'pol_start': 21}, 6736901], ['fully developed except tail', {'losses': 2333827, 'age': 72, 'ldfs': [1620, 1250, 1102, 1040, 1015], 'tail': 1020, 'trend': 45, 'exp_start': 54, 'pol_start': 89}, 2767485], ['beyond last factor', {'losses': 3119424, 'age': 84, 'ldfs': [1620, 1250, 1102, 1040, 1015], 'tail': 1030, 'trend': 30, 'exp_start': 3, 'pol_start': 30}, 3485374], ['trend span whole years', {'losses': 3522316, 'age': 36, 'ldfs': [1620, 1250, 1102, 1040, 1015], 'tail': 1010, 'trend': 30, 'exp_start': 2, 'pol_start': 14}, 4326472], ['trend span with remainder', {'losses': 8948897, 'age': 36, 'ldfs': [1620, 1250, 1102, 1040, 1015], 'tail': 1010, 'trend': 50, 'exp_start': 5, 'pol_start': 24}, 11640088], ['non-annual age rejected', {'losses': 3221043, 'age': 18, 'ldfs': [1620, 1250, 1102, 1040, 1015], 'tail': 1010, 'trend': 45, 'exp_start': 39, 'pol_start': 74}, 'invalid'], ['zero age rejected', {'losses': 8655065, 'age': 0, 'ldfs': [1620, 1250, 1102, 1040, 1015], 'tail': 1010, 'trend': 50, 'exp_start': 37, 'pol_start': 56}, 'invalid']], [['immature year', {'losses': 2645801, 'age': 12, 'ldfs': [1630, 1250, 1103, 1040, 1015], 'tail': 1010, 'trend': 45, 'exp_start': 29, 'pol_start': 65}, 7397130], ['second evaluation', {'losses': 6201911, 'age': 24, 'ldfs': [1630, 1250, 1103, 1040, 1015], 'tail': 1010, 'trend': 45, 'exp_start': 20, 'pol_start': 39}, 9992863], ['mid development', {'losses': 4311093, 'age': 48, 'ldfs': [1630, 1250, 1103, 1040, 1015], 'tail': 1010, 'trend': 30, 'exp_start': 52, 'pol_start': 71}, 4888403], ['fully developed except tail', {'losses': 6201749, 'age': 72, 'ldfs': [1630, 1250, 1103, 1040, 1015], 'tail': 1020, 'trend': 50, 'exp_start': 31, 'pol_start': 67}, 7505958], ['beyond last factor', {'losses': 6770980, 'age': 84, 'ldfs': [1630, 1250, 1103, 1040, 1015], 'tail': 1030, 'trend': 30, 'exp_start': 32, 'pol_start': 52}, 7435827], ['trend span whole years', {'losses': 5004070, 'age': 36, 'ldfs': [1630, 1250, 1103, 1040, 1015], 'tail': 1010, 'trend': 50, 'exp_start': 32, 'pol_start': 44}, 6333340], ['trend span with remainder', {'losses': 7309289, 'age': 36, 'ldfs': [1630, 1250, 1103, 1040, 1015], 'tail': 1010, 'trend': 50, 'exp_start': 1, 'pol_start': 20}, 9516030], ['non-annual age rejected', {'losses': 6899078, 'age': 18, 'ldfs': [1630, 1250, 1103, 1040, 1015], 'tail': 1010, 'trend': 30, 'exp_start': 13, 'pol_start': 37}, 'invalid'], ['zero age rejected', {'losses': 6441743, 'age': 0, 'ldfs': [1630, 1250, 1103, 1040, 1015], 'tail': 1010, 'trend': 50, 'exp_start': 39, 'pol_start': 71}, 'invalid']], [['immature year', {'losses': 6749109, 'age': 12, 'ldfs': [1640, 1250, 1104, 1040, 1015], 'tail': 1010, 'trend': 30, 'exp_start': 58, 'pol_start': 77}, 17320043], ['second evaluation', {'losses': 2964503, 'age': 24, 'ldfs': [1640, 1250, 1104, 1040, 1015], 'tail': 1010, 'trend': 50, 'exp_start': 23, 'pol_start': 52}, 5029129], ['mid development', {'losses': 1958599, 'age': 48, 'ldfs': [1640, 1250, 1104, 1040, 1015], 'tail': 1010, 'trend': 45, 'exp_start': 8, 'pol_start': 45}, 2445504], ['fully developed except tail', {'losses': 7303981, 'age': 72, 'ldfs': [1640, 1250, 1104, 1040, 1015], 'tail': 1020, 'trend': 50, 'exp_start': 46, 'pol_start': 76}, 8624376], ['beyond last factor', {'losses': 5438017, 'age': 84, 'ldfs': [1640, 1250, 1104, 1040, 1015], 'tail': 1030, 'trend': 45, 'exp_start': 33, 'pol_start': 56}, 6231290], ['trend span whole years', {'losses': 3936734, 'age': 36, 'ldfs': [1640, 1250, 1104, 1040, 1015], 'tail': 1010, 'trend': 45, 'exp_start': 18, 'pol_start': 30}, 4951143], ['trend span with remainder', {'losses': 3870348, 'age': 36, 'ldfs': [1640, 1250, 1104, 1040, 1015], 'tail': 1010, 'trend': 30, 'exp_start': 36, 'pol_start': 53}, 4821242], ['non-annual age rejected', {'losses': 8705484, 'age': 18, 'ldfs': [1640, 1250, 1104, 1040, 1015], 'tail': 1010, 'trend': 50, 'exp_start': 56, 'pol_start': 83}, 'invalid'], ['zero age rejected', {'losses': 8329864, 'age': 0, 'ldfs': [1640, 1250, 1104, 1040, 1015], 'tail': 1010, 'trend': 45, 'exp_start': 0, 'pol_start': 30}, 'invalid']], [['immature year', {'losses': 2993216, 'age': 12, 'ldfs': [1650, 1250, 1105, 1040, 1015], 'tail': 1010, 'trend': 50, 'exp_start': 42, 'pol_start': 72}, 8419435], ['second evaluation', {'losses': 8759921, 'age': 24, 'ldfs': [1650, 1250, 1105, 1040, 1015], 'tail': 1010, 'trend': 45, 'exp_start': 24, 'pol_start': 53}, 14668335], ['mid development', {'losses': 7728493, 'age': 48, 'ldfs': [1650, 1250, 1105, 1040, 1015], 'tail': 1010, 'trend': 45, 'exp_start': 21, 'pol_start': 45}, 9200501], ['fully developed except tail', {'losses': 8451860, 'age': 72, 'ldfs': [1650, 1250, 1105, 1040, 1015], 'tail': 1020, 'trend': 45, 'exp_start': 16, 'pol_start': 46}, 9837876], ['beyond last factor', {'losses': 5895078, 'age': 84, 'ldfs': [1650, 1250, 1105, 1040, 1015], 'tail': 1030, 'trend': 30, 'exp_start': 17, 'pol_start': 56}, 6784249], ['trend span whole years', {'losses': 4683619, 'age': 36, 'ldfs': [1650, 1250, 1105, 1040, 1015], 'tail': 1010, 'trend': 45, 'exp_start': 55, 'pol_start': 67}, 5895820], ['trend span with remainder', {'losses': 5208228, 'age': 36, 'ldfs': [1650, 1250, 1105, 1040, 1015], 'tail': 1010, 'trend': 30, 'exp_start': 58, 'pol_start': 77}, 6525771], ['non-annual age rejected', {'losses': 2071597, 'age': 18, 'ldfs': [1650, 1250, 1105, 1040, 1015], 'tail': 1010, 'trend': 50, 'exp_start': 44, 'pol_start': 71}, 'invalid'], ['zero age rejected', {'losses': 2191426, 'age': 0, 'ldfs': [1650, 1250, 1105, 1040, 1015], 'tail': 1010, 'trend': 30, 'exp_start': 13, 'pol_start': 49}, 'invalid']]]
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 |
|---|---|---|---|
| immature year | 16756377 | 16756377 | Passed |
| second evaluation | 9260396 | 9260396 | Passed |
| mid development | 8080698 | 8080698 | Passed |
| fully developed except tail | 5133744 | 5236419 | Failed |
| beyond last factor | 5133145 | 5287139 | Failed |
| trend span whole years | 8137012 | 8137012 | Passed |
| trend span with remainder | 10402433 | 10402433 | Passed |
| non-annual age rejected | invalid | invalid | Passed |
| zero age rejected | invalid | invalid | Passed |
SHA-256 / db388fad40155f1964524d16cc6d776c1b13f433234c040be42dd231f2958ef2
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):
age=x['age']
if age<=0 or age%12: return 'invalid'
k=age//12-1
cdf=Fraction(x['tail'],1000)
for f in x['ldfs'][k:]:
cdf*=Fraction(f,1000)
gap=(x['pol_start']+12)-(x['exp_start']+6)
t=Fraction(x['trend'],1000)
yrs,rem=divmod(gap,12)
tf=(1+t)**yrs*(1+t*rem/12)
return math.floor(x['losses']*cdf*tf+Fraction(1,2))
def check(label, actual, expected):
observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
fixtures = [[['immature year', {'losses': 6101262, 'age': 12, 'ldfs': [1610, 1250, 1101, 1040, 1015], 'tail': 1010, 'trend': 45, 'exp_start': 41, 'pol_start': 76}, 16756377], ['second evaluation', {'losses': 5233772, 'age': 24, 'ldfs': [1610, 1250, 1101, 1040, 1015], 'tail': 1010, 'trend': 50, 'exp_start': 8, 'pol_start': 48}, 9260396], ['mid development', {'losses': 6787850, 'age': 48, 'ldfs': [1610, 1250, 1101, 1040, 1015], 'tail': 1010, 'trend': 45, 'exp_start': 36, 'pol_start': 60}, 8080698], ['fully developed except tail', {'losses': 4698103, 'age': 72, 'ldfs': [1610, 1250, 1101, 1040, 1015], 'tail': 1020, 'trend': 30, 'exp_start': 28, 'pol_start': 58}, 5236419], ['beyond last factor', {'losses': 4308538, 'age': 84, 'ldfs': [1610, 1250, 1101, 1040, 1015], 'tail': 1030, 'trend': 50, 'exp_start': 31, 'pol_start': 68}, 5287139], ['trend span whole years', {'losses': 6440858, 'age': 36, 'ldfs': [1610, 1250, 1101, 1040, 1015], 'tail': 1010, 'trend': 50, 'exp_start': 25, 'pol_start': 37}, 8137012], ['trend span with remainder', {'losses': 8332360, 'age': 36, 'ldfs': [1610, 1250, 1101, 1040, 1015], 'tail': 1010, 'trend': 30, 'exp_start': 9, 'pol_start': 28}, 10402433], ['non-annual age rejected', {'losses': 7240281, 'age': 18, 'ldfs': [1610, 1250, 1101, 1040, 1015], 'tail': 1010, 'trend': 50, 'exp_start': 19, 'pol_start': 53}, 'invalid'], ['zero age rejected', {'losses': 1186186, 'age': 0, 'ldfs': [1610, 1250, 1101, 1040, 1015], 'tail': 1010, 'trend': 30, 'exp_start': 24, 'pol_start': 50}, 'invalid']], [['immature year', {'losses': 8928412, 'age': 12, 'ldfs': [1620, 1250, 1102, 1040, 1015], 'tail': 1010, 'trend': 30, 'exp_start': 21, 'pol_start': 50}, 23155698], ['second evaluation', {'losses': 8550609, 'age': 24, 'ldfs': [1620, 1250, 1102, 1040, 1015], 'tail': 1010, 'trend': 30, 'exp_start': 37, 'pol_start': 74}, 13962253], ['mid development', {'losses': 5731401, 'age': 48, 'ldfs': [1620, 1250, 1102, 1040, 1015], 'tail': 1010, 'trend': 50, 'exp_start': 3, 'pol_start': 21}, 6736901], ['fully developed except tail', {'losses': 2333827, 'age': 72, 'ldfs': [1620, 1250, 1102, 1040, 1015], 'tail': 1020, 'trend': 45, 'exp_start': 54, 'pol_start': 89}, 2767485], ['beyond last factor', {'losses': 3119424, 'age': 84, 'ldfs': [1620, 1250, 1102, 1040, 1015], 'tail': 1030, 'trend': 30, 'exp_start': 3, 'pol_start': 30}, 3485374], ['trend span whole years', {'losses': 3522316, 'age': 36, 'ldfs': [1620, 1250, 1102, 1040, 1015], 'tail': 1010, 'trend': 30, 'exp_start': 2, 'pol_start': 14}, 4326472], ['trend span with remainder', {'losses': 8948897, 'age': 36, 'ldfs': [1620, 1250, 1102, 1040, 1015], 'tail': 1010, 'trend': 50, 'exp_start': 5, 'pol_start': 24}, 11640088], ['non-annual age rejected', {'losses': 3221043, 'age': 18, 'ldfs': [1620, 1250, 1102, 1040, 1015], 'tail': 1010, 'trend': 45, 'exp_start': 39, 'pol_start': 74}, 'invalid'], ['zero age rejected', {'losses': 8655065, 'age': 0, 'ldfs': [1620, 1250, 1102, 1040, 1015], 'tail': 1010, 'trend': 50, 'exp_start': 37, 'pol_start': 56}, 'invalid']], [['immature year', {'losses': 2645801, 'age': 12, 'ldfs': [1630, 1250, 1103, 1040, 1015], 'tail': 1010, 'trend': 45, 'exp_start': 29, 'pol_start': 65}, 7397130], ['second evaluation', {'losses': 6201911, 'age': 24, 'ldfs': [1630, 1250, 1103, 1040, 1015], 'tail': 1010, 'trend': 45, 'exp_start': 20, 'pol_start': 39}, 9992863], ['mid development', {'losses': 4311093, 'age': 48, 'ldfs': [1630, 1250, 1103, 1040, 1015], 'tail': 1010, 'trend': 30, 'exp_start': 52, 'pol_start': 71}, 4888403], ['fully developed except tail', {'losses': 6201749, 'age': 72, 'ldfs': [1630, 1250, 1103, 1040, 1015], 'tail': 1020, 'trend': 50, 'exp_start': 31, 'pol_start': 67}, 7505958], ['beyond last factor', {'losses': 6770980, 'age': 84, 'ldfs': [1630, 1250, 1103, 1040, 1015], 'tail': 1030, 'trend': 30, 'exp_start': 32, 'pol_start': 52}, 7435827], ['trend span whole years', {'losses': 5004070, 'age': 36, 'ldfs': [1630, 1250, 1103, 1040, 1015], 'tail': 1010, 'trend': 50, 'exp_start': 32, 'pol_start': 44}, 6333340], ['trend span with remainder', {'losses': 7309289, 'age': 36, 'ldfs': [1630, 1250, 1103, 1040, 1015], 'tail': 1010, 'trend': 50, 'exp_start': 1, 'pol_start': 20}, 9516030], ['non-annual age rejected', {'losses': 6899078, 'age': 18, 'ldfs': [1630, 1250, 1103, 1040, 1015], 'tail': 1010, 'trend': 30, 'exp_start': 13, 'pol_start': 37}, 'invalid'], ['zero age rejected', {'losses': 6441743, 'age': 0, 'ldfs': [1630, 1250, 1103, 1040, 1015], 'tail': 1010, 'trend': 50, 'exp_start': 39, 'pol_start': 71}, 'invalid']], [['immature year', {'losses': 6749109, 'age': 12, 'ldfs': [1640, 1250, 1104, 1040, 1015], 'tail': 1010, 'trend': 30, 'exp_start': 58, 'pol_start': 77}, 17320043], ['second evaluation', {'losses': 2964503, 'age': 24, 'ldfs': [1640, 1250, 1104, 1040, 1015], 'tail': 1010, 'trend': 50, 'exp_start': 23, 'pol_start': 52}, 5029129], ['mid development', {'losses': 1958599, 'age': 48, 'ldfs': [1640, 1250, 1104, 1040, 1015], 'tail': 1010, 'trend': 45, 'exp_start': 8, 'pol_start': 45}, 2445504], ['fully developed except tail', {'losses': 7303981, 'age': 72, 'ldfs': [1640, 1250, 1104, 1040, 1015], 'tail': 1020, 'trend': 50, 'exp_start': 46, 'pol_start': 76}, 8624376], ['beyond last factor', {'losses': 5438017, 'age': 84, 'ldfs': [1640, 1250, 1104, 1040, 1015], 'tail': 1030, 'trend': 45, 'exp_start': 33, 'pol_start': 56}, 6231290], ['trend span whole years', {'losses': 3936734, 'age': 36, 'ldfs': [1640, 1250, 1104, 1040, 1015], 'tail': 1010, 'trend': 45, 'exp_start': 18, 'pol_start': 30}, 4951143], ['trend span with remainder', {'losses': 3870348, 'age': 36, 'ldfs': [1640, 1250, 1104, 1040, 1015], 'tail': 1010, 'trend': 30, 'exp_start': 36, 'pol_start': 53}, 4821242], ['non-annual age rejected', {'losses': 8705484, 'age': 18, 'ldfs': [1640, 1250, 1104, 1040, 1015], 'tail': 1010, 'trend': 50, 'exp_start': 56, 'pol_start': 83}, 'invalid'], ['zero age rejected', {'losses': 8329864, 'age': 0, 'ldfs': [1640, 1250, 1104, 1040, 1015], 'tail': 1010, 'trend': 45, 'exp_start': 0, 'pol_start': 30}, 'invalid']], [['immature year', {'losses': 2993216, 'age': 12, 'ldfs': [1650, 1250, 1105, 1040, 1015], 'tail': 1010, 'trend': 50, 'exp_start': 42, 'pol_start': 72}, 8419435], ['second evaluation', {'losses': 8759921, 'age': 24, 'ldfs': [1650, 1250, 1105, 1040, 1015], 'tail': 1010, 'trend': 45, 'exp_start': 24, 'pol_start': 53}, 14668335], ['mid development', {'losses': 7728493, 'age': 48, 'ldfs': [1650, 1250, 1105, 1040, 1015], 'tail': 1010, 'trend': 45, 'exp_start': 21, 'pol_start': 45}, 9200501], ['fully developed except tail', {'losses': 8451860, 'age': 72, 'ldfs': [1650, 1250, 1105, 1040, 1015], 'tail': 1020, 'trend': 45, 'exp_start': 16, 'pol_start': 46}, 9837876], ['beyond last factor', {'losses': 5895078, 'age': 84, 'ldfs': [1650, 1250, 1105, 1040, 1015], 'tail': 1030, 'trend': 30, 'exp_start': 17, 'pol_start': 56}, 6784249], ['trend span whole years', {'losses': 4683619, 'age': 36, 'ldfs': [1650, 1250, 1105, 1040, 1015], 'tail': 1010, 'trend': 45, 'exp_start': 55, 'pol_start': 67}, 5895820], ['trend span with remainder', {'losses': 5208228, 'age': 36, 'ldfs': [1650, 1250, 1105, 1040, 1015], 'tail': 1010, 'trend': 30, 'exp_start': 58, 'pol_start': 77}, 6525771], ['non-annual age rejected', {'losses': 2071597, 'age': 18, 'ldfs': [1650, 1250, 1105, 1040, 1015], 'tail': 1010, 'trend': 50, 'exp_start': 44, 'pol_start': 71}, 'invalid'], ['zero age rejected', {'losses': 2191426, 'age': 0, 'ldfs': [1650, 1250, 1105, 1040, 1015], 'tail': 1010, 'trend': 30, 'exp_start': 13, 'pol_start': 49}, 'invalid']]]
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 |
|---|---|---|---|
| immature year | 16756377 | 16756377 | Passed |
| second evaluation | 9260396 | 9260396 | Passed |
| mid development | 8080698 | 8080698 | Passed |
| fully developed except tail | 5236419 | 5236419 | Passed |
| beyond last factor | 5287139 | 5287139 | Passed |
| trend span whole years | 8137012 | 8137012 | Passed |
| trend span with remainder | 10402433 | 10402433 | Passed |
| non-annual age rejected | invalid | invalid | Passed |
| zero age rejected | invalid | invalid | Passed |
SHA-256 / 3418058ae8bb9a8b2ed6d503f09eb47a89a3fe2683291e97205c878166b5d293
Verification & scope
A deterministic bounded teaching model of a stipulated toy rating contract. Amounts are integer cents and factors exact rationals; it makes no claim of conformance with any filed rating plan, bureau manual or jurisdiction. 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:10.119061+00:00.
Case digest / 28c0a97b49d362bf13d2c79bef0b3eae64c08a0317e576a3cd64e0d7f72d1c67