```On Fri, 10 Oct 2008 15:29:02 -0500, Matthew Moss wrote:

>> Just a preview of my solution (inspired by TZFile):
>>
>> 	Mod26=ModularBase.new(26)
>>        Mod8=ModularBase.new(8)
>>
>>       def test_incompat_bases
>>         assert_raises(ArgumentError) do
>>           Mod26.new(1)+Mod8.new(1)
>>         end
>>         assert_nothing_raised do
>>           Mod26.new(1)+Mod8.new(1).to_i
>>         end
>>       end
>>
>> That said, please clarify on the behavior of a couple tests:
>>
>>       #new
>>          a = Mod26.new(15)
>>          assert_equal(???, 15 + a)
>>       end
>>
>>       #new
>>       def test_sub_reverse
>>          a = Mod26.new(15)
>>          assert_equal(???, 1-a)
>>       end
>>
>>       def test_mul_int_reverse
>>          a = Mod8.new(15)
>>          assert_equal(77, 11 * a)
>>       end
>>
>> Clearly a Modulo +/*/- an Integer should give a Modulo Should the
>> reverse relation promote the Modulo to an Integer, or promote
>> the Integer to a Modulo (of the same base)?
>
>
> This issue requires some sort of design decision, which I intentionally
> left out of the requirements, so you may do what you like in this
> respect, and perhaps explain why you made a particular choice.
>
> When I started experimenting with my own solution, my design choice was
> that the result type of any operation was the same as the first operand.
> I did this primarily for simplicity, since I didn't need to raise errors
> for incompatible bases, and most operations became trivial. Of course,
> this choice is not without problem, since some operations that might
> normally be communitive (if compatible modulo was enforced) are no
> longer communitive.
>
> In context with your questions above and my particular design choice:
>
> 1. I didn't raise any errors for mismatched bases; I used the left
> operand to determine the result's type. 2. As all your reverse tests
> (which should be considered by all implementors) have a left Integer
> argument, all the answers would be the same, and the domain would be the
> full integer set.  So, from top to bottom, I would replace ??? with 30
> and -14.

OK. I decided against that and decided that the operations would be
commutative, always promoting to modular arithmetic.

Here's my solution:

module ModularBase
def self.new  base
c=Class.new do
include ModularBase
end
c.class_eval do
define_method :base do
base
end
end
c
end

def self.define_op op
class_eval <<-"end;"
def #{op} rhs
case rhs
when self.class: self.class.new(@value #{op} rhs.instance_variable_get(:@value))
when Integer: self.class.new(@value #{op} rhs)
when ModularBase: raise ArgumentError, "Error performing modular arithmetic with incompatible bases"
else
raise ArgumentError, "Requires an integer or compatible base"
end
end
end;
end

#begin defining operations

def initialize value
@value = value % base
end

define_op :+
define_op :-
define_op :*

def == rhs
case rhs
when self.class: @value == rhs.instance_variable_get(:@value)
when ModularBase: false
when Integer: @value == rhs
else false
end
end

def coerce other
[self.class.new(other), self]
end

def <=> rhs
case rhs
when self.class: @value <=> rhs.instance_variable_get(:@value)
when Integer: @value <=> rhs
else raise ArgumentError
end
end

def to_i
@value
end
def to_s
@value.to_s
end
end

exit if __FILE__ != \$0

require 'test/unit'

class TestModulo < Test::Unit::TestCase
Mod26=ModularBase.new(26)
Mod11=ModularBase.new(11)
Mod8=ModularBase.new(8)

def test_modulo_equality
a = Mod26.new(23)
b = Mod26.new(23)
c = Mod26.new(179)
assert_equal(a, b)
assert_equal(a, c)
end

a = Mod26.new(15)
b = Mod26.new(0)
assert_equal(a, a + b)
end

a = Mod26.new(15)
b = Mod26.new(19)
c = Mod26.new(8)
assert_equal(c, a + b)
end

a = Mod26.new(15)
assert_equal(4, 15 + a)
end

a = Mod26.new(15)
c = Mod26.new(10)
assert_equal(c, a + 21)
end

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

def test_sub_reverse
a = Mod26.new(15)
assert_equal(12, 1-a)
end

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

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

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

def test_mul_int_reverse
a = Mod8.new(15)
assert_equal(5, 11 * a)
end

def test_non_default_modulo
a = Mod11.new(15)
b = Mod11.new(9)

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

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

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

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

def test_incompat_bases
assert_raises(ArgumentError) do
Mod26.new(1)+Mod8.new(1)
end
assert_nothing_raised do
Mod26.new(1)+Mod8.new(1).to_i
end
end
end

--
Chanoch (Ken) Bloom. PhD candidate. Linguistic Cognition Laboratory.
Department of Computer Science. Illinois Institute of Technology.
http://www.iit.edu/~kbloom1/

```