
// The basic container for multivariates
RuleBase("MultiNomial",{vars,terms});

// using the sparse tree driver for multivariate polynomials
//Use("multivar.rep/sparsenomial.ys");
//Use("multivar.rep/partialdensenomial.ys");

If(IsBound(MultiNomialDriver),
  `Use(@MultiNomialDriver),
  Use("multivar.rep/sparsenomial.ys"));

// Code that can build the internal representation of a multivariate polynomial
Use("multivar.rep/makemulti.ys");


MM(_expr) <--  MM(expr,MultiExpressionList(expr));
MM(_expr,_vars) <--  MakeMultiNomial(expr,vars);

MultiSimp(_expr) <--
[
  Local(vars);
  vars:=MultiExpressionList(expr);
//Echo({"step1 ",MM(expr,vars)});
  MultiSimp2(MM(expr,vars));
];

10 # MultiSimp2(_a / _b) <--
[
  Local(c1,c2,gcd,cmn,vars);


  c1 := MultiContentTerm(a);
  c2 := MultiContentTerm(b);
  gcd:=If(IsRational(c1[2]) And IsRational(c2[2]), Gcd(c1[2],c2[2]), 1);
  c1[2] := c1[2]/gcd;
  c2[2] := c2[2]/gcd;

  cmn:=Min(c1[1],c2[1]);
  c1[1] := c1[1] - cmn;
  c2[1] := c2[1] - cmn;

  vars:=MultiVars(a);
  Check(vars = MultiVars(b),"incompatible Multivars to simplify");

  (NormalForm(CreateTerm(vars,c1))/NormalForm(CreateTerm(vars,c2)))
    *(NormalForm(MultiPrimitivePart(a))/NormalForm(MultiPrimitivePart(b)));
];

20 # MultiSimp2(expr_IsMulti) <--
[
  NormalForm(MultiContent(expr))*NormalForm(MultiPrimitivePart(expr));
];
30 # MultiSimp2(_expr) <-- expr;

MultiContent(multi_IsMulti)
<--
[
  Local(least,gcd);
  Set(least, MultiDegree(multi));
  Set(gcd,MultiLeadingCoef(multi));
  ScanMultiNomial("MultiContentScan",multi);
  CreateTerm(MultiVars(multi),MultiContentTerm(multi));
];

MultiContentTerm(multi_IsMulti)
<--
[
  Local(least,gcd);
  Set(least, MultiDegree(multi));
  Set(gcd,MultiLeadingCoef(multi));
  ScanMultiNomial("MultiContentScan",multi);
  {least,gcd};
];

MultiContentScan(_coefs,_fact) <--
[
  Set(least,Min({least,coefs}));
  Set(gcd,If(IsRational(gcd) And IsRational(fact), Gcd(gcd,fact), 1));
];
UnFence("MultiContentScan",2);

MultiPrimitivePart(MultiNomial(vars_IsList,_terms))
<--
[
  Local(cont);
  Set(cont,MultiContentTerm(MultiNomial(vars,terms)));
  Set(cont,CreateTerm(vars,{-cont[1],1/(cont[2])}));
  MultiNomialMultiply(MultiNomial(vars,terms), cont);
];

10 # MultiRemoveGcd(x_IsMulti/y_IsMulti) <--
[
  Local(gcd);
  Set(gcd,MultiGcd(x,y));
  Set(x,MultiDivide(x,{gcd})[1][1]);
  Set(y,MultiDivide(y,{gcd})[1][1]);
  x/y;
];
20 # MultiRemoveGcd(_x) <-- x;



5 # MultiDegree(MultiNomial(_vars,_term))_(Not(IsList(term))) <-- {};
10 # MultiDegree(MultiNomial(_vars,{})) <-- FillList(-Infinity,Length(vars));
20 # MultiDegree(MultiNomial(_vars,_terms))
   <-- (MultiLeadingTerm(MultiNomial(vars,terms))[1]);


10 # MultiLeadingCoef(MultiNomial(_vars,_terms))
   <-- (MultiLeadingTerm(MultiNomial(vars,terms))[2]);

10 # MultiLeadingMono(MultiNomial(_vars,{})) <-- 0;
20 # MultiLeadingMono(MultiNomial(_vars,_terms))
   <-- Multiply(vars^(MultiDegree(MultiNomial(vars,terms))));

20 # MultiLeadingTerm(_m) <-- MultiLeadingCoef(m) * MultiLeadingMono(m);

MultiVars(MultiNomial(_vars,_terms)) <-- vars;

20 # MultiLT(multi_IsMulti)
   <-- CreateTerm(MultiVars(multi),MultiLeadingTerm(multi));


/*************************************************************
  MultiDivide :
  input
    f - a multivariate polynomial
    g[1 .. n] - a list of polynomials to divide by
  output
    {q[1 .. n],r} such that f = q[1]*g[1] + ... + q[n]*g[n] + r

  Basically quotient and remainder after division by a group of
  polynomials.
**************************************************************/
20 # MultiDivide(_f,g_IsList) <--
[
  Local(i,v,q,r,nr);
  v:=MultiExpressionList(f+Sum(g));
  f:=MakeMultiNomial(f,v);
  nr := Length(g);
  For(i:=1,i<=nr,i++)
  [
    g[i] := MakeMultiNomial(g[i],v);
  ];
  {q,r}:=MultiDivide(f,g);
  q:=MapSingle("NormalForm",q);
  r:=NormalForm(r);
  {q,r};
];

10 # MultiDivide(f_IsMulti,g_IsList) <--
[
  Local(i,nr,q,r,p,v,finished);
  Set(nr, Length(g));
  Set(v, MultiVars(f));
  Set(q, FillList(0,nr));
  Set(r, 0);
  Set(p, f);
  Set(finished,MultiZero(p));
  Local(plt,glt);
  While (Not finished)
  [
    Set(plt, MultiLT(p));
    For(i:=1,i<=nr,i++)
    [
      Set(glt, MultiLT(g[i]));

      if (MultiDegree(glt) = MultiDegree(plt) Or MultiTermLess({MultiDegree(glt),1}, {MultiDegree(plt),1}))
      if (Select({{n},n<0},MultiDegree(plt)-MultiDegree(glt)) = {})
      [
        Local(ff);
      Set(ff, CreateTerm(v,{MultiDegree(plt)-MultiDegree(glt),MultiLeadingCoef(plt)/MultiLeadingCoef(glt)}));
        q[i] := q[i] + ff;
        Local(ltbefore,ltafter);
        Set(ltbefore,MultiLeadingTerm(p));
//        Echo(ltbefore,MultiLeadingTerm(p));
        Set(p, p - ff*g[i]);
        Set(ltafter,MultiLeadingTerm(p));
//        Echo(ltbefore,MultiLeadingTerm(p));
        if (ltbefore[1] = ltafter[1])
        [
          Set(ltafter,MultiLT(p));
          Set(p,p-ltafter);
        ];
//        Echo(ltbefore,MultiLeadingTerm(p));
        Set(i,nr+2);
      ];
    ];

    If (i = nr+1,
    [
      Set(r, r + LocalSymbols(a,b)(Subst(a,b)plt));
      Set(p,  p - LocalSymbols(a,b)(Subst(a,b)plt));
    ]);
//Echo(p);
    Set(finished,MultiZero(p));
  ];
  {q,r};
];


//TODO optimize this! keeps on converting to and from internal format!

10 # MultiGcd( 0,_g) <-- g;
10 # MultiGcd(_f, 0) <-- f;

20 # MultiGcd(_f,_g) <--
[
  Local(v);
  v:=MultiExpressionList(f+g);  //hier
  NormalForm(MultiGcd(MakeMultiNomial(f,v),MakeMultiNomial(g,v)));
];


5 # MultiGcd(f_IsMulti,g_IsMulti)_(MultiTermLess({MultiDegree(f),1},{MultiDegree(g),1})) <--
[
//Echo("lesser");
  MultiGcd(g,f);
];

5  # MultiGcd(MultiNomial(_vars,_terms),g_IsMulti)_(MultiDegree(MultiNomial(vars,terms)) = MultiDegree(g))
     <-- CreateTerm(vars,{FillList(0,Length(vars)),1});

5  # MultiGcd(MultiNomial(_vars,_terms),g_IsMulti)_(Select({{n},n<0},MultiDegree(MultiNomial(vars,terms))-MultiDegree(g)) != {})
     <-- CreateTerm(vars,{FillList(0,Length(vars)),1});

5  # MultiGcd(MultiNomial(_vars,_terms),g_IsMulti)_(NormalForm(g) = 0)
     <-- CreateTerm(vars,{FillList(0,Length(vars)),1});
10 # MultiGcd(f_IsMulti,g_IsMulti) <--
[
  LocalSymbols(a)
  [
    Set(f,Subst(a,a)f);
    Set(g,Subst(a,a)g);
  ];
  Local(new);
  While(g != 0)
  [
//Echo("before f",f,NormalForm(f));
//Echo("before g",g,NormalForm(g));
    Set(new, MultiDivide(f,{g}));
//Echo("new g",NormalForm(new[1][1]),NormalForm(new[2]));
If(new[1][1]=0,
[
  g:=MakeMultiNomial(1,MultiVars(f));
//Echo("PRIM ",MultiPrimitivePart(g));
  new[2]:=0;
]);
    Set(new, new[2]);
    Set(f,g);
    Set(g,new);

//Echo("after f",f,NormalForm(f));
//Echo("after g",g,NormalForm(g));
  ];
  MultiPrimitivePart(f);
];



MultiDivTerm(MultiNomial(_vars,_term1),MultiNomial(_vars,_term2)) <--
[
  Local(lm1,lm2);
  Set(lm1,MultiLeadingTerm(MultiNomial(vars,term1)) );
  Set(lm2,MultiLeadingTerm(MultiNomial(vars,term2)) );
  CreateTerm(vars,{lm1[1]-lm2[1],lm1[2] / lm2[2]});
];
MultiS(_g,_h,MultiNomial(_vars,_terms)) <--
[
  Local(gamma);

  gamma :=Max(MultiDegree(g),MultiDegree(h));
  Local(result,topterm);
  topterm := MM(Multiply(vars^gamma), vars);

  result :=
    MultiDivTerm(topterm,MultiLT(g))*g -
    MultiDivTerm(topterm,MultiLT(h))*h;

  result;
];


/*
  Groebner : Calculate the Groebner basis of a set of polynomials.
  Nice example of its power is

In> TableForm(Groebner({x*(y-1),y*(x-1)}))
 x*y-x
 x*y-y
 y-x
 y^2-y
In> Factor(y^2-y)
Out> y*(y-1);

From which you can see that x = y, and x^2 = x so x is 0 or 1.

*/

Groebner(f_IsList) <--
[
  Local(vars,i,j,S,nr,r);
  nr:=Length(f);
  vars:=VarList(f);
  For(i:=1,i<=nr,i++)
  [
    f[i] := MakeMultiNomial(f[i],vars);
  ];
  S:={};
  For(i:=1,i<nr,i++)
  For(j:=i+1,j<=nr,j++)
  [
    r := (MultiDivide(MultiS(f[i],f[j],f[i]),f)[2]);
    If(NormalForm(r) != 0, S:= r:S);
    f:=Concat(f,S);
    S:={};
    nr:=Length(f);
  ];
  MapSingle("NormalForm",Concat(f));
];
