#!/usr/local/bin/ruby

require "runit/testcase"
require "runit/cui/testrunner"
require "ternary"

class Tester < RUNIT::TestCase
	POS_STR = { 
		"0", 0,
		"1",             1,
		"1T",            2,
		"10",            3,
		"11",            4,
		"1TT",           5,
		"1T0",           6,
		"1T1",           7,
		"10T",           8,
		"100",           9,
		"101",           10,
		"11T",           11,
		"110",           12,
		"111",           13,
		"1TTT",          14,
		"1TT0",          15,
		"1TT1",          16,
		"1T0T",          17,
		"1T00",          18,
		"1T01",          19,
		"1T1T",          20,
		"1T10",          21,
		"1T11",          22,
		"10TT",          23,
		"10T0",          24,
		"10T1",          25,
		"100T",          26,
		"1000",          27 }
	NEG_STR = { 
		"0", 0,
		"T",             -1,
		"T1",            -2,
		"T0",            -3,
		"TT",            -4,
		"T11",           -5,
		"T10",           -6,
		"T1T",           -7,
		"T01",           -8,
		"T00",           -9,
		"T0T",           -10,
		"TT1",           -11,
		"TT0",           -12,
		"TTT",           -13,
		"T111",          -14,
		"T110",          -15,
		"T11T",          -16,
		"T101",          -17,
		"T100",          -18,
		"T10T",          -19,
		"T1T1",          -20,
		"T1T0",          -21,
		"T1TT",          -22,
		"T011",          -23,
		"T010",          -24,
		"T01T",          -25,
		"T001",          -26,
		"T000",          -27 }
	POS = (0..100).to_a.collect {|v| BTernary.new(v)}
	NEG = (0..100).to_a.collect {|v| BTernary.new(-v)}

	def test_constructor
		1.upto 27 do |count|
			t = BTernary.new count
			assert_equal count, t.to_i
		end
		
		POS_STR.each do |key, value|
			t = BTernary.new key
			assert_equal value, t.to_i
		end
		
		NEG_STR.each do |key, value|
			t = BTernary.new key
			assert_equal value, t.to_i
		end
	end
	
	def test_from_int
		t = BTernary.new 0
		0.upto 27 do |count|
			assert_equal count, BTernary.new(count).to_i
		end

		0.downto -27 do |count|
			assert_equal count, BTernary.new(count).to_i
		end
	end

	def test_add
		res = POS[1] + POS[1]
		assert_equal POS[2].to_i, res.to_i

		res = POS[5] + POS[5]
		assert_equal POS[10].to_i, res.to_i

		res = POS[7] + POS[7]
		assert_equal POS[14].to_i, res.to_i

		30.times {
			x = rand(50)
			y = rand(50)
			z = x+y

			res = POS[x] + POS[y]
			assert_equal POS[z].to_i, res.to_i
		}

		30.times {
			x = rand(50)
			y = rand(50)
			z = x + y

			res = NEG[x] + NEG[y]
			assert_equal NEG[z].to_i, res.to_i
		}
	end

	def test_sub
		30.times {
			x = rand(50)+50
			y = rand(50)
			z = x - y

			res = POS[x] - POS[y]
			assert_equal POS[z].to_i, res.to_i
		}

		30.times {
			x = rand(50)
			y = rand(100)
			z = x - y

			res = POS[x] - POS[y]
			if z < 0
				assert_equal NEG[z.abs].to_i, res.to_i
			else
				assert_equal POS[z].to_i, res.to_i
			end
		}
	end

	def test_negate
		assert_equal BTernary::ONE, -BTernary::NONE
	end

	def test_gle
		100.times {
			aa = rand(2) == 0 ? POS : NEG
			ba = rand(2) == 0 ? POS : NEG
			a = aa[rand(100)]
			b = ba[rand(100)]
			assert_equal a.to_i<=>b.to_i, a<=>b
		}
	end

	def test_range
		a = (BTernary::ONE ... BTernary.new(30)).to_a
		assert_equal 1, a[0].to_i
		assert_equal 29, a[-1].to_i
		assert_equal 29, a.size
	end
			
	def test_upto
		cnt = -5
		BTernary.new(-5).upto(BTernary.new(5)) do |count|
			assert_equal cnt, count.to_i
			assert cnt<6, "Whoa nelly!"
			cnt += 1
		end
		assert_equal cnt, 6
	end

	def test_times
		cnt = 0
		final = nil
		BTernary.new(5).times do |count|
			cnt += 1
			final = count
		end
		assert_equal 5, cnt
		assert_equal 4, final.to_i
	end

	def test_downto
		cnt = 5
		BTernary.new(5).downto(BTernary.new(-5)) do |count|
			assert_equal cnt, count.to_i
			assert cnt>-6, "Whoa nelly!"
			cnt -= 1
		end
		assert_equal -6, cnt
	end

	def test_abs
		assert_equal 98, BTernary.new(-98).abs.to_i
		assert_equal 98, BTernary.new(98).abs.to_i
	end

	def test_times
		30.times {
			asign = rand(2) == 1 ? -1 : 1
			bsign = rand(2) == 1 ? -1 : 1
			a = (rand(49)+1) * asign
			b = rand(100/a) * bsign
			c = a*b
			aa = asign == 1 ? POS : NEG
			ba = bsign == 1 ? POS : NEG
			ca = c > 0 ? POS : NEG

			assert_equal ca[c.abs], aa[a.abs] * ba[b.abs]
		}
	end

	def test_divide
		30.times {
			asign = rand(2) == 1 ? -1 : 1
			bsign = rand(2) == 1 ? -1 : 1
			a = rand(100) * asign
			b = rand(100) * bsign
			b += 1 if b == 0
			c = a/b
			aa = asign == 1 ? POS : NEG
			ba = bsign == 1 ? POS : NEG
			ca = c > 0 ? POS : NEG

			assert_equal ca[c.abs], aa[a.abs] / ba[b.abs]
		}
	end

	def test_and
		30.times {
			asign = rand(2) == 1 ? -1 : 1
			bsign = rand(2) == 1 ? -1 : 1
			a = rand(50) * asign
			b = rand(50) * bsign
			c = a&b
			aa = asign == 1 ? POS : NEG
			ba = bsign == 1 ? POS : NEG
			ca = c > 0 ? POS : NEG

			assert_equal ca[c.abs], aa[a.abs] & ba[b.abs]
		}
	end
	
	def test_or
		30.times {
			asign = rand(2) == 1 ? -1 : 1
			bsign = rand(2) == 1 ? -1 : 1
			a = rand(40) * asign
			b = rand(40) * bsign
			c = a|b
			aa = asign == 1 ? POS : NEG
			ba = bsign == 1 ? POS : NEG
			ca = c > 0 ? POS : NEG

			assert_equal ca[c.abs], aa[a.abs] | ba[b.abs]
		}
	end
end


RUNIT::CUI::TestRunner.run( Tester.suite )
