diff options
80 files changed, 3720 insertions, 2296 deletions
diff --git a/.gitpod.yml b/.gitpod.yml index f9c35fd9b..c46752f10 100644 --- a/.gitpod.yml +++ b/.gitpod.yml @@ -6,18 +6,20 @@ image: numpy/numpy-gitpod:latest tasks: - - name: Prepare development + - name: Prepare development environment init: | mkdir -p .vscode cp tools/gitpod/settings.json .vscode/settings.json + rm -f /workspace/numpy/.git/shallow.lock conda activate numpy-dev + git pull --unshallow # need to force this else the prebuild fails + git fetch --tags python setup.py build_ext --inplace echo "🛠Completed rebuilding NumPy!! 🛠" echo "📖 Building docs 📖 " cd doc make html echo "✨ Pre-build complete! You can close this terminal ✨ " - # -------------------------------------------------------- # exposing ports for liveserve @@ -60,3 +62,4 @@ github: addBadge: false # add a label once the prebuild is ready to pull requests (defaults to false) addLabel: false +
\ No newline at end of file @@ -364,6 +364,8 @@ Nicholas A. Del Grosso <delgrosso@bio.lmu.de> nickdg <delgrosso@bio.lmu.de> Nicholas McKibben <nicholas.bgp@gmail.com> Nick Minkyu Lee <mknicklee@protonmail.com> fivemok <9394929+fivemok@users.noreply.github.com> Oliver Eberle <oliver_eberle@web.de> +Omid Rajaei <rajaei.net@gmail.com> +Omid Rajaei <rajaei.net@gmail.com> <89868505+rajaeinet@users.noreply.github.com> OndÅ™ej ÄŒertÃk <ondrej.certik@gmail.com> Óscar Villellas Guillén <oscar.villellas@continuum.io> Panos Mavrogiorgos <pmav99@users.noreply.github.com> @@ -13,7 +13,7 @@ []( https://numfocus.org) -[]( +[]( https://pypi.org/project/numpy/) []( https://anaconda.org/conda-forge/numpy) diff --git a/doc/changelog/1.12.0-changelog.rst b/doc/changelog/1.12.0-changelog.rst index 2e91f510f..052714374 100644 --- a/doc/changelog/1.12.0-changelog.rst +++ b/doc/changelog/1.12.0-changelog.rst @@ -283,7 +283,7 @@ A total of 418 pull requests were merged for this release. * `#7373 <https://github.com/numpy/numpy/pull/7373>`__: ENH: Add bitwise_and identity * `#7378 <https://github.com/numpy/numpy/pull/7378>`__: added NumPy logo and separator * `#7382 <https://github.com/numpy/numpy/pull/7382>`__: MAINT: cleanup np.average -* `#7385 <https://github.com/numpy/numpy/pull/7385>`__: DOC: note about wheels / windows wheels for pypi +* `#7385 <https://github.com/numpy/numpy/pull/7385>`__: DOC: note about wheels / windows wheels for PyPI * `#7386 <https://github.com/numpy/numpy/pull/7386>`__: Added label icon to Travis status * `#7397 <https://github.com/numpy/numpy/pull/7397>`__: BUG: incorrect type for objects whose __len__ fails * `#7398 <https://github.com/numpy/numpy/pull/7398>`__: DOC: fix typo diff --git a/doc/changelog/1.20.0-changelog.rst b/doc/changelog/1.20.0-changelog.rst index f06bd8a8d..f2af4a7de 100644 --- a/doc/changelog/1.20.0-changelog.rst +++ b/doc/changelog/1.20.0-changelog.rst @@ -714,7 +714,7 @@ A total of 716 pull requests were merged for this release. * `#17440 <https://github.com/numpy/numpy/pull/17440>`__: DOC: Cleaner template for PRs * `#17442 <https://github.com/numpy/numpy/pull/17442>`__: MAINT: fix exception chaining in format.py * `#17443 <https://github.com/numpy/numpy/pull/17443>`__: ENH: Warn on unsupported Python 3.10+ -* `#17444 <https://github.com/numpy/numpy/pull/17444>`__: ENH: Add ``Typing :: Typed`` to the PyPi classifier +* `#17444 <https://github.com/numpy/numpy/pull/17444>`__: ENH: Add ``Typing :: Typed`` to the PyPI classifier * `#17445 <https://github.com/numpy/numpy/pull/17445>`__: DOC: Fix the references for macros * `#17447 <https://github.com/numpy/numpy/pull/17447>`__: NEP: update NEP 42 with discussion of type hinting applications * `#17448 <https://github.com/numpy/numpy/pull/17448>`__: DOC: Remove CoC pages from Sphinx diff --git a/doc/changelog/1.22.1-changelog.rst b/doc/changelog/1.22.1-changelog.rst new file mode 100644 index 000000000..3b401c1d2 --- /dev/null +++ b/doc/changelog/1.22.1-changelog.rst @@ -0,0 +1,47 @@ + +Contributors +============ + +A total of 14 people contributed to this release. People with a "+" by their +names contributed a patch for the first time. + +* Arryan Singh +* Bas van Beek +* Charles Harris +* Denis Laxalde +* Isuru Fernando +* Kevin Sheppard +* Matthew Barber +* Matti Picus +* Melissa Weber Mendonça +* Mukulika Pahari +* Omid Rajaei + +* Pearu Peterson +* Ralf Gommers +* Sebastian Berg + +Pull requests merged +==================== + +A total of 20 pull requests were merged for this release. + +* `#20702 <https://github.com/numpy/numpy/pull/20702>`__: MAINT, DOC: Post 1.22.0 release fixes. +* `#20703 <https://github.com/numpy/numpy/pull/20703>`__: DOC, BUG: Use pngs instead of svgs. +* `#20704 <https://github.com/numpy/numpy/pull/20704>`__: DOC:Fixed the link on user-guide landing page +* `#20714 <https://github.com/numpy/numpy/pull/20714>`__: BUG: Restore vc141 support +* `#20724 <https://github.com/numpy/numpy/pull/20724>`__: BUG: Fix array dimensions solver for multidimensional arguments... +* `#20725 <https://github.com/numpy/numpy/pull/20725>`__: TYP: change type annotation for `__array_namespace__` to ModuleType +* `#20726 <https://github.com/numpy/numpy/pull/20726>`__: TYP, MAINT: Allow `ndindex` to accept integer tuples +* `#20757 <https://github.com/numpy/numpy/pull/20757>`__: BUG: Relax dtype identity check in reductions +* `#20763 <https://github.com/numpy/numpy/pull/20763>`__: TYP: Allow time manipulation functions to accept `date` and `timedelta`... +* `#20768 <https://github.com/numpy/numpy/pull/20768>`__: TYP: Relax the type of `ndarray.__array_finalize__` +* `#20795 <https://github.com/numpy/numpy/pull/20795>`__: MAINT: Raise RuntimeError if setuptools version is too recent. +* `#20796 <https://github.com/numpy/numpy/pull/20796>`__: BUG, DOC: Fixes SciPy docs build warnings +* `#20797 <https://github.com/numpy/numpy/pull/20797>`__: DOC: fix OpenBLAS version in release note +* `#20798 <https://github.com/numpy/numpy/pull/20798>`__: PERF: Optimize array check for bounded 0,1 values +* `#20805 <https://github.com/numpy/numpy/pull/20805>`__: BUG: Fix that reduce-likes honor out always (and live in the... +* `#20806 <https://github.com/numpy/numpy/pull/20806>`__: BUG: ``array_api.argsort(descending=True)`` respects relative... +* `#20807 <https://github.com/numpy/numpy/pull/20807>`__: BUG: Allow integer inputs for pow-related functions in `array_api` +* `#20814 <https://github.com/numpy/numpy/pull/20814>`__: DOC: Refer to NumPy, not pandas, in main page +* `#20815 <https://github.com/numpy/numpy/pull/20815>`__: DOC: Update Copyright to 2022 [License] +* `#20819 <https://github.com/numpy/numpy/pull/20819>`__: BUG: Return correctly shaped inverse indices in array_api set... diff --git a/doc/neps/nep-0017-split-out-maskedarray.rst b/doc/neps/nep-0017-split-out-maskedarray.rst index 5cb1c0c39..faad68828 100644 --- a/doc/neps/nep-0017-split-out-maskedarray.rst +++ b/doc/neps/nep-0017-split-out-maskedarray.rst @@ -123,7 +123,7 @@ References and Footnotes .. [1] Subclassing ndarray, https://docs.scipy.org/doc/numpy/user/basics.subclassing.html -.. [2] PyPi: maskedarray, https://pypi.org/project/maskedarray/ +.. [2] PyPI: maskedarray, https://pypi.org/project/maskedarray/ Copyright --------- diff --git a/doc/release/upcoming_changes/15844.new_feature.rst b/doc/release/upcoming_changes/15844.new_feature.rst new file mode 100644 index 000000000..f2746807b --- /dev/null +++ b/doc/release/upcoming_changes/15844.new_feature.rst @@ -0,0 +1,4 @@ +f2py supports reading access type attributes from derived type statements +------------------------------------------------------------------------- +As a result, one does not need to use `public` or `private` statements to +specify derived type access properties. diff --git a/doc/source/dev/development_workflow.rst b/doc/source/dev/development_workflow.rst index 457bcf34a..38f047ec0 100644 --- a/doc/source/dev/development_workflow.rst +++ b/doc/source/dev/development_workflow.rst @@ -204,10 +204,10 @@ fragments in your commit message:: ``[skip travis]``: skip TravisCI jobs ``[skip azurepipelines]``: skip Azure jobs -*Note: unfortunately not all CI systems implement this feature well, or at all. +*Note*: unfortunately not all CI systems implement this feature well, or at all. CircleCI supports ``ci skip`` but has no command to skip only CircleCI. Azure chooses to still run jobs with skip commands on PRs, the jobs only get -skipped on merging to master.* +skipped on merging to master. .. _workflow_mailing_list: diff --git a/doc/source/reference/random/index.rst b/doc/source/reference/random/index.rst index aaabc9b39..674799d47 100644 --- a/doc/source/reference/random/index.rst +++ b/doc/source/reference/random/index.rst @@ -185,7 +185,7 @@ What's New or Different methods which are 2-10 times faster than NumPy's Box-Muller or inverse CDF implementations. * Optional ``dtype`` argument that accepts ``np.float32`` or ``np.float64`` - to produce either single or double prevision uniform random variables for + to produce either single or double precision uniform random variables for select distributions * Optional ``out`` argument that allows existing arrays to be filled for select distributions diff --git a/doc/source/reference/random/new-or-different.rst b/doc/source/reference/random/new-or-different.rst index a81543926..7a206a2ce 100644 --- a/doc/source/reference/random/new-or-different.rst +++ b/doc/source/reference/random/new-or-different.rst @@ -84,7 +84,7 @@ And in more detail: * The bit generators can be used in downstream projects via Cython. * Optional ``dtype`` argument that accepts ``np.float32`` or ``np.float64`` - to produce either single or double prevision uniform random variables for + to produce either single or double precision uniform random variables for select distributions * Uniforms (`~.Generator.random` and `~.Generator.integers`) diff --git a/doc/source/reference/simd/generated_tables/cpu_features.inc b/doc/source/reference/simd/generated_tables/cpu_features.inc index 17d1b4951..7782172d2 100644 --- a/doc/source/reference/simd/generated_tables/cpu_features.inc +++ b/doc/source/reference/simd/generated_tables/cpu_features.inc @@ -36,26 +36,28 @@ On IBM/POWER big-endian .. table:: :align: left - ======== ================ - Name Implies - ======== ================ - ``VSX`` - ``VSX2`` ``VSX`` - ``VSX3`` ``VSX`` ``VSX2`` - ======== ================ + ======== ========================= + Name Implies + ======== ========================= + ``VSX`` + ``VSX2`` ``VSX`` + ``VSX3`` ``VSX`` ``VSX2`` + ``VSX4`` ``VSX`` ``VSX2`` ``VSX3`` + ======== ========================= On IBM/POWER little-endian ~~~~~~~~~~~~~~~~~~~~~~~~~~ .. table:: :align: left - ======== ================ - Name Implies - ======== ================ - ``VSX`` ``VSX2`` - ``VSX2`` ``VSX`` - ``VSX3`` ``VSX`` ``VSX2`` - ======== ================ + ======== ========================= + Name Implies + ======== ========================= + ``VSX`` ``VSX2`` + ``VSX2`` ``VSX`` + ``VSX3`` ``VSX`` ``VSX2`` + ``VSX4`` ``VSX`` ``VSX2`` ``VSX3`` + ======== ========================= On ARMv7/A32 ~~~~~~~~~~~~ diff --git a/doc/source/release.rst b/doc/source/release.rst index eb6437cf0..fe8027304 100644 --- a/doc/source/release.rst +++ b/doc/source/release.rst @@ -6,6 +6,7 @@ Release notes :maxdepth: 3 1.23.0 <release/1.23.0-notes> + 1.22.1 <release/1.22.1-notes> 1.22.0 <release/1.22.0-notes> 1.21.5 <release/1.21.5-notes> 1.21.4 <release/1.21.4-notes> diff --git a/doc/source/release/1.10.3-notes.rst b/doc/source/release/1.10.3-notes.rst index 0d4df4ce6..9172f7663 100644 --- a/doc/source/release/1.10.3-notes.rst +++ b/doc/source/release/1.10.3-notes.rst @@ -2,4 +2,4 @@ NumPy 1.10.3 Release Notes ========================== -N/A this release did not happen due to various screwups involving PyPi. +N/A this release did not happen due to various screwups involving PyPI. diff --git a/doc/source/release/1.11.1-notes.rst b/doc/source/release/1.11.1-notes.rst index 6303c32f0..a196502cf 100644 --- a/doc/source/release/1.11.1-notes.rst +++ b/doc/source/release/1.11.1-notes.rst @@ -4,7 +4,7 @@ NumPy 1.11.1 Release Notes Numpy 1.11.1 supports Python 2.6 - 2.7 and 3.2 - 3.5. It fixes bugs and regressions found in Numpy 1.11.0 and includes several build related -improvements. Wheels for Linux, Windows, and OSX can be found on pypi. +improvements. Wheels for Linux, Windows, and OSX can be found on PyPI. Fixes Merged ============ diff --git a/doc/source/release/1.12.1-notes.rst b/doc/source/release/1.12.1-notes.rst index f67dab108..09a2e6738 100644 --- a/doc/source/release/1.12.1-notes.rst +++ b/doc/source/release/1.12.1-notes.rst @@ -4,7 +4,7 @@ NumPy 1.12.1 Release Notes NumPy 1.12.1 supports Python 2.7 and 3.4 - 3.6 and fixes bugs and regressions found in NumPy 1.12.0. In particular, the regression in f2py constant parsing -is fixed. Wheels for Linux, Windows, and OSX can be found on pypi, +is fixed. Wheels for Linux, Windows, and OSX can be found on PyPI, Bugs Fixed ========== diff --git a/doc/source/release/1.22.1-notes.rst b/doc/source/release/1.22.1-notes.rst new file mode 100644 index 000000000..0012f199e --- /dev/null +++ b/doc/source/release/1.22.1-notes.rst @@ -0,0 +1,63 @@ +.. currentmodule:: numpy + +========================== +NumPy 1.22.1 Release Notes +========================== + +The NumPy 1.22.1 is a maintenance release that fixes bugs discovered after the +1.22.0 release. Notable fixes are: + +- Fix f2PY docstring problems (SciPy) +- Fix reduction type problems (AstroPy) +- Fix various typing bugs. + +The Python versions supported for this release are 3.8-3.10. + + +Contributors +============ + +A total of 14 people contributed to this release. People with a "+" by their +names contributed a patch for the first time. + +* Arryan Singh +* Bas van Beek +* Charles Harris +* Denis Laxalde +* Isuru Fernando +* Kevin Sheppard +* Matthew Barber +* Matti Picus +* Melissa Weber Mendonça +* Mukulika Pahari +* Omid Rajaei + +* Pearu Peterson +* Ralf Gommers +* Sebastian Berg + + +Pull requests merged +==================== + +A total of 20 pull requests were merged for this release. + +* `#20702 <https://github.com/numpy/numpy/pull/20702>`__: MAINT, DOC: Post 1.22.0 release fixes. +* `#20703 <https://github.com/numpy/numpy/pull/20703>`__: DOC, BUG: Use pngs instead of svgs. +* `#20704 <https://github.com/numpy/numpy/pull/20704>`__: DOC: Fixed the link on user-guide landing page +* `#20714 <https://github.com/numpy/numpy/pull/20714>`__: BUG: Restore vc141 support +* `#20724 <https://github.com/numpy/numpy/pull/20724>`__: BUG: Fix array dimensions solver for multidimensional arguments... +* `#20725 <https://github.com/numpy/numpy/pull/20725>`__: TYP: change type annotation for ``__array_namespace__`` to ModuleType +* `#20726 <https://github.com/numpy/numpy/pull/20726>`__: TYP, MAINT: Allow ``ndindex`` to accept integer tuples +* `#20757 <https://github.com/numpy/numpy/pull/20757>`__: BUG: Relax dtype identity check in reductions +* `#20763 <https://github.com/numpy/numpy/pull/20763>`__: TYP: Allow time manipulation functions to accept ``date`` and ``timedelta``... +* `#20768 <https://github.com/numpy/numpy/pull/20768>`__: TYP: Relax the type of ``ndarray.__array_finalize__`` +* `#20795 <https://github.com/numpy/numpy/pull/20795>`__: MAINT: Raise RuntimeError if setuptools version is too recent. +* `#20796 <https://github.com/numpy/numpy/pull/20796>`__: BUG, DOC: Fixes SciPy docs build warnings +* `#20797 <https://github.com/numpy/numpy/pull/20797>`__: DOC: fix OpenBLAS version in release note +* `#20798 <https://github.com/numpy/numpy/pull/20798>`__: PERF: Optimize array check for bounded 0,1 values +* `#20805 <https://github.com/numpy/numpy/pull/20805>`__: BUG: Fix that reduce-likes honor out always (and live in the... +* `#20806 <https://github.com/numpy/numpy/pull/20806>`__: BUG: ``array_api.argsort(descending=True)`` respects relative... +* `#20807 <https://github.com/numpy/numpy/pull/20807>`__: BUG: Allow integer inputs for pow-related functions in ``array_api`` +* `#20814 <https://github.com/numpy/numpy/pull/20814>`__: DOC: Refer to NumPy, not pandas, in main page +* `#20815 <https://github.com/numpy/numpy/pull/20815>`__: DOC: Update Copyright to 2022 [License] +* `#20819 <https://github.com/numpy/numpy/pull/20819>`__: BUG: Return correctly shaped inverse indices in array_api set... diff --git a/doc/source/user/absolute_beginners.rst b/doc/source/user/absolute_beginners.rst index 2c6882905..cf11c6745 100644 --- a/doc/source/user/absolute_beginners.rst +++ b/doc/source/user/absolute_beginners.rst @@ -1588,7 +1588,7 @@ If you created this array "a" :: .. for doctests The continuous integration truncates dataframe display without this setting. - >>> pd.set_option('max_columns', 10) + >>> pd.set_option('display.max_columns', 10) You could create a Pandas dataframe :: diff --git a/environment.yml b/environment.yml index 024bee2c7..214a75352 100644 --- a/environment.yml +++ b/environment.yml @@ -7,7 +7,7 @@ name: numpy-dev channels: - conda-forge dependencies: - - python + - python=3.9 #need to pin to avoid issues with builds - cython - compilers - openblas @@ -21,13 +21,14 @@ dependencies: # For type annotations - mypy=0.931 # For building docs - - sphinx=4.1.1 + - sphinx=4.2.0 + - sphinx-panels - numpydoc=1.1.0 - ipython - scipy - pandas - matplotlib - - pydata-sphinx-theme + - pydata-sphinx-theme=0.7.2 - breathe # For linting - pycodestyle=2.7.0 diff --git a/numpy/__init__.py b/numpy/__init__.py index e8d1820a1..abe53fe9a 100644 --- a/numpy/__init__.py +++ b/numpy/__init__.py @@ -413,6 +413,11 @@ else: # it is tidier organized. core.multiarray._multiarray_umath._reload_guard() + # Tell PyInstaller where to find hook-numpy.py + def _pyinstaller_hooks_dir(): + from pathlib import Path + return [str(Path(__file__).with_name("_pyinstaller").resolve())] + # get the version using versioneer from .version import __version__, git_revision as __git_version__ diff --git a/numpy/__init__.pyi b/numpy/__init__.pyi index 8e92e0f42..92f98a801 100644 --- a/numpy/__init__.pyi +++ b/numpy/__init__.pyi @@ -1451,13 +1451,13 @@ class ndarray(_ArrayOrScalarCommon, Generic[_ShapeType, _DType_co]): def size(self) -> int: ... @property def real( - self: NDArray[_SupportsReal[_ScalarType]], # type: ignore[type-var] + self: ndarray[_ShapeType, dtype[_SupportsReal[_ScalarType]]], # type: ignore[type-var] ) -> ndarray[_ShapeType, _dtype[_ScalarType]]: ... @real.setter def real(self, value: ArrayLike) -> None: ... @property def imag( - self: NDArray[_SupportsImag[_ScalarType]], # type: ignore[type-var] + self: ndarray[_ShapeType, dtype[_SupportsImag[_ScalarType]]], # type: ignore[type-var] ) -> ndarray[_ShapeType, _dtype[_ScalarType]]: ... @imag.setter def imag(self, value: ArrayLike) -> None: ... @@ -3673,6 +3673,8 @@ class memmap(ndarray[_ShapeType, _DType_co]): ) -> Any: ... def flush(self) -> None: ... +# TODO: Add a mypy plugin for managing functions whose output type is dependant +# on the literal value of some sort of signature (e.g. `einsum` and `vectorize`) class vectorize: pyfunc: Callable[..., Any] cache: bool @@ -3689,7 +3691,7 @@ class vectorize: cache: bool = ..., signature: None | str = ..., ) -> None: ... - def __call__(self, *args: Any, **kwargs: Any) -> NDArray[Any]: ... + def __call__(self, *args: Any, **kwargs: Any) -> Any: ... class poly1d: @property diff --git a/numpy/_pyinstaller/__init__.py b/numpy/_pyinstaller/__init__.py new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/numpy/_pyinstaller/__init__.py diff --git a/numpy/_pyinstaller/hook-numpy.py b/numpy/_pyinstaller/hook-numpy.py new file mode 100644 index 000000000..a08b7c963 --- /dev/null +++ b/numpy/_pyinstaller/hook-numpy.py @@ -0,0 +1,40 @@ +"""This hook should collect all binary files and any hidden modules that numpy +needs. + +Our (some-what inadequate) docs for writing PyInstaller hooks are kept here: +https://pyinstaller.readthedocs.io/en/stable/hooks.html + +""" +from PyInstaller.compat import is_conda, is_pure_conda +from PyInstaller.utils.hooks import collect_dynamic_libs, is_module_satisfies + +# Collect all DLLs inside numpy's installation folder, dump them into built +# app's root. +binaries = collect_dynamic_libs("numpy", ".") + +# If using Conda without any non-conda virtual environment manager: +if is_pure_conda: + # Assume running the NumPy from Conda-forge and collect it's DLLs from the + # communal Conda bin directory. DLLs from NumPy's dependencies must also be + # collected to capture MKL, OpenBlas, OpenMP, etc. + from PyInstaller.utils.hooks import conda_support + datas = conda_support.collect_dynamic_libs("numpy", dependencies=True) + +# Submodules PyInstaller cannot detect (probably because they are only imported +# by extension modules, which PyInstaller cannot read). +hiddenimports = ['numpy.core._dtype_ctypes'] +if is_conda: + hiddenimports.append("six") + +# Remove testing and building code and packages that are referenced throughout +# NumPy but are not really dependencies. +excludedimports = [ + "scipy", + "pytest", + "nose", + "f2py", + "setuptools", + "numpy.f2py", + "distutils", + "numpy.distutils", +] diff --git a/numpy/_pyinstaller/pyinstaller-smoke.py b/numpy/_pyinstaller/pyinstaller-smoke.py new file mode 100644 index 000000000..1c9f78ae3 --- /dev/null +++ b/numpy/_pyinstaller/pyinstaller-smoke.py @@ -0,0 +1,32 @@ +"""A crude *bit of everything* smoke test to verify PyInstaller compatibility. + +PyInstaller typically goes wrong by forgetting to package modules, extension +modules or shared libraries. This script should aim to touch as many of those +as possible in an attempt to trip a ModuleNotFoundError or a DLL load failure +due to an uncollected resource. Missing resources are unlikely to lead to +arithmitic errors so there's generally no need to verify any calculation's +output - merely that it made it to the end OK. This script should not +explicitly import any of numpy's submodules as that gives PyInstaller undue +hints that those submodules exist and should be collected (accessing implicitly +loaded submodules is OK). + +""" +import numpy as np + +a = np.arange(1., 10.).reshape((3, 3)) % 5 +np.linalg.det(a) +a @ a +a @ a.T +np.linalg.inv(a) +np.sin(np.exp(a)) +np.linalg.svd(a) +np.linalg.eigh(a) + +np.unique(np.random.randint(0, 10, 100)) +np.sort(np.random.uniform(0, 10, 100)) + +np.fft.fft(np.exp(2j * np.pi * np.arange(8) / 8)) +np.ma.masked_array(np.arange(10), np.random.rand(10) < .5).sum() +np.polynomial.Legendre([7, 8, 9]).roots() + +print("I made it!") diff --git a/numpy/_pyinstaller/test_pyinstaller.py b/numpy/_pyinstaller/test_pyinstaller.py new file mode 100644 index 000000000..a9061da19 --- /dev/null +++ b/numpy/_pyinstaller/test_pyinstaller.py @@ -0,0 +1,35 @@ +import subprocess +from pathlib import Path + +import pytest + + +# PyInstaller has been very unproactive about replacing 'imp' with 'importlib'. +@pytest.mark.filterwarnings('ignore::DeprecationWarning') +# It also leaks io.BytesIO()s. +@pytest.mark.filterwarnings('ignore::ResourceWarning') +@pytest.mark.parametrize("mode", ["--onedir", "--onefile"]) +@pytest.mark.slow +def test_pyinstaller(mode, tmp_path): + """Compile and run pyinstaller-smoke.py using PyInstaller.""" + + pyinstaller_cli = pytest.importorskip("PyInstaller.__main__").run + + source = Path(__file__).with_name("pyinstaller-smoke.py").resolve() + args = [ + # Place all generated files in ``tmp_path``. + '--workpath', str(tmp_path / "build"), + '--distpath', str(tmp_path / "dist"), + '--specpath', str(tmp_path), + mode, + str(source), + ] + pyinstaller_cli(args) + + if mode == "--onefile": + exe = tmp_path / "dist" / source.stem + else: + exe = tmp_path / "dist" / source.stem / source.stem + + p = subprocess.run([str(exe)], check=True, stdout=subprocess.PIPE) + assert p.stdout.strip() == b"I made it!" diff --git a/numpy/core/_add_newdocs.py b/numpy/core/_add_newdocs.py index 7081f9a59..1bbacad45 100644 --- a/numpy/core/_add_newdocs.py +++ b/numpy/core/_add_newdocs.py @@ -2943,7 +2943,7 @@ add_newdoc('numpy.core.multiarray', 'ndarray', ('any', add_newdoc('numpy.core.multiarray', 'ndarray', ('argmax', """ - a.argmax(axis=None, out=None) + a.argmax(axis=None, out=None, *, keepdims=False) Return indices of the maximum values along the given axis. @@ -2958,7 +2958,7 @@ add_newdoc('numpy.core.multiarray', 'ndarray', ('argmax', add_newdoc('numpy.core.multiarray', 'ndarray', ('argmin', """ - a.argmin(axis=None, out=None) + a.argmin(axis=None, out=None, *, keepdims=False) Return indices of the minimum values along the given axis. diff --git a/numpy/core/code_generators/generate_umath.py b/numpy/core/code_generators/generate_umath.py index 054150b28..b11504c03 100644 --- a/numpy/core/code_generators/generate_umath.py +++ b/numpy/core/code_generators/generate_umath.py @@ -844,7 +844,7 @@ defdict = { docstrings.get('numpy.core.umath.trunc'), None, TD('e', f='trunc', astype={'e': 'f'}), - TD(inexactvec, simd=[('fma', 'fd'), ('avx512f', 'fd')]), + TD(inexactvec, dispatch=[('loops_unary_fp', 'fd')]), TD('fdg', f='trunc'), TD(O, f='npy_ObjectTrunc'), ), @@ -860,7 +860,7 @@ defdict = { docstrings.get('numpy.core.umath.floor'), None, TD('e', f='floor', astype={'e': 'f'}), - TD(inexactvec, simd=[('fma', 'fd'), ('avx512f', 'fd')]), + TD(inexactvec, dispatch=[('loops_unary_fp', 'fd')]), TD('fdg', f='floor'), TD(O, f='npy_ObjectFloor'), ), @@ -869,7 +869,7 @@ defdict = { docstrings.get('numpy.core.umath.rint'), None, TD('e', f='rint', astype={'e': 'f'}), - TD(inexactvec, simd=[('fma', 'fd'), ('avx512f', 'fd')]), + TD(inexactvec, dispatch=[('loops_unary_fp', 'fd')]), TD('fdg' + cmplx, f='rint'), TD(P, f='rint'), ), diff --git a/numpy/core/fromnumeric.py b/numpy/core/fromnumeric.py index f26f306fa..0ec4f7687 100644 --- a/numpy/core/fromnumeric.py +++ b/numpy/core/fromnumeric.py @@ -3408,6 +3408,7 @@ def mean(a, axis=None, dtype=None, out=None, keepdims=np._NoValue, *, 0.55000000074505806 # may vary Specifying a where argument: + >>> a = np.array([[5, 9, 13], [14, 10, 12], [11, 15, 19]]) >>> np.mean(a) 12.0 diff --git a/numpy/core/include/numpy/ndarraytypes.h b/numpy/core/include/numpy/ndarraytypes.h index 47d063178..35d82ec03 100644 --- a/numpy/core/include/numpy/ndarraytypes.h +++ b/numpy/core/include/numpy/ndarraytypes.h @@ -87,7 +87,7 @@ enum NPY_TYPES { NPY_BOOL=0, /* The number of types not including the new 1.6 types */ NPY_NTYPES_ABI_COMPATIBLE=21 }; -#ifdef _MSC_VER +#if defined(_MSC_VER) && !defined(__clang__) #pragma deprecated(NPY_CHAR) #endif diff --git a/numpy/core/include/numpy/npy_common.h b/numpy/core/include/numpy/npy_common.h index 88794ca07..2bcc45e4f 100644 --- a/numpy/core/include/numpy/npy_common.h +++ b/numpy/core/include/numpy/npy_common.h @@ -131,9 +131,10 @@ #endif #endif -#if defined(_MSC_VER) - #define NPY_INLINE __inline -#elif defined(__GNUC__) +#if defined(_MSC_VER) && !defined(__clang__) + #define NPY_INLINE __inline +/* clang included here to handle clang-cl on Windows */ +#elif defined(__GNUC__) || defined(__clang__) #if defined(__STRICT_ANSI__) #define NPY_INLINE __inline__ #else @@ -180,12 +181,6 @@ defined(__MINGW32__) || defined(__MINGW64__) #include <io.h> -/* mingw based on 3.4.5 has lseek but not ftell/fseek */ -#if defined(__MINGW32__) || defined(__MINGW64__) -extern int __cdecl _fseeki64(FILE *, long long, int); -extern long long __cdecl _ftelli64(FILE *); -#endif - #define npy_fseek _fseeki64 #define npy_ftell _ftelli64 #define npy_lseek _lseeki64 diff --git a/numpy/core/setup.py b/numpy/core/setup.py index 22cac1e9a..c97747606 100644 --- a/numpy/core/setup.py +++ b/numpy/core/setup.py @@ -947,12 +947,12 @@ def configuration(parent_package='',top_path=None): join('src', 'multiarray', 'vdot.c'), join('src', 'common', 'npy_sort.h.src'), join('src', 'npysort', 'quicksort.c.src'), - join('src', 'npysort', 'mergesort.c.src'), - join('src', 'npysort', 'timsort.c.src'), - join('src', 'npysort', 'heapsort.c.src'), + join('src', 'npysort', 'mergesort.cpp'), + join('src', 'npysort', 'timsort.cpp'), + join('src', 'npysort', 'heapsort.cpp'), join('src', 'npysort', 'radixsort.cpp'), join('src', 'common', 'npy_partition.h.src'), - join('src', 'npysort', 'selection.c.src'), + join('src', 'npysort', 'selection.cpp'), join('src', 'common', 'npy_binsearch.h'), join('src', 'npysort', 'binsearch.cpp'), ] diff --git a/numpy/core/src/_simd/_simd.dispatch.c.src b/numpy/core/src/_simd/_simd.dispatch.c.src index 84de9a059..fabec069c 100644 --- a/numpy/core/src/_simd/_simd.dispatch.c.src +++ b/numpy/core/src/_simd/_simd.dispatch.c.src @@ -381,7 +381,7 @@ SIMD_IMPL_INTRIN_1(sumup_@sfx@, @esfx@, v@sfx@) ***************************/ #if @fp_only@ /**begin repeat1 - * #intrin = sqrt, recip, abs, square, ceil, trunc# + * #intrin = sqrt, recip, abs, square, rint, ceil, trunc, floor# */ SIMD_IMPL_INTRIN_1(@intrin@_@sfx@, v@sfx@, v@sfx@) /**end repeat1**/ @@ -615,7 +615,7 @@ SIMD_INTRIN_DEF(sumup_@sfx@) ***************************/ #if @fp_only@ /**begin repeat1 - * #intrin = sqrt, recip, abs, square, ceil, trunc# + * #intrin = sqrt, recip, abs, square, rint, ceil, trunc, floor# */ SIMD_INTRIN_DEF(@intrin@_@sfx@) /**end repeat1**/ diff --git a/numpy/core/src/common/npy_cpu_features.c.src b/numpy/core/src/common/npy_cpu_features.c.src index 1385220f9..ff4f9f60a 100644 --- a/numpy/core/src/common/npy_cpu_features.c.src +++ b/numpy/core/src/common/npy_cpu_features.c.src @@ -61,7 +61,7 @@ npy_cpu_features_dict(void) * AVX512VPOPCNTDQ, AVX512VL, AVX512BW, AVX512DQ, AVX512VNNI, * AVX512IFMA, AVX512VBMI, AVX512VBMI2, AVX512BITALG, * AVX512_KNL, AVX512_KNM, AVX512_SKX, AVX512_CLX, AVX512_CNL, AVX512_ICL, - * VSX, VSX2, VSX3, + * VSX, VSX2, VSX3, VSX4, * VX, VXE, VXE2, * NEON, NEON_FP16, NEON_VFPV4, ASIMD, FPHP, ASIMDHP, ASIMDDP, ASIMDFHM# */ @@ -474,9 +474,15 @@ npy__cpu_init_features(void) #ifndef AT_HWCAP2 #define AT_HWCAP2 26 #endif + #ifndef PPC_FEATURE2_ARCH_2_07 + #define PPC_FEATURE2_ARCH_2_07 0x80000000 + #endif #ifndef PPC_FEATURE2_ARCH_3_00 #define PPC_FEATURE2_ARCH_3_00 0x00800000 #endif + #ifndef PPC_FEATURE2_ARCH_3_1 + #define PPC_FEATURE2_ARCH_3_1 0x00040000 + #endif #endif static void @@ -489,15 +495,18 @@ npy__cpu_init_features(void) return; hwcap = getauxval(AT_HWCAP2); - if (hwcap & PPC_FEATURE2_ARCH_3_00) + if (hwcap & PPC_FEATURE2_ARCH_3_1) { npy__cpu_have[NPY_CPU_FEATURE_VSX] = npy__cpu_have[NPY_CPU_FEATURE_VSX2] = - npy__cpu_have[NPY_CPU_FEATURE_VSX3] = 1; + npy__cpu_have[NPY_CPU_FEATURE_VSX3] = + npy__cpu_have[NPY_CPU_FEATURE_VSX4] = 1; return; } - npy__cpu_have[NPY_CPU_FEATURE_VSX2] = (hwcap & PPC_FEATURE2_ARCH_2_07) != 0; npy__cpu_have[NPY_CPU_FEATURE_VSX] = 1; + npy__cpu_have[NPY_CPU_FEATURE_VSX2] = (hwcap & PPC_FEATURE2_ARCH_2_07) != 0; + npy__cpu_have[NPY_CPU_FEATURE_VSX3] = (hwcap & PPC_FEATURE2_ARCH_3_00) != 0; + npy__cpu_have[NPY_CPU_FEATURE_VSX4] = (hwcap & PPC_FEATURE2_ARCH_3_1) != 0; // TODO: AIX, FreeBSD #else npy__cpu_have[NPY_CPU_FEATURE_VSX] = 1; @@ -507,6 +516,9 @@ npy__cpu_init_features(void) #ifdef NPY_HAVE_VSX3 npy__cpu_have[NPY_CPU_FEATURE_VSX3] = 1; #endif + #ifdef NPY_HAVE_VSX4 + npy__cpu_have[NPY_CPU_FEATURE_VSX4] = 1; + #endif #endif } diff --git a/numpy/core/src/common/npy_cpu_features.h b/numpy/core/src/common/npy_cpu_features.h index 1f52a445d..3d5f2e75c 100644 --- a/numpy/core/src/common/npy_cpu_features.h +++ b/numpy/core/src/common/npy_cpu_features.h @@ -65,6 +65,8 @@ enum npy_cpu_features NPY_CPU_FEATURE_VSX2 = 201, // POWER9 NPY_CPU_FEATURE_VSX3 = 202, + // POWER10 + NPY_CPU_FEATURE_VSX4 = 203, // ARM NPY_CPU_FEATURE_NEON = 300, @@ -167,8 +169,8 @@ npy_cpu_baseline_list(void); * On x64: ['SSSE3', 'SSE41', 'POPCNT', 'SSE42', 'AVX', 'F16C', 'FMA3', 'AVX2', 'AVX512F', ...] * On armhf: ['NEON', 'NEON_FP16', 'NEON_VPFV4', 'ASIMD', 'ASIMDHP', 'ASIMDDP', 'ASIMDFHM'] * On aarch64: ['ASIMDHP', 'ASIMDDP', 'ASIMDFHM'] - * On ppc64: ['VSX', 'VSX2', 'VSX3'] - * On ppc64le: ['VSX3'] + * On ppc64: ['VSX', 'VSX2', 'VSX3', 'VSX4'] + * On ppc64le: ['VSX3', 'VSX4'] * On s390x: ['VX', 'VXE', VXE2] * On any other arch or if the optimization is disabled: [] */ diff --git a/numpy/core/src/common/npy_partition.h.src b/numpy/core/src/common/npy_partition.h.src index 72c2095f1..5ba652b41 100644 --- a/numpy/core/src/common/npy_partition.h.src +++ b/numpy/core/src/common/npy_partition.h.src @@ -28,6 +28,10 @@ #define NPY_MAX_PIVOT_STACK 50 +#ifdef __cplusplus +extern "C" { +#endif + /**begin repeat * * #TYPE = BOOL, BYTE, UBYTE, SHORT, USHORT, INT, UINT, LONG, ULONG, @@ -56,6 +60,10 @@ NPY_NO_EXPORT int aintroselect_@suff@(@type@ *v, npy_intp* tosort, npy_intp num, /**end repeat**/ +#ifdef __cplusplus +} +#endif + typedef struct { int typenum; PyArray_PartitionFunc * part[NPY_NSELECTS]; diff --git a/numpy/core/src/common/numpy_tag.h b/numpy/core/src/common/numpy_tag.h index 60e9b02cd..ee0c36cac 100644 --- a/numpy/core/src/common/numpy_tag.h +++ b/numpy/core/src/common/numpy_tag.h @@ -220,6 +220,40 @@ struct timedelta_tag : date_tag { } }; +struct string_tag { + using type = npy_char; + static constexpr NPY_TYPES type_value = NPY_STRING; + static int less(type const* a, type const* b, size_t len) { + return STRING_LT(a, b, len); + } + static int less_equal(type const* a, type const* b, size_t len) { + return !less(b, a, len); + } + static void swap(type* a, type* b, size_t len) { + STRING_SWAP(a, b, len); + } + static void copy(type * a, type const* b, size_t len) { + STRING_COPY(a, b, len); + } +}; + +struct unicode_tag { + using type = npy_ucs4; + static constexpr NPY_TYPES type_value = NPY_UNICODE; + static int less(type const* a, type const* b, size_t len) { + return UNICODE_LT(a, b, len); + } + static int less_equal(type const* a, type const* b, size_t len) { + return !less(b, a, len); + } + static void swap(type* a, type* b, size_t len) { + UNICODE_SWAP(a, b, len); + } + static void copy(type * a, type const* b, size_t len) { + UNICODE_COPY(a, b, len); + } +}; + } // namespace npy #endif diff --git a/numpy/core/src/common/simd/avx2/math.h b/numpy/core/src/common/simd/avx2/math.h index ec15e50e1..deaf4ad11 100644 --- a/numpy/core/src/common/simd/avx2/math.h +++ b/numpy/core/src/common/simd/avx2/math.h @@ -42,7 +42,7 @@ NPY_FINLINE npyv_f64 npyv_square_f64(npyv_f64 a) #define npyv_max_f64 _mm256_max_pd // Maximum, supports IEEE floating-point arithmetic (IEC 60559), // - If one of the two vectors contains NaN, the equivalent element of the other vector is set -// - Only if both corresponded elements are NaN, NaN is set. +// - Only if both corresponded elements are NaN, NaN is set. NPY_FINLINE npyv_f32 npyv_maxp_f32(npyv_f32 a, npyv_f32 b) { __m256 nn = _mm256_cmp_ps(b, b, _CMP_ORD_Q); @@ -76,7 +76,7 @@ NPY_FINLINE npyv_s64 npyv_max_s64(npyv_s64 a, npyv_s64 b) #define npyv_min_f64 _mm256_min_pd // Minimum, supports IEEE floating-point arithmetic (IEC 60559), // - If one of the two vectors contains NaN, the equivalent element of the other vector is set -// - Only if both corresponded elements are NaN, NaN is set. +// - Only if both corresponded elements are NaN, NaN is set. NPY_FINLINE npyv_f32 npyv_minp_f32(npyv_f32 a, npyv_f32 b) { __m256 nn = _mm256_cmp_ps(b, b, _CMP_ORD_Q); @@ -105,6 +105,10 @@ NPY_FINLINE npyv_s64 npyv_min_s64(npyv_s64 a, npyv_s64 b) return _mm256_blendv_epi8(a, b, _mm256_cmpgt_epi64(a, b)); } +// round to nearest intger even +#define npyv_rint_f32(A) _mm256_round_ps(A, _MM_FROUND_TO_NEAREST_INT) +#define npyv_rint_f64(A) _mm256_round_pd(A, _MM_FROUND_TO_NEAREST_INT) + // ceil #define npyv_ceil_f32 _mm256_ceil_ps #define npyv_ceil_f64 _mm256_ceil_pd @@ -113,4 +117,8 @@ NPY_FINLINE npyv_s64 npyv_min_s64(npyv_s64 a, npyv_s64 b) #define npyv_trunc_f32(A) _mm256_round_ps(A, _MM_FROUND_TO_ZERO) #define npyv_trunc_f64(A) _mm256_round_pd(A, _MM_FROUND_TO_ZERO) +// floor +#define npyv_floor_f32 _mm256_floor_ps +#define npyv_floor_f64 _mm256_floor_pd + #endif // _NPY_SIMD_AVX2_MATH_H diff --git a/numpy/core/src/common/simd/avx512/math.h b/numpy/core/src/common/simd/avx512/math.h index f30e50ad0..5a6cb6dcd 100644 --- a/numpy/core/src/common/simd/avx512/math.h +++ b/numpy/core/src/common/simd/avx512/math.h @@ -51,7 +51,7 @@ NPY_FINLINE npyv_f64 npyv_square_f64(npyv_f64 a) #define npyv_max_f64 _mm512_max_pd // Maximum, supports IEEE floating-point arithmetic (IEC 60559), // - If one of the two vectors contains NaN, the equivalent element of the other vector is set -// - Only if both corresponded elements are NaN, NaN is set. +// - Only if both corresponded elements are NaN, NaN is set. NPY_FINLINE npyv_f32 npyv_maxp_f32(npyv_f32 a, npyv_f32 b) { __mmask16 nn = _mm512_cmp_ps_mask(b, b, _CMP_ORD_Q); @@ -84,7 +84,7 @@ NPY_FINLINE npyv_f64 npyv_maxp_f64(npyv_f64 a, npyv_f64 b) #define npyv_min_f64 _mm512_min_pd // Minimum, supports IEEE floating-point arithmetic (IEC 60559), // - If one of the two vectors contains NaN, the equivalent element of the other vector is set -// - Only if both corresponded elements are NaN, NaN is set. +// - Only if both corresponded elements are NaN, NaN is set. NPY_FINLINE npyv_f32 npyv_minp_f32(npyv_f32 a, npyv_f32 b) { __mmask16 nn = _mm512_cmp_ps_mask(b, b, _CMP_ORD_Q); @@ -112,6 +112,10 @@ NPY_FINLINE npyv_f64 npyv_minp_f64(npyv_f64 a, npyv_f64 b) #define npyv_min_u64 _mm512_min_epu64 #define npyv_min_s64 _mm512_min_epi64 +// round to nearest integer even +#define npyv_rint_f32(A) _mm512_roundscale_ps(A, _MM_FROUND_TO_NEAREST_INT) +#define npyv_rint_f64(A) _mm512_roundscale_pd(A, _MM_FROUND_TO_NEAREST_INT) + // ceil #define npyv_ceil_f32(A) _mm512_roundscale_ps(A, _MM_FROUND_TO_POS_INF) #define npyv_ceil_f64(A) _mm512_roundscale_pd(A, _MM_FROUND_TO_POS_INF) @@ -120,4 +124,8 @@ NPY_FINLINE npyv_f64 npyv_minp_f64(npyv_f64 a, npyv_f64 b) #define npyv_trunc_f32(A) _mm512_roundscale_ps(A, _MM_FROUND_TO_ZERO) #define npyv_trunc_f64(A) _mm512_roundscale_pd(A, _MM_FROUND_TO_ZERO) +// floor +#define npyv_floor_f32(A) _mm512_roundscale_ps(A, _MM_FROUND_TO_NEG_INF) +#define npyv_floor_f64(A) _mm512_roundscale_pd(A, _MM_FROUND_TO_NEG_INF) + #endif // _NPY_SIMD_AVX512_MATH_H diff --git a/numpy/core/src/common/simd/intdiv.h b/numpy/core/src/common/simd/intdiv.h index a7a461721..42f022c55 100644 --- a/numpy/core/src/common/simd/intdiv.h +++ b/numpy/core/src/common/simd/intdiv.h @@ -136,7 +136,7 @@ NPY_FINLINE npy_uint64 npyv__divh128_u64(npy_uint64 high, npy_uint64 divisor) { assert(divisor > 1); npy_uint64 quotient; -#if defined(_M_X64) && defined(_MSC_VER) && _MSC_VER >= 1920 +#if defined(_M_X64) && defined(_MSC_VER) && _MSC_VER >= 1920 && !defined(__clang__) npy_uint64 remainder; quotient = _udiv128(high, 0, divisor, &remainder); (void)remainder; diff --git a/numpy/core/src/common/simd/neon/math.h b/numpy/core/src/common/simd/neon/math.h index 19e5cd846..4607d6f27 100644 --- a/numpy/core/src/common/simd/neon/math.h +++ b/numpy/core/src/common/simd/neon/math.h @@ -153,6 +153,33 @@ NPY_FINLINE npyv_s64 npyv_min_s64(npyv_s64 a, npyv_s64 b) return vbslq_s64(npyv_cmplt_s64(a, b), a, b); } +// round to nearest integer even +NPY_FINLINE npyv_f32 npyv_rint_f32(npyv_f32 a) +{ +#ifdef NPY_HAVE_ASIMD + return vrndnq_f32(a); +#else + // ARMv7 NEON only supports fp to int truncate conversion. + // a magic trick of adding 1.5 * 2**23 is used for rounding + // to nearest even and then substract this magic number to get + // the integer. + const npyv_s32 szero = vreinterpretq_s32_f32(vdupq_n_f32(-0.0f)); + const npyv_f32 magic = vdupq_n_f32(12582912.0f); // 1.5 * 2**23 + npyv_f32 round = vsubq_f32(vaddq_f32(a, magic), magic); + npyv_b32 overflow = vcleq_f32(vabsq_f32(a), vreinterpretq_f32_u32(vdupq_n_u32(0x4b000000))); + round = vbslq_f32(overflow, round, a); + // signed zero + round = vreinterpretq_f32_s32(vorrq_s32( + vreinterpretq_s32_f32(round), + vandq_s32(vreinterpretq_s32_f32(a), szero) + )); + return round; +#endif +} +#if NPY_SIMD_F64 + #define npyv_rint_f64 vrndnq_f64 +#endif // NPY_SIMD_F64 + // ceil #ifdef NPY_HAVE_ASIMD #define npyv_ceil_f32 vrndpq_f32 @@ -223,4 +250,36 @@ NPY_FINLINE npyv_s64 npyv_min_s64(npyv_s64 a, npyv_s64 b) #define npyv_trunc_f64 vrndq_f64 #endif // NPY_SIMD_F64 +// floor +#ifdef NPY_HAVE_ASIMD + #define npyv_floor_f32 vrndmq_f32 +#else + NPY_FINLINE npyv_f32 npyv_floor_f32(npyv_f32 a) + { + const npyv_s32 szero = vreinterpretq_s32_f32(vdupq_n_f32(-0.0f)); + const npyv_u32 one = vreinterpretq_u32_f32(vdupq_n_f32(1.0f)); + const npyv_s32 max_int = vdupq_n_s32(0x7fffffff); + + npyv_s32 roundi = vcvtq_s32_f32(a); + npyv_f32 round = vcvtq_f32_s32(roundi); + npyv_f32 floor = vsubq_f32(round, vreinterpretq_f32_u32( + vandq_u32(vcgtq_f32(round, a), one) + )); + // respect signed zero + npyv_f32 rzero = vreinterpretq_f32_s32(vorrq_s32( + vreinterpretq_s32_f32(floor), + vandq_s32(vreinterpretq_s32_f32(a), szero) + )); + npyv_u32 nnan = npyv_notnan_f32(a); + npyv_u32 overflow = vorrq_u32( + vceqq_s32(roundi, szero), vceqq_s32(roundi, max_int) + ); + + return vbslq_f32(vbicq_u32(nnan, overflow), rzero, a); + } +#endif // NPY_HAVE_ASIMD +#if NPY_SIMD_F64 + #define npyv_floor_f64 vrndmq_f64 +#endif // NPY_SIMD_F64 + #endif // _NPY_SIMD_NEON_MATH_H diff --git a/numpy/core/src/common/simd/sse/math.h b/numpy/core/src/common/simd/sse/math.h index 5daf7711e..e4b77b671 100644 --- a/numpy/core/src/common/simd/sse/math.h +++ b/numpy/core/src/common/simd/sse/math.h @@ -42,7 +42,7 @@ NPY_FINLINE npyv_f64 npyv_square_f64(npyv_f64 a) #define npyv_max_f64 _mm_max_pd // Maximum, supports IEEE floating-point arithmetic (IEC 60559), // - If one of the two vectors contains NaN, the equivalent element of the other vector is set -// - Only if both corresponded elements are NaN, NaN is set. +// - Only if both corresponded elements are NaN, NaN is set. NPY_FINLINE npyv_f32 npyv_maxp_f32(npyv_f32 a, npyv_f32 b) { __m128 nn = _mm_cmpord_ps(b, b); @@ -95,7 +95,7 @@ NPY_FINLINE npyv_s64 npyv_max_s64(npyv_s64 a, npyv_s64 b) #define npyv_min_f64 _mm_min_pd // Minimum, supports IEEE floating-point arithmetic (IEC 60559), // - If one of the two vectors contains NaN, the equivalent element of the other vector is set -// - Only if both corresponded elements are NaN, NaN is set. +// - Only if both corresponded elements are NaN, NaN is set. NPY_FINLINE npyv_f32 npyv_minp_f32(npyv_f32 a, npyv_f32 b) { __m128 nn = _mm_cmpord_ps(b, b); @@ -143,6 +143,38 @@ NPY_FINLINE npyv_s64 npyv_min_s64(npyv_s64 a, npyv_s64 b) return npyv_select_s64(npyv_cmplt_s64(a, b), a, b); } +// round to nearest integer even +NPY_FINLINE npyv_f32 npyv_rint_f32(npyv_f32 a) +{ +#ifdef NPY_HAVE_SSE41 + return _mm_round_ps(a, _MM_FROUND_TO_NEAREST_INT); +#else + const npyv_f32 szero = _mm_set1_ps(-0.0f); + __m128i roundi = _mm_cvtps_epi32(a); + __m128i overflow = _mm_cmpeq_epi32(roundi, _mm_castps_si128(szero)); + __m128 r = _mm_cvtepi32_ps(roundi); + // respect sign of zero + r = _mm_or_ps(r, _mm_and_ps(a, szero)); + return npyv_select_f32(overflow, a, r); +#endif +} + +// round to nearest integer even +NPY_FINLINE npyv_f64 npyv_rint_f64(npyv_f64 a) +{ +#ifdef NPY_HAVE_SSE41 + return _mm_round_pd(a, _MM_FROUND_TO_NEAREST_INT); +#else + const npyv_f64 szero = _mm_set1_pd(-0.0); + const npyv_f64 two_power_52 = _mm_set1_pd(0x10000000000000); + npyv_f64 sign_two52 = _mm_or_pd(two_power_52, _mm_and_pd(a, szero)); + // round by add magic number 2^52 + npyv_f64 round = _mm_sub_pd(_mm_add_pd(a, sign_two52), sign_two52); + // respect signed zero, e.g. -0.5 -> -0.0 + return _mm_or_pd(round, _mm_and_pd(a, szero)); +#endif +} + // ceil #ifdef NPY_HAVE_SSE41 #define npyv_ceil_f32 _mm_ceil_ps @@ -202,4 +234,23 @@ NPY_FINLINE npyv_s64 npyv_min_s64(npyv_s64 a, npyv_s64 b) } #endif +// floor +#ifdef NPY_HAVE_SSE41 + #define npyv_floor_f32 _mm_floor_ps + #define npyv_floor_f64 _mm_floor_pd +#else + NPY_FINLINE npyv_f32 npyv_floor_f32(npyv_f32 a) + { + const npyv_f32 one = _mm_set1_ps(1.0f); + npyv_f32 round = npyv_rint_f32(a); + return _mm_sub_ps(round, _mm_and_ps(_mm_cmpgt_ps(round, a), one)); + } + NPY_FINLINE npyv_f64 npyv_floor_f64(npyv_f64 a) + { + const npyv_f64 one = _mm_set1_pd(1.0); + npyv_f64 round = npyv_rint_f64(a); + return _mm_sub_pd(round, _mm_and_pd(_mm_cmpgt_pd(round, a), one)); + } +#endif // NPY_HAVE_SSE41 + #endif // _NPY_SIMD_SSE_MATH_H diff --git a/numpy/core/src/common/simd/vsx/math.h b/numpy/core/src/common/simd/vsx/math.h index d138cae8a..444bc9e54 100644 --- a/numpy/core/src/common/simd/vsx/math.h +++ b/numpy/core/src/common/simd/vsx/math.h @@ -38,7 +38,7 @@ NPY_FINLINE npyv_f64 npyv_square_f64(npyv_f64 a) #define npyv_max_f64 vec_max // Maximum, supports IEEE floating-point arithmetic (IEC 60559), // - If one of the two vectors contains NaN, the equivalent element of the other vector is set -// - Only if both corresponded elements are NaN, NaN is set. +// - Only if both corresponded elements are NaN, NaN is set. #define npyv_maxp_f32 vec_max #define npyv_maxp_f64 vec_max // Maximum, integer operations @@ -56,7 +56,7 @@ NPY_FINLINE npyv_f64 npyv_square_f64(npyv_f64 a) #define npyv_min_f64 vec_min // Minimum, supports IEEE floating-point arithmetic (IEC 60559), // - If one of the two vectors contains NaN, the equivalent element of the other vector is set -// - Only if both corresponded elements are NaN, NaN is set. +// - Only if both corresponded elements are NaN, NaN is set. #define npyv_minp_f32 vec_min #define npyv_minp_f64 vec_min // Minimum, integer operations @@ -69,6 +69,10 @@ NPY_FINLINE npyv_f64 npyv_square_f64(npyv_f64 a) #define npyv_min_u64 vec_min #define npyv_min_s64 vec_min +// round to nearest int even +#define npyv_rint_f32 vec_rint +#define npyv_rint_f64 vec_rint + // ceil #define npyv_ceil_f32 vec_ceil #define npyv_ceil_f64 vec_ceil @@ -77,4 +81,8 @@ NPY_FINLINE npyv_f64 npyv_square_f64(npyv_f64 a) #define npyv_trunc_f32 vec_trunc #define npyv_trunc_f64 vec_trunc +// floor +#define npyv_floor_f32 vec_floor +#define npyv_floor_f64 vec_floor + #endif // _NPY_SIMD_VSX_MATH_H diff --git a/numpy/core/src/multiarray/arraytypes.c.src b/numpy/core/src/multiarray/arraytypes.c.src index 71808cc48..71401c60e 100644 --- a/numpy/core/src/multiarray/arraytypes.c.src +++ b/numpy/core/src/multiarray/arraytypes.c.src @@ -2849,7 +2849,7 @@ static int #define LT(a,b) ((a) < (b) || ((b) != (b) && (a) ==(a))) static int -@TYPE@_compare(@type@ *pa, @type@ *pb) +@TYPE@_compare(@type@ *pa, @type@ *pb, PyArrayObject *NPY_UNUSED(ap)) { const @type@ a = *pa; const @type@ b = *pb; @@ -2869,7 +2869,7 @@ static int static int -C@TYPE@_compare(@type@ *pa, @type@ *pb) +C@TYPE@_compare(@type@ *pa, @type@ *pb, PyArrayObject *NPY_UNUSED(ap)) { const @type@ ar = pa[0]; const @type@ ai = pa[1]; @@ -2924,7 +2924,7 @@ C@TYPE@_compare(@type@ *pa, @type@ *pb) */ static int -@TYPE@_compare(@type@ *pa, @type@ *pb) +@TYPE@_compare(@type@ *pa, @type@ *pb, PyArrayObject *NPY_UNUSED(ap)) { const @type@ a = *pa; const @type@ b = *pb; diff --git a/numpy/core/src/multiarray/common_dtype.c b/numpy/core/src/multiarray/common_dtype.c index ca80b1ed7..3561a905a 100644 --- a/numpy/core/src/multiarray/common_dtype.c +++ b/numpy/core/src/multiarray/common_dtype.c @@ -41,7 +41,7 @@ * @param dtype2 Second DType class. * @return The common DType or NULL with an error set */ -NPY_NO_EXPORT NPY_INLINE PyArray_DTypeMeta * +NPY_NO_EXPORT PyArray_DTypeMeta * PyArray_CommonDType(PyArray_DTypeMeta *dtype1, PyArray_DTypeMeta *dtype2) { if (dtype1 == dtype2) { diff --git a/numpy/core/src/npysort/heapsort.c.src b/numpy/core/src/npysort/heapsort.c.src deleted file mode 100644 index 4bfea1388..000000000 --- a/numpy/core/src/npysort/heapsort.c.src +++ /dev/null @@ -1,402 +0,0 @@ -/* -*- c -*- */ - -/* - * The purpose of this module is to add faster sort functions - * that are type-specific. This is done by altering the - * function table for the builtin descriptors. - * - * These sorting functions are copied almost directly from numarray - * with a few modifications (complex comparisons compare the imaginary - * part if the real parts are equal, for example), and the names - * are changed. - * - * The original sorting code is due to Charles R. Harris who wrote - * it for numarray. - */ - -/* - * Quick sort is usually the fastest, but the worst case scenario can - * be slower than the merge and heap sorts. The merge sort requires - * extra memory and so for large arrays may not be useful. - * - * The merge sort is *stable*, meaning that equal components - * are unmoved from their entry versions, so it can be used to - * implement lexigraphic sorting on multiple keys. - * - * The heap sort is included for completeness. - */ - -#define NPY_NO_DEPRECATED_API NPY_API_VERSION - -#include "npy_sort.h" -#include "npysort_common.h" -#include <stdlib.h> - -#define NOT_USED NPY_UNUSED(unused) -#define PYA_QS_STACK 100 -#define SMALL_QUICKSORT 15 -#define SMALL_MERGESORT 20 -#define SMALL_STRING 16 - - -/* - ***************************************************************************** - ** NUMERIC SORTS ** - ***************************************************************************** - */ - - -/**begin repeat - * - * #TYPE = BOOL, BYTE, UBYTE, SHORT, USHORT, INT, UINT, LONG, ULONG, - * LONGLONG, ULONGLONG, HALF, FLOAT, DOUBLE, LONGDOUBLE, - * CFLOAT, CDOUBLE, CLONGDOUBLE, DATETIME, TIMEDELTA# - * #suff = bool, byte, ubyte, short, ushort, int, uint, long, ulong, - * longlong, ulonglong, half, float, double, longdouble, - * cfloat, cdouble, clongdouble, datetime, timedelta# - * #type = npy_bool, npy_byte, npy_ubyte, npy_short, npy_ushort, npy_int, - * npy_uint, npy_long, npy_ulong, npy_longlong, npy_ulonglong, - * npy_ushort, npy_float, npy_double, npy_longdouble, npy_cfloat, - * npy_cdouble, npy_clongdouble, npy_datetime, npy_timedelta# - */ - -NPY_NO_EXPORT int -heapsort_@suff@(void *start, npy_intp n, void *NOT_USED) -{ - @type@ tmp, *a; - npy_intp i,j,l; - - /* The array needs to be offset by one for heapsort indexing */ - a = (@type@ *)start - 1; - - for (l = n>>1; l > 0; --l) { - tmp = a[l]; - for (i = l, j = l<<1; j <= n;) { - if (j < n && @TYPE@_LT(a[j], a[j+1])) { - j += 1; - } - if (@TYPE@_LT(tmp, a[j])) { - a[i] = a[j]; - i = j; - j += j; - } - else { - break; - } - } - a[i] = tmp; - } - - for (; n > 1;) { - tmp = a[n]; - a[n] = a[1]; - n -= 1; - for (i = 1, j = 2; j <= n;) { - if (j < n && @TYPE@_LT(a[j], a[j+1])) { - j++; - } - if (@TYPE@_LT(tmp, a[j])) { - a[i] = a[j]; - i = j; - j += j; - } - else { - break; - } - } - a[i] = tmp; - } - - return 0; -} - - -NPY_NO_EXPORT int -aheapsort_@suff@(void *vv, npy_intp *tosort, npy_intp n, void *NOT_USED) -{ - @type@ *v = vv; - npy_intp *a, i,j,l, tmp; - /* The arrays need to be offset by one for heapsort indexing */ - a = tosort - 1; - - for (l = n>>1; l > 0; --l) { - tmp = a[l]; - for (i = l, j = l<<1; j <= n;) { - if (j < n && @TYPE@_LT(v[a[j]], v[a[j+1]])) { - j += 1; - } - if (@TYPE@_LT(v[tmp], v[a[j]])) { - a[i] = a[j]; - i = j; - j += j; - } - else { - break; - } - } - a[i] = tmp; - } - - for (; n > 1;) { - tmp = a[n]; - a[n] = a[1]; - n -= 1; - for (i = 1, j = 2; j <= n;) { - if (j < n && @TYPE@_LT(v[a[j]], v[a[j+1]])) { - j++; - } - if (@TYPE@_LT(v[tmp], v[a[j]])) { - a[i] = a[j]; - i = j; - j += j; - } - else { - break; - } - } - a[i] = tmp; - } - - return 0; -} - -/**end repeat**/ - - -/* - ***************************************************************************** - ** STRING SORTS ** - ***************************************************************************** - */ - - -/**begin repeat - * - * #TYPE = STRING, UNICODE# - * #suff = string, unicode# - * #type = npy_char, npy_ucs4# - */ - -NPY_NO_EXPORT int -heapsort_@suff@(void *start, npy_intp n, void *varr) -{ - PyArrayObject *arr = varr; - size_t len = PyArray_ITEMSIZE(arr)/sizeof(@type@); - @type@ *tmp = malloc(PyArray_ITEMSIZE(arr)); - @type@ *a = (@type@ *)start - len; - npy_intp i, j, l; - - if (tmp == NULL) { - return -NPY_ENOMEM; - } - - for (l = n>>1; l > 0; --l) { - @TYPE@_COPY(tmp, a + l*len, len); - for (i = l, j = l<<1; j <= n;) { - if (j < n && @TYPE@_LT(a + j*len, a + (j+1)*len, len)) - j += 1; - if (@TYPE@_LT(tmp, a + j*len, len)) { - @TYPE@_COPY(a + i*len, a + j*len, len); - i = j; - j += j; - } - else { - break; - } - } - @TYPE@_COPY(a + i*len, tmp, len); - } - - for (; n > 1;) { - @TYPE@_COPY(tmp, a + n*len, len); - @TYPE@_COPY(a + n*len, a + len, len); - n -= 1; - for (i = 1, j = 2; j <= n;) { - if (j < n && @TYPE@_LT(a + j*len, a + (j+1)*len, len)) - j++; - if (@TYPE@_LT(tmp, a + j*len, len)) { - @TYPE@_COPY(a + i*len, a + j*len, len); - i = j; - j += j; - } - else { - break; - } - } - @TYPE@_COPY(a + i*len, tmp, len); - } - - free(tmp); - return 0; -} - - -NPY_NO_EXPORT int -aheapsort_@suff@(void *vv, npy_intp *tosort, npy_intp n, void *varr) -{ - @type@ *v = vv; - PyArrayObject *arr = varr; - size_t len = PyArray_ITEMSIZE(arr)/sizeof(@type@); - npy_intp *a, i,j,l, tmp; - - /* The array needs to be offset by one for heapsort indexing */ - a = tosort - 1; - - for (l = n>>1; l > 0; --l) { - tmp = a[l]; - for (i = l, j = l<<1; j <= n;) { - if (j < n && @TYPE@_LT(v + a[j]*len, v + a[j+1]*len, len)) - j += 1; - if (@TYPE@_LT(v + tmp*len, v + a[j]*len, len)) { - a[i] = a[j]; - i = j; - j += j; - } - else { - break; - } - } - a[i] = tmp; - } - - for (; n > 1;) { - tmp = a[n]; - a[n] = a[1]; - n -= 1; - for (i = 1, j = 2; j <= n;) { - if (j < n && @TYPE@_LT(v + a[j]*len, v + a[j+1]*len, len)) - j++; - if (@TYPE@_LT(v + tmp*len, v + a[j]*len, len)) { - a[i] = a[j]; - i = j; - j += j; - } - else { - break; - } - } - a[i] = tmp; - } - - return 0; -} - -/**end repeat**/ - - -/* - ***************************************************************************** - ** GENERIC SORT ** - ***************************************************************************** - */ - - -NPY_NO_EXPORT int -npy_heapsort(void *start, npy_intp num, void *varr) -{ - PyArrayObject *arr = varr; - npy_intp elsize = PyArray_ITEMSIZE(arr); - PyArray_CompareFunc *cmp = PyArray_DESCR(arr)->f->compare; - char *tmp = malloc(elsize); - char *a = (char *)start - elsize; - npy_intp i, j, l; - - if (tmp == NULL) { - return -NPY_ENOMEM; - } - - for (l = num >> 1; l > 0; --l) { - GENERIC_COPY(tmp, a + l*elsize, elsize); - for (i = l, j = l << 1; j <= num;) { - if (j < num && cmp(a + j*elsize, a + (j+1)*elsize, arr) < 0) { - ++j; - } - if (cmp(tmp, a + j*elsize, arr) < 0) { - GENERIC_COPY(a + i*elsize, a + j*elsize, elsize); - i = j; - j += j; - } - else { - break; - } - } - GENERIC_COPY(a + i*elsize, tmp, elsize); - } - - for (; num > 1;) { - GENERIC_COPY(tmp, a + num*elsize, elsize); - GENERIC_COPY(a + num*elsize, a + elsize, elsize); - num -= 1; - for (i = 1, j = 2; j <= num;) { - if (j < num && cmp(a + j*elsize, a + (j+1)*elsize, arr) < 0) { - ++j; - } - if (cmp(tmp, a + j*elsize, arr) < 0) { - GENERIC_COPY(a + i*elsize, a + j*elsize, elsize); - i = j; - j += j; - } - else { - break; - } - } - GENERIC_COPY(a + i*elsize, tmp, elsize); - } - - free(tmp); - return 0; -} - - -NPY_NO_EXPORT int -npy_aheapsort(void *vv, npy_intp *tosort, npy_intp n, void *varr) -{ - char *v = vv; - PyArrayObject *arr = varr; - npy_intp elsize = PyArray_ITEMSIZE(arr); - PyArray_CompareFunc *cmp = PyArray_DESCR(arr)->f->compare; - npy_intp *a, i, j, l, tmp; - - /* The array needs to be offset by one for heapsort indexing */ - a = tosort - 1; - - for (l = n >> 1; l > 0; --l) { - tmp = a[l]; - for (i = l, j = l<<1; j <= n;) { - if (j < n && cmp(v + a[j]*elsize, v + a[j+1]*elsize, arr) < 0) { - ++j; - } - if (cmp(v + tmp*elsize, v + a[j]*elsize, arr) < 0) { - a[i] = a[j]; - i = j; - j += j; - } - else { - break; - } - } - a[i] = tmp; - } - - for (; n > 1;) { - tmp = a[n]; - a[n] = a[1]; - n -= 1; - for (i = 1, j = 2; j <= n;) { - if (j < n && cmp(v + a[j]*elsize, v + a[j+1]*elsize, arr) < 0) { - ++j; - } - if (cmp(v + tmp*elsize, v + a[j]*elsize, arr) < 0) { - a[i] = a[j]; - i = j; - j += j; - } - else { - break; - } - } - a[i] = tmp; - } - - return 0; -} diff --git a/numpy/core/src/npysort/heapsort.cpp b/numpy/core/src/npysort/heapsort.cpp new file mode 100644 index 000000000..3893bf5c2 --- /dev/null +++ b/numpy/core/src/npysort/heapsort.cpp @@ -0,0 +1,619 @@ +/* -*- c -*- */ + +/* + * The purpose of this module is to add faster sort functions + * that are type-specific. This is done by altering the + * function table for the builtin descriptors. + * + * These sorting functions are copied almost directly from numarray + * with a few modifications (complex comparisons compare the imaginary + * part if the real parts are equal, for example), and the names + * are changed. + * + * The original sorting code is due to Charles R. Harris who wrote + * it for numarray. + */ + +/* + * Quick sort is usually the fastest, but the worst case scenario can + * be slower than the merge and heap sorts. The merge sort requires + * extra memory and so for large arrays may not be useful. + * + * The merge sort is *stable*, meaning that equal components + * are unmoved from their entry versions, so it can be used to + * implement lexigraphic sorting on multiple keys. + * + * The heap sort is included for completeness. + */ + +#define NPY_NO_DEPRECATED_API NPY_API_VERSION + +#include "npy_sort.h" +#include "npysort_common.h" +#include "numpy_tag.h" + +#include <cstdlib> + +#define NOT_USED NPY_UNUSED(unused) +#define PYA_QS_STACK 100 +#define SMALL_QUICKSORT 15 +#define SMALL_MERGESORT 20 +#define SMALL_STRING 16 + +/* + ***************************************************************************** + ** NUMERIC SORTS ** + ***************************************************************************** + */ + +template <typename Tag, typename type> +static int +heapsort_(type *start, npy_intp n) +{ + type tmp, *a; + npy_intp i, j, l; + + /* The array needs to be offset by one for heapsort indexing */ + a = start - 1; + + for (l = n >> 1; l > 0; --l) { + tmp = a[l]; + for (i = l, j = l << 1; j <= n;) { + if (j < n && Tag::less(a[j], a[j + 1])) { + j += 1; + } + if (Tag::less(tmp, a[j])) { + a[i] = a[j]; + i = j; + j += j; + } + else { + break; + } + } + a[i] = tmp; + } + + for (; n > 1;) { + tmp = a[n]; + a[n] = a[1]; + n -= 1; + for (i = 1, j = 2; j <= n;) { + if (j < n && Tag::less(a[j], a[j + 1])) { + j++; + } + if (Tag::less(tmp, a[j])) { + a[i] = a[j]; + i = j; + j += j; + } + else { + break; + } + } + a[i] = tmp; + } + + return 0; +} + +template <typename Tag, typename type> +static int +aheapsort_(type *vv, npy_intp *tosort, npy_intp n) +{ + type *v = vv; + npy_intp *a, i, j, l, tmp; + /* The arrays need to be offset by one for heapsort indexing */ + a = tosort - 1; + + for (l = n >> 1; l > 0; --l) { + tmp = a[l]; + for (i = l, j = l << 1; j <= n;) { + if (j < n && Tag::less(v[a[j]], v[a[j + 1]])) { + j += 1; + } + if (Tag::less(v[tmp], v[a[j]])) { + a[i] = a[j]; + i = j; + j += j; + } + else { + break; + } + } + a[i] = tmp; + } + + for (; n > 1;) { + tmp = a[n]; + a[n] = a[1]; + n -= 1; + for (i = 1, j = 2; j <= n;) { + if (j < n && Tag::less(v[a[j]], v[a[j + 1]])) { + j++; + } + if (Tag::less(v[tmp], v[a[j]])) { + a[i] = a[j]; + i = j; + j += j; + } + else { + break; + } + } + a[i] = tmp; + } + + return 0; +} + +/* + ***************************************************************************** + ** STRING SORTS ** + ***************************************************************************** + */ + +template <typename Tag, typename type> +NPY_NO_EXPORT int +string_heapsort_(type *start, npy_intp n, void *varr) +{ + PyArrayObject *arr = (PyArrayObject *)varr; + size_t len = PyArray_ITEMSIZE(arr) / sizeof(type); + type *tmp = (type *)malloc(PyArray_ITEMSIZE(arr)); + type *a = (type *)start - len; + npy_intp i, j, l; + + if (tmp == NULL) { + return -NPY_ENOMEM; + } + + for (l = n >> 1; l > 0; --l) { + Tag::copy(tmp, a + l * len, len); + for (i = l, j = l << 1; j <= n;) { + if (j < n && Tag::less(a + j * len, a + (j + 1) * len, len)) + j += 1; + if (Tag::less(tmp, a + j * len, len)) { + Tag::copy(a + i * len, a + j * len, len); + i = j; + j += j; + } + else { + break; + } + } + Tag::copy(a + i * len, tmp, len); + } + + for (; n > 1;) { + Tag::copy(tmp, a + n * len, len); + Tag::copy(a + n * len, a + len, len); + n -= 1; + for (i = 1, j = 2; j <= n;) { + if (j < n && Tag::less(a + j * len, a + (j + 1) * len, len)) + j++; + if (Tag::less(tmp, a + j * len, len)) { + Tag::copy(a + i * len, a + j * len, len); + i = j; + j += j; + } + else { + break; + } + } + Tag::copy(a + i * len, tmp, len); + } + + free(tmp); + return 0; +} + +template <typename Tag, typename type> +NPY_NO_EXPORT int +string_aheapsort_(type *vv, npy_intp *tosort, npy_intp n, void *varr) +{ + type *v = vv; + PyArrayObject *arr = (PyArrayObject *)varr; + size_t len = PyArray_ITEMSIZE(arr) / sizeof(type); + npy_intp *a, i, j, l, tmp; + + /* The array needs to be offset by one for heapsort indexing */ + a = tosort - 1; + + for (l = n >> 1; l > 0; --l) { + tmp = a[l]; + for (i = l, j = l << 1; j <= n;) { + if (j < n && Tag::less(v + a[j] * len, v + a[j + 1] * len, len)) + j += 1; + if (Tag::less(v + tmp * len, v + a[j] * len, len)) { + a[i] = a[j]; + i = j; + j += j; + } + else { + break; + } + } + a[i] = tmp; + } + + for (; n > 1;) { + tmp = a[n]; + a[n] = a[1]; + n -= 1; + for (i = 1, j = 2; j <= n;) { + if (j < n && Tag::less(v + a[j] * len, v + a[j + 1] * len, len)) + j++; + if (Tag::less(v + tmp * len, v + a[j] * len, len)) { + a[i] = a[j]; + i = j; + j += j; + } + else { + break; + } + } + a[i] = tmp; + } + + return 0; +} + +/**end repeat**/ + +/* + ***************************************************************************** + ** GENERIC SORT ** + ***************************************************************************** + */ + +NPY_NO_EXPORT int +npy_heapsort(void *start, npy_intp num, void *varr) +{ + PyArrayObject *arr = (PyArrayObject *)varr; + npy_intp elsize = PyArray_ITEMSIZE(arr); + PyArray_CompareFunc *cmp = PyArray_DESCR(arr)->f->compare; + char *tmp = (char *)malloc(elsize); + char *a = (char *)start - elsize; + npy_intp i, j, l; + + if (tmp == NULL) { + return -NPY_ENOMEM; + } + + for (l = num >> 1; l > 0; --l) { + GENERIC_COPY(tmp, a + l * elsize, elsize); + for (i = l, j = l << 1; j <= num;) { + if (j < num && + cmp(a + j * elsize, a + (j + 1) * elsize, arr) < 0) { + ++j; + } + if (cmp(tmp, a + j * elsize, arr) < 0) { + GENERIC_COPY(a + i * elsize, a + j * elsize, elsize); + i = j; + j += j; + } + else { + break; + } + } + GENERIC_COPY(a + i * elsize, tmp, elsize); + } + + for (; num > 1;) { + GENERIC_COPY(tmp, a + num * elsize, elsize); + GENERIC_COPY(a + num * elsize, a + elsize, elsize); + num -= 1; + for (i = 1, j = 2; j <= num;) { + if (j < num && + cmp(a + j * elsize, a + (j + 1) * elsize, arr) < 0) { + ++j; + } + if (cmp(tmp, a + j * elsize, arr) < 0) { + GENERIC_COPY(a + i * elsize, a + j * elsize, elsize); + i = j; + j += j; + } + else { + break; + } + } + GENERIC_COPY(a + i * elsize, tmp, elsize); + } + + free(tmp); + return 0; +} + +NPY_NO_EXPORT int +npy_aheapsort(void *vv, npy_intp *tosort, npy_intp n, void *varr) +{ + char *v = (char *)vv; + PyArrayObject *arr = (PyArrayObject *)varr; + npy_intp elsize = PyArray_ITEMSIZE(arr); + PyArray_CompareFunc *cmp = PyArray_DESCR(arr)->f->compare; + npy_intp *a, i, j, l, tmp; + + /* The array needs to be offset by one for heapsort indexing */ + a = tosort - 1; + + for (l = n >> 1; l > 0; --l) { + tmp = a[l]; + for (i = l, j = l << 1; j <= n;) { + if (j < n && + cmp(v + a[j] * elsize, v + a[j + 1] * elsize, arr) < 0) { + ++j; + } + if (cmp(v + tmp * elsize, v + a[j] * elsize, arr) < 0) { + a[i] = a[j]; + i = j; + j += j; + } + else { + break; + } + } + a[i] = tmp; + } + + for (; n > 1;) { + tmp = a[n]; + a[n] = a[1]; + n -= 1; + for (i = 1, j = 2; j <= n;) { + if (j < n && + cmp(v + a[j] * elsize, v + a[j + 1] * elsize, arr) < 0) { + ++j; + } + if (cmp(v + tmp * elsize, v + a[j] * elsize, arr) < 0) { + a[i] = a[j]; + i = j; + j += j; + } + else { + break; + } + } + a[i] = tmp; + } + + return 0; +} + +/*************************************** + * C > C++ dispatch + ***************************************/ + +NPY_NO_EXPORT int +heapsort_bool(void *start, npy_intp n, void *NPY_UNUSED(varr)) +{ + return heapsort_<npy::bool_tag>((npy_bool *)start, n); +} +NPY_NO_EXPORT int +heapsort_byte(void *start, npy_intp n, void *NPY_UNUSED(varr)) +{ + return heapsort_<npy::byte_tag>((npy_byte *)start, n); +} +NPY_NO_EXPORT int +heapsort_ubyte(void *start, npy_intp n, void *NPY_UNUSED(varr)) +{ + return heapsort_<npy::ubyte_tag>((npy_ubyte *)start, n); +} +NPY_NO_EXPORT int +heapsort_short(void *start, npy_intp n, void *NPY_UNUSED(varr)) +{ + return heapsort_<npy::short_tag>((npy_short *)start, n); +} +NPY_NO_EXPORT int +heapsort_ushort(void *start, npy_intp n, void *NPY_UNUSED(varr)) +{ + return heapsort_<npy::ushort_tag>((npy_ushort *)start, n); +} +NPY_NO_EXPORT int +heapsort_int(void *start, npy_intp n, void *NPY_UNUSED(varr)) +{ + return heapsort_<npy::int_tag>((npy_int *)start, n); +} +NPY_NO_EXPORT int +heapsort_uint(void *start, npy_intp n, void *NPY_UNUSED(varr)) +{ + return heapsort_<npy::uint_tag>((npy_uint *)start, n); +} +NPY_NO_EXPORT int +heapsort_long(void *start, npy_intp n, void *NPY_UNUSED(varr)) +{ + return heapsort_<npy::long_tag>((npy_long *)start, n); +} +NPY_NO_EXPORT int +heapsort_ulong(void *start, npy_intp n, void *NPY_UNUSED(varr)) +{ + return heapsort_<npy::ulong_tag>((npy_ulong *)start, n); +} +NPY_NO_EXPORT int +heapsort_longlong(void *start, npy_intp n, void *NPY_UNUSED(varr)) +{ + return heapsort_<npy::longlong_tag>((npy_longlong *)start, n); +} +NPY_NO_EXPORT int +heapsort_ulonglong(void *start, npy_intp n, void *NPY_UNUSED(varr)) +{ + return heapsort_<npy::ulonglong_tag>((npy_ulonglong *)start, n); +} +NPY_NO_EXPORT int +heapsort_half(void *start, npy_intp n, void *NPY_UNUSED(varr)) +{ + return heapsort_<npy::half_tag>((npy_half *)start, n); +} +NPY_NO_EXPORT int +heapsort_float(void *start, npy_intp n, void *NPY_UNUSED(varr)) +{ + return heapsort_<npy::float_tag>((npy_float *)start, n); +} +NPY_NO_EXPORT int +heapsort_double(void *start, npy_intp n, void *NPY_UNUSED(varr)) +{ + return heapsort_<npy::double_tag>((npy_double *)start, n); +} +NPY_NO_EXPORT int +heapsort_longdouble(void *start, npy_intp n, void *NPY_UNUSED(varr)) +{ + return heapsort_<npy::longdouble_tag>((npy_longdouble *)start, n); +} +NPY_NO_EXPORT int +heapsort_cfloat(void *start, npy_intp n, void *NPY_UNUSED(varr)) +{ + return heapsort_<npy::cfloat_tag>((npy_cfloat *)start, n); +} +NPY_NO_EXPORT int +heapsort_cdouble(void *start, npy_intp n, void *NPY_UNUSED(varr)) +{ + return heapsort_<npy::cdouble_tag>((npy_cdouble *)start, n); +} +NPY_NO_EXPORT int +heapsort_clongdouble(void *start, npy_intp n, void *NPY_UNUSED(varr)) +{ + return heapsort_<npy::clongdouble_tag>((npy_clongdouble *)start, n); +} +NPY_NO_EXPORT int +heapsort_datetime(void *start, npy_intp n, void *NPY_UNUSED(varr)) +{ + return heapsort_<npy::datetime_tag>((npy_datetime *)start, n); +} +NPY_NO_EXPORT int +heapsort_timedelta(void *start, npy_intp n, void *NPY_UNUSED(varr)) +{ + return heapsort_<npy::timedelta_tag>((npy_timedelta *)start, n); +} + +NPY_NO_EXPORT int +aheapsort_bool(void *vv, npy_intp *tosort, npy_intp n, void *NPY_UNUSED(varr)) +{ + return aheapsort_<npy::bool_tag>((npy_bool *)vv, tosort, n); +} +NPY_NO_EXPORT int +aheapsort_byte(void *vv, npy_intp *tosort, npy_intp n, void *NPY_UNUSED(varr)) +{ + return aheapsort_<npy::byte_tag>((npy_byte *)vv, tosort, n); +} +NPY_NO_EXPORT int +aheapsort_ubyte(void *vv, npy_intp *tosort, npy_intp n, void *NPY_UNUSED(varr)) +{ + return aheapsort_<npy::ubyte_tag>((npy_ubyte *)vv, tosort, n); +} +NPY_NO_EXPORT int +aheapsort_short(void *vv, npy_intp *tosort, npy_intp n, void *NPY_UNUSED(varr)) +{ + return aheapsort_<npy::short_tag>((npy_short *)vv, tosort, n); +} +NPY_NO_EXPORT int +aheapsort_ushort(void *vv, npy_intp *tosort, npy_intp n, + void *NPY_UNUSED(varr)) +{ + return aheapsort_<npy::ushort_tag>((npy_ushort *)vv, tosort, n); +} +NPY_NO_EXPORT int +aheapsort_int(void *vv, npy_intp *tosort, npy_intp n, void *NPY_UNUSED(varr)) +{ + return aheapsort_<npy::int_tag>((npy_int *)vv, tosort, n); +} +NPY_NO_EXPORT int +aheapsort_uint(void *vv, npy_intp *tosort, npy_intp n, void *NPY_UNUSED(varr)) +{ + return aheapsort_<npy::uint_tag>((npy_uint *)vv, tosort, n); +} +NPY_NO_EXPORT int +aheapsort_long(void *vv, npy_intp *tosort, npy_intp n, void *NPY_UNUSED(varr)) +{ + return aheapsort_<npy::long_tag>((npy_long *)vv, tosort, n); +} +NPY_NO_EXPORT int +aheapsort_ulong(void *vv, npy_intp *tosort, npy_intp n, void *NPY_UNUSED(varr)) +{ + return aheapsort_<npy::ulong_tag>((npy_ulong *)vv, tosort, n); +} +NPY_NO_EXPORT int +aheapsort_longlong(void *vv, npy_intp *tosort, npy_intp n, + void *NPY_UNUSED(varr)) +{ + return aheapsort_<npy::longlong_tag>((npy_longlong *)vv, tosort, n); +} +NPY_NO_EXPORT int +aheapsort_ulonglong(void *vv, npy_intp *tosort, npy_intp n, + void *NPY_UNUSED(varr)) +{ + return aheapsort_<npy::ulonglong_tag>((npy_ulonglong *)vv, tosort, n); +} +NPY_NO_EXPORT int +aheapsort_half(void *vv, npy_intp *tosort, npy_intp n, void *NPY_UNUSED(varr)) +{ + return aheapsort_<npy::half_tag>((npy_half *)vv, tosort, n); +} +NPY_NO_EXPORT int +aheapsort_float(void *vv, npy_intp *tosort, npy_intp n, void *NPY_UNUSED(varr)) +{ + return aheapsort_<npy::float_tag>((npy_float *)vv, tosort, n); +} +NPY_NO_EXPORT int +aheapsort_double(void *vv, npy_intp *tosort, npy_intp n, + void *NPY_UNUSED(varr)) +{ + return aheapsort_<npy::double_tag>((npy_double *)vv, tosort, n); +} +NPY_NO_EXPORT int +aheapsort_longdouble(void *vv, npy_intp *tosort, npy_intp n, + void *NPY_UNUSED(varr)) +{ + return aheapsort_<npy::longdouble_tag>((npy_longdouble *)vv, tosort, n); +} +NPY_NO_EXPORT int +aheapsort_cfloat(void *vv, npy_intp *tosort, npy_intp n, + void *NPY_UNUSED(varr)) +{ + return aheapsort_<npy::cfloat_tag>((npy_cfloat *)vv, tosort, n); +} +NPY_NO_EXPORT int +aheapsort_cdouble(void *vv, npy_intp *tosort, npy_intp n, + void *NPY_UNUSED(varr)) +{ + return aheapsort_<npy::cdouble_tag>((npy_cdouble *)vv, tosort, n); +} +NPY_NO_EXPORT int +aheapsort_clongdouble(void *vv, npy_intp *tosort, npy_intp n, + void *NPY_UNUSED(varr)) +{ + return aheapsort_<npy::clongdouble_tag>((npy_clongdouble *)vv, tosort, n); +} +NPY_NO_EXPORT int +aheapsort_datetime(void *vv, npy_intp *tosort, npy_intp n, + void *NPY_UNUSED(varr)) +{ + return aheapsort_<npy::datetime_tag>((npy_datetime *)vv, tosort, n); +} +NPY_NO_EXPORT int +aheapsort_timedelta(void *vv, npy_intp *tosort, npy_intp n, + void *NPY_UNUSED(varr)) +{ + return aheapsort_<npy::timedelta_tag>((npy_timedelta *)vv, tosort, n); +} + +NPY_NO_EXPORT int +heapsort_string(void *start, npy_intp n, void *varr) +{ + return string_heapsort_<npy::string_tag>((npy_char *)start, n, varr); +} +NPY_NO_EXPORT int +heapsort_unicode(void *start, npy_intp n, void *varr) +{ + return string_heapsort_<npy::unicode_tag>((npy_ucs4 *)start, n, varr); +} + +NPY_NO_EXPORT int +aheapsort_string(void *vv, npy_intp *tosort, npy_intp n, void *varr) +{ + return string_aheapsort_<npy::string_tag>((npy_char *)vv, tosort, n, varr); +} +NPY_NO_EXPORT int +aheapsort_unicode(void *vv, npy_intp *tosort, npy_intp n, void *varr) +{ + return string_aheapsort_<npy::unicode_tag>((npy_ucs4 *)vv, tosort, n, + varr); +} diff --git a/numpy/core/src/npysort/mergesort.c.src b/numpy/core/src/npysort/mergesort.c.src deleted file mode 100644 index f83fbf758..000000000 --- a/numpy/core/src/npysort/mergesort.c.src +++ /dev/null @@ -1,511 +0,0 @@ -/* -*- c -*- */ - -/* - * The purpose of this module is to add faster sort functions - * that are type-specific. This is done by altering the - * function table for the builtin descriptors. - * - * These sorting functions are copied almost directly from numarray - * with a few modifications (complex comparisons compare the imaginary - * part if the real parts are equal, for example), and the names - * are changed. - * - * The original sorting code is due to Charles R. Harris who wrote - * it for numarray. - */ - -/* - * Quick sort is usually the fastest, but the worst case scenario can - * be slower than the merge and heap sorts. The merge sort requires - * extra memory and so for large arrays may not be useful. - * - * The merge sort is *stable*, meaning that equal components - * are unmoved from their entry versions, so it can be used to - * implement lexigraphic sorting on multiple keys. - * - * The heap sort is included for completeness. - */ - -#define NPY_NO_DEPRECATED_API NPY_API_VERSION - -#include "npy_sort.h" -#include "npysort_common.h" -#include <stdlib.h> - -#define NOT_USED NPY_UNUSED(unused) -#define PYA_QS_STACK 100 -#define SMALL_QUICKSORT 15 -#define SMALL_MERGESORT 20 -#define SMALL_STRING 16 - - -/* - ***************************************************************************** - ** NUMERIC SORTS ** - ***************************************************************************** - */ - - -/**begin repeat - * - * #TYPE = BOOL, BYTE, UBYTE, SHORT, USHORT, INT, UINT, LONG, ULONG, - * LONGLONG, ULONGLONG, HALF, FLOAT, DOUBLE, LONGDOUBLE, - * CFLOAT, CDOUBLE, CLONGDOUBLE, DATETIME, TIMEDELTA# - * #suff = bool, byte, ubyte, short, ushort, int, uint, long, ulong, - * longlong, ulonglong, half, float, double, longdouble, - * cfloat, cdouble, clongdouble, datetime, timedelta# - * #type = npy_bool, npy_byte, npy_ubyte, npy_short, npy_ushort, npy_int, - * npy_uint, npy_long, npy_ulong, npy_longlong, npy_ulonglong, - * npy_ushort, npy_float, npy_double, npy_longdouble, npy_cfloat, - * npy_cdouble, npy_clongdouble, npy_datetime, npy_timedelta# - */ - -static void -mergesort0_@suff@(@type@ *pl, @type@ *pr, @type@ *pw) -{ - @type@ vp, *pi, *pj, *pk, *pm; - - if (pr - pl > SMALL_MERGESORT) { - /* merge sort */ - pm = pl + ((pr - pl) >> 1); - mergesort0_@suff@(pl, pm, pw); - mergesort0_@suff@(pm, pr, pw); - for (pi = pw, pj = pl; pj < pm;) { - *pi++ = *pj++; - } - pi = pw + (pm - pl); - pj = pw; - pk = pl; - while (pj < pi && pm < pr) { - if (@TYPE@_LT(*pm, *pj)) { - *pk++ = *pm++; - } - else { - *pk++ = *pj++; - } - } - while(pj < pi) { - *pk++ = *pj++; - } - } - else { - /* insertion sort */ - for (pi = pl + 1; pi < pr; ++pi) { - vp = *pi; - pj = pi; - pk = pi - 1; - while (pj > pl && @TYPE@_LT(vp, *pk)) { - *pj-- = *pk--; - } - *pj = vp; - } - } -} - - -NPY_NO_EXPORT int -mergesort_@suff@(void *start, npy_intp num, void *NOT_USED) -{ - @type@ *pl, *pr, *pw; - - pl = start; - pr = pl + num; - pw = malloc((num/2) * sizeof(@type@)); - if (pw == NULL) { - return -NPY_ENOMEM; - } - mergesort0_@suff@(pl, pr, pw); - - free(pw); - return 0; -} - - -static void -amergesort0_@suff@(npy_intp *pl, npy_intp *pr, @type@ *v, npy_intp *pw) -{ - @type@ vp; - npy_intp vi, *pi, *pj, *pk, *pm; - - if (pr - pl > SMALL_MERGESORT) { - /* merge sort */ - pm = pl + ((pr - pl) >> 1); - amergesort0_@suff@(pl, pm, v, pw); - amergesort0_@suff@(pm, pr, v, pw); - for (pi = pw, pj = pl; pj < pm;) { - *pi++ = *pj++; - } - pi = pw + (pm - pl); - pj = pw; - pk = pl; - while (pj < pi && pm < pr) { - if (@TYPE@_LT(v[*pm], v[*pj])) { - *pk++ = *pm++; - } - else { - *pk++ = *pj++; - } - } - while(pj < pi) { - *pk++ = *pj++; - } - } - else { - /* insertion sort */ - for (pi = pl + 1; pi < pr; ++pi) { - vi = *pi; - vp = v[vi]; - pj = pi; - pk = pi - 1; - while (pj > pl && @TYPE@_LT(vp, v[*pk])) { - *pj-- = *pk--; - } - *pj = vi; - } - } -} - - -NPY_NO_EXPORT int -amergesort_@suff@(void *v, npy_intp *tosort, npy_intp num, void *NOT_USED) -{ - npy_intp *pl, *pr, *pw; - - pl = tosort; - pr = pl + num; - pw = malloc((num/2) * sizeof(npy_intp)); - if (pw == NULL) { - return -NPY_ENOMEM; - } - amergesort0_@suff@(pl, pr, v, pw); - free(pw); - - return 0; -} - -/**end repeat**/ - - -/* - ***************************************************************************** - ** STRING SORTS ** - ***************************************************************************** - */ - - -/**begin repeat - * - * #TYPE = STRING, UNICODE# - * #suff = string, unicode# - * #type = npy_char, npy_ucs4# - */ - -static void -mergesort0_@suff@(@type@ *pl, @type@ *pr, @type@ *pw, @type@ *vp, size_t len) -{ - @type@ *pi, *pj, *pk, *pm; - - if ((size_t)(pr - pl) > SMALL_MERGESORT*len) { - /* merge sort */ - pm = pl + (((pr - pl)/len) >> 1)*len; - mergesort0_@suff@(pl, pm, pw, vp, len); - mergesort0_@suff@(pm, pr, pw, vp, len); - @TYPE@_COPY(pw, pl, pm - pl); - pi = pw + (pm - pl); - pj = pw; - pk = pl; - while (pj < pi && pm < pr) { - if (@TYPE@_LT(pm, pj, len)) { - @TYPE@_COPY(pk, pm, len); - pm += len; - pk += len; - } - else { - @TYPE@_COPY(pk, pj, len); - pj += len; - pk += len; - } - } - @TYPE@_COPY(pk, pj, pi - pj); - } - else { - /* insertion sort */ - for (pi = pl + len; pi < pr; pi += len) { - @TYPE@_COPY(vp, pi, len); - pj = pi; - pk = pi - len; - while (pj > pl && @TYPE@_LT(vp, pk, len)) { - @TYPE@_COPY(pj, pk, len); - pj -= len; - pk -= len; - } - @TYPE@_COPY(pj, vp, len); - } - } -} - - -NPY_NO_EXPORT int -mergesort_@suff@(void *start, npy_intp num, void *varr) -{ - PyArrayObject *arr = varr; - size_t elsize = PyArray_ITEMSIZE(arr); - size_t len = elsize / sizeof(@type@); - @type@ *pl, *pr, *pw, *vp; - int err = 0; - - /* Items that have zero size don't make sense to sort */ - if (elsize == 0) { - return 0; - } - - pl = start; - pr = pl + num*len; - pw = malloc((num/2) * elsize); - if (pw == NULL) { - err = -NPY_ENOMEM; - goto fail_0; - } - vp = malloc(elsize); - if (vp == NULL) { - err = -NPY_ENOMEM; - goto fail_1; - } - mergesort0_@suff@(pl, pr, pw, vp, len); - - free(vp); -fail_1: - free(pw); -fail_0: - return err; -} - - -static void -amergesort0_@suff@(npy_intp *pl, npy_intp *pr, @type@ *v, npy_intp *pw, size_t len) -{ - @type@ *vp; - npy_intp vi, *pi, *pj, *pk, *pm; - - if (pr - pl > SMALL_MERGESORT) { - /* merge sort */ - pm = pl + ((pr - pl) >> 1); - amergesort0_@suff@(pl, pm, v, pw, len); - amergesort0_@suff@(pm, pr, v, pw, len); - for (pi = pw, pj = pl; pj < pm;) { - *pi++ = *pj++; - } - pi = pw + (pm - pl); - pj = pw; - pk = pl; - while (pj < pi && pm < pr) { - if (@TYPE@_LT(v + (*pm)*len, v + (*pj)*len, len)) { - *pk++ = *pm++; - } - else { - *pk++ = *pj++; - } - } - while (pj < pi) { - *pk++ = *pj++; - } - } - else { - /* insertion sort */ - for (pi = pl + 1; pi < pr; ++pi) { - vi = *pi; - vp = v + vi*len; - pj = pi; - pk = pi - 1; - while (pj > pl && @TYPE@_LT(vp, v + (*pk)*len, len)) { - *pj-- = *pk--; - } - *pj = vi; - } - } -} - - -NPY_NO_EXPORT int -amergesort_@suff@(void *v, npy_intp *tosort, npy_intp num, void *varr) -{ - PyArrayObject *arr = varr; - size_t elsize = PyArray_ITEMSIZE(arr); - size_t len = elsize / sizeof(@type@); - npy_intp *pl, *pr, *pw; - - /* Items that have zero size don't make sense to sort */ - if (elsize == 0) { - return 0; - } - - pl = tosort; - pr = pl + num; - pw = malloc((num/2) * sizeof(npy_intp)); - if (pw == NULL) { - return -NPY_ENOMEM; - } - amergesort0_@suff@(pl, pr, v, pw, len); - free(pw); - - return 0; -} - -/**end repeat**/ - - -/* - ***************************************************************************** - ** GENERIC SORT ** - ***************************************************************************** - */ - - -static void -npy_mergesort0(char *pl, char *pr, char *pw, char *vp, npy_intp elsize, - PyArray_CompareFunc *cmp, PyArrayObject *arr) -{ - char *pi, *pj, *pk, *pm; - - if (pr - pl > SMALL_MERGESORT*elsize) { - /* merge sort */ - pm = pl + (((pr - pl)/elsize) >> 1)*elsize; - npy_mergesort0(pl, pm, pw, vp, elsize, cmp, arr); - npy_mergesort0(pm, pr, pw, vp, elsize, cmp, arr); - GENERIC_COPY(pw, pl, pm - pl); - pi = pw + (pm - pl); - pj = pw; - pk = pl; - while (pj < pi && pm < pr) { - if (cmp(pm, pj, arr) < 0) { - GENERIC_COPY(pk, pm, elsize); - pm += elsize; - pk += elsize; - } - else { - GENERIC_COPY(pk, pj, elsize); - pj += elsize; - pk += elsize; - } - } - GENERIC_COPY(pk, pj, pi - pj); - } - else { - /* insertion sort */ - for (pi = pl + elsize; pi < pr; pi += elsize) { - GENERIC_COPY(vp, pi, elsize); - pj = pi; - pk = pi - elsize; - while (pj > pl && cmp(vp, pk, arr) < 0) { - GENERIC_COPY(pj, pk, elsize); - pj -= elsize; - pk -= elsize; - } - GENERIC_COPY(pj, vp, elsize); - } - } -} - - -NPY_NO_EXPORT int -npy_mergesort(void *start, npy_intp num, void *varr) -{ - PyArrayObject *arr = varr; - npy_intp elsize = PyArray_ITEMSIZE(arr); - PyArray_CompareFunc *cmp = PyArray_DESCR(arr)->f->compare; - char *pl = start; - char *pr = pl + num*elsize; - char *pw; - char *vp; - int err = -NPY_ENOMEM; - - /* Items that have zero size don't make sense to sort */ - if (elsize == 0) { - return 0; - } - - pw = malloc((num >> 1) *elsize); - vp = malloc(elsize); - - if (pw != NULL && vp != NULL) { - npy_mergesort0(pl, pr, pw, vp, elsize, cmp, arr); - err = 0; - } - - free(vp); - free(pw); - - return err; -} - - -static void -npy_amergesort0(npy_intp *pl, npy_intp *pr, char *v, npy_intp *pw, - npy_intp elsize, PyArray_CompareFunc *cmp, PyArrayObject *arr) -{ - char *vp; - npy_intp vi, *pi, *pj, *pk, *pm; - - if (pr - pl > SMALL_MERGESORT) { - /* merge sort */ - pm = pl + ((pr - pl) >> 1); - npy_amergesort0(pl, pm, v, pw, elsize, cmp, arr); - npy_amergesort0(pm, pr, v, pw, elsize, cmp, arr); - for (pi = pw, pj = pl; pj < pm;) { - *pi++ = *pj++; - } - pi = pw + (pm - pl); - pj = pw; - pk = pl; - while (pj < pi && pm < pr) { - if (cmp(v + (*pm)*elsize, v + (*pj)*elsize, arr) < 0) { - *pk++ = *pm++; - } - else { - *pk++ = *pj++; - } - } - while (pj < pi) { - *pk++ = *pj++; - } - } - else { - /* insertion sort */ - for (pi = pl + 1; pi < pr; ++pi) { - vi = *pi; - vp = v + vi*elsize; - pj = pi; - pk = pi - 1; - while (pj > pl && cmp(vp, v + (*pk)*elsize, arr) < 0) { - *pj-- = *pk--; - } - *pj = vi; - } - } -} - - -NPY_NO_EXPORT int -npy_amergesort(void *v, npy_intp *tosort, npy_intp num, void *varr) -{ - PyArrayObject *arr = varr; - npy_intp elsize = PyArray_ITEMSIZE(arr); - PyArray_CompareFunc *cmp = PyArray_DESCR(arr)->f->compare; - npy_intp *pl, *pr, *pw; - - /* Items that have zero size don't make sense to sort */ - if (elsize == 0) { - return 0; - } - - pl = tosort; - pr = pl + num; - pw = malloc((num >> 1) * sizeof(npy_intp)); - if (pw == NULL) { - return -NPY_ENOMEM; - } - npy_amergesort0(pl, pr, v, pw, elsize, cmp, arr); - free(pw); - - return 0; -} diff --git a/numpy/core/src/npysort/mergesort.cpp b/numpy/core/src/npysort/mergesort.cpp new file mode 100644 index 000000000..f892dd185 --- /dev/null +++ b/numpy/core/src/npysort/mergesort.cpp @@ -0,0 +1,733 @@ +/* -*- c -*- */ + +/* + * The purpose of this module is to add faster sort functions + * that are type-specific. This is done by altering the + * function table for the builtin descriptors. + * + * These sorting functions are copied almost directly from numarray + * with a few modifications (complex comparisons compare the imaginary + * part if the real parts are equal, for example), and the names + * are changed. + * + * The original sorting code is due to Charles R. Harris who wrote + * it for numarray. + */ + +/* + * Quick sort is usually the fastest, but the worst case scenario can + * be slower than the merge and heap sorts. The merge sort requires + * extra memory and so for large arrays may not be useful. + * + * The merge sort is *stable*, meaning that equal components + * are unmoved from their entry versions, so it can be used to + * implement lexigraphic sorting on multiple keys. + * + * The heap sort is included for completeness. + */ + +#define NPY_NO_DEPRECATED_API NPY_API_VERSION + +#include "npy_sort.h" +#include "npysort_common.h" +#include "numpy_tag.h" + +#include <cstdlib> + +#define NOT_USED NPY_UNUSED(unused) +#define PYA_QS_STACK 100 +#define SMALL_QUICKSORT 15 +#define SMALL_MERGESORT 20 +#define SMALL_STRING 16 + +/* + ***************************************************************************** + ** NUMERIC SORTS ** + ***************************************************************************** + */ + +template <typename Tag, typename type> +static void +mergesort0_(type *pl, type *pr, type *pw) +{ + type vp, *pi, *pj, *pk, *pm; + + if (pr - pl > SMALL_MERGESORT) { + /* merge sort */ + pm = pl + ((pr - pl) >> 1); + mergesort0_<Tag>(pl, pm, pw); + mergesort0_<Tag>(pm, pr, pw); + for (pi = pw, pj = pl; pj < pm;) { + *pi++ = *pj++; + } + pi = pw + (pm - pl); + pj = pw; + pk = pl; + while (pj < pi && pm < pr) { + if (Tag::less(*pm, *pj)) { + *pk++ = *pm++; + } + else { + *pk++ = *pj++; + } + } + while (pj < pi) { + *pk++ = *pj++; + } + } + else { + /* insertion sort */ + for (pi = pl + 1; pi < pr; ++pi) { + vp = *pi; + pj = pi; + pk = pi - 1; + while (pj > pl && Tag::less(vp, *pk)) { + *pj-- = *pk--; + } + *pj = vp; + } + } +} + +template <typename Tag, typename type> +NPY_NO_EXPORT int +mergesort_(type *start, npy_intp num) +{ + type *pl, *pr, *pw; + + pl = start; + pr = pl + num; + pw = (type *)malloc((num / 2) * sizeof(type)); + if (pw == NULL) { + return -NPY_ENOMEM; + } + mergesort0_<Tag>(pl, pr, pw); + + free(pw); + return 0; +} + +template <typename Tag, typename type> +static void +amergesort0_(npy_intp *pl, npy_intp *pr, type *v, npy_intp *pw) +{ + type vp; + npy_intp vi, *pi, *pj, *pk, *pm; + + if (pr - pl > SMALL_MERGESORT) { + /* merge sort */ + pm = pl + ((pr - pl) >> 1); + amergesort0_<Tag>(pl, pm, v, pw); + amergesort0_<Tag>(pm, pr, v, pw); + for (pi = pw, pj = pl; pj < pm;) { + *pi++ = *pj++; + } + pi = pw + (pm - pl); + pj = pw; + pk = pl; + while (pj < pi && pm < pr) { + if (Tag::less(v[*pm], v[*pj])) { + *pk++ = *pm++; + } + else { + *pk++ = *pj++; + } + } + while (pj < pi) { + *pk++ = *pj++; + } + } + else { + /* insertion sort */ + for (pi = pl + 1; pi < pr; ++pi) { + vi = *pi; + vp = v[vi]; + pj = pi; + pk = pi - 1; + while (pj > pl && Tag::less(vp, v[*pk])) { + *pj-- = *pk--; + } + *pj = vi; + } + } +} + +template <typename Tag, typename type> +NPY_NO_EXPORT int +amergesort_(type *v, npy_intp *tosort, npy_intp num) +{ + npy_intp *pl, *pr, *pw; + + pl = tosort; + pr = pl + num; + pw = (npy_intp *)malloc((num / 2) * sizeof(npy_intp)); + if (pw == NULL) { + return -NPY_ENOMEM; + } + amergesort0_<Tag>(pl, pr, v, pw); + free(pw); + + return 0; +} + +/* + ***************************************************************************** + ** STRING SORTS ** + ***************************************************************************** + */ + +template <typename Tag, typename type> +static void +mergesort0_(type *pl, type *pr, type *pw, type *vp, size_t len) +{ + type *pi, *pj, *pk, *pm; + + if ((size_t)(pr - pl) > SMALL_MERGESORT * len) { + /* merge sort */ + pm = pl + (((pr - pl) / len) >> 1) * len; + mergesort0_<Tag>(pl, pm, pw, vp, len); + mergesort0_<Tag>(pm, pr, pw, vp, len); + Tag::copy(pw, pl, pm - pl); + pi = pw + (pm - pl); + pj = pw; + pk = pl; + while (pj < pi && pm < pr) { + if (Tag::less(pm, pj, len)) { + Tag::copy(pk, pm, len); + pm += len; + pk += len; + } + else { + Tag::copy(pk, pj, len); + pj += len; + pk += len; + } + } + Tag::copy(pk, pj, pi - pj); + } + else { + /* insertion sort */ + for (pi = pl + len; pi < pr; pi += len) { + Tag::copy(vp, pi, len); + pj = pi; + pk = pi - len; + while (pj > pl && Tag::less(vp, pk, len)) { + Tag::copy(pj, pk, len); + pj -= len; + pk -= len; + } + Tag::copy(pj, vp, len); + } + } +} + +template <typename Tag, typename type> +static int +string_mergesort_(type *start, npy_intp num, void *varr) +{ + PyArrayObject *arr = (PyArrayObject *)varr; + size_t elsize = PyArray_ITEMSIZE(arr); + size_t len = elsize / sizeof(type); + type *pl, *pr, *pw, *vp; + int err = 0; + + /* Items that have zero size don't make sense to sort */ + if (elsize == 0) { + return 0; + } + + pl = start; + pr = pl + num * len; + pw = (type *)malloc((num / 2) * elsize); + if (pw == NULL) { + err = -NPY_ENOMEM; + goto fail_0; + } + vp = (type *)malloc(elsize); + if (vp == NULL) { + err = -NPY_ENOMEM; + goto fail_1; + } + mergesort0_<Tag>(pl, pr, pw, vp, len); + + free(vp); +fail_1: + free(pw); +fail_0: + return err; +} + +template <typename Tag, typename type> +static void +amergesort0_(npy_intp *pl, npy_intp *pr, type *v, npy_intp *pw, size_t len) +{ + type *vp; + npy_intp vi, *pi, *pj, *pk, *pm; + + if (pr - pl > SMALL_MERGESORT) { + /* merge sort */ + pm = pl + ((pr - pl) >> 1); + amergesort0_<Tag>(pl, pm, v, pw, len); + amergesort0_<Tag>(pm, pr, v, pw, len); + for (pi = pw, pj = pl; pj < pm;) { + *pi++ = *pj++; + } + pi = pw + (pm - pl); + pj = pw; + pk = pl; + while (pj < pi && pm < pr) { + if (Tag::less(v + (*pm) * len, v + (*pj) * len, len)) { + *pk++ = *pm++; + } + else { + *pk++ = *pj++; + } + } + while (pj < pi) { + *pk++ = *pj++; + } + } + else { + /* insertion sort */ + for (pi = pl + 1; pi < pr; ++pi) { + vi = *pi; + vp = v + vi * len; + pj = pi; + pk = pi - 1; + while (pj > pl && Tag::less(vp, v + (*pk) * len, len)) { + *pj-- = *pk--; + } + *pj = vi; + } + } +} + +template <typename Tag, typename type> +static int +string_amergesort_(type *v, npy_intp *tosort, npy_intp num, void *varr) +{ + PyArrayObject *arr = (PyArrayObject *)varr; + size_t elsize = PyArray_ITEMSIZE(arr); + size_t len = elsize / sizeof(type); + npy_intp *pl, *pr, *pw; + + /* Items that have zero size don't make sense to sort */ + if (elsize == 0) { + return 0; + } + + pl = tosort; + pr = pl + num; + pw = (npy_intp *)malloc((num / 2) * sizeof(npy_intp)); + if (pw == NULL) { + return -NPY_ENOMEM; + } + amergesort0_<Tag>(pl, pr, v, pw, len); + free(pw); + + return 0; +} + +/* + ***************************************************************************** + ** GENERIC SORT ** + ***************************************************************************** + */ + +static void +npy_mergesort0(char *pl, char *pr, char *pw, char *vp, npy_intp elsize, + PyArray_CompareFunc *cmp, PyArrayObject *arr) +{ + char *pi, *pj, *pk, *pm; + + if (pr - pl > SMALL_MERGESORT * elsize) { + /* merge sort */ + pm = pl + (((pr - pl) / elsize) >> 1) * elsize; + npy_mergesort0(pl, pm, pw, vp, elsize, cmp, arr); + npy_mergesort0(pm, pr, pw, vp, elsize, cmp, arr); + GENERIC_COPY(pw, pl, pm - pl); + pi = pw + (pm - pl); + pj = pw; + pk = pl; + while (pj < pi && pm < pr) { + if (cmp(pm, pj, arr) < 0) { + GENERIC_COPY(pk, pm, elsize); + pm += elsize; + pk += elsize; + } + else { + GENERIC_COPY(pk, pj, elsize); + pj += elsize; + pk += elsize; + } + } + GENERIC_COPY(pk, pj, pi - pj); + } + else { + /* insertion sort */ + for (pi = pl + elsize; pi < pr; pi += elsize) { + GENERIC_COPY(vp, pi, elsize); + pj = pi; + pk = pi - elsize; + while (pj > pl && cmp(vp, pk, arr) < 0) { + GENERIC_COPY(pj, pk, elsize); + pj -= elsize; + pk -= elsize; + } + GENERIC_COPY(pj, vp, elsize); + } + } +} + +NPY_NO_EXPORT int +npy_mergesort(void *start, npy_intp num, void *varr) +{ + PyArrayObject *arr = (PyArrayObject *)varr; + npy_intp elsize = PyArray_ITEMSIZE(arr); + PyArray_CompareFunc *cmp = PyArray_DESCR(arr)->f->compare; + char *pl = (char *)start; + char *pr = pl + num * elsize; + char *pw; + char *vp; + int err = -NPY_ENOMEM; + + /* Items that have zero size don't make sense to sort */ + if (elsize == 0) { + return 0; + } + + pw = (char *)malloc((num >> 1) * elsize); + vp = (char *)malloc(elsize); + + if (pw != NULL && vp != NULL) { + npy_mergesort0(pl, pr, pw, vp, elsize, cmp, arr); + err = 0; + } + + free(vp); + free(pw); + + return err; +} + +static void +npy_amergesort0(npy_intp *pl, npy_intp *pr, char *v, npy_intp *pw, + npy_intp elsize, PyArray_CompareFunc *cmp, PyArrayObject *arr) +{ + char *vp; + npy_intp vi, *pi, *pj, *pk, *pm; + + if (pr - pl > SMALL_MERGESORT) { + /* merge sort */ + pm = pl + ((pr - pl) >> 1); + npy_amergesort0(pl, pm, v, pw, elsize, cmp, arr); + npy_amergesort0(pm, pr, v, pw, elsize, cmp, arr); + for (pi = pw, pj = pl; pj < pm;) { + *pi++ = *pj++; + } + pi = pw + (pm - pl); + pj = pw; + pk = pl; + while (pj < pi && pm < pr) { + if (cmp(v + (*pm) * elsize, v + (*pj) * elsize, arr) < 0) { + *pk++ = *pm++; + } + else { + *pk++ = *pj++; + } + } + while (pj < pi) { + *pk++ = *pj++; + } + } + else { + /* insertion sort */ + for (pi = pl + 1; pi < pr; ++pi) { + vi = *pi; + vp = v + vi * elsize; + pj = pi; + pk = pi - 1; + while (pj > pl && cmp(vp, v + (*pk) * elsize, arr) < 0) { + *pj-- = *pk--; + } + *pj = vi; + } + } +} + +NPY_NO_EXPORT int +npy_amergesort(void *v, npy_intp *tosort, npy_intp num, void *varr) +{ + PyArrayObject *arr = (PyArrayObject *)varr; + npy_intp elsize = PyArray_ITEMSIZE(arr); + PyArray_CompareFunc *cmp = PyArray_DESCR(arr)->f->compare; + npy_intp *pl, *pr, *pw; + + /* Items that have zero size don't make sense to sort */ + if (elsize == 0) { + return 0; + } + + pl = tosort; + pr = pl + num; + pw = (npy_intp *)malloc((num >> 1) * sizeof(npy_intp)); + if (pw == NULL) { + return -NPY_ENOMEM; + } + npy_amergesort0(pl, pr, (char *)v, pw, elsize, cmp, arr); + free(pw); + + return 0; +} + +/*************************************** + * C > C++ dispatch + ***************************************/ +NPY_NO_EXPORT int +mergesort_bool(void *start, npy_intp num, void *NPY_UNUSED(varr)) +{ + return mergesort_<npy::bool_tag>((npy_bool *)start, num); +} +NPY_NO_EXPORT int +mergesort_byte(void *start, npy_intp num, void *NPY_UNUSED(varr)) +{ + return mergesort_<npy::byte_tag>((npy_byte *)start, num); +} +NPY_NO_EXPORT int +mergesort_ubyte(void *start, npy_intp num, void *NPY_UNUSED(varr)) +{ + return mergesort_<npy::ubyte_tag>((npy_ubyte *)start, num); +} +NPY_NO_EXPORT int +mergesort_short(void *start, npy_intp num, void *NPY_UNUSED(varr)) +{ + return mergesort_<npy::short_tag>((npy_short *)start, num); +} +NPY_NO_EXPORT int +mergesort_ushort(void *start, npy_intp num, void *NPY_UNUSED(varr)) +{ + return mergesort_<npy::ushort_tag>((npy_ushort *)start, num); +} +NPY_NO_EXPORT int +mergesort_int(void *start, npy_intp num, void *NPY_UNUSED(varr)) +{ + return mergesort_<npy::int_tag>((npy_int *)start, num); +} +NPY_NO_EXPORT int +mergesort_uint(void *start, npy_intp num, void *NPY_UNUSED(varr)) +{ + return mergesort_<npy::uint_tag>((npy_uint *)start, num); +} +NPY_NO_EXPORT int +mergesort_long(void *start, npy_intp num, void *NPY_UNUSED(varr)) +{ + return mergesort_<npy::long_tag>((npy_long *)start, num); +} +NPY_NO_EXPORT int +mergesort_ulong(void *start, npy_intp num, void *NPY_UNUSED(varr)) +{ + return mergesort_<npy::ulong_tag>((npy_ulong *)start, num); +} +NPY_NO_EXPORT int +mergesort_longlong(void *start, npy_intp num, void *NPY_UNUSED(varr)) +{ + return mergesort_<npy::longlong_tag>((npy_longlong *)start, num); +} +NPY_NO_EXPORT int +mergesort_ulonglong(void *start, npy_intp num, void *NPY_UNUSED(varr)) +{ + return mergesort_<npy::ulonglong_tag>((npy_ulonglong *)start, num); +} +NPY_NO_EXPORT int +mergesort_half(void *start, npy_intp num, void *NPY_UNUSED(varr)) +{ + return mergesort_<npy::half_tag>((npy_half *)start, num); +} +NPY_NO_EXPORT int +mergesort_float(void *start, npy_intp num, void *NPY_UNUSED(varr)) +{ + return mergesort_<npy::float_tag>((npy_float *)start, num); +} +NPY_NO_EXPORT int +mergesort_double(void *start, npy_intp num, void *NPY_UNUSED(varr)) +{ + return mergesort_<npy::double_tag>((npy_double *)start, num); +} +NPY_NO_EXPORT int +mergesort_longdouble(void *start, npy_intp num, void *NPY_UNUSED(varr)) +{ + return mergesort_<npy::longdouble_tag>((npy_longdouble *)start, num); +} +NPY_NO_EXPORT int +mergesort_cfloat(void *start, npy_intp num, void *NPY_UNUSED(varr)) +{ + return mergesort_<npy::cfloat_tag>((npy_cfloat *)start, num); +} +NPY_NO_EXPORT int +mergesort_cdouble(void *start, npy_intp num, void *NPY_UNUSED(varr)) +{ + return mergesort_<npy::cdouble_tag>((npy_cdouble *)start, num); +} +NPY_NO_EXPORT int +mergesort_clongdouble(void *start, npy_intp num, void *NPY_UNUSED(varr)) +{ + return mergesort_<npy::clongdouble_tag>((npy_clongdouble *)start, num); +} +NPY_NO_EXPORT int +mergesort_datetime(void *start, npy_intp num, void *NPY_UNUSED(varr)) +{ + return mergesort_<npy::datetime_tag>((npy_datetime *)start, num); +} +NPY_NO_EXPORT int +mergesort_timedelta(void *start, npy_intp num, void *NPY_UNUSED(varr)) +{ + return mergesort_<npy::timedelta_tag>((npy_timedelta *)start, num); +} + +NPY_NO_EXPORT int +amergesort_bool(void *start, npy_intp *tosort, npy_intp num, + void *NPY_UNUSED(varr)) +{ + return amergesort_<npy::bool_tag>((npy_bool *)start, tosort, num); +} +NPY_NO_EXPORT int +amergesort_byte(void *start, npy_intp *tosort, npy_intp num, + void *NPY_UNUSED(varr)) +{ + return amergesort_<npy::byte_tag>((npy_byte *)start, tosort, num); +} +NPY_NO_EXPORT int +amergesort_ubyte(void *start, npy_intp *tosort, npy_intp num, + void *NPY_UNUSED(varr)) +{ + return amergesort_<npy::ubyte_tag>((npy_ubyte *)start, tosort, num); +} +NPY_NO_EXPORT int +amergesort_short(void *start, npy_intp *tosort, npy_intp num, + void *NPY_UNUSED(varr)) +{ + return amergesort_<npy::short_tag>((npy_short *)start, tosort, num); +} +NPY_NO_EXPORT int +amergesort_ushort(void *start, npy_intp *tosort, npy_intp num, + void *NPY_UNUSED(varr)) +{ + return amergesort_<npy::ushort_tag>((npy_ushort *)start, tosort, num); +} +NPY_NO_EXPORT int +amergesort_int(void *start, npy_intp *tosort, npy_intp num, + void *NPY_UNUSED(varr)) +{ + return amergesort_<npy::int_tag>((npy_int *)start, tosort, num); +} +NPY_NO_EXPORT int +amergesort_uint(void *start, npy_intp *tosort, npy_intp num, + void *NPY_UNUSED(varr)) +{ + return amergesort_<npy::uint_tag>((npy_uint *)start, tosort, num); +} +NPY_NO_EXPORT int +amergesort_long(void *start, npy_intp *tosort, npy_intp num, + void *NPY_UNUSED(varr)) +{ + return amergesort_<npy::long_tag>((npy_long *)start, tosort, num); +} +NPY_NO_EXPORT int +amergesort_ulong(void *start, npy_intp *tosort, npy_intp num, + void *NPY_UNUSED(varr)) +{ + return amergesort_<npy::ulong_tag>((npy_ulong *)start, tosort, num); +} +NPY_NO_EXPORT int +amergesort_longlong(void *start, npy_intp *tosort, npy_intp num, + void *NPY_UNUSED(varr)) +{ + return amergesort_<npy::longlong_tag>((npy_longlong *)start, tosort, num); +} +NPY_NO_EXPORT int +amergesort_ulonglong(void *start, npy_intp *tosort, npy_intp num, + void *NPY_UNUSED(varr)) +{ + return amergesort_<npy::ulonglong_tag>((npy_ulonglong *)start, tosort, + num); +} +NPY_NO_EXPORT int +amergesort_half(void *start, npy_intp *tosort, npy_intp num, + void *NPY_UNUSED(varr)) +{ + return amergesort_<npy::half_tag>((npy_half *)start, tosort, num); +} +NPY_NO_EXPORT int +amergesort_float(void *start, npy_intp *tosort, npy_intp num, + void *NPY_UNUSED(varr)) +{ + return amergesort_<npy::float_tag>((npy_float *)start, tosort, num); +} +NPY_NO_EXPORT int +amergesort_double(void *start, npy_intp *tosort, npy_intp num, + void *NPY_UNUSED(varr)) +{ + return amergesort_<npy::double_tag>((npy_double *)start, tosort, num); +} +NPY_NO_EXPORT int +amergesort_longdouble(void *start, npy_intp *tosort, npy_intp num, + void *NPY_UNUSED(varr)) +{ + return amergesort_<npy::longdouble_tag>((npy_longdouble *)start, tosort, + num); +} +NPY_NO_EXPORT int +amergesort_cfloat(void *start, npy_intp *tosort, npy_intp num, + void *NPY_UNUSED(varr)) +{ + return amergesort_<npy::cfloat_tag>((npy_cfloat *)start, tosort, num); +} +NPY_NO_EXPORT int +amergesort_cdouble(void *start, npy_intp *tosort, npy_intp num, + void *NPY_UNUSED(varr)) +{ + return amergesort_<npy::cdouble_tag>((npy_cdouble *)start, tosort, num); +} +NPY_NO_EXPORT int +amergesort_clongdouble(void *start, npy_intp *tosort, npy_intp num, + void *NPY_UNUSED(varr)) +{ + return amergesort_<npy::clongdouble_tag>((npy_clongdouble *)start, tosort, + num); +} +NPY_NO_EXPORT int +amergesort_datetime(void *start, npy_intp *tosort, npy_intp num, + void *NPY_UNUSED(varr)) +{ + return amergesort_<npy::datetime_tag>((npy_datetime *)start, tosort, num); +} +NPY_NO_EXPORT int +amergesort_timedelta(void *start, npy_intp *tosort, npy_intp num, + void *NPY_UNUSED(varr)) +{ + return amergesort_<npy::timedelta_tag>((npy_timedelta *)start, tosort, + num); +} + +NPY_NO_EXPORT int +mergesort_string(void *start, npy_intp num, void *varr) +{ + return string_mergesort_<npy::string_tag>((npy_char *)start, num, varr); +} +NPY_NO_EXPORT int +mergesort_unicode(void *start, npy_intp num, void *varr) +{ + return string_mergesort_<npy::unicode_tag>((npy_ucs4 *)start, num, varr); +} +NPY_NO_EXPORT int +amergesort_string(void *v, npy_intp *tosort, npy_intp num, void *varr) +{ + return string_amergesort_<npy::string_tag>((npy_char *)v, tosort, num, + varr); +} +NPY_NO_EXPORT int +amergesort_unicode(void *v, npy_intp *tosort, npy_intp num, void *varr) +{ + return string_amergesort_<npy::unicode_tag>((npy_ucs4 *)v, tosort, num, + varr); +} diff --git a/numpy/core/src/npysort/npysort_common.h b/numpy/core/src/npysort/npysort_common.h index a537fcd08..ab9f456b6 100644 --- a/numpy/core/src/npysort/npysort_common.h +++ b/numpy/core/src/npysort/npysort_common.h @@ -259,7 +259,7 @@ CLONGDOUBLE_LT(npy_clongdouble a, npy_clongdouble b) NPY_INLINE static void -STRING_COPY(char *s1, char *s2, size_t len) +STRING_COPY(char *s1, char const*s2, size_t len) { memcpy(s1, s2, len); } @@ -295,7 +295,7 @@ STRING_LT(const char *s1, const char *s2, size_t len) NPY_INLINE static void -UNICODE_COPY(npy_ucs4 *s1, npy_ucs4 *s2, size_t len) +UNICODE_COPY(npy_ucs4 *s1, npy_ucs4 const *s2, size_t len) { while(len--) { *s1++ = *s2++; diff --git a/numpy/core/src/npysort/selection.c.src b/numpy/core/src/npysort/selection.c.src deleted file mode 100644 index 0e285b320..000000000 --- a/numpy/core/src/npysort/selection.c.src +++ /dev/null @@ -1,419 +0,0 @@ -/* -*- c -*- */ - -/* - * - * The code is loosely based on the quickselect from - * Nicolas Devillard - 1998 public domain - * http://ndevilla.free.fr/median/median/ - * - * Quick select with median of 3 pivot is usually the fastest, - * but the worst case scenario can be quadratic complexity, - * e.g. np.roll(np.arange(x), x / 2) - * To avoid this if it recurses too much it falls back to the - * worst case linear median of median of group 5 pivot strategy. - */ - - -#define NPY_NO_DEPRECATED_API NPY_API_VERSION - -#include "npy_sort.h" -#include "npysort_common.h" -#include "numpy/npy_math.h" -#include "npy_partition.h" -#include <stdlib.h> - -#define NOT_USED NPY_UNUSED(unused) - - -/* - ***************************************************************************** - ** NUMERIC SORTS ** - ***************************************************************************** - */ - - -static NPY_INLINE void store_pivot(npy_intp pivot, npy_intp kth, - npy_intp * pivots, npy_intp * npiv) -{ - if (pivots == NULL) { - return; - } - - /* - * If pivot is the requested kth store it, overwriting other pivots if - * required. This must be done so iterative partition can work without - * manually shifting lower data offset by kth each time - */ - if (pivot == kth && *npiv == NPY_MAX_PIVOT_STACK) { - pivots[*npiv - 1] = pivot; - } - /* - * we only need pivots larger than current kth, larger pivots are not - * useful as partitions on smaller kth would reorder the stored pivots - */ - else if (pivot >= kth && *npiv < NPY_MAX_PIVOT_STACK) { - pivots[*npiv] = pivot; - (*npiv) += 1; - } -} - -/**begin repeat - * - * #TYPE = BOOL, BYTE, UBYTE, SHORT, USHORT, INT, UINT, LONG, ULONG, - * LONGLONG, ULONGLONG, HALF, FLOAT, DOUBLE, LONGDOUBLE, - * CFLOAT, CDOUBLE, CLONGDOUBLE# - * #suff = bool, byte, ubyte, short, ushort, int, uint, long, ulong, - * longlong, ulonglong, half, float, double, longdouble, - * cfloat, cdouble, clongdouble# - * #type = npy_bool, npy_byte, npy_ubyte, npy_short, npy_ushort, npy_int, - * npy_uint, npy_long, npy_ulong, npy_longlong, npy_ulonglong, - * npy_ushort, npy_float, npy_double, npy_longdouble, npy_cfloat, - * npy_cdouble, npy_clongdouble# - * #inexact = 0*11, 1*7# - */ - -static npy_intp -amedian_of_median5_@suff@(@type@ *v, npy_intp* tosort, const npy_intp num, - npy_intp * pivots, - npy_intp * npiv); - -static npy_intp -median_of_median5_@suff@(@type@ *v, const npy_intp num, - npy_intp * pivots, - npy_intp * npiv); - -/**begin repeat1 - * #name = , a# - * #idx = , tosort# - * #arg = 0, 1# - */ -#if @arg@ -/* helper macros to avoid duplication of direct/indirect selection */ -#define IDX(x) tosort[x] -#define SORTEE(x) tosort[x] -#define SWAP INTP_SWAP -#define MEDIAN3_SWAP(v, tosort, low, mid, high) \ - amedian3_swap_@suff@(v, tosort, low, mid, high) -#define MEDIAN5(v, tosort, subleft) \ - amedian5_@suff@(v, tosort + subleft) -#define UNGUARDED_PARTITION(v, tosort, pivot, ll, hh) \ - aunguarded_partition_@suff@(v, tosort, pivot, ll, hh) -#define INTROSELECT(v, tosort, num, kth, pivots, npiv) \ - aintroselect_@suff@(v, tosort, nmed, nmed / 2, pivots, npiv, NULL) -#define DUMBSELECT(v, tosort, left, num, kth) \ - adumb_select_@suff@(v, tosort + left, num, kth) -#else -#define IDX(x) (x) -#define SORTEE(x) v[x] -#define SWAP @TYPE@_SWAP -#define MEDIAN3_SWAP(v, tosort, low, mid, high) \ - median3_swap_@suff@(v, low, mid, high) -#define MEDIAN5(v, tosort, subleft) \ - median5_@suff@(v + subleft) -#define UNGUARDED_PARTITION(v, tosort, pivot, ll, hh) \ - unguarded_partition_@suff@(v, pivot, ll, hh) -#define INTROSELECT(v, tosort, num, kth, pivots, npiv) \ - introselect_@suff@(v, nmed, nmed / 2, pivots, npiv, NULL) -#define DUMBSELECT(v, tosort, left, num, kth) \ - dumb_select_@suff@(v + left, num, kth) -#endif - - -/* - * median of 3 pivot strategy - * gets min and median and moves median to low and min to low + 1 - * for efficient partitioning, see unguarded_partition - */ -static NPY_INLINE void -@name@median3_swap_@suff@(@type@ * v, -#if @arg@ - npy_intp * tosort, -#endif - npy_intp low, npy_intp mid, npy_intp high) -{ - if (@TYPE@_LT(v[IDX(high)], v[IDX(mid)])) - SWAP(SORTEE(high), SORTEE(mid)); - if (@TYPE@_LT(v[IDX(high)], v[IDX(low)])) - SWAP(SORTEE(high), SORTEE(low)); - /* move pivot to low */ - if (@TYPE@_LT(v[IDX(low)], v[IDX(mid)])) - SWAP(SORTEE(low), SORTEE(mid)); - /* move 3-lowest element to low + 1 */ - SWAP(SORTEE(mid), SORTEE(low + 1)); -} - - -/* select index of median of five elements */ -static npy_intp @name@median5_@suff@( -#if @arg@ - const @type@ * v, npy_intp * tosort -#else - @type@ * v -#endif - ) -{ - /* could be optimized as we only need the index (no swaps) */ - if (@TYPE@_LT(v[IDX(1)], v[IDX(0)])) { - SWAP(SORTEE(1), SORTEE(0)); - } - if (@TYPE@_LT(v[IDX(4)], v[IDX(3)])) { - SWAP(SORTEE(4), SORTEE(3)); - } - if (@TYPE@_LT(v[IDX(3)], v[IDX(0)])) { - SWAP(SORTEE(3), SORTEE(0)); - } - if (@TYPE@_LT(v[IDX(4)], v[IDX(1)])) { - SWAP(SORTEE(4), SORTEE(1)); - } - if (@TYPE@_LT(v[IDX(2)], v[IDX(1)])) { - SWAP(SORTEE(2), SORTEE(1)); - } - if (@TYPE@_LT(v[IDX(3)], v[IDX(2)])) { - if (@TYPE@_LT(v[IDX(3)], v[IDX(1)])) { - return 1; - } - else { - return 3; - } - } - else { - /* v[1] and v[2] swapped into order above */ - return 2; - } -} - - -/* - * partition and return the index were the pivot belongs - * the data must have following property to avoid bound checks: - * ll ... hh - * lower-than-pivot [x x x x] larger-than-pivot - */ -static NPY_INLINE void -@name@unguarded_partition_@suff@(@type@ * v, -#if @arg@ - npy_intp * tosort, -#endif - const @type@ pivot, - npy_intp * ll, npy_intp * hh) -{ - for (;;) { - do (*ll)++; while (@TYPE@_LT(v[IDX(*ll)], pivot)); - do (*hh)--; while (@TYPE@_LT(pivot, v[IDX(*hh)])); - - if (*hh < *ll) - break; - - SWAP(SORTEE(*ll), SORTEE(*hh)); - } -} - - -/* - * select median of median of blocks of 5 - * if used as partition pivot it splits the range into at least 30%/70% - * allowing linear time worstcase quickselect - */ -static npy_intp -@name@median_of_median5_@suff@(@type@ *v, -#if @arg@ - npy_intp* tosort, -#endif - const npy_intp num, - npy_intp * pivots, - npy_intp * npiv) -{ - npy_intp i, subleft; - npy_intp right = num - 1; - npy_intp nmed = (right + 1) / 5; - for (i = 0, subleft = 0; i < nmed; i++, subleft += 5) { - npy_intp m = MEDIAN5(v, tosort, subleft); - SWAP(SORTEE(subleft + m), SORTEE(i)); - } - - if (nmed > 2) - INTROSELECT(v, tosort, nmed, nmed / 2, pivots, npiv); - return nmed / 2; -} - - -/* - * N^2 selection, fast only for very small kth - * useful for close multiple partitions - * (e.g. even element median, interpolating percentile) - */ -static int -@name@dumb_select_@suff@(@type@ *v, -#if @arg@ - npy_intp * tosort, -#endif - npy_intp num, npy_intp kth) -{ - npy_intp i; - for (i = 0; i <= kth; i++) { - npy_intp minidx = i; - @type@ minval = v[IDX(i)]; - npy_intp k; - for (k = i + 1; k < num; k++) { - if (@TYPE@_LT(v[IDX(k)], minval)) { - minidx = k; - minval = v[IDX(k)]; - } - } - SWAP(SORTEE(i), SORTEE(minidx)); - } - - return 0; -} - - -/* - * iterative median of 3 quickselect with cutoff to median-of-medians-of5 - * receives stack of already computed pivots in v to minimize the - * partition size were kth is searched in - * - * area that needs partitioning in [...] - * kth 0: [8 7 6 5 4 3 2 1 0] -> med3 partitions elements [4, 2, 0] - * 0 1 2 3 4 8 7 5 6 -> pop requested kth -> stack [4, 2] - * kth 3: 0 1 2 [3] 4 8 7 5 6 -> stack [4] - * kth 5: 0 1 2 3 4 [8 7 5 6] -> stack [6] - * kth 8: 0 1 2 3 4 5 6 [8 7] -> stack [] - * - */ -NPY_NO_EXPORT int -@name@introselect_@suff@(@type@ *v, -#if @arg@ - npy_intp* tosort, -#endif - npy_intp num, npy_intp kth, - npy_intp * pivots, - npy_intp * npiv, - void *NOT_USED) -{ - npy_intp low = 0; - npy_intp high = num - 1; - int depth_limit; - - if (npiv == NULL) - pivots = NULL; - - while (pivots != NULL && *npiv > 0) { - if (pivots[*npiv - 1] > kth) { - /* pivot larger than kth set it as upper bound */ - high = pivots[*npiv - 1] - 1; - break; - } - else if (pivots[*npiv - 1] == kth) { - /* kth was already found in a previous iteration -> done */ - return 0; - } - - low = pivots[*npiv - 1] + 1; - - /* pop from stack */ - *npiv -= 1; - } - - /* - * use a faster O(n*kth) algorithm for very small kth - * e.g. for interpolating percentile - */ - if (kth - low < 3) { - DUMBSELECT(v, tosort, low, high - low + 1, kth - low); - store_pivot(kth, kth, pivots, npiv); - return 0; - } - // Parenthesis around @inexact@ tells clang dead code as intentional - else if ((@inexact@) && kth == num - 1) { - /* useful to check if NaN present via partition(d, (x, -1)) */ - npy_intp k; - npy_intp maxidx = low; - @type@ maxval = v[IDX(low)]; - for (k = low + 1; k < num; k++) { - if (!@TYPE@_LT(v[IDX(k)], maxval)) { - maxidx = k; - maxval = v[IDX(k)]; - } - } - SWAP(SORTEE(kth), SORTEE(maxidx)); - return 0; - } - - depth_limit = npy_get_msb(num) * 2; - - /* guarantee three elements */ - for (;low + 1 < high;) { - npy_intp ll = low + 1; - npy_intp hh = high; - - /* - * if we aren't making sufficient progress with median of 3 - * fall back to median-of-median5 pivot for linear worst case - * med3 for small sizes is required to do unguarded partition - */ - if (depth_limit > 0 || hh - ll < 5) { - const npy_intp mid = low + (high - low) / 2; - /* median of 3 pivot strategy, - * swapping for efficient partition */ - MEDIAN3_SWAP(v, tosort, low, mid, high); - } - else { - npy_intp mid; - /* FIXME: always use pivots to optimize this iterative partition */ -#if @arg@ - mid = ll + amedian_of_median5_@suff@(v, tosort + ll, hh - ll, NULL, NULL); -#else - mid = ll + median_of_median5_@suff@(v + ll, hh - ll, NULL, NULL); -#endif - SWAP(SORTEE(mid), SORTEE(low)); - /* adapt for the larger partition than med3 pivot */ - ll--; - hh++; - } - - depth_limit--; - - /* - * find place to put pivot (in low): - * previous swapping removes need for bound checks - * pivot 3-lowest [x x x] 3-highest - */ - UNGUARDED_PARTITION(v, tosort, v[IDX(low)], &ll, &hh); - - /* move pivot into position */ - SWAP(SORTEE(low), SORTEE(hh)); - - /* kth pivot stored later */ - if (hh != kth) { - store_pivot(hh, kth, pivots, npiv); - } - - if (hh >= kth) - high = hh - 1; - if (hh <= kth) - low = ll; - } - - /* two elements */ - if (high == low + 1) { - if (@TYPE@_LT(v[IDX(high)], v[IDX(low)])) { - SWAP(SORTEE(high), SORTEE(low)) - } - } - store_pivot(kth, kth, pivots, npiv); - - return 0; -} - - -#undef IDX -#undef SWAP -#undef SORTEE -#undef MEDIAN3_SWAP -#undef MEDIAN5 -#undef UNGUARDED_PARTITION -#undef INTROSELECT -#undef DUMBSELECT -/**end repeat1**/ - -/**end repeat**/ diff --git a/numpy/core/src/npysort/selection.cpp b/numpy/core/src/npysort/selection.cpp new file mode 100644 index 000000000..8eb4a57aa --- /dev/null +++ b/numpy/core/src/npysort/selection.cpp @@ -0,0 +1,645 @@ +/* -*- c -*- */ + +/* + * + * The code is loosely based on the quickselect from + * Nicolas Devillard - 1998 public domain + * http://ndevilla.free.fr/median/median/ + * + * Quick select with median of 3 pivot is usually the fastest, + * but the worst case scenario can be quadratic complexity, + * e.g. np.roll(np.arange(x), x / 2) + * To avoid this if it recurses too much it falls back to the + * worst case linear median of median of group 5 pivot strategy. + */ + +#define NPY_NO_DEPRECATED_API NPY_API_VERSION + +#include "numpy/npy_math.h" + +#include "npy_partition.h" +#include "npy_sort.h" +#include "npysort_common.h" +#include "numpy_tag.h" + +#include <cstdlib> +#include <utility> + +#define NOT_USED NPY_UNUSED(unused) + +template <typename Tag, bool arg, typename type> +NPY_NO_EXPORT int +introselect_(type *v, npy_intp *tosort, npy_intp num, npy_intp kth, + npy_intp *pivots, npy_intp *npiv); + +/* + ***************************************************************************** + ** NUMERIC SORTS ** + ***************************************************************************** + */ + +static NPY_INLINE void +store_pivot(npy_intp pivot, npy_intp kth, npy_intp *pivots, npy_intp *npiv) +{ + if (pivots == NULL) { + return; + } + + /* + * If pivot is the requested kth store it, overwriting other pivots if + * required. This must be done so iterative partition can work without + * manually shifting lower data offset by kth each time + */ + if (pivot == kth && *npiv == NPY_MAX_PIVOT_STACK) { + pivots[*npiv - 1] = pivot; + } + /* + * we only need pivots larger than current kth, larger pivots are not + * useful as partitions on smaller kth would reorder the stored pivots + */ + else if (pivot >= kth && *npiv < NPY_MAX_PIVOT_STACK) { + pivots[*npiv] = pivot; + (*npiv) += 1; + } +} + +template <typename type, bool arg> +struct Sortee { + type *v; + Sortee(type *v, npy_intp *) : v(v) {} + type &operator()(npy_intp i) const { return v[i]; } +}; + +template <bool arg> +struct Idx { + Idx(npy_intp *) {} + npy_intp operator()(npy_intp i) const { return i; } +}; + +template <typename type> +struct Sortee<type, true> { + npy_intp *tosort; + Sortee(type *, npy_intp *tosort) : tosort(tosort) {} + npy_intp &operator()(npy_intp i) const { return tosort[i]; } +}; + +template <> +struct Idx<true> { + npy_intp *tosort; + Idx(npy_intp *tosort) : tosort(tosort) {} + npy_intp operator()(npy_intp i) const { return tosort[i]; } +}; + +template <class T> +static constexpr bool +inexact() +{ + return !std::is_integral<T>::value; +} + +/* + * median of 3 pivot strategy + * gets min and median and moves median to low and min to low + 1 + * for efficient partitioning, see unguarded_partition + */ +template <typename Tag, bool arg, typename type> +static NPY_INLINE void +median3_swap_(type *v, npy_intp *tosort, npy_intp low, npy_intp mid, + npy_intp high) +{ + Idx<arg> idx(tosort); + Sortee<type, arg> sortee(v, tosort); + + if (Tag::less(v[idx(high)], v[idx(mid)])) + std::swap(sortee(high), sortee(mid)); + if (Tag::less(v[idx(high)], v[idx(low)])) + std::swap(sortee(high), sortee(low)); + /* move pivot to low */ + if (Tag::less(v[idx(low)], v[idx(mid)])) + std::swap(sortee(low), sortee(mid)); + /* move 3-lowest element to low + 1 */ + std::swap(sortee(mid), sortee(low + 1)); +} + +/* select index of median of five elements */ +template <typename Tag, bool arg, typename type> +static npy_intp +median5_(type *v, npy_intp *tosort) +{ + Idx<arg> idx(tosort); + Sortee<type, arg> sortee(v, tosort); + + /* could be optimized as we only need the index (no swaps) */ + if (Tag::less(v[idx(1)], v[idx(0)])) { + std::swap(sortee(1), sortee(0)); + } + if (Tag::less(v[idx(4)], v[idx(3)])) { + std::swap(sortee(4), sortee(3)); + } + if (Tag::less(v[idx(3)], v[idx(0)])) { + std::swap(sortee(3), sortee(0)); + } + if (Tag::less(v[idx(4)], v[idx(1)])) { + std::swap(sortee(4), sortee(1)); + } + if (Tag::less(v[idx(2)], v[idx(1)])) { + std::swap(sortee(2), sortee(1)); + } + if (Tag::less(v[idx(3)], v[idx(2)])) { + if (Tag::less(v[idx(3)], v[idx(1)])) { + return 1; + } + else { + return 3; + } + } + else { + /* v[1] and v[2] swapped into order above */ + return 2; + } +} + +/* + * partition and return the index were the pivot belongs + * the data must have following property to avoid bound checks: + * ll ... hh + * lower-than-pivot [x x x x] larger-than-pivot + */ +template <typename Tag, bool arg, typename type> +static NPY_INLINE void +unguarded_partition_(type *v, npy_intp *tosort, const type pivot, npy_intp *ll, + npy_intp *hh) +{ + Idx<arg> idx(tosort); + Sortee<type, arg> sortee(v, tosort); + + for (;;) { + do (*ll)++; + while (Tag::less(v[idx(*ll)], pivot)); + do (*hh)--; + while (Tag::less(pivot, v[idx(*hh)])); + + if (*hh < *ll) + break; + + std::swap(sortee(*ll), sortee(*hh)); + } +} + +/* + * select median of median of blocks of 5 + * if used as partition pivot it splits the range into at least 30%/70% + * allowing linear time worstcase quickselect + */ +template <typename Tag, bool arg, typename type> +static npy_intp +median_of_median5_(type *v, npy_intp *tosort, const npy_intp num, + npy_intp *pivots, npy_intp *npiv) +{ + Idx<arg> idx(tosort); + Sortee<type, arg> sortee(v, tosort); + + npy_intp i, subleft; + npy_intp right = num - 1; + npy_intp nmed = (right + 1) / 5; + for (i = 0, subleft = 0; i < nmed; i++, subleft += 5) { + npy_intp m = median5_<Tag, arg>(v + (arg ? 0 : subleft), + tosort + (arg ? subleft : 0)); + std::swap(sortee(subleft + m), sortee(i)); + } + + if (nmed > 2) + introselect_<Tag, arg>(v, tosort, nmed, nmed / 2, pivots, npiv); + return nmed / 2; +} + +/* + * N^2 selection, fast only for very small kth + * useful for close multiple partitions + * (e.g. even element median, interpolating percentile) + */ +template <typename Tag, bool arg, typename type> +static int +dumb_select_(type *v, npy_intp *tosort, npy_intp num, npy_intp kth) +{ + Idx<arg> idx(tosort); + Sortee<type, arg> sortee(v, tosort); + + npy_intp i; + for (i = 0; i <= kth; i++) { + npy_intp minidx = i; + type minval = v[idx(i)]; + npy_intp k; + for (k = i + 1; k < num; k++) { + if (Tag::less(v[idx(k)], minval)) { + minidx = k; + minval = v[idx(k)]; + } + } + std::swap(sortee(i), sortee(minidx)); + } + + return 0; +} + +/* + * iterative median of 3 quickselect with cutoff to median-of-medians-of5 + * receives stack of already computed pivots in v to minimize the + * partition size were kth is searched in + * + * area that needs partitioning in [...] + * kth 0: [8 7 6 5 4 3 2 1 0] -> med3 partitions elements [4, 2, 0] + * 0 1 2 3 4 8 7 5 6 -> pop requested kth -> stack [4, 2] + * kth 3: 0 1 2 [3] 4 8 7 5 6 -> stack [4] + * kth 5: 0 1 2 3 4 [8 7 5 6] -> stack [6] + * kth 8: 0 1 2 3 4 5 6 [8 7] -> stack [] + * + */ +template <typename Tag, bool arg, typename type> +NPY_NO_EXPORT int +introselect_(type *v, npy_intp *tosort, npy_intp num, npy_intp kth, + npy_intp *pivots, npy_intp *npiv) +{ + Idx<arg> idx(tosort); + Sortee<type, arg> sortee(v, tosort); + + npy_intp low = 0; + npy_intp high = num - 1; + int depth_limit; + + if (npiv == NULL) + pivots = NULL; + + while (pivots != NULL && *npiv > 0) { + if (pivots[*npiv - 1] > kth) { + /* pivot larger than kth set it as upper bound */ + high = pivots[*npiv - 1] - 1; + break; + } + else if (pivots[*npiv - 1] == kth) { + /* kth was already found in a previous iteration -> done */ + return 0; + } + + low = pivots[*npiv - 1] + 1; + + /* pop from stack */ + *npiv -= 1; + } + + /* + * use a faster O(n*kth) algorithm for very small kth + * e.g. for interpolating percentile + */ + if (kth - low < 3) { + dumb_select_<Tag, arg>(v + (arg ? 0 : low), tosort + (arg ? low : 0), + high - low + 1, kth - low); + store_pivot(kth, kth, pivots, npiv); + return 0; + } + + else if (inexact<type>() && kth == num - 1) { + /* useful to check if NaN present via partition(d, (x, -1)) */ + npy_intp k; + npy_intp maxidx = low; + type maxval = v[idx(low)]; + for (k = low + 1; k < num; k++) { + if (!Tag::less(v[idx(k)], maxval)) { + maxidx = k; + maxval = v[idx(k)]; + } + } + std::swap(sortee(kth), sortee(maxidx)); + return 0; + } + + depth_limit = npy_get_msb(num) * 2; + + /* guarantee three elements */ + for (; low + 1 < high;) { + npy_intp ll = low + 1; + npy_intp hh = high; + + /* + * if we aren't making sufficient progress with median of 3 + * fall back to median-of-median5 pivot for linear worst case + * med3 for small sizes is required to do unguarded partition + */ + if (depth_limit > 0 || hh - ll < 5) { + const npy_intp mid = low + (high - low) / 2; + /* median of 3 pivot strategy, + * swapping for efficient partition */ + median3_swap_<Tag, arg>(v, tosort, low, mid, high); + } + else { + npy_intp mid; + /* FIXME: always use pivots to optimize this iterative partition */ + mid = ll + median_of_median5_<Tag, arg>(v + (arg ? 0 : ll), + tosort + (arg ? ll : 0), + hh - ll, NULL, NULL); + std::swap(sortee(mid), sortee(low)); + /* adapt for the larger partition than med3 pivot */ + ll--; + hh++; + } + + depth_limit--; + + /* + * find place to put pivot (in low): + * previous swapping removes need for bound checks + * pivot 3-lowest [x x x] 3-highest + */ + unguarded_partition_<Tag, arg>(v, tosort, v[idx(low)], &ll, &hh); + + /* move pivot into position */ + std::swap(sortee(low), sortee(hh)); + + /* kth pivot stored later */ + if (hh != kth) { + store_pivot(hh, kth, pivots, npiv); + } + + if (hh >= kth) + high = hh - 1; + if (hh <= kth) + low = ll; + } + + /* two elements */ + if (high == low + 1) { + if (Tag::less(v[idx(high)], v[idx(low)])) { + std::swap(sortee(high), sortee(low)); + } + } + store_pivot(kth, kth, pivots, npiv); + + return 0; +} + +/*************************************** + * C > C++ dispatch + ***************************************/ + +extern "C" { +NPY_NO_EXPORT int +introselect_bool(npy_bool *v, npy_intp num, npy_intp kth, npy_intp *pivots, + npy_intp *npiv, void *NOT_USED) +{ + return introselect_<npy::bool_tag, false>(v, nullptr, num, kth, pivots, + npiv); +} +NPY_NO_EXPORT int +introselect_byte(npy_byte *v, npy_intp num, npy_intp kth, npy_intp *pivots, + npy_intp *npiv, void *NOT_USED) +{ + return introselect_<npy::byte_tag, false>(v, nullptr, num, kth, pivots, + npiv); +} +NPY_NO_EXPORT int +introselect_ubyte(npy_ubyte *v, npy_intp num, npy_intp kth, npy_intp *pivots, + npy_intp *npiv, void *NOT_USED) +{ + return introselect_<npy::ubyte_tag, false>(v, nullptr, num, kth, pivots, + npiv); +} +NPY_NO_EXPORT int +introselect_short(npy_short *v, npy_intp num, npy_intp kth, npy_intp *pivots, + npy_intp *npiv, void *NOT_USED) +{ + return introselect_<npy::short_tag, false>(v, nullptr, num, kth, pivots, + npiv); +} +NPY_NO_EXPORT int +introselect_ushort(npy_ushort *v, npy_intp num, npy_intp kth, npy_intp *pivots, + npy_intp *npiv, void *NOT_USED) +{ + return introselect_<npy::ushort_tag, false>(v, nullptr, num, kth, pivots, + npiv); +} +NPY_NO_EXPORT int +introselect_int(npy_int *v, npy_intp num, npy_intp kth, npy_intp *pivots, + npy_intp *npiv, void *NOT_USED) +{ + return introselect_<npy::int_tag, false>(v, nullptr, num, kth, pivots, + npiv); +} +NPY_NO_EXPORT int +introselect_uint(npy_uint *v, npy_intp num, npy_intp kth, npy_intp *pivots, + npy_intp *npiv, void *NOT_USED) +{ + return introselect_<npy::uint_tag, false>(v, nullptr, num, kth, pivots, + npiv); +} +NPY_NO_EXPORT int +introselect_long(npy_long *v, npy_intp num, npy_intp kth, npy_intp *pivots, + npy_intp *npiv, void *NOT_USED) +{ + return introselect_<npy::long_tag, false>(v, nullptr, num, kth, pivots, + npiv); +} +NPY_NO_EXPORT int +introselect_ulong(npy_ulong *v, npy_intp num, npy_intp kth, npy_intp *pivots, + npy_intp *npiv, void *NOT_USED) +{ + return introselect_<npy::ulong_tag, false>(v, nullptr, num, kth, pivots, + npiv); +} +NPY_NO_EXPORT int +introselect_longlong(npy_longlong *v, npy_intp num, npy_intp kth, + npy_intp *pivots, npy_intp *npiv, void *NOT_USED) +{ + return introselect_<npy::longlong_tag, false>(v, nullptr, num, kth, pivots, + npiv); +} +NPY_NO_EXPORT int +introselect_ulonglong(npy_ulonglong *v, npy_intp num, npy_intp kth, + npy_intp *pivots, npy_intp *npiv, void *NOT_USED) +{ + return introselect_<npy::ulonglong_tag, false>(v, nullptr, num, kth, + pivots, npiv); +} +NPY_NO_EXPORT int +introselect_half(npy_half *v, npy_intp num, npy_intp kth, npy_intp *pivots, + npy_intp *npiv, void *NOT_USED) +{ + return introselect_<npy::half_tag, false>(v, nullptr, num, kth, pivots, + npiv); +} +NPY_NO_EXPORT int +introselect_float(npy_float *v, npy_intp num, npy_intp kth, npy_intp *pivots, + npy_intp *npiv, void *NOT_USED) +{ + return introselect_<npy::float_tag, false>(v, nullptr, num, kth, pivots, + npiv); +} +NPY_NO_EXPORT int +introselect_double(npy_double *v, npy_intp num, npy_intp kth, npy_intp *pivots, + npy_intp *npiv, void *NOT_USED) +{ + return introselect_<npy::double_tag, false>(v, nullptr, num, kth, pivots, + npiv); +} +NPY_NO_EXPORT int +introselect_longdouble(npy_longdouble *v, npy_intp num, npy_intp kth, + npy_intp *pivots, npy_intp *npiv, void *NOT_USED) +{ + return introselect_<npy::longdouble_tag, false>(v, nullptr, num, kth, + pivots, npiv); +} +NPY_NO_EXPORT int +introselect_cfloat(npy_cfloat *v, npy_intp num, npy_intp kth, npy_intp *pivots, + npy_intp *npiv, void *NOT_USED) +{ + return introselect_<npy::cfloat_tag, false>(v, nullptr, num, kth, pivots, + npiv); +} +NPY_NO_EXPORT int +introselect_cdouble(npy_cdouble *v, npy_intp num, npy_intp kth, + npy_intp *pivots, npy_intp *npiv, void *NOT_USED) +{ + return introselect_<npy::cdouble_tag, false>(v, nullptr, num, kth, pivots, + npiv); +} +NPY_NO_EXPORT int +introselect_clongdouble(npy_clongdouble *v, npy_intp num, npy_intp kth, + npy_intp *pivots, npy_intp *npiv, void *NOT_USED) +{ + return introselect_<npy::clongdouble_tag, false>(v, nullptr, num, kth, + pivots, npiv); +} + +NPY_NO_EXPORT int +aintroselect_bool(npy_bool *v, npy_intp *tosort, npy_intp num, npy_intp kth, + npy_intp *pivots, npy_intp *npiv, void *NOT_USED) +{ + return introselect_<npy::bool_tag, true>(v, tosort, num, kth, pivots, + npiv); +} +NPY_NO_EXPORT int +aintroselect_byte(npy_byte *v, npy_intp *tosort, npy_intp num, npy_intp kth, + npy_intp *pivots, npy_intp *npiv, void *NOT_USED) +{ + return introselect_<npy::byte_tag, true>(v, tosort, num, kth, pivots, + npiv); +} +NPY_NO_EXPORT int +aintroselect_ubyte(npy_ubyte *v, npy_intp *tosort, npy_intp num, npy_intp kth, + npy_intp *pivots, npy_intp *npiv, void *NOT_USED) +{ + return introselect_<npy::ubyte_tag, true>(v, tosort, num, kth, pivots, + npiv); +} +NPY_NO_EXPORT int +aintroselect_short(npy_short *v, npy_intp *tosort, npy_intp num, npy_intp kth, + npy_intp *pivots, npy_intp *npiv, void *NOT_USED) +{ + return introselect_<npy::short_tag, true>(v, tosort, num, kth, pivots, + npiv); +} +NPY_NO_EXPORT int +aintroselect_ushort(npy_ushort *v, npy_intp *tosort, npy_intp num, + npy_intp kth, npy_intp *pivots, npy_intp *npiv, + void *NOT_USED) +{ + return introselect_<npy::ushort_tag, true>(v, tosort, num, kth, pivots, + npiv); +} +NPY_NO_EXPORT int +aintroselect_int(npy_int *v, npy_intp *tosort, npy_intp num, npy_intp kth, + npy_intp *pivots, npy_intp *npiv, void *NOT_USED) +{ + return introselect_<npy::int_tag, true>(v, tosort, num, kth, pivots, npiv); +} +NPY_NO_EXPORT int +aintroselect_uint(npy_uint *v, npy_intp *tosort, npy_intp num, npy_intp kth, + npy_intp *pivots, npy_intp *npiv, void *NOT_USED) +{ + return introselect_<npy::uint_tag, true>(v, tosort, num, kth, pivots, + npiv); +} +NPY_NO_EXPORT int +aintroselect_long(npy_long *v, npy_intp *tosort, npy_intp num, npy_intp kth, + npy_intp *pivots, npy_intp *npiv, void *NOT_USED) +{ + return introselect_<npy::long_tag, true>(v, tosort, num, kth, pivots, + npiv); +} +NPY_NO_EXPORT int +aintroselect_ulong(npy_ulong *v, npy_intp *tosort, npy_intp num, npy_intp kth, + npy_intp *pivots, npy_intp *npiv, void *NOT_USED) +{ + return introselect_<npy::ulong_tag, true>(v, tosort, num, kth, pivots, + npiv); +} +NPY_NO_EXPORT int +aintroselect_longlong(npy_longlong *v, npy_intp *tosort, npy_intp num, + npy_intp kth, npy_intp *pivots, npy_intp *npiv, + void *NOT_USED) +{ + return introselect_<npy::longlong_tag, true>(v, tosort, num, kth, pivots, + npiv); +} +NPY_NO_EXPORT int +aintroselect_ulonglong(npy_ulonglong *v, npy_intp *tosort, npy_intp num, + npy_intp kth, npy_intp *pivots, npy_intp *npiv, + void *NOT_USED) +{ + return introselect_<npy::ulonglong_tag, true>(v, tosort, num, kth, pivots, + npiv); +} +NPY_NO_EXPORT int +aintroselect_half(npy_half *v, npy_intp *tosort, npy_intp num, npy_intp kth, + npy_intp *pivots, npy_intp *npiv, void *NOT_USED) +{ + return introselect_<npy::half_tag, true>(v, tosort, num, kth, pivots, + npiv); +} +NPY_NO_EXPORT int +aintroselect_float(npy_float *v, npy_intp *tosort, npy_intp num, npy_intp kth, + npy_intp *pivots, npy_intp *npiv, void *NOT_USED) +{ + return introselect_<npy::float_tag, true>(v, tosort, num, kth, pivots, + npiv); +} +NPY_NO_EXPORT int +aintroselect_double(npy_double *v, npy_intp *tosort, npy_intp num, + npy_intp kth, npy_intp *pivots, npy_intp *npiv, + void *NOT_USED) +{ + return introselect_<npy::double_tag, true>(v, tosort, num, kth, pivots, + npiv); +} +NPY_NO_EXPORT int +aintroselect_longdouble(npy_longdouble *v, npy_intp *tosort, npy_intp num, + npy_intp kth, npy_intp *pivots, npy_intp *npiv, + void *NOT_USED) +{ + return introselect_<npy::longdouble_tag, true>(v, tosort, num, kth, pivots, + npiv); +} +NPY_NO_EXPORT int +aintroselect_cfloat(npy_cfloat *v, npy_intp *tosort, npy_intp num, + npy_intp kth, npy_intp *pivots, npy_intp *npiv, + void *NOT_USED) +{ + return introselect_<npy::cfloat_tag, true>(v, tosort, num, kth, pivots, + npiv); +} +NPY_NO_EXPORT int +aintroselect_cdouble(npy_cdouble *v, npy_intp *tosort, npy_intp num, + npy_intp kth, npy_intp *pivots, npy_intp *npiv, + void *NOT_USED) +{ + return introselect_<npy::cdouble_tag, true>(v, tosort, num, kth, pivots, + npiv); +} +NPY_NO_EXPORT int +aintroselect_clongdouble(npy_clongdouble *v, npy_intp *tosort, npy_intp num, + npy_intp kth, npy_intp *pivots, npy_intp *npiv, + void *NOT_USED) +{ + return introselect_<npy::clongdouble_tag, true>(v, tosort, num, kth, + pivots, npiv); +} +} diff --git a/numpy/core/src/npysort/timsort.c.src b/numpy/core/src/npysort/timsort.cpp index 5298f5a1d..8bcd90061 100644 --- a/numpy/core/src/npysort/timsort.c.src +++ b/numpy/core/src/npysort/timsort.cpp @@ -26,7 +26,6 @@ * The heap sort is included for completeness. */ - /* For details of Timsort, refer to * https://github.com/python/cpython/blob/3.7/Objects/listsort.txt */ @@ -35,14 +34,16 @@ #include "npy_sort.h" #include "npysort_common.h" -#include <stdlib.h> +#include "numpy_tag.h" + +#include <cstdlib> +#include <utility> /* enough for 32 * 1.618 ** 128 elements */ #define TIMSORT_STACK_SIZE 128 - - -static npy_intp compute_min_run(npy_intp num) +static npy_intp +compute_min_run(npy_intp num) { npy_intp r = 0; @@ -59,14 +60,12 @@ typedef struct { npy_intp l; /* length */ } run; - /* buffer for argsort. Declared here to avoid multiple declarations. */ typedef struct { npy_intp *pw; npy_intp size; } buffer_intp; - /* buffer method */ static NPY_INLINE int resize_buffer_intp(buffer_intp *buffer, npy_intp new_size) @@ -76,16 +75,19 @@ resize_buffer_intp(buffer_intp *buffer, npy_intp new_size) } if (NPY_UNLIKELY(buffer->pw == NULL)) { - buffer->pw = malloc(new_size * sizeof(npy_intp)); - } else { - buffer->pw = realloc(buffer->pw, new_size * sizeof(npy_intp)); + buffer->pw = (npy_intp *)malloc(new_size * sizeof(npy_intp)); + } + else { + buffer->pw = + (npy_intp *)realloc(buffer->pw, new_size * sizeof(npy_intp)); } buffer->size = new_size; if (NPY_UNLIKELY(buffer->pw == NULL)) { return -NPY_ENOMEM; - } else { + } + else { return 0; } } @@ -96,56 +98,44 @@ resize_buffer_intp(buffer_intp *buffer, npy_intp new_size) ***************************************************************************** */ - -/**begin repeat - * - * #TYPE = BOOL, BYTE, UBYTE, SHORT, USHORT, INT, UINT, LONG, ULONG, - * LONGLONG, ULONGLONG, HALF, FLOAT, DOUBLE, LONGDOUBLE, - * CFLOAT, CDOUBLE, CLONGDOUBLE, DATETIME, TIMEDELTA# - * #suff = bool, byte, ubyte, short, ushort, int, uint, long, ulong, - * longlong, ulonglong, half, float, double, longdouble, - * cfloat, cdouble, clongdouble, datetime, timedelta# - * #type = npy_bool, npy_byte, npy_ubyte, npy_short, npy_ushort, npy_int, - * npy_uint, npy_long, npy_ulong, npy_longlong, npy_ulonglong, - * npy_ushort, npy_float, npy_double, npy_longdouble, npy_cfloat, - * npy_cdouble, npy_clongdouble, npy_datetime, npy_timedelta# - */ - - -typedef struct { - @type@ * pw; +template <typename Tag> +struct buffer_ { + typename Tag::type *pw; npy_intp size; -} buffer_@suff@; - +}; +template <typename Tag> static NPY_INLINE int -resize_buffer_@suff@(buffer_@suff@ *buffer, npy_intp new_size) +resize_buffer_(buffer_<Tag> *buffer, npy_intp new_size) { + using type = typename Tag::type; if (new_size <= buffer->size) { return 0; } if (NPY_UNLIKELY(buffer->pw == NULL)) { - buffer->pw = malloc(new_size * sizeof(@type@)); - } else { - buffer->pw = realloc(buffer->pw, new_size * sizeof(@type@)); + buffer->pw = (type *)malloc(new_size * sizeof(type)); + } + else { + buffer->pw = (type *)realloc(buffer->pw, new_size * sizeof(type)); } buffer->size = new_size; if (NPY_UNLIKELY(buffer->pw == NULL)) { return -NPY_ENOMEM; - } else { + } + else { return 0; } } - +template <typename Tag, typename type> static npy_intp -count_run_@suff@(@type@ *arr, npy_intp l, npy_intp num, npy_intp minrun) +count_run_(type *arr, npy_intp l, npy_intp num, npy_intp minrun) { npy_intp sz; - @type@ vc, *pl, *pi, *pj, *pr; + type vc, *pl, *pi, *pj, *pr; if (NPY_UNLIKELY(num - l == 1)) { return 1; @@ -154,15 +144,18 @@ count_run_@suff@(@type@ *arr, npy_intp l, npy_intp num, npy_intp minrun) pl = arr + l; /* (not strictly) ascending sequence */ - if (!@TYPE@_LT(*(pl + 1), *pl)) { - for (pi = pl + 1; pi < arr + num - 1 && !@TYPE@_LT(*(pi + 1), *pi); ++pi) { + if (!Tag::less(*(pl + 1), *pl)) { + for (pi = pl + 1; pi < arr + num - 1 && !Tag::less(*(pi + 1), *pi); + ++pi) { } - } else { /* (strictly) descending sequence */ - for (pi = pl + 1; pi < arr + num - 1 && @TYPE@_LT(*(pi + 1), *pi); ++pi) { + } + else { /* (strictly) descending sequence */ + for (pi = pl + 1; pi < arr + num - 1 && Tag::less(*(pi + 1), *pi); + ++pi) { } for (pj = pl, pr = pi; pj < pr; ++pj, --pr) { - @TYPE@_SWAP(*pj, *pr); + std::swap(*pj, *pr); } } @@ -172,7 +165,8 @@ count_run_@suff@(@type@ *arr, npy_intp l, npy_intp num, npy_intp minrun) if (sz < minrun) { if (l + minrun < num) { sz = minrun; - } else { + } + else { sz = num - l; } @@ -183,7 +177,7 @@ count_run_@suff@(@type@ *arr, npy_intp l, npy_intp num, npy_intp minrun) vc = *pi; pj = pi; - while (pl < pj && @TYPE@_LT(vc, *(pj - 1))) { + while (pl < pj && Tag::less(vc, *(pj - 1))) { *pj = *(pj - 1); --pj; } @@ -195,43 +189,42 @@ count_run_@suff@(@type@ *arr, npy_intp l, npy_intp num, npy_intp minrun) return sz; } - /* when the left part of the array (p1) is smaller, copy p1 to buffer * and merge from left to right */ +template <typename Tag, typename type> static void -merge_left_@suff@(@type@ *p1, npy_intp l1, @type@ *p2, npy_intp l2, - @type@ *p3) +merge_left_(type *p1, npy_intp l1, type *p2, npy_intp l2, type *p3) { - @type@ *end = p2 + l2; - memcpy(p3, p1, sizeof(@type@) * l1); + type *end = p2 + l2; + memcpy(p3, p1, sizeof(type) * l1); /* first element must be in p2 otherwise skipped in the caller */ *p1++ = *p2++; while (p1 < p2 && p2 < end) { - if (@TYPE@_LT(*p2, *p3)) { + if (Tag::less(*p2, *p3)) { *p1++ = *p2++; - } else { + } + else { *p1++ = *p3++; } } if (p1 != p2) { - memcpy(p1, p3, sizeof(@type@) * (p2 - p1)); + memcpy(p1, p3, sizeof(type) * (p2 - p1)); } } - /* when the right part of the array (p2) is smaller, copy p2 to buffer * and merge from right to left */ +template <typename Tag, typename type> static void -merge_right_@suff@(@type@ *p1, npy_intp l1, @type@ *p2, npy_intp l2, - @type@ *p3) +merge_right_(type *p1, npy_intp l1, type *p2, npy_intp l2, type *p3) { npy_intp ofs; - @type@ *start = p1 - 1; - memcpy(p3, p2, sizeof(@type@) * l2); + type *start = p1 - 1; + memcpy(p3, p2, sizeof(type) * l2); p1 += l1 - 1; p2 += l2 - 1; p3 += l2 - 1; @@ -239,31 +232,32 @@ merge_right_@suff@(@type@ *p1, npy_intp l1, @type@ *p2, npy_intp l2, *p2-- = *p1--; while (p1 < p2 && start < p1) { - if (@TYPE@_LT(*p3, *p1)) { + if (Tag::less(*p3, *p1)) { *p2-- = *p1--; - } else { + } + else { *p2-- = *p3--; } } if (p1 != p2) { ofs = p2 - start; - memcpy(start + 1, p3 - ofs + 1, sizeof(@type@) * ofs); + memcpy(start + 1, p3 - ofs + 1, sizeof(type) * ofs); } } - /* Note: the naming convention of gallop functions are different from that of * CPython. For example, here gallop_right means gallop from left toward right, * whereas in CPython gallop_right means gallop * and find the right most element among equal elements */ +template <typename Tag, typename type> static npy_intp -gallop_right_@suff@(const @type@ *arr, const npy_intp size, const @type@ key) +gallop_right_(const type *arr, const npy_intp size, const type key) { npy_intp last_ofs, ofs, m; - if (@TYPE@_LT(key, arr[0])) { + if (Tag::less(key, arr[0])) { return 0; } @@ -276,9 +270,10 @@ gallop_right_@suff@(const @type@ *arr, const npy_intp size, const @type@ key) break; } - if (@TYPE@_LT(key, arr[ofs])) { + if (Tag::less(key, arr[ofs])) { break; - } else { + } + else { last_ofs = ofs; /* ofs = 1, 3, 7, 15... */ ofs = (ofs << 1) + 1; @@ -289,9 +284,10 @@ gallop_right_@suff@(const @type@ *arr, const npy_intp size, const @type@ key) while (last_ofs + 1 < ofs) { m = last_ofs + ((ofs - last_ofs) >> 1); - if (@TYPE@_LT(key, arr[m])) { + if (Tag::less(key, arr[m])) { ofs = m; - } else { + } + else { last_ofs = m; } } @@ -300,13 +296,13 @@ gallop_right_@suff@(const @type@ *arr, const npy_intp size, const @type@ key) return ofs; } - +template <typename Tag, typename type> static npy_intp -gallop_left_@suff@(const @type@ *arr, const npy_intp size, const @type@ key) +gallop_left_(const type *arr, const npy_intp size, const type key) { npy_intp last_ofs, ofs, l, m, r; - if (@TYPE@_LT(arr[size - 1], key)) { + if (Tag::less(arr[size - 1], key)) { return size; } @@ -319,9 +315,10 @@ gallop_left_@suff@(const @type@ *arr, const npy_intp size, const @type@ key) break; } - if (@TYPE@_LT(arr[size - ofs - 1], key)) { + if (Tag::less(arr[size - ofs - 1], key)) { break; - } else { + } + else { last_ofs = ofs; ofs = (ofs << 1) + 1; } @@ -334,9 +331,10 @@ gallop_left_@suff@(const @type@ *arr, const npy_intp size, const @type@ key) while (l + 1 < r) { m = l + ((r - l) >> 1); - if (@TYPE@_LT(arr[m], key)) { + if (Tag::less(arr[m], key)) { l = m; - } else { + } + else { r = m; } } @@ -345,14 +343,13 @@ gallop_left_@suff@(const @type@ *arr, const npy_intp size, const @type@ key) return r; } - +template <typename Tag, typename type> static int -merge_at_@suff@(@type@ *arr, const run *stack, const npy_intp at, - buffer_@suff@ *buffer) +merge_at_(type *arr, const run *stack, const npy_intp at, buffer_<Tag> *buffer) { int ret; npy_intp s1, l1, s2, l2, k; - @type@ *p1, *p2; + type *p1, *p2; s1 = stack[at].s; l1 = stack[at].l; s2 = stack[at + 1].s; @@ -361,7 +358,7 @@ merge_at_@suff@(@type@ *arr, const run *stack, const npy_intp at, * if try to comment this out for debugging purpose, remember * in the merging process the first element is skipped */ - k = gallop_right_@suff@(arr + s1, l1, arr[s2]); + k = gallop_right_<Tag>(arr + s1, l1, arr[s2]); if (l1 == k) { /* already sorted */ @@ -372,29 +369,33 @@ merge_at_@suff@(@type@ *arr, const run *stack, const npy_intp at, l1 -= k; p2 = arr + s2; /* arr[s2-1] belongs to arr[s2+l2] */ - l2 = gallop_left_@suff@(arr + s2, l2, arr[s2 - 1]); + l2 = gallop_left_<Tag>(arr + s2, l2, arr[s2 - 1]); if (l2 < l1) { - ret = resize_buffer_@suff@(buffer, l2); + ret = resize_buffer_<Tag>(buffer, l2); - if (NPY_UNLIKELY(ret < 0)) { return ret; } + if (NPY_UNLIKELY(ret < 0)) { + return ret; + } - merge_right_@suff@(p1, l1, p2, l2, buffer->pw); - } else { - ret = resize_buffer_@suff@(buffer, l1); + merge_right_<Tag>(p1, l1, p2, l2, buffer->pw); + } + else { + ret = resize_buffer_<Tag>(buffer, l1); - if (NPY_UNLIKELY(ret < 0)) { return ret; } + if (NPY_UNLIKELY(ret < 0)) { + return ret; + } - merge_left_@suff@(p1, l1, p2, l2, buffer->pw); + merge_left_<Tag>(p1, l1, p2, l2, buffer->pw); } return 0; } - +template <typename Tag, typename type> static int -try_collapse_@suff@(@type@ *arr, run *stack, npy_intp *stack_ptr, - buffer_@suff@ *buffer) +try_collapse_(type *arr, run *stack, npy_intp *stack_ptr, buffer_<Tag> *buffer) { int ret; npy_intp A, B, C, top; @@ -405,33 +406,42 @@ try_collapse_@suff@(@type@ *arr, run *stack, npy_intp *stack_ptr, C = stack[top - 1].l; if ((2 < top && stack[top - 3].l <= B + C) || - (3 < top && stack[top - 4].l <= stack[top - 3].l + B)) { + (3 < top && stack[top - 4].l <= stack[top - 3].l + B)) { A = stack[top - 3].l; if (A <= C) { - ret = merge_at_@suff@(arr, stack, top - 3, buffer); + ret = merge_at_<Tag>(arr, stack, top - 3, buffer); - if (NPY_UNLIKELY(ret < 0)) { return ret; } + if (NPY_UNLIKELY(ret < 0)) { + return ret; + } stack[top - 3].l += B; stack[top - 2] = stack[top - 1]; --top; - } else { - ret = merge_at_@suff@(arr, stack, top - 2, buffer); + } + else { + ret = merge_at_<Tag>(arr, stack, top - 2, buffer); - if (NPY_UNLIKELY(ret < 0)) { return ret; } + if (NPY_UNLIKELY(ret < 0)) { + return ret; + } stack[top - 2].l += C; --top; } - } else if (1 < top && B <= C) { - ret = merge_at_@suff@(arr, stack, top - 2, buffer); + } + else if (1 < top && B <= C) { + ret = merge_at_<Tag>(arr, stack, top - 2, buffer); - if (NPY_UNLIKELY(ret < 0)) { return ret; } + if (NPY_UNLIKELY(ret < 0)) { + return ret; + } stack[top - 2].l += C; --top; - } else { + } + else { break; } } @@ -440,26 +450,32 @@ try_collapse_@suff@(@type@ *arr, run *stack, npy_intp *stack_ptr, return 0; } +template <typename Tag, typename type> static int -force_collapse_@suff@(@type@ *arr, run *stack, npy_intp *stack_ptr, - buffer_@suff@ *buffer) +force_collapse_(type *arr, run *stack, npy_intp *stack_ptr, + buffer_<Tag> *buffer) { int ret; npy_intp top = *stack_ptr; while (2 < top) { if (stack[top - 3].l <= stack[top - 1].l) { - ret = merge_at_@suff@(arr, stack, top - 3, buffer); + ret = merge_at_<Tag>(arr, stack, top - 3, buffer); - if (NPY_UNLIKELY(ret < 0)) { return ret; } + if (NPY_UNLIKELY(ret < 0)) { + return ret; + } stack[top - 3].l += stack[top - 2].l; stack[top - 2] = stack[top - 1]; --top; - } else { - ret = merge_at_@suff@(arr, stack, top - 2, buffer); + } + else { + ret = merge_at_<Tag>(arr, stack, top - 2, buffer); - if (NPY_UNLIKELY(ret < 0)) { return ret; } + if (NPY_UNLIKELY(ret < 0)) { + return ret; + } stack[top - 2].l += stack[top - 1].l; --top; @@ -467,21 +483,24 @@ force_collapse_@suff@(@type@ *arr, run *stack, npy_intp *stack_ptr, } if (1 < top) { - ret = merge_at_@suff@(arr, stack, top - 2, buffer); + ret = merge_at_<Tag>(arr, stack, top - 2, buffer); - if (NPY_UNLIKELY(ret < 0)) { return ret; } + if (NPY_UNLIKELY(ret < 0)) { + return ret; + } } return 0; } - -NPY_NO_EXPORT int -timsort_@suff@(void *start, npy_intp num, void *NPY_UNUSED(varr)) +template <typename Tag> +static int +timsort_(void *start, npy_intp num) { + using type = typename Tag::type; int ret; npy_intp l, n, stack_ptr, minrun; - buffer_@suff@ buffer; + buffer_<Tag> buffer; run stack[TIMSORT_STACK_SIZE]; buffer.pw = NULL; buffer.size = 0; @@ -489,20 +508,24 @@ timsort_@suff@(void *start, npy_intp num, void *NPY_UNUSED(varr)) minrun = compute_min_run(num); for (l = 0; l < num;) { - n = count_run_@suff@(start, l, num, minrun); + n = count_run_<Tag>((type *)start, l, num, minrun); stack[stack_ptr].s = l; stack[stack_ptr].l = n; ++stack_ptr; - ret = try_collapse_@suff@(start, stack, &stack_ptr, &buffer); + ret = try_collapse_<Tag>((type *)start, stack, &stack_ptr, &buffer); - if (NPY_UNLIKELY(ret < 0)) { goto cleanup; } + if (NPY_UNLIKELY(ret < 0)) { + goto cleanup; + } l += n; } - ret = force_collapse_@suff@(start, stack, &stack_ptr, &buffer); + ret = force_collapse_<Tag>((type *)start, stack, &stack_ptr, &buffer); - if (NPY_UNLIKELY(ret < 0)) { goto cleanup; } + if (NPY_UNLIKELY(ret < 0)) { + goto cleanup; + } ret = 0; cleanup: @@ -512,16 +535,15 @@ cleanup: return ret; } - /* argsort */ - +template <typename Tag, typename type> static npy_intp -acount_run_@suff@(@type@ *arr, npy_intp *tosort, npy_intp l, npy_intp num, - npy_intp minrun) +acount_run_(type *arr, npy_intp *tosort, npy_intp l, npy_intp num, + npy_intp minrun) { npy_intp sz; - @type@ vc; + type vc; npy_intp vi; npy_intp *pl, *pi, *pj, *pr; @@ -532,17 +554,20 @@ acount_run_@suff@(@type@ *arr, npy_intp *tosort, npy_intp l, npy_intp num, pl = tosort + l; /* (not strictly) ascending sequence */ - if (!@TYPE@_LT(arr[*(pl + 1)], arr[*pl])) { - for (pi = pl + 1; pi < tosort + num - 1 - && !@TYPE@_LT(arr[*(pi + 1)], arr[*pi]); ++pi) { + if (!Tag::less(arr[*(pl + 1)], arr[*pl])) { + for (pi = pl + 1; + pi < tosort + num - 1 && !Tag::less(arr[*(pi + 1)], arr[*pi]); + ++pi) { } - } else { /* (strictly) descending sequence */ - for (pi = pl + 1; pi < tosort + num - 1 - && @TYPE@_LT(arr[*(pi + 1)], arr[*pi]); ++pi) { + } + else { /* (strictly) descending sequence */ + for (pi = pl + 1; + pi < tosort + num - 1 && Tag::less(arr[*(pi + 1)], arr[*pi]); + ++pi) { } for (pj = pl, pr = pi; pj < pr; ++pj, --pr) { - INTP_SWAP(*pj, *pr); + std::swap(*pj, *pr); } } @@ -552,7 +577,8 @@ acount_run_@suff@(@type@ *arr, npy_intp *tosort, npy_intp l, npy_intp num, if (sz < minrun) { if (l + minrun < num) { sz = minrun; - } else { + } + else { sz = num - l; } @@ -564,7 +590,7 @@ acount_run_@suff@(@type@ *arr, npy_intp *tosort, npy_intp l, npy_intp num, vc = arr[*pi]; pj = pi; - while (pl < pj && @TYPE@_LT(vc, arr[*(pj - 1)])) { + while (pl < pj && Tag::less(vc, arr[*(pj - 1)])) { *pj = *(pj - 1); --pj; } @@ -576,14 +602,14 @@ acount_run_@suff@(@type@ *arr, npy_intp *tosort, npy_intp l, npy_intp num, return sz; } - +template <typename Tag, typename type> static npy_intp -agallop_right_@suff@(const @type@ *arr, const npy_intp *tosort, - const npy_intp size, const @type@ key) +agallop_right_(const type *arr, const npy_intp *tosort, const npy_intp size, + const type key) { npy_intp last_ofs, ofs, m; - if (@TYPE@_LT(key, arr[tosort[0]])) { + if (Tag::less(key, arr[tosort[0]])) { return 0; } @@ -596,9 +622,10 @@ agallop_right_@suff@(const @type@ *arr, const npy_intp *tosort, break; } - if (@TYPE@_LT(key, arr[tosort[ofs]])) { + if (Tag::less(key, arr[tosort[ofs]])) { break; - } else { + } + else { last_ofs = ofs; /* ofs = 1, 3, 7, 15... */ ofs = (ofs << 1) + 1; @@ -609,9 +636,10 @@ agallop_right_@suff@(const @type@ *arr, const npy_intp *tosort, while (last_ofs + 1 < ofs) { m = last_ofs + ((ofs - last_ofs) >> 1); - if (@TYPE@_LT(key, arr[tosort[m]])) { + if (Tag::less(key, arr[tosort[m]])) { ofs = m; - } else { + } + else { last_ofs = m; } } @@ -620,15 +648,14 @@ agallop_right_@suff@(const @type@ *arr, const npy_intp *tosort, return ofs; } - - +template <typename Tag, typename type> static npy_intp -agallop_left_@suff@(const @type@ *arr, const npy_intp *tosort, - const npy_intp size, const @type@ key) +agallop_left_(const type *arr, const npy_intp *tosort, const npy_intp size, + const type key) { npy_intp last_ofs, ofs, l, m, r; - if (@TYPE@_LT(arr[tosort[size - 1]], key)) { + if (Tag::less(arr[tosort[size - 1]], key)) { return size; } @@ -641,24 +668,27 @@ agallop_left_@suff@(const @type@ *arr, const npy_intp *tosort, break; } - if (@TYPE@_LT(arr[tosort[size - ofs - 1]], key)) { + if (Tag::less(arr[tosort[size - ofs - 1]], key)) { break; - } else { + } + else { last_ofs = ofs; ofs = (ofs << 1) + 1; } } - /* now that arr[tosort[size-ofs-1]] < key <= arr[tosort[size-last_ofs-1]] */ + /* now that arr[tosort[size-ofs-1]] < key <= arr[tosort[size-last_ofs-1]] + */ l = size - ofs - 1; r = size - last_ofs - 1; while (l + 1 < r) { m = l + ((r - l) >> 1); - if (@TYPE@_LT(arr[tosort[m]], key)) { + if (Tag::less(arr[tosort[m]], key)) { l = m; - } else { + } + else { r = m; } } @@ -667,11 +697,10 @@ agallop_left_@suff@(const @type@ *arr, const npy_intp *tosort, return r; } - +template <typename Tag, typename type> static void -amerge_left_@suff@(@type@ *arr, npy_intp *p1, npy_intp l1, npy_intp *p2, - npy_intp l2, - npy_intp *p3) +amerge_left_(type *arr, npy_intp *p1, npy_intp l1, npy_intp *p2, npy_intp l2, + npy_intp *p3) { npy_intp *end = p2 + l2; memcpy(p3, p1, sizeof(npy_intp) * l1); @@ -679,9 +708,10 @@ amerge_left_@suff@(@type@ *arr, npy_intp *p1, npy_intp l1, npy_intp *p2, *p1++ = *p2++; while (p1 < p2 && p2 < end) { - if (@TYPE@_LT(arr[*p2], arr[*p3])) { + if (Tag::less(arr[*p2], arr[*p3])) { *p1++ = *p2++; - } else { + } + else { *p1++ = *p3++; } } @@ -691,11 +721,10 @@ amerge_left_@suff@(@type@ *arr, npy_intp *p1, npy_intp l1, npy_intp *p2, } } - +template <typename Tag, typename type> static void -amerge_right_@suff@(@type@ *arr, npy_intp* p1, npy_intp l1, npy_intp *p2, - npy_intp l2, - npy_intp *p3) +amerge_right_(type *arr, npy_intp *p1, npy_intp l1, npy_intp *p2, npy_intp l2, + npy_intp *p3) { npy_intp ofs; npy_intp *start = p1 - 1; @@ -707,9 +736,10 @@ amerge_right_@suff@(@type@ *arr, npy_intp* p1, npy_intp l1, npy_intp *p2, *p2-- = *p1--; while (p1 < p2 && start < p1) { - if (@TYPE@_LT(arr[*p3], arr[*p1])) { + if (Tag::less(arr[*p3], arr[*p1])) { *p2-- = *p1--; - } else { + } + else { *p2-- = *p3--; } } @@ -720,11 +750,10 @@ amerge_right_@suff@(@type@ *arr, npy_intp* p1, npy_intp l1, npy_intp *p2, } } - +template <typename Tag, typename type> static int -amerge_at_@suff@(@type@ *arr, npy_intp *tosort, const run *stack, - const npy_intp at, - buffer_intp *buffer) +amerge_at_(type *arr, npy_intp *tosort, const run *stack, const npy_intp at, + buffer_intp *buffer) { int ret; npy_intp s1, l1, s2, l2, k; @@ -734,7 +763,7 @@ amerge_at_@suff@(@type@ *arr, npy_intp *tosort, const run *stack, s2 = stack[at + 1].s; l2 = stack[at + 1].l; /* tosort[s2] belongs to tosort[s1+k] */ - k = agallop_right_@suff@(arr, tosort + s1, l1, arr[tosort[s2]]); + k = agallop_right_<Tag>(arr, tosort + s1, l1, arr[tosort[s2]]); if (l1 == k) { /* already sorted */ @@ -745,30 +774,34 @@ amerge_at_@suff@(@type@ *arr, npy_intp *tosort, const run *stack, l1 -= k; p2 = tosort + s2; /* tosort[s2-1] belongs to tosort[s2+l2] */ - l2 = agallop_left_@suff@(arr, tosort + s2, l2, arr[tosort[s2 - 1]]); + l2 = agallop_left_<Tag>(arr, tosort + s2, l2, arr[tosort[s2 - 1]]); if (l2 < l1) { ret = resize_buffer_intp(buffer, l2); - if (NPY_UNLIKELY(ret < 0)) { return ret; } + if (NPY_UNLIKELY(ret < 0)) { + return ret; + } - amerge_right_@suff@(arr, p1, l1, p2, l2, buffer->pw); - } else { + amerge_right_<Tag>(arr, p1, l1, p2, l2, buffer->pw); + } + else { ret = resize_buffer_intp(buffer, l1); - if (NPY_UNLIKELY(ret < 0)) { return ret; } + if (NPY_UNLIKELY(ret < 0)) { + return ret; + } - amerge_left_@suff@(arr, p1, l1, p2, l2, buffer->pw); + amerge_left_<Tag>(arr, p1, l1, p2, l2, buffer->pw); } return 0; } - +template <typename Tag, typename type> static int -atry_collapse_@suff@(@type@ *arr, npy_intp *tosort, run *stack, - npy_intp *stack_ptr, - buffer_intp *buffer) +atry_collapse_(type *arr, npy_intp *tosort, run *stack, npy_intp *stack_ptr, + buffer_intp *buffer) { int ret; npy_intp A, B, C, top; @@ -779,33 +812,42 @@ atry_collapse_@suff@(@type@ *arr, npy_intp *tosort, run *stack, C = stack[top - 1].l; if ((2 < top && stack[top - 3].l <= B + C) || - (3 < top && stack[top - 4].l <= stack[top - 3].l + B)) { + (3 < top && stack[top - 4].l <= stack[top - 3].l + B)) { A = stack[top - 3].l; if (A <= C) { - ret = amerge_at_@suff@(arr, tosort, stack, top - 3, buffer); + ret = amerge_at_<Tag>(arr, tosort, stack, top - 3, buffer); - if (NPY_UNLIKELY(ret < 0)) { return ret; } + if (NPY_UNLIKELY(ret < 0)) { + return ret; + } stack[top - 3].l += B; stack[top - 2] = stack[top - 1]; --top; - } else { - ret = amerge_at_@suff@(arr, tosort, stack, top - 2, buffer); + } + else { + ret = amerge_at_<Tag>(arr, tosort, stack, top - 2, buffer); - if (NPY_UNLIKELY(ret < 0)) { return ret; } + if (NPY_UNLIKELY(ret < 0)) { + return ret; + } stack[top - 2].l += C; --top; } - } else if (1 < top && B <= C) { - ret = amerge_at_@suff@(arr, tosort, stack, top - 2, buffer); + } + else if (1 < top && B <= C) { + ret = amerge_at_<Tag>(arr, tosort, stack, top - 2, buffer); - if (NPY_UNLIKELY(ret < 0)) { return ret; } + if (NPY_UNLIKELY(ret < 0)) { + return ret; + } stack[top - 2].l += C; --top; - } else { + } + else { break; } } @@ -814,28 +856,32 @@ atry_collapse_@suff@(@type@ *arr, npy_intp *tosort, run *stack, return 0; } - +template <typename Tag, typename type> static int -aforce_collapse_@suff@(@type@ *arr, npy_intp *tosort, run *stack, - npy_intp *stack_ptr, - buffer_intp *buffer) +aforce_collapse_(type *arr, npy_intp *tosort, run *stack, npy_intp *stack_ptr, + buffer_intp *buffer) { int ret; npy_intp top = *stack_ptr; while (2 < top) { if (stack[top - 3].l <= stack[top - 1].l) { - ret = amerge_at_@suff@(arr, tosort, stack, top - 3, buffer); + ret = amerge_at_<Tag>(arr, tosort, stack, top - 3, buffer); - if (NPY_UNLIKELY(ret < 0)) { return ret; } + if (NPY_UNLIKELY(ret < 0)) { + return ret; + } stack[top - 3].l += stack[top - 2].l; stack[top - 2] = stack[top - 1]; --top; - } else { - ret = amerge_at_@suff@(arr, tosort, stack, top - 2, buffer); + } + else { + ret = amerge_at_<Tag>(arr, tosort, stack, top - 2, buffer); - if (NPY_UNLIKELY(ret < 0)) { return ret; } + if (NPY_UNLIKELY(ret < 0)) { + return ret; + } stack[top - 2].l += stack[top - 1].l; --top; @@ -843,19 +889,21 @@ aforce_collapse_@suff@(@type@ *arr, npy_intp *tosort, run *stack, } if (1 < top) { - ret = amerge_at_@suff@(arr, tosort, stack, top - 2, buffer); + ret = amerge_at_<Tag>(arr, tosort, stack, top - 2, buffer); - if (NPY_UNLIKELY(ret < 0)) { return ret; } + if (NPY_UNLIKELY(ret < 0)) { + return ret; + } } return 0; } - -NPY_NO_EXPORT int -atimsort_@suff@(void *v, npy_intp *tosort, npy_intp num, - void *NPY_UNUSED(varr)) +template <typename Tag> +static int +atimsort_(void *v, npy_intp *tosort, npy_intp num) { + using type = typename Tag::type; int ret; npy_intp l, n, stack_ptr, minrun; buffer_intp buffer; @@ -866,20 +914,25 @@ atimsort_@suff@(void *v, npy_intp *tosort, npy_intp num, minrun = compute_min_run(num); for (l = 0; l < num;) { - n = acount_run_@suff@(v, tosort, l, num, minrun); + n = acount_run_<Tag>((type *)v, tosort, l, num, minrun); stack[stack_ptr].s = l; stack[stack_ptr].l = n; ++stack_ptr; - ret = atry_collapse_@suff@(v, tosort, stack, &stack_ptr, &buffer); + ret = atry_collapse_<Tag>((type *)v, tosort, stack, &stack_ptr, + &buffer); - if (NPY_UNLIKELY(ret < 0)) { goto cleanup; } + if (NPY_UNLIKELY(ret < 0)) { + goto cleanup; + } l += n; } - ret = aforce_collapse_@suff@(v, tosort, stack, &stack_ptr, &buffer); + ret = aforce_collapse_<Tag>((type *)v, tosort, stack, &stack_ptr, &buffer); - if (NPY_UNLIKELY(ret < 0)) { goto cleanup; } + if (NPY_UNLIKELY(ret < 0)) { + goto cleanup; + } ret = 0; cleanup: @@ -891,18 +944,15 @@ cleanup: return ret; } -/**end repeat**/ - - - /* For string sorts and generic sort, element comparisons are very expensive, - * and the time cost of insertion sort (involves N**2 comparison) clearly hurts. - * Implementing binary insertion sort and probably gallop mode during merging process - * can hopefully boost the performance. Here as a temporary workaround we use shorter - * run length to reduce the cost of insertion sort. + * and the time cost of insertion sort (involves N**2 comparison) clearly + * hurts. Implementing binary insertion sort and probably gallop mode during + * merging process can hopefully boost the performance. Here as a temporary + * workaround we use shorter run length to reduce the cost of insertion sort. */ -static npy_intp compute_min_run_short(npy_intp num) +static npy_intp +compute_min_run_short(npy_intp num) { npy_intp r = 0; @@ -920,51 +970,47 @@ static npy_intp compute_min_run_short(npy_intp num) ***************************************************************************** */ - -/**begin repeat - * - * #TYPE = STRING, UNICODE# - * #suff = string, unicode# - * #type = npy_char, npy_ucs4# - */ - - -typedef struct { - @type@ * pw; +template <typename Tag> +struct string_buffer_ { + typename Tag::type *pw; npy_intp size; size_t len; -} buffer_@suff@; - +}; +template <typename Tag> static NPY_INLINE int -resize_buffer_@suff@(buffer_@suff@ *buffer, npy_intp new_size) +resize_buffer_(string_buffer_<Tag> *buffer, npy_intp new_size) { + using type = typename Tag::type; if (new_size <= buffer->size) { return 0; } if (NPY_UNLIKELY(buffer->pw == NULL)) { - buffer->pw = malloc(sizeof(@type@) * new_size * buffer->len); - } else { - buffer->pw = realloc(buffer->pw, sizeof(@type@) * new_size * buffer->len); + buffer->pw = (type *)malloc(sizeof(type) * new_size * buffer->len); + } + else { + buffer->pw = (type *)realloc(buffer->pw, + sizeof(type) * new_size * buffer->len); } buffer->size = new_size; if (NPY_UNLIKELY(buffer->pw == NULL)) { return -NPY_ENOMEM; - } else { + } + else { return 0; } } - +template <typename Tag, typename type> static npy_intp -count_run_@suff@(@type@ *arr, npy_intp l, npy_intp num, npy_intp minrun, - @type@ *vp, size_t len) +count_run_(type *arr, npy_intp l, npy_intp num, npy_intp minrun, type *vp, + size_t len) { npy_intp sz; - @type@ *pl, *pi, *pj, *pr; + type *pl, *pi, *pj, *pr; if (NPY_UNLIKELY(num - l == 1)) { return 1; @@ -973,17 +1019,20 @@ count_run_@suff@(@type@ *arr, npy_intp l, npy_intp num, npy_intp minrun, pl = arr + l * len; /* (not strictly) ascending sequence */ - if (!@TYPE@_LT(pl + len, pl, len)) { - for (pi = pl + len; pi < arr + (num - 1) * len - && !@TYPE@_LT(pi + len, pi, len); pi += len) { + if (!Tag::less(pl + len, pl, len)) { + for (pi = pl + len; + pi < arr + (num - 1) * len && !Tag::less(pi + len, pi, len); + pi += len) { } - } else { /* (strictly) descending sequence */ - for (pi = pl + len; pi < arr + (num - 1) * len - && @TYPE@_LT(pi + len, pi, len); pi += len) { + } + else { /* (strictly) descending sequence */ + for (pi = pl + len; + pi < arr + (num - 1) * len && Tag::less(pi + len, pi, len); + pi += len) { } for (pj = pl, pr = pi; pj < pr; pj += len, pr -= len) { - @TYPE@_SWAP(pj, pr, len); + Tag::swap(pj, pr, len); } } @@ -993,7 +1042,8 @@ count_run_@suff@(@type@ *arr, npy_intp l, npy_intp num, npy_intp minrun, if (sz < minrun) { if (l + minrun < num) { sz = minrun; - } else { + } + else { sz = num - l; } @@ -1001,29 +1051,29 @@ count_run_@suff@(@type@ *arr, npy_intp l, npy_intp num, npy_intp minrun, /* insertion sort */ for (; pi < pr; pi += len) { - @TYPE@_COPY(vp, pi, len); + Tag::copy(vp, pi, len); pj = pi; - while (pl < pj && @TYPE@_LT(vp, pj - len, len)) { - @TYPE@_COPY(pj, pj - len, len); + while (pl < pj && Tag::less(vp, pj - len, len)) { + Tag::copy(pj, pj - len, len); pj -= len; } - @TYPE@_COPY(pj, vp, len); + Tag::copy(pj, vp, len); } } return sz; } - +template <typename Tag> static npy_intp -gallop_right_@suff@(const @type@ *arr, const npy_intp size, - const @type@ *key, size_t len) +gallop_right_(const typename Tag::type *arr, const npy_intp size, + const typename Tag::type *key, size_t len) { npy_intp last_ofs, ofs, m; - if (@TYPE@_LT(key, arr, len)) { + if (Tag::less(key, arr, len)) { return 0; } @@ -1036,9 +1086,10 @@ gallop_right_@suff@(const @type@ *arr, const npy_intp size, break; } - if (@TYPE@_LT(key, arr + ofs * len, len)) { + if (Tag::less(key, arr + ofs * len, len)) { break; - } else { + } + else { last_ofs = ofs; /* ofs = 1, 3, 7, 15... */ ofs = (ofs << 1) + 1; @@ -1049,9 +1100,10 @@ gallop_right_@suff@(const @type@ *arr, const npy_intp size, while (last_ofs + 1 < ofs) { m = last_ofs + ((ofs - last_ofs) >> 1); - if (@TYPE@_LT(key, arr + m * len, len)) { + if (Tag::less(key, arr + m * len, len)) { ofs = m; - } else { + } + else { last_ofs = m; } } @@ -1060,15 +1112,14 @@ gallop_right_@suff@(const @type@ *arr, const npy_intp size, return ofs; } - - +template <typename Tag> static npy_intp -gallop_left_@suff@(const @type@ *arr, const npy_intp size, const @type@ *key, - size_t len) +gallop_left_(const typename Tag::type *arr, const npy_intp size, + const typename Tag::type *key, size_t len) { npy_intp last_ofs, ofs, l, m, r; - if (@TYPE@_LT(arr + (size - 1) * len, key, len)) { + if (Tag::less(arr + (size - 1) * len, key, len)) { return size; } @@ -1081,9 +1132,10 @@ gallop_left_@suff@(const @type@ *arr, const npy_intp size, const @type@ *key, break; } - if (@TYPE@_LT(arr + (size - ofs - 1) * len, key, len)) { + if (Tag::less(arr + (size - ofs - 1) * len, key, len)) { break; - } else { + } + else { last_ofs = ofs; ofs = (ofs << 1) + 1; } @@ -1096,9 +1148,10 @@ gallop_left_@suff@(const @type@ *arr, const npy_intp size, const @type@ *key, while (l + 1 < r) { m = l + ((r - l) >> 1); - if (@TYPE@_LT(arr + m * len, key, len)) { + if (Tag::less(arr + m * len, key, len)) { l = m; - } else { + } + else { r = m; } } @@ -1107,58 +1160,61 @@ gallop_left_@suff@(const @type@ *arr, const npy_intp size, const @type@ *key, return r; } - +template <typename Tag> static void -merge_left_@suff@(@type@ *p1, npy_intp l1, @type@ *p2, npy_intp l2, - @type@ *p3, size_t len) +merge_left_(typename Tag::type *p1, npy_intp l1, typename Tag::type *p2, + npy_intp l2, typename Tag::type *p3, size_t len) { - @type@ *end = p2 + l2 * len; - memcpy(p3, p1, sizeof(@type@) * l1 * len); + using type = typename Tag::type; + type *end = p2 + l2 * len; + memcpy(p3, p1, sizeof(type) * l1 * len); /* first element must be in p2 otherwise skipped in the caller */ - @TYPE@_COPY(p1, p2, len); + Tag::copy(p1, p2, len); p1 += len; p2 += len; while (p1 < p2 && p2 < end) { - if (@TYPE@_LT(p2, p3, len)) { - @TYPE@_COPY(p1, p2, len); + if (Tag::less(p2, p3, len)) { + Tag::copy(p1, p2, len); p1 += len; p2 += len; - } else { - @TYPE@_COPY(p1, p3, len); + } + else { + Tag::copy(p1, p3, len); p1 += len; p3 += len; } } if (p1 != p2) { - memcpy(p1, p3, sizeof(@type@) * (p2 - p1)); + memcpy(p1, p3, sizeof(type) * (p2 - p1)); } } - +template <typename Tag, typename type> static void -merge_right_@suff@(@type@ *p1, npy_intp l1, @type@ *p2, npy_intp l2, - @type@ *p3, size_t len) +merge_right_(type *p1, npy_intp l1, type *p2, npy_intp l2, type *p3, + size_t len) { npy_intp ofs; - @type@ *start = p1 - len; - memcpy(p3, p2, sizeof(@type@) * l2 * len); + type *start = p1 - len; + memcpy(p3, p2, sizeof(type) * l2 * len); p1 += (l1 - 1) * len; p2 += (l2 - 1) * len; p3 += (l2 - 1) * len; /* first element must be in p1 otherwise skipped in the caller */ - @TYPE@_COPY(p2, p1, len); + Tag::copy(p2, p1, len); p2 -= len; p1 -= len; while (p1 < p2 && start < p1) { - if (@TYPE@_LT(p3, p1, len)) { - @TYPE@_COPY(p2, p1, len); + if (Tag::less(p3, p1, len)) { + Tag::copy(p2, p1, len); p2 -= len; p1 -= len; - } else { - @TYPE@_COPY(p2, p3, len); + } + else { + Tag::copy(p2, p3, len); p2 -= len; p3 -= len; } @@ -1166,25 +1222,25 @@ merge_right_@suff@(@type@ *p1, npy_intp l1, @type@ *p2, npy_intp l2, if (p1 != p2) { ofs = p2 - start; - memcpy(start + len, p3 - ofs + len, sizeof(@type@) * ofs); + memcpy(start + len, p3 - ofs + len, sizeof(type) * ofs); } } - +template <typename Tag, typename type> static int -merge_at_@suff@(@type@ *arr, const run *stack, const npy_intp at, - buffer_@suff@ *buffer, size_t len) +merge_at_(type *arr, const run *stack, const npy_intp at, + string_buffer_<Tag> *buffer, size_t len) { int ret; npy_intp s1, l1, s2, l2, k; - @type@ *p1, *p2; + type *p1, *p2; s1 = stack[at].s; l1 = stack[at].l; s2 = stack[at + 1].s; l2 = stack[at + 1].l; /* arr[s2] belongs to arr[s1+k] */ - @TYPE@_COPY(buffer->pw, arr + s2 * len, len); - k = gallop_right_@suff@(arr + s1 * len, l1, buffer->pw, len); + Tag::copy(buffer->pw, arr + s2 * len, len); + k = gallop_right_<Tag>(arr + s1 * len, l1, buffer->pw, len); if (l1 == k) { /* already sorted */ @@ -1195,30 +1251,35 @@ merge_at_@suff@(@type@ *arr, const run *stack, const npy_intp at, l1 -= k; p2 = arr + s2 * len; /* arr[s2-1] belongs to arr[s2+l2] */ - @TYPE@_COPY(buffer->pw, arr + (s2 - 1) * len, len); - l2 = gallop_left_@suff@(arr + s2 * len, l2, buffer->pw, len); + Tag::copy(buffer->pw, arr + (s2 - 1) * len, len); + l2 = gallop_left_<Tag>(arr + s2 * len, l2, buffer->pw, len); if (l2 < l1) { - ret = resize_buffer_@suff@(buffer, l2); + ret = resize_buffer_<Tag>(buffer, l2); - if (NPY_UNLIKELY(ret < 0)) { return ret; } + if (NPY_UNLIKELY(ret < 0)) { + return ret; + } - merge_right_@suff@(p1, l1, p2, l2, buffer->pw, len); - } else { - ret = resize_buffer_@suff@(buffer, l1); + merge_right_<Tag>(p1, l1, p2, l2, buffer->pw, len); + } + else { + ret = resize_buffer_<Tag>(buffer, l1); - if (NPY_UNLIKELY(ret < 0)) { return ret; } + if (NPY_UNLIKELY(ret < 0)) { + return ret; + } - merge_left_@suff@(p1, l1, p2, l2, buffer->pw, len); + merge_left_<Tag>(p1, l1, p2, l2, buffer->pw, len); } return 0; } - +template <typename Tag, typename type> static int -try_collapse_@suff@(@type@ *arr, run *stack, npy_intp *stack_ptr, - buffer_@suff@ *buffer, size_t len) +try_collapse_(type *arr, run *stack, npy_intp *stack_ptr, + string_buffer_<Tag> *buffer, size_t len) { int ret; npy_intp A, B, C, top; @@ -1229,33 +1290,42 @@ try_collapse_@suff@(@type@ *arr, run *stack, npy_intp *stack_ptr, C = stack[top - 1].l; if ((2 < top && stack[top - 3].l <= B + C) || - (3 < top && stack[top - 4].l <= stack[top - 3].l + B)) { + (3 < top && stack[top - 4].l <= stack[top - 3].l + B)) { A = stack[top - 3].l; if (A <= C) { - ret = merge_at_@suff@(arr, stack, top - 3, buffer, len); + ret = merge_at_<Tag>(arr, stack, top - 3, buffer, len); - if (NPY_UNLIKELY(ret < 0)) { return ret; } + if (NPY_UNLIKELY(ret < 0)) { + return ret; + } stack[top - 3].l += B; stack[top - 2] = stack[top - 1]; --top; - } else { - ret = merge_at_@suff@(arr, stack, top - 2, buffer, len); + } + else { + ret = merge_at_<Tag>(arr, stack, top - 2, buffer, len); - if (NPY_UNLIKELY(ret < 0)) { return ret; } + if (NPY_UNLIKELY(ret < 0)) { + return ret; + } stack[top - 2].l += C; --top; } - } else if (1 < top && B <= C) { - ret = merge_at_@suff@(arr, stack, top - 2, buffer, len); + } + else if (1 < top && B <= C) { + ret = merge_at_<Tag>(arr, stack, top - 2, buffer, len); - if (NPY_UNLIKELY(ret < 0)) { return ret; } + if (NPY_UNLIKELY(ret < 0)) { + return ret; + } stack[top - 2].l += C; --top; - } else { + } + else { break; } } @@ -1264,27 +1334,32 @@ try_collapse_@suff@(@type@ *arr, run *stack, npy_intp *stack_ptr, return 0; } - +template <typename Tag, typename type> static int -force_collapse_@suff@(@type@ *arr, run *stack, npy_intp *stack_ptr, - buffer_@suff@ *buffer, size_t len) +force_collapse_(type *arr, run *stack, npy_intp *stack_ptr, + string_buffer_<Tag> *buffer, size_t len) { int ret; npy_intp top = *stack_ptr; while (2 < top) { if (stack[top - 3].l <= stack[top - 1].l) { - ret = merge_at_@suff@(arr, stack, top - 3, buffer, len); + ret = merge_at_<Tag>(arr, stack, top - 3, buffer, len); - if (NPY_UNLIKELY(ret < 0)) { return ret; } + if (NPY_UNLIKELY(ret < 0)) { + return ret; + } stack[top - 3].l += stack[top - 2].l; stack[top - 2] = stack[top - 1]; --top; - } else { - ret = merge_at_@suff@(arr, stack, top - 2, buffer, len); + } + else { + ret = merge_at_<Tag>(arr, stack, top - 2, buffer, len); - if (NPY_UNLIKELY(ret < 0)) { return ret; } + if (NPY_UNLIKELY(ret < 0)) { + return ret; + } stack[top - 2].l += stack[top - 1].l; --top; @@ -1292,25 +1367,28 @@ force_collapse_@suff@(@type@ *arr, run *stack, npy_intp *stack_ptr, } if (1 < top) { - ret = merge_at_@suff@(arr, stack, top - 2, buffer, len); + ret = merge_at_<Tag>(arr, stack, top - 2, buffer, len); - if (NPY_UNLIKELY(ret < 0)) { return ret; } + if (NPY_UNLIKELY(ret < 0)) { + return ret; + } } return 0; } - +template <typename Tag> NPY_NO_EXPORT int -timsort_@suff@(void *start, npy_intp num, void *varr) +string_timsort_(void *start, npy_intp num, void *varr) { - PyArrayObject *arr = varr; + using type = typename Tag::type; + PyArrayObject *arr = reinterpret_cast<PyArrayObject *>(varr); size_t elsize = PyArray_ITEMSIZE(arr); - size_t len = elsize / sizeof(@type@); + size_t len = elsize / sizeof(type); int ret; npy_intp l, n, stack_ptr, minrun; run stack[TIMSORT_STACK_SIZE]; - buffer_@suff@ buffer; + string_buffer_<Tag> buffer; /* Items that have zero size don't make sense to sort */ if (len == 0) { @@ -1323,26 +1401,33 @@ timsort_@suff@(void *start, npy_intp num, void *varr) stack_ptr = 0; minrun = compute_min_run_short(num); /* used for insertion sort and gallop key */ - ret = resize_buffer_@suff@(&buffer, 1); + ret = resize_buffer_<Tag>(&buffer, 1); - if (NPY_UNLIKELY(ret < 0)) { goto cleanup; } + if (NPY_UNLIKELY(ret < 0)) { + goto cleanup; + } for (l = 0; l < num;) { - n = count_run_@suff@(start, l, num, minrun, buffer.pw, len); + n = count_run_<Tag>((type *)start, l, num, minrun, buffer.pw, len); /* both s and l are scaled by len */ stack[stack_ptr].s = l; stack[stack_ptr].l = n; ++stack_ptr; - ret = try_collapse_@suff@(start, stack, &stack_ptr, &buffer, len); + ret = try_collapse_<Tag>((type *)start, stack, &stack_ptr, &buffer, + len); - if (NPY_UNLIKELY(ret < 0)) { goto cleanup; } + if (NPY_UNLIKELY(ret < 0)) { + goto cleanup; + } l += n; } - ret = force_collapse_@suff@(start, stack, &stack_ptr, &buffer, len); + ret = force_collapse_<Tag>((type *)start, stack, &stack_ptr, &buffer, len); - if (NPY_UNLIKELY(ret < 0)) { goto cleanup; } + if (NPY_UNLIKELY(ret < 0)) { + goto cleanup; + } ret = 0; @@ -1353,13 +1438,12 @@ cleanup: return ret; } - /* argsort */ - +template <typename Tag, typename type> static npy_intp -acount_run_@suff@(@type@ *arr, npy_intp *tosort, npy_intp l, npy_intp num, - npy_intp minrun, size_t len) +acount_run_(type *arr, npy_intp *tosort, npy_intp l, npy_intp num, + npy_intp minrun, size_t len) { npy_intp sz; npy_intp vi; @@ -1372,17 +1456,22 @@ acount_run_@suff@(@type@ *arr, npy_intp *tosort, npy_intp l, npy_intp num, pl = tosort + l; /* (not strictly) ascending sequence */ - if (!@TYPE@_LT(arr + (*(pl + 1)) * len, arr + (*pl) * len, len)) { - for (pi = pl + 1; pi < tosort + num - 1 - && !@TYPE@_LT(arr + (*(pi + 1)) * len, arr + (*pi) * len, len); ++pi) { + if (!Tag::less(arr + (*(pl + 1)) * len, arr + (*pl) * len, len)) { + for (pi = pl + 1; + pi < tosort + num - 1 && + !Tag::less(arr + (*(pi + 1)) * len, arr + (*pi) * len, len); + ++pi) { } - } else { /* (strictly) descending sequence */ - for (pi = pl + 1; pi < tosort + num - 1 - && @TYPE@_LT(arr + (*(pi + 1)) * len, arr + (*pi) * len, len); ++pi) { + } + else { /* (strictly) descending sequence */ + for (pi = pl + 1; + pi < tosort + num - 1 && + Tag::less(arr + (*(pi + 1)) * len, arr + (*pi) * len, len); + ++pi) { } for (pj = pl, pr = pi; pj < pr; ++pj, --pr) { - INTP_SWAP(*pj, *pr); + std::swap(*pj, *pr); } } @@ -1392,7 +1481,8 @@ acount_run_@suff@(@type@ *arr, npy_intp *tosort, npy_intp l, npy_intp num, if (sz < minrun) { if (l + minrun < num) { sz = minrun; - } else { + } + else { sz = num - l; } @@ -1403,7 +1493,8 @@ acount_run_@suff@(@type@ *arr, npy_intp *tosort, npy_intp l, npy_intp num, vi = *pi; pj = pi; - while (pl < pj && @TYPE@_LT(arr + vi * len, arr + (*(pj - 1)) * len, len)) { + while (pl < pj && + Tag::less(arr + vi * len, arr + (*(pj - 1)) * len, len)) { *pj = *(pj - 1); --pj; } @@ -1415,14 +1506,14 @@ acount_run_@suff@(@type@ *arr, npy_intp *tosort, npy_intp l, npy_intp num, return sz; } - +template <typename Tag, typename type> static npy_intp -agallop_left_@suff@(const @type@ *arr, const npy_intp *tosort, - const npy_intp size, const @type@ *key, size_t len) +agallop_left_(const type *arr, const npy_intp *tosort, const npy_intp size, + const type *key, size_t len) { npy_intp last_ofs, ofs, l, m, r; - if (@TYPE@_LT(arr + tosort[size - 1] * len, key, len)) { + if (Tag::less(arr + tosort[size - 1] * len, key, len)) { return size; } @@ -1435,24 +1526,27 @@ agallop_left_@suff@(const @type@ *arr, const npy_intp *tosort, break; } - if (@TYPE@_LT(arr + tosort[size - ofs - 1] * len, key, len)) { + if (Tag::less(arr + tosort[size - ofs - 1] * len, key, len)) { break; - } else { + } + else { last_ofs = ofs; ofs = (ofs << 1) + 1; } } - /* now that arr[tosort[size-ofs-1]*len] < key <= arr[tosort[size-last_ofs-1]*len] */ + /* now that arr[tosort[size-ofs-1]*len] < key <= + * arr[tosort[size-last_ofs-1]*len] */ l = size - ofs - 1; r = size - last_ofs - 1; while (l + 1 < r) { m = l + ((r - l) >> 1); - if (@TYPE@_LT(arr + tosort[m] * len, key, len)) { + if (Tag::less(arr + tosort[m] * len, key, len)) { l = m; - } else { + } + else { r = m; } } @@ -1461,14 +1555,14 @@ agallop_left_@suff@(const @type@ *arr, const npy_intp *tosort, return r; } - +template <typename Tag, typename type> static npy_intp -agallop_right_@suff@(const @type@ *arr, const npy_intp *tosort, - const npy_intp size, const @type@ *key, size_t len) +agallop_right_(const type *arr, const npy_intp *tosort, const npy_intp size, + const type *key, size_t len) { npy_intp last_ofs, ofs, m; - if (@TYPE@_LT(key, arr + tosort[0] * len, len)) { + if (Tag::less(key, arr + tosort[0] * len, len)) { return 0; } @@ -1481,9 +1575,10 @@ agallop_right_@suff@(const @type@ *arr, const npy_intp *tosort, break; } - if (@TYPE@_LT(key, arr + tosort[ofs] * len, len)) { + if (Tag::less(key, arr + tosort[ofs] * len, len)) { break; - } else { + } + else { last_ofs = ofs; /* ofs = 1, 3, 7, 15... */ ofs = (ofs << 1) + 1; @@ -1494,9 +1589,10 @@ agallop_right_@suff@(const @type@ *arr, const npy_intp *tosort, while (last_ofs + 1 < ofs) { m = last_ofs + ((ofs - last_ofs) >> 1); - if (@TYPE@_LT(key, arr + tosort[m] * len, len)) { + if (Tag::less(key, arr + tosort[m] * len, len)) { ofs = m; - } else { + } + else { last_ofs = m; } } @@ -1505,11 +1601,10 @@ agallop_right_@suff@(const @type@ *arr, const npy_intp *tosort, return ofs; } - - +template <typename Tag, typename type> static void -amerge_left_@suff@(@type@ *arr, npy_intp *p1, npy_intp l1, npy_intp *p2, - npy_intp l2, npy_intp *p3, size_t len) +amerge_left_(type *arr, npy_intp *p1, npy_intp l1, npy_intp *p2, npy_intp l2, + npy_intp *p3, size_t len) { npy_intp *end = p2 + l2; memcpy(p3, p1, sizeof(npy_intp) * l1); @@ -1517,9 +1612,10 @@ amerge_left_@suff@(@type@ *arr, npy_intp *p1, npy_intp l1, npy_intp *p2, *p1++ = *p2++; while (p1 < p2 && p2 < end) { - if (@TYPE@_LT(arr + (*p2) * len, arr + (*p3) * len, len)) { + if (Tag::less(arr + (*p2) * len, arr + (*p3) * len, len)) { *p1++ = *p2++; - } else { + } + else { *p1++ = *p3++; } } @@ -1529,10 +1625,10 @@ amerge_left_@suff@(@type@ *arr, npy_intp *p1, npy_intp l1, npy_intp *p2, } } - +template <typename Tag, typename type> static void -amerge_right_@suff@(@type@ *arr, npy_intp* p1, npy_intp l1, npy_intp *p2, - npy_intp l2, npy_intp *p3, size_t len) +amerge_right_(type *arr, npy_intp *p1, npy_intp l1, npy_intp *p2, npy_intp l2, + npy_intp *p3, size_t len) { npy_intp ofs; npy_intp *start = p1 - 1; @@ -1544,9 +1640,10 @@ amerge_right_@suff@(@type@ *arr, npy_intp* p1, npy_intp l1, npy_intp *p2, *p2-- = *p1--; while (p1 < p2 && start < p1) { - if (@TYPE@_LT(arr + (*p3) * len, arr + (*p1) * len, len)) { + if (Tag::less(arr + (*p3) * len, arr + (*p1) * len, len)) { *p2-- = *p1--; - } else { + } + else { *p2-- = *p3--; } } @@ -1557,11 +1654,10 @@ amerge_right_@suff@(@type@ *arr, npy_intp* p1, npy_intp l1, npy_intp *p2, } } - - +template <typename Tag, typename type> static int -amerge_at_@suff@(@type@ *arr, npy_intp *tosort, const run *stack, - const npy_intp at, buffer_intp *buffer, size_t len) +amerge_at_(type *arr, npy_intp *tosort, const run *stack, const npy_intp at, + buffer_intp *buffer, size_t len) { int ret; npy_intp s1, l1, s2, l2, k; @@ -1571,7 +1667,7 @@ amerge_at_@suff@(@type@ *arr, npy_intp *tosort, const run *stack, s2 = stack[at + 1].s; l2 = stack[at + 1].l; /* tosort[s2] belongs to tosort[s1+k] */ - k = agallop_right_@suff@(arr, tosort + s1, l1, arr + tosort[s2] * len, len); + k = agallop_right_<Tag>(arr, tosort + s1, l1, arr + tosort[s2] * len, len); if (l1 == k) { /* already sorted */ @@ -1582,30 +1678,35 @@ amerge_at_@suff@(@type@ *arr, npy_intp *tosort, const run *stack, l1 -= k; p2 = tosort + s2; /* tosort[s2-1] belongs to tosort[s2+l2] */ - l2 = agallop_left_@suff@(arr, tosort + s2, l2, arr + tosort[s2 - 1] * len, - len); + l2 = agallop_left_<Tag>(arr, tosort + s2, l2, arr + tosort[s2 - 1] * len, + len); if (l2 < l1) { ret = resize_buffer_intp(buffer, l2); - if (NPY_UNLIKELY(ret < 0)) { return ret; } + if (NPY_UNLIKELY(ret < 0)) { + return ret; + } - amerge_right_@suff@(arr, p1, l1, p2, l2, buffer->pw, len); - } else { + amerge_right_<Tag>(arr, p1, l1, p2, l2, buffer->pw, len); + } + else { ret = resize_buffer_intp(buffer, l1); - if (NPY_UNLIKELY(ret < 0)) { return ret; } + if (NPY_UNLIKELY(ret < 0)) { + return ret; + } - amerge_left_@suff@(arr, p1, l1, p2, l2, buffer->pw, len); + amerge_left_<Tag>(arr, p1, l1, p2, l2, buffer->pw, len); } return 0; } - +template <typename Tag, typename type> static int -atry_collapse_@suff@(@type@ *arr, npy_intp *tosort, run *stack, - npy_intp *stack_ptr, buffer_intp *buffer, size_t len) +atry_collapse_(type *arr, npy_intp *tosort, run *stack, npy_intp *stack_ptr, + buffer_intp *buffer, size_t len) { int ret; npy_intp A, B, C, top; @@ -1616,33 +1717,44 @@ atry_collapse_@suff@(@type@ *arr, npy_intp *tosort, run *stack, C = stack[top - 1].l; if ((2 < top && stack[top - 3].l <= B + C) || - (3 < top && stack[top - 4].l <= stack[top - 3].l + B)) { + (3 < top && stack[top - 4].l <= stack[top - 3].l + B)) { A = stack[top - 3].l; if (A <= C) { - ret = amerge_at_@suff@(arr, tosort, stack, top - 3, buffer, len); + ret = amerge_at_<Tag>(arr, tosort, stack, top - 3, buffer, + len); - if (NPY_UNLIKELY(ret < 0)) { return ret; } + if (NPY_UNLIKELY(ret < 0)) { + return ret; + } stack[top - 3].l += B; stack[top - 2] = stack[top - 1]; --top; - } else { - ret = amerge_at_@suff@(arr, tosort, stack, top - 2, buffer, len); + } + else { + ret = amerge_at_<Tag>(arr, tosort, stack, top - 2, buffer, + len); - if (NPY_UNLIKELY(ret < 0)) { return ret; } + if (NPY_UNLIKELY(ret < 0)) { + return ret; + } stack[top - 2].l += C; --top; } - } else if (1 < top && B <= C) { - ret = amerge_at_@suff@(arr, tosort, stack, top - 2, buffer, len); + } + else if (1 < top && B <= C) { + ret = amerge_at_<Tag>(arr, tosort, stack, top - 2, buffer, len); - if (NPY_UNLIKELY(ret < 0)) { return ret; } + if (NPY_UNLIKELY(ret < 0)) { + return ret; + } stack[top - 2].l += C; --top; - } else { + } + else { break; } } @@ -1651,28 +1763,32 @@ atry_collapse_@suff@(@type@ *arr, npy_intp *tosort, run *stack, return 0; } - - +template <typename Tag, typename type> static int -aforce_collapse_@suff@(@type@ *arr, npy_intp *tosort, run *stack, - npy_intp *stack_ptr, buffer_intp *buffer, size_t len) +aforce_collapse_(type *arr, npy_intp *tosort, run *stack, npy_intp *stack_ptr, + buffer_intp *buffer, size_t len) { int ret; npy_intp top = *stack_ptr; while (2 < top) { if (stack[top - 3].l <= stack[top - 1].l) { - ret = amerge_at_@suff@(arr, tosort, stack, top - 3, buffer, len); + ret = amerge_at_<Tag>(arr, tosort, stack, top - 3, buffer, len); - if (NPY_UNLIKELY(ret < 0)) { return ret; } + if (NPY_UNLIKELY(ret < 0)) { + return ret; + } stack[top - 3].l += stack[top - 2].l; stack[top - 2] = stack[top - 1]; --top; - } else { - ret = amerge_at_@suff@(arr, tosort, stack, top - 2, buffer, len); + } + else { + ret = amerge_at_<Tag>(arr, tosort, stack, top - 2, buffer, len); - if (NPY_UNLIKELY(ret < 0)) { return ret; } + if (NPY_UNLIKELY(ret < 0)) { + return ret; + } stack[top - 2].l += stack[top - 1].l; --top; @@ -1680,21 +1796,24 @@ aforce_collapse_@suff@(@type@ *arr, npy_intp *tosort, run *stack, } if (1 < top) { - ret = amerge_at_@suff@(arr, tosort, stack, top - 2, buffer, len); + ret = amerge_at_<Tag>(arr, tosort, stack, top - 2, buffer, len); - if (NPY_UNLIKELY(ret < 0)) { return ret; } + if (NPY_UNLIKELY(ret < 0)) { + return ret; + } } return 0; } - +template <typename Tag> NPY_NO_EXPORT int -atimsort_@suff@(void *start, npy_intp *tosort, npy_intp num, void *varr) +string_atimsort_(void *start, npy_intp *tosort, npy_intp num, void *varr) { - PyArrayObject *arr = varr; + using type = typename Tag::type; + PyArrayObject *arr = reinterpret_cast<PyArrayObject *>(varr); size_t elsize = PyArray_ITEMSIZE(arr); - size_t len = elsize / sizeof(@type@); + size_t len = elsize / sizeof(type); int ret; npy_intp l, n, stack_ptr, minrun; run stack[TIMSORT_STACK_SIZE]; @@ -1711,21 +1830,27 @@ atimsort_@suff@(void *start, npy_intp *tosort, npy_intp num, void *varr) minrun = compute_min_run_short(num); for (l = 0; l < num;) { - n = acount_run_@suff@(start, tosort, l, num, minrun, len); + n = acount_run_<Tag>((type *)start, tosort, l, num, minrun, len); /* both s and l are scaled by len */ stack[stack_ptr].s = l; stack[stack_ptr].l = n; ++stack_ptr; - ret = atry_collapse_@suff@(start, tosort, stack, &stack_ptr, &buffer, len); + ret = atry_collapse_<Tag>((type *)start, tosort, stack, &stack_ptr, + &buffer, len); - if (NPY_UNLIKELY(ret < 0)) { goto cleanup; } + if (NPY_UNLIKELY(ret < 0)) { + goto cleanup; + } l += n; } - ret = aforce_collapse_@suff@(start, tosort, stack, &stack_ptr, &buffer, len); + ret = aforce_collapse_<Tag>((type *)start, tosort, stack, &stack_ptr, + &buffer, len); - if (NPY_UNLIKELY(ret < 0)) { goto cleanup; } + if (NPY_UNLIKELY(ret < 0)) { + goto cleanup; + } ret = 0; @@ -1736,25 +1861,18 @@ cleanup: return ret; } - -/**end repeat**/ - - - /* ***************************************************************************** ** GENERIC SORT ** ***************************************************************************** */ - typedef struct { char *pw; npy_intp size; size_t len; } buffer_char; - static NPY_INLINE int resize_buffer_char(buffer_char *buffer, npy_intp new_size) { @@ -1763,24 +1881,26 @@ resize_buffer_char(buffer_char *buffer, npy_intp new_size) } if (NPY_UNLIKELY(buffer->pw == NULL)) { - buffer->pw = malloc(sizeof(char) * new_size * buffer->len); - } else { - buffer->pw = realloc(buffer->pw, sizeof(char) * new_size * buffer->len); + buffer->pw = (char *)malloc(sizeof(char) * new_size * buffer->len); + } + else { + buffer->pw = (char *)realloc(buffer->pw, + sizeof(char) * new_size * buffer->len); } buffer->size = new_size; if (NPY_UNLIKELY(buffer->pw == NULL)) { return -NPY_ENOMEM; - } else { + } + else { return 0; } } - static npy_intp -npy_count_run(char *arr, npy_intp l, npy_intp num, npy_intp minrun, - char *vp, size_t len, PyArray_CompareFunc *cmp, PyArrayObject *py_arr) +npy_count_run(char *arr, npy_intp l, npy_intp num, npy_intp minrun, char *vp, + size_t len, PyArray_CompareFunc *cmp, PyArrayObject *py_arr) { npy_intp sz; char *pl, *pi, *pj, *pr; @@ -1793,12 +1913,15 @@ npy_count_run(char *arr, npy_intp l, npy_intp num, npy_intp minrun, /* (not strictly) ascending sequence */ if (cmp(pl, pl + len, py_arr) <= 0) { - for (pi = pl + len; pi < arr + (num - 1) * len - && cmp(pi, pi + len, py_arr) <= 0; pi += len) { + for (pi = pl + len; + pi < arr + (num - 1) * len && cmp(pi, pi + len, py_arr) <= 0; + pi += len) { } - } else { /* (strictly) descending sequence */ - for (pi = pl + len; pi < arr + (num - 1) * len - && cmp(pi + len, pi, py_arr) < 0; pi += len) { + } + else { /* (strictly) descending sequence */ + for (pi = pl + len; + pi < arr + (num - 1) * len && cmp(pi + len, pi, py_arr) < 0; + pi += len) { } for (pj = pl, pr = pi; pj < pr; pj += len, pr -= len) { @@ -1812,7 +1935,8 @@ npy_count_run(char *arr, npy_intp l, npy_intp num, npy_intp minrun, if (sz < minrun) { if (l + minrun < num) { sz = minrun; - } else { + } + else { sz = num - l; } @@ -1835,7 +1959,6 @@ npy_count_run(char *arr, npy_intp l, npy_intp num, npy_intp minrun, return sz; } - static npy_intp npy_gallop_right(const char *arr, const npy_intp size, const char *key, size_t len, PyArray_CompareFunc *cmp, PyArrayObject *py_arr) @@ -1857,7 +1980,8 @@ npy_gallop_right(const char *arr, const npy_intp size, const char *key, if (cmp(key, arr + ofs * len, py_arr) < 0) { break; - } else { + } + else { last_ofs = ofs; /* ofs = 1, 3, 7, 15... */ ofs = (ofs << 1) + 1; @@ -1870,7 +1994,8 @@ npy_gallop_right(const char *arr, const npy_intp size, const char *key, if (cmp(key, arr + m * len, py_arr) < 0) { ofs = m; - } else { + } + else { last_ofs = m; } } @@ -1879,8 +2004,6 @@ npy_gallop_right(const char *arr, const npy_intp size, const char *key, return ofs; } - - static npy_intp npy_gallop_left(const char *arr, const npy_intp size, const char *key, size_t len, PyArray_CompareFunc *cmp, PyArrayObject *py_arr) @@ -1902,7 +2025,8 @@ npy_gallop_left(const char *arr, const npy_intp size, const char *key, if (cmp(arr + (size - ofs - 1) * len, key, py_arr) < 0) { break; - } else { + } + else { last_ofs = ofs; ofs = (ofs << 1) + 1; } @@ -1917,7 +2041,8 @@ npy_gallop_left(const char *arr, const npy_intp size, const char *key, if (cmp(arr + m * len, key, py_arr) < 0) { l = m; - } else { + } + else { r = m; } } @@ -1926,11 +2051,9 @@ npy_gallop_left(const char *arr, const npy_intp size, const char *key, return r; } - static void -npy_merge_left(char *p1, npy_intp l1, char *p2, npy_intp l2, - char *p3, size_t len, - PyArray_CompareFunc *cmp, PyArrayObject *py_arr) +npy_merge_left(char *p1, npy_intp l1, char *p2, npy_intp l2, char *p3, + size_t len, PyArray_CompareFunc *cmp, PyArrayObject *py_arr) { char *end = p2 + l2 * len; memcpy(p3, p1, sizeof(char) * l1 * len); @@ -1944,7 +2067,8 @@ npy_merge_left(char *p1, npy_intp l1, char *p2, npy_intp l2, GENERIC_COPY(p1, p2, len); p1 += len; p2 += len; - } else { + } + else { GENERIC_COPY(p1, p3, len); p1 += len; p3 += len; @@ -1956,11 +2080,9 @@ npy_merge_left(char *p1, npy_intp l1, char *p2, npy_intp l2, } } - static void -npy_merge_right(char *p1, npy_intp l1, char *p2, npy_intp l2, - char *p3, size_t len, - PyArray_CompareFunc *cmp, PyArrayObject *py_arr) +npy_merge_right(char *p1, npy_intp l1, char *p2, npy_intp l2, char *p3, + size_t len, PyArray_CompareFunc *cmp, PyArrayObject *py_arr) { npy_intp ofs; char *start = p1 - len; @@ -1978,7 +2100,8 @@ npy_merge_right(char *p1, npy_intp l1, char *p2, npy_intp l2, GENERIC_COPY(p2, p1, len); p2 -= len; p1 -= len; - } else { + } + else { GENERIC_COPY(p2, p3, len); p2 -= len; p3 -= len; @@ -1991,12 +2114,10 @@ npy_merge_right(char *p1, npy_intp l1, char *p2, npy_intp l2, } } - - static int npy_merge_at(char *arr, const run *stack, const npy_intp at, - buffer_char *buffer, size_t len, - PyArray_CompareFunc *cmp, PyArrayObject *py_arr) + buffer_char *buffer, size_t len, PyArray_CompareFunc *cmp, + PyArrayObject *py_arr) { int ret; npy_intp s1, l1, s2, l2, k; @@ -2024,13 +2145,18 @@ npy_merge_at(char *arr, const run *stack, const npy_intp at, if (l2 < l1) { ret = resize_buffer_char(buffer, l2); - if (NPY_UNLIKELY(ret < 0)) { return ret; } + if (NPY_UNLIKELY(ret < 0)) { + return ret; + } npy_merge_right(p1, l1, p2, l2, buffer->pw, len, cmp, py_arr); - } else { + } + else { ret = resize_buffer_char(buffer, l1); - if (NPY_UNLIKELY(ret < 0)) { return ret; } + if (NPY_UNLIKELY(ret < 0)) { + return ret; + } npy_merge_left(p1, l1, p2, l2, buffer->pw, len, cmp, py_arr); } @@ -2038,11 +2164,10 @@ npy_merge_at(char *arr, const run *stack, const npy_intp at, return 0; } - static int npy_try_collapse(char *arr, run *stack, npy_intp *stack_ptr, - buffer_char *buffer, size_t len, - PyArray_CompareFunc *cmp, PyArrayObject *py_arr) + buffer_char *buffer, size_t len, PyArray_CompareFunc *cmp, + PyArrayObject *py_arr) { int ret; npy_intp A, B, C, top; @@ -2053,33 +2178,44 @@ npy_try_collapse(char *arr, run *stack, npy_intp *stack_ptr, C = stack[top - 1].l; if ((2 < top && stack[top - 3].l <= B + C) || - (3 < top && stack[top - 4].l <= stack[top - 3].l + B)) { + (3 < top && stack[top - 4].l <= stack[top - 3].l + B)) { A = stack[top - 3].l; if (A <= C) { - ret = npy_merge_at(arr, stack, top - 3, buffer, len, cmp, py_arr); + ret = npy_merge_at(arr, stack, top - 3, buffer, len, cmp, + py_arr); - if (NPY_UNLIKELY(ret < 0)) { return ret; } + if (NPY_UNLIKELY(ret < 0)) { + return ret; + } stack[top - 3].l += B; stack[top - 2] = stack[top - 1]; --top; - } else { - ret = npy_merge_at(arr, stack, top - 2, buffer, len, cmp, py_arr); + } + else { + ret = npy_merge_at(arr, stack, top - 2, buffer, len, cmp, + py_arr); - if (NPY_UNLIKELY(ret < 0)) { return ret; } + if (NPY_UNLIKELY(ret < 0)) { + return ret; + } stack[top - 2].l += C; --top; } - } else if (1 < top && B <= C) { + } + else if (1 < top && B <= C) { ret = npy_merge_at(arr, stack, top - 2, buffer, len, cmp, py_arr); - if (NPY_UNLIKELY(ret < 0)) { return ret; } + if (NPY_UNLIKELY(ret < 0)) { + return ret; + } stack[top - 2].l += C; --top; - } else { + } + else { break; } } @@ -2088,11 +2224,10 @@ npy_try_collapse(char *arr, run *stack, npy_intp *stack_ptr, return 0; } - static int npy_force_collapse(char *arr, run *stack, npy_intp *stack_ptr, - buffer_char *buffer, size_t len, - PyArray_CompareFunc *cmp, PyArrayObject *py_arr) + buffer_char *buffer, size_t len, PyArray_CompareFunc *cmp, + PyArrayObject *py_arr) { int ret; npy_intp top = *stack_ptr; @@ -2101,15 +2236,20 @@ npy_force_collapse(char *arr, run *stack, npy_intp *stack_ptr, if (stack[top - 3].l <= stack[top - 1].l) { ret = npy_merge_at(arr, stack, top - 3, buffer, len, cmp, py_arr); - if (NPY_UNLIKELY(ret < 0)) { return ret; } + if (NPY_UNLIKELY(ret < 0)) { + return ret; + } stack[top - 3].l += stack[top - 2].l; stack[top - 2] = stack[top - 1]; --top; - } else { + } + else { ret = npy_merge_at(arr, stack, top - 2, buffer, len, cmp, py_arr); - if (NPY_UNLIKELY(ret < 0)) { return ret; } + if (NPY_UNLIKELY(ret < 0)) { + return ret; + } stack[top - 2].l += stack[top - 1].l; --top; @@ -2119,17 +2259,18 @@ npy_force_collapse(char *arr, run *stack, npy_intp *stack_ptr, if (1 < top) { ret = npy_merge_at(arr, stack, top - 2, buffer, len, cmp, py_arr); - if (NPY_UNLIKELY(ret < 0)) { return ret; } + if (NPY_UNLIKELY(ret < 0)) { + return ret; + } } return 0; } - NPY_NO_EXPORT int npy_timsort(void *start, npy_intp num, void *varr) { - PyArrayObject *arr = varr; + PyArrayObject *arr = reinterpret_cast<PyArrayObject *>(varr); size_t len = PyArray_ITEMSIZE(arr); PyArray_CompareFunc *cmp = PyArray_DESCR(arr)->f->compare; int ret; @@ -2151,25 +2292,34 @@ npy_timsort(void *start, npy_intp num, void *varr) /* used for insertion sort and gallop key */ ret = resize_buffer_char(&buffer, len); - if (NPY_UNLIKELY(ret < 0)) { goto cleanup; } + if (NPY_UNLIKELY(ret < 0)) { + goto cleanup; + } for (l = 0; l < num;) { - n = npy_count_run(start, l, num, minrun, buffer.pw, len, cmp, arr); + n = npy_count_run((char *)start, l, num, minrun, buffer.pw, len, cmp, + arr); /* both s and l are scaled by len */ stack[stack_ptr].s = l; stack[stack_ptr].l = n; ++stack_ptr; - ret = npy_try_collapse(start, stack, &stack_ptr, &buffer, len, cmp, arr); + ret = npy_try_collapse((char *)start, stack, &stack_ptr, &buffer, len, + cmp, arr); - if (NPY_UNLIKELY(ret < 0)) { goto cleanup; } + if (NPY_UNLIKELY(ret < 0)) { + goto cleanup; + } l += n; } - ret = npy_force_collapse(start, stack, &stack_ptr, &buffer, len, cmp, arr); + ret = npy_force_collapse((char *)start, stack, &stack_ptr, &buffer, len, + cmp, arr); - if (NPY_UNLIKELY(ret < 0)) { goto cleanup; } + if (NPY_UNLIKELY(ret < 0)) { + goto cleanup; + } ret = 0; @@ -2180,13 +2330,12 @@ cleanup: return ret; } - /* argsort */ static npy_intp npy_acount_run(char *arr, npy_intp *tosort, npy_intp l, npy_intp num, - npy_intp minrun, size_t len, - PyArray_CompareFunc *cmp, PyArrayObject *py_arr) + npy_intp minrun, size_t len, PyArray_CompareFunc *cmp, + PyArrayObject *py_arr) { npy_intp sz; npy_intp vi; @@ -2200,16 +2349,21 @@ npy_acount_run(char *arr, npy_intp *tosort, npy_intp l, npy_intp num, /* (not strictly) ascending sequence */ if (cmp(arr + (*pl) * len, arr + (*(pl + 1)) * len, py_arr) <= 0) { - for (pi = pl + 1; pi < tosort + num - 1 - && cmp(arr + (*pi) * len, arr + (*(pi + 1)) * len, py_arr) <= 0; ++pi) { + for (pi = pl + 1; + pi < tosort + num - 1 && + cmp(arr + (*pi) * len, arr + (*(pi + 1)) * len, py_arr) <= 0; + ++pi) { } - } else { /* (strictly) descending sequence */ - for (pi = pl + 1; pi < tosort + num - 1 - && cmp(arr + (*(pi + 1)) * len, arr + (*pi) * len, py_arr) < 0; ++pi) { + } + else { /* (strictly) descending sequence */ + for (pi = pl + 1; + pi < tosort + num - 1 && + cmp(arr + (*(pi + 1)) * len, arr + (*pi) * len, py_arr) < 0; + ++pi) { } for (pj = pl, pr = pi; pj < pr; ++pj, --pr) { - INTP_SWAP(*pj, *pr); + std::swap(*pj, *pr); } } @@ -2219,7 +2373,8 @@ npy_acount_run(char *arr, npy_intp *tosort, npy_intp l, npy_intp num, if (sz < minrun) { if (l + minrun < num) { sz = minrun; - } else { + } + else { sz = num - l; } @@ -2230,7 +2385,8 @@ npy_acount_run(char *arr, npy_intp *tosort, npy_intp l, npy_intp num, vi = *pi; pj = pi; - while (pl < pj && cmp(arr + vi * len, arr + (*(pj - 1)) * len, py_arr) < 0) { + while (pl < pj && + cmp(arr + vi * len, arr + (*(pj - 1)) * len, py_arr) < 0) { *pj = *(pj - 1); --pj; } @@ -2242,11 +2398,10 @@ npy_acount_run(char *arr, npy_intp *tosort, npy_intp l, npy_intp num, return sz; } - static npy_intp -npy_agallop_left(const char *arr, const npy_intp *tosort, - const npy_intp size, const char *key, size_t len, - PyArray_CompareFunc *cmp, PyArrayObject *py_arr) +npy_agallop_left(const char *arr, const npy_intp *tosort, const npy_intp size, + const char *key, size_t len, PyArray_CompareFunc *cmp, + PyArrayObject *py_arr) { npy_intp last_ofs, ofs, l, m, r; @@ -2265,13 +2420,15 @@ npy_agallop_left(const char *arr, const npy_intp *tosort, if (cmp(arr + tosort[size - ofs - 1] * len, key, py_arr) < 0) { break; - } else { + } + else { last_ofs = ofs; ofs = (ofs << 1) + 1; } } - /* now that arr[tosort[size-ofs-1]*len] < key <= arr[tosort[size-last_ofs-1]*len] */ + /* now that arr[tosort[size-ofs-1]*len] < key <= + * arr[tosort[size-last_ofs-1]*len] */ l = size - ofs - 1; r = size - last_ofs - 1; @@ -2280,7 +2437,8 @@ npy_agallop_left(const char *arr, const npy_intp *tosort, if (cmp(arr + tosort[m] * len, key, py_arr) < 0) { l = m; - } else { + } + else { r = m; } } @@ -2289,11 +2447,10 @@ npy_agallop_left(const char *arr, const npy_intp *tosort, return r; } - static npy_intp -npy_agallop_right(const char *arr, const npy_intp *tosort, - const npy_intp size, const char *key, size_t len, - PyArray_CompareFunc *cmp, PyArrayObject *py_arr) +npy_agallop_right(const char *arr, const npy_intp *tosort, const npy_intp size, + const char *key, size_t len, PyArray_CompareFunc *cmp, + PyArrayObject *py_arr) { npy_intp last_ofs, ofs, m; @@ -2312,7 +2469,8 @@ npy_agallop_right(const char *arr, const npy_intp *tosort, if (cmp(key, arr + tosort[ofs] * len, py_arr) < 0) { break; - } else { + } + else { last_ofs = ofs; /* ofs = 1, 3, 7, 15... */ ofs = (ofs << 1) + 1; @@ -2325,7 +2483,8 @@ npy_agallop_right(const char *arr, const npy_intp *tosort, if (cmp(key, arr + tosort[m] * len, py_arr) < 0) { ofs = m; - } else { + } + else { last_ofs = m; } } @@ -2334,7 +2493,6 @@ npy_agallop_right(const char *arr, const npy_intp *tosort, return ofs; } - static void npy_amerge_left(char *arr, npy_intp *p1, npy_intp l1, npy_intp *p2, npy_intp l2, npy_intp *p3, size_t len, @@ -2348,7 +2506,8 @@ npy_amerge_left(char *arr, npy_intp *p1, npy_intp l1, npy_intp *p2, while (p1 < p2 && p2 < end) { if (cmp(arr + (*p2) * len, arr + (*p3) * len, py_arr) < 0) { *p1++ = *p2++; - } else { + } + else { *p1++ = *p3++; } } @@ -2358,9 +2517,8 @@ npy_amerge_left(char *arr, npy_intp *p1, npy_intp l1, npy_intp *p2, } } - static void -npy_amerge_right(char *arr, npy_intp* p1, npy_intp l1, npy_intp *p2, +npy_amerge_right(char *arr, npy_intp *p1, npy_intp l1, npy_intp *p2, npy_intp l2, npy_intp *p3, size_t len, PyArray_CompareFunc *cmp, PyArrayObject *py_arr) { @@ -2376,7 +2534,8 @@ npy_amerge_right(char *arr, npy_intp* p1, npy_intp l1, npy_intp *p2, while (p1 < p2 && start < p1) { if (cmp(arr + (*p3) * len, arr + (*p1) * len, py_arr) < 0) { *p2-- = *p1--; - } else { + } + else { *p2-- = *p3--; } } @@ -2387,12 +2546,10 @@ npy_amerge_right(char *arr, npy_intp* p1, npy_intp l1, npy_intp *p2, } } - - static int -npy_amerge_at(char *arr, npy_intp *tosort, const run *stack, - const npy_intp at, buffer_intp *buffer, size_t len, - PyArray_CompareFunc *cmp, PyArrayObject *py_arr) +npy_amerge_at(char *arr, npy_intp *tosort, const run *stack, const npy_intp at, + buffer_intp *buffer, size_t len, PyArray_CompareFunc *cmp, + PyArrayObject *py_arr) { int ret; npy_intp s1, l1, s2, l2, k; @@ -2402,8 +2559,8 @@ npy_amerge_at(char *arr, npy_intp *tosort, const run *stack, s2 = stack[at + 1].s; l2 = stack[at + 1].l; /* tosort[s2] belongs to tosort[s1+k] */ - k = npy_agallop_right(arr, tosort + s1, l1, arr + tosort[s2] * len, len, cmp, - py_arr); + k = npy_agallop_right(arr, tosort + s1, l1, arr + tosort[s2] * len, len, + cmp, py_arr); if (l1 == k) { /* already sorted */ @@ -2420,13 +2577,18 @@ npy_amerge_at(char *arr, npy_intp *tosort, const run *stack, if (l2 < l1) { ret = resize_buffer_intp(buffer, l2); - if (NPY_UNLIKELY(ret < 0)) { return ret; } + if (NPY_UNLIKELY(ret < 0)) { + return ret; + } npy_amerge_right(arr, p1, l1, p2, l2, buffer->pw, len, cmp, py_arr); - } else { + } + else { ret = resize_buffer_intp(buffer, l1); - if (NPY_UNLIKELY(ret < 0)) { return ret; } + if (NPY_UNLIKELY(ret < 0)) { + return ret; + } npy_amerge_left(arr, p1, l1, p2, l2, buffer->pw, len, cmp, py_arr); } @@ -2434,11 +2596,10 @@ npy_amerge_at(char *arr, npy_intp *tosort, const run *stack, return 0; } - static int -npy_atry_collapse(char *arr, npy_intp *tosort, run *stack, - npy_intp *stack_ptr, buffer_intp *buffer, size_t len, - PyArray_CompareFunc *cmp, PyArrayObject *py_arr) +npy_atry_collapse(char *arr, npy_intp *tosort, run *stack, npy_intp *stack_ptr, + buffer_intp *buffer, size_t len, PyArray_CompareFunc *cmp, + PyArrayObject *py_arr) { int ret; npy_intp A, B, C, top; @@ -2449,33 +2610,45 @@ npy_atry_collapse(char *arr, npy_intp *tosort, run *stack, C = stack[top - 1].l; if ((2 < top && stack[top - 3].l <= B + C) || - (3 < top && stack[top - 4].l <= stack[top - 3].l + B)) { + (3 < top && stack[top - 4].l <= stack[top - 3].l + B)) { A = stack[top - 3].l; if (A <= C) { - ret = npy_amerge_at(arr, tosort, stack, top - 3, buffer, len, cmp, py_arr); + ret = npy_amerge_at(arr, tosort, stack, top - 3, buffer, len, + cmp, py_arr); - if (NPY_UNLIKELY(ret < 0)) { return ret; } + if (NPY_UNLIKELY(ret < 0)) { + return ret; + } stack[top - 3].l += B; stack[top - 2] = stack[top - 1]; --top; - } else { - ret = npy_amerge_at(arr, tosort, stack, top - 2, buffer, len, cmp, py_arr); + } + else { + ret = npy_amerge_at(arr, tosort, stack, top - 2, buffer, len, + cmp, py_arr); - if (NPY_UNLIKELY(ret < 0)) { return ret; } + if (NPY_UNLIKELY(ret < 0)) { + return ret; + } stack[top - 2].l += C; --top; } - } else if (1 < top && B <= C) { - ret = npy_amerge_at(arr, tosort, stack, top - 2, buffer, len, cmp, py_arr); + } + else if (1 < top && B <= C) { + ret = npy_amerge_at(arr, tosort, stack, top - 2, buffer, len, cmp, + py_arr); - if (NPY_UNLIKELY(ret < 0)) { return ret; } + if (NPY_UNLIKELY(ret < 0)) { + return ret; + } stack[top - 2].l += C; --top; - } else { + } + else { break; } } @@ -2484,7 +2657,6 @@ npy_atry_collapse(char *arr, npy_intp *tosort, run *stack, return 0; } - static int npy_aforce_collapse(char *arr, npy_intp *tosort, run *stack, npy_intp *stack_ptr, buffer_intp *buffer, size_t len, @@ -2495,17 +2667,24 @@ npy_aforce_collapse(char *arr, npy_intp *tosort, run *stack, while (2 < top) { if (stack[top - 3].l <= stack[top - 1].l) { - ret = npy_amerge_at(arr, tosort, stack, top - 3, buffer, len, cmp, py_arr); + ret = npy_amerge_at(arr, tosort, stack, top - 3, buffer, len, cmp, + py_arr); - if (NPY_UNLIKELY(ret < 0)) { return ret; } + if (NPY_UNLIKELY(ret < 0)) { + return ret; + } stack[top - 3].l += stack[top - 2].l; stack[top - 2] = stack[top - 1]; --top; - } else { - ret = npy_amerge_at(arr, tosort, stack, top - 2, buffer, len, cmp, py_arr); + } + else { + ret = npy_amerge_at(arr, tosort, stack, top - 2, buffer, len, cmp, + py_arr); - if (NPY_UNLIKELY(ret < 0)) { return ret; } + if (NPY_UNLIKELY(ret < 0)) { + return ret; + } stack[top - 2].l += stack[top - 1].l; --top; @@ -2513,19 +2692,21 @@ npy_aforce_collapse(char *arr, npy_intp *tosort, run *stack, } if (1 < top) { - ret = npy_amerge_at(arr, tosort, stack, top - 2, buffer, len, cmp, py_arr); + ret = npy_amerge_at(arr, tosort, stack, top - 2, buffer, len, cmp, + py_arr); - if (NPY_UNLIKELY(ret < 0)) { return ret; } + if (NPY_UNLIKELY(ret < 0)) { + return ret; + } } return 0; } - NPY_NO_EXPORT int npy_atimsort(void *start, npy_intp *tosort, npy_intp num, void *varr) { - PyArrayObject *arr = varr; + PyArrayObject *arr = reinterpret_cast<PyArrayObject *>(varr); size_t len = PyArray_ITEMSIZE(arr); PyArray_CompareFunc *cmp = PyArray_DESCR(arr)->f->compare; int ret; @@ -2544,23 +2725,28 @@ npy_atimsort(void *start, npy_intp *tosort, npy_intp num, void *varr) minrun = compute_min_run_short(num); for (l = 0; l < num;) { - n = npy_acount_run(start, tosort, l, num, minrun, len, cmp, arr); + n = npy_acount_run((char *)start, tosort, l, num, minrun, len, cmp, + arr); /* both s and l are scaled by len */ stack[stack_ptr].s = l; stack[stack_ptr].l = n; ++stack_ptr; - ret = npy_atry_collapse(start, tosort, stack, &stack_ptr, &buffer, len, cmp, - arr); + ret = npy_atry_collapse((char *)start, tosort, stack, &stack_ptr, + &buffer, len, cmp, arr); - if (NPY_UNLIKELY(ret < 0)) { goto cleanup; } + if (NPY_UNLIKELY(ret < 0)) { + goto cleanup; + } l += n; } - ret = npy_aforce_collapse(start, tosort, stack, &stack_ptr, &buffer, len, - cmp, arr); + ret = npy_aforce_collapse((char *)start, tosort, stack, &stack_ptr, + &buffer, len, cmp, arr); - if (NPY_UNLIKELY(ret < 0)) { goto cleanup; } + if (NPY_UNLIKELY(ret < 0)) { + goto cleanup; + } ret = 0; @@ -2570,3 +2756,239 @@ cleanup: } return ret; } + +/*************************************** + * C > C++ dispatch + ***************************************/ + +NPY_NO_EXPORT int +timsort_bool(void *start, npy_intp num, void *NPY_UNUSED(varr)) +{ + return timsort_<npy::bool_tag>(start, num); +} +NPY_NO_EXPORT int +timsort_byte(void *start, npy_intp num, void *NPY_UNUSED(varr)) +{ + return timsort_<npy::byte_tag>(start, num); +} +NPY_NO_EXPORT int +timsort_ubyte(void *start, npy_intp num, void *NPY_UNUSED(varr)) +{ + return timsort_<npy::ubyte_tag>(start, num); +} +NPY_NO_EXPORT int +timsort_short(void *start, npy_intp num, void *NPY_UNUSED(varr)) +{ + return timsort_<npy::short_tag>(start, num); +} +NPY_NO_EXPORT int +timsort_ushort(void *start, npy_intp num, void *NPY_UNUSED(varr)) +{ + return timsort_<npy::ushort_tag>(start, num); +} +NPY_NO_EXPORT int +timsort_int(void *start, npy_intp num, void *NPY_UNUSED(varr)) +{ + return timsort_<npy::int_tag>(start, num); +} +NPY_NO_EXPORT int +timsort_uint(void *start, npy_intp num, void *NPY_UNUSED(varr)) +{ + return timsort_<npy::uint_tag>(start, num); +} +NPY_NO_EXPORT int +timsort_long(void *start, npy_intp num, void *NPY_UNUSED(varr)) +{ + return timsort_<npy::long_tag>(start, num); +} +NPY_NO_EXPORT int +timsort_ulong(void *start, npy_intp num, void *NPY_UNUSED(varr)) +{ + return timsort_<npy::ulong_tag>(start, num); +} +NPY_NO_EXPORT int +timsort_longlong(void *start, npy_intp num, void *NPY_UNUSED(varr)) +{ + return timsort_<npy::longlong_tag>(start, num); +} +NPY_NO_EXPORT int +timsort_ulonglong(void *start, npy_intp num, void *NPY_UNUSED(varr)) +{ + return timsort_<npy::ulonglong_tag>(start, num); +} +NPY_NO_EXPORT int +timsort_half(void *start, npy_intp num, void *NPY_UNUSED(varr)) +{ + return timsort_<npy::half_tag>(start, num); +} +NPY_NO_EXPORT int +timsort_float(void *start, npy_intp num, void *NPY_UNUSED(varr)) +{ + return timsort_<npy::float_tag>(start, num); +} +NPY_NO_EXPORT int +timsort_double(void *start, npy_intp num, void *NPY_UNUSED(varr)) +{ + return timsort_<npy::double_tag>(start, num); +} +NPY_NO_EXPORT int +timsort_longdouble(void *start, npy_intp num, void *NPY_UNUSED(varr)) +{ + return timsort_<npy::longdouble_tag>(start, num); +} +NPY_NO_EXPORT int +timsort_cfloat(void *start, npy_intp num, void *NPY_UNUSED(varr)) +{ + return timsort_<npy::cfloat_tag>(start, num); +} +NPY_NO_EXPORT int +timsort_cdouble(void *start, npy_intp num, void *NPY_UNUSED(varr)) +{ + return timsort_<npy::cdouble_tag>(start, num); +} +NPY_NO_EXPORT int +timsort_clongdouble(void *start, npy_intp num, void *NPY_UNUSED(varr)) +{ + return timsort_<npy::clongdouble_tag>(start, num); +} +NPY_NO_EXPORT int +timsort_datetime(void *start, npy_intp num, void *NPY_UNUSED(varr)) +{ + return timsort_<npy::datetime_tag>(start, num); +} +NPY_NO_EXPORT int +timsort_timedelta(void *start, npy_intp num, void *NPY_UNUSED(varr)) +{ + return timsort_<npy::timedelta_tag>(start, num); +} +NPY_NO_EXPORT int +timsort_string(void *start, npy_intp num, void *varr) +{ + return string_timsort_<npy::string_tag>(start, num, varr); +} +NPY_NO_EXPORT int +timsort_unicode(void *start, npy_intp num, void *varr) +{ + return string_timsort_<npy::unicode_tag>(start, num, varr); +} + +NPY_NO_EXPORT int +atimsort_bool(void *v, npy_intp *tosort, npy_intp num, void *NPY_UNUSED(varr)) +{ + return atimsort_<npy::bool_tag>(v, tosort, num); +} +NPY_NO_EXPORT int +atimsort_byte(void *v, npy_intp *tosort, npy_intp num, void *NPY_UNUSED(varr)) +{ + return atimsort_<npy::byte_tag>(v, tosort, num); +} +NPY_NO_EXPORT int +atimsort_ubyte(void *v, npy_intp *tosort, npy_intp num, void *NPY_UNUSED(varr)) +{ + return atimsort_<npy::ubyte_tag>(v, tosort, num); +} +NPY_NO_EXPORT int +atimsort_short(void *v, npy_intp *tosort, npy_intp num, void *NPY_UNUSED(varr)) +{ + return atimsort_<npy::short_tag>(v, tosort, num); +} +NPY_NO_EXPORT int +atimsort_ushort(void *v, npy_intp *tosort, npy_intp num, + void *NPY_UNUSED(varr)) +{ + return atimsort_<npy::ushort_tag>(v, tosort, num); +} +NPY_NO_EXPORT int +atimsort_int(void *v, npy_intp *tosort, npy_intp num, void *NPY_UNUSED(varr)) +{ + return atimsort_<npy::int_tag>(v, tosort, num); +} +NPY_NO_EXPORT int +atimsort_uint(void *v, npy_intp *tosort, npy_intp num, void *NPY_UNUSED(varr)) +{ + return atimsort_<npy::uint_tag>(v, tosort, num); +} +NPY_NO_EXPORT int +atimsort_long(void *v, npy_intp *tosort, npy_intp num, void *NPY_UNUSED(varr)) +{ + return atimsort_<npy::long_tag>(v, tosort, num); +} +NPY_NO_EXPORT int +atimsort_ulong(void *v, npy_intp *tosort, npy_intp num, void *NPY_UNUSED(varr)) +{ + return atimsort_<npy::ulong_tag>(v, tosort, num); +} +NPY_NO_EXPORT int +atimsort_longlong(void *v, npy_intp *tosort, npy_intp num, + void *NPY_UNUSED(varr)) +{ + return atimsort_<npy::longlong_tag>(v, tosort, num); +} +NPY_NO_EXPORT int +atimsort_ulonglong(void *v, npy_intp *tosort, npy_intp num, + void *NPY_UNUSED(varr)) +{ + return atimsort_<npy::ulonglong_tag>(v, tosort, num); +} +NPY_NO_EXPORT int +atimsort_half(void *v, npy_intp *tosort, npy_intp num, void *NPY_UNUSED(varr)) +{ + return atimsort_<npy::half_tag>(v, tosort, num); +} +NPY_NO_EXPORT int +atimsort_float(void *v, npy_intp *tosort, npy_intp num, void *NPY_UNUSED(varr)) +{ + return atimsort_<npy::float_tag>(v, tosort, num); +} +NPY_NO_EXPORT int +atimsort_double(void *v, npy_intp *tosort, npy_intp num, + void *NPY_UNUSED(varr)) +{ + return atimsort_<npy::double_tag>(v, tosort, num); +} +NPY_NO_EXPORT int +atimsort_longdouble(void *v, npy_intp *tosort, npy_intp num, + void *NPY_UNUSED(varr)) +{ + return atimsort_<npy::longdouble_tag>(v, tosort, num); +} +NPY_NO_EXPORT int +atimsort_cfloat(void *v, npy_intp *tosort, npy_intp num, + void *NPY_UNUSED(varr)) +{ + return atimsort_<npy::cfloat_tag>(v, tosort, num); +} +NPY_NO_EXPORT int +atimsort_cdouble(void *v, npy_intp *tosort, npy_intp num, + void *NPY_UNUSED(varr)) +{ + return atimsort_<npy::cdouble_tag>(v, tosort, num); +} +NPY_NO_EXPORT int +atimsort_clongdouble(void *v, npy_intp *tosort, npy_intp num, + void *NPY_UNUSED(varr)) +{ + return atimsort_<npy::clongdouble_tag>(v, tosort, num); +} +NPY_NO_EXPORT int +atimsort_datetime(void *v, npy_intp *tosort, npy_intp num, + void *NPY_UNUSED(varr)) +{ + return atimsort_<npy::datetime_tag>(v, tosort, num); +} +NPY_NO_EXPORT int +atimsort_timedelta(void *v, npy_intp *tosort, npy_intp num, + void *NPY_UNUSED(varr)) +{ + return atimsort_<npy::timedelta_tag>(v, tosort, num); +} +NPY_NO_EXPORT int +atimsort_string(void *v, npy_intp *tosort, npy_intp num, void *varr) +{ + return string_atimsort_<npy::string_tag>(v, tosort, num, varr); +} +NPY_NO_EXPORT int +atimsort_unicode(void *v, npy_intp *tosort, npy_intp num, void *varr) +{ + return string_atimsort_<npy::unicode_tag>(v, tosort, num, varr); +} diff --git a/numpy/core/src/umath/loops.c.src b/numpy/core/src/umath/loops.c.src index 5f054d0a9..7f084ac39 100644 --- a/numpy/core/src/umath/loops.c.src +++ b/numpy/core/src/umath/loops.c.src @@ -1506,62 +1506,6 @@ TIMEDELTA_mm_qm_divmod(char **args, npy_intp const *dimensions, npy_intp const * */ /**begin repeat - * #func = rint, floor, trunc# - * #scalarf = npy_rint, npy_floor, npy_trunc# - */ - -/**begin repeat1 -* #TYPE = FLOAT, DOUBLE# -* #type = npy_float, npy_double# -* #typesub = f, # -*/ - -NPY_NO_EXPORT NPY_GCC_OPT_3 void -@TYPE@_@func@(char **args, npy_intp const *dimensions, npy_intp const *steps, void *NPY_UNUSED(data)) -{ - UNARY_LOOP { - const @type@ in1 = *(@type@ *)ip1; - *(@type@ *)op1 = @scalarf@@typesub@(in1); - } -} - - -/**end repeat1**/ -/**end repeat**/ - -/**begin repeat - * #isa = avx512f, fma# - * #ISA = AVX512F, FMA# - * #CHK = HAVE_ATTRIBUTE_TARGET_AVX512F_WITH_INTRINSICS, HAVE_ATTRIBUTE_TARGET_AVX2_WITH_INTRINSICS# - */ - -/**begin repeat1 - * #TYPE = FLOAT, DOUBLE# - * #type = npy_float, npy_double# - * #typesub = f, # - */ - -/**begin repeat2 - * #func = rint, floor, trunc# - * #scalarf = npy_rint, npy_floor, npy_trunc# - */ - -NPY_NO_EXPORT NPY_GCC_OPT_3 void -@TYPE@_@func@_@isa@(char **args, npy_intp const *dimensions, npy_intp const *steps, void *NPY_UNUSED(data)) -{ - if (!run_unary_@isa@_@func@_@TYPE@(args, dimensions, steps)) { - UNARY_LOOP { - const @type@ in1 = *(@type@ *)ip1; - *(@type@ *)op1 = @scalarf@@typesub@(in1); - } - } -} - -/**end repeat2**/ -/**end repeat1**/ -/**end repeat**/ - -/**begin repeat * Float types * #type = npy_float, npy_double, npy_longdouble# * #TYPE = FLOAT, DOUBLE, LONGDOUBLE# diff --git a/numpy/core/src/umath/loops.h.src b/numpy/core/src/umath/loops.h.src index 3eafbdf66..e5235b464 100644 --- a/numpy/core/src/umath/loops.h.src +++ b/numpy/core/src/umath/loops.h.src @@ -186,7 +186,7 @@ NPY_NO_EXPORT void * #TYPE = FLOAT, DOUBLE# */ /**begin repeat1 - * #kind = ceil, sqrt, absolute, square, reciprocal# + * #kind = rint, floor, trunc, ceil, sqrt, absolute, square, reciprocal# */ NPY_CPU_DISPATCH_DECLARE(NPY_NO_EXPORT void @TYPE@_@kind@, (char **args, npy_intp const *dimensions, npy_intp const *steps, void *NPY_UNUSED(data))) @@ -274,26 +274,6 @@ NPY_CPU_DISPATCH_DECLARE(NPY_NO_EXPORT void @TYPE@_@kind@, ( /**end repeat**/ /**begin repeat - * #func = rint, floor, trunc# - */ - -/**begin repeat1 -* #TYPE = FLOAT, DOUBLE# -*/ - -NPY_NO_EXPORT NPY_GCC_OPT_3 void -@TYPE@_@func@(char **args, npy_intp const *dimensions, npy_intp const *steps, void *NPY_UNUSED(data)); - -/**begin repeat2 - * #isa = avx512f, fma# - */ -NPY_NO_EXPORT NPY_GCC_OPT_3 void -@TYPE@_@func@_@isa@(char **args, npy_intp const *dimensions, npy_intp const *steps, void *NPY_UNUSED(data)); -/**end repeat2**/ -/**end repeat1**/ -/**end repeat**/ - -/**begin repeat * Float types * #TYPE = HALF, FLOAT, DOUBLE, LONGDOUBLE# * #c = f, f, , l# diff --git a/numpy/core/src/umath/loops_unary_fp.dispatch.c.src b/numpy/core/src/umath/loops_unary_fp.dispatch.c.src index 93761b98c..78e231965 100644 --- a/numpy/core/src/umath/loops_unary_fp.dispatch.c.src +++ b/numpy/core/src/umath/loops_unary_fp.dispatch.c.src @@ -70,6 +70,15 @@ NPY_FINLINE double c_square_f64(double a) #define c_ceil_f32 npy_ceilf #define c_ceil_f64 npy_ceil +#define c_trunc_f32 npy_truncf +#define c_trunc_f64 npy_trunc + +#define c_floor_f32 npy_floorf +#define c_floor_f64 npy_floor + +#define c_rint_f32 npy_rintf +#define c_rint_f64 npy_rint + /******************************************************************************** ** Defining the SIMD kernels ********************************************************************************/ @@ -119,6 +128,9 @@ NPY_FINLINE double c_square_f64(double a) #if __clang_major__ < 10 // Clang before v10 #define WORKAROUND_CLANG_RECIPROCAL_BUG 1 + #elif defined(_MSC_VER) + // clang-cl has the same bug + #define WORKAROUND_CLANG_RECIPROCAL_BUG 1 #elif defined(NPY_CPU_X86) || defined(NPY_CPU_AMD64) // Clang v10+, targeting i386 or x86_64 #define WORKAROUND_CLANG_RECIPROCAL_BUG 0 @@ -139,10 +151,10 @@ NPY_FINLINE double c_square_f64(double a) */ #if @VCHK@ /**begin repeat1 - * #kind = ceil, sqrt, absolute, square, reciprocal# - * #intr = ceil, sqrt, abs, square, recip# - * #repl_0w1 = 0, 0, 0, 0, 1# - * #RECIP_WORKAROUND = 0, 0, 0, 0, WORKAROUND_CLANG_RECIPROCAL_BUG# + * #kind = rint, floor, ceil, trunc, sqrt, absolute, square, reciprocal# + * #intr = rint, floor, ceil, trunc, sqrt, abs, square, recip# + * #repl_0w1 = 0*7, 1# + * #RECIP_WORKAROUND = 0*7, WORKAROUND_CLANG_RECIPROCAL_BUG# */ /**begin repeat2 * #STYPE = CONTIG, NCONTIG, CONTIG, NCONTIG# @@ -250,9 +262,9 @@ static void simd_@TYPE@_@kind@_@STYPE@_@DTYPE@ * #VCHK = NPY_SIMD, NPY_SIMD_F64# */ /**begin repeat1 - * #kind = ceil, sqrt, absolute, square, reciprocal# - * #intr = ceil, sqrt, abs, square, recip# - * #clear = 0, 0, 1, 0, 0# + * #kind = rint, floor, ceil, trunc, sqrt, absolute, square, reciprocal# + * #intr = rint, floor, ceil, trunc, sqrt, abs, square, recip# + * #clear = 0, 0, 0, 0, 0, 1, 0, 0# */ NPY_NO_EXPORT void NPY_CPU_DISPATCH_CURFX(@TYPE@_@kind@) (char **args, npy_intp const *dimensions, npy_intp const *steps, void *NPY_UNUSED(func)) diff --git a/numpy/core/src/umath/simd.inc.src b/numpy/core/src/umath/simd.inc.src index 8b833ee56..b477027b3 100644 --- a/numpy/core/src/umath/simd.inc.src +++ b/numpy/core/src/umath/simd.inc.src @@ -123,47 +123,6 @@ run_@func@_avx512_skx_@TYPE@(char **args, npy_intp const *dimensions, npy_intp c /**end repeat**/ /**begin repeat - * #ISA = FMA, AVX512F# - * #isa = fma, avx512f# - * #CHK = HAVE_ATTRIBUTE_TARGET_AVX2_WITH_INTRINSICS, HAVE_ATTRIBUTE_TARGET_AVX512F_WITH_INTRINSICS# - * #REGISTER_SIZE = 32, 64# - */ - -/* prototypes */ - -/**begin repeat1 - * #type = npy_float, npy_double# - * #TYPE = FLOAT, DOUBLE# - */ - -/**begin repeat2 - * #func = rint, floor, trunc# - */ - -#if defined @CHK@ && defined NPY_HAVE_SSE2_INTRINSICS -static NPY_INLINE NPY_GCC_TARGET_@ISA@ void -@ISA@_@func@_@TYPE@(@type@ *, @type@ *, const npy_intp n, const npy_intp stride); -#endif - -static NPY_INLINE int -run_unary_@isa@_@func@_@TYPE@(char **args, npy_intp const *dimensions, npy_intp const *steps) -{ -#if defined @CHK@ && defined NPY_HAVE_SSE2_INTRINSICS - if (IS_OUTPUT_BLOCKABLE_UNARY(sizeof(@type@), sizeof(@type@), @REGISTER_SIZE@)) { - @ISA@_@func@_@TYPE@((@type@*)args[1], (@type@*)args[0], dimensions[0], steps[0]); - return 1; - } - else - return 0; -#endif - return 0; -} - -/**end repeat2**/ -/**end repeat1**/ -/**end repeat**/ - -/**begin repeat * Float types * #type = npy_float, npy_double, npy_longdouble# * #TYPE = FLOAT, DOUBLE, LONGDOUBLE# @@ -1119,144 +1078,6 @@ AVX512_SKX_@func@_@TYPE@(npy_bool* op, @type@* ip, const npy_intp array_size, co /**end repeat**/ /**begin repeat - * #ISA = FMA, AVX512F# - * #isa = fma, avx512# - * #vsize = 256, 512# - * #BYTES = 32, 64# - * #cvtps_epi32 = _mm256_cvtps_epi32, # - * #mask = __m256, __mmask16# - * #vsub = , _mask# - * #vtype = __m256, __m512# - * #cvtps_epi32 = _mm256_cvtps_epi32, # - * #masked_store = _mm256_maskstore_ps, _mm512_mask_storeu_ps# - * #CHK = HAVE_ATTRIBUTE_TARGET_AVX2_WITH_INTRINSICS, HAVE_ATTRIBUTE_TARGET_AVX512F_WITH_INTRINSICS# - */ - -/**begin repeat1 - * #func = rint, floor, trunc# - * #vectorf = rint, floor, trunc# - */ - -#if defined @CHK@ -static NPY_INLINE NPY_GCC_OPT_3 NPY_GCC_TARGET_@ISA@ void -@ISA@_@func@_FLOAT(npy_float* op, - npy_float* ip, - const npy_intp array_size, - const npy_intp steps) -{ - const npy_intp stride = steps/(npy_intp)sizeof(npy_float); - const npy_int num_lanes = @BYTES@/(npy_intp)sizeof(npy_float); - npy_intp num_remaining_elements = array_size; - @vtype@ ones_f = _mm@vsize@_set1_ps(1.0f); - @mask@ load_mask = @isa@_get_full_load_mask_ps(); - /* - * Note: while generally indices are npy_intp, we ensure that our maximum index - * will fit in an int32 as a precondition for this function via - * IS_OUTPUT_BLOCKABLE_UNARY - */ - - npy_int32 indexarr[16]; - for (npy_int32 ii = 0; ii < 16; ii++) { - indexarr[ii] = ii*stride; - } - @vtype@i vindex = _mm@vsize@_loadu_si@vsize@((@vtype@i*)&indexarr[0]); - - while (num_remaining_elements > 0) { - if (num_remaining_elements < num_lanes) { - load_mask = @isa@_get_partial_load_mask_ps(num_remaining_elements, - num_lanes); - } - @vtype@ x; - if (stride == 1) { - x = @isa@_masked_load_ps(load_mask, ip); - } - else { - x = @isa@_masked_gather_ps(ones_f, ip, vindex, load_mask); - } - @vtype@ out = @isa@_@vectorf@_ps(x); - @masked_store@(op, @cvtps_epi32@(load_mask), out); - - ip += num_lanes*stride; - op += num_lanes; - num_remaining_elements -= num_lanes; - } -} -#endif -/**end repeat1**/ -/**end repeat**/ - -/**begin repeat - * #ISA = FMA, AVX512F# - * #isa = fma, avx512# - * #vsize = 256, 512# - * #BYTES = 32, 64# - * #cvtps_epi32 = _mm256_cvtps_epi32, # - * #mask = __m256i, __mmask8# - * #vsub = , _mask# - * #vtype = __m256d, __m512d# - * #vindextype = __m128i, __m256i# - * #vindexsize = 128, 256# - * #vindexload = _mm_loadu_si128, _mm256_loadu_si256# - * #cvtps_epi32 = _mm256_cvtpd_epi32, # - * #castmask = _mm256_castsi256_pd, # - * #masked_store = _mm256_maskstore_pd, _mm512_mask_storeu_pd# - * #CHK = HAVE_ATTRIBUTE_TARGET_AVX2_WITH_INTRINSICS, HAVE_ATTRIBUTE_TARGET_AVX512F_WITH_INTRINSICS# - */ - -/**begin repeat1 - * #func = rint, floor, trunc# - * #vectorf = rint, floor, trunc# - */ - -#if defined @CHK@ -static NPY_INLINE NPY_GCC_OPT_3 NPY_GCC_TARGET_@ISA@ void -@ISA@_@func@_DOUBLE(npy_double* op, - npy_double* ip, - const npy_intp array_size, - const npy_intp steps) -{ - const npy_intp stride = steps/(npy_intp)sizeof(npy_double); - const npy_int num_lanes = @BYTES@/(npy_intp)sizeof(npy_double); - npy_intp num_remaining_elements = array_size; - @mask@ load_mask = @isa@_get_full_load_mask_pd(); - @vtype@ ones_d = _mm@vsize@_set1_pd(1.0f); - - /* - * Note: while generally indices are npy_intp, we ensure that our maximum index - * will fit in an int32 as a precondition for this function via - * IS_OUTPUT_BLOCKABLE_UNARY - */ - npy_int32 indexarr[8]; - for (npy_int32 ii = 0; ii < 8; ii++) { - indexarr[ii] = ii*stride; - } - @vindextype@ vindex = @vindexload@((@vindextype@*)&indexarr[0]); - - while (num_remaining_elements > 0) { - if (num_remaining_elements < num_lanes) { - load_mask = @isa@_get_partial_load_mask_pd(num_remaining_elements, - num_lanes); - } - @vtype@ x; - if (stride == 1) { - x = @isa@_masked_load_pd(load_mask, ip); - } - else { - x = @isa@_masked_gather_pd(ones_d, ip, vindex, @castmask@(load_mask)); - } - @vtype@ out = @isa@_@vectorf@_pd(x); - @masked_store@(op, load_mask, out); - - ip += num_lanes*stride; - op += num_lanes; - num_remaining_elements -= num_lanes; - } -} -#endif -/**end repeat1**/ -/**end repeat**/ - -/**begin repeat * #TYPE = CFLOAT, CDOUBLE# * #type = npy_float, npy_double# * #num_lanes = 16, 8# @@ -1535,3 +1356,4 @@ sse2_@kind@_BOOL(@type@ * op, @type@ * ip, const npy_intp n) #undef VECTOR_SIZE_BYTES #endif /* NPY_HAVE_SSE2_INTRINSICS */ #endif + diff --git a/numpy/core/tests/test_cpu_features.py b/numpy/core/tests/test_cpu_features.py index 706cf7a7e..1a76897e2 100644 --- a/numpy/core/tests/test_cpu_features.py +++ b/numpy/core/tests/test_cpu_features.py @@ -140,8 +140,8 @@ class Test_X86_Features(AbstractTest): is_power = re.match("^(powerpc|ppc)64", machine, re.IGNORECASE) @pytest.mark.skipif(not is_linux or not is_power, reason="Only for Linux and Power") class Test_POWER_Features(AbstractTest): - features = ["VSX", "VSX2", "VSX3"] - features_map = dict(VSX2="ARCH_2_07", VSX3="ARCH_3_00") + features = ["VSX", "VSX2", "VSX3", "VSX4"] + features_map = dict(VSX2="ARCH_2_07", VSX3="ARCH_3_00", VSX4="ARCH_3_1") def load_flags(self): self.load_flags_auxv() diff --git a/numpy/core/tests/test_simd.py b/numpy/core/tests/test_simd.py index 12a67c44d..605baefe6 100644 --- a/numpy/core/tests/test_simd.py +++ b/numpy/core/tests/test_simd.py @@ -330,16 +330,18 @@ class _SIMD_FP(_Test_Utility): square = self.square(vdata) assert square == data_square - @pytest.mark.parametrize("intrin, func", [("self.ceil", math.ceil), - ("self.trunc", math.trunc)]) + @pytest.mark.parametrize("intrin, func", [("ceil", math.ceil), + ("trunc", math.trunc), ("floor", math.floor), ("rint", round)]) def test_rounding(self, intrin, func): """ Test intrinsics: + npyv_rint_##SFX npyv_ceil_##SFX npyv_trunc_##SFX + npyv_floor##SFX """ intrin_name = intrin - intrin = eval(intrin) + intrin = getattr(self, intrin) pinf, ninf, nan = self._pinfinity(), self._ninfinity(), self._nan() # special cases round_cases = ((nan, nan), (pinf, pinf), (ninf, ninf)) @@ -347,20 +349,25 @@ class _SIMD_FP(_Test_Utility): data_round = [desired]*self.nlanes _round = intrin(self.setall(case)) assert _round == pytest.approx(data_round, nan_ok=True) + for x in range(0, 2**20, 256**2): for w in (-1.05, -1.10, -1.15, 1.05, 1.10, 1.15): - data = [x*w+a for a in range(self.nlanes)] - vdata = self.load(data) + data = self.load([(x+a)*w for a in range(self.nlanes)]) data_round = [func(x) for x in data] - _round = intrin(vdata) + _round = intrin(data) assert _round == data_round + # signed zero - if "ceil" in intrin_name or "trunc" in intrin_name: - for w in (-0.25, -0.30, -0.45): - _round = self._to_unsigned(intrin(self.setall(w))) - data_round = self._to_unsigned(self.setall(-0.0)) - assert _round == data_round - + if intrin_name == "floor": + data_szero = (-0.0,) + else: + data_szero = (-0.0, -0.25, -0.30, -0.45, -0.5) + + for w in data_szero: + _round = self._to_unsigned(intrin(self.setall(w))) + data_round = self._to_unsigned(self.setall(-0.0)) + assert _round == data_round + def test_max(self): """ Test intrinsics: diff --git a/numpy/distutils/ccompiler_opt.py b/numpy/distutils/ccompiler_opt.py index f1d024b94..854584998 100644 --- a/numpy/distutils/ccompiler_opt.py +++ b/numpy/distutils/ccompiler_opt.py @@ -294,6 +294,9 @@ class _Config: VSX2 = dict(interest=2, implies="VSX", implies_detect=False), ## Power9/ISA 3.00 VSX3 = dict(interest=3, implies="VSX2", implies_detect=False), + ## Power10/ISA 3.1 + VSX4 = dict(interest=4, implies="VSX3", implies_detect=False, + extra_checks="VSX4_MMA"), # IBM/Z ## VX(z13) support VX = dict(interest=1, headers="vecintrin.h"), @@ -471,12 +474,16 @@ class _Config: ), VSX3 = dict( flags="-mcpu=power9 -mtune=power9", implies_detect=False + ), + VSX4 = dict( + flags="-mcpu=power10 -mtune=power10", implies_detect=False ) ) if self.cc_is_clang: partial["VSX"]["flags"] = "-maltivec -mvsx" partial["VSX2"]["flags"] = "-mpower8-vector" partial["VSX3"]["flags"] = "-mpower9-vector" + partial["VSX4"]["flags"] = "-mpower10-vector" return partial diff --git a/numpy/distutils/checks/cpu_vsx4.c b/numpy/distutils/checks/cpu_vsx4.c new file mode 100644 index 000000000..a6acc7384 --- /dev/null +++ b/numpy/distutils/checks/cpu_vsx4.c @@ -0,0 +1,14 @@ +#ifndef __VSX__ + #error "VSX is not supported" +#endif +#include <altivec.h> + +typedef __vector unsigned int v_uint32x4; + +int main(void) +{ + v_uint32x4 v1 = (v_uint32x4){2, 4, 8, 16}; + v_uint32x4 v2 = (v_uint32x4){2, 2, 2, 2}; + v_uint32x4 v3 = vec_mod(v1, v2); + return (int)vec_extractm(v3); +} diff --git a/numpy/distutils/checks/extra_vsx4_mma.c b/numpy/distutils/checks/extra_vsx4_mma.c new file mode 100644 index 000000000..a70b2a9f6 --- /dev/null +++ b/numpy/distutils/checks/extra_vsx4_mma.c @@ -0,0 +1,21 @@ +#ifndef __VSX__ + #error "VSX is not supported" +#endif +#include <altivec.h> + +typedef __vector float fv4sf_t; +typedef __vector unsigned char vec_t; + +int main(void) +{ + __vector_quad acc0; + float a[4] = {0,1,2,3}; + float b[4] = {0,1,2,3}; + vec_t *va = (vec_t *) a; + vec_t *vb = (vec_t *) b; + __builtin_mma_xvf32ger(&acc0, va[0], vb[0]); + fv4sf_t result[4]; + __builtin_mma_disassemble_acc((void *)result, &acc0); + fv4sf_t c0 = result[0]; + return (int)((float*)&c0)[0]; +} diff --git a/numpy/distutils/command/build.py b/numpy/distutils/command/build.py index dc1ab3b9b..80830d559 100644 --- a/numpy/distutils/command/build.py +++ b/numpy/distutils/command/build.py @@ -47,8 +47,8 @@ class build(old_build): - not part of dispatch-able features(--cpu-dispatch) - not supported by compiler or platform """ - self.simd_test = "BASELINE SSE2 SSE42 XOP FMA4 (FMA3 AVX2) AVX512F" \ - " AVX512_SKX VSX VSX2 VSX3 NEON ASIMD VX VXE VXE2" + self.simd_test = "BASELINE SSE2 SSE42 XOP FMA4 (FMA3 AVX2) AVX512F " \ + "AVX512_SKX VSX VSX2 VSX3 VSX4 NEON ASIMD VX VXE VXE2" def finalize_options(self): build_scripts = self.build_scripts diff --git a/numpy/distutils/command/build_ext.py b/numpy/distutils/command/build_ext.py index 7040a2411..36a62eb1a 100644 --- a/numpy/distutils/command/build_ext.py +++ b/numpy/distutils/command/build_ext.py @@ -231,20 +231,36 @@ class build_ext (old_build_ext): l = ext.language or self.compiler.detect_language(ext.sources) if l: ext_languages.add(l) + # reset language attribute for choosing proper linker + # + # When we build extensions with multiple languages, we have to + # choose a linker. The rules here are: + # 1. if there is Fortran code, always prefer the Fortran linker, + # 2. otherwise prefer C++ over C, + # 3. Users can force a particular linker by using + # `language='c'` # or 'c++', 'f90', 'f77' + # in their config.add_extension() calls. if 'c++' in ext_languages: ext_language = 'c++' - elif 'f90' in ext_languages: + else: + ext_language = 'c' # default + + has_fortran = False + if 'f90' in ext_languages: ext_language = 'f90' + has_fortran = True elif 'f77' in ext_languages: ext_language = 'f77' - else: - ext_language = 'c' # default - if l and l != ext_language and ext.language: - log.warn('resetting extension %r language from %r to %r.' % - (ext.name, l, ext_language)) - if not ext.language: + has_fortran = True + + if not ext.language or has_fortran: + if l and l != ext_language and ext.language: + log.warn('resetting extension %r language from %r to %r.' % + (ext.name, l, ext_language)) + ext.language = ext_language + # global language all_languages.update(ext_languages) diff --git a/numpy/distutils/tests/test_ccompiler_opt.py b/numpy/distutils/tests/test_ccompiler_opt.py index 6f9970c75..1ca8bc09b 100644 --- a/numpy/distutils/tests/test_ccompiler_opt.py +++ b/numpy/distutils/tests/test_ccompiler_opt.py @@ -405,7 +405,7 @@ class _Test_CCompilerOpt: # in msvc, avx512_knl avx512_knm aren't supported x86_msvc=".* xop fma4 .* avx512f .* avx512_skx .*", armhf=".* asimd asimdhp asimddp .*", - ppc64="vsx vsx2 vsx3.*", + ppc64="vsx vsx2 vsx3 vsx4.*", s390x="vx vxe vxe2.*" ) # min @@ -544,13 +544,13 @@ class _Test_CCompilerOpt: """ /*@targets sse sse2 sse41 avx avx2 avx512f - vsx vsx2 vsx3 + vsx vsx2 vsx3 vsx4 neon neon_fp16 asimdhp asimddp vx vxe vxe2 */ """, baseline="avx vsx2 asimd vx vxe", - x86="avx512f avx2", armhf="asimddp asimdhp", ppc64="vsx3", + x86="avx512f avx2", armhf="asimddp asimdhp", ppc64="vsx4 vsx3", s390x="vxe2" ) # test skipping non-dispatch features @@ -558,7 +558,7 @@ class _Test_CCompilerOpt: """ /*@targets sse41 avx avx2 avx512f - vsx2 vsx3 + vsx2 vsx3 vsx4 asimd asimdhp asimddp vx vxe vxe2 */ @@ -571,13 +571,13 @@ class _Test_CCompilerOpt: """ /*@targets sse2 sse41 avx2 avx512f - vsx2 vsx3 + vsx2 vsx3 vsx4 neon asimdhp asimddp vx vxe vxe2 */ """, baseline="", - trap_files=".*(avx2|avx512f|vsx3|asimddp|vxe2).c", + trap_files=".*(avx2|avx512f|vsx3|vsx4|asimddp|vxe2).c", x86="sse41 sse2", ppc64="vsx2", armhf="asimdhp neon", s390x="vxe vx" ) diff --git a/numpy/f2py/capi_maps.py b/numpy/f2py/capi_maps.py index 581f946e5..b4fab71f9 100644 --- a/numpy/f2py/capi_maps.py +++ b/numpy/f2py/capi_maps.py @@ -504,7 +504,8 @@ def sign2map(a, var): varname,ctype,atype init,init.r,init.i,pytype vardebuginfo,vardebugshowvalue,varshowvalue - varrfromat + varrformat + intent """ out_a = a diff --git a/numpy/f2py/cfuncs.py b/numpy/f2py/cfuncs.py index 528c4adee..bdd27adaf 100644 --- a/numpy/f2py/cfuncs.py +++ b/numpy/f2py/cfuncs.py @@ -572,18 +572,20 @@ cppmacros['OLDPYNUM'] = """\ """ cppmacros["F2PY_THREAD_LOCAL_DECL"] = """\ #ifndef F2PY_THREAD_LOCAL_DECL -#if defined(_MSC_VER) \\ - || defined(_WIN32) || defined(_WIN64) \\ - || defined(__MINGW32__) || defined(__MINGW64__) +#if defined(_MSC_VER) #define F2PY_THREAD_LOCAL_DECL __declspec(thread) +#elif defined(__MINGW32__) || defined(__MINGW64__) +#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(__GLIBC__) || __GLIBC__ > 2 || (__GLIBC__ == 2 && __GLIBC_MINOR__ > 12)) \\ + && !defined(__OpenBSD__) /* __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 */ + 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__) \\ diff --git a/numpy/f2py/crackfortran.py b/numpy/f2py/crackfortran.py index aacd2c676..0374ae8d7 100755 --- a/numpy/f2py/crackfortran.py +++ b/numpy/f2py/crackfortran.py @@ -892,6 +892,9 @@ def appenddecl(decl, decl2, force=1): 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( @@ -914,6 +917,17 @@ def _is_intent_callback(vdecl): 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 _resolvenameargspattern(line): line = markouterparen(line) m1 = nameargspattern.match(line) @@ -962,7 +976,13 @@ def analyzeline(m, case, line): block = 'python module' elif re.match(r'abstract\s*interface', block, re.I): block = 'abstract interface' - name, args, result, bind = _resolvenameargspattern(m.group('after')) + if block == 'type': + name, attrs, _ = _resolvetypedefpattern(m.group('after')) + groupcache[groupcounter]['vars'][name] = dict(attrspec = attrs) + args = [] + result = None + else: + name, args, result, _ = _resolvenameargspattern(m.group('after')) if name is None: if block == 'block data': name = '_BLOCK_DATA_' diff --git a/numpy/f2py/tests/src/crackfortran/accesstype.f90 b/numpy/f2py/tests/src/crackfortran/accesstype.f90 new file mode 100644 index 000000000..e2cbd445d --- /dev/null +++ b/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/numpy/f2py/tests/test_crackfortran.py b/numpy/f2py/tests/test_crackfortran.py index e33e12d62..ea618bf33 100644 --- a/numpy/f2py/tests/test_crackfortran.py +++ b/numpy/f2py/tests/test_crackfortran.py @@ -44,6 +44,15 @@ class TestPublicPrivate: 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'} + class TestModuleProcedure(): def test_moduleOperators(self, tmp_path): diff --git a/numpy/lib/function_base.py b/numpy/lib/function_base.py index d4abde425..d5b130b72 100644 --- a/numpy/lib/function_base.py +++ b/numpy/lib/function_base.py @@ -4907,7 +4907,7 @@ def meshgrid(*xi, copy=True, sparse=False, indexing='xy'): >>> x = np.linspace(-5, 5, 101) >>> y = np.linspace(-5, 5, 101) - >>> # full coorindate arrays + >>> # full coordinate arrays >>> xx, yy = np.meshgrid(x, y) >>> zz = np.sqrt(xx**2 + yy**2) >>> xx.shape, yy.shape, zz.shape diff --git a/numpy/lib/polynomial.py b/numpy/lib/polynomial.py index f824c4c5e..6aa708861 100644 --- a/numpy/lib/polynomial.py +++ b/numpy/lib/polynomial.py @@ -686,7 +686,7 @@ def polyfit(x, y, deg, rcond=None, full=False, w=None, cov=False): "to scale the covariance matrix") # note, this used to be: fac = resids / (len(x) - order - 2.0) # it was deciced that the "- 2" (originally justified by "Bayesian - # uncertainty analysis") is not was the user expects + # uncertainty analysis") is not what the user expects # (see gh-11196 and gh-11197) fac = resids / (len(x) - order) if y.ndim == 1: diff --git a/numpy/ma/core.py b/numpy/ma/core.py index e0e5403a9..9c9dfac68 100644 --- a/numpy/ma/core.py +++ b/numpy/ma/core.py @@ -3542,15 +3542,17 @@ class MaskedArray(ndarray): def harden_mask(self): """ - Force the mask to hard. + Force the mask to hard, preventing unmasking by assignment. Whether the mask of a masked array is hard or soft is determined by its `~ma.MaskedArray.hardmask` property. `harden_mask` sets - `~ma.MaskedArray.hardmask` to ``True``. + `~ma.MaskedArray.hardmask` to ``True`` (and returns the modified + self). See Also -------- ma.MaskedArray.hardmask + ma.MaskedArray.soften_mask """ self._hardmask = True @@ -3558,15 +3560,17 @@ class MaskedArray(ndarray): def soften_mask(self): """ - Force the mask to soft. + Force the mask to soft (default), allowing unmasking by assignment. Whether the mask of a masked array is hard or soft is determined by its `~ma.MaskedArray.hardmask` property. `soften_mask` sets - `~ma.MaskedArray.hardmask` to ``False``. + `~ma.MaskedArray.hardmask` to ``False`` (and returns the modified + self). See Also -------- ma.MaskedArray.hardmask + ma.MaskedArray.harden_mask """ self._hardmask = False @@ -3574,16 +3578,55 @@ class MaskedArray(ndarray): @property def hardmask(self): - """ Hardness of the mask """ + """ + Specifies whether values can be unmasked through assignments. + + By default, assigning definite values to masked array entries will + unmask them. When `hardmask` is ``True``, the mask will not change + through assignments. + + See Also + -------- + ma.MaskedArray.harden_mask + ma.MaskedArray.soften_mask + + Examples + -------- + >>> x = np.arange(10) + >>> m = np.ma.masked_array(x, x>5) + >>> assert not m.hardmask + + Since `m` has a soft mask, assigning an element value unmasks that + element: + + >>> m[8] = 42 + >>> m + masked_array(data=[0, 1, 2, 3, 4, 5, --, --, 42, --], + mask=[False, False, False, False, False, False, + True, True, False, True], + fill_value=999999) + + After hardening, the mask is not affected by assignments: + + >>> hardened = np.ma.harden_mask(m) + >>> assert m.hardmask and hardened is m + >>> m[:] = 23 + >>> m + masked_array(data=[23, 23, 23, 23, 23, 23, --, --, 23, --], + mask=[False, False, False, False, False, False, + True, True, False, True], + fill_value=999999) + + """ return self._hardmask def unshare_mask(self): """ - Copy the mask and set the sharedmask flag to False. + Copy the mask and set the `sharedmask` flag to ``False``. Whether the mask is shared between masked arrays can be seen from - the `sharedmask` property. `unshare_mask` ensures the mask is not shared. - A copy of the mask is only made if it was shared. + the `sharedmask` property. `unshare_mask` ensures the mask is not + shared. A copy of the mask is only made if it was shared. See Also -------- diff --git a/numpy/random/mtrand.pyx b/numpy/random/mtrand.pyx index 8bf74aa5d..38b5484bc 100644 --- a/numpy/random/mtrand.pyx +++ b/numpy/random/mtrand.pyx @@ -4341,7 +4341,7 @@ cdef class RandomState: The drawn samples, of shape ``(size, k)``. Raises - ------- + ------ ValueError If any value in ``alpha`` is less than or equal to zero diff --git a/numpy/setup.py b/numpy/setup.py index a0ca99919..ebad66122 100644 --- a/numpy/setup.py +++ b/numpy/setup.py @@ -23,6 +23,7 @@ def configuration(parent_package='',top_path=None): config.add_data_files('py.typed') config.add_data_files('*.pyi') config.add_subpackage('tests') + config.add_subpackage('_pyinstaller') config.make_config_py() # installs __config__.py return config diff --git a/numpy/testing/_private/extbuild.py b/numpy/testing/_private/extbuild.py index 940e2f7d7..b7a071e7f 100644 --- a/numpy/testing/_private/extbuild.py +++ b/numpy/testing/_private/extbuild.py @@ -8,8 +8,6 @@ import os import pathlib import sys import sysconfig -from numpy.distutils.ccompiler import new_compiler -from distutils.errors import CompileError __all__ = ['build_and_import_extension', 'compile_extension_module'] @@ -53,6 +51,7 @@ def build_and_import_extension( >>> assert not mod.test_bytes(u'abc') >>> assert mod.test_bytes(b'abc') """ + from distutils.errors import CompileError body = prologue + _make_methods(functions, modname) init = """PyObject *mod = PyModule_Create(&moduledef); @@ -221,6 +220,7 @@ def _c_compile(cfile, outputfilename, include_dirs=[], libraries=[], def build(cfile, outputfilename, compile_extra, link_extra, include_dirs, libraries, library_dirs): "cd into the directory where the cfile is, use distutils to build" + from numpy.distutils.ccompiler import new_compiler compiler = new_compiler(force=1, verbose=2) compiler.customize('') diff --git a/numpy/typing/tests/data/reveal/lib_function_base.pyi b/numpy/typing/tests/data/reveal/lib_function_base.pyi index c559eb295..eebe9fbfd 100644 --- a/numpy/typing/tests/data/reveal/lib_function_base.pyi +++ b/numpy/typing/tests/data/reveal/lib_function_base.pyi @@ -26,7 +26,7 @@ reveal_type(vectorized_func.signature) # E: Union[None, builtins.str] reveal_type(vectorized_func.otypes) # E: Union[None, builtins.str] reveal_type(vectorized_func.excluded) # E: set[Union[builtins.int, builtins.str]] reveal_type(vectorized_func.__doc__) # E: Union[None, builtins.str] -reveal_type(vectorized_func([1])) # E: ndarray[Any, dtype[Any]] +reveal_type(vectorized_func([1])) # E: Any reveal_type(np.vectorize(int)) # E: vectorize reveal_type(np.vectorize( # E: vectorize int, otypes="i", doc="doc", excluded=(), cache=True, signature=None @@ -291,7 +291,7 @@ def parse_setuppy_commands(): - `pip install .` (from a git repo or downloaded source release) - - `pip install numpy` (last NumPy release on PyPi) + - `pip install numpy` (last NumPy release on PyPI) """)) return True @@ -303,7 +303,7 @@ def parse_setuppy_commands(): To install NumPy from here with reliable uninstall, we recommend that you use `pip install .`. To install the latest NumPy release - from PyPi, use `pip install numpy`. + from PyPI, use `pip install numpy`. For help with build/installation issues, please ask on the numpy-discussion mailing list. If you are sure that you have run @@ -371,7 +371,7 @@ def get_docs_url(): if 'dev' in VERSION: return "https://numpy.org/devdocs" else: - # For releases, this URL ends up on pypi. + # For releases, this URL ends up on PyPI. # By pinning the version, users looking at old PyPI releases can get # to the associated docs easily. return "https://numpy.org/doc/{}.{}".format(MAJOR, MINOR) @@ -421,6 +421,7 @@ def setup_package(): entry_points={ 'console_scripts': f2py_cmds, 'array_api': ['numpy = numpy.array_api'], + 'pyinstaller40': ['hook-dirs = numpy:_pyinstaller_hooks_dir'], }, ) diff --git a/tools/gitpod/Dockerfile b/tools/gitpod/Dockerfile index e2e0e1bc9..592a5ee0a 100644 --- a/tools/gitpod/Dockerfile +++ b/tools/gitpod/Dockerfile @@ -27,7 +27,7 @@ # OS/ARCH: linux/amd64 FROM gitpod/workspace-base:latest -ARG MAMBAFORGE_VERSION="4.10.0-0" +ARG MAMBAFORGE_VERSION="4.11.0-0" ARG CONDA_ENV=numpy-dev diff --git a/tools/openblas_support.py b/tools/openblas_support.py index 4eb72dbc9..c89cb9284 100644 --- a/tools/openblas_support.py +++ b/tools/openblas_support.py @@ -13,8 +13,9 @@ from tempfile import mkstemp, gettempdir from urllib.request import urlopen, Request from urllib.error import HTTPError -OPENBLAS_V = '0.3.18' -OPENBLAS_LONG = 'v0.3.18' +# 0.3.19 fails AVX512_SKX tests, issue 20654, comments in PR 20660 +OPENBLAS_V = '0.3.19.dev' +OPENBLAS_LONG = 'v0.3.19-22-g5188aede' BASE_LOC = 'https://anaconda.org/multibuild-wheels-staging/openblas-libs' BASEURL = f'{BASE_LOC}/{OPENBLAS_LONG}/download' SUPPORTED_PLATFORMS = [ diff --git a/tools/travis-test.sh b/tools/travis-test.sh index b395942fb..db5b3f744 100755 --- a/tools/travis-test.sh +++ b/tools/travis-test.sh @@ -83,7 +83,7 @@ run_test() # in test_requirements.txt) does not provide a wheel, and the source tar # file does not install correctly when Python's optimization level is set # to strip docstrings (see https://github.com/eliben/pycparser/issues/291). - PYTHONOPTIMIZE="" $PIP install -r test_requirements.txt + PYTHONOPTIMIZE="" $PIP install -r test_requirements.txt pyinstaller DURATIONS_FLAG="--durations 10" if [ -n "$USE_DEBUG" ]; then |
