##
##    Title: 	gfun package (for Generating FUNctions)
##    Created:	Wed Mar  4 15:13:42 1992
##    Authors: 	Bruno Salvy <Bruno.Salvy@inria.fr>
##		Paul Zimmermann <Paul.Zimmermann@inria.fr>
##
## Description: converts implicit equations into differential equations,
##              differential equations into recurrences and vice-versa,
##              ordinary into exponential recurrences.
##		also recovers linear recurrences or differential equations
##		from finite lists.
##
##  Some of the ideas used in this file are due to
##              Simon Plouffe <plouffe@lacim.uqam.ca>
##              Francois Bergeron <bergeron@lacim.uqam.ca>
###########################################################################


macro(
algfuntodiffeq 	=  gfun[algfuntodiffeq],
diffeqtorec	=  gfun[diffeqtorec],
difforder	= `gfun/difforder`,
dorder		= `gfun/dorder`,
dorderinit	= `gfun/dorderinit`,
eqinittorec	= `gfun/eqinittorec`,
eqinittorec_aux	= `gfun/eqinittorec_aux`,
exptoord_aux	= `gfun/exptoord_aux`,
guessgf		=  gfun[guessgf],
hsum 		=  gfun[hsum],
hproduct 	=  gfun[hproduct],
inicond		= `gfun/inicond`,
initeqs		= `gfun/initeqs`,
initorder 	= `gfun/initorder`,
intoho		= `gfun/intoho`,
Isolve 		= `gfun/isolve`,
lindep 		= `gfun/lindep`,
listtoalgeq	=  gfun[listtoalgeq],
listtodiffeq	=  gfun[listtodiffeq],
listtohypergeom =  gfun[listtohypergeom],
listtolist	=  gfun[listtolist],
listtoratpoly	=  gfun[listtoratpoly],
listtorec	=  gfun[listtorec],
listtoseries	=  gfun[listtoseries],
`l2r/l2r`	= `gfun/listtorec/listtorec`,
`l2h/l2h`	= `gfun/listtohypergeom/listtohypergeom`,
maxordereqn	=  gfun['maxordereqn'],
minordereqn	=  gfun['minordereqn'],
maxdegcoeff	=  gfun['maxdegcoeff'],
mindegcoeff	=  gfun['mindegcoeff'],
maxdegeqn	=  gfun['maxdegeqn'],
mindegeqn	=  gfun['mindegeqn'],
maxindex	= `gfun/maxindex`,
minindex	= `gfun/minindex`,
mygcdex		= `gfun/mygcdex`,
gensolvelin	= `gfun/gensolvelin`,
ordtoexp_aux	= `gfun/ordtoexp_aux`,
ratsolvelin	= `gfun/ratsolvelin`,
ratpolytocoeff	=  gfun[ratpolytocoeff],
`ratpolytocoeff/pretreat`=`gfun/ratpolytocoeff/pretreat`,
`ratpolytocoeff/redfrac`=`gfun/ratpolytocoeff/redfrac`,
recinittoeq	= `gfun/recinittoeq`,
recinittoeq_aux	= `gfun/recinittoeq_aux`,
rectodiffeq	=  gfun[rectodiffeq],
rectodiffeq_aux	= `gfun/rectodiffeq_aux`,
rectoinits	= `gfun/rectoinits`,
rectoproc	=  gfun[rectoproc],
reduct		= `gfun/reduct`,
`s2d/s2d`	= `gfun/seriestodiffeq/seriestodiffeq`,
`s2a/s2a`	= `gfun/seriestoalgeq/seriestoalgeq`,
seriestoalgeq	=  gfun[seriestoalgeq],
seriestodiffeq	=  gfun[seriestodiffeq],
seriestohypergeom =gfun[seriestohypergeom],
seriestolist	=  gfun[seriestolist],
seriestoratpoly	=  gfun[seriestoratpoly],
seriestorec	=  gfun[seriestorec],
sg2coeff 	= `gfun/sg2coeff`,
sg2coeff_aux 	= `gfun/sg2coeff_aux`,
Solve		= `gfun/solve`,
typecheck	= `gfun/typecheck`,
verifyinits	= `gfun/verifyinits`
);

# This is to avoid several type checks of the same expression
typecheck:=proc (n)
local i;
    if n=1 then		# l, x, <met>
	if nargs>2 and type([args[2..3]],[list,name]) then
	    if nargs=3 then RETURN('stamped',args[2..3],'egf')
	    elif nargs=4 and type(gfun[cat(`listtoseries/`,args[4])],procedure)
		then RETURN('stamped',args[2..4])
	    elif nargs>4 then ERROR(`too many arguments`)
	    else ERROR(`invalid argument`,args[4])
	    fi
	elif nargs<3 then ERROR(`too few parameters`)
	elif not type(args[2],list) then ERROR(`not a list`,args[2])
	elif not type(args[3],name) then ERROR(`not a name`,args[3])
	else ERROR(`invalid arguments`)
	fi
    elif n=2 then	# l, y(x), <[met]>
	if nargs>2 and type([args[2..3]],[list,function(name)]) then
	    if nargs=3 then RETURN('stamped',args[2..3],['ogf','egf'])
	    elif nargs=4 and type(args[4],list) and
		type([seq(gfun[cat(`listtoseries/`,i)],i=args[4])],
		    list(procedure)) then RETURN('stamped',args[2..4])
	    elif nargs>4 then ERROR(`too many arguments`)
	    else ERROR(`invalid argument`,args[4])
	    fi
	elif nargs<3 then ERROR(`too few arguments`)
	elif not type(args[2],list) then ERROR(`not a list`,args[2])
	elif not type(args[3],function(name)) then
	    ERROR(`invalid unknown function`,args[3])
	else ERROR(`invalid arguments`)
	fi
    elif n=3 then	# l, x, [<met>]
	if nargs>2 and type([args[2..3]],[list,name]) then
	    if nargs=3 then RETURN('stamped',args[2..3],['ogf','egf'])
	    elif nargs=4 and type(args[4],list) and
		type([seq(gfun[`listtoseries/`.i],i=args[4])],list(procedure))
		    then RETURN('stamped',args[2..4])
	    elif nargs>4 then ERROR(`too many arguments`)
	    elif nargs=4 then ERROR(`invalid argument`,args[4])
	    fi
	elif nargs<3 then ERROR(`too few parameters`)
	elif not type(args[2],list) then ERROR(`not a list`,args[2])
	elif not type(args[3],name) then ERROR(`not a name`,args[3])
	else ERROR(`invalid argments`)
	fi
    elif n=4 then	# s, x, y
	if nargs=4 and type([args[2..4]],[series,name,name]) then
	    RETURN('stamped',args[2..4])
	elif nargs<>4 then ERROR(`wrong number of arguments`)
	elif not type(args[2],series) then ERROR(`not a series`,args[2])
	elif not type(args[3],name) then ERROR(`not a name`,args[3])
	elif not type(args[4],name) then ERROR(`not a name`,args[4])
	else ERROR(`invalid arguments`)
	fi
    elif n=5 then	# l, y(x)
	if nargs=3 and type([args[2..3]],[list,function(name)]) then
	    RETURN('stamped',args[2..3])
	elif nargs<>3 then ERROR(`wrong number of arguments`)
	elif not type(args[2],list) then ERROR(`not a list`,args[2])
	elif not type(args[3],function(name)) then
	    ERROR(`invalid unknown function`,args[3])
	else ERROR(`invalid arguments`)
	fi
    elif n=6 then	# s, y(x), <[met]>
	if nargs>2 and type([args[2..3]],[series,function(name)]) then
	    if nargs=3 then RETURN('stamped',args[2..3],['ogf','egf'])
	    elif nargs=4 and type(args[4],list) and
		type([seq(gfun[cat(`listtoseries/`,i)],i=args[4])],
		    list(procedure)) then RETURN('stamped',args[2..4])
	    elif nargs>4 then ERROR(`too many arguments`)
	    else ERROR(`invalid argument`,args[4])
	    fi
	elif nargs<3 then ERROR(`too few arguments`)
	elif not type(args[2],series) then ERROR(`not a series`,args[2])
	elif not type(args[3],function(name)) then
	    ERROR(`invalid unknown function`,args[3])
	else ERROR(`invalid arguments`)
	fi
    elif n=7 then 	# s, <[met]>
	if nargs>1 and type(args[2],series) then
	    if nargs=2 then RETURN('stamped',args[2],['ogf','egf'])
	    elif nargs=3 and type(args[3],list) and 
		type([seq(gfun[cat(`listtoseries/`,i)],i=args[3])],
		    list(procedure)) then RETURN('stamped',args[2..3])
	    elif nargs>3 then ERROR(`too many arguments`)
	    else ERROR(`invalid argument`,args[3])
	    fi
	elif nargs=1 then ERROR(`too few arguments`)
	elif not type(args[2],series) then ERROR(`not a series`,args[2])
	else ERROR(`invalid arguments`)
	fi
    elif n=8 then 	# s, <met>
	if nargs>1 and type(args[2],series) then
	    if nargs=2 then RETURN('stamped',args[2],'ogf')
	    elif nargs=3 and type(gfun[cat(`listtoseries/`,args[3])],procedure)
		then RETURN('stamped',args[2..3])
	    elif nargs>3 then ERROR(`too many arguments`)
	    else ERROR(`invalid argument`,args[3])
	    fi
	elif nargs=1 then ERROR(`too few arguments`)
	elif not type(args[2],series) then ERROR(`not a series`,args[2])
	else ERROR(`invalid arguments`)
	fi
    elif n=9 then 	# l, <met>
	if nargs>1 and type(args[2],list) then
	    if nargs=2 then RETURN('stamped',args[2],'ogf')
	    elif nargs=3 and type(gfun[cat(`listtoseries/`,args[3])],procedure)
		then RETURN('stamped',args[2..3])
	    elif nargs>3 then ERROR(`too many arguments`)
	    else ERROR(`invalid argument`,args[3])
	    fi
	elif nargs=1 then ERROR(`too few arguments`)
	elif not type(args[2],list) then ERROR(`not a list`,args[2])
	else ERROR(`invalid arguments`)
	fi
    else ERROR(`should not happen`)
    fi;
end: # typecheck

seriestolist:=proc()
local s, meth, l, x, i;
    if args[1]<>'stamped' then RETURN(seriestolist(typecheck(8,args))) fi;
    s:=args[2]; meth:=args[3]; x:=op(0,s);
    if op(nops(s)-1,s)=O(1) then l:=[seq(coeff(s,x,i),i=0..op(nops(s),s)-1)]
    else l:=[seq(coeff(s,x,i),i=0..op(nops(s),s))] fi;
    if meth='ogf' then RETURN(l)
    else RETURN(seriestolist('stamped',listtoseries('stamped',l,x,meth),'ogf'))
    fi
end: # seriestolist

listtolist:=proc()
local x;
    if args[1]<>'stamped' then RETURN(listtolist(typecheck(9,args))) fi;
    if args[3]='ogf' then RETURN(args[2]) fi;
    RETURN(seriestolist('stamped',
	listtoseries('stamped',args[2],x,args[3]),'ogf'))
end: #listtolist

seriestoseries:=proc ()
    if args[1]<>'stamped' then RETURN(seriestoseries(typecheck(8,args))) fi;
    RETURN(listtoseries('stamped',seriestolist('stamped',args[2],'ogf'),
	op(0,args[2]),args[3]))
end: # seriestoseries

listtoseries:=proc ()
    if args[1]<>'stamped' then RETURN(listtoseries(typecheck(1,args))) fi;
    RETURN(gfun[cat(`listtoseries/`,args[4])](args[2],args[3]))
end: # listtoseries

gfun[`listtoseries/egf`]:=proc(l,x)
local i;
    RETURN(series(convert([seq(op(i,l)*x^(i-1)/(i-1)!,i=1..nops(l)),
	O(x^(nops(l)))],`+`),x,nops(l)))
end:

gfun[`listtoseries/ogf`]:=proc(l,x)
local i;
    RETURN(series(convert([seq(op(i,l)*x^(i-1),i=1..nops(l)),
	O(x^(nops(l)))],`+`),x,nops(l)))
end:

gfun[`listtoseries/revogf`]:=proc(l,x)
local i, t;
    Order:=nops(l)+1;
    RETURN(solve(series(convert([seq(op(i,l)*t^i,i=1..nops(l)),t^Order],
	`+`),t)=x,t))
end:

gfun[`listtoseries/revegf`]:=proc(l,x)
local i, t;
    Order:=nops(l)+1;
    RETURN(solve(series(convert([seq(op(i,l)*t^i/i!,i=1..nops(l)),t^Order]
	,`+`),t)=x,t))
end:

gfun[`listtoseries/lgdogf`]:=proc(l,x)
local i;
    RETURN(series(convert([seq(i*op(i+1,l)*x^(i-1),i=1..nops(l)-1)],`+`)/
	    convert([seq(op(i,l)*x^(i-1),i=1..nops(l))],`+`),x,nops(l)-1))
end:

gfun[`listtoseries/lgdegf`]:=proc(l,x)
local i;
    RETURN(series(convert([seq(op(i,l)*x^(i-2)/(i-2)!,i=2..nops(l))],`+`)/
	convert([seq(op(i,l)*x^(i-1)/(i-1)!,i=1..nops(l))],`+`),x,nops(l)-1))
end: 

listtodiffeq:=proc()
local result, ex, methods, method, y, x, s, unkn, expr;
    if args[1]<>'stamped' then RETURN(listtodiffeq(typecheck(2,args))) fi;
    expr:=args[2];unkn:=args[3];methods:=args[4];
    y:=op(0,unkn);x:=op(unkn);ex:=expr;
    # first remove leading zeroes
#    if {op(ex)} minus {0} = {} then RETURN(0) fi;
#    while op(1,ex)=0 do ex:=subsop(1=NULL,ex) od;
    for method in methods do
	s:=listtoseries('stamped',ex,x,method);
	userinfo(3,'gfun',`Trying the `,method,s);
	result:=`s2d/s2d`(s,x,y);
	if result<>FAIL then
	    userinfo(2,'gfun','The',method,
		#'`divided by`',eval(x^(nops(expr)-nops(ex))),
		'`seems to satisfy`',result);
#	    RETURN([inicond(s,result,y,x),nops(expr)-nops(ex),method])
	    RETURN([inicond(s,result,y,x),method])
	fi
    od;
    FAIL
end: # listtodiffeq

seriestodiffeq:=proc ()
    if args[1]<>'stamped' then RETURN(seriestodiffeq(typecheck(6,args))) fi;
    RETURN(listtodiffeq('stamped',
	seriestolist('stamped',args[2],'ogf'),args[3],args[4]))
end: # seriestodiffeq

listtoalgeq:=proc()
local result, ex, methods, method, y, x, s, unkn, expr;
    if args[1]<>'stamped' then RETURN(listtoalgeq(typecheck(2,args))) fi;
    expr:=args[2];unkn:=args[3];methods:=args[4];
    y:=op(0,unkn);x:=op(unkn);ex:=expr;
    # first remove leading zeroes
#    if {op(ex)} minus {0} = {} then RETURN(0) fi;
#    while op(1,ex)=0 do ex:=subsop(1=NULL,ex) od;
#    Order:=nops(ex);
    for method in methods do
	s:=listtoseries('stamped',ex,x,method);
	userinfo(3,'gfun',`Trying the `,method,s);
	result:=`s2a/s2a`(s,x,y);
	if result<>FAIL then
	    userinfo(2,'gfun','The',method,
		# '`divided by`', eval(x^(nops(expr)-nops(ex))),
		'`seems to satisfy`',result);
	    RETURN([result,method])
	fi
    od;
    FAIL
end: # listtoalgeq

seriestoalgeq:=proc ()
    if args[1]<>'stamped' then RETURN(seriestoalgeq(typecheck(6,args))) fi;
    RETURN(listtoalgeq('stamped',
	seriestolist('stamped',args[2],'ogf'),args[3],args[4]))
end: # seriestoalgeq

listtoratpoly:=proc()
local result, ex, methods, method, s, x;
    if args[1]='stamped' then ex:=args[2];x:=args[3];methods:=args[4]
    else RETURN(listtoratpoly(typecheck(3,args))) fi;
    Order:=nops(ex);
    for method in methods do
	s:=listtoseries('stamped',ex,x,method);
	userinfo(3,'gfun',`Trying the `,method,s);
	result:=normal(factor(convert(s,ratpoly)));
	if degree(numer(result),x)+degree(denom(result),x)<Order
	and length(result)<length(ex)+20
	then
	    userinfo(2,'gfun','The',method,'`seems to be`',result);
	    RETURN([result,method])
	fi
    od;
    FAIL
end: # listoratpoly

seriestoratpoly:=proc () # yes, it's stupid to convert it to a list now.
    if args[1]<>'stamped' then RETURN(seriestoratpoly(typecheck(7,args))) fi;
    RETURN(listtoratpoly('stamped',
	    seriestolist('stamped',args[2],'ogf'),op(0,args[2]),args[3]))
end: # seriestoratpoly


maxordereqn:=2: # default 2nd order
minordereqn:=1: # default 1st order
maxdegcoeff:=3: # default degree 3 coefficients
mindegcoeff:=0: # default constant coefficients

`s2d/s2d`:=proc (s, x, y)
local serie,nbterms, unkncoef, eqnproto, eqns, homog, i, j, solver,
degcoef, ordereqn, ldeg, zerocoeff, eqn, neqns, trueinds, dim, inds, sol;
    serie:=eval(subs(O=<0>,s));
    nbterms:=op(nops(serie),serie)-1;
    unkncoef:=array(0..maxordereqn+1,0..maxdegcoeff);
    eqnproto:=convert([seq(unkncoef[0,j]*x^j,j=0..maxdegcoeff),
	convert([seq(unkncoef[1,j]*x^j,j=0..maxdegcoeff)],`+`)*y(x),
	seq(convert([seq(unkncoef[i+1,j]*x^j,j=0..maxdegcoeff)],`+`)*
	diff(y(x),x$i),i=1..maxordereqn)],`+`);
    eqns:=series(eval(subs(y(x)=serie,eqnproto)),x,nbterms+1);
    if type([seq(op(2*i-1,serie),i=1..iquo(nops(serie),2))],list(rational))
    then solver:=op(ratsolvelin) else solver:=op(gensolvelin) fi;
    for homog from 0 to 1 do for degcoef from mindegcoeff+1 to maxdegcoeff+1 do
    for ordereqn from minordereqn to maxordereqn do
	if homog=0 then ldeg:=[0,degcoef$(ordereqn+1)]
	else ldeg:=[degcoef$(ordereqn+2)] fi;
	userinfo(2,'gfun','`Trying degree sequence`',ldeg);
	zerocoeff:=seq(seq(unkncoef[i,j]=0,j=op(i+1,ldeg)..maxdegcoeff),
	    i=0..ordereqn+1),seq(seq(unkncoef[i,j]=0,j=0..maxdegcoeff),
	    i=ordereqn+2..maxordereqn+1);
	eqn:=eval(subs(zerocoeff,eqns));
	neqns:=iquo(nops(eqn),2)-1;
	if neqns=0 then next fi;
	trueinds:=indets(eqn,unkncoef[anything,anything]);
	dim:=nops(trueinds);
	if neqns<dim+1 or dim=1 then next fi;
	inds:={seq(seq(unkncoef[i,j],j=0..op(i+1,ldeg)-1),i=0..ordereqn+1)};
	sol:=solver({seq(op(2*i-1,eqn),i=1..neqns)},trueinds);
	if sol = FAIL or type(sol,set(anything=0)) then next fi;
	RETURN(eval(subs({op(sol),seq(j=0,j=inds minus trueinds),zerocoeff},
	    eqnproto)))
    od od od;
    FAIL
end: # `s2d/s2d`

maxdegeqn:=6:	# default maximum 6th degree
mindegeqn:=2:	# default minimum 2nd degree

`s2a/s2a`:=proc (s, x, y)
local serie, nbterms, unkncoef, eqnproto, eqns, i, j, solver,
degcoef, degeqn, ldeg, zerocoeff, eqn, neqns, trueinds, dim, inds, sol;
    serie:=eval(subs(O=<0>,s));
    nbterms:=op(nops(serie),serie)-1;
    unkncoef:=array(0..maxdegeqn+1,0..maxdegcoeff);
    eqnproto:=convert([seq(convert([seq(unkncoef[i,j]*x^j,j=0..maxdegcoeff)],
	`+`)*y(x)^i,i=0..maxdegeqn)],`+`);
    eqns:=series(eval(subs(y(x)=serie,eqnproto)),x,nbterms+1);
    if type([seq(op(2*i-1,serie),i=1..iquo(nops(serie),2))],list(rational))
    then solver:=op(ratsolvelin) else solver:=op(gensolvelin) fi;
    for degcoef from mindegcoeff+1 to maxdegcoeff+1 do
    for degeqn from mindegeqn to maxdegeqn do
	ldeg:=[degcoef$(degeqn+1)]; 
	userinfo(4,'gfun','`trying degree sequence`',ldeg);
	zerocoeff:=seq(seq(unkncoef[i,j]=0,j=op(i+1,ldeg)..maxdegcoeff),
	    i=0..degeqn),seq(seq(unkncoef[i,j]=0,j=0..maxdegcoeff),
	    i=degeqn+1..maxdegeqn+1);
	eqn:=eval(subs(zerocoeff,eqns));
	neqns:=iquo(nops(eqn),2)-1;
	if neqns=0 then next fi;
	trueinds:=indets(eqn,unkncoef[anything,anything]);
	dim:=nops(trueinds);
	if neqns<dim+1 or dim=1 then next fi;
	inds:={seq(seq(unkncoef[i,j],j=0..op(i+1,ldeg)-1),i=0..degeqn)};
	sol:=solver({seq(op(2*i-1,eqn),i=1..neqns)},trueinds);
	if sol = FAIL or type(sol,set(anything=0)) then next fi;
	RETURN(eval(subs({op(sol),seq(j=0,j=inds minus trueinds),zerocoeff},
	    eqnproto)));
    od od;
    FAIL
end: # `s2a/s2a`

listtorec:=proc()
local result, methods, method, u, n, s, unkn, expr;
    if args[1]<>'stamped' then RETURN(listtorec(typecheck(2,args))) fi;
    expr:=args[2];unkn:=args[3];methods:=args[4];
    u:=op(0,unkn);n:=op(unkn);
    for method in methods do
	s:=listtolist('stamped',expr,method);
	userinfo(3,'gfun',`Trying the `,method,s);
	result:=`l2r/l2r`(s,n,u);
	if result<>FAIL then
	    userinfo(2,'gfun','The',method,'`seems to satisfy`',result);
	    RETURN([result,method])
	fi
    od;
    FAIL
end: # listtorec

seriestorec:=proc ()
    if args[1]<>'stamped' then RETURN(seriestorec(typecheck(6,args))) fi;
    RETURN(listtorec('stamped',
	    seriestolist('stamped',args[2],'ogf'),args[3],args[4]))
end: # seriestorec

`l2r/l2r`:=proc (l,n,u)
local unkncoef, solver, homog, i, j, k, p, sys,
degcoef, ordereqn, ldeg, zerocoeff, eqn, trueinds, dim, inds, sol;
    if type(l,list(rational))
	then solver:=op(ratsolvelin) else solver:=op(gensolvelin) fi;
    unkncoef:=array(0..maxordereqn+1,0..maxdegcoeff);
    for i from 0 to maxordereqn+1 do
	p[i]:=convert([seq(unkncoef[i,j]*n^j,j=0..maxdegcoeff)],`+`)
    od;
    eqn:=convert([seq(p[i]*'op'(n+i-1,l),i=1..maxordereqn+1),p[0]],`+`);
    sys:=[seq(subs(n=k,eqn),k=1..nops(l))];
    for homog from 0 to 1 do for degcoef from mindegcoeff+1 to maxdegcoeff+1 do
    for ordereqn from minordereqn to maxordereqn do
	if homog=0 then ldeg:=[0,degcoef$(ordereqn+1)]
	else ldeg:=[degcoef$(ordereqn+2)] fi;
	userinfo(2,'gfun','`Trying degree sequence`',ldeg);
	zerocoeff:=seq(seq(unkncoef[i,j]=0,j=op(i+1,ldeg)..maxdegcoeff),
	    i=0..ordereqn+1),seq(seq(unkncoef[i,j]=0,j=0..maxdegcoeff),
	    i=ordereqn+2..maxordereqn+1);
	eqn:={op(eval(subs(zerocoeff,[op(1..nops(l)-ordereqn,sys)])))}minus{0};
	if eqn={} then next fi;
	trueinds:=indets(eqn,unkncoef[anything,anything]);
	dim:=nops(trueinds);
	if nops(eqn)<dim+1 or dim=1 then next fi;
	inds:={seq(seq(unkncoef[i,j],j=0..op(i+1,ldeg)-1),i=0..ordereqn+1)};
	sol:=solver(eqn,trueinds);
	if sol = FAIL or type(sol,set(anything=0)) then next fi;
	RETURN(eval(subs({k=n,op(sol),seq(j=0,j=inds minus trueinds),zerocoeff}
	    ,{convert([seq(p[i]*u(n+i-1),i=1..ordereqn+1),p[0]],`+`),
	    seq(u(i)=op(i,l),i=1..ordereqn)})))
    od od od;
    FAIL
end: # `l2r/l2r`

## Adapted from solve/linear/integer
ratsolvelin := proc (equations, unknowns)
local eqn, eqns, vars, sol, sols, i, k, l, pivot, var, c, j;
    # Suppress trivial equations and
    # normalize: convert from Q to Z and divide out by the content
    eqns := {seq(l/icontent(l)*sign(l), l=  equations minus {0})};
    # Solve the remaining system using a sparse implementation
    for k while eqns <> {} do
	l:=map(length,[op(eqns)]);
	member(min(op(l)),l,'i');
	eqn := eqns[i];
	vars := indets(eqn) intersect unknowns;
	var := vars[1];
	pivot := coeff(eqn,var,1);
	for j from 2 to nops(vars) do
	    # Eliminate the unknown with the smallest coefficient
	    c := coeff(eqn,vars[j],1);
	    if length(c) < length(pivot) then pivot := c; var := vars[j] fi
	od;
	eqns:=subs(var=-1/pivot*subs(var=0,eqn),subsop(i=NULL,eqns)) minus {0};
	sol[k]:=var,eqn,pivot
    od;
    sols := {};j:=0;
    for i from k-1 by -1 to 1 do
	eqn := - subs( sol[i][1] = 0, sols, sol[i][2] );
# MBM -- note l is a list !     if eqn<>0 then j:=j+1;l[j]:=i fi;
	sols := sols union {sol[i][1] = eqn / sol[i][3]}
    od;
    # Parameterize an infinite solution space
    vars := {op(unknowns)} minus {seq(op(1,i),i=sols)};
    if vars={} then RETURN(sols)
    elif nops(vars)=1 then RETURN({op(subs(op(vars)=1,sols)),op(vars)=1})
    elif select(type,{seq(op(2,i),i=sols)},rational) minus {0} <> {} then
	RETURN({op(subs(seq(i=0,i=vars),sols)),seq(i=0,i=vars)})
    else RETURN(FAIL) # we do not want to treat this
    fi
end: # ratsolvelin

`type/gfunidentity`:=proc(x) type(x,name=name) and op(1,x)=op(2,x) end:

# A simple interface to solve/linear
gensolvelin:=proc ()
local s, t;
    s:=readlib(`solve/linear`)(args);
    t:=select(type,s,'gfunidentity');
    if nops(t)<>1 then RETURN(FAIL) fi;
    RETURN(subs(op(1,op(t))=1,(1=1)=(op(1,op(t))=1),s))
end: # gensolvelin

inicond:=proc (s, eqn, y ,x)
local order, deq, i;
    deq:=select(has,indets(eqn,'diff(anything,identical(x))'),y(x));
    for order while deq<>{y(x)} do deq:=subs(diff(y(x),x)=y(x),deq) od;
    RETURN({eqn,seq((D@@i)(y)(0)=coeff(s,x,i)*i!,i=0..order-2)})
end: # inicond

listtohypergeom:=proc()
local result, methods, method, s, unkn, expr;
    if args[1]<>'stamped' then RETURN(listtohypergeom(typecheck(3,args))) fi;
    expr:=args[2];unkn:=args[3];methods:=args[4];
    for method in methods do
	s:=listtolist('stamped',expr,method);
	userinfo(3,'gfun',`Trying the `,method,s);
	result:=`l2h/l2h`(s,unkn);
	if result<>FAIL then
	    userinfo(2,'gfun','The',method,'`seems to satisfy`',result);
	    RETURN([result,method])
	fi
    od;
    FAIL
end: # listtohypergeom

seriestohypergeom:=proc ()
    if args[1]<>'stamped' then RETURN(seriestohypergeom(typecheck(7,args))) fi;
    RETURN(listtohypergeom('stamped',
	    seriestolist('stamped',args[2],'ogf'),op(0,args[2]),args[3]))
end: # seriestohypergeom

`l2h/l2h`:=proc (l, x)
local a, a0, k, eqn, u, v, w, den, i, z, c;
    a:=l;
    for k while op(1,a)=0 do a:=subsop(1=NULL,a) od;
    a0:=op(1,a);k:=k-1;
    if nops(a)<5 then RETURN(FAIL) fi;
    a:=[seq(op(i,a)/a0,i=2..nops(a))];
    eqn:=normal((6*a[4]*a[1]**2*a[2]+9*a[2]*a[3]**2+6*a[4]*a[1]*a[3]-6*
	a[3]**2*a[1]**2+a[2]**2*a[3]*a[1]-16*a[4]*a[2]**2)*x**2+(-32*a[4]
	*a[2]**2+5*a[2]**2*a[3]*a[1]+6*a[4]*a[1]*a[3]+18*a[4]*a[1]**2*
	a[2]+27*a[2]*a[3]**2-24*a[3]**2*a[1]**2)*x+6*a[2]**2*a[3]*a[1]-
	18*a[3]**2*a[1]**2+12*a[4]*a[1]**2*a[2]);
    if eqn=0 then v:=1
    elif degree(eqn,x)=0 then RETURN(FAIL)
    else v:=op(1,[solve(eqn,x)])
    fi;
    den:=normal(4*a[2]**2*v**2-3*a[3]*v**2*a[1]-a[2]*v**2*a[1]**2-3*a[2]*v*
	a[1]**2+8*a[2]**2*v-3*a[3]*v*a[1]-2*a[2]*a[1]**2);
    if den=0 then RETURN(FAIL) fi;
    w:=normal(-2*(2*a[2]**2*v+4*a[2]**2-3*a[3]*v*a[1]-3*a[3]*a[1])*v)/den;
    if type(w,negint) and w<>0 then RETURN(FAIL) fi;
    z:=normal(-3*a[3]*v*a[1]**2+a[1]*a[2]**2*v+3*a[3]*v*a[2]-3*a[3]*
	a[1]**2+2*a[2]**2*a[1]);
    if z=0 then RETURN(FAIL) fi;
    u:=-normal(2*a[2]**2*v+4*a[2]**2-3*a[3]*v*a[1]-3*a[3]*a[1])*a[1]/z;
    z:=2*z/den;
    userinfo(3,'gfun',`candidate: hypergeom(`,[u,v],[w],z*x,`)`);
    c:=u*(u+1)*(u+2)*(u+3)*v*(v+1)*(v+2)*(v+3)/w/(w+1)/(w+2)/(w+3)*z^4/24;
    for i from 5 to nops(a) do
	c:=c*(u+i-1)*(v+i-1)*z/(w+i-1)/i;
	if c<>op(i,a) then RETURN(FAIL) fi;
    od;
    userinfo(2,'gfun',`hypergeom found, parameters:`,[u,v],[w],z*x);
    RETURN(simplify(a0*x^k*hypergeom([u,v],[w],z*x),hypergeom))
end: # `l2h/l2h`

# This function is very expensive because of its factorization.
# One could avoid that in the style of fullparfrac.
ratpolytocoeff:=proc (fct, x, n)
local decomp, f, i, j;
    if not type(fct,ratpoly(anything,x)) then
	ERROR(`a rational function is expected`) fi;
    f:=normal(fct);
    decomp:=convert(numer(f)/`ratpolytocoeff/pretreat`(
	convert([seq(factor(i,RootOf(i,x)),i=seq(op(1,j),j=op(2,
	    readlib(factors)(denom(f)))))],`*`),x),parfrac,x,true);
    if type(decomp,`+`) then map(`ratpolytocoeff/redfrac`,decomp,x,n)
    else `ratpolytocoeff/redfrac`(decomp,x,n)
    fi
end: # ratpolytocoeff

`ratpolytocoeff/pretreat`:=proc (p,x)
   if not has(p,x) then p
   elif type(p,`*`) then map(procname,p,x)
   elif type(p,`^`) then procname(op(1,p),x)^op(2,p)
   elif degree(p,x)=2 then coeff(p,x,2)*convert(map(proc(x,y) y-x end,
      [solve(p,x)],x),`*`)
   else p
   fi
end: # `ratpolytocoeff/pretreat`

`ratpolytocoeff/redfrac`:=proc (fct, x, n)
local k, den, cte, f;
   if type(fct,polynom(anything,x)) then RETURN(0) fi;
   if type(fct,`*`) then
      f:=select(has,fct,x);
      cte:=fct/f
   else cte:=1; f:=fct
   fi;
   if type(denom(f),`^`) then k:=op(2,denom(f)); den:=op(1,denom(f))
   else k:=1; den:=denom(f)
   fi;
   if subs(x=0,den)=0 then ERROR(`Not a taylor series`) fi;
   cte*binomial(k+n-1,n)*solve(den,x)^(-k-n)/(-coeff(expand(den),x,1))^k
end: # `ratpolytocoeff/redfrac`

guessgf:=proc ()
local interres, y, result, l, x, methods;
    if args[1]<>'stamped' then RETURN(guessgf(typecheck(3,args))) fi;
    l:=args[2];x:=args[3];methods:=args[4];
    # First try to find a rational function
    userinfo(1,'gfun',`Trying to find a rational generating function`);
    result:=listtoratpoly('stamped',l,x,methods);
    if result<>FAIL then RETURN(result) fi;
    # Then trap easy hypergeometrics
    userinfo(1,'gfun',`Trying to find an hypergeometric generating function`);
    result:=listtohypergeom('stamped',l,x,methods);
    if result<>FAIL then RETURN(result) fi;
    # Then a linear differential equation
    userinfo(1,'gfun',`Trying to find a linear differential equation`);
    interres:=listtodiffeq('stamped',l,y(x),methods);
    if interres=FAIL then RETURN(FAIL) fi;
    userinfo(1,'gfun',`Trying to solve it`);
    result:=dsolve(op(1,interres),y(x));
    if result<>FAIL then RETURN([result,op(2,interres)]) fi;
#   if has(eqn,diff(y(x),x$2)) and not has(eqn,diff(y(x),x$3)) then
#	# try to trap hypergeometric equations there
#	result:=solvehypergeom(eqn,y,x);
#	if result<>FAIL then RETURN(result) fi;
#   fi;
    FAIL
end: # guessgf

#solvehypergeom:=proc(eqn,y,x)
#local leqn, t, c1, c2;
#   # pick up the coefficients one by one
#   leqn:=subs(diff(y(x),x,x)=t,eqn);
#   c1:=coeff(leqn,t,1);
#   leqn:=subs(t=0,leqn);
#   leqn:=subs(diff(y(x),x)=t,leqn);
#   c2:=coeff(leqn,t,1);
#   leqn:=subs(t=0,leqn);
#   c3:=coeff(leqn,y(x),1);
#   if subs(y(x)=0,leqn)<>0 then RETURN(FAIL) fi;
#   d1:=convert(c2/c1,parfrac,x);
#   
#end: # solvehypergeom

# returns a differential equation verified by RootOf(P,y)
algfuntodiffeq := proc(pol,yofz)
local y,z,d,i,Y,sys,unk,deq,inits,P,Dy, dP, dy,j, l, ord;
global `type/y^anything`;
    if type(pol,equation) then P:=op(2,pol)-op(1,pol) else P:=pol fi;
    if not type(yofz,function(name)) then ERROR(`invalid arguments`) fi;
    y:=op(0,yofz); z:=op(yofz); dy:=diff(y(z),z);
    if not type(P,polynom(anything,[y,z])) then ERROR(`invalid arguments`) fi;
    if nargs=3 and not type(args[3],set) then
	ERROR(`3rd argument must be a set`) fi;
    if not irreduc(P) then 
	l :=map(procname,select(has,[seq(op(1,i),i=op(2,factors(P)))],y),yofz);
	while nops(l)>1 do l:=subsop(1=hproduct(l[1],l[2],yofz),2=NULL,l) od;
	RETURN(op(l))
    fi;
#    d:=ldegree(P,z);
#    if d<>0 then P := expand(P/z^d) fi;
    d := degree(P,y);
    userinfo(3,'gfun',`degree is `,d);
    if d<=1 then RETURN(expand(subs(y=yofz,P))) fi;
    if not has(P,z) then RETURN(yofz-RootOf(P,y)) fi;
    Y:=table(); # Y[i] = diff(y,z$i) mod P
    userinfo(1,'gfun','computing',D(y),`in terms of`,seq(y^i,i=1..d-1));
    dP:=expand(diff(subs(y=yofz,P),z));
    Y[1]:=reduct(subs(yofz=y,-coeff(dP,dy,0)/coeff(dP,dy,1)),P,y,z);
    if Y[1]=0 then # dP/dz=f(z) P ==> P = g(y) exp(int(f(z),z))
    	userinfo(1,'gfun',`polynom is of the form`,'f'(z)*'g'(y));
	RETURN(yofz-RootOf(primpart(P,y)))
    fi;
    userinfo(2,'gfun',D(y)=Y[1]);
    sys := {Dy[1]=Y[1]};
    `type/y^anything` := subs(_Y=y,
	proc() type([args],[identical(_Y)^anything]) end):
    unk := {y} union indets(sys,`y^anything`);
    for i from 2 to d-1 while i-1<nops(unk) do
    	userinfo(3,'gfun',`#equations computed=`,nops(sys),
	    `#remaining unknowns=`,nops(unk));
	userinfo(2,'gfun','computing',(D@@i)(y),
	    `in terms of`,seq(y^j,j=1..d-1));
	Y[i]:=rem(subs(dy=Y[1],yofz=y,diff(subs(y=yofz,Y[i-1]),z)),P,y);
	userinfo(2,'gfun',(D@@i)(y)=Y[i]);
	sys:={op(sys),Dy[i]=Y[i]};
	unk := unk union indets(Y[i],`y^anything`)
    od;
    userinfo(2,'gfun',`size of linear system is`,length(sys));
    userinfo(4,'gfun',sys);
    deq:=collect(expand(numer(normal(yofz-subs(seq(Dy[i]=diff(yofz,z$i),
	i=1..d-1),op(2,op(select(type,solve(sys,unk),
	identical(y)=anything))))))),[seq(diff(y(z),z$(d-i)),i=1..d-1),y(z)]);
    userinfo(1,'gfun',`differential equation is`,deq);
    if nargs=2 then RETURN(deq) fi;
    userinfo(1,'gfun',`computing initial conditions`);
    ord := initorder(deq,y,z);
    for j from ord do
	l := series(subs(y=series(y(z),z=0,j),P),z=0,j);
	sys := {seq(op(2*i-1,l),i=1..iquo(nops(l),2)-1)} union args[3];
	inits := {solve(sys,indets(sys,initeq(y)))};
	if inits={} then ERROR(`incompatible initial conditions`,sys) fi;
	if nops(inits)>1 then ERROR(`several solutions`,op(inits)) fi;
	inits := select(x -> op(1,x)<>op(2,x),op(inits));
	if nops(inits)=ord then break fi;
    od;
    if inits={} then deq else {deq} union inits fi
end:

# F is a rational function of y, P a polynom in y and z
# returns a polynom Q in y such that F = Q mod P and deg(Q)<deg(P)
reduct := proc(F,P,y)
local g,u,v;
      g := normal(F);
      mygcdex(P,denom(g),y,'u','v');
      rem(numer(g)*v,P,y)
end:

# sg2coeff(y(z))		-> u[n]
# sg2coeff(diff(y(z),z))	-> (n + 1) u[n + 1]
# sg2coeff(diff(y(z),z$2))	-> (n + 1) (n + 2) u[n + 2]
##############################################################
################## SHOULD BE REWRITTEN #######################
##############################################################
sg2coeff := proc(expr,y,z,u,n) 
local aux;
    aux:=subs(y(z)=y,eval(subs(diff=proc(e) option remember; 
	if type(e,`^`) then e*op(1,e) else op(0,e)^2 fi end,expr)));
    if type(aux,`+`) then map(sg2coeff_aux,aux,y,z,u,n) 
    else sg2coeff_aux(aux,y,z,u,n) fi;
end:
##############################################################

sg2coeff_aux := proc(e,y,z,u,n)
local k,j,l;
    if not has(e,y) then RETURN(0) fi;
    k := degree(e,z);  j := degree(e,y)-1;
    e * convert([seq(n-k+l,l=1..j)],`*`) * u(n+j-k)/z^k/y^(j+1)
end:

# returns the smallest i such that u(n+i) appears
minindex := proc(rec,u,n)
local s;
    s:=indets(rec,u(anything));
    if s={} then -infinity else min(op(map(op,s)))-n fi;
end:

# returns the greatest i such that u(n+i) appears
maxindex := proc(rec,u,n)
local s;
    s:=indets(rec,u(anything));
    if s={} then infinity else max(op(map(op,s)))-n fi;
end:

difforder := proc(rec,u,n)
    maxindex(rec,u,n)-minindex(rec,u,n)
end:

verifyinits := proc(expr,z)
local sys;
    sys:=subs(z=0,convert(expr,D));
    if solve(sys,indets(sys,function))=NULL then
	ERROR(`incompatible initial conditions`,sys) fi;
end:

## because solve({x=RootOf(1+_Z^2),y=a},{x,y}) -> NULL
#Solve := proc(sys,unk)
#    subs(`&RootOf`=RootOf,solve(subs(RootOf=`&RootOf`,sys),unk))
#end:

# eq : differential equation (for example output of algfun_to_diffeq)
# returns a recurrence relation for the coefficients of the ordinary
# generating function solution of eq
diffeqtorec := proc(expr,yofz,uofk)
local y,l,z,u,k,eq,inits,inho,i,rec,debut, s,j;
    if not type(yofz,function(name)) then ERROR(`invalid arguments`) fi;
    y:=op(0,yofz); z:=op(yofz);
    verifyinits(expr,z);
    if type(expr,set) then
    	eq:=select(has,expr,z);
	if nops(eq)=0 then ERROR(`found no equation in`,yofz)
	elif nops(eq)>1 then ERROR(`only one equation allowed`) fi;
	inits := expr minus eq;
	eq := op(eq);
    else eq:=expr; inits:={} fi;
    if type(eq,equation) then eq:=op(2,eq)-op(1,eq) fi;
    if not type(uofk,function(name)) then ERROR(`invalid arguments`) fi;
    u:=op(0,uofk); k:=op(uofk);
    if has(eq,k) then ERROR(`invalid arguments`) fi;
    while has(eq,D) do eq:=convert(eq,diff,z) od;
    eq:=expand(numer(normal(eq)));
    inho := eval(subs(y(z)=0,eq));
    rec:=sg2coeff(eq,y,z,u,k);
    userinfo(1,'gfun',`the recurrence is`,rec);
    inits := inits union op(2,initeqs(eq,y,z));
    inits := eqinittorec(Solve(inits,indets(inits,initeq(y))),y,u);
    if inho=0 then debut:=-minindex(rec,u,k)
    else debut := max(degree(inho,z)+1,-minindex(rec,u,k)) fi;
    # u(-1)=u(-2)=...=0
    s:={seq(subs(k=i,rec)+coeff(inho,z,i),i=0..debut-1)};
    inits:=inits union subs(seq(i=0,i=indets(s,u(negint))),s);
    rec := collect(expand(rec),indets(rec,u(anything)));
    l:=inits;
    inits := select(x->op(1,x)<>op(2,x),Solve(inits,indets(inits,u(integer))));
    if inits=NULL then ERROR(`incompatible initial conditions`,l) fi;
#    inits := select(x->op(1,x)<>op(2,x),inits));
    rec := {rec} union inits union {seq(u(i)=(D@@i)(y)(0)/i!,
     i=map(op,{seq(u(j),j=0..difforder(rec,u,k)-1)} 
	minus indets(inits,u(anything))))};
    if nops(rec)=1 then op(rec) else rec fi
end:

# because solve({x=RootOf(1+_Z^2),y=a},{x,y}) -> NULL
Solve := proc(sys,unk)
    subs(`&RootOf`=RootOf,solve(subs(RootOf=`&RootOf`,sys),unk))
end:

eqinittorec := proc(expr,y,u)
local x;
#    subs(seq(x=eqinittorec_aux(x,y,u),
#	x=select(has,indets(expr,function),y)),expr)
    subs(seq(x=(eqinittorec_aux(op(1,x),y,u)=op(2,x)),x=indets(expr,equation)),
	expr)
end:

# y(0) -> u(0)
# D(y)(0) -> u(1)
# (D@@k)(y)(0) -> k!*u(k)
# x -> x
eqinittorec_aux := proc(expr,y,u)
local f;
    if not type(expr,function(0)) then RETURN(expr) fi;
    f:=op(0,expr);
    if f=y then u(0)
    elif f=D(y) then u(1)
    elif op(f)=y and type(op(0,f),`@@`(identical(D),integer)) then
    	op(2,op(0,f));
	"!*u(")
    else expr
    fi;
end:

recinittoeq := proc(expr,u,y)
local x;
    subs(seq(x=recinittoeq_aux(x,u,y),x=indets(expr,u(integer))),expr)
end:

# u(k) -> (D@@k)(y)(0)/k!
recinittoeq_aux := proc(expr,u,y)
    if not type(expr,u(nonnegint)) then ERROR(`invalid arguments`) fi;
    op(expr);
    (D@@")(y)(0)/"!
end:

rectoproc := proc(expr,yofn)
local y,rec,inits,n,eq,T,newinits,zero,i,difforder,mi,symb,m;
    y:=op(0,yofn); n:=op(yofn);
    if type(expr,set) then
    	rec:=select(has,expr,n);
	if nops(rec)<>1 then ERROR(`only one recurrence allowed`) fi;
	inits := expr minus rec;
	rec := op(rec)
    else rec:=expr; inits:={} fi;
    if type(rec,equation) then rec:=op(2,rec)-op(1,rec) fi;
    T:=proc(N) option remember; 
    	if not type(N,nonnegint) then ERROR(`invalid arguments`) fi;
	_REC end;
    T:=subsop(4=NULL,op(T));
    newinits := rectoinits(rec,y,n,inits,'zero','mi','difforder');
    symb:={seq(y(i),i=0..zero+mi+difforder-1)} minus {seq(op(1,i),i=newinits)};
    m:=maxindex(rec,y,n);
    rec:=normal(subs(n=n-m,solve(rec,y(n+m))));
    T:=subs(_REC=rec,y=`proc`.`name`,'N'=n,op(T));
    # following line must appear after substitution of y by procname
    # because initial values can be defined in terms of y(.)
    for eq in newinits do T(op(op(1,eq))):=op(2,eq) od;
    for eq in symb do T(op(eq)):=eq od;	# symbolic initial values
    op(T)
end:

rectoinits := proc(expr,a,n,inits) # zero mi difforder
local i,e,mi,ma,difforder,s,zero,debut, inds;
    mi:=minindex(expr,a,n); ma:=maxindex(expr,a,n); difforder:=ma-mi;
    userinfo(1,'gfun',`difference order =`,difforder);
    inds:=indets(inits,a[integer]);
    if nargs>=5 then # the recurrence is only valid for least index>=debut
    	if type(eval(args[5]),integer) then debut:=eval(args[5])
#	else debut:=-mi # convention that recurrence is valid from u(0)
    	else
	    if inds={} then debut:=-mi 
	    else # convention on recurrences
		debut:=max(-mi,max(op(map(op,inds)))-ma+1)
	    fi
	fi;
    fi;
    e := expand(expr);
    s:=select((x,y)->x>=y,Isolve(coeff(e,a(n+ma)),n),debut);
    if s={} then userinfo(1,'gfun',`dimension of solutions space =`,difforder)
    else userinfo(1,'gfun',difforder,`<= dimension of solutions space <=`,
    	difforder+nops(s))
    fi;
    # we must have zero + ma > max(indices(inits))
    #              zero > max(zero(coeffmax)), zero>=debut
    # usually zero=debut
    zero := max(op(s union {max(-1,op(map(op,inds)))-ma} union {debut-1}))+1;
    userinfo(2,'gfun',`non-singular summation starts from`,n=zero);
    if nargs>=5 then assign(args[5],zero) fi;
    if nargs>=6 then assign(args[6],mi) fi;
    if nargs>=7 then assign(args[7],difforder) fi;
    if zero=0 then RETURN(inits) fi;
    s:={seq(subs(n=i,e),i=debut..zero-1)} union inits;
    s:=solve(s,indets(s,a(anything)));
    if s=NULL then ERROR(`impossible to fulfill initial conditions`) fi;
    RETURN(select(x->op(1,x)<>op(2,x),s))
end:    

# returns the order of a differential equation in yofx
dorder := proc(deqns,yofx)
local eq;
    eq:=deqns;
    while has(eq,D) do eq:=convert(eq,diff,op(yofx)) od;
    RETURN(max(0,op(subs(yofx=0,eval(subs(diff=<x+1>,
	select(has,indets(eq,specfunc(anything,diff)),yofx)))))))
end:

# 1 -> -1
# y(0) -> 0
# D(y)(0) -> 1
# (D@@k)(y)(0) -> k
dorderinit := proc(expr,y)
local i;
    if not has(expr,D) then
	if has(expr,y(0)) then RETURN(0) else RETURN(-1) fi fi;
    RETURN(max(op(eval(subs(D=<x+1>,
	{seq(op(0,i),i=select(has,indets(expr,function(0)),D))}))))-y)
end:

rectodiffeq := proc(expr,aofn,foft)
local e,a,n,mi,zero,f,t,inits,newinits;
    if not type(aofn,function(name)) then ERROR(`invalid arguments`) fi;
    a:=op(0,aofn); n:=op(aofn);
    if type(expr,set) then
    	e:=select(has,expr,n);
	if nops(e)<>1 then ERROR(`only one recurrence allowed`) fi;
	inits := expr minus e;
	e := op(e);
    else e:=expr; inits:={} fi;
    if type(e,equation) then e:=op(2,e)-op(1,e) fi;
    if not type(foft,function(name)) then ERROR(`invalid arguments`) fi;
    f:=op(0,foft); t:=op(foft);
    if f=a then ERROR(`choose a different name for the function`) fi;
    newinits:=rectoinits(e,a,n,inits,'zero','mi');
    e:=expand(subs(newinits,map(rectodiffeq_aux,expand(e),a,n,zero,f,t)));
    if newinits={} then RETURN(e) fi;
    newinits := recinittoeq(newinits,a,f);
    e:= {e} union select(x->op(1,x)<>op(2,x),
	solve(newinits,indets(newinits,initeq(f))));
    if nops(e)=1 then RETURN(op(e)) else RETURN(e) fi
end:

# zD := z*diff
# C n^k a(n+i) -> C zD@@k (a(z)/z^i)
# one sums from "zero" to infinity
rectodiffeq_aux := proc(t,a,n,zero,y,z)
local zD,k,i,ind,m;
    zD := subs(_Z=z,
	proc(e,k) if k=0 then e else procname(_Z*diff(e,_Z),k-1) fi end);
    ind:=op(indets(t,a(anything)));
    if ind=NULL then # C n^k
    	k := degree(t,n);
    	RETURN(t/n^k*zD(1/(1-z),k)-convert([seq(m^k*z^m,m=0..zero-1)],`+`))
    fi;
    k := degree(t/ind,n);
    i := op(ind)-n;
    if k=0 then
	t/ind*(y(z)/z^i-convert([seq(a(m+i)*z^m,m=-i..zero-1)],`+`))
    else
	t/ind/n^k*(zD(y(z)/z^i,k)-
	    convert([seq(m^k*a(m+i)*z^m,m=-i..zero-1)],`+`))
    fi
end:

intoho := proc(e,inits,a,n,inho)
local i, s, inds;
    inds:=indets(inits,a(integer));
    if inds={} then 0 else max(op(map(op,inds))) fi;
    i := max(-minindex(e,a,n),-"-maxindex(e,a,n));
    s:={subs(n=i,e)} union inits;
    s:=select(x->op(1,x)<>op(2,x),solve(s,indets(s,a(anything))));
    RETURN({expand(inho*subs(n=n+1,e)-subs(n=n+1,inho)*e)} union s)
end:

# type(y(0),initeq(y))		-> true
# type(D(y)(0),initeq(y))	-> true
# type((D@@k)(y)(0),initeq(y))	-> true
# otherwise			-> false
`type/initeq` := proc(expr,y)
local f;
    if type(expr,function(0)) then
	f := op(0,expr);
	if f=y or f=D(y) or (type(f,function(identical(y))) and 
	    type(op(0,f),`@@`(identical(D),integer))) then RETURN(true)
	fi;
    fi;
    false
end:

`type/diffeq` := proc(expr,y,x) # type(expr,diffeq(y,x))
local l;
    l:=indets(convert(expr,D),function);
    l:=map(proc(f,_x) if op(f)=_x and type(op(0,f),function)
	then op(0,f) fi end,l,x);
    l:=map(proc(f,_y) if op(f)=_y then op(0,f) fi end,l,y);
    l:=select(f->f=D or type(f,`@@`(identical(D),integer)),l);
    if l={} then false else true fi;
end:

`gfun/skeletton` := proc(expr,aofn,b)
local e,inits,a,n,l,U,N, inho;
    if not type([aofn,b],[function(name),name]) then
	ERROR(`invalid arguments`) fi;
    a:=op(0,aofn); n:=op(aofn);
    if a=b then ERROR(`names should be different`) fi;
    if type(expr,diffeq(a,n)) then
	l:=rectodiffeq(procname(diffeqtorec(expr,aofn,U(N)),
	    U(N),V),V(N),b(op(aofn)));
	RETURN(subs(map(FUN2,{seq(e=e,e=indets(l,V(integer)))}),V=U,l))
    fi;
    if type(expr,set) then
	e:=select(has,expr,n);
	if nops(e)<>1 then ERROR(`only one recurrence allowed`) fi;
	inits := expr minus e;
	e := op(e);
    else e:=expr; inits:={} fi;
    if type(e,equation) then e:=op(2,e)-op(1,e) fi;
    inho := eval(subs(a=<0>,e));
    if inho<>0 then 
    	userinfo(1,'gfun',`non homogeneous recurrence: homogenizing it`);
	RETURN(procname(intoho(e,inits,a,n,inho),aofn,b));
    fi;
    l:=indets(e,function);
    e:=subs(a=b,collect(primpart(map('FUN',expand(e),a,n),l),l));
    inits:={seq(subs(a=b,op(1,l))=op(2,l),l=inits)} union {seq(subs(a=b,l)=l,
	l=indets({seq(op(2,l),l=inits)},a(nonnegint)))};
    if inits<>{} then e:={e} union map('FUN2',inits) fi;
    RETURN(e)
end:

ordtoexp_aux := proc(t,a,n) t*expand(n!/op(op(indets(t,a(anything))))!) end:
exptoord_aux := proc(t,a,n) t*expand(op(op(indets(t,a(anything))))!/n!) end:

gfun[exptoord]:=subs('FUN'=ordtoexp_aux,
    'FUN2'=proc(x) op(1,x)=op(2,x)*op(op(1,x))! end,op(`gfun/skeletton`)):
gfun[ordtoexp]:=subs('FUN'=exptoord_aux,
    'FUN2'=proc(x) op(1,x)=op(2,x)/op(op(1,x))! end,op(`gfun/skeletton`)):


# this procedure does not do exactly the same as gcdex, but is much faster
# it returns g' and affects s,t such that s*a+t*b=g'
# and g'=lambda gcd(a,b) with lambda a rational fraction independant of y
###################################################################
##########  ONE CAN PROBABLY IMPROVE IT MORE   ####################
###################################################################
mygcdex := proc(a,b,y,s,t)
local q,r,u,v,du,dv,alpha,beta,oldalpha,oldbeta;
    u:=expand(a); v:=expand(b);
    oldalpha:=1; oldbeta:=0;
    alpha:=0; beta:=1;
    # u = oldalpha*a + oldbeta*b
    # v = alpha*a + beta*b
    du:=degree(u,y);
    dv:=degree(v,y);
    do
	if du<dv then
	    dv; dv:=du; du:="";
	    v; v:=u; u:="";
	    alpha; alpha:=oldalpha; oldalpha:="";
	    beta; beta:=oldbeta; oldbeta:="";
	fi;
	userinfo(3,'gfun',`degrees of the polynomials`,du,dv);
	userinfo(5,'gfun','polynomials',u,v);
    	q:=lcoeff(u,y)/lcoeff(v,y)*y^(du-dv);
#	r:=expand(u-q*v); does not suffice
	r:=normal(u-q*v);
	if r=0 then break fi;
	oldalpha-q*alpha; oldalpha:=alpha; alpha:="";
	oldbeta-q*beta; oldbeta:=beta; beta:="";
	u:=v;du:=dv;
	v:=r;dv:=degree(v,y);
    od;
    if dv=0 then
	s:=rem(alpha/v,b,y); t:=rem(beta/v,a,y); RETURN(1)
    else
	s:=rem(alpha,b,y); t:=rem(beta,a,y); RETURN(v)
    fi;
end:
###################################################################

# if eq = a[d]*(D@@d)(y)(x)+a[d-1]*(D@@(d-1))(y)(x)+a[d-2]*(D@@(d-2))(y)(x)+...
# (D@@k)(eq) = a[d]*(D@@(k+d))(y)(x) + (ka[d]'+a[d-1])*(D@@(k+d-1))(y)(x) + ...
#	+ (binomial(k,i)*(D@@i)(a[d]) + binomial(k,i-1)*(D@@(i-1))(a[d-1]) 
#		+ ...) (D@@(k+d-i))(y)(x) + ...
# let i0 = least i such that c[i]=
# (binomial(k,i)*(D@@i)(a[d]) + binomial(k,i-1)*(D@@(i-1))(a[d-1]) + ...)
# is not identically null in 0. Number of freedom degrees = k0+1+d-i0 where
# k0 = least root of c[i0] >= i0-d (k0 = -1 if no root)
initeqs := proc(deq,y,z)
local d,i,sys,eq,j,lc;
    d := dorder(deq,y(z));
    eq:=convert(deq,D);
    i := initorder(eq,y,z,d,eq,'lc');
    sys := subs(subs(_T=series(y(z),z,i),_z=z,y=proc(_z) _T end),eq);
    #sys := series(eval(
    #   subs(subs(_T=series(y(z),z,i),_z=z,y=proc(_z) _T end),eq)),z,i+lc);
    sys := series(eval(sys),z,i+lc);
    sys := {seq(op(2*j-1,sys),j=1..iquo(nops(sys),2)-1)};
    RETURN([i,select(x -> op(1,x)<>op(2,x), solve(sys,indets(sys,initeq(y))))])
end:

initorder := proc(deq,y,z) # d eq 'lc'
local d,c,i,eq,j,a,k;
    if nargs>=4 then d:=args[4] else d:=dorder(deq,y(z)) fi;
    if nargs>=5 then eq:=args[5] else eq:=convert(deq,D) fi;
    a:=[seq(coeff(eq,(D@@i)(y)(z),1),i=0..d)];
    if nargs>=6 then assign(args[6],min(op(map(ldegree,a,z)))) fi;
    for i from 0 to d do
	c := expand(subs(z=0,convert([a[d-i+1],
	    seq(binomial(k,i-j)*diff(a[d-j+1],z$(i-j)),j=0..i-1)],`+`)));
	if c<>0 then RETURN(1+d-i+max(-1,op(Isolve(c,k)))) fi
    od;
    ERROR(`should not happen`)
end:

Isolve := proc(eq,n) # returns a set of solutions: {} or {1} or {1,2} ...
local i;
    RETURN({seq(op(2,i),i=indets({isolve(eq,n)},identical(n)=posint))})
end:

# u = [u1 , ... , uk]	A = array(1..k,1..l)
# such that u[i] = sum(A[i,j] e[j])
# output: a linear dependency relation between the u[i] if there is one,
#	  FAIL otherwise
lindep := proc(A,u)
local k,B,i,j,l;
    k := nops(u);
    l := linalg[coldim](A);
    userinfo(2,'gfun',`looking for a linear dependency in a`,k,'x',l,'matrix');
    B := linalg[augment](A,linalg[matrix](k,1,u));
    B := frontend(linalg[gausselim],[op(B)],[{table},{}]);
    # the relation is the last element on the first row that has only zeroes
    j:=1;
    for i to k do
	while B[i,j]=0 do j:=j+1 od;
	if j=l+1 then RETURN(primpart(B[i,j],u)) fi;
    od;
    FAIL
end:

hsum := proc(Eq1,Eq2,yofz)
local y,z,A,d1,d2,d,i,j,f,g,h,V,s,eq1,eq2;
    eq1:=convert(Eq1,D); eq2:=convert(Eq2,D);
    y:=op(0,yofz); z:=op(yofz);
    d1:=dorder(eq1,yofz); d2:=dorder(eq2,yofz);
    userinfo(2,gfun,`computing the sum of two holonomic functions of order`,
	d1,`and`,d2);
    d:=d1+d2; # maximal order of the sum
    if d=0 then RETURN(Eq1*Eq2) fi;
    A := linalg[matrix](d+1,d);
    # column 1: f ... column d1:(D@@(d1-1))(f)
    # column d1+1: g ... column d1+d2=d: (D@@(d2-1))(g)
    V := [seq((D@@i)(f)(z),i=0..d1-1),seq((D@@i)(g)(z),i=0..d2-1)];
    i:=(D@@d1)(y)(z); j:=(D@@d2)(y)(z);
    s := subs(y=f,i=-coeff(eq1,i,0)/coeff(eq1,i)),
	 subs(y=g,j=-coeff(eq2,j,0)/coeff(eq2,j));
    h := f(z)+g(z);
    for i to d+1 do
	h:=expand(subs(s,h));
	for j to d do A[i,j]:=coeff(h,V[j]) od;
	if i<=d then h:=convert(diff(h,z),D) fi;
    od;
    V := [seq((D@@i)(y)(z),i=0..d)];
    collect(lindep(op(A),V),V);
end:

hproduct := proc(Eq1,Eq2,yofz)
local y,z,A,d1,d2,d,i,j,f,g,h,V,s,eq1,eq2;
    eq1:=convert(Eq1,D); eq2:=convert(Eq2,D);
    y:=op(0,yofz); z:=op(yofz);
    d1:=dorder(eq1,yofz); d2:=dorder(eq2,yofz);
    userinfo(2,gfun,`computing the product of two holonomic functions of order`,
	d1,`and`,d2);
    d:=d1*d2; # maximal order of the product
    A := linalg[matrix](d+1,d);
    # (D@@i)(f)*(D@@j)(g) -> V[i+j*d1]
    V := [seq(seq((D@@i)(f)(z)*(D@@j)(g)(z),i=0..d1-1),j=0..d2-1)];
    i:=(D@@d1)(y)(z); j:=(D@@d2)(y)(z);
    s := subs(y=f,i=-coeff(eq1,i,0)/coeff(eq1,i)),
	 subs(y=g,j=-coeff(eq2,j,0)/coeff(eq2,j));
    h := f(z)*g(z);
    for i to d+1 do
	h:=expand(subs(s,h));
	for j to d do A[i,j]:=coeff(coeff(h,op(1,V[j])),op(2,V[j])) od;
	if i<=d then h:=convert(diff(h,z),D) fi;
    od;
    V := [seq((D@@i)(y)(z),i=0..d)];
    collect(lindep(op(A),V),V);
end:

# help

`help/text/gfun`:= TEXT(
`    `,
`HELP FOR: The generating function package`,
`    `,
`CALLING SEQUENCE:`,
`    <function>(args)`,
`    gfun[<function>](args)`,
`    `,
`SYNOPSIS:`,
`- The gfun package contains functions that help handle generating and`,
`holonomic functions.`,
`    `,
`- The functions available are:`,
`    `,
`       algfuntodiffeq  diffeqtorec     rectodiffeq`,
`       rectoproc       hsum            hproduct`,
`       exptoord        ordtoexp        guessgf`,
`       listtoalgeq     listtodiffeq    listtohypergeom`,
`       seriestoalgeq   seriestodiffeq  seriestohypergeom`,
`       listtoratpoly   listtorec       listtoseries`,
`       seriestoratpoly seriestorec     seriestolist`,
`       listtolist      seriestoseries  ratpolytocoeff`,
`    `,
`- For help with a particular function do: ?gfun[<function>], where`,
`<function> is taken from the above list.`,
`    `,
`- An example of using a function from the gfun package is the following.`,
`To compute the generating function of the Fibonacci numbers one`,
`would use the command with(gfun,guessgf) followed by`,
`guessgf([1,1,2,3,5,8,13],x).`,
`    `,
`- Information about the computations that are being done can be obtained by`,
`setting infolevel['gfun'] to anything between 1 and 5.`,
`    `,
`SEE ALSO: with, gfun[parameters].`):

`help/gfun/text/parameters`:=TEXT(
`    `,
`HELP FOR: gfun parameters`,
`    `,
`CALLING SEQUENCE:`,
`    gfun['maxordereqn']                gfun['minordereqn']`,
`    gfun['maxdegcoeff']                gfun['mindegcoeff']`,
`    gfun['maxdegeqn']          gfun['mindegeqn']`,
`    `,
`SYNOPSIS:`,
`- These "global" variables control the actions of various functions of the`,
`gfun package.`,
`    `,
`- For instance, when listtodiffeq is used, only those linear differential`,
`equations whose order lies between gfun['minordereqn'] and gfun['maxordereqn']`,
`and whose coefficients have a degree lying between gfun['mindegeqn'] and`,
`gfun['maxdegeqn'] are tried.`,
`    `,
`- These parameters can be changed like any Maple variable. The quotes are only`,
`necessary if you have loaded the package with the function with, or if one `,
`of your personal variables is called maxordereqn, maxdegcoeff,...`
):

`help/gfun/text/maxordereqn`:=`help/gfun/text/parameters`:
`help/gfun/text/minordereqn`:=`help/gfun/text/parameters`:
`help/gfun/text/maxdegeqn`:=`help/gfun/text/parameters`:
`help/gfun/text/mindegeqn`:=`help/gfun/text/parameters`:
`help/gfun/text/maxdegcoeff`:=`help/gfun/text/parameters`:
`help/gfun/text/mindegcoeff`:=`help/gfun/text/parameters`:

`help/gfun/text/listtoalgeq`:=TEXT(
`    `,
`FUNCTIONS: gfun[listtoalgeq]    - find an algebraic equation for`,
`           gfun[seriestoalgeq]    generating function`,
`    `,
`CALLING SEQUENCES:`,
`      listtoalgeq(l, y(x), <[typelist]>)`,
`    seriestoalgeq(s, y(x), <[typelist]>)`,
`    `,
`PARAMETERS:`,
`    l - a list,`,
`    s - a series,`,
` y(x) - the unknown function and its variable`,
` [typelist] - (optional) a list of generating function types`,
`    `,
`SYNOPSIS:`,
`- The procedures listtoalgeq and seriestoalgeq compute a polynomial`,
`equation in y and x satisfied by the generating function y(x) of the`,
`expressions in l or s, this generating function being of one of the types`,
`specified by typelist. These types must correspond to existing procedures`,
`listtoseries/typename (see listtoseries).`,
`    `,
`- If typelist contains more than one element, these types are tried in`,
`order.`,
`    `,
`- If typelist is not provided, a default ['ogf','egf'] is used.`,
`    `,
`- The output is a list whose first element is the polynomial in y(x) and x`,
`that was found, and whose second element is the type to which it corresponds.`,
`    `,
`- In the current implementation, the maximal degree in y is 6 and the `,
`maximum degree of the coefficients is 3. This can be changed by modifying`,
`the variables gfun['maxdegeqn'] and gfun['maxdegcoeff'].`,
`    `,
`- One should give as many terms as possible in the list l or the series s.`,
`    `,
`EXAMPLE:`,
`> with(gfun):`,
`> l:=[1, 1, 2, 5, 14, 42, 132, 429, 1430, 4862, 16796, 58786]:`,
`> listtoalgeq(l,y(x));`,
`                                            2`,
`                          [1 - y(x) + x y(x) , ogf]`,
`    `,
`> s:=series((1-sqrt(1-4*x)),x,9);    `,
`                  2      3       4       5       6        7        8      9`,
`    s := 2 x + 2 x  + 4 x  + 10 x  + 28 x  + 84 x  + 264 x  + 858 x  + O(x )`,
`    `,
`> seriestoalgeq(s,y(x));           `,
`                                               2`,
`                       [x - 1/2 y(x) + 1/4 y(x) , ogf]`,
`    `,
`SEE ALSO: gfun.`):

`help/gfun/text/seriestoalgeq`:=`help/gfun/text/listtoalgeq`:

`help/gfun/text/listtorec`:=TEXT(
`    `,
`FUNCTIONS: gfun[listtorec]   - find a recurrence for the elements`,
`           gfun[seriestorec]`,
`    `,
`CALLING SEQUENCES:`,
`    listtorec(l, u(n), <[typelist]>)`,
`  seriestorec(s, u(n), <[typelist]>)`,
`    `,
`PARAMETERS:`,
`       l  - a list,`,
`       s  - a series,`,
`     u(n) - the unknown function and its variable`,
` typelist - (optional) a list of generating function types`,
`    `,
`SYNOPSIS:`,
`- The procedures listtorec and seriestorec compute a linear recurrence with`,
`polynomial coefficients satisfied by the expressions in l or s, with`,
`a normalization specified by typelist. These types must correspond to`,
`existing procedures listtoseries/typename (see listtoseries).`,
`    `,
`- If typelist contains more than one element, these types are tried in`,
`order.`,
`    `,
`- If typelist is not provided, a default ['ogf','egf'] is used.`,
`    `,
`- The output is a list whose first element is a set containing the`,
`recurrence and its initial conditions, and whose second element is the`,
`type to which it corresponds.`,
`    `,
`- One should give as many terms as possible in the list l or the series s.`,
`    `,
`EXAMPLE:`,
`> with(gfun):`,
`> l:=[1, 1, 2, 5, 14, 42, 132, 429, 1430, 4862, 16796, 58786]:`,
`> listtorec(l,u(n));`,
`    `,
`                 [{u(1) = 1, (2 - 4 n) u(n) + (n + 1) u(n + 1)}, ogf]`,
`    `,
`> rsolve(op(1,"),u(n));`,
`    `,
`                             (n - 1)`,
`                            4        GAMMA(n - 1/2)`,
`                            -----------------------`,
`                                              1/2`,
`                               GAMMA(n + 1) Pi`,
`    `,
`SEE ALSO: gfun.`):

`help/gfun/text/seriestorec`:=`help/gfun/text/listtorec`:

`help/gfun/text/listtoratpoly`:=TEXT(
`    `,
`FUNCTIONS: gfun[listtoratpoly]  - find a rational generating function`,
`           gfun[seriestoratpoly]`,
`    `,
`CALLING SEQUENCE:`,
`    listtoratpoly(l, x, <[typelist]>)`,
`  seriestoratpoly(s, <[typelist]>)`,
`    `,
`PARAMETERS:`,
`    l - a list,`,
`    s - a series,`,
`    x - the unknown variable,`,
`    [typelist] - (optional) a list of generating function types`,
`    `,
`SYNOPSIS:`,
`- The procedures listtoratpoly and seriestoratpoly compute a rational`,
`function in x for the generating function of the expressions in l or s,`,
`this generating function being of one of the types specified by typelist.`,
`These types must correspond to procedures listtoseries/typename`,
`(see listtoseries).`,
`    `,
`- These functions are frontends to convert[ratpoly] which performs the`,
`actual computation.`,
`    `,
`- If typelist contains more than one element, these types are tried in`,
`order.`,
`    `,
`- If typelist is not provided, a default ['ogf','egf'] is used.`,
`    `,
`- The output is a list whose second element is the type for which a`,
`solution was found, and whose first element is the rational function.`,
`    `,
`- One should give as many terms as possible in the list l or the series s.`,
`    `,
`EXAMPLE:`,
`> with(gfun):`,
`> l:=[1,1,2,3,5,8,13];`,
`    `,
`                          l := [1, 1, 2, 3, 5, 8, 13]`,
`    `,
`> listtoratpoly(l,x);`,
`    `,
`                                      1`,
`                             [- ------------, ogf]`,
`                                           2`,
`                                - 1 + x + x`,
`    `,
`SEE ALSO: gfun, convert[ratpoly].`):

`help/gfun/text/seriestoratpoly`:=`help/gfun/text/listtoratpoly`:

`help/gfun/text/listtohypergeom`:=TEXT(
`    `,
`FUNCTIONS: gfun[listtohypergeom] - find an hypergeometric generating function`,
`         gfun[seriestohypergeom]`,
`    `,
`CALLING SEQUENCE:`,
`    listtohypergeom(l, x, <[typelist]>)`,
`  seriestohypergeom(s, <[typelist]>)`,
`    `,
`PARAMETERS:`,
`     l - a list,`,
`     s - a series,`,
`     x - the unknown variable,`,
`    [typelist] - (optinal) a list of generating function types`,
`    `,
`SYNOPSIS:`,
`- The procedures listtohypergeom and seriestohypergeom compute a 2F1 in x`,
`for the generating function of the expressions in l or s, this generating`,
`function being of one of the types specified by typelist. These types must`,
`correspond to procedures listtoseries/typename (see listtoseries).`,
`    `,
`- The 2F1 that are found have singular points at 0 and infinity but not`,
`necessarily at 1.`,
`    `,
`- If typelist contains more than one element, these types are tried in`,
`order.`,
`    `,
`- If typelist is not provided, a default ['ogf','egf'] is used.`,
`    `,
`- The output is a list whose second element is the type for which an`,
`equation was found, and whose first element is the hypergeometric function.`,
`    `,
`- One should give at least 6 terms in the list l or the series s.`,
`    `,
`EXAMPLE:`,
`> with(gfun):`,
`> l:=[2,5,14,42,132,429,1430];`,
`    `,
`                          l := [2, 5, 14, 42, 132, 429, 1430]`,
`    `,
`> listtohypergeom(l,x);`,
`    `,
`                       x                         3                3`,
` [- 16 -------------------------------- - -------------- + ---------------`,
`                1/2               1/2 3            1/2              1/2  2`,
`       (1 - 4 x)    (1 + (1 - 4 x)   )    (1 - 4 x)    x   (1 - 4 x)    x`,
`     `,
`                 1               1 - 4 x`,
`       - ----------------- + 1/2 -------,                                 ogf]`,
`                    1/2  3           3`,
`         2 (1 - 4 x)    x           x`,
`    `,
`SEE ALSO: gfun, gfun[guessgf].`):

`help/gfun/text/seriestohypergeom`:=`help/gfun/text/listtohypergeom`:

`help/gfun/text/listtodiffeq`:=TEXT(
`    `,
`FUNCTIONS: gfun[listtodiffeq]    - find a linear differential equation`,
`           gfun[seriestodiffeq]    for the generating function`,
`    `,
`CALLING SEQUENCES:`,
`    listtodiffeq(l, y(x),<[typelist]>)`,
`  seriestodiffeq(s, y(x),<[typelist]>)`,
`    `,
`PARAMETERS:`,
`    l   - a list,`,
`    s   - a series,`,
`   y(x) - the unknown function and its variable`,
`   [typelist] - (optional) a list of generating function types`,
`    `,
`SYNOPSIS:`,
`- The procedures listtodiffeq and seriestodiffeq compute a linear`,
`differential equation in y(x) with polynomial coefficients in x satisfied`,
`by the generating function y(x) of the expressions in l or s, this`,
`generating function being of one of the types specified by typelist. These`,
`types must correspond to procedures listtoseries/typename (see listtoseries).`,
`    `,
`- If typelist contains more than one element, these types are tried in`,
`order.`,
`    `,
`- If typelist is not provided, a default ['ogf','egf'] is used.`,
`    `,
`- The output is a list whose second element is the type for which an`,
`equation was found, and whose first element is the differential equation`,
`satisfied by the generating function.`,
`    `,
`- In the current implementation, the maximal order is 2 and the `,
`maximum degree of the coefficients is 3. This can be changed by modifying`,
`the variables gfun['maxordereqn'] and gfun['maxdegcoeff'].`,
`    `,
`- One should give as many terms as possible in the list l or the series s.`,
`    `,
`EXAMPLE:`,
`> with(gfun):`,
`> l:=[1,2,6,22,91,408,1938,9614,49335,260130,1402440,7702632,42975796,243035536`,
`> ,1390594458,8038677054,46892282815,275750636070,1633292229030,9737153323590]:`,
`> listtodiffeq(l,y(x));`,
`                                                              2  /  d      \\`,
`[{D(y)(0) = 2, y(0) = 1, 1 + (- 1 + 5 x) y(x) + (- 3/2 x + 9 x ) |---- y(x)|`,
`                                                                 \\ dx      /`,
`    `,
`                                                    /   2      \\`,
`                                        2        3  |  d       |`,
`                              + (- 1/3 x  + 9/4 x ) |----- y(x)|            },`,
`                                                    |   2      |`,
`                                                    \\ dx       /`,
`    `,
`    ogf]`,
`    `,
`> s:=series(exp(x)/sqrt(1-x),x,7);`,
`    `,
`                           2    53   3   115  4   2947  5   31411  6      7`,
`    s := 1 + 3/2 x + 11/8 x  + ---- x  + --- x  + ---- x  + ----- x  + O(x )`,
`                                48       128      3840      46080`,
`> seriestodiffeq(s,y(x));           `,
`    `,
`                                                  /  d      \\`,
`           [{y(0) = 1, (3/2 - x) y(x) + (- 1 + x) |---- y(x)|}, ogf]`,
`                                                  \\ dx      /`,
`    `,
`SEE ALSO: gfun, gfun[parameters].`):

`help/gfun/text/seriestodiffeq`:=`help/gfun/text/listtodiffeq`:

`help/gfun/text/listtoseries`:=TEXT(
`    `,
`FUNCTIONS: gfun[listtoseries]   - convert a list into a series`,
`           gfun[seriestolist]   - convert a series into a list`,
`           gfun[listtolist]     - convert a list into a list`,
`           gfun[seriestoseries] - convert a series into a series`,
`    `,
`CALLING SEQUENCES:`,
`    listtoseries(l, x, gf)`,
`      listtolist(l, gf)`,
`    seriestolist(s, gf)`,
`  seriestoseries(s, gf)`,
`    `,
`PARAMETERS:`,
`   l     - a list`,
`   s     - a series`,
`   x     - a name`,
`   gf    - (optional)`,
`    `,
`SYNOPSIS:`,
`- The procedure listtoseries creates a power series in the variable x whose`,
`coefficients are derived from the elements of l.`,
`    `,
`- The series is created according to the type gf, which defaults to egf.`,
`    `,
`- The procedures listtolist, seriestolist and seriestoseries perform a`,
`similar task, with different types of input and output.`,
`    `,
`- The following types of generating functions are known:`,
`    `,
`           ogf revogf  lgdogf`,
`           egf revegf  lgdegf.`,
`    `,
`- If type is 'ogf' (ordinary generating function), then the coefficients are`,
`the elements of l.`,
`    `,
`- If type is 'egf' (exponential generating function), then the ith coefficient`,
`is op(i,l)/i!.`,
`    `,
`- If type is 'revogf', then the series is the reciprocal of the ordinary `,
`generating function.`,
`    `,
`- If type is 'revegf', then the series is the reciprocal of the exponential`,
`generating function.`,
`    `,
`- If type is 'lgdogf', then the series is the logarithmic derivative of the`,
`ordinary generating function.`,
`    `,
`- If type is 'lgdegf', then the series is the logarithmic derivative of the`,
`exponential generating function.`,
`    `,
`- The user can define his own type by creating a procedure`,
`gfun[``listtoseries/mytypeofgf``], which takes a list and a variable as input,`,
`and yields a series in this variable.`,
`    `,
`EXAMPLE:`,
`> with(gfun):`,
`> l:=[1, 1, 2, 5, 14, 42, 132, 429, 1430, 4862, 16796, 58786]:`,
`> listtoseries(l,x,'egf');`,
`              2        3         4         5    11   6    143  7    143  8`,
`     1 + x + x  + 5/6 x  + 7/12 x  + 7/20 x  + ---- x  + ---- x  + ---- x`,
`                                                60       1680      4032`,
`    `,
`             2431   9    4199   10     4199   11`,
`          + ------ x  + ------ x   + ------- x`,
`            181440      907200       2851200`,
`    `,
`SEE ALSO: series, convert/series, solve, gfun.`):

`help/gfun/text/listtolist`:=`help/gfun/text/listtoseries`:
`help/gfun/text/seriestolist`:=`help/gfun/text/listtoseries`:
`help/gfun/text/seriestoseries`:=`help/gfun/text/listtoseries`:

`help/gfun/text/guessgf`:=TEXT(
`    `,
`FUNCTION: gfun[guessgf] - find a generating function from a list`,
`    `,
`CALLING SEQUENCE:`,
`    guessgf(L, x, <[typelist]>);`,
`    `,
`PARAMETERS:`,
`    L    - a list,`,
`    x    - a name,`,
`    [typelist] - (optional) a list of generating function types`,
`    `,
`SYNOPSIS:`,
`- The procedure guessgf attempts to find a closed form for the generating`,
`function specified by (L, typelist) (see gfun[listtoseries] for explanations`,
`about typelist), in the variable x.`,
`    `,
`- If typelist contains more than one element, these types are tried in`,
`order.`,
`    `,
`- If typelist is not provided, a default ['ogf','egf'] is used.`,
`    `,
`- This function first tries to find a rational function by listtoratpoly,`,
`then calls listtohypergeom to find some hypergeometric functions,`,
`and then listtodiffeq to find a linear differential equation with polynomial`,
`coefficients which is then passed to dsolve.`,
`    `,
`- One should give as many terms as possible in the list L.`,
`    `,
`EXAMPLE:`,
`> with(gfun):`,
`> guessgf([1,2,4,7,11,16,22],x);`,
`                                          2`,
`                                 1 - x + x`,
`                              [- ----------, ogf]`,
`                                         3`,
`                                  (x - 1)`,
`    `,
`SEE ALSO: gfun.`):

`help/gfun/text/ratpolytocoeff`:=TEXT(
`    `,
`FUNCTION: gfun[ratpolytocoeff] - computes the nth coefficient of a`,
`                                 rational function`,
`    `,
`CALLING SEQUENCE:`,
`    ratpolytocoeff(f, x, n);`,
`    `,
`PARAMETERS:`,
`    f    - a rational function in x,`,
`    x,n  - names.`,
`    `,
`SYNOPSIS:`,
`- The procedure ratpolytocoeff computes the expression for the nth`,
`coefficient of the Taylor expansion about the origin of f as a function`,
`of x.`,
`    `,
`EXAMPLE:`,
`> with(gfun):`,
`> ratpolytocoeff(1/(1-x-x^2),x,n);`,
`    `,
`       1/2               1/2 (- 1 - n)        1/2               1/2 (- 1 - n)`,
`  1/5 5    (- 1/2 + 1/2 5   )          - 1/5 5    (- 1/2 - 1/2 5   )`,
`    `,
`SEE ALSO: gfun.`):

`help/gfun/text/algfuntodiffeq` := 
TEXT(`FUNCTION: gfun[algfuntodiffeq] - compute a differential equation for`,
`                                   an algebraic function`,
`   `, 
`CALLING SEQUENCE: algfuntodiffeq(p,y(z),inits)`,
`   `, 
`PARAMETERS:`,`   p - a polynom in y and z (or a polynomial equation)`,
`   y - the name of the algebraic function`,
`   z - the generic variable`,`   `,
`   inits - (optional) a set of initial conditions`,
`    `,
`SYNOPSIS:   `,
`- The polynom p defines an algebraic function, RootOf(p,y) in Maple terms.`,
`  This procedure computes a linear differential equation with polynomial`,
`  coefficients verified by the function y(z). This equation is of order at`,
`  most degree(p,y)-1.`,
``,
`- The output contains initial conditions in zero (y(0), D(y)(0), and so on),`,
`  and can thus be given directly to dsolve. In general, y(0) is a RootOf a`,
`  polynom, D(y)(0) a rational expression in y(0), (D@@2)(y)(0) a rational`,
`  expression in y(0),D(y)(0), and so on.`,
``,
`- When the algebraic function defined by the polynom is not defined in z=0,`,
`  the procedure returns NULL.`,
`   `,
`EXAMPLES:`,
`> with(gfun):`,
`> algfuntodiffeq(y=1+z*y^2,y(z));`,
`    `,
`                          2  /  d      \\`,
`                (- z + 4 z ) |---- y(z)| + (- 1 + 2 z) y(z) + 1`,
`                             \\ dz      /`,
`    `,
`> algfuntodiffeq(56*a^3+7*a^3*y^3-14*y*z,y(z),{y(0)=-2});`,
`    `,
`                  /   2      \\`,
`        9      3  |  d       |              /  d      \\  2                1`,
` {(108 a  - 2 z ) |----- y(z)| + z y(z) - 3 |---- y(z)| z , D(y)(0) = - ----,`,
`                  |   2      |              \\ dz      /                    3`,
`                  \\ dz       /                                          3 a`,
`    `,
`     y(0) = -2}`,
`    `,
`> algfuntodiffeq(y^5*(1-x)=1,y(x),{y(0)=1});`,
`    `,
`                                          /  d      \\`,
`                   {y(0) = 1, (- 5 + 5 x) |---- y(x)| + y(x)}`,
`                                          \\ dx      /`,
`    `,
`> dsolve(",y(x));`,
``,
`                                            1`,
`                             y(x) = - ------------`,
`                                               1/5`,
`                                      (- 1 + x)`,
`    `,
`SEE ALSO: dsolve, gfun[diffeqtorec]`):

`help/gfun/text/diffeqtorec` := TEXT(
`FUNCTION: gfun[diffeqtorec] - convert a linear differential equation into`,
`                                a recurrence`,
``,
`CALLING SEQUENCE: diffeqtorec(deqns,y(z),u(n))`,
``,
`PARAMETERS:`,
`   deqns - a linear differential equation with polynomial coefficients`,
`           or a set of one differential equation and initial conditions`,
`   y,z   - the name and the variable of the function`,
`   u,n   - the name and the index of the recurrence`,
``,
`SYNOPSIS:   `,
`- Let f be a power series solution of the differential equation. If u(n) is`,
`  the n-th Taylor coefficient of f around zero, the procedure outputs a`,
`  linear recurrence for the numbers u(n), with rational coefficients in n.`,
``,
`- The syntax is the same as that of dsolve.`,
``,
`- Combined with gfun[algfuntodiffeq], this procedure produces a linear`,
`  recurrence for the Taylor coefficients of an algebraic function.`,
``,
`EXAMPLES:`,
`> with(gfun):`,
`> diffeqtorec(y(z)=a*diff(y(z),z),y(z),v(n));`,
``,
`                    {v(0) = y(0), (a n + a) v(n + 1) - v(n)}`,
``,
`> algfuntodiffeq(y=1+z*(y^2+y^3),y(z),{}):`,
`> diffeqtorec(",y(z),u(m));`,
``,
`  {u(2) = 10, u(1) = 2,`,
``,
`            2                              2`,
`      (- 4 m  - 10 m - 6) u(m + 1) + (- 2 m  + 7 m - 6) u(m - 2)`,
``,
`                              2                             2`,
`           + (42 m - 21 - 18 m ) u(m - 1) + (43 m + 9 + 46 m ) u(m), u(0) = 1}`,
``,
`SEE ALSO: gfun[algfuntodiffeq], gfun[rectodiffeq]`):

`help/gfun/text/rectoproc` := TEXT(
`FUNCTION: gfun[rectoproc] - convert a recurrence into a function`,
``,
`CALLING SEQUENCE: rectoproc(eqns, u(n))`,
``,
`PARAMETERS:`,
`   eqns - a single equation or a set of equations`,
`   u,n  - the name and index of the recurrence`,
``,
`SYNOPSIS:`,
`- The procedure outputs a Maple procedure that, given a non negative integer`,
`  n as input, gives the n-th term u(n) of the recurrence.`,
``,
`- When the first terms of the recurrence are not explicitely supplied, they`,
`  are represented symbolically.`,
``,
`EXAMPLES:`,
`> with(gfun):`,
`> fib := rectoproc({f(i)=f(i-1)+f(i-2),f(0)=0,f(1)=1},f(i));`,
``,
`fib := proc(i)`,
`       options remember;`,
`           if not type(i,nonnegint) then ERROR(``invalid arguments``) fi;`,
`           procname(i-1)+procname(i-2)`,
`       end`,
``,
`> fib(100);`,
``,
`                             354224848179261915075`,
``,
`> fib2 := rectoproc(f(i)=f(i-1)+f(i-2),f(i)):`,
`> fib2(100);`,
``,
`            354224848179261915075 f(1) + 218922995834555169026 f(0)`,
``,
`SEE ALSO: gfun[rectodiffeq]`):


`help/gfun/text/rectodiffeq` := TEXT(
`FUNCTION: gfun[rectodiffeq] - convert a linear recurrence into`,
`                                a differential equation`,
``,
`CALLING SEQUENCE: rectodiffeq(eqns, u(n), f(z))`,
``,
`PARAMETERS:`,
`   eqns - a single equation or a set of equations`,
`   u,n  - the name and index of the recurrence`,
`   f,z  - the name and variable of the function`,
``,
`SYNOPSIS:`,
`- Let f be the generating function associated to the sequence (u(n)):`,
`  f(z)=sum(u(n)*z^n,n=0..infinity). The procedure outputs a linear`,
`  differential equation with polynomial coefficients verified by f.`,
``,
`- The input syntax is the same as for rsolve: the first argument should be a`,
`  single recurrence relation or a set containing one recurrence relation and`,
`  boundary conditions.`,
``,
`- The output is either a single differential equation, or a set containing`,
`  a differential equation and initial conditions.`,
``,
`EXAMPLES:`,
`> with(gfun):`,
`> rectodiffeq(a(n)=a(n-1)+a(n-2),a(n),y(z));`,
``,
`                                   2`,
`                y(z) z - a(0) z + z  y(z) - y(z) + a(0) + a(1) z`,
``,
`> rectodiffeq({u(n)=k*u(n-1)+5*n*u(n-2),u(0)=0,u(1)=0},u(n),f(t));`,
``,
`                               /  d      \\  3            2`,
`    {D(f)(0) = 0, k f(t) t + 5 |---- f(t)| t  + 10 f(t) t  - f(t), f(0) = 0}`,
`                               \\ dt      /`,
``,
`> diffeqtorec(",f(t),u(n));`,
``,
`             {u(0) = 0, u(1) = 0, k u(n - 1) + 5 n u(n - 2) - u(n)}`,
``,
`> rectodiffeq((n-10)*u(n+1)-u(n),u(n),y(z));`,
``,
`  (5)             (2)             (9)             (8)`,
`{D   (y)(0) = 0, D   (y)(0) = 0, D   (y)(0) = 0, D   (y)(0) = 0,`,
``,
`     (7)             (4)             (6)             (3)`,
`    D   (y)(0) = 0, D   (y)(0) = 0, D   (y)(0) = 0, D   (y)(0) = 0,`,
``,
`     (10)                                              /  d      \\`,
`    D    (y)(0) = 0, D(y)(0) = 0, - z y(z) - 11 y(z) + |---- y(z)| z, y(0) = 0}`,
`                                                       \\ dz      /`,
``,
`> dsolve(",y(z));`,
``,
`                                            11`,
`                             y(z) = exp(z) z   _C1`,
``,
`SEE ALSO: gfun[diffeqtorec]`):

`help/gfun/text/ordtoexp` := TEXT(
`FUNCTION: gfun[ordtoexp] - convert from ordinary to exponential`,
``,
`CALLING SEQUENCE: ordtoexp(expr,a(n),b)`,
``,
`PARAMETERS:`,
`   expr - a linear recurrence with polynomial coefficients`,
`   a,n  - the name and index of the recurrence`,
`   b    - the name of the new recurrence`,
``,
`SYNOPSIS:`,
`- If (a(n),n=0..infinity) is the sequence of numbers defined by the recurrence`,
`  expr, the procedure computes the recurrence for the numbers b(n) such that`,
`  b(n)=a(n)/n!.`,
``,
`- When the input recurrence is inhomogeneous, the difference order is`,
`  increased until an equivalent homogeneous recurrence is found. The output`,
`  recurrence is always homogeneous.`,
``,
`- If expr is a linear differential equation with polynomial coefficients,`,
`  let f=sum(a(n)*z^n,n=0..infinity) be a series solution of the equation,`,
`  the procedure outputs a linear differential equation verified by the`,
`  series g=sum(a(n)*z^n/n!,n=0..infinity), that is the Borel transform of f.`,
``,
`EXAMPLES:`,
`> with(gfun):`,
`> ordtoexp(a(n)=a(n-1)+a(n-2),a(n),b);`,
``,
`                     2`,
`                 (- n  + n) b(n) + (n - 1) b(n - 1) + b(n - 2)`,
``,
`> exptoord(",b(n),a);`,
``,
`                           a(n - 1) + a(n - 2) - a(n)`,
``,
`> ordtoexp({a(n)=a(n-1)+a(n-2)+n,a(0)=0,a(1)=1},a(n),b);`,
``,
`         4    2`,
`    {(- n  + n ) b(n + 1) + (1 - n) b(n - 1) + (- n - 1) b(n - 2)`,
``,
`                2      3`,
`          + (- n  + 2 n  - n) b(n),                              b(1) = 1,`,
``,
`        b(2) = 3/2, b(0) = 0}`,
``,
`SEE ALSO: gfun[exptoord]`):
        
`help/gfun/text/exptoord` := TEXT(
`FUNCTION: gfun[exptoord] - convert an exponential into ordinary recurrence`,
``,
`CALLING SEQUENCE: exptoord(expr,a(n),b)`,
``,
`PARAMETERS:`,
`   expr - a linear recurrence with polynomial coefficients`,
`   a,n  - the name and index of the recurrence`,
`   b    - the name of the new recurrence`,
``,
`SYNOPSIS:`,
`- If (a(n),n=0..infinity) is the sequence of numbers defined by the recurrence`,
`  expr, the procedure computes the recurrence for the numbers b(n) such that`,
`  b(n)=n!*a(n).`,
``,
`- When the input recurrence is inhomogeneous, the difference order is`,
`  increased until an equivalent homogeneous recurrence is found. The output`,
`  recurrence is always homogeneous.`,
``,
`- If expr is a linear differential equation with polynomial coefficients,`,
`  let f=sum(a(n)*z^n,n=0..infinity) be a series solution of the equation,`,
`  the procedure outputs a linear differential equation verified by the`,
`  series g=sum(a(n)*n!*z^n,n=0..infinity), that is the Laplace transform of f.`,
``,
`EXAMPLES:`,
`> with(gfun):`,
`> rec:={a(n)=a(n-1)+a(n-2),a(0)=0,a(1)=1}:`,
`> exptoord(rec,a(n),b);`,
``,
`             2`,
`          {(n  - n) b(n - 2) + b(n - 1) n - b(n), b(1) = 1, b(0) = 0}`,
``,
`> rectodiffeq(",b(n),f(t));`,
``,
`               /  d      \\  2              /  d      \\  3           2`,
` {- f(t) + t + |---- f(t)| t  + f(t) t + 4 |---- f(t)| t  + 2 f(t) t`,
`               \\ dt      /                 \\ dt      /`,
``,
`         /   2      \\`,
`         |  d       |  4`,
`       + |----- f(t)| t ,                                            f(0) = 0,`,
`         |   2      |`,
`         \\ dt       /`,
``,
`     D(f)(0) = 1}`,
``,
`> ordtoexp(",f(t),g);`,
``,
`                                          2`,
`             {- g(t) + t + g(t) t + g(t) t , D(g)(0) = 1, g(0) = 0}`,
``,
`> diffeqtorec(",g(t),a(n));`,
``,
`                {a(1) = 1, a(0) = 0, a(n - 1) + a(n - 2) - a(n)}`,
``,
`SEE ALSO: gfun[ordtoexp]`):

`help/gfun/text/hsum` := TEXT(
`FUNCTION: gfun[hsum]     - compute the sum of two holonomic functions`,
`          gfun[hproduct] - compute the product of two holonomic functions`,
``,
`CALLING SEQUENCE: hsum(eq1,eq2,y(z))`,
`                  hproduct(eq1,eq2,y(z))`,
``,
`PARAMETERS:`,
`   eq1,eq2 - two linear differential equations with polynomial coefficients`,
`   y,z     - the name of the holonomic function and the generic variable`,
``,
`SYNOPSIS:`,
`- If f (resp. g) is an holonomic function solution of eq1 (resp. eq2),`,
`  gfun[hsum] outputs a linear differential equation verified by f+g, and`,
`  gfun[hproduct] outputs a linear differential equation verified by f*g.`,
``,
`- The order of the output equation is at most the sum of the input equations`,
`  orders for gfun[hsum], and their product for gfun[hproduct].`,
``,
`EXAMPLES:`,
`> with(gfun):`,
`> eq1 := D(y)(x)-y(x):`,
`> eq2 := (1+x)*(D@@2)(y)(x)+D(y)(x):`,
`> hsum(eq1,eq2,y(x));`,
``,
`                                   2   (2)                     2   (3)`,
`   (- 3 - x) D(y)(x) + (1 - 2 x - x ) D   (y)(x) + (2 + 3 x + x ) D   (y)(x)`,
``,
`> hproduct(eq1,eq2,y(x));`,
``,
`                                                       (2)`,
`               y(x) x + (- 2 x - 1) D(y)(x) + (1 + x) D   (y)(x)`
):
`help/gfun/text/hproduct` := `help/gfun/text/hsum`:

#save `gfun.m`;
#quit
