=begin $Id: unittest_bitvector.rb,v 1.5 2003/01/23 17:42:40 knu Exp $ Unit test for XX. Part of Yoda (Yet another jOb Distributing And queueing system). (c) Copyright 2000, Robert Feldt, feldt@ce.chalmers.se This software is distributed under a license. See LICENSE in the Yoda main directory for more information. =end require 'test/unit' require 'test/unit/ui/console/testrunner' require 'bitvector' class Test_BitVector < Test::Unit::TestCase def setup @be = BitVector.new(4) # 0000 @bf = BitVector.new(4) # 1111 @bf.flip @b1 = BitVector.new(4) # 1100 @b1.set(2,1) @b1.set(3,1) @b2 = BitVector.new(4) # 1010 @b2.set(1,1) @b2.set(3,1) @bl = BitVector.new(7) # 1001100 @bl.set(6,1) @bl.set(3,1) @bl.set(2,1) end def assert_is_bitvector(obj) assert_equal(BitVector, obj.class) end def test_01_initialize_and_length assert_is_bitvector(@b1) assert_equal(4, @b1.length) assert_equal(4, @b2.length) assert_equal(7, @bl.length) assert_equal(98765, BitVector.new(98765).length) end def test_02_clone_and_equal b1c = @b1.clone assert_is_bitvector(b1c) assert(@b1.id != b1c.id) assert(@b1.equal?(b1c)) assert(@b1 == b1c) assert(!@b2.equal?(b1c)) assert(@b2 != b1c) end def test_03_concat_and_inspect bc = @b1.concat(@b2) assert_is_bitvector(bc) assert_equal(@b1.length+@b2.length, bc.length) assert_equal(@b1.inspect+@b2.inspect, bc.inspect) assert((bc.id != @b1.id) && (bc.id != @b2.id)) assert_raises(TypeError) {@b1.concat([])} end def test_04_reverse assert_equal(@be, @be.reverse) assert_equal(@bf, @bf.reverse) assert_equal(@b1, @b1.reverse.reverse) assert_equal(@b2, @b2.reverse.reverse) assert_equal("0011", @b1.reverse.inspect) assert_equal("0101", @b2.reverse.inspect) assert_equal("0011001", @bl.reverse.inspect) end def test_05_empty assert(@be.empty?) assert(!(@bf.empty?)) assert((@bf.empty).empty?) # Interval emptying b = BitVector.new(10) b.fill b.empty(7) assert_equal("1101111111", b.inspect) b.empty(4,0) assert_equal("1101111111", b.inspect) b.empty(4,1) assert_equal("1101101111", b.inspect) b.empty(0...2) assert_equal("1101101100", b.inspect) b.empty(-7..-3) assert_equal("1100000100", b.inspect) assert_raises(RangeError) {b.empty(-18..-5)} end def test_06_fill_and_full assert(!(@be.full?)) assert(@bf.full?) assert((@be.fill).full?) # Interval fills b = BitVector.new(10) b.fill(5) assert_equal("0000100000", b.inspect) b.fill(4,0) assert_equal("0000100000", b.inspect) b.fill(4,2) assert_equal("0000110000", b.inspect) b.fill(1..2) assert_equal("0000110110", b.inspect) b.fill(-7..-3) assert_equal("0011111110", b.inspect) assert_raises(RangeError) {b.fill(-18..-5)} end def test_07_primes # Test primes < 20 assert_equal("10100010100010101100", BitVector.new(20).primes.inspect) end def test_08_equal assert(@b1.equal?(@b1)) assert(@b1 == @b1) assert(!@b2.equal?(@b1)) assert(@b2 != @b1) assert(@bl != @b1) assert(@b1 != @bl) end def test_09_compare assert_equal(1, @b1.compare(@b2)) # -4 > -6 assert_equal(-1, @b2.compare(@b1)) # -6 < -4 assert_equal(0, @b1 <=> @b1) assert_equal(1, @b1 <=> @b2) assert_equal(-1, @b2 <=> @b1) assert_equal(1, @be <=> @bf) # 0 > -1 end def test_10_lexicompare assert_equal(1, @b1.lexicompare(@b2)) # 12 > 10 assert_equal(-1, @b2.lexicompare(@b1)) # 10 < 12 assert_equal(-1, @be.lexicompare(@bf)) # 0 < 15 end def test_11_to_bin_str assert_equal("1100", @b1.to_bin_str) assert_equal("C", @b1.to_hex_str) assert_equal("-4", @b1.to_dec_str) assert_equal("2,3", @b1.to_enum_str) assert_equal("1001100", @bl.to_bin_str) assert_equal("4C", @bl.to_hex_str) assert_equal("2,3,6", @bl.to_enum_str) end def test_12_on_and_off assert_equal("1101", @b1.on(0).inspect) assert_equal("1111", @b1.on(1).inspect) assert_equal("1011", @b1.off(2).inspect) assert_equal("0011", @b1.off(3).inspect) assert_equal("1011", @b1.on(3).inspect) assert_equal("1111", @b1.on(2).inspect) assert_equal("1101", @b1.off(1).inspect) assert_equal("1100", @b1.off(0).inspect) end def test_13_bit_flip assert_equal(1, @b1.bit_flip(0)) assert_equal("1101", @b1.inspect) assert_equal(0, @b1.bit_flip(2)) assert_equal("1001", @b1.inspect) assert_equal(0, @b1.bit_flip(12)) end def test_14_bit_and_test assert_equal(1, @bl.bit(6)) assert_equal(0, @bl.bit(0)) assert_equal(true, @bl.test?(6)) assert_equal(false, @bl.test?(0)) end def test_15_set @b1.set(0,1) assert_equal("1101", @b1.inspect) @b2.set(3,0) assert_equal("0010", @b2.inspect) @bl.set(0,1) assert_equal("1001101", @bl.inspect) @bl.set(6,0) assert_equal("0001101", @bl.inspect) assert_raises(IndexError) {@bl.set(7,0)} assert_raises(IndexError) {@bl.set(-1,0)} end def test_16_union u1 = @b1.union(@b2) assert_is_bitvector(u1) assert_equal("1110", u1.inspect) u2 = @b1|@b2 assert_is_bitvector(u2) assert_equal("1110", u2.inspect) assert_raises(ArgumentError) {@b1|@bl} end def test_17_intersection i1 = @b1.intersection(@b2) assert_is_bitvector(i1) assert_equal("1000", i1.inspect) i2 = @b1&@b2 assert_is_bitvector(i2) assert_equal("1000", i2.inspect) assert_raises(ArgumentError) {@b1&@bl} end def test_18_difference d1 = @b1.difference(@b2) assert_is_bitvector(d1) assert_equal("0100", d1.inspect) assert_raises(ArgumentError) {@b1.difference(@bl)} d1 = @b2.difference(@b1) assert_is_bitvector(d1) assert_equal("0010", d1.inspect) assert_raises(ArgumentError) {@b2.difference(@bl)} end def test_19_exclusive_or e1 = @b1.exclusive_or(@b2) assert_is_bitvector(e1) assert_equal("0110", e1.inspect) e2 = @b1^@b2 assert_is_bitvector(e2) assert_equal("0110", e2.inspect) assert_raises(ArgumentError) {@b1^@bl} end def test_20_complement assert_equal("0011", @b1.complement.inspect) assert_equal("0110011", @bl.complement.inspect) assert_equal("0110011", (~@bl).inspect) end def test_21_subset_and_superset @b2.set(2,1) # 1110 assert(@b1.subset?(@b2)) assert(!@b2.subset?(@b1)) assert(!@b1.superset?(@b2)) assert(@b2.superset?(@b1)) end def test_22_norm assert_equal(2, @b1.norm) assert_equal(2, @b2.norm) assert_equal(3, @bl.norm) assert_equal(4, @bl.complement.norm) end def test_23_min_and_max assert_equal(2, @b1.min) assert_equal(3, @b1.max) assert_equal(1, @b2.min) assert_equal(3, @b2.max) assert_equal(2, @bl.min) assert_equal(6, @bl.max) end def test_24_aset @bl[6] = 0 assert_equal("0001100", @bl.inspect) @bl[2] = false assert_equal("0001000", @bl.inspect) @bl[0] = 1 assert_equal("0001001", @bl.inspect) @bl[4] = true assert_equal("0011001", @bl.inspect) @bl[5..6] = true assert_equal("1111001", @bl.inspect) @bl[4...6] = 0 assert_equal("1001001", @bl.inspect) @bl[-5...-1] = 1 assert_equal("1111101", @bl.inspect) assert_raises(RangeError) {@bl[-8..-5] = false} @bl[0,3] = false assert_equal("1111000", @bl.inspect) @bl[4,1] = 0 assert_equal("1101000", @bl.inspect) @bl[5,0] = 0 assert_equal("1101000", @bl.inspect) @bl[0,7] = true assert_equal("1111111", @bl.inspect) assert_raises(IndexError) {@bl[10**300] = false} end def test_25_new_string b = BitVector.from_bin(7, "1001100") assert_equal(7, b.length) assert_equal("1001100", b.inspect) b = BitVector.from_dec(7, "76") assert_equal(7, b.length) assert_equal("1001100", b.inspect) b = BitVector.from_hex(7, "4C") assert_equal(7, b.length) assert_equal("1001100", b.inspect) b = BitVector.from_enum(7, "2,3,6") assert_equal(7, b.length) assert_equal("1001100", b.inspect) b = BitVector.from_enum(10, "6,2-3") assert_equal(10, b.length) assert_equal("0001001100", b.inspect) b = BitVector.from_bin(2, "10101") assert_equal(2, b.length) assert_equal("01", b.inspect) end def test_26_new_fixnum b = BitVector.new(nil, 76) assert_equal(31, b.length) assert_equal(("0" * (31-7)) + "1001100", b.inspect) b = BitVector.new(false, 76) assert_equal(31, b.length) assert_equal(("0" * (31-7)) + "1001100", b.inspect) b = BitVector.new(true, 76) assert_equal(32, b.length) assert_equal(("0" * (32-7)) + "1001100", b.inspect) end def test_27_new_bignum b = BitVector.new(10, (256**20-1)) assert_equal(10, b.length) assert_equal("1"*10, b.inspect) b = BitVector.new(nil, (256**20-1)) assert_equal(8*20, b.length) assert_equal("1"*160, b.inspect) end def test_28_new_int b = BitVector.new(7, 76) assert_equal(7, b.length) assert_equal("1001100", b.inspect) b = BitVector.new(nil, 76) assert_equal(31, b.length) assert_equal(("0" * (31-7)) + "1001100", b.inspect) b = BitVector.new(false, 76) assert_equal(31, b.length) assert_equal(("0" * (31-7)) + "1001100", b.inspect) b = BitVector.new(true, 76) assert_equal(32, b.length) assert_equal(("0" * (32-7)) + "1001100", b.inspect) end def test_29_msb_and_lsb assert_equal(0, @b1.lsb) @b1.lsb = 1 assert_equal(1, @b1.lsb) assert_equal(1, @b1.msb) @b1.msb = 0 assert_equal(0, @b1.msb) end def test_30_rotate_left_and_right assert_equal(1, @b1.rotate_left) assert_equal("1001", @b1.inspect) assert_equal(0, @b2.rotate_right) assert_equal("0101", @b2.inspect) end def test_31_shift_left_and_right assert_equal(1, @b1.shift_left(1)) assert_equal("1001", @b1.inspect) assert_equal(0, @b2.shift_right(0)) assert_equal("0101", @b2.inspect) end def test_32_move_left_and_right @b1 << 1 assert_equal("1000", @b1.inspect) @b1 << 1 assert_equal("0000", @b1.inspect) @b2 >> 1 assert_equal("0101", @b2.inspect) @b2 >> 10 assert_equal("0000", @b2.inspect) end def test_33_succ_and_pred assert_equal("1101", @b1.succ.inspect) assert_equal("1001", @b2.pred.inspect) end def test_34_marshaling d1 = Marshal.dump(@b1) b1c = Marshal.load(d1) assert_is_bitvector(b1c) assert(b1c == @b1) assert_equal(4, b1c.length) assert_equal("1100", b1c.inspect) b = BitVector.new(32*100+1) d = Marshal.dump(b) bc = Marshal.load(d) assert_equal(3201, bc.length) assert(("0"*3201) == bc.inspect) end def test_35_new_clone b = BitVector.new(@b1) assert(b == @b1) assert_equal(4, b.length) assert_equal("1100", b.inspect) end def test_36_substitute @b1.substitute(@b2, 1, 2, 1, 3) assert_equal(5, @b1.length) assert_equal("11010", @b1.inspect) @bl.substitute(@b1, 7, 0, 0, 5) assert_equal(12, @bl.length, @bl.inspect) assert_equal("110101001100", @bl.inspect) assert_raises(IndexError) {@b1.substitute(@b2, -1, 0, 0, 0)} assert_raises(IndexError) {@b1.substitute(@b2, 0, 0, -1, 0)} end def test_37_to_i assert_equal(12-16, @b1.to_i) assert_equal(10-16, @b2.to_i) assert_equal(76-128, @bl.to_i) assert_equal(12, BitVector.from_bin(5, "01100").to_i) assert_equal(10, BitVector.from_bin(5, "01010").to_i) assert_equal(76, BitVector.from_bin(8, "01001100").to_i) assert_equal(2**160-1, BitVector.from_bin(161, "0" + ("1"*160)).to_i) end def test_38_ones_and_zeroes assert_equal([2,3], @b1.ones) assert_equal([1,3], @b2.ones) assert_equal([2,3,6], @bl.ones) assert_equal([0,1], @b1.zeroes) assert_equal([0,2], @b2.zeroes) assert_equal([0,1,4,5], @bl.zeroes) b = BitVector.new(10000).randomize assert_equal(b.norm, b.ones.length) assert_equal(10000-b.norm, b.zeroes.length) end def test_39_randomize b = BitVector.new(10) b.randomize b.empty b.randomize(0...5, 1.0) assert_equal("0000011111", b.inspect) b.randomize(0, 2, 1.0) assert_equal("0000011100", b.inspect) b.randomize(9, 1.0) assert_equal("1000011100", b.inspect) b.randomize(-2...-1, 1.0) assert_equal("1100011100", b.inspect) assert_raises(RangeError) {b.randomize(-18..-5)} assert_raises(IndexError) {b.randomize(-1)} assert_raises(IndexError) {b.randomize(b.length)} # Randomize a large number of times and make sure no bit gets unfair # treatment... counts = Hash.new(10) b.empty 10000.times { prev = b.clone b.randomize(0.5) (b ^ prev).ones.each {|differing_bit| counts[differing_bit]+=1} } # Very unlikely that number of alterations of a bit in 10000 randomizations # is outside [4820,5180] (binomial distribution). NOTE: It can happen # though (about once in 10000 test runs...) counts.each {|bit, count| assert(count.between?(4820,5180), "Bit #{bit} altered #{count} times. THIS IS UNLIKELY BUT MIGHT HAPPEN WITHOUT THERE BEING AN ERROR!") } end def test_40_aref assert_equal(0, @bl[0]) assert_equal("1001100", @bl.inspect) assert_equal(1, @bl[6]) assert_equal("1001100", @bl.inspect) b = @bl[0,7] assert_equal(@bl.inspect, b.inspect) assert_equal("1001100", @bl.inspect) b = @bl[4..6] assert_is_bitvector(b) assert_equal("100", b.inspect) assert_equal("1001100", @bl.inspect) b = @bl[4...6] assert_equal("00", b.inspect) assert_equal("1001100", @bl.inspect) b = @bl[-5...-3] assert_equal("11", b.inspect) b = @bl[0,3] assert_equal("100", b.inspect) b = @bl[4,1] assert_equal(Fixnum, b.class) assert_equal(0, b) b = @bl[6,1] assert_equal(Fixnum, b.class) assert_equal(1, b) b = @bl[6,0] assert_equal(nil, b) assert_raises(IndexError) {@bl[-1]} assert_raises(IndexError) {@bl[@bl.length]} assert_raises(IndexError) {@bl[10**300]} end def test_41_resize assert_equal(4, @b1.length) assert_equal(19, @b1.resize(19).length) assert_equal(2, @b1.resize(2).length) assert_equal(0, @b1.resize(0).length) assert_equal(7, @bl.length) assert_equal(49, @bl.resize(49).length) assert_equal(2, @bl.resize(2).length) assert_equal(0, @bl.resize(0).length) assert_raises(ArgumentError) {@bl.resize(-1)} assert_raises(ArgumentError) {@bl.resize(10**300)} end def test_42_from_int b = BitVector.from_int(86,8) assert_is_bitvector(b) assert_equal(8, b.length) assert_equal("01010110", b.inspect) b = BitVector.from_int(86) assert_is_bitvector(b) assert_equal(8, b.length) # 7 bits needed but extra to mark as positive assert_equal("01010110", b.inspect) b = BitVector.from_int(-4) assert_equal(3, b.length) # One extra bit since negative assert_equal("100", b.inspect); b = BitVector.from_int(-(2**32)) assert_equal(33, b.length) # One extra bit since negative end def test_43_add b1 = BitVector.from_bin(8, "01100101") # 1+4+32+64 = 101 b2 = BitVector.from_bin(8, "01010110") # 2+4+16+64 = 86 s = b1+b2 # 187 assert_is_bitvector(s) assert_equal(b1.length, s.length) assert_equal(b1.to_uint+b2.to_uint, s.to_uint) assert_equal(187, s.to_uint) assert_equal(0, BitVector.carry) b1 = BitVector.from_bin(8, "11100101") # 1+4+32+64+128 = 229 b2 = BitVector.from_bin(8, "01010110") # 2+4+16+64 = 86 s = b1+b2 # 229+86 = 315 but we get a carry # and 59 (315-256) assert_equal(59, s.to_i) assert_equal(1, BitVector.carry) b1 = BitVector.from_bin(8, "11100101") # 1+4+32+64+128 = 229 b2 = 86 # 86 s = b1+b2 # 229+86 = 315 but we get a carry # and 59 (315-256) assert_equal(59, s.to_i) assert_equal(1, BitVector.carry) assert_equal(226, (BitVector.from_bin(8,"11100101")+(-3)).to_uint) end def test_43_sub b1 = BitVector.from_bin(8, "01100101") # 1+4+32+64 = 101 b2 = BitVector.from_bin(8, "01010110") # 2+4+16+64 = 86 s = b1-b2 # 15 assert_is_bitvector(s) assert_equal(b1.length, s.length) assert_equal(b1.to_uint-b2.to_uint, s.to_uint) assert_equal(15, s.to_uint) assert_equal(0, BitVector.carry) b1 = BitVector.from_bin(8, "11100101") # 1+4+32+64+128 = 229 b2 = 86 # 86 s = b1-b2 assert_equal(143, s.to_uint) assert_equal(0, BitVector.carry) assert_equal(232, (BitVector.from_bin(8,"11100101")-(-3)).to_uint) end def test_44_negate assert_equal(-27, (-BitVector.from_int(27,8)).to_i) assert_equal(27, (-(-BitVector.from_int(27,8))).to_i) end def test_45_to_uint assert_equal(12, @b1.to_uint) assert_equal(10, @b2.to_uint) assert_equal(76, @bl.to_uint) assert_equal(2**33-1, BitVector.from_bin(33, "1"*33).to_uint) assert_equal(2**160-1, BitVector.from_bin(160, "1"*160).to_uint) assert_equal(2**160-1, BitVector.from_bin(161, "0" + ("1"*160)).to_uint) assert_equal(2*(2**160-1), BitVector.from_bin(162, "0" + ("1"*160) + "0").to_uint) end def test_46_abs assert_equal(27, (-BitVector.from_int(27,8)).abs.to_i) end def test_47_sign assert_equal(-1, (BitVector.from_int(-1, 8)).sign) assert_equal(0, (BitVector.from_int(0, 8)).sign) assert_equal(1, (BitVector.from_int(1, 8)).sign) assert_equal(-1, (BitVector.from_int(-100, 8)).sign) assert_equal(1, (BitVector.from_int(100, 8)).sign) assert_equal(-1, @b1.sign) assert_equal(-1, @b2.sign) assert_equal(-1, @bl.sign) assert_equal(1, (-@b1).sign) assert_equal(1, (-@b2).sign) assert_equal(1, (-@bl).sign) end def test_48_multiply assert_equal(24, (p = @b1*@b2).to_i) # -4*-6 assert_equal(8, p.length) assert_equal(120, (p = BitVector.from_int(12,8)*BitVector.from_int(10,8)).to_i) assert_equal(16, p.length) end def test_49_divide assert_equal(1, (q = @b2/@b1).to_i) # -6/-4 assert_equal(4, q.length) assert_equal(12, (p = BitVector.from_int(120,8)/BitVector.from_int(10,8)).to_i) assert_equal(8, p.length) end def test_50_strange_gc_bug # This bug showed up at some time during development. Unclear what causes/ # caused it. a = BitVector.new(10000).randomize a = nil GC.start a = BitVector.new(10000).randomize # => [BUG] segmentation fault end def test_51_aset_bitvector @be[1..2] = @bf[-4..-3] # Note that it is faster to use substitute... assert_equal("0110", @be.inspect) @bl[0,4] = @b2 assert_equal("1001010", @bl.inspect) assert_raises(RangeError) {@bl[0,1] = @be[3..4]} end end