about summary refs log tree commit diff
path: root/.venv/lib/python3.12/site-packages/numpy/f2py
diff options
context:
space:
mode:
authorS. Solomon Darnell2025-03-28 21:52:21 -0500
committerS. Solomon Darnell2025-03-28 21:52:21 -0500
commit4a52a71956a8d46fcb7294ac71734504bb09bcc2 (patch)
treeee3dc5af3b6313e921cd920906356f5d4febc4ed /.venv/lib/python3.12/site-packages/numpy/f2py
parentcc961e04ba734dd72309fb548a2f97d67d578813 (diff)
downloadgn-ai-master.tar.gz
two version of R2R are here HEAD master
Diffstat (limited to '.venv/lib/python3.12/site-packages/numpy/f2py')
-rw-r--r--.venv/lib/python3.12/site-packages/numpy/f2py/__init__.py194
-rw-r--r--.venv/lib/python3.12/site-packages/numpy/f2py/__init__.pyi42
-rw-r--r--.venv/lib/python3.12/site-packages/numpy/f2py/__main__.py5
-rw-r--r--.venv/lib/python3.12/site-packages/numpy/f2py/__version__.py1
-rw-r--r--.venv/lib/python3.12/site-packages/numpy/f2py/_backends/__init__.py9
-rw-r--r--.venv/lib/python3.12/site-packages/numpy/f2py/_backends/_backend.py46
-rw-r--r--.venv/lib/python3.12/site-packages/numpy/f2py/_backends/_distutils.py75
-rw-r--r--.venv/lib/python3.12/site-packages/numpy/f2py/_backends/_meson.py205
-rw-r--r--.venv/lib/python3.12/site-packages/numpy/f2py/_backends/meson.build.template54
-rw-r--r--.venv/lib/python3.12/site-packages/numpy/f2py/_isocbind.py62
-rw-r--r--.venv/lib/python3.12/site-packages/numpy/f2py/_src_pyf.py239
-rw-r--r--.venv/lib/python3.12/site-packages/numpy/f2py/auxfuncs.py988
-rw-r--r--.venv/lib/python3.12/site-packages/numpy/f2py/capi_maps.py819
-rw-r--r--.venv/lib/python3.12/site-packages/numpy/f2py/cb_rules.py644
-rw-r--r--.venv/lib/python3.12/site-packages/numpy/f2py/cfuncs.py1536
-rw-r--r--.venv/lib/python3.12/site-packages/numpy/f2py/common_rules.py146
-rwxr-xr-x.venv/lib/python3.12/site-packages/numpy/f2py/crackfortran.py3767
-rw-r--r--.venv/lib/python3.12/site-packages/numpy/f2py/diagnose.py154
-rwxr-xr-x.venv/lib/python3.12/site-packages/numpy/f2py/f2py2e.py768
-rw-r--r--.venv/lib/python3.12/site-packages/numpy/f2py/f90mod_rules.py264
-rw-r--r--.venv/lib/python3.12/site-packages/numpy/f2py/func2subr.py323
-rwxr-xr-x.venv/lib/python3.12/site-packages/numpy/f2py/rules.py1568
-rw-r--r--.venv/lib/python3.12/site-packages/numpy/f2py/setup.cfg3
-rw-r--r--.venv/lib/python3.12/site-packages/numpy/f2py/setup.py74
-rw-r--r--.venv/lib/python3.12/site-packages/numpy/f2py/src/fortranobject.c1423
-rw-r--r--.venv/lib/python3.12/site-packages/numpy/f2py/src/fortranobject.h173
-rw-r--r--.venv/lib/python3.12/site-packages/numpy/f2py/symbolic.py1517
-rw-r--r--.venv/lib/python3.12/site-packages/numpy/f2py/tests/__init__.py0
-rw-r--r--.venv/lib/python3.12/site-packages/numpy/f2py/tests/src/abstract_interface/foo.f9034
-rw-r--r--.venv/lib/python3.12/site-packages/numpy/f2py/tests/src/abstract_interface/gh18403_mod.f906
-rw-r--r--.venv/lib/python3.12/site-packages/numpy/f2py/tests/src/array_from_pyobj/wrapmodule.c230
-rw-r--r--.venv/lib/python3.12/site-packages/numpy/f2py/tests/src/assumed_shape/.f2py_f2cmap1
-rw-r--r--.venv/lib/python3.12/site-packages/numpy/f2py/tests/src/assumed_shape/foo_free.f9034
-rw-r--r--.venv/lib/python3.12/site-packages/numpy/f2py/tests/src/assumed_shape/foo_mod.f9041
-rw-r--r--.venv/lib/python3.12/site-packages/numpy/f2py/tests/src/assumed_shape/foo_use.f9019
-rw-r--r--.venv/lib/python3.12/site-packages/numpy/f2py/tests/src/assumed_shape/precision.f904
-rw-r--r--.venv/lib/python3.12/site-packages/numpy/f2py/tests/src/block_docstring/foo.f6
-rw-r--r--.venv/lib/python3.12/site-packages/numpy/f2py/tests/src/callback/foo.f62
-rw-r--r--.venv/lib/python3.12/site-packages/numpy/f2py/tests/src/callback/gh17797.f907
-rw-r--r--.venv/lib/python3.12/site-packages/numpy/f2py/tests/src/callback/gh18335.f9017
-rw-r--r--.venv/lib/python3.12/site-packages/numpy/f2py/tests/src/callback/gh25211.f10
-rw-r--r--.venv/lib/python3.12/site-packages/numpy/f2py/tests/src/callback/gh25211.pyf18
-rw-r--r--.venv/lib/python3.12/site-packages/numpy/f2py/tests/src/cli/gh_22819.pyf6
-rw-r--r--.venv/lib/python3.12/site-packages/numpy/f2py/tests/src/cli/hi77.f3
-rw-r--r--.venv/lib/python3.12/site-packages/numpy/f2py/tests/src/cli/hiworld.f903
-rw-r--r--.venv/lib/python3.12/site-packages/numpy/f2py/tests/src/common/block.f11
-rw-r--r--.venv/lib/python3.12/site-packages/numpy/f2py/tests/src/common/gh19161.f9010
-rw-r--r--.venv/lib/python3.12/site-packages/numpy/f2py/tests/src/crackfortran/accesstype.f9013
-rw-r--r--.venv/lib/python3.12/site-packages/numpy/f2py/tests/src/crackfortran/data_common.f8
-rw-r--r--.venv/lib/python3.12/site-packages/numpy/f2py/tests/src/crackfortran/data_multiplier.f5
-rw-r--r--.venv/lib/python3.12/site-packages/numpy/f2py/tests/src/crackfortran/data_stmts.f9020
-rw-r--r--.venv/lib/python3.12/site-packages/numpy/f2py/tests/src/crackfortran/data_with_comments.f8
-rw-r--r--.venv/lib/python3.12/site-packages/numpy/f2py/tests/src/crackfortran/foo_deps.f906
-rw-r--r--.venv/lib/python3.12/site-packages/numpy/f2py/tests/src/crackfortran/gh15035.f16
-rw-r--r--.venv/lib/python3.12/site-packages/numpy/f2py/tests/src/crackfortran/gh17859.f12
-rw-r--r--.venv/lib/python3.12/site-packages/numpy/f2py/tests/src/crackfortran/gh22648.pyf7
-rw-r--r--.venv/lib/python3.12/site-packages/numpy/f2py/tests/src/crackfortran/gh23533.f5
-rw-r--r--.venv/lib/python3.12/site-packages/numpy/f2py/tests/src/crackfortran/gh23598.f904
-rw-r--r--.venv/lib/python3.12/site-packages/numpy/f2py/tests/src/crackfortran/gh23598Warn.f9011
-rw-r--r--.venv/lib/python3.12/site-packages/numpy/f2py/tests/src/crackfortran/gh23879.f9020
-rw-r--r--.venv/lib/python3.12/site-packages/numpy/f2py/tests/src/crackfortran/gh2848.f9013
-rw-r--r--.venv/lib/python3.12/site-packages/numpy/f2py/tests/src/crackfortran/operators.f9049
-rw-r--r--.venv/lib/python3.12/site-packages/numpy/f2py/tests/src/crackfortran/privatemod.f9011
-rw-r--r--.venv/lib/python3.12/site-packages/numpy/f2py/tests/src/crackfortran/publicmod.f9010
-rw-r--r--.venv/lib/python3.12/site-packages/numpy/f2py/tests/src/crackfortran/pubprivmod.f9010
-rw-r--r--.venv/lib/python3.12/site-packages/numpy/f2py/tests/src/crackfortran/unicode_comment.f904
-rw-r--r--.venv/lib/python3.12/site-packages/numpy/f2py/tests/src/f2cmap/.f2py_f2cmap1
-rw-r--r--.venv/lib/python3.12/site-packages/numpy/f2py/tests/src/f2cmap/isoFortranEnvMap.f909
-rw-r--r--.venv/lib/python3.12/site-packages/numpy/f2py/tests/src/isocintrin/isoCtests.f9034
-rw-r--r--.venv/lib/python3.12/site-packages/numpy/f2py/tests/src/kind/foo.f9020
-rw-r--r--.venv/lib/python3.12/site-packages/numpy/f2py/tests/src/mixed/foo.f5
-rw-r--r--.venv/lib/python3.12/site-packages/numpy/f2py/tests/src/mixed/foo_fixed.f908
-rw-r--r--.venv/lib/python3.12/site-packages/numpy/f2py/tests/src/mixed/foo_free.f908
-rw-r--r--.venv/lib/python3.12/site-packages/numpy/f2py/tests/src/module_data/mod.modbin0 -> 412 bytes
-rw-r--r--.venv/lib/python3.12/site-packages/numpy/f2py/tests/src/module_data/module_data_docstring.f9012
-rw-r--r--.venv/lib/python3.12/site-packages/numpy/f2py/tests/src/negative_bounds/issue_20853.f907
-rw-r--r--.venv/lib/python3.12/site-packages/numpy/f2py/tests/src/parameter/constant_both.f9057
-rw-r--r--.venv/lib/python3.12/site-packages/numpy/f2py/tests/src/parameter/constant_compound.f9015
-rw-r--r--.venv/lib/python3.12/site-packages/numpy/f2py/tests/src/parameter/constant_integer.f9022
-rw-r--r--.venv/lib/python3.12/site-packages/numpy/f2py/tests/src/parameter/constant_non_compound.f9023
-rw-r--r--.venv/lib/python3.12/site-packages/numpy/f2py/tests/src/parameter/constant_real.f9023
-rw-r--r--.venv/lib/python3.12/site-packages/numpy/f2py/tests/src/quoted_character/foo.f14
-rw-r--r--.venv/lib/python3.12/site-packages/numpy/f2py/tests/src/regression/gh25337/data.f908
-rw-r--r--.venv/lib/python3.12/site-packages/numpy/f2py/tests/src/regression/gh25337/use_data.f906
-rw-r--r--.venv/lib/python3.12/site-packages/numpy/f2py/tests/src/regression/inout.f909
-rw-r--r--.venv/lib/python3.12/site-packages/numpy/f2py/tests/src/return_character/foo77.f45
-rw-r--r--.venv/lib/python3.12/site-packages/numpy/f2py/tests/src/return_character/foo90.f9048
-rw-r--r--.venv/lib/python3.12/site-packages/numpy/f2py/tests/src/return_complex/foo77.f45
-rw-r--r--.venv/lib/python3.12/site-packages/numpy/f2py/tests/src/return_complex/foo90.f9048
-rw-r--r--.venv/lib/python3.12/site-packages/numpy/f2py/tests/src/return_integer/foo77.f56
-rw-r--r--.venv/lib/python3.12/site-packages/numpy/f2py/tests/src/return_integer/foo90.f9059
-rw-r--r--.venv/lib/python3.12/site-packages/numpy/f2py/tests/src/return_logical/foo77.f56
-rw-r--r--.venv/lib/python3.12/site-packages/numpy/f2py/tests/src/return_logical/foo90.f9059
-rw-r--r--.venv/lib/python3.12/site-packages/numpy/f2py/tests/src/return_real/foo77.f45
-rw-r--r--.venv/lib/python3.12/site-packages/numpy/f2py/tests/src/return_real/foo90.f9048
-rw-r--r--.venv/lib/python3.12/site-packages/numpy/f2py/tests/src/size/foo.f9044
-rw-r--r--.venv/lib/python3.12/site-packages/numpy/f2py/tests/src/string/char.f9029
-rw-r--r--.venv/lib/python3.12/site-packages/numpy/f2py/tests/src/string/fixed_string.f9034
-rw-r--r--.venv/lib/python3.12/site-packages/numpy/f2py/tests/src/string/gh24008.f8
-rw-r--r--.venv/lib/python3.12/site-packages/numpy/f2py/tests/src/string/gh24662.f907
-rw-r--r--.venv/lib/python3.12/site-packages/numpy/f2py/tests/src/string/gh25286.f9014
-rw-r--r--.venv/lib/python3.12/site-packages/numpy/f2py/tests/src/string/gh25286.pyf12
-rw-r--r--.venv/lib/python3.12/site-packages/numpy/f2py/tests/src/string/gh25286_bc.pyf12
-rw-r--r--.venv/lib/python3.12/site-packages/numpy/f2py/tests/src/string/scalar_string.f909
-rw-r--r--.venv/lib/python3.12/site-packages/numpy/f2py/tests/src/string/string.f12
-rw-r--r--.venv/lib/python3.12/site-packages/numpy/f2py/tests/src/value_attrspec/gh21665.f909
-rw-r--r--.venv/lib/python3.12/site-packages/numpy/f2py/tests/test_abstract_interface.py25
-rw-r--r--.venv/lib/python3.12/site-packages/numpy/f2py/tests/test_array_from_pyobj.py686
-rw-r--r--.venv/lib/python3.12/site-packages/numpy/f2py/tests/test_assumed_shape.py49
-rw-r--r--.venv/lib/python3.12/site-packages/numpy/f2py/tests/test_block_docstring.py17
-rw-r--r--.venv/lib/python3.12/site-packages/numpy/f2py/tests/test_callback.py243
-rw-r--r--.venv/lib/python3.12/site-packages/numpy/f2py/tests/test_character.py636
-rw-r--r--.venv/lib/python3.12/site-packages/numpy/f2py/tests/test_common.py27
-rw-r--r--.venv/lib/python3.12/site-packages/numpy/f2py/tests/test_compile_function.py117
-rw-r--r--.venv/lib/python3.12/site-packages/numpy/f2py/tests/test_crackfortran.py350
-rw-r--r--.venv/lib/python3.12/site-packages/numpy/f2py/tests/test_data.py70
-rw-r--r--.venv/lib/python3.12/site-packages/numpy/f2py/tests/test_docs.py55
-rw-r--r--.venv/lib/python3.12/site-packages/numpy/f2py/tests/test_f2cmap.py15
-rw-r--r--.venv/lib/python3.12/site-packages/numpy/f2py/tests/test_f2py2e.py896
-rw-r--r--.venv/lib/python3.12/site-packages/numpy/f2py/tests/test_isoc.py52
-rw-r--r--.venv/lib/python3.12/site-packages/numpy/f2py/tests/test_kind.py47
-rw-r--r--.venv/lib/python3.12/site-packages/numpy/f2py/tests/test_mixed.py33
-rw-r--r--.venv/lib/python3.12/site-packages/numpy/f2py/tests/test_module_doc.py27
-rw-r--r--.venv/lib/python3.12/site-packages/numpy/f2py/tests/test_parameter.py112
-rw-r--r--.venv/lib/python3.12/site-packages/numpy/f2py/tests/test_pyf_src.py44
-rw-r--r--.venv/lib/python3.12/site-packages/numpy/f2py/tests/test_quoted_character.py16
-rw-r--r--.venv/lib/python3.12/site-packages/numpy/f2py/tests/test_regression.py77
-rw-r--r--.venv/lib/python3.12/site-packages/numpy/f2py/tests/test_return_character.py45
-rw-r--r--.venv/lib/python3.12/site-packages/numpy/f2py/tests/test_return_complex.py65
-rw-r--r--.venv/lib/python3.12/site-packages/numpy/f2py/tests/test_return_integer.py53
-rw-r--r--.venv/lib/python3.12/site-packages/numpy/f2py/tests/test_return_logical.py64
-rw-r--r--.venv/lib/python3.12/site-packages/numpy/f2py/tests/test_return_real.py107
-rw-r--r--.venv/lib/python3.12/site-packages/numpy/f2py/tests/test_semicolon_split.py74
-rw-r--r--.venv/lib/python3.12/site-packages/numpy/f2py/tests/test_size.py45
-rw-r--r--.venv/lib/python3.12/site-packages/numpy/f2py/tests/test_string.py100
-rw-r--r--.venv/lib/python3.12/site-packages/numpy/f2py/tests/test_symbolic.py494
-rw-r--r--.venv/lib/python3.12/site-packages/numpy/f2py/tests/test_value_attrspec.py14
-rw-r--r--.venv/lib/python3.12/site-packages/numpy/f2py/tests/util.py440
-rw-r--r--.venv/lib/python3.12/site-packages/numpy/f2py/use_rules.py106
139 files changed, 22034 insertions, 0 deletions
diff --git a/.venv/lib/python3.12/site-packages/numpy/f2py/__init__.py b/.venv/lib/python3.12/site-packages/numpy/f2py/__init__.py
new file mode 100644
index 00000000..e583250f
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/numpy/f2py/__init__.py
@@ -0,0 +1,194 @@
+#!/usr/bin/env python3
+"""Fortran to Python Interface Generator.
+
+Copyright 1999 -- 2011 Pearu Peterson all rights reserved.
+Copyright 2011 -- present NumPy Developers.
+Permission to use, modify, and distribute this software is given under the terms
+of the NumPy License.
+
+NO WARRANTY IS EXPRESSED OR IMPLIED.  USE AT YOUR OWN RISK.
+"""
+__all__ = ['run_main', 'compile', 'get_include']
+
+import sys
+import subprocess
+import os
+import warnings
+
+from numpy.exceptions import VisibleDeprecationWarning
+from . import f2py2e
+from . import diagnose
+
+run_main = f2py2e.run_main
+main = f2py2e.main
+
+
+def compile(source,
+            modulename='untitled',
+            extra_args='',
+            verbose=True,
+            source_fn=None,
+            extension='.f',
+            full_output=False
+           ):
+    """
+    Build extension module from a Fortran 77 source string with f2py.
+
+    Parameters
+    ----------
+    source : str or bytes
+        Fortran source of module / subroutine to compile
+
+        .. versionchanged:: 1.16.0
+           Accept str as well as bytes
+
+    modulename : str, optional
+        The name of the compiled python module
+    extra_args : str or list, optional
+        Additional parameters passed to f2py
+
+        .. versionchanged:: 1.16.0
+            A list of args may also be provided.
+
+    verbose : bool, optional
+        Print f2py output to screen
+    source_fn : str, optional
+        Name of the file where the fortran source is written.
+        The default is to use a temporary file with the extension
+        provided by the ``extension`` parameter
+    extension : ``{'.f', '.f90'}``, optional
+        Filename extension if `source_fn` is not provided.
+        The extension tells which fortran standard is used.
+        The default is ``.f``, which implies F77 standard.
+
+        .. versionadded:: 1.11.0
+
+    full_output : bool, optional
+        If True, return a `subprocess.CompletedProcess` containing
+        the stdout and stderr of the compile process, instead of just
+        the status code.
+
+        .. versionadded:: 1.20.0
+
+
+    Returns
+    -------
+    result : int or `subprocess.CompletedProcess`
+        0 on success, or a `subprocess.CompletedProcess` if
+        ``full_output=True``
+
+    Examples
+    --------
+    .. literalinclude:: ../../source/f2py/code/results/compile_session.dat
+        :language: python
+
+    """
+    import tempfile
+    import shlex
+
+    if source_fn is None:
+        f, fname = tempfile.mkstemp(suffix=extension)
+        # f is a file descriptor so need to close it
+        # carefully -- not with .close() directly
+        os.close(f)
+    else:
+        fname = source_fn
+
+    if not isinstance(source, str):
+        source = str(source, 'utf-8')
+    try:
+        with open(fname, 'w') as f:
+            f.write(source)
+
+        args = ['-c', '-m', modulename, f.name]
+
+        if isinstance(extra_args, str):
+            is_posix = (os.name == 'posix')
+            extra_args = shlex.split(extra_args, posix=is_posix)
+
+        args.extend(extra_args)
+
+        c = [sys.executable,
+             '-c',
+             'import numpy.f2py as f2py2e;f2py2e.main()'] + args
+        try:
+            cp = subprocess.run(c, capture_output=True)
+        except OSError:
+            # preserve historic status code used by exec_command()
+            cp = subprocess.CompletedProcess(c, 127, stdout=b'', stderr=b'')
+        else:
+            if verbose:
+                print(cp.stdout.decode())
+    finally:
+        if source_fn is None:
+            os.remove(fname)
+
+    if full_output:
+        return cp
+    else:
+        return cp.returncode
+
+
+def get_include():
+    """
+    Return the directory that contains the ``fortranobject.c`` and ``.h`` files.
+
+    .. note::
+
+        This function is not needed when building an extension with
+        `numpy.distutils` directly from ``.f`` and/or ``.pyf`` files
+        in one go.
+
+    Python extension modules built with f2py-generated code need to use
+    ``fortranobject.c`` as a source file, and include the ``fortranobject.h``
+    header. This function can be used to obtain the directory containing
+    both of these files.
+
+    Returns
+    -------
+    include_path : str
+        Absolute path to the directory containing ``fortranobject.c`` and
+        ``fortranobject.h``.
+
+    Notes
+    -----
+    .. versionadded:: 1.21.1
+
+    Unless the build system you are using has specific support for f2py,
+    building a Python extension using a ``.pyf`` signature file is a two-step
+    process. For a module ``mymod``:
+
+    * Step 1: run ``python -m numpy.f2py mymod.pyf --quiet``. This
+      generates ``_mymodmodule.c`` and (if needed)
+      ``_fblas-f2pywrappers.f`` files next to ``mymod.pyf``.
+    * Step 2: build your Python extension module. This requires the
+      following source files:
+
+      * ``_mymodmodule.c``
+      * ``_mymod-f2pywrappers.f`` (if it was generated in Step 1)
+      * ``fortranobject.c``
+
+    See Also
+    --------
+    numpy.get_include : function that returns the numpy include directory
+
+    """
+    return os.path.join(os.path.dirname(__file__), 'src')
+
+
+def __getattr__(attr):
+
+    # Avoid importing things that aren't needed for building
+    # which might import the main numpy module
+    if attr == "test":
+        from numpy._pytesttester import PytestTester
+        test = PytestTester(__name__)
+        return test
+
+    else:
+        raise AttributeError("module {!r} has no attribute "
+                              "{!r}".format(__name__, attr))
+
+
+def __dir__():
+    return list(globals().keys() | {"test"})
diff --git a/.venv/lib/python3.12/site-packages/numpy/f2py/__init__.pyi b/.venv/lib/python3.12/site-packages/numpy/f2py/__init__.pyi
new file mode 100644
index 00000000..81b6a24f
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/numpy/f2py/__init__.pyi
@@ -0,0 +1,42 @@
+import os
+import subprocess
+from collections.abc import Iterable
+from typing import Literal as L, Any, overload, TypedDict
+
+from numpy._pytesttester import PytestTester
+
+class _F2PyDictBase(TypedDict):
+    csrc: list[str]
+    h: list[str]
+
+class _F2PyDict(_F2PyDictBase, total=False):
+    fsrc: list[str]
+    ltx: list[str]
+
+__all__: list[str]
+test: PytestTester
+
+def run_main(comline_list: Iterable[str]) -> dict[str, _F2PyDict]: ...
+
+@overload
+def compile(  # type: ignore[misc]
+    source: str | bytes,
+    modulename: str = ...,
+    extra_args: str | list[str] = ...,
+    verbose: bool = ...,
+    source_fn: None | str | bytes | os.PathLike[Any] = ...,
+    extension: L[".f", ".f90"] = ...,
+    full_output: L[False] = ...,
+) -> int: ...
+@overload
+def compile(
+    source: str | bytes,
+    modulename: str = ...,
+    extra_args: str | list[str] = ...,
+    verbose: bool = ...,
+    source_fn: None | str | bytes | os.PathLike[Any] = ...,
+    extension: L[".f", ".f90"] = ...,
+    full_output: L[True] = ...,
+) -> subprocess.CompletedProcess[bytes]: ...
+
+def get_include() -> str: ...
diff --git a/.venv/lib/python3.12/site-packages/numpy/f2py/__main__.py b/.venv/lib/python3.12/site-packages/numpy/f2py/__main__.py
new file mode 100644
index 00000000..936a753a
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/numpy/f2py/__main__.py
@@ -0,0 +1,5 @@
+# See:
+# https://web.archive.org/web/20140822061353/http://cens.ioc.ee/projects/f2py2e
+from numpy.f2py.f2py2e import main
+
+main()
diff --git a/.venv/lib/python3.12/site-packages/numpy/f2py/__version__.py b/.venv/lib/python3.12/site-packages/numpy/f2py/__version__.py
new file mode 100644
index 00000000..e20d7c1d
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/numpy/f2py/__version__.py
@@ -0,0 +1 @@
+from numpy.version import version
diff --git a/.venv/lib/python3.12/site-packages/numpy/f2py/_backends/__init__.py b/.venv/lib/python3.12/site-packages/numpy/f2py/_backends/__init__.py
new file mode 100644
index 00000000..e91393c1
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/numpy/f2py/_backends/__init__.py
@@ -0,0 +1,9 @@
+def f2py_build_generator(name):
+    if name == "meson":
+        from ._meson import MesonBackend
+        return MesonBackend
+    elif name == "distutils":
+        from ._distutils import DistutilsBackend
+        return DistutilsBackend
+    else:
+        raise ValueError(f"Unknown backend: {name}")
diff --git a/.venv/lib/python3.12/site-packages/numpy/f2py/_backends/_backend.py b/.venv/lib/python3.12/site-packages/numpy/f2py/_backends/_backend.py
new file mode 100644
index 00000000..a7d43d25
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/numpy/f2py/_backends/_backend.py
@@ -0,0 +1,46 @@
+from __future__ import annotations
+
+from abc import ABC, abstractmethod
+
+
+class Backend(ABC):
+    def __init__(
+        self,
+        modulename,
+        sources,
+        extra_objects,
+        build_dir,
+        include_dirs,
+        library_dirs,
+        libraries,
+        define_macros,
+        undef_macros,
+        f2py_flags,
+        sysinfo_flags,
+        fc_flags,
+        flib_flags,
+        setup_flags,
+        remove_build_dir,
+        extra_dat,
+    ):
+        self.modulename = modulename
+        self.sources = sources
+        self.extra_objects = extra_objects
+        self.build_dir = build_dir
+        self.include_dirs = include_dirs
+        self.library_dirs = library_dirs
+        self.libraries = libraries
+        self.define_macros = define_macros
+        self.undef_macros = undef_macros
+        self.f2py_flags = f2py_flags
+        self.sysinfo_flags = sysinfo_flags
+        self.fc_flags = fc_flags
+        self.flib_flags = flib_flags
+        self.setup_flags = setup_flags
+        self.remove_build_dir = remove_build_dir
+        self.extra_dat = extra_dat
+
+    @abstractmethod
+    def compile(self) -> None:
+        """Compile the wrapper."""
+        pass
diff --git a/.venv/lib/python3.12/site-packages/numpy/f2py/_backends/_distutils.py b/.venv/lib/python3.12/site-packages/numpy/f2py/_backends/_distutils.py
new file mode 100644
index 00000000..e9b22a39
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/numpy/f2py/_backends/_distutils.py
@@ -0,0 +1,75 @@
+from ._backend import Backend
+
+from numpy.distutils.core import setup, Extension
+from numpy.distutils.system_info import get_info
+from numpy.distutils.misc_util import dict_append
+from numpy.exceptions import VisibleDeprecationWarning
+import os
+import sys
+import shutil
+import warnings
+
+
+class DistutilsBackend(Backend):
+    def __init__(sef, *args, **kwargs):
+        warnings.warn(
+            "distutils has been deprecated since NumPy 1.26.x"
+            "Use the Meson backend instead, or generate wrappers"
+            "without -c and use a custom build script",
+            VisibleDeprecationWarning,
+            stacklevel=2,
+        )
+        super().__init__(*args, **kwargs)
+
+    def compile(self):
+        num_info = {}
+        if num_info:
+            self.include_dirs.extend(num_info.get("include_dirs", []))
+        ext_args = {
+            "name": self.modulename,
+            "sources": self.sources,
+            "include_dirs": self.include_dirs,
+            "library_dirs": self.library_dirs,
+            "libraries": self.libraries,
+            "define_macros": self.define_macros,
+            "undef_macros": self.undef_macros,
+            "extra_objects": self.extra_objects,
+            "f2py_options": self.f2py_flags,
+        }
+
+        if self.sysinfo_flags:
+            for n in self.sysinfo_flags:
+                i = get_info(n)
+                if not i:
+                    print(
+                        f"No {repr(n)} resources found"
+                        "in system (try `f2py --help-link`)"
+                    )
+                dict_append(ext_args, **i)
+
+        ext = Extension(**ext_args)
+
+        sys.argv = [sys.argv[0]] + self.setup_flags
+        sys.argv.extend(
+            [
+                "build",
+                "--build-temp",
+                self.build_dir,
+                "--build-base",
+                self.build_dir,
+                "--build-platlib",
+                ".",
+                "--disable-optimization",
+            ]
+        )
+
+        if self.fc_flags:
+            sys.argv.extend(["config_fc"] + self.fc_flags)
+        if self.flib_flags:
+            sys.argv.extend(["build_ext"] + self.flib_flags)
+
+        setup(ext_modules=[ext])
+
+        if self.remove_build_dir and os.path.exists(self.build_dir):
+            print(f"Removing build directory {self.build_dir}")
+            shutil.rmtree(self.build_dir)
diff --git a/.venv/lib/python3.12/site-packages/numpy/f2py/_backends/_meson.py b/.venv/lib/python3.12/site-packages/numpy/f2py/_backends/_meson.py
new file mode 100644
index 00000000..f324e0f5
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/numpy/f2py/_backends/_meson.py
@@ -0,0 +1,205 @@
+from __future__ import annotations
+
+import os
+import errno
+import shutil
+import subprocess
+import sys
+from pathlib import Path
+
+from ._backend import Backend
+from string import Template
+from itertools import chain
+
+import warnings
+
+
+class MesonTemplate:
+    """Template meson build file generation class."""
+
+    def __init__(
+        self,
+        modulename: str,
+        sources: list[Path],
+        deps: list[str],
+        libraries: list[str],
+        library_dirs: list[Path],
+        include_dirs: list[Path],
+        object_files: list[Path],
+        linker_args: list[str],
+        c_args: list[str],
+        build_type: str,
+        python_exe: str,
+    ):
+        self.modulename = modulename
+        self.build_template_path = (
+            Path(__file__).parent.absolute() / "meson.build.template"
+        )
+        self.sources = sources
+        self.deps = deps
+        self.libraries = libraries
+        self.library_dirs = library_dirs
+        if include_dirs is not None:
+            self.include_dirs = include_dirs
+        else:
+            self.include_dirs = []
+        self.substitutions = {}
+        self.objects = object_files
+        self.pipeline = [
+            self.initialize_template,
+            self.sources_substitution,
+            self.deps_substitution,
+            self.include_substitution,
+            self.libraries_substitution,
+        ]
+        self.build_type = build_type
+        self.python_exe = python_exe
+
+    def meson_build_template(self) -> str:
+        if not self.build_template_path.is_file():
+            raise FileNotFoundError(
+                errno.ENOENT,
+                "Meson build template"
+                f" {self.build_template_path.absolute()}"
+                " does not exist.",
+            )
+        return self.build_template_path.read_text()
+
+    def initialize_template(self) -> None:
+        self.substitutions["modulename"] = self.modulename
+        self.substitutions["buildtype"] = self.build_type
+        self.substitutions["python"] = self.python_exe
+
+    def sources_substitution(self) -> None:
+        indent = " " * 21
+        self.substitutions["source_list"] = f",\n{indent}".join(
+            [f"{indent}'{source}'" for source in self.sources]
+        )
+
+    def deps_substitution(self) -> None:
+        indent = " " * 21
+        self.substitutions["dep_list"] = f",\n{indent}".join(
+            [f"{indent}dependency('{dep}')" for dep in self.deps]
+        )
+
+    def libraries_substitution(self) -> None:
+        self.substitutions["lib_dir_declarations"] = "\n".join(
+            [
+                f"lib_dir_{i} = declare_dependency(link_args : ['-L{lib_dir}'])"
+                for i, lib_dir in enumerate(self.library_dirs)
+            ]
+        )
+
+        self.substitutions["lib_declarations"] = "\n".join(
+            [
+                f"{lib} = declare_dependency(link_args : ['-l{lib}'])"
+                for lib in self.libraries
+            ]
+        )
+
+        indent = " " * 21
+        self.substitutions["lib_list"] = f"\n{indent}".join(
+            [f"{indent}{lib}," for lib in self.libraries]
+        )
+        self.substitutions["lib_dir_list"] = f"\n{indent}".join(
+            [f"{indent}lib_dir_{i}," for i in range(len(self.library_dirs))]
+        )
+
+    def include_substitution(self) -> None:
+        indent = " " * 21
+        self.substitutions["inc_list"] = f",\n{indent}".join(
+            [f"{indent}'{inc}'" for inc in self.include_dirs]
+        )
+
+    def generate_meson_build(self):
+        for node in self.pipeline:
+            node()
+        template = Template(self.meson_build_template())
+        return template.substitute(self.substitutions)
+
+
+class MesonBackend(Backend):
+    def __init__(self, *args, **kwargs):
+        super().__init__(*args, **kwargs)
+        self.dependencies = self.extra_dat.get("dependencies", [])
+        self.meson_build_dir = "bbdir"
+        self.build_type = (
+            "debug" if any("debug" in flag for flag in self.fc_flags) else "release"
+        )
+
+    def _move_exec_to_root(self, build_dir: Path):
+        walk_dir = Path(build_dir) / self.meson_build_dir
+        path_objects = chain(
+            walk_dir.glob(f"{self.modulename}*.so"),
+            walk_dir.glob(f"{self.modulename}*.pyd"),
+        )
+        # Same behavior as distutils
+        # https://github.com/numpy/numpy/issues/24874#issuecomment-1835632293
+        for path_object in path_objects:
+            dest_path = Path.cwd() / path_object.name
+            if dest_path.exists():
+                dest_path.unlink()
+            shutil.copy2(path_object, dest_path)
+            os.remove(path_object)
+
+    def write_meson_build(self, build_dir: Path) -> None:
+        """Writes the meson build file at specified location"""
+        meson_template = MesonTemplate(
+            self.modulename,
+            self.sources,
+            self.dependencies,
+            self.libraries,
+            self.library_dirs,
+            self.include_dirs,
+            self.extra_objects,
+            self.flib_flags,
+            self.fc_flags,
+            self.build_type,
+            sys.executable,
+        )
+        src = meson_template.generate_meson_build()
+        Path(build_dir).mkdir(parents=True, exist_ok=True)
+        meson_build_file = Path(build_dir) / "meson.build"
+        meson_build_file.write_text(src)
+        return meson_build_file
+
+    def _run_subprocess_command(self, command, cwd):
+        subprocess.run(command, cwd=cwd, check=True)
+
+    def run_meson(self, build_dir: Path):
+        setup_command = ["meson", "setup", self.meson_build_dir]
+        self._run_subprocess_command(setup_command, build_dir)
+        compile_command = ["meson", "compile", "-C", self.meson_build_dir]
+        self._run_subprocess_command(compile_command, build_dir)
+
+    def compile(self) -> None:
+        self.sources = _prepare_sources(self.modulename, self.sources, self.build_dir)
+        self.write_meson_build(self.build_dir)
+        self.run_meson(self.build_dir)
+        self._move_exec_to_root(self.build_dir)
+
+
+def _prepare_sources(mname, sources, bdir):
+    extended_sources = sources.copy()
+    Path(bdir).mkdir(parents=True, exist_ok=True)
+    # Copy sources
+    for source in sources:
+        if Path(source).exists() and Path(source).is_file():
+            shutil.copy(source, bdir)
+    generated_sources = [
+        Path(f"{mname}module.c"),
+        Path(f"{mname}-f2pywrappers2.f90"),
+        Path(f"{mname}-f2pywrappers.f"),
+    ]
+    bdir = Path(bdir)
+    for generated_source in generated_sources:
+        if generated_source.exists():
+            shutil.copy(generated_source, bdir / generated_source.name)
+            extended_sources.append(generated_source.name)
+            generated_source.unlink()
+    extended_sources = [
+        Path(source).name
+        for source in extended_sources
+        if not Path(source).suffix == ".pyf"
+    ]
+    return extended_sources
diff --git a/.venv/lib/python3.12/site-packages/numpy/f2py/_backends/meson.build.template b/.venv/lib/python3.12/site-packages/numpy/f2py/_backends/meson.build.template
new file mode 100644
index 00000000..8e34fdc8
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/numpy/f2py/_backends/meson.build.template
@@ -0,0 +1,54 @@
+project('${modulename}',
+        ['c', 'fortran'],
+        version : '0.1',
+        meson_version: '>= 1.1.0',
+        default_options : [
+                            'warning_level=1',
+                            'buildtype=${buildtype}'
+                          ])
+fc = meson.get_compiler('fortran')
+
+py = import('python').find_installation('${python}', pure: false)
+py_dep = py.dependency()
+
+incdir_numpy = run_command(py,
+  ['-c', 'import os; os.chdir(".."); import numpy; print(numpy.get_include())'],
+  check : true
+).stdout().strip()
+
+incdir_f2py = run_command(py,
+    ['-c', 'import os; os.chdir(".."); import numpy.f2py; print(numpy.f2py.get_include())'],
+    check : true
+).stdout().strip()
+
+inc_np = include_directories(incdir_numpy)
+np_dep = declare_dependency(include_directories: inc_np)
+
+incdir_f2py = incdir_numpy / '..' / '..' / 'f2py' / 'src'
+inc_f2py = include_directories(incdir_f2py)
+fortranobject_c = incdir_f2py / 'fortranobject.c'
+
+inc_np = include_directories(incdir_numpy, incdir_f2py)
+# gh-25000
+quadmath_dep = fc.find_library('quadmath', required: false)
+
+${lib_declarations}
+${lib_dir_declarations}
+
+py.extension_module('${modulename}',
+                     [
+${source_list},
+                     fortranobject_c
+                     ],
+                     include_directories: [
+                     inc_np,
+${inc_list}
+                     ],
+                     dependencies : [
+                     py_dep,
+                     quadmath_dep,
+${dep_list}
+${lib_list}
+${lib_dir_list}
+                     ],
+                     install : true)
diff --git a/.venv/lib/python3.12/site-packages/numpy/f2py/_isocbind.py b/.venv/lib/python3.12/site-packages/numpy/f2py/_isocbind.py
new file mode 100644
index 00000000..3043c5d9
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/numpy/f2py/_isocbind.py
@@ -0,0 +1,62 @@
+"""
+ISO_C_BINDING maps for f2py2e.
+Only required declarations/macros/functions will be used.
+
+Copyright 1999 -- 2011 Pearu Peterson all rights reserved.
+Copyright 2011 -- present NumPy Developers.
+Permission to use, modify, and distribute this software is given under the
+terms of the NumPy License.
+
+NO WARRANTY IS EXPRESSED OR IMPLIED.  USE AT YOUR OWN RISK.
+"""
+# These map to keys in c2py_map, via forced casting for now, see gh-25229
+iso_c_binding_map = {
+    'integer': {
+        'c_int': 'int',
+        'c_short': 'short',  # 'short' <=> 'int' for now
+        'c_long': 'long',  # 'long' <=> 'int' for now
+        'c_long_long': 'long_long',
+        'c_signed_char': 'signed_char',
+        'c_size_t': 'unsigned',  # size_t <=> 'unsigned' for now
+        'c_int8_t': 'signed_char',  # int8_t <=> 'signed_char' for now
+        'c_int16_t': 'short',  # int16_t <=> 'short' for now
+        'c_int32_t': 'int',  # int32_t <=> 'int' for now
+        'c_int64_t': 'long_long',
+        'c_int_least8_t': 'signed_char',  # int_least8_t <=> 'signed_char' for now
+        'c_int_least16_t': 'short',  # int_least16_t <=> 'short' for now
+        'c_int_least32_t': 'int',  # int_least32_t <=> 'int' for now
+        'c_int_least64_t': 'long_long',
+        'c_int_fast8_t': 'signed_char',  # int_fast8_t <=> 'signed_char' for now
+        'c_int_fast16_t': 'short',  # int_fast16_t <=> 'short' for now
+        'c_int_fast32_t': 'int',  # int_fast32_t <=> 'int' for now
+        'c_int_fast64_t': 'long_long',
+        'c_intmax_t': 'long_long',  # intmax_t <=> 'long_long' for now
+        'c_intptr_t': 'long',  # intptr_t <=> 'long' for now
+        'c_ptrdiff_t': 'long',  # ptrdiff_t <=> 'long' for now
+    },
+    'real': {
+        'c_float': 'float',
+        'c_double': 'double',
+        'c_long_double': 'long_double'
+    },
+    'complex': {
+        'c_float_complex': 'complex_float',
+        'c_double_complex': 'complex_double',
+        'c_long_double_complex': 'complex_long_double'
+    },
+    'logical': {
+        'c_bool': 'unsigned_char'  # _Bool <=> 'unsigned_char' for now
+    },
+    'character': {
+        'c_char': 'char'
+    }
+}
+
+# TODO: See gh-25229
+isoc_c2pycode_map = {}
+iso_c2py_map = {}
+
+isoc_kindmap = {}
+for fortran_type, c_type_dict in iso_c_binding_map.items():
+    for c_type in c_type_dict.keys():
+        isoc_kindmap[c_type] = fortran_type
diff --git a/.venv/lib/python3.12/site-packages/numpy/f2py/_src_pyf.py b/.venv/lib/python3.12/site-packages/numpy/f2py/_src_pyf.py
new file mode 100644
index 00000000..6247b95b
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/numpy/f2py/_src_pyf.py
@@ -0,0 +1,239 @@
+import re
+
+# START OF CODE VENDORED FROM `numpy.distutils.from_template`
+#############################################################
+"""
+process_file(filename)
+
+  takes templated file .xxx.src and produces .xxx file where .xxx
+  is .pyf .f90 or .f using the following template rules:
+
+  '<..>' denotes a template.
+
+  All function and subroutine blocks in a source file with names that
+  contain '<..>' will be replicated according to the rules in '<..>'.
+
+  The number of comma-separated words in '<..>' will determine the number of
+  replicates.
+
+  '<..>' may have two different forms, named and short. For example,
+
+  named:
+   <p=d,s,z,c> where anywhere inside a block '<p>' will be replaced with
+   'd', 's', 'z', and 'c' for each replicate of the block.
+
+   <_c>  is already defined: <_c=s,d,c,z>
+   <_t>  is already defined: <_t=real,double precision,complex,double complex>
+
+  short:
+   <s,d,c,z>, a short form of the named, useful when no <p> appears inside
+   a block.
+
+  In general, '<..>' contains a comma separated list of arbitrary
+  expressions. If these expression must contain a comma|leftarrow|rightarrow,
+  then prepend the comma|leftarrow|rightarrow with a backslash.
+
+  If an expression matches '\\<index>' then it will be replaced
+  by <index>-th expression.
+
+  Note that all '<..>' forms in a block must have the same number of
+  comma-separated entries.
+
+ Predefined named template rules:
+  <prefix=s,d,c,z>
+  <ftype=real,double precision,complex,double complex>
+  <ftypereal=real,double precision,\\0,\\1>
+  <ctype=float,double,complex_float,complex_double>
+  <ctypereal=float,double,\\0,\\1>
+"""
+
+routine_start_re = re.compile(r'(\n|\A)((     (\$|\*))|)\s*(subroutine|function)\b', re.I)
+routine_end_re = re.compile(r'\n\s*end\s*(subroutine|function)\b.*(\n|\Z)', re.I)
+function_start_re = re.compile(r'\n     (\$|\*)\s*function\b', re.I)
+
+def parse_structure(astr):
+    """ Return a list of tuples for each function or subroutine each
+    tuple is the start and end of a subroutine or function to be
+    expanded.
+    """
+
+    spanlist = []
+    ind = 0
+    while True:
+        m = routine_start_re.search(astr, ind)
+        if m is None:
+            break
+        start = m.start()
+        if function_start_re.match(astr, start, m.end()):
+            while True:
+                i = astr.rfind('\n', ind, start)
+                if i==-1:
+                    break
+                start = i
+                if astr[i:i+7]!='\n     $':
+                    break
+        start += 1
+        m = routine_end_re.search(astr, m.end())
+        ind = end = m and m.end()-1 or len(astr)
+        spanlist.append((start, end))
+    return spanlist
+
+template_re = re.compile(r"<\s*(\w[\w\d]*)\s*>")
+named_re = re.compile(r"<\s*(\w[\w\d]*)\s*=\s*(.*?)\s*>")
+list_re = re.compile(r"<\s*((.*?))\s*>")
+
+def find_repl_patterns(astr):
+    reps = named_re.findall(astr)
+    names = {}
+    for rep in reps:
+        name = rep[0].strip() or unique_key(names)
+        repl = rep[1].replace(r'\,', '@comma@')
+        thelist = conv(repl)
+        names[name] = thelist
+    return names
+
+def find_and_remove_repl_patterns(astr):
+    names = find_repl_patterns(astr)
+    astr = re.subn(named_re, '', astr)[0]
+    return astr, names
+
+item_re = re.compile(r"\A\\(?P<index>\d+)\Z")
+def conv(astr):
+    b = astr.split(',')
+    l = [x.strip() for x in b]
+    for i in range(len(l)):
+        m = item_re.match(l[i])
+        if m:
+            j = int(m.group('index'))
+            l[i] = l[j]
+    return ','.join(l)
+
+def unique_key(adict):
+    """ Obtain a unique key given a dictionary."""
+    allkeys = list(adict.keys())
+    done = False
+    n = 1
+    while not done:
+        newkey = '__l%s' % (n)
+        if newkey in allkeys:
+            n += 1
+        else:
+            done = True
+    return newkey
+
+
+template_name_re = re.compile(r'\A\s*(\w[\w\d]*)\s*\Z')
+def expand_sub(substr, names):
+    substr = substr.replace(r'\>', '@rightarrow@')
+    substr = substr.replace(r'\<', '@leftarrow@')
+    lnames = find_repl_patterns(substr)
+    substr = named_re.sub(r"<\1>", substr)  # get rid of definition templates
+
+    def listrepl(mobj):
+        thelist = conv(mobj.group(1).replace(r'\,', '@comma@'))
+        if template_name_re.match(thelist):
+            return "<%s>" % (thelist)
+        name = None
+        for key in lnames.keys():    # see if list is already in dictionary
+            if lnames[key] == thelist:
+                name = key
+        if name is None:      # this list is not in the dictionary yet
+            name = unique_key(lnames)
+            lnames[name] = thelist
+        return "<%s>" % name
+
+    substr = list_re.sub(listrepl, substr) # convert all lists to named templates
+                                           # newnames are constructed as needed
+
+    numsubs = None
+    base_rule = None
+    rules = {}
+    for r in template_re.findall(substr):
+        if r not in rules:
+            thelist = lnames.get(r, names.get(r, None))
+            if thelist is None:
+                raise ValueError('No replicates found for <%s>' % (r))
+            if r not in names and not thelist.startswith('_'):
+                names[r] = thelist
+            rule = [i.replace('@comma@', ',') for i in thelist.split(',')]
+            num = len(rule)
+
+            if numsubs is None:
+                numsubs = num
+                rules[r] = rule
+                base_rule = r
+            elif num == numsubs:
+                rules[r] = rule
+            else:
+                print("Mismatch in number of replacements (base <{}={}>) "
+                      "for <{}={}>. Ignoring.".format(base_rule, ','.join(rules[base_rule]), r, thelist))
+    if not rules:
+        return substr
+
+    def namerepl(mobj):
+        name = mobj.group(1)
+        return rules.get(name, (k+1)*[name])[k]
+
+    newstr = ''
+    for k in range(numsubs):
+        newstr += template_re.sub(namerepl, substr) + '\n\n'
+
+    newstr = newstr.replace('@rightarrow@', '>')
+    newstr = newstr.replace('@leftarrow@', '<')
+    return newstr
+
+def process_str(allstr):
+    newstr = allstr
+    writestr = ''
+
+    struct = parse_structure(newstr)
+
+    oldend = 0
+    names = {}
+    names.update(_special_names)
+    for sub in struct:
+        cleanedstr, defs = find_and_remove_repl_patterns(newstr[oldend:sub[0]])
+        writestr += cleanedstr
+        names.update(defs)
+        writestr += expand_sub(newstr[sub[0]:sub[1]], names)
+        oldend =  sub[1]
+    writestr += newstr[oldend:]
+
+    return writestr
+
+include_src_re = re.compile(r"(\n|\A)\s*include\s*['\"](?P<name>[\w\d./\\]+\.src)['\"]", re.I)
+
+def resolve_includes(source):
+    d = os.path.dirname(source)
+    with open(source) as fid:
+        lines = []
+        for line in fid:
+            m = include_src_re.match(line)
+            if m:
+                fn = m.group('name')
+                if not os.path.isabs(fn):
+                    fn = os.path.join(d, fn)
+                if os.path.isfile(fn):
+                    lines.extend(resolve_includes(fn))
+                else:
+                    lines.append(line)
+            else:
+                lines.append(line)
+    return lines
+
+def process_file(source):
+    lines = resolve_includes(source)
+    return process_str(''.join(lines))
+
+_special_names = find_repl_patterns('''
+<_c=s,d,c,z>
+<_t=real,double precision,complex,double complex>
+<prefix=s,d,c,z>
+<ftype=real,double precision,complex,double complex>
+<ctype=float,double,complex_float,complex_double>
+<ftypereal=real,double precision,\\0,\\1>
+<ctypereal=float,double,\\0,\\1>
+''')
+
+# END OF CODE VENDORED FROM `numpy.distutils.from_template`
+###########################################################
diff --git a/.venv/lib/python3.12/site-packages/numpy/f2py/auxfuncs.py b/.venv/lib/python3.12/site-packages/numpy/f2py/auxfuncs.py
new file mode 100644
index 00000000..13a1074b
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/numpy/f2py/auxfuncs.py
@@ -0,0 +1,988 @@
+"""
+Auxiliary functions for f2py2e.
+
+Copyright 1999 -- 2011 Pearu Peterson all rights reserved.
+Copyright 2011 -- present NumPy Developers.
+Permission to use, modify, and distribute this software is given under the
+terms of the NumPy (BSD style) LICENSE.
+
+NO WARRANTY IS EXPRESSED OR IMPLIED.  USE AT YOUR OWN RISK.
+"""
+import pprint
+import sys
+import re
+import types
+from functools import reduce
+from copy import deepcopy
+
+from . import __version__
+from . import cfuncs
+
+__all__ = [
+    'applyrules', 'debugcapi', 'dictappend', 'errmess', 'gentitle',
+    'getargs2', 'getcallprotoargument', 'getcallstatement',
+    'getfortranname', 'getpymethoddef', 'getrestdoc', 'getusercode',
+    'getusercode1', 'getdimension', 'hasbody', 'hascallstatement', 'hascommon',
+    'hasexternals', 'hasinitvalue', 'hasnote', 'hasresultnote',
+    'isallocatable', 'isarray', 'isarrayofstrings',
+    'ischaracter', 'ischaracterarray', 'ischaracter_or_characterarray',
+    'iscomplex',
+    'iscomplexarray', 'iscomplexfunction', 'iscomplexfunction_warn',
+    'isdouble', 'isdummyroutine', 'isexternal', 'isfunction',
+    'isfunction_wrap', 'isint1', 'isint1array', 'isinteger', 'isintent_aux',
+    'isintent_c', 'isintent_callback', 'isintent_copy', 'isintent_dict',
+    'isintent_hide', 'isintent_in', 'isintent_inout', 'isintent_inplace',
+    'isintent_nothide', 'isintent_out', 'isintent_overwrite', 'islogical',
+    'islogicalfunction', 'islong_complex', 'islong_double',
+    'islong_doublefunction', 'islong_long', 'islong_longfunction',
+    'ismodule', 'ismoduleroutine', 'isoptional', 'isprivate', 'isrequired',
+    'isroutine', 'isscalar', 'issigned_long_longarray', 'isstring',
+    'isstringarray', 'isstring_or_stringarray', 'isstringfunction',
+    'issubroutine', 'get_f2py_modulename',
+    'issubroutine_wrap', 'isthreadsafe', 'isunsigned', 'isunsigned_char',
+    'isunsigned_chararray', 'isunsigned_long_long',
+    'isunsigned_long_longarray', 'isunsigned_short',
+    'isunsigned_shortarray', 'l_and', 'l_not', 'l_or', 'outmess',
+    'replace', 'show', 'stripcomma', 'throw_error', 'isattr_value',
+    'getuseblocks', 'process_f2cmap_dict'
+]
+
+
+f2py_version = __version__.version
+
+
+errmess = sys.stderr.write
+show = pprint.pprint
+
+options = {}
+debugoptions = []
+wrapfuncs = 1
+
+
+def outmess(t):
+    if options.get('verbose', 1):
+        sys.stdout.write(t)
+
+
+def debugcapi(var):
+    return 'capi' in debugoptions
+
+
+def _ischaracter(var):
+    return 'typespec' in var and var['typespec'] == 'character' and \
+           not isexternal(var)
+
+
+def _isstring(var):
+    return 'typespec' in var and var['typespec'] == 'character' and \
+           not isexternal(var)
+
+
+def ischaracter_or_characterarray(var):
+    return _ischaracter(var) and 'charselector' not in var
+
+
+def ischaracter(var):
+    return ischaracter_or_characterarray(var) and not isarray(var)
+
+
+def ischaracterarray(var):
+    return ischaracter_or_characterarray(var) and isarray(var)
+
+
+def isstring_or_stringarray(var):
+    return _ischaracter(var) and 'charselector' in var
+
+
+def isstring(var):
+    return isstring_or_stringarray(var) and not isarray(var)
+
+
+def isstringarray(var):
+    return isstring_or_stringarray(var) and isarray(var)
+
+
+def isarrayofstrings(var):  # obsolete?
+    # leaving out '*' for now so that `character*(*) a(m)` and `character
+    # a(m,*)` are treated differently. Luckily `character**` is illegal.
+    return isstringarray(var) and var['dimension'][-1] == '(*)'
+
+
+def isarray(var):
+    return 'dimension' in var and not isexternal(var)
+
+
+def isscalar(var):
+    return not (isarray(var) or isstring(var) or isexternal(var))
+
+
+def iscomplex(var):
+    return isscalar(var) and \
+           var.get('typespec') in ['complex', 'double complex']
+
+
+def islogical(var):
+    return isscalar(var) and var.get('typespec') == 'logical'
+
+
+def isinteger(var):
+    return isscalar(var) and var.get('typespec') == 'integer'
+
+
+def isreal(var):
+    return isscalar(var) and var.get('typespec') == 'real'
+
+
+def get_kind(var):
+    try:
+        return var['kindselector']['*']
+    except KeyError:
+        try:
+            return var['kindselector']['kind']
+        except KeyError:
+            pass
+
+
+def isint1(var):
+    return var.get('typespec') == 'integer' \
+        and get_kind(var) == '1' and not isarray(var)
+
+
+def islong_long(var):
+    if not isscalar(var):
+        return 0
+    if var.get('typespec') not in ['integer', 'logical']:
+        return 0
+    return get_kind(var) == '8'
+
+
+def isunsigned_char(var):
+    if not isscalar(var):
+        return 0
+    if var.get('typespec') != 'integer':
+        return 0
+    return get_kind(var) == '-1'
+
+
+def isunsigned_short(var):
+    if not isscalar(var):
+        return 0
+    if var.get('typespec') != 'integer':
+        return 0
+    return get_kind(var) == '-2'
+
+
+def isunsigned(var):
+    if not isscalar(var):
+        return 0
+    if var.get('typespec') != 'integer':
+        return 0
+    return get_kind(var) == '-4'
+
+
+def isunsigned_long_long(var):
+    if not isscalar(var):
+        return 0
+    if var.get('typespec') != 'integer':
+        return 0
+    return get_kind(var) == '-8'
+
+
+def isdouble(var):
+    if not isscalar(var):
+        return 0
+    if not var.get('typespec') == 'real':
+        return 0
+    return get_kind(var) == '8'
+
+
+def islong_double(var):
+    if not isscalar(var):
+        return 0
+    if not var.get('typespec') == 'real':
+        return 0
+    return get_kind(var) == '16'
+
+
+def islong_complex(var):
+    if not iscomplex(var):
+        return 0
+    return get_kind(var) == '32'
+
+
+def iscomplexarray(var):
+    return isarray(var) and \
+           var.get('typespec') in ['complex', 'double complex']
+
+
+def isint1array(var):
+    return isarray(var) and var.get('typespec') == 'integer' \
+        and get_kind(var) == '1'
+
+
+def isunsigned_chararray(var):
+    return isarray(var) and var.get('typespec') in ['integer', 'logical']\
+        and get_kind(var) == '-1'
+
+
+def isunsigned_shortarray(var):
+    return isarray(var) and var.get('typespec') in ['integer', 'logical']\
+        and get_kind(var) == '-2'
+
+
+def isunsignedarray(var):
+    return isarray(var) and var.get('typespec') in ['integer', 'logical']\
+        and get_kind(var) == '-4'
+
+
+def isunsigned_long_longarray(var):
+    return isarray(var) and var.get('typespec') in ['integer', 'logical']\
+        and get_kind(var) == '-8'
+
+
+def issigned_chararray(var):
+    return isarray(var) and var.get('typespec') in ['integer', 'logical']\
+        and get_kind(var) == '1'
+
+
+def issigned_shortarray(var):
+    return isarray(var) and var.get('typespec') in ['integer', 'logical']\
+        and get_kind(var) == '2'
+
+
+def issigned_array(var):
+    return isarray(var) and var.get('typespec') in ['integer', 'logical']\
+        and get_kind(var) == '4'
+
+
+def issigned_long_longarray(var):
+    return isarray(var) and var.get('typespec') in ['integer', 'logical']\
+        and get_kind(var) == '8'
+
+
+def isallocatable(var):
+    return 'attrspec' in var and 'allocatable' in var['attrspec']
+
+
+def ismutable(var):
+    return not ('dimension' not in var or isstring(var))
+
+
+def ismoduleroutine(rout):
+    return 'modulename' in rout
+
+
+def ismodule(rout):
+    return 'block' in rout and 'module' == rout['block']
+
+
+def isfunction(rout):
+    return 'block' in rout and 'function' == rout['block']
+
+
+def isfunction_wrap(rout):
+    if isintent_c(rout):
+        return 0
+    return wrapfuncs and isfunction(rout) and (not isexternal(rout))
+
+
+def issubroutine(rout):
+    return 'block' in rout and 'subroutine' == rout['block']
+
+
+def issubroutine_wrap(rout):
+    if isintent_c(rout):
+        return 0
+    return issubroutine(rout) and hasassumedshape(rout)
+
+def isattr_value(var):
+    return 'value' in var.get('attrspec', [])
+
+
+def hasassumedshape(rout):
+    if rout.get('hasassumedshape'):
+        return True
+    for a in rout['args']:
+        for d in rout['vars'].get(a, {}).get('dimension', []):
+            if d == ':':
+                rout['hasassumedshape'] = True
+                return True
+    return False
+
+
+def requiresf90wrapper(rout):
+    return ismoduleroutine(rout) or hasassumedshape(rout)
+
+
+def isroutine(rout):
+    return isfunction(rout) or issubroutine(rout)
+
+
+def islogicalfunction(rout):
+    if not isfunction(rout):
+        return 0
+    if 'result' in rout:
+        a = rout['result']
+    else:
+        a = rout['name']
+    if a in rout['vars']:
+        return islogical(rout['vars'][a])
+    return 0
+
+
+def islong_longfunction(rout):
+    if not isfunction(rout):
+        return 0
+    if 'result' in rout:
+        a = rout['result']
+    else:
+        a = rout['name']
+    if a in rout['vars']:
+        return islong_long(rout['vars'][a])
+    return 0
+
+
+def islong_doublefunction(rout):
+    if not isfunction(rout):
+        return 0
+    if 'result' in rout:
+        a = rout['result']
+    else:
+        a = rout['name']
+    if a in rout['vars']:
+        return islong_double(rout['vars'][a])
+    return 0
+
+
+def iscomplexfunction(rout):
+    if not isfunction(rout):
+        return 0
+    if 'result' in rout:
+        a = rout['result']
+    else:
+        a = rout['name']
+    if a in rout['vars']:
+        return iscomplex(rout['vars'][a])
+    return 0
+
+
+def iscomplexfunction_warn(rout):
+    if iscomplexfunction(rout):
+        outmess("""\
+    **************************************************************
+        Warning: code with a function returning complex value
+        may not work correctly with your Fortran compiler.
+        When using GNU gcc/g77 compilers, codes should work
+        correctly for callbacks with:
+        f2py -c -DF2PY_CB_RETURNCOMPLEX
+    **************************************************************\n""")
+        return 1
+    return 0
+
+
+def isstringfunction(rout):
+    if not isfunction(rout):
+        return 0
+    if 'result' in rout:
+        a = rout['result']
+    else:
+        a = rout['name']
+    if a in rout['vars']:
+        return isstring(rout['vars'][a])
+    return 0
+
+
+def hasexternals(rout):
+    return 'externals' in rout and rout['externals']
+
+
+def isthreadsafe(rout):
+    return 'f2pyenhancements' in rout and \
+           'threadsafe' in rout['f2pyenhancements']
+
+
+def hasvariables(rout):
+    return 'vars' in rout and rout['vars']
+
+
+def isoptional(var):
+    return ('attrspec' in var and 'optional' in var['attrspec'] and
+            'required' not in var['attrspec']) and isintent_nothide(var)
+
+
+def isexternal(var):
+    return 'attrspec' in var and 'external' in var['attrspec']
+
+
+def getdimension(var):
+    dimpattern = r"\((.*?)\)"
+    if 'attrspec' in var.keys():
+        if any('dimension' in s for s in var['attrspec']):
+            return [re.findall(dimpattern, v) for v in var['attrspec']][0]
+
+
+def isrequired(var):
+    return not isoptional(var) and isintent_nothide(var)
+
+
+def isintent_in(var):
+    if 'intent' not in var:
+        return 1
+    if 'hide' in var['intent']:
+        return 0
+    if 'inplace' in var['intent']:
+        return 0
+    if 'in' in var['intent']:
+        return 1
+    if 'out' in var['intent']:
+        return 0
+    if 'inout' in var['intent']:
+        return 0
+    if 'outin' in var['intent']:
+        return 0
+    return 1
+
+
+def isintent_inout(var):
+    return ('intent' in var and ('inout' in var['intent'] or
+            'outin' in var['intent']) and 'in' not in var['intent'] and
+            'hide' not in var['intent'] and 'inplace' not in var['intent'])
+
+
+def isintent_out(var):
+    return 'out' in var.get('intent', [])
+
+
+def isintent_hide(var):
+    return ('intent' in var and ('hide' in var['intent'] or
+            ('out' in var['intent'] and 'in' not in var['intent'] and
+                (not l_or(isintent_inout, isintent_inplace)(var)))))
+
+
+def isintent_nothide(var):
+    return not isintent_hide(var)
+
+
+def isintent_c(var):
+    return 'c' in var.get('intent', [])
+
+
+def isintent_cache(var):
+    return 'cache' in var.get('intent', [])
+
+
+def isintent_copy(var):
+    return 'copy' in var.get('intent', [])
+
+
+def isintent_overwrite(var):
+    return 'overwrite' in var.get('intent', [])
+
+
+def isintent_callback(var):
+    return 'callback' in var.get('intent', [])
+
+
+def isintent_inplace(var):
+    return 'inplace' in var.get('intent', [])
+
+
+def isintent_aux(var):
+    return 'aux' in var.get('intent', [])
+
+
+def isintent_aligned4(var):
+    return 'aligned4' in var.get('intent', [])
+
+
+def isintent_aligned8(var):
+    return 'aligned8' in var.get('intent', [])
+
+
+def isintent_aligned16(var):
+    return 'aligned16' in var.get('intent', [])
+
+
+isintent_dict = {isintent_in: 'INTENT_IN', isintent_inout: 'INTENT_INOUT',
+                 isintent_out: 'INTENT_OUT', isintent_hide: 'INTENT_HIDE',
+                 isintent_cache: 'INTENT_CACHE',
+                 isintent_c: 'INTENT_C', isoptional: 'OPTIONAL',
+                 isintent_inplace: 'INTENT_INPLACE',
+                 isintent_aligned4: 'INTENT_ALIGNED4',
+                 isintent_aligned8: 'INTENT_ALIGNED8',
+                 isintent_aligned16: 'INTENT_ALIGNED16',
+                 }
+
+
+def isprivate(var):
+    return 'attrspec' in var and 'private' in var['attrspec']
+
+
+def hasinitvalue(var):
+    return '=' in var
+
+
+def hasinitvalueasstring(var):
+    if not hasinitvalue(var):
+        return 0
+    return var['='][0] in ['"', "'"]
+
+
+def hasnote(var):
+    return 'note' in var
+
+
+def hasresultnote(rout):
+    if not isfunction(rout):
+        return 0
+    if 'result' in rout:
+        a = rout['result']
+    else:
+        a = rout['name']
+    if a in rout['vars']:
+        return hasnote(rout['vars'][a])
+    return 0
+
+
+def hascommon(rout):
+    return 'common' in rout
+
+
+def containscommon(rout):
+    if hascommon(rout):
+        return 1
+    if hasbody(rout):
+        for b in rout['body']:
+            if containscommon(b):
+                return 1
+    return 0
+
+
+def containsmodule(block):
+    if ismodule(block):
+        return 1
+    if not hasbody(block):
+        return 0
+    for b in block['body']:
+        if containsmodule(b):
+            return 1
+    return 0
+
+
+def hasbody(rout):
+    return 'body' in rout
+
+
+def hascallstatement(rout):
+    return getcallstatement(rout) is not None
+
+
+def istrue(var):
+    return 1
+
+
+def isfalse(var):
+    return 0
+
+
+class F2PYError(Exception):
+    pass
+
+
+class throw_error:
+
+    def __init__(self, mess):
+        self.mess = mess
+
+    def __call__(self, var):
+        mess = '\n\n  var = %s\n  Message: %s\n' % (var, self.mess)
+        raise F2PYError(mess)
+
+
+def l_and(*f):
+    l1, l2 = 'lambda v', []
+    for i in range(len(f)):
+        l1 = '%s,f%d=f[%d]' % (l1, i, i)
+        l2.append('f%d(v)' % (i))
+    return eval('%s:%s' % (l1, ' and '.join(l2)))
+
+
+def l_or(*f):
+    l1, l2 = 'lambda v', []
+    for i in range(len(f)):
+        l1 = '%s,f%d=f[%d]' % (l1, i, i)
+        l2.append('f%d(v)' % (i))
+    return eval('%s:%s' % (l1, ' or '.join(l2)))
+
+
+def l_not(f):
+    return eval('lambda v,f=f:not f(v)')
+
+
+def isdummyroutine(rout):
+    try:
+        return rout['f2pyenhancements']['fortranname'] == ''
+    except KeyError:
+        return 0
+
+
+def getfortranname(rout):
+    try:
+        name = rout['f2pyenhancements']['fortranname']
+        if name == '':
+            raise KeyError
+        if not name:
+            errmess('Failed to use fortranname from %s\n' %
+                    (rout['f2pyenhancements']))
+            raise KeyError
+    except KeyError:
+        name = rout['name']
+    return name
+
+
+def getmultilineblock(rout, blockname, comment=1, counter=0):
+    try:
+        r = rout['f2pyenhancements'].get(blockname)
+    except KeyError:
+        return
+    if not r:
+        return
+    if counter > 0 and isinstance(r, str):
+        return
+    if isinstance(r, list):
+        if counter >= len(r):
+            return
+        r = r[counter]
+    if r[:3] == "'''":
+        if comment:
+            r = '\t/* start ' + blockname + \
+                ' multiline (' + repr(counter) + ') */\n' + r[3:]
+        else:
+            r = r[3:]
+        if r[-3:] == "'''":
+            if comment:
+                r = r[:-3] + '\n\t/* end multiline (' + repr(counter) + ')*/'
+            else:
+                r = r[:-3]
+        else:
+            errmess("%s multiline block should end with `'''`: %s\n"
+                    % (blockname, repr(r)))
+    return r
+
+
+def getcallstatement(rout):
+    return getmultilineblock(rout, 'callstatement')
+
+
+def getcallprotoargument(rout, cb_map={}):
+    r = getmultilineblock(rout, 'callprotoargument', comment=0)
+    if r:
+        return r
+    if hascallstatement(rout):
+        outmess(
+            'warning: callstatement is defined without callprotoargument\n')
+        return
+    from .capi_maps import getctype
+    arg_types, arg_types2 = [], []
+    if l_and(isstringfunction, l_not(isfunction_wrap))(rout):
+        arg_types.extend(['char*', 'size_t'])
+    for n in rout['args']:
+        var = rout['vars'][n]
+        if isintent_callback(var):
+            continue
+        if n in cb_map:
+            ctype = cb_map[n] + '_typedef'
+        else:
+            ctype = getctype(var)
+            if l_and(isintent_c, l_or(isscalar, iscomplex))(var):
+                pass
+            elif isstring(var):
+                pass
+            else:
+                if not isattr_value(var):
+                    ctype = ctype + '*'
+            if ((isstring(var)
+                 or isarrayofstrings(var)  # obsolete?
+                 or isstringarray(var))):
+                arg_types2.append('size_t')
+        arg_types.append(ctype)
+
+    proto_args = ','.join(arg_types + arg_types2)
+    if not proto_args:
+        proto_args = 'void'
+    return proto_args
+
+
+def getusercode(rout):
+    return getmultilineblock(rout, 'usercode')
+
+
+def getusercode1(rout):
+    return getmultilineblock(rout, 'usercode', counter=1)
+
+
+def getpymethoddef(rout):
+    return getmultilineblock(rout, 'pymethoddef')
+
+
+def getargs(rout):
+    sortargs, args = [], []
+    if 'args' in rout:
+        args = rout['args']
+        if 'sortvars' in rout:
+            for a in rout['sortvars']:
+                if a in args:
+                    sortargs.append(a)
+            for a in args:
+                if a not in sortargs:
+                    sortargs.append(a)
+        else:
+            sortargs = rout['args']
+    return args, sortargs
+
+
+def getargs2(rout):
+    sortargs, args = [], rout.get('args', [])
+    auxvars = [a for a in rout['vars'].keys() if isintent_aux(rout['vars'][a])
+               and a not in args]
+    args = auxvars + args
+    if 'sortvars' in rout:
+        for a in rout['sortvars']:
+            if a in args:
+                sortargs.append(a)
+        for a in args:
+            if a not in sortargs:
+                sortargs.append(a)
+    else:
+        sortargs = auxvars + rout['args']
+    return args, sortargs
+
+
+def getrestdoc(rout):
+    if 'f2pymultilines' not in rout:
+        return None
+    k = None
+    if rout['block'] == 'python module':
+        k = rout['block'], rout['name']
+    return rout['f2pymultilines'].get(k, None)
+
+
+def gentitle(name):
+    ln = (80 - len(name) - 6) // 2
+    return '/*%s %s %s*/' % (ln * '*', name, ln * '*')
+
+
+def flatlist(lst):
+    if isinstance(lst, list):
+        return reduce(lambda x, y, f=flatlist: x + f(y), lst, [])
+    return [lst]
+
+
+def stripcomma(s):
+    if s and s[-1] == ',':
+        return s[:-1]
+    return s
+
+
+def replace(str, d, defaultsep=''):
+    if isinstance(d, list):
+        return [replace(str, _m, defaultsep) for _m in d]
+    if isinstance(str, list):
+        return [replace(_m, d, defaultsep) for _m in str]
+    for k in 2 * list(d.keys()):
+        if k == 'separatorsfor':
+            continue
+        if 'separatorsfor' in d and k in d['separatorsfor']:
+            sep = d['separatorsfor'][k]
+        else:
+            sep = defaultsep
+        if isinstance(d[k], list):
+            str = str.replace('#%s#' % (k), sep.join(flatlist(d[k])))
+        else:
+            str = str.replace('#%s#' % (k), d[k])
+    return str
+
+
+def dictappend(rd, ar):
+    if isinstance(ar, list):
+        for a in ar:
+            rd = dictappend(rd, a)
+        return rd
+    for k in ar.keys():
+        if k[0] == '_':
+            continue
+        if k in rd:
+            if isinstance(rd[k], str):
+                rd[k] = [rd[k]]
+            if isinstance(rd[k], list):
+                if isinstance(ar[k], list):
+                    rd[k] = rd[k] + ar[k]
+                else:
+                    rd[k].append(ar[k])
+            elif isinstance(rd[k], dict):
+                if isinstance(ar[k], dict):
+                    if k == 'separatorsfor':
+                        for k1 in ar[k].keys():
+                            if k1 not in rd[k]:
+                                rd[k][k1] = ar[k][k1]
+                    else:
+                        rd[k] = dictappend(rd[k], ar[k])
+        else:
+            rd[k] = ar[k]
+    return rd
+
+
+def applyrules(rules, d, var={}):
+    ret = {}
+    if isinstance(rules, list):
+        for r in rules:
+            rr = applyrules(r, d, var)
+            ret = dictappend(ret, rr)
+            if '_break' in rr:
+                break
+        return ret
+    if '_check' in rules and (not rules['_check'](var)):
+        return ret
+    if 'need' in rules:
+        res = applyrules({'needs': rules['need']}, d, var)
+        if 'needs' in res:
+            cfuncs.append_needs(res['needs'])
+
+    for k in rules.keys():
+        if k == 'separatorsfor':
+            ret[k] = rules[k]
+            continue
+        if isinstance(rules[k], str):
+            ret[k] = replace(rules[k], d)
+        elif isinstance(rules[k], list):
+            ret[k] = []
+            for i in rules[k]:
+                ar = applyrules({k: i}, d, var)
+                if k in ar:
+                    ret[k].append(ar[k])
+        elif k[0] == '_':
+            continue
+        elif isinstance(rules[k], dict):
+            ret[k] = []
+            for k1 in rules[k].keys():
+                if isinstance(k1, types.FunctionType) and k1(var):
+                    if isinstance(rules[k][k1], list):
+                        for i in rules[k][k1]:
+                            if isinstance(i, dict):
+                                res = applyrules({'supertext': i}, d, var)
+                                if 'supertext' in res:
+                                    i = res['supertext']
+                                else:
+                                    i = ''
+                            ret[k].append(replace(i, d))
+                    else:
+                        i = rules[k][k1]
+                        if isinstance(i, dict):
+                            res = applyrules({'supertext': i}, d)
+                            if 'supertext' in res:
+                                i = res['supertext']
+                            else:
+                                i = ''
+                        ret[k].append(replace(i, d))
+        else:
+            errmess('applyrules: ignoring rule %s.\n' % repr(rules[k]))
+        if isinstance(ret[k], list):
+            if len(ret[k]) == 1:
+                ret[k] = ret[k][0]
+            if ret[k] == []:
+                del ret[k]
+    return ret
+
+_f2py_module_name_match = re.compile(r'\s*python\s*module\s*(?P<name>[\w_]+)',
+                                     re.I).match
+_f2py_user_module_name_match = re.compile(r'\s*python\s*module\s*(?P<name>[\w_]*?'
+                                          r'__user__[\w_]*)', re.I).match
+
+def get_f2py_modulename(source):
+    name = None
+    with open(source) as f:
+        for line in f:
+            m = _f2py_module_name_match(line)
+            if m:
+                if _f2py_user_module_name_match(line): # skip *__user__* names
+                    continue
+                name = m.group('name')
+                break
+    return name
+
+def getuseblocks(pymod):
+    all_uses = []
+    for inner in pymod['body']:
+        for modblock in inner['body']:
+            if modblock.get('use'):
+                all_uses.extend([x for x in modblock.get("use").keys() if "__" not in x])
+    return all_uses
+
+def process_f2cmap_dict(f2cmap_all, new_map, c2py_map, verbose = False):
+    """
+    Update the Fortran-to-C type mapping dictionary with new mappings and
+    return a list of successfully mapped C types.
+
+    This function integrates a new mapping dictionary into an existing
+    Fortran-to-C type mapping dictionary. It ensures that all keys are in
+    lowercase and validates new entries against a given C-to-Python mapping
+    dictionary. Redefinitions and invalid entries are reported with a warning.
+
+    Parameters
+    ----------
+    f2cmap_all : dict
+        The existing Fortran-to-C type mapping dictionary that will be updated.
+        It should be a dictionary of dictionaries where the main keys represent
+        Fortran types and the nested dictionaries map Fortran type specifiers
+        to corresponding C types.
+
+    new_map : dict
+        A dictionary containing new type mappings to be added to `f2cmap_all`.
+        The structure should be similar to `f2cmap_all`, with keys representing
+        Fortran types and values being dictionaries of type specifiers and their
+        C type equivalents.
+
+    c2py_map : dict
+        A dictionary used for validating the C types in `new_map`. It maps C
+        types to corresponding Python types and is used to ensure that the C
+        types specified in `new_map` are valid.
+
+    verbose : boolean
+        A flag used to provide information about the types mapped
+
+    Returns
+    -------
+    tuple of (dict, list)
+        The updated Fortran-to-C type mapping dictionary and a list of
+        successfully mapped C types.
+    """
+    f2cmap_mapped = []
+
+    new_map_lower = {}
+    for k, d1 in new_map.items():
+        d1_lower = {k1.lower(): v1 for k1, v1 in d1.items()}
+        new_map_lower[k.lower()] = d1_lower
+
+    for k, d1 in new_map_lower.items():
+        if k not in f2cmap_all:
+            f2cmap_all[k] = {}
+
+        for k1, v1 in d1.items():
+            if v1 in c2py_map:
+                if k1 in f2cmap_all[k]:
+                    outmess(
+                        "\tWarning: redefinition of {'%s':{'%s':'%s'->'%s'}}\n"
+                        % (k, k1, f2cmap_all[k][k1], v1)
+                    )
+                f2cmap_all[k][k1] = v1
+                if verbose:
+                    outmess('\tMapping "%s(kind=%s)" to "%s"\n' % (k, k1, v1))
+                f2cmap_mapped.append(v1)
+            else:
+                if verbose:
+                    errmess(
+                        "\tIgnoring map {'%s':{'%s':'%s'}}: '%s' must be in %s\n"
+                        % (k, k1, v1, v1, list(c2py_map.keys()))
+                    )
+
+    return f2cmap_all, f2cmap_mapped
diff --git a/.venv/lib/python3.12/site-packages/numpy/f2py/capi_maps.py b/.venv/lib/python3.12/site-packages/numpy/f2py/capi_maps.py
new file mode 100644
index 00000000..fa477a5b
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/numpy/f2py/capi_maps.py
@@ -0,0 +1,819 @@
+"""
+Copyright 1999 -- 2011 Pearu Peterson all rights reserved.
+Copyright 2011 -- present NumPy Developers.
+Permission to use, modify, and distribute this software is given under the
+terms of the NumPy License.
+
+NO WARRANTY IS EXPRESSED OR IMPLIED.  USE AT YOUR OWN RISK.
+"""
+from . import __version__
+f2py_version = __version__.version
+
+import copy
+import re
+import os
+from .crackfortran import markoutercomma
+from . import cb_rules
+from ._isocbind import iso_c_binding_map, isoc_c2pycode_map, iso_c2py_map
+
+# The environment provided by auxfuncs.py is needed for some calls to eval.
+# As the needed functions cannot be determined by static inspection of the
+# code, it is safest to use import * pending a major refactoring of f2py.
+from .auxfuncs import *
+
+__all__ = [
+    'getctype', 'getstrlength', 'getarrdims', 'getpydocsign',
+    'getarrdocsign', 'getinit', 'sign2map', 'routsign2map', 'modsign2map',
+    'cb_sign2map', 'cb_routsign2map', 'common_sign2map', 'process_f2cmap_dict'
+]
+
+
+depargs = []
+lcb_map = {}
+lcb2_map = {}
+# forced casting: mainly caused by the fact that Python or Numeric
+#                 C/APIs do not support the corresponding C types.
+c2py_map = {'double': 'float',
+            'float': 'float',                          # forced casting
+            'long_double': 'float',                    # forced casting
+            'char': 'int',                             # forced casting
+            'signed_char': 'int',                      # forced casting
+            'unsigned_char': 'int',                    # forced casting
+            'short': 'int',                            # forced casting
+            'unsigned_short': 'int',                   # forced casting
+            'int': 'int',                              # forced casting
+            'long': 'int',
+            'long_long': 'long',
+            'unsigned': 'int',                         # forced casting
+            'complex_float': 'complex',                # forced casting
+            'complex_double': 'complex',
+            'complex_long_double': 'complex',          # forced casting
+            'string': 'string',
+            'character': 'bytes',
+            }
+
+c2capi_map = {'double': 'NPY_DOUBLE',
+                'float': 'NPY_FLOAT',
+                'long_double': 'NPY_LONGDOUBLE',
+                'char': 'NPY_BYTE',
+                'unsigned_char': 'NPY_UBYTE',
+                'signed_char': 'NPY_BYTE',
+                'short': 'NPY_SHORT',
+                'unsigned_short': 'NPY_USHORT',
+                'int': 'NPY_INT',
+                'unsigned': 'NPY_UINT',
+                'long': 'NPY_LONG',
+                'unsigned_long': 'NPY_ULONG',
+                'long_long': 'NPY_LONGLONG',
+                'unsigned_long_long': 'NPY_ULONGLONG',
+                'complex_float': 'NPY_CFLOAT',
+                'complex_double': 'NPY_CDOUBLE',
+                'complex_long_double': 'NPY_CDOUBLE',
+                'string': 'NPY_STRING',
+                'character': 'NPY_STRING'}
+
+c2pycode_map = {'double': 'd',
+                'float': 'f',
+                'long_double': 'g',
+                'char': 'b',
+                'unsigned_char': 'B',
+                'signed_char': 'b',
+                'short': 'h',
+                'unsigned_short': 'H',
+                'int': 'i',
+                'unsigned': 'I',
+                'long': 'l',
+                'unsigned_long': 'L',
+                'long_long': 'q',
+                'unsigned_long_long': 'Q',
+                'complex_float': 'F',
+                'complex_double': 'D',
+                'complex_long_double': 'G',
+                'string': 'S',
+                'character': 'c'}
+
+# https://docs.python.org/3/c-api/arg.html#building-values
+c2buildvalue_map = {'double': 'd',
+                    'float': 'f',
+                    'char': 'b',
+                    'signed_char': 'b',
+                    'short': 'h',
+                    'int': 'i',
+                    'long': 'l',
+                    'long_long': 'L',
+                    'complex_float': 'N',
+                    'complex_double': 'N',
+                    'complex_long_double': 'N',
+                    'string': 'y',
+                    'character': 'c'}
+
+f2cmap_all = {'real': {'': 'float', '4': 'float', '8': 'double',
+                       '12': 'long_double', '16': 'long_double'},
+              'integer': {'': 'int', '1': 'signed_char', '2': 'short',
+                          '4': 'int', '8': 'long_long',
+                          '-1': 'unsigned_char', '-2': 'unsigned_short',
+                          '-4': 'unsigned', '-8': 'unsigned_long_long'},
+              'complex': {'': 'complex_float', '8': 'complex_float',
+                          '16': 'complex_double', '24': 'complex_long_double',
+                          '32': 'complex_long_double'},
+              'complexkind': {'': 'complex_float', '4': 'complex_float',
+                              '8': 'complex_double', '12': 'complex_long_double',
+                              '16': 'complex_long_double'},
+              'logical': {'': 'int', '1': 'char', '2': 'short', '4': 'int',
+                          '8': 'long_long'},
+              'double complex': {'': 'complex_double'},
+              'double precision': {'': 'double'},
+              'byte': {'': 'char'},
+              }
+
+# Add ISO_C handling
+c2pycode_map.update(isoc_c2pycode_map)
+c2py_map.update(iso_c2py_map)
+f2cmap_all, _ = process_f2cmap_dict(f2cmap_all, iso_c_binding_map, c2py_map)
+# End ISO_C handling
+f2cmap_default = copy.deepcopy(f2cmap_all)
+
+f2cmap_mapped = []
+
+def load_f2cmap_file(f2cmap_file):
+    global f2cmap_all, f2cmap_mapped
+
+    f2cmap_all = copy.deepcopy(f2cmap_default)
+
+    if f2cmap_file is None:
+        # Default value
+        f2cmap_file = '.f2py_f2cmap'
+        if not os.path.isfile(f2cmap_file):
+            return
+
+    # User defined additions to f2cmap_all.
+    # f2cmap_file must contain a dictionary of dictionaries, only. For
+    # example, {'real':{'low':'float'}} means that Fortran 'real(low)' is
+    # interpreted as C 'float'. This feature is useful for F90/95 users if
+    # they use PARAMETERS in type specifications.
+    try:
+        outmess('Reading f2cmap from {!r} ...\n'.format(f2cmap_file))
+        with open(f2cmap_file) as f:
+            d = eval(f.read().lower(), {}, {})
+        f2cmap_all, f2cmap_mapped = process_f2cmap_dict(f2cmap_all, d, c2py_map, True)
+        outmess('Successfully applied user defined f2cmap changes\n')
+    except Exception as msg:
+        errmess('Failed to apply user defined f2cmap changes: %s. Skipping.\n' % (msg))
+
+
+cformat_map = {'double': '%g',
+               'float': '%g',
+               'long_double': '%Lg',
+               'char': '%d',
+               'signed_char': '%d',
+               'unsigned_char': '%hhu',
+               'short': '%hd',
+               'unsigned_short': '%hu',
+               'int': '%d',
+               'unsigned': '%u',
+               'long': '%ld',
+               'unsigned_long': '%lu',
+               'long_long': '%ld',
+               'complex_float': '(%g,%g)',
+               'complex_double': '(%g,%g)',
+               'complex_long_double': '(%Lg,%Lg)',
+               'string': '\\"%s\\"',
+               'character': "'%c'",
+               }
+
+# Auxiliary functions
+
+
+def getctype(var):
+    """
+    Determines C type
+    """
+    ctype = 'void'
+    if isfunction(var):
+        if 'result' in var:
+            a = var['result']
+        else:
+            a = var['name']
+        if a in var['vars']:
+            return getctype(var['vars'][a])
+        else:
+            errmess('getctype: function %s has no return value?!\n' % a)
+    elif issubroutine(var):
+        return ctype
+    elif ischaracter_or_characterarray(var):
+        return 'character'
+    elif isstring_or_stringarray(var):
+        return 'string'
+    elif 'typespec' in var and var['typespec'].lower() in f2cmap_all:
+        typespec = var['typespec'].lower()
+        f2cmap = f2cmap_all[typespec]
+        ctype = f2cmap['']  # default type
+        if 'kindselector' in var:
+            if '*' in var['kindselector']:
+                try:
+                    ctype = f2cmap[var['kindselector']['*']]
+                except KeyError:
+                    errmess('getctype: "%s %s %s" not supported.\n' %
+                            (var['typespec'], '*', var['kindselector']['*']))
+            elif 'kind' in var['kindselector']:
+                if typespec + 'kind' in f2cmap_all:
+                    f2cmap = f2cmap_all[typespec + 'kind']
+                try:
+                    ctype = f2cmap[var['kindselector']['kind']]
+                except KeyError:
+                    if typespec in f2cmap_all:
+                        f2cmap = f2cmap_all[typespec]
+                    try:
+                        ctype = f2cmap[str(var['kindselector']['kind'])]
+                    except KeyError:
+                        errmess('getctype: "%s(kind=%s)" is mapped to C "%s" (to override define dict(%s = dict(%s="<C typespec>")) in %s/.f2py_f2cmap file).\n'
+                                % (typespec, var['kindselector']['kind'], ctype,
+                                   typespec, var['kindselector']['kind'], os.getcwd()))
+    else:
+        if not isexternal(var):
+            errmess('getctype: No C-type found in "%s", assuming void.\n' % var)
+    return ctype
+
+
+def f2cexpr(expr):
+    """Rewrite Fortran expression as f2py supported C expression.
+
+    Due to the lack of a proper expression parser in f2py, this
+    function uses a heuristic approach that assumes that Fortran
+    arithmetic expressions are valid C arithmetic expressions when
+    mapping Fortran function calls to the corresponding C function/CPP
+    macros calls.
+
+    """
+    # TODO: support Fortran `len` function with optional kind parameter
+    expr = re.sub(r'\blen\b', 'f2py_slen', expr)
+    return expr
+
+
+def getstrlength(var):
+    if isstringfunction(var):
+        if 'result' in var:
+            a = var['result']
+        else:
+            a = var['name']
+        if a in var['vars']:
+            return getstrlength(var['vars'][a])
+        else:
+            errmess('getstrlength: function %s has no return value?!\n' % a)
+    if not isstring(var):
+        errmess(
+            'getstrlength: expected a signature of a string but got: %s\n' % (repr(var)))
+    len = '1'
+    if 'charselector' in var:
+        a = var['charselector']
+        if '*' in a:
+            len = a['*']
+        elif 'len' in a:
+            len = f2cexpr(a['len'])
+    if re.match(r'\(\s*(\*|:)\s*\)', len) or re.match(r'(\*|:)', len):
+        if isintent_hide(var):
+            errmess('getstrlength:intent(hide): expected a string with defined length but got: %s\n' % (
+                repr(var)))
+        len = '-1'
+    return len
+
+
+def getarrdims(a, var, verbose=0):
+    ret = {}
+    if isstring(var) and not isarray(var):
+        ret['size'] = getstrlength(var)
+        ret['rank'] = '0'
+        ret['dims'] = ''
+    elif isscalar(var):
+        ret['size'] = '1'
+        ret['rank'] = '0'
+        ret['dims'] = ''
+    elif isarray(var):
+        dim = copy.copy(var['dimension'])
+        ret['size'] = '*'.join(dim)
+        try:
+            ret['size'] = repr(eval(ret['size']))
+        except Exception:
+            pass
+        ret['dims'] = ','.join(dim)
+        ret['rank'] = repr(len(dim))
+        ret['rank*[-1]'] = repr(len(dim) * [-1])[1:-1]
+        for i in range(len(dim)):  # solve dim for dependencies
+            v = []
+            if dim[i] in depargs:
+                v = [dim[i]]
+            else:
+                for va in depargs:
+                    if re.match(r'.*?\b%s\b.*' % va, dim[i]):
+                        v.append(va)
+            for va in v:
+                if depargs.index(va) > depargs.index(a):
+                    dim[i] = '*'
+                    break
+        ret['setdims'], i = '', -1
+        for d in dim:
+            i = i + 1
+            if d not in ['*', ':', '(*)', '(:)']:
+                ret['setdims'] = '%s#varname#_Dims[%d]=%s,' % (
+                    ret['setdims'], i, d)
+        if ret['setdims']:
+            ret['setdims'] = ret['setdims'][:-1]
+        ret['cbsetdims'], i = '', -1
+        for d in var['dimension']:
+            i = i + 1
+            if d not in ['*', ':', '(*)', '(:)']:
+                ret['cbsetdims'] = '%s#varname#_Dims[%d]=%s,' % (
+                    ret['cbsetdims'], i, d)
+            elif isintent_in(var):
+                outmess('getarrdims:warning: assumed shape array, using 0 instead of %r\n'
+                        % (d))
+                ret['cbsetdims'] = '%s#varname#_Dims[%d]=%s,' % (
+                    ret['cbsetdims'], i, 0)
+            elif verbose:
+                errmess(
+                    'getarrdims: If in call-back function: array argument %s must have bounded dimensions: got %s\n' % (repr(a), repr(d)))
+        if ret['cbsetdims']:
+            ret['cbsetdims'] = ret['cbsetdims'][:-1]
+#         if not isintent_c(var):
+#             var['dimension'].reverse()
+    return ret
+
+
+def getpydocsign(a, var):
+    global lcb_map
+    if isfunction(var):
+        if 'result' in var:
+            af = var['result']
+        else:
+            af = var['name']
+        if af in var['vars']:
+            return getpydocsign(af, var['vars'][af])
+        else:
+            errmess('getctype: function %s has no return value?!\n' % af)
+        return '', ''
+    sig, sigout = a, a
+    opt = ''
+    if isintent_in(var):
+        opt = 'input'
+    elif isintent_inout(var):
+        opt = 'in/output'
+    out_a = a
+    if isintent_out(var):
+        for k in var['intent']:
+            if k[:4] == 'out=':
+                out_a = k[4:]
+                break
+    init = ''
+    ctype = getctype(var)
+
+    if hasinitvalue(var):
+        init, showinit = getinit(a, var)
+        init = ', optional\\n    Default: %s' % showinit
+    if isscalar(var):
+        if isintent_inout(var):
+            sig = '%s : %s rank-0 array(%s,\'%s\')%s' % (a, opt, c2py_map[ctype],
+                                                         c2pycode_map[ctype], init)
+        else:
+            sig = '%s : %s %s%s' % (a, opt, c2py_map[ctype], init)
+        sigout = '%s : %s' % (out_a, c2py_map[ctype])
+    elif isstring(var):
+        if isintent_inout(var):
+            sig = '%s : %s rank-0 array(string(len=%s),\'c\')%s' % (
+                a, opt, getstrlength(var), init)
+        else:
+            sig = '%s : %s string(len=%s)%s' % (
+                a, opt, getstrlength(var), init)
+        sigout = '%s : string(len=%s)' % (out_a, getstrlength(var))
+    elif isarray(var):
+        dim = var['dimension']
+        rank = repr(len(dim))
+        sig = '%s : %s rank-%s array(\'%s\') with bounds (%s)%s' % (a, opt, rank,
+                                                                    c2pycode_map[
+                                                                        ctype],
+                                                                    ','.join(dim), init)
+        if a == out_a:
+            sigout = '%s : rank-%s array(\'%s\') with bounds (%s)'\
+                % (a, rank, c2pycode_map[ctype], ','.join(dim))
+        else:
+            sigout = '%s : rank-%s array(\'%s\') with bounds (%s) and %s storage'\
+                % (out_a, rank, c2pycode_map[ctype], ','.join(dim), a)
+    elif isexternal(var):
+        ua = ''
+        if a in lcb_map and lcb_map[a] in lcb2_map and 'argname' in lcb2_map[lcb_map[a]]:
+            ua = lcb2_map[lcb_map[a]]['argname']
+            if not ua == a:
+                ua = ' => %s' % ua
+            else:
+                ua = ''
+        sig = '%s : call-back function%s' % (a, ua)
+        sigout = sig
+    else:
+        errmess(
+            'getpydocsign: Could not resolve docsignature for "%s".\n' % a)
+    return sig, sigout
+
+
+def getarrdocsign(a, var):
+    ctype = getctype(var)
+    if isstring(var) and (not isarray(var)):
+        sig = '%s : rank-0 array(string(len=%s),\'c\')' % (a,
+                                                           getstrlength(var))
+    elif isscalar(var):
+        sig = '%s : rank-0 array(%s,\'%s\')' % (a, c2py_map[ctype],
+                                                c2pycode_map[ctype],)
+    elif isarray(var):
+        dim = var['dimension']
+        rank = repr(len(dim))
+        sig = '%s : rank-%s array(\'%s\') with bounds (%s)' % (a, rank,
+                                                               c2pycode_map[
+                                                                   ctype],
+                                                               ','.join(dim))
+    return sig
+
+
+def getinit(a, var):
+    if isstring(var):
+        init, showinit = '""', "''"
+    else:
+        init, showinit = '', ''
+    if hasinitvalue(var):
+        init = var['=']
+        showinit = init
+        if iscomplex(var) or iscomplexarray(var):
+            ret = {}
+
+            try:
+                v = var["="]
+                if ',' in v:
+                    ret['init.r'], ret['init.i'] = markoutercomma(
+                        v[1:-1]).split('@,@')
+                else:
+                    v = eval(v, {}, {})
+                    ret['init.r'], ret['init.i'] = str(v.real), str(v.imag)
+            except Exception:
+                raise ValueError(
+                    'getinit: expected complex number `(r,i)\' but got `%s\' as initial value of %r.' % (init, a))
+            if isarray(var):
+                init = '(capi_c.r=%s,capi_c.i=%s,capi_c)' % (
+                    ret['init.r'], ret['init.i'])
+        elif isstring(var):
+            if not init:
+                init, showinit = '""', "''"
+            if init[0] == "'":
+                init = '"%s"' % (init[1:-1].replace('"', '\\"'))
+            if init[0] == '"':
+                showinit = "'%s'" % (init[1:-1])
+    return init, showinit
+
+
+def get_elsize(var):
+    if isstring(var) or isstringarray(var):
+        elsize = getstrlength(var)
+        # override with user-specified length when available:
+        elsize = var['charselector'].get('f2py_len', elsize)
+        return elsize
+    if ischaracter(var) or ischaracterarray(var):
+        return '1'
+    # for numerical types, PyArray_New* functions ignore specified
+    # elsize, so we just return 1 and let elsize be determined at
+    # runtime, see fortranobject.c
+    return '1'
+
+
+def sign2map(a, var):
+    """
+    varname,ctype,atype
+    init,init.r,init.i,pytype
+    vardebuginfo,vardebugshowvalue,varshowvalue
+    varrformat
+
+    intent
+    """
+    out_a = a
+    if isintent_out(var):
+        for k in var['intent']:
+            if k[:4] == 'out=':
+                out_a = k[4:]
+                break
+    ret = {'varname': a, 'outvarname': out_a, 'ctype': getctype(var)}
+    intent_flags = []
+    for f, s in isintent_dict.items():
+        if f(var):
+            intent_flags.append('F2PY_%s' % s)
+    if intent_flags:
+        # TODO: Evaluate intent_flags here.
+        ret['intent'] = '|'.join(intent_flags)
+    else:
+        ret['intent'] = 'F2PY_INTENT_IN'
+    if isarray(var):
+        ret['varrformat'] = 'N'
+    elif ret['ctype'] in c2buildvalue_map:
+        ret['varrformat'] = c2buildvalue_map[ret['ctype']]
+    else:
+        ret['varrformat'] = 'O'
+    ret['init'], ret['showinit'] = getinit(a, var)
+    if hasinitvalue(var) and iscomplex(var) and not isarray(var):
+        ret['init.r'], ret['init.i'] = markoutercomma(
+            ret['init'][1:-1]).split('@,@')
+    if isexternal(var):
+        ret['cbnamekey'] = a
+        if a in lcb_map:
+            ret['cbname'] = lcb_map[a]
+            ret['maxnofargs'] = lcb2_map[lcb_map[a]]['maxnofargs']
+            ret['nofoptargs'] = lcb2_map[lcb_map[a]]['nofoptargs']
+            ret['cbdocstr'] = lcb2_map[lcb_map[a]]['docstr']
+            ret['cblatexdocstr'] = lcb2_map[lcb_map[a]]['latexdocstr']
+        else:
+            ret['cbname'] = a
+            errmess('sign2map: Confused: external %s is not in lcb_map%s.\n' % (
+                a, list(lcb_map.keys())))
+    if isstring(var):
+        ret['length'] = getstrlength(var)
+    if isarray(var):
+        ret = dictappend(ret, getarrdims(a, var))
+        dim = copy.copy(var['dimension'])
+    if ret['ctype'] in c2capi_map:
+        ret['atype'] = c2capi_map[ret['ctype']]
+        ret['elsize'] = get_elsize(var)
+    # Debug info
+    if debugcapi(var):
+        il = [isintent_in, 'input', isintent_out, 'output',
+              isintent_inout, 'inoutput', isrequired, 'required',
+              isoptional, 'optional', isintent_hide, 'hidden',
+              iscomplex, 'complex scalar',
+              l_and(isscalar, l_not(iscomplex)), 'scalar',
+              isstring, 'string', isarray, 'array',
+              iscomplexarray, 'complex array', isstringarray, 'string array',
+              iscomplexfunction, 'complex function',
+              l_and(isfunction, l_not(iscomplexfunction)), 'function',
+              isexternal, 'callback',
+              isintent_callback, 'callback',
+              isintent_aux, 'auxiliary',
+              ]
+        rl = []
+        for i in range(0, len(il), 2):
+            if il[i](var):
+                rl.append(il[i + 1])
+        if isstring(var):
+            rl.append('slen(%s)=%s' % (a, ret['length']))
+        if isarray(var):
+            ddim = ','.join(
+                map(lambda x, y: '%s|%s' % (x, y), var['dimension'], dim))
+            rl.append('dims(%s)' % ddim)
+        if isexternal(var):
+            ret['vardebuginfo'] = 'debug-capi:%s=>%s:%s' % (
+                a, ret['cbname'], ','.join(rl))
+        else:
+            ret['vardebuginfo'] = 'debug-capi:%s %s=%s:%s' % (
+                ret['ctype'], a, ret['showinit'], ','.join(rl))
+        if isscalar(var):
+            if ret['ctype'] in cformat_map:
+                ret['vardebugshowvalue'] = 'debug-capi:%s=%s' % (
+                    a, cformat_map[ret['ctype']])
+        if isstring(var):
+            ret['vardebugshowvalue'] = 'debug-capi:slen(%s)=%%d %s=\\"%%s\\"' % (
+                a, a)
+        if isexternal(var):
+            ret['vardebugshowvalue'] = 'debug-capi:%s=%%p' % (a)
+    if ret['ctype'] in cformat_map:
+        ret['varshowvalue'] = '#name#:%s=%s' % (a, cformat_map[ret['ctype']])
+        ret['showvalueformat'] = '%s' % (cformat_map[ret['ctype']])
+    if isstring(var):
+        ret['varshowvalue'] = '#name#:slen(%s)=%%d %s=\\"%%s\\"' % (a, a)
+    ret['pydocsign'], ret['pydocsignout'] = getpydocsign(a, var)
+    if hasnote(var):
+        ret['note'] = var['note']
+    return ret
+
+
+def routsign2map(rout):
+    """
+    name,NAME,begintitle,endtitle
+    rname,ctype,rformat
+    routdebugshowvalue
+    """
+    global lcb_map
+    name = rout['name']
+    fname = getfortranname(rout)
+    ret = {'name': name,
+           'texname': name.replace('_', '\\_'),
+           'name_lower': name.lower(),
+           'NAME': name.upper(),
+           'begintitle': gentitle(name),
+           'endtitle': gentitle('end of %s' % name),
+           'fortranname': fname,
+           'FORTRANNAME': fname.upper(),
+           'callstatement': getcallstatement(rout) or '',
+           'usercode': getusercode(rout) or '',
+           'usercode1': getusercode1(rout) or '',
+           }
+    if '_' in fname:
+        ret['F_FUNC'] = 'F_FUNC_US'
+    else:
+        ret['F_FUNC'] = 'F_FUNC'
+    if '_' in name:
+        ret['F_WRAPPEDFUNC'] = 'F_WRAPPEDFUNC_US'
+    else:
+        ret['F_WRAPPEDFUNC'] = 'F_WRAPPEDFUNC'
+    lcb_map = {}
+    if 'use' in rout:
+        for u in rout['use'].keys():
+            if u in cb_rules.cb_map:
+                for un in cb_rules.cb_map[u]:
+                    ln = un[0]
+                    if 'map' in rout['use'][u]:
+                        for k in rout['use'][u]['map'].keys():
+                            if rout['use'][u]['map'][k] == un[0]:
+                                ln = k
+                                break
+                    lcb_map[ln] = un[1]
+    elif 'externals' in rout and rout['externals']:
+        errmess('routsign2map: Confused: function %s has externals %s but no "use" statement.\n' % (
+            ret['name'], repr(rout['externals'])))
+    ret['callprotoargument'] = getcallprotoargument(rout, lcb_map) or ''
+    if isfunction(rout):
+        if 'result' in rout:
+            a = rout['result']
+        else:
+            a = rout['name']
+        ret['rname'] = a
+        ret['pydocsign'], ret['pydocsignout'] = getpydocsign(a, rout)
+        ret['ctype'] = getctype(rout['vars'][a])
+        if hasresultnote(rout):
+            ret['resultnote'] = rout['vars'][a]['note']
+            rout['vars'][a]['note'] = ['See elsewhere.']
+        if ret['ctype'] in c2buildvalue_map:
+            ret['rformat'] = c2buildvalue_map[ret['ctype']]
+        else:
+            ret['rformat'] = 'O'
+            errmess('routsign2map: no c2buildvalue key for type %s\n' %
+                    (repr(ret['ctype'])))
+        if debugcapi(rout):
+            if ret['ctype'] in cformat_map:
+                ret['routdebugshowvalue'] = 'debug-capi:%s=%s' % (
+                    a, cformat_map[ret['ctype']])
+            if isstringfunction(rout):
+                ret['routdebugshowvalue'] = 'debug-capi:slen(%s)=%%d %s=\\"%%s\\"' % (
+                    a, a)
+        if isstringfunction(rout):
+            ret['rlength'] = getstrlength(rout['vars'][a])
+            if ret['rlength'] == '-1':
+                errmess('routsign2map: expected explicit specification of the length of the string returned by the fortran function %s; taking 10.\n' % (
+                    repr(rout['name'])))
+                ret['rlength'] = '10'
+    if hasnote(rout):
+        ret['note'] = rout['note']
+        rout['note'] = ['See elsewhere.']
+    return ret
+
+
+def modsign2map(m):
+    """
+    modulename
+    """
+    if ismodule(m):
+        ret = {'f90modulename': m['name'],
+               'F90MODULENAME': m['name'].upper(),
+               'texf90modulename': m['name'].replace('_', '\\_')}
+    else:
+        ret = {'modulename': m['name'],
+               'MODULENAME': m['name'].upper(),
+               'texmodulename': m['name'].replace('_', '\\_')}
+    ret['restdoc'] = getrestdoc(m) or []
+    if hasnote(m):
+        ret['note'] = m['note']
+    ret['usercode'] = getusercode(m) or ''
+    ret['usercode1'] = getusercode1(m) or ''
+    if m['body']:
+        ret['interface_usercode'] = getusercode(m['body'][0]) or ''
+    else:
+        ret['interface_usercode'] = ''
+    ret['pymethoddef'] = getpymethoddef(m) or ''
+    if 'coutput' in m:
+        ret['coutput'] = m['coutput']
+    if 'f2py_wrapper_output' in m:
+        ret['f2py_wrapper_output'] = m['f2py_wrapper_output']
+    return ret
+
+
+def cb_sign2map(a, var, index=None):
+    ret = {'varname': a}
+    ret['varname_i'] = ret['varname']
+    ret['ctype'] = getctype(var)
+    if ret['ctype'] in c2capi_map:
+        ret['atype'] = c2capi_map[ret['ctype']]
+        ret['elsize'] = get_elsize(var)
+    if ret['ctype'] in cformat_map:
+        ret['showvalueformat'] = '%s' % (cformat_map[ret['ctype']])
+    if isarray(var):
+        ret = dictappend(ret, getarrdims(a, var))
+    ret['pydocsign'], ret['pydocsignout'] = getpydocsign(a, var)
+    if hasnote(var):
+        ret['note'] = var['note']
+        var['note'] = ['See elsewhere.']
+    return ret
+
+
+def cb_routsign2map(rout, um):
+    """
+    name,begintitle,endtitle,argname
+    ctype,rctype,maxnofargs,nofoptargs,returncptr
+    """
+    ret = {'name': 'cb_%s_in_%s' % (rout['name'], um),
+           'returncptr': ''}
+    if isintent_callback(rout):
+        if '_' in rout['name']:
+            F_FUNC = 'F_FUNC_US'
+        else:
+            F_FUNC = 'F_FUNC'
+        ret['callbackname'] = '%s(%s,%s)' \
+                              % (F_FUNC,
+                                 rout['name'].lower(),
+                                 rout['name'].upper(),
+                                 )
+        ret['static'] = 'extern'
+    else:
+        ret['callbackname'] = ret['name']
+        ret['static'] = 'static'
+    ret['argname'] = rout['name']
+    ret['begintitle'] = gentitle(ret['name'])
+    ret['endtitle'] = gentitle('end of %s' % ret['name'])
+    ret['ctype'] = getctype(rout)
+    ret['rctype'] = 'void'
+    if ret['ctype'] == 'string':
+        ret['rctype'] = 'void'
+    else:
+        ret['rctype'] = ret['ctype']
+    if ret['rctype'] != 'void':
+        if iscomplexfunction(rout):
+            ret['returncptr'] = """
+#ifdef F2PY_CB_RETURNCOMPLEX
+return_value=
+#endif
+"""
+        else:
+            ret['returncptr'] = 'return_value='
+    if ret['ctype'] in cformat_map:
+        ret['showvalueformat'] = '%s' % (cformat_map[ret['ctype']])
+    if isstringfunction(rout):
+        ret['strlength'] = getstrlength(rout)
+    if isfunction(rout):
+        if 'result' in rout:
+            a = rout['result']
+        else:
+            a = rout['name']
+        if hasnote(rout['vars'][a]):
+            ret['note'] = rout['vars'][a]['note']
+            rout['vars'][a]['note'] = ['See elsewhere.']
+        ret['rname'] = a
+        ret['pydocsign'], ret['pydocsignout'] = getpydocsign(a, rout)
+        if iscomplexfunction(rout):
+            ret['rctype'] = """
+#ifdef F2PY_CB_RETURNCOMPLEX
+#ctype#
+#else
+void
+#endif
+"""
+    else:
+        if hasnote(rout):
+            ret['note'] = rout['note']
+            rout['note'] = ['See elsewhere.']
+    nofargs = 0
+    nofoptargs = 0
+    if 'args' in rout and 'vars' in rout:
+        for a in rout['args']:
+            var = rout['vars'][a]
+            if l_or(isintent_in, isintent_inout)(var):
+                nofargs = nofargs + 1
+                if isoptional(var):
+                    nofoptargs = nofoptargs + 1
+    ret['maxnofargs'] = repr(nofargs)
+    ret['nofoptargs'] = repr(nofoptargs)
+    if hasnote(rout) and isfunction(rout) and 'result' in rout:
+        ret['routnote'] = rout['note']
+        rout['note'] = ['See elsewhere.']
+    return ret
+
+
+def common_sign2map(a, var):  # obsolute
+    ret = {'varname': a, 'ctype': getctype(var)}
+    if isstringarray(var):
+        ret['ctype'] = 'char'
+    if ret['ctype'] in c2capi_map:
+        ret['atype'] = c2capi_map[ret['ctype']]
+        ret['elsize'] = get_elsize(var)
+    if ret['ctype'] in cformat_map:
+        ret['showvalueformat'] = '%s' % (cformat_map[ret['ctype']])
+    if isarray(var):
+        ret = dictappend(ret, getarrdims(a, var))
+    elif isstring(var):
+        ret['size'] = getstrlength(var)
+        ret['rank'] = '1'
+    ret['pydocsign'], ret['pydocsignout'] = getpydocsign(a, var)
+    if hasnote(var):
+        ret['note'] = var['note']
+        var['note'] = ['See elsewhere.']
+    # for strings this returns 0-rank but actually is 1-rank
+    ret['arrdocstr'] = getarrdocsign(a, var)
+    return ret
diff --git a/.venv/lib/python3.12/site-packages/numpy/f2py/cb_rules.py b/.venv/lib/python3.12/site-packages/numpy/f2py/cb_rules.py
new file mode 100644
index 00000000..721e075b
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/numpy/f2py/cb_rules.py
@@ -0,0 +1,644 @@
+"""
+Build call-back mechanism for f2py2e.
+
+Copyright 1999 -- 2011 Pearu Peterson all rights reserved.
+Copyright 2011 -- present NumPy Developers.
+Permission to use, modify, and distribute this software is given under the
+terms of the NumPy License.
+
+NO WARRANTY IS EXPRESSED OR IMPLIED.  USE AT YOUR OWN RISK.
+"""
+from . import __version__
+from .auxfuncs import (
+    applyrules, debugcapi, dictappend, errmess, getargs, hasnote, isarray,
+    iscomplex, iscomplexarray, iscomplexfunction, isfunction, isintent_c,
+    isintent_hide, isintent_in, isintent_inout, isintent_nothide,
+    isintent_out, isoptional, isrequired, isscalar, isstring,
+    isstringfunction, issubroutine, l_and, l_not, l_or, outmess, replace,
+    stripcomma, throw_error
+)
+from . import cfuncs
+
+f2py_version = __version__.version
+
+
+################## Rules for callback function ##############
+
+cb_routine_rules = {
+    'cbtypedefs': 'typedef #rctype#(*#name#_typedef)(#optargs_td##args_td##strarglens_td##noargs#);',
+    'body': """
+#begintitle#
+typedef struct {
+    PyObject *capi;
+    PyTupleObject *args_capi;
+    int nofargs;
+    jmp_buf jmpbuf;
+} #name#_t;
+
+#if defined(F2PY_THREAD_LOCAL_DECL) && !defined(F2PY_USE_PYTHON_TLS)
+
+static F2PY_THREAD_LOCAL_DECL #name#_t *_active_#name# = NULL;
+
+static #name#_t *swap_active_#name#(#name#_t *ptr) {
+    #name#_t *prev = _active_#name#;
+    _active_#name# = ptr;
+    return prev;
+}
+
+static #name#_t *get_active_#name#(void) {
+    return _active_#name#;
+}
+
+#else
+
+static #name#_t *swap_active_#name#(#name#_t *ptr) {
+    char *key = "__f2py_cb_#name#";
+    return (#name#_t *)F2PySwapThreadLocalCallbackPtr(key, ptr);
+}
+
+static #name#_t *get_active_#name#(void) {
+    char *key = "__f2py_cb_#name#";
+    return (#name#_t *)F2PyGetThreadLocalCallbackPtr(key);
+}
+
+#endif
+
+/*typedef #rctype#(*#name#_typedef)(#optargs_td##args_td##strarglens_td##noargs#);*/
+#static# #rctype# #callbackname# (#optargs##args##strarglens##noargs#) {
+    #name#_t cb_local = { NULL, NULL, 0 };
+    #name#_t *cb = NULL;
+    PyTupleObject *capi_arglist = NULL;
+    PyObject *capi_return = NULL;
+    PyObject *capi_tmp = NULL;
+    PyObject *capi_arglist_list = NULL;
+    int capi_j,capi_i = 0;
+    int capi_longjmp_ok = 1;
+#decl#
+#ifdef F2PY_REPORT_ATEXIT
+f2py_cb_start_clock();
+#endif
+    cb = get_active_#name#();
+    if (cb == NULL) {
+        capi_longjmp_ok = 0;
+        cb = &cb_local;
+    }
+    capi_arglist = cb->args_capi;
+    CFUNCSMESS(\"cb:Call-back function #name# (maxnofargs=#maxnofargs#(-#nofoptargs#))\\n\");
+    CFUNCSMESSPY(\"cb:#name#_capi=\",cb->capi);
+    if (cb->capi==NULL) {
+        capi_longjmp_ok = 0;
+        cb->capi = PyObject_GetAttrString(#modulename#_module,\"#argname#\");
+        CFUNCSMESSPY(\"cb:#name#_capi=\",cb->capi);
+    }
+    if (cb->capi==NULL) {
+        PyErr_SetString(#modulename#_error,\"cb: Callback #argname# not defined (as an argument or module #modulename# attribute).\\n\");
+        goto capi_fail;
+    }
+    if (F2PyCapsule_Check(cb->capi)) {
+    #name#_typedef #name#_cptr;
+    #name#_cptr = F2PyCapsule_AsVoidPtr(cb->capi);
+    #returncptr#(*#name#_cptr)(#optargs_nm##args_nm##strarglens_nm#);
+    #return#
+    }
+    if (capi_arglist==NULL) {
+        capi_longjmp_ok = 0;
+        capi_tmp = PyObject_GetAttrString(#modulename#_module,\"#argname#_extra_args\");
+        if (capi_tmp) {
+            capi_arglist = (PyTupleObject *)PySequence_Tuple(capi_tmp);
+            Py_DECREF(capi_tmp);
+            if (capi_arglist==NULL) {
+                PyErr_SetString(#modulename#_error,\"Failed to convert #modulename#.#argname#_extra_args to tuple.\\n\");
+                goto capi_fail;
+            }
+        } else {
+            PyErr_Clear();
+            capi_arglist = (PyTupleObject *)Py_BuildValue(\"()\");
+        }
+    }
+    if (capi_arglist == NULL) {
+        PyErr_SetString(#modulename#_error,\"Callback #argname# argument list is not set.\\n\");
+        goto capi_fail;
+    }
+#setdims#
+#ifdef PYPY_VERSION
+#define CAPI_ARGLIST_SETITEM(idx, value) PyList_SetItem((PyObject *)capi_arglist_list, idx, value)
+    capi_arglist_list = PySequence_List(capi_arglist);
+    if (capi_arglist_list == NULL) goto capi_fail;
+#else
+#define CAPI_ARGLIST_SETITEM(idx, value) PyTuple_SetItem((PyObject *)capi_arglist, idx, value)
+#endif
+#pyobjfrom#
+#undef CAPI_ARGLIST_SETITEM
+#ifdef PYPY_VERSION
+    CFUNCSMESSPY(\"cb:capi_arglist=\",capi_arglist_list);
+#else
+    CFUNCSMESSPY(\"cb:capi_arglist=\",capi_arglist);
+#endif
+    CFUNCSMESS(\"cb:Call-back calling Python function #argname#.\\n\");
+#ifdef F2PY_REPORT_ATEXIT
+f2py_cb_start_call_clock();
+#endif
+#ifdef PYPY_VERSION
+    capi_return = PyObject_CallObject(cb->capi,(PyObject *)capi_arglist_list);
+    Py_DECREF(capi_arglist_list);
+    capi_arglist_list = NULL;
+#else
+    capi_return = PyObject_CallObject(cb->capi,(PyObject *)capi_arglist);
+#endif
+#ifdef F2PY_REPORT_ATEXIT
+f2py_cb_stop_call_clock();
+#endif
+    CFUNCSMESSPY(\"cb:capi_return=\",capi_return);
+    if (capi_return == NULL) {
+        fprintf(stderr,\"capi_return is NULL\\n\");
+        goto capi_fail;
+    }
+    if (capi_return == Py_None) {
+        Py_DECREF(capi_return);
+        capi_return = Py_BuildValue(\"()\");
+    }
+    else if (!PyTuple_Check(capi_return)) {
+        capi_return = Py_BuildValue(\"(N)\",capi_return);
+    }
+    capi_j = PyTuple_Size(capi_return);
+    capi_i = 0;
+#frompyobj#
+    CFUNCSMESS(\"cb:#name#:successful\\n\");
+    Py_DECREF(capi_return);
+#ifdef F2PY_REPORT_ATEXIT
+f2py_cb_stop_clock();
+#endif
+    goto capi_return_pt;
+capi_fail:
+    fprintf(stderr,\"Call-back #name# failed.\\n\");
+    Py_XDECREF(capi_return);
+    Py_XDECREF(capi_arglist_list);
+    if (capi_longjmp_ok) {
+        longjmp(cb->jmpbuf,-1);
+    }
+capi_return_pt:
+    ;
+#return#
+}
+#endtitle#
+""",
+    'need': ['setjmp.h', 'CFUNCSMESS', 'F2PY_THREAD_LOCAL_DECL'],
+    'maxnofargs': '#maxnofargs#',
+    'nofoptargs': '#nofoptargs#',
+    'docstr': """\
+    def #argname#(#docsignature#): return #docreturn#\\n\\
+#docstrsigns#""",
+    'latexdocstr': """
+{{}\\verb@def #argname#(#latexdocsignature#): return #docreturn#@{}}
+#routnote#
+
+#latexdocstrsigns#""",
+    'docstrshort': 'def #argname#(#docsignature#): return #docreturn#'
+}
+cb_rout_rules = [
+    {  # Init
+        'separatorsfor': {'decl': '\n',
+                          'args': ',', 'optargs': '', 'pyobjfrom': '\n', 'freemem': '\n',
+                          'args_td': ',', 'optargs_td': '',
+                          'args_nm': ',', 'optargs_nm': '',
+                          'frompyobj': '\n', 'setdims': '\n',
+                          'docstrsigns': '\\n"\n"',
+                          'latexdocstrsigns': '\n',
+                          'latexdocstrreq': '\n', 'latexdocstropt': '\n',
+                          'latexdocstrout': '\n', 'latexdocstrcbs': '\n',
+                          },
+        'decl': '/*decl*/', 'pyobjfrom': '/*pyobjfrom*/', 'frompyobj': '/*frompyobj*/',
+        'args': [], 'optargs': '', 'return': '', 'strarglens': '', 'freemem': '/*freemem*/',
+        'args_td': [], 'optargs_td': '', 'strarglens_td': '',
+        'args_nm': [], 'optargs_nm': '', 'strarglens_nm': '',
+        'noargs': '',
+        'setdims': '/*setdims*/',
+        'docstrsigns': '', 'latexdocstrsigns': '',
+        'docstrreq': '    Required arguments:',
+        'docstropt': '    Optional arguments:',
+        'docstrout': '    Return objects:',
+        'docstrcbs': '    Call-back functions:',
+        'docreturn': '', 'docsign': '', 'docsignopt': '',
+        'latexdocstrreq': '\\noindent Required arguments:',
+        'latexdocstropt': '\\noindent Optional arguments:',
+        'latexdocstrout': '\\noindent Return objects:',
+        'latexdocstrcbs': '\\noindent Call-back functions:',
+        'routnote': {hasnote: '--- #note#', l_not(hasnote): ''},
+    }, {  # Function
+        'decl': '    #ctype# return_value = 0;',
+        'frompyobj': [
+            {debugcapi: '    CFUNCSMESS("cb:Getting return_value->");'},
+            '''\
+    if (capi_j>capi_i) {
+        GETSCALARFROMPYTUPLE(capi_return,capi_i++,&return_value,#ctype#,
+          "#ctype#_from_pyobj failed in converting return_value of"
+          " call-back function #name# to C #ctype#\\n");
+    } else {
+        fprintf(stderr,"Warning: call-back function #name# did not provide"
+                       " return value (index=%d, type=#ctype#)\\n",capi_i);
+    }''',
+            {debugcapi:
+             '    fprintf(stderr,"#showvalueformat#.\\n",return_value);'}
+        ],
+        'need': ['#ctype#_from_pyobj', {debugcapi: 'CFUNCSMESS'}, 'GETSCALARFROMPYTUPLE'],
+        'return': '    return return_value;',
+        '_check': l_and(isfunction, l_not(isstringfunction), l_not(iscomplexfunction))
+    },
+    {  # String function
+        'pyobjfrom': {debugcapi: '    fprintf(stderr,"debug-capi:cb:#name#:%d:\\n",return_value_len);'},
+        'args': '#ctype# return_value,int return_value_len',
+        'args_nm': 'return_value,&return_value_len',
+        'args_td': '#ctype# ,int',
+        'frompyobj': [
+            {debugcapi: '    CFUNCSMESS("cb:Getting return_value->\\"");'},
+            """\
+    if (capi_j>capi_i) {
+        GETSTRFROMPYTUPLE(capi_return,capi_i++,return_value,return_value_len);
+    } else {
+        fprintf(stderr,"Warning: call-back function #name# did not provide"
+                       " return value (index=%d, type=#ctype#)\\n",capi_i);
+    }""",
+            {debugcapi:
+             '    fprintf(stderr,"#showvalueformat#\\".\\n",return_value);'}
+        ],
+        'need': ['#ctype#_from_pyobj', {debugcapi: 'CFUNCSMESS'},
+                 'string.h', 'GETSTRFROMPYTUPLE'],
+        'return': 'return;',
+        '_check': isstringfunction
+    },
+    {  # Complex function
+        'optargs': """
+#ifndef F2PY_CB_RETURNCOMPLEX
+#ctype# *return_value
+#endif
+""",
+        'optargs_nm': """
+#ifndef F2PY_CB_RETURNCOMPLEX
+return_value
+#endif
+""",
+        'optargs_td': """
+#ifndef F2PY_CB_RETURNCOMPLEX
+#ctype# *
+#endif
+""",
+        'decl': """
+#ifdef F2PY_CB_RETURNCOMPLEX
+    #ctype# return_value = {0, 0};
+#endif
+""",
+        'frompyobj': [
+            {debugcapi: '    CFUNCSMESS("cb:Getting return_value->");'},
+            """\
+    if (capi_j>capi_i) {
+#ifdef F2PY_CB_RETURNCOMPLEX
+        GETSCALARFROMPYTUPLE(capi_return,capi_i++,&return_value,#ctype#,
+          \"#ctype#_from_pyobj failed in converting return_value of call-back\"
+          \" function #name# to C #ctype#\\n\");
+#else
+        GETSCALARFROMPYTUPLE(capi_return,capi_i++,return_value,#ctype#,
+          \"#ctype#_from_pyobj failed in converting return_value of call-back\"
+          \" function #name# to C #ctype#\\n\");
+#endif
+    } else {
+        fprintf(stderr,
+                \"Warning: call-back function #name# did not provide\"
+                \" return value (index=%d, type=#ctype#)\\n\",capi_i);
+    }""",
+            {debugcapi: """\
+#ifdef F2PY_CB_RETURNCOMPLEX
+    fprintf(stderr,\"#showvalueformat#.\\n\",(return_value).r,(return_value).i);
+#else
+    fprintf(stderr,\"#showvalueformat#.\\n\",(*return_value).r,(*return_value).i);
+#endif
+"""}
+        ],
+        'return': """
+#ifdef F2PY_CB_RETURNCOMPLEX
+    return return_value;
+#else
+    return;
+#endif
+""",
+        'need': ['#ctype#_from_pyobj', {debugcapi: 'CFUNCSMESS'},
+                 'string.h', 'GETSCALARFROMPYTUPLE', '#ctype#'],
+        '_check': iscomplexfunction
+    },
+    {'docstrout': '        #pydocsignout#',
+     'latexdocstrout': ['\\item[]{{}\\verb@#pydocsignout#@{}}',
+                        {hasnote: '--- #note#'}],
+     'docreturn': '#rname#,',
+     '_check': isfunction},
+    {'_check': issubroutine, 'return': 'return;'}
+]
+
+cb_arg_rules = [
+    {  # Doc
+        'docstropt': {l_and(isoptional, isintent_nothide): '        #pydocsign#'},
+        'docstrreq': {l_and(isrequired, isintent_nothide): '        #pydocsign#'},
+        'docstrout': {isintent_out: '        #pydocsignout#'},
+        'latexdocstropt': {l_and(isoptional, isintent_nothide): ['\\item[]{{}\\verb@#pydocsign#@{}}',
+                                                                 {hasnote: '--- #note#'}]},
+        'latexdocstrreq': {l_and(isrequired, isintent_nothide): ['\\item[]{{}\\verb@#pydocsign#@{}}',
+                                                                 {hasnote: '--- #note#'}]},
+        'latexdocstrout': {isintent_out: ['\\item[]{{}\\verb@#pydocsignout#@{}}',
+                                          {l_and(hasnote, isintent_hide): '--- #note#',
+                                           l_and(hasnote, isintent_nothide): '--- See above.'}]},
+        'docsign': {l_and(isrequired, isintent_nothide): '#varname#,'},
+        'docsignopt': {l_and(isoptional, isintent_nothide): '#varname#,'},
+        'depend': ''
+    },
+    {
+        'args': {
+            l_and(isscalar, isintent_c): '#ctype# #varname_i#',
+            l_and(isscalar, l_not(isintent_c)): '#ctype# *#varname_i#_cb_capi',
+            isarray: '#ctype# *#varname_i#',
+            isstring: '#ctype# #varname_i#'
+        },
+        'args_nm': {
+            l_and(isscalar, isintent_c): '#varname_i#',
+            l_and(isscalar, l_not(isintent_c)): '#varname_i#_cb_capi',
+            isarray: '#varname_i#',
+            isstring: '#varname_i#'
+        },
+        'args_td': {
+            l_and(isscalar, isintent_c): '#ctype#',
+            l_and(isscalar, l_not(isintent_c)): '#ctype# *',
+            isarray: '#ctype# *',
+            isstring: '#ctype#'
+        },
+        'need': {l_or(isscalar, isarray, isstring): '#ctype#'},
+        # untested with multiple args
+        'strarglens': {isstring: ',int #varname_i#_cb_len'},
+        'strarglens_td': {isstring: ',int'},  # untested with multiple args
+        # untested with multiple args
+        'strarglens_nm': {isstring: ',#varname_i#_cb_len'},
+    },
+    {  # Scalars
+        'decl': {l_not(isintent_c): '    #ctype# #varname_i#=(*#varname_i#_cb_capi);'},
+        'error': {l_and(isintent_c, isintent_out,
+                        throw_error('intent(c,out) is forbidden for callback scalar arguments')):
+                  ''},
+        'frompyobj': [{debugcapi: '    CFUNCSMESS("cb:Getting #varname#->");'},
+                      {isintent_out:
+                       '    if (capi_j>capi_i)\n        GETSCALARFROMPYTUPLE(capi_return,capi_i++,#varname_i#_cb_capi,#ctype#,"#ctype#_from_pyobj failed in converting argument #varname# of call-back function #name# to C #ctype#\\n");'},
+                      {l_and(debugcapi, l_and(l_not(iscomplex), isintent_c)):
+                          '    fprintf(stderr,"#showvalueformat#.\\n",#varname_i#);'},
+                      {l_and(debugcapi, l_and(l_not(iscomplex), l_not( isintent_c))):
+                          '    fprintf(stderr,"#showvalueformat#.\\n",*#varname_i#_cb_capi);'},
+                      {l_and(debugcapi, l_and(iscomplex, isintent_c)):
+                          '    fprintf(stderr,"#showvalueformat#.\\n",(#varname_i#).r,(#varname_i#).i);'},
+                      {l_and(debugcapi, l_and(iscomplex, l_not( isintent_c))):
+                          '    fprintf(stderr,"#showvalueformat#.\\n",(*#varname_i#_cb_capi).r,(*#varname_i#_cb_capi).i);'},
+                      ],
+        'need': [{isintent_out: ['#ctype#_from_pyobj', 'GETSCALARFROMPYTUPLE']},
+                 {debugcapi: 'CFUNCSMESS'}],
+        '_check': isscalar
+    }, {
+        'pyobjfrom': [{isintent_in: """\
+    if (cb->nofargs>capi_i)
+        if (CAPI_ARGLIST_SETITEM(capi_i++,pyobj_from_#ctype#1(#varname_i#)))
+            goto capi_fail;"""},
+                      {isintent_inout: """\
+    if (cb->nofargs>capi_i)
+        if (CAPI_ARGLIST_SETITEM(capi_i++,pyarr_from_p_#ctype#1(#varname_i#_cb_capi)))
+            goto capi_fail;"""}],
+        'need': [{isintent_in: 'pyobj_from_#ctype#1'},
+                 {isintent_inout: 'pyarr_from_p_#ctype#1'},
+                 {iscomplex: '#ctype#'}],
+        '_check': l_and(isscalar, isintent_nothide),
+        '_optional': ''
+    }, {  # String
+        'frompyobj': [{debugcapi: '    CFUNCSMESS("cb:Getting #varname#->\\"");'},
+                      """    if (capi_j>capi_i)
+        GETSTRFROMPYTUPLE(capi_return,capi_i++,#varname_i#,#varname_i#_cb_len);""",
+                      {debugcapi:
+                       '    fprintf(stderr,"#showvalueformat#\\":%d:.\\n",#varname_i#,#varname_i#_cb_len);'},
+                      ],
+        'need': ['#ctype#', 'GETSTRFROMPYTUPLE',
+                 {debugcapi: 'CFUNCSMESS'}, 'string.h'],
+        '_check': l_and(isstring, isintent_out)
+    }, {
+        'pyobjfrom': [
+            {debugcapi:
+             ('    fprintf(stderr,"debug-capi:cb:#varname#=#showvalueformat#:'
+              '%d:\\n",#varname_i#,#varname_i#_cb_len);')},
+            {isintent_in: """\
+    if (cb->nofargs>capi_i)
+        if (CAPI_ARGLIST_SETITEM(capi_i++,pyobj_from_#ctype#1size(#varname_i#,#varname_i#_cb_len)))
+            goto capi_fail;"""},
+                      {isintent_inout: """\
+    if (cb->nofargs>capi_i) {
+        int #varname_i#_cb_dims[] = {#varname_i#_cb_len};
+        if (CAPI_ARGLIST_SETITEM(capi_i++,pyarr_from_p_#ctype#1(#varname_i#,#varname_i#_cb_dims)))
+            goto capi_fail;
+    }"""}],
+        'need': [{isintent_in: 'pyobj_from_#ctype#1size'},
+                 {isintent_inout: 'pyarr_from_p_#ctype#1'}],
+        '_check': l_and(isstring, isintent_nothide),
+        '_optional': ''
+    },
+    # Array ...
+    {
+        'decl': '    npy_intp #varname_i#_Dims[#rank#] = {#rank*[-1]#};',
+        'setdims': '    #cbsetdims#;',
+        '_check': isarray,
+        '_depend': ''
+    },
+    {
+        'pyobjfrom': [{debugcapi: '    fprintf(stderr,"debug-capi:cb:#varname#\\n");'},
+                      {isintent_c: """\
+    if (cb->nofargs>capi_i) {
+        /* tmp_arr will be inserted to capi_arglist_list that will be
+           destroyed when leaving callback function wrapper together
+           with tmp_arr. */
+        PyArrayObject *tmp_arr = (PyArrayObject *)PyArray_New(&PyArray_Type,
+          #rank#,#varname_i#_Dims,#atype#,NULL,(char*)#varname_i#,#elsize#,
+          NPY_ARRAY_CARRAY,NULL);
+""",
+                       l_not(isintent_c): """\
+    if (cb->nofargs>capi_i) {
+        /* tmp_arr will be inserted to capi_arglist_list that will be
+           destroyed when leaving callback function wrapper together
+           with tmp_arr. */
+        PyArrayObject *tmp_arr = (PyArrayObject *)PyArray_New(&PyArray_Type,
+          #rank#,#varname_i#_Dims,#atype#,NULL,(char*)#varname_i#,#elsize#,
+          NPY_ARRAY_FARRAY,NULL);
+""",
+                       },
+                      """
+        if (tmp_arr==NULL)
+            goto capi_fail;
+        if (CAPI_ARGLIST_SETITEM(capi_i++,(PyObject *)tmp_arr))
+            goto capi_fail;
+}"""],
+        '_check': l_and(isarray, isintent_nothide, l_or(isintent_in, isintent_inout)),
+        '_optional': '',
+    }, {
+        'frompyobj': [{debugcapi: '    CFUNCSMESS("cb:Getting #varname#->");'},
+                      """    if (capi_j>capi_i) {
+        PyArrayObject *rv_cb_arr = NULL;
+        if ((capi_tmp = PyTuple_GetItem(capi_return,capi_i++))==NULL) goto capi_fail;
+        rv_cb_arr =  array_from_pyobj(#atype#,#varname_i#_Dims,#rank#,F2PY_INTENT_IN""",
+                      {isintent_c: '|F2PY_INTENT_C'},
+                      """,capi_tmp);
+        if (rv_cb_arr == NULL) {
+            fprintf(stderr,\"rv_cb_arr is NULL\\n\");
+            goto capi_fail;
+        }
+        MEMCOPY(#varname_i#,PyArray_DATA(rv_cb_arr),PyArray_NBYTES(rv_cb_arr));
+        if (capi_tmp != (PyObject *)rv_cb_arr) {
+            Py_DECREF(rv_cb_arr);
+        }
+    }""",
+                      {debugcapi: '    fprintf(stderr,"<-.\\n");'},
+                      ],
+        'need': ['MEMCOPY', {iscomplexarray: '#ctype#'}],
+        '_check': l_and(isarray, isintent_out)
+    }, {
+        'docreturn': '#varname#,',
+        '_check': isintent_out
+    }
+]
+
+################## Build call-back module #############
+cb_map = {}
+
+
+def buildcallbacks(m):
+    cb_map[m['name']] = []
+    for bi in m['body']:
+        if bi['block'] == 'interface':
+            for b in bi['body']:
+                if b:
+                    buildcallback(b, m['name'])
+                else:
+                    errmess('warning: empty body for %s\n' % (m['name']))
+
+
+def buildcallback(rout, um):
+    from . import capi_maps
+
+    outmess('    Constructing call-back function "cb_%s_in_%s"\n' %
+            (rout['name'], um))
+    args, depargs = getargs(rout)
+    capi_maps.depargs = depargs
+    var = rout['vars']
+    vrd = capi_maps.cb_routsign2map(rout, um)
+    rd = dictappend({}, vrd)
+    cb_map[um].append([rout['name'], rd['name']])
+    for r in cb_rout_rules:
+        if ('_check' in r and r['_check'](rout)) or ('_check' not in r):
+            ar = applyrules(r, vrd, rout)
+            rd = dictappend(rd, ar)
+    savevrd = {}
+    for i, a in enumerate(args):
+        vrd = capi_maps.cb_sign2map(a, var[a], index=i)
+        savevrd[a] = vrd
+        for r in cb_arg_rules:
+            if '_depend' in r:
+                continue
+            if '_optional' in r and isoptional(var[a]):
+                continue
+            if ('_check' in r and r['_check'](var[a])) or ('_check' not in r):
+                ar = applyrules(r, vrd, var[a])
+                rd = dictappend(rd, ar)
+                if '_break' in r:
+                    break
+    for a in args:
+        vrd = savevrd[a]
+        for r in cb_arg_rules:
+            if '_depend' in r:
+                continue
+            if ('_optional' not in r) or ('_optional' in r and isrequired(var[a])):
+                continue
+            if ('_check' in r and r['_check'](var[a])) or ('_check' not in r):
+                ar = applyrules(r, vrd, var[a])
+                rd = dictappend(rd, ar)
+                if '_break' in r:
+                    break
+    for a in depargs:
+        vrd = savevrd[a]
+        for r in cb_arg_rules:
+            if '_depend' not in r:
+                continue
+            if '_optional' in r:
+                continue
+            if ('_check' in r and r['_check'](var[a])) or ('_check' not in r):
+                ar = applyrules(r, vrd, var[a])
+                rd = dictappend(rd, ar)
+                if '_break' in r:
+                    break
+    if 'args' in rd and 'optargs' in rd:
+        if isinstance(rd['optargs'], list):
+            rd['optargs'] = rd['optargs'] + ["""
+#ifndef F2PY_CB_RETURNCOMPLEX
+,
+#endif
+"""]
+            rd['optargs_nm'] = rd['optargs_nm'] + ["""
+#ifndef F2PY_CB_RETURNCOMPLEX
+,
+#endif
+"""]
+            rd['optargs_td'] = rd['optargs_td'] + ["""
+#ifndef F2PY_CB_RETURNCOMPLEX
+,
+#endif
+"""]
+    if isinstance(rd['docreturn'], list):
+        rd['docreturn'] = stripcomma(
+            replace('#docreturn#', {'docreturn': rd['docreturn']}))
+    optargs = stripcomma(replace('#docsignopt#',
+                                 {'docsignopt': rd['docsignopt']}
+                                 ))
+    if optargs == '':
+        rd['docsignature'] = stripcomma(
+            replace('#docsign#', {'docsign': rd['docsign']}))
+    else:
+        rd['docsignature'] = replace('#docsign#[#docsignopt#]',
+                                     {'docsign': rd['docsign'],
+                                      'docsignopt': optargs,
+                                      })
+    rd['latexdocsignature'] = rd['docsignature'].replace('_', '\\_')
+    rd['latexdocsignature'] = rd['latexdocsignature'].replace(',', ', ')
+    rd['docstrsigns'] = []
+    rd['latexdocstrsigns'] = []
+    for k in ['docstrreq', 'docstropt', 'docstrout', 'docstrcbs']:
+        if k in rd and isinstance(rd[k], list):
+            rd['docstrsigns'] = rd['docstrsigns'] + rd[k]
+        k = 'latex' + k
+        if k in rd and isinstance(rd[k], list):
+            rd['latexdocstrsigns'] = rd['latexdocstrsigns'] + rd[k][0:1] +\
+                ['\\begin{description}'] + rd[k][1:] +\
+                ['\\end{description}']
+    if 'args' not in rd:
+        rd['args'] = ''
+        rd['args_td'] = ''
+        rd['args_nm'] = ''
+    if not (rd.get('args') or rd.get('optargs') or rd.get('strarglens')):
+        rd['noargs'] = 'void'
+
+    ar = applyrules(cb_routine_rules, rd)
+    cfuncs.callbacks[rd['name']] = ar['body']
+    if isinstance(ar['need'], str):
+        ar['need'] = [ar['need']]
+
+    if 'need' in rd:
+        for t in cfuncs.typedefs.keys():
+            if t in rd['need']:
+                ar['need'].append(t)
+
+    cfuncs.typedefs_generated[rd['name'] + '_typedef'] = ar['cbtypedefs']
+    ar['need'].append(rd['name'] + '_typedef')
+    cfuncs.needs[rd['name']] = ar['need']
+
+    capi_maps.lcb2_map[rd['name']] = {'maxnofargs': ar['maxnofargs'],
+                                      'nofoptargs': ar['nofoptargs'],
+                                      'docstr': ar['docstr'],
+                                      'latexdocstr': ar['latexdocstr'],
+                                      'argname': rd['argname']
+                                      }
+    outmess('      %s\n' % (ar['docstrshort']))
+    return
+################## Build call-back function #############
diff --git a/.venv/lib/python3.12/site-packages/numpy/f2py/cfuncs.py b/.venv/lib/python3.12/site-packages/numpy/f2py/cfuncs.py
new file mode 100644
index 00000000..4328a6e5
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/numpy/f2py/cfuncs.py
@@ -0,0 +1,1536 @@
+#!/usr/bin/env python3
+"""
+C declarations, CPP macros, and C functions for f2py2e.
+Only required declarations/macros/functions will be used.
+
+Copyright 1999 -- 2011 Pearu Peterson all rights reserved.
+Copyright 2011 -- present NumPy Developers.
+Permission to use, modify, and distribute this software is given under the
+terms of the NumPy License.
+
+NO WARRANTY IS EXPRESSED OR IMPLIED.  USE AT YOUR OWN RISK.
+"""
+import sys
+import copy
+
+from . import __version__
+
+f2py_version = __version__.version
+errmess = sys.stderr.write
+
+##################### Definitions ##################
+
+outneeds = {'includes0': [], 'includes': [], 'typedefs': [], 'typedefs_generated': [],
+            'userincludes': [],
+            'cppmacros': [], 'cfuncs': [], 'callbacks': [], 'f90modhooks': [],
+            'commonhooks': []}
+needs = {}
+includes0 = {'includes0': '/*need_includes0*/'}
+includes = {'includes': '/*need_includes*/'}
+userincludes = {'userincludes': '/*need_userincludes*/'}
+typedefs = {'typedefs': '/*need_typedefs*/'}
+typedefs_generated = {'typedefs_generated': '/*need_typedefs_generated*/'}
+cppmacros = {'cppmacros': '/*need_cppmacros*/'}
+cfuncs = {'cfuncs': '/*need_cfuncs*/'}
+callbacks = {'callbacks': '/*need_callbacks*/'}
+f90modhooks = {'f90modhooks': '/*need_f90modhooks*/',
+               'initf90modhooksstatic': '/*initf90modhooksstatic*/',
+               'initf90modhooksdynamic': '/*initf90modhooksdynamic*/',
+               }
+commonhooks = {'commonhooks': '/*need_commonhooks*/',
+               'initcommonhooks': '/*need_initcommonhooks*/',
+               }
+
+############ Includes ###################
+
+includes0['math.h'] = '#include <math.h>'
+includes0['string.h'] = '#include <string.h>'
+includes0['setjmp.h'] = '#include <setjmp.h>'
+
+includes['arrayobject.h'] = '''#define PY_ARRAY_UNIQUE_SYMBOL PyArray_API
+#include "arrayobject.h"'''
+includes['npy_math.h'] = '#include "numpy/npy_math.h"'
+
+includes['arrayobject.h'] = '#include "fortranobject.h"'
+includes['stdarg.h'] = '#include <stdarg.h>'
+
+############# Type definitions ###############
+
+typedefs['unsigned_char'] = 'typedef unsigned char unsigned_char;'
+typedefs['unsigned_short'] = 'typedef unsigned short unsigned_short;'
+typedefs['unsigned_long'] = 'typedef unsigned long unsigned_long;'
+typedefs['signed_char'] = 'typedef signed char signed_char;'
+typedefs['long_long'] = """
+#if defined(NPY_OS_WIN32)
+typedef __int64 long_long;
+#else
+typedef long long long_long;
+typedef unsigned long long unsigned_long_long;
+#endif
+"""
+typedefs['unsigned_long_long'] = """
+#if defined(NPY_OS_WIN32)
+typedef __uint64 long_long;
+#else
+typedef unsigned long long unsigned_long_long;
+#endif
+"""
+typedefs['long_double'] = """
+#ifndef _LONG_DOUBLE
+typedef long double long_double;
+#endif
+"""
+typedefs[
+    'complex_long_double'] = 'typedef struct {long double r,i;} complex_long_double;'
+typedefs['complex_float'] = 'typedef struct {float r,i;} complex_float;'
+typedefs['complex_double'] = 'typedef struct {double r,i;} complex_double;'
+typedefs['string'] = """typedef char * string;"""
+typedefs['character'] = """typedef char character;"""
+
+
+############### CPP macros ####################
+cppmacros['CFUNCSMESS'] = """
+#ifdef DEBUGCFUNCS
+#define CFUNCSMESS(mess) fprintf(stderr,\"debug-capi:\"mess);
+#define CFUNCSMESSPY(mess,obj) CFUNCSMESS(mess) \\
+    PyObject_Print((PyObject *)obj,stderr,Py_PRINT_RAW);\\
+    fprintf(stderr,\"\\n\");
+#else
+#define CFUNCSMESS(mess)
+#define CFUNCSMESSPY(mess,obj)
+#endif
+"""
+cppmacros['F_FUNC'] = """
+#if defined(PREPEND_FORTRAN)
+#if defined(NO_APPEND_FORTRAN)
+#if defined(UPPERCASE_FORTRAN)
+#define F_FUNC(f,F) _##F
+#else
+#define F_FUNC(f,F) _##f
+#endif
+#else
+#if defined(UPPERCASE_FORTRAN)
+#define F_FUNC(f,F) _##F##_
+#else
+#define F_FUNC(f,F) _##f##_
+#endif
+#endif
+#else
+#if defined(NO_APPEND_FORTRAN)
+#if defined(UPPERCASE_FORTRAN)
+#define F_FUNC(f,F) F
+#else
+#define F_FUNC(f,F) f
+#endif
+#else
+#if defined(UPPERCASE_FORTRAN)
+#define F_FUNC(f,F) F##_
+#else
+#define F_FUNC(f,F) f##_
+#endif
+#endif
+#endif
+#if defined(UNDERSCORE_G77)
+#define F_FUNC_US(f,F) F_FUNC(f##_,F##_)
+#else
+#define F_FUNC_US(f,F) F_FUNC(f,F)
+#endif
+"""
+cppmacros['F_WRAPPEDFUNC'] = """
+#if defined(PREPEND_FORTRAN)
+#if defined(NO_APPEND_FORTRAN)
+#if defined(UPPERCASE_FORTRAN)
+#define F_WRAPPEDFUNC(f,F) _F2PYWRAP##F
+#else
+#define F_WRAPPEDFUNC(f,F) _f2pywrap##f
+#endif
+#else
+#if defined(UPPERCASE_FORTRAN)
+#define F_WRAPPEDFUNC(f,F) _F2PYWRAP##F##_
+#else
+#define F_WRAPPEDFUNC(f,F) _f2pywrap##f##_
+#endif
+#endif
+#else
+#if defined(NO_APPEND_FORTRAN)
+#if defined(UPPERCASE_FORTRAN)
+#define F_WRAPPEDFUNC(f,F) F2PYWRAP##F
+#else
+#define F_WRAPPEDFUNC(f,F) f2pywrap##f
+#endif
+#else
+#if defined(UPPERCASE_FORTRAN)
+#define F_WRAPPEDFUNC(f,F) F2PYWRAP##F##_
+#else
+#define F_WRAPPEDFUNC(f,F) f2pywrap##f##_
+#endif
+#endif
+#endif
+#if defined(UNDERSCORE_G77)
+#define F_WRAPPEDFUNC_US(f,F) F_WRAPPEDFUNC(f##_,F##_)
+#else
+#define F_WRAPPEDFUNC_US(f,F) F_WRAPPEDFUNC(f,F)
+#endif
+"""
+cppmacros['F_MODFUNC'] = """
+#if defined(F90MOD2CCONV1) /*E.g. Compaq Fortran */
+#if defined(NO_APPEND_FORTRAN)
+#define F_MODFUNCNAME(m,f) $ ## m ## $ ## f
+#else
+#define F_MODFUNCNAME(m,f) $ ## m ## $ ## f ## _
+#endif
+#endif
+
+#if defined(F90MOD2CCONV2) /*E.g. IBM XL Fortran, not tested though */
+#if defined(NO_APPEND_FORTRAN)
+#define F_MODFUNCNAME(m,f)  __ ## m ## _MOD_ ## f
+#else
+#define F_MODFUNCNAME(m,f)  __ ## m ## _MOD_ ## f ## _
+#endif
+#endif
+
+#if defined(F90MOD2CCONV3) /*E.g. MIPSPro Compilers */
+#if defined(NO_APPEND_FORTRAN)
+#define F_MODFUNCNAME(m,f)  f ## .in. ## m
+#else
+#define F_MODFUNCNAME(m,f)  f ## .in. ## m ## _
+#endif
+#endif
+/*
+#if defined(UPPERCASE_FORTRAN)
+#define F_MODFUNC(m,M,f,F) F_MODFUNCNAME(M,F)
+#else
+#define F_MODFUNC(m,M,f,F) F_MODFUNCNAME(m,f)
+#endif
+*/
+
+#define F_MODFUNC(m,f) (*(f2pymodstruct##m##.##f))
+"""
+cppmacros['SWAPUNSAFE'] = """
+#define SWAP(a,b) (size_t)(a) = ((size_t)(a) ^ (size_t)(b));\\
+ (size_t)(b) = ((size_t)(a) ^ (size_t)(b));\\
+ (size_t)(a) = ((size_t)(a) ^ (size_t)(b))
+"""
+cppmacros['SWAP'] = """
+#define SWAP(a,b,t) {\\
+    t *c;\\
+    c = a;\\
+    a = b;\\
+    b = c;}
+"""
+# cppmacros['ISCONTIGUOUS']='#define ISCONTIGUOUS(m) (PyArray_FLAGS(m) &
+# NPY_ARRAY_C_CONTIGUOUS)'
+cppmacros['PRINTPYOBJERR'] = """
+#define PRINTPYOBJERR(obj)\\
+    fprintf(stderr,\"#modulename#.error is related to \");\\
+    PyObject_Print((PyObject *)obj,stderr,Py_PRINT_RAW);\\
+    fprintf(stderr,\"\\n\");
+"""
+cppmacros['MINMAX'] = """
+#ifndef max
+#define max(a,b) ((a > b) ? (a) : (b))
+#endif
+#ifndef min
+#define min(a,b) ((a < b) ? (a) : (b))
+#endif
+#ifndef MAX
+#define MAX(a,b) ((a > b) ? (a) : (b))
+#endif
+#ifndef MIN
+#define MIN(a,b) ((a < b) ? (a) : (b))
+#endif
+"""
+cppmacros['len..'] = """
+/* See fortranobject.h for definitions. The macros here are provided for BC. */
+#define rank f2py_rank
+#define shape f2py_shape
+#define fshape f2py_shape
+#define len f2py_len
+#define flen f2py_flen
+#define slen f2py_slen
+#define size f2py_size
+"""
+cppmacros['pyobj_from_char1'] = r"""
+#define pyobj_from_char1(v) (PyLong_FromLong(v))
+"""
+cppmacros['pyobj_from_short1'] = r"""
+#define pyobj_from_short1(v) (PyLong_FromLong(v))
+"""
+needs['pyobj_from_int1'] = ['signed_char']
+cppmacros['pyobj_from_int1'] = r"""
+#define pyobj_from_int1(v) (PyLong_FromLong(v))
+"""
+cppmacros['pyobj_from_long1'] = r"""
+#define pyobj_from_long1(v) (PyLong_FromLong(v))
+"""
+needs['pyobj_from_long_long1'] = ['long_long']
+cppmacros['pyobj_from_long_long1'] = """
+#ifdef HAVE_LONG_LONG
+#define pyobj_from_long_long1(v) (PyLong_FromLongLong(v))
+#else
+#warning HAVE_LONG_LONG is not available. Redefining pyobj_from_long_long.
+#define pyobj_from_long_long1(v) (PyLong_FromLong(v))
+#endif
+"""
+needs['pyobj_from_long_double1'] = ['long_double']
+cppmacros['pyobj_from_long_double1'] = """
+#define pyobj_from_long_double1(v) (PyFloat_FromDouble(v))"""
+cppmacros['pyobj_from_double1'] = """
+#define pyobj_from_double1(v) (PyFloat_FromDouble(v))"""
+cppmacros['pyobj_from_float1'] = """
+#define pyobj_from_float1(v) (PyFloat_FromDouble(v))"""
+needs['pyobj_from_complex_long_double1'] = ['complex_long_double']
+cppmacros['pyobj_from_complex_long_double1'] = """
+#define pyobj_from_complex_long_double1(v) (PyComplex_FromDoubles(v.r,v.i))"""
+needs['pyobj_from_complex_double1'] = ['complex_double']
+cppmacros['pyobj_from_complex_double1'] = """
+#define pyobj_from_complex_double1(v) (PyComplex_FromDoubles(v.r,v.i))"""
+needs['pyobj_from_complex_float1'] = ['complex_float']
+cppmacros['pyobj_from_complex_float1'] = """
+#define pyobj_from_complex_float1(v) (PyComplex_FromDoubles(v.r,v.i))"""
+needs['pyobj_from_string1'] = ['string']
+cppmacros['pyobj_from_string1'] = """
+#define pyobj_from_string1(v) (PyUnicode_FromString((char *)v))"""
+needs['pyobj_from_string1size'] = ['string']
+cppmacros['pyobj_from_string1size'] = """
+#define pyobj_from_string1size(v,len) (PyUnicode_FromStringAndSize((char *)v, len))"""
+needs['TRYPYARRAYTEMPLATE'] = ['PRINTPYOBJERR']
+cppmacros['TRYPYARRAYTEMPLATE'] = """
+/* New SciPy */
+#define TRYPYARRAYTEMPLATECHAR case NPY_STRING: *(char *)(PyArray_DATA(arr))=*v; break;
+#define TRYPYARRAYTEMPLATELONG case NPY_LONG: *(long *)(PyArray_DATA(arr))=*v; break;
+#define TRYPYARRAYTEMPLATEOBJECT case NPY_OBJECT: PyArray_SETITEM(arr,PyArray_DATA(arr),pyobj_from_ ## ctype ## 1(*v)); break;
+
+#define TRYPYARRAYTEMPLATE(ctype,typecode) \\
+        PyArrayObject *arr = NULL;\\
+        if (!obj) return -2;\\
+        if (!PyArray_Check(obj)) return -1;\\
+        if (!(arr=(PyArrayObject *)obj)) {fprintf(stderr,\"TRYPYARRAYTEMPLATE:\");PRINTPYOBJERR(obj);return 0;}\\
+        if (PyArray_DESCR(arr)->type==typecode)  {*(ctype *)(PyArray_DATA(arr))=*v; return 1;}\\
+        switch (PyArray_TYPE(arr)) {\\
+                case NPY_DOUBLE: *(npy_double *)(PyArray_DATA(arr))=*v; break;\\
+                case NPY_INT: *(npy_int *)(PyArray_DATA(arr))=*v; break;\\
+                case NPY_LONG: *(npy_long *)(PyArray_DATA(arr))=*v; break;\\
+                case NPY_FLOAT: *(npy_float *)(PyArray_DATA(arr))=*v; break;\\
+                case NPY_CDOUBLE: *(npy_double *)(PyArray_DATA(arr))=*v; break;\\
+                case NPY_CFLOAT: *(npy_float *)(PyArray_DATA(arr))=*v; break;\\
+                case NPY_BOOL: *(npy_bool *)(PyArray_DATA(arr))=(*v!=0); break;\\
+                case NPY_UBYTE: *(npy_ubyte *)(PyArray_DATA(arr))=*v; break;\\
+                case NPY_BYTE: *(npy_byte *)(PyArray_DATA(arr))=*v; break;\\
+                case NPY_SHORT: *(npy_short *)(PyArray_DATA(arr))=*v; break;\\
+                case NPY_USHORT: *(npy_ushort *)(PyArray_DATA(arr))=*v; break;\\
+                case NPY_UINT: *(npy_uint *)(PyArray_DATA(arr))=*v; break;\\
+                case NPY_ULONG: *(npy_ulong *)(PyArray_DATA(arr))=*v; break;\\
+                case NPY_LONGLONG: *(npy_longlong *)(PyArray_DATA(arr))=*v; break;\\
+                case NPY_ULONGLONG: *(npy_ulonglong *)(PyArray_DATA(arr))=*v; break;\\
+                case NPY_LONGDOUBLE: *(npy_longdouble *)(PyArray_DATA(arr))=*v; break;\\
+                case NPY_CLONGDOUBLE: *(npy_longdouble *)(PyArray_DATA(arr))=*v; break;\\
+                case NPY_OBJECT: PyArray_SETITEM(arr, PyArray_DATA(arr), pyobj_from_ ## ctype ## 1(*v)); break;\\
+        default: return -2;\\
+        };\\
+        return 1
+"""
+
+needs['TRYCOMPLEXPYARRAYTEMPLATE'] = ['PRINTPYOBJERR']
+cppmacros['TRYCOMPLEXPYARRAYTEMPLATE'] = """
+#define TRYCOMPLEXPYARRAYTEMPLATEOBJECT case NPY_OBJECT: PyArray_SETITEM(arr, PyArray_DATA(arr), pyobj_from_complex_ ## ctype ## 1((*v))); break;
+#define TRYCOMPLEXPYARRAYTEMPLATE(ctype,typecode)\\
+        PyArrayObject *arr = NULL;\\
+        if (!obj) return -2;\\
+        if (!PyArray_Check(obj)) return -1;\\
+        if (!(arr=(PyArrayObject *)obj)) {fprintf(stderr,\"TRYCOMPLEXPYARRAYTEMPLATE:\");PRINTPYOBJERR(obj);return 0;}\\
+        if (PyArray_DESCR(arr)->type==typecode) {\\
+            *(ctype *)(PyArray_DATA(arr))=(*v).r;\\
+            *(ctype *)(PyArray_DATA(arr)+sizeof(ctype))=(*v).i;\\
+            return 1;\\
+        }\\
+        switch (PyArray_TYPE(arr)) {\\
+                case NPY_CDOUBLE: *(npy_double *)(PyArray_DATA(arr))=(*v).r;\\
+                                  *(npy_double *)(PyArray_DATA(arr)+sizeof(npy_double))=(*v).i;\\
+                                  break;\\
+                case NPY_CFLOAT: *(npy_float *)(PyArray_DATA(arr))=(*v).r;\\
+                                 *(npy_float *)(PyArray_DATA(arr)+sizeof(npy_float))=(*v).i;\\
+                                 break;\\
+                case NPY_DOUBLE: *(npy_double *)(PyArray_DATA(arr))=(*v).r; break;\\
+                case NPY_LONG: *(npy_long *)(PyArray_DATA(arr))=(*v).r; break;\\
+                case NPY_FLOAT: *(npy_float *)(PyArray_DATA(arr))=(*v).r; break;\\
+                case NPY_INT: *(npy_int *)(PyArray_DATA(arr))=(*v).r; break;\\
+                case NPY_SHORT: *(npy_short *)(PyArray_DATA(arr))=(*v).r; break;\\
+                case NPY_UBYTE: *(npy_ubyte *)(PyArray_DATA(arr))=(*v).r; break;\\
+                case NPY_BYTE: *(npy_byte *)(PyArray_DATA(arr))=(*v).r; break;\\
+                case NPY_BOOL: *(npy_bool *)(PyArray_DATA(arr))=((*v).r!=0 && (*v).i!=0); break;\\
+                case NPY_USHORT: *(npy_ushort *)(PyArray_DATA(arr))=(*v).r; break;\\
+                case NPY_UINT: *(npy_uint *)(PyArray_DATA(arr))=(*v).r; break;\\
+                case NPY_ULONG: *(npy_ulong *)(PyArray_DATA(arr))=(*v).r; break;\\
+                case NPY_LONGLONG: *(npy_longlong *)(PyArray_DATA(arr))=(*v).r; break;\\
+                case NPY_ULONGLONG: *(npy_ulonglong *)(PyArray_DATA(arr))=(*v).r; break;\\
+                case NPY_LONGDOUBLE: *(npy_longdouble *)(PyArray_DATA(arr))=(*v).r; break;\\
+                case NPY_CLONGDOUBLE: *(npy_longdouble *)(PyArray_DATA(arr))=(*v).r;\\
+                                      *(npy_longdouble *)(PyArray_DATA(arr)+sizeof(npy_longdouble))=(*v).i;\\
+                                      break;\\
+                case NPY_OBJECT: PyArray_SETITEM(arr, PyArray_DATA(arr), pyobj_from_complex_ ## ctype ## 1((*v))); break;\\
+                default: return -2;\\
+        };\\
+        return -1;
+"""
+# cppmacros['NUMFROMARROBJ']="""
+# define NUMFROMARROBJ(typenum,ctype) \\
+#     if (PyArray_Check(obj)) arr = (PyArrayObject *)obj;\\
+#     else arr = (PyArrayObject *)PyArray_ContiguousFromObject(obj,typenum,0,0);\\
+#     if (arr) {\\
+#         if (PyArray_TYPE(arr)==NPY_OBJECT) {\\
+#             if (!ctype ## _from_pyobj(v,(PyArray_DESCR(arr)->getitem)(PyArray_DATA(arr)),\"\"))\\
+#             goto capi_fail;\\
+#         } else {\\
+#             (PyArray_DESCR(arr)->cast[typenum])(PyArray_DATA(arr),1,(char*)v,1,1);\\
+#         }\\
+#         if ((PyObject *)arr != obj) { Py_DECREF(arr); }\\
+#         return 1;\\
+#     }
+# """
+# XXX: Note that CNUMFROMARROBJ is identical with NUMFROMARROBJ
+# cppmacros['CNUMFROMARROBJ']="""
+# define CNUMFROMARROBJ(typenum,ctype) \\
+#     if (PyArray_Check(obj)) arr = (PyArrayObject *)obj;\\
+#     else arr = (PyArrayObject *)PyArray_ContiguousFromObject(obj,typenum,0,0);\\
+#     if (arr) {\\
+#         if (PyArray_TYPE(arr)==NPY_OBJECT) {\\
+#             if (!ctype ## _from_pyobj(v,(PyArray_DESCR(arr)->getitem)(PyArray_DATA(arr)),\"\"))\\
+#             goto capi_fail;\\
+#         } else {\\
+#             (PyArray_DESCR(arr)->cast[typenum])((void *)(PyArray_DATA(arr)),1,(void *)(v),1,1);\\
+#         }\\
+#         if ((PyObject *)arr != obj) { Py_DECREF(arr); }\\
+#         return 1;\\
+#     }
+# """
+
+
+needs['GETSTRFROMPYTUPLE'] = ['STRINGCOPYN', 'PRINTPYOBJERR']
+cppmacros['GETSTRFROMPYTUPLE'] = """
+#define GETSTRFROMPYTUPLE(tuple,index,str,len) {\\
+        PyObject *rv_cb_str = PyTuple_GetItem((tuple),(index));\\
+        if (rv_cb_str == NULL)\\
+            goto capi_fail;\\
+        if (PyBytes_Check(rv_cb_str)) {\\
+            str[len-1]='\\0';\\
+            STRINGCOPYN((str),PyBytes_AS_STRING((PyBytesObject*)rv_cb_str),(len));\\
+        } else {\\
+            PRINTPYOBJERR(rv_cb_str);\\
+            PyErr_SetString(#modulename#_error,\"string object expected\");\\
+            goto capi_fail;\\
+        }\\
+    }
+"""
+cppmacros['GETSCALARFROMPYTUPLE'] = """
+#define GETSCALARFROMPYTUPLE(tuple,index,var,ctype,mess) {\\
+        if ((capi_tmp = PyTuple_GetItem((tuple),(index)))==NULL) goto capi_fail;\\
+        if (!(ctype ## _from_pyobj((var),capi_tmp,mess)))\\
+            goto capi_fail;\\
+    }
+"""
+
+cppmacros['FAILNULL'] = """\
+#define FAILNULL(p) do {                                            \\
+    if ((p) == NULL) {                                              \\
+        PyErr_SetString(PyExc_MemoryError, "NULL pointer found");   \\
+        goto capi_fail;                                             \\
+    }                                                               \\
+} while (0)
+"""
+needs['MEMCOPY'] = ['string.h', 'FAILNULL']
+cppmacros['MEMCOPY'] = """
+#define MEMCOPY(to,from,n)\\
+    do { FAILNULL(to); FAILNULL(from); (void)memcpy(to,from,n); } while (0)
+"""
+cppmacros['STRINGMALLOC'] = """
+#define STRINGMALLOC(str,len)\\
+    if ((str = (string)malloc(len+1)) == NULL) {\\
+        PyErr_SetString(PyExc_MemoryError, \"out of memory\");\\
+        goto capi_fail;\\
+    } else {\\
+        (str)[len] = '\\0';\\
+    }
+"""
+cppmacros['STRINGFREE'] = """
+#define STRINGFREE(str) do {if (!(str == NULL)) free(str);} while (0)
+"""
+needs['STRINGPADN'] = ['string.h']
+cppmacros['STRINGPADN'] = """
+/*
+STRINGPADN replaces null values with padding values from the right.
+
+`to` must have size of at least N bytes.
+
+If the `to[N-1]` has null value, then replace it and all the
+preceding, nulls with the given padding.
+
+STRINGPADN(to, N, PADDING, NULLVALUE) is an inverse operation.
+*/
+#define STRINGPADN(to, N, NULLVALUE, PADDING)                   \\
+    do {                                                        \\
+        int _m = (N);                                           \\
+        char *_to = (to);                                       \\
+        for (_m -= 1; _m >= 0 && _to[_m] == NULLVALUE; _m--) {  \\
+             _to[_m] = PADDING;                                 \\
+        }                                                       \\
+    } while (0)
+"""
+needs['STRINGCOPYN'] = ['string.h', 'FAILNULL']
+cppmacros['STRINGCOPYN'] = """
+/*
+STRINGCOPYN copies N bytes.
+
+`to` and `from` buffers must have sizes of at least N bytes.
+*/
+#define STRINGCOPYN(to,from,N)                                  \\
+    do {                                                        \\
+        int _m = (N);                                           \\
+        char *_to = (to);                                       \\
+        char *_from = (from);                                   \\
+        FAILNULL(_to); FAILNULL(_from);                         \\
+        (void)strncpy(_to, _from, _m);             \\
+    } while (0)
+"""
+needs['STRINGCOPY'] = ['string.h', 'FAILNULL']
+cppmacros['STRINGCOPY'] = """
+#define STRINGCOPY(to,from)\\
+    do { FAILNULL(to); FAILNULL(from); (void)strcpy(to,from); } while (0)
+"""
+cppmacros['CHECKGENERIC'] = """
+#define CHECKGENERIC(check,tcheck,name) \\
+    if (!(check)) {\\
+        PyErr_SetString(#modulename#_error,\"(\"tcheck\") failed for \"name);\\
+        /*goto capi_fail;*/\\
+    } else """
+cppmacros['CHECKARRAY'] = """
+#define CHECKARRAY(check,tcheck,name) \\
+    if (!(check)) {\\
+        PyErr_SetString(#modulename#_error,\"(\"tcheck\") failed for \"name);\\
+        /*goto capi_fail;*/\\
+    } else """
+cppmacros['CHECKSTRING'] = """
+#define CHECKSTRING(check,tcheck,name,show,var)\\
+    if (!(check)) {\\
+        char errstring[256];\\
+        sprintf(errstring, \"%s: \"show, \"(\"tcheck\") failed for \"name, slen(var), var);\\
+        PyErr_SetString(#modulename#_error, errstring);\\
+        /*goto capi_fail;*/\\
+    } else """
+cppmacros['CHECKSCALAR'] = """
+#define CHECKSCALAR(check,tcheck,name,show,var)\\
+    if (!(check)) {\\
+        char errstring[256];\\
+        sprintf(errstring, \"%s: \"show, \"(\"tcheck\") failed for \"name, var);\\
+        PyErr_SetString(#modulename#_error,errstring);\\
+        /*goto capi_fail;*/\\
+    } else """
+# cppmacros['CHECKDIMS']="""
+# define CHECKDIMS(dims,rank) \\
+#     for (int i=0;i<(rank);i++)\\
+#         if (dims[i]<0) {\\
+#             fprintf(stderr,\"Unspecified array argument requires a complete dimension specification.\\n\");\\
+#             goto capi_fail;\\
+#         }
+# """
+cppmacros[
+    'ARRSIZE'] = '#define ARRSIZE(dims,rank) (_PyArray_multiply_list(dims,rank))'
+cppmacros['OLDPYNUM'] = """
+#ifdef OLDPYNUM
+#error You need to install NumPy version 0.13 or higher. See https://scipy.org/install.html
+#endif
+"""
+cppmacros["F2PY_THREAD_LOCAL_DECL"] = """
+#ifndef F2PY_THREAD_LOCAL_DECL
+#if defined(_MSC_VER)
+#define F2PY_THREAD_LOCAL_DECL __declspec(thread)
+#elif defined(NPY_OS_MINGW)
+#define F2PY_THREAD_LOCAL_DECL __thread
+#elif defined(__STDC_VERSION__) \\
+      && (__STDC_VERSION__ >= 201112L) \\
+      && !defined(__STDC_NO_THREADS__) \\
+      && (!defined(__GLIBC__) || __GLIBC__ > 2 || (__GLIBC__ == 2 && __GLIBC_MINOR__ > 12)) \\
+      && !defined(NPY_OS_OPENBSD) && !defined(NPY_OS_HAIKU)
+/* __STDC_NO_THREADS__ was first defined in a maintenance release of glibc 2.12,
+   see https://lists.gnu.org/archive/html/commit-hurd/2012-07/msg00180.html,
+   so `!defined(__STDC_NO_THREADS__)` may give false positive for the existence
+   of `threads.h` when using an older release of glibc 2.12
+   See gh-19437 for details on OpenBSD */
+#include <threads.h>
+#define F2PY_THREAD_LOCAL_DECL thread_local
+#elif defined(__GNUC__) \\
+      && (__GNUC__ > 4 || (__GNUC__ == 4 && (__GNUC_MINOR__ >= 4)))
+#define F2PY_THREAD_LOCAL_DECL __thread
+#endif
+#endif
+"""
+################# C functions ###############
+
+cfuncs['calcarrindex'] = """
+static int calcarrindex(int *i,PyArrayObject *arr) {
+    int k,ii = i[0];
+    for (k=1; k < PyArray_NDIM(arr); k++)
+        ii += (ii*(PyArray_DIM(arr,k) - 1)+i[k]); /* assuming contiguous arr */
+    return ii;
+}"""
+cfuncs['calcarrindextr'] = """
+static int calcarrindextr(int *i,PyArrayObject *arr) {
+    int k,ii = i[PyArray_NDIM(arr)-1];
+    for (k=1; k < PyArray_NDIM(arr); k++)
+        ii += (ii*(PyArray_DIM(arr,PyArray_NDIM(arr)-k-1) - 1)+i[PyArray_NDIM(arr)-k-1]); /* assuming contiguous arr */
+    return ii;
+}"""
+cfuncs['forcomb'] = """
+static struct { int nd;npy_intp *d;int *i,*i_tr,tr; } forcombcache;
+static int initforcomb(npy_intp *dims,int nd,int tr) {
+  int k;
+  if (dims==NULL) return 0;
+  if (nd<0) return 0;
+  forcombcache.nd = nd;
+  forcombcache.d = dims;
+  forcombcache.tr = tr;
+  if ((forcombcache.i = (int *)malloc(sizeof(int)*nd))==NULL) return 0;
+  if ((forcombcache.i_tr = (int *)malloc(sizeof(int)*nd))==NULL) return 0;
+  for (k=1;k<nd;k++) {
+    forcombcache.i[k] = forcombcache.i_tr[nd-k-1] = 0;
+  }
+  forcombcache.i[0] = forcombcache.i_tr[nd-1] = -1;
+  return 1;
+}
+static int *nextforcomb(void) {
+  int j,*i,*i_tr,k;
+  int nd=forcombcache.nd;
+  if ((i=forcombcache.i) == NULL) return NULL;
+  if ((i_tr=forcombcache.i_tr) == NULL) return NULL;
+  if (forcombcache.d == NULL) return NULL;
+  i[0]++;
+  if (i[0]==forcombcache.d[0]) {
+    j=1;
+    while ((j<nd) && (i[j]==forcombcache.d[j]-1)) j++;
+    if (j==nd) {
+      free(i);
+      free(i_tr);
+      return NULL;
+    }
+    for (k=0;k<j;k++) i[k] = i_tr[nd-k-1] = 0;
+    i[j]++;
+    i_tr[nd-j-1]++;
+  } else
+    i_tr[nd-1]++;
+  if (forcombcache.tr) return i_tr;
+  return i;
+}"""
+needs['try_pyarr_from_string'] = ['STRINGCOPYN', 'PRINTPYOBJERR', 'string']
+cfuncs['try_pyarr_from_string'] = """
+/*
+  try_pyarr_from_string copies str[:len(obj)] to the data of an `ndarray`.
+
+  If obj is an `ndarray`, it is assumed to be contiguous.
+
+  If the specified len==-1, str must be null-terminated.
+*/
+static int try_pyarr_from_string(PyObject *obj,
+                                 const string str, const int len) {
+#ifdef DEBUGCFUNCS
+fprintf(stderr, "try_pyarr_from_string(str='%s', len=%d, obj=%p)\\n",
+        (char*)str,len, obj);
+#endif
+    if (!obj) return -2; /* Object missing */
+    if (obj == Py_None) return -1; /* None */
+    if (!PyArray_Check(obj)) goto capi_fail; /* not an ndarray */
+    if (PyArray_Check(obj)) {
+        PyArrayObject *arr = (PyArrayObject *)obj;
+        assert(ISCONTIGUOUS(arr));
+        string buf = PyArray_DATA(arr);
+        npy_intp n = len;
+        if (n == -1) {
+            /* Assuming null-terminated str. */
+            n = strlen(str);
+        }
+        if (n > PyArray_NBYTES(arr)) {
+            n = PyArray_NBYTES(arr);
+        }
+        STRINGCOPYN(buf, str, n);
+        return 1;
+    }
+capi_fail:
+    PRINTPYOBJERR(obj);
+    PyErr_SetString(#modulename#_error, \"try_pyarr_from_string failed\");
+    return 0;
+}
+"""
+needs['string_from_pyobj'] = ['string', 'STRINGMALLOC', 'STRINGCOPYN']
+cfuncs['string_from_pyobj'] = """
+/*
+  Create a new string buffer `str` of at most length `len` from a
+  Python string-like object `obj`.
+
+  The string buffer has given size (len) or the size of inistr when len==-1.
+
+  The string buffer is padded with blanks: in Fortran, trailing blanks
+  are insignificant contrary to C nulls.
+ */
+static int
+string_from_pyobj(string *str, int *len, const string inistr, PyObject *obj,
+                  const char *errmess)
+{
+    PyObject *tmp = NULL;
+    string buf = NULL;
+    npy_intp n = -1;
+#ifdef DEBUGCFUNCS
+fprintf(stderr,\"string_from_pyobj(str='%s',len=%d,inistr='%s',obj=%p)\\n\",
+               (char*)str, *len, (char *)inistr, obj);
+#endif
+    if (obj == Py_None) {
+        n = strlen(inistr);
+        buf = inistr;
+    }
+    else if (PyArray_Check(obj)) {
+        PyArrayObject *arr = (PyArrayObject *)obj;
+        if (!ISCONTIGUOUS(arr)) {
+            PyErr_SetString(PyExc_ValueError,
+                            \"array object is non-contiguous.\");
+            goto capi_fail;
+        }
+        n = PyArray_NBYTES(arr);
+        buf = PyArray_DATA(arr);
+        n = strnlen(buf, n);
+    }
+    else {
+        if (PyBytes_Check(obj)) {
+            tmp = obj;
+            Py_INCREF(tmp);
+        }
+        else if (PyUnicode_Check(obj)) {
+            tmp = PyUnicode_AsASCIIString(obj);
+        }
+        else {
+            PyObject *tmp2;
+            tmp2 = PyObject_Str(obj);
+            if (tmp2) {
+                tmp = PyUnicode_AsASCIIString(tmp2);
+                Py_DECREF(tmp2);
+            }
+            else {
+                tmp = NULL;
+            }
+        }
+        if (tmp == NULL) goto capi_fail;
+        n = PyBytes_GET_SIZE(tmp);
+        buf = PyBytes_AS_STRING(tmp);
+    }
+    if (*len == -1) {
+        /* TODO: change the type of `len` so that we can remove this */
+        if (n > NPY_MAX_INT) {
+            PyErr_SetString(PyExc_OverflowError,
+                            "object too large for a 32-bit int");
+            goto capi_fail;
+        }
+        *len = n;
+    }
+    else if (*len < n) {
+        /* discard the last (len-n) bytes of input buf */
+        n = *len;
+    }
+    if (n < 0 || *len < 0 || buf == NULL) {
+        goto capi_fail;
+    }
+    STRINGMALLOC(*str, *len);  // *str is allocated with size (*len + 1)
+    if (n < *len) {
+        /*
+          Pad fixed-width string with nulls. The caller will replace
+          nulls with blanks when the corresponding argument is not
+          intent(c).
+        */
+        memset(*str + n, '\\0', *len - n);
+    }
+    STRINGCOPYN(*str, buf, n);
+    Py_XDECREF(tmp);
+    return 1;
+capi_fail:
+    Py_XDECREF(tmp);
+    {
+        PyObject* err = PyErr_Occurred();
+        if (err == NULL) {
+            err = #modulename#_error;
+        }
+        PyErr_SetString(err, errmess);
+    }
+    return 0;
+}
+"""
+
+cfuncs['character_from_pyobj'] = """
+static int
+character_from_pyobj(character* v, PyObject *obj, const char *errmess) {
+    if (PyBytes_Check(obj)) {
+        /* empty bytes has trailing null, so dereferencing is always safe */
+        *v = PyBytes_AS_STRING(obj)[0];
+        return 1;
+    } else if (PyUnicode_Check(obj)) {
+        PyObject* tmp = PyUnicode_AsASCIIString(obj);
+        if (tmp != NULL) {
+            *v = PyBytes_AS_STRING(tmp)[0];
+            Py_DECREF(tmp);
+            return 1;
+        }
+    } else if (PyArray_Check(obj)) {
+        PyArrayObject* arr = (PyArrayObject*)obj;
+        if (F2PY_ARRAY_IS_CHARACTER_COMPATIBLE(arr)) {
+            *v = PyArray_BYTES(arr)[0];
+            return 1;
+        } else if (F2PY_IS_UNICODE_ARRAY(arr)) {
+            // TODO: update when numpy will support 1-byte and
+            // 2-byte unicode dtypes
+            PyObject* tmp = PyUnicode_FromKindAndData(
+                              PyUnicode_4BYTE_KIND,
+                              PyArray_BYTES(arr),
+                              (PyArray_NBYTES(arr)>0?1:0));
+            if (tmp != NULL) {
+                if (character_from_pyobj(v, tmp, errmess)) {
+                    Py_DECREF(tmp);
+                    return 1;
+                }
+                Py_DECREF(tmp);
+            }
+        }
+    } else if (PySequence_Check(obj)) {
+        PyObject* tmp = PySequence_GetItem(obj,0);
+        if (tmp != NULL) {
+            if (character_from_pyobj(v, tmp, errmess)) {
+                Py_DECREF(tmp);
+                return 1;
+            }
+            Py_DECREF(tmp);
+        }
+    }
+    {
+        /* TODO: This error (and most other) error handling needs cleaning. */
+        char mess[F2PY_MESSAGE_BUFFER_SIZE];
+        strcpy(mess, errmess);
+        PyObject* err = PyErr_Occurred();
+        if (err == NULL) {
+            err = PyExc_TypeError;
+            Py_INCREF(err);
+        }
+        else {
+            Py_INCREF(err);
+            PyErr_Clear();
+        }
+        sprintf(mess + strlen(mess),
+                " -- expected str|bytes|sequence-of-str-or-bytes, got ");
+        f2py_describe(obj, mess + strlen(mess));
+        PyErr_SetString(err, mess);
+        Py_DECREF(err);
+    }
+    return 0;
+}
+"""
+
+# TODO: These should be dynamically generated, too many mapped to int things,
+# see note in _isocbind.py
+needs['char_from_pyobj'] = ['int_from_pyobj']
+cfuncs['char_from_pyobj'] = """
+static int
+char_from_pyobj(char* v, PyObject *obj, const char *errmess) {
+    int i = 0;
+    if (int_from_pyobj(&i, obj, errmess)) {
+        *v = (char)i;
+        return 1;
+    }
+    return 0;
+}
+"""
+
+
+needs['signed_char_from_pyobj'] = ['int_from_pyobj', 'signed_char']
+cfuncs['signed_char_from_pyobj'] = """
+static int
+signed_char_from_pyobj(signed_char* v, PyObject *obj, const char *errmess) {
+    int i = 0;
+    if (int_from_pyobj(&i, obj, errmess)) {
+        *v = (signed_char)i;
+        return 1;
+    }
+    return 0;
+}
+"""
+
+
+needs['short_from_pyobj'] = ['int_from_pyobj']
+cfuncs['short_from_pyobj'] = """
+static int
+short_from_pyobj(short* v, PyObject *obj, const char *errmess) {
+    int i = 0;
+    if (int_from_pyobj(&i, obj, errmess)) {
+        *v = (short)i;
+        return 1;
+    }
+    return 0;
+}
+"""
+
+
+cfuncs['int_from_pyobj'] = """
+static int
+int_from_pyobj(int* v, PyObject *obj, const char *errmess)
+{
+    PyObject* tmp = NULL;
+
+    if (PyLong_Check(obj)) {
+        *v = Npy__PyLong_AsInt(obj);
+        return !(*v == -1 && PyErr_Occurred());
+    }
+
+    tmp = PyNumber_Long(obj);
+    if (tmp) {
+        *v = Npy__PyLong_AsInt(tmp);
+        Py_DECREF(tmp);
+        return !(*v == -1 && PyErr_Occurred());
+    }
+
+    if (PyComplex_Check(obj)) {
+        PyErr_Clear();
+        tmp = PyObject_GetAttrString(obj,\"real\");
+    }
+    else if (PyBytes_Check(obj) || PyUnicode_Check(obj)) {
+        /*pass*/;
+    }
+    else if (PySequence_Check(obj)) {
+        PyErr_Clear();
+        tmp = PySequence_GetItem(obj, 0);
+    }
+
+    if (tmp) {
+        if (int_from_pyobj(v, tmp, errmess)) {
+            Py_DECREF(tmp);
+            return 1;
+        }
+        Py_DECREF(tmp);
+    }
+
+    {
+        PyObject* err = PyErr_Occurred();
+        if (err == NULL) {
+            err = #modulename#_error;
+        }
+        PyErr_SetString(err, errmess);
+    }
+    return 0;
+}
+"""
+
+
+cfuncs['long_from_pyobj'] = """
+static int
+long_from_pyobj(long* v, PyObject *obj, const char *errmess) {
+    PyObject* tmp = NULL;
+
+    if (PyLong_Check(obj)) {
+        *v = PyLong_AsLong(obj);
+        return !(*v == -1 && PyErr_Occurred());
+    }
+
+    tmp = PyNumber_Long(obj);
+    if (tmp) {
+        *v = PyLong_AsLong(tmp);
+        Py_DECREF(tmp);
+        return !(*v == -1 && PyErr_Occurred());
+    }
+
+    if (PyComplex_Check(obj)) {
+        PyErr_Clear();
+        tmp = PyObject_GetAttrString(obj,\"real\");
+    }
+    else if (PyBytes_Check(obj) || PyUnicode_Check(obj)) {
+        /*pass*/;
+    }
+    else if (PySequence_Check(obj)) {
+        PyErr_Clear();
+        tmp = PySequence_GetItem(obj, 0);
+    }
+
+    if (tmp) {
+        if (long_from_pyobj(v, tmp, errmess)) {
+            Py_DECREF(tmp);
+            return 1;
+        }
+        Py_DECREF(tmp);
+    }
+    {
+        PyObject* err = PyErr_Occurred();
+        if (err == NULL) {
+            err = #modulename#_error;
+        }
+        PyErr_SetString(err, errmess);
+    }
+    return 0;
+}
+"""
+
+
+needs['long_long_from_pyobj'] = ['long_long']
+cfuncs['long_long_from_pyobj'] = """
+static int
+long_long_from_pyobj(long_long* v, PyObject *obj, const char *errmess)
+{
+    PyObject* tmp = NULL;
+
+    if (PyLong_Check(obj)) {
+        *v = PyLong_AsLongLong(obj);
+        return !(*v == -1 && PyErr_Occurred());
+    }
+
+    tmp = PyNumber_Long(obj);
+    if (tmp) {
+        *v = PyLong_AsLongLong(tmp);
+        Py_DECREF(tmp);
+        return !(*v == -1 && PyErr_Occurred());
+    }
+
+    if (PyComplex_Check(obj)) {
+        PyErr_Clear();
+        tmp = PyObject_GetAttrString(obj,\"real\");
+    }
+    else if (PyBytes_Check(obj) || PyUnicode_Check(obj)) {
+        /*pass*/;
+    }
+    else if (PySequence_Check(obj)) {
+        PyErr_Clear();
+        tmp = PySequence_GetItem(obj, 0);
+    }
+
+    if (tmp) {
+        if (long_long_from_pyobj(v, tmp, errmess)) {
+            Py_DECREF(tmp);
+            return 1;
+        }
+        Py_DECREF(tmp);
+    }
+    {
+        PyObject* err = PyErr_Occurred();
+        if (err == NULL) {
+            err = #modulename#_error;
+        }
+        PyErr_SetString(err,errmess);
+    }
+    return 0;
+}
+"""
+
+
+needs['long_double_from_pyobj'] = ['double_from_pyobj', 'long_double']
+cfuncs['long_double_from_pyobj'] = """
+static int
+long_double_from_pyobj(long_double* v, PyObject *obj, const char *errmess)
+{
+    double d=0;
+    if (PyArray_CheckScalar(obj)){
+        if PyArray_IsScalar(obj, LongDouble) {
+            PyArray_ScalarAsCtype(obj, v);
+            return 1;
+        }
+        else if (PyArray_Check(obj) && PyArray_TYPE(obj) == NPY_LONGDOUBLE) {
+            (*v) = *((npy_longdouble *)PyArray_DATA(obj));
+            return 1;
+        }
+    }
+    if (double_from_pyobj(&d, obj, errmess)) {
+        *v = (long_double)d;
+        return 1;
+    }
+    return 0;
+}
+"""
+
+
+cfuncs['double_from_pyobj'] = """
+static int
+double_from_pyobj(double* v, PyObject *obj, const char *errmess)
+{
+    PyObject* tmp = NULL;
+    if (PyFloat_Check(obj)) {
+        *v = PyFloat_AsDouble(obj);
+        return !(*v == -1.0 && PyErr_Occurred());
+    }
+
+    tmp = PyNumber_Float(obj);
+    if (tmp) {
+        *v = PyFloat_AsDouble(tmp);
+        Py_DECREF(tmp);
+        return !(*v == -1.0 && PyErr_Occurred());
+    }
+
+    if (PyComplex_Check(obj)) {
+        PyErr_Clear();
+        tmp = PyObject_GetAttrString(obj,\"real\");
+    }
+    else if (PyBytes_Check(obj) || PyUnicode_Check(obj)) {
+        /*pass*/;
+    }
+    else if (PySequence_Check(obj)) {
+        PyErr_Clear();
+        tmp = PySequence_GetItem(obj, 0);
+    }
+
+    if (tmp) {
+        if (double_from_pyobj(v,tmp,errmess)) {Py_DECREF(tmp); return 1;}
+        Py_DECREF(tmp);
+    }
+    {
+        PyObject* err = PyErr_Occurred();
+        if (err==NULL) err = #modulename#_error;
+        PyErr_SetString(err,errmess);
+    }
+    return 0;
+}
+"""
+
+
+needs['float_from_pyobj'] = ['double_from_pyobj']
+cfuncs['float_from_pyobj'] = """
+static int
+float_from_pyobj(float* v, PyObject *obj, const char *errmess)
+{
+    double d=0.0;
+    if (double_from_pyobj(&d,obj,errmess)) {
+        *v = (float)d;
+        return 1;
+    }
+    return 0;
+}
+"""
+
+
+needs['complex_long_double_from_pyobj'] = ['complex_long_double', 'long_double',
+                                           'complex_double_from_pyobj', 'npy_math.h']
+cfuncs['complex_long_double_from_pyobj'] = """
+static int
+complex_long_double_from_pyobj(complex_long_double* v, PyObject *obj, const char *errmess)
+{
+    complex_double cd = {0.0,0.0};
+    if (PyArray_CheckScalar(obj)){
+        if PyArray_IsScalar(obj, CLongDouble) {
+            PyArray_ScalarAsCtype(obj, v);
+            return 1;
+        }
+        else if (PyArray_Check(obj) && PyArray_TYPE(obj)==NPY_CLONGDOUBLE) {
+            (*v).r = npy_creall(*(((npy_clongdouble *)PyArray_DATA(obj))));
+            (*v).i = npy_cimagl(*(((npy_clongdouble *)PyArray_DATA(obj))));
+            return 1;
+        }
+    }
+    if (complex_double_from_pyobj(&cd,obj,errmess)) {
+        (*v).r = (long_double)cd.r;
+        (*v).i = (long_double)cd.i;
+        return 1;
+    }
+    return 0;
+}
+"""
+
+
+needs['complex_double_from_pyobj'] = ['complex_double', 'npy_math.h']
+cfuncs['complex_double_from_pyobj'] = """
+static int
+complex_double_from_pyobj(complex_double* v, PyObject *obj, const char *errmess) {
+    Py_complex c;
+    if (PyComplex_Check(obj)) {
+        c = PyComplex_AsCComplex(obj);
+        (*v).r = c.real;
+        (*v).i = c.imag;
+        return 1;
+    }
+    if (PyArray_IsScalar(obj, ComplexFloating)) {
+        if (PyArray_IsScalar(obj, CFloat)) {
+            npy_cfloat new;
+            PyArray_ScalarAsCtype(obj, &new);
+            (*v).r = (double)npy_crealf(new);
+            (*v).i = (double)npy_cimagf(new);
+        }
+        else if (PyArray_IsScalar(obj, CLongDouble)) {
+            npy_clongdouble new;
+            PyArray_ScalarAsCtype(obj, &new);
+            (*v).r = (double)npy_creall(new);
+            (*v).i = (double)npy_cimagl(new);
+        }
+        else { /* if (PyArray_IsScalar(obj, CDouble)) */
+            PyArray_ScalarAsCtype(obj, v);
+        }
+        return 1;
+    }
+    if (PyArray_CheckScalar(obj)) { /* 0-dim array or still array scalar */
+        PyArrayObject *arr;
+        if (PyArray_Check(obj)) {
+            arr = (PyArrayObject *)PyArray_Cast((PyArrayObject *)obj, NPY_CDOUBLE);
+        }
+        else {
+            arr = (PyArrayObject *)PyArray_FromScalar(obj, PyArray_DescrFromType(NPY_CDOUBLE));
+        }
+        if (arr == NULL) {
+            return 0;
+        }
+        (*v).r = npy_creal(*(((npy_cdouble *)PyArray_DATA(arr))));
+        (*v).i = npy_cimag(*(((npy_cdouble *)PyArray_DATA(arr))));
+        Py_DECREF(arr);
+        return 1;
+    }
+    /* Python does not provide PyNumber_Complex function :-( */
+    (*v).i = 0.0;
+    if (PyFloat_Check(obj)) {
+        (*v).r = PyFloat_AsDouble(obj);
+        return !((*v).r == -1.0 && PyErr_Occurred());
+    }
+    if (PyLong_Check(obj)) {
+        (*v).r = PyLong_AsDouble(obj);
+        return !((*v).r == -1.0 && PyErr_Occurred());
+    }
+    if (PySequence_Check(obj) && !(PyBytes_Check(obj) || PyUnicode_Check(obj))) {
+        PyObject *tmp = PySequence_GetItem(obj,0);
+        if (tmp) {
+            if (complex_double_from_pyobj(v,tmp,errmess)) {
+                Py_DECREF(tmp);
+                return 1;
+            }
+            Py_DECREF(tmp);
+        }
+    }
+    {
+        PyObject* err = PyErr_Occurred();
+        if (err==NULL)
+            err = PyExc_TypeError;
+        PyErr_SetString(err,errmess);
+    }
+    return 0;
+}
+"""
+
+
+needs['complex_float_from_pyobj'] = [
+    'complex_float', 'complex_double_from_pyobj']
+cfuncs['complex_float_from_pyobj'] = """
+static int
+complex_float_from_pyobj(complex_float* v,PyObject *obj,const char *errmess)
+{
+    complex_double cd={0.0,0.0};
+    if (complex_double_from_pyobj(&cd,obj,errmess)) {
+        (*v).r = (float)cd.r;
+        (*v).i = (float)cd.i;
+        return 1;
+    }
+    return 0;
+}
+"""
+
+
+cfuncs['try_pyarr_from_character'] = """
+static int try_pyarr_from_character(PyObject* obj, character* v) {
+    PyArrayObject *arr = (PyArrayObject*)obj;
+    if (!obj) return -2;
+    if (PyArray_Check(obj)) {
+        if (F2PY_ARRAY_IS_CHARACTER_COMPATIBLE(arr))  {
+            *(character *)(PyArray_DATA(arr)) = *v;
+            return 1;
+        }
+    }
+    {
+        char mess[F2PY_MESSAGE_BUFFER_SIZE];
+        PyObject* err = PyErr_Occurred();
+        if (err == NULL) {
+            err = PyExc_ValueError;
+            strcpy(mess, "try_pyarr_from_character failed"
+                         " -- expected bytes array-scalar|array, got ");
+            f2py_describe(obj, mess + strlen(mess));
+            PyErr_SetString(err, mess);
+        }
+    }
+    return 0;
+}
+"""
+
+needs['try_pyarr_from_char'] = ['pyobj_from_char1', 'TRYPYARRAYTEMPLATE']
+cfuncs[
+    'try_pyarr_from_char'] = 'static int try_pyarr_from_char(PyObject* obj,char* v) {\n    TRYPYARRAYTEMPLATE(char,\'c\');\n}\n'
+needs['try_pyarr_from_signed_char'] = ['TRYPYARRAYTEMPLATE', 'unsigned_char']
+cfuncs[
+    'try_pyarr_from_unsigned_char'] = 'static int try_pyarr_from_unsigned_char(PyObject* obj,unsigned_char* v) {\n    TRYPYARRAYTEMPLATE(unsigned_char,\'b\');\n}\n'
+needs['try_pyarr_from_signed_char'] = ['TRYPYARRAYTEMPLATE', 'signed_char']
+cfuncs[
+    'try_pyarr_from_signed_char'] = 'static int try_pyarr_from_signed_char(PyObject* obj,signed_char* v) {\n    TRYPYARRAYTEMPLATE(signed_char,\'1\');\n}\n'
+needs['try_pyarr_from_short'] = ['pyobj_from_short1', 'TRYPYARRAYTEMPLATE']
+cfuncs[
+    'try_pyarr_from_short'] = 'static int try_pyarr_from_short(PyObject* obj,short* v) {\n    TRYPYARRAYTEMPLATE(short,\'s\');\n}\n'
+needs['try_pyarr_from_int'] = ['pyobj_from_int1', 'TRYPYARRAYTEMPLATE']
+cfuncs[
+    'try_pyarr_from_int'] = 'static int try_pyarr_from_int(PyObject* obj,int* v) {\n    TRYPYARRAYTEMPLATE(int,\'i\');\n}\n'
+needs['try_pyarr_from_long'] = ['pyobj_from_long1', 'TRYPYARRAYTEMPLATE']
+cfuncs[
+    'try_pyarr_from_long'] = 'static int try_pyarr_from_long(PyObject* obj,long* v) {\n    TRYPYARRAYTEMPLATE(long,\'l\');\n}\n'
+needs['try_pyarr_from_long_long'] = [
+    'pyobj_from_long_long1', 'TRYPYARRAYTEMPLATE', 'long_long']
+cfuncs[
+    'try_pyarr_from_long_long'] = 'static int try_pyarr_from_long_long(PyObject* obj,long_long* v) {\n    TRYPYARRAYTEMPLATE(long_long,\'L\');\n}\n'
+needs['try_pyarr_from_float'] = ['pyobj_from_float1', 'TRYPYARRAYTEMPLATE']
+cfuncs[
+    'try_pyarr_from_float'] = 'static int try_pyarr_from_float(PyObject* obj,float* v) {\n    TRYPYARRAYTEMPLATE(float,\'f\');\n}\n'
+needs['try_pyarr_from_double'] = ['pyobj_from_double1', 'TRYPYARRAYTEMPLATE']
+cfuncs[
+    'try_pyarr_from_double'] = 'static int try_pyarr_from_double(PyObject* obj,double* v) {\n    TRYPYARRAYTEMPLATE(double,\'d\');\n}\n'
+needs['try_pyarr_from_complex_float'] = [
+    'pyobj_from_complex_float1', 'TRYCOMPLEXPYARRAYTEMPLATE', 'complex_float']
+cfuncs[
+    'try_pyarr_from_complex_float'] = 'static int try_pyarr_from_complex_float(PyObject* obj,complex_float* v) {\n    TRYCOMPLEXPYARRAYTEMPLATE(float,\'F\');\n}\n'
+needs['try_pyarr_from_complex_double'] = [
+    'pyobj_from_complex_double1', 'TRYCOMPLEXPYARRAYTEMPLATE', 'complex_double']
+cfuncs[
+    'try_pyarr_from_complex_double'] = 'static int try_pyarr_from_complex_double(PyObject* obj,complex_double* v) {\n    TRYCOMPLEXPYARRAYTEMPLATE(double,\'D\');\n}\n'
+
+
+needs['create_cb_arglist'] = ['CFUNCSMESS', 'PRINTPYOBJERR', 'MINMAX']
+# create the list of arguments to be used when calling back to python
+cfuncs['create_cb_arglist'] = """
+static int
+create_cb_arglist(PyObject* fun, PyTupleObject* xa , const int maxnofargs,
+                  const int nofoptargs, int *nofargs, PyTupleObject **args,
+                  const char *errmess)
+{
+    PyObject *tmp = NULL;
+    PyObject *tmp_fun = NULL;
+    Py_ssize_t tot, opt, ext, siz, i, di = 0;
+    CFUNCSMESS(\"create_cb_arglist\\n\");
+    tot=opt=ext=siz=0;
+    /* Get the total number of arguments */
+    if (PyFunction_Check(fun)) {
+        tmp_fun = fun;
+        Py_INCREF(tmp_fun);
+    }
+    else {
+        di = 1;
+        if (PyObject_HasAttrString(fun,\"im_func\")) {
+            tmp_fun = PyObject_GetAttrString(fun,\"im_func\");
+        }
+        else if (PyObject_HasAttrString(fun,\"__call__\")) {
+            tmp = PyObject_GetAttrString(fun,\"__call__\");
+            if (PyObject_HasAttrString(tmp,\"im_func\"))
+                tmp_fun = PyObject_GetAttrString(tmp,\"im_func\");
+            else {
+                tmp_fun = fun; /* built-in function */
+                Py_INCREF(tmp_fun);
+                tot = maxnofargs;
+                if (PyCFunction_Check(fun)) {
+                    /* In case the function has a co_argcount (like on PyPy) */
+                    di = 0;
+                }
+                if (xa != NULL)
+                    tot += PyTuple_Size((PyObject *)xa);
+            }
+            Py_XDECREF(tmp);
+        }
+        else if (PyFortran_Check(fun) || PyFortran_Check1(fun)) {
+            tot = maxnofargs;
+            if (xa != NULL)
+                tot += PyTuple_Size((PyObject *)xa);
+            tmp_fun = fun;
+            Py_INCREF(tmp_fun);
+        }
+        else if (F2PyCapsule_Check(fun)) {
+            tot = maxnofargs;
+            if (xa != NULL)
+                ext = PyTuple_Size((PyObject *)xa);
+            if(ext>0) {
+                fprintf(stderr,\"extra arguments tuple cannot be used with PyCapsule call-back\\n\");
+                goto capi_fail;
+            }
+            tmp_fun = fun;
+            Py_INCREF(tmp_fun);
+        }
+    }
+
+    if (tmp_fun == NULL) {
+        fprintf(stderr,
+                \"Call-back argument must be function|instance|instance.__call__|f2py-function \"
+                \"but got %s.\\n\",
+                ((fun == NULL) ? \"NULL\" : Py_TYPE(fun)->tp_name));
+        goto capi_fail;
+    }
+
+    if (PyObject_HasAttrString(tmp_fun,\"__code__\")) {
+        if (PyObject_HasAttrString(tmp = PyObject_GetAttrString(tmp_fun,\"__code__\"),\"co_argcount\")) {
+            PyObject *tmp_argcount = PyObject_GetAttrString(tmp,\"co_argcount\");
+            Py_DECREF(tmp);
+            if (tmp_argcount == NULL) {
+                goto capi_fail;
+            }
+            tot = PyLong_AsSsize_t(tmp_argcount) - di;
+            Py_DECREF(tmp_argcount);
+        }
+    }
+    /* Get the number of optional arguments */
+    if (PyObject_HasAttrString(tmp_fun,\"__defaults__\")) {
+        if (PyTuple_Check(tmp = PyObject_GetAttrString(tmp_fun,\"__defaults__\")))
+            opt = PyTuple_Size(tmp);
+        Py_XDECREF(tmp);
+    }
+    /* Get the number of extra arguments */
+    if (xa != NULL)
+        ext = PyTuple_Size((PyObject *)xa);
+    /* Calculate the size of call-backs argument list */
+    siz = MIN(maxnofargs+ext,tot);
+    *nofargs = MAX(0,siz-ext);
+
+#ifdef DEBUGCFUNCS
+    fprintf(stderr,
+            \"debug-capi:create_cb_arglist:maxnofargs(-nofoptargs),\"
+            \"tot,opt,ext,siz,nofargs = %d(-%d), %zd, %zd, %zd, %zd, %d\\n\",
+            maxnofargs, nofoptargs, tot, opt, ext, siz, *nofargs);
+#endif
+
+    if (siz < tot-opt) {
+        fprintf(stderr,
+                \"create_cb_arglist: Failed to build argument list \"
+                \"(siz) with enough arguments (tot-opt) required by \"
+                \"user-supplied function (siz,tot,opt=%zd, %zd, %zd).\\n\",
+                siz, tot, opt);
+        goto capi_fail;
+    }
+
+    /* Initialize argument list */
+    *args = (PyTupleObject *)PyTuple_New(siz);
+    for (i=0;i<*nofargs;i++) {
+        Py_INCREF(Py_None);
+        PyTuple_SET_ITEM((PyObject *)(*args),i,Py_None);
+    }
+    if (xa != NULL)
+        for (i=(*nofargs);i<siz;i++) {
+            tmp = PyTuple_GetItem((PyObject *)xa,i-(*nofargs));
+            Py_INCREF(tmp);
+            PyTuple_SET_ITEM(*args,i,tmp);
+        }
+    CFUNCSMESS(\"create_cb_arglist-end\\n\");
+    Py_DECREF(tmp_fun);
+    return 1;
+
+capi_fail:
+    if (PyErr_Occurred() == NULL)
+        PyErr_SetString(#modulename#_error, errmess);
+    Py_XDECREF(tmp_fun);
+    return 0;
+}
+"""
+
+
+def buildcfuncs():
+    from .capi_maps import c2capi_map
+    for k in c2capi_map.keys():
+        m = 'pyarr_from_p_%s1' % k
+        cppmacros[
+            m] = '#define %s(v) (PyArray_SimpleNewFromData(0,NULL,%s,(char *)v))' % (m, c2capi_map[k])
+    k = 'string'
+    m = 'pyarr_from_p_%s1' % k
+    # NPY_CHAR compatibility, NPY_STRING with itemsize 1
+    cppmacros[
+        m] = '#define %s(v,dims) (PyArray_New(&PyArray_Type, 1, dims, NPY_STRING, NULL, v, 1, NPY_ARRAY_CARRAY, NULL))' % (m)
+
+
+############ Auxiliary functions for sorting needs ###################
+
+def append_needs(need, flag=1):
+    # This function modifies the contents of the global `outneeds` dict.
+    if isinstance(need, list):
+        for n in need:
+            append_needs(n, flag)
+    elif isinstance(need, str):
+        if not need:
+            return
+        if need in includes0:
+            n = 'includes0'
+        elif need in includes:
+            n = 'includes'
+        elif need in typedefs:
+            n = 'typedefs'
+        elif need in typedefs_generated:
+            n = 'typedefs_generated'
+        elif need in cppmacros:
+            n = 'cppmacros'
+        elif need in cfuncs:
+            n = 'cfuncs'
+        elif need in callbacks:
+            n = 'callbacks'
+        elif need in f90modhooks:
+            n = 'f90modhooks'
+        elif need in commonhooks:
+            n = 'commonhooks'
+        else:
+            errmess('append_needs: unknown need %s\n' % (repr(need)))
+            return
+        if need in outneeds[n]:
+            return
+        if flag:
+            tmp = {}
+            if need in needs:
+                for nn in needs[need]:
+                    t = append_needs(nn, 0)
+                    if isinstance(t, dict):
+                        for nnn in t.keys():
+                            if nnn in tmp:
+                                tmp[nnn] = tmp[nnn] + t[nnn]
+                            else:
+                                tmp[nnn] = t[nnn]
+            for nn in tmp.keys():
+                for nnn in tmp[nn]:
+                    if nnn not in outneeds[nn]:
+                        outneeds[nn] = [nnn] + outneeds[nn]
+            outneeds[n].append(need)
+        else:
+            tmp = {}
+            if need in needs:
+                for nn in needs[need]:
+                    t = append_needs(nn, flag)
+                    if isinstance(t, dict):
+                        for nnn in t.keys():
+                            if nnn in tmp:
+                                tmp[nnn] = t[nnn] + tmp[nnn]
+                            else:
+                                tmp[nnn] = t[nnn]
+            if n not in tmp:
+                tmp[n] = []
+            tmp[n].append(need)
+            return tmp
+    else:
+        errmess('append_needs: expected list or string but got :%s\n' %
+                (repr(need)))
+
+
+def get_needs():
+    # This function modifies the contents of the global `outneeds` dict.
+    res = {}
+    for n in outneeds.keys():
+        out = []
+        saveout = copy.copy(outneeds[n])
+        while len(outneeds[n]) > 0:
+            if outneeds[n][0] not in needs:
+                out.append(outneeds[n][0])
+                del outneeds[n][0]
+            else:
+                flag = 0
+                for k in outneeds[n][1:]:
+                    if k in needs[outneeds[n][0]]:
+                        flag = 1
+                        break
+                if flag:
+                    outneeds[n] = outneeds[n][1:] + [outneeds[n][0]]
+                else:
+                    out.append(outneeds[n][0])
+                    del outneeds[n][0]
+            if saveout and (0 not in map(lambda x, y: x == y, saveout, outneeds[n])) \
+                    and outneeds[n] != []:
+                print(n, saveout)
+                errmess(
+                    'get_needs: no progress in sorting needs, probably circular dependence, skipping.\n')
+                out = out + saveout
+                break
+            saveout = copy.copy(outneeds[n])
+        if out == []:
+            out = [n]
+        res[n] = out
+    return res
diff --git a/.venv/lib/python3.12/site-packages/numpy/f2py/common_rules.py b/.venv/lib/python3.12/site-packages/numpy/f2py/common_rules.py
new file mode 100644
index 00000000..64347b73
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/numpy/f2py/common_rules.py
@@ -0,0 +1,146 @@
+"""
+Build common block mechanism for f2py2e.
+
+Copyright 1999 -- 2011 Pearu Peterson all rights reserved.
+Copyright 2011 -- present NumPy Developers.
+Permission to use, modify, and distribute this software is given under the
+terms of the NumPy License
+
+NO WARRANTY IS EXPRESSED OR IMPLIED.  USE AT YOUR OWN RISK.
+"""
+from . import __version__
+f2py_version = __version__.version
+
+from .auxfuncs import (
+    hasbody, hascommon, hasnote, isintent_hide, outmess, getuseblocks
+)
+from . import capi_maps
+from . import func2subr
+from .crackfortran import rmbadname
+
+
+def findcommonblocks(block, top=1):
+    ret = []
+    if hascommon(block):
+        for key, value in block['common'].items():
+            vars_ = {v: block['vars'][v] for v in value}
+            ret.append((key, value, vars_))
+    elif hasbody(block):
+        for b in block['body']:
+            ret = ret + findcommonblocks(b, 0)
+    if top:
+        tret = []
+        names = []
+        for t in ret:
+            if t[0] not in names:
+                names.append(t[0])
+                tret.append(t)
+        return tret
+    return ret
+
+
+def buildhooks(m):
+    ret = {'commonhooks': [], 'initcommonhooks': [],
+           'docs': ['"COMMON blocks:\\n"']}
+    fwrap = ['']
+
+    def fadd(line, s=fwrap):
+        s[0] = '%s\n      %s' % (s[0], line)
+    chooks = ['']
+
+    def cadd(line, s=chooks):
+        s[0] = '%s\n%s' % (s[0], line)
+    ihooks = ['']
+
+    def iadd(line, s=ihooks):
+        s[0] = '%s\n%s' % (s[0], line)
+    doc = ['']
+
+    def dadd(line, s=doc):
+        s[0] = '%s\n%s' % (s[0], line)
+    for (name, vnames, vars) in findcommonblocks(m):
+        lower_name = name.lower()
+        hnames, inames = [], []
+        for n in vnames:
+            if isintent_hide(vars[n]):
+                hnames.append(n)
+            else:
+                inames.append(n)
+        if hnames:
+            outmess('\t\tConstructing COMMON block support for "%s"...\n\t\t  %s\n\t\t  Hidden: %s\n' % (
+                name, ','.join(inames), ','.join(hnames)))
+        else:
+            outmess('\t\tConstructing COMMON block support for "%s"...\n\t\t  %s\n' % (
+                name, ','.join(inames)))
+        fadd('subroutine f2pyinit%s(setupfunc)' % name)
+        for usename in getuseblocks(m):
+            fadd(f'use {usename}')
+        fadd('external setupfunc')
+        for n in vnames:
+            fadd(func2subr.var2fixfortran(vars, n))
+        if name == '_BLNK_':
+            fadd('common %s' % (','.join(vnames)))
+        else:
+            fadd('common /%s/ %s' % (name, ','.join(vnames)))
+        fadd('call setupfunc(%s)' % (','.join(inames)))
+        fadd('end\n')
+        cadd('static FortranDataDef f2py_%s_def[] = {' % (name))
+        idims = []
+        for n in inames:
+            ct = capi_maps.getctype(vars[n])
+            elsize = capi_maps.get_elsize(vars[n])
+            at = capi_maps.c2capi_map[ct]
+            dm = capi_maps.getarrdims(n, vars[n])
+            if dm['dims']:
+                idims.append('(%s)' % (dm['dims']))
+            else:
+                idims.append('')
+            dms = dm['dims'].strip()
+            if not dms:
+                dms = '-1'
+            cadd('\t{\"%s\",%s,{{%s}},%s, %s},'
+                 % (n, dm['rank'], dms, at, elsize))
+        cadd('\t{NULL}\n};')
+        inames1 = rmbadname(inames)
+        inames1_tps = ','.join(['char *' + s for s in inames1])
+        cadd('static void f2py_setup_%s(%s) {' % (name, inames1_tps))
+        cadd('\tint i_f2py=0;')
+        for n in inames1:
+            cadd('\tf2py_%s_def[i_f2py++].data = %s;' % (name, n))
+        cadd('}')
+        if '_' in lower_name:
+            F_FUNC = 'F_FUNC_US'
+        else:
+            F_FUNC = 'F_FUNC'
+        cadd('extern void %s(f2pyinit%s,F2PYINIT%s)(void(*)(%s));'
+             % (F_FUNC, lower_name, name.upper(),
+                ','.join(['char*'] * len(inames1))))
+        cadd('static void f2py_init_%s(void) {' % name)
+        cadd('\t%s(f2pyinit%s,F2PYINIT%s)(f2py_setup_%s);'
+             % (F_FUNC, lower_name, name.upper(), name))
+        cadd('}\n')
+        iadd('\ttmp = PyFortranObject_New(f2py_%s_def,f2py_init_%s);' % (name, name))
+        iadd('\tif (tmp == NULL) return NULL;')
+        iadd('\tif (F2PyDict_SetItemString(d, \"%s\", tmp) == -1) return NULL;'
+             % name)
+        iadd('\tPy_DECREF(tmp);')
+        tname = name.replace('_', '\\_')
+        dadd('\\subsection{Common block \\texttt{%s}}\n' % (tname))
+        dadd('\\begin{description}')
+        for n in inames:
+            dadd('\\item[]{{}\\verb@%s@{}}' %
+                 (capi_maps.getarrdocsign(n, vars[n])))
+            if hasnote(vars[n]):
+                note = vars[n]['note']
+                if isinstance(note, list):
+                    note = '\n'.join(note)
+                dadd('--- %s' % (note))
+        dadd('\\end{description}')
+        ret['docs'].append(
+            '"\t/%s/ %s\\n"' % (name, ','.join(map(lambda v, d: v + d, inames, idims))))
+    ret['commonhooks'] = chooks
+    ret['initcommonhooks'] = ihooks
+    ret['latexdoc'] = doc[0]
+    if len(ret['docs']) <= 1:
+        ret['docs'] = ''
+    return ret, fwrap[0]
diff --git a/.venv/lib/python3.12/site-packages/numpy/f2py/crackfortran.py b/.venv/lib/python3.12/site-packages/numpy/f2py/crackfortran.py
new file mode 100755
index 00000000..8d3fc276
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/numpy/f2py/crackfortran.py
@@ -0,0 +1,3767 @@
+#!/usr/bin/env python3
+"""
+crackfortran --- read fortran (77,90) code and extract declaration information.
+
+Copyright 1999 -- 2011 Pearu Peterson all rights reserved.
+Copyright 2011 -- present NumPy Developers.
+Permission to use, modify, and distribute this software is given under the
+terms of the NumPy License.
+
+NO WARRANTY IS EXPRESSED OR IMPLIED.  USE AT YOUR OWN RISK.
+
+
+Usage of crackfortran:
+======================
+Command line keys: -quiet,-verbose,-fix,-f77,-f90,-show,-h <pyffilename>
+                   -m <module name for f77 routines>,--ignore-contains
+Functions: crackfortran, crack2fortran
+The following Fortran statements/constructions are supported
+(or will be if needed):
+   block data,byte,call,character,common,complex,contains,data,
+   dimension,double complex,double precision,end,external,function,
+   implicit,integer,intent,interface,intrinsic,
+   logical,module,optional,parameter,private,public,
+   program,real,(sequence?),subroutine,type,use,virtual,
+   include,pythonmodule
+Note: 'virtual' is mapped to 'dimension'.
+Note: 'implicit integer (z) static (z)' is 'implicit static (z)' (this is minor bug).
+Note: code after 'contains' will be ignored until its scope ends.
+Note: 'common' statement is extended: dimensions are moved to variable definitions
+Note: f2py directive: <commentchar>f2py<line> is read as <line>
+Note: pythonmodule is introduced to represent Python module
+
+Usage:
+  `postlist=crackfortran(files)`
+  `postlist` contains declaration information read from the list of files `files`.
+  `crack2fortran(postlist)` returns a fortran code to be saved to pyf-file
+
+  `postlist` has the following structure:
+ *** it is a list of dictionaries containing `blocks':
+     B = {'block','body','vars','parent_block'[,'name','prefix','args','result',
+          'implicit','externals','interfaced','common','sortvars',
+          'commonvars','note']}
+     B['block'] = 'interface' | 'function' | 'subroutine' | 'module' |
+                  'program' | 'block data' | 'type' | 'pythonmodule' |
+                  'abstract interface'
+     B['body'] --- list containing `subblocks' with the same structure as `blocks'
+     B['parent_block'] --- dictionary of a parent block:
+                             C['body'][<index>]['parent_block'] is C
+     B['vars'] --- dictionary of variable definitions
+     B['sortvars'] --- dictionary of variable definitions sorted by dependence (independent first)
+     B['name'] --- name of the block (not if B['block']=='interface')
+     B['prefix'] --- prefix string (only if B['block']=='function')
+     B['args'] --- list of argument names if B['block']== 'function' | 'subroutine'
+     B['result'] --- name of the return value (only if B['block']=='function')
+     B['implicit'] --- dictionary {'a':<variable definition>,'b':...} | None
+     B['externals'] --- list of variables being external
+     B['interfaced'] --- list of variables being external and defined
+     B['common'] --- dictionary of common blocks (list of objects)
+     B['commonvars'] --- list of variables used in common blocks (dimensions are moved to variable definitions)
+     B['from'] --- string showing the 'parents' of the current block
+     B['use'] --- dictionary of modules used in current block:
+         {<modulename>:{['only':<0|1>],['map':{<local_name1>:<use_name1>,...}]}}
+     B['note'] --- list of LaTeX comments on the block
+     B['f2pyenhancements'] --- optional dictionary
+          {'threadsafe':'','fortranname':<name>,
+           'callstatement':<C-expr>|<multi-line block>,
+           'callprotoargument':<C-expr-list>,
+           'usercode':<multi-line block>|<list of multi-line blocks>,
+           'pymethoddef:<multi-line block>'
+           }
+     B['entry'] --- dictionary {entryname:argslist,..}
+     B['varnames'] --- list of variable names given in the order of reading the
+                       Fortran code, useful for derived types.
+     B['saved_interface'] --- a string of scanned routine signature, defines explicit interface
+ *** Variable definition is a dictionary
+     D = B['vars'][<variable name>] =
+     {'typespec'[,'attrspec','kindselector','charselector','=','typename']}
+     D['typespec'] = 'byte' | 'character' | 'complex' | 'double complex' |
+                     'double precision' | 'integer' | 'logical' | 'real' | 'type'
+     D['attrspec'] --- list of attributes (e.g. 'dimension(<arrayspec>)',
+                       'external','intent(in|out|inout|hide|c|callback|cache|aligned4|aligned8|aligned16)',
+                       'optional','required', etc)
+     K = D['kindselector'] = {['*','kind']} (only if D['typespec'] =
+                         'complex' | 'integer' | 'logical' | 'real' )
+     C = D['charselector'] = {['*','len','kind','f2py_len']}
+                             (only if D['typespec']=='character')
+     D['='] --- initialization expression string
+     D['typename'] --- name of the type if D['typespec']=='type'
+     D['dimension'] --- list of dimension bounds
+     D['intent'] --- list of intent specifications
+     D['depend'] --- list of variable names on which current variable depends on
+     D['check'] --- list of C-expressions; if C-expr returns zero, exception is raised
+     D['note'] --- list of LaTeX comments on the variable
+ *** Meaning of kind/char selectors (few examples):
+     D['typespec>']*K['*']
+     D['typespec'](kind=K['kind'])
+     character*C['*']
+     character(len=C['len'],kind=C['kind'], f2py_len=C['f2py_len'])
+     (see also fortran type declaration statement formats below)
+
+Fortran 90 type declaration statement format (F77 is subset of F90)
+====================================================================
+(Main source: IBM XL Fortran 5.1 Language Reference Manual)
+type declaration = <typespec> [[<attrspec>]::] <entitydecl>
+<typespec> = byte                          |
+             character[<charselector>]     |
+             complex[<kindselector>]       |
+             double complex                |
+             double precision              |
+             integer[<kindselector>]       |
+             logical[<kindselector>]       |
+             real[<kindselector>]          |
+             type(<typename>)
+<charselector> = * <charlen>               |
+             ([len=]<len>[,[kind=]<kind>]) |
+             (kind=<kind>[,len=<len>])
+<kindselector> = * <intlen>                |
+             ([kind=]<kind>)
+<attrspec> = comma separated list of attributes.
+             Only the following attributes are used in
+             building up the interface:
+                external
+                (parameter --- affects '=' key)
+                optional
+                intent
+             Other attributes are ignored.
+<intentspec> = in | out | inout
+<arrayspec> = comma separated list of dimension bounds.
+<entitydecl> = <name> [[*<charlen>][(<arrayspec>)] | [(<arrayspec>)]*<charlen>]
+                      [/<init_expr>/ | =<init_expr>] [,<entitydecl>]
+
+In addition, the following attributes are used: check,depend,note
+
+TODO:
+    * Apply 'parameter' attribute (e.g. 'integer parameter :: i=2' 'real x(i)'
+                                   -> 'real x(2)')
+    The above may be solved by creating appropriate preprocessor program, for example.
+
+"""
+import sys
+import string
+import fileinput
+import re
+import os
+import copy
+import platform
+import codecs
+from pathlib import Path
+try:
+    import charset_normalizer
+except ImportError:
+    charset_normalizer = None
+
+from . import __version__
+
+# The environment provided by auxfuncs.py is needed for some calls to eval.
+# As the needed functions cannot be determined by static inspection of the
+# code, it is safest to use import * pending a major refactoring of f2py.
+from .auxfuncs import *
+from . import symbolic
+
+f2py_version = __version__.version
+
+# Global flags:
+strictf77 = 1          # Ignore `!' comments unless line[0]=='!'
+sourcecodeform = 'fix'  # 'fix','free'
+quiet = 0              # Be verbose if 0 (Obsolete: not used any more)
+verbose = 1            # Be quiet if 0, extra verbose if > 1.
+tabchar = 4 * ' '
+pyffilename = ''
+f77modulename = ''
+skipemptyends = 0      # for old F77 programs without 'program' statement
+ignorecontains = 1
+dolowercase = 1
+debug = []
+
+# Global variables
+beginpattern = ''
+currentfilename = ''
+expectbegin = 1
+f90modulevars = {}
+filepositiontext = ''
+gotnextfile = 1
+groupcache = None
+groupcounter = 0
+grouplist = {groupcounter: []}
+groupname = ''
+include_paths = []
+neededmodule = -1
+onlyfuncs = []
+previous_context = None
+skipblocksuntil = -1
+skipfuncs = []
+skipfunctions = []
+usermodules = []
+
+
+def reset_global_f2py_vars():
+    global groupcounter, grouplist, neededmodule, expectbegin
+    global skipblocksuntil, usermodules, f90modulevars, gotnextfile
+    global filepositiontext, currentfilename, skipfunctions, skipfuncs
+    global onlyfuncs, include_paths, previous_context
+    global strictf77, sourcecodeform, quiet, verbose, tabchar, pyffilename
+    global f77modulename, skipemptyends, ignorecontains, dolowercase, debug
+
+    # flags
+    strictf77 = 1
+    sourcecodeform = 'fix'
+    quiet = 0
+    verbose = 1
+    tabchar = 4 * ' '
+    pyffilename = ''
+    f77modulename = ''
+    skipemptyends = 0
+    ignorecontains = 1
+    dolowercase = 1
+    debug = []
+    # variables
+    groupcounter = 0
+    grouplist = {groupcounter: []}
+    neededmodule = -1
+    expectbegin = 1
+    skipblocksuntil = -1
+    usermodules = []
+    f90modulevars = {}
+    gotnextfile = 1
+    filepositiontext = ''
+    currentfilename = ''
+    skipfunctions = []
+    skipfuncs = []
+    onlyfuncs = []
+    include_paths = []
+    previous_context = None
+
+
+def outmess(line, flag=1):
+    global filepositiontext
+
+    if not verbose:
+        return
+    if not quiet:
+        if flag:
+            sys.stdout.write(filepositiontext)
+        sys.stdout.write(line)
+
+re._MAXCACHE = 50
+defaultimplicitrules = {}
+for c in "abcdefghopqrstuvwxyz$_":
+    defaultimplicitrules[c] = {'typespec': 'real'}
+for c in "ijklmn":
+    defaultimplicitrules[c] = {'typespec': 'integer'}
+badnames = {}
+invbadnames = {}
+for n in ['int', 'double', 'float', 'char', 'short', 'long', 'void', 'case', 'while',
+          'return', 'signed', 'unsigned', 'if', 'for', 'typedef', 'sizeof', 'union',
+          'struct', 'static', 'register', 'new', 'break', 'do', 'goto', 'switch',
+          'continue', 'else', 'inline', 'extern', 'delete', 'const', 'auto',
+          'len', 'rank', 'shape', 'index', 'slen', 'size', '_i',
+          'max', 'min',
+          'flen', 'fshape',
+          'string', 'complex_double', 'float_double', 'stdin', 'stderr', 'stdout',
+          'type', 'default']:
+    badnames[n] = n + '_bn'
+    invbadnames[n + '_bn'] = n
+
+
+def rmbadname1(name):
+    if name in badnames:
+        errmess('rmbadname1: Replacing "%s" with "%s".\n' %
+                (name, badnames[name]))
+        return badnames[name]
+    return name
+
+
+def rmbadname(names):
+    return [rmbadname1(_m) for _m in names]
+
+
+def undo_rmbadname1(name):
+    if name in invbadnames:
+        errmess('undo_rmbadname1: Replacing "%s" with "%s".\n'
+                % (name, invbadnames[name]))
+        return invbadnames[name]
+    return name
+
+
+def undo_rmbadname(names):
+    return [undo_rmbadname1(_m) for _m in names]
+
+
+_has_f_header = re.compile(r'-\*-\s*fortran\s*-\*-', re.I).search
+_has_f90_header = re.compile(r'-\*-\s*f90\s*-\*-', re.I).search
+_has_fix_header = re.compile(r'-\*-\s*fix\s*-\*-', re.I).search
+_free_f90_start = re.compile(r'[^c*]\s*[^\s\d\t]', re.I).match
+
+# Extensions
+COMMON_FREE_EXTENSIONS = ['.f90', '.f95', '.f03', '.f08']
+COMMON_FIXED_EXTENSIONS = ['.for', '.ftn', '.f77', '.f']
+
+
+def openhook(filename, mode):
+    """Ensures that filename is opened with correct encoding parameter.
+
+    This function uses charset_normalizer package, when available, for
+    determining the encoding of the file to be opened. When charset_normalizer
+    is not available, the function detects only UTF encodings, otherwise, ASCII
+    encoding is used as fallback.
+    """
+    # Reads in the entire file. Robust detection of encoding.
+    # Correctly handles comments or late stage unicode characters
+    # gh-22871
+    if charset_normalizer is not None:
+        encoding = charset_normalizer.from_path(filename).best().encoding
+    else:
+        # hint: install charset_normalizer for correct encoding handling
+        # No need to read the whole file for trying with startswith
+        nbytes = min(32, os.path.getsize(filename))
+        with open(filename, 'rb') as fhandle:
+            raw = fhandle.read(nbytes)
+            if raw.startswith(codecs.BOM_UTF8):
+                encoding = 'UTF-8-SIG'
+            elif raw.startswith((codecs.BOM_UTF32_LE, codecs.BOM_UTF32_BE)):
+                encoding = 'UTF-32'
+            elif raw.startswith((codecs.BOM_LE, codecs.BOM_BE)):
+                encoding = 'UTF-16'
+            else:
+                # Fallback, without charset_normalizer
+                encoding = 'ascii'
+    return open(filename, mode, encoding=encoding)
+
+
+def is_free_format(fname):
+    """Check if file is in free format Fortran."""
+    # f90 allows both fixed and free format, assuming fixed unless
+    # signs of free format are detected.
+    result = False
+    if Path(fname).suffix.lower() in COMMON_FREE_EXTENSIONS:
+        result = True
+    with openhook(fname, 'r') as fhandle:
+        line = fhandle.readline()
+        n = 15  # the number of non-comment lines to scan for hints
+        if _has_f_header(line):
+            n = 0
+        elif _has_f90_header(line):
+            n = 0
+            result = True
+        while n > 0 and line:
+            if line[0] != '!' and line.strip():
+                n -= 1
+                if (line[0] != '\t' and _free_f90_start(line[:5])) or line[-2:-1] == '&':
+                    result = True
+                    break
+            line = fhandle.readline()
+    return result
+
+
+# Read fortran (77,90) code
+def readfortrancode(ffile, dowithline=show, istop=1):
+    """
+    Read fortran codes from files and
+     1) Get rid of comments, line continuations, and empty lines; lower cases.
+     2) Call dowithline(line) on every line.
+     3) Recursively call itself when statement \"include '<filename>'\" is met.
+    """
+    global gotnextfile, filepositiontext, currentfilename, sourcecodeform, strictf77
+    global beginpattern, quiet, verbose, dolowercase, include_paths
+
+    if not istop:
+        saveglobals = gotnextfile, filepositiontext, currentfilename, sourcecodeform, strictf77,\
+            beginpattern, quiet, verbose, dolowercase
+    if ffile == []:
+        return
+    localdolowercase = dolowercase
+    # cont: set to True when the content of the last line read
+    # indicates statement continuation
+    cont = False
+    finalline = ''
+    ll = ''
+    includeline = re.compile(
+        r'\s*include\s*(\'|")(?P<name>[^\'"]*)(\'|")', re.I)
+    cont1 = re.compile(r'(?P<line>.*)&\s*\Z')
+    cont2 = re.compile(r'(\s*&|)(?P<line>.*)')
+    mline_mark = re.compile(r".*?'''")
+    if istop:
+        dowithline('', -1)
+    ll, l1 = '', ''
+    spacedigits = [' '] + [str(_m) for _m in range(10)]
+    filepositiontext = ''
+    fin = fileinput.FileInput(ffile, openhook=openhook)
+    while True:
+        try:
+            l = fin.readline()
+        except UnicodeDecodeError as msg:
+            raise Exception(
+                f'readfortrancode: reading {fin.filename()}#{fin.lineno()}'
+                f' failed with\n{msg}.\nIt is likely that installing charset_normalizer'
+                ' package will help f2py determine the input file encoding'
+                ' correctly.')
+        if not l:
+            break
+        if fin.isfirstline():
+            filepositiontext = ''
+            currentfilename = fin.filename()
+            gotnextfile = 1
+            l1 = l
+            strictf77 = 0
+            sourcecodeform = 'fix'
+            ext = os.path.splitext(currentfilename)[1]
+            if Path(currentfilename).suffix.lower() in COMMON_FIXED_EXTENSIONS and \
+                    not (_has_f90_header(l) or _has_fix_header(l)):
+                strictf77 = 1
+            elif is_free_format(currentfilename) and not _has_fix_header(l):
+                sourcecodeform = 'free'
+            if strictf77:
+                beginpattern = beginpattern77
+            else:
+                beginpattern = beginpattern90
+            outmess('\tReading file %s (format:%s%s)\n'
+                    % (repr(currentfilename), sourcecodeform,
+                       strictf77 and ',strict' or ''))
+
+        l = l.expandtabs().replace('\xa0', ' ')
+        # Get rid of newline characters
+        while not l == '':
+            if l[-1] not in "\n\r\f":
+                break
+            l = l[:-1]
+        if not strictf77:
+            (l, rl) = split_by_unquoted(l, '!')
+            l += ' '
+            if rl[:5].lower() == '!f2py':  # f2py directive
+                l, _ = split_by_unquoted(l + 4 * ' ' + rl[5:], '!')
+        if l.strip() == '':  # Skip empty line
+            if sourcecodeform == 'free':
+                # In free form, a statement continues in the next line
+                # that is not a comment line [3.3.2.4^1], lines with
+                # blanks are comment lines [3.3.2.3^1]. Hence, the
+                # line continuation flag must retain its state.
+                pass
+            else:
+                # In fixed form, statement continuation is determined
+                # by a non-blank character at the 6-th position. Empty
+                # line indicates a start of a new statement
+                # [3.3.3.3^1]. Hence, the line continuation flag must
+                # be reset.
+                cont = False
+            continue
+        if sourcecodeform == 'fix':
+            if l[0] in ['*', 'c', '!', 'C', '#']:
+                if l[1:5].lower() == 'f2py':  # f2py directive
+                    l = '     ' + l[5:]
+                else:  # Skip comment line
+                    cont = False
+                    continue
+            elif strictf77:
+                if len(l) > 72:
+                    l = l[:72]
+            if not (l[0] in spacedigits):
+                raise Exception('readfortrancode: Found non-(space,digit) char '
+                                'in the first column.\n\tAre you sure that '
+                                'this code is in fix form?\n\tline=%s' % repr(l))
+
+            if (not cont or strictf77) and (len(l) > 5 and not l[5] == ' '):
+                # Continuation of a previous line
+                ll = ll + l[6:]
+                finalline = ''
+                origfinalline = ''
+            else:
+                if not strictf77:
+                    # F90 continuation
+                    r = cont1.match(l)
+                    if r:
+                        l = r.group('line')  # Continuation follows ..
+                    if cont:
+                        ll = ll + cont2.match(l).group('line')
+                        finalline = ''
+                        origfinalline = ''
+                    else:
+                        # clean up line beginning from possible digits.
+                        l = '     ' + l[5:]
+                        if localdolowercase:
+                            finalline = ll.lower()
+                        else:
+                            finalline = ll
+                        origfinalline = ll
+                        ll = l
+                    cont = (r is not None)
+                else:
+                    # clean up line beginning from possible digits.
+                    l = '     ' + l[5:]
+                    if localdolowercase:
+                        finalline = ll.lower()
+                    else:
+                        finalline = ll
+                    origfinalline = ll
+                    ll = l
+
+        elif sourcecodeform == 'free':
+            if not cont and ext == '.pyf' and mline_mark.match(l):
+                l = l + '\n'
+                while True:
+                    lc = fin.readline()
+                    if not lc:
+                        errmess(
+                            'Unexpected end of file when reading multiline\n')
+                        break
+                    l = l + lc
+                    if mline_mark.match(lc):
+                        break
+                l = l.rstrip()
+            r = cont1.match(l)
+            if r:
+                l = r.group('line')  # Continuation follows ..
+            if cont:
+                ll = ll + cont2.match(l).group('line')
+                finalline = ''
+                origfinalline = ''
+            else:
+                if localdolowercase:
+                    finalline = ll.lower()
+                else:
+                    finalline = ll
+                origfinalline = ll
+                ll = l
+            cont = (r is not None)
+        else:
+            raise ValueError(
+                "Flag sourcecodeform must be either 'fix' or 'free': %s" % repr(sourcecodeform))
+        filepositiontext = 'Line #%d in %s:"%s"\n\t' % (
+            fin.filelineno() - 1, currentfilename, l1)
+        m = includeline.match(origfinalline)
+        if m:
+            fn = m.group('name')
+            if os.path.isfile(fn):
+                readfortrancode(fn, dowithline=dowithline, istop=0)
+            else:
+                include_dirs = [
+                    os.path.dirname(currentfilename)] + include_paths
+                foundfile = 0
+                for inc_dir in include_dirs:
+                    fn1 = os.path.join(inc_dir, fn)
+                    if os.path.isfile(fn1):
+                        foundfile = 1
+                        readfortrancode(fn1, dowithline=dowithline, istop=0)
+                        break
+                if not foundfile:
+                    outmess('readfortrancode: could not find include file %s in %s. Ignoring.\n' % (
+                        repr(fn), os.pathsep.join(include_dirs)))
+        else:
+            dowithline(finalline)
+        l1 = ll
+    if localdolowercase:
+        finalline = ll.lower()
+    else:
+        finalline = ll
+    origfinalline = ll
+    filepositiontext = 'Line #%d in %s:"%s"\n\t' % (
+        fin.filelineno() - 1, currentfilename, l1)
+    m = includeline.match(origfinalline)
+    if m:
+        fn = m.group('name')
+        if os.path.isfile(fn):
+            readfortrancode(fn, dowithline=dowithline, istop=0)
+        else:
+            include_dirs = [os.path.dirname(currentfilename)] + include_paths
+            foundfile = 0
+            for inc_dir in include_dirs:
+                fn1 = os.path.join(inc_dir, fn)
+                if os.path.isfile(fn1):
+                    foundfile = 1
+                    readfortrancode(fn1, dowithline=dowithline, istop=0)
+                    break
+            if not foundfile:
+                outmess('readfortrancode: could not find include file %s in %s. Ignoring.\n' % (
+                    repr(fn), os.pathsep.join(include_dirs)))
+    else:
+        dowithline(finalline)
+    filepositiontext = ''
+    fin.close()
+    if istop:
+        dowithline('', 1)
+    else:
+        gotnextfile, filepositiontext, currentfilename, sourcecodeform, strictf77,\
+            beginpattern, quiet, verbose, dolowercase = saveglobals
+
+# Crack line
+beforethisafter = r'\s*(?P<before>%s(?=\s*(\b(%s)\b)))' + \
+    r'\s*(?P<this>(\b(%s)\b))' + \
+    r'\s*(?P<after>%s)\s*\Z'
+##
+fortrantypes = r'character|logical|integer|real|complex|double\s*(precision\s*(complex|)|complex)|type(?=\s*\([\w\s,=(*)]*\))|byte'
+typespattern = re.compile(
+    beforethisafter % ('', fortrantypes, fortrantypes, '.*'), re.I), 'type'
+typespattern4implicit = re.compile(beforethisafter % (
+    '', fortrantypes + '|static|automatic|undefined', fortrantypes + '|static|automatic|undefined', '.*'), re.I)
+#
+functionpattern = re.compile(beforethisafter % (
+    r'([a-z]+[\w\s(=*+-/)]*?|)', 'function', 'function', '.*'), re.I), 'begin'
+subroutinepattern = re.compile(beforethisafter % (
+    r'[a-z\s]*?', 'subroutine', 'subroutine', '.*'), re.I), 'begin'
+# modulepattern=re.compile(beforethisafter%('[a-z\s]*?','module','module','.*'),re.I),'begin'
+#
+groupbegins77 = r'program|block\s*data'
+beginpattern77 = re.compile(
+    beforethisafter % ('', groupbegins77, groupbegins77, '.*'), re.I), 'begin'
+groupbegins90 = groupbegins77 + \
+    r'|module(?!\s*procedure)|python\s*module|(abstract|)\s*interface|' + \
+    r'type(?!\s*\()'
+beginpattern90 = re.compile(
+    beforethisafter % ('', groupbegins90, groupbegins90, '.*'), re.I), 'begin'
+groupends = (r'end|endprogram|endblockdata|endmodule|endpythonmodule|'
+             r'endinterface|endsubroutine|endfunction')
+endpattern = re.compile(
+    beforethisafter % ('', groupends, groupends, '.*'), re.I), 'end'
+# block, the Fortran 2008 construct needs special handling in the rest of the file
+endifs = r'end\s*(if|do|where|select|while|forall|associate|' + \
+         r'critical|enum|team)'
+endifpattern = re.compile(
+    beforethisafter % (r'[\w]*?', endifs, endifs, '.*'), re.I), 'endif'
+#
+moduleprocedures = r'module\s*procedure'
+moduleprocedurepattern = re.compile(
+    beforethisafter % ('', moduleprocedures, moduleprocedures, '.*'), re.I), \
+    'moduleprocedure'
+implicitpattern = re.compile(
+    beforethisafter % ('', 'implicit', 'implicit', '.*'), re.I), 'implicit'
+dimensionpattern = re.compile(beforethisafter % (
+    '', 'dimension|virtual', 'dimension|virtual', '.*'), re.I), 'dimension'
+externalpattern = re.compile(
+    beforethisafter % ('', 'external', 'external', '.*'), re.I), 'external'
+optionalpattern = re.compile(
+    beforethisafter % ('', 'optional', 'optional', '.*'), re.I), 'optional'
+requiredpattern = re.compile(
+    beforethisafter % ('', 'required', 'required', '.*'), re.I), 'required'
+publicpattern = re.compile(
+    beforethisafter % ('', 'public', 'public', '.*'), re.I), 'public'
+privatepattern = re.compile(
+    beforethisafter % ('', 'private', 'private', '.*'), re.I), 'private'
+intrinsicpattern = re.compile(
+    beforethisafter % ('', 'intrinsic', 'intrinsic', '.*'), re.I), 'intrinsic'
+intentpattern = re.compile(beforethisafter % (
+    '', 'intent|depend|note|check', 'intent|depend|note|check', r'\s*\(.*?\).*'), re.I), 'intent'
+parameterpattern = re.compile(
+    beforethisafter % ('', 'parameter', 'parameter', r'\s*\(.*'), re.I), 'parameter'
+datapattern = re.compile(
+    beforethisafter % ('', 'data', 'data', '.*'), re.I), 'data'
+callpattern = re.compile(
+    beforethisafter % ('', 'call', 'call', '.*'), re.I), 'call'
+entrypattern = re.compile(
+    beforethisafter % ('', 'entry', 'entry', '.*'), re.I), 'entry'
+callfunpattern = re.compile(
+    beforethisafter % ('', 'callfun', 'callfun', '.*'), re.I), 'callfun'
+commonpattern = re.compile(
+    beforethisafter % ('', 'common', 'common', '.*'), re.I), 'common'
+usepattern = re.compile(
+    beforethisafter % ('', 'use', 'use', '.*'), re.I), 'use'
+containspattern = re.compile(
+    beforethisafter % ('', 'contains', 'contains', ''), re.I), 'contains'
+formatpattern = re.compile(
+    beforethisafter % ('', 'format', 'format', '.*'), re.I), 'format'
+# Non-fortran and f2py-specific statements
+f2pyenhancementspattern = re.compile(beforethisafter % ('', 'threadsafe|fortranname|callstatement|callprotoargument|usercode|pymethoddef',
+                                                        'threadsafe|fortranname|callstatement|callprotoargument|usercode|pymethoddef', '.*'), re.I | re.S), 'f2pyenhancements'
+multilinepattern = re.compile(
+    r"\s*(?P<before>''')(?P<this>.*?)(?P<after>''')\s*\Z", re.S), 'multiline'
+##
+
+def split_by_unquoted(line, characters):
+    """
+    Splits the line into (line[:i], line[i:]),
+    where i is the index of first occurrence of one of the characters
+    not within quotes, or len(line) if no such index exists
+    """
+    assert not (set('"\'') & set(characters)), "cannot split by unquoted quotes"
+    r = re.compile(
+        r"\A(?P<before>({single_quoted}|{double_quoted}|{not_quoted})*)"
+        r"(?P<after>{char}.*)\Z".format(
+            not_quoted="[^\"'{}]".format(re.escape(characters)),
+            char="[{}]".format(re.escape(characters)),
+            single_quoted=r"('([^'\\]|(\\.))*')",
+            double_quoted=r'("([^"\\]|(\\.))*")'))
+    m = r.match(line)
+    if m:
+        d = m.groupdict()
+        return (d["before"], d["after"])
+    return (line, "")
+
+def _simplifyargs(argsline):
+    a = []
+    for n in markoutercomma(argsline).split('@,@'):
+        for r in '(),':
+            n = n.replace(r, '_')
+        a.append(n)
+    return ','.join(a)
+
+crackline_re_1 = re.compile(r'\s*(?P<result>\b[a-z]+\w*\b)\s*=.*', re.I)
+crackline_bind_1 = re.compile(r'\s*(?P<bind>\b[a-z]+\w*\b)\s*=.*', re.I)
+crackline_bindlang = re.compile(r'\s*bind\(\s*(?P<lang>[^,]+)\s*,\s*name\s*=\s*"(?P<lang_name>[^"]+)"\s*\)', re.I)
+
+def crackline(line, reset=0):
+    """
+    reset=-1  --- initialize
+    reset=0   --- crack the line
+    reset=1   --- final check if mismatch of blocks occurred
+
+    Cracked data is saved in grouplist[0].
+    """
+    global beginpattern, groupcounter, groupname, groupcache, grouplist
+    global filepositiontext, currentfilename, neededmodule, expectbegin
+    global skipblocksuntil, skipemptyends, previous_context, gotnextfile
+
+    _, has_semicolon = split_by_unquoted(line, ";")
+    if has_semicolon and not (f2pyenhancementspattern[0].match(line) or
+                               multilinepattern[0].match(line)):
+        # XXX: non-zero reset values need testing
+        assert reset == 0, repr(reset)
+        # split line on unquoted semicolons
+        line, semicolon_line = split_by_unquoted(line, ";")
+        while semicolon_line:
+            crackline(line, reset)
+            line, semicolon_line = split_by_unquoted(semicolon_line[1:], ";")
+        crackline(line, reset)
+        return
+    if reset < 0:
+        groupcounter = 0
+        groupname = {groupcounter: ''}
+        groupcache = {groupcounter: {}}
+        grouplist = {groupcounter: []}
+        groupcache[groupcounter]['body'] = []
+        groupcache[groupcounter]['vars'] = {}
+        groupcache[groupcounter]['block'] = ''
+        groupcache[groupcounter]['name'] = ''
+        neededmodule = -1
+        skipblocksuntil = -1
+        return
+    if reset > 0:
+        fl = 0
+        if f77modulename and neededmodule == groupcounter:
+            fl = 2
+        while groupcounter > fl:
+            outmess('crackline: groupcounter=%s groupname=%s\n' %
+                    (repr(groupcounter), repr(groupname)))
+            outmess(
+                'crackline: Mismatch of blocks encountered. Trying to fix it by assuming "end" statement.\n')
+            grouplist[groupcounter - 1].append(groupcache[groupcounter])
+            grouplist[groupcounter - 1][-1]['body'] = grouplist[groupcounter]
+            del grouplist[groupcounter]
+            groupcounter = groupcounter - 1
+        if f77modulename and neededmodule == groupcounter:
+            grouplist[groupcounter - 1].append(groupcache[groupcounter])
+            grouplist[groupcounter - 1][-1]['body'] = grouplist[groupcounter]
+            del grouplist[groupcounter]
+            groupcounter = groupcounter - 1  # end interface
+            grouplist[groupcounter - 1].append(groupcache[groupcounter])
+            grouplist[groupcounter - 1][-1]['body'] = grouplist[groupcounter]
+            del grouplist[groupcounter]
+            groupcounter = groupcounter - 1  # end module
+            neededmodule = -1
+        return
+    if line == '':
+        return
+    flag = 0
+    for pat in [dimensionpattern, externalpattern, intentpattern, optionalpattern,
+                requiredpattern,
+                parameterpattern, datapattern, publicpattern, privatepattern,
+                intrinsicpattern,
+                endifpattern, endpattern,
+                formatpattern,
+                beginpattern, functionpattern, subroutinepattern,
+                implicitpattern, typespattern, commonpattern,
+                callpattern, usepattern, containspattern,
+                entrypattern,
+                f2pyenhancementspattern,
+                multilinepattern,
+                moduleprocedurepattern
+                ]:
+        m = pat[0].match(line)
+        if m:
+            break
+        flag = flag + 1
+    if not m:
+        re_1 = crackline_re_1
+        if 0 <= skipblocksuntil <= groupcounter:
+            return
+        if 'externals' in groupcache[groupcounter]:
+            for name in groupcache[groupcounter]['externals']:
+                if name in invbadnames:
+                    name = invbadnames[name]
+                if 'interfaced' in groupcache[groupcounter] and name in groupcache[groupcounter]['interfaced']:
+                    continue
+                m1 = re.match(
+                    r'(?P<before>[^"]*)\b%s\b\s*@\(@(?P<args>[^@]*)@\)@.*\Z' % name, markouterparen(line), re.I)
+                if m1:
+                    m2 = re_1.match(m1.group('before'))
+                    a = _simplifyargs(m1.group('args'))
+                    if m2:
+                        line = 'callfun %s(%s) result (%s)' % (
+                            name, a, m2.group('result'))
+                    else:
+                        line = 'callfun %s(%s)' % (name, a)
+                    m = callfunpattern[0].match(line)
+                    if not m:
+                        outmess(
+                            'crackline: could not resolve function call for line=%s.\n' % repr(line))
+                        return
+                    analyzeline(m, 'callfun', line)
+                    return
+        if verbose > 1 or (verbose == 1 and currentfilename.lower().endswith('.pyf')):
+            previous_context = None
+            outmess('crackline:%d: No pattern for line\n' % (groupcounter))
+        return
+    elif pat[1] == 'end':
+        if 0 <= skipblocksuntil < groupcounter:
+            groupcounter = groupcounter - 1
+            if skipblocksuntil <= groupcounter:
+                return
+        if groupcounter <= 0:
+            raise Exception('crackline: groupcounter(=%s) is nonpositive. '
+                            'Check the blocks.'
+                            % (groupcounter))
+        m1 = beginpattern[0].match((line))
+        if (m1) and (not m1.group('this') == groupname[groupcounter]):
+            raise Exception('crackline: End group %s does not match with '
+                            'previous Begin group %s\n\t%s' %
+                            (repr(m1.group('this')), repr(groupname[groupcounter]),
+                             filepositiontext)
+                            )
+        if skipblocksuntil == groupcounter:
+            skipblocksuntil = -1
+        grouplist[groupcounter - 1].append(groupcache[groupcounter])
+        grouplist[groupcounter - 1][-1]['body'] = grouplist[groupcounter]
+        del grouplist[groupcounter]
+        groupcounter = groupcounter - 1
+        if not skipemptyends:
+            expectbegin = 1
+    elif pat[1] == 'begin':
+        if 0 <= skipblocksuntil <= groupcounter:
+            groupcounter = groupcounter + 1
+            return
+        gotnextfile = 0
+        analyzeline(m, pat[1], line)
+        expectbegin = 0
+    elif pat[1] == 'endif':
+        pass
+    elif pat[1] == 'moduleprocedure':
+        analyzeline(m, pat[1], line)
+    elif pat[1] == 'contains':
+        if ignorecontains:
+            return
+        if 0 <= skipblocksuntil <= groupcounter:
+            return
+        skipblocksuntil = groupcounter
+    else:
+        if 0 <= skipblocksuntil <= groupcounter:
+            return
+        analyzeline(m, pat[1], line)
+
+
+def markouterparen(line):
+    l = ''
+    f = 0
+    for c in line:
+        if c == '(':
+            f = f + 1
+            if f == 1:
+                l = l + '@(@'
+                continue
+        elif c == ')':
+            f = f - 1
+            if f == 0:
+                l = l + '@)@'
+                continue
+        l = l + c
+    return l
+
+
+def markoutercomma(line, comma=','):
+    l = ''
+    f = 0
+    before, after = split_by_unquoted(line, comma + '()')
+    l += before
+    while after:
+        if (after[0] == comma) and (f == 0):
+            l += '@' + comma + '@'
+        else:
+            l += after[0]
+            if after[0] == '(':
+                f += 1
+            elif after[0] == ')':
+                f -= 1
+        before, after = split_by_unquoted(after[1:], comma + '()')
+        l += before
+    assert not f, repr((f, line, l))
+    return l
+
+def unmarkouterparen(line):
+    r = line.replace('@(@', '(').replace('@)@', ')')
+    return r
+
+
+def appenddecl(decl, decl2, force=1):
+    if not decl:
+        decl = {}
+    if not decl2:
+        return decl
+    if decl is decl2:
+        return decl
+    for k in list(decl2.keys()):
+        if k == 'typespec':
+            if force or k not in decl:
+                decl[k] = decl2[k]
+        elif k == 'attrspec':
+            for l in decl2[k]:
+                decl = setattrspec(decl, l, force)
+        elif k == 'kindselector':
+            decl = setkindselector(decl, decl2[k], force)
+        elif k == 'charselector':
+            decl = setcharselector(decl, decl2[k], force)
+        elif k in ['=', 'typename']:
+            if force or k not in decl:
+                decl[k] = decl2[k]
+        elif k == 'note':
+            pass
+        elif k in ['intent', 'check', 'dimension', 'optional',
+                   'required', 'depend']:
+            errmess('appenddecl: "%s" not implemented.\n' % k)
+        else:
+            raise Exception('appenddecl: Unknown variable definition key: ' +
+                            str(k))
+    return decl
+
+selectpattern = re.compile(
+    r'\s*(?P<this>(@\(@.*?@\)@|\*[\d*]+|\*\s*@\(@.*?@\)@|))(?P<after>.*)\Z', re.I)
+typedefpattern = re.compile(
+    r'(?:,(?P<attributes>[\w(),]+))?(::)?(?P<name>\b[a-z$_][\w$]*\b)'
+    r'(?:\((?P<params>[\w,]*)\))?\Z', re.I)
+nameargspattern = re.compile(
+    r'\s*(?P<name>\b[\w$]+\b)\s*(@\(@\s*(?P<args>[\w\s,]*)\s*@\)@|)\s*((result(\s*@\(@\s*(?P<result>\b[\w$]+\b)\s*@\)@|))|(bind\s*@\(@\s*(?P<bind>(?:(?!@\)@).)*)\s*@\)@))*\s*\Z', re.I)
+operatorpattern = re.compile(
+    r'\s*(?P<scheme>(operator|assignment))'
+    r'@\(@\s*(?P<name>[^)]+)\s*@\)@\s*\Z', re.I)
+callnameargspattern = re.compile(
+    r'\s*(?P<name>\b[\w$]+\b)\s*@\(@\s*(?P<args>.*)\s*@\)@\s*\Z', re.I)
+real16pattern = re.compile(
+    r'([-+]?(?:\d+(?:\.\d*)?|\d*\.\d+))[dD]((?:[-+]?\d+)?)')
+real8pattern = re.compile(
+    r'([-+]?((?:\d+(?:\.\d*)?|\d*\.\d+))[eE]((?:[-+]?\d+)?)|(\d+\.\d*))')
+
+_intentcallbackpattern = re.compile(r'intent\s*\(.*?\bcallback\b', re.I)
+
+
+def _is_intent_callback(vdecl):
+    for a in vdecl.get('attrspec', []):
+        if _intentcallbackpattern.match(a):
+            return 1
+    return 0
+
+
+def _resolvetypedefpattern(line):
+    line = ''.join(line.split())  # removes whitespace
+    m1 = typedefpattern.match(line)
+    print(line, m1)
+    if m1:
+        attrs = m1.group('attributes')
+        attrs = [a.lower() for a in attrs.split(',')] if attrs else []
+        return m1.group('name'), attrs, m1.group('params')
+    return None, [], None
+
+def parse_name_for_bind(line):
+    pattern = re.compile(r'bind\(\s*(?P<lang>[^,]+)(?:\s*,\s*name\s*=\s*["\'](?P<name>[^"\']+)["\']\s*)?\)', re.I)
+    match = pattern.search(line)
+    bind_statement = None
+    if match:
+        bind_statement = match.group(0)
+        # Remove the 'bind' construct from the line.
+        line = line[:match.start()] + line[match.end():]
+    return line, bind_statement
+
+def _resolvenameargspattern(line):
+    line, bind_cname = parse_name_for_bind(line)
+    line = markouterparen(line)
+    m1 = nameargspattern.match(line)
+    if m1:
+        return m1.group('name'), m1.group('args'), m1.group('result'), bind_cname
+    m1 = operatorpattern.match(line)
+    if m1:
+        name = m1.group('scheme') + '(' + m1.group('name') + ')'
+        return name, [], None, None
+    m1 = callnameargspattern.match(line)
+    if m1:
+        return m1.group('name'), m1.group('args'), None, None
+    return None, [], None, None
+
+
+def analyzeline(m, case, line):
+    """
+    Reads each line in the input file in sequence and updates global vars.
+
+    Effectively reads and collects information from the input file to the
+    global variable groupcache, a dictionary containing info about each part
+    of the fortran module.
+
+    At the end of analyzeline, information is filtered into the correct dict
+    keys, but parameter values and dimensions are not yet interpreted.
+    """
+    global groupcounter, groupname, groupcache, grouplist, filepositiontext
+    global currentfilename, f77modulename, neededinterface, neededmodule
+    global expectbegin, gotnextfile, previous_context
+
+    block = m.group('this')
+    if case != 'multiline':
+        previous_context = None
+    if expectbegin and case not in ['begin', 'call', 'callfun', 'type'] \
+       and not skipemptyends and groupcounter < 1:
+        newname = os.path.basename(currentfilename).split('.')[0]
+        outmess(
+            'analyzeline: no group yet. Creating program group with name "%s".\n' % newname)
+        gotnextfile = 0
+        groupcounter = groupcounter + 1
+        groupname[groupcounter] = 'program'
+        groupcache[groupcounter] = {}
+        grouplist[groupcounter] = []
+        groupcache[groupcounter]['body'] = []
+        groupcache[groupcounter]['vars'] = {}
+        groupcache[groupcounter]['block'] = 'program'
+        groupcache[groupcounter]['name'] = newname
+        groupcache[groupcounter]['from'] = 'fromsky'
+        expectbegin = 0
+    if case in ['begin', 'call', 'callfun']:
+        # Crack line => block,name,args,result
+        block = block.lower()
+        if re.match(r'block\s*data', block, re.I):
+            block = 'block data'
+        elif re.match(r'python\s*module', block, re.I):
+            block = 'python module'
+        elif re.match(r'abstract\s*interface', block, re.I):
+            block = 'abstract interface'
+        if block == 'type':
+            name, attrs, _ = _resolvetypedefpattern(m.group('after'))
+            groupcache[groupcounter]['vars'][name] = dict(attrspec = attrs)
+            args = []
+            result = None
+        else:
+            name, args, result, bindcline = _resolvenameargspattern(m.group('after'))
+        if name is None:
+            if block == 'block data':
+                name = '_BLOCK_DATA_'
+            else:
+                name = ''
+            if block not in ['interface', 'block data', 'abstract interface']:
+                outmess('analyzeline: No name/args pattern found for line.\n')
+
+        previous_context = (block, name, groupcounter)
+        if args:
+            args = rmbadname([x.strip()
+                              for x in markoutercomma(args).split('@,@')])
+        else:
+            args = []
+        if '' in args:
+            while '' in args:
+                args.remove('')
+            outmess(
+                'analyzeline: argument list is malformed (missing argument).\n')
+
+        # end of crack line => block,name,args,result
+        needmodule = 0
+        needinterface = 0
+
+        if case in ['call', 'callfun']:
+            needinterface = 1
+            if 'args' not in groupcache[groupcounter]:
+                return
+            if name not in groupcache[groupcounter]['args']:
+                return
+            for it in grouplist[groupcounter]:
+                if it['name'] == name:
+                    return
+            if name in groupcache[groupcounter]['interfaced']:
+                return
+            block = {'call': 'subroutine', 'callfun': 'function'}[case]
+        if f77modulename and neededmodule == -1 and groupcounter <= 1:
+            neededmodule = groupcounter + 2
+            needmodule = 1
+            if block not in ['interface', 'abstract interface']:
+                needinterface = 1
+        # Create new block(s)
+        groupcounter = groupcounter + 1
+        groupcache[groupcounter] = {}
+        grouplist[groupcounter] = []
+        if needmodule:
+            if verbose > 1:
+                outmess('analyzeline: Creating module block %s\n' %
+                        repr(f77modulename), 0)
+            groupname[groupcounter] = 'module'
+            groupcache[groupcounter]['block'] = 'python module'
+            groupcache[groupcounter]['name'] = f77modulename
+            groupcache[groupcounter]['from'] = ''
+            groupcache[groupcounter]['body'] = []
+            groupcache[groupcounter]['externals'] = []
+            groupcache[groupcounter]['interfaced'] = []
+            groupcache[groupcounter]['vars'] = {}
+            groupcounter = groupcounter + 1
+            groupcache[groupcounter] = {}
+            grouplist[groupcounter] = []
+        if needinterface:
+            if verbose > 1:
+                outmess('analyzeline: Creating additional interface block (groupcounter=%s).\n' % (
+                    groupcounter), 0)
+            groupname[groupcounter] = 'interface'
+            groupcache[groupcounter]['block'] = 'interface'
+            groupcache[groupcounter]['name'] = 'unknown_interface'
+            groupcache[groupcounter]['from'] = '%s:%s' % (
+                groupcache[groupcounter - 1]['from'], groupcache[groupcounter - 1]['name'])
+            groupcache[groupcounter]['body'] = []
+            groupcache[groupcounter]['externals'] = []
+            groupcache[groupcounter]['interfaced'] = []
+            groupcache[groupcounter]['vars'] = {}
+            groupcounter = groupcounter + 1
+            groupcache[groupcounter] = {}
+            grouplist[groupcounter] = []
+        groupname[groupcounter] = block
+        groupcache[groupcounter]['block'] = block
+        if not name:
+            name = 'unknown_' + block.replace(' ', '_')
+        groupcache[groupcounter]['prefix'] = m.group('before')
+        groupcache[groupcounter]['name'] = rmbadname1(name)
+        groupcache[groupcounter]['result'] = result
+        if groupcounter == 1:
+            groupcache[groupcounter]['from'] = currentfilename
+        else:
+            if f77modulename and groupcounter == 3:
+                groupcache[groupcounter]['from'] = '%s:%s' % (
+                    groupcache[groupcounter - 1]['from'], currentfilename)
+            else:
+                groupcache[groupcounter]['from'] = '%s:%s' % (
+                    groupcache[groupcounter - 1]['from'], groupcache[groupcounter - 1]['name'])
+        for k in list(groupcache[groupcounter].keys()):
+            if not groupcache[groupcounter][k]:
+                del groupcache[groupcounter][k]
+
+        groupcache[groupcounter]['args'] = args
+        groupcache[groupcounter]['body'] = []
+        groupcache[groupcounter]['externals'] = []
+        groupcache[groupcounter]['interfaced'] = []
+        groupcache[groupcounter]['vars'] = {}
+        groupcache[groupcounter]['entry'] = {}
+        # end of creation
+        if block == 'type':
+            groupcache[groupcounter]['varnames'] = []
+
+        if case in ['call', 'callfun']:  # set parents variables
+            if name not in groupcache[groupcounter - 2]['externals']:
+                groupcache[groupcounter - 2]['externals'].append(name)
+            groupcache[groupcounter]['vars'] = copy.deepcopy(
+                groupcache[groupcounter - 2]['vars'])
+            try:
+                del groupcache[groupcounter]['vars'][name][
+                    groupcache[groupcounter]['vars'][name]['attrspec'].index('external')]
+            except Exception:
+                pass
+        if block in ['function', 'subroutine']:  # set global attributes
+            # name is fortran name
+            if bindcline:
+                bindcdat = re.search(crackline_bindlang, bindcline)
+                if bindcdat:
+                    groupcache[groupcounter]['bindlang'] = {name : {}}
+                    groupcache[groupcounter]['bindlang'][name]["lang"] = bindcdat.group('lang')
+                    if bindcdat.group('lang_name'):
+                        groupcache[groupcounter]['bindlang'][name]["name"] = bindcdat.group('lang_name')
+            try:
+                groupcache[groupcounter]['vars'][name] = appenddecl(
+                    groupcache[groupcounter]['vars'][name], groupcache[groupcounter - 2]['vars'][''])
+            except Exception:
+                pass
+            if case == 'callfun':  # return type
+                if result and result in groupcache[groupcounter]['vars']:
+                    if not name == result:
+                        groupcache[groupcounter]['vars'][name] = appenddecl(
+                            groupcache[groupcounter]['vars'][name], groupcache[groupcounter]['vars'][result])
+            # if groupcounter>1: # name is interfaced
+            try:
+                groupcache[groupcounter - 2]['interfaced'].append(name)
+            except Exception:
+                pass
+        if block == 'function':
+            t = typespattern[0].match(m.group('before') + ' ' + name)
+            if t:
+                typespec, selector, attr, edecl = cracktypespec0(
+                    t.group('this'), t.group('after'))
+                updatevars(typespec, selector, attr, edecl)
+
+        if case in ['call', 'callfun']:
+            grouplist[groupcounter - 1].append(groupcache[groupcounter])
+            grouplist[groupcounter - 1][-1]['body'] = grouplist[groupcounter]
+            del grouplist[groupcounter]
+            groupcounter = groupcounter - 1  # end routine
+            grouplist[groupcounter - 1].append(groupcache[groupcounter])
+            grouplist[groupcounter - 1][-1]['body'] = grouplist[groupcounter]
+            del grouplist[groupcounter]
+            groupcounter = groupcounter - 1  # end interface
+
+    elif case == 'entry':
+        name, args, result, _= _resolvenameargspattern(m.group('after'))
+        if name is not None:
+            if args:
+                args = rmbadname([x.strip()
+                                  for x in markoutercomma(args).split('@,@')])
+            else:
+                args = []
+            assert result is None, repr(result)
+            groupcache[groupcounter]['entry'][name] = args
+            previous_context = ('entry', name, groupcounter)
+    elif case == 'type':
+        typespec, selector, attr, edecl = cracktypespec0(
+            block, m.group('after'))
+        last_name = updatevars(typespec, selector, attr, edecl)
+        if last_name is not None:
+            previous_context = ('variable', last_name, groupcounter)
+    elif case in ['dimension', 'intent', 'optional', 'required', 'external', 'public', 'private', 'intrinsic']:
+        edecl = groupcache[groupcounter]['vars']
+        ll = m.group('after').strip()
+        i = ll.find('::')
+        if i < 0 and case == 'intent':
+            i = markouterparen(ll).find('@)@') - 2
+            ll = ll[:i + 1] + '::' + ll[i + 1:]
+            i = ll.find('::')
+            if ll[i:] == '::' and 'args' in groupcache[groupcounter]:
+                outmess('All arguments will have attribute %s%s\n' %
+                        (m.group('this'), ll[:i]))
+                ll = ll + ','.join(groupcache[groupcounter]['args'])
+        if i < 0:
+            i = 0
+            pl = ''
+        else:
+            pl = ll[:i].strip()
+            ll = ll[i + 2:]
+        ch = markoutercomma(pl).split('@,@')
+        if len(ch) > 1:
+            pl = ch[0]
+            outmess('analyzeline: cannot handle multiple attributes without type specification. Ignoring %r.\n' % (
+                ','.join(ch[1:])))
+        last_name = None
+
+        for e in [x.strip() for x in markoutercomma(ll).split('@,@')]:
+            m1 = namepattern.match(e)
+            if not m1:
+                if case in ['public', 'private']:
+                    k = ''
+                else:
+                    print(m.groupdict())
+                    outmess('analyzeline: no name pattern found in %s statement for %s. Skipping.\n' % (
+                        case, repr(e)))
+                    continue
+            else:
+                k = rmbadname1(m1.group('name'))
+            if case in ['public', 'private'] and \
+               (k == 'operator' or k == 'assignment'):
+                k += m1.group('after')
+            if k not in edecl:
+                edecl[k] = {}
+            if case == 'dimension':
+                ap = case + m1.group('after')
+            if case == 'intent':
+                ap = m.group('this') + pl
+                if _intentcallbackpattern.match(ap):
+                    if k not in groupcache[groupcounter]['args']:
+                        if groupcounter > 1:
+                            if '__user__' not in groupcache[groupcounter - 2]['name']:
+                                outmess(
+                                    'analyzeline: missing __user__ module (could be nothing)\n')
+                            # fixes ticket 1693
+                            if k != groupcache[groupcounter]['name']:
+                                outmess('analyzeline: appending intent(callback) %s'
+                                        ' to %s arguments\n' % (k, groupcache[groupcounter]['name']))
+                                groupcache[groupcounter]['args'].append(k)
+                        else:
+                            errmess(
+                                'analyzeline: intent(callback) %s is ignored\n' % (k))
+                    else:
+                        errmess('analyzeline: intent(callback) %s is already'
+                                ' in argument list\n' % (k))
+            if case in ['optional', 'required', 'public', 'external', 'private', 'intrinsic']:
+                ap = case
+            if 'attrspec' in edecl[k]:
+                edecl[k]['attrspec'].append(ap)
+            else:
+                edecl[k]['attrspec'] = [ap]
+            if case == 'external':
+                if groupcache[groupcounter]['block'] == 'program':
+                    outmess('analyzeline: ignoring program arguments\n')
+                    continue
+                if k not in groupcache[groupcounter]['args']:
+                    continue
+                if 'externals' not in groupcache[groupcounter]:
+                    groupcache[groupcounter]['externals'] = []
+                groupcache[groupcounter]['externals'].append(k)
+            last_name = k
+        groupcache[groupcounter]['vars'] = edecl
+        if last_name is not None:
+            previous_context = ('variable', last_name, groupcounter)
+    elif case == 'moduleprocedure':
+        groupcache[groupcounter]['implementedby'] = \
+            [x.strip() for x in m.group('after').split(',')]
+    elif case == 'parameter':
+        edecl = groupcache[groupcounter]['vars']
+        ll = m.group('after').strip()[1:-1]
+        last_name = None
+        for e in markoutercomma(ll).split('@,@'):
+            try:
+                k, initexpr = [x.strip() for x in e.split('=')]
+            except Exception:
+                outmess(
+                    'analyzeline: could not extract name,expr in parameter statement "%s" of "%s"\n' % (e, ll))
+                continue
+            params = get_parameters(edecl)
+            k = rmbadname1(k)
+            if k not in edecl:
+                edecl[k] = {}
+            if '=' in edecl[k] and (not edecl[k]['='] == initexpr):
+                outmess('analyzeline: Overwriting the value of parameter "%s" ("%s") with "%s".\n' % (
+                    k, edecl[k]['='], initexpr))
+            t = determineexprtype(initexpr, params)
+            if t:
+                if t.get('typespec') == 'real':
+                    tt = list(initexpr)
+                    for m in real16pattern.finditer(initexpr):
+                        tt[m.start():m.end()] = list(
+                            initexpr[m.start():m.end()].lower().replace('d', 'e'))
+                    initexpr = ''.join(tt)
+                elif t.get('typespec') == 'complex':
+                    initexpr = initexpr[1:].lower().replace('d', 'e').\
+                        replace(',', '+1j*(')
+            try:
+                v = eval(initexpr, {}, params)
+            except (SyntaxError, NameError, TypeError) as msg:
+                errmess('analyzeline: Failed to evaluate %r. Ignoring: %s\n'
+                        % (initexpr, msg))
+                continue
+            edecl[k]['='] = repr(v)
+            if 'attrspec' in edecl[k]:
+                edecl[k]['attrspec'].append('parameter')
+            else:
+                edecl[k]['attrspec'] = ['parameter']
+            last_name = k
+        groupcache[groupcounter]['vars'] = edecl
+        if last_name is not None:
+            previous_context = ('variable', last_name, groupcounter)
+    elif case == 'implicit':
+        if m.group('after').strip().lower() == 'none':
+            groupcache[groupcounter]['implicit'] = None
+        elif m.group('after'):
+            if 'implicit' in groupcache[groupcounter]:
+                impl = groupcache[groupcounter]['implicit']
+            else:
+                impl = {}
+            if impl is None:
+                outmess(
+                    'analyzeline: Overwriting earlier "implicit none" statement.\n')
+                impl = {}
+            for e in markoutercomma(m.group('after')).split('@,@'):
+                decl = {}
+                m1 = re.match(
+                    r'\s*(?P<this>.*?)\s*(\(\s*(?P<after>[a-z-, ]+)\s*\)\s*|)\Z', e, re.I)
+                if not m1:
+                    outmess(
+                        'analyzeline: could not extract info of implicit statement part "%s"\n' % (e))
+                    continue
+                m2 = typespattern4implicit.match(m1.group('this'))
+                if not m2:
+                    outmess(
+                        'analyzeline: could not extract types pattern of implicit statement part "%s"\n' % (e))
+                    continue
+                typespec, selector, attr, edecl = cracktypespec0(
+                    m2.group('this'), m2.group('after'))
+                kindselect, charselect, typename = cracktypespec(
+                    typespec, selector)
+                decl['typespec'] = typespec
+                decl['kindselector'] = kindselect
+                decl['charselector'] = charselect
+                decl['typename'] = typename
+                for k in list(decl.keys()):
+                    if not decl[k]:
+                        del decl[k]
+                for r in markoutercomma(m1.group('after')).split('@,@'):
+                    if '-' in r:
+                        try:
+                            begc, endc = [x.strip() for x in r.split('-')]
+                        except Exception:
+                            outmess(
+                                'analyzeline: expected "<char>-<char>" instead of "%s" in range list of implicit statement\n' % r)
+                            continue
+                    else:
+                        begc = endc = r.strip()
+                    if not len(begc) == len(endc) == 1:
+                        outmess(
+                            'analyzeline: expected "<char>-<char>" instead of "%s" in range list of implicit statement (2)\n' % r)
+                        continue
+                    for o in range(ord(begc), ord(endc) + 1):
+                        impl[chr(o)] = decl
+            groupcache[groupcounter]['implicit'] = impl
+    elif case == 'data':
+        ll = []
+        dl = ''
+        il = ''
+        f = 0
+        fc = 1
+        inp = 0
+        for c in m.group('after'):
+            if not inp:
+                if c == "'":
+                    fc = not fc
+                if c == '/' and fc:
+                    f = f + 1
+                    continue
+            if c == '(':
+                inp = inp + 1
+            elif c == ')':
+                inp = inp - 1
+            if f == 0:
+                dl = dl + c
+            elif f == 1:
+                il = il + c
+            elif f == 2:
+                dl = dl.strip()
+                if dl.startswith(','):
+                    dl = dl[1:].strip()
+                ll.append([dl, il])
+                dl = c
+                il = ''
+                f = 0
+        if f == 2:
+            dl = dl.strip()
+            if dl.startswith(','):
+                dl = dl[1:].strip()
+            ll.append([dl, il])
+        vars = groupcache[groupcounter].get('vars', {})
+        last_name = None
+        for l in ll:
+            l[0], l[1] = l[0].strip(), l[1].strip()
+            if l[0].startswith(','):
+                l[0] = l[0][1:]
+            if l[0].startswith('('):
+                outmess('analyzeline: implied-DO list "%s" is not supported. Skipping.\n' % l[0])
+                continue
+            for idx, v in enumerate(rmbadname([x.strip() for x in markoutercomma(l[0]).split('@,@')])):
+                if v.startswith('('):
+                    outmess('analyzeline: implied-DO list "%s" is not supported. Skipping.\n' % v)
+                    # XXX: subsequent init expressions may get wrong values.
+                    # Ignoring since data statements are irrelevant for
+                    # wrapping.
+                    continue
+                if '!' in l[1]:
+                    # Fixes gh-24746 pyf generation
+                    # XXX: This essentially ignores the value for generating the pyf which is fine:
+                    # integer dimension(3) :: mytab
+                    # common /mycom/ mytab
+                    # Since in any case it is initialized in the Fortran code
+                    outmess('Comment line in declaration "%s" is not supported. Skipping.\n' % l[1])
+                    continue
+                vars.setdefault(v, {})
+                vtype = vars[v].get('typespec')
+                vdim = getdimension(vars[v])
+                matches = re.findall(r"\(.*?\)", l[1]) if vtype == 'complex' else l[1].split(',')
+                try:
+                    new_val = "(/{}/)".format(", ".join(matches)) if vdim else matches[idx]
+                except IndexError:
+                    # gh-24746
+                    # Runs only if above code fails. Fixes the line
+                    # DATA IVAR1, IVAR2, IVAR3, IVAR4, EVAR5 /4*0,0.0D0/
+                    # by expanding to ['0', '0', '0', '0', '0.0d0']
+                    if any("*" in m for m in matches):
+                        expanded_list = []
+                        for match in matches:
+                            if "*" in match:
+                                try:
+                                    multiplier, value = match.split("*")
+                                    expanded_list.extend([value.strip()] * int(multiplier))
+                                except ValueError: # if int(multiplier) fails
+                                    expanded_list.append(match.strip())
+                            else:
+                                expanded_list.append(match.strip())
+                        matches = expanded_list
+                    new_val = "(/{}/)".format(", ".join(matches)) if vdim else matches[idx]
+                current_val = vars[v].get('=')
+                if current_val and (current_val != new_val):
+                    outmess('analyzeline: changing init expression of "%s" ("%s") to "%s"\n' % (v, current_val, new_val))
+                vars[v]['='] = new_val
+                last_name = v
+        groupcache[groupcounter]['vars'] = vars
+        if last_name:
+            previous_context = ('variable', last_name, groupcounter)
+    elif case == 'common':
+        line = m.group('after').strip()
+        if not line[0] == '/':
+            line = '//' + line
+        cl = []
+        f = 0
+        bn = ''
+        ol = ''
+        for c in line:
+            if c == '/':
+                f = f + 1
+                continue
+            if f >= 3:
+                bn = bn.strip()
+                if not bn:
+                    bn = '_BLNK_'
+                cl.append([bn, ol])
+                f = f - 2
+                bn = ''
+                ol = ''
+            if f % 2:
+                bn = bn + c
+            else:
+                ol = ol + c
+        bn = bn.strip()
+        if not bn:
+            bn = '_BLNK_'
+        cl.append([bn, ol])
+        commonkey = {}
+        if 'common' in groupcache[groupcounter]:
+            commonkey = groupcache[groupcounter]['common']
+        for c in cl:
+            if c[0] not in commonkey:
+                commonkey[c[0]] = []
+            for i in [x.strip() for x in markoutercomma(c[1]).split('@,@')]:
+                if i:
+                    commonkey[c[0]].append(i)
+        groupcache[groupcounter]['common'] = commonkey
+        previous_context = ('common', bn, groupcounter)
+    elif case == 'use':
+        m1 = re.match(
+            r'\A\s*(?P<name>\b\w+\b)\s*((,(\s*\bonly\b\s*:|(?P<notonly>))\s*(?P<list>.*))|)\s*\Z', m.group('after'), re.I)
+        if m1:
+            mm = m1.groupdict()
+            if 'use' not in groupcache[groupcounter]:
+                groupcache[groupcounter]['use'] = {}
+            name = m1.group('name')
+            groupcache[groupcounter]['use'][name] = {}
+            isonly = 0
+            if 'list' in mm and mm['list'] is not None:
+                if 'notonly' in mm and mm['notonly'] is None:
+                    isonly = 1
+                groupcache[groupcounter]['use'][name]['only'] = isonly
+                ll = [x.strip() for x in mm['list'].split(',')]
+                rl = {}
+                for l in ll:
+                    if '=' in l:
+                        m2 = re.match(
+                            r'\A\s*(?P<local>\b\w+\b)\s*=\s*>\s*(?P<use>\b\w+\b)\s*\Z', l, re.I)
+                        if m2:
+                            rl[m2.group('local').strip()] = m2.group(
+                                'use').strip()
+                        else:
+                            outmess(
+                                'analyzeline: Not local=>use pattern found in %s\n' % repr(l))
+                    else:
+                        rl[l] = l
+                    groupcache[groupcounter]['use'][name]['map'] = rl
+            else:
+                pass
+        else:
+            print(m.groupdict())
+            outmess('analyzeline: Could not crack the use statement.\n')
+    elif case in ['f2pyenhancements']:
+        if 'f2pyenhancements' not in groupcache[groupcounter]:
+            groupcache[groupcounter]['f2pyenhancements'] = {}
+        d = groupcache[groupcounter]['f2pyenhancements']
+        if m.group('this') == 'usercode' and 'usercode' in d:
+            if isinstance(d['usercode'], str):
+                d['usercode'] = [d['usercode']]
+            d['usercode'].append(m.group('after'))
+        else:
+            d[m.group('this')] = m.group('after')
+    elif case == 'multiline':
+        if previous_context is None:
+            if verbose:
+                outmess('analyzeline: No context for multiline block.\n')
+            return
+        gc = groupcounter
+        appendmultiline(groupcache[gc],
+                        previous_context[:2],
+                        m.group('this'))
+    else:
+        if verbose > 1:
+            print(m.groupdict())
+            outmess('analyzeline: No code implemented for line.\n')
+
+
+def appendmultiline(group, context_name, ml):
+    if 'f2pymultilines' not in group:
+        group['f2pymultilines'] = {}
+    d = group['f2pymultilines']
+    if context_name not in d:
+        d[context_name] = []
+    d[context_name].append(ml)
+    return
+
+
+def cracktypespec0(typespec, ll):
+    selector = None
+    attr = None
+    if re.match(r'double\s*complex', typespec, re.I):
+        typespec = 'double complex'
+    elif re.match(r'double\s*precision', typespec, re.I):
+        typespec = 'double precision'
+    else:
+        typespec = typespec.strip().lower()
+    m1 = selectpattern.match(markouterparen(ll))
+    if not m1:
+        outmess(
+            'cracktypespec0: no kind/char_selector pattern found for line.\n')
+        return
+    d = m1.groupdict()
+    for k in list(d.keys()):
+        d[k] = unmarkouterparen(d[k])
+    if typespec in ['complex', 'integer', 'logical', 'real', 'character', 'type']:
+        selector = d['this']
+        ll = d['after']
+    i = ll.find('::')
+    if i >= 0:
+        attr = ll[:i].strip()
+        ll = ll[i + 2:]
+    return typespec, selector, attr, ll
+#####
+namepattern = re.compile(r'\s*(?P<name>\b\w+\b)\s*(?P<after>.*)\s*\Z', re.I)
+kindselector = re.compile(
+    r'\s*(\(\s*(kind\s*=)?\s*(?P<kind>.*)\s*\)|\*\s*(?P<kind2>.*?))\s*\Z', re.I)
+charselector = re.compile(
+    r'\s*(\((?P<lenkind>.*)\)|\*\s*(?P<charlen>.*))\s*\Z', re.I)
+lenkindpattern = re.compile(
+    r'\s*(kind\s*=\s*(?P<kind>.*?)\s*(@,@\s*len\s*=\s*(?P<len>.*)|)'
+    r'|(len\s*=\s*|)(?P<len2>.*?)\s*(@,@\s*(kind\s*=\s*|)(?P<kind2>.*)'
+    r'|(f2py_len\s*=\s*(?P<f2py_len>.*))|))\s*\Z', re.I)
+lenarraypattern = re.compile(
+    r'\s*(@\(@\s*(?!/)\s*(?P<array>.*?)\s*@\)@\s*\*\s*(?P<len>.*?)|(\*\s*(?P<len2>.*?)|)\s*(@\(@\s*(?!/)\s*(?P<array2>.*?)\s*@\)@|))\s*(=\s*(?P<init>.*?)|(@\(@|)/\s*(?P<init2>.*?)\s*/(@\)@|)|)\s*\Z', re.I)
+
+
+def removespaces(expr):
+    expr = expr.strip()
+    if len(expr) <= 1:
+        return expr
+    expr2 = expr[0]
+    for i in range(1, len(expr) - 1):
+        if (expr[i] == ' ' and
+            ((expr[i + 1] in "()[]{}=+-/* ") or
+                (expr[i - 1] in "()[]{}=+-/* "))):
+            continue
+        expr2 = expr2 + expr[i]
+    expr2 = expr2 + expr[-1]
+    return expr2
+
+
+def markinnerspaces(line):
+    """
+    The function replace all spaces in the input variable line which are 
+    surrounded with quotation marks, with the triplet "@_@".
+
+    For instance, for the input "a 'b c'" the function returns "a 'b@_@c'"
+
+    Parameters
+    ----------
+    line : str
+
+    Returns
+    -------
+    str
+
+    """  
+    fragment = ''
+    inside = False
+    current_quote = None
+    escaped = ''
+    for c in line:
+        if escaped == '\\' and c in ['\\', '\'', '"']:
+            fragment += c
+            escaped = c
+            continue
+        if not inside and c in ['\'', '"']:
+            current_quote = c
+        if c == current_quote:
+            inside = not inside
+        elif c == ' ' and inside:
+            fragment += '@_@'
+            continue
+        fragment += c
+        escaped = c  # reset to non-backslash
+    return fragment
+
+
+def updatevars(typespec, selector, attrspec, entitydecl):
+    """
+    Returns last_name, the variable name without special chars, parenthesis
+        or dimension specifiers.
+
+    Alters groupcache to add the name, typespec, attrspec (and possibly value)
+    of current variable.
+    """
+    global groupcache, groupcounter
+
+    last_name = None
+    kindselect, charselect, typename = cracktypespec(typespec, selector)
+    # Clean up outer commas, whitespace and undesired chars from attrspec
+    if attrspec:
+        attrspec = [x.strip() for x in markoutercomma(attrspec).split('@,@')]
+        l = []
+        c = re.compile(r'(?P<start>[a-zA-Z]+)')
+        for a in attrspec:
+            if not a:
+                continue
+            m = c.match(a)
+            if m:
+                s = m.group('start').lower()
+                a = s + a[len(s):]
+            l.append(a)
+        attrspec = l
+    el = [x.strip() for x in markoutercomma(entitydecl).split('@,@')]
+    el1 = []
+    for e in el:
+        for e1 in [x.strip() for x in markoutercomma(removespaces(markinnerspaces(e)), comma=' ').split('@ @')]:
+            if e1:
+                el1.append(e1.replace('@_@', ' '))
+    for e in el1:
+        m = namepattern.match(e)
+        if not m:
+            outmess(
+                'updatevars: no name pattern found for entity=%s. Skipping.\n' % (repr(e)))
+            continue
+        ename = rmbadname1(m.group('name'))
+        edecl = {}
+        if ename in groupcache[groupcounter]['vars']:
+            edecl = groupcache[groupcounter]['vars'][ename].copy()
+            not_has_typespec = 'typespec' not in edecl
+            if not_has_typespec:
+                edecl['typespec'] = typespec
+            elif typespec and (not typespec == edecl['typespec']):
+                outmess('updatevars: attempt to change the type of "%s" ("%s") to "%s". Ignoring.\n' % (
+                    ename, edecl['typespec'], typespec))
+            if 'kindselector' not in edecl:
+                edecl['kindselector'] = copy.copy(kindselect)
+            elif kindselect:
+                for k in list(kindselect.keys()):
+                    if k in edecl['kindselector'] and (not kindselect[k] == edecl['kindselector'][k]):
+                        outmess('updatevars: attempt to change the kindselector "%s" of "%s" ("%s") to "%s". Ignoring.\n' % (
+                            k, ename, edecl['kindselector'][k], kindselect[k]))
+                    else:
+                        edecl['kindselector'][k] = copy.copy(kindselect[k])
+            if 'charselector' not in edecl and charselect:
+                if not_has_typespec:
+                    edecl['charselector'] = charselect
+                else:
+                    errmess('updatevars:%s: attempt to change empty charselector to %r. Ignoring.\n'
+                            % (ename, charselect))
+            elif charselect:
+                for k in list(charselect.keys()):
+                    if k in edecl['charselector'] and (not charselect[k] == edecl['charselector'][k]):
+                        outmess('updatevars: attempt to change the charselector "%s" of "%s" ("%s") to "%s". Ignoring.\n' % (
+                            k, ename, edecl['charselector'][k], charselect[k]))
+                    else:
+                        edecl['charselector'][k] = copy.copy(charselect[k])
+            if 'typename' not in edecl:
+                edecl['typename'] = typename
+            elif typename and (not edecl['typename'] == typename):
+                outmess('updatevars: attempt to change the typename of "%s" ("%s") to "%s". Ignoring.\n' % (
+                    ename, edecl['typename'], typename))
+            if 'attrspec' not in edecl:
+                edecl['attrspec'] = copy.copy(attrspec)
+            elif attrspec:
+                for a in attrspec:
+                    if a not in edecl['attrspec']:
+                        edecl['attrspec'].append(a)
+        else:
+            edecl['typespec'] = copy.copy(typespec)
+            edecl['kindselector'] = copy.copy(kindselect)
+            edecl['charselector'] = copy.copy(charselect)
+            edecl['typename'] = typename
+            edecl['attrspec'] = copy.copy(attrspec)
+        if 'external' in (edecl.get('attrspec') or []) and e in groupcache[groupcounter]['args']:
+            if 'externals' not in groupcache[groupcounter]:
+                groupcache[groupcounter]['externals'] = []
+            groupcache[groupcounter]['externals'].append(e)
+        if m.group('after'):
+            m1 = lenarraypattern.match(markouterparen(m.group('after')))
+            if m1:
+                d1 = m1.groupdict()
+                for lk in ['len', 'array', 'init']:
+                    if d1[lk + '2'] is not None:
+                        d1[lk] = d1[lk + '2']
+                        del d1[lk + '2']
+                for k in list(d1.keys()):
+                    if d1[k] is not None:
+                        d1[k] = unmarkouterparen(d1[k])
+                    else:
+                        del d1[k]
+
+                if 'len' in d1 and 'array' in d1:
+                    if d1['len'] == '':
+                        d1['len'] = d1['array']
+                        del d1['array']
+                    elif typespec == 'character':
+                        if ('charselector' not in edecl) or (not edecl['charselector']):
+                            edecl['charselector'] = {}
+                        if 'len' in edecl['charselector']:
+                            del edecl['charselector']['len']
+                        edecl['charselector']['*'] = d1['len']
+                        del d1['len']
+                    else:
+                        d1['array'] = d1['array'] + ',' + d1['len']
+                        del d1['len']
+                        errmess('updatevars: "%s %s" is mapped to "%s %s(%s)"\n' % (
+                            typespec, e, typespec, ename, d1['array']))
+
+                if 'len' in d1:
+                    if typespec in ['complex', 'integer', 'logical', 'real']:
+                        if ('kindselector' not in edecl) or (not edecl['kindselector']):
+                            edecl['kindselector'] = {}
+                        edecl['kindselector']['*'] = d1['len']
+                        del d1['len']
+                    elif typespec == 'character':
+                        if ('charselector' not in edecl) or (not edecl['charselector']):
+                            edecl['charselector'] = {}
+                        if 'len' in edecl['charselector']:
+                            del edecl['charselector']['len']
+                        edecl['charselector']['*'] = d1['len']
+                        del d1['len']
+
+                if 'init' in d1:
+                    if '=' in edecl and (not edecl['='] == d1['init']):
+                        outmess('updatevars: attempt to change the init expression of "%s" ("%s") to "%s". Ignoring.\n' % (
+                            ename, edecl['='], d1['init']))
+                    else:
+                        edecl['='] = d1['init']
+
+                if 'array' in d1:
+                    dm = 'dimension(%s)' % d1['array']
+                    if 'attrspec' not in edecl or (not edecl['attrspec']):
+                        edecl['attrspec'] = [dm]
+                    else:
+                        edecl['attrspec'].append(dm)
+                        for dm1 in edecl['attrspec']:
+                            if dm1[:9] == 'dimension' and dm1 != dm:
+                                del edecl['attrspec'][-1]
+                                errmess('updatevars:%s: attempt to change %r to %r. Ignoring.\n'
+                                        % (ename, dm1, dm))
+                                break
+
+            else:
+                outmess('updatevars: could not crack entity declaration "%s". Ignoring.\n' % (
+                    ename + m.group('after')))
+        for k in list(edecl.keys()):
+            if not edecl[k]:
+                del edecl[k]
+        groupcache[groupcounter]['vars'][ename] = edecl
+        if 'varnames' in groupcache[groupcounter]:
+            groupcache[groupcounter]['varnames'].append(ename)
+        last_name = ename
+    return last_name
+
+
+def cracktypespec(typespec, selector):
+    kindselect = None
+    charselect = None
+    typename = None
+    if selector:
+        if typespec in ['complex', 'integer', 'logical', 'real']:
+            kindselect = kindselector.match(selector)
+            if not kindselect:
+                outmess(
+                    'cracktypespec: no kindselector pattern found for %s\n' % (repr(selector)))
+                return
+            kindselect = kindselect.groupdict()
+            kindselect['*'] = kindselect['kind2']
+            del kindselect['kind2']
+            for k in list(kindselect.keys()):
+                if not kindselect[k]:
+                    del kindselect[k]
+            for k, i in list(kindselect.items()):
+                kindselect[k] = rmbadname1(i)
+        elif typespec == 'character':
+            charselect = charselector.match(selector)
+            if not charselect:
+                outmess(
+                    'cracktypespec: no charselector pattern found for %s\n' % (repr(selector)))
+                return
+            charselect = charselect.groupdict()
+            charselect['*'] = charselect['charlen']
+            del charselect['charlen']
+            if charselect['lenkind']:
+                lenkind = lenkindpattern.match(
+                    markoutercomma(charselect['lenkind']))
+                lenkind = lenkind.groupdict()
+                for lk in ['len', 'kind']:
+                    if lenkind[lk + '2']:
+                        lenkind[lk] = lenkind[lk + '2']
+                    charselect[lk] = lenkind[lk]
+                    del lenkind[lk + '2']
+                if lenkind['f2py_len'] is not None:
+                    # used to specify the length of assumed length strings
+                    charselect['f2py_len'] = lenkind['f2py_len']
+            del charselect['lenkind']
+            for k in list(charselect.keys()):
+                if not charselect[k]:
+                    del charselect[k]
+            for k, i in list(charselect.items()):
+                charselect[k] = rmbadname1(i)
+        elif typespec == 'type':
+            typename = re.match(r'\s*\(\s*(?P<name>\w+)\s*\)', selector, re.I)
+            if typename:
+                typename = typename.group('name')
+            else:
+                outmess('cracktypespec: no typename found in %s\n' %
+                        (repr(typespec + selector)))
+        else:
+            outmess('cracktypespec: no selector used for %s\n' %
+                    (repr(selector)))
+    return kindselect, charselect, typename
+######
+
+
+def setattrspec(decl, attr, force=0):
+    if not decl:
+        decl = {}
+    if not attr:
+        return decl
+    if 'attrspec' not in decl:
+        decl['attrspec'] = [attr]
+        return decl
+    if force:
+        decl['attrspec'].append(attr)
+    if attr in decl['attrspec']:
+        return decl
+    if attr == 'static' and 'automatic' not in decl['attrspec']:
+        decl['attrspec'].append(attr)
+    elif attr == 'automatic' and 'static' not in decl['attrspec']:
+        decl['attrspec'].append(attr)
+    elif attr == 'public':
+        if 'private' not in decl['attrspec']:
+            decl['attrspec'].append(attr)
+    elif attr == 'private':
+        if 'public' not in decl['attrspec']:
+            decl['attrspec'].append(attr)
+    else:
+        decl['attrspec'].append(attr)
+    return decl
+
+
+def setkindselector(decl, sel, force=0):
+    if not decl:
+        decl = {}
+    if not sel:
+        return decl
+    if 'kindselector' not in decl:
+        decl['kindselector'] = sel
+        return decl
+    for k in list(sel.keys()):
+        if force or k not in decl['kindselector']:
+            decl['kindselector'][k] = sel[k]
+    return decl
+
+
+def setcharselector(decl, sel, force=0):
+    if not decl:
+        decl = {}
+    if not sel:
+        return decl
+    if 'charselector' not in decl:
+        decl['charselector'] = sel
+        return decl
+
+    for k in list(sel.keys()):
+        if force or k not in decl['charselector']:
+            decl['charselector'][k] = sel[k]
+    return decl
+
+
+def getblockname(block, unknown='unknown'):
+    if 'name' in block:
+        return block['name']
+    return unknown
+
+# post processing
+
+
+def setmesstext(block):
+    global filepositiontext
+
+    try:
+        filepositiontext = 'In: %s:%s\n' % (block['from'], block['name'])
+    except Exception:
+        pass
+
+
+def get_usedict(block):
+    usedict = {}
+    if 'parent_block' in block:
+        usedict = get_usedict(block['parent_block'])
+    if 'use' in block:
+        usedict.update(block['use'])
+    return usedict
+
+
+def get_useparameters(block, param_map=None):
+    global f90modulevars
+
+    if param_map is None:
+        param_map = {}
+    usedict = get_usedict(block)
+    if not usedict:
+        return param_map
+    for usename, mapping in list(usedict.items()):
+        usename = usename.lower()
+        if usename not in f90modulevars:
+            outmess('get_useparameters: no module %s info used by %s\n' %
+                    (usename, block.get('name')))
+            continue
+        mvars = f90modulevars[usename]
+        params = get_parameters(mvars)
+        if not params:
+            continue
+        # XXX: apply mapping
+        if mapping:
+            errmess('get_useparameters: mapping for %s not impl.\n' % (mapping))
+        for k, v in list(params.items()):
+            if k in param_map:
+                outmess('get_useparameters: overriding parameter %s with'
+                        ' value from module %s\n' % (repr(k), repr(usename)))
+            param_map[k] = v
+
+    return param_map
+
+
+def postcrack2(block, tab='', param_map=None):
+    global f90modulevars
+
+    if not f90modulevars:
+        return block
+    if isinstance(block, list):
+        ret = [postcrack2(g, tab=tab + '\t', param_map=param_map)
+               for g in block]
+        return ret
+    setmesstext(block)
+    outmess('%sBlock: %s\n' % (tab, block['name']), 0)
+
+    if param_map is None:
+        param_map = get_useparameters(block)
+
+    if param_map is not None and 'vars' in block:
+        vars = block['vars']
+        for n in list(vars.keys()):
+            var = vars[n]
+            if 'kindselector' in var:
+                kind = var['kindselector']
+                if 'kind' in kind:
+                    val = kind['kind']
+                    if val in param_map:
+                        kind['kind'] = param_map[val]
+    new_body = [postcrack2(b, tab=tab + '\t', param_map=param_map)
+                for b in block['body']]
+    block['body'] = new_body
+
+    return block
+
+
+def postcrack(block, args=None, tab=''):
+    """
+    TODO:
+          function return values
+          determine expression types if in argument list
+    """
+    global usermodules, onlyfunctions
+
+    if isinstance(block, list):
+        gret = []
+        uret = []
+        for g in block:
+            setmesstext(g)
+            g = postcrack(g, tab=tab + '\t')
+            # sort user routines to appear first
+            if 'name' in g and '__user__' in g['name']:
+                uret.append(g)
+            else:
+                gret.append(g)
+        return uret + gret
+    setmesstext(block)
+    if not isinstance(block, dict) and 'block' not in block:
+        raise Exception('postcrack: Expected block dictionary instead of ' +
+                        str(block))
+    if 'name' in block and not block['name'] == 'unknown_interface':
+        outmess('%sBlock: %s\n' % (tab, block['name']), 0)
+    block = analyzeargs(block)
+    block = analyzecommon(block)
+    block['vars'] = analyzevars(block)
+    block['sortvars'] = sortvarnames(block['vars'])
+    if 'args' in block and block['args']:
+        args = block['args']
+    block['body'] = analyzebody(block, args, tab=tab)
+
+    userisdefined = []
+    if 'use' in block:
+        useblock = block['use']
+        for k in list(useblock.keys()):
+            if '__user__' in k:
+                userisdefined.append(k)
+    else:
+        useblock = {}
+    name = ''
+    if 'name' in block:
+        name = block['name']
+    # and not userisdefined: # Build a __user__ module
+    if 'externals' in block and block['externals']:
+        interfaced = []
+        if 'interfaced' in block:
+            interfaced = block['interfaced']
+        mvars = copy.copy(block['vars'])
+        if name:
+            mname = name + '__user__routines'
+        else:
+            mname = 'unknown__user__routines'
+        if mname in userisdefined:
+            i = 1
+            while '%s_%i' % (mname, i) in userisdefined:
+                i = i + 1
+            mname = '%s_%i' % (mname, i)
+        interface = {'block': 'interface', 'body': [],
+                     'vars': {}, 'name': name + '_user_interface'}
+        for e in block['externals']:
+            if e in interfaced:
+                edef = []
+                j = -1
+                for b in block['body']:
+                    j = j + 1
+                    if b['block'] == 'interface':
+                        i = -1
+                        for bb in b['body']:
+                            i = i + 1
+                            if 'name' in bb and bb['name'] == e:
+                                edef = copy.copy(bb)
+                                del b['body'][i]
+                                break
+                        if edef:
+                            if not b['body']:
+                                del block['body'][j]
+                            del interfaced[interfaced.index(e)]
+                            break
+                interface['body'].append(edef)
+            else:
+                if e in mvars and not isexternal(mvars[e]):
+                    interface['vars'][e] = mvars[e]
+        if interface['vars'] or interface['body']:
+            block['interfaced'] = interfaced
+            mblock = {'block': 'python module', 'body': [
+                interface], 'vars': {}, 'name': mname, 'interfaced': block['externals']}
+            useblock[mname] = {}
+            usermodules.append(mblock)
+    if useblock:
+        block['use'] = useblock
+    return block
+
+
+def sortvarnames(vars):
+    indep = []
+    dep = []
+    for v in list(vars.keys()):
+        if 'depend' in vars[v] and vars[v]['depend']:
+            dep.append(v)
+        else:
+            indep.append(v)
+    n = len(dep)
+    i = 0
+    while dep:  # XXX: How to catch dependence cycles correctly?
+        v = dep[0]
+        fl = 0
+        for w in dep[1:]:
+            if w in vars[v]['depend']:
+                fl = 1
+                break
+        if fl:
+            dep = dep[1:] + [v]
+            i = i + 1
+            if i > n:
+                errmess('sortvarnames: failed to compute dependencies because'
+                        ' of cyclic dependencies between '
+                        + ', '.join(dep) + '\n')
+                indep = indep + dep
+                break
+        else:
+            indep.append(v)
+            dep = dep[1:]
+            n = len(dep)
+            i = 0
+    return indep
+
+
+def analyzecommon(block):
+    if not hascommon(block):
+        return block
+    commonvars = []
+    for k in list(block['common'].keys()):
+        comvars = []
+        for e in block['common'][k]:
+            m = re.match(
+                r'\A\s*\b(?P<name>.*?)\b\s*(\((?P<dims>.*?)\)|)\s*\Z', e, re.I)
+            if m:
+                dims = []
+                if m.group('dims'):
+                    dims = [x.strip()
+                            for x in markoutercomma(m.group('dims')).split('@,@')]
+                n = rmbadname1(m.group('name').strip())
+                if n in block['vars']:
+                    if 'attrspec' in block['vars'][n]:
+                        block['vars'][n]['attrspec'].append(
+                            'dimension(%s)' % (','.join(dims)))
+                    else:
+                        block['vars'][n]['attrspec'] = [
+                            'dimension(%s)' % (','.join(dims))]
+                else:
+                    if dims:
+                        block['vars'][n] = {
+                            'attrspec': ['dimension(%s)' % (','.join(dims))]}
+                    else:
+                        block['vars'][n] = {}
+                if n not in commonvars:
+                    commonvars.append(n)
+            else:
+                n = e
+                errmess(
+                    'analyzecommon: failed to extract "<name>[(<dims>)]" from "%s" in common /%s/.\n' % (e, k))
+            comvars.append(n)
+        block['common'][k] = comvars
+    if 'commonvars' not in block:
+        block['commonvars'] = commonvars
+    else:
+        block['commonvars'] = block['commonvars'] + commonvars
+    return block
+
+
+def analyzebody(block, args, tab=''):
+    global usermodules, skipfuncs, onlyfuncs, f90modulevars
+
+    setmesstext(block)
+
+    maybe_private = {
+        key: value
+        for key, value in block['vars'].items()
+        if 'attrspec' not in value or 'public' not in value['attrspec']
+    }
+
+    body = []
+    for b in block['body']:
+        b['parent_block'] = block
+        if b['block'] in ['function', 'subroutine']:
+            if args is not None and b['name'] not in args:
+                continue
+            else:
+                as_ = b['args']
+            # Add private members to skipfuncs for gh-23879
+            if b['name'] in maybe_private.keys():
+                skipfuncs.append(b['name'])
+            if b['name'] in skipfuncs:
+                continue
+            if onlyfuncs and b['name'] not in onlyfuncs:
+                continue
+            b['saved_interface'] = crack2fortrangen(
+                b, '\n' + ' ' * 6, as_interface=True)
+
+        else:
+            as_ = args
+        b = postcrack(b, as_, tab=tab + '\t')
+        if b['block'] in ['interface', 'abstract interface'] and \
+           not b['body'] and not b.get('implementedby'):
+            if 'f2pyenhancements' not in b:
+                continue
+        if b['block'].replace(' ', '') == 'pythonmodule':
+            usermodules.append(b)
+        else:
+            if b['block'] == 'module':
+                f90modulevars[b['name']] = b['vars']
+            body.append(b)
+    return body
+
+
+def buildimplicitrules(block):
+    setmesstext(block)
+    implicitrules = defaultimplicitrules
+    attrrules = {}
+    if 'implicit' in block:
+        if block['implicit'] is None:
+            implicitrules = None
+            if verbose > 1:
+                outmess(
+                    'buildimplicitrules: no implicit rules for routine %s.\n' % repr(block['name']))
+        else:
+            for k in list(block['implicit'].keys()):
+                if block['implicit'][k].get('typespec') not in ['static', 'automatic']:
+                    implicitrules[k] = block['implicit'][k]
+                else:
+                    attrrules[k] = block['implicit'][k]['typespec']
+    return implicitrules, attrrules
+
+
+def myeval(e, g=None, l=None):
+    """ Like `eval` but returns only integers and floats """
+    r = eval(e, g, l)
+    if type(r) in [int, float]:
+        return r
+    raise ValueError('r=%r' % (r))
+
+getlincoef_re_1 = re.compile(r'\A\b\w+\b\Z', re.I)
+
+
+def getlincoef(e, xset):  # e = a*x+b ; x in xset
+    """
+    Obtain ``a`` and ``b`` when ``e == "a*x+b"``, where ``x`` is a symbol in
+    xset.
+
+    >>> getlincoef('2*x + 1', {'x'})
+    (2, 1, 'x')
+    >>> getlincoef('3*x + x*2 + 2 + 1', {'x'})
+    (5, 3, 'x')
+    >>> getlincoef('0', {'x'})
+    (0, 0, None)
+    >>> getlincoef('0*x', {'x'})
+    (0, 0, 'x')
+    >>> getlincoef('x*x', {'x'})
+    (None, None, None)
+
+    This can be tricked by sufficiently complex expressions
+
+    >>> getlincoef('(x - 0.5)*(x - 1.5)*(x - 1)*x + 2*x + 3', {'x'})
+    (2.0, 3.0, 'x')
+    """
+    try:
+        c = int(myeval(e, {}, {}))
+        return 0, c, None
+    except Exception:
+        pass
+    if getlincoef_re_1.match(e):
+        return 1, 0, e
+    len_e = len(e)
+    for x in xset:
+        if len(x) > len_e:
+            continue
+        if re.search(r'\w\s*\([^)]*\b' + x + r'\b', e):
+            # skip function calls having x as an argument, e.g max(1, x)
+            continue
+        re_1 = re.compile(r'(?P<before>.*?)\b' + x + r'\b(?P<after>.*)', re.I)
+        m = re_1.match(e)
+        if m:
+            try:
+                m1 = re_1.match(e)
+                while m1:
+                    ee = '%s(%s)%s' % (
+                        m1.group('before'), 0, m1.group('after'))
+                    m1 = re_1.match(ee)
+                b = myeval(ee, {}, {})
+                m1 = re_1.match(e)
+                while m1:
+                    ee = '%s(%s)%s' % (
+                        m1.group('before'), 1, m1.group('after'))
+                    m1 = re_1.match(ee)
+                a = myeval(ee, {}, {}) - b
+                m1 = re_1.match(e)
+                while m1:
+                    ee = '%s(%s)%s' % (
+                        m1.group('before'), 0.5, m1.group('after'))
+                    m1 = re_1.match(ee)
+                c = myeval(ee, {}, {})
+                # computing another point to be sure that expression is linear
+                m1 = re_1.match(e)
+                while m1:
+                    ee = '%s(%s)%s' % (
+                        m1.group('before'), 1.5, m1.group('after'))
+                    m1 = re_1.match(ee)
+                c2 = myeval(ee, {}, {})
+                if (a * 0.5 + b == c and a * 1.5 + b == c2):
+                    return a, b, x
+            except Exception:
+                pass
+            break
+    return None, None, None
+
+
+word_pattern = re.compile(r'\b[a-z][\w$]*\b', re.I)
+
+
+def _get_depend_dict(name, vars, deps):
+    if name in vars:
+        words = vars[name].get('depend', [])
+
+        if '=' in vars[name] and not isstring(vars[name]):
+            for word in word_pattern.findall(vars[name]['=']):
+                # The word_pattern may return values that are not
+                # only variables, they can be string content for instance
+                if word not in words and word in vars and word != name:
+                    words.append(word)
+        for word in words[:]:
+            for w in deps.get(word, []) \
+                    or _get_depend_dict(word, vars, deps):
+                if w not in words:
+                    words.append(w)
+    else:
+        outmess('_get_depend_dict: no dependence info for %s\n' % (repr(name)))
+        words = []
+    deps[name] = words
+    return words
+
+
+def _calc_depend_dict(vars):
+    names = list(vars.keys())
+    depend_dict = {}
+    for n in names:
+        _get_depend_dict(n, vars, depend_dict)
+    return depend_dict
+
+
+def get_sorted_names(vars):
+    depend_dict = _calc_depend_dict(vars)
+    names = []
+    for name in list(depend_dict.keys()):
+        if not depend_dict[name]:
+            names.append(name)
+            del depend_dict[name]
+    while depend_dict:
+        for name, lst in list(depend_dict.items()):
+            new_lst = [n for n in lst if n in depend_dict]
+            if not new_lst:
+                names.append(name)
+                del depend_dict[name]
+            else:
+                depend_dict[name] = new_lst
+    return [name for name in names if name in vars]
+
+
+def _kind_func(string):
+    # XXX: return something sensible.
+    if string[0] in "'\"":
+        string = string[1:-1]
+    if real16pattern.match(string):
+        return 8
+    elif real8pattern.match(string):
+        return 4
+    return 'kind(' + string + ')'
+
+
+def _selected_int_kind_func(r):
+    # XXX: This should be processor dependent
+    m = 10 ** r
+    if m <= 2 ** 8:
+        return 1
+    if m <= 2 ** 16:
+        return 2
+    if m <= 2 ** 32:
+        return 4
+    if m <= 2 ** 63:
+        return 8
+    if m <= 2 ** 128:
+        return 16
+    return -1
+
+
+def _selected_real_kind_func(p, r=0, radix=0):
+    # XXX: This should be processor dependent
+    # This is only verified for 0 <= p <= 20, possibly good for p <= 33 and above
+    if p < 7:
+        return 4
+    if p < 16:
+        return 8
+    machine = platform.machine().lower()
+    if machine.startswith(('aarch64', 'alpha', 'arm64', 'loongarch', 'mips', 'power', 'ppc', 'riscv', 's390x', 'sparc')):
+        if p <= 33:
+            return 16
+    else:
+        if p < 19:
+            return 10
+        elif p <= 33:
+            return 16
+    return -1
+
+
+def get_parameters(vars, global_params={}):
+    params = copy.copy(global_params)
+    g_params = copy.copy(global_params)
+    for name, func in [('kind', _kind_func),
+                       ('selected_int_kind', _selected_int_kind_func),
+                       ('selected_real_kind', _selected_real_kind_func), ]:
+        if name not in g_params:
+            g_params[name] = func
+    param_names = []
+    for n in get_sorted_names(vars):
+        if 'attrspec' in vars[n] and 'parameter' in vars[n]['attrspec']:
+            param_names.append(n)
+    kind_re = re.compile(r'\bkind\s*\(\s*(?P<value>.*)\s*\)', re.I)
+    selected_int_kind_re = re.compile(
+        r'\bselected_int_kind\s*\(\s*(?P<value>.*)\s*\)', re.I)
+    selected_kind_re = re.compile(
+        r'\bselected_(int|real)_kind\s*\(\s*(?P<value>.*)\s*\)', re.I)
+    for n in param_names:
+        if '=' in vars[n]:
+            v = vars[n]['=']
+            if islogical(vars[n]):
+                v = v.lower()
+                for repl in [
+                    ('.false.', 'False'),
+                    ('.true.', 'True'),
+                    # TODO: test .eq., .neq., etc replacements.
+                ]:
+                    v = v.replace(*repl)
+
+            v = kind_re.sub(r'kind("\1")', v)
+            v = selected_int_kind_re.sub(r'selected_int_kind(\1)', v)
+
+            # We need to act according to the data.
+            # The easy case is if the data has a kind-specifier,
+            # then we may easily remove those specifiers.
+            # However, it may be that the user uses other specifiers...(!)
+            is_replaced = False
+
+            if 'kindselector' in vars[n]:
+                # Remove kind specifier (including those defined
+                # by parameters)
+                if 'kind' in vars[n]['kindselector']:
+                    orig_v_len = len(v)
+                    v = v.replace('_' + vars[n]['kindselector']['kind'], '')
+                    # Again, this will be true if even a single specifier
+                    # has been replaced, see comment above.
+                    is_replaced = len(v) < orig_v_len
+
+            if not is_replaced:
+                if not selected_kind_re.match(v):
+                    v_ = v.split('_')
+                    # In case there are additive parameters
+                    if len(v_) > 1: 
+                        v = ''.join(v_[:-1]).lower().replace(v_[-1].lower(), '')
+
+            # Currently this will not work for complex numbers.
+            # There is missing code for extracting a complex number,
+            # which may be defined in either of these:
+            #  a) (Re, Im)
+            #  b) cmplx(Re, Im)
+            #  c) dcmplx(Re, Im)
+            #  d) cmplx(Re, Im, <prec>)
+
+            if isdouble(vars[n]):
+                tt = list(v)
+                for m in real16pattern.finditer(v):
+                    tt[m.start():m.end()] = list(
+                        v[m.start():m.end()].lower().replace('d', 'e'))
+                v = ''.join(tt)
+
+            elif iscomplex(vars[n]):
+                outmess(f'get_parameters[TODO]: '
+                        f'implement evaluation of complex expression {v}\n')
+
+            dimspec = ([s.lstrip('dimension').strip()
+                        for s in vars[n]['attrspec']
+                       if s.startswith('dimension')] or [None])[0]
+
+            # Handle _dp for gh-6624
+            # Also fixes gh-20460
+            if real16pattern.search(v):
+                v = 8
+            elif real8pattern.search(v):
+                v = 4
+            try:
+                params[n] = param_eval(v, g_params, params, dimspec=dimspec)
+            except Exception as msg:
+                params[n] = v
+                outmess(f'get_parameters: got "{msg}" on {n!r}\n')
+
+            if isstring(vars[n]) and isinstance(params[n], int):
+                params[n] = chr(params[n])
+            nl = n.lower()
+            if nl != n:
+                params[nl] = params[n]
+        else:
+            print(vars[n])
+            outmess(f'get_parameters:parameter {n!r} does not have value?!\n')
+    return params
+
+
+def _eval_length(length, params):
+    if length in ['(:)', '(*)', '*']:
+        return '(*)'
+    return _eval_scalar(length, params)
+
+
+_is_kind_number = re.compile(r'\d+_').match
+
+
+def _eval_scalar(value, params):
+    if _is_kind_number(value):
+        value = value.split('_')[0]
+    try:
+        # TODO: use symbolic from PR #19805
+        value = eval(value, {}, params)
+        value = (repr if isinstance(value, str) else str)(value)
+    except (NameError, SyntaxError, TypeError):
+        return value
+    except Exception as msg:
+        errmess('"%s" in evaluating %r '
+                '(available names: %s)\n'
+                % (msg, value, list(params.keys())))
+    return value
+
+
+def analyzevars(block):
+    """
+    Sets correct dimension information for each variable/parameter
+    """
+
+    global f90modulevars
+
+    setmesstext(block)
+    implicitrules, attrrules = buildimplicitrules(block)
+    vars = copy.copy(block['vars'])
+    if block['block'] == 'function' and block['name'] not in vars:
+        vars[block['name']] = {}
+    if '' in block['vars']:
+        del vars['']
+        if 'attrspec' in block['vars']['']:
+            gen = block['vars']['']['attrspec']
+            for n in set(vars) | set(b['name'] for b in block['body']):
+                for k in ['public', 'private']:
+                    if k in gen:
+                        vars[n] = setattrspec(vars.get(n, {}), k)
+    svars = []
+    args = block['args']
+    for a in args:
+        try:
+            vars[a]
+            svars.append(a)
+        except KeyError:
+            pass
+    for n in list(vars.keys()):
+        if n not in args:
+            svars.append(n)
+
+    params = get_parameters(vars, get_useparameters(block))
+    # At this point, params are read and interpreted, but
+    # the params used to define vars are not yet parsed
+    dep_matches = {}
+    name_match = re.compile(r'[A-Za-z][\w$]*').match
+    for v in list(vars.keys()):
+        m = name_match(v)
+        if m:
+            n = v[m.start():m.end()]
+            try:
+                dep_matches[n]
+            except KeyError:
+                dep_matches[n] = re.compile(r'.*\b%s\b' % (v), re.I).match
+    for n in svars:
+        if n[0] in list(attrrules.keys()):
+            vars[n] = setattrspec(vars[n], attrrules[n[0]])
+        if 'typespec' not in vars[n]:
+            if not('attrspec' in vars[n] and 'external' in vars[n]['attrspec']):
+                if implicitrules:
+                    ln0 = n[0].lower()
+                    for k in list(implicitrules[ln0].keys()):
+                        if k == 'typespec' and implicitrules[ln0][k] == 'undefined':
+                            continue
+                        if k not in vars[n]:
+                            vars[n][k] = implicitrules[ln0][k]
+                        elif k == 'attrspec':
+                            for l in implicitrules[ln0][k]:
+                                vars[n] = setattrspec(vars[n], l)
+                elif n in block['args']:
+                    outmess('analyzevars: typespec of variable %s is not defined in routine %s.\n' % (
+                        repr(n), block['name']))
+        if 'charselector' in vars[n]:
+            if 'len' in vars[n]['charselector']:
+                l = vars[n]['charselector']['len']
+                try:
+                    l = str(eval(l, {}, params))
+                except Exception:
+                    pass
+                vars[n]['charselector']['len'] = l
+
+        if 'kindselector' in vars[n]:
+            if 'kind' in vars[n]['kindselector']:
+                l = vars[n]['kindselector']['kind']
+                try:
+                    l = str(eval(l, {}, params))
+                except Exception:
+                    pass
+                vars[n]['kindselector']['kind'] = l
+
+        dimension_exprs = {}
+        if 'attrspec' in vars[n]:
+            attr = vars[n]['attrspec']
+            attr.reverse()
+            vars[n]['attrspec'] = []
+            dim, intent, depend, check, note = None, None, None, None, None
+            for a in attr:
+                if a[:9] == 'dimension':
+                    dim = (a[9:].strip())[1:-1]
+                elif a[:6] == 'intent':
+                    intent = (a[6:].strip())[1:-1]
+                elif a[:6] == 'depend':
+                    depend = (a[6:].strip())[1:-1]
+                elif a[:5] == 'check':
+                    check = (a[5:].strip())[1:-1]
+                elif a[:4] == 'note':
+                    note = (a[4:].strip())[1:-1]
+                else:
+                    vars[n] = setattrspec(vars[n], a)
+                if intent:
+                    if 'intent' not in vars[n]:
+                        vars[n]['intent'] = []
+                    for c in [x.strip() for x in markoutercomma(intent).split('@,@')]:
+                        # Remove spaces so that 'in out' becomes 'inout'
+                        tmp = c.replace(' ', '')
+                        if tmp not in vars[n]['intent']:
+                            vars[n]['intent'].append(tmp)
+                    intent = None
+                if note:
+                    note = note.replace('\\n\\n', '\n\n')
+                    note = note.replace('\\n ', '\n')
+                    if 'note' not in vars[n]:
+                        vars[n]['note'] = [note]
+                    else:
+                        vars[n]['note'].append(note)
+                    note = None
+                if depend is not None:
+                    if 'depend' not in vars[n]:
+                        vars[n]['depend'] = []
+                    for c in rmbadname([x.strip() for x in markoutercomma(depend).split('@,@')]):
+                        if c not in vars[n]['depend']:
+                            vars[n]['depend'].append(c)
+                    depend = None
+                if check is not None:
+                    if 'check' not in vars[n]:
+                        vars[n]['check'] = []
+                    for c in [x.strip() for x in markoutercomma(check).split('@,@')]:
+                        if c not in vars[n]['check']:
+                            vars[n]['check'].append(c)
+                    check = None
+            if dim and 'dimension' not in vars[n]:
+                vars[n]['dimension'] = []
+                for d in rmbadname(
+                        [x.strip() for x in markoutercomma(dim).split('@,@')]
+                ):
+                    # d is the expression inside the dimension declaration
+                    # Evaluate `d` with respect to params
+                    try:
+                        # the dimension for this variable depends on a
+                        # previously defined parameter
+                        d = param_parse(d, params)
+                    except (ValueError, IndexError, KeyError):
+                        outmess(
+                            ('analyzevars: could not parse dimension for '
+                            f'variable {d!r}\n')
+                        )
+
+                    dim_char = ':' if d == ':' else '*'
+                    if d == dim_char:
+                        dl = [dim_char]
+                    else:
+                        dl = markoutercomma(d, ':').split('@:@')
+                    if len(dl) == 2 and '*' in dl:  # e.g. dimension(5:*)
+                        dl = ['*']
+                        d = '*'
+                    if len(dl) == 1 and dl[0] != dim_char:
+                        dl = ['1', dl[0]]
+                    if len(dl) == 2:
+                        d1, d2 = map(symbolic.Expr.parse, dl)
+                        dsize = d2 - d1 + 1
+                        d = dsize.tostring(language=symbolic.Language.C)
+                        # find variables v that define d as a linear
+                        # function, `d == a * v + b`, and store
+                        # coefficients a and b for further analysis.
+                        solver_and_deps = {}
+                        for v in block['vars']:
+                            s = symbolic.as_symbol(v)
+                            if dsize.contains(s):
+                                try:
+                                    a, b = dsize.linear_solve(s)
+
+                                    def solve_v(s, a=a, b=b):
+                                        return (s - b) / a
+
+                                    all_symbols = set(a.symbols())
+                                    all_symbols.update(b.symbols())
+                                except RuntimeError as msg:
+                                    # d is not a linear function of v,
+                                    # however, if v can be determined
+                                    # from d using other means,
+                                    # implement the corresponding
+                                    # solve_v function here.
+                                    solve_v = None
+                                    all_symbols = set(dsize.symbols())
+                                v_deps = set(
+                                    s.data for s in all_symbols
+                                    if s.data in vars)
+                                solver_and_deps[v] = solve_v, list(v_deps)
+                        # Note that dsize may contain symbols that are
+                        # not defined in block['vars']. Here we assume
+                        # these correspond to Fortran/C intrinsic
+                        # functions or that are defined by other
+                        # means. We'll let the compiler validate the
+                        # definiteness of such symbols.
+                        dimension_exprs[d] = solver_and_deps
+                    vars[n]['dimension'].append(d)
+
+        if 'check' not in vars[n] and 'args' in block and n in block['args']:
+            # n is an argument that has no checks defined. Here we
+            # generate some consistency checks for n, and when n is an
+            # array, generate checks for its dimensions and construct
+            # initialization expressions.
+            n_deps = vars[n].get('depend', [])
+            n_checks = []
+            n_is_input = l_or(isintent_in, isintent_inout,
+                              isintent_inplace)(vars[n])
+            if isarray(vars[n]):  # n is array
+                for i, d in enumerate(vars[n]['dimension']):
+                    coeffs_and_deps = dimension_exprs.get(d)
+                    if coeffs_and_deps is None:
+                        # d is `:` or `*` or a constant expression
+                        pass
+                    elif n_is_input:
+                        # n is an input array argument and its shape
+                        # may define variables used in dimension
+                        # specifications.
+                        for v, (solver, deps) in coeffs_and_deps.items():
+                            def compute_deps(v, deps):
+                                for v1 in coeffs_and_deps.get(v, [None, []])[1]:
+                                    if v1 not in deps:
+                                        deps.add(v1)
+                                        compute_deps(v1, deps)
+                            all_deps = set()
+                            compute_deps(v, all_deps)
+                            if ((v in n_deps
+                                 or '=' in vars[v]
+                                 or 'depend' in vars[v])):
+                                # Skip a variable that
+                                # - n depends on
+                                # - has user-defined initialization expression
+                                # - has user-defined dependencies
+                                continue
+                            if solver is not None and v not in all_deps:
+                                # v can be solved from d, hence, we
+                                # make it an optional argument with
+                                # initialization expression:
+                                is_required = False
+                                init = solver(symbolic.as_symbol(
+                                    f'shape({n}, {i})'))
+                                init = init.tostring(
+                                    language=symbolic.Language.C)
+                                vars[v]['='] = init
+                                # n needs to be initialized before v. So,
+                                # making v dependent on n and on any
+                                # variables in solver or d.
+                                vars[v]['depend'] = [n] + deps
+                                if 'check' not in vars[v]:
+                                    # add check only when no
+                                    # user-specified checks exist
+                                    vars[v]['check'] = [
+                                        f'shape({n}, {i}) == {d}']
+                            else:
+                                # d is a non-linear function on v,
+                                # hence, v must be a required input
+                                # argument that n will depend on
+                                is_required = True
+                                if 'intent' not in vars[v]:
+                                    vars[v]['intent'] = []
+                                if 'in' not in vars[v]['intent']:
+                                    vars[v]['intent'].append('in')
+                                # v needs to be initialized before n
+                                n_deps.append(v)
+                                n_checks.append(
+                                    f'shape({n}, {i}) == {d}')
+                            v_attr = vars[v].get('attrspec', [])
+                            if not ('optional' in v_attr
+                                    or 'required' in v_attr):
+                                v_attr.append(
+                                    'required' if is_required else 'optional')
+                            if v_attr:
+                                vars[v]['attrspec'] = v_attr
+                    if coeffs_and_deps is not None:
+                        # extend v dependencies with ones specified in attrspec
+                        for v, (solver, deps) in coeffs_and_deps.items():
+                            v_deps = vars[v].get('depend', [])
+                            for aa in vars[v].get('attrspec', []):
+                                if aa.startswith('depend'):
+                                    aa = ''.join(aa.split())
+                                    v_deps.extend(aa[7:-1].split(','))
+                            if v_deps:
+                                vars[v]['depend'] = list(set(v_deps))
+                            if n not in v_deps:
+                                n_deps.append(v)
+            elif isstring(vars[n]):
+                if 'charselector' in vars[n]:
+                    if '*' in vars[n]['charselector']:
+                        length = _eval_length(vars[n]['charselector']['*'],
+                                              params)
+                        vars[n]['charselector']['*'] = length
+                    elif 'len' in vars[n]['charselector']:
+                        length = _eval_length(vars[n]['charselector']['len'],
+                                              params)
+                        del vars[n]['charselector']['len']
+                        vars[n]['charselector']['*'] = length
+            if n_checks:
+                vars[n]['check'] = n_checks
+            if n_deps:
+                vars[n]['depend'] = list(set(n_deps))
+
+        if '=' in vars[n]:
+            if 'attrspec' not in vars[n]:
+                vars[n]['attrspec'] = []
+            if ('optional' not in vars[n]['attrspec']) and \
+               ('required' not in vars[n]['attrspec']):
+                vars[n]['attrspec'].append('optional')
+            if 'depend' not in vars[n]:
+                vars[n]['depend'] = []
+                for v, m in list(dep_matches.items()):
+                    if m(vars[n]['=']):
+                        vars[n]['depend'].append(v)
+                if not vars[n]['depend']:
+                    del vars[n]['depend']
+            if isscalar(vars[n]):
+                vars[n]['='] = _eval_scalar(vars[n]['='], params)
+
+    for n in list(vars.keys()):
+        if n == block['name']:  # n is block name
+            if 'note' in vars[n]:
+                block['note'] = vars[n]['note']
+            if block['block'] == 'function':
+                if 'result' in block and block['result'] in vars:
+                    vars[n] = appenddecl(vars[n], vars[block['result']])
+                if 'prefix' in block:
+                    pr = block['prefix']
+                    pr1 = pr.replace('pure', '')
+                    ispure = (not pr == pr1)
+                    pr = pr1.replace('recursive', '')
+                    isrec = (not pr == pr1)
+                    m = typespattern[0].match(pr)
+                    if m:
+                        typespec, selector, attr, edecl = cracktypespec0(
+                            m.group('this'), m.group('after'))
+                        kindselect, charselect, typename = cracktypespec(
+                            typespec, selector)
+                        vars[n]['typespec'] = typespec
+                        try:
+                            if block['result']:
+                                vars[block['result']]['typespec'] = typespec
+                        except Exception:
+                            pass
+                        if kindselect:
+                            if 'kind' in kindselect:
+                                try:
+                                    kindselect['kind'] = eval(
+                                        kindselect['kind'], {}, params)
+                                except Exception:
+                                    pass
+                            vars[n]['kindselector'] = kindselect
+                        if charselect:
+                            vars[n]['charselector'] = charselect
+                        if typename:
+                            vars[n]['typename'] = typename
+                        if ispure:
+                            vars[n] = setattrspec(vars[n], 'pure')
+                        if isrec:
+                            vars[n] = setattrspec(vars[n], 'recursive')
+                    else:
+                        outmess(
+                            'analyzevars: prefix (%s) were not used\n' % repr(block['prefix']))
+    if not block['block'] in ['module', 'pythonmodule', 'python module', 'block data']:
+        if 'commonvars' in block:
+            neededvars = copy.copy(block['args'] + block['commonvars'])
+        else:
+            neededvars = copy.copy(block['args'])
+        for n in list(vars.keys()):
+            if l_or(isintent_callback, isintent_aux)(vars[n]):
+                neededvars.append(n)
+        if 'entry' in block:
+            neededvars.extend(list(block['entry'].keys()))
+            for k in list(block['entry'].keys()):
+                for n in block['entry'][k]:
+                    if n not in neededvars:
+                        neededvars.append(n)
+        if block['block'] == 'function':
+            if 'result' in block:
+                neededvars.append(block['result'])
+            else:
+                neededvars.append(block['name'])
+        if block['block'] in ['subroutine', 'function']:
+            name = block['name']
+            if name in vars and 'intent' in vars[name]:
+                block['intent'] = vars[name]['intent']
+        if block['block'] == 'type':
+            neededvars.extend(list(vars.keys()))
+        for n in list(vars.keys()):
+            if n not in neededvars:
+                del vars[n]
+    return vars
+
+
+analyzeargs_re_1 = re.compile(r'\A[a-z]+[\w$]*\Z', re.I)
+
+
+def param_eval(v, g_params, params, dimspec=None):
+    """
+    Creates a dictionary of indices and values for each parameter in a
+    parameter array to be evaluated later.
+
+    WARNING: It is not possible to initialize multidimensional array
+    parameters e.g. dimension(-3:1, 4, 3:5) at this point. This is because in
+    Fortran initialization through array constructor requires the RESHAPE
+    intrinsic function. Since the right-hand side of the parameter declaration
+    is not executed in f2py, but rather at the compiled c/fortran extension,
+    later, it is not possible to execute a reshape of a parameter array.
+    One issue remains: if the user wants to access the array parameter from
+    python, we should either
+    1) allow them to access the parameter array using python standard indexing
+       (which is often incompatible with the original fortran indexing)
+    2) allow the parameter array to be accessed in python as a dictionary with
+       fortran indices as keys
+    We are choosing 2 for now.
+    """
+    if dimspec is None:
+        try:
+            p = eval(v, g_params, params)
+        except Exception as msg:
+            p = v
+            outmess(f'param_eval: got "{msg}" on {v!r}\n')
+        return p
+
+    # This is an array parameter.
+    # First, we parse the dimension information
+    if len(dimspec) < 2 or dimspec[::len(dimspec)-1] != "()":
+        raise ValueError(f'param_eval: dimension {dimspec} can\'t be parsed')
+    dimrange = dimspec[1:-1].split(',')
+    if len(dimrange) == 1:
+        # e.g. dimension(2) or dimension(-1:1)
+        dimrange = dimrange[0].split(':')
+        # now, dimrange is a list of 1 or 2 elements
+        if len(dimrange) == 1:
+            bound = param_parse(dimrange[0], params)
+            dimrange = range(1, int(bound)+1)
+        else:
+            lbound = param_parse(dimrange[0], params)
+            ubound = param_parse(dimrange[1], params)
+            dimrange = range(int(lbound), int(ubound)+1)
+    else:
+        raise ValueError(f'param_eval: multidimensional array parameters '
+                         '{dimspec} not supported')
+
+    # Parse parameter value
+    v = (v[2:-2] if v.startswith('(/') else v).split(',')
+    v_eval = []
+    for item in v:
+        try:
+            item = eval(item, g_params, params)
+        except Exception as msg:
+            outmess(f'param_eval: got "{msg}" on {item!r}\n')
+        v_eval.append(item)
+
+    p = dict(zip(dimrange, v_eval))
+
+    return p
+
+
+def param_parse(d, params):
+    """Recursively parse array dimensions.
+
+    Parses the declaration of an array variable or parameter
+    `dimension` keyword, and is called recursively if the
+    dimension for this array is a previously defined parameter
+    (found in `params`).
+
+    Parameters
+    ----------
+    d : str
+        Fortran expression describing the dimension of an array.
+    params : dict
+        Previously parsed parameters declared in the Fortran source file.
+
+    Returns
+    -------
+    out : str
+        Parsed dimension expression.
+
+    Examples
+    --------
+
+    * If the line being analyzed is
+
+      `integer, parameter, dimension(2) :: pa = (/ 3, 5 /)`
+
+      then `d = 2` and we return immediately, with
+
+    >>> d = '2'
+    >>> param_parse(d, params)
+    2
+
+    * If the line being analyzed is
+
+      `integer, parameter, dimension(pa) :: pb = (/1, 2, 3/)`
+
+      then `d = 'pa'`; since `pa` is a previously parsed parameter,
+      and `pa = 3`, we call `param_parse` recursively, to obtain
+
+    >>> d = 'pa'
+    >>> params = {'pa': 3}
+    >>> param_parse(d, params)
+    3
+
+    * If the line being analyzed is
+
+      `integer, parameter, dimension(pa(1)) :: pb = (/1, 2, 3/)`
+
+      then `d = 'pa(1)'`; since `pa` is a previously parsed parameter,
+      and `pa(1) = 3`, we call `param_parse` recursively, to obtain
+
+    >>> d = 'pa(1)'
+    >>> params = dict(pa={1: 3, 2: 5})
+    >>> param_parse(d, params)
+    3
+    """
+    if "(" in d:
+        # this dimension expression is an array
+        dname = d[:d.find("(")]
+        ddims = d[d.find("(")+1:d.rfind(")")]
+        # this dimension expression is also a parameter;
+        # parse it recursively
+        index = int(param_parse(ddims, params))
+        return str(params[dname][index])
+    elif d in params:
+        return str(params[d])
+    else:
+        for p in params:
+            re_1 = re.compile(
+                r'(?P<before>.*?)\b' + p + r'\b(?P<after>.*)', re.I
+            )
+            m = re_1.match(d)
+            while m:
+                d = m.group('before') + \
+                    str(params[p]) + m.group('after')
+                m = re_1.match(d)
+        return d
+
+
+def expr2name(a, block, args=[]):
+    orig_a = a
+    a_is_expr = not analyzeargs_re_1.match(a)
+    if a_is_expr:  # `a` is an expression
+        implicitrules, attrrules = buildimplicitrules(block)
+        at = determineexprtype(a, block['vars'], implicitrules)
+        na = 'e_'
+        for c in a:
+            c = c.lower()
+            if c not in string.ascii_lowercase + string.digits:
+                c = '_'
+            na = na + c
+        if na[-1] == '_':
+            na = na + 'e'
+        else:
+            na = na + '_e'
+        a = na
+        while a in block['vars'] or a in block['args']:
+            a = a + 'r'
+    if a in args:
+        k = 1
+        while a + str(k) in args:
+            k = k + 1
+        a = a + str(k)
+    if a_is_expr:
+        block['vars'][a] = at
+    else:
+        if a not in block['vars']:
+            if orig_a in block['vars']:
+                block['vars'][a] = block['vars'][orig_a]
+            else:
+                block['vars'][a] = {}
+        if 'externals' in block and orig_a in block['externals'] + block['interfaced']:
+            block['vars'][a] = setattrspec(block['vars'][a], 'external')
+    return a
+
+
+def analyzeargs(block):
+    setmesstext(block)
+    implicitrules, _ = buildimplicitrules(block)
+    if 'args' not in block:
+        block['args'] = []
+    args = []
+    for a in block['args']:
+        a = expr2name(a, block, args)
+        args.append(a)
+    block['args'] = args
+    if 'entry' in block:
+        for k, args1 in list(block['entry'].items()):
+            for a in args1:
+                if a not in block['vars']:
+                    block['vars'][a] = {}
+
+    for b in block['body']:
+        if b['name'] in args:
+            if 'externals' not in block:
+                block['externals'] = []
+            if b['name'] not in block['externals']:
+                block['externals'].append(b['name'])
+    if 'result' in block and block['result'] not in block['vars']:
+        block['vars'][block['result']] = {}
+    return block
+
+determineexprtype_re_1 = re.compile(r'\A\(.+?,.+?\)\Z', re.I)
+determineexprtype_re_2 = re.compile(r'\A[+-]?\d+(_(?P<name>\w+)|)\Z', re.I)
+determineexprtype_re_3 = re.compile(
+    r'\A[+-]?[\d.]+[-\d+de.]*(_(?P<name>\w+)|)\Z', re.I)
+determineexprtype_re_4 = re.compile(r'\A\(.*\)\Z', re.I)
+determineexprtype_re_5 = re.compile(r'\A(?P<name>\w+)\s*\(.*?\)\s*\Z', re.I)
+
+
+def _ensure_exprdict(r):
+    if isinstance(r, int):
+        return {'typespec': 'integer'}
+    if isinstance(r, float):
+        return {'typespec': 'real'}
+    if isinstance(r, complex):
+        return {'typespec': 'complex'}
+    if isinstance(r, dict):
+        return r
+    raise AssertionError(repr(r))
+
+
+def determineexprtype(expr, vars, rules={}):
+    if expr in vars:
+        return _ensure_exprdict(vars[expr])
+    expr = expr.strip()
+    if determineexprtype_re_1.match(expr):
+        return {'typespec': 'complex'}
+    m = determineexprtype_re_2.match(expr)
+    if m:
+        if 'name' in m.groupdict() and m.group('name'):
+            outmess(
+                'determineexprtype: selected kind types not supported (%s)\n' % repr(expr))
+        return {'typespec': 'integer'}
+    m = determineexprtype_re_3.match(expr)
+    if m:
+        if 'name' in m.groupdict() and m.group('name'):
+            outmess(
+                'determineexprtype: selected kind types not supported (%s)\n' % repr(expr))
+        return {'typespec': 'real'}
+    for op in ['+', '-', '*', '/']:
+        for e in [x.strip() for x in markoutercomma(expr, comma=op).split('@' + op + '@')]:
+            if e in vars:
+                return _ensure_exprdict(vars[e])
+    t = {}
+    if determineexprtype_re_4.match(expr):  # in parenthesis
+        t = determineexprtype(expr[1:-1], vars, rules)
+    else:
+        m = determineexprtype_re_5.match(expr)
+        if m:
+            rn = m.group('name')
+            t = determineexprtype(m.group('name'), vars, rules)
+            if t and 'attrspec' in t:
+                del t['attrspec']
+            if not t:
+                if rn[0] in rules:
+                    return _ensure_exprdict(rules[rn[0]])
+    if expr[0] in '\'"':
+        return {'typespec': 'character', 'charselector': {'*': '*'}}
+    if not t:
+        outmess(
+            'determineexprtype: could not determine expressions (%s) type.\n' % (repr(expr)))
+    return t
+
+######
+
+
+def crack2fortrangen(block, tab='\n', as_interface=False):
+    global skipfuncs, onlyfuncs
+
+    setmesstext(block)
+    ret = ''
+    if isinstance(block, list):
+        for g in block:
+            if g and g['block'] in ['function', 'subroutine']:
+                if g['name'] in skipfuncs:
+                    continue
+                if onlyfuncs and g['name'] not in onlyfuncs:
+                    continue
+            ret = ret + crack2fortrangen(g, tab, as_interface=as_interface)
+        return ret
+    prefix = ''
+    name = ''
+    args = ''
+    blocktype = block['block']
+    if blocktype == 'program':
+        return ''
+    argsl = []
+    if 'name' in block:
+        name = block['name']
+    if 'args' in block:
+        vars = block['vars']
+        for a in block['args']:
+            a = expr2name(a, block, argsl)
+            if not isintent_callback(vars[a]):
+                argsl.append(a)
+        if block['block'] == 'function' or argsl:
+            args = '(%s)' % ','.join(argsl)
+    f2pyenhancements = ''
+    if 'f2pyenhancements' in block:
+        for k in list(block['f2pyenhancements'].keys()):
+            f2pyenhancements = '%s%s%s %s' % (
+                f2pyenhancements, tab + tabchar, k, block['f2pyenhancements'][k])
+    intent_lst = block.get('intent', [])[:]
+    if blocktype == 'function' and 'callback' in intent_lst:
+        intent_lst.remove('callback')
+    if intent_lst:
+        f2pyenhancements = '%s%sintent(%s) %s' %\
+                           (f2pyenhancements, tab + tabchar,
+                            ','.join(intent_lst), name)
+    use = ''
+    if 'use' in block:
+        use = use2fortran(block['use'], tab + tabchar)
+    common = ''
+    if 'common' in block:
+        common = common2fortran(block['common'], tab + tabchar)
+    if name == 'unknown_interface':
+        name = ''
+    result = ''
+    if 'result' in block:
+        result = ' result (%s)' % block['result']
+        if block['result'] not in argsl:
+            argsl.append(block['result'])
+    body = crack2fortrangen(block['body'], tab + tabchar, as_interface=as_interface)
+    vars = vars2fortran(
+        block, block['vars'], argsl, tab + tabchar, as_interface=as_interface)
+    mess = ''
+    if 'from' in block and not as_interface:
+        mess = '! in %s' % block['from']
+    if 'entry' in block:
+        entry_stmts = ''
+        for k, i in list(block['entry'].items()):
+            entry_stmts = '%s%sentry %s(%s)' \
+                          % (entry_stmts, tab + tabchar, k, ','.join(i))
+        body = body + entry_stmts
+    if blocktype == 'block data' and name == '_BLOCK_DATA_':
+        name = ''
+    ret = '%s%s%s %s%s%s %s%s%s%s%s%s%send %s %s' % (
+        tab, prefix, blocktype, name, args, result, mess, f2pyenhancements, use, vars, common, body, tab, blocktype, name)
+    return ret
+
+
+def common2fortran(common, tab=''):
+    ret = ''
+    for k in list(common.keys()):
+        if k == '_BLNK_':
+            ret = '%s%scommon %s' % (ret, tab, ','.join(common[k]))
+        else:
+            ret = '%s%scommon /%s/ %s' % (ret, tab, k, ','.join(common[k]))
+    return ret
+
+
+def use2fortran(use, tab=''):
+    ret = ''
+    for m in list(use.keys()):
+        ret = '%s%suse %s,' % (ret, tab, m)
+        if use[m] == {}:
+            if ret and ret[-1] == ',':
+                ret = ret[:-1]
+            continue
+        if 'only' in use[m] and use[m]['only']:
+            ret = '%s only:' % (ret)
+        if 'map' in use[m] and use[m]['map']:
+            c = ' '
+            for k in list(use[m]['map'].keys()):
+                if k == use[m]['map'][k]:
+                    ret = '%s%s%s' % (ret, c, k)
+                    c = ','
+                else:
+                    ret = '%s%s%s=>%s' % (ret, c, k, use[m]['map'][k])
+                    c = ','
+        if ret and ret[-1] == ',':
+            ret = ret[:-1]
+    return ret
+
+
+def true_intent_list(var):
+    lst = var['intent']
+    ret = []
+    for intent in lst:
+        try:
+            f = globals()['isintent_%s' % intent]
+        except KeyError:
+            pass
+        else:
+            if f(var):
+                ret.append(intent)
+    return ret
+
+
+def vars2fortran(block, vars, args, tab='', as_interface=False):
+    setmesstext(block)
+    ret = ''
+    nout = []
+    for a in args:
+        if a in block['vars']:
+            nout.append(a)
+    if 'commonvars' in block:
+        for a in block['commonvars']:
+            if a in vars:
+                if a not in nout:
+                    nout.append(a)
+            else:
+                errmess(
+                    'vars2fortran: Confused?!: "%s" is not defined in vars.\n' % a)
+    if 'varnames' in block:
+        nout.extend(block['varnames'])
+    if not as_interface:
+        for a in list(vars.keys()):
+            if a not in nout:
+                nout.append(a)
+    for a in nout:
+        if 'depend' in vars[a]:
+            for d in vars[a]['depend']:
+                if d in vars and 'depend' in vars[d] and a in vars[d]['depend']:
+                    errmess(
+                        'vars2fortran: Warning: cross-dependence between variables "%s" and "%s"\n' % (a, d))
+        if 'externals' in block and a in block['externals']:
+            if isintent_callback(vars[a]):
+                ret = '%s%sintent(callback) %s' % (ret, tab, a)
+            ret = '%s%sexternal %s' % (ret, tab, a)
+            if isoptional(vars[a]):
+                ret = '%s%soptional %s' % (ret, tab, a)
+            if a in vars and 'typespec' not in vars[a]:
+                continue
+            cont = 1
+            for b in block['body']:
+                if a == b['name'] and b['block'] == 'function':
+                    cont = 0
+                    break
+            if cont:
+                continue
+        if a not in vars:
+            show(vars)
+            outmess('vars2fortran: No definition for argument "%s".\n' % a)
+            continue
+        if a == block['name']:
+            if block['block'] != 'function' or block.get('result'):
+                # 1) skip declaring a variable that name matches with
+                #    subroutine name
+                # 2) skip declaring function when its type is
+                #    declared via `result` construction
+                continue
+        if 'typespec' not in vars[a]:
+            if 'attrspec' in vars[a] and 'external' in vars[a]['attrspec']:
+                if a in args:
+                    ret = '%s%sexternal %s' % (ret, tab, a)
+                continue
+            show(vars[a])
+            outmess('vars2fortran: No typespec for argument "%s".\n' % a)
+            continue
+        vardef = vars[a]['typespec']
+        if vardef == 'type' and 'typename' in vars[a]:
+            vardef = '%s(%s)' % (vardef, vars[a]['typename'])
+        selector = {}
+        if 'kindselector' in vars[a]:
+            selector = vars[a]['kindselector']
+        elif 'charselector' in vars[a]:
+            selector = vars[a]['charselector']
+        if '*' in selector:
+            if selector['*'] in ['*', ':']:
+                vardef = '%s*(%s)' % (vardef, selector['*'])
+            else:
+                vardef = '%s*%s' % (vardef, selector['*'])
+        else:
+            if 'len' in selector:
+                vardef = '%s(len=%s' % (vardef, selector['len'])
+                if 'kind' in selector:
+                    vardef = '%s,kind=%s)' % (vardef, selector['kind'])
+                else:
+                    vardef = '%s)' % (vardef)
+            elif 'kind' in selector:
+                vardef = '%s(kind=%s)' % (vardef, selector['kind'])
+        c = ' '
+        if 'attrspec' in vars[a]:
+            attr = [l for l in vars[a]['attrspec']
+                    if l not in ['external']]
+            if as_interface and 'intent(in)' in attr and 'intent(out)' in attr:
+                # In Fortran, intent(in, out) are conflicting while
+                # intent(in, out) can be specified only via
+                # `!f2py intent(out) ..`.
+                # So, for the Fortran interface, we'll drop
+                # intent(out) to resolve the conflict.
+                attr.remove('intent(out)')
+            if attr:
+                vardef = '%s, %s' % (vardef, ','.join(attr))
+                c = ','
+        if 'dimension' in vars[a]:
+            vardef = '%s%sdimension(%s)' % (
+                vardef, c, ','.join(vars[a]['dimension']))
+            c = ','
+        if 'intent' in vars[a]:
+            lst = true_intent_list(vars[a])
+            if lst:
+                vardef = '%s%sintent(%s)' % (vardef, c, ','.join(lst))
+            c = ','
+        if 'check' in vars[a]:
+            vardef = '%s%scheck(%s)' % (vardef, c, ','.join(vars[a]['check']))
+            c = ','
+        if 'depend' in vars[a]:
+            vardef = '%s%sdepend(%s)' % (
+                vardef, c, ','.join(vars[a]['depend']))
+            c = ','
+        if '=' in vars[a]:
+            v = vars[a]['=']
+            if vars[a]['typespec'] in ['complex', 'double complex']:
+                try:
+                    v = eval(v)
+                    v = '(%s,%s)' % (v.real, v.imag)
+                except Exception:
+                    pass
+            vardef = '%s :: %s=%s' % (vardef, a, v)
+        else:
+            vardef = '%s :: %s' % (vardef, a)
+        ret = '%s%s%s' % (ret, tab, vardef)
+    return ret
+######
+
+
+# We expose post_processing_hooks as global variable so that
+# user-libraries could register their own hooks to f2py.
+post_processing_hooks = []
+
+
+def crackfortran(files):
+    global usermodules, post_processing_hooks
+
+    outmess('Reading fortran codes...\n', 0)
+    readfortrancode(files, crackline)
+    outmess('Post-processing...\n', 0)
+    usermodules = []
+    postlist = postcrack(grouplist[0])
+    outmess('Applying post-processing hooks...\n', 0)
+    for hook in post_processing_hooks:
+        outmess(f'  {hook.__name__}\n', 0)
+        postlist = traverse(postlist, hook)
+    outmess('Post-processing (stage 2)...\n', 0)
+    postlist = postcrack2(postlist)
+    return usermodules + postlist
+
+
+def crack2fortran(block):
+    global f2py_version
+
+    pyf = crack2fortrangen(block) + '\n'
+    header = """!    -*- f90 -*-
+! Note: the context of this file is case sensitive.
+"""
+    footer = """
+! This file was auto-generated with f2py (version:%s).
+! See:
+! https://web.archive.org/web/20140822061353/http://cens.ioc.ee/projects/f2py2e
+""" % (f2py_version)
+    return header + pyf + footer
+
+
+def _is_visit_pair(obj):
+    return (isinstance(obj, tuple)
+            and len(obj) == 2
+            and isinstance(obj[0], (int, str)))
+
+
+def traverse(obj, visit, parents=[], result=None, *args, **kwargs):
+    '''Traverse f2py data structure with the following visit function:
+
+    def visit(item, parents, result, *args, **kwargs):
+        """
+
+        parents is a list of key-"f2py data structure" pairs from which
+        items are taken from.
+
+        result is a f2py data structure that is filled with the
+        return value of the visit function.
+
+        item is 2-tuple (index, value) if parents[-1][1] is a list
+        item is 2-tuple (key, value) if parents[-1][1] is a dict
+
+        The return value of visit must be None, or of the same kind as
+        item, that is, if parents[-1] is a list, the return value must
+        be 2-tuple (new_index, new_value), or if parents[-1] is a
+        dict, the return value must be 2-tuple (new_key, new_value).
+
+        If new_index or new_value is None, the return value of visit
+        is ignored, that is, it will not be added to the result.
+
+        If the return value is None, the content of obj will be
+        traversed, otherwise not.
+        """
+    '''
+
+    if _is_visit_pair(obj):
+        if obj[0] == 'parent_block':
+            # avoid infinite recursion
+            return obj
+        new_result = visit(obj, parents, result, *args, **kwargs)
+        if new_result is not None:
+            assert _is_visit_pair(new_result)
+            return new_result
+        parent = obj
+        result_key, obj = obj
+    else:
+        parent = (None, obj)
+        result_key = None
+
+    if isinstance(obj, list):
+        new_result = []
+        for index, value in enumerate(obj):
+            new_index, new_item = traverse((index, value), visit,
+                                           parents=parents + [parent],
+                                           result=result, *args, **kwargs)
+            if new_index is not None:
+                new_result.append(new_item)
+    elif isinstance(obj, dict):
+        new_result = dict()
+        for key, value in obj.items():
+            new_key, new_value = traverse((key, value), visit,
+                                          parents=parents + [parent],
+                                          result=result, *args, **kwargs)
+            if new_key is not None:
+                new_result[new_key] = new_value
+    else:
+        new_result = obj
+
+    if result_key is None:
+        return new_result
+    return result_key, new_result
+
+
+def character_backward_compatibility_hook(item, parents, result,
+                                          *args, **kwargs):
+    """Previously, Fortran character was incorrectly treated as
+    character*1. This hook fixes the usage of the corresponding
+    variables in `check`, `dimension`, `=`, and `callstatement`
+    expressions.
+
+    The usage of `char*` in `callprotoargument` expression can be left
+    unchanged because C `character` is C typedef of `char`, although,
+    new implementations should use `character*` in the corresponding
+    expressions.
+
+    See https://github.com/numpy/numpy/pull/19388 for more information.
+
+    """
+    parent_key, parent_value = parents[-1]
+    key, value = item
+
+    def fix_usage(varname, value):
+        value = re.sub(r'[*]\s*\b' + varname + r'\b', varname, value)
+        value = re.sub(r'\b' + varname + r'\b\s*[\[]\s*0\s*[\]]',
+                       varname, value)
+        return value
+
+    if parent_key in ['dimension', 'check']:
+        assert parents[-3][0] == 'vars'
+        vars_dict = parents[-3][1]
+    elif key == '=':
+        assert parents[-2][0] == 'vars'
+        vars_dict = parents[-2][1]
+    else:
+        vars_dict = None
+
+    new_value = None
+    if vars_dict is not None:
+        new_value = value
+        for varname, vd in vars_dict.items():
+            if ischaracter(vd):
+                new_value = fix_usage(varname, new_value)
+    elif key == 'callstatement':
+        vars_dict = parents[-2][1]['vars']
+        new_value = value
+        for varname, vd in vars_dict.items():
+            if ischaracter(vd):
+                # replace all occurrences of `<varname>` with
+                # `&<varname>` in argument passing
+                new_value = re.sub(
+                    r'(?<![&])\b' + varname + r'\b', '&' + varname, new_value)
+
+    if new_value is not None:
+        if new_value != value:
+            # We report the replacements here so that downstream
+            # software could update their source codes
+            # accordingly. However, such updates are recommended only
+            # when BC with numpy 1.21 or older is not required.
+            outmess(f'character_bc_hook[{parent_key}.{key}]:'
+                    f' replaced `{value}` -> `{new_value}`\n', 1)
+        return (key, new_value)
+
+
+post_processing_hooks.append(character_backward_compatibility_hook)
+
+
+if __name__ == "__main__":
+    files = []
+    funcs = []
+    f = 1
+    f2 = 0
+    f3 = 0
+    showblocklist = 0
+    for l in sys.argv[1:]:
+        if l == '':
+            pass
+        elif l[0] == ':':
+            f = 0
+        elif l == '-quiet':
+            quiet = 1
+            verbose = 0
+        elif l == '-verbose':
+            verbose = 2
+            quiet = 0
+        elif l == '-fix':
+            if strictf77:
+                outmess(
+                    'Use option -f90 before -fix if Fortran 90 code is in fix form.\n', 0)
+            skipemptyends = 1
+            sourcecodeform = 'fix'
+        elif l == '-skipemptyends':
+            skipemptyends = 1
+        elif l == '--ignore-contains':
+            ignorecontains = 1
+        elif l == '-f77':
+            strictf77 = 1
+            sourcecodeform = 'fix'
+        elif l == '-f90':
+            strictf77 = 0
+            sourcecodeform = 'free'
+            skipemptyends = 1
+        elif l == '-h':
+            f2 = 1
+        elif l == '-show':
+            showblocklist = 1
+        elif l == '-m':
+            f3 = 1
+        elif l[0] == '-':
+            errmess('Unknown option %s\n' % repr(l))
+        elif f2:
+            f2 = 0
+            pyffilename = l
+        elif f3:
+            f3 = 0
+            f77modulename = l
+        elif f:
+            try:
+                open(l).close()
+                files.append(l)
+            except OSError as detail:
+                errmess(f'OSError: {detail!s}\n')
+        else:
+            funcs.append(l)
+    if not strictf77 and f77modulename and not skipemptyends:
+        outmess("""\
+  Warning: You have specified module name for non Fortran 77 code that
+  should not need one (expect if you are scanning F90 code for non
+  module blocks but then you should use flag -skipemptyends and also
+  be sure that the files do not contain programs without program
+  statement).
+""", 0)
+
+    postlist = crackfortran(files)
+    if pyffilename:
+        outmess('Writing fortran code to file %s\n' % repr(pyffilename), 0)
+        pyf = crack2fortran(postlist)
+        with open(pyffilename, 'w') as f:
+            f.write(pyf)
+    if showblocklist:
+        show(postlist)
diff --git a/.venv/lib/python3.12/site-packages/numpy/f2py/diagnose.py b/.venv/lib/python3.12/site-packages/numpy/f2py/diagnose.py
new file mode 100644
index 00000000..86d7004a
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/numpy/f2py/diagnose.py
@@ -0,0 +1,154 @@
+#!/usr/bin/env python3
+import os
+import sys
+import tempfile
+
+
+def run_command(cmd):
+    print('Running %r:' % (cmd))
+    os.system(cmd)
+    print('------')
+
+
+def run():
+    _path = os.getcwd()
+    os.chdir(tempfile.gettempdir())
+    print('------')
+    print('os.name=%r' % (os.name))
+    print('------')
+    print('sys.platform=%r' % (sys.platform))
+    print('------')
+    print('sys.version:')
+    print(sys.version)
+    print('------')
+    print('sys.prefix:')
+    print(sys.prefix)
+    print('------')
+    print('sys.path=%r' % (':'.join(sys.path)))
+    print('------')
+
+    try:
+        import numpy
+        has_newnumpy = 1
+    except ImportError as e:
+        print('Failed to import new numpy:', e)
+        has_newnumpy = 0
+
+    try:
+        from numpy.f2py import f2py2e
+        has_f2py2e = 1
+    except ImportError as e:
+        print('Failed to import f2py2e:', e)
+        has_f2py2e = 0
+
+    try:
+        import numpy.distutils
+        has_numpy_distutils = 2
+    except ImportError:
+        try:
+            import numpy_distutils
+            has_numpy_distutils = 1
+        except ImportError as e:
+            print('Failed to import numpy_distutils:', e)
+            has_numpy_distutils = 0
+
+    if has_newnumpy:
+        try:
+            print('Found new numpy version %r in %s' %
+                  (numpy.__version__, numpy.__file__))
+        except Exception as msg:
+            print('error:', msg)
+            print('------')
+
+    if has_f2py2e:
+        try:
+            print('Found f2py2e version %r in %s' %
+                  (f2py2e.__version__.version, f2py2e.__file__))
+        except Exception as msg:
+            print('error:', msg)
+            print('------')
+
+    if has_numpy_distutils:
+        try:
+            if has_numpy_distutils == 2:
+                print('Found numpy.distutils version %r in %r' % (
+                    numpy.distutils.__version__,
+                    numpy.distutils.__file__))
+            else:
+                print('Found numpy_distutils version %r in %r' % (
+                    numpy_distutils.numpy_distutils_version.numpy_distutils_version,
+                    numpy_distutils.__file__))
+            print('------')
+        except Exception as msg:
+            print('error:', msg)
+            print('------')
+        try:
+            if has_numpy_distutils == 1:
+                print(
+                    'Importing numpy_distutils.command.build_flib ...', end=' ')
+                import numpy_distutils.command.build_flib as build_flib
+                print('ok')
+                print('------')
+                try:
+                    print(
+                        'Checking availability of supported Fortran compilers:')
+                    for compiler_class in build_flib.all_compilers:
+                        compiler_class(verbose=1).is_available()
+                        print('------')
+                except Exception as msg:
+                    print('error:', msg)
+                    print('------')
+        except Exception as msg:
+            print(
+                'error:', msg, '(ignore it, build_flib is obsolute for numpy.distutils 0.2.2 and up)')
+            print('------')
+        try:
+            if has_numpy_distutils == 2:
+                print('Importing numpy.distutils.fcompiler ...', end=' ')
+                import numpy.distutils.fcompiler as fcompiler
+            else:
+                print('Importing numpy_distutils.fcompiler ...', end=' ')
+                import numpy_distutils.fcompiler as fcompiler
+            print('ok')
+            print('------')
+            try:
+                print('Checking availability of supported Fortran compilers:')
+                fcompiler.show_fcompilers()
+                print('------')
+            except Exception as msg:
+                print('error:', msg)
+                print('------')
+        except Exception as msg:
+            print('error:', msg)
+            print('------')
+        try:
+            if has_numpy_distutils == 2:
+                print('Importing numpy.distutils.cpuinfo ...', end=' ')
+                from numpy.distutils.cpuinfo import cpuinfo
+                print('ok')
+                print('------')
+            else:
+                try:
+                    print(
+                        'Importing numpy_distutils.command.cpuinfo ...', end=' ')
+                    from numpy_distutils.command.cpuinfo import cpuinfo
+                    print('ok')
+                    print('------')
+                except Exception as msg:
+                    print('error:', msg, '(ignore it)')
+                    print('Importing numpy_distutils.cpuinfo ...', end=' ')
+                    from numpy_distutils.cpuinfo import cpuinfo
+                    print('ok')
+                    print('------')
+            cpu = cpuinfo()
+            print('CPU information:', end=' ')
+            for name in dir(cpuinfo):
+                if name[0] == '_' and name[1] != '_' and getattr(cpu, name[1:])():
+                    print(name[1:], end=' ')
+            print('------')
+        except Exception as msg:
+            print('error:', msg)
+            print('------')
+    os.chdir(_path)
+if __name__ == "__main__":
+    run()
diff --git a/.venv/lib/python3.12/site-packages/numpy/f2py/f2py2e.py b/.venv/lib/python3.12/site-packages/numpy/f2py/f2py2e.py
new file mode 100755
index 00000000..ce22b2d8
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/numpy/f2py/f2py2e.py
@@ -0,0 +1,768 @@
+#!/usr/bin/env python3
+"""
+
+f2py2e - Fortran to Python C/API generator. 2nd Edition.
+         See __usage__ below.
+
+Copyright 1999 -- 2011 Pearu Peterson all rights reserved.
+Copyright 2011 -- present NumPy Developers.
+Permission to use, modify, and distribute this software is given under the
+terms of the NumPy License.
+
+NO WARRANTY IS EXPRESSED OR IMPLIED.  USE AT YOUR OWN RISK.
+"""
+import sys
+import os
+import pprint
+import re
+from pathlib import Path
+from itertools import dropwhile
+import argparse
+import copy
+
+from . import crackfortran
+from . import rules
+from . import cb_rules
+from . import auxfuncs
+from . import cfuncs
+from . import f90mod_rules
+from . import __version__
+from . import capi_maps
+from numpy.f2py._backends import f2py_build_generator
+
+f2py_version = __version__.version
+numpy_version = __version__.version
+errmess = sys.stderr.write
+# outmess=sys.stdout.write
+show = pprint.pprint
+outmess = auxfuncs.outmess
+MESON_ONLY_VER = (sys.version_info >= (3, 12))
+
+__usage__ =\
+f"""Usage:
+
+1) To construct extension module sources:
+
+      f2py [<options>] <fortran files> [[[only:]||[skip:]] \\
+                                        <fortran functions> ] \\
+                                       [: <fortran files> ...]
+
+2) To compile fortran files and build extension modules:
+
+      f2py -c [<options>, <build_flib options>, <extra options>] <fortran files>
+
+3) To generate signature files:
+
+      f2py -h <filename.pyf> ...< same options as in (1) >
+
+Description: This program generates a Python C/API file (<modulename>module.c)
+             that contains wrappers for given fortran functions so that they
+             can be called from Python. With the -c option the corresponding
+             extension modules are built.
+
+Options:
+
+  -h <filename>    Write signatures of the fortran routines to file <filename>
+                   and exit. You can then edit <filename> and use it instead
+                   of <fortran files>. If <filename>==stdout then the
+                   signatures are printed to stdout.
+  <fortran functions>  Names of fortran routines for which Python C/API
+                   functions will be generated. Default is all that are found
+                   in <fortran files>.
+  <fortran files>  Paths to fortran/signature files that will be scanned for
+                   <fortran functions> in order to determine their signatures.
+  skip:            Ignore fortran functions that follow until `:'.
+  only:            Use only fortran functions that follow until `:'.
+  :                Get back to <fortran files> mode.
+
+  -m <modulename>  Name of the module; f2py generates a Python/C API
+                   file <modulename>module.c or extension module <modulename>.
+                   Default is 'untitled'.
+
+  '-include<header>'  Writes additional headers in the C wrapper, can be passed
+                      multiple times, generates #include <header> each time.
+
+  --[no-]lower     Do [not] lower the cases in <fortran files>. By default,
+                   --lower is assumed with -h key, and --no-lower without -h key.
+
+  --build-dir <dirname>  All f2py generated files are created in <dirname>.
+                   Default is tempfile.mkdtemp().
+
+  --overwrite-signature  Overwrite existing signature file.
+
+  --[no-]latex-doc Create (or not) <modulename>module.tex.
+                   Default is --no-latex-doc.
+  --short-latex    Create 'incomplete' LaTeX document (without commands
+                   \\documentclass, \\tableofcontents, and \\begin{{document}},
+                   \\end{{document}}).
+
+  --[no-]rest-doc Create (or not) <modulename>module.rst.
+                   Default is --no-rest-doc.
+
+  --debug-capi     Create C/API code that reports the state of the wrappers
+                   during runtime. Useful for debugging.
+
+  --[no-]wrap-functions    Create Fortran subroutine wrappers to Fortran 77
+                   functions. --wrap-functions is default because it ensures
+                   maximum portability/compiler independence.
+
+  --include-paths <path1>:<path2>:...   Search include files from the given
+                   directories.
+
+  --help-link [..] List system resources found by system_info.py. See also
+                   --link-<resource> switch below. [..] is optional list
+                   of resources names. E.g. try 'f2py --help-link lapack_opt'.
+
+  --f2cmap <filename>  Load Fortran-to-Python KIND specification from the given
+                   file. Default: .f2py_f2cmap in current directory.
+
+  --quiet          Run quietly.
+  --verbose        Run with extra verbosity.
+  --skip-empty-wrappers   Only generate wrapper files when needed.
+  -v               Print f2py version ID and exit.
+
+
+build backend options (only effective with -c)
+[NO_MESON] is used to indicate an option not meant to be used
+with the meson backend or above Python 3.12:
+
+  --fcompiler=         Specify Fortran compiler type by vendor [NO_MESON]
+  --compiler=          Specify distutils C compiler type [NO_MESON]
+
+  --help-fcompiler     List available Fortran compilers and exit [NO_MESON]
+  --f77exec=           Specify the path to F77 compiler [NO_MESON]
+  --f90exec=           Specify the path to F90 compiler [NO_MESON]
+  --f77flags=          Specify F77 compiler flags
+  --f90flags=          Specify F90 compiler flags
+  --opt=               Specify optimization flags [NO_MESON]
+  --arch=              Specify architecture specific optimization flags [NO_MESON]
+  --noopt              Compile without optimization [NO_MESON]
+  --noarch             Compile without arch-dependent optimization [NO_MESON]
+  --debug              Compile with debugging information
+
+  --dep                <dependency>
+                       Specify a meson dependency for the module. This may
+                       be passed multiple times for multiple dependencies.
+                       Dependencies are stored in a list for further processing.
+
+                       Example: --dep lapack --dep scalapack
+                       This will identify "lapack" and "scalapack" as dependencies
+                       and remove them from argv, leaving a dependencies list
+                       containing ["lapack", "scalapack"].
+
+  --backend            <backend_type>
+                       Specify the build backend for the compilation process.
+                       The supported backends are 'meson' and 'distutils'.
+                       If not specified, defaults to 'distutils'. On
+                       Python 3.12 or higher, the default is 'meson'.
+
+Extra options (only effective with -c):
+
+  --link-<resource>    Link extension module with <resource> as defined
+                       by numpy.distutils/system_info.py. E.g. to link
+                       with optimized LAPACK libraries (vecLib on MacOSX,
+                       ATLAS elsewhere), use --link-lapack_opt.
+                       See also --help-link switch. [NO_MESON]
+
+  -L/path/to/lib/ -l<libname>
+  -D<define> -U<name>
+  -I/path/to/include/
+  <filename>.o <filename>.so <filename>.a
+
+  Using the following macros may be required with non-gcc Fortran
+  compilers:
+    -DPREPEND_FORTRAN -DNO_APPEND_FORTRAN -DUPPERCASE_FORTRAN
+    -DUNDERSCORE_G77
+
+  When using -DF2PY_REPORT_ATEXIT, a performance report of F2PY
+  interface is printed out at exit (platforms: Linux).
+
+  When using -DF2PY_REPORT_ON_ARRAY_COPY=<int>, a message is
+  sent to stderr whenever F2PY interface makes a copy of an
+  array. Integer <int> sets the threshold for array sizes when
+  a message should be shown.
+
+Version:     {f2py_version}
+numpy Version: {numpy_version}
+License:     NumPy license (see LICENSE.txt in the NumPy source code)
+Copyright 1999 -- 2011 Pearu Peterson all rights reserved.
+Copyright 2011 -- present NumPy Developers.
+https://numpy.org/doc/stable/f2py/index.html\n"""
+
+
+def scaninputline(inputline):
+    files, skipfuncs, onlyfuncs, debug = [], [], [], []
+    f, f2, f3, f5, f6, f8, f9, f10 = 1, 0, 0, 0, 0, 0, 0, 0
+    verbose = 1
+    emptygen = True
+    dolc = -1
+    dolatexdoc = 0
+    dorestdoc = 0
+    wrapfuncs = 1
+    buildpath = '.'
+    include_paths, inputline = get_includes(inputline)
+    signsfile, modulename = None, None
+    options = {'buildpath': buildpath,
+               'coutput': None,
+               'f2py_wrapper_output': None}
+    for l in inputline:
+        if l == '':
+            pass
+        elif l == 'only:':
+            f = 0
+        elif l == 'skip:':
+            f = -1
+        elif l == ':':
+            f = 1
+        elif l[:8] == '--debug-':
+            debug.append(l[8:])
+        elif l == '--lower':
+            dolc = 1
+        elif l == '--build-dir':
+            f6 = 1
+        elif l == '--no-lower':
+            dolc = 0
+        elif l == '--quiet':
+            verbose = 0
+        elif l == '--verbose':
+            verbose += 1
+        elif l == '--latex-doc':
+            dolatexdoc = 1
+        elif l == '--no-latex-doc':
+            dolatexdoc = 0
+        elif l == '--rest-doc':
+            dorestdoc = 1
+        elif l == '--no-rest-doc':
+            dorestdoc = 0
+        elif l == '--wrap-functions':
+            wrapfuncs = 1
+        elif l == '--no-wrap-functions':
+            wrapfuncs = 0
+        elif l == '--short-latex':
+            options['shortlatex'] = 1
+        elif l == '--coutput':
+            f8 = 1
+        elif l == '--f2py-wrapper-output':
+            f9 = 1
+        elif l == '--f2cmap':
+            f10 = 1
+        elif l == '--overwrite-signature':
+            options['h-overwrite'] = 1
+        elif l == '-h':
+            f2 = 1
+        elif l == '-m':
+            f3 = 1
+        elif l[:2] == '-v':
+            print(f2py_version)
+            sys.exit()
+        elif l == '--show-compilers':
+            f5 = 1
+        elif l[:8] == '-include':
+            cfuncs.outneeds['userincludes'].append(l[9:-1])
+            cfuncs.userincludes[l[9:-1]] = '#include ' + l[8:]
+        elif l == '--skip-empty-wrappers':
+            emptygen = False
+        elif l[0] == '-':
+            errmess('Unknown option %s\n' % repr(l))
+            sys.exit()
+        elif f2:
+            f2 = 0
+            signsfile = l
+        elif f3:
+            f3 = 0
+            modulename = l
+        elif f6:
+            f6 = 0
+            buildpath = l
+        elif f8:
+            f8 = 0
+            options["coutput"] = l
+        elif f9:
+            f9 = 0
+            options["f2py_wrapper_output"] = l
+        elif f10:
+            f10 = 0
+            options["f2cmap_file"] = l
+        elif f == 1:
+            try:
+                with open(l):
+                    pass
+                files.append(l)
+            except OSError as detail:
+                errmess(f'OSError: {detail!s}. Skipping file "{l!s}".\n')
+        elif f == -1:
+            skipfuncs.append(l)
+        elif f == 0:
+            onlyfuncs.append(l)
+    if not f5 and not files and not modulename:
+        print(__usage__)
+        sys.exit()
+    if not os.path.isdir(buildpath):
+        if not verbose:
+            outmess('Creating build directory %s\n' % (buildpath))
+        os.mkdir(buildpath)
+    if signsfile:
+        signsfile = os.path.join(buildpath, signsfile)
+    if signsfile and os.path.isfile(signsfile) and 'h-overwrite' not in options:
+        errmess(
+            'Signature file "%s" exists!!! Use --overwrite-signature to overwrite.\n' % (signsfile))
+        sys.exit()
+
+    options['emptygen'] = emptygen
+    options['debug'] = debug
+    options['verbose'] = verbose
+    if dolc == -1 and not signsfile:
+        options['do-lower'] = 0
+    else:
+        options['do-lower'] = dolc
+    if modulename:
+        options['module'] = modulename
+    if signsfile:
+        options['signsfile'] = signsfile
+    if onlyfuncs:
+        options['onlyfuncs'] = onlyfuncs
+    if skipfuncs:
+        options['skipfuncs'] = skipfuncs
+    options['dolatexdoc'] = dolatexdoc
+    options['dorestdoc'] = dorestdoc
+    options['wrapfuncs'] = wrapfuncs
+    options['buildpath'] = buildpath
+    options['include_paths'] = include_paths
+    options.setdefault('f2cmap_file', None)
+    return files, options
+
+
+def callcrackfortran(files, options):
+    rules.options = options
+    crackfortran.debug = options['debug']
+    crackfortran.verbose = options['verbose']
+    if 'module' in options:
+        crackfortran.f77modulename = options['module']
+    if 'skipfuncs' in options:
+        crackfortran.skipfuncs = options['skipfuncs']
+    if 'onlyfuncs' in options:
+        crackfortran.onlyfuncs = options['onlyfuncs']
+    crackfortran.include_paths[:] = options['include_paths']
+    crackfortran.dolowercase = options['do-lower']
+    postlist = crackfortran.crackfortran(files)
+    if 'signsfile' in options:
+        outmess('Saving signatures to file "%s"\n' % (options['signsfile']))
+        pyf = crackfortran.crack2fortran(postlist)
+        if options['signsfile'][-6:] == 'stdout':
+            sys.stdout.write(pyf)
+        else:
+            with open(options['signsfile'], 'w') as f:
+                f.write(pyf)
+    if options["coutput"] is None:
+        for mod in postlist:
+            mod["coutput"] = "%smodule.c" % mod["name"]
+    else:
+        for mod in postlist:
+            mod["coutput"] = options["coutput"]
+    if options["f2py_wrapper_output"] is None:
+        for mod in postlist:
+            mod["f2py_wrapper_output"] = "%s-f2pywrappers.f" % mod["name"]
+    else:
+        for mod in postlist:
+            mod["f2py_wrapper_output"] = options["f2py_wrapper_output"]
+    return postlist
+
+
+def buildmodules(lst):
+    cfuncs.buildcfuncs()
+    outmess('Building modules...\n')
+    modules, mnames, isusedby = [], [], {}
+    for item in lst:
+        if '__user__' in item['name']:
+            cb_rules.buildcallbacks(item)
+        else:
+            if 'use' in item:
+                for u in item['use'].keys():
+                    if u not in isusedby:
+                        isusedby[u] = []
+                    isusedby[u].append(item['name'])
+            modules.append(item)
+            mnames.append(item['name'])
+    ret = {}
+    for module, name in zip(modules, mnames):
+        if name in isusedby:
+            outmess('\tSkipping module "%s" which is used by %s.\n' % (
+                name, ','.join('"%s"' % s for s in isusedby[name])))
+        else:
+            um = []
+            if 'use' in module:
+                for u in module['use'].keys():
+                    if u in isusedby and u in mnames:
+                        um.append(modules[mnames.index(u)])
+                    else:
+                        outmess(
+                            f'\tModule "{name}" uses nonexisting "{u}" '
+                            'which will be ignored.\n')
+            ret[name] = {}
+            dict_append(ret[name], rules.buildmodule(module, um))
+    return ret
+
+
+def dict_append(d_out, d_in):
+    for (k, v) in d_in.items():
+        if k not in d_out:
+            d_out[k] = []
+        if isinstance(v, list):
+            d_out[k] = d_out[k] + v
+        else:
+            d_out[k].append(v)
+
+
+def run_main(comline_list):
+    """
+    Equivalent to running::
+
+        f2py <args>
+
+    where ``<args>=string.join(<list>,' ')``, but in Python.  Unless
+    ``-h`` is used, this function returns a dictionary containing
+    information on generated modules and their dependencies on source
+    files.
+
+    You cannot build extension modules with this function, that is,
+    using ``-c`` is not allowed. Use the ``compile`` command instead.
+
+    Examples
+    --------
+    The command ``f2py -m scalar scalar.f`` can be executed from Python as
+    follows.
+
+    .. literalinclude:: ../../source/f2py/code/results/run_main_session.dat
+        :language: python
+
+    """
+    crackfortran.reset_global_f2py_vars()
+    f2pydir = os.path.dirname(os.path.abspath(cfuncs.__file__))
+    fobjhsrc = os.path.join(f2pydir, 'src', 'fortranobject.h')
+    fobjcsrc = os.path.join(f2pydir, 'src', 'fortranobject.c')
+    # gh-22819 -- begin
+    parser = make_f2py_compile_parser()
+    args, comline_list = parser.parse_known_args(comline_list)
+    pyf_files, _ = filter_files("", "[.]pyf([.]src|)", comline_list)
+    # Checks that no existing modulename is defined in a pyf file
+    # TODO: Remove all this when scaninputline is replaced
+    if args.module_name:
+        if "-h" in comline_list:
+            modname = (
+                args.module_name
+            )  # Directly use from args when -h is present
+        else:
+            modname = validate_modulename(
+                pyf_files, args.module_name
+            )  # Validate modname when -h is not present
+        comline_list += ['-m', modname]  # needed for the rest of scaninputline
+    # gh-22819 -- end
+    files, options = scaninputline(comline_list)
+    auxfuncs.options = options
+    capi_maps.load_f2cmap_file(options['f2cmap_file'])
+    postlist = callcrackfortran(files, options)
+    isusedby = {}
+    for plist in postlist:
+        if 'use' in plist:
+            for u in plist['use'].keys():
+                if u not in isusedby:
+                    isusedby[u] = []
+                isusedby[u].append(plist['name'])
+    for plist in postlist:
+        if plist['block'] == 'python module' and '__user__' in plist['name']:
+            if plist['name'] in isusedby:
+                # if not quiet:
+                outmess(
+                    f'Skipping Makefile build for module "{plist["name"]}" '
+                    'which is used by {}\n'.format(
+                        ','.join(f'"{s}"' for s in isusedby[plist['name']])))
+    if 'signsfile' in options:
+        if options['verbose'] > 1:
+            outmess(
+                'Stopping. Edit the signature file and then run f2py on the signature file: ')
+            outmess('%s %s\n' %
+                    (os.path.basename(sys.argv[0]), options['signsfile']))
+        return
+    for plist in postlist:
+        if plist['block'] != 'python module':
+            if 'python module' not in options:
+                errmess(
+                    'Tip: If your original code is Fortran source then you must use -m option.\n')
+            raise TypeError('All blocks must be python module blocks but got %s' % (
+                repr(plist['block'])))
+    auxfuncs.debugoptions = options['debug']
+    f90mod_rules.options = options
+    auxfuncs.wrapfuncs = options['wrapfuncs']
+
+    ret = buildmodules(postlist)
+
+    for mn in ret.keys():
+        dict_append(ret[mn], {'csrc': fobjcsrc, 'h': fobjhsrc})
+    return ret
+
+
+def filter_files(prefix, suffix, files, remove_prefix=None):
+    """
+    Filter files by prefix and suffix.
+    """
+    filtered, rest = [], []
+    match = re.compile(prefix + r'.*' + suffix + r'\Z').match
+    if remove_prefix:
+        ind = len(prefix)
+    else:
+        ind = 0
+    for file in [x.strip() for x in files]:
+        if match(file):
+            filtered.append(file[ind:])
+        else:
+            rest.append(file)
+    return filtered, rest
+
+
+def get_prefix(module):
+    p = os.path.dirname(os.path.dirname(module.__file__))
+    return p
+
+
+class CombineIncludePaths(argparse.Action):
+    def __call__(self, parser, namespace, values, option_string=None):
+        include_paths_set = set(getattr(namespace, 'include_paths', []) or [])
+        if option_string == "--include_paths":
+            outmess("Use --include-paths or -I instead of --include_paths which will be removed")
+        if option_string == "--include-paths" or option_string == "--include_paths":
+            include_paths_set.update(values.split(':'))
+        else:
+            include_paths_set.add(values)
+        setattr(namespace, 'include_paths', list(include_paths_set))
+
+def include_parser():
+    parser = argparse.ArgumentParser(add_help=False)
+    parser.add_argument("-I", dest="include_paths", action=CombineIncludePaths)
+    parser.add_argument("--include-paths", dest="include_paths", action=CombineIncludePaths)
+    parser.add_argument("--include_paths", dest="include_paths", action=CombineIncludePaths)
+    return parser
+
+def get_includes(iline):
+    iline = (' '.join(iline)).split()
+    parser = include_parser()
+    args, remain = parser.parse_known_args(iline)
+    ipaths = args.include_paths
+    if args.include_paths is None:
+        ipaths = []
+    return ipaths, remain
+
+def make_f2py_compile_parser():
+    parser = argparse.ArgumentParser(add_help=False)
+    parser.add_argument("--dep", action="append", dest="dependencies")
+    parser.add_argument("--backend", choices=['meson', 'distutils'], default='distutils')
+    parser.add_argument("-m", dest="module_name")
+    return parser
+
+def preparse_sysargv():
+    # To keep backwards bug compatibility, newer flags are handled by argparse,
+    # and `sys.argv` is passed to the rest of `f2py` as is.
+    parser = make_f2py_compile_parser()
+
+    args, remaining_argv = parser.parse_known_args()
+    sys.argv = [sys.argv[0]] + remaining_argv
+
+    backend_key = args.backend
+    if MESON_ONLY_VER and backend_key == 'distutils':
+        outmess("Cannot use distutils backend with Python>=3.12,"
+                " using meson backend instead.\n")
+        backend_key = "meson"
+
+    return {
+        "dependencies": args.dependencies or [],
+        "backend": backend_key,
+        "modulename": args.module_name,
+    }
+
+def run_compile():
+    """
+    Do it all in one call!
+    """
+    import tempfile
+
+    # Collect dependency flags, preprocess sys.argv
+    argy = preparse_sysargv()
+    modulename = argy["modulename"]
+    if modulename is None:
+        modulename = 'untitled'
+    dependencies = argy["dependencies"]
+    backend_key = argy["backend"]
+    build_backend = f2py_build_generator(backend_key)
+
+    i = sys.argv.index('-c')
+    del sys.argv[i]
+
+    remove_build_dir = 0
+    try:
+        i = sys.argv.index('--build-dir')
+    except ValueError:
+        i = None
+    if i is not None:
+        build_dir = sys.argv[i + 1]
+        del sys.argv[i + 1]
+        del sys.argv[i]
+    else:
+        remove_build_dir = 1
+        build_dir = tempfile.mkdtemp()
+
+    _reg1 = re.compile(r'--link-')
+    sysinfo_flags = [_m for _m in sys.argv[1:] if _reg1.match(_m)]
+    sys.argv = [_m for _m in sys.argv if _m not in sysinfo_flags]
+    if sysinfo_flags:
+        sysinfo_flags = [f[7:] for f in sysinfo_flags]
+
+    _reg2 = re.compile(
+        r'--((no-|)(wrap-functions|lower)|debug-capi|quiet|skip-empty-wrappers)|-include')
+    f2py_flags = [_m for _m in sys.argv[1:] if _reg2.match(_m)]
+    sys.argv = [_m for _m in sys.argv if _m not in f2py_flags]
+    f2py_flags2 = []
+    fl = 0
+    for a in sys.argv[1:]:
+        if a in ['only:', 'skip:']:
+            fl = 1
+        elif a == ':':
+            fl = 0
+        if fl or a == ':':
+            f2py_flags2.append(a)
+    if f2py_flags2 and f2py_flags2[-1] != ':':
+        f2py_flags2.append(':')
+    f2py_flags.extend(f2py_flags2)
+    sys.argv = [_m for _m in sys.argv if _m not in f2py_flags2]
+    _reg3 = re.compile(
+        r'--((f(90)?compiler(-exec|)|compiler)=|help-compiler)')
+    flib_flags = [_m for _m in sys.argv[1:] if _reg3.match(_m)]
+    sys.argv = [_m for _m in sys.argv if _m not in flib_flags]
+    _reg4 = re.compile(
+        r'--((f(77|90)(flags|exec)|opt|arch)=|(debug|noopt|noarch|help-fcompiler))')
+    fc_flags = [_m for _m in sys.argv[1:] if _reg4.match(_m)]
+    sys.argv = [_m for _m in sys.argv if _m not in fc_flags]
+
+    del_list = []
+    for s in flib_flags:
+        v = '--fcompiler='
+        if s[:len(v)] == v:
+            if MESON_ONLY_VER or backend_key == 'meson':
+                outmess(
+                    "--fcompiler cannot be used with meson,"
+                    "set compiler with the FC environment variable\n"
+                    )
+            else:
+                from numpy.distutils import fcompiler
+                fcompiler.load_all_fcompiler_classes()
+                allowed_keys = list(fcompiler.fcompiler_class.keys())
+                nv = ov = s[len(v):].lower()
+                if ov not in allowed_keys:
+                    vmap = {}  # XXX
+                    try:
+                        nv = vmap[ov]
+                    except KeyError:
+                        if ov not in vmap.values():
+                            print('Unknown vendor: "%s"' % (s[len(v):]))
+                    nv = ov
+                i = flib_flags.index(s)
+                flib_flags[i] = '--fcompiler=' + nv
+                continue
+    for s in del_list:
+        i = flib_flags.index(s)
+        del flib_flags[i]
+    assert len(flib_flags) <= 2, repr(flib_flags)
+
+    _reg5 = re.compile(r'--(verbose)')
+    setup_flags = [_m for _m in sys.argv[1:] if _reg5.match(_m)]
+    sys.argv = [_m for _m in sys.argv if _m not in setup_flags]
+
+    if '--quiet' in f2py_flags:
+        setup_flags.append('--quiet')
+
+    # Ugly filter to remove everything but sources
+    sources = sys.argv[1:]
+    f2cmapopt = '--f2cmap'
+    if f2cmapopt in sys.argv:
+        i = sys.argv.index(f2cmapopt)
+        f2py_flags.extend(sys.argv[i:i + 2])
+        del sys.argv[i + 1], sys.argv[i]
+        sources = sys.argv[1:]
+
+    pyf_files, _sources = filter_files("", "[.]pyf([.]src|)", sources)
+    sources = pyf_files + _sources
+    modulename = validate_modulename(pyf_files, modulename)
+    extra_objects, sources = filter_files('', '[.](o|a|so|dylib)', sources)
+    library_dirs, sources = filter_files('-L', '', sources, remove_prefix=1)
+    libraries, sources = filter_files('-l', '', sources, remove_prefix=1)
+    undef_macros, sources = filter_files('-U', '', sources, remove_prefix=1)
+    define_macros, sources = filter_files('-D', '', sources, remove_prefix=1)
+    for i in range(len(define_macros)):
+        name_value = define_macros[i].split('=', 1)
+        if len(name_value) == 1:
+            name_value.append(None)
+        if len(name_value) == 2:
+            define_macros[i] = tuple(name_value)
+        else:
+            print('Invalid use of -D:', name_value)
+
+    # Construct wrappers / signatures / things
+    if backend_key == 'meson':
+        if not pyf_files:
+            outmess('Using meson backend\nWill pass --lower to f2py\nSee https://numpy.org/doc/stable/f2py/buildtools/meson.html\n')
+            f2py_flags.append('--lower')
+            run_main(f" {' '.join(f2py_flags)} -m {modulename} {' '.join(sources)}".split())
+        else:
+            run_main(f" {' '.join(f2py_flags)} {' '.join(pyf_files)}".split())
+
+    # Order matters here, includes are needed for run_main above
+    include_dirs, sources = get_includes(sources)
+    # Now use the builder
+    builder = build_backend(
+        modulename,
+        sources,
+        extra_objects,
+        build_dir,
+        include_dirs,
+        library_dirs,
+        libraries,
+        define_macros,
+        undef_macros,
+        f2py_flags,
+        sysinfo_flags,
+        fc_flags,
+        flib_flags,
+        setup_flags,
+        remove_build_dir,
+        {"dependencies": dependencies},
+    )
+
+    builder.compile()
+
+
+def validate_modulename(pyf_files, modulename='untitled'):
+    if len(pyf_files) > 1:
+        raise ValueError("Only one .pyf file per call")
+    if pyf_files:
+        pyff = pyf_files[0]
+        pyf_modname = auxfuncs.get_f2py_modulename(pyff)
+        if modulename != pyf_modname:
+            outmess(
+                f"Ignoring -m {modulename}.\n"
+                f"{pyff} defines {pyf_modname} to be the modulename.\n"
+            )
+            modulename = pyf_modname
+    return modulename
+
+def main():
+    if '--help-link' in sys.argv[1:]:
+        sys.argv.remove('--help-link')
+        if MESON_ONLY_VER:
+            outmess("Use --dep for meson builds\n")
+        else:
+            from numpy.distutils.system_info import show_all
+            show_all()
+        return
+
+    if '-c' in sys.argv[1:]:
+        run_compile()
+    else:
+        run_main(sys.argv[1:])
diff --git a/.venv/lib/python3.12/site-packages/numpy/f2py/f90mod_rules.py b/.venv/lib/python3.12/site-packages/numpy/f2py/f90mod_rules.py
new file mode 100644
index 00000000..2f8a8dc1
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/numpy/f2py/f90mod_rules.py
@@ -0,0 +1,264 @@
+"""
+Build F90 module support for f2py2e.
+
+Copyright 1999 -- 2011 Pearu Peterson all rights reserved.
+Copyright 2011 -- present NumPy Developers.
+Permission to use, modify, and distribute this software is given under the
+terms of the NumPy License.
+
+NO WARRANTY IS EXPRESSED OR IMPLIED.  USE AT YOUR OWN RISK.
+"""
+__version__ = "$Revision: 1.27 $"[10:-1]
+
+f2py_version = 'See `f2py -v`'
+
+import numpy as np
+
+from . import capi_maps
+from . import func2subr
+from .crackfortran import undo_rmbadname, undo_rmbadname1
+
+# The environment provided by auxfuncs.py is needed for some calls to eval.
+# As the needed functions cannot be determined by static inspection of the
+# code, it is safest to use import * pending a major refactoring of f2py.
+from .auxfuncs import *
+
+options = {}
+
+
+def findf90modules(m):
+    if ismodule(m):
+        return [m]
+    if not hasbody(m):
+        return []
+    ret = []
+    for b in m['body']:
+        if ismodule(b):
+            ret.append(b)
+        else:
+            ret = ret + findf90modules(b)
+    return ret
+
+fgetdims1 = """\
+      external f2pysetdata
+      logical ns
+      integer r,i
+      integer(%d) s(*)
+      ns = .FALSE.
+      if (allocated(d)) then
+         do i=1,r
+            if ((size(d,i).ne.s(i)).and.(s(i).ge.0)) then
+               ns = .TRUE.
+            end if
+         end do
+         if (ns) then
+            deallocate(d)
+         end if
+      end if
+      if ((.not.allocated(d)).and.(s(1).ge.1)) then""" % np.intp().itemsize
+
+fgetdims2 = """\
+      end if
+      if (allocated(d)) then
+         do i=1,r
+            s(i) = size(d,i)
+         end do
+      end if
+      flag = 1
+      call f2pysetdata(d,allocated(d))"""
+
+fgetdims2_sa = """\
+      end if
+      if (allocated(d)) then
+         do i=1,r
+            s(i) = size(d,i)
+         end do
+         !s(r) must be equal to len(d(1))
+      end if
+      flag = 2
+      call f2pysetdata(d,allocated(d))"""
+
+
+def buildhooks(pymod):
+    from . import rules
+    ret = {'f90modhooks': [], 'initf90modhooks': [], 'body': [],
+           'need': ['F_FUNC', 'arrayobject.h'],
+           'separatorsfor': {'includes0': '\n', 'includes': '\n'},
+           'docs': ['"Fortran 90/95 modules:\\n"'],
+           'latexdoc': []}
+    fhooks = ['']
+
+    def fadd(line, s=fhooks):
+        s[0] = '%s\n      %s' % (s[0], line)
+    doc = ['']
+
+    def dadd(line, s=doc):
+        s[0] = '%s\n%s' % (s[0], line)
+
+    usenames = getuseblocks(pymod)
+    for m in findf90modules(pymod):
+        sargs, fargs, efargs, modobjs, notvars, onlyvars = [], [], [], [], [
+            m['name']], []
+        sargsp = []
+        ifargs = []
+        mfargs = []
+        if hasbody(m):
+            for b in m['body']:
+                notvars.append(b['name'])
+        for n in m['vars'].keys():
+            var = m['vars'][n]
+            if (n not in notvars) and (not l_or(isintent_hide, isprivate)(var)):
+                onlyvars.append(n)
+                mfargs.append(n)
+        outmess('\t\tConstructing F90 module support for "%s"...\n' %
+                (m['name']))
+        if m['name'] in usenames and not onlyvars:
+            outmess(f"\t\t\tSkipping {m['name']} since it is in 'use'...\n")
+            continue
+        if onlyvars:
+            outmess('\t\t  Variables: %s\n' % (' '.join(onlyvars)))
+        chooks = ['']
+
+        def cadd(line, s=chooks):
+            s[0] = '%s\n%s' % (s[0], line)
+        ihooks = ['']
+
+        def iadd(line, s=ihooks):
+            s[0] = '%s\n%s' % (s[0], line)
+
+        vrd = capi_maps.modsign2map(m)
+        cadd('static FortranDataDef f2py_%s_def[] = {' % (m['name']))
+        dadd('\\subsection{Fortran 90/95 module \\texttt{%s}}\n' % (m['name']))
+        if hasnote(m):
+            note = m['note']
+            if isinstance(note, list):
+                note = '\n'.join(note)
+            dadd(note)
+        if onlyvars:
+            dadd('\\begin{description}')
+        for n in onlyvars:
+            var = m['vars'][n]
+            modobjs.append(n)
+            ct = capi_maps.getctype(var)
+            at = capi_maps.c2capi_map[ct]
+            dm = capi_maps.getarrdims(n, var)
+            dms = dm['dims'].replace('*', '-1').strip()
+            dms = dms.replace(':', '-1').strip()
+            if not dms:
+                dms = '-1'
+            use_fgetdims2 = fgetdims2
+            cadd('\t{"%s",%s,{{%s}},%s, %s},' %
+                 (undo_rmbadname1(n), dm['rank'], dms, at,
+                  capi_maps.get_elsize(var)))
+            dadd('\\item[]{{}\\verb@%s@{}}' %
+                 (capi_maps.getarrdocsign(n, var)))
+            if hasnote(var):
+                note = var['note']
+                if isinstance(note, list):
+                    note = '\n'.join(note)
+                dadd('--- %s' % (note))
+            if isallocatable(var):
+                fargs.append('f2py_%s_getdims_%s' % (m['name'], n))
+                efargs.append(fargs[-1])
+                sargs.append(
+                    'void (*%s)(int*,npy_intp*,void(*)(char*,npy_intp*),int*)' % (n))
+                sargsp.append('void (*)(int*,npy_intp*,void(*)(char*,npy_intp*),int*)')
+                iadd('\tf2py_%s_def[i_f2py++].func = %s;' % (m['name'], n))
+                fadd('subroutine %s(r,s,f2pysetdata,flag)' % (fargs[-1]))
+                fadd('use %s, only: d => %s\n' %
+                     (m['name'], undo_rmbadname1(n)))
+                fadd('integer flag\n')
+                fhooks[0] = fhooks[0] + fgetdims1
+                dms = range(1, int(dm['rank']) + 1)
+                fadd(' allocate(d(%s))\n' %
+                     (','.join(['s(%s)' % i for i in dms])))
+                fhooks[0] = fhooks[0] + use_fgetdims2
+                fadd('end subroutine %s' % (fargs[-1]))
+            else:
+                fargs.append(n)
+                sargs.append('char *%s' % (n))
+                sargsp.append('char*')
+                iadd('\tf2py_%s_def[i_f2py++].data = %s;' % (m['name'], n))
+        if onlyvars:
+            dadd('\\end{description}')
+        if hasbody(m):
+            for b in m['body']:
+                if not isroutine(b):
+                    outmess("f90mod_rules.buildhooks:"
+                            f" skipping {b['block']} {b['name']}\n")
+                    continue
+                modobjs.append('%s()' % (b['name']))
+                b['modulename'] = m['name']
+                api, wrap = rules.buildapi(b)
+                if isfunction(b):
+                    fhooks[0] = fhooks[0] + wrap
+                    fargs.append('f2pywrap_%s_%s' % (m['name'], b['name']))
+                    ifargs.append(func2subr.createfuncwrapper(b, signature=1))
+                else:
+                    if wrap:
+                        fhooks[0] = fhooks[0] + wrap
+                        fargs.append('f2pywrap_%s_%s' % (m['name'], b['name']))
+                        ifargs.append(
+                            func2subr.createsubrwrapper(b, signature=1))
+                    else:
+                        fargs.append(b['name'])
+                        mfargs.append(fargs[-1])
+                api['externroutines'] = []
+                ar = applyrules(api, vrd)
+                ar['docs'] = []
+                ar['docshort'] = []
+                ret = dictappend(ret, ar)
+                cadd(('\t{"%s",-1,{{-1}},0,0,NULL,(void *)'
+                      'f2py_rout_#modulename#_%s_%s,'
+                      'doc_f2py_rout_#modulename#_%s_%s},')
+                     % (b['name'], m['name'], b['name'], m['name'], b['name']))
+                sargs.append('char *%s' % (b['name']))
+                sargsp.append('char *')
+                iadd('\tf2py_%s_def[i_f2py++].data = %s;' %
+                     (m['name'], b['name']))
+        cadd('\t{NULL}\n};\n')
+        iadd('}')
+        ihooks[0] = 'static void f2py_setup_%s(%s) {\n\tint i_f2py=0;%s' % (
+            m['name'], ','.join(sargs), ihooks[0])
+        if '_' in m['name']:
+            F_FUNC = 'F_FUNC_US'
+        else:
+            F_FUNC = 'F_FUNC'
+        iadd('extern void %s(f2pyinit%s,F2PYINIT%s)(void (*)(%s));'
+             % (F_FUNC, m['name'], m['name'].upper(), ','.join(sargsp)))
+        iadd('static void f2py_init_%s(void) {' % (m['name']))
+        iadd('\t%s(f2pyinit%s,F2PYINIT%s)(f2py_setup_%s);'
+             % (F_FUNC, m['name'], m['name'].upper(), m['name']))
+        iadd('}\n')
+        ret['f90modhooks'] = ret['f90modhooks'] + chooks + ihooks
+        ret['initf90modhooks'] = ['\tPyDict_SetItemString(d, "%s", PyFortranObject_New(f2py_%s_def,f2py_init_%s));' % (
+            m['name'], m['name'], m['name'])] + ret['initf90modhooks']
+        fadd('')
+        fadd('subroutine f2pyinit%s(f2pysetupfunc)' % (m['name']))
+        if mfargs:
+            for a in undo_rmbadname(mfargs):
+                fadd('use %s, only : %s' % (m['name'], a))
+        if ifargs:
+            fadd(' '.join(['interface'] + ifargs))
+            fadd('end interface')
+        fadd('external f2pysetupfunc')
+        if efargs:
+            for a in undo_rmbadname(efargs):
+                fadd('external %s' % (a))
+        fadd('call f2pysetupfunc(%s)' % (','.join(undo_rmbadname(fargs))))
+        fadd('end subroutine f2pyinit%s\n' % (m['name']))
+
+        dadd('\n'.join(ret['latexdoc']).replace(
+            r'\subsection{', r'\subsubsection{'))
+
+        ret['latexdoc'] = []
+        ret['docs'].append('"\t%s --- %s"' % (m['name'],
+                                              ','.join(undo_rmbadname(modobjs))))
+
+    ret['routine_defs'] = ''
+    ret['doc'] = []
+    ret['docshort'] = []
+    ret['latexdoc'] = doc[0]
+    if len(ret['docs']) <= 1:
+        ret['docs'] = ''
+    return ret, fhooks[0]
diff --git a/.venv/lib/python3.12/site-packages/numpy/f2py/func2subr.py b/.venv/lib/python3.12/site-packages/numpy/f2py/func2subr.py
new file mode 100644
index 00000000..b9aa9fc0
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/numpy/f2py/func2subr.py
@@ -0,0 +1,323 @@
+"""
+
+Rules for building C/API module with f2py2e.
+
+Copyright 1999 -- 2011 Pearu Peterson all rights reserved.
+Copyright 2011 -- present NumPy Developers.
+Permission to use, modify, and distribute this software is given under the
+terms of the NumPy License.
+
+NO WARRANTY IS EXPRESSED OR IMPLIED.  USE AT YOUR OWN RISK.
+"""
+import copy
+
+from .auxfuncs import (
+    getfortranname, isexternal, isfunction, isfunction_wrap, isintent_in,
+    isintent_out, islogicalfunction, ismoduleroutine, isscalar,
+    issubroutine, issubroutine_wrap, outmess, show
+)
+
+from ._isocbind import isoc_kindmap
+
+def var2fixfortran(vars, a, fa=None, f90mode=None):
+    if fa is None:
+        fa = a
+    if a not in vars:
+        show(vars)
+        outmess('var2fixfortran: No definition for argument "%s".\n' % a)
+        return ''
+    if 'typespec' not in vars[a]:
+        show(vars[a])
+        outmess('var2fixfortran: No typespec for argument "%s".\n' % a)
+        return ''
+    vardef = vars[a]['typespec']
+    if vardef == 'type' and 'typename' in vars[a]:
+        vardef = '%s(%s)' % (vardef, vars[a]['typename'])
+    selector = {}
+    lk = ''
+    if 'kindselector' in vars[a]:
+        selector = vars[a]['kindselector']
+        lk = 'kind'
+    elif 'charselector' in vars[a]:
+        selector = vars[a]['charselector']
+        lk = 'len'
+    if '*' in selector:
+        if f90mode:
+            if selector['*'] in ['*', ':', '(*)']:
+                vardef = '%s(len=*)' % (vardef)
+            else:
+                vardef = '%s(%s=%s)' % (vardef, lk, selector['*'])
+        else:
+            if selector['*'] in ['*', ':']:
+                vardef = '%s*(%s)' % (vardef, selector['*'])
+            else:
+                vardef = '%s*%s' % (vardef, selector['*'])
+    else:
+        if 'len' in selector:
+            vardef = '%s(len=%s' % (vardef, selector['len'])
+            if 'kind' in selector:
+                vardef = '%s,kind=%s)' % (vardef, selector['kind'])
+            else:
+                vardef = '%s)' % (vardef)
+        elif 'kind' in selector:
+            vardef = '%s(kind=%s)' % (vardef, selector['kind'])
+
+    vardef = '%s %s' % (vardef, fa)
+    if 'dimension' in vars[a]:
+        vardef = '%s(%s)' % (vardef, ','.join(vars[a]['dimension']))
+    return vardef
+
+def useiso_c_binding(rout):
+    useisoc = False
+    for key, value in rout['vars'].items():
+        kind_value = value.get('kindselector', {}).get('kind')
+        if kind_value in isoc_kindmap:
+            return True
+    return useisoc
+
+def createfuncwrapper(rout, signature=0):
+    assert isfunction(rout)
+
+    extra_args = []
+    vars = rout['vars']
+    for a in rout['args']:
+        v = rout['vars'][a]
+        for i, d in enumerate(v.get('dimension', [])):
+            if d == ':':
+                dn = 'f2py_%s_d%s' % (a, i)
+                dv = dict(typespec='integer', intent=['hide'])
+                dv['='] = 'shape(%s, %s)' % (a, i)
+                extra_args.append(dn)
+                vars[dn] = dv
+                v['dimension'][i] = dn
+    rout['args'].extend(extra_args)
+    need_interface = bool(extra_args)
+
+    ret = ['']
+
+    def add(line, ret=ret):
+        ret[0] = '%s\n      %s' % (ret[0], line)
+    name = rout['name']
+    fortranname = getfortranname(rout)
+    f90mode = ismoduleroutine(rout)
+    newname = '%sf2pywrap' % (name)
+
+    if newname not in vars:
+        vars[newname] = vars[name]
+        args = [newname] + rout['args'][1:]
+    else:
+        args = [newname] + rout['args']
+
+    l_tmpl = var2fixfortran(vars, name, '@@@NAME@@@', f90mode)
+    if l_tmpl[:13] == 'character*(*)':
+        if f90mode:
+            l_tmpl = 'character(len=10)' + l_tmpl[13:]
+        else:
+            l_tmpl = 'character*10' + l_tmpl[13:]
+        charselect = vars[name]['charselector']
+        if charselect.get('*', '') == '(*)':
+            charselect['*'] = '10'
+
+    l1 = l_tmpl.replace('@@@NAME@@@', newname)
+    rl = None
+
+    useisoc = useiso_c_binding(rout)
+    sargs = ', '.join(args)
+    if f90mode:
+        # gh-23598 fix warning
+        # Essentially, this gets called again with modules where the name of the
+        # function is added to the arguments, which is not required, and removed
+        sargs = sargs.replace(f"{name}, ", '')
+        args = [arg for arg in args if arg != name]
+        rout['args'] = args
+        add('subroutine f2pywrap_%s_%s (%s)' %
+            (rout['modulename'], name, sargs))
+        if not signature:
+            add('use %s, only : %s' % (rout['modulename'], fortranname))
+        if useisoc:
+            add('use iso_c_binding')
+    else:
+        add('subroutine f2pywrap%s (%s)' % (name, sargs))
+        if useisoc:
+            add('use iso_c_binding')
+        if not need_interface:
+            add('external %s' % (fortranname))
+            rl = l_tmpl.replace('@@@NAME@@@', '') + ' ' + fortranname
+
+    if need_interface:
+        for line in rout['saved_interface'].split('\n'):
+            if line.lstrip().startswith('use ') and '__user__' not in line:
+                add(line)
+
+    args = args[1:]
+    dumped_args = []
+    for a in args:
+        if isexternal(vars[a]):
+            add('external %s' % (a))
+            dumped_args.append(a)
+    for a in args:
+        if a in dumped_args:
+            continue
+        if isscalar(vars[a]):
+            add(var2fixfortran(vars, a, f90mode=f90mode))
+            dumped_args.append(a)
+    for a in args:
+        if a in dumped_args:
+            continue
+        if isintent_in(vars[a]):
+            add(var2fixfortran(vars, a, f90mode=f90mode))
+            dumped_args.append(a)
+    for a in args:
+        if a in dumped_args:
+            continue
+        add(var2fixfortran(vars, a, f90mode=f90mode))
+
+    add(l1)
+    if rl is not None:
+        add(rl)
+
+    if need_interface:
+        if f90mode:
+            # f90 module already defines needed interface
+            pass
+        else:
+            add('interface')
+            add(rout['saved_interface'].lstrip())
+            add('end interface')
+
+    sargs = ', '.join([a for a in args if a not in extra_args])
+
+    if not signature:
+        if islogicalfunction(rout):
+            add('%s = .not.(.not.%s(%s))' % (newname, fortranname, sargs))
+        else:
+            add('%s = %s(%s)' % (newname, fortranname, sargs))
+    if f90mode:
+        add('end subroutine f2pywrap_%s_%s' % (rout['modulename'], name))
+    else:
+        add('end')
+    return ret[0]
+
+
+def createsubrwrapper(rout, signature=0):
+    assert issubroutine(rout)
+
+    extra_args = []
+    vars = rout['vars']
+    for a in rout['args']:
+        v = rout['vars'][a]
+        for i, d in enumerate(v.get('dimension', [])):
+            if d == ':':
+                dn = 'f2py_%s_d%s' % (a, i)
+                dv = dict(typespec='integer', intent=['hide'])
+                dv['='] = 'shape(%s, %s)' % (a, i)
+                extra_args.append(dn)
+                vars[dn] = dv
+                v['dimension'][i] = dn
+    rout['args'].extend(extra_args)
+    need_interface = bool(extra_args)
+
+    ret = ['']
+
+    def add(line, ret=ret):
+        ret[0] = '%s\n      %s' % (ret[0], line)
+    name = rout['name']
+    fortranname = getfortranname(rout)
+    f90mode = ismoduleroutine(rout)
+
+    args = rout['args']
+
+    useisoc = useiso_c_binding(rout)
+    sargs = ', '.join(args)
+    if f90mode:
+        add('subroutine f2pywrap_%s_%s (%s)' %
+            (rout['modulename'], name, sargs))
+        if useisoc:
+            add('use iso_c_binding')
+        if not signature:
+            add('use %s, only : %s' % (rout['modulename'], fortranname))
+    else:
+        add('subroutine f2pywrap%s (%s)' % (name, sargs))
+        if useisoc:
+            add('use iso_c_binding')
+        if not need_interface:
+            add('external %s' % (fortranname))
+
+    if need_interface:
+        for line in rout['saved_interface'].split('\n'):
+            if line.lstrip().startswith('use ') and '__user__' not in line:
+                add(line)
+
+    dumped_args = []
+    for a in args:
+        if isexternal(vars[a]):
+            add('external %s' % (a))
+            dumped_args.append(a)
+    for a in args:
+        if a in dumped_args:
+            continue
+        if isscalar(vars[a]):
+            add(var2fixfortran(vars, a, f90mode=f90mode))
+            dumped_args.append(a)
+    for a in args:
+        if a in dumped_args:
+            continue
+        add(var2fixfortran(vars, a, f90mode=f90mode))
+
+    if need_interface:
+        if f90mode:
+            # f90 module already defines needed interface
+            pass
+        else:
+            add('interface')
+            for line in rout['saved_interface'].split('\n'):
+                if line.lstrip().startswith('use ') and '__user__' in line:
+                    continue
+                add(line)
+            add('end interface')
+
+    sargs = ', '.join([a for a in args if a not in extra_args])
+
+    if not signature:
+        add('call %s(%s)' % (fortranname, sargs))
+    if f90mode:
+        add('end subroutine f2pywrap_%s_%s' % (rout['modulename'], name))
+    else:
+        add('end')
+    return ret[0]
+
+
+def assubr(rout):
+    if isfunction_wrap(rout):
+        fortranname = getfortranname(rout)
+        name = rout['name']
+        outmess('\t\tCreating wrapper for Fortran function "%s"("%s")...\n' % (
+            name, fortranname))
+        rout = copy.copy(rout)
+        fname = name
+        rname = fname
+        if 'result' in rout:
+            rname = rout['result']
+            rout['vars'][fname] = rout['vars'][rname]
+        fvar = rout['vars'][fname]
+        if not isintent_out(fvar):
+            if 'intent' not in fvar:
+                fvar['intent'] = []
+            fvar['intent'].append('out')
+            flag = 1
+            for i in fvar['intent']:
+                if i.startswith('out='):
+                    flag = 0
+                    break
+            if flag:
+                fvar['intent'].append('out=%s' % (rname))
+        rout['args'][:] = [fname] + rout['args']
+        return rout, createfuncwrapper(rout)
+    if issubroutine_wrap(rout):
+        fortranname = getfortranname(rout)
+        name = rout['name']
+        outmess('\t\tCreating wrapper for Fortran subroutine "%s"("%s")...\n'
+                % (name, fortranname))
+        rout = copy.copy(rout)
+        return rout, createsubrwrapper(rout)
+    return rout, ''
diff --git a/.venv/lib/python3.12/site-packages/numpy/f2py/rules.py b/.venv/lib/python3.12/site-packages/numpy/f2py/rules.py
new file mode 100755
index 00000000..009365e0
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/numpy/f2py/rules.py
@@ -0,0 +1,1568 @@
+#!/usr/bin/env python3
+"""
+
+Rules for building C/API module with f2py2e.
+
+Here is a skeleton of a new wrapper function (13Dec2001):
+
+wrapper_function(args)
+  declarations
+  get_python_arguments, say, `a' and `b'
+
+  get_a_from_python
+  if (successful) {
+
+    get_b_from_python
+    if (successful) {
+
+      callfortran
+      if (successful) {
+
+        put_a_to_python
+        if (successful) {
+
+          put_b_to_python
+          if (successful) {
+
+            buildvalue = ...
+
+          }
+
+        }
+
+      }
+
+    }
+    cleanup_b
+
+  }
+  cleanup_a
+
+  return buildvalue
+
+Copyright 1999 -- 2011 Pearu Peterson all rights reserved.
+Copyright 2011 -- present NumPy Developers.
+Permission to use, modify, and distribute this software is given under the
+terms of the NumPy License.
+
+NO WARRANTY IS EXPRESSED OR IMPLIED.  USE AT YOUR OWN RISK.
+"""
+import os, sys
+import time
+import copy
+from pathlib import Path
+
+# __version__.version is now the same as the NumPy version
+from . import __version__
+
+from .auxfuncs import (
+    applyrules, debugcapi, dictappend, errmess, gentitle, getargs2,
+    hascallstatement, hasexternals, hasinitvalue, hasnote,
+    hasresultnote, isarray, isarrayofstrings, ischaracter,
+    ischaracterarray, ischaracter_or_characterarray, iscomplex,
+    iscomplexarray, iscomplexfunction, iscomplexfunction_warn,
+    isdummyroutine, isexternal, isfunction, isfunction_wrap, isint1,
+    isint1array, isintent_aux, isintent_c, isintent_callback,
+    isintent_copy, isintent_hide, isintent_inout, isintent_nothide,
+    isintent_out, isintent_overwrite, islogical, islong_complex,
+    islong_double, islong_doublefunction, islong_long,
+    islong_longfunction, ismoduleroutine, isoptional, isrequired,
+    isscalar, issigned_long_longarray, isstring, isstringarray,
+    isstringfunction, issubroutine, isattr_value,
+    issubroutine_wrap, isthreadsafe, isunsigned, isunsigned_char,
+    isunsigned_chararray, isunsigned_long_long,
+    isunsigned_long_longarray, isunsigned_short, isunsigned_shortarray,
+    l_and, l_not, l_or, outmess, replace, stripcomma, requiresf90wrapper
+)
+
+from . import capi_maps
+from . import cfuncs
+from . import common_rules
+from . import use_rules
+from . import f90mod_rules
+from . import func2subr
+
+f2py_version = __version__.version
+numpy_version = __version__.version
+
+options = {}
+sepdict = {}
+# for k in ['need_cfuncs']: sepdict[k]=','
+for k in ['decl',
+          'frompyobj',
+          'cleanupfrompyobj',
+          'topyarr', 'method',
+          'pyobjfrom', 'closepyobjfrom',
+          'freemem',
+          'userincludes',
+          'includes0', 'includes', 'typedefs', 'typedefs_generated',
+          'cppmacros', 'cfuncs', 'callbacks',
+          'latexdoc',
+          'restdoc',
+          'routine_defs', 'externroutines',
+          'initf2pywraphooks',
+          'commonhooks', 'initcommonhooks',
+          'f90modhooks', 'initf90modhooks']:
+    sepdict[k] = '\n'
+
+#################### Rules for C/API module #################
+
+generationtime = int(os.environ.get('SOURCE_DATE_EPOCH', time.time()))
+module_rules = {
+    'modulebody': """\
+/* File: #modulename#module.c
+ * This file is auto-generated with f2py (version:#f2py_version#).
+ * f2py is a Fortran to Python Interface Generator (FPIG), Second Edition,
+ * written by Pearu Peterson <pearu@cens.ioc.ee>.
+ * Generation date: """ + time.asctime(time.gmtime(generationtime)) + """
+ * Do not edit this file directly unless you know what you are doing!!!
+ */
+
+#ifdef __cplusplus
+extern \"C\" {
+#endif
+
+#ifndef PY_SSIZE_T_CLEAN
+#define PY_SSIZE_T_CLEAN
+#endif /* PY_SSIZE_T_CLEAN */
+
+/* Unconditionally included */
+#include <Python.h>
+#include <numpy/npy_os.h>
+
+""" + gentitle("See f2py2e/cfuncs.py: includes") + """
+#includes#
+#includes0#
+
+""" + gentitle("See f2py2e/rules.py: mod_rules['modulebody']") + """
+static PyObject *#modulename#_error;
+static PyObject *#modulename#_module;
+
+""" + gentitle("See f2py2e/cfuncs.py: typedefs") + """
+#typedefs#
+
+""" + gentitle("See f2py2e/cfuncs.py: typedefs_generated") + """
+#typedefs_generated#
+
+""" + gentitle("See f2py2e/cfuncs.py: cppmacros") + """
+#cppmacros#
+
+""" + gentitle("See f2py2e/cfuncs.py: cfuncs") + """
+#cfuncs#
+
+""" + gentitle("See f2py2e/cfuncs.py: userincludes") + """
+#userincludes#
+
+""" + gentitle("See f2py2e/capi_rules.py: usercode") + """
+#usercode#
+
+/* See f2py2e/rules.py */
+#externroutines#
+
+""" + gentitle("See f2py2e/capi_rules.py: usercode1") + """
+#usercode1#
+
+""" + gentitle("See f2py2e/cb_rules.py: buildcallback") + """
+#callbacks#
+
+""" + gentitle("See f2py2e/rules.py: buildapi") + """
+#body#
+
+""" + gentitle("See f2py2e/f90mod_rules.py: buildhooks") + """
+#f90modhooks#
+
+""" + gentitle("See f2py2e/rules.py: module_rules['modulebody']") + """
+
+""" + gentitle("See f2py2e/common_rules.py: buildhooks") + """
+#commonhooks#
+
+""" + gentitle("See f2py2e/rules.py") + """
+
+static FortranDataDef f2py_routine_defs[] = {
+#routine_defs#
+    {NULL}
+};
+
+static PyMethodDef f2py_module_methods[] = {
+#pymethoddef#
+    {NULL,NULL}
+};
+
+static struct PyModuleDef moduledef = {
+    PyModuleDef_HEAD_INIT,
+    "#modulename#",
+    NULL,
+    -1,
+    f2py_module_methods,
+    NULL,
+    NULL,
+    NULL,
+    NULL
+};
+
+PyMODINIT_FUNC PyInit_#modulename#(void) {
+    int i;
+    PyObject *m,*d, *s, *tmp;
+    m = #modulename#_module = PyModule_Create(&moduledef);
+    Py_SET_TYPE(&PyFortran_Type, &PyType_Type);
+    import_array();
+    if (PyErr_Occurred())
+        {PyErr_SetString(PyExc_ImportError, \"can't initialize module #modulename# (failed to import numpy)\"); return m;}
+    d = PyModule_GetDict(m);
+    s = PyUnicode_FromString(\"#f2py_version#\");
+    PyDict_SetItemString(d, \"__version__\", s);
+    Py_DECREF(s);
+    s = PyUnicode_FromString(
+        \"This module '#modulename#' is auto-generated with f2py (version:#f2py_version#).\\nFunctions:\\n\"\n#docs#\".\");
+    PyDict_SetItemString(d, \"__doc__\", s);
+    Py_DECREF(s);
+    s = PyUnicode_FromString(\"""" + numpy_version + """\");
+    PyDict_SetItemString(d, \"__f2py_numpy_version__\", s);
+    Py_DECREF(s);
+    #modulename#_error = PyErr_NewException (\"#modulename#.error\", NULL, NULL);
+    /*
+     * Store the error object inside the dict, so that it could get deallocated.
+     * (in practice, this is a module, so it likely will not and cannot.)
+     */
+    PyDict_SetItemString(d, \"_#modulename#_error\", #modulename#_error);
+    Py_DECREF(#modulename#_error);
+    for(i=0;f2py_routine_defs[i].name!=NULL;i++) {
+        tmp = PyFortranObject_NewAsAttr(&f2py_routine_defs[i]);
+        PyDict_SetItemString(d, f2py_routine_defs[i].name, tmp);
+        Py_DECREF(tmp);
+    }
+#initf2pywraphooks#
+#initf90modhooks#
+#initcommonhooks#
+#interface_usercode#
+
+#ifdef F2PY_REPORT_ATEXIT
+    if (! PyErr_Occurred())
+        on_exit(f2py_report_on_exit,(void*)\"#modulename#\");
+#endif
+    return m;
+}
+#ifdef __cplusplus
+}
+#endif
+""",
+    'separatorsfor': {'latexdoc': '\n\n',
+                      'restdoc': '\n\n'},
+    'latexdoc': ['\\section{Module \\texttt{#texmodulename#}}\n',
+                 '#modnote#\n',
+                 '#latexdoc#'],
+    'restdoc': ['Module #modulename#\n' + '=' * 80,
+                '\n#restdoc#']
+}
+
+defmod_rules = [
+    {'body': '/*eof body*/',
+     'method': '/*eof method*/',
+     'externroutines': '/*eof externroutines*/',
+     'routine_defs': '/*eof routine_defs*/',
+     'initf90modhooks': '/*eof initf90modhooks*/',
+     'initf2pywraphooks': '/*eof initf2pywraphooks*/',
+     'initcommonhooks': '/*eof initcommonhooks*/',
+     'latexdoc': '',
+     'restdoc': '',
+     'modnote': {hasnote: '#note#', l_not(hasnote): ''},
+     }
+]
+
+routine_rules = {
+    'separatorsfor': sepdict,
+    'body': """
+#begintitle#
+static char doc_#apiname#[] = \"\\\n#docreturn##name#(#docsignatureshort#)\\n\\nWrapper for ``#name#``.\\\n\\n#docstrsigns#\";
+/* #declfortranroutine# */
+static PyObject *#apiname#(const PyObject *capi_self,
+                           PyObject *capi_args,
+                           PyObject *capi_keywds,
+                           #functype# (*f2py_func)(#callprotoargument#)) {
+    PyObject * volatile capi_buildvalue = NULL;
+    volatile int f2py_success = 1;
+#decl#
+    static char *capi_kwlist[] = {#kwlist##kwlistopt##kwlistxa#NULL};
+#usercode#
+#routdebugenter#
+#ifdef F2PY_REPORT_ATEXIT
+f2py_start_clock();
+#endif
+    if (!PyArg_ParseTupleAndKeywords(capi_args,capi_keywds,\\
+        \"#argformat#|#keyformat##xaformat#:#pyname#\",\\
+        capi_kwlist#args_capi##keys_capi##keys_xa#))\n        return NULL;
+#frompyobj#
+/*end of frompyobj*/
+#ifdef F2PY_REPORT_ATEXIT
+f2py_start_call_clock();
+#endif
+#callfortranroutine#
+if (PyErr_Occurred())
+  f2py_success = 0;
+#ifdef F2PY_REPORT_ATEXIT
+f2py_stop_call_clock();
+#endif
+/*end of callfortranroutine*/
+        if (f2py_success) {
+#pyobjfrom#
+/*end of pyobjfrom*/
+        CFUNCSMESS(\"Building return value.\\n\");
+        capi_buildvalue = Py_BuildValue(\"#returnformat#\"#return#);
+/*closepyobjfrom*/
+#closepyobjfrom#
+        } /*if (f2py_success) after callfortranroutine*/
+/*cleanupfrompyobj*/
+#cleanupfrompyobj#
+    if (capi_buildvalue == NULL) {
+#routdebugfailure#
+    } else {
+#routdebugleave#
+    }
+    CFUNCSMESS(\"Freeing memory.\\n\");
+#freemem#
+#ifdef F2PY_REPORT_ATEXIT
+f2py_stop_clock();
+#endif
+    return capi_buildvalue;
+}
+#endtitle#
+""",
+    'routine_defs': '#routine_def#',
+    'initf2pywraphooks': '#initf2pywraphook#',
+    'externroutines': '#declfortranroutine#',
+    'doc': '#docreturn##name#(#docsignature#)',
+    'docshort': '#docreturn##name#(#docsignatureshort#)',
+    'docs': '"    #docreturn##name#(#docsignature#)\\n"\n',
+    'need': ['arrayobject.h', 'CFUNCSMESS', 'MINMAX'],
+    'cppmacros': {debugcapi: '#define DEBUGCFUNCS'},
+    'latexdoc': ['\\subsection{Wrapper function \\texttt{#texname#}}\n',
+                 """
+\\noindent{{}\\verb@#docreturn##name#@{}}\\texttt{(#latexdocsignatureshort#)}
+#routnote#
+
+#latexdocstrsigns#
+"""],
+    'restdoc': ['Wrapped function ``#name#``\n' + '-' * 80,
+
+                ]
+}
+
+################## Rules for C/API function ##############
+
+rout_rules = [
+    {  # Init
+        'separatorsfor': {'callfortranroutine': '\n', 'routdebugenter': '\n', 'decl': '\n',
+                          'routdebugleave': '\n', 'routdebugfailure': '\n',
+                          'setjmpbuf': ' || ',
+                          'docstrreq': '\n', 'docstropt': '\n', 'docstrout': '\n',
+                          'docstrcbs': '\n', 'docstrsigns': '\\n"\n"',
+                          'latexdocstrsigns': '\n',
+                          'latexdocstrreq': '\n', 'latexdocstropt': '\n',
+                          'latexdocstrout': '\n', 'latexdocstrcbs': '\n',
+                          },
+        'kwlist': '', 'kwlistopt': '', 'callfortran': '', 'callfortranappend': '',
+        'docsign': '', 'docsignopt': '', 'decl': '/*decl*/',
+        'freemem': '/*freemem*/',
+        'docsignshort': '', 'docsignoptshort': '',
+        'docstrsigns': '', 'latexdocstrsigns': '',
+        'docstrreq': '\\nParameters\\n----------',
+        'docstropt': '\\nOther Parameters\\n----------------',
+        'docstrout': '\\nReturns\\n-------',
+        'docstrcbs': '\\nNotes\\n-----\\nCall-back functions::\\n',
+        'latexdocstrreq': '\\noindent Required arguments:',
+        'latexdocstropt': '\\noindent Optional arguments:',
+        'latexdocstrout': '\\noindent Return objects:',
+        'latexdocstrcbs': '\\noindent Call-back functions:',
+        'args_capi': '', 'keys_capi': '', 'functype': '',
+        'frompyobj': '/*frompyobj*/',
+        # this list will be reversed
+        'cleanupfrompyobj': ['/*end of cleanupfrompyobj*/'],
+        'pyobjfrom': '/*pyobjfrom*/',
+        # this list will be reversed
+        'closepyobjfrom': ['/*end of closepyobjfrom*/'],
+        'topyarr': '/*topyarr*/', 'routdebugleave': '/*routdebugleave*/',
+        'routdebugenter': '/*routdebugenter*/',
+        'routdebugfailure': '/*routdebugfailure*/',
+        'callfortranroutine': '/*callfortranroutine*/',
+        'argformat': '', 'keyformat': '', 'need_cfuncs': '',
+        'docreturn': '', 'return': '', 'returnformat': '', 'rformat': '',
+        'kwlistxa': '', 'keys_xa': '', 'xaformat': '', 'docsignxa': '', 'docsignxashort': '',
+        'initf2pywraphook': '',
+        'routnote': {hasnote: '--- #note#', l_not(hasnote): ''},
+    }, {
+        'apiname': 'f2py_rout_#modulename#_#name#',
+        'pyname': '#modulename#.#name#',
+        'decl': '',
+        '_check': l_not(ismoduleroutine)
+    }, {
+        'apiname': 'f2py_rout_#modulename#_#f90modulename#_#name#',
+        'pyname': '#modulename#.#f90modulename#.#name#',
+        'decl': '',
+        '_check': ismoduleroutine
+    }, {  # Subroutine
+        'functype': 'void',
+        'declfortranroutine': {l_and(l_not(l_or(ismoduleroutine, isintent_c)), l_not(isdummyroutine)): 'extern void #F_FUNC#(#fortranname#,#FORTRANNAME#)(#callprotoargument#);',
+                               l_and(l_not(ismoduleroutine), isintent_c, l_not(isdummyroutine)): 'extern void #fortranname#(#callprotoargument#);',
+                               ismoduleroutine: '',
+                               isdummyroutine: ''
+                               },
+        'routine_def': {
+            l_not(l_or(ismoduleroutine, isintent_c, isdummyroutine)):
+            '    {\"#name#\",-1,{{-1}},0,0,(char *)'
+            '  #F_FUNC#(#fortranname#,#FORTRANNAME#),'
+            '  (f2py_init_func)#apiname#,doc_#apiname#},',
+            l_and(l_not(ismoduleroutine), isintent_c, l_not(isdummyroutine)):
+            '    {\"#name#\",-1,{{-1}},0,0,(char *)#fortranname#,'
+            '  (f2py_init_func)#apiname#,doc_#apiname#},',
+            l_and(l_not(ismoduleroutine), isdummyroutine):
+            '    {\"#name#\",-1,{{-1}},0,0,NULL,'
+            '  (f2py_init_func)#apiname#,doc_#apiname#},',
+        },
+        'need': {l_and(l_not(l_or(ismoduleroutine, isintent_c)), l_not(isdummyroutine)): 'F_FUNC'},
+        'callfortranroutine': [
+            {debugcapi: [
+                """    fprintf(stderr,\"debug-capi:Fortran subroutine `#fortranname#(#callfortran#)\'\\n\");"""]},
+            {hasexternals: """\
+        if (#setjmpbuf#) {
+            f2py_success = 0;
+        } else {"""},
+            {isthreadsafe: '            Py_BEGIN_ALLOW_THREADS'},
+            {hascallstatement: '''                #callstatement#;
+                /*(*f2py_func)(#callfortran#);*/'''},
+            {l_not(l_or(hascallstatement, isdummyroutine))
+                   : '                (*f2py_func)(#callfortran#);'},
+            {isthreadsafe: '            Py_END_ALLOW_THREADS'},
+            {hasexternals: """        }"""}
+        ],
+        '_check': l_and(issubroutine, l_not(issubroutine_wrap)),
+    }, {  # Wrapped function
+        'functype': 'void',
+        'declfortranroutine': {l_not(l_or(ismoduleroutine, isdummyroutine)): 'extern void #F_WRAPPEDFUNC#(#name_lower#,#NAME#)(#callprotoargument#);',
+                               isdummyroutine: '',
+                               },
+
+        'routine_def': {
+            l_not(l_or(ismoduleroutine, isdummyroutine)):
+            '    {\"#name#\",-1,{{-1}},0,0,(char *)'
+            '  #F_WRAPPEDFUNC#(#name_lower#,#NAME#),'
+            '  (f2py_init_func)#apiname#,doc_#apiname#},',
+            isdummyroutine:
+            '    {\"#name#\",-1,{{-1}},0,0,NULL,'
+            '  (f2py_init_func)#apiname#,doc_#apiname#},',
+        },
+        'initf2pywraphook': {l_not(l_or(ismoduleroutine, isdummyroutine)): '''
+    {
+      extern #ctype# #F_FUNC#(#name_lower#,#NAME#)(void);
+      PyObject* o = PyDict_GetItemString(d,"#name#");
+      tmp = F2PyCapsule_FromVoidPtr((void*)#F_FUNC#(#name_lower#,#NAME#),NULL);
+      PyObject_SetAttrString(o,"_cpointer", tmp);
+      Py_DECREF(tmp);
+      s = PyUnicode_FromString("#name#");
+      PyObject_SetAttrString(o,"__name__", s);
+      Py_DECREF(s);
+    }
+    '''},
+        'need': {l_not(l_or(ismoduleroutine, isdummyroutine)): ['F_WRAPPEDFUNC', 'F_FUNC']},
+        'callfortranroutine': [
+            {debugcapi: [
+                """    fprintf(stderr,\"debug-capi:Fortran subroutine `f2pywrap#name_lower#(#callfortran#)\'\\n\");"""]},
+            {hasexternals: """\
+    if (#setjmpbuf#) {
+        f2py_success = 0;
+    } else {"""},
+            {isthreadsafe: '    Py_BEGIN_ALLOW_THREADS'},
+            {l_not(l_or(hascallstatement, isdummyroutine))
+                   : '    (*f2py_func)(#callfortran#);'},
+            {hascallstatement:
+                '    #callstatement#;\n    /*(*f2py_func)(#callfortran#);*/'},
+            {isthreadsafe: '    Py_END_ALLOW_THREADS'},
+            {hasexternals: '    }'}
+        ],
+        '_check': isfunction_wrap,
+    }, {  # Wrapped subroutine
+        'functype': 'void',
+        'declfortranroutine': {l_not(l_or(ismoduleroutine, isdummyroutine)): 'extern void #F_WRAPPEDFUNC#(#name_lower#,#NAME#)(#callprotoargument#);',
+                               isdummyroutine: '',
+                               },
+
+        'routine_def': {
+            l_not(l_or(ismoduleroutine, isdummyroutine)):
+            '    {\"#name#\",-1,{{-1}},0,0,(char *)'
+            '  #F_WRAPPEDFUNC#(#name_lower#,#NAME#),'
+            '  (f2py_init_func)#apiname#,doc_#apiname#},',
+            isdummyroutine:
+            '    {\"#name#\",-1,{{-1}},0,0,NULL,'
+            '  (f2py_init_func)#apiname#,doc_#apiname#},',
+        },
+        'initf2pywraphook': {l_not(l_or(ismoduleroutine, isdummyroutine)): '''
+    {
+      extern void #F_FUNC#(#name_lower#,#NAME#)(void);
+      PyObject* o = PyDict_GetItemString(d,"#name#");
+      tmp = F2PyCapsule_FromVoidPtr((void*)#F_FUNC#(#name_lower#,#NAME#),NULL);
+      PyObject_SetAttrString(o,"_cpointer", tmp);
+      Py_DECREF(tmp);
+      s = PyUnicode_FromString("#name#");
+      PyObject_SetAttrString(o,"__name__", s);
+      Py_DECREF(s);
+    }
+    '''},
+        'need': {l_not(l_or(ismoduleroutine, isdummyroutine)): ['F_WRAPPEDFUNC', 'F_FUNC']},
+        'callfortranroutine': [
+            {debugcapi: [
+                """    fprintf(stderr,\"debug-capi:Fortran subroutine `f2pywrap#name_lower#(#callfortran#)\'\\n\");"""]},
+            {hasexternals: """\
+    if (#setjmpbuf#) {
+        f2py_success = 0;
+    } else {"""},
+            {isthreadsafe: '    Py_BEGIN_ALLOW_THREADS'},
+            {l_not(l_or(hascallstatement, isdummyroutine))
+                   : '    (*f2py_func)(#callfortran#);'},
+            {hascallstatement:
+                '    #callstatement#;\n    /*(*f2py_func)(#callfortran#);*/'},
+            {isthreadsafe: '    Py_END_ALLOW_THREADS'},
+            {hasexternals: '    }'}
+        ],
+        '_check': issubroutine_wrap,
+    }, {  # Function
+        'functype': '#ctype#',
+        'docreturn': {l_not(isintent_hide): '#rname#,'},
+        'docstrout': '#pydocsignout#',
+        'latexdocstrout': ['\\item[]{{}\\verb@#pydocsignout#@{}}',
+                           {hasresultnote: '--- #resultnote#'}],
+        'callfortranroutine': [{l_and(debugcapi, isstringfunction): """\
+#ifdef USESCOMPAQFORTRAN
+    fprintf(stderr,\"debug-capi:Fortran function #ctype# #fortranname#(#callcompaqfortran#)\\n\");
+#else
+    fprintf(stderr,\"debug-capi:Fortran function #ctype# #fortranname#(#callfortran#)\\n\");
+#endif
+"""},
+                               {l_and(debugcapi, l_not(isstringfunction)): """\
+    fprintf(stderr,\"debug-capi:Fortran function #ctype# #fortranname#(#callfortran#)\\n\");
+"""}
+                               ],
+        '_check': l_and(isfunction, l_not(isfunction_wrap))
+    }, {  # Scalar function
+        'declfortranroutine': {l_and(l_not(l_or(ismoduleroutine, isintent_c)), l_not(isdummyroutine)): 'extern #ctype# #F_FUNC#(#fortranname#,#FORTRANNAME#)(#callprotoargument#);',
+                               l_and(l_not(ismoduleroutine), isintent_c, l_not(isdummyroutine)): 'extern #ctype# #fortranname#(#callprotoargument#);',
+                               isdummyroutine: ''
+                               },
+        'routine_def': {
+            l_and(l_not(l_or(ismoduleroutine, isintent_c)),
+                  l_not(isdummyroutine)):
+            ('    {\"#name#\",-1,{{-1}},0,0,(char *)'
+             '  #F_FUNC#(#fortranname#,#FORTRANNAME#),'
+             '  (f2py_init_func)#apiname#,doc_#apiname#},'),
+            l_and(l_not(ismoduleroutine), isintent_c, l_not(isdummyroutine)):
+            ('    {\"#name#\",-1,{{-1}},0,0,(char *)#fortranname#,'
+             '  (f2py_init_func)#apiname#,doc_#apiname#},'),
+            isdummyroutine:
+            '    {\"#name#\",-1,{{-1}},0,0,NULL,'
+            '(f2py_init_func)#apiname#,doc_#apiname#},',
+        },
+        'decl': [{iscomplexfunction_warn: '    #ctype# #name#_return_value={0,0};',
+                  l_not(iscomplexfunction): '    #ctype# #name#_return_value=0;'},
+                 {iscomplexfunction:
+                  '    PyObject *#name#_return_value_capi = Py_None;'}
+                 ],
+        'callfortranroutine': [
+            {hasexternals: """\
+    if (#setjmpbuf#) {
+        f2py_success = 0;
+    } else {"""},
+            {isthreadsafe: '    Py_BEGIN_ALLOW_THREADS'},
+            {hascallstatement: '''    #callstatement#;
+/*    #name#_return_value = (*f2py_func)(#callfortran#);*/
+'''},
+            {l_not(l_or(hascallstatement, isdummyroutine))
+                   : '    #name#_return_value = (*f2py_func)(#callfortran#);'},
+            {isthreadsafe: '    Py_END_ALLOW_THREADS'},
+            {hasexternals: '    }'},
+            {l_and(debugcapi, iscomplexfunction)
+                   : '    fprintf(stderr,"#routdebugshowvalue#\\n",#name#_return_value.r,#name#_return_value.i);'},
+            {l_and(debugcapi, l_not(iscomplexfunction)): '    fprintf(stderr,"#routdebugshowvalue#\\n",#name#_return_value);'}],
+        'pyobjfrom': {iscomplexfunction: '    #name#_return_value_capi = pyobj_from_#ctype#1(#name#_return_value);'},
+        'need': [{l_not(isdummyroutine): 'F_FUNC'},
+                 {iscomplexfunction: 'pyobj_from_#ctype#1'},
+                 {islong_longfunction: 'long_long'},
+                 {islong_doublefunction: 'long_double'}],
+        'returnformat': {l_not(isintent_hide): '#rformat#'},
+        'return': {iscomplexfunction: ',#name#_return_value_capi',
+                   l_not(l_or(iscomplexfunction, isintent_hide)): ',#name#_return_value'},
+        '_check': l_and(isfunction, l_not(isstringfunction), l_not(isfunction_wrap))
+    }, {  # String function # in use for --no-wrap
+        'declfortranroutine': 'extern void #F_FUNC#(#fortranname#,#FORTRANNAME#)(#callprotoargument#);',
+        'routine_def': {l_not(l_or(ismoduleroutine, isintent_c)):
+                        '    {\"#name#\",-1,{{-1}},0,0,(char *)#F_FUNC#(#fortranname#,#FORTRANNAME#),(f2py_init_func)#apiname#,doc_#apiname#},',
+                        l_and(l_not(ismoduleroutine), isintent_c):
+                        '    {\"#name#\",-1,{{-1}},0,0,(char *)#fortranname#,(f2py_init_func)#apiname#,doc_#apiname#},'
+                        },
+        'decl': ['    #ctype# #name#_return_value = NULL;',
+                 '    int #name#_return_value_len = 0;'],
+        'callfortran':'#name#_return_value,#name#_return_value_len,',
+        'callfortranroutine':['    #name#_return_value_len = #rlength#;',
+                              '    if ((#name#_return_value = (string)malloc('
+                              + '#name#_return_value_len+1) == NULL) {',
+                              '        PyErr_SetString(PyExc_MemoryError, \"out of memory\");',
+                              '        f2py_success = 0;',
+                              '    } else {',
+                              "        (#name#_return_value)[#name#_return_value_len] = '\\0';",
+                              '    }',
+                              '    if (f2py_success) {',
+                              {hasexternals: """\
+        if (#setjmpbuf#) {
+            f2py_success = 0;
+        } else {"""},
+                              {isthreadsafe: '        Py_BEGIN_ALLOW_THREADS'},
+                              """\
+#ifdef USESCOMPAQFORTRAN
+        (*f2py_func)(#callcompaqfortran#);
+#else
+        (*f2py_func)(#callfortran#);
+#endif
+""",
+                              {isthreadsafe: '        Py_END_ALLOW_THREADS'},
+                              {hasexternals: '        }'},
+                              {debugcapi:
+                                  '        fprintf(stderr,"#routdebugshowvalue#\\n",#name#_return_value_len,#name#_return_value);'},
+                              '    } /* if (f2py_success) after (string)malloc */',
+                              ],
+        'returnformat': '#rformat#',
+        'return': ',#name#_return_value',
+        'freemem': '    STRINGFREE(#name#_return_value);',
+        'need': ['F_FUNC', '#ctype#', 'STRINGFREE'],
+        '_check':l_and(isstringfunction, l_not(isfunction_wrap))  # ???obsolete
+    },
+    {  # Debugging
+        'routdebugenter': '    fprintf(stderr,"debug-capi:Python C/API function #modulename#.#name#(#docsignature#)\\n");',
+        'routdebugleave': '    fprintf(stderr,"debug-capi:Python C/API function #modulename#.#name#: successful.\\n");',
+        'routdebugfailure': '    fprintf(stderr,"debug-capi:Python C/API function #modulename#.#name#: failure.\\n");',
+        '_check': debugcapi
+    }
+]
+
+################ Rules for arguments ##################
+
+typedef_need_dict = {islong_long: 'long_long',
+                     islong_double: 'long_double',
+                     islong_complex: 'complex_long_double',
+                     isunsigned_char: 'unsigned_char',
+                     isunsigned_short: 'unsigned_short',
+                     isunsigned: 'unsigned',
+                     isunsigned_long_long: 'unsigned_long_long',
+                     isunsigned_chararray: 'unsigned_char',
+                     isunsigned_shortarray: 'unsigned_short',
+                     isunsigned_long_longarray: 'unsigned_long_long',
+                     issigned_long_longarray: 'long_long',
+                     isint1: 'signed_char',
+                     ischaracter_or_characterarray: 'character',
+                     }
+
+aux_rules = [
+    {
+        'separatorsfor': sepdict
+    },
+    {  # Common
+        'frompyobj': ['    /* Processing auxiliary variable #varname# */',
+                      {debugcapi: '    fprintf(stderr,"#vardebuginfo#\\n");'}, ],
+        'cleanupfrompyobj': '    /* End of cleaning variable #varname# */',
+        'need': typedef_need_dict,
+    },
+    # Scalars (not complex)
+    {  # Common
+        'decl': '    #ctype# #varname# = 0;',
+        'need': {hasinitvalue: 'math.h'},
+        'frompyobj': {hasinitvalue: '    #varname# = #init#;'},
+        '_check': l_and(isscalar, l_not(iscomplex)),
+    },
+    {
+        'return': ',#varname#',
+        'docstrout': '#pydocsignout#',
+        'docreturn': '#outvarname#,',
+        'returnformat': '#varrformat#',
+        '_check': l_and(isscalar, l_not(iscomplex), isintent_out),
+    },
+    # Complex scalars
+    {  # Common
+        'decl': '    #ctype# #varname#;',
+        'frompyobj': {hasinitvalue: '    #varname#.r = #init.r#, #varname#.i = #init.i#;'},
+        '_check': iscomplex
+    },
+    # String
+    {  # Common
+        'decl': ['    #ctype# #varname# = NULL;',
+                 '    int slen(#varname#);',
+                 ],
+        'need':['len..'],
+        '_check':isstring
+    },
+    # Array
+    {  # Common
+        'decl': ['    #ctype# *#varname# = NULL;',
+                 '    npy_intp #varname#_Dims[#rank#] = {#rank*[-1]#};',
+                 '    const int #varname#_Rank = #rank#;',
+                 ],
+        'need':['len..', {hasinitvalue: 'forcomb'}, {hasinitvalue: 'CFUNCSMESS'}],
+        '_check': isarray
+    },
+    # Scalararray
+    {  # Common
+        '_check': l_and(isarray, l_not(iscomplexarray))
+    }, {  # Not hidden
+        '_check': l_and(isarray, l_not(iscomplexarray), isintent_nothide)
+    },
+    # Integer*1 array
+    {'need': '#ctype#',
+     '_check': isint1array,
+     '_depend': ''
+     },
+    # Integer*-1 array
+    {'need': '#ctype#',
+     '_check': l_or(isunsigned_chararray, isunsigned_char),
+     '_depend': ''
+     },
+    # Integer*-2 array
+    {'need': '#ctype#',
+     '_check': isunsigned_shortarray,
+     '_depend': ''
+     },
+    # Integer*-8 array
+    {'need': '#ctype#',
+     '_check': isunsigned_long_longarray,
+     '_depend': ''
+     },
+    # Complexarray
+    {'need': '#ctype#',
+     '_check': iscomplexarray,
+     '_depend': ''
+     },
+    # Stringarray
+    {
+        'callfortranappend': {isarrayofstrings: 'flen(#varname#),'},
+        'need': 'string',
+        '_check': isstringarray
+    }
+]
+
+arg_rules = [
+    {
+        'separatorsfor': sepdict
+    },
+    {  # Common
+        'frompyobj': ['    /* Processing variable #varname# */',
+                      {debugcapi: '    fprintf(stderr,"#vardebuginfo#\\n");'}, ],
+        'cleanupfrompyobj': '    /* End of cleaning variable #varname# */',
+        '_depend': '',
+        'need': typedef_need_dict,
+    },
+    # Doc signatures
+    {
+        'docstropt': {l_and(isoptional, isintent_nothide): '#pydocsign#'},
+        'docstrreq': {l_and(isrequired, isintent_nothide): '#pydocsign#'},
+        'docstrout': {isintent_out: '#pydocsignout#'},
+        'latexdocstropt': {l_and(isoptional, isintent_nothide): ['\\item[]{{}\\verb@#pydocsign#@{}}',
+                                                                 {hasnote: '--- #note#'}]},
+        'latexdocstrreq': {l_and(isrequired, isintent_nothide): ['\\item[]{{}\\verb@#pydocsign#@{}}',
+                                                                 {hasnote: '--- #note#'}]},
+        'latexdocstrout': {isintent_out: ['\\item[]{{}\\verb@#pydocsignout#@{}}',
+                                          {l_and(hasnote, isintent_hide): '--- #note#',
+                                           l_and(hasnote, isintent_nothide): '--- See above.'}]},
+        'depend': ''
+    },
+    # Required/Optional arguments
+    {
+        'kwlist': '"#varname#",',
+        'docsign': '#varname#,',
+        '_check': l_and(isintent_nothide, l_not(isoptional))
+    },
+    {
+        'kwlistopt': '"#varname#",',
+        'docsignopt': '#varname#=#showinit#,',
+        'docsignoptshort': '#varname#,',
+        '_check': l_and(isintent_nothide, isoptional)
+    },
+    # Docstring/BuildValue
+    {
+        'docreturn': '#outvarname#,',
+        'returnformat': '#varrformat#',
+        '_check': isintent_out
+    },
+    # Externals (call-back functions)
+    {  # Common
+        'docsignxa': {isintent_nothide: '#varname#_extra_args=(),'},
+        'docsignxashort': {isintent_nothide: '#varname#_extra_args,'},
+        'docstropt': {isintent_nothide: '#varname#_extra_args : input tuple, optional\\n    Default: ()'},
+        'docstrcbs': '#cbdocstr#',
+        'latexdocstrcbs': '\\item[] #cblatexdocstr#',
+        'latexdocstropt': {isintent_nothide: '\\item[]{{}\\verb@#varname#_extra_args := () input tuple@{}} --- Extra arguments for call-back function {{}\\verb@#varname#@{}}.'},
+        'decl': ['    #cbname#_t #varname#_cb = { Py_None, NULL, 0 };',
+                 '    #cbname#_t *#varname#_cb_ptr = &#varname#_cb;',
+                 '    PyTupleObject *#varname#_xa_capi = NULL;',
+                 {l_not(isintent_callback):
+                  '    #cbname#_typedef #varname#_cptr;'}
+                 ],
+        'kwlistxa': {isintent_nothide: '"#varname#_extra_args",'},
+        'argformat': {isrequired: 'O'},
+        'keyformat': {isoptional: 'O'},
+        'xaformat': {isintent_nothide: 'O!'},
+        'args_capi': {isrequired: ',&#varname#_cb.capi'},
+        'keys_capi': {isoptional: ',&#varname#_cb.capi'},
+        'keys_xa': ',&PyTuple_Type,&#varname#_xa_capi',
+        'setjmpbuf': '(setjmp(#varname#_cb.jmpbuf))',
+        'callfortran': {l_not(isintent_callback): '#varname#_cptr,'},
+        'need': ['#cbname#', 'setjmp.h'],
+        '_check':isexternal
+    },
+    {
+        'frompyobj': [{l_not(isintent_callback): """\
+if(F2PyCapsule_Check(#varname#_cb.capi)) {
+  #varname#_cptr = F2PyCapsule_AsVoidPtr(#varname#_cb.capi);
+} else {
+  #varname#_cptr = #cbname#;
+}
+"""}, {isintent_callback: """\
+if (#varname#_cb.capi==Py_None) {
+  #varname#_cb.capi = PyObject_GetAttrString(#modulename#_module,\"#varname#\");
+  if (#varname#_cb.capi) {
+    if (#varname#_xa_capi==NULL) {
+      if (PyObject_HasAttrString(#modulename#_module,\"#varname#_extra_args\")) {
+        PyObject* capi_tmp = PyObject_GetAttrString(#modulename#_module,\"#varname#_extra_args\");
+        if (capi_tmp) {
+          #varname#_xa_capi = (PyTupleObject *)PySequence_Tuple(capi_tmp);
+          Py_DECREF(capi_tmp);
+        }
+        else {
+          #varname#_xa_capi = (PyTupleObject *)Py_BuildValue(\"()\");
+        }
+        if (#varname#_xa_capi==NULL) {
+          PyErr_SetString(#modulename#_error,\"Failed to convert #modulename#.#varname#_extra_args to tuple.\\n\");
+          return NULL;
+        }
+      }
+    }
+  }
+  if (#varname#_cb.capi==NULL) {
+    PyErr_SetString(#modulename#_error,\"Callback #varname# not defined (as an argument or module #modulename# attribute).\\n\");
+    return NULL;
+  }
+}
+"""},
+            """\
+    if (create_cb_arglist(#varname#_cb.capi,#varname#_xa_capi,#maxnofargs#,#nofoptargs#,&#varname#_cb.nofargs,&#varname#_cb.args_capi,\"failed in processing argument list for call-back #varname#.\")) {
+""",
+            {debugcapi: ["""\
+        fprintf(stderr,\"debug-capi:Assuming %d arguments; at most #maxnofargs#(-#nofoptargs#) is expected.\\n\",#varname#_cb.nofargs);
+        CFUNCSMESSPY(\"for #varname#=\",#varname#_cb.capi);""",
+                         {l_not(isintent_callback): """        fprintf(stderr,\"#vardebugshowvalue# (call-back in C).\\n\",#cbname#);"""}]},
+            """\
+        CFUNCSMESS(\"Saving callback variables for `#varname#`.\\n\");
+        #varname#_cb_ptr = swap_active_#cbname#(#varname#_cb_ptr);""",
+        ],
+        'cleanupfrompyobj':
+        """\
+        CFUNCSMESS(\"Restoring callback variables for `#varname#`.\\n\");
+        #varname#_cb_ptr = swap_active_#cbname#(#varname#_cb_ptr);
+        Py_DECREF(#varname#_cb.args_capi);
+    }""",
+        'need': ['SWAP', 'create_cb_arglist'],
+        '_check':isexternal,
+        '_depend':''
+    },
+    # Scalars (not complex)
+    {  # Common
+        'decl': '    #ctype# #varname# = 0;',
+        'pyobjfrom': {debugcapi: '    fprintf(stderr,"#vardebugshowvalue#\\n",#varname#);'},
+        'callfortran': {l_or(isintent_c, isattr_value): '#varname#,', l_not(l_or(isintent_c, isattr_value)): '&#varname#,'},
+        'return': {isintent_out: ',#varname#'},
+        '_check': l_and(isscalar, l_not(iscomplex))
+    }, {
+        'need': {hasinitvalue: 'math.h'},
+        '_check': l_and(isscalar, l_not(iscomplex)),
+    }, {  # Not hidden
+        'decl': '    PyObject *#varname#_capi = Py_None;',
+        'argformat': {isrequired: 'O'},
+        'keyformat': {isoptional: 'O'},
+        'args_capi': {isrequired: ',&#varname#_capi'},
+        'keys_capi': {isoptional: ',&#varname#_capi'},
+        'pyobjfrom': {isintent_inout: """\
+    f2py_success = try_pyarr_from_#ctype#(#varname#_capi,&#varname#);
+    if (f2py_success) {"""},
+        'closepyobjfrom': {isintent_inout: "    } /*if (f2py_success) of #varname# pyobjfrom*/"},
+        'need': {isintent_inout: 'try_pyarr_from_#ctype#'},
+        '_check': l_and(isscalar, l_not(iscomplex), l_not(isstring),
+                        isintent_nothide)
+    }, {
+        'frompyobj': [
+            # hasinitvalue...
+            #   if pyobj is None:
+            #     varname = init
+            #   else
+            #     from_pyobj(varname)
+            #
+            # isoptional and noinitvalue...
+            #   if pyobj is not None:
+            #     from_pyobj(varname)
+            #   else:
+            #     varname is uninitialized
+            #
+            # ...
+            #   from_pyobj(varname)
+            #
+            {hasinitvalue: '    if (#varname#_capi == Py_None) #varname# = #init#; else',
+             '_depend': ''},
+            {l_and(isoptional, l_not(hasinitvalue)): '    if (#varname#_capi != Py_None)',
+             '_depend': ''},
+            {l_not(islogical): '''\
+        f2py_success = #ctype#_from_pyobj(&#varname#,#varname#_capi,"#pyname#() #nth# (#varname#) can\'t be converted to #ctype#");
+    if (f2py_success) {'''},
+            {islogical: '''\
+        #varname# = (#ctype#)PyObject_IsTrue(#varname#_capi);
+        f2py_success = 1;
+    if (f2py_success) {'''},
+        ],
+        'cleanupfrompyobj': '    } /*if (f2py_success) of #varname#*/',
+        'need': {l_not(islogical): '#ctype#_from_pyobj'},
+        '_check': l_and(isscalar, l_not(iscomplex), isintent_nothide),
+        '_depend': ''
+    }, {  # Hidden
+        'frompyobj': {hasinitvalue: '    #varname# = #init#;'},
+        'need': typedef_need_dict,
+        '_check': l_and(isscalar, l_not(iscomplex), isintent_hide),
+        '_depend': ''
+    }, {  # Common
+        'frompyobj': {debugcapi: '    fprintf(stderr,"#vardebugshowvalue#\\n",#varname#);'},
+        '_check': l_and(isscalar, l_not(iscomplex)),
+        '_depend': ''
+    },
+    # Complex scalars
+    {  # Common
+        'decl': '    #ctype# #varname#;',
+        'callfortran': {isintent_c: '#varname#,', l_not(isintent_c): '&#varname#,'},
+        'pyobjfrom': {debugcapi: '    fprintf(stderr,"#vardebugshowvalue#\\n",#varname#.r,#varname#.i);'},
+        'return': {isintent_out: ',#varname#_capi'},
+        '_check': iscomplex
+    }, {  # Not hidden
+        'decl': '    PyObject *#varname#_capi = Py_None;',
+        'argformat': {isrequired: 'O'},
+        'keyformat': {isoptional: 'O'},
+        'args_capi': {isrequired: ',&#varname#_capi'},
+        'keys_capi': {isoptional: ',&#varname#_capi'},
+        'need': {isintent_inout: 'try_pyarr_from_#ctype#'},
+        'pyobjfrom': {isintent_inout: """\
+        f2py_success = try_pyarr_from_#ctype#(#varname#_capi,&#varname#);
+        if (f2py_success) {"""},
+        'closepyobjfrom': {isintent_inout: "        } /*if (f2py_success) of #varname# pyobjfrom*/"},
+        '_check': l_and(iscomplex, isintent_nothide)
+    }, {
+        'frompyobj': [{hasinitvalue: '    if (#varname#_capi==Py_None) {#varname#.r = #init.r#, #varname#.i = #init.i#;} else'},
+                      {l_and(isoptional, l_not(hasinitvalue))
+                             : '    if (#varname#_capi != Py_None)'},
+                      '        f2py_success = #ctype#_from_pyobj(&#varname#,#varname#_capi,"#pyname#() #nth# (#varname#) can\'t be converted to #ctype#");'
+                      '\n    if (f2py_success) {'],
+        'cleanupfrompyobj': '    }  /*if (f2py_success) of #varname# frompyobj*/',
+        'need': ['#ctype#_from_pyobj'],
+        '_check': l_and(iscomplex, isintent_nothide),
+        '_depend': ''
+    }, {  # Hidden
+        'decl': {isintent_out: '    PyObject *#varname#_capi = Py_None;'},
+        '_check': l_and(iscomplex, isintent_hide)
+    }, {
+        'frompyobj': {hasinitvalue: '    #varname#.r = #init.r#, #varname#.i = #init.i#;'},
+        '_check': l_and(iscomplex, isintent_hide),
+        '_depend': ''
+    }, {  # Common
+        'pyobjfrom': {isintent_out: '    #varname#_capi = pyobj_from_#ctype#1(#varname#);'},
+        'need': ['pyobj_from_#ctype#1'],
+        '_check': iscomplex
+    }, {
+        'frompyobj': {debugcapi: '    fprintf(stderr,"#vardebugshowvalue#\\n",#varname#.r,#varname#.i);'},
+        '_check': iscomplex,
+        '_depend': ''
+    },
+    # String
+    {  # Common
+        'decl': ['    #ctype# #varname# = NULL;',
+                 '    int slen(#varname#);',
+                 '    PyObject *#varname#_capi = Py_None;'],
+        'callfortran':'#varname#,',
+        'callfortranappend':'slen(#varname#),',
+        'pyobjfrom':[
+            {debugcapi:
+             '    fprintf(stderr,'
+             '"#vardebugshowvalue#\\n",slen(#varname#),#varname#);'},
+            # The trailing null value for Fortran is blank.
+            {l_and(isintent_out, l_not(isintent_c)):
+             "        STRINGPADN(#varname#, slen(#varname#), ' ', '\\0');"},
+        ],
+        'return': {isintent_out: ',#varname#'},
+        'need': ['len..',
+                 {l_and(isintent_out, l_not(isintent_c)): 'STRINGPADN'}],
+        '_check': isstring
+    }, {  # Common
+        'frompyobj': [
+            """\
+    slen(#varname#) = #elsize#;
+    f2py_success = #ctype#_from_pyobj(&#varname#,&slen(#varname#),#init#,"""
+"""#varname#_capi,\"#ctype#_from_pyobj failed in converting #nth#"""
+"""`#varname#\' of #pyname# to C #ctype#\");
+    if (f2py_success) {""",
+            # The trailing null value for Fortran is blank.
+            {l_not(isintent_c):
+             "        STRINGPADN(#varname#, slen(#varname#), '\\0', ' ');"},
+        ],
+        'cleanupfrompyobj': """\
+        STRINGFREE(#varname#);
+    }  /*if (f2py_success) of #varname#*/""",
+        'need': ['#ctype#_from_pyobj', 'len..', 'STRINGFREE',
+                 {l_not(isintent_c): 'STRINGPADN'}],
+        '_check':isstring,
+        '_depend':''
+    }, {  # Not hidden
+        'argformat': {isrequired: 'O'},
+        'keyformat': {isoptional: 'O'},
+        'args_capi': {isrequired: ',&#varname#_capi'},
+        'keys_capi': {isoptional: ',&#varname#_capi'},
+        'pyobjfrom': [
+            {l_and(isintent_inout, l_not(isintent_c)):
+             "        STRINGPADN(#varname#, slen(#varname#), ' ', '\\0');"},
+            {isintent_inout: '''\
+    f2py_success = try_pyarr_from_#ctype#(#varname#_capi, #varname#,
+                                          slen(#varname#));
+    if (f2py_success) {'''}],
+        'closepyobjfrom': {isintent_inout: '    } /*if (f2py_success) of #varname# pyobjfrom*/'},
+        'need': {isintent_inout: 'try_pyarr_from_#ctype#',
+                 l_and(isintent_inout, l_not(isintent_c)): 'STRINGPADN'},
+        '_check': l_and(isstring, isintent_nothide)
+    }, {  # Hidden
+        '_check': l_and(isstring, isintent_hide)
+    }, {
+        'frompyobj': {debugcapi: '    fprintf(stderr,"#vardebugshowvalue#\\n",slen(#varname#),#varname#);'},
+        '_check': isstring,
+        '_depend': ''
+    },
+    # Array
+    {  # Common
+        'decl': ['    #ctype# *#varname# = NULL;',
+                 '    npy_intp #varname#_Dims[#rank#] = {#rank*[-1]#};',
+                 '    const int #varname#_Rank = #rank#;',
+                 '    PyArrayObject *capi_#varname#_as_array = NULL;',
+                 '    int capi_#varname#_intent = 0;',
+                 {isstringarray: '    int slen(#varname#) = 0;'},
+                 ],
+        'callfortran':'#varname#,',
+        'callfortranappend': {isstringarray: 'slen(#varname#),'},
+        'return': {isintent_out: ',capi_#varname#_as_array'},
+        'need': 'len..',
+        '_check': isarray
+    }, {  # intent(overwrite) array
+        'decl': '    int capi_overwrite_#varname# = 1;',
+        'kwlistxa': '"overwrite_#varname#",',
+        'xaformat': 'i',
+        'keys_xa': ',&capi_overwrite_#varname#',
+        'docsignxa': 'overwrite_#varname#=1,',
+        'docsignxashort': 'overwrite_#varname#,',
+        'docstropt': 'overwrite_#varname# : input int, optional\\n    Default: 1',
+        '_check': l_and(isarray, isintent_overwrite),
+    }, {
+        'frompyobj': '    capi_#varname#_intent |= (capi_overwrite_#varname#?0:F2PY_INTENT_COPY);',
+        '_check': l_and(isarray, isintent_overwrite),
+        '_depend': '',
+    },
+    {  # intent(copy) array
+        'decl': '    int capi_overwrite_#varname# = 0;',
+        'kwlistxa': '"overwrite_#varname#",',
+        'xaformat': 'i',
+        'keys_xa': ',&capi_overwrite_#varname#',
+        'docsignxa': 'overwrite_#varname#=0,',
+        'docsignxashort': 'overwrite_#varname#,',
+        'docstropt': 'overwrite_#varname# : input int, optional\\n    Default: 0',
+        '_check': l_and(isarray, isintent_copy),
+    }, {
+        'frompyobj': '    capi_#varname#_intent |= (capi_overwrite_#varname#?0:F2PY_INTENT_COPY);',
+        '_check': l_and(isarray, isintent_copy),
+        '_depend': '',
+    }, {
+        'need': [{hasinitvalue: 'forcomb'}, {hasinitvalue: 'CFUNCSMESS'}],
+        '_check': isarray,
+        '_depend': ''
+    }, {  # Not hidden
+        'decl': '    PyObject *#varname#_capi = Py_None;',
+        'argformat': {isrequired: 'O'},
+        'keyformat': {isoptional: 'O'},
+        'args_capi': {isrequired: ',&#varname#_capi'},
+        'keys_capi': {isoptional: ',&#varname#_capi'},
+        '_check': l_and(isarray, isintent_nothide)
+    }, {
+        'frompyobj': [
+            '    #setdims#;',
+            '    capi_#varname#_intent |= #intent#;',
+            ('    const char * capi_errmess = "#modulename#.#pyname#:'
+             ' failed to create array from the #nth# `#varname#`";'),
+            {isintent_hide:
+             '    capi_#varname#_as_array = ndarray_from_pyobj('
+             '  #atype#,#elsize#,#varname#_Dims,#varname#_Rank,'
+             '  capi_#varname#_intent,Py_None,capi_errmess);'},
+            {isintent_nothide:
+             '    capi_#varname#_as_array = ndarray_from_pyobj('
+             '  #atype#,#elsize#,#varname#_Dims,#varname#_Rank,'
+             '  capi_#varname#_intent,#varname#_capi,capi_errmess);'},
+            """\
+    if (capi_#varname#_as_array == NULL) {
+        PyObject* capi_err = PyErr_Occurred();
+        if (capi_err == NULL) {
+            capi_err = #modulename#_error;
+            PyErr_SetString(capi_err, capi_errmess);
+        }
+    } else {
+        #varname# = (#ctype# *)(PyArray_DATA(capi_#varname#_as_array));
+""",
+            {isstringarray:
+             '    slen(#varname#) = f2py_itemsize(#varname#);'},
+            {hasinitvalue: [
+                {isintent_nothide:
+                 '    if (#varname#_capi == Py_None) {'},
+                {isintent_hide: '    {'},
+                {iscomplexarray: '        #ctype# capi_c;'},
+                """\
+        int *_i,capi_i=0;
+        CFUNCSMESS(\"#name#: Initializing #varname#=#init#\\n\");
+        if (initforcomb(PyArray_DIMS(capi_#varname#_as_array),
+                        PyArray_NDIM(capi_#varname#_as_array),1)) {
+            while ((_i = nextforcomb()))
+                #varname#[capi_i++] = #init#; /* fortran way */
+        } else {
+            PyObject *exc, *val, *tb;
+            PyErr_Fetch(&exc, &val, &tb);
+            PyErr_SetString(exc ? exc : #modulename#_error,
+                \"Initialization of #nth# #varname# failed (initforcomb).\");
+            npy_PyErr_ChainExceptionsCause(exc, val, tb);
+            f2py_success = 0;
+        }
+    }
+    if (f2py_success) {"""]},
+                      ],
+        'cleanupfrompyobj': [  # note that this list will be reversed
+            '    }  '
+            '/* if (capi_#varname#_as_array == NULL) ... else of #varname# */',
+            {l_not(l_or(isintent_out, isintent_hide)): """\
+    if((PyObject *)capi_#varname#_as_array!=#varname#_capi) {
+        Py_XDECREF(capi_#varname#_as_array); }"""},
+            {l_and(isintent_hide, l_not(isintent_out))
+                   : """        Py_XDECREF(capi_#varname#_as_array);"""},
+            {hasinitvalue: '    }  /*if (f2py_success) of #varname# init*/'},
+        ],
+        '_check': isarray,
+        '_depend': ''
+    },
+    # Scalararray
+    {  # Common
+        '_check': l_and(isarray, l_not(iscomplexarray))
+    }, {  # Not hidden
+        '_check': l_and(isarray, l_not(iscomplexarray), isintent_nothide)
+    },
+    # Integer*1 array
+    {'need': '#ctype#',
+     '_check': isint1array,
+     '_depend': ''
+     },
+    # Integer*-1 array
+    {'need': '#ctype#',
+     '_check': isunsigned_chararray,
+     '_depend': ''
+     },
+    # Integer*-2 array
+    {'need': '#ctype#',
+     '_check': isunsigned_shortarray,
+     '_depend': ''
+     },
+    # Integer*-8 array
+    {'need': '#ctype#',
+     '_check': isunsigned_long_longarray,
+     '_depend': ''
+     },
+    # Complexarray
+    {'need': '#ctype#',
+     '_check': iscomplexarray,
+     '_depend': ''
+     },
+    # Character
+    {
+        'need': 'string',
+        '_check': ischaracter,
+    },
+    # Character array
+    {
+        'need': 'string',
+        '_check': ischaracterarray,
+    },
+    # Stringarray
+    {
+        'callfortranappend': {isarrayofstrings: 'flen(#varname#),'},
+        'need': 'string',
+        '_check': isstringarray
+    }
+]
+
+################# Rules for checking ###############
+
+check_rules = [
+    {
+        'frompyobj': {debugcapi: '    fprintf(stderr,\"debug-capi:Checking `#check#\'\\n\");'},
+        'need': 'len..'
+    }, {
+        'frompyobj': '    CHECKSCALAR(#check#,\"#check#\",\"#nth# #varname#\",\"#varshowvalue#\",#varname#) {',
+        'cleanupfrompyobj': '    } /*CHECKSCALAR(#check#)*/',
+        'need': 'CHECKSCALAR',
+        '_check': l_and(isscalar, l_not(iscomplex)),
+        '_break': ''
+    }, {
+        'frompyobj': '    CHECKSTRING(#check#,\"#check#\",\"#nth# #varname#\",\"#varshowvalue#\",#varname#) {',
+        'cleanupfrompyobj': '    } /*CHECKSTRING(#check#)*/',
+        'need': 'CHECKSTRING',
+        '_check': isstring,
+        '_break': ''
+    }, {
+        'need': 'CHECKARRAY',
+        'frompyobj': '    CHECKARRAY(#check#,\"#check#\",\"#nth# #varname#\") {',
+        'cleanupfrompyobj': '    } /*CHECKARRAY(#check#)*/',
+        '_check': isarray,
+        '_break': ''
+    }, {
+        'need': 'CHECKGENERIC',
+        'frompyobj': '    CHECKGENERIC(#check#,\"#check#\",\"#nth# #varname#\") {',
+        'cleanupfrompyobj': '    } /*CHECKGENERIC(#check#)*/',
+    }
+]
+
+########## Applying the rules. No need to modify what follows #############
+
+#################### Build C/API module #######################
+
+
+def buildmodule(m, um):
+    """
+    Return
+    """
+    outmess('    Building module "%s"...\n' % (m['name']))
+    ret = {}
+    mod_rules = defmod_rules[:]
+    vrd = capi_maps.modsign2map(m)
+    rd = dictappend({'f2py_version': f2py_version}, vrd)
+    funcwrappers = []
+    funcwrappers2 = []  # F90 codes
+    for n in m['interfaced']:
+        nb = None
+        for bi in m['body']:
+            if bi['block'] not in ['interface', 'abstract interface']:
+                errmess('buildmodule: Expected interface block. Skipping.\n')
+                continue
+            for b in bi['body']:
+                if b['name'] == n:
+                    nb = b
+                    break
+
+        if not nb:
+            print(
+                'buildmodule: Could not find the body of interfaced routine "%s". Skipping.\n' % (n), file=sys.stderr)
+            continue
+        nb_list = [nb]
+        if 'entry' in nb:
+            for k, a in nb['entry'].items():
+                nb1 = copy.deepcopy(nb)
+                del nb1['entry']
+                nb1['name'] = k
+                nb1['args'] = a
+                nb_list.append(nb1)
+        for nb in nb_list:
+            # requiresf90wrapper must be called before buildapi as it
+            # rewrites assumed shape arrays as automatic arrays.
+            isf90 = requiresf90wrapper(nb)
+            # options is in scope here
+            if options['emptygen']:
+                b_path = options['buildpath']
+                m_name = vrd['modulename']
+                outmess('    Generating possibly empty wrappers"\n')
+                Path(f"{b_path}/{vrd['coutput']}").touch()
+                if isf90:
+                    # f77 + f90 wrappers
+                    outmess(f'    Maybe empty "{m_name}-f2pywrappers2.f90"\n')
+                    Path(f'{b_path}/{m_name}-f2pywrappers2.f90').touch()
+                    outmess(f'    Maybe empty "{m_name}-f2pywrappers.f"\n')
+                    Path(f'{b_path}/{m_name}-f2pywrappers.f').touch()
+                else:
+                    # only f77 wrappers
+                    outmess(f'    Maybe empty "{m_name}-f2pywrappers.f"\n')
+                    Path(f'{b_path}/{m_name}-f2pywrappers.f').touch()
+            api, wrap = buildapi(nb)
+            if wrap:
+                if isf90:
+                    funcwrappers2.append(wrap)
+                else:
+                    funcwrappers.append(wrap)
+            ar = applyrules(api, vrd)
+            rd = dictappend(rd, ar)
+
+    # Construct COMMON block support
+    cr, wrap = common_rules.buildhooks(m)
+    if wrap:
+        funcwrappers.append(wrap)
+    ar = applyrules(cr, vrd)
+    rd = dictappend(rd, ar)
+
+    # Construct F90 module support
+    mr, wrap = f90mod_rules.buildhooks(m)
+    if wrap:
+        funcwrappers2.append(wrap)
+    ar = applyrules(mr, vrd)
+    rd = dictappend(rd, ar)
+
+    for u in um:
+        ar = use_rules.buildusevars(u, m['use'][u['name']])
+        rd = dictappend(rd, ar)
+
+    needs = cfuncs.get_needs()
+    # Add mapped definitions
+    needs['typedefs'] += [cvar for cvar in capi_maps.f2cmap_mapped #
+                          if cvar in typedef_need_dict.values()]
+    code = {}
+    for n in needs.keys():
+        code[n] = []
+        for k in needs[n]:
+            c = ''
+            if k in cfuncs.includes0:
+                c = cfuncs.includes0[k]
+            elif k in cfuncs.includes:
+                c = cfuncs.includes[k]
+            elif k in cfuncs.userincludes:
+                c = cfuncs.userincludes[k]
+            elif k in cfuncs.typedefs:
+                c = cfuncs.typedefs[k]
+            elif k in cfuncs.typedefs_generated:
+                c = cfuncs.typedefs_generated[k]
+            elif k in cfuncs.cppmacros:
+                c = cfuncs.cppmacros[k]
+            elif k in cfuncs.cfuncs:
+                c = cfuncs.cfuncs[k]
+            elif k in cfuncs.callbacks:
+                c = cfuncs.callbacks[k]
+            elif k in cfuncs.f90modhooks:
+                c = cfuncs.f90modhooks[k]
+            elif k in cfuncs.commonhooks:
+                c = cfuncs.commonhooks[k]
+            else:
+                errmess('buildmodule: unknown need %s.\n' % (repr(k)))
+                continue
+            code[n].append(c)
+    mod_rules.append(code)
+    for r in mod_rules:
+        if ('_check' in r and r['_check'](m)) or ('_check' not in r):
+            ar = applyrules(r, vrd, m)
+            rd = dictappend(rd, ar)
+    ar = applyrules(module_rules, rd)
+
+    fn = os.path.join(options['buildpath'], vrd['coutput'])
+    ret['csrc'] = fn
+    with open(fn, 'w') as f:
+        f.write(ar['modulebody'].replace('\t', 2 * ' '))
+    outmess('    Wrote C/API module "%s" to file "%s"\n' % (m['name'], fn))
+
+    if options['dorestdoc']:
+        fn = os.path.join(
+            options['buildpath'], vrd['modulename'] + 'module.rest')
+        with open(fn, 'w') as f:
+            f.write('.. -*- rest -*-\n')
+            f.write('\n'.join(ar['restdoc']))
+        outmess('    ReST Documentation is saved to file "%s/%smodule.rest"\n' %
+                (options['buildpath'], vrd['modulename']))
+    if options['dolatexdoc']:
+        fn = os.path.join(
+            options['buildpath'], vrd['modulename'] + 'module.tex')
+        ret['ltx'] = fn
+        with open(fn, 'w') as f:
+            f.write(
+                '%% This file is auto-generated with f2py (version:%s)\n' % (f2py_version))
+            if 'shortlatex' not in options:
+                f.write(
+                    '\\documentclass{article}\n\\usepackage{a4wide}\n\\begin{document}\n\\tableofcontents\n\n')
+                f.write('\n'.join(ar['latexdoc']))
+            if 'shortlatex' not in options:
+                f.write('\\end{document}')
+        outmess('    Documentation is saved to file "%s/%smodule.tex"\n' %
+                (options['buildpath'], vrd['modulename']))
+    if funcwrappers:
+        wn = os.path.join(options['buildpath'], vrd['f2py_wrapper_output'])
+        ret['fsrc'] = wn
+        with open(wn, 'w') as f:
+            f.write('C     -*- fortran -*-\n')
+            f.write(
+                'C     This file is autogenerated with f2py (version:%s)\n' % (f2py_version))
+            f.write(
+                'C     It contains Fortran 77 wrappers to fortran functions.\n')
+            lines = []
+            for l in ('\n\n'.join(funcwrappers) + '\n').split('\n'):
+                if 0 <= l.find('!') < 66:
+                    # don't split comment lines
+                    lines.append(l + '\n')
+                elif l and l[0] == ' ':
+                    while len(l) >= 66:
+                        lines.append(l[:66] + '\n     &')
+                        l = l[66:]
+                    lines.append(l + '\n')
+                else:
+                    lines.append(l + '\n')
+            lines = ''.join(lines).replace('\n     &\n', '\n')
+            f.write(lines)
+        outmess('    Fortran 77 wrappers are saved to "%s"\n' % (wn))
+    if funcwrappers2:
+        wn = os.path.join(
+            options['buildpath'], '%s-f2pywrappers2.f90' % (vrd['modulename']))
+        ret['fsrc'] = wn
+        with open(wn, 'w') as f:
+            f.write('!     -*- f90 -*-\n')
+            f.write(
+                '!     This file is autogenerated with f2py (version:%s)\n' % (f2py_version))
+            f.write(
+                '!     It contains Fortran 90 wrappers to fortran functions.\n')
+            lines = []
+            for l in ('\n\n'.join(funcwrappers2) + '\n').split('\n'):
+                if 0 <= l.find('!') < 72:
+                    # don't split comment lines
+                    lines.append(l + '\n')
+                elif len(l) > 72 and l[0] == ' ':
+                    lines.append(l[:72] + '&\n     &')
+                    l = l[72:]
+                    while len(l) > 66:
+                        lines.append(l[:66] + '&\n     &')
+                        l = l[66:]
+                    lines.append(l + '\n')
+                else:
+                    lines.append(l + '\n')
+            lines = ''.join(lines).replace('\n     &\n', '\n')
+            f.write(lines)
+        outmess('    Fortran 90 wrappers are saved to "%s"\n' % (wn))
+    return ret
+
+################## Build C/API function #############
+
+stnd = {1: 'st', 2: 'nd', 3: 'rd', 4: 'th', 5: 'th',
+        6: 'th', 7: 'th', 8: 'th', 9: 'th', 0: 'th'}
+
+
+def buildapi(rout):
+    rout, wrap = func2subr.assubr(rout)
+    args, depargs = getargs2(rout)
+    capi_maps.depargs = depargs
+    var = rout['vars']
+
+    if ismoduleroutine(rout):
+        outmess('            Constructing wrapper function "%s.%s"...\n' %
+                (rout['modulename'], rout['name']))
+    else:
+        outmess('        Constructing wrapper function "%s"...\n' % (rout['name']))
+    # Routine
+    vrd = capi_maps.routsign2map(rout)
+    rd = dictappend({}, vrd)
+    for r in rout_rules:
+        if ('_check' in r and r['_check'](rout)) or ('_check' not in r):
+            ar = applyrules(r, vrd, rout)
+            rd = dictappend(rd, ar)
+
+    # Args
+    nth, nthk = 0, 0
+    savevrd = {}
+    for a in args:
+        vrd = capi_maps.sign2map(a, var[a])
+        if isintent_aux(var[a]):
+            _rules = aux_rules
+        else:
+            _rules = arg_rules
+            if not isintent_hide(var[a]):
+                if not isoptional(var[a]):
+                    nth = nth + 1
+                    vrd['nth'] = repr(nth) + stnd[nth % 10] + ' argument'
+                else:
+                    nthk = nthk + 1
+                    vrd['nth'] = repr(nthk) + stnd[nthk % 10] + ' keyword'
+            else:
+                vrd['nth'] = 'hidden'
+        savevrd[a] = vrd
+        for r in _rules:
+            if '_depend' in r:
+                continue
+            if ('_check' in r and r['_check'](var[a])) or ('_check' not in r):
+                ar = applyrules(r, vrd, var[a])
+                rd = dictappend(rd, ar)
+                if '_break' in r:
+                    break
+    for a in depargs:
+        if isintent_aux(var[a]):
+            _rules = aux_rules
+        else:
+            _rules = arg_rules
+        vrd = savevrd[a]
+        for r in _rules:
+            if '_depend' not in r:
+                continue
+            if ('_check' in r and r['_check'](var[a])) or ('_check' not in r):
+                ar = applyrules(r, vrd, var[a])
+                rd = dictappend(rd, ar)
+                if '_break' in r:
+                    break
+        if 'check' in var[a]:
+            for c in var[a]['check']:
+                vrd['check'] = c
+                ar = applyrules(check_rules, vrd, var[a])
+                rd = dictappend(rd, ar)
+    if isinstance(rd['cleanupfrompyobj'], list):
+        rd['cleanupfrompyobj'].reverse()
+    if isinstance(rd['closepyobjfrom'], list):
+        rd['closepyobjfrom'].reverse()
+    rd['docsignature'] = stripcomma(replace('#docsign##docsignopt##docsignxa#',
+                                            {'docsign': rd['docsign'],
+                                             'docsignopt': rd['docsignopt'],
+                                             'docsignxa': rd['docsignxa']}))
+    optargs = stripcomma(replace('#docsignopt##docsignxa#',
+                                 {'docsignxa': rd['docsignxashort'],
+                                  'docsignopt': rd['docsignoptshort']}
+                                 ))
+    if optargs == '':
+        rd['docsignatureshort'] = stripcomma(
+            replace('#docsign#', {'docsign': rd['docsign']}))
+    else:
+        rd['docsignatureshort'] = replace('#docsign#[#docsignopt#]',
+                                          {'docsign': rd['docsign'],
+                                           'docsignopt': optargs,
+                                           })
+    rd['latexdocsignatureshort'] = rd['docsignatureshort'].replace('_', '\\_')
+    rd['latexdocsignatureshort'] = rd[
+        'latexdocsignatureshort'].replace(',', ', ')
+    cfs = stripcomma(replace('#callfortran##callfortranappend#', {
+                     'callfortran': rd['callfortran'], 'callfortranappend': rd['callfortranappend']}))
+    if len(rd['callfortranappend']) > 1:
+        rd['callcompaqfortran'] = stripcomma(replace('#callfortran# 0,#callfortranappend#', {
+                                             'callfortran': rd['callfortran'], 'callfortranappend': rd['callfortranappend']}))
+    else:
+        rd['callcompaqfortran'] = cfs
+    rd['callfortran'] = cfs
+    if isinstance(rd['docreturn'], list):
+        rd['docreturn'] = stripcomma(
+            replace('#docreturn#', {'docreturn': rd['docreturn']})) + ' = '
+    rd['docstrsigns'] = []
+    rd['latexdocstrsigns'] = []
+    for k in ['docstrreq', 'docstropt', 'docstrout', 'docstrcbs']:
+        if k in rd and isinstance(rd[k], list):
+            rd['docstrsigns'] = rd['docstrsigns'] + rd[k]
+        k = 'latex' + k
+        if k in rd and isinstance(rd[k], list):
+            rd['latexdocstrsigns'] = rd['latexdocstrsigns'] + rd[k][0:1] +\
+                ['\\begin{description}'] + rd[k][1:] +\
+                ['\\end{description}']
+
+    ar = applyrules(routine_rules, rd)
+    if ismoduleroutine(rout):
+        outmess('              %s\n' % (ar['docshort']))
+    else:
+        outmess('          %s\n' % (ar['docshort']))
+    return ar, wrap
+
+
+#################### EOF rules.py #######################
diff --git a/.venv/lib/python3.12/site-packages/numpy/f2py/setup.cfg b/.venv/lib/python3.12/site-packages/numpy/f2py/setup.cfg
new file mode 100644
index 00000000..14669544
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/numpy/f2py/setup.cfg
@@ -0,0 +1,3 @@
+[bdist_rpm]
+doc_files = docs/
+            tests/
\ No newline at end of file
diff --git a/.venv/lib/python3.12/site-packages/numpy/f2py/setup.py b/.venv/lib/python3.12/site-packages/numpy/f2py/setup.py
new file mode 100644
index 00000000..05bef300
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/numpy/f2py/setup.py
@@ -0,0 +1,74 @@
+#!/usr/bin/env python3
+"""
+setup.py for installing F2PY
+
+Usage:
+   pip install .
+
+Copyright 2001-2005 Pearu Peterson all rights reserved,
+Pearu Peterson <pearu@cens.ioc.ee>
+Permission to use, modify, and distribute this software is given under the
+terms of the NumPy License.
+
+NO WARRANTY IS EXPRESSED OR IMPLIED.  USE AT YOUR OWN RISK.
+$Revision: 1.32 $
+$Date: 2005/01/30 17:22:14 $
+Pearu Peterson
+
+"""
+from numpy.distutils.core import setup
+from numpy.distutils.misc_util import Configuration
+
+
+from __version__ import version
+
+
+def configuration(parent_package='', top_path=None):
+    config = Configuration('f2py', parent_package, top_path)
+    config.add_subpackage('tests')
+    config.add_subpackage('_backends')
+    config.add_data_dir('tests/src')
+    config.add_data_files(
+        'src/fortranobject.c',
+        'src/fortranobject.h',
+        '_backends/meson.build.template',
+    )
+    config.add_data_files('*.pyi')
+    return config
+
+
+if __name__ == "__main__":
+
+    config = configuration(top_path='')
+    config = config.todict()
+
+    config['classifiers'] = [
+        'Development Status :: 5 - Production/Stable',
+        'Intended Audience :: Developers',
+        'Intended Audience :: Science/Research',
+        'License :: OSI Approved :: NumPy License',
+        'Natural Language :: English',
+        'Operating System :: OS Independent',
+        'Programming Language :: C',
+        'Programming Language :: Fortran',
+        'Programming Language :: Python',
+        'Topic :: Scientific/Engineering',
+        'Topic :: Software Development :: Code Generators',
+    ]
+    setup(version=version,
+          description="F2PY - Fortran to Python Interface Generator",
+          author="Pearu Peterson",
+          author_email="pearu@cens.ioc.ee",
+          maintainer="Pearu Peterson",
+          maintainer_email="pearu@cens.ioc.ee",
+          license="BSD",
+          platforms="Unix, Windows (mingw|cygwin), Mac OSX",
+          long_description="""\
+The Fortran to Python Interface Generator, or F2PY for short, is a
+command line tool (f2py) for generating Python C/API modules for
+wrapping Fortran 77/90/95 subroutines, accessing common blocks from
+Python, and calling Python functions from Fortran (call-backs).
+Interfacing subroutines/data from Fortran 90/95 modules is supported.""",
+          url="https://numpy.org/doc/stable/f2py/",
+          keywords=['Fortran', 'f2py'],
+          **config)
diff --git a/.venv/lib/python3.12/site-packages/numpy/f2py/src/fortranobject.c b/.venv/lib/python3.12/site-packages/numpy/f2py/src/fortranobject.c
new file mode 100644
index 00000000..072392bb
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/numpy/f2py/src/fortranobject.c
@@ -0,0 +1,1423 @@
+#define FORTRANOBJECT_C
+#include "fortranobject.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <stdarg.h>
+#include <stdlib.h>
+#include <string.h>
+
+/*
+  This file implements: FortranObject, array_from_pyobj, copy_ND_array
+
+  Author: Pearu Peterson <pearu@cens.ioc.ee>
+  $Revision: 1.52 $
+  $Date: 2005/07/11 07:44:20 $
+*/
+
+int
+F2PyDict_SetItemString(PyObject *dict, char *name, PyObject *obj)
+{
+    if (obj == NULL) {
+        fprintf(stderr, "Error loading %s\n", name);
+        if (PyErr_Occurred()) {
+            PyErr_Print();
+            PyErr_Clear();
+        }
+        return -1;
+    }
+    return PyDict_SetItemString(dict, name, obj);
+}
+
+/*
+ * Python-only fallback for thread-local callback pointers
+ */
+void *
+F2PySwapThreadLocalCallbackPtr(char *key, void *ptr)
+{
+    PyObject *local_dict, *value;
+    void *prev;
+
+    local_dict = PyThreadState_GetDict();
+    if (local_dict == NULL) {
+        Py_FatalError(
+                "F2PySwapThreadLocalCallbackPtr: PyThreadState_GetDict "
+                "failed");
+    }
+
+    value = PyDict_GetItemString(local_dict, key);
+    if (value != NULL) {
+        prev = PyLong_AsVoidPtr(value);
+        if (PyErr_Occurred()) {
+            Py_FatalError(
+                    "F2PySwapThreadLocalCallbackPtr: PyLong_AsVoidPtr failed");
+        }
+    }
+    else {
+        prev = NULL;
+    }
+
+    value = PyLong_FromVoidPtr((void *)ptr);
+    if (value == NULL) {
+        Py_FatalError(
+                "F2PySwapThreadLocalCallbackPtr: PyLong_FromVoidPtr failed");
+    }
+
+    if (PyDict_SetItemString(local_dict, key, value) != 0) {
+        Py_FatalError(
+                "F2PySwapThreadLocalCallbackPtr: PyDict_SetItemString failed");
+    }
+
+    Py_DECREF(value);
+
+    return prev;
+}
+
+void *
+F2PyGetThreadLocalCallbackPtr(char *key)
+{
+    PyObject *local_dict, *value;
+    void *prev;
+
+    local_dict = PyThreadState_GetDict();
+    if (local_dict == NULL) {
+        Py_FatalError(
+                "F2PyGetThreadLocalCallbackPtr: PyThreadState_GetDict failed");
+    }
+
+    value = PyDict_GetItemString(local_dict, key);
+    if (value != NULL) {
+        prev = PyLong_AsVoidPtr(value);
+        if (PyErr_Occurred()) {
+            Py_FatalError(
+                    "F2PyGetThreadLocalCallbackPtr: PyLong_AsVoidPtr failed");
+        }
+    }
+    else {
+        prev = NULL;
+    }
+
+    return prev;
+}
+
+static PyArray_Descr *
+get_descr_from_type_and_elsize(const int type_num, const int elsize)  {
+  PyArray_Descr * descr = PyArray_DescrFromType(type_num);
+  if (type_num == NPY_STRING) {
+    // PyArray_DescrFromType returns descr with elsize = 0.
+    PyArray_DESCR_REPLACE(descr);
+    if (descr == NULL) {
+      return NULL;
+    }
+    descr->elsize = elsize;
+  }
+  return descr;
+}
+
+/************************* FortranObject *******************************/
+
+typedef PyObject *(*fortranfunc)(PyObject *, PyObject *, PyObject *, void *);
+
+PyObject *
+PyFortranObject_New(FortranDataDef *defs, f2py_void_func init)
+{
+    int i;
+    PyFortranObject *fp = NULL;
+    PyObject *v = NULL;
+    if (init != NULL) { /* Initialize F90 module objects */
+        (*(init))();
+    }
+    fp = PyObject_New(PyFortranObject, &PyFortran_Type);
+    if (fp == NULL) {
+        return NULL;
+    }
+    if ((fp->dict = PyDict_New()) == NULL) {
+        Py_DECREF(fp);
+        return NULL;
+    }
+    fp->len = 0;
+    while (defs[fp->len].name != NULL) {
+        fp->len++;
+    }
+    if (fp->len == 0) {
+        goto fail;
+    }
+    fp->defs = defs;
+    for (i = 0; i < fp->len; i++) {
+        if (fp->defs[i].rank == -1) { /* Is Fortran routine */
+            v = PyFortranObject_NewAsAttr(&(fp->defs[i]));
+            if (v == NULL) {
+                goto fail;
+            }
+            PyDict_SetItemString(fp->dict, fp->defs[i].name, v);
+            Py_XDECREF(v);
+        }
+        else if ((fp->defs[i].data) !=
+                 NULL) { /* Is Fortran variable or array (not allocatable) */
+            PyArray_Descr *
+            descr = get_descr_from_type_and_elsize(fp->defs[i].type,
+                                                   fp->defs[i].elsize);
+            if (descr == NULL) {
+                goto fail;
+            }
+            v = PyArray_NewFromDescr(&PyArray_Type, descr, fp->defs[i].rank,
+                                     fp->defs[i].dims.d, NULL, fp->defs[i].data,
+                                     NPY_ARRAY_FARRAY, NULL);
+            if (v == NULL) {
+                Py_DECREF(descr);
+                goto fail;
+            }
+            PyDict_SetItemString(fp->dict, fp->defs[i].name, v);
+            Py_XDECREF(v);
+        }
+    }
+    return (PyObject *)fp;
+fail:
+    Py_XDECREF(fp);
+    return NULL;
+}
+
+PyObject *
+PyFortranObject_NewAsAttr(FortranDataDef *defs)
+{ /* used for calling F90 module routines */
+    PyFortranObject *fp = NULL;
+    fp = PyObject_New(PyFortranObject, &PyFortran_Type);
+    if (fp == NULL)
+        return NULL;
+    if ((fp->dict = PyDict_New()) == NULL) {
+        PyObject_Del(fp);
+        return NULL;
+    }
+    fp->len = 1;
+    fp->defs = defs;
+    if (defs->rank == -1) {
+      PyDict_SetItemString(fp->dict, "__name__", PyUnicode_FromFormat("function %s", defs->name));
+    } else if (defs->rank == 0) {
+      PyDict_SetItemString(fp->dict, "__name__", PyUnicode_FromFormat("scalar %s", defs->name));
+    } else {
+      PyDict_SetItemString(fp->dict, "__name__", PyUnicode_FromFormat("array %s", defs->name));
+    }
+    return (PyObject *)fp;
+}
+
+/* Fortran methods */
+
+static void
+fortran_dealloc(PyFortranObject *fp)
+{
+    Py_XDECREF(fp->dict);
+    PyObject_Del(fp);
+}
+
+/* Returns number of bytes consumed from buf, or -1 on error. */
+static Py_ssize_t
+format_def(char *buf, Py_ssize_t size, FortranDataDef def)
+{
+    char *p = buf;
+    int i;
+    npy_intp n;
+
+    n = PyOS_snprintf(p, size, "array(%" NPY_INTP_FMT, def.dims.d[0]);
+    if (n < 0 || n >= size) {
+        return -1;
+    }
+    p += n;
+    size -= n;
+
+    for (i = 1; i < def.rank; i++) {
+        n = PyOS_snprintf(p, size, ",%" NPY_INTP_FMT, def.dims.d[i]);
+        if (n < 0 || n >= size) {
+            return -1;
+        }
+        p += n;
+        size -= n;
+    }
+
+    if (size <= 0) {
+        return -1;
+    }
+
+    *p++ = ')';
+    size--;
+
+    if (def.data == NULL) {
+        static const char notalloc[] = ", not allocated";
+        if ((size_t)size < sizeof(notalloc)) {
+            return -1;
+        }
+        memcpy(p, notalloc, sizeof(notalloc));
+        p += sizeof(notalloc);
+        size -= sizeof(notalloc);
+    }
+
+    return p - buf;
+}
+
+static PyObject *
+fortran_doc(FortranDataDef def)
+{
+    char *buf, *p;
+    PyObject *s = NULL;
+    Py_ssize_t n, origsize, size = 100;
+
+    if (def.doc != NULL) {
+        size += strlen(def.doc);
+    }
+    origsize = size;
+    buf = p = (char *)PyMem_Malloc(size);
+    if (buf == NULL) {
+        return PyErr_NoMemory();
+    }
+
+    if (def.rank == -1) {
+        if (def.doc) {
+            n = strlen(def.doc);
+            if (n > size) {
+                goto fail;
+            }
+            memcpy(p, def.doc, n);
+            p += n;
+            size -= n;
+        }
+        else {
+            n = PyOS_snprintf(p, size, "%s - no docs available", def.name);
+            if (n < 0 || n >= size) {
+                goto fail;
+            }
+            p += n;
+            size -= n;
+        }
+    }
+    else {
+        PyArray_Descr *d = PyArray_DescrFromType(def.type);
+        n = PyOS_snprintf(p, size, "%s : '%c'-", def.name, d->type);
+        Py_DECREF(d);
+        if (n < 0 || n >= size) {
+            goto fail;
+        }
+        p += n;
+        size -= n;
+
+        if (def.data == NULL) {
+            n = format_def(p, size, def);
+            if (n < 0) {
+                goto fail;
+            }
+            p += n;
+            size -= n;
+        }
+        else if (def.rank > 0) {
+            n = format_def(p, size, def);
+            if (n < 0) {
+                goto fail;
+            }
+            p += n;
+            size -= n;
+        }
+        else {
+            n = strlen("scalar");
+            if (size < n) {
+                goto fail;
+            }
+            memcpy(p, "scalar", n);
+            p += n;
+            size -= n;
+        }
+    }
+    if (size <= 1) {
+        goto fail;
+    }
+    *p++ = '\n';
+    size--;
+
+    /* p now points one beyond the last character of the string in buf */
+    s = PyUnicode_FromStringAndSize(buf, p - buf);
+
+    PyMem_Free(buf);
+    return s;
+
+fail:
+    fprintf(stderr,
+            "fortranobject.c: fortran_doc: len(p)=%zd>%zd=size:"
+            " too long docstring required, increase size\n",
+            p - buf, origsize);
+    PyMem_Free(buf);
+    return NULL;
+}
+
+static FortranDataDef *save_def; /* save pointer of an allocatable array */
+static void
+set_data(char *d, npy_intp *f)
+{           /* callback from Fortran */
+    if (*f) /* In fortran f=allocated(d) */
+        save_def->data = d;
+    else
+        save_def->data = NULL;
+    /* printf("set_data: d=%p,f=%d\n",d,*f); */
+}
+
+static PyObject *
+fortran_getattr(PyFortranObject *fp, char *name)
+{
+    int i, j, k, flag;
+    if (fp->dict != NULL) {
+        PyObject *v = _PyDict_GetItemStringWithError(fp->dict, name);
+        if (v == NULL && PyErr_Occurred()) {
+            return NULL;
+        }
+        else if (v != NULL) {
+            Py_INCREF(v);
+            return v;
+        }
+    }
+    for (i = 0, j = 1; i < fp->len && (j = strcmp(name, fp->defs[i].name));
+         i++)
+        ;
+    if (j == 0)
+        if (fp->defs[i].rank != -1) { /* F90 allocatable array */
+            if (fp->defs[i].func == NULL)
+                return NULL;
+            for (k = 0; k < fp->defs[i].rank; ++k) fp->defs[i].dims.d[k] = -1;
+            save_def = &fp->defs[i];
+            (*(fp->defs[i].func))(&fp->defs[i].rank, fp->defs[i].dims.d,
+                                  set_data, &flag);
+            if (flag == 2)
+                k = fp->defs[i].rank + 1;
+            else
+                k = fp->defs[i].rank;
+            if (fp->defs[i].data != NULL) { /* array is allocated */
+                PyObject *v = PyArray_New(
+                        &PyArray_Type, k, fp->defs[i].dims.d, fp->defs[i].type,
+                        NULL, fp->defs[i].data, 0, NPY_ARRAY_FARRAY, NULL);
+                if (v == NULL)
+                    return NULL;
+                /* Py_INCREF(v); */
+                return v;
+            }
+            else { /* array is not allocated */
+                Py_RETURN_NONE;
+            }
+        }
+    if (strcmp(name, "__dict__") == 0) {
+        Py_INCREF(fp->dict);
+        return fp->dict;
+    }
+    if (strcmp(name, "__doc__") == 0) {
+        PyObject *s = PyUnicode_FromString(""), *s2, *s3;
+        for (i = 0; i < fp->len; i++) {
+            s2 = fortran_doc(fp->defs[i]);
+            s3 = PyUnicode_Concat(s, s2);
+            Py_DECREF(s2);
+            Py_DECREF(s);
+            s = s3;
+        }
+        if (PyDict_SetItemString(fp->dict, name, s))
+            return NULL;
+        return s;
+    }
+    if ((strcmp(name, "_cpointer") == 0) && (fp->len == 1)) {
+        PyObject *cobj =
+                F2PyCapsule_FromVoidPtr((void *)(fp->defs[0].data), NULL);
+        if (PyDict_SetItemString(fp->dict, name, cobj))
+            return NULL;
+        return cobj;
+    }
+    PyObject *str, *ret;
+    str = PyUnicode_FromString(name);
+    ret = PyObject_GenericGetAttr((PyObject *)fp, str);
+    Py_DECREF(str);
+    return ret;
+}
+
+static int
+fortran_setattr(PyFortranObject *fp, char *name, PyObject *v)
+{
+    int i, j, flag;
+    PyArrayObject *arr = NULL;
+    for (i = 0, j = 1; i < fp->len && (j = strcmp(name, fp->defs[i].name));
+         i++)
+        ;
+    if (j == 0) {
+        if (fp->defs[i].rank == -1) {
+            PyErr_SetString(PyExc_AttributeError,
+                            "over-writing fortran routine");
+            return -1;
+        }
+        if (fp->defs[i].func != NULL) { /* is allocatable array */
+            npy_intp dims[F2PY_MAX_DIMS];
+            int k;
+            save_def = &fp->defs[i];
+            if (v != Py_None) { /* set new value (reallocate if needed --
+                                   see f2py generated code for more
+                                   details ) */
+                for (k = 0; k < fp->defs[i].rank; k++) dims[k] = -1;
+                if ((arr = array_from_pyobj(fp->defs[i].type, dims,
+                                            fp->defs[i].rank, F2PY_INTENT_IN,
+                                            v)) == NULL)
+                    return -1;
+                (*(fp->defs[i].func))(&fp->defs[i].rank, PyArray_DIMS(arr),
+                                      set_data, &flag);
+            }
+            else { /* deallocate */
+                for (k = 0; k < fp->defs[i].rank; k++) dims[k] = 0;
+                (*(fp->defs[i].func))(&fp->defs[i].rank, dims, set_data,
+                                      &flag);
+                for (k = 0; k < fp->defs[i].rank; k++) dims[k] = -1;
+            }
+            memcpy(fp->defs[i].dims.d, dims,
+                   fp->defs[i].rank * sizeof(npy_intp));
+        }
+        else { /* not allocatable array */
+            if ((arr = array_from_pyobj(fp->defs[i].type, fp->defs[i].dims.d,
+                                        fp->defs[i].rank, F2PY_INTENT_IN,
+                                        v)) == NULL)
+                return -1;
+        }
+        if (fp->defs[i].data !=
+            NULL) { /* copy Python object to Fortran array */
+            npy_intp s = PyArray_MultiplyList(fp->defs[i].dims.d,
+                                              PyArray_NDIM(arr));
+            if (s == -1)
+                s = PyArray_MultiplyList(PyArray_DIMS(arr), PyArray_NDIM(arr));
+            if (s < 0 || (memcpy(fp->defs[i].data, PyArray_DATA(arr),
+                                 s * PyArray_ITEMSIZE(arr))) == NULL) {
+                if ((PyObject *)arr != v) {
+                    Py_DECREF(arr);
+                }
+                return -1;
+            }
+            if ((PyObject *)arr != v) {
+                Py_DECREF(arr);
+            }
+        }
+        else
+            return (fp->defs[i].func == NULL ? -1 : 0);
+        return 0; /* successful */
+    }
+    if (fp->dict == NULL) {
+        fp->dict = PyDict_New();
+        if (fp->dict == NULL)
+            return -1;
+    }
+    if (v == NULL) {
+        int rv = PyDict_DelItemString(fp->dict, name);
+        if (rv < 0)
+            PyErr_SetString(PyExc_AttributeError,
+                            "delete non-existing fortran attribute");
+        return rv;
+    }
+    else
+        return PyDict_SetItemString(fp->dict, name, v);
+}
+
+static PyObject *
+fortran_call(PyFortranObject *fp, PyObject *arg, PyObject *kw)
+{
+    int i = 0;
+    /*  printf("fortran call
+        name=%s,func=%p,data=%p,%p\n",fp->defs[i].name,
+        fp->defs[i].func,fp->defs[i].data,&fp->defs[i].data); */
+    if (fp->defs[i].rank == -1) { /* is Fortran routine */
+        if (fp->defs[i].func == NULL) {
+            PyErr_Format(PyExc_RuntimeError, "no function to call");
+            return NULL;
+        }
+        else if (fp->defs[i].data == NULL)
+            /* dummy routine */
+            return (*((fortranfunc)(fp->defs[i].func)))((PyObject *)fp, arg,
+                                                        kw, NULL);
+        else
+            return (*((fortranfunc)(fp->defs[i].func)))(
+                    (PyObject *)fp, arg, kw, (void *)fp->defs[i].data);
+    }
+    PyErr_Format(PyExc_TypeError, "this fortran object is not callable");
+    return NULL;
+}
+
+static PyObject *
+fortran_repr(PyFortranObject *fp)
+{
+    PyObject *name = NULL, *repr = NULL;
+    name = PyObject_GetAttrString((PyObject *)fp, "__name__");
+    PyErr_Clear();
+    if (name != NULL && PyUnicode_Check(name)) {
+        repr = PyUnicode_FromFormat("<fortran %U>", name);
+    }
+    else {
+        repr = PyUnicode_FromString("<fortran object>");
+    }
+    Py_XDECREF(name);
+    return repr;
+}
+
+PyTypeObject PyFortran_Type = {
+        PyVarObject_HEAD_INIT(NULL, 0).tp_name = "fortran",
+        .tp_basicsize = sizeof(PyFortranObject),
+        .tp_dealloc = (destructor)fortran_dealloc,
+        .tp_getattr = (getattrfunc)fortran_getattr,
+        .tp_setattr = (setattrfunc)fortran_setattr,
+        .tp_repr = (reprfunc)fortran_repr,
+        .tp_call = (ternaryfunc)fortran_call,
+};
+
+/************************* f2py_report_atexit *******************************/
+
+#ifdef F2PY_REPORT_ATEXIT
+static int passed_time = 0;
+static int passed_counter = 0;
+static int passed_call_time = 0;
+static struct timeb start_time;
+static struct timeb stop_time;
+static struct timeb start_call_time;
+static struct timeb stop_call_time;
+static int cb_passed_time = 0;
+static int cb_passed_counter = 0;
+static int cb_passed_call_time = 0;
+static struct timeb cb_start_time;
+static struct timeb cb_stop_time;
+static struct timeb cb_start_call_time;
+static struct timeb cb_stop_call_time;
+
+extern void
+f2py_start_clock(void)
+{
+    ftime(&start_time);
+}
+extern void
+f2py_start_call_clock(void)
+{
+    f2py_stop_clock();
+    ftime(&start_call_time);
+}
+extern void
+f2py_stop_clock(void)
+{
+    ftime(&stop_time);
+    passed_time += 1000 * (stop_time.time - start_time.time);
+    passed_time += stop_time.millitm - start_time.millitm;
+}
+extern void
+f2py_stop_call_clock(void)
+{
+    ftime(&stop_call_time);
+    passed_call_time += 1000 * (stop_call_time.time - start_call_time.time);
+    passed_call_time += stop_call_time.millitm - start_call_time.millitm;
+    passed_counter += 1;
+    f2py_start_clock();
+}
+
+extern void
+f2py_cb_start_clock(void)
+{
+    ftime(&cb_start_time);
+}
+extern void
+f2py_cb_start_call_clock(void)
+{
+    f2py_cb_stop_clock();
+    ftime(&cb_start_call_time);
+}
+extern void
+f2py_cb_stop_clock(void)
+{
+    ftime(&cb_stop_time);
+    cb_passed_time += 1000 * (cb_stop_time.time - cb_start_time.time);
+    cb_passed_time += cb_stop_time.millitm - cb_start_time.millitm;
+}
+extern void
+f2py_cb_stop_call_clock(void)
+{
+    ftime(&cb_stop_call_time);
+    cb_passed_call_time +=
+            1000 * (cb_stop_call_time.time - cb_start_call_time.time);
+    cb_passed_call_time +=
+            cb_stop_call_time.millitm - cb_start_call_time.millitm;
+    cb_passed_counter += 1;
+    f2py_cb_start_clock();
+}
+
+static int f2py_report_on_exit_been_here = 0;
+extern void
+f2py_report_on_exit(int exit_flag, void *name)
+{
+    if (f2py_report_on_exit_been_here) {
+        fprintf(stderr, "             %s\n", (char *)name);
+        return;
+    }
+    f2py_report_on_exit_been_here = 1;
+    fprintf(stderr, "                      /-----------------------\\\n");
+    fprintf(stderr, "                     < F2PY performance report >\n");
+    fprintf(stderr, "                      \\-----------------------/\n");
+    fprintf(stderr, "Overall time spent in ...\n");
+    fprintf(stderr, "(a) wrapped (Fortran/C) functions           : %8d msec\n",
+            passed_call_time);
+    fprintf(stderr, "(b) f2py interface,           %6d calls  : %8d msec\n",
+            passed_counter, passed_time);
+    fprintf(stderr, "(c) call-back (Python) functions            : %8d msec\n",
+            cb_passed_call_time);
+    fprintf(stderr, "(d) f2py call-back interface, %6d calls  : %8d msec\n",
+            cb_passed_counter, cb_passed_time);
+
+    fprintf(stderr,
+            "(e) wrapped (Fortran/C) functions (actual) : %8d msec\n\n",
+            passed_call_time - cb_passed_call_time - cb_passed_time);
+    fprintf(stderr,
+            "Use -DF2PY_REPORT_ATEXIT_DISABLE to disable this message.\n");
+    fprintf(stderr, "Exit status: %d\n", exit_flag);
+    fprintf(stderr, "Modules    : %s\n", (char *)name);
+}
+#endif
+
+/********************** report on array copy ****************************/
+
+#ifdef F2PY_REPORT_ON_ARRAY_COPY
+static void
+f2py_report_on_array_copy(PyArrayObject *arr)
+{
+    const npy_intp arr_size = PyArray_Size((PyObject *)arr);
+    if (arr_size > F2PY_REPORT_ON_ARRAY_COPY) {
+        fprintf(stderr,
+                "copied an array: size=%ld, elsize=%" NPY_INTP_FMT "\n",
+                arr_size, (npy_intp)PyArray_ITEMSIZE(arr));
+    }
+}
+static void
+f2py_report_on_array_copy_fromany(void)
+{
+    fprintf(stderr, "created an array from object\n");
+}
+
+#define F2PY_REPORT_ON_ARRAY_COPY_FROMARR \
+    f2py_report_on_array_copy((PyArrayObject *)arr)
+#define F2PY_REPORT_ON_ARRAY_COPY_FROMANY f2py_report_on_array_copy_fromany()
+#else
+#define F2PY_REPORT_ON_ARRAY_COPY_FROMARR
+#define F2PY_REPORT_ON_ARRAY_COPY_FROMANY
+#endif
+
+/************************* array_from_obj *******************************/
+
+/*
+ * File: array_from_pyobj.c
+ *
+ * Description:
+ * ------------
+ * Provides array_from_pyobj function that returns a contiguous array
+ * object with the given dimensions and required storage order, either
+ * in row-major (C) or column-major (Fortran) order. The function
+ * array_from_pyobj is very flexible about its Python object argument
+ * that can be any number, list, tuple, or array.
+ *
+ * array_from_pyobj is used in f2py generated Python extension
+ * modules.
+ *
+ * Author: Pearu Peterson <pearu@cens.ioc.ee>
+ * Created: 13-16 January 2002
+ * $Id: fortranobject.c,v 1.52 2005/07/11 07:44:20 pearu Exp $
+ */
+
+static int check_and_fix_dimensions(const PyArrayObject* arr,
+                                    const int rank,
+                                    npy_intp *dims,
+                                    const char *errmess);
+
+static int
+find_first_negative_dimension(const int rank, const npy_intp *dims)
+{
+    int i;
+    for (i = 0; i < rank; ++i) {
+        if (dims[i] < 0) {
+            return i;
+        }
+    }
+    return -1;
+}
+
+#ifdef DEBUG_COPY_ND_ARRAY
+void
+dump_dims(int rank, npy_intp const *dims)
+{
+    int i;
+    printf("[");
+    for (i = 0; i < rank; ++i) {
+        printf("%3" NPY_INTP_FMT, dims[i]);
+    }
+    printf("]\n");
+}
+void
+dump_attrs(const PyArrayObject *obj)
+{
+    const PyArrayObject_fields *arr = (const PyArrayObject_fields *)obj;
+    int rank = PyArray_NDIM(arr);
+    npy_intp size = PyArray_Size((PyObject *)arr);
+    printf("\trank = %d, flags = %d, size = %" NPY_INTP_FMT "\n", rank,
+           arr->flags, size);
+    printf("\tstrides = ");
+    dump_dims(rank, arr->strides);
+    printf("\tdimensions = ");
+    dump_dims(rank, arr->dimensions);
+}
+#endif
+
+#define SWAPTYPE(a, b, t) \
+    {                     \
+        t c;              \
+        c = (a);          \
+        (a) = (b);        \
+        (b) = c;          \
+    }
+
+static int
+swap_arrays(PyArrayObject *obj1, PyArrayObject *obj2)
+{
+    PyArrayObject_fields *arr1 = (PyArrayObject_fields *)obj1,
+                         *arr2 = (PyArrayObject_fields *)obj2;
+    SWAPTYPE(arr1->data, arr2->data, char *);
+    SWAPTYPE(arr1->nd, arr2->nd, int);
+    SWAPTYPE(arr1->dimensions, arr2->dimensions, npy_intp *);
+    SWAPTYPE(arr1->strides, arr2->strides, npy_intp *);
+    SWAPTYPE(arr1->base, arr2->base, PyObject *);
+    SWAPTYPE(arr1->descr, arr2->descr, PyArray_Descr *);
+    SWAPTYPE(arr1->flags, arr2->flags, int);
+    /* SWAPTYPE(arr1->weakreflist,arr2->weakreflist,PyObject*); */
+    return 0;
+}
+
+#define ARRAY_ISCOMPATIBLE(arr,type_num)                                \
+    ((PyArray_ISINTEGER(arr) && PyTypeNum_ISINTEGER(type_num)) ||     \
+     (PyArray_ISFLOAT(arr) && PyTypeNum_ISFLOAT(type_num)) ||         \
+     (PyArray_ISCOMPLEX(arr) && PyTypeNum_ISCOMPLEX(type_num)) ||     \
+     (PyArray_ISBOOL(arr) && PyTypeNum_ISBOOL(type_num)) ||           \
+     (PyArray_ISSTRING(arr) && PyTypeNum_ISSTRING(type_num)))
+
+static int
+get_elsize(PyObject *obj) {
+  /*
+    get_elsize determines array itemsize from a Python object.  Returns
+    elsize if successful, -1 otherwise.
+
+    Supported types of the input are: numpy.ndarray, bytes, str, tuple,
+    list.
+  */
+
+  if (PyArray_Check(obj)) {
+    return PyArray_DESCR((PyArrayObject *)obj)->elsize;
+  } else if (PyBytes_Check(obj)) {
+    return PyBytes_GET_SIZE(obj);
+  } else if (PyUnicode_Check(obj)) {
+    return PyUnicode_GET_LENGTH(obj);
+  } else if (PySequence_Check(obj)) {
+    PyObject* fast = PySequence_Fast(obj, "f2py:fortranobject.c:get_elsize");
+    if (fast != NULL) {
+      Py_ssize_t i, n = PySequence_Fast_GET_SIZE(fast);
+      int sz, elsize = 0;
+      for (i=0; i<n; i++) {
+        sz = get_elsize(PySequence_Fast_GET_ITEM(fast, i) /* borrowed */);
+        if (sz > elsize) {
+          elsize = sz;
+        }
+      }
+      Py_DECREF(fast);
+      return elsize;
+    }
+  }
+  return -1;
+}
+
+extern PyArrayObject *
+ndarray_from_pyobj(const int type_num,
+                   const int elsize_,
+                   npy_intp *dims,
+                   const int rank,
+                   const int intent,
+                   PyObject *obj,
+                   const char *errmess) {
+    /*
+     * Return an array with given element type and shape from a Python
+     * object while taking into account the usage intent of the array.
+     *
+     * - element type is defined by type_num and elsize
+     * - shape is defined by dims and rank
+     *
+     * ndarray_from_pyobj is used to convert Python object arguments
+     * to numpy ndarrays with given type and shape that data is passed
+     * to interfaced Fortran or C functions.
+     *
+     * errmess (if not NULL), contains a prefix of an error message
+     * for an exception to be triggered within this function.
+     *
+     * Negative elsize value means that elsize is to be determined
+     * from the Python object in runtime.
+     *
+     * Note on strings
+     * ---------------
+     *
+     * String type (type_num == NPY_STRING) does not have fixed
+     * element size and, by default, the type object sets it to
+     * 0. Therefore, for string types, one has to use elsize
+     * argument. For other types, elsize value is ignored.
+     *
+     * NumPy defines the type of a fixed-width string as
+     * dtype('S<width>'). In addition, there is also dtype('c'), that
+     * appears as dtype('S1') (these have the same type_num value),
+     * but is actually different (.char attribute is either 'S' or
+     * 'c', respecitely).
+     *
+     * In Fortran, character arrays and strings are different
+     * concepts.  The relation between Fortran types, NumPy dtypes,
+     * and type_num-elsize pairs, is defined as follows:
+     *
+     * character*5 foo     | dtype('S5')  | elsize=5, shape=()
+     * character(5) foo    | dtype('S1')  | elsize=1, shape=(5)
+     * character*5 foo(n)  | dtype('S5')  | elsize=5, shape=(n,)
+     * character(5) foo(n) | dtype('S1')  | elsize=1, shape=(5, n)
+     * character*(*) foo   | dtype('S')   | elsize=-1, shape=()
+     *
+     * Note about reference counting
+     * -----------------------------
+     *
+     * If the caller returns the array to Python, it must be done with
+     * Py_BuildValue("N",arr).  Otherwise, if obj!=arr then the caller
+     * must call Py_DECREF(arr).
+     *
+     * Note on intent(cache,out,..)
+     * ----------------------------
+     * Don't expect correct data when returning intent(cache) array.
+     *
+     */
+    char mess[F2PY_MESSAGE_BUFFER_SIZE];
+    PyArrayObject *arr = NULL;
+    int elsize = (elsize_ < 0 ? get_elsize(obj) : elsize_);
+    if (elsize < 0) {
+      if (errmess != NULL) {
+        strcpy(mess, errmess);
+      }
+      sprintf(mess + strlen(mess),
+              " -- failed to determine element size from %s",
+              Py_TYPE(obj)->tp_name);
+      PyErr_SetString(PyExc_SystemError, mess);
+      return NULL;
+    }
+    PyArray_Descr * descr = get_descr_from_type_and_elsize(type_num, elsize);  // new reference
+    if (descr == NULL) {
+      return NULL;
+    }
+    elsize = descr->elsize;
+    if ((intent & F2PY_INTENT_HIDE)
+        || ((intent & F2PY_INTENT_CACHE) && (obj == Py_None))
+        || ((intent & F2PY_OPTIONAL) && (obj == Py_None))
+        ) {
+        /* intent(cache), optional, intent(hide) */
+        int ineg = find_first_negative_dimension(rank, dims);
+        if (ineg >= 0) {
+            int i;
+            strcpy(mess, "failed to create intent(cache|hide)|optional array"
+                   "-- must have defined dimensions but got (");
+            for(i = 0; i < rank; ++i)
+                sprintf(mess + strlen(mess), "%" NPY_INTP_FMT ",", dims[i]);
+            strcat(mess, ")");
+            PyErr_SetString(PyExc_ValueError, mess);
+            Py_DECREF(descr);
+            return NULL;
+        }
+        arr = (PyArrayObject *)                                      \
+          PyArray_NewFromDescr(&PyArray_Type, descr, rank, dims,
+                               NULL, NULL, !(intent & F2PY_INTENT_C), NULL);
+        if (arr == NULL) {
+          Py_DECREF(descr);
+          return NULL;
+        }
+        if (PyArray_ITEMSIZE(arr) != elsize) {
+          strcpy(mess, "failed to create intent(cache|hide)|optional array");
+          sprintf(mess+strlen(mess)," -- expected elsize=%d got %" NPY_INTP_FMT, elsize, (npy_intp)PyArray_ITEMSIZE(arr));
+          PyErr_SetString(PyExc_ValueError,mess);
+          Py_DECREF(arr);
+          return NULL;
+        }
+        if (!(intent & F2PY_INTENT_CACHE)) {
+          PyArray_FILLWBYTE(arr, 0);
+        }
+        return arr;
+    }
+
+    if (PyArray_Check(obj)) {
+        arr = (PyArrayObject *)obj;
+        if (intent & F2PY_INTENT_CACHE) {
+            /* intent(cache) */
+            if (PyArray_ISONESEGMENT(arr)
+                && PyArray_ITEMSIZE(arr) >= elsize) {
+                if (check_and_fix_dimensions(arr, rank, dims, errmess)) {
+                  Py_DECREF(descr);
+                  return NULL;
+                }
+                if (intent & F2PY_INTENT_OUT)
+                  Py_INCREF(arr);
+                Py_DECREF(descr);
+                return arr;
+            }
+            strcpy(mess, "failed to initialize intent(cache) array");
+            if (!PyArray_ISONESEGMENT(arr))
+                strcat(mess, " -- input must be in one segment");
+            if (PyArray_ITEMSIZE(arr) < elsize)
+                sprintf(mess + strlen(mess),
+                        " -- expected at least elsize=%d but got "
+                        "%" NPY_INTP_FMT,
+                        elsize, (npy_intp)PyArray_ITEMSIZE(arr));
+            PyErr_SetString(PyExc_ValueError, mess);
+            Py_DECREF(descr);
+            return NULL;
+        }
+
+        /* here we have always intent(in) or intent(inout) or intent(inplace)
+         */
+
+        if (check_and_fix_dimensions(arr, rank, dims, errmess)) {
+          Py_DECREF(descr);
+          return NULL;
+        }
+        /*
+        printf("intent alignment=%d\n", F2PY_GET_ALIGNMENT(intent));
+        printf("alignment check=%d\n", F2PY_CHECK_ALIGNMENT(arr, intent));
+        int i;
+        for (i=1;i<=16;i++)
+          printf("i=%d isaligned=%d\n", i, ARRAY_ISALIGNED(arr, i));
+        */
+        if ((! (intent & F2PY_INTENT_COPY)) &&
+            PyArray_ITEMSIZE(arr) == elsize &&
+            ARRAY_ISCOMPATIBLE(arr,type_num) &&
+            F2PY_CHECK_ALIGNMENT(arr, intent)) {
+            if ((intent & F2PY_INTENT_INOUT || intent & F2PY_INTENT_INPLACE)
+              ? ((intent & F2PY_INTENT_C) ? PyArray_ISCARRAY(arr) : PyArray_ISFARRAY(arr))
+              : ((intent & F2PY_INTENT_C) ? PyArray_ISCARRAY_RO(arr) : PyArray_ISFARRAY_RO(arr))) {
+                if ((intent & F2PY_INTENT_OUT)) {
+                    Py_INCREF(arr);
+                }
+                /* Returning input array */
+                Py_DECREF(descr);
+                return arr;
+            }
+        }
+        if (intent & F2PY_INTENT_INOUT) {
+            strcpy(mess, "failed to initialize intent(inout) array");
+            /* Must use PyArray_IS*ARRAY because intent(inout) requires
+             * writable input */
+            if ((intent & F2PY_INTENT_C) && !PyArray_ISCARRAY(arr))
+                strcat(mess, " -- input not contiguous");
+            if (!(intent & F2PY_INTENT_C) && !PyArray_ISFARRAY(arr))
+                strcat(mess, " -- input not fortran contiguous");
+            if (PyArray_ITEMSIZE(arr) != elsize)
+                sprintf(mess + strlen(mess),
+                        " -- expected elsize=%d but got %" NPY_INTP_FMT,
+                        elsize,
+                        (npy_intp)PyArray_ITEMSIZE(arr)
+                        );
+            if (!(ARRAY_ISCOMPATIBLE(arr, type_num))) {
+                sprintf(mess + strlen(mess),
+                        " -- input '%c' not compatible to '%c'",
+                        PyArray_DESCR(arr)->type, descr->type);
+            }
+            if (!(F2PY_CHECK_ALIGNMENT(arr, intent)))
+                sprintf(mess + strlen(mess), " -- input not %d-aligned",
+                        F2PY_GET_ALIGNMENT(intent));
+            PyErr_SetString(PyExc_ValueError, mess);
+            Py_DECREF(descr);
+            return NULL;
+        }
+
+        /* here we have always intent(in) or intent(inplace) */
+
+        {
+          PyArrayObject * retarr = (PyArrayObject *)                    \
+            PyArray_NewFromDescr(&PyArray_Type, descr, PyArray_NDIM(arr), PyArray_DIMS(arr),
+                                 NULL, NULL, !(intent & F2PY_INTENT_C), NULL);
+          if (retarr==NULL) {
+            Py_DECREF(descr);
+            return NULL;
+          }
+          F2PY_REPORT_ON_ARRAY_COPY_FROMARR;
+          if (PyArray_CopyInto(retarr, arr)) {
+            Py_DECREF(retarr);
+            return NULL;
+          }
+          if (intent & F2PY_INTENT_INPLACE) {
+            if (swap_arrays(arr,retarr)) {
+              Py_DECREF(retarr);
+              return NULL; /* XXX: set exception */
+            }
+            Py_XDECREF(retarr);
+            if (intent & F2PY_INTENT_OUT)
+              Py_INCREF(arr);
+          } else {
+            arr = retarr;
+          }
+        }
+        return arr;
+    }
+
+    if ((intent & F2PY_INTENT_INOUT) || (intent & F2PY_INTENT_INPLACE) ||
+        (intent & F2PY_INTENT_CACHE)) {
+        PyErr_Format(PyExc_TypeError,
+                     "failed to initialize intent(inout|inplace|cache) "
+                     "array, input '%s' object is not an array",
+                     Py_TYPE(obj)->tp_name);
+        Py_DECREF(descr);
+        return NULL;
+    }
+
+    {
+        F2PY_REPORT_ON_ARRAY_COPY_FROMANY;
+        arr = (PyArrayObject *)PyArray_FromAny(
+                obj, descr, 0, 0,
+                ((intent & F2PY_INTENT_C) ? NPY_ARRAY_CARRAY
+                                          : NPY_ARRAY_FARRAY) |
+                        NPY_ARRAY_FORCECAST,
+                NULL);
+        // Warning: in the case of NPY_STRING, PyArray_FromAny may
+        // reset descr->elsize, e.g. dtype('S0') becomes dtype('S1').
+        if (arr == NULL) {
+          Py_DECREF(descr);
+          return NULL;
+        }
+        if (type_num != NPY_STRING && PyArray_ITEMSIZE(arr) != elsize) {
+          // This is internal sanity tests: elsize has been set to
+          // descr->elsize in the beginning of this function.
+          strcpy(mess, "failed to initialize intent(in) array");
+          sprintf(mess + strlen(mess),
+                  " -- expected elsize=%d got %" NPY_INTP_FMT, elsize,
+                  (npy_intp)PyArray_ITEMSIZE(arr));
+          PyErr_SetString(PyExc_ValueError, mess);
+          Py_DECREF(arr);
+          return NULL;
+        }
+        if (check_and_fix_dimensions(arr, rank, dims, errmess)) {
+          Py_DECREF(arr);
+          return NULL;
+        }
+        return arr;
+    }
+}
+
+extern PyArrayObject *
+array_from_pyobj(const int type_num,
+                                npy_intp *dims,
+                                const int rank,
+                                const int intent,
+                                PyObject *obj) {
+  /*
+    Same as ndarray_from_pyobj but with elsize determined from type,
+    if possible. Provided for backward compatibility.
+   */
+  PyArray_Descr* descr = PyArray_DescrFromType(type_num);
+  int elsize = descr->elsize;
+  Py_DECREF(descr);
+  return ndarray_from_pyobj(type_num, elsize, dims, rank, intent, obj, NULL);
+}
+
+/*****************************************/
+/* Helper functions for array_from_pyobj */
+/*****************************************/
+
+static int
+check_and_fix_dimensions(const PyArrayObject* arr, const int rank,
+                         npy_intp *dims, const char *errmess)
+{
+    /*
+     * This function fills in blanks (that are -1's) in dims list using
+     * the dimensions from arr. It also checks that non-blank dims will
+     * match with the corresponding values in arr dimensions.
+     *
+     * Returns 0 if the function is successful.
+     *
+     * If an error condition is detected, an exception is set and 1 is
+     * returned.
+     */
+    char mess[F2PY_MESSAGE_BUFFER_SIZE];
+    const npy_intp arr_size =
+            (PyArray_NDIM(arr)) ? PyArray_Size((PyObject *)arr) : 1;
+#ifdef DEBUG_COPY_ND_ARRAY
+    dump_attrs(arr);
+    printf("check_and_fix_dimensions:init: dims=");
+    dump_dims(rank, dims);
+#endif
+    if (rank > PyArray_NDIM(arr)) { /* [1,2] -> [[1],[2]]; 1 -> [[1]]  */
+        npy_intp new_size = 1;
+        int free_axe = -1;
+        int i;
+        npy_intp d;
+        /* Fill dims where -1 or 0; check dimensions; calc new_size; */
+        for (i = 0; i < PyArray_NDIM(arr); ++i) {
+            d = PyArray_DIM(arr, i);
+            if (dims[i] >= 0) {
+                if (d > 1 && dims[i] != d) {
+                    PyErr_Format(
+                            PyExc_ValueError,
+                            "%d-th dimension must be fixed to %" NPY_INTP_FMT
+                            " but got %" NPY_INTP_FMT "\n",
+                            i, dims[i], d);
+                    return 1;
+                }
+                if (!dims[i])
+                    dims[i] = 1;
+            }
+            else {
+                dims[i] = d ? d : 1;
+            }
+            new_size *= dims[i];
+        }
+        for (i = PyArray_NDIM(arr); i < rank; ++i)
+            if (dims[i] > 1) {
+                PyErr_Format(PyExc_ValueError,
+                             "%d-th dimension must be %" NPY_INTP_FMT
+                             " but got 0 (not defined).\n",
+                             i, dims[i]);
+                return 1;
+            }
+            else if (free_axe < 0)
+                free_axe = i;
+            else
+                dims[i] = 1;
+        if (free_axe >= 0) {
+            dims[free_axe] = arr_size / new_size;
+            new_size *= dims[free_axe];
+        }
+        if (new_size != arr_size) {
+            PyErr_Format(PyExc_ValueError,
+                         "unexpected array size: new_size=%" NPY_INTP_FMT
+                         ", got array with arr_size=%" NPY_INTP_FMT
+                         " (maybe too many free indices)\n",
+                         new_size, arr_size);
+            return 1;
+        }
+    }
+    else if (rank == PyArray_NDIM(arr)) {
+        npy_intp new_size = 1;
+        int i;
+        npy_intp d;
+        for (i = 0; i < rank; ++i) {
+            d = PyArray_DIM(arr, i);
+            if (dims[i] >= 0) {
+                if (d > 1 && d != dims[i]) {
+                    if (errmess != NULL) {
+                        strcpy(mess, errmess);
+                    }
+                    sprintf(mess + strlen(mess),
+                            " -- %d-th dimension must be fixed to %"
+                            NPY_INTP_FMT " but got %" NPY_INTP_FMT,
+                            i, dims[i], d);
+                    PyErr_SetString(PyExc_ValueError, mess);
+                    return 1;
+                }
+                if (!dims[i])
+                    dims[i] = 1;
+            }
+            else
+                dims[i] = d;
+            new_size *= dims[i];
+        }
+        if (new_size != arr_size) {
+            PyErr_Format(PyExc_ValueError,
+                         "unexpected array size: new_size=%" NPY_INTP_FMT
+                         ", got array with arr_size=%" NPY_INTP_FMT "\n",
+                         new_size, arr_size);
+            return 1;
+        }
+    }
+    else { /* [[1,2]] -> [[1],[2]] */
+        int i, j;
+        npy_intp d;
+        int effrank;
+        npy_intp size;
+        for (i = 0, effrank = 0; i < PyArray_NDIM(arr); ++i)
+            if (PyArray_DIM(arr, i) > 1)
+                ++effrank;
+        if (dims[rank - 1] >= 0)
+            if (effrank > rank) {
+                PyErr_Format(PyExc_ValueError,
+                             "too many axes: %d (effrank=%d), "
+                             "expected rank=%d\n",
+                             PyArray_NDIM(arr), effrank, rank);
+                return 1;
+            }
+
+        for (i = 0, j = 0; i < rank; ++i) {
+            while (j < PyArray_NDIM(arr) && PyArray_DIM(arr, j) < 2) ++j;
+            if (j >= PyArray_NDIM(arr))
+                d = 1;
+            else
+                d = PyArray_DIM(arr, j++);
+            if (dims[i] >= 0) {
+                if (d > 1 && d != dims[i]) {
+                    if (errmess != NULL) {
+                        strcpy(mess, errmess);
+                    }
+                    sprintf(mess + strlen(mess),
+                            " -- %d-th dimension must be fixed to %"
+                            NPY_INTP_FMT " but got %" NPY_INTP_FMT
+                            " (real index=%d)\n",
+                            i, dims[i], d, j-1);
+                    PyErr_SetString(PyExc_ValueError, mess);
+                    return 1;
+                }
+                if (!dims[i])
+                    dims[i] = 1;
+            }
+            else
+                dims[i] = d;
+        }
+
+        for (i = rank; i < PyArray_NDIM(arr);
+             ++i) { /* [[1,2],[3,4]] -> [1,2,3,4] */
+            while (j < PyArray_NDIM(arr) && PyArray_DIM(arr, j) < 2) ++j;
+            if (j >= PyArray_NDIM(arr))
+                d = 1;
+            else
+                d = PyArray_DIM(arr, j++);
+            dims[rank - 1] *= d;
+        }
+        for (i = 0, size = 1; i < rank; ++i) size *= dims[i];
+        if (size != arr_size) {
+            char msg[200];
+            int len;
+            snprintf(msg, sizeof(msg),
+                     "unexpected array size: size=%" NPY_INTP_FMT
+                     ", arr_size=%" NPY_INTP_FMT
+                     ", rank=%d, effrank=%d, arr.nd=%d, dims=[",
+                     size, arr_size, rank, effrank, PyArray_NDIM(arr));
+            for (i = 0; i < rank; ++i) {
+                len = strlen(msg);
+                snprintf(msg + len, sizeof(msg) - len, " %" NPY_INTP_FMT,
+                         dims[i]);
+            }
+            len = strlen(msg);
+            snprintf(msg + len, sizeof(msg) - len, " ], arr.dims=[");
+            for (i = 0; i < PyArray_NDIM(arr); ++i) {
+                len = strlen(msg);
+                snprintf(msg + len, sizeof(msg) - len, " %" NPY_INTP_FMT,
+                         PyArray_DIM(arr, i));
+            }
+            len = strlen(msg);
+            snprintf(msg + len, sizeof(msg) - len, " ]\n");
+            PyErr_SetString(PyExc_ValueError, msg);
+            return 1;
+        }
+    }
+#ifdef DEBUG_COPY_ND_ARRAY
+    printf("check_and_fix_dimensions:end: dims=");
+    dump_dims(rank, dims);
+#endif
+    return 0;
+}
+
+/* End of file: array_from_pyobj.c */
+
+/************************* copy_ND_array *******************************/
+
+extern int
+copy_ND_array(const PyArrayObject *arr, PyArrayObject *out)
+{
+    F2PY_REPORT_ON_ARRAY_COPY_FROMARR;
+    return PyArray_CopyInto(out, (PyArrayObject *)arr);
+}
+
+/********************* Various utility functions ***********************/
+
+extern int
+f2py_describe(PyObject *obj, char *buf) {
+  /*
+    Write the description of a Python object to buf. The caller must
+    provide buffer with size sufficient to write the description.
+
+    Return 1 on success.
+  */
+  char localbuf[F2PY_MESSAGE_BUFFER_SIZE];
+  if (PyBytes_Check(obj)) {
+    sprintf(localbuf, "%d-%s", (npy_int)PyBytes_GET_SIZE(obj), Py_TYPE(obj)->tp_name);
+  } else if (PyUnicode_Check(obj)) {
+    sprintf(localbuf, "%d-%s", (npy_int)PyUnicode_GET_LENGTH(obj), Py_TYPE(obj)->tp_name);
+  } else if (PyArray_CheckScalar(obj)) {
+    PyArrayObject* arr = (PyArrayObject*)obj;
+    sprintf(localbuf, "%c%" NPY_INTP_FMT "-%s-scalar", PyArray_DESCR(arr)->kind, PyArray_ITEMSIZE(arr), Py_TYPE(obj)->tp_name);
+  } else if (PyArray_Check(obj)) {
+    int i;
+    PyArrayObject* arr = (PyArrayObject*)obj;
+    strcpy(localbuf, "(");
+    for (i=0; i<PyArray_NDIM(arr); i++) {
+      if (i) {
+        strcat(localbuf, " ");
+      }
+      sprintf(localbuf + strlen(localbuf), "%" NPY_INTP_FMT ",", PyArray_DIM(arr, i));
+    }
+    sprintf(localbuf + strlen(localbuf), ")-%c%" NPY_INTP_FMT "-%s", PyArray_DESCR(arr)->kind, PyArray_ITEMSIZE(arr), Py_TYPE(obj)->tp_name);
+  } else if (PySequence_Check(obj)) {
+    sprintf(localbuf, "%d-%s", (npy_int)PySequence_Length(obj), Py_TYPE(obj)->tp_name);
+  } else {
+    sprintf(localbuf, "%s instance", Py_TYPE(obj)->tp_name);
+  }
+  // TODO: detect the size of buf and make sure that size(buf) >= size(localbuf).
+  strcpy(buf, localbuf);
+  return 1;
+}
+
+extern npy_intp
+f2py_size_impl(PyArrayObject* var, ...)
+{
+  npy_intp sz = 0;
+  npy_intp dim;
+  npy_intp rank;
+  va_list argp;
+  va_start(argp, var);
+  dim = va_arg(argp, npy_int);
+  if (dim==-1)
+    {
+      sz = PyArray_SIZE(var);
+    }
+  else
+    {
+      rank = PyArray_NDIM(var);
+      if (dim>=1 && dim<=rank)
+        sz = PyArray_DIM(var, dim-1);
+      else
+        fprintf(stderr, "f2py_size: 2nd argument value=%" NPY_INTP_FMT
+                " fails to satisfy 1<=value<=%" NPY_INTP_FMT
+                ". Result will be 0.\n", dim, rank);
+    }
+  va_end(argp);
+  return sz;
+}
+
+/*********************************************/
+/* Compatibility functions for Python >= 3.0 */
+/*********************************************/
+
+PyObject *
+F2PyCapsule_FromVoidPtr(void *ptr, void (*dtor)(PyObject *))
+{
+    PyObject *ret = PyCapsule_New(ptr, NULL, dtor);
+    if (ret == NULL) {
+        PyErr_Clear();
+    }
+    return ret;
+}
+
+void *
+F2PyCapsule_AsVoidPtr(PyObject *obj)
+{
+    void *ret = PyCapsule_GetPointer(obj, NULL);
+    if (ret == NULL) {
+        PyErr_Clear();
+    }
+    return ret;
+}
+
+int
+F2PyCapsule_Check(PyObject *ptr)
+{
+    return PyCapsule_CheckExact(ptr);
+}
+
+#ifdef __cplusplus
+}
+#endif
+/************************* EOF fortranobject.c *******************************/
diff --git a/.venv/lib/python3.12/site-packages/numpy/f2py/src/fortranobject.h b/.venv/lib/python3.12/site-packages/numpy/f2py/src/fortranobject.h
new file mode 100644
index 00000000..abd699c2
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/numpy/f2py/src/fortranobject.h
@@ -0,0 +1,173 @@
+#ifndef Py_FORTRANOBJECT_H
+#define Py_FORTRANOBJECT_H
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <Python.h>
+
+#ifndef NPY_NO_DEPRECATED_API
+#define NPY_NO_DEPRECATED_API NPY_API_VERSION
+#endif
+#ifdef FORTRANOBJECT_C
+#define NO_IMPORT_ARRAY
+#endif
+#define PY_ARRAY_UNIQUE_SYMBOL _npy_f2py_ARRAY_API
+#include "numpy/arrayobject.h"
+#include "numpy/npy_3kcompat.h"
+
+#ifdef F2PY_REPORT_ATEXIT
+#include <sys/timeb.h>
+// clang-format off
+extern void f2py_start_clock(void);
+extern void f2py_stop_clock(void);
+extern void f2py_start_call_clock(void);
+extern void f2py_stop_call_clock(void);
+extern void f2py_cb_start_clock(void);
+extern void f2py_cb_stop_clock(void);
+extern void f2py_cb_start_call_clock(void);
+extern void f2py_cb_stop_call_clock(void);
+extern void f2py_report_on_exit(int, void *);
+// clang-format on
+#endif
+
+#ifdef DMALLOC
+#include "dmalloc.h"
+#endif
+
+/* Fortran object interface */
+
+/*
+123456789-123456789-123456789-123456789-123456789-123456789-123456789-12
+
+PyFortranObject represents various Fortran objects:
+Fortran (module) routines, COMMON blocks, module data.
+
+Author: Pearu Peterson <pearu@cens.ioc.ee>
+*/
+
+#define F2PY_MAX_DIMS 40
+#define F2PY_MESSAGE_BUFFER_SIZE 300  // Increase on "stack smashing detected"
+
+typedef void (*f2py_set_data_func)(char *, npy_intp *);
+typedef void (*f2py_void_func)(void);
+typedef void (*f2py_init_func)(int *, npy_intp *, f2py_set_data_func, int *);
+
+/*typedef void* (*f2py_c_func)(void*,...);*/
+
+typedef void *(*f2pycfunc)(void);
+
+typedef struct {
+    char *name; /* attribute (array||routine) name */
+    int rank;   /* array rank, 0 for scalar, max is F2PY_MAX_DIMS,
+                   || rank=-1 for Fortran routine */
+    struct {
+        npy_intp d[F2PY_MAX_DIMS];
+    } dims;              /* dimensions of the array, || not used */
+    int type;            /* PyArray_<type> || not used */
+    int elsize;                /* Element size || not used */
+    char *data;          /* pointer to array || Fortran routine */
+    f2py_init_func func; /* initialization function for
+                            allocatable arrays:
+                            func(&rank,dims,set_ptr_func,name,len(name))
+                            || C/API wrapper for Fortran routine */
+    char *doc;           /* documentation string; only recommended
+                            for routines. */
+} FortranDataDef;
+
+typedef struct {
+    PyObject_HEAD
+    int len;              /* Number of attributes */
+    FortranDataDef *defs; /* An array of FortranDataDef's */
+    PyObject *dict;       /* Fortran object attribute dictionary */
+} PyFortranObject;
+
+#define PyFortran_Check(op) (Py_TYPE(op) == &PyFortran_Type)
+#define PyFortran_Check1(op) (0 == strcmp(Py_TYPE(op)->tp_name, "fortran"))
+
+extern PyTypeObject PyFortran_Type;
+extern int
+F2PyDict_SetItemString(PyObject *dict, char *name, PyObject *obj);
+extern PyObject *
+PyFortranObject_New(FortranDataDef *defs, f2py_void_func init);
+extern PyObject *
+PyFortranObject_NewAsAttr(FortranDataDef *defs);
+
+PyObject *
+F2PyCapsule_FromVoidPtr(void *ptr, void (*dtor)(PyObject *));
+void *
+F2PyCapsule_AsVoidPtr(PyObject *obj);
+int
+F2PyCapsule_Check(PyObject *ptr);
+
+extern void *
+F2PySwapThreadLocalCallbackPtr(char *key, void *ptr);
+extern void *
+F2PyGetThreadLocalCallbackPtr(char *key);
+
+#define ISCONTIGUOUS(m) (PyArray_FLAGS(m) & NPY_ARRAY_C_CONTIGUOUS)
+#define F2PY_INTENT_IN 1
+#define F2PY_INTENT_INOUT 2
+#define F2PY_INTENT_OUT 4
+#define F2PY_INTENT_HIDE 8
+#define F2PY_INTENT_CACHE 16
+#define F2PY_INTENT_COPY 32
+#define F2PY_INTENT_C 64
+#define F2PY_OPTIONAL 128
+#define F2PY_INTENT_INPLACE 256
+#define F2PY_INTENT_ALIGNED4 512
+#define F2PY_INTENT_ALIGNED8 1024
+#define F2PY_INTENT_ALIGNED16 2048
+
+#define ARRAY_ISALIGNED(ARR, SIZE) ((size_t)(PyArray_DATA(ARR)) % (SIZE) == 0)
+#define F2PY_ALIGN4(intent) (intent & F2PY_INTENT_ALIGNED4)
+#define F2PY_ALIGN8(intent) (intent & F2PY_INTENT_ALIGNED8)
+#define F2PY_ALIGN16(intent) (intent & F2PY_INTENT_ALIGNED16)
+
+#define F2PY_GET_ALIGNMENT(intent) \
+    (F2PY_ALIGN4(intent)           \
+             ? 4                   \
+             : (F2PY_ALIGN8(intent) ? 8 : (F2PY_ALIGN16(intent) ? 16 : 1)))
+#define F2PY_CHECK_ALIGNMENT(arr, intent) \
+    ARRAY_ISALIGNED(arr, F2PY_GET_ALIGNMENT(intent))
+#define F2PY_ARRAY_IS_CHARACTER_COMPATIBLE(arr) ((PyArray_DESCR(arr)->type_num == NPY_STRING && PyArray_DESCR(arr)->elsize >= 1) \
+                                                 || PyArray_DESCR(arr)->type_num == NPY_UINT8)
+#define F2PY_IS_UNICODE_ARRAY(arr) (PyArray_DESCR(arr)->type_num == NPY_UNICODE)
+
+extern PyArrayObject *
+ndarray_from_pyobj(const int type_num, const int elsize_, npy_intp *dims,
+                   const int rank, const int intent, PyObject *obj,
+                   const char *errmess);
+
+extern PyArrayObject *
+array_from_pyobj(const int type_num, npy_intp *dims, const int rank,
+                 const int intent, PyObject *obj);
+extern int
+copy_ND_array(const PyArrayObject *in, PyArrayObject *out);
+
+#ifdef DEBUG_COPY_ND_ARRAY
+extern void
+dump_attrs(const PyArrayObject *arr);
+#endif
+
+  extern int f2py_describe(PyObject *obj, char *buf);
+
+  /* Utility CPP macros and functions that can be used in signature file
+     expressions. See signature-file.rst for documentation.
+  */
+
+#define f2py_itemsize(var) (PyArray_DESCR((capi_ ## var ## _as_array))->elsize)
+#define f2py_size(var, ...) f2py_size_impl((PyArrayObject *)(capi_ ## var ## _as_array), ## __VA_ARGS__, -1)
+#define f2py_rank(var) var ## _Rank
+#define f2py_shape(var,dim) var ## _Dims[dim]
+#define f2py_len(var) f2py_shape(var,0)
+#define f2py_fshape(var,dim) f2py_shape(var,rank(var)-dim-1)
+#define f2py_flen(var) f2py_fshape(var,0)
+#define f2py_slen(var) capi_ ## var ## _len
+
+  extern npy_intp f2py_size_impl(PyArrayObject* var, ...);
+
+#ifdef __cplusplus
+}
+#endif
+#endif /* !Py_FORTRANOBJECT_H */
diff --git a/.venv/lib/python3.12/site-packages/numpy/f2py/symbolic.py b/.venv/lib/python3.12/site-packages/numpy/f2py/symbolic.py
new file mode 100644
index 00000000..67120d79
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/numpy/f2py/symbolic.py
@@ -0,0 +1,1517 @@
+"""Fortran/C symbolic expressions
+
+References:
+- J3/21-007: Draft Fortran 202x. https://j3-fortran.org/doc/year/21/21-007.pdf
+
+Copyright 1999 -- 2011 Pearu Peterson all rights reserved.
+Copyright 2011 -- present NumPy Developers.
+Permission to use, modify, and distribute this software is given under the
+terms of the NumPy License.
+
+NO WARRANTY IS EXPRESSED OR IMPLIED.  USE AT YOUR OWN RISK.
+"""
+
+# To analyze Fortran expressions to solve dimensions specifications,
+# for instances, we implement a minimal symbolic engine for parsing
+# expressions into a tree of expression instances. As a first
+# instance, we care only about arithmetic expressions involving
+# integers and operations like addition (+), subtraction (-),
+# multiplication (*), division (Fortran / is Python //, Fortran // is
+# concatenate), and exponentiation (**).  In addition, .pyf files may
+# contain C expressions that support here is implemented as well.
+#
+# TODO: support logical constants (Op.BOOLEAN)
+# TODO: support logical operators (.AND., ...)
+# TODO: support defined operators (.MYOP., ...)
+#
+__all__ = ['Expr']
+
+
+import re
+import warnings
+from enum import Enum
+from math import gcd
+
+
+class Language(Enum):
+    """
+    Used as Expr.tostring language argument.
+    """
+    Python = 0
+    Fortran = 1
+    C = 2
+
+
+class Op(Enum):
+    """
+    Used as Expr op attribute.
+    """
+    INTEGER = 10
+    REAL = 12
+    COMPLEX = 15
+    STRING = 20
+    ARRAY = 30
+    SYMBOL = 40
+    TERNARY = 100
+    APPLY = 200
+    INDEXING = 210
+    CONCAT = 220
+    RELATIONAL = 300
+    TERMS = 1000
+    FACTORS = 2000
+    REF = 3000
+    DEREF = 3001
+
+
+class RelOp(Enum):
+    """
+    Used in Op.RELATIONAL expression to specify the function part.
+    """
+    EQ = 1
+    NE = 2
+    LT = 3
+    LE = 4
+    GT = 5
+    GE = 6
+
+    @classmethod
+    def fromstring(cls, s, language=Language.C):
+        if language is Language.Fortran:
+            return {'.eq.': RelOp.EQ, '.ne.': RelOp.NE,
+                    '.lt.': RelOp.LT, '.le.': RelOp.LE,
+                    '.gt.': RelOp.GT, '.ge.': RelOp.GE}[s.lower()]
+        return {'==': RelOp.EQ, '!=': RelOp.NE, '<': RelOp.LT,
+                '<=': RelOp.LE, '>': RelOp.GT, '>=': RelOp.GE}[s]
+
+    def tostring(self, language=Language.C):
+        if language is Language.Fortran:
+            return {RelOp.EQ: '.eq.', RelOp.NE: '.ne.',
+                    RelOp.LT: '.lt.', RelOp.LE: '.le.',
+                    RelOp.GT: '.gt.', RelOp.GE: '.ge.'}[self]
+        return {RelOp.EQ: '==', RelOp.NE: '!=',
+                RelOp.LT: '<', RelOp.LE: '<=',
+                RelOp.GT: '>', RelOp.GE: '>='}[self]
+
+
+class ArithOp(Enum):
+    """
+    Used in Op.APPLY expression to specify the function part.
+    """
+    POS = 1
+    NEG = 2
+    ADD = 3
+    SUB = 4
+    MUL = 5
+    DIV = 6
+    POW = 7
+
+
+class OpError(Exception):
+    pass
+
+
+class Precedence(Enum):
+    """
+    Used as Expr.tostring precedence argument.
+    """
+    ATOM = 0
+    POWER = 1
+    UNARY = 2
+    PRODUCT = 3
+    SUM = 4
+    LT = 6
+    EQ = 7
+    LAND = 11
+    LOR = 12
+    TERNARY = 13
+    ASSIGN = 14
+    TUPLE = 15
+    NONE = 100
+
+
+integer_types = (int,)
+number_types = (int, float)
+
+
+def _pairs_add(d, k, v):
+    # Internal utility method for updating terms and factors data.
+    c = d.get(k)
+    if c is None:
+        d[k] = v
+    else:
+        c = c + v
+        if c:
+            d[k] = c
+        else:
+            del d[k]
+
+
+class ExprWarning(UserWarning):
+    pass
+
+
+def ewarn(message):
+    warnings.warn(message, ExprWarning, stacklevel=2)
+
+
+class Expr:
+    """Represents a Fortran expression as a op-data pair.
+
+    Expr instances are hashable and sortable.
+    """
+
+    @staticmethod
+    def parse(s, language=Language.C):
+        """Parse a Fortran expression to a Expr.
+        """
+        return fromstring(s, language=language)
+
+    def __init__(self, op, data):
+        assert isinstance(op, Op)
+
+        # sanity checks
+        if op is Op.INTEGER:
+            # data is a 2-tuple of numeric object and a kind value
+            # (default is 4)
+            assert isinstance(data, tuple) and len(data) == 2
+            assert isinstance(data[0], int)
+            assert isinstance(data[1], (int, str)), data
+        elif op is Op.REAL:
+            # data is a 2-tuple of numeric object and a kind value
+            # (default is 4)
+            assert isinstance(data, tuple) and len(data) == 2
+            assert isinstance(data[0], float)
+            assert isinstance(data[1], (int, str)), data
+        elif op is Op.COMPLEX:
+            # data is a 2-tuple of constant expressions
+            assert isinstance(data, tuple) and len(data) == 2
+        elif op is Op.STRING:
+            # data is a 2-tuple of quoted string and a kind value
+            # (default is 1)
+            assert isinstance(data, tuple) and len(data) == 2
+            assert (isinstance(data[0], str)
+                    and data[0][::len(data[0])-1] in ('""', "''", '@@'))
+            assert isinstance(data[1], (int, str)), data
+        elif op is Op.SYMBOL:
+            # data is any hashable object
+            assert hash(data) is not None
+        elif op in (Op.ARRAY, Op.CONCAT):
+            # data is a tuple of expressions
+            assert isinstance(data, tuple)
+            assert all(isinstance(item, Expr) for item in data), data
+        elif op in (Op.TERMS, Op.FACTORS):
+            # data is {<term|base>:<coeff|exponent>} where dict values
+            # are nonzero Python integers
+            assert isinstance(data, dict)
+        elif op is Op.APPLY:
+            # data is (<function>, <operands>, <kwoperands>) where
+            # operands are Expr instances
+            assert isinstance(data, tuple) and len(data) == 3
+            # function is any hashable object
+            assert hash(data[0]) is not None
+            assert isinstance(data[1], tuple)
+            assert isinstance(data[2], dict)
+        elif op is Op.INDEXING:
+            # data is (<object>, <indices>)
+            assert isinstance(data, tuple) and len(data) == 2
+            # function is any hashable object
+            assert hash(data[0]) is not None
+        elif op is Op.TERNARY:
+            # data is (<cond>, <expr1>, <expr2>)
+            assert isinstance(data, tuple) and len(data) == 3
+        elif op in (Op.REF, Op.DEREF):
+            # data is Expr instance
+            assert isinstance(data, Expr)
+        elif op is Op.RELATIONAL:
+            # data is (<relop>, <left>, <right>)
+            assert isinstance(data, tuple) and len(data) == 3
+        else:
+            raise NotImplementedError(
+                f'unknown op or missing sanity check: {op}')
+
+        self.op = op
+        self.data = data
+
+    def __eq__(self, other):
+        return (isinstance(other, Expr)
+                and self.op is other.op
+                and self.data == other.data)
+
+    def __hash__(self):
+        if self.op in (Op.TERMS, Op.FACTORS):
+            data = tuple(sorted(self.data.items()))
+        elif self.op is Op.APPLY:
+            data = self.data[:2] + tuple(sorted(self.data[2].items()))
+        else:
+            data = self.data
+        return hash((self.op, data))
+
+    def __lt__(self, other):
+        if isinstance(other, Expr):
+            if self.op is not other.op:
+                return self.op.value < other.op.value
+            if self.op in (Op.TERMS, Op.FACTORS):
+                return (tuple(sorted(self.data.items()))
+                        < tuple(sorted(other.data.items())))
+            if self.op is Op.APPLY:
+                if self.data[:2] != other.data[:2]:
+                    return self.data[:2] < other.data[:2]
+                return tuple(sorted(self.data[2].items())) < tuple(
+                    sorted(other.data[2].items()))
+            return self.data < other.data
+        return NotImplemented
+
+    def __le__(self, other): return self == other or self < other
+
+    def __gt__(self, other): return not (self <= other)
+
+    def __ge__(self, other): return not (self < other)
+
+    def __repr__(self):
+        return f'{type(self).__name__}({self.op}, {self.data!r})'
+
+    def __str__(self):
+        return self.tostring()
+
+    def tostring(self, parent_precedence=Precedence.NONE,
+                 language=Language.Fortran):
+        """Return a string representation of Expr.
+        """
+        if self.op in (Op.INTEGER, Op.REAL):
+            precedence = (Precedence.SUM if self.data[0] < 0
+                          else Precedence.ATOM)
+            r = str(self.data[0]) + (f'_{self.data[1]}'
+                                     if self.data[1] != 4 else '')
+        elif self.op is Op.COMPLEX:
+            r = ', '.join(item.tostring(Precedence.TUPLE, language=language)
+                          for item in self.data)
+            r = '(' + r + ')'
+            precedence = Precedence.ATOM
+        elif self.op is Op.SYMBOL:
+            precedence = Precedence.ATOM
+            r = str(self.data)
+        elif self.op is Op.STRING:
+            r = self.data[0]
+            if self.data[1] != 1:
+                r = self.data[1] + '_' + r
+            precedence = Precedence.ATOM
+        elif self.op is Op.ARRAY:
+            r = ', '.join(item.tostring(Precedence.TUPLE, language=language)
+                          for item in self.data)
+            r = '[' + r + ']'
+            precedence = Precedence.ATOM
+        elif self.op is Op.TERMS:
+            terms = []
+            for term, coeff in sorted(self.data.items()):
+                if coeff < 0:
+                    op = ' - '
+                    coeff = -coeff
+                else:
+                    op = ' + '
+                if coeff == 1:
+                    term = term.tostring(Precedence.SUM, language=language)
+                else:
+                    if term == as_number(1):
+                        term = str(coeff)
+                    else:
+                        term = f'{coeff} * ' + term.tostring(
+                            Precedence.PRODUCT, language=language)
+                if terms:
+                    terms.append(op)
+                elif op == ' - ':
+                    terms.append('-')
+                terms.append(term)
+            r = ''.join(terms) or '0'
+            precedence = Precedence.SUM if terms else Precedence.ATOM
+        elif self.op is Op.FACTORS:
+            factors = []
+            tail = []
+            for base, exp in sorted(self.data.items()):
+                op = ' * '
+                if exp == 1:
+                    factor = base.tostring(Precedence.PRODUCT,
+                                           language=language)
+                elif language is Language.C:
+                    if exp in range(2, 10):
+                        factor = base.tostring(Precedence.PRODUCT,
+                                               language=language)
+                        factor = ' * '.join([factor] * exp)
+                    elif exp in range(-10, 0):
+                        factor = base.tostring(Precedence.PRODUCT,
+                                               language=language)
+                        tail += [factor] * -exp
+                        continue
+                    else:
+                        factor = base.tostring(Precedence.TUPLE,
+                                               language=language)
+                        factor = f'pow({factor}, {exp})'
+                else:
+                    factor = base.tostring(Precedence.POWER,
+                                           language=language) + f' ** {exp}'
+                if factors:
+                    factors.append(op)
+                factors.append(factor)
+            if tail:
+                if not factors:
+                    factors += ['1']
+                factors += ['/', '(', ' * '.join(tail), ')']
+            r = ''.join(factors) or '1'
+            precedence = Precedence.PRODUCT if factors else Precedence.ATOM
+        elif self.op is Op.APPLY:
+            name, args, kwargs = self.data
+            if name is ArithOp.DIV and language is Language.C:
+                numer, denom = [arg.tostring(Precedence.PRODUCT,
+                                             language=language)
+                                for arg in args]
+                r = f'{numer} / {denom}'
+                precedence = Precedence.PRODUCT
+            else:
+                args = [arg.tostring(Precedence.TUPLE, language=language)
+                        for arg in args]
+                args += [k + '=' + v.tostring(Precedence.NONE)
+                         for k, v in kwargs.items()]
+                r = f'{name}({", ".join(args)})'
+                precedence = Precedence.ATOM
+        elif self.op is Op.INDEXING:
+            name = self.data[0]
+            args = [arg.tostring(Precedence.TUPLE, language=language)
+                    for arg in self.data[1:]]
+            r = f'{name}[{", ".join(args)}]'
+            precedence = Precedence.ATOM
+        elif self.op is Op.CONCAT:
+            args = [arg.tostring(Precedence.PRODUCT, language=language)
+                    for arg in self.data]
+            r = " // ".join(args)
+            precedence = Precedence.PRODUCT
+        elif self.op is Op.TERNARY:
+            cond, expr1, expr2 = [a.tostring(Precedence.TUPLE,
+                                             language=language)
+                                  for a in self.data]
+            if language is Language.C:
+                r = f'({cond}?{expr1}:{expr2})'
+            elif language is Language.Python:
+                r = f'({expr1} if {cond} else {expr2})'
+            elif language is Language.Fortran:
+                r = f'merge({expr1}, {expr2}, {cond})'
+            else:
+                raise NotImplementedError(
+                    f'tostring for {self.op} and {language}')
+            precedence = Precedence.ATOM
+        elif self.op is Op.REF:
+            r = '&' + self.data.tostring(Precedence.UNARY, language=language)
+            precedence = Precedence.UNARY
+        elif self.op is Op.DEREF:
+            r = '*' + self.data.tostring(Precedence.UNARY, language=language)
+            precedence = Precedence.UNARY
+        elif self.op is Op.RELATIONAL:
+            rop, left, right = self.data
+            precedence = (Precedence.EQ if rop in (RelOp.EQ, RelOp.NE)
+                          else Precedence.LT)
+            left = left.tostring(precedence, language=language)
+            right = right.tostring(precedence, language=language)
+            rop = rop.tostring(language=language)
+            r = f'{left} {rop} {right}'
+        else:
+            raise NotImplementedError(f'tostring for op {self.op}')
+        if parent_precedence.value < precedence.value:
+            # If parent precedence is higher than operand precedence,
+            # operand will be enclosed in parenthesis.
+            return '(' + r + ')'
+        return r
+
+    def __pos__(self):
+        return self
+
+    def __neg__(self):
+        return self * -1
+
+    def __add__(self, other):
+        other = as_expr(other)
+        if isinstance(other, Expr):
+            if self.op is other.op:
+                if self.op in (Op.INTEGER, Op.REAL):
+                    return as_number(
+                        self.data[0] + other.data[0],
+                        max(self.data[1], other.data[1]))
+                if self.op is Op.COMPLEX:
+                    r1, i1 = self.data
+                    r2, i2 = other.data
+                    return as_complex(r1 + r2, i1 + i2)
+                if self.op is Op.TERMS:
+                    r = Expr(self.op, dict(self.data))
+                    for k, v in other.data.items():
+                        _pairs_add(r.data, k, v)
+                    return normalize(r)
+            if self.op is Op.COMPLEX and other.op in (Op.INTEGER, Op.REAL):
+                return self + as_complex(other)
+            elif self.op in (Op.INTEGER, Op.REAL) and other.op is Op.COMPLEX:
+                return as_complex(self) + other
+            elif self.op is Op.REAL and other.op is Op.INTEGER:
+                return self + as_real(other, kind=self.data[1])
+            elif self.op is Op.INTEGER and other.op is Op.REAL:
+                return as_real(self, kind=other.data[1]) + other
+            return as_terms(self) + as_terms(other)
+        return NotImplemented
+
+    def __radd__(self, other):
+        if isinstance(other, number_types):
+            return as_number(other) + self
+        return NotImplemented
+
+    def __sub__(self, other):
+        return self + (-other)
+
+    def __rsub__(self, other):
+        if isinstance(other, number_types):
+            return as_number(other) - self
+        return NotImplemented
+
+    def __mul__(self, other):
+        other = as_expr(other)
+        if isinstance(other, Expr):
+            if self.op is other.op:
+                if self.op in (Op.INTEGER, Op.REAL):
+                    return as_number(self.data[0] * other.data[0],
+                                     max(self.data[1], other.data[1]))
+                elif self.op is Op.COMPLEX:
+                    r1, i1 = self.data
+                    r2, i2 = other.data
+                    return as_complex(r1 * r2 - i1 * i2, r1 * i2 + r2 * i1)
+
+                if self.op is Op.FACTORS:
+                    r = Expr(self.op, dict(self.data))
+                    for k, v in other.data.items():
+                        _pairs_add(r.data, k, v)
+                    return normalize(r)
+                elif self.op is Op.TERMS:
+                    r = Expr(self.op, {})
+                    for t1, c1 in self.data.items():
+                        for t2, c2 in other.data.items():
+                            _pairs_add(r.data, t1 * t2, c1 * c2)
+                    return normalize(r)
+
+            if self.op is Op.COMPLEX and other.op in (Op.INTEGER, Op.REAL):
+                return self * as_complex(other)
+            elif other.op is Op.COMPLEX and self.op in (Op.INTEGER, Op.REAL):
+                return as_complex(self) * other
+            elif self.op is Op.REAL and other.op is Op.INTEGER:
+                return self * as_real(other, kind=self.data[1])
+            elif self.op is Op.INTEGER and other.op is Op.REAL:
+                return as_real(self, kind=other.data[1]) * other
+
+            if self.op is Op.TERMS:
+                return self * as_terms(other)
+            elif other.op is Op.TERMS:
+                return as_terms(self) * other
+
+            return as_factors(self) * as_factors(other)
+        return NotImplemented
+
+    def __rmul__(self, other):
+        if isinstance(other, number_types):
+            return as_number(other) * self
+        return NotImplemented
+
+    def __pow__(self, other):
+        other = as_expr(other)
+        if isinstance(other, Expr):
+            if other.op is Op.INTEGER:
+                exponent = other.data[0]
+                # TODO: other kind not used
+                if exponent == 0:
+                    return as_number(1)
+                if exponent == 1:
+                    return self
+                if exponent > 0:
+                    if self.op is Op.FACTORS:
+                        r = Expr(self.op, {})
+                        for k, v in self.data.items():
+                            r.data[k] = v * exponent
+                        return normalize(r)
+                    return self * (self ** (exponent - 1))
+                elif exponent != -1:
+                    return (self ** (-exponent)) ** -1
+                return Expr(Op.FACTORS, {self: exponent})
+            return as_apply(ArithOp.POW, self, other)
+        return NotImplemented
+
+    def __truediv__(self, other):
+        other = as_expr(other)
+        if isinstance(other, Expr):
+            # Fortran / is different from Python /:
+            # - `/` is a truncate operation for integer operands
+            return normalize(as_apply(ArithOp.DIV, self, other))
+        return NotImplemented
+
+    def __rtruediv__(self, other):
+        other = as_expr(other)
+        if isinstance(other, Expr):
+            return other / self
+        return NotImplemented
+
+    def __floordiv__(self, other):
+        other = as_expr(other)
+        if isinstance(other, Expr):
+            # Fortran // is different from Python //:
+            # - `//` is a concatenate operation for string operands
+            return normalize(Expr(Op.CONCAT, (self, other)))
+        return NotImplemented
+
+    def __rfloordiv__(self, other):
+        other = as_expr(other)
+        if isinstance(other, Expr):
+            return other // self
+        return NotImplemented
+
+    def __call__(self, *args, **kwargs):
+        # In Fortran, parenthesis () are use for both function call as
+        # well as indexing operations.
+        #
+        # TODO: implement a method for deciding when __call__ should
+        # return an INDEXING expression.
+        return as_apply(self, *map(as_expr, args),
+                        **dict((k, as_expr(v)) for k, v in kwargs.items()))
+
+    def __getitem__(self, index):
+        # Provided to support C indexing operations that .pyf files
+        # may contain.
+        index = as_expr(index)
+        if not isinstance(index, tuple):
+            index = index,
+        if len(index) > 1:
+            ewarn(f'C-index should be a single expression but got `{index}`')
+        return Expr(Op.INDEXING, (self,) + index)
+
+    def substitute(self, symbols_map):
+        """Recursively substitute symbols with values in symbols map.
+
+        Symbols map is a dictionary of symbol-expression pairs.
+        """
+        if self.op is Op.SYMBOL:
+            value = symbols_map.get(self)
+            if value is None:
+                return self
+            m = re.match(r'\A(@__f2py_PARENTHESIS_(\w+)_\d+@)\Z', self.data)
+            if m:
+                # complement to fromstring method
+                items, paren = m.groups()
+                if paren in ['ROUNDDIV', 'SQUARE']:
+                    return as_array(value)
+                assert paren == 'ROUND', (paren, value)
+            return value
+        if self.op in (Op.INTEGER, Op.REAL, Op.STRING):
+            return self
+        if self.op in (Op.ARRAY, Op.COMPLEX):
+            return Expr(self.op, tuple(item.substitute(symbols_map)
+                                       for item in self.data))
+        if self.op is Op.CONCAT:
+            return normalize(Expr(self.op, tuple(item.substitute(symbols_map)
+                                                 for item in self.data)))
+        if self.op is Op.TERMS:
+            r = None
+            for term, coeff in self.data.items():
+                if r is None:
+                    r = term.substitute(symbols_map) * coeff
+                else:
+                    r += term.substitute(symbols_map) * coeff
+            if r is None:
+                ewarn('substitute: empty TERMS expression interpreted as'
+                      ' int-literal 0')
+                return as_number(0)
+            return r
+        if self.op is Op.FACTORS:
+            r = None
+            for base, exponent in self.data.items():
+                if r is None:
+                    r = base.substitute(symbols_map) ** exponent
+                else:
+                    r *= base.substitute(symbols_map) ** exponent
+            if r is None:
+                ewarn('substitute: empty FACTORS expression interpreted'
+                      ' as int-literal 1')
+                return as_number(1)
+            return r
+        if self.op is Op.APPLY:
+            target, args, kwargs = self.data
+            if isinstance(target, Expr):
+                target = target.substitute(symbols_map)
+            args = tuple(a.substitute(symbols_map) for a in args)
+            kwargs = dict((k, v.substitute(symbols_map))
+                          for k, v in kwargs.items())
+            return normalize(Expr(self.op, (target, args, kwargs)))
+        if self.op is Op.INDEXING:
+            func = self.data[0]
+            if isinstance(func, Expr):
+                func = func.substitute(symbols_map)
+            args = tuple(a.substitute(symbols_map) for a in self.data[1:])
+            return normalize(Expr(self.op, (func,) + args))
+        if self.op is Op.TERNARY:
+            operands = tuple(a.substitute(symbols_map) for a in self.data)
+            return normalize(Expr(self.op, operands))
+        if self.op in (Op.REF, Op.DEREF):
+            return normalize(Expr(self.op, self.data.substitute(symbols_map)))
+        if self.op is Op.RELATIONAL:
+            rop, left, right = self.data
+            left = left.substitute(symbols_map)
+            right = right.substitute(symbols_map)
+            return normalize(Expr(self.op, (rop, left, right)))
+        raise NotImplementedError(f'substitute method for {self.op}: {self!r}')
+
+    def traverse(self, visit, *args, **kwargs):
+        """Traverse expression tree with visit function.
+
+        The visit function is applied to an expression with given args
+        and kwargs.
+
+        Traverse call returns an expression returned by visit when not
+        None, otherwise return a new normalized expression with
+        traverse-visit sub-expressions.
+        """
+        result = visit(self, *args, **kwargs)
+        if result is not None:
+            return result
+
+        if self.op in (Op.INTEGER, Op.REAL, Op.STRING, Op.SYMBOL):
+            return self
+        elif self.op in (Op.COMPLEX, Op.ARRAY, Op.CONCAT, Op.TERNARY):
+            return normalize(Expr(self.op, tuple(
+                item.traverse(visit, *args, **kwargs)
+                for item in self.data)))
+        elif self.op in (Op.TERMS, Op.FACTORS):
+            data = {}
+            for k, v in self.data.items():
+                k = k.traverse(visit, *args, **kwargs)
+                v = (v.traverse(visit, *args, **kwargs)
+                     if isinstance(v, Expr) else v)
+                if k in data:
+                    v = data[k] + v
+                data[k] = v
+            return normalize(Expr(self.op, data))
+        elif self.op is Op.APPLY:
+            obj = self.data[0]
+            func = (obj.traverse(visit, *args, **kwargs)
+                    if isinstance(obj, Expr) else obj)
+            operands = tuple(operand.traverse(visit, *args, **kwargs)
+                             for operand in self.data[1])
+            kwoperands = dict((k, v.traverse(visit, *args, **kwargs))
+                              for k, v in self.data[2].items())
+            return normalize(Expr(self.op, (func, operands, kwoperands)))
+        elif self.op is Op.INDEXING:
+            obj = self.data[0]
+            obj = (obj.traverse(visit, *args, **kwargs)
+                   if isinstance(obj, Expr) else obj)
+            indices = tuple(index.traverse(visit, *args, **kwargs)
+                            for index in self.data[1:])
+            return normalize(Expr(self.op, (obj,) + indices))
+        elif self.op in (Op.REF, Op.DEREF):
+            return normalize(Expr(self.op,
+                                  self.data.traverse(visit, *args, **kwargs)))
+        elif self.op is Op.RELATIONAL:
+            rop, left, right = self.data
+            left = left.traverse(visit, *args, **kwargs)
+            right = right.traverse(visit, *args, **kwargs)
+            return normalize(Expr(self.op, (rop, left, right)))
+        raise NotImplementedError(f'traverse method for {self.op}')
+
+    def contains(self, other):
+        """Check if self contains other.
+        """
+        found = []
+
+        def visit(expr, found=found):
+            if found:
+                return expr
+            elif expr == other:
+                found.append(1)
+                return expr
+
+        self.traverse(visit)
+
+        return len(found) != 0
+
+    def symbols(self):
+        """Return a set of symbols contained in self.
+        """
+        found = set()
+
+        def visit(expr, found=found):
+            if expr.op is Op.SYMBOL:
+                found.add(expr)
+
+        self.traverse(visit)
+
+        return found
+
+    def polynomial_atoms(self):
+        """Return a set of expressions used as atoms in polynomial self.
+        """
+        found = set()
+
+        def visit(expr, found=found):
+            if expr.op is Op.FACTORS:
+                for b in expr.data:
+                    b.traverse(visit)
+                return expr
+            if expr.op in (Op.TERMS, Op.COMPLEX):
+                return
+            if expr.op is Op.APPLY and isinstance(expr.data[0], ArithOp):
+                if expr.data[0] is ArithOp.POW:
+                    expr.data[1][0].traverse(visit)
+                    return expr
+                return
+            if expr.op in (Op.INTEGER, Op.REAL):
+                return expr
+
+            found.add(expr)
+
+            if expr.op in (Op.INDEXING, Op.APPLY):
+                return expr
+
+        self.traverse(visit)
+
+        return found
+
+    def linear_solve(self, symbol):
+        """Return a, b such that a * symbol + b == self.
+
+        If self is not linear with respect to symbol, raise RuntimeError.
+        """
+        b = self.substitute({symbol: as_number(0)})
+        ax = self - b
+        a = ax.substitute({symbol: as_number(1)})
+
+        zero, _ = as_numer_denom(a * symbol - ax)
+
+        if zero != as_number(0):
+            raise RuntimeError(f'not a {symbol}-linear equation:'
+                               f' {a} * {symbol} + {b} == {self}')
+        return a, b
+
+
+def normalize(obj):
+    """Normalize Expr and apply basic evaluation methods.
+    """
+    if not isinstance(obj, Expr):
+        return obj
+
+    if obj.op is Op.TERMS:
+        d = {}
+        for t, c in obj.data.items():
+            if c == 0:
+                continue
+            if t.op is Op.COMPLEX and c != 1:
+                t = t * c
+                c = 1
+            if t.op is Op.TERMS:
+                for t1, c1 in t.data.items():
+                    _pairs_add(d, t1, c1 * c)
+            else:
+                _pairs_add(d, t, c)
+        if len(d) == 0:
+            # TODO: determine correct kind
+            return as_number(0)
+        elif len(d) == 1:
+            (t, c), = d.items()
+            if c == 1:
+                return t
+        return Expr(Op.TERMS, d)
+
+    if obj.op is Op.FACTORS:
+        coeff = 1
+        d = {}
+        for b, e in obj.data.items():
+            if e == 0:
+                continue
+            if b.op is Op.TERMS and isinstance(e, integer_types) and e > 1:
+                # expand integer powers of sums
+                b = b * (b ** (e - 1))
+                e = 1
+
+            if b.op in (Op.INTEGER, Op.REAL):
+                if e == 1:
+                    coeff *= b.data[0]
+                elif e > 0:
+                    coeff *= b.data[0] ** e
+                else:
+                    _pairs_add(d, b, e)
+            elif b.op is Op.FACTORS:
+                if e > 0 and isinstance(e, integer_types):
+                    for b1, e1 in b.data.items():
+                        _pairs_add(d, b1, e1 * e)
+                else:
+                    _pairs_add(d, b, e)
+            else:
+                _pairs_add(d, b, e)
+        if len(d) == 0 or coeff == 0:
+            # TODO: determine correct kind
+            assert isinstance(coeff, number_types)
+            return as_number(coeff)
+        elif len(d) == 1:
+            (b, e), = d.items()
+            if e == 1:
+                t = b
+            else:
+                t = Expr(Op.FACTORS, d)
+            if coeff == 1:
+                return t
+            return Expr(Op.TERMS, {t: coeff})
+        elif coeff == 1:
+            return Expr(Op.FACTORS, d)
+        else:
+            return Expr(Op.TERMS, {Expr(Op.FACTORS, d): coeff})
+
+    if obj.op is Op.APPLY and obj.data[0] is ArithOp.DIV:
+        dividend, divisor = obj.data[1]
+        t1, c1 = as_term_coeff(dividend)
+        t2, c2 = as_term_coeff(divisor)
+        if isinstance(c1, integer_types) and isinstance(c2, integer_types):
+            g = gcd(c1, c2)
+            c1, c2 = c1//g, c2//g
+        else:
+            c1, c2 = c1/c2, 1
+
+        if t1.op is Op.APPLY and t1.data[0] is ArithOp.DIV:
+            numer = t1.data[1][0] * c1
+            denom = t1.data[1][1] * t2 * c2
+            return as_apply(ArithOp.DIV, numer, denom)
+
+        if t2.op is Op.APPLY and t2.data[0] is ArithOp.DIV:
+            numer = t2.data[1][1] * t1 * c1
+            denom = t2.data[1][0] * c2
+            return as_apply(ArithOp.DIV, numer, denom)
+
+        d = dict(as_factors(t1).data)
+        for b, e in as_factors(t2).data.items():
+            _pairs_add(d, b, -e)
+        numer, denom = {}, {}
+        for b, e in d.items():
+            if e > 0:
+                numer[b] = e
+            else:
+                denom[b] = -e
+        numer = normalize(Expr(Op.FACTORS, numer)) * c1
+        denom = normalize(Expr(Op.FACTORS, denom)) * c2
+
+        if denom.op in (Op.INTEGER, Op.REAL) and denom.data[0] == 1:
+            # TODO: denom kind not used
+            return numer
+        return as_apply(ArithOp.DIV, numer, denom)
+
+    if obj.op is Op.CONCAT:
+        lst = [obj.data[0]]
+        for s in obj.data[1:]:
+            last = lst[-1]
+            if (
+                    last.op is Op.STRING
+                    and s.op is Op.STRING
+                    and last.data[0][0] in '"\''
+                    and s.data[0][0] == last.data[0][-1]
+            ):
+                new_last = as_string(last.data[0][:-1] + s.data[0][1:],
+                                     max(last.data[1], s.data[1]))
+                lst[-1] = new_last
+            else:
+                lst.append(s)
+        if len(lst) == 1:
+            return lst[0]
+        return Expr(Op.CONCAT, tuple(lst))
+
+    if obj.op is Op.TERNARY:
+        cond, expr1, expr2 = map(normalize, obj.data)
+        if cond.op is Op.INTEGER:
+            return expr1 if cond.data[0] else expr2
+        return Expr(Op.TERNARY, (cond, expr1, expr2))
+
+    return obj
+
+
+def as_expr(obj):
+    """Convert non-Expr objects to Expr objects.
+    """
+    if isinstance(obj, complex):
+        return as_complex(obj.real, obj.imag)
+    if isinstance(obj, number_types):
+        return as_number(obj)
+    if isinstance(obj, str):
+        # STRING expression holds string with boundary quotes, hence
+        # applying repr:
+        return as_string(repr(obj))
+    if isinstance(obj, tuple):
+        return tuple(map(as_expr, obj))
+    return obj
+
+
+def as_symbol(obj):
+    """Return object as SYMBOL expression (variable or unparsed expression).
+    """
+    return Expr(Op.SYMBOL, obj)
+
+
+def as_number(obj, kind=4):
+    """Return object as INTEGER or REAL constant.
+    """
+    if isinstance(obj, int):
+        return Expr(Op.INTEGER, (obj, kind))
+    if isinstance(obj, float):
+        return Expr(Op.REAL, (obj, kind))
+    if isinstance(obj, Expr):
+        if obj.op in (Op.INTEGER, Op.REAL):
+            return obj
+    raise OpError(f'cannot convert {obj} to INTEGER or REAL constant')
+
+
+def as_integer(obj, kind=4):
+    """Return object as INTEGER constant.
+    """
+    if isinstance(obj, int):
+        return Expr(Op.INTEGER, (obj, kind))
+    if isinstance(obj, Expr):
+        if obj.op is Op.INTEGER:
+            return obj
+    raise OpError(f'cannot convert {obj} to INTEGER constant')
+
+
+def as_real(obj, kind=4):
+    """Return object as REAL constant.
+    """
+    if isinstance(obj, int):
+        return Expr(Op.REAL, (float(obj), kind))
+    if isinstance(obj, float):
+        return Expr(Op.REAL, (obj, kind))
+    if isinstance(obj, Expr):
+        if obj.op is Op.REAL:
+            return obj
+        elif obj.op is Op.INTEGER:
+            return Expr(Op.REAL, (float(obj.data[0]), kind))
+    raise OpError(f'cannot convert {obj} to REAL constant')
+
+
+def as_string(obj, kind=1):
+    """Return object as STRING expression (string literal constant).
+    """
+    return Expr(Op.STRING, (obj, kind))
+
+
+def as_array(obj):
+    """Return object as ARRAY expression (array constant).
+    """
+    if isinstance(obj, Expr):
+        obj = obj,
+    return Expr(Op.ARRAY, obj)
+
+
+def as_complex(real, imag=0):
+    """Return object as COMPLEX expression (complex literal constant).
+    """
+    return Expr(Op.COMPLEX, (as_expr(real), as_expr(imag)))
+
+
+def as_apply(func, *args, **kwargs):
+    """Return object as APPLY expression (function call, constructor, etc.)
+    """
+    return Expr(Op.APPLY,
+                (func, tuple(map(as_expr, args)),
+                 dict((k, as_expr(v)) for k, v in kwargs.items())))
+
+
+def as_ternary(cond, expr1, expr2):
+    """Return object as TERNARY expression (cond?expr1:expr2).
+    """
+    return Expr(Op.TERNARY, (cond, expr1, expr2))
+
+
+def as_ref(expr):
+    """Return object as referencing expression.
+    """
+    return Expr(Op.REF, expr)
+
+
+def as_deref(expr):
+    """Return object as dereferencing expression.
+    """
+    return Expr(Op.DEREF, expr)
+
+
+def as_eq(left, right):
+    return Expr(Op.RELATIONAL, (RelOp.EQ, left, right))
+
+
+def as_ne(left, right):
+    return Expr(Op.RELATIONAL, (RelOp.NE, left, right))
+
+
+def as_lt(left, right):
+    return Expr(Op.RELATIONAL, (RelOp.LT, left, right))
+
+
+def as_le(left, right):
+    return Expr(Op.RELATIONAL, (RelOp.LE, left, right))
+
+
+def as_gt(left, right):
+    return Expr(Op.RELATIONAL, (RelOp.GT, left, right))
+
+
+def as_ge(left, right):
+    return Expr(Op.RELATIONAL, (RelOp.GE, left, right))
+
+
+def as_terms(obj):
+    """Return expression as TERMS expression.
+    """
+    if isinstance(obj, Expr):
+        obj = normalize(obj)
+        if obj.op is Op.TERMS:
+            return obj
+        if obj.op is Op.INTEGER:
+            return Expr(Op.TERMS, {as_integer(1, obj.data[1]): obj.data[0]})
+        if obj.op is Op.REAL:
+            return Expr(Op.TERMS, {as_real(1, obj.data[1]): obj.data[0]})
+        return Expr(Op.TERMS, {obj: 1})
+    raise OpError(f'cannot convert {type(obj)} to terms Expr')
+
+
+def as_factors(obj):
+    """Return expression as FACTORS expression.
+    """
+    if isinstance(obj, Expr):
+        obj = normalize(obj)
+        if obj.op is Op.FACTORS:
+            return obj
+        if obj.op is Op.TERMS:
+            if len(obj.data) == 1:
+                (term, coeff), = obj.data.items()
+                if coeff == 1:
+                    return Expr(Op.FACTORS, {term: 1})
+                return Expr(Op.FACTORS, {term: 1, Expr.number(coeff): 1})
+        if ((obj.op is Op.APPLY
+             and obj.data[0] is ArithOp.DIV
+             and not obj.data[2])):
+            return Expr(Op.FACTORS, {obj.data[1][0]: 1, obj.data[1][1]: -1})
+        return Expr(Op.FACTORS, {obj: 1})
+    raise OpError(f'cannot convert {type(obj)} to terms Expr')
+
+
+def as_term_coeff(obj):
+    """Return expression as term-coefficient pair.
+    """
+    if isinstance(obj, Expr):
+        obj = normalize(obj)
+        if obj.op is Op.INTEGER:
+            return as_integer(1, obj.data[1]), obj.data[0]
+        if obj.op is Op.REAL:
+            return as_real(1, obj.data[1]), obj.data[0]
+        if obj.op is Op.TERMS:
+            if len(obj.data) == 1:
+                (term, coeff), = obj.data.items()
+                return term, coeff
+            # TODO: find common divisor of coefficients
+        if obj.op is Op.APPLY and obj.data[0] is ArithOp.DIV:
+            t, c = as_term_coeff(obj.data[1][0])
+            return as_apply(ArithOp.DIV, t, obj.data[1][1]), c
+        return obj, 1
+    raise OpError(f'cannot convert {type(obj)} to term and coeff')
+
+
+def as_numer_denom(obj):
+    """Return expression as numer-denom pair.
+    """
+    if isinstance(obj, Expr):
+        obj = normalize(obj)
+        if obj.op in (Op.INTEGER, Op.REAL, Op.COMPLEX, Op.SYMBOL,
+                      Op.INDEXING, Op.TERNARY):
+            return obj, as_number(1)
+        elif obj.op is Op.APPLY:
+            if obj.data[0] is ArithOp.DIV and not obj.data[2]:
+                numers, denoms = map(as_numer_denom, obj.data[1])
+                return numers[0] * denoms[1], numers[1] * denoms[0]
+            return obj, as_number(1)
+        elif obj.op is Op.TERMS:
+            numers, denoms = [], []
+            for term, coeff in obj.data.items():
+                n, d = as_numer_denom(term)
+                n = n * coeff
+                numers.append(n)
+                denoms.append(d)
+            numer, denom = as_number(0), as_number(1)
+            for i in range(len(numers)):
+                n = numers[i]
+                for j in range(len(numers)):
+                    if i != j:
+                        n *= denoms[j]
+                numer += n
+                denom *= denoms[i]
+            if denom.op in (Op.INTEGER, Op.REAL) and denom.data[0] < 0:
+                numer, denom = -numer, -denom
+            return numer, denom
+        elif obj.op is Op.FACTORS:
+            numer, denom = as_number(1), as_number(1)
+            for b, e in obj.data.items():
+                bnumer, bdenom = as_numer_denom(b)
+                if e > 0:
+                    numer *= bnumer ** e
+                    denom *= bdenom ** e
+                elif e < 0:
+                    numer *= bdenom ** (-e)
+                    denom *= bnumer ** (-e)
+            return numer, denom
+    raise OpError(f'cannot convert {type(obj)} to numer and denom')
+
+
+def _counter():
+    # Used internally to generate unique dummy symbols
+    counter = 0
+    while True:
+        counter += 1
+        yield counter
+
+
+COUNTER = _counter()
+
+
+def eliminate_quotes(s):
+    """Replace quoted substrings of input string.
+
+    Return a new string and a mapping of replacements.
+    """
+    d = {}
+
+    def repl(m):
+        kind, value = m.groups()[:2]
+        if kind:
+            # remove trailing underscore
+            kind = kind[:-1]
+        p = {"'": "SINGLE", '"': "DOUBLE"}[value[0]]
+        k = f'{kind}@__f2py_QUOTES_{p}_{COUNTER.__next__()}@'
+        d[k] = value
+        return k
+
+    new_s = re.sub(r'({kind}_|)({single_quoted}|{double_quoted})'.format(
+        kind=r'\w[\w\d_]*',
+        single_quoted=r"('([^'\\]|(\\.))*')",
+        double_quoted=r'("([^"\\]|(\\.))*")'),
+        repl, s)
+
+    assert '"' not in new_s
+    assert "'" not in new_s
+
+    return new_s, d
+
+
+def insert_quotes(s, d):
+    """Inverse of eliminate_quotes.
+    """
+    for k, v in d.items():
+        kind = k[:k.find('@')]
+        if kind:
+            kind += '_'
+        s = s.replace(k, kind + v)
+    return s
+
+
+def replace_parenthesis(s):
+    """Replace substrings of input that are enclosed in parenthesis.
+
+    Return a new string and a mapping of replacements.
+    """
+    # Find a parenthesis pair that appears first.
+
+    # Fortran deliminator are `(`, `)`, `[`, `]`, `(/', '/)`, `/`.
+    # We don't handle `/` deliminator because it is not a part of an
+    # expression.
+    left, right = None, None
+    mn_i = len(s)
+    for left_, right_ in (('(/', '/)'),
+                          '()',
+                          '{}',  # to support C literal structs
+                          '[]'):
+        i = s.find(left_)
+        if i == -1:
+            continue
+        if i < mn_i:
+            mn_i = i
+            left, right = left_, right_
+
+    if left is None:
+        return s, {}
+
+    i = mn_i
+    j = s.find(right, i)
+
+    while s.count(left, i + 1, j) != s.count(right, i + 1, j):
+        j = s.find(right, j + 1)
+        if j == -1:
+            raise ValueError(f'Mismatch of {left+right} parenthesis in {s!r}')
+
+    p = {'(': 'ROUND', '[': 'SQUARE', '{': 'CURLY', '(/': 'ROUNDDIV'}[left]
+
+    k = f'@__f2py_PARENTHESIS_{p}_{COUNTER.__next__()}@'
+    v = s[i+len(left):j]
+    r, d = replace_parenthesis(s[j+len(right):])
+    d[k] = v
+    return s[:i] + k + r, d
+
+
+def _get_parenthesis_kind(s):
+    assert s.startswith('@__f2py_PARENTHESIS_'), s
+    return s.split('_')[4]
+
+
+def unreplace_parenthesis(s, d):
+    """Inverse of replace_parenthesis.
+    """
+    for k, v in d.items():
+        p = _get_parenthesis_kind(k)
+        left = dict(ROUND='(', SQUARE='[', CURLY='{', ROUNDDIV='(/')[p]
+        right = dict(ROUND=')', SQUARE=']', CURLY='}', ROUNDDIV='/)')[p]
+        s = s.replace(k, left + v + right)
+    return s
+
+
+def fromstring(s, language=Language.C):
+    """Create an expression from a string.
+
+    This is a "lazy" parser, that is, only arithmetic operations are
+    resolved, non-arithmetic operations are treated as symbols.
+    """
+    r = _FromStringWorker(language=language).parse(s)
+    if isinstance(r, Expr):
+        return r
+    raise ValueError(f'failed to parse `{s}` to Expr instance: got `{r}`')
+
+
+class _Pair:
+    # Internal class to represent a pair of expressions
+
+    def __init__(self, left, right):
+        self.left = left
+        self.right = right
+
+    def substitute(self, symbols_map):
+        left, right = self.left, self.right
+        if isinstance(left, Expr):
+            left = left.substitute(symbols_map)
+        if isinstance(right, Expr):
+            right = right.substitute(symbols_map)
+        return _Pair(left, right)
+
+    def __repr__(self):
+        return f'{type(self).__name__}({self.left}, {self.right})'
+
+
+class _FromStringWorker:
+
+    def __init__(self, language=Language.C):
+        self.original = None
+        self.quotes_map = None
+        self.language = language
+
+    def finalize_string(self, s):
+        return insert_quotes(s, self.quotes_map)
+
+    def parse(self, inp):
+        self.original = inp
+        unquoted, self.quotes_map = eliminate_quotes(inp)
+        return self.process(unquoted)
+
+    def process(self, s, context='expr'):
+        """Parse string within the given context.
+
+        The context may define the result in case of ambiguous
+        expressions. For instance, consider expressions `f(x, y)` and
+        `(x, y) + (a, b)` where `f` is a function and pair `(x, y)`
+        denotes complex number. Specifying context as "args" or
+        "expr", the subexpression `(x, y)` will be parse to an
+        argument list or to a complex number, respectively.
+        """
+        if isinstance(s, (list, tuple)):
+            return type(s)(self.process(s_, context) for s_ in s)
+
+        assert isinstance(s, str), (type(s), s)
+
+        # replace subexpressions in parenthesis with f2py @-names
+        r, raw_symbols_map = replace_parenthesis(s)
+        r = r.strip()
+
+        def restore(r):
+            # restores subexpressions marked with f2py @-names
+            if isinstance(r, (list, tuple)):
+                return type(r)(map(restore, r))
+            return unreplace_parenthesis(r, raw_symbols_map)
+
+        # comma-separated tuple
+        if ',' in r:
+            operands = restore(r.split(','))
+            if context == 'args':
+                return tuple(self.process(operands))
+            if context == 'expr':
+                if len(operands) == 2:
+                    # complex number literal
+                    return as_complex(*self.process(operands))
+            raise NotImplementedError(
+                f'parsing comma-separated list (context={context}): {r}')
+
+        # ternary operation
+        m = re.match(r'\A([^?]+)[?]([^:]+)[:](.+)\Z', r)
+        if m:
+            assert context == 'expr', context
+            oper, expr1, expr2 = restore(m.groups())
+            oper = self.process(oper)
+            expr1 = self.process(expr1)
+            expr2 = self.process(expr2)
+            return as_ternary(oper, expr1, expr2)
+
+        # relational expression
+        if self.language is Language.Fortran:
+            m = re.match(
+                r'\A(.+)\s*[.](eq|ne|lt|le|gt|ge)[.]\s*(.+)\Z', r, re.I)
+        else:
+            m = re.match(
+                r'\A(.+)\s*([=][=]|[!][=]|[<][=]|[<]|[>][=]|[>])\s*(.+)\Z', r)
+        if m:
+            left, rop, right = m.groups()
+            if self.language is Language.Fortran:
+                rop = '.' + rop + '.'
+            left, right = self.process(restore((left, right)))
+            rop = RelOp.fromstring(rop, language=self.language)
+            return Expr(Op.RELATIONAL, (rop, left, right))
+
+        # keyword argument
+        m = re.match(r'\A(\w[\w\d_]*)\s*[=](.*)\Z', r)
+        if m:
+            keyname, value = m.groups()
+            value = restore(value)
+            return _Pair(keyname, self.process(value))
+
+        # addition/subtraction operations
+        operands = re.split(r'((?<!\d[edED])[+-])', r)
+        if len(operands) > 1:
+            result = self.process(restore(operands[0] or '0'))
+            for op, operand in zip(operands[1::2], operands[2::2]):
+                operand = self.process(restore(operand))
+                op = op.strip()
+                if op == '+':
+                    result += operand
+                else:
+                    assert op == '-'
+                    result -= operand
+            return result
+
+        # string concatenate operation
+        if self.language is Language.Fortran and '//' in r:
+            operands = restore(r.split('//'))
+            return Expr(Op.CONCAT,
+                        tuple(self.process(operands)))
+
+        # multiplication/division operations
+        operands = re.split(r'(?<=[@\w\d_])\s*([*]|/)',
+                            (r if self.language is Language.C
+                             else r.replace('**', '@__f2py_DOUBLE_STAR@')))
+        if len(operands) > 1:
+            operands = restore(operands)
+            if self.language is not Language.C:
+                operands = [operand.replace('@__f2py_DOUBLE_STAR@', '**')
+                            for operand in operands]
+            # Expression is an arithmetic product
+            result = self.process(operands[0])
+            for op, operand in zip(operands[1::2], operands[2::2]):
+                operand = self.process(operand)
+                op = op.strip()
+                if op == '*':
+                    result *= operand
+                else:
+                    assert op == '/'
+                    result /= operand
+            return result
+
+        # referencing/dereferencing
+        if r.startswith('*') or r.startswith('&'):
+            op = {'*': Op.DEREF, '&': Op.REF}[r[0]]
+            operand = self.process(restore(r[1:]))
+            return Expr(op, operand)
+
+        # exponentiation operations
+        if self.language is not Language.C and '**' in r:
+            operands = list(reversed(restore(r.split('**'))))
+            result = self.process(operands[0])
+            for operand in operands[1:]:
+                operand = self.process(operand)
+                result = operand ** result
+            return result
+
+        # int-literal-constant
+        m = re.match(r'\A({digit_string})({kind}|)\Z'.format(
+            digit_string=r'\d+',
+            kind=r'_(\d+|\w[\w\d_]*)'), r)
+        if m:
+            value, _, kind = m.groups()
+            if kind and kind.isdigit():
+                kind = int(kind)
+            return as_integer(int(value), kind or 4)
+
+        # real-literal-constant
+        m = re.match(r'\A({significant}({exponent}|)|\d+{exponent})({kind}|)\Z'
+                     .format(
+                         significant=r'[.]\d+|\d+[.]\d*',
+                         exponent=r'[edED][+-]?\d+',
+                         kind=r'_(\d+|\w[\w\d_]*)'), r)
+        if m:
+            value, _, _, kind = m.groups()
+            if kind and kind.isdigit():
+                kind = int(kind)
+            value = value.lower()
+            if 'd' in value:
+                return as_real(float(value.replace('d', 'e')), kind or 8)
+            return as_real(float(value), kind or 4)
+
+        # string-literal-constant with kind parameter specification
+        if r in self.quotes_map:
+            kind = r[:r.find('@')]
+            return as_string(self.quotes_map[r], kind or 1)
+
+        # array constructor or literal complex constant or
+        # parenthesized expression
+        if r in raw_symbols_map:
+            paren = _get_parenthesis_kind(r)
+            items = self.process(restore(raw_symbols_map[r]),
+                                 'expr' if paren == 'ROUND' else 'args')
+            if paren == 'ROUND':
+                if isinstance(items, Expr):
+                    return items
+            if paren in ['ROUNDDIV', 'SQUARE']:
+                # Expression is a array constructor
+                if isinstance(items, Expr):
+                    items = (items,)
+                return as_array(items)
+
+        # function call/indexing
+        m = re.match(r'\A(.+)\s*(@__f2py_PARENTHESIS_(ROUND|SQUARE)_\d+@)\Z',
+                     r)
+        if m:
+            target, args, paren = m.groups()
+            target = self.process(restore(target))
+            args = self.process(restore(args)[1:-1], 'args')
+            if not isinstance(args, tuple):
+                args = args,
+            if paren == 'ROUND':
+                kwargs = dict((a.left, a.right) for a in args
+                              if isinstance(a, _Pair))
+                args = tuple(a for a in args if not isinstance(a, _Pair))
+                # Warning: this could also be Fortran indexing operation..
+                return as_apply(target, *args, **kwargs)
+            else:
+                # Expression is a C/Python indexing operation
+                # (e.g. used in .pyf files)
+                assert paren == 'SQUARE'
+                return target[args]
+
+        # Fortran standard conforming identifier
+        m = re.match(r'\A\w[\w\d_]*\Z', r)
+        if m:
+            return as_symbol(r)
+
+        # fall-back to symbol
+        r = self.finalize_string(restore(r))
+        ewarn(
+            f'fromstring: treating {r!r} as symbol (original={self.original})')
+        return as_symbol(r)
diff --git a/.venv/lib/python3.12/site-packages/numpy/f2py/tests/__init__.py b/.venv/lib/python3.12/site-packages/numpy/f2py/tests/__init__.py
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/numpy/f2py/tests/__init__.py
diff --git a/.venv/lib/python3.12/site-packages/numpy/f2py/tests/src/abstract_interface/foo.f90 b/.venv/lib/python3.12/site-packages/numpy/f2py/tests/src/abstract_interface/foo.f90
new file mode 100644
index 00000000..76d16aae
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/numpy/f2py/tests/src/abstract_interface/foo.f90
@@ -0,0 +1,34 @@
+module ops_module
+
+  abstract interface
+    subroutine op(x, y, z)
+      integer, intent(in) :: x, y
+      integer, intent(out) :: z
+    end subroutine
+  end interface
+
+contains
+
+  subroutine foo(x, y, r1, r2)
+    integer, intent(in) :: x, y
+    integer, intent(out) :: r1, r2
+    procedure (op) add1, add2
+    procedure (op), pointer::p
+    p=>add1
+    call p(x, y, r1)
+    p=>add2
+    call p(x, y, r2)
+  end subroutine
+end module
+
+subroutine add1(x, y, z)
+  integer, intent(in) :: x, y
+  integer, intent(out) :: z
+  z = x + y
+end subroutine
+
+subroutine add2(x, y, z)
+  integer, intent(in) :: x, y
+  integer, intent(out) :: z
+  z = x + 2 * y
+end subroutine
diff --git a/.venv/lib/python3.12/site-packages/numpy/f2py/tests/src/abstract_interface/gh18403_mod.f90 b/.venv/lib/python3.12/site-packages/numpy/f2py/tests/src/abstract_interface/gh18403_mod.f90
new file mode 100644
index 00000000..36791e46
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/numpy/f2py/tests/src/abstract_interface/gh18403_mod.f90
@@ -0,0 +1,6 @@
+module test
+  abstract interface
+    subroutine foo()
+    end subroutine
+  end interface
+end module test
diff --git a/.venv/lib/python3.12/site-packages/numpy/f2py/tests/src/array_from_pyobj/wrapmodule.c b/.venv/lib/python3.12/site-packages/numpy/f2py/tests/src/array_from_pyobj/wrapmodule.c
new file mode 100644
index 00000000..9a8b4a75
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/numpy/f2py/tests/src/array_from_pyobj/wrapmodule.c
@@ -0,0 +1,230 @@
+/*
+ * This file was auto-generated with f2py (version:2_1330) and hand edited by
+ * Pearu for testing purposes.  Do not edit this file unless you know what you
+ * are doing!!!
+ */
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*********************** See f2py2e/cfuncs.py: includes ***********************/
+
+#define PY_SSIZE_T_CLEAN
+#include <Python.h>
+#include "fortranobject.h"
+#include <math.h>
+
+static PyObject *wrap_error;
+static PyObject *wrap_module;
+
+/************************************ call ************************************/
+static char doc_f2py_rout_wrap_call[] = "\
+Function signature:\n\
+  arr = call(type_num,dims,intent,obj)\n\
+Required arguments:\n"
+"  type_num : input int\n"
+"  dims : input int-sequence\n"
+"  intent : input int\n"
+"  obj : input python object\n"
+"Return objects:\n"
+"  arr : array";
+static PyObject *f2py_rout_wrap_call(PyObject *capi_self,
+                                     PyObject *capi_args) {
+  PyObject * volatile capi_buildvalue = NULL;
+  int type_num = 0;
+  int elsize = 0;
+  npy_intp *dims = NULL;
+  PyObject *dims_capi = Py_None;
+  int rank = 0;
+  int intent = 0;
+  PyArrayObject *capi_arr_tmp = NULL;
+  PyObject *arr_capi = Py_None;
+  int i;
+
+  if (!PyArg_ParseTuple(capi_args,"iiOiO|:wrap.call",\
+                        &type_num,&elsize,&dims_capi,&intent,&arr_capi))
+    return NULL;
+  rank = PySequence_Length(dims_capi);
+  dims = malloc(rank*sizeof(npy_intp));
+  for (i=0;i<rank;++i) {
+    PyObject *tmp;
+    tmp = PySequence_GetItem(dims_capi, i);
+    if (tmp == NULL) {
+        goto fail;
+    }
+    dims[i] = (npy_intp)PyLong_AsLong(tmp);
+    Py_DECREF(tmp);
+    if (dims[i] == -1 && PyErr_Occurred()) {
+        goto fail;
+    }
+  }
+  capi_arr_tmp = ndarray_from_pyobj(type_num,elsize,dims,rank,intent|F2PY_INTENT_OUT,arr_capi,"wrap.call failed");
+  if (capi_arr_tmp == NULL) {
+    free(dims);
+    return NULL;
+  }
+  capi_buildvalue = Py_BuildValue("N",capi_arr_tmp);
+  free(dims);
+  return capi_buildvalue;
+
+fail:
+  free(dims);
+  return NULL;
+}
+
+static char doc_f2py_rout_wrap_attrs[] = "\
+Function signature:\n\
+  arr = array_attrs(arr)\n\
+Required arguments:\n"
+"  arr : input array object\n"
+"Return objects:\n"
+"  data : data address in hex\n"
+"  nd : int\n"
+"  dimensions : tuple\n"
+"  strides : tuple\n"
+"  base : python object\n"
+"  (kind,type,type_num,elsize,alignment) : 4-tuple\n"
+"  flags : int\n"
+"  itemsize : int\n"
+;
+static PyObject *f2py_rout_wrap_attrs(PyObject *capi_self,
+                                      PyObject *capi_args) {
+  PyObject *arr_capi = Py_None;
+  PyArrayObject *arr = NULL;
+  PyObject *dimensions = NULL;
+  PyObject *strides = NULL;
+  char s[100];
+  int i;
+  memset(s,0,100);
+  if (!PyArg_ParseTuple(capi_args,"O!|:wrap.attrs",
+                        &PyArray_Type,&arr_capi))
+    return NULL;
+  arr = (PyArrayObject *)arr_capi;
+  sprintf(s,"%p",PyArray_DATA(arr));
+  dimensions = PyTuple_New(PyArray_NDIM(arr));
+  strides = PyTuple_New(PyArray_NDIM(arr));
+  for (i=0;i<PyArray_NDIM(arr);++i) {
+    PyTuple_SetItem(dimensions,i,PyLong_FromLong(PyArray_DIM(arr,i)));
+    PyTuple_SetItem(strides,i,PyLong_FromLong(PyArray_STRIDE(arr,i)));
+  }
+  return Py_BuildValue("siNNO(cciii)ii",s,PyArray_NDIM(arr),
+                       dimensions,strides,
+                       (PyArray_BASE(arr)==NULL?Py_None:PyArray_BASE(arr)),
+                       PyArray_DESCR(arr)->kind,
+                       PyArray_DESCR(arr)->type,
+                       PyArray_TYPE(arr),
+                       PyArray_ITEMSIZE(arr),
+                       PyArray_DESCR(arr)->alignment,
+                       PyArray_FLAGS(arr),
+                       PyArray_ITEMSIZE(arr));
+}
+
+static PyMethodDef f2py_module_methods[] = {
+
+  {"call",f2py_rout_wrap_call,METH_VARARGS,doc_f2py_rout_wrap_call},
+  {"array_attrs",f2py_rout_wrap_attrs,METH_VARARGS,doc_f2py_rout_wrap_attrs},
+  {NULL,NULL}
+};
+
+static struct PyModuleDef moduledef = {
+    PyModuleDef_HEAD_INIT,
+    "test_array_from_pyobj_ext",
+    NULL,
+    -1,
+    f2py_module_methods,
+    NULL,
+    NULL,
+    NULL,
+    NULL
+};
+
+PyMODINIT_FUNC PyInit_test_array_from_pyobj_ext(void) {
+  PyObject *m,*d, *s;
+  m = wrap_module = PyModule_Create(&moduledef);
+  Py_SET_TYPE(&PyFortran_Type, &PyType_Type);
+  import_array();
+  if (PyErr_Occurred())
+    Py_FatalError("can't initialize module wrap (failed to import numpy)");
+  d = PyModule_GetDict(m);
+  s = PyUnicode_FromString("This module 'wrap' is auto-generated with f2py (version:2_1330).\nFunctions:\n"
+                           "  arr = call(type_num,dims,intent,obj)\n"
+                           ".");
+  PyDict_SetItemString(d, "__doc__", s);
+  wrap_error = PyErr_NewException ("wrap.error", NULL, NULL);
+  Py_DECREF(s);
+
+#define ADDCONST(NAME, CONST)              \
+    s = PyLong_FromLong(CONST);             \
+    PyDict_SetItemString(d, NAME, s);      \
+    Py_DECREF(s)
+
+  ADDCONST("F2PY_INTENT_IN", F2PY_INTENT_IN);
+  ADDCONST("F2PY_INTENT_INOUT", F2PY_INTENT_INOUT);
+  ADDCONST("F2PY_INTENT_OUT", F2PY_INTENT_OUT);
+  ADDCONST("F2PY_INTENT_HIDE", F2PY_INTENT_HIDE);
+  ADDCONST("F2PY_INTENT_CACHE", F2PY_INTENT_CACHE);
+  ADDCONST("F2PY_INTENT_COPY", F2PY_INTENT_COPY);
+  ADDCONST("F2PY_INTENT_C", F2PY_INTENT_C);
+  ADDCONST("F2PY_OPTIONAL", F2PY_OPTIONAL);
+  ADDCONST("F2PY_INTENT_INPLACE", F2PY_INTENT_INPLACE);
+  ADDCONST("NPY_BOOL", NPY_BOOL);
+  ADDCONST("NPY_BYTE", NPY_BYTE);
+  ADDCONST("NPY_UBYTE", NPY_UBYTE);
+  ADDCONST("NPY_SHORT", NPY_SHORT);
+  ADDCONST("NPY_USHORT", NPY_USHORT);
+  ADDCONST("NPY_INT", NPY_INT);
+  ADDCONST("NPY_UINT", NPY_UINT);
+  ADDCONST("NPY_INTP", NPY_INTP);
+  ADDCONST("NPY_UINTP", NPY_UINTP);
+  ADDCONST("NPY_LONG", NPY_LONG);
+  ADDCONST("NPY_ULONG", NPY_ULONG);
+  ADDCONST("NPY_LONGLONG", NPY_LONGLONG);
+  ADDCONST("NPY_ULONGLONG", NPY_ULONGLONG);
+  ADDCONST("NPY_FLOAT", NPY_FLOAT);
+  ADDCONST("NPY_DOUBLE", NPY_DOUBLE);
+  ADDCONST("NPY_LONGDOUBLE", NPY_LONGDOUBLE);
+  ADDCONST("NPY_CFLOAT", NPY_CFLOAT);
+  ADDCONST("NPY_CDOUBLE", NPY_CDOUBLE);
+  ADDCONST("NPY_CLONGDOUBLE", NPY_CLONGDOUBLE);
+  ADDCONST("NPY_OBJECT", NPY_OBJECT);
+  ADDCONST("NPY_STRING", NPY_STRING);
+  ADDCONST("NPY_UNICODE", NPY_UNICODE);
+  ADDCONST("NPY_VOID", NPY_VOID);
+  ADDCONST("NPY_NTYPES", NPY_NTYPES);
+  ADDCONST("NPY_NOTYPE", NPY_NOTYPE);
+  ADDCONST("NPY_USERDEF", NPY_USERDEF);
+
+  ADDCONST("CONTIGUOUS", NPY_ARRAY_C_CONTIGUOUS);
+  ADDCONST("FORTRAN", NPY_ARRAY_F_CONTIGUOUS);
+  ADDCONST("OWNDATA", NPY_ARRAY_OWNDATA);
+  ADDCONST("FORCECAST", NPY_ARRAY_FORCECAST);
+  ADDCONST("ENSURECOPY", NPY_ARRAY_ENSURECOPY);
+  ADDCONST("ENSUREARRAY", NPY_ARRAY_ENSUREARRAY);
+  ADDCONST("ALIGNED", NPY_ARRAY_ALIGNED);
+  ADDCONST("WRITEABLE", NPY_ARRAY_WRITEABLE);
+  ADDCONST("WRITEBACKIFCOPY", NPY_ARRAY_WRITEBACKIFCOPY);
+
+  ADDCONST("BEHAVED", NPY_ARRAY_BEHAVED);
+  ADDCONST("BEHAVED_NS", NPY_ARRAY_BEHAVED_NS);
+  ADDCONST("CARRAY", NPY_ARRAY_CARRAY);
+  ADDCONST("FARRAY", NPY_ARRAY_FARRAY);
+  ADDCONST("CARRAY_RO", NPY_ARRAY_CARRAY_RO);
+  ADDCONST("FARRAY_RO", NPY_ARRAY_FARRAY_RO);
+  ADDCONST("DEFAULT", NPY_ARRAY_DEFAULT);
+  ADDCONST("UPDATE_ALL", NPY_ARRAY_UPDATE_ALL);
+
+#undef ADDCONST(
+
+  if (PyErr_Occurred())
+    Py_FatalError("can't initialize module wrap");
+
+#ifdef F2PY_REPORT_ATEXIT
+  on_exit(f2py_report_on_exit,(void*)"array_from_pyobj.wrap.call");
+#endif
+
+  return m;
+}
+#ifdef __cplusplus
+}
+#endif
diff --git a/.venv/lib/python3.12/site-packages/numpy/f2py/tests/src/assumed_shape/.f2py_f2cmap b/.venv/lib/python3.12/site-packages/numpy/f2py/tests/src/assumed_shape/.f2py_f2cmap
new file mode 100644
index 00000000..2665f89b
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/numpy/f2py/tests/src/assumed_shape/.f2py_f2cmap
@@ -0,0 +1 @@
+dict(real=dict(rk="double"))
diff --git a/.venv/lib/python3.12/site-packages/numpy/f2py/tests/src/assumed_shape/foo_free.f90 b/.venv/lib/python3.12/site-packages/numpy/f2py/tests/src/assumed_shape/foo_free.f90
new file mode 100644
index 00000000..b301710f
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/numpy/f2py/tests/src/assumed_shape/foo_free.f90
@@ -0,0 +1,34 @@
+
+subroutine sum(x, res)
+  implicit none
+  real, intent(in) :: x(:)
+  real, intent(out) :: res
+
+  integer :: i
+
+  !print *, "sum: size(x) = ", size(x)
+
+  res = 0.0
+
+  do i = 1, size(x)
+    res = res + x(i)
+  enddo
+
+end subroutine sum
+
+function fsum(x) result (res)
+  implicit none
+  real, intent(in) :: x(:)
+  real :: res
+
+  integer :: i
+
+  !print *, "fsum: size(x) = ", size(x)
+
+  res = 0.0
+
+  do i = 1, size(x)
+    res = res + x(i)
+  enddo
+
+end function fsum
diff --git a/.venv/lib/python3.12/site-packages/numpy/f2py/tests/src/assumed_shape/foo_mod.f90 b/.venv/lib/python3.12/site-packages/numpy/f2py/tests/src/assumed_shape/foo_mod.f90
new file mode 100644
index 00000000..cbe6317e
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/numpy/f2py/tests/src/assumed_shape/foo_mod.f90
@@ -0,0 +1,41 @@
+
+module mod
+
+contains
+
+subroutine sum(x, res)
+  implicit none
+  real, intent(in) :: x(:)
+  real, intent(out) :: res
+
+  integer :: i
+
+  !print *, "sum: size(x) = ", size(x)
+
+  res = 0.0
+
+  do i = 1, size(x)
+    res = res + x(i)
+  enddo
+
+end subroutine sum
+
+function fsum(x) result (res)
+  implicit none
+  real, intent(in) :: x(:)
+  real :: res
+
+  integer :: i
+
+  !print *, "fsum: size(x) = ", size(x)
+
+  res = 0.0
+
+  do i = 1, size(x)
+    res = res + x(i)
+  enddo
+
+end function fsum
+
+
+end module mod
diff --git a/.venv/lib/python3.12/site-packages/numpy/f2py/tests/src/assumed_shape/foo_use.f90 b/.venv/lib/python3.12/site-packages/numpy/f2py/tests/src/assumed_shape/foo_use.f90
new file mode 100644
index 00000000..337465ac
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/numpy/f2py/tests/src/assumed_shape/foo_use.f90
@@ -0,0 +1,19 @@
+subroutine sum_with_use(x, res)
+  use precision
+
+  implicit none
+
+  real(kind=rk), intent(in) :: x(:)
+  real(kind=rk), intent(out) :: res
+
+  integer :: i
+
+  !print *, "size(x) = ", size(x)
+
+  res = 0.0
+
+  do i = 1, size(x)
+    res = res + x(i)
+  enddo
+
+ end subroutine
diff --git a/.venv/lib/python3.12/site-packages/numpy/f2py/tests/src/assumed_shape/precision.f90 b/.venv/lib/python3.12/site-packages/numpy/f2py/tests/src/assumed_shape/precision.f90
new file mode 100644
index 00000000..ed6c70cb
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/numpy/f2py/tests/src/assumed_shape/precision.f90
@@ -0,0 +1,4 @@
+module precision
+  integer, parameter :: rk = selected_real_kind(8)
+  integer, parameter :: ik = selected_real_kind(4)
+end module
diff --git a/.venv/lib/python3.12/site-packages/numpy/f2py/tests/src/block_docstring/foo.f b/.venv/lib/python3.12/site-packages/numpy/f2py/tests/src/block_docstring/foo.f
new file mode 100644
index 00000000..c8315f12
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/numpy/f2py/tests/src/block_docstring/foo.f
@@ -0,0 +1,6 @@
+      SUBROUTINE FOO()
+      INTEGER BAR(2, 3)
+
+      COMMON  /BLOCK/ BAR
+      RETURN
+      END
diff --git a/.venv/lib/python3.12/site-packages/numpy/f2py/tests/src/callback/foo.f b/.venv/lib/python3.12/site-packages/numpy/f2py/tests/src/callback/foo.f
new file mode 100644
index 00000000..ba397bb3
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/numpy/f2py/tests/src/callback/foo.f
@@ -0,0 +1,62 @@
+       subroutine t(fun,a)
+       integer a
+cf2py  intent(out) a
+       external fun
+       call fun(a)
+       end
+
+       subroutine func(a)
+cf2py  intent(in,out) a
+       integer a
+       a = a + 11
+       end
+
+       subroutine func0(a)
+cf2py  intent(out) a
+       integer a
+       a = 11
+       end
+
+       subroutine t2(a)
+cf2py  intent(callback) fun
+       integer a
+cf2py  intent(out) a
+       external fun
+       call fun(a)
+       end
+
+       subroutine string_callback(callback, a)
+       external callback
+       double precision callback
+       double precision a
+       character*1 r
+cf2py  intent(out) a
+       r = 'r'
+       a = callback(r)
+       end
+
+       subroutine string_callback_array(callback, cu, lencu, a)
+       external callback
+       integer callback
+       integer lencu
+       character*8 cu(lencu)
+       integer a
+cf2py  intent(out) a
+
+       a = callback(cu, lencu)
+       end
+
+       subroutine hidden_callback(a, r)
+       external global_f
+cf2py  intent(callback, hide) global_f
+       integer a, r, global_f
+cf2py  intent(out) r
+       r = global_f(a)
+       end
+
+       subroutine hidden_callback2(a, r)
+       external global_f
+       integer a, r, global_f
+cf2py  intent(out) r
+       r = global_f(a)
+       end
diff --git a/.venv/lib/python3.12/site-packages/numpy/f2py/tests/src/callback/gh17797.f90 b/.venv/lib/python3.12/site-packages/numpy/f2py/tests/src/callback/gh17797.f90
new file mode 100644
index 00000000..49853afd
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/numpy/f2py/tests/src/callback/gh17797.f90
@@ -0,0 +1,7 @@
+function gh17797(f, y) result(r)
+  external f
+  integer(8) :: r, f
+  integer(8), dimension(:) :: y
+  r = f(0)
+  r = r + sum(y)
+end function gh17797
diff --git a/.venv/lib/python3.12/site-packages/numpy/f2py/tests/src/callback/gh18335.f90 b/.venv/lib/python3.12/site-packages/numpy/f2py/tests/src/callback/gh18335.f90
new file mode 100644
index 00000000..92b6d754
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/numpy/f2py/tests/src/callback/gh18335.f90
@@ -0,0 +1,17 @@
+        ! When gh18335_workaround is defined as an extension,
+        ! the issue cannot be reproduced.
+        !subroutine gh18335_workaround(f, y)
+        !  implicit none
+        !  external f
+        !  integer(kind=1) :: y(1)
+        !  call f(y)
+        !end subroutine gh18335_workaround
+
+        function gh18335(f) result (r)
+          implicit none
+          external f
+          integer(kind=1) :: y(1), r
+          y(1) = 123
+          call f(y)
+          r = y(1)
+        end function gh18335
diff --git a/.venv/lib/python3.12/site-packages/numpy/f2py/tests/src/callback/gh25211.f b/.venv/lib/python3.12/site-packages/numpy/f2py/tests/src/callback/gh25211.f
new file mode 100644
index 00000000..ba727a10
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/numpy/f2py/tests/src/callback/gh25211.f
@@ -0,0 +1,10 @@
+      SUBROUTINE FOO(FUN,R)
+      EXTERNAL FUN
+      INTEGER I
+      REAL*8 R, FUN
+Cf2py intent(out) r
+      R = 0D0
+      DO I=-5,5
+         R = R + FUN(I)
+      ENDDO
+      END
diff --git a/.venv/lib/python3.12/site-packages/numpy/f2py/tests/src/callback/gh25211.pyf b/.venv/lib/python3.12/site-packages/numpy/f2py/tests/src/callback/gh25211.pyf
new file mode 100644
index 00000000..f1201115
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/numpy/f2py/tests/src/callback/gh25211.pyf
@@ -0,0 +1,18 @@
+python module __user__routines
+    interface
+        function fun(i) result (r)
+            integer :: i
+            real*8 :: r
+        end function fun
+    end interface
+end python module __user__routines
+
+python module callback2
+    interface
+        subroutine foo(f,r)
+            use __user__routines, f=>fun
+            external f
+            real*8 intent(out) :: r
+        end subroutine foo
+    end interface
+end python module callback2
diff --git a/.venv/lib/python3.12/site-packages/numpy/f2py/tests/src/cli/gh_22819.pyf b/.venv/lib/python3.12/site-packages/numpy/f2py/tests/src/cli/gh_22819.pyf
new file mode 100644
index 00000000..8eb5bb10
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/numpy/f2py/tests/src/cli/gh_22819.pyf
@@ -0,0 +1,6 @@
+python module test_22819
+    interface
+        subroutine hello()
+        end subroutine hello
+    end interface
+end python module test_22819
diff --git a/.venv/lib/python3.12/site-packages/numpy/f2py/tests/src/cli/hi77.f b/.venv/lib/python3.12/site-packages/numpy/f2py/tests/src/cli/hi77.f
new file mode 100644
index 00000000..8b916ebe
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/numpy/f2py/tests/src/cli/hi77.f
@@ -0,0 +1,3 @@
+      SUBROUTINE HI
+        PRINT*, "HELLO WORLD"
+      END SUBROUTINE
diff --git a/.venv/lib/python3.12/site-packages/numpy/f2py/tests/src/cli/hiworld.f90 b/.venv/lib/python3.12/site-packages/numpy/f2py/tests/src/cli/hiworld.f90
new file mode 100644
index 00000000..981f8775
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/numpy/f2py/tests/src/cli/hiworld.f90
@@ -0,0 +1,3 @@
+function hi()
+  print*, "Hello World"
+end function
diff --git a/.venv/lib/python3.12/site-packages/numpy/f2py/tests/src/common/block.f b/.venv/lib/python3.12/site-packages/numpy/f2py/tests/src/common/block.f
new file mode 100644
index 00000000..7ea7968f
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/numpy/f2py/tests/src/common/block.f
@@ -0,0 +1,11 @@
+      SUBROUTINE INITCB
+      DOUBLE PRECISION LONG
+      CHARACTER        STRING
+      INTEGER          OK
+    
+      COMMON  /BLOCK/ LONG, STRING, OK
+      LONG = 1.0
+      STRING = '2'
+      OK = 3
+      RETURN
+      END
diff --git a/.venv/lib/python3.12/site-packages/numpy/f2py/tests/src/common/gh19161.f90 b/.venv/lib/python3.12/site-packages/numpy/f2py/tests/src/common/gh19161.f90
new file mode 100644
index 00000000..a2f40735
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/numpy/f2py/tests/src/common/gh19161.f90
@@ -0,0 +1,10 @@
+module typedefmod
+  use iso_fortran_env, only: real32
+end module typedefmod
+
+module data
+  use typedefmod, only: real32
+  implicit none
+  real(kind=real32) :: x
+  common/test/x
+end module data
diff --git a/.venv/lib/python3.12/site-packages/numpy/f2py/tests/src/crackfortran/accesstype.f90 b/.venv/lib/python3.12/site-packages/numpy/f2py/tests/src/crackfortran/accesstype.f90
new file mode 100644
index 00000000..e2cbd445
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/numpy/f2py/tests/src/crackfortran/accesstype.f90
@@ -0,0 +1,13 @@
+module foo
+  public
+  type, private, bind(c) :: a
+     integer :: i
+  end type a
+  type, bind(c) :: b_
+     integer :: j
+  end type b_
+  public :: b_
+  type :: c
+     integer :: k
+  end type c
+end module foo
diff --git a/.venv/lib/python3.12/site-packages/numpy/f2py/tests/src/crackfortran/data_common.f b/.venv/lib/python3.12/site-packages/numpy/f2py/tests/src/crackfortran/data_common.f
new file mode 100644
index 00000000..5ffd865c
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/numpy/f2py/tests/src/crackfortran/data_common.f
@@ -0,0 +1,8 @@
+        BLOCK DATA PARAM_INI
+        COMMON /MYCOM/ MYDATA
+            DATA MYDATA /0/
+        END
+        SUBROUTINE SUB1
+        COMMON /MYCOM/ MYDATA
+        MYDATA = MYDATA + 1
+        END
diff --git a/.venv/lib/python3.12/site-packages/numpy/f2py/tests/src/crackfortran/data_multiplier.f b/.venv/lib/python3.12/site-packages/numpy/f2py/tests/src/crackfortran/data_multiplier.f
new file mode 100644
index 00000000..19ff8a83
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/numpy/f2py/tests/src/crackfortran/data_multiplier.f
@@ -0,0 +1,5 @@
+      BLOCK DATA MYBLK
+      IMPLICIT DOUBLE PRECISION (A-H,O-Z)
+      COMMON /MYCOM/ IVAR1, IVAR2, IVAR3, IVAR4, EVAR5
+            DATA IVAR1, IVAR2, IVAR3, IVAR4, EVAR5 /2*3,2*2,0.0D0/
+      END
diff --git a/.venv/lib/python3.12/site-packages/numpy/f2py/tests/src/crackfortran/data_stmts.f90 b/.venv/lib/python3.12/site-packages/numpy/f2py/tests/src/crackfortran/data_stmts.f90
new file mode 100644
index 00000000..576c5e48
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/numpy/f2py/tests/src/crackfortran/data_stmts.f90
@@ -0,0 +1,20 @@
+! gh-23276
+module cmplxdat
+  implicit none
+  integer :: i, j
+  real :: x, y
+  real, dimension(2) :: z
+  real(kind=8) :: pi
+  complex(kind=8), target :: medium_ref_index
+  complex(kind=8), target :: ref_index_one, ref_index_two
+  complex(kind=8), dimension(2) :: my_array
+  real(kind=8), dimension(3) :: my_real_array = (/1.0d0, 2.0d0, 3.0d0/)
+
+  data i, j / 2, 3 /
+  data x, y / 1.5, 2.0 /
+  data z / 3.5, 7.0 /
+  data medium_ref_index / (1.d0, 0.d0) /
+  data ref_index_one, ref_index_two / (13.0d0, 21.0d0), (-30.0d0, 43.0d0) /
+  data my_array / (1.0d0, 2.0d0), (-3.0d0, 4.0d0) /
+  data pi / 3.1415926535897932384626433832795028841971693993751058209749445923078164062d0 /
+end module cmplxdat
diff --git a/.venv/lib/python3.12/site-packages/numpy/f2py/tests/src/crackfortran/data_with_comments.f b/.venv/lib/python3.12/site-packages/numpy/f2py/tests/src/crackfortran/data_with_comments.f
new file mode 100644
index 00000000..4128f004
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/numpy/f2py/tests/src/crackfortran/data_with_comments.f
@@ -0,0 +1,8 @@
+      BLOCK DATA PARAM_INI
+      COMMON /MYCOM/ MYTAB
+      INTEGER  MYTAB(3)
+      DATA MYTAB/
+     *   0, ! 1 and more commenty stuff
+     *   4, ! 2
+     *   0 /
+      END
diff --git a/.venv/lib/python3.12/site-packages/numpy/f2py/tests/src/crackfortran/foo_deps.f90 b/.venv/lib/python3.12/site-packages/numpy/f2py/tests/src/crackfortran/foo_deps.f90
new file mode 100644
index 00000000..e327b25c
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/numpy/f2py/tests/src/crackfortran/foo_deps.f90
@@ -0,0 +1,6 @@
+module foo
+  type bar
+    character(len = 4) :: text
+  end type bar
+  type(bar), parameter :: abar = bar('abar')
+end module foo
diff --git a/.venv/lib/python3.12/site-packages/numpy/f2py/tests/src/crackfortran/gh15035.f b/.venv/lib/python3.12/site-packages/numpy/f2py/tests/src/crackfortran/gh15035.f
new file mode 100644
index 00000000..1bb2e674
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/numpy/f2py/tests/src/crackfortran/gh15035.f
@@ -0,0 +1,16 @@
+        subroutine subb(k)
+          real(8), intent(inout) :: k(:)
+          k=k+1
+        endsubroutine
+
+        subroutine subc(w,k)
+          real(8), intent(in) :: w(:)
+          real(8), intent(out) :: k(size(w))
+          k=w+1
+        endsubroutine
+
+        function t0(value)
+          character value
+          character t0
+          t0 = value
+        endfunction
diff --git a/.venv/lib/python3.12/site-packages/numpy/f2py/tests/src/crackfortran/gh17859.f b/.venv/lib/python3.12/site-packages/numpy/f2py/tests/src/crackfortran/gh17859.f
new file mode 100644
index 00000000..99595384
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/numpy/f2py/tests/src/crackfortran/gh17859.f
@@ -0,0 +1,12 @@
+        integer(8) function external_as_statement(fcn)
+        implicit none
+        external fcn
+        integer(8) :: fcn
+        external_as_statement = fcn(0)
+        end
+
+        integer(8) function external_as_attribute(fcn)
+        implicit none
+        integer(8), external :: fcn
+        external_as_attribute = fcn(0)
+        end
diff --git a/.venv/lib/python3.12/site-packages/numpy/f2py/tests/src/crackfortran/gh22648.pyf b/.venv/lib/python3.12/site-packages/numpy/f2py/tests/src/crackfortran/gh22648.pyf
new file mode 100644
index 00000000..b3454f18
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/numpy/f2py/tests/src/crackfortran/gh22648.pyf
@@ -0,0 +1,7 @@
+python module iri16py ! in
+    interface  ! in :iri16py
+        block data  ! in :iri16py:iridreg_modified.for
+           COMMON /fircom/ eden,tabhe,tabla,tabmo,tabza,tabfl
+       end block data 
+    end interface 
+end python module iri16py
diff --git a/.venv/lib/python3.12/site-packages/numpy/f2py/tests/src/crackfortran/gh23533.f b/.venv/lib/python3.12/site-packages/numpy/f2py/tests/src/crackfortran/gh23533.f
new file mode 100644
index 00000000..db522afa
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/numpy/f2py/tests/src/crackfortran/gh23533.f
@@ -0,0 +1,5 @@
+      SUBROUTINE EXAMPLE( )
+        IF( .TRUE. ) THEN
+            CALL DO_SOMETHING()
+        END IF ! ** .TRUE. **
+      END
diff --git a/.venv/lib/python3.12/site-packages/numpy/f2py/tests/src/crackfortran/gh23598.f90 b/.venv/lib/python3.12/site-packages/numpy/f2py/tests/src/crackfortran/gh23598.f90
new file mode 100644
index 00000000..e0dffb5e
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/numpy/f2py/tests/src/crackfortran/gh23598.f90
@@ -0,0 +1,4 @@
+integer function intproduct(a, b) result(res)
+  integer, intent(in) :: a, b
+  res = a*b
+end function
diff --git a/.venv/lib/python3.12/site-packages/numpy/f2py/tests/src/crackfortran/gh23598Warn.f90 b/.venv/lib/python3.12/site-packages/numpy/f2py/tests/src/crackfortran/gh23598Warn.f90
new file mode 100644
index 00000000..3b44efc5
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/numpy/f2py/tests/src/crackfortran/gh23598Warn.f90
@@ -0,0 +1,11 @@
+module test_bug
+    implicit none
+    private
+    public :: intproduct
+
+contains
+    integer function intproduct(a, b) result(res)
+    integer, intent(in) :: a, b
+    res = a*b
+    end function
+end module
diff --git a/.venv/lib/python3.12/site-packages/numpy/f2py/tests/src/crackfortran/gh23879.f90 b/.venv/lib/python3.12/site-packages/numpy/f2py/tests/src/crackfortran/gh23879.f90
new file mode 100644
index 00000000..fac262d5
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/numpy/f2py/tests/src/crackfortran/gh23879.f90
@@ -0,0 +1,20 @@
+module gh23879
+    implicit none
+    private
+    public :: foo
+
+ contains
+
+    subroutine foo(a, b)
+       integer, intent(in) :: a
+       integer, intent(out) :: b
+       b = a
+       call bar(b)
+    end subroutine
+
+    subroutine bar(x)
+        integer, intent(inout) :: x
+        x = 2*x
+     end subroutine
+
+ end module gh23879
diff --git a/.venv/lib/python3.12/site-packages/numpy/f2py/tests/src/crackfortran/gh2848.f90 b/.venv/lib/python3.12/site-packages/numpy/f2py/tests/src/crackfortran/gh2848.f90
new file mode 100644
index 00000000..31ea9327
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/numpy/f2py/tests/src/crackfortran/gh2848.f90
@@ -0,0 +1,13 @@
+      subroutine gh2848( &
+        ! first 2 parameters
+        par1, par2,&
+        ! last 2 parameters
+        par3, par4)
+
+        integer, intent(in)  :: par1, par2
+        integer, intent(out) :: par3, par4
+
+        par3 = par1
+        par4 = par2
+
+      end subroutine gh2848
diff --git a/.venv/lib/python3.12/site-packages/numpy/f2py/tests/src/crackfortran/operators.f90 b/.venv/lib/python3.12/site-packages/numpy/f2py/tests/src/crackfortran/operators.f90
new file mode 100644
index 00000000..1d060a3d
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/numpy/f2py/tests/src/crackfortran/operators.f90
@@ -0,0 +1,49 @@
+module foo
+  type bar
+     character(len = 32) :: item
+  end type bar
+  interface operator(.item.)
+     module procedure item_int, item_real
+  end interface operator(.item.)
+  interface operator(==)
+     module procedure items_are_equal
+  end interface operator(==)
+  interface assignment(=)
+     module procedure get_int, get_real
+  end interface assignment(=)
+contains
+  function item_int(val) result(elem)
+    integer, intent(in) :: val
+    type(bar) :: elem
+
+    write(elem%item, "(I32)") val
+  end function item_int
+
+  function item_real(val) result(elem)
+    real, intent(in) :: val
+    type(bar) :: elem
+
+    write(elem%item, "(1PE32.12)") val
+  end function item_real
+
+  function items_are_equal(val1, val2) result(equal)
+    type(bar), intent(in) :: val1, val2
+    logical :: equal
+
+    equal = (val1%item == val2%item)
+  end function items_are_equal
+
+  subroutine get_real(rval, item)
+    real, intent(out) :: rval
+    type(bar), intent(in) :: item
+
+    read(item%item, *) rval
+  end subroutine get_real
+
+  subroutine get_int(rval, item)
+    integer, intent(out) :: rval
+    type(bar), intent(in) :: item
+
+    read(item%item, *) rval
+  end subroutine get_int
+end module foo
diff --git a/.venv/lib/python3.12/site-packages/numpy/f2py/tests/src/crackfortran/privatemod.f90 b/.venv/lib/python3.12/site-packages/numpy/f2py/tests/src/crackfortran/privatemod.f90
new file mode 100644
index 00000000..2674c214
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/numpy/f2py/tests/src/crackfortran/privatemod.f90
@@ -0,0 +1,11 @@
+module foo
+  private
+  integer :: a
+  public :: setA
+  integer :: b
+contains
+  subroutine setA(v)
+    integer, intent(in) :: v
+    a = v
+  end subroutine setA
+end module foo
diff --git a/.venv/lib/python3.12/site-packages/numpy/f2py/tests/src/crackfortran/publicmod.f90 b/.venv/lib/python3.12/site-packages/numpy/f2py/tests/src/crackfortran/publicmod.f90
new file mode 100644
index 00000000..1db76e3f
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/numpy/f2py/tests/src/crackfortran/publicmod.f90
@@ -0,0 +1,10 @@
+module foo
+  public
+  integer, private :: a
+  public :: setA
+contains
+  subroutine setA(v)
+    integer, intent(in) :: v
+    a = v
+  end subroutine setA
+end module foo
diff --git a/.venv/lib/python3.12/site-packages/numpy/f2py/tests/src/crackfortran/pubprivmod.f90 b/.venv/lib/python3.12/site-packages/numpy/f2py/tests/src/crackfortran/pubprivmod.f90
new file mode 100644
index 00000000..46bef7cb
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/numpy/f2py/tests/src/crackfortran/pubprivmod.f90
@@ -0,0 +1,10 @@
+module foo
+  public
+  integer, private :: a
+  integer :: b
+contains
+  subroutine setA(v)
+    integer, intent(in) :: v
+    a = v
+  end subroutine setA
+end module foo
diff --git a/.venv/lib/python3.12/site-packages/numpy/f2py/tests/src/crackfortran/unicode_comment.f90 b/.venv/lib/python3.12/site-packages/numpy/f2py/tests/src/crackfortran/unicode_comment.f90
new file mode 100644
index 00000000..13515ce9
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/numpy/f2py/tests/src/crackfortran/unicode_comment.f90
@@ -0,0 +1,4 @@
+subroutine foo(x)
+  real(8), intent(in) :: x
+  ! Écrit à l'écran la valeur de x
+end subroutine
diff --git a/.venv/lib/python3.12/site-packages/numpy/f2py/tests/src/f2cmap/.f2py_f2cmap b/.venv/lib/python3.12/site-packages/numpy/f2py/tests/src/f2cmap/.f2py_f2cmap
new file mode 100644
index 00000000..a4425f88
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/numpy/f2py/tests/src/f2cmap/.f2py_f2cmap
@@ -0,0 +1 @@
+dict(real=dict(real32='float', real64='double'), integer=dict(int64='long_long'))
diff --git a/.venv/lib/python3.12/site-packages/numpy/f2py/tests/src/f2cmap/isoFortranEnvMap.f90 b/.venv/lib/python3.12/site-packages/numpy/f2py/tests/src/f2cmap/isoFortranEnvMap.f90
new file mode 100644
index 00000000..1e1dc1d4
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/numpy/f2py/tests/src/f2cmap/isoFortranEnvMap.f90
@@ -0,0 +1,9 @@
+      subroutine func1(n, x, res)
+        use, intrinsic :: iso_fortran_env, only: int64, real64
+        implicit none
+        integer(int64), intent(in) :: n
+        real(real64), intent(in) :: x(n)
+        real(real64), intent(out) :: res
+!f2py   intent(hide) :: n
+        res = sum(x)
+      end
diff --git a/.venv/lib/python3.12/site-packages/numpy/f2py/tests/src/isocintrin/isoCtests.f90 b/.venv/lib/python3.12/site-packages/numpy/f2py/tests/src/isocintrin/isoCtests.f90
new file mode 100644
index 00000000..765f7c1c
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/numpy/f2py/tests/src/isocintrin/isoCtests.f90
@@ -0,0 +1,34 @@
+  module coddity
+    use iso_c_binding, only: c_double, c_int, c_int64_t
+    implicit none
+    contains
+      subroutine c_add(a, b, c) bind(c, name="c_add")
+        real(c_double), intent(in) :: a, b
+        real(c_double), intent(out) :: c
+        c = a + b
+      end subroutine c_add
+      ! gh-9693
+      function wat(x, y) result(z) bind(c)
+          integer(c_int), intent(in) :: x, y
+          integer(c_int) :: z
+
+          z = x + 7
+      end function wat
+      ! gh-25207
+      subroutine c_add_int64(a, b, c) bind(c)
+        integer(c_int64_t), intent(in) :: a, b
+        integer(c_int64_t), intent(out) :: c
+        c = a + b
+      end subroutine c_add_int64
+      ! gh-25207
+      subroutine add_arr(A, B, C)
+         integer(c_int64_t), intent(in) :: A(3)
+         integer(c_int64_t), intent(in) :: B(3)
+         integer(c_int64_t), intent(out) :: C(3)
+         integer :: j
+
+         do j = 1, 3
+            C(j) = A(j)+B(j)
+         end do
+      end subroutine
+  end module coddity
diff --git a/.venv/lib/python3.12/site-packages/numpy/f2py/tests/src/kind/foo.f90 b/.venv/lib/python3.12/site-packages/numpy/f2py/tests/src/kind/foo.f90
new file mode 100644
index 00000000..d3d15cfb
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/numpy/f2py/tests/src/kind/foo.f90
@@ -0,0 +1,20 @@
+
+
+subroutine selectedrealkind(p, r, res)
+  implicit none
+  
+  integer, intent(in) :: p, r
+  !f2py integer :: r=0
+  integer, intent(out) :: res
+  res = selected_real_kind(p, r)
+
+end subroutine
+
+subroutine selectedintkind(p, res)
+  implicit none
+
+  integer, intent(in) :: p
+  integer, intent(out) :: res
+  res = selected_int_kind(p)
+
+end subroutine
diff --git a/.venv/lib/python3.12/site-packages/numpy/f2py/tests/src/mixed/foo.f b/.venv/lib/python3.12/site-packages/numpy/f2py/tests/src/mixed/foo.f
new file mode 100644
index 00000000..c3474257
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/numpy/f2py/tests/src/mixed/foo.f
@@ -0,0 +1,5 @@
+      subroutine bar11(a)
+cf2py intent(out) a
+      integer a
+      a = 11
+      end
diff --git a/.venv/lib/python3.12/site-packages/numpy/f2py/tests/src/mixed/foo_fixed.f90 b/.venv/lib/python3.12/site-packages/numpy/f2py/tests/src/mixed/foo_fixed.f90
new file mode 100644
index 00000000..7543a6ac
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/numpy/f2py/tests/src/mixed/foo_fixed.f90
@@ -0,0 +1,8 @@
+      module foo_fixed
+      contains
+        subroutine bar12(a)
+!f2py intent(out) a
+          integer a
+          a = 12
+        end subroutine bar12
+      end module foo_fixed
diff --git a/.venv/lib/python3.12/site-packages/numpy/f2py/tests/src/mixed/foo_free.f90 b/.venv/lib/python3.12/site-packages/numpy/f2py/tests/src/mixed/foo_free.f90
new file mode 100644
index 00000000..c1b641f1
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/numpy/f2py/tests/src/mixed/foo_free.f90
@@ -0,0 +1,8 @@
+module foo_free
+contains
+  subroutine bar13(a)
+    !f2py intent(out) a
+    integer a
+    a = 13
+  end subroutine bar13
+end module foo_free
diff --git a/.venv/lib/python3.12/site-packages/numpy/f2py/tests/src/module_data/mod.mod b/.venv/lib/python3.12/site-packages/numpy/f2py/tests/src/module_data/mod.mod
new file mode 100644
index 00000000..8670a97e
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/numpy/f2py/tests/src/module_data/mod.mod
Binary files differdiff --git a/.venv/lib/python3.12/site-packages/numpy/f2py/tests/src/module_data/module_data_docstring.f90 b/.venv/lib/python3.12/site-packages/numpy/f2py/tests/src/module_data/module_data_docstring.f90
new file mode 100644
index 00000000..4505e0cb
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/numpy/f2py/tests/src/module_data/module_data_docstring.f90
@@ -0,0 +1,12 @@
+module mod
+  integer :: i
+  integer :: x(4)
+  real, dimension(2,3) :: a
+  real, allocatable, dimension(:,:) :: b
+contains
+  subroutine foo
+    integer :: k
+    k = 1
+    a(1,2) = a(1,2)+3
+  end subroutine foo
+end module mod
diff --git a/.venv/lib/python3.12/site-packages/numpy/f2py/tests/src/negative_bounds/issue_20853.f90 b/.venv/lib/python3.12/site-packages/numpy/f2py/tests/src/negative_bounds/issue_20853.f90
new file mode 100644
index 00000000..bf1fa928
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/numpy/f2py/tests/src/negative_bounds/issue_20853.f90
@@ -0,0 +1,7 @@
+subroutine foo(is_, ie_, arr, tout)
+ implicit none
+ integer :: is_,ie_
+ real, intent(in) :: arr(is_:ie_)
+ real, intent(out) :: tout(is_:ie_)
+ tout = arr
+end
diff --git a/.venv/lib/python3.12/site-packages/numpy/f2py/tests/src/parameter/constant_both.f90 b/.venv/lib/python3.12/site-packages/numpy/f2py/tests/src/parameter/constant_both.f90
new file mode 100644
index 00000000..ac90cedc
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/numpy/f2py/tests/src/parameter/constant_both.f90
@@ -0,0 +1,57 @@
+! Check that parameters are correct intercepted.
+! Constants with comma separations are commonly
+! used, for instance Pi = 3._dp
+subroutine foo(x)
+  implicit none
+  integer, parameter :: sp = selected_real_kind(6)
+  integer, parameter :: dp = selected_real_kind(15)
+  integer, parameter :: ii = selected_int_kind(9)
+  integer, parameter :: il = selected_int_kind(18)
+  real(dp), intent(inout) :: x
+  dimension x(3)
+  real(sp), parameter :: three_s = 3._sp
+  real(dp), parameter :: three_d = 3._dp
+  integer(ii), parameter :: three_i = 3_ii
+  integer(il), parameter :: three_l = 3_il
+  x(1) = x(1) + x(2) * three_s * three_i + x(3) * three_d * three_l
+  x(2) = x(2) * three_s
+  x(3) = x(3) * three_l
+  return
+end subroutine
+
+
+subroutine foo_no(x)
+  implicit none
+  integer, parameter :: sp = selected_real_kind(6)
+  integer, parameter :: dp = selected_real_kind(15)
+  integer, parameter :: ii = selected_int_kind(9)
+  integer, parameter :: il = selected_int_kind(18)
+  real(dp), intent(inout) :: x
+  dimension x(3)
+  real(sp), parameter :: three_s = 3.
+  real(dp), parameter :: three_d = 3.
+  integer(ii), parameter :: three_i = 3
+  integer(il), parameter :: three_l = 3
+  x(1) = x(1) + x(2) * three_s * three_i + x(3) * three_d * three_l
+  x(2) = x(2) * three_s
+  x(3) = x(3) * three_l
+  return
+end subroutine
+
+subroutine foo_sum(x)
+  implicit none
+  integer, parameter :: sp = selected_real_kind(6)
+  integer, parameter :: dp = selected_real_kind(15)
+  integer, parameter :: ii = selected_int_kind(9)
+  integer, parameter :: il = selected_int_kind(18)
+  real(dp), intent(inout) :: x
+  dimension x(3)
+  real(sp), parameter :: three_s = 2._sp + 1._sp
+  real(dp), parameter :: three_d = 1._dp + 2._dp
+  integer(ii), parameter :: three_i = 2_ii + 1_ii
+  integer(il), parameter :: three_l = 1_il + 2_il
+  x(1) = x(1) + x(2) * three_s * three_i + x(3) * three_d * three_l
+  x(2) = x(2) * three_s
+  x(3) = x(3) * three_l
+  return
+end subroutine
diff --git a/.venv/lib/python3.12/site-packages/numpy/f2py/tests/src/parameter/constant_compound.f90 b/.venv/lib/python3.12/site-packages/numpy/f2py/tests/src/parameter/constant_compound.f90
new file mode 100644
index 00000000..e51f5e9b
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/numpy/f2py/tests/src/parameter/constant_compound.f90
@@ -0,0 +1,15 @@
+! Check that parameters are correct intercepted.
+! Constants with comma separations are commonly
+! used, for instance Pi = 3._dp
+subroutine foo_compound_int(x)
+  implicit none
+  integer, parameter :: ii = selected_int_kind(9)
+  integer(ii), intent(inout) :: x
+  dimension x(3)
+  integer(ii), parameter :: three = 3_ii
+  integer(ii), parameter :: two = 2_ii
+  integer(ii), parameter :: six = three * 1_ii * two
+
+  x(1) = x(1) + x(2) + x(3) * six
+  return
+end subroutine
diff --git a/.venv/lib/python3.12/site-packages/numpy/f2py/tests/src/parameter/constant_integer.f90 b/.venv/lib/python3.12/site-packages/numpy/f2py/tests/src/parameter/constant_integer.f90
new file mode 100644
index 00000000..aaa83d2e
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/numpy/f2py/tests/src/parameter/constant_integer.f90
@@ -0,0 +1,22 @@
+! Check that parameters are correct intercepted.
+! Constants with comma separations are commonly
+! used, for instance Pi = 3._dp
+subroutine foo_int(x)
+  implicit none
+  integer, parameter :: ii = selected_int_kind(9)
+  integer(ii), intent(inout) :: x
+  dimension x(3)
+  integer(ii), parameter :: three = 3_ii
+  x(1) = x(1) + x(2) + x(3) * three
+  return
+end subroutine
+
+subroutine foo_long(x)
+  implicit none
+  integer, parameter :: ii = selected_int_kind(18)
+  integer(ii), intent(inout) :: x
+  dimension x(3)
+  integer(ii), parameter :: three = 3_ii
+  x(1) = x(1) + x(2) + x(3) * three
+  return
+end subroutine
diff --git a/.venv/lib/python3.12/site-packages/numpy/f2py/tests/src/parameter/constant_non_compound.f90 b/.venv/lib/python3.12/site-packages/numpy/f2py/tests/src/parameter/constant_non_compound.f90
new file mode 100644
index 00000000..62c9a5b9
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/numpy/f2py/tests/src/parameter/constant_non_compound.f90
@@ -0,0 +1,23 @@
+! Check that parameters are correct intercepted.
+! Specifically that types of constants without 
+! compound kind specs are correctly inferred
+! adapted Gibbs iteration code from pymc 
+! for this test case 
+subroutine foo_non_compound_int(x)
+  implicit none
+  integer, parameter :: ii = selected_int_kind(9)
+
+  integer(ii)   maxiterates
+  parameter (maxiterates=2)
+
+  integer(ii)   maxseries
+  parameter (maxseries=2)
+
+  integer(ii)   wasize
+  parameter (wasize=maxiterates*maxseries)
+  integer(ii), intent(inout) :: x
+  dimension x(wasize)
+
+  x(1) = x(1) + x(2) + x(3) + x(4) * wasize
+  return
+end subroutine
diff --git a/.venv/lib/python3.12/site-packages/numpy/f2py/tests/src/parameter/constant_real.f90 b/.venv/lib/python3.12/site-packages/numpy/f2py/tests/src/parameter/constant_real.f90
new file mode 100644
index 00000000..02ac9dd9
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/numpy/f2py/tests/src/parameter/constant_real.f90
@@ -0,0 +1,23 @@
+! Check that parameters are correct intercepted.
+! Constants with comma separations are commonly
+! used, for instance Pi = 3._dp
+subroutine foo_single(x)
+  implicit none
+  integer, parameter :: rp = selected_real_kind(6)
+  real(rp), intent(inout) :: x
+  dimension x(3)
+  real(rp), parameter :: three = 3._rp
+  x(1) = x(1) + x(2) + x(3) * three
+  return
+end subroutine
+
+subroutine foo_double(x)
+  implicit none
+  integer, parameter :: rp = selected_real_kind(15)
+  real(rp), intent(inout) :: x
+  dimension x(3)
+  real(rp), parameter :: three = 3._rp
+  x(1) = x(1) + x(2) + x(3) * three
+  return
+end subroutine
+
diff --git a/.venv/lib/python3.12/site-packages/numpy/f2py/tests/src/quoted_character/foo.f b/.venv/lib/python3.12/site-packages/numpy/f2py/tests/src/quoted_character/foo.f
new file mode 100644
index 00000000..9dc1cfa4
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/numpy/f2py/tests/src/quoted_character/foo.f
@@ -0,0 +1,14 @@
+      SUBROUTINE FOO(OUT1, OUT2, OUT3, OUT4, OUT5, OUT6)
+      CHARACTER SINGLE, DOUBLE, SEMICOL, EXCLA, OPENPAR, CLOSEPAR
+      PARAMETER (SINGLE="'", DOUBLE='"', SEMICOL=';', EXCLA="!",
+     1           OPENPAR="(", CLOSEPAR=")")
+      CHARACTER OUT1, OUT2, OUT3, OUT4, OUT5, OUT6
+Cf2py intent(out) OUT1, OUT2, OUT3, OUT4, OUT5, OUT6
+      OUT1 = SINGLE
+      OUT2 = DOUBLE
+      OUT3 = SEMICOL
+      OUT4 = EXCLA
+      OUT5 = OPENPAR
+      OUT6 = CLOSEPAR
+      RETURN
+      END
diff --git a/.venv/lib/python3.12/site-packages/numpy/f2py/tests/src/regression/gh25337/data.f90 b/.venv/lib/python3.12/site-packages/numpy/f2py/tests/src/regression/gh25337/data.f90
new file mode 100644
index 00000000..483d13ce
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/numpy/f2py/tests/src/regression/gh25337/data.f90
@@ -0,0 +1,8 @@
+module data
+   real(8) :: shift
+contains
+   subroutine set_shift(in_shift)
+      real(8), intent(in) :: in_shift
+      shift = in_shift
+   end subroutine set_shift
+end module data
diff --git a/.venv/lib/python3.12/site-packages/numpy/f2py/tests/src/regression/gh25337/use_data.f90 b/.venv/lib/python3.12/site-packages/numpy/f2py/tests/src/regression/gh25337/use_data.f90
new file mode 100644
index 00000000..b3fae8b8
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/numpy/f2py/tests/src/regression/gh25337/use_data.f90
@@ -0,0 +1,6 @@
+subroutine shift_a(dim_a, a)
+    use data, only: shift
+    integer, intent(in) :: dim_a
+    real(8), intent(inout), dimension(dim_a) :: a
+    a = a + shift
+end subroutine shift_a
diff --git a/.venv/lib/python3.12/site-packages/numpy/f2py/tests/src/regression/inout.f90 b/.venv/lib/python3.12/site-packages/numpy/f2py/tests/src/regression/inout.f90
new file mode 100644
index 00000000..80cdad90
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/numpy/f2py/tests/src/regression/inout.f90
@@ -0,0 +1,9 @@
+! Check that intent(in out) translates as intent(inout).
+! The separation seems to be a common usage.
+      subroutine foo(x)
+          implicit none
+          real(4), intent(in out) :: x
+          dimension x(3)
+          x(1) = x(1) + x(2) + x(3)
+          return
+      end
diff --git a/.venv/lib/python3.12/site-packages/numpy/f2py/tests/src/return_character/foo77.f b/.venv/lib/python3.12/site-packages/numpy/f2py/tests/src/return_character/foo77.f
new file mode 100644
index 00000000..facae101
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/numpy/f2py/tests/src/return_character/foo77.f
@@ -0,0 +1,45 @@
+       function t0(value)
+         character value
+         character t0
+         t0 = value
+       end
+       function t1(value)
+         character*1 value
+         character*1 t1
+         t1 = value
+       end
+       function t5(value)
+         character*5 value
+         character*5 t5
+         t5 = value
+       end
+       function ts(value)
+         character*(*) value
+         character*(*) ts
+         ts = value
+       end
+
+       subroutine s0(t0,value)
+         character value
+         character t0
+cf2py    intent(out) t0
+         t0 = value
+       end
+       subroutine s1(t1,value)
+         character*1 value
+         character*1 t1
+cf2py    intent(out) t1
+         t1 = value
+       end
+       subroutine s5(t5,value)
+         character*5 value
+         character*5 t5
+cf2py    intent(out) t5
+         t5 = value
+       end
+       subroutine ss(ts,value)
+         character*(*) value
+         character*10 ts
+cf2py    intent(out) ts
+         ts = value
+       end
diff --git a/.venv/lib/python3.12/site-packages/numpy/f2py/tests/src/return_character/foo90.f90 b/.venv/lib/python3.12/site-packages/numpy/f2py/tests/src/return_character/foo90.f90
new file mode 100644
index 00000000..36182bcf
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/numpy/f2py/tests/src/return_character/foo90.f90
@@ -0,0 +1,48 @@
+module f90_return_char
+  contains
+       function t0(value)
+         character :: value
+         character :: t0
+         t0 = value
+       end function t0
+       function t1(value)
+         character(len=1) :: value
+         character(len=1) :: t1
+         t1 = value
+       end function t1
+       function t5(value)
+         character(len=5) :: value
+         character(len=5) :: t5
+         t5 = value
+       end function t5
+       function ts(value)
+         character(len=*) :: value
+         character(len=10) :: ts
+         ts = value
+       end function ts
+
+       subroutine s0(t0,value)
+         character :: value
+         character :: t0
+!f2py    intent(out) t0
+         t0 = value
+       end subroutine s0
+       subroutine s1(t1,value)
+         character(len=1) :: value
+         character(len=1) :: t1
+!f2py    intent(out) t1
+         t1 = value
+       end subroutine s1
+       subroutine s5(t5,value)
+         character(len=5) :: value
+         character(len=5) :: t5
+!f2py    intent(out) t5
+         t5 = value
+       end subroutine s5
+       subroutine ss(ts,value)
+         character(len=*) :: value
+         character(len=10) :: ts
+!f2py    intent(out) ts
+         ts = value
+       end subroutine ss
+end module f90_return_char
diff --git a/.venv/lib/python3.12/site-packages/numpy/f2py/tests/src/return_complex/foo77.f b/.venv/lib/python3.12/site-packages/numpy/f2py/tests/src/return_complex/foo77.f
new file mode 100644
index 00000000..37a1ec84
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/numpy/f2py/tests/src/return_complex/foo77.f
@@ -0,0 +1,45 @@
+       function t0(value)
+         complex value
+         complex t0
+         t0 = value
+       end
+       function t8(value)
+         complex*8 value
+         complex*8 t8
+         t8 = value
+       end
+       function t16(value)
+         complex*16 value
+         complex*16 t16
+         t16 = value
+       end
+       function td(value)
+         double complex value
+         double complex td
+         td = value
+       end
+
+       subroutine s0(t0,value)
+         complex value
+         complex t0
+cf2py    intent(out) t0
+         t0 = value
+       end
+       subroutine s8(t8,value)
+         complex*8 value
+         complex*8 t8
+cf2py    intent(out) t8
+         t8 = value
+       end
+       subroutine s16(t16,value)
+         complex*16 value
+         complex*16 t16
+cf2py    intent(out) t16
+         t16 = value
+       end
+       subroutine sd(td,value)
+         double complex value
+         double complex td
+cf2py    intent(out) td
+         td = value
+       end
diff --git a/.venv/lib/python3.12/site-packages/numpy/f2py/tests/src/return_complex/foo90.f90 b/.venv/lib/python3.12/site-packages/numpy/f2py/tests/src/return_complex/foo90.f90
new file mode 100644
index 00000000..adc27b47
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/numpy/f2py/tests/src/return_complex/foo90.f90
@@ -0,0 +1,48 @@
+module f90_return_complex
+  contains
+       function t0(value)
+         complex :: value
+         complex :: t0
+         t0 = value
+       end function t0
+       function t8(value)
+         complex(kind=4) :: value
+         complex(kind=4) :: t8
+         t8 = value
+       end function t8
+       function t16(value)
+         complex(kind=8) :: value
+         complex(kind=8) :: t16
+         t16 = value
+       end function t16
+       function td(value)
+         double complex :: value
+         double complex :: td
+         td = value
+       end function td
+
+       subroutine s0(t0,value)
+         complex :: value
+         complex :: t0
+!f2py    intent(out) t0
+         t0 = value
+       end subroutine s0
+       subroutine s8(t8,value)
+         complex(kind=4) :: value
+         complex(kind=4) :: t8
+!f2py    intent(out) t8
+         t8 = value
+       end subroutine s8
+       subroutine s16(t16,value)
+         complex(kind=8) :: value
+         complex(kind=8) :: t16
+!f2py    intent(out) t16
+         t16 = value
+       end subroutine s16
+       subroutine sd(td,value)
+         double complex :: value
+         double complex :: td
+!f2py    intent(out) td
+         td = value
+       end subroutine sd
+end module f90_return_complex
diff --git a/.venv/lib/python3.12/site-packages/numpy/f2py/tests/src/return_integer/foo77.f b/.venv/lib/python3.12/site-packages/numpy/f2py/tests/src/return_integer/foo77.f
new file mode 100644
index 00000000..1ab895b9
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/numpy/f2py/tests/src/return_integer/foo77.f
@@ -0,0 +1,56 @@
+       function t0(value)
+         integer value
+         integer t0
+         t0 = value
+       end
+       function t1(value)
+         integer*1 value
+         integer*1 t1
+         t1 = value
+       end
+       function t2(value)
+         integer*2 value
+         integer*2 t2
+         t2 = value
+       end
+       function t4(value)
+         integer*4 value
+         integer*4 t4
+         t4 = value
+       end
+       function t8(value)
+         integer*8 value
+         integer*8 t8
+         t8 = value
+       end
+
+       subroutine s0(t0,value)
+         integer value
+         integer t0
+cf2py    intent(out) t0
+         t0 = value
+       end
+       subroutine s1(t1,value)
+         integer*1 value
+         integer*1 t1
+cf2py    intent(out) t1
+         t1 = value
+       end
+       subroutine s2(t2,value)
+         integer*2 value
+         integer*2 t2
+cf2py    intent(out) t2
+         t2 = value
+       end
+       subroutine s4(t4,value)
+         integer*4 value
+         integer*4 t4
+cf2py    intent(out) t4
+         t4 = value
+       end
+       subroutine s8(t8,value)
+         integer*8 value
+         integer*8 t8
+cf2py    intent(out) t8
+         t8 = value
+       end
diff --git a/.venv/lib/python3.12/site-packages/numpy/f2py/tests/src/return_integer/foo90.f90 b/.venv/lib/python3.12/site-packages/numpy/f2py/tests/src/return_integer/foo90.f90
new file mode 100644
index 00000000..ba9249aa
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/numpy/f2py/tests/src/return_integer/foo90.f90
@@ -0,0 +1,59 @@
+module f90_return_integer
+  contains
+       function t0(value)
+         integer :: value
+         integer :: t0
+         t0 = value
+       end function t0
+       function t1(value)
+         integer(kind=1) :: value
+         integer(kind=1) :: t1
+         t1 = value
+       end function t1
+       function t2(value)
+         integer(kind=2) :: value
+         integer(kind=2) :: t2
+         t2 = value
+       end function t2
+       function t4(value)
+         integer(kind=4) :: value
+         integer(kind=4) :: t4
+         t4 = value
+       end function t4
+       function t8(value)
+         integer(kind=8) :: value
+         integer(kind=8) :: t8
+         t8 = value
+       end function t8
+
+       subroutine s0(t0,value)
+         integer :: value
+         integer :: t0
+!f2py    intent(out) t0
+         t0 = value
+       end subroutine s0
+       subroutine s1(t1,value)
+         integer(kind=1) :: value
+         integer(kind=1) :: t1
+!f2py    intent(out) t1
+         t1 = value
+       end subroutine s1
+       subroutine s2(t2,value)
+         integer(kind=2) :: value
+         integer(kind=2) :: t2
+!f2py    intent(out) t2
+         t2 = value
+       end subroutine s2
+       subroutine s4(t4,value)
+         integer(kind=4) :: value
+         integer(kind=4) :: t4
+!f2py    intent(out) t4
+         t4 = value
+       end subroutine s4
+       subroutine s8(t8,value)
+         integer(kind=8) :: value
+         integer(kind=8) :: t8
+!f2py    intent(out) t8
+         t8 = value
+       end subroutine s8
+end module f90_return_integer
diff --git a/.venv/lib/python3.12/site-packages/numpy/f2py/tests/src/return_logical/foo77.f b/.venv/lib/python3.12/site-packages/numpy/f2py/tests/src/return_logical/foo77.f
new file mode 100644
index 00000000..ef530145
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/numpy/f2py/tests/src/return_logical/foo77.f
@@ -0,0 +1,56 @@
+       function t0(value)
+         logical value
+         logical t0
+         t0 = value
+       end
+       function t1(value)
+         logical*1 value
+         logical*1 t1
+         t1 = value
+       end
+       function t2(value)
+         logical*2 value
+         logical*2 t2
+         t2 = value
+       end
+       function t4(value)
+         logical*4 value
+         logical*4 t4
+         t4 = value
+       end
+c       function t8(value)
+c         logical*8 value
+c         logical*8 t8
+c         t8 = value
+c       end
+
+       subroutine s0(t0,value)
+         logical value
+         logical t0
+cf2py    intent(out) t0
+         t0 = value
+       end
+       subroutine s1(t1,value)
+         logical*1 value
+         logical*1 t1
+cf2py    intent(out) t1
+         t1 = value
+       end
+       subroutine s2(t2,value)
+         logical*2 value
+         logical*2 t2
+cf2py    intent(out) t2
+         t2 = value
+       end
+       subroutine s4(t4,value)
+         logical*4 value
+         logical*4 t4
+cf2py    intent(out) t4
+         t4 = value
+       end
+c       subroutine s8(t8,value)
+c         logical*8 value
+c         logical*8 t8
+cf2py    intent(out) t8
+c         t8 = value
+c       end
diff --git a/.venv/lib/python3.12/site-packages/numpy/f2py/tests/src/return_logical/foo90.f90 b/.venv/lib/python3.12/site-packages/numpy/f2py/tests/src/return_logical/foo90.f90
new file mode 100644
index 00000000..a4526468
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/numpy/f2py/tests/src/return_logical/foo90.f90
@@ -0,0 +1,59 @@
+module f90_return_logical
+  contains
+       function t0(value)
+         logical :: value
+         logical :: t0
+         t0 = value
+       end function t0
+       function t1(value)
+         logical(kind=1) :: value
+         logical(kind=1) :: t1
+         t1 = value
+       end function t1
+       function t2(value)
+         logical(kind=2) :: value
+         logical(kind=2) :: t2
+         t2 = value
+       end function t2
+       function t4(value)
+         logical(kind=4) :: value
+         logical(kind=4) :: t4
+         t4 = value
+       end function t4
+       function t8(value)
+         logical(kind=8) :: value
+         logical(kind=8) :: t8
+         t8 = value
+       end function t8
+
+       subroutine s0(t0,value)
+         logical :: value
+         logical :: t0
+!f2py    intent(out) t0
+         t0 = value
+       end subroutine s0
+       subroutine s1(t1,value)
+         logical(kind=1) :: value
+         logical(kind=1) :: t1
+!f2py    intent(out) t1
+         t1 = value
+       end subroutine s1
+       subroutine s2(t2,value)
+         logical(kind=2) :: value
+         logical(kind=2) :: t2
+!f2py    intent(out) t2
+         t2 = value
+       end subroutine s2
+       subroutine s4(t4,value)
+         logical(kind=4) :: value
+         logical(kind=4) :: t4
+!f2py    intent(out) t4
+         t4 = value
+       end subroutine s4
+       subroutine s8(t8,value)
+         logical(kind=8) :: value
+         logical(kind=8) :: t8
+!f2py    intent(out) t8
+         t8 = value
+       end subroutine s8
+end module f90_return_logical
diff --git a/.venv/lib/python3.12/site-packages/numpy/f2py/tests/src/return_real/foo77.f b/.venv/lib/python3.12/site-packages/numpy/f2py/tests/src/return_real/foo77.f
new file mode 100644
index 00000000..bf43dbf1
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/numpy/f2py/tests/src/return_real/foo77.f
@@ -0,0 +1,45 @@
+       function t0(value)
+         real value
+         real t0
+         t0 = value
+       end
+       function t4(value)
+         real*4 value
+         real*4 t4
+         t4 = value
+       end
+       function t8(value)
+         real*8 value
+         real*8 t8
+         t8 = value
+       end
+       function td(value)
+         double precision value
+         double precision td
+         td = value
+       end
+
+       subroutine s0(t0,value)
+         real value
+         real t0
+cf2py    intent(out) t0
+         t0 = value
+       end
+       subroutine s4(t4,value)
+         real*4 value
+         real*4 t4
+cf2py    intent(out) t4
+         t4 = value
+       end
+       subroutine s8(t8,value)
+         real*8 value
+         real*8 t8
+cf2py    intent(out) t8
+         t8 = value
+       end
+       subroutine sd(td,value)
+         double precision value
+         double precision td
+cf2py    intent(out) td
+         td = value
+       end
diff --git a/.venv/lib/python3.12/site-packages/numpy/f2py/tests/src/return_real/foo90.f90 b/.venv/lib/python3.12/site-packages/numpy/f2py/tests/src/return_real/foo90.f90
new file mode 100644
index 00000000..df971998
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/numpy/f2py/tests/src/return_real/foo90.f90
@@ -0,0 +1,48 @@
+module f90_return_real
+  contains
+       function t0(value)
+         real :: value
+         real :: t0
+         t0 = value
+       end function t0
+       function t4(value)
+         real(kind=4) :: value
+         real(kind=4) :: t4
+         t4 = value
+       end function t4
+       function t8(value)
+         real(kind=8) :: value
+         real(kind=8) :: t8
+         t8 = value
+       end function t8
+       function td(value)
+         double precision :: value
+         double precision :: td
+         td = value
+       end function td
+
+       subroutine s0(t0,value)
+         real :: value
+         real :: t0
+!f2py    intent(out) t0
+         t0 = value
+       end subroutine s0
+       subroutine s4(t4,value)
+         real(kind=4) :: value
+         real(kind=4) :: t4
+!f2py    intent(out) t4
+         t4 = value
+       end subroutine s4
+       subroutine s8(t8,value)
+         real(kind=8) :: value
+         real(kind=8) :: t8
+!f2py    intent(out) t8
+         t8 = value
+       end subroutine s8
+       subroutine sd(td,value)
+         double precision :: value
+         double precision :: td
+!f2py    intent(out) td
+         td = value
+       end subroutine sd
+end module f90_return_real
diff --git a/.venv/lib/python3.12/site-packages/numpy/f2py/tests/src/size/foo.f90 b/.venv/lib/python3.12/site-packages/numpy/f2py/tests/src/size/foo.f90
new file mode 100644
index 00000000..5b66f8c4
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/numpy/f2py/tests/src/size/foo.f90
@@ -0,0 +1,44 @@
+
+subroutine foo(a, n, m, b)
+  implicit none
+
+  real, intent(in) :: a(n, m)
+  integer, intent(in) :: n, m
+  real, intent(out) :: b(size(a, 1))
+
+  integer :: i
+
+  do i = 1, size(b)
+    b(i) = sum(a(i,:))
+  enddo
+end subroutine
+
+subroutine trans(x,y)
+  implicit none
+  real, intent(in), dimension(:,:) :: x
+  real, intent(out), dimension( size(x,2), size(x,1) ) :: y
+  integer :: N, M, i, j
+  N = size(x,1)
+  M = size(x,2)
+  DO i=1,N
+     do j=1,M
+        y(j,i) = x(i,j)
+     END DO
+  END DO
+end subroutine trans
+
+subroutine flatten(x,y)
+  implicit none
+  real, intent(in), dimension(:,:) :: x
+  real, intent(out), dimension( size(x) ) :: y
+  integer :: N, M, i, j, k
+  N = size(x,1)
+  M = size(x,2)
+  k = 1
+  DO i=1,N
+     do j=1,M
+        y(k) = x(i,j)
+        k = k + 1
+     END DO
+  END DO
+end subroutine flatten
diff --git a/.venv/lib/python3.12/site-packages/numpy/f2py/tests/src/string/char.f90 b/.venv/lib/python3.12/site-packages/numpy/f2py/tests/src/string/char.f90
new file mode 100644
index 00000000..bb7985ce
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/numpy/f2py/tests/src/string/char.f90
@@ -0,0 +1,29 @@
+MODULE char_test
+
+CONTAINS
+
+SUBROUTINE change_strings(strings, n_strs, out_strings)
+    IMPLICIT NONE
+
+    ! Inputs
+    INTEGER, INTENT(IN) :: n_strs
+    CHARACTER, INTENT(IN), DIMENSION(2,n_strs) :: strings
+    CHARACTER, INTENT(OUT), DIMENSION(2,n_strs) :: out_strings
+
+!f2py INTEGER, INTENT(IN) :: n_strs
+!f2py CHARACTER, INTENT(IN), DIMENSION(2,n_strs) :: strings
+!f2py CHARACTER, INTENT(OUT), DIMENSION(2,n_strs) :: strings
+
+    ! Misc.
+    INTEGER*4 :: j
+
+
+    DO j=1, n_strs
+        out_strings(1,j) = strings(1,j)
+        out_strings(2,j) = 'A'
+    END DO
+
+END SUBROUTINE change_strings
+
+END MODULE char_test
+
diff --git a/.venv/lib/python3.12/site-packages/numpy/f2py/tests/src/string/fixed_string.f90 b/.venv/lib/python3.12/site-packages/numpy/f2py/tests/src/string/fixed_string.f90
new file mode 100644
index 00000000..7fd15854
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/numpy/f2py/tests/src/string/fixed_string.f90
@@ -0,0 +1,34 @@
+function sint(s) result(i)
+   implicit none
+   character(len=*) :: s
+   integer :: j, i
+   i = 0
+   do j=len(s), 1, -1
+    if (.not.((i.eq.0).and.(s(j:j).eq.' '))) then
+      i = i + ichar(s(j:j)) * 10 ** (j - 1)
+    endif
+   end do
+   return
+ end function sint
+
+ function test_in_bytes4(a) result (i)
+   implicit none
+   integer :: sint
+   character(len=4) :: a
+   integer :: i
+   i = sint(a)
+   a(1:1) = 'A'
+   return
+ end function test_in_bytes4
+
+ function test_inout_bytes4(a) result (i)
+   implicit none
+   integer :: sint
+   character(len=4), intent(inout) :: a
+   integer :: i
+   if (a(1:1).ne.' ') then
+     a(1:1) = 'E'
+   endif
+   i = sint(a)
+   return
+ end function test_inout_bytes4
diff --git a/.venv/lib/python3.12/site-packages/numpy/f2py/tests/src/string/gh24008.f b/.venv/lib/python3.12/site-packages/numpy/f2py/tests/src/string/gh24008.f
new file mode 100644
index 00000000..ab64cf77
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/numpy/f2py/tests/src/string/gh24008.f
@@ -0,0 +1,8 @@
+      SUBROUTINE GREET(NAME, GREETING)
+      CHARACTER NAME*(*), GREETING*(*)
+      CHARACTER*(50) MESSAGE
+
+      MESSAGE = 'Hello, ' // NAME // ', ' // GREETING
+c$$$      PRINT *, MESSAGE
+
+      END SUBROUTINE GREET
diff --git a/.venv/lib/python3.12/site-packages/numpy/f2py/tests/src/string/gh24662.f90 b/.venv/lib/python3.12/site-packages/numpy/f2py/tests/src/string/gh24662.f90
new file mode 100644
index 00000000..ca53413c
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/numpy/f2py/tests/src/string/gh24662.f90
@@ -0,0 +1,7 @@
+subroutine string_inout_optional(output)
+    implicit none
+    character*(32), optional, intent(inout) :: output
+    if (present(output)) then
+      output="output string"
+    endif
+end subroutine
diff --git a/.venv/lib/python3.12/site-packages/numpy/f2py/tests/src/string/gh25286.f90 b/.venv/lib/python3.12/site-packages/numpy/f2py/tests/src/string/gh25286.f90
new file mode 100644
index 00000000..db1c7100
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/numpy/f2py/tests/src/string/gh25286.f90
@@ -0,0 +1,14 @@
+subroutine charint(trans, info)
+    character, intent(in) :: trans
+    integer, intent(out) :: info
+    if (trans == 'N') then
+        info = 1
+    else if (trans == 'T') then
+        info = 2
+    else if (trans == 'C') then
+        info = 3
+    else
+        info = -1
+    end if
+
+end subroutine charint
diff --git a/.venv/lib/python3.12/site-packages/numpy/f2py/tests/src/string/gh25286.pyf b/.venv/lib/python3.12/site-packages/numpy/f2py/tests/src/string/gh25286.pyf
new file mode 100644
index 00000000..7b960907
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/numpy/f2py/tests/src/string/gh25286.pyf
@@ -0,0 +1,12 @@
+python module _char_handling_test
+    interface
+    subroutine charint(trans, info)
+        callstatement (*f2py_func)(&trans, &info)
+        callprotoargument char*, int*
+
+        character, intent(in), check(trans=='N'||trans=='T'||trans=='C') :: trans = 'N'
+        integer intent(out) :: info
+
+    end subroutine charint
+    end interface
+end python module _char_handling_test
diff --git a/.venv/lib/python3.12/site-packages/numpy/f2py/tests/src/string/gh25286_bc.pyf b/.venv/lib/python3.12/site-packages/numpy/f2py/tests/src/string/gh25286_bc.pyf
new file mode 100644
index 00000000..e7b10fa9
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/numpy/f2py/tests/src/string/gh25286_bc.pyf
@@ -0,0 +1,12 @@
+python module _char_handling_test
+    interface
+    subroutine charint(trans, info)
+        callstatement (*f2py_func)(&trans, &info)
+        callprotoargument char*, int*
+
+        character, intent(in), check(*trans=='N'||*trans=='T'||*trans=='C') :: trans = 'N'
+        integer intent(out) :: info
+
+    end subroutine charint
+    end interface
+end python module _char_handling_test
diff --git a/.venv/lib/python3.12/site-packages/numpy/f2py/tests/src/string/scalar_string.f90 b/.venv/lib/python3.12/site-packages/numpy/f2py/tests/src/string/scalar_string.f90
new file mode 100644
index 00000000..f8f07617
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/numpy/f2py/tests/src/string/scalar_string.f90
@@ -0,0 +1,9 @@
+MODULE string_test
+
+  character(len=8) :: string
+  character string77 * 8
+
+  character(len=12), dimension(5,7) :: strarr
+  character strarr77(5,7) * 12
+
+END MODULE string_test
diff --git a/.venv/lib/python3.12/site-packages/numpy/f2py/tests/src/string/string.f b/.venv/lib/python3.12/site-packages/numpy/f2py/tests/src/string/string.f
new file mode 100644
index 00000000..5210ca4d
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/numpy/f2py/tests/src/string/string.f
@@ -0,0 +1,12 @@
+C FILE: STRING.F
+      SUBROUTINE FOO(A,B,C,D)
+      CHARACTER*5 A, B
+      CHARACTER*(*) C,D
+Cf2py intent(in) a,c
+Cf2py intent(inout) b,d
+      A(1:1) = 'A'
+      B(1:1) = 'B'
+      C(1:1) = 'C'
+      D(1:1) = 'D'
+      END
+C END OF FILE STRING.F
diff --git a/.venv/lib/python3.12/site-packages/numpy/f2py/tests/src/value_attrspec/gh21665.f90 b/.venv/lib/python3.12/site-packages/numpy/f2py/tests/src/value_attrspec/gh21665.f90
new file mode 100644
index 00000000..7d9dc0fd
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/numpy/f2py/tests/src/value_attrspec/gh21665.f90
@@ -0,0 +1,9 @@
+module fortfuncs
+  implicit none
+contains
+  subroutine square(x,y)
+    integer, intent(in), value :: x
+    integer, intent(out) :: y
+    y = x*x
+  end subroutine square
+end module fortfuncs
diff --git a/.venv/lib/python3.12/site-packages/numpy/f2py/tests/test_abstract_interface.py b/.venv/lib/python3.12/site-packages/numpy/f2py/tests/test_abstract_interface.py
new file mode 100644
index 00000000..42902913
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/numpy/f2py/tests/test_abstract_interface.py
@@ -0,0 +1,25 @@
+from pathlib import Path
+import pytest
+import textwrap
+from . import util
+from numpy.f2py import crackfortran
+from numpy.testing import IS_WASM
+
+
+@pytest.mark.skipif(IS_WASM, reason="Cannot start subprocess")
+class TestAbstractInterface(util.F2PyTest):
+    sources = [util.getpath("tests", "src", "abstract_interface", "foo.f90")]
+
+    skip = ["add1", "add2"]
+
+    def test_abstract_interface(self):
+        assert self.module.ops_module.foo(3, 5) == (8, 13)
+
+    def test_parse_abstract_interface(self):
+        # Test gh18403
+        fpath = util.getpath("tests", "src", "abstract_interface",
+                             "gh18403_mod.f90")
+        mod = crackfortran.crackfortran([str(fpath)])
+        assert len(mod) == 1
+        assert len(mod[0]["body"]) == 1
+        assert mod[0]["body"][0]["block"] == "abstract interface"
diff --git a/.venv/lib/python3.12/site-packages/numpy/f2py/tests/test_array_from_pyobj.py b/.venv/lib/python3.12/site-packages/numpy/f2py/tests/test_array_from_pyobj.py
new file mode 100644
index 00000000..2b8c8def
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/numpy/f2py/tests/test_array_from_pyobj.py
@@ -0,0 +1,686 @@
+import os
+import sys
+import copy
+import platform
+import pytest
+
+import numpy as np
+
+from numpy.testing import assert_, assert_equal
+from numpy.core.multiarray import typeinfo as _typeinfo
+from . import util
+
+wrap = None
+
+# Extend core typeinfo with CHARACTER to test dtype('c')
+_ti = _typeinfo['STRING']
+typeinfo = dict(
+    CHARACTER=type(_ti)(('c', _ti.num, 8, _ti.alignment, _ti.type)),
+    **_typeinfo)
+
+
+def setup_module():
+    """
+    Build the required testing extension module
+
+    """
+    global wrap
+
+    # Check compiler availability first
+    if not util.has_c_compiler():
+        pytest.skip("No C compiler available")
+
+    if wrap is None:
+        config_code = """
+        config.add_extension('test_array_from_pyobj_ext',
+                             sources=['wrapmodule.c', 'fortranobject.c'],
+                             define_macros=[])
+        """
+        d = os.path.dirname(__file__)
+        src = [
+            util.getpath("tests", "src", "array_from_pyobj", "wrapmodule.c"),
+            util.getpath("src", "fortranobject.c"),
+            util.getpath("src", "fortranobject.h"),
+        ]
+        wrap = util.build_module_distutils(src, config_code,
+                                           "test_array_from_pyobj_ext")
+
+
+def flags_info(arr):
+    flags = wrap.array_attrs(arr)[6]
+    return flags2names(flags)
+
+
+def flags2names(flags):
+    info = []
+    for flagname in [
+            "CONTIGUOUS",
+            "FORTRAN",
+            "OWNDATA",
+            "ENSURECOPY",
+            "ENSUREARRAY",
+            "ALIGNED",
+            "NOTSWAPPED",
+            "WRITEABLE",
+            "WRITEBACKIFCOPY",
+            "UPDATEIFCOPY",
+            "BEHAVED",
+            "BEHAVED_RO",
+            "CARRAY",
+            "FARRAY",
+    ]:
+        if abs(flags) & getattr(wrap, flagname, 0):
+            info.append(flagname)
+    return info
+
+
+class Intent:
+    def __init__(self, intent_list=[]):
+        self.intent_list = intent_list[:]
+        flags = 0
+        for i in intent_list:
+            if i == "optional":
+                flags |= wrap.F2PY_OPTIONAL
+            else:
+                flags |= getattr(wrap, "F2PY_INTENT_" + i.upper())
+        self.flags = flags
+
+    def __getattr__(self, name):
+        name = name.lower()
+        if name == "in_":
+            name = "in"
+        return self.__class__(self.intent_list + [name])
+
+    def __str__(self):
+        return "intent(%s)" % (",".join(self.intent_list))
+
+    def __repr__(self):
+        return "Intent(%r)" % (self.intent_list)
+
+    def is_intent(self, *names):
+        for name in names:
+            if name not in self.intent_list:
+                return False
+        return True
+
+    def is_intent_exact(self, *names):
+        return len(self.intent_list) == len(names) and self.is_intent(*names)
+
+
+intent = Intent()
+
+_type_names = [
+    "BOOL",
+    "BYTE",
+    "UBYTE",
+    "SHORT",
+    "USHORT",
+    "INT",
+    "UINT",
+    "LONG",
+    "ULONG",
+    "LONGLONG",
+    "ULONGLONG",
+    "FLOAT",
+    "DOUBLE",
+    "CFLOAT",
+    "STRING1",
+    "STRING5",
+    "CHARACTER",
+]
+
+_cast_dict = {"BOOL": ["BOOL"]}
+_cast_dict["BYTE"] = _cast_dict["BOOL"] + ["BYTE"]
+_cast_dict["UBYTE"] = _cast_dict["BOOL"] + ["UBYTE"]
+_cast_dict["BYTE"] = ["BYTE"]
+_cast_dict["UBYTE"] = ["UBYTE"]
+_cast_dict["SHORT"] = _cast_dict["BYTE"] + ["UBYTE", "SHORT"]
+_cast_dict["USHORT"] = _cast_dict["UBYTE"] + ["BYTE", "USHORT"]
+_cast_dict["INT"] = _cast_dict["SHORT"] + ["USHORT", "INT"]
+_cast_dict["UINT"] = _cast_dict["USHORT"] + ["SHORT", "UINT"]
+
+_cast_dict["LONG"] = _cast_dict["INT"] + ["LONG"]
+_cast_dict["ULONG"] = _cast_dict["UINT"] + ["ULONG"]
+
+_cast_dict["LONGLONG"] = _cast_dict["LONG"] + ["LONGLONG"]
+_cast_dict["ULONGLONG"] = _cast_dict["ULONG"] + ["ULONGLONG"]
+
+_cast_dict["FLOAT"] = _cast_dict["SHORT"] + ["USHORT", "FLOAT"]
+_cast_dict["DOUBLE"] = _cast_dict["INT"] + ["UINT", "FLOAT", "DOUBLE"]
+
+_cast_dict["CFLOAT"] = _cast_dict["FLOAT"] + ["CFLOAT"]
+
+_cast_dict['STRING1'] = ['STRING1']
+_cast_dict['STRING5'] = ['STRING5']
+_cast_dict['CHARACTER'] = ['CHARACTER']
+
+# 32 bit system malloc typically does not provide the alignment required by
+# 16 byte long double types this means the inout intent cannot be satisfied
+# and several tests fail as the alignment flag can be randomly true or fals
+# when numpy gains an aligned allocator the tests could be enabled again
+#
+# Furthermore, on macOS ARM64, LONGDOUBLE is an alias for DOUBLE.
+if ((np.intp().dtype.itemsize != 4 or np.clongdouble().dtype.alignment <= 8)
+        and sys.platform != "win32"
+        and (platform.system(), platform.processor()) != ("Darwin", "arm")):
+    _type_names.extend(["LONGDOUBLE", "CDOUBLE", "CLONGDOUBLE"])
+    _cast_dict["LONGDOUBLE"] = _cast_dict["LONG"] + [
+        "ULONG",
+        "FLOAT",
+        "DOUBLE",
+        "LONGDOUBLE",
+    ]
+    _cast_dict["CLONGDOUBLE"] = _cast_dict["LONGDOUBLE"] + [
+        "CFLOAT",
+        "CDOUBLE",
+        "CLONGDOUBLE",
+    ]
+    _cast_dict["CDOUBLE"] = _cast_dict["DOUBLE"] + ["CFLOAT", "CDOUBLE"]
+
+
+class Type:
+    _type_cache = {}
+
+    def __new__(cls, name):
+        if isinstance(name, np.dtype):
+            dtype0 = name
+            name = None
+            for n, i in typeinfo.items():
+                if not isinstance(i, type) and dtype0.type is i.type:
+                    name = n
+                    break
+        obj = cls._type_cache.get(name.upper(), None)
+        if obj is not None:
+            return obj
+        obj = object.__new__(cls)
+        obj._init(name)
+        cls._type_cache[name.upper()] = obj
+        return obj
+
+    def _init(self, name):
+        self.NAME = name.upper()
+
+        if self.NAME == 'CHARACTER':
+            info = typeinfo[self.NAME]
+            self.type_num = getattr(wrap, 'NPY_STRING')
+            self.elsize = 1
+            self.dtype = np.dtype('c')
+        elif self.NAME.startswith('STRING'):
+            info = typeinfo[self.NAME[:6]]
+            self.type_num = getattr(wrap, 'NPY_STRING')
+            self.elsize = int(self.NAME[6:] or 0)
+            self.dtype = np.dtype(f'S{self.elsize}')
+        else:
+            info = typeinfo[self.NAME]
+            self.type_num = getattr(wrap, 'NPY_' + self.NAME)
+            self.elsize = info.bits // 8
+            self.dtype = np.dtype(info.type)
+
+        assert self.type_num == info.num
+        self.type = info.type
+        self.dtypechar = info.char
+
+    def __repr__(self):
+        return (f"Type({self.NAME})|type_num={self.type_num},"
+                f" dtype={self.dtype},"
+                f" type={self.type}, elsize={self.elsize},"
+                f" dtypechar={self.dtypechar}")
+
+    def cast_types(self):
+        return [self.__class__(_m) for _m in _cast_dict[self.NAME]]
+
+    def all_types(self):
+        return [self.__class__(_m) for _m in _type_names]
+
+    def smaller_types(self):
+        bits = typeinfo[self.NAME].alignment
+        types = []
+        for name in _type_names:
+            if typeinfo[name].alignment < bits:
+                types.append(Type(name))
+        return types
+
+    def equal_types(self):
+        bits = typeinfo[self.NAME].alignment
+        types = []
+        for name in _type_names:
+            if name == self.NAME:
+                continue
+            if typeinfo[name].alignment == bits:
+                types.append(Type(name))
+        return types
+
+    def larger_types(self):
+        bits = typeinfo[self.NAME].alignment
+        types = []
+        for name in _type_names:
+            if typeinfo[name].alignment > bits:
+                types.append(Type(name))
+        return types
+
+
+class Array:
+
+    def __repr__(self):
+        return (f'Array({self.type}, {self.dims}, {self.intent},'
+                f' {self.obj})|arr={self.arr}')
+
+    def __init__(self, typ, dims, intent, obj):
+        self.type = typ
+        self.dims = dims
+        self.intent = intent
+        self.obj_copy = copy.deepcopy(obj)
+        self.obj = obj
+
+        # arr.dtypechar may be different from typ.dtypechar
+        self.arr = wrap.call(typ.type_num,
+                             typ.elsize,
+                             dims, intent.flags, obj)
+
+        assert isinstance(self.arr, np.ndarray)
+
+        self.arr_attr = wrap.array_attrs(self.arr)
+
+        if len(dims) > 1:
+            if self.intent.is_intent("c"):
+                assert (intent.flags & wrap.F2PY_INTENT_C)
+                assert not self.arr.flags["FORTRAN"]
+                assert self.arr.flags["CONTIGUOUS"]
+                assert (not self.arr_attr[6] & wrap.FORTRAN)
+            else:
+                assert (not intent.flags & wrap.F2PY_INTENT_C)
+                assert self.arr.flags["FORTRAN"]
+                assert not self.arr.flags["CONTIGUOUS"]
+                assert (self.arr_attr[6] & wrap.FORTRAN)
+
+        if obj is None:
+            self.pyarr = None
+            self.pyarr_attr = None
+            return
+
+        if intent.is_intent("cache"):
+            assert isinstance(obj, np.ndarray), repr(type(obj))
+            self.pyarr = np.array(obj).reshape(*dims).copy()
+        else:
+            self.pyarr = np.array(
+                np.array(obj, dtype=typ.dtypechar).reshape(*dims),
+                order=self.intent.is_intent("c") and "C" or "F",
+            )
+            assert self.pyarr.dtype == typ
+        self.pyarr.setflags(write=self.arr.flags["WRITEABLE"])
+        assert self.pyarr.flags["OWNDATA"], (obj, intent)
+        self.pyarr_attr = wrap.array_attrs(self.pyarr)
+
+        if len(dims) > 1:
+            if self.intent.is_intent("c"):
+                assert not self.pyarr.flags["FORTRAN"]
+                assert self.pyarr.flags["CONTIGUOUS"]
+                assert (not self.pyarr_attr[6] & wrap.FORTRAN)
+            else:
+                assert self.pyarr.flags["FORTRAN"]
+                assert not self.pyarr.flags["CONTIGUOUS"]
+                assert (self.pyarr_attr[6] & wrap.FORTRAN)
+
+        assert self.arr_attr[1] == self.pyarr_attr[1]  # nd
+        assert self.arr_attr[2] == self.pyarr_attr[2]  # dimensions
+        if self.arr_attr[1] <= 1:
+            assert self.arr_attr[3] == self.pyarr_attr[3], repr((
+                self.arr_attr[3],
+                self.pyarr_attr[3],
+                self.arr.tobytes(),
+                self.pyarr.tobytes(),
+            ))  # strides
+        assert self.arr_attr[5][-2:] == self.pyarr_attr[5][-2:], repr((
+            self.arr_attr[5], self.pyarr_attr[5]
+            ))  # descr
+        assert self.arr_attr[6] == self.pyarr_attr[6], repr((
+            self.arr_attr[6],
+            self.pyarr_attr[6],
+            flags2names(0 * self.arr_attr[6] - self.pyarr_attr[6]),
+            flags2names(self.arr_attr[6]),
+            intent,
+        ))  # flags
+
+        if intent.is_intent("cache"):
+            assert self.arr_attr[5][3] >= self.type.elsize
+        else:
+            assert self.arr_attr[5][3] == self.type.elsize
+            assert (self.arr_equal(self.pyarr, self.arr))
+
+        if isinstance(self.obj, np.ndarray):
+            if typ.elsize == Type(obj.dtype).elsize:
+                if not intent.is_intent("copy") and self.arr_attr[1] <= 1:
+                    assert self.has_shared_memory()
+
+    def arr_equal(self, arr1, arr2):
+        if arr1.shape != arr2.shape:
+            return False
+        return (arr1 == arr2).all()
+
+    def __str__(self):
+        return str(self.arr)
+
+    def has_shared_memory(self):
+        """Check that created array shares data with input array."""
+        if self.obj is self.arr:
+            return True
+        if not isinstance(self.obj, np.ndarray):
+            return False
+        obj_attr = wrap.array_attrs(self.obj)
+        return obj_attr[0] == self.arr_attr[0]
+
+
+class TestIntent:
+    def test_in_out(self):
+        assert str(intent.in_.out) == "intent(in,out)"
+        assert intent.in_.c.is_intent("c")
+        assert not intent.in_.c.is_intent_exact("c")
+        assert intent.in_.c.is_intent_exact("c", "in")
+        assert intent.in_.c.is_intent_exact("in", "c")
+        assert not intent.in_.is_intent("c")
+
+
+class TestSharedMemory:
+
+    @pytest.fixture(autouse=True, scope="class", params=_type_names)
+    def setup_type(self, request):
+        request.cls.type = Type(request.param)
+        request.cls.array = lambda self, dims, intent, obj: Array(
+            Type(request.param), dims, intent, obj)
+
+    @property
+    def num2seq(self):
+        if self.type.NAME.startswith('STRING'):
+            elsize = self.type.elsize
+            return ['1' * elsize, '2' * elsize]
+        return [1, 2]
+
+    @property
+    def num23seq(self):
+        if self.type.NAME.startswith('STRING'):
+            elsize = self.type.elsize
+            return [['1' * elsize, '2' * elsize, '3' * elsize],
+                    ['4' * elsize, '5' * elsize, '6' * elsize]]
+        return [[1, 2, 3], [4, 5, 6]]
+
+    def test_in_from_2seq(self):
+        a = self.array([2], intent.in_, self.num2seq)
+        assert not a.has_shared_memory()
+
+    def test_in_from_2casttype(self):
+        for t in self.type.cast_types():
+            obj = np.array(self.num2seq, dtype=t.dtype)
+            a = self.array([len(self.num2seq)], intent.in_, obj)
+            if t.elsize == self.type.elsize:
+                assert a.has_shared_memory(), repr((self.type.dtype, t.dtype))
+            else:
+                assert not a.has_shared_memory()
+
+    @pytest.mark.parametrize("write", ["w", "ro"])
+    @pytest.mark.parametrize("order", ["C", "F"])
+    @pytest.mark.parametrize("inp", ["2seq", "23seq"])
+    def test_in_nocopy(self, write, order, inp):
+        """Test if intent(in) array can be passed without copies"""
+        seq = getattr(self, "num" + inp)
+        obj = np.array(seq, dtype=self.type.dtype, order=order)
+        obj.setflags(write=(write == 'w'))
+        a = self.array(obj.shape,
+                       ((order == 'C' and intent.in_.c) or intent.in_), obj)
+        assert a.has_shared_memory()
+
+    def test_inout_2seq(self):
+        obj = np.array(self.num2seq, dtype=self.type.dtype)
+        a = self.array([len(self.num2seq)], intent.inout, obj)
+        assert a.has_shared_memory()
+
+        try:
+            a = self.array([2], intent.in_.inout, self.num2seq)
+        except TypeError as msg:
+            if not str(msg).startswith(
+                    "failed to initialize intent(inout|inplace|cache) array"):
+                raise
+        else:
+            raise SystemError("intent(inout) should have failed on sequence")
+
+    def test_f_inout_23seq(self):
+        obj = np.array(self.num23seq, dtype=self.type.dtype, order="F")
+        shape = (len(self.num23seq), len(self.num23seq[0]))
+        a = self.array(shape, intent.in_.inout, obj)
+        assert a.has_shared_memory()
+
+        obj = np.array(self.num23seq, dtype=self.type.dtype, order="C")
+        shape = (len(self.num23seq), len(self.num23seq[0]))
+        try:
+            a = self.array(shape, intent.in_.inout, obj)
+        except ValueError as msg:
+            if not str(msg).startswith(
+                    "failed to initialize intent(inout) array"):
+                raise
+        else:
+            raise SystemError(
+                "intent(inout) should have failed on improper array")
+
+    def test_c_inout_23seq(self):
+        obj = np.array(self.num23seq, dtype=self.type.dtype)
+        shape = (len(self.num23seq), len(self.num23seq[0]))
+        a = self.array(shape, intent.in_.c.inout, obj)
+        assert a.has_shared_memory()
+
+    def test_in_copy_from_2casttype(self):
+        for t in self.type.cast_types():
+            obj = np.array(self.num2seq, dtype=t.dtype)
+            a = self.array([len(self.num2seq)], intent.in_.copy, obj)
+            assert not a.has_shared_memory()
+
+    def test_c_in_from_23seq(self):
+        a = self.array(
+            [len(self.num23seq), len(self.num23seq[0])], intent.in_,
+            self.num23seq)
+        assert not a.has_shared_memory()
+
+    def test_in_from_23casttype(self):
+        for t in self.type.cast_types():
+            obj = np.array(self.num23seq, dtype=t.dtype)
+            a = self.array(
+                [len(self.num23seq), len(self.num23seq[0])], intent.in_, obj)
+            assert not a.has_shared_memory()
+
+    def test_f_in_from_23casttype(self):
+        for t in self.type.cast_types():
+            obj = np.array(self.num23seq, dtype=t.dtype, order="F")
+            a = self.array(
+                [len(self.num23seq), len(self.num23seq[0])], intent.in_, obj)
+            if t.elsize == self.type.elsize:
+                assert a.has_shared_memory()
+            else:
+                assert not a.has_shared_memory()
+
+    def test_c_in_from_23casttype(self):
+        for t in self.type.cast_types():
+            obj = np.array(self.num23seq, dtype=t.dtype)
+            a = self.array(
+                [len(self.num23seq), len(self.num23seq[0])], intent.in_.c, obj)
+            if t.elsize == self.type.elsize:
+                assert a.has_shared_memory()
+            else:
+                assert not a.has_shared_memory()
+
+    def test_f_copy_in_from_23casttype(self):
+        for t in self.type.cast_types():
+            obj = np.array(self.num23seq, dtype=t.dtype, order="F")
+            a = self.array(
+                [len(self.num23seq), len(self.num23seq[0])], intent.in_.copy,
+                obj)
+            assert not a.has_shared_memory()
+
+    def test_c_copy_in_from_23casttype(self):
+        for t in self.type.cast_types():
+            obj = np.array(self.num23seq, dtype=t.dtype)
+            a = self.array(
+                [len(self.num23seq), len(self.num23seq[0])], intent.in_.c.copy,
+                obj)
+            assert not a.has_shared_memory()
+
+    def test_in_cache_from_2casttype(self):
+        for t in self.type.all_types():
+            if t.elsize != self.type.elsize:
+                continue
+            obj = np.array(self.num2seq, dtype=t.dtype)
+            shape = (len(self.num2seq), )
+            a = self.array(shape, intent.in_.c.cache, obj)
+            assert a.has_shared_memory()
+
+            a = self.array(shape, intent.in_.cache, obj)
+            assert a.has_shared_memory()
+
+            obj = np.array(self.num2seq, dtype=t.dtype, order="F")
+            a = self.array(shape, intent.in_.c.cache, obj)
+            assert a.has_shared_memory()
+
+            a = self.array(shape, intent.in_.cache, obj)
+            assert a.has_shared_memory(), repr(t.dtype)
+
+            try:
+                a = self.array(shape, intent.in_.cache, obj[::-1])
+            except ValueError as msg:
+                if not str(msg).startswith(
+                        "failed to initialize intent(cache) array"):
+                    raise
+            else:
+                raise SystemError(
+                    "intent(cache) should have failed on multisegmented array")
+
+    def test_in_cache_from_2casttype_failure(self):
+        for t in self.type.all_types():
+            if t.NAME == 'STRING':
+                # string elsize is 0, so skipping the test
+                continue
+            if t.elsize >= self.type.elsize:
+                continue
+            obj = np.array(self.num2seq, dtype=t.dtype)
+            shape = (len(self.num2seq), )
+            try:
+                self.array(shape, intent.in_.cache, obj)  # Should succeed
+            except ValueError as msg:
+                if not str(msg).startswith(
+                        "failed to initialize intent(cache) array"):
+                    raise
+            else:
+                raise SystemError(
+                    "intent(cache) should have failed on smaller array")
+
+    def test_cache_hidden(self):
+        shape = (2, )
+        a = self.array(shape, intent.cache.hide, None)
+        assert a.arr.shape == shape
+
+        shape = (2, 3)
+        a = self.array(shape, intent.cache.hide, None)
+        assert a.arr.shape == shape
+
+        shape = (-1, 3)
+        try:
+            a = self.array(shape, intent.cache.hide, None)
+        except ValueError as msg:
+            if not str(msg).startswith(
+                    "failed to create intent(cache|hide)|optional array"):
+                raise
+        else:
+            raise SystemError(
+                "intent(cache) should have failed on undefined dimensions")
+
+    def test_hidden(self):
+        shape = (2, )
+        a = self.array(shape, intent.hide, None)
+        assert a.arr.shape == shape
+        assert a.arr_equal(a.arr, np.zeros(shape, dtype=self.type.dtype))
+
+        shape = (2, 3)
+        a = self.array(shape, intent.hide, None)
+        assert a.arr.shape == shape
+        assert a.arr_equal(a.arr, np.zeros(shape, dtype=self.type.dtype))
+        assert a.arr.flags["FORTRAN"] and not a.arr.flags["CONTIGUOUS"]
+
+        shape = (2, 3)
+        a = self.array(shape, intent.c.hide, None)
+        assert a.arr.shape == shape
+        assert a.arr_equal(a.arr, np.zeros(shape, dtype=self.type.dtype))
+        assert not a.arr.flags["FORTRAN"] and a.arr.flags["CONTIGUOUS"]
+
+        shape = (-1, 3)
+        try:
+            a = self.array(shape, intent.hide, None)
+        except ValueError as msg:
+            if not str(msg).startswith(
+                    "failed to create intent(cache|hide)|optional array"):
+                raise
+        else:
+            raise SystemError(
+                "intent(hide) should have failed on undefined dimensions")
+
+    def test_optional_none(self):
+        shape = (2, )
+        a = self.array(shape, intent.optional, None)
+        assert a.arr.shape == shape
+        assert a.arr_equal(a.arr, np.zeros(shape, dtype=self.type.dtype))
+
+        shape = (2, 3)
+        a = self.array(shape, intent.optional, None)
+        assert a.arr.shape == shape
+        assert a.arr_equal(a.arr, np.zeros(shape, dtype=self.type.dtype))
+        assert a.arr.flags["FORTRAN"] and not a.arr.flags["CONTIGUOUS"]
+
+        shape = (2, 3)
+        a = self.array(shape, intent.c.optional, None)
+        assert a.arr.shape == shape
+        assert a.arr_equal(a.arr, np.zeros(shape, dtype=self.type.dtype))
+        assert not a.arr.flags["FORTRAN"] and a.arr.flags["CONTIGUOUS"]
+
+    def test_optional_from_2seq(self):
+        obj = self.num2seq
+        shape = (len(obj), )
+        a = self.array(shape, intent.optional, obj)
+        assert a.arr.shape == shape
+        assert not a.has_shared_memory()
+
+    def test_optional_from_23seq(self):
+        obj = self.num23seq
+        shape = (len(obj), len(obj[0]))
+        a = self.array(shape, intent.optional, obj)
+        assert a.arr.shape == shape
+        assert not a.has_shared_memory()
+
+        a = self.array(shape, intent.optional.c, obj)
+        assert a.arr.shape == shape
+        assert not a.has_shared_memory()
+
+    def test_inplace(self):
+        obj = np.array(self.num23seq, dtype=self.type.dtype)
+        assert not obj.flags["FORTRAN"] and obj.flags["CONTIGUOUS"]
+        shape = obj.shape
+        a = self.array(shape, intent.inplace, obj)
+        assert obj[1][2] == a.arr[1][2], repr((obj, a.arr))
+        a.arr[1][2] = 54
+        assert obj[1][2] == a.arr[1][2] == np.array(54, dtype=self.type.dtype)
+        assert a.arr is obj
+        assert obj.flags["FORTRAN"]  # obj attributes are changed inplace!
+        assert not obj.flags["CONTIGUOUS"]
+
+    def test_inplace_from_casttype(self):
+        for t in self.type.cast_types():
+            if t is self.type:
+                continue
+            obj = np.array(self.num23seq, dtype=t.dtype)
+            assert obj.dtype.type == t.type
+            assert obj.dtype.type is not self.type.type
+            assert not obj.flags["FORTRAN"] and obj.flags["CONTIGUOUS"]
+            shape = obj.shape
+            a = self.array(shape, intent.inplace, obj)
+            assert obj[1][2] == a.arr[1][2], repr((obj, a.arr))
+            a.arr[1][2] = 54
+            assert obj[1][2] == a.arr[1][2] == np.array(54,
+                                                        dtype=self.type.dtype)
+            assert a.arr is obj
+            assert obj.flags["FORTRAN"]  # obj attributes changed inplace!
+            assert not obj.flags["CONTIGUOUS"]
+            assert obj.dtype.type is self.type.type  # obj changed inplace!
diff --git a/.venv/lib/python3.12/site-packages/numpy/f2py/tests/test_assumed_shape.py b/.venv/lib/python3.12/site-packages/numpy/f2py/tests/test_assumed_shape.py
new file mode 100644
index 00000000..d4664cf8
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/numpy/f2py/tests/test_assumed_shape.py
@@ -0,0 +1,49 @@
+import os
+import pytest
+import tempfile
+
+from . import util
+
+
+class TestAssumedShapeSumExample(util.F2PyTest):
+    sources = [
+        util.getpath("tests", "src", "assumed_shape", "foo_free.f90"),
+        util.getpath("tests", "src", "assumed_shape", "foo_use.f90"),
+        util.getpath("tests", "src", "assumed_shape", "precision.f90"),
+        util.getpath("tests", "src", "assumed_shape", "foo_mod.f90"),
+        util.getpath("tests", "src", "assumed_shape", ".f2py_f2cmap"),
+    ]
+
+    @pytest.mark.slow
+    def test_all(self):
+        r = self.module.fsum([1, 2])
+        assert r == 3
+        r = self.module.sum([1, 2])
+        assert r == 3
+        r = self.module.sum_with_use([1, 2])
+        assert r == 3
+
+        r = self.module.mod.sum([1, 2])
+        assert r == 3
+        r = self.module.mod.fsum([1, 2])
+        assert r == 3
+
+
+class TestF2cmapOption(TestAssumedShapeSumExample):
+    def setup_method(self):
+        # Use a custom file name for .f2py_f2cmap
+        self.sources = list(self.sources)
+        f2cmap_src = self.sources.pop(-1)
+
+        self.f2cmap_file = tempfile.NamedTemporaryFile(delete=False)
+        with open(f2cmap_src, "rb") as f:
+            self.f2cmap_file.write(f.read())
+        self.f2cmap_file.close()
+
+        self.sources.append(self.f2cmap_file.name)
+        self.options = ["--f2cmap", self.f2cmap_file.name]
+
+        super().setup_method()
+
+    def teardown_method(self):
+        os.unlink(self.f2cmap_file.name)
diff --git a/.venv/lib/python3.12/site-packages/numpy/f2py/tests/test_block_docstring.py b/.venv/lib/python3.12/site-packages/numpy/f2py/tests/test_block_docstring.py
new file mode 100644
index 00000000..e0eacc03
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/numpy/f2py/tests/test_block_docstring.py
@@ -0,0 +1,17 @@
+import sys
+import pytest
+from . import util
+
+from numpy.testing import IS_PYPY
+
+
+class TestBlockDocString(util.F2PyTest):
+    sources = [util.getpath("tests", "src", "block_docstring", "foo.f")]
+
+    @pytest.mark.skipif(sys.platform == "win32",
+                        reason="Fails with MinGW64 Gfortran (Issue #9673)")
+    @pytest.mark.xfail(IS_PYPY,
+                       reason="PyPy cannot modify tp_doc after PyType_Ready")
+    def test_block_docstring(self):
+        expected = "bar : 'i'-array(2,3)\n"
+        assert self.module.block.__doc__ == expected
diff --git a/.venv/lib/python3.12/site-packages/numpy/f2py/tests/test_callback.py b/.venv/lib/python3.12/site-packages/numpy/f2py/tests/test_callback.py
new file mode 100644
index 00000000..5b6c294d
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/numpy/f2py/tests/test_callback.py
@@ -0,0 +1,243 @@
+import math
+import textwrap
+import sys
+import pytest
+import threading
+import traceback
+import time
+
+import numpy as np
+from numpy.testing import IS_PYPY
+from . import util
+
+
+class TestF77Callback(util.F2PyTest):
+    sources = [util.getpath("tests", "src", "callback", "foo.f")]
+
+    @pytest.mark.parametrize("name", "t,t2".split(","))
+    def test_all(self, name):
+        self.check_function(name)
+
+    @pytest.mark.xfail(IS_PYPY,
+                       reason="PyPy cannot modify tp_doc after PyType_Ready")
+    def test_docstring(self):
+        expected = textwrap.dedent("""\
+        a = t(fun,[fun_extra_args])
+
+        Wrapper for ``t``.
+
+        Parameters
+        ----------
+        fun : call-back function
+
+        Other Parameters
+        ----------------
+        fun_extra_args : input tuple, optional
+            Default: ()
+
+        Returns
+        -------
+        a : int
+
+        Notes
+        -----
+        Call-back functions::
+
+            def fun(): return a
+            Return objects:
+                a : int
+        """)
+        assert self.module.t.__doc__ == expected
+
+    def check_function(self, name):
+        t = getattr(self.module, name)
+        r = t(lambda: 4)
+        assert r == 4
+        r = t(lambda a: 5, fun_extra_args=(6, ))
+        assert r == 5
+        r = t(lambda a: a, fun_extra_args=(6, ))
+        assert r == 6
+        r = t(lambda a: 5 + a, fun_extra_args=(7, ))
+        assert r == 12
+        r = t(lambda a: math.degrees(a), fun_extra_args=(math.pi, ))
+        assert r == 180
+        r = t(math.degrees, fun_extra_args=(math.pi, ))
+        assert r == 180
+
+        r = t(self.module.func, fun_extra_args=(6, ))
+        assert r == 17
+        r = t(self.module.func0)
+        assert r == 11
+        r = t(self.module.func0._cpointer)
+        assert r == 11
+
+        class A:
+            def __call__(self):
+                return 7
+
+            def mth(self):
+                return 9
+
+        a = A()
+        r = t(a)
+        assert r == 7
+        r = t(a.mth)
+        assert r == 9
+
+    @pytest.mark.skipif(sys.platform == 'win32',
+                        reason='Fails with MinGW64 Gfortran (Issue #9673)')
+    def test_string_callback(self):
+        def callback(code):
+            if code == "r":
+                return 0
+            else:
+                return 1
+
+        f = getattr(self.module, "string_callback")
+        r = f(callback)
+        assert r == 0
+
+    @pytest.mark.skipif(sys.platform == 'win32',
+                        reason='Fails with MinGW64 Gfortran (Issue #9673)')
+    def test_string_callback_array(self):
+        # See gh-10027
+        cu1 = np.zeros((1, ), "S8")
+        cu2 = np.zeros((1, 8), "c")
+        cu3 = np.array([""], "S8")
+
+        def callback(cu, lencu):
+            if cu.shape != (lencu,):
+                return 1
+            if cu.dtype != "S8":
+                return 2
+            if not np.all(cu == b""):
+                return 3
+            return 0
+
+        f = getattr(self.module, "string_callback_array")
+        for cu in [cu1, cu2, cu3]:
+            res = f(callback, cu, cu.size)
+            assert res == 0
+
+    def test_threadsafety(self):
+        # Segfaults if the callback handling is not threadsafe
+
+        errors = []
+
+        def cb():
+            # Sleep here to make it more likely for another thread
+            # to call their callback at the same time.
+            time.sleep(1e-3)
+
+            # Check reentrancy
+            r = self.module.t(lambda: 123)
+            assert r == 123
+
+            return 42
+
+        def runner(name):
+            try:
+                for j in range(50):
+                    r = self.module.t(cb)
+                    assert r == 42
+                    self.check_function(name)
+            except Exception:
+                errors.append(traceback.format_exc())
+
+        threads = [
+            threading.Thread(target=runner, args=(arg, ))
+            for arg in ("t", "t2") for n in range(20)
+        ]
+
+        for t in threads:
+            t.start()
+
+        for t in threads:
+            t.join()
+
+        errors = "\n\n".join(errors)
+        if errors:
+            raise AssertionError(errors)
+
+    def test_hidden_callback(self):
+        try:
+            self.module.hidden_callback(2)
+        except Exception as msg:
+            assert str(msg).startswith("Callback global_f not defined")
+
+        try:
+            self.module.hidden_callback2(2)
+        except Exception as msg:
+            assert str(msg).startswith("cb: Callback global_f not defined")
+
+        self.module.global_f = lambda x: x + 1
+        r = self.module.hidden_callback(2)
+        assert r == 3
+
+        self.module.global_f = lambda x: x + 2
+        r = self.module.hidden_callback(2)
+        assert r == 4
+
+        del self.module.global_f
+        try:
+            self.module.hidden_callback(2)
+        except Exception as msg:
+            assert str(msg).startswith("Callback global_f not defined")
+
+        self.module.global_f = lambda x=0: x + 3
+        r = self.module.hidden_callback(2)
+        assert r == 5
+
+        # reproducer of gh18341
+        r = self.module.hidden_callback2(2)
+        assert r == 3
+
+
+class TestF77CallbackPythonTLS(TestF77Callback):
+    """
+    Callback tests using Python thread-local storage instead of
+    compiler-provided
+    """
+
+    options = ["-DF2PY_USE_PYTHON_TLS"]
+
+
+class TestF90Callback(util.F2PyTest):
+    sources = [util.getpath("tests", "src", "callback", "gh17797.f90")]
+
+    def test_gh17797(self):
+        def incr(x):
+            return x + 123
+
+        y = np.array([1, 2, 3], dtype=np.int64)
+        r = self.module.gh17797(incr, y)
+        assert r == 123 + 1 + 2 + 3
+
+
+class TestGH18335(util.F2PyTest):
+    """The reproduction of the reported issue requires specific input that
+    extensions may break the issue conditions, so the reproducer is
+    implemented as a separate test class. Do not extend this test with
+    other tests!
+    """
+    sources = [util.getpath("tests", "src", "callback", "gh18335.f90")]
+
+    def test_gh18335(self):
+        def foo(x):
+            x[0] += 1
+
+        r = self.module.gh18335(foo)
+        assert r == 123 + 1
+
+
+class TestGH25211(util.F2PyTest):
+    sources = [util.getpath("tests", "src", "callback", "gh25211.f"),
+               util.getpath("tests", "src", "callback", "gh25211.pyf")]
+    module_name = "callback2"
+
+    def test_gh18335(self):
+        def bar(x):
+            return x*x
+
+        res = self.module.foo(bar)
+        assert res == 110
diff --git a/.venv/lib/python3.12/site-packages/numpy/f2py/tests/test_character.py b/.venv/lib/python3.12/site-packages/numpy/f2py/tests/test_character.py
new file mode 100644
index 00000000..e55b1b6b
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/numpy/f2py/tests/test_character.py
@@ -0,0 +1,636 @@
+import pytest
+import textwrap
+from numpy.testing import assert_array_equal, assert_equal, assert_raises
+import numpy as np
+from numpy.f2py.tests import util
+
+
+class TestCharacterString(util.F2PyTest):
+    # options = ['--debug-capi', '--build-dir', '/tmp/test-build-f2py']
+    suffix = '.f90'
+    fprefix = 'test_character_string'
+    length_list = ['1', '3', 'star']
+
+    code = ''
+    for length in length_list:
+        fsuffix = length
+        clength = dict(star='(*)').get(length, length)
+
+        code += textwrap.dedent(f"""
+
+        subroutine {fprefix}_input_{fsuffix}(c, o, n)
+          character*{clength}, intent(in) :: c
+          integer n
+          !f2py integer, depend(c), intent(hide) :: n = slen(c)
+          integer*1, dimension(n) :: o
+          !f2py intent(out) o
+          o = transfer(c, o)
+        end subroutine {fprefix}_input_{fsuffix}
+
+        subroutine {fprefix}_output_{fsuffix}(c, o, n)
+          character*{clength}, intent(out) :: c
+          integer n
+          integer*1, dimension(n), intent(in) :: o
+          !f2py integer, depend(o), intent(hide) :: n = len(o)
+          c = transfer(o, c)
+        end subroutine {fprefix}_output_{fsuffix}
+
+        subroutine {fprefix}_array_input_{fsuffix}(c, o, m, n)
+          integer m, i, n
+          character*{clength}, intent(in), dimension(m) :: c
+          !f2py integer, depend(c), intent(hide) :: m = len(c)
+          !f2py integer, depend(c), intent(hide) :: n = f2py_itemsize(c)
+          integer*1, dimension(m, n), intent(out) :: o
+          do i=1,m
+            o(i, :) = transfer(c(i), o(i, :))
+          end do
+        end subroutine {fprefix}_array_input_{fsuffix}
+
+        subroutine {fprefix}_array_output_{fsuffix}(c, o, m, n)
+          character*{clength}, intent(out), dimension(m) :: c
+          integer n
+          integer*1, dimension(m, n), intent(in) :: o
+          !f2py character(f2py_len=n) :: c
+          !f2py integer, depend(o), intent(hide) :: m = len(o)
+          !f2py integer, depend(o), intent(hide) :: n = shape(o, 1)
+          do i=1,m
+            c(i) = transfer(o(i, :), c(i))
+          end do
+        end subroutine {fprefix}_array_output_{fsuffix}
+
+        subroutine {fprefix}_2d_array_input_{fsuffix}(c, o, m1, m2, n)
+          integer m1, m2, i, j, n
+          character*{clength}, intent(in), dimension(m1, m2) :: c
+          !f2py integer, depend(c), intent(hide) :: m1 = len(c)
+          !f2py integer, depend(c), intent(hide) :: m2 = shape(c, 1)
+          !f2py integer, depend(c), intent(hide) :: n = f2py_itemsize(c)
+          integer*1, dimension(m1, m2, n), intent(out) :: o
+          do i=1,m1
+            do j=1,m2
+              o(i, j, :) = transfer(c(i, j), o(i, j, :))
+            end do
+          end do
+        end subroutine {fprefix}_2d_array_input_{fsuffix}
+        """)
+
+    @pytest.mark.parametrize("length", length_list)
+    def test_input(self, length):
+        fsuffix = {'(*)': 'star'}.get(length, length)
+        f = getattr(self.module, self.fprefix + '_input_' + fsuffix)
+
+        a = {'1': 'a', '3': 'abc', 'star': 'abcde' * 3}[length]
+
+        assert_array_equal(f(a), np.array(list(map(ord, a)), dtype='u1'))
+
+    @pytest.mark.parametrize("length", length_list[:-1])
+    def test_output(self, length):
+        fsuffix = length
+        f = getattr(self.module, self.fprefix + '_output_' + fsuffix)
+
+        a = {'1': 'a', '3': 'abc'}[length]
+
+        assert_array_equal(f(np.array(list(map(ord, a)), dtype='u1')),
+                           a.encode())
+
+    @pytest.mark.parametrize("length", length_list)
+    def test_array_input(self, length):
+        fsuffix = length
+        f = getattr(self.module, self.fprefix + '_array_input_' + fsuffix)
+
+        a = np.array([{'1': 'a', '3': 'abc', 'star': 'abcde' * 3}[length],
+                      {'1': 'A', '3': 'ABC', 'star': 'ABCDE' * 3}[length],
+                      ], dtype='S')
+
+        expected = np.array([[c for c in s] for s in a], dtype='u1')
+        assert_array_equal(f(a), expected)
+
+    @pytest.mark.parametrize("length", length_list)
+    def test_array_output(self, length):
+        fsuffix = length
+        f = getattr(self.module, self.fprefix + '_array_output_' + fsuffix)
+
+        expected = np.array(
+            [{'1': 'a', '3': 'abc', 'star': 'abcde' * 3}[length],
+             {'1': 'A', '3': 'ABC', 'star': 'ABCDE' * 3}[length]], dtype='S')
+
+        a = np.array([[c for c in s] for s in expected], dtype='u1')
+        assert_array_equal(f(a), expected)
+
+    @pytest.mark.parametrize("length", length_list)
+    def test_2d_array_input(self, length):
+        fsuffix = length
+        f = getattr(self.module, self.fprefix + '_2d_array_input_' + fsuffix)
+
+        a = np.array([[{'1': 'a', '3': 'abc', 'star': 'abcde' * 3}[length],
+                       {'1': 'A', '3': 'ABC', 'star': 'ABCDE' * 3}[length]],
+                      [{'1': 'f', '3': 'fgh', 'star': 'fghij' * 3}[length],
+                       {'1': 'F', '3': 'FGH', 'star': 'FGHIJ' * 3}[length]]],
+                     dtype='S')
+        expected = np.array([[[c for c in item] for item in row] for row in a],
+                            dtype='u1', order='F')
+        assert_array_equal(f(a), expected)
+
+
+class TestCharacter(util.F2PyTest):
+    # options = ['--debug-capi', '--build-dir', '/tmp/test-build-f2py']
+    suffix = '.f90'
+    fprefix = 'test_character'
+
+    code = textwrap.dedent(f"""
+       subroutine {fprefix}_input(c, o)
+          character, intent(in) :: c
+          integer*1 o
+          !f2py intent(out) o
+          o = transfer(c, o)
+       end subroutine {fprefix}_input
+
+       subroutine {fprefix}_output(c, o)
+          character :: c
+          integer*1, intent(in) :: o
+          !f2py intent(out) c
+          c = transfer(o, c)
+       end subroutine {fprefix}_output
+
+       subroutine {fprefix}_input_output(c, o)
+          character, intent(in) :: c
+          character o
+          !f2py intent(out) o
+          o = c
+       end subroutine {fprefix}_input_output
+
+       subroutine {fprefix}_inout(c, n)
+          character :: c, n
+          !f2py intent(in) n
+          !f2py intent(inout) c
+          c = n
+       end subroutine {fprefix}_inout
+
+       function {fprefix}_return(o) result (c)
+          character :: c
+          character, intent(in) :: o
+          c = transfer(o, c)
+       end function {fprefix}_return
+
+       subroutine {fprefix}_array_input(c, o)
+          character, intent(in) :: c(3)
+          integer*1 o(3)
+          !f2py intent(out) o
+          integer i
+          do i=1,3
+            o(i) = transfer(c(i), o(i))
+          end do
+       end subroutine {fprefix}_array_input
+
+       subroutine {fprefix}_2d_array_input(c, o)
+          character, intent(in) :: c(2, 3)
+          integer*1 o(2, 3)
+          !f2py intent(out) o
+          integer i, j
+          do i=1,2
+            do j=1,3
+              o(i, j) = transfer(c(i, j), o(i, j))
+            end do
+          end do
+       end subroutine {fprefix}_2d_array_input
+
+       subroutine {fprefix}_array_output(c, o)
+          character :: c(3)
+          integer*1, intent(in) :: o(3)
+          !f2py intent(out) c
+          do i=1,3
+            c(i) = transfer(o(i), c(i))
+          end do
+       end subroutine {fprefix}_array_output
+
+       subroutine {fprefix}_array_inout(c, n)
+          character :: c(3), n(3)
+          !f2py intent(in) n(3)
+          !f2py intent(inout) c(3)
+          do i=1,3
+            c(i) = n(i)
+          end do
+       end subroutine {fprefix}_array_inout
+
+       subroutine {fprefix}_2d_array_inout(c, n)
+          character :: c(2, 3), n(2, 3)
+          !f2py intent(in) n(2, 3)
+          !f2py intent(inout) c(2. 3)
+          integer i, j
+          do i=1,2
+            do j=1,3
+              c(i, j) = n(i, j)
+            end do
+          end do
+       end subroutine {fprefix}_2d_array_inout
+
+       function {fprefix}_array_return(o) result (c)
+          character, dimension(3) :: c
+          character, intent(in) :: o(3)
+          do i=1,3
+            c(i) = o(i)
+          end do
+       end function {fprefix}_array_return
+
+       function {fprefix}_optional(o) result (c)
+          character, intent(in) :: o
+          !f2py character o = "a"
+          character :: c
+          c = o
+       end function {fprefix}_optional
+    """)
+
+    @pytest.mark.parametrize("dtype", ['c', 'S1'])
+    def test_input(self, dtype):
+        f = getattr(self.module, self.fprefix + '_input')
+
+        assert_equal(f(np.array('a', dtype=dtype)), ord('a'))
+        assert_equal(f(np.array(b'a', dtype=dtype)), ord('a'))
+        assert_equal(f(np.array(['a'], dtype=dtype)), ord('a'))
+        assert_equal(f(np.array('abc', dtype=dtype)), ord('a'))
+        assert_equal(f(np.array([['a']], dtype=dtype)), ord('a'))
+
+    def test_input_varia(self):
+        f = getattr(self.module, self.fprefix + '_input')
+
+        assert_equal(f('a'), ord('a'))
+        assert_equal(f(b'a'), ord(b'a'))
+        assert_equal(f(''), 0)
+        assert_equal(f(b''), 0)
+        assert_equal(f(b'\0'), 0)
+        assert_equal(f('ab'), ord('a'))
+        assert_equal(f(b'ab'), ord('a'))
+        assert_equal(f(['a']), ord('a'))
+
+        assert_equal(f(np.array(b'a')), ord('a'))
+        assert_equal(f(np.array([b'a'])), ord('a'))
+        a = np.array('a')
+        assert_equal(f(a), ord('a'))
+        a = np.array(['a'])
+        assert_equal(f(a), ord('a'))
+
+        try:
+            f([])
+        except IndexError as msg:
+            if not str(msg).endswith(' got 0-list'):
+                raise
+        else:
+            raise SystemError(f'{f.__name__} should have failed on empty list')
+
+        try:
+            f(97)
+        except TypeError as msg:
+            if not str(msg).endswith(' got int instance'):
+                raise
+        else:
+            raise SystemError(f'{f.__name__} should have failed on int value')
+
+    @pytest.mark.parametrize("dtype", ['c', 'S1', 'U1'])
+    def test_array_input(self, dtype):
+        f = getattr(self.module, self.fprefix + '_array_input')
+
+        assert_array_equal(f(np.array(['a', 'b', 'c'], dtype=dtype)),
+                           np.array(list(map(ord, 'abc')), dtype='i1'))
+        assert_array_equal(f(np.array([b'a', b'b', b'c'], dtype=dtype)),
+                           np.array(list(map(ord, 'abc')), dtype='i1'))
+
+    def test_array_input_varia(self):
+        f = getattr(self.module, self.fprefix + '_array_input')
+        assert_array_equal(f(['a', 'b', 'c']),
+                           np.array(list(map(ord, 'abc')), dtype='i1'))
+        assert_array_equal(f([b'a', b'b', b'c']),
+                           np.array(list(map(ord, 'abc')), dtype='i1'))
+
+        try:
+            f(['a', 'b', 'c', 'd'])
+        except ValueError as msg:
+            if not str(msg).endswith(
+                    'th dimension must be fixed to 3 but got 4'):
+                raise
+        else:
+            raise SystemError(
+                f'{f.__name__} should have failed on wrong input')
+
+    @pytest.mark.parametrize("dtype", ['c', 'S1', 'U1'])
+    def test_2d_array_input(self, dtype):
+        f = getattr(self.module, self.fprefix + '_2d_array_input')
+
+        a = np.array([['a', 'b', 'c'],
+                      ['d', 'e', 'f']], dtype=dtype, order='F')
+        expected = a.view(np.uint32 if dtype == 'U1' else np.uint8)
+        assert_array_equal(f(a), expected)
+
+    def test_output(self):
+        f = getattr(self.module, self.fprefix + '_output')
+
+        assert_equal(f(ord(b'a')), b'a')
+        assert_equal(f(0), b'\0')
+
+    def test_array_output(self):
+        f = getattr(self.module, self.fprefix + '_array_output')
+
+        assert_array_equal(f(list(map(ord, 'abc'))),
+                           np.array(list('abc'), dtype='S1'))
+
+    def test_input_output(self):
+        f = getattr(self.module, self.fprefix + '_input_output')
+
+        assert_equal(f(b'a'), b'a')
+        assert_equal(f('a'), b'a')
+        assert_equal(f(''), b'\0')
+
+    @pytest.mark.parametrize("dtype", ['c', 'S1'])
+    def test_inout(self, dtype):
+        f = getattr(self.module, self.fprefix + '_inout')
+
+        a = np.array(list('abc'), dtype=dtype)
+        f(a, 'A')
+        assert_array_equal(a, np.array(list('Abc'), dtype=a.dtype))
+        f(a[1:], 'B')
+        assert_array_equal(a, np.array(list('ABc'), dtype=a.dtype))
+
+        a = np.array(['abc'], dtype=dtype)
+        f(a, 'A')
+        assert_array_equal(a, np.array(['Abc'], dtype=a.dtype))
+
+    def test_inout_varia(self):
+        f = getattr(self.module, self.fprefix + '_inout')
+        a = np.array('abc', dtype='S3')
+        f(a, 'A')
+        assert_array_equal(a, np.array('Abc', dtype=a.dtype))
+
+        a = np.array(['abc'], dtype='S3')
+        f(a, 'A')
+        assert_array_equal(a, np.array(['Abc'], dtype=a.dtype))
+
+        try:
+            f('abc', 'A')
+        except ValueError as msg:
+            if not str(msg).endswith(' got 3-str'):
+                raise
+        else:
+            raise SystemError(f'{f.__name__} should have failed on str value')
+
+    @pytest.mark.parametrize("dtype", ['c', 'S1'])
+    def test_array_inout(self, dtype):
+        f = getattr(self.module, self.fprefix + '_array_inout')
+        n = np.array(['A', 'B', 'C'], dtype=dtype, order='F')
+
+        a = np.array(['a', 'b', 'c'], dtype=dtype, order='F')
+        f(a, n)
+        assert_array_equal(a, n)
+
+        a = np.array(['a', 'b', 'c', 'd'], dtype=dtype)
+        f(a[1:], n)
+        assert_array_equal(a, np.array(['a', 'A', 'B', 'C'], dtype=dtype))
+
+        a = np.array([['a', 'b', 'c']], dtype=dtype, order='F')
+        f(a, n)
+        assert_array_equal(a, np.array([['A', 'B', 'C']], dtype=dtype))
+
+        a = np.array(['a', 'b', 'c', 'd'], dtype=dtype, order='F')
+        try:
+            f(a, n)
+        except ValueError as msg:
+            if not str(msg).endswith(
+                    'th dimension must be fixed to 3 but got 4'):
+                raise
+        else:
+            raise SystemError(
+                f'{f.__name__} should have failed on wrong input')
+
+    @pytest.mark.parametrize("dtype", ['c', 'S1'])
+    def test_2d_array_inout(self, dtype):
+        f = getattr(self.module, self.fprefix + '_2d_array_inout')
+        n = np.array([['A', 'B', 'C'],
+                      ['D', 'E', 'F']],
+                     dtype=dtype, order='F')
+        a = np.array([['a', 'b', 'c'],
+                      ['d', 'e', 'f']],
+                     dtype=dtype, order='F')
+        f(a, n)
+        assert_array_equal(a, n)
+
+    def test_return(self):
+        f = getattr(self.module, self.fprefix + '_return')
+
+        assert_equal(f('a'), b'a')
+
+    @pytest.mark.skip('fortran function returning array segfaults')
+    def test_array_return(self):
+        f = getattr(self.module, self.fprefix + '_array_return')
+
+        a = np.array(list('abc'), dtype='S1')
+        assert_array_equal(f(a), a)
+
+    def test_optional(self):
+        f = getattr(self.module, self.fprefix + '_optional')
+
+        assert_equal(f(), b"a")
+        assert_equal(f(b'B'), b"B")
+
+
+class TestMiscCharacter(util.F2PyTest):
+    # options = ['--debug-capi', '--build-dir', '/tmp/test-build-f2py']
+    suffix = '.f90'
+    fprefix = 'test_misc_character'
+
+    code = textwrap.dedent(f"""
+       subroutine {fprefix}_gh18684(x, y, m)
+         character(len=5), dimension(m), intent(in) :: x
+         character*5, dimension(m), intent(out) :: y
+         integer i, m
+         !f2py integer, intent(hide), depend(x) :: m = f2py_len(x)
+         do i=1,m
+           y(i) = x(i)
+         end do
+       end subroutine {fprefix}_gh18684
+
+       subroutine {fprefix}_gh6308(x, i)
+         integer i
+         !f2py check(i>=0 && i<12) i
+         character*5 name, x
+         common name(12)
+         name(i + 1) = x
+       end subroutine {fprefix}_gh6308
+
+       subroutine {fprefix}_gh4519(x)
+         character(len=*), intent(in) :: x(:)
+         !f2py intent(out) x
+         integer :: i
+         ! Uncomment for debug printing:
+         !do i=1, size(x)
+         !   print*, "x(",i,")=", x(i)
+         !end do
+       end subroutine {fprefix}_gh4519
+
+       pure function {fprefix}_gh3425(x) result (y)
+         character(len=*), intent(in) :: x
+         character(len=len(x)) :: y
+         integer :: i
+         do i = 1, len(x)
+           j = iachar(x(i:i))
+           if (j>=iachar("a") .and. j<=iachar("z") ) then
+             y(i:i) = achar(j-32)
+           else
+             y(i:i) = x(i:i)
+           endif
+         end do
+       end function {fprefix}_gh3425
+
+       subroutine {fprefix}_character_bc_new(x, y, z)
+         character, intent(in) :: x
+         character, intent(out) :: y
+         !f2py character, depend(x) :: y = x
+         !f2py character, dimension((x=='a'?1:2)), depend(x), intent(out) :: z
+         character, dimension(*) :: z
+         !f2py character, optional, check(x == 'a' || x == 'b') :: x = 'a'
+         !f2py callstatement (*f2py_func)(&x, &y, z)
+         !f2py callprotoargument character*, character*, character*
+         if (y.eq.x) then
+           y = x
+         else
+           y = 'e'
+         endif
+         z(1) = 'c'
+       end subroutine {fprefix}_character_bc_new
+
+       subroutine {fprefix}_character_bc_old(x, y, z)
+         character, intent(in) :: x
+         character, intent(out) :: y
+         !f2py character, depend(x) :: y = x[0]
+         !f2py character, dimension((*x=='a'?1:2)), depend(x), intent(out) :: z
+         character, dimension(*) :: z
+         !f2py character, optional, check(*x == 'a' || x[0] == 'b') :: x = 'a'
+         !f2py callstatement (*f2py_func)(x, y, z)
+         !f2py callprotoargument char*, char*, char*
+          if (y.eq.x) then
+           y = x
+         else
+           y = 'e'
+         endif
+         z(1) = 'c'
+       end subroutine {fprefix}_character_bc_old
+    """)
+
+    def test_gh18684(self):
+        # Test character(len=5) and character*5 usages
+        f = getattr(self.module, self.fprefix + '_gh18684')
+        x = np.array(["abcde", "fghij"], dtype='S5')
+        y = f(x)
+
+        assert_array_equal(x, y)
+
+    def test_gh6308(self):
+        # Test character string array in a common block
+        f = getattr(self.module, self.fprefix + '_gh6308')
+
+        assert_equal(self.module._BLNK_.name.dtype, np.dtype('S5'))
+        assert_equal(len(self.module._BLNK_.name), 12)
+        f("abcde", 0)
+        assert_equal(self.module._BLNK_.name[0], b"abcde")
+        f("12345", 5)
+        assert_equal(self.module._BLNK_.name[5], b"12345")
+
+    def test_gh4519(self):
+        # Test array of assumed length strings
+        f = getattr(self.module, self.fprefix + '_gh4519')
+
+        for x, expected in [
+                ('a', dict(shape=(), dtype=np.dtype('S1'))),
+                ('text', dict(shape=(), dtype=np.dtype('S4'))),
+                (np.array(['1', '2', '3'], dtype='S1'),
+                 dict(shape=(3,), dtype=np.dtype('S1'))),
+                (['1', '2', '34'],
+                 dict(shape=(3,), dtype=np.dtype('S2'))),
+                (['', ''], dict(shape=(2,), dtype=np.dtype('S1')))]:
+            r = f(x)
+            for k, v in expected.items():
+                assert_equal(getattr(r, k), v)
+
+    def test_gh3425(self):
+        # Test returning a copy of assumed length string
+        f = getattr(self.module, self.fprefix + '_gh3425')
+        # f is equivalent to bytes.upper
+
+        assert_equal(f('abC'), b'ABC')
+        assert_equal(f(''), b'')
+        assert_equal(f('abC12d'), b'ABC12D')
+
+    @pytest.mark.parametrize("state", ['new', 'old'])
+    def test_character_bc(self, state):
+        f = getattr(self.module, self.fprefix + '_character_bc_' + state)
+
+        c, a = f()
+        assert_equal(c, b'a')
+        assert_equal(len(a), 1)
+
+        c, a = f(b'b')
+        assert_equal(c, b'b')
+        assert_equal(len(a), 2)
+
+        assert_raises(Exception, lambda: f(b'c'))
+
+
+class TestStringScalarArr(util.F2PyTest):
+    sources = [util.getpath("tests", "src", "string", "scalar_string.f90")]
+
+    def test_char(self):
+        for out in (self.module.string_test.string,
+                    self.module.string_test.string77):
+            expected = ()
+            assert out.shape == expected
+            expected = '|S8'
+            assert out.dtype == expected
+
+    def test_char_arr(self):
+        for out in (self.module.string_test.strarr,
+                    self.module.string_test.strarr77):
+            expected = (5,7)
+            assert out.shape == expected
+            expected = '|S12'
+            assert out.dtype == expected
+
+class TestStringAssumedLength(util.F2PyTest):
+    sources = [util.getpath("tests", "src", "string", "gh24008.f")]
+
+    def test_gh24008(self):
+        self.module.greet("joe", "bob")
+
+class TestStringOptionalInOut(util.F2PyTest):
+    sources = [util.getpath("tests", "src", "string", "gh24662.f90")]
+
+    def test_gh24662(self):
+        self.module.string_inout_optional()
+        a = np.array('hi', dtype='S32')
+        self.module.string_inout_optional(a)
+        assert "output string" in a.tobytes().decode()
+        with pytest.raises(Exception):
+            aa = "Hi"
+            self.module.string_inout_optional(aa)
+
+
+@pytest.mark.slow
+class TestNewCharHandling(util.F2PyTest):
+    # from v1.24 onwards, gh-19388
+    sources = [
+        util.getpath("tests", "src", "string", "gh25286.pyf"),
+        util.getpath("tests", "src", "string", "gh25286.f90")
+    ]
+    module_name = "_char_handling_test"
+
+    def test_gh25286(self):
+        info = self.module.charint('T')
+        assert info == 2
+
+@pytest.mark.slow
+class TestBCCharHandling(util.F2PyTest):
+    # SciPy style, "incorrect" bindings with a hook
+    sources = [
+        util.getpath("tests", "src", "string", "gh25286_bc.pyf"),
+        util.getpath("tests", "src", "string", "gh25286.f90")
+    ]
+    module_name = "_char_handling_test"
+
+    def test_gh25286(self):
+        info = self.module.charint('T')
+        assert info == 2
diff --git a/.venv/lib/python3.12/site-packages/numpy/f2py/tests/test_common.py b/.venv/lib/python3.12/site-packages/numpy/f2py/tests/test_common.py
new file mode 100644
index 00000000..68c1b3b3
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/numpy/f2py/tests/test_common.py
@@ -0,0 +1,27 @@
+import os
+import sys
+import pytest
+
+import numpy as np
+from . import util
+
+
+class TestCommonBlock(util.F2PyTest):
+    sources = [util.getpath("tests", "src", "common", "block.f")]
+
+    @pytest.mark.skipif(sys.platform == "win32",
+                        reason="Fails with MinGW64 Gfortran (Issue #9673)")
+    def test_common_block(self):
+        self.module.initcb()
+        assert self.module.block.long_bn == np.array(1.0, dtype=np.float64)
+        assert self.module.block.string_bn == np.array("2", dtype="|S1")
+        assert self.module.block.ok == np.array(3, dtype=np.int32)
+
+
+class TestCommonWithUse(util.F2PyTest):
+    sources = [util.getpath("tests", "src", "common", "gh19161.f90")]
+
+    @pytest.mark.skipif(sys.platform == "win32",
+                        reason="Fails with MinGW64 Gfortran (Issue #9673)")
+    def test_common_gh19161(self):
+        assert self.module.data.x == 0
diff --git a/.venv/lib/python3.12/site-packages/numpy/f2py/tests/test_compile_function.py b/.venv/lib/python3.12/site-packages/numpy/f2py/tests/test_compile_function.py
new file mode 100644
index 00000000..3c16f319
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/numpy/f2py/tests/test_compile_function.py
@@ -0,0 +1,117 @@
+"""See https://github.com/numpy/numpy/pull/11937.
+
+"""
+import sys
+import os
+import uuid
+from importlib import import_module
+import pytest
+
+import numpy.f2py
+
+from . import util
+
+
+def setup_module():
+    if not util.has_c_compiler():
+        pytest.skip("Needs C compiler")
+    if not util.has_f77_compiler():
+        pytest.skip("Needs FORTRAN 77 compiler")
+
+
+# extra_args can be a list (since gh-11937) or string.
+# also test absence of extra_args
+@pytest.mark.parametrize("extra_args",
+                         [["--noopt", "--debug"], "--noopt --debug", ""])
+@pytest.mark.leaks_references(reason="Imported module seems never deleted.")
+def test_f2py_init_compile(extra_args):
+    # flush through the f2py __init__ compile() function code path as a
+    # crude test for input handling following migration from
+    # exec_command() to subprocess.check_output() in gh-11937
+
+    # the Fortran 77 syntax requires 6 spaces before any commands, but
+    # more space may be added/
+    fsource = """
+        integer function foo()
+        foo = 10 + 5
+        return
+        end
+    """
+    # use various helper functions in util.py to enable robust build /
+    # compile and reimport cycle in test suite
+    moddir = util.get_module_dir()
+    modname = util.get_temp_module_name()
+
+    cwd = os.getcwd()
+    target = os.path.join(moddir, str(uuid.uuid4()) + ".f")
+    # try running compile() with and without a source_fn provided so
+    # that the code path where a temporary file for writing Fortran
+    # source is created is also explored
+    for source_fn in [target, None]:
+        # mimic the path changing behavior used by build_module() in
+        # util.py, but don't actually use build_module() because it has
+        # its own invocation of subprocess that circumvents the
+        # f2py.compile code block under test
+        with util.switchdir(moddir):
+            ret_val = numpy.f2py.compile(fsource,
+                                         modulename=modname,
+                                         extra_args=extra_args,
+                                         source_fn=source_fn)
+
+            # check for compile success return value
+            assert ret_val == 0
+
+    # we are not currently able to import the Python-Fortran
+    # interface module on Windows / Appveyor, even though we do get
+    # successful compilation on that platform with Python 3.x
+    if sys.platform != "win32":
+        # check for sensible result of Fortran function; that means
+        # we can import the module name in Python and retrieve the
+        # result of the sum operation
+        return_check = import_module(modname)
+        calc_result = return_check.foo()
+        assert calc_result == 15
+        # Removal from sys.modules, is not as such necessary. Even with
+        # removal, the module (dict) stays alive.
+        del sys.modules[modname]
+
+
+def test_f2py_init_compile_failure():
+    # verify an appropriate integer status value returned by
+    # f2py.compile() when invalid Fortran is provided
+    ret_val = numpy.f2py.compile(b"invalid")
+    assert ret_val == 1
+
+
+def test_f2py_init_compile_bad_cmd():
+    # verify that usage of invalid command in f2py.compile() returns
+    # status value of 127 for historic consistency with exec_command()
+    # error handling
+
+    # patch the sys Python exe path temporarily to induce an OSError
+    # downstream NOTE: how bad of an idea is this patching?
+    try:
+        temp = sys.executable
+        sys.executable = "does not exist"
+
+        # the OSError should take precedence over invalid Fortran
+        ret_val = numpy.f2py.compile(b"invalid")
+        assert ret_val == 127
+    finally:
+        sys.executable = temp
+
+
+@pytest.mark.parametrize(
+    "fsource",
+    [
+        "program test_f2py\nend program test_f2py",
+        b"program test_f2py\nend program test_f2py",
+    ],
+)
+def test_compile_from_strings(tmpdir, fsource):
+    # Make sure we can compile str and bytes gh-12796
+    with util.switchdir(tmpdir):
+        ret_val = numpy.f2py.compile(fsource,
+                                     modulename="test_compile_from_strings",
+                                     extension=".f90")
+        assert ret_val == 0
diff --git a/.venv/lib/python3.12/site-packages/numpy/f2py/tests/test_crackfortran.py b/.venv/lib/python3.12/site-packages/numpy/f2py/tests/test_crackfortran.py
new file mode 100644
index 00000000..c8d9ddb8
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/numpy/f2py/tests/test_crackfortran.py
@@ -0,0 +1,350 @@
+import importlib
+import codecs
+import time
+import unicodedata
+import pytest
+import numpy as np
+from numpy.f2py.crackfortran import markinnerspaces, nameargspattern
+from . import util
+from numpy.f2py import crackfortran
+import textwrap
+import contextlib
+import io
+
+
+class TestNoSpace(util.F2PyTest):
+    # issue gh-15035: add handling for endsubroutine, endfunction with no space
+    # between "end" and the block name
+    sources = [util.getpath("tests", "src", "crackfortran", "gh15035.f")]
+
+    def test_module(self):
+        k = np.array([1, 2, 3], dtype=np.float64)
+        w = np.array([1, 2, 3], dtype=np.float64)
+        self.module.subb(k)
+        assert np.allclose(k, w + 1)
+        self.module.subc([w, k])
+        assert np.allclose(k, w + 1)
+        assert self.module.t0("23") == b"2"
+
+
+class TestPublicPrivate:
+    def test_defaultPrivate(self):
+        fpath = util.getpath("tests", "src", "crackfortran", "privatemod.f90")
+        mod = crackfortran.crackfortran([str(fpath)])
+        assert len(mod) == 1
+        mod = mod[0]
+        assert "private" in mod["vars"]["a"]["attrspec"]
+        assert "public" not in mod["vars"]["a"]["attrspec"]
+        assert "private" in mod["vars"]["b"]["attrspec"]
+        assert "public" not in mod["vars"]["b"]["attrspec"]
+        assert "private" not in mod["vars"]["seta"]["attrspec"]
+        assert "public" in mod["vars"]["seta"]["attrspec"]
+
+    def test_defaultPublic(self, tmp_path):
+        fpath = util.getpath("tests", "src", "crackfortran", "publicmod.f90")
+        mod = crackfortran.crackfortran([str(fpath)])
+        assert len(mod) == 1
+        mod = mod[0]
+        assert "private" in mod["vars"]["a"]["attrspec"]
+        assert "public" not in mod["vars"]["a"]["attrspec"]
+        assert "private" not in mod["vars"]["seta"]["attrspec"]
+        assert "public" in mod["vars"]["seta"]["attrspec"]
+
+    def test_access_type(self, tmp_path):
+        fpath = util.getpath("tests", "src", "crackfortran", "accesstype.f90")
+        mod = crackfortran.crackfortran([str(fpath)])
+        assert len(mod) == 1
+        tt = mod[0]['vars']
+        assert set(tt['a']['attrspec']) == {'private', 'bind(c)'}
+        assert set(tt['b_']['attrspec']) == {'public', 'bind(c)'}
+        assert set(tt['c']['attrspec']) == {'public'}
+
+    def test_nowrap_private_proceedures(self, tmp_path):
+        fpath = util.getpath("tests", "src", "crackfortran", "gh23879.f90")
+        mod = crackfortran.crackfortran([str(fpath)])
+        assert len(mod) == 1
+        pyf = crackfortran.crack2fortran(mod)
+        assert 'bar' not in pyf
+
+class TestModuleProcedure():
+    def test_moduleOperators(self, tmp_path):
+        fpath = util.getpath("tests", "src", "crackfortran", "operators.f90")
+        mod = crackfortran.crackfortran([str(fpath)])
+        assert len(mod) == 1
+        mod = mod[0]
+        assert "body" in mod and len(mod["body"]) == 9
+        assert mod["body"][1]["name"] == "operator(.item.)"
+        assert "implementedby" in mod["body"][1]
+        assert mod["body"][1]["implementedby"] == \
+            ["item_int", "item_real"]
+        assert mod["body"][2]["name"] == "operator(==)"
+        assert "implementedby" in mod["body"][2]
+        assert mod["body"][2]["implementedby"] == ["items_are_equal"]
+        assert mod["body"][3]["name"] == "assignment(=)"
+        assert "implementedby" in mod["body"][3]
+        assert mod["body"][3]["implementedby"] == \
+            ["get_int", "get_real"]
+
+    def test_notPublicPrivate(self, tmp_path):
+        fpath = util.getpath("tests", "src", "crackfortran", "pubprivmod.f90")
+        mod = crackfortran.crackfortran([str(fpath)])
+        assert len(mod) == 1
+        mod = mod[0]
+        assert mod['vars']['a']['attrspec'] == ['private', ]
+        assert mod['vars']['b']['attrspec'] == ['public', ]
+        assert mod['vars']['seta']['attrspec'] == ['public', ]
+
+
+class TestExternal(util.F2PyTest):
+    # issue gh-17859: add external attribute support
+    sources = [util.getpath("tests", "src", "crackfortran", "gh17859.f")]
+
+    def test_external_as_statement(self):
+        def incr(x):
+            return x + 123
+
+        r = self.module.external_as_statement(incr)
+        assert r == 123
+
+    def test_external_as_attribute(self):
+        def incr(x):
+            return x + 123
+
+        r = self.module.external_as_attribute(incr)
+        assert r == 123
+
+
+class TestCrackFortran(util.F2PyTest):
+    # gh-2848: commented lines between parameters in subroutine parameter lists
+    sources = [util.getpath("tests", "src", "crackfortran", "gh2848.f90")]
+
+    def test_gh2848(self):
+        r = self.module.gh2848(1, 2)
+        assert r == (1, 2)
+
+
+class TestMarkinnerspaces:
+    # gh-14118: markinnerspaces does not handle multiple quotations
+
+    def test_do_not_touch_normal_spaces(self):
+        test_list = ["a ", " a", "a b c", "'abcdefghij'"]
+        for i in test_list:
+            assert markinnerspaces(i) == i
+
+    def test_one_relevant_space(self):
+        assert markinnerspaces("a 'b c' \\' \\'") == "a 'b@_@c' \\' \\'"
+        assert markinnerspaces(r'a "b c" \" \"') == r'a "b@_@c" \" \"'
+
+    def test_ignore_inner_quotes(self):
+        assert markinnerspaces("a 'b c\" \" d' e") == "a 'b@_@c\"@_@\"@_@d' e"
+        assert markinnerspaces("a \"b c' ' d\" e") == "a \"b@_@c'@_@'@_@d\" e"
+
+    def test_multiple_relevant_spaces(self):
+        assert markinnerspaces("a 'b c' 'd e'") == "a 'b@_@c' 'd@_@e'"
+        assert markinnerspaces(r'a "b c" "d e"') == r'a "b@_@c" "d@_@e"'
+
+
+class TestDimSpec(util.F2PyTest):
+    """This test suite tests various expressions that are used as dimension
+    specifications.
+
+    There exists two usage cases where analyzing dimensions
+    specifications are important.
+
+    In the first case, the size of output arrays must be defined based
+    on the inputs to a Fortran function. Because Fortran supports
+    arbitrary bases for indexing, for instance, `arr(lower:upper)`,
+    f2py has to evaluate an expression `upper - lower + 1` where
+    `lower` and `upper` are arbitrary expressions of input parameters.
+    The evaluation is performed in C, so f2py has to translate Fortran
+    expressions to valid C expressions (an alternative approach is
+    that a developer specifies the corresponding C expressions in a
+    .pyf file).
+
+    In the second case, when user provides an input array with a given
+    size but some hidden parameters used in dimensions specifications
+    need to be determined based on the input array size. This is a
+    harder problem because f2py has to solve the inverse problem: find
+    a parameter `p` such that `upper(p) - lower(p) + 1` equals to the
+    size of input array. In the case when this equation cannot be
+    solved (e.g. because the input array size is wrong), raise an
+    error before calling the Fortran function (that otherwise would
+    likely crash Python process when the size of input arrays is
+    wrong). f2py currently supports this case only when the equation
+    is linear with respect to unknown parameter.
+
+    """
+
+    suffix = ".f90"
+
+    code_template = textwrap.dedent("""
+      function get_arr_size_{count}(a, n) result (length)
+        integer, intent(in) :: n
+        integer, dimension({dimspec}), intent(out) :: a
+        integer length
+        length = size(a)
+      end function
+
+      subroutine get_inv_arr_size_{count}(a, n)
+        integer :: n
+        ! the value of n is computed in f2py wrapper
+        !f2py intent(out) n
+        integer, dimension({dimspec}), intent(in) :: a
+      end subroutine
+    """)
+
+    linear_dimspecs = [
+        "n", "2*n", "2:n", "n/2", "5 - n/2", "3*n:20", "n*(n+1):n*(n+5)",
+        "2*n, n"
+    ]
+    nonlinear_dimspecs = ["2*n:3*n*n+2*n"]
+    all_dimspecs = linear_dimspecs + nonlinear_dimspecs
+
+    code = ""
+    for count, dimspec in enumerate(all_dimspecs):
+        lst = [(d.split(":")[0] if ":" in d else "1") for d in dimspec.split(',')]
+        code += code_template.format(
+            count=count,
+            dimspec=dimspec,
+            first=", ".join(lst),
+        )
+
+    @pytest.mark.parametrize("dimspec", all_dimspecs)
+    def test_array_size(self, dimspec):
+
+        count = self.all_dimspecs.index(dimspec)
+        get_arr_size = getattr(self.module, f"get_arr_size_{count}")
+
+        for n in [1, 2, 3, 4, 5]:
+            sz, a = get_arr_size(n)
+            assert a.size == sz
+
+    @pytest.mark.parametrize("dimspec", all_dimspecs)
+    def test_inv_array_size(self, dimspec):
+
+        count = self.all_dimspecs.index(dimspec)
+        get_arr_size = getattr(self.module, f"get_arr_size_{count}")
+        get_inv_arr_size = getattr(self.module, f"get_inv_arr_size_{count}")
+
+        for n in [1, 2, 3, 4, 5]:
+            sz, a = get_arr_size(n)
+            if dimspec in self.nonlinear_dimspecs:
+                # one must specify n as input, the call we'll ensure
+                # that a and n are compatible:
+                n1 = get_inv_arr_size(a, n)
+            else:
+                # in case of linear dependence, n can be determined
+                # from the shape of a:
+                n1 = get_inv_arr_size(a)
+            # n1 may be different from n (for instance, when `a` size
+            # is a function of some `n` fraction) but it must produce
+            # the same sized array
+            sz1, _ = get_arr_size(n1)
+            assert sz == sz1, (n, n1, sz, sz1)
+
+
+class TestModuleDeclaration:
+    def test_dependencies(self, tmp_path):
+        fpath = util.getpath("tests", "src", "crackfortran", "foo_deps.f90")
+        mod = crackfortran.crackfortran([str(fpath)])
+        assert len(mod) == 1
+        assert mod[0]["vars"]["abar"]["="] == "bar('abar')"
+
+
+class TestEval(util.F2PyTest):
+    def test_eval_scalar(self):
+        eval_scalar = crackfortran._eval_scalar
+
+        assert eval_scalar('123', {}) == '123'
+        assert eval_scalar('12 + 3', {}) == '15'
+        assert eval_scalar('a + b', dict(a=1, b=2)) == '3'
+        assert eval_scalar('"123"', {}) == "'123'"
+
+
+class TestFortranReader(util.F2PyTest):
+    @pytest.mark.parametrize("encoding",
+                             ['ascii', 'utf-8', 'utf-16', 'utf-32'])
+    def test_input_encoding(self, tmp_path, encoding):
+        # gh-635
+        f_path = tmp_path / f"input_with_{encoding}_encoding.f90"
+        with f_path.open('w', encoding=encoding) as ff:
+            ff.write("""
+                     subroutine foo()
+                     end subroutine foo
+                     """)
+        mod = crackfortran.crackfortran([str(f_path)])
+        assert mod[0]['name'] == 'foo'
+
+
+class TestUnicodeComment(util.F2PyTest):
+    sources = [util.getpath("tests", "src", "crackfortran", "unicode_comment.f90")]
+
+    @pytest.mark.skipif(
+        (importlib.util.find_spec("charset_normalizer") is None),
+        reason="test requires charset_normalizer which is not installed",
+    )
+    def test_encoding_comment(self):
+        self.module.foo(3)
+
+
+class TestNameArgsPatternBacktracking:
+    @pytest.mark.parametrize(
+        ['adversary'],
+        [
+            ('@)@bind@(@',),
+            ('@)@bind                         @(@',),
+            ('@)@bind foo bar baz@(@',)
+        ]
+    )
+    def test_nameargspattern_backtracking(self, adversary):
+        '''address ReDOS vulnerability:
+        https://github.com/numpy/numpy/issues/23338'''
+        trials_per_batch = 12
+        batches_per_regex = 4
+        start_reps, end_reps = 15, 25
+        for ii in range(start_reps, end_reps):
+            repeated_adversary = adversary * ii
+            # test times in small batches.
+            # this gives us more chances to catch a bad regex
+            # while still catching it before too long if it is bad
+            for _ in range(batches_per_regex):
+                times = []
+                for _ in range(trials_per_batch):
+                    t0 = time.perf_counter()
+                    mtch = nameargspattern.search(repeated_adversary)
+                    times.append(time.perf_counter() - t0)
+                # our pattern should be much faster than 0.2s per search
+                # it's unlikely that a bad regex will pass even on fast CPUs
+                assert np.median(times) < 0.2
+            assert not mtch
+            # if the adversary is capped with @)@, it becomes acceptable
+            # according to the old version of the regex.
+            # that should still be true.
+            good_version_of_adversary = repeated_adversary + '@)@'
+            assert nameargspattern.search(good_version_of_adversary)
+
+
+class TestFunctionReturn(util.F2PyTest):
+    sources = [util.getpath("tests", "src", "crackfortran", "gh23598.f90")]
+
+    def test_function_rettype(self):
+        # gh-23598
+        assert self.module.intproduct(3, 4) == 12
+
+
+class TestFortranGroupCounters(util.F2PyTest):
+    def test_end_if_comment(self):
+        # gh-23533
+        fpath = util.getpath("tests", "src", "crackfortran", "gh23533.f")
+        try:
+            crackfortran.crackfortran([str(fpath)])
+        except Exception as exc:
+            assert False, f"'crackfortran.crackfortran' raised an exception {exc}"
+
+
+class TestF77CommonBlockReader():
+    def test_gh22648(self, tmp_path):
+        fpath = util.getpath("tests", "src", "crackfortran", "gh22648.pyf")
+        with contextlib.redirect_stdout(io.StringIO()) as stdout_f2py:
+            mod = crackfortran.crackfortran([str(fpath)])
+        assert "Mismatch" not in stdout_f2py.getvalue()
diff --git a/.venv/lib/python3.12/site-packages/numpy/f2py/tests/test_data.py b/.venv/lib/python3.12/site-packages/numpy/f2py/tests/test_data.py
new file mode 100644
index 00000000..4e5604c0
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/numpy/f2py/tests/test_data.py
@@ -0,0 +1,70 @@
+import os
+import pytest
+import numpy as np
+
+from . import util
+from numpy.f2py.crackfortran import crackfortran
+
+
+class TestData(util.F2PyTest):
+    sources = [util.getpath("tests", "src", "crackfortran", "data_stmts.f90")]
+
+    # For gh-23276
+    def test_data_stmts(self):
+        assert self.module.cmplxdat.i == 2
+        assert self.module.cmplxdat.j == 3
+        assert self.module.cmplxdat.x == 1.5
+        assert self.module.cmplxdat.y == 2.0
+        assert self.module.cmplxdat.pi == 3.1415926535897932384626433832795028841971693993751058209749445923078164062
+        assert self.module.cmplxdat.medium_ref_index == np.array(1.+0.j)
+        assert np.all(self.module.cmplxdat.z == np.array([3.5, 7.0]))
+        assert np.all(self.module.cmplxdat.my_array == np.array([ 1.+2.j, -3.+4.j]))
+        assert np.all(self.module.cmplxdat.my_real_array == np.array([ 1., 2., 3.]))
+        assert np.all(self.module.cmplxdat.ref_index_one == np.array([13.0 + 21.0j]))
+        assert np.all(self.module.cmplxdat.ref_index_two == np.array([-30.0 + 43.0j]))
+
+    def test_crackedlines(self):
+        mod = crackfortran(self.sources)
+        assert mod[0]['vars']['x']['='] == '1.5'
+        assert mod[0]['vars']['y']['='] == '2.0'
+        assert mod[0]['vars']['pi']['='] == '3.1415926535897932384626433832795028841971693993751058209749445923078164062d0'
+        assert mod[0]['vars']['my_real_array']['='] == '(/1.0d0, 2.0d0, 3.0d0/)'
+        assert mod[0]['vars']['ref_index_one']['='] == '(13.0d0, 21.0d0)'
+        assert mod[0]['vars']['ref_index_two']['='] == '(-30.0d0, 43.0d0)'
+        assert mod[0]['vars']['my_array']['='] == '(/(1.0d0, 2.0d0), (-3.0d0, 4.0d0)/)'
+        assert mod[0]['vars']['z']['='] == '(/3.5,  7.0/)'
+
+class TestDataF77(util.F2PyTest):
+    sources = [util.getpath("tests", "src", "crackfortran", "data_common.f")]
+
+    # For gh-23276
+    def test_data_stmts(self):
+        assert self.module.mycom.mydata == 0
+
+    def test_crackedlines(self):
+        mod = crackfortran(str(self.sources[0]))
+        print(mod[0]['vars'])
+        assert mod[0]['vars']['mydata']['='] == '0'
+
+
+class TestDataMultiplierF77(util.F2PyTest):
+    sources = [util.getpath("tests", "src", "crackfortran", "data_multiplier.f")]
+
+    # For gh-23276
+    def test_data_stmts(self):
+        assert self.module.mycom.ivar1 == 3
+        assert self.module.mycom.ivar2 == 3
+        assert self.module.mycom.ivar3 == 2
+        assert self.module.mycom.ivar4 == 2
+        assert self.module.mycom.evar5 == 0
+
+
+class TestDataWithCommentsF77(util.F2PyTest):
+    sources = [util.getpath("tests", "src", "crackfortran", "data_with_comments.f")]
+
+    # For gh-23276
+    def test_data_stmts(self):
+        assert len(self.module.mycom.mytab) == 3
+        assert self.module.mycom.mytab[0] == 0
+        assert self.module.mycom.mytab[1] == 4
+        assert self.module.mycom.mytab[2] == 0
diff --git a/.venv/lib/python3.12/site-packages/numpy/f2py/tests/test_docs.py b/.venv/lib/python3.12/site-packages/numpy/f2py/tests/test_docs.py
new file mode 100644
index 00000000..6631dd82
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/numpy/f2py/tests/test_docs.py
@@ -0,0 +1,55 @@
+import os
+import pytest
+import numpy as np
+from numpy.testing import assert_array_equal, assert_equal
+from . import util
+
+
+def get_docdir():
+    # assuming that documentation tests are run from a source
+    # directory
+    return os.path.abspath(os.path.join(
+        os.path.dirname(__file__),
+        '..', '..', '..',
+        'doc', 'source', 'f2py', 'code'))
+
+
+pytestmark = pytest.mark.skipif(
+    not os.path.isdir(get_docdir()),
+    reason=('Could not find f2py documentation sources'
+            f' ({get_docdir()} does not exists)'))
+
+
+def _path(*a):
+    return os.path.join(*((get_docdir(),) + a))
+
+
+class TestDocAdvanced(util.F2PyTest):
+    # options = ['--debug-capi', '--build-dir', '/tmp/build-f2py']
+    sources = [_path('asterisk1.f90'), _path('asterisk2.f90'),
+               _path('ftype.f')]
+
+    def test_asterisk1(self):
+        foo = getattr(self.module, 'foo1')
+        assert_equal(foo(), b'123456789A12')
+
+    def test_asterisk2(self):
+        foo = getattr(self.module, 'foo2')
+        assert_equal(foo(2), b'12')
+        assert_equal(foo(12), b'123456789A12')
+        assert_equal(foo(24), b'123456789A123456789B')
+
+    def test_ftype(self):
+        ftype = self.module
+        ftype.foo()
+        assert_equal(ftype.data.a, 0)
+        ftype.data.a = 3
+        ftype.data.x = [1, 2, 3]
+        assert_equal(ftype.data.a, 3)
+        assert_array_equal(ftype.data.x,
+                           np.array([1, 2, 3], dtype=np.float32))
+        ftype.data.x[1] = 45
+        assert_array_equal(ftype.data.x,
+                           np.array([1, 45, 3], dtype=np.float32))
+
+    # TODO: implement test methods for other example Fortran codes
diff --git a/.venv/lib/python3.12/site-packages/numpy/f2py/tests/test_f2cmap.py b/.venv/lib/python3.12/site-packages/numpy/f2py/tests/test_f2cmap.py
new file mode 100644
index 00000000..d2967e4f
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/numpy/f2py/tests/test_f2cmap.py
@@ -0,0 +1,15 @@
+from . import util
+import numpy as np
+
+class TestF2Cmap(util.F2PyTest):
+    sources = [
+        util.getpath("tests", "src", "f2cmap", "isoFortranEnvMap.f90"),
+        util.getpath("tests", "src", "f2cmap", ".f2py_f2cmap")
+    ]
+
+    # gh-15095
+    def test_long_long_map(self):
+        inp = np.ones(3)
+        out = self.module.func1(inp)
+        exp_out = 3
+        assert out == exp_out
diff --git a/.venv/lib/python3.12/site-packages/numpy/f2py/tests/test_f2py2e.py b/.venv/lib/python3.12/site-packages/numpy/f2py/tests/test_f2py2e.py
new file mode 100644
index 00000000..659e0e96
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/numpy/f2py/tests/test_f2py2e.py
@@ -0,0 +1,896 @@
+import textwrap, re, sys, subprocess, shlex
+from pathlib import Path
+from collections import namedtuple
+import platform
+
+import pytest
+
+from . import util
+from numpy.f2py.f2py2e import main as f2pycli
+
+#########################
+# CLI utils and classes #
+#########################
+
+PPaths = namedtuple("PPaths", "finp, f90inp, pyf, wrap77, wrap90, cmodf")
+
+
+def get_io_paths(fname_inp, mname="untitled"):
+    """Takes in a temporary file for testing and returns the expected output and input paths
+
+    Here expected output is essentially one of any of the possible generated
+    files.
+
+    ..note::
+
+         Since this does not actually run f2py, none of these are guaranteed to
+         exist, and module names are typically incorrect
+
+    Parameters
+    ----------
+    fname_inp : str
+                The input filename
+    mname : str, optional
+                The name of the module, untitled by default
+
+    Returns
+    -------
+    genp : NamedTuple PPaths
+            The possible paths which are generated, not all of which exist
+    """
+    bpath = Path(fname_inp)
+    return PPaths(
+        finp=bpath.with_suffix(".f"),
+        f90inp=bpath.with_suffix(".f90"),
+        pyf=bpath.with_suffix(".pyf"),
+        wrap77=bpath.with_name(f"{mname}-f2pywrappers.f"),
+        wrap90=bpath.with_name(f"{mname}-f2pywrappers2.f90"),
+        cmodf=bpath.with_name(f"{mname}module.c"),
+    )
+
+
+##############
+# CLI Fixtures and Tests #
+#############
+
+
+@pytest.fixture(scope="session")
+def hello_world_f90(tmpdir_factory):
+    """Generates a single f90 file for testing"""
+    fdat = util.getpath("tests", "src", "cli", "hiworld.f90").read_text()
+    fn = tmpdir_factory.getbasetemp() / "hello.f90"
+    fn.write_text(fdat, encoding="ascii")
+    return fn
+
+
+@pytest.fixture(scope="session")
+def gh23598_warn(tmpdir_factory):
+    """F90 file for testing warnings in gh23598"""
+    fdat = util.getpath("tests", "src", "crackfortran", "gh23598Warn.f90").read_text()
+    fn = tmpdir_factory.getbasetemp() / "gh23598Warn.f90"
+    fn.write_text(fdat, encoding="ascii")
+    return fn
+
+
+@pytest.fixture(scope="session")
+def gh22819_cli(tmpdir_factory):
+    """F90 file for testing disallowed CLI arguments in ghff819"""
+    fdat = util.getpath("tests", "src", "cli", "gh_22819.pyf").read_text()
+    fn = tmpdir_factory.getbasetemp() / "gh_22819.pyf"
+    fn.write_text(fdat, encoding="ascii")
+    return fn
+
+
+@pytest.fixture(scope="session")
+def hello_world_f77(tmpdir_factory):
+    """Generates a single f77 file for testing"""
+    fdat = util.getpath("tests", "src", "cli", "hi77.f").read_text()
+    fn = tmpdir_factory.getbasetemp() / "hello.f"
+    fn.write_text(fdat, encoding="ascii")
+    return fn
+
+
+@pytest.fixture(scope="session")
+def retreal_f77(tmpdir_factory):
+    """Generates a single f77 file for testing"""
+    fdat = util.getpath("tests", "src", "return_real", "foo77.f").read_text()
+    fn = tmpdir_factory.getbasetemp() / "foo.f"
+    fn.write_text(fdat, encoding="ascii")
+    return fn
+
+@pytest.fixture(scope="session")
+def f2cmap_f90(tmpdir_factory):
+    """Generates a single f90 file for testing"""
+    fdat = util.getpath("tests", "src", "f2cmap", "isoFortranEnvMap.f90").read_text()
+    f2cmap = util.getpath("tests", "src", "f2cmap", ".f2py_f2cmap").read_text()
+    fn = tmpdir_factory.getbasetemp() / "f2cmap.f90"
+    fmap = tmpdir_factory.getbasetemp() / "mapfile"
+    fn.write_text(fdat, encoding="ascii")
+    fmap.write_text(f2cmap, encoding="ascii")
+    return fn
+
+
+def test_gh22819_cli(capfd, gh22819_cli, monkeypatch):
+    """Check that module names are handled correctly
+    gh-22819
+    Essentially, the -m name cannot be used to import the module, so the module
+    named in the .pyf needs to be used instead
+
+    CLI :: -m and a .pyf file
+    """
+    ipath = Path(gh22819_cli)
+    monkeypatch.setattr(sys, "argv", f"f2py -m blah {ipath}".split())
+    with util.switchdir(ipath.parent):
+        f2pycli()
+        gen_paths = [item.name for item in ipath.parent.rglob("*") if item.is_file()]
+        assert "blahmodule.c" not in gen_paths # shouldn't be generated
+        assert "blah-f2pywrappers.f" not in gen_paths
+        assert "test_22819-f2pywrappers.f" in gen_paths
+        assert "test_22819module.c" in gen_paths
+        assert "Ignoring blah"
+
+
+def test_gh22819_many_pyf(capfd, gh22819_cli, monkeypatch):
+    """Only one .pyf file allowed
+    gh-22819
+    CLI :: .pyf files
+    """
+    ipath = Path(gh22819_cli)
+    monkeypatch.setattr(sys, "argv", f"f2py -m blah {ipath} hello.pyf".split())
+    with util.switchdir(ipath.parent):
+        with pytest.raises(ValueError, match="Only one .pyf file per call"):
+            f2pycli()
+
+
+def test_gh23598_warn(capfd, gh23598_warn, monkeypatch):
+    foutl = get_io_paths(gh23598_warn, mname="test")
+    ipath = foutl.f90inp
+    monkeypatch.setattr(
+        sys, "argv",
+        f'f2py {ipath} -m test'.split())
+
+    with util.switchdir(ipath.parent):
+        f2pycli()  # Generate files
+        wrapper = foutl.wrap90.read_text()
+        assert "intproductf2pywrap, intpr" not in wrapper
+
+
+def test_gen_pyf(capfd, hello_world_f90, monkeypatch):
+    """Ensures that a signature file is generated via the CLI
+    CLI :: -h
+    """
+    ipath = Path(hello_world_f90)
+    opath = Path(hello_world_f90).stem + ".pyf"
+    monkeypatch.setattr(sys, "argv", f'f2py -h {opath} {ipath}'.split())
+
+    with util.switchdir(ipath.parent):
+        f2pycli()  # Generate wrappers
+        out, _ = capfd.readouterr()
+        assert "Saving signatures to file" in out
+        assert Path(f'{opath}').exists()
+
+
+def test_gen_pyf_stdout(capfd, hello_world_f90, monkeypatch):
+    """Ensures that a signature file can be dumped to stdout
+    CLI :: -h
+    """
+    ipath = Path(hello_world_f90)
+    monkeypatch.setattr(sys, "argv", f'f2py -h stdout {ipath}'.split())
+    with util.switchdir(ipath.parent):
+        f2pycli()
+        out, _ = capfd.readouterr()
+        assert "Saving signatures to file" in out
+        assert "function hi() ! in " in out
+
+
+def test_gen_pyf_no_overwrite(capfd, hello_world_f90, monkeypatch):
+    """Ensures that the CLI refuses to overwrite signature files
+    CLI :: -h without --overwrite-signature
+    """
+    ipath = Path(hello_world_f90)
+    monkeypatch.setattr(sys, "argv", f'f2py -h faker.pyf {ipath}'.split())
+
+    with util.switchdir(ipath.parent):
+        Path("faker.pyf").write_text("Fake news", encoding="ascii")
+        with pytest.raises(SystemExit):
+            f2pycli()  # Refuse to overwrite
+            _, err = capfd.readouterr()
+            assert "Use --overwrite-signature to overwrite" in err
+
+
+@pytest.mark.skipif((platform.system() != 'Linux') or (sys.version_info <= (3, 12)),
+                    reason='Compiler and 3.12 required')
+def test_untitled_cli(capfd, hello_world_f90, monkeypatch):
+    """Check that modules are named correctly
+
+    CLI :: defaults
+    """
+    ipath = Path(hello_world_f90)
+    monkeypatch.setattr(sys, "argv", f"f2py --backend meson -c {ipath}".split())
+    with util.switchdir(ipath.parent):
+        f2pycli()
+        out, _ = capfd.readouterr()
+        assert "untitledmodule.c" in out
+
+
+@pytest.mark.skipif((platform.system() != 'Linux') or (sys.version_info <= (3, 12)), reason='Compiler and 3.12 required')
+def test_no_py312_distutils_fcompiler(capfd, hello_world_f90, monkeypatch):
+    """Check that no distutils imports are performed on 3.12
+    CLI :: --fcompiler --help-link --backend distutils
+    """
+    MNAME = "hi"
+    foutl = get_io_paths(hello_world_f90, mname=MNAME)
+    ipath = foutl.f90inp
+    monkeypatch.setattr(
+        sys, "argv", f"f2py {ipath} -c --fcompiler=gfortran -m {MNAME}".split()
+    )
+    with util.switchdir(ipath.parent):
+        f2pycli()
+        out, _ = capfd.readouterr()
+        assert "--fcompiler cannot be used with meson" in out
+    monkeypatch.setattr(
+        sys, "argv", f"f2py --help-link".split()
+    )
+    with util.switchdir(ipath.parent):
+        f2pycli()
+        out, _ = capfd.readouterr()
+        assert "Use --dep for meson builds" in out
+    MNAME = "hi2" # Needs to be different for a new -c
+    monkeypatch.setattr(
+        sys, "argv", f"f2py {ipath} -c -m {MNAME} --backend distutils".split()
+    )
+    with util.switchdir(ipath.parent):
+        f2pycli()
+        out, _ = capfd.readouterr()
+        assert "Cannot use distutils backend with Python>=3.12" in out
+
+
+@pytest.mark.xfail
+def test_f2py_skip(capfd, retreal_f77, monkeypatch):
+    """Tests that functions can be skipped
+    CLI :: skip:
+    """
+    foutl = get_io_paths(retreal_f77, mname="test")
+    ipath = foutl.finp
+    toskip = "t0 t4 t8 sd s8 s4"
+    remaining = "td s0"
+    monkeypatch.setattr(
+        sys, "argv",
+        f'f2py {ipath} -m test skip: {toskip}'.split())
+
+    with util.switchdir(ipath.parent):
+        f2pycli()
+        out, err = capfd.readouterr()
+        for skey in toskip.split():
+            assert (
+                f'buildmodule: Could not found the body of interfaced routine "{skey}". Skipping.'
+                in err)
+        for rkey in remaining.split():
+            assert f'Constructing wrapper function "{rkey}"' in out
+
+
+def test_f2py_only(capfd, retreal_f77, monkeypatch):
+    """Test that functions can be kept by only:
+    CLI :: only:
+    """
+    foutl = get_io_paths(retreal_f77, mname="test")
+    ipath = foutl.finp
+    toskip = "t0 t4 t8 sd s8 s4"
+    tokeep = "td s0"
+    monkeypatch.setattr(
+        sys, "argv",
+        f'f2py {ipath} -m test only: {tokeep}'.split())
+
+    with util.switchdir(ipath.parent):
+        f2pycli()
+        out, err = capfd.readouterr()
+        for skey in toskip.split():
+            assert (
+                f'buildmodule: Could not find the body of interfaced routine "{skey}". Skipping.'
+                in err)
+        for rkey in tokeep.split():
+            assert f'Constructing wrapper function "{rkey}"' in out
+
+
+def test_file_processing_switch(capfd, hello_world_f90, retreal_f77,
+                                monkeypatch):
+    """Tests that it is possible to return to file processing mode
+    CLI :: :
+    BUG: numpy-gh #20520
+    """
+    foutl = get_io_paths(retreal_f77, mname="test")
+    ipath = foutl.finp
+    toskip = "t0 t4 t8 sd s8 s4"
+    ipath2 = Path(hello_world_f90)
+    tokeep = "td s0 hi"  # hi is in ipath2
+    mname = "blah"
+    monkeypatch.setattr(
+        sys,
+        "argv",
+        f'f2py {ipath} -m {mname} only: {tokeep} : {ipath2}'.split(
+        ),
+    )
+
+    with util.switchdir(ipath.parent):
+        f2pycli()
+        out, err = capfd.readouterr()
+        for skey in toskip.split():
+            assert (
+                f'buildmodule: Could not find the body of interfaced routine "{skey}". Skipping.'
+                in err)
+        for rkey in tokeep.split():
+            assert f'Constructing wrapper function "{rkey}"' in out
+
+
+def test_mod_gen_f77(capfd, hello_world_f90, monkeypatch):
+    """Checks the generation of files based on a module name
+    CLI :: -m
+    """
+    MNAME = "hi"
+    foutl = get_io_paths(hello_world_f90, mname=MNAME)
+    ipath = foutl.f90inp
+    monkeypatch.setattr(sys, "argv", f'f2py {ipath} -m {MNAME}'.split())
+    with util.switchdir(ipath.parent):
+        f2pycli()
+
+    # Always generate C module
+    assert Path.exists(foutl.cmodf)
+    # File contains a function, check for F77 wrappers
+    assert Path.exists(foutl.wrap77)
+
+
+def test_mod_gen_gh25263(capfd, hello_world_f77, monkeypatch):
+    """Check that pyf files are correctly generated with module structure
+    CLI :: -m <name> -h pyf_file
+    BUG: numpy-gh #20520
+    """
+    MNAME = "hi"
+    foutl = get_io_paths(hello_world_f77, mname=MNAME)
+    ipath = foutl.finp
+    monkeypatch.setattr(sys, "argv", f'f2py {ipath} -m {MNAME} -h hi.pyf'.split())
+    with util.switchdir(ipath.parent):
+        f2pycli()
+        with Path('hi.pyf').open() as hipyf:
+            pyfdat = hipyf.read()
+            assert "python module hi" in pyfdat
+
+
+def test_lower_cmod(capfd, hello_world_f77, monkeypatch):
+    """Lowers cases by flag or when -h is present
+
+    CLI :: --[no-]lower
+    """
+    foutl = get_io_paths(hello_world_f77, mname="test")
+    ipath = foutl.finp
+    capshi = re.compile(r"HI\(\)")
+    capslo = re.compile(r"hi\(\)")
+    # Case I: --lower is passed
+    monkeypatch.setattr(sys, "argv", f'f2py {ipath} -m test --lower'.split())
+    with util.switchdir(ipath.parent):
+        f2pycli()
+        out, _ = capfd.readouterr()
+        assert capslo.search(out) is not None
+        assert capshi.search(out) is None
+    # Case II: --no-lower is passed
+    monkeypatch.setattr(sys, "argv",
+                        f'f2py {ipath} -m test --no-lower'.split())
+    with util.switchdir(ipath.parent):
+        f2pycli()
+        out, _ = capfd.readouterr()
+        assert capslo.search(out) is None
+        assert capshi.search(out) is not None
+
+
+def test_lower_sig(capfd, hello_world_f77, monkeypatch):
+    """Lowers cases in signature files by flag or when -h is present
+
+    CLI :: --[no-]lower -h
+    """
+    foutl = get_io_paths(hello_world_f77, mname="test")
+    ipath = foutl.finp
+    # Signature files
+    capshi = re.compile(r"Block: HI")
+    capslo = re.compile(r"Block: hi")
+    # Case I: --lower is implied by -h
+    # TODO: Clean up to prevent passing --overwrite-signature
+    monkeypatch.setattr(
+        sys,
+        "argv",
+        f'f2py {ipath} -h {foutl.pyf} -m test --overwrite-signature'.split(),
+    )
+
+    with util.switchdir(ipath.parent):
+        f2pycli()
+        out, _ = capfd.readouterr()
+        assert capslo.search(out) is not None
+        assert capshi.search(out) is None
+
+    # Case II: --no-lower overrides -h
+    monkeypatch.setattr(
+        sys,
+        "argv",
+        f'f2py {ipath} -h {foutl.pyf} -m test --overwrite-signature --no-lower'
+        .split(),
+    )
+
+    with util.switchdir(ipath.parent):
+        f2pycli()
+        out, _ = capfd.readouterr()
+        assert capslo.search(out) is None
+        assert capshi.search(out) is not None
+
+
+def test_build_dir(capfd, hello_world_f90, monkeypatch):
+    """Ensures that the build directory can be specified
+
+    CLI :: --build-dir
+    """
+    ipath = Path(hello_world_f90)
+    mname = "blah"
+    odir = "tttmp"
+    monkeypatch.setattr(sys, "argv",
+                        f'f2py -m {mname} {ipath} --build-dir {odir}'.split())
+
+    with util.switchdir(ipath.parent):
+        f2pycli()
+        out, _ = capfd.readouterr()
+        assert f"Wrote C/API module \"{mname}\"" in out
+
+
+def test_overwrite(capfd, hello_world_f90, monkeypatch):
+    """Ensures that the build directory can be specified
+
+    CLI :: --overwrite-signature
+    """
+    ipath = Path(hello_world_f90)
+    monkeypatch.setattr(
+        sys, "argv",
+        f'f2py -h faker.pyf {ipath} --overwrite-signature'.split())
+
+    with util.switchdir(ipath.parent):
+        Path("faker.pyf").write_text("Fake news", encoding="ascii")
+        f2pycli()
+        out, _ = capfd.readouterr()
+        assert "Saving signatures to file" in out
+
+
+def test_latexdoc(capfd, hello_world_f90, monkeypatch):
+    """Ensures that TeX documentation is written out
+
+    CLI :: --latex-doc
+    """
+    ipath = Path(hello_world_f90)
+    mname = "blah"
+    monkeypatch.setattr(sys, "argv",
+                        f'f2py -m {mname} {ipath} --latex-doc'.split())
+
+    with util.switchdir(ipath.parent):
+        f2pycli()
+        out, _ = capfd.readouterr()
+        assert "Documentation is saved to file" in out
+        with Path(f"{mname}module.tex").open() as otex:
+            assert "\\documentclass" in otex.read()
+
+
+def test_nolatexdoc(capfd, hello_world_f90, monkeypatch):
+    """Ensures that TeX documentation is written out
+
+    CLI :: --no-latex-doc
+    """
+    ipath = Path(hello_world_f90)
+    mname = "blah"
+    monkeypatch.setattr(sys, "argv",
+                        f'f2py -m {mname} {ipath} --no-latex-doc'.split())
+
+    with util.switchdir(ipath.parent):
+        f2pycli()
+        out, _ = capfd.readouterr()
+        assert "Documentation is saved to file" not in out
+
+
+def test_shortlatex(capfd, hello_world_f90, monkeypatch):
+    """Ensures that truncated documentation is written out
+
+    TODO: Test to ensure this has no effect without --latex-doc
+    CLI :: --latex-doc --short-latex
+    """
+    ipath = Path(hello_world_f90)
+    mname = "blah"
+    monkeypatch.setattr(
+        sys,
+        "argv",
+        f'f2py -m {mname} {ipath} --latex-doc --short-latex'.split(),
+    )
+
+    with util.switchdir(ipath.parent):
+        f2pycli()
+        out, _ = capfd.readouterr()
+        assert "Documentation is saved to file" in out
+        with Path(f"./{mname}module.tex").open() as otex:
+            assert "\\documentclass" not in otex.read()
+
+
+def test_restdoc(capfd, hello_world_f90, monkeypatch):
+    """Ensures that RsT documentation is written out
+
+    CLI :: --rest-doc
+    """
+    ipath = Path(hello_world_f90)
+    mname = "blah"
+    monkeypatch.setattr(sys, "argv",
+                        f'f2py -m {mname} {ipath} --rest-doc'.split())
+
+    with util.switchdir(ipath.parent):
+        f2pycli()
+        out, _ = capfd.readouterr()
+        assert "ReST Documentation is saved to file" in out
+        with Path(f"./{mname}module.rest").open() as orst:
+            assert r".. -*- rest -*-" in orst.read()
+
+
+def test_norestexdoc(capfd, hello_world_f90, monkeypatch):
+    """Ensures that TeX documentation is written out
+
+    CLI :: --no-rest-doc
+    """
+    ipath = Path(hello_world_f90)
+    mname = "blah"
+    monkeypatch.setattr(sys, "argv",
+                        f'f2py -m {mname} {ipath} --no-rest-doc'.split())
+
+    with util.switchdir(ipath.parent):
+        f2pycli()
+        out, _ = capfd.readouterr()
+        assert "ReST Documentation is saved to file" not in out
+
+
+def test_debugcapi(capfd, hello_world_f90, monkeypatch):
+    """Ensures that debugging wrappers are written
+
+    CLI :: --debug-capi
+    """
+    ipath = Path(hello_world_f90)
+    mname = "blah"
+    monkeypatch.setattr(sys, "argv",
+                        f'f2py -m {mname} {ipath} --debug-capi'.split())
+
+    with util.switchdir(ipath.parent):
+        f2pycli()
+        with Path(f"./{mname}module.c").open() as ocmod:
+            assert r"#define DEBUGCFUNCS" in ocmod.read()
+
+
+@pytest.mark.xfail(reason="Consistently fails on CI.")
+def test_debugcapi_bld(hello_world_f90, monkeypatch):
+    """Ensures that debugging wrappers work
+
+    CLI :: --debug-capi -c
+    """
+    ipath = Path(hello_world_f90)
+    mname = "blah"
+    monkeypatch.setattr(sys, "argv",
+                        f'f2py -m {mname} {ipath} -c --debug-capi'.split())
+
+    with util.switchdir(ipath.parent):
+        f2pycli()
+        cmd_run = shlex.split("python3 -c \"import blah; blah.hi()\"")
+        rout = subprocess.run(cmd_run, capture_output=True, encoding='UTF-8')
+        eout = ' Hello World\n'
+        eerr = textwrap.dedent("""\
+debug-capi:Python C/API function blah.hi()
+debug-capi:float hi=:output,hidden,scalar
+debug-capi:hi=0
+debug-capi:Fortran subroutine `f2pywraphi(&hi)'
+debug-capi:hi=0
+debug-capi:Building return value.
+debug-capi:Python C/API function blah.hi: successful.
+debug-capi:Freeing memory.
+        """)
+        assert rout.stdout == eout
+        assert rout.stderr == eerr
+
+
+def test_wrapfunc_def(capfd, hello_world_f90, monkeypatch):
+    """Ensures that fortran subroutine wrappers for F77 are included by default
+
+    CLI :: --[no]-wrap-functions
+    """
+    # Implied
+    ipath = Path(hello_world_f90)
+    mname = "blah"
+    monkeypatch.setattr(sys, "argv", f'f2py -m {mname} {ipath}'.split())
+
+    with util.switchdir(ipath.parent):
+        f2pycli()
+    out, _ = capfd.readouterr()
+    assert r"Fortran 77 wrappers are saved to" in out
+
+    # Explicit
+    monkeypatch.setattr(sys, "argv",
+                        f'f2py -m {mname} {ipath} --wrap-functions'.split())
+
+    with util.switchdir(ipath.parent):
+        f2pycli()
+        out, _ = capfd.readouterr()
+        assert r"Fortran 77 wrappers are saved to" in out
+
+
+def test_nowrapfunc(capfd, hello_world_f90, monkeypatch):
+    """Ensures that fortran subroutine wrappers for F77 can be disabled
+
+    CLI :: --no-wrap-functions
+    """
+    ipath = Path(hello_world_f90)
+    mname = "blah"
+    monkeypatch.setattr(sys, "argv",
+                        f'f2py -m {mname} {ipath} --no-wrap-functions'.split())
+
+    with util.switchdir(ipath.parent):
+        f2pycli()
+        out, _ = capfd.readouterr()
+        assert r"Fortran 77 wrappers are saved to" not in out
+
+
+def test_inclheader(capfd, hello_world_f90, monkeypatch):
+    """Add to the include directories
+
+    CLI :: -include
+    TODO: Document this in the help string
+    """
+    ipath = Path(hello_world_f90)
+    mname = "blah"
+    monkeypatch.setattr(
+        sys,
+        "argv",
+        f'f2py -m {mname} {ipath} -include<stdbool.h> -include<stdio.h> '.
+        split(),
+    )
+
+    with util.switchdir(ipath.parent):
+        f2pycli()
+        with Path(f"./{mname}module.c").open() as ocmod:
+            ocmr = ocmod.read()
+            assert "#include <stdbool.h>" in ocmr
+            assert "#include <stdio.h>" in ocmr
+
+
+def test_inclpath():
+    """Add to the include directories
+
+    CLI :: --include-paths
+    """
+    # TODO: populate
+    pass
+
+
+def test_hlink():
+    """Add to the include directories
+
+    CLI :: --help-link
+    """
+    # TODO: populate
+    pass
+
+
+def test_f2cmap(capfd, f2cmap_f90, monkeypatch):
+    """Check that Fortran-to-Python KIND specs can be passed
+
+    CLI :: --f2cmap
+    """
+    ipath = Path(f2cmap_f90)
+    monkeypatch.setattr(sys, "argv", f'f2py -m blah {ipath} --f2cmap mapfile'.split())
+
+    with util.switchdir(ipath.parent):
+        f2pycli()
+        out, _ = capfd.readouterr()
+        assert "Reading f2cmap from 'mapfile' ..." in out
+        assert "Mapping \"real(kind=real32)\" to \"float\"" in out
+        assert "Mapping \"real(kind=real64)\" to \"double\"" in out
+        assert "Mapping \"integer(kind=int64)\" to \"long_long\"" in out
+        assert "Successfully applied user defined f2cmap changes" in out
+
+
+def test_quiet(capfd, hello_world_f90, monkeypatch):
+    """Reduce verbosity
+
+    CLI :: --quiet
+    """
+    ipath = Path(hello_world_f90)
+    monkeypatch.setattr(sys, "argv", f'f2py -m blah {ipath} --quiet'.split())
+
+    with util.switchdir(ipath.parent):
+        f2pycli()
+        out, _ = capfd.readouterr()
+        assert len(out) == 0
+
+
+def test_verbose(capfd, hello_world_f90, monkeypatch):
+    """Increase verbosity
+
+    CLI :: --verbose
+    """
+    ipath = Path(hello_world_f90)
+    monkeypatch.setattr(sys, "argv", f'f2py -m blah {ipath} --verbose'.split())
+
+    with util.switchdir(ipath.parent):
+        f2pycli()
+        out, _ = capfd.readouterr()
+        assert "analyzeline" in out
+
+
+def test_version(capfd, monkeypatch):
+    """Ensure version
+
+    CLI :: -v
+    """
+    monkeypatch.setattr(sys, "argv", 'f2py -v'.split())
+    # TODO: f2py2e should not call sys.exit() after printing the version
+    with pytest.raises(SystemExit):
+        f2pycli()
+        out, _ = capfd.readouterr()
+        import numpy as np
+        assert np.__version__ == out.strip()
+
+
+@pytest.mark.xfail(reason="Consistently fails on CI.")
+def test_npdistop(hello_world_f90, monkeypatch):
+    """
+    CLI :: -c
+    """
+    ipath = Path(hello_world_f90)
+    monkeypatch.setattr(sys, "argv", f'f2py -m blah {ipath} -c'.split())
+
+    with util.switchdir(ipath.parent):
+        f2pycli()
+        cmd_run = shlex.split("python -c \"import blah; blah.hi()\"")
+        rout = subprocess.run(cmd_run, capture_output=True, encoding='UTF-8')
+        eout = ' Hello World\n'
+        assert rout.stdout == eout
+
+
+# Numpy distutils flags
+# TODO: These should be tested separately
+
+
+def test_npd_fcompiler():
+    """
+    CLI :: -c --fcompiler
+    """
+    # TODO: populate
+    pass
+
+
+def test_npd_compiler():
+    """
+    CLI :: -c --compiler
+    """
+    # TODO: populate
+    pass
+
+
+def test_npd_help_fcompiler():
+    """
+    CLI :: -c --help-fcompiler
+    """
+    # TODO: populate
+    pass
+
+
+def test_npd_f77exec():
+    """
+    CLI :: -c --f77exec
+    """
+    # TODO: populate
+    pass
+
+
+def test_npd_f90exec():
+    """
+    CLI :: -c --f90exec
+    """
+    # TODO: populate
+    pass
+
+
+def test_npd_f77flags():
+    """
+    CLI :: -c --f77flags
+    """
+    # TODO: populate
+    pass
+
+
+def test_npd_f90flags():
+    """
+    CLI :: -c --f90flags
+    """
+    # TODO: populate
+    pass
+
+
+def test_npd_opt():
+    """
+    CLI :: -c --opt
+    """
+    # TODO: populate
+    pass
+
+
+def test_npd_arch():
+    """
+    CLI :: -c --arch
+    """
+    # TODO: populate
+    pass
+
+
+def test_npd_noopt():
+    """
+    CLI :: -c --noopt
+    """
+    # TODO: populate
+    pass
+
+
+def test_npd_noarch():
+    """
+    CLI :: -c --noarch
+    """
+    # TODO: populate
+    pass
+
+
+def test_npd_debug():
+    """
+    CLI :: -c --debug
+    """
+    # TODO: populate
+    pass
+
+
+def test_npd_link_auto():
+    """
+    CLI :: -c --link-<resource>
+    """
+    # TODO: populate
+    pass
+
+
+def test_npd_lib():
+    """
+    CLI :: -c -L/path/to/lib/ -l<libname>
+    """
+    # TODO: populate
+    pass
+
+
+def test_npd_define():
+    """
+    CLI :: -D<define>
+    """
+    # TODO: populate
+    pass
+
+
+def test_npd_undefine():
+    """
+    CLI :: -U<name>
+    """
+    # TODO: populate
+    pass
+
+
+def test_npd_incl():
+    """
+    CLI :: -I/path/to/include/
+    """
+    # TODO: populate
+    pass
+
+
+def test_npd_linker():
+    """
+    CLI :: <filename>.o <filename>.so <filename>.a
+    """
+    # TODO: populate
+    pass
diff --git a/.venv/lib/python3.12/site-packages/numpy/f2py/tests/test_isoc.py b/.venv/lib/python3.12/site-packages/numpy/f2py/tests/test_isoc.py
new file mode 100644
index 00000000..594bd7ca
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/numpy/f2py/tests/test_isoc.py
@@ -0,0 +1,52 @@
+from . import util
+import numpy as np
+import pytest
+from numpy.testing import assert_allclose
+
+class TestISOC(util.F2PyTest):
+    sources = [
+        util.getpath("tests", "src", "isocintrin", "isoCtests.f90"),
+    ]
+
+    # gh-24553
+    def test_c_double(self):
+        out = self.module.coddity.c_add(1, 2)
+        exp_out = 3
+        assert  out == exp_out
+
+    # gh-9693
+    def test_bindc_function(self):
+        out = self.module.coddity.wat(1, 20)
+        exp_out = 8
+        assert  out == exp_out
+
+    # gh-25207
+    def test_bindc_kinds(self):
+        out = self.module.coddity.c_add_int64(1, 20)
+        exp_out = 21
+        assert  out == exp_out
+
+    # gh-25207
+    def test_bindc_add_arr(self):
+        a = np.array([1,2,3])
+        b = np.array([1,2,3])
+        out = self.module.coddity.add_arr(a, b)
+        exp_out = a*2
+        assert_allclose(out, exp_out)
+
+
+def test_process_f2cmap_dict():
+    from numpy.f2py.auxfuncs import process_f2cmap_dict
+
+    f2cmap_all = {"integer": {"8": "rubbish_type"}}
+    new_map = {"INTEGER": {"4": "int"}}
+    c2py_map = {"int": "int", "rubbish_type": "long"}
+
+    exp_map, exp_maptyp = ({"integer": {"8": "rubbish_type", "4": "int"}}, ["int"])
+
+    # Call the function
+    res_map, res_maptyp = process_f2cmap_dict(f2cmap_all, new_map, c2py_map)
+
+    # Assert the result is as expected
+    assert res_map == exp_map
+    assert res_maptyp == exp_maptyp
diff --git a/.venv/lib/python3.12/site-packages/numpy/f2py/tests/test_kind.py b/.venv/lib/python3.12/site-packages/numpy/f2py/tests/test_kind.py
new file mode 100644
index 00000000..69b85aaa
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/numpy/f2py/tests/test_kind.py
@@ -0,0 +1,47 @@
+import os
+import pytest
+import platform
+
+from numpy.f2py.crackfortran import (
+    _selected_int_kind_func as selected_int_kind,
+    _selected_real_kind_func as selected_real_kind,
+)
+from . import util
+
+
+class TestKind(util.F2PyTest):
+    sources = [util.getpath("tests", "src", "kind", "foo.f90")]
+
+    def test_int(self):
+        """Test `int` kind_func for integers up to 10**40."""
+        selectedintkind = self.module.selectedintkind
+
+        for i in range(40):
+            assert selectedintkind(i) == selected_int_kind(
+                i
+            ), f"selectedintkind({i}): expected {selected_int_kind(i)!r} but got {selectedintkind(i)!r}"
+
+    def test_real(self):
+        """
+        Test (processor-dependent) `real` kind_func for real numbers
+        of up to 31 digits precision (extended/quadruple).
+        """
+        selectedrealkind = self.module.selectedrealkind
+
+        for i in range(32):
+            assert selectedrealkind(i) == selected_real_kind(
+                i
+            ), f"selectedrealkind({i}): expected {selected_real_kind(i)!r} but got {selectedrealkind(i)!r}"
+
+    @pytest.mark.xfail(platform.machine().lower().startswith("ppc"),
+                       reason="Some PowerPC may not support full IEEE 754 precision")
+    def test_quad_precision(self):
+        """
+        Test kind_func for quadruple precision [`real(16)`] of 32+ digits .
+        """
+        selectedrealkind = self.module.selectedrealkind
+
+        for i in range(32, 40):
+            assert selectedrealkind(i) == selected_real_kind(
+                i
+            ), f"selectedrealkind({i}): expected {selected_real_kind(i)!r} but got {selectedrealkind(i)!r}"
diff --git a/.venv/lib/python3.12/site-packages/numpy/f2py/tests/test_mixed.py b/.venv/lib/python3.12/site-packages/numpy/f2py/tests/test_mixed.py
new file mode 100644
index 00000000..80653b7d
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/numpy/f2py/tests/test_mixed.py
@@ -0,0 +1,33 @@
+import os
+import textwrap
+import pytest
+
+from numpy.testing import IS_PYPY
+from . import util
+
+
+class TestMixed(util.F2PyTest):
+    sources = [
+        util.getpath("tests", "src", "mixed", "foo.f"),
+        util.getpath("tests", "src", "mixed", "foo_fixed.f90"),
+        util.getpath("tests", "src", "mixed", "foo_free.f90"),
+    ]
+
+    def test_all(self):
+        assert self.module.bar11() == 11
+        assert self.module.foo_fixed.bar12() == 12
+        assert self.module.foo_free.bar13() == 13
+
+    @pytest.mark.xfail(IS_PYPY,
+                       reason="PyPy cannot modify tp_doc after PyType_Ready")
+    def test_docstring(self):
+        expected = textwrap.dedent("""\
+        a = bar11()
+
+        Wrapper for ``bar11``.
+
+        Returns
+        -------
+        a : int
+        """)
+        assert self.module.bar11.__doc__ == expected
diff --git a/.venv/lib/python3.12/site-packages/numpy/f2py/tests/test_module_doc.py b/.venv/lib/python3.12/site-packages/numpy/f2py/tests/test_module_doc.py
new file mode 100644
index 00000000..28822d40
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/numpy/f2py/tests/test_module_doc.py
@@ -0,0 +1,27 @@
+import os
+import sys
+import pytest
+import textwrap
+
+from . import util
+from numpy.testing import IS_PYPY
+
+
+class TestModuleDocString(util.F2PyTest):
+    sources = [
+        util.getpath("tests", "src", "module_data",
+                     "module_data_docstring.f90")
+    ]
+
+    @pytest.mark.skipif(sys.platform == "win32",
+                        reason="Fails with MinGW64 Gfortran (Issue #9673)")
+    @pytest.mark.xfail(IS_PYPY,
+                       reason="PyPy cannot modify tp_doc after PyType_Ready")
+    def test_module_docstring(self):
+        assert self.module.mod.__doc__ == textwrap.dedent("""\
+                     i : 'i'-scalar
+                     x : 'i'-array(4)
+                     a : 'f'-array(2,3)
+                     b : 'f'-array(-1,-1), not allocated\x00
+                     foo()\n
+                     Wrapper for ``foo``.\n\n""")
diff --git a/.venv/lib/python3.12/site-packages/numpy/f2py/tests/test_parameter.py b/.venv/lib/python3.12/site-packages/numpy/f2py/tests/test_parameter.py
new file mode 100644
index 00000000..2f620eaa
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/numpy/f2py/tests/test_parameter.py
@@ -0,0 +1,112 @@
+import os
+import pytest
+
+import numpy as np
+
+from . import util
+
+
+class TestParameters(util.F2PyTest):
+    # Check that intent(in out) translates as intent(inout)
+    sources = [
+        util.getpath("tests", "src", "parameter", "constant_real.f90"),
+        util.getpath("tests", "src", "parameter", "constant_integer.f90"),
+        util.getpath("tests", "src", "parameter", "constant_both.f90"),
+        util.getpath("tests", "src", "parameter", "constant_compound.f90"),
+        util.getpath("tests", "src", "parameter", "constant_non_compound.f90"),
+    ]
+
+    @pytest.mark.slow
+    def test_constant_real_single(self):
+        # non-contiguous should raise error
+        x = np.arange(6, dtype=np.float32)[::2]
+        pytest.raises(ValueError, self.module.foo_single, x)
+
+        # check values with contiguous array
+        x = np.arange(3, dtype=np.float32)
+        self.module.foo_single(x)
+        assert np.allclose(x, [0 + 1 + 2 * 3, 1, 2])
+
+    @pytest.mark.slow
+    def test_constant_real_double(self):
+        # non-contiguous should raise error
+        x = np.arange(6, dtype=np.float64)[::2]
+        pytest.raises(ValueError, self.module.foo_double, x)
+
+        # check values with contiguous array
+        x = np.arange(3, dtype=np.float64)
+        self.module.foo_double(x)
+        assert np.allclose(x, [0 + 1 + 2 * 3, 1, 2])
+
+    @pytest.mark.slow
+    def test_constant_compound_int(self):
+        # non-contiguous should raise error
+        x = np.arange(6, dtype=np.int32)[::2]
+        pytest.raises(ValueError, self.module.foo_compound_int, x)
+
+        # check values with contiguous array
+        x = np.arange(3, dtype=np.int32)
+        self.module.foo_compound_int(x)
+        assert np.allclose(x, [0 + 1 + 2 * 6, 1, 2])
+
+    @pytest.mark.slow
+    def test_constant_non_compound_int(self):
+        # check values
+        x = np.arange(4, dtype=np.int32)
+        self.module.foo_non_compound_int(x)
+        assert np.allclose(x, [0 + 1 + 2 + 3 * 4, 1, 2, 3])
+
+    @pytest.mark.slow
+    def test_constant_integer_int(self):
+        # non-contiguous should raise error
+        x = np.arange(6, dtype=np.int32)[::2]
+        pytest.raises(ValueError, self.module.foo_int, x)
+
+        # check values with contiguous array
+        x = np.arange(3, dtype=np.int32)
+        self.module.foo_int(x)
+        assert np.allclose(x, [0 + 1 + 2 * 3, 1, 2])
+
+    @pytest.mark.slow
+    def test_constant_integer_long(self):
+        # non-contiguous should raise error
+        x = np.arange(6, dtype=np.int64)[::2]
+        pytest.raises(ValueError, self.module.foo_long, x)
+
+        # check values with contiguous array
+        x = np.arange(3, dtype=np.int64)
+        self.module.foo_long(x)
+        assert np.allclose(x, [0 + 1 + 2 * 3, 1, 2])
+
+    @pytest.mark.slow
+    def test_constant_both(self):
+        # non-contiguous should raise error
+        x = np.arange(6, dtype=np.float64)[::2]
+        pytest.raises(ValueError, self.module.foo, x)
+
+        # check values with contiguous array
+        x = np.arange(3, dtype=np.float64)
+        self.module.foo(x)
+        assert np.allclose(x, [0 + 1 * 3 * 3 + 2 * 3 * 3, 1 * 3, 2 * 3])
+
+    @pytest.mark.slow
+    def test_constant_no(self):
+        # non-contiguous should raise error
+        x = np.arange(6, dtype=np.float64)[::2]
+        pytest.raises(ValueError, self.module.foo_no, x)
+
+        # check values with contiguous array
+        x = np.arange(3, dtype=np.float64)
+        self.module.foo_no(x)
+        assert np.allclose(x, [0 + 1 * 3 * 3 + 2 * 3 * 3, 1 * 3, 2 * 3])
+
+    @pytest.mark.slow
+    def test_constant_sum(self):
+        # non-contiguous should raise error
+        x = np.arange(6, dtype=np.float64)[::2]
+        pytest.raises(ValueError, self.module.foo_sum, x)
+
+        # check values with contiguous array
+        x = np.arange(3, dtype=np.float64)
+        self.module.foo_sum(x)
+        assert np.allclose(x, [0 + 1 * 3 * 3 + 2 * 3 * 3, 1 * 3, 2 * 3])
diff --git a/.venv/lib/python3.12/site-packages/numpy/f2py/tests/test_pyf_src.py b/.venv/lib/python3.12/site-packages/numpy/f2py/tests/test_pyf_src.py
new file mode 100644
index 00000000..f77ded2f
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/numpy/f2py/tests/test_pyf_src.py
@@ -0,0 +1,44 @@
+# This test is ported from numpy.distutils
+from numpy.f2py._src_pyf import process_str
+from numpy.testing import assert_equal
+
+
+pyf_src = """
+python module foo
+    <_rd=real,double precision>
+    interface
+        subroutine <s,d>foosub(tol)
+            <_rd>, intent(in,out) :: tol
+        end subroutine <s,d>foosub
+    end interface
+end python module foo
+"""
+
+expected_pyf = """
+python module foo
+    interface
+        subroutine sfoosub(tol)
+            real, intent(in,out) :: tol
+        end subroutine sfoosub
+        subroutine dfoosub(tol)
+            double precision, intent(in,out) :: tol
+        end subroutine dfoosub
+    end interface
+end python module foo
+"""
+
+
+def normalize_whitespace(s):
+    """
+    Remove leading and trailing whitespace, and convert internal
+    stretches of whitespace to a single space.
+    """
+    return ' '.join(s.split())
+
+
+def test_from_template():
+    """Regression test for gh-10712."""
+    pyf = process_str(pyf_src)
+    normalized_pyf = normalize_whitespace(pyf)
+    normalized_expected_pyf = normalize_whitespace(expected_pyf)
+    assert_equal(normalized_pyf, normalized_expected_pyf)
diff --git a/.venv/lib/python3.12/site-packages/numpy/f2py/tests/test_quoted_character.py b/.venv/lib/python3.12/site-packages/numpy/f2py/tests/test_quoted_character.py
new file mode 100644
index 00000000..82671cd8
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/numpy/f2py/tests/test_quoted_character.py
@@ -0,0 +1,16 @@
+"""See https://github.com/numpy/numpy/pull/10676.
+
+"""
+import sys
+import pytest
+
+from . import util
+
+
+class TestQuotedCharacter(util.F2PyTest):
+    sources = [util.getpath("tests", "src", "quoted_character", "foo.f")]
+
+    @pytest.mark.skipif(sys.platform == "win32",
+                        reason="Fails with MinGW64 Gfortran (Issue #9673)")
+    def test_quoted_character(self):
+        assert self.module.foo() == (b"'", b'"', b";", b"!", b"(", b")")
diff --git a/.venv/lib/python3.12/site-packages/numpy/f2py/tests/test_regression.py b/.venv/lib/python3.12/site-packages/numpy/f2py/tests/test_regression.py
new file mode 100644
index 00000000..1c109783
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/numpy/f2py/tests/test_regression.py
@@ -0,0 +1,77 @@
+import os
+import pytest
+
+import numpy as np
+
+from . import util
+
+
+class TestIntentInOut(util.F2PyTest):
+    # Check that intent(in out) translates as intent(inout)
+    sources = [util.getpath("tests", "src", "regression", "inout.f90")]
+
+    @pytest.mark.slow
+    def test_inout(self):
+        # non-contiguous should raise error
+        x = np.arange(6, dtype=np.float32)[::2]
+        pytest.raises(ValueError, self.module.foo, x)
+
+        # check values with contiguous array
+        x = np.arange(3, dtype=np.float32)
+        self.module.foo(x)
+        assert np.allclose(x, [3, 1, 2])
+
+
+class TestNegativeBounds(util.F2PyTest):
+    # Check that negative bounds work correctly
+    sources = [util.getpath("tests", "src", "negative_bounds", "issue_20853.f90")]
+
+    @pytest.mark.slow
+    def test_negbound(self):
+        xvec = np.arange(12)
+        xlow = -6
+        xhigh = 4
+        # Calculate the upper bound,
+        # Keeping the 1 index in mind
+        def ubound(xl, xh):
+            return xh - xl + 1
+        rval = self.module.foo(is_=xlow, ie_=xhigh,
+                        arr=xvec[:ubound(xlow, xhigh)])
+        expval = np.arange(11, dtype = np.float32)
+        assert np.allclose(rval, expval)
+
+
+class TestNumpyVersionAttribute(util.F2PyTest):
+    # Check that th attribute __f2py_numpy_version__ is present
+    # in the compiled module and that has the value np.__version__.
+    sources = [util.getpath("tests", "src", "regression", "inout.f90")]
+
+    @pytest.mark.slow
+    def test_numpy_version_attribute(self):
+
+        # Check that self.module has an attribute named "__f2py_numpy_version__"
+        assert hasattr(self.module, "__f2py_numpy_version__")
+
+        # Check that the attribute __f2py_numpy_version__ is a string
+        assert isinstance(self.module.__f2py_numpy_version__, str)
+
+        # Check that __f2py_numpy_version__ has the value numpy.__version__
+        assert np.__version__ == self.module.__f2py_numpy_version__
+
+
+def test_include_path():
+    incdir = np.f2py.get_include()
+    fnames_in_dir = os.listdir(incdir)
+    for fname in ("fortranobject.c", "fortranobject.h"):
+        assert fname in fnames_in_dir
+
+
+class TestModuleAndSubroutine(util.F2PyTest):
+    module_name = "example"
+    sources = [util.getpath("tests", "src", "regression", "gh25337", "data.f90"),
+               util.getpath("tests", "src", "regression", "gh25337", "use_data.f90")]
+
+    @pytest.mark.slow
+    def test_gh25337(self):
+        self.module.data.set_shift(3)
+        assert "data" in dir(self.module)
diff --git a/.venv/lib/python3.12/site-packages/numpy/f2py/tests/test_return_character.py b/.venv/lib/python3.12/site-packages/numpy/f2py/tests/test_return_character.py
new file mode 100644
index 00000000..36c1f10f
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/numpy/f2py/tests/test_return_character.py
@@ -0,0 +1,45 @@
+import pytest
+
+from numpy import array
+from . import util
+import platform
+
+IS_S390X = platform.machine() == "s390x"
+
+
+class TestReturnCharacter(util.F2PyTest):
+    def check_function(self, t, tname):
+        if tname in ["t0", "t1", "s0", "s1"]:
+            assert t("23") == b"2"
+            r = t("ab")
+            assert r == b"a"
+            r = t(array("ab"))
+            assert r == b"a"
+            r = t(array(77, "u1"))
+            assert r == b"M"
+        elif tname in ["ts", "ss"]:
+            assert t(23) == b"23"
+            assert t("123456789abcdef") == b"123456789a"
+        elif tname in ["t5", "s5"]:
+            assert t(23) == b"23"
+            assert t("ab") == b"ab"
+            assert t("123456789abcdef") == b"12345"
+        else:
+            raise NotImplementedError
+
+
+class TestFReturnCharacter(TestReturnCharacter):
+    sources = [
+        util.getpath("tests", "src", "return_character", "foo77.f"),
+        util.getpath("tests", "src", "return_character", "foo90.f90"),
+    ]
+
+    @pytest.mark.xfail(IS_S390X, reason="callback returns ' '")
+    @pytest.mark.parametrize("name", "t0,t1,t5,s0,s1,s5,ss".split(","))
+    def test_all_f77(self, name):
+        self.check_function(getattr(self.module, name), name)
+
+    @pytest.mark.xfail(IS_S390X, reason="callback returns ' '")
+    @pytest.mark.parametrize("name", "t0,t1,t5,ts,s0,s1,s5,ss".split(","))
+    def test_all_f90(self, name):
+        self.check_function(getattr(self.module.f90_return_char, name), name)
diff --git a/.venv/lib/python3.12/site-packages/numpy/f2py/tests/test_return_complex.py b/.venv/lib/python3.12/site-packages/numpy/f2py/tests/test_return_complex.py
new file mode 100644
index 00000000..9df79632
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/numpy/f2py/tests/test_return_complex.py
@@ -0,0 +1,65 @@
+import pytest
+
+from numpy import array
+from . import util
+
+
+class TestReturnComplex(util.F2PyTest):
+    def check_function(self, t, tname):
+        if tname in ["t0", "t8", "s0", "s8"]:
+            err = 1e-5
+        else:
+            err = 0.0
+        assert abs(t(234j) - 234.0j) <= err
+        assert abs(t(234.6) - 234.6) <= err
+        assert abs(t(234) - 234.0) <= err
+        assert abs(t(234.6 + 3j) - (234.6 + 3j)) <= err
+        # assert abs(t('234')-234.)<=err
+        # assert abs(t('234.6')-234.6)<=err
+        assert abs(t(-234) + 234.0) <= err
+        assert abs(t([234]) - 234.0) <= err
+        assert abs(t((234, )) - 234.0) <= err
+        assert abs(t(array(234)) - 234.0) <= err
+        assert abs(t(array(23 + 4j, "F")) - (23 + 4j)) <= err
+        assert abs(t(array([234])) - 234.0) <= err
+        assert abs(t(array([[234]])) - 234.0) <= err
+        assert abs(t(array([234]).astype("b")) + 22.0) <= err
+        assert abs(t(array([234], "h")) - 234.0) <= err
+        assert abs(t(array([234], "i")) - 234.0) <= err
+        assert abs(t(array([234], "l")) - 234.0) <= err
+        assert abs(t(array([234], "q")) - 234.0) <= err
+        assert abs(t(array([234], "f")) - 234.0) <= err
+        assert abs(t(array([234], "d")) - 234.0) <= err
+        assert abs(t(array([234 + 3j], "F")) - (234 + 3j)) <= err
+        assert abs(t(array([234], "D")) - 234.0) <= err
+
+        # pytest.raises(TypeError, t, array([234], 'a1'))
+        pytest.raises(TypeError, t, "abc")
+
+        pytest.raises(IndexError, t, [])
+        pytest.raises(IndexError, t, ())
+
+        pytest.raises(TypeError, t, t)
+        pytest.raises(TypeError, t, {})
+
+        try:
+            r = t(10**400)
+            assert repr(r) in ["(inf+0j)", "(Infinity+0j)"]
+        except OverflowError:
+            pass
+
+
+class TestFReturnComplex(TestReturnComplex):
+    sources = [
+        util.getpath("tests", "src", "return_complex", "foo77.f"),
+        util.getpath("tests", "src", "return_complex", "foo90.f90"),
+    ]
+
+    @pytest.mark.parametrize("name", "t0,t8,t16,td,s0,s8,s16,sd".split(","))
+    def test_all_f77(self, name):
+        self.check_function(getattr(self.module, name), name)
+
+    @pytest.mark.parametrize("name", "t0,t8,t16,td,s0,s8,s16,sd".split(","))
+    def test_all_f90(self, name):
+        self.check_function(getattr(self.module.f90_return_complex, name),
+                            name)
diff --git a/.venv/lib/python3.12/site-packages/numpy/f2py/tests/test_return_integer.py b/.venv/lib/python3.12/site-packages/numpy/f2py/tests/test_return_integer.py
new file mode 100644
index 00000000..3b2f42e2
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/numpy/f2py/tests/test_return_integer.py
@@ -0,0 +1,53 @@
+import pytest
+
+from numpy import array
+from . import util
+
+
+class TestReturnInteger(util.F2PyTest):
+    def check_function(self, t, tname):
+        assert t(123) == 123
+        assert t(123.6) == 123
+        assert t("123") == 123
+        assert t(-123) == -123
+        assert t([123]) == 123
+        assert t((123, )) == 123
+        assert t(array(123)) == 123
+        assert t(array(123, "b")) == 123
+        assert t(array(123, "h")) == 123
+        assert t(array(123, "i")) == 123
+        assert t(array(123, "l")) == 123
+        assert t(array(123, "B")) == 123
+        assert t(array(123, "f")) == 123
+        assert t(array(123, "d")) == 123
+
+        # pytest.raises(ValueError, t, array([123],'S3'))
+        pytest.raises(ValueError, t, "abc")
+
+        pytest.raises(IndexError, t, [])
+        pytest.raises(IndexError, t, ())
+
+        pytest.raises(Exception, t, t)
+        pytest.raises(Exception, t, {})
+
+        if tname in ["t8", "s8"]:
+            pytest.raises(OverflowError, t, 100000000000000000000000)
+            pytest.raises(OverflowError, t, 10000000011111111111111.23)
+
+
+class TestFReturnInteger(TestReturnInteger):
+    sources = [
+        util.getpath("tests", "src", "return_integer", "foo77.f"),
+        util.getpath("tests", "src", "return_integer", "foo90.f90"),
+    ]
+
+    @pytest.mark.parametrize("name",
+                             "t0,t1,t2,t4,t8,s0,s1,s2,s4,s8".split(","))
+    def test_all_f77(self, name):
+        self.check_function(getattr(self.module, name), name)
+
+    @pytest.mark.parametrize("name",
+                             "t0,t1,t2,t4,t8,s0,s1,s2,s4,s8".split(","))
+    def test_all_f90(self, name):
+        self.check_function(getattr(self.module.f90_return_integer, name),
+                            name)
diff --git a/.venv/lib/python3.12/site-packages/numpy/f2py/tests/test_return_logical.py b/.venv/lib/python3.12/site-packages/numpy/f2py/tests/test_return_logical.py
new file mode 100644
index 00000000..92fb902a
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/numpy/f2py/tests/test_return_logical.py
@@ -0,0 +1,64 @@
+import pytest
+
+from numpy import array
+from . import util
+
+
+class TestReturnLogical(util.F2PyTest):
+    def check_function(self, t):
+        assert t(True) == 1
+        assert t(False) == 0
+        assert t(0) == 0
+        assert t(None) == 0
+        assert t(0.0) == 0
+        assert t(0j) == 0
+        assert t(1j) == 1
+        assert t(234) == 1
+        assert t(234.6) == 1
+        assert t(234.6 + 3j) == 1
+        assert t("234") == 1
+        assert t("aaa") == 1
+        assert t("") == 0
+        assert t([]) == 0
+        assert t(()) == 0
+        assert t({}) == 0
+        assert t(t) == 1
+        assert t(-234) == 1
+        assert t(10**100) == 1
+        assert t([234]) == 1
+        assert t((234, )) == 1
+        assert t(array(234)) == 1
+        assert t(array([234])) == 1
+        assert t(array([[234]])) == 1
+        assert t(array([127], "b")) == 1
+        assert t(array([234], "h")) == 1
+        assert t(array([234], "i")) == 1
+        assert t(array([234], "l")) == 1
+        assert t(array([234], "f")) == 1
+        assert t(array([234], "d")) == 1
+        assert t(array([234 + 3j], "F")) == 1
+        assert t(array([234], "D")) == 1
+        assert t(array(0)) == 0
+        assert t(array([0])) == 0
+        assert t(array([[0]])) == 0
+        assert t(array([0j])) == 0
+        assert t(array([1])) == 1
+        pytest.raises(ValueError, t, array([0, 0]))
+
+
+class TestFReturnLogical(TestReturnLogical):
+    sources = [
+        util.getpath("tests", "src", "return_logical", "foo77.f"),
+        util.getpath("tests", "src", "return_logical", "foo90.f90"),
+    ]
+
+    @pytest.mark.slow
+    @pytest.mark.parametrize("name", "t0,t1,t2,t4,s0,s1,s2,s4".split(","))
+    def test_all_f77(self, name):
+        self.check_function(getattr(self.module, name))
+
+    @pytest.mark.slow
+    @pytest.mark.parametrize("name",
+                             "t0,t1,t2,t4,t8,s0,s1,s2,s4,s8".split(","))
+    def test_all_f90(self, name):
+        self.check_function(getattr(self.module.f90_return_logical, name))
diff --git a/.venv/lib/python3.12/site-packages/numpy/f2py/tests/test_return_real.py b/.venv/lib/python3.12/site-packages/numpy/f2py/tests/test_return_real.py
new file mode 100644
index 00000000..a15d6475
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/numpy/f2py/tests/test_return_real.py
@@ -0,0 +1,107 @@
+import platform
+import pytest
+import numpy as np
+
+from numpy import array
+from . import util
+
+
+class TestReturnReal(util.F2PyTest):
+    def check_function(self, t, tname):
+        if tname in ["t0", "t4", "s0", "s4"]:
+            err = 1e-5
+        else:
+            err = 0.0
+        assert abs(t(234) - 234.0) <= err
+        assert abs(t(234.6) - 234.6) <= err
+        assert abs(t("234") - 234) <= err
+        assert abs(t("234.6") - 234.6) <= err
+        assert abs(t(-234) + 234) <= err
+        assert abs(t([234]) - 234) <= err
+        assert abs(t((234, )) - 234.0) <= err
+        assert abs(t(array(234)) - 234.0) <= err
+        assert abs(t(array(234).astype("b")) + 22) <= err
+        assert abs(t(array(234, "h")) - 234.0) <= err
+        assert abs(t(array(234, "i")) - 234.0) <= err
+        assert abs(t(array(234, "l")) - 234.0) <= err
+        assert abs(t(array(234, "B")) - 234.0) <= err
+        assert abs(t(array(234, "f")) - 234.0) <= err
+        assert abs(t(array(234, "d")) - 234.0) <= err
+        if tname in ["t0", "t4", "s0", "s4"]:
+            assert t(1e200) == t(1e300)  # inf
+
+        # pytest.raises(ValueError, t, array([234], 'S1'))
+        pytest.raises(ValueError, t, "abc")
+
+        pytest.raises(IndexError, t, [])
+        pytest.raises(IndexError, t, ())
+
+        pytest.raises(Exception, t, t)
+        pytest.raises(Exception, t, {})
+
+        try:
+            r = t(10**400)
+            assert repr(r) in ["inf", "Infinity"]
+        except OverflowError:
+            pass
+
+
+@pytest.mark.skipif(
+    platform.system() == "Darwin",
+    reason="Prone to error when run with numpy/f2py/tests on mac os, "
+    "but not when run in isolation",
+)
+@pytest.mark.skipif(
+    np.dtype(np.intp).itemsize < 8,
+    reason="32-bit builds are buggy"
+)
+class TestCReturnReal(TestReturnReal):
+    suffix = ".pyf"
+    module_name = "c_ext_return_real"
+    code = """
+python module c_ext_return_real
+usercode \'\'\'
+float t4(float value) { return value; }
+void s4(float *t4, float value) { *t4 = value; }
+double t8(double value) { return value; }
+void s8(double *t8, double value) { *t8 = value; }
+\'\'\'
+interface
+  function t4(value)
+    real*4 intent(c) :: t4,value
+  end
+  function t8(value)
+    real*8 intent(c) :: t8,value
+  end
+  subroutine s4(t4,value)
+    intent(c) s4
+    real*4 intent(out) :: t4
+    real*4 intent(c) :: value
+  end
+  subroutine s8(t8,value)
+    intent(c) s8
+    real*8 intent(out) :: t8
+    real*8 intent(c) :: value
+  end
+end interface
+end python module c_ext_return_real
+    """
+
+    @pytest.mark.parametrize("name", "t4,t8,s4,s8".split(","))
+    def test_all(self, name):
+        self.check_function(getattr(self.module, name), name)
+
+
+class TestFReturnReal(TestReturnReal):
+    sources = [
+        util.getpath("tests", "src", "return_real", "foo77.f"),
+        util.getpath("tests", "src", "return_real", "foo90.f90"),
+    ]
+
+    @pytest.mark.parametrize("name", "t0,t4,t8,td,s0,s4,s8,sd".split(","))
+    def test_all_f77(self, name):
+        self.check_function(getattr(self.module, name), name)
+
+    @pytest.mark.parametrize("name", "t0,t4,t8,td,s0,s4,s8,sd".split(","))
+    def test_all_f90(self, name):
+        self.check_function(getattr(self.module.f90_return_real, name), name)
diff --git a/.venv/lib/python3.12/site-packages/numpy/f2py/tests/test_semicolon_split.py b/.venv/lib/python3.12/site-packages/numpy/f2py/tests/test_semicolon_split.py
new file mode 100644
index 00000000..6d499046
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/numpy/f2py/tests/test_semicolon_split.py
@@ -0,0 +1,74 @@
+import platform
+import pytest
+import numpy as np
+
+from . import util
+
+
+@pytest.mark.skipif(
+    platform.system() == "Darwin",
+    reason="Prone to error when run with numpy/f2py/tests on mac os, "
+    "but not when run in isolation",
+)
+@pytest.mark.skipif(
+    np.dtype(np.intp).itemsize < 8,
+    reason="32-bit builds are buggy"
+)
+class TestMultiline(util.F2PyTest):
+    suffix = ".pyf"
+    module_name = "multiline"
+    code = f"""
+python module {module_name}
+    usercode '''
+void foo(int* x) {{
+    char dummy = ';';
+    *x = 42;
+}}
+'''
+    interface
+        subroutine foo(x)
+            intent(c) foo
+            integer intent(out) :: x
+        end subroutine foo
+    end interface
+end python module {module_name}
+    """
+
+    def test_multiline(self):
+        assert self.module.foo() == 42
+
+
+@pytest.mark.skipif(
+    platform.system() == "Darwin",
+    reason="Prone to error when run with numpy/f2py/tests on mac os, "
+    "but not when run in isolation",
+)
+@pytest.mark.skipif(
+    np.dtype(np.intp).itemsize < 8,
+    reason="32-bit builds are buggy"
+)
+class TestCallstatement(util.F2PyTest):
+    suffix = ".pyf"
+    module_name = "callstatement"
+    code = f"""
+python module {module_name}
+    usercode '''
+void foo(int* x) {{
+}}
+'''
+    interface
+        subroutine foo(x)
+            intent(c) foo
+            integer intent(out) :: x
+            callprotoargument int*
+            callstatement {{ &
+                ; &
+                x = 42; &
+            }}
+        end subroutine foo
+    end interface
+end python module {module_name}
+    """
+
+    def test_callstatement(self):
+        assert self.module.foo() == 42
diff --git a/.venv/lib/python3.12/site-packages/numpy/f2py/tests/test_size.py b/.venv/lib/python3.12/site-packages/numpy/f2py/tests/test_size.py
new file mode 100644
index 00000000..bd2c349d
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/numpy/f2py/tests/test_size.py
@@ -0,0 +1,45 @@
+import os
+import pytest
+import numpy as np
+
+from . import util
+
+
+class TestSizeSumExample(util.F2PyTest):
+    sources = [util.getpath("tests", "src", "size", "foo.f90")]
+
+    @pytest.mark.slow
+    def test_all(self):
+        r = self.module.foo([[]])
+        assert r == [0]
+
+        r = self.module.foo([[1, 2]])
+        assert r == [3]
+
+        r = self.module.foo([[1, 2], [3, 4]])
+        assert np.allclose(r, [3, 7])
+
+        r = self.module.foo([[1, 2], [3, 4], [5, 6]])
+        assert np.allclose(r, [3, 7, 11])
+
+    @pytest.mark.slow
+    def test_transpose(self):
+        r = self.module.trans([[]])
+        assert np.allclose(r.T, np.array([[]]))
+
+        r = self.module.trans([[1, 2]])
+        assert np.allclose(r, [[1.], [2.]])
+
+        r = self.module.trans([[1, 2, 3], [4, 5, 6]])
+        assert np.allclose(r, [[1, 4], [2, 5], [3, 6]])
+
+    @pytest.mark.slow
+    def test_flatten(self):
+        r = self.module.flatten([[]])
+        assert np.allclose(r, [])
+
+        r = self.module.flatten([[1, 2]])
+        assert np.allclose(r, [1, 2])
+
+        r = self.module.flatten([[1, 2, 3], [4, 5, 6]])
+        assert np.allclose(r, [1, 2, 3, 4, 5, 6])
diff --git a/.venv/lib/python3.12/site-packages/numpy/f2py/tests/test_string.py b/.venv/lib/python3.12/site-packages/numpy/f2py/tests/test_string.py
new file mode 100644
index 00000000..9e937188
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/numpy/f2py/tests/test_string.py
@@ -0,0 +1,100 @@
+import os
+import pytest
+import textwrap
+import numpy as np
+from . import util
+
+
+class TestString(util.F2PyTest):
+    sources = [util.getpath("tests", "src", "string", "char.f90")]
+
+    @pytest.mark.slow
+    def test_char(self):
+        strings = np.array(["ab", "cd", "ef"], dtype="c").T
+        inp, out = self.module.char_test.change_strings(
+            strings, strings.shape[1])
+        assert inp == pytest.approx(strings)
+        expected = strings.copy()
+        expected[1, :] = "AAA"
+        assert out == pytest.approx(expected)
+
+
+class TestDocStringArguments(util.F2PyTest):
+    sources = [util.getpath("tests", "src", "string", "string.f")]
+
+    def test_example(self):
+        a = np.array(b"123\0\0")
+        b = np.array(b"123\0\0")
+        c = np.array(b"123")
+        d = np.array(b"123")
+
+        self.module.foo(a, b, c, d)
+
+        assert a.tobytes() == b"123\0\0"
+        assert b.tobytes() == b"B23\0\0"
+        assert c.tobytes() == b"123"
+        assert d.tobytes() == b"D23"
+
+
+class TestFixedString(util.F2PyTest):
+    sources = [util.getpath("tests", "src", "string", "fixed_string.f90")]
+
+    @staticmethod
+    def _sint(s, start=0, end=None):
+        """Return the content of a string buffer as integer value.
+
+        For example:
+          _sint('1234') -> 4321
+          _sint('123A') -> 17321
+        """
+        if isinstance(s, np.ndarray):
+            s = s.tobytes()
+        elif isinstance(s, str):
+            s = s.encode()
+        assert isinstance(s, bytes)
+        if end is None:
+            end = len(s)
+        i = 0
+        for j in range(start, min(end, len(s))):
+            i += s[j] * 10**j
+        return i
+
+    def _get_input(self, intent="in"):
+        if intent in ["in"]:
+            yield ""
+            yield "1"
+            yield "1234"
+            yield "12345"
+            yield b""
+            yield b"\0"
+            yield b"1"
+            yield b"\01"
+            yield b"1\0"
+            yield b"1234"
+            yield b"12345"
+        yield np.ndarray((), np.bytes_, buffer=b"")  # array(b'', dtype='|S0')
+        yield np.array(b"")  # array(b'', dtype='|S1')
+        yield np.array(b"\0")
+        yield np.array(b"1")
+        yield np.array(b"1\0")
+        yield np.array(b"\01")
+        yield np.array(b"1234")
+        yield np.array(b"123\0")
+        yield np.array(b"12345")
+
+    def test_intent_in(self):
+        for s in self._get_input():
+            r = self.module.test_in_bytes4(s)
+            # also checks that s is not changed inplace
+            expected = self._sint(s, end=4)
+            assert r == expected, s
+
+    def test_intent_inout(self):
+        for s in self._get_input(intent="inout"):
+            rest = self._sint(s, start=4)
+            r = self.module.test_inout_bytes4(s)
+            expected = self._sint(s, end=4)
+            assert r == expected
+
+            # check that the rest of input string is preserved
+            assert rest == self._sint(s, start=4)
diff --git a/.venv/lib/python3.12/site-packages/numpy/f2py/tests/test_symbolic.py b/.venv/lib/python3.12/site-packages/numpy/f2py/tests/test_symbolic.py
new file mode 100644
index 00000000..84527831
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/numpy/f2py/tests/test_symbolic.py
@@ -0,0 +1,494 @@
+import pytest
+
+from numpy.f2py.symbolic import (
+    Expr,
+    Op,
+    ArithOp,
+    Language,
+    as_symbol,
+    as_number,
+    as_string,
+    as_array,
+    as_complex,
+    as_terms,
+    as_factors,
+    eliminate_quotes,
+    insert_quotes,
+    fromstring,
+    as_expr,
+    as_apply,
+    as_numer_denom,
+    as_ternary,
+    as_ref,
+    as_deref,
+    normalize,
+    as_eq,
+    as_ne,
+    as_lt,
+    as_gt,
+    as_le,
+    as_ge,
+)
+from . import util
+
+
+class TestSymbolic(util.F2PyTest):
+    def test_eliminate_quotes(self):
+        def worker(s):
+            r, d = eliminate_quotes(s)
+            s1 = insert_quotes(r, d)
+            assert s1 == s
+
+        for kind in ["", "mykind_"]:
+            worker(kind + '"1234" // "ABCD"')
+            worker(kind + '"1234" // ' + kind + '"ABCD"')
+            worker(kind + "\"1234\" // 'ABCD'")
+            worker(kind + '"1234" // ' + kind + "'ABCD'")
+            worker(kind + '"1\\"2\'AB\'34"')
+            worker("a = " + kind + "'1\\'2\"AB\"34'")
+
+    def test_sanity(self):
+        x = as_symbol("x")
+        y = as_symbol("y")
+        z = as_symbol("z")
+
+        assert x.op == Op.SYMBOL
+        assert repr(x) == "Expr(Op.SYMBOL, 'x')"
+        assert x == x
+        assert x != y
+        assert hash(x) is not None
+
+        n = as_number(123)
+        m = as_number(456)
+        assert n.op == Op.INTEGER
+        assert repr(n) == "Expr(Op.INTEGER, (123, 4))"
+        assert n == n
+        assert n != m
+        assert hash(n) is not None
+
+        fn = as_number(12.3)
+        fm = as_number(45.6)
+        assert fn.op == Op.REAL
+        assert repr(fn) == "Expr(Op.REAL, (12.3, 4))"
+        assert fn == fn
+        assert fn != fm
+        assert hash(fn) is not None
+
+        c = as_complex(1, 2)
+        c2 = as_complex(3, 4)
+        assert c.op == Op.COMPLEX
+        assert repr(c) == ("Expr(Op.COMPLEX, (Expr(Op.INTEGER, (1, 4)),"
+                           " Expr(Op.INTEGER, (2, 4))))")
+        assert c == c
+        assert c != c2
+        assert hash(c) is not None
+
+        s = as_string("'123'")
+        s2 = as_string('"ABC"')
+        assert s.op == Op.STRING
+        assert repr(s) == "Expr(Op.STRING, (\"'123'\", 1))", repr(s)
+        assert s == s
+        assert s != s2
+
+        a = as_array((n, m))
+        b = as_array((n, ))
+        assert a.op == Op.ARRAY
+        assert repr(a) == ("Expr(Op.ARRAY, (Expr(Op.INTEGER, (123, 4)),"
+                           " Expr(Op.INTEGER, (456, 4))))")
+        assert a == a
+        assert a != b
+
+        t = as_terms(x)
+        u = as_terms(y)
+        assert t.op == Op.TERMS
+        assert repr(t) == "Expr(Op.TERMS, {Expr(Op.SYMBOL, 'x'): 1})"
+        assert t == t
+        assert t != u
+        assert hash(t) is not None
+
+        v = as_factors(x)
+        w = as_factors(y)
+        assert v.op == Op.FACTORS
+        assert repr(v) == "Expr(Op.FACTORS, {Expr(Op.SYMBOL, 'x'): 1})"
+        assert v == v
+        assert w != v
+        assert hash(v) is not None
+
+        t = as_ternary(x, y, z)
+        u = as_ternary(x, z, y)
+        assert t.op == Op.TERNARY
+        assert t == t
+        assert t != u
+        assert hash(t) is not None
+
+        e = as_eq(x, y)
+        f = as_lt(x, y)
+        assert e.op == Op.RELATIONAL
+        assert e == e
+        assert e != f
+        assert hash(e) is not None
+
+    def test_tostring_fortran(self):
+        x = as_symbol("x")
+        y = as_symbol("y")
+        z = as_symbol("z")
+        n = as_number(123)
+        m = as_number(456)
+        a = as_array((n, m))
+        c = as_complex(n, m)
+
+        assert str(x) == "x"
+        assert str(n) == "123"
+        assert str(a) == "[123, 456]"
+        assert str(c) == "(123, 456)"
+
+        assert str(Expr(Op.TERMS, {x: 1})) == "x"
+        assert str(Expr(Op.TERMS, {x: 2})) == "2 * x"
+        assert str(Expr(Op.TERMS, {x: -1})) == "-x"
+        assert str(Expr(Op.TERMS, {x: -2})) == "-2 * x"
+        assert str(Expr(Op.TERMS, {x: 1, y: 1})) == "x + y"
+        assert str(Expr(Op.TERMS, {x: -1, y: -1})) == "-x - y"
+        assert str(Expr(Op.TERMS, {x: 2, y: 3})) == "2 * x + 3 * y"
+        assert str(Expr(Op.TERMS, {x: -2, y: 3})) == "-2 * x + 3 * y"
+        assert str(Expr(Op.TERMS, {x: 2, y: -3})) == "2 * x - 3 * y"
+
+        assert str(Expr(Op.FACTORS, {x: 1})) == "x"
+        assert str(Expr(Op.FACTORS, {x: 2})) == "x ** 2"
+        assert str(Expr(Op.FACTORS, {x: -1})) == "x ** -1"
+        assert str(Expr(Op.FACTORS, {x: -2})) == "x ** -2"
+        assert str(Expr(Op.FACTORS, {x: 1, y: 1})) == "x * y"
+        assert str(Expr(Op.FACTORS, {x: 2, y: 3})) == "x ** 2 * y ** 3"
+
+        v = Expr(Op.FACTORS, {x: 2, Expr(Op.TERMS, {x: 1, y: 1}): 3})
+        assert str(v) == "x ** 2 * (x + y) ** 3", str(v)
+        v = Expr(Op.FACTORS, {x: 2, Expr(Op.FACTORS, {x: 1, y: 1}): 3})
+        assert str(v) == "x ** 2 * (x * y) ** 3", str(v)
+
+        assert str(Expr(Op.APPLY, ("f", (), {}))) == "f()"
+        assert str(Expr(Op.APPLY, ("f", (x, ), {}))) == "f(x)"
+        assert str(Expr(Op.APPLY, ("f", (x, y), {}))) == "f(x, y)"
+        assert str(Expr(Op.INDEXING, ("f", x))) == "f[x]"
+
+        assert str(as_ternary(x, y, z)) == "merge(y, z, x)"
+        assert str(as_eq(x, y)) == "x .eq. y"
+        assert str(as_ne(x, y)) == "x .ne. y"
+        assert str(as_lt(x, y)) == "x .lt. y"
+        assert str(as_le(x, y)) == "x .le. y"
+        assert str(as_gt(x, y)) == "x .gt. y"
+        assert str(as_ge(x, y)) == "x .ge. y"
+
+    def test_tostring_c(self):
+        language = Language.C
+        x = as_symbol("x")
+        y = as_symbol("y")
+        z = as_symbol("z")
+        n = as_number(123)
+
+        assert Expr(Op.FACTORS, {x: 2}).tostring(language=language) == "x * x"
+        assert (Expr(Op.FACTORS, {
+            x + y: 2
+        }).tostring(language=language) == "(x + y) * (x + y)")
+        assert Expr(Op.FACTORS, {
+            x: 12
+        }).tostring(language=language) == "pow(x, 12)"
+
+        assert as_apply(ArithOp.DIV, x,
+                        y).tostring(language=language) == "x / y"
+        assert (as_apply(ArithOp.DIV, x,
+                         x + y).tostring(language=language) == "x / (x + y)")
+        assert (as_apply(ArithOp.DIV, x - y, x +
+                         y).tostring(language=language) == "(x - y) / (x + y)")
+        assert (x + (x - y) / (x + y) +
+                n).tostring(language=language) == "123 + x + (x - y) / (x + y)"
+
+        assert as_ternary(x, y, z).tostring(language=language) == "(x?y:z)"
+        assert as_eq(x, y).tostring(language=language) == "x == y"
+        assert as_ne(x, y).tostring(language=language) == "x != y"
+        assert as_lt(x, y).tostring(language=language) == "x < y"
+        assert as_le(x, y).tostring(language=language) == "x <= y"
+        assert as_gt(x, y).tostring(language=language) == "x > y"
+        assert as_ge(x, y).tostring(language=language) == "x >= y"
+
+    def test_operations(self):
+        x = as_symbol("x")
+        y = as_symbol("y")
+        z = as_symbol("z")
+
+        assert x + x == Expr(Op.TERMS, {x: 2})
+        assert x - x == Expr(Op.INTEGER, (0, 4))
+        assert x + y == Expr(Op.TERMS, {x: 1, y: 1})
+        assert x - y == Expr(Op.TERMS, {x: 1, y: -1})
+        assert x * x == Expr(Op.FACTORS, {x: 2})
+        assert x * y == Expr(Op.FACTORS, {x: 1, y: 1})
+
+        assert +x == x
+        assert -x == Expr(Op.TERMS, {x: -1}), repr(-x)
+        assert 2 * x == Expr(Op.TERMS, {x: 2})
+        assert 2 + x == Expr(Op.TERMS, {x: 1, as_number(1): 2})
+        assert 2 * x + 3 * y == Expr(Op.TERMS, {x: 2, y: 3})
+        assert (x + y) * 2 == Expr(Op.TERMS, {x: 2, y: 2})
+
+        assert x**2 == Expr(Op.FACTORS, {x: 2})
+        assert (x + y)**2 == Expr(
+            Op.TERMS,
+            {
+                Expr(Op.FACTORS, {x: 2}): 1,
+                Expr(Op.FACTORS, {y: 2}): 1,
+                Expr(Op.FACTORS, {
+                    x: 1,
+                    y: 1
+                }): 2,
+            },
+        )
+        assert (x + y) * x == x**2 + x * y
+        assert (x + y)**2 == x**2 + 2 * x * y + y**2
+        assert (x + y)**2 + (x - y)**2 == 2 * x**2 + 2 * y**2
+        assert (x + y) * z == x * z + y * z
+        assert z * (x + y) == x * z + y * z
+
+        assert (x / 2) == as_apply(ArithOp.DIV, x, as_number(2))
+        assert (2 * x / 2) == x
+        assert (3 * x / 2) == as_apply(ArithOp.DIV, 3 * x, as_number(2))
+        assert (4 * x / 2) == 2 * x
+        assert (5 * x / 2) == as_apply(ArithOp.DIV, 5 * x, as_number(2))
+        assert (6 * x / 2) == 3 * x
+        assert ((3 * 5) * x / 6) == as_apply(ArithOp.DIV, 5 * x, as_number(2))
+        assert (30 * x**2 * y**4 / (24 * x**3 * y**3)) == as_apply(
+            ArithOp.DIV, 5 * y, 4 * x)
+        assert ((15 * x / 6) / 5) == as_apply(ArithOp.DIV, x,
+                                              as_number(2)), (15 * x / 6) / 5
+        assert (x / (5 / x)) == as_apply(ArithOp.DIV, x**2, as_number(5))
+
+        assert (x / 2.0) == Expr(Op.TERMS, {x: 0.5})
+
+        s = as_string('"ABC"')
+        t = as_string('"123"')
+
+        assert s // t == Expr(Op.STRING, ('"ABC123"', 1))
+        assert s // x == Expr(Op.CONCAT, (s, x))
+        assert x // s == Expr(Op.CONCAT, (x, s))
+
+        c = as_complex(1.0, 2.0)
+        assert -c == as_complex(-1.0, -2.0)
+        assert c + c == as_expr((1 + 2j) * 2)
+        assert c * c == as_expr((1 + 2j)**2)
+
+    def test_substitute(self):
+        x = as_symbol("x")
+        y = as_symbol("y")
+        z = as_symbol("z")
+        a = as_array((x, y))
+
+        assert x.substitute({x: y}) == y
+        assert (x + y).substitute({x: z}) == y + z
+        assert (x * y).substitute({x: z}) == y * z
+        assert (x**4).substitute({x: z}) == z**4
+        assert (x / y).substitute({x: z}) == z / y
+        assert x.substitute({x: y + z}) == y + z
+        assert a.substitute({x: y + z}) == as_array((y + z, y))
+
+        assert as_ternary(x, y,
+                          z).substitute({x: y + z}) == as_ternary(y + z, y, z)
+        assert as_eq(x, y).substitute({x: y + z}) == as_eq(y + z, y)
+
+    def test_fromstring(self):
+
+        x = as_symbol("x")
+        y = as_symbol("y")
+        z = as_symbol("z")
+        f = as_symbol("f")
+        s = as_string('"ABC"')
+        t = as_string('"123"')
+        a = as_array((x, y))
+
+        assert fromstring("x") == x
+        assert fromstring("+ x") == x
+        assert fromstring("-  x") == -x
+        assert fromstring("x + y") == x + y
+        assert fromstring("x + 1") == x + 1
+        assert fromstring("x * y") == x * y
+        assert fromstring("x * 2") == x * 2
+        assert fromstring("x / y") == x / y
+        assert fromstring("x ** 2", language=Language.Python) == x**2
+        assert fromstring("x ** 2 ** 3", language=Language.Python) == x**2**3
+        assert fromstring("(x + y) * z") == (x + y) * z
+
+        assert fromstring("f(x)") == f(x)
+        assert fromstring("f(x,y)") == f(x, y)
+        assert fromstring("f[x]") == f[x]
+        assert fromstring("f[x][y]") == f[x][y]
+
+        assert fromstring('"ABC"') == s
+        assert (normalize(
+            fromstring('"ABC" // "123" ',
+                       language=Language.Fortran)) == s // t)
+        assert fromstring('f("ABC")') == f(s)
+        assert fromstring('MYSTRKIND_"ABC"') == as_string('"ABC"', "MYSTRKIND")
+
+        assert fromstring("(/x, y/)") == a, fromstring("(/x, y/)")
+        assert fromstring("f((/x, y/))") == f(a)
+        assert fromstring("(/(x+y)*z/)") == as_array(((x + y) * z, ))
+
+        assert fromstring("123") == as_number(123)
+        assert fromstring("123_2") == as_number(123, 2)
+        assert fromstring("123_myintkind") == as_number(123, "myintkind")
+
+        assert fromstring("123.0") == as_number(123.0, 4)
+        assert fromstring("123.0_4") == as_number(123.0, 4)
+        assert fromstring("123.0_8") == as_number(123.0, 8)
+        assert fromstring("123.0e0") == as_number(123.0, 4)
+        assert fromstring("123.0d0") == as_number(123.0, 8)
+        assert fromstring("123d0") == as_number(123.0, 8)
+        assert fromstring("123e-0") == as_number(123.0, 4)
+        assert fromstring("123d+0") == as_number(123.0, 8)
+        assert fromstring("123.0_myrealkind") == as_number(123.0, "myrealkind")
+        assert fromstring("3E4") == as_number(30000.0, 4)
+
+        assert fromstring("(1, 2)") == as_complex(1, 2)
+        assert fromstring("(1e2, PI)") == as_complex(as_number(100.0),
+                                                     as_symbol("PI"))
+
+        assert fromstring("[1, 2]") == as_array((as_number(1), as_number(2)))
+
+        assert fromstring("POINT(x, y=1)") == as_apply(as_symbol("POINT"),
+                                                       x,
+                                                       y=as_number(1))
+        assert fromstring(
+            'PERSON(name="John", age=50, shape=(/34, 23/))') == as_apply(
+                as_symbol("PERSON"),
+                name=as_string('"John"'),
+                age=as_number(50),
+                shape=as_array((as_number(34), as_number(23))),
+            )
+
+        assert fromstring("x?y:z") == as_ternary(x, y, z)
+
+        assert fromstring("*x") == as_deref(x)
+        assert fromstring("**x") == as_deref(as_deref(x))
+        assert fromstring("&x") == as_ref(x)
+        assert fromstring("(*x) * (*y)") == as_deref(x) * as_deref(y)
+        assert fromstring("(*x) * *y") == as_deref(x) * as_deref(y)
+        assert fromstring("*x * *y") == as_deref(x) * as_deref(y)
+        assert fromstring("*x**y") == as_deref(x) * as_deref(y)
+
+        assert fromstring("x == y") == as_eq(x, y)
+        assert fromstring("x != y") == as_ne(x, y)
+        assert fromstring("x < y") == as_lt(x, y)
+        assert fromstring("x > y") == as_gt(x, y)
+        assert fromstring("x <= y") == as_le(x, y)
+        assert fromstring("x >= y") == as_ge(x, y)
+
+        assert fromstring("x .eq. y", language=Language.Fortran) == as_eq(x, y)
+        assert fromstring("x .ne. y", language=Language.Fortran) == as_ne(x, y)
+        assert fromstring("x .lt. y", language=Language.Fortran) == as_lt(x, y)
+        assert fromstring("x .gt. y", language=Language.Fortran) == as_gt(x, y)
+        assert fromstring("x .le. y", language=Language.Fortran) == as_le(x, y)
+        assert fromstring("x .ge. y", language=Language.Fortran) == as_ge(x, y)
+
+    def test_traverse(self):
+        x = as_symbol("x")
+        y = as_symbol("y")
+        z = as_symbol("z")
+        f = as_symbol("f")
+
+        # Use traverse to substitute a symbol
+        def replace_visit(s, r=z):
+            if s == x:
+                return r
+
+        assert x.traverse(replace_visit) == z
+        assert y.traverse(replace_visit) == y
+        assert z.traverse(replace_visit) == z
+        assert (f(y)).traverse(replace_visit) == f(y)
+        assert (f(x)).traverse(replace_visit) == f(z)
+        assert (f[y]).traverse(replace_visit) == f[y]
+        assert (f[z]).traverse(replace_visit) == f[z]
+        assert (x + y + z).traverse(replace_visit) == (2 * z + y)
+        assert (x +
+                f(y, x - z)).traverse(replace_visit) == (z +
+                                                         f(y, as_number(0)))
+        assert as_eq(x, y).traverse(replace_visit) == as_eq(z, y)
+
+        # Use traverse to collect symbols, method 1
+        function_symbols = set()
+        symbols = set()
+
+        def collect_symbols(s):
+            if s.op is Op.APPLY:
+                oper = s.data[0]
+                function_symbols.add(oper)
+                if oper in symbols:
+                    symbols.remove(oper)
+            elif s.op is Op.SYMBOL and s not in function_symbols:
+                symbols.add(s)
+
+        (x + f(y, x - z)).traverse(collect_symbols)
+        assert function_symbols == {f}
+        assert symbols == {x, y, z}
+
+        # Use traverse to collect symbols, method 2
+        def collect_symbols2(expr, symbols):
+            if expr.op is Op.SYMBOL:
+                symbols.add(expr)
+
+        symbols = set()
+        (x + f(y, x - z)).traverse(collect_symbols2, symbols)
+        assert symbols == {x, y, z, f}
+
+        # Use traverse to partially collect symbols
+        def collect_symbols3(expr, symbols):
+            if expr.op is Op.APPLY:
+                # skip traversing function calls
+                return expr
+            if expr.op is Op.SYMBOL:
+                symbols.add(expr)
+
+        symbols = set()
+        (x + f(y, x - z)).traverse(collect_symbols3, symbols)
+        assert symbols == {x}
+
+    def test_linear_solve(self):
+        x = as_symbol("x")
+        y = as_symbol("y")
+        z = as_symbol("z")
+
+        assert x.linear_solve(x) == (as_number(1), as_number(0))
+        assert (x + 1).linear_solve(x) == (as_number(1), as_number(1))
+        assert (2 * x).linear_solve(x) == (as_number(2), as_number(0))
+        assert (2 * x + 3).linear_solve(x) == (as_number(2), as_number(3))
+        assert as_number(3).linear_solve(x) == (as_number(0), as_number(3))
+        assert y.linear_solve(x) == (as_number(0), y)
+        assert (y * z).linear_solve(x) == (as_number(0), y * z)
+
+        assert (x + y).linear_solve(x) == (as_number(1), y)
+        assert (z * x + y).linear_solve(x) == (z, y)
+        assert ((z + y) * x + y).linear_solve(x) == (z + y, y)
+        assert (z * y * x + y).linear_solve(x) == (z * y, y)
+
+        pytest.raises(RuntimeError, lambda: (x * x).linear_solve(x))
+
+    def test_as_numer_denom(self):
+        x = as_symbol("x")
+        y = as_symbol("y")
+        n = as_number(123)
+
+        assert as_numer_denom(x) == (x, as_number(1))
+        assert as_numer_denom(x / n) == (x, n)
+        assert as_numer_denom(n / x) == (n, x)
+        assert as_numer_denom(x / y) == (x, y)
+        assert as_numer_denom(x * y) == (x * y, as_number(1))
+        assert as_numer_denom(n + x / y) == (x + n * y, y)
+        assert as_numer_denom(n + x / (y - x / n)) == (y * n**2, y * n - x)
+
+    def test_polynomial_atoms(self):
+        x = as_symbol("x")
+        y = as_symbol("y")
+        n = as_number(123)
+
+        assert x.polynomial_atoms() == {x}
+        assert n.polynomial_atoms() == set()
+        assert (y[x]).polynomial_atoms() == {y[x]}
+        assert (y(x)).polynomial_atoms() == {y(x)}
+        assert (y(x) + x).polynomial_atoms() == {y(x), x}
+        assert (y(x) * x[y]).polynomial_atoms() == {y(x), x[y]}
+        assert (y(x)**x).polynomial_atoms() == {y(x)}
diff --git a/.venv/lib/python3.12/site-packages/numpy/f2py/tests/test_value_attrspec.py b/.venv/lib/python3.12/site-packages/numpy/f2py/tests/test_value_attrspec.py
new file mode 100644
index 00000000..83aaf6c9
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/numpy/f2py/tests/test_value_attrspec.py
@@ -0,0 +1,14 @@
+import os
+import pytest
+
+from . import util
+
+class TestValueAttr(util.F2PyTest):
+    sources = [util.getpath("tests", "src", "value_attrspec", "gh21665.f90")]
+
+    # gh-21665
+    def test_long_long_map(self):
+        inp = 2
+        out = self.module.fortfuncs.square(inp)
+        exp_out = 4
+        assert out == exp_out
diff --git a/.venv/lib/python3.12/site-packages/numpy/f2py/tests/util.py b/.venv/lib/python3.12/site-packages/numpy/f2py/tests/util.py
new file mode 100644
index 00000000..6ed6c085
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/numpy/f2py/tests/util.py
@@ -0,0 +1,440 @@
+"""
+Utility functions for
+
+- building and importing modules on test time, using a temporary location
+- detecting if compilers are present
+- determining paths to tests
+
+"""
+import glob
+import os
+import sys
+import subprocess
+import tempfile
+import shutil
+import atexit
+import textwrap
+import re
+import pytest
+import contextlib
+import numpy
+
+from pathlib import Path
+from numpy.compat import asstr
+from numpy._utils import asunicode
+from numpy.testing import temppath, IS_WASM
+from importlib import import_module
+
+#
+# Maintaining a temporary module directory
+#
+
+_module_dir = None
+_module_num = 5403
+
+if sys.platform == "cygwin":
+    NUMPY_INSTALL_ROOT = Path(__file__).parent.parent.parent
+    _module_list = list(NUMPY_INSTALL_ROOT.glob("**/*.dll"))
+
+
+def _cleanup():
+    global _module_dir
+    if _module_dir is not None:
+        try:
+            sys.path.remove(_module_dir)
+        except ValueError:
+            pass
+        try:
+            shutil.rmtree(_module_dir)
+        except OSError:
+            pass
+        _module_dir = None
+
+
+def get_module_dir():
+    global _module_dir
+    if _module_dir is None:
+        _module_dir = tempfile.mkdtemp()
+        atexit.register(_cleanup)
+        if _module_dir not in sys.path:
+            sys.path.insert(0, _module_dir)
+    return _module_dir
+
+
+def get_temp_module_name():
+    # Assume single-threaded, and the module dir usable only by this thread
+    global _module_num
+    get_module_dir()
+    name = "_test_ext_module_%d" % _module_num
+    _module_num += 1
+    if name in sys.modules:
+        # this should not be possible, but check anyway
+        raise RuntimeError("Temporary module name already in use.")
+    return name
+
+
+def _memoize(func):
+    memo = {}
+
+    def wrapper(*a, **kw):
+        key = repr((a, kw))
+        if key not in memo:
+            try:
+                memo[key] = func(*a, **kw)
+            except Exception as e:
+                memo[key] = e
+                raise
+        ret = memo[key]
+        if isinstance(ret, Exception):
+            raise ret
+        return ret
+
+    wrapper.__name__ = func.__name__
+    return wrapper
+
+
+#
+# Building modules
+#
+
+
+@_memoize
+def build_module(source_files, options=[], skip=[], only=[], module_name=None):
+    """
+    Compile and import a f2py module, built from the given files.
+
+    """
+
+    code = f"import sys; sys.path = {sys.path!r}; import numpy.f2py; numpy.f2py.main()"
+
+    d = get_module_dir()
+
+    # Copy files
+    dst_sources = []
+    f2py_sources = []
+    for fn in source_files:
+        if not os.path.isfile(fn):
+            raise RuntimeError("%s is not a file" % fn)
+        dst = os.path.join(d, os.path.basename(fn))
+        shutil.copyfile(fn, dst)
+        dst_sources.append(dst)
+
+        base, ext = os.path.splitext(dst)
+        if ext in (".f90", ".f", ".c", ".pyf"):
+            f2py_sources.append(dst)
+
+    assert f2py_sources
+
+    # Prepare options
+    if module_name is None:
+        module_name = get_temp_module_name()
+    f2py_opts = ["-c", "-m", module_name] + options + f2py_sources
+    if skip:
+        f2py_opts += ["skip:"] + skip
+    if only:
+        f2py_opts += ["only:"] + only
+
+    # Build
+    cwd = os.getcwd()
+    try:
+        os.chdir(d)
+        cmd = [sys.executable, "-c", code] + f2py_opts
+        p = subprocess.Popen(cmd,
+                             stdout=subprocess.PIPE,
+                             stderr=subprocess.STDOUT)
+        out, err = p.communicate()
+        if p.returncode != 0:
+            raise RuntimeError("Running f2py failed: %s\n%s" %
+                               (cmd[4:], asunicode(out)))
+    finally:
+        os.chdir(cwd)
+
+        # Partial cleanup
+        for fn in dst_sources:
+            os.unlink(fn)
+
+    # Rebase (Cygwin-only)
+    if sys.platform == "cygwin":
+        # If someone starts deleting modules after import, this will
+        # need to change to record how big each module is, rather than
+        # relying on rebase being able to find that from the files.
+        _module_list.extend(
+            glob.glob(os.path.join(d, "{:s}*".format(module_name)))
+        )
+        subprocess.check_call(
+            ["/usr/bin/rebase", "--database", "--oblivious", "--verbose"]
+            + _module_list
+        )
+
+
+
+    # Import
+    return import_module(module_name)
+
+
+@_memoize
+def build_code(source_code,
+               options=[],
+               skip=[],
+               only=[],
+               suffix=None,
+               module_name=None):
+    """
+    Compile and import Fortran code using f2py.
+
+    """
+    if suffix is None:
+        suffix = ".f"
+    with temppath(suffix=suffix) as path:
+        with open(path, "w") as f:
+            f.write(source_code)
+        return build_module([path],
+                            options=options,
+                            skip=skip,
+                            only=only,
+                            module_name=module_name)
+
+
+#
+# Check if compilers are available at all...
+#
+
+_compiler_status = None
+
+
+def _get_compiler_status():
+    global _compiler_status
+    if _compiler_status is not None:
+        return _compiler_status
+
+    _compiler_status = (False, False, False)
+    if IS_WASM:
+        # Can't run compiler from inside WASM.
+        return _compiler_status
+
+    # XXX: this is really ugly. But I don't know how to invoke Distutils
+    #      in a safer way...
+    code = textwrap.dedent(f"""\
+        import os
+        import sys
+        sys.path = {repr(sys.path)}
+
+        def configuration(parent_name='',top_path=None):
+            global config
+            from numpy.distutils.misc_util import Configuration
+            config = Configuration('', parent_name, top_path)
+            return config
+
+        from numpy.distutils.core import setup
+        setup(configuration=configuration)
+
+        config_cmd = config.get_config_cmd()
+        have_c = config_cmd.try_compile('void foo() {{}}')
+        print('COMPILERS:%%d,%%d,%%d' %% (have_c,
+                                          config.have_f77c(),
+                                          config.have_f90c()))
+        sys.exit(99)
+        """)
+    code = code % dict(syspath=repr(sys.path))
+
+    tmpdir = tempfile.mkdtemp()
+    try:
+        script = os.path.join(tmpdir, "setup.py")
+
+        with open(script, "w") as f:
+            f.write(code)
+
+        cmd = [sys.executable, "setup.py", "config"]
+        p = subprocess.Popen(cmd,
+                             stdout=subprocess.PIPE,
+                             stderr=subprocess.STDOUT,
+                             cwd=tmpdir)
+        out, err = p.communicate()
+    finally:
+        shutil.rmtree(tmpdir)
+
+    m = re.search(br"COMPILERS:(\d+),(\d+),(\d+)", out)
+    if m:
+        _compiler_status = (
+            bool(int(m.group(1))),
+            bool(int(m.group(2))),
+            bool(int(m.group(3))),
+        )
+    # Finished
+    return _compiler_status
+
+
+def has_c_compiler():
+    return _get_compiler_status()[0]
+
+
+def has_f77_compiler():
+    return _get_compiler_status()[1]
+
+
+def has_f90_compiler():
+    return _get_compiler_status()[2]
+
+
+#
+# Building with distutils
+#
+
+
+@_memoize
+def build_module_distutils(source_files, config_code, module_name, **kw):
+    """
+    Build a module via distutils and import it.
+
+    """
+    d = get_module_dir()
+
+    # Copy files
+    dst_sources = []
+    for fn in source_files:
+        if not os.path.isfile(fn):
+            raise RuntimeError("%s is not a file" % fn)
+        dst = os.path.join(d, os.path.basename(fn))
+        shutil.copyfile(fn, dst)
+        dst_sources.append(dst)
+
+    # Build script
+    config_code = textwrap.dedent(config_code).replace("\n", "\n    ")
+
+    code = fr"""
+import os
+import sys
+sys.path = {repr(sys.path)}
+
+def configuration(parent_name='',top_path=None):
+    from numpy.distutils.misc_util import Configuration
+    config = Configuration('', parent_name, top_path)
+    {config_code}
+    return config
+
+if __name__ == "__main__":
+    from numpy.distutils.core import setup
+    setup(configuration=configuration)
+    """
+    script = os.path.join(d, get_temp_module_name() + ".py")
+    dst_sources.append(script)
+    with open(script, "wb") as f:
+        f.write(code.encode('latin1'))
+
+    # Build
+    cwd = os.getcwd()
+    try:
+        os.chdir(d)
+        cmd = [sys.executable, script, "build_ext", "-i"]
+        p = subprocess.Popen(cmd,
+                             stdout=subprocess.PIPE,
+                             stderr=subprocess.STDOUT)
+        out, err = p.communicate()
+        if p.returncode != 0:
+            raise RuntimeError("Running distutils build failed: %s\n%s" %
+                               (cmd[4:], asstr(out)))
+    finally:
+        os.chdir(cwd)
+
+        # Partial cleanup
+        for fn in dst_sources:
+            os.unlink(fn)
+
+    # Import
+    __import__(module_name)
+    return sys.modules[module_name]
+
+
+#
+# Unittest convenience
+#
+
+
+class F2PyTest:
+    code = None
+    sources = None
+    options = []
+    skip = []
+    only = []
+    suffix = ".f"
+    module = None
+
+    @property
+    def module_name(self):
+        cls = type(self)
+        return f'_{cls.__module__.rsplit(".",1)[-1]}_{cls.__name__}_ext_module'
+
+    def setup_method(self):
+        if sys.platform == "win32":
+            pytest.skip("Fails with MinGW64 Gfortran (Issue #9673)")
+
+        if self.module is not None:
+            return
+
+        # Check compiler availability first
+        if not has_c_compiler():
+            pytest.skip("No C compiler available")
+
+        codes = []
+        if self.sources:
+            codes.extend(self.sources)
+        if self.code is not None:
+            codes.append(self.suffix)
+
+        needs_f77 = False
+        needs_f90 = False
+        needs_pyf = False
+        for fn in codes:
+            if str(fn).endswith(".f"):
+                needs_f77 = True
+            elif str(fn).endswith(".f90"):
+                needs_f90 = True
+            elif str(fn).endswith(".pyf"):
+                needs_pyf = True
+        if needs_f77 and not has_f77_compiler():
+            pytest.skip("No Fortran 77 compiler available")
+        if needs_f90 and not has_f90_compiler():
+            pytest.skip("No Fortran 90 compiler available")
+        if needs_pyf and not (has_f90_compiler() or has_f77_compiler()):
+            pytest.skip("No Fortran compiler available")
+
+        # Build the module
+        if self.code is not None:
+            self.module = build_code(
+                self.code,
+                options=self.options,
+                skip=self.skip,
+                only=self.only,
+                suffix=self.suffix,
+                module_name=self.module_name,
+            )
+
+        if self.sources is not None:
+            self.module = build_module(
+                self.sources,
+                options=self.options,
+                skip=self.skip,
+                only=self.only,
+                module_name=self.module_name,
+            )
+
+
+#
+# Helper functions
+#
+
+
+def getpath(*a):
+    # Package root
+    d = Path(numpy.f2py.__file__).parent.resolve()
+    return d.joinpath(*a)
+
+
+@contextlib.contextmanager
+def switchdir(path):
+    curpath = Path.cwd()
+    os.chdir(path)
+    try:
+        yield
+    finally:
+        os.chdir(curpath)
diff --git a/.venv/lib/python3.12/site-packages/numpy/f2py/use_rules.py b/.venv/lib/python3.12/site-packages/numpy/f2py/use_rules.py
new file mode 100644
index 00000000..808b3dd9
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/numpy/f2py/use_rules.py
@@ -0,0 +1,106 @@
+"""
+Build 'use others module data' mechanism for f2py2e.
+
+Copyright 1999 -- 2011 Pearu Peterson all rights reserved.
+Copyright 2011 -- present NumPy Developers.
+Permission to use, modify, and distribute this software is given under the
+terms of the NumPy License.
+
+NO WARRANTY IS EXPRESSED OR IMPLIED.  USE AT YOUR OWN RISK.
+"""
+__version__ = "$Revision: 1.3 $"[10:-1]
+
+f2py_version = 'See `f2py -v`'
+
+
+from .auxfuncs import (
+    applyrules, dictappend, gentitle, hasnote, outmess
+)
+
+
+usemodule_rules = {
+    'body': """
+#begintitle#
+static char doc_#apiname#[] = \"\\\nVariable wrapper signature:\\n\\
+\t #name# = get_#name#()\\n\\
+Arguments:\\n\\
+#docstr#\";
+extern F_MODFUNC(#usemodulename#,#USEMODULENAME#,#realname#,#REALNAME#);
+static PyObject *#apiname#(PyObject *capi_self, PyObject *capi_args) {
+/*#decl#*/
+\tif (!PyArg_ParseTuple(capi_args, \"\")) goto capi_fail;
+printf(\"c: %d\\n\",F_MODFUNC(#usemodulename#,#USEMODULENAME#,#realname#,#REALNAME#));
+\treturn Py_BuildValue(\"\");
+capi_fail:
+\treturn NULL;
+}
+""",
+    'method': '\t{\"get_#name#\",#apiname#,METH_VARARGS|METH_KEYWORDS,doc_#apiname#},',
+    'need': ['F_MODFUNC']
+}
+
+################
+
+
+def buildusevars(m, r):
+    ret = {}
+    outmess(
+        '\t\tBuilding use variable hooks for module "%s" (feature only for F90/F95)...\n' % (m['name']))
+    varsmap = {}
+    revmap = {}
+    if 'map' in r:
+        for k in r['map'].keys():
+            if r['map'][k] in revmap:
+                outmess('\t\t\tVariable "%s<=%s" is already mapped by "%s". Skipping.\n' % (
+                    r['map'][k], k, revmap[r['map'][k]]))
+            else:
+                revmap[r['map'][k]] = k
+    if 'only' in r and r['only']:
+        for v in r['map'].keys():
+            if r['map'][v] in m['vars']:
+
+                if revmap[r['map'][v]] == v:
+                    varsmap[v] = r['map'][v]
+                else:
+                    outmess('\t\t\tIgnoring map "%s=>%s". See above.\n' %
+                            (v, r['map'][v]))
+            else:
+                outmess(
+                    '\t\t\tNo definition for variable "%s=>%s". Skipping.\n' % (v, r['map'][v]))
+    else:
+        for v in m['vars'].keys():
+            if v in revmap:
+                varsmap[v] = revmap[v]
+            else:
+                varsmap[v] = v
+    for v in varsmap.keys():
+        ret = dictappend(ret, buildusevar(v, varsmap[v], m['vars'], m['name']))
+    return ret
+
+
+def buildusevar(name, realname, vars, usemodulename):
+    outmess('\t\t\tConstructing wrapper function for variable "%s=>%s"...\n' % (
+        name, realname))
+    ret = {}
+    vrd = {'name': name,
+           'realname': realname,
+           'REALNAME': realname.upper(),
+           'usemodulename': usemodulename,
+           'USEMODULENAME': usemodulename.upper(),
+           'texname': name.replace('_', '\\_'),
+           'begintitle': gentitle('%s=>%s' % (name, realname)),
+           'endtitle': gentitle('end of %s=>%s' % (name, realname)),
+           'apiname': '#modulename#_use_%s_from_%s' % (realname, usemodulename)
+           }
+    nummap = {0: 'Ro', 1: 'Ri', 2: 'Rii', 3: 'Riii', 4: 'Riv',
+              5: 'Rv', 6: 'Rvi', 7: 'Rvii', 8: 'Rviii', 9: 'Rix'}
+    vrd['texnamename'] = name
+    for i in nummap.keys():
+        vrd['texnamename'] = vrd['texnamename'].replace(repr(i), nummap[i])
+    if hasnote(vars[realname]):
+        vrd['note'] = vars[realname]['note']
+    rd = dictappend({}, vrd)
+
+    print(name, realname, vars[realname])
+    ret = applyrules(usemodule_rules, rd)
+    return ret