)abbrev domain ISUPS InnerSparseUnivariatePowerSeries
++ Author: Clifton J. Williamson
++ Date Created: 28 October 1994
++ Basic Operations:
++ Related Domains: SparseUnivariateTaylorSeries, SparseUnivariateLaurentSeries
++   SparseUnivariatePuiseuxSeries
++ Also See:
++ AMS Classifications:
++ Keywords: sparse, series
++ Examples:
++ References:
++ Description: InnerSparseUnivariatePowerSeries is an internal domain
++   used for creating sparse Taylor and Laurent series.
InnerSparseUnivariatePowerSeries(Coef) : Exports == Implementation where
  Coef  : Ring
  B    ==> Boolean
  COM  ==> OrderedCompletion Integer
  I    ==> Integer
  L    ==> List
  NNI  ==> NonNegativeInteger
  OUT  ==> OutputForm
  PI   ==> PositiveInteger
  REF  ==> Reference OrderedCompletion Integer
  RN   ==> Fraction Integer
  Term ==> Record(k : Integer, c : Coef)
  SG   ==> String
  ST   ==> Stream Term

  Exports ==> UnivariatePowerSeriesCategory(Coef, Integer) with
    makeSeries : (REF, ST) -> %
      ++ makeSeries(refer, str) creates a power series from the reference
      ++ \spad{refer} and the stream \spad{str}.
    getRef : % -> REF
      ++ getRef(f) returns a reference containing the order to which the
      ++ terms of f have been computed.
    getStream : % -> ST
      ++ getStream(f) returns the stream of terms representing the series f.
    series : ST -> %
      ++ series(st) creates a series from a stream of non-zero terms,
      ++ where a term is an exponent-coefficient pair.  The terms in the
      ++ stream should be ordered by increasing order of exponents.
    monomial? : % -> B
      ++ monomial?(f) tests if f is a single monomial.
    multiplyCoefficients : (I -> Coef, %) -> %
      ++ multiplyCoefficients(fn, f) returns the series
      ++ \spad{sum(fn(n) * an * x^n, n = n0..)},
      ++ where f is the series \spad{sum(an * x^n, n = n0..)}.
    iExquo : (%,%,B) -> Union(%,"failed")
      ++ iExquo(f, g, taylor?) is the quotient of the power series f and g.
      ++ If \spad{taylor?} is \spad{true}, then we must have
      ++ \spad{order(f) >= order(g)}.
    taylorQuoByVar : % -> %
      ++ taylorQuoByVar(a0 + a1 x + a2 x^2 + ...)
      ++ returns \spad{a1 + a2 x + a3 x^2 + ...}
    iCompose : (%, %) -> %
      ++ iCompose(f, g) returns \spad{f(g(x))}.  This is an internal function
      ++ which should only be called for Taylor series \spad{f(x)} and
      ++ \spad{g(x)} such that the constant coefficient of \spad{g(x)} is zero.
    seriesToOutputForm : (ST, REF, Symbol, Coef, RN) -> OutputForm
      ++ seriesToOutputForm(st, refer, var, cen, r) prints the series
      ++ \spad{f((var - cen)^r)}.
    if Coef has Algebra Fraction Integer then
      integrate : % -> %
        ++ integrate(f(x)) returns an anti-derivative of the power series
        ++ \spad{f(x)} with constant coefficient 0.
        ++ Warning: function does not check for a term of degree -1.
      cPower : (%, Coef) -> %
        ++ cPower(f, r) computes \spad{f^r}, where f has constant coefficient 1.
        ++ For use when the coefficient ring is commutative.
      cRationalPower : (%, RN) -> %
        ++ cRationalPower(f, r) computes \spad{f^r}.
        ++ For use when the coefficient ring is commutative.
      cExp : % -> %
        ++ cExp(f) computes the exponential of the power series f.
        ++ For use when the coefficient ring is commutative.
      cLog : % -> %
        ++ cLog(f) computes the logarithm of the power series f.
        ++ For use when the coefficient ring is commutative.
      cSin : % -> %
        ++ cSin(f) computes the sine of the power series f.
        ++ For use when the coefficient ring is commutative.
      cCos : % -> %
        ++ cCos(f) computes the cosine of the power series f.
        ++ For use when the coefficient ring is commutative.
      cTan : % -> %
        ++ cTan(f) computes the tangent of the power series f.
        ++ For use when the coefficient ring is commutative.
      cCot : % -> %
        ++ cCot(f) computes the cotangent of the power series f.
        ++ For use when the coefficient ring is commutative.
      cSec : % -> %
        ++ cSec(f) computes the secant of the power series f.
        ++ For use when the coefficient ring is commutative.
      cCsc : % -> %
        ++ cCsc(f) computes the cosecant of the power series f.
        ++ For use when the coefficient ring is commutative.
      cAsin : % -> %
        ++ cAsin(f) computes the arcsine of the power series f.
        ++ For use when the coefficient ring is commutative.
      cAcos : % -> %
        ++ cAcos(f) computes the arccosine of the power series f.
        ++ For use when the coefficient ring is commutative.
      cAtan : % -> %
        ++ cAtan(f) computes the arctangent of the power series f.
        ++ For use when the coefficient ring is commutative.
      cAcot : % -> %
        ++ cAcot(f) computes the arccotangent of the power series f.
        ++ For use when the coefficient ring is commutative.
      cAsec : % -> %
        ++ cAsec(f) computes the arcsecant of the power series f.
        ++ For use when the coefficient ring is commutative.
      cAcsc : % -> %
        ++ cAcsc(f) computes the arccosecant of the power series f.
        ++ For use when the coefficient ring is commutative.
      cSinh : % -> %
        ++ cSinh(f) computes the hyperbolic sine of the power series f.
        ++ For use when the coefficient ring is commutative.
      cCosh : % -> %
        ++ cCosh(f) computes the hyperbolic cosine of the power series f.
        ++ For use when the coefficient ring is commutative.
      cTanh : % -> %
        ++ cTanh(f) computes the hyperbolic tangent of the power series f.
        ++ For use when the coefficient ring is commutative.
      cCoth : % -> %
        ++ cCoth(f) computes the hyperbolic cotangent of the power series f.
        ++ For use when the coefficient ring is commutative.
      cSech : % -> %
        ++ cSech(f) computes the hyperbolic secant of the power series f.
        ++ For use when the coefficient ring is commutative.
      cCsch : % -> %
        ++ cCsch(f) computes the hyperbolic cosecant of the power series f.
        ++ For use when the coefficient ring is commutative.
      cAsinh : % -> %
        ++ cAsinh(f) computes the inverse hyperbolic sine of the power
        ++ series f.  For use when the coefficient ring is commutative.
      cAcosh : % -> %
        ++ cAcosh(f) computes the inverse hyperbolic cosine of the power
        ++ series f.  For use when the coefficient ring is commutative.
      cAtanh : % -> %
        ++ cAtanh(f) computes the inverse hyperbolic tangent of the power
        ++ series f.  For use when the coefficient ring is commutative.
      cAcoth : % -> %
        ++ cAcoth(f) computes the inverse hyperbolic cotangent of the power
        ++ series f.  For use when the coefficient ring is commutative.
      cAsech : % -> %
        ++ cAsech(f) computes the inverse hyperbolic secant of the power
        ++ series f.  For use when the coefficient ring is commutative.
      cAcsch : % -> %
        ++ cAcsch(f) computes the inverse hyperbolic cosecant of the power
        ++ series f.  For use when the coefficient ring is commutative.

  Implementation ==> add
    import from REF

    Rep := Record(%ord : REF, %str : Stream Term)
    -- when the value of 'ord' is n, this indicates that all non-zero
    -- terms of order up to and including n have been computed;
    -- when 'ord' is plusInfinity, all terms have been computed;
    -- lazy evaluation of 'str' has the side-effect of modifying the value
    -- of 'ord'

--% Local functions

    makeTerm :        (Integer, Coef) -> Term
    getCoef :         Term -> Coef
    getExpon :        Term -> Integer
    iSeries :         (ST, REF) -> ST
    iExtend :         (ST, COM, REF) -> ST
    iTruncate0 :      (ST, REF, REF, COM, I, I) -> ST
    iTruncate :       (%, COM, I) -> %
    iCoefficient :    (ST, Integer) -> Coef
    iOrder :          (ST, COM, REF) -> I
    iMap1 :           ((Coef, I) -> Coef, I -> I, B, ST, REF, REF, Integer) -> ST
    iMap2 :           ((Coef, I) -> Coef, I -> I, B, %) -> %
    iPlus1 :          ((Coef, Coef) -> Coef, ST, REF, ST, REF, REF, I) -> ST
    iPlus2 :          ((Coef, Coef) -> Coef, %, %) -> %
    productByTerm :   (Coef, I, ST, REF, REF, I) -> ST
    productLazyEval : (ST, REF, ST, REF, COM) -> Void
    iTimes :          (ST, REF, ST, REF, REF, I) -> ST
    iDivide :         (ST, REF, ST, REF, Coef, I, REF, I) -> ST
    divide :          (%, I, %, I, Coef) -> %
    compose0 :        (ST, REF, ST, REF, I, %, %, I, REF, I) -> ST
    factorials? :     () -> Boolean
    termOutput :      (RN, Coef, OUT) -> OUT
    showAll? :        () -> Boolean

--% macros

    makeTerm(exp, coef) == [exp, coef]
    getCoef term == term.c
    getExpon term == term.k

    makeSeries(refer, x) == [refer, x]
    getRef ups == ups.%ord
    getStream ups == ups.%str

--% creation and destruction of series

    monomial(coef, expon) ==
      nix : ST := empty()
      st :=
        zero? coef => nix
        concat(makeTerm(expon, coef), nix)
      makeSeries(ref plusInfinity(), st)

    monomial? ups == (not empty? getStream ups) and (empty? rst getStream ups)

    coerce(n : I)    == n :: Coef :: %
    coerce(r : Coef) == monomial(r, 0)

    iSeries(x, refer) == delay
      empty? x => (setelt!(refer, plusInfinity()); empty())
      setelt!(refer, (getExpon frst x) :: COM)
      concat(frst x, iSeries(rst x, refer))

    series(x : ST) ==
      empty? x => 0
      n := getExpon frst x; refer := ref((n-1) :: COM)
      makeSeries(refer, iSeries(x, refer))

--% values

    characteristic() == characteristic()$Coef

    0 == monomial(0, 0)
    1 == monomial(1, 0)

    iExtend(st, n, refer) ==
      (elt refer) < n =>
        explicitlyEmpty? st => (setelt!(refer, plusInfinity()); st)
        explicitEntries? st => iExtend(rst st, n, refer)
        iExtend(lazyEvaluate st, n, refer)
      st

    extend(x, n) == (iExtend(getStream x, n :: COM, getRef x); x)
    complete x  == (iExtend(getStream x, plusInfinity(), getRef x); x)

    iTruncate0(x, xRefer, refer, minExp, maxExp, n) == delay
      explicitlyEmpty? x => (setelt!(refer, plusInfinity()); empty())
      nn := n :: COM
      while (elt xRefer) < nn repeat lazyEvaluate x
      explicitEntries? x =>
        (nx := getExpon(xTerm := frst x)) > maxExp =>
          (setelt!(refer, plusInfinity()); empty())
        setelt!(refer, nx :: COM)
        (nx :: COM) >= minExp =>
          concat(makeTerm(nx, getCoef xTerm), _
                 iTruncate0(rst x, xRefer, refer, minExp, maxExp, nx + 1))
        iTruncate0(rst x, xRefer, refer, minExp, maxExp, nx + 1)
      -- can't have elt(xRefer) = infty unless all terms have been computed
      degr := retract(elt xRefer)@I
      setelt!(refer, degr :: COM)
      iTruncate0(x, xRefer, refer, minExp, maxExp, degr + 1)

    iTruncate(ups, minExp, maxExp) ==
      x := getStream ups; xRefer := getRef ups
      explicitlyEmpty? x => 0
      explicitEntries? x =>
        deg := getExpon frst x
        refer := ref((deg - 1) :: COM)
        makeSeries(refer, iTruncate0(x, xRefer, refer, minExp, maxExp, deg))
      -- can't have elt(xRefer) = infty unless all terms have been computed
      degr := retract(elt xRefer)@I
      refer := ref(degr :: COM)
      makeSeries(refer, iTruncate0(x, xRefer, refer, minExp, maxExp, degr + 1))

    truncate(ups, n) == iTruncate(ups, minusInfinity(), n)
    truncate(ups, n1, n2) ==
      if n1 > n2 then (n1, n2) := (n2, n1)
      iTruncate(ups, n1 :: COM, n2)

    iCoefficient(st, n) ==
      explicitEntries? st =>
        term := frst st
        (expon := getExpon term) > n => 0
        expon = n => getCoef term
        iCoefficient(rst st, n)
      0

    coefficient(x, n)   == (extend(x, n); iCoefficient(getStream x, n))
    elt(x : %, n : Integer) == coefficient(x, n)

    iOrder(st, n, refer) ==
      explicitlyEmpty? st =>
          finite?(n) => retract(n)@Integer
          error "order: series has infinite order"
      explicitEntries? st =>
        ((r := getExpon frst st) :: COM) >= n => retract(n)@Integer
        r
      -- can't have elt(xRefer) = infty unless all terms have been computed
      degr := retract(elt refer)@I
      (degr :: COM) >= n => retract(n)@Integer
      iOrder(lazyEvaluate st, n, refer)

    order x    == iOrder(getStream x, plusInfinity(), getRef x)
    order(x, n) == iOrder(getStream x, n :: COM, getRef x)

    terms x    == getStream x

--% predicates

    zero? ups ==
      x := getStream ups; ref := getRef ups
      whatInfinity(n := elt ref) = 1 => explicitlyEmpty? x
      count : NNI := _$streamCount$Lisp
      for i in 1..count repeat
        explicitlyEmpty? x => return true
        explicitEntries? x => return false
        lazyEvaluate x
      false

    ups1 = ups2 == zero?(ups1 - ups2)

--% arithmetic

    iMap1(cFcn, eFcn, check?, x, xRefer, refer, n) == delay
      -- when this function is called, all terms in 'x' of order < n have been
      -- computed and we compute the eFcn(n)th order coefficient of the result
      explicitlyEmpty? x => (setelt!(refer, plusInfinity()); empty())
      -- if terms in 'x' up to order n have not been computed,
      -- apply lazy evaluation
      nn := n :: COM
      while (elt xRefer) < nn repeat lazyEvaluate x
      -- 'x' may now be empty: retest
      explicitlyEmpty? x => (setelt!(refer, plusInfinity()); empty())
      -- must have nx >= n
      explicitEntries? x =>
        xCoef := getCoef(xTerm := frst x); nx := getExpon xTerm
        newCoef := cFcn(xCoef, nx); m := eFcn nx
        setelt!(refer, m :: COM)
        not check? =>
          concat(makeTerm(m, newCoef), _
                 iMap1(cFcn, eFcn, check?, rst x, xRefer, refer, nx + 1))
        zero? newCoef => iMap1(cFcn, eFcn, check?, rst x, xRefer, refer, nx + 1)
        concat(makeTerm(m, newCoef), _
               iMap1(cFcn, eFcn, check?, rst x, xRefer, refer, nx + 1))
      -- can't have elt(xRefer) = infty unless all terms have been computed
      degr := retract(elt xRefer)@I
      setelt!(refer, eFcn(degr) :: COM)
      iMap1(cFcn, eFcn, check?, x, xRefer, refer, degr + 1)

    iMap2(cFcn, eFcn, check?, ups) ==
      -- 'eFcn' must be a strictly increasing function,
      -- i.e. i < j => eFcn(i) < eFcn(j)
      xRefer := getRef ups; x := getStream ups
      explicitlyEmpty? x => 0
      explicitEntries? x =>
        deg := getExpon frst x
        refer := ref(eFcn(deg - 1) :: COM)
        makeSeries(refer, iMap1(cFcn, eFcn, check?, x, xRefer, refer, deg))
      -- can't have elt(xRefer) = infty unless all terms have been computed
      degr := retract(elt xRefer)@I
      refer := ref(eFcn(degr) :: COM)
      makeSeries(refer, iMap1(cFcn, eFcn, check?, x, xRefer, refer, degr + 1))

    map(fcn, x) == iMap2((y, n) +-> fcn(y), z +-> z, true, x)
    differentiate x == iMap2((y, n) +-> n*y, z +-> z - 1, true, x)
    multiplyCoefficients(f, x) == iMap2((y, n) +-> f(n)*y, z +-> z, true, x)
    multiplyExponents(x, n) == iMap2((y, m) +-> y, z +-> n*z, false, x)

    iPlus1(op, x, xRefer, y, yRefer, refer, n) == delay
      -- when this function is called, all terms in 'x' and 'y' of order < n
      -- have been computed and we are computing the nth order coefficient of
      -- the result; note the 'op' is either '+' or '-'
      explicitlyEmpty? x => iMap1((x1, m) +-> op(0, x1), z +-> z,
                                  false, y, yRefer, refer, n)
      explicitlyEmpty? y => iMap1((x1, m) +-> op(x1, 0), z +-> z,
                                  false, x, xRefer, refer, n)
      -- if terms up to order n have not been computed,
      -- apply lazy evaluation
      nn := n :: COM
      while (elt xRefer) < nn repeat lazyEvaluate x
      while (elt yRefer) < nn repeat lazyEvaluate y
      -- 'x' or 'y' may now be empty: retest
      explicitlyEmpty? x => iMap1((x1, m) +-> op(0, x1), z +-> z,
                                  false, y, yRefer, refer, n)
      explicitlyEmpty? y => iMap1((x1, m) +-> op(x1, 0), z +-> z,
                                  false, x, xRefer, refer, n)
      -- must have nx >= n, ny >= n
      -- both x and y have explicit terms
      explicitEntries?(x) and explicitEntries?(y) =>
        xCoef := getCoef(xTerm := frst x); nx := getExpon xTerm
        yCoef := getCoef(yTerm := frst y); ny := getExpon yTerm
        nx = ny =>
          setelt!(refer, nx :: COM)
          zero? (coef := op(xCoef, yCoef)) =>
            iPlus1(op, rst x, xRefer, rst y, yRefer, refer, nx + 1)
          concat(makeTerm(nx, coef), _
                 iPlus1(op, rst x, xRefer, rst y, yRefer, refer, nx + 1))
        nx < ny =>
          setelt!(refer, nx :: COM)
          concat(makeTerm(nx, op(xCoef, 0)), _
                 iPlus1(op, rst x, xRefer, y, yRefer, refer, nx + 1))
        setelt!(refer, ny :: COM)
        concat(makeTerm(ny, op(0, yCoef)), _
               iPlus1(op, x, xRefer, rst y, yRefer, refer, ny + 1))
      -- y has no term of degree n
      explicitEntries? x =>
        xCoef := getCoef(xTerm := frst x); nx := getExpon xTerm
        -- can't have elt(yRefer) = infty unless all terms have been computed
        (degr := retract(elt yRefer)@I) < nx =>
          setelt!(refer, elt yRefer)
          iPlus1(op, x, xRefer, y, yRefer, refer, degr + 1)
        setelt!(refer, nx :: COM)
        concat(makeTerm(nx, op(xCoef, 0)), _
               iPlus1(op, rst x, xRefer, y, yRefer, refer, nx + 1))
      -- x has no term of degree n
      explicitEntries? y =>
        yCoef := getCoef(yTerm := frst y); ny := getExpon yTerm
        -- can't have elt(xRefer) = infty unless all terms have been computed
        (degr := retract(elt xRefer)@I) < ny =>
          setelt!(refer, elt xRefer)
          iPlus1(op, x, xRefer, y, yRefer, refer, degr + 1)
        setelt!(refer, ny :: COM)
        concat(makeTerm(ny, op(0, yCoef)), _
               iPlus1(op, x, xRefer, rst y, yRefer, refer, ny + 1))
      -- neither x nor y has a term of degree n
      setelt!(refer, xyRef := min(elt xRefer, elt yRefer))
      -- can't have xyRef = infty unless all terms have been computed
      iPlus1(op, x, xRefer, y, yRefer, refer, retract(xyRef)@I + 1)

    iPlus2(op, ups1, ups2) ==
      xRefer := getRef ups1; x := getStream ups1
      xDeg :=
        explicitlyEmpty? x => return map(z +-> op(0$Coef, z), ups2)
        explicitEntries? x => (getExpon frst x) - 1
        -- can't have elt(xRefer) = infty unless all terms have been computed
        retract(elt xRefer)@I
      yRefer := getRef ups2; y := getStream ups2
      yDeg :=
        explicitlyEmpty? y => return map(z +-> op(z, 0$Coef), ups1)
        explicitEntries? y => (getExpon frst y) - 1
        -- can't have elt(yRefer) = infty unless all terms have been computed
        retract(elt yRefer)@I
      deg := min(xDeg, yDeg); refer := ref(deg :: COM)
      makeSeries(refer, iPlus1(op, x, xRefer, y, yRefer, refer, deg + 1))

    x + y == iPlus2((xi, yi) +-> xi + yi, x, y)
    x - y == iPlus2((xi, yi) +-> xi - yi, x, y)
    - y   == iMap2((x, n) +-> -x, z +-> z, false, y)

    -- gives correct defaults for I, NNI and PI
    n : I   * x : % == (zero? n => 0; map(z +-> n*z, x))
    n : NNI * x : % == (zero? n => 0; map(z +-> n*z, x))
    n : PI  * x : % == (zero? n => 0; map(z +-> n*z, x))

    productByTerm(coef, expon, x, xRefer, refer, n) ==
      iMap1((y, m) +-> coef*y, z +-> z + expon, true, x, xRefer, refer, n)

    productLazyEval(x, xRefer, y, yRefer, nn) ==
      explicitlyEmpty?(x) or explicitlyEmpty?(y) => void()
      explicitEntries? x =>
        explicitEntries? y => void()
        xDeg := (getExpon frst x) :: COM
        while (xDeg + elt(yRefer)) < nn repeat lazyEvaluate y
        void()
      explicitEntries? y =>
        yDeg := (getExpon frst y) :: COM
        while (yDeg + elt(xRefer)) < nn repeat lazyEvaluate x
        void()
      lazyEvaluate x
      -- if x = y, then y may now have explicit entries
      if lazy? y then lazyEvaluate y
      productLazyEval(x, xRefer, y, yRefer, nn)

    iTimes(x, xRefer, y, yRefer, refer, n) == delay
      -- when this function is called, we are computing the nth order
      -- coefficient of the product
      productLazyEval(x, xRefer, y, yRefer, n :: COM)
      explicitlyEmpty?(x) or explicitlyEmpty?(y) =>
        (setelt!(refer, plusInfinity()); empty())
      -- must have nx + ny >= n
      explicitEntries?(x) and explicitEntries?(y) =>
        xCoef := getCoef(xTerm := frst x); xExpon := getExpon xTerm
        yCoef := getCoef(yTerm := frst y); yExpon := getExpon yTerm
        expon := xExpon + yExpon
        setelt!(refer, expon :: COM)
        scRefer := ref(expon :: COM)
        scMult := productByTerm(xCoef, xExpon, rst y, yRefer, scRefer, yExpon + 1)
        prRefer := ref(expon :: COM)
        pr := iTimes(rst x, xRefer, y, yRefer, prRefer, expon + 1)
        sm := iPlus1((xi, yi) +-> xi + yi, scMult, scRefer,
                      pr, prRefer, refer, expon + 1)
        zero?(coef := xCoef * yCoef) => sm
        concat(makeTerm(expon, coef), sm)
      explicitEntries? x =>
        xExpon := getExpon frst x
        -- can't have elt(yRefer) = infty unless all terms have been computed
        degr := retract(elt yRefer)@I
        setelt!(refer, (xExpon + degr) :: COM)
        iTimes(x, xRefer, y, yRefer, refer, xExpon + degr + 1)
      explicitEntries? y =>
        yExpon := getExpon frst y
        -- can't have elt(xRefer) = infty unless all terms have been computed
        degr := retract(elt xRefer)@I
        setelt!(refer, (yExpon + degr) :: COM)
        iTimes(x, xRefer, y, yRefer, refer, yExpon + degr + 1)
      -- can't have elt(xRefer) = infty unless all terms have been computed
      xDegr := retract(elt xRefer)@I
      yDegr := retract(elt yRefer)@I
      setelt!(refer, (xDegr + yDegr) :: COM)
      iTimes(x, xRefer, y, yRefer, refer, xDegr + yDegr + 1)

    ups1 : % * ups2 : % ==
      xRefer := getRef ups1; x := getStream ups1
      xDeg :=
        explicitlyEmpty? x => return 0
        explicitEntries? x => (getExpon frst x) - 1
        -- can't have elt(xRefer) = infty unless all terms have been computed
        retract(elt xRefer)@I
      yRefer := getRef ups2; y := getStream ups2
      yDeg :=
        explicitlyEmpty? y => return 0
        explicitEntries? y => (getExpon frst y) - 1
        -- can't have elt(yRefer) = infty unless all terms have been computed
        retract(elt yRefer)@I
      deg := xDeg + yDeg + 1; refer := ref(deg :: COM)
      makeSeries(refer, iTimes(x, xRefer, y, yRefer, refer, deg + 1))

    iDivide(x, xRefer, y, yRefer, rym, m, refer, n) == delay
      -- when this function is called, we are computing the nth order
      -- coefficient of the result
      explicitlyEmpty? x => (setelt!(refer, plusInfinity()); empty())
      -- if terms up to order n - m have not been computed,
      -- apply lazy evaluation
      nm := (n + m) :: COM
      while (elt xRefer) < nm repeat lazyEvaluate x
      -- 'x' may now be empty: retest
      explicitlyEmpty? x => (setelt!(refer, plusInfinity()); empty())
      -- must have nx >= n + m
      explicitEntries? x =>
        newCoef := getCoef(xTerm := frst x) * rym; nx := getExpon xTerm
        prodRefer := ref(nx :: COM)
        prod := productByTerm(-newCoef, nx - m, rst y, yRefer, prodRefer, 1)
        sumRefer := ref(nx :: COM)
        sum := iPlus1((xi, yi) +-> xi + yi, rst x, xRefer, prod,
                       prodRefer, sumRefer, nx + 1)
        setelt!(refer, (nx - m) :: COM); term := makeTerm(nx - m, newCoef)
        concat(term, iDivide(sum, sumRefer, y, yRefer, rym, m, refer, nx - m + 1))
      -- can't have elt(xRefer) = infty unless all terms have been computed
      degr := retract(elt xRefer)@I
      setelt!(refer, (degr - m) :: COM)
      iDivide(x, xRefer, y, yRefer, rym, m, refer, degr - m + 1)

    divide(ups1, deg1, ups2, deg2, r) ==
      xRefer := getRef ups1; x := getStream ups1
      yRefer := getRef ups2; y := getStream ups2
      refer := ref((deg1 - deg2) :: COM)
      makeSeries(refer, iDivide(x, xRefer, y, yRefer, r, deg2, refer, deg1 - deg2 + 1))

    iExquo(ups1, ups2, taylor?) ==
      xRefer := getRef ups1; x := getStream ups1
      y := getStream ups2
      n : I := 0
      -- try to find first non-zero term in y
      -- give up after 1000 lazy evaluations
      while not explicitEntries? y repeat
        explicitlyEmpty? y => return "failed"
        lazyEvaluate y
        (n := n + 1) > 1000 => return "failed"
      yCoef := getCoef(yTerm := frst y); ny := getExpon yTerm
      (ry := recip yCoef) case "failed" => "failed"
      nn := ny :: COM
      if taylor? then
        while (elt(xRefer) < nn) repeat
          explicitlyEmpty? x => return 0
          explicitEntries? x => return "failed"
          lazyEvaluate x
      -- check if ups2 is a monomial
      empty? rst y => iMap2((y1, m) +-> y1*(ry@Coef), z +-> z - ny,
                            false, ups1)
      explicitlyEmpty? x => 0
      nx :=
        explicitEntries? x =>
          ((deg := getExpon frst x) < ny) and taylor? => return "failed"
          deg - 1
        -- can't have elt(xRefer) = infty unless all terms have been computed
        retract(elt xRefer)@I
      divide(ups1, nx, ups2, ny, ry@Coef)

    taylorQuoByVar ups ==
      iMap2((y, n) +-> y, z +-> z - 1, false,
            ups - monomial(coefficient(ups, 0), 0))

    compose0(x : ST, xRefer : REF, y : ST, yRefer : REF, yOrd : I,
             y1 : %, yn0 : %, n0 : I, refer : REF, n : I) : ST == delay
      -- when this function is called, we are computing the nth order
      -- coefficient of the composite
      explicitlyEmpty? x => (setelt!(refer, plusInfinity()); empty())
      -- if terms in 'x' up to order n have not been computed,
      -- apply lazy evaluation
      repeat
          x_ord := elt(xRefer)
          whatInfinity(x_ord) ~= 0 => break
          yOrd*retract(x_ord)@I < n => lazyEvaluate x
          break
      explicitEntries? x =>
        xCoef := getCoef(xTerm := frst x); n1 := getExpon xTerm
        zero? n1 =>
          setelt!(refer, n1 :: COM)
          concat(makeTerm(n1, xCoef), _
                 compose0(rst x, xRefer, y, yRefer, yOrd, y1, yn0, n0, refer, n1 + 1))
        yn1 := yn0 * y1 ^ ((n1 - n0) :: NNI)
        z := getStream yn1; zRefer := getRef yn1
        degr := yOrd * n1; prodRefer := ref((degr - 1) :: COM)
        prod := iMap1((s, k) +-> xCoef*s, m +-> m, true, z, zRefer,
                      prodRefer, degr)
        coRefer := ref((degr + yOrd - 1) :: COM)
        co := compose0(rst x, xRefer, y, yRefer, yOrd, y1, yn1, n1, coRefer, degr + yOrd)
        setelt!(refer, (degr - 1) :: COM)
        iPlus1((xi, yi) +-> xi + yi, prod, prodRefer, co, coRefer, refer, degr)
      -- can't have elt(xRefer) = infty unless all terms have been computed
      degr := yOrd * (retract(elt xRefer)@I + 1)
      setelt!(refer, (degr - 1) :: COM)
      compose0(x, xRefer, y, yRefer, yOrd, y1, yn0, n0, refer, degr)

    iCompose(ups1, ups2) ==
      x := getStream ups1; xRefer := getRef ups1
      y := getStream ups2; yRefer := getRef ups2
      -- try to compute the order of 'ups2'
      n : I := _$streamCount$Lisp
      for i in 1..n while not explicitEntries? y repeat
        explicitlyEmpty? y => return coefficient(ups1, 0) :: %
        lazyEvaluate y
      explicitlyEmpty? y => coefficient(ups1, 0) :: %
      yOrd : I :=
        explicitEntries? y => getExpon frst y
        retract(elt yRefer)@I
      compRefer := ref((-1)@I :: COM)
      makeSeries(compRefer, _
                 compose0(x, xRefer, y, yRefer, yOrd, ups2, 1, 0, compRefer, 0))

    if Coef has Algebra Fraction Integer then

      integrate x == iMap2((y, n) +-> 1/(n + 1)*y, z +-> z + 1, true, x)

--% Fixed point computations

      Ys ==> Y$ParadoxicalCombinatorsForStreams(Term)

      integ0 : (ST, REF, REF, I) -> ST
      integ0(x, intRef, ansRef, n) == delay
        nLess1 := (n - 1) :: COM
        while (elt intRef) < nLess1 repeat lazyEvaluate x
        explicitlyEmpty? x => (setelt!(ansRef, plusInfinity()); empty())
        explicitEntries? x =>
          xCoef := getCoef(xTerm := frst x); nx := getExpon xTerm
          setelt!(ansRef, (n1 := (nx + 1)) :: COM)
          concat(makeTerm(n1, inv(n1 :: RN) * xCoef), _
                 integ0(rst x, intRef, ansRef, n1))
        -- can't have elt(intRef) = infty unless all terms have been computed
        degr := retract(elt intRef)@I; setelt!(ansRef, (degr + 1) :: COM)
        integ0(x, intRef, ansRef, degr + 2)

      integ1 : (ST, REF, REF) -> ST
      integ1(x, intRef, ansRef) == integ0(x, intRef, ansRef, 1)

      lazyInteg : (Coef, () -> ST, REF, REF) -> ST
      lazyInteg(a, xf, intRef, ansRef) ==
        ansStr : ST := integ1(delay xf, intRef, ansRef)
        concat(makeTerm(0, a), ansStr)

      cPower(f, r) ==
        -- computes f^r.  f should have constant coefficient 1.
        fp := differentiate f
        fInv := iExquo(1, f, false) :: %; y := r * fp * fInv
        yRef := getRef y; yStr := getStream y
        intRef := ref((-1)@I :: COM); ansRef := ref(0$I :: COM)
        ansStr := Ys(s +-> lazyInteg(1,
                               iTimes(s, ansRef, yStr, yRef, intRef, 0),
                               intRef, ansRef))
        makeSeries(ansRef, ansStr)

      iExp : (%, Coef) -> %
      iExp(f, cc) ==
        -- computes exp(f).  cc = exp coefficient(f, 0)
        fp := differentiate f
        fpRef := getRef fp; fpStr := getStream fp
        intRef := ref((-1)@I :: COM); ansRef := ref(0$I :: COM)
        ansStr := Ys(s +-> lazyInteg(cc,
                              iTimes(s, ansRef, fpStr, fpRef, intRef, 0),
                              intRef, ansRef))
        makeSeries(ansRef, ansStr)

      sincos0 : (Coef, Coef, L ST, REF, REF, ST, REF, ST, REF) -> L ST
      sincos0(sinc, cosc, list, sinRef, cosRef, fpStr, fpRef, fpStr2, fpRef2) ==
        sinStr := first list; cosStr := second list
        prodRef1 := ref((-1)@I :: COM); prodRef2 := ref((-1)@I :: COM)
        prodStr1 := iTimes(cosStr, cosRef, fpStr, fpRef, prodRef1, 0)
        prodStr2 := iTimes(sinStr, sinRef, fpStr2, fpRef2, prodRef2, 0)
        [lazyInteg(sinc, prodStr1, prodRef1, sinRef), _
         lazyInteg(cosc, prodStr2, prodRef2, cosRef)]

      iSincos : (%, Coef, Coef, I) -> Record(%sin : %, %cos : %)
      iSincos(f, sinc, cosc, sign) ==
        fp := differentiate f
        fpRef := getRef fp; fpStr := getStream fp
        fp2 := ((sign = 1) => fp; -fp)
        fpRef2 := getRef fp2; fpStr2 := getStream fp2
        sinRef := ref(0$I :: COM); cosRef := ref(0$I :: COM)
        sincos :=
          Ys(s +-> sincos0(sinc, cosc, s, sinRef, cosRef,
                           fpStr, fpRef, fpStr2, fpRef2),
             2)
        sinStr := (zero? sinc => rst first sincos; first sincos)
        cosStr := (zero? cosc => rst second sincos; second sincos)
        [makeSeries(sinRef, sinStr), makeSeries(cosRef, cosStr)]

      tan0 : (Coef, ST, REF, ST, REF, I) -> ST
      tan0(cc, ansStr, ansRef, fpStr, fpRef, sign) ==
        sqRef := ref((-1)@I :: COM)
        sqStr := iTimes(ansStr, ansRef, ansStr, ansRef, sqRef, 0)
        one : % := 1; oneStr := getStream one; oneRef := getRef one
        yRef := ref((-1)@I :: COM)
        yStr : ST :=
          (sign = 1) => iPlus1((xi, yi) +-> xi + yi, oneStr, oneRef,
                               sqStr, sqRef, yRef, 0)
          iPlus1((xi, yi) +-> xi - yi, oneStr, oneRef, sqStr, sqRef, yRef, 0)
        intRef := ref((-1)@I :: COM)
        lazyInteg(cc, iTimes(yStr, yRef, fpStr, fpRef, intRef, 0), intRef, ansRef)

      iTan : (%, %, Coef, I) -> %
      iTan(f, fp, cc, sign) ==
        -- computes the tangent (and related functions) of f.
        fpRef := getRef fp; fpStr := getStream fp
        ansRef := ref(0$I :: COM)
        ansStr := Ys(s +-> tan0(cc, s, ansRef, fpStr, fpRef, sign))
        zero? cc => makeSeries(ansRef, rst ansStr)
        makeSeries(ansRef, ansStr)

--% Error Reporting

      TRCONST : SG := "series expansion involves transcendental constants"
      NPOWERS : SG := "series expansion has terms of negative degree"
      FPOWERS : SG := "series expansion has terms of fractional degree"
      MAYFPOW : SG := "series expansion may have terms of fractional degree"
      LOGS : SG := "series expansion has logarithmic term"
      NPOWLOG : SG :=
         "series expansion has terms of negative degree or logarithmic term"
      NOTINV : SG := "leading coefficient not invertible"

--% Rational powers and transcendental functions

      orderOrFailed : % -> Union(I,"failed")
      orderOrFailed uts ==
      -- returns the order of x or "failed"
      -- if -1 is returned, the series is identically zero
        x := getStream uts
        for n in 0..1000 repeat
          explicitlyEmpty? x => return -1
          explicitEntries? x => return getExpon frst x
          lazyEvaluate x
        "failed"

      RATPOWERS : Boolean := Coef has "^": (Coef,RN) -> Coef
      TRANSFCN  : Boolean := Coef has TranscendentalFunctionCategory

      cRationalPower(uts, r) ==
        (ord0 := orderOrFailed uts) case "failed" =>
          error "^: series with many leading zero coefficients"
        order := ord0@I
        (n := order exquo denom(r)) case "failed" =>
          error "^: rational power does not exist"
        cc := coefficient(uts, order)
        (ccInv := recip cc) case "failed" => error concat("^: ",NOTINV)
        ccPow :=
          (cc = 1) => cc
          (denom r) = 1 =>
            not negative?(num := numer r) => cc ^ (num :: NNI)
            (ccInv :: Coef) ^ ((-num) :: NNI)
          RATPOWERS => cc ^ r
          error "^ rational power of coefficient undefined"
        uts1 := (ccInv@Coef) * uts
        uts2 := uts1 * monomial(1, -order)
        monomial(ccPow, (n@I) * numer(r)) * cPower(uts2, r :: Coef)

      cExp uts ==
        zero?(cc := coefficient(uts, 0)) => iExp(uts, 1)
        TRANSFCN => iExp(uts, exp cc)
        error concat("exp: ",TRCONST)

      cLog uts ==
        zero?(cc := coefficient(uts, 0)) =>
          error "log: constant coefficient should not be 0"
        (cc = 1) => integrate(differentiate(uts) * (iExquo(1, uts, true) :: %))
        TRANSFCN =>
          y := iExquo(1, uts, true) :: %
          (log(cc) :: %) + integrate(y * differentiate(uts))
        error concat("log: ",TRCONST)

      sincos : % -> Record(%sin : %, %cos : %)
      sincos uts ==
        zero?(cc := coefficient(uts, 0)) => iSincos(uts, 0, 1, -1)
        TRANSFCN => iSincos(uts, sin cc, cos cc, -1)
        error concat("sincos: ",TRCONST)

      cSin uts == sincos(uts).%sin
      cCos uts == sincos(uts).%cos

      cTan uts ==
        zero?(cc := coefficient(uts, 0)) => iTan(uts, differentiate uts, 0, 1)
        TRANSFCN => iTan(uts, differentiate uts, tan cc, 1)
        error concat("tan: ",TRCONST)

      cCot uts ==
        zero? uts => error "cot: cot(0) is undefined"
        zero?(cc := coefficient(uts,0)) => error error concat("cot: ",NPOWERS)
        TRANSFCN => iTan(uts, -differentiate uts, cot cc, 1)
        error concat("cot: ",TRCONST)

      cSec uts ==
        zero?(cc := coefficient(uts, 0)) => iExquo(1, cCos uts, true) :: %
        TRANSFCN =>
          cosUts := cCos uts
          zero? coefficient(cosUts,0) => error concat("sec: ",NPOWERS)
          iExquo(1, cosUts, true) :: %
        error concat("sec: ",TRCONST)

      cCsc uts ==
        zero? uts => error "csc: csc(0) is undefined"
        TRANSFCN =>
          sinUts := cSin uts
          zero? coefficient(sinUts,0) => error concat("csc: ",NPOWERS)
          iExquo(1, sinUts, true) :: %
        error concat("csc: ",TRCONST)

      cAsin uts ==
        zero?(cc := coefficient(uts, 0)) =>
          integrate(cRationalPower(1 - uts*uts, -1/2) * differentiate(uts))
        TRANSFCN =>
          x := 1 - uts * uts
          cc = 1 or cc = -1 =>
            -- compute order of 'x'
            (ord := orderOrFailed x) case "failed" =>
              error concat("asin: ",MAYFPOW)
            (order := ord@I) = -1 => return asin(cc) :: %
            odd? order => error concat("asin: ",FPOWERS)
            c0 := asin(cc) :: %
            c0 + integrate(cRationalPower(x, -1/2) * differentiate(uts))
          c0 := asin(cc) :: %
          c0 + integrate(cRationalPower(x, -1/2) * differentiate(uts))
        error concat("asin: ",TRCONST)

      cAcos uts ==
        zero? uts =>
          TRANSFCN => acos(0)$Coef :: %
          error concat("acos: ",TRCONST)
        TRANSFCN =>
          x := 1 - uts * uts
          cc := coefficient(uts, 0)
          cc = 1 or cc = -1 =>
            -- compute order of 'x'
            (ord := orderOrFailed x) case "failed" =>
              error concat("acos: ",MAYFPOW)
            (order := ord@I) = -1 => return acos(cc) :: %
            odd? order => error concat("acos: ",FPOWERS)
            c0 := acos(cc) :: %
            c0 + integrate(-cRationalPower(x, -1/2) * differentiate(uts))
          c0 := acos(cc) :: %
          c0 + integrate(-cRationalPower(x, -1/2) * differentiate(uts))
        error concat("acos: ",TRCONST)

      cAtan uts ==
        zero?(cc := coefficient(uts, 0)) =>
          y := iExquo(1, (1 :: %) + uts*uts, true) :: %
          integrate(y * (differentiate uts))
        TRANSFCN =>
          (y := iExquo(1,(1 :: %) + uts*uts,true)) case "failed" =>
            error concat("atan: ",LOGS)
          (atan(cc) :: %) + integrate((y@%) * (differentiate uts))
        error concat("atan: ",TRCONST)

      cAcot uts ==
        TRANSFCN =>
          (y := iExquo(1,(1 :: %) + uts*uts,true)) case "failed" =>
            error concat("acot: ",LOGS)
          cc := coefficient(uts, 0)
          (acot(cc) :: %) + integrate(-(y@%) * (differentiate uts))
        error concat("acot: ",TRCONST)

      cAsec uts ==
        zero?(cc := coefficient(uts, 0)) =>
          error "asec: constant coefficient should not be 0"
        TRANSFCN =>
          x := uts * uts - 1
          y :=
            cc = 1 or cc = -1 =>
              -- compute order of 'x'
              (ord := orderOrFailed x) case "failed" =>
                error concat("asec: ",MAYFPOW)
              (order := ord@I) = -1 => return asec(cc) :: %
              odd? order => error concat("asec: ",FPOWERS)
              cRationalPower(x, -1/2) * differentiate(uts)
            cRationalPower(x, -1/2) * differentiate(uts)
          (z := iExquo(y,uts,true)) case "failed" =>
            error concat("asec: ",NOTINV)
          (asec(cc) :: %) + integrate(z@%)
        error concat("asec: ",TRCONST)

      cAcsc uts ==
        zero?(cc := coefficient(uts, 0)) =>
          error "acsc: constant coefficient should not be 0"
        TRANSFCN =>
          x := uts * uts - 1
          y :=
            cc = 1 or cc = -1 =>
              -- compute order of 'x'
              (ord := orderOrFailed x) case "failed" =>
                error concat("acsc: ",MAYFPOW)
              (order := ord@I) = -1 => return acsc(cc) :: %
              odd? order => error concat("acsc: ",FPOWERS)
              -cRationalPower(x, -1/2) * differentiate(uts)
            -cRationalPower(x, -1/2) * differentiate(uts)
          (z := iExquo(y,uts,true)) case "failed" =>
            error concat("asec: ",NOTINV)
          (acsc(cc) :: %) + integrate(z@%)
        error concat("acsc: ",TRCONST)

      sinhcosh : % -> Record(%sinh : %, %cosh : %)
      sinhcosh uts ==
        zero?(cc := coefficient(uts, 0)) =>
          tmp := iSincos(uts, 0, 1, 1)
          [tmp.%sin, tmp.%cos]
        TRANSFCN =>
          tmp := iSincos(uts, sinh cc, cosh cc, 1)
          [tmp.%sin, tmp.%cos]
        error concat("sinhcosh: ",TRCONST)

      cSinh uts == sinhcosh(uts).%sinh
      cCosh uts == sinhcosh(uts).%cosh

      cTanh uts ==
        zero?(cc := coefficient(uts, 0)) => iTan(uts, differentiate uts, 0, -1)
        TRANSFCN => iTan(uts, differentiate uts, tanh cc, -1)
        error concat("tanh: ",TRCONST)

      cCoth uts ==
        tanhUts := cTanh uts
        zero? tanhUts => error "coth: coth(0) is undefined"
        zero? coefficient(tanhUts,0) => error concat("coth: ",NPOWERS)
        iExquo(1, tanhUts, true) :: %

      cSech uts ==
        coshUts := cCosh uts
        zero? coefficient(coshUts,0) => error concat("sech: ",NPOWERS)
        iExquo(1, coshUts, true) :: %

      cCsch uts ==
        sinhUts := cSinh uts
        zero? coefficient(sinhUts,0) => error concat("csch: ",NPOWERS)
        iExquo(1, sinhUts, true) :: %

      cAsinh uts ==
        x := 1 + uts * uts
        zero?(cc := coefficient(uts, 0)) => cLog(uts + cRationalPower(x, 1/2))
        TRANSFCN =>
          (ord := orderOrFailed x) case "failed" =>
            error concat("asinh: ",MAYFPOW)
          (order := ord@I) = -1 => return asinh(cc) :: %
          odd? order => error concat("asinh: ",FPOWERS)
          -- the argument to 'log' must have a non-zero constant term
          cLog(uts + cRationalPower(x, 1/2))
        error concat("asinh: ",TRCONST)

      cAcosh uts ==
        zero? uts =>
          TRANSFCN => acosh(0)$Coef :: %
          error concat("acosh: ",TRCONST)
        TRANSFCN =>
          cc := coefficient(uts, 0); x := uts*uts - 1
          cc = 1 or cc = -1 =>
            -- compute order of 'x'
            (ord := orderOrFailed x) case "failed" =>
              error concat("acosh: ",MAYFPOW)
            (order := ord@I) = -1 => return acosh(cc) :: %
            odd? order => error concat("acosh: ",FPOWERS)
            -- the argument to 'log' must have a non-zero constant term
            cLog(uts + cRationalPower(x, 1/2))
          cLog(uts + cRationalPower(x, 1/2))
        error concat("acosh: ",TRCONST)

      cAtanh uts ==
        half := inv(2 :: RN) :: Coef
        zero?(cc := coefficient(uts, 0)) =>
          half * (cLog(1 + uts) - cLog(1 - uts))
        TRANSFCN =>
          cc = 1 or cc = -1 => error concat("atanh: ",LOGS)
          half * (cLog(1 + uts) - cLog(1 - uts))
        error concat("atanh: ",TRCONST)

      cAcoth uts ==
        zero? uts =>
          TRANSFCN => acoth(0)$Coef :: %
          error concat("acoth: ",TRCONST)
        TRANSFCN =>
          cc := coefficient(uts, 0); half := inv(2 :: RN) :: Coef
          cc = 1 or cc = -1 => error concat("acoth: ",LOGS)
          half * (cLog(uts + 1) - cLog(uts - 1))
        error concat("acoth: ",TRCONST)

      cAsech uts ==
        zero? uts => error "asech: asech(0) is undefined"
        TRANSFCN =>
          zero?(cc := coefficient(uts, 0)) =>
            error concat("asech: ",NPOWLOG)
          x := 1 - uts * uts
          cc = 1 or cc = -1 =>
            -- compute order of 'x'
            (ord := orderOrFailed x) case "failed" =>
              error concat("asech: ",MAYFPOW)
            (order := ord@I) = -1 => return asech(cc) :: %
            odd? order => error concat("asech: ",FPOWERS)
            (utsInv := iExquo(1,uts,true)) case "failed" =>
              error concat("asech: ",NOTINV)
            cLog((1 + cRationalPower(x, 1/2)) * (utsInv@%))
          (utsInv := iExquo(1,uts,true)) case "failed" =>
            error concat("asech: ",NOTINV)
          cLog((1 + cRationalPower(x, 1/2)) * (utsInv@%))
        error concat("asech: ",TRCONST)

      cAcsch uts ==
        zero? uts => error "acsch: acsch(0) is undefined"
        TRANSFCN =>
          zero?(cc := coefficient(uts,0)) => error concat("acsch: ",NPOWLOG)
          x := uts * uts + 1
          -- compute order of 'x'
          (ord := orderOrFailed x) case "failed" =>
            error concat("acsc: ",MAYFPOW)
          (order := ord@I) = -1 => return acsch(cc) :: %
          odd? order => error concat("acsch: ",FPOWERS)
          (utsInv := iExquo(1,uts,true)) case "failed" =>
            error concat("acsch: ",NOTINV)
          cLog((1 + cRationalPower(x, 1/2)) * (utsInv@%))
        error concat("acsch: ",TRCONST)

--% Output forms

    -- check a global Lisp variable
    factorials?() == false

    termOutput(k, c, vv) ==
    -- creates a term c * vv ^ k
      k = 0 => c :: OUT
      mon := (k = 1 => vv; vv ^ (k :: OUT))
--       if factorials?() and k > 1 then
--         c := factorial(k)$IntegerCombinatoricFunctions * c
--         mon := mon / hconcat(k :: OUT,"!" :: OUT)
      c = 1 => mon
      c = -1 => -mon
      (c :: OUT) * mon

    -- check a global Lisp variable
    showAll?() == true

    seriesToOutputForm(st, refer, var, cen, r) ==
      vv :=
        zero? cen => var :: OUT
        paren(var :: OUT - cen :: OUT)
      l : L OUT := empty()
      while explicitEntries? st repeat
        term := frst st
        l := concat(termOutput(getExpon(term) * r, getCoef term, vv), l)
        st := rst st
      l :=
        explicitlyEmpty? st => l
        (deg := retractIfCan(elt refer)@Union(I,"failed")) case I =>
            concat(prefix('O :: OUT, [vv^((((deg@I) + 1)*r) :: OUT)]), l)
        l
      empty? l => (0$Coef) :: OUT
      reduce("+",reverse! l)

)abbrev domain SUTS SparseUnivariateTaylorSeries
++ Author: Clifton J. Williamson
++ Date Created: 16 February 1990
++ Basic Operations:
++ Related Domains: InnerSparseUnivariatePowerSeries,
++   SparseUnivariateLaurentSeries, SparseUnivariatePuiseuxSeries
++ Also See:
++ AMS Classifications:
++ Keywords: Taylor series, sparse power series
++ Examples:
++ References:
++ Description: Sparse Taylor series in one variable
++   \spadtype{SparseUnivariateTaylorSeries} is a domain representing Taylor
++   series in one variable with coefficients in an arbitrary ring.  The
++   parameters of the type specify the coefficient ring, the power series
++   variable, and the center of the power series expansion.  For example,
++   \spadtype{SparseUnivariateTaylorSeries}(Integer, x, 3) represents Taylor
++   series in \spad{(x - 3)} with \spadtype{Integer} coefficients.
SparseUnivariateTaylorSeries(Coef, var, cen) : Exports == Implementation where
  Coef : Ring
  var  : Symbol
  cen  : Coef
  COM  ==> OrderedCompletion Integer
  I    ==> Integer
  L    ==> List
  NNI  ==> NonNegativeInteger
  OUT  ==> OutputForm
  P    ==> Polynomial Coef
  REF  ==> Reference OrderedCompletion Integer
  RN   ==> Fraction Integer
  Term ==> Record(k : Integer, c : Coef)
  SG   ==> String
  ST   ==> Stream Term
  UP   ==> UnivariatePolynomial(var, Coef)

  Exports ==> UnivariateTaylorSeriesCategory(Coef) with
    coerce : UP -> %
      ++\spad{coerce(p)} converts a univariate polynomial p in the variable
      ++\spad{var} to a univariate Taylor series in \spad{var}.
    univariatePolynomial : (%, NNI) -> UP
      ++\spad{univariatePolynomial(f, k)} returns a univariate polynomial
      ++ consisting of the sum of all terms of f of degree \spad{<= k}.
    coerce : Variable(var) -> %
      ++\spad{coerce(var)} converts the series variable \spad{var} into a
      ++ Taylor series.
    differentiate : (%, Variable(var)) -> %
      ++ \spad{differentiate(f(x), x)} computes the derivative of
      ++ \spad{f(x)} with respect to \spad{x}.
    if Coef has Algebra Fraction Integer then
      integrate : (%, Variable(var)) -> %
        ++ \spad{integrate(f(x), x)} returns an anti-derivative of the power
        ++ series \spad{f(x)} with constant coefficient 0.
        ++ We may integrate a series when we can divide coefficients
        ++ by integers.

  Implementation ==> InnerSparseUnivariatePowerSeries(Coef) add
    import from REF

    Rep := InnerSparseUnivariatePowerSeries(Coef)

    makeTerm : (Integer, Coef) -> Term
    makeTerm(exp, coef) == [exp, coef]
    getCoef : Term -> Coef
    getCoef term == term.c
    getExpon : Term -> Integer
    getExpon term == term.k

    monomial(coef, expon) == monomial(coef, expon)$Rep
    extend(x, n) == extend(x, n)$Rep

    0 == monomial(0, 0)$Rep
    1 == monomial(1, 0)$Rep

    recip uts == iExquo(1, uts, true)

    if Coef has IntegralDomain then
      uts1 exquo uts2 == iExquo(uts1, uts2, true)

    quoByVar uts == taylorQuoByVar(uts)$Rep

    differentiate(x : %, v : Variable(var)) == differentiate x

--% Creation and destruction of series

    coerce(v : Variable(var)) ==
      zero? cen => monomial(1, 1)
      monomial(1, 1) + monomial(cen, 0)

    coerce(p : UP) ==
      zero? p => 0
      if not zero? cen then p := p(monomial(1, 1)$UP + monomial(cen, 0)$UP)
      st : ST := empty()
      while not zero? p repeat
        st := concat(makeTerm(degree p, leadingCoefficient p), st)
        p := reductum p
      makeSeries(ref plusInfinity(), st)

    univariatePolynomial(x, n) ==
      extend(x, n); st := getStream x
      ans : UP := 0; oldDeg : I := 0;
      mon := monomial(1, 1)$UP - monomial(center x, 0)$UP; monPow : UP := 1
      while explicitEntries? st repeat
        (xExpon := getExpon(xTerm := frst st)) > n => return ans
        pow := (xExpon - oldDeg) :: NNI; oldDeg := xExpon
        monPow := monPow * mon ^ pow
        ans := ans + getCoef(xTerm) * monPow
        st := rst st
      ans

    polynomial(x, n) ==
      extend(x, n); st := getStream x
      ans : P := 0; oldDeg : I := 0;
      mon := (var :: P) - (center(x) :: P); monPow : P := 1
      while explicitEntries? st repeat
        (xExpon := getExpon(xTerm := frst st)) > n => return ans
        pow := (xExpon - oldDeg) :: NNI; oldDeg := xExpon
        monPow := monPow * mon ^ pow
        ans := ans + getCoef(xTerm) * monPow
        st := rst st
      ans

    polynomial(x, n1, n2) == polynomial(truncate(x, n1, n2), n2)

    truncate(x, n)     == truncate(x, n)$Rep
    truncate(x, n1, n2) == truncate(x, n1, n2)$Rep

    iCoefficients : (ST, REF, I) -> Stream Coef
    iCoefficients(x, refer, n) == delay
      -- when this function is called, we are computing the nth order
      -- coefficient of the series
      explicitlyEmpty? x => empty()
      -- if terms up to order n have not been computed,
      -- apply lazy evaluation
      nn := n :: COM
      while (nx := elt refer) < nn repeat lazyEvaluate x
      -- must have nx >= n
      explicitEntries? x =>
        xCoef := getCoef(xTerm := frst x); xExpon := getExpon xTerm
        xExpon = n => concat(xCoef, iCoefficients(rst x, refer, n + 1))
        -- must have nx > n
        concat(0, iCoefficients(x, refer, n + 1))
      concat(0, iCoefficients(x, refer, n + 1))

    coefficients uts ==
      refer := getRef uts; x := getStream uts
      iCoefficients(x, refer, 0)

    terms uts == terms(uts)$Rep pretend Stream Record(k : NNI, c : Coef)

    iSeries : (Stream Coef, I, REF) -> ST
    iSeries(st, n, refer) == delay
      -- when this function is called, we are creating the nth order
      -- term of a series
      empty? st => (setelt!(refer, plusInfinity()); empty())
      setelt!(refer, n :: COM)
      zero? (coef := frst st) => iSeries(rst st, n + 1, refer)
      concat(makeTerm(n, coef), iSeries(rst st, n + 1, refer))

    series(st : Stream Coef) ==
      refer := ref((-1)@I::COM)
      makeSeries(refer, iSeries(st, 0, refer))

    -- We consider the Rep of  InnerSparseUnivariatePowerSeries as
    -- local knowledge and build on the fact that NNI=SubDomain(INT, #>=0)
    nniToI x ==> (x @ Stream Record(k : NNI, c : Coef)) pretend ST
    series(st : Stream Record(k : NNI, c : Coef)) == series(nniToI st)$Rep

--% Values

    variable x == var
    center   x == cen

    coefficient(x, n) == coefficient(x, n)$Rep
    elt(x : %, n : NonNegativeInteger) == coefficient(x, n)

    pole? x == false

    order x    == (order(x)$Rep) :: NNI
    order(x, n) == (order(x, n)$Rep) :: NNI

--% Composition

    elt(uts1 : %, uts2 : %) ==
      zero? uts2 => coefficient(uts1, 0) :: %
      not zero? coefficient(uts2, 0) =>
        error "elt: second argument must have positive order"
      iCompose(uts1, uts2)

--% Integration

    if Coef has Algebra Fraction Integer then

      integrate(x : %, v : Variable(var)) == integrate x

--% Transcendental functions

      (uts1 : %) ^ (uts2 : %) == exp(log(uts1) * uts2)

      if Coef has CommutativeRing then

        (uts : %) ^ (r : RN) == cRationalPower(uts, r)

        exp uts == cExp uts
        log uts == cLog uts

        sin uts == cSin uts
        cos uts == cCos uts
        tan uts == cTan uts
        cot uts == cCot uts
        sec uts == cSec uts
        csc uts == cCsc uts

        asin uts == cAsin uts
        acos uts == cAcos uts
        atan uts == cAtan uts
        acot uts == cAcot uts
        asec uts == cAsec uts
        acsc uts == cAcsc uts

        sinh uts == cSinh uts
        cosh uts == cCosh uts
        tanh uts == cTanh uts
        coth uts == cCoth uts
        sech uts == cSech uts
        csch uts == cCsch uts

        asinh uts == cAsinh uts
        acosh uts == cAcosh uts
        atanh uts == cAtanh uts
        acoth uts == cAcoth uts
        asech uts == cAsech uts
        acsch uts == cAcsch uts

      else

        ZERO    : SG := "series must have constant coefficient zero"
        ONE     : SG := "series must have constant coefficient one"
        NPOWERS : SG := "series expansion has terms of negative degree"

        (uts : %) ^ (r : RN) ==
          not (coefficient(uts, 0) = 1) =>
            error "^: constant coefficient must be one"
          ratPow := cPower(uts, r :: Coef)
          iCompose(ratPow, uts - 1)

        exp uts ==
          zero? coefficient(uts, 0) =>
            expx := cExp monomial(1, 1)
            iCompose(expx, uts)
          error concat("exp: ",ZERO)

        log uts ==
          (coefficient(uts, 0) = 1) =>
            log1PlusX := cLog(monomial(1, 0) + monomial(1, 1))
            iCompose(log1PlusX, uts - 1)
          error concat("log: ",ONE)

        sin uts ==
          zero? coefficient(uts, 0) =>
            sinx := cSin monomial(1, 1)
            iCompose(sinx, uts)
          error concat("sin: ",ZERO)

        cos uts ==
          zero? coefficient(uts, 0) =>
            cosx := cCos monomial(1, 1)
            iCompose(cosx, uts)
          error concat("cos: ",ZERO)

        tan uts ==
          zero? coefficient(uts, 0) =>
            tanx := cTan monomial(1, 1)
            iCompose(tanx, uts)
          error concat("tan: ",ZERO)

        cot uts ==
          zero? uts => error "cot: cot(0) is undefined"
          zero? coefficient(uts,0) => error concat("cot: ",NPOWERS)
          error concat("cot: ",ZERO)

        sec uts ==
          zero? coefficient(uts, 0) =>
            secx := cSec monomial(1, 1)
            iCompose(secx, uts)
          error concat("sec: ",ZERO)

        csc uts ==
          zero? uts => error "csc: csc(0) is undefined"
          zero? coefficient(uts,0) => error concat("csc: ",NPOWERS)
          error concat("csc: ",ZERO)

        asin uts ==
          zero? coefficient(uts, 0) =>
            asinx := cAsin monomial(1, 1)
            iCompose(asinx, uts)
          error concat("asin: ",ZERO)

        atan uts ==
          zero? coefficient(uts, 0) =>
            atanx := cAtan monomial(1, 1)
            iCompose(atanx, uts)
          error concat("atan: ",ZERO)

        acos z == error "acos: acos undefined on this coefficient domain"
        acot z == error "acot: acot undefined on this coefficient domain"
        asec z == error "asec: asec undefined on this coefficient domain"
        acsc z == error "acsc: acsc undefined on this coefficient domain"

        sinh uts ==
          zero? coefficient(uts, 0) =>
            sinhx := cSinh monomial(1, 1)
            iCompose(sinhx, uts)
          error concat("sinh: ",ZERO)

        cosh uts ==
          zero? coefficient(uts, 0) =>
            coshx := cCosh monomial(1, 1)
            iCompose(coshx, uts)
          error concat("cosh: ",ZERO)

        tanh uts ==
          zero? coefficient(uts, 0) =>
            tanhx := cTanh monomial(1, 1)
            iCompose(tanhx, uts)
          error concat("tanh: ",ZERO)

        coth uts ==
          zero? uts => error "coth: coth(0) is undefined"
          zero? coefficient(uts,0) => error concat("coth: ",NPOWERS)
          error concat("coth: ",ZERO)

        sech uts ==
          zero? coefficient(uts, 0) =>
            sechx := cSech monomial(1, 1)
            iCompose(sechx, uts)
          error concat("sech: ",ZERO)

        csch uts ==
          zero? uts => error "csch: csch(0) is undefined"
          zero? coefficient(uts,0) => error concat("csch: ",NPOWERS)
          error concat("csch: ",ZERO)

        asinh uts ==
          zero? coefficient(uts, 0) =>
            asinhx := cAsinh monomial(1, 1)
            iCompose(asinhx, uts)
          error concat("asinh: ",ZERO)

        atanh uts ==
          zero? coefficient(uts, 0) =>
            atanhx := cAtanh monomial(1, 1)
            iCompose(atanhx, uts)
          error concat("atanh: ",ZERO)

        acosh uts == error "acosh: acosh undefined on this coefficient domain"
        acoth uts == error "acoth: acoth undefined on this coefficient domain"
        asech uts == error "asech: asech undefined on this coefficient domain"
        acsch uts == error "acsch: acsch undefined on this coefficient domain"

    if Coef has Field then
      if Coef has Algebra Fraction Integer then

        (uts : %) ^ (r : Coef) ==
          not (coefficient(uts, 1) = 1) =>
            error "^: constant coefficient should be 1"
          cPower(uts, r)

--% OutputForms

    coerce(x : %) : OUT ==
      count : NNI := _$streamCount$Lisp
      extend(x, count)
      seriesToOutputForm(getStream x, getRef x, variable x, center x, 1)

)abbrev domain SULS SparseUnivariateLaurentSeries
++ Author: Clifton J. Williamson
++ Date Created: 11 November 1994
++ Basic Operations:
++ Related Domains: InnerSparseUnivariatePowerSeries,
++   SparseUnivariateTaylorSeries, SparseUnivariatePuiseuxSeries
++ Also See:
++ AMS Classifications:
++ Keywords: sparse, series
++ Examples:
++ References:
++ Description: Sparse Laurent series in one variable
++   \spadtype{SparseUnivariateLaurentSeries} is a domain representing Laurent
++   series in one variable with coefficients in an arbitrary ring.  The
++   parameters of the type specify the coefficient ring, the power series
++   variable, and the center of the power series expansion.  For example,
++   \spad{SparseUnivariateLaurentSeries(Integer, x, 3)} represents Laurent
++   series in \spad{(x - 3)} with integer coefficients.
SparseUnivariateLaurentSeries(Coef, var, cen) : Exports == Implementation where
  Coef : Ring
  var  : Symbol
  cen  : Coef
  I     ==> Integer
  NNI   ==> NonNegativeInteger
  OUT   ==> OutputForm
  P     ==> Polynomial Coef
  RF    ==> Fraction Polynomial Coef
  RN    ==> Fraction Integer
  S     ==> String
  SUTS  ==> SparseUnivariateTaylorSeries(Coef, var, cen)
  EFULS ==> ElementaryFunctionsUnivariateLaurentSeries(Coef, SUTS, %)

  Exports ==> UnivariateLaurentSeriesConstructorCategory(Coef, SUTS) with
    coerce : Variable(var) -> %
      ++ \spad{coerce(var)} converts the series variable \spad{var} into a
      ++ Laurent series.
    differentiate : (%, Variable(var)) -> %
      ++ \spad{differentiate(f(x), x)} returns the derivative of
      ++ \spad{f(x)} with respect to \spad{x}.
    if Coef has Algebra Fraction Integer then
      integrate : (%, Variable(var)) -> %
        ++ \spad{integrate(f(x))} returns an anti-derivative of the power
        ++ series \spad{f(x)} with constant coefficient 0.
        ++ We may integrate a series when we can divide coefficients
        ++ by integers.

  Implementation ==> InnerSparseUnivariatePowerSeries(Coef) add

    Rep := InnerSparseUnivariatePowerSeries(Coef)

    variable x == var
    center   x == cen

    coerce(v : Variable(var)) ==
      zero? cen => monomial(1, 1)
      monomial(1, 1) + monomial(cen, 0)

    pole? x == negative? order(x, 0)

--% operations with Taylor series

    coerce(uts : SUTS) == uts pretend %

    taylorIfCan uls ==
      pole? uls => "failed"
      uls pretend SUTS

    taylor uls ==
      (uts := taylorIfCan uls) case "failed" =>
        error "taylor: Laurent series has a pole"
      uts

    retractIfCan(x:%):Union(SUTS,"failed") == taylorIfCan x

    laurent(n: I, uts: SUTS): % == monomial(1, n) * (uts :: %)

    removeZeroes uls    == uls
    removeZeroes(n, uls) == uls

    taylorRep uls == taylor(monomial(1, -order(uls, 0)) * uls)
    degree uls    == order(uls, 0)

    numer uls == taylorRep uls
    denom uls == monomial(1, (-order(uls, 0)) :: NNI)$SUTS

    (uts : SUTS) * (uls : %) == (uts :: %) * uls
    (uls : %) * (uts : SUTS) == uls * (uts :: %)

    if Coef has Field then
      (uts1 : SUTS) / (uts2 : SUTS) == (uts1 :: %) / (uts2 :: %)

    recip(uls) == iExquo(1, uls, false)

    if Coef has IntegralDomain then
      uls1 exquo uls2 == iExquo(uls1, uls2, false)

    if Coef has Field then
      uls1 : % / uls2 : % ==
        (q := uls1 exquo uls2) case "failed" =>
          error "quotient cannot be computed"
        q

    differentiate(uls : %, v : Variable(var)) == differentiate uls

    elt(uls1 : %, uls2 : %) ==
      order(uls2, 1) < 1 =>
        error "elt: second argument must have positive order"
      negative?(ord := order(uls1, 0)) =>
        (recipr := recip uls2) case "failed" =>
          error "elt: second argument not invertible"
        uls3 := uls1 * monomial(1, -ord)
        iCompose(uls3, uls2) * (recipr@%) ^ ((-ord) :: NNI)
      iCompose(uls1, uls2)

    if Coef has IntegralDomain then
      rationalFunction(uls, n) ==
        zero?(e := order(uls, 0)) =>
          negative? n => 0
          polynomial(taylor uls, n :: NNI) :: RF
        negative?(m := n - e) => 0
        poly := polynomial(taylor(monomial(1, -e) * uls), m :: NNI) :: RF
        v := variable(uls) :: RF; c := center(uls) :: P :: RF
        poly / (v - c) ^ ((-e) :: NNI)

      rationalFunction(uls, n1, n2) == rationalFunction(truncate(uls, n1, n2), n2)

    if Coef has Algebra Fraction Integer then

      integrate uls ==
        zero? coefficient(uls, -1) =>
          error "integrate: series has term of order -1"
        integrate(uls)$Rep

      integrate(uls : %, v : Variable(var)) == integrate uls

      (uls1 : %) ^ (uls2 : %) == exp(log(uls1) * uls2)

      exp uls   == exp(uls)$EFULS
      log uls   == log(uls)$EFULS
      sin uls   == sin(uls)$EFULS
      cos uls   == cos(uls)$EFULS
      tan uls   == tan(uls)$EFULS
      cot uls   == cot(uls)$EFULS
      sec uls   == sec(uls)$EFULS
      csc uls   == csc(uls)$EFULS
      asin uls  == asin(uls)$EFULS
      acos uls  == acos(uls)$EFULS
      atan uls  == atan(uls)$EFULS
      acot uls  == acot(uls)$EFULS
      asec uls  == asec(uls)$EFULS
      acsc uls  == acsc(uls)$EFULS
      sinh uls  == sinh(uls)$EFULS
      cosh uls  == cosh(uls)$EFULS
      tanh uls  == tanh(uls)$EFULS
      coth uls  == coth(uls)$EFULS
      sech uls  == sech(uls)$EFULS
      csch uls  == csch(uls)$EFULS
      asinh uls == asinh(uls)$EFULS
      acosh uls == acosh(uls)$EFULS
      atanh uls == atanh(uls)$EFULS
      acoth uls == acoth(uls)$EFULS
      asech uls == asech(uls)$EFULS
      acsch uls == acsch(uls)$EFULS

      if Coef has CommutativeRing then

        (uls : %) ^ (r : RN) == cRationalPower(uls, r)

      else

        (uls : %) ^ (r : RN) ==
          negative?(ord0 := order(uls, 0)) =>
            order := ord0 :: I
            (n := order exquo denom(r)) case "failed" =>
              error "^: rational power does not exist"
            uts := retract(uls * monomial(1, -order))@SUTS
            utsPow := (uts ^ r) :: %
            monomial(1, (n@I) * numer(r)) * utsPow
          uts := retract(uls)@SUTS
          (uts ^ r) :: %

--% OutputForms

    coerce(uls : %) : OUT ==
      st := getStream uls
      if not(explicitlyEmpty? st or explicitEntries? st) _
        and (nx := retractIfCan(elt getRef uls))@Union(I,"failed") case I then
        count : NNI := _$streamCount$Lisp
        degr := min(count, (nx::I) + count + 1)
        extend(uls, degr)
      seriesToOutputForm(st, getRef uls, variable uls, center uls, 1)

)abbrev domain SUPXS SparseUnivariatePuiseuxSeries
++ Author: Clifton J. Williamson
++ Date Created: 11 November 1994
++ Basic Operations:
++ Related Domains: InnerSparseUnivariatePowerSeries,
++   SparseUnivariateTaylorSeries, SparseUnivariateLaurentSeries
++ Also See:
++ AMS Classifications:
++ Keywords: sparse, series
++ Examples:
++ References:
++ Description: Sparse Puiseux series in one variable
++   \spadtype{SparseUnivariatePuiseuxSeries} is a domain representing Puiseux
++   series in one variable with coefficients in an arbitrary ring.  The
++   parameters of the type specify the coefficient ring, the power series
++   variable, and the center of the power series expansion.  For example,
++   \spad{SparseUnivariatePuiseuxSeries(Integer, x, 3)} represents Puiseux
++   series in \spad{(x - 3)} with \spadtype{Integer} coefficients.
SparseUnivariatePuiseuxSeries(Coef, var, cen) : Exports == Implementation where
  Coef : Ring
  var  : Symbol
  cen  : Coef
  I    ==> Integer
  NNI  ==> NonNegativeInteger
  OUT  ==> OutputForm
  RN   ==> Fraction Integer
  SUTS ==> SparseUnivariateTaylorSeries(Coef, var, cen)
  SULS ==> SparseUnivariateLaurentSeries(Coef, var, cen)
  SUPS ==> InnerSparseUnivariatePowerSeries(Coef)

  Exports ==> Join(UnivariatePuiseuxSeriesConstructorCategory(Coef, SULS), _
                   RetractableTo SUTS) with
    coerce : Variable(var) -> %
      ++ coerce(var) converts the series variable \spad{var} into a
      ++ Puiseux series.
    differentiate : (%, Variable(var)) -> %
      ++ \spad{differentiate(f(x), x)} returns the derivative of
      ++ \spad{f(x)} with respect to \spad{x}.
    if Coef has Algebra Fraction Integer then
      integrate : (%, Variable(var)) -> %
        ++ \spad{integrate(f(x))} returns an anti-derivative of the power
        ++ series \spad{f(x)} with constant coefficient 0.
        ++ We may integrate a series when we can divide coefficients
        ++ by integers.

  Implementation ==> UnivariatePuiseuxSeriesConstructor(Coef, SULS) add

    Rep := Record(expon : RN, lSeries : SULS)

    getExpon : % -> RN
    getExpon pxs == pxs.expon

    variable x == var
    center   x == cen

    coerce(v : Variable(var)) ==
      zero? cen => monomial(1, 1)
      monomial(1, 1) + monomial(cen, 0)

    coerce(uts : SUTS) == uts :: SULS :: %

    retractIfCan(upxs:%):Union(SUTS,"failed") ==
      (uls := retractIfCan(upxs)@Union(SULS,"failed")) case "failed" =>
        "failed"
      retractIfCan(uls@SULS)@Union(SUTS,"failed")

    if Coef has "*": (Fraction Integer, Coef) -> Coef then
      differentiate(upxs : %, v : Variable(var)) == differentiate upxs

    if Coef has Algebra Fraction Integer then
      integrate(upxs : %, v : Variable(var)) == integrate upxs

--% OutputForms

    coerce(x : %) : OUT ==
      sups : SUPS := laurentRep(x) pretend SUPS
      st := getStream sups; refer := getRef sups
      if not(explicitlyEmpty? st or explicitEntries? st) _
        and (nx := retractIfCan(elt refer)@Union(I,"failed")) case I then
        count : NNI := _$streamCount$Lisp
        degr := min(count, (nx@I) + count + 1)
        extend(sups, degr)
      seriesToOutputForm(st, refer, variable x, center x, rationalPower x)

--Copyright (c) 1991-2002, The Numerical ALgorithms Group Ltd.
--All rights reserved.
--
--Redistribution and use in source and binary forms, with or without
--modification, are permitted provided that the following conditions are
--met:
--
--    - Redistributions of source code must retain the above copyright
--      notice, this list of conditions and the following disclaimer.
--
--    - Redistributions in binary form must reproduce the above copyright
--      notice, this list of conditions and the following disclaimer in
--      the documentation and/or other materials provided with the
--      distribution.
--
--    - Neither the name of The Numerical ALgorithms Group Ltd. nor the
--      names of its contributors may be used to endorse or promote products
--      derived from this software without specific prior written permission.
--
--THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
--IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
--TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
--PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
--OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
--EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
--PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
--PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
--LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
--NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
--SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
