Skip to content

Instantly share code, notes, and snippets.

@crodjer
Created May 31, 2013 19:51
Show Gist options
  • Save crodjer/5687539 to your computer and use it in GitHub Desktop.
Save crodjer/5687539 to your computer and use it in GitHub Desktop.
An unbounded integer stored in a string.
#!/usr/bin/env python3
import unittest
import random
try:
# Python3, cool!
from itertools import zip_longest
except ImportError:
# I am on python2
from itertools import izip_longest as zip_longest
class StringIntegerException(Exception):
pass
class StringInteger(str):
'''
A integer stored as strings. Currently only supports addition.
Usage:
> integer_f = StringInteger("45345")
> integer_s = StringInteger("4953")
> integer_f + integer_s
'50298' # A new StringInteger object.
'''
def __init__(self, string):
'''
Iniitializer. Acts as a validator - Rejects non numeric characters as input.
'''
for char in string.upper():
if not (48 <= ord(char) <= 70):
raise StringIntegerException("Invalid character %c input for '%s'"
%(char, self.__class__.__name__))
super(StringInteger, self).__init__()
@property
def as_iterable(self):
'''
Return an iterable object over the digits as integers.
'''
return map(int, self)
@property
def as_list(self):
'''
Return a list of digits as integers.
'''
return list(self.as_iterable)
@classmethod
def from_int(cls, integer):
'''
Generate a StringInteger from a integer as a input
'''
return cls(str(integer))
@classmethod
def from_list(cls, integer_list):
'''
Generate a StringInteger from a list of integer digits as a input
'''
return cls("".join(map(str, integer_list)))
def __add__(self, other_sint):
'''
Override add, to behave like arithmetic addition of two numbers.
Returns a new StringInteger instance
'''
first = self.as_list
second = other_sint.as_list
first.reverse()
second.reverse()
carry = 0
result = []
for fst, snd in zip_longest(first, second, fillvalue=0):
digit_sum = str(fst + snd + carry)
result_digit = int(digit_sum[-1])
carry = int(digit_sum[:-1] or 0)
result.insert(0, result_digit)
if carry > 0:
# Prepend any remaining carrys
result = list(map(int, str(carry))) + result
return self.__class__.from_list(result)
SI = StringInteger.from_int
class StringIntegerTest(unittest.TestCase):
def setUp(self):
# Set a custom initial random seed, for comparibility across tests.
random.seed(1)
def _rand_int(self):
return random.randint(0, 10 ** 10)
def _test_add(self, first, second):
self.assertEqual(str(first + second),
SI(first) + SI(second))
def test_multiple_additions(self):
for i in range(5000):
first = self._rand_int()
second = self._rand_int()
self._test_add(first, second)
if __name__ == '__main__':
unittest.main()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment