@compstats/core 0.3.0 → 0.4.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 (42) hide show
  1. package/CHANGELOG.md +71 -0
  2. package/README.md +117 -116
  3. package/dist/3d.js +1120 -122
  4. package/dist/3d.js.map +16 -9
  5. package/dist/core/arith.d.ts.map +1 -1
  6. package/dist/core/linalg/cov.d.ts +50 -0
  7. package/dist/core/linalg/cov.d.ts.map +1 -0
  8. package/dist/core/linalg/eigen.d.ts +53 -0
  9. package/dist/core/linalg/eigen.d.ts.map +1 -0
  10. package/dist/core/linalg/lm.d.ts +78 -0
  11. package/dist/core/linalg/lm.d.ts.map +1 -0
  12. package/dist/core/linalg/lu.d.ts +154 -0
  13. package/dist/core/linalg/lu.d.ts.map +1 -0
  14. package/dist/core/linalg/matrix.d.ts +131 -0
  15. package/dist/core/linalg/matrix.d.ts.map +1 -0
  16. package/dist/core/linalg/modelMatrix.d.ts +69 -0
  17. package/dist/core/linalg/modelMatrix.d.ts.map +1 -0
  18. package/dist/core/linalg/namedVector.d.ts +37 -0
  19. package/dist/core/linalg/namedVector.d.ts.map +1 -0
  20. package/dist/core/linalg/ops.d.ts +120 -0
  21. package/dist/core/linalg/ops.d.ts.map +1 -0
  22. package/dist/core/linalg/prcomp.d.ts +66 -0
  23. package/dist/core/linalg/prcomp.d.ts.map +1 -0
  24. package/dist/core/linalg/qr.d.ts +134 -0
  25. package/dist/core/linalg/qr.d.ts.map +1 -0
  26. package/dist/core/linalg/vector.d.ts +68 -0
  27. package/dist/core/linalg/vector.d.ts.map +1 -0
  28. package/dist/core/moderation.d.ts +6 -3
  29. package/dist/core/moderation.d.ts.map +1 -1
  30. package/dist/core/ols.d.ts +4 -7
  31. package/dist/core/ols.d.ts.map +1 -1
  32. package/dist/data/moderationData.d.ts +2 -2
  33. package/dist/data/pcaDegenerate.d.ts +1 -1
  34. package/dist/index.js +1455 -863
  35. package/dist/index.js.map +16 -10
  36. package/dist/linalg.d.ts +36 -0
  37. package/dist/linalg.d.ts.map +1 -0
  38. package/dist/linalg.js +1860 -0
  39. package/dist/linalg.js.map +24 -0
  40. package/dist/plot/moderation3d.d.ts +1 -1
  41. package/dist/plot/scatter3d.d.ts +1 -1
  42. package/package.json +7 -2
package/dist/index.js CHANGED
@@ -48,6 +48,9 @@ function meanAbsoluteDeviation(values) {
48
48
  return mean(values.map((value) => Math.abs(value - center)));
49
49
  }
50
50
  function fusedMultiplyAdd(a, b, c) {
51
+ if (a * b === 0) {
52
+ return c + a * b;
53
+ }
51
54
  const [product, productError] = twoProduct(a, b);
52
55
  const [sum2, sumError] = twoSum(c, product);
53
56
  const rounded = sum2 + (sumError + productError);
@@ -630,960 +633,1549 @@ function requireNumericColumn(data, name, role) {
630
633
  }
631
634
  return column;
632
635
  }
633
- // src/core/ols.ts
634
- var DEFAULT_LEAST_SQUARES_TOLERANCE = 0.0000001;
635
- function leastSquares(design, y, options = {}) {
636
- const { weights, tolerance = DEFAULT_LEAST_SQUARES_TOLERANCE } = options;
637
- const rows = design.length;
638
- if (rows === 0) {
639
- throw new RangeError("least squares needs at least one row");
636
+ // src/core/special.ts
637
+ var LANCZOS_G = 607 / 128;
638
+ var LANCZOS_LEAD = 0.9999999999999971;
639
+ var LANCZOS_TAIL = [
640
+ 57.15623566586292,
641
+ -59.59796035547549,
642
+ 14.136097974741746,
643
+ -0.4919138160976202,
644
+ 0.00003399464998481189,
645
+ 0.00004652362892704858,
646
+ -0.00009837447530487956,
647
+ 0.0001580887032249125,
648
+ -0.00021026444172410488,
649
+ 0.00021743961811521265,
650
+ -0.0001643181065367639,
651
+ 0.00008441822398385275,
652
+ -0.000026190838401581408,
653
+ 0.0000036899182659531625
654
+ ];
655
+ var LOG_SQRT_TWO_PI = 0.5 * Math.log(2 * Math.PI);
656
+ function lanczosSeries(x) {
657
+ return LANCZOS_LEAD + sum(LANCZOS_TAIL.map((coefficient, index) => coefficient / (x + index)));
658
+ }
659
+ function logGamma(x) {
660
+ if (!(x > 0)) {
661
+ return Number.NaN;
640
662
  }
641
- const width = design[0].length;
642
- if (design.some((row) => row.length !== width)) {
643
- throw new RangeError("every design row needs the same number of columns");
663
+ const shifted = x + LANCZOS_G - 0.5;
664
+ return LOG_SQRT_TWO_PI + (x - 0.5) * Math.log(shifted) - shifted + Math.log(lanczosSeries(x));
665
+ }
666
+ function logBeta(a, b) {
667
+ if (!(a > 0) || !(b > 0)) {
668
+ return Number.NaN;
644
669
  }
645
- if (y.length !== rows) {
646
- throw new RangeError(`the response has ${y.length} values but the design has ${rows} rows`);
670
+ const shiftedSum = a + b + LANCZOS_G - 0.5;
671
+ return LOG_SQRT_TWO_PI - (LANCZOS_G - 0.5) + Math.log(lanczosSeries(a)) + Math.log(lanczosSeries(b)) - Math.log(lanczosSeries(a + b)) + (a - 0.5) * Math.log1p(-b / shiftedSum) + (b - 0.5) * Math.log1p(-a / shiftedSum) - 0.5 * Math.log(shiftedSum);
672
+ }
673
+ var FRACTION_MAX_STEPS = 400;
674
+ var FRACTION_EPSILON = 0.0000000000000003;
675
+ var FRACTION_FLOOR = 0.000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001;
676
+ function betaContinuedFraction(x, a, b) {
677
+ const total = a + b;
678
+ const aPlus = a + 1;
679
+ const aMinus = a - 1;
680
+ let c = 1;
681
+ let d = 1 - total * x / aPlus;
682
+ if (Math.abs(d) < FRACTION_FLOOR) {
683
+ d = FRACTION_FLOOR;
647
684
  }
648
- if (weights !== undefined) {
649
- if (weights.length !== rows) {
650
- throw new RangeError(`there are ${weights.length} weights but ${rows} rows`);
685
+ d = 1 / d;
686
+ let value = d;
687
+ for (let step = 1;step <= FRACTION_MAX_STEPS; step += 1) {
688
+ const twice = 2 * step;
689
+ const even = step * (b - step) * x / ((aMinus + twice) * (a + twice));
690
+ d = 1 + even * d;
691
+ if (Math.abs(d) < FRACTION_FLOOR) {
692
+ d = FRACTION_FLOOR;
651
693
  }
652
- if (weights.some((weight) => !(weight >= 0))) {
653
- throw new RangeError("weights cannot be negative or missing");
694
+ c = 1 + even / c;
695
+ if (Math.abs(c) < FRACTION_FLOOR) {
696
+ c = FRACTION_FLOOR;
654
697
  }
655
- }
656
- const scale = weights?.map((weight) => Math.sqrt(weight));
657
- const scaled = (value, row) => scale === undefined ? value : value * scale[row];
658
- const columns = Array.from({ length: width }, (_, column) => design.map((row, index) => scaled(row[column], index)));
659
- const projected = y.map(scaled);
660
- const { householders, pivot, rank } = decompose(columns, tolerance, rows);
661
- applyHouseholders(columns, householders, projected, Math.min(rank, rows - 1));
662
- const solved = backSubstitute(columns, projected, rank);
663
- const coefficients = new Array(width).fill(null);
664
- pivot.slice(0, rank).forEach((column, position) => {
665
- coefficients[column] = solved[position];
666
- });
667
- const fitted = design.map((row) => sum(zipWith(row, coefficients, (value, coefficient) => coefficient === null ? 0 : value * coefficient)));
668
- const residuals = zipWith(y, fitted, (value, fit) => value - fit);
669
- return { coefficients, fitted, residuals, rank };
670
- }
671
- function decompose(columns, tolerance, rows) {
672
- const width = columns.length;
673
- const pivot = columns.map((_, column) => column);
674
- const originalNorms = columns.map((column) => norm(column, 0) || 1);
675
- const householders = new Array(width).fill(0);
676
- let live = width;
677
- for (let step = 0;step < Math.min(rows, width); step++) {
678
- while (step < live && norm(columns[step], step) < originalNorms[step] * tolerance) {
679
- cycleToEnd(columns, pivot, originalNorms, step);
680
- live -= 1;
698
+ d = 1 / d;
699
+ value *= d * c;
700
+ const odd = -(a + step) * (total + step) * x / ((a + twice) * (aPlus + twice));
701
+ d = 1 + odd * d;
702
+ if (Math.abs(d) < FRACTION_FLOOR) {
703
+ d = FRACTION_FLOOR;
681
704
  }
682
- if (step === rows - 1) {
683
- continue;
705
+ c = 1 + odd / c;
706
+ if (Math.abs(c) < FRACTION_FLOOR) {
707
+ c = FRACTION_FLOOR;
708
+ }
709
+ d = 1 / d;
710
+ const delta = d * c;
711
+ value *= delta;
712
+ if (Math.abs(delta - 1) < FRACTION_EPSILON) {
713
+ break;
684
714
  }
685
- householders[step] = reflect(columns, step, rows);
686
715
  }
687
- return { householders, pivot, rank: Math.min(live, rows) };
716
+ return value;
688
717
  }
689
- function reflect(columns, step, rows) {
690
- const column = columns[step];
691
- const length = norm(column, step);
692
- if (length === 0) {
718
+ function incompleteBeta(x, a, b) {
719
+ if (Number.isNaN(x)) {
720
+ return Number.NaN;
721
+ }
722
+ if (x <= 0) {
693
723
  return 0;
694
724
  }
695
- const pivotNorm = column[step] < 0 ? -length : length;
696
- for (let row = step;row < rows; row++) {
697
- column[row] = column[row] / pivotNorm;
725
+ if (x >= 1) {
726
+ return 1;
698
727
  }
699
- const leading = 1 + column[step];
700
- column[step] = leading;
701
- for (let index = step + 1;index < columns.length; index++) {
702
- const other = columns[index];
703
- let inner = 0;
704
- for (let row = step;row < rows; row++) {
705
- inner += column[row] * other[row];
706
- }
707
- const factor = -inner / leading;
708
- for (let row = step;row < rows; row++) {
709
- other[row] = other[row] + factor * column[row];
728
+ return incompleteBetaSplit(x, 1 - x, a, b);
729
+ }
730
+ function incompleteBetaSplit(x, complement, a, b) {
731
+ if (Number.isNaN(x) || Number.isNaN(complement) || !(a > 0) || !(b > 0)) {
732
+ return Number.NaN;
733
+ }
734
+ if (x <= 0) {
735
+ return 0;
736
+ }
737
+ if (complement <= 0) {
738
+ return 1;
739
+ }
740
+ const logX = complement < 0.5 ? Math.log1p(-complement) : Math.log(x);
741
+ const logComplement = x < 0.5 ? Math.log1p(-x) : Math.log(complement);
742
+ const front = Math.exp(a * logX + b * logComplement - logBeta(a, b));
743
+ if (x < (a + 1) / (a + b + 2)) {
744
+ return front * betaContinuedFraction(x, a, b) / a;
745
+ }
746
+ return 1 - front * betaContinuedFraction(complement, b, a) / b;
747
+ }
748
+ var GAMMA_MAX_STEPS = 1000;
749
+ var GAMMA_EPSILON = 0.0000000000000003;
750
+ function lowerGammaSeries(a, x) {
751
+ let term = 1 / a;
752
+ let total = term;
753
+ for (let step = 1;step <= GAMMA_MAX_STEPS; step += 1) {
754
+ term *= x / (a + step);
755
+ total += term;
756
+ if (Math.abs(term) < Math.abs(total) * GAMMA_EPSILON) {
757
+ break;
710
758
  }
711
759
  }
712
- column[step] = -pivotNorm;
713
- return leading;
760
+ return total * Math.exp(-x + a * Math.log(x) - logGamma(a));
714
761
  }
715
- function applyHouseholders(columns, householders, response, count) {
716
- const rows = response.length;
717
- for (let step = 0;step < count; step++) {
718
- const leading = householders[step];
719
- if (leading === 0) {
720
- continue;
762
+ function upperGammaFraction(a, x) {
763
+ let b = x + 1 - a;
764
+ let c = 1 / FRACTION_FLOOR;
765
+ let d = 1 / b;
766
+ let value = d;
767
+ for (let step = 1;step <= GAMMA_MAX_STEPS; step += 1) {
768
+ const numerator = -step * (step - a);
769
+ b += 2;
770
+ d = numerator * d + b;
771
+ if (Math.abs(d) < FRACTION_FLOOR) {
772
+ d = FRACTION_FLOOR;
721
773
  }
722
- const column = columns[step];
723
- let inner = leading * response[step];
724
- for (let row = step + 1;row < rows; row++) {
725
- inner += column[row] * response[row];
774
+ c = b + numerator / c;
775
+ if (Math.abs(c) < FRACTION_FLOOR) {
776
+ c = FRACTION_FLOOR;
726
777
  }
727
- const factor = -inner / leading;
728
- response[step] = response[step] + factor * leading;
729
- for (let row = step + 1;row < rows; row++) {
730
- response[row] = response[row] + factor * column[row];
778
+ d = 1 / d;
779
+ const delta = d * c;
780
+ value *= delta;
781
+ if (Math.abs(delta - 1) < GAMMA_EPSILON) {
782
+ break;
731
783
  }
732
784
  }
785
+ return value * Math.exp(-x + a * Math.log(x) - logGamma(a));
733
786
  }
734
- function backSubstitute(columns, response, rank) {
735
- const solved = new Array(rank).fill(0);
736
- for (let row = rank - 1;row >= 0; row--) {
737
- let value = response[row];
738
- for (let column = row + 1;column < rank; column++) {
739
- value -= columns[column][row] * solved[column];
740
- }
741
- solved[row] = value / columns[row][row];
787
+ function upperGamma(a, x) {
788
+ if (x <= 0) {
789
+ return 1;
742
790
  }
743
- return solved;
744
- }
745
- function cycleToEnd(columns, pivot, originalNorms, step) {
746
- moveToEnd(columns, step);
747
- moveToEnd(pivot, step);
748
- moveToEnd(originalNorms, step);
791
+ if (!Number.isFinite(x)) {
792
+ return 0;
793
+ }
794
+ return x < a + 1 ? 1 - lowerGammaSeries(a, x) : upperGammaFraction(a, x);
749
795
  }
750
- function moveToEnd(track, from) {
751
- const [moved] = track.splice(from, 1);
752
- track.push(moved);
796
+ function normalCdf(z) {
797
+ if (Number.isNaN(z)) {
798
+ return Number.NaN;
799
+ }
800
+ const lower = 0.5 * upperGamma(0.5, 0.5 * z * z);
801
+ return z > 0 ? 1 - lower : lower;
753
802
  }
754
- function norm(column, from) {
755
- return Math.hypot(...column.slice(from));
803
+ var BELOW_ONE = 1 - Number.EPSILON / 2;
804
+ var INVERSE_MAX_STEPS = 200;
805
+ function inverseGuess(p, a, b) {
806
+ if (a >= 1 && b >= 1) {
807
+ const tail = p < 0.5 ? p : 1 - p;
808
+ const t = Math.sqrt(-2 * Math.log(tail));
809
+ const normal = (p < 0.5 ? -1 : 1) * ((2.30753 + t * 0.27061) / (1 + t * (0.99229 + t * 0.04481)) - t);
810
+ const scale = (normal * normal - 3) / 6;
811
+ const harmonic = 2 / (1 / (2 * a - 1) + 1 / (2 * b - 1));
812
+ const w = normal * Math.sqrt(scale + harmonic) / harmonic - (1 / (2 * b - 1) - 1 / (2 * a - 1)) * (scale + 5 / 6 - 2 / (3 * harmonic));
813
+ return a / (a + b * Math.exp(2 * w));
814
+ }
815
+ const lower = Math.exp(a * Math.log(a / (a + b))) / a;
816
+ const upper = Math.exp(b * Math.log(b / (a + b))) / b;
817
+ const total = lower + upper;
818
+ if (p < lower / total) {
819
+ return Math.pow(a * total * p, 1 / a);
820
+ }
821
+ return 1 - Math.pow(b * total * (1 - p), 1 / b);
756
822
  }
757
-
758
- // src/core/moderation.ts
759
- var GRID_STEPS = 15;
760
- function moderationSurface(data, options) {
761
- const { outcome, iv, mod, interaction = true, controls = [] } = options;
762
- if (iv === mod) {
763
- throw new RangeError(`\`iv\` and \`mod\` must name different columns, both name "${iv}"`);
823
+ function inverseIncompleteBeta(p, a, b) {
824
+ if (Number.isNaN(p) || !(a > 0) || !(b > 0)) {
825
+ return Number.NaN;
764
826
  }
765
- const named = new Set([outcome, iv, mod]);
766
- controls.forEach((control) => {
767
- if (named.has(control)) {
768
- throw new RangeError(`control "${control}" already names the outcome, the iv, the mod, ` + "or another control");
769
- }
770
- named.add(control);
771
- });
772
- const rows = frameRows(data);
773
- const y = requireNumericColumn(data, outcome, "outcome");
774
- const ivColumn = requireNumericColumn(data, iv, "iv");
775
- const modColumn = requireNumericColumn(data, mod, "mod");
776
- const controlColumns = controls.map((control) => requireNumericColumn(data, control, "controls"));
777
- const modelColumns = [y, ivColumn, modColumn, ...controlColumns];
778
- const completeRows = y.map((_, row) => row).filter((row) => modelColumns.every((column) => Number.isFinite(column[row])));
779
- if (completeRows.length === 0) {
780
- throw new RangeError("the model has no complete rows: every row is missing a value in " + "the outcome, the IV, the moderator, or a control");
827
+ if (p <= 0) {
828
+ return 0;
781
829
  }
782
- const designColumns = [
783
- { name: "(Intercept)", values: new Array(rows).fill(1) },
784
- { name: iv, values: ivColumn },
785
- { name: mod, values: modColumn },
786
- ...controls.map((control, index) => ({
787
- name: control,
788
- values: controlColumns[index]
789
- })),
790
- ...interaction ? [
791
- {
792
- name: `${iv}:${mod}`,
793
- values: zipWith(ivColumn, modColumn, (a, b) => a * b)
794
- }
795
- ] : []
796
- ];
797
- const design = completeRows.map((row) => designColumns.map((column) => column.values[row]));
798
- const fit = leastSquares(design, completeRows.map((row) => y[row]));
799
- const coefficients = designColumns.map((column, index) => ({
800
- name: column.name,
801
- value: fit.coefficients[index] ?? null
802
- }));
803
- const fitted = new Array(rows).fill(Number.NaN);
804
- const residuals = new Array(rows).fill(Number.NaN);
805
- completeRows.forEach((row, survivor) => {
806
- fitted[row] = fit.fitted[survivor];
807
- residuals[row] = fit.residuals[survivor];
808
- });
809
- const ivValues = rSeq(...extent(ivColumn), GRID_STEPS);
810
- const modValues = rSeq(...extent(modColumn), GRID_STEPS);
811
- const holds = Object.fromEntries(controls.map((control, index) => [
812
- control,
813
- mean(controlColumns[index].filter(Number.isFinite))
814
- ]));
815
- const weights = coefficients.map((term) => term.value ?? 0);
816
- const predictions = modValues.flatMap((modValue) => ivValues.map((ivValue) => {
817
- const gridRow = [
818
- 1,
819
- ivValue,
820
- modValue,
821
- ...controls.map((control) => holds[control]),
822
- ...interaction ? [ivValue * modValue] : []
823
- ];
824
- return sum(zipWith(gridRow, weights, (value, weight) => value * weight));
825
- }));
826
- const [dataLow, dataHigh] = extent(y);
827
- const [surfaceLow, surfaceHigh] = extent(predictions);
828
- return {
829
- coefficients,
830
- fitted,
831
- residuals,
832
- ivValues,
833
- modValues,
834
- predictions,
835
- zlim: [Math.min(dataLow, surfaceLow), Math.max(dataHigh, surfaceHigh)],
836
- holds
837
- };
830
+ if (p >= 1) {
831
+ return 1;
832
+ }
833
+ const logBetaValue = logBeta(a, b);
834
+ let lower = 0;
835
+ let upper = 1;
836
+ let x = inverseGuess(p, a, b);
837
+ if (!(x > 0) || !(x < 1)) {
838
+ x = 0.5;
839
+ }
840
+ for (let step = 0;step < INVERSE_MAX_STEPS; step += 1) {
841
+ const residual = incompleteBeta(x, a, b) - p;
842
+ if (residual < 0) {
843
+ lower = x;
844
+ } else {
845
+ upper = x;
846
+ }
847
+ const density = Math.exp((a - 1) * Math.log(x) + (b - 1) * Math.log1p(-x) - logBetaValue);
848
+ let next = density > 0 && Number.isFinite(density) ? x - residual / density : Number.NaN;
849
+ if (!(next > lower) || !(next < upper)) {
850
+ next = 0.5 * (lower + upper);
851
+ }
852
+ if (next === x) {
853
+ break;
854
+ }
855
+ const moved = Math.abs(next - x);
856
+ x = next;
857
+ if (moved <= Number.EPSILON * x) {
858
+ break;
859
+ }
860
+ }
861
+ return Math.min(x, BELOW_ONE);
838
862
  }
839
- function rSeq(from, to, length) {
840
- if (from === to) {
841
- return new Array(length).fill(from);
863
+
864
+ // src/core/tdist.ts
865
+ function isNonCentral(ncp) {
866
+ return ncp !== undefined && ncp !== 0;
867
+ }
868
+ function isBadArgument(value, df, ncp) {
869
+ return Number.isNaN(value) || !(df > 0) || ncp !== undefined && Number.isNaN(ncp);
870
+ }
871
+ function dt(x, df, ncp) {
872
+ if (isBadArgument(x, df, ncp)) {
873
+ return Number.NaN;
842
874
  }
843
- const by = (to - from) / (length - 1);
844
- return Array.from({ length }, (_, index) => index === 0 ? from : index === length - 1 ? to : from + index * by);
875
+ return isNonCentral(ncp) ? nonCentralDensity(x, df, ncp) : centralDensity(x, df);
845
876
  }
846
- // src/core/regression.ts
847
- function completePointRows(points) {
848
- return points.flatMap((point, row) => Number.isFinite(point.x) && Number.isFinite(point.y) ? [row] : []);
877
+ function pt(x, df, ncp) {
878
+ if (isBadArgument(x, df, ncp)) {
879
+ return Number.NaN;
880
+ }
881
+ return isNonCentral(ncp) ? nonCentralProbability(x, df, ncp) : centralProbability(x, df);
849
882
  }
850
- function linearRegression(points) {
851
- const rows = completePointRows(points);
852
- if (rows.length === 0) {
853
- return null;
883
+ function qt(p, df, ncp) {
884
+ if (isBadArgument(p, df, ncp) || p < 0 || p > 1) {
885
+ return Number.NaN;
854
886
  }
855
- const xs = rows.map((row) => points[row].x);
856
- const ys = rows.map((row) => points[row].y);
857
- const meanX = mean(xs);
858
- const meanY = mean(ys);
859
- const devX = xs.map((x) => x - meanX);
860
- const devY = ys.map((y) => y - meanY);
861
- const sumSquaresX = sum(devX.map((d) => d * d));
862
- const sumSquaresY = sum(devY.map((d) => d * d));
863
- const sumProducts = sum(zipWith(devX, devY, (dx, dy) => dx * dy));
864
- const slope = sumSquaresX === 0 ? null : sumProducts / sumSquaresX;
865
- const intercept = slope === null ? meanY : meanY - slope * meanX;
866
- const correlation = sumSquaresX === 0 || sumSquaresY === 0 ? null : sumProducts / Math.sqrt(sumSquaresX * sumSquaresY);
867
- const fittedComplete = xs.map((x) => slope === null ? intercept : intercept + slope * x);
868
- const ssr = sum(fittedComplete.map((f) => (f - meanY) * (f - meanY)));
869
- const sse = sum(zipWith(ys, fittedComplete, (y, f) => (y - f) * (y - f)));
870
- const sst = sumSquaresY;
871
- const rSquared = sst === 0 ? null : ssr / sst;
872
- const fitted = new Array(points.length).fill(Number.NaN);
873
- rows.forEach((row, survivor) => {
874
- fitted[row] = fittedComplete[survivor];
875
- });
876
- return {
877
- intercept,
878
- slope,
879
- correlation,
880
- ssr,
881
- sse,
882
- sst,
883
- rSquared,
884
- fitted
885
- };
887
+ return isNonCentral(ncp) ? nonCentralQuantile(p, df, ncp) : centralQuantile(p, df);
886
888
  }
887
- // src/core/matrix.ts
888
- var SMALLEST_NORMAL = 0.000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000022250738585072014;
889
- function determinant(matrix) {
890
- return determinantOf(factorize(matrix));
889
+ function centralDensity(x, df) {
890
+ if (!Number.isFinite(x)) {
891
+ return 0;
892
+ }
893
+ const logDensity = -0.5 * Math.log(df) - logBeta(0.5, df / 2) - (df + 1) / 2 * Math.log1p(x * x / df);
894
+ return Math.exp(logDensity);
891
895
  }
892
- function invertMatrix(matrix) {
893
- const factorization = factorize(matrix);
894
- const determinant2 = determinantOf(factorization);
895
- const zeroPivot = zeroPivotOf(factorization);
896
- if (zeroPivot !== null) {
897
- return {
898
- determinant: determinant2,
899
- inverse: null,
900
- singularity: "exact",
901
- rcond: 0,
902
- zeroPivot
903
- };
896
+ function centralProbability(x, df) {
897
+ if (x === 0) {
898
+ return 0.5;
904
899
  }
905
- const inverse = solveForIdentity(factorization);
906
- const rcond = 1 / (oneNorm(matrix) * oneNorm(inverse));
907
- if (rcond < Number.EPSILON) {
908
- return {
909
- determinant: determinant2,
910
- inverse: null,
911
- singularity: "computational",
912
- rcond,
913
- zeroPivot: null
914
- };
900
+ if (x === Number.POSITIVE_INFINITY) {
901
+ return 1;
915
902
  }
916
- return {
917
- determinant: determinant2,
918
- inverse,
919
- singularity: null,
920
- rcond,
921
- zeroPivot: null
922
- };
903
+ if (x === Number.NEGATIVE_INFINITY) {
904
+ return 0;
905
+ }
906
+ const tail = upperTail(Math.abs(x), df);
907
+ return x < 0 ? tail : 1 - tail;
923
908
  }
924
- function factorize(matrix) {
925
- const interchanged = Math.abs(matrix.y1) > Math.abs(matrix.x1);
926
- const leading = interchanged ? matrix.y1 : matrix.x1;
927
- const upperRight = interchanged ? matrix.y2 : matrix.x2;
928
- const below = interchanged ? matrix.x1 : matrix.y1;
929
- const belowRight = interchanged ? matrix.x2 : matrix.y2;
930
- const multiplier = Math.abs(leading) >= SMALLEST_NORMAL ? below * (1 / leading) : below / leading;
931
- const trailing = fusedMultiplyAdd(-multiplier, upperRight, belowRight);
932
- return {
933
- interchanged,
934
- multiplier,
935
- pivots: [leading, trailing],
936
- upperRight
937
- };
909
+ function upperTail(t, df) {
910
+ const squared = t * t;
911
+ if (!Number.isFinite(squared)) {
912
+ return 0;
913
+ }
914
+ const total = df + squared;
915
+ return 0.5 * incompleteBetaSplit(df / total, squared / total, df / 2, 0.5);
938
916
  }
939
- function determinantOf(factorization) {
940
- const { interchanged, pivots } = factorization;
941
- if (zeroPivotOf(factorization) !== null) {
917
+ var POLISH_MAX_STEPS = 4;
918
+ function centralQuantile(p, df) {
919
+ if (p === 0.5) {
942
920
  return 0;
943
921
  }
944
- const modulus = sum(pivots.map((pivot) => Math.log(Math.abs(pivot))));
945
- const sign = pivots.reduce((carried, pivot) => pivot < 0 ? -carried : carried, interchanged ? -1 : 1);
946
- return sign * Math.exp(modulus);
922
+ if (p <= 0) {
923
+ return Number.NEGATIVE_INFINITY;
924
+ }
925
+ if (p >= 1) {
926
+ return Number.POSITIVE_INFINITY;
927
+ }
928
+ const tail = p < 0.5 ? p : 1 - p;
929
+ const sign = p < 0.5 ? -1 : 1;
930
+ const twoSided = 2 * tail;
931
+ let squared;
932
+ if (twoSided > 0.5) {
933
+ const near = inverseIncompleteBeta(1 - twoSided, 0.5, df / 2);
934
+ squared = df * near / (1 - near);
935
+ } else {
936
+ const far = inverseIncompleteBeta(twoSided, df / 2, 0.5);
937
+ squared = df * (1 - far) / far;
938
+ }
939
+ return sign * polish(Math.sqrt(squared), tail, df);
940
+ }
941
+ function polish(start, tail, df) {
942
+ let t = start;
943
+ for (let step = 0;step < POLISH_MAX_STEPS; step += 1) {
944
+ const density = centralDensity(t, df);
945
+ if (!(density > 0) || !Number.isFinite(t)) {
946
+ break;
947
+ }
948
+ const move = (upperTail(t, df) - tail) / density;
949
+ const next = t + move;
950
+ if (!(next > 0) || !Number.isFinite(next) || Math.abs(move) > 0.25 * t) {
951
+ break;
952
+ }
953
+ if (next === t) {
954
+ break;
955
+ }
956
+ t = next;
957
+ if (Math.abs(move) <= Number.EPSILON * t) {
958
+ break;
959
+ }
960
+ }
961
+ return t;
962
+ }
963
+ var SERIES_MAX_STEPS = 1000;
964
+ var SERIES_ERROR_MAX = 0.000000000001;
965
+ var SERIES_NCP_LIMIT_SQUARED = 2 * Math.LN2 * 1022;
966
+ var SERIES_DF_LIMIT = 400000;
967
+ var SQRT_TWO_OVER_PI = Math.sqrt(2 / Math.PI);
968
+ function nonCentralProbability(x, df, ncp) {
969
+ if (x === Number.POSITIVE_INFINITY) {
970
+ return 1;
971
+ }
972
+ if (x === Number.NEGATIVE_INFINITY) {
973
+ return 0;
974
+ }
975
+ const reflected = x < 0;
976
+ const t = reflected ? -x : x;
977
+ const delta = reflected ? -ncp : ncp;
978
+ const lower = df > SERIES_DF_LIMIT || delta * delta > SERIES_NCP_LIMIT_SQUARED ? normalApproximation(t, df, delta) : lenthSeries(t, df, delta);
979
+ return reflected ? 1 - lower : lower;
980
+ }
981
+ function normalApproximation(t, df, delta) {
982
+ const shrink = 1 / (4 * df);
983
+ const spread = Math.sqrt(1 + t * t * 2 * shrink);
984
+ return normalCdf((t * (1 - shrink) - delta) / spread);
985
+ }
986
+ function lenthSeries(t, df, delta) {
987
+ const squared = t * t;
988
+ const total = df + squared;
989
+ const x = squared / total;
990
+ const complement = df / total;
991
+ let sum2 = 0;
992
+ if (x > 0) {
993
+ const lambda = delta * delta;
994
+ let oddWeight = 0.5 * Math.exp(-0.5 * lambda);
995
+ let evenWeight = SQRT_TWO_OVER_PI * oddWeight * delta;
996
+ let remaining = 0.5 - oddWeight;
997
+ if (remaining < 0.0000001) {
998
+ remaining = -0.5 * Math.expm1(-0.5 * lambda);
999
+ }
1000
+ let a = 0.5;
1001
+ const b = 0.5 * df;
1002
+ const powered = Math.pow(complement, b);
1003
+ const logBetaValue = logBeta(0.5, b);
1004
+ let oddTerm = incompleteBetaSplit(x, complement, a, b);
1005
+ let oddStep = 2 * powered * Math.exp(a * Math.log(x) - logBetaValue);
1006
+ let evenTerm = 1 - powered;
1007
+ let evenStep = b * x * powered;
1008
+ sum2 = oddWeight * oddTerm + evenWeight * evenTerm;
1009
+ for (let step = 1;step <= SERIES_MAX_STEPS; step += 1) {
1010
+ a += 1;
1011
+ oddTerm -= oddStep;
1012
+ evenTerm -= evenStep;
1013
+ oddStep *= x * (a + b - 1) / a;
1014
+ evenStep *= x * (a + b - 0.5) / (a + 0.5);
1015
+ oddWeight *= lambda / (2 * step);
1016
+ evenWeight *= lambda / (2 * step + 1);
1017
+ remaining -= oddWeight;
1018
+ if (remaining <= 0) {
1019
+ break;
1020
+ }
1021
+ sum2 += oddWeight * oddTerm + evenWeight * evenTerm;
1022
+ if (Math.abs(2 * remaining * (oddTerm - oddStep)) < SERIES_ERROR_MAX) {
1023
+ break;
1024
+ }
1025
+ }
1026
+ }
1027
+ return Math.min(Math.max(sum2 + normalCdf(-delta), 0), 1);
1028
+ }
1029
+ function nonCentralDensity(x, df, ncp) {
1030
+ if (!Number.isFinite(x)) {
1031
+ return 0;
1032
+ }
1033
+ if (Math.abs(x) > Math.sqrt(df * Number.EPSILON)) {
1034
+ const stepped = x * Math.sqrt((df + 2) / df);
1035
+ const difference = nonCentralProbability(stepped, df + 2, ncp) - nonCentralProbability(x, df, ncp);
1036
+ return df / Math.abs(x) * Math.abs(difference);
1037
+ }
1038
+ return Math.exp(-0.5 * Math.log(df) - logBeta(0.5, df / 2) - 0.5 * ncp * ncp);
947
1039
  }
948
- function zeroPivotOf(factorization) {
949
- const [leading, trailing] = factorization.pivots;
950
- if (leading === 0) {
951
- return 1;
1040
+ var QUANTILE_MAX_STEPS = 200;
1041
+ function nonCentralQuantile(p, df, ncp) {
1042
+ if (p <= 0) {
1043
+ return Number.NEGATIVE_INFINITY;
952
1044
  }
953
- return trailing === 0 ? 2 : null;
954
- }
955
- function solveForIdentity(factorization) {
956
- const { interchanged } = factorization;
957
- const [firstTop, firstBottom] = interchanged ? [0, 1] : [1, 0];
958
- const [secondTop, secondBottom] = interchanged ? [1, 0] : [0, 1];
959
- const [x1, y1] = solveColumn(factorization, firstTop, firstBottom);
960
- const [x2, y2] = solveColumn(factorization, secondTop, secondBottom);
961
- return {
962
- x1: withoutNegativeZero(x1),
963
- y1: withoutNegativeZero(y1),
964
- x2: withoutNegativeZero(x2),
965
- y2: withoutNegativeZero(y2)
966
- };
967
- }
968
- function solveColumn(factorization, top, bottom) {
969
- const { multiplier, pivots, upperRight } = factorization;
970
- const lower = fusedMultiplyAdd(-multiplier, top, bottom) / pivots[1];
971
- const upper = fusedMultiplyAdd(-upperRight, lower, top) / pivots[0];
972
- return [upper, lower];
973
- }
974
- function oneNorm(matrix) {
975
- return Math.max(Math.abs(matrix.x1) + Math.abs(matrix.y1), Math.abs(matrix.x2) + Math.abs(matrix.y2));
976
- }
977
- // src/core/pca.ts
978
- function principalComponents(points) {
979
- const rows = completePointRows(points);
980
- if (rows.length === 0) {
981
- return null;
1045
+ if (p >= 1) {
1046
+ return Number.POSITIVE_INFINITY;
982
1047
  }
983
- const complete = rows.map((row) => points[row]);
984
- const centerX = mean(complete.map((point) => point.x));
985
- const centerY = mean(complete.map((point) => point.y));
986
- const devX = complete.map((point) => point.x - centerX);
987
- const devY = complete.map((point) => point.y - centerY);
988
- const divisor = Math.max(1, complete.length - 1);
989
- const varX = sum(devX.map((d) => d * d)) / divisor;
990
- const varY = sum(devY.map((d) => d * d)) / divisor;
991
- const covariance = sum(zipWith(devX, devY, (dx, dy) => dx * dy)) / divisor;
992
- const [first, second] = componentsOf(varX, varY, covariance);
993
- const scores = points.map(() => ({ x: Number.NaN, y: Number.NaN }));
994
- rows.forEach((row, survivor) => {
995
- const dx = devX[survivor];
996
- const dy = devY[survivor];
997
- scores[row] = {
998
- x: dx * first.loadings[0] + dy * first.loadings[1],
999
- y: dx * second.loadings[0] + dy * second.loadings[1]
1000
- };
1001
- });
1002
- return {
1003
- sdev: [sdevOf(first.variance), sdevOf(second.variance)],
1004
- rotation: [first.loadings, second.loadings],
1005
- center: { x: centerX, y: centerY },
1006
- scores
1007
- };
1008
- }
1009
- function componentsOf(varX, varY, covariance) {
1010
- if (covariance === 0) {
1011
- const alongX = { variance: varX, loadings: [1, 0] };
1012
- const alongY = { variance: varY, loadings: [0, 1] };
1013
- return varX >= varY ? [alongX, alongY] : [alongY, alongX];
1048
+ let upper = Math.max(1, ncp);
1049
+ while (Number.isFinite(upper) && nonCentralProbability(upper, df, ncp) < p) {
1050
+ upper *= 2;
1014
1051
  }
1015
- const middle = (varX + varY) / 2;
1016
- const spread = Math.hypot((varX - varY) / 2, covariance);
1017
- const larger = middle + spread;
1018
- const smaller = middle - spread;
1019
- const fromRowX = larger - varX;
1020
- const fromRowY = larger - varY;
1021
- const direction = fromRowX >= fromRowY ? [covariance, fromRowX] : [fromRowY, covariance];
1022
- const leading = signed(unit(direction));
1023
- const trailing = signed([negated(leading[1]), leading[0]]);
1024
- return [
1025
- { variance: larger, loadings: leading },
1026
- { variance: smaller, loadings: trailing }
1027
- ];
1028
- }
1029
- function unit(vector) {
1030
- const length = Math.hypot(vector[0], vector[1]);
1031
- return [vector[0] / length, vector[1] / length];
1032
- }
1033
- function signed(vector) {
1034
- const dominant = Math.abs(vector[0]) >= Math.abs(vector[1]) ? vector[0] : vector[1];
1035
- return dominant >= 0 ? vector : [negated(vector[0]), negated(vector[1])];
1052
+ let lower = Math.min(-1, -ncp);
1053
+ while (Number.isFinite(lower) && nonCentralProbability(lower, df, ncp) > p) {
1054
+ lower *= 2;
1055
+ }
1056
+ let t = 0.5 * (lower + upper);
1057
+ for (let step = 0;step < QUANTILE_MAX_STEPS; step += 1) {
1058
+ const residual = nonCentralProbability(t, df, ncp) - p;
1059
+ if (residual < 0) {
1060
+ lower = t;
1061
+ } else {
1062
+ upper = t;
1063
+ }
1064
+ const density = nonCentralDensity(t, df, ncp);
1065
+ let next = density > 0 && Number.isFinite(density) ? t - residual / density : Number.NaN;
1066
+ if (!(next > lower) || !(next < upper)) {
1067
+ next = 0.5 * (lower + upper);
1068
+ }
1069
+ if (next === t) {
1070
+ break;
1071
+ }
1072
+ const moved = Math.abs(next - t);
1073
+ t = next;
1074
+ if (moved <= Number.EPSILON * Math.abs(t)) {
1075
+ break;
1076
+ }
1077
+ }
1078
+ return t;
1036
1079
  }
1037
- function negated(value) {
1038
- return withoutNegativeZero(-value);
1080
+
1081
+ // src/core/linalg/matrix.ts
1082
+ function matrix(values, options) {
1083
+ const { byrow = false, dimnames } = options;
1084
+ const [nrow, ncol] = extents(values.length, options);
1085
+ const dense = Float64Array.from(values);
1086
+ const data = new Float64Array(nrow * ncol);
1087
+ if (dense.length === 1 && data.length > 1) {
1088
+ data.fill(dense[0]);
1089
+ } else if (byrow) {
1090
+ dense.forEach((value, index) => {
1091
+ const i = Math.floor(index / ncol);
1092
+ const j = index % ncol;
1093
+ data[j * nrow + i] = value;
1094
+ });
1095
+ } else {
1096
+ data.set(dense);
1097
+ }
1098
+ return make(nrow, ncol, data, dimnames ?? null);
1039
1099
  }
1040
- function sdevOf(variance) {
1041
- return Math.sqrt(Math.max(0, variance));
1100
+ function extents(length, { nrow, ncol }) {
1101
+ if (nrow === undefined && ncol === undefined) {
1102
+ throw new RangeError("matrix() needs nrow or ncol");
1103
+ }
1104
+ if (nrow !== undefined) {
1105
+ requireExtent(nrow, "nrow");
1106
+ }
1107
+ if (ncol !== undefined) {
1108
+ requireExtent(ncol, "ncol");
1109
+ }
1110
+ const scalar = length === 1;
1111
+ if (nrow !== undefined && ncol !== undefined) {
1112
+ if (!scalar && nrow * ncol !== length) {
1113
+ throw new RangeError(length > nrow * ncol ? "data is too long" : `data length [${length}] is not nrow * ncol [${nrow} * ${ncol}]`);
1114
+ }
1115
+ return [nrow, ncol];
1116
+ }
1117
+ const given = nrow ?? ncol;
1118
+ const name = nrow !== undefined ? "rows" : "columns";
1119
+ if (given === 0) {
1120
+ if (length !== 0) {
1121
+ throw new RangeError("data is too long");
1122
+ }
1123
+ return [0, 0];
1124
+ }
1125
+ const other = scalar ? 1 : length / given;
1126
+ if (!scalar && length % given !== 0) {
1127
+ throw new RangeError(`data length [${length}] is not a multiple of the number of ${name} [${given}]`);
1128
+ }
1129
+ return nrow !== undefined ? [nrow, other] : [other, given];
1042
1130
  }
1043
- // src/core/logit.ts
1044
- var DEFAULT_LOGIT_EPSILON = 0.00000001;
1045
- var DEFAULT_LOGIT_MAX_ITERATIONS = 25;
1046
- var LINK_THRESHOLD = 30;
1047
- var BOUND = 10 * Number.EPSILON;
1048
- function logisticRegression(points, options = {}) {
1049
- const {
1050
- epsilon = DEFAULT_LOGIT_EPSILON,
1051
- maxIterations = DEFAULT_LOGIT_MAX_ITERATIONS
1052
- } = options;
1053
- const rows = completePointRows(points);
1054
- if (rows.length === 0) {
1055
- return null;
1131
+ function requireExtent(value, name) {
1132
+ if (!Number.isSafeInteger(value) || value < 0) {
1133
+ throw new RangeError(`${name} must be a non-negative integer, got ${value}`);
1056
1134
  }
1057
- const complete = rows.map((row) => points[row]);
1058
- if (complete.some((point) => point.y !== 0 && point.y !== 1)) {
1059
- throw new RangeError("every outcome must be 0 or 1");
1135
+ }
1136
+ function make(nrow, ncol, data, dimnames) {
1137
+ if (data.length !== nrow * ncol) {
1138
+ throw new RangeError(`data length [${data.length}] is not nrow * ncol [${nrow} * ${ncol}]`);
1060
1139
  }
1061
- const outcomes = complete.map((point) => point.y);
1062
- const design = complete.map((point) => [1, point.x]);
1063
- let fitted = outcomes.map((outcome) => (outcome + 0.5) / 2);
1064
- let predictors = fitted.map(logit);
1065
- let previousDeviance = totalDeviance(outcomes, fitted);
1066
- let deviance = previousDeviance;
1067
- let coefficients = [Number.NaN, null];
1068
- let rank = 0;
1069
- let iterations = 0;
1070
- let converged = false;
1071
- for (let iteration = 1;iteration <= maxIterations; iteration++) {
1072
- iterations = iteration;
1073
- const working = predictors.map((predictor, row) => {
1074
- const probability = fitted[row];
1075
- const slope = linkSlope(predictor);
1076
- return {
1077
- weight: slope * slope / (probability * (1 - probability)),
1078
- response: predictor + (outcomes[row] - probability) / slope
1079
- };
1080
- });
1081
- const step = leastSquares(design, working.map((row) => row.response), {
1082
- weights: working.map((row) => row.weight),
1083
- tolerance: Math.min(0.0000001, epsilon / 1000)
1084
- });
1085
- if (step.coefficients.some((coefficient) => coefficient !== null && !Number.isFinite(coefficient))) {
1086
- break;
1140
+ if (dimnames !== null) {
1141
+ const [rows, columns] = dimnames;
1142
+ if (rows !== null && rows.length !== nrow) {
1143
+ throw new RangeError(`length of dimnames [1] (${rows.length}) not equal to array extent (${nrow})`);
1087
1144
  }
1088
- coefficients = step.coefficients;
1089
- rank = step.rank;
1090
- predictors = design.map((row) => sum(zipWith(row, coefficients, (value, coefficient) => coefficient === null ? 0 : value * coefficient)));
1091
- fitted = predictors.map(linkInverse);
1092
- deviance = totalDeviance(outcomes, fitted);
1093
- if (Math.abs(deviance - previousDeviance) / (0.1 + Math.abs(deviance)) < epsilon) {
1094
- converged = true;
1095
- break;
1145
+ if (columns !== null && columns.length !== ncol) {
1146
+ throw new RangeError(`length of dimnames [2] (${columns.length}) not equal to array extent (${ncol})`);
1096
1147
  }
1097
- previousDeviance = deviance;
1148
+ dimnames = rows === null && columns === null ? null : [rows === null ? null : [...rows], columns === null ? null : [...columns]];
1098
1149
  }
1099
- const wholeMean = mean(outcomes);
1100
- const nullDeviance = totalDeviance(outcomes, outcomes.map(() => wholeMean));
1101
- const paddedFitted = new Array(points.length).fill(Number.NaN);
1102
- const paddedPredictors = new Array(points.length).fill(Number.NaN);
1103
- rows.forEach((row, survivor) => {
1104
- paddedFitted[row] = fitted[survivor];
1105
- paddedPredictors[row] = predictors[survivor];
1106
- });
1107
- return {
1108
- intercept: coefficients[0] ?? Number.NaN,
1109
- slope: coefficients[1] ?? null,
1110
- fitted: paddedFitted,
1111
- linearPredictors: paddedPredictors,
1112
- deviance,
1113
- nullDeviance,
1114
- aic: deviance + 2 * rank,
1115
- rank,
1116
- iterations,
1117
- converged,
1118
- saturated: fitted.some((probability) => probability > 1 - BOUND || probability < BOUND)
1119
- };
1150
+ return { nrow, ncol, data, dimnames };
1120
1151
  }
1121
- function predictLogit(fit, x) {
1122
- return linkInverse(fit.intercept + (fit.slope ?? 0) * x);
1152
+ function fromRows(rows) {
1153
+ const nrow = rows.length;
1154
+ const first = rows[0];
1155
+ if (first === undefined || first.length === 0) {
1156
+ throw new RangeError("fromRows() needs at least one row with one value");
1157
+ }
1158
+ const ncol = first.length;
1159
+ const ragged = rows.findIndex((row) => row.length !== ncol);
1160
+ if (ragged !== -1) {
1161
+ throw new RangeError(`every row needs ${ncol} values; row ${ragged} has ${rows[ragged].length}`);
1162
+ }
1163
+ const data = new Float64Array(nrow * ncol);
1164
+ rows.forEach((row, i) => {
1165
+ Float64Array.from(row).forEach((value, j) => {
1166
+ data[j * nrow + i] = value;
1167
+ });
1168
+ });
1169
+ return make(nrow, ncol, data, null);
1170
+ }
1171
+ function fromColumns(columns) {
1172
+ const ncol = columns.length;
1173
+ const first = columns[0];
1174
+ if (first === undefined || first.length === 0) {
1175
+ throw new RangeError("fromColumns() needs at least one column with one value");
1176
+ }
1177
+ const nrow = first.length;
1178
+ const ragged = columns.findIndex((column) => column.length !== nrow);
1179
+ if (ragged !== -1) {
1180
+ throw new RangeError(`every column needs ${nrow} values; column ${ragged} has ${columns[ragged].length}`);
1181
+ }
1182
+ const data = new Float64Array(nrow * ncol);
1183
+ columns.forEach((column, j) => {
1184
+ data.set(column, j * nrow);
1185
+ });
1186
+ return make(nrow, ncol, data, null);
1123
1187
  }
1124
- function logit(probability) {
1125
- return Math.log(probability / (1 - probability));
1188
+ function at(m, i, j) {
1189
+ if (!Number.isInteger(i) || i < 0 || i >= m.nrow) {
1190
+ throw new RangeError(`row index ${i} is outside 0..${m.nrow - 1}`);
1191
+ }
1192
+ if (!Number.isInteger(j) || j < 0 || j >= m.ncol) {
1193
+ throw new RangeError(`column index ${j} is outside 0..${m.ncol - 1}`);
1194
+ }
1195
+ return m.data[j * m.nrow + i];
1126
1196
  }
1127
- function linkInverse(predictor) {
1128
- const odds = predictor < -LINK_THRESHOLD ? Number.EPSILON : predictor > LINK_THRESHOLD ? 1 / Number.EPSILON : Math.exp(predictor);
1129
- return odds / (1 + odds);
1197
+ function row(m, i) {
1198
+ if (!Number.isInteger(i) || i < 0 || i >= m.nrow) {
1199
+ throw new RangeError(`row index ${i} is outside 0..${m.nrow - 1}`);
1200
+ }
1201
+ return Array.from({ length: m.ncol }, (_, j) => m.data[j * m.nrow + i]);
1130
1202
  }
1131
- function linkSlope(predictor) {
1132
- if (predictor > LINK_THRESHOLD || predictor < -LINK_THRESHOLD) {
1133
- return Number.EPSILON;
1203
+ function column(m, j) {
1204
+ if (!Number.isInteger(j) || j < 0 || j >= m.ncol) {
1205
+ throw new RangeError(`column index ${j} is outside 0..${m.ncol - 1}`);
1134
1206
  }
1135
- const odds = Math.exp(predictor);
1136
- return odds / ((1 + odds) * (1 + odds));
1207
+ return Array.from(m.data.subarray(j * m.nrow, (j + 1) * m.nrow));
1137
1208
  }
1138
- function totalDeviance(outcomes, probabilities) {
1139
- return sum(zipWith(outcomes, probabilities, (outcome, probability) => 2 * (surprise(outcome, probability) + surprise(1 - outcome, 1 - probability))));
1209
+ function toRows(m) {
1210
+ return Array.from({ length: m.nrow }, (_, i) => row(m, i));
1140
1211
  }
1141
- function surprise(outcome, probability) {
1142
- return outcome !== 0 ? outcome * Math.log(outcome / probability) : 0;
1212
+ function toColumns(m) {
1213
+ return Array.from({ length: m.ncol }, (_, j) => column(m, j));
1143
1214
  }
1144
- // src/core/special.ts
1145
- var LANCZOS_G = 607 / 128;
1146
- var LANCZOS_LEAD = 0.9999999999999971;
1147
- var LANCZOS_TAIL = [
1148
- 57.15623566586292,
1149
- -59.59796035547549,
1150
- 14.136097974741746,
1151
- -0.4919138160976202,
1152
- 0.00003399464998481189,
1153
- 0.00004652362892704858,
1154
- -0.00009837447530487956,
1155
- 0.0001580887032249125,
1156
- -0.00021026444172410488,
1157
- 0.00021743961811521265,
1158
- -0.0001643181065367639,
1159
- 0.00008441822398385275,
1160
- -0.000026190838401581408,
1161
- 0.0000036899182659531625
1162
- ];
1163
- var LOG_SQRT_TWO_PI = 0.5 * Math.log(2 * Math.PI);
1164
- function lanczosSeries(x) {
1165
- return LANCZOS_LEAD + sum(LANCZOS_TAIL.map((coefficient, index) => coefficient / (x + index)));
1215
+ function fromFrame(data, columns) {
1216
+ const nrow = frameRows(data);
1217
+ const names = columns ?? numericColumns(data);
1218
+ const buffer = new Float64Array(nrow * names.length);
1219
+ names.forEach((name, j) => {
1220
+ buffer.set(requireNumericColumn(data, name, "columns"), j * nrow);
1221
+ });
1222
+ return make(nrow, names.length, buffer, names.length === 0 ? null : [null, [...names]]);
1223
+ }
1224
+
1225
+ // src/core/linalg/modelMatrix.ts
1226
+ function modelMatrix(data, spec) {
1227
+ const { outcome, intercept = true } = spec;
1228
+ const rowCount = frameRows(data);
1229
+ const terms = orderTerms(spec.terms);
1230
+ const columns = new Map;
1231
+ const read = (name, role) => {
1232
+ const held = columns.get(name);
1233
+ if (held !== undefined) {
1234
+ return held;
1235
+ }
1236
+ const column2 = requireNumericColumn(data, name, role);
1237
+ columns.set(name, column2);
1238
+ return column2;
1239
+ };
1240
+ if (outcome !== undefined) {
1241
+ read(outcome, "outcome");
1242
+ }
1243
+ terms.forEach((factors) => {
1244
+ factors.forEach((name) => read(name, "terms"));
1245
+ });
1246
+ const involved = [...columns.values()];
1247
+ const rows = Array.from({ length: rowCount }, (_, row2) => row2).filter((row2) => involved.every((column2) => Number.isFinite(column2[row2])));
1248
+ const termColumns = terms.map((factors) => rows.map((row2) => factors.reduce((product, name) => product * columns.get(name)[row2], 1)));
1249
+ const design = intercept ? [rows.map(() => 1), ...termColumns] : termColumns;
1250
+ const names = [
1251
+ ...intercept ? ["(Intercept)"] : [],
1252
+ ...terms.map((factors) => factors.join(":"))
1253
+ ];
1254
+ const assign = [
1255
+ ...intercept ? [0] : [],
1256
+ ...terms.map((_, index) => index + 1)
1257
+ ];
1258
+ const n = rows.length;
1259
+ const p = design.length;
1260
+ const buffer = new Float64Array(n * p);
1261
+ design.forEach((column2, j) => {
1262
+ buffer.set(column2, j * n);
1263
+ });
1264
+ return {
1265
+ matrix: make(n, p, buffer, p === 0 ? null : [null, names]),
1266
+ rows,
1267
+ assign,
1268
+ termLabels: names.slice(intercept ? 1 : 0)
1269
+ };
1166
1270
  }
1167
- function logGamma(x) {
1168
- if (!(x > 0)) {
1169
- return Number.NaN;
1271
+ function orderTerms(terms) {
1272
+ const seen = new Set;
1273
+ const unique = [];
1274
+ terms.forEach((term) => {
1275
+ const factors = typeof term === "string" ? [term] : term;
1276
+ if (factors.length === 0) {
1277
+ throw new RangeError("an interaction term needs at least one column name");
1278
+ }
1279
+ const key = [...factors].sort().join("\x00");
1280
+ if (!seen.has(key)) {
1281
+ seen.add(key);
1282
+ unique.push(factors);
1283
+ }
1284
+ });
1285
+ return unique.map((factors, index) => ({ factors, index })).sort((a, b) => a.factors.length - b.factors.length || a.index - b.index).map(({ factors }) => factors);
1286
+ }
1287
+
1288
+ // src/core/linalg/namedVector.ts
1289
+ function namedVector(names, values) {
1290
+ if (names.length !== values.length) {
1291
+ throw new RangeError(`a named vector needs one name per value: ${names.length} names, ${values.length} values`);
1170
1292
  }
1171
- const shifted = x + LANCZOS_G - 0.5;
1172
- return LOG_SQRT_TWO_PI + (x - 0.5) * Math.log(shifted) - shifted + Math.log(lanczosSeries(x));
1293
+ return { names: [...names], values: [...values] };
1173
1294
  }
1174
- function logBeta(a, b) {
1175
- if (!(a > 0) || !(b > 0)) {
1176
- return Number.NaN;
1295
+ function lookup(v, name) {
1296
+ const index = v.names.indexOf(name);
1297
+ return index === -1 ? undefined : v.values[index];
1298
+ }
1299
+
1300
+ // src/core/linalg/ops.ts
1301
+ function t(m) {
1302
+ const { nrow, ncol } = m;
1303
+ const data = new Float64Array(nrow * ncol);
1304
+ for (let j = 0;j < ncol; j++) {
1305
+ for (let i = 0;i < nrow; i++) {
1306
+ data[i * ncol + j] = m.data[j * nrow + i];
1307
+ }
1177
1308
  }
1178
- const shiftedSum = a + b + LANCZOS_G - 0.5;
1179
- return LOG_SQRT_TWO_PI - (LANCZOS_G - 0.5) + Math.log(lanczosSeries(a)) + Math.log(lanczosSeries(b)) - Math.log(lanczosSeries(a + b)) + (a - 0.5) * Math.log1p(-b / shiftedSum) + (b - 0.5) * Math.log1p(-a / shiftedSum) - 0.5 * Math.log(shiftedSum);
1309
+ const dimnames = m.dimnames === null ? null : [m.dimnames[1], m.dimnames[0]];
1310
+ return make(ncol, nrow, data, dimnames);
1180
1311
  }
1181
- var FRACTION_MAX_STEPS = 400;
1182
- var FRACTION_EPSILON = 0.0000000000000003;
1183
- var FRACTION_FLOOR = 0.000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001;
1184
- function betaContinuedFraction(x, a, b) {
1185
- const total = a + b;
1186
- const aPlus = a + 1;
1187
- const aMinus = a - 1;
1188
- let c = 1;
1189
- let d = 1 - total * x / aPlus;
1190
- if (Math.abs(d) < FRACTION_FLOOR) {
1191
- d = FRACTION_FLOOR;
1312
+ var transpose = t;
1313
+ function matmul(x, y) {
1314
+ const [left, right] = conformProduct(x, y);
1315
+ if (left.ncol !== right.nrow) {
1316
+ throw new RangeError(`non-conformable arguments: ${left.nrow} x ${left.ncol} %*% ${right.nrow} x ${right.ncol}`);
1192
1317
  }
1193
- d = 1 / d;
1194
- let value = d;
1195
- for (let step = 1;step <= FRACTION_MAX_STEPS; step += 1) {
1196
- const twice = 2 * step;
1197
- const even = step * (b - step) * x / ((aMinus + twice) * (a + twice));
1198
- d = 1 + even * d;
1199
- if (Math.abs(d) < FRACTION_FLOOR) {
1200
- d = FRACTION_FLOOR;
1318
+ const data = product(left, right);
1319
+ return make(left.nrow, right.ncol, data, productDimnames(left, right));
1320
+ }
1321
+ function conformProduct(x, y) {
1322
+ if (isMatrix(x)) {
1323
+ if (isMatrix(y)) {
1324
+ return [x, y];
1201
1325
  }
1202
- c = 1 + even / c;
1203
- if (Math.abs(c) < FRACTION_FLOOR) {
1204
- c = FRACTION_FLOOR;
1326
+ return [x, y.length === x.ncol ? asColumn(y) : asRow(y)];
1327
+ }
1328
+ if (isMatrix(y)) {
1329
+ return [x.length === y.nrow ? asRow(x) : asColumn(x), y];
1330
+ }
1331
+ return [asRow(x), x.length === 1 ? asRow(y) : asColumn(y)];
1332
+ }
1333
+ function crossprod(x, y = x) {
1334
+ const left = asColumn(x);
1335
+ const right = isMatrix(y) ? y : y.length === left.nrow ? asColumn(y) : asRow(y);
1336
+ if (left.nrow !== right.nrow) {
1337
+ throw new RangeError(`non-conformable arguments: crossprod of ${left.nrow} x ${left.ncol} and ${right.nrow} x ${right.ncol}`);
1338
+ }
1339
+ return matmul(t(left), right);
1340
+ }
1341
+ function tcrossprod(x, y = x) {
1342
+ const bothVectors = !isMatrix(x) && !isMatrix(y);
1343
+ const left = isMatrix(x) ? x : isMatrix(y) && y.ncol === x.length ? asRow(x) : asColumn(x);
1344
+ const right = isMatrix(y) ? y : !bothVectors && left.nrow === 1 ? asRow(y) : asColumn(y);
1345
+ if (left.ncol !== right.ncol) {
1346
+ throw new RangeError(`non-conformable arguments: tcrossprod of ${left.nrow} x ${left.ncol} and ${right.nrow} x ${right.ncol}`);
1347
+ }
1348
+ return matmul(left, t(right));
1349
+ }
1350
+ function product(x, y) {
1351
+ const { nrow, ncol: inner } = x;
1352
+ const { ncol } = y;
1353
+ const data = new Float64Array(nrow * ncol);
1354
+ for (let j = 0;j < ncol; j++) {
1355
+ for (let k = 0;k < inner; k++) {
1356
+ const factor = y.data[j * y.nrow + k];
1357
+ for (let i = 0;i < nrow; i++) {
1358
+ data[j * nrow + i] = fusedMultiplyAdd(x.data[k * nrow + i], factor, data[j * nrow + i]);
1359
+ }
1205
1360
  }
1206
- d = 1 / d;
1207
- value *= d * c;
1208
- const odd = -(a + step) * (total + step) * x / ((a + twice) * (aPlus + twice));
1209
- d = 1 + odd * d;
1210
- if (Math.abs(d) < FRACTION_FLOOR) {
1211
- d = FRACTION_FLOOR;
1361
+ }
1362
+ return data;
1363
+ }
1364
+ function productDimnames(x, y) {
1365
+ const rows = x.dimnames?.[0] ?? null;
1366
+ const columns = y.dimnames?.[1] ?? null;
1367
+ return rows === null && columns === null ? null : [rows, columns];
1368
+ }
1369
+ function asColumn(value) {
1370
+ if (isMatrix(value)) {
1371
+ return value;
1372
+ }
1373
+ return make(value.length, 1, Float64Array.from(value), null);
1374
+ }
1375
+ function asRow(value) {
1376
+ return make(1, value.length, Float64Array.from(value), null);
1377
+ }
1378
+ function isMatrix(value) {
1379
+ if (Array.isArray(value)) {
1380
+ return false;
1381
+ }
1382
+ if (typeof value === "object" && value !== null && "nrow" in value && "ncol" in value && "data" in value) {
1383
+ return true;
1384
+ }
1385
+ throw new TypeError("expected a Matrix or an array of numbers");
1386
+ }
1387
+ function cbind(...parts) {
1388
+ const matrices = parts.map(asColumn);
1389
+ const first = matrices[0];
1390
+ if (first === undefined) {
1391
+ throw new RangeError("cbind() needs at least one argument");
1392
+ }
1393
+ const nrow = first.nrow;
1394
+ matrices.forEach((m, index) => {
1395
+ if (m.nrow !== nrow) {
1396
+ throw new RangeError(isMatrix(parts[index]) ? `number of rows of matrices must match (see arg ${index + 1})` : `number of rows of result is not a multiple of vector length (arg ${index + 1})`);
1212
1397
  }
1213
- c = 1 + odd / c;
1214
- if (Math.abs(c) < FRACTION_FLOOR) {
1215
- c = FRACTION_FLOOR;
1398
+ });
1399
+ const ncol = matrices.reduce((total, m) => total + m.ncol, 0);
1400
+ const data = new Float64Array(nrow * ncol);
1401
+ let offset = 0;
1402
+ matrices.forEach((m) => {
1403
+ data.set(m.data, offset);
1404
+ offset += m.data.length;
1405
+ });
1406
+ const rows = matrices.find((m) => m.dimnames?.[0])?.dimnames?.[0] ?? null;
1407
+ const columns = boundNames(matrices.map((m) => ({ names: m.dimnames?.[1] ?? null, count: m.ncol })));
1408
+ return make(nrow, ncol, data, rows === null && columns === null ? null : [rows, columns]);
1409
+ }
1410
+ function rbind(...parts) {
1411
+ if (parts.length === 0) {
1412
+ throw new RangeError("rbind() needs at least one argument");
1413
+ }
1414
+ const transposed = parts.map((part) => isMatrix(part) ? t(part) : part);
1415
+ try {
1416
+ return t(cbind(...transposed));
1417
+ } catch (error) {
1418
+ if (error instanceof RangeError) {
1419
+ throw new RangeError(error.message.replace("number of rows", "number of columns"));
1216
1420
  }
1217
- d = 1 / d;
1218
- const delta = d * c;
1219
- value *= delta;
1220
- if (Math.abs(delta - 1) < FRACTION_EPSILON) {
1221
- break;
1421
+ throw error;
1422
+ }
1423
+ }
1424
+ function boundNames(parts) {
1425
+ if (parts.every((part) => part.names === null)) {
1426
+ return null;
1427
+ }
1428
+ return parts.flatMap((part) => part.names ?? new Array(part.count).fill(""));
1429
+ }
1430
+ function diag(arg) {
1431
+ if (typeof arg === "number") {
1432
+ return identity(arg);
1433
+ }
1434
+ if (Array.isArray(arg)) {
1435
+ const values = arg;
1436
+ const n2 = values.length;
1437
+ const data = new Float64Array(n2 * n2);
1438
+ values.forEach((value, i) => {
1439
+ data[i * n2 + i] = value;
1440
+ });
1441
+ return make(n2, n2, data, null);
1442
+ }
1443
+ const m = arg;
1444
+ const n = Math.min(m.nrow, m.ncol);
1445
+ return Array.from({ length: n }, (_, i) => m.data[i * m.nrow + i]);
1446
+ }
1447
+ function identity(order) {
1448
+ if (!Number.isInteger(order) || order < 0) {
1449
+ throw new RangeError(`order must be a non-negative integer, got ${order}`);
1450
+ }
1451
+ const data = new Float64Array(order * order);
1452
+ for (let i = 0;i < order; i++) {
1453
+ data[i * order + i] = 1;
1454
+ }
1455
+ return make(order, order, data, null);
1456
+ }
1457
+
1458
+ // src/core/linalg/qr.ts
1459
+ var DEFAULT_QR_TOLERANCE = 0.0000001;
1460
+ function qr(x, options = {}) {
1461
+ const { tolerance = DEFAULT_QR_TOLERANCE } = options;
1462
+ if (!(tolerance >= 0)) {
1463
+ throw new RangeError(`tolerance must be a non-negative number, got ${tolerance}`);
1464
+ }
1465
+ if (!isMatrix(x)) {
1466
+ throw new TypeError("expected a Matrix");
1467
+ }
1468
+ if (!x.data.every(Number.isFinite)) {
1469
+ throw new RangeError("NA/NaN/Inf in foreign function call (arg 1)");
1470
+ }
1471
+ const { nrow, ncol } = x;
1472
+ const columns = Array.from({ length: ncol }, (_, j) => Array.from(x.data.subarray(j * nrow, (j + 1) * nrow)));
1473
+ const { householders, pivot, rank } = decompose(columns, tolerance, nrow);
1474
+ const data = new Float64Array(nrow * ncol);
1475
+ columns.forEach((column2, j) => {
1476
+ data.set(column2, j * nrow);
1477
+ });
1478
+ const rows = x.dimnames?.[0] ?? null;
1479
+ const names = x.dimnames?.[1] ?? null;
1480
+ const dimnames = rows === null && names === null ? null : [rows, names === null ? null : pivot.map((from) => names[from])];
1481
+ return { qr: make(nrow, ncol, data, dimnames), qraux: householders, pivot, rank };
1482
+ }
1483
+ function qrCoef(q, y) {
1484
+ if (isMatrix(y)) {
1485
+ requireRows(q, y.nrow);
1486
+ const width = q.qr.ncol;
1487
+ const data = new Float64Array(width * y.ncol);
1488
+ for (let j = 0;j < y.ncol; j++) {
1489
+ const solved = coefficientsOf(q, Array.from(y.data.subarray(j * y.nrow, (j + 1) * y.nrow)));
1490
+ solved.forEach((value, i) => {
1491
+ data[j * width + i] = value ?? Number.NaN;
1492
+ });
1222
1493
  }
1494
+ return make(width, y.ncol, data, readerDimnames(originalColumnNames(q), y));
1223
1495
  }
1224
- return value;
1496
+ requireRows(q, y.length);
1497
+ return coefficientsOf(q, y);
1225
1498
  }
1226
- function incompleteBeta(x, a, b) {
1227
- if (Number.isNaN(x)) {
1228
- return Number.NaN;
1499
+ function coefficientsOf(q, y) {
1500
+ const qty = transformed(q, y, true);
1501
+ const solved = backSubstitute(q.qr, qty, q.rank);
1502
+ const coefficients = new Array(q.qr.ncol).fill(null);
1503
+ q.pivot.slice(0, q.rank).forEach((column2, position) => {
1504
+ coefficients[column2] = solved[position];
1505
+ });
1506
+ return coefficients;
1507
+ }
1508
+ function originalColumnNames(q) {
1509
+ const pivoted = q.qr.dimnames?.[1] ?? null;
1510
+ if (pivoted === null) {
1511
+ return null;
1229
1512
  }
1230
- if (x <= 0) {
1231
- return 0;
1513
+ const names = new Array(pivoted.length);
1514
+ q.pivot.forEach((original, position) => {
1515
+ names[original] = pivoted[position];
1516
+ });
1517
+ return names;
1518
+ }
1519
+ function qrFitted(q, y) {
1520
+ return perColumn(q, y, (column2) => transformed(q, transformed(q, column2, true).map((value, index) => index < q.rank ? value : 0), false));
1521
+ }
1522
+ function qrResid(q, y) {
1523
+ return perColumn(q, y, (column2) => transformed(q, transformed(q, column2, true).map((value, index) => index < q.rank ? 0 : value), false));
1524
+ }
1525
+ function qrQty(q, y) {
1526
+ return perColumn(q, y, (column2) => transformed(q, column2, true));
1527
+ }
1528
+ function qrQy(q, y) {
1529
+ return perColumn(q, y, (column2) => transformed(q, column2, false));
1530
+ }
1531
+ function perColumn(q, y, read) {
1532
+ if (isMatrix(y)) {
1533
+ requireRows(q, y.nrow);
1534
+ const data = new Float64Array(y.nrow * y.ncol);
1535
+ for (let j = 0;j < y.ncol; j++) {
1536
+ data.set(read(Array.from(y.data.subarray(j * y.nrow, (j + 1) * y.nrow))), j * y.nrow);
1537
+ }
1538
+ return make(y.nrow, y.ncol, data, readerDimnames(null, y));
1232
1539
  }
1233
- if (x >= 1) {
1234
- return 1;
1540
+ requireRows(q, y.length);
1541
+ return read(y);
1542
+ }
1543
+ function readerDimnames(rows, y) {
1544
+ const columns = y.dimnames?.[1] ?? null;
1545
+ return rows === null && columns === null ? null : [rows, columns];
1546
+ }
1547
+ function transformed(q, y, transpose2) {
1548
+ const result = [...y];
1549
+ const count = reflectorCount(q);
1550
+ if (transpose2) {
1551
+ for (let step = 0;step < count; step++) {
1552
+ applyReflector(q, step, result);
1553
+ }
1554
+ } else {
1555
+ for (let step = count - 1;step >= 0; step--) {
1556
+ applyReflector(q, step, result);
1557
+ }
1235
1558
  }
1236
- return incompleteBetaSplit(x, 1 - x, a, b);
1559
+ return result;
1237
1560
  }
1238
- function incompleteBetaSplit(x, complement, a, b) {
1239
- if (Number.isNaN(x) || Number.isNaN(complement) || !(a > 0) || !(b > 0)) {
1240
- return Number.NaN;
1561
+ function qrQ(q) {
1562
+ const { nrow, ncol } = q.qr;
1563
+ const width = Math.min(nrow, ncol);
1564
+ const data = new Float64Array(nrow * width);
1565
+ for (let j = 0;j < width; j++) {
1566
+ const unit = new Array(nrow).fill(0);
1567
+ unit[j] = 1;
1568
+ data.set(transformed(q, unit, false), j * nrow);
1569
+ }
1570
+ return make(nrow, width, data, null);
1571
+ }
1572
+ function qrR(q) {
1573
+ const { nrow, ncol } = q.qr;
1574
+ const height = Math.min(nrow, ncol);
1575
+ const data = new Float64Array(height * ncol);
1576
+ for (let j = 0;j < ncol; j++) {
1577
+ for (let i = 0;i <= Math.min(j, height - 1); i++) {
1578
+ data[j * height + i] = q.qr.data[j * nrow + i];
1579
+ }
1241
1580
  }
1242
- if (x <= 0) {
1243
- return 0;
1581
+ const rows = q.qr.dimnames?.[0]?.slice(0, height) ?? null;
1582
+ const columns = q.qr.dimnames?.[1] ?? null;
1583
+ return make(height, ncol, data, rows === null && columns === null ? null : [rows, columns]);
1584
+ }
1585
+ function requireRows(q, rows) {
1586
+ if (rows !== q.qr.nrow) {
1587
+ throw new RangeError("'qr' and 'y' must have the same number of rows");
1244
1588
  }
1245
- if (complement <= 0) {
1246
- return 1;
1589
+ }
1590
+ function reflectorCount(q) {
1591
+ return Math.min(q.rank, q.qr.nrow - 1);
1592
+ }
1593
+ function applyReflector(q, step, vector) {
1594
+ const leading = q.qraux[step];
1595
+ if (leading === 0) {
1596
+ return;
1247
1597
  }
1248
- const logX = complement < 0.5 ? Math.log1p(-complement) : Math.log(x);
1249
- const logComplement = x < 0.5 ? Math.log1p(-x) : Math.log(complement);
1250
- const front = Math.exp(a * logX + b * logComplement - logBeta(a, b));
1251
- if (x < (a + 1) / (a + b + 2)) {
1252
- return front * betaContinuedFraction(x, a, b) / a;
1598
+ const { nrow } = q.qr;
1599
+ const column2 = q.qr.data.subarray(step * nrow, (step + 1) * nrow);
1600
+ let inner = leading * vector[step];
1601
+ for (let row2 = step + 1;row2 < nrow; row2++) {
1602
+ inner = fusedMultiplyAdd(column2[row2], vector[row2], inner);
1603
+ }
1604
+ const factor = -inner / leading;
1605
+ vector[step] = fusedMultiplyAdd(factor, leading, vector[step]);
1606
+ for (let row2 = step + 1;row2 < nrow; row2++) {
1607
+ vector[row2] = fusedMultiplyAdd(factor, column2[row2], vector[row2]);
1253
1608
  }
1254
- return 1 - front * betaContinuedFraction(complement, b, a) / b;
1255
1609
  }
1256
- var GAMMA_MAX_STEPS = 1000;
1257
- var GAMMA_EPSILON = 0.0000000000000003;
1258
- function lowerGammaSeries(a, x) {
1259
- let term = 1 / a;
1260
- let total = term;
1261
- for (let step = 1;step <= GAMMA_MAX_STEPS; step += 1) {
1262
- term *= x / (a + step);
1263
- total += term;
1264
- if (Math.abs(term) < Math.abs(total) * GAMMA_EPSILON) {
1265
- break;
1610
+ function decompose(columns, tolerance, rows) {
1611
+ const width = columns.length;
1612
+ const pivot = columns.map((_, column2) => column2);
1613
+ const qraux = columns.map((column2) => norm(column2, 0));
1614
+ const lastNorms = [...qraux];
1615
+ const originalNorms = qraux.map((value) => value || 1);
1616
+ let k = width + 1;
1617
+ for (let step = 0;step < Math.min(rows, width); step++) {
1618
+ while (step + 1 < k && qraux[step] < originalNorms[step] * tolerance) {
1619
+ moveToEnd(columns, step);
1620
+ moveToEnd(pivot, step);
1621
+ moveToEnd(qraux, step);
1622
+ moveToEnd(lastNorms, step);
1623
+ moveToEnd(originalNorms, step);
1624
+ k -= 1;
1625
+ }
1626
+ if (step === rows - 1) {
1627
+ continue;
1266
1628
  }
1629
+ reflect(columns, qraux, lastNorms, step, rows);
1267
1630
  }
1268
- return total * Math.exp(-x + a * Math.log(x) - logGamma(a));
1631
+ return { householders: qraux, pivot, rank: Math.min(k - 1, rows) };
1269
1632
  }
1270
- function upperGammaFraction(a, x) {
1271
- let b = x + 1 - a;
1272
- let c = 1 / FRACTION_FLOOR;
1273
- let d = 1 / b;
1274
- let value = d;
1275
- for (let step = 1;step <= GAMMA_MAX_STEPS; step += 1) {
1276
- const numerator = -step * (step - a);
1277
- b += 2;
1278
- d = numerator * d + b;
1279
- if (Math.abs(d) < FRACTION_FLOOR) {
1280
- d = FRACTION_FLOOR;
1633
+ function reflect(columns, qraux, lastNorms, step, rows) {
1634
+ const column2 = columns[step];
1635
+ const length = norm(column2, step);
1636
+ if (length === 0) {
1637
+ return;
1638
+ }
1639
+ const pivotNorm = column2[step] < 0 ? -length : length;
1640
+ const reciprocal = 1 / pivotNorm;
1641
+ for (let row2 = step;row2 < rows; row2++) {
1642
+ column2[row2] = column2[row2] * reciprocal;
1643
+ }
1644
+ const leading = 1 + column2[step];
1645
+ column2[step] = leading;
1646
+ for (let index = step + 1;index < columns.length; index++) {
1647
+ const other = columns[index];
1648
+ let inner = 0;
1649
+ for (let row2 = step;row2 < rows; row2++) {
1650
+ inner = fusedMultiplyAdd(column2[row2], other[row2], inner);
1281
1651
  }
1282
- c = b + numerator / c;
1283
- if (Math.abs(c) < FRACTION_FLOOR) {
1284
- c = FRACTION_FLOOR;
1652
+ const factor = -inner / leading;
1653
+ for (let row2 = step;row2 < rows; row2++) {
1654
+ other[row2] = fusedMultiplyAdd(factor, column2[row2], other[row2]);
1285
1655
  }
1286
- d = 1 / d;
1287
- const delta = d * c;
1288
- value *= delta;
1289
- if (Math.abs(delta - 1) < GAMMA_EPSILON) {
1290
- break;
1656
+ const running = qraux[index];
1657
+ if (running !== 0) {
1658
+ const ratio = Math.abs(other[step]) / running;
1659
+ const remaining = Math.max(1 - ratio * ratio, 0);
1660
+ if (Math.abs(remaining) < 0.000001) {
1661
+ qraux[index] = norm(other, step + 1);
1662
+ lastNorms[index] = qraux[index];
1663
+ } else {
1664
+ qraux[index] = running * Math.sqrt(remaining);
1665
+ }
1291
1666
  }
1292
1667
  }
1293
- return value * Math.exp(-x + a * Math.log(x) - logGamma(a));
1668
+ qraux[step] = leading;
1669
+ column2[step] = -pivotNorm;
1670
+ }
1671
+ function backSubstitute(compact, response, rank) {
1672
+ const { nrow } = compact;
1673
+ const entry = (i, j) => compact.data[j * nrow + i];
1674
+ const solved = response.slice(0, rank);
1675
+ for (let column2 = rank - 1;column2 >= 0; column2--) {
1676
+ const value = solved[column2] / entry(column2, column2);
1677
+ solved[column2] = value;
1678
+ for (let row2 = 0;row2 < column2; row2++) {
1679
+ solved[row2] = fusedMultiplyAdd(-value, entry(row2, column2), solved[row2]);
1680
+ }
1681
+ }
1682
+ return solved;
1294
1683
  }
1295
- function upperGamma(a, x) {
1296
- if (x <= 0) {
1297
- return 1;
1684
+ function moveToEnd(track, from) {
1685
+ const [moved] = track.splice(from, 1);
1686
+ track.push(moved);
1687
+ }
1688
+ function norm(column2, from) {
1689
+ let squares = 0;
1690
+ for (let row2 = from;row2 < column2.length; row2++) {
1691
+ const value = column2[row2];
1692
+ squares = fusedMultiplyAdd(value, value, squares);
1298
1693
  }
1299
- if (!Number.isFinite(x)) {
1300
- return 0;
1694
+ return Math.sqrt(squares);
1695
+ }
1696
+
1697
+ // src/core/linalg/lm.ts
1698
+ function lm(data, options) {
1699
+ const { outcome, intercept = true, tolerance = DEFAULT_QR_TOLERANCE } = options;
1700
+ const design = modelMatrix(data, options);
1701
+ const { rows } = design;
1702
+ const n = rows.length;
1703
+ if (n === 0) {
1704
+ throw new RangeError("0 (non-NA) cases");
1705
+ }
1706
+ const outcomeColumn = data[outcome];
1707
+ const y = rows.map((row2) => outcomeColumn[row2]);
1708
+ const factored = qr(design.matrix, { tolerance });
1709
+ const coefficients = qrCoef(factored, y);
1710
+ const residuals = qrResid(factored, y);
1711
+ const fitted = zipWith(y, residuals, (value, residual) => value - residual);
1712
+ const names = design.matrix.dimnames?.[1] ?? [];
1713
+ const { rank } = factored;
1714
+ const dfResidual = n - rank;
1715
+ const interceptCount = intercept ? 1 : 0;
1716
+ const rss = sum(residuals.map((r) => r * r));
1717
+ const centered = intercept ? mean(fitted) : 0;
1718
+ const mss = sum(fitted.map((f) => (f - centered) * (f - centered)));
1719
+ const resvar = rss / dfResidual;
1720
+ const numdf = rank - interceptCount;
1721
+ const rSquared = numdf > 0 ? mss / (mss + rss) : 0;
1722
+ const adjRSquared = numdf > 0 ? 1 - (1 - rSquared) * ((n - interceptCount) / dfResidual) : 0;
1723
+ const standardErrors = standardErrorsOf(factored, resvar, names.length);
1724
+ const tValues = zipWith(coefficients, standardErrors, (b, se) => b === null || se === null ? null : b / se);
1725
+ const pValues = tValues.map((tv) => tv === null ? null : 2 * pt(-Math.abs(tv), dfResidual));
1726
+ return {
1727
+ coefficients: namedVector(names, coefficients),
1728
+ standardErrors: namedVector(names, standardErrors),
1729
+ tValues: namedVector(names, tValues),
1730
+ pValues: namedVector(names, pValues),
1731
+ fitted: padded(fitted, rows, outcomeColumn.length),
1732
+ residuals: padded(residuals, rows, outcomeColumn.length),
1733
+ rank,
1734
+ dfResidual,
1735
+ rSquared,
1736
+ adjRSquared,
1737
+ sigma: Math.sqrt(resvar),
1738
+ fStatistic: numdf > 0 ? { value: mss / numdf / resvar, numdf, dendf: dfResidual } : null,
1739
+ rows,
1740
+ termLabels: design.termLabels
1741
+ };
1742
+ }
1743
+ function standardErrorsOf(factored, resvar, width) {
1744
+ const { rank, pivot } = factored;
1745
+ const { nrow } = factored.qr;
1746
+ const r = (i, j) => factored.qr.data[j * nrow + i];
1747
+ const inverse = Array.from({ length: rank }, (_, k) => {
1748
+ const x = new Array(rank).fill(0);
1749
+ x[k] = 1 / r(k, k);
1750
+ for (let i = k - 1;i >= 0; i--) {
1751
+ let total = 0;
1752
+ for (let j = i + 1;j <= k; j++) {
1753
+ total += r(i, j) * x[j];
1754
+ }
1755
+ x[i] = -total / r(i, i);
1756
+ }
1757
+ return x;
1758
+ });
1759
+ const diagonal = Array.from({ length: rank }, (_, i) => sum(inverse.map((columnOfInverse) => columnOfInverse[i] ** 2)));
1760
+ const errors = new Array(width).fill(null);
1761
+ pivot.slice(0, rank).forEach((original, position) => {
1762
+ errors[original] = Math.sqrt(diagonal[position] * resvar);
1763
+ });
1764
+ return errors;
1765
+ }
1766
+ function padded(values, rows, length) {
1767
+ const out = new Array(length).fill(Number.NaN);
1768
+ rows.forEach((row2, index) => {
1769
+ out[row2] = values[index];
1770
+ });
1771
+ return out;
1772
+ }
1773
+
1774
+ // src/core/moderation.ts
1775
+ var GRID_STEPS = 15;
1776
+ function moderationSurface(data, options) {
1777
+ const { outcome, iv, mod, interaction = true, controls = [] } = options;
1778
+ if (iv === mod) {
1779
+ throw new RangeError(`\`iv\` and \`mod\` must name different columns, both name "${iv}"`);
1780
+ }
1781
+ const named = new Set([outcome, iv, mod]);
1782
+ controls.forEach((control) => {
1783
+ if (named.has(control)) {
1784
+ throw new RangeError(`control "${control}" already names the outcome, the iv, the mod, ` + "or another control");
1785
+ }
1786
+ named.add(control);
1787
+ });
1788
+ frameRows(data);
1789
+ const y = requireNumericColumn(data, outcome, "outcome");
1790
+ const ivColumn = requireNumericColumn(data, iv, "iv");
1791
+ const modColumn = requireNumericColumn(data, mod, "mod");
1792
+ const controlColumns = controls.map((control) => requireNumericColumn(data, control, "controls"));
1793
+ const modelColumns = [y, ivColumn, modColumn, ...controlColumns];
1794
+ const anyComplete = y.some((_, row2) => modelColumns.every((column2) => Number.isFinite(column2[row2])));
1795
+ if (!anyComplete) {
1796
+ throw new RangeError("the model has no complete rows: every row is missing a value in " + "the outcome, the IV, the moderator, or a control");
1797
+ }
1798
+ const fit = lm(data, {
1799
+ outcome,
1800
+ terms: [iv, mod, ...controls, ...interaction ? [[iv, mod]] : []]
1801
+ });
1802
+ const { fitted, residuals } = fit;
1803
+ const coefficients = fit.coefficients.names.map((name, index) => ({
1804
+ name,
1805
+ value: fit.coefficients.values[index] ?? null
1806
+ }));
1807
+ const ivValues = rSeq(...extent(ivColumn), GRID_STEPS);
1808
+ const modValues = rSeq(...extent(modColumn), GRID_STEPS);
1809
+ const holds = Object.fromEntries(controls.map((control, index) => [
1810
+ control,
1811
+ mean(controlColumns[index].filter(Number.isFinite))
1812
+ ]));
1813
+ const weights = coefficients.map((term) => term.value ?? 0);
1814
+ const predictions = modValues.flatMap((modValue) => ivValues.map((ivValue) => {
1815
+ const gridRow = [
1816
+ 1,
1817
+ ivValue,
1818
+ modValue,
1819
+ ...controls.map((control) => holds[control]),
1820
+ ...interaction ? [ivValue * modValue] : []
1821
+ ];
1822
+ return sum(zipWith(gridRow, weights, (value, weight) => value * weight));
1823
+ }));
1824
+ const [dataLow, dataHigh] = extent(y);
1825
+ const [surfaceLow, surfaceHigh] = extent(predictions);
1826
+ return {
1827
+ coefficients,
1828
+ fitted,
1829
+ residuals,
1830
+ ivValues,
1831
+ modValues,
1832
+ predictions,
1833
+ zlim: [Math.min(dataLow, surfaceLow), Math.max(dataHigh, surfaceHigh)],
1834
+ holds
1835
+ };
1836
+ }
1837
+ function rSeq(from, to, length) {
1838
+ if (from === to) {
1839
+ return new Array(length).fill(from);
1301
1840
  }
1302
- return x < a + 1 ? 1 - lowerGammaSeries(a, x) : upperGammaFraction(a, x);
1841
+ const by = (to - from) / (length - 1);
1842
+ return Array.from({ length }, (_, index) => index === 0 ? from : index === length - 1 ? to : from + index * by);
1303
1843
  }
1304
- function normalCdf(z) {
1305
- if (Number.isNaN(z)) {
1306
- return Number.NaN;
1844
+ // src/core/regression.ts
1845
+ function completePointRows(points) {
1846
+ return points.flatMap((point, row2) => Number.isFinite(point.x) && Number.isFinite(point.y) ? [row2] : []);
1847
+ }
1848
+ function linearRegression(points) {
1849
+ const rows = completePointRows(points);
1850
+ if (rows.length === 0) {
1851
+ return null;
1307
1852
  }
1308
- const lower = 0.5 * upperGamma(0.5, 0.5 * z * z);
1309
- return z > 0 ? 1 - lower : lower;
1853
+ const xs = rows.map((row2) => points[row2].x);
1854
+ const ys = rows.map((row2) => points[row2].y);
1855
+ const meanX = mean(xs);
1856
+ const meanY = mean(ys);
1857
+ const devX = xs.map((x) => x - meanX);
1858
+ const devY = ys.map((y) => y - meanY);
1859
+ const sumSquaresX = sum(devX.map((d) => d * d));
1860
+ const sumSquaresY = sum(devY.map((d) => d * d));
1861
+ const sumProducts = sum(zipWith(devX, devY, (dx, dy) => dx * dy));
1862
+ const slope = sumSquaresX === 0 ? null : sumProducts / sumSquaresX;
1863
+ const intercept = slope === null ? meanY : meanY - slope * meanX;
1864
+ const correlation = sumSquaresX === 0 || sumSquaresY === 0 ? null : sumProducts / Math.sqrt(sumSquaresX * sumSquaresY);
1865
+ const fittedComplete = xs.map((x) => slope === null ? intercept : intercept + slope * x);
1866
+ const ssr = sum(fittedComplete.map((f) => (f - meanY) * (f - meanY)));
1867
+ const sse = sum(zipWith(ys, fittedComplete, (y, f) => (y - f) * (y - f)));
1868
+ const sst = sumSquaresY;
1869
+ const rSquared = sst === 0 ? null : ssr / sst;
1870
+ const fitted = new Array(points.length).fill(Number.NaN);
1871
+ rows.forEach((row2, survivor) => {
1872
+ fitted[row2] = fittedComplete[survivor];
1873
+ });
1874
+ return {
1875
+ intercept,
1876
+ slope,
1877
+ correlation,
1878
+ ssr,
1879
+ sse,
1880
+ sst,
1881
+ rSquared,
1882
+ fitted
1883
+ };
1310
1884
  }
1311
- var BELOW_ONE = 1 - Number.EPSILON / 2;
1312
- var INVERSE_MAX_STEPS = 200;
1313
- function inverseGuess(p, a, b) {
1314
- if (a >= 1 && b >= 1) {
1315
- const tail = p < 0.5 ? p : 1 - p;
1316
- const t = Math.sqrt(-2 * Math.log(tail));
1317
- const normal = (p < 0.5 ? -1 : 1) * ((2.30753 + t * 0.27061) / (1 + t * (0.99229 + t * 0.04481)) - t);
1318
- const scale = (normal * normal - 3) / 6;
1319
- const harmonic = 2 / (1 / (2 * a - 1) + 1 / (2 * b - 1));
1320
- const w = normal * Math.sqrt(scale + harmonic) / harmonic - (1 / (2 * b - 1) - 1 / (2 * a - 1)) * (scale + 5 / 6 - 2 / (3 * harmonic));
1321
- return a / (a + b * Math.exp(2 * w));
1885
+ // src/core/matrix.ts
1886
+ var SMALLEST_NORMAL = 0.000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000022250738585072014;
1887
+ function determinant(matrix2) {
1888
+ return determinantOf(factorize(matrix2));
1889
+ }
1890
+ function invertMatrix(matrix2) {
1891
+ const factorization = factorize(matrix2);
1892
+ const determinant2 = determinantOf(factorization);
1893
+ const zeroPivot = zeroPivotOf(factorization);
1894
+ if (zeroPivot !== null) {
1895
+ return {
1896
+ determinant: determinant2,
1897
+ inverse: null,
1898
+ singularity: "exact",
1899
+ rcond: 0,
1900
+ zeroPivot
1901
+ };
1322
1902
  }
1323
- const lower = Math.exp(a * Math.log(a / (a + b))) / a;
1324
- const upper = Math.exp(b * Math.log(b / (a + b))) / b;
1325
- const total = lower + upper;
1326
- if (p < lower / total) {
1327
- return Math.pow(a * total * p, 1 / a);
1903
+ const inverse = solveForIdentity(factorization);
1904
+ const rcond = 1 / (oneNorm(matrix2) * oneNorm(inverse));
1905
+ if (rcond < Number.EPSILON) {
1906
+ return {
1907
+ determinant: determinant2,
1908
+ inverse: null,
1909
+ singularity: "computational",
1910
+ rcond,
1911
+ zeroPivot: null
1912
+ };
1328
1913
  }
1329
- return 1 - Math.pow(b * total * (1 - p), 1 / b);
1914
+ return {
1915
+ determinant: determinant2,
1916
+ inverse,
1917
+ singularity: null,
1918
+ rcond,
1919
+ zeroPivot: null
1920
+ };
1330
1921
  }
1331
- function inverseIncompleteBeta(p, a, b) {
1332
- if (Number.isNaN(p) || !(a > 0) || !(b > 0)) {
1333
- return Number.NaN;
1334
- }
1335
- if (p <= 0) {
1922
+ function factorize(matrix2) {
1923
+ const interchanged = Math.abs(matrix2.y1) > Math.abs(matrix2.x1);
1924
+ const leading = interchanged ? matrix2.y1 : matrix2.x1;
1925
+ const upperRight = interchanged ? matrix2.y2 : matrix2.x2;
1926
+ const below = interchanged ? matrix2.x1 : matrix2.y1;
1927
+ const belowRight = interchanged ? matrix2.x2 : matrix2.y2;
1928
+ const multiplier = Math.abs(leading) >= SMALLEST_NORMAL ? below * (1 / leading) : below / leading;
1929
+ const trailing = fusedMultiplyAdd(-multiplier, upperRight, belowRight);
1930
+ return {
1931
+ interchanged,
1932
+ multiplier,
1933
+ pivots: [leading, trailing],
1934
+ upperRight
1935
+ };
1936
+ }
1937
+ function determinantOf(factorization) {
1938
+ const { interchanged, pivots } = factorization;
1939
+ if (zeroPivotOf(factorization) !== null) {
1336
1940
  return 0;
1337
1941
  }
1338
- if (p >= 1) {
1942
+ const modulus = sum(pivots.map((pivot) => Math.log(Math.abs(pivot))));
1943
+ const sign = pivots.reduce((carried, pivot) => pivot < 0 ? -carried : carried, interchanged ? -1 : 1);
1944
+ return sign * Math.exp(modulus);
1945
+ }
1946
+ function zeroPivotOf(factorization) {
1947
+ const [leading, trailing] = factorization.pivots;
1948
+ if (leading === 0) {
1339
1949
  return 1;
1340
1950
  }
1341
- const logBetaValue = logBeta(a, b);
1342
- let lower = 0;
1343
- let upper = 1;
1344
- let x = inverseGuess(p, a, b);
1345
- if (!(x > 0) || !(x < 1)) {
1346
- x = 0.5;
1347
- }
1348
- for (let step = 0;step < INVERSE_MAX_STEPS; step += 1) {
1349
- const residual = incompleteBeta(x, a, b) - p;
1350
- if (residual < 0) {
1351
- lower = x;
1352
- } else {
1353
- upper = x;
1354
- }
1355
- const density = Math.exp((a - 1) * Math.log(x) + (b - 1) * Math.log1p(-x) - logBetaValue);
1356
- let next = density > 0 && Number.isFinite(density) ? x - residual / density : Number.NaN;
1357
- if (!(next > lower) || !(next < upper)) {
1358
- next = 0.5 * (lower + upper);
1359
- }
1360
- if (next === x) {
1361
- break;
1362
- }
1363
- const moved = Math.abs(next - x);
1364
- x = next;
1365
- if (moved <= Number.EPSILON * x) {
1366
- break;
1367
- }
1368
- }
1369
- return Math.min(x, BELOW_ONE);
1951
+ return trailing === 0 ? 2 : null;
1370
1952
  }
1371
-
1372
- // src/core/tdist.ts
1373
- function isNonCentral(ncp) {
1374
- return ncp !== undefined && ncp !== 0;
1953
+ function solveForIdentity(factorization) {
1954
+ const { interchanged } = factorization;
1955
+ const [firstTop, firstBottom] = interchanged ? [0, 1] : [1, 0];
1956
+ const [secondTop, secondBottom] = interchanged ? [1, 0] : [0, 1];
1957
+ const [x1, y1] = solveColumn(factorization, firstTop, firstBottom);
1958
+ const [x2, y2] = solveColumn(factorization, secondTop, secondBottom);
1959
+ return {
1960
+ x1: withoutNegativeZero(x1),
1961
+ y1: withoutNegativeZero(y1),
1962
+ x2: withoutNegativeZero(x2),
1963
+ y2: withoutNegativeZero(y2)
1964
+ };
1375
1965
  }
1376
- function isBadArgument(value, df, ncp) {
1377
- return Number.isNaN(value) || !(df > 0) || ncp !== undefined && Number.isNaN(ncp);
1966
+ function solveColumn(factorization, top, bottom) {
1967
+ const { multiplier, pivots, upperRight } = factorization;
1968
+ const lower = fusedMultiplyAdd(-multiplier, top, bottom) / pivots[1];
1969
+ const upper = fusedMultiplyAdd(-upperRight, lower, top) / pivots[0];
1970
+ return [upper, lower];
1378
1971
  }
1379
- function dt(x, df, ncp) {
1380
- if (isBadArgument(x, df, ncp)) {
1381
- return Number.NaN;
1382
- }
1383
- return isNonCentral(ncp) ? nonCentralDensity(x, df, ncp) : centralDensity(x, df);
1972
+ function oneNorm(matrix2) {
1973
+ return Math.max(Math.abs(matrix2.x1) + Math.abs(matrix2.y1), Math.abs(matrix2.x2) + Math.abs(matrix2.y2));
1384
1974
  }
1385
- function pt(x, df, ncp) {
1386
- if (isBadArgument(x, df, ncp)) {
1387
- return Number.NaN;
1975
+ // src/core/pca.ts
1976
+ function principalComponents(points) {
1977
+ const rows = completePointRows(points);
1978
+ if (rows.length === 0) {
1979
+ return null;
1388
1980
  }
1389
- return isNonCentral(ncp) ? nonCentralProbability(x, df, ncp) : centralProbability(x, df);
1981
+ const complete = rows.map((row2) => points[row2]);
1982
+ const centerX = mean(complete.map((point) => point.x));
1983
+ const centerY = mean(complete.map((point) => point.y));
1984
+ const devX = complete.map((point) => point.x - centerX);
1985
+ const devY = complete.map((point) => point.y - centerY);
1986
+ const divisor = Math.max(1, complete.length - 1);
1987
+ const varX = sum(devX.map((d) => d * d)) / divisor;
1988
+ const varY = sum(devY.map((d) => d * d)) / divisor;
1989
+ const covariance = sum(zipWith(devX, devY, (dx, dy) => dx * dy)) / divisor;
1990
+ const [first, second] = componentsOf(varX, varY, covariance);
1991
+ const scores = points.map(() => ({ x: Number.NaN, y: Number.NaN }));
1992
+ rows.forEach((row2, survivor) => {
1993
+ const dx = devX[survivor];
1994
+ const dy = devY[survivor];
1995
+ scores[row2] = {
1996
+ x: dx * first.loadings[0] + dy * first.loadings[1],
1997
+ y: dx * second.loadings[0] + dy * second.loadings[1]
1998
+ };
1999
+ });
2000
+ return {
2001
+ sdev: [sdevOf(first.variance), sdevOf(second.variance)],
2002
+ rotation: [first.loadings, second.loadings],
2003
+ center: { x: centerX, y: centerY },
2004
+ scores
2005
+ };
1390
2006
  }
1391
- function qt(p, df, ncp) {
1392
- if (isBadArgument(p, df, ncp) || p < 0 || p > 1) {
1393
- return Number.NaN;
2007
+ function componentsOf(varX, varY, covariance) {
2008
+ if (covariance === 0) {
2009
+ const alongX = { variance: varX, loadings: [1, 0] };
2010
+ const alongY = { variance: varY, loadings: [0, 1] };
2011
+ return varX >= varY ? [alongX, alongY] : [alongY, alongX];
1394
2012
  }
1395
- return isNonCentral(ncp) ? nonCentralQuantile(p, df, ncp) : centralQuantile(p, df);
2013
+ const middle = (varX + varY) / 2;
2014
+ const spread = Math.hypot((varX - varY) / 2, covariance);
2015
+ const larger = middle + spread;
2016
+ const smaller = middle - spread;
2017
+ const fromRowX = larger - varX;
2018
+ const fromRowY = larger - varY;
2019
+ const direction = fromRowX >= fromRowY ? [covariance, fromRowX] : [fromRowY, covariance];
2020
+ const leading = signed(unit(direction));
2021
+ const trailing = signed([negated(leading[1]), leading[0]]);
2022
+ return [
2023
+ { variance: larger, loadings: leading },
2024
+ { variance: smaller, loadings: trailing }
2025
+ ];
1396
2026
  }
1397
- function centralDensity(x, df) {
1398
- if (!Number.isFinite(x)) {
1399
- return 0;
1400
- }
1401
- const logDensity = -0.5 * Math.log(df) - logBeta(0.5, df / 2) - (df + 1) / 2 * Math.log1p(x * x / df);
1402
- return Math.exp(logDensity);
2027
+ function unit(vector) {
2028
+ const length = Math.hypot(vector[0], vector[1]);
2029
+ return [vector[0] / length, vector[1] / length];
1403
2030
  }
1404
- function centralProbability(x, df) {
1405
- if (x === 0) {
1406
- return 0.5;
2031
+ function signed(vector) {
2032
+ const dominant = Math.abs(vector[0]) >= Math.abs(vector[1]) ? vector[0] : vector[1];
2033
+ return dominant >= 0 ? vector : [negated(vector[0]), negated(vector[1])];
2034
+ }
2035
+ function negated(value) {
2036
+ return withoutNegativeZero(-value);
2037
+ }
2038
+ function sdevOf(variance) {
2039
+ return Math.sqrt(Math.max(0, variance));
2040
+ }
2041
+ // src/core/ols.ts
2042
+ var DEFAULT_LEAST_SQUARES_TOLERANCE = 0.0000001;
2043
+ function leastSquares(design, y, options = {}) {
2044
+ const { weights, tolerance = DEFAULT_LEAST_SQUARES_TOLERANCE } = options;
2045
+ const rows = design.length;
2046
+ if (rows === 0) {
2047
+ throw new RangeError("least squares needs at least one row");
1407
2048
  }
1408
- if (x === Number.POSITIVE_INFINITY) {
1409
- return 1;
2049
+ const width = design[0].length;
2050
+ if (design.some((row2) => row2.length !== width)) {
2051
+ throw new RangeError("every design row needs the same number of columns");
1410
2052
  }
1411
- if (x === Number.NEGATIVE_INFINITY) {
1412
- return 0;
2053
+ if (y.length !== rows) {
2054
+ throw new RangeError(`the response has ${y.length} values but the design has ${rows} rows`);
1413
2055
  }
1414
- const tail = upperTail(Math.abs(x), df);
1415
- return x < 0 ? tail : 1 - tail;
1416
- }
1417
- function upperTail(t, df) {
1418
- const squared = t * t;
1419
- if (!Number.isFinite(squared)) {
1420
- return 0;
2056
+ if (weights !== undefined) {
2057
+ if (weights.length !== rows) {
2058
+ throw new RangeError(`there are ${weights.length} weights but ${rows} rows`);
2059
+ }
2060
+ if (weights.some((weight) => !(weight >= 0))) {
2061
+ throw new RangeError("weights cannot be negative or missing");
2062
+ }
1421
2063
  }
1422
- const total = df + squared;
1423
- return 0.5 * incompleteBetaSplit(df / total, squared / total, df / 2, 0.5);
2064
+ const scale = weights?.map((weight) => Math.sqrt(weight));
2065
+ const scaled = (value, row2) => scale === undefined ? value : value * scale[row2];
2066
+ const buffer = new Float64Array(rows * width);
2067
+ design.forEach((row2, index) => {
2068
+ for (let column2 = 0;column2 < width; column2++) {
2069
+ buffer[column2 * rows + index] = scaled(row2[column2], index);
2070
+ }
2071
+ });
2072
+ const factored = qr(make(rows, width, buffer, null), { tolerance });
2073
+ const coefficients = qrCoef(factored, y.map(scaled));
2074
+ const fitted = design.map((row2) => sum(zipWith(row2, coefficients, (value, coefficient) => coefficient === null ? 0 : value * coefficient)));
2075
+ const residuals = zipWith(y, fitted, (value, fit) => value - fit);
2076
+ return { coefficients, fitted, residuals, rank: factored.rank };
1424
2077
  }
1425
- var POLISH_MAX_STEPS = 4;
1426
- function centralQuantile(p, df) {
1427
- if (p === 0.5) {
1428
- return 0;
1429
- }
1430
- if (p <= 0) {
1431
- return Number.NEGATIVE_INFINITY;
1432
- }
1433
- if (p >= 1) {
1434
- return Number.POSITIVE_INFINITY;
2078
+
2079
+ // src/core/logit.ts
2080
+ var DEFAULT_LOGIT_EPSILON = 0.00000001;
2081
+ var DEFAULT_LOGIT_MAX_ITERATIONS = 25;
2082
+ var LINK_THRESHOLD = 30;
2083
+ var BOUND = 10 * Number.EPSILON;
2084
+ function logisticRegression(points, options = {}) {
2085
+ const {
2086
+ epsilon = DEFAULT_LOGIT_EPSILON,
2087
+ maxIterations = DEFAULT_LOGIT_MAX_ITERATIONS
2088
+ } = options;
2089
+ const rows = completePointRows(points);
2090
+ if (rows.length === 0) {
2091
+ return null;
1435
2092
  }
1436
- const tail = p < 0.5 ? p : 1 - p;
1437
- const sign = p < 0.5 ? -1 : 1;
1438
- const twoSided = 2 * tail;
1439
- let squared;
1440
- if (twoSided > 0.5) {
1441
- const near = inverseIncompleteBeta(1 - twoSided, 0.5, df / 2);
1442
- squared = df * near / (1 - near);
1443
- } else {
1444
- const far = inverseIncompleteBeta(twoSided, df / 2, 0.5);
1445
- squared = df * (1 - far) / far;
2093
+ const complete = rows.map((row2) => points[row2]);
2094
+ if (complete.some((point) => point.y !== 0 && point.y !== 1)) {
2095
+ throw new RangeError("every outcome must be 0 or 1");
1446
2096
  }
1447
- return sign * polish(Math.sqrt(squared), tail, df);
1448
- }
1449
- function polish(start, tail, df) {
1450
- let t = start;
1451
- for (let step = 0;step < POLISH_MAX_STEPS; step += 1) {
1452
- const density = centralDensity(t, df);
1453
- if (!(density > 0) || !Number.isFinite(t)) {
1454
- break;
1455
- }
1456
- const move = (upperTail(t, df) - tail) / density;
1457
- const next = t + move;
1458
- if (!(next > 0) || !Number.isFinite(next) || Math.abs(move) > 0.25 * t) {
1459
- break;
1460
- }
1461
- if (next === t) {
2097
+ const outcomes = complete.map((point) => point.y);
2098
+ const design = complete.map((point) => [1, point.x]);
2099
+ let fitted = outcomes.map((outcome) => (outcome + 0.5) / 2);
2100
+ let predictors = fitted.map(logit);
2101
+ let previousDeviance = totalDeviance(outcomes, fitted);
2102
+ let deviance = previousDeviance;
2103
+ let coefficients = [Number.NaN, null];
2104
+ let rank = 0;
2105
+ let iterations = 0;
2106
+ let converged = false;
2107
+ for (let iteration = 1;iteration <= maxIterations; iteration++) {
2108
+ iterations = iteration;
2109
+ const working = predictors.map((predictor, row2) => {
2110
+ const probability = fitted[row2];
2111
+ const slope = linkSlope(predictor);
2112
+ return {
2113
+ weight: slope * slope / (probability * (1 - probability)),
2114
+ response: predictor + (outcomes[row2] - probability) / slope
2115
+ };
2116
+ });
2117
+ const step = leastSquares(design, working.map((row2) => row2.response), {
2118
+ weights: working.map((row2) => row2.weight),
2119
+ tolerance: Math.min(0.0000001, epsilon / 1000)
2120
+ });
2121
+ if (step.coefficients.some((coefficient) => coefficient !== null && !Number.isFinite(coefficient))) {
1462
2122
  break;
1463
2123
  }
1464
- t = next;
1465
- if (Math.abs(move) <= Number.EPSILON * t) {
2124
+ coefficients = step.coefficients;
2125
+ rank = step.rank;
2126
+ predictors = design.map((row2) => sum(zipWith(row2, coefficients, (value, coefficient) => coefficient === null ? 0 : value * coefficient)));
2127
+ fitted = predictors.map(linkInverse);
2128
+ deviance = totalDeviance(outcomes, fitted);
2129
+ if (Math.abs(deviance - previousDeviance) / (0.1 + Math.abs(deviance)) < epsilon) {
2130
+ converged = true;
1466
2131
  break;
1467
2132
  }
2133
+ previousDeviance = deviance;
1468
2134
  }
1469
- return t;
2135
+ const wholeMean = mean(outcomes);
2136
+ const nullDeviance = totalDeviance(outcomes, outcomes.map(() => wholeMean));
2137
+ const paddedFitted = new Array(points.length).fill(Number.NaN);
2138
+ const paddedPredictors = new Array(points.length).fill(Number.NaN);
2139
+ rows.forEach((row2, survivor) => {
2140
+ paddedFitted[row2] = fitted[survivor];
2141
+ paddedPredictors[row2] = predictors[survivor];
2142
+ });
2143
+ return {
2144
+ intercept: coefficients[0] ?? Number.NaN,
2145
+ slope: coefficients[1] ?? null,
2146
+ fitted: paddedFitted,
2147
+ linearPredictors: paddedPredictors,
2148
+ deviance,
2149
+ nullDeviance,
2150
+ aic: deviance + 2 * rank,
2151
+ rank,
2152
+ iterations,
2153
+ converged,
2154
+ saturated: fitted.some((probability) => probability > 1 - BOUND || probability < BOUND)
2155
+ };
1470
2156
  }
1471
- var SERIES_MAX_STEPS = 1000;
1472
- var SERIES_ERROR_MAX = 0.000000000001;
1473
- var SERIES_NCP_LIMIT_SQUARED = 2 * Math.LN2 * 1022;
1474
- var SERIES_DF_LIMIT = 400000;
1475
- var SQRT_TWO_OVER_PI = Math.sqrt(2 / Math.PI);
1476
- function nonCentralProbability(x, df, ncp) {
1477
- if (x === Number.POSITIVE_INFINITY) {
1478
- return 1;
1479
- }
1480
- if (x === Number.NEGATIVE_INFINITY) {
1481
- return 0;
1482
- }
1483
- const reflected = x < 0;
1484
- const t = reflected ? -x : x;
1485
- const delta = reflected ? -ncp : ncp;
1486
- const lower = df > SERIES_DF_LIMIT || delta * delta > SERIES_NCP_LIMIT_SQUARED ? normalApproximation(t, df, delta) : lenthSeries(t, df, delta);
1487
- return reflected ? 1 - lower : lower;
2157
+ function predictLogit(fit, x) {
2158
+ return linkInverse(fit.intercept + (fit.slope ?? 0) * x);
1488
2159
  }
1489
- function normalApproximation(t, df, delta) {
1490
- const shrink = 1 / (4 * df);
1491
- const spread = Math.sqrt(1 + t * t * 2 * shrink);
1492
- return normalCdf((t * (1 - shrink) - delta) / spread);
2160
+ function logit(probability) {
2161
+ return Math.log(probability / (1 - probability));
1493
2162
  }
1494
- function lenthSeries(t, df, delta) {
1495
- const squared = t * t;
1496
- const total = df + squared;
1497
- const x = squared / total;
1498
- const complement = df / total;
1499
- let sum2 = 0;
1500
- if (x > 0) {
1501
- const lambda = delta * delta;
1502
- let oddWeight = 0.5 * Math.exp(-0.5 * lambda);
1503
- let evenWeight = SQRT_TWO_OVER_PI * oddWeight * delta;
1504
- let remaining = 0.5 - oddWeight;
1505
- if (remaining < 0.0000001) {
1506
- remaining = -0.5 * Math.expm1(-0.5 * lambda);
1507
- }
1508
- let a = 0.5;
1509
- const b = 0.5 * df;
1510
- const powered = Math.pow(complement, b);
1511
- const logBetaValue = logBeta(0.5, b);
1512
- let oddTerm = incompleteBetaSplit(x, complement, a, b);
1513
- let oddStep = 2 * powered * Math.exp(a * Math.log(x) - logBetaValue);
1514
- let evenTerm = 1 - powered;
1515
- let evenStep = b * x * powered;
1516
- sum2 = oddWeight * oddTerm + evenWeight * evenTerm;
1517
- for (let step = 1;step <= SERIES_MAX_STEPS; step += 1) {
1518
- a += 1;
1519
- oddTerm -= oddStep;
1520
- evenTerm -= evenStep;
1521
- oddStep *= x * (a + b - 1) / a;
1522
- evenStep *= x * (a + b - 0.5) / (a + 0.5);
1523
- oddWeight *= lambda / (2 * step);
1524
- evenWeight *= lambda / (2 * step + 1);
1525
- remaining -= oddWeight;
1526
- if (remaining <= 0) {
1527
- break;
1528
- }
1529
- sum2 += oddWeight * oddTerm + evenWeight * evenTerm;
1530
- if (Math.abs(2 * remaining * (oddTerm - oddStep)) < SERIES_ERROR_MAX) {
1531
- break;
1532
- }
1533
- }
1534
- }
1535
- return Math.min(Math.max(sum2 + normalCdf(-delta), 0), 1);
2163
+ function linkInverse(predictor) {
2164
+ const odds = predictor < -LINK_THRESHOLD ? Number.EPSILON : predictor > LINK_THRESHOLD ? 1 / Number.EPSILON : Math.exp(predictor);
2165
+ return odds / (1 + odds);
1536
2166
  }
1537
- function nonCentralDensity(x, df, ncp) {
1538
- if (!Number.isFinite(x)) {
1539
- return 0;
1540
- }
1541
- if (Math.abs(x) > Math.sqrt(df * Number.EPSILON)) {
1542
- const stepped = x * Math.sqrt((df + 2) / df);
1543
- const difference = nonCentralProbability(stepped, df + 2, ncp) - nonCentralProbability(x, df, ncp);
1544
- return df / Math.abs(x) * Math.abs(difference);
2167
+ function linkSlope(predictor) {
2168
+ if (predictor > LINK_THRESHOLD || predictor < -LINK_THRESHOLD) {
2169
+ return Number.EPSILON;
1545
2170
  }
1546
- return Math.exp(-0.5 * Math.log(df) - logBeta(0.5, df / 2) - 0.5 * ncp * ncp);
2171
+ const odds = Math.exp(predictor);
2172
+ return odds / ((1 + odds) * (1 + odds));
1547
2173
  }
1548
- var QUANTILE_MAX_STEPS = 200;
1549
- function nonCentralQuantile(p, df, ncp) {
1550
- if (p <= 0) {
1551
- return Number.NEGATIVE_INFINITY;
1552
- }
1553
- if (p >= 1) {
1554
- return Number.POSITIVE_INFINITY;
1555
- }
1556
- let upper = Math.max(1, ncp);
1557
- while (Number.isFinite(upper) && nonCentralProbability(upper, df, ncp) < p) {
1558
- upper *= 2;
1559
- }
1560
- let lower = Math.min(-1, -ncp);
1561
- while (Number.isFinite(lower) && nonCentralProbability(lower, df, ncp) > p) {
1562
- lower *= 2;
1563
- }
1564
- let t = 0.5 * (lower + upper);
1565
- for (let step = 0;step < QUANTILE_MAX_STEPS; step += 1) {
1566
- const residual = nonCentralProbability(t, df, ncp) - p;
1567
- if (residual < 0) {
1568
- lower = t;
1569
- } else {
1570
- upper = t;
1571
- }
1572
- const density = nonCentralDensity(t, df, ncp);
1573
- let next = density > 0 && Number.isFinite(density) ? t - residual / density : Number.NaN;
1574
- if (!(next > lower) || !(next < upper)) {
1575
- next = 0.5 * (lower + upper);
1576
- }
1577
- if (next === t) {
1578
- break;
1579
- }
1580
- const moved = Math.abs(next - t);
1581
- t = next;
1582
- if (moved <= Number.EPSILON * Math.abs(t)) {
1583
- break;
1584
- }
1585
- }
1586
- return t;
2174
+ function totalDeviance(outcomes, probabilities) {
2175
+ return sum(zipWith(outcomes, probabilities, (outcome, probability) => 2 * (surprise(outcome, probability) + surprise(1 - outcome, 1 - probability))));
2176
+ }
2177
+ function surprise(outcome, probability) {
2178
+ return outcome !== 0 ? outcome * Math.log(outcome / probability) : 0;
1587
2179
  }
1588
2180
  // src/core/ttest.ts
1589
2181
  var DEFAULT_T_TEST_OPTIONS = {
@@ -1596,27 +2188,27 @@ var FILL_UPPER_QUANTILE = 0.999;
1596
2188
  function tTestStats(options = {}) {
1597
2189
  const { diff, sd: sd2, n, alpha } = { ...DEFAULT_T_TEST_OPTIONS, ...options };
1598
2190
  const df = n - 1;
1599
- const t = diff / (sd2 / Math.sqrt(n));
2191
+ const t2 = diff / (sd2 / Math.sqrt(n));
1600
2192
  const criticalValue = qt(1 - alpha, df);
1601
- const beta = pt(criticalValue, df, t);
2193
+ const beta = pt(criticalValue, df, t2);
1602
2194
  const power = 1 - beta;
1603
- const altMedian = qt(0.5, df, t);
1604
- const altFillFrom = qt(beta, df, t);
2195
+ const altMedian = qt(0.5, df, t2);
2196
+ const altFillFrom = qt(beta, df, t2);
1605
2197
  return {
1606
2198
  diff,
1607
2199
  sd: sd2,
1608
2200
  n,
1609
2201
  alpha,
1610
2202
  df,
1611
- t,
2203
+ t: t2,
1612
2204
  criticalValue,
1613
2205
  beta,
1614
2206
  power,
1615
2207
  altMedian,
1616
- altMedianDensity: dt(altMedian, df, t),
2208
+ altMedianDensity: dt(altMedian, df, t2),
1617
2209
  altFill: {
1618
2210
  from: altFillFrom,
1619
- to: qt(FILL_UPPER_QUANTILE, df, t)
2211
+ to: qt(FILL_UPPER_QUANTILE, df, t2)
1620
2212
  },
1621
2213
  nullFill: {
1622
2214
  from: criticalValue,
@@ -2701,8 +3293,8 @@ var HEAD_LENGTH = 24;
2701
3293
  function matrixInverseScale(width, height) {
2702
3294
  return createScale({ width, height, x: WORLD, y: WORLD });
2703
3295
  }
2704
- function plotMatrixInverse(target, matrix) {
2705
- const inversion = invertMatrix(matrix);
3296
+ function plotMatrixInverse(target, matrix2) {
3297
+ const inversion = invertMatrix(matrix2);
2706
3298
  const { ctx, width, height } = resolveTarget(target);
2707
3299
  if (inversion.inverse === null) {
2708
3300
  return inversion;
@@ -2713,22 +3305,22 @@ function plotMatrixInverse(target, matrix) {
2713
3305
  const { area } = scale;
2714
3306
  ctx.save();
2715
3307
  clipToArea(ctx, area);
2716
- drawMatrix(ctx, scale, matrix, MATRIX_FILL);
3308
+ drawMatrix(ctx, scale, matrix2, MATRIX_FILL);
2717
3309
  drawMatrix(ctx, scale, inversion.inverse, INVERSE_FILL);
2718
3310
  ctx.restore();
2719
3311
  return inversion;
2720
3312
  }
2721
- function drawMatrix(ctx, scale, matrix, fill) {
2722
- drawParallelogram(ctx, scale, matrix, fill);
2723
- drawColumnArrow(ctx, scale, matrix.x1, matrix.y1);
2724
- drawColumnArrow(ctx, scale, matrix.x2, matrix.y2);
3313
+ function drawMatrix(ctx, scale, matrix2, fill) {
3314
+ drawParallelogram(ctx, scale, matrix2, fill);
3315
+ drawColumnArrow(ctx, scale, matrix2.x1, matrix2.y1);
3316
+ drawColumnArrow(ctx, scale, matrix2.x2, matrix2.y2);
2725
3317
  }
2726
- function drawParallelogram(ctx, scale, matrix, fill) {
3318
+ function drawParallelogram(ctx, scale, matrix2, fill) {
2727
3319
  const corners = [
2728
3320
  [0, 0],
2729
- [matrix.x1, matrix.y1],
2730
- [matrix.x1 + matrix.x2, matrix.y1 + matrix.y2],
2731
- [matrix.x2, matrix.y2],
3321
+ [matrix2.x1, matrix2.y1],
3322
+ [matrix2.x1 + matrix2.x2, matrix2.y1 + matrix2.y2],
3323
+ [matrix2.x2, matrix2.y2],
2732
3324
  [0, 0]
2733
3325
  ];
2734
3326
  ctx.save();
@@ -2932,29 +3524,29 @@ function drawRows(ctx, scale, rows, colors) {
2932
3524
  ctx.setLineDash([]);
2933
3525
  ctx.lineWidth = SPAN_WIDTH;
2934
3526
  ctx.strokeStyle = colors.span99;
2935
- for (const { interval, row } of rows) {
2936
- drawSpan(ctx, scale, interval.ci99.low, interval.ci99.high, row);
3527
+ for (const { interval, row: row2 } of rows) {
3528
+ drawSpan(ctx, scale, interval.ci99.low, interval.ci99.high, row2);
2937
3529
  }
2938
3530
  ctx.strokeStyle = colors.span95;
2939
- for (const { interval, row } of rows) {
2940
- drawSpan(ctx, scale, interval.ci95.low, interval.ci95.high, row);
3531
+ for (const { interval, row: row2 } of rows) {
3532
+ drawSpan(ctx, scale, interval.ci95.low, interval.ci95.high, row2);
2941
3533
  }
2942
3534
  ctx.fillStyle = colors.point;
2943
- for (const { interval, row } of rows) {
2944
- drawDiamond(ctx, scale, interval.mean, row);
3535
+ for (const { interval, row: row2 } of rows) {
3536
+ drawDiamond(ctx, scale, interval.mean, row2);
2945
3537
  }
2946
3538
  ctx.restore();
2947
3539
  }
2948
- function drawSpan(ctx, scale, low, high, row) {
2949
- const y = scale.toPixelY(row);
3540
+ function drawSpan(ctx, scale, low, high, row2) {
3541
+ const y = scale.toPixelY(row2);
2950
3542
  ctx.beginPath();
2951
3543
  ctx.moveTo(scale.toPixelX(low), y);
2952
3544
  ctx.lineTo(scale.toPixelX(high), y);
2953
3545
  ctx.stroke();
2954
3546
  }
2955
- function drawDiamond(ctx, scale, at, row) {
2956
- const x = scale.toPixelX(at);
2957
- const y = scale.toPixelY(row);
3547
+ function drawDiamond(ctx, scale, at2, row2) {
3548
+ const x = scale.toPixelX(at2);
3549
+ const y = scale.toPixelY(row2);
2958
3550
  ctx.beginPath();
2959
3551
  ctx.moveTo(x, y - POINT_RADIUS2);
2960
3552
  ctx.lineTo(x + POINT_RADIUS2, y);
@@ -2962,12 +3554,12 @@ function drawDiamond(ctx, scale, at, row) {
2962
3554
  ctx.lineTo(x - POINT_RADIUS2, y);
2963
3555
  ctx.fill();
2964
3556
  }
2965
- function drawPopulationMean(ctx, scale, at) {
2966
- if (!Number.isFinite(at)) {
3557
+ function drawPopulationMean(ctx, scale, at2) {
3558
+ if (!Number.isFinite(at2)) {
2967
3559
  return;
2968
3560
  }
2969
3561
  const { area } = scale;
2970
- const x = scale.toPixelX(at);
3562
+ const x = scale.toPixelX(at2);
2971
3563
  ctx.save();
2972
3564
  ctx.setLineDash([]);
2973
3565
  ctx.strokeStyle = MEAN_LINE_COLOR;
@@ -3244,17 +3836,17 @@ function drawCaret(ctx, scale, atRight, color) {
3244
3836
  ctx.fill();
3245
3837
  ctx.restore();
3246
3838
  }
3247
- function drawMarkNote(ctx, scale, note, at) {
3839
+ function drawMarkNote(ctx, scale, note, at2) {
3248
3840
  ctx.save();
3249
3841
  ctx.setLineDash([]);
3250
3842
  ctx.fillStyle = MARK_COLOR;
3251
3843
  ctx.font = COUNT_FONT;
3252
3844
  ctx.textAlign = "left";
3253
3845
  ctx.textBaseline = "middle";
3254
- ctx.fillText(note, scale.area.left, scale.toPixelY(at) + LABEL_LINE_HEIGHT);
3846
+ ctx.fillText(note, scale.area.left, scale.toPixelY(at2) + LABEL_LINE_HEIGHT);
3255
3847
  ctx.restore();
3256
3848
  }
3257
- function drawLabel(ctx, scale, lines, at, font) {
3849
+ function drawLabel(ctx, scale, lines, at2, font) {
3258
3850
  ctx.save();
3259
3851
  ctx.setLineDash([]);
3260
3852
  ctx.fillStyle = TEXT_COLOR2;
@@ -3262,7 +3854,7 @@ function drawLabel(ctx, scale, lines, at, font) {
3262
3854
  ctx.textAlign = "left";
3263
3855
  ctx.textBaseline = "middle";
3264
3856
  lines.forEach((line, index) => {
3265
- ctx.fillText(line, scale.area.left, scale.toPixelY(at) + index * LABEL_LINE_HEIGHT);
3857
+ ctx.fillText(line, scale.area.left, scale.toPixelY(at2) + index * LABEL_LINE_HEIGHT);
3266
3858
  });
3267
3859
  ctx.restore();
3268
3860
  }
@@ -4337,5 +4929,5 @@ export {
4337
4929
  DEFAULT_LEAST_SQUARES_TOLERANCE
4338
4930
  };
4339
4931
 
4340
- //# debugId=CC683922C616AE5764756E2164756E21
4932
+ //# debugId=6C77280FC4EBBC8264756E2164756E21
4341
4933
  //# sourceMappingURL=index.js.map