Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.8/site-packages/bitstring/bitstring_array.py: 20%
498 statements
« prev ^ index » next coverage.py v7.3.1, created at 2023-09-25 06:15 +0000
« prev ^ index » next coverage.py v7.3.1, created at 2023-09-25 06:15 +0000
1from __future__ import annotations
3import math
4import numbers
5from collections.abc import Sized
6from bitstring.exceptions import CreationError, InterpretError
7from typing import Union, List, Iterable, Any, Optional, BinaryIO, overload, TextIO
8from bitstring.classes import BitArray, Bits, BitsType
9from bitstring.dtypes import Dtype
10from bitstring.utils import tokenparser
11import functools
12import copy
13import array
14import operator
15import io
16import sys
18# The possible types stored in each element of the Array
19ElementType = Union[float, str, int, bytes, bool, Bits]
22class Array:
23 """Return an Array whose elements are initialised according to the fmt string.
24 The dtype string can be typecode as used in the struct module or any fixed-length bitstring
25 format.
27 a = Array('>H', [1, 15, 105])
28 b = Array('int5', [-9, 0, 4])
30 The Array data is stored compactly as a BitArray object and the Array behaves very like
31 a list of items of the given format. Both the Array data and fmt properties can be freely
32 modified after creation. If the data length is not a multiple of the fmt length then the
33 Array will have 'trailing_bits' which will prevent some methods from appending to the
34 Array.
36 Methods:
38 append() -- Append a single item to the end of the Array.
39 byteswap() -- Change byte endianness of all items.
40 count() -- Count the number of occurences of a value.
41 extend() -- Append new items to the end of the Array from an iterable.
42 fromfile() -- Append items read from a file object.
43 insert() -- Insert an item at a given position.
44 pop() -- Remove and return an item.
45 pp() -- Pretty print the Array.
46 reverse() -- Reverse the order of all items.
47 tobytes() -- Return Array data as bytes object, padding with zero bits at the end if needed.
48 tofile() -- Write Array data to a file, padding with zero bits at the end if needed.
49 tolist() -- Return Array items as a list.
51 Special methods:
53 Also available are the operators [], ==, !=, +, *, <<, >>, &, |, ^,
54 plus the mutating operators [], +=, *=, <<=, >>=, &=, |=, ^=.
56 Properties:
58 data -- The BitArray binary data of the Array. Can be freely modified.
59 dtype -- The format string or typecode. Can be freely modified.
60 itemsize -- The length *in bits* of a single item. Read only.
61 trailing_bits -- If the data length is not a multiple of the fmt length, this BitArray
62 gives the leftovers at the end of the data.
65 """
67 def __init__(self, dtype: Union[str, Dtype], initializer: Optional[Union[int, Array, array.array, Iterable, Bits, bytes, bytearray, memoryview, BinaryIO]] = None,
68 trailing_bits: Optional[BitsType] = None) -> None:
69 self.data = BitArray()
70 try:
71 self.dtype = dtype
72 except ValueError as e:
73 raise CreationError(e)
75 if isinstance(initializer, numbers.Integral):
76 self.data = BitArray(initializer * self._dtype.length)
77 elif isinstance(initializer, (Bits, bytes, bytearray, memoryview)):
78 self.data += initializer
79 elif isinstance(initializer, io.BufferedReader):
80 self.fromfile(initializer)
81 elif initializer is not None:
82 self.extend(initializer)
84 if trailing_bits is not None:
85 self.data += BitArray._create_from_bitstype(trailing_bits)
87 @property
88 def itemsize(self) -> int:
89 return self._dtype.length
91 @property
92 def trailing_bits(self) -> BitArray:
93 trailing_bit_length = len(self.data) % self._dtype.length
94 return BitArray() if trailing_bit_length == 0 else self.data[-trailing_bit_length:]
96 # Converting array.array typecodes to our equivalents.
97 _array_typecodes: dict[str, str] = {'b': 'int8',
98 'B': 'uint8',
99 'h': 'intne16',
100 'H': 'uintne16',
101 'l': 'intne32',
102 'L': 'uintne32',
103 'q': 'intne64',
104 'Q': 'uintne64',
105 'e': 'floatne16',
106 'f': 'floatne32',
107 'd': 'floatne64'}
109 @property
110 def dtype(self) -> str:
111 return self._fmt
113 @dtype.setter
114 def dtype(self, new_dtype: Union[str, Dtype]) -> None:
115 if isinstance(new_dtype, Dtype):
116 self._dtype = new_dtype
117 self._fmt = str(self._dtype)
118 else:
119 dtype = Dtype(new_dtype)
120 if dtype.length == 0:
121 raise ValueError(f"A fixed length format is needed for an Array, received '{new_dtype}'.")
122 self._dtype = dtype
123 self._fmt = new_dtype
125 def _create_element(self, value: ElementType) -> Bits:
126 """Create Bits from value according to the token_name and token_length"""
127 b = Bits()
128 self._dtype.set(b, value)
129 if len(b) != self._dtype.length:
130 raise ValueError(f"The value {value!r} has the wrong length for the format '{self._fmt}'.")
131 return b
133 def __len__(self) -> int:
134 return len(self.data) // self._dtype.length
136 @overload
137 def __getitem__(self, key: slice) -> Array:
138 ...
140 @overload
141 def __getitem__(self, key: int) -> ElementType:
142 ...
144 def __getitem__(self, key: Union[slice, int]) -> Union[Array, ElementType]:
145 if isinstance(key, slice):
146 start, stop, step = key.indices(len(self))
147 if step != 1:
148 d = BitArray()
149 for s in range(start * self._dtype.length, stop * self._dtype.length, step * self._dtype.length):
150 d.append(self.data[s: s + self._dtype.length])
151 a = Array(self._dtype)
152 a.data = d
153 return a
154 else:
155 a = Array(self._dtype)
156 a.data = self.data[start * self._dtype.length: stop * self._dtype.length]
157 return a
158 else:
159 if key < 0:
160 key += len(self)
161 if key < 0 or key >= len(self):
162 raise IndexError(f"Index {key} out of range for Array of length {len(self)}.")
163 return self._dtype.get(self.data, start=self._dtype.length * key)
165 @overload
166 def __setitem__(self, key: slice, value: Iterable[ElementType]) -> None:
167 ...
169 @overload
170 def __setitem__(self, key: int, value: ElementType) -> None:
171 ...
173 def __setitem__(self, key: Union[slice, int], value: Union[Iterable[ElementType], ElementType]) -> None:
174 if isinstance(key, slice):
175 start, stop, step = key.indices(len(self))
176 if not isinstance(value, Iterable):
177 raise TypeError("Can only assign an iterable to a slice.")
178 if step == 1:
179 new_data = BitArray()
180 for x in value:
181 new_data += self._create_element(x)
182 self.data[start * self._dtype.length: stop * self._dtype.length] = new_data
183 return
184 items_in_slice = len(range(start, stop, step))
185 if not isinstance(value, Sized):
186 value = list(value)
187 if len(value) == items_in_slice:
188 for s, v in zip(range(start, stop, step), value):
189 self.data.overwrite(self._create_element(v), s * self._dtype.length)
190 else:
191 raise ValueError(f"Can't assign {len(value)} values to an extended slice of length {stop - start}.")
192 else:
193 if key < 0:
194 key += len(self)
195 if key < 0 or key >= len(self):
196 raise IndexError(f"Index {key} out of range for Array of length {len(self)}.")
197 start = self._dtype.length * key
198 self.data.overwrite(self._create_element(value), start)
199 return
201 def __delitem__(self, key: Union[slice, int]) -> None:
202 if isinstance(key, slice):
203 start, stop, step = key.indices(len(self))
204 if step == 1:
205 self.data.__delitem__(slice(start * self._dtype.length, stop * self._dtype.length))
206 return
207 # We need to delete from the end or the earlier positions will change
208 r = reversed(range(start, stop, step)) if step > 0 else range(start, stop, step)
209 for s in r:
210 self.data.__delitem__(slice(s * self._dtype.length, (s + 1) * self._dtype.length))
211 else:
212 if key < 0:
213 key += len(self)
214 if key < 0 or key >= len(self):
215 raise IndexError
216 start = self._dtype.length * key
217 del self.data[start: start + self._dtype.length]
219 def __repr__(self) -> str:
220 list_str = f"{self.tolist()}"
221 trailing_bit_length = len(self.data) % self._dtype.length
222 final_str = "" if trailing_bit_length == 0 else ", trailing_bits=" + repr(
223 self.data[-trailing_bit_length:])
224 return f"Array('{self._fmt}', {list_str}{final_str})"
226 def astype(self, dtype: Union[str, Dtype]) -> Array:
227 """Return Array with elements of new dtype, initialised from current Array."""
228 new_array = Array(dtype, self.tolist())
229 return new_array
231 def tolist(self) -> List[ElementType]:
232 return [self._dtype.get(self.data, start=start)
233 for start in range(0, len(self.data) - self._dtype.length + 1, self._dtype.length)]
235 def append(self, x: ElementType) -> None:
236 if len(self.data) % self._dtype.length != 0:
237 raise ValueError("Cannot append to Array as its length is not a multiple of the format length.")
238 self.data += self._create_element(x)
240 def extend(self, iterable: Union[Array, array.array, Iterable]) -> None:
241 if len(self.data) % self._dtype.length != 0:
242 raise ValueError(f"Cannot extend Array as its data length ({len(self.data)} bits) is not a multiple of the format length ({self._dtype.length} bits).")
243 if isinstance(iterable, Array):
244 if self._dtype.name != iterable._dtype.name or self._dtype.length != iterable._dtype.length:
245 raise TypeError(
246 f"Cannot extend an Array with format '{self._fmt}' from an Array of format '{iterable._fmt}'.")
247 # No need to iterate over the elements, we can just append the data
248 self.data.append(iterable.data)
249 elif isinstance(iterable, array.array):
250 other_fmt = Array._array_typecodes.get(iterable.typecode, iterable.typecode)
251 token_name, token_length, _ = tokenparser(other_fmt)[1][0]
252 if self._dtype.name != token_name or self._dtype.length != token_length:
253 raise ValueError(
254 f"Cannot extend an Array with format '{self._fmt}' from an array with typecode '{iterable.typecode}'.")
255 self.data += iterable.tobytes()
256 else:
257 if isinstance(iterable, str):
258 raise TypeError("Can't extend an Array with a str.")
259 for item in iterable:
260 self.data += self._create_element(item)
262 def insert(self, i: int, x: ElementType) -> None:
263 """Insert a new element into the Array at position i.
265 """
266 i = min(i, len(self)) # Inserting beyond len of array inserts at the end (copying standard behaviour)
267 self.data.insert(self._create_element(x), i * self._dtype.length)
269 def pop(self, i: int = -1) -> ElementType:
270 """Return and remove an element of the Array.
272 Default is to return and remove the final element.
274 """
275 if len(self) == 0:
276 raise IndexError("Can't pop from an empty Array.")
277 x = self[i]
278 del self[i]
279 return x
281 def byteswap(self) -> None:
282 """Change the endianness in-place of all items in the Array.
284 If the Array format is not a whole number of bytes a ValueError will be raised.
286 """
287 if self._dtype.length % 8 != 0:
288 raise ValueError(
289 f"byteswap can only be used for whole-byte elements. The '{self._fmt}' format is {self._dtype.length} bits long.")
290 self.data.byteswap(self.itemsize // 8)
292 def count(self, value: ElementType) -> int:
293 """Return count of Array items that equal value.
295 value -- The quantity to compare each Array element to. Type should be appropriate for the Array format.
297 For floating point types using a value of float('nan') will count the number of elements that are NaN.
299 """
300 if math.isnan(value):
301 return sum(math.isnan(i) for i in self)
302 else:
303 return sum(i == value for i in self)
305 def tobytes(self) -> bytes:
306 """Return the Array data as a bytes object, padding with zero bits if needed.
308 Up to seven zero bits will be added at the end to byte align.
310 """
311 return self.data.tobytes()
313 def tofile(self, f: BinaryIO) -> None:
314 """Write the Array data to a file object, padding with zero bits if needed.
316 Up to seven zero bits will be added at the end to byte align.
318 """
319 self.data.tofile(f)
321 def fromfile(self, f: BinaryIO, n: Optional[int] = None) -> None:
322 trailing_bit_length = len(self.data) % self._dtype.length
323 if trailing_bit_length != 0:
324 raise ValueError(f"Cannot extend Array as its data length ({len(self.data)} bits) is not a multiple of the format length ({self._dtype.length} bits).")
326 new_data = Bits(f)
327 max_items = len(new_data) // self._dtype.length
328 items_to_append = max_items if n is None else min(n, max_items)
329 self.data += new_data[0: items_to_append * self._dtype.length]
330 if n is not None and items_to_append < n:
331 raise EOFError(f"Only {items_to_append} were appended, not the {n} items requested.")
333 def reverse(self) -> None:
334 trailing_bit_length = len(self.data) % self._dtype.length
335 if trailing_bit_length != 0:
336 raise ValueError(f"Cannot reverse the items in the Array as its data length ({len(self.data)} bits) is not a multiple of the format length ({self._dtype.length} bits).")
337 for start_bit in range(0, len(self.data) // 2, self._dtype.length):
338 start_swap_bit = len(self.data) - start_bit - self._dtype.length
339 temp = self.data[start_bit: start_bit + self._dtype.length]
340 self.data[start_bit: start_bit + self._dtype.length] = self.data[
341 start_swap_bit: start_swap_bit + self._dtype.length]
342 self.data[start_swap_bit: start_swap_bit + self._dtype.length] = temp
344 def pp(self, fmt: Optional[str] = None, width: int = 120,
345 show_offset: bool = False, stream: TextIO = sys.stdout) -> None:
346 """Pretty-print the Array contents.
348 fmt -- Data format string. Defaults to current Array fmt.
349 width -- Max width of printed lines in characters. Defaults to 120. A single group will always
350 be printed per line even if it exceeds the max width.
351 show_offset -- If True shows the element offset in the first column of each line.
352 stream -- A TextIO object with a write() method. Defaults to sys.stdout.
354 """
355 sep = ' '
356 fmt_is_dtype = False
357 if fmt is None:
358 fmt = self.dtype
359 fmt_is_dtype = True
361 tokens = tokenparser(fmt)[1]
362 token_names_and_lengths = [(x[0], x[1]) for x in tokens]
363 if len(token_names_and_lengths) not in [1, 2]:
364 raise ValueError(
365 f"Only one or two tokens can be used in an Array.pp() format - '{fmt}' has {len(token_names_and_lengths)} tokens.")
366 token_name, token_length = token_names_and_lengths[0]
367 token_name2, token_length2 = None, None
368 getter_func2 = None
369 if len(token_names_and_lengths) == 1:
370 if token_length is None:
371 token_length = self.itemsize
372 fmt += str(token_length)
373 if len(token_names_and_lengths) == 2:
374 token_name2, token_length2 = token_names_and_lengths[1]
375 if token_length is None and token_length2 is None:
376 token_length = token_length2 = self.itemsize
377 fmt += str(token_length)
378 if token_length is None:
379 token_length = token_length2
380 if token_length2 is None:
381 token_length2 = token_length
382 if token_length != token_length2:
383 raise ValueError(f"Two different format lengths specified ('{fmt}'). Either specify just one, or two the same length.")
384 getter_func2 = functools.partial(Bits._name_to_read[token_name2], length=token_length2)
386 getter_func = functools.partial(Bits._name_to_read[token_name], length=token_length)
388 # Check that the getter functions will work
389 temp = BitArray(token_length)
390 try:
391 getter_func(temp, 0)
392 except InterpretError as e:
393 raise ValueError(f"Pretty print format not valid: {e.msg}")
394 if token_name2 is not None:
395 try:
396 getter_func2(temp, 0)
397 except InterpretError as e:
398 raise ValueError(f"Pretty print format not valid: {e.msg}")
400 trailing_bit_length = len(self.data) % token_length
401 format_sep = " : " # String to insert on each line between multiple formats
403 if trailing_bit_length == 0:
404 data = self.data
405 else:
406 data = self.data[0: -trailing_bit_length]
407 length = len(self.data) // token_length
408 parameter_name = "dtype" if fmt_is_dtype else "fmt"
409 stream.write(f"<Array {parameter_name}='{fmt}', length={length}, itemsize={token_length} bits, total data size={(len(self.data) + 7) // 8} bytes>\n[\n")
410 data._pp(token_name, token_name2, token_length, width, sep, format_sep, show_offset, stream, False, token_length, getter_func, getter_func2)
411 stream.write("]")
412 if trailing_bit_length != 0:
413 stream.write(" + trailing_bits = " + str(self.data[-trailing_bit_length:]))
414 stream.write("\n")
416 def equals(self, other: Any) -> bool:
417 """Return True if format and all Array items are equal."""
418 if isinstance(other, Array):
419 if self._dtype.length != other._dtype.length:
420 return False
421 if self._dtype.name != other._dtype.name:
422 return False
423 if self.data != other.data:
424 return False
425 return True
426 elif isinstance(other, array.array):
427 # Assume we are comparing with an array type
428 if self.trailing_bits:
429 return False
430 # array's itemsize is in bytes, not bits.
431 if self.itemsize != other.itemsize * 8:
432 return False
433 if len(self) != len(other):
434 return False
435 if self.tolist() != other.tolist():
436 return False
437 return True
438 return False
440 def __iter__(self) -> Iterable[ElementType]:
441 start = 0
442 for _ in range(len(self)):
443 yield self._dtype.get(self.data, start=start)
444 start += self._dtype.length
446 def __copy__(self) -> Array:
447 a_copy = Array(self._fmt)
448 a_copy.data = copy.copy(self.data)
449 return a_copy
451 def _apply_op_to_all_elements(self, op, value: Union[int, float, None], is_comparison: bool = False) -> Array:
452 """Apply op with value to each element of the Array and return a new Array"""
453 new_array = Array('bool' if is_comparison else self._dtype)
454 new_data = BitArray()
455 failures = index = 0
456 msg = ''
457 if value is not None:
458 def partial_op(a):
459 return op(a, value)
460 else:
461 def partial_op(a):
462 return op(a)
463 for i in range(len(self)):
464 v = self._dtype.get(self.data, start=self._dtype.length * i)
465 try:
466 new_data.append(new_array._create_element(partial_op(v)))
467 except (CreationError, ZeroDivisionError, ValueError) as e:
468 if failures == 0:
469 msg = str(e)
470 index = i
471 failures += 1
472 if failures != 0:
473 raise ValueError(f"Applying operator '{op.__name__}' to Array caused {failures} errors. "
474 f'First error at index {index} was: "{msg}"')
475 new_array.data = new_data
476 return new_array
478 def _apply_op_to_all_elements_inplace(self, op, value: Union[int, float]) -> Array:
479 """Apply op with value to each element of the Array in place."""
480 # This isn't really being done in-place, but it's simpler and faster for now?
481 new_data = BitArray()
482 failures = index = 0
483 msg = ''
484 for i in range(len(self)):
485 v = self._dtype.get(self.data, start=self._dtype.length * i)
486 try:
487 new_data.append(self._create_element(op(v, value)))
488 except (CreationError, ZeroDivisionError, ValueError) as e:
489 if failures == 0:
490 msg = str(e)
491 index = i
492 failures += 1
493 if failures != 0:
494 raise ValueError(f"Applying operator '{op.__name__}' to Array caused {failures} errors. "
495 f'First error at index {index} was: "{msg}"')
496 self.data = new_data
497 return self
499 def _apply_bitwise_op_to_all_elements(self, op, value: BitsType) -> Array:
500 """Apply op with value to each element of the Array as an unsigned integer and return a new Array"""
501 a_copy = self[:]
502 a_copy._apply_bitwise_op_to_all_elements_inplace(op, value)
503 return a_copy
505 def _apply_bitwise_op_to_all_elements_inplace(self, op, value: BitsType) -> Array:
506 """Apply op with value to each element of the Array as an unsigned integer in place."""
507 value = BitArray._create_from_bitstype(value)
508 if len(value) != self._dtype.length:
509 raise ValueError(f"Bitwise op needs a bitstring of length {self._dtype.length} to match format {self._fmt}.")
510 for start in range(0, len(self) * self._dtype.length, self._dtype.length):
511 self.data[start: start + self._dtype.length] = op(self.data[start: start + self._dtype.length], value)
512 return self
514 def _apply_op_between_arrays(self, op, other: Array, is_comparison: bool = False) -> Array:
515 if len(self) != len(other):
516 msg = f"Cannot operate element-wise on Arrays with different lengths ({len(self)} and {len(other)})."
517 if op == operator.add or op == operator.iadd:
518 msg += " Use extend() if you want to concatenate Arrays."
519 raise ValueError(msg)
520 if is_comparison:
521 new_type = Dtype('bool')
522 else:
523 new_type = self._promotetype(self._dtype, other._dtype)
524 new_array = Array(new_type)
525 new_data = BitArray()
526 failures = index = 0
527 msg = ''
528 for i in range(len(self)):
529 a = self._dtype.get(self.data, start=self._dtype.length * i)
530 b = other._dtype.get(other.data, start=other._dtype.length * i)
531 try:
532 new_data.append(new_array._create_element(op(a, b)))
533 except (CreationError, ValueError, ZeroDivisionError) as e:
534 if failures == 0:
535 msg = str(e)
536 index = i
537 failures += 1
538 if failures != 0:
539 raise ValueError(f"Applying operator '{op.__name__}' between Arrays caused {failures} errors. "
540 f'First error at index {index} was: "{msg}"')
541 new_array.data = new_data
542 return new_array
544 @classmethod
545 def _promotetype(cls, type1: Dtype, type2: Dtype) -> Dtype:
546 """When combining types which one wins?
548 1. We only deal with types representing floats or integers.
549 2. One of the two types gets returned. We never create a new one.
550 3. Floating point types always win against integer types.
551 4. Signed integer types always win against unsigned integer types.
552 5. Longer types win against shorter types.
553 6. In a tie the first type wins against the second type.
555 """
556 if type1.is_float + type1.is_integer + type2.is_float + type2.is_integer != 2:
557 raise ValueError(f"Only integer and floating point types can be combined - not '{type1}' and '{type2}'.")
558 # If same type choose the widest
559 if type1.name == type2.name:
560 return type1 if type1.length > type2.length else type2
561 # We choose floats above integers, irrespective of the widths
562 if type1.is_float and type2.is_integer:
563 return type1
564 if type1.is_integer and type2.is_float:
565 return type2
566 if type1.is_float and type2.is_float:
567 return type2 if type2.length > type1.length else type1
568 assert type1.is_integer and type2.is_integer
569 if type1.is_signed and not type2.is_signed:
570 return type1
571 if type2.is_signed and not type1.is_signed:
572 return type2
573 return type2 if type2.length > type1.length else type1
575 # Operators between Arrays or an Array and scalar value
577 def __add__(self, other: Union[int, float, Array]) -> Array:
578 """Add int or float to all elements."""
579 if isinstance(other, Array):
580 return self._apply_op_between_arrays(operator.add, other)
581 return self._apply_op_to_all_elements(operator.add, other)
583 def __iadd__(self, other: Union[int, float, Array]) -> Array:
584 if isinstance(other, Array):
585 return self._apply_op_between_arrays(operator.add, other)
586 return self._apply_op_to_all_elements_inplace(operator.add, other)
588 def __isub__(self, other: Union[int, float, Array]) -> Array:
589 if isinstance(other, Array):
590 return self._apply_op_between_arrays(operator.sub, other)
591 return self._apply_op_to_all_elements_inplace(operator.sub, other)
593 def __sub__(self, other: Union[int, float, Array]) -> Array:
594 if isinstance(other, Array):
595 return self._apply_op_between_arrays(operator.sub, other)
596 return self._apply_op_to_all_elements(operator.sub, other)
598 def __mul__(self, other: Union[int, float, Array]) -> Array:
599 if isinstance(other, Array):
600 return self._apply_op_between_arrays(operator.mul, other)
601 return self._apply_op_to_all_elements(operator.mul, other)
603 def __imul__(self, other: Union[int, float, Array]) -> Array:
604 if isinstance(other, Array):
605 return self._apply_op_between_arrays(operator.mul, other)
606 return self._apply_op_to_all_elements_inplace(operator.mul, other)
608 def __floordiv__(self, other: Union[int, float, Array]) -> Array:
609 if isinstance(other, Array):
610 return self._apply_op_between_arrays(operator.floordiv, other)
611 return self._apply_op_to_all_elements(operator.floordiv, other)
613 def __ifloordiv__(self, other: Union[int, float, Array]) -> Array:
614 if isinstance(other, Array):
615 return self._apply_op_between_arrays(operator.floordiv, other)
616 return self._apply_op_to_all_elements_inplace(operator.floordiv, other)
618 def __truediv__(self, other: Union[int, float, Array]) -> Array:
619 if isinstance(other, Array):
620 return self._apply_op_between_arrays(operator.truediv, other)
621 return self._apply_op_to_all_elements(operator.truediv, other)
623 def __itruediv__(self, other: Union[int, float, Array]) -> Array:
624 if isinstance(other, Array):
625 return self._apply_op_between_arrays(operator.truediv, other)
626 return self._apply_op_to_all_elements_inplace(operator.truediv, other)
628 def __rshift__(self, other: Union[int, Array]) -> Array:
629 if isinstance(other, Array):
630 return self._apply_op_between_arrays(operator.rshift, other)
631 return self._apply_op_to_all_elements(operator.rshift, other)
633 def __lshift__(self, other: Union[int, Array]) -> Array:
634 if isinstance(other, Array):
635 return self._apply_op_between_arrays(operator.lshift, other)
636 return self._apply_op_to_all_elements(operator.lshift, other)
638 def __irshift__(self, other: Union[int, Array]) -> Array:
639 if isinstance(other, Array):
640 return self._apply_op_between_arrays(operator.rshift, other)
641 return self._apply_op_to_all_elements_inplace(operator.rshift, other)
643 def __ilshift__(self, other: Union[int, Array]) -> Array:
644 if isinstance(other, Array):
645 return self._apply_op_between_arrays(operator.lshift, other)
646 return self._apply_op_to_all_elements_inplace(operator.lshift, other)
648 def __mod__(self, other: Union[int, Array]) -> Array:
649 if isinstance(other, Array):
650 return self._apply_op_between_arrays(operator.mod, other)
651 return self._apply_op_to_all_elements(operator.mod, other)
653 def __imod__(self, other: Union[int, Array]) -> Array:
654 if isinstance(other, Array):
655 return self._apply_op_between_arrays(operator.mod, other)
656 return self._apply_op_to_all_elements_inplace(operator.mod, other)
658 # Bitwise operators
660 def __and__(self, other: BitsType) -> Array:
661 return self._apply_bitwise_op_to_all_elements(operator.iand, other)
663 def __iand__(self, other: BitsType) -> Array:
664 return self._apply_bitwise_op_to_all_elements_inplace(operator.iand, other)
666 def __or__(self, other: BitsType) -> Array:
667 return self._apply_bitwise_op_to_all_elements(operator.ior, other)
669 def __ior__(self, other: BitsType) -> Array:
670 return self._apply_bitwise_op_to_all_elements_inplace(operator.ior, other)
672 def __xor__(self, other: BitsType) -> Array:
673 return self._apply_bitwise_op_to_all_elements(operator.ixor, other)
675 def __ixor__(self, other: BitsType) -> Array:
676 return self._apply_bitwise_op_to_all_elements_inplace(operator.ixor, other)
678 # Reverse operators between a scalar value and an Array
680 def __rmul__(self, other: Union[int, float]) -> Array:
681 return self._apply_op_to_all_elements(operator.mul, other)
683 def __radd__(self, other: Union[int, float]) -> Array:
684 return self._apply_op_to_all_elements(operator.add, other)
686 def __rsub__(self, other: Union[int, float]) -> Array:
687 # i - A == (-A) + i
688 neg = self._apply_op_to_all_elements(operator.neg, None)
689 return neg._apply_op_to_all_elements(operator.add, other)
691 def __rand__(self, other: BitsType) -> Array:
692 return self._apply_bitwise_op_to_all_elements(operator.iand, other)
694 def __ror__(self, other: BitsType) -> Array:
695 return self._apply_bitwise_op_to_all_elements(operator.ior, other)
697 def __rxor__(self, other: BitsType) -> Array:
698 return self._apply_bitwise_op_to_all_elements(operator.ixor, other)
700 # Comparison operators
702 def __lt__(self, other: Union[int, float, Array]) -> Array:
703 if isinstance(other, Array):
704 return self._apply_op_between_arrays(operator.lt, other, is_comparison=True)
705 return self._apply_op_to_all_elements(operator.lt, other, is_comparison=True)
707 def __gt__(self, other: Union[int, float, Array]) -> Array:
708 if isinstance(other, Array):
709 return self._apply_op_between_arrays(operator.gt, other, is_comparison=True)
710 return self._apply_op_to_all_elements(operator.gt, other, is_comparison=True)
712 def __ge__(self, other: Union[int, float, Array]) -> Array:
713 if isinstance(other, Array):
714 return self._apply_op_between_arrays(operator.ge, other, is_comparison=True)
715 return self._apply_op_to_all_elements(operator.ge, other, is_comparison=True)
717 def __le__(self, other: Union[int, float, Array]) -> Array:
718 if isinstance(other, Array):
719 return self._apply_op_between_arrays(operator.le, other, is_comparison=True)
720 return self._apply_op_to_all_elements(operator.le, other, is_comparison=True)
722 def __eq__(self, other: Union[int, float, str, BitsType, Array]) -> Array:
723 if isinstance(other, Array):
724 return self._apply_op_between_arrays(operator.eq, other, is_comparison=True)
725 return self._apply_op_to_all_elements(operator.eq, other, is_comparison=True)
727 def __ne__(self, other: Union[int, float, str, BitsType, Array]) -> Array:
728 if isinstance(other, Array):
729 return self._apply_op_between_arrays(operator.ne, other, is_comparison=True)
730 return self._apply_op_to_all_elements(operator.ne, other, is_comparison=True)
732 # Unary operators
734 def __neg__(self):
735 return self._apply_op_to_all_elements(operator.neg, None)
737 def __abs__(self):
738 return self._apply_op_to_all_elements(operator.abs, None)