Skip to content

Instantly share code, notes, and snippets.

@acatton
Created June 26, 2020 19:55
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save acatton/63b1ea6d4062091883efc01e4ffb619f to your computer and use it in GitHub Desktop.
Save acatton/63b1ea6d4062091883efc01e4ffb619f to your computer and use it in GitHub Desktop.
Is there any way to carry over 1?
#!/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