\documentclass{article}
\usepackage{axiom}
\begin{document}
\title{\$SPAD/src/algebra fs2ups.spad}
\author{Clifton J. Williamson}
\maketitle
\begin{abstract}
\end{abstract}
\eject
\tableofcontents
\eject
\section{package FS2UPS FunctionSpaceToUnivariatePowerSeries}
<<package FS2UPS FunctionSpaceToUnivariatePowerSeries>>=
)abbrev package FS2UPS FunctionSpaceToUnivariatePowerSeries
++ Author: Clifton J. Williamson
++ Date Created: 21 March 1989
++ Date Last Updated: 2 December 1994
++ Basic Operations:
++ Related Domains:
++ Also See:
++ AMS Classifications:
++ Keywords: elementary function, power series
++ Examples:
++ References:
++ Description:
++   This package converts expressions in some function space to power
++   series in a variable x with coefficients in that function space.
++   The function \spadfun{exprToUPS} converts expressions to power series
++   whose coefficients do not contain the variable x. The function
++   \spadfun{exprToGenUPS} converts functional expressions to power series
++   whose coefficients may involve functions of \spad{log(x)}.
FunctionSpaceToUnivariatePowerSeries(R, FE, Expon, UPS, TRAN, UTS, TEXP, x):_
 Exports == Implementation where
  R     : Join(GcdDomain, Comparable, RetractableTo Integer,
               LinearlyExplicitRingOver Integer)
  FE    : Join(AlgebraicallyClosedField, TranscendentalFunctionCategory,
               Algebra(Fraction(Integer)), FunctionSpace R)
            with
              coerce: Expon -> %
                ++ coerce(e) converts an 'exponent' e to an 'expression'
  Expon : Join(OrderedAbelianMonoid, OrderedRing)
  UPS   : Join(UnivariatePowerSeriesCategory(FE,Expon),Field,
               PowerSeriesCategory(FE, Expon, SingletonAsOrderedSet),
               TranscendentalFunctionCategory)
            with
              differentiate: % -> %
                ++ differentiate(x) returns the derivative of x since we
                ++ need to be able to differentiate a power series
              integrate: % -> %
                ++ integrate(x) returns the integral of x since
                ++ we need to be able to integrate a power series
  TRAN  : PartialTranscendentalFunctions UPS
  UTS : UnivariateTaylorSeriesCategory(FE)
  TEXP : TaylorSeriesExpansion(FE, Expon, SingletonAsOrderedSet, UPS, UTS)
  x     : Symbol
  B       ==> Boolean
  BOP     ==> BasicOperator
  I       ==> Integer
  NNI     ==> NonNegativeInteger
  K       ==> Kernel FE
  L       ==> List
  RN      ==> Fraction Integer
  ATANFLAG ==> Union("complex", _
                    "real: two sides", _
                    "real: left side", _
                    "real: right side", _
                    "just do it")
  PROBLEM ==> String
  FUNCTIONNAME ==> String
  SY      ==> Symbol
  PCL     ==> PolynomialCategoryLifting(IndexedExponents K,K,R,SMP,FE)
  POL     ==> Polynomial R
  SMP     ==> SparseMultivariatePolynomial(R,K)
  SUP     ==> SparseUnivariatePolynomial Polynomial R
  Problem ==> Record(func:String,prob:String)
  Result  ==> Union(%series:UPS,%problem:Problem)
  SIGNEF  ==> ElementaryFunctionSign(R,FE)

  Exports ==> with
    exprToUPS : (FE,B,ATANFLAG) -> Result
      ++ exprToUPS(fcn,posCheck?,atanFlag) converts the expression
      ++ \spad{fcn} to a power series.  If \spad{posCheck?} is true,
      ++ log's of negative numbers are not allowed nor are nth roots of
      ++ negative numbers with n even.  If \spad{posCheck?} is false,
      ++ these are allowed.  \spad{atanFlag} determines how the case
      ++ \spad{atan(f(x))}, where \spad{f(x)} has a pole, will be treated.
      ++ The possible values of \spad{atanFlag} are \spad{"complex"},
      ++ \spad{"real: two sides"}, \spad{"real: left side"},
      ++ \spad{"real: right side"}, and \spad{"just do it"}.
      ++ If \spad{atanFlag} is \spad{"complex"}, then no series expansion
      ++ will be computed because, viewed as a function of a complex
      ++ variable, \spad{atan(f(x))} has an essential singularity.
      ++ Otherwise, the sign of the leading coefficient of the series
      ++ expansion of \spad{f(x)} determines the constant coefficient
      ++ in the series expansion of \spad{atan(f(x))}.  If this sign cannot
      ++ be determined, a series expansion is computed only when
      ++ \spad{atanFlag} is \spad{"just do it"}.  When the leading term
      ++ in the series expansion of \spad{f(x)} is of odd degree (or is a
      ++ rational degree with odd numerator), then the constant coefficient
      ++ in the series expansion of \spad{atan(f(x))} for values to the
      ++ left differs from that for values to the right.  If \spad{atanFlag}
      ++ is \spad{"real: two sides"}, no series expansion will be computed.
      ++ If \spad{atanFlag} is \spad{"real: left side"} the constant
      ++ coefficient for values to the left will be used and if \spad{atanFlag}
      ++ \spad{"real: right side"} the constant coefficient for values to the
      ++ right will be used.
      ++ If there is a problem in converting the function to a power series,
      ++ a record containing the name of the function that caused the problem
      ++ and a brief description of the problem is returned.
      ++ When expanding the expression into a series it is assumed that
      ++ the series is centered at 0.  For a series centered at a, the
      ++ user should perform the substitution \spad{x -> x + a} before calling
      ++ this function.

    exprToGenUPS : (FE,B,ATANFLAG) -> Result
      ++ exprToGenUPS(fcn,posCheck?,atanFlag) converts the expression
      ++ \spad{fcn} to a generalized power series.  If \spad{posCheck?}
      ++ is true, log's of negative numbers are not allowed nor are nth roots
      ++ of negative numbers with n even. If \spad{posCheck?} is false,
      ++ these are allowed.  \spad{atanFlag} determines how the case
      ++ \spad{atan(f(x))}, where \spad{f(x)} has a pole, will be treated.
      ++ The possible values of \spad{atanFlag} are \spad{"complex"},
      ++ \spad{"real: two sides"}, \spad{"real: left side"},
      ++ \spad{"real: right side"}, and \spad{"just do it"}.
      ++ If \spad{atanFlag} is \spad{"complex"}, then no series expansion
      ++ will be computed because, viewed as a function of a complex
      ++ variable, \spad{atan(f(x))} has an essential singularity.
      ++ Otherwise, the sign of the leading coefficient of the series
      ++ expansion of \spad{f(x)} determines the constant coefficient
      ++ in the series expansion of \spad{atan(f(x))}.  If this sign cannot
      ++ be determined, a series expansion is computed only when
      ++ \spad{atanFlag} is \spad{"just do it"}.  When the leading term
      ++ in the series expansion of \spad{f(x)} is of odd degree (or is a
      ++ rational degree with odd numerator), then the constant coefficient
      ++ in the series expansion of \spad{atan(f(x))} for values to the
      ++ left differs from that for values to the right.  If \spad{atanFlag}
      ++ is \spad{"real: two sides"}, no series expansion will be computed.
      ++ If \spad{atanFlag} is \spad{"real: left side"} the constant
      ++ coefficient for values to the left will be used and if \spad{atanFlag}
      ++ \spad{"real: right side"} the constant coefficient for values to the
      ++ right will be used.
      ++ If there is a problem in converting the function to a power
      ++ series, we return a record containing the name of the function
      ++ that caused the problem and a brief description of the problem.
      ++ When expanding the expression into a series it is assumed that
      ++ the series is centered at 0.  For a series centered at a, the
      ++ user should perform the substitution \spad{x -> x + a} before calling
      ++ this function.
    localAbs: FE -> FE
      ++ localAbs(fcn) = \spad{abs(fcn)} or \spad{sqrt(fcn^2)} depending
      ++ on whether or not FE has a function \spad{abs}.  This should be
      ++ a local function, but the compiler won't allow it.

  Implementation ==> add

    ratIfCan            : FE -> Union(RN,"failed")
    carefulNthRootIfCan : (UPS,NNI,B,B) -> Result
    stateProblem        : (PROBLEM, PROBLEM) -> Result
    polyToUPS           : SUP -> UPS
    listToUPS           : (L FE,(FE,B,ATANFLAG) -> Result,B,ATANFLAG,UPS,(UPS,UPS) -> UPS)_
                                            -> Result
    isNonTrivPower      : FE -> Union(Record(val:FE,exponent:I),"failed")
    powerToUPS          : (FE,I,B,ATANFLAG) -> Result
    kernelToUPS         : (K,B,ATANFLAG) -> Result
    nthRootToUPS        : (FE,NNI,B,ATANFLAG) -> Result
    logToUPS            : (FE,B,ATANFLAG) -> Result
    atancotToUPS        : (FE,B,ATANFLAG,I) -> Result
    applyIfCan          : (UPS -> Union(UPS,"failed"),FE,FUNCTIONNAME,B,ATANFLAG) -> Result
    tranToUPS           : (K,FE,B,ATANFLAG) -> Result
    powToUPS            : (L FE,B,ATANFLAG) -> Result
    newElem             : FE -> FE
    smpElem             : SMP -> FE
    k2Elem              : K -> FE
    contOnReals?        : FUNCTIONNAME -> B
    bddOnReals?         : FUNCTIONNAME -> B
    iExprToGenUPS       : (FE,B,ATANFLAG) -> Result
    opsInvolvingX       : FE -> L BOP
    opInOpList?         : (SY,L BOP) -> B
    exponential?        : FE -> B
    productOfNonZeroes? : FE -> B
    powerToGenUPS       : (FE,I,B,ATANFLAG) -> Result
    kernelToGenUPS      : (K,B,ATANFLAG) -> Result
    nthRootToGenUPS     : (FE,NNI,B,ATANFLAG) -> Result
    logToGenUPS         : (FE,B,ATANFLAG) -> Result
    expToGenUPS         : (FE,B,ATANFLAG) -> Result
    expGenUPS           : (UPS,B,ATANFLAG) -> Result
    atancotToGenUPS     : (FE,FE,B,ATANFLAG,I) -> Result
    genUPSApplyIfCan    : (UPS -> Union(UPS,"failed"),FE,FUNCTIONNAME,B,ATANFLAG) -> Result
    applyBddIfCan       : (FE,UPS -> Union(UPS,"failed"),FE,FUNCTIONNAME,B,ATANFLAG) -> Result
    tranToGenUPS        : (K,FE,B,ATANFLAG) -> Result
    powToGenUPS         : (L FE,B,ATANFLAG) -> Result

    ZEROCOUNT : I := 1000
    -- number of zeroes to be removed when taking logs or nth roots

    ratIfCan fcn == retractIfCan(fcn)@Union(RN,"failed")

    carefulNthRootIfCan(ups,n,posCheck?,rightOnly?) ==
      -- similar to 'nthRootIfCan', but it is fussy about the series
      -- it takes as an argument.  If 'n' is EVEN and 'posCheck?'
      -- is truem then the leading coefficient of the series must
      -- be POSITIVE.  In this case, if 'rightOnly?' is false, the
      -- order of the series must be zero.  The idea is that the
      -- series represents a real function of a real variable, and
      -- we want a unique real nth root defined on a neighborhood
      -- of zero.
      n < 1 => error "nthRoot: n must be positive"
      deg := degree ups
      if (coef := coefficient(ups,deg)) = 0 then
        deg := order(ups,deg + ZEROCOUNT :: Expon)
        (coef := coefficient(ups,deg)) = 0 =>
          error "log of series with many leading zero coefficients"
      -- if 'posCheck?' is true, we do not allow nth roots of negative
      -- numbers when n in even
      if even?(n :: I) then
        if posCheck? and ((signum := sign(coef)$SIGNEF) case I) then
          (signum :: I) = -1 =>
            return stateProblem("nth root","negative leading coefficient")
          not rightOnly? and not zero? deg => -- nth root not unique
            return stateProblem("nth root","series of non-zero order")
      (ans := nthRootIfCan(ups,n)) case "failed" =>
        stateProblem("nth root","no nth root")
      [ans :: UPS]

    stateProblem(function,problem) ==
      -- records the problem which occured in converting an expression
      -- to a power series
      [[function,problem]]

    exprToUPS(fcn,posCheck?,atanFlag) ==
      -- converts a functional expression to a power series
      --!! The following line is commented out so that expressions of
      --!! the form a^b will be normalized to exp(b * log(a)) even if
      --!! 'a' and 'b' do not involve the limiting variable 'x'.
      --!!                         - cjw 1 Dec 94
      --not member?(x,variables fcn) => [monomial(fcn,0)]
      (poly := retractIfCan(fcn)@Union(POL,"failed")) case POL =>
        [polyToUPS univariate(poly :: POL,x)]
      (sum := isPlus fcn) case L(FE) =>
        listToUPS(sum :: L(FE), exprToUPS, posCheck?, atanFlag, 0,
                  (y1, y2) +-> y1 + y2)
      (prod := isTimes fcn) case L(FE) =>
        listToUPS(prod :: L(FE), exprToUPS, posCheck?, atanFlag, 1,
                  (y1, y2) +-> y1*y2)
      (expt := isNonTrivPower fcn) case Record(val:FE,exponent:I) =>
        power := expt :: Record(val:FE,exponent:I)
        powerToUPS(power.val,power.exponent,posCheck?,atanFlag)
      (ker := retractIfCan(fcn)@Union(K,"failed")) case K =>
        kernelToUPS(ker :: K,posCheck?,atanFlag)
      error "exprToUPS: neither a sum, product, power, nor kernel"

    polyToUPS poly ==
      -- converts a polynomial to a power series
      zero? poly => 0
      -- we don't start with 'ans := 0' as this may lead to an
      -- enormous number of leading zeroes in the power series
      deg  := degree poly
      coef := leadingCoefficient(poly) :: FE
      ans  := monomial(coef,deg :: Expon)$UPS
      poly := reductum poly
      while not zero? poly repeat
        deg  := degree poly
        coef := leadingCoefficient(poly) :: FE
        ans  := ans + monomial(coef,deg :: Expon)$UPS
        poly := reductum poly
      ans

    listToUPS(list,feToUPS,posCheck?,atanFlag,ans,op) ==
      -- converts each element of a list of expressions to a power
      -- series and returns the sum of these series, when 'op' is +
      -- and 'ans' is 0, or the product of these series, when 'op' is *
      -- and 'ans' is 1
      while not null list repeat
        (term := feToUPS(first list,posCheck?,atanFlag)) case %problem =>
          return term
        ans := op(ans,term.%series)
        list := rest list
      [ans]

    isNonTrivPower fcn ==
      -- is the function a power with exponent other than 0 or 1?
      (expt := isPower fcn) case "failed" => "failed"
      power := expt :: Record(val:FE,exponent:I)
--      one? power.exponent => "failed"
      (power.exponent = 1) => "failed"
      power

    powerToUPS(fcn,n,posCheck?,atanFlag) ==
      -- converts an integral power to a power series
      (b := exprToUPS(fcn,posCheck?,atanFlag)) case %problem => b
      n > 0 => [(b.%series) ^ n]
      -- check lowest order coefficient when n < 0
      ups := b.%series; deg := degree ups
      if (coef := coefficient(ups,deg)) = 0 then
        deg := order(ups,deg + ZEROCOUNT :: Expon)
        (coef := coefficient(ups,deg)) = 0 =>
          error "inverse of series with many leading zero coefficients"
      [ups ^ n]

    ELITS := EllipticFunctionsUnivariateTaylorSeries(FE, UTS)

    ARES ==> Record(osers : L(UPS), sers : L(UPS), sere : L(FE), sers0 : L(FE))
    ARESU ==> Union(ARES, Result)

    handle_args(args : L(FE), posCheck? : B, atanFlag : ATANFLAG) : ARESU ==
        losers : L(UPS) := []
        lsers :  L(UPS) := []
        lsere : L(FE) := []
        lcoef : L(FE) := []
        for arg in args repeat
            not(member?(x, variables(arg))) =>
                losers := cons(arg::UPS, losers)
                lsers := cons(0$UPS, lsers)
                lsere := cons(0$FE, lsere)
                lcoef := cons(arg, lcoef)
            (nsu := exprToUPS(arg, posCheck?, atanFlag)) case %problem =>
                return nsu
            ups := nsu.%series
            order(ups, 0) < 0 =>
                return stateProblem("handle_args", "argument not Taylor")
            coef := coefficient(ups, 0)
            losers := cons(ups, losers)
            lcoef := cons(coef, lcoef)
            lsere := cons(1$FE, lsere)
            lsers := cons(ups - coef::UPS, lsers)
        [reverse!(losers), reverse!(lsers), reverse!(lsere),
          reverse!(lcoef)]$ARES

    do_taylor_via_deriv(nf : UPS, lsyms : L(Symbol), lser : L(UPS)) : Result ==
        lders : L(FE -> FE) := ([(c : FE) : FE +->
                   differentiate(c, sym) for sym in lsyms])
        ups : UPS := taylor_via_deriv(nf, lser, lders)$TEXP
        [map((c : FE) : FE +-> eval(c, [sym::FE = 0$FE for sym in lsyms]), ups)]

    do_taylor_via_deriv2(nk : FE, lsyms : L(Symbol), lser : L(UPS)) : Result ==
        lders : L(FE -> FE) := ([(c : FE) : FE +->
                   differentiate(c, sym) for sym in lsyms])
        ups : UPS := taylor_via_deriv(nk, lser, lders)$TEXP
        [map((c : FE) : FE +-> eval(c, [sym::FE = 0$FE for sym in lsyms]), ups)]

    NARGS ==> Record(nargs0 : L(FE), sers : L(UPS), syms : L(Symbol))

    convert_args(lsers : L(UPS), lsere : L(FE), lser0 : L(FE)) : NARGS ==
        lsyms : List(Symbol) := []
        lser : List(UPS) := []
        nargs : List(FE) := []
        for s in lsers for e in lsere for c in lser0 repeat
            e = 0 =>
                nargs := cons(c, nargs)
            nsym := new()$Symbol
            nargs := cons(c + nsym::FE, nargs)
            lsyms := cons(nsym, lsyms)
            lser := cons(s, lser)
        nargs := reverse!(nargs)
        [nargs, lser, lsyms]

    do_ell(losers : L(UPS), lsers : L(UPS), lsere : L(FE),
            lser0 : L(FE), ef : (UTS, L(FE)) -> UTS) : Result ==
        cargs := convert_args(rest lsers, rest lsere, rest lser0)
        nres := applyTaylor(f +-> ef(f, cargs.nargs0), losers(1))
        do_taylor_via_deriv(nres, cargs.syms, cargs.sers)

    do_ell2(losers : L(UPS), lsers : L(UPS), lsere : L(FE),
            lser0 : L(FE), ef : (UTS, FE) -> UTS) : Result ==
        do_ell(losers, lsers, lsere, lser0, (f, l) +-> ef(f, l(1)))

    do_ell3(losers : L(UPS), lsers : L(UPS), lsere : L(FE),
            lser0 : L(FE), ef : (UTS, FE, FE) -> UTS) : Result ==
        do_ell(losers, lsers, lsere, lser0, (f, l) +-> ef(f, l(1), l(2)))

    SupF ==> SparseUnivariatePolynomial(FE)
    -- zvar := monomial(1,1)$SupF
    -- zvar := monomial(1, 1::Expon)$UPS

    -- taylor_via_deriv is good enough for Airy functions
    -- f'' - zf = 0
    -- airyEq(lc : L(FE), z0 : FE) : L(UPS) ==
    --    zvar := monomial(1, 1::Expon)$UPS + z0::UPS
    --    [1, 0, zvar]

    -- z^2f'' + zf' +(z^2 - v^2)f = 0
    besselEq(lc : L(FE), z0 : FE) : L(UPS) ==
        v := lc(1)::UPS
        zvar := monomial(1, 1::Expon)$UPS + z0::UPS
        [zvar^2, zvar, zvar^2 - v^2]

    -- z^2f'' + zf' +(z^2 + v^2)f = 0
    besselEqm(lc : L(FE), z0 : FE) : L(UPS) ==
        v := lc(1)::UPS
        zvar := monomial(1, 1::Expon)$UPS + z0::UPS
        [zvar^2, zvar, zvar^2 + v^2]

    -- z^4f^(4) + 2z^3f^(3) - (1 + 2v^2)z^2f'' + (1 + 2v^2)zf'
    -- (v^2 - 4v^2 + z^4)f = 0 
    kelvinEq(lc : L(FE), z0 : FE) : L(UPS) ==
        v := lc(1)::UPS
        zvar := monomial(1, 1::Expon)$UPS + z0::UPS
        v2 := 1$UPS + 2*v^2
        [zvar^4, 2*zvar^3, -v2*zvar^2, v2*zvar, v^2 - 4*v^2 + zvar^4]

    -- zf'' + (b-z)f' - af = 0
    kummerEq(lc : L(FE), z0 : FE) : L(UPS) ==
        a := lc(1)::UPS
        b := lc(2)::UPS
        zvar := monomial(1, 1::Expon)$UPS + z0::UPS
        [zvar, (b - zvar), -a]

    -- (1 - z^2)^2f'' -2z(1-z^2)f' + (nu(nu+1)(1-z^2) - mu)f = 0 
    legendreEq(lc : L(FE), z0 : FE) : L(UPS) ==
        nu := lc(1)::UPS
        mu := lc(2)::UPS
        zvar := monomial(1, 1::Expon)$UPS + z0::UPS
        z2 := 1 - zvar^2
        [z2^2, -2*zvar*z2, nu*(nu + 1)*z2 - mu]

    --z^2f'' + (1/4 - m^2 + kz - z^2/4)f = 0
    whittakerEq(lc : L(FE), z0 : FE) : L(UPS) ==
        k := lc(1)::UPS
        m := lc(2)::UPS
        o4 := (1/(4::R::FE))::UPS
        zvar := monomial(1, 1::Expon)$UPS + z0::UPS
        [zvar^2, 0, o4 - m^2 + k*zvar - zvar^2/(4::R::FE)]

    do_diff_eq(ker : K, losers : L(UPS), lsers : L(UPS),
               lsere : L(FE), lser0 : L(FE),
               getEq : (L(FE), FE) -> L(UPS)) : Result ==
        cargs := convert_args(lsers, lsere, lser0)
        nargs := cargs.nargs0
        last(lsere) = 0 =>
            nker : FE := kernel(operator(ker), nargs)
            do_taylor_via_deriv2(nker, cargs.syms, cargs.sers)
        z0 := last(lser0)
        ecl := getEq(nargs, z0)
        cn := first(ecl)
        cn1 := -cn^(-1)
        ecl := [c*cn1 for c in rest(ecl)]
        nker : FE := kernel(operator(ker), nargs)
        lc : L(FE) := [nker]
        lsyms := cargs.syms
        sym := first(lsyms)
        lsyms := rest(lsyms)
        lsers := rest(cargs.sers)
        for c in rest(ecl) repeat
            nker := differentiate(nker, sym)
            lc := cons(nker, lc)
        lc := [eval(c, sym::FE = 0$FE) for c in reverse!(lc)]
        ups := taylor_via_lode(reverse(ecl), first(cargs.sers), lc)
        do_taylor_via_deriv(ups, lsyms, lsers)

    spec_to_UPS(ker : K, args : L(FE), posCheck? : B,
                atanFlag : ATANFLAG) : Result ==
        (aresu := handle_args(args, posCheck?, atanFlag)) case Result =>
             aresu
        ares := aresu::ARES
        losers := ares.osers
        lsers := ares.sers
        lsere := ares.sere
        lser0 := ares.sers0
        nm := name(ker)
        nm = 'besselI or nm = 'besselK =>
            lser0(2) = 0 => stateProblem(string(nm), "expansion at 0")
            do_diff_eq(ker, losers, lsers, lsere, lser0, besselEqm)
        nm = 'besselJ or nm = 'besselY or nm = 'hankelH1 or nm = 'hankelH2 =>
            lser0(2) = 0 => stateProblem(string(nm), "expansion at 0")
            do_diff_eq(ker, losers, lsers, lsere, lser0, besselEq)
        nm = kummerM or nm = kummerU =>
            lser0(3) = 0 => stateProblem(string(nm), "expansion at 0")
            do_diff_eq(ker, losers, lsers, lsere, lser0, kummerEq)
        nm = whittakerM or nm = whittakerW =>
            lser0(3) = 0 => stateProblem(string(nm), "expansion at 0")
            do_diff_eq(ker, losers, lsers, lsere, lser0, whittakerEq)
        nm = kelvinBer or nm = kelvinBei or nm = kelvinKer or nm = kelvinKei =>
            lser0(2) = 0 => stateProblem(string(nm), "expansion at 0")
            do_diff_eq(ker, losers, lsers, lsere, lser0, kelvinEq)
        nm = legendreP or nm = legendreQ =>
            lser0(3) = 1 or lser0(3) = -1 =>
                stateProblem(string(nm), "expansion at +-1")
            do_diff_eq(ker, losers, lsers, lsere, lser0, legendreEq)
)if false
        nm = 'hypergeometricF =>
        nm = 'meijerG =>
        nm = 'struveH =>
        nm = 'struveL =>
        nm = 'lommelS1 =>
        nm = 'lommelS2 =>
)endif
        nm = 'jacobiSn =>
            do_ell2(losers, lsers, lsere, lser0, jacobiSn$ELITS)
        nm = 'jacobiCn =>
            do_ell2(losers, lsers, lsere, lser0, jacobiCn$ELITS)
        nm = 'jacobiDn =>
            do_ell2(losers, lsers, lsere, lser0, jacobiDn$ELITS)
        nm = 'ellipticE2 =>
            do_ell2(losers, lsers, lsere, lser0, ellipticE$ELITS)
        nm = 'ellipticF =>
            do_ell2(losers, lsers, lsere, lser0, ellipticF$ELITS)
        nm = 'ellipticPi =>
            do_ell3(losers, lsers, lsere, lser0, ellipticPi$ELITS)
        cargs := convert_args(lsers, lsere, lser0)
        nargs := cargs.nargs0
        nker : FE := kernel(operator(ker), nargs)
        do_taylor_via_deriv2(nker, cargs.syms, cargs.sers)

    kernelToUPS(ker,posCheck?,atanFlag) ==
      -- converts a kernel to a power series
      (sym := symbolIfCan(ker)) case Symbol =>
        (sym :: Symbol) = x => [monomial(1,1)]
        [monomial(ker :: FE,0)]
      empty?(args := argument ker) => [monomial(ker :: FE,0)]
      not member?(x, variables(ker :: FE)) => [monomial(ker :: FE,0)]
      op := operator(ker)
      has?(op, 'special) =>
          spec_to_UPS(ker, args, posCheck?, atanFlag)
      empty? rest args =>
        arg := first args
        is?(ker, 'abs) =>
          nthRootToUPS(arg*arg,2,posCheck?,atanFlag)
        is?(ker,"%paren" :: Symbol) => exprToUPS(arg,posCheck?,atanFlag)
        is?(ker, 'log) => logToUPS(arg, posCheck?, atanFlag)
        is?(ker, 'exp) =>
          applyIfCan(expIfCan,arg,"exp",posCheck?,atanFlag)
        tranToUPS(ker,arg,posCheck?,atanFlag)
      is?(ker,"%power" :: Symbol) => powToUPS(args,posCheck?,atanFlag)
      is?(ker, 'nthRoot) =>
        n := retract(second args)@I
        nthRootToUPS(first args,n :: NNI,posCheck?,atanFlag)
      stateProblem(string name ker,"unknown kernel")

    nthRootToUPS(arg,n,posCheck?,atanFlag) ==
      -- converts an nth root to a power series
      -- this is not used in the limit package, so the series may
      -- have non-zero order, in which case nth roots may not be unique
      (result := exprToUPS(arg,posCheck?,atanFlag)) case %problem => result
      ans := carefulNthRootIfCan(result.%series,n,posCheck?,false)
      ans case %problem => ans
      [ans.%series]

    logToUPS(arg,posCheck?,atanFlag) ==
      -- converts a logarithm log(f(x)) to a power series
      -- f(x) must have order 0 and if 'posCheck?' is true,
      -- then f(x) must have a non-negative leading coefficient
      (result := exprToUPS(arg,posCheck?,atanFlag)) case %problem => result
      ups := result.%series
      not zero? order(ups,1) =>
        stateProblem("log","series of non-zero order")
      coef := coefficient(ups,0)
      -- if 'posCheck?' is true, we do not allow logs of negative numbers
      if posCheck? then
        if ((signum := sign(coef)$SIGNEF) case I) then
          (signum :: I) = -1 =>
            return stateProblem("log","negative leading coefficient")
      [logIfCan(ups) :: UPS]

    if FE has abs: FE -> FE then
      localAbs fcn == abs fcn
    else
      localAbs fcn == sqrt(fcn * fcn)

    signOfExpression: FE -> FE
    signOfExpression arg == localAbs(arg)/arg

    atancotToUPS(arg,posCheck?,atanFlag,plusMinus) ==
      -- converts atan(f(x)) to a power series
      (result := exprToUPS(arg,posCheck?,atanFlag)) case %problem => result
      ups := result.%series; coef := coefficient(ups,0)
      (ord := order(ups,0)) = 0 and coef * coef = -1 =>
        -- series involves complex numbers
        return stateProblem("atan","logarithmic singularity")
      cc : FE :=
        ord < 0 =>
          atanFlag = "complex" =>
            return stateProblem("atan","essential singularity")
          (rn := ratIfCan(ord :: FE)) case "failed" =>
            -- this condition usually won't occur because exponents will
            -- be integers or rational numbers
            return stateProblem("atan","branch problem")
          if (atanFlag = "real: two sides") and (odd? numer(rn :: RN)) then
            -- expansions to the left and right of zero have different
            -- constant coefficients
            return stateProblem("atan","branch problem")
          lc := coefficient(ups,ord)
          (signum := sign(lc)$SIGNEF) case "failed" =>
            -- can't determine sign
            atanFlag = "just do it" =>
              plusMinus = 1 => pi()/(2 :: FE)
              0
            posNegPi2 := signOfExpression(lc) * pi()/(2 :: FE)
            plusMinus = 1 => posNegPi2
            pi()/(2 :: FE) - posNegPi2
            --return stateProblem("atan","branch problem")
          left? : B := atanFlag = "real: left side"; n := signum :: Integer
          (left? and n = 1) or (not left? and n = -1) =>
            plusMinus = 1 => -pi()/(2 :: FE)
            pi()
          plusMinus = 1 => pi()/(2 :: FE)
          0
        atan coef
      [(cc :: UPS) + integrate(plusMinus * differentiate(ups)/(1 + ups*ups))]

    applyIfCan(fcn,arg,fcnName,posCheck?,atanFlag) ==
      -- converts fcn(arg) to a power series
      (ups := exprToUPS(arg,posCheck?,atanFlag)) case %problem => ups
      ans := fcn(ups.%series)
      ans case "failed" => stateProblem(fcnName,"essential singularity")
      [ans :: UPS]

    tranToUPS(ker,arg,posCheck?,atanFlag) ==
      -- converts ker to a power series for certain functions
      -- in trig or hyperbolic trig categories
      is?(ker, 'sin) =>
        applyIfCan(sinIfCan,arg,"sin",posCheck?,atanFlag)
      is?(ker, 'cos) =>
        applyIfCan(cosIfCan,arg,"cos",posCheck?,atanFlag)
      is?(ker, 'tan) =>
        applyIfCan(tanIfCan,arg,"tan",posCheck?,atanFlag)
      is?(ker, 'cot) =>
        applyIfCan(cotIfCan,arg,"cot",posCheck?,atanFlag)
      is?(ker, 'sec) =>
        applyIfCan(secIfCan,arg,"sec",posCheck?,atanFlag)
      is?(ker, 'csc) =>
        applyIfCan(cscIfCan,arg,"csc",posCheck?,atanFlag)
      is?(ker, 'asin) =>
        applyIfCan(asinIfCan,arg,"asin",posCheck?,atanFlag)
      is?(ker, 'acos) =>
        applyIfCan(acosIfCan,arg,"acos",posCheck?,atanFlag)
      is?(ker, 'atan) => atancotToUPS(arg,posCheck?,atanFlag,1)
      is?(ker, 'acot) => atancotToUPS(arg,posCheck?,atanFlag,-1)
      is?(ker, 'asec) =>
        applyIfCan(asecIfCan,arg,"asec",posCheck?,atanFlag)
      is?(ker, 'acsc) =>
        applyIfCan(acscIfCan,arg,"acsc",posCheck?,atanFlag)
      is?(ker, 'sinh) =>
        applyIfCan(sinhIfCan,arg,"sinh",posCheck?,atanFlag)
      is?(ker, 'cosh) =>
        applyIfCan(coshIfCan,arg,"cosh",posCheck?,atanFlag)
      is?(ker, 'tanh) =>
        applyIfCan(tanhIfCan,arg,"tanh",posCheck?,atanFlag)
      is?(ker, 'coth) =>
        applyIfCan(cothIfCan,arg,"coth",posCheck?,atanFlag)
      is?(ker, 'sech) =>
        applyIfCan(sechIfCan,arg,"sech",posCheck?,atanFlag)
      is?(ker, 'csch) =>
        applyIfCan(cschIfCan,arg,"csch",posCheck?,atanFlag)
      is?(ker, 'asinh) =>
        applyIfCan(asinhIfCan,arg,"asinh",posCheck?,atanFlag)
      is?(ker, 'acosh) =>
        applyIfCan(acoshIfCan,arg,"acosh",posCheck?,atanFlag)
      is?(ker, 'atanh) =>
        applyIfCan(atanhIfCan,arg,"atanh",posCheck?,atanFlag)
      is?(ker, 'acoth) =>
        applyIfCan(acothIfCan,arg,"acoth",posCheck?,atanFlag)
      is?(ker, 'asech) =>
        applyIfCan(asechIfCan,arg,"asech",posCheck?,atanFlag)
      is?(ker, 'acsch) =>
        applyIfCan(acschIfCan,arg,"acsch",posCheck?,atanFlag)
      stateProblem(string name ker,"unknown kernel")

    powToUPS(args,posCheck?,atanFlag) ==
      -- converts a power f(x) ^ g(x) to a power series
      (logBase := logToUPS(first args,posCheck?,atanFlag)) case %problem =>
        logBase
      (expon := exprToUPS(second args,posCheck?,atanFlag)) case %problem =>
        expon
      ans := expIfCan((expon.%series) * (logBase.%series))
      ans case "failed" => stateProblem("exp","essential singularity")
      [ans :: UPS]

-- Generalized power series: power series in x, where log(x) and
-- bounded functions of x are allowed to appear in the coefficients
-- of the series.  Used for evaluating REAL limits at x = 0.

    newElem f ==
    -- rewrites a functional expression; all trig functions are
    -- expressed in terms of sin and cos; all hyperbolic trig
    -- functions are expressed in terms of exp
      smpElem(numer f) / smpElem(denom f)

    smpElem p == map(k2Elem, (x1: R): FE +-> x1::FE, p)$PCL

    k2Elem k ==
    -- rewrites a kernel; all trig functions are
    -- expressed in terms of sin and cos; all hyperbolic trig
    -- functions are expressed in terms of exp
      null(args := [newElem a for a in argument k]) => k::FE
      iez  := inv(ez  := exp(z := first args))
      sinz := sin z; cosz := cos z
      is?(k, 'tan)  => sinz / cosz
      is?(k, 'cot)  => cosz / sinz
      is?(k, 'sec)  => inv cosz
      is?(k, 'csc)  => inv sinz
      is?(k, 'sinh) => (ez - iez) / (2 :: FE)
      is?(k, 'cosh) => (ez + iez) / (2 :: FE)
      is?(k, 'tanh) => (ez - iez) / (ez + iez)
      is?(k, 'coth) => (ez + iez) / (ez - iez)
      is?(k, 'sech) => 2 * inv(ez + iez)
      is?(k, 'csch) => 2 * inv(ez - iez)
      (operator k) args

    CONTFCNS : L FUNCTIONNAME := ["sin","cos","atan","acot","exp","asinh"]
    -- functions which are defined and continuous at all real numbers

    BDDFCNS : L FUNCTIONNAME := ["sin","cos","atan","acot"]
    -- functions which are bounded on the reals

    contOnReals? fcn == member?(fcn,CONTFCNS)
    bddOnReals? fcn  == member?(fcn,BDDFCNS)

    exprToGenUPS(fcn,posCheck?,atanFlag) ==
      -- converts a functional expression to a generalized power
      -- series; "generalized" means that log(x) and bounded functions
      -- of x are allowed to appear in the coefficients of the series
      iExprToGenUPS(newElem fcn,posCheck?,atanFlag)

    iExprToGenUPS(fcn,posCheck?,atanFlag) ==
      -- converts a functional expression to a generalized power
      -- series without first normalizing the expression
      --!! The following line is commented out so that expressions of
      --!! the form a^b will be normalized to exp(b * log(a)) even if
      --!! 'a' and 'b' do not involve the limiting variable 'x'.
      --!!                         - cjw 1 Dec 94
      --not member?(x,variables fcn) => [monomial(fcn,0)]
      (poly := retractIfCan(fcn)@Union(POL,"failed")) case POL =>
        [polyToUPS univariate(poly :: POL,x)]
      (sum := isPlus fcn) case L(FE) =>
        listToUPS(sum :: L(FE), iExprToGenUPS, posCheck?, atanFlag, 0,
                  (y1, y2) +-> y1 + y2)
      (prod := isTimes fcn) case L(FE) =>
        listToUPS(prod :: L(FE), iExprToGenUPS, posCheck?, atanFlag, 1,
                  (y1, y2) +-> y1*y2)
      (expt := isNonTrivPower fcn) case Record(val:FE,exponent:I) =>
        power := expt :: Record(val:FE,exponent:I)
        powerToGenUPS(power.val,power.exponent,posCheck?,atanFlag)
      (ker := retractIfCan(fcn)@Union(K,"failed")) case K =>
        kernelToGenUPS(ker :: K,posCheck?,atanFlag)
      error "exprToGenUPS: neither a sum, product, power, nor kernel"

    opsInvolvingX fcn ==
      opList := [op for k in tower fcn | unary?(op := operator k) _
                 and member?(x,variables first argument k)]
      removeDuplicates opList

    opInOpList?(name,opList) ==
      for op in opList repeat
        is?(op,name) => return true
      false

    exponential? fcn ==
      -- is 'fcn' of the form exp(f)?
      (ker := retractIfCan(fcn)@Union(K,"failed")) case K =>
        is?(ker::K, 'exp)
      false

    productOfNonZeroes? fcn ==
      -- is 'fcn' a product of non-zero terms, where 'non-zero'
      -- means an exponential or a function not involving x
      exponential? fcn => true
      (prod := isTimes fcn) case "failed" => false
      for term in (prod :: L(FE)) repeat
        (not exponential? term) and member?(x,variables term) =>
          return false
      true

    powerToGenUPS(fcn,n,posCheck?,atanFlag) ==
      -- converts an integral power to a generalized power series
      -- if n < 0 and the lowest order coefficient of the series
      -- involves x, we are careful about inverting this coefficient
      -- the coefficient is inverted only if
      -- (a) the only function involving x is 'log', or
      -- (b) the lowest order coefficient is a product of exponentials
      --     and functions not involving x
      (b := iExprToGenUPS(fcn,posCheck?,atanFlag)) case %problem => b
      n > 0 => [(b.%series) ^ n]
      -- check lowest order coefficient when n < 0
      ups := b.%series; deg := degree ups
      if (coef := coefficient(ups,deg)) = 0 then
        deg := order(ups,deg + ZEROCOUNT :: Expon)
        (coef := coefficient(ups,deg)) = 0 =>
          error "inverse of series with many leading zero coefficients"
      xOpList := opsInvolvingX coef
      -- only function involving x is 'log'
      (null xOpList) => [ups ^ n]
      (null rest xOpList and is?(first xOpList, 'log)) =>
        [ups ^ n]
      -- lowest order coefficient is a product of exponentials and
      -- functions not involving x
      productOfNonZeroes? coef => [ups ^ n]
      stateProblem("inv","lowest order coefficient involves x")

    kernelToGenUPS(ker,posCheck?,atanFlag) ==
      -- converts a kernel to a generalized power series
      (sym := symbolIfCan(ker)) case Symbol =>
        (sym :: Symbol) = x => [monomial(1,1)]
        [monomial(ker :: FE,0)]
      empty?(args := argument ker) => [monomial(ker :: FE,0)]
      empty? rest args =>
        arg := first args
        is?(ker, 'abs) =>
          nthRootToGenUPS(arg*arg,2,posCheck?,atanFlag)
        is?(ker,"%paren" :: Symbol) => iExprToGenUPS(arg,posCheck?,atanFlag)
        is?(ker, 'log) => logToGenUPS(arg, posCheck?, atanFlag)
        is?(ker, 'exp) => expToGenUPS(arg, posCheck?, atanFlag)
        tranToGenUPS(ker,arg,posCheck?,atanFlag)
      is?(ker,"%power" :: Symbol) => powToGenUPS(args,posCheck?,atanFlag)
      is?(ker, 'nthRoot) =>
        n := retract(second args)@I
        nthRootToGenUPS(first args,n :: NNI,posCheck?,atanFlag)
      stateProblem(string name ker,"unknown kernel")

    nthRootToGenUPS(arg,n,posCheck?,atanFlag) ==
      -- convert an nth root to a power series
      -- used for computing right hand limits, so the series may have
      -- non-zero order, but may not have a negative leading coefficient
      -- when n is even
      (result := iExprToGenUPS(arg,posCheck?,atanFlag)) case %problem =>
        result
      ans := carefulNthRootIfCan(result.%series,n,posCheck?,true)
      ans case %problem => ans
      [ans.%series]

    logToGenUPS(arg,posCheck?,atanFlag) ==
      -- converts a logarithm log(f(x)) to a generalized power series
      (result := iExprToGenUPS(arg,posCheck?,atanFlag)) case %problem =>
        result
      ups := result.%series; deg := degree ups
      if (coef := coefficient(ups,deg)) = 0 then
        deg := order(ups,deg + ZEROCOUNT :: Expon)
        (coef := coefficient(ups,deg)) = 0 =>
          error "log of series with many leading zero coefficients"
      -- if 'posCheck?' is true, we do not allow logs of negative numbers
      if posCheck? then
        if ((signum := sign(coef)$SIGNEF) case I) then
          (signum :: I) = -1 =>
            return stateProblem("log","negative leading coefficient")
      -- create logarithmic term, avoiding log's of negative rationals
      lt := monomial(coef,deg)$UPS; cen := center lt
      -- check to see if lowest order coefficient is a negative rational
      negRat? : Boolean :=
        ((rat := ratIfCan coef) case RN) =>
          (rat :: RN) < 0 => true
          false
        false
      logTerm : FE :=
        mon : FE := (x :: FE) - (cen :: FE)
        pow : FE := mon ^ (deg :: FE)
        negRat? => log(coef * pow)
        term1 : FE := (deg :: FE) * log(mon)
        log(coef) + term1
      [monomial(logTerm,0) + log(ups/lt)]

    expToGenUPS(arg,posCheck?,atanFlag) ==
      -- converts an exponential exp(f(x)) to a generalized
      -- power series
      (ups := iExprToGenUPS(arg,posCheck?,atanFlag)) case %problem => ups
      expGenUPS(ups.%series,posCheck?,atanFlag)

    expGenUPS(ups,posCheck?,atanFlag) ==
      -- computes the exponential of a generalized power series.
      -- If the series has order zero and the constant term a0 of the
      -- series involves x, the function tries to expand exp(a0) as
      -- a power series.
      (deg := order(ups,1)) < 0 =>
        stateProblem("exp","essential singularity")
      deg > 0 => [exp ups]
      lc := coefficient(ups,0); xOpList := opsInvolvingX lc
      not opInOpList?('log, xOpList) => [exp ups]
      -- try to fix exp(lc) if necessary
      expCoef :=
        normalize(exp lc,x)$ElementaryFunctionStructurePackage(R,FE)
      opInOpList?('log, opsInvolvingX expCoef) =>
        stateProblem("exp","logs in constant coefficient")
      result := exprToGenUPS(expCoef,posCheck?,atanFlag)
      result case %problem => result
      [(result.%series) * exp(ups - monomial(lc,0))]

    atancotToGenUPS(fe,arg,posCheck?,atanFlag,plusMinus) ==
      -- converts atan(f(x)) to a generalized power series
      (result := iExprToGenUPS(arg,posCheck?,atanFlag)) case %problem =>
        trouble := result.%problem
        trouble.prob = "essential singularity" => [monomial(fe,0)$UPS]
        result
      ups := result.%series; coef := coefficient(ups,0)
      -- series involves complex numbers
      (ord := order(ups,0)) = 0 and coef * coef = -1 =>
        y := differentiate(ups)/(1 + ups*ups)
        yCoef := coefficient(y,-1)
        [monomial(log yCoef,0) + integrate(y - monomial(yCoef,-1)$UPS)]
      cc : FE :=
        ord < 0 =>
          atanFlag = "complex" =>
            return stateProblem("atan","essential singularity")
          (rn := ratIfCan(ord :: FE)) case "failed" =>
            -- this condition usually won't occur because exponents will
            -- be integers or rational numbers
            return stateProblem("atan","branch problem")
          if (atanFlag = "real: two sides") and (odd? numer(rn :: RN)) then
            -- expansions to the left and right of zero have different
            -- constant coefficients
            return stateProblem("atan","branch problem")
          lc := coefficient(ups,ord)
          (signum := sign(lc)$SIGNEF) case "failed" =>
            -- can't determine sign
            atanFlag = "just do it" =>
              plusMinus = 1 => pi()/(2 :: FE)
              0
            posNegPi2 := signOfExpression(lc) * pi()/(2 :: FE)
            plusMinus = 1 => posNegPi2
            pi()/(2 :: FE) - posNegPi2
            --return stateProblem("atan","branch problem")
          left? : B := atanFlag = "real: left side"; n := signum :: Integer
          (left? and n = 1) or (not left? and n = -1) =>
            plusMinus = 1 => -pi()/(2 :: FE)
            pi()
          plusMinus = 1 => pi()/(2 :: FE)
          0
        atan coef
      [(cc :: UPS) + integrate(differentiate(ups)/(1 + ups*ups))]

    genUPSApplyIfCan(fcn,arg,fcnName,posCheck?,atanFlag) ==
      -- converts fcn(arg) to a generalized power series
      (series := iExprToGenUPS(arg,posCheck?,atanFlag)) case %problem =>
        series
      ups := series.%series
      (deg := order(ups,1)) < 0 =>
        stateProblem(fcnName,"essential singularity")
      deg > 0 => [fcn(ups) :: UPS]
      lc := coefficient(ups,0); xOpList := opsInvolvingX lc
      null xOpList => [fcn(ups) :: UPS]
      opInOpList?('log, xOpList) =>
        stateProblem(fcnName,"logs in constant coefficient")
      contOnReals? fcnName => [fcn(ups) :: UPS]
      stateProblem(fcnName,"x in constant coefficient")

    applyBddIfCan(fe,fcn,arg,fcnName,posCheck?,atanFlag) ==
      -- converts fcn(arg) to a generalized power series, where the
      -- function fcn is bounded for real values
      -- if fcn(arg) has an essential singularity as a complex
      -- function, we return fcn(arg) as a monomial of degree 0
      (ups := iExprToGenUPS(arg,posCheck?,atanFlag)) case %problem =>
        trouble := ups.%problem
        trouble.prob = "essential singularity" => [monomial(fe,0)$UPS]
        ups
      (ans := fcn(ups.%series)) case "failed" => [monomial(fe,0)$UPS]
      [ans :: UPS]

    tranToGenUPS(ker,arg,posCheck?,atanFlag) ==
      -- converts op(arg) to a power series for certain functions
      -- op in trig or hyperbolic trig categories
      -- N.B. when this function is called, 'k2Elem' will have been
      -- applied, so the following functions cannot appear:
      -- tan, cot, sec, csc, sinh, cosh, tanh, coth, sech, csch
      is?(ker, 'sin) =>
        applyBddIfCan(ker :: FE,sinIfCan,arg,"sin",posCheck?,atanFlag)
      is?(ker, 'cos) =>
        applyBddIfCan(ker :: FE,cosIfCan,arg,"cos",posCheck?,atanFlag)
      is?(ker, 'asin) =>
        genUPSApplyIfCan(asinIfCan,arg,"asin",posCheck?,atanFlag)
      is?(ker, 'acos) =>
        genUPSApplyIfCan(acosIfCan,arg,"acos",posCheck?,atanFlag)
      is?(ker, 'atan) =>
        atancotToGenUPS(ker :: FE,arg,posCheck?,atanFlag,1)
      is?(ker, 'acot) =>
        atancotToGenUPS(ker :: FE,arg,posCheck?,atanFlag,-1)
      is?(ker, 'asec) =>
        genUPSApplyIfCan(asecIfCan,arg,"asec",posCheck?,atanFlag)
      is?(ker, 'acsc) =>
        genUPSApplyIfCan(acscIfCan,arg,"acsc",posCheck?,atanFlag)
      is?(ker, 'asinh) =>
        genUPSApplyIfCan(asinhIfCan,arg,"asinh",posCheck?,atanFlag)
      is?(ker, 'acosh) =>
        genUPSApplyIfCan(acoshIfCan,arg,"acosh",posCheck?,atanFlag)
      is?(ker, 'atanh) =>
        genUPSApplyIfCan(atanhIfCan,arg,"atanh",posCheck?,atanFlag)
      is?(ker, 'acoth) =>
        genUPSApplyIfCan(acothIfCan,arg,"acoth",posCheck?,atanFlag)
      is?(ker, 'asech) =>
        genUPSApplyIfCan(asechIfCan,arg,"asech",posCheck?,atanFlag)
      is?(ker, 'acsch) =>
        genUPSApplyIfCan(acschIfCan,arg,"acsch",posCheck?,atanFlag)
      stateProblem(string name ker,"unknown kernel")

    powToGenUPS(args,posCheck?,atanFlag) ==
      -- converts a power f(x) ^ g(x) to a generalized power series
      (logBase := logToGenUPS(first args,posCheck?,atanFlag)) case %problem =>
        logBase
      expon := iExprToGenUPS(second args,posCheck?,atanFlag)
      expon case %problem => expon
      expGenUPS((expon.%series) * (logBase.%series),posCheck?,atanFlag)

@
\section{License}
<<license>>=
--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.
@
<<*>>=
<<license>>

<<package FS2UPS FunctionSpaceToUnivariatePowerSeries>>
@
\eject
\begin{thebibliography}{99}
\bibitem{1} nothing
\end{thebibliography}
\end{document}
