From 670a147cf73760b2400b16022609bf57771e0a6a Mon Sep 17 00:00:00 2001
From: Joris Vaillant <joris.vaillant@inria.fr>
Date: Wed, 3 Apr 2024 14:23:46 +0200
Subject: [PATCH] int_conv: Fix specific scalar conversion and support more
 scalar type

int_conv: Add more integer type binding and add a unit test

changelog: Add changelog entry

int_conv: Manage int8_t and long on Windows

int_conv: Try to use stdint type to manage long hack on windows

int_conv: Fix Windows build

int_conv: Try to fix Mac build and don't compile twice 64 bits integer on Mac and Windows

int_conv: Don't use np.dtypes (introduced in numpy 1.25)

int_conv: Test long and int difference in unit test

[pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci

int_conv: Expose long in MacOS

int_conv: Don't use deprecated type np.intc

int_conv: Fix Mac build

int_conv: Remove int128 (doesn't exists)

int_conv: Disable long long test on Mac because the type is not reachable

int_conv: Begin to work on promotion

int_conv: Add unsigned promotion

int_conv: Manage complex promotion

int_conv: Split matrix binding to avoid memory issue while building

int_conv: Split decomposition binding to avoid memory issue while building

int_conv: Use clongdouble instead of complex256

int_conv: Avoid out of heap space error on Windows

int_conv: Fix test for Mac

int_conv: Try to manage longlong on Mac and int on Windows, also reduce compile time

int_conv: Manage Windows and Mac int and long long when casting

int_conv: Allow scalar to complex conversion

int_conv: Manage casting from numpy to eigen with tensor

int_conv: Add changelog entries

int_conv: Remove conversion from Eigen to Numpy

int_conv: Add reference to this PR

int_conv: Activate SciPy tests by default
---
 .github/workflows/windows-conda.yml           |   2 +-
 CHANGELOG.md                                  |  10 +
 CMakeLists.txt                                |  30 ++-
 include/eigenpy/eigen-allocator.hpp           | 241 +++++++-----------
 include/eigenpy/numpy-type.hpp                |  56 +++-
 include/eigenpy/numpy.hpp                     | 114 +++++++--
 include/eigenpy/scalar-conversion.hpp         |  15 +-
 src/decompositions/decompositions.cpp         |  40 ++-
 src/decompositions/eigen-solver.cpp           |  13 +
 src/decompositions/ldlt-solver.cpp            |  12 +
 src/decompositions/llt-solver.cpp             |  12 +
 src/decompositions/minres-solver.cpp          |  12 +
 src/decompositions/permutation-matrix.cpp     |  12 +
 src/decompositions/seigen-solver.cpp          |  13 +
 .../self-adjoint-eigen-solver.cpp             |  13 +
 src/decompositions/simplicial-ldlt-solver.cpp |  13 +
 src/decompositions/simplicial-llt-solver.cpp  |  13 +
 src/eigenpy.cpp                               |  34 ++-
 src/matrix-char.cpp                           |  14 +
 src/matrix-int.cpp                            |  12 -
 src/matrix-int16.cpp                          |  14 +
 src/matrix-int32.cpp                          |  14 +
 src/matrix-int64.cpp                          |  14 +
 src/matrix-int8.cpp                           |  14 +
 src/matrix-linux-long-long.cpp                |  15 ++
 src/matrix-linux-ulong-long.cpp               |  15 ++
 src/matrix-mac-long.cpp                       |  15 ++
 src/matrix-mac-ulong.cpp                      |  15 ++
 src/matrix-uint16.cpp                         |  14 +
 src/matrix-uint32.cpp                         |  14 +
 src/matrix-uint64.cpp                         |  14 +
 src/matrix-uint8.cpp                          |  14 +
 ...atrix-long.cpp => matrix-windows-long.cpp} |  10 +-
 src/matrix-windows-ulong.cpp                  |  15 ++
 unittest/matrix.cpp                           |  40 +++
 unittest/python/test_matrix.py                | 166 ++++++++++++
 unittest/python/test_user_type.py             |   2 +-
 37 files changed, 849 insertions(+), 237 deletions(-)
 create mode 100644 src/decompositions/eigen-solver.cpp
 create mode 100644 src/decompositions/ldlt-solver.cpp
 create mode 100644 src/decompositions/llt-solver.cpp
 create mode 100644 src/decompositions/minres-solver.cpp
 create mode 100644 src/decompositions/permutation-matrix.cpp
 create mode 100644 src/decompositions/seigen-solver.cpp
 create mode 100644 src/decompositions/self-adjoint-eigen-solver.cpp
 create mode 100644 src/decompositions/simplicial-ldlt-solver.cpp
 create mode 100644 src/decompositions/simplicial-llt-solver.cpp
 create mode 100644 src/matrix-char.cpp
 delete mode 100644 src/matrix-int.cpp
 create mode 100644 src/matrix-int16.cpp
 create mode 100644 src/matrix-int32.cpp
 create mode 100644 src/matrix-int64.cpp
 create mode 100644 src/matrix-int8.cpp
 create mode 100644 src/matrix-linux-long-long.cpp
 create mode 100644 src/matrix-linux-ulong-long.cpp
 create mode 100644 src/matrix-mac-long.cpp
 create mode 100644 src/matrix-mac-ulong.cpp
 create mode 100644 src/matrix-uint16.cpp
 create mode 100644 src/matrix-uint32.cpp
 create mode 100644 src/matrix-uint64.cpp
 create mode 100644 src/matrix-uint8.cpp
 rename src/{matrix-long.cpp => matrix-windows-long.cpp} (51%)
 create mode 100644 src/matrix-windows-ulong.cpp

diff --git a/.github/workflows/windows-conda.yml b/.github/workflows/windows-conda.yml
index b3f8ad81..6315c0f4 100644
--- a/.github/workflows/windows-conda.yml
+++ b/.github/workflows/windows-conda.yml
@@ -58,7 +58,7 @@ jobs:
         if errorlevel 1 exit 1
 
         :: Build and Install
-        cmake --build . --config Release --target install
+        cmake --build . -j3 --config Release --target install
         if errorlevel 1 exit 1
 
         :: Testing
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 18791046..12eeb536 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -9,11 +9,21 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
 ### Added
 - Allow use of installed JRL-cmakemodule ([#446](https://github.com/stack-of-tasks/eigenpy/pull/446)
 - Support of Numpy 2.0.0b1 ([#448](https://github.com/stack-of-tasks/eigenpy/pull/448))
+- Support new primitive type (char, int8_t, uint8_t, int16_t, uint16_t, uint32_t, uint64_t) ([#455]()https://github.com/stack-of-tasks/eigenpy/pull/455)
+- Support conversion between signed <-> unsigned integers ([#455](https://github.com/stack-of-tasks/eigenpy/pull/455))
+- Support conversion between complex numbers ([#455](https://github.com/stack-of-tasks/eigenpy/pull/455))
 
 ### Fixed
 - Fix unit test build in C++11 ([#442](https://github.com/stack-of-tasks/eigenpy/pull/442))
 - Fix unit test function signature [#443](https://github.com/stack-of-tasks/eigenpy/pull/443))
 - Fix CMake export ([#446](https://github.com/stack-of-tasks/eigenpy/pull/446)
+- Fix `int` management on Windows ([#455](https://github.com/stack-of-tasks/eigenpy/pull/455))
+- Fix `long long` management on Mac ([#455](https://github.com/stack-of-tasks/eigenpy/pull/455))
+
+### Removed
+- Remove casting when converting from Eigen scalar to Numpy scalar.
+  This should not remove any functionality since Numpy array are created from the Eigen scalar type
+  ([#455](https://github.com/stack-of-tasks/eigenpy/pull/455))
 
 ## [3.4.0] - 2024-02-26
 
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 1fc16e5d..47502c23 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -54,7 +54,7 @@ set(CMAKE_VERBOSE_MAKEFILE True)
 option(INSTALL_DOCUMENTATION "Generate and install the documentation" OFF)
 option(SUFFIX_SO_VERSION "Suffix library name with its version" OFF)
 option(BUILD_TESTING_SCIPY
-       "Build the SciPy tests (scipy should be installed on the machine)" OFF)
+       "Build the SciPy tests (scipy should be installed on the machine)" ON)
 
 include("${JRL_CMAKE_MODULES}/base.cmake")
 compute_project_args(PROJECT_ARGS LANGUAGES CXX)
@@ -267,7 +267,16 @@ set(${PROJECT_NAME}_SOLVERS_SOURCES src/solvers/preconditioners.cpp
                                     src/solvers/solvers.cpp)
 
 set(${PROJECT_NAME}_DECOMPOSITIONS_SOURCES
-    src/decompositions/decompositions.cpp)
+    src/decompositions/decompositions.cpp
+    src/decompositions/eigen-solver.cpp
+    src/decompositions/llt-solver.cpp
+    src/decompositions/ldlt-solver.cpp
+    src/decompositions/minres-solver.cpp
+    src/decompositions/eigen-solver.cpp
+    src/decompositions/self-adjoint-eigen-solver.cpp
+    src/decompositions/permutation-matrix.cpp
+    src/decompositions/simplicial-llt-solver.cpp
+    src/decompositions/simplicial-ldlt-solver.cpp)
 
 if(BUILD_WITH_CHOLMOD_SUPPORT)
   list(APPEND ${PROJECT_NAME}_DECOMPOSITIONS_SOURCES
@@ -294,8 +303,21 @@ set(${PROJECT_NAME}_SOURCES
     src/matrix-long-double.cpp
     src/matrix-complex-long-double.cpp
     src/matrix-bool.cpp
-    src/matrix-int.cpp
-    src/matrix-long.cpp
+    src/matrix-char.cpp
+    src/matrix-int8.cpp
+    src/matrix-uint8.cpp
+    src/matrix-int16.cpp
+    src/matrix-uint16.cpp
+    src/matrix-int32.cpp
+    src/matrix-uint32.cpp
+    src/matrix-windows-long.cpp
+    src/matrix-windows-ulong.cpp
+    src/matrix-mac-long.cpp
+    src/matrix-mac-ulong.cpp
+    src/matrix-int64.cpp
+    src/matrix-uint64.cpp
+    src/matrix-linux-long-long.cpp
+    src/matrix-linux-ulong-long.cpp
     src/angle-axis.cpp
     src/quaternion.cpp
     src/geometry-conversion.cpp
diff --git a/include/eigenpy/eigen-allocator.hpp b/include/eigenpy/eigen-allocator.hpp
index 7c31c56f..3b47a9a1 100644
--- a/include/eigenpy/eigen-allocator.hpp
+++ b/include/eigenpy/eigen-allocator.hpp
@@ -198,6 +198,91 @@ struct cast<Scalar, NewScalar, EigenBase, false> {
       mat, NumpyMap<MatType, NewScalar>::map(                                 \
                pyArray, details::check_swap(pyArray, mat)))
 
+// Define specific cast for Windows and Mac
+#if defined _WIN32 || defined __CYGWIN__
+// Manage NPY_INT on Windows (NPY_INT32 is NPY_LONG).
+// See https://github.com/stack-of-tasks/eigenpy/pull/455
+#define EIGENPY_CAST_FROM_NUMPY_TO_EIGEN_SWITCH_OS_SPECIFIC( \
+    MatType, Scalar, pyArray, mat, CAST_MACRO)               \
+  case NPY_INT:                                              \
+    CAST_MACRO(MatType, int32_t, Scalar, pyArray, mat);      \
+    break;                                                   \
+  case NPY_UINT:                                             \
+    CAST_MACRO(MatType, uint32_t, Scalar, pyArray, mat);     \
+    break;
+#elif defined __APPLE__
+// Manage NPY_LONGLONG on Mac (NPY_INT64 is NPY_LONG).
+// long long and long are both the same type
+// but NPY_LONGLONG and NPY_LONGĂ‚ are different dtype.
+// See https://github.com/stack-of-tasks/eigenpy/pull/455
+#define EIGENPY_CAST_FROM_NUMPY_TO_EIGEN_SWITCH_OS_SPECIFIC( \
+    MatType, Scalar, pyArray, mat, CAST_MACRO)               \
+  case NPY_LONGLONG:                                         \
+    CAST_MACRO(MatType, int64_t, Scalar, pyArray, mat);      \
+    break;                                                   \
+  case NPY_ULONGLONG:                                        \
+    CAST_MACRO(MatType, uint64_t, Scalar, pyArray, mat);     \
+    break;
+#else
+#define EIGENPY_CAST_FROM_NUMPY_TO_EIGEN_SWITCH_OS_SPECIFIC( \
+    MatType, Scalar, pyArray, mat, CAST_MACRO)
+#endif
+
+/// Define casting between Numpy matrix type to Eigen type.
+#define EIGENPY_CAST_FROM_NUMPY_TO_EIGEN_SWITCH(                               \
+    pyArray_type_code, MatType, Scalar, pyArray, mat, CAST_MACRO)              \
+  switch (pyArray_type_code) {                                                 \
+    case NPY_BOOL:                                                             \
+      CAST_MACRO(MatType, bool, Scalar, pyArray, mat);                         \
+      break;                                                                   \
+    case NPY_INT8:                                                             \
+      CAST_MACRO(MatType, int8_t, Scalar, pyArray, mat);                       \
+      break;                                                                   \
+    case NPY_INT16:                                                            \
+      CAST_MACRO(MatType, int16_t, Scalar, pyArray, mat);                      \
+      break;                                                                   \
+    case NPY_INT32:                                                            \
+      CAST_MACRO(MatType, int32_t, Scalar, pyArray, mat);                      \
+      break;                                                                   \
+    case NPY_INT64:                                                            \
+      CAST_MACRO(MatType, int64_t, Scalar, pyArray, mat);                      \
+      break;                                                                   \
+    case NPY_UINT8:                                                            \
+      CAST_MACRO(MatType, uint8_t, Scalar, pyArray, mat);                      \
+      break;                                                                   \
+    case NPY_UINT16:                                                           \
+      CAST_MACRO(MatType, uint16_t, Scalar, pyArray, mat);                     \
+      break;                                                                   \
+    case NPY_UINT32:                                                           \
+      CAST_MACRO(MatType, uint32_t, Scalar, pyArray, mat);                     \
+      break;                                                                   \
+    case NPY_UINT64:                                                           \
+      CAST_MACRO(MatType, uint64_t, Scalar, pyArray, mat);                     \
+      break;                                                                   \
+    case NPY_FLOAT:                                                            \
+      CAST_MACRO(MatType, float, Scalar, pyArray, mat);                        \
+      break;                                                                   \
+    case NPY_CFLOAT:                                                           \
+      CAST_MACRO(MatType, std::complex<float>, Scalar, pyArray, mat);          \
+      break;                                                                   \
+    case NPY_DOUBLE:                                                           \
+      CAST_MACRO(MatType, double, Scalar, pyArray, mat);                       \
+      break;                                                                   \
+    case NPY_CDOUBLE:                                                          \
+      CAST_MACRO(MatType, std::complex<double>, Scalar, pyArray, mat);         \
+      break;                                                                   \
+    case NPY_LONGDOUBLE:                                                       \
+      CAST_MACRO(MatType, long double, Scalar, pyArray, mat);                  \
+      break;                                                                   \
+    case NPY_CLONGDOUBLE:                                                      \
+      CAST_MACRO(MatType, std::complex<long double>, Scalar, pyArray, mat);    \
+      break;                                                                   \
+      EIGENPY_CAST_FROM_NUMPY_TO_EIGEN_SWITCH_OS_SPECIFIC(                     \
+          MatType, Scalar, pyArray, mat, CAST_MACRO)                           \
+    default:                                                                   \
+      throw Exception("You asked for a conversion which is not implemented."); \
+  }
+
 template <typename EigenType>
 struct EigenAllocator;
 
@@ -247,43 +332,9 @@ struct eigen_allocator_impl_matrix {
           pyArray, details::check_swap(pyArray, mat));  // avoid useless cast
       return;
     }
-
-    switch (pyArray_type_code) {
-      case NPY_INT:
-        EIGENPY_CAST_FROM_PYARRAY_TO_EIGEN_MATRIX(MatType, int, Scalar, pyArray,
-                                                  mat);
-        break;
-      case NPY_LONG:
-        EIGENPY_CAST_FROM_PYARRAY_TO_EIGEN_MATRIX(MatType, long, Scalar,
-                                                  pyArray, mat);
-        break;
-      case NPY_FLOAT:
-        EIGENPY_CAST_FROM_PYARRAY_TO_EIGEN_MATRIX(MatType, float, Scalar,
-                                                  pyArray, mat);
-        break;
-      case NPY_CFLOAT:
-        EIGENPY_CAST_FROM_PYARRAY_TO_EIGEN_MATRIX(MatType, std::complex<float>,
-                                                  Scalar, pyArray, mat);
-        break;
-      case NPY_DOUBLE:
-        EIGENPY_CAST_FROM_PYARRAY_TO_EIGEN_MATRIX(MatType, double, Scalar,
-                                                  pyArray, mat);
-        break;
-      case NPY_CDOUBLE:
-        EIGENPY_CAST_FROM_PYARRAY_TO_EIGEN_MATRIX(MatType, std::complex<double>,
-                                                  Scalar, pyArray, mat);
-        break;
-      case NPY_LONGDOUBLE:
-        EIGENPY_CAST_FROM_PYARRAY_TO_EIGEN_MATRIX(MatType, long double, Scalar,
-                                                  pyArray, mat);
-        break;
-      case NPY_CLONGDOUBLE:
-        EIGENPY_CAST_FROM_PYARRAY_TO_EIGEN_MATRIX(
-            MatType, std::complex<long double>, Scalar, pyArray, mat);
-        break;
-      default:
-        throw Exception("You asked for a conversion which is not implemented.");
-    }
+    EIGENPY_CAST_FROM_NUMPY_TO_EIGEN_SWITCH(
+        pyArray_type_code, MatType, Scalar, pyArray, mat,
+        EIGENPY_CAST_FROM_PYARRAY_TO_EIGEN_MATRIX);
   }
 
   /// \brief Copy mat into the Python array using Eigen::Map
@@ -301,43 +352,8 @@ struct eigen_allocator_impl_matrix {
                                      details::check_swap(pyArray, mat)) = mat;
       return;
     }
-
-    switch (pyArray_type_code) {
-      case NPY_INT:
-        EIGENPY_CAST_FROM_EIGEN_MATRIX_TO_PYARRAY(MatType, Scalar, int, mat,
-                                                  pyArray);
-        break;
-      case NPY_LONG:
-        EIGENPY_CAST_FROM_EIGEN_MATRIX_TO_PYARRAY(MatType, Scalar, long, mat,
-                                                  pyArray);
-        break;
-      case NPY_FLOAT:
-        EIGENPY_CAST_FROM_EIGEN_MATRIX_TO_PYARRAY(MatType, Scalar, float, mat,
-                                                  pyArray);
-        break;
-      case NPY_CFLOAT:
-        EIGENPY_CAST_FROM_EIGEN_MATRIX_TO_PYARRAY(
-            MatType, Scalar, std::complex<float>, mat, pyArray);
-        break;
-      case NPY_DOUBLE:
-        EIGENPY_CAST_FROM_EIGEN_MATRIX_TO_PYARRAY(MatType, Scalar, double, mat,
-                                                  pyArray);
-        break;
-      case NPY_CDOUBLE:
-        EIGENPY_CAST_FROM_EIGEN_MATRIX_TO_PYARRAY(
-            MatType, Scalar, std::complex<double>, mat, pyArray);
-        break;
-      case NPY_LONGDOUBLE:
-        EIGENPY_CAST_FROM_EIGEN_MATRIX_TO_PYARRAY(MatType, Scalar, long double,
-                                                  mat, pyArray);
-        break;
-      case NPY_CLONGDOUBLE:
-        EIGENPY_CAST_FROM_EIGEN_MATRIX_TO_PYARRAY(
-            MatType, Scalar, std::complex<long double>, mat, pyArray);
-        break;
-      default:
-        throw Exception("You asked for a conversion which is not implemented.");
-    }
+    throw Exception(
+        "Scalar conversion from Eigen to Numpy is not implemented.");
   }
 };
 
@@ -394,42 +410,9 @@ struct eigen_allocator_impl_tensor {
       return;
     }
 
-    switch (pyArray_type_code) {
-      case NPY_INT:
-        EIGENPY_CAST_FROM_PYARRAY_TO_EIGEN_TENSOR(TensorType, int, Scalar,
-                                                  pyArray, tensor);
-        break;
-      case NPY_LONG:
-        EIGENPY_CAST_FROM_PYARRAY_TO_EIGEN_TENSOR(TensorType, long, Scalar,
-                                                  pyArray, tensor);
-        break;
-      case NPY_FLOAT:
-        EIGENPY_CAST_FROM_PYARRAY_TO_EIGEN_TENSOR(TensorType, float, Scalar,
-                                                  pyArray, tensor);
-        break;
-      case NPY_CFLOAT:
-        EIGENPY_CAST_FROM_PYARRAY_TO_EIGEN_TENSOR(
-            TensorType, std::complex<float>, Scalar, pyArray, tensor);
-        break;
-      case NPY_DOUBLE:
-        EIGENPY_CAST_FROM_PYARRAY_TO_EIGEN_TENSOR(TensorType, double, Scalar,
-                                                  pyArray, tensor);
-        break;
-      case NPY_CDOUBLE:
-        EIGENPY_CAST_FROM_PYARRAY_TO_EIGEN_TENSOR(
-            TensorType, std::complex<double>, Scalar, pyArray, tensor);
-        break;
-      case NPY_LONGDOUBLE:
-        EIGENPY_CAST_FROM_PYARRAY_TO_EIGEN_TENSOR(TensorType, long double,
-                                                  Scalar, pyArray, tensor);
-        break;
-      case NPY_CLONGDOUBLE:
-        EIGENPY_CAST_FROM_PYARRAY_TO_EIGEN_TENSOR(
-            TensorType, std::complex<long double>, Scalar, pyArray, tensor);
-        break;
-      default:
-        throw Exception("You asked for a conversion which is not implemented.");
-    }
+    EIGENPY_CAST_FROM_NUMPY_TO_EIGEN_SWITCH(
+        pyArray_type_code, TensorType, Scalar, pyArray, tensor,
+        EIGENPY_CAST_FROM_PYARRAY_TO_EIGEN_TENSOR);
   }
 
 #define EIGENPY_CAST_FROM_EIGEN_TENSOR_TO_PYARRAY(TensorType, Scalar,         \
@@ -454,42 +437,8 @@ struct eigen_allocator_impl_tensor {
       return;
     }
 
-    switch (pyArray_type_code) {
-      case NPY_INT:
-        EIGENPY_CAST_FROM_EIGEN_TENSOR_TO_PYARRAY(TensorType, Scalar, int,
-                                                  tensor, pyArray);
-        break;
-      case NPY_LONG:
-        EIGENPY_CAST_FROM_EIGEN_TENSOR_TO_PYARRAY(TensorType, Scalar, long,
-                                                  tensor, pyArray);
-        break;
-      case NPY_FLOAT:
-        EIGENPY_CAST_FROM_EIGEN_TENSOR_TO_PYARRAY(TensorType, Scalar, float,
-                                                  tensor, pyArray);
-        break;
-      case NPY_CFLOAT:
-        EIGENPY_CAST_FROM_EIGEN_TENSOR_TO_PYARRAY(
-            TensorType, Scalar, std::complex<float>, tensor, pyArray);
-        break;
-      case NPY_DOUBLE:
-        EIGENPY_CAST_FROM_EIGEN_TENSOR_TO_PYARRAY(TensorType, Scalar, double,
-                                                  tensor, pyArray);
-        break;
-      case NPY_CDOUBLE:
-        EIGENPY_CAST_FROM_EIGEN_TENSOR_TO_PYARRAY(
-            TensorType, Scalar, std::complex<double>, tensor, pyArray);
-        break;
-      case NPY_LONGDOUBLE:
-        EIGENPY_CAST_FROM_EIGEN_TENSOR_TO_PYARRAY(TensorType, Scalar,
-                                                  long double, tensor, pyArray);
-        break;
-      case NPY_CLONGDOUBLE:
-        EIGENPY_CAST_FROM_EIGEN_TENSOR_TO_PYARRAY(
-            TensorType, Scalar, std::complex<long double>, tensor, pyArray);
-        break;
-      default:
-        throw Exception("You asked for a conversion which is not implemented.");
-    }
+    throw Exception(
+        "Scalar conversion from Eigen to Numpy is not implemented.");
   }
 };
 #endif
diff --git a/include/eigenpy/numpy-type.hpp b/include/eigenpy/numpy-type.hpp
index 25dee99f..c57a914d 100644
--- a/include/eigenpy/numpy-type.hpp
+++ b/include/eigenpy/numpy-type.hpp
@@ -17,26 +17,54 @@ namespace eigenpy {
 
 template <typename Scalar>
 bool np_type_is_convertible_into_scalar(const int np_type) {
-  if (static_cast<NPY_TYPES>(NumpyEquivalentType<Scalar>::type_code) >=
-      NPY_USERDEF)
+  const auto scalar_np_code =
+      static_cast<NPY_TYPES>(NumpyEquivalentType<Scalar>::type_code);
+
+  if (scalar_np_code >= NPY_USERDEF)
     return np_type == Register::getTypeCode<Scalar>();
 
-  if (NumpyEquivalentType<Scalar>::type_code == np_type) return true;
+  if (scalar_np_code == np_type) return true;
 
+  // Manage type promotion
   switch (np_type) {
-#ifdef WIN32
-    case NPY_INT:
-    case NPY_LONG:
-      return FromTypeToType<int, Scalar>::value;
-    case NPY_LONGLONG:
-      return FromTypeToType<__int64, Scalar>::value;
-#else
+    case NPY_BOOL:
+      return FromTypeToType<bool, Scalar>::value;
+    case NPY_INT8:
+      return FromTypeToType<int8_t, Scalar>::value;
+    case NPY_INT16:
+      return FromTypeToType<int16_t, Scalar>::value;
+    case NPY_INT32:
+      return FromTypeToType<int32_t, Scalar>::value;
+    case NPY_INT64:
+      return FromTypeToType<int64_t, Scalar>::value;
+    case NPY_UINT8:
+      return FromTypeToType<uint8_t, Scalar>::value;
+    case NPY_UINT16:
+      return FromTypeToType<uint16_t, Scalar>::value;
+    case NPY_UINT32:
+      return FromTypeToType<uint32_t, Scalar>::value;
+    case NPY_UINT64:
+      return FromTypeToType<uint64_t, Scalar>::value;
+
+#if defined _WIN32 || defined __CYGWIN__
+    // Manage NPY_INT on Windows (NPY_INT32 is NPY_LONG).
+    // See https://github.com/stack-of-tasks/eigenpy/pull/455
     case NPY_INT:
-      return FromTypeToType<int, Scalar>::value;
-    case NPY_LONG:
+      return FromTypeToType<int32_t, Scalar>::value;
+    case NPY_UINT:
+      return FromTypeToType<uint32_t, Scalar>::value;
+#endif  // WIN32
+
+#if defined __APPLE__
+    // Manage NPY_LONGLONG on Mac (NPY_INT64 is NPY_LONG)..
+    // long long and long are both the same type
+    // but NPY_LONGLONG and NPY_LONGĂ‚ are different dtype.
+    // See https://github.com/stack-of-tasks/eigenpy/pull/455
     case NPY_LONGLONG:
-      return FromTypeToType<long, Scalar>::value;
-#endif
+      return FromTypeToType<int64_t, Scalar>::value;
+    case NPY_ULONGLONG:
+      return FromTypeToType<uint64_t, Scalar>::value;
+#endif  // MAC
     case NPY_FLOAT:
       return FromTypeToType<float, Scalar>::value;
     case NPY_CFLOAT:
diff --git a/include/eigenpy/numpy.hpp b/include/eigenpy/numpy.hpp
index 3529ec25..3786b091 100644
--- a/include/eigenpy/numpy.hpp
+++ b/include/eigenpy/numpy.hpp
@@ -61,57 +61,119 @@ struct NumpyEquivalentType {
 };
 
 template <>
-struct NumpyEquivalentType<float> {
-  enum { type_code = NPY_FLOAT };
+struct NumpyEquivalentType<bool> {
+  enum { type_code = NPY_BOOL };
 };
+
 template <>
-struct NumpyEquivalentType<std::complex<float> > {
-  enum { type_code = NPY_CFLOAT };
+struct NumpyEquivalentType<char> {
+  enum { type_code = NPY_INT8 };
 };
 template <>
-struct NumpyEquivalentType<double> {
-  enum { type_code = NPY_DOUBLE };
+struct NumpyEquivalentType<unsigned char> {
+  enum { type_code = NPY_UINT8 };
 };
 template <>
-struct NumpyEquivalentType<std::complex<double> > {
-  enum { type_code = NPY_CDOUBLE };
+struct NumpyEquivalentType<int8_t> {
+  enum { type_code = NPY_INT8 };
 };
+
 template <>
-struct NumpyEquivalentType<long double> {
-  enum { type_code = NPY_LONGDOUBLE };
+struct NumpyEquivalentType<int16_t> {
+  enum { type_code = NPY_INT16 };
 };
 template <>
-struct NumpyEquivalentType<std::complex<long double> > {
-  enum { type_code = NPY_CLONGDOUBLE };
+struct NumpyEquivalentType<uint16_t> {
+  enum { type_code = NPY_UINT16 };
+};
+
+template <>
+struct NumpyEquivalentType<int32_t> {
+  enum { type_code = NPY_INT32 };
 };
 template <>
-struct NumpyEquivalentType<bool> {
-  enum { type_code = NPY_BOOL };
+struct NumpyEquivalentType<uint32_t> {
+  enum { type_code = NPY_UINT32 };
 };
+
+// On Windows, long is a 32 bytes type but it's a different type than int
+// See https://github.com/stack-of-tasks/eigenpy/pull/455
+#if defined _WIN32 || defined __CYGWIN__
+
 template <>
-struct NumpyEquivalentType<int> {
-  enum { type_code = NPY_INT };
+struct NumpyEquivalentType<long> {
+  enum { type_code = NPY_INT32 };
 };
 template <>
-struct NumpyEquivalentType<unsigned int> {
-  enum { type_code = NPY_UINT };
+struct NumpyEquivalentType<unsigned long> {
+  enum { type_code = NPY_UINT32 };
 };
+
+#endif  // WIN32
+
+template <>
+struct NumpyEquivalentType<int64_t> {
+  enum { type_code = NPY_INT64 };
+};
+template <>
+struct NumpyEquivalentType<uint64_t> {
+  enum { type_code = NPY_UINT64 };
+};
+
+// On Mac, long is a 64 bytes type but it's a different type than int64_t
+// See https://github.com/stack-of-tasks/eigenpy/pull/455
+#if defined __APPLE__
+
 template <>
 struct NumpyEquivalentType<long> {
-  enum { type_code = NPY_LONG };
+  enum { type_code = NPY_INT64 };
 };
-// #if defined _WIN32 || defined __CYGWIN__
+template <>
+struct NumpyEquivalentType<unsigned long> {
+  enum { type_code = NPY_UINT64 };
+};
+
+#endif  // MAC
+
+// On Linux, long long is a 64 bytes type but it's a different type than int64_t
+// See https://github.com/stack-of-tasks/eigenpy/pull/455
+#if defined __linux__
+
 template <>
 struct NumpyEquivalentType<long long> {
   enum { type_code = NPY_LONGLONG };
 };
-// #else
-//   template <> struct NumpyEquivalentType<long long>    { enum { type_code =
-//   NPY_LONGLONG    };};
-// #endif
 template <>
-struct NumpyEquivalentType<unsigned long> {
-  enum { type_code = NPY_ULONG };
+struct NumpyEquivalentType<unsigned long long> {
+  enum { type_code = NPY_ULONGLONG };
+};
+
+#endif  // Linux
+
+template <>
+struct NumpyEquivalentType<float> {
+  enum { type_code = NPY_FLOAT };
+};
+template <>
+struct NumpyEquivalentType<double> {
+  enum { type_code = NPY_DOUBLE };
+};
+template <>
+struct NumpyEquivalentType<long double> {
+  enum { type_code = NPY_LONGDOUBLE };
+};
+
+template <>
+struct NumpyEquivalentType<std::complex<float> > {
+  enum { type_code = NPY_CFLOAT };
+};
+template <>
+struct NumpyEquivalentType<std::complex<double> > {
+  enum { type_code = NPY_CDOUBLE };
+};
+template <>
+struct NumpyEquivalentType<std::complex<long double> > {
+  enum { type_code = NPY_CLONGDOUBLE };
 };
 
 template <typename Scalar>
diff --git a/include/eigenpy/scalar-conversion.hpp b/include/eigenpy/scalar-conversion.hpp
index 8a8532d9..18a50a21 100644
--- a/include/eigenpy/scalar-conversion.hpp
+++ b/include/eigenpy/scalar-conversion.hpp
@@ -7,11 +7,24 @@
 
 #include "eigenpy/config.hpp"
 #include <boost/numeric/conversion/conversion_traits.hpp>
+#include <complex>
 
 namespace eigenpy {
+
 template <typename Source, typename Target>
 struct FromTypeToType
-    : public boost::numeric::conversion_traits<Source, Target>::subranged {};
+    : public boost::mpl::if_c<std::is_same<Source, Target>::value,
+                              std::true_type,
+                              typename boost::numeric::conversion_traits<
+                                  Source, Target>::subranged>::type {};
+
+/// FromTypeToType specialization to manage std::complex
+template <typename ScalarSource, typename ScalarTarget>
+struct FromTypeToType<std::complex<ScalarSource>, std::complex<ScalarTarget> >
+    : public boost::mpl::if_c<
+          std::is_same<ScalarSource, ScalarTarget>::value, std::true_type,
+          typename boost::numeric::conversion_traits<
+              ScalarSource, ScalarTarget>::subranged>::type {};
 
 }  // namespace eigenpy
 
diff --git a/src/decompositions/decompositions.cpp b/src/decompositions/decompositions.cpp
index 2b58c03d..a497db1e 100644
--- a/src/decompositions/decompositions.cpp
+++ b/src/decompositions/decompositions.cpp
@@ -4,29 +4,27 @@
 
 #include "eigenpy/decompositions/decompositions.hpp"
 
-#include "eigenpy/decompositions/EigenSolver.hpp"
-#include "eigenpy/decompositions/LDLT.hpp"
-#include "eigenpy/decompositions/LLT.hpp"
-#include "eigenpy/decompositions/PermutationMatrix.hpp"
-#include "eigenpy/decompositions/sparse/LLT.hpp"
-#include "eigenpy/decompositions/sparse/LDLT.hpp"
-#include "eigenpy/decompositions/SelfAdjointEigenSolver.hpp"
-#include "eigenpy/decompositions/minres.hpp"
 #include "eigenpy/fwd.hpp"
 
 namespace eigenpy {
-void exposeDecompositions() {
-  using namespace Eigen;
 
-  typedef Eigen::SparseMatrix<double, Eigen::ColMajor> ColMajorSparseMatrix;
-  //  typedef Eigen::SparseMatrix<double,Eigen::RowMajor> RowMajorSparseMatrix;
+void exposeEigenSolver();
+void exposeSelfAdjointEigenSolver();
+void exposeLLTSolver();
+void exposeLDLTSolver();
+void exposeMINRESSolver();
+void exposeSimplicialLLTSolver();
+void exposeSimplicialLDLTSolver();
+void exposePermutationMatrix();
 
-  EigenSolverVisitor<MatrixXd>::expose("EigenSolver");
-  SelfAdjointEigenSolverVisitor<MatrixXd>::expose("SelfAdjointEigenSolver");
-  LLTSolverVisitor<MatrixXd>::expose("LLT");
-  LDLTSolverVisitor<MatrixXd>::expose("LDLT");
+void exposeDecompositions() {
+  using namespace Eigen;
 
-  MINRESSolverVisitor<MatrixXd>::expose("MINRES");
+  exposeEigenSolver();
+  exposeSelfAdjointEigenSolver();
+  exposeLLTSolver();
+  exposeLDLTSolver();
+  exposeMINRESSolver();
 
   {
     bp::enum_<DecompositionOptions>("DecompositionOptions")
@@ -42,12 +40,10 @@ void exposeDecompositions() {
   }
 
   // Expose sparse decompositions
-  {
-    SimplicialLLTVisitor<ColMajorSparseMatrix>::expose("SimplicialLLT");
-    SimplicialLDLTVisitor<ColMajorSparseMatrix>::expose("SimplicialLDLT");
-  }
+  exposeSimplicialLLTSolver();
+  exposeSimplicialLDLTSolver();
 
-  PermutationMatrixVisitor<Eigen::Dynamic>::expose("PermutationMatrix");
+  exposePermutationMatrix();
 
 #ifdef EIGENPY_WITH_CHOLMOD_SUPPORT
   exposeCholmod();
diff --git a/src/decompositions/eigen-solver.cpp b/src/decompositions/eigen-solver.cpp
new file mode 100644
index 00000000..8fdfbf4a
--- /dev/null
+++ b/src/decompositions/eigen-solver.cpp
@@ -0,0 +1,13 @@
+
+/*
+ * Copyright 2024 INRIA
+ */
+
+#include "eigenpy/decompositions/EigenSolver.hpp"
+
+namespace eigenpy {
+void exposeEigenSolver() {
+  using namespace Eigen;
+  EigenSolverVisitor<MatrixXd>::expose("EigenSolver");
+}
+}  // namespace eigenpy
diff --git a/src/decompositions/ldlt-solver.cpp b/src/decompositions/ldlt-solver.cpp
new file mode 100644
index 00000000..6f3a2a60
--- /dev/null
+++ b/src/decompositions/ldlt-solver.cpp
@@ -0,0 +1,12 @@
+/*
+ * Copyright 2024 INRIA
+ */
+
+#include "eigenpy/decompositions/LDLT.hpp"
+
+namespace eigenpy {
+void exposeLDLTSolver() {
+  using namespace Eigen;
+  LDLTSolverVisitor<MatrixXd>::expose("LDLT");
+}
+}  // namespace eigenpy
diff --git a/src/decompositions/llt-solver.cpp b/src/decompositions/llt-solver.cpp
new file mode 100644
index 00000000..a58db9b4
--- /dev/null
+++ b/src/decompositions/llt-solver.cpp
@@ -0,0 +1,12 @@
+/*
+ * Copyright 2024 INRIA
+ */
+
+#include "eigenpy/decompositions/LLT.hpp"
+
+namespace eigenpy {
+void exposeLLTSolver() {
+  using namespace Eigen;
+  LLTSolverVisitor<MatrixXd>::expose("LLT");
+}
+}  // namespace eigenpy
diff --git a/src/decompositions/minres-solver.cpp b/src/decompositions/minres-solver.cpp
new file mode 100644
index 00000000..44a12cea
--- /dev/null
+++ b/src/decompositions/minres-solver.cpp
@@ -0,0 +1,12 @@
+/*
+ * Copyright 2024 INRIA
+ */
+
+#include "eigenpy/decompositions/minres.hpp"
+
+namespace eigenpy {
+void exposeMINRESSolver() {
+  using namespace Eigen;
+  MINRESSolverVisitor<MatrixXd>::expose("MINRES");
+}
+}  // namespace eigenpy
diff --git a/src/decompositions/permutation-matrix.cpp b/src/decompositions/permutation-matrix.cpp
new file mode 100644
index 00000000..082ac809
--- /dev/null
+++ b/src/decompositions/permutation-matrix.cpp
@@ -0,0 +1,12 @@
+/*
+ * Copyright 2024 INRIA
+ */
+
+#include "eigenpy/decompositions/PermutationMatrix.hpp"
+
+namespace eigenpy {
+void exposePermutationMatrix() {
+  using namespace Eigen;
+  PermutationMatrixVisitor<Eigen::Dynamic>::expose("PermutationMatrix");
+}
+}  // namespace eigenpy
diff --git a/src/decompositions/seigen-solver.cpp b/src/decompositions/seigen-solver.cpp
new file mode 100644
index 00000000..8fdfbf4a
--- /dev/null
+++ b/src/decompositions/seigen-solver.cpp
@@ -0,0 +1,13 @@
+
+/*
+ * Copyright 2024 INRIA
+ */
+
+#include "eigenpy/decompositions/EigenSolver.hpp"
+
+namespace eigenpy {
+void exposeEigenSolver() {
+  using namespace Eigen;
+  EigenSolverVisitor<MatrixXd>::expose("EigenSolver");
+}
+}  // namespace eigenpy
diff --git a/src/decompositions/self-adjoint-eigen-solver.cpp b/src/decompositions/self-adjoint-eigen-solver.cpp
new file mode 100644
index 00000000..1497f3be
--- /dev/null
+++ b/src/decompositions/self-adjoint-eigen-solver.cpp
@@ -0,0 +1,13 @@
+
+/*
+ * Copyright 2024 INRIA
+ */
+
+#include "eigenpy/decompositions/SelfAdjointEigenSolver.hpp"
+
+namespace eigenpy {
+void exposeSelfAdjointEigenSolver() {
+  using namespace Eigen;
+  SelfAdjointEigenSolverVisitor<MatrixXd>::expose("SelfAdjointEigenSolver");
+}
+}  // namespace eigenpy
diff --git a/src/decompositions/simplicial-ldlt-solver.cpp b/src/decompositions/simplicial-ldlt-solver.cpp
new file mode 100644
index 00000000..c9a3e7b9
--- /dev/null
+++ b/src/decompositions/simplicial-ldlt-solver.cpp
@@ -0,0 +1,13 @@
+/*
+ * Copyright 2024 INRIA
+ */
+
+#include "eigenpy/decompositions/sparse/LDLT.hpp"
+
+namespace eigenpy {
+void exposeSimplicialLDLTSolver() {
+  using namespace Eigen;
+  typedef SparseMatrix<double, ColMajor> ColMajorSparseMatrix;
+  SimplicialLDLTVisitor<ColMajorSparseMatrix>::expose("SimplicialLDLT");
+}
+}  // namespace eigenpy
diff --git a/src/decompositions/simplicial-llt-solver.cpp b/src/decompositions/simplicial-llt-solver.cpp
new file mode 100644
index 00000000..f46e36f2
--- /dev/null
+++ b/src/decompositions/simplicial-llt-solver.cpp
@@ -0,0 +1,13 @@
+/*
+ * Copyright 2024 INRIA
+ */
+
+#include "eigenpy/decompositions/sparse/LLT.hpp"
+
+namespace eigenpy {
+void exposeSimplicialLLTSolver() {
+  using namespace Eigen;
+  typedef SparseMatrix<double, ColMajor> ColMajorSparseMatrix;
+  SimplicialLLTVisitor<ColMajorSparseMatrix>::expose("SimplicialLLT");
+}
+}  // namespace eigenpy
diff --git a/src/eigenpy.cpp b/src/eigenpy.cpp
index 6a74bc6a..cc2dcefb 100644
--- a/src/eigenpy.cpp
+++ b/src/eigenpy.cpp
@@ -12,8 +12,21 @@ namespace eigenpy {
 void seed(unsigned int seed_value) { srand(seed_value); }
 
 void exposeMatrixBool();
-void exposeMatrixInt();
-void exposeMatrixLong();
+void exposeMatrixInt8();
+void exposeMatrixChar();
+void exposeMatrixUInt8();
+void exposeMatrixInt16();
+void exposeMatrixUInt16();
+void exposeMatrixInt32();
+void exposeMatrixUInt32();
+void exposeMatrixWindowsLong();
+void exposeMatrixWindowsULong();
+void exposeMatrixMacLong();
+void exposeMatrixMacULong();
+void exposeMatrixInt64();
+void exposeMatrixUInt64();
+void exposeMatrixLinuxLongLong();
+void exposeMatrixLinuxULongLong();
 void exposeMatrixFloat();
 void exposeMatrixDouble();
 void exposeMatrixLongDouble();
@@ -47,8 +60,21 @@ void enableEigenPy() {
           "seed_value.");
 
   exposeMatrixBool();
-  exposeMatrixInt();
-  exposeMatrixLong();
+  exposeMatrixInt8();
+  exposeMatrixChar();
+  exposeMatrixUInt8();
+  exposeMatrixInt16();
+  exposeMatrixUInt16();
+  exposeMatrixInt32();
+  exposeMatrixUInt32();
+  exposeMatrixWindowsLong();
+  exposeMatrixWindowsULong();
+  exposeMatrixMacLong();
+  exposeMatrixMacULong();
+  exposeMatrixInt64();
+  exposeMatrixUInt64();
+  exposeMatrixLinuxLongLong();
+  exposeMatrixLinuxULongLong();
   exposeMatrixFloat();
   exposeMatrixDouble();
   exposeMatrixLongDouble();
diff --git a/src/matrix-char.cpp b/src/matrix-char.cpp
new file mode 100644
index 00000000..3c1a3a1c
--- /dev/null
+++ b/src/matrix-char.cpp
@@ -0,0 +1,14 @@
+/*
+ * Copyright 2024 INRIA
+ */
+
+#include "eigenpy/eigenpy.hpp"
+
+#include <cstdint>
+
+namespace eigenpy {
+void exposeMatrixChar() {
+  exposeType<char>();
+  exposeType<char, Eigen::RowMajor>();
+}
+}  // namespace eigenpy
diff --git a/src/matrix-int.cpp b/src/matrix-int.cpp
deleted file mode 100644
index 26dbe3ca..00000000
--- a/src/matrix-int.cpp
+++ /dev/null
@@ -1,12 +0,0 @@
-/*
- * Copyright 2020 INRIA
- */
-
-#include "eigenpy/eigenpy.hpp"
-
-namespace eigenpy {
-void exposeMatrixInt() {
-  exposeType<int>();
-  exposeType<int, Eigen::RowMajor>();
-}
-}  // namespace eigenpy
diff --git a/src/matrix-int16.cpp b/src/matrix-int16.cpp
new file mode 100644
index 00000000..8e75cfdf
--- /dev/null
+++ b/src/matrix-int16.cpp
@@ -0,0 +1,14 @@
+/*
+ * Copyright 2024 INRIA
+ */
+
+#include "eigenpy/eigenpy.hpp"
+
+#include <cstdint>
+
+namespace eigenpy {
+void exposeMatrixInt16() {
+  exposeType<int16_t>();
+  exposeType<int16_t, Eigen::RowMajor>();
+}
+}  // namespace eigenpy
diff --git a/src/matrix-int32.cpp b/src/matrix-int32.cpp
new file mode 100644
index 00000000..f6f1bdcb
--- /dev/null
+++ b/src/matrix-int32.cpp
@@ -0,0 +1,14 @@
+/*
+ * Copyright 2020 INRIA
+ */
+
+#include "eigenpy/eigenpy.hpp"
+
+#include <cstdint>
+
+namespace eigenpy {
+void exposeMatrixInt32() {
+  exposeType<int32_t>();
+  exposeType<int32_t, Eigen::RowMajor>();
+}
+}  // namespace eigenpy
diff --git a/src/matrix-int64.cpp b/src/matrix-int64.cpp
new file mode 100644
index 00000000..9bbe6899
--- /dev/null
+++ b/src/matrix-int64.cpp
@@ -0,0 +1,14 @@
+/*
+ * Copyright 2020 INRIA
+ */
+
+#include "eigenpy/eigenpy.hpp"
+
+#include <cstdint>
+
+namespace eigenpy {
+void exposeMatrixInt64() {
+  exposeType<int64_t>();
+  exposeType<int64_t, Eigen::RowMajor>();
+}
+}  // namespace eigenpy
diff --git a/src/matrix-int8.cpp b/src/matrix-int8.cpp
new file mode 100644
index 00000000..7d4ca642
--- /dev/null
+++ b/src/matrix-int8.cpp
@@ -0,0 +1,14 @@
+/*
+ * Copyright 2020 INRIA
+ */
+
+#include "eigenpy/eigenpy.hpp"
+
+#include <cstdint>
+
+namespace eigenpy {
+void exposeMatrixInt8() {
+  exposeType<int8_t>();
+  exposeType<int8_t, Eigen::RowMajor>();
+}
+}  // namespace eigenpy
diff --git a/src/matrix-linux-long-long.cpp b/src/matrix-linux-long-long.cpp
new file mode 100644
index 00000000..7084ea4e
--- /dev/null
+++ b/src/matrix-linux-long-long.cpp
@@ -0,0 +1,15 @@
+/*
+ * Copyright 2024 INRIA
+ */
+
+#include "eigenpy/eigenpy.hpp"
+
+namespace eigenpy {
+void exposeMatrixLinuxLongLong() {
+// On Linux, long long is a 64 bytes type but it's a different type than int64_t
+#ifdef __linux__
+  exposeType<long long>();
+  exposeType<long long, Eigen::RowMajor>();
+#endif  // linux
+}
+}  // namespace eigenpy
diff --git a/src/matrix-linux-ulong-long.cpp b/src/matrix-linux-ulong-long.cpp
new file mode 100644
index 00000000..bac418ee
--- /dev/null
+++ b/src/matrix-linux-ulong-long.cpp
@@ -0,0 +1,15 @@
+/*
+ * Copyright 2024 INRIA
+ */
+
+#include "eigenpy/eigenpy.hpp"
+
+namespace eigenpy {
+void exposeMatrixLinuxULongLong() {
+// On Linux, long long is a 64 bytes type but it's a different type than int64_t
+#ifdef __linux__
+  exposeType<unsigned long long>();
+  exposeType<unsigned long long, Eigen::RowMajor>();
+#endif  // linux
+}
+}  // namespace eigenpy
diff --git a/src/matrix-mac-long.cpp b/src/matrix-mac-long.cpp
new file mode 100644
index 00000000..f2956f36
--- /dev/null
+++ b/src/matrix-mac-long.cpp
@@ -0,0 +1,15 @@
+/*
+ * Copyright 2024 INRIA
+ */
+
+#include "eigenpy/eigenpy.hpp"
+
+namespace eigenpy {
+void exposeMatrixMacLong() {
+// On Mac, long is a 64 bytes type but it's a different type than int64_t
+#ifdef __APPLE__
+  exposeType<long>();
+  exposeType<long, Eigen::RowMajor>();
+#endif  // Mac
+}
+}  // namespace eigenpy
diff --git a/src/matrix-mac-ulong.cpp b/src/matrix-mac-ulong.cpp
new file mode 100644
index 00000000..99374011
--- /dev/null
+++ b/src/matrix-mac-ulong.cpp
@@ -0,0 +1,15 @@
+/*
+ * Copyright 2024 INRIA
+ */
+
+#include "eigenpy/eigenpy.hpp"
+
+namespace eigenpy {
+void exposeMatrixMacULong() {
+// On Mac, long is a 64 bytes type but it's a different type than int64_t
+#ifdef __APPLE__
+  exposeType<unsigned long>();
+  exposeType<unsigned long, Eigen::RowMajor>();
+#endif  // Mac
+}
+}  // namespace eigenpy
diff --git a/src/matrix-uint16.cpp b/src/matrix-uint16.cpp
new file mode 100644
index 00000000..fd89e868
--- /dev/null
+++ b/src/matrix-uint16.cpp
@@ -0,0 +1,14 @@
+/*
+ * Copyright 2024 INRIA
+ */
+
+#include "eigenpy/eigenpy.hpp"
+
+#include <cstdint>
+
+namespace eigenpy {
+void exposeMatrixUInt16() {
+  exposeType<uint16_t>();
+  exposeType<uint16_t, Eigen::RowMajor>();
+}
+}  // namespace eigenpy
diff --git a/src/matrix-uint32.cpp b/src/matrix-uint32.cpp
new file mode 100644
index 00000000..c8c227d8
--- /dev/null
+++ b/src/matrix-uint32.cpp
@@ -0,0 +1,14 @@
+/*
+ * Copyright 2024 INRIA
+ */
+
+#include "eigenpy/eigenpy.hpp"
+
+#include <cstdint>
+
+namespace eigenpy {
+void exposeMatrixUInt32() {
+  exposeType<uint32_t>();
+  exposeType<uint32_t, Eigen::RowMajor>();
+}
+}  // namespace eigenpy
diff --git a/src/matrix-uint64.cpp b/src/matrix-uint64.cpp
new file mode 100644
index 00000000..26a6df99
--- /dev/null
+++ b/src/matrix-uint64.cpp
@@ -0,0 +1,14 @@
+/*
+ * Copyright 2020 INRIA
+ */
+
+#include "eigenpy/eigenpy.hpp"
+
+#include <cstdint>
+
+namespace eigenpy {
+void exposeMatrixUInt64() {
+  exposeType<uint64_t>();
+  exposeType<uint64_t, Eigen::RowMajor>();
+}
+}  // namespace eigenpy
diff --git a/src/matrix-uint8.cpp b/src/matrix-uint8.cpp
new file mode 100644
index 00000000..a2cf3449
--- /dev/null
+++ b/src/matrix-uint8.cpp
@@ -0,0 +1,14 @@
+/*
+ * Copyright 2020 INRIA
+ */
+
+#include "eigenpy/eigenpy.hpp"
+
+#include <cstdint>
+
+namespace eigenpy {
+void exposeMatrixUInt8() {
+  exposeType<std::uint8_t>();
+  exposeType<std::uint8_t, Eigen::RowMajor>();
+}
+}  // namespace eigenpy
diff --git a/src/matrix-long.cpp b/src/matrix-windows-long.cpp
similarity index 51%
rename from src/matrix-long.cpp
rename to src/matrix-windows-long.cpp
index f52e89f1..44871047 100644
--- a/src/matrix-long.cpp
+++ b/src/matrix-windows-long.cpp
@@ -1,17 +1,15 @@
 /*
- * Copyright 2020 INRIA
+ * Copyright 2024 INRIA
  */
 
 #include "eigenpy/eigenpy.hpp"
 
 namespace eigenpy {
-void exposeMatrixLong() {
+void exposeMatrixWindowsLong() {
+// On Windows, long is a 32 bytes type but it's a different type than int
 #ifdef WIN32
-  exposeType<__int64>();
-  exposeType<__int64, Eigen::RowMajor>();
-#else
   exposeType<long>();
   exposeType<long, Eigen::RowMajor>();
-#endif
+#endif  // WIN32
 }
 }  // namespace eigenpy
diff --git a/src/matrix-windows-ulong.cpp b/src/matrix-windows-ulong.cpp
new file mode 100644
index 00000000..a657ece1
--- /dev/null
+++ b/src/matrix-windows-ulong.cpp
@@ -0,0 +1,15 @@
+/*
+ * Copyright 2024 INRIA
+ */
+
+#include "eigenpy/eigenpy.hpp"
+
+namespace eigenpy {
+void exposeMatrixWindowsULong() {
+// On Windows, long is a 32 bytes type but it's a different type than int
+#ifdef WIN32
+  exposeType<unsigned long>();
+  exposeType<unsigned long, Eigen::RowMajor>();
+#endif  // WIN32
+}
+}  // namespace eigenpy
diff --git a/unittest/matrix.cpp b/unittest/matrix.cpp
index e202c572..e9fcf194 100644
--- a/unittest/matrix.cpp
+++ b/unittest/matrix.cpp
@@ -132,6 +132,11 @@ ReturnMatrix copy(const Eigen::MatrixBase<Matrix>& mat) {
   return mat;
 }
 
+template <typename Matrix>
+Matrix copy_same(const Eigen::MatrixBase<Matrix>& mat) {
+  return mat;
+}
+
 BOOST_PYTHON_MODULE(matrix) {
   using namespace Eigen;
   namespace bp = boost::python;
@@ -190,4 +195,39 @@ BOOST_PYTHON_MODULE(matrix) {
           copy<RowMajorMatrixXd, RowMajorMatrixXd>);
   bp::def("asRowMajorFromRowMajorVector",
           copy<Eigen::RowVectorXd, Eigen::RowVectorXd>);
+
+  bp::def("copyBoolToBool", copy_same<Eigen::Matrix<bool, -1, -1> >);
+
+  bp::def("copyInt8ToInt8", copy_same<Eigen::Matrix<int8_t, -1, -1> >);
+  bp::def("copyCharToChar", copy_same<Eigen::Matrix<char, -1, -1> >);
+  bp::def("copyUCharToUChar", copy_same<Eigen::Matrix<unsigned char, -1, -1> >);
+
+  bp::def("copyInt16ToInt16", copy_same<Eigen::Matrix<int16_t, -1, -1> >);
+  bp::def("copyUInt16ToUInt16", copy_same<Eigen::Matrix<uint16_t, -1, -1> >);
+
+  bp::def("copyInt32ToInt32", copy_same<Eigen::Matrix<int32_t, -1, -1> >);
+  bp::def("copyUInt32ToUInt32", copy_same<Eigen::Matrix<uint32_t, -1, -1> >);
+
+  bp::def("copyInt64ToInt64", copy_same<Eigen::Matrix<int64_t, -1, -1> >);
+  bp::def("copyUInt64ToUInt64", copy_same<Eigen::Matrix<uint64_t, -1, -1> >);
+
+  bp::def("copyLongToLong", copy_same<Eigen::Matrix<long, -1, -1> >);
+  bp::def("copyULongToULong", copy_same<Eigen::Matrix<unsigned long, -1, -1> >);
+
+  bp::def("copyLongLongToLongLong",
+          copy_same<Eigen::Matrix<long long, -1, -1> >);
+  bp::def("copyULongLongToULongLong",
+          copy_same<Eigen::Matrix<unsigned long long, -1, -1> >);
+
+  bp::def("copyFloatToFloat", copy_same<Eigen::Matrix<float, -1, -1> >);
+  bp::def("copyDoubleToDouble", copy_same<Eigen::Matrix<double, -1, -1> >);
+  bp::def("copyLongDoubleToLongDouble",
+          copy_same<Eigen::Matrix<long double, -1, -1> >);
+
+  bp::def("copyCFloatToCFloat",
+          copy_same<Eigen::Matrix<std::complex<float>, -1, -1> >);
+  bp::def("copyCDoubleToCDouble",
+          copy_same<Eigen::Matrix<std::complex<double>, -1, -1> >);
+  bp::def("copyCLongDoubleToCLongDouble",
+          copy_same<Eigen::Matrix<std::complex<long double>, -1, -1> >);
 }
diff --git a/unittest/python/test_matrix.py b/unittest/python/test_matrix.py
index 87b3fd10..9c14ad93 100644
--- a/unittest/python/test_matrix.py
+++ b/unittest/python/test_matrix.py
@@ -1,3 +1,5 @@
+import platform
+
 import matrix as eigenpy
 import numpy as np
 
@@ -180,3 +182,167 @@ assert (eigenpy.asRowMajorFromColMajorMatrix(vec) == vec).all()
 assert (eigenpy.asRowMajorFromColMajorVector(vec) == vec).all()
 assert (eigenpy.asRowMajorFromRowMajorMatrix(vec) == vec).all()
 assert (eigenpy.asRowMajorFromRowMajorVector(vec) == vec).all()
+
+# Test numpy -> Eigen -> numpy for all same type
+
+
+def test_conversion(function, dtype):
+    input_array = np.array([1, 0], dtype=dtype)
+    assert input_array.dtype == dtype
+    output_array = function(input_array)
+    assert output_array.dtype == dtype
+    assert (output_array == input_array).all()
+
+
+bool_t = np.dtype(np.bool_)
+int8_t = np.dtype(np.int8)
+uint8_t = np.dtype(np.uint8)
+int16_t = np.dtype(np.int16)
+uint16_t = np.dtype(np.uint16)
+int32_t = np.dtype(np.int32)
+uint32_t = np.dtype(np.uint32)
+int64_t = np.dtype(np.int64)
+uint64_t = np.dtype(np.uint64)
+# On Windows long is a 32 bits integer but is a different type than int32.
+long_t = np.dtype(np.int32 if platform.system() == "Windows" else np.int64)
+ulong_t = np.dtype(np.uint32 if platform.system() == "Windows" else np.uint64)
+longlong_t = np.dtype(np.longlong)
+ulonglong_t = np.dtype(np.ulonglong)
+
+float32_t = np.dtype(np.float32)
+float64_t = np.dtype(np.float64)
+
+complex64_t = np.dtype(np.complex64)
+complex128_t = np.dtype(np.complex128)
+complex256_t = np.dtype(np.clongdouble)
+
+
+test_conversion(eigenpy.copyBoolToBool, bool_t)
+
+test_conversion(eigenpy.copyInt8ToInt8, int8_t)
+test_conversion(eigenpy.copyCharToChar, int8_t)
+test_conversion(eigenpy.copyUCharToUChar, uint8_t)
+
+test_conversion(eigenpy.copyInt16ToInt16, int16_t)
+test_conversion(eigenpy.copyUInt16ToUInt16, uint16_t)
+
+test_conversion(eigenpy.copyInt32ToInt32, int32_t)
+test_conversion(eigenpy.copyUInt32ToUInt32, uint32_t)
+
+test_conversion(eigenpy.copyInt64ToInt64, int64_t)
+test_conversion(eigenpy.copyUInt64ToUInt64, uint64_t)
+
+test_conversion(eigenpy.copyLongToLong, long_t)
+test_conversion(eigenpy.copyULongToULong, ulong_t)
+
+# On Windows long long is an int64_t alias.
+# The numpy dtype match between longlong and int64.
+
+# On Linux long long is a 64 bits integer but is a different type than int64_t.
+# The numpy dtype doesn't match and it's not an issue since C++ type are different.
+
+# On Mac long long is an int64_t and long alias.
+# This is an issue because longlong numpy dtype is different than long numpy dtype
+# but long and long long are the same type in C++.
+# The test should pass thanks to the promotion code.
+test_conversion(eigenpy.copyLongLongToLongLong, longlong_t)
+test_conversion(eigenpy.copyULongLongToULongLong, ulonglong_t)
+
+test_conversion(eigenpy.copyFloatToFloat, float32_t)
+test_conversion(eigenpy.copyDoubleToDouble, float64_t)
+# On Windows and Mac longdouble is 64 bits
+test_conversion(eigenpy.copyLongDoubleToLongDouble, np.dtype(np.longdouble))
+
+test_conversion(eigenpy.copyCFloatToCFloat, complex64_t)
+test_conversion(eigenpy.copyCDoubleToCDouble, complex128_t)
+# On Windows and Mac clongdouble is 128 bits
+test_conversion(eigenpy.copyCLongDoubleToCLongDouble, complex256_t)
+
+
+# Test numpy -> Eigen -> numpy promotion
+
+
+def test_conversion_promotion(function, input_dtype, output_dtype):
+    input_array = np.array([1, 0], dtype=input_dtype)
+    assert input_array.dtype == input_dtype
+    output_array = function(input_array)
+    assert output_array.dtype == output_dtype
+    assert (output_array == input_array).all()
+
+
+# Test bool to other type
+test_conversion_promotion(eigenpy.copyInt8ToInt8, bool_t, int8_t)
+test_conversion_promotion(eigenpy.copyUCharToUChar, bool_t, uint8_t)
+test_conversion_promotion(eigenpy.copyInt16ToInt16, bool_t, int16_t)
+test_conversion_promotion(eigenpy.copyUInt16ToUInt16, bool_t, uint16_t)
+test_conversion_promotion(eigenpy.copyInt32ToInt32, bool_t, int32_t)
+test_conversion_promotion(eigenpy.copyUInt32ToUInt32, bool_t, uint32_t)
+test_conversion_promotion(eigenpy.copyInt64ToInt64, bool_t, int64_t)
+test_conversion_promotion(eigenpy.copyUInt64ToUInt64, bool_t, uint64_t)
+test_conversion_promotion(eigenpy.copyLongToLong, bool_t, long_t)
+test_conversion_promotion(eigenpy.copyULongToULong, bool_t, ulong_t)
+test_conversion_promotion(eigenpy.copyLongLongToLongLong, bool_t, int64_t)
+test_conversion_promotion(eigenpy.copyULongLongToULongLong, bool_t, uint64_t)
+
+# Test int8 to other type
+test_conversion_promotion(eigenpy.copyInt16ToInt16, int8_t, int16_t)
+test_conversion_promotion(eigenpy.copyInt16ToInt16, uint8_t, int16_t)
+test_conversion_promotion(eigenpy.copyUInt16ToUInt16, uint8_t, uint16_t)
+test_conversion_promotion(eigenpy.copyInt32ToInt32, int8_t, int32_t)
+test_conversion_promotion(eigenpy.copyInt32ToInt32, uint8_t, int32_t)
+test_conversion_promotion(eigenpy.copyUInt32ToUInt32, uint8_t, uint32_t)
+test_conversion_promotion(eigenpy.copyInt64ToInt64, int8_t, int64_t)
+test_conversion_promotion(eigenpy.copyInt64ToInt64, uint8_t, int64_t)
+test_conversion_promotion(eigenpy.copyUInt64ToUInt64, uint8_t, uint64_t)
+test_conversion_promotion(eigenpy.copyLongToLong, int8_t, long_t)
+test_conversion_promotion(eigenpy.copyLongToLong, uint8_t, long_t)
+test_conversion_promotion(eigenpy.copyULongToULong, uint8_t, ulong_t)
+test_conversion_promotion(eigenpy.copyLongLongToLongLong, int8_t, int64_t)
+test_conversion_promotion(eigenpy.copyLongLongToLongLong, uint8_t, int64_t)
+test_conversion_promotion(eigenpy.copyULongLongToULongLong, uint8_t, uint64_t)
+
+# Test int16 to other type
+test_conversion_promotion(eigenpy.copyInt32ToInt32, int16_t, int32_t)
+test_conversion_promotion(eigenpy.copyInt32ToInt32, uint16_t, int32_t)
+test_conversion_promotion(eigenpy.copyUInt32ToUInt32, uint16_t, uint32_t)
+test_conversion_promotion(eigenpy.copyInt64ToInt64, int16_t, int64_t)
+test_conversion_promotion(eigenpy.copyInt64ToInt64, uint16_t, int64_t)
+test_conversion_promotion(eigenpy.copyUInt64ToUInt64, uint16_t, uint64_t)
+test_conversion_promotion(eigenpy.copyLongToLong, int16_t, long_t)
+test_conversion_promotion(eigenpy.copyLongToLong, uint16_t, long_t)
+test_conversion_promotion(eigenpy.copyULongToULong, uint16_t, ulong_t)
+test_conversion_promotion(eigenpy.copyLongLongToLongLong, int16_t, int64_t)
+test_conversion_promotion(eigenpy.copyLongLongToLongLong, uint16_t, int64_t)
+test_conversion_promotion(eigenpy.copyULongLongToULongLong, uint16_t, uint64_t)
+
+# Test int32 to other type
+test_conversion_promotion(eigenpy.copyInt64ToInt64, int32_t, int64_t)
+test_conversion_promotion(eigenpy.copyInt64ToInt64, uint32_t, int64_t)
+test_conversion_promotion(eigenpy.copyUInt64ToUInt64, uint32_t, uint64_t)
+test_conversion_promotion(eigenpy.copyLongToLong, int32_t, long_t)
+test_conversion_promotion(eigenpy.copyLongToLong, uint32_t, long_t)
+test_conversion_promotion(eigenpy.copyULongToULong, uint32_t, ulong_t)
+test_conversion_promotion(eigenpy.copyLongLongToLongLong, int32_t, int64_t)
+test_conversion_promotion(eigenpy.copyLongLongToLongLong, uint32_t, int64_t)
+test_conversion_promotion(eigenpy.copyULongLongToULongLong, uint32_t, uint64_t)
+
+# Test float to double
+test_conversion_promotion(eigenpy.copyDoubleToDouble, float32_t, float64_t)
+
+# Test complex to other type
+test_conversion_promotion(eigenpy.copyCDoubleToCDouble, complex64_t, complex128_t)
+
+test_conversion_promotion(
+    eigenpy.copyCLongDoubleToCLongDouble,
+    complex64_t,
+    complex256_t,
+)
+
+# Only Linux store complex double into 258 bits.
+# Then, 128 bits complex can be promoted.
+if platform.system() == "Linux":
+    test_conversion_promotion(
+        eigenpy.copyCLongDoubleToCLongDouble,
+        complex128_t,
+        complex256_t,
+    )
diff --git a/unittest/python/test_user_type.py b/unittest/python/test_user_type.py
index e22b534c..76f8591f 100644
--- a/unittest/python/test_user_type.py
+++ b/unittest/python/test_user_type.py
@@ -9,7 +9,7 @@ cols = 20
 
 def test(dtype):
     rng = np.random.default_rng()
-    mat = np.array(np.ones((rows, cols)).astype(np.intc), dtype=dtype)
+    mat = np.array(np.ones((rows, cols)).astype(np.int32), dtype=dtype)
     mat = rng.random((rows, cols)).astype(dtype)
     mat_copy = mat.copy()
     assert (mat == mat_copy).all()
-- 
GitLab