# This class has most of the same operators as Integer, is a Comparable, and
# can be used in a Range.
#
# All of the algorithms herein are of my own devising, and are nobody's fault
# but my own.
#
# This code ic © 2001 Sean  Russell.<ser@germane-software.com>
class BTernary
	include Comparable

	# Constructor
	# @p num will be used to initialize the value of this BTernary.  If an
	# Integer, the integer will be converted to the value.  If a String, it
	# should consist of only T, 0, and 1 characters, and it will be parsed to
	# set the value.  If an Array, it should consist of only -1, 0, and 1s.  If
	# a BTernary, the parameter will be cloned.
	def initialize num
		@value = []
		if num.kind_of? BTernary
			parse_from num.to_s
		elsif num.kind_of? Integer
			from_int num
		elsif num.kind_of? String
			parse_from num
		elsif num.kind_of? Array
			@value = num
			reset
		end
	end

	# @r the value of this number converted to an Integer
	def to_i
		@intval
	end

	# @r a String representation of this BTernary.  The String is made up of T,
	# 0, and 1s; T represents bar-one.
	def to_s
		@string
	end

	# @r an Array of bits that make up this BTernary.  Each bit is -1, 0, or 1,
	# corresponding to bar-one, zero, or one.
	def to_a
		@value.clone
	end

	def [](ind)
		@value[ind]
	end

	# @r the value of this BTernary negated
	def -@
		# To negate a BTernary number, simply reverse the sign of each bit
		result = @value.collect { |x| x * -1 }
		BTernary.new result
	end

	# Add another BTernary to this one and return the result
	# @r a BTernary
	def +(other)
		result = []
		me = @value.reverse
		he = other.to_a.reverse
		max = [me.size, he.size].max
		carry = 0
		max.times do |ind|
			top = bot = 0
			top = me[ind] if ind < me.size
			bot = he[ind] if ind < he.size
			val = top + bot + carry
			carry = 0
			if val.abs > 1
				carry = (val / 2.0).truncate
				val = ( val.abs==3 ? 0 : -1 )*carry
			end
			result << val
		end
		result << carry if carry != 0
		result.pop while result[-1] == 0
		rv = BTernary.new result.reverse
		rv
	end

	# Subtract another BTernary from this one and return the result
	# @r a BTernary
	def -(other)
		self + -other
	end

	# A bit-wise comparison of the values.
	# @r 1 if the value of this BTernary is greater;
	# 0 if the value is equal to;
	# -1 if the value is less than.
	def <=>( other )
		othera = other.to_a
		#puts "Comparing [#{@value.join ', '}] to [#{othera.join ', '}]"
		rv = @value[0] <=> othera[0]
		return rv unless rv == 0
		case @value.size <=> othera.size
		when -1
			return -1 if @value[0] == 1
			return 1
		when 1
			return 1 if @value[0] == 1
			return -1
		end
		# if we get here, the sizes are the same
		@value.size.times do |index|
			rv = @value[index] <=> othera[index]
			return rv unless rv == 0
		end
		0
	end

	def succ
		self + ONE
	end
	alias :next :succ

	def upto( max )
		count = self
		#puts "#{self} up to #{max}"
		while count <= max
			yield count
			count = count + ONE
		end
		self
	end

	def times(&block)
		ZERO.upto(self-ONE, &block)
	end

	def downto( min )
		count = self
		while count >= min
			yield count
			count = count + NONE
		end
		self
	end

	def abs
		return -self if @value[0]==-1
		self
	end

	def inspect
		@string
	end

	# THE FOLLOWING ARE TRIVIALLY IMPLEMENTED.  IE, they cheat, by operating
	# on the int values.  They will be replaced with "native" implementations
	# as I have time.
	def *(other)
		BTernary.new( to_i * other.to_i )
	end

	def /(other)
		BTernary.new( to_i / other.to_i )
	end

	def &(other)
		BTernary.new( to_i & other.to_i )
	end

	def |(other)
		BTernary.new( to_i | other.to_i )
	end

	############################################################
	# Private methods                                          #
	############################################################

	private

	def reset
		@intval = 0
		@value << 0 if @value.size == 0
		pow = 0
		@value.reverse_each do |pos|
			@intval += 3**pow * pos
			pow += 1
		end
		@string = @value.collect { |x| STR[x+1] }.join
	end

	def from_int n
		# ai = [ (n - f(i)) / 3**i  % 3   | 0 -> 0   ]
		#      [                          | 1 -> 1   ]
		#      [                          | 2 -> -1  ]
		@value.clear
		if n == 0
			@value[0] = Z
		else
			num = 0
			ind = 0
			num_bits = num_bits(n)
			0.upto num_bits do |i|
				three_i = 3**i
				f_i = (three_i / 2).floor
				a_i = ( (n-f_i).to_f/three_i ).ceil % 3
				@value[i] = a_i
				@value[i] = N if a_i == 2
			end
			@value.reverse!
		end
		reset
	end

	def parse_from string
		@value = string.split("").collect do |item|
			STR.index(item) - 1
		end
		reset
	end

	# The number of bits a number takes is equal to i, where 
	# 3**(i+1) / 3 <= n.  Solve for i, and you get the following
	def num_bits num
		num = num.abs
		((Math.log(2*num)/Math.log(3))-1).ceil
	end


	STR = [ 'T', '0', '1' ]
	N = -1
	Z = 0
	P = 1

	public

	ONE = BTernary.new(1)
	ZERO = BTernary.new(0)
	NONE = BTernary.new(-1)

end
