# Author: Mark van Hoeij, e-mail address:    hoeij@sci.kun.nl
# If you change/improve this program, please let me know

# Date: Oct 21 1993
# Date previous version: Feb 2 1993
# 2 changes: 1 feature added, the genus computation, and 1 feature removed
# In the previous version integral_basis could be called with a set of algebraic
# functions instead of just 1 function. However, in this set case it can give
# the wrong answer. Instead of fixing this I have now simply removed this feature.
# Maybe I will include this set case in a next version, if anyone would want that.
# For the rest nothing changed, so the running times will be exactly the same.

# ff is a polynomial in x and y
IntBasis := `See ?integral_basis, ?genus, and ?puiseux`:
genus:=proc(ff,x,y) local d,f,n,result,i,df,disc,ext,extl,f_translated,k,nulp,
pl_transl;
    if degree(ff,[x,y])>degree(ff,y) then
        RETURN(genus(evala(Expand(subs(x=x+y,ff))),x,y)) fi;
    d:=evala(Expand(discrim(ff,y)));
    if ldegree(d,x)>0 then RETURN(genus(expand(subs(x=x+1,ff)),x,y)) fi;
    # Now there are no singularities at x=0.
    n:=degree(ff,y);
    f:=expand(subs({y=y/x,x=1/x},ff)*x^n);
    # Now f has no singularities in infinity
    extl:=`puiseux/findRootOfs`(f);
    f:=`puiseux/convert_int`(f);
    f:=`puiseux/monic`(f,y,extl,'q');
    disc:=`puiseux/evala`(discrim(f,y),extl);
    df:=`integral_basis/double_factors`(disc,x,extl);
    result:=(n-1)*(n-2)/2;
    for k in df do
        nulp:=`puiseux/zero_of`(k,x,'ext');ext:=eval(ext);ext:=[ext,op(extl)];
        f_translated:=`puiseux/evala`(subs(x=x+nulp,f),ext);
        pl_transl:=[op(`puiseux/technical_answer`(f_translated,x,y,0,ext))];
        # Now this (n-1)*(n-2)/2 should be decreased something for every singularity:
        result:=result-degree(k,x)*convert([seq(i[6]*i[7]-i[6]*(i[3]-1)/i[3],
                                                i=pl_transl)],`+`)/2
    od;
    result
end:

`help/text/genus`:=TEXT(
``,
`FUNCTION:  genus - determines genus an algebraic curve`,
``,
`CALLING SEQUENCE: genus(f,x,y)`,
``,
`PARAMETERS:`,
`   f    - a polynomial in x and y describing the curve`,
`   x, y - variables`,
``,
`SYNOPSIS:`,
`- The genus of an irreducible algebraic curve is a nonnegative integer.`,
`  However, the procedure genus can give a negative outcome. This is only`,
`  possible if the given curve is reducible.`,
``,
`EXAMPLE:`,
`f:=761328152*x^6*z^4-5431439286*x^2*y^8+2494*x^2*z^8+228715574724*x^6*y^4+`,
` 9127158539954*x^10-15052058268*x^6*y^2*z^2+3212722859346*x^8*y^2-`,
` 134266087241*x^8*z^2-202172841*y^8*z^2-34263110700*x^4*y^6-6697080*y^6*z^4-`,
` 2042158*x^4*z^6-201803238*y^10+12024807786*x^4*y^4*z^2-128361096*x^4*y^2*z^4+`,
` 506101284*x^2*z^2*y^6+47970216*x^2*z^4*y^4+660492*x^2*z^6*y^2-`,
` z^10-474*z^8*y^2-84366*z^6*y^4;`,
`  # This f is a polynomial of degree 10 having a maximal number of cusps`,
`  # according to the Plucker formulas. It was found by Rob Koelman. It has`,
`  # 26 cusps and no other singularities, hence the genus is 10.`,
`f:=subs(x=1,f);`,
`genus(f,y,z); # This may take a couple of minutes`,
``,
`f:=x^2+x*y+y^2;`,
`factor(f); # It is irreducible in Q[x,y]`,
`genus(f,x,y); # The result is impossible for an irreducible algebraic curve`,
`evala(AFactor(f)); # And we see that f is reducible`,
``
):

# The main procedure in this part of the file is puiseux
lprint(`Note: this program requires Maple V Release 2`);
lprint(`For help about genus computation type: ?genus`);
lprint(`For help about Puiseux expansions type:  ?puiseux`);

# Brief description of other procedures
# `puiseux/evala`       : Faster replacement of Maple's evala, to be able to
#  use this procedure the expression must be converted to an internal format
# `puiseux/convert_int` : This procedure converts expressions to the internal format.
# `puiseux/convert_ext` : This procedure converts expressions to Maple's format.
# `puiseux/findRootOfs`: This procedure must be used first in order to be
#  able to use `puiseux/convert_int`. It gives a list of the algebraic extensions.
# `puiseux/zero_of`     : Gives the zero of a polynomial and changes the global variables
#  that are needed for `puiseux/evala`, `puiseux/convert_int` and `puiseux/convert_ext`.
# `puiseux/technical_answer`: This procedure computes Puiseux expansions. It's input must
# be in internal format.

# The global variables are
# `puiseux/rootofs` : These are Maple's RootOf's that occurred.
# `puiseux/x`1 `puiseux/x`2 ... : I replace the RootOf's by these variables for faster
#  computation with algebraic numbers.
# `puiseux/polynomials` : These are the minimal polynomials of these RootOf's
# `puiseux/subs1`       : This set can be used to substitute for the `puiseux/x`.i's the RootOf's
#  that these `puiseux/x`.i's stand for.
# `puiseux/subs2`       : This set can be used to substite RootOf(..)=`puiseux/x`.i for faster
#  computation.

`puiseux/convert_ext`:=proc(a) subs(`puiseux/subs1`,a) end:
`puiseux/convert_int`:=proc(a) subs(`puiseux/subs2`,a) end:

# The next procedure is needed since Maple's Factor can only handle polynomials
# Result is not monic
`puiseux/Factor`:=proc(ff,ext,x) local f;
f:=numer(convert(ff,sqrfree,x));
Factor(`puiseux/convert_ext`(f),op(`puiseux/convert_ext`(ext)));
`puiseux/convert_int`(evala("));
end:


# This procedure gives the RootOf's that appear in an expression a. These
# RootOfs are being converted to the internal
# format, and returned in a list, where the first element of the list stands
# for the last (= the top) algebraic extension.
`puiseux/findRootOfs`:=proc(a) `puiseux/init`();`puiseux/findr`(a) end:
`puiseux/findr`:=proc(a) local v,vv,i,tail,dummy,dummy2;
v:=indets(a,RootOf) minus `puiseux/rootofs`;
if nops(v)=0 then RETURN([]) fi;
vv:={};
for i in v do vv:=vv union indets(op(i),RootOf) od;
tail:=`puiseux/findr`(vv);
v:=[op(v minus vv)];
v:=`puiseux/convert_int`(v);
[seq(`puiseux/zero_of`(op(dummy),_Z,'dummy2'),dummy=v),op(tail)]
end:

# Input : an irreducible polynomial kk in x, not necessarily monic
# Output: a zero of kk
# If an algebraic extension is needed it will be placed in ext.
# To introduce algebraic extensions one must use this procedure in order for
# the global variables to be set correctly.
`puiseux/zero_of`:=proc(kk,xx,ext) local n,r,k,dummy;
global `puiseux/polynomials`,`puiseux/subs1`,`puiseux/subs2`,`puiseux/rootofs`;
n:=nops(`puiseux/polynomials`)+1;
k:=subs(xx=`puiseux/x`.n,`puiseux/convert_int`(kk));
k:=expand(k/lcoeff(k,`puiseux/x`.n));
k:=`puiseux/evala`(k,[seq(`puiseux/x`.(-dummy),dummy=-(n-1)..-1)]);
if degree(k,`puiseux/x`.n)=1 then ext:=NULL;RETURN(`puiseux/x`.n-k) fi;
k:=expand(primpart(k,[seq(`puiseux/x`.dummy,dummy=1..n)]));
r:=RootOf(`puiseux/convert_ext`(k),`puiseux/x`.n);
if member(r,`puiseux/rootofs`) then ext:=`puiseux/convert_int`(r);RETURN(eval(ext)) fi;
`puiseux/polynomials`:=`puiseux/polynomials` union {`puiseux/x`.n=k};
`puiseux/subs1`:=`puiseux/subs1` union {`puiseux/x`.n=r};
`puiseux/subs2`:=r=`puiseux/x`.n,`puiseux/subs2`;
`puiseux/rootofs`:=`puiseux/rootofs` union {r};
ext:=`puiseux/x`.n;
`puiseux/x`.n end:

# Faster replacement for Maple's evala, this procedure evaluates an expression
# a where the algebraic extension is given in ext. To be able to use this
# procedure the RootOf's must be replaced by `puiseux/x`.i's, and the global variables
# (see the top of this file) must be set correctly.
`puiseux/evala`:=proc(a,ext) local r,aa,dummy,dummy2;
aa:=expand(a);
if not type(a,polynom) then aa:=expand(normal(aa)) fi;
if not type(aa,polynom(anything,ext))
then
r:=evala(Normal(`puiseux/convert_ext`(aa)));
r:=`puiseux/convert_int`(evala(Expand(r)));
if r<>a then r:=`puiseux/evala`(r,ext) fi;# To avoid loops
RETURN(r) fi;
if nops(ext)=0 then RETURN(aa) fi;
r:=ext[1];
`puiseux/evala`(sum('coeff(aa,r,dummy)*`puiseux/remember_evala`(r^dummy,ext)',dummy=
 0..degree(aa,r)),[seq(ext[dummy2],dummy2=2..nops(ext))])
end:
`puiseux/remember_evala`:=proc(a,ext)
options remember;
expand(grobner[normalf](expand(a),subs(`puiseux/polynomials`,ext),ext,'plex')) end:


`help/text/puiseux`:=TEXT(
``,
`FUNCTION:  puiseux - determines the Puiseux expansions of an algebraic function`,
``,
`CALLING SEQUENCE: puiseux(a,x=v,n)`,
``,
`PARAMETERS:`,
`   a   - an algebraic function in RootOf form`,
`   x=v - gives the point around which the expansions are computed`,
`   n   - a real number`,
``,
`SYNOPSIS:`,
`- The number of Puiseux expansions is equal to the degree of the minimal`,
`  polynomial of a. The minimal polynomial is a polynomial over a field L(x).`,
`  The procedure puiseux determines the field L from a.`,
`  The groundfield L of the computation is taken the field Q extended with`, 
`  the algebraic numbers (which must be in RootOf form) that appear in the`, 
`  minimal polynomial of a. If more than 1 litteral appears in a, than these`, 
`  litterals are also elements of L, except the litteral on the left side of`, 
`  the second argument.`,
``,
`- If a number of Puiseux expansions are algebraically conjugated over L, than`, 
`  only one of these expansions is given.`,
``,
`- The Puiseux expansions are being computed modulo x^n, so if for instance`,
`  n=10, then the term x^(49/5) would be computed, but not the term x^10`,
`  The Puiseux expansions are being computed further than x^n if this would`,
`  be necessary to seperate all different branches. So if n=0, the computation`,
`  would stop precisely at the moment when all Puiseux expansions can be`,
`  distinguished from one another, that is when all computed expansions are`,
`  different.`,
``,
`EXAMPLES:`,
` alias(alpha=RootOf(x^3+7));`,
` f:=y^8+3*x*y^5+5*x^4+x^6*alpha;`,
` alias(beta=RootOf(f,y));`,
` puiseux(beta,x=0,0);`,
` puiseux(beta,x=0,9.5);`,
``,
`BUGS: The Maple "alias" function does not recognize an alias in terms of`,
`  another alias. Therefor, you must not use nested aliasses for algebraic`,
`  numbers.`,
``,
`EXAMPLE:`,
` alpha:=RootOf(x^3+7); # alias(alpha=RootOf(x^3+7)) would go wrong`,
` alias(beta=RootOf(x^4+alpha*x+7)); # Or beta:=RootOf(x^4+alpha*x+7)`,
` f:=y^5+alpha*x*y^4+beta*x^5*y^2+7*x^7;`,
` alias(delta=RootOf(f,y)); # Or delta:=RootOf(f,y)`,
` puiseux(delta,x=0,0);`,
``
):

# Gives the zeros of the factors, their multiplicities and algebraic extensions
`puiseux/v_ext_m`:=proc(f,x) local ext,nulp,i,result;
if degree(f,x)=0 then RETURN({}) fi;
if type(f,`^`) then
  nulp:=`puiseux/zero_of`(op(f)[1],x,'ext');ext:=eval(ext);
  RETURN({[nulp,op(f)[2],[ext],degree(op(f)[1],x)]})
fi;
if type(f,`*`) then result:={};
  for i in {op(f)} do result:=result union `puiseux/v_ext_m`(i,x) od;
  RETURN(result)
fi;
nulp:=`puiseux/zero_of`(f,x,'ext');ext:=eval(ext);
{[nulp,1,[ext],degree(f,x)]} end:

`puiseux/rem`:=proc(a,n,x) local dummy;
expand(sum('x^dummy*coeff(a,x,dummy)',dummy=0..n-1)) end:

`puiseux/monic`:=proc(f,y,ext,q) local dummy,ff,lc,qq;
ff:=numer(normal(`puiseux/evala`(f,ext)));
lc:=lcoeff(ff,y);
if indets(lc,`name`) minus {op(ext)}={} then
 q:=1;RETURN(`puiseux/evala`(ff/lc,ext)) fi;
lc:=`puiseux/convert_int`(evala(Sqrfree(`puiseux/convert_ext`(lc))));
lc:=lc[2];
lc:=`puiseux/evala`(product(lc[dummy][1],dummy=1..nops(lc)),ext);
ff:=`puiseux/monic`(subs(y=y/lc,ff),y,ext,'qq');qq:=eval(qq);
q:=`puiseux/evala`(qq*lc,ext); ff end:

# So far all the preparations, now the algorithm
puiseux:=proc(aa:algfun(rational),eqn:name=algfun(rational),n:numeric)
local x,v,a,f,y,ext,q,verz,i,result,ma,dummy,dummy2,qx;
if not type(aa,RootOf) then ERROR(`First argument must be a RootOf`) fi;
x:=eval(op(1,eqn));v:=op(2,eqn);
a:=subs(x=x+v,aa);
ext:=`puiseux/findRootOfs`(op(a));
f:=`puiseux/convert_int`(op(a));
f:=subs(_Z=y,f);
f:=`puiseux/monic`(f,y,ext,'q');q:=eval(q);
qx:=ldegree(q,x);q:=expand(q/x^qx);
verz:=`puiseux/technical_answer`(f,x,y,n+qx,ext);
ma:=ceil(max(seq(dummy[2]/dummy[3],dummy=verz)))+qx;
q:=taylor(1/q,x=0,ma);
q:=`puiseux/evala`(sum('x^dummy2*coeff(q,x,dummy2)',dummy2=0..ma-1),ext);
result:={};
for i in verz do 
result:=result union {expand(subs(x=x^(1/i[3]),
 `puiseux/rem`(`puiseux/evala`(subs(x=x^i[3],q)*i[1],ext),i[2],x))/x^qx)}
od;
a:=`puiseux/convert_ext`(result);
subs(x=x-v,a)
end:

`puiseux/technical_answer`:=proc(f,x,y,n,ext) local result,i,verz;
options remember;
verz:=`puiseux/cont_exp`([0,0,1,ext,degree(f,y),1],f,x,y);
result:={};
for i in verz do
result:=result union `puiseux/cont_exp_m1`(i,f,x,y,n)
od;
result
end:

# This procedure continues an expansion until it has multiplicity 1
# Input is a list v
# v[1] = The expansion so far
# v[2] = This expansion is determined modulo x^(v[2]/v[3])
# v[3] = Least common multiple of the denominators of the powers of x
# v[4] = The algebraic extensions in this expansion
# v[5] = The multiplicity of this factor
# v[6] = The degree of the algebraic extensions above the groundfield
# It will also return (not needed in the input)
# v[7] = sum of the valuations ( v(x^(1/d))=1/d ) of the differences
# of this expansion with the other expansions. This sum is needed for
# computing an integral basis in an algebraic function field.
# f    = minimal polynomial of y over L(x), must be monic
`puiseux/cont_exp`:=proc(v,f,x,y) local t,n,a,i,ii,r,som,result,vv7;
if v[5]=1 then RETURN({v}) fi;
result:={};
r:=v[1]+a*x^v[2];
# I will find an equation for the unknown a
r:=`puiseux/evala`(subs({x=x^v[3],y=r},f),v[4]);
vv7:=(ldegree(r,x)-v[2])/v[3];
r:=expand(r/x^ldegree(r,x));
r:=coeff(r,x,0);
if degree(r,a)>0 then
r:=`puiseux/evala`(`puiseux/evala`(1/lcoeff(r,a),v[4])*r,v[4]);
if v[5]=0 then r:=expand(r/a^ldegree(r,a)) fi;
if degree(r,a)>1 then
  r:=`puiseux/Factor`(r,v[4],a)
fi;
r:=`puiseux/v_ext_m`(r,a);
for i in r do
result:=result union `puiseux/cont_exp`([v[1]+x^v[2]*i[1],v[2]+1,v[3],
 [op(i[3]),op(v[4])],i[2],v[6]*i[4],vv7],f,x,y)
od
fi;
som:=0;for ii in result do som:=som+ii[5]*ii[6] od;
n:=2;
while som<v[5] do 
for t from 1 to n-1 do if igcd(t,n)=1 then
  result:=result union `puiseux/cont_exp`([subs(x=x^n,v[1]),(v[2]-1)*n+t,
   v[3]*n,v[4],0,v[6]],f,x,y);
  som:=0;for i in result do som:=som+i[5]*i[6] od;
fi od;
n:=n+1;
od;
result
end:

# This procedure continues (= computes more terms of) expansions that have
# multiplicity 1. This could also be done with `puiseux/cont_exp`, but this
# procedure is faster.
`puiseux/cont_exp_m1`:=proc(v,f,x,y,nnk) local nk,a,result,machtx,r,rr;
nk:=ceil(nnk*v[3])/v[3];
machtx:=v[2];
if nk<=machtx/v[3] then RETURN({v}) fi;
result:=v[1]+a*x^v[2];
r:=`puiseux/evala`(subs({x=x^v[3],y=result},f),v[4]);
r:=`puiseux/rem`(expand(r/x^ldegree(r,x)),v[3]*nk-machtx,x);
while nk>machtx/v[3] do
rr:=coeff(r,x,0);
if degree(rr,a)<>1 then ERROR(`degree is not 1`) fi;
rr:=`puiseux/evala`(-coeff(rr,a,0)/coeff(rr,a,1),v[4]);
result:=expand(subs(a=rr+x*a,result));
machtx:=machtx+1;
if nk>machtx/v[3] then
 r:=subs(a=rr+x*a,r);
 r:=`puiseux/evala`(r,v[4]);
 if ldegree(r,x)<>1 then ERROR(`degree is not 1`) fi;
 r:=`puiseux/rem`(expand(r/x),v[3]*nk-machtx,x)
fi
od;
{[subs(a=0,result),machtx,v[3],v[4],v[5],v[6],v[7]]}
end:

readlib(forget):
`puiseux/init`:=proc()
global `puiseux/polynomials`,`puiseux/subs1`,`puiseux/subs2`,`puiseux/rootofs`;
    `puiseux/polynomials`:={}:
    `puiseux/subs1`:={}:
    `puiseux/subs2`:=NULL:
    `puiseux/rootofs`:={}:
    forget(`puiseux/remember_evala`):
end:
`puiseux/init`():

# If you only want to compute Puiseux expansions you can delete everything
# below this line.

lprint(`For help about the integral basis type:  ?integral_basis`);

`help/text/integral_basis`:=TEXT(
``,
`FUNCTION:  integral_basis - the integral basis of an algebraic function field`,
``,
`CALLING SEQUENCE: integral_basis(a,x)`,
``,
`PARAMETERS:`,
`   a   - an algebraic function in one variable`,
`   x   - this variable (only needed if the number of litterals in a is greater`,
`         than 1)`,
``,
`SYNOPSIS:`,
`- This procedure computes an integral basis for an algebraic function that is`,
`  given as a RootOf. The method is based on Puiseux expansions. For more`,
`  information (a LaTeX file) send an e-mail to hoeij@sci.kun.nl`,
``,
`- If printlevel > 1, some information will be printed during the computation.`,
``,
`EXAMPLE:`,
` alias(alpha=RootOf(x^3+7));`,
` # Or: alpha:=RootOf(x^3+7); `,
` f:=y^8+3*x*y^5*alpha+5*x^4+x^6*alpha;`,
` alias(beta=RootOf(f,y));`,
` printlevel:=2;`,
` integral_basis(beta);`,
``,
`SEE ALSO:  maxorder, puiseux`
):

integral_basis:=proc() local alfa,f,x,y,extl,disc,df,basis,pl_transl,
 k,ext,nulp,f_translated,max_v7,places,d,i,b,found_something,
 dummy,dummy2,skip_test,q,
 power_of_k,equations,values_basis_in_places,value_new_one,kk,max_power_k;
alfa:=eval(args[1]);
if nargs=1 then x:=op(indets(alfa,`name`) minus {_Z}) else x:=args[2] fi;
if not (type(alfa,algfun(rational)) and type(x,`name`)) then ERROR(`Wrong arguments`) fi;
extl:=`puiseux/findRootOfs`(op(alfa));
# extl contains the alg. numbers in the groundfield
f:=subs(_Z=y,`puiseux/convert_int`(op(alfa)));
f:=`puiseux/monic`(f,y,extl,'q');q:=eval(q);
# f is the minimal polynomial of y over L(x), converted to the internal format
# with `puiseux/convert_int` (see the file puiseux). Note that `puiseux/convert_int` and subs must
# be executed in this order, since the RootOfs also contain _Z
# Now f is monic
if printlevel>1 then lprint(`Computing the discriminant ...`) fi;
disc:=`puiseux/evala`(discrim(f,y),extl);
if printlevel>1 then
lprint(`disc=`,`puiseux/convert_ext`(disc));lprint();
lprint(`Computing those factors of the discriminant that appear more than once  ...`) fi;
df:=`integral_basis/double_factors`(disc,x,extl);
if printlevel>1 then lprint(`Factors that appear more than once:`,
 `puiseux/convert_ext`(df));lprint() fi;
# df contains those factors k that appear more than once in the
# discriminant discrim(f,y)
for k in df do
  power_of_k[k]:=1;
  # Later we will search for integrals of the shape (..)/k^power_of_k[k]
  nulp:=`puiseux/zero_of`(k,x,'ext');ext:=eval(ext);ext:=[ext,op(extl)];
  # the zero of k is now in nulp, if an algebraic extension is needed it is
  # placed in ext
  f_translated:=`puiseux/evala`(subs(x=x+nulp,f),ext);
  if printlevel > 1 then
   lprint(`Computing the Puiseux expansions belonging to the factor`,`puiseux/convert_ext`(k)) fi;
  pl_transl:=[op(`puiseux/technical_answer`(f_translated,x,y,0,ext))];
  # Now pl_transl contains the puiseux expansions, but translated x=x+nulp
  max_v7:=max(seq(dummy[7],dummy=pl_transl));
  # this number is the maximum internal intersection multiplicity (however,
  # the way we count it x^(1/2) and -(x^(1/2)) have intersection multiplicity
  # 1/2), see also in the file puiseux the procedure `puiseux/cont_exp`
  max_power_k[k]:=floor(max_v7);
  if printlevel >1 then lprint(`Maximum number of factors`,`puiseux/convert_ext`(k),
   `in the denominator is`,
   max_power_k[k]);lprint() fi;
  pl_transl:=[seq(op(`puiseux/cont_exp_m1`(dummy,f_translated,x,y,
   min(max_v7-dummy[7]+dummy[2]/dummy[3],
       max_power_k[k])
   )),dummy=pl_transl)];
  # Here I computed more terms of the puiseux expansions
  places[k]:=[seq([dummy[1],
   nulp,dummy[3],
   # The following `integral_basis/ext_needed`(..) was pl_transl[dummy][4] in the 
   # previous version
   `integral_basis/ext_needed`(dummy[7],max_power_k[k],dummy[4],
    dummy[1],x)
   ],dummy=pl_transl)];
  # places[k][dummy] contains: (dummy=1..number of puiseux expansions)
  # 1) The puiseux expansion (but translated x=x+nulp)
  # 2) Contains the zero of k   (= this number nulp)
  # 3) The least common multiple of the denominators of the powers of x,
  #    so x here must be interpreted as x^(1/d) if this lcm is d.
  #    If we take into account the translation it would be x^(1/d)-nulp.
  #    So in order to interpret a normal x we substitute subs(x=x^d+nulp,..)
  # 4) The algebraic extension belonging to this puiseux expansion
  values_basis_in_places[k,1]:=[seq(1,dummy=pl_transl)]
  # Because the first basis-element will be 1.
od;
basis:=[1];
for d from 2 to degree(f,y) do
if printlevel>1 then lprint(`Now all integral elements of degree less than`,d-1,
`have been computed`) fi;
basis:=[op(basis),y*basis[d-1]];
# This basis is our first guess
for k in df do
# Now we compute the values of this new basis-element in all
# places (that is, we substitute y=puiseux expansions)
values_basis_in_places[k,d]:=
[seq(`puiseux/evala`(values_basis_in_places[k,d-1][dummy]*places[k][dummy][1],
 places[k][dummy][4]),dummy=1..nops(places[k]))]
od;
for k in df do
# using the values of this new basis-element in the places,
# we will see if we can find an integral, with a bigger denominator
found_something:=true;
while found_something and power_of_k[k]<=max_power_k[k] do
for i from 1 to d-1 do b[i]:=evaln(b[i]) od;b[i]:=1;
# Now we compute the values of basis[1]*b[1]+..+basis[d]*b[d] in the places
# and we try to put an extra factor k in the denominator
value_new_one:=expand(eval(
 [seq(sum('b[dummy2]*values_basis_in_places[k,dummy2][dummy]',dummy2=1..d)
 ,dummy=1..nops(places[k]))]));
# All coefficients of powers of x less than power_of_k must be zero, in order
# for this new_one to be dividable by k^power_of_k[k]. So we find
# equations by taking the remainder
equations:=[seq(`puiseux/rem`(value_new_one[dummy],places[k][dummy][3]*power_of_k[k],x)
 ,dummy=1..nops(places[k]))];
for i in indets(equations) minus {places[k][1][2],op(extl),seq(b[dummy],dummy=1..d-1)} do
equations:=[op({seq(coeffs(dummy,i),dummy=equations)})] od;
equations:=`puiseux/convert_ext`(equations);
equations:=solve({op(equations)});
# Now we know what values b[1] .. b[d] must have
if equations=NULL then found_something:=false
else equations:=`puiseux/convert_int`(equations);
if indets(equations) minus {op(extl),seq(b[dummy],dummy=1..d-1)} <> {}
then equations:=subs(places[k][1][2]=x,equations) fi;
assign(equations);
# Now basis[1]*b[1]+..basis[d]*b[d] is dividable by k
# In the following for_do_od we compute the values of
# basis[1]*b[1]+..+basis[d]*b[d] in all places.
for kk in df do
values_basis_in_places[kk,d]:=[seq(`puiseux/evala`(
 `puiseux/rem`(expand(
  sum('subs(x=x^places[kk][dummy][3]+places[kk][dummy][2],eval(b[dummy2]))*
  values_basis_in_places[kk,dummy2][dummy]',dummy2=1..d)),
 places[kk][dummy][3]*max_power_k[kk],x)
,places[kk][dummy][4])
  ,dummy=1..nops(places[kk]))]
od;
# Now we will put (basis[1]*b[1]+..+basis[d]*b[d])/k in the basis
basis:=[seq(basis[dummy],dummy=1..d-1),
 normal(sum('eval(b[dummy2])*basis[dummy2]',dummy2=1..d)/k)];
# Now we should divide values_basis_in_places[.. ,d] by k, but instead
# we multiply all other values_basis_in_places[.., <>d] by k, this will
# be done in the following nested for_do_od
for i from 1 to d-1 do for kk in df do
values_basis_in_places[kk,i]:=
[seq(`puiseux/evala`(
`puiseux/rem`(expand(
  values_basis_in_places[kk,i][dummy]*
  subs(x=x^places[kk][dummy][3]+places[kk][dummy][2],k)),
 places[kk][dummy][3]*max_power_k[kk],x)
,places[kk][dummy][4]),dummy=1..nops(places[kk]))] od od;
# Now we increase power_of_k, so we will try to put more factors k in the
# denominator
power_of_k[k]:=power_of_k[k]+1 fi
od od od;
# Now we convert our basis back to the external format (= Maple's format)
subs(y=alfa*q,`puiseux/convert_ext`(basis))
end:

`integral_basis/ext_needed`:=proc(a,b,c,d,x) local dummy;
if a<b or nops(c)=0 then RETURN(c) fi;
if lcoeff(d,x)<>c[1] then RETURN(c) fi;
if member(c[1],indets(eval(d-c[1]*x^degree(d,x)))) then RETURN(c) fi;
[seq(c[dummy],dummy=2..nops(c))] end:

# This procedure gives those factors which appear more than once
`integral_basis/double_factors`:=proc(ff,x,ex) local iim,fr,f,ext,i,ii,vv,result,v,dummy;
f:=ff;ext:=ex;
while nops(ext)>0 do
f:=resultant(subs(`puiseux/polynomials`,ext[1]),f,ext[1]);
ext:=[seq(ext[dummy],dummy=2..nops(ext))] od;
f:=normal(f);
f:=factor(numer(f));
v:=`integral_basis/double_factors_old`(f);
result:={};
for i in v do
if ex=[] then result:=result union {expand(i/lcoeff(i,x))}
else fr:=expand(rem(ff,expand(i^2),x));
  if nops(i)>degree(i,x)+1 then
    vv:=normal(rem(fr,i,x)); if vv=0 then vv:=i fi
  else vv:=i fi; # For reasons of efficiency
  vv:=`integral_basis/double_factors_old`(`puiseux/Factor`(vv,ex,x)^2);
  for ii in vv do iim:=`puiseux/evala`(ii/lcoeff(ii,x),ex);
   if `puiseux/evala`(normal(rem(fr,`puiseux/evala`(iim^2,ex),x)),ex)=0 then
     result:=result union {iim} fi od
fi
od;
v:={};
for i in result do
 if degree(i,x)>0 then v:=v union {i} fi od;v
end:

`integral_basis/double_factors_old`:=proc(ff) local result,i;
if type(ff,`+`) then RETURN({}) fi;
if type(ff,`^`) then RETURN({op(1,ff)}) fi;
result:={};
for i in {op(ff)} do
if type(i,`^`) then result:={op(result),op(1,i)} fi od;
result end:

# save `IntBasis.m`;
# quit
