Repository: pydata/bottleneck (language: python)

Issue:
New release?
It looks like there was an attempt to release bottleneck 1.3.3 https://github.com/pydata/bottleneck/pull/347 but it didn't reach PyPI. Since #306 hasn't been released yet, users are finding all sorts of installation problems in Python 3.9, see #376. This will continue happening with the release of Python 3.10.

Even if the package is now unmaintained or dormand, if a new release is made with #306, at least some common installation issues will be addressed.

cc @qwhelan 
New release?
It looks like there was an attempt to release bottleneck 1.3.3 https://github.com/pydata/bottleneck/pull/347 but it didn't reach PyPI. Since #306 hasn't been released yet, users are finding all sorts of installation problems in Python 3.9, see #376. This will continue happening with the release of Python 3.10.

Even if the package is now unmaintained or dormand, if a new release is made with #306, at least some common installation issues will be addressed.

cc @qwhelan 


Discussion / hints:
Ehi @qwhelan, is there any chance we might enlarge the maintainer cohort for this project? At least for housekeeping related things?  There are some problems with installation with `pip` I'd like to see resolved. 
@shoyer Thousands of users would be delighted, if we could start using Python-3.10 for our projects depending on bottleneck. Is there still an active maintainer?
I'd love to hear from @qwhelan here, who last I heard was taking over for Keith Goodman. Seems like adding additional maintainers could be a good idea if there are any volunteers.
Pushing releases to conda forge and PyPI seems not too much of a burden for me. I just have to admit, that I do not know anything about this code-base and release pipeline. But it also looks like the CI needs some fix ups too.
I can also take care of the housekeeping of the package not related to the "math". I use the package at work, it would be directly useful for us. 
I'd be happy to help with housekeeping as well, particularly if we just need more people who can act as reviewers.
So it seems there is no lack for volunteers, so we should proceed and choose somebody, right?
I've sent invites to @rdbisme and @stroxler for the GitHub account - I'll need PyPI accounts as well to give upload rights.

Apologies for the lag in responding to this issue, I've had zero free bandwidth between work and personal matters as of late.
> I've sent invites to @rdbisme and @stroxler for the GitHub account - I'll need PyPI accounts as well to give upload rights.
> 
> Apologies for the lag in responding to this issue, I've had zero free bandwidth between work and personal matters as of late.

Thanks for the trust! 
@qwhelan Thanks!

Sorry for the delay - my PyPi account is user `stroxler` (same as my github account).
Ehi @qwhelan, is there any chance we might enlarge the maintainer cohort for this project? At least for housekeeping related things?  There are some problems with installation with `pip` I'd like to see resolved. 
@shoyer Thousands of users would be delighted, if we could start using Python-3.10 for our projects depending on bottleneck. Is there still an active maintainer?
I'd love to hear from @qwhelan here, who last I heard was taking over for Keith Goodman. Seems like adding additional maintainers could be a good idea if there are any volunteers.
Pushing releases to conda forge and PyPI seems not too much of a burden for me. I just have to admit, that I do not know anything about this code-base and release pipeline. But it also looks like the CI needs some fix ups too.
I can also take care of the housekeeping of the package not related to the "math". I use the package at work, it would be directly useful for us. 
I'd be happy to help with housekeeping as well, particularly if we just need more people who can act as reviewers.
So it seems there is no lack for volunteers, so we should proceed and choose somebody, right?
I've sent invites to @rdbisme and @stroxler for the GitHub account - I'll need PyPI accounts as well to give upload rights.

Apologies for the lag in responding to this issue, I've had zero free bandwidth between work and personal matters as of late.
> I've sent invites to @rdbisme and @stroxler for the GitHub account - I'll need PyPI accounts as well to give upload rights.
> 
> Apologies for the lag in responding to this issue, I've had zero free bandwidth between work and personal matters as of late.

Thanks for the trust! 
@qwhelan Thanks!

Sorry for the delay - my PyPi account is user `stroxler` (same as my github account).

Fix patch:
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
new file mode 100644
index 0000000000..8e796d4d11
--- /dev/null
+++ b/.github/workflows/ci.yml
@@ -0,0 +1,126 @@
+name: Github Actions
+
+on: 
+  push: 
+    branches:
+      - "master"
+  pull_request: 
+
+jobs:
+  test:
+    runs-on: ${{ matrix.os }}
+    strategy:
+      matrix:
+        python-version: ["3.6", "3.7", "3.8", "3.9", "3.10"]
+        os: [ubuntu-18.04, ubuntu-latest, macos-latest, windows-latest, windows-2022, macos-10.15]
+    steps:
+    - uses: actions/checkout@v2
+
+    - name: Set up Python ${{ matrix.python-version }}
+      uses: actions/setup-python@v2
+      with:
+        python-version: ${{ matrix.python-version }}
+  
+    - name: Install as develop
+      run: |
+        pip install -e .
+
+    - name: Test with pytest
+      run: |
+        pip install pytest
+        pytest
+
+  build_win_mac:
+    runs-on: ${{ matrix.os }}
+    if: startsWith(github.ref, 'refs/tags')
+    strategy:
+      matrix:
+        python-version: ["3.6", "3.7", "3.8", "3.9", "3.10"]
+        os: [macos-latest, windows-latest]
+    steps:
+      - uses: actions/checkout@v2
+        with:
+          fetch-depth: 0
+
+      - name: Set up Python ${{ matrix.python-version }}
+        uses: actions/setup-python@v2
+        with:
+          python-version: ${{ matrix.python-version }}
+
+      - name: Install build
+        run: |
+          pip install build
+
+      - name: Build wheel
+        run: |
+          python -m build -w
+
+      - name: Store wheel artifacts
+        uses: actions/upload-artifact@v2
+        with:
+          name: ${{ matrix.os }}-${{ matrix.python-version }}-dist
+          path: dist/
+
+  build_manylinux:
+    runs-on: ubuntu-latest
+    if: startsWith(github.ref, 'refs/tags')
+    steps:
+      - uses: actions/checkout@v2
+        with:
+          fetch-depth: 0
+
+      - uses: RalfG/python-wheels-manylinux-build@v0.4.2
+        with:
+          python-versions: "cp36-cp36m cp37-cp37m cp38-cp38 cp39-cp39 cp310-cp310"
+
+      - name: Store wheel artifacts
+        uses: actions/upload-artifact@v2
+        with:
+          name: linux-wheel
+          path: dist/
+
+  build_sdist:
+    runs-on: ubuntu-latest
+    if: startsWith(github.ref, 'refs/tags')
+    steps:
+      - uses: actions/checkout@v2
+        with:
+          fetch-depth: 0
+
+      - name: Install build
+        run: |
+          pip install build
+
+      - name: Build wheel
+        run: |
+          python -m build -s
+
+      - name: Store wheel artifacts
+        uses: actions/upload-artifact@v2
+        with:
+          name: linux-sdist
+          path: dist/
+
+  release:
+    needs: [build_manylinux, build_win_mac, build_sdist]
+    if: startsWith(github.ref, 'refs/tags')
+    runs-on: ubuntu-latest
+    steps:
+      - name: Download all workflow run artifacts
+        uses: actions/download-artifact@v2
+
+      - name: Aggregate artifacts
+        shell: bash
+        run: |
+          mkdir dist
+          find . '(' -type f -iname "*-manylinux*.whl" -or -iname "*-win*.whl" -or -iname "*-macosx*.whl" -or -iname "*.tar.gz" ')' -exec mv -t dist/ {} +
+
+      - name: Set up Python ${{ matrix.python-version }}
+        uses: actions/setup-python@v2
+        with:
+          python-version: "3.10"
+
+      - name: Publish distribution 📦 to PyPI
+        uses: pypa/gh-action-pypi-publish@master
+        with:
+          password: ${{ secrets.PYPI_API_TOKEN }}
diff --git a/.github/workflows/pythonpackage.yml b/.github/workflows/pythonpackage.yml
deleted file mode 100644
index 19fe55adf9..0000000000
--- a/.github/workflows/pythonpackage.yml
+++ /dev/null
@@ -1,75 +0,0 @@
-name: Github Actions
-
-on: [push, pull_request]
-
-jobs:
-  build:
-    runs-on: ${{ matrix.os }}
-    strategy:
-      max-parallel: 12
-      matrix:
-        python-version: [3.7, 3.8, 3.9]
-        os: [ubuntu-latest, macOS-latest, windows-latest]
-
-    steps:
-    - uses: actions/checkout@v1
-    - name: Set up Python ${{ matrix.python-version }}
-      uses: actions/setup-python@v1
-      with:
-        python-version: ${{ matrix.python-version }}
-    - uses: actions/cache@v1
-      if: startsWith(runner.os, 'Linux')
-      with:
-        path: ~/.cache/pip
-        key: ${{ runner.os }}-py${{ matrix.python-version }}-pip-${{ hashFiles('pyproject.toml') }}
-        restore-keys: |
-          ${{ runner.os }}-py${{ matrix.python-version }}-pip-
-
-    - uses: actions/cache@v1
-      if: startsWith(runner.os, 'macOS')
-      with:
-        path: ~/Library/Caches/pip
-        key: ${{ runner.os }}-py${{ matrix.python-version }}-pip-${{ hashFiles('pyproject.toml') }}
-        restore-keys: |
-          ${{ runner.os }}-py${{ matrix.python-version }}-pip-
-
-    - uses: actions/cache@v1
-      if: startsWith(runner.os, 'Windows')
-      with:
-        path: ~\AppData\Local\pip\Cache
-        key: ${{ runner.os }}-py${{ matrix.python-version }}-pip-${{ hashFiles('pyproject.toml') }}
-        restore-keys: |
-          ${{ runner.os }}-py${{ matrix.python-version }}-pip-
-
-    - name: Install style dependencies
-      if: matrix.python-version == 3.9
-      run: |
-        python -m pip install --upgrade pip
-        pip install flake8 isort>=5 black>=20.8b1
-    - name: Build
-      run: |
-        python setup.py build_ext --inplace
-        pip install .[test]
-    - name: Lint with flake8
-      if: matrix.python-version == 3.9
-      run: |
-        # stop the build if there are Python syntax errors or undefined names
-        flake8 . --count --select=E9,F63,F7,F82 --show-source --statistics
-        # exit-zero treats all errors as warnings. The GitHub editor is 127 chars wide
-        flake8 . --count --statistics
-    - name: Check import sorting with isort
-      if: matrix.python-version == 3.9
-      run: |
-        isort -c --df .
-    - name: Check formatting with black
-      if: matrix.python-version == 3.9
-      run: |
-        black . --check --exclude "(build/|dist/|\.git/|\.mypy_cache/|\.tox/|\.venv/\.asv/|env|\.eggs)"
-    - name: Check formatting with clang-format
-      if: (matrix.python-version == 3.9) && (startsWith(runner.os, 'Linux'))
-      run: |
-        clang-format -i --style=file bottleneck/**/*.c bottleneck/**/*.h
-        git diff --exit-code
-    - name: Test with pytest
-      run: |
-        pytest
diff --git a/.gitignore b/.gitignore
index 3b1640bb3a..81808d667d 100644
--- a/.gitignore
+++ b/.gitignore
@@ -9,9 +9,4 @@ MANIFEST
 .*.swp
 *~
 \#*#
-.eggs
-.hypothesis
-.mypy_cache
-**/env
-**/*results
-**/html
+bottleneck/src/bn_config.h
diff --git a/.travis.yml b/.travis.yml
deleted file mode 100644
index 2e1484df30..0000000000
--- a/.travis.yml
+++ /dev/null
@@ -1,39 +0,0 @@
-os: linux
-language: generic
-cache:
-    directories:
-      - $HOME/.cache/pip
-      - .hypothesis
-
-git:
-    depth: false
-
-jobs:
-    fast_finish: true
-    include:
-        - os: linux
-          arch: arm64
-          env: TEST_DEPS="numpy"
-               PYTHON_VERSION="3.7"
-               PYTHON_ARCH="64"
-
-        - os: linux
-          arch: ppc64le
-          env: TEST_DEPS="numpy gfortran_linux-ppc64le=8.2.0"
-               PYTHON_VERSION="3.7"
-               PYTHON_ARCH="64"
-
-before_install:
-    - uname -a
-    - source "tools/travis/conda_setup.sh"
-
-install:
-    - source "tools/travis/conda_install.sh"
-
-script:
-    - ./tools/travis/bn_setup.sh
-
-notifications:
-    email:
-        on_success: never
-        on_failure: always
diff --git a/MANIFEST.in b/MANIFEST.in
index 64d11127db..005115f187 100644
--- a/MANIFEST.in
+++ b/MANIFEST.in
@@ -16,8 +16,6 @@ recursive-include doc *
 recursive-exclude doc/build *
 include versioneer.py
 include bottleneck/_version.py
-include bottleneck/tests/data/template_test/truth.c
-include bottleneck/tests/data/template_test/test_template.c
 
 global-exclude __pycache__
 global-exclude *.pyc
diff --git a/Makefile b/Makefile
index c9ce80cfd2..6f7aa6bd3f 100644
--- a/Makefile
+++ b/Makefile
@@ -7,14 +7,11 @@ srcdir := bottleneck
 help:
 	@echo "Available tasks:"
 	@echo "help    -->  This help page"
-	@echo "all     -->  clean, flake8, isort, black, build, test"
+	@echo "all     -->  clean, build, flake8, test"
 	@echo "build   -->  Build the Python C extensions"
 	@echo "clean   -->  Remove all the build files for a fresh start"
 	@echo "test    -->  Run unit tests"
 	@echo "flake8  -->  Check for pep8 errors"
-	@echo "isort   -->  Run isort to format imports"
-	@echo "black   -->  Run black formatting tool"
-	@echo "clang-format   -->  Run clang-format tool"
 	@echo "readme  -->  Update benchmark results in README.rst"
 	@echo "bench   -->  Run performance benchmark"
 	@echo "detail  -->  Detailed benchmarks for all functions"
@@ -22,7 +19,7 @@ help:
 	@echo "doc     -->  Build Sphinx manual"
 	@echo "pypi    -->  Upload to pypi"
 
-all: clean flake8 isort black build test
+all: clean build test flake8
 
 build:
 	${PYTHON} setup.py build_ext --inplace
@@ -33,15 +30,9 @@ test:
 flake8:
 	flake8
 
-isort:
-	isort -rc .
-
 black:
 	black . --exclude "(build/|dist/|\.git/|\.mypy_cache/|\.tox/|\.venv/\.asv/|env|\.eggs)"
 
-clang-format:
-	clang-format --style=file -i bottleneck/**/*.c bottleneck/**/*.h
-
 readme:
 	PYTHONPATH=`pwd`:PYTHONPATH ${PYTHON} tools/update_readme.py
 
diff --git a/README.rst b/README.rst
index 6b8e67df71..7eb6f3989d 100644
--- a/README.rst
+++ b/README.rst
@@ -1,5 +1,3 @@
-.. image:: https://travis-ci.org/pydata/bottleneck.svg?branch=master
-    :target: https://travis-ci.org/pydata/bottleneck
 .. image:: https://github.com/pydata/bottleneck/workflows/Github%20Actions/badge.svg
     :target: https://github.com/pydata/bottleneck/actions
 
@@ -7,26 +5,7 @@
 Bottleneck
 ==========
 
-Bottleneck is a collection of fast, NaN-aware NumPy array functions
-written in C.
-
-As one example, to check if a ``np.array`` has any NaNs using numpy,
-one must call ``np.any(np.isnan(array))``. The :meth:`bottleneck.anynan`
-function interleaves the :meth:`np.isnan` check with :meth:`np.any`
-pre-exit, enabling up to an ``O(N)`` speedup relative to numpy.
-
-Bottleneck strives to be a drop-in accelerator for NumPy functions.
-When using the following libraries, Bottleneck support is automatically
-enabled and utilized:
-
- - `pandas <https://pandas.pydata.org/pandas-docs/stable/install.html#recommended-dependencies>`_
- - `xarray <http://xarray.pydata.org/en/stable/installing.html#instructions>`_
- - `astropy <https://docs.astropy.org/en/stable/install.html>`_
-
-Details on the performance benefits can be found in :ref:`benchmarking`
-
-Example
-=======
+Bottleneck is a collection of fast NumPy array functions written in C.
 
 Let's give it a try. Create a NumPy array::
 
@@ -44,8 +23,6 @@ Moving window mean::
     >>> bn.move_mean(a, window=2, min_count=1)
     array([ 1. ,  1.5,  2. ,  4. ,  4.5])
 
-.. _benchmarking:
-
 Benchmark
 =========
 
@@ -123,14 +100,12 @@ Install
 Requirements:
 
 ======================== ====================================================
-Bottleneck               Python 3.6, 3.7, 3.8; NumPy 1.15.0+ (follows NEP 29)
+Bottleneck               Python 2.7, 3.5, 3.6, 3.7, 3.8; NumPy 1.16.0+
 Compile                  gcc, clang, MinGW or MSVC
-Unit tests               pytest, hypothesis
+Unit tests               pytest
 Documentation            sphinx, numpydoc
 ======================== ====================================================
 
-Detailed installation instructions can be found at :ref:`installing`
-
 To install Bottleneck on Linux, Mac OS X, et al.::
 
     $ pip install .
@@ -146,15 +121,6 @@ http://www.lfd.uci.edu/~gohlke/pythonlibs/#bottleneck
 Unit tests
 ==========
 
-To keep the install dependencies light, test dependencies are made available
-via a setuptools "extra":
-
-    $ pip install bottleneck[test]
-
-Or, if working locally:
-
-    $ pip install .[test]
-
 After you have installed Bottleneck, run the suite of unit tests::
 
   In [1]: import bottleneck as bn
@@ -166,7 +132,7 @@ After you have installed Bottleneck, run the suite of unit tests::
   rootdir: /home/chris/code/bottleneck, inifile: setup.cfg
   plugins: openfiles-0.3.2, remotedata-0.3.2, doctestplus-0.3.0, mock-1.10.4, forked-1.0.2, cov-2.7.1, hypothesis-4.32.2, xdist-1.26.1, arraydiff-0.3
   collected 190 items
-
+  
   bottleneck/tests/input_modification_test.py ........................... [ 14%]
   ..                                                                      [ 15%]
   bottleneck/tests/list_input_test.py .............................       [ 30%]
@@ -176,7 +142,7 @@ After you have installed Bottleneck, run the suite of unit tests::
   bottleneck/tests/reduce_test.py ....................................... [ 84%]
   ............                                                            [ 90%]
   bottleneck/tests/scalar_input_test.py ..................                [100%]
-
+  
   ========================= 190 passed in 46.42 seconds =========================
   Out[2]: True
 
diff --git a/RELEASE.rst b/RELEASE.rst
new file mode 100644
index 0000000000..6f9d0687b4
--- /dev/null
+++ b/RELEASE.rst
@@ -0,0 +1,575 @@
+
+=============
+Release Notes
+=============
+
+These are the major changes made in each release. For details of the changes
+see the commit log at https://github.com/pydata/bottleneck
+
+Bottleneck 1.3.3
+================
+
+*Release date: 2022-02-21*
+
+Bug Fixes
+~~~~~~~~~
+- Fix Python 3.10 build
+
+Enhancements
+~~~~~~~~~~~~
+- Provide pre-compiled wheels for most x86_64 architectures
+
+Project Updates
+~~~~~~~~~~~~~~~
+- The project has two new maintainers: Ruben Di Battista (``@rdbisme`` on Github) and
+  Steven Troxler (``@stroxler`` on Github)
+
+Contributors
+~~~~~~~~~~~~
+
+.. contributors:: v1.3.2..v1.3.3
+
+
+Bottleneck 1.3.2
+================
+
+*Release date: 2020-02-20*
+
+Bug Fixes
+~~~~~~~~~
+- Explicitly declare numpy version dependency in ``pyproject.toml`` for Python 3.8, fixing
+  certain cases where ``pip install`` would fail. Thanks to ``@goggle``, ``@astrofrog``,
+  and ``@0xb0b`` for reporting. (:issue:`277`)
+
+Contributors
+~~~~~~~~~~~~
+
+.. contributors:: v1.3.1..v1.3.2
+
+Older Releases
+~~~~~~~~~~~~~~
+
+Bottleneck 1.3.1
+----------------
+
+*Release date: 2019-11-18*
+
+Bug Fixes
+~~~~~~~~~
+- Fix memory leak in :func:`bottleneck.nanmedian` with the default argument of ``axis=None``. Thanks to ``@jsmodic`` for reporting! (:issue:`276`, :issue:`278`)
+- Add regression test for memory leak case (:issue:`279`)
+
+Contributors
+~~~~~~~~~~~~
+
+.. contributors:: v1.3.0..v1.3.1
+
+
+Bottleneck 1.3.0
+----------------
+
+*Release date: 2019-11-12*
+
+Project Updates
+~~~~~~~~~~~~~~~
+- Bottleneck has a new maintainer, Christopher Whelan (``@qwhelan`` on GitHub).
+- Documentation now hosted at https://bottleneck.readthedocs.io
+- 1.3.x will be the last release to support Python 2.7
+- Bottleneck now supports and is tested against Python 3.7 and 3.8. (:issue:`211`, :issue:`268`)
+- The ``LICENSE`` file has been restructured to only include the license for the Bottleneck project to aid license audit tools. There has been no change to the licensing of Bottleneck.
+
+  - Licenses for other projects incorporated by Bottleneck are now reproduced in full in separate files in the ``LICENSES/`` directory (eg, ``LICENSES/NUMPY_LICENSE``)
+  - All licenses have been updated. Notably, setuptools is now MIT licensed and no longer under the ambiguous dual PSF/Zope license.
+- Bottleneck now uses :pep:`518` for specifying build dependencies, with per Python version specifications (:issue:`247`)
+
+
+Enhancements
+~~~~~~~~~~~~
+- Remove ``numpydoc`` package from Bottleneck source distribution
+- :func:`bottleneck.slow.reduce.nansum` and :func:`bottleneck.slow.reduce.ss` now longer coerce output to have the same dtype as input
+- Test (tox, travis, appveyor) against latest ``numpy`` (in conda)
+- Performance benchmarking also available via ``asv``
+- ``versioneer`` now used for versioning (:issue:`213`)
+- Test suite now uses ``pytest`` as ``nose`` is deprecated (:issue:`222`)
+- ``python setup.py build_ext --inplace`` is now incremental (:issue:`224`)
+- ``python setup.py clean`` now cleans all artifacts (:issue:`226`)
+- Compiler feature support now identified by testing rather than hardcoding (:issue:`227`)
+- The ``BN_OPT_3`` macro allows selective use of ``-O3`` at the function level (:issue:`223`)
+- Contributors are now automatically cited in the release notes (:issue:`244`)
+
+Performance
+~~~~~~~~~~~
+- Speed up :func:`bottleneck.reduce.anynan` and :func:`bottleneck.reduce.allnan` by 2x via ``BN_OPT_3`` (:issue:`223`)
+- All functions covered by ``asv`` benchmarks
+- :func:`bottleneck.nonreduce.replace` speedup of 4x via more explicit typing (:issue:`239`)
+- :func:`bottleneck.reduce.median` up to 2x faster for Fortran-ordered arrays (:issue:`248`)
+
+
+Bug Fixes
+~~~~~~~~~
+
+- Documentation fails to build on Python 3 (:issue:`170`)
+- :func:`bottleneck.benchmark.bench` crashes on python 3.6.3, numpy 1.13.3 (:issue:`175`)
+- :func:`bottleneck.nonreduce_axis.push` raises when :code:`n=None` is explicitly passed (:issue:`178`)
+- :func:`bottleneck.reduce.nansum` wrong output when :code:`a = np.ones((2, 2))[..., np.newaxis]`
+  same issue of other reduce functions (:issue:`183`)
+- Silenced FutureWarning from NumPy in the slow version of move functions (:issue:`194`)
+- Installing bottleneck onto a system that does not already have Numpy (:issue:`195`)
+- Memory leaked when input was not a NumPy array (:issue:`201`)
+- Tautological comparison in :func:`bottleneck.move.move_rank` removed (:issue:`207`, :issue:`212`)
+
+Cleanup
+~~~~~~~
+
+- The ``ez_setup.py`` module is no longer packaged (:issue:`211`)
+- Building documentation is now self-contained in ``make doc`` (:issue:`214`)
+- Codebase now ``flake8`` compliant and run on every commit
+- Codebase now uses ``black`` for autoformatting (:issue:`253`)
+
+Contributors
+~~~~~~~~~~~~
+
+.. contributors:: v1.2.1..v1.3.0
+
+
+Bottleneck 1.2.1
+----------------
+
+*Release date: 2017-05-15*
+
+This release adds support for NumPy's relaxed strides checking and
+fixes a few bugs.
+
+**Bug Fixes**
+
+- Installing bottleneck when two versions of NumPy are present (:issue:`156`)
+- Compiling on Ubuntu 14.04 inside a Windows 7 WMware (:issue:`157`)
+- Occasional segmentation fault in :func:`bn.nanargmin`, :func:`nanargmax`, :func:`median`,
+  and :func:`nanmedian` when all of the following conditions are met:
+  axis is None, input array is 2d or greater, and input array is not C
+  contiguous. (:issue:`159`)
+- Reducing np.array([2**31], dtype=np.int64) overflows on Windows (:issue:`163`)
+
+**Contributors**
+
+.. contributors:: v1.2.0..v1.2.1
+
+Bottleneck 1.2.0
+----------------
+
+*Release date: 2016-10-20*
+
+This release is a complete rewrite of Bottleneck.
+
+**Port to C**
+
+- Bottleneck is now written in C
+- Cython is no longer a dependency
+- Source tarball size reduced by 80%
+- Build time reduced by 66%
+- Install size reduced by 45%
+
+**Redesign**
+
+- Besides porting to C, much of bottleneck has been redesigned to be
+  simpler and faster. For example, bottleneck now uses its own N-dimensional
+  array iterators, reducing function call overhead.
+
+**New features**
+
+- The new function bench_detailed runs a detailed performance benchmark on
+  a single bottleneck function.
+- Bottleneck can be installed on systems that do not yet have NumPy
+  installed. Previously that only worked on some systems.
+
+**Beware**
+
+- Functions partsort and argpartsort have been renamed to partition and
+  argpartition to match NumPy. Additionally the meaning of the input
+  arguments have changed: :func:`bn.partsort(a, n)` is now equivalent to
+  :func:`bn.partition(a, kth=n-1)`. Similarly for bn.argpartition.
+- The keyword for array input has been changed from `arr` to `a` in all
+  functions. It now matches NumPy.
+
+**Thanks**
+
+- Moritz E. Beber: continuous integration with AppVeyor
+- Christoph Gohlke: Windows compatibility
+- Jennifer Olsen: comments and suggestions
+- A special thanks to the Cython developers. The quickest way to appreciate
+  their work is to remove Cython from your project. It is not easy.
+
+**Contributors**
+
+.. contributors:: v1.1.0..v1.2.0
+
+Bottleneck 1.1.0
+----------------
+
+*Release date: 2016-06-22*
+
+This release makes Bottleneck more robust, releases GIL, adds new functions.
+
+**More Robust**
+
+- :func:`bn.move_median` can now handle NaNs and `min_count` parameter
+- :func:`bn.move_std` is slower but numerically more stable
+- Bottleneck no longer crashes on byte-swapped input arrays
+
+**Faster**
+
+- All Bottleneck functions release the GIL
+- median is faster if the input array contains NaN
+- move_median is faster for input arrays that contain lots of NaNs
+- No speed penalty for median, nanmedian, nanargmin, nanargmax for Fortran
+  ordered input arrays when axis is None
+- Function call overhead cut in half for reduction along all axes (axis=None)
+  if the input array satisfies at least one of the following properties: 1d,
+  C contiguous, F contiguous
+- Reduction along all axes (axis=None) is more than twice as fast for long,
+  narrow input arrays such as a (1000000, 2) C contiguous array and a
+  (2, 1000000) F contiguous array
+
+**New Functions**
+
+- move_var
+- move_argmin
+- move_argmax
+- move_rank
+- push
+
+**Beware**
+
+- :func:`bn.median` now returns NaN for a slice that contains one or more NaNs
+- Instead of using the distutils default, the '-O2' C compiler flag is forced
+- :func:`bn.move_std` output changed when mean is large compared to standard deviation
+- Fixed: Non-accelerated moving window functions used min_count incorrectly
+- :func:`bn.move_median` is a bit slower for float input arrays that do not contain NaN
+
+**Thanks**
+
+Alphabeticaly by last name
+
+- Alessandro Amici worked on setup.py
+- Pietro Battiston modernized bottleneck installation
+- Moritz E. Beber set up continuous integration with Travis CI
+- Jaime Frio improved the numerical stability of move_std
+- Christoph Gohlke revived Windows compatibility
+- Jennifer Olsen added NaN support to move_median
+
+**Contributors**
+
+.. contributors:: v1.0.0..v1.1.0
+
+Bottleneck 1.0.0
+----------------
+
+*Release date: 2015-02-06*
+
+This release is a complete rewrite of Bottleneck.
+
+**Faster**
+
+- "python setup.py build" is 18.7 times faster
+- Function-call overhead cut in half---a big speed up for small input arrays
+- Arbitrary ndim input arrays accelerated; previously only 1d, 2d, and 3d
+- bn.nanrankdata is twice as fast for float input arrays
+- bn.move_max, bn.move_min are faster for int input arrays
+- No speed penalty for reducing along all axes when input is Fortran ordered
+
+**Smaller**
+
+- Compiled binaries 14.1 times smaller
+- Source tarball 4.7 times smaller
+- 9.8 times less C code
+- 4.3 times less Cython code
+- 3.7 times less Python code
+
+**Beware**
+
+- Requires numpy 1.9.1
+- Single API, e.g.: bn.nansum instead of bn.nansum and nansum_2d_float64_axis0
+- On 64-bit systems bn.nansum(int32) returns int32 instead of int64
+- bn.nansum now returns 0 for all NaN slices (as does numpy 1.9.1)
+- Reducing over all axes returns, e.g., 6.0; previously np.float64(6.0)
+- bn.ss() now has default axis=None instead of axis=0
+- bn.nn() is no longer in bottleneck
+
+**min_count**
+
+- Previous releases had moving window function pairs: move_sum, move_nansum
+- This release only has half of the pairs: move_sum
+- Instead a new input parameter, min_count, has been added
+- min_count=None same as old move_sum; min_count=1 same as old move_nansum
+- If # non-NaN values in window < min_count, then NaN assigned to the window
+- Exception: move_median does not take min_count as input
+
+**Bug Fixes**
+
+- Can now install bottleneck with pip even if numpy is not already installed
+- bn.move_max, bn.move_min now return float32 for float32 input
+
+**Contributors**
+
+.. contributors:: v0.8.0..v1.0.0
+
+Bottleneck 0.8.0
+----------------
+
+*Release date: 2014-01-21*
+
+This version of Bottleneck requires NumPy 1.8.
+
+**Breaks from 0.7.0**
+
+- This version of Bottleneck requires NumPy 1.8
+- nanargmin and nanargmax behave like the corresponding functions in NumPy 1.8
+
+**Bug fixes**
+
+- nanargmax/nanargmin wrong for redundant max/min values in 1d int arrays
+
+**Contributors**
+
+.. contributors:: v0.7.0..v0.8.0
+
+Bottleneck 0.7.0
+----------------
+
+*Release date: 2013-09-10*
+
+**Enhancements**
+
+- bn.rankdata() is twice as fast (with input a = np.random.rand(1000000))
+- C files now included in github repo; cython not needed to try latest
+- C files are now generated with Cython 0.19.1 instead of 0.16
+- Test bottleneck across multiple python/numpy versions using tox
+- Source tarball size cut in half
+
+**Bug fixes**
+
+- move_std, move_nanstd return inappropriate NaNs (sqrt of negative #) (:issue:`50`)
+- `make test` fails on some computers (:issue:`52`)
+- scipy optional yet some unit tests depend on scipy (:issue:`57`)
+- now works on Mac OS X 10.8 using clang compiler (:issue:`49`, :issue:`55`)
+- nanstd([1.0], ddof=1) and nanvar([1.0], ddof=1) crash (:issue:`60`)
+
+**Contributors**
+
+.. contributors:: v0.6.0..v0.7.0
+
+Bottleneck 0.6.0
+----------------
+
+*Release date: 2012-06-04*
+
+Thanks to Dougal Sutherland, Bottleneck now runs on Python 3.2.
+
+**New functions**
+
+- replace(arr, old, new), e.g, replace(arr, np.nan, 0)
+- nn(arr, arr0, axis) nearest neighbor and its index of 1d arr0 in 2d arr
+- anynan(arr, axis) faster alternative to np.isnan(arr).any(axis)
+- allnan(arr, axis) faster alternative to np.isnan(arr).all(axis)
+
+**Enhancements**
+
+- Python 3.2 support (may work on earlier versions of Python 3)
+- C files are now generated with Cython 0.16 instead of 0.14.1
+- Upgrade numpydoc from 0.3.1 to 0.4 to support Sphinx 1.0.1
+
+**Breaks from 0.5.0**
+
+- Support for Python 2.5 dropped
+- Default axis for benchmark suite is now axis=1 (was 0)
+
+**Bug fixes**
+
+- Confusing error message in partsort and argpartsort (:issue:`31`)
+- Update path in MANIFEST.in (:issue:`32`)
+- Wrong output for very large (2**31) input arrays (:issue:`35`)
+
+**Contributors**
+
+.. contributors:: v0.5.0..v0.6.0
+
+Bottleneck 0.5.0
+----------------
+
+*Release date: 2011-06-13*
+
+The fifth release of bottleneck adds four new functions, comes in a single
+source distribution instead of separate 32 and 64 bit versions, and contains
+bug fixes.
+
+J. David Lee wrote the C-code implementation of the double heap moving
+window median.
+
+**New functions**
+
+- move_median(), moving window median
+- partsort(), partial sort
+- argpartsort()
+- ss(), sum of squares, faster version of scipy.stats.ss
+
+**Changes**
+
+- Single source distribution instead of separate 32 and 64 bit versions
+- nanmax and nanmin now follow Numpy 1.6 (not 1.5.1) when input is all NaN
+
+**Bug fixes**
+
+- Support python 2.5 by importing `with` statement (:issue:`14`)
+- nanmedian wrong for particular ordering of NaN and non-NaN elements (:issue:`22`)
+- argpartsort, nanargmin, nanargmax returned wrong dtype on 64-bit Windows (:issue:`26`)
+- rankdata and nanrankdata crashed on 64-bit Windows (:issue:`29`)
+
+Bottleneck 0.4.3
+----------------
+
+*Release date: 2011-03-17*
+
+This is a bug fix release.
+
+**Bug fixes**
+
+- median and nanmedian modified (partial sort) input array (:issue:`11`)
+- nanmedian wrong when odd number of elements with all but last a NaN (:issue:`12`)
+
+**Enhancement**
+
+- Lazy import of SciPy (rarely used) speeds Bottleneck import 3x
+
+Bottleneck 0.4.2
+----------------
+
+*Release date: 2011-03-08*
+
+This is a bug fix release.
+
+Same bug fixed in Bottleneck 0.4.1 for nanstd() was fixed for nanvar() in
+this release. Thanks again to Christoph Gohlke for finding the bug.
+
+Bottleneck 0.4.1
+----------------
+
+*Release date: 2011-03-08*
+
+This is a bug fix release.
+
+The low-level functions nanstd_3d_int32_axis1 and nanstd_3d_int64_axis1,
+called by bottleneck.nanstd(), wrote beyond the memory owned by the output
+array if arr.shape[1] == 0 and arr.shape[0] > arr.shape[2], where arr is
+the input array.
+
+Thanks to Christoph Gohlke for finding an example to demonstrate the bug.
+
+Bottleneck 0.4.0
+----------------
+
+*Release date: 2011-03-08*
+
+The fourth release of Bottleneck contains new functions and bug fixes.
+Separate source code distributions are now made for 32 bit and 64 bit
+operating systems.
+
+**New functions**
+
+- rankdata()
+- nanrankdata()
+
+**Enhancements**
+
+- Optionally specify the shapes of the arrays used in benchmark
+- Can specify which input arrays to fill with one-third NaNs in benchmark
+
+**Breaks from 0.3.0**
+
+- Removed group_nanmean() function
+- Bump dependency from NumPy 1.4.1 to NumPy 1.5.1
+- C files are now generated with Cython 0.14.1 instead of 0.13
+
+**Bug fixes**
+
+- Some functions gave wrong output dtype for some input dtypes on 32 bit OS (:issue:`6`)
+- Some functions choked on size zero input arrays (:issue:`7`)
+- Segmentation fault with Cython 0.14.1 (but not 0.13) (:issue:`8`)
+
+Bottleneck 0.3.0
+----------------
+
+*Release date: 2010-01-19*
+
+The third release of Bottleneck is twice as fast for small input arrays and
+contains 10 new functions.
+
+**Faster**
+
+- All functions are faster (less overhead in selector functions)
+
+**New functions**
+
+- nansum()
+- move_sum()
+- move_nansum()
+- move_mean()
+- move_std()
+- move_nanstd()
+- move_min()
+- move_nanmin()
+- move_max()
+- move_nanmax()
+
+**Enhancements**
+
+- You can now specify the dtype and axis to use in the benchmark timings
+- Improved documentation and more unit tests
+
+**Breaks from 0.2.0**
+
+- Moving window functions now default to axis=-1 instead of axis=0
+- Low-level moving window selector functions no longer take window as input
+
+**Bug fix**
+
+- int input array resulted in call to slow, non-cython version of move_nanmean
+
+Bottleneck 0.2.0
+----------------
+
+*Release date: 2010-12-27*
+
+The second release of Bottleneck is faster, contains more functions, and
+supports more dtypes.
+
+**Faster**
+
+- All functions faster (less overhead) when output is not a scalar
+- Faster nanmean() for 2d, 3d arrays containing NaNs when axis is not None
+
+**New functions**
+
+- nanargmin()
+- nanargmax()
+- nanmedian()
+
+**Enhancements**
+
+- Added support for float32
+- Fallback to slower, non-Cython functions for unaccelerated ndim/dtype
+- Scipy is no longer a dependency
+- Added support for older versions of NumPy (1.4.1)
+- All functions are now templated for dtype and axis
+- Added a sandbox for prototyping of new Bottleneck functions
+- Rewrote benchmarking code
+
+Bottleneck 0.1.0
+----------------
+
+*Release date: 2010-12-10*
+
+Initial release. The three categories of Bottleneck functions:
+
+- Faster replacement for NumPy and SciPy functions
+- Moving window functions
+- Group functions that bin calculations by like-labeled elements
diff --git a/appveyor.yml b/appveyor.yml
deleted file mode 100644
index c1be5584ec..0000000000
--- a/appveyor.yml
+++ /dev/null
@@ -1,58 +0,0 @@
-shallow_clone: false # download the zip instead of the whole history
-
-max_jobs: 100
-
-environment:
-
-    global:
-        DEPS: "numpy"
-        CONDA_VENV: "testy"
-        # SDK v7.0 MSVC Express 2008's SetEnv.cmd script will fail if the
-        # /E:ON and /V:ON options are not enabled in the batch script
-        # intepreter. See: http://stackoverflow.com/a/13751649/163740
-        CMD_IN_ENV: "cmd /E:ON /V:ON /C .\\tools\\appveyor\\windows_sdk.cmd"
-
-    matrix:
-        - PYTHON_VERSION: "3.7"
-          PYTHON_ARCH: "32"
-          platform: x86
-          CONDA_HOME: "C:\\Miniconda3"
-
-        - PYTHON_VERSION: "3.7"
-          PYTHON_ARCH: "64"
-          platform: x64
-          CONDA_HOME: "C:\\Miniconda3-x64"
-
-        - PYTHON_VERSION: "3.8"
-          PYTHON_ARCH: "32"
-          platform: x86
-          CONDA_HOME: "C:\\Miniconda3"
-
-        - PYTHON_VERSION: "3.8"
-          PYTHON_ARCH: "64"
-          platform: x64
-          CONDA_HOME: "C:\\Miniconda3-x64"
-
-        - PYTHON_VERSION: "3.9"
-          PYTHON_ARCH: "32"
-          platform: x86
-          CONDA_HOME: "C:\\Miniconda3"
-
-        - PYTHON_VERSION: "3.9"
-          PYTHON_ARCH: "64"
-          platform: x64
-          CONDA_HOME: "C:\\Miniconda3-x64"
-
-install:
-    - "SET PATH=%CONDA_HOME%;%CONDA_HOME%\\Scripts;%PATH%"
-    # configures the Miniconda environment (Py2/Py3, 32/64 bit)
-    - "python -u tools\\appveyor\\conda_setup.py"
-
-build: false
-
-test_script:
-    - "activate %CONDA_VENV%"
-    - "%CMD_IN_ENV% python.exe -m pip install --upgrade pip"
-    - "%CMD_IN_ENV% pip install .[test]"
-    - "%CMD_IN_ENV% python setup.py build_ext --inplace"
-    - "python tools\\test-installed-bottleneck.py"
diff --git a/asv_bench/asv.conf.json b/asv_bench/asv.conf.json
index 106a6e60bc..07fbc90fef 100644
--- a/asv_bench/asv.conf.json
+++ b/asv_bench/asv.conf.json
@@ -55,7 +55,7 @@
 
     // The Pythons you'd like to test against.  If not provided, defaults
     // to the current version of Python used to run `asv`.
-    "pythons": ["3.7"],
+    "pythons": ["2.7", "3.7"],
 
     // The list of conda channel names to be searched for benchmark
     // dependency packages in the specified order
@@ -89,8 +89,8 @@
 	    "numpy": ["1.16"],
 	},
 	"env": {
-	    "CC": ["gcc-9"],
-	    "CXX": ["g++-9"],
+	    "CC": ["gcc"],
+	    "CXX": ["g++"],
 	}
     },
 
@@ -133,28 +133,8 @@
 		"numpy": "1.16",
 	    },
 	    "env": {
-		"CC": "clang-9",
-		"CXX": "clang++-9",
-	    },
-	    "python": "3.7"
-	},
-        {
-	    "req": {
-		"numpy": "1.16",
-	    },
-	    "env": {
-		"CC": "gcc-7",
-		"CXX": "g++-7",
-	    },
-	    "python": "3.7"
-	},
-        {
-	    "req": {
-		"numpy": "1.16",
-	    },
-	    "env": {
-		"CC": "gcc-8",
-		"CXX": "g++-8",
+		"CC": "clang",
+		"CXX": "clang++",
 	    },
 	    "python": "3.7"
 	}
diff --git a/asv_bench/benchmarks/memory.py b/asv_bench/benchmarks/memory.py
index fbd0612007..a80f5034aa 100644
--- a/asv_bench/benchmarks/memory.py
+++ b/asv_bench/benchmarks/memory.py
@@ -1,6 +1,5 @@
-import numpy as np
-
 import bottleneck as bn
+import numpy as np
 
 
 class Memory:
diff --git a/asv_bench/benchmarks/nonreduce.py b/asv_bench/benchmarks/nonreduce.py
index 0206d0d94b..bdb051691d 100644
--- a/asv_bench/benchmarks/nonreduce.py
+++ b/asv_bench/benchmarks/nonreduce.py
@@ -1,6 +1,5 @@
-import numpy as np
-
 import bottleneck as bn
+import numpy as np
 
 
 class TimeReplace2D:
diff --git a/asv_bench/benchmarks/reduce.py b/asv_bench/benchmarks/reduce.py
index 9d348d2496..94cd0369a0 100644
--- a/asv_bench/benchmarks/reduce.py
+++ b/asv_bench/benchmarks/reduce.py
@@ -1,6 +1,6 @@
+import bottleneck as bn
 import numpy as np
 
-import bottleneck as bn
 
 RAND_ARRAY_CACHE = {}
 
diff --git a/bottleneck/__init__.py b/bottleneck/__init__.py
index 62905a6773..c5495b3337 100644
--- a/bottleneck/__init__.py
+++ b/bottleneck/__init__.py
@@ -1,53 +1,24 @@
-try:
-    from . import slow
-    from .move import (
-        move_argmax,
-        move_argmin,
-        move_max,
-        move_mean,
-        move_median,
-        move_min,
-        move_rank,
-        move_std,
-        move_sum,
-        move_var,
-    )
-    from .nonreduce import replace
-    from .nonreduce_axis import argpartition, nanrankdata, partition, push, rankdata
-    from .reduce import (
-        allnan,
-        anynan,
-        median,
-        nanargmax,
-        nanargmin,
-        nanmax,
-        nanmean,
-        nanmedian,
-        nanmin,
-        nanstd,
-        nansum,
-        nanvar,
-        ss,
-    )
-
-except ImportError:
-    raise ImportError(
-        "bottleneck modules failed to import, likely due to a "
-        "mismatch in NumPy versions. Either upgrade numpy to "
-        "1.16+ or install with:\n\t"
-        "pip install --no-build-isolation --no-cache-dir "
-        "bottleneck"
-    )
-
 from bottleneck.benchmark.bench import bench
 from bottleneck.benchmark.bench_detailed import bench_detailed
 from bottleneck.tests.util import get_functions
+
+from . import slow
 from ._pytesttester import PytestTester
-from ._version import get_versions  # noqa: E402
+from .move import (move_argmax, move_argmin, move_max, move_mean, move_median,
+                   move_min, move_rank, move_std, move_sum, move_var)
+from .nonreduce import replace
+from .nonreduce_axis import (argpartition, nanrankdata, partition, push,
+                             rankdata)
+from .reduce import (allnan, anynan, median, nanargmax, nanargmin, nanmax,
+                     nanmean, nanmedian, nanmin, nanstd, nansum, nanvar, ss)
 
 test = PytestTester(__name__)
 del PytestTester
 
+from ._version import get_versions  # noqa: E402
 
-__version__ = get_versions()["version"]  # type: ignore
+__version__ = get_versions()["version"]
 del get_versions
+
+from . import _version
+__version__ = _version.get_versions()['version']
diff --git a/bottleneck/_version.py b/bottleneck/_version.py
index d3419bdd37..ede572984a 100644
--- a/bottleneck/_version.py
+++ b/bottleneck/_version.py
@@ -1,3 +1,4 @@
+
 # This file helps to compute a version number in source trees obtained from
 # git-archive tarball (such as those provided by githubs download-from-tag
 # feature). Distribution tarballs (built by setup.py sdist) and build
@@ -5,7 +6,7 @@
 # that just contains the computed version number.
 
 # This file is released into the public domain. Generated by
-# versioneer-0.19 (https://github.com/python-versioneer/python-versioneer)
+# versioneer-0.22.dev0 (https://github.com/python-versioneer/python-versioneer)
 
 """Git implementation of _version.py."""
 
@@ -14,6 +15,7 @@
 import re
 import subprocess
 import sys
+from typing import Callable, Dict
 
 
 def get_keywords():
@@ -39,7 +41,7 @@ def get_config():
     # _version.py
     cfg = VersioneerConfig()
     cfg.VCS = "git"
-    cfg.style = "pep440"
+    cfg.style = "pep440-pre"
     cfg.tag_prefix = "v"
     cfg.parentdir_prefix = "bottleneck-"
     cfg.versionfile_source = "bottleneck/_version.py"
@@ -51,40 +53,44 @@ class NotThisMethod(Exception):
     """Exception raised if a method is not valid for the current scenario."""
 
 
-LONG_VERSION_PY = {}
-HANDLERS = {}
+LONG_VERSION_PY: Dict[str, str] = {}
+HANDLERS: Dict[str, Dict[str, Callable]] = {}
 
 
 def register_vcs_handler(vcs, method):  # decorator
     """Create decorator to mark a method as the handler of a VCS."""
-
     def decorate(f):
         """Store f in HANDLERS[vcs][method]."""
         if vcs not in HANDLERS:
             HANDLERS[vcs] = {}
         HANDLERS[vcs][method] = f
         return f
-
     return decorate
 
 
-def run_command(commands, args, cwd=None, verbose=False, hide_stderr=False, env=None):
+def run_command(commands, args, cwd=None, verbose=False, hide_stderr=False,
+                env=None):
     """Call the given command(s)."""
     assert isinstance(commands, list)
-    p = None
-    for c in commands:
+    process = None
+
+    popen_kwargs = {}
+    if sys.platform == "win32":
+        # This hides the console window if pythonw.exe is used
+        startupinfo = subprocess.STARTUPINFO()
+        startupinfo.dwFlags |= subprocess.STARTF_USESHOWWINDOW
+        popen_kwargs["startupinfo"] = startupinfo
+
+    for command in commands:
         try:
-            dispcmd = str([c] + args)
+            dispcmd = str([command] + args)
             # remember shell=False, so use git.cmd on windows, not just git
-            p = subprocess.Popen(
-                [c] + args,
-                cwd=cwd,
-                env=env,
-                stdout=subprocess.PIPE,
-                stderr=(subprocess.PIPE if hide_stderr else None),
-            )
+            process = subprocess.Popen([command] + args, cwd=cwd, env=env,
+                                       stdout=subprocess.PIPE,
+                                       stderr=(subprocess.PIPE if hide_stderr
+                                               else None), **popen_kwargs)
             break
-        except EnvironmentError:
+        except OSError:
             e = sys.exc_info()[1]
             if e.errno == errno.ENOENT:
                 continue
@@ -96,13 +102,13 @@ def run_command(commands, args, cwd=None, verbose=False, hide_stderr=False, env=
         if verbose:
             print("unable to find command, tried %s" % (commands,))
         return None, None
-    stdout = p.communicate()[0].strip().decode()
-    if p.returncode != 0:
+    stdout = process.communicate()[0].strip().decode()
+    if process.returncode != 0:
         if verbose:
             print("unable to run %s (error)" % dispcmd)
             print("stdout was %s" % stdout)
-        return None, p.returncode
-    return stdout, p.returncode
+        return None, process.returncode
+    return stdout, process.returncode
 
 
 def versions_from_parentdir(parentdir_prefix, root, verbose):
@@ -114,25 +120,18 @@ def versions_from_parentdir(parentdir_prefix, root, verbose):
     """
     rootdirs = []
 
-    for i in range(3):
+    for _ in range(3):
         dirname = os.path.basename(root)
         if dirname.startswith(parentdir_prefix):
-            return {
-                "version": dirname[len(parentdir_prefix) :],
-                "full-revisionid": None,
-                "dirty": False,
-                "error": None,
-                "date": None,
-            }
-        else:
-            rootdirs.append(root)
-            root = os.path.dirname(root)  # up a level
+            return {"version": dirname[len(parentdir_prefix):],
+                    "full-revisionid": None,
+                    "dirty": False, "error": None, "date": None}
+        rootdirs.append(root)
+        root = os.path.dirname(root)  # up a level
 
     if verbose:
-        print(
-            "Tried directories %s but none started with prefix %s"
-            % (str(rootdirs), parentdir_prefix)
-        )
+        print("Tried directories %s but none started with prefix %s" %
+              (str(rootdirs), parentdir_prefix))
     raise NotThisMethod("rootdir doesn't start with parentdir_prefix")
 
 
@@ -145,22 +144,21 @@ def git_get_keywords(versionfile_abs):
     # _version.py.
     keywords = {}
     try:
-        f = open(versionfile_abs, "r")
-        for line in f.readlines():
-            if line.strip().startswith("git_refnames ="):
-                mo = re.search(r'=\s*"(.*)"', line)
-                if mo:
-                    keywords["refnames"] = mo.group(1)
-            if line.strip().startswith("git_full ="):
-                mo = re.search(r'=\s*"(.*)"', line)
-                if mo:
-                    keywords["full"] = mo.group(1)
-            if line.strip().startswith("git_date ="):
-                mo = re.search(r'=\s*"(.*)"', line)
-                if mo:
-                    keywords["date"] = mo.group(1)
-        f.close()
-    except EnvironmentError:
+        with open(versionfile_abs, "r") as fobj:
+            for line in fobj:
+                if line.strip().startswith("git_refnames ="):
+                    mo = re.search(r'=\s*"(.*)"', line)
+                    if mo:
+                        keywords["refnames"] = mo.group(1)
+                if line.strip().startswith("git_full ="):
+                    mo = re.search(r'=\s*"(.*)"', line)
+                    if mo:
+                        keywords["full"] = mo.group(1)
+                if line.strip().startswith("git_date ="):
+                    mo = re.search(r'=\s*"(.*)"', line)
+                    if mo:
+                        keywords["date"] = mo.group(1)
+    except OSError:
         pass
     return keywords
 
@@ -168,8 +166,8 @@ def git_get_keywords(versionfile_abs):
 @register_vcs_handler("git", "keywords")
 def git_versions_from_keywords(keywords, tag_prefix, verbose):
     """Get version information from git keywords."""
-    if not keywords:
-        raise NotThisMethod("no keywords at all, weird")
+    if "refnames" not in keywords:
+        raise NotThisMethod("Short version file found")
     date = keywords.get("date")
     if date is not None:
         # Use only the last line.  Previous lines may contain GPG signature
@@ -188,11 +186,11 @@ def git_versions_from_keywords(keywords, tag_prefix, verbose):
         if verbose:
             print("keywords are unexpanded, not using")
         raise NotThisMethod("unexpanded keywords, not a git-archive tarball")
-    refs = set([r.strip() for r in refnames.strip("()").split(",")])
+    refs = {r.strip() for r in refnames.strip("()").split(",")}
     # starting in git-1.8.3, tags are listed as "tag: foo-1.0" instead of
     # just "foo-1.0". If we see a "tag: " prefix, prefer those.
     TAG = "tag: "
-    tags = set([r[len(TAG) :] for r in refs if r.startswith(TAG)])
+    tags = {r[len(TAG):] for r in refs if r.startswith(TAG)}
     if not tags:
         # Either we're using git < 1.8.3, or there really are no tags. We use
         # a heuristic: assume all version tags have a digit. The old git %d
@@ -201,7 +199,7 @@ def git_versions_from_keywords(keywords, tag_prefix, verbose):
         # between branches and tags. By ignoring refnames without digits, we
         # filter out many common branch names like "release" and
         # "stabilization", as well as "HEAD" and "master".
-        tags = set([r for r in refs if re.search(r"\d", r)])
+        tags = {r for r in refs if re.search(r'\d', r)}
         if verbose:
             print("discarding '%s', no digits" % ",".join(refs - tags))
     if verbose:
@@ -209,30 +207,28 @@ def git_versions_from_keywords(keywords, tag_prefix, verbose):
     for ref in sorted(tags):
         # sorting will prefer e.g. "2.0" over "2.0rc1"
         if ref.startswith(tag_prefix):
-            r = ref[len(tag_prefix) :]
+            r = ref[len(tag_prefix):]
+            # Filter out refs that exactly match prefix or that don't start
+            # with a number once the prefix is stripped (mostly a concern
+            # when prefix is '')
+            if not re.match(r'\d', r):
+                continue
             if verbose:
                 print("picking %s" % r)
-            return {
-                "version": r,
-                "full-revisionid": keywords["full"].strip(),
-                "dirty": False,
-                "error": None,
-                "date": date,
-            }
+            return {"version": r,
+                    "full-revisionid": keywords["full"].strip(),
+                    "dirty": False, "error": None,
+                    "date": date}
     # no suitable tags, so version is "0+unknown", but full hex is still there
     if verbose:
         print("no suitable tags, using unknown + full revision id")
-    return {
-        "version": "0+unknown",
-        "full-revisionid": keywords["full"].strip(),
-        "dirty": False,
-        "error": "no suitable tags",
-        "date": None,
-    }
+    return {"version": "0+unknown",
+            "full-revisionid": keywords["full"].strip(),
+            "dirty": False, "error": "no suitable tags", "date": None}
 
 
 @register_vcs_handler("git", "pieces_from_vcs")
-def git_pieces_from_vcs(tag_prefix, root, verbose, run_command=run_command):
+def git_pieces_from_vcs(tag_prefix, root, verbose, runner=run_command):
     """Get version from 'git describe' in the root of the source tree.
 
     This only gets called if the git-archive 'subst' keywords were *not*
@@ -243,32 +239,25 @@ def git_pieces_from_vcs(tag_prefix, root, verbose, run_command=run_command):
     if sys.platform == "win32":
         GITS = ["git.cmd", "git.exe"]
 
-    out, rc = run_command(GITS, ["rev-parse", "--git-dir"], cwd=root, hide_stderr=True)
+    _, rc = runner(GITS, ["rev-parse", "--git-dir"], cwd=root,
+                   hide_stderr=True)
     if rc != 0:
         if verbose:
             print("Directory %s not under git control" % root)
         raise NotThisMethod("'git rev-parse --git-dir' returned error")
 
+    MATCH_ARGS = ["--match", "%s*" % tag_prefix] if tag_prefix else []
+
     # if there is a tag matching tag_prefix, this yields TAG-NUM-gHEX[-dirty]
     # if there isn't one, this yields HEX[-dirty] (no NUM)
-    describe_out, rc = run_command(
-        GITS,
-        [
-            "describe",
-            "--tags",
-            "--dirty",
-            "--always",
-            "--long",
-            "--match",
-            "%s*" % tag_prefix,
-        ],
-        cwd=root,
-    )
+    describe_out, rc = runner(GITS, ["describe", "--tags", "--dirty",
+                                     "--always", "--long", *MATCH_ARGS],
+                              cwd=root)
     # --long was added in git-1.5.5
     if describe_out is None:
         raise NotThisMethod("'git describe' failed")
     describe_out = describe_out.strip()
-    full_out, rc = run_command(GITS, ["rev-parse", "HEAD"], cwd=root)
+    full_out, rc = runner(GITS, ["rev-parse", "HEAD"], cwd=root)
     if full_out is None:
         raise NotThisMethod("'git rev-parse' failed")
     full_out = full_out.strip()
@@ -278,6 +267,39 @@ def git_pieces_from_vcs(tag_prefix, root, verbose, run_command=run_command):
     pieces["short"] = full_out[:7]  # maybe improved later
     pieces["error"] = None
 
+    branch_name, rc = runner(GITS, ["rev-parse", "--abbrev-ref", "HEAD"],
+                             cwd=root)
+    # --abbrev-ref was added in git-1.6.3
+    if rc != 0 or branch_name is None:
+        raise NotThisMethod("'git rev-parse --abbrev-ref' returned error")
+    branch_name = branch_name.strip()
+
+    if branch_name == "HEAD":
+        # If we aren't exactly on a branch, pick a branch which represents
+        # the current commit. If all else fails, we are on a branchless
+        # commit.
+        branches, rc = runner(GITS, ["branch", "--contains"], cwd=root)
+        # --contains was added in git-1.5.4
+        if rc != 0 or branches is None:
+            raise NotThisMethod("'git branch --contains' returned error")
+        branches = branches.split("\n")
+
+        # Remove the first line if we're running detached
+        if "(" in branches[0]:
+            branches.pop(0)
+
+        # Strip off the leading "* " from the list of branches.
+        branches = [branch[2:] for branch in branches]
+        if "master" in branches:
+            branch_name = "master"
+        elif not branches:
+            branch_name = None
+        else:
+            # Pick the first branch that is returned. Good or bad.
+            branch_name = branches[0]
+
+    pieces["branch"] = branch_name
+
     # parse describe_out. It will be like TAG-NUM-gHEX[-dirty] or HEX[-dirty]
     # TAG might have hyphens.
     git_describe = describe_out
@@ -286,16 +308,17 @@ def git_pieces_from_vcs(tag_prefix, root, verbose, run_command=run_command):
     dirty = git_describe.endswith("-dirty")
     pieces["dirty"] = dirty
     if dirty:
-        git_describe = git_describe[: git_describe.rindex("-dirty")]
+        git_describe = git_describe[:git_describe.rindex("-dirty")]
 
     # now we have TAG-NUM-gHEX or HEX
 
     if "-" in git_describe:
         # TAG-NUM-gHEX
-        mo = re.search(r"^(.+)-(\d+)-g([0-9a-f]+)$", git_describe)
+        mo = re.search(r'^(.+)-(\d+)-g([0-9a-f]+)$', git_describe)
         if not mo:
-            # unparseable. Maybe git-describe is misbehaving?
-            pieces["error"] = "unable to parse git-describe output: '%s'" % describe_out
+            # unparsable. Maybe git-describe is misbehaving?
+            pieces["error"] = ("unable to parse git-describe output: '%s'"
+                               % describe_out)
             return pieces
 
         # tag
@@ -304,12 +327,10 @@ def git_pieces_from_vcs(tag_prefix, root, verbose, run_command=run_command):
             if verbose:
                 fmt = "tag '%s' doesn't start with prefix '%s'"
                 print(fmt % (full_tag, tag_prefix))
-            pieces["error"] = "tag '%s' doesn't start with prefix '%s'" % (
-                full_tag,
-                tag_prefix,
-            )
+            pieces["error"] = ("tag '%s' doesn't start with prefix '%s'"
+                               % (full_tag, tag_prefix))
             return pieces
-        pieces["closest-tag"] = full_tag[len(tag_prefix) :]
+        pieces["closest-tag"] = full_tag[len(tag_prefix):]
 
         # distance: number of commits since tag
         pieces["distance"] = int(mo.group(2))
@@ -320,13 +341,11 @@ def git_pieces_from_vcs(tag_prefix, root, verbose, run_command=run_command):
     else:
         # HEX: no tags
         pieces["closest-tag"] = None
-        count_out, rc = run_command(GITS, ["rev-list", "HEAD", "--count"], cwd=root)
+        count_out, rc = runner(GITS, ["rev-list", "HEAD", "--count"], cwd=root)
         pieces["distance"] = int(count_out)  # total number of commits
 
     # commit date: see ISO-8601 comment in git_versions_from_keywords()
-    date = run_command(GITS, ["show", "-s", "--format=%ci", "HEAD"], cwd=root)[
-        0
-    ].strip()
+    date = runner(GITS, ["show", "-s", "--format=%ci", "HEAD"], cwd=root)[0].strip()
     # Use only the last line.  Previous lines may contain GPG signature
     # information.
     date = date.splitlines()[-1]
@@ -360,22 +379,71 @@ def render_pep440(pieces):
                 rendered += ".dirty"
     else:
         # exception #1
-        rendered = "0+untagged.%d.g%s" % (pieces["distance"], pieces["short"])
+        rendered = "0+untagged.%d.g%s" % (pieces["distance"],
+                                          pieces["short"])
+        if pieces["dirty"]:
+            rendered += ".dirty"
+    return rendered
+
+
+def render_pep440_branch(pieces):
+    """TAG[[.dev0]+DISTANCE.gHEX[.dirty]] .
+
+    The ".dev0" means not master branch. Note that .dev0 sorts backwards
+    (a feature branch will appear "older" than the master branch).
+
+    Exceptions:
+    1: no tags. 0[.dev0]+untagged.DISTANCE.gHEX[.dirty]
+    """
+    if pieces["closest-tag"]:
+        rendered = pieces["closest-tag"]
+        if pieces["distance"] or pieces["dirty"]:
+            if pieces["branch"] != "master":
+                rendered += ".dev0"
+            rendered += plus_or_dot(pieces)
+            rendered += "%d.g%s" % (pieces["distance"], pieces["short"])
+            if pieces["dirty"]:
+                rendered += ".dirty"
+    else:
+        # exception #1
+        rendered = "0"
+        if pieces["branch"] != "master":
+            rendered += ".dev0"
+        rendered += "+untagged.%d.g%s" % (pieces["distance"],
+                                          pieces["short"])
         if pieces["dirty"]:
             rendered += ".dirty"
     return rendered
 
 
+def pep440_split_post(ver):
+    """Split pep440 version string at the post-release segment.
+
+    Returns the release segments before the post-release and the
+    post-release version number (or -1 if no post-release segment is present).
+    """
+    vc = str.split(ver, ".post")
+    return vc[0], int(vc[1] or 0) if len(vc) == 2 else None
+
+
 def render_pep440_pre(pieces):
-    """TAG[.post0.devDISTANCE] -- No -dirty.
+    """TAG[.postN.devDISTANCE] -- No -dirty.
 
     Exceptions:
     1: no tags. 0.post0.devDISTANCE
     """
     if pieces["closest-tag"]:
-        rendered = pieces["closest-tag"]
         if pieces["distance"]:
-            rendered += ".post0.dev%d" % pieces["distance"]
+            # update the post release segment
+            tag_version, post_version = pep440_split_post(pieces["closest-tag"])
+            rendered = tag_version
+            if post_version is not None:
+                rendered += ".post%d.dev%d" % (post_version+1, pieces["distance"])
+            else:
+                rendered += ".post0.dev%d" % (pieces["distance"])
+        else:
+            # no commits, use the tag as the version
+            rendered = pieces["closest-tag"]
     else:
         # exception #1
         rendered = "0.post0.dev%d" % pieces["distance"]
@@ -409,6 +477,35 @@ def render_pep440_post(pieces):
     return rendered
 
 
+def render_pep440_post_branch(pieces):
+    """TAG[.postDISTANCE[.dev0]+gHEX[.dirty]] .
+
+    The ".dev0" means not master branch.
+
+    Exceptions:
+    1: no tags. 0.postDISTANCE[.dev0]+gHEX[.dirty]
+    """
+    if pieces["closest-tag"]:
+        rendered = pieces["closest-tag"]
+        if pieces["distance"] or pieces["dirty"]:
+            rendered += ".post%d" % pieces["distance"]
+            if pieces["branch"] != "master":
+                rendered += ".dev0"
+            rendered += plus_or_dot(pieces)
+            rendered += "g%s" % pieces["short"]
+            if pieces["dirty"]:
+                rendered += ".dirty"
+    else:
+        # exception #1
+        rendered = "0.post%d" % pieces["distance"]
+        if pieces["branch"] != "master":
+            rendered += ".dev0"
+        rendered += "+g%s" % pieces["short"]
+        if pieces["dirty"]:
+            rendered += ".dirty"
+    return rendered
+
+
 def render_pep440_old(pieces):
     """TAG[.postDISTANCE[.dev0]] .
 
@@ -474,23 +571,25 @@ def render_git_describe_long(pieces):
 def render(pieces, style):
     """Render the given version pieces into the requested style."""
     if pieces["error"]:
-        return {
-            "version": "unknown",
-            "full-revisionid": pieces.get("long"),
-            "dirty": None,
-            "error": pieces["error"],
-            "date": None,
-        }
+        return {"version": "unknown",
+                "full-revisionid": pieces.get("long"),
+                "dirty": None,
+                "error": pieces["error"],
+                "date": None}
 
     if not style or style == "default":
         style = "pep440"  # the default
 
     if style == "pep440":
         rendered = render_pep440(pieces)
+    elif style == "pep440-branch":
+        rendered = render_pep440_branch(pieces)
     elif style == "pep440-pre":
         rendered = render_pep440_pre(pieces)
     elif style == "pep440-post":
         rendered = render_pep440_post(pieces)
+    elif style == "pep440-post-branch":
+        rendered = render_pep440_post_branch(pieces)
     elif style == "pep440-old":
         rendered = render_pep440_old(pieces)
     elif style == "git-describe":
@@ -500,13 +599,9 @@ def render(pieces, style):
     else:
         raise ValueError("unknown style '%s'" % style)
 
-    return {
-        "version": rendered,
-        "full-revisionid": pieces["long"],
-        "dirty": pieces["dirty"],
-        "error": None,
-        "date": pieces.get("date"),
-    }
+    return {"version": rendered, "full-revisionid": pieces["long"],
+            "dirty": pieces["dirty"], "error": None,
+            "date": pieces.get("date")}
 
 
 def get_versions():
@@ -520,7 +615,8 @@ def get_versions():
     verbose = cfg.verbose
 
     try:
-        return git_versions_from_keywords(get_keywords(), cfg.tag_prefix, verbose)
+        return git_versions_from_keywords(get_keywords(), cfg.tag_prefix,
+                                          verbose)
     except NotThisMethod:
         pass
 
@@ -529,16 +625,13 @@ def get_versions():
         # versionfile_source is the relative path from the top of the source
         # tree (where the .git directory might live) to this file. Invert
         # this to find the root from __file__.
-        for i in cfg.versionfile_source.split("/"):
+        for _ in cfg.versionfile_source.split('/'):
             root = os.path.dirname(root)
     except NameError:
-        return {
-            "version": "0+unknown",
-            "full-revisionid": None,
-            "dirty": None,
-            "error": "unable to find root of source tree",
-            "date": None,
-        }
+        return {"version": "0+unknown", "full-revisionid": None,
+                "dirty": None,
+                "error": "unable to find root of source tree",
+                "date": None}
 
     try:
         pieces = git_pieces_from_vcs(cfg.tag_prefix, root, verbose)
@@ -552,10 +645,6 @@ def get_versions():
     except NotThisMethod:
         pass
 
-    return {
-        "version": "0+unknown",
-        "full-revisionid": None,
-        "dirty": None,
-        "error": "unable to compute version",
-        "date": None,
-    }
+    return {"version": "0+unknown", "full-revisionid": None,
+            "dirty": None,
+            "error": "unable to compute version", "date": None}
diff --git a/bottleneck/benchmark/autotimeit.py b/bottleneck/benchmark/autotimeit.py
index 2f8c41a19c..b37c59a5e0 100644
--- a/bottleneck/benchmark/autotimeit.py
+++ b/bottleneck/benchmark/autotimeit.py
@@ -1,19 +1,16 @@
 import timeit
-from typing import Tuple
 
 
-def autotimeit(
-    stmt: str, setup: str = "pass", repeat: int = 3, mintime: float = 0.2
-) -> float:
+def autotimeit(stmt, setup="pass", repeat=3, mintime=0.2):
     timer = timeit.Timer(stmt, setup)
     number, time1 = autoscaler(timer, mintime)
     time2 = timer.repeat(repeat=repeat - 1, number=number)
     return min(time2 + [time1]) / number
 
 
-def autoscaler(timer: timeit.Timer, mintime: float) -> Tuple[int, float]:
+def autoscaler(timer, mintime):
     number = 1
-    for _ in range(12):
+    for i in range(12):
         time = timer.timeit(number)
         if time > mintime:
             return number, time
diff --git a/bottleneck/benchmark/bench.py b/bottleneck/benchmark/bench.py
index 5875f76cdb..f18ec5a19c 100644
--- a/bottleneck/benchmark/bench.py
+++ b/bottleneck/benchmark/bench.py
@@ -1,24 +1,14 @@
 import numpy as np
-
 import bottleneck as bn
 from .autotimeit import autotimeit
 
 __all__ = ["bench"]
 
-ARRAY_CACHE = {}
-
 
 def bench(
-    shapes=[
-        (100,),
-        (1000, 1000),
-        (1000, 1000),
-        (1000, 1000),
-        (1000, 1000),
-        (1000, 1000),
-    ],
-    axes=[0, None, 0, 0, 1, 1],
-    nans=[False, True, False, True, False, True],
+    shapes=[(100,), (1000, 1000), (1000, 1000), (1000, 1000), (1000, 1000)],
+    axes=[0, 0, 0, 1, 1],
+    nans=[False, False, True, False, True],
     dtype="float64",
     order="C",
     functions=None,
@@ -99,42 +89,41 @@ def timer(statements, setups):
     return speed
 
 
-def getarray(shape, dtype, nans, order, allnans=False):
-    key = (tuple(shape), dtype, nans, order, allnans)
-    if key not in ARRAY_CACHE:
-        a = np.arange(np.prod(shape), dtype=dtype)
-        if issubclass(a.dtype.type, np.inexact):
-            if nans:
-                a[::5] = np.nan
-            if allnans:
-                a[:] = np.nan
-        rs = np.random.RandomState(shape)
-        rs.shuffle(a)
-        ARRAY_CACHE[key] = np.array(a.reshape(*shape), order=order)
-
-    return ARRAY_CACHE[key].copy(order=order)
+def getarray(shape, dtype, nans, order):
+    a = np.arange(np.prod(shape), dtype=dtype)
+    if nans and issubclass(a.dtype.type, np.inexact):
+        a[::5] = np.nan
+    rs = np.random.RandomState(shape)
+    rs.shuffle(a)
+    return np.array(a.reshape(*shape), order=order)
 
 
 def benchsuite(shapes, dtype, nans, axes, order, functions):
 
     suite = []
 
-    def getsetups(setup, shapes, nans, axes, dtype, order, allnan=False):
+    def getsetups(setup, shapes, nans, axes, dtype, order):
+        template = """
+        from bottleneck.benchmark.bench import getarray
+        a = getarray(%s, '%s', %s, '%s')
+        axis=%s
+        %s"""
         setups = []
         for shape, axis, nan in zip(shapes, axes, nans):
-            s = f"""
-from bottleneck.benchmark.bench import getarray
-a = getarray({shape}, '{dtype}', {nan}, '{order}', allnans={allnan})
-axis={axis}
-{setup}"""
+            s = template % (
+                str(shape),
+                str(dtype),
+                str(nan),
+                str(order),
+                str(axis),
+                setup,
+            )
             s = "\n".join([line.strip() for line in s.split("\n")])
             setups.append(s)
         return setups
 
     # non-moving window functions
     funcs = bn.get_functions("reduce", as_string=True)
-    # Handle all/any separately
-    funcs = sorted(set(funcs) - set(["allnan", "anynan"]))
     funcs += ["rankdata", "nanrankdata"]
     for func in funcs:
         if functions is not None and func not in functions:
@@ -156,39 +145,6 @@ def getsetups(setup, shapes, nans, axes, dtype, order, allnan=False):
         run["setups"] = getsetups(setup, shapes, nans, axes, dtype, order)
         suite.append(run)
 
-    for func in ["allnan", "anynan"]:
-        if functions is not None and func not in functions:
-            continue
-        for case in ["", "_fast", "_slow"]:
-            run = {}
-            run["name"] = func + case
-            run["statements"] = ["bn_func(a, axis)", "sl_func(a, axis)"]
-            setup = """
-            from bottleneck import %s as bn_func
-            try: from numpy import %s as sl_func
-            except ImportError: from bottleneck.slow import %s as sl_func
-            if "%s" == "median": from bottleneck.slow import median as sl_func
-        """ % (
-                func,
-                func,
-                func,
-                func,
-            )
-            if case:
-                if func == "allnan":
-                    allnan_case = "slow" in case
-                else:
-                    allnan_case = "fast" in case
-
-                new_nans = [allnan_case] * len(nans)
-            else:
-                new_nans = nans
-                allnan_case = False
-            run["setups"] = getsetups(
-                setup, shapes, new_nans, axes, dtype, order, allnan=allnan_case
-            )
-            suite.append(run)
-
     # partition, argpartition
     funcs = ["partition", "argpartition"]
     for func in funcs:
diff --git a/bottleneck/benchmark/bench_detailed.py b/bottleneck/benchmark/bench_detailed.py
index 231ed51638..4907446414 100644
--- a/bottleneck/benchmark/bench_detailed.py
+++ b/bottleneck/benchmark/bench_detailed.py
@@ -1,5 +1,4 @@
 import numpy as np
-
 import bottleneck as bn
 from .autotimeit import autotimeit
 
diff --git a/bottleneck/slow/__init__.py b/bottleneck/slow/__init__.py
index 6f4c1a5a5a..4a3192e8d2 100644
--- a/bottleneck/slow/__init__.py
+++ b/bottleneck/slow/__init__.py
@@ -1,65 +1,6 @@
-from .move import (
-    move_argmax,
-    move_argmin,
-    move_max,
-    move_mean,
-    move_median,
-    move_min,
-    move_rank,
-    move_std,
-    move_sum,
-    move_var,
-)
-from .nonreduce import replace
-from .nonreduce_axis import argpartition, nanrankdata, partition, push, rankdata
-from .reduce import (
-    allnan,
-    anynan,
-    median,
-    nanargmax,
-    nanargmin,
-    nanmax,
-    nanmean,
-    nanmedian,
-    nanmin,
-    nanstd,
-    nansum,
-    nanvar,
-    ss,
-)
+# flake8: noqa
 
-__all__ = [
-    # move functions
-    "move_argmax",
-    "move_argmin",
-    "move_max",
-    "move_mean",
-    "move_median",
-    "move_min",
-    "move_rank",
-    "move_std",
-    "move_sum",
-    "move_var",
-    # nonreduce functions
-    "replace",
-    # nonreduce_axis functions
-    "argpartition",
-    "nanrankdata",
-    "partition",
-    "push",
-    "rankdata",
-    # reduce functions
-    "allnan",
-    "anynan",
-    "median",
-    "nanargmax",
-    "nanargmin",
-    "nanmax",
-    "nanmean",
-    "nanmedian",
-    "nanmin",
-    "nanstd",
-    "nansum",
-    "nanvar",
-    "ss",
-]
+from bottleneck.slow.reduce import *
+from bottleneck.slow.nonreduce import *
+from bottleneck.slow.nonreduce_axis import *
+from bottleneck.slow.move import *
diff --git a/bottleneck/slow/move.py b/bottleneck/slow/move.py
index af2ed31eb9..6b48c7bc13 100644
--- a/bottleneck/slow/move.py
+++ b/bottleneck/slow/move.py
@@ -4,6 +4,7 @@
 
 import numpy as np
 
+
 __all__ = [
     "move_sum",
     "move_mean",
@@ -164,7 +165,7 @@ def _mask(a, window, min_count, axis):
     idx3 = tuple(idx3)
     nidx1 = n[idx1]
     nidx1 = nidx1 - n[idx2]
-    idx = np.empty(a.shape, dtype=bool)
+    idx = np.empty(a.shape, dtype=np.bool)
     idx[idx1] = nidx1 < min_count
     idx[idx3] = n[idx3] < min_count
     return idx
diff --git a/bottleneck/slow/nonreduce.py b/bottleneck/slow/nonreduce.py
index 10b8c47f0b..24585a0289 100644
--- a/bottleneck/slow/nonreduce.py
+++ b/bottleneck/slow/nonreduce.py
@@ -1,11 +1,9 @@
-from typing import Union
-
 import numpy as np
 
 __all__ = ["replace"]
 
 
-def replace(a: np.ndarray, old: Union[int, float], new: Union[int, float]) -> None:
+def replace(a, old, new):
     "Slow replace (inplace) used for unaccelerated dtypes."
     if type(a) is not np.ndarray:
         raise TypeError("`a` must be a numpy array.")
diff --git a/bottleneck/slow/nonreduce_axis.py b/bottleneck/slow/nonreduce_axis.py
index 06fa3ef2d6..f09dfa7393 100644
--- a/bottleneck/slow/nonreduce_axis.py
+++ b/bottleneck/slow/nonreduce_axis.py
@@ -1,5 +1,5 @@
 import numpy as np
-from numpy import argpartition, partition
+from numpy import partition, argpartition
 
 __all__ = ["rankdata", "nanrankdata", "partition", "argpartition", "push"]
 
diff --git a/bottleneck/slow/reduce.py b/bottleneck/slow/reduce.py
index 8700ac8739..4743bdb43b 100644
--- a/bottleneck/slow/reduce.py
+++ b/bottleneck/slow/reduce.py
@@ -1,6 +1,4 @@
 import warnings
-from typing import Optional, Union
-
 import numpy as np
 from numpy import nanmean, nansum
 
@@ -21,78 +19,74 @@
 ]
 
 
-def nanargmin(a: np.ndarray, axis: Optional[int] = None) -> Union[int, np.ndarray]:
+def nanargmin(a, axis=None):
     "Slow nanargmin function used for unaccelerated dtypes."
     with warnings.catch_warnings():
         warnings.simplefilter("ignore")
         return np.nanargmin(a, axis=axis)
 
 
-def nanargmax(a: np.ndarray, axis: Optional[int] = None) -> Union[int, np.ndarray]:
+def nanargmax(a, axis=None):
     "Slow nanargmax function used for unaccelerated dtypes."
     with warnings.catch_warnings():
         warnings.simplefilter("ignore")
         return np.nanargmax(a, axis=axis)
 
 
-def nanvar(
-    a: np.ndarray, axis: Optional[int] = None, ddof: int = 0
-) -> Union[float, np.ndarray]:
+def nanvar(a, axis=None, ddof=0):
     "Slow nanvar function used for unaccelerated dtypes."
     with warnings.catch_warnings():
         warnings.simplefilter("ignore")
         return np.nanvar(a, axis=axis, ddof=ddof)
 
 
-def nanstd(
-    a: np.ndarray, axis: Optional[int] = None, ddof: int = 0
-) -> Union[float, np.ndarray]:
+def nanstd(a, axis=None, ddof=0):
     "Slow nanstd function used for unaccelerated dtypes."
     with warnings.catch_warnings():
         warnings.simplefilter("ignore")
         return np.nanstd(a, axis=axis, ddof=ddof)
 
 
-def nanmin(a: np.ndarray, axis: Optional[int] = None) -> Union[int, float, np.ndarray]:
+def nanmin(a, axis=None):
     "Slow nanmin function used for unaccelerated dtypes."
     with warnings.catch_warnings():
         warnings.simplefilter("ignore")
         return np.nanmin(a, axis=axis)
 
 
-def nanmax(a: np.ndarray, axis: Optional[int] = None) -> Union[int, float, np.ndarray]:
+def nanmax(a, axis=None):
     "Slow nanmax function used for unaccelerated dtypes."
     with warnings.catch_warnings():
         warnings.simplefilter("ignore")
         return np.nanmax(a, axis=axis)
 
 
-def median(a: np.ndarray, axis: Optional[int] = None) -> Union[float, np.ndarray]:
+def median(a, axis=None):
     "Slow median function used for unaccelerated dtypes."
     with warnings.catch_warnings():
         warnings.simplefilter("ignore")
         return np.median(a, axis=axis)
 
 
-def nanmedian(a: np.ndarray, axis: Optional[int] = None) -> Union[float, np.ndarray]:
+def nanmedian(a, axis=None):
     "Slow nanmedian function used for unaccelerated dtypes."
     with warnings.catch_warnings():
         warnings.simplefilter("ignore")
         return np.nanmedian(a, axis=axis)
 
 
-def ss(a: np.ndarray, axis: Optional[int] = None) -> Union[int, float, np.ndarray]:
+def ss(a, axis=None):
     "Slow sum of squares used for unaccelerated dtypes."
     a = np.asarray(a)
     y = np.multiply(a, a).sum(axis)
     return y
 
 
-def anynan(a: np.ndarray, axis: Optional[int] = None) -> Union[bool, np.ndarray]:
+def anynan(a, axis=None):
     "Slow check for Nans used for unaccelerated dtypes."
     return np.isnan(a).any(axis)
 
 
-def allnan(a: np.ndarray, axis: Optional[int] = None) -> Union[bool, np.ndarray]:
+def allnan(a, axis=None):
     "Slow check for all Nans used for unaccelerated dtypes."
     return np.isnan(a).all(axis)
diff --git a/bottleneck/src/bn_config.py b/bottleneck/src/bn_config.py
index c2bbe2a109..e3c49a2f2d 100644
--- a/bottleneck/src/bn_config.py
+++ b/bottleneck/src/bn_config.py
@@ -2,40 +2,15 @@
 Unfortunately that file is not exposed, so re-implement the portions we need.
 """
 import os
-import sys
 import textwrap
-from distutils.command.config import config as Config
-from typing import List
 
 OPTIONAL_FUNCTION_ATTRIBUTES = [
     ("HAVE_ATTRIBUTE_OPTIMIZE_OPT_3", '__attribute__((optimize("O3")))')
 ]
 
-OPTIONAL_HEADERS = [("HAVE_SSE2", "emmintrin.h")]
 
-OPTIONAL_INTRINSICS = [
-    ("HAVE___BUILTIN_ISNAN", "__builtin_isnan", "0."),
-    ("HAVE_ISNAN", "isnan", "0."),
-    ("HAVE__ISNAN", "_isnan", "0."),
-]
-
-
-def get_python_header_include() -> List[str]:
-    if sys.platform == "win32":
-        suffix = ["include"]
-    else:
-        python_ver = "python{0.major}.{0.minor}".format(sys.version_info)
-        suffix = ["include", python_ver + sys.abiflags]
-
-    results = []
-    for prefix in [sys.prefix, sys.exec_prefix]:
-        results.append(os.path.join(prefix, *suffix))
-
-    return results
-
-
-def _get_compiler_list(cmd: Config) -> str:
-    """Return the compiler command as a list of strings. Distutils provides a
+def _get_compiler_list(cmd):
+    """ Return the compiler command as a list of strings. Distutils provides a
     wildly inconsistent API here:
       - UnixCCompiler returns a list
       - MSVCCompiler intentionally doesn't set this variable
@@ -50,16 +25,17 @@ def _get_compiler_list(cmd: Config) -> str:
     return compiler
 
 
-def is_gcc(cmd: Config) -> bool:
+def is_gcc(cmd):
     return any("gcc" in x for x in _get_compiler_list(cmd))
 
 
-def is_clang(cmd: Config) -> bool:
+def is_clang(cmd):
     return any("clang" in x for x in _get_compiler_list(cmd))
 
 
-def check_inline(cmd: Config) -> str:
+def check_inline(cmd):
     """Return the inline identifier (may be empty)."""
+    cmd._check_compiler()
     body = textwrap.dedent(
         """
         #ifndef __cplusplus
@@ -88,8 +64,9 @@ def check_inline(cmd: Config) -> str:
     return ""
 
 
-def check_gcc_function_attribute(cmd: Config, attribute: str, name: str) -> bool:
+def check_gcc_function_attribute(cmd, attribute, name):
     """Return True if the given function attribute is supported."""
+    cmd._check_compiler()
     if is_gcc(cmd):
         pragma = '#pragma GCC diagnostic error "-Wattributes"'
     elif is_clang(cmd):
@@ -112,45 +89,12 @@ def check_gcc_function_attribute(cmd: Config, attribute: str, name: str) -> bool
         )
         % (pragma, attribute, name)
     )
-    if cmd.try_compile(body, None, None):
-        return True
-    else:
-        return False
-
-
-def check_gcc_header(cmd: Config, header: str) -> bool:
-    return cmd.check_header(
-        header,
-        include_dirs=get_python_header_include(),
-    )
-
+    return cmd.try_compile(body, None, None) != 0
 
-def check_gcc_intrinsic(cmd: Config, intrinsic: str, value: str) -> bool:
-    """Return True if the given intrinsic is supported."""
-    body = (
-        textwrap.dedent(
-            """
-        int check(void) {
-            return %s(%s);
-        }
 
-        int main(void)
-        {
-            return check();
-        }
-        """
-        )
-        % (intrinsic, value)
-    )
-    if cmd.try_link(body, headers=["math.h"]):
-        return True
-    else:
-        return False
-
-
-def create_config_h(config: Config) -> None:
+def create_config_h(config):
     dirname = os.path.dirname(__file__)
-    config_h = os.path.join(dirname, "../include", "bn_config.h")
+    config_h = os.path.join(dirname, "bn_config.h")
 
     if (
         os.path.exists(config_h)
@@ -158,17 +102,6 @@ def create_config_h(config: Config) -> None:
     ):
         return
 
-    if not check_gcc_header(config, "Python.h"):
-        raise ValueError(
-            """Cannot compile a trivial program with Python.h! Please check the following:
- - A supported compiler is installed
- - Python development libraries are installed
-
- For detailed installation instructions, please see:
- https://bottleneck.readthedocs.io/en/latest/installing.html
-"""
-        )
-
     output = []
 
     for config_attr, func_attr in OPTIONAL_FUNCTION_ATTRIBUTES:
@@ -177,18 +110,6 @@ def create_config_h(config: Config) -> None:
         else:
             output.append((config_attr, "0"))
 
-    for config_attr, header in OPTIONAL_HEADERS:
-        if check_gcc_header(config, header):
-            output.append((config_attr, "1"))
-        else:
-            output.append((config_attr, "0"))
-
-    for config_attr, intrinsic, value in OPTIONAL_INTRINSICS:
-        if check_gcc_intrinsic(config, intrinsic, value):
-            output.append((config_attr, "1"))
-        else:
-            output.append((config_attr, "0"))
-
     inline_alias = check_inline(config)
 
     with open(config_h, "w") as f:
@@ -199,6 +120,3 @@ def create_config_h(config: Config) -> None:
             f.write("/* undef inline */\n")
         else:
             f.write("#define inline {}\n".format(inline_alias))
-
-        # ISO C requires every translation unit to have 1+ declarations
-        f.write("typedef int _make_iso_compilers_happy;\n")
diff --git a/bottleneck/src/bottleneck.h b/bottleneck/src/bottleneck.h
new file mode 100644
index 0000000000..869850ee2a
--- /dev/null
+++ b/bottleneck/src/bottleneck.h
@@ -0,0 +1,191 @@
+// Copyright 2010-2019 Keith Goodman
+// Copyright 2019 Bottleneck Developers
+#ifndef BOTTLENECK_H_
+#define BOTTLENECK_H_
+
+#include <Python.h>
+#define NPY_NO_DEPRECATED_API NPY_1_11_API_VERSION
+#include <numpy/arrayobject.h>
+#include <bn_config.h>
+
+/* THREADS=1 releases the GIL but increases function call
+ * overhead. THREADS=0 does not release the GIL but keeps
+ * function call overhead low. Curly brackets are for C89
+ * support. */
+#define THREADS 1
+#if THREADS
+    #define BN_BEGIN_ALLOW_THREADS Py_BEGIN_ALLOW_THREADS {
+    #define BN_END_ALLOW_THREADS   ;} Py_END_ALLOW_THREADS
+#else
+    #define BN_BEGIN_ALLOW_THREADS {
+    #define BN_END_ALLOW_THREADS }
+#endif
+
+/* for ease of dtype templating */
+#define NPY_float64 NPY_FLOAT64
+#define NPY_float32 NPY_FLOAT32
+#define NPY_int64   NPY_INT64
+#define NPY_int32   NPY_INT32
+#define NPY_intp    NPY_INTP
+#define NPY_MAX_int64 NPY_MAX_INT64
+#define NPY_MAX_int32 NPY_MAX_INT32
+#define NPY_MIN_int64 NPY_MIN_INT64
+#define NPY_MIN_int32 NPY_MIN_INT32
+
+#if PY_MAJOR_VERSION >= 3
+    #define PyString_FromString PyBytes_FromString
+    #define PyInt_AsLong PyLong_AsLong
+    #define PyString_InternFromString PyUnicode_InternFromString
+#endif
+
+#define VARKEY METH_VARARGS | METH_KEYWORDS
+#define error_converting(x) (((x) == -1) && PyErr_Occurred())
+
+#define VALUE_ERR(text)   PyErr_SetString(PyExc_ValueError, text)
+#define TYPE_ERR(text)    PyErr_SetString(PyExc_TypeError, text)
+#define MEMORY_ERR(text)  PyErr_SetString(PyExc_MemoryError, text)
+#define RUNTIME_ERR(text) PyErr_SetString(PyExc_RuntimeError, text)
+
+/* `inline` and `opt_3` copied from NumPy. */
+#if HAVE_ATTRIBUTE_OPTIMIZE_OPT_3
+    #define BN_OPT_3 __attribute__((optimize("O3")))
+#else
+    #define BN_OPT_3
+#endif
+
+/*
+ * NAN and INFINITY like macros (same behavior as glibc for NAN, same as C99
+ * for INFINITY). Copied from NumPy.
+ */
+static inline float __bn_inff(void)
+{
+    const union { npy_uint32 __i; float __f;} __bint = {0x7f800000UL};
+    return __bint.__f;
+}
+
+static inline float __bn_nanf(void)
+{
+    const union { npy_uint32 __i; float __f;} __bint = {0x7fc00000UL};
+    return __bint.__f;
+}
+
+#define BN_INFINITYF __bn_inff()
+#define BN_NANF      __bn_nanf()
+#define BN_INFINITY ((npy_double)BN_INFINITYF)
+#define BN_NAN      ((npy_double)BN_NANF)
+
+#define C_CONTIGUOUS(a) PyArray_CHKFLAGS(a, NPY_ARRAY_C_CONTIGUOUS)
+#define F_CONTIGUOUS(a) PyArray_CHKFLAGS(a, NPY_ARRAY_F_CONTIGUOUS)
+#define IS_CONTIGUOUS(a) (C_CONTIGUOUS(a) || F_CONTIGUOUS(a))
+
+/* WIRTH ----------------------------------------------------------------- */
+
+/*
+ WIRTH macro based on:
+   Fast median search: an ANSI C implementation
+   Nicolas Devillard - ndevilla AT free DOT fr
+   July 1998
+ which, in turn, took the algorithm from
+   Wirth, Niklaus
+   Algorithms + data structures = programs, p. 366
+   Englewood Cliffs: Prentice-Hall, 1976
+
+ Adapted for Bottleneck:
+ (C) 2016 Keith Goodman
+*/
+
+#define WIRTH(dtype) \
+    x = B(dtype, k); \
+    i = l; \
+    j = r; \
+    do { \
+        while (B(dtype, i) < x) i++; \
+        while (x < B(dtype, j)) j--; \
+        if (i <= j) { \
+            npy_##dtype atmp = B(dtype, i); \
+            B(dtype, i) = B(dtype, j); \
+            B(dtype, j) = atmp; \
+            i++; \
+            j--; \
+        } \
+    } while (i <= j); \
+    if (j < k) l = i; \
+    if (k < i) r = j;
+
+/* partition ------------------------------------------------------------- */
+
+#define PARTITION(dtype) \
+    while (l < r) { \
+        npy_##dtype x; \
+        npy_##dtype al = B(dtype, l); \
+        npy_##dtype ak = B(dtype, k); \
+        npy_##dtype ar = B(dtype, r); \
+        if (al > ak) { \
+            if (ak < ar) { \
+                if (al < ar) { \
+                    B(dtype, k) = al; \
+                    B(dtype, l) = ak; \
+                } else { \
+                    B(dtype, k) = ar; \
+                    B(dtype, r) = ak; \
+                } \
+            } \
+        } else { \
+            if (ak > ar) { \
+                if (al > ar) { \
+                    B(dtype, k) = al; \
+                    B(dtype, l) = ak; \
+                } else { \
+                    B(dtype, k) = ar; \
+                    B(dtype, r) = ak; \
+                } \
+            } \
+        } \
+        WIRTH(dtype) \
+    }
+
+/* slow ------------------------------------------------------------------ */
+
+static PyObject *slow_module = NULL;
+
+static PyObject *
+slow(char *name, PyObject *args, PyObject *kwds)
+{
+    PyObject *func = NULL;
+    PyObject *out = NULL;
+
+    if (slow_module == NULL) {
+        /* bottleneck.slow has not been imported during the current
+         * python session. Only import it once per session to save time */
+        slow_module = PyImport_ImportModule("bottleneck.slow");
+        if (slow_module == NULL) {
+            PyErr_SetString(PyExc_RuntimeError,
+                            "Cannot import bottleneck.slow");
+            return NULL;
+        }
+    }
+
+    func = PyObject_GetAttrString(slow_module, name);
+    if (func == NULL) {
+        PyErr_Format(PyExc_RuntimeError,
+                     "Cannot import %s from bottleneck.slow", name);
+        return NULL;
+    }
+    if (PyCallable_Check(func)) {
+        out = PyObject_Call(func, args, kwds);
+        if (out == NULL) {
+            Py_XDECREF(func);
+            return NULL;
+        }
+    } else {
+        Py_XDECREF(func);
+        PyErr_Format(PyExc_RuntimeError,
+                     "bottleneck.slow.%s is not callable", name);
+        return NULL;
+    }
+    Py_XDECREF(func);
+
+    return out;
+}
+
+#endif  // BOTTLENECK_H_
diff --git a/bottleneck/src/iterators.h b/bottleneck/src/iterators.h
new file mode 100644
index 0000000000..78fb6fe713
--- /dev/null
+++ b/bottleneck/src/iterators.h
@@ -0,0 +1,386 @@
+// Copyright 2010-2019 Keith Goodman
+// Copyright 2019 Bottleneck Developers
+#ifndef ITERATORS_H_
+#define ITERATORS_H_
+
+#include <numpy/arrayobject.h>
+
+/*
+   Bottleneck iterators are based on ideas from NumPy's PyArray_IterAllButAxis
+   and PyArray_ITER_NEXT.
+*/
+
+/* one input array ------------------------------------------------------- */
+
+/* these iterators are used mainly by reduce functions such as nansum */
+
+struct _iter {
+    int        ndim_m2; /* ndim - 2 */
+    int        axis;    /* axis to not iterate over */
+    Py_ssize_t length;  /* a.shape[axis] */
+    Py_ssize_t astride; /* a.strides[axis] */
+    npy_intp   stride;  /* element-level stride to take in the array */
+    npy_intp   i;       /* integer used by some macros */
+    npy_intp   its;     /* number of iterations completed */
+    npy_intp   nits;    /* number of iterations iterator plans to make */
+    npy_intp   indices[NPY_MAXDIMS];  /* current location of iterator */
+    npy_intp   astrides[NPY_MAXDIMS]; /* a.strides, a.strides[axis] removed */
+    npy_intp   shape[NPY_MAXDIMS];    /* a.shape, a.shape[axis] removed */
+    char       *pa;     /* pointer to data corresponding to indices */
+    PyArrayObject *a_ravel; /* NULL or pointer to ravelled input array */
+};
+typedef struct _iter iter;
+
+static inline void
+init_iter_one(iter *it, PyArrayObject *a, int axis)
+{
+    int i, j = 0;
+    const int ndim = PyArray_NDIM(a);
+    const npy_intp *shape = PyArray_SHAPE(a);
+    const npy_intp *strides = PyArray_STRIDES(a);
+    const npy_intp item_size = PyArray_ITEMSIZE(a);
+
+    it->axis = axis;
+    it->its = 0;
+    it->nits = 1;
+    it->pa = PyArray_BYTES(a);
+
+    it->ndim_m2 = -1;
+    it->length = 1;
+    it->astride = 0;
+
+    if (ndim != 0) {
+        it->ndim_m2 = ndim - 2;
+        for (i = 0; i < ndim; i++) {
+            if (i == axis) {
+                it->astride = strides[i];
+                it->length = shape[i];
+            } else {
+                it->indices[j] = 0;
+                it->astrides[j] = strides[i];
+                it->shape[j] = shape[i];
+                it->nits *= shape[i];
+                j++;
+            }
+        }
+    }
+    it->stride = it->astride / item_size;
+}
+
+/*
+ * If both ravel != 0 and it.a_ravel != NULL then you are responsible for
+ * calling Py_DECREF(it.a_ravel) after you are done with the iterator.
+ * See nanargmin for an example.
+ */
+static inline void
+init_iter_all(iter *it, PyArrayObject *a, int ravel, int anyorder)
+{
+    int i, j = 0;
+    const int ndim = PyArray_NDIM(a);
+    const npy_intp *shape = PyArray_SHAPE(a);
+    const npy_intp *strides = PyArray_STRIDES(a);
+    const npy_intp item_size = PyArray_ITEMSIZE(a);
+
+    it->axis = 0;
+    it->its = 0;
+    it->nits = 1;
+    it->a_ravel = NULL;
+
+    /* The fix for relaxed strides checking in numpy and the fix for
+     * issue #183 has left this if..else tree in need of a refactor from the
+     * the ground up */
+    if (ndim == 1) {
+        it->ndim_m2 = -1;
+        it->length = shape[0];
+        it->astride = strides[0];
+    } else if (ndim == 0) {
+        it->ndim_m2 = -1;
+        it->length = 1;
+        it->astride = 0;
+    } else if (C_CONTIGUOUS(a) && !F_CONTIGUOUS(a)) {
+        /* The &&! in the next two else ifs is to deal with relaxed
+         * stride checking introduced in numpy 1.12.0; see gh #161 */
+        it->ndim_m2 = -1;
+        it->axis = ndim - 1;
+        it->length = PyArray_SIZE(a);
+        it->astride = 0;
+        for (i=ndim-1; i > -1; i--) {
+            /* protect against length zero  strides such as in
+             * np.ones((2, 2))[..., np.newaxis] */
+            if (strides[i] == 0) {
+                continue;
+            }
+            it->astride = strides[i];
+            break;
+       }
+    } else if (F_CONTIGUOUS(a) && !C_CONTIGUOUS(a)) {
+        if (anyorder || !ravel) {
+            it->ndim_m2 = -1;
+            it->length = PyArray_SIZE(a);
+            it->astride = 0;
+            for (i=0; i < ndim; i++) {
+                /* protect against length zero  strides such as in
+                 * np.ones((2, 2), order='F')[np.newaxis, ...] */
+                if (strides[i] == 0) {
+                    continue;
+                }
+                it->astride = strides[i];
+                break;
+           }
+        } else {
+            it->ndim_m2 = -1;
+            if (anyorder) {
+                a = (PyArrayObject *)PyArray_Ravel(a, NPY_ANYORDER);
+            } else {
+                a = (PyArrayObject *)PyArray_Ravel(a, NPY_CORDER);
+            }
+            it->a_ravel = a;
+            it->length = PyArray_DIM(a, 0);
+            it->astride = PyArray_STRIDE(a, 0);
+        }
+    } else if (ravel) {
+        it->ndim_m2 = -1;
+        if (anyorder) {
+            a = (PyArrayObject *)PyArray_Ravel(a, NPY_ANYORDER);
+        } else {
+            a = (PyArrayObject *)PyArray_Ravel(a, NPY_CORDER);
+        }
+        it->a_ravel = a;
+        it->length = PyArray_DIM(a, 0);
+        it->astride = PyArray_STRIDE(a, 0);
+    } else {
+        it->ndim_m2 = ndim - 2;
+        it->astride = strides[0];
+        for (i = 1; i < ndim; i++) {
+            if (strides[i] < it->astride) {
+                it->astride = strides[i];
+                it->axis = i;
+            }
+        }
+        it->length = shape[it->axis];
+        for (i = 0; i < ndim; i++) {
+            if (i != it->axis) {
+                it->indices[j] = 0;
+                it->astrides[j] = strides[i];
+                it->shape[j] = shape[i];
+                it->nits *= shape[i];
+                j++;
+            }
+        }
+    }
+
+    it->stride = it->astride / item_size;
+    it->pa = PyArray_BYTES(a);
+}
+
+#define NEXT \
+    for (it.i = it.ndim_m2; it.i > -1; it.i--) { \
+        if (it.indices[it.i] < it.shape[it.i] - 1) { \
+            it.pa += it.astrides[it.i]; \
+            it.indices[it.i]++; \
+            break; \
+        } \
+        it.pa -= it.indices[it.i] * it.astrides[it.i]; \
+        it.indices[it.i] = 0; \
+    } \
+    it.its++;
+
+/* two input arrays ------------------------------------------------------ */
+
+/* this iterator is used mainly by moving window functions such as move_sum */
+
+struct _iter2 {
+    int        ndim_m2;
+    int        axis;
+    Py_ssize_t length;
+    Py_ssize_t astride;
+    Py_ssize_t ystride;
+    npy_intp   i;
+    npy_intp   its;
+    npy_intp   nits;
+    npy_intp   indices[NPY_MAXDIMS];
+    npy_intp   astrides[NPY_MAXDIMS];
+    npy_intp   ystrides[NPY_MAXDIMS];
+    npy_intp   shape[NPY_MAXDIMS];
+    char       *pa;
+    char       *py;
+};
+typedef struct _iter2 iter2;
+
+static inline void
+init_iter2(iter2 *it, PyArrayObject *a, PyObject *y, int axis)
+{
+    int i, j = 0;
+    const int ndim = PyArray_NDIM(a);
+    const npy_intp *shape = PyArray_SHAPE(a);
+    const npy_intp *astrides = PyArray_STRIDES(a);
+    const npy_intp *ystrides = PyArray_STRIDES((PyArrayObject *)y);
+
+    /* to avoid compiler warning of uninitialized variables */
+    it->length = 0;
+    it->astride = 0;
+    it->ystride = 0;
+
+    it->ndim_m2 = ndim - 2;
+    it->axis = axis;
+    it->its = 0;
+    it->nits = 1;
+    it->pa = PyArray_BYTES(a);
+    it->py = PyArray_BYTES((PyArrayObject *)y);
+
+    for (i = 0; i < ndim; i++) {
+        if (i == axis) {
+            it->astride = astrides[i];
+            it->ystride = ystrides[i];
+            it->length = shape[i];
+        } else {
+            it->indices[j] = 0;
+            it->astrides[j] = astrides[i];
+            it->ystrides[j] = ystrides[i];
+            it->shape[j] = shape[i];
+            it->nits *= shape[i];
+            j++;
+        }
+    }
+}
+
+#define NEXT2 \
+    for (it.i = it.ndim_m2; it.i > -1; it.i--) { \
+        if (it.indices[it.i] < it.shape[it.i] - 1) { \
+            it.pa += it.astrides[it.i]; \
+            it.py += it.ystrides[it.i]; \
+            it.indices[it.i]++; \
+            break; \
+        } \
+        it.pa -= it.indices[it.i] * it.astrides[it.i]; \
+        it.py -= it.indices[it.i] * it.ystrides[it.i]; \
+        it.indices[it.i] = 0; \
+    } \
+    it.its++;
+
+/* three input arrays ---------------------------------------------------- */
+
+/* this iterator is used mainly by rankdata and nanrankdata */
+
+struct _iter3 {
+    int        ndim_m2;
+    int        axis;
+    Py_ssize_t length;
+    Py_ssize_t astride;
+    Py_ssize_t ystride;
+    Py_ssize_t zstride;
+    npy_intp   i;
+    npy_intp   its;
+    npy_intp   nits;
+    npy_intp   indices[NPY_MAXDIMS];
+    npy_intp   astrides[NPY_MAXDIMS];
+    npy_intp   ystrides[NPY_MAXDIMS];
+    npy_intp   zstrides[NPY_MAXDIMS];
+    npy_intp   shape[NPY_MAXDIMS];
+    char       *pa;
+    char       *py;
+    char       *pz;
+};
+typedef struct _iter3 iter3;
+
+static inline void
+init_iter3(iter3 *it, PyArrayObject *a, PyObject *y, PyObject *z, int axis)
+{
+    int i, j = 0;
+    const int ndim = PyArray_NDIM(a);
+    const npy_intp *shape = PyArray_SHAPE(a);
+    const npy_intp *astrides = PyArray_STRIDES(a);
+    const npy_intp *ystrides = PyArray_STRIDES((PyArrayObject *)y);
+    const npy_intp *zstrides = PyArray_STRIDES((PyArrayObject *)z);
+
+    /* to avoid compiler warning of uninitialized variables */
+    it->length = 0;
+    it->astride = 0;
+    it->ystride = 0;
+    it->zstride = 0;
+
+    it->ndim_m2 = ndim - 2;
+    it->axis = axis;
+    it->its = 0;
+    it->nits = 1;
+    it->pa = PyArray_BYTES(a);
+    it->py = PyArray_BYTES((PyArrayObject *)y);
+    it->pz = PyArray_BYTES((PyArrayObject *)z);
+
+    for (i = 0; i < ndim; i++) {
+        if (i == axis) {
+            it->astride = astrides[i];
+            it->ystride = ystrides[i];
+            it->zstride = zstrides[i];
+            it->length = shape[i];
+        } else {
+            it->indices[j] = 0;
+            it->astrides[j] = astrides[i];
+            it->ystrides[j] = ystrides[i];
+            it->zstrides[j] = zstrides[i];
+            it->shape[j] = shape[i];
+            it->nits *= shape[i];
+            j++;
+        }
+    }
+}
+
+#define NEXT3 \
+    for (it.i = it.ndim_m2; it.i > -1; it.i--) { \
+        if (it.indices[it.i] < it.shape[it.i] - 1) { \
+            it.pa += it.astrides[it.i]; \
+            it.py += it.ystrides[it.i]; \
+            it.pz += it.zstrides[it.i]; \
+            it.indices[it.i]++; \
+            break; \
+        } \
+        it.pa -= it.indices[it.i] * it.astrides[it.i]; \
+        it.py -= it.indices[it.i] * it.ystrides[it.i]; \
+        it.pz -= it.indices[it.i] * it.zstrides[it.i]; \
+        it.indices[it.i] = 0; \
+    } \
+    it.its++;
+
+/* macros used with iterators -------------------------------------------- */
+
+/* most of these macros assume iterator is named `it` */
+
+#define  NDIM           it.ndim_m2 + 2
+#define  SHAPE          it.shape
+#define  SIZE           it.nits * it.length
+#define  LENGTH         it.length
+#define  INDEX          it.i
+
+#define  WHILE          while (it.its < it.nits)
+#define  WHILE0         it.i = 0; while (it.i < min_count - 1)
+#define  WHILE1         while (it.i < window)
+#define  WHILE2         while (it.i < it.length)
+
+#define  FOR            for (it.i = 0; it.i < it.length; it.i++)
+#define  FOR_REVERSE    for (it.i = it.length - 1; it.i > -1; it.i--)
+
+#define  RESET          it.its = 0;
+
+#define  PA(dtype)      (npy_##dtype *)(it.pa)
+
+#define  A0(dtype)      *(npy_##dtype *)(it.pa)
+#define  AI(dtype)      *(npy_##dtype *)(it.pa + it.i * it.astride)
+#define  AX(dtype, x)   *(npy_##dtype *)(it.pa + (x) * it.astride)
+#define  AOLD(dtype)    *(npy_##dtype *)(it.pa + (it.i - window) * it.astride)
+
+#define  SI(pa)         pa[it.i * it.stride]    
+
+#define  YPP            *py++
+#define  YI(dtype)      *(npy_##dtype *)(it.py + it.i++ * it.ystride)
+#define  YX(dtype, x)   *(npy_##dtype *)(it.py + (x) * it.ystride)
+
+#define  ZX(dtype, x)   *(npy_##dtype *)(it.pz + (x) * it.zstride)
+
+#define FILL_Y(value) \
+    npy_intp _i; \
+    npy_intp size = PyArray_SIZE((PyArrayObject *)y); \
+    for (_i = 0; _i < size; _i++) { \
+        YPP = value; \
+    }
+
+#endif  // ITERATORS_H_
diff --git a/bottleneck/src/move_median/move_median.c b/bottleneck/src/move_median/move_median.c
index 46ba25567b..3f0709abe4 100644
--- a/bottleneck/src/move_median/move_median.c
+++ b/bottleneck/src/move_median/move_median.c
@@ -489,6 +489,7 @@ mm_get_median(mm_handle *mm) {
 
 static inline void
 heapify_small_node(mm_handle *mm, idx_t idx) {
+    idx_t idx2;
     mm_node *node;
     mm_node *node2;
     mm_node **s_heap;
@@ -505,7 +506,7 @@ heapify_small_node(mm_handle *mm, idx_t idx) {
 
     /* Internal or leaf node */
     if (idx > 0) {
-        idx_t idx2 = P_IDX(idx);
+        idx2 = P_IDX(idx);
         node2 = s_heap[idx2];
 
         /* Move up */
@@ -577,6 +578,7 @@ heapify_large_node(mm_handle *mm, idx_t idx) {
             mm_move_up_large(l_heap, n_l, idx, node);
         }
     }
+
 }
 
 
diff --git a/bottleneck/src/move_median/move_median.h b/bottleneck/src/move_median/move_median.h
index 297cddc2ab..ccc3176096 100644
--- a/bottleneck/src/move_median/move_median.h
+++ b/bottleneck/src/move_median/move_median.h
@@ -9,7 +9,7 @@
 typedef size_t idx_t;
 typedef double ai_t;
 
-#if BINARY_TREE == 1
+#if BINARY_TREE==1
     #define NUM_CHILDREN 2
 #else
     /* maximum of 8 due to the manual loop-unrolling used in the code */
diff --git a/bottleneck/src/move_median/move_median_debug.c b/bottleneck/src/move_median/move_median_debug.c
index a1ff018f82..13871d2e6e 100644
--- a/bottleneck/src/move_median/move_median_debug.c
+++ b/bottleneck/src/move_median/move_median_debug.c
@@ -12,288 +12,308 @@ void mm_print_chain(mm_handle *mm);
 void mm_print_line(void);
 void mm_print_node(mm_node *node);
 
-int main(void) { return mm_unit_test(); }
+
+int main(void) {
+    return mm_unit_test();
+}
+
 
 /* moving window median of 1d arrays returns output array */
 ai_t *mm_move_median(ai_t *a, idx_t length, idx_t window, idx_t min_count) {
-  mm_handle *mm;
-  ai_t *out;
-  idx_t i;
-
-  out = malloc(length * sizeof(ai_t));
-  mm = mm_new_nan(window, min_count);
-  for (i = 0; i < length; i++) {
-    if (i < window) {
-      out[i] = mm_update_init_nan(mm, a[i]);
-    } else {
-      out[i] = mm_update_nan(mm, a[i]);
-    }
-    if (i == window) {
-      mm_print_line();
-      printf("window complete; switch to mm_update\n");
+    mm_handle *mm;
+    ai_t *out;
+    idx_t i;
+
+    out = malloc(length * sizeof(ai_t));
+    mm = mm_new_nan(window, min_count);
+    for (i=0; i < length; i++) {
+        if (i < window) {
+            out[i] = mm_update_init_nan(mm, a[i]);
+        } else {
+            out[i] = mm_update_nan(mm, a[i]);
+        }
+        if (i == window) {
+            mm_print_line();
+            printf("window complete; switch to mm_update\n");
+        }
+        mm_print_line();
+        printf("inserting ai = %f\n", a[i]);
+        mm_print_chain(mm);
+        mm_dump(mm);
+        printf("\nmedian = %f\n\n", out[i]);
+        mm_check(mm);
     }
-    mm_print_line();
-    printf("inserting ai = %f\n", a[i]);
-    mm_print_chain(mm);
-    mm_dump(mm);
-    printf("\nmedian = %f\n\n", out[i]);
-    mm_check(mm);
-  }
-  mm_free(mm);
-
-  return out;
+    mm_free(mm);
+
+    return out;
 }
 
+
 /* assert that two arrays are equal */
 int mm_assert_equal(ai_t *actual, ai_t *desired, ai_t *input, idx_t length,
-                    char *err_msg) {
-  idx_t i;
-  int failed = 0;
-
-  mm_print_line();
-  printf("%s\n", err_msg);
-  mm_print_line();
-  printf("input     actual    desired\n");
-  for (i = 0; i < length; i++) {
-    if (isnan(actual[i]) && isnan(desired[i])) {
-      printf("%9f %9f %9f\n", input[i], actual[i], desired[i]);
-    } else if (actual[i] != desired[i]) {
-      failed = 1;
-      printf("%9f %9f %9f BUG\n", input[i], actual[i], desired[i]);
-    } else {
-      printf("%9f %9f %9f\n", input[i], actual[i], desired[i]);
+                 char *err_msg) {
+    idx_t i;
+    int failed = 0;
+
+    mm_print_line();
+    printf("%s\n", err_msg);
+    mm_print_line();
+    printf("input     actual    desired\n");
+    for (i=0; i < length; i++)
+    {
+        if (isnan(actual[i]) && isnan(desired[i])) {
+            printf("%9f %9f %9f\n", input[i], actual[i], desired[i]);
+        } else if (actual[i] != desired[i]) {
+            failed = 1;
+            printf("%9f %9f %9f BUG\n", input[i], actual[i], desired[i]);
+        } else {
+            printf("%9f %9f %9f\n", input[i], actual[i], desired[i]);
+        }
     }
-  }
 
-  return failed;
+    return failed;
 }
 
+
 int mm_unit_test(void) {
-  ai_t arr_input[] = {0, 3, 7, NAN, 1, 5, 8, 9, 2, NAN};
-  ai_t desired[] = {0, 1.5, 3, 5, 4, 3, 5, 8, 8, 5.5};
-  ai_t *actual;
-  int window = 3;
-  int min_count = 1;
-  int length;
-  char *err_msg;
-  int failed;
-
-  length = sizeof(arr_input) / sizeof(*arr_input);
-  err_msg = malloc(1024 * sizeof *err_msg);
-  sprintf(err_msg, "move_median failed with window=%d, min_count=%d", window,
-          min_count);
-
-  actual = mm_move_median(arr_input, length, window, min_count);
-  failed = mm_assert_equal(actual, desired, arr_input, length, err_msg);
-
-  free(actual);
-  free(err_msg);
-
-  return failed;
+    ai_t arr_input[] = {0,   3,   7,  NAN, 1,   5,   8,   9,   2,  NAN};
+    ai_t desired[] =   {0 ,  1.5, 3,  5,   4,   3,   5,   8,   8,  5.5};
+    ai_t *actual;
+    int window = 3;
+    int min_count = 1;
+    int length;
+    char *err_msg;
+    int failed;
+
+    length = sizeof(arr_input) / sizeof(*arr_input);
+    err_msg = malloc(1024 * sizeof *err_msg);
+    sprintf(err_msg, "move_median failed with window=%d, min_count=%d",
+            window, min_count);
+
+    actual = mm_move_median(arr_input, length, window, min_count);
+    failed = mm_assert_equal(actual, desired, arr_input, length, err_msg);
+
+    free(actual);
+    free(err_msg);
+
+    return failed;
 }
 
+
 void mm_print_node(mm_node *node) {
-  printf("\n\n%d small\n", node->region);
-  printf("%lu idx\n", node->idx);
-  printf("%f ai\n", node->ai);
-  printf("%p next\n\n", node->next);
+    printf("\n\n%d small\n", node->region);
+    printf("%d idx\n", node->idx);
+    printf("%f ai\n", node->ai);
+    printf("%p next\n\n", node->next);
 }
 
+
 void mm_print_chain(mm_handle *mm) {
-  idx_t i;
-  mm_node *node;
-
-  printf("\nchain\n");
-  node = mm->oldest;
-  printf("\t%6.2f region %d idx %lu addr %p\n", node->ai, node->region,
-         node->idx, node);
-  for (i = 1; i < mm->n_s + mm->n_l + mm->n_n; i++) {
-    node = node->next;
-    printf("\t%6.2f region %d idx %lu addr %p\n", node->ai, node->region,
+    idx_t i;
+    mm_node *node;
+
+    printf("\nchain\n");
+    node = mm->oldest;
+    printf("\t%6.2f region %d idx %d addr %p\n", node->ai, node->region,
            node->idx, node);
-  }
+    for (i=1; i < mm->n_s + mm->n_l + mm->n_n; i++) {
+        node = node->next;
+        printf("\t%6.2f region %d idx %d addr %p\n", node->ai, node->region,
+               node->idx, node);
+    }
 }
 
+
 void mm_check(mm_handle *mm) {
-  int ndiff;
-  idx_t i;
-  mm_node *child;
-  mm_node *parent;
-
-  // small heap
-  for (i = 0; i < mm->n_s; i++) {
-    assert(mm->s_heap[i]->idx == i);
-    assert(mm->s_heap[i]->ai == mm->s_heap[i]->ai);
-    if (i > 0) {
-      child = mm->s_heap[i];
-      parent = mm->s_heap[P_IDX(child->idx)];
-      assert(child->ai <= parent->ai);
-    }
-  }
-
-  // large heap
-  for (i = 0; i < mm->n_l; i++) {
-    assert(mm->l_heap[i]->idx == i);
-    assert(mm->l_heap[i]->ai == mm->l_heap[i]->ai);
-    if (i > 0) {
-      child = mm->l_heap[i];
-      parent = mm->l_heap[P_IDX(child->idx)];
-      assert(child->ai >= parent->ai);
-    }
-  }
-
-  // nan array
-  for (i = 0; i < mm->n_n; i++) {
-    assert(mm->n_array[i]->idx == i);
-    assert(mm->n_array[i]->ai != mm->n_array[i]->ai);
-  }
-
-  // handle
-  assert(mm->window >= mm->n_s + mm->n_l + mm->n_n);
-  assert(mm->min_count <= mm->window);
-  if (mm->n_s == 0) {
-    assert(mm->s_first_leaf == 0);
-  } else {
-    assert(mm->s_first_leaf == FIRST_LEAF(mm->n_s));
-  }
-  if (mm->n_l == 0) {
-    assert(mm->l_first_leaf == 0);
-  } else {
-    assert(mm->l_first_leaf == FIRST_LEAF(mm->n_l));
-  }
-  ndiff = (int)mm->n_s - (int)mm->n_l;
-  if (ndiff < 0) {
-    ndiff *= -1;
-  }
-  assert(ndiff <= 1);
-
-  if (mm->n_s > 0 && mm->n_l > 0) {
-    assert(mm->s_heap[0]->ai <= mm->l_heap[0]->ai);
-  }
-}
 
-/* Print the two heaps to the screen */
-void mm_dump(mm_handle *mm) {
-  int i;
-
-  if (!mm) {
-    printf("mm is empty");
-    return;
-  }
-
-  printf("\nhandle\n");
-  printf("\t%lu window\n", mm->window);
-  printf("\t%lu n_s\n", mm->n_s);
-  printf("\t%lu n_l\n", mm->n_l);
-  printf("\t%lu n_n\n", mm->n_n);
-  printf("\t%lu min_count\n", mm->min_count);
-  printf("\t%lu s_first_leaf\n", mm->s_first_leaf);
-  printf("\t%lu l_first_leaf\n", mm->l_first_leaf);
-
-  if (NUM_CHILDREN == 2) {
-    // binary heap
-    int idx0;
-    int idx1;
-
-    printf("\nsmall heap\n");
-    idx0 = -1;
-    if (mm->oldest->region == SH) {
-      idx0 = mm->oldest->idx;
+    int ndiff;
+    idx_t i;
+    mm_node *child;
+    mm_node *parent;
+
+    // small heap
+    for (i=0; i<mm->n_s; i++) {
+        assert(mm->s_heap[i]->idx == i);
+        assert(mm->s_heap[i]->ai == mm->s_heap[i]->ai);
+        if (i > 0) {
+            child = mm->s_heap[i];
+            parent = mm->s_heap[P_IDX(child->idx)];
+            assert(child->ai <= parent->ai);
+        }
     }
-    idx1 = -1;
-    if (mm->newest->region == SH) {
-      idx1 = mm->newest->idx;
-    }
-    mm_print_binary_heap(mm->s_heap, mm->n_s, idx0, idx1);
-    printf("\nlarge heap\n");
-    idx0 = -1;
-    if (mm->oldest->region == LH) {
-      idx0 = mm->oldest->idx;
+
+    // large heap
+    for (i=0; i<mm->n_l; i++) {
+        assert(mm->l_heap[i]->idx == i);
+        assert(mm->l_heap[i]->ai == mm->l_heap[i]->ai);
+        if (i > 0) {
+            child = mm->l_heap[i];
+            parent = mm->l_heap[P_IDX(child->idx)];
+            assert(child->ai >= parent->ai);
+        }
     }
-    idx1 = -1;
-    if (mm->newest->region == LH) {
-      idx1 = mm->newest->idx;
+
+    // nan array
+    for (i=0; i<mm->n_n; i++) {
+         assert(mm->n_array[i]->idx == i);
+         assert(mm->n_array[i]->ai != mm->n_array[i]->ai);
     }
-    mm_print_binary_heap(mm->l_heap, mm->n_l, idx0, idx1);
-    printf("\nnan array\n");
-    idx0 = -1;
-    if (mm->oldest->region == NA) {
-      idx0 = mm->oldest->idx;
+
+    // handle
+    assert(mm->window >= mm->n_s + mm->n_l + mm->n_n);
+    assert(mm->min_count <= mm->window);
+    if (mm->n_s == 0) {
+        assert(mm->s_first_leaf == 0);
+    } else {
+        assert(mm->s_first_leaf == FIRST_LEAF(mm->n_s));
     }
-    idx1 = -1;
-    if (mm->newest->region == NA) {
-      idx1 = mm->newest->idx;
+    if (mm->n_l == 0) {
+        assert(mm->l_first_leaf == 0);
+    } else {
+        assert(mm->l_first_leaf == FIRST_LEAF(mm->n_l));
     }
-    for (i = 0; i < (int)mm->n_n; ++i) {
-      idx_t idx = mm->n_array[i]->idx;
-      if (i == idx0 && i == idx1) {
-        printf("\t%lu >%f<\n", idx, mm->n_array[i]->ai);
-      } else if (i == idx0) {
-        printf("\t%lu >%f\n", idx, mm->n_array[i]->ai);
-      } else if (i == idx1) {
-        printf("\t%lu  %f<\n", idx, mm->n_array[i]->ai);
-      } else {
-        printf("\t%lu  %f\n", idx, mm->n_array[i]->ai);
-      }
+    ndiff = (int)mm->n_s - (int)mm->n_l;
+    if (ndiff < 0) {
+        ndiff *= -1;
     }
-  } else {
-    // not a binary heap
-    if (mm->oldest)
-      printf("\n\nFirst: %f\n", (double)mm->oldest->ai);
-    if (mm->newest)
-      printf("Last: %f\n", (double)mm->newest->ai);
-
-    printf("\n\nSmall heap:\n");
-    for (i = 0; i < (int)mm->n_s; ++i) {
-      printf("%i %f\n", (int)mm->s_heap[i]->idx, mm->s_heap[i]->ai);
+    assert(ndiff <= 1);
+
+    if (mm->n_s > 0 && mm->n_l > 0) {
+        assert(mm->s_heap[0]->ai <= mm->l_heap[0]->ai);
     }
-    printf("\n\nLarge heap:\n");
-    for (i = 0; i < (int)mm->n_l; ++i) {
-      printf("%i %f\n", (int)mm->l_heap[i]->idx, mm->l_heap[i]->ai);
+}
+
+
+/* Print the two heaps to the screen */
+void mm_dump(mm_handle *mm) {
+    int i;
+    idx_t idx;
+
+    if (!mm) {
+        printf("mm is empty");
+        return;
     }
-    printf("\n\nNaN heap:\n");
-    for (i = 0; i < (int)mm->n_n; ++i) {
-      printf("%i %f\n", (int)mm->n_array[i]->idx, mm->n_array[i]->ai);
+
+    printf("\nhandle\n");
+    printf("\t%2d window\n", mm->window);
+    printf("\t%2d n_s\n", mm->n_s);
+    printf("\t%2d n_l\n", mm->n_l);
+    printf("\t%2d n_n\n", mm->n_n);
+    printf("\t%2d min_count\n", mm->min_count);
+    printf("\t%2d s_first_leaf\n", mm->s_first_leaf);
+    printf("\t%2d l_first_leaf\n", mm->l_first_leaf);
+
+    if (NUM_CHILDREN == 2) {
+
+        // binary heap
+
+        int idx0;
+        int idx1;
+
+        printf("\nsmall heap\n");
+        idx0 = -1;
+        if (mm->oldest->region == SH) {
+            idx0 = mm->oldest->idx;
+        }
+        idx1 = -1;
+        if (mm->newest->region == SH) {
+            idx1 = mm->newest->idx;
+        }
+        mm_print_binary_heap(mm->s_heap, mm->n_s, idx0, idx1);
+        printf("\nlarge heap\n");
+        idx0 = -1;
+        if (mm->oldest->region == LH) {
+            idx0 = mm->oldest->idx;
+        }
+        idx1 = -1;
+        if (mm->newest->region == LH) {
+            idx1 = mm->newest->idx;
+        }
+        mm_print_binary_heap(mm->l_heap, mm->n_l, idx0, idx1);
+        printf("\nnan array\n");
+        idx0 = -1;
+        if (mm->oldest->region == NA) {
+            idx0 = mm->oldest->idx;
+        }
+        idx1 = -1;
+        if (mm->newest->region == NA) {
+            idx1 = mm->newest->idx;
+        }
+        for (i = 0; i < (int)mm->n_n; ++i) {
+            idx = mm->n_array[i]->idx;
+            if (i == idx0 && i == idx1) {
+                printf("\t%i >%f<\n", idx, mm->n_array[i]->ai);
+            } else if (i == idx0) {
+                printf("\t%i >%f\n", idx, mm->n_array[i]->ai);
+            } else if (i == idx1) {
+                printf("\t%i  %f<\n", idx, mm->n_array[i]->ai);
+            } else {
+                printf("\t%i  %f\n", idx, mm->n_array[i]->ai);
+            }
+        }
+
+    } else {
+
+        // not a binary heap
+
+        if (mm->oldest)
+            printf("\n\nFirst: %f\n", (double)mm->oldest->ai);
+        if (mm->newest)
+            printf("Last: %f\n", (double)mm->newest->ai);
+
+        printf("\n\nSmall heap:\n");
+        for (i = 0; i < (int)mm->n_s; ++i) {
+            printf("%i %f\n", (int)mm->s_heap[i]->idx, mm->s_heap[i]->ai);
+        }
+        printf("\n\nLarge heap:\n");
+        for (i = 0; i < (int)mm->n_l; ++i) {
+            printf("%i %f\n", (int)mm->l_heap[i]->idx, mm->l_heap[i]->ai);
+        }
+        printf("\n\nNaN heap:\n");
+        for (i = 0; i < (int)mm->n_n; ++i) {
+            printf("%i %f\n", (int)mm->n_array[i]->idx, mm->n_array[i]->ai);
+        }
     }
-  }
 }
 
+
 /* Code to print a binary tree from http://stackoverflow.com/a/13755783
  * Code modified for bottleneck's needs. */
-void mm_print_binary_heap(mm_node **heap, idx_t n_array, idx_t oldest_idx,
-                          idx_t newest_idx) {
-  const int line_width = 77;
-  int print_pos[n_array];
-  int i, j, k, pos, x = 1, level = 0;
-
-  print_pos[0] = 0;
-  for (i = 0, j = 1; i < (int)n_array; i++, j++) {
-    pos = print_pos[(i - 1) / 2];
-    pos += ((i % 2) ? -1 : 1) * (line_width / (pow(2, level + 1)) + 1);
-
-    for (k = 0; k < pos - x; k++)
-      printf("%c", i == 0 || i % 2 ? ' ' : '-');
-    if (i == (int)oldest_idx) {
-      printf(">%.2f", heap[i]->ai);
-    } else if (i == (int)newest_idx) {
-      printf("%.2f<", heap[i]->ai);
-    } else {
-      printf("%.2f", heap[i]->ai);
+void
+mm_print_binary_heap(mm_node **heap, idx_t n_array, idx_t oldest_idx,
+                     idx_t newest_idx) {
+    const int line_width = 77;
+    int print_pos[n_array];
+    int i, j, k, pos, x=1, level=0;
+
+    print_pos[0] = 0;
+    for (i=0,j=1; i<(int)n_array; i++,j++) {
+        pos = print_pos[(i-1)/2];
+        pos +=  (i%2?-1:1)*(line_width/(pow(2,level+1))+1);
+
+        for (k=0; k<pos-x; k++) printf("%c",i==0||i%2?' ':'-');
+        if (i == (int)oldest_idx) {
+            printf(">%.2f", heap[i]->ai);
+        } else if (i == (int)newest_idx) {
+            printf("%.2f<", heap[i]->ai);
+        } else {
+            printf("%.2f", heap[i]->ai);
+        }
+
+        print_pos[i] = x = pos+1;
+        if (j==pow(2,level)) {
+            printf("\n");
+            level++;
+            x = 1;
+            j = 0;
+        }
     }
-
-    print_pos[i] = x = pos + 1;
-    if (j == pow(2, level)) {
-      printf("\n");
-      level++;
-      x = 1;
-      j = 0;
-    }
-  }
 }
 
+
 void mm_print_line(void) {
-  int i, width = 70;
-  for (i = 0; i < width; i++)
-    printf("-");
-  printf("\n");
+    int i, width = 70;
+    for (i=0; i < width; i++)
+        printf("-");
+    printf("\n");
 }
diff --git a/bottleneck/src/move_template.c b/bottleneck/src/move_template.c
index cb4caf9ce6..f7e1e45157 100644
--- a/bottleneck/src/move_template.c
+++ b/bottleneck/src/move_template.c
@@ -18,32 +18,33 @@
 
 /* macros ---------------------------------------------------------------- */
 
-#define INIT(dtype)                                                           \
+#define INIT(dtype) \
     PyObject *y = PyArray_EMPTY(PyArray_NDIM(a), PyArray_SHAPE(a), dtype, 0); \
-    iter2     it;                                                             \
+    iter2 it; \
     init_iter2(&it, a, y, axis);
 
 /* low-level functions such as move_sum_float64 */
-#define MOVE(name, dtype)                        \
-    static PyObject *                            \
-        name##_##dtype(PyArrayObject *a,         \
-                       int            window,    \
-                       int            min_count, \
-                       int            axis,      \
-                       int            ddof)
+#define MOVE(name, dtype) \
+    static PyObject * \
+    name##_##dtype(PyArrayObject *a, \
+                   int           window, \
+                   int           min_count, \
+                   int           axis, \
+                   int           ddof)
 
 /* top-level functions such as move_sum */
-#define MOVE_MAIN(name, ddof)                              \
-    static PyObject *                                      \
-    name(PyObject *self, PyObject *args, PyObject *kwds) { \
-        return mover(#name,                                \
-                     args,                                 \
-                     kwds,                                 \
-                     name##_float64,                       \
-                     name##_float32,                       \
-                     name##_int64,                         \
-                     name##_int32,                         \
-                     ddof);                                \
+#define MOVE_MAIN(name, ddof) \
+    static PyObject * \
+    name(PyObject *self, PyObject *args, PyObject *kwds) \
+    { \
+        return mover(#name, \
+                     args, \
+                     kwds, \
+                     name##_float64, \
+                     name##_float32, \
+                     name##_int64, \
+                     name##_int32, \
+                     ddof); \
     }
 
 /* typedefs and prototypes ----------------------------------------------- */
@@ -51,7 +52,7 @@
 /* used by move_min and move_max */
 struct _pairs {
     double value;
-    int    death;
+    int death;
 };
 typedef struct _pairs pairs;
 
@@ -59,52 +60,53 @@ typedef struct _pairs pairs;
 typedef PyObject *(*move_t)(PyArrayObject *, int, int, int, int);
 
 static PyObject *
-mover(char *    name,
+mover(char *name,
       PyObject *args,
       PyObject *kwds,
-      move_t    move_float64,
-      move_t    move_float32,
-      move_t    move_int64,
-      move_t    move_int32,
-      int       has_ddof);
+      move_t,
+      move_t,
+      move_t,
+      move_t,
+      int has_ddof);
 
 /* move_sum -------------------------------------------------------------- */
 
 /* dtype = [['float64'], ['float32']] */
 MOVE(move_sum, DTYPE0) {
+    Py_ssize_t count;
+    npy_DTYPE0 asum, ai, aold;
     INIT(NPY_DTYPE0)
     BN_BEGIN_ALLOW_THREADS
     WHILE {
-        Py_ssize_t count = 0;
-        npy_DTYPE0 asum = 0;
+        asum = count = 0;
         WHILE0 {
-            const npy_DTYPE0 ai = AI(DTYPE0);
-            if (!bn_isnan(ai)) {
+            ai = AI(DTYPE0);
+            if (ai == ai) {
                 asum += ai;
                 count += 1;
             }
             YI(DTYPE0) = BN_NAN;
         }
         WHILE1 {
-            const npy_DTYPE0 ai = AI(DTYPE0);
-            if (!bn_isnan(ai)) {
+            ai = AI(DTYPE0);
+            if (ai == ai) {
                 asum += ai;
                 count += 1;
             }
             YI(DTYPE0) = count >= min_count ? asum : BN_NAN;
         }
         WHILE2 {
-            const npy_DTYPE0 ai = AI(DTYPE0);
-            const npy_DTYPE0 aold = AOLD(DTYPE0);
-            if (!bn_isnan(ai)) {
-                if (!bn_isnan(aold)) {
+            ai = AI(DTYPE0);
+            aold = AOLD(DTYPE0);
+            if (ai == ai) {
+                if (aold == aold) {
                     asum += ai - aold;
                 } else {
                     asum += ai;
                     count++;
                 }
             } else {
-                if (!bn_isnan(aold)) {
+                if (aold == aold) {
                     asum -= aold;
                     count--;
                 }
@@ -118,6 +120,7 @@ MOVE(move_sum, DTYPE0) {
 }
 /* dtype end */
 
+
 /* dtype = [['int64', 'float64'], ['int32', 'float64']] */
 MOVE(move_sum, DTYPE0) {
     npy_DTYPE1 asum;
@@ -144,29 +147,31 @@ MOVE(move_sum, DTYPE0) {
 }
 /* dtype end */
 
+
 MOVE_MAIN(move_sum, 0)
 
+
 /* move_mean -------------------------------------------------------------- */
 
 /* dtype = [['float64'], ['float32']] */
 MOVE(move_mean, DTYPE0) {
-    npy_DTYPE0 asum, count_inv;
+    Py_ssize_t count;
+    npy_DTYPE0 asum, ai, aold, count_inv;
     INIT(NPY_DTYPE0)
     BN_BEGIN_ALLOW_THREADS
     WHILE {
-        Py_ssize_t count = 0;
-        asum = 0;
+        asum = count = 0;
         WHILE0 {
-            const npy_DTYPE0 ai = AI(DTYPE0);
-            if (!bn_isnan(ai)) {
+            ai = AI(DTYPE0);
+            if (ai == ai) {
                 asum += ai;
                 count += 1;
             }
             YI(DTYPE0) = BN_NAN;
         }
         WHILE1 {
-            const npy_DTYPE0 ai = AI(DTYPE0);
-            if (!bn_isnan(ai)) {
+            ai = AI(DTYPE0);
+            if (ai == ai) {
                 asum += ai;
                 count += 1;
             }
@@ -174,10 +179,10 @@ MOVE(move_mean, DTYPE0) {
         }
         count_inv = 1.0 / count;
         WHILE2 {
-            const npy_DTYPE0 ai = AI(DTYPE0);
-            const npy_DTYPE0 aold = AOLD(DTYPE0);
-            if (!bn_isnan(ai)) {
-                if (!bn_isnan(aold)) {
+            ai = AI(DTYPE0);
+            aold = AOLD(DTYPE0);
+            if (ai == ai) {
+                if (aold == aold) {
                     asum += ai - aold;
                 } else {
                     asum += ai;
@@ -185,7 +190,7 @@ MOVE(move_mean, DTYPE0) {
                     count_inv = 1.0 / count;
                 }
             } else {
-                if (!bn_isnan(aold)) {
+                if (aold == aold) {
                     asum -= aold;
                     count--;
                     count_inv = 1.0 / count;
@@ -200,6 +205,7 @@ MOVE(move_mean, DTYPE0) {
 }
 /* dtype end */
 
+
 /* dtype = [['int64', 'float64'], ['int32', 'float64']] */
 MOVE(move_mean, DTYPE0) {
     npy_DTYPE1 asum, window_inv = 1.0 / window;
@@ -213,7 +219,7 @@ MOVE(move_mean, DTYPE0) {
         }
         WHILE1 {
             asum += AI(DTYPE0);
-            *(npy_DTYPE1 *)(it.py + it.i * it.ystride) = (npy_DTYPE1)asum / (it.i + 1);
+            *(npy_DTYPE1*)(it.py + it.i * it.ystride) = (npy_DTYPE1)asum / (it.i + 1);
             it.i++;
         }
         WHILE2 {
@@ -227,35 +233,37 @@ MOVE(move_mean, DTYPE0) {
 }
 /* dtype end */
 
+
 MOVE_MAIN(move_mean, 0)
 
+
 /* move_std, move_var ---------------------------------------------------- */
 
 /* repeat = {'NAME': ['move_std', 'move_var'],
              'FUNC': ['sqrt',     '']} */
 /* dtype = [['float64'], ['float32']] */
 MOVE(NAME, DTYPE0) {
-    npy_DTYPE0 amean, assqdm, yi, count_inv, ddof_inv;
+    Py_ssize_t count;
+    npy_DTYPE0 delta, amean, assqdm, ai, aold, yi, count_inv, ddof_inv;
     INIT(NPY_DTYPE0)
     BN_BEGIN_ALLOW_THREADS
     WHILE {
-        Py_ssize_t count = 0;
-        amean = assqdm = 0;
+        amean = assqdm = count = 0;
         WHILE0 {
-            const npy_DTYPE0 ai = AI(DTYPE0);
-            if (!bn_isnan(ai)) {
+            ai = AI(DTYPE0);
+            if (ai == ai) {
                 count += 1;
-                const npy_DTYPE0 delta = ai - amean;
+                delta = ai - amean;
                 amean += delta / count;
                 assqdm += delta * (ai - amean);
             }
             YI(DTYPE0) = BN_NAN;
         }
         WHILE1 {
-            const npy_DTYPE0 ai = AI(DTYPE0);
-            if (!bn_isnan(ai)) {
+            ai = AI(DTYPE0);
+            if (ai == ai) {
                 count += 1;
-                const npy_DTYPE0 delta = ai - amean;
+                delta = ai - amean;
                 amean += delta / count;
                 assqdm += delta * (ai - amean);
             }
@@ -272,11 +280,11 @@ MOVE(NAME, DTYPE0) {
         count_inv = 1.0 / count;
         ddof_inv = 1.0 / (count - ddof);
         WHILE2 {
-            npy_DTYPE0 ai = AI(DTYPE0);
-            npy_DTYPE0 aold = AOLD(DTYPE0);
-            if (!bn_isnan(ai)) {
-                if (!bn_isnan(aold)) {
-                    const npy_DTYPE0 delta = ai - aold;
+            ai = AI(DTYPE0);
+            aold = AOLD(DTYPE0);
+            if (ai == ai) {
+                if (aold == aold) {
+                    delta = ai - aold;
                     aold -= amean;
                     amean += delta * count_inv;
                     ai -= amean;
@@ -285,17 +293,17 @@ MOVE(NAME, DTYPE0) {
                     count++;
                     count_inv = 1.0 / count;
                     ddof_inv = 1.0 / (count - ddof);
-                    const npy_DTYPE0 delta = ai - amean;
+                    delta = ai - amean;
                     amean += delta * count_inv;
                     assqdm += delta * (ai - amean);
                 }
             } else {
-                if (!bn_isnan(aold)) {
+                if (aold == aold) {
                     count--;
                     count_inv = 1.0 / count;
                     ddof_inv = 1.0 / (count - ddof);
                     if (count > 0) {
-                        const npy_DTYPE0 delta = aold - amean;
+                        delta = aold - amean;
                         amean -= delta * count_inv;
                         assqdm -= delta * (aold - amean);
                     } else {
@@ -323,32 +331,32 @@ MOVE(NAME, DTYPE0) {
 
 /* dtype = [['int64', 'float64'], ['int32', 'float64']] */
 MOVE(NAME, DTYPE0) {
-    const int        winddof = window - ddof;
-    npy_DTYPE1       amean, assqdm, yi;
-    const npy_DTYPE1 window_inv = 1.0 / window, winddof_inv = 1.0 / winddof;
+    int winddof = window - ddof;
+    npy_DTYPE1 delta, amean, assqdm, yi, ai, aold;
+    npy_DTYPE1 window_inv = 1.0 / window, winddof_inv = 1.0 / winddof;
     INIT(NPY_DTYPE1)
     BN_BEGIN_ALLOW_THREADS
     WHILE {
         amean = assqdm = 0;
         WHILE0 {
-            const npy_DTYPE1 ai = AI(DTYPE0);
-            const npy_DTYPE1 delta = ai - amean;
+            ai = AI(DTYPE0);
+            delta = ai - amean;
             amean += delta / (INDEX + 1);
             assqdm += delta * (ai - amean);
             YI(DTYPE1) = BN_NAN;
         }
         WHILE1 {
-            const npy_DTYPE1 ai = AI(DTYPE0);
-            const npy_DTYPE1 delta = ai - amean;
+            ai = AI(DTYPE0);
+            delta = ai - amean;
             amean += delta / (INDEX + 1);
             assqdm += delta * (ai - amean);
             yi = FUNC(assqdm / (INDEX + 1 - ddof));
             YI(DTYPE1) = yi;
         }
         WHILE2 {
-            npy_DTYPE1       ai = AI(DTYPE0);
-            npy_DTYPE1       aold = AOLD(DTYPE0);
-            const npy_DTYPE1 delta = ai - aold;
+            ai = AI(DTYPE0);
+            aold = AOLD(DTYPE0);
+            delta = ai - aold;
             aold -= amean;
             amean += delta * window_inv;
             ai -= amean;
@@ -368,6 +376,7 @@ MOVE(NAME, DTYPE0) {
 MOVE_MAIN(NAME, 1)
 /* repeat end */
 
+
 /* move_min, move_max, move_argmin, move_argmax -------------------------- */
 
 /* repeat = {'MACRO_FLOAT': ['MOVE_NANMIN', 'MOVE_NANMAX'],
@@ -376,49 +385,45 @@ MOVE_MAIN(NAME, 1)
              'FLIP':        ['>=',          '<='],
              'BIG_FLOAT':   ['BN_INFINITY', '-BN_INFINITY']} */
 
-#define MACRO_FLOAT(dtype, yi, code)                          \
-    ai = AI(dtype);                                           \
-    if (!bn_isnan(ai)) {                                      \
-        count++;                                              \
-    } else {                                                  \
-        ai = BIG_FLOAT;                                       \
-    }                                                         \
-    code;                                                     \
-    if (ai COMPARE extreme_pair->value) {                     \
-        extreme_pair->value = ai;                             \
-        extreme_pair->death = INDEX + window;                 \
-        last = extreme_pair;                                  \
-    } else {                                                  \
-        while (last->value FLIP ai) {                         \
-            if (last == ring) last = end;                     \
-            last--;                                           \
-        }                                                     \
-        last++;                                               \
-        if (last == end) last = ring;                         \
-        last->value = ai;                                     \
-        last->death = INDEX + window;                         \
-    }                                                         \
+#define MACRO_FLOAT(dtype, yi, code) \
+    ai = AI(dtype); \
+    if (ai == ai) count++; else ai = BIG_FLOAT; \
+    code; \
+    if (ai COMPARE extreme_pair->value) { \
+        extreme_pair->value = ai; \
+        extreme_pair->death = INDEX + window; \
+        last = extreme_pair; \
+    } else { \
+        while (last->value FLIP ai) { \
+            if (last == ring) last = end; \
+            last--; \
+        } \
+        last++; \
+        if (last == end) last = ring; \
+        last->value = ai; \
+        last->death = INDEX + window; \
+    } \
     yi_tmp = yi; /* yi might contain i and YI contains i++ */ \
     YI(dtype) = yi_tmp;
 
 #define MACRO_INT(a_dtype, y_dtype, yi, code) \
-    ai = AI(a_dtype);                         \
-    code;                                     \
-    if (ai COMPARE extreme_pair->value) {     \
-        extreme_pair->value = ai;             \
+    ai = AI(a_dtype); \
+    code; \
+    if (ai COMPARE extreme_pair->value) { \
+        extreme_pair->value = ai; \
         extreme_pair->death = INDEX + window; \
-        last = extreme_pair;                  \
-    } else {                                  \
-        while (last->value FLIP ai) {         \
-            if (last == ring) last = end;     \
-            last--;                           \
-        }                                     \
-        last++;                               \
-        if (last == end) last = ring;         \
-        last->value = ai;                     \
-        last->death = INDEX + window;         \
-    }                                         \
-    yi_tmp = yi;                              \
+        last = extreme_pair; \
+    } else { \
+        while (last->value FLIP ai) { \
+            if (last == ring) last = end; \
+            last--; \
+        } \
+        last++; \
+        if (last == end) last = ring; \
+        last->value = ai; \
+        last->death = INDEX + window; \
+    } \
+    yi_tmp = yi; \
     YI(y_dtype) = yi_tmp;
 /* repeat end */
 
@@ -440,39 +445,41 @@ MOVE_MAIN(NAME, 1)
    } */
 /* dtype = [['float64'], ['float32']] */
 MOVE(NAME, DTYPE0) {
-    npy_DTYPE0 yi_tmp;
-    pairs *    extreme_pair;
-    pairs *    end;
-    pairs *    last;
-    pairs *    ring = (pairs *)calloc(sizeof(pairs), window);
+    npy_DTYPE0 ai, aold, yi_tmp;
+    Py_ssize_t count;
+    pairs *extreme_pair;
+    pairs *end;
+    pairs *last;
+    pairs *ring = (pairs *)malloc(window * sizeof(pairs));
     INIT(NPY_DTYPE0)
     BN_BEGIN_ALLOW_THREADS
     WHILE {
-        Py_ssize_t count = 0;
+        count = 0;
         end = ring + window;
         last = ring;
         extreme_pair = ring;
-        npy_DTYPE0 ai = A0(DTYPE0);
-        extreme_pair->value = !bn_isnan(ai) ? ai : BIG_FLOAT;
+        ai = A0(DTYPE0);
+        extreme_pair->value = ai == ai ? ai : BIG_FLOAT;
         extreme_pair->death = window;
         WHILE0 {
             MACRO_FLOAT(DTYPE0,
-                        BN_NAN, )
+                        BN_NAN,
+                        )
         }
         WHILE1 {
             MACRO_FLOAT(DTYPE0,
-                        count >= min_count ? VALUE : BN_NAN, )
+                        count >= min_count ? VALUE : BN_NAN,
+                        )
         }
         WHILE2 {
-            MACRO_FLOAT(
-                DTYPE0,
-                count >= min_count ? VALUE : BN_NAN,
-                const npy_DTYPE0 aold = AOLD(DTYPE0);
-                if (!bn_isnan(aold)) count--;
-                if (extreme_pair->death == INDEX) {
-                    extreme_pair++;
-                    if (extreme_pair >= end) extreme_pair = ring;
-                })
+            MACRO_FLOAT(DTYPE0,
+                        count >= min_count ? VALUE : BN_NAN,
+                        aold = AOLD(DTYPE0);
+                        if (aold == aold) count--;
+                        if (extreme_pair->death == INDEX) {
+                            extreme_pair++;
+                            if (extreme_pair >= end) extreme_pair = ring;
+                        })
         }
         NEXT2
     }
@@ -484,39 +491,41 @@ MOVE(NAME, DTYPE0) {
 
 /* dtype = [['int64', 'float64'], ['int32', 'float64']] */
 MOVE(NAME, DTYPE0) {
+    npy_DTYPE0 ai;
     npy_DTYPE1 yi_tmp;
-    pairs *    extreme_pair;
-    pairs *    end;
-    pairs *    last;
-    pairs *    ring = (pairs *)calloc(sizeof(pairs), window);
+    pairs *extreme_pair;
+    pairs *end;
+    pairs *last;
+    pairs *ring = (pairs *)malloc(window * sizeof(pairs));
     INIT(NPY_DTYPE1)
     BN_BEGIN_ALLOW_THREADS
     WHILE {
         end = ring + window;
         last = ring;
         extreme_pair = ring;
-        npy_DTYPE0 ai = A0(DTYPE0);
+        ai = A0(DTYPE0);
         extreme_pair->value = ai;
         extreme_pair->death = window;
         WHILE0 {
             MACRO_INT(DTYPE0,
                       DTYPE1,
-                      BN_NAN, )
+                      BN_NAN,
+                      )
         }
         WHILE1 {
             MACRO_INT(DTYPE0,
                       DTYPE1,
-                      VALUE, )
+                      VALUE,
+                      )
         }
         WHILE2 {
-            MACRO_INT(
-                DTYPE0,
-                DTYPE1,
-                VALUE,
-                if (extreme_pair->death == INDEX) {
-                    extreme_pair++;
-                    if (extreme_pair >= end) extreme_pair = ring;
-                })
+            MACRO_INT(DTYPE0,
+                      DTYPE1,
+                      VALUE,
+                      if (extreme_pair->death == INDEX) {
+                          extreme_pair++;
+                          if (extreme_pair >= end) extreme_pair = ring;
+                      })
         }
         NEXT2
     }
@@ -533,6 +542,7 @@ MOVE_MAIN(NAME, 0)
 
 /* dtype = [['float64'], ['float32']] */
 MOVE(move_median, DTYPE0) {
+    npy_DTYPE0 ai;
     mm_handle *mm = mm_new_nan(window, min_count);
     INIT(NPY_DTYPE0)
     if (window == 1) {
@@ -545,15 +555,15 @@ MOVE(move_median, DTYPE0) {
     BN_BEGIN_ALLOW_THREADS
     WHILE {
         WHILE0 {
-            const npy_DTYPE0 ai = AI(DTYPE0);
+            ai = AI(DTYPE0);
             YI(DTYPE0) = mm_update_init_nan(mm, ai);
         }
         WHILE1 {
-            const npy_DTYPE0 ai = AI(DTYPE0);
+            ai = AI(DTYPE0);
             YI(DTYPE0) = mm_update_init_nan(mm, ai);
         }
         WHILE2 {
-            const npy_DTYPE0 ai = AI(DTYPE0);
+            ai = AI(DTYPE0);
             YI(DTYPE0) = mm_update_nan(mm, ai);
         }
         mm_reset(mm);
@@ -567,6 +577,7 @@ MOVE(move_median, DTYPE0) {
 
 /* dtype = [['int64', 'float64'], ['int32', 'float64']] */
 MOVE(move_median, DTYPE0) {
+    npy_DTYPE0 ai;
     mm_handle *mm = mm_new(window, min_count);
     INIT(NPY_DTYPE1)
     if (window == 1) {
@@ -580,15 +591,15 @@ MOVE(move_median, DTYPE0) {
     BN_BEGIN_ALLOW_THREADS
     WHILE {
         WHILE0 {
-            const npy_DTYPE0 ai = AI(DTYPE0);
+            ai = AI(DTYPE0);
             YI(DTYPE1) = mm_update_init(mm, ai);
         }
         WHILE1 {
-            const npy_DTYPE0 ai = AI(DTYPE0);
+            ai = AI(DTYPE0);
             YI(DTYPE1) = mm_update_init(mm, ai);
         }
         WHILE2 {
-            const npy_DTYPE0 ai = AI(DTYPE0);
+            ai = AI(DTYPE0);
             YI(DTYPE1) = mm_update(mm, ai);
         }
         mm_reset(mm);
@@ -600,42 +611,45 @@ MOVE(move_median, DTYPE0) {
 }
 /* dtype end */
 
+
 MOVE_MAIN(move_median, 0)
 
+
 /* move_rank-------------------------------------------------------------- */
 
-#define MOVE_RANK(dtype0, dtype1, limit)  \
-    Py_ssize_t   j;                       \
-    npy_##dtype0 ai, aj;                  \
-    npy_##dtype1 g, e, n, r;              \
-    ai = AI(dtype0);                      \
-    if (!bn_isnan(ai)) {                  \
-        g = 0;                            \
-        e = 1;                            \
-        n = 1;                            \
+#define MOVE_RANK(dtype0, dtype1, limit) \
+    Py_ssize_t j; \
+    npy_##dtype0 ai, aj; \
+    npy_##dtype1 g, e, n, r; \
+    ai = AI(dtype0); \
+    if (ai == ai) { \
+        g = 0; \
+        e = 1; \
+        n = 1; \
+        r = 0; \
         for (j = limit; j < INDEX; j++) { \
-            aj = AX(dtype0, j);           \
-            if (!bn_isnan(aj)) {          \
-                n++;                      \
-                if (ai > aj) {            \
-                    g += 2;               \
-                } else if (ai == aj) {    \
-                    e++;                  \
-                }                         \
-            }                             \
-        }                                 \
-        if (n < min_count) {              \
-            r = BN_NAN;                   \
-        } else if (n == 1) {              \
-            r = 0.0;                      \
-        } else {                          \
-            r = 0.5 * (g + e - 1.0);      \
-            r = r / (n - 1.0);            \
-            r = 2.0 * (r - 0.5);          \
-        }                                 \
-    } else {                              \
-        r = BN_NAN;                       \
-    }
+            aj = AX(dtype0, j); \
+            if (aj == aj) { \
+                n++; \
+                if (ai > aj) { \
+                    g += 2; \
+                } else if (ai == aj) { \
+                    e++; \
+                } \
+            } \
+        } \
+        if (n < min_count) { \
+            r = BN_NAN; \
+        } else if (n == 1) { \
+            r = 0.0; \
+        } else { \
+            r = 0.5 * (g + e - 1.0); \
+            r = r / (n - 1.0); \
+            r = 2.0 * (r - 0.5); \
+        } \
+    } else { \
+        r = BN_NAN; \
+    } \
 
 /* dtype = [['float64', 'float64'], ['float32', 'float32']] */
 MOVE(move_rank, DTYPE0) {
@@ -663,6 +677,7 @@ MOVE(move_rank, DTYPE0) {
 /* dtype = [['int64', 'float64'], ['int32', 'float64']] */
 MOVE(move_rank, DTYPE0) {
     Py_ssize_t j;
+    npy_DTYPE0 ai, aj;
     npy_DTYPE1 g, e, r, window_inv = 0.5 * 1.0 / (window - 1);
     INIT(NPY_DTYPE1)
     BN_BEGIN_ALLOW_THREADS
@@ -671,11 +686,12 @@ MOVE(move_rank, DTYPE0) {
             YI(DTYPE1) = BN_NAN;
         }
         WHILE1 {
-            const npy_DTYPE0 ai = AI(DTYPE0);
+            ai = AI(DTYPE0);
             g = 0;
             e = 1;
+            r = 0;
             for (j = 0; j < INDEX; j++) {
-                const npy_DTYPE0 aj = AX(DTYPE0, j);
+                aj = AX(DTYPE0, j);
                 if (ai > aj) {
                     g += 2;
                 } else if (ai == aj) {
@@ -694,12 +710,13 @@ MOVE(move_rank, DTYPE0) {
             YI(DTYPE1) = r;
         }
         WHILE2 {
-            const npy_DTYPE0 ai = AI(DTYPE0);
+            ai = AI(DTYPE0);
             g = 0;
             e = 1;
+            r = 0;
             for (j = INDEX - window + 1; j < INDEX; j++) {
-                const npy_DTYPE0 aj = AX(DTYPE0, j);
-                if (ai > aj) {
+                aj = AX(DTYPE0, j);
+		if (ai > aj) {
                     g += 2;
                 } else if (ai == aj) {
                     e++;
@@ -720,8 +737,10 @@ MOVE(move_rank, DTYPE0) {
 }
 /* dtype end */
 
+
 MOVE_MAIN(move_rank, 0)
 
+
 /* python strings -------------------------------------------------------- */
 
 PyObject *pystr_a = NULL;
@@ -744,9 +763,9 @@ intern_strings(void) {
 /* mover ----------------------------------------------------------------- */
 
 static inline int
-parse_args(PyObject * args,
-           PyObject * kwds,
-           int        has_ddof,
+parse_args(PyObject *args,
+           PyObject *kwds,
+           int has_ddof,
            PyObject **a,
            PyObject **window,
            PyObject **min_count,
@@ -755,7 +774,7 @@ parse_args(PyObject * args,
     const Py_ssize_t nargs = PyTuple_GET_SIZE(args);
     const Py_ssize_t nkwds = kwds == NULL ? 0 : PyDict_Size(kwds);
     if (nkwds) {
-        int       nkwds_found = 0;
+        int nkwds_found = 0;
         PyObject *tmp;
         switch (nargs) {
             case 4:
@@ -765,14 +784,10 @@ parse_args(PyObject * args,
                     TYPE_ERR("wrong number of arguments");
                     return 0;
                 }
-            case 3:
-                *min_count = PyTuple_GET_ITEM(args, 2);
-            case 2:
-                *window = PyTuple_GET_ITEM(args, 1);
-            case 1:
-                *a = PyTuple_GET_ITEM(args, 0);
-            case 0:
-                break;
+            case 3: *min_count = PyTuple_GET_ITEM(args, 2);
+            case 2: *window = PyTuple_GET_ITEM(args, 1);
+            case 1: *a = PyTuple_GET_ITEM(args, 0);
+            case 0: break;
             default:
                 TYPE_ERR("wrong number of arguments");
                 return 0;
@@ -848,22 +863,33 @@ parse_args(PyObject * args,
                 return 0;
         }
     }
+
     return 1;
+
 }
 
+
 static PyObject *
-mover(char *    name,
+mover(char *name,
       PyObject *args,
       PyObject *kwds,
-      move_t    move_float64,
-      move_t    move_float32,
-      move_t    move_int64,
-      move_t    move_int32,
-      int       has_ddof) {
+      move_t move_float64,
+      move_t move_float32,
+      move_t move_int64,
+      move_t move_int32,
+      int has_ddof) {
+
+    int mc;
+    int window;
+    int axis;
+    int ddof;
+    int dtype;
+    int ndim;
+
     Py_ssize_t length;
 
     PyArrayObject *a;
-    PyObject *     y;
+    PyObject *y;
 
     PyObject *a_obj = NULL;
     PyObject *window_obj = NULL;
@@ -871,7 +897,8 @@ mover(char *    name,
     PyObject *axis_obj = NULL;
     PyObject *ddof_obj = NULL;
 
-    if (!parse_args(args, kwds, has_ddof, &a_obj, &window_obj, &min_count_obj, &axis_obj, &ddof_obj)) {
+    if (!parse_args(args, kwds, has_ddof, &a_obj, &window_obj,
+                    &min_count_obj, &axis_obj, &ddof_obj)) {
         return NULL;
     }
 
@@ -888,19 +915,17 @@ mover(char *    name,
 
     /* check for byte swapped input array */
     if (PyArray_ISBYTESWAPPED(a)) {
-        Py_DECREF(a);
         return slow(name, args, kwds);
     }
 
     /* window */
-    int window = PyArray_PyIntAsInt(window_obj);
+    window = PyArray_PyIntAsInt(window_obj);
     if (error_converting(window)) {
         TYPE_ERR("`window` must be an integer");
         goto error;
     }
 
     /* min_count */
-    int mc;
     if (min_count_obj == Py_None) {
         mc = window;
     } else {
@@ -912,8 +937,7 @@ mover(char *    name,
         if (mc > window) {
             PyErr_Format(PyExc_ValueError,
                          "min_count (%d) cannot be greater than window (%d)",
-                         mc,
-                         window);
+                         mc, window);
             goto error;
         } else if (mc <= 0) {
             VALUE_ERR("`min_count` must be greater than zero.");
@@ -921,7 +945,7 @@ mover(char *    name,
         }
     }
 
-    int ndim = PyArray_NDIM(a);
+    ndim = PyArray_NDIM(a);
 
     /* defend against 0d beings */
     if (ndim == 0) {
@@ -930,7 +954,6 @@ mover(char *    name,
     }
 
     /* defend against the axis of negativity */
-    int axis;
     if (axis_obj == NULL) {
         axis = ndim - 1;
     } else {
@@ -943,8 +966,7 @@ mover(char *    name,
             axis += ndim;
             if (axis < 0) {
                 PyErr_Format(PyExc_ValueError,
-                             "axis(=%d) out of bounds",
-                             axis);
+                             "axis(=%d) out of bounds", axis);
                 goto error;
             }
         } else if (axis >= ndim) {
@@ -954,7 +976,6 @@ mover(char *    name,
     }
 
     /* ddof */
-    int ddof;
     if (ddof_obj == NULL) {
         ddof = 0;
     } else {
@@ -969,12 +990,11 @@ mover(char *    name,
     if ((window < 1) || (window > length)) {
         PyErr_Format(PyExc_ValueError,
                      "Moving window (=%d) must between 1 and %zu, inclusive",
-                     window,
-                     length);
+                     window, length);
         goto error;
     }
 
-    int dtype = PyArray_TYPE(a);
+    dtype = PyArray_TYPE(a);
 
     if (dtype == NPY_float64) {
         y = move_float64(a, window, mc, axis, ddof);
@@ -991,14 +1011,17 @@ mover(char *    name,
     Py_DECREF(a);
 
     return y;
+
 error:
     Py_DECREF(a);
     return NULL;
+
 }
 
 /* docstrings ------------------------------------------------------------- */
-// clang-format off
-static char move_doc[] = "Bottleneck moving window functions.";
+
+static char move_doc[] =
+"Bottleneck moving window functions.";
 
 static char move_sum_doc[] =
 /* MULTILINE STRING BEGIN
@@ -1479,32 +1502,33 @@ move_methods[] = {
     {NULL, NULL, 0, NULL}
 };
 
-// clang-format on
 
 #if PY_MAJOR_VERSION >= 3
 static struct PyModuleDef
-    move_def = {
-        PyModuleDef_HEAD_INIT,
-        "move",
-        move_doc,
-        -1,
-        move_methods};
+move_def = {
+   PyModuleDef_HEAD_INIT,
+   "move",
+   move_doc,
+   -1,
+   move_methods
+};
 #endif
 
+
 PyMODINIT_FUNC
 #if PY_MAJOR_VERSION >= 3
-    #define RETVAL m
+#define RETVAL m
 PyInit_move(void)
 #else
-    #define RETVAL
+#define RETVAL
 initmove(void)
 #endif
 {
-#if PY_MAJOR_VERSION >= 3
-    PyObject *m = PyModule_Create(&move_def);
-#else
-    PyObject *m = Py_InitModule3("move", move_methods, move_doc);
-#endif
+    #if PY_MAJOR_VERSION >=3
+        PyObject *m = PyModule_Create(&move_def);
+    #else
+        PyObject *m = Py_InitModule3("move", move_methods, move_doc);
+    #endif
     if (m == NULL) return RETVAL;
     import_array();
     if (!intern_strings()) {
diff --git a/bottleneck/src/nonreduce_axis_template.c b/bottleneck/src/nonreduce_axis_template.c
index 142945b7af..6acf8492ff 100644
--- a/bottleneck/src/nonreduce_axis_template.c
+++ b/bottleneck/src/nonreduce_axis_template.c
@@ -7,41 +7,40 @@
 
 /* low-level functions such as move_sum_float64 */
 #define NRA(name, dtype) \
-    static PyObject *    \
-        name##_##dtype(PyArrayObject *a, int axis, int n)
+    static PyObject * \
+    name##_##dtype(PyArrayObject *a, int axis, int n)
 
 /* top-level functions such as move_sum */
-#define NRA_MAIN(name, parse)                              \
-    static PyObject *                                      \
-    name(PyObject *self, PyObject *args, PyObject *kwds) { \
-        return nonreducer_axis(#name,                      \
-                               args,                       \
-                               kwds,                       \
-                               name##_float64,             \
-                               name##_float32,             \
-                               name##_int64,               \
-                               name##_int32,               \
-                               parse);                     \
+#define NRA_MAIN(name, parse) \
+    static PyObject * \
+    name(PyObject *self, PyObject *args, PyObject *kwds) \
+    { \
+        return nonreducer_axis(#name, \
+                               args, \
+                               kwds, \
+                               name##_float64, \
+                               name##_float32, \
+                               name##_int64, \
+                               name##_int32, \
+                               parse); \
     }
 
 /* typedefs and prototypes ----------------------------------------------- */
 
 /* how should input be parsed? */
-typedef enum { PARSE_PARTITION,
-               PARSE_RANKDATA,
-               PARSE_PUSH } parse_type;
+typedef enum {PARSE_PARTITION, PARSE_RANKDATA, PARSE_PUSH} parse_type;
 
 /* function pointer for functions passed to nonreducer_axis */
 typedef PyObject *(*nra_t)(PyArrayObject *, int, int);
 
 static PyObject *
-nonreducer_axis(char *    name,
+nonreducer_axis(char *name,
                 PyObject *args,
                 PyObject *kwds,
-                nra_t     nra_float64,
-                nra_t     nra_float32,
-                nra_t     nra_int64,
-                nra_t     nra_int32,
+                nra_t,
+                nra_t,
+                nra_t,
+                nra_t,
                 parse_type);
 
 /* partition ------------------------------------------------------------- */
@@ -50,28 +49,26 @@ nonreducer_axis(char *    name,
 
 /* dtype = [['float64'], ['float32'], ['int64'], ['int32']] */
 NRA(partition, DTYPE0) {
-    npy_intp j = 0, k = 0;
-    iter     it;
+    npy_intp i;
+    npy_intp j, l, r, k;
+    iter it;
 
     a = (PyArrayObject *)PyArray_NewCopy(a, NPY_ANYORDER);
     init_iter_one(&it, a, axis);
 
-    if (LENGTH == 0) {
-        return (PyObject *)a;
-    }
+    if (LENGTH == 0) return (PyObject *)a;
     if (n < 0 || n > LENGTH - 1) {
         PyErr_Format(PyExc_ValueError,
                      "`n` (=%d) must be between 0 and %zd, inclusive.",
-                     n,
-                     LENGTH - 1);
+                     n, LENGTH - 1);
         return NULL;
     }
 
     BN_BEGIN_ALLOW_THREADS
     k = n;
     WHILE {
-        npy_intp l = 0;
-        npy_intp r = LENGTH - 1;
+        l = 0;
+        r = LENGTH - 1;
         PARTITION(DTYPE0)
         NEXT
     }
@@ -83,103 +80,98 @@ NRA(partition, DTYPE0) {
 
 NRA_MAIN(partition, PARSE_PARTITION)
 
+
 /* argpartition ----------------------------------------------------------- */
 
-#define BUFFER_NEW(dtype) dtype *B = calloc(LENGTH, sizeof(dtype));
-#define BUFFER_DELETE     free(B);
-
-#define ARGWIRTH(dtype0, dtype1)           \
-    x = B[k];                              \
-    npy_intp m = l;                        \
-    j = r;                                 \
-    do {                                   \
-        while (B[m] < x)                   \
-            m++;                           \
-        while (x < B[j])                   \
-            j--;                           \
-        if (m <= j) {                      \
-            npy_##dtype0 atmp = B[m];      \
-            B[m] = B[j];                   \
-            B[j] = atmp;                   \
-            ytmp = YX(dtype1, m);          \
-            YX(dtype1, m) = YX(dtype1, j); \
-            YX(dtype1, j) = ytmp;          \
-            m++;                           \
-            j--;                           \
-        }                                  \
-    } while (m <= j);                      \
-    if (j < k)                             \
-        l = m;                             \
-    if (k < m)                             \
-        r = j;
-
-#define ARGPARTITION(dtype0, dtype1)               \
-    while (l < r) {                                \
-        npy_##dtype0 x;                            \
-        npy_##dtype0 al = B[l];                    \
-        npy_##dtype0 ak = B[k];                    \
-        npy_##dtype0 ar = B[r];                    \
-        npy_##dtype1 ytmp;                         \
-        if (al > ak) {                             \
-            if (ak < ar) {                         \
-                if (al < ar) {                     \
-                    B[k] = al;                     \
-                    B[l] = ak;                     \
-                    ytmp = YX(dtype1, k);          \
+#define BUFFER_NEW(dtype) dtype *B = malloc(LENGTH * sizeof(dtype));
+#define BUFFER_DELETE free(B);
+
+#define ARGWIRTH(dtype0, dtype1) \
+    x = B[k]; \
+    i = l; \
+    j = r; \
+    do { \
+        while (B[i] < x) i++; \
+        while (x < B[j]) j--; \
+        if (i <= j) { \
+            npy_##dtype0 atmp = B[i]; \
+            B[i] = B[j]; \
+            B[j] = atmp; \
+            ytmp = YX(dtype1, i); \
+            YX(dtype1, i) = YX(dtype1, j); \
+            YX(dtype1, j) = ytmp; \
+            i++; \
+            j--; \
+        } \
+    } while (i <= j); \
+    if (j < k) l = i; \
+    if (k < i) r = j;
+
+#define ARGPARTITION(dtype0, dtype1) \
+    while (l < r) { \
+        npy_##dtype0 x; \
+        npy_##dtype0 al = B[l]; \
+        npy_##dtype0 ak = B[k]; \
+        npy_##dtype0 ar = B[r]; \
+        npy_##dtype1 ytmp; \
+        if (al > ak) { \
+            if (ak < ar) { \
+                if (al < ar) { \
+                    B[k] = al; \
+                    B[l] = ak; \
+                    ytmp = YX(dtype1, k); \
                     YX(dtype1, k) = YX(dtype1, l); \
-                    YX(dtype1, l) = ytmp;          \
-                } else {                           \
-                    B[k] = ar;                     \
-                    B[r] = ak;                     \
-                    ytmp = YX(dtype1, k);          \
+                    YX(dtype1, l) = ytmp; \
+                } else { \
+                    B[k] = ar; \
+                    B[r] = ak; \
+                    ytmp = YX(dtype1, k); \
                     YX(dtype1, k) = YX(dtype1, r); \
-                    YX(dtype1, r) = ytmp;          \
-                }                                  \
-            }                                      \
-        } else {                                   \
-            if (ak > ar) {                         \
-                if (al > ar) {                     \
-                    B[k] = al;                     \
-                    B[l] = ak;                     \
-                    ytmp = YX(dtype1, k);          \
+                    YX(dtype1, r) = ytmp; \
+                } \
+            } \
+        } else { \
+            if (ak > ar) { \
+                if (al > ar) { \
+                    B[k] = al; \
+                    B[l] = ak; \
+                    ytmp = YX(dtype1, k); \
                     YX(dtype1, k) = YX(dtype1, l); \
-                    YX(dtype1, l) = ytmp;          \
-                } else {                           \
-                    B[k] = ar;                     \
-                    B[r] = ak;                     \
-                    ytmp = YX(dtype1, k);          \
+                    YX(dtype1, l) = ytmp; \
+                } else { \
+                    B[k] = ar; \
+                    B[r] = ak; \
+                    ytmp = YX(dtype1, k); \
                     YX(dtype1, k) = YX(dtype1, r); \
-                    YX(dtype1, r) = ytmp;          \
-                }                                  \
-            }                                      \
-        }                                          \
-        ARGWIRTH(dtype0, dtype1)                   \
+                    YX(dtype1, r) = ytmp; \
+                } \
+            } \
+        } \
+        ARGWIRTH(dtype0, dtype1) \
     }
 
-#define ARGPARTSORT(dtype0, dtype1)         \
-    for (npy_intp i = 0; i < LENGTH; i++) { \
-        B[i] = AX(dtype0, i);               \
-        YX(dtype1, i) = i;                  \
-    }                                       \
-    l = 0;                                  \
-    r = LENGTH - 1;                         \
+#define ARGPARTSORT(dtype0, dtype1) \
+    for (i = 0; i < LENGTH; i++) { \
+        B[i] = AX(dtype0, i); \
+        YX(dtype1, i) = i; \
+    } \
+    l = 0; \
+    r = LENGTH - 1; \
     ARGPARTITION(dtype0, dtype1)
 
 /* dtype = [['float64', 'intp'], ['float32', 'intp'],
             ['int64',   'intp'], ['int32',   'intp']] */
 NRA(argpartition, DTYPE0) {
-    npy_intp  i;
-    PyObject *y = PyArray_EMPTY(PyArray_NDIM(a), PyArray_SHAPE(a), NPY_DTYPE1, 0);
-    iter2     it;
+    npy_intp i;
+    PyObject *y = PyArray_EMPTY(PyArray_NDIM(a), PyArray_SHAPE(a),
+                                NPY_DTYPE1, 0);
+    iter2 it;
     init_iter2(&it, a, y, axis);
-    if (LENGTH == 0) {
-        return y;
-    }
+    if (LENGTH == 0) return y;
     if (n < 0 || n > LENGTH - 1) {
         PyErr_Format(PyExc_ValueError,
                      "`n` (=%d) must be between 0 and %zd, inclusive.",
-                     n,
-                     LENGTH - 1);
+                     n, LENGTH - 1);
         return NULL;
     }
     BN_BEGIN_ALLOW_THREADS
@@ -187,6 +179,8 @@ NRA(argpartition, DTYPE0) {
     npy_intp j, l, r, k;
     k = n;
     WHILE {
+        l = 0;
+        r = LENGTH - 1;
         ARGPARTSORT(DTYPE0, DTYPE1)
         NEXT2
     }
@@ -198,36 +192,34 @@ NRA(argpartition, DTYPE0) {
 
 NRA_MAIN(argpartition, PARSE_PARTITION)
 
+
 /* rankdata -------------------------------------------------------------- */
 
 /* dtype = [['float64', 'float64', 'intp'], ['float32', 'float64', 'intp'],
             ['int64',   'float64', 'intp'], ['int32',   'float64', 'intp']] */
 NRA(rankdata, DTYPE0) {
-    Py_ssize_t j = 0, k, idx;
-    npy_DTYPE1 new, averank, sumranks = 0;
+    Py_ssize_t j=0, k, idx, dupcount=0, i;
+    npy_DTYPE1 old, new, averank, sumranks = 0;
 
     PyObject *z = PyArray_ArgSort(a, axis, NPY_QUICKSORT);
     PyObject *y = PyArray_EMPTY(PyArray_NDIM(a),
-                                PyArray_SHAPE(a),
-                                NPY_DTYPE1,
-                                0);
+                                PyArray_SHAPE(a), NPY_DTYPE1, 0);
 
     iter3 it;
     init_iter3(&it, a, y, z, axis);
 
     BN_BEGIN_ALLOW_THREADS
     if (LENGTH == 0) {
-        Py_ssize_t  size = PyArray_SIZE((PyArrayObject *)y);
+        Py_ssize_t size = PyArray_SIZE((PyArrayObject *)y);
         npy_DTYPE1 *py = (npy_DTYPE1 *)PyArray_DATA(a);
-        for (npy_intp i = 0; i < size; i++)
-            YPP = BN_NAN;
+        for (i = 0; i < size; i++) YPP = BN_NAN;
     } else {
         WHILE {
             idx = ZX(DTYPE2, 0);
-            npy_DTYPE1 old = AX(DTYPE0, idx);
+            old = AX(DTYPE0, idx);
             sumranks = 0;
-            Py_ssize_t dupcount = 0;
-            for (npy_intp i = 0; i < LENGTH - 1; i++) {
+            dupcount = 0;
+            for (i = 0; i < LENGTH - 1; i++) {
                 sumranks += i;
                 dupcount++;
                 k = i + 1;
@@ -263,35 +255,33 @@ NRA(rankdata, DTYPE0) {
 
 NRA_MAIN(rankdata, PARSE_RANKDATA)
 
+
 /* nanrankdata ----------------------------------------------------------- */
 
 /* dtype = [['float64', 'float64', 'intp'], ['float32', 'float64', 'intp']] */
 NRA(nanrankdata, DTYPE0) {
-    Py_ssize_t j = 0, k, idx, dupcount = 0;
+    Py_ssize_t j=0, k, idx, dupcount=0, i;
     npy_DTYPE1 old, new, averank, sumranks = 0;
 
     PyObject *z = PyArray_ArgSort(a, axis, NPY_QUICKSORT);
     PyObject *y = PyArray_EMPTY(PyArray_NDIM(a),
-                                PyArray_SHAPE(a),
-                                NPY_DTYPE1,
-                                0);
+                                PyArray_SHAPE(a), NPY_DTYPE1, 0);
 
     iter3 it;
     init_iter3(&it, a, y, z, axis);
 
     BN_BEGIN_ALLOW_THREADS
     if (LENGTH == 0) {
-        Py_ssize_t  size = PyArray_SIZE((PyArrayObject *)y);
+        Py_ssize_t size = PyArray_SIZE((PyArrayObject *)y);
         npy_DTYPE1 *py = (npy_DTYPE1 *)PyArray_DATA(a);
-        for (npy_intp i = 0; i < size; i++)
-            YPP = BN_NAN;
+        for (i = 0; i < size; i++) YPP = BN_NAN;
     } else {
         WHILE {
             idx = ZX(DTYPE2, 0);
             old = AX(DTYPE0, idx);
             sumranks = 0;
             dupcount = 0;
-            for (npy_intp i = 0; i < LENGTH - 1; i++) {
+            for (i = 0; i < LENGTH - 1; i++) {
                 sumranks += i;
                 dupcount++;
                 k = i + 1;
@@ -347,14 +337,15 @@ nanrankdata(PyObject *self, PyObject *args, PyObject *kwds) {
                            PARSE_RANKDATA);
 }
 
+
 /* push ------------------------------------------------------------------ */
 
 /* dtype = [['float64'], ['float32']] */
 NRA(push, DTYPE0) {
-    npy_intp   index;
+    npy_intp index;
     npy_DTYPE0 ai, ai_last, n_float;
-    PyObject * y = PyArray_Copy(a);
-    iter       it;
+    PyObject *y = PyArray_Copy(a);
+    iter it;
     init_iter_one(&it, (PyArrayObject *)y, axis);
     if (LENGTH == 0 || NDIM == 0) {
         return y;
@@ -366,7 +357,7 @@ NRA(push, DTYPE0) {
         ai_last = BN_NAN;
         FOR {
             ai = AI(DTYPE0);
-            if (!bn_isnan(ai)) {
+            if (ai == ai) {
                 ai_last = ai;
                 index = INDEX;
             } else {
@@ -391,14 +382,16 @@ NRA(push, DTYPE0) {
 
 NRA_MAIN(push, PARSE_PUSH)
 
+
 /* python strings -------------------------------------------------------- */
 
-static PyObject *pystr_a = NULL;
-static PyObject *pystr_n = NULL;
-static PyObject *pystr_kth = NULL;
-static PyObject *pystr_axis = NULL;
+PyObject *pystr_a = NULL;
+PyObject *pystr_n = NULL;
+PyObject *pystr_kth = NULL;
+PyObject *pystr_axis = NULL;
 
-static int intern_strings(void) {
+static int
+intern_strings(void) {
     pystr_a = PyString_InternFromString("a");
     pystr_n = PyString_InternFromString("n");
     pystr_kth = PyString_InternFromString("kth");
@@ -409,23 +402,20 @@ static int intern_strings(void) {
 /* nonreducer_axis ------------------------------------------------------- */
 
 static inline int
-parse_partition(PyObject * args,
-                PyObject * kwds,
+parse_partition(PyObject *args,
+                PyObject *kwds,
                 PyObject **a,
                 PyObject **n,
                 PyObject **axis) {
     const Py_ssize_t nargs = PyTuple_GET_SIZE(args);
     const Py_ssize_t nkwds = kwds == NULL ? 0 : PyDict_Size(kwds);
     if (nkwds) {
-        int       nkwds_found = 0;
-        PyObject *tmp = NULL;
+        int nkwds_found = 0;
+        PyObject *tmp;
         switch (nargs) {
-            case 2:
-                *n = PyTuple_GET_ITEM(args, 1);
-            case 1:
-                *a = PyTuple_GET_ITEM(args, 0);
-            case 0:
-                break;
+            case 2: *n = PyTuple_GET_ITEM(args, 1);
+            case 1: *a = PyTuple_GET_ITEM(args, 0);
+            case 0: break;
             default:
                 TYPE_ERR("wrong number of arguments");
                 return 0;
@@ -477,24 +467,24 @@ parse_partition(PyObject * args,
                 return 0;
         }
     }
+
     return 1;
+
 }
 
 static inline int
-parse_rankdata(PyObject * args,
-               PyObject * kwds,
+parse_rankdata(PyObject *args,
+               PyObject *kwds,
                PyObject **a,
                PyObject **axis) {
     const Py_ssize_t nargs = PyTuple_GET_SIZE(args);
     const Py_ssize_t nkwds = kwds == NULL ? 0 : PyDict_Size(kwds);
     if (nkwds) {
-        int       nkwds_found = 0;
-        PyObject *tmp = NULL;
+        int nkwds_found = 0;
+        PyObject *tmp;
         switch (nargs) {
-            case 1:
-                *a = PyTuple_GET_ITEM(args, 0);
-            case 0:
-                break;
+            case 1: *a = PyTuple_GET_ITEM(args, 0);
+            case 0: break;
             default:
                 TYPE_ERR("wrong number of arguments");
                 return 0;
@@ -538,27 +528,26 @@ parse_rankdata(PyObject * args,
                 return 0;
         }
     }
+
     return 1;
+
 }
 
 static inline int
-parse_push(PyObject * args,
-           PyObject * kwds,
+parse_push(PyObject *args,
+           PyObject *kwds,
            PyObject **a,
            PyObject **n,
            PyObject **axis) {
     const Py_ssize_t nargs = PyTuple_GET_SIZE(args);
     const Py_ssize_t nkwds = kwds == NULL ? 0 : PyDict_Size(kwds);
     if (nkwds) {
-        int       nkwds_found = 0;
-        PyObject *tmp = NULL;
+        int nkwds_found = 0;
+        PyObject *tmp;
         switch (nargs) {
-            case 2:
-                *n = PyTuple_GET_ITEM(args, 1);
-            case 1:
-                *a = PyTuple_GET_ITEM(args, 0);
-            case 0:
-                break;
+            case 2: *n = PyTuple_GET_ITEM(args, 1);
+            case 1: *a = PyTuple_GET_ITEM(args, 0);
+            case 0: break;
             default:
                 TYPE_ERR("wrong number of arguments");
                 return 0;
@@ -610,23 +599,27 @@ parse_push(PyObject * args,
                 return 0;
         }
     }
+
     return 1;
+
 }
 
 static PyObject *
-nonreducer_axis(char *     name,
-                PyObject * args,
-                PyObject * kwds,
-                nra_t      nra_float64,
-                nra_t      nra_float32,
-                nra_t      nra_int64,
-                nra_t      nra_int32,
+nonreducer_axis(char *name,
+                PyObject *args,
+                PyObject *kwds,
+                nra_t nra_float64,
+                nra_t nra_float32,
+                nra_t nra_int64,
+                nra_t nra_int32,
                 parse_type parse) {
-    int n = -1;
+
+    int n;
     int axis;
+    int dtype;
 
     PyArrayObject *a;
-    PyObject *     y = NULL;
+    PyObject *y;
 
     PyObject *a_obj = NULL;
     PyObject *n_obj = NULL;
@@ -661,7 +654,6 @@ nonreducer_axis(char *     name,
 
     /* check for byte swapped input array */
     if (PyArray_ISBYTESWAPPED(a)) {
-        Py_DECREF(a);
         return slow(name, args, kwds);
     }
 
@@ -671,8 +663,7 @@ nonreducer_axis(char *     name,
             axis = PyArray_NDIM(a) - 1;
             if (axis < 0) {
                 PyErr_Format(PyExc_ValueError,
-                             "axis(=%d) out of bounds",
-                             axis);
+                             "axis(=%d) out of bounds", axis);
                 goto error;
             }
         } else {
@@ -700,8 +691,7 @@ nonreducer_axis(char *     name,
             axis += PyArray_NDIM(a);
             if (axis < 0) {
                 PyErr_Format(PyExc_ValueError,
-                             "axis(=%d) out of bounds",
-                             axis);
+                             "axis(=%d) out of bounds", axis);
                 goto error;
             }
         } else if (axis >= PyArray_NDIM(a)) {
@@ -727,18 +717,12 @@ nonreducer_axis(char *     name,
         }
     }
 
-    int dtype = PyArray_TYPE(a);
-    if (dtype == NPY_float64) {
-        y = nra_float64(a, axis, n);
-    } else if (dtype == NPY_float32) {
-        y = nra_float32(a, axis, n);
-    } else if (dtype == NPY_int64) {
-        y = nra_int64(a, axis, n);
-    } else if (dtype == NPY_int32) {
-        y = nra_int32(a, axis, n);
-    } else {
-        y = slow(name, args, kwds);
-    }
+    dtype = PyArray_TYPE(a);
+    if      (dtype == NPY_float64) y = nra_float64(a, axis, n);
+    else if (dtype == NPY_float32) y = nra_float32(a, axis, n);
+    else if (dtype == NPY_int64)   y = nra_int64(a, axis, n);
+    else if (dtype == NPY_int32)   y = nra_int32(a, axis, n);
+    else                           y = slow(name, args, kwds);
 
     Py_DECREF(a);
 
@@ -747,12 +731,11 @@ nonreducer_axis(char *     name,
 error:
     Py_DECREF(a);
     return NULL;
+
 }
 
 /* docstrings ------------------------------------------------------------- */
 
-// clang-format off
-
 static char nra_doc[] =
 "Bottleneck non-reducing functions that operate along an axis.";
 
@@ -1027,16 +1010,15 @@ nra_methods[] = {
     {NULL, NULL, 0, NULL}
 };
 
-// clang-format off
 
 #if PY_MAJOR_VERSION >= 3
 static struct PyModuleDef
 nra_def = {
-    PyModuleDef_HEAD_INIT,
-    "nonreduce_axis",
-    nra_doc,
-    -1,
-    nra_methods
+   PyModuleDef_HEAD_INIT,
+   "nonreduce_axis",
+   nra_doc,
+   -1,
+   nra_methods
 };
 #endif
 
diff --git a/bottleneck/src/nonreduce_template.c b/bottleneck/src/nonreduce_template.c
index df0c05d864..ac058f8800 100644
--- a/bottleneck/src/nonreduce_template.c
+++ b/bottleneck/src/nonreduce_template.c
@@ -9,14 +9,14 @@
 typedef PyObject *(*nr_t)(PyArrayObject *, double, double);
 
 static PyObject *
-nonreducer(char *    name,
+nonreducer(char *name,
            PyObject *args,
            PyObject *kwds,
-           nr_t      nr_float64,
-           nr_t      nr_float32,
-           nr_t      nr_int64,
-           nr_t      nr_int32,
-           int       inplace);
+           nr_t,
+           nr_t,
+           nr_t,
+           nr_t,
+           int inplace);
 
 /* replace --------------------------------------------------------------- */
 
@@ -27,25 +27,23 @@ replace_DTYPE0(PyArrayObject *a, double old, double new) {
     init_iter_all(&it, a, 0, 1);
     BN_BEGIN_ALLOW_THREADS
     const npy_DTYPE0 oldf = (npy_DTYPE0)old;
-    const npy_DTYPE0 newf = (npy_DTYPE0) new;
+    const npy_DTYPE0 newf = (npy_DTYPE0)new;
     if (old == old) {
-        REDUCE_SPECIALIZE (
-            WHILE {
-                npy_DTYPE0 *array = PA(DTYPE0);
-                FOR {
-                    array[it.i * it.stride] = array[it.i * it.stride] == oldf ? newf : array[it.i * it.stride];
-                }
-                NEXT
-            })
+        WHILE {
+            npy_DTYPE0* array = PA(DTYPE0);
+            FOR {
+                array[it.i] = array[it.i] == oldf ? newf : array[it.i];
+            }
+            NEXT
+        }
     } else {
-        REDUCE_SPECIALIZE (
-            WHILE {
-                npy_DTYPE0 *array = PA(DTYPE0);
-                FOR {
-                    array[it.i * it.stride] = array[it.i * it.stride] != array[it.i * it.stride] ? newf : array[it.i * it.stride];
-                }
-                NEXT
-            })
+        WHILE {
+            npy_DTYPE0* array = PA(DTYPE0);
+            FOR {
+                array[it.i] = array[it.i] != array[it.i] ? newf : array[it.i];
+            }
+            NEXT
+        }
     }
     BN_END_ALLOW_THREADS
     Py_INCREF(a);
@@ -53,6 +51,7 @@ replace_DTYPE0(PyArrayObject *a, double old, double new) {
 }
 /* dtype end */
 
+
 /* dtype = [['int64'], ['int32']] */
 static BN_OPT_3 PyObject *
 replace_DTYPE0(PyArrayObject *a, double old, double new) {
@@ -60,7 +59,7 @@ replace_DTYPE0(PyArrayObject *a, double old, double new) {
     init_iter_all(&it, a, 0, 1);
     if (old == old) {
         const npy_DTYPE0 oldint = (npy_DTYPE0)old;
-        const npy_DTYPE0 newint = (npy_DTYPE0) new;
+        const npy_DTYPE0 newint = (npy_DTYPE0)new;
         if (oldint != old) {
             VALUE_ERR("Cannot safely cast `old` to int");
             return NULL;
@@ -70,26 +69,14 @@ replace_DTYPE0(PyArrayObject *a, double old, double new) {
             return NULL;
         }
         BN_BEGIN_ALLOW_THREADS
-        if (REDUCE_CONTIGUOUS) {
-            npy_DTYPE0 *   array = PA(DTYPE0);
-            const npy_intp nits = it.nits;
-            const npy_intp length = it.length;
-            for (npy_intp i = 0; i < nits; i++) {
-                // clang has a large perf regression when using the FOR macro here
-                for (npy_intp j = 0; j < length; j++) {
-                    array[i * nits + j] = array[i * nits + j] == oldint ? newint : array[i * nits + j];
-                }
-            }
-        } else {
-            WHILE {
-                const npy_intp stride = it.stride;
-                npy_DTYPE0 *   array = PA(DTYPE0);
-                // clang has a large perf regression when using the FOR macro here
-                for (npy_intp i = 0; i < it.length; i++) {
-                    array[i * stride] = array[i * stride] == oldint ? newint : array[i * stride];
-                }
-                NEXT
+        WHILE {
+            npy_DTYPE0* array = (npy_DTYPE0 *)it.pa;
+            npy_intp i;
+            // clang has a large perf regression when using the FOR macro here
+            for (i=0; i < it.length; i++) {
+                array[i] = array[i] == oldint ? newint : array[i];
             }
+            NEXT
         }
         BN_END_ALLOW_THREADS
     }
@@ -110,11 +97,12 @@ replace(PyObject *self, PyObject *args, PyObject *kwds) {
                       1);
 }
 
+
 /* python strings -------------------------------------------------------- */
 
-static PyObject *pystr_a = NULL;
-static PyObject *pystr_old = NULL;
-static PyObject *pystr_new = NULL;
+PyObject *pystr_a = NULL;
+PyObject *pystr_old = NULL;
+PyObject *pystr_new = NULL;
 
 static int
 intern_strings(void) {
@@ -127,8 +115,8 @@ intern_strings(void) {
 /* nonreduce ------------------------------------------------------------- */
 
 static inline int
-parse_args(PyObject * args,
-           PyObject * kwds,
+parse_args(PyObject *args,
+           PyObject *kwds,
            PyObject **a,
            PyObject **old,
            PyObject **new) {
@@ -137,12 +125,9 @@ parse_args(PyObject * args,
     if (nkwds) {
         int nkwds_found = 0;
         switch (nargs) {
-            case 2:
-                *old = PyTuple_GET_ITEM(args, 1);
-            case 1:
-                *a = PyTuple_GET_ITEM(args, 0);
-            case 0:
-                break;
+            case 2: *old = PyTuple_GET_ITEM(args, 1);
+            case 1: *a = PyTuple_GET_ITEM(args, 0);
+            case 0: break;
             default:
                 TYPE_ERR("wrong number of arguments 1");
                 return 0;
@@ -194,28 +179,31 @@ parse_args(PyObject * args,
                 return 0;
         }
     }
+
     return 1;
+
 }
 
 static PyObject *
-nonreducer(char *    name,
+nonreducer(char *name,
            PyObject *args,
            PyObject *kwds,
-           nr_t      nr_float64,
-           nr_t      nr_float32,
-           nr_t      nr_int64,
-           nr_t      nr_int32,
-           int       inplace) {
-    PyArrayObject *a = NULL;
-    PyObject *     y = NULL;
+           nr_t nr_float64,
+           nr_t nr_float32,
+           nr_t nr_int64,
+           nr_t nr_int32,
+           int inplace) {
+    int dtype;
+    double old, new;
+
+    PyArrayObject *a;
+    PyObject *y;
 
     PyObject *a_obj = NULL;
     PyObject *old_obj = NULL;
     PyObject *new_obj = NULL;
 
-    if (!parse_args(args, kwds, &a_obj, &old_obj, &new_obj)) {
-        return NULL;
-    }
+    if (!parse_args(args, kwds, &a_obj, &old_obj, &new_obj)) return NULL;
 
     /* convert to array if necessary */
     if (PyArray_Check(a_obj)) {
@@ -223,7 +211,8 @@ nonreducer(char *    name,
         Py_INCREF(a);
     } else {
         if (inplace) {
-            TYPE_ERR("works in place so input must be an array, not (e.g.) a list");
+            TYPE_ERR("works in place so input must be an array, "
+                     "not (e.g.) a list");
             return NULL;
         }
         a = (PyArrayObject *)PyArray_FROM_O(a_obj);
@@ -234,7 +223,6 @@ nonreducer(char *    name,
 
     /* check for byte swapped input array */
     if (PyArray_ISBYTESWAPPED(a)) {
-        Py_DECREF(a);
         return slow(name, args, kwds);
     }
 
@@ -242,37 +230,33 @@ nonreducer(char *    name,
     if (old_obj == NULL) {
         RUNTIME_ERR("`old_obj` should never be NULL; please report this bug.");
         goto error;
-    }
-    const double old_value = PyFloat_AsDouble(old_obj);
-    if (error_converting(old_value)) {
-        TYPE_ERR("`old` must be a number");
-        goto error;
+    } else {
+        old = PyFloat_AsDouble(old_obj);
+        if (error_converting(old)) {
+            TYPE_ERR("`old` must be a number");
+            goto error;
+        }
     }
 
     /* new */
     if (new_obj == NULL) {
         RUNTIME_ERR("`new_obj` should never be NULL; please report this bug.");
         goto error;
-    }
-    const double new_value = PyFloat_AsDouble(new_obj);
-    if (error_converting(new_value)) {
-        TYPE_ERR("`new` must be a number");
-        goto error;
+    } else {
+        new = PyFloat_AsDouble(new_obj);
+        if (error_converting(new)) {
+            TYPE_ERR("`new` must be a number");
+            goto error;
+        }
     }
 
-    const int dtype = PyArray_TYPE(a);
+    dtype = PyArray_TYPE(a);
 
-    if (dtype == NPY_float64) {
-        y = nr_float64(a, old_value, new_value);
-    } else if (dtype == NPY_float32) {
-        y = nr_float32(a, old_value, new_value);
-    } else if (dtype == NPY_int64) {
-        y = nr_int64(a, old_value, new_value);
-    } else if (dtype == NPY_int32) {
-        y = nr_int32(a, old_value, new_value);
-    } else {
-        y = slow(name, args, kwds);
-    }
+    if      (dtype == NPY_float64) y = nr_float64(a, old, new);
+    else if (dtype == NPY_float32) y = nr_float32(a, old, new);
+    else if (dtype == NPY_int64)   y = nr_int64(a, old, new);
+    else if (dtype == NPY_int32)   y = nr_int32(a, old, new);
+    else                           y = slow(name, args, kwds);
 
     Py_DECREF(a);
 
@@ -281,13 +265,13 @@ nonreducer(char *    name,
 error:
     Py_DECREF(a);
     return NULL;
+
 }
 
 /* docstrings ------------------------------------------------------------- */
 
-// clang-format off
-
-static char nonreduce_doc[] = "Bottleneck nonreducing functions.";
+static char nonreduce_doc[] =
+"Bottleneck nonreducing functions.";
 
 static char replace_doc[] =
 /* MULTILINE STRING BEGIN
@@ -344,35 +328,35 @@ nonreduce_methods[] = {
     {NULL, NULL, 0, NULL}
 };
 
-// clang-format on
 
 #if PY_MAJOR_VERSION >= 3
 static struct PyModuleDef
-    nonreduce_def = {
-        PyModuleDef_HEAD_INIT,
-        "nonreduce",
-        nonreduce_doc,
-        -1,
-        nonreduce_methods};
+nonreduce_def = {
+   PyModuleDef_HEAD_INIT,
+   "nonreduce",
+   nonreduce_doc,
+   -1,
+   nonreduce_methods
+};
 #endif
 
+
 PyMODINIT_FUNC
 #if PY_MAJOR_VERSION >= 3
-    #define RETVAL m
+#define RETVAL m
 PyInit_nonreduce(void)
 #else
-    #define RETVAL
+#define RETVAL
 initnonreduce(void)
 #endif
 {
-#if PY_MAJOR_VERSION >= 3
-    PyObject *m = PyModule_Create(&nonreduce_def);
-#else
-    PyObject *m = Py_InitModule3("nonreduce", nonreduce_methods, nonreduce_doc);
-#endif
-    if (m == NULL) {
-        return RETVAL;
-    }
+    #if PY_MAJOR_VERSION >=3
+        PyObject *m = PyModule_Create(&nonreduce_def);
+    #else
+        PyObject *m = Py_InitModule3("nonreduce", nonreduce_methods,
+                                     nonreduce_doc);
+    #endif
+    if (m == NULL) return RETVAL;
     import_array();
     if (!intern_strings()) {
         return RETVAL;
diff --git a/bottleneck/src/reduce_template.c b/bottleneck/src/reduce_template.c
index f80d96ab03..e906ec6b8f 100644
--- a/bottleneck/src/reduce_template.c
+++ b/bottleneck/src/reduce_template.c
@@ -3,37 +3,31 @@
 #include "bottleneck.h"
 #include "iterators.h"
 
-#ifdef _MSC_VER
-    #include "intrin.h"
-#elif HAVE_SSE2
-    #include "x86intrin.h"
-#endif
-
 /* init macros ----------------------------------------------------------- */
 
 #define INIT_ALL \
-    iter it;     \
+    iter it; \
     init_iter_all(&it, a, 0, 1);
 
 #define INIT_ALL_RAVEL \
-    iter it;           \
+    iter it; \
     init_iter_all(&it, a, 1, 0);
 
 #define INIT_ALL_RAVEL_ANY_ORDER \
-    iter it;                     \
+    iter it; \
     init_iter_all(&it, a, 1, 1);
 
 /* used with INIT_ALL_RAVEL */
-#define DECREF_INIT_ALL_RAVEL  \
-    if (it.a_ravel != NULL) {  \
+#define DECREF_INIT_ALL_RAVEL \
+    if (it.a_ravel != NULL) { \
         Py_DECREF(it.a_ravel); \
     }
 
-#define INIT_ONE(dtype0, dtype1)                         \
-    iter          it;                                    \
-    PyObject *    y;                                     \
-    npy_##dtype1 *py;                                    \
-    init_iter_one(&it, a, axis);                         \
+#define INIT_ONE(dtype0, dtype1) \
+    iter it; \
+    PyObject *y; \
+    npy_##dtype1 *py; \
+    init_iter_one(&it, a, axis); \
     y = PyArray_EMPTY(NDIM - 1, SHAPE, NPY_##dtype0, 0); \
     py = (npy_##dtype1 *)PyArray_DATA((PyArrayObject *)y);
 
@@ -41,30 +35,31 @@
 
 /* low-level functions such as nansum_all_float64 */
 #define REDUCE_ALL(name, dtype) \
-    static PyObject *           \
-        name##_all_##dtype(PyArrayObject *a, int ddof)
+    static PyObject * \
+    name##_all_##dtype(PyArrayObject *a, int ddof)
 
 /* low-level functions such as nansum_one_float64 */
 #define REDUCE_ONE(name, dtype) \
-    static PyObject *           \
-        name##_one_##dtype(PyArrayObject *a, int axis, int ddof)
+    static PyObject * \
+    name##_one_##dtype(PyArrayObject *a, int axis, int ddof)
 
 /* top-level functions such as nansum */
-#define REDUCE_MAIN(name, has_ddof)                        \
-    static PyObject *                                      \
-    name(PyObject *self, PyObject *args, PyObject *kwds) { \
-        return reducer(#name,                              \
-                       args,                               \
-                       kwds,                               \
-                       name##_all_float64,                 \
-                       name##_all_float32,                 \
-                       name##_all_int64,                   \
-                       name##_all_int32,                   \
-                       name##_one_float64,                 \
-                       name##_one_float32,                 \
-                       name##_one_int64,                   \
-                       name##_one_int32,                   \
-                       has_ddof);                          \
+#define REDUCE_MAIN(name, has_ddof) \
+    static PyObject * \
+    name(PyObject *self, PyObject *args, PyObject *kwds) \
+    { \
+        return reducer(#name, \
+                       args, \
+                       kwds, \
+                       name##_all_float64, \
+                       name##_all_float32, \
+                       name##_all_int64, \
+                       name##_all_int32, \
+                       name##_one_float64, \
+                       name##_one_float32, \
+                       name##_one_int64, \
+                       name##_one_int32, \
+                       has_ddof); \
     }
 
 /* typedefs and prototypes ----------------------------------------------- */
@@ -73,32 +68,30 @@ typedef PyObject *(*fall_t)(PyArrayObject *a, int ddof);
 typedef PyObject *(*fone_t)(PyArrayObject *a, int axis, int ddof);
 
 static PyObject *
-reducer(char *    name,
+reducer(char *name,
         PyObject *args,
         PyObject *kwds,
-        fall_t    fall_float64,
-        fall_t    fall_float32,
-        fall_t    fall_int64,
-        fall_t    fall_int32,
-        fone_t    fone_float64,
-        fone_t    fone_float32,
-        fone_t    fone_int64,
-        fone_t    fone_int32,
-        int       has_ddof);
+        fall_t fall_float64,
+        fall_t fall_float32,
+        fall_t fall_int64,
+        fall_t fall_int32,
+        fone_t fone_float64,
+        fone_t fone_float32,
+        fone_t fone_int64,
+        fone_t fone_int32,
+        int has_ddof);
 
 /* nansum ---------------------------------------------------------------- */
 
 /* dtype = [['float64'], ['float32']] */
 REDUCE_ALL(nansum, DTYPE0) {
-    npy_DTYPE0 asum = 0;
+    npy_DTYPE0 ai, asum = 0;
     INIT_ALL
     BN_BEGIN_ALLOW_THREADS
     WHILE {
         FOR {
-            const npy_DTYPE0 ai = AI(DTYPE0);
-            if (!bn_isnan(ai)) {
-                asum += ai;
-            }
+            ai = AI(DTYPE0);
+            if (ai == ai) asum += ai;
         }
         NEXT
     }
@@ -107,18 +100,17 @@ REDUCE_ALL(nansum, DTYPE0) {
 }
 
 REDUCE_ONE(nansum, DTYPE0) {
+    npy_DTYPE0 ai, asum;
     INIT_ONE(DTYPE0, DTYPE0)
     BN_BEGIN_ALLOW_THREADS
     if (LENGTH == 0) {
         FILL_Y(0)
     } else {
         WHILE {
-            npy_DTYPE0 asum = 0;
+            asum = 0;
             FOR {
-                const npy_DTYPE0 ai = AI(DTYPE0);
-                if (!bn_isnan(ai)) {
-                    asum += ai;
-                }
+                ai = AI(DTYPE0);
+                if (ai == ai) asum += ai;
             }
             YPP = asum;
             NEXT
@@ -129,74 +121,31 @@ REDUCE_ONE(nansum, DTYPE0) {
 }
 /* dtype end */
 
-/* dtype = [['int64', 'int64'], ['int32', 'long']] */
-BN_OPT_3
+/* dtype = [['int64'], ['int32']] */
 REDUCE_ALL(nansum, DTYPE0) {
-    npy_DTYPE1 asum = 0;
+    npy_DTYPE0 asum = 0;
     INIT_ALL
     BN_BEGIN_ALLOW_THREADS
-    if (REDUCE_CONTIGUOUS) {
-        npy_DTYPE0 *   pa = PA(DTYPE0);
-        const npy_intp nits = it.nits;
-        const npy_intp length = it.length;
-        for (npy_intp i = 0; i < nits; i++) {
-            for (npy_intp j = 0; j < length; j++) {
-                asum += pa[i * length + j];
-            }
-        }
-    } else {
-        WHILE {
-            npy_DTYPE0 *pa = PA(DTYPE0);
-            FOR {
-                asum += pa[it.i * it.stride];
-            }
-            NEXT
-        }
+    WHILE {
+        FOR asum += AI(DTYPE0);
+        NEXT
     }
     BN_END_ALLOW_THREADS
     return PyLong_FromLongLong(asum);
 }
 
-BN_OPT_3
 REDUCE_ONE(nansum, DTYPE0) {
-    INIT_ONE(DTYPE1, DTYPE1)
+    npy_DTYPE0 asum;
+    INIT_ONE(DTYPE0, DTYPE0)
     BN_BEGIN_ALLOW_THREADS
     if (LENGTH == 0) {
         FILL_Y(0)
     } else {
-        if (ONE_CONTIGUOUS) {
-            WHILE {
-                npy_DTYPE1        asum = 0;
-                const npy_DTYPE0 *pa = PA(DTYPE0);
-                for (npy_intp i = 0; i < LENGTH; i++) {
-                    asum += pa[i];
-                }
-                YPP = asum;
-                NEXT
-            }
-        } else if (ONE_TRANSPOSE(npy_DTYPE0)) {
-            const npy_intp    LOOP_SIZE = it.nits;
-            const npy_DTYPE0 *pa = PA(DTYPE0);
-
-            for (npy_intp i = 0; i < LOOP_SIZE; i++) {
-                py[i] = 0;
-            }
-
-            for (npy_intp i = 0; i < LENGTH; i++) {
-                for (npy_intp j = 0; j < LOOP_SIZE; j++) {
-                    py[j] += pa[i * LOOP_SIZE + j];
-                }
-            }
-        } else {
-            WHILE {
-                npy_DTYPE1        asum = 0;
-                const npy_DTYPE0 *pa = PA(DTYPE0);
-                FOR {
-                    asum += SI(pa);
-                }
-                YPP = asum;
-                NEXT
-            }
+        WHILE {
+            asum = 0;
+            FOR asum += AI(DTYPE0);
+            YPP = asum;
+            NEXT
         }
     }
     BN_END_ALLOW_THREADS
@@ -206,18 +155,19 @@ REDUCE_ONE(nansum, DTYPE0) {
 
 REDUCE_MAIN(nansum, 0)
 
+
 /* nanmean ---------------------------------------------------------------- */
 
 /* dtype = [['float64'], ['float32']] */
 REDUCE_ALL(nanmean, DTYPE0) {
     Py_ssize_t count = 0;
-    npy_DTYPE0 asum = 0;
+    npy_DTYPE0 ai, asum = 0;
     INIT_ALL
     BN_BEGIN_ALLOW_THREADS
     WHILE {
         FOR {
-            const npy_DTYPE0 ai = AI(DTYPE0);
-            if (!bn_isnan(ai)) {
+            ai = AI(DTYPE0);
+            if (ai == ai) {
                 asum += ai;
                 count += 1;
             }
@@ -233,17 +183,19 @@ REDUCE_ALL(nanmean, DTYPE0) {
 }
 
 REDUCE_ONE(nanmean, DTYPE0) {
+    Py_ssize_t count;
+    npy_DTYPE0 ai, asum;
     INIT_ONE(DTYPE0, DTYPE0)
     BN_BEGIN_ALLOW_THREADS
     if (LENGTH == 0) {
         FILL_Y(BN_NAN)
     } else {
         WHILE {
-            Py_ssize_t count = 0;
-            npy_DTYPE0 asum = 0;
+            count = 0;
+            asum = 0;
             FOR {
-                const npy_DTYPE0 ai = AI(DTYPE0);
-                if (!bn_isnan(ai)) {
+                ai = AI(DTYPE0);
+                if (ai == ai) {
                     asum += ai;
                     count += 1;
                 }
@@ -269,9 +221,7 @@ REDUCE_ALL(nanmean, DTYPE0) {
     INIT_ALL
     BN_BEGIN_ALLOW_THREADS
     WHILE {
-        FOR {
-            asum += AI(DTYPE0);
-        }
+        FOR asum += AI(DTYPE0);
         total_length += LENGTH;
         NEXT
     }
@@ -284,16 +234,15 @@ REDUCE_ALL(nanmean, DTYPE0) {
 }
 
 REDUCE_ONE(nanmean, DTYPE0) {
+    npy_DTYPE1 asum;
     INIT_ONE(DTYPE1, DTYPE1)
     BN_BEGIN_ALLOW_THREADS
     if (LENGTH == 0) {
         FILL_Y(BN_NAN)
     } else {
         WHILE {
-            npy_DTYPE1 asum = 0;
-            FOR {
-                asum += AI(DTYPE0);
-            }
+            asum = 0;
+            FOR asum += AI(DTYPE0);
             if (LENGTH > 0) {
                 asum /= LENGTH;
             } else {
@@ -310,6 +259,7 @@ REDUCE_ONE(nanmean, DTYPE0) {
 
 REDUCE_MAIN(nanmean, 0)
 
+
 /* nanstd, nanvar- ------------------------------------------------------- */
 
 /* repeat = {'NAME': ['nanstd', 'nanvar'],
@@ -317,13 +267,13 @@ REDUCE_MAIN(nanmean, 0)
 /* dtype = [['float64'], ['float32']] */
 REDUCE_ALL(NAME, DTYPE0) {
     Py_ssize_t count = 0;
-    npy_DTYPE0 out, asum = 0;
+    npy_DTYPE0 ai, amean, out, asum = 0;
     INIT_ALL
     BN_BEGIN_ALLOW_THREADS
     WHILE {
         FOR {
-            const npy_DTYPE0 ai = AI(DTYPE0);
-            if (!bn_isnan(ai)) {
+            ai = AI(DTYPE0);
+            if (ai == ai) {
                 asum += ai;
                 count++;
             }
@@ -331,13 +281,13 @@ REDUCE_ALL(NAME, DTYPE0) {
         NEXT
     }
     if (count > ddof) {
-        const npy_DTYPE0 amean = asum / count;
+        amean = asum / count;
         asum = 0;
         RESET
         WHILE {
             FOR {
-                npy_DTYPE0 ai = AI(DTYPE0);
-                if (!bn_isnan(ai)) {
+                ai = AI(DTYPE0);
+                if (ai == ai) {
                     ai -= amean;
                     asum += ai * ai;
                 }
@@ -353,27 +303,29 @@ REDUCE_ALL(NAME, DTYPE0) {
 }
 
 REDUCE_ONE(NAME, DTYPE0) {
+    Py_ssize_t count;
+    npy_DTYPE0 ai, asum, amean;
     INIT_ONE(DTYPE0, DTYPE0)
     BN_BEGIN_ALLOW_THREADS
     if (LENGTH == 0) {
         FILL_Y(BN_NAN)
     } else {
         WHILE {
-            Py_ssize_t count = 0;
-            npy_DTYPE0 asum = 0;
+            count = 0;
+            asum = 0;
             FOR {
-                const npy_DTYPE0 ai = AI(DTYPE0);
-                if (!bn_isnan(ai)) {
+                ai = AI(DTYPE0);
+                if (ai == ai) {
                     asum += ai;
                     count++;
                 }
             }
             if (count > ddof) {
-                const npy_DTYPE0 amean = asum / count;
+                amean = asum / count;
                 asum = 0;
                 FOR {
-                    npy_DTYPE0 ai = AI(DTYPE0);
-                    if (!bn_isnan(ai)) {
+                    ai = AI(DTYPE0);
+                    if (ai == ai) {
                         ai -= amean;
                         asum += ai * ai;
                     }
@@ -395,23 +347,21 @@ REDUCE_ONE(NAME, DTYPE0) {
 REDUCE_ALL(NAME, DTYPE0) {
     npy_DTYPE1 out;
     Py_ssize_t size = 0;
-    npy_DTYPE1 asum = 0;
+    npy_DTYPE1 ai, amean, asum = 0;
     INIT_ALL
     BN_BEGIN_ALLOW_THREADS
     WHILE {
-        FOR {
-            asum += AI(DTYPE0);
-        }
+        FOR asum += AI(DTYPE0);
         size += LENGTH;
         NEXT
     }
     if (size > ddof) {
-        const npy_DTYPE1 amean = asum / size;
+        amean = asum / size;
         asum = 0;
         RESET
         WHILE {
             FOR {
-                const npy_DTYPE1 ai = AI(DTYPE0) - amean;
+                ai = AI(DTYPE0) - amean;
                 asum += ai * ai;
             }
             NEXT
@@ -425,23 +375,22 @@ REDUCE_ALL(NAME, DTYPE0) {
 }
 
 REDUCE_ONE(NAME, DTYPE0) {
+    npy_DTYPE1 ai, asum, amean, length_inv, length_ddof_inv;
     INIT_ONE(DTYPE1, DTYPE1)
     BN_BEGIN_ALLOW_THREADS
-    const npy_DTYPE1 length_inv = 1.0 / LENGTH;
-    const npy_DTYPE1 length_ddof_inv = 1.0 / (LENGTH - ddof);
+    length_inv = 1.0 / LENGTH;
+    length_ddof_inv = 1.0 / (LENGTH - ddof);
     if (LENGTH == 0) {
         FILL_Y(BN_NAN)
     } else {
         WHILE {
-            npy_DTYPE1 asum = 0;
-            FOR {
-                asum += AI(DTYPE0);
-            }
+            asum = 0;
+            FOR asum += AI(DTYPE0);
             if (LENGTH > ddof) {
-                const npy_DTYPE1 amean = asum * length_inv;
+                amean = asum * length_inv;
                 asum = 0;
                 FOR {
-                    const npy_DTYPE1 ai = AI(DTYPE0) - amean;
+                    ai = AI(DTYPE0) - amean;
                     asum += ai * ai;
                 }
                 asum = FUNC(asum * length_ddof_inv);
@@ -460,17 +409,17 @@ REDUCE_ONE(NAME, DTYPE0) {
 REDUCE_MAIN(NAME, 1)
 /* repeat end */
 
+
 /* nanmin, nanmax -------------------------------------------------------- */
 
 /* repeat = {'NAME':      ['nanmin',         'nanmax'],
-             'INTCOMP':   ['<',              '>'],
              'COMPARE':   ['<=',             '>='],
              'BIG_FLOAT': ['BN_INFINITY',    '-BN_INFINITY'],
              'BIG_INT':   ['NPY_MAX_DTYPE0', 'NPY_MIN_DTYPE0']} */
-/* dtype = [['float64', '16'], ['float32', '32']] */
-BN_OPT_3
+/* dtype = [['float64'], ['float32']] */
 REDUCE_ALL(NAME, DTYPE0) {
-    npy_DTYPE0 extreme = BIG_FLOAT;
+    npy_DTYPE0 ai, extreme = BIG_FLOAT;
+    int allnan = 1;
     INIT_ALL
     if (SIZE == 0) {
         VALUE_ERR("numpy.NAME raises on a.size==0 and axis=None; "
@@ -478,75 +427,24 @@ REDUCE_ALL(NAME, DTYPE0) {
         return NULL;
     }
     BN_BEGIN_ALLOW_THREADS
-    npy_bool is_allnan = 1;
-    if (REDUCE_CONTIGUOUS) {
-        const npy_intp LOOP_SIZE = DTYPE1;
-        // Allow vectorization by keeping an array of extremums
-        npy_DTYPE0 extremes[DTYPE1];
-        for (npy_intp i = 0; i < LOOP_SIZE; i++) {
-            extremes[i] = BIG_FLOAT;
-        }
-        const npy_intp loops = LENGTH / LOOP_SIZE;
-        const npy_intp residual = LENGTH % LOOP_SIZE;
-        WHILE {
-            const npy_DTYPE0 *pa = PA(DTYPE0);
-            npy_intp          i = 0;
-            for (; i < loops && is_allnan; i++) {
-                for (npy_intp j = 0; j < LOOP_SIZE; j++) {
-                    const npy_DTYPE0 ai = pa[i * LOOP_SIZE + j];
-                    // We only need <=/>= until we identify our first non-nan value
-                    if (ai COMPARE extremes[j]) {
-                        is_allnan = 0;
-                        extremes[j] = ai;
-                    }
-                }
-            }
-            for (; i < loops; i++) {
-                // Fully vectorize since the result cannot be NaN now
-                for (npy_intp j = 0; j < LOOP_SIZE; j++) {
-                    const npy_DTYPE0         ai = pa[i * LOOP_SIZE + j];
-                    extremes[j] = ai INTCOMP extremes[j] ? ai : extremes[j];
-                }
-            }
-            for (npy_intp j = 0; j < residual; j++) {
-                const npy_DTYPE0 ai = pa[loops * LOOP_SIZE + j];
-                if (ai COMPARE extremes[j]) {
-                    is_allnan = 0;
-                    extremes[j] = ai;
-                }
-            }
-            NEXT
-        }
-        // Reduce our array to find the global max/min
-        for (npy_intp j = 0; j < LOOP_SIZE; j++) {
-            if (extremes[j] COMPARE extreme) {
-                extreme = extremes[j];
-            }
-        }
-    } else {
-        WHILE {
-            const npy_DTYPE0 *pa = PA(DTYPE0);
-            FOR {
-                const npy_DTYPE0 ai = pa[it.i * it.stride];
-                if (ai COMPARE extreme) {
-                    extreme = ai;
-                    is_allnan = 0;
-                }
+    WHILE {
+        FOR {
+            ai = AI(DTYPE0);
+            if (ai COMPARE extreme) {
+                extreme = ai;
+                allnan = 0;
             }
-            NEXT
         }
+        NEXT
     }
-    if (is_allnan) {
-        extreme = BN_NAN;
-    }
+    if (allnan) extreme = BN_NAN;
     BN_END_ALLOW_THREADS
     return PyFloat_FromDouble(extreme);
 }
 
-BN_OPT_3
 REDUCE_ONE(NAME, DTYPE0) {
-    npy_DTYPE0 extreme;
-    npy_bool   is_allnan;
+    npy_DTYPE0 ai, extreme;
+    int allnan;
     INIT_ONE(DTYPE0, DTYPE0)
     if (LENGTH == 0) {
         VALUE_ERR("numpy.NAME raises on a.shape[axis]==0; "
@@ -554,114 +452,19 @@ REDUCE_ONE(NAME, DTYPE0) {
         return NULL;
     }
     BN_BEGIN_ALLOW_THREADS
-    if (ONE_TRANSPOSE(npy_DTYPE0)) {
-        npy_intp    LOOP_SIZE = it.nits;
-        npy_DTYPE0 *extremes = malloc(LOOP_SIZE * sizeof(npy_DTYPE0));
-        npy_bool *  allnans = malloc(LOOP_SIZE * sizeof(npy_bool));
-        for (npy_intp i = 0; i < LOOP_SIZE; i++) {
-            extremes[i] = BIG_FLOAT;
-            allnans[i] = 1;
-        }
-
-        const npy_DTYPE0 *pa = PA(DTYPE0);
-        npy_intp          i = 0;
-        for (; i < LENGTH; i++) {
-            for (npy_intp j = 0; j < LOOP_SIZE; j++) {
-                const npy_DTYPE0 ai = pa[i * LOOP_SIZE + j];
-                if (ai COMPARE extremes[j]) {
-                    allnans[j] = 0;
-                    extremes[j] = ai;
-                }
-            }
-            if (i % 8 == 0) {
-                npy_intp nan_count = 0;
-                for (npy_intp j = 0; j < LOOP_SIZE; j++) {
-                    nan_count += allnans[j];
-                }
-                if (nan_count == 0) {
-                    break;
-                }
-            }
-        }
-        for (; i < LENGTH; i++) {
-            for (npy_intp j = 0; j < LOOP_SIZE; j++) {
-                const npy_DTYPE0         ai = pa[i * LOOP_SIZE + j];
-                extremes[j] = ai INTCOMP extremes[j] ? ai : extremes[j];
-            }
-        }
-        for (npy_intp k = 0; k < LOOP_SIZE; k++) {
-            if (allnans[k]) {
-                py[k] = BN_NAN;
-            } else {
-                py[k] = extremes[k];
-            }
-        }
-        free(extremes);
-        free(allnans);
-    } else if (ONE_CONTIGUOUS) {
-        const npy_intp LOOP_SIZE = DTYPE1;
-        npy_DTYPE0     extremes[DTYPE1];
-        const npy_intp loops = LENGTH / LOOP_SIZE;
-        const npy_intp residual = LENGTH % LOOP_SIZE;
-        WHILE {
-            const npy_DTYPE0 *pa = PA(DTYPE0);
-
-            for (npy_intp i = 0; i < LOOP_SIZE; i++) {
-                extremes[i] = BIG_FLOAT;
-            }
-            is_allnan = 1;
-            extreme = BIG_FLOAT;
-
-            npy_intp i = 0;
-            for (; i < loops && is_allnan; i++) {
-                for (npy_intp j = 0; j < LOOP_SIZE; j++) {
-                    const npy_DTYPE0 ai = pa[i * LOOP_SIZE + j];
-                    if (ai COMPARE extremes[j]) {
-                        is_allnan = 0;
-                        extremes[j] = ai;
-                    }
-                }
-            }
-            for (; i < loops; i++) {
-                for (npy_intp j = 0; j < LOOP_SIZE; j++) {
-                    const npy_DTYPE0         ai = pa[i * LOOP_SIZE + j];
-                    extremes[j] = ai INTCOMP extremes[j] ? ai : extremes[j];
-                }
-            }
-            for (npy_intp j = 0; j < residual; j++) {
-                const npy_DTYPE0 ai = pa[i * LOOP_SIZE + j];
-                if (ai COMPARE extremes[j]) {
-                    is_allnan = 0;
-                    extremes[j] = ai;
-                }
-            }
-            for (npy_intp j = 0; j < LOOP_SIZE; j++) {
-                if (extremes[j] COMPARE extreme) {
-                    extreme = extremes[j];
-                }
-            }
-
-            if (is_allnan) {
-                extreme = BN_NAN;
-            }
-            YPP = extreme;
-            NEXT
-        }
-    } else {
-        WHILE {
-            extreme = BIG_FLOAT;
-            is_allnan = 1;
-            FOR {
-                const npy_DTYPE0 ai = AI(DTYPE0);
-                if (ai COMPARE extreme) {
-                    extreme = ai;
-                    is_allnan = 0;
-                }
+    WHILE {
+        extreme = BIG_FLOAT;
+        allnan = 1;
+        FOR {
+            ai = AI(DTYPE0);
+            if (ai COMPARE extreme) {
+                extreme = ai;
+                allnan = 0;
             }
-            if (is_allnan) extreme = BN_NAN;
-            YPP = extreme;
-            NEXT
         }
+        if (allnan) extreme = BN_NAN;
+        YPP = extreme;
+        NEXT
     }
     BN_END_ALLOW_THREADS
     return y;
@@ -669,9 +472,8 @@ REDUCE_ONE(NAME, DTYPE0) {
 /* dtype end */
 
 /* dtype = [['int64'], ['int32']] */
-BN_OPT_3
 REDUCE_ALL(NAME, DTYPE0) {
-    npy_DTYPE0 extreme = BIG_INT;
+    npy_DTYPE0 ai, extreme = BIG_INT;
     INIT_ALL
     if (SIZE == 0) {
         VALUE_ERR("numpy.NAME raises on a.size==0 and axis=None; "
@@ -679,32 +481,19 @@ REDUCE_ALL(NAME, DTYPE0) {
         return NULL;
     }
     BN_BEGIN_ALLOW_THREADS
-    if (REDUCE_CONTIGUOUS) {
-        WHILE {
-            const npy_DTYPE0 *pa = PA(DTYPE0);
-            FOR {
-                const npy_DTYPE0     ai = pa[it.i];
-                extreme = ai INTCOMP extreme ? ai : extreme;
-            }
-            NEXT
-        }
-    } else {
-        WHILE {
-            const npy_DTYPE0 *pa = PA(DTYPE0);
-            FOR {
-                const npy_DTYPE0     ai = pa[it.i * it.stride];
-                extreme = ai INTCOMP extreme ? ai : extreme;
-            }
-            NEXT
+    WHILE {
+        FOR {
+            ai = AI(DTYPE0);
+            if (ai COMPARE extreme) extreme = ai;
         }
+        NEXT
     }
     BN_END_ALLOW_THREADS
     return PyLong_FromLongLong(extreme);
 }
 
-BN_OPT_3
 REDUCE_ONE(NAME, DTYPE0) {
-    npy_DTYPE0 extreme;
+    npy_DTYPE0 ai, extreme;
     INIT_ONE(DTYPE0, DTYPE0)
     if (LENGTH == 0) {
         VALUE_ERR("numpy.NAME raises on a.shape[axis]==0; "
@@ -712,47 +501,14 @@ REDUCE_ONE(NAME, DTYPE0) {
         return NULL;
     }
     BN_BEGIN_ALLOW_THREADS
-    if (ONE_TRANSPOSE(npy_DTYPE0)) {
-        npy_intp    LOOP_SIZE = it.nits;
-        npy_DTYPE0 *extremes = malloc(LOOP_SIZE * sizeof(npy_DTYPE0));
-        for (npy_intp i = 0; i < LOOP_SIZE; i++) {
-            extremes[i] = BIG_INT;
-        }
-
-        const npy_DTYPE0 *pa = PA(DTYPE0);
-        for (npy_intp i = 0; i < LENGTH; i++) {
-            for (npy_intp j = 0; j < LOOP_SIZE; j++) {
-                const npy_DTYPE0         ai = pa[i * LOOP_SIZE + j];
-                extremes[j] = ai INTCOMP extremes[j] ? ai : extremes[j];
-            }
-        }
-        for (npy_intp i = 0; i < LOOP_SIZE; i++) {
-            py[i] = extremes[i];
-        }
-        free(extremes);
-    } else if (ONE_CONTIGUOUS) {
-        WHILE {
-            extreme = BIG_INT;
-            const npy_DTYPE0 *pa = PA(DTYPE0);
-            FOR {
-                const npy_DTYPE0     ai = pa[it.i];
-                extreme = ai INTCOMP extreme ? ai : extreme;
-            }
-            YPP = extreme;
-            NEXT
-        }
-    } else {
-        WHILE {
-            extreme = BIG_INT;
-            FOR {
-                const npy_DTYPE0 ai = AI(DTYPE0);
-                if (ai INTCOMP extreme) {
-                    extreme = ai;
-                }
-            }
-            YPP = extreme;
-            NEXT
+    WHILE {
+        extreme = BIG_INT;
+        FOR {
+            ai = AI(DTYPE0);
+            if (ai COMPARE extreme) extreme = ai;
         }
+        YPP = extreme;
+        NEXT
     }
     BN_END_ALLOW_THREADS
     return y;
@@ -762,17 +518,17 @@ REDUCE_ONE(NAME, DTYPE0) {
 REDUCE_MAIN(NAME, 0)
 /* repeat end */
 
+
 /* nanargmin, nanargmax -------------------------------------------------- */
 
 /* repeat = {'NAME':      ['nanargmin',      'nanargmax'],
              'COMPARE':   ['<=',             '>='],
-             'INTCOMP':   ['<',              '>'],
              'BIG_FLOAT': ['BN_INFINITY',    '-BN_INFINITY'],
              'BIG_INT':   ['NPY_MAX_DTYPE0', 'NPY_MIN_DTYPE0']} */
 /* dtype = [['float64'], ['float32']] */
 REDUCE_ALL(NAME, DTYPE0) {
-    npy_DTYPE0 extreme = BIG_FLOAT;
-    npy_bool   is_allnan = 1;
+    npy_DTYPE0 ai, extreme = BIG_FLOAT;
+    int allnan = 1;
     Py_ssize_t idx = 0;
     INIT_ALL_RAVEL
     if (SIZE == 0) {
@@ -783,16 +539,16 @@ REDUCE_ALL(NAME, DTYPE0) {
     }
     BN_BEGIN_ALLOW_THREADS
     FOR_REVERSE {
-        const npy_DTYPE0 ai = AI(DTYPE0);
+        ai = AI(DTYPE0);
         if (ai COMPARE extreme) {
             extreme = ai;
-            is_allnan = 0;
+            allnan = 0;
             idx = INDEX;
         }
     }
     BN_END_ALLOW_THREADS
     DECREF_INIT_ALL_RAVEL
-    if (is_allnan) {
+    if (allnan) {
         VALUE_ERR("All-NaN slice encountered");
         return NULL;
     } else {
@@ -801,7 +557,9 @@ REDUCE_ALL(NAME, DTYPE0) {
 }
 
 REDUCE_ONE(NAME, DTYPE0) {
-    int err_code = 0;
+    int allnan, err_code = 0;
+    Py_ssize_t idx = 0;
+    npy_DTYPE0 ai, extreme;
     INIT_ONE(INTP, intp)
     if (LENGTH == 0) {
         VALUE_ERR("numpy.NAME raises on a.shape[axis]==0; "
@@ -810,18 +568,17 @@ REDUCE_ONE(NAME, DTYPE0) {
     }
     BN_BEGIN_ALLOW_THREADS
     WHILE {
-        npy_DTYPE0 extreme = BIG_FLOAT;
-        npy_bool   is_allnan = 1;
-        npy_intp   idx = 0;
+        extreme = BIG_FLOAT;
+        allnan = 1;
         FOR_REVERSE {
-            const npy_DTYPE0 ai = AI(DTYPE0);
+            ai = AI(DTYPE0);
             if (ai COMPARE extreme) {
                 extreme = ai;
-                is_allnan = 0;
+                allnan = 0;
                 idx = INDEX;
             }
         }
-        if (is_allnan == 0) {
+        if (allnan == 0) {
             YPP = idx;
         } else {
             err_code = 1;
@@ -838,10 +595,9 @@ REDUCE_ONE(NAME, DTYPE0) {
 /* dtype end */
 
 /* dtype = [['int64', 'intp'], ['int32', 'intp']] */
-BN_OPT_3
 REDUCE_ALL(NAME, DTYPE0) {
     npy_DTYPE1 idx = 0;
-    npy_DTYPE0 extreme = BIG_INT;
+    npy_DTYPE0 ai, extreme = BIG_INT;
     INIT_ALL_RAVEL
     if (SIZE == 0) {
         DECREF_INIT_ALL_RAVEL
@@ -850,10 +606,9 @@ REDUCE_ALL(NAME, DTYPE0) {
         return NULL;
     }
     BN_BEGIN_ALLOW_THREADS
-    const npy_DTYPE0 *pa = PA(DTYPE0);
-    FOR {
-        const npy_DTYPE0 ai = SI(pa);
-        if (ai INTCOMP extreme) {
+    FOR_REVERSE {
+        ai = AI(DTYPE0);
+        if (ai COMPARE extreme) {
             extreme = ai;
             idx = INDEX;
         }
@@ -863,8 +618,9 @@ REDUCE_ALL(NAME, DTYPE0) {
     return PyLong_FromLongLong(idx);
 }
 
-BN_OPT_3
 REDUCE_ONE(NAME, DTYPE0) {
+    npy_DTYPE1 idx = 0;
+    npy_DTYPE0 ai, extreme;
     INIT_ONE(DTYPE1, DTYPE1)
     if (LENGTH == 0) {
         VALUE_ERR("numpy.NAME raises on a.shape[axis]==0; "
@@ -873,12 +629,10 @@ REDUCE_ONE(NAME, DTYPE0) {
     }
     BN_BEGIN_ALLOW_THREADS
     WHILE {
-        npy_DTYPE0        extreme = BIG_INT;
-        npy_DTYPE1        idx = 0;
-        const npy_DTYPE0 *pa = PA(DTYPE0);
-        FOR {
-            const npy_DTYPE0 ai = SI(pa);
-            if (ai INTCOMP extreme) {
+        extreme = BIG_INT;
+        FOR_REVERSE {
+            ai = AI(DTYPE0);
+            if (ai COMPARE extreme) {
                 extreme = ai;
                 idx = INDEX;
             }
@@ -894,16 +648,17 @@ REDUCE_ONE(NAME, DTYPE0) {
 REDUCE_MAIN(NAME, 0)
 /* repeat end */
 
+
 /* ss ---------------------------------------------------------------- */
 
 /* dtype = [['float64'], ['float32']] */
 REDUCE_ALL(ss, DTYPE0) {
-    npy_DTYPE0 asum = 0;
+    npy_DTYPE0 ai, asum = 0;
     INIT_ALL
     BN_BEGIN_ALLOW_THREADS
     WHILE {
         FOR {
-            const npy_DTYPE0 ai = AI(DTYPE0);
+            ai = AI(DTYPE0);
             asum += ai * ai;
         }
         NEXT
@@ -913,15 +668,16 @@ REDUCE_ALL(ss, DTYPE0) {
 }
 
 REDUCE_ONE(ss, DTYPE0) {
+    npy_DTYPE0 ai, asum;
     INIT_ONE(DTYPE0, DTYPE0)
     BN_BEGIN_ALLOW_THREADS
     if (LENGTH == 0) {
         FILL_Y(0)
     } else {
         WHILE {
-            npy_DTYPE0 asum = 0;
+            asum = 0;
             FOR {
-                const npy_DTYPE0 ai = AI(DTYPE0);
+                ai = AI(DTYPE0);
                 asum += ai * ai;
             }
             YPP = asum;
@@ -933,43 +689,33 @@ REDUCE_ONE(ss, DTYPE0) {
 }
 /* dtype end */
 
-/* dtype = [['int64', 'int64'], ['int32', 'long']] */
-BN_OPT_3
+/* dtype = [['int64'], ['int32']] */
 REDUCE_ALL(ss, DTYPE0) {
-    npy_DTYPE1 asum = 0;
+    npy_DTYPE0 ai, asum = 0;
     INIT_ALL
     BN_BEGIN_ALLOW_THREADS
-    if (REDUCE_CONTIGUOUS) {
-        const npy_intp    count = it.nits * it.length;
-        const npy_DTYPE0 *pa = PA(DTYPE0);
-        for (npy_intp i = 0; i < count; i++) {
-            const npy_DTYPE0 ai = pa[i];
+    WHILE {
+        FOR {
+            ai = AI(DTYPE0);
             asum += ai * ai;
         }
-    } else {
-        WHILE {
-            FOR {
-                const npy_DTYPE0 ai = AI(DTYPE0);
-                asum += ai * ai;
-            }
-            NEXT
-        }
+        NEXT
     }
     BN_END_ALLOW_THREADS
     return PyLong_FromLongLong(asum);
 }
 
-BN_OPT_3
 REDUCE_ONE(ss, DTYPE0) {
-    INIT_ONE(DTYPE1, DTYPE1)
+    npy_DTYPE0 ai, asum;
+    INIT_ONE(DTYPE0, DTYPE0)
     BN_BEGIN_ALLOW_THREADS
     if (LENGTH == 0) {
         FILL_Y(0)
     } else {
         WHILE {
-            npy_DTYPE1 asum = 0;
+            asum = 0;
             FOR {
-                const npy_DTYPE0 ai = AI(DTYPE0);
+                ai = AI(DTYPE0);
                 asum += ai * ai;
             }
             YPP = asum;
@@ -983,86 +729,78 @@ REDUCE_ONE(ss, DTYPE0) {
 
 REDUCE_MAIN(ss, 0)
 
+
 /* median, nanmedian MACROS ---------------------------------------------- */
 
 #define B(dtype, i) buffer[i] /* used by PARTITION */
 
-#define EVEN_ODD(dtype, N)                 \
-    if (N % 2 == 0) {                      \
-        npy_##dtype amax = B(dtype, 0);    \
-        for (npy_intp i = 1; i < k; i++) { \
-            ai = B(dtype, i);              \
-            if (ai > amax) amax = ai;      \
-        }                                  \
-        med = 0.5 * (B(dtype, k) + amax);  \
-    } else {                               \
-        med = B(dtype, k);                 \
-    }
-
-#define MEDIAN(dtype)                       \
-    npy_intp    j, l, r, k;                 \
-    npy_##dtype ai;                         \
-    l = 0;                                  \
-    for (npy_intp i = 0; i < LENGTH; i++) { \
-        ai = AX(dtype, i);                  \
-        if (!bn_isnan(ai)) {                \
-            B(dtype, l++) = ai;             \
-        } else {                            \
-            break;                          \
-        }                                   \
-    }                                       \
-    if (l != LENGTH) {                      \
-        med = BN_NAN;                       \
-        goto done;                          \
-    }                                       \
-    k = LENGTH >> 1;                        \
-    l = 0;                                  \
-    r = LENGTH - 1;                         \
-    PARTITION(dtype)                        \
+#define EVEN_ODD(dtype, N) \
+    if (N % 2 == 0) { \
+        npy_##dtype amax = B(dtype, 0); \
+        for (i = 1; i < k; i++) { \
+            ai = B(dtype, i); \
+            if (ai > amax) amax = ai; \
+        } \
+        med = 0.5 * (B(dtype, k) + amax); \
+    } else { \
+        med =  B(dtype, k); \
+    } \
+
+#define MEDIAN(dtype) \
+    npy_intp j, l, r, k; \
+    npy_##dtype ai; \
+    l = 0; \
+    for (i = 0; i < LENGTH; i++) { \
+        ai = AX(dtype, i); \
+        if (ai == ai) { \
+            B(dtype, l++) = ai; \
+        } \
+    } \
+    if (l != LENGTH) { \
+        med = BN_NAN; \
+        goto done; \
+    } \
+    k = LENGTH >> 1; \
+    l = 0; \
+    r = LENGTH - 1; \
+    PARTITION(dtype) \
     EVEN_ODD(dtype, LENGTH)
 
-#define MEDIAN_INT(dtype)                       \
-    npy_intp           j, l, r, k;              \
-    npy_##dtype        ai;                      \
-    const npy_##dtype *pa = PA(dtype);          \
-    if (it.stride == 1) {                       \
-        for (npy_intp i = 0; i < LENGTH; i++) { \
-            B(dtype, i) = pa[i];                \
-        }                                       \
-    } else {                                    \
-        for (npy_intp i = 0; i < LENGTH; i++) { \
-            B(dtype, i) = SX(pa, i);            \
-        }                                       \
-    }                                           \
-    k = LENGTH >> 1;                            \
-    l = 0;                                      \
-    r = LENGTH - 1;                             \
-    PARTITION(dtype)                            \
+#define MEDIAN_INT(dtype) \
+    npy_intp j, l, r, k; \
+    npy_##dtype ai; \
+    for (i = 0; i < LENGTH; i++) { \
+        B(dtype, i) = AX(dtype, i); \
+    } \
+    k = LENGTH >> 1; \
+    l = 0; \
+    r = LENGTH - 1; \
+    PARTITION(dtype) \
     EVEN_ODD(dtype, LENGTH)
 
-#define NANMEDIAN(dtype)                    \
-    npy_intp    j, l, r, k, n;              \
-    npy_##dtype ai;                         \
-    l = 0;                                  \
-    for (npy_intp i = 0; i < LENGTH; i++) { \
-        ai = AX(dtype, i);                  \
-        if (!bn_isnan(ai)) {                \
-            B(dtype, l++) = ai;             \
-        }                                   \
-    }                                       \
-    n = l;                                  \
-    k = n >> 1;                             \
-    l = 0;                                  \
-    r = n - 1;                              \
-    if (n == 0) {                           \
-        med = BN_NAN;                       \
-        goto done;                          \
-    }                                       \
-    PARTITION(dtype)                        \
+#define NANMEDIAN(dtype) \
+    npy_intp j, l, r, k, n; \
+    npy_##dtype ai; \
+    l = 0; \
+    for (i = 0; i < LENGTH; i++) { \
+        ai = AX(dtype, i); \
+        if (ai == ai) { \
+            B(dtype, l++) = ai; \
+        } \
+    } \
+    n = l; \
+    k = n >> 1; \
+    l = 0; \
+    r = n - 1; \
+    if (n == 0) { \
+        med = BN_NAN; \
+        goto done; \
+    } \
+    PARTITION(dtype) \
     EVEN_ODD(dtype, n)
 
 #define BUFFER_NEW(dtype, length) \
-    npy_##dtype *buffer = calloc(length, sizeof(npy_##dtype));
+        npy_##dtype *buffer = malloc(length * sizeof(npy_##dtype));
 
 #define BUFFER_DELETE free(buffer);
 
@@ -1073,33 +811,35 @@ REDUCE_MAIN(ss, 0)
 /* dtype = [['float64', 'float64'], ['float32', 'float32']] */
 
 REDUCE_ALL(NAME, DTYPE0) {
-    npy_DTYPE1 med = BN_NAN;
+    npy_intp i;
+    npy_DTYPE1 med;
     INIT_ALL_RAVEL_ANY_ORDER
     BN_BEGIN_ALLOW_THREADS
+    BUFFER_NEW(DTYPE0, LENGTH)
     if (LENGTH == 0) {
         med = BN_NAN;
     } else {
-        BUFFER_NEW(DTYPE0, LENGTH)
         FUNC(DTYPE0)
-    done:
-        BUFFER_DELETE
     }
+    done:
+    BUFFER_DELETE
     BN_END_ALLOW_THREADS
     DECREF_INIT_ALL_RAVEL
     return PyFloat_FromDouble(med);
 }
 
 REDUCE_ONE(NAME, DTYPE0) {
+    npy_intp i;
+    npy_DTYPE1 med;
     INIT_ONE(DTYPE1, DTYPE1)
     BN_BEGIN_ALLOW_THREADS
     if (LENGTH == 0) {
         FILL_Y(BN_NAN)
     } else {
-        npy_DTYPE1 med = BN_NAN;
         BUFFER_NEW(DTYPE0, LENGTH)
         WHILE {
             FUNC(DTYPE0)
-        done:
+            done:
             YPP = med;
             NEXT
         }
@@ -1112,8 +852,8 @@ REDUCE_ONE(NAME, DTYPE0) {
 /* repeat end */
 
 /* dtype = [['int64', 'float64'], ['int32', 'float64']] */
-BN_OPT_3
 REDUCE_ALL(median, DTYPE0) {
+    npy_intp i;
     npy_DTYPE1 med;
     INIT_ALL_RAVEL_ANY_ORDER
     BN_BEGIN_ALLOW_THREADS
@@ -1129,14 +869,14 @@ REDUCE_ALL(median, DTYPE0) {
     return PyFloat_FromDouble(med);
 }
 
-BN_OPT_3
 REDUCE_ONE(median, DTYPE0) {
+    npy_intp i;
+    npy_DTYPE1 med;
     INIT_ONE(DTYPE1, DTYPE1)
     BN_BEGIN_ALLOW_THREADS
     if (LENGTH == 0) {
         FILL_Y(BN_NAN)
     } else {
-        npy_DTYPE1 med;
         BUFFER_NEW(DTYPE0, LENGTH)
         WHILE {
             MEDIAN_INT(DTYPE0)
@@ -1168,220 +908,49 @@ nanmedian(PyObject *self, PyObject *args, PyObject *kwds) {
                    0);
 }
 
-/* SSE2 anynan/allnan ---------------------------------------------------- */
-
-#if HAVE_SSE2
-
-int inline sse2_allnan_float64(const npy_float64 *pa) {
-    __m128d values = _mm_load_pd(pa);
-    __m128d result = _mm_cmpneq_pd(values, values);
-    /* If a value is NaN, a bit gets set to 1. As 2 64-bit floats fit in a
-       128-bit vector, 11 = 3 */
-    return _mm_movemask_pd(result) != 3;
-}
-
-int inline sse2_allnan_float32(const npy_float32 *pa) {
-    __m128 values = _mm_load_ps(pa);
-    __m128 result = _mm_cmpneq_ps(values, values);
-    /* If a value is NaN, a bit gets set to 1. As 4 32-bit floats fit in a
-       128-bit vector, 1111 = 15 */
-    return _mm_movemask_ps(result) != 15;
-}
-
-int inline sse2_anynan_float64(const npy_float64 *pa) {
-    __m128d values = _mm_load_pd(pa);
-    __m128d result = _mm_cmpneq_pd(values, values);
-    return _mm_movemask_pd(result) > 0;
-}
-
-int inline sse2_anynan_float32(const npy_float32 *pa) {
-    __m128 values = _mm_load_ps(pa);
-    __m128 result = _mm_cmpneq_ps(values, values);
-    return _mm_movemask_ps(result) > 0;
-}
-
-#endif
-
 /* anynan ---------------------------------------------------------------- */
 
 /* dtype = [['float64'], ['float32']] */
-BN_OPT_3
 REDUCE_ALL(anynan, DTYPE0) {
-    npy_bool f = 0;
+    int f = 0;
+    npy_DTYPE0 ai;
     INIT_ALL
     BN_BEGIN_ALLOW_THREADS
-    if (REDUCE_CONTIGUOUS) {
-        const int         VECTOR_ALIGN = 16;
-        const npy_DTYPE0 *pa = PA(DTYPE0);
-        const npy_intp    count = it.nits * it.length;
-        npy_intp          vector_offset = (((npy_intp)pa) % VECTOR_ALIGN);
-        vector_offset = vector_offset > 0 ? VECTOR_ALIGN - vector_offset : 0;
-        vector_offset /= sizeof(npy_DTYPE0);
-        vector_offset = vector_offset <= count ? vector_offset : count;
-
-        const npy_intp LOOP_SIZE = 16 / sizeof(npy_DTYPE0);
-        const npy_intp loop_count = (count - vector_offset) / LOOP_SIZE;
-        const npy_intp residual = (count - vector_offset) % LOOP_SIZE;
-
-        for (npy_intp i = 0; (i < vector_offset) && (f == 0); i++) {
-            const npy_DTYPE0 ai = pa[i];
-            if (bn_isnan(ai)) {
-                f = 1;
-            }
-        }
-
-#if HAVE_SSE2
-        for (npy_intp i = 0; (i < loop_count) && (f == 0); i++) {
-            f = sse2_anynan_DTYPE0(&pa[vector_offset + i * LOOP_SIZE]);
-        }
-#else
-        npy_bool *f_arr = malloc(LOOP_SIZE * sizeof(npy_bool));
-        for (npy_intp i = 0; (i < loop_count) && (f == 0); i++) {
-            for (npy_intp j = 0; j < LOOP_SIZE; j++) {
-                f_arr[j] = bn_isnan(pa[vector_offset + i * LOOP_SIZE + j]);
-            }
-
-            for (npy_intp j = 0; j < LOOP_SIZE; j++) {
-                f += f_arr[j];
-            }
-        }
-        free(f_arr);
-#endif
-
-        for (npy_intp j = 0; (j < residual) && (f == 0); j++) {
-            const npy_DTYPE0 ai = pa[vector_offset + loop_count * LOOP_SIZE + j];
-            if (bn_isnan(ai)) {
+    WHILE {
+        FOR {
+            ai = AI(DTYPE0);
+            if (ai != ai) {
                 f = 1;
+                goto done;
             }
         }
-    } else {
-        WHILE {
-            const npy_DTYPE0 *pa = PA(DTYPE0);
-            FOR {
-                const npy_DTYPE0 ai = pa[it.i * it.stride];
-                if (bn_isnan(ai)) {
-                    f = 1;
-                    goto done;
-                }
-            }
-            NEXT
-        }
+        NEXT
     }
-done:
+    done:
     BN_END_ALLOW_THREADS
-    if (f) {
-        Py_RETURN_TRUE;
-    } else {
-        Py_RETURN_FALSE;
-    }
+    if (f) Py_RETURN_TRUE;
+    Py_RETURN_FALSE;
 }
 
-BN_OPT_3
 REDUCE_ONE(anynan, DTYPE0) {
-    npy_bool f;
+    int f;
+    npy_DTYPE0 ai;
     INIT_ONE(BOOL, uint8)
     BN_BEGIN_ALLOW_THREADS
     if (LENGTH == 0) {
         FILL_Y(0)
     } else {
-        if (ONE_CONTIGUOUS) {
-            WHILE {
-                f = 0;
-                const int         VECTOR_ALIGN = 16;
-                const npy_DTYPE0 *pa = PA(DTYPE0);
-                const npy_intp    count = LENGTH;
-                npy_intp          vector_offset = (((npy_intp)pa) % VECTOR_ALIGN);
-                vector_offset = vector_offset > 0 ? VECTOR_ALIGN - vector_offset : 0;
-                vector_offset /= sizeof(npy_DTYPE0);
-                vector_offset = vector_offset <= count ? vector_offset : count;
-
-                const npy_intp LOOP_SIZE = 16 / sizeof(npy_DTYPE0);
-                const npy_intp loop_count = (count - vector_offset) / LOOP_SIZE;
-                const npy_intp residual = (count - vector_offset) % LOOP_SIZE;
-
-                for (npy_intp i = 0; (i < vector_offset) && (f == 0); i++) {
-                    const npy_DTYPE0 ai = pa[i];
-                    if (bn_isnan(ai)) {
-                        f = 1;
-                    }
-                }
-
-#if HAVE_SSE2
-                for (npy_intp i = 0; (i < loop_count) && (f == 0); i++) {
-                    f = sse2_anynan_DTYPE0(&pa[vector_offset + i * LOOP_SIZE]);
-                }
-#else
-                for (npy_intp i = 0; (i < loop_count) && (f == 0); i++) {
-                    for (npy_intp j = 0; j < LOOP_SIZE; j++) {
-                        ai = pa[vector_offset + i * LOOP_SIZE + j];
-                        f += bn_isnan(ai);
-                    }
-                }
-#endif
-                for (npy_intp j = 0; (j < residual) && (f == 0); j++) {
-                    const npy_DTYPE0 ai = pa[vector_offset + loop_count * LOOP_SIZE + j];
-                    if (bn_isnan(ai)) {
-                        f = 1;
-                    }
-                }
-                YPP = f;
-                NEXT
-            }
-        } else if (ONE_TRANSPOSE(npy_DTYPE0)) {
-            const npy_intp    LOOP_SIZE = it.nits;
-            const npy_DTYPE0 *pa = PA(DTYPE0);
-
-            for (npy_intp i = 0; i < LOOP_SIZE; i++) {
-                py[i] = 0;
-            }
-
-            const npy_intp loop_count = LENGTH / 16;
-            const npy_intp residual = LENGTH % 16;
-            npy_intp       min_k = 0;
-
-            for (npy_intp i = 0; i < residual; i++) {
-                for (npy_intp k = min_k; k < LOOP_SIZE; k++) {
-                    const npy_DTYPE0 ai = pa[i * LOOP_SIZE + k];
-                    py[k] = bn_isnan(ai) ? 1 : py[k];
-                    if ((k == min_k) && py[k]) {
-                        min_k++;
-                    }
-                }
-            }
-
-            for (npy_intp i = 0; (i < loop_count) && (min_k < LOOP_SIZE - 1); i++) {
-                for (npy_intp j = 0; j < 16; j++) {
-                    for (npy_intp k = min_k; k < LOOP_SIZE; k++) {
-                        const npy_DTYPE0 ai = pa[(i * 16 + j + residual) * LOOP_SIZE + k];
-                        py[k] = bn_isnan(ai) ? 1 : py[k];
-                    }
-                }
-                for (npy_intp k = min_k; k < LOOP_SIZE; k++) {
-                    if (py[k] == 0) {
-                        min_k = k;
-                        break;
-                    } else if (k == LOOP_SIZE - 1) {
-                        min_k = k;
-                    }
-                }
-                if (min_k == LOOP_SIZE - 1) {
+        WHILE {
+            f = 0;
+            FOR {
+                ai = AI(DTYPE0);
+                if (ai != ai) {
+                    f = 1;
                     break;
                 }
             }
-        } else {
-            WHILE {
-                f = 0;
-                const npy_DTYPE0 *pa = PA(DTYPE0);
-                FOR {
-                    const npy_DTYPE0 ai = SI(pa);
-                    if (bn_isnan(ai)) {
-                        f = 1;
-                        break;
-                    }
-                }
-                YPP = f;
-                NEXT
-            }
+            YPP = f;
+            NEXT
         }
     }
     BN_END_ALLOW_THREADS
@@ -1407,189 +976,50 @@ REDUCE_ONE(anynan, DTYPE0) {
 
 REDUCE_MAIN(anynan, 0)
 
+
 /* allnan ---------------------------------------------------------------- */
 
 /* dtype = [['float64'], ['float32']] */
-BN_OPT_3
 REDUCE_ALL(allnan, DTYPE0) {
     int f = 0;
+    npy_DTYPE0 ai;
     INIT_ALL
     BN_BEGIN_ALLOW_THREADS
-    if (REDUCE_CONTIGUOUS) {
-        const int         VECTOR_ALIGN = 16;
-        const npy_DTYPE0 *pa = PA(DTYPE0);
-        const npy_intp    count = it.nits * it.length;
-        npy_intp          vector_offset = (((npy_intp)pa) % VECTOR_ALIGN);
-        vector_offset = vector_offset > 0 ? VECTOR_ALIGN - vector_offset : 0;
-        vector_offset /= sizeof(npy_DTYPE0);
-        vector_offset = vector_offset <= count ? vector_offset : count;
-
-        const npy_intp LOOP_SIZE = 16 / sizeof(npy_DTYPE0);
-        const npy_intp loop_count = (count - vector_offset) / LOOP_SIZE;
-        const npy_intp residual = (count - vector_offset) % LOOP_SIZE;
-
-        for (npy_intp i = 0; (i < vector_offset) && (f == 0); i++) {
-            const npy_DTYPE0 ai = pa[i];
-            if (!bn_isnan(ai)) {
-                f = 1;
-            }
-        }
-
-#if HAVE_SSE2
-        for (npy_intp i = 0; (i < loop_count) && (f == 0); i++) {
-            f = sse2_allnan_DTYPE0(&pa[vector_offset + i * LOOP_SIZE]);
-        }
-#else
-        npy_bool *f_arr = malloc(LOOP_SIZE * sizeof(npy_bool));
-        for (npy_intp i = 0; (i < loop_count) && (f == 0); i++) {
-            for (npy_intp j = 0; j < LOOP_SIZE; j++) {
-                f_arr[j] = !bn_isnan(pa[vector_offset + i * LOOP_SIZE + j]);
-            }
-
-            for (npy_intp j = 0; j < LOOP_SIZE; j++) {
-                f += f_arr[j];
-            }
-        }
-        free(f_arr);
-#endif
-
-        for (npy_intp j = 0; (j < residual) && (f == 0); j++) {
-            const npy_DTYPE0 ai = pa[vector_offset + loop_count * LOOP_SIZE + j];
-            if (!bn_isnan(ai)) {
+    WHILE {
+        FOR {
+            ai = AI(DTYPE0);
+            if (ai == ai) {
                 f = 1;
+                goto done;
             }
         }
-    } else {
-        WHILE {
-            FOR {
-                const npy_DTYPE0 ai = AI(DTYPE0);
-                if (!bn_isnan(ai)) {
-                    f = 1;
-                    goto done;
-                }
-            }
-            NEXT
-        }
+        NEXT
     }
-done:
+    done:
     BN_END_ALLOW_THREADS
-    if (f) {
-        Py_RETURN_FALSE;
-    } else {
-        Py_RETURN_TRUE;
-    }
+    if (f) Py_RETURN_FALSE;
+    Py_RETURN_TRUE;
 }
 
-BN_OPT_3
 REDUCE_ONE(allnan, DTYPE0) {
-    npy_bool f;
+    int f;
+    npy_DTYPE0 ai;
     INIT_ONE(BOOL, uint8)
     BN_BEGIN_ALLOW_THREADS
     if (LENGTH == 0) {
         FILL_Y(1)
     } else {
-        if (ONE_CONTIGUOUS) {
-            WHILE {
-                f = 0;
-                const int         VECTOR_ALIGN = 16;
-                const npy_DTYPE0 *pa = PA(DTYPE0);
-                const npy_intp    count = LENGTH;
-                npy_intp          vector_offset = (((npy_intp)pa) % VECTOR_ALIGN);
-                vector_offset = vector_offset > 0 ? VECTOR_ALIGN - vector_offset : 0;
-                vector_offset /= sizeof(npy_DTYPE0);
-                vector_offset = vector_offset <= count ? vector_offset : count;
-
-                const npy_intp LOOP_SIZE = 16 / sizeof(npy_DTYPE0);
-                const npy_intp loop_count = (count - vector_offset) / LOOP_SIZE;
-                const npy_intp residual = (count - vector_offset) % LOOP_SIZE;
-
-                for (npy_intp i = 0; (i < vector_offset) && (f == 0); i++) {
-                    const npy_DTYPE0 ai = pa[i];
-                    if (!bn_isnan(ai)) {
-                        f = 1;
-                    }
-                }
-
-#if HAVE_SSE2
-                for (npy_intp i = 0; (i < loop_count) && (f == 0); i++) {
-                    f = sse2_allnan_DTYPE0(&pa[vector_offset + i * LOOP_SIZE]);
-                }
-#else
-                for (npy_intp i = 0; (i < loop_count) && (f == 0); i++) {
-                    for (npy_intp j = 0; j < LOOP_SIZE; j++) {
-                        ai = pa[vector_offset + i * LOOP_SIZE + j];
-                        f += !bn_isnan(ai);
-                    }
-                }
-#endif
-                for (npy_intp j = 0; (j < residual) && (f == 0); j++) {
-                    const npy_DTYPE0 ai = pa[vector_offset + loop_count * LOOP_SIZE + j];
-                    if (!bn_isnan(ai)) {
-                        f = 1;
-                    }
-                }
-                YPP = !f;
-                NEXT
-            }
-        } else if (ONE_TRANSPOSE(npy_DTYPE0)) {
-            const npy_intp    LOOP_SIZE = it.nits;
-            const npy_DTYPE0 *pa = PA(DTYPE0);
-
-            for (npy_intp i = 0; i < LOOP_SIZE; i++) {
-                py[i] = 0;
-            }
-
-            const npy_intp loop_count = LENGTH / 16;
-            const npy_intp residual = LENGTH % 16;
-            npy_intp       min_k = 0;
-
-            for (npy_intp i = 0; i < residual; i++) {
-                for (npy_intp k = min_k; k < LOOP_SIZE; k++) {
-                    const npy_DTYPE0 ai = pa[i * LOOP_SIZE + k];
-                    py[k] = !bn_isnan(ai) ? 1 : py[k];
-                    if ((k == min_k) && py[k]) {
-                        min_k++;
-                    }
-                }
-            }
-
-            for (npy_intp i = 0; (i < loop_count) && (min_k < LOOP_SIZE - 1); i++) {
-                for (npy_intp j = 0; j < 16; j++) {
-                    for (npy_intp k = min_k; k < LOOP_SIZE; k++) {
-                        const npy_DTYPE0 ai = pa[(i * 16 + j + residual) * LOOP_SIZE + k];
-                        py[k] = !bn_isnan(ai) ? 1 : py[k];
-                    }
-                }
-                for (npy_intp k = min_k; k < LOOP_SIZE; k++) {
-                    if (py[k] == 0) {
-                        min_k = k;
-                        break;
-                    } else if (k == LOOP_SIZE - 1) {
-                        min_k = k;
-                    }
-                }
-                if (min_k == LOOP_SIZE - 1) {
+        WHILE {
+            f = 1;
+            FOR {
+                ai = AI(DTYPE0);
+                if (ai == ai) {
+                    f = 0;
                     break;
                 }
             }
-
-            for (npy_intp k = 0; k < LOOP_SIZE; k++) {
-                py[k] = !py[k];
-            }
-        } else {
-            WHILE {
-                f = 0;
-                const npy_DTYPE0 *pa = PA(DTYPE0);
-                FOR {
-                    const npy_DTYPE0 ai = SI(pa);
-                    if (!bn_isnan(ai)) {
-                        f = 1;
-                        break;
-                    }
-                }
-                YPP = !f;
-                NEXT
-            }
+            YPP = f;
+            NEXT
         }
     }
     BN_END_ALLOW_THREADS
@@ -1620,6 +1050,7 @@ REDUCE_ONE(allnan, DTYPE0) {
 
 REDUCE_MAIN(allnan, 0)
 
+
 /* python strings -------------------------------------------------------- */
 
 PyObject *pystr_a = NULL;
@@ -1637,16 +1068,16 @@ intern_strings(void) {
 /* reducer --------------------------------------------------------------- */
 
 static inline int
-parse_args(PyObject * args,
-           PyObject * kwds,
-           int        has_ddof,
+parse_args(PyObject *args,
+           PyObject *kwds,
+           int has_ddof,
            PyObject **a,
            PyObject **axis,
            PyObject **ddof) {
     const Py_ssize_t nargs = PyTuple_GET_SIZE(args);
     const Py_ssize_t nkwds = kwds == NULL ? 0 : PyDict_Size(kwds);
     if (nkwds) {
-        int       nkwds_found = 0;
+        int nkwds_found = 0;
         PyObject *tmp;
         switch (nargs) {
             case 2:
@@ -1656,10 +1087,8 @@ parse_args(PyObject * args,
                     TYPE_ERR("wrong number of arguments");
                     return 0;
                 }
-            case 1:
-                *a = PyTuple_GET_ITEM(args, 0);
-            case 0:
-                break;
+            case 1: *a = PyTuple_GET_ITEM(args, 0);
+            case 0: break;
             default:
                 TYPE_ERR("wrong number of arguments");
                 return 0;
@@ -1721,21 +1150,23 @@ parse_args(PyObject * args,
     }
 
     return 1;
+
 }
 
 static PyObject *
-reducer(char *    name,
+reducer(char *name,
         PyObject *args,
         PyObject *kwds,
-        fall_t    fall_float64,
-        fall_t    fall_float32,
-        fall_t    fall_int64,
-        fall_t    fall_int32,
-        fone_t    fone_float64,
-        fone_t    fone_float32,
-        fone_t    fone_int64,
-        fone_t    fone_int32,
-        int       has_ddof) {
+        fall_t fall_float64,
+        fall_t fall_float32,
+        fall_t fall_int64,
+        fall_t fall_int32,
+        fone_t fone_float64,
+        fone_t fone_float32,
+        fone_t fone_int64,
+        fone_t fone_int32,
+        int has_ddof) {
+
     int ndim;
     int axis = 0; /* initialize to avoid compiler error */
     int dtype;
@@ -1743,7 +1174,7 @@ reducer(char *    name,
     int reduce_all = 0;
 
     PyArrayObject *a;
-    PyObject *     y;
+    PyObject *y;
 
     PyObject *a_obj = NULL;
     PyObject *axis_obj = Py_None;
@@ -1766,7 +1197,6 @@ reducer(char *    name,
 
     /* check for byte swapped input array */
     if (PyArray_ISBYTESWAPPED(a)) {
-        Py_DECREF(a);
         return slow(name, args, kwds);
     }
 
@@ -1784,8 +1214,7 @@ reducer(char *    name,
             axis += ndim;
             if (axis < 0) {
                 PyErr_Format(PyExc_ValueError,
-                             "axis(=%d) out of bounds",
-                             axis);
+                             "axis(=%d) out of bounds", axis);
                 goto error;
             }
         } else if (axis >= ndim) {
@@ -1836,6 +1265,7 @@ reducer(char *    name,
         } else {
             y = slow(name, args, kwds);
         }
+
     }
 
     Py_DECREF(a);
@@ -1845,12 +1275,11 @@ reducer(char *    name,
 error:
     Py_DECREF(a);
     return NULL;
+
 }
 
 /* docstrings ------------------------------------------------------------- */
 
-// clang-format off
-
 static char reduce_doc[] =
 "Bottleneck functions that reduce the input array along a specified axis.";
 
@@ -2520,32 +1949,33 @@ reduce_methods[] = {
     {NULL, NULL, 0, NULL}
 };
 
-// clang-format on
 
 #if PY_MAJOR_VERSION >= 3
 static struct PyModuleDef
-    reduce_def = {
-        PyModuleDef_HEAD_INIT,
-        "reduce",
-        reduce_doc,
-        -1,
-        reduce_methods};
+reduce_def = {
+   PyModuleDef_HEAD_INIT,
+   "reduce",
+   reduce_doc,
+   -1,
+   reduce_methods
+};
 #endif
 
+
 PyMODINIT_FUNC
 #if PY_MAJOR_VERSION >= 3
-    #define RETVAL m
+#define RETVAL m
 PyInit_reduce(void)
 #else
-    #define RETVAL
+#define RETVAL
 initreduce(void)
 #endif
 {
-#if PY_MAJOR_VERSION >= 3
-    PyObject *m = PyModule_Create(&reduce_def);
-#else
-    PyObject *m = Py_InitModule3("reduce", reduce_methods, reduce_doc);
-#endif
+    #if PY_MAJOR_VERSION >=3
+        PyObject *m = PyModule_Create(&reduce_def);
+    #else
+        PyObject *m = Py_InitModule3("reduce", reduce_methods, reduce_doc);
+    #endif
     if (m == NULL) return RETVAL;
     import_array();
     if (!intern_strings()) {
diff --git a/doc/doc_howto b/doc/doc_howto
index 5a55eb7130..edd1b9b1c8 100644
--- a/doc/doc_howto
+++ b/doc/doc_howto
@@ -1,9 +1,12 @@
 
-Sphinx doc generation
-=====================
+Upload Sphinx docs to web server
+================================
 
-To generate docs locally, run:
+Godaddy doesn't allow rsyc. So to upload Sphinx docs to
+http://berkeleyanalytics.com/bottleneck:
 
 $ make doc
 
-Docs are automatically build by ReadTheDocs directly from the Github repo.
+Then to upload (it will ask for the password):
+
+$ scp -r build/sphinx/html/* username@berkeleyanalytics.com:html/bottleneck/
diff --git a/doc/source/bottleneck.rst b/doc/source/bottleneck.rst
index c976a3d1ec..8e5767d17c 100644
--- a/doc/source/bottleneck.rst
+++ b/doc/source/bottleneck.rst
@@ -1,5 +1,5 @@
-Bottleneck Package Structure
-============================
+bottleneck package
+==================
 
 Subpackages
 -----------
@@ -14,3 +14,11 @@ Subpackages
    bottleneck.slow
    bottleneck.src
    bottleneck.tests
+
+Module contents
+---------------
+
+.. automodule:: bottleneck
+   :members:
+   :undoc-members:
+   :show-inheritance:
diff --git a/doc/source/conf.py b/doc/source/conf.py
index 0aa4f775a5..7efdfd8ada 100644
--- a/doc/source/conf.py
+++ b/doc/source/conf.py
@@ -11,8 +11,7 @@
 # All configuration values have a default; values that are commented out
 # serve to show the default.
 
-import os
-import sys
+import sys, os
 
 # If extensions (or modules to document with autodoc) are in another directory,
 # add these directories to sys.path here. If the directory is relative to the
@@ -21,8 +20,7 @@
 sys.path.insert(0, os.path.abspath("../sphinxext"))
 if "READTHEDOCS" not in os.environ or not os.environ["READTHEDOCS"]:
     sys.path.insert(0, os.path.join(os.path.dirname(__file__), "../.."))
-
-import bottleneck  # isort:skip
+import bottleneck
 
 # -- General configuration -----------------------------------------------------
 
@@ -107,35 +105,25 @@
 
 # The theme to use for HTML and HTML Help pages.  Major themes that come with
 # Sphinx are currently 'default' and 'sphinxdoc'.
-html_theme = "alabaster"
+html_theme = "default"
 
 # Theme options are theme-specific and customize the look and feel of a theme
 # further.  For a list of options available for each theme, see the
 # documentation.
-# html_theme_options = {
-#     "headtextcolor": "#333333",
-#     "sidebarbgcolor": "#dddddd",
-#     "footerbgcolor": "#cccccc",
-#     "footertextcolor": "black",
-#     "headbgcolor": "#cccccc",
-#     "sidebartextcolor": "#333333",
-#     "sidebarlinkcolor": "default",
-#     "relbarbgcolor": "#cccccc",
-#     "relbartextcolor": "default",
-#     "relbarlinkcolor": "default",
-#     "codebgcolor": "#ffffff",
-#     "textcolor": "#333333",
-#     "bgcolor": "#f5f5f5",
-# }
-
 html_theme_options = {
-    "show_powered_by": True,
-    "github_user": "pydata",
-    "github_repo": "bottleneck",
-    "github_banner": True,
-    "show_related": True,
-    "travis_button": True,
-    "codecov_button": True,
+    "headtextcolor": "#333333",
+    "sidebarbgcolor": "#dddddd",
+    "footerbgcolor": "#cccccc",
+    "footertextcolor": "black",
+    "headbgcolor": "#cccccc",
+    "sidebartextcolor": "#333333",
+    "sidebarlinkcolor": "default",
+    "relbarbgcolor": "#cccccc",
+    "relbartextcolor": "default",
+    "relbarlinkcolor": "default",
+    "codebgcolor": "#ffffff",
+    "textcolor": "#333333",
+    "bgcolor": "#f5f5f5",
 }
 
 # Add any paths that contain custom themes here, relative to this directory.
diff --git a/doc/source/index.rst b/doc/source/index.rst
index 8e02ca785f..ad41651fa5 100644
--- a/doc/source/index.rst
+++ b/doc/source/index.rst
@@ -2,46 +2,18 @@
 Bottleneck
 ==========
 
-Bottleneck is a collection of fast, NaN-aware NumPy array functions
-written in C.
+Fast NumPy array functions written in C.
 
-As one example, to check if a ``np.array`` has any NaNs using numpy,
-one must call ``np.any(np.isnan(array))``. The
-:meth:`bottleneck.anynan` function interleaves the :meth:`np.isnan`
-check with :meth:`np.any` pre-exit, enabling up to an ``O(N)``
-speedup relative to numpy.
-
-Bottleneck strives to be a drop-in accelerator for NumPy functions.
-When using the following libraries, Bottleneck support is
-automatically enabled and utilized:
-
- - `pandas <https://pandas.pydata.org/pandas-docs/stable/install.html#recommended-dependencies>`_
- - `xarray <http://xarray.pydata.org/en/stable/installing.html#instructions>`_
- - `astropy <https://docs.astropy.org/en/stable/install.html>`_
-
-Details on the performance benefits can be found in :ref:`benchmarking`
-
-Table of Contents
-=================
 .. toctree::
-   :maxdepth: 3
-
+   :maxdepth: 2
+   
    intro
-   installing
    reference
+   release
    license
 
-.. toctree::
-   :maxdepth: 2
-
-   releases/RELEASE
-
 Indices and tables
 ==================
-.. toctree::
-   :maxdepth: 1
-
-   bottleneck
 
 * :ref:`genindex`
 * :ref:`modindex`
diff --git a/doc/source/license.rst b/doc/source/license.rst
index cba1b673ab..8be7b1fea2 100644
--- a/doc/source/license.rst
+++ b/doc/source/license.rst
@@ -1,8 +1,7 @@
 Licenses
 ========
 Bottleneck is distributed under a Simplified BSD license. Parts of NumPy and
-SciPy, which have BSD licenses, are included in Bottleneck. The setuptools
-project has a MIT license and is used for configuration and installation.
+SciPy, which have BSD licenses, are included in Bottleneck. The setuptools project has a MIT license and is used for configuration and installation.
 
 Bottleneck License
 ~~~~~~~~~~~~~~~~~~
diff --git a/doc/source/reference.rst b/doc/source/reference.rst
index 75d43323c5..eae6d79070 100644
--- a/doc/source/reference.rst
+++ b/doc/source/reference.rst
@@ -4,41 +4,28 @@ Function reference
 
 Bottleneck provides the following functions:
 
-====================    ===============================================
-reduce                  :meth:`nansum <bottleneck.nansum>`,
-                        :meth:`nanmean <bottleneck.nanmean>`,
-                        :meth:`nanstd <bottleneck.nanstd>`,
-                        :meth:`nanvar <bottleneck.nanvar>`,
-                        :meth:`nanmin <bottleneck.nanmin>`,
-                        :meth:`nanmax <bottleneck.nanmax>`,
-                        :meth:`median <bottleneck.median>`,
-                        :meth:`nanmedian <bottleneck.nanmedian>`,
-                        :meth:`ss <bottleneck.ss>`,
-                        :meth:`nanargmin <bottleneck.nanargmin>`,
-                        :meth:`nanargmax <bottleneck.nanargmax>`,
-                        :meth:`anynan <bottleneck.anynan>`,
-                        :meth:`allnan <bottleneck.allnan>`
-
-non-reduce              :meth:`replace <bottleneck.replace>`
-
-non-reduce with axis    :meth:`rankdata <bottleneck.rankdata>`,
-                        :meth:`nanrankdata <bottleneck.nanrankdata>`,
-                        :meth:`partition <bottleneck.partition>`,
-                        :meth:`argpartition <bottleneck.argpartition>`,
-                        :meth:`push <bottleneck.push>`
-
-moving window           :meth:`move_sum <bottleneck.move_sum>`,
-                        :meth:`move_mean <bottleneck.move_mean>`,
-                        :meth:`move_std <bottleneck.move_std>`,
-                        :meth:`move_var <bottleneck.move_var>`,
-                        :meth:`move_min <bottleneck.move_min>`,
-                        :meth:`move_max <bottleneck.move_max>`,
-                        :meth:`move_argmin <bottleneck.move_argmin>`,
-                        :meth:`move_argmax <bottleneck.move_argmax>`,
-                        :meth:`move_median <bottleneck.move_median>`,
-                        :meth:`move_rank <bottleneck.move_rank>`
-
-====================    ===============================================
+=================================  ==============================================================================================
+reduce                             :meth:`nansum <bottleneck.nansum>`, :meth:`nanmean <bottleneck.nanmean>`,
+                                   :meth:`nanstd <bottleneck.nanstd>`, :meth:`nanvar <bottleneck.nanvar>`,
+                                   :meth:`nanmin <bottleneck.nanmin>`, :meth:`nanmax <bottleneck.nanmax>`,
+                                   :meth:`median <bottleneck.median>`, :meth:`nanmedian <bottleneck.nanmedian>`,
+                                   :meth:`ss <bottleneck.ss>`, :meth:`nanargmin <bottleneck.nanargmin>`,
+                                   :meth:`nanargmax <bottleneck.nanargmax>`, :meth:`anynan <bottleneck.anynan>`,
+                                   :meth:`allnan <bottleneck.allnan>`
+
+non-reduce                         :meth:`replace <bottleneck.replace>`
+
+non-reduce with axis               :meth:`rankdata <bottleneck.rankdata>`, :meth:`nanrankdata <bottleneck.nanrankdata>`,
+                                   :meth:`partition <bottleneck.partition>`, :meth:`argpartition <bottleneck.argpartition>`,
+                                   :meth:`push <bottleneck.push>`
+
+moving window                      :meth:`move_sum <bottleneck.move_sum>`, :meth:`move_mean <bottleneck.move_mean>`,
+                                   :meth:`move_std <bottleneck.move_std>`, :meth:`move_var <bottleneck.move_var>`,
+                                   :meth:`move_min <bottleneck.move_min>`, :meth:`move_max <bottleneck.move_max>`,
+                                   :meth:`move_argmin <bottleneck.move_argmin>`, :meth:`move_argmax <bottleneck.move_argmax>`,
+                                   :meth:`move_median <bottleneck.move_median>`, :meth:`move_rank <bottleneck.move_rank>`
+
+=================================  ==============================================================================================
 
 
 Reduce
@@ -46,53 +33,56 @@ Reduce
 
 Functions the reduce the input array along the specified axis.
 
+------------
+
 .. autofunction:: bottleneck.nansum
 
-|
+------------
 
 .. autofunction:: bottleneck.nanmean
 
-|
+------------
 
 .. autofunction:: bottleneck.nanstd
 
-|
+------------
 
 .. autofunction:: bottleneck.nanvar
 
-|
+------------
 
 .. autofunction:: bottleneck.nanmin
 
-|
+------------
 
 .. autofunction:: bottleneck.nanmax
 
-|
+------------
 
 .. autofunction:: bottleneck.median
 
-|
+------------
 
 .. autofunction:: bottleneck.nanmedian
 
-|
+------------
 
 .. autofunction:: bottleneck.ss
 
-|
+------------
 
 .. autofunction:: bottleneck.nanargmin
 
-|
+------------
 
 .. autofunction:: bottleneck.nanargmax
 
-|
+
+------------
 
 .. autofunction:: bottleneck.anynan
 
-|
+------------
 
 .. autofunction:: bottleneck.allnan
 
@@ -102,7 +92,7 @@ Non-reduce
 
 Functions that do not reduce the input array and do not take `axis` as input.
 
-|
+------------
 
 .. autofunction:: bottleneck.replace
 
@@ -113,23 +103,23 @@ Non-reduce with axis
 Functions that do not reduce the input array but operate along a specified
 axis.
 
-|
+------------
 
 .. autofunction:: bottleneck.rankdata
 
-|
+------------
 
 .. autofunction:: bottleneck.nanrankdata
 
-|
+------------
 
 .. autofunction:: bottleneck.partition
 
-|
+------------
 
 .. autofunction:: bottleneck.argpartition
 
-|
+------------
 
 .. autofunction:: bottleneck.push
 
@@ -139,43 +129,43 @@ Moving window functions
 
 Functions that operate along a (1d) moving window.
 
-|
+------------
 
 .. autofunction:: bottleneck.move_sum
 
-|
+------------
 
 .. autofunction:: bottleneck.move_mean
 
-|
+------------
 
 .. autofunction:: bottleneck.move_std
 
-|
+------------
 
 .. autofunction:: bottleneck.move_var
 
-|
+------------
 
 .. autofunction:: bottleneck.move_min
 
-|
+------------
 
 .. autofunction:: bottleneck.move_max
 
-|
+------------
 
 .. autofunction:: bottleneck.move_argmin
 
-|
+------------
 
 .. autofunction:: bottleneck.move_argmax
 
-|
+------------
 
 .. autofunction:: bottleneck.move_median
 
-|
+------------
 
 .. autofunction:: bottleneck.move_rank
 
diff --git a/doc/source/release.rst b/doc/source/release.rst
new file mode 100644
index 0000000000..3601d9ad80
--- /dev/null
+++ b/doc/source/release.rst
@@ -0,0 +1,1 @@
+.. include:: ../../RELEASE.rst
diff --git a/doc/sphinxext/contributors.py b/doc/sphinxext/contributors.py
index 5ecbfa3d97..f76b44f9df 100644
--- a/doc/sphinxext/contributors.py
+++ b/doc/sphinxext/contributors.py
@@ -10,10 +10,10 @@
 code contributors and commits, and then list each contributor
 individually.
 """
-import git
 from announce import build_components
 from docutils import nodes
 from docutils.parsers.rst import Directive
+import git
 
 
 class ContributorsDirective(Directive):
diff --git a/pyproject.toml b/pyproject.toml
index 72c2df7e17..c3479b5c6d 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -1,6 +1,7 @@
 [build-system]
 requires = [
     "setuptools",
+    "versioneer", 
     "wheel",
     "oldest-supported-numpy"
 ]
diff --git a/setup.cfg b/setup.cfg
index f18e4c9c7b..6d93804319 100644
--- a/setup.cfg
+++ b/setup.cfg
@@ -1,6 +1,6 @@
 [versioneer]
 VCS = git
-style = pep440
+style = pep440-pre
 versionfile_source = bottleneck/_version.py
 versionfile_build = bottleneck/_version.py
 tag_prefix = v
@@ -14,7 +14,6 @@ ignore = \
     W503  # Line break before binary operator - conflicts with W504
 per-file-ignores= \
     bottleneck/__init__.py:F401
-    bottleneck/slow/__init__.py:F401
 exclude = \
     doc/*
     versioneer.py
@@ -32,17 +31,3 @@ norecursedirs =
     *.egg
     venv
     env
-
-[tool:isort]
-default_section = THIRDPARTY
-include_trailing_comma = True
-line_length = 88
-known_first_party = bottleneck
-known_third_party = pandas,git,docutils,numpy,pytest,hypothesis
-multi_line_output = 3
-no_lines_before = LOCALFOLDER
-skip = bottleneck/tests/docker/temp_clone/,.eggs/,.tox/
-skip_glob = **/env
-
-[build_sphinx]
-warning_is_error = True
diff --git a/setup.py b/setup.py
index 9fcffb4966..db629bd29c 100644
--- a/setup.py
+++ b/setup.py
@@ -1,12 +1,9 @@
 #!/usr/bin/env python
 
 import os
-import platform
 import shutil
 import sys
 from distutils.command.config import config as _config
-from subprocess import check_output
-from typing import List
 
 from setuptools import Command, find_packages, setup
 from setuptools.command.build_ext import build_ext as _build_ext
@@ -16,7 +13,7 @@
 
 
 class config(_config):
-    def run(self) -> None:
+    def run(self):
         from bn_config import create_config_h
 
         create_config_h(self)
@@ -25,7 +22,7 @@ def run(self) -> None:
 class clean(Command):
     user_options = [("all", "a", "")]
 
-    def initialize_options(self) -> None:
+    def initialize_options(self):
         self.all = True
         self.delete_dirs = []
         self.delete_files = []
@@ -47,17 +44,17 @@ def initialize_options(self) -> None:
                     if os.path.exists(generated_file):
                         self.delete_files.append(generated_file)
 
-        config_h = "bottleneck/include/bn_config.h"
+        config_h = "bottleneck/src/bn_config.h"
         if os.path.exists(config_h):
             self.delete_files.append(config_h)
 
         if os.path.exists("build"):
             self.delete_dirs.append("build")
 
-    def finalize_options(self) -> None:
+    def finalize_options(self):
         pass
 
-    def run(self) -> None:
+    def run(self):
         for delete_dir in self.delete_dirs:
             shutil.rmtree(delete_dir)
         for delete_file in self.delete_files:
@@ -68,7 +65,7 @@ def run(self) -> None:
 class build_ext(_build_ext):
     # taken from: stackoverflow.com/questions/19919905/
     # how-to-bootstrap-numpy-installation-in-setup-py#21621689
-    def finalize_options(self) -> None:
+    def finalize_options(self):
         _build_ext.finalize_options(self)
         # prevent numpy from thinking it is still in its setup process
         if sys.version_info < (3,):
@@ -81,13 +78,14 @@ def finalize_options(self) -> None:
         # place numpy includes first, see gh #156
         self.include_dirs.insert(0, numpy.get_include())
         self.include_dirs.append("bottleneck/src")
-        self.include_dirs.append("bottleneck/include")
 
-    def build_extensions(self) -> None:
+    def build_extensions(self):
         from bn_template import make_c_files
 
         self.run_command("config")
-        make_c_files()
+        dirpath = "bottleneck/src"
+        modules = ["reduce", "move", "nonreduce", "nonreduce_axis"]
+        make_c_files(dirpath, modules)
 
         _build_ext.build_extensions(self)
 
@@ -97,48 +95,23 @@ def build_extensions(self) -> None:
 cmdclass["clean"] = clean
 cmdclass["config"] = config
 
-
-def is_old_gcc() -> bool:
-    if sys.platform != "win32":
-        gcc_version = check_output(["gcc", "-dumpversion"]).decode("utf8").split(".")[0]
-        if int(gcc_version) < 5:
-            return True
-    return False
-
-
-IS_OLD_GCC = is_old_gcc()
-DEFAULT_FLAGS = ["-O2"]
-if IS_OLD_GCC:
-    DEFAULT_FLAGS.append("-std=gnu11")
-
 # Add our template path to the path so that we don't have a circular reference
 # of working install to be able to re-compile
 sys.path.append(os.path.join(os.path.dirname(__file__), "bottleneck/src"))
 
 
-def get_cpu_arch_flags() -> List[str]:
-    if platform.processor() == "ppc64le":
-        # Needed to support SSE2 intrinsics
-        return ["-DNO_WARN_X86_INTRINSICS"]
-    else:
-        return []
-
-
-def prepare_modules() -> List[Extension]:
+def prepare_modules():
     base_includes = [
-        "bottleneck/include/bottleneck.h",
-        "bottleneck/include/bn_config.h",
-        "bottleneck/include/iterators.h",
+        "bottleneck/src/bottleneck.h",
+        "bottleneck/src/bn_config.h",
+        "bottleneck/src/iterators.h",
     ]
-
-    arch_flags = get_cpu_arch_flags()
-
     ext = [
         Extension(
             "bottleneck.reduce",
             sources=["bottleneck/src/reduce.c"],
             depends=base_includes,
-            extra_compile_args=DEFAULT_FLAGS + arch_flags,
+            extra_compile_args=["-O2"],
         )
     ]
     ext += [
@@ -149,7 +122,7 @@ def prepare_modules() -> List[Extension]:
                 "bottleneck/src/move_median/move_median.c",
             ],
             depends=base_includes + ["bottleneck/src/move_median/move_median.h"],
-            extra_compile_args=DEFAULT_FLAGS + arch_flags,
+            extra_compile_args=["-O2"],
         )
     ]
     ext += [
@@ -157,7 +130,7 @@ def prepare_modules() -> List[Extension]:
             "bottleneck.nonreduce",
             sources=["bottleneck/src/nonreduce.c"],
             depends=base_includes,
-            extra_compile_args=DEFAULT_FLAGS + arch_flags,
+            extra_compile_args=["-O2"],
         )
     ]
     ext += [
@@ -165,13 +138,13 @@ def prepare_modules() -> List[Extension]:
             "bottleneck.nonreduce_axis",
             sources=["bottleneck/src/nonreduce_axis.c"],
             depends=base_includes,
-            extra_compile_args=DEFAULT_FLAGS + arch_flags,
+            extra_compile_args=["-O2"],
         )
     ]
     return ext
 
 
-def get_long_description() -> str:
+def get_long_description():
     with open("README.rst", "r") as fid:
         long_description = fid.read()
     idx = max(0, long_description.find("Bottleneck is a collection"))
@@ -189,6 +162,7 @@ def get_long_description() -> str:
     "Programming Language :: C",
     "Programming Language :: Python",
     "Programming Language :: Python :: 3",
+    "Programming Language :: Python :: 3.5",
     "Programming Language :: Python :: 3.6",
     "Programming Language :: Python :: 3.7",
     "Programming Language :: Python :: 3.8",
@@ -211,16 +185,13 @@ def get_long_description() -> str:
     platforms="OS Independent",
     version=versioneer.get_version(),
     packages=find_packages(),
-    package_data={"bottleneck": ["LICENSE", "tests/data/**/*.c"]},
+    package_data={"bottleneck": ["LICENSE"]},
+    requires=["numpy"],
     install_requires=["numpy"],
-    extras_require={
-        "doc": ["numpydoc", "sphinx", "gitpython"],
-        "test": ["hypothesis", "pytest"],
-    },
+    extras_require={"doc": ["numpydoc", "sphinx", "gitpython"]},
     cmdclass=cmdclass,
     setup_requires=["numpy"],
     ext_modules=prepare_modules(),
-    python_requires=">=3.6",
     zip_safe=False,
 )
 
diff --git a/tools/appveyor/conda_setup.py b/tools/appveyor/conda_setup.py
index 483a278e29..897a3e0438 100644
--- a/tools/appveyor/conda_setup.py
+++ b/tools/appveyor/conda_setup.py
@@ -2,12 +2,12 @@
 
 
 from __future__ import absolute_import
-
 import logging
 from os import environ
 
 from conda_wrapper import CondaWrapper
 
+
 if __name__ == "__main__":
     logging.basicConfig(level=logging.INFO)
     with CondaWrapper(
diff --git a/tools/appveyor/conda_wrapper.py b/tools/appveyor/conda_wrapper.py
index 5df878bafb..e41f9b82bc 100644
--- a/tools/appveyor/conda_wrapper.py
+++ b/tools/appveyor/conda_wrapper.py
@@ -1,11 +1,11 @@
 # -*- coding: utf-8 -*-
 
 from __future__ import absolute_import
-
-import logging
 import sys
+import logging
 from subprocess import check_output
 
+
 if sys.version_info[0] == 2:
 
     def decode(string):
diff --git a/tools/travis/bn_setup.sh b/tools/travis/bn_setup.sh
old mode 100755
new mode 100644
index 4603f71554..562e55e18c
--- a/tools/travis/bn_setup.sh
+++ b/tools/travis/bn_setup.sh
@@ -2,41 +2,23 @@
 
 set -ev # exit on first error, print commands
 
-COVERAGE=""
-if [ "${TEST_RUN}" == "coverage" ]; then
-    COVERAGE="--coverage"
-    export CFLAGS="-fprofile-arcs -ftest-coverage"
-    export LDFLAGS="-fprofile-arcs"
-fi
-
-if [ "${TEST_RUN}" == "style" ]; then
+if [ "${TEST_RUN}" = "style" ]; then
     flake8
-    isort -c --df .
     black . --check --exclude "(build/|dist/|\.git/|\.mypy_cache/|\.tox/|\.venv/\.asv/|env|\.eggs)"
 else
-    if [ "${TEST_RUN}" == "sdist" ]; then
+    if [ "${TEST_RUN}" = "sdist" ]; then
         python setup.py sdist
         ARCHIVE=`ls dist/*.tar.gz`
         pip install "${ARCHIVE[0]}"
-    elif [ "${TEST_RUN}" != "coverage" ]; then
-        # CFLAGS gets ignored by PEP 518, so do coverage from inplace build
-        EXTRAS="test"
-        if [ "${TEST_RUN}" == "doc" ]; then
-            EXTRAS="${EXTRAS},doc"
-        fi
-        pip install ".[${EXTRAS}]"
+    else
+        pip install "."
     fi
     python setup.py build_ext --inplace
-    if [ "${TEST_RUN}" == "doc" ]; then
-        make doc
-    elif [ "${TEST_RUN}" == "coverage" ]; then
-        py.test --cov=bottleneck --cov-branch
-        pushd build/temp*
-        find . -iname "*.gc*" -exec mv {} ../../{} \;
-        popd
-        bash <(curl -s https://codecov.io/bash)
+    set +e
+    if [ "${TEST_RUN}" = "doc" ]; then
+	make doc
     else
-        # Workaround for https://github.com/travis-ci/travis-ci/issues/6522
-        python "tools/test-installed-bottleneck.py"
+	# Workaround for https://github.com/travis-ci/travis-ci/issues/6522
+	python "tools/test-installed-bottleneck.py"
     fi
 fi
diff --git a/tools/travis/conda_install.sh b/tools/travis/conda_install.sh
index d218b48de3..ca949c0abc 100644
--- a/tools/travis/conda_install.sh
+++ b/tools/travis/conda_install.sh
@@ -16,7 +16,6 @@ IFS=" " TEST_DEPS=(${TEST_DEPS})
 echo "Creating environment '${TEST_NAME}'..."
 conda create -q -n "${TEST_NAME}" python="${PYTHON_VERSION}" "${TEST_DEPS[@]}"
 
-
 set +v # we dont want to see commands in the conda script
 
 source activate "${TEST_NAME}"
diff --git a/tools/travis/conda_setup.sh b/tools/travis/conda_setup.sh
index faa1481d94..14fcfcb4b5 100644
--- a/tools/travis/conda_setup.sh
+++ b/tools/travis/conda_setup.sh
@@ -3,54 +3,31 @@
 set -ev # exit on first error, print commands
 
 CONDA_URL="http://repo.continuum.io/miniconda"
-MINIFORGE_URL="https://github.com/conda-forge/miniforge/"
-MINIFORGE_VERSION="4.8.2-1"
-
-if [ "${TRAVIS_CPU_ARCH}" == "amd64" ]; then
-    ARCH="x86_64"
-elif [ "${TRAVIS_CPU_ARCH}" == "arm64" ]; then
-    ARCH="aarch64"
-else
-    ARCH="${TRAVIS_CPU_ARCH}"
-fi
-
-if [ "${ARCH}" == "aarch64" ]; then
-    CONDA="Miniforge"
-else
-    CONDA="Miniconda"
-fi
 
 if [ "${PYTHON_VERSION:0:1}" == "2" ]; then
-    CONDA="${CONDA}2"
+    CONDA="Miniconda2"
 else
-    CONDA="${CONDA}3"
+    CONDA="Miniconda3"
 fi
-
 if [ "${TRAVIS_OS_NAME}" == "osx" ]; then
     CONDA_OS="MacOSX"
 else
     CONDA_OS="Linux"
 fi
-
-if [ "${ARCH}" == "aarch64" ]; then
-    URL="${MINIFORGE_URL}/releases/download/${MINIFORGE_VERSION}/${CONDA}-${MINIFORGE_VERSION}-${CONDA_OS}-${ARCH}.sh"
-elif [ "${PYTHON_ARCH}" == "64" ]; then
-    URL="${CONDA_URL}/${CONDA}-latest-${CONDA_OS}-${ARCH}.sh"
+if [ "${PYTHON_ARCH}" == "64" ]; then
+    URL="${CONDA_URL}/${CONDA}-latest-${CONDA_OS}-x86_64.sh"
 else
     URL="${CONDA_URL}/${CONDA}-latest-${CONDA_OS}-x86.sh"
 fi
 echo "Downloading '${URL}'..."
 
 set +e
-travis_retry wget "${URL}" -O installer.sh
+travis_retry wget "${URL}" -O miniconda.sh
 set -e
 
-
-chmod +x installer.sh
-./installer.sh -b -p "${HOME}/miniconda"
-
+chmod +x miniconda.sh
+./miniconda.sh -b -p "${HOME}/miniconda"
 export PATH="${HOME}/miniconda/bin:${PATH}"
 hash -r
-
 conda config --set always_yes yes --set changeps1 no
 conda update -q conda
diff --git a/tools/update_readme.py b/tools/update_readme.py
index 2aad8ef7a2..4d4d800a2d 100644
--- a/tools/update_readme.py
+++ b/tools/update_readme.py
@@ -1,6 +1,6 @@
-import os
-import sys
 from io import StringIO
+import sys
+import os
 
 import bottleneck as bn
 
diff --git a/tox.ini b/tox.ini
deleted file mode 100644
index cb2f219ea7..0000000000
--- a/tox.ini
+++ /dev/null
@@ -1,31 +0,0 @@
-# Tox (http://tox.testrun.org/) configuration
-
-[tox]
-envlist =  py27, py37
-requires = tox-venv
-    pip >= 19.0
-
-[testenv]
-changedir={envdir}
-commands={envpython} {toxinidir}/tools/test-installed-bottleneck.py {posargs:}
-
-[testenv:py27]
-basepython = python2.7
-deps =
-    pytest
-    numpy
-    hypothesis
-
-[testenv:py37]
-basepython = python3.7
-deps =
-    pytest
-    numpy
-    hypothesis
-
-# Not run by default. Use 'tox -e py27_npmaster' to call it
-[testenv:py37_npmaster]
-basepython = python3.7
-deps =
-    pytest
-    https://github.com/numpy/numpy/zipball/master
diff --git a/versioneer.py b/versioneer.py
index d5e0eb5ed2..414cb9d2d0 100644
--- a/versioneer.py
+++ b/versioneer.py
@@ -1,4 +1,5 @@
-# Version: 0.19
+
+# Version: 0.22.dev0
 
 """The Versioneer - like a rocketeer, but for versions.
 
@@ -9,11 +10,11 @@
 * https://github.com/python-versioneer/python-versioneer
 * Brian Warner
 * License: Public Domain
-* Compatible with: Python 3.6, 3.7, 3.8, 3.9 and pypy3
+* Compatible with: Python 3.6, 3.7, 3.8, 3.9, 3.10 and pypy3
 * [![Latest Version][pypi-image]][pypi-url]
 * [![Build Status][travis-image]][travis-url]
 
-This is a tool for managing a recorded version number in distutils-based
+This is a tool for managing a recorded version number in distutils/setuptools-based
 python projects. The goal is to remove the tedious and error-prone "update
 the embedded version string" step from your release process. Making a new
 release should be as easy as recording a new tag in your version-control
@@ -255,6 +256,8 @@
   dependency
 * [minver](https://github.com/jbweston/miniver) - a lightweight reimplementation of
   versioneer
+* [versioningit](https://github.com/jwodder/versioningit) - a PEP 518-based setuptools
+  plugin
 
 ## License
 
@@ -271,6 +274,11 @@
 [travis-url]: https://travis-ci.com/github/python-versioneer/python-versioneer
 
 """
+# pylint:disable=invalid-name,import-outside-toplevel,missing-function-docstring
+# pylint:disable=missing-class-docstring,too-many-branches,too-many-statements
+# pylint:disable=raise-missing-from,too-many-lines,too-many-locals,import-error
+# pylint:disable=too-few-public-methods,redefined-outer-name,consider-using-with
+# pylint:disable=attribute-defined-outside-init,too-many-arguments
 
 import configparser
 import errno
@@ -279,6 +287,7 @@
 import re
 import subprocess
 import sys
+from typing import Callable, Dict
 
 
 class VersioneerConfig:
@@ -300,13 +309,11 @@ def get_root():
         setup_py = os.path.join(root, "setup.py")
         versioneer_py = os.path.join(root, "versioneer.py")
     if not (os.path.exists(setup_py) or os.path.exists(versioneer_py)):
-        err = (
-            "Versioneer was unable to run the project root directory. "
-            "Versioneer requires setup.py to be executed from "
-            "its immediate directory (like 'python setup.py COMMAND'), "
-            "or in a way that lets it use sys.argv[0] to find the root "
-            "(like 'python path/to/setup.py COMMAND')."
-        )
+        err = ("Versioneer was unable to run the project root directory. "
+               "Versioneer requires setup.py to be executed from "
+               "its immediate directory (like 'python setup.py COMMAND'), "
+               "or in a way that lets it use sys.argv[0] to find the root "
+               "(like 'python path/to/setup.py COMMAND').")
         raise VersioneerBadRootError(err)
     try:
         # Certain runtime workflows (setup.py install/develop in a setuptools
@@ -315,14 +322,12 @@ def get_root():
         # module-import table will cache the first one. So we can't use
         # os.path.dirname(__file__), as that will find whichever
         # versioneer.py was first imported, even in later projects.
-        me = os.path.realpath(os.path.abspath(__file__))
-        me_dir = os.path.normcase(os.path.splitext(me)[0])
+        my_path = os.path.realpath(os.path.abspath(__file__))
+        me_dir = os.path.normcase(os.path.splitext(my_path)[0])
         vsr_dir = os.path.normcase(os.path.splitext(versioneer_py)[0])
         if me_dir != vsr_dir:
-            print(
-                "Warning: build in %s is using versioneer.py from %s"
-                % (os.path.dirname(me), versioneer_py)
-            )
+            print("Warning: build in %s is using versioneer.py from %s"
+                  % (os.path.dirname(my_path), versioneer_py))
     except NameError:
         pass
     return root
@@ -330,31 +335,29 @@ def get_root():
 
 def get_config_from_root(root):
     """Read the project setup.cfg file to determine Versioneer config."""
-    # This might raise EnvironmentError (if setup.cfg is missing), or
+    # This might raise OSError (if setup.cfg is missing), or
     # configparser.NoSectionError (if it lacks a [versioneer] section), or
     # configparser.NoOptionError (if it lacks "VCS="). See the docstring at
     # the top of versioneer.py for instructions on writing your setup.cfg .
     setup_cfg = os.path.join(root, "setup.cfg")
     parser = configparser.ConfigParser()
-    with open(setup_cfg, "r") as f:
-        parser.read_file(f)
+    with open(setup_cfg, "r") as cfg_file:
+        parser.read_file(cfg_file)
     VCS = parser.get("versioneer", "VCS")  # mandatory
 
-    def get(parser, name):
-        if parser.has_option("versioneer", name):
-            return parser.get("versioneer", name)
-        return None
+    # Dict-like interface for non-mandatory entries
+    section = parser["versioneer"]
 
     cfg = VersioneerConfig()
     cfg.VCS = VCS
-    cfg.style = get(parser, "style") or ""
-    cfg.versionfile_source = get(parser, "versionfile_source")
-    cfg.versionfile_build = get(parser, "versionfile_build")
-    cfg.tag_prefix = get(parser, "tag_prefix")
+    cfg.style = section.get("style", "")
+    cfg.versionfile_source = section.get("versionfile_source")
+    cfg.versionfile_build = section.get("versionfile_build")
+    cfg.tag_prefix = section.get("tag_prefix")
     if cfg.tag_prefix in ("''", '""'):
         cfg.tag_prefix = ""
-    cfg.parentdir_prefix = get(parser, "parentdir_prefix")
-    cfg.verbose = get(parser, "verbose")
+    cfg.parentdir_prefix = section.get("parentdir_prefix")
+    cfg.verbose = section.get("verbose")
     return cfg
 
 
@@ -363,40 +366,42 @@ class NotThisMethod(Exception):
 
 
 # these dictionaries contain VCS-specific tools
-LONG_VERSION_PY = {}
-HANDLERS = {}
+LONG_VERSION_PY: Dict[str, str] = {}
+HANDLERS: Dict[str, Dict[str, Callable]] = {}
 
 
 def register_vcs_handler(vcs, method):  # decorator
     """Create decorator to mark a method as the handler of a VCS."""
-
     def decorate(f):
         """Store f in HANDLERS[vcs][method]."""
-        if vcs not in HANDLERS:
-            HANDLERS[vcs] = {}
-        HANDLERS[vcs][method] = f
+        HANDLERS.setdefault(vcs, {})[method] = f
         return f
-
     return decorate
 
 
-def run_command(commands, args, cwd=None, verbose=False, hide_stderr=False, env=None):
+def run_command(commands, args, cwd=None, verbose=False, hide_stderr=False,
+                env=None):
     """Call the given command(s)."""
     assert isinstance(commands, list)
-    p = None
-    for c in commands:
+    process = None
+
+    popen_kwargs = {}
+    if sys.platform == "win32":
+        # This hides the console window if pythonw.exe is used
+        startupinfo = subprocess.STARTUPINFO()
+        startupinfo.dwFlags |= subprocess.STARTF_USESHOWWINDOW
+        popen_kwargs["startupinfo"] = startupinfo
+
+    for command in commands:
         try:
-            dispcmd = str([c] + args)
+            dispcmd = str([command] + args)
             # remember shell=False, so use git.cmd on windows, not just git
-            p = subprocess.Popen(
-                [c] + args,
-                cwd=cwd,
-                env=env,
-                stdout=subprocess.PIPE,
-                stderr=(subprocess.PIPE if hide_stderr else None),
-            )
+            process = subprocess.Popen([command] + args, cwd=cwd, env=env,
+                                       stdout=subprocess.PIPE,
+                                       stderr=(subprocess.PIPE if hide_stderr
+                                               else None), **popen_kwargs)
             break
-        except EnvironmentError:
+        except OSError:
             e = sys.exc_info()[1]
             if e.errno == errno.ENOENT:
                 continue
@@ -408,18 +413,16 @@ def run_command(commands, args, cwd=None, verbose=False, hide_stderr=False, env=
         if verbose:
             print("unable to find command, tried %s" % (commands,))
         return None, None
-    stdout = p.communicate()[0].strip().decode()
-    if p.returncode != 0:
+    stdout = process.communicate()[0].strip().decode()
+    if process.returncode != 0:
         if verbose:
             print("unable to run %s (error)" % dispcmd)
             print("stdout was %s" % stdout)
-        return None, p.returncode
-    return stdout, p.returncode
+        return None, process.returncode
+    return stdout, process.returncode
 
 
-LONG_VERSION_PY[
-    "git"
-] = r'''
+LONG_VERSION_PY['git'] = r'''
 # This file helps to compute a version number in source trees obtained from
 # git-archive tarball (such as those provided by githubs download-from-tag
 # feature). Distribution tarballs (built by setup.py sdist) and build
@@ -427,7 +430,7 @@ def run_command(commands, args, cwd=None, verbose=False, hide_stderr=False, env=
 # that just contains the computed version number.
 
 # This file is released into the public domain. Generated by
-# versioneer-0.19 (https://github.com/python-versioneer/python-versioneer)
+# versioneer-0.22.dev0 (https://github.com/python-versioneer/python-versioneer)
 
 """Git implementation of _version.py."""
 
@@ -436,6 +439,7 @@ def run_command(commands, args, cwd=None, verbose=False, hide_stderr=False, env=
 import re
 import subprocess
 import sys
+from typing import Callable, Dict
 
 
 def get_keywords():
@@ -473,8 +477,8 @@ class NotThisMethod(Exception):
     """Exception raised if a method is not valid for the current scenario."""
 
 
-LONG_VERSION_PY = {}
-HANDLERS = {}
+LONG_VERSION_PY: Dict[str, str] = {}
+HANDLERS: Dict[str, Dict[str, Callable]] = {}
 
 
 def register_vcs_handler(vcs, method):  # decorator
@@ -492,17 +496,25 @@ def run_command(commands, args, cwd=None, verbose=False, hide_stderr=False,
                 env=None):
     """Call the given command(s)."""
     assert isinstance(commands, list)
-    p = None
-    for c in commands:
+    process = None
+
+    popen_kwargs = {}
+    if sys.platform == "win32":
+        # This hides the console window if pythonw.exe is used
+        startupinfo = subprocess.STARTUPINFO()
+        startupinfo.dwFlags |= subprocess.STARTF_USESHOWWINDOW
+        popen_kwargs["startupinfo"] = startupinfo
+
+    for command in commands:
         try:
-            dispcmd = str([c] + args)
+            dispcmd = str([command] + args)
             # remember shell=False, so use git.cmd on windows, not just git
-            p = subprocess.Popen([c] + args, cwd=cwd, env=env,
-                                 stdout=subprocess.PIPE,
-                                 stderr=(subprocess.PIPE if hide_stderr
-                                         else None))
+            process = subprocess.Popen([command] + args, cwd=cwd, env=env,
+                                       stdout=subprocess.PIPE,
+                                       stderr=(subprocess.PIPE if hide_stderr
+                                               else None), **popen_kwargs)
             break
-        except EnvironmentError:
+        except OSError:
             e = sys.exc_info()[1]
             if e.errno == errno.ENOENT:
                 continue
@@ -514,13 +526,13 @@ def run_command(commands, args, cwd=None, verbose=False, hide_stderr=False,
         if verbose:
             print("unable to find command, tried %%s" %% (commands,))
         return None, None
-    stdout = p.communicate()[0].strip().decode()
-    if p.returncode != 0:
+    stdout = process.communicate()[0].strip().decode()
+    if process.returncode != 0:
         if verbose:
             print("unable to run %%s (error)" %% dispcmd)
             print("stdout was %%s" %% stdout)
-        return None, p.returncode
-    return stdout, p.returncode
+        return None, process.returncode
+    return stdout, process.returncode
 
 
 def versions_from_parentdir(parentdir_prefix, root, verbose):
@@ -532,15 +544,14 @@ def versions_from_parentdir(parentdir_prefix, root, verbose):
     """
     rootdirs = []
 
-    for i in range(3):
+    for _ in range(3):
         dirname = os.path.basename(root)
         if dirname.startswith(parentdir_prefix):
             return {"version": dirname[len(parentdir_prefix):],
                     "full-revisionid": None,
                     "dirty": False, "error": None, "date": None}
-        else:
-            rootdirs.append(root)
-            root = os.path.dirname(root)  # up a level
+        rootdirs.append(root)
+        root = os.path.dirname(root)  # up a level
 
     if verbose:
         print("Tried directories %%s but none started with prefix %%s" %%
@@ -557,22 +568,21 @@ def git_get_keywords(versionfile_abs):
     # _version.py.
     keywords = {}
     try:
-        f = open(versionfile_abs, "r")
-        for line in f.readlines():
-            if line.strip().startswith("git_refnames ="):
-                mo = re.search(r'=\s*"(.*)"', line)
-                if mo:
-                    keywords["refnames"] = mo.group(1)
-            if line.strip().startswith("git_full ="):
-                mo = re.search(r'=\s*"(.*)"', line)
-                if mo:
-                    keywords["full"] = mo.group(1)
-            if line.strip().startswith("git_date ="):
-                mo = re.search(r'=\s*"(.*)"', line)
-                if mo:
-                    keywords["date"] = mo.group(1)
-        f.close()
-    except EnvironmentError:
+        with open(versionfile_abs, "r") as fobj:
+            for line in fobj:
+                if line.strip().startswith("git_refnames ="):
+                    mo = re.search(r'=\s*"(.*)"', line)
+                    if mo:
+                        keywords["refnames"] = mo.group(1)
+                if line.strip().startswith("git_full ="):
+                    mo = re.search(r'=\s*"(.*)"', line)
+                    if mo:
+                        keywords["full"] = mo.group(1)
+                if line.strip().startswith("git_date ="):
+                    mo = re.search(r'=\s*"(.*)"', line)
+                    if mo:
+                        keywords["date"] = mo.group(1)
+    except OSError:
         pass
     return keywords
 
@@ -580,8 +590,8 @@ def git_get_keywords(versionfile_abs):
 @register_vcs_handler("git", "keywords")
 def git_versions_from_keywords(keywords, tag_prefix, verbose):
     """Get version information from git keywords."""
-    if not keywords:
-        raise NotThisMethod("no keywords at all, weird")
+    if "refnames" not in keywords:
+        raise NotThisMethod("Short version file found")
     date = keywords.get("date")
     if date is not None:
         # Use only the last line.  Previous lines may contain GPG signature
@@ -600,11 +610,11 @@ def git_versions_from_keywords(keywords, tag_prefix, verbose):
         if verbose:
             print("keywords are unexpanded, not using")
         raise NotThisMethod("unexpanded keywords, not a git-archive tarball")
-    refs = set([r.strip() for r in refnames.strip("()").split(",")])
+    refs = {r.strip() for r in refnames.strip("()").split(",")}
     # starting in git-1.8.3, tags are listed as "tag: foo-1.0" instead of
     # just "foo-1.0". If we see a "tag: " prefix, prefer those.
     TAG = "tag: "
-    tags = set([r[len(TAG):] for r in refs if r.startswith(TAG)])
+    tags = {r[len(TAG):] for r in refs if r.startswith(TAG)}
     if not tags:
         # Either we're using git < 1.8.3, or there really are no tags. We use
         # a heuristic: assume all version tags have a digit. The old git %%d
@@ -613,7 +623,7 @@ def git_versions_from_keywords(keywords, tag_prefix, verbose):
         # between branches and tags. By ignoring refnames without digits, we
         # filter out many common branch names like "release" and
         # "stabilization", as well as "HEAD" and "master".
-        tags = set([r for r in refs if re.search(r'\d', r)])
+        tags = {r for r in refs if re.search(r'\d', r)}
         if verbose:
             print("discarding '%%s', no digits" %% ",".join(refs - tags))
     if verbose:
@@ -622,6 +632,11 @@ def git_versions_from_keywords(keywords, tag_prefix, verbose):
         # sorting will prefer e.g. "2.0" over "2.0rc1"
         if ref.startswith(tag_prefix):
             r = ref[len(tag_prefix):]
+            # Filter out refs that exactly match prefix or that don't start
+            # with a number once the prefix is stripped (mostly a concern
+            # when prefix is '')
+            if not re.match(r'\d', r):
+                continue
             if verbose:
                 print("picking %%s" %% r)
             return {"version": r,
@@ -637,7 +652,7 @@ def git_versions_from_keywords(keywords, tag_prefix, verbose):
 
 
 @register_vcs_handler("git", "pieces_from_vcs")
-def git_pieces_from_vcs(tag_prefix, root, verbose, run_command=run_command):
+def git_pieces_from_vcs(tag_prefix, root, verbose, runner=run_command):
     """Get version from 'git describe' in the root of the source tree.
 
     This only gets called if the git-archive 'subst' keywords were *not*
@@ -648,24 +663,25 @@ def git_pieces_from_vcs(tag_prefix, root, verbose, run_command=run_command):
     if sys.platform == "win32":
         GITS = ["git.cmd", "git.exe"]
 
-    out, rc = run_command(GITS, ["rev-parse", "--git-dir"], cwd=root,
-                          hide_stderr=True)
+    _, rc = runner(GITS, ["rev-parse", "--git-dir"], cwd=root,
+                   hide_stderr=True)
     if rc != 0:
         if verbose:
             print("Directory %%s not under git control" %% root)
         raise NotThisMethod("'git rev-parse --git-dir' returned error")
 
+    MATCH_ARGS = ["--match", "%%s*" %% tag_prefix] if tag_prefix else []
+
     # if there is a tag matching tag_prefix, this yields TAG-NUM-gHEX[-dirty]
     # if there isn't one, this yields HEX[-dirty] (no NUM)
-    describe_out, rc = run_command(GITS, ["describe", "--tags", "--dirty",
-                                          "--always", "--long",
-                                          "--match", "%%s*" %% tag_prefix],
-                                   cwd=root)
+    describe_out, rc = runner(GITS, ["describe", "--tags", "--dirty",
+                                     "--always", "--long", *MATCH_ARGS],
+                              cwd=root)
     # --long was added in git-1.5.5
     if describe_out is None:
         raise NotThisMethod("'git describe' failed")
     describe_out = describe_out.strip()
-    full_out, rc = run_command(GITS, ["rev-parse", "HEAD"], cwd=root)
+    full_out, rc = runner(GITS, ["rev-parse", "HEAD"], cwd=root)
     if full_out is None:
         raise NotThisMethod("'git rev-parse' failed")
     full_out = full_out.strip()
@@ -675,6 +691,39 @@ def git_pieces_from_vcs(tag_prefix, root, verbose, run_command=run_command):
     pieces["short"] = full_out[:7]  # maybe improved later
     pieces["error"] = None
 
+    branch_name, rc = runner(GITS, ["rev-parse", "--abbrev-ref", "HEAD"],
+                             cwd=root)
+    # --abbrev-ref was added in git-1.6.3
+    if rc != 0 or branch_name is None:
+        raise NotThisMethod("'git rev-parse --abbrev-ref' returned error")
+    branch_name = branch_name.strip()
+
+    if branch_name == "HEAD":
+        # If we aren't exactly on a branch, pick a branch which represents
+        # the current commit. If all else fails, we are on a branchless
+        # commit.
+        branches, rc = runner(GITS, ["branch", "--contains"], cwd=root)
+        # --contains was added in git-1.5.4
+        if rc != 0 or branches is None:
+            raise NotThisMethod("'git branch --contains' returned error")
+        branches = branches.split("\n")
+
+        # Remove the first line if we're running detached
+        if "(" in branches[0]:
+            branches.pop(0)
+
+        # Strip off the leading "* " from the list of branches.
+        branches = [branch[2:] for branch in branches]
+        if "master" in branches:
+            branch_name = "master"
+        elif not branches:
+            branch_name = None
+        else:
+            # Pick the first branch that is returned. Good or bad.
+            branch_name = branches[0]
+
+    pieces["branch"] = branch_name
+
     # parse describe_out. It will be like TAG-NUM-gHEX[-dirty] or HEX[-dirty]
     # TAG might have hyphens.
     git_describe = describe_out
@@ -691,7 +740,7 @@ def git_pieces_from_vcs(tag_prefix, root, verbose, run_command=run_command):
         # TAG-NUM-gHEX
         mo = re.search(r'^(.+)-(\d+)-g([0-9a-f]+)$', git_describe)
         if not mo:
-            # unparseable. Maybe git-describe is misbehaving?
+            # unparsable. Maybe git-describe is misbehaving?
             pieces["error"] = ("unable to parse git-describe output: '%%s'"
                                %% describe_out)
             return pieces
@@ -716,13 +765,11 @@ def git_pieces_from_vcs(tag_prefix, root, verbose, run_command=run_command):
     else:
         # HEX: no tags
         pieces["closest-tag"] = None
-        count_out, rc = run_command(GITS, ["rev-list", "HEAD", "--count"],
-                                    cwd=root)
+        count_out, rc = runner(GITS, ["rev-list", "HEAD", "--count"], cwd=root)
         pieces["distance"] = int(count_out)  # total number of commits
 
     # commit date: see ISO-8601 comment in git_versions_from_keywords()
-    date = run_command(GITS, ["show", "-s", "--format=%%ci", "HEAD"],
-                       cwd=root)[0].strip()
+    date = runner(GITS, ["show", "-s", "--format=%%ci", "HEAD"], cwd=root)[0].strip()
     # Use only the last line.  Previous lines may contain GPG signature
     # information.
     date = date.splitlines()[-1]
@@ -763,16 +810,64 @@ def render_pep440(pieces):
     return rendered
 
 
+def render_pep440_branch(pieces):
+    """TAG[[.dev0]+DISTANCE.gHEX[.dirty]] .
+
+    The ".dev0" means not master branch. Note that .dev0 sorts backwards
+    (a feature branch will appear "older" than the master branch).
+
+    Exceptions:
+    1: no tags. 0[.dev0]+untagged.DISTANCE.gHEX[.dirty]
+    """
+    if pieces["closest-tag"]:
+        rendered = pieces["closest-tag"]
+        if pieces["distance"] or pieces["dirty"]:
+            if pieces["branch"] != "master":
+                rendered += ".dev0"
+            rendered += plus_or_dot(pieces)
+            rendered += "%%d.g%%s" %% (pieces["distance"], pieces["short"])
+            if pieces["dirty"]:
+                rendered += ".dirty"
+    else:
+        # exception #1
+        rendered = "0"
+        if pieces["branch"] != "master":
+            rendered += ".dev0"
+        rendered += "+untagged.%%d.g%%s" %% (pieces["distance"],
+                                          pieces["short"])
+        if pieces["dirty"]:
+            rendered += ".dirty"
+    return rendered
+
+
+def pep440_split_post(ver):
+    """Split pep440 version string at the post-release segment.
+
+    Returns the release segments before the post-release and the
+    post-release version number (or -1 if no post-release segment is present).
+    """
+    vc = str.split(ver, ".post")
+    return vc[0], int(vc[1] or 0) if len(vc) == 2 else None
+
+
 def render_pep440_pre(pieces):
-    """TAG[.post0.devDISTANCE] -- No -dirty.
+    """TAG[.postN.devDISTANCE] -- No -dirty.
 
     Exceptions:
     1: no tags. 0.post0.devDISTANCE
     """
     if pieces["closest-tag"]:
-        rendered = pieces["closest-tag"]
         if pieces["distance"]:
-            rendered += ".post0.dev%%d" %% pieces["distance"]
+            # update the post release segment
+            tag_version, post_version = pep440_split_post(pieces["closest-tag"])
+            rendered = tag_version
+            if post_version is not None:
+                rendered += ".post%%d.dev%%d" %% (post_version+1, pieces["distance"])
+            else:
+                rendered += ".post0.dev%%d" %% (pieces["distance"])
+        else:
+            # no commits, use the tag as the version
+            rendered = pieces["closest-tag"]
     else:
         # exception #1
         rendered = "0.post0.dev%%d" %% pieces["distance"]
@@ -806,6 +901,35 @@ def render_pep440_post(pieces):
     return rendered
 
 
+def render_pep440_post_branch(pieces):
+    """TAG[.postDISTANCE[.dev0]+gHEX[.dirty]] .
+
+    The ".dev0" means not master branch.
+
+    Exceptions:
+    1: no tags. 0.postDISTANCE[.dev0]+gHEX[.dirty]
+    """
+    if pieces["closest-tag"]:
+        rendered = pieces["closest-tag"]
+        if pieces["distance"] or pieces["dirty"]:
+            rendered += ".post%%d" %% pieces["distance"]
+            if pieces["branch"] != "master":
+                rendered += ".dev0"
+            rendered += plus_or_dot(pieces)
+            rendered += "g%%s" %% pieces["short"]
+            if pieces["dirty"]:
+                rendered += ".dirty"
+    else:
+        # exception #1
+        rendered = "0.post%%d" %% pieces["distance"]
+        if pieces["branch"] != "master":
+            rendered += ".dev0"
+        rendered += "+g%%s" %% pieces["short"]
+        if pieces["dirty"]:
+            rendered += ".dirty"
+    return rendered
+
+
 def render_pep440_old(pieces):
     """TAG[.postDISTANCE[.dev0]] .
 
@@ -882,10 +1006,14 @@ def render(pieces, style):
 
     if style == "pep440":
         rendered = render_pep440(pieces)
+    elif style == "pep440-branch":
+        rendered = render_pep440_branch(pieces)
     elif style == "pep440-pre":
         rendered = render_pep440_pre(pieces)
     elif style == "pep440-post":
         rendered = render_pep440_post(pieces)
+    elif style == "pep440-post-branch":
+        rendered = render_pep440_post_branch(pieces)
     elif style == "pep440-old":
         rendered = render_pep440_old(pieces)
     elif style == "git-describe":
@@ -921,7 +1049,7 @@ def get_versions():
         # versionfile_source is the relative path from the top of the source
         # tree (where the .git directory might live) to this file. Invert
         # this to find the root from __file__.
-        for i in cfg.versionfile_source.split('/'):
+        for _ in cfg.versionfile_source.split('/'):
             root = os.path.dirname(root)
     except NameError:
         return {"version": "0+unknown", "full-revisionid": None,
@@ -956,22 +1084,21 @@ def git_get_keywords(versionfile_abs):
     # _version.py.
     keywords = {}
     try:
-        f = open(versionfile_abs, "r")
-        for line in f.readlines():
-            if line.strip().startswith("git_refnames ="):
-                mo = re.search(r'=\s*"(.*)"', line)
-                if mo:
-                    keywords["refnames"] = mo.group(1)
-            if line.strip().startswith("git_full ="):
-                mo = re.search(r'=\s*"(.*)"', line)
-                if mo:
-                    keywords["full"] = mo.group(1)
-            if line.strip().startswith("git_date ="):
-                mo = re.search(r'=\s*"(.*)"', line)
-                if mo:
-                    keywords["date"] = mo.group(1)
-        f.close()
-    except EnvironmentError:
+        with open(versionfile_abs, "r") as fobj:
+            for line in fobj:
+                if line.strip().startswith("git_refnames ="):
+                    mo = re.search(r'=\s*"(.*)"', line)
+                    if mo:
+                        keywords["refnames"] = mo.group(1)
+                if line.strip().startswith("git_full ="):
+                    mo = re.search(r'=\s*"(.*)"', line)
+                    if mo:
+                        keywords["full"] = mo.group(1)
+                if line.strip().startswith("git_date ="):
+                    mo = re.search(r'=\s*"(.*)"', line)
+                    if mo:
+                        keywords["date"] = mo.group(1)
+    except OSError:
         pass
     return keywords
 
@@ -979,8 +1106,8 @@ def git_get_keywords(versionfile_abs):
 @register_vcs_handler("git", "keywords")
 def git_versions_from_keywords(keywords, tag_prefix, verbose):
     """Get version information from git keywords."""
-    if not keywords:
-        raise NotThisMethod("no keywords at all, weird")
+    if "refnames" not in keywords:
+        raise NotThisMethod("Short version file found")
     date = keywords.get("date")
     if date is not None:
         # Use only the last line.  Previous lines may contain GPG signature
@@ -999,11 +1126,11 @@ def git_versions_from_keywords(keywords, tag_prefix, verbose):
         if verbose:
             print("keywords are unexpanded, not using")
         raise NotThisMethod("unexpanded keywords, not a git-archive tarball")
-    refs = set([r.strip() for r in refnames.strip("()").split(",")])
+    refs = {r.strip() for r in refnames.strip("()").split(",")}
     # starting in git-1.8.3, tags are listed as "tag: foo-1.0" instead of
     # just "foo-1.0". If we see a "tag: " prefix, prefer those.
     TAG = "tag: "
-    tags = set([r[len(TAG) :] for r in refs if r.startswith(TAG)])
+    tags = {r[len(TAG):] for r in refs if r.startswith(TAG)}
     if not tags:
         # Either we're using git < 1.8.3, or there really are no tags. We use
         # a heuristic: assume all version tags have a digit. The old git %d
@@ -1012,7 +1139,7 @@ def git_versions_from_keywords(keywords, tag_prefix, verbose):
         # between branches and tags. By ignoring refnames without digits, we
         # filter out many common branch names like "release" and
         # "stabilization", as well as "HEAD" and "master".
-        tags = set([r for r in refs if re.search(r"\d", r)])
+        tags = {r for r in refs if re.search(r'\d', r)}
         if verbose:
             print("discarding '%s', no digits" % ",".join(refs - tags))
     if verbose:
@@ -1020,30 +1147,28 @@ def git_versions_from_keywords(keywords, tag_prefix, verbose):
     for ref in sorted(tags):
         # sorting will prefer e.g. "2.0" over "2.0rc1"
         if ref.startswith(tag_prefix):
-            r = ref[len(tag_prefix) :]
+            r = ref[len(tag_prefix):]
+            # Filter out refs that exactly match prefix or that don't start
+            # with a number once the prefix is stripped (mostly a concern
+            # when prefix is '')
+            if not re.match(r'\d', r):
+                continue
             if verbose:
                 print("picking %s" % r)
-            return {
-                "version": r,
-                "full-revisionid": keywords["full"].strip(),
-                "dirty": False,
-                "error": None,
-                "date": date,
-            }
+            return {"version": r,
+                    "full-revisionid": keywords["full"].strip(),
+                    "dirty": False, "error": None,
+                    "date": date}
     # no suitable tags, so version is "0+unknown", but full hex is still there
     if verbose:
         print("no suitable tags, using unknown + full revision id")
-    return {
-        "version": "0+unknown",
-        "full-revisionid": keywords["full"].strip(),
-        "dirty": False,
-        "error": "no suitable tags",
-        "date": None,
-    }
+    return {"version": "0+unknown",
+            "full-revisionid": keywords["full"].strip(),
+            "dirty": False, "error": "no suitable tags", "date": None}
 
 
 @register_vcs_handler("git", "pieces_from_vcs")
-def git_pieces_from_vcs(tag_prefix, root, verbose, run_command=run_command):
+def git_pieces_from_vcs(tag_prefix, root, verbose, runner=run_command):
     """Get version from 'git describe' in the root of the source tree.
 
     This only gets called if the git-archive 'subst' keywords were *not*
@@ -1054,32 +1179,25 @@ def git_pieces_from_vcs(tag_prefix, root, verbose, run_command=run_command):
     if sys.platform == "win32":
         GITS = ["git.cmd", "git.exe"]
 
-    out, rc = run_command(GITS, ["rev-parse", "--git-dir"], cwd=root, hide_stderr=True)
+    _, rc = runner(GITS, ["rev-parse", "--git-dir"], cwd=root,
+                   hide_stderr=True)
     if rc != 0:
         if verbose:
             print("Directory %s not under git control" % root)
         raise NotThisMethod("'git rev-parse --git-dir' returned error")
 
+    MATCH_ARGS = ["--match", "%s*" % tag_prefix] if tag_prefix else []
+
     # if there is a tag matching tag_prefix, this yields TAG-NUM-gHEX[-dirty]
     # if there isn't one, this yields HEX[-dirty] (no NUM)
-    describe_out, rc = run_command(
-        GITS,
-        [
-            "describe",
-            "--tags",
-            "--dirty",
-            "--always",
-            "--long",
-            "--match",
-            "%s*" % tag_prefix,
-        ],
-        cwd=root,
-    )
+    describe_out, rc = runner(GITS, ["describe", "--tags", "--dirty",
+                                     "--always", "--long", *MATCH_ARGS],
+                              cwd=root)
     # --long was added in git-1.5.5
     if describe_out is None:
         raise NotThisMethod("'git describe' failed")
     describe_out = describe_out.strip()
-    full_out, rc = run_command(GITS, ["rev-parse", "HEAD"], cwd=root)
+    full_out, rc = runner(GITS, ["rev-parse", "HEAD"], cwd=root)
     if full_out is None:
         raise NotThisMethod("'git rev-parse' failed")
     full_out = full_out.strip()
@@ -1089,6 +1207,39 @@ def git_pieces_from_vcs(tag_prefix, root, verbose, run_command=run_command):
     pieces["short"] = full_out[:7]  # maybe improved later
     pieces["error"] = None
 
+    branch_name, rc = runner(GITS, ["rev-parse", "--abbrev-ref", "HEAD"],
+                             cwd=root)
+    # --abbrev-ref was added in git-1.6.3
+    if rc != 0 or branch_name is None:
+        raise NotThisMethod("'git rev-parse --abbrev-ref' returned error")
+    branch_name = branch_name.strip()
+
+    if branch_name == "HEAD":
+        # If we aren't exactly on a branch, pick a branch which represents
+        # the current commit. If all else fails, we are on a branchless
+        # commit.
+        branches, rc = runner(GITS, ["branch", "--contains"], cwd=root)
+        # --contains was added in git-1.5.4
+        if rc != 0 or branches is None:
+            raise NotThisMethod("'git branch --contains' returned error")
+        branches = branches.split("\n")
+
+        # Remove the first line if we're running detached
+        if "(" in branches[0]:
+            branches.pop(0)
+
+        # Strip off the leading "* " from the list of branches.
+        branches = [branch[2:] for branch in branches]
+        if "master" in branches:
+            branch_name = "master"
+        elif not branches:
+            branch_name = None
+        else:
+            # Pick the first branch that is returned. Good or bad.
+            branch_name = branches[0]
+
+    pieces["branch"] = branch_name
+
     # parse describe_out. It will be like TAG-NUM-gHEX[-dirty] or HEX[-dirty]
     # TAG might have hyphens.
     git_describe = describe_out
@@ -1097,16 +1248,17 @@ def git_pieces_from_vcs(tag_prefix, root, verbose, run_command=run_command):
     dirty = git_describe.endswith("-dirty")
     pieces["dirty"] = dirty
     if dirty:
-        git_describe = git_describe[: git_describe.rindex("-dirty")]
+        git_describe = git_describe[:git_describe.rindex("-dirty")]
 
     # now we have TAG-NUM-gHEX or HEX
 
     if "-" in git_describe:
         # TAG-NUM-gHEX
-        mo = re.search(r"^(.+)-(\d+)-g([0-9a-f]+)$", git_describe)
+        mo = re.search(r'^(.+)-(\d+)-g([0-9a-f]+)$', git_describe)
         if not mo:
-            # unparseable. Maybe git-describe is misbehaving?
-            pieces["error"] = "unable to parse git-describe output: '%s'" % describe_out
+            # unparsable. Maybe git-describe is misbehaving?
+            pieces["error"] = ("unable to parse git-describe output: '%s'"
+                               % describe_out)
             return pieces
 
         # tag
@@ -1115,12 +1267,10 @@ def git_pieces_from_vcs(tag_prefix, root, verbose, run_command=run_command):
             if verbose:
                 fmt = "tag '%s' doesn't start with prefix '%s'"
                 print(fmt % (full_tag, tag_prefix))
-            pieces["error"] = "tag '%s' doesn't start with prefix '%s'" % (
-                full_tag,
-                tag_prefix,
-            )
+            pieces["error"] = ("tag '%s' doesn't start with prefix '%s'"
+                               % (full_tag, tag_prefix))
             return pieces
-        pieces["closest-tag"] = full_tag[len(tag_prefix) :]
+        pieces["closest-tag"] = full_tag[len(tag_prefix):]
 
         # distance: number of commits since tag
         pieces["distance"] = int(mo.group(2))
@@ -1131,13 +1281,11 @@ def git_pieces_from_vcs(tag_prefix, root, verbose, run_command=run_command):
     else:
         # HEX: no tags
         pieces["closest-tag"] = None
-        count_out, rc = run_command(GITS, ["rev-list", "HEAD", "--count"], cwd=root)
+        count_out, rc = runner(GITS, ["rev-list", "HEAD", "--count"], cwd=root)
         pieces["distance"] = int(count_out)  # total number of commits
 
     # commit date: see ISO-8601 comment in git_versions_from_keywords()
-    date = run_command(GITS, ["show", "-s", "--format=%ci", "HEAD"], cwd=root)[
-        0
-    ].strip()
+    date = runner(GITS, ["show", "-s", "--format=%ci", "HEAD"], cwd=root)[0].strip()
     # Use only the last line.  Previous lines may contain GPG signature
     # information.
     date = date.splitlines()[-1]
@@ -1159,27 +1307,26 @@ def do_vcs_install(manifest_in, versionfile_source, ipy):
     if ipy:
         files.append(ipy)
     try:
-        me = __file__
-        if me.endswith(".pyc") or me.endswith(".pyo"):
-            me = os.path.splitext(me)[0] + ".py"
-        versioneer_file = os.path.relpath(me)
+        my_path = __file__
+        if my_path.endswith(".pyc") or my_path.endswith(".pyo"):
+            my_path = os.path.splitext(my_path)[0] + ".py"
+        versioneer_file = os.path.relpath(my_path)
     except NameError:
         versioneer_file = "versioneer.py"
     files.append(versioneer_file)
     present = False
     try:
-        f = open(".gitattributes", "r")
-        for line in f.readlines():
-            if line.strip().startswith(versionfile_source):
-                if "export-subst" in line.strip().split()[1:]:
-                    present = True
-        f.close()
-    except EnvironmentError:
+        with open(".gitattributes", "r") as fobj:
+            for line in fobj:
+                if line.strip().startswith(versionfile_source):
+                    if "export-subst" in line.strip().split()[1:]:
+                        present = True
+                        break
+    except OSError:
         pass
     if not present:
-        f = open(".gitattributes", "a+")
-        f.write("%s export-subst\n" % versionfile_source)
-        f.close()
+        with open(".gitattributes", "a+") as fobj:
+            fobj.write(f"{versionfile_source} export-subst\n")
         files.append(".gitattributes")
     run_command(GITS, ["add", "--"] + files)
 
@@ -1193,30 +1340,23 @@ def versions_from_parentdir(parentdir_prefix, root, verbose):
     """
     rootdirs = []
 
-    for i in range(3):
+    for _ in range(3):
         dirname = os.path.basename(root)
         if dirname.startswith(parentdir_prefix):
-            return {
-                "version": dirname[len(parentdir_prefix) :],
-                "full-revisionid": None,
-                "dirty": False,
-                "error": None,
-                "date": None,
-            }
-        else:
-            rootdirs.append(root)
-            root = os.path.dirname(root)  # up a level
+            return {"version": dirname[len(parentdir_prefix):],
+                    "full-revisionid": None,
+                    "dirty": False, "error": None, "date": None}
+        rootdirs.append(root)
+        root = os.path.dirname(root)  # up a level
 
     if verbose:
-        print(
-            "Tried directories %s but none started with prefix %s"
-            % (str(rootdirs), parentdir_prefix)
-        )
+        print("Tried directories %s but none started with prefix %s" %
+              (str(rootdirs), parentdir_prefix))
     raise NotThisMethod("rootdir doesn't start with parentdir_prefix")
 
 
 SHORT_VERSION_PY = """
-# This file was generated by 'versioneer.py' (0.19) from
+# This file was generated by 'versioneer.py' (0.22.dev0) from
 # revision-control system data, or from the parent directory name of an
 # unpacked source archive. Distribution tarballs contain a pre-generated copy
 # of this file.
@@ -1238,15 +1378,13 @@ def versions_from_file(filename):
     try:
         with open(filename) as f:
             contents = f.read()
-    except EnvironmentError:
+    except OSError:
         raise NotThisMethod("unable to read _version.py")
-    mo = re.search(
-        r"version_json = '''\n(.*)'''  # END VERSION_JSON", contents, re.M | re.S
-    )
+    mo = re.search(r"version_json = '''\n(.*)'''  # END VERSION_JSON",
+                   contents, re.M | re.S)
     if not mo:
-        mo = re.search(
-            r"version_json = '''\r\n(.*)'''  # END VERSION_JSON", contents, re.M | re.S
-        )
+        mo = re.search(r"version_json = '''\r\n(.*)'''  # END VERSION_JSON",
+                       contents, re.M | re.S)
     if not mo:
         raise NotThisMethod("no version_json in _version.py")
     return json.loads(mo.group(1))
@@ -1255,7 +1393,8 @@ def versions_from_file(filename):
 def write_to_version_file(filename, versions):
     """Write the given version number to the given _version.py file."""
     os.unlink(filename)
-    contents = json.dumps(versions, sort_keys=True, indent=1, separators=(",", ": "))
+    contents = json.dumps(versions, sort_keys=True,
+                          indent=1, separators=(",", ": "))
     with open(filename, "w") as f:
         f.write(SHORT_VERSION_PY % contents)
 
@@ -1287,22 +1426,71 @@ def render_pep440(pieces):
                 rendered += ".dirty"
     else:
         # exception #1
-        rendered = "0+untagged.%d.g%s" % (pieces["distance"], pieces["short"])
+        rendered = "0+untagged.%d.g%s" % (pieces["distance"],
+                                          pieces["short"])
+        if pieces["dirty"]:
+            rendered += ".dirty"
+    return rendered
+
+
+def render_pep440_branch(pieces):
+    """TAG[[.dev0]+DISTANCE.gHEX[.dirty]] .
+
+    The ".dev0" means not master branch. Note that .dev0 sorts backwards
+    (a feature branch will appear "older" than the master branch).
+
+    Exceptions:
+    1: no tags. 0[.dev0]+untagged.DISTANCE.gHEX[.dirty]
+    """
+    if pieces["closest-tag"]:
+        rendered = pieces["closest-tag"]
+        if pieces["distance"] or pieces["dirty"]:
+            if pieces["branch"] != "master":
+                rendered += ".dev0"
+            rendered += plus_or_dot(pieces)
+            rendered += "%d.g%s" % (pieces["distance"], pieces["short"])
+            if pieces["dirty"]:
+                rendered += ".dirty"
+    else:
+        # exception #1
+        rendered = "0"
+        if pieces["branch"] != "master":
+            rendered += ".dev0"
+        rendered += "+untagged.%d.g%s" % (pieces["distance"],
+                                          pieces["short"])
         if pieces["dirty"]:
             rendered += ".dirty"
     return rendered
 
 
+def pep440_split_post(ver):
+    """Split pep440 version string at the post-release segment.
+
+    Returns the release segments before the post-release and the
+    post-release version number (or -1 if no post-release segment is present).
+    """
+    vc = str.split(ver, ".post")
+    return vc[0], int(vc[1] or 0) if len(vc) == 2 else None
+
+
 def render_pep440_pre(pieces):
-    """TAG[.post0.devDISTANCE] -- No -dirty.
+    """TAG[.postN.devDISTANCE] -- No -dirty.
 
     Exceptions:
     1: no tags. 0.post0.devDISTANCE
     """
     if pieces["closest-tag"]:
-        rendered = pieces["closest-tag"]
         if pieces["distance"]:
-            rendered += ".post0.dev%d" % pieces["distance"]
+            # update the post release segment
+            tag_version, post_version = pep440_split_post(pieces["closest-tag"])
+            rendered = tag_version
+            if post_version is not None:
+                rendered += ".post%d.dev%d" % (post_version+1, pieces["distance"])
+            else:
+                rendered += ".post0.dev%d" % (pieces["distance"])
+        else:
+            # no commits, use the tag as the version
+            rendered = pieces["closest-tag"]
     else:
         # exception #1
         rendered = "0.post0.dev%d" % pieces["distance"]
@@ -1336,6 +1524,35 @@ def render_pep440_post(pieces):
     return rendered
 
 
+def render_pep440_post_branch(pieces):
+    """TAG[.postDISTANCE[.dev0]+gHEX[.dirty]] .
+
+    The ".dev0" means not master branch.
+
+    Exceptions:
+    1: no tags. 0.postDISTANCE[.dev0]+gHEX[.dirty]
+    """
+    if pieces["closest-tag"]:
+        rendered = pieces["closest-tag"]
+        if pieces["distance"] or pieces["dirty"]:
+            rendered += ".post%d" % pieces["distance"]
+            if pieces["branch"] != "master":
+                rendered += ".dev0"
+            rendered += plus_or_dot(pieces)
+            rendered += "g%s" % pieces["short"]
+            if pieces["dirty"]:
+                rendered += ".dirty"
+    else:
+        # exception #1
+        rendered = "0.post%d" % pieces["distance"]
+        if pieces["branch"] != "master":
+            rendered += ".dev0"
+        rendered += "+g%s" % pieces["short"]
+        if pieces["dirty"]:
+            rendered += ".dirty"
+    return rendered
+
+
 def render_pep440_old(pieces):
     """TAG[.postDISTANCE[.dev0]] .
 
@@ -1401,23 +1618,25 @@ def render_git_describe_long(pieces):
 def render(pieces, style):
     """Render the given version pieces into the requested style."""
     if pieces["error"]:
-        return {
-            "version": "unknown",
-            "full-revisionid": pieces.get("long"),
-            "dirty": None,
-            "error": pieces["error"],
-            "date": None,
-        }
+        return {"version": "unknown",
+                "full-revisionid": pieces.get("long"),
+                "dirty": None,
+                "error": pieces["error"],
+                "date": None}
 
     if not style or style == "default":
         style = "pep440"  # the default
 
     if style == "pep440":
         rendered = render_pep440(pieces)
+    elif style == "pep440-branch":
+        rendered = render_pep440_branch(pieces)
     elif style == "pep440-pre":
         rendered = render_pep440_pre(pieces)
     elif style == "pep440-post":
         rendered = render_pep440_post(pieces)
+    elif style == "pep440-post-branch":
+        rendered = render_pep440_post_branch(pieces)
     elif style == "pep440-old":
         rendered = render_pep440_old(pieces)
     elif style == "git-describe":
@@ -1427,13 +1646,9 @@ def render(pieces, style):
     else:
         raise ValueError("unknown style '%s'" % style)
 
-    return {
-        "version": rendered,
-        "full-revisionid": pieces["long"],
-        "dirty": pieces["dirty"],
-        "error": None,
-        "date": pieces.get("date"),
-    }
+    return {"version": rendered, "full-revisionid": pieces["long"],
+            "dirty": pieces["dirty"], "error": None,
+            "date": pieces.get("date")}
 
 
 class VersioneerBadRootError(Exception):
@@ -1456,9 +1671,8 @@ def get_versions(verbose=False):
     handlers = HANDLERS.get(cfg.VCS)
     assert handlers, "unrecognized VCS '%s'" % cfg.VCS
     verbose = verbose or cfg.verbose
-    assert (
-        cfg.versionfile_source is not None
-    ), "please set versioneer.versionfile_source"
+    assert cfg.versionfile_source is not None, \
+        "please set versioneer.versionfile_source"
     assert cfg.tag_prefix is not None, "please set versioneer.tag_prefix"
 
     versionfile_abs = os.path.join(root, cfg.versionfile_source)
@@ -1512,13 +1726,9 @@ def get_versions(verbose=False):
     if verbose:
         print("unable to compute version")
 
-    return {
-        "version": "0+unknown",
-        "full-revisionid": None,
-        "dirty": None,
-        "error": "unable to compute version",
-        "date": None,
-    }
+    return {"version": "0+unknown", "full-revisionid": None,
+            "dirty": None, "error": "unable to compute version",
+            "date": None}
 
 
 def get_version():
@@ -1550,7 +1760,10 @@ def get_cmdclass(cmdclass=None):
     cmds = {} if cmdclass is None else cmdclass.copy()
 
     # we add "version" to both distutils and setuptools
-    from distutils.core import Command
+    try:
+        from setuptools import Command
+    except ImportError:
+        from distutils.core import Command
 
     class cmd_version(Command):
         description = "report generated version string"
@@ -1571,7 +1784,6 @@ def run(self):
             print(" date: %s" % vers.get("date"))
             if vers["error"]:
                 print(" error: %s" % vers["error"])
-
     cmds["version"] = cmd_version
 
     # we override "build_py" in both distutils and setuptools
@@ -1590,8 +1802,8 @@ def run(self):
     #  setup.py egg_info -> ?
 
     # we override different "build_py" commands for both environments
-    if "build_py" in cmds:
-        _build_py = cmds["build_py"]
+    if 'build_py' in cmds:
+        _build_py = cmds['build_py']
     elif "setuptools" in sys.modules:
         from setuptools.command.build_py import build_py as _build_py
     else:
@@ -1606,13 +1818,15 @@ def run(self):
             # now locate _version.py in the new build/ directory and replace
             # it with an updated value
             if cfg.versionfile_build:
-                target_versionfile = os.path.join(self.build_lib, cfg.versionfile_build)
+                target_versionfile = os.path.join(self.build_lib,
+                                                  cfg.versionfile_build)
                 print("UPDATING %s" % target_versionfile)
                 write_to_version_file(target_versionfile, versions)
-
     cmds["build_py"] = cmd_build_py
 
-    if "setuptools" in sys.modules:
+    if 'build_ext' in cmds:
+        _build_ext = cmds['build_ext']
+    elif "setuptools" in sys.modules:
         from setuptools.command.build_ext import build_ext as _build_ext
     else:
         from distutils.command.build_ext import build_ext as _build_ext
@@ -1631,15 +1845,14 @@ def run(self):
                 return
             # now locate _version.py in the new build/ directory and replace
             # it with an updated value
-            target_versionfile = os.path.join(self.build_lib, cfg.versionfile_source)
+            target_versionfile = os.path.join(self.build_lib,
+                                              cfg.versionfile_build)
             print("UPDATING %s" % target_versionfile)
             write_to_version_file(target_versionfile, versions)
-
     cmds["build_ext"] = cmd_build_ext
 
     if "cx_Freeze" in sys.modules:  # cx_freeze enabled?
         from cx_Freeze.dist import build_exe as _build_exe
-
         # nczeczulin reports that py2exe won't like the pep440-style string
         # as FILEVERSION, but it can be used for PRODUCTVERSION, e.g.
         # setup(console=[{
@@ -1660,21 +1873,17 @@ def run(self):
                 os.unlink(target_versionfile)
                 with open(cfg.versionfile_source, "w") as f:
                     LONG = LONG_VERSION_PY[cfg.VCS]
-                    f.write(
-                        LONG
-                        % {
-                            "DOLLAR": "$",
-                            "STYLE": cfg.style,
-                            "TAG_PREFIX": cfg.tag_prefix,
-                            "PARENTDIR_PREFIX": cfg.parentdir_prefix,
-                            "VERSIONFILE_SOURCE": cfg.versionfile_source,
-                        }
-                    )
-
+                    f.write(LONG %
+                            {"DOLLAR": "$",
+                             "STYLE": cfg.style,
+                             "TAG_PREFIX": cfg.tag_prefix,
+                             "PARENTDIR_PREFIX": cfg.parentdir_prefix,
+                             "VERSIONFILE_SOURCE": cfg.versionfile_source,
+                             })
         cmds["build_exe"] = cmd_build_exe
         del cmds["build_py"]
 
-    if "py2exe" in sys.modules:  # py2exe enabled?
+    if 'py2exe' in sys.modules:  # py2exe enabled?
         from py2exe.distutils_buildexe import py2exe as _py2exe
 
         class cmd_py2exe(_py2exe):
@@ -1690,22 +1899,18 @@ def run(self):
                 os.unlink(target_versionfile)
                 with open(cfg.versionfile_source, "w") as f:
                     LONG = LONG_VERSION_PY[cfg.VCS]
-                    f.write(
-                        LONG
-                        % {
-                            "DOLLAR": "$",
-                            "STYLE": cfg.style,
-                            "TAG_PREFIX": cfg.tag_prefix,
-                            "PARENTDIR_PREFIX": cfg.parentdir_prefix,
-                            "VERSIONFILE_SOURCE": cfg.versionfile_source,
-                        }
-                    )
-
+                    f.write(LONG %
+                            {"DOLLAR": "$",
+                             "STYLE": cfg.style,
+                             "TAG_PREFIX": cfg.tag_prefix,
+                             "PARENTDIR_PREFIX": cfg.parentdir_prefix,
+                             "VERSIONFILE_SOURCE": cfg.versionfile_source,
+                             })
         cmds["py2exe"] = cmd_py2exe
 
     # we override different "sdist" commands for both environments
-    if "sdist" in cmds:
-        _sdist = cmds["sdist"]
+    if 'sdist' in cmds:
+        _sdist = cmds['sdist']
     elif "setuptools" in sys.modules:
         from setuptools.command.sdist import sdist as _sdist
     else:
@@ -1729,10 +1934,8 @@ def make_release_tree(self, base_dir, files):
             # updated value
             target_versionfile = os.path.join(base_dir, cfg.versionfile_source)
             print("UPDATING %s" % target_versionfile)
-            write_to_version_file(
-                target_versionfile, self._versioneer_generated_versions
-            )
-
+            write_to_version_file(target_versionfile,
+                                  self._versioneer_generated_versions)
     cmds["sdist"] = cmd_sdist
 
     return cmds
@@ -1775,25 +1978,28 @@ def make_release_tree(self, base_dir, files):
 
 """
 
-INIT_PY_SNIPPET = """
+OLD_SNIPPET = """
 from ._version import get_versions
 __version__ = get_versions()['version']
 del get_versions
 """
 
+INIT_PY_SNIPPET = """
+from . import {0}
+__version__ = {0}.get_versions()['version']
+"""
+
 
 def do_setup():
     """Do main VCS-independent setup function for installing Versioneer."""
     root = get_root()
     try:
         cfg = get_config_from_root(root)
-    except (
-        EnvironmentError,
-        configparser.NoSectionError,
-        configparser.NoOptionError,
-    ) as e:
-        if isinstance(e, (EnvironmentError, configparser.NoSectionError)):
-            print("Adding sample versioneer config to setup.cfg", file=sys.stderr)
+    except (OSError, configparser.NoSectionError,
+            configparser.NoOptionError) as e:
+        if isinstance(e, (OSError, configparser.NoSectionError)):
+            print("Adding sample versioneer config to setup.cfg",
+                  file=sys.stderr)
             with open(os.path.join(root, "setup.cfg"), "a") as f:
                 f.write(SAMPLE_CONFIG)
         print(CONFIG_ERROR, file=sys.stderr)
@@ -1802,28 +2008,31 @@ def do_setup():
     print(" creating %s" % cfg.versionfile_source)
     with open(cfg.versionfile_source, "w") as f:
         LONG = LONG_VERSION_PY[cfg.VCS]
-        f.write(
-            LONG
-            % {
-                "DOLLAR": "$",
-                "STYLE": cfg.style,
-                "TAG_PREFIX": cfg.tag_prefix,
-                "PARENTDIR_PREFIX": cfg.parentdir_prefix,
-                "VERSIONFILE_SOURCE": cfg.versionfile_source,
-            }
-        )
-
-    ipy = os.path.join(os.path.dirname(cfg.versionfile_source), "__init__.py")
+        f.write(LONG % {"DOLLAR": "$",
+                        "STYLE": cfg.style,
+                        "TAG_PREFIX": cfg.tag_prefix,
+                        "PARENTDIR_PREFIX": cfg.parentdir_prefix,
+                        "VERSIONFILE_SOURCE": cfg.versionfile_source,
+                        })
+
+    ipy = os.path.join(os.path.dirname(cfg.versionfile_source),
+                       "__init__.py")
     if os.path.exists(ipy):
         try:
             with open(ipy, "r") as f:
                 old = f.read()
-        except EnvironmentError:
+        except OSError:
             old = ""
-        if INIT_PY_SNIPPET not in old:
+        module = os.path.splitext(os.path.basename(cfg.versionfile_source))[0]
+        snippet = INIT_PY_SNIPPET.format(module)
+        if OLD_SNIPPET in old:
+            print(" replacing boilerplate in %s" % ipy)
+            with open(ipy, "w") as f:
+                f.write(old.replace(OLD_SNIPPET, snippet))
+        elif snippet not in old:
             print(" appending to %s" % ipy)
             with open(ipy, "a") as f:
-                f.write(INIT_PY_SNIPPET)
+                f.write(snippet)
         else:
             print(" %s unmodified" % ipy)
     else:
@@ -1842,7 +2051,7 @@ def do_setup():
                 if line.startswith("include "):
                     for include in line.split()[1:]:
                         simple_includes.add(include)
-    except EnvironmentError:
+    except OSError:
         pass
     # That doesn't cover everything MANIFEST.in can do
     # (http://docs.python.org/2/distutils/sourcedist.html#commands), so
@@ -1855,10 +2064,8 @@ def do_setup():
     else:
         print(" 'versioneer.py' already in MANIFEST.in")
     if cfg.versionfile_source not in simple_includes:
-        print(
-            " appending versionfile_source ('%s') to MANIFEST.in"
-            % cfg.versionfile_source
-        )
+        print(" appending versionfile_source ('%s') to MANIFEST.in" %
+              cfg.versionfile_source)
         with open(manifest_in, "a") as f:
             f.write("include %s\n" % cfg.versionfile_source)
     else:


Test patch:
diff --git a/bottleneck/_pytesttester.py b/bottleneck/_pytesttester.py
index 1d0e867ace..b83103df58 100644
--- a/bottleneck/_pytesttester.py
+++ b/bottleneck/_pytesttester.py
@@ -4,9 +4,11 @@
 Based on scipy._libs._testutils
 """
 
+from __future__ import division, print_function, absolute_import
+
 import os
 import sys
-from typing import List, Optional
+
 
 __all__ = ["PytestTester"]
 
@@ -16,19 +18,19 @@ class PytestTester(object):
     Pytest test runner entry point.
     """
 
-    def __init__(self, module_name: str) -> None:
+    def __init__(self, module_name):
         self.module_name = module_name
 
     def __call__(
         self,
-        label: str = "fast",
-        verbose: int = 1,
-        extra_argv: Optional[List[str]] = None,
-        doctests: bool = False,
-        coverage: bool = False,
-        tests: Optional[List[str]] = None,
-        parallel: Optional[int] = None,
-    ) -> bool:
+        label="fast",
+        verbose=1,
+        extra_argv=None,
+        doctests=False,
+        coverage=False,
+        tests=None,
+        parallel=None,
+    ):
         import pytest
 
         module = sys.modules[self.module_name]
@@ -69,12 +71,6 @@ def __call__(
 
         pytest_args += ["--pyargs"] + list(tests)
 
-        if not _have_test_extras():
-            warnings.warn(
-                "The pytest and/or hypothesis packages are not installed, install with "
-                "pip install bottleneck[test]"
-            )
-
         try:
             code = pytest.main(pytest_args)
         except SystemExit as exc:
@@ -83,7 +79,7 @@ def __call__(
         return code == 0
 
 
-def _pytest_has_xdist() -> bool:
+def _pytest_has_xdist():
     """
     Check if the pytest-xdist plugin is installed, providing parallel tests
     """
@@ -91,12 +87,3 @@ def _pytest_has_xdist() -> bool:
     from importlib.util import find_spec
 
     return find_spec("xdist") is not None
-
-
-def _have_test_extras() -> bool:
-    """
-    Check if the test extra is installed
-    """
-    from importlib.util import find_spec
-
-    return all(find_spec(x) is not None for x in ["pytest", "hypothesis"])
diff --git a/bottleneck/tests/input_modification_test.py b/bottleneck/tests/input_modification_test.py
index e90ccc64c4..1546d81828 100644
--- a/bottleneck/tests/input_modification_test.py
+++ b/bottleneck/tests/input_modification_test.py
@@ -1,51 +1,71 @@
 """Test functions."""
 
 import warnings
-from typing import List, Optional
 
-import hypothesis
 import numpy as np
-import pytest
 from numpy.testing import assert_equal
+import bottleneck as bn
+from .util import DTYPES
+import pytest
+
 
-from .common import hy_array_gen
-from .util import get_functions
+def arrays(dtypes):
+    """Iterator that yield arrays to use for unit testing."""
+    ss = {}
+    ss[1] = {"size": 4, "shapes": [(4,)]}
+    ss[2] = {"size": 6, "shapes": [(2, 3)]}
+    ss[3] = {"size": 6, "shapes": [(1, 2, 3)]}
+    rs = np.random.RandomState([1, 2, 3])
+    for ndim in ss:
+        size = ss[ndim]["size"]
+        shapes = ss[ndim]["shapes"]
+        for dtype in dtypes:
+            a = np.arange(size, dtype=dtype)
+            if issubclass(a.dtype.type, np.inexact):
+                idx = rs.rand(*a.shape) < 0.2
+                a[idx] = np.inf
+                idx = rs.rand(*a.shape) < 0.2
+                a[idx] = np.nan
+                idx = rs.rand(*a.shape) < 0.2
+                a[idx] *= -1
+            for shape in shapes:
+                a = a.reshape(shape)
+                yield a
 
 
-@pytest.mark.parametrize("func", get_functions("all"), ids=lambda x: x.__name__)
-@hypothesis.settings(deadline=1000)
-@hypothesis.given(array=hy_array_gen)
-def test_modification(func, array):
+@pytest.mark.parametrize("func", bn.get_functions("all"), ids=lambda x: x.__name__)
+def test_modification(func):
     """Test that bn.xxx gives the same output as np.xxx."""
     name = func.__name__
     if name == "replace":
         return
     msg = "\nInput array modified by %s.\n\n"
     msg += "input array before:\n%s\nafter:\n%s\n"
-    axes: List[Optional[int]] = list(range(-array.ndim, array.ndim))
-    if all(x not in name for x in ["push", "move", "sort", "partition"]):
-        axes += [None]
-
-    second_arg = 1
-    if "partition" in name:
-        second_arg = 0
-
-    for axis in axes:
-        with np.errstate(invalid="ignore"):
-            a1 = array.copy()
-            a2 = array.copy()
-            if any(x in name for x in ["move", "sort", "partition"]):
-                with warnings.catch_warnings():
-                    warnings.simplefilter("ignore")
-                    func(a1, second_arg, axis=axis)
-            else:
-                try:
+    for i, a in enumerate(arrays(DTYPES)):
+        axes = list(range(-a.ndim, a.ndim))
+        if all(x not in name for x in ["push", "move", "sort", "partition"]):
+            axes += [None]
+
+        second_arg = 1
+        if "partition" in name:
+            second_arg = 0
+
+        for axis in axes:
+            with np.errstate(invalid="ignore"):
+                a1 = a.copy()
+                a2 = a.copy()
+                if any(x in name for x in ["move", "sort", "partition"]):
                     with warnings.catch_warnings():
                         warnings.simplefilter("ignore")
-                        func(a1, axis=axis)
-                except ValueError as e:
-                    if name.startswith("nanarg") and "All-NaN slice encountered" in str(
-                        e
-                    ):
-                        continue
-            assert_equal(a1, a2, msg % (name, a1, a2))
+                        func(a1, second_arg, axis=axis)
+                else:
+                    try:
+                        with warnings.catch_warnings():
+                            warnings.simplefilter("ignore")
+                            func(a1, axis=axis)
+                    except ValueError as e:
+                        if name.startswith(
+                            "nanarg"
+                        ) and "All-NaN slice encountered" in str(e):
+                            continue
+                assert_equal(a1, a2, msg % (name, a1, a2))
diff --git a/bottleneck/tests/list_input_test.py b/bottleneck/tests/list_input_test.py
index 0dd943ae91..4306a23990 100644
--- a/bottleneck/tests/list_input_test.py
+++ b/bottleneck/tests/list_input_test.py
@@ -3,11 +3,10 @@
 import warnings
 
 import numpy as np
-import pytest
 from numpy.testing import assert_array_almost_equal
-
 import bottleneck as bn
 from .util import DTYPES
+import pytest
 
 
 def lists(dtypes=DTYPES):
diff --git a/bottleneck/tests/memory_test.py b/bottleneck/tests/memory_test.py
index b789d5f868..577db31f6a 100644
--- a/bottleneck/tests/memory_test.py
+++ b/bottleneck/tests/memory_test.py
@@ -1,45 +1,35 @@
-import sys
-
 import numpy as np
-import pytest
-
+import sys
 import bottleneck as bn
+import pytest
 
 
 @pytest.mark.skipif(
     sys.platform.startswith("win"), reason="resource module not available on windows"
 )
-def test_memory_leak() -> None:
+def test_memory_leak():
     import resource
 
     arr = np.arange(1).reshape((1, 1))
 
-    n_attempts = 3
-    results = []
-
-    for _ in range(n_attempts):
-        starting = resource.getrusage(resource.RUSAGE_SELF).ru_maxrss
-
-        for _ in range(1000):
-            for axis in [None, 0, 1]:
-                bn.nansum(arr, axis=axis)
-                bn.nanargmax(arr, axis=axis)
-                bn.nanargmin(arr, axis=axis)
-                bn.nanmedian(arr, axis=axis)
-                bn.nansum(arr, axis=axis)
-                bn.nanmean(arr, axis=axis)
-                bn.nanmin(arr, axis=axis)
-                bn.nanmax(arr, axis=axis)
-                bn.nanvar(arr, axis=axis)
-
-        ending = resource.getrusage(resource.RUSAGE_SELF).ru_maxrss
-
-        diff = ending - starting
-        diff_bytes = diff * resource.getpagesize()
-        # For 1.3.0 release, this had value of ~100kB
-        if diff_bytes:
-            results.append(diff_bytes)
-        else:
-            break
-
-    assert len(results) < n_attempts
+    starting = resource.getrusage(resource.RUSAGE_SELF).ru_maxrss
+
+    for i in range(1000):
+        for axis in [None, 0, 1]:
+            bn.nansum(arr, axis=axis)
+            bn.nanargmax(arr, axis=axis)
+            bn.nanargmin(arr, axis=axis)
+            bn.nanmedian(arr, axis=axis)
+            bn.nansum(arr, axis=axis)
+            bn.nanmean(arr, axis=axis)
+            bn.nanmin(arr, axis=axis)
+            bn.nanmax(arr, axis=axis)
+            bn.nanvar(arr, axis=axis)
+
+    ending = resource.getrusage(resource.RUSAGE_SELF).ru_maxrss
+
+    diff = ending - starting
+    diff_bytes = diff * resource.getpagesize()
+    print(diff_bytes)
+    # For 1.3.0 release, this had value of ~100kB
+    assert diff_bytes == 0
diff --git a/bottleneck/tests/move_test.py b/bottleneck/tests/move_test.py
index 2b8362539e..26fe36f843 100644
--- a/bottleneck/tests/move_test.py
+++ b/bottleneck/tests/move_test.py
@@ -1,11 +1,10 @@
 """Test moving window functions."""
 
 import numpy as np
-import pytest
-from numpy.testing import assert_array_almost_equal, assert_equal, assert_raises
-
+from numpy.testing import assert_equal, assert_array_almost_equal, assert_raises
 import bottleneck as bn
-from .util import array_order, arrays
+from .util import arrays, array_order
+import pytest
 
 
 @pytest.mark.parametrize("func", bn.get_functions("move"), ids=lambda x: x.__name__)
@@ -56,7 +55,7 @@ def test_move(func):
 
 
 @pytest.mark.parametrize("func", bn.get_functions("move"), ids=lambda x: x.__name__)
-def test_arg_parsing(func, decimal=5) -> None:
+def test_arg_parsing(func, decimal=5):
     """test argument parsing."""
 
     name = func.__name__
@@ -121,7 +120,7 @@ def test_arg_parsing(func, decimal=5) -> None:
 
 
 @pytest.mark.parametrize("func", bn.get_functions("move"), ids=lambda x: x.__name__)
-def test_arg_parse_raises(func) -> None:
+def test_arg_parse_raises(func):
     """test argument parsing raises in move"""
     a = np.array([1.0, 2, 3])
     assert_raises(TypeError, func)
@@ -144,7 +143,7 @@ def test_arg_parse_raises(func) -> None:
 # LONG time to run.
 
 
-def test_move_median_with_nans() -> None:
+def test_move_median_with_nans():
     """test move_median.c with nans"""
     fmt = "\nfunc %s | window %d | min_count %s\n\nInput array:\n%s\n"
     aaae = assert_array_almost_equal
@@ -167,7 +166,7 @@ def test_move_median_with_nans() -> None:
             aaae(actual, desired, decimal=5, err_msg=err_msg)
 
 
-def test_move_median_without_nans() -> None:
+def test_move_median_without_nans():
     """test move_median.c without nans"""
     fmt = "\nfunc %s | window %d | min_count %s\n\nInput array:\n%s\n"
     aaae = assert_array_almost_equal
@@ -190,7 +189,7 @@ def test_move_median_without_nans() -> None:
 # Regression test for square roots of negative numbers
 
 
-def test_move_std_sqrt() -> None:
+def test_move_std_sqrt():
     """Test move_std for neg sqrt."""
 
     a = [
diff --git a/bottleneck/tests/nonreduce_axis_test.py b/bottleneck/tests/nonreduce_axis_test.py
index 831ce15b19..718100169d 100644
--- a/bottleneck/tests/nonreduce_axis_test.py
+++ b/bottleneck/tests/nonreduce_axis_test.py
@@ -1,16 +1,18 @@
 import numpy as np
-import pytest
 from numpy.testing import (
-    assert_array_almost_equal,
-    assert_array_equal,
     assert_equal,
+    assert_array_equal,
+    assert_array_almost_equal,
     assert_raises,
 )
 
 import bottleneck as bn
-from .reduce_test import unit_maker as reduce_unit_maker
-from .reduce_test import unit_maker_argparse as unit_maker_parse_rankdata
-from .util import DTYPES, array_order, arrays
+from .reduce_test import (
+    unit_maker as reduce_unit_maker,
+    unit_maker_argparse as unit_maker_parse_rankdata,
+)
+from .util import arrays, array_order, DTYPES
+import pytest
 
 # ---------------------------------------------------------------------------
 # partition, argpartition
@@ -19,7 +21,7 @@
 @pytest.mark.parametrize(
     "func", (bn.partition, bn.argpartition), ids=lambda x: x.__name__
 )
-def test_partition_and_argpartition(func) -> None:
+def test_partition_and_argpartition(func):
     """test partition or argpartition"""
 
     msg = "\nfunc %s | input %s (%s) | shape %s | n %d | axis %s | order %s\n"
@@ -127,7 +129,7 @@ def complete_the_argpartition(index, a, n, axis):
     return a
 
 
-def test_transpose() -> None:
+def test_transpose():
     """partition transpose test"""
     a = np.arange(12).reshape(4, 3)
     actual = bn.partition(a.T, 2, -1).T
@@ -147,7 +149,7 @@ def test_nonreduce_axis(func):
     return reduce_unit_maker(func)
 
 
-def test_push() -> None:
+def test_push():
     """Test push"""
     ns = (0, 1, 2, 3, 4, 5, None)
     a = np.array([np.nan, 1, 2, np.nan, np.nan, np.nan, np.nan, 3, np.nan])
@@ -176,16 +178,10 @@ def test_arg_parsing(func):
     else:
         fmt = "``%s` is an unknown nonreduce_axis function"
         raise ValueError(fmt % name)
-
-
-@pytest.mark.parametrize(
-    "func", bn.get_functions("nonreduce_axis"), ids=lambda x: x.__name__
-)
-def test_arg_raises(func):
     return unit_maker_raises(func)
 
 
-def unit_maker_parse(func, decimal=5) -> None:
+def unit_maker_parse(func, decimal=5):
     """test argument parsing."""
 
     name = func.__name__
@@ -232,7 +228,7 @@ def unit_maker_parse(func, decimal=5) -> None:
         func(*args, **kwargs)
 
 
-def unit_maker_raises(func) -> None:
+def unit_maker_raises(func):
     """test argument parsing raises in nonreduce_axis"""
     a = np.array([1.0, 2, 3])
     assert_raises(TypeError, func)
@@ -248,7 +244,7 @@ def unit_maker_raises(func) -> None:
 @pytest.mark.parametrize(
     "func", (bn.partition, bn.argpartition), ids=lambda x: x.__name__
 )
-def test_out_of_bounds_raises(func, dtype) -> None:
+def test_out_of_bounds_raises(func, dtype):
     array = np.ones((10, 10), dtype=dtype)
     for axis in [None, 0, 1, -1]:
         with pytest.raises(ValueError, match="must be between"):
diff --git a/bottleneck/tests/nonreduce_test.py b/bottleneck/tests/nonreduce_test.py
index b1081b2ff7..41599bfe44 100644
--- a/bottleneck/tests/nonreduce_test.py
+++ b/bottleneck/tests/nonreduce_test.py
@@ -3,17 +3,16 @@
 import warnings
 
 import numpy as np
-import pytest
-from numpy.testing import assert_array_equal, assert_equal, assert_raises
-
+from numpy.testing import assert_equal, assert_array_equal, assert_raises
 import bottleneck as bn
-from .util import DTYPES, INT_DTYPES, array_order, arrays
+from .util import arrays, array_order, DTYPES, INT_DTYPES
+import pytest
 
 
 @pytest.mark.parametrize(
     "func", bn.get_functions("nonreduce"), ids=lambda x: x.__name__
 )
-def test_nonreduce(func) -> None:
+def test_nonreduce(func):
     """Test that bn.xxx gives the same output as np.xxx."""
     msg = "\nfunc %s | input %s (%s) | shape %s | old %f | new %f | order %s\n"
     msg += "\nInput array:\n%s\n"
@@ -67,7 +66,7 @@ def test_nonreduce(func) -> None:
 # Check that exceptions are raised
 
 
-def test_replace_unsafe_cast() -> None:
+def test_replace_unsafe_cast():
     """Test replace for unsafe casts"""
     dtypes = INT_DTYPES
     for dtype in dtypes:
@@ -78,7 +77,7 @@ def test_replace_unsafe_cast() -> None:
         assert_raises(ValueError, bn.slow.replace, a.copy(), 0, 0.1)
 
 
-def test_non_array() -> None:
+def test_non_array():
     """Test that non-array input raises"""
     a = [1, 2, 3]
     assert_raises(TypeError, bn.replace, a, 0, 1)
@@ -92,7 +91,7 @@ def test_non_array() -> None:
 
 
 @pytest.mark.parametrize("dtype", INT_DTYPES)
-def test_replace_nan_int(dtype) -> None:
+def test_replace_nan_int(dtype):
     """Test replace, int array, old=nan, new=0"""
     a = np.arange(2 * 3 * 4, dtype=dtype).reshape(2, 3, 4)
     actual = a.copy()
@@ -106,7 +105,7 @@ def test_replace_nan_int(dtype) -> None:
     assert_array_equal(actual, desired, err_msg=msg)
 
 
-def test_replace_bad_args() -> None:
+def test_replace_bad_args():
     array = np.ones((10, 10))
     bad_vals = [None, "", [0], "0"]
     for bad_val in bad_vals:
@@ -133,17 +132,7 @@ def test_replace_bad_args() -> None:
 
 
 @pytest.mark.parametrize("dtype", DTYPES)
-def test_replace_newaxis(dtype) -> None:
+def test_replace_newaxis(dtype):
     array = np.ones((2, 2), dtype=dtype)[..., np.newaxis]
     result = bn.replace(array, 1, 2)
     assert (result == 2).all().all()
-
-
-@pytest.mark.parametrize("dtype", DTYPES)
-def test_replace_view(dtype) -> None:
-    array = np.arange(20, dtype=dtype)
-    view = array[::2]
-
-    bn.replace(view, 5, -1)
-    assert view.min() == 0
-    assert array.min() == 0
diff --git a/bottleneck/tests/reduce_test.py b/bottleneck/tests/reduce_test.py
index 0243029929..56723e1d9a 100644
--- a/bottleneck/tests/reduce_test.py
+++ b/bottleneck/tests/reduce_test.py
@@ -1,80 +1,14 @@
 """Test reduce functions."""
 
-import traceback
 import warnings
-from typing import Callable, List, Optional, Union
+import traceback
 
-import hypothesis
 import numpy as np
-import pytest
-from numpy.testing import assert_array_almost_equal, assert_equal, assert_raises
+from numpy.testing import assert_equal, assert_raises, assert_array_almost_equal
 
 import bottleneck as bn
-from .common import hy_array_gen, hy_int_array_gen
-from .util import DTYPES, array_order, arrays
-
-
-def _hypothesis_helper(
-    func: Callable[[np.ndarray], Union[int, float, np.ndarray]],
-    array: np.ndarray,
-    skip_all_nans: bool = False,
-) -> None:
-    slow_func = eval("bn.slow.%s" % func.__name__)
-    ndim = array.ndim
-    axes: List[Optional[int]] = list(range(-ndim, ndim)) + [None]
-    for order in ["C", "F"]:
-        if order == "F":
-            arr = np.asfortranarray(array)
-        else:
-            arr = np.ascontiguousarray(array)
-
-        for axis in axes:
-            if skip_all_nans:
-                if not np.isfinite(arr).all(axis=axis).all():
-                    # The documentation for numpy.nanargmin/max has the following
-                    # errata:
-                    #
-                    # Warning: the results cannot be trusted if a slice contains only
-                    # NaNs and Infs.
-                    #
-                    # So skip in that case, as it is definitely wrong
-                    continue
-            try:
-                bn_result = func(arr, axis=axis)
-            except ValueError:
-                try:
-                    slow_result = slow_func(arr, axis=axis)
-                    assert False
-                except ValueError:
-                    return
-
-            slow_result = slow_func(arr, axis=axis)
-
-            hypothesis.note(f"axis: {axis}")
-            assert_array_almost_equal(bn_result, slow_result)
-
-
-@pytest.mark.parametrize(
-    "func", (bn.nanmin, bn.nanmax, bn.anynan, bn.allnan), ids=lambda x: x.__name__
-)
-@hypothesis.given(array=hy_array_gen)
-@hypothesis.settings(max_examples=500)
-def test_reduce_hypothesis(func, array):
-    _hypothesis_helper(func, array)
-
-
-@pytest.mark.parametrize("func", (bn.ss, bn.nansum), ids=lambda x: x.__name__)
-@hypothesis.given(array=hy_int_array_gen)
-@hypothesis.settings(max_examples=500)
-def test_reduce_hypothesis_ints_only(func, array):
-    _hypothesis_helper(func, array)
-
-
-@pytest.mark.parametrize("func", (bn.nanargmin, bn.nanargmax), ids=lambda x: x.__name__)
-@hypothesis.given(array=hy_array_gen)
-@hypothesis.settings(max_examples=500)
-def test_reduce_hypothesis_errata(func, array):
-    _hypothesis_helper(func, array, skip_all_nans=True)
+from .util import arrays, array_order, DTYPES
+import pytest
 
 
 @pytest.mark.parametrize("func", bn.get_functions("reduce"), ids=lambda x: x.__name__)
@@ -223,7 +157,7 @@ def unit_maker_argparse_raises(func):
 # Check that exceptions are raised
 
 
-def test_nanmax_size_zero(dtypes=DTYPES) -> None:
+def test_nanmax_size_zero(dtypes=DTYPES):
     """Test nanmax for size zero input arrays."""
     shapes = [(0,), (2, 0), (1, 2, 0)]
     for shape in shapes:
@@ -233,7 +167,7 @@ def test_nanmax_size_zero(dtypes=DTYPES) -> None:
             assert_raises(ValueError, bn.slow.nanmax, a)
 
 
-def test_nanmin_size_zero(dtypes=DTYPES) -> None:
+def test_nanmin_size_zero(dtypes=DTYPES):
     """Test nanmin for size zero input arrays."""
     shapes = [(0,), (2, 0), (1, 2, 0)]
     for shape in shapes:
@@ -247,7 +181,7 @@ def test_nanmin_size_zero(dtypes=DTYPES) -> None:
 # nanstd and nanvar regression test (issue #60)
 
 
-def test_nanstd_issue60() -> None:
+def test_nanstd_issue60():
     """nanstd regression test (issue #60)"""
 
     f = bn.nanstd([1.0], ddof=1)
@@ -271,7 +205,7 @@ def test_nanstd_issue60() -> None:
     assert_equal(f, s, err_msg="issue #60 regression")
 
 
-def test_nanvar_issue60() -> None:
+def test_nanvar_issue60():
     """nanvar regression test (issue #60)"""
 
     f = bn.nanvar([1.0], ddof=1)
@@ -297,7 +231,7 @@ def test_nanvar_issue60() -> None:
 
 @pytest.mark.parametrize("dtype", DTYPES)
 @pytest.mark.parametrize("func", (bn.nanstd, bn.nanvar), ids=lambda x: x.__name__)
-def test_ddof_nans(func, dtype) -> None:
+def test_ddof_nans(func, dtype):
     array = np.ones((1, 1), dtype=dtype)
     for axis in [None, 0, 1, -1]:
         result = func(array, axis=axis, ddof=3)
diff --git a/bottleneck/tests/scalar_input_test.py b/bottleneck/tests/scalar_input_test.py
index cea311063f..3f73085591 100644
--- a/bottleneck/tests/scalar_input_test.py
+++ b/bottleneck/tests/scalar_input_test.py
@@ -1,53 +1,24 @@
 """Check that functions can handle scalar input"""
 
-from typing import Callable, Union
-
-import hypothesis
-import numpy as np
-import pytest
-from hypothesis.strategies import floats, integers, one_of
 from numpy.testing import assert_array_almost_equal
-
-import bottleneck as bn  # noqa: F401
-from .util import get_functions
-
-int64_iinfo = np.iinfo(np.int64)
-
-
-scalars = one_of(
-    [integers(min_value=int64_iinfo.min, max_value=int64_iinfo.max), floats()]
-)
+import bottleneck as bn
+import pytest
 
 
-@hypothesis.given(scalar=scalars)
 @pytest.mark.parametrize(
     "func",
-    get_functions("reduce") + get_functions("nonreduce_axis"),
+    bn.get_functions("reduce") + bn.get_functions("nonreduce_axis"),  # noqa: W504
     ids=lambda x: x.__name__,
 )
-def test_scalar_input(
-    func: Callable[[np.array], Union[int, float, np.array]], scalar: Union[int, float]
-) -> None:
+def test_scalar_input(func, args=tuple()):
     """Test that bn.xxx gives the same output as bn.slow.xxx for scalar input."""
     if func.__name__ in ("partition", "argpartition", "push"):
         return
     func0 = eval("bn.slow.%s" % func.__name__)
     msg = "\nfunc %s | input %s\n"
-    actual_raised = False
-    desired_raised = False
-
-    try:
-        actual = func(scalar)
-    except ValueError:
-        actual_raised = True
-
-    try:
-        desired = func0(scalar)
-    except ValueError:
-        desired_raised = True
-
-    if desired_raised or actual_raised:
-        assert actual_raised and desired_raised
-    else:
-        err_msg = msg % (func.__name__, scalar)
-        assert_array_almost_equal(actual, desired, err_msg=err_msg)
+    a = -9
+    argsi = [a] + list(args)
+    actual = func(*argsi)
+    desired = func0(*argsi)
+    err_msg = msg % (func.__name__, a)
+    assert_array_almost_equal(actual, desired, err_msg=err_msg)
diff --git a/bottleneck/tests/util.py b/bottleneck/tests/util.py
index acb06ba320..cd19d63042 100644
--- a/bottleneck/tests/util.py
+++ b/bottleneck/tests/util.py
@@ -1,7 +1,4 @@
-from typing import Callable, List, Union
-
 import numpy as np
-
 import bottleneck as bn
 
 INT_DTYPES = [np.int64, np.int32]
@@ -9,9 +6,7 @@
 DTYPES = tuple(FLOAT_DTYPES + INT_DTYPES)
 
 
-def get_functions(
-    module_name: str, as_string: bool = False
-) -> List[Union[str, Callable[[np.array], Union[int, float, np.array]]]]:
+def get_functions(module_name, as_string=False):
     """Returns a list of functions, optionally as string function names"""
     if module_name == "all":
         funcs = []
diff --git a/tools/test-installed-bottleneck.py b/tools/test-installed-bottleneck.py
index 7e07268424..449e4f91be 100644
--- a/tools/test-installed-bottleneck.py
+++ b/tools/test-installed-bottleneck.py
@@ -1,10 +1,8 @@
 #!/usr/bin/env python
 
 from __future__ import division
-
 import sys
 from optparse import OptionParser
-
 import bottleneck
 
 # This file is a modified version of the original numpy file: