#
# DistributedMutlivariatePolynomial(R,X)
#
# A distributed multivariate polynomials category R[X]
#
# R must be a ring and X must be an exponent vector which is an ordered
# Abelian monoid. See also SparseDistributedMultivariatePolynomial
#
# Authors: MBM and DG 1991/1992
#
macro(UFD=UniqueFactorizationDomain);

DistributedMultivariatePolynomial := proc() local P,R,S,env;
    
    R := args[1];
    S := args[2];

    if not hasCategory(R,Ring) then ERROR(`1st argument must be a Ring`)
    elif not hasCategory(S,ExponentVector) then
    	ERROR(`2nd argument must be an ExponentVector`) fi;

    if nargs = 2 then P := newCategory() else P := args[3] fi;

    addCategory(P,MultivariatePolynomial);
    if hasCategory(R,UFD) then P := UFD(P)
    elif hasCategory(R,GcdDomain) then P := GcdDomain(P)
    elif hasCategory(R,IntegralDomain) then P := IntegralDomain(P)
    elif hasCategory(R,CommutativeRing) then P := CommutativeRing(P)
    else P := Ring(P)
    fi;

    defOperation( List2Poly, List(Record(R,S)) &-> P, P );
    defOperation( Poly2List, P &-> List(Record(R,S)), P );
    defOperation( TotalDegree, P &-> Integer, P );
    defOperation( Degree, [P,Name] &-> Integer, P );
    defOperation( Coeff, [P,S] &-> R, P );
    defOperation( Lcoeff, [P,Name] &-> R, P );
    defOperation( Lterm, P &-> P, P );
    defOperation( CoefficientRing, Ring, P );
    defOperation( ExponentVector, ExponentVector, P);
    defOperation( Variables, List(Name), P);
    defOperation( `.`, [R,P] &-> P, P );

    env := ['D' = P, 'C' = R, 'E' = S];

    P[CoefficientRing] := R;
    P[ExponentVector] := S;
    P[Characteristic] := R[Characteristic];
    P[Variables] := S[Variables];
    P[Random] := subs(env, proc() 
    	D[List2Poly]( [ '[C[Random](),E[Random]()]' $ 5 ] )
    end);
    P[Input] := subs(env, proc(a) `DMP/Input`(D,a) end);
    P[Output] := subs(env, proc(a) `DMP/Output`(D,a) end);
    P[Coerce] := subs(env, proc(a) 
    	D[List2Poly]([[C[Coerce](a), E[0]]])
    end);
    P[TotalDegree] := subs(env, proc(a) local t;
    	max( seq( E[TotalDegree](t[2]), t=D[Poly2List](a) ) )
    end);
    P[Coeff] := subs(env, proc(a,e) local c,t;
       c := C[0];
       for t in D[Poly2List](a) do
          if E[`=`](t[2],e) then c := C[`+`](c, t[1]) fi
       od;
       c
    end);
    P[Degree] := subs(env, proc(a,n) local d,i,m,t;
        if not member(n,D[Variables],i) then ERROR(`bad variable`,n) fi;
        m := 0;
        for t in D[Poly2List](a) do
    	d := E[Degree](t[2],i);
    	if d > m then m := d fi;
        od;
        m
    end);
    P[Lcoeff] := subs(env, proc(a,t) local c, e, m, s;
       c := C[0]; e := E[0];
       for m in D[Poly2List](a) do
          s := E[`<>=`](m[2], e);
          if s=1 then c := m[1]; e := m[2]
          elif s=0 then c := C[`+`](c,m[1])
          fi
       od;
       if nargs=2 then t := e fi;
       c
    end);
    P[Lterm] := subs(env, proc(a) local c,t;
       c := D[Lcoeff](a,t);
       D[List2Poly]( [ [c,t] ] )
    end);
    P[`.`] := subs(env, proc(x,a) 
    	D[List2Poly](map(subs('CC'=C, (t,c) -> [CC[`*`](c,t[1]),t[2]]),
                    D[Poly2List](a), x))
    end);

    P[`+`] := subs(env, proc(a,b)
       if nargs = 1 then RETURN(a) fi;
       if nargs > 2 then RETURN(D[`+`](D[`+`](a,b),args[3..nargs])) fi;
       D[List2Poly]([op(D[Poly2List](a)), op(D[Poly2List](b))])
    end);
    P[`*`] := subs(env, proc(a,b) local t1,t2,c,i,n,A,B;
       if nargs = 1 then RETURN(a) fi;
       if nargs > 2 then RETURN(D[`*`](D[`*`](a,b),args[3..nargs])) fi;
       if type(a,integer) then RETURN(D[`.`](C[Coerce](a),b)) fi;
       A := D[Poly2List](a);
       B := D[Poly2List](b);
       n := 0;
       for t1 in A do
          for t2 in B do
    	 n := n+1;
    	 c[n] := [C[`*`](t1[1],t2[1]), E[`+`](t1[2],t2[2])];
          od;
       od;
       D[List2Poly]([seq( c[i], i=1..n )])
    end);
        P[Inv] := subs(env, proc(a) local t;
                if D[TotalDegree](a) > 0 then RETURN( FAIL ) fi;
    	t := C[Inv](D[Lcoeff](a));
    	if t = FAIL then FAIL else D[List2Poly]( [[t,E[0]]] ) fi
        end);

    if hasCategory(R,IntegralDomain) then
    	defOperation( PseudoRem, [P,P,Name,Name] &-> P, P );
    	defOperation( Resultant, [P,P,Name] &-> R, P );
    	P[Unit] := subs(env, proc(a)
    	    D[List2Poly]( [ [C[Unit](D[Lcoeff](a)),E[0]] ] )
    	end);
    fi;

    if hasCategory(R,GcdDomain) then
    	defOperation( Content, [P,Name] &-> R, P );
    	defOperation( Primpart, [P,Name] &-> P, P );
    	P[Content] := subs(env, proc(a,r) local b,c,t;
    	    b := D[Poly2List](a);
    	    if a = D[0] then c := C[0]
    		else c := C[Gcd](seq(t[1], t=b)) fi;
    	    if nargs = 2 then
    	        if a = D[0] or c = C[1] then r := a else
                r := D[List2Poly]([seq( [C[Div](t[1],c),t[2]], t=b )])
    	        fi;
    	    fi;
    	    c
    	end);
    	P[Primpart] := subs(env, proc(a,r) local b,c,p,t,u;
    	    if a = D[0] then
    	        if nargs = 2 then r := C[0] fi;
    	        RETURN( D[0] )
    	    fi;
    	    b := D[Poly2List](a);
    	    c := C[Gcd]( seq(t[1], t=b) );
    	    u := C[Unit](D[Lcoeff](a));
    	    c := C[`*`](u,c);
    	    if c = C[1] then p := a
    	    else p := D[List2Poly]([seq( [C[Div](t[1],c),t[2]], t=b )])
    	    fi;
    	    if nargs = 2 then r := c fi;
    	    p
    	end);
    fi;

    if hasCategory(R, Field) then
    	P[Content] := subs(env, proc(a,p)
    	   if nargs=2 then p := a fi;
    	   if D[`=`](a, D[0]) then C[0] else C[1] fi
    	end);
    	P[Primpart] := subs(env, proc(a,r) local u;
    	   if D[`=`](a, D[0]) then
    	      if nargs=2 then r := C[0] fi; a
    	   else
    	      u := D[UnitNormal](a);
    	      if nargs=2 then r := u[1] fi; u[2]
    	   fi
    	end);
    fi;
    op(P)
end:

`DMP/Input` := proc(P,a) local C,E,R,T,X,c,i,k,n,t,p;

    R := P[CoefficientRing]; E := P[ExponentVector]; X := E[Variables];
    C := traperror( collect(a,X,distributed) );
    if C = lasterror then RETURN( FAIL ) fi;

    C := coeffs( C, X, 'T' ); C := [C]; T := [T]; n := nops(C);
    for i to n do
        c := R[Input](C[i]); t := E[Input](T[i]);
        if c = FAIL or t = FAIL then RETURN(FAIL) fi;
        p[i] := [c,t];
    od;
    P[List2Poly]( [p[k] $ k=1..n] )
end:


`DMP/Output` := proc(P,a) local R,E,A;
    R := P[CoefficientRing];
    E := P[ExponentVector];
    A := P[Poly2List](a);
    A := map( proc(x,R,E) R[Output](x[1]) * E[Output](x[2]) end, A, R, E );
    # A := [ seq( R[Output](x[1]) * E[Output](x[2]), x=A ) ]
    convert(A,`+`)
end:

save `DMP.m`;
quit
