"""
The :mod:`pyeda.util` module contains top-level utilities,
such as fundamental functions and decorators.
Interface Functions:
* :func:`bit_on` --- Return the value of a number's bit position
* :func:`clog2` --- Return the ceiling log base two of an integer
* :func:`parity` --- Return the parity of an integer
Decorators:
* :func:`cached_property`
"""
[docs]def bit_on(num: int, bit: int) -> int:
"""Return the value of a number's bit position.
For example, since :math:`42 = 2^1 + 2^3 + 2^5`,
this function will return 1 in bit positions 1, 3, 5:
>>> [bit_on(42, i) for i in range(clog2(42))]
[0, 1, 0, 1, 0, 1]
"""
return (num >> bit) & 1
[docs]def clog2(num: int) -> int:
r"""Return the ceiling log base two of an integer :math:`\ge 1`.
This function tells you the minimum dimension of a Boolean space with at
least N points.
For example, here are the values of ``clog2(N)`` for :math:`1 \le N < 18`:
>>> [clog2(n) for n in range(1, 18)]
[0, 1, 2, 2, 3, 3, 3, 3, 4, 4, 4, 4, 4, 4, 4, 4, 5]
This function is undefined for non-positive integers:
>>> clog2(0)
Traceback (most recent call last):
...
ValueError: expected num >= 1
"""
if num < 1:
raise ValueError("expected num >= 1")
accum, shifter = 0, 1
while num > shifter:
shifter <<= 1
accum += 1
return accum
[docs]def parity(num: int) -> int:
"""Return the parity of a non-negative integer.
For example, here are the parities of the first ten integers:
>>> [parity(n) for n in range(10)]
[0, 1, 1, 0, 1, 0, 0, 1, 1, 0]
This function is undefined for negative integers:
>>> parity(-1)
Traceback (most recent call last):
...
ValueError: expected num >= 0
"""
if num < 0:
raise ValueError("expected num >= 0")
par = 0
while num:
par ^= (num & 1)
num >>= 1
return par
[docs]def cached_property(func):
"""Return a cached property calculated by the input function.
Unlike the ``property`` decorator builtin, this decorator will cache the
return value in order to avoid repeated calculations.
This is particularly useful when the property involves some non-trivial
computation.
For example, consider a class that models a right triangle.
The hypotenuse ``c`` will only be calculated once.
.. code-block:: python
import math
class RightTriangle:
def __init__(self, a, b):
self.a = a
self.b = b
@cached_property
def c(self):
return math.sqrt(self.a**2 + self.b**2)
"""
def get(self):
"""this docstring will be over-written by func.__doc__"""
try:
return self._property_cache[func]
except AttributeError:
self._property_cache = dict()
prop = self._property_cache[func] = func(self)
return prop
except KeyError:
prop = self._property_cache[func] = func(self)
return prop
get.__doc__ = func.__doc__
return property(get)