[QUIZ] C-Style Ints (#85) - not a spoiler, a test harness

Given the number of edge conditions for this quiz it seemed like a good
place for a test harness. Here is the irb session in the quiz translated to
a test harness. It is my first use of the test framework so hopefully I
haven't made any horrible errors. Just put this in the file quiz85.rb and
then use "ruby quiz85.rb" to test for correctness (it works better from the
command line than from irb - I don't know why but someone here does). Have
fun!

# test harness for ruby quiz 85
# see it at:
# Ruby Quiz - C-Style Ints (#85)

require 'test/unit'
require 'UnsignedFixedWidthInt.rb'
require 'SignedFixedWidthInt.rb'

class TestUnsignedFixedWidthInt < Test::Unit::TestCase
  def test_quiz_example_unsigned
    n = UnsignedFixedWidthInt.new(0xFF, 8)
    assert_equal( n, 255 )
    n += 2
    assert_equal( n, 1 )
    n = n << 1
    assert_equal( n, 2 )
    n = n >> 1
    assert_equal( n, 1 )
    assert_equal( ~n, 254 )
    n += 12
    assert_equal( n, 13 )
    n = n & 0x0E
    assert_equal( n, 12 )
  end
  def test_quiz_example_too_wide
    n = UnsignedFixedWidthInt.new(0x0, 8)
    assert_equal( n, 0 )
    n += 0xFFEE
    assert_equal( n, 238 )
  end
end

class TestUnsignedFixedWidthInt < Test::Unit::TestCase
  def test_quiz_example_signed
    n = SignedFixedWidthInt.new(0x01, 8)
    assert_equal( n, 1 )
    n = n << 7
    assert_equal( n, -128 )
    n -= 1
    assert_equal( n, 127 )
    n = n >> 6
    assert_equal( n, 1 )
    n -= 2
    assert_equal( n, -1 )
    n = n ^ 0xF3
    assert_equal( n, 12 )
    n = n | 0x01
    assert_equal( n, 13 )
  end
end

···

On 6/30/06, Ruby Quiz <james@grayproductions.net> wrote:

The three rules of Ruby Quiz:

1. Please do not post any solutions or spoiler discussion for this quiz
until
48 hours have passed from the time on this message.

2. Support Ruby Quiz by submitting ideas as often as you can:

http://www.rubyquiz.com/

3. Enjoy!

Suggestion: A [QUIZ] in the subject of emails about the problem helps
everyone
on Ruby Talk follow the discussion. Please reply to the original quiz
message,
if you can.

-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=

by Aaron Patterson

Write a class that can represent a signed or unsigned number with an
arbitrary
number of bits. This class should support all bitwise operations ( & ^ ~
> ),
basic math operations ( + - / * ), and comparison operators. It would
behave
like an integer in C (except with arbitrary length!), so an unsigned int
0xFFFFFFFF + 1 would equal 0x00000000.

One edge case is what to do in an overflow case ( see the first irb
session
number 2 ). Another is how to handle numbers that are wider than the
specified
number of bits. I'm not really sure how to handle that part, but what I
do is
just take the last N number of bits. So if 0xFFEE was passed in to my 8
bit
vector, I would just take 0xEE.

        Example irb sessions

Here is an example of using an 8 bit unsigned int with an initial value of
0xFF:

        irb(main):001:0> n = UnsignedFixedWidthInt.new(0xFF, 8)
        => 255
        irb(main):002:0> n += 2
        => 1
        irb(main):003:0> n = n << 1
        => 2
        irb(main):004:0> n = n >> 1
        => 1
        irb(main):005:0> ~n
        => 254
        irb(main):006:0> n += 12
        => 13
        irb(main):007:0> n = n & 0x0E
        => 12
        irb(main):008:0>

Now an example of an 8 bit signed int with an initial value of 0x01:

        irb(main):001:0> n = SignedFixedWidthInt.new(0x01, 8)
        => 1
        irb(main):002:0> n = n << 7
        => -128
        irb(main):003:0> n -= 1
        => 127
        irb(main):004:0> n = n >> 6
        => 1
        irb(main):005:0> n -= 2
        => -1
        irb(main):006:0> n = n ^ 0xF3
        => 12
        irb(main):007:0> n = n | 0x01
        => 13
        irb(main):008:0>

Here is an example of handling numbers that are too wide:

        irb(main):001:0> n = UnsignedFixedWidthInt.new(0x0, 8)
        => 0
        irb(main):002:0> n += 0xFFEE
        => 238
        irb(main):003:0>

Good idea, here are the tests i'm using. It also includes the irb
session as well as a number of other tests.
It assumes the result of any calculation with FWI's and integers is a FWI.
I'm using a different syntax for the FWI's, but it should be easy
enough to convert this to whatever you're using.

Gmail will probably mess up my indent again, so here's a rafb link as well:
http://rafb.net/paste/results/rwO1Kf64.html

class FWITest < Test::Unit::TestCase
  def test_unsigned_8
    uint8 = UnsignedFWI(8)
  n=uint8.new(0xFF)
  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 -= 13
  assert_equal(255 , n)
  end

  def test_signed_8
    int8 = SignedFWI(8)
  n = int8.new(1)
  assert_equal(n,1)
  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)
  end

  def test_too_wide
    uint8 = UnsignedFWI(8)
    assert_equal(238, uint8.new(0) + 0xFFEE)
  end

  def test_short_length_unsigned
    uint3 = UnsignedFWI(3)
  uint10 = UnsignedFWI(10)
  n = uint3.new(8)
  assert_equal(0, n)
  n -= 7
  assert_equal(1, n)
  assert_equal(0, n**23 + 7)
  m = uint3.new(8)
  assert_equal(1, n + m)
    assert_equal(1, m + n)
    assert_equal(1, 8 + n)
  l = uint10.new(11)
    assert_equal(1100-1024, l*100)
    assert_equal(1100-1024, 100*l)
  end

  def test_short_length_signed
    int3 = SignedFWI(3)
  n = int3.new(4)
  assert_equal(-4, n)
    assert_equal(3 , (+n)-1)
    assert_equal(n , 8+n)
    assert_equal(-n, n)
  n -= 1
  assert_equal(3 , n)
  end

  def test_long_length_signed
    int76 = SignedFWI(76)
  n = int76.new( 1<<75 )
  assert_equal(-(1<<75) , n)
  n -= 1
    assert_equal((1<<75)-1 , n)
  n = int76.new(2)
  n *= (1<<75)-1
  assert_equal(-2 , n)
  end

  def test_power
    uint3 = UnsignedFWI(3)
    assert_equal(2, 2 ** uint3.new(1))
  assert_equal(0, 2 ** uint3.new(3))
  assert_equal(0, 2 ** uint3.new(67))
  assert_equal(1, 3 ** uint3.new(2))
  assert_equal(3, uint3.new(3) ** uint3.new(-1)) # 3**7 mod 8
  end
end

···

On 7/1/06, John Baylor <john.baylor@gmail.com> wrote:

Given the number of edge conditions for this quiz it seemed like a good
place for a test harness. Here is the irb session in the quiz translated to
a test harness. It is my first use of the test framework so hopefully I
haven't made any horrible errors.

"Sander Land" <sander.land@gmail.com> writes:

Good idea, here are the tests i'm using. It also includes the irb
session as well as a number of other tests.
It assumes the result of any calculation with FWI's and integers is a FWI.
I'm using a different syntax for the FWI's, but it should be easy
enough to convert this to whatever you're using.

I like this syntax though better than the one in the quiz and may
adopt it. Actually, it shouldn't be too hard to support both
syntaxes...

I do take issue with one of your tests, though:

def test_power
   uint3 = UnsignedFWI(3)
   assert_equal(2, 2 ** uint3.new(1))
   assert_equal(0, 2 ** uint3.new(3))
   assert_equal(0, 2 ** uint3.new(67))
   assert_equal(1, 3 ** uint3.new(2))
   assert_equal(3, uint3.new(3) ** uint3.new(-1)) # 3**7 mod 8
end

That just feels wrong. (except for the last one) I would write:

def test_power
  uint3 = UnsignedFWI(3)
  assert_equal(2, uint3.new(2) ** 1)
  assert_equal(0, uint3.new(2) ** 3)
  assert_equal(0, uint3.new(2) ** 67)
  assert_equal(1, uint3.new(3) ** 2)
  assert_equal(3, uint3.new(3) ** uint3.new(-1)) # 3**7 mod 8
end

And maybe:

def test_power_fwi_exponent
  uint3 = UnsignedFWI(3)
  assert_equal(2, 2 ** uint3.new(1))
  assert_equal(8, 2 ** uint3.new(3))
  assert_equal(8, 2 ** uint3.new(67))
end

I really think that you want to keep exponentiation looking like
repeated multiplication of the first argument.

Of course, it would be nice if our kind quiz host would respond with a
few rulings for the edge cases I outlined before. At least then we'd
know when we were ignoring the rules of the quiz.

Daniel Martin <martind@martinhouse.internal> writes:

Of course, it would be nice if our kind quiz host would respond with a
few rulings for the edge cases I outlined before. At least then we'd
know when we were ignoring the rules of the quiz.

And I see that he already did, so once again see that I should learn
to read my entire mailbox before responding to anything.

I do take issue with one of your tests, though:
...
That just feels wrong. (except for the last one) I would write:
def test_power
...
end

Agreed, the test put way too much focus on fixnum ** fwi.
This was because my implementation first assumed any binary operation
was commutative (it just switched both operands if the first was a
fixnum), so 2 ** uint3.new(1) was uint3.new(1)**2=1, and the test was
there to help me fix this.

And maybe:
  assert_equal(8, 2 ** uint3.new(67))
...
I really think that you want to keep exponentiation looking like
repeated multiplication of the first argument.

And i really think the result should be a fwi, for consistency with
multiplication ('repeated addition').
Anyway, in my implementation 2 ** uint3.new(67).to_i == 8, so there's
still a way to get those calculations done as well.

···

On 7/2/06, Daniel Martin <martin@snowplow.org> wrote:

I do take issue with one of your tests, though:

def test_power
   uint3 = UnsignedFWI(3)
   assert_equal(2, 2 ** uint3.new(1))
   assert_equal(0, 2 ** uint3.new(3))
   assert_equal(0, 2 ** uint3.new(67))
   assert_equal(1, 3 ** uint3.new(2))
   assert_equal(3, uint3.new(3) ** uint3.new(-1)) # 3**7 mod 8
end

That just feels wrong. (except for the last one) I would write:

def test_power
  uint3 = UnsignedFWI(3)
  assert_equal(2, uint3.new(2) ** 1)
  assert_equal(0, uint3.new(2) ** 3)
  assert_equal(0, uint3.new(2) ** 67)
  assert_equal(1, uint3.new(3) ** 2)
  assert_equal(3, uint3.new(3) ** uint3.new(-1)) # 3**7 mod 8
end

And maybe:

def test_power_fwi_exponent
  uint3 = UnsignedFWI(3)
  assert_equal(2, 2 ** uint3.new(1))
  assert_equal(8, 2 ** uint3.new(3))
  assert_equal(8, 2 ** uint3.new(67))
end

I really think that you want to keep exponentiation looking like
repeated multiplication of the first argument.

I think that's a good point.

Of course, it would be nice if our kind quiz host would respond with a
few rulings for the edge cases I outlined before. At least then we'd
know when we were ignoring the rules of the quiz.

The main rule: Do what you feel is right. If you explain why you make the choices you do and convince me you are right, I'll talk about how clever you are in the summary. :wink:

Sorry I wasn't around this weekend. I felt it better to be hard to reach for a couple of days than to cancel this week's quiz altogether.

James Edward Gray II

···

On Jul 1, 2006, at 7:39 PM, Daniel Martin wrote: