[QUIZ] C-Style Ints (#85)

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>

Hi!

···

At Fri, 30 Jun 2006 22:47:46 +0900, Ruby Quiz wrote:

Write a class that can represent a signed or unsigned number with an
arbitrary number of bits.
:
  irb(main):001:0> n = UnsignedFixedWidthInt.new(0xFF, 8)
:
  irb(main):001:0> n = SignedFixedWidthInt.new(0x01, 8)

Does "write a class" imply that UnsignedFixedWidthInt and
SignedFixedWidthInt are two different names for the just one class?

Josef 'Jupp' Schugt

I have some questions about some more edge cases.

Ruby Quiz <james@grayproductions.net> writes:

  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>

It would seem from the above that whenever there's arithmetic with a
FWI as one operand and an Integer as the other, the result should be a
FWI and we should do simple twos-complement arithmetic in that width.

Now, should we also try to ensure that (2 + n) has the same result as
(n + 2) ?

What happens when we operate on two FWIs at once? I would propose
that the answer should be that the result is a FWI with as many bits
as the larger of the two operands, but I'm stuck as to what to do when
you add a SignedFixedWidthInt to an UnsignedFixedWidthInt. What is
the result?

I'd like to propose these rules of arithmetic:

Any operation between a FWI and a Float, Rational, or Complex has the
same result as though the FWI were replaced by an Integer of equal
value.

For operations +, -, ^, &&, *, /, %, and ||, an Integer with a FWI (in
either order) produces the same type of FWI as the FWI argument. For
those same operations, two FWIs produce an FWI with as many bits as
the longer of the two and unsigned if either operand is unsigned.
(rationale: like C)

An alternative set of rules (I'm asking for a ruling here) sets the
result of * and / to the same type as the first argument, and % to the
same type as the second argument. (Rationale: a * 2 is (a + a),
whether a is a Fixnum, Float, Array, or String. This seems like a
property we might want to preserve when a is a FWI, even if "2" is
only a FWI with the value of "2")

For ** (exponentiation), I'm not sure. My instinct is to say that
it's an error to raise a FWI to a negative power, and that FWI **
FWI should produce the same type as the first argument. (Rationale:
it's like repeated multiplication)

As for just taking the last n bits when passed an initial argument
that's too wide - that's exactly what you want to do. That lets us
emulate the C cast-to-smaller-type:

  short int g = (short int) some_big_variable;

with FWI.new(). C is always taking only the least significant
portion, so if we want to look like C...

fr ruby quiz:
# 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

and bonus if it handles ++ ?:slight_smile:

Hello,

I am lazy, so I avoided math as much as possible. In fact, I went to
some length to avoid dealing with the problem altogether, delegating
to ruby's Integer and even let ruby define my delegated functions :slight_smile:

# Fixed Width Integer Class for Ruby Quiz #85

···

# (C) 2006 Jürgen Strobel <juergen@strobel.info>
#
# This program is free software; you can redistribute it
# and/or modify it under the terms of the GNU General Public
# License as published by the Free Software Foundation;
# either version 2 of the License, or (at your option) any
# later version.

require "forwardable.rb"

# Base class for Integer classes with a fixed bit width
#
# Almost all methods are delegated to an encapsulated integer object.
# Metaprogramming is used to automate delegation & conversion.
#
class FixedWidthInt

  include Comparable
  extend Forwardable
  private_class_method :new
  
  def self.def_fwi_delegator(accessor, method) # taken from forward.rb
    module_eval(<<-EOS, "(__FWI_DELEGATION__)", 1)
      def #{method}(*args, &block)
        begin
          #puts "delegating #{method} and converting the result"
          self.class.new(#{accessor}.__send__(:#{method}, *args, &block), width)
        rescue Exception
          $@.delete_if{|s| /^\\(__FWI_DELEGATION__\\):confused: =~ s} unless Forwardable::debug
          Kernel::raise
        end
      end
    EOS
  end
  def method_missing(op, *args) # a method is missing?
    if @i.respond_to?(op) # our @i could handle it?
      #puts "defining new method #{op}"
      FixedWidthInt.def_fwi_delegator :@i, op # define it by delegation!
      self.send(op, *args) # and try again
    else # else
      super # NoMethodError
    end
  end
  
  def initialize(i = 0, w = 8)
    w = w.to_i
    raise "Invalid width" unless w >= 1
    @width, @i = w, i.to_i & ((1<<w) - 1)
  end

  def coerce_to_width(nw)
    self.class.new(i, nw)
  end

  def inspect
    "#{self.i}(#{self.class.name[0,1]}#{width})"
  end

  attr_reader :i, :width
  def_delegators :@i, :[], :zero?
  def_delegators :i, :to_i, :to_s, :<=>, :times, :coerce, :divmod, :quo, :to_f

  # We might have to define or delegate more methods explicitly which
  # are not working correctly with #method_missing. Especially those
  # not returning a FixedWidthInt.
end

# A fixed width unsigned integer
class UnsignedFixedWidthInt < FixedWidthInt
  public_class_method :new
end

# A fixed width signed integer
class SignedFixedWidthInt < FixedWidthInt
  public_class_method :new

  # Interpret @i differently if the highest bit is set, everything
  # else works magically thanks to 2's complement arithmentic.
  def i
    if (@i >> (width-1)).zero?
      @i
    else
      @i - (1 << width)
    end
  end
end

############ snip

I also took the tests from the irb session und extended them:

#!/usr/bin/ruby

# Fixed Width Integer Class Unit Tests (Ruby Quiz #85)
# (C) 2006 Jürgen Strobel <juergen@strobel.info>
#
# This program is free software; you can redistribute it
# and/or modify it under the terms of the GNU General Public
# License as published by the Free Software Foundation;
# either version 2 of the License, or (at your option) any
# later version.

require "fixed_width_int.rb"
require 'test/unit'

class FWITest < Test::Unit::TestCase
  def test_unsigned
    assert_equal 255, (n = UnsignedFixedWidthInt.new(0xff, 8))
    assert_equal 1, (n += 2)
    assert_equal 2, (n = n << 1)
    assert_equal 1, (n = n >> 1)
    assert_equal 254, (~n)
    assert_equal 13, (n += 12)
    assert_equal 12, (n = n & 0x0E)
    
    assert_kind_of FixedWidthInt, n
    assert_instance_of UnsignedFixedWidthInt, n
    assert_equal 144, (n * n)
    assert_equal 0, (n-n)
    assert_equal 3, (m = -9 + n)
    assert_kind_of Integer, m
    assert_kind_of Float, n.to_f
  end

  def test_signed
    assert_equal 1, (n = SignedFixedWidthInt.new(0x01, 8))
    assert_equal -128, (n = n << 7)
    assert_equal 127, (n -= 1)
    assert_equal 1, (n = n >> 6)
    assert_equal -1, (n -= 2)
    assert_equal 12, (n = n ^ 0xF3)
    assert_equal 13, (n = n | 0x01)
    
    assert_kind_of FixedWidthInt, n
    assert_instance_of SignedFixedWidthInt, n
    assert_equal -169&0xff, (n * (-n))
    assert_equal 0, (n-n)
    assert_equal -1, (m = -14 + n)
    assert_kind_of Integer, m
    assert_kind_of Float, n.quo(17)
  end
  
  def test_too_wide
    assert_equal 0, (n = UnsignedFixedWidthInt.new(0x0, 8))
    assert_equal 238, (n += 0xFFEE)
  end
end

############### snip

~/ruby/% ruby test_fixed_width_int.rb
Loaded suite test_fixed_width_int
Started
...
Finished in 0.023573 seconds.

-Jürgen

--
The box said it requires Windows 95 or better so I installed Linux

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

This is a little bit late (Wednesday), and doesn't behave as I
originally thought it ought to behave, because I don't have time to do
the different-operations-choose-types-differently properly. (the basic
idea is to coerce Integers to a class that wraps an Integer and does
coercions in different ways when presented with different operations)
Like most others, I implemented each differently sized FWI as a
separate class. In my code, one creates a new value by doing:

u_int_eight = FWI.new(32,false).new(8)

However, I also implement two other syntaxes (the syntax of the
original quiz spec and the syntax of Sander Land's test case) for
convenience. However, the number of bits aren't confined to any fixed
set; instead, classes are created on the fly as needed. (After using
the class a bit in irb, ask for FWI.constants)

As I worked on this quiz, I got into the idea of implementing C
arithmetic as closely as possible. This is significantly trickier
than it might appear at first; consider this C program:

int main(int argc, char *argv)
{
  unsigned short int u_four = 4;
  short int four = 4;
  long int l_neg_nine = -9;

  printf("The division yields %ld\n", l_neg_nine / u_four);
  printf("The modulus yields %ld\n", l_neg_nine % u_four);

  printf("The all signed division yields %ld\n", l_neg_nine / four);
  printf("The all signed modulus yields %ld\n", l_neg_nine % four);
}

What should it produce? Well, it turns out that even if you know the
size of "long" and "short" ints (32 and 16 bits on most modern
machines), the result depends on whether you're using a compiler that
follows ansi C numeric rules or the rules of K&R C. (The second two
lines are the same under both sets of rules, but the first two lines
aren't)

Even then, there's also the issue that C and Ruby disagree on how
integer division and remainder should behave with negative numbers.

As everyone who's dealt with C much should know, -9/4 (assuming
everything is signed and long enough) is -2. However, in Ruby:

irb(main):001:0> -9 / 4
=> -3

That is, C produces (a*1.0/b).truncate whereas Ruby produces
(a*1.0/b).floor - I'll note that Python agrees with Ruby in this.
Perl simply promotes integers to floats when the division isn't
exact.

The definition of % in both C and Ruby is consistent with the
definition of integer /; this means that in C, -9 % 4 is -1 but in
Ruby -9 % 4 is 3. Note that I think that the Ruby behavior makes much
more sense, mathematically, and both Python and Perl agree with this
behavior. Still, it makes emulating C arithmetic (say, if one were
copying an algorithm from ancient source code) tricky.

My solution was to add two new methods to Integer: c_div and
c_modulo. These implementinteger division and integer mod with C
semantics. I also then added a class method called c_math that takes
three arguments (two operands and an operation) and does the
equivalent of statements like this:

int c = a / b;

Including promoting a and b to a common type and doing the
cast-to-signed-int if necessary. This method uses c_div and c_modulo
when appropriate.

Thinking of other uses of this FWI class, specifically of how it could
be useful in translating algorithms, led me to add a few x86 assembly
language operations. After all, fixed width integers are all over the
place in assembly, and it's perfectly conceivable that someone might
want to translate an algorithm from ancient 8086 assembly into ruby
for a modern machine. In this version, I've only added one assembly
operation (rcl - rotate left through carry) since the demonstrations
of the other operations weren't very interesting after all.

So here's the code. First, the file fwi_use.rb that demonstrates some
uses of the class: it reimplements the C program above in both K&R
mode and ansi mode, and then implements the CRC algorithm used in
xmodem (adapted from assembly code).

======================= fwi_use.rb ===========================
# The following C program was typed into t.c and then
# compiled with gcc in K&R mode, and gcc in ansi mode.
# The results are below.

···

#
# int main(int argc, char *argv)
# {
# unsigned short int u_four = 4;
# short int four = 4;
# long int l_neg_nine = -9;
#
# printf("The division yields %ld\n", l_neg_nine / u_four);
# printf("The modulus yields %ld\n", l_neg_nine % u_four);
#
# printf("The all signed division yields %ld\n", l_neg_nine / four);
# printf("The all signed modulus yields %ld\n", l_neg_nine % four);
# }
#
# esau:~$ gcc-2.95 -traditional -o t t.c && ./t
# The division yields 1073741821
# The modulus yields 3
# The all signed division yields -2
# The all signed modulus yields -1
# esau:~$ gcc-2.95 -ansi -o t t.c && ./t
# The division yields -2
# The modulus yields -1
# The all signed division yields -2
# The all signed modulus yields -1
  
require 'fwi'

u_four = FWI.new(16,false).new(4)
four = FWI.new(16,true).new(4)
l_neg_nine = FWI.new(32,true).new(-9)

FWI.new(32).set_coerce_method(:kr)
puts "K&R Math:"
print("The division yields %d\n" %
  [FWI.new(32).c_math(l_neg_nine, :/, u_four)])
print("The modulus yields %d\n" %
  [FWI.new(32).c_math(l_neg_nine, :%, u_four)])
print("The all signed division yields %d\n" %
  [FWI.new(32).c_math(l_neg_nine, :/, four)])
print("The all signed modulus yields %d\n" %
  [FWI.new(32).c_math(l_neg_nine, :%, four)])

FWI.new(32).set_coerce_method(:ansi)
puts "\nansi Math:"
print("The division yields %d\n" %
  [FWI.new(32).c_math(l_neg_nine, :/, u_four)])
print("The modulus yields %d\n" %
  [FWI.new(32).c_math(l_neg_nine, :%, u_four)])
print("The all signed division yields %d\n" %
  [FWI.new(32).c_math(l_neg_nine, :/, four)])
print("The all signed modulus yields %d\n" %
  [FWI.new(32).c_math(l_neg_nine, :%, four)])

FWI.new(32).set_coerce_method(:first)
puts "\nRuby Math:"
print("The division yields %d\n" %
  [l_neg_nine / u_four])
print("The modulus yields %d\n" %
  [l_neg_nine % u_four])

print("The all signed division yields %d\n" %
  [l_neg_nine / four])
print("The all signed modulus yields %d\n" %
  [l_neg_nine % four])

# CRC test
# adapted from the assembly-language routines at
# http://www.wps.com/FidoNet/source/DOS-C-sources/Old%20DOS%20C%20library%20source/crc.asm

def xmodem_crc(a,prev_crc=FWI.new(16,false).new(0))
  crc = prev_crc
  a.each_byte{ |al_val|
    al = FWI.new(8,false).new(al_val)
    8.times {
      al.rcl!(1)
      crc.rcl!(1)
      crc ^= 0x1021 if FWI::FWIBase.carry>
    }
  }
  crc
end

def add_crc(s)
  a = s + "\x00\x00"
  crc = xmodem_crc(a)
  a[-2] = crc.to_i >> 8
  a[-1] = crc.to_i & 0xFF
  a
end

def check_crc(s)
  xmodem_crc(s) == 0
end

puts "\nAdding a crc to a classic string:"
a = add_crc('Hello World!')
p a
puts "Modifying:"
a[1] = ?i
p a
puts "check_crc yields: " + check_crc(a).inspect
a[1] = ?e
p a
puts "check_crc yields: " + check_crc(a).inspect

__END__

================ fwi.rb ======================

class Integer
  def c_modulo(o)
    if ((self >= 0 and o >= 0) or (self <=0 and o < 0))
      return self % o
    else
      return 0 if (self % o) == 0
      return (self % o) - o
    end
  end
  def c_div(o)
    return((self - self.c_modulo(o))/o);
  end
end

class FWI
  class FWIBase
    attr_reader :rawval
    include Comparable
    @@carry = 0
    def initialize(n)
      @rawval = n.to_i & maskval
      @@carry = (n.to_i != to_i) ? 1 : 0
    end
    def FWIBase.carry; @@carry; end
    def FWIBase.carry?; @@carry==1; end
    def maskval; 0xF; end
    def nbits; 4; end
    def signed?; false; end
    def to_i; rawval; end
    def to_s; to_i.to_s; end
    #def inspect; "<0x%0#{(nbits/4.0).ceil}x;%d>" % [rawval,to_i]; end
    def inspect; to_s; end
    def hash; to_i.hash; end
    def coerce(o)
      return self.class.fwi_coerce(o,self) if o.is_a? Integer
      to_i.coerce(o)
    end
    def ==(o)
      if (o.is_a? FWIBase) then
        to_i == o.to_i
      else
        to_i == o
      end
    end
    def eql?(o)
      o.class == self.class and self == o
    end
    def FWIBase.set_coerce_method(meth)
      case meth
      when :kr
        class << self
          def fwi_coerce(a,b)
            c = FWI.kr_math_class(a,b)
            [c.new(a),c.new(b)]
          end
        end
      when :ansi
        class << self
          def fwi_coerce(a,b)
            c = FWI.ansi_math_class(a,b)
            [c.new(a),c.new(b)]
          end
        end
      when :first
        class << self
          def fwi_coerce(a,b)
            return [a,b.to_i] if a.is_a? Integer
            [a,a.class.new(b)]
          end
        end
      when :second
        class << self
          def fwi_coerce(a,b)
            return [a.to_i,b] if b.is_a? Integer
            [b.class.new(a),b]
          end
        end
      else
        class << self
          self.send(:undef_method,:fwi_coerce)
        end
      end
    end
    %w(+ - / * ^ & | ** % << >> c_div c_modulo div modulo).each { |op|
      ops = op.to_sym
      FWIBase.send(:define_method,ops) { |o|
        if (o.class == self.class)
          self.class.new(to_i.send(ops,o.to_i))
        elsif o.is_a? Integer or o.is_a? FWIBase then
          b = self.class.fwi_coerce(self,o)
          b[0].send(ops,b[1])
        else
          to_i.send(ops,o)
        end
      }
    }
    %w(-@ +@ ~).each { |op|
      ops = op.to_sym
      FWIBase.send(:define_method,ops) {
        self.class.new(to_i.send(ops))
      }
    }
    def <=>(o)
      to_i.<=>(o)
    end
    # And now add a few x86 assembly operations
    # I only bother with rcl here, but for completeness
    # one could easily implement rcr, rol, ror, adc, sbb, etc.
    def rcl(n=1)
      lbits = n % (nbits+1)
      big = @rawval << lbits
      big |= (FWIBase.carry << (lbits-1))
      self.class.new((big & (2*maskval+1)) | (big >> (nbits+1)))
    end
    def rcl!(n)
      @rawval = maskval && rcl(n).to_i
    end
    def FWIBase.c_math(a, op, b)
      op = :c_div if op == :confused:
      op = :c_modulo if op == :%
      a,b = self.fwi_coerce(a,b)
      self.new(a.send(op,b))
    end
  end

  @@clazzhash = Hash.new {|h,k|
    l1 = __LINE__
    FWI.class_eval(%Q[
    class FWI_#{k} < FWI::FWIBase
      def initialize(n); super(n); end
      def maskval; #{(1<<k) - 1};end
      def signed?; false; end
      def nbits; #{k}; end
    end
    class FWI_#{k}_S < FWI_#{k}
      def signed?; true; end
      def to_i
        if rawval < #{1<<(k-1)} then
          rawval
        else
          rawval - #{1<<k}
        end
      end
    end
    ], __FILE__, l1+1)
    h[k] = FWI.class_eval(%Q"[FWI_#{k},FWI_#{k}_S]",
                __FILE__, __LINE__ - 1)
  }
  def FWI.new(n,signed=true)
    @@clazzhash[n][signed ? 1 : 0]
  end
  # K&R-like
  # First, promote both to the larger size.
  # Promotions of smaller to larger preserve unsignedness
  # Once promotions are done, result is unsigned if
  # either input is unsigned
  def FWI.kr_math_class(a,b)
    return b.class if (a.is_a? Integer)
    return a.class if (b.is_a? Integer)
    nbits = a.nbits
    nbits = b.nbits if b.nbits > nbits
    signed = a.signed? && b.signed?
    FWI.new(nbits,signed)
  end
  # ANSI C-like
  # promotions of smaller to larger promote to signed
  def FWI.ansi_math_class(a,b)
    return b.class if (a.is_a? Integer)
    return a.class if (b.is_a? Integer)
    nbits = a.nbits
    nbits = b.nbits if b.nbits > nbits
    signed = true
    signed &&= a.signed? if (a.nbits == nbits)
    signed &&= b.signed? if (b.nbits == nbits)
    FWI.new(nbits,signed)
  end
  FWIBase.set_coerce_method(:ansi)
end

# support the syntax from the quiz spec
class UnsignedFixedWidthInt
  def UnsignedFixedWidthInt.new(width,val)
    FWI.new(width,false).new(val)
  end
end
class SignedFixedWidthInt
  def SignedFixedWidthInt.new(width,val)
    FWI.new(width,true).new(val)
  end
end

# support the syntax in Sander Land's test case
def UnsignedFWI(width)
  FWI.new(width,false)
end

def SignedFWI(width)
  FWI.new(width,true)
end

__END__

It certainly looks like two classes to me (and that is what my earlier test
harness assumes).
The only way to do it with a single class would be to somehow tell it
whether it was signed or unsigned.
My plan is to implement one of them and then use it or extend it from the
other one.

If I didn't have this darn job I would be doing the quiz right now... :slight_smile:
JB

···

On 6/30/06, Josef 'Jupp' SCHUGT <jupp@gmx.de> wrote:

Hi!

At Fri, 30 Jun 2006 22:47:46 +0900, Ruby Quiz wrote:

> Write a class that can represent a signed or unsigned number with an
> arbitrary number of bits.
> :
> irb(main):001:0> n = UnsignedFixedWidthInt.new(0xFF, 8)
> :
> irb(main):001:0> n = SignedFixedWidthInt.new(0x01, 8)

Does "write a class" imply that UnsignedFixedWidthInt and
SignedFixedWidthInt are two different names for the just one class?

Josef 'Jupp' Schugt

Hmm, good point. Confusing wording there. Go with whatever method is easiest to implement. (I would probably make two classes.)

James Edward Gray II

···

On Jun 30, 2006, at 4:41 PM, Josef 'Jupp' SCHUGT wrote:

Hi!

At Fri, 30 Jun 2006 22:47:46 +0900, Ruby Quiz wrote:

Write a class that can represent a signed or unsigned number with an
arbitrary number of bits.
:
  irb(main):001:0> n = UnsignedFixedWidthInt.new(0xFF, 8)
:
  irb(main):001:0> n = SignedFixedWidthInt.new(0x01, 8)

Does "write a class" imply that UnsignedFixedWidthInt and
SignedFixedWidthInt are two different names for the just one class?

Peña schrieb:

and bonus if it handles ++ ?:slight_smile:

How do you plan to implement the pre increment operator?
Is this even possible?

Rob

Sorry for my ignorance but FWI stands for what?
(http://www.acronymattic.com/results.aspx?q=fwi\)

./alex

···

--
.w( the_mindstorm )p.
---
(http://themindstorms.blogspot.com)

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

I have some questions about some more edge cases.

Ruby Quiz <james@grayproductions.net> writes:

> 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>

It would seem from the above that whenever there's arithmetic with a
FWI as one operand and an Integer as the other, the result should be a
FWI and we should do simple twos-complement arithmetic in that width.

Now, should we also try to ensure that (2 + n) has the same result as
(n + 2) ?

What happens when we operate on two FWIs at once? I would propose
that the answer should be that the result is a FWI with as many bits
as the larger of the two operands, but I'm stuck as to what to do when
you add a SignedFixedWidthInt to an UnsignedFixedWidthInt. What is
the result?

I'd like to propose these rules of arithmetic:

Any operation between a FWI and a Float, Rational, or Complex has the
same result as though the FWI were replaced by an Integer of equal
value.

For operations +, -, ^, &&, *, /, %, and ||, an Integer with a FWI (in
either order) produces the same type of FWI as the FWI argument. For
those same operations, two FWIs produce an FWI with as many bits as
the longer of the two and unsigned if either operand is unsigned.
(rationale: like C)

An alternative set of rules (I'm asking for a ruling here) sets the
result of * and / to the same type as the first argument, and % to the
same type as the second argument. (Rationale: a * 2 is (a + a),
whether a is a Fixnum, Float, Array, or String. This seems like a
property we might want to preserve when a is a FWI, even if "2" is
only a FWI with the value of "2")

For ** (exponentiation), I'm not sure. My instinct is to say that
it's an error to raise a FWI to a negative power, and that FWI **
FWI should produce the same type as the first argument. (Rationale:
it's like repeated multiplication)

As for just taking the last n bits when passed an initial argument
that's too wide - that's exactly what you want to do. That lets us
emulate the C cast-to-smaller-type:

  short int g = (short int) some_big_variable;

with FWI.new(). C is always taking only the least significant
portion, so if we want to look like C...

I have some questions about some more edge cases.

Ruby Quiz <james@grayproductions.net> writes:

> 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>

It would seem from the above that whenever there's arithmetic with a
FWI as one operand and an Integer as the other, the result should be a
FWI and we should do simple twos-complement arithmetic in that width.

Now, should we also try to ensure that (2 + n) has the same result as
(n + 2) ?

What happens when we operate on two FWIs at once? I would propose
that the answer should be that the result is a FWI with as many bits
as the larger of the two operands, but I'm stuck as to what to do when
you add a SignedFixedWidthInt to an UnsignedFixedWidthInt. What is
the result?

I'd like to propose these rules of arithmetic:

Any operation between a FWI and a Float, Rational, or Complex has the
same result as though the FWI were replaced by an Integer of equal
value.

I agree. And that's a good hint for implementation :stuck_out_tongue:

For operations +, -, ^, &&, *, /, %, and ||, an Integer with a FWI (in
either order) produces the same type of FWI as the FWI argument. For
those same operations, two FWIs produce an FWI with as many bits as
the longer of the two and unsigned if either operand is unsigned.
(rationale: like C)

An alternative set of rules (I'm asking for a ruling here) sets the
result of * and / to the same type as the first argument, and % to the
same type as the second argument. (Rationale: a * 2 is (a + a),
whether a is a Fixnum, Float, Array, or String. This seems like a
property we might want to preserve when a is a FWI, even if "2" is
only a FWI with the value of "2")

I like this second set of rules much better, or something even
simpler: The result has to be of the same class as the first
argument. If I am going to use a width-restricted integer class at
all, I expect the width to stay the same for things like

fwi += other

and not be coerced to the larger width of both or some other class.
If other would be an (infinite size) ruby int, we don't coerce to that
either, don't we?

I also think we can safely ignore subtle points about C floats,
rationals etc, since we don't need to reimplement all of C's numeric
types here and sane interaction with other ruby numerics is hard
enough.

-Jürgen

···

On Sat, Jul 01, 2006 at 09:57:18AM +0900, Daniel Martin wrote:

For ** (exponentiation), I'm not sure. My instinct is to say that
it's an error to raise a FWI to a negative power, and that FWI **
FWI should produce the same type as the first argument. (Rationale:
it's like repeated multiplication)

As for just taking the last n bits when passed an initial argument
that's too wide - that's exactly what you want to do. That lets us
emulate the C cast-to-smaller-type:

  short int g = (short int) some_big_variable;

with FWI.new(). C is always taking only the least significant
portion, so if we want to look like C...

--
The box said it requires Windows 95 or better so I installed Linux

Jupp wrote:

Does "write a class" imply that UnsignedFixedWidthInt and
SignedFixedWidthInt are two different names for the just one class?

This is my first quiz submission for a while. This question is what
intrigued me, actually.

In C, an unsigned int is a different type to an unsigned long long.
Therefore, my solution provides a mechanism for generating these
subtypes: one class for each distinct combination of size and either
signed or unsigned, which all share the parent class FixedWidthInt.

Here's what the code that uses this library looks like:

  UInt32 = FixedWidthInt.type(:unsigned, 32) # define a new int type
  i = UInt32.get(42) # get instance of the type
  i == 42 #=> true

  j = FixedWidthInt.get(:unsigned, 32, 86) # no explicit type in call
  j.kind_of? UInt32 #=> true # but result type is same

  k = UnsignedFixedWidthInt.new(0xFF00FF00, 32) # quiz compatible syntax
  k.kind_of? UnsignedFixedWidthInt #=> true
  k.kind_of? UInt32 #=> true

My solution is not well-tested, and is pretty much guaranteed to have
some bugs due to the way it uses method_missing to delegate to an
underlying Integer. I also threw in Mauricio Fernandez' WeakHash (an
improved version to work with Bignum values) for the heck of it.

Here's the code:

http://dave.burt.id.au/ruby/fixed_width_ints.rb
http://dave.burt.id.au/ruby/weak_hash.rb

Cheers,
Dave

My solution consists of two functions which generate new FWI classes
you can use.
By using coerce it supports calculations with any combination of FWI
and normal integers.

rafb link: http://rafb.net/paste/results/We7miQ42.html

require 'delegate'

def UnsignedFWI(bits = 32)
  Class.new(DelegateClass(Bignum)) {
    define_method(:fix) {|n| n & (1<<bits)-1 }
    define_method(:initialize){|n| super fix(n.to_i) }
    define_method(:coerce) {|n| [self.class.new(n),self.to_i] }
    [:+,:-,:/,:*,:%,:**,:-@,:+@,:&,:|,:^,:<<,:>>,:~].each{|m|
      define_method(m) {|*args| self.class.new super(*args) }
    }
  }
end

def SignedFWI(bits = 32)
  Class.new(UnsignedFWI(bits)) {
    define_method(:fix){|n| (n & (1<<bits)-1) - (n[bits-1] << bits) }
  }
end

I have some questions about some more edge cases.

I'll give my two cents...

Ruby Quiz <james@grayproductions.net> writes:

  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>

It would seem from the above that whenever there's arithmetic with a
FWI as one operand and an Integer as the other, the result should be a
FWI and we should do simple twos-complement arithmetic in that width.

Now, should we also try to ensure that (2 + n) has the same result as
(n + 2) ?

Sure, if you can manage it.

What happens when we operate on two FWIs at once? I would propose
that the answer should be that the result is a FWI with as many bits
as the larger of the two operands

Makes sense to me.

, but I'm stuck as to what to do when
you add a SignedFixedWidthInt to an UnsignedFixedWidthInt. What is
the result?

That is a tough call. I think I can come up with good arguments for doing it two different ways, so I'm no help on this one.

I'd like to propose these rules of arithmetic:

Any operation between a FWI and a Float, Rational, or Complex has the
same result as though the FWI were replaced by an Integer of equal
value.

Makes sense to me.

For operations +, -, ^, &&, *, /, %, and ||, an Integer with a FWI (in
either order) produces the same type of FWI as the FWI argument. For
those same operations, two FWIs produce an FWI with as many bits as
the longer of the two and unsigned if either operand is unsigned.
(rationale: like C)

An alternative set of rules (I'm asking for a ruling here) sets the
result of * and / to the same type as the first argument, and % to the
same type as the second argument. (Rationale: a * 2 is (a + a),
whether a is a Fixnum, Float, Array, or String. This seems like a
property we might want to preserve when a is a FWI, even if "2" is
only a FWI with the value of "2")

For ** (exponentiation), I'm not sure. My instinct is to say that
it's an error to raise a FWI to a negative power, and that FWI **
FWI should produce the same type as the first argument. (Rationale:
it's like repeated multiplication)

This alternate set of rules makes the most sense to me, but the only "ruling" is what you think is best.

As for just taking the last n bits when passed an initial argument
that's too wide - that's exactly what you want to do. That lets us
emulate the C cast-to-smaller-type:

  short int g = (short int) some_big_variable;

with FWI.new(). C is always taking only the least significant
portion, so if we want to look like C...

I buy that.

James Edward Gray II

···

On Jun 30, 2006, at 7:57 PM, Daniel Martin wrote:

Daniel Martin <martin@snowplow.org> writes:

      crc ^= 0x1021 if FWI::FWIBase.carry>

Of course, this line was intended to be:

       crc ^= 0x1021 if FWI::FWIBase.carry?

I'm not sure how the typo'ed version got posted.

fr robert:
# How do you plan to implement the pre increment operator?
# Is this even possible?

sorry, i was just joking. i thought the smiley was enough to give hint :slight_smile:
kind regards -botp

Kerry

···

On 1 Jul 2006, at 09:53, Alexandru Popescu wrote:

Sorry for my ignorance but FWI stands for what?

"Alexandru Popescu" <the.mindstorm.mailinglist@gmail.com> writes:

Sorry for my ignorance but FWI stands for what?
(http://www.acronymattic.com/results.aspx?q=fwi\)

FixedWidthInt, meant to include both UnsignedFixedWidthInt and
SignedFixedWidthInt.

Robert Retzbach <rretzbach@googlemail.com> writes:

Peña schrieb:

and bonus if it handles ++ ?:slight_smile:

How do you plan to implement the pre increment operator?
Is this even possible?

Pre is, post isn't.
The rest is left as an exercise to the reader. :wink:

···

Rob

--
Christian Neukirchen <chneukirchen@gmail.com> http://chneukirchen.org