Back to home page

EIC code displayed by LXR

 
 

    


File indexing completed on 2026-09-16 09:20:23

0001 /*
0002     pybind11/numpy.h: Basic NumPy support, vectorize() wrapper
0003 
0004     Copyright (c) 2016 Wenzel Jakob <wenzel.jakob@epfl.ch>
0005 
0006     All rights reserved. Use of this source code is governed by a
0007     BSD-style license that can be found in the LICENSE file.
0008 */
0009 
0010 #pragma once
0011 
0012 #include "pybind11.h"
0013 #include "detail/common.h"
0014 #include "complex.h"
0015 #include "gil_safe_call_once.h"
0016 #include "pytypes.h"
0017 
0018 #include <algorithm>
0019 #include <array>
0020 #include <cstdint>
0021 #include <cstdlib>
0022 #include <cstring>
0023 #include <functional>
0024 #include <numeric>
0025 #include <sstream>
0026 #include <string>
0027 #include <type_traits>
0028 #include <typeindex>
0029 #include <utility>
0030 #include <vector>
0031 
0032 #ifdef PYBIND11_HAS_SPAN
0033 #    include <span>
0034 #endif
0035 
0036 #if defined(PYBIND11_NUMPY_1_ONLY)
0037 #    error "PYBIND11_NUMPY_1_ONLY is no longer supported (see PR #5595)."
0038 #endif
0039 
0040 /* This will be true on all flat address space platforms and allows us to reduce the
0041    whole npy_intp / ssize_t / Py_intptr_t business down to just ssize_t for all size
0042    and dimension types (e.g. shape, strides, indexing), instead of inflicting this
0043    upon the library user.
0044    Note that NumPy 2 now uses ssize_t for `npy_intp` to simplify this. */
0045 static_assert(sizeof(::pybind11::ssize_t) == sizeof(Py_intptr_t), "ssize_t != Py_intptr_t");
0046 static_assert(std::is_signed<Py_intptr_t>::value, "Py_intptr_t must be signed");
0047 // We now can reinterpret_cast between py::ssize_t and Py_intptr_t (MSVC + PyPy cares)
0048 
0049 PYBIND11_NAMESPACE_BEGIN(PYBIND11_NAMESPACE)
0050 
0051 PYBIND11_WARNING_DISABLE_MSVC(4127)
0052 
0053 class dtype; // Forward declaration
0054 class array; // Forward declaration
0055 
0056 template <typename>
0057 struct numpy_scalar; // Forward declaration
0058 
0059 PYBIND11_NAMESPACE_BEGIN(detail)
0060 
0061 template <>
0062 struct handle_type_name<dtype> {
0063     static constexpr auto name = const_name("numpy.dtype");
0064 };
0065 
0066 template <>
0067 struct handle_type_name<array> {
0068     static constexpr auto name = const_name("numpy.ndarray");
0069 };
0070 
0071 template <typename type, typename SFINAE = void>
0072 struct npy_format_descriptor;
0073 
0074 /* NumPy 1 proxy (always includes legacy fields) */
0075 struct PyArrayDescr1_Proxy {
0076     PyObject_HEAD
0077     PyObject *typeobj;
0078     char kind;
0079     char type;
0080     char byteorder;
0081     char flags;
0082     int type_num;
0083     int elsize;
0084     int alignment;
0085     char *subarray;
0086     PyObject *fields;
0087     PyObject *names;
0088 };
0089 
0090 struct PyArrayDescr_Proxy {
0091     PyObject_HEAD
0092     PyObject *typeobj;
0093     char kind;
0094     char type;
0095     char byteorder;
0096     char _former_flags;
0097     int type_num;
0098     /* Additional fields are NumPy version specific. */
0099 };
0100 
0101 /* NumPy 2 proxy, including legacy fields */
0102 struct PyArrayDescr2_Proxy {
0103     PyObject_HEAD
0104     PyObject *typeobj;
0105     char kind;
0106     char type;
0107     char byteorder;
0108     char _former_flags;
0109     int type_num;
0110     std::uint64_t flags;
0111     ssize_t elsize;
0112     ssize_t alignment;
0113     PyObject *metadata;
0114     Py_hash_t hash;
0115     void *reserved_null[2];
0116     /* The following fields only exist if 0 <= type_num < 2056 */
0117     char *subarray;
0118     PyObject *fields;
0119     PyObject *names;
0120 };
0121 
0122 struct PyArray_Proxy {
0123     PyObject_HEAD
0124     char *data;
0125     int nd;
0126     ssize_t *dimensions;
0127     ssize_t *strides;
0128     PyObject *base;
0129     PyObject *descr;
0130     int flags;
0131 };
0132 
0133 struct PyVoidScalarObject_Proxy {
0134     PyObject_VAR_HEAD char *obval;
0135     PyArrayDescr_Proxy *descr;
0136     int flags;
0137     PyObject *base;
0138 };
0139 
0140 struct numpy_type_info {
0141     PyObject *dtype_ptr;
0142     std::string format_str;
0143 };
0144 
0145 struct numpy_internals {
0146     std::unordered_map<std::type_index, numpy_type_info> registered_dtypes;
0147 
0148     numpy_type_info *get_type_info(const std::type_info &tinfo, bool throw_if_missing = true) {
0149         auto it = registered_dtypes.find(std::type_index(tinfo));
0150         if (it != registered_dtypes.end()) {
0151             return &(it->second);
0152         }
0153         if (throw_if_missing) {
0154             pybind11_fail(std::string("NumPy type info missing for ") + tinfo.name());
0155         }
0156         return nullptr;
0157     }
0158 
0159     template <typename T>
0160     numpy_type_info *get_type_info(bool throw_if_missing = true) {
0161         return get_type_info(typeid(typename std::remove_cv<T>::type), throw_if_missing);
0162     }
0163 };
0164 
0165 PYBIND11_NOINLINE void load_numpy_internals(numpy_internals *&ptr) {
0166     ptr = &get_or_create_shared_data<numpy_internals>("_numpy_internals");
0167 }
0168 
0169 inline numpy_internals &get_numpy_internals() {
0170     static numpy_internals *ptr = nullptr;
0171     if (!ptr) {
0172         load_numpy_internals(ptr);
0173     }
0174     return *ptr;
0175 }
0176 
0177 PYBIND11_NOINLINE module_ import_numpy_core_submodule(const char *submodule_name) {
0178     module_ numpy = module_::import("numpy");
0179     str version_string = numpy.attr("__version__");
0180     module_ numpy_lib = module_::import("numpy.lib");
0181     object numpy_version = numpy_lib.attr("NumpyVersion")(version_string);
0182     int major_version = numpy_version.attr("major").cast<int>();
0183 
0184     /* `numpy.core` was renamed to `numpy._core` in NumPy 2.0 as it officially
0185         became a private module. */
0186     std::string numpy_core_path = major_version >= 2 ? "numpy._core" : "numpy.core";
0187     return module_::import((numpy_core_path + "." + submodule_name).c_str());
0188 }
0189 
0190 template <typename T>
0191 struct same_size {
0192     template <typename U>
0193     using as = bool_constant<sizeof(T) == sizeof(U)>;
0194 };
0195 
0196 template <typename Concrete>
0197 constexpr int platform_lookup() {
0198     return -1;
0199 }
0200 
0201 // Lookup a type according to its size, and return a value corresponding to the NumPy typenum.
0202 template <typename Concrete, typename T, typename... Ts, typename... Ints>
0203 constexpr int platform_lookup(int I, Ints... Is) {
0204     return sizeof(Concrete) == sizeof(T) ? I : platform_lookup<Concrete, Ts...>(Is...);
0205 }
0206 
0207 struct npy_api {
0208     // If you change this code, please review `normalized_dtype_num` below.
0209     enum constants {
0210         NPY_ARRAY_C_CONTIGUOUS_ = 0x0001,
0211         NPY_ARRAY_F_CONTIGUOUS_ = 0x0002,
0212         NPY_ARRAY_OWNDATA_ = 0x0004,
0213         NPY_ARRAY_FORCECAST_ = 0x0010,
0214         NPY_ARRAY_ENSUREARRAY_ = 0x0040,
0215         NPY_ARRAY_ALIGNED_ = 0x0100,
0216         NPY_ARRAY_WRITEABLE_ = 0x0400,
0217         NPY_BOOL_ = 0,
0218         NPY_BYTE_,
0219         NPY_UBYTE_,
0220         NPY_SHORT_,
0221         NPY_USHORT_,
0222         NPY_INT_,
0223         NPY_UINT_,
0224         NPY_LONG_,
0225         NPY_ULONG_,
0226         NPY_LONGLONG_,
0227         NPY_ULONGLONG_,
0228         NPY_FLOAT_,
0229         NPY_DOUBLE_,
0230         NPY_LONGDOUBLE_,
0231         NPY_CFLOAT_,
0232         NPY_CDOUBLE_,
0233         NPY_CLONGDOUBLE_,
0234         NPY_OBJECT_ = 17,
0235         NPY_STRING_,
0236         NPY_UNICODE_,
0237         NPY_VOID_,
0238         // Platform-dependent normalization
0239         NPY_INT8_ = NPY_BYTE_,
0240         NPY_UINT8_ = NPY_UBYTE_,
0241         NPY_INT16_ = NPY_SHORT_,
0242         NPY_UINT16_ = NPY_USHORT_,
0243         // `npy_common.h` defines the integer aliases. In order, it checks:
0244         // NPY_BITSOF_LONG, NPY_BITSOF_LONGLONG, NPY_BITSOF_INT, NPY_BITSOF_SHORT, NPY_BITSOF_CHAR
0245         // and assigns the alias to the first matching size, so we should check in this order.
0246         NPY_INT32_
0247         = platform_lookup<std::int32_t, long, int, short>(NPY_LONG_, NPY_INT_, NPY_SHORT_),
0248         NPY_UINT32_ = platform_lookup<std::uint32_t, unsigned long, unsigned int, unsigned short>(
0249             NPY_ULONG_, NPY_UINT_, NPY_USHORT_),
0250         NPY_INT64_
0251         = platform_lookup<std::int64_t, long, long long, int>(NPY_LONG_, NPY_LONGLONG_, NPY_INT_),
0252         NPY_UINT64_
0253         = platform_lookup<std::uint64_t, unsigned long, unsigned long long, unsigned int>(
0254             NPY_ULONG_, NPY_ULONGLONG_, NPY_UINT_),
0255         NPY_FLOAT32_ = platform_lookup<float, double, float, long double>(
0256             NPY_DOUBLE_, NPY_FLOAT_, NPY_LONGDOUBLE_),
0257         NPY_FLOAT64_ = platform_lookup<double, double, float, long double>(
0258             NPY_DOUBLE_, NPY_FLOAT_, NPY_LONGDOUBLE_),
0259         NPY_COMPLEX64_
0260         = platform_lookup<std::complex<float>,
0261                           std::complex<double>,
0262                           std::complex<float>,
0263                           std::complex<long double>>(NPY_DOUBLE_, NPY_FLOAT_, NPY_LONGDOUBLE_),
0264         NPY_COMPLEX128_
0265         = platform_lookup<std::complex<double>,
0266                           std::complex<double>,
0267                           std::complex<float>,
0268                           std::complex<long double>>(NPY_DOUBLE_, NPY_FLOAT_, NPY_LONGDOUBLE_),
0269         NPY_CHAR_ = std::is_signed<char>::value ? NPY_BYTE_ : NPY_UBYTE_,
0270     };
0271 
0272     unsigned int PyArray_RUNTIME_VERSION_;
0273 
0274     struct PyArray_Dims {
0275         Py_intptr_t *ptr;
0276         int len;
0277     };
0278 
0279     static npy_api &get() {
0280         PYBIND11_CONSTINIT static gil_safe_call_once_and_store<npy_api> storage;
0281         return storage.call_once_and_store_result(lookup).get_stored();
0282     }
0283 
0284     bool PyArray_Check_(PyObject *obj) const {
0285         return PyObject_TypeCheck(obj, PyArray_Type_) != 0;
0286     }
0287     bool PyArrayDescr_Check_(PyObject *obj) const {
0288         return PyObject_TypeCheck(obj, PyArrayDescr_Type_) != 0;
0289     }
0290 
0291     unsigned int (*PyArray_GetNDArrayCFeatureVersion_)();
0292     PyObject *(*PyArray_DescrFromType_)(int);
0293     PyObject *(*PyArray_TypeObjectFromType_)(int);
0294     PyObject *(*PyArray_NewFromDescr_)(PyTypeObject *,
0295                                        PyObject *,
0296                                        int,
0297                                        Py_intptr_t const *,
0298                                        Py_intptr_t const *,
0299                                        void *,
0300                                        int,
0301                                        PyObject *);
0302     // Unused. Not removed because that affects ABI of the class.
0303     PyObject *(*PyArray_DescrNewFromType_)(int);
0304     int (*PyArray_CopyInto_)(PyObject *, PyObject *);
0305     PyObject *(*PyArray_NewCopy_)(PyObject *, int);
0306     PyTypeObject *PyArray_Type_;
0307     PyTypeObject *PyVoidArrType_Type_;
0308     PyTypeObject *PyArrayDescr_Type_;
0309     PyObject *(*PyArray_DescrFromScalar_)(PyObject *);
0310     PyObject *(*PyArray_Scalar_)(void *, PyObject *, PyObject *);
0311     void (*PyArray_ScalarAsCtype_)(PyObject *, void *);
0312     PyObject *(*PyArray_FromAny_)(PyObject *, PyObject *, int, int, int, PyObject *);
0313     int (*PyArray_DescrConverter_)(PyObject *, PyObject **);
0314     bool (*PyArray_EquivTypes_)(PyObject *, PyObject *);
0315     PyObject *(*PyArray_Squeeze_)(PyObject *);
0316     // Unused. Not removed because that affects ABI of the class.
0317     int (*PyArray_SetBaseObject_)(PyObject *, PyObject *);
0318     PyObject *(*PyArray_Resize_)(PyObject *, PyArray_Dims *, int, int);
0319     PyObject *(*PyArray_Newshape_)(PyObject *, PyArray_Dims *, int);
0320     PyObject *(*PyArray_View_)(PyObject *, PyObject *, PyObject *);
0321 
0322 private:
0323     enum functions {
0324         API_PyArray_GetNDArrayCFeatureVersion = 211,
0325         API_PyArray_Type = 2,
0326         API_PyArrayDescr_Type = 3,
0327         API_PyVoidArrType_Type = 39,
0328         API_PyArray_DescrFromType = 45,
0329         API_PyArray_TypeObjectFromType = 46,
0330         API_PyArray_DescrFromScalar = 57,
0331         API_PyArray_Scalar = 60,
0332         API_PyArray_ScalarAsCtype = 62,
0333         API_PyArray_FromAny = 69,
0334         API_PyArray_Resize = 80,
0335         // CopyInto was slot 82 and 50 was effectively an alias. NumPy 2 removed 82.
0336         API_PyArray_CopyInto = 50,
0337         API_PyArray_NewCopy = 85,
0338         API_PyArray_NewFromDescr = 94,
0339         API_PyArray_DescrNewFromType = 96,
0340         API_PyArray_Newshape = 135,
0341         API_PyArray_Squeeze = 136,
0342         API_PyArray_View = 137,
0343         API_PyArray_DescrConverter = 174,
0344         API_PyArray_EquivTypes = 182,
0345         API_PyArray_SetBaseObject = 282
0346     };
0347 
0348     static npy_api lookup() {
0349         module_ m = detail::import_numpy_core_submodule("multiarray");
0350         auto c = m.attr("_ARRAY_API");
0351         void **api_ptr = (void **) PyCapsule_GetPointer(c.ptr(), nullptr);
0352         if (api_ptr == nullptr) {
0353             raise_from(PyExc_SystemError, "FAILURE obtaining numpy _ARRAY_API pointer.");
0354             throw error_already_set();
0355         }
0356         npy_api api;
0357 #define DECL_NPY_API(Func) api.Func##_ = (decltype(api.Func##_)) api_ptr[API_##Func];
0358         DECL_NPY_API(PyArray_GetNDArrayCFeatureVersion);
0359         api.PyArray_RUNTIME_VERSION_ = api.PyArray_GetNDArrayCFeatureVersion_();
0360         if (api.PyArray_RUNTIME_VERSION_ < 0x7) {
0361             pybind11_fail("pybind11 numpy support requires numpy >= 1.7.0");
0362         }
0363         DECL_NPY_API(PyArray_Type);
0364         DECL_NPY_API(PyVoidArrType_Type);
0365         DECL_NPY_API(PyArrayDescr_Type);
0366         DECL_NPY_API(PyArray_DescrFromType);
0367         DECL_NPY_API(PyArray_TypeObjectFromType);
0368         DECL_NPY_API(PyArray_DescrFromScalar);
0369         DECL_NPY_API(PyArray_Scalar);
0370         DECL_NPY_API(PyArray_ScalarAsCtype);
0371         DECL_NPY_API(PyArray_FromAny);
0372         DECL_NPY_API(PyArray_Resize);
0373         DECL_NPY_API(PyArray_CopyInto);
0374         DECL_NPY_API(PyArray_NewCopy);
0375         DECL_NPY_API(PyArray_NewFromDescr);
0376         DECL_NPY_API(PyArray_DescrNewFromType);
0377         DECL_NPY_API(PyArray_Newshape);
0378         DECL_NPY_API(PyArray_Squeeze);
0379         DECL_NPY_API(PyArray_View);
0380         DECL_NPY_API(PyArray_DescrConverter);
0381         DECL_NPY_API(PyArray_EquivTypes);
0382         DECL_NPY_API(PyArray_SetBaseObject);
0383 
0384 #undef DECL_NPY_API
0385         return api;
0386     }
0387 };
0388 
0389 template <typename T>
0390 struct is_complex : std::false_type {};
0391 template <typename T>
0392 struct is_complex<std::complex<T>> : std::true_type {};
0393 
0394 template <typename T, typename = void>
0395 struct npy_format_descriptor_name;
0396 
0397 template <typename T>
0398 struct npy_format_descriptor_name<T, enable_if_t<std::is_integral<T>::value>> {
0399     static constexpr auto name = const_name<std::is_same<T, bool>::value>(
0400         const_name("numpy.bool"),
0401         const_name<std::is_signed<T>::value>("numpy.int", "numpy.uint")
0402             + const_name<sizeof(T) * 8>());
0403 };
0404 
0405 template <typename T>
0406 struct npy_format_descriptor_name<T, enable_if_t<std::is_floating_point<T>::value>> {
0407     static constexpr auto name = const_name < std::is_same<T, float>::value
0408                                  || std::is_same<T, const float>::value
0409                                  || std::is_same<T, double>::value
0410                                  || std::is_same<T, const double>::value
0411                                         > (const_name("numpy.float") + const_name<sizeof(T) * 8>(),
0412                                            const_name("numpy.longdouble"));
0413 };
0414 
0415 template <typename T>
0416 struct npy_format_descriptor_name<T, enable_if_t<is_complex<T>::value>> {
0417     static constexpr auto name = const_name < std::is_same<typename T::value_type, float>::value
0418                                  || std::is_same<typename T::value_type, const float>::value
0419                                  || std::is_same<typename T::value_type, double>::value
0420                                  || std::is_same<typename T::value_type, const double>::value
0421                                         > (const_name("numpy.complex")
0422                                                + const_name<sizeof(typename T::value_type) * 16>(),
0423                                            const_name("numpy.clongdouble"));
0424 };
0425 
0426 template <typename T>
0427 struct numpy_scalar_info {};
0428 
0429 #define PYBIND11_NUMPY_SCALAR_IMPL(ctype_, typenum_)                                              \
0430     template <>                                                                                   \
0431     struct numpy_scalar_info<ctype_> {                                                            \
0432         static constexpr auto name = npy_format_descriptor_name<ctype_>::name;                    \
0433         static constexpr int typenum = npy_api::typenum_##_;                                      \
0434     }
0435 
0436 // boolean type
0437 PYBIND11_NUMPY_SCALAR_IMPL(bool, NPY_BOOL);
0438 
0439 // character types
0440 PYBIND11_NUMPY_SCALAR_IMPL(char, NPY_CHAR);
0441 PYBIND11_NUMPY_SCALAR_IMPL(signed char, NPY_BYTE);
0442 PYBIND11_NUMPY_SCALAR_IMPL(unsigned char, NPY_UBYTE);
0443 
0444 // signed integer types
0445 PYBIND11_NUMPY_SCALAR_IMPL(std::int16_t, NPY_INT16);
0446 PYBIND11_NUMPY_SCALAR_IMPL(std::int32_t, NPY_INT32);
0447 PYBIND11_NUMPY_SCALAR_IMPL(std::int64_t, NPY_INT64);
0448 
0449 // unsigned integer types
0450 PYBIND11_NUMPY_SCALAR_IMPL(std::uint16_t, NPY_UINT16);
0451 PYBIND11_NUMPY_SCALAR_IMPL(std::uint32_t, NPY_UINT32);
0452 PYBIND11_NUMPY_SCALAR_IMPL(std::uint64_t, NPY_UINT64);
0453 
0454 // floating point types
0455 PYBIND11_NUMPY_SCALAR_IMPL(float, NPY_FLOAT);
0456 PYBIND11_NUMPY_SCALAR_IMPL(double, NPY_DOUBLE);
0457 PYBIND11_NUMPY_SCALAR_IMPL(long double, NPY_LONGDOUBLE);
0458 
0459 // complex types
0460 PYBIND11_NUMPY_SCALAR_IMPL(std::complex<float>, NPY_CFLOAT);
0461 PYBIND11_NUMPY_SCALAR_IMPL(std::complex<double>, NPY_CDOUBLE);
0462 PYBIND11_NUMPY_SCALAR_IMPL(std::complex<long double>, NPY_CLONGDOUBLE);
0463 
0464 #undef PYBIND11_NUMPY_SCALAR_IMPL
0465 
0466 // This table normalizes typenums by mapping NPY_INT_, NPY_LONG, ... to NPY_INT32_, NPY_INT64, ...
0467 // This is needed to correctly handle situations where multiple typenums map to the same type,
0468 // e.g. NPY_LONG_ may be equivalent to NPY_INT_ or NPY_LONGLONG_ despite having a different
0469 // typenum. The normalized typenum should always match the values used in npy_format_descriptor.
0470 // If you change this code, please review `enum constants` above.
0471 static constexpr int normalized_dtype_num[npy_api::NPY_VOID_ + 1] = {
0472     // NPY_BOOL_ =>
0473     npy_api::NPY_BOOL_,
0474     // NPY_BYTE_ =>
0475     npy_api::NPY_BYTE_,
0476     // NPY_UBYTE_ =>
0477     npy_api::NPY_UBYTE_,
0478     // NPY_SHORT_ =>
0479     npy_api::NPY_INT16_,
0480     // NPY_USHORT_ =>
0481     npy_api::NPY_UINT16_,
0482     // NPY_INT_ =>
0483     sizeof(int) == sizeof(std::int16_t)   ? npy_api::NPY_INT16_
0484     : sizeof(int) == sizeof(std::int32_t) ? npy_api::NPY_INT32_
0485     : sizeof(int) == sizeof(std::int64_t) ? npy_api::NPY_INT64_
0486                                           : npy_api::NPY_INT_,
0487     // NPY_UINT_ =>
0488     sizeof(unsigned int) == sizeof(std::uint16_t)   ? npy_api::NPY_UINT16_
0489     : sizeof(unsigned int) == sizeof(std::uint32_t) ? npy_api::NPY_UINT32_
0490     : sizeof(unsigned int) == sizeof(std::uint64_t) ? npy_api::NPY_UINT64_
0491                                                     : npy_api::NPY_UINT_,
0492     // NPY_LONG_ =>
0493     sizeof(long) == sizeof(std::int16_t)   ? npy_api::NPY_INT16_
0494     : sizeof(long) == sizeof(std::int32_t) ? npy_api::NPY_INT32_
0495     : sizeof(long) == sizeof(std::int64_t) ? npy_api::NPY_INT64_
0496                                            : npy_api::NPY_LONG_,
0497     // NPY_ULONG_ =>
0498     sizeof(unsigned long) == sizeof(std::uint16_t)   ? npy_api::NPY_UINT16_
0499     : sizeof(unsigned long) == sizeof(std::uint32_t) ? npy_api::NPY_UINT32_
0500     : sizeof(unsigned long) == sizeof(std::uint64_t) ? npy_api::NPY_UINT64_
0501                                                      : npy_api::NPY_ULONG_,
0502     // NPY_LONGLONG_ =>
0503     sizeof(long long) == sizeof(std::int16_t)   ? npy_api::NPY_INT16_
0504     : sizeof(long long) == sizeof(std::int32_t) ? npy_api::NPY_INT32_
0505     : sizeof(long long) == sizeof(std::int64_t) ? npy_api::NPY_INT64_
0506                                                 : npy_api::NPY_LONGLONG_,
0507     // NPY_ULONGLONG_ =>
0508     sizeof(unsigned long long) == sizeof(std::uint16_t)   ? npy_api::NPY_UINT16_
0509     : sizeof(unsigned long long) == sizeof(std::uint32_t) ? npy_api::NPY_UINT32_
0510     : sizeof(unsigned long long) == sizeof(std::uint64_t) ? npy_api::NPY_UINT64_
0511                                                           : npy_api::NPY_ULONGLONG_,
0512     // NPY_FLOAT_ =>
0513     npy_api::NPY_FLOAT_,
0514     // NPY_DOUBLE_ =>
0515     npy_api::NPY_DOUBLE_,
0516     // NPY_LONGDOUBLE_ =>
0517     npy_api::NPY_LONGDOUBLE_,
0518     // NPY_CFLOAT_ =>
0519     npy_api::NPY_CFLOAT_,
0520     // NPY_CDOUBLE_ =>
0521     npy_api::NPY_CDOUBLE_,
0522     // NPY_CLONGDOUBLE_ =>
0523     npy_api::NPY_CLONGDOUBLE_,
0524     // NPY_OBJECT_ =>
0525     npy_api::NPY_OBJECT_,
0526     // NPY_STRING_ =>
0527     npy_api::NPY_STRING_,
0528     // NPY_UNICODE_ =>
0529     npy_api::NPY_UNICODE_,
0530     // NPY_VOID_ =>
0531     npy_api::NPY_VOID_,
0532 };
0533 
0534 inline PyArray_Proxy *array_proxy(void *ptr) { return reinterpret_cast<PyArray_Proxy *>(ptr); }
0535 
0536 inline const PyArray_Proxy *array_proxy(const void *ptr) {
0537     return reinterpret_cast<const PyArray_Proxy *>(ptr);
0538 }
0539 
0540 inline PyArrayDescr_Proxy *array_descriptor_proxy(PyObject *ptr) {
0541     return reinterpret_cast<PyArrayDescr_Proxy *>(ptr);
0542 }
0543 
0544 inline const PyArrayDescr_Proxy *array_descriptor_proxy(const PyObject *ptr) {
0545     return reinterpret_cast<const PyArrayDescr_Proxy *>(ptr);
0546 }
0547 
0548 inline const PyArrayDescr1_Proxy *array_descriptor1_proxy(const PyObject *ptr) {
0549     return reinterpret_cast<const PyArrayDescr1_Proxy *>(ptr);
0550 }
0551 
0552 inline const PyArrayDescr2_Proxy *array_descriptor2_proxy(const PyObject *ptr) {
0553     return reinterpret_cast<const PyArrayDescr2_Proxy *>(ptr);
0554 }
0555 
0556 inline bool check_flags(const void *ptr, int flag) {
0557     return (flag == (array_proxy(ptr)->flags & flag));
0558 }
0559 
0560 template <typename T>
0561 struct is_std_array : std::false_type {};
0562 template <typename T, size_t N>
0563 struct is_std_array<std::array<T, N>> : std::true_type {};
0564 
0565 template <typename T>
0566 struct array_info_scalar {
0567     using type = T;
0568     static constexpr bool is_array = false;
0569     static constexpr bool is_empty = false;
0570     static constexpr auto extents = const_name("");
0571     static void append_extents(list & /* shape */) {}
0572 };
0573 // Computes underlying type and a comma-separated list of extents for array
0574 // types (any mix of std::array and built-in arrays). An array of char is
0575 // treated as scalar because it gets special handling.
0576 template <typename T>
0577 struct array_info : array_info_scalar<T> {};
0578 template <typename T, size_t N>
0579 struct array_info<std::array<T, N>> {
0580     using type = typename array_info<T>::type;
0581     static constexpr bool is_array = true;
0582     static constexpr bool is_empty = (N == 0) || array_info<T>::is_empty;
0583     static constexpr size_t extent = N;
0584 
0585     // appends the extents to shape
0586     static void append_extents(list &shape) {
0587         shape.append(N);
0588         array_info<T>::append_extents(shape);
0589     }
0590 
0591     static constexpr auto extents = const_name<array_info<T>::is_array>(
0592         ::pybind11::detail::concat(const_name<N>(), array_info<T>::extents), const_name<N>());
0593 };
0594 // For numpy we have special handling for arrays of characters, so we don't include
0595 // the size in the array extents.
0596 template <size_t N>
0597 struct array_info<char[N]> : array_info_scalar<char[N]> {};
0598 template <size_t N>
0599 struct array_info<std::array<char, N>> : array_info_scalar<std::array<char, N>> {};
0600 template <typename T, size_t N>
0601 struct array_info<T[N]> : array_info<std::array<T, N>> {};
0602 template <typename T>
0603 using remove_all_extents_t = typename array_info<T>::type;
0604 
0605 template <typename T>
0606 using is_pod_struct
0607     = all_of<std::is_standard_layout<T>, // since we're accessing directly in memory
0608                                          // we need a standard layout type
0609 #if defined(__GLIBCXX__)                                                                          \
0610     && (__GLIBCXX__ < 20150422 || __GLIBCXX__ == 20150426 || __GLIBCXX__ == 20150623              \
0611         || __GLIBCXX__ == 20150626 || __GLIBCXX__ == 20160803)
0612              // libstdc++ < 5 (including versions 4.8.5, 4.9.3 and 4.9.4 which were released after
0613              // 5) don't implement is_trivially_copyable, so approximate it
0614              std::is_trivially_destructible<T>,
0615              satisfies_any_of<T, std::has_trivial_copy_constructor, std::has_trivial_copy_assign>,
0616 #else
0617              std::is_trivially_copyable<T>,
0618 #endif
0619              satisfies_none_of<T,
0620                                std::is_reference,
0621                                std::is_array,
0622                                is_std_array,
0623                                std::is_arithmetic,
0624                                is_complex,
0625                                std::is_enum>>;
0626 
0627 // Replacement for std::is_pod (deprecated in C++20)
0628 template <typename T>
0629 using is_pod = all_of<std::is_standard_layout<T>, std::is_trivial<T>>;
0630 
0631 template <ssize_t Dim = 0, typename Strides>
0632 ssize_t byte_offset_unsafe(const Strides &) {
0633     return 0;
0634 }
0635 template <ssize_t Dim = 0, typename Strides, typename... Ix>
0636 ssize_t byte_offset_unsafe(const Strides &strides, ssize_t i, Ix... index) {
0637     return i * strides[Dim] + byte_offset_unsafe<Dim + 1>(strides, index...);
0638 }
0639 
0640 /**
0641  * Proxy class providing unsafe, unchecked const access to array data.  This is constructed through
0642  * the `unchecked<T, N>()` method of `array` or the `unchecked<N>()` method of `array_t<T>`. `Dims`
0643  * will be -1 for dimensions determined at runtime.
0644  */
0645 template <typename T, ssize_t Dims>
0646 class unchecked_reference {
0647 protected:
0648     static constexpr bool Dynamic = Dims < 0;
0649     const unsigned char *data_;
0650     // Storing the shape & strides in local variables (i.e. these arrays) allows the compiler to
0651     // make large performance gains on big, nested loops, but requires compile-time dimensions
0652     conditional_t<Dynamic, const ssize_t *, std::array<ssize_t, (size_t) Dims>> shape_, strides_;
0653     const ssize_t dims_;
0654 
0655     friend class pybind11::array;
0656     // Constructor for compile-time dimensions:
0657     template <bool Dyn = Dynamic>
0658     unchecked_reference(const void *data,
0659                         const ssize_t *shape,
0660                         const ssize_t *strides,
0661                         enable_if_t<!Dyn, ssize_t>)
0662         : data_{reinterpret_cast<const unsigned char *>(data)}, dims_{Dims} {
0663         for (size_t i = 0; i < (size_t) dims_; i++) {
0664             shape_[i] = shape[i];
0665             strides_[i] = strides[i];
0666         }
0667     }
0668     // Constructor for runtime dimensions:
0669     template <bool Dyn = Dynamic>
0670     unchecked_reference(const void *data,
0671                         const ssize_t *shape,
0672                         const ssize_t *strides,
0673                         enable_if_t<Dyn, ssize_t> dims)
0674         : data_{reinterpret_cast<const unsigned char *>(data)}, shape_{shape}, strides_{strides},
0675           dims_{dims} {}
0676 
0677 public:
0678     /**
0679      * Unchecked const reference access to data at the given indices.  For a compile-time known
0680      * number of dimensions, this requires the correct number of arguments; for run-time
0681      * dimensionality, this is not checked (and so is up to the caller to use safely).
0682      */
0683     template <typename... Ix>
0684     const T &operator()(Ix... index) const {
0685         static_assert(ssize_t{sizeof...(Ix)} == Dims || Dynamic,
0686                       "Invalid number of indices for unchecked array reference");
0687         return *reinterpret_cast<const T *>(data_
0688                                             + byte_offset_unsafe(strides_, ssize_t(index)...));
0689     }
0690     /**
0691      * Unchecked const reference access to data; this operator only participates if the reference
0692      * is to a 1-dimensional array.  When present, this is exactly equivalent to `obj(index)`.
0693      */
0694     template <ssize_t D = Dims, typename = enable_if_t<D == 1 || Dynamic>>
0695     const T &operator[](ssize_t index) const {
0696         return operator()(index);
0697     }
0698 
0699     /// Pointer access to the data at the given indices.
0700     template <typename... Ix>
0701     const T *data(Ix... ix) const {
0702         return &operator()(ssize_t(ix)...);
0703     }
0704 
0705     /// Returns the item size, i.e. sizeof(T)
0706     constexpr static ssize_t itemsize() { return sizeof(T); }
0707 
0708     /// Returns the shape (i.e. size) of dimension `dim`
0709     ssize_t shape(ssize_t dim) const { return shape_[(size_t) dim]; }
0710 
0711     /// Returns the number of dimensions of the array
0712     ssize_t ndim() const { return dims_; }
0713 
0714     /// Returns the total number of elements in the referenced array, i.e. the product of the
0715     /// shapes
0716     template <bool Dyn = Dynamic>
0717     enable_if_t<!Dyn, ssize_t> size() const {
0718         return std::accumulate(
0719             shape_.begin(), shape_.end(), (ssize_t) 1, std::multiplies<ssize_t>());
0720     }
0721     template <bool Dyn = Dynamic>
0722     enable_if_t<Dyn, ssize_t> size() const {
0723         return std::accumulate(shape_, shape_ + ndim(), (ssize_t) 1, std::multiplies<ssize_t>());
0724     }
0725 
0726     /// Returns the total number of bytes used by the referenced data.  Note that the actual span
0727     /// in memory may be larger if the referenced array has non-contiguous strides (e.g. for a
0728     /// slice).
0729     ssize_t nbytes() const { return size() * itemsize(); }
0730 };
0731 
0732 template <typename T, ssize_t Dims>
0733 class unchecked_mutable_reference : public unchecked_reference<T, Dims> {
0734     friend class pybind11::array;
0735     using ConstBase = unchecked_reference<T, Dims>;
0736     using ConstBase::ConstBase;
0737     using ConstBase::Dynamic;
0738 
0739 public:
0740     // Bring in const-qualified versions from base class
0741     using ConstBase::operator();
0742     using ConstBase::operator[];
0743 
0744     /// Mutable, unchecked access to data at the given indices.
0745     template <typename... Ix>
0746     T &operator()(Ix... index) {
0747         static_assert(ssize_t{sizeof...(Ix)} == Dims || Dynamic,
0748                       "Invalid number of indices for unchecked array reference");
0749         return const_cast<T &>(ConstBase::operator()(index...));
0750     }
0751     /**
0752      * Mutable, unchecked access data at the given index; this operator only participates if the
0753      * reference is to a 1-dimensional array (or has runtime dimensions).  When present, this is
0754      * exactly equivalent to `obj(index)`.
0755      */
0756     template <ssize_t D = Dims, typename = enable_if_t<D == 1 || Dynamic>>
0757     T &operator[](ssize_t index) {
0758         return operator()(index);
0759     }
0760 
0761     /// Mutable pointer access to the data at the given indices.
0762     template <typename... Ix>
0763     T *mutable_data(Ix... ix) {
0764         return &operator()(ssize_t(ix)...);
0765     }
0766 };
0767 
0768 template <typename T, ssize_t Dim>
0769 struct type_caster<unchecked_reference<T, Dim>> {
0770     static_assert(Dim == 0 && Dim > 0 /* always fail */,
0771                   "unchecked array proxy object is not castable");
0772 };
0773 template <typename T, ssize_t Dim>
0774 struct type_caster<unchecked_mutable_reference<T, Dim>>
0775     : type_caster<unchecked_reference<T, Dim>> {};
0776 
0777 template <typename T>
0778 struct type_caster<numpy_scalar<T>> {
0779     using value_type = T;
0780     using type_info = numpy_scalar_info<T>;
0781 
0782     PYBIND11_TYPE_CASTER(numpy_scalar<T>, type_info::name);
0783 
0784     static handle &target_type() {
0785         static handle tp = npy_api::get().PyArray_TypeObjectFromType_(type_info::typenum);
0786         return tp;
0787     }
0788 
0789     static handle &target_dtype() {
0790         static handle tp = npy_api::get().PyArray_DescrFromType_(type_info::typenum);
0791         return tp;
0792     }
0793 
0794     bool load(handle src, bool) {
0795         if (isinstance(src, target_type())) {
0796             npy_api::get().PyArray_ScalarAsCtype_(src.ptr(), &value.value);
0797             return true;
0798         }
0799         return false;
0800     }
0801 
0802     static handle cast(numpy_scalar<T> src, return_value_policy, handle) {
0803         return npy_api::get().PyArray_Scalar_(&src.value, target_dtype().ptr(), nullptr);
0804     }
0805 };
0806 
0807 PYBIND11_NAMESPACE_END(detail)
0808 
0809 template <typename T>
0810 struct numpy_scalar {
0811     using value_type = T;
0812 
0813     value_type value;
0814 
0815     numpy_scalar() = default;
0816     explicit numpy_scalar(value_type value) : value(value) {}
0817 
0818     explicit operator value_type() const { return value; }
0819     numpy_scalar &operator=(value_type value) {
0820         this->value = value;
0821         return *this;
0822     }
0823 
0824     friend bool operator==(const numpy_scalar &a, const numpy_scalar &b) {
0825         return a.value == b.value;
0826     }
0827 
0828     friend bool operator!=(const numpy_scalar &a, const numpy_scalar &b) { return !(a == b); }
0829 };
0830 
0831 template <typename T>
0832 numpy_scalar<T> make_scalar(T value) {
0833     return numpy_scalar<T>(value);
0834 }
0835 
0836 class dtype : public object {
0837 public:
0838     PYBIND11_OBJECT_DEFAULT(dtype, object, detail::npy_api::get().PyArrayDescr_Check_)
0839 
0840     explicit dtype(const buffer_info &info) {
0841         dtype descr(_dtype_from_pep3118()(pybind11::str(info.format)));
0842         // If info.itemsize == 0, use the value calculated from the format string
0843         m_ptr = descr.strip_padding(info.itemsize != 0 ? info.itemsize : descr.itemsize())
0844                     .release()
0845                     .ptr();
0846     }
0847 
0848     explicit dtype(const pybind11::str &format) : dtype(from_args(format)) {}
0849 
0850     explicit dtype(const std::string &format) : dtype(pybind11::str(format)) {}
0851 
0852     explicit dtype(const char *format) : dtype(pybind11::str(format)) {}
0853 
0854     dtype(list names, list formats, list offsets, ssize_t itemsize) {
0855         dict args;
0856         args["names"] = std::move(names);
0857         args["formats"] = std::move(formats);
0858         args["offsets"] = std::move(offsets);
0859         args["itemsize"] = pybind11::int_(itemsize);
0860         m_ptr = from_args(args).release().ptr();
0861     }
0862 
0863     /// Return dtype for the given typenum (one of the NPY_TYPES).
0864     /// https://numpy.org/devdocs/reference/c-api/array.html#c.PyArray_DescrFromType
0865     explicit dtype(int typenum)
0866         : object(detail::npy_api::get().PyArray_DescrFromType_(typenum), stolen_t{}) {
0867         if (m_ptr == nullptr) {
0868             throw error_already_set();
0869         }
0870     }
0871 
0872     /// This is essentially the same as calling numpy.dtype(args) in Python.
0873     static dtype from_args(const object &args) {
0874         PyObject *ptr = nullptr;
0875         if ((detail::npy_api::get().PyArray_DescrConverter_(args.ptr(), &ptr) == 0) || !ptr) {
0876             throw error_already_set();
0877         }
0878         return reinterpret_steal<dtype>(ptr);
0879     }
0880 
0881     /// Return dtype associated with a C++ type.
0882     template <typename T>
0883     static dtype of() {
0884         return detail::npy_format_descriptor<typename std::remove_cv<T>::type>::dtype();
0885     }
0886 
0887     /// Return the type number associated with a C++ type.
0888     /// This is the constexpr equivalent of `dtype::of<T>().num()`.
0889     template <typename T>
0890     static constexpr int num_of() {
0891         return detail::npy_format_descriptor<typename std::remove_cv<T>::type>::value;
0892     }
0893 
0894     /// Size of the data type in bytes.
0895     ssize_t itemsize() const {
0896         if (detail::npy_api::get().PyArray_RUNTIME_VERSION_ < 0x12) {
0897             return detail::array_descriptor1_proxy(m_ptr)->elsize;
0898         }
0899         return detail::array_descriptor2_proxy(m_ptr)->elsize;
0900     }
0901 
0902     /// Returns true for structured data types.
0903     bool has_fields() const {
0904         if (detail::npy_api::get().PyArray_RUNTIME_VERSION_ < 0x12) {
0905             return detail::array_descriptor1_proxy(m_ptr)->names != nullptr;
0906         }
0907         const auto *proxy = detail::array_descriptor2_proxy(m_ptr);
0908         if (proxy->type_num < 0 || proxy->type_num >= 2056) {
0909             return false;
0910         }
0911         return proxy->names != nullptr;
0912     }
0913 
0914     /// Single-character code for dtype's kind.
0915     /// For example, floating point types are 'f' and integral types are 'i'.
0916     char kind() const { return detail::array_descriptor_proxy(m_ptr)->kind; }
0917 
0918     /// Single-character for dtype's type.
0919     /// For example, ``float`` is 'f', ``double`` 'd', ``int`` 'i', and ``long`` 'l'.
0920     char char_() const {
0921         // Note: The signature, `dtype::char_` follows the naming of NumPy's
0922         // public Python API (i.e., ``dtype.char``), rather than its internal
0923         // C API (``PyArray_Descr::type``).
0924         return detail::array_descriptor_proxy(m_ptr)->type;
0925     }
0926 
0927     /// Type number of dtype. Note that different values may be returned for equivalent types,
0928     /// e.g. even though ``long`` may be equivalent to ``int`` or ``long long``, they still have
0929     /// different type numbers. Consider using `normalized_num` to avoid this.
0930     int num() const {
0931         // Note: The signature, `dtype::num` follows the naming of NumPy's public
0932         // Python API (i.e., ``dtype.num``), rather than its internal
0933         // C API (``PyArray_Descr::type_num``).
0934         return detail::array_descriptor_proxy(m_ptr)->type_num;
0935     }
0936 
0937     /// Type number of dtype, normalized to match the return value of `num_of` for equivalent
0938     /// types. This function can be used to write switch statements that correctly handle
0939     /// equivalent types with different type numbers.
0940     int normalized_num() const {
0941         int value = num();
0942         if (value >= 0 && value <= detail::npy_api::NPY_VOID_) {
0943             return detail::normalized_dtype_num[value];
0944         }
0945         return value;
0946     }
0947 
0948     /// Single character for byteorder
0949     char byteorder() const { return detail::array_descriptor_proxy(m_ptr)->byteorder; }
0950 
0951     /// Alignment of the data type
0952     ssize_t alignment() const {
0953         if (detail::npy_api::get().PyArray_RUNTIME_VERSION_ < 0x12) {
0954             return detail::array_descriptor1_proxy(m_ptr)->alignment;
0955         }
0956         return detail::array_descriptor2_proxy(m_ptr)->alignment;
0957     }
0958 
0959     /// Flags for the array descriptor
0960     std::uint64_t flags() const {
0961         if (detail::npy_api::get().PyArray_RUNTIME_VERSION_ < 0x12) {
0962             return (unsigned char) detail::array_descriptor1_proxy(m_ptr)->flags;
0963         }
0964         return detail::array_descriptor2_proxy(m_ptr)->flags;
0965     }
0966 
0967 private:
0968     static object &_dtype_from_pep3118() {
0969         PYBIND11_CONSTINIT static gil_safe_call_once_and_store<object> storage;
0970         return storage
0971             .call_once_and_store_result([]() {
0972                 return detail::import_numpy_core_submodule("_internal")
0973                     .attr("_dtype_from_pep3118");
0974             })
0975             .get_stored();
0976     }
0977 
0978     dtype strip_padding(ssize_t itemsize) {
0979         // Recursively strip all void fields with empty names that are generated for
0980         // padding fields (as of NumPy v1.11).
0981         if (!has_fields()) {
0982             return *this;
0983         }
0984 
0985         struct field_descr {
0986             pybind11::str name;
0987             object format;
0988             pybind11::int_ offset;
0989             field_descr(pybind11::str &&name, object &&format, pybind11::int_ &&offset)
0990                 : name{std::move(name)}, format{std::move(format)}, offset{std::move(offset)} {};
0991         };
0992         auto field_dict = attr("fields").cast<dict>();
0993         std::vector<field_descr> field_descriptors;
0994         field_descriptors.reserve(field_dict.size());
0995 
0996         for (auto field : field_dict.attr("items")()) {
0997             auto spec = field.cast<tuple>();
0998             auto name = spec[0].cast<pybind11::str>();
0999             auto spec_fo = spec[1].cast<tuple>();
1000             auto format = spec_fo[0].cast<dtype>();
1001             auto offset = spec_fo[1].cast<pybind11::int_>();
1002             if ((len(name) == 0u) && format.kind() == 'V') {
1003                 continue;
1004             }
1005             field_descriptors.emplace_back(
1006                 std::move(name), format.strip_padding(format.itemsize()), std::move(offset));
1007         }
1008 
1009         std::sort(field_descriptors.begin(),
1010                   field_descriptors.end(),
1011                   [](const field_descr &a, const field_descr &b) {
1012                       return a.offset.cast<int>() < b.offset.cast<int>();
1013                   });
1014 
1015         list names, formats, offsets;
1016         for (auto &descr : field_descriptors) {
1017             names.append(std::move(descr.name));
1018             formats.append(std::move(descr.format));
1019             offsets.append(std::move(descr.offset));
1020         }
1021         return dtype(std::move(names), std::move(formats), std::move(offsets), itemsize);
1022     }
1023 };
1024 
1025 class array : public buffer {
1026 public:
1027     PYBIND11_OBJECT_CVT(array, buffer, detail::npy_api::get().PyArray_Check_, raw_array)
1028 
1029     enum {
1030         c_style = detail::npy_api::NPY_ARRAY_C_CONTIGUOUS_,
1031         f_style = detail::npy_api::NPY_ARRAY_F_CONTIGUOUS_,
1032         forcecast = detail::npy_api::NPY_ARRAY_FORCECAST_
1033     };
1034 
1035     array() : array(0, static_cast<const double *>(nullptr)) {}
1036 
1037     using ShapeContainer = detail::any_container<ssize_t>;
1038     using StridesContainer = detail::any_container<ssize_t>;
1039 
1040     // Constructs an array taking shape/strides from arbitrary container types
1041     array(const pybind11::dtype &dt,
1042           ShapeContainer shape,
1043           StridesContainer strides,
1044           const void *ptr = nullptr,
1045           handle base = handle()) {
1046 
1047         if (strides->empty()) {
1048             *strides = detail::c_strides(*shape, dt.itemsize());
1049         }
1050 
1051         auto ndim = shape->size();
1052         if (ndim != strides->size()) {
1053             pybind11_fail("NumPy: shape ndim doesn't match strides ndim");
1054         }
1055         auto descr = dt;
1056 
1057         int flags = 0;
1058         if (base && ptr) {
1059             if (isinstance<array>(base)) {
1060                 /* Copy flags from base (except ownership bit) */
1061                 flags = reinterpret_borrow<array>(base).flags()
1062                         & ~detail::npy_api::NPY_ARRAY_OWNDATA_;
1063             } else {
1064                 /* Writable by default, easy to downgrade later on if needed */
1065                 flags = detail::npy_api::NPY_ARRAY_WRITEABLE_;
1066             }
1067         }
1068 
1069         auto &api = detail::npy_api::get();
1070         auto tmp = reinterpret_steal<object>(api.PyArray_NewFromDescr_(
1071             api.PyArray_Type_,
1072             descr.release().ptr(),
1073             (int) ndim,
1074             // Use reinterpret_cast for PyPy on Windows (remove if fixed, checked on 7.3.1)
1075             reinterpret_cast<Py_intptr_t *>(shape->data()),
1076             reinterpret_cast<Py_intptr_t *>(strides->data()),
1077             const_cast<void *>(ptr),
1078             flags,
1079             nullptr));
1080         if (!tmp) {
1081             throw error_already_set();
1082         }
1083         if (ptr) {
1084             if (base) {
1085                 api.PyArray_SetBaseObject_(tmp.ptr(), base.inc_ref().ptr());
1086             } else {
1087                 tmp = reinterpret_steal<object>(
1088                     api.PyArray_NewCopy_(tmp.ptr(), -1 /* any order */));
1089             }
1090         }
1091         m_ptr = tmp.release().ptr();
1092     }
1093 
1094     array(const pybind11::dtype &dt,
1095           ShapeContainer shape,
1096           const void *ptr = nullptr,
1097           handle base = handle())
1098         : array(dt, std::move(shape), {}, ptr, base) {}
1099 
1100     template <typename T,
1101               typename
1102               = detail::enable_if_t<std::is_integral<T>::value && !std::is_same<bool, T>::value>>
1103     array(const pybind11::dtype &dt, T count, const void *ptr = nullptr, handle base = handle())
1104         : array(dt, {{count}}, ptr, base) {}
1105 
1106     template <typename T>
1107     array(ShapeContainer shape, StridesContainer strides, const T *ptr, handle base = handle())
1108         : array(pybind11::dtype::of<T>(),
1109                 std::move(shape),
1110                 std::move(strides),
1111                 reinterpret_cast<const void *>(ptr),
1112                 base) {}
1113 
1114     template <typename T>
1115     array(ShapeContainer shape, const T *ptr, handle base = handle())
1116         : array(std::move(shape), {}, ptr, base) {}
1117 
1118     template <typename T>
1119     explicit array(ssize_t count, const T *ptr, handle base = handle())
1120         : array({count}, {}, ptr, base) {}
1121 
1122     explicit array(const buffer_info &info, handle base = handle())
1123         : array(pybind11::dtype(info), info.shape, info.strides, info.ptr, base) {}
1124 
1125     /// Array descriptor (dtype)
1126     pybind11::dtype dtype() const {
1127         return reinterpret_borrow<pybind11::dtype>(detail::array_proxy(m_ptr)->descr);
1128     }
1129 
1130     /// Total number of elements
1131     ssize_t size() const {
1132         return std::accumulate(shape(), shape() + ndim(), (ssize_t) 1, std::multiplies<ssize_t>());
1133     }
1134 
1135     /// Byte size of a single element
1136     ssize_t itemsize() const { return dtype().itemsize(); }
1137 
1138     /// Total number of bytes
1139     ssize_t nbytes() const { return size() * itemsize(); }
1140 
1141     /// Number of dimensions
1142     ssize_t ndim() const { return detail::array_proxy(m_ptr)->nd; }
1143 
1144     /// Base object
1145     object base() const { return reinterpret_borrow<object>(detail::array_proxy(m_ptr)->base); }
1146 
1147     /// Dimensions of the array
1148     const ssize_t *shape() const { return detail::array_proxy(m_ptr)->dimensions; }
1149 
1150 #ifdef PYBIND11_HAS_SPAN
1151     /// Dimensions of the array as a span
1152     std::span<const ssize_t, std::dynamic_extent> shape_span() const {
1153         return std::span(shape(), static_cast<std::size_t>(ndim()));
1154     }
1155 #endif
1156 
1157     /// Dimension along a given axis
1158     ssize_t shape(ssize_t dim) const {
1159         if (dim >= ndim()) {
1160             fail_dim_check(dim, "invalid axis");
1161         }
1162         return shape()[dim];
1163     }
1164 
1165     /// Strides of the array
1166     const ssize_t *strides() const { return detail::array_proxy(m_ptr)->strides; }
1167 
1168 #ifdef PYBIND11_HAS_SPAN
1169     /// Strides of the array as a span
1170     std::span<const ssize_t, std::dynamic_extent> strides_span() const {
1171         return std::span(strides(), static_cast<std::size_t>(ndim()));
1172     }
1173 #endif
1174 
1175     /// Stride along a given axis
1176     ssize_t strides(ssize_t dim) const {
1177         if (dim >= ndim()) {
1178             fail_dim_check(dim, "invalid axis");
1179         }
1180         return strides()[dim];
1181     }
1182 
1183     /// Return the NumPy array flags
1184     int flags() const { return detail::array_proxy(m_ptr)->flags; }
1185 
1186     /// If set, the array is writeable (otherwise the buffer is read-only)
1187     bool writeable() const {
1188         return detail::check_flags(m_ptr, detail::npy_api::NPY_ARRAY_WRITEABLE_);
1189     }
1190 
1191     /// If set, the array owns the data (will be freed when the array is deleted)
1192     bool owndata() const {
1193         return detail::check_flags(m_ptr, detail::npy_api::NPY_ARRAY_OWNDATA_);
1194     }
1195 
1196     /// Pointer to the contained data. If index is not provided, points to the
1197     /// beginning of the buffer. May throw if the index would lead to out of bounds access.
1198     template <typename... Ix>
1199     const void *data(Ix... index) const {
1200         return static_cast<const void *>(detail::array_proxy(m_ptr)->data + offset_at(index...));
1201     }
1202 
1203     /// Mutable pointer to the contained data. If index is not provided, points to the
1204     /// beginning of the buffer. May throw if the index would lead to out of bounds access.
1205     /// May throw if the array is not writeable.
1206     template <typename... Ix>
1207     void *mutable_data(Ix... index) {
1208         check_writeable();
1209         return static_cast<void *>(detail::array_proxy(m_ptr)->data + offset_at(index...));
1210     }
1211 
1212     /// Byte offset from beginning of the array to a given index (full or partial).
1213     /// May throw if the index would lead to out of bounds access.
1214     template <typename... Ix>
1215     ssize_t offset_at(Ix... index) const {
1216         if ((ssize_t) sizeof...(index) > ndim()) {
1217             fail_dim_check(sizeof...(index), "too many indices for an array");
1218         }
1219         return byte_offset(ssize_t(index)...);
1220     }
1221 
1222     ssize_t offset_at() const { return 0; }
1223 
1224     /// Item count from beginning of the array to a given index (full or partial).
1225     /// May throw if the index would lead to out of bounds access.
1226     template <typename... Ix>
1227     ssize_t index_at(Ix... index) const {
1228         return offset_at(index...) / itemsize();
1229     }
1230 
1231     /**
1232      * Returns a proxy object that provides access to the array's data without bounds or
1233      * dimensionality checking.  Will throw if the array is missing the `writeable` flag.  Use with
1234      * care: the array must not be destroyed or reshaped for the duration of the returned object,
1235      * and the caller must take care not to access invalid dimensions or dimension indices.
1236      */
1237     template <typename T, ssize_t Dims = -1>
1238     detail::unchecked_mutable_reference<T, Dims> mutable_unchecked() & {
1239         if (Dims >= 0 && ndim() != Dims) {
1240             throw std::domain_error("array has incorrect number of dimensions: "
1241                                     + std::to_string(ndim()) + "; expected "
1242                                     + std::to_string(Dims));
1243         }
1244         return detail::unchecked_mutable_reference<T, Dims>(
1245             mutable_data(), shape(), strides(), ndim());
1246     }
1247 
1248     /**
1249      * Returns a proxy object that provides const access to the array's data without bounds or
1250      * dimensionality checking.  Unlike `mutable_unchecked()`, this does not require that the
1251      * underlying array have the `writable` flag.  Use with care: the array must not be destroyed
1252      * or reshaped for the duration of the returned object, and the caller must take care not to
1253      * access invalid dimensions or dimension indices.
1254      */
1255     template <typename T, ssize_t Dims = -1>
1256     detail::unchecked_reference<T, Dims> unchecked() const & {
1257         if (Dims >= 0 && ndim() != Dims) {
1258             throw std::domain_error("array has incorrect number of dimensions: "
1259                                     + std::to_string(ndim()) + "; expected "
1260                                     + std::to_string(Dims));
1261         }
1262         return detail::unchecked_reference<T, Dims>(data(), shape(), strides(), ndim());
1263     }
1264 
1265     /// Return a new view with all of the dimensions of length 1 removed
1266     array squeeze() {
1267         auto &api = detail::npy_api::get();
1268         return reinterpret_steal<array>(api.PyArray_Squeeze_(m_ptr));
1269     }
1270 
1271     /// Resize array to given shape
1272     /// If refcheck is true and more that one reference exist to this array
1273     /// then resize will succeed only if it makes a reshape, i.e. original size doesn't change
1274     void resize(ShapeContainer new_shape, bool refcheck = true) {
1275         detail::npy_api::PyArray_Dims d
1276             = {// Use reinterpret_cast for PyPy on Windows (remove if fixed, checked on 7.3.1)
1277                reinterpret_cast<Py_intptr_t *>(new_shape->data()),
1278                int(new_shape->size())};
1279         // try to resize, set ordering param to -1 cause it's not used anyway
1280         auto new_array = reinterpret_steal<object>(
1281             detail::npy_api::get().PyArray_Resize_(m_ptr, &d, int(refcheck), -1));
1282         if (!new_array) {
1283             throw error_already_set();
1284         }
1285         if (isinstance<array>(new_array)) {
1286             *this = std::move(new_array);
1287         }
1288     }
1289 
1290     /// Optional `order` parameter omitted, to be added as needed.
1291     array reshape(ShapeContainer new_shape) {
1292         detail::npy_api::PyArray_Dims d
1293             = {reinterpret_cast<Py_intptr_t *>(new_shape->data()), int(new_shape->size())};
1294         auto new_array
1295             = reinterpret_steal<array>(detail::npy_api::get().PyArray_Newshape_(m_ptr, &d, 0));
1296         if (!new_array) {
1297             throw error_already_set();
1298         }
1299         return new_array;
1300     }
1301 
1302     /// Create a view of an array in a different data type.
1303     /// This function may fundamentally reinterpret the data in the array.
1304     /// It is the responsibility of the caller to ensure that this is safe.
1305     /// Only supports the `dtype` argument, the `type` argument is omitted,
1306     /// to be added as needed.
1307     array view(const std::string &dtype) {
1308         auto &api = detail::npy_api::get();
1309         auto new_view = reinterpret_steal<array>(api.PyArray_View_(
1310             m_ptr, dtype::from_args(pybind11::str(dtype)).release().ptr(), nullptr));
1311         if (!new_view) {
1312             throw error_already_set();
1313         }
1314         return new_view;
1315     }
1316 
1317     /// Ensure that the argument is a NumPy array
1318     /// In case of an error, nullptr is returned and the Python error is cleared.
1319     static array ensure(handle h, int ExtraFlags = 0) {
1320         auto result = reinterpret_steal<array>(raw_array(h.ptr(), ExtraFlags));
1321         if (!result) {
1322             PyErr_Clear();
1323         }
1324         return result;
1325     }
1326 
1327 protected:
1328     template <typename, typename>
1329     friend struct detail::npy_format_descriptor;
1330 
1331     void fail_dim_check(ssize_t dim, const std::string &msg) const {
1332         throw index_error(msg + ": " + std::to_string(dim) + " (ndim = " + std::to_string(ndim())
1333                           + ')');
1334     }
1335 
1336     template <typename... Ix>
1337     ssize_t byte_offset(Ix... index) const {
1338         check_dimensions(index...);
1339         return detail::byte_offset_unsafe(strides(), ssize_t(index)...);
1340     }
1341 
1342     void check_writeable() const {
1343         if (!writeable()) {
1344             throw std::domain_error("array is not writeable");
1345         }
1346     }
1347 
1348     template <typename... Ix>
1349     void check_dimensions(Ix... index) const {
1350         check_dimensions_impl(ssize_t(0), shape(), ssize_t(index)...);
1351     }
1352 
1353     void check_dimensions_impl(ssize_t, const ssize_t *) const {}
1354 
1355     template <typename... Ix>
1356     void check_dimensions_impl(ssize_t axis, const ssize_t *shape, ssize_t i, Ix... index) const {
1357         if (i >= *shape) {
1358             throw index_error(std::string("index ") + std::to_string(i)
1359                               + " is out of bounds for axis " + std::to_string(axis)
1360                               + " with size " + std::to_string(*shape));
1361         }
1362         check_dimensions_impl(axis + 1, shape + 1, index...);
1363     }
1364 
1365     /// Create array from any object -- always returns a new reference
1366     static PyObject *raw_array(PyObject *ptr, int ExtraFlags = 0) {
1367         if (ptr == nullptr) {
1368             set_error(PyExc_ValueError, "cannot create a pybind11::array from a nullptr");
1369             return nullptr;
1370         }
1371         return detail::npy_api::get().PyArray_FromAny_(
1372             ptr, nullptr, 0, 0, detail::npy_api::NPY_ARRAY_ENSUREARRAY_ | ExtraFlags, nullptr);
1373     }
1374 };
1375 
1376 template <typename T, int ExtraFlags = array::forcecast>
1377 class array_t : public array {
1378 private:
1379     struct private_ctor {};
1380     // Delegating constructor needed when both moving and accessing in the same constructor
1381     array_t(private_ctor,
1382             ShapeContainer &&shape,
1383             StridesContainer &&strides,
1384             const T *ptr,
1385             handle base)
1386         : array(std::move(shape), std::move(strides), ptr, base) {}
1387 
1388 public:
1389     static_assert(!detail::array_info<T>::is_array, "Array types cannot be used with array_t");
1390 
1391     using value_type = T;
1392 
1393     array_t() : array(0, static_cast<const T *>(nullptr)) {}
1394     array_t(handle h, borrowed_t) : array(h, borrowed_t{}) {}
1395     array_t(handle h, stolen_t) : array(h, stolen_t{}) {}
1396 
1397     PYBIND11_DEPRECATED("Use array_t<T>::ensure() instead")
1398     array_t(handle h, bool is_borrowed) : array(raw_array_t(h.ptr()), stolen_t{}) {
1399         if (!m_ptr) {
1400             PyErr_Clear();
1401         }
1402         if (!is_borrowed) {
1403             Py_XDECREF(h.ptr());
1404         }
1405     }
1406 
1407     // NOLINTNEXTLINE(google-explicit-constructor)
1408     array_t(const object &o) : array(raw_array_t(o.ptr()), stolen_t{}) {
1409         if (!m_ptr) {
1410             throw error_already_set();
1411         }
1412     }
1413 
1414     explicit array_t(const buffer_info &info, handle base = handle()) : array(info, base) {}
1415 
1416     array_t(ShapeContainer shape,
1417             StridesContainer strides,
1418             const T *ptr = nullptr,
1419             handle base = handle())
1420         : array(std::move(shape), std::move(strides), ptr, base) {}
1421 
1422     explicit array_t(ShapeContainer shape, const T *ptr = nullptr, handle base = handle())
1423         : array_t(private_ctor{},
1424                   std::move(shape),
1425                   (ExtraFlags & f_style) != 0 ? detail::f_strides(*shape, itemsize())
1426                                               : detail::c_strides(*shape, itemsize()),
1427                   ptr,
1428                   base) {}
1429 
1430     explicit array_t(ssize_t count, const T *ptr = nullptr, handle base = handle())
1431         : array({count}, {}, ptr, base) {}
1432 
1433     constexpr ssize_t itemsize() const { return sizeof(T); }
1434 
1435     template <typename... Ix>
1436     ssize_t index_at(Ix... index) const {
1437         return offset_at(index...) / itemsize();
1438     }
1439 
1440     template <typename... Ix>
1441     const T *data(Ix... index) const {
1442         return static_cast<const T *>(array::data(index...));
1443     }
1444 
1445     template <typename... Ix>
1446     T *mutable_data(Ix... index) {
1447         return static_cast<T *>(array::mutable_data(index...));
1448     }
1449 
1450     // Reference to element at a given index
1451     template <typename... Ix>
1452     const T &at(Ix... index) const {
1453         if ((ssize_t) sizeof...(index) != ndim()) {
1454             fail_dim_check(sizeof...(index), "index dimension mismatch");
1455         }
1456         return *(static_cast<const T *>(array::data())
1457                  + byte_offset(ssize_t(index)...) / itemsize());
1458     }
1459 
1460     // Mutable reference to element at a given index
1461     template <typename... Ix>
1462     T &mutable_at(Ix... index) {
1463         if ((ssize_t) sizeof...(index) != ndim()) {
1464             fail_dim_check(sizeof...(index), "index dimension mismatch");
1465         }
1466         return *(static_cast<T *>(array::mutable_data())
1467                  + byte_offset(ssize_t(index)...) / itemsize());
1468     }
1469 
1470     /**
1471      * Returns a proxy object that provides access to the array's data without bounds or
1472      * dimensionality checking.  Will throw if the array is missing the `writeable` flag.  Use with
1473      * care: the array must not be destroyed or reshaped for the duration of the returned object,
1474      * and the caller must take care not to access invalid dimensions or dimension indices.
1475      */
1476     template <ssize_t Dims = -1>
1477     detail::unchecked_mutable_reference<T, Dims> mutable_unchecked() & {
1478         return array::mutable_unchecked<T, Dims>();
1479     }
1480 
1481     /**
1482      * Returns a proxy object that provides const access to the array's data without bounds or
1483      * dimensionality checking.  Unlike `mutable_unchecked()`, this does not require that the
1484      * underlying array have the `writable` flag.  Use with care: the array must not be destroyed
1485      * or reshaped for the duration of the returned object, and the caller must take care not to
1486      * access invalid dimensions or dimension indices.
1487      */
1488     template <ssize_t Dims = -1>
1489     detail::unchecked_reference<T, Dims> unchecked() const & {
1490         return array::unchecked<T, Dims>();
1491     }
1492 
1493     /// Ensure that the argument is a NumPy array of the correct dtype (and if not, try to convert
1494     /// it).  In case of an error, nullptr is returned and the Python error is cleared.
1495     static array_t ensure(handle h) {
1496         auto result = reinterpret_steal<array_t>(raw_array_t(h.ptr()));
1497         if (!result) {
1498             PyErr_Clear();
1499         }
1500         return result;
1501     }
1502 
1503     static bool check_(handle h) {
1504         const auto &api = detail::npy_api::get();
1505         return api.PyArray_Check_(h.ptr())
1506                && api.PyArray_EquivTypes_(detail::array_proxy(h.ptr())->descr,
1507                                           dtype::of<T>().ptr())
1508                && detail::check_flags(h.ptr(), ExtraFlags & (array::c_style | array::f_style));
1509     }
1510 
1511 protected:
1512     /// Create array from any object -- always returns a new reference
1513     static PyObject *raw_array_t(PyObject *ptr) {
1514         if (ptr == nullptr) {
1515             set_error(PyExc_ValueError, "cannot create a pybind11::array_t from a nullptr");
1516             return nullptr;
1517         }
1518         return detail::npy_api::get().PyArray_FromAny_(ptr,
1519                                                        dtype::of<T>().release().ptr(),
1520                                                        0,
1521                                                        0,
1522                                                        detail::npy_api::NPY_ARRAY_ENSUREARRAY_
1523                                                            | ExtraFlags,
1524                                                        nullptr);
1525     }
1526 };
1527 
1528 template <typename T>
1529 struct format_descriptor<T, detail::enable_if_t<detail::is_pod_struct<T>::value>> {
1530     static std::string format() {
1531         return detail::npy_format_descriptor<typename std::remove_cv<T>::type>::format();
1532     }
1533 };
1534 
1535 template <size_t N>
1536 struct format_descriptor<char[N]> {
1537     static std::string format() { return std::to_string(N) + 's'; }
1538 };
1539 template <size_t N>
1540 struct format_descriptor<std::array<char, N>> {
1541     static std::string format() { return std::to_string(N) + 's'; }
1542 };
1543 
1544 template <typename T>
1545 struct format_descriptor<T, detail::enable_if_t<std::is_enum<T>::value>> {
1546     static std::string format() {
1547         return format_descriptor<
1548             typename std::remove_cv<typename std::underlying_type<T>::type>::type>::format();
1549     }
1550 };
1551 
1552 template <typename T>
1553 struct format_descriptor<T, detail::enable_if_t<detail::array_info<T>::is_array>> {
1554     static std::string format() {
1555         using namespace detail;
1556         static constexpr auto extents = const_name("(") + array_info<T>::extents + const_name(")");
1557         return extents.text + format_descriptor<remove_all_extents_t<T>>::format();
1558     }
1559 };
1560 
1561 PYBIND11_NAMESPACE_BEGIN(detail)
1562 template <typename T, int ExtraFlags>
1563 struct pyobject_caster<array_t<T, ExtraFlags>> {
1564     using type = array_t<T, ExtraFlags>;
1565 
1566     bool load(handle src, bool convert) {
1567         if (!convert && !type::check_(src)) {
1568             return false;
1569         }
1570         value = type::ensure(src);
1571         return static_cast<bool>(value);
1572     }
1573 
1574     static handle cast(const handle &src, return_value_policy /* policy */, handle /* parent */) {
1575         return src.inc_ref();
1576     }
1577     PYBIND11_TYPE_CASTER(type, handle_type_name<type>::name);
1578 };
1579 
1580 template <typename T>
1581 struct compare_buffer_info<T, detail::enable_if_t<detail::is_pod_struct<T>::value>> {
1582     static bool compare(const buffer_info &b) {
1583         return npy_api::get().PyArray_EquivTypes_(dtype::of<T>().ptr(), dtype(b).ptr());
1584     }
1585 };
1586 
1587 template <typename T>
1588 struct npy_format_descriptor<
1589     T,
1590     enable_if_t<satisfies_any_of<T, std::is_arithmetic, is_complex>::value>>
1591     : npy_format_descriptor_name<T> {
1592 private:
1593     // NB: the order here must match the one in common.h
1594     constexpr static const int values[15] = {npy_api::NPY_BOOL_,
1595                                              npy_api::NPY_BYTE_,
1596                                              npy_api::NPY_UBYTE_,
1597                                              npy_api::NPY_INT16_,
1598                                              npy_api::NPY_UINT16_,
1599                                              npy_api::NPY_INT32_,
1600                                              npy_api::NPY_UINT32_,
1601                                              npy_api::NPY_INT64_,
1602                                              npy_api::NPY_UINT64_,
1603                                              npy_api::NPY_FLOAT_,
1604                                              npy_api::NPY_DOUBLE_,
1605                                              npy_api::NPY_LONGDOUBLE_,
1606                                              npy_api::NPY_CFLOAT_,
1607                                              npy_api::NPY_CDOUBLE_,
1608                                              npy_api::NPY_CLONGDOUBLE_};
1609 
1610 public:
1611     static constexpr int value = values[detail::is_fmt_numeric<T>::index];
1612 
1613     static pybind11::dtype dtype() { return pybind11::dtype(/*typenum*/ value); }
1614 };
1615 
1616 template <typename T>
1617 struct npy_format_descriptor<
1618     T,
1619     enable_if_t<is_same_ignoring_cvref<T, PyObject *>::value
1620                 || ((std::is_same<T, handle>::value || std::is_same<T, object>::value)
1621                     && sizeof(T) == sizeof(PyObject *))>> {
1622     static constexpr auto name = const_name("numpy.object_");
1623 
1624     static constexpr int value = npy_api::NPY_OBJECT_;
1625 
1626     static pybind11::dtype dtype() { return pybind11::dtype(/*typenum*/ value); }
1627 };
1628 
1629 #define PYBIND11_DECL_CHAR_FMT                                                                    \
1630     static constexpr auto name = const_name("S") + const_name<N>();                               \
1631     static pybind11::dtype dtype() {                                                              \
1632         return pybind11::dtype(std::string("S") + std::to_string(N));                             \
1633     }
1634 template <size_t N>
1635 struct npy_format_descriptor<char[N]> {
1636     PYBIND11_DECL_CHAR_FMT
1637 };
1638 template <size_t N>
1639 struct npy_format_descriptor<std::array<char, N>> {
1640     PYBIND11_DECL_CHAR_FMT
1641 };
1642 #undef PYBIND11_DECL_CHAR_FMT
1643 
1644 template <typename T>
1645 struct npy_format_descriptor<T, enable_if_t<array_info<T>::is_array>> {
1646 private:
1647     using base_descr = npy_format_descriptor<typename array_info<T>::type>;
1648 
1649 public:
1650     static_assert(!array_info<T>::is_empty, "Zero-sized arrays are not supported");
1651 
1652     static constexpr auto name
1653         = const_name("(") + array_info<T>::extents + const_name(")") + base_descr::name;
1654     static pybind11::dtype dtype() {
1655         list shape;
1656         array_info<T>::append_extents(shape);
1657         return pybind11::dtype::from_args(
1658             pybind11::make_tuple(base_descr::dtype(), std::move(shape)));
1659     }
1660 };
1661 
1662 template <typename T>
1663 struct npy_format_descriptor<T, enable_if_t<std::is_enum<T>::value>> {
1664 private:
1665     using base_descr = npy_format_descriptor<typename std::underlying_type<T>::type>;
1666 
1667 public:
1668     static constexpr auto name = base_descr::name;
1669     static pybind11::dtype dtype() { return base_descr::dtype(); }
1670 };
1671 
1672 struct field_descriptor {
1673     const char *name;
1674     ssize_t offset;
1675     ssize_t size;
1676     std::string format;
1677     dtype descr;
1678 };
1679 
1680 PYBIND11_NOINLINE void register_structured_dtype(any_container<field_descriptor> fields,
1681                                                  const std::type_info &tinfo,
1682                                                  ssize_t itemsize,
1683                                                  bool (*direct_converter)(PyObject *, void *&)) {
1684 
1685     auto &numpy_internals = get_numpy_internals();
1686     if (numpy_internals.get_type_info(tinfo, false)) {
1687         pybind11_fail("NumPy: dtype is already registered");
1688     }
1689 
1690     // Use ordered fields because order matters as of NumPy 1.14:
1691     // https://docs.scipy.org/doc/numpy/release.html#multiple-field-indexing-assignment-of-structured-arrays
1692     std::vector<field_descriptor> ordered_fields(std::move(fields));
1693     std::sort(
1694         ordered_fields.begin(),
1695         ordered_fields.end(),
1696         [](const field_descriptor &a, const field_descriptor &b) { return a.offset < b.offset; });
1697 
1698     list names, formats, offsets;
1699     for (auto &field : ordered_fields) {
1700         if (!field.descr) {
1701             pybind11_fail(std::string("NumPy: unsupported field dtype: `") + field.name + "` @ "
1702                           + tinfo.name());
1703         }
1704         names.append(pybind11::str(field.name));
1705         formats.append(field.descr);
1706         offsets.append(pybind11::int_(field.offset));
1707     }
1708     auto *dtype_ptr
1709         = pybind11::dtype(std::move(names), std::move(formats), std::move(offsets), itemsize)
1710               .release()
1711               .ptr();
1712 
1713     // There is an existing bug in NumPy (as of v1.11): trailing bytes are
1714     // not encoded explicitly into the format string. This will supposedly
1715     // get fixed in v1.12; for further details, see these:
1716     // - https://github.com/numpy/numpy/issues/7797
1717     // - https://github.com/numpy/numpy/pull/7798
1718     // Because of this, we won't use numpy's logic to generate buffer format
1719     // strings and will just do it ourselves.
1720     ssize_t offset = 0;
1721     std::ostringstream oss;
1722     // mark the structure as unaligned with '^', because numpy and C++ don't
1723     // always agree about alignment (particularly for complex), and we're
1724     // explicitly listing all our padding. This depends on none of the fields
1725     // overriding the endianness. Putting the ^ in front of individual fields
1726     // isn't guaranteed to work due to https://github.com/numpy/numpy/issues/9049
1727     oss << "^T{";
1728     for (auto &field : ordered_fields) {
1729         if (field.offset > offset) {
1730             oss << (field.offset - offset) << 'x';
1731         }
1732         oss << field.format << ':' << field.name << ':';
1733         offset = field.offset + field.size;
1734     }
1735     if (itemsize > offset) {
1736         oss << (itemsize - offset) << 'x';
1737     }
1738     oss << '}';
1739     auto format_str = oss.str();
1740 
1741     // Smoke test: verify that NumPy properly parses our buffer format string
1742     auto &api = npy_api::get();
1743     auto arr = array(buffer_info(nullptr, itemsize, format_str, 1));
1744     if (!api.PyArray_EquivTypes_(dtype_ptr, arr.dtype().ptr())) {
1745         pybind11_fail("NumPy: invalid buffer descriptor!");
1746     }
1747 
1748     auto tindex = std::type_index(tinfo);
1749     numpy_internals.registered_dtypes[tindex] = {dtype_ptr, std::move(format_str)};
1750     with_internals([tindex, &direct_converter](internals &internals) {
1751         internals.direct_conversions[tindex].push_back(direct_converter);
1752     });
1753 }
1754 
1755 template <typename T, typename SFINAE>
1756 struct npy_format_descriptor {
1757     static_assert(is_pod_struct<T>::value,
1758                   "Attempt to use a non-POD or unimplemented POD type as a numpy dtype");
1759 
1760     static constexpr auto name = make_caster<T>::name;
1761 
1762     static pybind11::dtype dtype() { return reinterpret_borrow<pybind11::dtype>(dtype_ptr()); }
1763 
1764     static std::string format() {
1765         static auto format_str = get_numpy_internals().get_type_info<T>(true)->format_str;
1766         return format_str;
1767     }
1768 
1769     static void register_dtype(any_container<field_descriptor> fields) {
1770         register_structured_dtype(std::move(fields),
1771                                   typeid(typename std::remove_cv<T>::type),
1772                                   sizeof(T),
1773                                   &direct_converter);
1774     }
1775 
1776 private:
1777     static PyObject *dtype_ptr() {
1778         static PyObject *ptr = get_numpy_internals().get_type_info<T>(true)->dtype_ptr;
1779         return ptr;
1780     }
1781 
1782     static bool direct_converter(PyObject *obj, void *&value) {
1783         auto &api = npy_api::get();
1784         if (!PyObject_TypeCheck(obj, api.PyVoidArrType_Type_)) {
1785             return false;
1786         }
1787         if (auto descr = reinterpret_steal<object>(api.PyArray_DescrFromScalar_(obj))) {
1788             if (api.PyArray_EquivTypes_(dtype_ptr(), descr.ptr())) {
1789                 value = ((PyVoidScalarObject_Proxy *) obj)->obval;
1790                 return true;
1791             }
1792         }
1793         return false;
1794     }
1795 };
1796 
1797 #ifdef __CLION_IDE__ // replace heavy macro with dummy code for the IDE (doesn't affect code)
1798 #    define PYBIND11_NUMPY_DTYPE(Type, ...) ((void) 0)
1799 #    define PYBIND11_NUMPY_DTYPE_EX(Type, ...) ((void) 0)
1800 #else
1801 
1802 #    define PYBIND11_FIELD_DESCRIPTOR_EX(T, Field, Name)                                          \
1803         ::pybind11::detail::field_descriptor {                                                    \
1804             Name, offsetof(T, Field), sizeof(decltype(std::declval<T>().Field)),                  \
1805                 ::pybind11::format_descriptor<decltype(std::declval<T>().Field)>::format(),       \
1806                 ::pybind11::detail::npy_format_descriptor<                                        \
1807                     decltype(std::declval<T>().Field)>::dtype()                                   \
1808         }
1809 
1810 // Extract name, offset and format descriptor for a struct field
1811 #    define PYBIND11_FIELD_DESCRIPTOR(T, Field) PYBIND11_FIELD_DESCRIPTOR_EX(T, Field, #Field)
1812 
1813 // The main idea of this macro is borrowed from https://github.com/swansontec/map-macro
1814 // (C) William Swanson, Paul Fultz
1815 #    define PYBIND11_EVAL0(...) __VA_ARGS__
1816 #    define PYBIND11_EVAL1(...) PYBIND11_EVAL0(PYBIND11_EVAL0(PYBIND11_EVAL0(__VA_ARGS__)))
1817 #    define PYBIND11_EVAL2(...) PYBIND11_EVAL1(PYBIND11_EVAL1(PYBIND11_EVAL1(__VA_ARGS__)))
1818 #    define PYBIND11_EVAL3(...) PYBIND11_EVAL2(PYBIND11_EVAL2(PYBIND11_EVAL2(__VA_ARGS__)))
1819 #    define PYBIND11_EVAL4(...) PYBIND11_EVAL3(PYBIND11_EVAL3(PYBIND11_EVAL3(__VA_ARGS__)))
1820 #    define PYBIND11_EVAL(...) PYBIND11_EVAL4(PYBIND11_EVAL4(PYBIND11_EVAL4(__VA_ARGS__)))
1821 #    define PYBIND11_MAP_END(...)
1822 #    define PYBIND11_MAP_OUT
1823 #    define PYBIND11_MAP_COMMA ,
1824 #    define PYBIND11_MAP_GET_END() 0, PYBIND11_MAP_END
1825 #    define PYBIND11_MAP_NEXT0(test, next, ...) next PYBIND11_MAP_OUT
1826 #    define PYBIND11_MAP_NEXT1(test, next) PYBIND11_MAP_NEXT0(test, next, 0)
1827 #    define PYBIND11_MAP_NEXT(test, next) PYBIND11_MAP_NEXT1(PYBIND11_MAP_GET_END test, next)
1828 #    if defined(_MSC_VER)                                                                         \
1829         && !defined(__clang__) // MSVC is not as eager to expand macros, hence this workaround
1830 #        define PYBIND11_MAP_LIST_NEXT1(test, next)                                               \
1831             PYBIND11_EVAL0(PYBIND11_MAP_NEXT0(test, PYBIND11_MAP_COMMA next, 0))
1832 #    else
1833 #        define PYBIND11_MAP_LIST_NEXT1(test, next)                                               \
1834             PYBIND11_MAP_NEXT0(test, PYBIND11_MAP_COMMA next, 0)
1835 #    endif
1836 #    define PYBIND11_MAP_LIST_NEXT(test, next)                                                    \
1837         PYBIND11_MAP_LIST_NEXT1(PYBIND11_MAP_GET_END test, next)
1838 #    define PYBIND11_MAP_LIST0(f, t, x, peek, ...)                                                \
1839         f(t, x) PYBIND11_MAP_LIST_NEXT(peek, PYBIND11_MAP_LIST1)(f, t, peek, __VA_ARGS__)
1840 #    define PYBIND11_MAP_LIST1(f, t, x, peek, ...)                                                \
1841         f(t, x) PYBIND11_MAP_LIST_NEXT(peek, PYBIND11_MAP_LIST0)(f, t, peek, __VA_ARGS__)
1842 // PYBIND11_MAP_LIST(f, t, a1, a2, ...) expands to f(t, a1), f(t, a2), ...
1843 #    define PYBIND11_MAP_LIST(f, t, ...)                                                          \
1844         PYBIND11_EVAL(PYBIND11_MAP_LIST1(f, t, __VA_ARGS__, (), 0))
1845 
1846 #    define PYBIND11_NUMPY_DTYPE(Type, ...)                                                       \
1847         ::pybind11::detail::npy_format_descriptor<Type>::register_dtype(                          \
1848             ::std::vector<::pybind11::detail::field_descriptor>{                                  \
1849                 PYBIND11_MAP_LIST(PYBIND11_FIELD_DESCRIPTOR, Type, __VA_ARGS__)})
1850 
1851 #    if defined(_MSC_VER) && !defined(__clang__)
1852 #        define PYBIND11_MAP2_LIST_NEXT1(test, next)                                              \
1853             PYBIND11_EVAL0(PYBIND11_MAP_NEXT0(test, PYBIND11_MAP_COMMA next, 0))
1854 #    else
1855 #        define PYBIND11_MAP2_LIST_NEXT1(test, next)                                              \
1856             PYBIND11_MAP_NEXT0(test, PYBIND11_MAP_COMMA next, 0)
1857 #    endif
1858 #    define PYBIND11_MAP2_LIST_NEXT(test, next)                                                   \
1859         PYBIND11_MAP2_LIST_NEXT1(PYBIND11_MAP_GET_END test, next)
1860 #    define PYBIND11_MAP2_LIST0(f, t, x1, x2, peek, ...)                                          \
1861         f(t, x1, x2) PYBIND11_MAP2_LIST_NEXT(peek, PYBIND11_MAP2_LIST1)(f, t, peek, __VA_ARGS__)
1862 #    define PYBIND11_MAP2_LIST1(f, t, x1, x2, peek, ...)                                          \
1863         f(t, x1, x2) PYBIND11_MAP2_LIST_NEXT(peek, PYBIND11_MAP2_LIST0)(f, t, peek, __VA_ARGS__)
1864 // PYBIND11_MAP2_LIST(f, t, a1, a2, ...) expands to f(t, a1, a2), f(t, a3, a4), ...
1865 #    define PYBIND11_MAP2_LIST(f, t, ...)                                                         \
1866         PYBIND11_EVAL(PYBIND11_MAP2_LIST1(f, t, __VA_ARGS__, (), 0))
1867 
1868 #    define PYBIND11_NUMPY_DTYPE_EX(Type, ...)                                                    \
1869         ::pybind11::detail::npy_format_descriptor<Type>::register_dtype(                          \
1870             ::std::vector<::pybind11::detail::field_descriptor>{                                  \
1871                 PYBIND11_MAP2_LIST(PYBIND11_FIELD_DESCRIPTOR_EX, Type, __VA_ARGS__)})
1872 
1873 #endif // __CLION_IDE__
1874 
1875 class common_iterator {
1876 public:
1877     using container_type = std::vector<ssize_t>;
1878     using value_type = container_type::value_type;
1879     using size_type = container_type::size_type;
1880 
1881     common_iterator() = default;
1882 
1883     common_iterator(void *ptr, const container_type &strides, const container_type &shape)
1884         : p_ptr(reinterpret_cast<char *>(ptr)), m_strides(strides.size()) {
1885         m_strides.back() = static_cast<value_type>(strides.back());
1886         for (size_type i = m_strides.size() - 1; i != 0; --i) {
1887             size_type j = i - 1;
1888             auto s = static_cast<value_type>(shape[i]);
1889             m_strides[j] = strides[j] + m_strides[i] - strides[i] * s;
1890         }
1891     }
1892 
1893     void increment(size_type dim) { p_ptr += m_strides[dim]; }
1894 
1895     void *data() const { return p_ptr; }
1896 
1897 private:
1898     char *p_ptr{nullptr};
1899     container_type m_strides;
1900 };
1901 
1902 template <size_t N>
1903 class multi_array_iterator {
1904 public:
1905     using container_type = std::vector<ssize_t>;
1906 
1907     multi_array_iterator(const std::array<buffer_info, N> &buffers, const container_type &shape)
1908         : m_shape(shape.size()), m_index(shape.size(), 0), m_common_iterator() {
1909 
1910         // Manual copy to avoid conversion warning if using std::copy
1911         for (size_t i = 0; i < shape.size(); ++i) {
1912             m_shape[i] = shape[i];
1913         }
1914 
1915         container_type strides(shape.size());
1916         for (size_t i = 0; i < N; ++i) {
1917             init_common_iterator(buffers[i], shape, m_common_iterator[i], strides);
1918         }
1919     }
1920 
1921     multi_array_iterator &operator++() {
1922         for (size_t j = m_index.size(); j != 0; --j) {
1923             size_t i = j - 1;
1924             if (++m_index[i] != m_shape[i]) {
1925                 increment_common_iterator(i);
1926                 break;
1927             }
1928             m_index[i] = 0;
1929         }
1930         return *this;
1931     }
1932 
1933     template <size_t K, class T = void>
1934     T *data() const {
1935         return reinterpret_cast<T *>(m_common_iterator[K].data());
1936     }
1937 
1938 private:
1939     using common_iter = common_iterator;
1940 
1941     void init_common_iterator(const buffer_info &buffer,
1942                               const container_type &shape,
1943                               common_iter &iterator,
1944                               container_type &strides) {
1945         auto buffer_shape_iter = buffer.shape.rbegin();
1946         auto buffer_strides_iter = buffer.strides.rbegin();
1947         auto shape_iter = shape.rbegin();
1948         auto strides_iter = strides.rbegin();
1949 
1950         while (buffer_shape_iter != buffer.shape.rend()) {
1951             if (*shape_iter == *buffer_shape_iter) {
1952                 *strides_iter = *buffer_strides_iter;
1953             } else {
1954                 *strides_iter = 0;
1955             }
1956 
1957             ++buffer_shape_iter;
1958             ++buffer_strides_iter;
1959             ++shape_iter;
1960             ++strides_iter;
1961         }
1962 
1963         std::fill(strides_iter, strides.rend(), 0);
1964         iterator = common_iter(buffer.ptr, strides, shape);
1965     }
1966 
1967     void increment_common_iterator(size_t dim) {
1968         for (auto &iter : m_common_iterator) {
1969             iter.increment(dim);
1970         }
1971     }
1972 
1973     container_type m_shape;
1974     container_type m_index;
1975     std::array<common_iter, N> m_common_iterator;
1976 };
1977 
1978 enum class broadcast_trivial { non_trivial, c_trivial, f_trivial };
1979 
1980 // Populates the shape and number of dimensions for the set of buffers.  Returns a
1981 // broadcast_trivial enum value indicating whether the broadcast is "trivial"--that is, has each
1982 // buffer being either a singleton or a full-size, C-contiguous (`c_trivial`) or Fortran-contiguous
1983 // (`f_trivial`) storage buffer; returns `non_trivial` otherwise.
1984 template <size_t N>
1985 broadcast_trivial
1986 broadcast(const std::array<buffer_info, N> &buffers, ssize_t &ndim, std::vector<ssize_t> &shape) {
1987     ndim = std::accumulate(
1988         buffers.begin(), buffers.end(), ssize_t(0), [](ssize_t res, const buffer_info &buf) {
1989             return std::max(res, buf.ndim);
1990         });
1991 
1992     shape.clear();
1993     shape.resize((size_t) ndim, 1);
1994 
1995     // Figure out the output size, and make sure all input arrays conform (i.e. are either size 1
1996     // or the full size).
1997     for (size_t i = 0; i < N; ++i) {
1998         auto res_iter = shape.rbegin();
1999         auto end = buffers[i].shape.rend();
2000         for (auto shape_iter = buffers[i].shape.rbegin(); shape_iter != end;
2001              ++shape_iter, ++res_iter) {
2002             const auto &dim_size_in = *shape_iter;
2003             auto &dim_size_out = *res_iter;
2004 
2005             // Each input dimension can either be 1 or `n`, but `n` values must match across
2006             // buffers
2007             if (dim_size_out == 1) {
2008                 dim_size_out = dim_size_in;
2009             } else if (dim_size_in != 1 && dim_size_in != dim_size_out) {
2010                 pybind11_fail("pybind11::vectorize: incompatible size/dimension of inputs!");
2011             }
2012         }
2013     }
2014 
2015     bool trivial_broadcast_c = true;
2016     bool trivial_broadcast_f = true;
2017     for (size_t i = 0; i < N && (trivial_broadcast_c || trivial_broadcast_f); ++i) {
2018         if (buffers[i].size == 1) {
2019             continue;
2020         }
2021 
2022         // Require the same number of dimensions:
2023         if (buffers[i].ndim != ndim) {
2024             return broadcast_trivial::non_trivial;
2025         }
2026 
2027         // Require all dimensions be full-size:
2028         if (!std::equal(buffers[i].shape.cbegin(), buffers[i].shape.cend(), shape.cbegin())) {
2029             return broadcast_trivial::non_trivial;
2030         }
2031 
2032         // Check for C contiguity (but only if previous inputs were also C contiguous)
2033         if (trivial_broadcast_c) {
2034             ssize_t expect_stride = buffers[i].itemsize;
2035             auto end = buffers[i].shape.crend();
2036             for (auto shape_iter = buffers[i].shape.crbegin(),
2037                       stride_iter = buffers[i].strides.crbegin();
2038                  trivial_broadcast_c && shape_iter != end;
2039                  ++shape_iter, ++stride_iter) {
2040                 if (expect_stride == *stride_iter) {
2041                     expect_stride *= *shape_iter;
2042                 } else {
2043                     trivial_broadcast_c = false;
2044                 }
2045             }
2046         }
2047 
2048         // Check for Fortran contiguity (if previous inputs were also F contiguous)
2049         if (trivial_broadcast_f) {
2050             ssize_t expect_stride = buffers[i].itemsize;
2051             auto end = buffers[i].shape.cend();
2052             for (auto shape_iter = buffers[i].shape.cbegin(),
2053                       stride_iter = buffers[i].strides.cbegin();
2054                  trivial_broadcast_f && shape_iter != end;
2055                  ++shape_iter, ++stride_iter) {
2056                 if (expect_stride == *stride_iter) {
2057                     expect_stride *= *shape_iter;
2058                 } else {
2059                     trivial_broadcast_f = false;
2060                 }
2061             }
2062         }
2063     }
2064 
2065     return trivial_broadcast_c   ? broadcast_trivial::c_trivial
2066            : trivial_broadcast_f ? broadcast_trivial::f_trivial
2067                                  : broadcast_trivial::non_trivial;
2068 }
2069 
2070 template <typename T>
2071 struct vectorize_arg {
2072     static_assert(!std::is_rvalue_reference<T>::value,
2073                   "Functions with rvalue reference arguments cannot be vectorized");
2074     // The wrapped function gets called with this type:
2075     using call_type = remove_reference_t<T>;
2076     // Is this a vectorized argument?
2077     static constexpr bool vectorize
2078         = satisfies_any_of<call_type, std::is_arithmetic, is_complex, is_pod>::value
2079           && satisfies_none_of<call_type,
2080                                std::is_pointer,
2081                                std::is_array,
2082                                is_std_array,
2083                                std::is_enum>::value
2084           && (!std::is_reference<T>::value
2085               || (std::is_lvalue_reference<T>::value && std::is_const<call_type>::value));
2086     // Accept this type: an array for vectorized types, otherwise the type as-is:
2087     using type = conditional_t<vectorize, array_t<remove_cv_t<call_type>, array::forcecast>, T>;
2088 };
2089 
2090 // py::vectorize when a return type is present
2091 template <typename Func, typename Return, typename... Args>
2092 struct vectorize_returned_array {
2093     using Type = array_t<Return>;
2094 
2095     static Type create(broadcast_trivial trivial, const std::vector<ssize_t> &shape) {
2096         if (trivial == broadcast_trivial::f_trivial) {
2097             return array_t<Return, array::f_style>(shape);
2098         }
2099         return array_t<Return>(shape);
2100     }
2101 
2102     static Return *mutable_data(Type &array) { return array.mutable_data(); }
2103 
2104     static Return call(Func &f, Args &...args) { return f(args...); }
2105 
2106     static void call(Return *out, size_t i, Func &f, Args &...args) { out[i] = f(args...); }
2107 };
2108 
2109 // py::vectorize when a return type is not present
2110 template <typename Func, typename... Args>
2111 struct vectorize_returned_array<Func, void, Args...> {
2112     using Type = none;
2113 
2114     static Type create(broadcast_trivial, const std::vector<ssize_t> &) { return none(); }
2115 
2116     static void *mutable_data(Type &) { return nullptr; }
2117 
2118     static detail::void_type call(Func &f, Args &...args) {
2119         f(args...);
2120         return {};
2121     }
2122 
2123     static void call(void *, size_t, Func &f, Args &...args) { f(args...); }
2124 };
2125 
2126 template <typename Func, typename Return, typename... Args>
2127 struct vectorize_helper {
2128 
2129 // NVCC for some reason breaks if NVectorized is private
2130 #ifdef __CUDACC__
2131 public:
2132 #else
2133 private:
2134 #endif
2135 
2136     static constexpr size_t N = sizeof...(Args);
2137     static constexpr size_t NVectorized = constexpr_sum(vectorize_arg<Args>::vectorize...);
2138     static_assert(
2139         NVectorized >= 1,
2140         "pybind11::vectorize(...) requires a function with at least one vectorizable argument");
2141 
2142 public:
2143     template <typename T,
2144               // SFINAE to prevent shadowing the copy constructor.
2145               typename = detail::enable_if_t<
2146                   !std::is_same<vectorize_helper, typename std::decay<T>::type>::value>>
2147     explicit vectorize_helper(T &&f) : f(std::forward<T>(f)) {}
2148 
2149     object operator()(typename vectorize_arg<Args>::type... args) {
2150         return run(args...,
2151                    make_index_sequence<N>(),
2152                    select_indices<vectorize_arg<Args>::vectorize...>(),
2153                    make_index_sequence<NVectorized>());
2154     }
2155 
2156 private:
2157     remove_reference_t<Func> f;
2158 
2159     // Internal compiler error in MSVC 19.16.27025.1 (Visual Studio 2017 15.9.4), when compiling
2160     // with "/permissive-" flag when arg_call_types is manually inlined.
2161     using arg_call_types = std::tuple<typename vectorize_arg<Args>::call_type...>;
2162     template <size_t Index>
2163     using param_n_t = typename std::tuple_element<Index, arg_call_types>::type;
2164 
2165     using returned_array = vectorize_returned_array<Func, Return, Args...>;
2166 
2167     // Runs a vectorized function given arguments tuple and three index sequences:
2168     //     - Index is the full set of 0 ... (N-1) argument indices;
2169     //     - VIndex is the subset of argument indices with vectorized parameters, letting us access
2170     //       vectorized arguments (anything not in this sequence is passed through)
2171     //     - BIndex is a incremental sequence (beginning at 0) of the same size as VIndex, so that
2172     //       we can store vectorized buffer_infos in an array (argument VIndex has its buffer at
2173     //       index BIndex in the array).
2174     template <size_t... Index, size_t... VIndex, size_t... BIndex>
2175     object run(typename vectorize_arg<Args>::type &...args,
2176                index_sequence<Index...> i_seq,
2177                index_sequence<VIndex...> vi_seq,
2178                index_sequence<BIndex...> bi_seq) {
2179 
2180         // Pointers to values the function was called with; the vectorized ones set here will start
2181         // out as array_t<T> pointers, but they will be changed them to T pointers before we make
2182         // call the wrapped function.  Non-vectorized pointers are left as-is.
2183         std::array<void *, N> params{{reinterpret_cast<void *>(&args)...}};
2184 
2185         // The array of `buffer_info`s of vectorized arguments:
2186         std::array<buffer_info, NVectorized> buffers{
2187             {reinterpret_cast<array *>(params[VIndex])->request()...}};
2188 
2189         /* Determine dimensions parameters of output array */
2190         ssize_t nd = 0;
2191         std::vector<ssize_t> shape(0);
2192         auto trivial = broadcast(buffers, nd, shape);
2193         auto ndim = (size_t) nd;
2194 
2195         size_t size
2196             = std::accumulate(shape.begin(), shape.end(), (size_t) 1, std::multiplies<size_t>());
2197 
2198         // If all arguments are 0-dimension arrays (i.e. single values) return a plain value (i.e.
2199         // not wrapped in an array).
2200         if (size == 1 && ndim == 0) {
2201             PYBIND11_EXPAND_SIDE_EFFECTS(params[VIndex] = buffers[BIndex].ptr);
2202             return cast(
2203                 returned_array::call(f, *reinterpret_cast<param_n_t<Index> *>(params[Index])...));
2204         }
2205 
2206         auto result = returned_array::create(trivial, shape);
2207 
2208         PYBIND11_WARNING_PUSH
2209 #ifdef PYBIND11_DETECTED_CLANG_WITH_MISLEADING_CALL_STD_MOVE_EXPLICITLY_WARNING
2210         PYBIND11_WARNING_DISABLE_CLANG("-Wreturn-std-move")
2211 #endif
2212 
2213         if (size == 0) {
2214             return result;
2215         }
2216 
2217         /* Call the function */
2218         auto *mutable_data = returned_array::mutable_data(result);
2219         if (trivial == broadcast_trivial::non_trivial) {
2220             apply_broadcast(buffers, params, mutable_data, size, shape, i_seq, vi_seq, bi_seq);
2221         } else {
2222             apply_trivial(buffers, params, mutable_data, size, i_seq, vi_seq, bi_seq);
2223         }
2224 
2225         return result;
2226         PYBIND11_WARNING_POP
2227     }
2228 
2229     template <size_t... Index, size_t... VIndex, size_t... BIndex>
2230     void apply_trivial(std::array<buffer_info, NVectorized> &buffers,
2231                        std::array<void *, N> &params,
2232                        Return *out,
2233                        size_t size,
2234                        index_sequence<Index...>,
2235                        index_sequence<VIndex...>,
2236                        index_sequence<BIndex...>) {
2237 
2238         // Initialize an array of mutable byte references and sizes with references set to the
2239         // appropriate pointer in `params`; as we iterate, we'll increment each pointer by its size
2240         // (except for singletons, which get an increment of 0).
2241         std::array<std::pair<unsigned char *&, const size_t>, NVectorized> vecparams{
2242             {std::pair<unsigned char *&, const size_t>(
2243                 reinterpret_cast<unsigned char *&>(params[VIndex] = buffers[BIndex].ptr),
2244                 buffers[BIndex].size == 1 ? 0 : sizeof(param_n_t<VIndex>))...}};
2245 
2246         for (size_t i = 0; i < size; ++i) {
2247             returned_array::call(
2248                 out, i, f, *reinterpret_cast<param_n_t<Index> *>(params[Index])...);
2249             for (auto &x : vecparams) {
2250                 x.first += x.second;
2251             }
2252         }
2253     }
2254 
2255     template <size_t... Index, size_t... VIndex, size_t... BIndex>
2256     void apply_broadcast(std::array<buffer_info, NVectorized> &buffers,
2257                          std::array<void *, N> &params,
2258                          Return *out,
2259                          size_t size,
2260                          const std::vector<ssize_t> &output_shape,
2261                          index_sequence<Index...>,
2262                          index_sequence<VIndex...>,
2263                          index_sequence<BIndex...>) {
2264 
2265         multi_array_iterator<NVectorized> input_iter(buffers, output_shape);
2266 
2267         for (size_t i = 0; i < size; ++i, ++input_iter) {
2268             PYBIND11_EXPAND_SIDE_EFFECTS((params[VIndex] = input_iter.template data<BIndex>()));
2269             returned_array::call(
2270                 out, i, f, *reinterpret_cast<param_n_t<Index> *>(std::get<Index>(params))...);
2271         }
2272     }
2273 };
2274 
2275 template <typename Func, typename Return, typename... Args>
2276 vectorize_helper<Func, Return, Args...> vectorize_extractor(const Func &f, Return (*)(Args...)) {
2277     return detail::vectorize_helper<Func, Return, Args...>(f);
2278 }
2279 
2280 template <typename T, int Flags>
2281 struct handle_type_name<array_t<T, Flags>> {
2282     static constexpr auto name
2283         = io_name("typing.Annotated[numpy.typing.ArrayLike, ", "numpy.typing.NDArray[")
2284           + npy_format_descriptor<T>::name + const_name("]");
2285 };
2286 
2287 PYBIND11_NAMESPACE_END(detail)
2288 
2289 // Vanilla pointer vectorizer:
2290 template <typename Return, typename... Args>
2291 detail::vectorize_helper<Return (*)(Args...), Return, Args...> vectorize(Return (*f)(Args...)) {
2292     return detail::vectorize_helper<Return (*)(Args...), Return, Args...>(f);
2293 }
2294 
2295 // lambda vectorizer:
2296 template <typename Func, detail::enable_if_t<detail::is_lambda<Func>::value, int> = 0>
2297 auto vectorize(Func &&f)
2298     -> decltype(detail::vectorize_extractor(std::forward<Func>(f),
2299                                             (detail::function_signature_t<Func> *) nullptr)) {
2300     return detail::vectorize_extractor(std::forward<Func>(f),
2301                                        (detail::function_signature_t<Func> *) nullptr);
2302 }
2303 
2304 // Vectorize a class method (non-const):
2305 template <typename Return,
2306           typename Class,
2307           typename... Args,
2308           typename Helper = detail::vectorize_helper<
2309               decltype(std::mem_fn(std::declval<Return (Class::*)(Args...)>())),
2310               Return,
2311               Class *,
2312               Args...>>
2313 Helper vectorize(Return (Class::*f)(Args...)) {
2314     return Helper(std::mem_fn(f));
2315 }
2316 
2317 // Vectorize a class method (const):
2318 template <typename Return,
2319           typename Class,
2320           typename... Args,
2321           typename Helper = detail::vectorize_helper<
2322               decltype(std::mem_fn(std::declval<Return (Class::*)(Args...) const>())),
2323               Return,
2324               const Class *,
2325               Args...>>
2326 Helper vectorize(Return (Class::*f)(Args...) const) {
2327     return Helper(std::mem_fn(f));
2328 }
2329 
2330 PYBIND11_NAMESPACE_END(PYBIND11_NAMESPACE)