On Oct 10, 2008, at 11:13 AM, Matthew Moss wrote:
> ## Modular Arithmetic (#179)
>
> [1]: http://mathworld.wolfram.com/ModularArithmetic.html
> [2]: http://en.wikipedia.org/wiki/Modular_arithmetic
> [3]: http://www.math.harvard.edu/~sarah/magic/topics/division

I agreed with Matthew's design choices regarding the type of "Integer  
{op} Modulo" being Integer.  I'd intended to add some tests to show  
that Modulo.new(0)-10 would give Modulo.new(16) and generally give an  
integer value in the 0...modulus domain.  (Of course, if I were less  
busy, I'd have taken a shot at division, too.)

-Rob

==> modulo.rb <==
class Modulo
  include Comparable

  # Returns a new object that behaves according to the rules of
  # Modular Arithmetic, but otherwise responds like an Integer from
  # the set of integers between 0 and modulus-1.
  def initialize(value=0, modulus=26)
    @modulus = modulus
    @value = value % @modulus
  end

  def to_int
    @value
  end

  def to_i
    self.to_int
  end

  def <=>(other)
    case other
    when Modulo
      @value <=> other.to_int
    when Integer
      @value <=> other
    else
      raise ArgumentError, "I don't know how to compare a  
#{other.class.name}"
    end
  end

  def coerce(other)
    case other
    when Integer
      return other, self.to_int
    else
      super
    end
  end

  [ :+, :-, :* ].each do |method|
    define_method method do |other|
      self.class.new(@value.__send__(method, other.to_int), @modulus)
    end
  end

end
__END__

==> test_modulo.rb <==
require 'test/unit'
require 'modulo'

class TestModulo < Test::Unit::TestCase
  def test_modulo_equality
    a = Modulo.new(23)
    b = Modulo.new(23)
    c = Modulo.new(179)
    assert_equal(a, b)
    assert_equal(a, c)
  end

  def test_add_zero
    a = Modulo.new(15)
    b = Modulo.new(0)
    assert_equal(a, a + b)
  end

  def test_add
    a = Modulo.new(15)
    b = Modulo.new(19)
    c = Modulo.new(8)
    assert_equal(c, a + b)
  end

  def test_add_int
    a = Modulo.new(15)
    c = Modulo.new(10)
    assert_equal(c, a + 21)
  end

  def test_sub
    a = Modulo.new(15)
    b = Modulo.new(19)
    c = Modulo.new(22)
    assert_equal(c, a - b)
  end

  def test_sub_int
    a = Modulo.new(15)
    c = Modulo.new(1)
    assert_equal(c, a - 66)
  end

  def test_mul
    a = Modulo.new(15)
    b = Modulo.new(7)
    c = Modulo.new(1)
    assert_equal(c, a * b)
  end

  def test_mul_int
    a = Modulo.new(15)
    c = Modulo.new(9)
    assert_equal(c, a * 11)
  end

  # from Ken Bloom
  def test_add_reverse
    a = Modulo.new(15)
    assert_equal(30, 15 + a)
  end

  # from Ken Bloom
  def test_sub_reverse
    a = Modulo.new(15)
    assert_equal(-14, 1-a)
  end

  def test_mul_int_reverse
    a = Modulo.new(15, 8)
    assert_equal(77, 11 * a)
  end

  def test_non_default_modulo
    a = Modulo.new(15, 11)
    b = Modulo.new(9, 11)

    assert_equal(2, a + b)
    assert_equal(6, a - b)
    assert_equal(3, a * b)
  end

  def test_compare
    assert_equal(1, Modulo.new(15) <=> Modulo.new(30))
  end

  def test_compare_int
    assert_equal(-1, Modulo.new(15) <=> 35)
  end

  def test_sort
    x = [Modulo.new(15), Modulo.new(29), Modulo.new(-6), Modulo.new(57)]
    y = [3, 5, 15, 20]
    assert_equal(y, x.sort)
  end

#   def test_value_range
#   end
end
__END__



Rob Biedenharn		http://agileconsultingllc.com
Rob / AgileConsultingLLC.com