# Package for formal power series.
#
# Calling sequence:   PS [ <funcname> ] ( <arguments> )
#
#   or else do:   with(PS);   or   with(PS,fcn1,fcn2,...);
#      and then use the simpler calling sequence
#         funcname ( <arguments> )
#
#            D. Gruntz, Apr 1992
#

macro(variable   = `PS/variable`):
macro(coeffmap   = PS[coeffmap] ):
macro(coeffzip   = PS[coeffzip] ):
macro(coeffshift = PS[coeffshift] ):
macro(powcreate1 = `PS/powcreate/algebraic`):
macro(powcreate2 = `PS/powcreate/equation`):
macro(powcoeff   = PS[powcoeff]):
macro(powcreate  = PS[powcreate]):
macro(fix        = `PS/fixedpoint`):
macro(odeprod    = `PS/odeprod` ):

# Creation of PS:
##################

powcreate := proc (p) local coeffproc, i; 
   if   type(p, procedure) then coeffproc := subsop(3=remember,p)
   elif type(p, algebraic) then RETURN(powcreate1(args))
   elif type(p, equation)  then RETURN(powcreate2(args))
   else ERROR(`wrong arguments`)
   fi;
   
   if nargs<2 then ERROR(`wrong arguments`) fi:

   if type(args[2],{list,set}) then
      for i to nops(args[2]) do
         coeffproc(op(1,args[2][i])) := op(2,args[2][i]):
      od
   fi:
   if not type(args[nargs], name) then ERROR(`last argument must be a name`) fi;

   coeffproc('VAR') := args[nargs];
   _powerseries(eval(coeffproc)): 
end: # powcreate

powcreate1 := proc(expr) local x, f, n, d, e, ind;
   if nargs=2 then x := args[2]
   else ind := indets(expr, name);
      if nops(ind)=1 then x := op(ind) else ERROR(`specify expansion variable`) fi;
   fi:

   if   type(expr, polynom(anything, x)) then PS[powpoly](expr, x)
   elif type(expr, series) and op(0,expr) = x then
      PS[add](
         PS[powpoly](convert(expr, polynom), x),
         powcreate(subs('m' = op(nops(expr), expr), 
            proc(k) if k < m then 0 else 'unknown' fi end), 
            x
         )
      )
   elif type(expr, `+`) then PS[add](op(map(procname, expr, x)))
   elif type(expr, `*`) then 
      if not has(op(1,expr), x) then # this is a constant
         PS[multconst](procname(expr/op(1,expr), x), op(1,expr))
      else n := numer(expr); d := denom(expr);
         if d <> 1 then 
            PS[quotient](procname(n,x), procname(d,x))
         else 
            PS[multiply](op(map(procname, expr, x)))
         fi
      fi
   elif type(expr, `^`) then e := op(2,expr);
      if not has(e, x) then
         PS[`^`](procname(op(1,expr), x), e)
      else
         PS[`^`](procname(op(1,expr), x), procname(e, x))
      fi;
   elif type(expr, function) then f := `pow`.(op(0,expr));
      if assigned(PS[f]) then
         PS[f]( procname(op(1,expr), x) )
      else
         ERROR(`don't know PS of `,op(0,expr))
      fi
   else
      ERROR(`don't know how to develop `, expr, `about `,x,`=0`);
   fi;
end: # powcreate1      

powcreate2 := proc(eqns)
# creates formal power series with initial conditions = init, 
#   and general term = recur( a(n-1)...a(n-m)i,n )
   local i, init, z, n, p, recur, temp, temp2, a, var;

   if type(args[nargs],name) then var := args[nargs] else var := 'x' fi;
   if nargs>1 then init:={args[2..nargs]} minus {var} else init:={} fi;
   a := op(0,op(1,eqns));
   map( 
      proc(x) if not (type(x,`=`) and type(op(1,x),function)) then x fi end, 
      init union {eqns}
   ) union 
   map(
      proc(x,a) if op(0,op(1,x))<>a or (not type(op(1,op(1,x)),integer)) then x fi end, 
      init, a
   );
   if " <> {} then ERROR(`invalid arguments`); fi;

   n := op(1,op(1,eqns)); recur := op(2,eqns);
   temp := map( 
      proc(x, a) if op(0,x)=a and nops(x)=1 then op(1,x) fi end,
      indets(recur,function), a 
   );
   if temp <> {} then
      if member(n, temp) then ERROR( a(n),`included in its own defn`) fi;
      temp2 := {};
      for z in temp do 
         if not has(z,n) then 
            temp2 := temp2 union { a(z) }
         elif type(-z+n,integer) then
            temp2 := temp2 union { seq(a(i), i=0..-z+n-1) }
         fi;
      od;
      map( type, subs(init, temp2), numeric);
      if has(", false) then ERROR(`insufficient initial conditions`) fi;
   fi;
   init := map( proc(x) [op(op(1,x)),op(2,x)] end, init);

   p := subs('_F'=recur, a=p, proc(n) option remember; _F end);
   powcreate(p, init, var);
end: # powcreate2

PS[powpoly] := proc (poly, var) local L, i, ci;
   if not ( nargs=2 and type(poly, polynom(anything,var)) ) then
      ERROR (`wrong type or number of arguments`)
   fi; L := NULL;
   for i from ldegree(poly, var) to degree(poly, var) do
      ci := coeff(poly, var, i); if ci <> 0 then L := L, [i, ci] fi
   od;
   powcreate(proc(k) 0 end, [L], var)
end: # powpoly

# Output of PS:
################

variable := proc(p) op(p)('VAR') end:

PS[tpsform] := proc(s) local var, deg, k, t;
   if nargs>=2 and type(args[2], integer) then deg := args[2] else deg := Order-1 fi;
   var := variable(s);

   t := 0:
   for k from 0 to deg do t := t + powcoeff(s,k) * var^k od: 
   series(t+O(var^k), var, deg+1)
end: # tpsform

`print/_powerseries` := proc(p):
   PS[tpsform](_powerseries(p))
end:

# Type testing: type(., powerseries);
######################################

`type/powerseries` := proc(arg) type(arg, _powerseries(procedure)) end:

# Operations on coefficients of a powerseries:
###############################################

powcoeff := proc(s, n);
   if type(n, integer) then
      if n < 0 then ERROR(`improper index`)
      else eval(op(1,s)(n))
      fi;
   else ERROR(`wrong arguments`) fi
end: # powcoeff

coeffmap := proc(f, u);
   powcreate(subs(['_f'=f, '_u'=u, 'arg'=args[3..nargs]], 
      proc(k) option remember; _f(powcoeff(_u,k), arg) end),
      variable(u)
   )
end: # coeffmap

coeffzip := proc(f, u, v) ;
   powcreate(subs(['_f'=f, '_u'=u, '_v'=v], 
      proc(k) option remember; 
         _f(powcoeff(_u,k),powcoeff(_v,k)) 
      end),
      variable(u)
   )
end: # coeffzip

coeffshift := proc(s, n);
   if n <> 0 then
      powcreate(subs(['_s'=s, '_n'=n],
         proc(k) if k < _n then 0 else powcoeff(_s,k-_n) fi end),
         variable(s)
      )
   else s fi;
end: # coeffshift

# fixedpoint computation:
##########################

PS[fixedpoint] := proc(F, var, C) local e, p, i;
   if not type(F,procedure) then
      ERROR(`first argument must be of type procedure`) fi:
   if nops([op(1,eval(F))]) > 1 then RETURN(fix(args)) fi:
   
   p('VAR') := var:
   if nargs>=3 and type(C,list) then for i to nops(C) do p(i-1) := C[i] od fi;
   e := F(_powerseries(p)):
   p := op(1,e):
   e:
end:

fix := proc(F, var) local e, p, n, unique, i;
   if not type(F,procedure) then
      ERROR(`first argument must be of type procedure`) fi:
      
   unique := proc() local p; p end;
   n := nops([op(1,eval(F))]); p := [seq(unique(), i=1..n)];
   for i to n do p[i]('VAR') := var od:
   e := F(op(map(_powerseries, [seq(p[i], i=1..n)]))):
   for i to n do assign(p[i], op(1,e[i])) od:
   e:
end:

# simple arithmetic on powerseries {+,-,*,/,^}:
################################################

PS[add] := proc (u, v) local w;
   if variable(u) <> variable(v) then ERROR(`different variables`) fi;
   w := coeffzip(<x+y>, u, v);
   if nargs > 2 then PS[add](w, args[3..nargs]) else w fi
end: # add

PS[subtract] := proc (u,v);
   if variable(u) <> variable(v) then ERROR(`different variables`) fi;
   coeffzip(<x-y>, u, v)
end: # subtract

PS[negative] := proc (u);
   coeffmap(<-x>, u)
end: # negative

PS[multconst] := proc(u, const);
   coeffmap(subs('_const'=const, x -> _const*x), u)
end: # multconst

PS[multiply] := proc (u, v) local w;
   if variable(u) <> variable(v) then ERROR(`different variables`) fi;
   w := powcreate(subs(['_u'=u, '_v'=v],
           proc(k) local i,t: t := 0:
              for i from 0 to k do
                 t := t + powcoeff(_u,i ) * powcoeff(_v,k-i)
              od:  t
           end),
           variable(u)
        );
   if nargs > 2 then PS[multiply](w, args[3..nargs]) else w fi
end: # multiply

PS[inverse] := proc(v) local w; # u0 = 1, uk = 0, k>0
   if powcoeff(v,0)=0 then
      ERROR(`inverse will have pole at zero`)
   fi;
   w := powcreate(subs(['_v'=v, '_w'=w],
          proc(k) local i, t: 
             if k=0 then t := 1 else t := 0 fi:
             for i from 0 to k-1 do
                t := t - powcoeff(_w,i) * powcoeff(_v,k-i)
             od: 
             t / powcoeff(_v,0)
          end),
          variable(v)
       );
end: # inverse

PS[quotient] := proc (U, V) local u, v, m, mu, mv, w;
   if variable(U) <> variable(V) then ERROR(`different variables`) fi;
   for mu from 0 while powcoeff(U, mu)=0 do od;
   for mv from 0 while powcoeff(V, mv)=0 do od;
   m := min(mu,mv);
   if m <> 0 then
      u := coeffshift(U,-m);
      v := coeffshift(V,-m);
   else u := U; v := V;
   fi;
   if powcoeff(v,0)=0 then
      ERROR(`inverse will have pole at zero`)
   fi;
   w := powcreate(subs(['_u'=u, '_v'=v, '_w'=w],
           proc(k) local i, t: t := 0:
              for i from 0 to k-1 do
                 t := t + powcoeff(_w,i) * powcoeff(_v,k-i)
              od: (powcoeff(_u, k)-t) / powcoeff(_v,0)
           end),
           variable(U)
        );
end: # quotient

PS[`^`] := proc(s, alpha) local m, u1, u2:   
   if type(alpha, powerseries) then
      PS[powexp](PS[multiply](alpha, PS[powlog](s)))
   else
      if   alpha=0 then RETURN(powcreate(proc(k) 0 end,[[0,1]],variable(s)))
      elif alpha=1 then RETURN(s)
      else 
         for m from 0 while powcoeff(s, m)=0 do od;

         if not type(alpha*m,integer) then 
            ERROR(`unable to power`) fi:
   
         u1 := coeffshift(s,-m):
         u2 := PS['fixedpoint'](subs(['_s'=u1, '_alpha'=alpha],
                  e -> PS[powint](
                          PS[multconst](
                             PS[multiply](
                                e,
                                PS[quotient](PS[powdiff](_s),_s)
                             ),
                             _alpha
                          ),
                          powcoeff(_s,0)^_alpha)
            ),
            variable(s)
         );
         coeffshift(u2,alpha*m)
      fi
   fi
end: # `^`

# integration and differentiation of powerseries:
##################################################

PS[powdiff] := proc (s) 
   powcreate(subs('_s'=s, k -> powcoeff(_s,k+1)*(k+1)), variable(s))
end: # powdiff

PS[powint] := proc (s) local c;
   if nargs=1 then c := 0 else c := args[2] fi;
   powcreate(subs('_s'=s, k -> powcoeff(_s,k-1)/(k)), [[0,c]], variable(s))
end: # powint

# powerseries reversion and composition:
#########################################

PS[reversion] :=  proc(u) local u1, v, powersOfU;
   if not ((powcoeff(u,0) = 0) and (powcoeff(u,1) <> 0)) then
      ERROR(`can not reverse`) fi;

   powersOfU := subs(['_u'=u], 
      proc(k) option remember; PS[`^`](_u,k)
   end); # powersOfG

   u1 := 1/powcoeff(u,1);
   v := powcreate(subs(['_u'=u, '_u1'=u1, '_v'=v, '_ui'=powersOfU],
           proc(k) local s, i:
              if   k=0 then 0
              elif k=1 then _u1
              else s := 0;
                 for i to k-1 do
                    s := s + powcoeff(_v,i) * powcoeff(_ui(i),k)
                 od;
                 -(_u1^k)*s
              fi
           end),
           variable(u)
        )
end: # reversion

PS[compose] := proc(F, G) local powersOfG; # F(G(x))
   if powcoeff(G,0) <> 0 then
      ERROR(`G`,`must have coefficient of x^0 = zero`) fi;

   powersOfG := subs(['_G'=G], 
      proc(k) option remember; PS[`^`](_G,k)
   end); # powersOfG

   powcreate(subs(['_G'=G, '_F'=F, '_Gi'=powersOfG],
      proc(k) local s, i;
         if k=0 then powcoeff(_F,0)
         else s := 0;
            for i to k do
               s := s + powcoeff(_F,i) * powcoeff(_Gi(i), k)
            od; s
         fi
      end),
      variable(G)
   )
end:

# elementary functions on powerseries {exp, log, ln, sin, cos, tan, sinh, cosh, tanh} :
########################################################################################

PS[powexp] := proc (s)
   PS['fixedpoint'](subs('_s'=s, 
      e -> PS[powint](PS[multiply](e, PS[powdiff](_s)),
                               exp(powcoeff(_s,0)))
      ), 
      variable(s)
   )
end: # powexp

PS[powlog] := proc (s)
   if powcoeff(s,0)=0 then 
      ERROR(`singularity encountered`)
   fi;
   PS[powint](PS[quotient](PS[powdiff](s), s),
                       log(powcoeff(s,0)))
end: # powlog 
PS[powln] := ":

PS[powtan] := proc(s) local t0:
   t0 := traperror(tan(powcoeff(s,0)));
   if t0=lasterror then ERROR(lasterror) fi;
   PS['fixedpoint'](subs(['_s'=s,'_t0'=t0],
        e -> PS[powint](
                PS[multiply](
                   PS[add](powcreate(proc(k) 0 end,[[0,1]],variable(_s)),
                                    PS[multiply](e,e)
                   ),
                   PS[powdiff](_s)
                ),
                _t0
             )
      ),
      variable(s)
   )
end: # powtan

PS[powsin] := proc(s);
   PS['fixedpoint'](subs([_s=s], 
      (s,c) -> ( 
         PS[powint](
            PS[multiply](c,PS[powdiff](_s)), 
            sin(powcoeff(_s,0))
         ),  
         PS[powint](
            PS[multiply](PS[negative](s),PS[powdiff](_s)),
            cos(powcoeff(_s,0))
         )
      )),
      variable(s)
   )[1]
end:
PS[powcos] := proc(s);
   PS['fixedpoint'](subs([_s=s], 
      (s,c) -> ( 
         PS[powint](
            PS[multiply](c,PS[powdiff](_s)), 
            sin(powcoeff(_s,0))
         ),  
         PS[powint](
            PS[multiply](PS[negative](s),PS[powdiff](_s)),
            cos(powcoeff(_s,0))
         )
      )),
      variable(s)
   )[2]
end:

PS[powsinh] := proc(s);
   PS['fixedpoint'](subs([_s=s], 
      (s,c) -> ( 
         PS[powint](
            PS[multiply](c,PS[powdiff](_s)), 
            sinh(powcoeff(_s,0))
         ),  
         PS[powint](
            PS[multiply](s,PS[powdiff](_s)),
            cosh(powcoeff(_s,0))
         )
      )),
      variable(s)
   )[1]
end:
PS[powcosh] := proc(s);
   PS['fixedpoint'](subs([_s=s], 
      (s,c) -> ( 
         PS[powint](
            PS[multiply](c,PS[powdiff](_s)), 
            sinh(powcoeff(_s,0))
         ),  
         PS[powint](
            PS[multiply](s,PS[powdiff](_s)),
            cosh(powcoeff(_s,0))
         )
      )),
      variable(s)
   )[2]
end:

PS[powtanh] := proc(s) local t1, t2;
   t1 := PS[powexp](s);
   t2 := PS[powexp](PS[negative](s));
   PS[quotient](PS[subtract](t1,t2), PS[add](t1,t2))
end:

# evaluation of expressions including powerserues:
###################################################

PS[evalpow] := proc (expr) local var, n, d, e, f, f1, f2;
   if nargs=2 then 
      var := args[2] 
   else
      var := map(variable, indets(expr, powerseries));
      if nops(var) > 1 then ERROR(`series in more than one variable`) else var := op(var) fi;
   fi;

   if not has(expr, _powerseries) then powcreate(expr, var)
   elif type(expr, powerseries) then expr
   elif type(expr, `+`) then
      PS[add](op(map(procname, [op(expr)], var)))
   elif type(expr, `*`) then
      if not (has(op(1,expr), _powerseries) or has(op(1,expr), var)) then # this is a constant
         PS[multconst](procname(expr/op(1,expr),var), op(1,expr))
      else
         n := numer(expr); d := denom(expr);
         if d <> 1 then 
            PS[quotient](procname(n,var), procname(d,var))
         else
            PS[multiply](op(map(procname, [op(expr)], var)))
         fi;
      fi
   elif type(expr, `^`) then e := op(2,expr);
      if has(e,var) then
         PS[`^`](procname(op(1,expr), var), procname(op(2,expr), var))
      else
         PS[`^`](procname(op(1,expr), var), e)
      fi;
   elif type(expr, function) then f := op(0,expr);
      if type(f, powerseries) then
         PS[compose](f, procname(op(1,expr),var))
      elif type(f, name) then 
         f1 := `pow`.(substring(f,2..length(f))):
         f2 := `pow`.f:
         if   assigned(PS[f]) then
            PS[f ](op(map(procname, [op(expr)], var)))
         elif assigned(PS[f1]) then
            PS[f1](op(map(procname, [op(expr)], var)))
         elif assigned(PS[f2]) then
            PS[f2](op(map(procname, [op(expr)], var)))
         elif f='prev' then PS[reversion](op(map(procname, [op(expr)], var)))
         elif f='pinv' then PS[inverse]  (op(map(procname, [op(expr)], var)))
         elif f='pquo' then PS[quotient] (op(map(procname, [op(expr)], var)))
         elif f='pmul' then PS[multiply] (op(map(procname, [op(expr)], var)))
         elif f='psub' then PS[subtract] (op(map(procname, [op(expr)], var)))
         elif f='pneg' then PS[negative] (op(map(procname, [op(expr)], var)))
         else
            ERROR(`unknown function `,f)
         fi
      else ERROR(`dont know`);
      fi
   else ERROR(`dont know`)
   fi
end:

# solving of ordinary differential equations:
##############################################

PS[ode] := proc(f, C) local var,i;
# y[n] = f(x,y,y', ..., y[n-1]), y(0)=C[1], y'(0)=C[2], y''(0)=C[3], y[n-1](0)=C[n]
# Examples:
# ode((x,y) -> y, [1], x);
# ode((x,y,yp,ypp,yppp) -> y, [3/2,-1/2,-3/2,1/2], x);
# ode( (x,y) -> y/(y*y+x), [2], x);
# ode( (x,y,yp) -> 1/(1+y^2), [0,1] );
   if nargs=3 then var := args[3] else var := 'x' fi:
   PS['fixedpoint'](
      subs(['_f'=f, '_C'=C, '_x'=var],
         y -> odeprod(_f, _C, _x, y, nops(_C))),
      var,
      [seq(C[i]/(i-1)!, i=1..nops(C))]
   )
end:
odeprod := proc(f, C, x, y, n) local argseq, yp;
   if C=[] then
      argseq := y; yp := y;
      to n-1 do
         yp := PS[powdiff](yp);
         argseq := argseq, yp;
      od;
      PS[evalpow](f(x, argseq), x);
   else
      PS[powint](
         odeprod(f, [C[2..nops(C)]], x, y, n),
         C[1]
      )
   fi;
end:

`help/text/PS` :=  TEXT(
`HELP FOR: The formal power series package PS`,
`      `,
`CALLING SEQUENCE:`,
`   PS[<function>](args)`,
`   <function>(args)`,
`      `,
`SYNOPSIS:   `,
`- The PS package contains functions to create and manipulate formal`,
`  power series. It differs from the Maple library powseries package in many `,
`  points:   `,
`    o the user sees the powerseries and must not always use tpsform`,
`    o it is also possible to input an expression, e.g.`,
`         > powcreate(sin(x), x);`,
`                                  3          5      6`,
`                         x - 1/6 x  + 1/120 x  + O(x )`,
`    o the functions sin, cos, tan, sinh, cosh, tanh are implemented,`,
`      also on powerseries, as well as functions in the powseries package.`,
`    o New is also the function fixedpoint which computes the fixedpoint`,
`      of a given map, e.g. exp(x) can be defined by`,
`         > fixedpoint(t -> powint(t, 1), x);`,
`                           2        3         4          5      6`,
`              1 + x + 1/2 x  + 1/6 x  + 1/24 x  + 1/120 x  + O(x )`,
`    o It is no longer necessary to name each intermediate powerseries`,
`      created in a calculation, e.g. taking the logarithm of the above `,
`      powerseries gives`,
`         > powlog(");`,
`                                          6`,
`                                   x + O(x )`,
`   `,
`- All functions from the Maple powseries package are supported (powsolve`,
`  has been renamed to ode). For more information on a particular function`,
`  see also powseries[<function>].`,
`   `,
`- The following functions are new or have been extended:`,
`   `,
`       powcreate   evalpow   coeffmap   coeffshift  powcoeff`,
`       powint      ode       ^          fixedpoint   `,
`       powcos      powcosh   powsin     powsinh     powtan     powtanh  `,
`   `,
`- See also PS[<function>], where <function> may be one of`,
`   `,
`           powcreate, evalpow, coeffmap, coeffshift, `,
`           powcoeff,  powint,  ode,      fixedpoint. `,
`   `,
`- It is no longer possible to ask for the symbolic form of the k-th`,
`  coefficient.`,
`   `,
`- Please mail any bug reports to gruntz@inf.ethz.ch.`,
`   `,
`SEE ALSO:  powseries, with, series`,
`   `
):

`help/PS/text/powcreate` := TEXT(
`FUNCTION: PS[powcreate] - create formal power series`,
`   `,
`CALLING SEQUENCE:`,
`   PS[powcreate](eqns)`,
`   PS[powcreate](expr, var)`,
`   PS[powcreate](proc, var)`,
`   PS[powcreate](proc, init, var)`,
`      `,
`PARAMETERS:`,
`   eqns  - expression sequence of equations`,
`   expr  - algebraic expression`,
`   proc  - procedure N -> expr`,
`   var   - name`,
`   init  - (optional) list of lists`,
`      `,
`SYNOPSIS:   `,
`- The function powcreate creates a formal power series whose coefficients are`,
`  specified by eqns or by the given expression.`,
`     `,
`- For the first form see powseries[powcreate]. With the last argument optionally`,
`  the name of the variable may be specified (per default x).`,
`   `,
`- With the second form the given expression is developed in a powerseries in`,
`  var about 0. If var is not specified, indets(expr, name) is chosen, if it is`,
`  unique.   `,
`   `,
`- The third form acceps a procedure which knows how to compute the k'th `,
`  coefficient of the powerseries. Optional a list of coefficients may be passed`,
`  as second argument. These values will override the common definition and must`,
`  be given as a list of lists of two elements, the first one the index and the`,
`  second one the value of that coefficient.`,
`   `,
`EXAMPLES:   `,
`> powcreate(t(n)=1/n!,t(0)=1);`,
`   `,
`                           2        3         4          5      6`,
`              1 + x + 1/2 x  + 1/6 x  + 1/24 x  + 1/120 x  + O(x )`,
`   `,
`> powcreate(cos(sin(x+x^2)));`,
`   `,
`                            2    3         4        5      6`,
`                   1 - 1/2 x  - x  - 7/24 x  + 5/6 x  + O(x )`,
`   `,
`> powcreate(k -> 1/k^2, [[0,1]], w);`,
`   `,
`                           2        3         4         5      6`,
`              1 + w + 1/4 w  + 1/9 w  + 1/16 w  + 1/25 w  + O(w )`,
`   `,
`SEE ALSO:  powseries[powcreate]`
):
`help/text/powcreate` := ":

`help/PS/text/evalpow` := TEXT(
`FUNCTION: PS[evalpow] - general evaluator for power series expressions`,
`      `,
`CALLING SEQUENCE:`,
`   PS[evalpow](expr)`,
`      `,
`PARAMETERS:`,
`   expr - any arithmetic expression involving formal power series and constants`,
`      `,
`SYNOPSIS:   `,
`- The function evalpow evaluates any arithmetic expression involving power`,
`  series and constants.  It returns an unnamed power series.`,
`      `,
`- The following operators can be used: +, -, *, /, ^ .`,
`   `,
`- The following functions can be used: sin,  cos,  tan,  exp, log, ln,`,
`                                       sinh, cosh, tanh, `,
`      `,
`- Also, functions may be composed with each other.  For example, f(g) can be`,
`  used.      `,
`      `,
`- The other functions that can be used in evalpow are:`,
`      `,
`        pexp                     pinv                   plog`,
`        pmul                     pneg                   prev (reversion)  `,
`        pdiff (first derivative) pint (first integral)  pquo (quotient)   `,
`        psub  (subtract)`,
`           `,
`EXAMPLES:   `,
`> f := powcreate(exp(x)):`,
`> g := powcreate(x/(1-x/2)):`,
`> h := powcreate(1/(1-x/5)):`,
`> evalpow(f^3+g-h/f):`,
`                     233  2   7273  3   52171  4   313717  5      6`,
`            24/5 x + --- x  + ---- x  + ----- x  + ------ x  + O(x )`,
`                      50      1500      15000      150000`,
`      `,
`> t := powcreate(n->1/(n-5),[[5,0]],x):`,
`> u := powcreate(u(n)=u(n-1)+u(n-2),u(0)=0,u(1)=1):`,
`> evalpow(t(u)+sin(u));`,
`                                2        3    59   4   157  5      6`,
`          - 1/5 - 1/4 x - 7/12 x  - 5/3 x  - ---- x  - --- x  + O(x )`,
`                                              12        12`,
`> evalpow(sin(u)^2+cos(u)^2);`,
`                                          6`,
`                                   1 + O(x )`,
`> # as expected!`,
`   `,
`SEE ALSO:  powseries[evalpow], powcreate`
):
`help/text/evalpow` := ":

`help/PS/text/coeffmap` := TEXT(
`FUNCTION: PS[coeffmap] - apply a procedure to each coefficient of a`,
`                         power series.`,
`   `,
`CALLING SEQUENCE:`,
`   PS[coeffmap](fcn, ps, arg_2, ..., arg_n)`,
`   `,
`PARAMETERS:`,
`   fcn   - a procedure or a name`,
`   ps    - name`,
`   arg_i - (optional) further arguments to fcn`,
`      `,
`SYNOPSIS:   `,
`- The map function applies fcn to the coefficients of the power series ps.`,
`      `,
`- If fcn takes more than one argument, they are to be specified as additional`,
`  arguments, arg_2, arg_3,..., arg_n, which are simply passed through as the`,
`  second, third ,..., n-th arguments to fcn.`,
`   `,
`EXAMPLES:  `,
`> powcreate(sin(cos(x)));`,
`                              2                               4      6`,
`         sin(1) - 1/2 cos(1) x  + (1/24 cos(1) - 1/8 sin(1)) x  + O(x )`,
`   `,
`> coeffmap(evalf, ", 5);`,
`                                     2            4      6`,
`                    .84147 - .27015 x  - .082667 x  + O(x )`,
`   `,
`SEE ALSO:  map, proc, powcreate`
):
`help/text/coeffmap` := ":

`help/PS/text/ode` := TEXT(
`FUNCTION: PS[ode] - solve ordinary differential equations as power series`,
`      `,
`CALLING SEQUENCE:`,
`   PS[ode](fnc, init, var)`,
`      `,
`PARAMETERS:`,
`   fnc  - function`,
`   init - list`,
`   var  - name`,
`      `,
`SYNOPSIS:   `,
`- The function powsolve solves a ordinary differential equation.`,
`   `,
`- The differential equation is given as a function f`,
`    diff(y(x), x$n) = f( x, y(x), diff(y(x),x), ..., diff(y(x), x$(n-1) )`,
`      `,
`- The result of that function is evaluated using evalpow.`,
`      `,
`- The initial n conditions are specified in the list init. `,
`    init = [ y(0), D(y)(0), D(D(y))(0), ..., (D@@(n-1))(y)(0) ]`,
`      `,
`- The solution returned is a formal power series in the variable var which`,
`  represents the infinite series solution.`,
`      `,
`EXAMPLES:   `,
`> # diff(y(x),x)=y(x), y(0)=1`,
`> ode((x,y) -> y, [1], x);`,
`                           2        3         4          5      6`,
`              1 + x + 1/2 x  + 1/6 x  + 1/24 x  + 1/120 x  + O(x )`,
`   `,
`> # diff(y(x),x$4)=y(x), y(0)=3/2, D(y)(0)=-1/2, D(D(y))(0)=-3/2, D(D(D(y)))(0)=1/2`,
`> ode((x,y,yp,ypp,yppp) -> y, [3/2,-1/2,-3/2,1/2], x);`,
`                             2         3         4          5      6`,
`          3/2 - 1/2 x - 3/4 x  + 1/12 x  + 1/16 x  - 1/240 x  + O(x )`,
`   `,
`> # diff(y(x),x)=y(x)/(y(x)^2+x), y(0)=2`,
`> ode( (x,y) -> y/(y*y+x), [2], x);`,
`                            2         3          4          5      6`,
`           2 + 1/2 x - 1/8 x  + 1/16 x  - 5/128 x  + 7/256 x  + O(x )`,
`   `,
`> # diff(y(x),x$2)=1/(1+y(x)^2), y(0)=0, D(y)(0)=1`,
`> ode( (x,y,yp) -> 1/(1+y^2), [0,1] );`,
`                              2         4         5      6`,
`                     x + 1/2 x  - 1/12 x  - 1/20 x  + O(x )`,
`   `,
`SEE ALSO:  powcreate[powsolve], dsolve`
):
`help/text/ode` := ":

`help/PS/text/powint` := TEXT(
`FUNCTION: PS[powint] - integration of a formal power series`,
`      `,
`CALLING SEQUENCE:`,
`   PS[powint](p)`,
`   PS[powint](p, c)`,
`      `,
`PARAMETERS:`,
`   p - formal power series`,
`   c - (optional) algebraic expression`,
`      `,
`SYNOPSIS:   `,
`- The function powint(p) returns the formal power series which is the integral`,
`  of p with respect to the variable of the power series.`,
`      `,
`- Optionally an integration constant may specified as second argument. The `,
`  default value for the integration constant is 0.`,
`      `,
`EXAMPLES:   `,
`> powcreate(1/(1-x), x);`,
`                                2    3    4    5      6`,
`                       1 + x + x  + x  + x  + x  + O(x )`,
`   `,
`> powint(");`,
`                          2        3        4        5      6`,
`                 x + 1/2 x  + 1/3 x  + 1/4 x  + 1/5 x  + O(x )`,
`   `,
`> powint(", 3);`,
`                         2        3         4         5      6`,
`                3 + 1/2 x  + 1/6 x  + 1/12 x  + 1/20 x  + O(x )`,
`   `,
`SEE ALSO:  powcreate`
):
`help/text/powint` := ":

`help/PS/text/powcoeff` := TEXT(
`FUNCTION: PS[powcoeff] - extract a coefficient of a powerseries`,
`      `,
`CALLING SEQUENCE:`,
`   PS[powcoeff](p, n)`,
`      `,
`PARAMETERS:`,
`   p - formal power series`,
`   n - an integer`,
`      `,
`SYNOPSIS:   `,
`- The powcoeff function extracts the coefficient of x^n in the powerseries p.`,
`      `,
`EXAMPLES:   `,
`> e := powcreate(exp(x), x);`,
`                           2        3         4          5      6`,
`              1 + x + 1/2 x  + 1/6 x  + 1/24 x  + 1/120 x  + O(x )`,
`   `,
`> powcoeff(e, 3);`,
`                                      1/6`,
`   `,
`> seq(powcoeff(e, i), i=0..10);`,
`    1, 1, 1/2, 1/6, 1/24, 1/120, 1/720, 1/5040, 1/40320, 1/362880, 1/3628800`,
`   `,
`> powcoeff(e,50);`,
`      1/30414093201713378043612608166064768844377641568960512000000000000`,
`   `,
`SEE ALSO: coeff, powcreate`
):
`help/text/powcoeff` := ":

`help/PS/text/coeffshift` := TEXT(
`FUNCTION: PS[coeffshift] - multiplication of a powerseries by a monomial`,
`      `,
`CALLING SEQUENCE:`,
`   PS[coeffshift](p, n)`,
`      `,
`PARAMETERS:`,
`   p - formal power series`,
`   n - an integer`,
`      `,
`SYNOPSIS:   `,
`- The coeffshift function "shifts" the coefficients of the powerseries p by n,`,
`  i.e. multiplies p by x^n (if n >= 0).`,
`   `,
`- If n<0, then the coefficients are shifted to the left and all coefficients`,
`  of negative powers of x are set to 0.`,
`      `,
`EXAMPLES:   `,
`> e := powcreate(exp(x), x);`,
`                           2        3         4          5      6`,
`              1 + x + 1/2 x  + 1/6 x  + 1/24 x  + 1/120 x  + O(x )`,
`   `,
`> coeffshift(e,3);`,
`                             3    4        5      6`,
`                            x  + x  + 1/2 x  + O(x )`,
`   `,
`> evalpow(e*x^3);`,
`                             3    4        5      6`,
`                            x  + x  + 1/2 x  + O(x )`,
`   `,
`> coeffshift(e,-4);`,
`                            2           3            4             5      6`,
`    1/24 + 1/120 x + 1/720 x  + 1/5040 x  + 1/40320 x  + 1/362880 x  + O(x )`,
`   `,
`SEE ALSO: powcreate, evalpow, `
):
`help/text/coeffshift` := ":

`help/PS/text/fixedpoint` := TEXT(
`FUNCTION: PS[fixedpoint] - multiplication of a powerseries by a monomial`,
`      `,
`CALLING SEQUENCE:`,
`   PS[fixedpoint](fnc, var)`,
`   PS[fixedpoint](fnc, var, init)`,
`      `,
`PARAMETERS:`,
`   fnc  - procedure`,
`   var  - name`,
`   init - list`,
`      `,
`SYNOPSIS:   `,
`- The fixedpoint of the procedure fnc which maps a powerseries to a powerseries is`,
`  computed. The result is a powerseries in the variable var. Optionally the coeff-`,
`  itients p(0) .. p(n-1) may be specified in the list init.`,
`   `,
`REFERENCE:   `,
`S.M. Watt, A fixedpoint method for power series computations, ISSAC 1988, p 206,`,
`Lecture Notes in Computer Science, Vol. 358`,
`   `,
`EXAMPLES:   `,
`> # definition of exp(x)  { e = int(e,x)+1 }`,
`> fixedpoint(e -> powint(e, 1), x);`,
`                           2        3         4          5      6`,
`              1 + x + 1/2 x  + 1/6 x  + 1/24 x  + 1/120 x  + O(x )`,
`   `,
`> # definition of 1/(1-x) { s = x*s+1 }`,
`> fixedpoint(s -> evalpow(1+coeffshift(s,1)), x);`,
`                                2    3    4    5      6`,
`                       1 + x + x  + x  + x  + x  + O(x )`,
`     `,
`SEE ALSO: powint, evalpow, coeffshift`
):
`help/text/fixedpoint` := ":

#save PS,
#     powcreate1, 
#     powcreate2,
#     variable,
#     odeprod,
#     fix,
#     `print/_powerseries`,
#     `type/powerseries`,
#
#     `help/text/PS`,
#     `help/PS/text/powcreate`,
#     `help/PS/text/evalpow`,
#     `help/PS/text/powcoeff`,
#     `help/PS/text/coeffmap`,
#     `help/PS/text/coeffshift`,
#     `help/PS/text/powint`,
#     `help/PS/text/fixedpoint`,
#     `help/PS/text/ode`,
#
#     `help/text/powcreate`,
#     `help/text/evalpow`,
#     `help/text/powcoeff`,
#     `help/text/coeffmap`,
#     `help/text/coeffshift`,
#     `help/text/powint`,
#     `help/text/fixedpoint`,
#     `help/text/ode`,
#
#     `PS.m`;
#
#quit
