#
#--> evalmod(a,b,x): evaluate the expression a(x) modulo (b(x))
#--> evalmod(a,b,x,p): evaluate the expression a(x) modulo (b(x)) mod p
#
# a:ratpoly(ratpoly,x)
# b:polynom(ratpoly,x)
# x:name
# p:posint (characteristic)
#
# Author: MBM Nov/91
#

evalmod := proc(a,b,x,p)
    if not type(x,name) then ERROR(`3rd argument must be a name`) fi;
    if not type(a,ratpoly(ratpoly,x)) then
	ERROR(`1st argument must be of type`,ratpoly(ratpoly,x)) fi;
    if not type(b,polynom(ratpoly,x)) then 
	ERROR(`2nd argument must be of type`,polynom(ratpoly,x)) fi;
    if nargs = 3 then RETURN( `evalmod/evalmod/rational`(a,b,x) ) fi;
    if not type(p,posint) then
	ERROR(`4th argument (modulus) must be of type`,posint) fi;
    `evalmod/evalmod/finite`(a,b,x,p)
end:

`evalmod/evalmod/finite` := proc(a,b,x,p) local n,k,r,t;
    if type(a,`^`) then
	n := op(2,a);
	if not type(n,integer) then ERROR(`exponent must be an integer`) fi;
	t := procname(op(1,a),b,x,p);
	Powmod(t,n,b,x) mod p
    elif type(a,polynom(anything,x)) and degree(a,x) < 2*degree(b,x) then Rem(a,b,x) mod p
    elif type(a,`*`) then
	r := procname(op(1,a),b,x,p);
	for k from 2 to nops(a) do
	    t := procname(op(k,a),b,x,p);
	    r := Rem(r*t,b,x) mod p
	od;
	r
    elif type(a,`+`) then Rem(map(procname,args),b,x) mod p
    else ERROR(`this should not happen`)
    fi
end:

`evalmod/evalmod/rational` := proc(a,b,x) local n,k,r,t;
    if type(a,polynom(anything,x)) and degree(a,x) < 2*degree(b,x) then rem(a,b,x)
    elif type(a,`*`) then
	r := procname(op(1,a),b,x);
	for k from 2 to nops(a) do
	    t := procname(op(k,a),b,x);
	    r := rem(r*t,b,x)
	od;
	r
    elif type(a,`^`) then
	n := op(2,a);
	if not type(n,integer) then ERROR(`exponent must be an integer`) fi;
	t := procname(op(1,a),b,x);
	powmod(t,n,b,x)
    elif type(a,`+`) then rem(map(procname,args),b,x)
    else ERROR(`this should not happen`)
    fi
end:

#
#--> powmod(a,n,b,x): computes a^n mod b = rem(a^n,b,x) using binary powering
#
# Where (a,b):polynom(R,x), n:integer, x:name
# For characteristic 0 use Powmod(a,n,b,x) mod p
#
# Author MBM: Nov/91
#
powmod := proc(a,n,b,x) local e,y,z;

    option `Copyright 1991 by the University of Waterloo`;
    if not type([n,x,a,b],
		[integer,name,polynom(ratpoly,x),polynom(ratpoly,x)]) then
	ERROR(`arguments must be of type`,
		[polynom(ratpoly,x),integer,polynom(ratpoly,x),name]) fi;

    z := rem(a,b,x);
    if n = 0 then
	if z = 0 then ERROR(`0^0 is undefined`) else RETURN(1) fi;
    elif n = 1 then RETURN( z )
    elif n < 0 then
	if gcdex(z,b,x,'z') = 1 then e := -n
	else ERROR(`inverse does not exist`)
	fi
    else e := n
    fi;

    y := 1;
    do	#  binary powering
	if irem(e,2,'e') = 1 then y := rem( z*y, b, x ) fi;
	if e = 0 then RETURN( y ) else z := rem( z*z, b, x ) fi
    od;

end:

`help/text/evalmod` := TEXT(
`FUNCTION: evalmod - evaluate an expression in a residue ring`,
`      `,
`CALLING SEQUENCES: evalmod(a, b, x);  or  evalmod(a, b, x, p);`,
`      `,
`PARAMETERS:`,
`   a - a rational expression over the rational numbers`,
`   b - a polynomial in x over the rationals`,
`   x - a name`,
`   p - (optional) a positive integer`,
`      `,
`SYNOPSIS:   `,
`   `,
`- Given the polynomial ideal (b) where b is a univariate polynomial in x`,
`  over Q or Z mod p, evaluate the expression a modulo (b).`,
`   `,
`- Given 3 arguments, this procedure evaluates a(x) over Q.`,
`   `,
`- Given 4 arguments, this procedure evaluate a(x) over Z mod p.`,
`      `,
`EXAMPLES:   `,
`> evalmod(x^4,x^2+1,x);`,
`                                       1`,
`   `,
`> evalmod(x^2-1,x,x,7);`,
`                                       6`,
`      `,
`> evalmod(x^8+2*x,x^3-9*x+2,x,3);`,
`                                     2`,
`                                    x  + 2 x`,
`      `,
`> evalmod(x^100,x^2-2,x);`,
`                                1125899906842624`,
`   `,
`> evalmod(x^(2^100),x^4+x+1,x,2);`,
`                                       x`,
`   `,
`SEE ALSO:  evala, mod, Powmod`
):

#save `evalmod.m`;
#quit
