Created
June 26, 2020 19:55
-
-
Save acatton/63b1ea6d4062091883efc01e4ffb619f to your computer and use it in GitHub Desktop.
Is there any way to carry over 1?
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/env python3 | |
import typing as ty | |
import itertools | |
import random | |
import logging | |
MAX_COMPOSITE = 5000 # Maximum amount of numbers used to compose a number in gen() | |
MAX_NUM = 1000000 # Maximum number generated when composing a number in gen() | |
MAX_BASE = 1000 # Maximum base | |
TRIES = 10000 | |
Result = ty.NamedTuple("Result", [("value", int), ("carry", int)]) | |
def add_digits(*nums: int, base: int) -> Result: | |
"""Add two numbers. | |
>>> add_digits(9, 8, base=10) | |
Result(value=7, carry=1) | |
>>> add_digits(9, 9, 9, base=10) | |
Result(value=7, carry=2) | |
""" | |
assert all(i < base for i in nums) | |
assert base != 0 | |
result: int = sum(nums) | |
carry: int = result // base | |
overflowed_result: int = result - carry * base | |
return Result(overflowed_result, carry) | |
def digits_of(x: int, base: int) -> ty.List[int]: | |
"""Convert an arbitrary presision number to a list of digits. | |
The digits order is inverted. (First digit is the most significant digit) | |
>>> digits_of(0x1234A, base=16) | |
[10, 4, 3, 2, 1] | |
""" | |
assert base != 0 | |
accumulator: ty.List[int] = [] | |
value: int = x | |
while value > 0: | |
next_value: int = value // base | |
n: int = value - next_value * base | |
accumulator.append(n) | |
value = next_value | |
return accumulator | |
def to_n(x: ty.List[int], base: int) -> int: | |
"""Convert a list of digits to an arbitrary precision number. | |
>>> to_n([3, 2, 1], base=10) | |
123 | |
""" | |
assert base != 0 | |
accumulator = 0 | |
for p, n in enumerate(x): | |
accumulator += n * (base ** p) | |
return accumulator | |
def gen(rand: random.Random) -> int: | |
"""Generate a random number""" | |
accumulator = 0 | |
for _ in range(rand.randint(1, MAX_COMPOSITE)): | |
accumulator += rand.randint(1, MAX_NUM) | |
return accumulator | |
if __name__ == "__main__": | |
## We use a pseudo-random generator for full reproducibility | |
rand = random.Random(42) | |
for _ in range(TRIES): | |
base = rand.randint(2, MAX_BASE) | |
x_orig, y_orig = gen(rand), gen(rand) | |
x, y = digits_of(x_orig, base), digits_of(y_orig, base) | |
## Addition | |
result = [] | |
carry = 0 | |
for n, (a, b) in enumerate(itertools.zip_longest(x, y, fillvalue=0)): | |
digit, carry = add_digits(a, b, carry, base=base) | |
if carry > 1: | |
logging.warning( | |
f"Carry for {x_orig} + {y_orig} in base {base} was {carry} at position {n}" | |
) | |
result.append(digit) | |
if carry > 0: | |
result.append(carry) | |
got, expect = to_n(result, base), (x_orig + y_orig) | |
if to_n(result, base) != x_orig + y_orig: | |
logging.error( | |
f"Wrong result for {x_orig} + {y_orig} in base {base}: got {got}, expected {expect}" | |
) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment