113 lines
2.6 KiB
Plaintext
113 lines
2.6 KiB
Plaintext
# -*- ObjectIcon -*-
|
|
#
|
|
# Object Icon has a "Number" class (with subclasses) that has "add"
|
|
# and "mul" methods. These methods can be implemented in a modular
|
|
# numbers class, even though we cannot redefine the symbolic operators
|
|
# "+" and "*". Neither can we inherit from Number, but that turns out
|
|
# not to get in our way.
|
|
#
|
|
|
|
import io
|
|
import ipl.types
|
|
import numbers (Rat)
|
|
import util (need_integer)
|
|
|
|
procedure main ()
|
|
local x
|
|
|
|
x := Rat (10) # The number 10 as a rational with denominator 1.
|
|
write ("no modulus: ", f(x).n)
|
|
|
|
x := Modular (10, 13)
|
|
write ("modulus 13: ", f(x).least_residue)
|
|
end
|
|
|
|
procedure f(x)
|
|
return npow(x, 100).add(x).add(1)
|
|
end
|
|
|
|
procedure npow (x, i)
|
|
# Raise a number to a non-negative power, using the methods of its
|
|
# class. The algorithm is the squaring method.
|
|
|
|
local accum, i_halved
|
|
|
|
if i < 0 then runerr ("Non-negative number expected", i)
|
|
|
|
accum := typeof(x) (1)
|
|
|
|
# Perhaps the following hack can be eliminated?
|
|
if is (x, Modular) then accum := Modular (1, x.modulus)
|
|
|
|
while i ~= 0 do
|
|
{
|
|
i_halved := i / 2
|
|
if i_halved + i_halved ~= i then accum := x.mul(accum)
|
|
x := x.mul(x)
|
|
i := i_halved
|
|
}
|
|
return accum
|
|
end
|
|
|
|
class Modular ()
|
|
public const least_residue
|
|
public const modulus
|
|
|
|
public new (num, m)
|
|
if /m & is (num, Modular) then
|
|
{
|
|
self.least_residue := num.least_residue
|
|
self.modulus := num.modulus
|
|
}
|
|
else
|
|
{
|
|
/m := 0
|
|
m := need_integer (m)
|
|
if m < 0 then runerr ("Non-negative number expected", m)
|
|
self.modulus := m
|
|
num := need_integer (num)
|
|
if m = 0 then
|
|
self.least_residue := num # A regular integer.
|
|
else
|
|
self.least_residue := residue (num, modulus)
|
|
}
|
|
return
|
|
end
|
|
|
|
public add (x)
|
|
if is (x, Modular) then x := x.least_residue
|
|
return Modular (least_residue + x, need_modulus (self, x))
|
|
end
|
|
|
|
public mul (x)
|
|
if is (x, Modular) then x := x.least_residue
|
|
return Modular (least_residue * x, need_modulus (self, x))
|
|
end
|
|
end
|
|
|
|
procedure need_modulus (x, y)
|
|
local mx, my
|
|
|
|
mx := if is (x, Modular) then x.modulus else 0
|
|
my := if is (y, Modular) then y.modulus else 0
|
|
if mx = 0 then
|
|
{
|
|
if my = 0 then runerr ("Cannot determine the modulus", [x, y])
|
|
mx := my
|
|
}
|
|
else if my = 0 then
|
|
my := mx
|
|
if mx ~= my then runerr ("Mismatched moduli", [x, y])
|
|
return mx
|
|
end
|
|
|
|
procedure residue(i, m, j)
|
|
# Residue for j-based integers, taken from the Arizona Icon IPL
|
|
# (which is in the public domain). With the default value j=0, this
|
|
# is what we want for reducing numbers to their least residues.
|
|
/j := 0
|
|
i %:= m
|
|
if i < j then i +:= m
|
|
return i
|
|
end
|