##
##    Title:   fullparfrac
##    Created: Wed Jul 31
##    Author:  Bruno Salvy
##    <salvy@rully.inria.fr>
##
## Description: full partial fraction decomposition.
##  Based on an idea in M. Bronstein's "Formulas for Series Computations".

fullparfrac:=proc (ratfun, x)
local f, p, q, c, d, dq, r, i, j, nd, n, u, den, c1, c2, result, l,  derivd, k,
   Dn, res, h, Q, polpart, al;
   f:=normal(ratfun);
   if not type(f,ratpoly(rational,x)) then
      ERROR(`Not a rational function`,ratfun)
   fi;
   p:=numer(f);
   q:=denom(f);
   Q:=q;
   polpart:=quo(p,q,x);
   c:=content(q,x,'q');
   # Square-free decomposition
   d:=[0];
   for i while degree(q,x)>0 do
      dq:=diff(q,x);
      q:=gcd(q,dq,'r');
      d:=[r,quo(op(1,d),r,x),op(subsop(1=NULL,d))]
   od;
   # For all the elements of the decomposition,
   # compute the residues of positive order
   nd:=nops(d);
   res:=[];
   for i to nd-1 do
      if op(i,d)=1 then next fi;
      n:=nd-i;                           #  order of the factor
      Dn:=op(i,d);
      # Precompute a list of derivatives of Dn
      derivd:=[diff(Dn,x)];
      for j to n-1 do derivd:=[op(derivd),diff(op(j,derivd),x)] od;
      # Compute the residue of largest order
      den:=quo(Q,Dn^n,x);
      h:=p/u(x)^n/den;                   #  h(x,u(x))
      gcd(p,den*(diff(Dn,x)^n),'c1','c2');
      l:=[c1/c2/c];                      #  l is the list of residues
      # Compute the other ones
      for j to n-1 do                    #  j=n-m
         h:=normal(diff(h,x)/j);
         gcd(op(subs(seq(diff(u(x),x$(j-k))=op(j-k+1,derivd)/(j-k+1),
            k=0..j-1),u(x)=op(1,derivd),[numer(h),denom(h)])),'c1','c2');
         l:=[c1/c2/c,op(l)]
      od;
      res:=[[Dn,l],op(res)]
   od;
   # Format the result
   result:=polpart;
   for i while i<=nops(res) do
      if degree(op(1,op(i,res)),x)=1 then
         al:=solve(op(1,op(i,res)),x);
         result:=result+convert([seq(subs(x=al,op(j,op(2,op(i,res))))/(x-al)^j,
            j=1..nops(op(2,op(i,res))))],`+`)
      else
         al:=RootOf(op(1,op(i,res)),x);
         l:=traperror(map(evala,map(Normal,subs(x=al,op(2,op(i,res))))));
         if l=lasterror then
            if op(1,[l])=`reducible RootOf detected.  Substitutions are` then
               for al in op(2,[l]) do
                  if type(op(2,al),numeric) then
                     res:=[op(res),[x-op(2,al),op(2,op(i,res))]]
                  else
                     res:=[op(res),[subs(_Z=x,op(op(2,al))),op(2,op(i,res))]]
                  fi
               od
            else ERROR(l)
            fi
         else
            for j to nops(l) do
               if type(op(j,l),numeric) then
                  result:=result+op(j,l)*Sum(1/(x-'alpha')^j,'alpha'=al)
               else
                  result:=result+Sum(subs(al='alpha',op(j,l))/(x-'alpha')^j,
                     'alpha'=al)
               fi
            od
         fi
      fi
   od;
   result
end: # fullparfrac
fparfrac := eval(fullparfrac):

`help/text/fullparfrac` := TEXT(
`FUNCTION: fullparfrac - full partial fraction decomposition`,
`CALLING SEQUENCE:`,
`  fullparfrac(expr,var)`,
`PARAMETERS:`,
`  expr - a rational function over the rationals`,
`  var  - its variable`,
`SYNOPSIS:   `,
`- The fullparfrac function computes a partial fraction decomposition of expr`,
`over C, i.e. the denominators are linear.`,
`EXAMPLES:   `,
`> f:=expand(x^5*(x-1))/expand((x^2+x+1)^2*(x-2)^3);`,
`   `,
`                                        6    5`,
`                                       x  - x`,
`                 f := ----------------------------------------`,
`                       7      6      5      3      2`,
`                      x  - 4 x  + 3 x  + 9 x  - 6 x  - 4 x - 8`,
`   `,
`> fullparfrac(f,x);`,
`       /              179          135\\   /            37           20 \\`,
`       |  -----    - ---- alpha + ----|   |  -----    ---- alpha + ----|`,
`       |   \\         2401         2401|   |   \\       1029         1029|`,
`       |    )      -------------------| + |    )      -----------------|`,
`       |   /            x - alpha     |   |   /                     2  |`,
`       |  -----                       |   |  -----       (x - alpha)   |`,
`       \\alpha = %1                    /   \\alpha = %1                  /`,
`   `,
`                  1952            464            32`,
`            + ------------ + ------------ + -----------`,
`              2401 (x - 2)              2             3`,
`                             343 (x - 2)    49 (x - 2)`,
`   `,
`                                     2`,
`%1 :=                       RootOf(_Z  + _Z + 1)`,
`   `
):
`help/text/fparfrac` := `help/text/fullparfrac`:

#save `fparfrac.m`;
#quit
