-
Notifications
You must be signed in to change notification settings - Fork 0
/
ternary.py
97 lines (79 loc) · 3.06 KB
/
ternary.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
"""
Rosetta Code: http://www.rosettacode.org/wiki/Balanced_ternary#Python
"""
from functools import reduce
class BalancedTernary:
# Represented as a list of 0, 1 or -1s, with least significant digit first.
str2dig = {'+': 1, '-': -1, '0': 0} # immutable
dig2str = {1: '+', -1: '-', 0: '0'} # immutable
table = ((0, -1), (1, -1), (-1, 0), (0, 0), (1, 0), (-1, 1), (0, 1)) # immutable
def __init__(self, inp):
if isinstance(inp, str):
self.digits = [BalancedTernary.str2dig[c] for c in reversed(inp)]
elif isinstance(inp, int):
self.digits = self._int2ternary(inp)
elif isinstance(inp, BalancedTernary):
self.digits = list(inp.digits)
elif isinstance(inp, list):
if all(d in (0, 1, -1) for d in inp):
self.digits = list(inp)
else:
raise ValueError("BalancedTernary: Wrong input digits.")
else:
raise TypeError("BalancedTernary: Wrong constructor input.")
@staticmethod
def _int2ternary(n):
if n == 0: return []
if (n % 3) == 0: return [0] + BalancedTernary._int2ternary(n // 3)
if (n % 3) == 1: return [1] + BalancedTernary._int2ternary(n // 3)
if (n % 3) == 2: return [-1] + BalancedTernary._int2ternary((n + 1) // 3)
def to_int(self):
return reduce(lambda y,x: x + 3 * y, reversed(self.digits), 0)
def __repr__(self):
if not self.digits: return "0"
return "".join(BalancedTernary.dig2str[d] for d in reversed(self.digits))
@staticmethod
def _neg(digs):
return [-d for d in digs]
def __neg__(self):
return BalancedTernary(BalancedTernary._neg(self.digits))
@staticmethod
def _add(a, b, c=0):
if not (a and b):
if c == 0:
return a or b
else:
return BalancedTernary._add([c], a or b)
else:
(d, c) = BalancedTernary.table[3 + (a[0] if a else 0) + (b[0] if b else 0) + c]
res = BalancedTernary._add(a[1:], b[1:], c)
# trim leading zeros
if res or d != 0:
return [d] + res
else:
return res
def __add__(self, b):
return BalancedTernary(BalancedTernary._add(self.digits, b.digits))
def __sub__(self, b):
return self + (-b)
@staticmethod
def _mul(a, b):
if not (a and b):
return []
else:
if a[0] == -1: x = BalancedTernary._neg(b)
elif a[0] == 0: x = []
elif a[0] == 1: x = b
else: assert False
y = [0] + BalancedTernary._mul(a[1:], b)
return BalancedTernary._add(x, y)
def __mul__(self, b):
return BalancedTernary(BalancedTernary._mul(self.digits, b.digits))
# a = BalancedTernary("+-0++0+")
# print("a:", a.to_int(), a)
# b = BalancedTernary(-436)
# print("b:", b.to_int(), b)
# c = BalancedTernary("+-++-")
# print("c:", c.to_int(), c)
# r = a * (b - c)
# print("a * (b - c):", r.to_int(), r)