129 lines
2.5 KiB
Ruby
129 lines
2.5 KiB
Ruby
class BalancedTernary
|
|
def initialize(str = "")
|
|
if str !~ /^[-+0]+$/
|
|
raise ArgumentError, "invalid BalancedTernary number: #{str}"
|
|
end
|
|
@digits = trim0(str)
|
|
end
|
|
|
|
def self.from_int(value)
|
|
n = value
|
|
digits = ""
|
|
while n != 0
|
|
quo, rem = n.divmod(3)
|
|
case rem
|
|
when 0
|
|
digits = "0" + digits
|
|
n = quo
|
|
when 1
|
|
digits = "+" + digits
|
|
n = quo
|
|
when 2
|
|
digits = "-" + digits
|
|
n = quo + 1
|
|
end
|
|
end
|
|
new(digits)
|
|
end
|
|
|
|
def to_int
|
|
@digits.chars.inject(0) do |sum, char|
|
|
sum *= 3
|
|
case char
|
|
when "+"
|
|
sum += 1
|
|
when "-"
|
|
sum -= 1
|
|
end
|
|
sum
|
|
end
|
|
end
|
|
alias :to_i :to_int
|
|
|
|
def to_s
|
|
@digits
|
|
end
|
|
alias :inspect :to_s
|
|
|
|
ADDITION_TABLE = {
|
|
"-" => {"-" => ["-","+"], "0" => ["0","-"], "+" => ["0","0"]},
|
|
"0" => {"-" => ["0","-"], "0" => ["0","0"], "+" => ["0","+"]},
|
|
"+" => {"-" => ["0","0"], "0" => ["0","+"], "+" => ["+","-"]},
|
|
}
|
|
|
|
def +(other)
|
|
maxl = [to_s, other.to_s].collect {|s| s.length}.max
|
|
a = pad0(to_s, maxl)
|
|
b = pad0(other.to_s, maxl)
|
|
carry = "0"
|
|
sum = a.reverse.chars.zip( b.reverse.chars ).inject("") do |sum, (c1, c2)|
|
|
carry1, digit1 = ADDITION_TABLE[c1][c2]
|
|
carry2, digit2 = ADDITION_TABLE[carry][digit1]
|
|
sum = digit2 + sum
|
|
carry = ADDITION_TABLE[carry1][carry2][1]
|
|
sum
|
|
end
|
|
self.class.new(carry + sum)
|
|
end
|
|
|
|
MULTIPLICATION_TABLE = {
|
|
"-" => "+0-",
|
|
"0" => "000",
|
|
"+" => "-0+",
|
|
}
|
|
|
|
def *(other)
|
|
product = self.class.new("0")
|
|
other.to_s.each_char do |bdigit|
|
|
row = to_s.tr("-0+", MULTIPLICATION_TABLE[bdigit])
|
|
product += self.class.new(row)
|
|
product << 1
|
|
end
|
|
product >> 1
|
|
end
|
|
|
|
# negation
|
|
def -@()
|
|
self * BalancedTernary.new("-")
|
|
end
|
|
|
|
# subtraction
|
|
def -(other)
|
|
self + (-other)
|
|
end
|
|
|
|
# shift left
|
|
def <<(count)
|
|
@digits = trim0(@digits + "0"*count)
|
|
self
|
|
end
|
|
|
|
# shift right
|
|
def >>(count)
|
|
@digits[-count..-1] = "" if count > 0
|
|
@digits = trim0(@digits)
|
|
self
|
|
end
|
|
|
|
private
|
|
|
|
def trim0(str)
|
|
str = str.sub(/^0+/, "")
|
|
str = "0" if str.empty?
|
|
str
|
|
end
|
|
|
|
def pad0(str, len)
|
|
str.rjust(len, "0")
|
|
end
|
|
end
|
|
|
|
a = BalancedTernary.new("+-0++0+")
|
|
b = BalancedTernary.from_int(-436)
|
|
c = BalancedTernary.new("+-++-")
|
|
calc = a * (b - c)
|
|
puts "%s\t%d\t%s\n" % ['a', a.to_i, a]
|
|
puts "%s\t%d\t%s\n" % ['b', b.to_i, b]
|
|
puts "%s\t%d\t%s\n" % ['c', c.to_i, c]
|
|
puts "%s\t%d\t%s\n" % ['a*(b-c)', calc.to_i, calc]
|