bitarray 3.6.0__tar.gz → 3.6.1__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: bitarray
3
- Version: 3.6.0
3
+ Version: 3.6.1
4
4
  Summary: efficient arrays of booleans -- C extension
5
5
  Home-page: https://github.com/ilanschnell/bitarray
6
6
  Author: Ilan Schnell
@@ -83,20 +83,13 @@ versions. Which means you can simply:
83
83
 
84
84
  $ pip install bitarray
85
85
 
86
- In addition, conda packages are available (both the default Anaconda
87
- repository as well as conda-forge support bitarray):
88
-
89
- .. code-block:: shell-session
90
-
91
- $ conda install bitarray
92
-
93
86
  Once you have installed the package, you may want to test it:
94
87
 
95
88
  .. code-block:: shell-session
96
89
 
97
90
  $ python -c 'import bitarray; bitarray.test()'
98
91
  bitarray is installed in: /Users/ilan/bitarray/bitarray
99
- bitarray version: 3.6.0
92
+ bitarray version: 3.6.1
100
93
  sys.version: 3.13.5 (main, Jun 16 2025) [Clang 18.1.8]
101
94
  sys.prefix: /Users/ilan/miniforge
102
95
  pointer size: 64 bit
@@ -110,7 +103,7 @@ Once you have installed the package, you may want to test it:
110
103
  .........................................................................
111
104
  ................................................................
112
105
  ----------------------------------------------------------------------
113
- Ran 589 tests in 0.163s
106
+ Ran 591 tests in 0.163s
114
107
 
115
108
  OK
116
109
 
@@ -427,7 +420,7 @@ and can therefore be used as a dictionary key:
427
420
  Reference
428
421
  =========
429
422
 
430
- bitarray version: 3.6.0 -- `change log <https://github.com/ilanschnell/bitarray/blob/master/doc/changelog.rst>`__
423
+ bitarray version: 3.6.1 -- `change log <https://github.com/ilanschnell/bitarray/blob/master/doc/changelog.rst>`__
431
424
 
432
425
  In the following, ``item`` and ``value`` are usually a single bit -
433
426
  an integer 0 or 1.
@@ -1003,7 +996,7 @@ This sub-module was added in version 1.2.
1003
996
 
1004
997
  ``sum_indices(a, /)`` -> int
1005
998
  Return sum of indices of all active bits in bitarray ``a``.
1006
- This is equivalent to ``sum(i for i, v in enumerate(a) if v)``.
999
+ Equivalent to ``sum(i for i, v in enumerate(a) if v)``.
1007
1000
 
1008
1001
  New in version 3.6
1009
1002
 
@@ -51,20 +51,13 @@ versions. Which means you can simply:
51
51
 
52
52
  $ pip install bitarray
53
53
 
54
- In addition, conda packages are available (both the default Anaconda
55
- repository as well as conda-forge support bitarray):
56
-
57
- .. code-block:: shell-session
58
-
59
- $ conda install bitarray
60
-
61
54
  Once you have installed the package, you may want to test it:
62
55
 
63
56
  .. code-block:: shell-session
64
57
 
65
58
  $ python -c 'import bitarray; bitarray.test()'
66
59
  bitarray is installed in: /Users/ilan/bitarray/bitarray
67
- bitarray version: 3.6.0
60
+ bitarray version: 3.6.1
68
61
  sys.version: 3.13.5 (main, Jun 16 2025) [Clang 18.1.8]
69
62
  sys.prefix: /Users/ilan/miniforge
70
63
  pointer size: 64 bit
@@ -78,7 +71,7 @@ Once you have installed the package, you may want to test it:
78
71
  .........................................................................
79
72
  ................................................................
80
73
  ----------------------------------------------------------------------
81
- Ran 589 tests in 0.163s
74
+ Ran 591 tests in 0.163s
82
75
 
83
76
  OK
84
77
 
@@ -395,7 +388,7 @@ and can therefore be used as a dictionary key:
395
388
  Reference
396
389
  =========
397
390
 
398
- bitarray version: 3.6.0 -- `change log <https://github.com/ilanschnell/bitarray/blob/master/doc/changelog.rst>`__
391
+ bitarray version: 3.6.1 -- `change log <https://github.com/ilanschnell/bitarray/blob/master/doc/changelog.rst>`__
399
392
 
400
393
  In the following, ``item`` and ``value`` are usually a single bit -
401
394
  an integer 0 or 1.
@@ -971,7 +964,7 @@ This sub-module was added in version 1.2.
971
964
 
972
965
  ``sum_indices(a, /)`` -> int
973
966
  Return sum of indices of all active bits in bitarray ``a``.
974
- This is equivalent to ``sum(i for i, v in enumerate(a) if v)``.
967
+ Equivalent to ``sum(i for i, v in enumerate(a) if v)``.
975
968
 
976
969
  New in version 3.6
977
970
 
@@ -4195,44 +4195,6 @@ Return tuple containing:\n\
4195
4195
  7. PY_LITTLE_ENDIAN\n\
4196
4196
  8. PY_BIG_ENDIAN");
4197
4197
 
4198
- /* ---------- module functions exposed in debug mode for testing ------- */
4199
-
4200
- #ifndef NDEBUG
4201
-
4202
- static PyObject *
4203
- module_setup_table(PyObject *module, PyObject *obj)
4204
- {
4205
- char table[256];
4206
- Py_UCS4 ch;
4207
-
4208
- assert(PyUnicode_Check(obj));
4209
- assert(PyUnicode_GET_LENGTH(obj) == 1);
4210
- ch = PyUnicode_READ_CHAR(obj, 0);
4211
-
4212
- setup_table(table, ch);
4213
- return PyBytes_FromStringAndSize(table, 256);
4214
- }
4215
-
4216
- /* Return zlw(a) as a new bitarray, rather than an int object.
4217
- This makes testing easier, because the int result would depend
4218
- on the machine byteorder. */
4219
- static PyObject *
4220
- module_zlw(PyObject *module, PyObject *obj)
4221
- {
4222
- bitarrayobject *a, *res;
4223
- uint64_t w;
4224
-
4225
- assert(bitarray_Check(obj));
4226
- a = (bitarrayobject *) obj;
4227
- w = zlw(a);
4228
- if ((res = newbitarrayobject(&Bitarray_Type, 64, a->endian)) == NULL)
4229
- return NULL;
4230
- memcpy(res->ob_item, &w, 8);
4231
- return (PyObject *) res;
4232
- }
4233
-
4234
- #endif /* NDEBUG */
4235
-
4236
4198
 
4237
4199
  static PyMethodDef module_functions[] = {
4238
4200
  {"bits2bytes", (PyCFunction) bits2bytes, METH_O,
@@ -4246,13 +4208,6 @@ static PyMethodDef module_functions[] = {
4246
4208
  set_default_endian_doc},
4247
4209
  {"_sysinfo", (PyCFunction) sysinfo, METH_NOARGS,
4248
4210
  sysinfo_doc},
4249
-
4250
- #ifndef NDEBUG
4251
- /* functions exposed in debug mode for testing */
4252
- {"_setup_table", (PyCFunction) module_setup_table, METH_O, 0},
4253
- {"_zlw", (PyCFunction) module_zlw, METH_O, 0},
4254
- #endif
4255
-
4256
4211
  {NULL, NULL} /* sentinel */
4257
4212
  };
4258
4213
 
@@ -265,71 +265,58 @@ Return parity of bitarray `a`.\n\
265
265
  `parity(a)` is equivalent to `a.count() % 2` but more efficient.");
266
266
 
267
267
 
268
+ /* Internal functions, like sum_indices(), but bitarrays are limited in
269
+ size. For details see: devel/test_sum_indices.py
270
+ */
268
271
  static PyObject *
269
- add_uint64(PyObject *number, uint64_t i)
270
- {
271
- PyObject *res, *tmp = PyLong_FromUnsignedLongLong(i);
272
-
273
- res = PyNumber_Add(number, tmp);
274
- Py_DECREF(tmp);
275
- Py_DECREF(number);
276
- return res;
277
- }
278
-
279
- static PyObject *
280
- sum_indices(PyObject *module, PyObject *obj)
272
+ ssqi(PyObject *module, PyObject *args)
281
273
  {
282
- static char table[256];
283
- static int setup = -1; /* endianness of table */
284
- PyObject *res;
274
+ static char count_table[256], sum_table[256], sum_sqr_table[256];
275
+ static int setup = -1; /* endianness of tables */
285
276
  bitarrayobject *a;
286
- Py_ssize_t nbytes, i;
277
+ uint64_t nbytes, i;
287
278
  uint64_t sm = 0; /* accumulated sum */
279
+ int mode = 1;
288
280
 
289
- if (ensure_bitarray(obj) < 0)
281
+ if (!PyArg_ParseTuple(args, "O!|i", bitarray_type,
282
+ (PyObject *) &a, &mode))
290
283
  return NULL;
291
-
292
- res = PyLong_FromLong(0);
293
- a = (bitarrayobject *) obj;
294
- nbytes = Py_SIZE(a);
295
- set_padbits(a);
284
+ if (mode < 1 || mode > 2)
285
+ return PyErr_Format(PyExc_ValueError, "unexpected mode %d", mode);
286
+ if (((uint64_t) a->nbits) > (mode == 1 ? 6074001000LLU : 3810778LLU))
287
+ return PyErr_Format(PyExc_OverflowError, "ssqi %zd", a->nbits);
296
288
 
297
289
  if (setup != a->endian) {
298
- setup_table(table, IS_LE(a) ? 'a' : 'A');
290
+ setup_table(count_table, 'c');
291
+ setup_table(sum_table, IS_LE(a) ? 'a' : 'A');
292
+ setup_table(sum_sqr_table, IS_LE(a) ? 's' : 'S');
299
293
  setup = a->endian;
300
294
  }
301
295
 
296
+ nbytes = Py_SIZE(a);
297
+ set_padbits(a);
302
298
  for (i = 0; i < nbytes; i++) {
303
299
  unsigned char c = a->ob_item[i];
304
- if (!c)
305
- continue;
306
- sm += ((uint64_t) i) * ((uint64_t) (8 * popcnt_64(c)));
307
- sm += table[c];
308
-
309
- if (sm > ((uint64_t ) 1) << 63) {
310
- /* Flush accumulated sum into Python number object.
311
- For ones(n) this will already happen when n > 2**32.
312
- printf("%llu %30zd\n", sm, i); */
313
- if ((res = add_uint64(res, sm)) == NULL)
314
- return NULL;
315
- sm = 0;
300
+ if (c) {
301
+ uint64_t k = count_table[c], z1 = sum_table[c];
302
+ if (mode == 1) {
303
+ sm += k * 8LLU * i + z1;
304
+ }
305
+ else {
306
+ uint64_t z2 = (unsigned char) sum_sqr_table[c];
307
+ sm += (k * 64LLU * i + 16LLU * z1) * i + z2;
308
+ }
316
309
  }
317
310
  }
318
- return add_uint64(res, sm);
311
+ return PyLong_FromUnsignedLongLong(sm);
319
312
  }
320
313
 
321
- PyDoc_STRVAR(sum_indices_doc,
322
- "sum_indices(a, /) -> int\n\
323
- \n\
324
- Return sum of indices of all active bits in bitarray `a`.\n\
325
- This is equivalent to `sum(i for i, v in enumerate(a) if v)`.");
326
-
327
314
 
328
315
  static PyObject *
329
316
  xor_indices(PyObject *module, PyObject *obj)
330
317
  {
331
- static char table[256];
332
- static int setup = -1; /* endianness of table */
318
+ static char parity_table[256], xor_table[256];
319
+ static int setup = -1; /* endianness of xor_table */
333
320
  bitarrayobject *a;
334
321
  Py_ssize_t res = 0, nbytes, i;
335
322
 
@@ -341,15 +328,16 @@ xor_indices(PyObject *module, PyObject *obj)
341
328
  set_padbits(a);
342
329
 
343
330
  if (setup != a->endian) {
344
- setup_table(table, IS_LE(a) ? 'x' : 'X');
331
+ setup_table(xor_table, IS_LE(a) ? 'x' : 'X');
332
+ setup_table(parity_table, 'p');
345
333
  setup = a->endian;
346
334
  }
347
335
 
348
336
  for (i = 0; i < nbytes; i++) {
349
337
  unsigned char c = a->ob_item[i];
350
- if (parity_64(c))
338
+ if (parity_table[c])
351
339
  res ^= i << 3;
352
- res ^= table[c];
340
+ res ^= xor_table[c];
353
341
  }
354
342
  return PyLong_FromSsize_t(res);
355
343
  }
@@ -2103,14 +2091,43 @@ static PyTypeObject CHDI_Type = {
2103
2091
 
2104
2092
  #ifndef NDEBUG
2105
2093
 
2094
+ static PyObject *
2095
+ module_setup_table(PyObject *module, PyObject *obj)
2096
+ {
2097
+ char table[256];
2098
+
2099
+ assert(PyUnicode_Check(obj));
2100
+ assert(PyUnicode_GET_LENGTH(obj) == 1);
2101
+ setup_table(table, PyUnicode_READ_CHAR(obj, 0));
2102
+ return PyBytes_FromStringAndSize(table, 256);
2103
+ }
2104
+
2105
+ /* Return zlw(a) as a new bitarray, rather than an int object.
2106
+ This makes testing easier, because the int result would depend
2107
+ on the machine byteorder. */
2108
+ static PyObject *
2109
+ module_zlw(PyObject *module, PyObject *obj)
2110
+ {
2111
+ bitarrayobject *a, *res;
2112
+ uint64_t w;
2113
+
2114
+ assert(bitarray_Check(obj));
2115
+ a = (bitarrayobject *) obj;
2116
+ w = zlw(a);
2117
+ if ((res = new_bitarray(64, Py_None, -1)) == NULL)
2118
+ return NULL;
2119
+ res->endian = a->endian;
2120
+ memcpy(res->ob_item, &w, 8);
2121
+ return (PyObject *) res;
2122
+ }
2123
+
2106
2124
  static PyObject *
2107
2125
  module_cfw(PyObject *module, PyObject *args) /* count_from_word() */
2108
2126
  {
2109
2127
  bitarrayobject *a;
2110
2128
  Py_ssize_t i;
2111
2129
 
2112
- if (!PyArg_ParseTuple(args, "O!n",
2113
- bitarray_type, (PyObject *) &a, &i))
2130
+ if (!PyArg_ParseTuple(args, "O!n", bitarray_type, (PyObject *) &a, &i))
2114
2131
  return NULL;
2115
2132
  return PyLong_FromSsize_t(count_from_word(a, i));
2116
2133
  }
@@ -2156,12 +2173,8 @@ static PyMethodDef module_functions[] = {
2156
2173
  METH_VARARGS, ones_doc},
2157
2174
  {"count_n", (PyCFunction) count_n, METH_VARARGS, count_n_doc},
2158
2175
  {"parity", (PyCFunction) parity, METH_O, parity_doc},
2159
- {"sum_indices",
2160
- (PyCFunction) sum_indices,
2161
- METH_O, sum_indices_doc},
2162
- {"xor_indices",
2163
- (PyCFunction) xor_indices,
2164
- METH_O, xor_indices_doc},
2176
+ {"_ssqi", (PyCFunction) ssqi, METH_VARARGS, 0},
2177
+ {"xor_indices", (PyCFunction) xor_indices, METH_O, xor_indices_doc},
2165
2178
  {"count_and", (PyCFunction) count_and, METH_VARARGS, count_and_doc},
2166
2179
  {"count_or", (PyCFunction) count_or, METH_VARARGS, count_or_doc},
2167
2180
  {"count_xor", (PyCFunction) count_xor, METH_VARARGS, count_xor_doc},
@@ -2171,10 +2184,8 @@ static PyMethodDef module_functions[] = {
2171
2184
  (PyCFunction) correspond_all,
2172
2185
  METH_VARARGS, correspond_all_doc},
2173
2186
  {"byteswap", (PyCFunction) byteswap, METH_VARARGS, byteswap_doc},
2174
- {"serialize", (PyCFunction) serialize, METH_O, serialize_doc},
2175
- {"deserialize",
2176
- (PyCFunction) deserialize,
2177
- METH_O, deserialize_doc},
2187
+ {"serialize", (PyCFunction) serialize, METH_O, serialize_doc},
2188
+ {"deserialize", (PyCFunction) deserialize, METH_O, deserialize_doc},
2178
2189
  {"ba2hex", (PyCFunction) ba2hex, METH_KEYWORDS |
2179
2190
  METH_VARARGS, ba2hex_doc},
2180
2191
  {"hex2ba", (PyCFunction) hex2ba, METH_KEYWORDS |
@@ -2193,10 +2204,12 @@ static PyMethodDef module_functions[] = {
2193
2204
 
2194
2205
  #ifndef NDEBUG
2195
2206
  /* functions exposed in debug mode for testing */
2196
- {"_cfw", (PyCFunction) module_cfw, METH_VARARGS, 0},
2197
- {"_read_n", (PyCFunction) module_read_n, METH_VARARGS, 0},
2198
- {"_write_n", (PyCFunction) module_write_n, METH_VARARGS, 0},
2199
- {"_sc_rts", (PyCFunction) module_sc_rts, METH_O, 0},
2207
+ {"_setup_table", (PyCFunction) module_setup_table, METH_O, 0},
2208
+ {"_zlw", (PyCFunction) module_zlw, METH_O, 0},
2209
+ {"_cfw", (PyCFunction) module_cfw, METH_VARARGS, 0},
2210
+ {"_read_n", (PyCFunction) module_read_n, METH_VARARGS, 0},
2211
+ {"_write_n", (PyCFunction) module_write_n, METH_VARARGS, 0},
2212
+ {"_sc_rts", (PyCFunction) module_sc_rts, METH_O, 0},
2200
2213
  #endif
2201
2214
 
2202
2215
  {NULL, NULL} /* sentinel */
@@ -4,7 +4,7 @@
4
4
 
5
5
  Author: Ilan Schnell
6
6
  */
7
- #define BITARRAY_VERSION "3.6.0"
7
+ #define BITARRAY_VERSION "3.6.1"
8
8
 
9
9
  #ifdef STDC_HEADERS
10
10
  # include <stddef.h>
@@ -229,20 +229,28 @@ swap_bytes(char *p, Py_ssize_t n)
229
229
  static inline void
230
230
  setup_table(char *table, char kop)
231
231
  {
232
- int j, k;
232
+ int k;
233
233
  for (k = 0; k < 256; k++) {
234
- table[k] = 0;
234
+ char t = 0, j;
235
235
  for (j = 0; j < 8; j++) {
236
236
  if (k & 1 << j)
237
+ /* j are the indices of active bits in k (little endian) */
237
238
  switch (kop) {
238
- case 'a': table[k] += j; break;
239
- case 'A': table[k] += 7 - j; break;
240
- case 'x': table[k] ^= j; break;
241
- case 'X': table[k] ^= 7 - j; break;
242
- case 'r': table[k] |= 128 >> j; break;
239
+ case 'a': t += j; break; /* add active indices */
240
+ case 'A': t += 7 - j; break; /* 'a' for big endian */
241
+ case 's': t += j * j; /* add squares of active indices */
242
+ break;
243
+ case 'S': t += (7-j) * (7-j); /* 's' for big endian */
244
+ break;
245
+ case 'x': t ^= j; break; /* xor active indices */
246
+ case 'X': t ^= 7 - j; break; /* 'x' for big endian */
247
+ case 'c': t++; break; /* bit count */
248
+ case 'p': t ^= 1; break; /* parity */
249
+ case 'r': t |= 128 >> j; break; /* reverse bits */
243
250
  default: Py_UNREACHABLE();
244
251
  }
245
252
  }
253
+ table[k] = t;
246
254
  }
247
255
  }
248
256
 
@@ -17,7 +17,8 @@ import unittest
17
17
  from io import StringIO
18
18
  from functools import reduce
19
19
  from string import hexdigits
20
- from random import choice, getrandbits, randrange, randint, random, seed
20
+ from random import (choice, getrandbits, randrange, randint, random, sample,
21
+ seed)
21
22
  from collections import Counter
22
23
 
23
24
  from bitarray import (bitarray, frozenbitarray, decodetree, bits2bytes,
@@ -36,7 +37,7 @@ from bitarray.util import (
36
37
  _huffman_tree, huffman_code, canonical_huffman, canonical_decode,
37
38
  )
38
39
 
39
- from bitarray.util import _Random # type: ignore
40
+ from bitarray.util import _Random, _ssqi # type: ignore
40
41
 
41
42
  # ---------------------------------------------------------------------------
42
43
 
@@ -147,6 +148,7 @@ class Random_K_Tests(unittest.TestCase):
147
148
  self.assertRaises(TypeError, R, 4)
148
149
  self.assertRaises(TypeError, R, 1, "0.5")
149
150
  self.assertRaises(TypeError, R, 1, p=1)
151
+ self.assertRaises(TypeError, R, 11, 5.5) # see issue #239
150
152
  self.assertRaises(ValueError, R, -1, 0)
151
153
  for k in -1, 11: # k is not 0 <= k <= n
152
154
  self.assertRaises(ValueError, R, 10, k)
@@ -157,6 +159,12 @@ class Random_K_Tests(unittest.TestCase):
157
159
  self.assertEqual(R(n, k=n), ones(n))
158
160
 
159
161
  def test_count(self):
162
+ for n in range(10): # test explicitly for small n
163
+ for k in range(n + 1):
164
+ a = random_k(n, k)
165
+ self.assertEqual(len(a), n)
166
+ self.assertEqual(a.count(), k)
167
+
160
168
  for _ in range(100):
161
169
  n = randrange(10_000)
162
170
  k = randint(0, n)
@@ -358,7 +366,7 @@ class Random_P_Tests(unittest.TestCase):
358
366
 
359
367
  def test_small_p_limit(self):
360
368
  # For understanding how the algorithm works, see ./doc/random_p.rst
361
- # Also, see VerificationTests in ./devel/test_random.py
369
+ # Also, see VerificationTests in devel/test_random.py
362
370
  r = _Random()
363
371
  limit = 1.0 / (r.K + 1) # lower limit for p
364
372
  self.assertTrue(r.SMALL_P > limit)
@@ -1022,50 +1030,114 @@ class ParityTests(unittest.TestCase, Util):
1022
1030
 
1023
1031
  # ---------------------------------------------------------------------------
1024
1032
 
1025
- class SumIndicesTests(unittest.TestCase, Util):
1033
+ class SumIndicesUtil(unittest.TestCase):
1026
1034
 
1027
- def test_explicit(self):
1028
- for s, r in [("", 0), ("0", 0), ("1", 0), ("11", 1),
1029
- ("011", 3), ("001", 2), ("0001100", 7),
1030
- ("00001111", 22), ("01100111 1101", 49)]:
1031
- a = bitarray(s, self.random_endian())
1032
- self.assertEqual(sum_indices(a), r)
1035
+ def check_explicit(self, S):
1036
+ for s, r1, r2 in [
1037
+ ("", 0, 0), ("0", 0, 0), ("1", 0, 0), ("11", 1, 1),
1038
+ ("011", 3, 5), ("001", 2, 4), ("0001100", 7, 25),
1039
+ ("00001111", 22, 126), ("01100111 1101", 49, 381),
1040
+ ]:
1041
+ for a in [bitarray(s, choice(['little', 'big'])),
1042
+ frozenbitarray(s, choice(['little', 'big']))]:
1043
+ self.assertEqual(S(a, 1), r1)
1044
+ self.assertEqual(S(a, 2), r2)
1045
+ self.assertEqual(a, bitarray(s))
1033
1046
 
1034
- def test_wrong_args(self):
1035
- S = sum_indices
1047
+ def check_wrong_args(self, S):
1036
1048
  self.assertRaises(TypeError, S, '')
1037
- self.assertRaises(TypeError, S, 1)
1049
+ self.assertRaises(TypeError, S, 1.0)
1038
1050
  self.assertRaises(TypeError, S)
1039
- self.assertRaises(TypeError, S, bitarray("110"), 1)
1051
+ for mode in -1, 0, 3, 4:
1052
+ self.assertRaises(ValueError, S, bitarray("110"), mode)
1053
+
1054
+ def check_urandom(self, S, n):
1055
+ a = urandom_2(n)
1056
+ self.assertEqual(S(a, 1), sum(i for i, v in enumerate(a) if v))
1057
+ self.assertEqual(S(a, 2), sum(i * i for i, v in enumerate(a) if v))
1058
+
1059
+ def check_sparse(self, S, n, k, mode=1, freeze=False, inv=False):
1060
+ a = zeros(n, choice(['little', 'big']))
1061
+ self.assertEqual(S(a, mode), 0)
1062
+ self.assertFalse(a.any())
1063
+
1064
+ indices = sample(range(n), k)
1065
+ a[indices] = 1
1066
+ res = sum(indices) if mode == 1 else sum(i * i for i in indices)
1067
+
1068
+ if inv:
1069
+ a.invert()
1070
+ sum_ones = 3 if mode == 1 else 2 * n - 1
1071
+ sum_ones *= n * (n - 1)
1072
+ sum_ones //= 6
1073
+ res = sum_ones - res
1040
1074
 
1041
- def test_ones_small(self):
1075
+ if freeze:
1076
+ a = frozenbitarray(a)
1077
+
1078
+ c = a.copy()
1079
+ self.assertEqual(a.count(), n - k if inv else k)
1080
+ self.assertEqual(S(a, mode), res)
1081
+ self.assertEqual(a, c)
1082
+
1083
+
1084
+ class SSQI_Tests(SumIndicesUtil):
1085
+
1086
+ # Additional tests for _ssqi() in: devel/test_sum_indices.py
1087
+
1088
+ def test_explicit(self):
1089
+ self.check_explicit(_ssqi)
1090
+
1091
+ def test_wrong_args(self):
1092
+ self.check_wrong_args(_ssqi)
1093
+
1094
+ def test_small(self):
1042
1095
  a = bitarray()
1043
- sm = 0
1044
- for i in range(1000):
1045
- a.append(1)
1046
- sm += i
1047
- self.assertEqual(sum_indices(a), sm)
1048
-
1049
- def test_large_ones(self):
1050
- # Note that this will also work on 32-bit machines, even though
1051
- # for n=100_000, we get: n*(n-1)/2 = 4999950000 > 2**32
1052
- n = 100_000
1053
- a = bitarray(n)
1054
- self.assertEqual(sum_indices(a), 0)
1055
- a.setall(1)
1056
- self.assertEqual(sum_indices(a), n * (n - 1) // 2)
1096
+ sm1 = sm2 = 0
1097
+ for i in range(100):
1098
+ v = getrandbits(1)
1099
+ a.append(v)
1100
+ if v:
1101
+ sm1 += i
1102
+ sm2 += i * i
1103
+ self.assertEqual(_ssqi(a, 1), sm1)
1104
+ self.assertEqual(_ssqi(a, 2), sm2)
1057
1105
 
1058
- def test_large_random(self):
1059
- n = 100_000
1060
- a = urandom_2(n)
1061
- self.assertEqual(sum_indices(a),
1062
- sum(i for i, v in enumerate(a) if v))
1106
+ def test_urandom(self):
1107
+ self.check_urandom(_ssqi, 10_037)
1063
1108
 
1064
- def test_random(self):
1065
- for a in self.randombitarrays():
1066
- res = sum_indices(a)
1067
- self.assertEqual(res, sum(i for i, v in enumerate(a) if v))
1068
- self.assertEqual(res, sum(a.search(1)))
1109
+ def test_sparse(self):
1110
+ for _ in range(5):
1111
+ mode = randint(1, 2)
1112
+ freeze = getrandbits(1)
1113
+ inv = getrandbits(1)
1114
+ self.check_sparse(_ssqi, n=1_000_003, k=400,
1115
+ mode=mode, freeze=freeze, inv=inv)
1116
+
1117
+
1118
+ class SumIndicesTests(SumIndicesUtil):
1119
+
1120
+ # Additional tests in: devel/test_sum_indices.py
1121
+
1122
+ def test_explicit(self):
1123
+ self.check_explicit(sum_indices)
1124
+
1125
+ def test_wrong_args(self):
1126
+ self.check_wrong_args(sum_indices)
1127
+
1128
+ def test_ones(self):
1129
+ for mode in 1, 2:
1130
+ self.check_sparse(sum_indices, n=1_600_037, k=0,
1131
+ mode=mode, freeze=True, inv=True)
1132
+
1133
+ def test_sparse(self):
1134
+ for _ in range(20):
1135
+ n = choice([500_029, 600_011]) # below and above block size
1136
+ k = randrange(1_000)
1137
+ mode = randint(1, 2)
1138
+ freeze = getrandbits(1)
1139
+ inv = getrandbits(1)
1140
+ self.check_sparse(sum_indices, n, k, mode, freeze, inv)
1069
1141
 
1070
1142
  # ---------------------------------------------------------------------------
1071
1143
 
@@ -1075,8 +1147,9 @@ class XoredIndicesTests(unittest.TestCase, Util):
1075
1147
  for s, r in [("", 0), ("0", 0), ("1", 0), ("11", 1),
1076
1148
  ("011", 3), ("001", 2), ("0001100", 7),
1077
1149
  ("01100111 1101", 13)]:
1078
- a = bitarray(s, self.random_endian())
1079
- self.assertEqual(xor_indices(a), r)
1150
+ for a in [bitarray(s, self.random_endian()),
1151
+ frozenbitarray(s, self.random_endian())]:
1152
+ self.assertEqual(xor_indices(a), r)
1080
1153
 
1081
1154
  def test_wrong_args(self):
1082
1155
  X = xor_indices
@@ -1099,22 +1172,18 @@ class XoredIndicesTests(unittest.TestCase, Util):
1099
1172
  self.assertEqual(lst[i], x)
1100
1173
 
1101
1174
  def test_large_random(self):
1102
- n = 100_000
1103
- a = urandom_2(n)
1104
- self.assertEqual(
1105
- xor_indices(a),
1106
- reduce(operator.xor, (i for i, v in enumerate(a) if v))
1107
- )
1175
+ n = 10_037
1176
+ for a in [urandom_2(n), frozenbitarray(urandom_2(n))]:
1177
+ res = reduce(operator.xor, (i for i, v in enumerate(a) if v))
1178
+ b = a.copy()
1179
+ self.assertEqual(xor_indices(a), res)
1180
+ self.assertEqual(a, b)
1108
1181
 
1109
1182
  def test_random(self):
1110
1183
  for a in self.randombitarrays():
1111
- cnt = a.count()
1112
- if cnt == 0:
1113
- c = 0
1114
- elif cnt == 1:
1115
- c = a.index(1)
1116
- else:
1117
- c = reduce(operator.xor, (i for i, v in enumerate(a) if v))
1184
+ c = 0
1185
+ for i, v in enumerate(a):
1186
+ c ^= i * v
1118
1187
  self.assertEqual(xor_indices(a), c)
1119
1188
 
1120
1189
  def test_flips(self):
@@ -2239,22 +2308,6 @@ class MixedTests(unittest.TestCase, Util):
2239
2308
  c <<= n
2240
2309
  self.assertEqual(ba2int(c), i << n)
2241
2310
 
2242
- def test_primes(self): # Sieve of Eratosthenes
2243
- sieve = ones(10000)
2244
- sieve[:2] = 0 # zero and one are not prime
2245
- for i in range(2, 100):
2246
- if sieve[i]:
2247
- sieve[i * i::i] = 0
2248
- # primes up to 40
2249
- self.assertEqual(list(sieve.search(1, 0, 40)),
2250
- [2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37])
2251
- # there are 1229 primes between 1 and 10000
2252
- self.assertEqual(sieve.count(1), 1229)
2253
- # there are 119 primes between 4000 and 5000
2254
- self.assertEqual(sieve.count(1, 4000, 5000), 119)
2255
- # the 1000th prime is 7919
2256
- self.assertEqual(count_n(sieve, 1000) - 1, 7919)
2257
-
2258
2311
  # ---------------------------------------------------------------------------
2259
2312
 
2260
2313
  class SerializationTests(unittest.TestCase, Util):
@@ -15,7 +15,7 @@ import random
15
15
  from bitarray import bitarray, bits2bytes
16
16
 
17
17
  from bitarray._util import (
18
- zeros, ones, count_n, parity, sum_indices, xor_indices,
18
+ zeros, ones, count_n, parity, _ssqi, xor_indices,
19
19
  count_and, count_or, count_xor, any_and, subset,
20
20
  correspond_all, byteswap,
21
21
  serialize, deserialize,
@@ -69,6 +69,9 @@ when Python version is too low.
69
69
  raise NotImplementedError("bitarray.util.random_k() requires "
70
70
  "Python 3.9 or higher")
71
71
  r = _Random(__n, endian)
72
+ if not isinstance(k, int):
73
+ raise TypeError("int expected, got '%s'" % type(k).__name__)
74
+
72
75
  return r.random_k(k)
73
76
 
74
77
 
@@ -97,7 +100,7 @@ class _Random:
97
100
  # individually in the test class Random_P_Tests in 'test_util.py'.
98
101
  # The test class also contains many comments and explanations.
99
102
  # To better understand how the algorithm works, see ./doc/random_p.rst
100
- # See also, VerificationTests in ./devel/test_random.py
103
+ # See also, VerificationTests in devel/test_random.py
101
104
 
102
105
  # maximal number of calls to .random_half() in .combine()
103
106
  M = 8
@@ -165,7 +168,7 @@ class _Random:
165
168
  a.invert() # use in-place to avoid copying
166
169
  return a
167
170
 
168
- # decide on sequence, see VerificationTests ./devel/test_random.py
171
+ # decide on sequence, see VerificationTests devel/test_random.py
169
172
  if k < 16 or k * self.K < 3 * n:
170
173
  i = 0
171
174
  else:
@@ -225,7 +228,7 @@ class _Random:
225
228
 
226
229
  # calculate operator sequence
227
230
  i = int(p * self.K)
228
- if p * (self.K + 1) > i + 1: # see ./devel/test_random.py
231
+ if p * (self.K + 1) > i + 1: # see devel/test_random.py
229
232
  i += 1
230
233
  seq = self.op_seq(i)
231
234
  q = i / self.K
@@ -247,6 +250,50 @@ class _Random:
247
250
  return a
248
251
 
249
252
 
253
+ def sum_indices(__a, __mode=1):
254
+ """sum_indices(a, /) -> int
255
+
256
+ Return sum of indices of all active bits in bitarray `a`.
257
+ Equivalent to `sum(i for i, v in enumerate(a) if v)`.
258
+ """
259
+ if __mode not in (1, 2):
260
+ raise ValueError("unexpected mode %r" % __mode)
261
+
262
+ # For details see: devel/test_sum_indices.py
263
+ n = 1 << 19 # block size 512 Kbits
264
+ if len(__a) <= n: # shortcut for single block
265
+ return _ssqi(__a, __mode)
266
+
267
+ # Constants
268
+ m = n // 8 # block size in bytes
269
+ o1 = n * (n - 1) // 2
270
+ o2 = o1 * (2 * n - 1) // 3
271
+
272
+ nblocks = (len(__a) + n - 1) // n
273
+ padbits = __a.padbits
274
+ sm = 0
275
+ for i in range(nblocks):
276
+ # use memoryview to avoid copying memory
277
+ v = memoryview(__a)[i * m : (i + 1) * m]
278
+ block = bitarray(None, __a.endian, buffer=v)
279
+ if padbits and i == nblocks - 1:
280
+ if block.readonly:
281
+ block = bitarray(block)
282
+ block[-padbits:] = 0
283
+
284
+ k = block.count()
285
+ if k:
286
+ y = n * i
287
+ z1 = o1 if k == n else _ssqi(block)
288
+ if __mode == 1:
289
+ sm += k * y + z1
290
+ else:
291
+ z2 = o2 if k == n else _ssqi(block, 2)
292
+ sm += (k * y + 2 * z1) * y + z2
293
+
294
+ return sm
295
+
296
+
250
297
  def pprint(__a, stream=None, group=8, indent=4, width=80):
251
298
  """pprint(bitarray, /, stream=None, group=8, indent=4, width=80)
252
299
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: bitarray
3
- Version: 3.6.0
3
+ Version: 3.6.1
4
4
  Summary: efficient arrays of booleans -- C extension
5
5
  Home-page: https://github.com/ilanschnell/bitarray
6
6
  Author: Ilan Schnell
@@ -83,20 +83,13 @@ versions. Which means you can simply:
83
83
 
84
84
  $ pip install bitarray
85
85
 
86
- In addition, conda packages are available (both the default Anaconda
87
- repository as well as conda-forge support bitarray):
88
-
89
- .. code-block:: shell-session
90
-
91
- $ conda install bitarray
92
-
93
86
  Once you have installed the package, you may want to test it:
94
87
 
95
88
  .. code-block:: shell-session
96
89
 
97
90
  $ python -c 'import bitarray; bitarray.test()'
98
91
  bitarray is installed in: /Users/ilan/bitarray/bitarray
99
- bitarray version: 3.6.0
92
+ bitarray version: 3.6.1
100
93
  sys.version: 3.13.5 (main, Jun 16 2025) [Clang 18.1.8]
101
94
  sys.prefix: /Users/ilan/miniforge
102
95
  pointer size: 64 bit
@@ -110,7 +103,7 @@ Once you have installed the package, you may want to test it:
110
103
  .........................................................................
111
104
  ................................................................
112
105
  ----------------------------------------------------------------------
113
- Ran 589 tests in 0.163s
106
+ Ran 591 tests in 0.163s
114
107
 
115
108
  OK
116
109
 
@@ -427,7 +420,7 @@ and can therefore be used as a dictionary key:
427
420
  Reference
428
421
  =========
429
422
 
430
- bitarray version: 3.6.0 -- `change log <https://github.com/ilanschnell/bitarray/blob/master/doc/changelog.rst>`__
423
+ bitarray version: 3.6.1 -- `change log <https://github.com/ilanschnell/bitarray/blob/master/doc/changelog.rst>`__
431
424
 
432
425
  In the following, ``item`` and ``value`` are usually a single bit -
433
426
  an integer 0 or 1.
@@ -1003,7 +996,7 @@ This sub-module was added in version 1.2.
1003
996
 
1004
997
  ``sum_indices(a, /)`` -> int
1005
998
  Return sum of indices of all active bits in bitarray ``a``.
1006
- This is equivalent to ``sum(i for i, v in enumerate(a) if v)``.
999
+ Equivalent to ``sum(i for i, v in enumerate(a) if v)``.
1007
1000
 
1008
1001
  New in version 3.6
1009
1002
 
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes