Created
May 31, 2013 19:51
-
-
Save crodjer/5687539 to your computer and use it in GitHub Desktop.
An unbounded integer stored in a string.
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 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