diff -pruN 3.6.1-1/CHANGE_LOG 3.7.1-1/CHANGE_LOG
--- 3.6.1-1/CHANGE_LOG	2025-08-12 08:35:42.000000000 +0000
+++ 3.7.1-1/CHANGE_LOG	2025-08-28 20:59:12.000000000 +0000
@@ -1,3 +1,23 @@
+2025-08-28   3.7.1:
+-------------------
+  * fix type hinting for memoryviews, see #241
+  * add [bit-endianness](endianness.rst) documentation
+  * improve testing, including debug mode test for `digit_to_int()`
+
+
+2025-08-24   3.7.0:
+-------------------
+  * add `util.gen_primes()`, generate bitarrays in which active indices are
+    prime numbers
+  * improve `.buffer_info()` to return named tuple
+  * add optional `mode` argument to `util.sum_indices()` to sum square of
+    active indices
+  * improve internal `_sysinfo()` to include `Py_DEBUG`
+  * add [Dubner's conjecture](../examples/dubner.rst) (in memory of Harvey
+    Dubner)
+  * add [dynamically growing sieve](../examples/dyn_sieve.py)
+
+
 2025-08-12   3.6.1:
 -------------------
   * add development files for statistical tests in `devel/random/`
diff -pruN 3.6.1-1/README.rst 3.7.1-1/README.rst
--- 3.6.1-1/README.rst	2025-08-12 08:35:42.000000000 +0000
+++ 3.7.1-1/README.rst	2025-08-28 20:59:12.000000000 +0000
@@ -27,7 +27,7 @@ Key features
 * Immutable ``frozenbitarray`` objects which are hashable
 * Sequential search
 * Type hinting
-* Extensive test suite with over 500 unittests
+* Extensive test suite with about 600 unittests
 * Utility module ``bitarray.util``:
 
   * conversion to and from hexadecimal strings
@@ -57,7 +57,7 @@ Once you have installed the package, you
 
     $ python -c 'import bitarray; bitarray.test()'
     bitarray is installed in: /Users/ilan/bitarray/bitarray
-    bitarray version: 3.6.1
+    bitarray version: 3.7.1
     sys.version: 3.13.5 (main, Jun 16 2025) [Clang 18.1.8]
     sys.prefix: /Users/ilan/miniforge
     pointer size: 64 bit
@@ -66,12 +66,13 @@ Once you have installed the package, you
     HAVE_BUILTIN_BSWAP64: 1
     default bit-endianness: big
     machine byte-order: little
+    Py_DEBUG: 0
     DEBUG: 0
     .........................................................................
     .........................................................................
     ................................................................
     ----------------------------------------------------------------------
-    Ran 591 tests in 0.163s
+    Ran 597 tests in 0.165s
 
     OK
 
@@ -223,85 +224,15 @@ shift (``>>``) always shifts towards hig
 Bit-endianness
 --------------
 
-Unless explicitly converting to machine representation, i.e. initializing
-the buffer directly, using ``.tobytes()``, ``.frombytes()``, ``.tofile()``
-or ``.fromfile()``, as well as using ``memoryview()``, the bit-endianness
-will have no effect on any computation, and one can skip this section.
-
-Since bitarrays allows addressing individual bits, where the machine
-represents 8 bits in one byte, there are two obvious choices for this
-mapping: little-endian and big-endian.
-
-When dealing with the machine representation of bitarray objects, it is
-recommended to always explicitly specify the endianness.
-
-By default, bitarrays use big-endian representation:
-
-.. code-block:: python
-
-    >>> a = bitarray(b'A')
-    >>> a.endian
-    'big'
-    >>> a
-    bitarray('01000001')
-    >>> a[6] = 1
-    >>> a.tobytes()
-    b'C'
-
-Big-endian means that the most-significant bit comes first.
-Here, ``a[0]`` is the lowest address (index) and most significant bit,
-and ``a[7]`` is the highest address and least significant bit.
-
-When creating a new bitarray object, the endianness can always be
-specified explicitly:
-
-.. code-block:: python
-
-    >>> a = bitarray(b'A', endian='little')
-    >>> a
-    bitarray('10000010')
-    >>> a.endian
-    'little'
-
-Here, the low-bit comes first because little-endian means that increasing
-numeric significance corresponds to an increasing address.
-So ``a[0]`` is the lowest address and least significant bit,
-and ``a[7]`` is the highest address and most significant bit.
-
-The bit-endianness is a property of the bitarray object.
-The endianness cannot be changed once a bitarray object has been created.
-When comparing bitarray objects, the endianness (and hence the machine
-representation) is irrelevant; what matters is the mapping from indices
-to bits:
-
-.. code-block:: python
-
-    >>> bitarray('11001', endian='big') == bitarray('11001', endian='little')
-    True
-    >>> a = bitarray(b'\x01', endian='little')
-    >>> b = bitarray(b'\x80', endian='big')
-    >>> a == b
-    True
-    >>> a.tobytes() == b.tobytes()
-    False
-
-Bitwise operations (``|``, ``^``, ``&=``, ``|=``, ``^=``, ``~``) are
-implemented efficiently using the corresponding byte operations in C, i.e. the
-operators act on the machine representation of the bitarray objects.
-Therefore, it is not possible to perform bitwise operators on bitarrays
-with different endianness.
-
-As mentioned above, the endianness can not be changed once an object is
-created.  However, you can create a new bitarray with different endianness:
-
-.. code-block:: python
-
-    >>> a = bitarray('111000', endian='little')
-    >>> b = bitarray(a, endian='big')
-    >>> b
-    bitarray('111000')
-    >>> a == b
-    True
+For many purposes the bit-endianness is not of any relevance to the end user
+and can be regarded as an implementation detail of bitarray objects.
+However, there are use cases when the bit-endianness becomes important.
+These use cases involve explicitly reading and writing the bitarray buffer
+using ``.tobytes()``, ``.frombytes()``, ``.tofile()`` or ``.fromfile()``,
+importing and exporting buffers.  Also, a number of utility functions
+in ``bitarray.util`` will return different results depending on
+bit-endianness, such as ``ba2hex()`` or ``ba2int``.
+To better understand this topic, please read `bit-endianness <https://github.com/ilanschnell/bitarray/blob/master/doc/endianness.rst>`__.
 
 
 Buffer protocol
@@ -388,7 +319,7 @@ and can therefore be used as a dictionar
 Reference
 =========
 
-bitarray version: 3.6.1 -- `change log <https://github.com/ilanschnell/bitarray/blob/master/doc/changelog.rst>`__
+bitarray version: 3.7.1 -- `change log <https://github.com/ilanschnell/bitarray/blob/master/doc/changelog.rst>`__
 
 In the following, ``item`` and ``value`` are usually a single bit -
 an integer 0 or 1.
@@ -427,34 +358,36 @@ bitarray methods:
 -----------------
 
 ``all()`` -> bool
-   Return True when all bits in bitarray are True.
-   Note that ``a.all()`` is faster than ``all(a)``.
+   Return ``True`` when all bits in bitarray are 1.
+   ``a.all()`` is a faster version of ``all(a)``.
 
 
 ``any()`` -> bool
-   Return True when any bit in bitarray is True.
-   Note that ``a.any()`` is faster than ``any(a)``.
+   Return ``True`` when any bit in bitarray is 1.
+   ``a.any()`` is a faster version of ``any(a)``.
 
 
 ``append(item, /)``
    Append ``item`` to the end of the bitarray.
 
 
-``buffer_info()`` -> tuple
-   Return a tuple containing:
+``buffer_info()`` -> BufferInfo
+   Return named tuple with following fields:
 
-   0. memory address of buffer
-   1. buffer size (in bytes)
-   2. bit-endianness as a Unicode string
-   3. number of pad bits
-   4. allocated memory for the buffer (in bytes)
-   5. memory is read-only
-   6. buffer is imported
-   7. number of buffer exports
+   0. ``address``: memory address of buffer
+   1. ``nbytes``: buffer size (in bytes)
+   2. ``endian``: bit-endianness as a string
+   3. ``padbits``: number of pad bits
+   4. ``alloc``: allocated memory for buffer (in bytes)
+   5. ``readonly``: memory is read-only (bool)
+   6. ``imported``: buffer is imported (bool)
+   7. ``exports``: number of buffer exports
+
+   New in version 3.7: return named tuple
 
 
 ``bytereverse(start=0, stop=<end of buffer>, /)``
-   For each byte in byte-range(start, stop) reverse bits in-place.
+   For each byte in byte-range(``start``, ``stop``) reverse bits in-place.
    The start and stop indices are given in terms of bytes (not bits).
    Also note that this method only changes the buffer; it does not change the
    bit-endianness of the bitarray object.  Pad bits are left unchanged such
@@ -464,13 +397,13 @@ bitarray methods:
 
 
 ``clear()``
-   Remove all items from the bitarray.
+   Remove all items from bitarray.
 
    New in version 1.4
 
 
 ``copy()`` -> bitarray
-   Return a copy of the bitarray.
+   Return copy of bitarray (with same bit-endianness).
 
 
 ``count(value=1, start=0, stop=<end>, step=1, /)`` -> int
@@ -506,7 +439,7 @@ bitarray methods:
 
 ``extend(iterable, /)``
    Append items from to the end of the bitarray.
-   If ``iterable`` is a Unicode string, each ``0`` and ``1`` are appended as
+   If ``iterable`` is a (Unicode) string, each ``0`` and ``1`` are appended as
    bits (ignoring whitespace and underscore).
 
    New in version 3.4: allow ``bytes`` object
@@ -546,7 +479,7 @@ bitarray methods:
 ``index(sub_bitarray, start=0, stop=<end>, /, right=False)`` -> int
    Return lowest (or rightmost when ``right=True``) index where sub_bitarray
    is found, such that sub_bitarray is contained within ``[start:stop]``.
-   Raises ``ValueError`` when the sub_bitarray is not present.
+   Raises ``ValueError`` when sub_bitarray is not present.
 
    New in version 2.9: add optional keyword argument ``right``
 
@@ -557,7 +490,7 @@ bitarray methods:
 
 ``invert(index=<all bits>, /)``
    Invert all bits in bitarray (in-place).
-   When the optional ``index`` is given, only invert the single bit at index.
+   When the optional ``index`` is given, only invert the single bit at ``index``.
 
    New in version 1.5.3: optional index argument
 
@@ -612,7 +545,7 @@ bitarray methods:
 
 
 ``to01(group=0, sep=' ')`` -> str
-   Return bitarray as Unicode string of '0's and '1's.
+   Return bitarray as (Unicode) string of ``0``s and ``1``s.
    The bits are grouped into ``group`` bits (default is no grouping).
    When grouped, the string ``sep`` is inserted between groups
    of ``group`` characters, default is a space.
@@ -621,11 +554,11 @@ bitarray methods:
 
 
 ``tobytes()`` -> bytes
-   Return the bitarray buffer in bytes (pad bits are set to zero).
+   Return the bitarray buffer (pad bits are set to zero).
 
 
 ``tofile(f, /)``
-   Write byte representation of bitarray to file object f.
+   Write bitarray buffer to file object ``f``.
 
 
 ``tolist()`` -> list
@@ -699,7 +632,7 @@ Functions defined in the `bitarray` modu
 
 
 ``test(verbosity=1)`` -> TextTestResult
-   Run self-test, and return unittest.runner.TextTestResult object.
+   Run self-test, and return ``unittest.runner.TextTestResult`` object.
 
 
 Functions defined in `bitarray.util` module:
@@ -757,7 +690,7 @@ This sub-module was added in version 1.2
    New in version 3.3: ignore whitespace
 
 
-``byteswap(a, /, n=<buffer size>)``
+``byteswap(a, n=<buffer size>, /)``
    Reverse every ``n`` consecutive bytes of ``a`` in-place.
    By default, all bytes are reversed.  Note that ``n`` is not limited to 2, 4
    or 8, but can be any positive integer.
@@ -833,6 +766,19 @@ This sub-module was added in version 1.2
    New in version 2.5.0: allow bytes-like argument
 
 
+``gen_primes(n, /, endian=None, odd=False)`` -> bitarray
+   Generate a bitarray of length ``n`` in which active indices are prime numbers.
+   By default (``odd=False``), active indices correspond to prime numbers directly.
+   When ``odd=True``, only odd prime numbers are represented in the resulting
+   bitarray ``a``, and ``a[i]`` corresponds to ``2*i+1`` being prime or not.
+
+   Apart from working with prime numbers, this function is useful for
+   testing, as it provides a simple way to create a well-defined bitarray
+   of any length.
+
+   New in version 3.7
+
+
 ``hex2ba(hexstr, /, endian=None)`` -> bitarray
    Bitarray of hexadecimal representation.  hexstr may contain any number
    (including odd numbers) of hex digits (upper or lower case).
@@ -880,11 +826,9 @@ This sub-module was added in version 1.2
 
 
 ``pprint(bitarray, /, stream=None, group=8, indent=4, width=80)``
-   Prints the formatted representation of object on ``stream`` (which defaults
-   to ``sys.stdout``).  By default, elements are grouped in bytes (8 elements),
-   and 8 bytes (64 elements) per line.
-   Non-bitarray objects are printed by the standard library
-   function ``pprint.pprint()``.
+   Pretty-print bitarray object to ``stream``, defaults is ``sys.stdout``.
+   By default, bits are grouped in bytes (8 bits), and 64 bits per line.
+   Non-bitarray objects are printed using ``pprint.pprint()``.
 
    New in version 1.8
 
@@ -919,7 +863,7 @@ This sub-module was added in version 1.2
    New in version 3.5
 
 
-``sc_decode(stream)`` -> bitarray
+``sc_decode(stream, /)`` -> bitarray
    Decompress binary stream (an integer iterator, or bytes-like object) of a
    sparse compressed (``sc``) bitarray, and return the decoded  bitarray.
    This function consumes only one bitarray and leaves the remaining stream
@@ -962,12 +906,15 @@ This sub-module was added in version 1.2
    iteration is stopped as soon as one mismatch is found.
 
 
-``sum_indices(a, /)`` -> int
+``sum_indices(a, /, mode=1)`` -> int
    Return sum of indices of all active bits in bitarray ``a``.
    Equivalent to ``sum(i for i, v in enumerate(a) if v)``.
+   ``mode=2`` sums square of indices.
 
    New in version 3.6
 
+   New in version 3.7: add optional mode argument
+
 
 ``urandom(n, /, endian=None)`` -> bitarray
    Return random bitarray of length ``n`` (uses ``os.urandom()``).
diff -pruN 3.6.1-1/bitarray/__init__.py 3.7.1-1/bitarray/__init__.py
--- 3.6.1-1/bitarray/__init__.py	2025-08-12 08:35:42.000000000 +0000
+++ 3.7.1-1/bitarray/__init__.py	2025-08-28 20:59:12.000000000 +0000
@@ -10,6 +10,7 @@ Please find a description of this packag
 Author: Ilan Schnell
 """
 from __future__ import absolute_import
+from collections import namedtuple
 
 from bitarray._bitarray import (bitarray, decodetree, _sysinfo,
                                 bits2bytes, _bitarray_reconstructor,
@@ -19,6 +20,9 @@ from bitarray._bitarray import (bitarray
 
 __all__ = ['bitarray', 'frozenbitarray', 'decodetree', 'bits2bytes']
 
+BufferInfo = namedtuple('BufferInfo',
+                        ['address', 'nbytes', 'endian', 'padbits',
+                         'alloc', 'readonly', 'imported', 'exports'])
 
 class frozenbitarray(bitarray):
     """frozenbitarray(initializer=0, /, endian='big', buffer=None) -> \
@@ -57,7 +61,7 @@ and may therefore be used as a dictionar
 def test(verbosity=1):
     """test(verbosity=1) -> TextTestResult
 
-Run self-test, and return unittest.runner.TextTestResult object.
+Run self-test, and return `unittest.runner.TextTestResult` object.
 """
     from bitarray import test_bitarray
     return test_bitarray.run(verbosity=verbosity)
diff -pruN 3.6.1-1/bitarray/__init__.pyi 3.7.1-1/bitarray/__init__.pyi
--- 3.6.1-1/bitarray/__init__.pyi	2025-08-12 08:35:42.000000000 +0000
+++ 3.7.1-1/bitarray/__init__.pyi	2025-08-28 20:59:12.000000000 +0000
@@ -5,7 +5,7 @@
 from collections.abc import Iterable, Iterator, Sequence
 from unittest.runner import TextTestResult
 
-from typing import Any, BinaryIO, Dict, Union, overload
+from typing import Any, BinaryIO, Dict, Union, overload, NamedTuple
 
 
 CodeDict = Dict[Any, bitarray]
@@ -13,6 +13,17 @@ CodeDict = Dict[Any, bitarray]
 BytesLike = Union[bytes, bytearray]
 
 
+class BufferInfo(NamedTuple):
+    address: int
+    nbytes: int
+    endian: str
+    padbits: int
+    alloc: int
+    readonly: bool
+    imported: bool
+    exports: int
+
+
 class decodetree:
     def __init__(self, code: CodeDict) -> None: ...
     def complete(self) -> bool: ...
@@ -29,7 +40,7 @@ class bitarray:
     def all(self) -> bool: ...
     def any(self) -> bool: ...
     def append(self, value: int) -> None: ...
-    def buffer_info(self) -> tuple: ...
+    def buffer_info(self) -> BufferInfo: ...
     def bytereverse(self,
                     start: int = ...,
                     stop: int = ...) -> None: ...
@@ -106,6 +117,9 @@ class bitarray:
     def __delitem__(self,
                     i: Union[int, slice, bitarray, Sequence]) -> None: ...
 
+    def __buffer__(self, flags: int, /) -> memoryview: ...
+    def __release_buffer__(self, buffer: memoryview, /) -> None: ...
+
     def __add__(self, other: bitarray) -> bitarray: ...
     def __iadd__(self, other: bitarray) -> bitarray: ...
     def __mul__(self, n: int) -> bitarray: ...
@@ -149,7 +163,7 @@ def bits2bytes(n: int) -> int: ...
 def get_default_endian() -> str: ...
 def test(verbosity: int = ...) -> TextTestResult: ...
 def _set_default_endian(endian: str) -> None: ...
-def _sysinfo() -> tuple: ...
+def _sysinfo(key: str) -> int: ...
 def _bitarray_reconstructor(cls: type,
                             buffer: bytes,
                             endian: str,
diff -pruN 3.6.1-1/bitarray/_bitarray.c 3.7.1-1/bitarray/_bitarray.c
--- 3.6.1-1/bitarray/_bitarray.c	2025-08-12 08:35:42.000000000 +0000
+++ 3.7.1-1/bitarray/_bitarray.c	2025-08-28 20:59:12.000000000 +0000
@@ -942,8 +942,8 @@ bitarray_all(bitarrayobject *self)
 PyDoc_STRVAR(all_doc,
 "all() -> bool\n\
 \n\
-Return True when all bits in bitarray are True.\n\
-Note that `a.all()` is faster than `all(a)`.");
+Return `True` when all bits in bitarray are 1.\n\
+`a.all()` is a faster version of `all(a)`.");
 
 
 static PyObject *
@@ -955,8 +955,8 @@ bitarray_any(bitarrayobject *self)
 PyDoc_STRVAR(any_doc,
 "any() -> bool\n\
 \n\
-Return True when any bit in bitarray is True.\n\
-Note that `a.any()` is faster than `any(a)`.");
+Return `True` when any bit in bitarray is 1.\n\
+`a.any()` is a faster version of `any(a)`.");
 
 
 static PyObject *
@@ -1008,7 +1008,7 @@ bitarray_bytereverse(bitarrayobject *sel
 PyDoc_STRVAR(bytereverse_doc,
 "bytereverse(start=0, stop=<end of buffer>, /)\n\
 \n\
-For each byte in byte-range(start, stop) reverse bits in-place.\n\
+For each byte in byte-range(`start`, `stop`) reverse bits in-place.\n\
 The start and stop indices are given in terms of bytes (not bits).\n\
 Also note that this method only changes the buffer; it does not change the\n\
 bit-endianness of the bitarray object.  Pad bits are left unchanged such\n\
@@ -1018,38 +1018,51 @@ that two consecutive calls will always l
 static PyObject *
 bitarray_buffer_info(bitarrayobject *self)
 {
-    PyObject *res, *ptr;
+    static PyObject *info = NULL;   /* BufferInfo object */
+    PyObject *res, *args, *address, *readonly, *imported;
 
-    ptr = PyLong_FromVoidPtr((void *) self->ob_item);
-    if (ptr == NULL)
-        return NULL;
+    if (info == NULL) {
+        info = bitarray_module_attr("BufferInfo");
+        if (info == NULL)
+            return NULL;
+    }
 
-    res = Py_BuildValue("Onsnniii",
-                        ptr,
-                        Py_SIZE(self),
-                        ENDIAN_STR(self->endian),
-                        PADBITS(self),
-                        self->allocated,
-                        self->readonly,
-                        self->buffer ? 1 : 0,
-                        self->ob_exports);
-    Py_DECREF(ptr);
+    address = PyLong_FromVoidPtr((void *) self->ob_item);
+    readonly = PyBool_FromLong(self->readonly);
+    imported = PyBool_FromLong(self->buffer ? 1 : 0);
+    if (address == NULL || readonly == NULL || imported == NULL)
+        return NULL;
+
+    args = Py_BuildValue("OnsnnOOi",
+                         address,
+                         Py_SIZE(self),
+                         ENDIAN_STR(self->endian),
+                         PADBITS(self),
+                         self->allocated,
+                         readonly,
+                         imported,
+                         self->ob_exports);
+    Py_DECREF(address);
+    Py_DECREF(readonly);
+    Py_DECREF(imported);
+    res = PyObject_CallObject(info, args);
+    Py_DECREF(args);
     return res;
 }
 
 PyDoc_STRVAR(buffer_info_doc,
-"buffer_info() -> tuple\n\
+"buffer_info() -> BufferInfo\n\
 \n\
-Return a tuple containing:\n\
+Return named tuple with following fields:\n\
 \n\
-0. memory address of buffer\n\
-1. buffer size (in bytes)\n\
-2. bit-endianness as a Unicode string\n\
-3. number of pad bits\n\
-4. allocated memory for the buffer (in bytes)\n\
-5. memory is read-only\n\
-6. buffer is imported\n\
-7. number of buffer exports");
+0. `address`: memory address of buffer\n\
+1. `nbytes`: buffer size (in bytes)\n\
+2. `endian`: bit-endianness as a string\n\
+3. `padbits`: number of pad bits\n\
+4. `alloc`: allocated memory for buffer (in bytes)\n\
+5. `readonly`: memory is read-only (bool)\n\
+6. `imported`: buffer is imported (bool)\n\
+7. `exports`: number of buffer exports");
 
 
 static PyObject *
@@ -1064,7 +1077,7 @@ bitarray_clear(bitarrayobject *self)
 PyDoc_STRVAR(clear_doc,
 "clear()\n\
 \n\
-Remove all items from the bitarray.");
+Remove all items from bitarray.");
 
 
 /* Set readonly member to 1 if self is an instance of frozenbitarray.
@@ -1077,12 +1090,7 @@ freeze_if_frozen(bitarrayobject *self)
 
     assert(self->ob_exports == 0 && self->buffer == NULL);
     if (frozen == NULL) {
-        PyObject *bitarray_module;
-
-        if ((bitarray_module = PyImport_ImportModule("bitarray")) == NULL)
-            return NULL;
-        frozen = PyObject_GetAttrString(bitarray_module, "frozenbitarray");
-        Py_DECREF(bitarray_module);
+        frozen = bitarray_module_attr("frozenbitarray");
         if (frozen == NULL)
             return NULL;
     }
@@ -1111,7 +1119,7 @@ bitarray_copy(bitarrayobject *self)
 PyDoc_STRVAR(copy_doc,
 "copy() -> bitarray\n\
 \n\
-Return a copy of the bitarray.");
+Return copy of bitarray (with same bit-endianness).");
 
 
 static PyObject *
@@ -1178,7 +1186,7 @@ PyDoc_STRVAR(extend_doc,
 "extend(iterable, /)\n\
 \n\
 Append items from to the end of the bitarray.\n\
-If `iterable` is a Unicode string, each `0` and `1` are appended as\n\
+If `iterable` is a (Unicode) string, each `0` and `1` are appended as\n\
 bits (ignoring whitespace and underscore).");
 
 
@@ -1259,7 +1267,7 @@ PyDoc_STRVAR(index_doc,
 \n\
 Return lowest (or rightmost when `right=True`) index where sub_bitarray\n\
 is found, such that sub_bitarray is contained within `[start:stop]`.\n\
-Raises `ValueError` when the sub_bitarray is not present.");
+Raises `ValueError` when sub_bitarray is not present.");
 
 
 static PyObject *
@@ -1340,7 +1348,7 @@ PyDoc_STRVAR(invert_doc,
 "invert(index=<all bits>, /)\n\
 \n\
 Invert all bits in bitarray (in-place).\n\
-When the optional `index` is given, only invert the single bit at index.");
+When the optional `index` is given, only invert the single bit at `index`.");
 
 
 static PyObject *
@@ -1350,13 +1358,7 @@ bitarray_reduce(bitarrayobject *self)
     PyObject *dict, *bytes, *result;
 
     if (reconstructor == NULL) {
-        PyObject *bitarray_module;
-
-        if ((bitarray_module = PyImport_ImportModule("bitarray")) == NULL)
-            return NULL;
-        reconstructor = PyObject_GetAttrString(bitarray_module,
-                                               "_bitarray_reconstructor");
-        Py_DECREF(bitarray_module);
+        reconstructor = bitarray_module_attr("_bitarray_reconstructor");
         if (reconstructor == NULL)
             return NULL;
     }
@@ -1575,7 +1577,7 @@ bitarray_tobytes(bitarrayobject *self)
 PyDoc_STRVAR(tobytes_doc,
 "tobytes() -> bytes\n\
 \n\
-Return the bitarray buffer in bytes (pad bits are set to zero).");
+Return the bitarray buffer (pad bits are set to zero).");
 
 
 /* Extend self with bytes from f.read(n).  Return number of bytes actually
@@ -1675,7 +1677,7 @@ bitarray_tofile(bitarrayobject *self, Py
 PyDoc_STRVAR(tofile_doc,
 "tofile(f, /)\n\
 \n\
-Write byte representation of bitarray to file object f.");
+Write bitarray buffer to file object `f`.");
 
 
 static PyObject *
@@ -1720,7 +1722,7 @@ bitarray_to01(bitarrayobject *self, PyOb
 PyDoc_STRVAR(to01_doc,
 "to01(group=0, sep=' ') -> str\n\
 \n\
-Return bitarray as Unicode string of '0's and '1's.\n\
+Return bitarray as (Unicode) string of `0`s and `1`s.\n\
 The bits are grouped into `group` bits (default is no grouping).\n\
 When grouped, the string `sep` is inserted between groups\n\
 of `group` characters, default is a space.");
@@ -4161,39 +4163,44 @@ Set the default bit-endianness for new b
 
 
 static PyObject *
-sysinfo(PyObject *module)
+sysinfo(PyObject *module, PyObject *args)
 {
-    return Py_BuildValue("iiiiiiiii",
-                         (int) sizeof(void *),
-                         (int) sizeof(size_t),
-                         (int) sizeof(bitarrayobject),
-                         (int) sizeof(decodetreeobject),
-                         (int) sizeof(binode),
-                         (int) HAVE_BUILTIN_BSWAP64,
-#ifndef NDEBUG
-                         1,
+    char *key;
+
+    if (!PyArg_ParseTuple(args, "s:_sysinfo", &key))
+        return NULL;
+
+#define R(k, v)                             \
+    if (strcmp(key, k) == 0)                \
+        return PyLong_FromLong((long) (v))
+
+    R("void*", sizeof(void *));
+    R("size_t", sizeof(size_t));
+    R("bitarrayobject", sizeof(bitarrayobject));
+    R("decodetreeobject", sizeof(decodetreeobject));
+    R("binode", sizeof(binode));
+    R("PY_LITTLE_ENDIAN", PY_LITTLE_ENDIAN);
+    R("PY_BIG_ENDIAN", PY_BIG_ENDIAN);
+    R("HAVE_BUILTIN_BSWAP64", HAVE_BUILTIN_BSWAP64);
+#ifdef Py_DEBUG          /* Python configured using --with-pydebug  */
+    R("Py_DEBUG", 1);
 #else
-                         0,
+    R("Py_DEBUG", 0);
 #endif
-                         (int) PY_LITTLE_ENDIAN,
-                         (int) PY_BIG_ENDIAN
-                         );
+#ifndef NDEBUG           /* bitarray compiled without -DNDEBUG */
+    R("DEBUG", 1);
+#else
+    R("DEBUG", 0);
+#endif
+
+    return PyErr_Format(PyExc_KeyError, "%s", key);
+#undef R
 }
 
 PyDoc_STRVAR(sysinfo_doc,
-"_sysinfo() -> tuple\n\
-\n\
-Return tuple containing:\n\
+"_sysinfo(key) -> int\n\
 \n\
-0. sizeof(void *)\n\
-1. sizeof(size_t)\n\
-2. sizeof(bitarrayobject)\n\
-3. sizeof(decodetreeobject)\n\
-4. sizeof(binode)\n\
-5. HAVE_BUILTIN_BSWAP64\n\
-6. NDEBUG not defined\n\
-7. PY_LITTLE_ENDIAN\n\
-8. PY_BIG_ENDIAN");
+Return system and compile specific information given a key.");
 
 
 static PyMethodDef module_functions[] = {
@@ -4206,7 +4213,7 @@ static PyMethodDef module_functions[] =
      get_default_endian_doc},
     {"_set_default_endian", (PyCFunction) set_default_endian, METH_VARARGS,
      set_default_endian_doc},
-    {"_sysinfo",            (PyCFunction) sysinfo,            METH_NOARGS,
+    {"_sysinfo",            (PyCFunction) sysinfo,            METH_VARARGS,
      sysinfo_doc},
     {NULL,                  NULL}  /* sentinel */
 };
diff -pruN 3.6.1-1/bitarray/_util.c 3.7.1-1/bitarray/_util.c
--- 3.6.1-1/bitarray/_util.c	2025-08-12 08:35:42.000000000 +0000
+++ 3.7.1-1/bitarray/_util.c	2025-08-28 20:59:12.000000000 +0000
@@ -572,7 +572,7 @@ byteswap(PyObject *module, PyObject *arg
 }
 
 PyDoc_STRVAR(byteswap_doc,
-"byteswap(a, /, n=<buffer size>)\n\
+"byteswap(a, n=<buffer size>, /)\n\
 \n\
 Reverse every `n` consecutive bytes of `a` in-place.\n\
 By default, all bytes are reversed.  Note that `n` is not limited to 2, 4\n\
@@ -837,7 +837,7 @@ static const char base64_alphabet[] =
 
 /* Given the length of the base m in [1..6] and a character c, return
    its index in the base 2**m alphabet , or -1 if when c is not included.
-   Note: i >> m checks if i is in range(0, 2**m) */
+   Note: i >> m is true when i is not in range(0, 2**m) */
 static int
 digit_to_int(int m, char c)
 {
@@ -869,14 +869,18 @@ digit_to_int(int m, char c)
 static int
 base_to_length(int n)
 {
-    int m;
+    int m = 0;
 
-    for (m = 1; m < 7; m++) {
-        if (n == (1 << m))
-            return m;
+    if (!n || n & (n - 1)) {
+        PyErr_SetString(PyExc_ValueError, "base must be a power of 2");
+        return -1;
     }
-    PyErr_Format(PyExc_ValueError,
-                 "base must be 2, 4, 8, 16, 32 or 64, not %d", n);
+    while (n >>= 1)
+        m++;
+    if (1 <= m && m <= 6)
+        return m;
+
+    PyErr_SetString(PyExc_ValueError, "base must be 2, 4, 8, 16, 32 or 64");
     return -1;
 }
 
@@ -1731,7 +1735,7 @@ sc_decode(PyObject *module, PyObject *ob
 }
 
 PyDoc_STRVAR(sc_decode_doc,
-"sc_decode(stream) -> bitarray\n\
+"sc_decode(stream, /) -> bitarray\n\
 \n\
 Decompress binary stream (an integer iterator, or bytes-like object) of a\n\
 sparse compressed (`sc`) bitarray, and return the decoded  bitarray.\n\
@@ -2133,6 +2137,17 @@ module_cfw(PyObject *module, PyObject *a
 }
 
 static PyObject *
+module_d2i(PyObject *module, PyObject *args)
+{
+    int m;
+    char c;
+
+    if (!PyArg_ParseTuple(args, "ic", &m, &c))
+        return NULL;
+    return PyLong_FromLong(digit_to_int(m, c));
+}
+
+static PyObject *
 module_read_n(PyObject *module, PyObject *args)
 {
     PyObject *iter;
@@ -2207,6 +2222,7 @@ static PyMethodDef module_functions[] =
     {"_setup_table", (PyCFunction) module_setup_table, METH_O,       0},
     {"_zlw",         (PyCFunction) module_zlw,         METH_O,       0},
     {"_cfw",         (PyCFunction) module_cfw,         METH_VARARGS, 0},
+    {"_d2i",         (PyCFunction) module_d2i,         METH_VARARGS, 0},
     {"_read_n",      (PyCFunction) module_read_n,      METH_VARARGS, 0},
     {"_write_n",     (PyCFunction) module_write_n,     METH_VARARGS, 0},
     {"_sc_rts",      (PyCFunction) module_sc_rts,      METH_O,       0},
@@ -2224,14 +2240,9 @@ static PyModuleDef moduledef = {
 PyMODINIT_FUNC
 PyInit__util(void)
 {
-    PyObject *m, *bitarray_module;
+    PyObject *m;
 
-    bitarray_module = PyImport_ImportModule("bitarray");
-    if (bitarray_module == NULL)
-        return NULL;
-    bitarray_type = (PyTypeObject *) PyObject_GetAttrString(bitarray_module,
-                                                            "bitarray");
-    Py_DECREF(bitarray_module);
+    bitarray_type = (PyTypeObject *) bitarray_module_attr("bitarray");
     if (bitarray_type == NULL)
         return NULL;
 
diff -pruN 3.6.1-1/bitarray/bitarray.h 3.7.1-1/bitarray/bitarray.h
--- 3.6.1-1/bitarray/bitarray.h	2025-08-12 08:35:42.000000000 +0000
+++ 3.7.1-1/bitarray/bitarray.h	2025-08-28 20:59:12.000000000 +0000
@@ -4,7 +4,7 @@
 
    Author: Ilan Schnell
 */
-#define BITARRAY_VERSION  "3.6.1"
+#define BITARRAY_VERSION  "3.7.1"
 
 #ifdef STDC_HEADERS
 #  include <stddef.h>
@@ -334,3 +334,18 @@ ensure_eq_size_endian(bitarrayobject *a,
     }
     return 0;
 }
+
+/* Equivalent to: import bitarray; return getattr(bitarray, name) */
+static inline PyObject *
+bitarray_module_attr(char *name)
+{
+    PyObject *bitarray_module, *result;
+
+    bitarray_module = PyImport_ImportModule("bitarray");
+    if (bitarray_module == NULL)
+        return NULL;
+
+    result = PyObject_GetAttrString(bitarray_module, name);
+    Py_DECREF(bitarray_module);
+    return result;
+}
diff -pruN 3.6.1-1/bitarray/test_bitarray.py 3.7.1-1/bitarray/test_bitarray.py
--- 3.6.1-1/bitarray/test_bitarray.py	2025-08-12 08:35:42.000000000 +0000
+++ 3.7.1-1/bitarray/test_bitarray.py	2025-08-28 20:59:12.000000000 +0000
@@ -1,3 +1,7 @@
+# Copyright (c) 2008 - 2025, Ilan Schnell; All Rights Reserved
+# bitarray is published under the PSF license.
+#
+# Author: Ilan Schnell
 """
 Tests for bitarray
 
@@ -14,6 +18,7 @@ import shutil
 import tempfile
 from io import BytesIO, UnsupportedOperation
 from random import choice, getrandbits, randrange, randint, shuffle
+from string import whitespace
 
 # imports needed inside tests
 import array
@@ -31,7 +36,8 @@ is_pypy = bool(platform.python_implement
 
 from bitarray import (bitarray, frozenbitarray, bits2bytes, decodetree,
                       get_default_endian, _set_default_endian,
-                      _bitarray_reconstructor, _sysinfo, __version__)
+                      _bitarray_reconstructor, _sysinfo as sysinfo,
+                      BufferInfo, __version__)
 
 def skipIf(condition):
     "Skip a test if the condition is true."
@@ -39,26 +45,7 @@ def skipIf(condition):
         return lambda f: None
     return lambda f: f
 
-SYSINFO = _sysinfo()
-PTRSIZE = SYSINFO[0]  # pointer size in bytes
-DEBUG = SYSINFO[6]
-
-
-def buffer_info(a, key=None):
-    fields = (
-        "address",    # 0. address of byte buffer
-        "size",       # 1. buffer size in bytes
-        "endian",     # 2. bit-endianness
-        "padbits",    # 3. number of pad bits
-        "allocated",  # 4. allocated memory
-        "readonly",   # 5. memory is read-only
-        "imported",   # 6. buffer is imported
-        "exports",    # 7. number of buffer exports
-    )
-    info = a.buffer_info()
-    res = dict(zip(fields, info))
-    return res if key is None else res[key]
-
+PTRSIZE = sysinfo("void*")  # pointer size in bytes
 
 # avoid importing from bitarray.util
 zeros = bitarray
@@ -75,8 +62,6 @@ def urandom_2(n, endian="<RAND>"):
     del a[n:]
     return a
 
-WHITESPACE = ' \n\r\t\v'
-
 
 class Util(object):
 
@@ -110,45 +95,37 @@ class Util(object):
     def check_obj(self, a):
         self.assertIsInstance(a, bitarray)
 
-        ptr, nbytes, endian, padbits, alloc, readonly, buf, exports = \
-                                                          a.buffer_info()
-        self.assertEqual(nbytes, bits2bytes(len(a)))
-        self.assertTrue(0 <= padbits < 8)
-        self.assertEqual(endian, a.endian)
-        self.assertTrue(endian in ('little', 'big'))
-        self.assertEqual(a.nbytes, nbytes)
-        self.assertEqual(a.padbits, padbits)
-        self.assertEqual(a.readonly, readonly)
-        self.assertEqual(len(a) + padbits, 8 * nbytes)
+        self.assertEqual(a.nbytes, bits2bytes(len(a)))
+        self.assertTrue(0 <= a.padbits < 8)
+        self.assertEqual(len(a) + a.padbits, 8 * a.nbytes)
 
-        if buf:
+        info = a.buffer_info()
+        if info.imported:
             # imported buffer implies that no extra memory is allocated
-            self.assertEqual(alloc, 0)
+            self.assertEqual(info.alloc, 0)
             # an imported buffer will always have a multiple of 8 bits
-            self.assertEqual(len(a) % 8, 0)
-            self.assertEqual(len(a), 8 * nbytes)
-            self.assertEqual(padbits, 0)
+            self.assertEqual(len(a), 8 * a.nbytes)
+            self.assertEqual(a.padbits, 0)
         else:
             # the allocated memory is always larger than the buffer size
-            self.assertTrue(alloc >= nbytes)
+            self.assertTrue(info.alloc >= a.nbytes)
 
-        if ptr == 0:
+        if info.address == 0:
             # the buffer being a NULL pointer implies that the buffer size
             # and the allocated memory size are 0
-            self.assertEqual(nbytes, 0)
-            self.assertEqual(alloc, 0)
+            self.assertEqual(a.nbytes, 0)
+            self.assertEqual(info.alloc, 0)
 
         if type(a) == frozenbitarray:
             # frozenbitarray have read-only memory
-            self.assertEqual(readonly, 1)
-            if padbits:  # ensure padbits are zero
-                b = bitarray(bytes(a)[-1:], endian=endian)[-padbits:]
-                self.assertEqual(len(b), padbits)
+            self.assertEqual(a.readonly, 1)
+            if a.padbits:  # ensure padbits are zero
+                b = bitarray(bytes(a)[-1:], endian=a.endian)[-a.padbits:]
+                self.assertEqual(len(b), a.padbits)
                 self.assertEqual(b.count(), 0)
-        elif not buf:
-            self.assertFalse(isinstance(a, frozenbitarray))
+        elif not info.imported:
             # otherwise, unless the buffer is imported, it is writable
-            self.assertEqual(readonly, 0)
+            self.assertFalse(a.readonly)
 
     def assertEQUAL(self, a, b):
         self.assertEqual(a, b)
@@ -167,37 +144,40 @@ class Util(object):
             if msg != str(e):
                 raise AssertionError("message: %s\n got: %s" % (msg, e))
 
-# ---------------------------------------------------------------------------
+# --------------------------  Module Functions  -----------------------------
 
-class ModuleFunctionsTests(unittest.TestCase, Util):
+class ModuleFunctionsTests(unittest.TestCase):
 
     def test_version_string(self):
         # the version string is not a function, but test it here anyway
         self.assertEqual(type(__version__), str)
 
     def test_sysinfo(self):
-        info = _sysinfo()
-        self.assertEqual(info[0], PTRSIZE)
-        self.assertEqual(info[1], PTRSIZE)
-
-        self.assertEqual(type(info), tuple)
-        for x in info:
-            self.assertEqual(type(x), int)
-
-        self.assertEqual(info[7], int(sys.byteorder == 'little'))
-        self.assertEqual(info[8], int(sys.byteorder == 'big'))
-
-    @skipIf(is_pypy)  # PyPy doesn't have tuple.__itemsize__
-    def test_ptrsize(self):
-        self.assertEqual(PTRSIZE, tuple.__itemsize__)
-
-    def test_maxsize(self):
-        self.assertEqual(sys.maxsize, 2 ** (PTRSIZE * 8 - 1) - 1)
+        for key in ["void*", "size_t", "bitarrayobject", "decodetreeobject",
+                    "binode", "HAVE_BUILTIN_BSWAP64", "PY_LITTLE_ENDIAN",
+                    "PY_BIG_ENDIAN", "Py_DEBUG", "DEBUG"]:
+            res = sysinfo(key)
+            self.assertEqual(type(res), int)
+
+    def test_sysinfo_errors(self):
+        self.assertRaises(TypeError, sysinfo)
+        self.assertRaises(TypeError, sysinfo, b"void*")
+        self.assertRaises(KeyError, sysinfo, "foo")
+
+    def test_sysinfo_pointer_size(self):
+        self.assertEqual(sysinfo("void*"), PTRSIZE)
+        self.assertEqual(sysinfo("size_t"), PTRSIZE)
+        self.assertEqual(sys.maxsize, 2 ** (8 * PTRSIZE - 1) - 1)
+        if not is_pypy:  # PyPy doesn't have tuple.__itemsize__
+            self.assertEqual(PTRSIZE, tuple.__itemsize__)
+
+    def test_sysinfo_byteorder(self):
+        self.assertEqual(sys.byteorder == "little",
+                         sysinfo("PY_LITTLE_ENDIAN"))
+        self.assertEqual(sys.byteorder == "big",
+                         sysinfo("PY_BIG_ENDIAN"))
 
     def test_set_default_endian(self):
-        self.assertRaises(TypeError, _set_default_endian, 0)
-        self.assertRaises(TypeError, _set_default_endian, 'little', 0)
-        self.assertRaises(ValueError, _set_default_endian, 'foo')
         for default_endian in 'big', 'little':
             _set_default_endian(default_endian)
             a = bitarray()
@@ -211,30 +191,28 @@ class ModuleFunctionsTests(unittest.Test
                 self.assertEqual(a.endian,
                                  default_endian if endian is None else endian)
 
-            # make sure that calling _set_default_endian wrong does not
+            # make sure that wrong calling _set_default_endian() does not
             # change the default endianness
             self.assertRaises(ValueError, _set_default_endian, 'foobar')
             self.assertEqual(bitarray().endian, default_endian)
 
+    def test_set_default_endian_errors(self):
+        self.assertRaises(TypeError, _set_default_endian, 0)
+        self.assertRaises(TypeError, _set_default_endian, 'little', 0)
+        self.assertRaises(ValueError, _set_default_endian, 'foo')
+
     def test_get_default_endian(self):
-        # takes no arguments
-        self.assertRaises(TypeError, get_default_endian, 'big')
         for default_endian in 'big', 'little':
             _set_default_endian(default_endian)
             endian = get_default_endian()
             self.assertEqual(endian, default_endian)
             self.assertEqual(type(endian), str)
 
-    def test_bits2bytes(self):
-        for arg in 'foo', [], None, {}, 187.0, -4.0:
-            self.assertRaises(TypeError, bits2bytes, arg)
-
-        self.assertRaises(TypeError, bits2bytes)
-        self.assertRaises(TypeError, bits2bytes, 1, 2)
-
-        self.assertRaises(ValueError, bits2bytes, -1)
-        self.assertRaises(ValueError, bits2bytes, -924)
+    def test_get_default_endian_errors(self):
+        # takes no arguments
+        self.assertRaises(TypeError, get_default_endian, 'big')
 
+    def test_bits2bytes(self):
         for n, res in (0, 0), (1, 1), (7, 1), (8, 1), (9, 2):
             self.assertEqual(bits2bytes(n), res)
 
@@ -246,6 +224,16 @@ class ModuleFunctionsTests(unittest.Test
             k = (1 << n) + randrange(1000)
             self.assertEqual(bits2bytes(k), (k - 1) // 8 + 1)
 
+    def test_bits2bytes_errors(self):
+        for arg in 'foo', [], None, {}, 187.0, -4.0:
+            self.assertRaises(TypeError, bits2bytes, arg)
+
+        self.assertRaises(TypeError, bits2bytes)
+        self.assertRaises(TypeError, bits2bytes, 1, 2)
+
+        self.assertRaises(ValueError, bits2bytes, -1)
+        self.assertRaises(ValueError, bits2bytes, -924)
+
 # ---------------------------------------------------------------------------
 
 class CreateObjectTests(unittest.TestCase, Util):
@@ -415,10 +403,10 @@ class CreateObjectTests(unittest.TestCas
         self.assertRaises(ValueError, bitarray, '0\U00010348')  # UCS4
 
     def test_string01_whitespace(self):
-        a = bitarray(WHITESPACE)
+        a = bitarray(whitespace)
         self.assertEqual(a, bitarray())
 
-        for c in WHITESPACE:
+        for c in whitespace:
             a = bitarray(c + '1101110001')
             self.assertEqual(a, bitarray('1101110001'))
 
@@ -556,28 +544,6 @@ class ToObjectsTests(unittest.TestCase,
         for a in self.randombitarrays():
             self.assertEqual(set(a), set(a.tolist()))
 
-# ---------------------------------------------------------------------------
-
-class MetaDataTests(unittest.TestCase):
-
-    def test_buffer_info(self):
-        a = bitarray(13, endian='little')
-        self.assertEqual(a.buffer_info()[1:4], (2, 'little', 3))
-
-        info = a.buffer_info()
-        self.assertEqual(type(info), tuple)
-        self.assertEqual(len(info), 8)
-        for i, item in enumerate(info):
-            if i == 2:
-                self.assertEqual(type(item), str)
-                continue
-            self.assertEqual(type(item), int)
-
-    def test_endian(self):
-        for endian in 'big', 'little':
-            a = bitarray(endian=endian)
-            self.assertEqual(a.endian, endian)
-
 # -------------------------- (Number) index tests ---------------------------
 
 class GetItemTests(unittest.TestCase, Util):
@@ -2651,7 +2617,7 @@ class ExtendTests(unittest.TestCase, Uti
 
     def test_string01_whitespace(self):
         a = bitarray()
-        a.extend(WHITESPACE)
+        a.extend(whitespace)
         self.assertEqual(len(a), 0)
         a.extend('0 1\n0\r1\t0\v1_')
         self.assertEqual(a, bitarray('010101'))
@@ -2737,6 +2703,53 @@ class AppendTests(unittest.TestCase, Uti
             self.assertEQUAL(b, a[:i+1])
         self.check_obj(b)
 
+class BufferInfoTests(unittest.TestCase):
+
+    def test_values(self):
+        for a, views, res in [
+
+                (bitarray(11, endian='little'), 0,
+                 (2, 'little', 5, 2, False, False, 0)),
+
+                (bitarray(endian='big', buffer=b"ABC"), 2,
+                 (3, 'big', 0, 0, True, True, 2)),
+
+                (frozenbitarray("00100", 'big'), 5,
+                 (1, 'big', 3, 1, True, False, 5)),
+        ]:
+            d = {}
+            for i in range(views):
+                d[i] = memoryview(a)
+            self.assertEqual(len(d), views)
+
+            info = a.buffer_info()
+            self.assertEqual(info[1:8], res)
+            self.assertEqual(info.nbytes, a.nbytes)
+            self.assertEqual(info.endian, a.endian)
+            self.assertEqual(info.padbits, a.padbits)
+            self.assertEqual(info.readonly, a.readonly)
+            self.assertEqual(info.exports, views)
+
+    def test_types(self):
+        a = urandom_2(57)
+        info = a.buffer_info()
+        self.assertTrue(isinstance(info, tuple))
+        self.assertEqual(type(info), BufferInfo)
+        self.assertEqual(len(info), 8)
+
+        for i, (item, tp) in enumerate([
+                (info.address, int),
+                (info.nbytes, int),
+                (info.endian, str),
+                (info.padbits, int),
+                (info.alloc, int),
+                (info.readonly, bool),
+                (info.imported, bool),
+                (info.exports, int),
+        ]):
+            self.assertEqual(type(item), tp)
+            self.assertTrue(info[i] is item)
+
 class InsertTests(unittest.TestCase, Util):
 
     def test_basic(self):
@@ -3943,12 +3956,21 @@ class BytesTests(unittest.TestCase, Util
 
 class DescriptorTests(unittest.TestCase, Util):
 
+    def test_endian(self):
+        for endian in "little", "big":
+            a = bitarray('1101100', endian)
+            self.assertEqual(a.endian, endian)
+            self.assertEqual(type(a.endian), str)
+
     def test_nbytes_padbits(self):
-        for a in self.randombitarrays():
-            self.assertEqual(a.nbytes, bits2bytes(len(a)))
-            self.assertEqual(a.padbits, 8 * a.nbytes - len(a))
-            self.assertTrue(0 <= a.padbits < 8)
+        for n in range(50):
+            a = bitarray(n)
+            # .nbytes
+            self.assertEqual(a.nbytes, bits2bytes(n))
             self.assertEqual(type(a.nbytes), int)
+            # .padbits
+            self.assertEqual(a.padbits, 8 * a.nbytes - n)
+            self.assertTrue(0 <= a.padbits < 8)
             self.assertEqual(type(a.padbits), int)
 
     def test_readonly(self):
@@ -4073,7 +4095,7 @@ class FileTests(unittest.TestCase, Util)
             with open(self.tmpfname, 'rb') as fi:
                 a.fromfile(fi)
             self.assertEqual(len(a), 8 * N)
-            self.assertEqual(buffer_info(a, 'size'), N)
+            self.assertEqual(a.nbytes, N)
             self.assertEqual(a.tobytes(), data)
             self.check_obj(a)
 
@@ -4221,9 +4243,9 @@ class FileTests(unittest.TestCase, Util)
         with open(self.tmpfname, 'r+b') as f:  # see issue #141
             with mmap.mmap(f.fileno(), 0) as mapping:
                 a = bitarray(buffer=mapping, endian='little')
-                info = buffer_info(a)
-                self.assertFalse(info['readonly'])
-                self.assertTrue(info['imported'])
+                info = a.buffer_info()
+                self.assertFalse(info.readonly)
+                self.assertTrue(info.imported)
                 self.assertEqual(a, zeros(8000))
                 a[::2] = True
                 # not sure this is necessary, without 'del a', I get:
@@ -4240,9 +4262,9 @@ class FileTests(unittest.TestCase, Util)
 
         with open(self.tmpfname, 'r+b') as f:
             a = bitarray(buffer=mmap.mmap(f.fileno(), 0), endian='little')
-            info = buffer_info(a)
-            self.assertFalse(info['readonly'])
-            self.assertTrue(info['imported'])
+            info = a.buffer_info()
+            self.assertFalse(info.readonly)
+            self.assertTrue(info.imported)
             self.assertEqual(a, 1000 * bitarray('0100 0100'))
             a[::4] = 1
 
@@ -4256,9 +4278,9 @@ class FileTests(unittest.TestCase, Util)
         with open(self.tmpfname, 'rb') as fi:  # readonly
             m = mmap.mmap(fi.fileno(), 0, access=mmap.ACCESS_READ)
             a = bitarray(buffer=m, endian='big')
-            info = buffer_info(a)
-            self.assertTrue(info['readonly'])
-            self.assertTrue(info['imported'])
+            info = a.buffer_info()
+            self.assertTrue(info.readonly)
+            self.assertTrue(info.imported)
             self.assertRaisesMessage(TypeError,
                                      "cannot modify read-only memory",
                                      a.__setitem__, 0, 1)
@@ -4620,10 +4642,10 @@ class BufferImportTests(unittest.TestCas
         b = 100 * b'\0'
         a = bitarray(buffer=b)
 
-        info = buffer_info(a)
-        self.assertFalse(info['allocated'])
-        self.assertTrue(info['readonly'])
-        self.assertTrue(info['imported'])
+        info = a.buffer_info()
+        self.assertEqual(info.alloc, 0)
+        self.assertTrue(info.readonly)
+        self.assertTrue(info.imported)
 
         self.assertRaises(TypeError, a.setall, 1)
         self.assertRaises(TypeError, a.clear)
@@ -4635,10 +4657,10 @@ class BufferImportTests(unittest.TestCas
         b = bytearray(100 * [0])
         a = bitarray(buffer=b, endian='little')
 
-        info = buffer_info(a)
-        self.assertFalse(info['allocated'])
-        self.assertFalse(info['readonly'])
-        self.assertTrue(info['imported'])
+        info = a.buffer_info()
+        self.assertEqual(info.alloc, 0)
+        self.assertFalse(info.readonly)
+        self.assertTrue(info.imported)
 
         a[0] = 1
         self.assertEqual(b[0], 1)
@@ -4675,14 +4697,14 @@ class BufferImportTests(unittest.TestCas
         # a and b are two distinct bitarrays that share the same buffer now
         self.assertFalse(a is b)
 
-        a_info = buffer_info(a)
-        self.assertFalse(a_info['imported'])
-        self.assertEqual(a_info['exports'], 1)
-        b_info = buffer_info(b)
-        self.assertTrue(b_info['imported'])
-        self.assertEqual(b_info['exports'], 0)
+        a_info = a.buffer_info()
+        self.assertFalse(a_info.imported)
+        self.assertEqual(a_info.exports, 1)
+        b_info = b.buffer_info()
+        self.assertTrue(b_info.imported)
+        self.assertEqual(b_info.exports, 0)
         # buffer address is the same
-        self.assertEqual(a_info['address'], b_info['address'])
+        self.assertEqual(a_info.address, b_info.address)
 
         self.assertFalse(a is b)
         self.assertEqual(a, b)
@@ -4717,11 +4739,11 @@ class BufferImportTests(unittest.TestCas
     def test_bitarray_shared_sections(self):
         a = urandom_2(0x2000, 'big')
         b = bitarray(buffer=memoryview(a)[0x100:0x300])
-        self.assertEqual(buffer_info(b, 'address'),
-                         buffer_info(a, 'address') + 0x100)
+        self.assertEqual(b.buffer_info().address,
+                         a.buffer_info().address + 0x100)
         c = bitarray(buffer=memoryview(a)[0x200:0x800])
-        self.assertEqual(buffer_info(c, 'address'),
-                         buffer_info(a, 'address') + 0x200)
+        self.assertEqual(c.buffer_info().address,
+                         a.buffer_info().address + 0x200)
         self.assertEqual(a[8 * 0x100 : 8 * 0x300], b)
         self.assertEqual(a[8 * 0x200 : 8 * 0x800], c)
         a.setall(0)
@@ -4785,9 +4807,9 @@ class BufferImportTests(unittest.TestCas
     @skipIf(is_pypy)
     def test_readonly_errors(self):
         a = bitarray(buffer=b'A')
-        info = buffer_info(a)
-        self.assertTrue(info['readonly'])
-        self.assertTrue(info['imported'])
+        info = a.buffer_info()
+        self.assertTrue(info.readonly)
+        self.assertTrue(info.imported)
 
         self.assertRaises(TypeError, a.append, True)
         self.assertRaises(TypeError, a.bytereverse)
@@ -4817,9 +4839,9 @@ class BufferImportTests(unittest.TestCas
     @skipIf(is_pypy)
     def test_resize_errors(self):
         a = bitarray(buffer=bytearray([123]))
-        info = buffer_info(a)
-        self.assertFalse(info['readonly'])
-        self.assertTrue(info['imported'])
+        info = a.buffer_info()
+        self.assertFalse(info.readonly)
+        self.assertTrue(info.imported)
 
         self.assertRaises(BufferError, a.append, True)
         self.assertRaises(BufferError, a.clear)
@@ -4841,7 +4863,7 @@ class BufferExportTests(unittest.TestCas
         a = bitarray('01000001 01000010 01000011', endian='big')
         v = memoryview(a)
         self.assertFalse(v.readonly)
-        self.assertEqual(buffer_info(a, 'exports'), 1)
+        self.assertEqual(a.buffer_info().exports, 1)
         self.assertEqual(len(v), 3)
         self.assertEqual(v[0], 65)
         self.assertEqual(v.tobytes(), b'ABC')
@@ -4850,7 +4872,7 @@ class BufferExportTests(unittest.TestCas
 
         w = memoryview(a)  # a second buffer export
         self.assertFalse(w.readonly)
-        self.assertEqual(buffer_info(a, 'exports'), 2)
+        self.assertEqual(a.buffer_info().exports, 2)
         self.check_obj(a)
 
     def test_many_exports(self):
@@ -4858,7 +4880,7 @@ class BufferExportTests(unittest.TestCas
         d = {}  # put bitarrays in dict to key object around
         for n in range(1, 20):
             d[n] = bitarray(buffer=a)
-            self.assertEqual(buffer_info(a, 'exports'), n)
+            self.assertEqual(a.buffer_info().exports, n)
             self.assertEqual(len(d[n]), 16)
         self.check_obj(a)
 
@@ -4867,10 +4889,10 @@ class BufferExportTests(unittest.TestCas
             a = bitarray(n)
             v = memoryview(a)
             self.assertEqual(len(v), a.nbytes)
-            info = buffer_info(a)
-            self.assertFalse(info['readonly'])
-            self.assertFalse(info['imported'])
-            self.assertEqual(info['exports'], 1)
+            info = a.buffer_info()
+            self.assertFalse(info.readonly)
+            self.assertFalse(info.imported)
+            self.assertEqual(info.exports, 1)
             self.check_obj(a)
 
     def test_read_random(self):
@@ -5065,9 +5087,9 @@ class FrozenbitarrayTests(unittest.TestC
         b = bytes([15, 95, 128])
         a = frozenbitarray(buffer=b, endian='big')
         self.assertEQUAL(a, bitarray('00001111 01011111 10000000', 'big'))
-        info = buffer_info(a)
-        self.assertTrue(info['readonly'])
-        self.assertTrue(info['imported'])
+        info = a.buffer_info()
+        self.assertTrue(info.readonly)
+        self.assertTrue(info.imported)
 
     @skipIf(is_pypy)
     def test_buffer_import_writable(self):
@@ -5159,13 +5181,14 @@ def run(verbosity=1):
     print('bitarray version: %s' % __version__)
     print('sys.version: %s' % sys.version)
     print('sys.prefix: %s' % sys.prefix)
-    print('pointer size: %d bit' % (8 * SYSINFO[0]))
-    print('sizeof(size_t): %d' % SYSINFO[1])
-    print('sizeof(bitarrayobject): %d' % SYSINFO[2])
-    print('HAVE_BUILTIN_BSWAP64: %d' % SYSINFO[5])
+    print('pointer size: %d bit' % (8 * PTRSIZE))
+    print('sizeof(size_t): %d' % sysinfo("size_t"));
+    print('sizeof(bitarrayobject): %d' % sysinfo("bitarrayobject"))
+    print('HAVE_BUILTIN_BSWAP64: %d' % sysinfo("HAVE_BUILTIN_BSWAP64"))
     print('default bit-endianness: %s' % default_endian)
     print('machine byte-order: %s' % sys.byteorder)
-    print('DEBUG: %s' % DEBUG)
+    print('Py_DEBUG: %s' % sysinfo("Py_DEBUG"))
+    print('DEBUG: %s' % sysinfo("DEBUG"))
     loader = unittest.TestLoader()
     suite = unittest.TestSuite()
     suite.addTests(loader.loadTestsFromModule(sys.modules[__name__]))
diff -pruN 3.6.1-1/bitarray/test_util.py 3.7.1-1/bitarray/test_util.py
--- 3.6.1-1/bitarray/test_util.py	2025-08-12 08:35:42.000000000 +0000
+++ 3.7.1-1/bitarray/test_util.py	2025-08-28 20:59:12.000000000 +0000
@@ -1,3 +1,7 @@
+# Copyright (c) 2019 - 2025, Ilan Schnell; All Rights Reserved
+# bitarray is published under the PSF license.
+#
+# Author: Ilan Schnell
 """
 Tests for bitarray.util module
 """
@@ -16,19 +20,18 @@ import tempfile
 import unittest
 from io import StringIO
 from functools import reduce
-from string import hexdigits
 from random import (choice, getrandbits, randrange, randint, random, sample,
                     seed)
+from string import hexdigits, whitespace
 from collections import Counter
 
 from bitarray import (bitarray, frozenbitarray, decodetree, bits2bytes,
                       _set_default_endian)
-from bitarray.test_bitarray import (Util, skipIf, is_pypy, urandom_2,
-                                    PTRSIZE, WHITESPACE)
+from bitarray.test_bitarray import Util, skipIf, is_pypy, urandom_2, PTRSIZE
 
 from bitarray.util import (
     zeros, ones, urandom, random_k, random_p, pprint, strip, count_n,
-    parity, sum_indices, xor_indices,
+    parity, gen_primes, sum_indices, xor_indices,
     count_and, count_or, count_xor, any_and, subset,
     correspond_all, byteswap, intervals,
     serialize, deserialize, ba2hex, hex2ba, ba2base, base2ba,
@@ -39,7 +42,7 @@ from bitarray.util import (
 
 from bitarray.util import _Random, _ssqi  # type: ignore
 
-# ---------------------------------------------------------------------------
+# ---------------------------  zeros()  ones()  -----------------------------
 
 class ZerosOnesTests(unittest.TestCase):
 
@@ -79,7 +82,7 @@ class ZerosOnesTests(unittest.TestCase):
             # endian wrong string
             self.assertRaises(ValueError, f, 0, 'foo')
 
-# ---------------------------------------------------------------------------
+# -----------------------------  urandom()  ---------------------------------
 
 class URandomTests(unittest.TestCase):
 
@@ -115,7 +118,7 @@ class URandomTests(unittest.TestCase):
         # see if population is within expectation
         self.assertTrue(abs(a.count() - 5_000_000) <= 15_811)
 
-# ---------------------------- .random_k() ----------------------------------
+# ----------------------------  random_k()  ---------------------------------
 
 HAVE_RANDBYTES = sys.version_info[:2] >= (3, 9)
 
@@ -279,7 +282,7 @@ class Random_K_Tests(unittest.TestCase):
             a = r.combine_half(seq)
             self.assertTrue(abs(a.count() - mean) < 5_000)
 
-# ---------------------------- .random_p() ----------------------------------
+# ----------------------------  random_p()  ---------------------------------
 
 HAVE_BINOMIALVARIATE = sys.version_info[:2] >= (3, 12)
 
@@ -371,7 +374,92 @@ class Random_P_Tests(unittest.TestCase):
         limit = 1.0 / (r.K + 1)  # lower limit for p
         self.assertTrue(r.SMALL_P > limit)
 
-# ---------------------------------------------------------------------------
+# ----------------------------  gen_primes()  -------------------------------
+
+class PrimeTests(unittest.TestCase):
+
+    primes = [
+        2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59, 61,
+        67, 71, 73, 79, 83, 89, 97, 101, 103, 107, 109, 113, 127, 131, 137,
+        139, 149, 151, 157, 163, 167, 173, 179, 181, 191, 193, 197, 199, 211,
+        223, 227, 229, 233, 239, 241, 251, 257, 263, 269, 271, 277, 281, 283,
+        293, 307, 311, 313, 317, 331, 337, 347, 349, 353, 359, 367, 373, 379,
+        383, 389, 397, 401, 409, 419, 421, 431, 433, 439, 443, 449, 457, 461,
+    ]
+
+    def test_errors(self):
+        P = gen_primes
+        self.assertRaises(TypeError, P, 3, 1)
+        self.assertRaises(ValueError, P, "1.0")
+        self.assertRaises(ValueError, P, -1)
+        self.assertRaises(TypeError, P, 8, 4)
+        self.assertRaises(TypeError, P, 8, foo="big")
+        self.assertRaises(ValueError, P, 8, "foo")
+        self.assertRaises(ValueError, P, 8, endian="foo")
+
+    def test_explitcit(self):
+        for n in range(230):
+            default_endian = choice(['little', 'big'])
+            _set_default_endian(default_endian)
+            endian = choice(["little", "big", None])
+            odd = getrandbits(1)
+            a = gen_primes(n, endian, odd)
+            self.assertEqual(len(a), n)
+            self.assertEqual(a.endian, endian or default_endian)
+            if odd:
+                lst = [2] + [2 * i + 1 for i in a.search(1)]
+            else:
+                lst = [i for i in a.search(1)]
+            self.assertEqual(lst, self.primes[:len(lst)])
+
+    def test_cmp(self):
+        N = 10_000
+        c = ones(N)
+        c[:2] = 0
+        for i in range(int(math.sqrt(N) + 1.0)):
+            if c[i]:
+                c[i * i :: i] = 0
+        self.assertEqual(list(c.search(1, 0, 462)), self.primes)
+
+        for _ in range(20):
+            n = randrange(N)
+            endian = choice(["little", "big"])
+            a = gen_primes(n, endian=endian)
+            self.assertEqual(a, c[:n])
+            self.assertEqual(a.endian, endian)
+
+            b = gen_primes(n // 2, endian, odd=True)
+            self.assertEqual(b, a[1::2])
+            self.assertEqual(b, c[1:n:2])
+
+        for _ in range(20):
+            i = randrange(10, 100)
+            x = randint(-1, 1)
+            n = i * i + x
+            self.assertEqual(gen_primes(n), c[:n])
+            self.assertEqual(gen_primes(n // 2, odd=1), c[1:n:2])
+
+        self.assertEqual(gen_primes(N), c)
+        self.assertEqual(gen_primes(N // 2, odd=1), c[1::2])
+
+    def test_count(self):
+        for n, count, sum_p, sum_sqr_p in [
+                (    10,    4,        17,             87),
+                (   100,   25,     1_060,         65_796),
+                ( 1_000,  168,    76_127,     49_345_379),
+                (10_000, 1229, 5_736_396, 37_546_387_960),
+        ]:
+            a = gen_primes(n)
+            self.assertEqual(len(a), n)
+            self.assertEqual(a.count(), count)
+            self.assertEqual(sum_indices(a), sum_p)
+            self.assertEqual(sum_indices(a, 2), sum_sqr_p)
+            b = gen_primes(n // 2, odd=1)
+            self.assertEqual(len(b), n // 2)
+            self.assertEqual(b.count() + 1, count)  # +1 because of prime 2
+            self.assertEqual(b, a[1::2])
+
+# -----------------------------  pprint()  ----------------------------------
 
 class PPrintTests(unittest.TestCase):
 
@@ -432,7 +520,7 @@ class PPrintTests(unittest.TestCase):
     def test_file(self):
         tmpdir = tempfile.mkdtemp()
         tmpfile = os.path.join(tmpdir, 'testfile')
-        a = bitarray(1000)
+        a = urandom_2(1000)
         try:
             with open(tmpfile, 'w') as fo:
                 pprint(a, fo)
@@ -442,7 +530,7 @@ class PPrintTests(unittest.TestCase):
         finally:
             shutil.rmtree(tmpdir)
 
-# ---------------------------------------------------------------------------
+# -----------------------------  strip()  -----------------------------------
 
 class StripTests(unittest.TestCase, Util):
 
@@ -509,7 +597,7 @@ class StripTests(unittest.TestCase, Util
             self.assertEqual(strip(a, 'both'), bitarray('1'))
             self.assertEqual(len(a), n)
 
-# ---------------------------------------------------------------------------
+# -----------------------------  count_n()  ---------------------------------
 
 class CountN_Tests(unittest.TestCase, Util):
 
@@ -622,6 +710,15 @@ class CountN_Tests(unittest.TestCase, Ut
                 msg = "n = 2 exceeds total count (a.count(1) = 1)"
             self.assertRaisesMessage(ValueError, msg, count_n, a, 2)
 
+    def test_primes(self):
+        a = gen_primes(10_000)
+        # there are 1229 primes below 10,000
+        self.assertEqual(a.count(), 1229)
+        for n, p in [(  10,   29),   # the 10th prime number is 29
+                     ( 100,  541),   # the 100th prime number is 541
+                     (1000, 7919)]:  # the 1000th prime number is 7919
+            self.assertEqual(count_n(a, n) - 1, p)
+
     def test_large(self):
         for _ in range(100):
             N = randint(100_000, 250_000)
@@ -726,7 +823,7 @@ class BitwiseCountTests(unittest.TestCas
             self.assertEqual(all(a ^ b), count_xor(a, b) == n)
             self.assertEqual(all(a ^ b), a == ~b)
 
-# ---------------------------------------------------------------------------
+# ---------------------------  any_and()  -----------------------------------
 
 class BitwiseAnyTests(unittest.TestCase, Util):
 
@@ -809,7 +906,7 @@ class BitwiseAnyTests(unittest.TestCase,
             a[i] = 1
             self.assertEqual(b[i], any_and(a, b))
 
-# ---------------------------------------------------------------------------
+# ----------------------------  subset()  -----------------------------------
 
 class SubsetTests(unittest.TestCase, Util):
 
@@ -850,7 +947,7 @@ class SubsetTests(unittest.TestCase, Uti
         for a in self.randombitarrays(start=1):
             b = a.copy()
             # we set one random bit in b to 1, so a is always a subset of b
-            b[randrange(len(a))] == 1
+            b[randrange(len(a))] = 1
             self.check(a, b, True)
             # but b is only a subset when they are equal
             self.check(b, a, a == b)
@@ -858,9 +955,9 @@ class SubsetTests(unittest.TestCase, Uti
             a.setall(1)
             self.check(b, a, True)
 
-# ---------------------------------------------------------------------------
+# -------------------------  correspond_all()  ------------------------------
 
-class CorrespondAllTests(unittest.TestCase, Util):
+class CorrespondAllTests(unittest.TestCase):
 
     def test_basic(self):
         a = frozenbitarray('0101')
@@ -882,8 +979,9 @@ class CorrespondAllTests(unittest.TestCa
             self.assertEqual(correspond_all(bitarray(a), bitarray(b)), res)
 
     def test_random(self):
-        for a in self.randombitarrays():
-            n = len(a)
+        for _ in range(100):
+            n = randrange(3000)
+            a = urandom_2(n)
             b = urandom(n, a.endian)
             res = correspond_all(a, b)
             self.assertEqual(res[0], count_and(~a, ~b))
@@ -895,10 +993,10 @@ class CorrespondAllTests(unittest.TestCa
             self.assertEqual(res[1] + res[2], count_xor(a, b))
             self.assertEqual(sum(res), n)
 
-# ---------------------------------------------------------------------------
+# -----------------------------  byteswap()  --------------------------------
 
 @skipIf(is_pypy)
-class ByteswapTests(unittest.TestCase, Util):
+class ByteSwapTests(unittest.TestCase):
 
     def test_basic_bytearray(self):
         a = bytearray(b"ABCD")
@@ -993,9 +1091,9 @@ class ByteswapTests(unittest.TestCase, U
             a.bytereverse()
             self.assertEqual(a, b[::-1])
 
-# ---------------------------------------------------------------------------
+# ------------------------------  parity()  ---------------------------------
 
-class ParityTests(unittest.TestCase, Util):
+class ParityTests(unittest.TestCase):
 
     def test_explitcit(self):
         for s, res in [('', 0), ('1', 1), ('0010011', 1), ('10100110', 0)]:
@@ -1008,10 +1106,14 @@ class ParityTests(unittest.TestCase, Uti
             self.assertEqual(parity(ones(n)), n % 2)
 
     def test_random(self):
-        a = bitarray()
+        endian = choice(["little", "big"])
+        a = bitarray(endian=endian)
         par = 0
-        for _ in range(2000):
+        for i in range(2000):
             self.assertEqual(parity(a), par)
+            self.assertEqual(par, a.count() % 2)
+            self.assertEqual(a.endian, endian)
+            self.assertEqual(len(a), i)
             v = getrandbits(1)
             a.append(v)
             par ^= v
@@ -1022,13 +1124,7 @@ class ParityTests(unittest.TestCase, Uti
         self.assertRaises(TypeError, parity)
         self.assertRaises(TypeError, parity, bitarray("110"), 1)
 
-    def test_random2(self):
-        for a in self.randombitarrays():
-            b = a.copy()
-            self.assertEqual(parity(a), a.count() % 2)
-            self.assertEqual(a, b)
-
-# ---------------------------------------------------------------------------
+# ----------------------------  sum_indices()  ------------------------------
 
 class SumIndicesUtil(unittest.TestCase):
 
@@ -1121,6 +1217,9 @@ class SumIndicesTests(SumIndicesUtil):
 
     def test_explicit(self):
         self.check_explicit(sum_indices)
+        a = gen_primes(100)
+        self.assertEqual(sum_indices(a, mode=1),  1_060)
+        self.assertEqual(sum_indices(a, mode=2), 65_796)
 
     def test_wrong_args(self):
         self.check_wrong_args(sum_indices)
@@ -1171,6 +1270,17 @@ class XoredIndicesTests(unittest.TestCas
             if i < 19:
                 self.assertEqual(lst[i], x)
 
+    def test_primes(self):
+        # OEIS A126084
+        lst = [0, 2, 1, 4, 3, 8, 5, 20, 7, 16, 13, 18, 55, 30, 53, 26, 47]
+        primes = gen_primes(1000)
+        x = 0
+        for i, p in enumerate(primes.search(1)):
+            self.assertEqual(xor_indices(primes[:p]), x)
+            if i < 17:
+                self.assertEqual(lst[i], x)
+            x ^= p
+
     def test_large_random(self):
         n = 10_037
         for a in [urandom_2(n), frozenbitarray(urandom_2(n))]:
@@ -1262,25 +1372,25 @@ class IntervalsTests(unittest.TestCase,
                 v = not v
             self.assertEqual(a, b)
 
-# ---------------------------------------------------------------------------
+# --------------------------  ba2hex()  hex2ba()  ---------------------------
 
 class HexlifyTests(unittest.TestCase, Util):
 
-    def test_ba2hex(self):
-        self.assertEqual(ba2hex(bitarray(0, 'big')), '')
-        self.assertEqual(ba2hex(bitarray('1110', 'big')), 'e')
-        self.assertEqual(ba2hex(bitarray('1110', 'little')), '7')
-        self.assertEqual(ba2hex(bitarray('0000 0001', 'big')), '01')
-        self.assertEqual(ba2hex(bitarray('1000 0000', 'big')), '80')
-        self.assertEqual(ba2hex(bitarray('0000 0001', 'little')), '08')
-        self.assertEqual(ba2hex(bitarray('1000 0000', 'little')), '10')
-        self.assertEqual(ba2hex(frozenbitarray('1100 0111', 'big')), 'c7')
-        # length not multiple of 4
-        self.assertRaises(ValueError, ba2hex, bitarray('10'))
-        self.assertRaises(TypeError, ba2hex, '101')
-
-        c = ba2hex(bitarray('1101', 'big'))
-        self.assertEqual(type(c), str)
+    def test_explicit(self):
+        data = [ #                  little   big
+            ('',                    '',      ''),
+            ('1000',                '1',     '8'),
+            ('0101 0110',           'a6',    '56'),
+            ('0100 1001 1101',      '29b',   '49d'),
+            ('0000 1100 1110 1111', '037f',  '0cef'),
+        ]
+        for bs, hex_le, hex_be in data:
+            a_be = bitarray(bs, 'big')
+            a_le = bitarray(bs, 'little')
+            self.assertEQUAL(hex2ba(hex_be, 'big'), a_be)
+            self.assertEQUAL(hex2ba(hex_le, 'little'), a_le)
+            self.assertEqual(ba2hex(a_be), hex_be)
+            self.assertEqual(ba2hex(a_le), hex_le)
 
     def test_ba2hex_group(self):
         a = bitarray('1000 0000 0101 1111', 'little')
@@ -1294,34 +1404,25 @@ class HexlifyTests(unittest.TestCase, Ut
         self.assertEqual(ba2hex(a, 3, sep=", "), "10a, f")
 
     def test_ba2hex_errors(self):
+        self.assertRaises(TypeError, ba2hex)
+        self.assertRaises(TypeError, ba2hex, None)
+        self.assertRaises(TypeError, ba2hex, '101')
+
+        # length not multiple of 4
+        self.assertRaises(ValueError, ba2hex, bitarray('10'))
+
         a = bitarray('1000 0000 0101 1111', 'little')
         self.assertRaises(ValueError, ba2hex, a, -1)
         self.assertRaises(ValueError, ba2hex, a, group=-1)
+        # sep not str
         self.assertRaises(TypeError, ba2hex, a, 1, b" ")
         # embedded null character in sep
-        self.assertRaises(ValueError, ba2hex, a, 2, "\0")
-
-    def test_hex2ba(self):
-        _set_default_endian('big')
-        self.assertEqual(hex2ba(''), bitarray())
-        for c in 'e', 'E', b'e', b'E', bytearray(b'e'), bytearray(b'E'):
-            a = hex2ba(c)
-            self.assertEqual(a.to01(), '1110')
-            self.assertEqual(a.endian, 'big')
-            self.assertEqual(type(a), bitarray)
-        self.assertEQUAL(hex2ba('01'), bitarray('0000 0001', 'big'))
-        self.assertEQUAL(hex2ba('08', 'little'),
-                         bitarray('0000 0001', 'little'))
-        self.assertEQUAL(hex2ba('aD'), bitarray('1010 1101', 'big'))
-        self.assertEQUAL(hex2ba('10aF'),
-                         bitarray('0001 0000 1010 1111', 'big'))
-        self.assertEQUAL(hex2ba(b'10 aF', 'little'),
-                         bitarray('1000 0000 0101 1111', 'little'))
+        self.assertRaises(ValueError, ba2hex, a, 2, " \0")
 
     def test_hex2ba_whitespace(self):
         _set_default_endian('big')
-        self.assertEqual(hex2ba("F1 F2 %s f3 c0" % WHITESPACE),
-                         bitarray("11110001 11110010 11110011 11000000"))
+        self.assertEqual(hex2ba("F1 FA %s f3 c0" % whitespace),
+                         bitarray("11110001 11111010 11110011 11000000"))
         self.assertEQUAL(hex2ba(b' a F ', 'big'),
                          bitarray('1010 1111', 'big'))
         self.assertEQUAL(hex2ba(860 * " " + '0  1D' + 590 * " ", 'little'),
@@ -1329,6 +1430,8 @@ class HexlifyTests(unittest.TestCase, Ut
 
     def test_hex2ba_errors(self):
         self.assertRaises(TypeError, hex2ba, 0)
+        self.assertRaises(TypeError, hex2ba, "F", 1)
+        self.assertRaises(ValueError, hex2ba, "F", "foo")
 
         for s in '01a7g89', '0\u20ac', '0 \0', b'\x00':
             self.assertRaises(ValueError, hex2ba, s)
@@ -1337,44 +1440,35 @@ class HexlifyTests(unittest.TestCase, Ut
             msg = "invalid digit found for base16, got 'g' (0x67)"
             self.assertRaisesMessage(ValueError, msg, hex2ba, s, 'big')
 
-    def test_explicit(self):
-        data = [ #                       little   big
-            ('',                         '',      ''),
-            ('1000',                     '1',     '8'),
-            ('1000 1100',                '13',    '8c'),
-            ('1000 1100 1110',           '137',   '8ce'),
-            ('1000 1100 1110 1111' ,     '137f',  '8cef'),
-            ('1000 1100 1110 1111 0100', '137f2', '8cef4'),
-        ]
-        for bs, hex_le, hex_be in data:
-            a_be = bitarray(bs, 'big')
-            a_le = bitarray(bs, 'little')
-            self.assertEQUAL(hex2ba(hex_be, 'big'), a_be)
-            self.assertEQUAL(hex2ba(hex_le, 'little'), a_le)
-            self.assertEqual(ba2hex(a_be), hex_be)
-            self.assertEqual(ba2hex(a_le), hex_le)
+    def test_hex2ba_types(self):
+        for c in 'e', 'E', b'e', b'E', bytearray(b'e'), bytearray(b'E'):
+            a = hex2ba(c, "big")
+            self.assertEqual(a.to01(), '1110')
+            self.assertEqual(a.endian, 'big')
+            self.assertEqual(type(a), bitarray)
 
     def test_random(self):
         for _ in range(100):
-            a = urandom_2(4 * randrange(100))
-            s = ba2hex(a, group=randrange(10), sep=randrange(5) * " ")
-            b = hex2ba(s, endian=a.endian)
-            self.assertEQUAL(a, b)
+            default_endian = self.random_endian()
+            _set_default_endian(default_endian)
+            endian = choice(["little", "big", None])
+            a = urandom_2(4 * randrange(100), endian)
+            s = ba2hex(a, group=randrange(10), sep=choice(whitespace))
+            b = hex2ba(s, endian)
+            self.assertEqual(b.endian, endian or default_endian)
+            self.assertEqual(a, b)
             self.check_obj(b)
 
     def test_hexdigits(self):
-        for default_endian in 'big', 'little':
-            _set_default_endian(default_endian)
-            a = hex2ba(hexdigits)
-            self.assertEqual(len(a) % 4, 0)
-            self.assertEqual(a.endian, default_endian)
-            self.assertEqual(type(a), bitarray)
-            self.check_obj(a)
-
-            t = ba2hex(a)
-            self.assertEqual(t, hexdigits.lower())
-            self.assertEqual(type(t), str)
-            self.assertEQUAL(a, hex2ba(t, default_endian))
+        a = hex2ba(hexdigits)
+        self.assertEqual(len(a), 4 * len(hexdigits))
+        self.assertEqual(type(a), bitarray)
+        self.check_obj(a)
+
+        t = ba2hex(a)
+        self.assertEqual(t, hexdigits.lower())
+        self.assertEqual(type(t), str)
+        self.assertEQUAL(a, hex2ba(t))
 
     def test_binascii(self):
         a = urandom(80, 'big')
@@ -1383,21 +1477,32 @@ class HexlifyTests(unittest.TestCase, Ut
         b = bitarray(binascii.unhexlify(s), endian='big')
         self.assertEQUAL(hex2ba(s, 'big'), b)
 
-# ---------------------------------------------------------------------------
+# --------------------------  ba2base()  base2ba()  -------------------------
 
 class BaseTests(unittest.TestCase, Util):
 
-    def test_ba2base(self):
-        s = ba2base(16, bitarray('1101 0100', 'big'))
-        self.assertEqual(type(s), str)
-        self.assertEqual(s, 'd4')
+    def test_explicit(self):
+        data = [ #              n  little   big
+            ('',                2, '',      ''),
+            ('1 0 1',           2, '101',   '101'),
+            ('11 01 00',        4, '320',   '310'),
+            ('111 001',         8, '74',    '71'),
+            ('1111 0001',      16, 'f8',    'f1'),
+            ('11111 00001',    32, '7Q',    '7B'),
+            ('111111 000001',  64, '/g',    '/B'),
+        ]
+        for bs, n, s_le, s_be in data:
+            a_le = bitarray(bs, 'little')
+            a_be = bitarray(bs, 'big')
+            self.assertEQUAL(base2ba(n, s_le, 'little'), a_le)
+            self.assertEQUAL(base2ba(n, s_be, 'big'),    a_be)
+            self.assertEqual(ba2base(n, a_le), s_le)
+            self.assertEqual(ba2base(n, a_be), s_be)
 
-    def test_base2ba(self):
-        _set_default_endian('big')
-        for c in 'e', 'E', b'e', b'E':
-            a = base2ba(16, c)
-            self.assertEqual(a.to01(), '1110')
-            self.assertEqual(a.endian, 'big')
+    def test_base2ba_types(self):
+        for c in '7', b'7', bytearray(b'7'):
+            a = base2ba(32, c)
+            self.assertEqual(a.to01(), '11111')
             self.assertEqual(type(a), bitarray)
 
     def test_base2ba_whitespace(self):
@@ -1406,12 +1511,12 @@ class BaseTests(unittest.TestCase, Util)
         self.assertEqual(base2ba(32, "7 A"), bitarray("11111 00000"))
         self.assertEqual(base2ba(64, b"A /"), bitarray("000000 111111"))
         for n in 2, 4, 8, 16, 32, 64:
-            a = base2ba(n, WHITESPACE)
+            a = base2ba(n, whitespace)
             self.assertEqual(a, bitarray())
             a = urandom(60)
             c = list(ba2base(n, a))
             for _ in range(randrange(80)):
-                c.insert(randint(0, len(c)), choice(WHITESPACE))
+                c.insert(randint(0, len(c)), choice(whitespace))
             s = ''.join(c)
             self.assertEqual(base2ba(n, s), a)
 
@@ -1435,24 +1540,6 @@ class BaseTests(unittest.TestCase, Util)
             self.assertEqual(type(s), str)
             self.assertEqual(s, res)
 
-    def test_explicit(self):
-        data = [ #              n  little   big
-            ('',                2, '',      ''),
-            ('1 0 1',           2, '101',   '101'),
-            ('11 01 00',        4, '320',   '310'),
-            ('111 001',         8, '74',    '71'),
-            ('1111 0001',      16, 'f8',    'f1'),
-            ('11111 00001',    32, '7Q',    '7B'),
-            ('111111 000001',  64, '/g',    '/B'),
-        ]
-        for bs, n, s_le, s_be in data:
-            a_le = bitarray(bs, 'little')
-            a_be = bitarray(bs, 'big')
-            self.assertEQUAL(base2ba(n, s_le, 'little'), a_le)
-            self.assertEQUAL(base2ba(n, s_be, 'big'),    a_be)
-            self.assertEqual(ba2base(n, a_le), s_le)
-            self.assertEqual(ba2base(n, a_be), s_be)
-
     def test_empty(self):
         for n in 2, 4, 8, 16, 32, 64:
             a = base2ba(n, '')
@@ -1478,34 +1565,22 @@ class BaseTests(unittest.TestCase, Util)
         self.assertRaises(TypeError, base2ba, None, '')
         self.assertRaises(TypeError, ba2base, 16.0, a)
         self.assertRaises(TypeError, base2ba, 16.0, '')
-        for i in range(-10, 100):
-            if i in (2, 4, 8, 16, 32, 64):
-                continue
-            self.assertRaises(ValueError, ba2base, i, a)
-            self.assertRaises(ValueError, base2ba, i, '')
-
         self.assertRaises(TypeError, ba2base, 32, None)
         self.assertRaises(TypeError, base2ba, 32, None)
 
-    def test_binary(self):
-        a = base2ba(2, '1011')
-        self.assertEqual(a, bitarray('1011'))
-        self.assertEqual(ba2base(2, a), '1011')
-
-        for a in self.randombitarrays():
-            s = ba2base(2, a)
-            self.assertEqual(s, a.to01())
-            self.assertEQUAL(base2ba(2, s, a.endian), a)
-
-    def test_quaternary(self):
-        a = base2ba(4, '0123', 'big')
-        self.assertEqual(a, bitarray('00 01 10 11'))
-        self.assertEqual(ba2base(4, a), '0123')
-
-    def test_octal(self):
-        a = base2ba(8, '0147', 'big')
-        self.assertEqual(a, bitarray('000 001 100 111'))
-        self.assertEqual(ba2base(8, a), '0147')
+        for values, msg in [
+                ([-1023, -16, -1, 0, 3, 5, 31, 48, 63, 129, 511, 4123],
+                 "base must be a power of 2"),
+                ([1, 128, 256, 512, 1024, 2048, 4096, 8192],
+                 "base must be 2, 4, 8, 16, 32 or 64")]:
+            for i in values:
+                self.assertRaisesMessage(ValueError, msg, ba2base, i, a)
+                self.assertRaisesMessage(ValueError, msg, base2ba, i, '')
+
+        a = bitarray(29)
+        for m in range(2, 7):
+            msg = "bitarray length 29 not multiple of %d" % m
+            self.assertRaisesMessage(ValueError, msg, ba2base, 1 << m, a)
 
     def test_hexadecimal(self):
         a = base2ba(16, 'F61', 'big')
@@ -1520,26 +1595,46 @@ class BaseTests(unittest.TestCase, Util)
             self.assertEqual(ba2base(16, a), ba2hex(a))
 
     def test_base32(self):
-        a = base2ba(32, '7SH', 'big')
-        self.assertEqual(a, bitarray('11111 10010 00111'))
-        self.assertEqual(ba2base(32, a), '7SH')
-
         msg = os.urandom(randint(10, 100) * 5)
         s = base64.b32encode(msg).decode()
         a = base2ba(32, s, 'big')
         self.assertEqual(a.tobytes(), msg)
         self.assertEqual(ba2base(32, a), s)
+        self.assertEqual(base64.b32decode(s), msg)
 
     def test_base64(self):
-        a = base2ba(64, '/jH', 'big')
-        self.assertEqual(a, bitarray('111111 100011 000111'))
-        self.assertEqual(ba2base(64, a), '/jH')
-
         msg = os.urandom(randint(10, 100) * 3)
         s = base64.standard_b64encode(msg).decode()
         a = base2ba(64, s, 'big')
         self.assertEqual(a.tobytes(), msg)
         self.assertEqual(ba2base(64, a), s)
+        self.assertEqual(base64.standard_b64decode(s), msg)
+
+    def test_primes(self):
+        primes = gen_primes(60, odd=True)
+        base_2 = primes.to01()
+        for n, endian, rep in [
+                ( 2, "little", base_2),
+                ( 2, "big",    base_2),
+                ( 4, "little", "232132030132012122122010132110"),
+                ( 4, "big",    "131231030231021211211020231220"),
+                ( 8, "little", "65554155441515405550"),
+                ( 8, "big",    "35551455114545105550"),
+                (16, "little", "e6bc4b46a921d61"),
+                (16, "big",    "76d32d265948b68"),
+                (32, "little", "O3SJLSJTSI3C"),
+                (32, "big",    "O3JS2JSZJC3I"),
+                (64, "little", "utMtkppEtF"),
+                (64, "big",    "dtMtJllIto"),
+        ]:
+            a = bitarray(primes, endian)
+            s = ba2base(n, a)
+            self.assertEqual(type(s), str)
+            self.assertEqual(s, rep)
+            b = base2ba(n, rep, endian)
+            self.assertEqual(b, a)
+            self.assertEqual(type(b), bitarray)
+            self.assertEqual(b.endian, endian)
 
     alphabets = [
     #    m   n  alphabet
@@ -1547,6 +1642,7 @@ class BaseTests(unittest.TestCase, Util)
         (2,  4, '0123'),
         (3,  8, '01234567'),
         (4, 16, '0123456789abcdef'),
+        (4, 16, '0123456789ABCDEF'),
         (5, 32, 'ABCDEFGHIJKLMNOPQRSTUVWXYZ234567'),
         (6, 64, 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdef'
                 'ghijklmnopqrstuvwxyz0123456789+/'),
@@ -1559,6 +1655,8 @@ class BaseTests(unittest.TestCase, Util)
             for i, c in enumerate(alphabet):
                 endian = self.random_endian()
                 self.assertEqual(ba2int(base2ba(n, c, endian)), i)
+                if m == 4 and c in "ABCDEF":
+                    c = chr(ord(c) + 32)
                 self.assertEqual(ba2base(n, int2ba(i, m, endian)), c)
 
     def test_not_alphabets(self):
@@ -1567,7 +1665,7 @@ class BaseTests(unittest.TestCase, Util)
                 c = chr(i)
                 if c in alphabet or c.isspace():
                     continue
-                if n == 16 and c in "ABCDEF":
+                if n == 16 and c in hexdigits:
                     continue
                 self.assertRaises(ValueError, base2ba, n, c)
 
@@ -1575,9 +1673,12 @@ class BaseTests(unittest.TestCase, Util)
         for _ in range(100):
             m = randint(1, 6)
             a = urandom_2(m * randrange(100))
-            self.assertEqual(len(a) % m, 0)
             n = 1 << m
             s = ba2base(n, a, group=randrange(10), sep=randrange(5) * " ")
+            if m == 4 and getrandbits(1):
+                s = s.upper()
+            if getrandbits(1):
+                s = s.encode()
             b = base2ba(n, s, a.endian)
             self.assertEQUAL(a, b)
             self.check_obj(b)
@@ -2236,9 +2337,9 @@ class MixedTests(unittest.TestCase, Util
             self.assertEqual(s[:2], '0b')
             a = bitarray(s[2:], 'big')
             self.assertEqual(ba2int(a), i)
-            t = '0b%s' % a.to01()
-            self.assertEqual(t, s)
-            self.assertEqual(eval(t), i)
+            t = a.to01()
+            self.assertEqual(t, s[2:])
+            self.assertEqual(int(t, 2), i)
 
     def test_oct(self):
         for _ in range(20):
@@ -2247,9 +2348,9 @@ class MixedTests(unittest.TestCase, Util
             self.assertEqual(s[:2], '0o')
             a = base2ba(8, s[2:], 'big')
             self.assertEqual(ba2int(a), i)
-            t = '0o%s' % ba2base(8, a)
-            self.assertEqual(t, s)
-            self.assertEqual(eval(t), i)
+            t = ba2base(8, a)
+            self.assertEqual(t, s[2:])
+            self.assertEqual(int(t, 8), i)
 
     def test_hex(self):
         for _ in range(20):
@@ -2258,9 +2359,9 @@ class MixedTests(unittest.TestCase, Util
             self.assertEqual(s[:2], '0x')
             a = hex2ba(s[2:], 'big')
             self.assertEqual(ba2int(a), i)
-            t = '0x%s' % ba2hex(a)
-            self.assertEqual(t, s)
-            self.assertEqual(eval(t), i)
+            t = ba2hex(a)
+            self.assertEqual(t, s[2:])
+            self.assertEqual(int(t, 16), i)
 
     def test_bitwise(self):
         for a in self.randombitarrays(start=1):
@@ -2308,7 +2409,7 @@ class MixedTests(unittest.TestCase, Util
                 c <<= n
                 self.assertEqual(ba2int(c), i << n)
 
-# ---------------------------------------------------------------------------
+# ----------------------  serialize()  deserialize()  -----------------------
 
 class SerializationTests(unittest.TestCase, Util):
 
diff -pruN 3.6.1-1/bitarray/util.py 3.7.1-1/bitarray/util.py
--- 3.6.1-1/bitarray/util.py	2025-08-12 08:35:42.000000000 +0000
+++ 3.7.1-1/bitarray/util.py	2025-08-28 20:59:12.000000000 +0000
@@ -27,7 +27,7 @@ from bitarray._util import (
 )
 
 __all__ = [
-    'zeros', 'ones', 'urandom', 'random_k', 'random_p',
+    'zeros', 'ones', 'urandom', 'random_k', 'random_p', 'gen_primes',
     'pprint', 'strip', 'count_n',
     'parity', 'sum_indices', 'xor_indices',
     'count_and', 'count_or', 'count_xor', 'any_and', 'subset',
@@ -250,19 +250,61 @@ class _Random:
         return a
 
 
-def sum_indices(__a, __mode=1):
-    """sum_indices(a, /) -> int
+def gen_primes(__n, endian=None, odd=False):
+    """gen_primes(n, /, endian=None, odd=False) -> bitarray
+
+Generate a bitarray of length `n` in which active indices are prime numbers.
+By default (`odd=False`), active indices correspond to prime numbers directly.
+When `odd=True`, only odd prime numbers are represented in the resulting
+bitarray `a`, and `a[i]` corresponds to `2*i+1` being prime or not.
+"""
+    n = int(__n)
+    if n < 0:
+        raise ValueError("bitarray length must be >= 0")
+
+    if odd:
+        a = ones(105, endian)  # 105 = 3 * 5 * 7
+        a[1::3] = 0
+        a[2::5] = 0
+        a[3::7] = 0
+        f = "01110110"
+    else:
+        a = ones(210, endian)  # 210 = 2 * 3 * 5 * 7
+        for i in 2, 3, 5, 7:
+            a[::i] = 0
+        f = "00110101"
+
+    # repeating the array many times is faster than setting the multiples
+    # of the low primes to 0
+    a *= (n + len(a) - 1) // len(a)
+    a[:8] = bitarray(f, endian)
+    del a[n:]
+    # perform sieve starting at 11
+    if odd:
+        for i in a.search(1, 5, int(math.sqrt(n // 2) + 1.0)):  # 11//2 = 5
+            j = 2 * i + 1
+            a[(j * j) // 2 :: j] = 0
+    else:
+        # i*i is always odd, and even bits are already set to 0: use step 2*i
+        for i in a.search(1, 11, int(math.sqrt(n) + 1.0)):
+            a[i * i :: 2 * i] = 0
+    return a
+
+
+def sum_indices(__a, mode=1):
+    """sum_indices(a, /, mode=1) -> int
 
 Return sum of indices of all active bits in bitarray `a`.
 Equivalent to `sum(i for i, v in enumerate(a) if v)`.
+`mode=2` sums square of indices.
 """
-    if __mode not in (1, 2):
-        raise ValueError("unexpected mode %r" % __mode)
+    if mode not in (1, 2):
+        raise ValueError("unexpected mode %r" % mode)
 
     # For details see: devel/test_sum_indices.py
     n = 1 << 19  # block size  512 Kbits
     if len(__a) <= n:  # shortcut for single block
-        return _ssqi(__a, __mode)
+        return _ssqi(__a, mode)
 
     # Constants
     m = n // 8  # block size in bytes
@@ -285,7 +327,7 @@ Equivalent to `sum(i for i, v in enumera
         if k:
             y = n * i
             z1 = o1 if k == n else _ssqi(block)
-            if __mode == 1:
+            if mode == 1:
                 sm += k * y + z1
             else:
                 z2 = o2 if k == n else _ssqi(block, 2)
@@ -297,11 +339,9 @@ Equivalent to `sum(i for i, v in enumera
 def pprint(__a, stream=None, group=8, indent=4, width=80):
     """pprint(bitarray, /, stream=None, group=8, indent=4, width=80)
 
-Prints the formatted representation of object on `stream` (which defaults
-to `sys.stdout`).  By default, elements are grouped in bytes (8 elements),
-and 8 bytes (64 elements) per line.
-Non-bitarray objects are printed by the standard library
-function `pprint.pprint()`.
+Pretty-print bitarray object to `stream`, defaults is `sys.stdout`.
+By default, bits are grouped in bytes (8 bits), and 64 bits per line.
+Non-bitarray objects are printed using `pprint.pprint()`.
 """
     if stream is None:
         stream = sys.stdout
diff -pruN 3.6.1-1/bitarray/util.pyi 3.7.1-1/bitarray/util.pyi
--- 3.6.1-1/bitarray/util.pyi	2025-08-12 08:35:42.000000000 +0000
+++ 3.7.1-1/bitarray/util.pyi	2025-08-28 20:59:12.000000000 +0000
@@ -20,6 +20,9 @@ def random_p(n: int,
 def random_k(n: int,
              k: int,
              endian: Optional[str] = ...) -> bitarray: ...
+def gen_primes(n: int,
+               endian: Optional[str] = ...,
+               odd: Optional[bool] = ...) -> bitarray: ...
 
 def pprint(a: Any, stream: BinaryIO = ...,
            group: int = ...,
@@ -33,7 +36,7 @@ def count_n(a: bitarray,
             value: int = ...) -> int: ...
 
 def parity(a: bitarray) -> int: ...
-def sum_indices(a: bitarray) -> int: ...
+def sum_indices(a: bitarray, mode: int = ...) -> int: ...
 def xor_indices(a: bitarray) -> int: ...
 def count_and(a: bitarray, b: bitarray) -> int: ...
 def count_or(a: bitarray, b: bitarray) -> int: ...
diff -pruN 3.6.1-1/debian/changelog 3.7.1-1/debian/changelog
--- 3.6.1-1/debian/changelog	2025-08-17 00:02:06.000000000 +0000
+++ 3.7.1-1/debian/changelog	2025-08-31 15:08:12.000000000 +0000
@@ -1,3 +1,10 @@
+python-bitarray (3.7.1-1) unstable; urgency=medium
+
+  * Team upload.
+  * New upstream release.
+
+ -- Colin Watson <cjwatson@debian.org>  Sun, 31 Aug 2025 16:08:12 +0100
+
 python-bitarray (3.6.1-1) unstable; urgency=medium
 
   * Team upload.
diff -pruN 3.6.1-1/devel/random/binomial.py 3.7.1-1/devel/random/binomial.py
--- 3.6.1-1/devel/random/binomial.py	2025-08-12 08:35:42.000000000 +0000
+++ 3.7.1-1/devel/random/binomial.py	2025-08-28 20:59:12.000000000 +0000
@@ -60,10 +60,7 @@ class BinomialDistTests(unittest.TestCas
     def test_pmf_sum(self):
         for n in 10, 100, 1_000, 10_000:
             b = BinomialDist(n, 0.5)
-            tot = 0
-            for k in range(n + 1):
-                tot += b.pmf(k)
-            self.assertAlmostEqual(tot, 1.0)
+            self.assertAlmostEqual(sum(b.pmf(k) for k in range(n + 1)), 1.0)
 
     def test_pmf(self):
         for n in 10, 50, 100, 250:
diff -pruN 3.6.1-1/devel/random/sample.py 3.7.1-1/devel/random/sample.py
--- 3.6.1-1/devel/random/sample.py	2025-08-12 08:35:42.000000000 +0000
+++ 3.7.1-1/devel/random/sample.py	2025-08-28 20:59:12.000000000 +0000
@@ -75,11 +75,8 @@ class SampleMeanDistTests(unittest.TestC
         smd = SampleMeanDist(n, k)
         N = 1000
         dx = n / N
-        p = 0.0
-        for i in range(N + 1):
-            x = i * dx
-            p += smd.pdf(x) * dx
-        self.assertAlmostEqual(p, 1.0)
+        self.assertAlmostEqual(
+            sum(smd.pdf(i * dx) * dx for i in range(N + 1)), 1.0)
 
     def test_cdf(self):
         smd = SampleMeanDist(100, 30)
diff -pruN 3.6.1-1/devel/resize/test_resize.py 3.7.1-1/devel/resize/test_resize.py
--- 3.6.1-1/devel/resize/test_resize.py	2025-08-12 08:35:42.000000000 +0000
+++ 3.7.1-1/devel/resize/test_resize.py	2025-08-28 20:59:12.000000000 +0000
@@ -5,7 +5,8 @@ from bitarray import bitarray
 PATTERN = [0, 1, 4, 8, 16, 24, 32, 40, 48, 56, 64, 76, 88, 100, 112, 124, 136]
 
 def get_alloc(a):
-    return a.buffer_info()[4]
+    info = a.buffer_info()
+    return info.alloc
 
 def resize(a, n):
     increase = n - len(a)
@@ -16,9 +17,7 @@ def resize(a, n):
 
 def show(a):
     info = a.buffer_info()
-    size = info[1]
-    alloc = info[4]
-    print('%d  %d' % (size, alloc))
+    print('%d  %d' % (info.nbytes, info.alloc))
 
 
 class ResizeTests(unittest.TestCase):
@@ -52,7 +51,7 @@ class ResizeTests(unittest.TestCase):
         prev = get_alloc(a)
         while a:
             del a[-100_000:]
-            alloc = a.buffer_info()[4]
+            alloc = get_alloc(a)
             self.assertTrue(alloc <= prev)
             prev = alloc
 
diff -pruN 3.6.1-1/devel/test_debug.py 3.7.1-1/devel/test_debug.py
--- 3.6.1-1/devel/test_debug.py	2025-08-12 08:35:42.000000000 +0000
+++ 3.7.1-1/devel/test_debug.py	2025-08-28 20:59:12.000000000 +0000
@@ -3,7 +3,7 @@ import sys
 import unittest
 from random import randint, randrange
 
-from bitarray import bitarray
+from bitarray import bitarray, _sysinfo
 from bitarray.util import zeros, ones, int2ba, parity
 
 from bitarray.test_bitarray import Util, urandom_2, skipIf, PTRSIZE
@@ -12,7 +12,7 @@ from bitarray.test_bitarray import Util,
 
 from bitarray._util import (
     _setup_table, _zlw,                          # defined in bitarray.h
-    _cfw, _read_n, _write_n, _sc_rts, _SEGSIZE,  # defined in _util.h
+    _cfw, _d2i, _read_n, _write_n, _sc_rts, _SEGSIZE,       # _util.h
 )
 SEGBITS = 8 * _SEGSIZE
 
@@ -104,9 +104,9 @@ class SetupTableTests(unittest.TestCase)
             self.assertEqual(table[j], i)
             self.assertEqual(int2ba(i, 8, 'little'), int2ba(j, 8, 'big'))
 
-    def test_endian(self):
+    def test_opposite_endian(self):
         reverse_trans = _setup_table('r')
-        for kop1, kop2 in 'aA', 'xX':
+        for kop1, kop2 in 'aA', 'xX', 'sS':
             a = _setup_table(kop1)
             b = _setup_table(kop2)
             for i in range(256):
@@ -140,6 +140,12 @@ class ZLW_Tests(unittest.TestCase, Util)
 
 # ----------------------------  _bitarray.c  --------------------------------
 
+class SysInfo_Tests(unittest.TestCase):
+
+    def test_debug(self):
+        self.assertTrue(_sysinfo("DEBUG"))
+
+
 class ShiftR8_Tests(unittest.TestCase, Util):
 
     def test_empty(self):
@@ -345,6 +351,35 @@ class CountFromWord_Tests(unittest.TestC
             self.assertEqual(res, a[64 * i:].count())
 
 
+class DigitToInt_Tests(unittest.TestCase):
+
+    # digit_to_int()
+
+    alphabets = [
+        (1, b'01'),
+        (2, b'0123'),
+        (3, b'01234567'),
+        (4, b'0123456789abcdef'),
+        (4, b'0123456789ABCDEF'),
+        (5, b'ABCDEFGHIJKLMNOPQRSTUVWXYZ234567'),
+        (6, b'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdef'
+            b'ghijklmnopqrstuvwxyz0123456789+/'),
+    ]
+
+    def test_alphabets(self):
+        for m, alphabet in self.alphabets:
+            self.assertEqual(len(alphabet), 1 << m)
+            for i, c in enumerate(alphabet):
+                self.assertEqual(_d2i(m, bytes([c])), i)
+
+    def test_not_alphabets(self):
+        for m, alphabet in self.alphabets:
+            for c in range(256):
+                if c in alphabet or (m == 4 and c in b'abcdefABCDEF'):
+                    continue
+                self.assertEqual(_d2i(m, bytes([c])), -1)
+
+
 class RTS_Tests(unittest.TestCase):
 
     # _sc_rts()   (running totals debug test)
diff -pruN 3.6.1-1/devel/test_sum_indices.py 3.7.1-1/devel/test_sum_indices.py
--- 3.6.1-1/devel/test_sum_indices.py	2025-08-12 08:35:42.000000000 +0000
+++ 3.7.1-1/devel/test_sum_indices.py	2025-08-28 20:59:12.000000000 +0000
@@ -37,10 +37,11 @@ k                  count_table[c]
 z1 = sum z_j       sum_table[c]         _ssqi(block)
 z2 = sum z_j**2    sum_sqr_table[c]     _ssqi(block, 2)
 """
+import math
 import unittest
-from random import getrandbits, randint, randrange, sample
+from random import choice, getrandbits, randint, randrange, sample
 
-from bitarray.util import zeros, ones, urandom, _ssqi, sum_indices
+from bitarray.util import zeros, ones, gen_primes, urandom, _ssqi, sum_indices
 from bitarray.test_util import SumIndicesUtil
 
 
@@ -171,6 +172,13 @@ class SSQI_Tests(SumIndicesUtil):
             self.assertTrue(f(len(a)) > MAX_UINT64)
             self.assertRaises(OverflowError, _ssqi, a, mode)
 
+    def test_primes(self):
+        n = 3_800_000
+        endian = choice(['little', 'big'])
+        a = gen_primes(n, endian)
+        self.assertEqual(_ssqi(a, 1),           493_187_952_850)
+        self.assertEqual(_ssqi(a, 2), 1_234_421_634_142_352_974)
+
     def test_sparse(self):
         for _  in range(500):
             n = randint(2, 3_810_778)
@@ -194,6 +202,13 @@ class SumIndicesTests(SumIndicesUtil):
             inv = getrandbits(1)
             self.check_sparse(sum_indices, n, k, mode, freeze, inv)
 
+    def test_primes(self):
+        n = 10_000_000
+        endian = choice(['little', 'big'])
+        a = gen_primes(n, endian)
+        self.assertEqual(sum_indices(a, 1),          3_203_324_994_356)
+        self.assertEqual(sum_indices(a, 2), 21_113_978_675_102_768_574)
+
     def test_ones(self):
         for m in range(19, 32):
             n = randrange(1 << m)
@@ -210,6 +225,11 @@ class SumIndicesTests(SumIndicesUtil):
             inv = getrandbits(1)
             self.check_sparse(sum_indices, n, k, mode, freeze, inv)
 
+    def test_hypot(self):
+        a = urandom(10_000)
+        self.assertAlmostEqual(math.sqrt(sum_indices(a, 2)),
+                               math.hypot(*list(a.search(1))))
+
 
 class VarianceTests(unittest.TestCase):
 
diff -pruN 3.6.1-1/doc/buffer.rst 3.7.1-1/doc/buffer.rst
--- 3.6.1-1/doc/buffer.rst	2025-08-12 08:35:42.000000000 +0000
+++ 3.7.1-1/doc/buffer.rst	2025-08-28 20:59:12.000000000 +0000
@@ -88,11 +88,14 @@ The bytearray ``c`` is now exporting its
 to big-endian bitarray ``a``, and a little-endian bitarray ``b``.
 At this point all three object ``a``, ``b`` and ``c`` share the same buffer.
 Using the ``.buffer_info()`` method, we can actually verify that the
-bitarrays ``a`` and ``b`` point to the same buffer address:
+bitarrays ``a`` and ``b`` point to the same address:
 
 .. code-block:: python
 
-    >>> assert a.buffer_info()[0] == b.buffer_info()[0]
+    >>> def address(a):
+    ...     info = a.buffer_info()
+    ...     return info[0]  # using bitarray 3.7, we can also: info.address
+    >>> assert address(a) == address(b)
 
 As bitarray's expose their buffer, we can also directly create a bitarray
 which imports the buffer from another bitarray:
@@ -102,7 +105,7 @@ which imports the buffer from another bi
     >>> a = bitarray(32)
     >>> b = bitarray(buffer=a)
     >>> # the buffer address is the same
-    >>> assert a.buffer_info()[0] == b.buffer_info()[0]
+    >>> assert address(a) == address(b)
     >>> a.setall(0)
     >>> assert a == b
     >>> b[::7] = 1
@@ -119,9 +122,9 @@ of ``a``'s buffer:
     >>> a = bitarray(1 << 23)
     >>> a.setall(0)
     >>> b = bitarray(buffer=memoryview(a)[0x10000:0x30000])
-    >>> assert a.buffer_info()[0] + 0x10000 == b.buffer_info()[0]
+    >>> assert address(a) + 0x10000 == address(b)
     >>> c = bitarray(buffer=memoryview(a)[0x20000:0x50000])
-    >>> assert a.buffer_info()[0] + 0x20000 == c.buffer_info()[0]
+    >>> assert address(a) + 0x20000 == address(c)
     >>> c[0] = 1
     >>> assert b[8 * 0x10000] == 1
     >>> assert a[8 * 0x20000] == 1
diff -pruN 3.6.1-1/doc/changelog.rst 3.7.1-1/doc/changelog.rst
--- 3.6.1-1/doc/changelog.rst	2025-08-12 08:35:42.000000000 +0000
+++ 3.7.1-1/doc/changelog.rst	2025-08-28 20:59:12.000000000 +0000
@@ -1,6 +1,26 @@
 Change log
 ==========
 
+**3.7.1** (2025-08-28):
+
+* fix type hinting for memoryviews, see `#241 <https://github.com/ilanschnell/bitarray/issues/241>`__
+* add `bit-endianness <endianness.rst>`__ documentation
+* improve testing, including debug mode test for ``digit_to_int()``
+
+
+**3.7.0** (2025-08-24):
+
+* add ``util.gen_primes()``, generate bitarrays in which active indices are
+  prime numbers
+* improve ``.buffer_info()`` to return named tuple
+* add optional ``mode`` argument to ``util.sum_indices()`` to sum square of
+  active indices
+* improve internal ``_sysinfo()`` to include ``Py_DEBUG``
+* add `Dubner's conjecture <../examples/dubner.rst>`__ (in memory of Harvey
+  Dubner)
+* add `dynamically growing sieve <../examples/dyn_sieve.py>`__
+
+
 **3.6.1** (2025-08-12):
 
 * add development files for statistical tests in ``devel/random/``
diff -pruN 3.6.1-1/doc/endianness.rst 3.7.1-1/doc/endianness.rst
--- 3.6.1-1/doc/endianness.rst	1970-01-01 00:00:00.000000000 +0000
+++ 3.7.1-1/doc/endianness.rst	2025-08-28 20:59:12.000000000 +0000
@@ -0,0 +1,145 @@
+Bit-endianness
+==============
+
+Unless explicitly converting to machine representation, i.e. initializing
+the buffer directly, using ``.tobytes()``, ``.frombytes()``, ``.tofile()``
+or ``.fromfile()``, as well as using ``memoryview()``, the bit-endianness
+will have no effect on any computation, and one can skip this section.
+
+Since bitarrays allows addressing individual bits, where the machine
+represents 8 bits in one byte, there are two obvious choices for this
+mapping: little-endian and big-endian.
+
+When dealing with the machine representation of bitarray objects, it is
+recommended to always explicitly specify the endianness.
+
+By default, bitarrays use big-endian representation:
+
+.. code-block:: python
+
+    >>> from bitarray import bitarray
+    >>> a = bitarray(b'A')
+    >>> a.endian
+    'big'
+    >>> a
+    bitarray('01000001')
+    >>> a[6] = 1
+    >>> a.tobytes()
+    b'C'
+
+Big-endian means that the most-significant bit comes first.
+Here, ``a[0]`` is the lowest address (index) and most significant bit,
+and ``a[7]`` is the highest address and least significant bit.
+
+When creating a new bitarray object, the endianness can always be
+specified explicitly:
+
+.. code-block:: python
+
+    >>> a = bitarray(b'A', endian='little')
+    >>> a
+    bitarray('10000010')
+    >>> a.endian
+    'little'
+
+Here, the low-bit comes first because little-endian means that increasing
+numeric significance corresponds to an increasing address.
+So ``a[0]`` is the lowest address and least significant bit,
+and ``a[7]`` is the highest address and most significant bit.
+
+The bit-endianness is a property of the bitarray object.
+The endianness cannot be changed once a bitarray object has been created.
+When comparing bitarray objects, the endianness (and hence the machine
+representation) is irrelevant; what matters is the mapping from indices
+to bits:
+
+.. code-block:: python
+
+    >>> bitarray('11001', endian='big') == bitarray('11001', endian='little')
+    True
+    >>> a = bitarray(b'\x01', endian='little')
+    >>> b = bitarray(b'\x80', endian='big')
+    >>> a == b
+    True
+    >>> a.tobytes() == b.tobytes()
+    False
+
+Bitwise operations (``|``, ``^``, ``&=``, ``|=``, ``^=``, ``~``) are
+implemented efficiently using the corresponding byte operations in C, i.e. the
+operators act on the machine representation of the bitarray objects.
+Therefore, it is not possible to perform bitwise operators on bitarrays
+with different endianness.
+
+As mentioned above, the endianness can not be changed once an object is
+created.  However, you can create a new bitarray with different endianness:
+
+.. code-block:: python
+
+    >>> a = bitarray('111000', endian='little')
+    >>> b = bitarray(a, endian='big')
+    >>> b
+    bitarray('111000')
+    >>> a == b
+    True
+
+
+Utility functions
+-----------------
+
+A number of utility functions take into the bit-endianness into account.
+For example consider:
+
+.. code-block:: python
+
+    >>> from bitarray.util import ba2int, int2ba
+    >>> int2ba(12)
+    bitarray('1100')
+
+This is what one would normally expect, as Python's built-in ``bin()`` gives
+the same result:
+
+.. code-block:: python
+
+    >>> bin(12)
+    '0b1100'
+
+However, this is only true because big-endian is the default bit-endianness.
+When explicitly requesting a little-endian bitarray, we get:
+
+.. code-block:: python
+
+    >>> int2ba(12, endian="little")
+    bitarray('0011')
+
+Similarly, the function ``ba2int()`` takes into account the bit-endianness of
+the bitarray it is provided with:
+
+.. code-block:: python
+
+    >>> a = bitarray("11001", "little")
+    >>> ba2int(a)
+    19
+    >>> ba2int(bitarray(a, "big"))
+    25
+
+The same behavior is valid for ``hex2ba()``, ``ba2hex()``, ``base2ba()``
+and ``ba2base()``.  Regardless of bit-endianness, these are always
+inverse functions of each other:
+
+.. code-block:: python
+
+    >>> from bitarray.util import ba2hex, hex2ba, ba2base, base2ba
+    >>> for endian in "little", "big":
+    ...     a = bitarray("1010 0011 1110", endian)
+    ...     assert int2ba(ba2int(a), len(a), a.endian) == a
+    ...     assert hex2ba(ba2hex(a), a.endian) == a
+    ...     assert base2ba(64, ba2base(64, a), a.endian) == a
+
+or:
+
+.. code-block:: python
+
+    >>> for endian in "little", "big":
+    ...     assert ba2int(int2ba(29, endian=endian)) == 29
+    ...     assert ba2hex(hex2ba("e7a", endian)) == "e7a"
+    ...     assert ba2base(64, base2ba(64, "h+E7", endian)) == "h+E7"
diff -pruN 3.6.1-1/doc/reference.rst 3.7.1-1/doc/reference.rst
--- 3.6.1-1/doc/reference.rst	2025-08-12 08:35:42.000000000 +0000
+++ 3.7.1-1/doc/reference.rst	2025-08-28 20:59:12.000000000 +0000
@@ -1,7 +1,7 @@
 Reference
 =========
 
-bitarray version: 3.6.1 -- `change log <https://github.com/ilanschnell/bitarray/blob/master/doc/changelog.rst>`__
+bitarray version: 3.7.1 -- `change log <https://github.com/ilanschnell/bitarray/blob/master/doc/changelog.rst>`__
 
 In the following, ``item`` and ``value`` are usually a single bit -
 an integer 0 or 1.
@@ -40,34 +40,36 @@ bitarray methods:
 -----------------
 
 ``all()`` -> bool
-   Return True when all bits in bitarray are True.
-   Note that ``a.all()`` is faster than ``all(a)``.
+   Return ``True`` when all bits in bitarray are 1.
+   ``a.all()`` is a faster version of ``all(a)``.
 
 
 ``any()`` -> bool
-   Return True when any bit in bitarray is True.
-   Note that ``a.any()`` is faster than ``any(a)``.
+   Return ``True`` when any bit in bitarray is 1.
+   ``a.any()`` is a faster version of ``any(a)``.
 
 
 ``append(item, /)``
    Append ``item`` to the end of the bitarray.
 
 
-``buffer_info()`` -> tuple
-   Return a tuple containing:
+``buffer_info()`` -> BufferInfo
+   Return named tuple with following fields:
 
-   0. memory address of buffer
-   1. buffer size (in bytes)
-   2. bit-endianness as a Unicode string
-   3. number of pad bits
-   4. allocated memory for the buffer (in bytes)
-   5. memory is read-only
-   6. buffer is imported
-   7. number of buffer exports
+   0. ``address``: memory address of buffer
+   1. ``nbytes``: buffer size (in bytes)
+   2. ``endian``: bit-endianness as a string
+   3. ``padbits``: number of pad bits
+   4. ``alloc``: allocated memory for buffer (in bytes)
+   5. ``readonly``: memory is read-only (bool)
+   6. ``imported``: buffer is imported (bool)
+   7. ``exports``: number of buffer exports
+
+   New in version 3.7: return named tuple
 
 
 ``bytereverse(start=0, stop=<end of buffer>, /)``
-   For each byte in byte-range(start, stop) reverse bits in-place.
+   For each byte in byte-range(``start``, ``stop``) reverse bits in-place.
    The start and stop indices are given in terms of bytes (not bits).
    Also note that this method only changes the buffer; it does not change the
    bit-endianness of the bitarray object.  Pad bits are left unchanged such
@@ -77,13 +79,13 @@ bitarray methods:
 
 
 ``clear()``
-   Remove all items from the bitarray.
+   Remove all items from bitarray.
 
    New in version 1.4
 
 
 ``copy()`` -> bitarray
-   Return a copy of the bitarray.
+   Return copy of bitarray (with same bit-endianness).
 
 
 ``count(value=1, start=0, stop=<end>, step=1, /)`` -> int
@@ -119,7 +121,7 @@ bitarray methods:
 
 ``extend(iterable, /)``
    Append items from to the end of the bitarray.
-   If ``iterable`` is a Unicode string, each ``0`` and ``1`` are appended as
+   If ``iterable`` is a (Unicode) string, each ``0`` and ``1`` are appended as
    bits (ignoring whitespace and underscore).
 
    New in version 3.4: allow ``bytes`` object
@@ -159,7 +161,7 @@ bitarray methods:
 ``index(sub_bitarray, start=0, stop=<end>, /, right=False)`` -> int
    Return lowest (or rightmost when ``right=True``) index where sub_bitarray
    is found, such that sub_bitarray is contained within ``[start:stop]``.
-   Raises ``ValueError`` when the sub_bitarray is not present.
+   Raises ``ValueError`` when sub_bitarray is not present.
 
    New in version 2.9: add optional keyword argument ``right``
 
@@ -170,7 +172,7 @@ bitarray methods:
 
 ``invert(index=<all bits>, /)``
    Invert all bits in bitarray (in-place).
-   When the optional ``index`` is given, only invert the single bit at index.
+   When the optional ``index`` is given, only invert the single bit at ``index``.
 
    New in version 1.5.3: optional index argument
 
@@ -225,7 +227,7 @@ bitarray methods:
 
 
 ``to01(group=0, sep=' ')`` -> str
-   Return bitarray as Unicode string of '0's and '1's.
+   Return bitarray as (Unicode) string of ``0``s and ``1``s.
    The bits are grouped into ``group`` bits (default is no grouping).
    When grouped, the string ``sep`` is inserted between groups
    of ``group`` characters, default is a space.
@@ -234,11 +236,11 @@ bitarray methods:
 
 
 ``tobytes()`` -> bytes
-   Return the bitarray buffer in bytes (pad bits are set to zero).
+   Return the bitarray buffer (pad bits are set to zero).
 
 
 ``tofile(f, /)``
-   Write byte representation of bitarray to file object f.
+   Write bitarray buffer to file object ``f``.
 
 
 ``tolist()`` -> list
@@ -312,7 +314,7 @@ Functions defined in the `bitarray` modu
 
 
 ``test(verbosity=1)`` -> TextTestResult
-   Run self-test, and return unittest.runner.TextTestResult object.
+   Run self-test, and return ``unittest.runner.TextTestResult`` object.
 
 
 Functions defined in `bitarray.util` module:
@@ -370,7 +372,7 @@ This sub-module was added in version 1.2
    New in version 3.3: ignore whitespace
 
 
-``byteswap(a, /, n=<buffer size>)``
+``byteswap(a, n=<buffer size>, /)``
    Reverse every ``n`` consecutive bytes of ``a`` in-place.
    By default, all bytes are reversed.  Note that ``n`` is not limited to 2, 4
    or 8, but can be any positive integer.
@@ -446,6 +448,19 @@ This sub-module was added in version 1.2
    New in version 2.5.0: allow bytes-like argument
 
 
+``gen_primes(n, /, endian=None, odd=False)`` -> bitarray
+   Generate a bitarray of length ``n`` in which active indices are prime numbers.
+   By default (``odd=False``), active indices correspond to prime numbers directly.
+   When ``odd=True``, only odd prime numbers are represented in the resulting
+   bitarray ``a``, and ``a[i]`` corresponds to ``2*i+1`` being prime or not.
+
+   Apart from working with prime numbers, this function is useful for
+   testing, as it provides a simple way to create a well-defined bitarray
+   of any length.
+
+   New in version 3.7
+
+
 ``hex2ba(hexstr, /, endian=None)`` -> bitarray
    Bitarray of hexadecimal representation.  hexstr may contain any number
    (including odd numbers) of hex digits (upper or lower case).
@@ -493,11 +508,9 @@ This sub-module was added in version 1.2
 
 
 ``pprint(bitarray, /, stream=None, group=8, indent=4, width=80)``
-   Prints the formatted representation of object on ``stream`` (which defaults
-   to ``sys.stdout``).  By default, elements are grouped in bytes (8 elements),
-   and 8 bytes (64 elements) per line.
-   Non-bitarray objects are printed by the standard library
-   function ``pprint.pprint()``.
+   Pretty-print bitarray object to ``stream``, defaults is ``sys.stdout``.
+   By default, bits are grouped in bytes (8 bits), and 64 bits per line.
+   Non-bitarray objects are printed using ``pprint.pprint()``.
 
    New in version 1.8
 
@@ -532,7 +545,7 @@ This sub-module was added in version 1.2
    New in version 3.5
 
 
-``sc_decode(stream)`` -> bitarray
+``sc_decode(stream, /)`` -> bitarray
    Decompress binary stream (an integer iterator, or bytes-like object) of a
    sparse compressed (``sc``) bitarray, and return the decoded  bitarray.
    This function consumes only one bitarray and leaves the remaining stream
@@ -575,12 +588,15 @@ This sub-module was added in version 1.2
    iteration is stopped as soon as one mismatch is found.
 
 
-``sum_indices(a, /)`` -> int
+``sum_indices(a, /, mode=1)`` -> int
    Return sum of indices of all active bits in bitarray ``a``.
    Equivalent to ``sum(i for i, v in enumerate(a) if v)``.
+   ``mode=2`` sums square of indices.
 
    New in version 3.6
 
+   New in version 3.7: add optional mode argument
+
 
 ``urandom(n, /, endian=None)`` -> bitarray
    Return random bitarray of length ``n`` (uses ``os.urandom()``).
diff -pruN 3.6.1-1/doc/represent.rst 3.7.1-1/doc/represent.rst
--- 3.6.1-1/doc/represent.rst	2025-08-12 08:35:42.000000000 +0000
+++ 3.7.1-1/doc/represent.rst	2025-08-28 20:59:12.000000000 +0000
@@ -117,29 +117,28 @@ The bitarray has to be multiple of lengt
 
 .. code-block:: python
 
-    >>> from bitarray.util import ba2base, base2ba
-    >>> a = bitarray('001010011010100000111011100110110001111100101110000100010010')
+    >>> from bitarray.util import ba2base
+    >>> a = bitarray('001010111111100000111011100110110001111100101110111110010010')
     >>> len(a)          # divisible by 2, 3, 4, 5 and 6
     60
     >>> ba2base(2, a)   # binary
-    '001010011010100000111011100110110001111100101110000100010010'
+    '001010111111100000111011100110110001111100101110111110010010'
     >>> ba2base(4, a)   # quaternary
-    '022122200323212301330232010102'
+    '022333200323212301330232332102'
     >>> ba2base(8, a)   # octal
-    '12324073466174560422'
+    '12774073466174567622'
     >>> ba2base(16, a)  # hexadecimal
-    '29a83b9b1f2e112'
+    '2bf83b9b1f2ef92'
     >>> ba2base(32, a)  # base 32 (using RFC 4648 Base32 alphabet)
-    'FGUDXGY7FYIS'
+    'FP4DXGY7F34S'
     >>> ba2base(64, a)  # base 64 (using standard base 64 alphabet)
-    'Kag7mx8uES'
+    'K/g7mx8u+S'
 
 Note that ``ba2base(2, a)`` is equivalent to ``a.to01()`` and
 that ``ba2base(16, a)`` is equivalent to ``ba2hex(a)``.
 Unlike ``ba2hex()``, ``ba2base()`` does not take advantage of byte level
-operations and is therefore a lot slower, although still implemented in C.
+operations and is therefore slower, although it is also implemented in C.
 The inverse function is called ``base2ba()``.
-See also `this example <../examples/base-n.py>`__.
 
 
 Variable length representation
diff -pruN 3.6.1-1/examples/README 3.7.1-1/examples/README
--- 3.6.1-1/examples/README	2025-08-12 08:35:42.000000000 +0000
+++ 3.7.1-1/examples/README	2025-08-28 20:59:12.000000000 +0000
@@ -1,7 +1,7 @@
 All files under this 'examples/' directory are unsupported.
 
 While bitarray itself supports Python 3.6+, some examples may require
-a later Python versoin.
+higher Python versions.
 
 
 bloom.py
@@ -19,6 +19,17 @@ double.py
     (IEEE 754 binary64).
 
 
+dubner.rst
+    We show how bitarrays can be used to calculate twin primes
+    and "middle numbers" very efficiently, and verify the some
+    conjectures in: https://oeis.org/A007534/a007534.pdf
+
+
+dyn_sieve.py
+    Prime numbers, implemented as a dynamically growing sieve of Eratosthenes.
+    Similar to prime number sieve in SymPy, but implemented using a bitarray.
+
+
 extend_json.py
     Demonstrates how to construct a json encoder and decoder (using the
     'json' standard library) which can handle extended Python data structures
diff -pruN 3.6.1-1/examples/distance.py 3.7.1-1/examples/distance.py
--- 3.6.1-1/examples/distance.py	2025-08-12 08:35:42.000000000 +0000
+++ 3.7.1-1/examples/distance.py	2025-08-28 20:59:12.000000000 +0000
@@ -22,9 +22,6 @@ def jaccard(u, v):
     x = count_xor(u, v)
     return x / (count_and(u, v) + x)
 
-def kulczynski1(u, v):
-    return count_and(u, v) / count_xor(u, v)
-
 def rogerstanimoto(u, v):
     x = count_xor(u, v)
     return 2 * x / (len(u) + x)
@@ -33,10 +30,6 @@ def russellrao(u, v):
     n = len(u)
     return (n - count_and(u, v)) / n
 
-def sokalmichener(u, v):
-    x = count_xor(u, v)
-    return 2 * x / (len(u) + x)
-
 def sokalsneath(u, v):
     R = 2 * count_xor(u, v)
     return R / (count_and(u, v) + R)
@@ -56,8 +49,7 @@ def test(n):
     aa = numpy.frombuffer(a.unpack(), dtype=bool)
     bb = numpy.frombuffer(b.unpack(), dtype=bool)
 
-    for name in ['dice', 'hamming', 'jaccard', 'kulczynski1',
-                 'rogerstanimoto', 'russellrao', 'sokalmichener',
+    for name in ['dice', 'hamming', 'jaccard', 'rogerstanimoto', 'russellrao',
                  'sokalsneath', 'yule']:
 
         f1 = eval(name)               # function defined above
@@ -74,4 +66,6 @@ def test(n):
 
         assert abs(x1 - x2) < 1E-14
 
-test(2 ** 20 + 67)
+
+if __name__ == "__main__":
+    test(2 ** 22 + 67)
diff -pruN 3.6.1-1/examples/dubner.rst 3.7.1-1/examples/dubner.rst
--- 3.6.1-1/examples/dubner.rst	1970-01-01 00:00:00.000000000 +0000
+++ 3.7.1-1/examples/dubner.rst	2025-08-28 20:59:12.000000000 +0000
@@ -0,0 +1,93 @@
+Dubner's conjecture
+===================
+
+Harvey Dubner proposed a strengthening of the Goldbach conjecture:
+every even integer greater than 4208 is the sum of two twin primes (not
+necessarily belonging to the same pair).
+Only 34 even integers less than 4208 are not the sum of two twin primes.
+Dubner has verified computationally that this list is complete up to 2e10.
+A proof of this stronger conjecture would imply not only Goldbach's
+conjecture but also the twin prime conjecture.  For more details,
+see `Dubner's conjecture <https://oeis.org/A007534/a007534.pdf>`__.
+
+In this document, we want to show how bitarrays can be used to calculate
+twin primes and "middle numbers" very efficiently, and with very little
+code.  We start by calculating all primes up to a limit ``N`` using
+the `Sieve of Eratosthenes <../examples/sieve.py>`__:
+
+.. code-block:: python
+
+    >>> from bitarray.util import zeros, gen_primes
+    >>> N = 1_000_000
+    >>> primes = gen_primes(N)
+    >>> list(primes.search(1, 0, 50))
+    [2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47]
+
+In order to save memory and compute time, we only consider odd numbers
+being twin primes.  Note that 2 is not considered a twin prime.
+
+.. code-block:: python
+
+    >>> twins = primes[1::2]
+    >>> # we have a twin when next odd number is also prime:
+    >>> twins &= twins << 1
+    >>> # The first twin primes (only lower of each pair) are:
+    >>> [2 * i + 1 for i in twins.search(1, 0, 60)]
+    [3, 5, 11, 17, 29, 41, 59, 71, 101, 107]
+
+We define a "middle number" to be ``m``, the number sandwiched between
+a pair of twin primes ``m − 1`` and ``m + 1``.  It is obvious from the
+characteristics of twin primes that all such ``m`` greater that 4 are
+divisible by 6.  Again, to save memory and compute time, we only
+consider multiples of 6.  If we let ``i=m//6``, we are looking for the
+numbers ``i`` such that ``6*i-1``, ``6*i+1`` are twin primes.  The
+numbers ``i`` are given in `sequence A002822 <https://oeis.org/A002822>`__.
+
+.. code-block:: python
+
+    >>> middles = zeros(1)  # middle numbers
+    >>> middles += twins[2::3]
+    >>> list(middles.search(1, 0, 46))  # sequence A002822
+    [1, 2, 3, 5, 7, 10, 12, 17, 18, 23, 25, 30, 32, 33, 38, 40, 45]
+
+Although not as memory efficient, a very elegant alternative to calculate
+the middle numbers directly from primes is:
+
+.. code-block:: python
+
+    >>> ((primes >> 1) & (primes << 1))[::6] == middles
+    True
+
+We now mark multiples of 6 that are sum of two middle numbers:
+
+.. code-block:: python
+
+    >>> M = len(middles)
+    >>> mark = zeros(M)
+    >>> for i in middles.search(1):
+    ...     mark[i:] |= middles[:M - i]
+
+Positive integers divisible by 6 and greater than 6 that are not the sum
+of two middle numbers (greater than 4):
+
+.. code-block:: python
+
+    >>> [6 * i for i in mark.search(0, 2)]
+    [96, 402, 516, 786, 906, 1116, 1146, 1266, 1356, 3246, 4206]
+
+This is `sequence A179825 <https://oeis.org/A179825>`__, the multiples of 6
+which are not the sum of a pair of twin primes.
+None of the above values are middle numbers themselves (this would
+contradict Conjecture 1):
+
+.. code-block:: python
+
+    >>> any(middles[m] for m in mark.search(0, 2))
+    False
+
+As `A007534 <https://oeis.org/A007534>`__, is the sequence of positive even
+numbers that are not the sum of a pair of twin primes (not just multiples
+of 6), A179825 is a subset of A007534.
+
+.. image:: https://github.com/ilanschnell/visual/blob/master/dubner/image.png?raw=true
+   :alt: visualization of middle numbers
diff -pruN 3.6.1-1/examples/dyn_sieve.py 3.7.1-1/examples/dyn_sieve.py
--- 3.6.1-1/examples/dyn_sieve.py	1970-01-01 00:00:00.000000000 +0000
+++ 3.7.1-1/examples/dyn_sieve.py	2025-08-28 20:59:12.000000000 +0000
@@ -0,0 +1,147 @@
+import math
+import itertools
+
+from bitarray import bitarray
+from bitarray.util import ones, count_n
+
+
+class Sieve:
+    """
+    Prime numbers, implemented as a dynamically growing sieve of
+    Eratosthenes.  Similar to prime number sieve in SymPy, but implemented
+    using a bitarray.
+    """
+    a = ones(105)
+    a[1::3] = 0
+    a[2::5] = 0
+    a[3::7] = 0
+
+    def __init__(self):
+        self.data = bitarray()
+
+    def extend(self, i):
+        "grow to accomodate i, ie. self.data[i//2] will not raise IndexError"
+        if i < 0:
+            raise ValueError("positive integer expected")
+
+        if i == 0:  # reset
+            self.data = bitarray()
+            return
+
+        n = i // 2 + 1  # n is minimal length of self.data
+        if n <= len(self.data):
+            return
+
+        fresh_data = not self.data
+        if fresh_data:
+            self.data = self.a.copy()
+            self.data[:8] = bitarray("01110110")
+
+        a = self.data
+        n1 = len(a)
+        m = (n - n1 + 105 - 1) // 105
+        assert fresh_data or m > 0
+        a += m * self.a
+        if fresh_data:
+            n1 = 60
+        for i in a.search(1, 5, int(math.sqrt(len(a) // 2) + 1.0)):
+            j = 2 * i + 1
+            j2 = (j * j) // 2
+            k = (j2 - n1) % j + n1 if j2 < n1 else j2
+            assert k >= n1
+            a[k :: j] = 0
+
+    def extend_to_no(self, n):
+        while self.data.count() + 1 < n:
+            self.extend(3 * len(self.data) + 1)
+
+    def __contains__(self, i):
+        if i < 0:
+            raise ValueError("positive integer expected")
+        if i % 2 == 0:
+            return i == 2
+        self.extend(i)
+        return self.data[i // 2]
+
+    def __iter__(self):
+        yield 2
+        for i in itertools.count(start=3, step=2):
+            self.extend(i)
+            if self.data[i // 2]:
+                yield i
+
+    def __getitem__(self, n):
+        "return n-th prime"
+        if n < 1:
+            # offset is one, so forbid explicit access to sieve[0]
+            raise IndexError("Sieve indices start at 1")
+        if n == 1:
+            return 2
+        self.extend_to_no(n)
+        i = count_n(self.data, n - 1) - 1
+        assert self.data[i]
+        return 2 * i + 1
+
+# ---------------------------------------------------------------------------
+
+import unittest
+from random import randrange
+
+from bitarray.util import gen_primes
+
+
+N = 1_000_000
+PRIMES = gen_primes(N)
+ODD_PRIMES = PRIMES[1::2]
+
+
+class SieveTests(unittest.TestCase):
+
+    def check_data(self, s, i):
+        if i == 0:
+            self.assertEqual(len(s.data), 0)
+            return
+        n = i // 2 + 1
+        if n <= len(s.data):
+            n = len(s.data)
+        n = 105 * ((n + 105 - 1) // 105)
+        self.assertEqual(len(s.data), n)
+        self.assertEqual(s.data, ODD_PRIMES[:n])
+
+    def test_random(self):
+        s = Sieve()
+        for _ in range(1000):
+            i = randrange(1000) if randrange(10) else 0
+            s.extend(i)
+            self.check_data(s, i)
+            #print(n, len(s.data))
+
+    def test_contains(self):
+        s = Sieve()
+        for i, v in enumerate(PRIMES[:1000]):
+            self.assertEqual(i in s, v)
+        for _ in range(1000):
+            i = randrange(1_000_000)
+            self.assertEqual(i in s, PRIMES[i])
+
+    def test_iter(self):
+        s = Sieve()
+        a = []
+        for i in s:
+            if len(a) >= 168:
+                break
+            a.append(i)
+        self.assertEqual(a[-1], 997)
+        self.assertEqual(a, list(PRIMES.search(1, 0, 1000)))
+
+    def test_getitem(self):
+        s = Sieve()
+        self.assertEqual(s[1], 2)
+        self.assertEqual(s[2], 3)
+        self.assertEqual(s[3], 5)
+        self.assertEqual(s[10], 29)
+        self.assertEqual(s[1_000_000], 15_485_863)
+
+
+if __name__ == '__main__':
+    unittest.main()
diff -pruN 3.6.1-1/examples/huffman/compress.py 3.7.1-1/examples/huffman/compress.py
--- 3.6.1-1/examples/huffman/compress.py	2025-08-12 08:35:42.000000000 +0000
+++ 3.7.1-1/examples/huffman/compress.py	2025-08-28 20:59:12.000000000 +0000
@@ -48,7 +48,7 @@ def encode(filename):
         assert len(a) == 0
     else:
         print('Bits: %d / %d' % (len(a), 8 * len(plain)))
-        print('Ratio =%6.2f%%' % (100.0 * a.buffer_info()[1] / len(plain)))
+        print('Ratio =%6.2f%%' % (100.0 * a.nbytes / len(plain)))
 
 def decode(filename):
     assert filename.endswith('.huff')
diff -pruN 3.6.1-1/examples/huffman/compress2.py 3.7.1-1/examples/huffman/compress2.py
--- 3.6.1-1/examples/huffman/compress2.py	2025-08-12 08:35:42.000000000 +0000
+++ 3.7.1-1/examples/huffman/compress2.py	2025-08-28 20:59:12.000000000 +0000
@@ -36,7 +36,7 @@ def encode(filename):
 
     if len(plain) > 0:
         print('Bits: %d / %d' % (len(a), 8 * len(plain)))
-        print('Ratio =%6.2f%%' % (100.0 * a.buffer_info()[1] / len(plain)))
+        print('Ratio =%6.2f%%' % (100.0 * a.nbytes / len(plain)))
 
 def decode(filename):
     assert filename.endswith('.huff2')
diff -pruN 3.6.1-1/examples/lexico.py 3.7.1-1/examples/lexico.py
--- 3.6.1-1/examples/lexico.py	2025-08-12 08:35:42.000000000 +0000
+++ 3.7.1-1/examples/lexico.py	2025-08-28 20:59:12.000000000 +0000
@@ -5,8 +5,8 @@ from bitarray import bitarray
 from bitarray.util import zeros, ba2int, int2ba
 
 
-def all_perm(n, k, endian=None):
-    """all_perm(n, k, endian=None) -> iterator
+def lexico_all(n, k, endian=None):
+    """lexico_all(n, k, endian=None) -> iterator
 
 Return an iterator over all bitarrays of length `n` and
 population count `k` in lexicographical order.
@@ -31,15 +31,18 @@ population count `k` in lexicographical
         v = t | ((((t & -t) // (v & -v)) >> 1) - 1)
 
 
-def next_perm(__a):
-    """next_perm(a, /) -> bitarray
+def lexico_next(__a):
+    """lexico_next(a, /) -> bitarray
 
-Return the next lexicographical permutation of bitarray `a`.  The length
-and population count of the result is that of `a`.  The integer
-value (`ba2int()`) of the next permutation will always increase, except
-when the cycle is completed.  In that case, the lowest lexicographical
+Return the next lexicographical permutation of bitarray `a`.
+The length and population count of the result remains unchanged.
+The integer value (`ba2int()`) of the next permutation will always increase,
+except when the cycle is completed.  In that case, the lowest lexicographical
 permutation will be returned.
 """
+    if not __a:
+        return __a
+
     v = ba2int(__a)
     if v == 0:
         return __a
@@ -51,10 +54,10 @@ permutation will be returned.
     except OverflowError:
         return __a[::-1]
 
-# ---------------------------------------------------------------------------
+# ---------------------- lexicographical permutations -----------------------
 
+import math
 import unittest
-from math import comb
 from random import choice, getrandbits, randrange
 from itertools import pairwise
 
@@ -62,16 +65,15 @@ from bitarray import frozenbitarray
 from bitarray.util import random_k
 
 
-class PermTests(unittest.TestCase):
+class LexicoTests(unittest.TestCase):
 
     def test_errors(self):
-        N = next_perm
+        N = lexico_next
         self.assertRaises(TypeError, N)
         self.assertRaises(TypeError, N, bitarray('1'), 1)
         self.assertRaises(TypeError, N, '1')
-        self.assertRaises(ValueError, N, bitarray())
 
-        A = all_perm
+        A = lexico_all
         self.assertRaises(TypeError, A)
         self.assertRaises(TypeError, A, 4)
         self.assertRaises(TypeError, next, A("4", 2))
@@ -85,71 +87,83 @@ class PermTests(unittest.TestCase):
         self.assertRaises(ValueError, next, A(10, 7, endian='foo'))
 
     def test_zeros_ones(self):
-        for n in range(1, 30):
+        for n in range(30):
             endian = choice(["little", "big"])
-            v = getrandbits(1)
+            a = bitarray(n, endian)
+            a.setall(getrandbits(1))
+            b = lexico_next(a)
+            self.assertEqual(b.endian, endian)
+            # nothing to permute
+            self.assertEqual(b, a)
+
+    def test_next_explicit(self):
+        for perm, endian in [
+                (['100', '010', '001', '100'], 'little'),
+                (['00001', '00010', '00100', '01000', '10000'], 'big'),
+                (['0011', '0101', '0110', '1001', '1010',
+                  '1100', '0011'], 'big'),
+                (['0000111', '1110000'], 'little'),
+        ]:
+            a = bitarray(perm[0], endian)
+            for s in perm[1:]:
+                a = lexico_next(a)
+                self.assertEqual(a, bitarray(s))
+
+    def check_cycle(self, n, k):
+        endian = choice(["little", "big"])
+        a0 = bitarray(n, endian)
+        a0[:k] = 1
+        if endian == "big":
+            a0.reverse()
 
-            lst = list(all_perm(n, v * n, endian))
-            self.assertEqual(len(lst), 1)
-            a = lst[0]
-            c = a.copy()
-            self.assertEqual(a.endian, endian)
-            self.assertEqual(len(a), n)
-            if v:
-                self.assertTrue(a.all())
-            else:
-                self.assertFalse(a.any())
-            self.assertEqual(next_perm(a), a)
-            self.assertEqual(a, c)
-
-    def test_next_perm_explicit(self):
-        a = bitarray('00010011', 'big')
-        for s in ['00010101', '00010110', '00011001',
-                  '00011010', '00011100', '00100011']:
-            a = next_perm(a)
-            self.assertEqual(a.count(), 3)
-            self.assertEqual(a, bitarray(s, 'big'))
-
-    def test_next_perm_turnover(self):
-        for a in [bitarray('11111110000', 'big'),
-                  bitarray('0000001111111', 'little')]:
-            self.assertEqual(next_perm(a), a[::-1])
+        ncycle = math.comb(n, k)  # cycle length
+        self.assertTrue(ncycle >= 2)
+        coll = set()
+        a = a0.copy()
+        for i in range(ncycle):
+            coll.add(frozenbitarray(a))
+            b = lexico_next(a)
+            self.assertEqual(len(b), n)
+            self.assertEqual(b.count(), k)
+            self.assertEqual(b.endian, endian)
+            self.assertNotEqual(a, b)
+            if b == a0:
+                self.assertEqual(i, ncycle - 1)
+                self.assertTrue(ba2int(b) < ba2int(a))
+                break
+            self.assertTrue(ba2int(b) > ba2int(a))
+            a = b
+        else:
+            self.fail()
+
+        self.assertEqual(len(coll), ncycle)
+
+    def test_cycle(self):
+        for _ in range(20):
+            n = randrange(2, 10)
+            k = randrange(1, n)
+            self.check_cycle(n, k)
 
-    def test_next_perm_random(self):
+    def test_next_random(self):
         for _ in range(100):
-            n = randrange(2, 1_000_000)
+            endian = choice(["little", "big"])
+            n = randrange(2, 1_000)
             k = randrange(1, n)
-            a = random_k(n, k, endian=choice(["little", "big"]))
-            b = next_perm(a)
+            a = random_k(n, k, endian)
+            b = lexico_next(a)
             self.assertEqual(len(b), n)
             self.assertEqual(b.count(), k)
-            self.assertEqual(b.endian, a.endian)
+            self.assertEqual(b.endian, endian)
             self.assertNotEqual(a, b)
             if ba2int(a) > ba2int(b):
-                print(n)
                 c = a.copy()
-                c.sort(c.endian == 'big')
+                c.sort(endian == 'big')
                 self.assertEqual(a, c)
                 self.assertEqual(b, a[::-1])
 
-    def check_perm_cycle(self, start):
-        n, k = len(start), start.count()
-        a = bitarray(start)
-        coll = set()
-        c = 0
-        while True:
-            a = next_perm(a)
-            coll.add(frozenbitarray(a))
-            self.assertEqual(len(a), n)
-            self.assertEqual(a.count(), k)
-            self.assertEqual(a.endian, start.endian)
-            c += 1
-            if a == start:
-                break
-        self.assertEqual(c, comb(n, k))
-        self.assertEqual(len(coll), c)
+    # -------------------  lexico_all  -------------------
 
-    def test_all_perm_explicit(self):
+    def test_all_explicit(self):
         for n, k, res in [
                 (0, 0, ['']),
                 (1, 0, ['0']),
@@ -163,15 +177,15 @@ class PermTests(unittest.TestCase):
                 (3, 3, ['111']),
                 (4, 2, ['0011', '0101', '0110', '1001', '1010', '1100']),
         ]:
-            lst = list(all_perm(n, k, 'big'))
-            self.assertEqual(len(lst), comb(n, k))
+            lst = list(lexico_all(n, k, 'big'))
+            self.assertEqual(len(lst), math.comb(n, k))
             self.assertEqual(lst, [bitarray(s) for s in res])
             if n == 0:
                 continue
             a = lst[0]
             for i in range(20):
                 self.assertEqual(a, bitarray(res[i % len(lst)]))
-                a = next_perm(a)
+                a = lexico_next(a)
 
     def test_all_perm(self):
         n, k = 17, 5
@@ -180,7 +194,7 @@ class PermTests(unittest.TestCase):
         prev = None
         cnt = 0
         coll = set()
-        for a in all_perm(n, k, endian):
+        for a in lexico_all(n, k, endian):
             self.assertEqual(type(a), bitarray)
             self.assertEqual(len(a), n)
             self.assertEqual(a.count(), k)
@@ -193,12 +207,12 @@ class PermTests(unittest.TestCase):
                 self.assertEqual(a, c)
             else:
                 self.assertNotEqual(a, first)
-                self.assertEqual(next_perm(prev), a)
+                self.assertEqual(lexico_next(prev), a)
                 self.assertTrue(ba2int(prev) < ba2int(a))
             prev = a
             cnt += 1
 
-        self.assertEqual(cnt, comb(n, k))
+        self.assertEqual(cnt, math.comb(n, k))
         self.assertEqual(len(coll), cnt)
 
         # a is now the last permutation
@@ -206,11 +220,11 @@ class PermTests(unittest.TestCase):
         self.assertTrue(ba2int(first) < ba2int(last))
         self.assertEqual(last, first[::-1])
 
-    def test_all_perm_order(self):
+    def test_all_order(self):
         n, k = 10, 5
-        for a, b in pairwise(all_perm(n, k, 'little')):
+        for a, b in pairwise(lexico_all(n, k, 'little')):
             self.assertTrue(ba2int(b) > ba2int(a))
-            self.assertEqual(next_perm(a), b)
+            self.assertEqual(lexico_next(a), b)
 
 
 if __name__ == '__main__':
diff -pruN 3.6.1-1/examples/puff/_puff.c 3.7.1-1/examples/puff/_puff.c
--- 3.6.1-1/examples/puff/_puff.c	2025-08-12 08:35:42.000000000 +0000
+++ 3.7.1-1/examples/puff/_puff.c	2025-08-28 20:59:12.000000000 +0000
@@ -26,7 +26,6 @@
 */
 #define PY_SSIZE_T_CLEAN
 #include "Python.h"
-#include "pythoncapi_compat.h"
 #include "bitarray.h"
 
 
@@ -633,13 +632,9 @@ static PyModuleDef moduledef = {
 
 PyMODINIT_FUNC PyInit__puff(void)
 {
-    PyObject *m, *bitarray_module;
+    PyObject *m;
 
-    if ((bitarray_module = PyImport_ImportModule("bitarray")) == NULL)
-        return NULL;
-    bitarray_type = (PyTypeObject *) PyObject_GetAttrString(bitarray_module,
-                                                            "bitarray");
-    Py_DECREF(bitarray_module);
+    bitarray_type = (PyTypeObject *) bitarray_module_attr("bitarray");
     if (bitarray_type == NULL)
         return NULL;
 
diff -pruN 3.6.1-1/examples/sieve.py 3.7.1-1/examples/sieve.py
--- 3.6.1-1/examples/sieve.py	2025-08-12 08:35:42.000000000 +0000
+++ 3.7.1-1/examples/sieve.py	2025-08-28 20:59:12.000000000 +0000
@@ -1,6 +1,10 @@
 """
 Demonstrates the implementation of "Sieve of Eratosthenes" algorithm for
 finding all prime numbers up to any given limit.
+
+It should be noted that bitarray version 3.7 added util.gen_primes().
+The library function basically uses an optimized version of the same
+algorithm.
 """
 from math import isqrt
 
diff -pruN 3.6.1-1/update_doc.py 3.7.1-1/update_doc.py
--- 3.6.1-1/update_doc.py	2025-08-12 08:35:42.000000000 +0000
+++ 3.7.1-1/update_doc.py	2025-08-28 20:59:12.000000000 +0000
@@ -13,6 +13,7 @@ NEW_IN = {
     'bitarray':              ['2.3: optional `buffer` argument',
                               '3.4: allow initializer `bytes` or `bytearray` '
                                    'to set buffer directly'],
+    'bitarray.buffer_info':   '3.7: return named tuple',
     'bitarray.bytereverse':   '2.2.5: optional start and stop arguments',
     'bitarray.clear':         '1.4',
     'bitarray.count':        ['1.1.0: optional start and stop arguments',
@@ -25,7 +26,7 @@ NEW_IN = {
     'bitarray.find':         ['2.1',
                               '2.9: add optional keyword argument `right`'],
     'bitarray.frombytes':     '2.5.0: allow bytes-like argument',
-    'bitarray.index':        ['2.9: add optional keyword argument `right`'],
+    'bitarray.index':         '2.9: add optional keyword argument `right`',
     'bitarray.invert':        '1.5.3: optional index argument',
     'bitarray.pack':          '2.5.0: allow bytes-like argument',
     'bitarray.search':       ['2.9: optional start and stop arguments - '
@@ -51,13 +52,15 @@ NEW_IN = {
     'util.intervals':         '2.7',
     'util.ones':              '2.9',
     'util.parity':            '1.9',
-    'util.sum_indices':       '3.6',
+    'util.sum_indices':      ['3.6',
+                              '3.7: add optional mode argument'],
     'util.xor_indices':       '3.2',
     'util.pprint':            '1.8',
     'util.serialize':         '1.8',
     'util.urandom':           '1.7',
     'util.random_k':          '3.6',
     'util.random_p':          '3.5',
+    'util.gen_primes':        '3.7',
     'util.sc_encode':         '2.7',
     'util.sc_decode':         '2.7',
     'util.vl_decode':         '2.2',
@@ -102,6 +105,11 @@ NOTES = {
    memory (depending on the machine architecture) than the bitarray object,
    which may cause a memory error if the bitarray is very large.""",
 
+    'util.gen_primes': """\
+   Apart from working with prime numbers, this function is useful for
+   testing, as it provides a simple way to create a well-defined bitarray
+   of any length.""",
+
     'util.count_xor': "   This is also known as the Hamming distance."
 }
 
@@ -298,6 +306,8 @@ def main():
     testfile('./README.rst')
     for path in glob("./doc/*.rst"):
         testfile(path)
+    for path in glob("./examples/*.rst"):
+        testfile(path)
 
 
 if __name__ == '__main__':
