From: Boris Prinz Date: 2006-07-03T05:12:33+09:00 Subject: [Solution] [QUIZ] C-Style Ints (#85) Hi, here is my solution and some unit tests. (I didn't know that it's possible to write a block with an array argument, as in "define_method meth do |*other|". That's a nice feature.) Regards, Boris ### c_int.rb class UnsignedFixedWidthInt def initialize(value, bits) raise ArgumentError.new('number of bits must be > 0') if bits <= 0 @bits = bits @value = value % 2**@bits end # operators returning a new FixedWidthInt %w{& ^ | << >> + - * / +@ -@ ~@}.each do |operator| define_method operator do |*other| self.class.new(@value.send(operator, *other), @bits) end end # methods forwarded to @value %w{== < > <=> inspect to_s to_i to_int}.each do |meth| define_method meth do |*other| @value.send(meth, *other) end end def coerce(other) Integer == other ? [other, @value] : [Float(other), Float(@value)] end end class SignedFixedWidthInt < UnsignedFixedWidthInt def initialize(value, bits) super(value, bits) # value is negative if MSB is set: @value -= 2**@bits if @value[@bits - 1] == 1 end end ### c_int_test.rb require 'c_int' require 'test/unit' class CIntTest < Test::Unit::TestCase def test_no_bits assert_raises(ArgumentError) { UnsignedFixedWidthInt.new(0, 0) } assert_raises(ArgumentError) { UnsignedFixedWidthInt.new(0, -1) } end def test_one_bit n = UnsignedFixedWidthInt.new(0xFF, 1) assert_equal 1, n n += 1 assert_equal 0, n n = SignedFixedWidthInt.new(0xFF, 1) assert_equal(-1, n) n += 1 assert_equal 0, n n += 1 assert_equal(-1, n) end def test_unsigned n = UnsignedFixedWidthInt.new(0xFF, 8) assert_equal 255, n n += 2 assert_equal 1, n n = n << 1 assert_equal 2, n n = n >> 1 assert_equal 1, n assert_equal 254, (~n) n += 12 assert_equal 13, n n = n & 0x0E assert_equal 12, n n = n * 24 assert_equal 32, n n = n / 15 assert_equal 2, n end def test_signed n = SignedFixedWidthInt.new(0x01, 8) assert_equal 1, n n = n << 7 assert_equal(-128, n) n -= 1 assert_equal 127, n n = n >> 6 assert_equal 1, n n -= 2 assert_equal(-1, n) n = n ^ 0xF3 assert_equal 12, n n = n | 0x01 assert_equal 13, n n = +n assert_equal 13, n n = -n assert_equal(-13, n) end def test_too_wide n = UnsignedFixedWidthInt.new(0x0, 8) n += 0xFFEE assert_equal 238, n end def test_signed_shift_right n = SignedFixedWidthInt.new(0x80, 8) n = n >> 1 assert_equal(-64, n) # with sign extension end def test_equal s1 = SignedFixedWidthInt.new(-1, 4) s2 = SignedFixedWidthInt.new(-1, 4) s3 = SignedFixedWidthInt.new(1, 4) u1 = UnsignedFixedWidthInt.new(1, 4) u2 = UnsignedFixedWidthInt.new(1, 4) assert u1 != s1 assert s1 != s3 assert u1 == u2 assert s1 == s2 assert s3 == u1 assert(-1 == s1) assert s1 == -1 end def test_conversions n = SignedFixedWidthInt.new(-100, 7) assert "-100", n.to_s assert "-100", n.inspect assert(-100, n.to_i) assert(-100, n.to_int) end def test_coerce n = UnsignedFixedWidthInt.new(1, 4) assert 1.1 > n assert 0.9 < n assert 1 == n end def test_comparison s = SignedFixedWidthInt.new(-1, 4) u = UnsignedFixedWidthInt.new(1, 4) assert u > s assert s < u assert s < 0 assert u > 0 assert 0 > s assert 0 < u assert_equal(-1, s <=> u) assert_equal 1, u <=> s assert_equal 0, 1 <=> u assert_equal 0, -1 <=> s end end