FA-63006 / Insurance premium rating / Open access
Loss development and trend to rating period: Remainder months are trended as whole years · case 01
Trend factors explode when the span is not a whole number of years.
ROOT CAUSE
The simple-interest remainder uses months as years.
VERIFIED REPAIR
Scale the remainder by rem/12.
Unsuccessful approach: Integer dividing the remainder by 12 discards the partial year entirely.
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(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)
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 | 20148773 | 16756377 | Failed |
| second evaluation | 13334970 | 9260396 | Failed |
| mid development | 10036662 | 8080698 | Failed |
| fully developed except tail | 5236419 | 5236419 | Passed |
| beyond last factor | 6935357 | 5287139 | Failed |
| trend span whole years | 10320113 | 8137012 | Failed |
| trend span with remainder | 10687787 | 10402433 | Failed |
| non-annual age rejected | invalid | invalid | Passed |
| zero age rejected | invalid | invalid | Passed |
SHA-256 / 1fee54198d5da0c5409ae7fc6f585b56fad8e85ec2c27143395eb61da20f6e75
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)
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 | 16447978 | 16756377 | Failed |
| second evaluation | 8889980 | 9260396 | Failed |
| mid development | 7902883 | 8080698 | Failed |
| fully developed except tail | 5236419 | 5236419 | Passed |
| beyond last factor | 5137301 | 5287139 | Failed |
| trend span whole years | 7938548 | 8137012 | Failed |
| trend span with remainder | 10376492 | 10402433 | Failed |
| non-annual age rejected | invalid | invalid | Passed |
| zero age rejected | invalid | invalid | Passed |
SHA-256 / d22867beaf720ad9ea4b72f643bc6d7131373a951e3a3b3b790a2c33af574194
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.166732+00:00.
Case digest / b95ac464011e64b5d1b110cf9b80acdab874ef828af2aaa41feb85a3c303c117