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

Nic30 / pyMathBitPrecise / 697bca74-2893-4324-97a5-dd0e956ca48b

23 Dec 2025 12:19PM UTC coverage: 66.747% (-0.3%) from 67.014%
697bca74-2893-4324-97a5-dd0e956ca48b

push

circleci

Nic30
feat(bit_utils): separate_least_significant_1, ctlo, ctto

224 of 390 branches covered (57.44%)

Branch coverage included in aggregate %.

3 of 6 new or added lines in 1 file covered. (50.0%)

103 existing lines in 1 file now uncovered.

878 of 1261 relevant lines covered (69.63%)

0.7 hits per line

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

73.93
/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
    :ivar ~.strict_sign: same thing as strict_width just for signed/unsigned
38
    :ivar ~.strict_width: if True the arithmetic, bitwise
39
        and comparison operators can be performed only on value
40
        of this exact same width
41
    :note: operation is not strict if at least one operand
42
        does not have strict flag set,
43
        the result width/sign is taken from other operand
44
        (or first if both are not strict)
45
    """
46

47
    def __init__(self, bit_length: int, signed:Optional[bool]=False, name: Optional[str]=None,
1✔
48
                 force_vector=False,
49
                 strict_sign=True, strict_width=True):
50
        if force_vector and bit_length != 1:
1!
51
            assert bit_length == 1, "force_vector=True is appliable only for 1b values"
×
52
        self._bit_length = bit_length
1✔
53
        self.signed = signed
1✔
54
        self._all_mask = mask(bit_length)
1✔
55
        self.name = name
1✔
56
        self.force_vector = force_vector
1✔
57
        self.strict_sign = strict_sign
1✔
58
        self.strict_width = strict_width
1✔
59

60
    def _createMutated(self,
1✔
61
            bit_length: int=_NOT_SPECIFIED,
62
            signed:Optional[bool]=_NOT_SPECIFIED, name: Optional[str]=_NOT_SPECIFIED,
63
            force_vector=_NOT_SPECIFIED,
64
            strict_sign=_NOT_SPECIFIED,
65
            strict_width=_NOT_SPECIFIED) -> Self:
66
        if bit_length is _NOT_SPECIFIED:
1✔
67
            bit_length = self._bit_length
1✔
68
        if signed is  _NOT_SPECIFIED:
1✔
69
            signed = self.signed
1✔
70
        if name  is _NOT_SPECIFIED:
1!
71
            name = self.name
1✔
72
        if force_vector is _NOT_SPECIFIED:
1!
73
            force_vector = self.force_vector
1✔
74
        if strict_sign is _NOT_SPECIFIED:
1!
75
            strict_sign = self.strict_sign
1✔
76
        if strict_width is _NOT_SPECIFIED:
1!
77
            strict_width = self.strict_width
1✔
78
        return self.__class__(bit_length, signed=signed, name=name, force_vector=force_vector, strict_sign=strict_sign, strict_width=strict_width)
1✔
79

80
    def __copy__(self) -> Self:
1✔
81
        t = self.__class__(self._bit_length, signed=self.signed,
×
82
                           name=self.name,
83
                           force_vector=self.force_vector,
84
                           strict_sign=self.strict_sign,
85
                           strict_width=self.strict_width)
86
        return t
×
87

88
    def all_mask(self) -> int:
1✔
89
        """
90
        :return: mask for bites of this type ( 0b111 for Bits(3) )
91
        """
92
        return self._all_mask
1✔
93

94
    def get_domain_range(self):
1✔
95
        """
96
        equvalent of (get_min_value(), get_max_value())
97
        :note: as an independent function from performace reasons
98
        """
99
        m = self._all_mask
1✔
100
        if self.signed:
1✔
101
            intMin = -(m // 2) - 1
1✔
102
            intMax = m // 2
1✔
103
        else:
104
            intMin = 0
1✔
105
            intMax = m
1✔
106

107
        return (intMin, intMax)
1✔
108

109
    def get_min_value(self):
1✔
110
        m = self._all_mask
×
111
        if self.signed:
×
112
            intMin = -(m // 2) - 1
×
113
        else:
114
            intMin = 0
×
115
        return intMin
×
116

117
    def get_max_value(self):
1✔
118
        m = self._all_mask
×
119
        if self.signed:
×
120
            intMax = m // 2
×
121
        else:
122
            intMax = m
×
123
        return intMax
×
124

125
    def bit_length(self) -> int:
1✔
126
        """
127
        :return: number of bits required for representation
128
            of value of this type
129
        """
130
        return self._bit_length
1✔
131

132
    def __eq__(self, other) -> bool:
1✔
133
        return (self is other
×
134
                or (isinstance(other, Bits3t)
135
                    and self._bit_length == other._bit_length
136
                    and self.name == other.name
137
                    and self.force_vector == other.force_vector
138
                    and self.strict_sign == other.strict_sign
139
                    and self.strict_width == other.strict_width
140
                    and self.signed == other.signed
141
                    )
142
                )
143

144
    def _normalize_val_and_mask(self, val: Union[int, None, Self], vld_mask: Optional[int]) -> tuple[int, int]:
1✔
145
        if val is None:
1✔
146
            vld = 0
1✔
147
            val = 0
1✔
148
            assert vld_mask is None or vld_mask == 0
1✔
149
        else:
150
            all_mask = self.all_mask()
1✔
151
            w = self._bit_length
1✔
152
            if isinstance(val, int):
1✔
153
                pass
1✔
154
            elif isinstance(val, bytes):
1!
155
                val = int.from_bytes(
×
156
                    val, byteorder="little", signed=bool(self.signed))
157
            elif isinstance(val, str):
1✔
158
                if not (val.startswith("0") and len(val) > 2):
1!
159
                    raise ValueError(val)
×
160

161
                base = INT_BASES[val[1]]
1✔
162
                try:
1✔
163
                    _val = int(val[2:], base)
1✔
164
                except ValueError:
1✔
165
                    _val = None
1✔
166

167
                if _val is None:
1!
168
                    assert vld_mask is None
1✔
169
                    val = val.lower()
1✔
170
                    if base == 10 and "x" in val:
1✔
171
                        raise NotImplementedError()
172
                    v = 0
1✔
173
                    m = 0
1✔
174
                    bits_per_char = ceil(log2(base))
1✔
175
                    char_mask = mask(bits_per_char)
1✔
176
                    for digit in val[2:]:
1✔
177
                        v <<= bits_per_char
1✔
178
                        m <<= bits_per_char
1✔
179
                        if digit == "x":
1✔
180
                            pass
1✔
181
                        else:
182
                            m |= char_mask
1✔
183
                            v |= int(digit, base)
1✔
184
                    val = v
1✔
185
                    vld_mask = m
1✔
186
                else:
187
                    val = _val
×
188
            elif isinstance(val, Bits3val):
1✔
189
                assert vld_mask is None or vld_mask == val.vld_mask
1✔
190
                vld_mask = val.vld_mask
1✔
191
                val = val.val
1✔
192
            else:
193
                try:
1✔
194
                    val = int(val)
1✔
195
                except TypeError as e:
1✔
196
                    if isinstance(val, Enum):
1!
UNCOV
197
                        val = int(val.value)
×
198
                    else:
199
                        raise e
1✔
200

201
            if vld_mask is None:
1✔
202
                vld = all_mask
1✔
203
            else:
204
                if vld_mask > all_mask or vld_mask < 0:
1!
UNCOV
205
                    raise ValueError("Mask in incorrect format", vld_mask, w, all_mask)
×
206
                vld = vld_mask
1✔
207

208
            if val < 0:
1✔
209
                if not self.signed:
1✔
210
                    raise ValueError("Negative value for unsigned int")
1✔
211
                _val = to_signed(val & all_mask, w)
1✔
212
                if _val != val:
1✔
213
                    raise ValueError("Too large value", val, _val)
1✔
214
                val = to_unsigned(_val, w)
1✔
215
            else:
216
                if self.signed:
1✔
217
                    msb = 1 << (w - 1)
1✔
218
                    if msb & val:
1✔
219
                        if val > 0:
1!
220
                            raise ValueError(
1✔
221
                                "Value too large to fit in this type", val)
222

223
                if val & all_mask != val:
1✔
224
                    raise ValueError(
1✔
225
                        "Not enough bits to represent value",
226
                        val, "on", w, "bit" if w == 1 else "bits", val & all_mask)
227
                val = val & vld
1✔
228
        return val, vld
1✔
229

230
    def _from_py(self, val: int, vld_mask: int) -> "Bits3val":
1✔
231
        """
232
        from_py without normalization
233
        """
234
        return Bits3val(self, val, vld_mask)
1✔
235

236
    def from_py(self, val: Union[int, bytes, str, Enum],
1✔
237
                vld_mask: Optional[int]=None) -> "Bits3val":
238
        """
239
        Construct value from pythonic value
240
        :note: str value has to start with base specifier (0b, 0h)
241
            and is much slower than the value specified
242
            by 'val' and 'vld_mask'. Does support x.
243
        """
244
        val, vld_mask = self._normalize_val_and_mask(val, vld_mask)
1✔
245
        return Bits3val(self, val, vld_mask)
1✔
246

247
    def __getitem__(self, i):
1✔
248
        ":return: an item from this array"
249
        return Array3t(self, i)
1✔
250

251
    def __hash__(self):
1✔
UNCOV
252
        return hash((
×
253
            self._bit_length,
254
            self.signed,
255
            self._all_mask,
256
            self.name,
257
            self.force_vector,
258
            self.strict_sign,
259
            self.strict_width
260
        ))
261

262
    def __repr__(self):
263
        """
264
        :param indent: number of indentation
265
        :param withAddr: if is not None is used as a additional
266
            information about on which address this type is stored
267
            (used only by HStruct)
268
        :param expandStructs: expand HStructTypes (used by HStruct and HArray)
269
        """
270
        constr = []
271
        if self.name is not None:
272
            constr.append(f'"{self.name:s}"')
273
        c = self.bit_length()
274

275
        if self.signed:
276
            sign = "i"
277
        elif self.signed is False:
278
            sign = "u"
279
        else:
280
            sign = "b"
281

282
        constr.append(f"{sign:s}{c:d}")
283
        if self.force_vector:
284
            constr.append("force_vector")
285

286
        if not self.strict_sign:
287
            constr.append("strict_sign=False")
288
        if not self.strict_width:
289
            constr.append("strict_width=False")
290

291
        return "<%s %s>" % (self.__class__.__name__,
292
                             ", ".join(constr))
293

294

295
class Bits3val():
1✔
296
    """
297
    Class for value of `Bits3t` type
298

299
    :ivar ~._dtype: reference on type of this value
300
    :ivar ~.val: always unsigned representation int value
301
    :note: the reason why the unsigned is always used is that
302
        the signed variant would require cast to unsigned on every bitwise operation
303
    :ivar ~.vld_mask: always unsigned value of the mask, if bit in mask is '0'
304
            the corresponding bit in val is invalid
305
    """
306
    _BOOL = Bits3t(1)
1✔
307
    _SIGNED_FOR_SLICE_RESULT = False
1✔
308
    _SIGNED_FOR_CONCAT_RESULT = False
1✔
309

310
    def __init__(self, t: Bits3t, val: int, vld_mask: int):
1✔
311
        if not isinstance(t, Bits3t):
1!
312
            raise TypeError(t)
×
313
        if type(val) != int:
1!
UNCOV
314
            raise TypeError(val)
×
315
        if type(vld_mask) != int:
1!
UNCOV
316
            raise TypeError(vld_mask)
×
317
        self._dtype = t
1✔
318
        self.val = val
1✔
319
        self.vld_mask = vld_mask
1✔
320

321
    def __copy__(self) -> Self:
1✔
322
        return self.__class__(self._dtype, self.val, self.vld_mask)
1✔
323

324
    def to_py(self) -> int | None:
1✔
UNCOV
325
        if self._is_full_valid():
×
UNCOV
326
            return int(self)
×
UNCOV
327
        elif self.vld_mask == 0:
×
UNCOV
328
            return None
×
329
        else:
UNCOV
330
            raise ValidityError("Can not convert to python because some bits are defined and other bits undefined")
×
331

332
    def _is_full_valid(self) -> bool:
1✔
333
        """
334
        :return: True if all bits in value are valid
335
        """
336
        return self.vld_mask == self._dtype.all_mask()
1✔
337

338
    def __int__(self) -> int:
1✔
339
        "int(self)"
340
        if not self._is_full_valid():
1✔
341
            raise ValidityError(self)
1✔
342
        if self._dtype.signed:
1✔
343
            return to_signed(self.val, self._dtype.bit_length())
1✔
344
        else:
345
            return self.val
1✔
346

347
    def __bool__(self) -> bool:
1✔
348
        "bool(self)"
349
        return bool(self.__int__())
1✔
350

351
    def _auto_cast(self, dtype):
1✔
352
        """
353
        Cast value to a compatible type
354
        """
UNCOV
355
        return dtype.from_py(self.val, self.vld_mask)
×
356

357
    def _cast_sign(self, signed: Optional[bool], **typeMutateKwArgs) -> Self:
1✔
358
        """
359
        Cast signed-unsigned value
360
        """
361
        t = self._dtype
1✔
362
        if t.signed == signed:
1!
UNCOV
363
            return self
×
364
        v = self.__copy__()
1✔
365
        v._dtype = v._dtype._createMutated(signed=signed, **typeMutateKwArgs)
1✔
366
        return v
1✔
367

368
    def _concat(self, other: "Bits3val") -> Self:
1✔
369
        """
370
        Concatenate two bit vectors together (self will be at MSB side)
371
        Verilog: {self, other}, VHDL: self & other
372
        """
373
        if not isinstance(other, Bits3val):
1✔
374
            raise TypeError(other)
1✔
375
        w = self._dtype.bit_length()
1✔
376
        other_w = other._dtype.bit_length()
1✔
377
        resWidth = w + other_w
1✔
378
        resT = self._dtype.__class__(resWidth, signed=self._SIGNED_FOR_CONCAT_RESULT)
1✔
379
        other_val = other.val
1✔
380
        assert other_val >= 0, other_val
1✔
381
        v = self.__copy__()
1✔
382
        assert v.val >= 0, v
1✔
383
        v.val = (v.val << other_w) | other_val
1✔
384
        v.vld_mask = (v.vld_mask << other_w) | other.vld_mask
1✔
385
        v._dtype = resT
1✔
386
        return v
1✔
387

388
    def _ext(self, newWidth: Union[int, Self], signed: Union[bool, Literal[_NOT_SPECIFIED]]=_NOT_SPECIFIED) -> Self:
1✔
389
        """
390
        :note: preserves sign of type
391
        """
UNCOV
392
        if signed is _NOT_SPECIFIED:
×
UNCOV
393
            signed = self._dtype.signed
×
UNCOV
394
        if signed:
×
UNCOV
395
            return self._sext(newWidth)
×
396
        else:
UNCOV
397
            return self._zext(newWidth)
×
398

399
    def _sext(self, newWidth: Union[int, Self]) -> Self:
1✔
400
        """
401
        signed extension, pad with MSB bit on MSB side to newWidth result width
402
        :see: :meth:`Bits3val._ext`
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
        val = self.val
1✔
411
        newBitsMask = bit_field(w, newWidth)
1✔
412
        if get_bit(val, w - 1):
1✔
413
            val |= newBitsMask
1✔
414
        vldMask = self.vld_mask
1✔
415
        if get_bit(vldMask, w - 1):
1!
416
            vldMask |= newBitsMask
1✔
417

418
        return resTy._from_py(val, vldMask)
1✔
419
        # alfternatively:
420
        # sign_bit = 1 << (bits - 1)
421
        # return (value & (sign_bit - 1)) - (value & sign_bit)
422

423
    def _zext(self, newWidth: Union[int, Self]) -> Self:
1✔
424
        """
425
        zero extension, pad with 0 on msb side to newWidth result width
426
        :see: :meth:`Bits3val._ext`
427
        """
428

429
        t = self._dtype
1✔
430
        w = t.bit_length()
1✔
431
        if newWidth == w:
1✔
432
            return self
1✔
433
        assert newWidth > w, (newWidth, w)
1✔
434
        resTy = t._createMutated(newWidth)
1✔
435
        return resTy.from_py(self.val, vld_mask=self.vld_mask | bit_field(w, newWidth))
1✔
436

437
    def _trunc(self, newWidth: Union[int, Self]) -> Self:
1✔
438
        assert newWidth > 0, newWidth
1✔
439
        w = self._dtype.bit_length()
1✔
440
        assert newWidth <= w, newWidth
1✔
441
        resTy = self._dtype._createMutated(newWidth)
1✔
442
        resMask = mask(newWidth)
1✔
443
        return resTy._from_py(self.val & resMask, self.vld_mask & resMask)
1✔
444

445
    def _extOrTrunc(self, newWidth: int, signed: Union[bool, Literal[_NOT_SPECIFIED]]=_NOT_SPECIFIED) -> Self:
1✔
UNCOV
446
        w = self._dtype.bit_length()
×
UNCOV
447
        if w < newWidth:
×
UNCOV
448
            return self._ext(newWidth, signed)
×
UNCOV
449
        elif w > newWidth:
×
UNCOV
450
            return self._trunc(newWidth)
×
451
        else:
UNCOV
452
            return self
×
453

454
    def __getitem__(self, key: Union[int, slice, Self]) -> Self:
1✔
455
        "self[key]"
456
        if isinstance(key, slice):
1✔
457
            firstBitNo, size = normalize_slice(key, self._dtype.bit_length())
1✔
458
            val = get_bit_range(self.val, firstBitNo, size)
1✔
459
            vld = get_bit_range(self.vld_mask, firstBitNo, size)
1✔
460
        elif isinstance(key, (int, Bits3val)):
1✔
461
            size = 1
1✔
462
            try:
1✔
463
                _i = int(key)
1✔
UNCOV
464
            except ValidityError:
×
UNCOV
465
                _i = None
×
466

467
            if _i is None:
1!
UNCOV
468
                val = 0
×
UNCOV
469
                vld = 0
×
470
            else:
471
                if _i < 0 or _i >= self._dtype.bit_length():
1✔
472
                    raise IndexError("Index out of range", _i)
1✔
473

474
                val = get_bit(self.val, _i)
1✔
475
                vld = get_bit(self.vld_mask, _i)
1✔
476
        else:
477
            raise TypeError(key)
1✔
478

479
        new_t = self._dtype._createMutated(size, signed=self._SIGNED_FOR_SLICE_RESULT)
1✔
480
        return new_t._from_py(val, vld)
1✔
481

482
    def __setitem__(self, index: Union[slice, int, Self],
1✔
483
                    value: Union[int, Self]):
484
        "An item assignment operator self[index] = value."
485
        if isinstance(index, slice):
1✔
486
            firstBitNo, size = normalize_slice(index, self._dtype.bit_length())
1✔
487
            if isinstance(value, Bits3val):
1!
UNCOV
488
                v = value.val
×
UNCOV
489
                m = value.vld_mask
×
490
            else:
491
                v = value
1✔
492
                m = mask(size)
1✔
493

494
            self.val = set_bit_range(self.val, firstBitNo, size, v)
1✔
495
            self.vld_mask = set_bit_range(
1✔
496
                self.vld_mask, firstBitNo, size, m)
497
        else:
498
            if index is None:
1✔
499
                raise TypeError(index)
1✔
500
            try:
1✔
501
                _i = int(index)
1✔
UNCOV
502
            except ValidityError:
×
UNCOV
503
                _i = None
×
504

505
            if _i is None:
1!
UNCOV
506
                self.val = 0
×
UNCOV
507
                self.vld_mask = 0
×
508
            else:
509
                if value is None:
1✔
510
                    v = 0
1✔
511
                    m = 0
1✔
512
                elif isinstance(value, Bits3val):
1!
UNCOV
513
                    v = value.val
×
514
                    m = value.vld_mask
×
515
                else:
516
                    v = value
1✔
517
                    m = 0b1
1✔
518
                try:
1✔
519
                    index = int(index)
1✔
UNCOV
520
                except ValidityError:
×
UNCOV
521
                    index = None
×
522
                if index is None:
1!
UNCOV
523
                    self.val = 0
×
UNCOV
524
                    self.vld_mask = 0
×
525
                else:
526
                    self.val = bit_set_to(self.val, index, v)
1✔
527
                    self.vld_mask = bit_set_to(self.vld_mask, index, m)
1✔
528

529
    def __invert__(self) -> Self:
1✔
530
        "Operator ~x."
531
        v = self.__copy__()
1✔
532
        v.val = ~v.val
1✔
533
        w = v._dtype.bit_length()
1✔
534
        v.val &= mask(w)
1✔
535
        if v.val < 0:
1!
UNCOV
536
            v.val = to_unsigned(v.val, w)
×
537

538
        return v
1✔
539

540
    def __neg__(self) -> Self:
1✔
541
        "Operator -x."
542
        v = self.__copy__()
1✔
543
        width = self._dtype.bit_length()
1✔
544
        _v = -to_signed(v.val, width)
1✔
545
        v.val = to_unsigned(_v, width)
1✔
546
        return v
1✔
547

548
    def __hash__(self) -> int:
1✔
UNCOV
549
        return hash((self._dtype, self.val, self.vld_mask))
×
550

551
    def _is(self, other) -> bool:
1✔
552
        """check if other is object with same values"""
UNCOV
553
        return isinstance(other, Bits3val)\
×
554
            and self._dtype == other._dtype\
555
            and self.val == other.val\
556
            and self.vld_mask == other.vld_mask
557

558
    def _eq(self, other: Union[int, Self]) -> Self:
1✔
559
        """
560
        Operator self._eq(other) as self == other
561
        == is not overridden in order to prevent tricky behavior if hashing partially valid values
562
        """
563
        return bitsCmp__val_EQ(self, other)
1✔
564

565
    def __req__(self, other: int) -> Self:
1✔
566
        "Operator ==."
UNCOV
567
        return bitsCmp__val_EQ(self._dtype.from_py(other), self)
×
568

569
    def __ne__(self, other: Union[int, Self]) -> Self:
1✔
570
        "Operator !=."
571
        return bitsCmp__val_NE(self, other)
1✔
572

573
    def __rne__(self, other: int) -> Self:
1✔
574
        "Operator !=."
UNCOV
575
        return bitsCmp__val_NE(self._dtype.from_py(other), self)
×
576

577
    def __lt__(self, other: Union[int, Self]) -> Self:
1✔
578
        "Operator <."
579
        return bitsCmp__val(self, other, lt)
1✔
580

581
    def __rlt__(self, other: int) -> Self:
1✔
582
        "Operator <."
UNCOV
583
        return bitsCmp__val(self._dtype.from_py(other), self, lt)
×
584

585
    def __gt__(self, other: Union[int, Self]) -> Self:
1✔
586
        "Operator >."
587
        return bitsCmp__val(self, other, gt)
1✔
588

589
    def __rgt__(self, other: int) -> Self:
1✔
590
        "Operator >."
UNCOV
591
        return bitsCmp__val(self._dtype.from_py(other), self, gt)
×
592

593
    def __ge__(self, other: Union[int, Self]) -> Self:
1✔
594
        "Operator >=."
595
        return bitsCmp__val(self, other, ge)
1✔
596

597
    def __rge__(self, other: int) -> Self:
1✔
598
        "Operator >=."
UNCOV
599
        return bitsCmp__val(self._dtype.from_py(other), self, ge)
×
600

601
    def __le__(self, other: Union[int, Self]) -> Self:
1✔
602
        "Operator <=."
603
        return bitsCmp__val(self, other, le)
1✔
604

605
    def __rle__(self, other: int) -> Self:
1✔
606
        "Operator <=."
UNCOV
607
        return bitsCmp__val(self._dtype.from_py(other), self, le)
×
608

609
    def __xor__(self, other: Union[int, Self]) -> Self:
1✔
610
        "Operator ^."
611
        return bitsBitOp__val(self, other, xor, vld_mask_for_xor)
1✔
612

613
    def __rxor__(self, other: int) -> Self:
1✔
614
        "Operator ^."
615
        return bitsBitOp__val(self._dtype.from_py(other), self, xor,
×
616
                              vld_mask_for_xor)
617

618
    def __and__(self, other: Union[int, Self]) -> Self:
1✔
619
        "Operator &."
620
        return bitsBitOp__val(self, other, and_, vld_mask_for_and)
1✔
621

622
    def __rand__(self, other: int) -> Self:
1✔
623
        "Operator &."
624
        return bitsBitOp__val(self._dtype.from_py(other), self, and_,
×
625
                              vld_mask_for_and)
626

627
    def __or__(self, other: Union[int, Self]) -> Self:
1✔
628
        "Operator |."
629
        return bitsBitOp__val(self, other, or_, vld_mask_for_or)
1✔
630

631
    def __ror__(self, other: int) -> Self:
1✔
632
        "Operator |."
633
        return bitsBitOp__val(self._dtype.from_py(other), self, or_,
×
634
                              vld_mask_for_or)
635

636
    def __sub__(self, other: Union[int, Self]) -> Self:
1✔
637
        "Operator -."
638
        return bitsArithOp__val(self, other, sub)
1✔
639

640
    def __rsub__(self, other: Union[int, Self]) -> Self:
1✔
641
        "Operator -."
UNCOV
642
        return bitsArithOp__val(self._dtype.from_py(other), self, sub)
×
643

644
    def __add__(self, other: Union[int, Self]) -> Self:
1✔
645
        "Operator +."
646
        return bitsArithOp__val(self, other, add)
1✔
647

648
    def __radd__(self, other: Union[int, Self]) -> Self:
1✔
649
        "Operator +."
UNCOV
650
        return bitsArithOp__val(self._dtype.from_py(other), self, add)
×
651

652
    def __rshift__(self, other: Union[int, Self]) -> Self:
1✔
653
        "Operator >>."
654
        if self._dtype.signed:
1✔
655
            return bitsBitOp__ashr(self, other)
1✔
656
        else:
657
            return bitsBitOp__lshr(self, other)
1✔
658

659
    def __lshift__(self, other: Union[int, Self]) -> Self:
1✔
660
        "Operator <<. (shifts in 0)"
661
        try:
1✔
662
            o = int(other)
1✔
UNCOV
663
        except ValidityError:
×
UNCOV
664
            o = None
×
665

666
        v = self.__copy__()
1✔
667
        if o is None:
1!
UNCOV
668
            v.vld_mask = 0
×
UNCOV
669
            v.val = 0
×
670
        elif o == 0:
1✔
671
            return v
1✔
672
        else:
673
            if o < 0:
1!
UNCOV
674
                raise ValueError("negative shift count")
×
675
            t = self._dtype
1✔
676
            m = t.all_mask()
1✔
677
            v.vld_mask <<= o
1✔
678
            v.vld_mask |= mask(o)
1✔
679
            v.vld_mask &= m
1✔
680
            v.val <<= o
1✔
681
            v.val &= m
1✔
682
            assert v.val >= 0, v.val
1✔
683
        return v
1✔
684

685
    def __floordiv__(self, other: Union[int, Self]) -> Self:
1✔
686
        "Operator //."
687
        other_is_int = isinstance(other, int)
1✔
688
        t = self._dtype
1✔
689
        if other_is_int:
1✔
690
            v0 = self.val
1✔
691
            if t.signed:
1✔
692
                w = t.bit_length()
1✔
693
                v0 = to_signed(v0, w)
1✔
694
            v = v0 // other
1✔
695
            m = self._dtype.all_mask()
1✔
696
        else:
697
            if self._is_full_valid() and other._is_full_valid():
1✔
698
                v0 = self.val
1✔
699
                v1 = other.val
1✔
700
                if t.signed:
1✔
701
                    w = t.bit_length()
1✔
702
                    v0 = to_signed(v0, w)
1✔
703
                    v1 = to_signed(v1, w)
1✔
704
                v = v0 // v1
1✔
705
                m = self._dtype.all_mask()
1✔
706
            else:
707
                v = 0
1✔
708
                m = 0
1✔
709
        return self._dtype._from_py(v, m)
1✔
710

711
    def __mul__(self, other: Union[int, Self]) -> Self:
1✔
712
        "Operator *."
713
        resT = self._dtype
1✔
714
        other_is_int = isinstance(other, int)
1✔
715
        if other_is_int:
1!
UNCOV
716
            v0 = self.val
×
UNCOV
717
            if resT.signed:
×
UNCOV
718
                w = resT.bit_length()
×
UNCOV
719
                v0 = to_signed(v0, w)
×
720

UNCOV
721
            v = v0 * other
×
722
        elif isinstance(other, Bits3val):
1✔
723
            v0 = self.val
1✔
724
            v1 = other.val
1✔
725
            if resT.signed:
1✔
726
                w = resT.bit_length()
1✔
727
                v0 = to_signed(v0, w)
1✔
728
                v1 = to_signed(v1, w)
1✔
729

730
            v = v0 * v1
1✔
731
        else:
732
            raise TypeError(other)
1✔
733

734
        v &= resT.all_mask()
1✔
735
        if resT.signed:
1✔
736
            v = to_signed(v, resT.bit_length())
1✔
737

738
        if self._is_full_valid() and (other_is_int
1✔
739
                                      or other._is_full_valid()):
740
            vld_mask = resT._all_mask
1✔
741
        else:
742
            vld_mask = 0
1✔
743

744
        return resT._from_py(v, vld_mask)
1✔
745

746
    def __mod__(self, other: Union[int, Self]) -> Self:
1✔
747
        "Operator %."
748
        resT = self._dtype
×
749
        other_is_int = isinstance(other, int)
×
750
        if other_is_int:
×
751
            v0 = self.val
×
752
            if resT.signed:
×
753
                w = resT.bit_length()
×
754
                v0 = to_signed(v0, w)
×
755

756
            v = v0 % other
×
UNCOV
757
        elif isinstance(other, Bits3val):
×
758
            v0 = self.val
×
UNCOV
759
            v1 = other.val
×
760
            if resT.signed:
×
761
                w = resT.bit_length()
×
762
                v0 = to_signed(v0, w)
×
UNCOV
763
                v1 = to_signed(v1, w)
×
764

UNCOV
765
            v = v0 % v1
×
766
        else:
UNCOV
767
            raise TypeError(other)
×
768

UNCOV
769
        v &= resT.all_mask()
×
770
        if resT.signed:
×
UNCOV
771
            v = to_signed(v, resT.bit_length())
×
772

UNCOV
773
        if self._is_full_valid() and (other_is_int
×
774
                                      or other._is_full_valid()):
UNCOV
775
            vld_mask = resT._all_mask
×
776
        else:
UNCOV
777
            vld_mask = 0
×
778

UNCOV
779
        return resT._from_py(v, vld_mask)
×
780

781
    def _ternary(self, a, b):
1✔
782
        """
783
        Ternary operator (a if self else b).
784
        """
785
        try:
1✔
786
            if self:
1✔
787
                return a
1✔
788
            else:
789
                return b
1✔
UNCOV
790
        except ValidityError:
×
UNCOV
791
            pass
×
UNCOV
792
        res = copy(a)
×
UNCOV
793
        res.vld_mask = 0
×
UNCOV
794
        return res
×
795

796
    def __repr__(self):
797
        t = self._dtype
798
        if self.vld_mask != t.all_mask():
799
            m = ", mask {0:x}".format(self.vld_mask)
800
        else:
801
            m = ""
802
        typeDescrChar = 'b' if t.signed is None else 'i' if t.signed else 'u'
803
        if t.bit_length() == 1 and t.force_vector:
804
            vecSpec = "vec"
805
        else:
806
            vecSpec = ""
807
        return (f"<{self.__class__.__name__:s} {typeDescrChar:s}{t.bit_length():d}{vecSpec:s}"
808
                f" {to_signed(self.val, t.bit_length()) if t.signed else self.val:d}{m:s}>")
809

810

811
def bitsBitOp__ror(self: Bits3val, shAmount: Union[Bits3val, int]):
1✔
812
    """
813
    rotate right by specified amount
814
    """
815
    t = self._dtype
×
816
    width = t.bit_length()
×
817
    try:
×
UNCOV
818
        shAmount = int(shAmount)
×
UNCOV
819
    except ValidityError:
×
UNCOV
820
        return t.from_py(None)
×
UNCOV
821
    assert shAmount >= 0
×
822

UNCOV
823
    v = rotate_right(self.val, width, shAmount)
×
824
    if t.signed:
×
825
        v = to_signed(v, width)
×
826
    return t.from_py(v, rotate_right(self.vld_mask, width, shAmount))
×
827

828

829
def bitsBitOp__rol(self: Bits3val, shAmount: Union[Bits3val, int]):
1✔
830
    """
831
    rotate left by specified amount
832
    """
833
    t = self._dtype
×
834
    width = t.bit_length()
×
UNCOV
835
    try:
×
UNCOV
836
        shAmount = int(shAmount)
×
UNCOV
837
    except ValidityError:
×
UNCOV
838
        return t.from_py(None)
×
UNCOV
839
    assert shAmount >= 0
×
UNCOV
840
    v = rotate_left(self.val, width, shAmount)
×
UNCOV
841
    if t.signed:
×
UNCOV
842
        v = to_signed(v, width)
×
UNCOV
843
    return t.from_py(v, rotate_left(self.vld_mask, width, shAmount))
×
844

845

846
def bitsBitOp__lshr(self: Bits3val, shAmount: Union[Bits3val, int]) -> Bits3val:
1✔
847
    """
848
    logical shift right (shifts in 0)
849
    """
850
    t = self._dtype
1✔
851
    width = t.bit_length()
1✔
852
    try:
1✔
853
        sh = int(shAmount)
1✔
UNCOV
854
    except ValidityError:
×
UNCOV
855
        return t.from_py(None)
×
856
    assert sh >= 0, sh
1✔
857

858
    if sh >= width:
1!
859
        # all bits are shifted out
UNCOV
860
        return t.from_py(0, mask(width))
×
861

862
    v = self.val >> sh
1✔
863
    if t.signed:
1✔
864
        v = to_signed(v, width)
1✔
865
    newBitsMask = bit_field(width - sh, width)
1✔
866
    return t.from_py(v, (self.vld_mask >> sh) | newBitsMask)
1✔
867

868

869
def bitsBitOp__ashr(self: Bits3val, shAmount: Union[Bits3val, int]) -> Bits3val:
1✔
870
    """
871
    arithmetic shift right (shifts in MSB)
872
    """
873
    try:
1✔
874
        sh = int(shAmount)
1✔
UNCOV
875
    except ValidityError:
×
UNCOV
876
        sh = None
×
877

878
    v = self.__copy__()
1✔
879
    if sh is None:
1!
UNCOV
880
        v.vld_mask = 0
×
UNCOV
881
        v.val = 0
×
882
    elif sh == 0:
1✔
883
        pass
1✔
884
    else:
885
        if shAmount < 0:
1!
UNCOV
886
            raise ValueError("negative shift count")
×
887
        w = self._dtype.bit_length()
1✔
888
        if sh < w:
1!
889
            msb = v.val >> (w - 1)
1✔
890
            newBitsMask = bit_field(w - sh, w)
1✔
891
            v.vld_mask >>= sh
1✔
892
            v.vld_mask |= newBitsMask  # set newly shifted-in bits to defined
1✔
893
            v.val >>= sh
1✔
894
            if msb:
1✔
895
                v.val |= newBitsMask
1✔
896
        else:
897
            # completely shifted out
UNCOV
898
            v.val = 0
×
UNCOV
899
            v.vld_mask = mask(w)
×
900
    return v
1✔
901

902

903
def bitsBitOp__val(self: Bits3val, other: Union[Bits3val, int],
1✔
904
                   evalFn, getVldFn) -> "Bits3val":
905
    """
906
    Apply bitwise operator
907
    """
908
    res_t = self._dtype
1✔
909
    if isinstance(other, int):
1✔
910
        other = res_t.from_py(other)
1✔
911
    w = res_t.bit_length()
1✔
912
    assert w == other._dtype.bit_length(), (res_t, other._dtype)
1✔
913
    vld = getVldFn(self, other)
1✔
914
    res = evalFn(self.val, other.val) & vld
1✔
915
    assert res >= 0, res
1✔
916

917
    return res_t._from_py(res, vld)
1✔
918

919

920
def bitsCmp__val(self: Bits3val, other: Union[Bits3val, int],
1✔
921
                 evalFn: Callable[[int, int], bool]) -> "Bits3val":
922
    """
923
    Apply comparative operator
924
    """
925
    assert evalFn is not eq and evalFn is not ne, ("use bitsCmp__val_EQ/bitsCmp__val_NE instead")
1✔
926
    t = self._dtype
1✔
927
    w = t.bit_length()
1✔
928
    if isinstance(other, int):
1✔
929
        other = t.from_py(other)
1✔
930
        ot = other._dtype
1✔
931
    else:
932
        ot = other._dtype
1✔
933
        if bool(t.signed) != bool(ot.signed) or w != ot.bit_length():
1✔
934
            raise TypeError("Value compare supports only same width and sign type", t, ot)
1✔
935

936
    v0 = self.val
1✔
937
    v1 = other.val
1✔
938
    if t.signed:
1✔
939
        v0 = to_signed(v0, w)
1✔
940
        v1 = to_signed(v1, w)
1✔
941

942
    vld = self.vld_mask & other.vld_mask
1✔
943
    _vld = int(vld == t._all_mask)
1✔
944
    res = evalFn(v0, v1) & _vld
1✔
945

946
    return self._BOOL._from_py(int(res), int(_vld))
1✔
947

948

949
def bitsCmp__val_NE(self: Bits3val, other: Union[Bits3val, int]) -> "Bits3val":
1✔
950
    """
951
    Apply != operator
952
    """
953
    t = self._dtype
1✔
954
    w = t.bit_length()
1✔
955
    if isinstance(other, int):
1✔
956
        other = t.from_py(other)
1✔
957
        ot = other._dtype
1✔
958
    else:
959
        ot = other._dtype
1✔
960
        if bool(t.signed) != bool(ot.signed) or w != ot.bit_length():
1✔
961
            raise TypeError("Value compare supports only same width and sign type", t, ot)
1✔
962

963
    v0 = self.val
1✔
964
    v1 = other.val
1✔
965

966
    vld = self.vld_mask & other.vld_mask
1✔
967
    _vld = int(vld == t._all_mask)
1✔
968
    res = ((v0 ^ v1) & vld) != 0  # at least some valid bit non equal
1✔
969

970
    return self._BOOL._from_py(int(res), _vld | int(res))
1✔
971

972

973
def bitsCmp__val_EQ(self: Bits3val, other: Union[Bits3val, int]) -> "Bits3val":
1✔
974
    """
975
    Apply == operator
976
    """
977
    t = self._dtype
1✔
978
    w = t.bit_length()
1✔
979
    if isinstance(other, int):
1✔
980
        other = t.from_py(other)
1✔
981
        ot = other._dtype
1✔
982
    else:
983
        ot = other._dtype
1✔
984
        if bool(t.signed) != bool(ot.signed) or w != ot.bit_length():
1✔
985
            raise TypeError("Value compare supports only same width and sign type", t, ot)
1✔
986

987
    v0 = self.val
1✔
988
    v1 = other.val
1✔
989

990
    vld = self.vld_mask & other.vld_mask
1✔
991
    _vld = int(vld == t._all_mask)
1✔
992
    ne = ((v0 ^ v1) & vld) != 0  # all valid bits equal
1✔
993
    res = int(not ne)
1✔
994
    if not _vld:
1!
UNCOV
995
        if ne:
×
UNCOV
996
            _vld = 1  # some bits invalid, but from valid bytes we already know that the value does not equal
×
997
        else:
UNCOV
998
            res = 0  # set value for invalid value to 0
×
999
    return self._BOOL._from_py(int(res), _vld)
1✔
1000

1001

1002
def bitsArithOp__val(self: Bits3val, other: Union[Bits3val, int],
1✔
1003
                     evalFn: Callable[[int, int], int]) -> "Bits3val":
1004
    """
1005
    Apply arithmetic operator
1006
    """
1007
    if isinstance(other, int):
1!
1008
        other = self._dtype.from_py(other)
1✔
1009
    v = self.__copy__()
1✔
1010
    self_vld = self._is_full_valid()
1✔
1011
    other_vld = other._is_full_valid()
1✔
1012
    v0 = self.val
1✔
1013
    v1 = other.val
1✔
1014
    w = v._dtype.bit_length()
1✔
1015
    t = self._dtype
1✔
1016
    if t.signed:
1✔
1017
        v0 = to_signed(v0, w)
1✔
1018
        v1 = to_signed(v1, w)
1✔
1019

1020
    _v = evalFn(v0, v1)
1✔
1021

1022
    if t.signed:
1✔
1023
        _v = to_unsigned(_v, w)
1✔
1024
    else:
1025
        _v &= mask(w)
1✔
1026

1027
    v.val = _v
1✔
1028
    if self_vld and other_vld:
1!
1029
        v.vld_mask = mask(w)
1✔
1030
    else:
UNCOV
1031
        v.vld_mask = 0
×
1032

1033
    return v
1✔
1034

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

© 2025 Coveralls, Inc