--KdquIMZPjGJQvRdI
Content-Type: text/plain; charset=iso-8859-1
Content-Disposition: inline
Content-Transfer-Encoding: quoted-printable

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


# Fixed Width Integer Class for Ruby Quiz #85
# (C) 2006 Jgen 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__\\):/ =~ 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 Jgen 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.


-Jgen

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

--KdquIMZPjGJQvRdI
Content-Type: application/pgp-signature; name="signature.asc"
Content-Description: Digital signature
Content-Disposition: inline

-----BEGIN PGP SIGNATURE-----
Version: GnuPG v1.4.3 (GNU/Linux)

iQEVAwUBRKf6fvy64gyiEfXtAQIorgf/ZqH71zObLEH8tulw0qG77uv/hBYyz1H6
com5Y+xTjCewmvfWM6EYbJYxg63bmoEdsHzI4LFCliadNh3NMY7W3ynw3a17HoeX
FwP1zxEgKa+Amy7PfM5sZpg5yFkXo0UClj71gq/P72lnQCuRD2gcrvPigT+P0/sX
vWM4VKa+YyGKv/McX/fhwO74XJjaceAP9+FPKXQswH84eA5hp6cyf26/PJz39FT2
pbrSfgOFss8ujZNSbk+U0HQUcOUTcMoTPTW9b0/HvafuUMhPs7HnRLiVTfVOuIyr
CSXyimvZLmUv7WuNampkWY1r+UIlDlEyPvPbCg18B4RDb5O6zKk5Ow¥¢xf
-----END PGP SIGNATURE-----

--KdquIMZPjGJQvRdI--