154 lines
3.6 KiB
Python
154 lines
3.6 KiB
Python
import ctypes, unittest
|
|
|
|
|
|
def is_i32(n):
|
|
return -2**31 <= n < 2**31
|
|
|
|
|
|
class BigInt:
|
|
|
|
def __init__(self, initial=0):
|
|
# We store a sign bit (True == non-negative)
|
|
# and a list of OberonInt, least significant digit
|
|
# to most.
|
|
self.sign = initial >= 0
|
|
if not self.sign:
|
|
initial = -initial
|
|
self.digits = list(self.digitize(initial)) # List of OberonInt.
|
|
|
|
@staticmethod
|
|
def digitize(n):
|
|
if n < 0:
|
|
raise ValueError(f'Non-negative only: {n}')
|
|
if not n:
|
|
yield OberonInt(0)
|
|
return # Not strictly needed as the following while
|
|
# will not do anything for n == 0.
|
|
while n:
|
|
n, digit = divmod(n, 2**31)
|
|
yield OberonInt(digit)
|
|
|
|
def __str__(self):
|
|
return str(self.to_int())
|
|
|
|
def to_int(self):
|
|
power = 1
|
|
n = 0
|
|
for digit in self.digits:
|
|
n += digit.value.value * power
|
|
power <<= 31
|
|
if not self.sign:
|
|
n = -n
|
|
return n
|
|
|
|
|
|
class OberonInt:
|
|
'''
|
|
Let's model the Oberon RISC integers,
|
|
32-bit, two's complement.
|
|
'''
|
|
|
|
def __init__(self, initial=0):
|
|
assert is_i32(initial)
|
|
self.value = ctypes.c_int32(initial)
|
|
assert self.value.value == initial
|
|
|
|
def __add__(self, other):
|
|
'''
|
|
Return carry bit and new value.
|
|
'''
|
|
if not isinstance(other, OberonInt):
|
|
other = OberonInt(other)
|
|
n = self.value.value + other.value.value
|
|
carry = not is_i32(n)
|
|
if carry:
|
|
n &= (2**31-1)
|
|
return int(carry), OberonInt(n)
|
|
|
|
__radd__ = __add__
|
|
|
|
def negate(self):
|
|
# Instead of binary ops, just cheat:
|
|
return OberonInt(
|
|
0 # Handle negation of obmin.
|
|
if self.value.value == -(2**31)
|
|
else -self.value.value
|
|
)
|
|
|
|
def __sub__(self, other):
|
|
if not isinstance(other, OberonInt):
|
|
other = OberonInt(other)
|
|
return self + other.negate()
|
|
|
|
__rsub__ = __sub__
|
|
|
|
def __repr__(self):
|
|
#b = bin(self.value.value & (2**32-1))
|
|
return f'OberonInt({self.value.value})'
|
|
|
|
def __eq__(self, other):
|
|
assert isinstance(other, OberonInt)
|
|
return self.value.value == other.value.value
|
|
|
|
|
|
obmin, zero, one, obmax = map(OberonInt, (
|
|
-(2**31),
|
|
0,
|
|
1,
|
|
2**31-1,
|
|
))
|
|
|
|
|
|
class OberonIntTest(unittest.TestCase):
|
|
|
|
def test_Addition(self):
|
|
carry, z = obmax + one
|
|
self.assertTrue(carry)
|
|
self.assertEqual(z, zero)
|
|
|
|
def test_Negation(self):
|
|
negative_one = one.negate()
|
|
carry, m = obmin + negative_one
|
|
self.assertTrue(carry)
|
|
self.assertEqual(m, obmax)
|
|
|
|
def test_Subtraction(self):
|
|
# Ergo, subtraction.
|
|
carry, m = obmin - one
|
|
self.assertTrue(carry)
|
|
self.assertEqual(m, obmax)
|
|
|
|
def test_twice_max(self):
|
|
carry, hmm = obmax + obmax
|
|
self.assertTrue(carry)
|
|
self.assertEqual(hmm.value.value, 2**31 - 2)
|
|
|
|
carry, eh = obmax - hmm
|
|
self.assertFalse(carry)
|
|
self.assertEqual(eh, one)
|
|
self.assertEqual( (hmm + one)[1] , obmax )
|
|
|
|
|
|
class BigIntTest(unittest.TestCase):
|
|
|
|
def test_to_int(self):
|
|
n = 12345678901234567898090123445678990
|
|
x = BigInt(n).to_int()
|
|
self.assertEqual(n, x)
|
|
|
|
|
|
if __name__ == '__main__':
|
|
unittest.main()
|
|
|
|
|
|
|
|
|
|
|
|
|
|
## if initial >= 2**31:
|
|
## raise ValueError(f'too big: {initial!r}')
|
|
## if initial < -2**31:
|
|
## raise ValueError(f'too small: {initial!r}')
|
|
|
|
|