@actuarial-ts/core 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (120) hide show
  1. package/LICENSE +202 -0
  2. package/NOTICE +5 -0
  3. package/README.md +133 -0
  4. package/dist/benktander.d.ts +45 -0
  5. package/dist/benktander.d.ts.map +1 -0
  6. package/dist/benktander.js +38 -0
  7. package/dist/benktander.js.map +1 -0
  8. package/dist/berquist.d.ts +14 -0
  9. package/dist/berquist.d.ts.map +1 -0
  10. package/dist/berquist.js +292 -0
  11. package/dist/berquist.js.map +1 -0
  12. package/dist/bf.d.ts +23 -0
  13. package/dist/bf.d.ts.map +1 -0
  14. package/dist/bf.js +83 -0
  15. package/dist/bf.js.map +1 -0
  16. package/dist/capping.d.ts +96 -0
  17. package/dist/capping.d.ts.map +1 -0
  18. package/dist/capping.js +207 -0
  19. package/dist/capping.js.map +1 -0
  20. package/dist/caseOutstanding.d.ts +80 -0
  21. package/dist/caseOutstanding.d.ts.map +1 -0
  22. package/dist/caseOutstanding.js +147 -0
  23. package/dist/caseOutstanding.js.map +1 -0
  24. package/dist/chainladder.d.ts +15 -0
  25. package/dist/chainladder.d.ts.map +1 -0
  26. package/dist/chainladder.js +81 -0
  27. package/dist/chainladder.js.map +1 -0
  28. package/dist/clark.d.ts +91 -0
  29. package/dist/clark.d.ts.map +1 -0
  30. package/dist/clark.js +490 -0
  31. package/dist/clark.js.map +1 -0
  32. package/dist/diagnostics.d.ts +80 -0
  33. package/dist/diagnostics.d.ts.map +1 -0
  34. package/dist/diagnostics.js +359 -0
  35. package/dist/diagnostics.js.map +1 -0
  36. package/dist/discounting.d.ts +166 -0
  37. package/dist/discounting.d.ts.map +1 -0
  38. package/dist/discounting.js +210 -0
  39. package/dist/discounting.js.map +1 -0
  40. package/dist/elrMethods.d.ts +104 -0
  41. package/dist/elrMethods.d.ts.map +1 -0
  42. package/dist/elrMethods.js +151 -0
  43. package/dist/elrMethods.js.map +1 -0
  44. package/dist/factors.d.ts +19 -0
  45. package/dist/factors.d.ts.map +1 -0
  46. package/dist/factors.js +136 -0
  47. package/dist/factors.js.map +1 -0
  48. package/dist/fisherLange.d.ts +92 -0
  49. package/dist/fisherLange.d.ts.map +1 -0
  50. package/dist/fisherLange.js +224 -0
  51. package/dist/fisherLange.js.map +1 -0
  52. package/dist/freqSev.d.ts +57 -0
  53. package/dist/freqSev.d.ts.map +1 -0
  54. package/dist/freqSev.js +82 -0
  55. package/dist/freqSev.js.map +1 -0
  56. package/dist/ilf.d.ts +111 -0
  57. package/dist/ilf.d.ts.map +1 -0
  58. package/dist/ilf.js +449 -0
  59. package/dist/ilf.js.map +1 -0
  60. package/dist/index.d.ts +29 -0
  61. package/dist/index.d.ts.map +1 -0
  62. package/dist/index.js +29 -0
  63. package/dist/index.js.map +1 -0
  64. package/dist/mack.d.ts +55 -0
  65. package/dist/mack.d.ts.map +1 -0
  66. package/dist/mack.js +244 -0
  67. package/dist/mack.js.map +1 -0
  68. package/dist/merzWuthrich.d.ts +29 -0
  69. package/dist/merzWuthrich.d.ts.map +1 -0
  70. package/dist/merzWuthrich.js +132 -0
  71. package/dist/merzWuthrich.js.map +1 -0
  72. package/dist/munichChainLadder.d.ts +101 -0
  73. package/dist/munichChainLadder.d.ts.map +1 -0
  74. package/dist/munichChainLadder.js +254 -0
  75. package/dist/munichChainLadder.js.map +1 -0
  76. package/dist/odpBootstrap.d.ts +65 -0
  77. package/dist/odpBootstrap.d.ts.map +1 -0
  78. package/dist/odpBootstrap.js +243 -0
  79. package/dist/odpBootstrap.js.map +1 -0
  80. package/dist/onlevel.d.ts +39 -0
  81. package/dist/onlevel.d.ts.map +1 -0
  82. package/dist/onlevel.js +104 -0
  83. package/dist/onlevel.js.map +1 -0
  84. package/dist/salvageSubro.d.ts +82 -0
  85. package/dist/salvageSubro.d.ts.map +1 -0
  86. package/dist/salvageSubro.js +100 -0
  87. package/dist/salvageSubro.js.map +1 -0
  88. package/dist/stochastic.d.ts +56 -0
  89. package/dist/stochastic.d.ts.map +1 -0
  90. package/dist/stochastic.js +99 -0
  91. package/dist/stochastic.js.map +1 -0
  92. package/dist/tail.d.ts +18 -0
  93. package/dist/tail.d.ts.map +1 -0
  94. package/dist/tail.js +121 -0
  95. package/dist/tail.js.map +1 -0
  96. package/dist/trend.d.ts +46 -0
  97. package/dist/trend.d.ts.map +1 -0
  98. package/dist/trend.js +99 -0
  99. package/dist/trend.js.map +1 -0
  100. package/dist/triangle.d.ts +19 -0
  101. package/dist/triangle.d.ts.map +1 -0
  102. package/dist/triangle.js +163 -0
  103. package/dist/triangle.js.map +1 -0
  104. package/dist/triangleAlgebra.d.ts +10 -0
  105. package/dist/triangleAlgebra.d.ts.map +1 -0
  106. package/dist/triangleAlgebra.js +97 -0
  107. package/dist/triangleAlgebra.js.map +1 -0
  108. package/dist/types.d.ts +280 -0
  109. package/dist/types.d.ts.map +1 -0
  110. package/dist/types.js +79 -0
  111. package/dist/types.js.map +1 -0
  112. package/dist/ulae.d.ts +172 -0
  113. package/dist/ulae.d.ts.map +1 -0
  114. package/dist/ulae.js +164 -0
  115. package/dist/ulae.js.map +1 -0
  116. package/dist/util.d.ts +24 -0
  117. package/dist/util.d.ts.map +1 -0
  118. package/dist/util.js +66 -0
  119. package/dist/util.js.map +1 -0
  120. package/package.json +66 -0
@@ -0,0 +1,46 @@
1
+ /**
2
+ * Trend machinery: log-linear regressions on year-indexed actuarial series
3
+ * (ultimate frequency, severity, pure premium), with the same menu
4
+ * discipline as the LDF exhibit - several fitted windows plus judgment.
5
+ *
6
+ * ln(y) = a + b·t fitted by ordinary least squares; the annual trend is
7
+ * e^b - 1. R-squared is reported so thin or noisy series are judged, not
8
+ * trusted blindly.
9
+ */
10
+ export interface TrendPoint {
11
+ /** Origin year (the regression x, centered internally). */
12
+ year: number;
13
+ /** The series value (must be positive to enter the log fit). */
14
+ value: number | null;
15
+ }
16
+ export interface TrendFit {
17
+ key: string;
18
+ label: string;
19
+ /** Annual trend rate, e.g. 0.05 = +5%/yr; null when the window can't fit. */
20
+ annualRate: number | null;
21
+ rSquared: number | null;
22
+ nPoints: number;
23
+ /** Yearly values the window actually used. */
24
+ usedYears: number[];
25
+ warnings: string[];
26
+ }
27
+ export interface TrendAnalysis {
28
+ points: TrendPoint[];
29
+ fits: TrendFit[];
30
+ }
31
+ /**
32
+ * Fits the standard windows over a year-indexed series: all years, last 5
33
+ * YEARS, last 3 YEARS, and all years excluding the highest and lowest values
34
+ * (the ex-hi-lo medial convention). Windows are sized in POINTS PER YEAR so
35
+ * a quarterly series' "Last 5 years" really spans 5 years (20 quarters), not
36
+ * 5 points. Non-positive and missing values are excluded from every window
37
+ * (a log fit cannot see them), with a warning.
38
+ */
39
+ export declare function analyzeTrend(points: TrendPoint[], pointsPerYear?: number): TrendAnalysis;
40
+ /**
41
+ * Trend a value from one year's cost level to another's:
42
+ * value × (1 + rate)^(toYear − fromYear). Midpoint-to-midpoint conventions
43
+ * are the caller's responsibility (whole years in, whole years out).
44
+ */
45
+ export declare function trendValue(value: number, rate: number, fromYear: number, toYear: number): number;
46
+ //# sourceMappingURL=trend.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"trend.d.ts","sourceRoot":"","sources":["../src/trend.ts"],"names":[],"mappings":"AAGA;;;;;;;;GAQG;AAEH,MAAM,WAAW,UAAU;IACzB,2DAA2D;IAC3D,IAAI,EAAE,MAAM,CAAC;IACb,gEAAgE;IAChE,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;CACtB;AAED,MAAM,WAAW,QAAQ;IACvB,GAAG,EAAE,MAAM,CAAC;IACZ,KAAK,EAAE,MAAM,CAAC;IACd,6EAA6E;IAC7E,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;IACxB,OAAO,EAAE,MAAM,CAAC;IAChB,8CAA8C;IAC9C,SAAS,EAAE,MAAM,EAAE,CAAC;IACpB,QAAQ,EAAE,MAAM,EAAE,CAAC;CACpB;AAED,MAAM,WAAW,aAAa;IAC5B,MAAM,EAAE,UAAU,EAAE,CAAC;IACrB,IAAI,EAAE,QAAQ,EAAE,CAAC;CAClB;AAgDD;;;;;;;GAOG;AACH,wBAAgB,YAAY,CAAC,MAAM,EAAE,UAAU,EAAE,EAAE,aAAa,SAAI,GAAG,aAAa,CAyCnF;AAED;;;;GAIG;AACH,wBAAgB,UAAU,CACxB,KAAK,EAAE,MAAM,EACb,IAAI,EAAE,MAAM,EACZ,QAAQ,EAAE,MAAM,EAChB,MAAM,EAAE,MAAM,GACb,MAAM,CAKR"}
package/dist/trend.js ADDED
@@ -0,0 +1,99 @@
1
+ import { ReservingError } from "./types.js";
2
+ import { isNum, ols } from "./util.js";
3
+ function fitWindow(points, key, label) {
4
+ const warnings = [];
5
+ if (points.length < 3) {
6
+ return {
7
+ key,
8
+ label,
9
+ annualRate: null,
10
+ rSquared: null,
11
+ nPoints: points.length,
12
+ usedYears: points.map((p) => p.year),
13
+ warnings: ["Fewer than 3 usable points; no fit"],
14
+ };
15
+ }
16
+ const xs = points.map((p) => p.year);
17
+ const ys = points.map((p) => Math.log(p.value));
18
+ // n >= 3 here, so a null fit can only mean zero variation in years.
19
+ const fit = ols(xs, ys);
20
+ if (fit === null) {
21
+ return {
22
+ key,
23
+ label,
24
+ annualRate: null,
25
+ rSquared: null,
26
+ nPoints: points.length,
27
+ usedYears: xs,
28
+ warnings: ["No variation in years; no fit"],
29
+ };
30
+ }
31
+ if (points.length < 5) {
32
+ warnings.push(`Only ${points.length} points; the fitted trend is volatile`);
33
+ }
34
+ return {
35
+ key,
36
+ label,
37
+ annualRate: Math.exp(fit.slope) - 1,
38
+ rSquared: fit.rSquared,
39
+ nPoints: points.length,
40
+ usedYears: xs,
41
+ warnings,
42
+ };
43
+ }
44
+ /**
45
+ * Fits the standard windows over a year-indexed series: all years, last 5
46
+ * YEARS, last 3 YEARS, and all years excluding the highest and lowest values
47
+ * (the ex-hi-lo medial convention). Windows are sized in POINTS PER YEAR so
48
+ * a quarterly series' "Last 5 years" really spans 5 years (20 quarters), not
49
+ * 5 points. Non-positive and missing values are excluded from every window
50
+ * (a log fit cannot see them), with a warning.
51
+ */
52
+ export function analyzeTrend(points, pointsPerYear = 1) {
53
+ const usable = points
54
+ .filter((p) => isNum(p.value) && p.value > 0)
55
+ .sort((a, b) => a.year - b.year);
56
+ const excluded = points.length - usable.length;
57
+ const fits = [];
58
+ const base = (key, label, pts) => {
59
+ const fit = fitWindow(pts, key, label);
60
+ if (excluded > 0) {
61
+ fit.warnings.push(`${excluded} missing/non-positive year(s) excluded from the series`);
62
+ }
63
+ fits.push(fit);
64
+ };
65
+ const ppy = Math.max(1, Math.round(pointsPerYear));
66
+ base("all", "All years", usable);
67
+ base("last5", "Last 5 years", usable.slice(-5 * ppy));
68
+ base("last3", "Last 3 years", usable.slice(-3 * ppy));
69
+ if (usable.length >= 5) {
70
+ const sortedByValue = [...usable].sort((a, b) => a.value - b.value);
71
+ const hi = sortedByValue[sortedByValue.length - 1];
72
+ const lo = sortedByValue[0];
73
+ base("exhilo", "Ex high/low", usable.filter((p) => p !== hi && p !== lo));
74
+ }
75
+ else {
76
+ fits.push({
77
+ key: "exhilo",
78
+ label: "Ex high/low",
79
+ annualRate: null,
80
+ rSquared: null,
81
+ nPoints: usable.length,
82
+ usedYears: [],
83
+ warnings: ["Needs at least 5 usable points"],
84
+ });
85
+ }
86
+ return { points, fits };
87
+ }
88
+ /**
89
+ * Trend a value from one year's cost level to another's:
90
+ * value × (1 + rate)^(toYear − fromYear). Midpoint-to-midpoint conventions
91
+ * are the caller's responsibility (whole years in, whole years out).
92
+ */
93
+ export function trendValue(value, rate, fromYear, toYear) {
94
+ if (!isNum(rate) || rate <= -1) {
95
+ throw new ReservingError("BAD_TREND", "A trend rate must be a number greater than -100%");
96
+ }
97
+ return value * Math.pow(1 + rate, toYear - fromYear);
98
+ }
99
+ //# sourceMappingURL=trend.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"trend.js","sourceRoot":"","sources":["../src/trend.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,19 @@
1
+ import type { ClaimSnapshot, OriginCadence, Triangle, TriangleKind } from "./types.js";
2
+ export interface BuildTrianglesOptions {
3
+ cadence: OriginCadence;
4
+ /** ISO evaluation date of the analysis (the latest diagonal). */
5
+ asOfDate: string;
6
+ }
7
+ export interface TriangleSet {
8
+ paid: Triangle;
9
+ incurred: Triangle;
10
+ caseReserve: Triangle;
11
+ reportedCount: Triangle;
12
+ openCount: Triangle;
13
+ closedCount: Triangle;
14
+ closedWithPayCount: Triangle;
15
+ }
16
+ export declare function buildTriangles(claims: ClaimSnapshot[], options: BuildTrianglesOptions): TriangleSet;
17
+ /** Constructs a triangle directly from a grid of values (import path). */
18
+ export declare function triangleFromGrid(kind: TriangleKind, origins: string[], ages: number[], values: (number | null)[][]): Triangle;
19
+ //# sourceMappingURL=triangle.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"triangle.d.ts","sourceRoot":"","sources":["../src/triangle.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,aAAa,EACb,aAAa,EACb,QAAQ,EACR,YAAY,EACb,MAAM,YAAY,CAAC;AAqEpB,MAAM,WAAW,qBAAqB;IACpC,OAAO,EAAE,aAAa,CAAC;IACvB,iEAAiE;IACjE,QAAQ,EAAE,MAAM,CAAC;CAClB;AAED,MAAM,WAAW,WAAW;IAC1B,IAAI,EAAE,QAAQ,CAAC;IACf,QAAQ,EAAE,QAAQ,CAAC;IACnB,WAAW,EAAE,QAAQ,CAAC;IACtB,aAAa,EAAE,QAAQ,CAAC;IACxB,SAAS,EAAE,QAAQ,CAAC;IACpB,WAAW,EAAE,QAAQ,CAAC;IACtB,kBAAkB,EAAE,QAAQ,CAAC;CAC9B;AASD,wBAAgB,cAAc,CAC5B,MAAM,EAAE,aAAa,EAAE,EACvB,OAAO,EAAE,qBAAqB,GAC7B,WAAW,CAoHb;AAED,0EAA0E;AAC1E,wBAAgB,gBAAgB,CAC9B,IAAI,EAAE,YAAY,EAClB,OAAO,EAAE,MAAM,EAAE,EACjB,IAAI,EAAE,MAAM,EAAE,EACd,MAAM,EAAE,CAAC,MAAM,GAAG,IAAI,CAAC,EAAE,EAAE,GAC1B,QAAQ,CAUV"}
@@ -0,0 +1,163 @@
1
+ import { ReservingError } from "./types.js";
2
+ function parseISO(date) {
3
+ const match = /^(\d{4})-(\d{2})-(\d{2})/.exec(date);
4
+ if (!match) {
5
+ throw new ReservingError("BAD_DATE", `Invalid ISO date: "${date}"`);
6
+ }
7
+ const y = Number(match[1]);
8
+ const m = Number(match[2]);
9
+ const d = Number(match[3]);
10
+ if (m < 1 || m > 12 || d < 1 || d > 31) {
11
+ throw new ReservingError("BAD_DATE", `Invalid ISO date: "${date}"`);
12
+ }
13
+ return { y, m, d };
14
+ }
15
+ function monthIndex(p) {
16
+ return p.y * 12 + (p.m - 1);
17
+ }
18
+ function daysInMonth(y, m) {
19
+ return new Date(Date.UTC(y, m, 0)).getUTCDate();
20
+ }
21
+ /** ISO date of the last day of the month holding this month index. */
22
+ function endOfMonthISO(mIdx) {
23
+ const y = Math.floor(mIdx / 12);
24
+ const m = (mIdx % 12) + 1;
25
+ const d = daysInMonth(y, m);
26
+ return `${String(y).padStart(4, "0")}-${String(m).padStart(2, "0")}-${String(d).padStart(2, "0")}`;
27
+ }
28
+ function periodIndexOf(p, cadence) {
29
+ return cadence === "annual" ? p.y : p.y * 4 + Math.floor((p.m - 1) / 3);
30
+ }
31
+ function periodLabel(index, cadence) {
32
+ if (cadence === "annual")
33
+ return String(index);
34
+ const y = Math.floor(index / 4);
35
+ const q = (index % 4) + 1;
36
+ return `${y}Q${q}`;
37
+ }
38
+ /** Month index of the first month of an origin period. */
39
+ function periodStartMonth(index, cadence) {
40
+ return cadence === "annual" ? index * 12 : index * 3;
41
+ }
42
+ export function buildTriangles(claims, options) {
43
+ const { cadence, asOfDate } = options;
44
+ if (claims.length === 0) {
45
+ throw new ReservingError("NO_CLAIMS", "Cannot build triangles from an empty loss run");
46
+ }
47
+ const asOf = parseISO(asOfDate);
48
+ const asOfMonth = monthIndex(asOf);
49
+ const asOfIsMonthEnd = asOf.d === daysInMonth(asOf.y, asOf.m);
50
+ // The latest complete evaluation month.
51
+ const lastCompleteMonth = asOfIsMonthEnd ? asOfMonth : asOfMonth - 1;
52
+ const cadenceMonths = cadence === "annual" ? 12 : 3;
53
+ // Group snapshots into claim timelines and find the origin period range.
54
+ const byClaim = new Map();
55
+ let minPeriod = Infinity;
56
+ let maxPeriod = -Infinity;
57
+ for (const snap of claims) {
58
+ const accident = parseISO(snap.accidentDate);
59
+ if (snap.evaluationDate > asOfDate)
60
+ continue; // beyond the analysis date
61
+ const period = periodIndexOf(accident, cadence);
62
+ minPeriod = Math.min(minPeriod, period);
63
+ maxPeriod = Math.max(maxPeriod, period);
64
+ let timeline = byClaim.get(snap.claimId);
65
+ if (!timeline) {
66
+ timeline = { originIdx: period, reportISO: snap.reportDate, snapshots: [] };
67
+ byClaim.set(snap.claimId, timeline);
68
+ }
69
+ timeline.snapshots.push(snap);
70
+ }
71
+ if (!Number.isFinite(minPeriod)) {
72
+ throw new ReservingError("NO_CLAIMS", "No claim snapshots fall on or before the as-of date");
73
+ }
74
+ for (const timeline of byClaim.values()) {
75
+ timeline.snapshots.sort((a, b) => a.evaluationDate < b.evaluationDate ? -1 : a.evaluationDate > b.evaluationDate ? 1 : 0);
76
+ }
77
+ const nOrigins = maxPeriod - minPeriod + 1;
78
+ const origins = [];
79
+ for (let p = minPeriod; p <= maxPeriod; p++)
80
+ origins.push(periodLabel(p, cadence));
81
+ // Ages available to the oldest origin period determine the column count.
82
+ const oldestStart = periodStartMonth(minPeriod, cadence);
83
+ const maxAge = lastCompleteMonth - oldestStart + 1;
84
+ const nAges = Math.floor(maxAge / cadenceMonths);
85
+ if (nAges < 1) {
86
+ throw new ReservingError("NO_DEVELOPMENT", "The as-of date precedes the first complete development age");
87
+ }
88
+ const ages = [];
89
+ for (let j = 1; j <= nAges; j++)
90
+ ages.push(j * cadenceMonths);
91
+ const mk = (kind) => ({
92
+ kind,
93
+ origins: [...origins],
94
+ ages: [...ages],
95
+ values: Array.from({ length: nOrigins }, (_, i) => ages.map((age) => {
96
+ const evalMonth = periodStartMonth(minPeriod + i, cadence) + age - 1;
97
+ return evalMonth <= lastCompleteMonth ? 0 : null;
98
+ })),
99
+ });
100
+ const set = {
101
+ paid: mk("paid"),
102
+ incurred: mk("incurred"),
103
+ caseReserve: mk("caseReserve"),
104
+ reportedCount: mk("reportedCount"),
105
+ openCount: mk("openCount"),
106
+ closedCount: mk("closedCount"),
107
+ closedWithPayCount: mk("closedWithPayCount"),
108
+ };
109
+ const add = (tri, i, j, v) => {
110
+ const cell = tri.values[i][j];
111
+ if (cell !== null && cell !== undefined)
112
+ tri.values[i][j] = cell + v;
113
+ };
114
+ for (const timeline of byClaim.values()) {
115
+ const i = timeline.originIdx - minPeriod;
116
+ const originStart = periodStartMonth(timeline.originIdx, cadence);
117
+ for (let j = 0; j < nAges; j++) {
118
+ const evalMonth = originStart + ages[j] - 1;
119
+ if (evalMonth > lastCompleteMonth)
120
+ break; // this and later cells are null
121
+ const evalISO = endOfMonthISO(evalMonth);
122
+ if (timeline.reportISO > evalISO)
123
+ continue; // not yet reported
124
+ // Latest snapshot on or before the cell's evaluation date.
125
+ let state = null;
126
+ for (const snap of timeline.snapshots) {
127
+ if (snap.evaluationDate <= evalISO)
128
+ state = snap;
129
+ else
130
+ break;
131
+ }
132
+ const paid = state?.paidToDate ?? 0;
133
+ const caseReserve = state?.status === "open" ? (state?.caseReserve ?? 0) : 0;
134
+ const isClosed = state?.status === "closed";
135
+ add(set.reportedCount, i, j, 1);
136
+ add(set.paid, i, j, paid);
137
+ add(set.caseReserve, i, j, caseReserve);
138
+ add(set.incurred, i, j, paid + caseReserve);
139
+ if (isClosed) {
140
+ add(set.closedCount, i, j, 1);
141
+ if (paid > 0)
142
+ add(set.closedWithPayCount, i, j, 1);
143
+ }
144
+ else {
145
+ add(set.openCount, i, j, 1);
146
+ }
147
+ }
148
+ }
149
+ return set;
150
+ }
151
+ /** Constructs a triangle directly from a grid of values (import path). */
152
+ export function triangleFromGrid(kind, origins, ages, values) {
153
+ if (values.length !== origins.length) {
154
+ throw new ReservingError("SHAPE", "Row count does not match origin count");
155
+ }
156
+ for (const row of values) {
157
+ if (row.length !== ages.length) {
158
+ throw new ReservingError("SHAPE", "Column count does not match age count");
159
+ }
160
+ }
161
+ return { kind, origins: [...origins], ages: [...ages], values: values.map((r) => [...r]) };
162
+ }
163
+ //# sourceMappingURL=triangle.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"triangle.js","sourceRoot":"","sources":["../src/triangle.ts"],"names":[],"mappings":"AAMA,OAAO,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AAsB5C,SAAS,QAAQ,CAAC,IAAY;IAC5B,MAAM,KAAK,GAAG,0BAA0B,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IACpD,IAAI,CAAC,KAAK,EAAE,CAAC;QACX,MAAM,IAAI,cAAc,CAAC,UAAU,EAAE,sBAAsB,IAAI,GAAG,CAAC,CAAC;IACtE,CAAC;IACD,MAAM,CAAC,GAAG,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC;IAC3B,MAAM,CAAC,GAAG,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC;IAC3B,MAAM,CAAC,GAAG,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC;IAC3B,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,GAAG,EAAE,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,GAAG,EAAE,EAAE,CAAC;QACvC,MAAM,IAAI,cAAc,CAAC,UAAU,EAAE,sBAAsB,IAAI,GAAG,CAAC,CAAC;IACtE,CAAC;IACD,OAAO,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC;AACrB,CAAC;AAED,SAAS,UAAU,CAAC,CAAa;IAC/B,OAAO,CAAC,CAAC,CAAC,GAAG,EAAE,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC;AAC9B,CAAC;AAED,SAAS,WAAW,CAAC,CAAS,EAAE,CAAS;IACvC,OAAO,IAAI,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,UAAU,EAAE,CAAC;AAClD,CAAC;AAED,sEAAsE;AACtE,SAAS,aAAa,CAAC,IAAY;IACjC,MAAM,CAAC,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,GAAG,EAAE,CAAC,CAAC;IAChC,MAAM,CAAC,GAAG,CAAC,IAAI,GAAG,EAAE,CAAC,GAAG,CAAC,CAAC;IAC1B,MAAM,CAAC,GAAG,WAAW,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;IAC5B,OAAO,GAAG,MAAM,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,EAAE,GAAG,CAAC,IAAI,MAAM,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,EAAE,GAAG,CAAC,IAAI,MAAM,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,EAAE,GAAG,CAAC,EAAE,CAAC;AACrG,CAAC;AAED,SAAS,aAAa,CAAC,CAAa,EAAE,OAAsB;IAC1D,OAAO,OAAO,KAAK,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC;AAC1E,CAAC;AAED,SAAS,WAAW,CAAC,KAAa,EAAE,OAAsB;IACxD,IAAI,OAAO,KAAK,QAAQ;QAAE,OAAO,MAAM,CAAC,KAAK,CAAC,CAAC;IAC/C,MAAM,CAAC,GAAG,IAAI,CAAC,KAAK,CAAC,KAAK,GAAG,CAAC,CAAC,CAAC;IAChC,MAAM,CAAC,GAAG,CAAC,KAAK,GAAG,CAAC,CAAC,GAAG,CAAC,CAAC;IAC1B,OAAO,GAAG,CAAC,IAAI,CAAC,EAAE,CAAC;AACrB,CAAC;AAED,0DAA0D;AAC1D,SAAS,gBAAgB,CAAC,KAAa,EAAE,OAAsB;IAC7D,OAAO,OAAO,KAAK,QAAQ,CAAC,CAAC,CAAC,KAAK,GAAG,EAAE,CAAC,CAAC,CAAC,KAAK,GAAG,CAAC,CAAC;AACvD,CAAC;AAyBD,MAAM,UAAU,cAAc,CAC5B,MAAuB,EACvB,OAA8B;IAE9B,MAAM,EAAE,OAAO,EAAE,QAAQ,EAAE,GAAG,OAAO,CAAC;IACtC,IAAI,MAAM,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACxB,MAAM,IAAI,cAAc,CAAC,WAAW,EAAE,+CAA+C,CAAC,CAAC;IACzF,CAAC;IACD,MAAM,IAAI,GAAG,QAAQ,CAAC,QAAQ,CAAC,CAAC;IAChC,MAAM,SAAS,GAAG,UAAU,CAAC,IAAI,CAAC,CAAC;IACnC,MAAM,cAAc,GAAG,IAAI,CAAC,CAAC,KAAK,WAAW,CAAC,IAAI,CAAC,CAAC,EAAE,IAAI,CAAC,CAAC,CAAC,CAAC;IAC9D,wCAAwC;IACxC,MAAM,iBAAiB,GAAG,cAAc,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,SAAS,GAAG,CAAC,CAAC;IACrE,MAAM,aAAa,GAAG,OAAO,KAAK,QAAQ,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC;IAEpD,yEAAyE;IACzE,MAAM,OAAO,GAAG,IAAI,GAAG,EAAyB,CAAC;IACjD,IAAI,SAAS,GAAG,QAAQ,CAAC;IACzB,IAAI,SAAS,GAAG,CAAC,QAAQ,CAAC;IAC1B,KAAK,MAAM,IAAI,IAAI,MAAM,EAAE,CAAC;QAC1B,MAAM,QAAQ,GAAG,QAAQ,CAAC,IAAI,CAAC,YAAY,CAAC,CAAC;QAC7C,IAAI,IAAI,CAAC,cAAc,GAAG,QAAQ;YAAE,SAAS,CAAC,2BAA2B;QACzE,MAAM,MAAM,GAAG,aAAa,CAAC,QAAQ,EAAE,OAAO,CAAC,CAAC;QAChD,SAAS,GAAG,IAAI,CAAC,GAAG,CAAC,SAAS,EAAE,MAAM,CAAC,CAAC;QACxC,SAAS,GAAG,IAAI,CAAC,GAAG,CAAC,SAAS,EAAE,MAAM,CAAC,CAAC;QACxC,IAAI,QAAQ,GAAG,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;QACzC,IAAI,CAAC,QAAQ,EAAE,CAAC;YACd,QAAQ,GAAG,EAAE,SAAS,EAAE,MAAM,EAAE,SAAS,EAAE,IAAI,CAAC,UAAU,EAAE,SAAS,EAAE,EAAE,EAAE,CAAC;YAC5E,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,OAAO,EAAE,QAAQ,CAAC,CAAC;QACtC,CAAC;QACD,QAAQ,CAAC,SAAS,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IAChC,CAAC;IACD,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,SAAS,CAAC,EAAE,CAAC;QAChC,MAAM,IAAI,cAAc,CACtB,WAAW,EACX,qDAAqD,CACtD,CAAC;IACJ,CAAC;IACD,KAAK,MAAM,QAAQ,IAAI,OAAO,CAAC,MAAM,EAAE,EAAE,CAAC;QACxC,QAAQ,CAAC,SAAS,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAC/B,CAAC,CAAC,cAAc,GAAG,CAAC,CAAC,cAAc,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,cAAc,GAAG,CAAC,CAAC,cAAc,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CACvF,CAAC;IACJ,CAAC;IAED,MAAM,QAAQ,GAAG,SAAS,GAAG,SAAS,GAAG,CAAC,CAAC;IAC3C,MAAM,OAAO,GAAa,EAAE,CAAC;IAC7B,KAAK,IAAI,CAAC,GAAG,SAAS,EAAE,CAAC,IAAI,SAAS,EAAE,CAAC,EAAE;QAAE,OAAO,CAAC,IAAI,CAAC,WAAW,CAAC,CAAC,EAAE,OAAO,CAAC,CAAC,CAAC;IAEnF,yEAAyE;IACzE,MAAM,WAAW,GAAG,gBAAgB,CAAC,SAAS,EAAE,OAAO,CAAC,CAAC;IACzD,MAAM,MAAM,GAAG,iBAAiB,GAAG,WAAW,GAAG,CAAC,CAAC;IACnD,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,MAAM,GAAG,aAAa,CAAC,CAAC;IACjD,IAAI,KAAK,GAAG,CAAC,EAAE,CAAC;QACd,MAAM,IAAI,cAAc,CACtB,gBAAgB,EAChB,4DAA4D,CAC7D,CAAC;IACJ,CAAC;IACD,MAAM,IAAI,GAAa,EAAE,CAAC;IAC1B,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,IAAI,KAAK,EAAE,CAAC,EAAE;QAAE,IAAI,CAAC,IAAI,CAAC,CAAC,GAAG,aAAa,CAAC,CAAC;IAE9D,MAAM,EAAE,GAAG,CAAC,IAAkB,EAAY,EAAE,CAAC,CAAC;QAC5C,IAAI;QACJ,OAAO,EAAE,CAAC,GAAG,OAAO,CAAC;QACrB,IAAI,EAAE,CAAC,GAAG,IAAI,CAAC;QACf,MAAM,EAAE,KAAK,CAAC,IAAI,CAAC,EAAE,MAAM,EAAE,QAAQ,EAAE,EAAE,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAChD,IAAI,CAAC,GAAG,CAAC,CAAC,GAAG,EAAE,EAAE;YACf,MAAM,SAAS,GAAG,gBAAgB,CAAC,SAAS,GAAG,CAAC,EAAE,OAAO,CAAC,GAAG,GAAG,GAAG,CAAC,CAAC;YACrE,OAAO,SAAS,IAAI,iBAAiB,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC;QACnD,CAAC,CAAC,CACH;KACF,CAAC,CAAC;IAEH,MAAM,GAAG,GAAgB;QACvB,IAAI,EAAE,EAAE,CAAC,MAAM,CAAC;QAChB,QAAQ,EAAE,EAAE,CAAC,UAAU,CAAC;QACxB,WAAW,EAAE,EAAE,CAAC,aAAa,CAAC;QAC9B,aAAa,EAAE,EAAE,CAAC,eAAe,CAAC;QAClC,SAAS,EAAE,EAAE,CAAC,WAAW,CAAC;QAC1B,WAAW,EAAE,EAAE,CAAC,aAAa,CAAC;QAC9B,kBAAkB,EAAE,EAAE,CAAC,oBAAoB,CAAC;KAC7C,CAAC;IAEF,MAAM,GAAG,GAAG,CAAC,GAAa,EAAE,CAAS,EAAE,CAAS,EAAE,CAAS,EAAE,EAAE;QAC7D,MAAM,IAAI,GAAG,GAAG,CAAC,MAAM,CAAC,CAAC,CAAE,CAAC,CAAC,CAAC,CAAC;QAC/B,IAAI,IAAI,KAAK,IAAI,IAAI,IAAI,KAAK,SAAS;YAAE,GAAG,CAAC,MAAM,CAAC,CAAC,CAAE,CAAC,CAAC,CAAC,GAAG,IAAI,GAAG,CAAC,CAAC;IACxE,CAAC,CAAC;IAEF,KAAK,MAAM,QAAQ,IAAI,OAAO,CAAC,MAAM,EAAE,EAAE,CAAC;QACxC,MAAM,CAAC,GAAG,QAAQ,CAAC,SAAS,GAAG,SAAS,CAAC;QACzC,MAAM,WAAW,GAAG,gBAAgB,CAAC,QAAQ,CAAC,SAAS,EAAE,OAAO,CAAC,CAAC;QAClE,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,KAAK,EAAE,CAAC,EAAE,EAAE,CAAC;YAC/B,MAAM,SAAS,GAAG,WAAW,GAAG,IAAI,CAAC,CAAC,CAAE,GAAG,CAAC,CAAC;YAC7C,IAAI,SAAS,GAAG,iBAAiB;gBAAE,MAAM,CAAC,gCAAgC;YAC1E,MAAM,OAAO,GAAG,aAAa,CAAC,SAAS,CAAC,CAAC;YACzC,IAAI,QAAQ,CAAC,SAAS,GAAG,OAAO;gBAAE,SAAS,CAAC,mBAAmB;YAC/D,2DAA2D;YAC3D,IAAI,KAAK,GAAyB,IAAI,CAAC;YACvC,KAAK,MAAM,IAAI,IAAI,QAAQ,CAAC,SAAS,EAAE,CAAC;gBACtC,IAAI,IAAI,CAAC,cAAc,IAAI,OAAO;oBAAE,KAAK,GAAG,IAAI,CAAC;;oBAC5C,MAAM;YACb,CAAC;YACD,MAAM,IAAI,GAAG,KAAK,EAAE,UAAU,IAAI,CAAC,CAAC;YACpC,MAAM,WAAW,GAAG,KAAK,EAAE,MAAM,KAAK,MAAM,CAAC,CAAC,CAAC,CAAC,KAAK,EAAE,WAAW,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;YAC7E,MAAM,QAAQ,GAAG,KAAK,EAAE,MAAM,KAAK,QAAQ,CAAC;YAC5C,GAAG,CAAC,GAAG,CAAC,aAAa,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,CAAC;YAChC,GAAG,CAAC,GAAG,CAAC,IAAI,EAAE,CAAC,EAAE,CAAC,EAAE,IAAI,CAAC,CAAC;YAC1B,GAAG,CAAC,GAAG,CAAC,WAAW,EAAE,CAAC,EAAE,CAAC,EAAE,WAAW,CAAC,CAAC;YACxC,GAAG,CAAC,GAAG,CAAC,QAAQ,EAAE,CAAC,EAAE,CAAC,EAAE,IAAI,GAAG,WAAW,CAAC,CAAC;YAC5C,IAAI,QAAQ,EAAE,CAAC;gBACb,GAAG,CAAC,GAAG,CAAC,WAAW,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,CAAC;gBAC9B,IAAI,IAAI,GAAG,CAAC;oBAAE,GAAG,CAAC,GAAG,CAAC,kBAAkB,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,CAAC;YACrD,CAAC;iBAAM,CAAC;gBACN,GAAG,CAAC,GAAG,CAAC,SAAS,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,CAAC;YAC9B,CAAC;QACH,CAAC;IACH,CAAC;IAED,OAAO,GAAG,CAAC;AACb,CAAC;AAED,0EAA0E;AAC1E,MAAM,UAAU,gBAAgB,CAC9B,IAAkB,EAClB,OAAiB,EACjB,IAAc,EACd,MAA2B;IAE3B,IAAI,MAAM,CAAC,MAAM,KAAK,OAAO,CAAC,MAAM,EAAE,CAAC;QACrC,MAAM,IAAI,cAAc,CAAC,OAAO,EAAE,uCAAuC,CAAC,CAAC;IAC7E,CAAC;IACD,KAAK,MAAM,GAAG,IAAI,MAAM,EAAE,CAAC;QACzB,IAAI,GAAG,CAAC,MAAM,KAAK,IAAI,CAAC,MAAM,EAAE,CAAC;YAC/B,MAAM,IAAI,cAAc,CAAC,OAAO,EAAE,uCAAuC,CAAC,CAAC;QAC7E,CAAC;IACH,CAAC;IACD,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,CAAC,GAAG,OAAO,CAAC,EAAE,IAAI,EAAE,CAAC,GAAG,IAAI,CAAC,EAAE,MAAM,EAAE,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC;AAC7F,CAAC"}
@@ -0,0 +1,10 @@
1
+ import type { Triangle } from "./types.js";
2
+ /** Cumulative -> incremental. First cell passes through; nulls propagate. */
3
+ export declare function cumulativeToIncremental(tri: Triangle): Triangle;
4
+ /** Incremental -> cumulative. A null stops accumulation for the row. */
5
+ export declare function incrementalToCumulative(tri: Triangle): Triangle;
6
+ /** Cell-wise a + b; null wherever either side is null. */
7
+ export declare function addTriangles(a: Triangle, b: Triangle): Triangle;
8
+ /** Cell-wise a - b (gross - ceded = net); null wherever either side is null. */
9
+ export declare function subtractTriangles(a: Triangle, b: Triangle): Triangle;
10
+ //# sourceMappingURL=triangleAlgebra.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"triangleAlgebra.d.ts","sourceRoot":"","sources":["../src/triangleAlgebra.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AAqC3C,6EAA6E;AAC7E,wBAAgB,uBAAuB,CAAC,GAAG,EAAE,QAAQ,GAAG,QAAQ,CAoB/D;AAED,wEAAwE;AACxE,wBAAgB,uBAAuB,CAAC,GAAG,EAAE,QAAQ,GAAG,QAAQ,CAiB/D;AAED,0DAA0D;AAC1D,wBAAgB,YAAY,CAAC,CAAC,EAAE,QAAQ,EAAE,CAAC,EAAE,QAAQ,GAAG,QAAQ,CAa/D;AAED,gFAAgF;AAChF,wBAAgB,iBAAiB,CAAC,CAAC,EAAE,QAAQ,EAAE,CAAC,EAAE,QAAQ,GAAG,QAAQ,CAapE"}
@@ -0,0 +1,97 @@
1
+ import { ReservingError } from "./types.js";
2
+ import { isNum } from "./util.js";
3
+ /**
4
+ * Triangle algebra: incremental <-> cumulative conversion and cell-wise
5
+ * arithmetic. Prerequisites for the stochastic methods (the ODP model works
6
+ * on incrementals) and for gross/ceded/net and paid+case identities.
7
+ *
8
+ * Ground truth:
9
+ * - cumulativeToIncremental: an interior null makes the increments touching
10
+ * it undefined - the hole's cell and the cell immediately after it are
11
+ * null, and increments RESUME wherever two consecutive cells are both
12
+ * observed. Nothing is ever fabricated to bridge a hole.
13
+ * - incrementalToCumulative: accumulation stops at the FIRST null in a row
14
+ * (a later observed increment has no defined cumulative base).
15
+ * - Incremental[0] = cumulative[0] (the first cell is its own increment).
16
+ */
17
+ function sameShape(a, b) {
18
+ return (a.origins.length === b.origins.length &&
19
+ a.origins.every((o, i) => o === b.origins[i]) &&
20
+ a.ages.length === b.ages.length &&
21
+ a.ages.every((v, j) => v === b.ages[j]));
22
+ }
23
+ function assertShape(a, b) {
24
+ if (!sameShape(a, b)) {
25
+ throw new ReservingError("SHAPE", "Triangle algebra requires identical origins and development ages");
26
+ }
27
+ }
28
+ /** Cumulative -> incremental. First cell passes through; nulls propagate. */
29
+ export function cumulativeToIncremental(tri) {
30
+ return {
31
+ kind: tri.kind,
32
+ origins: [...tri.origins],
33
+ ages: [...tri.ages],
34
+ values: tri.values.map((row) => {
35
+ const out = new Array(row.length).fill(null);
36
+ for (let j = 0; j < row.length; j++) {
37
+ const cur = row[j] ?? null;
38
+ if (!isNum(cur))
39
+ continue;
40
+ if (j === 0) {
41
+ out[0] = cur;
42
+ continue;
43
+ }
44
+ const prev = row[j - 1] ?? null;
45
+ out[j] = isNum(prev) ? cur - prev : null;
46
+ }
47
+ return out;
48
+ }),
49
+ };
50
+ }
51
+ /** Incremental -> cumulative. A null stops accumulation for the row. */
52
+ export function incrementalToCumulative(tri) {
53
+ return {
54
+ kind: tri.kind,
55
+ origins: [...tri.origins],
56
+ ages: [...tri.ages],
57
+ values: tri.values.map((row) => {
58
+ const out = new Array(row.length).fill(null);
59
+ let running = null;
60
+ for (let j = 0; j < row.length; j++) {
61
+ const v = row[j] ?? null;
62
+ if (!isNum(v))
63
+ break;
64
+ running = (running ?? 0) + v;
65
+ out[j] = running;
66
+ }
67
+ return out;
68
+ }),
69
+ };
70
+ }
71
+ /** Cell-wise a + b; null wherever either side is null. */
72
+ export function addTriangles(a, b) {
73
+ assertShape(a, b);
74
+ return {
75
+ kind: a.kind,
76
+ origins: [...a.origins],
77
+ ages: [...a.ages],
78
+ values: a.values.map((row, i) => row.map((v, j) => {
79
+ const w = b.values[i][j] ?? null;
80
+ return isNum(v ?? null) && isNum(w) ? v + w : null;
81
+ })),
82
+ };
83
+ }
84
+ /** Cell-wise a - b (gross - ceded = net); null wherever either side is null. */
85
+ export function subtractTriangles(a, b) {
86
+ assertShape(a, b);
87
+ return {
88
+ kind: a.kind,
89
+ origins: [...a.origins],
90
+ ages: [...a.ages],
91
+ values: a.values.map((row, i) => row.map((v, j) => {
92
+ const w = b.values[i][j] ?? null;
93
+ return isNum(v ?? null) && isNum(w) ? v - w : null;
94
+ })),
95
+ };
96
+ }
97
+ //# sourceMappingURL=triangleAlgebra.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"triangleAlgebra.js","sourceRoot":"","sources":["../src/triangleAlgebra.ts"],"names":[],"mappings":"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"}