Repository: scipy/scipy

Issue:
BUG: scipy.special.factorial2 doesn't handle `uint32` dtypes
### Describe your issue.

Double factorial does't work on `uint32` dtypes in version `1.13` which is not an issue in `1.12`.

### Reproducing Code Example

```python
print(scipy.special.factorial2(numpy.array(3, dtype="int")))
print(scipy.special.factorial2(numpy.array(3, dtype="uint32")))
```


### Error message

```shell
3.
/Users/jonathf/personal/chaospy/.311/lib/python3.11/site-packages/scipy/special/_basic.py:2930: RuntimeWarning: overflow encountered in scalar negative
  def corr(k, r): return np.power(k, -r / k) / gamma(r / k + 1) * r
/Users/jonathf/personal/chaospy/.311/lib/python3.11/site-packages/scipy/special/_basic.py:2930: RuntimeWarning: overflow encountered in power
  def corr(k, r): return np.power(k, -r / k) / gamma(r / k + 1) * r
inf
```


### SciPy/NumPy/Python version and system information

```shell
1.13.0 1.26.4 sys.version_info(major=3, minor=11, micro=5, releaselevel='final', serial=0)
Build Dependencies:
  blas:
    detection method: pkgconfig
    found: true
    include directory: /usr/local/include
    lib directory: /usr/local/lib
    name: openblas
    openblas configuration: USE_64BITINT=0 DYNAMIC_ARCH=1 DYNAMIC_OLDER= NO_CBLAS=
      NO_LAPACK= NO_LAPACKE= NO_AFFINITY=1 USE_OPENMP= SANDYBRIDGE MAX_THREADS=64
    pc file directory: /usr/local/lib/pkgconfig
    version: 0.3.26.dev
  lapack:
    detection method: pkgconfig
    found: true
    include directory: /usr/local/include
    lib directory: /usr/local/lib
    name: openblas
    openblas configuration: USE_64BITINT=0 DYNAMIC_ARCH=1 DYNAMIC_OLDER= NO_CBLAS=
      NO_LAPACK= NO_LAPACKE= NO_AFFINITY=1 USE_OPENMP= SANDYBRIDGE MAX_THREADS=64
    pc file directory: /usr/local/lib/pkgconfig
    version: 0.3.26.dev
  pybind11:
    detection method: config-tool
    include directory: unknown
    name: pybind11
    version: 2.12.0
Compilers:
  c:
    commands: cc
    linker: ld64
    name: clang
    version: 13.0.0
  c++:
    commands: c++
    linker: ld64
    name: clang
    version: 13.0.0
  cython:
    commands: cython
    linker: cython
    name: cython
    version: 3.0.10
  fortran:
    commands: gfortran
    linker: ld64
    name: gcc
    version: 11.3.0
  pythran:
    include directory: ../../../../../../private/var/folders/24/8k48jl6d249_n_qfxwsl6xvm0000gn/T/pip-build-env-mo36y8b_/overlay/lib/python3.11/site-packages/pythran
    version: 0.15.0
Machine Information:
  build:
    cpu: x86_64
    endian: little
    family: x86_64
    system: darwin
  cross-compiled: false
  host:
    cpu: x86_64
    endian: little
    family: x86_64
    system: darwin
Python Information:
  path: /private/var/folders/24/8k48jl6d249_n_qfxwsl6xvm0000gn/T/cibw-run-1iia41ej/cp311-macosx_x86_64/build/venv/bin/python
  version: '3.11'
```



Discussion:
cc @h-vetinari 

Resolving patch:
diff --git a/scipy/special/_basic.py b/scipy/special/_basic.py
--- a/scipy/special/_basic.py
+++ b/scipy/special/_basic.py
@@ -2943,7 +2943,8 @@ def corr(k, r): return np.power(k, -r / k) / gamma(r / k + 1) * r
     for r in np.unique(n_mod_k):
         if r == 0:
             continue
-        result[n_mod_k == r] *= corr(k, r)
+        # cast to int because uint types break on `-r`
+        result[n_mod_k == r] *= corr(k, int(r))
     return result
 
 


Test patch:
diff --git a/scipy/special/tests/test_basic.py b/scipy/special/tests/test_basic.py
--- a/scipy/special/tests/test_basic.py
+++ b/scipy/special/tests/test_basic.py
@@ -2130,9 +2130,13 @@ def _check(res, nucleus):
         _check(special.factorialk(n, 3, exact=exact), exp_nucleus[3])
 
     @pytest.mark.parametrize("exact", [True, False])
+    @pytest.mark.parametrize("dtype", [
+        None, int, np.int8, np.int16, np.int32, np.int64,
+        np.uint8, np.uint16, np.uint32, np.uint64
+    ])
     @pytest.mark.parametrize("dim", range(0, 5))
-    def test_factorialx_array_dimension(self, dim, exact):
-        n = np.array(5, ndmin=dim)
+    def test_factorialx_array_dimension(self, dim, dtype, exact):
+        n = np.array(5, dtype=dtype, ndmin=dim)
         exp = {1: 120, 2: 15, 3: 10}
         assert_allclose(special.factorial(n, exact=exact),
                         np.array(exp[1], ndmin=dim))