I guess subtraction works!?

This commit is contained in:
Simon Forman 2022-10-04 16:28:43 -07:00
parent 70b8bbdc0e
commit 4cea474166
1 changed files with 173 additions and 6 deletions

View File

@ -1,3 +1,4 @@
from copy import copy
from itertools import zip_longest
from pprint import pprint as P
import unittest
@ -22,10 +23,10 @@ class BigInt:
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.
#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)
@ -43,12 +44,33 @@ class BigInt:
n = -n
return n
def negate(self):
result = BigInt()
result.sign = not self.sign
result.digits = [
OberonInt(digit.value ^ 2**31 - 1)
for digit in self.digits
]
return result
def __add__(self, other):
if not isinstance(other, BigInt):
other = BigInt(other)
if self.sign == other.sign:
return self.add_like_signs(other)
return self.add_unlike_signs(other)
def __sub__(self, other):
if not isinstance(other, BigInt):
other = BigInt(other)
#print(23)
#print(self.to_int(), '-', other.to_int())
z = copy(other)
z.sign = not z.sign
#print(self.to_int(), '+', z.to_int(), 'sub')
return self + z
def add_like_signs(self, other):
'''
Add a BigInt of the same sign as self.
@ -71,6 +93,108 @@ class BigInt:
result.digits = out
return result
def add_unlike_signs(self, other):
'''
Add a BigInt of unlike sign as self.
'''
assert self.sign != other.sign
# So we have -a and +b
# or +a and -b
if self.sign:
a, b = self, other
else:
b, a = self, other
#print(a.to_int(), '+', b.to_int(), 'add_unlike_signs')
# So now we have:
# a + (-b) == a - b
# I don't know how to subtract a larger (abs) number
# from a smaller (abs) one
# However:
#
# a - b == -(b - a)
#
# I.e. 9 - 17 == -(17 - 9)
#if abs(a) < abs(b):
if not a.abs_gt_abs(b):
#print(f'abs({a.to_int()}) < abs({b.to_int()})')
x = b._subtract_smaller(a)
x.sign = not x.sign
return x
#print(f'abs({a.to_int()}) > abs({b.to_int()})')
return a._subtract_smaller(b)
def _subtract_smaller(self, other):
assert self.abs_gt_abs(other)
out = []
carry = 0
Z = zip_longest(
self.digits,
other.digits,
fillvalue=zero,
)
#P(list(Z))
for a, b in Z:
carry, digit = a.sub_with_carry(b, carry)
out.append(digit)
if carry:
out.append(one)
result = BigInt()
result.sign = self.sign
result.digits = out
return result
def abs_gt_abs(self, other):
a_len, b_len = len(self.digits), len(other.digits)
if a_len > b_len: return True
if a_len < b_len: return False
# a_len == b_len
if not a_len: # a == b == 0
return False
return self.digits[-1] > other.digits[-1]
## result = BigInt()
## result.sign = self.sign
## result.digits = (
## self.subtract_digits(other)
## if self.sign else
## other.subtract_digits(self)
## )
## return result
## def subtract_digits(self, other):
## return []
##def _sort_key(list_of_obint):
## n = len(list_of_obint)
## last = list_of_obint[-1] if n else None
## return n, zero
##def subtract_list_of_obints(A, B):
## L = [A, B]
## K = sorted(L, key=_sort_key)
## A, B = K
## swapped = L != K
## carry = 0
## out = []
## for a, b in zip_longest(A, B, fillvalue=zero):
## carry, digit = a.sub_with_carry(b, carry)
## out.append(digit)
## if carry:
## out.append(one)
## result = BigInt()
## result.sign = self.sign
## result.digits = out
## return result
class OberonInt:
'''
@ -94,6 +218,24 @@ class OberonInt:
assert not z, repr(z)
return c, digit
def sub_with_carry(self, other, carry):
'''
In terms of single base-10 skool arithmetic:
a, b in {0..9}
carry in {0..1}
0 - 9 - 1
9 + 9 + 1 = 18 + 1 = 19
aka = 1,(8+1) = 1, 9
'''
c, digit = self - other
if carry:
z, digit = digit - one
assert not z, repr(z)
return c, digit
def __init__(self, initial=0):
assert is_i32(initial)
self.value = initial
@ -211,6 +353,33 @@ class BigIntTest(unittest.TestCase):
t = z.to_int()
self.assertEqual(t, n + m)
def test_Addition_of_unlike_signs(self):
n = 12345678901234567898090123445678990
m = -901234567898090
x = BigInt(n)
y = BigInt(m)
z = x + y
t = z.to_int()
self.assertEqual(t, n + m)
def _test_invert(self):
n = 7 * (2**16)
x = BigInt(n)
y = x.negate()
print()
print(y.to_int(), bin(y.to_int()), y.digits)
print(x.to_int(), bin(x.to_int()), x.digits)
print()
print(x + y)
def test_Subtraction(self):
n = 12345678901234567898090123445678990
m = 901234567898090
x = BigInt(n)
y = BigInt(m)
z = x - y
t = z.to_int()
self.assertEqual(t, n - m)
if __name__ == '__main__':
@ -225,5 +394,3 @@ if __name__ == '__main__':
## raise ValueError(f'too big: {initial!r}')
## if initial < -2**31:
## raise ValueError(f'too small: {initial!r}')