• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

Nic30 / pyMathBitPrecise / 39929cba-065a-48de-a4b5-055482f40943

13 May 2026 03:45PM UTC coverage: 68.05% (+1.3%) from 66.747%
39929cba-065a-48de-a4b5-055482f40943

push

circleci

Nic30
style: typing, better err msg

241 of 406 branches covered (59.36%)

Branch coverage included in aggregate %.

2 of 2 new or added lines in 1 file covered. (100.0%)

160 existing lines in 2 files now uncovered.

907 of 1281 relevant lines covered (70.8%)

0.71 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

73.29
/pyMathBitPrecise/bits3t.py
1
#!/usr/bin/env python3
2
# -*- coding: UTF-8 -*-
3

4
from copy import copy
1✔
5
from enum import Enum
1✔
6
from math import log2, ceil
1✔
7
from operator import le, ge, gt, lt, ne, eq, and_, or_, xor, sub, add
1✔
8
from typing import Union, Optional, Callable, Self, Literal
1✔
9

10
from pyMathBitPrecise.array3t import Array3t
1✔
11
from pyMathBitPrecise.bit_utils import mask, get_bit, get_bit_range, \
1✔
12
    to_signed, set_bit_range, bit_set_to, bit_field, to_unsigned, INT_BASES, \
13
    ValidityError, normalize_slice, rotate_right, rotate_left
14
from pyMathBitPrecise.bits3t_vld_masks import vld_mask_for_xor, vld_mask_for_and, \
1✔
15
    vld_mask_for_or
16

17

18
class _NOT_SPECIFIED:
1✔
19

20
    def __init__(self):
1✔
21
        raise AssertionError("This class should be used as a constant")
22

23

24
class Bits3t():
1✔
25
    """
26
    Meta type for integer of specified size where
27
    each bit can be '1', '0' or 'X' for undefined value.
28

29
    :ivar ~.bit_length: number representation of value of this type
30
    :ivar ~.signed: flag which tells if this type is signed or not
31
    :ivar ~._all_mask: cached value of mask for all bits
32
    :ivar ~.name: name for annotation
33
    :ivar ~.force_vector: use always hdl vector type
34
            (for example std_logic_vector(0 downto 0)
35
             instead of std_logic in VHDL,
36
             wire[1] instead of wire)
37
    """
38

39
    def __init__(self, bit_length: int, signed:Optional[bool]=False, name: Optional[str]=None,
1✔
40
                 force_vector=False):
41
        if force_vector and bit_length != 1:
1!
UNCOV
42
            assert bit_length == 1, "force_vector=True is appliable only for 1b values"
×
43
        self._bit_length = bit_length
1✔
44
        self.signed = signed
1✔
45
        self._all_mask = mask(bit_length)
1✔
46
        self.name = name
1✔
47
        self.force_vector = force_vector
1✔
48

49
    def _createMutated(self,
1✔
50
            bit_length: int=_NOT_SPECIFIED,
51
            signed:Optional[bool]=_NOT_SPECIFIED, name: Optional[str]=_NOT_SPECIFIED,
52
            force_vector=_NOT_SPECIFIED,
53
            ) -> Self:
54
        if bit_length is _NOT_SPECIFIED:
1✔
55
            bit_length = self._bit_length
1✔
56
        if signed is  _NOT_SPECIFIED:
1✔
57
            signed = self.signed
1✔
58
        if name  is _NOT_SPECIFIED:
1!
59
            name = self.name
1✔
60
        if force_vector is _NOT_SPECIFIED:
1!
61
            force_vector = self.force_vector
1✔
62
        return self.__class__(bit_length, signed=signed, name=name, force_vector=force_vector)
1✔
63

64
    def __copy__(self) -> Self:
1✔
UNCOV
65
        t = self._createMutated()
×
UNCOV
66
        return t
×
67

68
    def all_mask(self) -> int:
1✔
69
        """
70
        :return: mask for bites of this type ( 0b111 for Bits(3) )
71
        """
72
        return self._all_mask
1✔
73

74
    def get_domain_range(self):
1✔
75
        """
76
        equvalent of (get_min_value(), get_max_value())
77
        :note: as an independent function from performace reasons
78
        """
79
        m = self._all_mask
1✔
80
        if self.signed:
1✔
81
            intMin = -(m // 2) - 1
1✔
82
            intMax = m // 2
1✔
83
        else:
84
            intMin = 0
1✔
85
            intMax = m
1✔
86

87
        return (intMin, intMax)
1✔
88

89
    def get_min_value(self):
1✔
UNCOV
90
        m = self._all_mask
×
UNCOV
91
        if self.signed:
×
UNCOV
92
            intMin = -(m // 2) - 1
×
93
        else:
UNCOV
94
            intMin = 0
×
UNCOV
95
        return intMin
×
96

97
    def get_max_value(self):
1✔
UNCOV
98
        m = self._all_mask
×
UNCOV
99
        if self.signed:
×
UNCOV
100
            intMax = m // 2
×
101
        else:
UNCOV
102
            intMax = m
×
UNCOV
103
        return intMax
×
104

105
    def bit_length(self) -> int:
1✔
106
        """
107
        :return: number of bits required for representation
108
            of value of this type
109
        """
110
        return self._bit_length
1✔
111

112
    def __eq__(self, other) -> bool:
1✔
UNCOV
113
        return (self is other
×
114
                or (isinstance(other, Bits3t)
115
                    and self._bit_length == other._bit_length
116
                    and self.name == other.name
117
                    and self.force_vector == other.force_vector
118
                    and self.signed == other.signed
119
                    )
120
                )
121

122
    def _normalize_val_and_mask(self, val: Union[int, None, bytes, Self], vld_mask: Optional[int]) -> tuple[int, int]:
1✔
123
        if val is None:
1✔
124
            vld = 0
1✔
125
            val = 0
1✔
126
            assert vld_mask is None or vld_mask == 0
1✔
127
        else:
128
            all_mask = self.all_mask()
1✔
129
            w = self._bit_length
1✔
130
            if isinstance(val, int):
1✔
131
                val = int(val)  # to cover for bool
1✔
132
            elif isinstance(val, bytes):
1!
133
                val = int.from_bytes(
×
134
                    val, byteorder="little", signed=bool(self.signed))
135
            elif isinstance(val, str):
1✔
136
                if not (val.startswith("0") and len(val) > 2):
1!
UNCOV
137
                    raise ValueError(val)
×
138

139
                base = INT_BASES[val[1]]
1✔
140
                try:
1✔
141
                    _val = int(val[2:], base)
1✔
142
                except ValueError:
1✔
143
                    _val = None
1✔
144

145
                if _val is None:
1!
146
                    assert vld_mask is None
1✔
147
                    val = val.lower()
1✔
148
                    if base == 10 and "x" in val:
1✔
149
                        raise NotImplementedError()
150
                    v = 0
1✔
151
                    m = 0
1✔
152
                    bits_per_char = ceil(log2(base))
1✔
153
                    char_mask = mask(bits_per_char)
1✔
154
                    for digit in val[2:]:
1✔
155
                        v <<= bits_per_char
1✔
156
                        m <<= bits_per_char
1✔
157
                        if digit == "x":
1✔
158
                            pass
1✔
159
                        else:
160
                            m |= char_mask
1✔
161
                            v |= int(digit, base)
1✔
162
                    val = v
1✔
163
                    vld_mask = m
1✔
164
                else:
UNCOV
165
                    val = _val
×
166
            elif isinstance(val, Bits3val):
1✔
167
                assert vld_mask is None or vld_mask == val.vld_mask, (
1✔
168
                    "vld_mask for initialization from Bits3val should not be specified or"
169
                    " should be same as val.vld_mask to prevent ambiguity", val, vld_mask)
170
                vld_mask = val.vld_mask
1✔
171
                val = val.val
1✔
172
            else:
173
                try:
1✔
174
                    val = int(val)
1✔
175
                except TypeError as e:
1✔
176
                    if isinstance(val, Enum):
1!
UNCOV
177
                        val = int(val.value)
×
178
                    else:
179
                        raise e
1✔
180

181
            if vld_mask is None:
1✔
182
                vld = all_mask
1✔
183
            else:
184
                if vld_mask > all_mask or vld_mask < 0:
1!
UNCOV
185
                    raise ValueError("Mask in incorrect format", vld_mask, w, all_mask)
×
186
                vld = vld_mask
1✔
187

188
            if val < 0:
1✔
189
                if not self.signed:
1✔
190
                    raise ValueError("Negative value for unsigned int")
1✔
191
                _val = to_signed(val & all_mask, w)
1✔
192
                if _val != val:
1✔
193
                    raise ValueError("Too large value", val, _val)
1✔
194
                val = to_unsigned(_val, w)
1✔
195
            else:
196
                if self.signed:
1✔
197
                    msb = 1 << (w - 1)
1✔
198
                    if msb & val:
1✔
199
                        if val > 0:
1!
200
                            raise ValueError(
1✔
201
                                "Value too large to fit in this type", val)
202

203
                if val & all_mask != val:
1✔
204
                    raise ValueError(
1✔
205
                        "Not enough bits to represent value",
206
                        val, "on", w, "bit" if w == 1 else "bits", val & all_mask)
207
                val = val & vld
1✔
208
        return val, vld
1✔
209

210
    def _from_py(self, val: int, vld_mask: int) -> "Bits3val":
1✔
211
        """
212
        from_py without normalization
213
        """
214
        return Bits3val(self, val, vld_mask)
1✔
215

216
    def from_py(self, val: Union[int, bytes, str, Enum],
1✔
217
                vld_mask: Optional[int]=None) -> "Bits3val":
218
        """
219
        Construct value from pythonic value
220
        :note: str value has to start with base specifier (0b, 0h)
221
            and is much slower than the value specified
222
            by 'val' and 'vld_mask'. Does support x.
223
        """
224
        val, vld_mask = self._normalize_val_and_mask(val, vld_mask)
1✔
225
        return Bits3val(self, val, vld_mask)
1✔
226

227
    def __getitem__(self, i):
1✔
228
        ":return: an item from this array"
229
        return Array3t(self, i)
1✔
230

231
    def __hash__(self):
1✔
UNCOV
232
        return hash((
×
233
            self._bit_length,
234
            self.signed,
235
            self._all_mask,
236
            self.name,
237
            self.force_vector,
238
        ))
239

240
    def __repr__(self):
241
        """
242
        :param indent: number of indentation
243
        :param withAddr: if is not None is used as a additional
244
            information about on which address this type is stored
245
            (used only by HStruct)
246
        :param expandStructs: expand HStructTypes (used by HStruct and HArray)
247
        """
248
        constr = []
249
        if self.name is not None:
250
            constr.append(f'"{self.name:s}"')
251
        c = self.bit_length()
252

253
        if self.signed:
254
            sign = "i"
255
        elif self.signed is False:
256
            sign = "u"
257
        else:
258
            sign = "b"
259

260
        constr.append(f"{sign:s}{c:d}")
261
        if self.force_vector:
262
            constr.append("force_vector")
263

264
        return "<%s %s>" % (self.__class__.__name__,
265
                             ", ".join(constr))
266

267

268
class Bits3val():
1✔
269
    """
270
    Class for value of `Bits3t` type
271

272
    :ivar ~._dtype: reference on type of this value
273
    :ivar ~.val: always unsigned representation int value
274
    :note: the reason why the unsigned is always used is that
275
        the signed variant would require cast to unsigned on every bitwise operation
276
    :ivar ~.vld_mask: always unsigned value of the mask, if bit in mask is '0'
277
            the corresponding bit in val is invalid
278
    """
279
    _BOOL = Bits3t(1)
1✔
280
    _SIGNED_FOR_SLICE_RESULT = False
1✔
281
    _SIGNED_FOR_CONCAT_RESULT = False
1✔
282

283
    def __init__(self, t: Bits3t, val: int, vld_mask: int):
1✔
284
        if not isinstance(t, Bits3t):
1!
UNCOV
285
            raise TypeError(t)
×
286
        if type(val) != int:
1!
UNCOV
287
            raise TypeError(val)
×
288
        if type(vld_mask) != int:
1!
UNCOV
289
            raise TypeError(vld_mask)
×
290
        if val < 0:
1!
UNCOV
291
            raise ValueError(t, val)
×
292
        self._dtype = t
1✔
293
        self.val = val
1✔
294
        self.vld_mask = vld_mask
1✔
295

296
    def __copy__(self) -> Self:
1✔
297
        return self.__class__(self._dtype, self.val, self.vld_mask)
1✔
298

299
    def to_py(self) -> int | None:
1✔
UNCOV
300
        if self._is_full_valid():
×
UNCOV
301
            return int(self)
×
UNCOV
302
        elif self.vld_mask == 0:
×
UNCOV
303
            return None
×
304
        else:
UNCOV
305
            raise ValidityError("Can not convert to python because some bits are defined and other bits undefined")
×
306

307
    def _is_full_valid(self) -> bool:
1✔
308
        """
309
        :return: True if all bits in value are valid
310
        """
311
        return self.vld_mask == self._dtype.all_mask()
1✔
312

313
    def __int__(self) -> int:
1✔
314
        "int(self)"
315
        if not self._is_full_valid():
1✔
316
            raise ValidityError(self)
1✔
317
        if self._dtype.signed:
1✔
318
            return to_signed(self.val, self._dtype.bit_length())
1✔
319
        else:
320
            return self.val
1✔
321

322
    def __bool__(self) -> bool:
1✔
323
        "bool(self)"
324
        return bool(self.__int__())
1✔
325

326
    def _auto_cast(self, dtype):
1✔
327
        """
328
        Cast value to a compatible type
329
        """
330
        return dtype.from_py(self.val, self.vld_mask)
×
331

332
    def _cast_sign(self, signed: Optional[bool], **typeMutateKwArgs) -> Self:
1✔
333
        """
334
        Cast signed-unsigned value
335
        """
336
        t = self._dtype
1✔
337
        if t.signed == signed:
1!
UNCOV
338
            return self
×
339
        v = self.__copy__()
1✔
340
        v._dtype = v._dtype._createMutated(signed=signed, **typeMutateKwArgs)
1✔
341
        return v
1✔
342

343
    def _concat(self, other: "Bits3val") -> Self:
1✔
344
        """
345
        Concatenate two bit vectors together (self will be at MSB side)
346
        Verilog: {self, other}, VHDL: self & other
347
        """
348
        if not isinstance(other, Bits3val):
1✔
349
            raise TypeError(other)
1✔
350
        w = self._dtype.bit_length()
1✔
351
        other_w = other._dtype.bit_length()
1✔
352
        resWidth = w + other_w
1✔
353
        resT = self._dtype.__class__(resWidth, signed=self._SIGNED_FOR_CONCAT_RESULT)
1✔
354
        other_val = other.val
1✔
355
        assert other_val >= 0, other_val
1✔
356
        v = self.__copy__()
1✔
357
        assert v.val >= 0, v
1✔
358
        v.val = (v.val << other_w) | other_val
1✔
359
        v.vld_mask = (v.vld_mask << other_w) | other.vld_mask
1✔
360
        v._dtype = resT
1✔
361
        return v
1✔
362

363
    def _ext(self, newWidth: Union[int, Self], signed: Union[bool, Literal[_NOT_SPECIFIED]]=_NOT_SPECIFIED) -> Self:
1✔
364
        """
365
        :note: preserves sign of type
366
        """
UNCOV
367
        if signed is _NOT_SPECIFIED:
×
UNCOV
368
            signed = self._dtype.signed
×
UNCOV
369
        if signed:
×
UNCOV
370
            return self._sext(newWidth)
×
371
        else:
UNCOV
372
            return self._zext(newWidth)
×
373

374
    def _sext(self, newWidth: Union[int, Self]) -> Self:
1✔
375
        """
376
        signed extension, pad with MSB bit on MSB side to newWidth result width
377
        :see: :meth:`Bits3val._ext`
378
        """
379
        t = self._dtype
1✔
380
        w = t.bit_length()
1✔
381
        if newWidth == w:
1✔
382
            return self
1✔
383
        assert newWidth > w, (newWidth, w)
1✔
384
        resTy = t._createMutated(newWidth)
1✔
385
        val = self.val
1✔
386
        newBitsMask = bit_field(w, newWidth)
1✔
387
        if get_bit(val, w - 1):
1✔
388
            val |= newBitsMask
1✔
389
        vldMask = self.vld_mask
1✔
390
        if get_bit(vldMask, w - 1):
1!
391
            vldMask |= newBitsMask
1✔
392

393
        return resTy._from_py(val, vldMask)
1✔
394
        # alfternatively:
395
        # sign_bit = 1 << (bits - 1)
396
        # return (value & (sign_bit - 1)) - (value & sign_bit)
397

398
    def _zext(self, newWidth: Union[int, Self]) -> Self:
1✔
399
        """
400
        zero extension, pad with 0 on msb side to newWidth result width
401
        :see: :meth:`Bits3val._ext`
402
        """
403

404
        t = self._dtype
1✔
405
        w = t.bit_length()
1✔
406
        if newWidth == w:
1✔
407
            return self
1✔
408
        assert newWidth > w, (newWidth, w)
1✔
409
        resTy = t._createMutated(newWidth)
1✔
410
        return resTy.from_py(self.val, vld_mask=self.vld_mask | bit_field(w, newWidth))
1✔
411

412
    def _trunc(self, newWidth: Union[int, Self]) -> Self:
1✔
413
        assert newWidth > 0, newWidth
1✔
414
        w = self._dtype.bit_length()
1✔
415
        assert newWidth <= w, newWidth
1✔
416
        resTy = self._dtype._createMutated(newWidth)
1✔
417
        resMask = mask(newWidth)
1✔
418
        return resTy._from_py(self.val & resMask, self.vld_mask & resMask)
1✔
419

420
    def _extOrTrunc(self, newWidth: int, signed: Union[bool, Literal[_NOT_SPECIFIED]]=_NOT_SPECIFIED) -> Self:
1✔
UNCOV
421
        w = self._dtype.bit_length()
×
UNCOV
422
        if w < newWidth:
×
UNCOV
423
            return self._ext(newWidth, signed)
×
UNCOV
424
        elif w > newWidth:
×
UNCOV
425
            return self._trunc(newWidth)
×
426
        else:
UNCOV
427
            return self
×
428

429
    def __getitem__(self, key: Union[int, slice, Self]) -> Self:
1✔
430
        "self[key]"
431
        if isinstance(key, slice):
1✔
432
            firstBitNo, size = normalize_slice(key, self._dtype.bit_length())
1✔
433
            val = get_bit_range(self.val, firstBitNo, size)
1✔
434
            vld = get_bit_range(self.vld_mask, firstBitNo, size)
1✔
435
        elif isinstance(key, (int, Bits3val)):
1✔
436
            size = 1
1✔
437
            try:
1✔
438
                _i = int(key)
1✔
UNCOV
439
            except ValidityError:
×
UNCOV
440
                _i = None
×
441

442
            if _i is None:
1!
UNCOV
443
                val = 0
×
UNCOV
444
                vld = 0
×
445
            else:
446
                if _i < 0 or _i >= self._dtype.bit_length():
1✔
447
                    raise IndexError("Index out of range", _i)
1✔
448

449
                val = get_bit(self.val, _i)
1✔
450
                vld = get_bit(self.vld_mask, _i)
1✔
451
        else:
452
            raise TypeError(key)
1✔
453

454
        new_t = self._dtype._createMutated(size, signed=self._SIGNED_FOR_SLICE_RESULT)
1✔
455
        return new_t._from_py(val, vld)
1✔
456

457
    def __setitem__(self, index: Union[slice, int, Self],
1✔
458
                    value: Union[int, Self]):
459
        "An item assignment operator self[index] = value."
460
        if isinstance(index, slice):
1✔
461
            firstBitNo, size = normalize_slice(index, self._dtype.bit_length())
1✔
462
            if isinstance(value, Bits3val):
1!
UNCOV
463
                v = value.val
×
UNCOV
464
                m = value.vld_mask
×
465
            else:
466
                v = value
1✔
467
                m = mask(size)
1✔
468

469
            self.val = set_bit_range(self.val, firstBitNo, size, v)
1✔
470
            self.vld_mask = set_bit_range(
1✔
471
                self.vld_mask, firstBitNo, size, m)
472
        else:
473
            if index is None:
1✔
474
                raise TypeError(index)
1✔
475
            try:
1✔
476
                _i = int(index)
1✔
UNCOV
477
            except ValidityError:
×
UNCOV
478
                _i = None
×
479

480
            if _i is None:
1!
UNCOV
481
                self.val = 0
×
UNCOV
482
                self.vld_mask = 0
×
483
            else:
484
                if value is None:
1✔
485
                    v = 0
1✔
486
                    m = 0
1✔
487
                elif isinstance(value, Bits3val):
1!
UNCOV
488
                    v = value.val
×
UNCOV
489
                    m = value.vld_mask
×
490
                else:
491
                    v = value
1✔
492
                    m = 0b1
1✔
493
                try:
1✔
494
                    index = int(index)
1✔
UNCOV
495
                except ValidityError:
×
UNCOV
496
                    index = None
×
497
                if index is None:
1!
UNCOV
498
                    self.val = 0
×
UNCOV
499
                    self.vld_mask = 0
×
500
                else:
501
                    self.val = bit_set_to(self.val, index, v)
1✔
502
                    self.vld_mask = bit_set_to(self.vld_mask, index, m)
1✔
503

504
    def __invert__(self) -> Self:
1✔
505
        "Operator ~x."
506
        v = self.__copy__()
1✔
507
        v.val = ~v.val
1✔
508
        w = v._dtype.bit_length()
1✔
509
        v.val &= mask(w)
1✔
510
        if v.val < 0:
1!
UNCOV
511
            v.val = to_unsigned(v.val, w)
×
512

513
        return v
1✔
514

515
    def __neg__(self) -> Self:
1✔
516
        "Operator -x."
517
        v = self.__copy__()
1✔
518
        width = self._dtype.bit_length()
1✔
519
        _v = -to_signed(v.val, width)
1✔
520
        v.val = to_unsigned(_v, width)
1✔
521
        return v
1✔
522

523
    def __hash__(self) -> int:
1✔
UNCOV
524
        return hash((self._dtype, self.val, self.vld_mask))
×
525

526
    def _is(self, other) -> bool:
1✔
527
        """check if other is object with same values"""
UNCOV
528
        return isinstance(other, Bits3val)\
×
529
            and self._dtype == other._dtype\
530
            and self.val == other.val\
531
            and self.vld_mask == other.vld_mask
532

533
    def _eq(self, other: Union[int, Self]) -> Self:
1✔
534
        """
535
        Operator self._eq(other) as self == other
536
        == is not overridden in order to prevent tricky behavior if hashing partially valid values
537
        """
538
        return bitsCmp__val_EQ(self, other)
1✔
539

540
    def __req__(self, other: int) -> Self:
1✔
541
        "Operator ==."
UNCOV
542
        return bitsCmp__val_EQ(self._dtype.from_py(other), self)
×
543

544
    def __ne__(self, other: Union[int, Self]) -> Self:
1✔
545
        "Operator !=."
546
        return bitsCmp__val_NE(self, other)
1✔
547

548
    def __rne__(self, other: int) -> Self:
1✔
549
        "Operator !=."
UNCOV
550
        return bitsCmp__val_NE(self._dtype.from_py(other), self)
×
551

552
    def __lt__(self, other: Union[int, Self]) -> Self:
1✔
553
        "Operator <."
554
        return bitsCmp__val(self, other, lt)
1✔
555

556
    def __rlt__(self, other: int) -> Self:
1✔
557
        "Operator <."
UNCOV
558
        return bitsCmp__val(self._dtype.from_py(other), self, lt)
×
559

560
    def __gt__(self, other: Union[int, Self]) -> Self:
1✔
561
        "Operator >."
562
        return bitsCmp__val(self, other, gt)
1✔
563

564
    def __rgt__(self, other: int) -> Self:
1✔
565
        "Operator >."
UNCOV
566
        return bitsCmp__val(self._dtype.from_py(other), self, gt)
×
567

568
    def __ge__(self, other: Union[int, Self]) -> Self:
1✔
569
        "Operator >=."
570
        return bitsCmp__val(self, other, ge)
1✔
571

572
    def __rge__(self, other: int) -> Self:
1✔
573
        "Operator >=."
UNCOV
574
        return bitsCmp__val(self._dtype.from_py(other), self, ge)
×
575

576
    def __le__(self, other: Union[int, Self]) -> Self:
1✔
577
        "Operator <=."
578
        return bitsCmp__val(self, other, le)
1✔
579

580
    def __rle__(self, other: int) -> Self:
1✔
581
        "Operator <=."
UNCOV
582
        return bitsCmp__val(self._dtype.from_py(other), self, le)
×
583

584
    def __xor__(self, other: Union[int, Self]) -> Self:
1✔
585
        "Operator ^."
586
        return bitsBitOp__val(self, other, xor, vld_mask_for_xor)
1✔
587

588
    def __rxor__(self, other: int) -> Self:
1✔
589
        "Operator ^."
UNCOV
590
        return bitsBitOp__val(self._dtype.from_py(other), self, xor,
×
591
                              vld_mask_for_xor)
592

593
    def __and__(self, other: Union[int, Self]) -> Self:
1✔
594
        "Operator &."
595
        return bitsBitOp__val(self, other, and_, vld_mask_for_and)
1✔
596

597
    def __rand__(self, other: int) -> Self:
1✔
598
        "Operator &."
UNCOV
599
        return bitsBitOp__val(self._dtype.from_py(other), self, and_,
×
600
                              vld_mask_for_and)
601

602
    def __or__(self, other: Union[int, Self]) -> Self:
1✔
603
        "Operator |."
604
        return bitsBitOp__val(self, other, or_, vld_mask_for_or)
1✔
605

606
    def __ror__(self, other: int) -> Self:
1✔
607
        "Operator |."
UNCOV
608
        return bitsBitOp__val(self._dtype.from_py(other), self, or_,
×
609
                              vld_mask_for_or)
610

611
    def __sub__(self, other: Union[int, Self]) -> Self:
1✔
612
        "Operator -."
613
        return bitsArithOp__val(self, other, sub)
1✔
614

615
    def __rsub__(self, other: Union[int, Self]) -> Self:
1✔
616
        "Operator -."
617
        return bitsArithOp__val(self._dtype.from_py(other), self, sub)
×
618

619
    def __add__(self, other: Union[int, Self]) -> Self:
1✔
620
        "Operator +."
621
        return bitsArithOp__val(self, other, add)
1✔
622

623
    def __radd__(self, other: Union[int, Self]) -> Self:
1✔
624
        "Operator +."
UNCOV
625
        return bitsArithOp__val(self._dtype.from_py(other), self, add)
×
626

627
    def __rshift__(self, other: Union[int, Self]) -> Self:
1✔
628
        "Operator >>."
629
        if self._dtype.signed:
1✔
630
            return bitsBitOp__ashr(self, other)
1✔
631
        else:
632
            return bitsBitOp__lshr(self, other)
1✔
633

634
    def __lshift__(self, other: Union[int, Self]) -> Self:
1✔
635
        "Operator <<. (shifts in 0)"
636
        try:
1✔
637
            o = int(other)
1✔
UNCOV
638
        except ValidityError:
×
UNCOV
639
            o = None
×
640

641
        v = self.__copy__()
1✔
642
        if o is None:
1!
UNCOV
643
            v.vld_mask = 0
×
644
            v.val = 0
×
645
        elif o == 0:
1✔
646
            return v
1✔
647
        else:
648
            if o < 0:
1!
UNCOV
649
                raise ValueError("negative shift count")
×
650
            t = self._dtype
1✔
651
            m = t.all_mask()
1✔
652
            v.vld_mask <<= o
1✔
653
            v.vld_mask |= mask(o)
1✔
654
            v.vld_mask &= m
1✔
655
            v.val <<= o
1✔
656
            v.val &= m
1✔
657
            assert v.val >= 0, v.val
1✔
658
        return v
1✔
659

660
    def __floordiv__(self, other: Union[int, Self]) -> Self:
1✔
661
        "Operator //."
662
        other_is_int = isinstance(other, int)
1✔
663
        t = self._dtype
1✔
664
        w = t.bit_length()
1✔
665
        if other_is_int:
1✔
666
            v0 = self.val
1✔
667
            if t.signed:
1✔
668
                v0 = to_signed(v0, w)
1✔
669
            v = v0 // other
1✔
670
            m = self._dtype.all_mask()
1✔
671
        else:
672
            if self._is_full_valid() and other._is_full_valid():
1✔
673
                v0 = self.val
1✔
674
                v1 = other.val
1✔
675
                if t.signed:
1✔
676
                    v0 = to_signed(v0, w)
1✔
677
                    v1 = to_signed(v1, w)
1✔
678
                v = v0 // v1
1✔
679
                m = self._dtype.all_mask()
1✔
680
            else:
681
                v = 0
1✔
682
                m = 0
1✔
683
        if v < 0:
1✔
684
            v = to_unsigned(v, w)
1✔
685
        return self._dtype._from_py(v, m)
1✔
686

687
    def __mul__(self, other: Union[int, Self]) -> Self:
1✔
688
        "Operator *."
689
        resT = self._dtype
1✔
690
        other_is_int = isinstance(other, int)
1✔
691
        w = resT.bit_length()
1✔
692
        if other_is_int:
1!
UNCOV
693
            v0 = self.val
×
UNCOV
694
            if resT.signed:
×
UNCOV
695
                v0 = to_signed(v0, w)
×
696

UNCOV
697
            v = v0 * other
×
698
        elif isinstance(other, Bits3val):
1✔
699
            v0 = self.val
1✔
700
            v1 = other.val
1✔
701
            if resT.signed:
1✔
702
                v0 = to_signed(v0, w)
1✔
703
                v1 = to_signed(v1, w)
1✔
704

705
            v = v0 * v1
1✔
706
        else:
707
            raise TypeError(other)
1✔
708

709
        v &= resT.all_mask()
1✔
710
        if resT.signed:
1✔
711
            v = to_signed(v, resT.bit_length())
1✔
712

713
        if self._is_full_valid() and (other_is_int
1✔
714
                                      or other._is_full_valid()):
715
            vld_mask = resT._all_mask
1✔
716
        else:
717
            vld_mask = 0
1✔
718

719
        if v < 0:
1✔
720
            v = to_unsigned(v, w)
1✔
721

722
        return resT._from_py(v, vld_mask)
1✔
723

724
    def __mod__(self, other: Union[int, Self]) -> Self:
1✔
725
        "Operator %."
UNCOV
726
        resT = self._dtype
×
UNCOV
727
        other_is_int = isinstance(other, int)
×
UNCOV
728
        w = resT.bit_length()
×
UNCOV
729
        if other_is_int:
×
UNCOV
730
            v0 = self.val
×
UNCOV
731
            if resT.signed:
×
UNCOV
732
                v0 = to_signed(v0, w)
×
733

UNCOV
734
            v = v0 % other
×
UNCOV
735
        elif isinstance(other, Bits3val):
×
UNCOV
736
            v0 = self.val
×
UNCOV
737
            v1 = other.val
×
UNCOV
738
            if resT.signed:
×
UNCOV
739
                v0 = to_signed(v0, w)
×
UNCOV
740
                v1 = to_signed(v1, w)
×
741

UNCOV
742
            v = v0 % v1
×
743
        else:
UNCOV
744
            raise TypeError(other)
×
745

UNCOV
746
        v &= resT.all_mask()
×
UNCOV
747
        if resT.signed:
×
UNCOV
748
            v = to_signed(v, resT.bit_length())
×
749

750
        if self._is_full_valid() and (other_is_int
×
751
                                      or other._is_full_valid()):
752
            vld_mask = resT._all_mask
×
753
        else:
754
            vld_mask = 0
×
755
        if v < 0:
×
756
            v = to_unsigned(v, w)
×
757

758
        return resT._from_py(v, vld_mask)
×
759

760
    def _ternary(self, a, b):
1✔
761
        """
762
        Ternary operator (a if self else b).
763
        """
764
        try:
1✔
765
            if self:
1✔
766
                return a
1✔
767
            else:
768
                return b
1✔
769
        except ValidityError:
×
UNCOV
770
            pass
×
771
        res = copy(a)
×
772
        res.vld_mask = 0
×
773
        return res
×
774

775
    def __abs__(self):
1✔
UNCOV
776
        if self < 0:
×
777
            return -self
×
778
        else:
779
            return self
×
780

781
    def __repr__(self):
782
        t = self._dtype
783
        if self.vld_mask != t.all_mask():
784
            m = f", mask {self.vld_mask:x}"
785
        else:
786
            m = ""
787
        typeDescrChar = 'b' if t.signed is None else 'i' if t.signed else 'u'
788
        if t.bit_length() == 1 and t.force_vector:
789
            vecSpec = "vec"
790
        else:
791
            vecSpec = ""
792
        return (f"<{self.__class__.__name__:s} {typeDescrChar:s}{t.bit_length():d}{vecSpec:s}"
793
                f" {to_signed(self.val, t.bit_length()) if t.signed else self.val:d}{m:s}>")
794

795

796
def bitsBitOp__ror(self: Bits3val, shAmount: Union[Bits3val, int]):
1✔
797
    """
798
    rotate right by specified amount
799
    """
UNCOV
800
    t = self._dtype
×
UNCOV
801
    width = t.bit_length()
×
UNCOV
802
    try:
×
UNCOV
803
        shAmount = int(shAmount)
×
UNCOV
804
    except ValidityError:
×
UNCOV
805
        return t.from_py(None)
×
UNCOV
806
    assert shAmount >= 0
×
807

UNCOV
808
    v = rotate_right(self.val, width, shAmount)
×
UNCOV
809
    if t.signed:
×
UNCOV
810
        v = to_signed(v, width)
×
UNCOV
811
    return t.from_py(v, rotate_right(self.vld_mask, width, shAmount))
×
812

813

814
def bitsBitOp__rol(self: Bits3val, shAmount: Union[Bits3val, int]):
1✔
815
    """
816
    rotate left by specified amount
817
    """
818
    t = self._dtype
×
819
    width = t.bit_length()
×
820
    try:
×
821
        shAmount = int(shAmount)
×
822
    except ValidityError:
×
823
        return t.from_py(None)
×
UNCOV
824
    assert shAmount >= 0
×
825
    v = rotate_left(self.val, width, shAmount)
×
826
    if t.signed:
×
827
        v = to_signed(v, width)
×
828
    return t.from_py(v, rotate_left(self.vld_mask, width, shAmount))
×
829

830

831
def bitsBitOp__lshr(self: Bits3val, shAmount: Union[Bits3val, int]) -> Bits3val:
1✔
832
    """
833
    logical shift right (shifts in 0)
834
    """
835
    t = self._dtype
1✔
836
    width = t.bit_length()
1✔
837
    try:
1✔
838
        sh = int(shAmount)
1✔
839
    except ValidityError:
×
840
        return t.from_py(None)
×
841
    assert sh >= 0, sh
1✔
842

843
    if sh >= width:
1!
844
        # all bits are shifted out
845
        return t.from_py(0, mask(width))
×
846

847
    v = self.val >> sh
1✔
848
    if t.signed:
1✔
849
        v = to_signed(v, width)
1✔
850
    newBitsMask = bit_field(width - sh, width)
1✔
851
    return t.from_py(v, (self.vld_mask >> sh) | newBitsMask)
1✔
852

853

854
def bitsBitOp__ashr(self: Bits3val, shAmount: Union[Bits3val, int]) -> Bits3val:
1✔
855
    """
856
    arithmetic shift right (shifts in MSB)
857
    """
858
    try:
1✔
859
        sh = int(shAmount)
1✔
UNCOV
860
    except ValidityError:
×
UNCOV
861
        sh = None
×
862

863
    v = self.__copy__()
1✔
864
    if sh is None:
1!
UNCOV
865
        v.vld_mask = 0
×
UNCOV
866
        v.val = 0
×
867
    elif sh == 0:
1✔
868
        pass
1✔
869
    else:
870
        if shAmount < 0:
1!
UNCOV
871
            raise ValueError("negative shift count")
×
872
        w = self._dtype.bit_length()
1✔
873
        if sh < w:
1!
874
            msb = v.val >> (w - 1)
1✔
875
            newBitsMask = bit_field(w - sh, w)
1✔
876
            v.vld_mask >>= sh
1✔
877
            v.vld_mask |= newBitsMask  # set newly shifted-in bits to defined
1✔
878
            v.val >>= sh
1✔
879
            if msb:
1✔
880
                v.val |= newBitsMask
1✔
881
        else:
882
            # completely shifted out
883
            v.val = 0
×
UNCOV
884
            v.vld_mask = mask(w)
×
885
    return v
1✔
886

887

888
def bitsBitOp__val(self: Bits3val, other: Union[Bits3val, int],
1✔
889
                   evalFn, getVldFn) -> "Bits3val":
890
    """
891
    Apply bitwise operator
892
    """
893
    res_t = self._dtype
1✔
894
    if isinstance(other, int):
1✔
895
        other = res_t.from_py(other)
1✔
896
    w = res_t.bit_length()
1✔
897
    assert w == other._dtype.bit_length(), (res_t, other._dtype)
1✔
898
    vld = getVldFn(self, other)
1✔
899
    res = evalFn(self.val, other.val) & vld
1✔
900
    assert res >= 0, res
1✔
901

902
    return res_t._from_py(res, vld)
1✔
903

904

905
def bitsCmp__val(self: Bits3val, other: Union[Bits3val, int],
1✔
906
                 evalFn: Callable[[int, int], bool]) -> "Bits3val":
907
    """
908
    Apply comparative operator
909
    """
910
    assert evalFn is not eq and evalFn is not ne, ("use bitsCmp__val_EQ/bitsCmp__val_NE instead")
1✔
911
    t = self._dtype
1✔
912
    w = t.bit_length()
1✔
913
    if isinstance(other, int):
1✔
914
        other = t.from_py(other)
1✔
915
        ot = other._dtype
1✔
916
    else:
917
        ot = other._dtype
1✔
918
        if bool(t.signed) != bool(ot.signed) or w != ot.bit_length():
1✔
919
            raise TypeError("Value compare supports only same width and sign type", t, ot)
1✔
920

921
    v0 = self.val
1✔
922
    v1 = other.val
1✔
923
    if t.signed:
1✔
924
        v0 = to_signed(v0, w)
1✔
925
        v1 = to_signed(v1, w)
1✔
926

927
    vld = self.vld_mask & other.vld_mask
1✔
928
    _vld = int(vld == t._all_mask)
1✔
929
    res = evalFn(v0, v1) & _vld
1✔
930

931
    return self._BOOL._from_py(int(res), int(_vld))
1✔
932

933

934
def bitsCmp__val_NE(self: Bits3val, other: Union[Bits3val, int]) -> "Bits3val":
1✔
935
    """
936
    Apply != operator
937
    """
938
    t = self._dtype
1✔
939
    w = t.bit_length()
1✔
940
    if isinstance(other, int):
1✔
941
        other = t.from_py(other)
1✔
942
        ot = other._dtype
1✔
943
    else:
944
        ot = other._dtype
1✔
945
        if bool(t.signed) != bool(ot.signed) or w != ot.bit_length():
1✔
946
            raise TypeError("Value compare supports only same width and sign type", t, ot)
1✔
947

948
    v0 = self.val
1✔
949
    v1 = other.val
1✔
950

951
    vld = self.vld_mask & other.vld_mask
1✔
952
    _vld = int(vld == t._all_mask)
1✔
953
    res = ((v0 ^ v1) & vld) != 0  # at least some valid bit non equal
1✔
954

955
    return self._BOOL._from_py(int(res), _vld | int(res))
1✔
956

957

958
def bitsCmp__val_EQ(self: Bits3val, other: Union[Bits3val, int]) -> "Bits3val":
1✔
959
    """
960
    Apply == operator
961
    """
962
    t = self._dtype
1✔
963
    w = t.bit_length()
1✔
964
    if isinstance(other, int):
1✔
965
        other = t.from_py(other)
1✔
966
        ot = other._dtype
1✔
967
    else:
968
        ot = other._dtype
1✔
969
        if bool(t.signed) != bool(ot.signed) or w != ot.bit_length():
1✔
970
            raise TypeError("Value compare supports only same width and sign type", t, ot)
1✔
971

972
    v0 = self.val
1✔
973
    v1 = other.val
1✔
974

975
    vld = self.vld_mask & other.vld_mask
1✔
976
    _vld = int(vld == t._all_mask)
1✔
977
    ne = ((v0 ^ v1) & vld) != 0  # all valid bits equal
1✔
978
    res = int(not ne)
1✔
979
    if not _vld:
1!
UNCOV
980
        if ne:
×
UNCOV
981
            _vld = 1  # some bits invalid, but from valid bytes we already know that the value does not equal
×
982
        else:
UNCOV
983
            res = 0  # set value for invalid value to 0
×
984
    return self._BOOL._from_py(int(res), _vld)
1✔
985

986

987
def bitsArithOp__val(self: Bits3val, other: Union[Bits3val, int],
1✔
988
                     evalFn: Callable[[int, int], int]) -> "Bits3val":
989
    """
990
    Apply arithmetic operator
991
    """
992
    t = self._dtype
1✔
993
    if isinstance(other, int):
1!
994
        other = self._dtype.from_py(other)
1✔
995
    else:
UNCOV
996
        assert t == other._dtype, (self, other, t, other._dtype)
×
997

998
    v = self.__copy__()
1✔
999
    self_vld = self._is_full_valid()
1✔
1000
    other_vld = other._is_full_valid()
1✔
1001
    v0 = self.val
1✔
1002
    v1 = other.val
1✔
1003
    w = v._dtype.bit_length()
1✔
1004
    if t.signed:
1✔
1005
        v0 = to_signed(v0, w)
1✔
1006
        v1 = to_signed(v1, w)
1✔
1007

1008
    _v = evalFn(v0, v1)
1✔
1009

1010
    if _v < 0:
1✔
1011
        _v = to_unsigned(_v, w)
1✔
1012

1013
    _v &= t._all_mask
1✔
1014

1015
    v.val = _v
1✔
1016
    if self_vld and other_vld:
1!
1017
        v.vld_mask = t._all_mask
1✔
1018
    else:
UNCOV
1019
        v.vld_mask = 0
×
1020

1021
    return v
1✔
1022

STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc