#-------------------------------------------------------------------------------
# SYNOPSIS :    : Computation of indefinite hyperelliptic integrals.
#                 Reduction of poles at infinity and computation of the
#                 logarithmic part using a combination of Trager algorithm and
#                 Cantor arithmetic in the Jacobian of hyperelliptic curves.
#                 The integral to compute must be in the form:
#                                          /
#                                          |(p(x)/q(x)y)dx
#                                          /
#                 where y^2=f(x) defines the hyperelliptic curve.
#                 f and q must be squarefree polynomials, we must have
#                 gcd(q,f)=gcd(p,q)=1 and degree(f)=2g+1 (odd). We suppose that
#                 the algebraic part corresponding to the multiple finite poles
#                 is already computed (nothing new here).
#
# Input :     h : The integrand. It must be as above. 
#
#             x : The integration variable.
#
# Output:       : An antiderivative if there exists one. When there is no
#                 antiderivative, simplifications can be made in the returned
#                 integral (Computation of the algebraic part at infinity,
#                 simplification of poles when it is possible).
#
# Note:         : The case with parameters is not implemented (Problem with the
#                 computation of the basis of the residues).
#                 We don't verify the integrand is in the good form because
#                 such a function should be included in the more general
#                 integration algorithm.
#
#                                                              L.Bertrand 12/93
#                                                      e-mail: lbertran@cict.fr

macro(m_hyperint = (hyperint)):

hint:=proc(h,x)
          local p,q,f,rof,ground,fun,z,a;
          p:=numer(h);
          q:=denom(h);
          q:=convert(q,'RootOf');
          rof:=indets(q,'RootOf');
          fun := select(has,rof,x);
          ground := rof minus fun;
          if nops(fun)=0 then RETURN(int(h,x)) fi;
          if nops(fun)>1 then ERROR(`Not a single hyperelliptic function`) fi;
	  fun := fun[1];

# Replace the _Z by local name z so we can compute the degree, coeff in z etc.
a := frontend(subs,[_Z=z,op(1,fun)],[{`+`,`*`,`=`},{}]); 
if not type(a,polynom(ratpoly(rational,x),z)) then
    ERROR(`Not a single hyperelliptic function`) fi;
if degree(a,z) <> 2 then ERROR(`Not a single hyperelliptic function`) fi;
if coeff(a,z,1) <> 0 then ERROR(`Not a single hyperelliptic function`) fi;
if not type(p,polynom(anything,x)) then
    ERROR(`Numerator must be a polynomial in x`) fi;
if not type(q,polynom(polynom(anything,x),fun)) then
    ERROR(`Denominator is of the wrong form`) fi;
if degree(q,fun) > 1 then
    ERROR(`Denominator is of the wrong form`) fi;
q := collect(q,fun);
if coeff(q,fun,0) <> 0 then
    ERROR(`Denominator is of the wrong form`) fi;
if not type([p,q],'list'(polynom(rational,[x,fun]))) then
    ERROR(`Sorry: implemented only for coefficient field K = Q`) fi;

          f:=-coeff(a,z,0)/coeff(a,z,2);
          q:=coeff(q,fun,1);
          if degree(gcd(q,diff(q,x)),x)<>0 then 
                                    ERROR(`denominator should be squarefree`)
          fi;
          if gcd(q,f)<>1 then ERROR(`denominator should be prime to f`) fi;
          m_hyperint(p,q,f,x);
end:


#-------------------------------------------------------------------------------
# SYNOPSIS :    : Computation of indefinite hyperelliptic integrals.
#                 Reduction of poles at infinity and computation of the
#                 logarithmic part using a combination of Trager algorithm and
#                 Cantor arithmetic in the Jacobian of hyperelliptic curves.
#                 The integral to compute must be in the form:
#                                          /
#                                          |(p(x)/q(x)y)dx
#                                          /
#                 where y^2=f(x) defines the hyperelliptic curve.
#                 f and q must be squarefree polynomials, we must have
#                 gcd(q,f)=gcd(p,q)=1 and degree(f)=2g+1 (odd). We suppose that 
#                 the algebraic part corresponding to the multiple finite poles 
#                 is already computed (nothing new here).
#
# Input :     p : The numerator of the differential: a univariate polynomial in
#                 x.
#
#             q : The denominator of the differential: a univariate squarefree
#                 polynomial in x.
#
#             f : The defining polynomial of the curve: a squarefree univariate
#                 polynomial in x of odd degree prime to q.
#
#             x : The integration variable.
#
# Output:       : An antiderivative if there exists one. When there is no
#                 antiderivative, simplifications can be made in the returned
#                 integral (Computation of the algebraic part at infinity,
#                 simplification of poles when it is possible).
#
# Note:         : The case with parameters is not implemented (Problem with the
#                 computation of the basis of the residues).
#                 We don't verify the integrand is in the good form because
#                 such a function should be included in the more general
#                 integration algorithm.
#
#                                                              L.Bertrand 12/93
macro(m_polyresidues1 = (polyresidues1)):
macro(m_resbasis = (resbasis)):
macro(m_divtoresidues = (divtoresidues)):
macro(m_construcdivi = (construcdivi)):
macro(m_ordredivi = (ordredivi)):
macro(m_ntimesdivi = (ntimesdivi)):
macro(m_isprincipal = (isprincipal)):
macro(m_verif = (verif)):
macro(m_logparthyperel1 = (logparthyperel1)):
macro(m_diviaddition2 = (diviaddition2)):
macro(m_diviaddition2mod = (diviaddition2mod)):
macro(m_oppositedivi = (oppositedivi)):
macro(m_divireduction = (divireduction)):
macro(m_divireductionmod = (divireductionmod)):
macro(m_divtoresidue = (divtoresidue)):
macro(m_goodprime = (goodprime)):
macro(m_ordredivimod = (ordredivimod)):
macro(m_coeffs2 = (coeffs2)):

hyperint:=proc(p,q,f,x)
          local g,T,S,algpart,B;
          g:=(degree(f,x)-1)/2;
          userinfo(1,hint,`Starting reduction at infinity at time`,time());
#
# Hermite reduction at infinity.
#
          T:=quo(p,q,x);
          S:=rem(p,q,x);
          algpart:=0;
          while degree(T,x)>=2*g do
             userinfo(3,hint,`One step of Hermite reduction at infinity`);
             B:=2*lcoeff(expand(T),x)*x^(degree(T,x)-2*g)/lcoeff(f,x)
                /(2*degree(T)-2*g+1);
             algpart:=algpart+B;
             T:=expand((2*T-2*diff(B,x)*f-diff(f,x)*B)/2);
             od;
          userinfo(1,hint,`End of reduction at infinity at time`,time());
#
# Test of non-elementarity and computation of the logarithmic part when it is
# required.
#
          if degree(T,x)>=g then
           userinfo(1,hint,`Not elementary because pole of order>=2`);
           algpart*RootOf(_Z^2-f,_Z)+'int'(normal(T*q+S)/q/RootOf(_Z^2-f,_Z),x);
           else algpart*RootOf(_Z^2-f,_Z)+m_logparthyperel1(T*q+S,q,f,x);
          fi;
end:

#-------------------------------------------------------------------------------
# SYNOPSIS :    : Computation of indefinite hyperelliptic integrals.
#                 Computation of the logarithmic part using a combination of
#                 Trager algorithm and Cantor arithmetic in the Jacobian of
#                 hyperelliptic curves.
#                 The integral to compute must be in the form:
#                                          /
#                                          |(p(x)/q(x)y)dx
#                                          /
#                 where y^2=p0(x) defines the hyperelliptic curve.
#                 p0 and q must be squarefree polynomials, we must have
#                 gcd(q,p0)=1, degree(p0)=2g+1 (odd) and degree(p)-degree(q)<g.
#
# Input :     p : The numerator of the differential: a univariate polynomial.
#
#             q : The denominator of the differential: a univariate squarefree
#                 polynomial.
#
#            p0 : The defining polynomial of the curve: a squarefree univariate
#                 polynomial of odd degree prime to q.
#
#             x : The integration variable.
#
# Output:       : An antiderivative if there exists one. When there is no
#                 antiderivative, simplifications can be made in the returned
#                 integral (simplification of poles when it is possible).
#

logparthyperel1:=proc(p,q,p0,x)
                local f,i,j,y,z,g,O,R,base,M,H,V,residu,D,nbres,d,F,mul;
#
# Computation of the genus.
#
                g:=(degree(p0,x)-1)/2;
                if p=0 then RETURN(0);fi;
                userinfo(2,hint,`The genus is`,g);
                userinfo(1,hint,`Starting computation of logarithmic part`,
                                     `at time`,time());
#
# Computation of the residues polynomial.
#
                R:=m_polyresidues1(p,q,p0,x,z);
                if degree(R)=0 then
                               userinfo(1,hint,`Not elementary, no poles`);
                               RETURN('int'(p/q/RootOf(_Z^2-p0,_Z),x));
                               fi;
                userinfo(1,hint,`Starting computation of residues`,time());
                userinfo(3,hint,`Residues are roots of`,R);
#
# Computation of the basis of the residues.
#
                M:=m_resbasis(R,z,{},base,mul);
                for j from 1 to linalg[coldim](M) do
                    residu[j]:=0;
                    for i from 1 to linalg[rowdim](M) do
                        residu[j]:=residu[j]+M[i,j]*base[i];
                    od;
                od;
                nbres:=linalg[coldim](M);
                userinfo(3,hint,`Nber of distinct residues is`,nbres);
                userinfo(3,hint,`Distinct residues are`,eval(residu));
                userinfo(3,hint,`Residues basis is`,eval(base));
                userinfo(1,hint,`End of computation of residues`,time());
                userinfo(1,hint,`Computing divisors corresponding to the`,
                                    `basis of the residues`,time());
#
# Computation of divisors corresponding to each residue
#
                H:=m_divtoresidues(p,q,p0,g,residu,nbres,x,y);
#
# Computation of divisors corresponding to the basis of the residues.
#
                D:=m_construcdivi(p,q,p0,g,H,M,nbres,x,y);
                userinfo(1,hint,`Divisors computed at time`,time());
                userinfo(1,hint,`Computing the order of divisors`,time());
#
# Computation of the orders of the divisors.
#
                O:=m_ordredivi(D,p0,g,M,x);
                userinfo(1,hint,`Order of divisors computed`,time());
#
# Tests of principality and computation of the candidate for an antiderivative
# if all the divisors are of finite order.
#
                if O='infini' then
                userinfo(1,hint,`Not elementary,divisor of infinite order`);
                                  RETURN('int'(p/q/RootOf(_Z^2-p0,_Z),x));
                fi;
                for i from 1 to linalg[rowdim](M) do
                      d:=m_ntimesdivi(D[i],O[i],p0,g,x,y);
                      if m_isprincipal(d) then f[i]:=op(3,d);
                         else userinfo(1,hint,`Divisor not principal`);
                             RETURN('int'(p/q/RootOf(_Z^2-p0,_Z),x));
                      fi
                      od;
                F:=0;
                for i from 1 to linalg[rowdim](M) do
                F:=F+base[i]/O[i]*log(f[i])
                od;
                userinfo(3,hint,`The computed function is`,F);
#
# Verification of the candidate.
#
                V:=m_verif(p,q,p0,F,x,y);
                if V=0 then
                   userinfo(1,hint,`Logarithmic part computed at`,time());
                   subs(y=RootOf(_Z^2-p0,_Z),F);
                else
                   userinfo(1,hint,`Not elementary, the computed function`,
                            `is not an antiderivative. There is a differential`,
                            `of the first kind.`);
                   userinfo(1,hint,`Logarithmic part computed at`,time());
                   subs(y=RootOf(_Z^2-p0,_Z),F)-'int'(V,x);
     fi;
end:

#-------------------------------------------------------------------------------
# SYNOPSIS :    : Computation of indefinite hyperelliptic integrals.
#                 Construction of divisors corresponding to the basis of the
#                 residues.
#
# Input :     p : The numerator of the differential: a univariate polynomial.
#             q : The denominator of the differential: a univariate squarefree
#                 polynomial.
#            p0 : The defining polynomial of the curve: a squarefree univariate
#                 polynomial of odd degree prime to q.
#             g : The genus of the curve.
#             H : A table containing the divisors corresponding to each residue.
#             M : The matrix of decomposition of the residues in the basis.
#         nbres : The number of residues.
#             x : The integration variable.
#             y : The variable representing RootOf(_Z^2-p0(x)).
#
# Output:       : A table containing the divisors corresponding to the basis
#                 of the residues.
# Representation: A divisor D=sum(n_iP_i) -sum(n_i)P_infinity + div(h)
#                 is represented by [A,B,h] where A(x)=sum(x-x_i),
#                 deg(B(x))<deg(A(x)) and valuation_P_i(y-B)>=n_i.
#

construcdivi:=proc(p,q,p0,g,H,M,nbres,x,y)
                  local i,j,D;
      userinfo(2,hint,`Computation of divisors corresponding to the basis`);
      for i from 1 to linalg[rowdim](M) do
          D[i]:=[1,0,1];
          for j from 1 to nbres do
              if M[i,j]=0 then
              else if signum(M[i,j])=1 then
                   D[i]:=m_diviaddition2(D[i],
                                  m_ntimesdivi(H[j],M[i,j],p0,g,x,y),p0,g,x,y);
                   else D[i]:=m_diviaddition2(D[i],m_ntimesdivi(
                              m_oppositedivi(H[j]),-M[i,j],p0,g,x,y),p0,g,x,y);
                   fi;
              fi;
          od;
      od;
      D;
end:

#-------------------------------------------------------------------------------
# SYNOPSIS :    : Computation of indefinite hyperelliptic integrals.
#                 Computation of the sum of two divisors D1+D2.
#
# Input : D1,D2 : The two divisors to sum.
#            p0 : The defining polynomial of the curve: a squarefree univariate
#                 polynomial of odd degree prime to q.
#             g : The genus of the curve.
#             x : The integration variable.
#             y : The variable representing RootOf(_Z^2-p0(x)).
#
# Output:       : The sum of the two divisors D1+D2. (reduced)
#
# Representation: A divisor D=sum(n_iP_i) -sum(n_i)P_infinity + div(h)
#                 is represented by [A,B,h] where A(x)=sum(x-x_i),
#                 deg(B(x))<deg(A(x)) and valuation_P_i(y-B)>=n_i.
#
#
#

diviaddition2:=proc(D1,D2,p0,g,x,y)
              local A1,B1,h1,A2,B2,h2,d0,d,h1,h2,h3,alpha0,beta0,alpha1,beta1,A,
                    B,g1,g2;
              A1 := op(1,D1); B1 := op(2,D1); g1 := op(3,D1);
              A2 := op(1,D2); B2 := op(2,D2); g2 := op(3,D2);
              d0:=evala(Gcdex(A1,A2,x,alpha0,beta0));
              d:=evala(Gcdex(d0,B1+B2,x,alpha1,beta1));
              h1:=alpha0*alpha1; h2:=alpha1*beta0; h3:=beta1;
              A:=quo(A1*A2,d^2,x);
              B:=rem(quo(h1*A1*B2+h2*A2*B1+h3*(B1*B2+p0),d,x),A,x);
              m_divireduction([A,B,g1*g2*d],p0,g,x,y);
end:

#-------------------------------------------------------------------------------
# SYNOPSIS :    : Computation of indefinite hyperelliptic integrals.
#                 Computation of the sum of two divisors D1+D2 in the finite
#                 field at p (prime) elements.
#
# Input : D1,D2 : The two divisors to sum.
#            p0 : The defining polynomial of the curve: a squarefree univariate
#                 polynomial of odd degree prime to q.
#             g : The genus of the curve.
#             p : The prime defining the reduction.
#             x : The integration variable.
#
# Output:       : The sum of the two divisors D1+D2 in the finite field.
#
# Representation: A divisor D=sum(n_iP_i) -sum(n_i)P_infinity + div(h)
#                 is represented by [A,B,h] where A(x)=sum(x-x_i),
#                 deg(B(x))<deg(A(x)) and valuation_P_i(y-B)>=n_i.


diviaddition2mod:=proc(D1,D2,p0,g,p,x)
              local A1,B1,h1,A2,B2,h2,d0,d,h3,alpha0,beta0,alpha1,beta1,A,
                    B;
              A1 := op(1,D1) mod p; B1 := op(2,D1) mod p;
              A2 := op(1,D2) mod p; B2 := op(2,D2) mod p;
              d0:=Gcdex(A1,A2,x,'alpha0','beta0') mod p;
              d:=Gcdex(d0,B1+B2,x,'alpha1','beta1') mod p;
              h1:=alpha0*alpha1 mod p; h2:=alpha1*beta0 mod p; h3:=beta1;
              A:=Quo(A1*A2,d^2,x) mod p;
              B:=Rem(Quo(h1*A1*B2+h2*A2*B1+h3*(B1*B2+p0),d,x) mod p,A,x) mod p;
              m_divireductionmod([A,B],p0,g,p,x);
end:

#-------------------------------------------------------------------------------
# SYNOPSIS :    : Computation of indefinite hyperelliptic integrals.
#                 Computation of the reduced representative of a semi-reduced
#                 divisor.
#
# Input :     D : The divisor to reduce.
#            p0 : The defining polynomial of the curve: a squarefree univariate
#                 polynomial of odd degree prime to q.
#             g : The genus of the curve.
#             x : The integration variable.
#             y : The variable representing RootOf(_Z^2-p0(x)).
#
# Output:       : The reduced representative of D.
#
# Representation: A divisor D=sum(n_iP_i) -sum(n_i)P_infinity + div(h)
#                 is represented by [A,B,h] where A(x)=sum(x-x_i),
#                 deg(B(x))<deg(A(x)) and valuation_P_i(y-B)>=n_i.
#                 D is reduced if deg(A)<=g.

divireduction:=proc(D,p0,g,x,y)
               local A,B,B1,h  ;
               A:=op(1,D);
               B:=op(2,D);
               h:=op(3,D);
               while degree(A,x)>g do
                     A:=evala(quo(p0-B^2,A,x));
                     B1:=evala(rem(-B,A,x));
                     h:=h*(B-y)/A;
                     B:=B1;
               od;
               [A,B,h];
end:

#-------------------------------------------------------------------------------
# SYNOPSIS :    : Computation of indefinite hyperelliptic integrals.
#                 Computation of the reduced representative of a semi-reduced
#                 divisor in the finite field of p (prime) elements.
#
# Input :     D : The divisor to reduce.
#            p0 : The defining polynomial of the curve: a squarefree univariate
#                 polynomial of odd degree prime to q.
#             g : The genus of the curve.
#             p : The prime defining the reduction.
#             x : The integration variable.
#
# Output:       : The reduced representative of D in the finite field.
#
# Representation: A divisor D=sum(n_iP_i) -sum(n_i)P_infinity + div(h)
#                 is represented by [A,B,h] where A(x)=sum(x-x_i),
#                 deg(B(x))<deg(A(x)) and valuation_P_i(y-B)>=n_i.
#                 D is reduced if deg(A)<=g.

divireductionmod:=proc(D,p0,g,p,x)
           local A,B,B1;
           A:=op(1,D) mod p;
           B:=op(2,D) mod p;
           while degree(A)>g do
                 A:=Quo(p0-B^2,A,x) mod p;
                 B1:=Rem(-B,A,x) mod p;
                 B:=B1;
           od;
           [A,B];
end:

#-------------------------------------------------------------------------------
# SYNOPSIS :    : Computation of indefinite hyperelliptic integrals.
#                 Construction of the divisor corresponding to a residue.
#
# Input :     p : The numerator of the differential: a univariate polynomial.
#             q : The denominator of the differential: a univariate squarefree
#                 polynomial.
#            p0 : The defining polynomial of the curve: a squarefree univariate
#                 polynomial of odd degree prime to q.
#             g : The genus of the curve.
#             r : A residue.
#             x : The integration variable.
#             y : The variable representing RootOf(_Z^2-p0(x)).
#
# Output:       : The reduced divisor corresponding to the residue r.
#
# Representation: A divisor D=sum(n_iP_i) -sum(n_i)P_infinity + div(h)
#                 is represented by [A,B,h] where A(x)=sum(x-x_i),
#                 deg(B(x))<deg(A(x)) and valuation_P_i(y-B)>=n_i.

divtoresidue:=proc(p,q,p0,g,r,x,y)
          local A,B,qprime,alpha,s,t;
          userinfo(3,hint,`Computation of the divisor corresponding to a`,
                               `residu`);
          qprime:=diff(q,x);
          A:=evala(Gcdex(q,p^2-r^2*qprime^2*p0,x));
          evala(Gcdex(qprime,A,x,'s','t'));
          B:=evala(Rem(p*s/r,A,x));
          m_divireduction([A,B,1],p0,g,x,y);
end:

#-------------------------------------------------------------------------------
# SYNOPSIS :    : Computation of indefinite hyperelliptic integrals.
#                 Construction of divisors corresponding to a table of
#                 residues.
#
# Input :     p : The numerator of the differential: a univariate polynomial.
#             q : The denominator of the differential: a univariate squarefree
#                 polynomial.
#            p0 : The defining polynomial of the curve: a squarefree univariate
#                 polynomial of odd degree prime to q.
#             g : The genus of the curve.
#       residus : A table of residues.
#          nbre : The number of residues.
#             x : The integration variable.
#             y : The variable representing RootOf(_Z^2-p0(x)).
#
# Output:       : A table containing the divisors corresponding to the table of
#                 residues.
# Representation: A divisor D=sum(n_iP_i) -sum(n_i)P_infinity + div(h)
#                 is represented by [A,B,h] where A(x)=sum(x-x_i),
#                 deg(B(x))<deg(A(x)) and valuation_P_i(y-B)>=n_i.
#
#Computes the divisors corresponding to a table of residues (residus).

divtoresidues:=proc(p,q,p0,g,residus,nbre,x,y)
  local i,H;
  userinfo(3,hint,`Computation of divisors corresponding to each residue.`);
  for i from 1 to nbre do
      H[i]:=m_divtoresidue(p,q,p0,g,residus[i],x,y);
  od;
  H;
end:

#-------------------------------------------------------------------------------
# SYNOPSIS :    : Computation of indefinite hyperelliptic integrals.
#                 Computation of the first p with good reduction after prime1.
#
# Input :     d : A divisor.
#            p0 : The defining polynomial of the curve: a squarefree univariate
#                 polynomial of odd degree prime to q.
#          poly : The defining polynomial of the RootOf defining the ground
#                 field.
#        prime1 : A prime.
#             x : The integration variable.
#
# Output:     i : The first p with good reduction after prime1.
#       newpoly : The defining polynomial of the new RootOf defining the ground
#                 field modulo i.
#
goodprime:=proc(d,p0,poly,prime1,x)
           local i,a,q,b,boo,newpoly,factorlist,fact,fact1,fact2;
           userinfo(1,hint,`Looking for a good prime.`);
           i:=prime1;
           a:=op(1,d); b:=op(2,d);
           if degree(a,x)=0 then [i,poly]
           else
           q:=p0-b^2;
#
# Tests for good reduction.
#
           while evala(rem(q,a,x))=0 do
                 q:=evala(quo(q,a,x));
                 od;
           q:=normal(q);
            while (traperror(a mod i)=lasterror or
                   traperror(b mod i)=lasterror or
                   traperror(q mod i)=lasterror or
                  lcoeff(p0,x) mod i = 0 or lcoeff(a,x) mod i = 0 or
                  Gcd(p0,diff(p0,x)) mod i <> 1 or Gcd(a,q) mod i <> 1)
                  do
                  i:=nextprime(i);
           od;
#
# Computation of defining polynomial of the new RootOf, when the previous is
# not irreducible modulo i.
#
           if poly=0 then [i,poly];
           else
           newpoly:=poly;
           boo:=Irreduc(poly) mod i;
           if boo='false' then
                           factorlist:=op(2,Factors(poly) mod i) ;
                           factorlist:=map(l->op(1,l),factorlist);
                           newpoly:=op(1,factorlist);
                           for fact in factorlist do
                               if degree(fact)<degree(newpoly) then
                                                                  newpoly:=fact;
                               fi;
                           od;
           fi; # irreducibility test.
           [i,newpoly];
           fi; # test on poly.
           fi; # test on degree(a).
end:

#-------------------------------------------------------------------------------
# SYNOPSIS :    : Computation of indefinite hyperelliptic integrals.
#                 Principality test for a reduced divisor.
#
# Input :     D : The reduced divisor to test.
#
# Output:       : A boolean: if D is principal then true else false.
#
# Representation: A divisor D=sum(n_iP_i) -sum(n_i)P_infinity + div(h)
#                 is represented by [A,B,h] where A(x)=sum(x-x_i),
#                 deg(B(x))<deg(A(x)) and valuation_P_i(y-B)>=n_i.
#                 D is reduced if deg(A)<=g.
isprincipal:=proc(D)
             userinfo(3,hint,`Principality test`);
             evalb(degree(op(1,D))=0);
end:

#-------------------------------------------------------------------------------
# SYNOPSIS :    : Computation of indefinite hyperelliptic integrals.
#                 Computation of the divisor equal to n-times a given divisor D.
#
# Input :     D : The given divisor.
#             n : A positive integer.
#            p0 : The defining polynomial of the curve: a squarefree univariate
#                 polynomial of odd degree prime to q.
#             g : The genus of the curve.
#             x : The integration variable.
#             y : The variable representing RootOf(_Z^2-p0(x)).
#
# Output:       : The reduced representative of the divisor nD.
#
# Representation: A divisor D=sum(n_iP_i) -sum(n_i)P_infinity + div(h)
#                 is represented by [A,B,h] where A(x)=sum(x-x_i),
#                 deg(B(x))<deg(A(x)) and valuation_P_i(y-B)>=n_i.
#                 D is reduced if deg(A)<=g.

ntimesdivi:=proc(D,n,p0,g,x,y)
    local i,D1 ;
    D1:=[1,0,1];
    for i from 1 to n do
        D1:=m_diviaddition2(D,D1,p0,g,x,y);
    od;
    D1;
end:

#-------------------------------------------------------------------------------
# SYNOPSIS :    : Computation of indefinite hyperelliptic integrals.
#                 Computation of the negative of a divisor D.
#
# Input :     D : The given divisor.
#
# Output:       : The divisor -D.
#
# Representation: A divisor D=sum(n_iP_i) -sum(n_i)P_infinity + div(h)
#                 is represented by [A,B,h] where A(x)=sum(x-x_i),
#                 deg(B(x))<deg(A(x)) and valuation_P_i(y-B)>=n_i.
#                 D is reduced if deg(A)<=g.

oppositedivi:=proc(D)
    local A,B,h;
    A := op(1,D);
    B := op(2,D);
    h := op(3,D);
    [A,-B,1/h/A];
end:

#-------------------------------------------------------------------------------
# SYNOPSIS :    : Computation of indefinite hyperelliptic integrals.
#                 Computation of the orders of a table of divisors.
#
# Input :     D : A table of divisors.
#            p0 : The defining polynomial of the curve: a squarefree univariate
#                 polynomial of odd degree prime to q.
#             g : The genus of the curve.
#             x : The integration variable.
#
# Output:       : The table of the orders of the divisors when they all are of
#                 finite order, 'infini' if there exists a divisor of infinite
#                 order.
#
# Representation: A divisor D=sum(n_iP_i) -sum(n_i)P_infinity + div(h)
#                 is represented by [A,B,h] where A(x)=sum(x-x_i),
#                 deg(B(x))<deg(A(x)) and valuation_P_i(y-B)>=n_i.
#                 D is reduced if deg(A)<=g.

ordredivi:=proc(D,p0,g,M,x)
           local i,d,j,k,p1,p2,o1,o2,O,a,b,rofa,rofb,rof,poly,newpoly1
                 ,newpoly2,d1,d2;
           userinfo(2,hint,`Computation of the orders of the divisors`);
           for i from 1 to linalg[rowdim](M) do
                 a:=op(1,D[i]); b:=op(2,D[i]); d:=[a,b];
#
# Searching for the RootOf defining the ground field and computation of a
# primitive element (No code available for imbricated RootOf modulo p).
#

                 rofa:=indets(a,'algext'); rofb:=indets(b,'algext');
                 rof:=rofa union rofb;
                 if rof <> {} then
                    poly:=evala(Primfield(rof));
                    d:=subs(op(2,poly),d);
                    poly:=op(lhs(op(op(1,poly))));
                    else poly:=0;
                 fi;
#
# Computation of a good prime.
#
                 p1:=m_goodprime(d,p0,poly,3,x);
                 newpoly1:=op(2,p1); p1:=op(1,p1);
                 userinfo(3,hint,`Good prime :`,p1);
#
# Computation of a second good prime.
#
                 p2:=m_goodprime(d,p0,poly,nextprime(p1),x);
                 newpoly2:=op(2,p2); p2:=op(1,p2);
                 userinfo(3,hint,`Good prime :`,p2);
                 d1:=d; d2:=d;
#
# Substitution of RootOfs when they are reducible modulo p1 or p2 and
# computation of the orders of the divisor in the finite fields defined by p1
# and p2.
#
                 if degree(newpoly1)<degree(poly) then
                    d1:=evala(subs(RootOf(poly)=RootOf(newpoly1),d1));
                 fi;
                 o1:=m_ordredivimod(d1,p0,g,p1,x);
                 userinfo(2,hint,`Order =`,o1,`modulo`,p1);
                 if degree(newpoly2)<degree(poly) then
                    d2:=evala(subs(RootOf(poly)=RootOf(newpoly2),d2));
                 fi;
                 o2:=m_ordredivimod(d2,p0,g,p2,x);
                 userinfo(2,hint,`Order =`,o2,`modulo`,p2);
#
# Verification of compatibility of the two orders modulo p1 and p2.
#
                 k:=0;
                 while gcd(o1,p2)=p2 do
                                     o1:=o1/p2;
                                     k:=k+1
                                     od;
                 j:=0;
                 while gcd(o2,p1)=p1 do
                                     o2:=o2/p1;
                                     j:=j+1
                                     od;
                if o1<>o2 then RETURN('infini');
                          userinfo(2,hint,`Infinite order.`);
                          else O[i]:=o1*p1^j*p2^k
                fi;
                userinfo(2,hint,`The order must be `,O[i]);
             od;
             O
end:

#-------------------------------------------------------------------------------
# SYNOPSIS :    : Computation of indefinite hyperelliptic integrals.
#                 Computation of the order of a divisor in a finite field.
#
# Input :     D : A  divisor.
#            p0 : The defining polynomial of the curve: a squarefree univariate
#                 polynomial of odd degree prime to q.
#             g : The genus of the curve.
#         prime : The prime defining the finite field.
#             x : The integration variable.
#
# Output:       : The order of the divisor in the finite field.
#
# Representation: A divisor D=sum(n_iP_i) -sum(n_i)P_infinity + div(h)
#                 is represented by [A,B,h] where A(x)=sum(x-x_i),
#                 deg(B(x))<deg(A(x)) and valuation_P_i(y-B)>=n_i.
#                 D is reduced if deg(A)<=g.

ordredivimod:=proc(D,p0,g,prime,x)
      local i,D0;
      userinfo(1,hint,`Computation of the order of a divisor modulo`,prime);
      i:=1;
      D0:=D;
      while not m_isprincipal(D0) do
            userinfo(3,hint,`Power`,i);
            D0:=m_diviaddition2mod(D0,D,p0,g,prime,x);
            i:=i+1;
            userinfo(3,hint,`D^`,i,`=`,D0);
      od;
      i;
end:

#-------------------------------------------------------------------------------
# SYNOPSIS :    : Computation of indefinite hyperelliptic integrals.
#                 Computation of the polynomial whose roots are the residues.
#
# Input :     p : The numerator of the differential: a univariate polynomial.
#             q : The denominator of the differential: a univariate squarefree
#                 polynomial.
#            p0 : The defining polynomial of the curve: a squarefree univariate
#                 polynomial of odd degree prime to q.
#             x : The integration variable.
#             z : The indeterminate of the polynomial.
#
# Output:       : The polynomial whose roots are the residues.

polyresidues1:=proc(p,q,p0,x,z)
         local y;
         userinfo(1,hint,`Computation of the polynomial for the residues.`);
         resultant(resultant(z*y*diff(q,x)-p,y^2-p0,y),q,x);
end:

#-------------------------------------------------------------------------------
# SYNOPSIS :    : Computation of indefinite hyperelliptic integrals.
#                 Computation of the basis of the residues.
#
# Input :     R1: The polynomial for the residues.
#             z : The indeterminate of the polynomial.
#        ground : A set of RootOf defining the ground field.
#
# Output:       : a matrix S where S[i,j] is the i-th coefficients of
#                 the j-th residue in the basis Zbasis.
#        Zbasis : a basis of the Z-module generated by the roots of R
#          mult : a list containing the multiplicities of the residues.
#
# Note  : R1 must be a polynomial with rational coefficients.

resbasis := proc(R1,z,ground,Zbasis1,mult)
   local R, M, N, T, Zbasis, c, c1, i, j, m, n, t, coord, den, fl,
         Zgenerators, newm, residue, rofs, tm, num;
   if degree(R1,z) = 0 then
      ERROR(`degree should be positive`);
   elif not type(R1,'polynom'('algnum',z)) then
      RETURN('FAIL'); # not implemented yet
   fi;
   R := normal(R1/z^ldegree(R1,z));
   userinfo(2,{'hint'},`computing a splitting field at time`, time());
   userinfo(3,{'hint'},`polynomial has degree`,degree(R,z));
   fl := evala(AFactors(R))[2];
   userinfo(3,{'hint'},`splitting field computed at time`, time());
   mult := [seq(tm[2],tm=fl)];
   fl := [seq(tm[1],tm=fl)];
   n := nops(fl);
   userinfo(3,{'hint'}, `number of distinct residues`,n);
   rofs := [op(indets(fl,'RootOf'))];
   T := table();
   for i to n do
      residue := evala(Expand(-coeff(fl[i],z,0)));
      c1 := frontend(m_coeffs2,[residue,rofs],[{`+`,`*`,'list'}]);
      c := c1[1];
      t := c1[2];
      for j to nops(c) do
         if not assigned(T[t[j]]) then
            T[t[j]] := array('sparse',1..n);
         fi;
         T[t[j]][i] := c[j];
      od;
   od;
#
# Convert to matrices.
#
   Zgenerators := array(map(op,[indices(T)]));
   coord := map(op,[entries(T)]);
   M := array(map(convert,coord,'list'));
   m := linalg[vectdim](Zgenerators);
#
# Remove the lcm of the denominators (integer) and the gcd
# of the numerators.
#
   for i to m do
      den := ilcm(seq(denom(M[i,j]),j=1..n));
      num := igcd(seq(numer(M[i,j]),j=1..n));
      for j to n do M[i,j] := M[i,j] * den / num od;
      Zgenerators[i] := Zgenerators[i] * num / den;
   od;
   M := linalg[concat](M,linalg[diag](1$m));
   M := linalg[ihermite](M);
   N := linalg[inverse](linalg[submatrix](M,1..m,n+1..n+m));
   N := linalg[transpose](N);
   newm := linalg[rank](linalg[submatrix](M,1..m,1..n));
   M := linalg[submatrix](M,1..newm,1..n);
   N := linalg[submatrix](N,1..newm,1..m);
   Zbasis := evalm(N &* Zgenerators);
#
# Simplify
#
   for i to newm do
      num := igcd(seq(numer(M[i,j]),j=1..n));
      for j to n do M[i,j] := M[i,j] / num od;
      Zbasis[i] := Zbasis[i] * num;
   od;

   Zbasis1 := Zbasis;
   eval(M);

end:


coeffs2 := proc(residue, rofs)
   local t, c;
   c := coeffs(residue,rofs,'t');
   [[c],[t]];
end:


#-------------------------------------------------------------------------------
# SYNOPSIS :    : Computation of indefinite hyperelliptic integrals.
#                 Verification for the computed function to be an antiderivative
#
# Input :     p : The numerator of the differential: a univariate polynomial.
#             q : The denominator of the differential: a univariate squarefree
#                 polynomial.
#            p0 : The defining polynomial of the curve: a squarefree univariate
#                 polynomial of odd degree prime to q.
#             F : The computed function.
#             x : The integration variable.
#             y : The variable representing RootOf(_Z^2-p0(x)).
#
# Output:       : The difference between the derivative of the computed function
#                 and the initial function.

verif:=proc(p,q,p0,F,x,y)
       local F1,DF1;
       userinfo(3,hint,`Verification of the computed function`);
       F1:=subs(y=RootOf(_Z^2-p0,_Z),F);
       DF1:=diff(F1,x);
       evala(DF1-p/q/RootOf(_Z^2-p0,_Z))
end:

`help/text/hyperint`:=TEXT(
`   `,
`FUNCTION: hyperint - computes an indefinite hyperelliptic integral`,
`    `,
`CALLING SEQUENCE:`,
`   hyperint(p,q,f,x)`,
`    `,
`PARAMETERS:`,
`   p          - a univariate polynomial, the numerator of the integrand`,
`   q          - a univariate polynomial, the denominator of the integrand`,
`   f          - a squarefree univariate polynomial of odd degree`,
`   x          - a name, the integration variable`,
`     `,
`SYNOPSIS:   `,
`- Computation of indefinite hyperelliptic integrals.`,
`  Reduction of poles at infinity and computation of the logarithmic part using`
,
`  a combination of Trager algorithm and Cantor arithmetic in the Jacobian of `,
`  hyperelliptic curves.`,
`  `,
`- The integral to compute must be in the form:`,
`                                         /`,
`                                         |(p(x)/q(x)y)dx`,
`                                         /`,
`  where y^2=f(x) defines the hyperelliptic curve.  f and q must be squarefree`,
`  polynomials, we must have gcd(q,f)=1 and degree(f)=2g+1 (odd). We suppose `,
`  that the algebraic part corresponding to the multiple finite poles is `,
`  already computed (nothing new here).`,
`- It returns An antiderivative if there exists one. When there is no`,
`   antiderivative, simplifications can be made in the returned integral `,
`   (Computation of the algebraic part at infinity, simplification of poles `,
`   when it is possible).`,
`   `,
`EXAMPLES:   `,
`> hyperint(1,x,x^3+1,x);`,
`   `,
`               /               2   3     3                2   3     \\`,
`               | (1 - RootOf(_Z - x - 1))  (-1 - RootOf(_Z - x -1)) |`,
`        1/3 ln | -------------------------------------------------- |`,
`               |                          6                         |`,
`               \\                         x                          /`,
`  `,
`> alias(y=RootOf(_Z^2-x^5-x+1)):`,
`  `,
`> hyperint(5*x^9-5*x^6+9*x^5-5*x^4-x^2+1,x^5-x^2+x-1,x^5+x-1,x);`,
`  `,
`                                / -x - y  \\`,
`                        2y - ln |---------|`,
`                                \\  x - y  /`,
`  `,
`> alias(y=RootOf(_Z^2-t^3-1)):`,
`  `,
`> hyperint(t-1,t-2,t^3+1,t);`,
`   `,
`               /             3         \\   /`,
`               | (-1 + 2t -y)  (1 - y) |   |        y`,
`        1/9 ln | --------------------- | + | 2/3 -------- dt`,
`               |            3  3       |   |       3`,
`               \\     (t - 2)  t        /   /      t  + 1`,
`  `,
`SEE ALSO: int,hint`
):
`help/text/hint`:=TEXT(
`   `,
`FUNCTION: hint - computes an indefinite hyperelliptic integral`,
`    `,
`CALLING SEQUENCE:`,
`   hint(h,x)`,
`    `,
`PARAMETERS:`,
`   h          - an hyperelliptic function`, 
`   x          - a name, the integration variable`,
`     `,
`SYNOPSIS:   `,
`- Computation of indefinite hyperelliptic integrals.`,
`  Reduction of poles at infinity and computation of the logarithmic part using`
,
`  a combination of Trager algorithm and Cantor arithmetic in the Jacobian of `,
`  hyperelliptic curves.`,
`  `,
`- The integral to compute must be in the form:`,
`                                         /`,
`                                         |(p(x)/q(x)y)dx`,
`                                         /`,
`  where y^2=f(x) defines the hyperelliptic curve.  f and q must be squarefree`,
`  polynomials, we must have gcd(q,f)=1 and degree(f)=2g+1 (odd). We suppose `,
`  that the algebraic part corresponding to the multiple finite poles is `,
`  already computed (nothing new here).`,
`  `,
`- It returns an antiderivative if there exists one. When there is no`,
`   antiderivative, simplifications can be made in the returned integral `,
`   (Computation of the algebraic part at infinity, simplification of poles `,
`   when it is possible).`,
`   `,
`NOTE: `,
`- The case with parameters is not implemented (Problem with the computation of`,
`  the basis of the residues).  We don't verify the integrand is in the good `,
`  form because such a function should be included in the more general`,
`   integration algorithm.`,
`  `,
`- We can have informations using infolevel[hint].`,
`  `,
`EXAMPLES:   `,
`> hint(1/x/RootOf(_Z^2-x^3-1),x);`,
`   `,
`               /               2   3     3                2   3     \\`,
`               | (1 - RootOf(_Z - x - 1))  (-1 - RootOf(_Z - x -1)) |`,
`        1/3 ln | -------------------------------------------------- |`,
`               |                          6                         |`,
`               \\                         x                          /`,
`  `,
`> alias(y=RootOf(_Z^2-x^5-x+1)):`,
`  `,
`> hint((5*x^9-5*x^6+9*x^5-5*x^4-x^2+1)/(x^5-x^2+x-1)/y,x);`,
`  `,
`                                / -x - y  \\`,
`                        2y - ln |---------|`,
`                                \\  x - y  /`,
`  `,
`> alias(y=RootOf(_Z^2-t^3-1)):`,
`  `,
`> hint((t-1)/(t-2)/sqrt(t^3+1),t);`,
`   `,
`               /             3         \\   /`,
`               | (-1 + 2t -y)  (1 - y) |   |        y`,
`        1/9 ln | --------------------- | + | 2/3 -------- dt`,
`               |            3  3       |   |       3`,
`               \\     (t - 2)  t        /   /      t  + 1`,
`  `,
`SEE ALSO: int,hyperint`
):

#save `hint.m`;
#quit();
