--* From BMT%WATSON.vnet.ibm.com@yktvmh.watson.ibm.com  Thu Dec 30 11:32:50 1993
--* Received: from yktvmh.watson.ibm.com by leonardo.watson.ibm.com (AIX 3.2/UCB 5.64/4.03)
--*           id AA25800; Thu, 30 Dec 1993 11:32:50 -0500
--* Received: from watson.vnet.ibm.com by yktvmh.watson.ibm.com (IBM VM SMTP V2R3)
--*    with BSMTP id 5592; Thu, 30 Dec 93 11:38:57 EST
--* Received: from YKTVMH by watson.vnet.ibm.com with "VAGENT.V1.0"
--*           id <A.BMT.NOTE.VAGENT2.8455.Dec.30.11:38:56.-0500>
--*           for asbugs@watson; Thu, 30 Dec 93 11:38:56 -0500
--* Received: from YKTVMH by watson.vnet.ibm.com with "VAGENT.V1.0"
--*           id 8453; Thu, 30 Dec 1993 11:38:55 EST
--* Received: from cyst.watson.ibm.com by yktvmh.watson.ibm.com (IBM VM SMTP V2R3)
--*    with TCP; Thu, 30 Dec 93 11:38:54 EST
--* Received: from spadserv.watson.ibm.com by cyst.watson.ibm.com (AIX 3.2/UCB 5.64/900528)
--*   id AA40472; Thu, 30 Dec 1993 11:38:44 -0500
--* Received: by spadserv.watson.ibm.com (AIX 3.2/UCB 5.64/900524)
--*           id AA30914; Thu, 30 Dec 1993 11:40:54 -0500
--* Date: Thu, 30 Dec 1993 11:40:54 -0500
--* From: bmt@spadserv.watson.ibm.com
--* X-External-Networks: yes
--* Message-Id: <9312301640.AA30914@spadserv.watson.ibm.com>
--* To: asbugs@watson.ibm.com
--* Subject: [4] compiler dies with program fault [bug2.as][33.4 (current)]

--@ Fixed  by:  SSD   Mon Apr 18 15:06:10 1994 
--@ Tested by:  none 
--@ Summary:    Call tfExpr instead of tfGetExpr. 
-- PI: the crash is in tinfer phase
-- PI2 (3/28/94): now is no more crashing, but giving some error msgs.
-- Or is a substituion bug or not a bug



#include "aslib.as"
MODRING ==> ModularRing
EMR ==> EuclideanModularRing
MODFIELD ==> ModularField
one?(xxx) ==> xxx = 1

CommutativeRing:Category == Ring with
   *: (Integer,%) -> %
   coerce: Integer -> %


+++
ModularRing(R: CommutativeRing,Mod: AbelianMonoid,reduction: (R,Mod) -> R,
  merge: (Mod,Mod) -> Partial(Mod),exactQuo: (R,R,Mod) -> Partial(R)): with
      CommutativeRing;
      modulus: % -> Mod;
      coerce: % -> R;
      reduce: (R,Mod) -> %;
      exQuo: (%,%) -> Partial(%);
      recip: % -> Partial(%);
      inv: % -> %;
 == add
  Rep ==> Record(val: R,modulo: Mod);
  import from Mod;
  import from R;
  import from Rep;
  import from Partial Mod
  import from Partial %
  import from Partial R
  default x,y: %;
  modulus (x:%):Mod == (rep(x)).modulo;
  coerce (x:%):R == (rep(x)).val;
  coerce (i:Integer):% == per([i::R,0]$Rep);
  (i:Integer) * (x:%):% == i::% * x;
--  coerce (x:%):OutputForm == (rep(x)).val::OutputForm;
  apply(p:OutPort,x:%):OutPort == p(rep(x).val)
  reduce(a:R,m:Mod):% == per([reduction(a,m),m]$Rep);
  0:% == per([0$R,0$Mod]$Rep);
  1:% == per([1$R,0$Mod]$Rep);
  zero? (x:%):Boolean == zero?( (rep(x)).val);
--  one? (x:%):Boolean == one?( (rep(x)).val);
  newmodulo(m1:Mod,m2:Mod):Mod ==
    r := merge(m1,m2);
    r case "failed" => error "incompatible moduli";
    r::Mod;
  (x:%) = (y:%):Boolean == {
    (rep(x)).val = (rep(y)).val => true;
    (rep(x)).modulo = (rep(y)).modulo => false;
    (rep(x - y)).val = 0;
  }
  (x:%) + (y:%):% == {
          reduce((rep(x)).val +$R (rep(y)).val,
              newmodulo((rep(x)).modulo,(rep(y)).modulo))
  }

  (x:%) - (y:%):% == {
          reduce((rep(x)).val -$R (rep(y)).val,
              newmodulo((rep(x)).modulo,(rep(y)).modulo))
  }

  - (x:%):% == reduce(-$R (rep(x)).val,(rep(x)).modulo);
  (x:%) * (y:%):% == {
          reduce((rep(x)).val *$R (rep(y)).val,
              newmodulo((rep(x)).modulo,(rep(y)).modulo))
  }

  exQuo(x:%,y:%):Partial(%) == {
    xm := (rep(x)).modulo;
    if not (xm =$Mod (rep(y)).modulo) then
        xm := newmodulo(xm,(rep(y)).modulo);
    r := exactQuo((rep(x)).val,(rep(y)).val,xm);
    r case "failed" => failed;
    per([r::R,xm]$Rep)::Partial %
  }
  recip (x:%):Partial(%) == {
    r := exactQuo(1$R,(rep(x)).val,(rep(x)).modulo);
    r case "failed" => failed;
    per([r::R,(rep(x)).modulo]$Rep)::Partial %
  }
  inv (x:%):% == {
    (u := recip x) case "failed" => error("not invertible");
    u::%
  };

UnivariatePolynomialCategory(S:CommutativeRing):Category == with
   CommutativeRing
   degree: % -> Integer
   leadingCoefficient: % -> S
   reductum: % -> %
   monicDivide: (%, %) -> Record(quotient: %, remainder: %)
   coerce: S -> %

EuclideanDomain:Category == with
   CommutativeRing
   QuotientRemainder
   GcdDomain

EuclideanModularRing(S:CommutativeRing,R:UnivariatePolynomialCategory S,
         Mod:AbelianMonoid,reduction:(R,Mod) -> R,
           merge:(Mod,Mod) -> Partial Mod,
            exactQuo : (R,R,Mod) -> Partial(R)): EuclideanDomain with
                modulus :   %     -> Mod
                coerce  :   %     -> R
                reduce  : (R,Mod) -> %
                exQuo   :  (%,%)  -> Partial(%)
                recip   :    %    -> Partial(%)
                inv     :    %    -> %
--                if S has IntegerNumberSystem then
--                     frobenius: % -> %

 == ModularRing(R,Mod,reduction,merge,exactQuo) add

    --representation
      Rep ==> Record(val:R,modulo:Mod)
      import from Rep
      import from Mod
      import from S
      import from Partial(Mod)

    --declarations
      default x,y,z: %

      divide(x:%,y:%):Record(quotient:%, remainder:%) ==
        t:=merge(rep(x).modulo,rep(y).modulo)
        t case "failed" => error "incompatible moduli"
        xm:=t::Mod
        yv:=rep(y).val
        local invlcy:R
        if one? leadingCoefficient yv then invlcy:=1
        else
          invlcy:=rep(inv reduce((leadingCoefficient yv)::R,xm)).val
          yv:=reduction(invlcy*yv,xm)
        r:=monicDivide(rep(x).val,yv)
        [reduce(invlcy*r.quotient,xm),reduce(r.remainder,xm)]

      (x:%) rem (y:%):%  ==
        t:=merge(x.modulo,y.modulo)
        t case "failed" => error "incompatible moduli"
        xm:=t::Mod
        yv:=rep(y).val
        local invlcy:R
        if not one? leadingCoefficient yv then
          invlcy:=rep(inv reduce((leadingCoefficient yv)::R,xm)).val
          yv:=reduction(invlcy*yv,xm)
        r:=monicDivide(rep(x).val,yv)
        reduce(r.remainder,xm)

      euclideanSize(x:%):Integer == degree( rep(x).val)

      unitCanonical(x:%):% ==
        zero? x => x
        degree(rep(x).val) = 0 => 1
        one? leadingCoefficient(rep(x).val) => x
        invlcx:%:=inv reduce((leadingCoefficient(rep(x).val))::R,rep(x).modulo)
        invlcx * x

      unitNormal(x:%):Record(unit:%, corr:%, assoc:%)  ==
        zero?(x) or one?(leadingCoefficient(rep(x).val)) => [1, x, 1]
        lcx := reduce((leadingCoefficient(rep(x).val))::R,rep(x).modulo)
        invlcx:=inv lcx
        degree(rep(x).val) = 0 => [lcx, 1, invlcx]
        [lcx, invlcx * x, invlcx]
--      if S has IntegerNumberSystem then
--         frobenius x ==
--            zero?(m:=x.modulo) => x
--            e := convert(m)@Integer::PositiveInteger
--            xp := monomial(1,e)

--)abb domain MODFIELD ModularField

ModularField(R:CommutativeRing,Mod:AbelianMonoid,
          reduction:(R,Mod) -> R,
               merge:(Mod,Mod) -> Partial(Mod),
                      exactQuo : (R,R,Mod) -> Partial(R)): Field with
                modulus :   %     -> Mod
                coerce  :   %     -> R
                reduce  : (R,Mod) -> %
                exQuo   :  (%,%)  -> Partial(%)

  == ModularRing(R,Mod,reduction,merge,exactQuo) add
 
