dev(dtypes): add a function to make Integers able to hold a set of values

This commit is contained in:
Arthur Meyre
2021-07-30 14:14:34 +02:00
parent e55284b3ea
commit b1a3b28a20
2 changed files with 103 additions and 1 deletions

View File

@@ -1,5 +1,8 @@
"""This file holds the definitions for integer types"""
import math
from typing import Iterable
from . import base
@@ -79,3 +82,60 @@ def create_unsigned_integer(bit_width: int) -> Integer:
UnsignedInteger = create_unsigned_integer
def make_integer_to_hold_ints(values: Iterable[int], force_signed: bool) -> Integer:
"""Returns an Integer able to hold all values, it is possible to force the Integer to be signed
Args:
values (Iterable[int]): The values to hold
force_signed (bool): Set to True to force the result to be a signed Integer
Returns:
Integer: The Integer able to hold values
"""
assert all(map(lambda x: isinstance(x, int), values))
min_value = min(values)
max_value = max(values)
make_signed_integer = force_signed or min_value < 0
num_bits = max(
get_bits_to_represent_int(min_value, make_signed_integer),
get_bits_to_represent_int(max_value, make_signed_integer),
)
return Integer(num_bits, is_signed=make_signed_integer)
def get_bits_to_represent_int(value: int, force_signed: bool) -> int:
"""Returns how many bits are required to represent a single int
Args:
value (int): The int for which we want to know how many bits are required
force_signed (bool): Set to True to force the result to be a signed Integer
Returns:
int: required amount of bits
"""
# Writing this in a very dumb way
num_bits: int
if value < 0:
abs_value = abs(value)
if abs_value > 1:
num_bits = math.ceil(math.log2(abs_value)) + 1
else:
# -1 case
num_bits = 2
else:
if value > 1:
num_bits = math.ceil(math.log2(value + 1))
else:
# 0 and 1 case
num_bits = 1
if force_signed:
num_bits += 1
return num_bits

View File

@@ -4,7 +4,12 @@ import random
import pytest
from hdk.common.data_types.integers import Integer, SignedInteger, UnsignedInteger
from hdk.common.data_types.integers import (
Integer,
SignedInteger,
UnsignedInteger,
make_integer_to_hold_ints,
)
@pytest.mark.parametrize(
@@ -68,3 +73,40 @@ def test_basic_integers(integer: Integer, expected_min: int, expected_max: int):
def test_integers_repr(integer: Integer, expected_repr_str: str):
"""Test integer repr"""
assert integer.__repr__() == expected_repr_str
@pytest.mark.parametrize(
"values,force_signed,expected_result",
[
([0], False, Integer(1, is_signed=False)),
([0], True, Integer(2, is_signed=True)),
([1], False, Integer(1, is_signed=False)),
([1], True, Integer(2, is_signed=True)),
([-1], False, Integer(2, is_signed=True)),
([-2], False, Integer(2, is_signed=True)),
([0, 1], False, Integer(1, is_signed=False)),
([0, 1], True, Integer(2, is_signed=True)),
([7], False, Integer(3, is_signed=False)),
([7], True, Integer(4, is_signed=True)),
([8], False, Integer(4, is_signed=False)),
([8], True, Integer(5, is_signed=True)),
([-7], False, Integer(4, is_signed=True)),
([-8], False, Integer(4, is_signed=True)),
([-7, -8], False, Integer(4, is_signed=True)),
([-9], False, Integer(5, is_signed=True)),
([-9], True, Integer(5, is_signed=True)),
([0, 127], False, Integer(7, is_signed=False)),
([0, 127], True, Integer(8, is_signed=True)),
([0, 128], False, Integer(8, is_signed=False)),
([0, 128], True, Integer(9, is_signed=True)),
([-1, 127], False, Integer(8, is_signed=True)),
([-256, 127], False, Integer(9, is_signed=True)),
([-128, 127], False, Integer(8, is_signed=True)),
([-128, 128], False, Integer(9, is_signed=True)),
([-13, 4], False, Integer(5, is_signed=True)),
([42, 1019], False, Integer(10, is_signed=False)),
],
)
def test_make_integer_to_hold(values, force_signed, expected_result):
"""Test make_integer_to_hold"""
assert expected_result == make_integer_to_hold_ints(values, force_signed)