Splines: Avoid integer overflow in basis derivative scaling

libeigen/eigen!3421

diff --git a/contrib/Eigen/src/Splines/Spline.h b/contrib/Eigen/src/Splines/Spline.h
index 74eca8e..06d70be 100644
--- a/contrib/Eigen/src/Splines/Spline.h
+++ b/contrib/Eigen/src/Splines/Spline.h
@@ -448,10 +448,10 @@
 
   /* Multiply through by the correct factors */
   /* (Eq. [2.9])                             */
-  r = p;
+  Scalar factor = Scalar(1);
   for (DenseIndex k = 1; k <= static_cast<DenseIndex>(n); ++k) {
-    for (j = p; j >= 0; --j) N_(k, j) *= r;
-    r *= p - k;
+    factor *= Scalar(p - k + 1);
+    for (j = p; j >= 0; --j) N_(k, j) *= factor;
   }
 }
 
diff --git a/contrib/test/splines.cpp b/contrib/test/splines.cpp
index a6cd449..e57de4a 100644
--- a/contrib/test/splines.cpp
+++ b/contrib/test/splines.cpp
@@ -233,7 +233,35 @@
   }
 }
 
+template <typename Scalar>
+void check_high_degree_basis_derivatives() {
+  using SplineType = Spline<Scalar, 1>;
+  // 13! exceeds signed 32-bit indices; 21! exceeds signed 64-bit indices.
+  for (DenseIndex degree : {DenseIndex(13), DenseIndex(21), DenseIndex(22)}) {
+    typename SplineType::KnotVectorType knots(2 * (degree + 1));
+    knots.head(degree + 1).setZero();
+    knots.tail(degree + 1).setOnes();
+    const auto derivatives = SplineType::BasisFunctionDerivatives(Scalar(0.5), degree, degree, knots);
+    long double factorial = 1;
+    for (DenseIndex k = 2; k <= degree; ++k) factorial *= static_cast<long double>(k);
+    long double binomial = 1;
+    for (DenseIndex j = 0; j <= degree; ++j) {
+      // Clamped knots give Bernstein polynomials: B_j^(p) = (-1)^(p-j) p! binomial(p,j).
+      const Scalar expected = static_cast<Scalar>(((degree - j) % 2 ? -factorial : factorial) * binomial);
+      const Scalar actual = derivatives(degree, j);
+      // At most p-1 factorial roundings, one final product, and reference rounding; 2p*eps leaves margin.
+      const Scalar bound = Scalar(2 * degree) * NumTraits<Scalar>::epsilon() * numext::abs(expected);
+      VERIFY((numext::isfinite)(actual));
+      VERIFY((numext::isfinite)(bound));
+      VERIFY(numext::abs(actual - expected) <= bound);
+      binomial = binomial * static_cast<long double>(degree - j) / static_cast<long double>(j + 1);
+    }
+  }
+}
+
 EIGEN_DECLARE_TEST(splines) {
+  CALL_SUBTEST(check_high_degree_basis_derivatives<float>());
+  CALL_SUBTEST(check_high_degree_basis_derivatives<double>());
   for (int i = 0; i < g_repeat; ++i) {
     CALL_SUBTEST(eval_spline3d());
     CALL_SUBTEST(eval_spline3d_onbrks());