)abbrev package ARRAY22 TwoDimensionalArrayFunctions
TwoDimensionalArrayFunctions(A, RA, CA, MA, B, RB, CB, MB
                            ) : Exports == Implementation where
  A : Type
  RA : FiniteLinearAggregate A
  CA : FiniteLinearAggregate A
  MA : TwoDimensionalArrayCategory(A, RA, CA)
  B : Type
  RB : FiniteLinearAggregate B
  CB : FiniteLinearAggregate B
  MB : TwoDimensionalArrayCategory(B, RB, CB)

  Exports ==> with

    map : (A -> B, MA) -> MB
      ++ map(f, m) applies the function f to every element of the array m
      ++ producing a new array containing the values.

  Implementation ==> add

    map(f, m) ==
        nr := nrows(m)
        nc := ncols(m)
        res := qnew(nr, nc)$MB
        for i in minRowIndex(m)..maxRowIndex(m) repeat
            for k in minColIndex(m)..maxColIndex(m) repeat
                qsetelt!(res, i, k, f(qelt(m, i, k)))
        res



)abbrev package CLINDEP ConstantLinearDependence
++ Description:
++ \spad{ConstantLinearDependence(R, F)} supports solving linear
++ systems with coefficients in \spad{Expression(R)} over constants.
++ The parameter \spad{ld} to each function is a list of derivations
++ to use.  By definition element \spad{f} from \spad{Expression(R)}
++ is a constant if for each \spad{d} in \spad{ld} we have \spad{d(f) = 0}.
++ We assume that inputs are normalized in such a way that
++ field of constants is generated by constant kernels.
ConstantLinearDependence(R, F) : Exports == Implementation where
  R : Join(IntegralDomain, Comparable)
  F : FunctionSpace R
  PartialQ ==>  Union(Vector F, "failed")
  BothQ ==> Record(particular : PartialQ, basis : List Vector F)
  LFF ==> List(F -> F)
  Exports ==> with
    linearlyDependentOverConstants? : (Vector F, LFF) -> Boolean
      ++ \spad{linearlyDependentOverConstants?([v1, ..., vn], ld)}
      ++ returns true if the vi's are linearly dependent over
      ++ constants, false otherwise.
    linearDependenceOverConstants : (Vector F, LFF) ->
                                      Union(Vector F, "failed")
      ++ \spad{linearlyDependenceOverConstants([v1, ..., vn], ld)} returns
      ++ \spad{[c1, ..., cn]} if
      ++ \spad{c1*v1 + ... + cn*vn = 0} and not all the ci's are 0, "failed"
      ++ if the vi's are linearly independent over constants.
    particularSolutionOverConstants : (Vector F, F, LFF) -> PartialQ
      ++ \spad{particularSolutionOverConstants([v1, ..., vn], u, ld)} returns
      ++ \spad{[c1, ..., cn]} such that \spad{c1*v1 + ... + cn*vn = u},
      ++ "failed" if no such constants numbers ci's exist.
    particularSolutionOverConstants : (Matrix F, Vector F, LFF) -> PartialQ
      ++ \spad{solveLinearlyOverConstants([v1, ..., vn], u, ld)} returns
      ++ \spad{[c1, ..., cn]} such that \spad{c1*v1 + ... + cn*vn = u},
      ++ "failed" if no such rational numbers ci's exist.
    solveLinearOverConstants : (Vector F, F, LFF) -> BothQ
      ++ \spad{solveLinearOverConstants([v1, ..., vn], u, ld)} returns
      ++ solution of the system  \spad{c1*v1 + ... + cn*vn = u} and
      ++ and a basis of the associated homogeneous system
      ++ \spad{c1*v1 + ... + cn*vn = 0}
    solveLinearOverConstants : (Matrix F, Vector F, LFF) -> BothQ
      ++ \spad{solveLinearOverConstants([v1, ..., vn], u, ld)} returns
      ++ solution of the system  \spad{c1*v1 + ... + cn*vn = u} and
      ++ and a basis of the associated homogeneous system
      ++ \spad{c1*v1 + ... + cn*vn = 0}
    reducedSystem : (Matrix F, Vector F, LFF) ->
                    Record(mat : Matrix F, vec : Vector F)
      ++ reducedSystem(A, v, ld) returns matrix of constants B and
      ++ vector of constants w such
      ++ that \spad{A x = v} and \spad{B x = w}
      ++ have the same solutions over constants.
    reducedSystem : (Matrix(F), LFF) -> Matrix(F)
      ++ reducedSystem(A, ld) returns a matrix of constants B such
      ++ that \spad{A x = 0} and \spad{B x = 0}
      ++ have the same solutions over constants.
    nullSpaceOverConstants : (Matrix F, LFF) -> List Vector F
      ++ nullSpaceOverConstants(A, ld) returns basis of the
      ++ homogeneous system \spad{A*vc = 0} where vc is a
      ++ vector of constants.
    constant_subspace : (List Vector(F),  LFF) ->
                    Record(transform : Matrix(F), basis : List Vector(F))
      ++ constant_subspace(b, ld) returns [M, nb] such that
      ++ nb is basis of subspace spanned by b over F consistng
      ++ of vectors with constant coordinates.  M is the transformation
      ++ matrix, i.e. mnb = M mb where mb (mnb respectively) is matrix
      ++ having b (nb respectively) as rows.

  Implementation ==> add

    EF ==> Expression(F)

    LD ==> LinearDependence(F, EF)

    K1 ==> Kernel(F)

    K2 ==> Kernel(EF)

    MP ==> SparseMultivariatePolynomial(R, K1)

    IE ==> IndexedExponents(K1)

    Lift ==> PolynomialCategoryLifting(IE, K1, R, MP, EF)

    F_to_EF1 : (F, K1 -> EF) -> EF

    DUMMYVAR := '%dummyVar

    kmap(k : K1, ldmvar : List K1, ldmval : List EF, ld : LFF) : EF ==
        for dmvar in ldmvar for dmval in ldmval repeat
            if k = dmvar then return dmval
        is_const := true
        kf := k::F
        for d in ld repeat
            if d(kf) ~= 0 then
                is_const := false
                break
        is_const => kf::EF
        op := operator(operator(k))$EF
        args := argument(k)
        nargs : List(EF)
        if (u := property(op, DUMMYVAR)) case None then
            n := u@None pretend NonNegativeInteger
            da := retract(a1 := second(args))@K1
            if not(member?(da, ldmvar)) then
                ldmvar := cons(da, ldmvar)
                ldmval := cons(new()$Symbol::EF, ldmval)
                sa1 := retract(a1)@Symbol
                ld := cons((ff : F) : F +-> differentiate(ff, sa1), ld)
            if n = 2 then
                da2 := retract(a2 := args(3))@K1
                if not(member?(da2, ldmvar)) then
                    ldmvar := cons(da2, ldmvar)
                    ldmval := cons(new()$Symbol::EF, ldmval)
                    sa2 := retract(a2)@Symbol
                    ld := cons((ff : F) : F +-> differentiate(ff, sa2), ld)
        kmap1 := (kk : K1) : EF +-> kmap(kk, ldmvar, ldmval, ld)
        kernel(op, [F_to_EF1(a, kmap1) for a in argument(k)])$EF

    F_to_EF1(f : F, kmap1 : K1 -> EF) : EF ==
        map(kmap1, (c : R) : EF +-> c::F::EF, numer(f))$Lift
          /map(kmap1, (c : R) : EF +-> c::F::EF, denom(f))$Lift

    F_to_EF(f : F, ld : LFF) : EF ==
        F_to_EF1(f, (k : K1) : EF +-> kmap(k, [], [], ld))

    VF_to_VEF(v : Vector(F), ld : LFF) : Vector(EF) ==
        map(x +-> F_to_EF(x, ld), v)$VectorFunctions2(F, EF)

    MF_to_MEF(m : Matrix(F), ld : LFF) : Matrix(EF) ==
        map(x +-> F_to_EF(x, ld), m)$TwoDimensionalArrayFunctions(
            F, Vector(F), Vector(F), Matrix(F),
            EF, Vector(EF), Vector(EF), Matrix(EF))

    linearlyDependentOverConstants?(v, ld) ==
        linearlyDependent?(VF_to_VEF(v, ld))$LD

    linearDependenceOverConstants(v, ld) ==
        linearDependence(VF_to_VEF(v, ld))$LD

    particularSolutionOverConstants(v : Vector F, u : F, ld : LFF) ==
        particularSolution(VF_to_VEF(v, ld), F_to_EF(u, ld))$LD

    particularSolutionOverConstants(m : Matrix F, v : Vector F, ld : LFF) ==
        particularSolution(MF_to_MEF(m, ld), VF_to_VEF(v, ld))$LD

    solveLinearOverConstants(v : Vector F, u : F, ld : LFF) ==
        solveLinear(VF_to_VEF(v, ld), F_to_EF(u, ld))$LD

    solveLinearOverConstants(m : Matrix F, v : Vector F, ld : LFF) ==
        solveLinear(MF_to_MEF(m, ld), VF_to_VEF(v, ld))$LD

    reducedSystem(m : Matrix F, ld : LFF) ==
        reducedSystem(MF_to_MEF(m, ld))$EF

    reducedSystem(m : Matrix F, v : Vector F, ld : LFF) ==
        reducedSystem(MF_to_MEF(m, ld), VF_to_VEF(v, ld))$EF

    nullSpaceOverConstants(m : Matrix F, ld : LFF) ==
        solveLinearOverConstants(m, new(nrows(m), 0)$Vector(F), ld).basis

    UP  ==> SparseUnivariatePolynomial F

    constant_subspace(b : List Vector(F), ld : LFF
          ) : Record(transform : Matrix(F), basis : List Vector(F)) ==
        empty?(b) => [new(0, 0, 1), []]
        nc := #first(b)
        nr := #b
        m0b := matrix([entries(bv) for bv in b])$Matrix(F)
        m0 := horizConcat(m0b, scalarMatrix(nr, 1))
        m1 := rowEchelon(m0)
        k : Integer := 1
        lpiv : List Integer := []
        lri :  List Integer := []
        for i in 1..nrows(m1) repeat
            k1 := k
            while m1(i, k1) = 0 and k1 <= nc repeat k1 := k1 + 1
            nc < k1 => iterate
            k := k1
            m1(i, k) ~= 1 => error "impossible"
            lri := cons(i, lri)
            lpiv := cons(k, lpiv)
        lpiv := cons(nc + 1, lpiv)
        lpiv := reverse!(lpiv)
        lr : List Vector F := []
        ll : List List F := []
        for i in lri repeat
            lpiv1 := lpiv
            kk := first(lpiv1)
            lpiv1 := rest(lpiv1)
            ll1 : List F := []
            for j in 1..nc repeat
                j = kk =>
                    kk := first(lpiv1)
                    lpiv1 := rest(lpiv1)
                for d1 in ld repeat
                    ll1 := cons(d1(m1(i, j)), ll1)
            ll := cons(ll1, ll)
            lr := cons(row(m1, i), lr)
        m := transpose(matrix(ll))
        s1 := nullSpaceOverConstants(m, ld)
        l3 := [lin_comb(bv, lr)$LinearCombinationUtilities(F, UP) for bv in s1]
        m2 := matrix([[bv(i) for i in nc+1..ncols(m0)] for bv in l3]
                    )$Matrix(F)
        [m2, [bv(1..nc) for bv in l3]]


)abbrev package LLINDEP LiftLinearDependence
LiftLinearDependence(F, Q, UP) : Exports == Implementation where
  F  : Field
  Q  : Field
  UP : UnivariatePolynomialCategory(F)
  RF  ==> Fraction(UP)
  Exports ==> with
    csolve_pol : (Matrix(F) -> List(Vector(Q)), List(UP)) -> List(Vector(Q))
      ++ csolve_pol(solf, lp) uses \spad{solf} to solve linear dependence
      ++ for univariate polynomials.
    csolve_rf : (Matrix(F) -> List(Vector(Q)), List(RF)) -> List(Vector(Q))
      ++ csolve_rf(solf, lrf) uses \spad{solf} to solve linear dependence
      ++ for univariate rational functions.
    csolve_rmat : (Matrix(F) -> List(Vector(Q)), Matrix(RF)) -> List(Vector(Q))
      ++ csolve_rmat(solf, mrf) uses \spad{solf} to solve linear dependence
      ++ for univariate rational functions.
  Implementation ==> add

    csolve_pol(csolve, lp) ==
        m1 := matrix([lp])$Matrix(UP)
        rs1 : Matrix F := reducedSystem(m1)
        csolve(rs1)

    csolve_rf(csolve, lrf) ==
        m1 := matrix([lrf])$Matrix(RF)
        rs1 : Matrix UP := reducedSystem(m1)
        rs2 : Matrix F := reducedSystem(rs1)
        csolve(rs2)

    csolve_rmat(csolve, mrf) ==
        rs1 : Matrix UP := reducedSystem(mrf)$RF
        rs2 : Matrix F := reducedSystem(rs1)
        csolve(rs2)

)abbrev package PRROOT PolynomialRationalRoots
PolynomialRationalRoots(F, UP) : Exports == Implementation where
  F  : Join(IntegralDomain, LinearlyExplicitOver(Integer))
  UP : UnivariatePolynomialCategory F

  Z   ==> Integer
  Q   ==> Fraction Z

  Exports ==> with
    get_rational_roots : UP -> List(Q)
      ++ get_rational_roots(p) finds rational root of p.
    integerBound : UP -> Z
      ++ integerBound(p) returns a lower bound on the negative integer
      ++ roots of p, and 0 if p has no negative integer roots.

  Implementation ==> add

    N   ==> NonNegativeInteger
    UPZ ==> SparseUnivariatePolynomial Z
    LF  ==> List F

    get_rational_roots(p : UP) : List(Q) ==
        nn := degree(p) +$N 1
        vv := vectorise(p, nn)
        vm := matrix([parts(vv)$Vector(F)]$List(LF))$Matrix(F)
        vvr : Matrix(Z) := reducedSystem(vm)
        i : N
        pl2 : List(UPZ) := [unvectorise(row(vvr, i)$Matrix(Z))$UPZ
                  for i in 1..nrows(vvr)]
        eq2z := gcd(pl2)
        ff := factorPolynomial(eq2z)$Z
        facl := factorList(ff)$Factored(UPZ)
        res : List(Q) := []
        for facr in facl repeat
            fac := facr.factor
            degree(fac) ~= 1 => iterate
            val1r : Q := -coefficient(fac, 0) /$Q leadingCoefficient(fac)
            res := cons(val1r, res)
        res

    integerBound(p) ==
        lr := get_rational_roots(p)
        res : Integer := 0
        for r in lr repeat
            qu := retractIfCan(r)@Union(Integer, "failed")
            qu case "failed" => iterate
            q := qu@Integer
            if q < res then res := q
        res

)abbrev package FSRROOT FunctionSpaceRationalRoots
FunctionSpaceRationalRoots(R, F)  : Exports == Implementation where
  R : Join(IntegralDomain, Comparable, CharacteristicZero,
           RetractableTo Integer, LinearlyExplicitOver Integer)
  F : FunctionSpace R
  K   ==> Kernel F
  Z   ==> Integer
  Q   ==> Fraction Z

  Exports ==> with

    get_rational_roots : (F, K) -> List(Q)
      ++ get_rational_roots(f, k) finds rational root of
      ++ f treated as univariate polynomial in k.

  Implementation ==> add

    UP  ==> SparseUnivariatePolynomial F
    RF  ==> Fraction UP

    get_rational_roots(eq1f : F, k1 : K) : List(Q) ==
        eq2p := numer(univariate(eq1f, k1))$RF
        get_rational_roots(eq2p)$PolynomialRationalRoots(F, UP)

)abbrev package RDEEFX2 ElementaryRischDEX2
++ Risch differential equation, wrapper for extended version.
ElementaryRischDEX2(R, F) : Exports == Implementation where
  R : Join(GcdDomain, Comparable, CharacteristicZero,
           PolynomialFactorizationExplicit,
           RetractableTo Integer, LinearlyExplicitOver Integer)
  F : Join(TranscendentalFunctionCategory, AlgebraicallyClosedField,
           FunctionSpace R)

  Z   ==> Integer
  SY  ==> Symbol
  LF  ==> List F
  UP  ==> SparseUnivariatePolynomial F
  PSOL2 ==> Record(ans : F, right : F, primpart : F, sol? : Boolean)
  Param_Rec_F ==> Record(ratpart : F, coeffs : Vector F)
  Partial_F ==> Union(Param_Rec_F, "failed")
  K ==> Kernel F

  Exports ==> with
    risch_de_ext : (Z, F, F, SY) -> PSOL2
         ++ risch_de_ext(n, f, g, x) returns \spad{[y, h, p, b]} such that
         ++ \spad{dy/dx + n df/dx y + exp(-nf)dp/dx = h} and \spad{b := h = g}.
         ++ The equation \spad{dy/dx + n df/dx y = g} has no solution
         ++ if \spad{h \~~= g} (y is a partial solution in that case).
    do_risch_DE : (F, F, SY, List(K)) -> PSOL2
         ++ do_risch_DE(f, g, x, lk) returns \spad{[y, h, 0, b]}
         ++ such that \spad{dy/dx + df/dx y = h} and \spad{b := h = g}.
         ++ The equation \spad{dy/dx + df/dx y = g} has no solution
         ++ in the field generated by lk if \spad{h \~~= g}
         ++ (y is a partial solution in that case).
    do_param_RDE : (F, F, LF, SY, List(K)) -> Partial_F
         ++ do_param_RDE(f, h, lg, x, lk) solves parametric Risch
         ++ differential equation, that is returns \spad{[y, [c1, ..., ck]]}
         ++ such that dy/dx + n df/dx y + c1 g1 + ... cn gn = h
         ++ where y is in the is in field generated by lk and ci are
         ++ constants or "failed" if there is no solution.

  Implementation ==> add

    Q ==> Fraction(Z)
    L_Param_F ==> List Param_Rec_F
    Param_Rec_QF ==> Record(logands : List F, basis : List Vector Q)

    do_param_RDE(f : F, h : F, lg : LF, x : SY, lk : List(K)) : Partial_F ==
        ext := (x4 : List K, x3 : List F) : L_Param_F +->
                 extendedint(x, x4, x3
                            )$ParametricIntegration(R, F)
        logi := (x2 : List K, x3 : List F) : Param_Rec_QF +->
                  logextint(x, x2, x3
                           )$ParametricIntegration(R, F)
        res1 := param_rde(1, f, h, lg, x, lk, ext, logi
                         )$ParametricRischDE(R, F)
        res1.particular

    do_risch_DE(f : F, g : F, x : SY, lk : List(K)) : PSOL2 ==
        part := do_param_RDE(f, g, [], x, lk)
        part case "failed" =>
            [0, 0, 0, false]
        p1 := part@Param_Rec_F
        [p1.ratpart, g, 0, true]

    if R has EuclideanDomain and F has LiouvillianFunctionCategory
      and F has SpecialFunctionCategory then

        risch_de_ext(n, f, g, x) ==
            ei_int(n, f, g, x)$ElementaryRischDEX(R, F)

    else

        import from IntegrationTools(R, F)

        risch_de_ext(n, f, g, x) ==
            lk := varselect(union(tower(f), tower(g)), x)
            do_risch_DE(n*f, g, x, lk)

)abbrev package RDEEFX ElementaryRischDEX
)boot $tryRecompileArguments := nil
++ Risch differential equation, extended version.
++ Description:
++   This package generates exponential integrals corresponding
++   to polynomial part of the integral.
++
ElementaryRischDEX(R, F) : Exports == Implementation where
  R : Join(PolynomialFactorizationExplicit, Comparable, CharacteristicZero,
           RetractableTo Integer, LinearlyExplicitOver Integer)
  F : Join(TranscendentalFunctionCategory, AlgebraicallyClosedField,
           LiouvillianFunctionCategory, SpecialFunctionCategory,
           FunctionSpace R)

  N   ==> NonNegativeInteger
  Z   ==> Integer
  Q   ==> Fraction(Z)
  SY  ==> Symbol
  LF  ==> List F
  K   ==> Kernel F
  LK  ==> List K
  P   ==> SparseMultivariatePolynomial(R, K)
  UP  ==> SparseUnivariatePolynomial F
  RF  ==> Fraction UP
  RRF ==> Record(mainpart : F, limitedlogs : List NL)
  NL  ==> Record(coeff : F, logand : F)
  U   ==> Union(RRF, "failed")
  UEX ==> Union(Record(ratpart:F,  coeff:F), "failed")
  PSOL2 ==> Record(ans : F, right : F, primpart : F, sol? : Boolean)

  Exports ==> with

    ei_int : (Z, F, F, SY) -> PSOL2
      ++ ei_int(n, f, g, x) returns \spad{[h, u, t, s]} such that
      ++ \spad{u*exp(n*f) = (h*exp(n*f))' + t'} where t involves
      ++ special functions.  When s is true then \spad{g = u}, otherwise
      ++ ei_int returns partial answer,
    mk_erf : (F, F) -> F
      ++ mk_erf(u, f) should be local but conditional

  Implementation ==> add
    import from IntegrationTools(R, F)
    import from TranscendentalIntegration(F, UP)
    import from PolynomialCategoryQuotientFunctions(IndexedExponents K,
                                                             K, R, P, F)
    ALGOP := '%alg
    PRIM := 'prim

    top_kernel(f : F, g : F, x : SY) : K ==
        lkf := varselect(kernels(f), x)
        lk := union(varselect(kernels(g), x), lkf)
        tk := first lk
        htk := height(tk)
        extra_tk : Boolean := not(member?(tk, lkf))
        for k in rest(lk) repeat
            hk := height(k)
            hk > htk =>
                tk := k
                htk := hk
                extra_tk := not(member?(tk, lkf))
            hk < htk => iterate
            is?(tk, 'exp) and extra_tk => iterate
            is?(k, 'exp) and not(member?(k, lkf)) =>
                tk := k
                extra_tk := true
            is?(tk, 'exp) => iterate
            is?(k, 'exp) =>
                tk := k
                extra_tk := false
            is?(tk, 'log) and extra_tk => iterate
            is?(k, 'log) and not(member?(k, lkf)) =>
                tk := k
                extra_tk := true
            is?(tk, 'log) => iterate
            is?(k, 'log) =>
                tk := k
                extra_tk := false
            if has?(operator tk, ALGOP)$BasicOperator then
                has?(operator k, ALGOP)$BasicOperator =>
                    extra_tk => iterate
                    if not(member?(k, lkf)) then
                        tk := k
                        extra_tk := true
                tk := k
                extra_tk := not(member?(k, lkf))
        tk

    import from OutputForm

    EI_REC ==> Record(factor : UP, ratpart : Z, v_part : F,
                    alg_part : UP, u_part : F, exponent : Z,
                    shift_part : F)

    EI_REC2 ==> Record(factor : UP, ratpart : Z, v_part : F,
                    alg_part : UP, u_part : F, exponent : Z,
                    shift_part : F, has_part2? : Boolean,
                    ratpart2 : Z, v_part2 : F, u_part2 : F,
                    shift_part2 : F)

    UPUP ==> SparseUnivariatePolynomial(UP)

    CV_REC ==> Record(coef1 : RF, coef2 : RF, exponent : N)
    genus0_if_can(k : K, kx : K) : Union(CV_REC, "failed") ==
        not(is?(k, 'nthRoot)) => "failed"
        args := argument(k)
        n := retract(args(2))@Integer::N
        r1 := args(1)
        r1rf := univariate(r1, kx)
        nrf := numer(r1rf)
        drf := denom(r1rf)
        degree(nrf)$UP >$Z 1 or degree(drf) >$Z 1 => "failed"
        a := coefficient(nrf, 1)
        b := coefficient(nrf, 0)
        c := coefficient(drf, 1)
        d := coefficient(drf, 0)
        c1 := (monomial(d, n)$UP - b::UP)::RF/
              (monomial(-c, n)$UP + a::UP)::RF
        [c1, r1rf, n]

    INTEF ==> ElementaryIntegration(R, F)

    ei_int_alg(f : F, g : F, x : SY, k : K, lk : LK) : PSOL2 ==
        kx := kmax(lk)
        not(symbolIfCan(kx) case SY) =>
            do_risch_DE(f, g, x, cons(k, lk))$ElementaryRischDEX2(R, F)
        (cvu := genus0_if_can(k, kx)) case "failed" =>
            do_risch_DE(f, g, x, cons(k, lk))$ElementaryRischDEX2(R, F)
        cv := cvu@CV_REC
        nx := new()$Symbol
        nxk := kernel(nx)
        fx := multivariate(cv.coef1, nxk)
        fy := nxk::F
        dfx := D(fx, nx)
        ng := dfx*eval(g, [kx, k], [fx, fy])
        nf := eval(f, [kx, k], [fx, fy])
        res1 := ei_int(1, nf, ng, nx)
        kf := k::F
        r1 := eval(res1.right/dfx, nxk, kf)
        a1 := eval(res1.ans, nxk, kf)
        ansp := eval(res1.primpart, nxk, kf)
        [a1, r1, ansp, res1.sol?]

    UP_to_UPUP(p : UP) : UPUP ==
        map((c : F) : UP +-> c::UP, p
           )$SparseUnivariatePolynomialFunctions2(F, UP)

    UV_REC ==> Record(u_part : F, v_part : F, exponent : Z, coeff : Z)

    get_uv0(tt : F, coeffs : List(Z), vlst : List(F), ulst : List(F),
            k : K, x : SY) : UV_REC ==
        v0 := tt - reduce(_+, [ki::F*vi for vi in vlst for ki in coeffs], 0)
        ku : Z := 0
        u : F := 1
        if is?(k, 'exp) then
            kf := k::F
            for ui in ulst for ki in coeffs repeat
                ui = kf => ku := ki
                u := u*ui^ki
        else
            u := reduce(_*, [ui^ki for ui in ulst for ki in coeffs], 1)
        kc : Z := 0
        if is?(k, 'log) then
            kf := k::F
            for vi in vlst for ki in coeffs repeat
                if vi = kf then kc := ki
        [u, v0, ku, kc]

    get_uv(tt : F, vlst : List(F), ulst : List(F), k : K, x : SY
         ) : Union(UV_REC, "failed") ==
        dvvec := vector([D(vi, x) for vi in vlst])$Vector(F)
        su := particularSolutionOverQ(dvvec, D(tt, x)
                                )$IntegerLinearDependence(F)
        su case "failed" => "failed"
        sv := su@Vector(Q)
        cd := splitDenominator(sv)$InnerCommonDenominator(
                     Integer, Q, List(Integer), Vector(Q))
        d := cd.den
        d ~= 1 => "failed"
        get_uv0(tt, cd.num, vlst, ulst, k, x)

    dummy := create()$SingletonAsOrderedSet

    handle_factor(gd : UP, nf : UP, denf : UP, f1 : UP, r0 : Z, u0 : F,
                  vlst : LF, ulst : LF, k : K, x : SY,
                  known_dividing : Boolean) : Union(EI_REC, "failed") ==
        deg1 : N := degree(f1)
        tt := -$F coefficient(f1, qcoerce(deg1 - 1)$N)$UP /$F
                ((deg1::F)*coefficient(f1, deg1)$UP)
        f2 := eval(f1, dummy, monomial(1, 1)$UP + tt::UP)
        not(every?((c : F) : Boolean +-> D(c, x) = 0,
               coefficients(f2))$LF) => return "failed"
        uvu := get_uv(tt, vlst, ulst, k, x)
        uvu case "failed" => return "failed"
        uv := uvu@UV_REC
        u := uv.u_part
        u_exp := uv.exponent
        v0 := uv.v_part
        tt1 : UP
        if r0 = 0 then
            r0 := uv.coeff
            tt := tt - (r0::F)*(k::F)
        else
            u := u0^r0*u
        tt1 := monomial(r0::F, 1)$UP + tt::UP
        ff := nf + tt1*denf
        if deg1 > 1 then
            ff := resultant(monomial(denf, 1)$UPUP + ff::UPUP,
                            UP_to_UPUP(f2))
        f2pp : UP :=
            deg1 = 1 => 0
            f2
        known_dividing =>
            ff := ff/leadingCoefficient(ff)
            [ff, r0, tt, f2pp, u, u_exp, v0]$EI_REC
        ff2 := ff1 := gcd(ff, gd)
        ff3 := (ff exquo ff1)::UP
        while (ff2 := gcd(ff2, ff3)) ~= 1 repeat
            ff3 := (ff3 exquo ff2)::UP
        not(degree(ff3) = 0 or
            (is?(k, 'exp) and reductum(ff3) = 0)) => return "failed"
        ff1 := ff1/leadingCoefficient(ff1)
        [ff1, r0, tt, f2pp, u, u_exp, v0]$EI_REC

    Part_REC ==> Record(particular : Vector Z, basis : List Vector Z)

    -- Given solution a and basis b of homogeneous system over Q
    -- find solution over Z and basis of homogeneous solutions
    -- over Z.  Works only if basis has at most 1 element.
    z_basis(a : Vector Q, b : List Vector Q
           ) : Union(Part_REC, "failed") ==
        (nn := #b) > 1 => error "#b > 1"
        cd := splitDenominator(a)$InnerCommonDenominator(
                     Integer, Q, List(Integer), Vector(Q))
        da := cd.den
        av := vector(cd.num)$Vector(Z)
        nn = 0 =>
            da ~= 1 => "failed"
            [av, []]$Part_REC
        bvp := first(b)
        cd := splitDenominator(bvp)$InnerCommonDenominator(
                     Integer, Q, List(Integer), Vector(Q))
        db := cd.den
        bl := cd.num
        gb := gcd(bl)
        bv := vector(bl)$Vector(Z)
        n := #bv
        for i in 1..n repeat bv(i) := (bv(i) exquo gb)::Z
        da = 1 => [av, [bv]]$Part_REC
        (db exquo da) case "failed" => "failed"
        ki : Z := 0
        di : Z := 1
        for i in 1..n repeat
            ai := av(i)
            bi := bv(i)
            (ai - ki*bi) rem di ~= 0 => return "failed"
            di1 := (da exquo gcd(bi, da))::Z
            di2 := (di1 exquo gcd(di1, di))::Z
            di2 = 1 => iterate
            ki1 := invmod(bi, di2)$Integer
            (c1, c2, g) := extendedEuclidean(di, di2)
            di := di2*di
            ki := (ki1*c1 + ki*c2) rem di
        av := av + ki*bv
        for i in 1..n repeat av(i) := (av(i) exquo da)::Z
        [av, [bv]]$Part_REC

    lin_args(gd : UP, nf : UP, k : K, vlst : List(F),
             ulst : List(F), x : SY) : List(EI_REC2) ==
        fgd := factorPolynomial(gd)$ExpressionFactorPolynomial(R, F)
        degree(nf) >$Z 1 =>
            error "lin_args only works for linear nf"
        a1 : F :=
            degree(nf) = 1 => leadingCoefficient(nf)
            0
        a0 := coefficient(nf, 0)
        v0 := k::F
        vlst1 : List(F) := []
        ulst1 : List(F) := []
        u0 : F
        for v in vlst for u in ulst repeat
            v = v0 =>
                u0 := u
            vlst1 := cons(v, vlst1)
            ulst1 := cons(u, ulst1)
        res : List(EI_REC2) := []
        for fac in factorList(fgd) repeat
            f1 := fac.factor
            deg1 : N := degree(f1)
            deg1 = 0 => iterate
            f1 := f1/leadingCoefficient(f1)
            tt := -$F coefficient(f1, qcoerce(deg1 - 1)$N)$UP /$F (deg1::F)
            no_sol : Boolean := false
            has_r0 : Boolean := false
            f2 : UP := 0
            r0 : Z := 0
            r0f : F := 0
            if deg1 > 1 then
                f2 := eval(f1, dummy, monomial(1, 1)$UP + tt::UP)
                f2 := f2/leadingCoefficient(f2)
                if (a1p := D(a1, x)) ~= 0 then
                    has_r0 := true
                    b0 := coefficient(f2, 0)
                    (b0p := D(b0, x)) = 0 =>
                        no_sol := true
                    r0f := -(deg1::F)*a1p*b0/b0p - a1
                    r0u := retractIfCan(r0f)@Union(Z, "failed")
                    r0u case "failed" =>
                        no_sol := true
                    r0 := r0u@Z
                    f2 := (a1 + r0f)^deg1*eval(f2,
                             dummy, monomial(1/(a1 + r0f), 1))
                not(every?((c : F) : Boolean +-> D(c, x) = 0,
                    coefficients(f2))$LF) => no_sol := true
            no_sol => iterate
            dv0 := -D(a1*tt + a0, x)
            dv1 := D(tt, x)
            dvs := [D(v, x) for v in vlst1]
            vecf :=
                has_r0 =>
                    dv0 := dv0 + r0f*dv1
                    vector(dvs)$Vector(F)
                vector(cons(dv1, dvs))$Vector(F)
            sol_rec := solveLinearlyOverQ(vecf, dv0)$IntegerLinearDependence(F)
            psolu := sol_rec.particular
            psolu case "failed" => iterate
            psol := psolu@Vector(Q)
            if has_r0 then
                psol := vector(cons(r0::Q, entries(psol)))$Vector(Q)
            zbu := z_basis(psol, sol_rec.basis)
            zbu case "failed" => iterate
            zb := zbu@Record(particular : Vector Z, basis : List Vector Z)
            vec1 := zb.particular
            r0 := vec1(1)
            a1n := a1 + r0::F
            tt1 := -a1n*tt - a0
            uv1 := get_uv0(tt1, rest(entries(vec1)), vlst1, ulst1, k, x)
            u1 := uv1.u_part*u0^r0
            v1 := uv1.v_part
            empty?(zb.basis)$List(Vector Z) =>
                res := cons([f1, r0, tt1, f2, u1, 0, v1,
                                 false, 0, 0, 1, 0]$EI_REC2, res)
            bv := first(zb.basis)
            r2 := bv(1)
            uv2 := get_uv0(-tt, rest(entries(bv)), vlst1, ulst1, k, x)
            u2 := uv2.u_part*u0^r2
            v2 := uv2.v_part
            res := cons([f1, r0, tt1, f2, u1, 0, v1, true,
                          r2, -tt, u2, v2]$EI_REC2, res)
        res

    SOL_REC ==> Record(ratpart : Q, alg_part : UP)

    FP ==> Polynomial(F)
    FRF ==> Fraction(FP)

    get_rational_solutions(eq1 : Equation(FRF), s1 : SY, s2 : SY) : List(Q) ==
        (rv := rhs(eq1)) ~=$FRF 0 =>
            s1 ~= retract(lhs(eq1))@SY =>
                error "wrong format of solution 1"
            ru0 := retractIfCan(rv)@Union(FP, "failed")
            ru0 case "failed" => []
            ru1 := retractIfCan(ru0@FP)@Union(F, "failed")
            ru1 case "failed" => []
            ru2 := retractIfCan(ru1@F)@Union(Q, "failed")
            ru2 case "failed" => []
            [ru2@Q]
        eq1p := numer(lhs(eq1))$FRF
        member?(s2, variables(eq1p))$List(SY) =>
                error "wrong format of solution 2"
        k1 := kernel(s1)$K
        eq1f := retract(eval(eq1p, s1, k1::F))@F
        get_rational_roots(eq1f, k1)$FunctionSpaceRationalRoots(R, F)

    F_to_FP(p : F, ls : List(SY), lk : List(K)) : FP ==
        empty?(lk) => p::FP
        k1 := first(lk)
        s1 := first(ls)
        pu := numer(univariate(p, k1))$RF
        pu1 := map((c : F) : FP +-> F_to_FP(c, rest(ls), rest(lk)), pu
                  )$SparseUnivariatePolynomialFunctions2(F, FP)
        multivariate(pu1, s1)

    solve_poly_eqs(eqs : List(F), s1 : SY, s2 : SY) : List SOL_REC ==
        k1 := kernel(s1)$K
        k2 := kernel(s2)$K
        eqs0 := [F_to_FP(numer(eqi)::F, [s1, s2], [k1, k2])
                 for eqi in eqs]$List(FP)
        lsol0 := solve([ci::FRF for ci in eqs0], [s2, s1]
                      )$SystemSolvePackage(F)
        res : List(SOL_REC) := []
        for sol0 in lsol0 repeat
            #sol0 ~=$Z 2 => error "wrong format of solution 0"
            eq0 := first(sol0)
            eq0p := numer(lhs(eq0) - rhs(eq0))
            eq1 := last(sol0)
            for val1r in get_rational_solutions(eq1, s1, s2) repeat
                val2eq := eval(eq0p, s1, val1r::F)
                val2alg : UP := univariate(val2eq)$FP
                res := cons([val1r, val2alg]$SOL_REC, res)
        res

    ei_args3 : (UP, UP, UP, K, LF, LF, SY) -> List(EI_REC)

    ei_args0(gd : UP, nf : UP, denf : UP, k : K, vlst0 : List(F),
          ulst0 : List(F), x : SY) : List(EI_REC) ==
        vlst : LF := []
        ulst : LF := []
        kf := k::F
        u0 : F
        for v in vlst0 for u in ulst0 repeat
            if v ~= kf then
                vlst := cons(v, vlst)
                ulst := cons(u, ulst)
            else
                u0 := u
        as := new()$Symbol
        a := kernel(as)$K
        bs := new()$Symbol
        b := kernel(bs)$K
        nn := nf + (monomial(a::F, 1)$UP + b::F::UP)*denf
        qq := gd rem nn
        eqs0 := [numer(ci)::F for ci in coefficients(qq)]
        lsol := solve_poly_eqs(eqs0, as, bs)
        has_r_bad : Boolean := false
        r_bad : Z := 0
        if degree(denf) + 1 >= degree(nf) then
            degree(denf) + 1 > degree(nf) =>
                has_r_bad := true
                r_bad := 0
            cc := -leadingCoefficient(nf)/leadingCoefficient(denf)
            ccu := retractIfCan(cc)@Union(Z, "failed")
            if ccu case Z then
                has_r_bad := true
                r_bad := ccu@Z
        res : List(EI_REC) := []
        for sol in lsol repeat
            (ru := retractIfCan(sol.ratpart)@Union(Z, "failed"))
               case "failed" => iterate
            has_r_bad and ru@Z = r_bad => iterate
            res1 := handle_factor(gd, nf, denf, sol.alg_part, ru::Z, u,
                     vlst, ulst, k, x, true)
            res1 case "failed" => iterate
            res := cons(res1@EI_REC, res)
        nnp := differentiate(nf)*denf - differentiate(denf)*nf + a::F::UP
        eq1 := resultant(nnp, gd)
        ratl := get_rational_roots(eq1, a)$FunctionSpaceRationalRoots(R, F)
        if has_r_bad and not(member?(r_bad::Q, ratl)) then
            ratl := cons(r_bad::Q, ratl)
        degd := degree(denf)
        degn := degree(nf)
        degn1 := max(degn, degd + 1)
        for r0 in ratl repeat
            (ru := retractIfCan(r0)@Union(Z, "failed")) case "failed" =>
                iterate
            ri := ru@Z
            nn := nf + monomial(ri::F, 1)$UP * denf
            res1l := ei_args3(gd, nn, denf, k, vlst, ulst, x)
            for res1 in res1l repeat
                ap := res1.alg_part
                fac := res1.factor
                dega : Z :=
                    ap = 0 => 1
                    degree(ap)
                degree(fac) = degn1*dega => iterate
                res := cons([fac, ri, res1.v_part, ap, u0^ri*res1.u_part,
                             0, res1.shift_part]$EI_REC, res)
        res

    ei_args1(gd : UP, nf : UP, denf : UP, t : K) : Factored(UP) ==
        r : F := resultant(nf + (t::F)*denf, gd)
        factorPolynomial(numer(univariate(r, t))$RF
                        )$ExpressionFactorPolynomial(R, F)

    ei_args2(gd : UP, nf : UP, denf : UP, f_res : Factored(UP),
             k : K, vlst : List(F), ulst : List(F), x : SY
            ) : List(EI_REC)  ==
        rfac := factorList(f_res)$Factored(UP)
        res : List EI_REC := []
        for fac in rfac repeat
            f1 := fac.factor
            deg1 := degree(f1)
            deg1 = 0 => iterate
            resu := handle_factor(gd, nf, denf, f1, 0, 1, vlst, ulst,
                                  k, x, false)
            if resu case EI_REC then
                res := cons(resu@EI_REC, res)
        res

    ei_args3(gd : UP, nf : UP, denf : UP, k : K, vlst : List(F),
          ulst : List(F), x : SY) : List(EI_REC) ==
        t := kernel(new()$Symbol)$K
        f_res := ei_args1(gd, nf, denf, t)
        ei_args2(gd, nf, denf, f_res, k, vlst, ulst, x)

    ei_args(gd : UP, nf : UP, denf : UP, k : K, vlst : List(F),
          ulst : List(F), x : SY) : List(EI_REC) ==
        degree(gd) =$N 0 => []
        res1 : List(EI_REC) :=
            is?(k, 'log) =>
                ei_args0(gd, nf, denf, k, vlst, ulst, x)
            ei_args3(gd, nf, denf, k, vlst, ulst, x)
        res1

    my_exp(p : UP, n : N) : UP == p^n

    SREC ==> Record(degree : Z, factor : UP, coeff : F, ratpart : Z,
                    v_part : F, alg_part : UP, new_f : RF, radicand : RF,
                    u_part : F, exponent : Z, shift_part : F, a_val : F)

    ei_candidate(nf : UP, denf : UP, vlst : List(F), ulst : List(F),
                 k : K, x : SY, a : Z, tt : F, res : List SREC) : List SREC ==
        tt := -tt
        nf1 := nf + tt*denf
        reductum(nf1) ~= 0 => res
        degree(nf1) > 0 and not(is?(k, 'exp)) => res
        uvu := get_uv(tt, vlst, ulst, k, x)
        uvu case "failed" => res
        uv := uvu@UV_REC
        cons([1, nf1, 1, a, tt, 0, nf1/denf, 0, uv.u_part, uv.exponent,
                                 uv.v_part, 0]$SREC, res)

    special_ei_candidates(nf : UP, denf : UP, vlst : LF,
                       ulst : LF, k : K, x : SY) : List SREC ==
        tt : F := 0
        a : Z := 0
        is?(k, 'exp) =>
            res : List(SREC) := []
            n1 := degree(nf)
            n2 := degree(denf)
            rnf := reductum(nf)
            rdf := reductum(denf)
            n1 = n2 =>
                tt := leadingCoefficient(nf)
                res := ei_candidate(nf, denf, vlst, ulst, k, x, a, tt, res)
                rdf ~= 0 and degree(rdf) = degree(rnf) =>
                    tt := leadingCoefficient(rnf)/leadingCoefficient(rdf)
                    ei_candidate(nf, denf, vlst, ulst, k, x, a, tt, res)
                res
            res := ei_candidate(nf, denf, vlst, ulst, k, x, a, 0, res)
            n1 < n2 and rdf ~= 0 and degree(rdf) = n1 =>
                tt := leadingCoefficient(nf)/leadingCoefficient(rdf)
                ei_candidate(nf, denf, vlst, ulst, k, x, a, tt, res)
            n1 > n2 and rnf ~= 0 and degree(rnf) = n2 =>
                tt := leadingCoefficient(rnf)
                ei_candidate(nf, denf, vlst, ulst, k, x, a, tt, res)
            res
        degree(nf) >$Z degree(denf) + 1 => []
        if is?(k, 'log) and degree(nf) = degree(denf) + 1 then
            lc := leadingCoefficient(nf)
            (rcu := retractIfCan(lc)@Union(Z, "failed")) case "failed" =>
                return []
            a := -(rcu@Z)
            nf := nf + monomial(a::F,1)$UP*denf
        if degree(nf) = degree(denf) then
            tt := leadingCoefficient(nf)
        ei_candidate(nf, denf, vlst, ulst, k, x, a, tt, [])

    gamma_candidate(fnf : Factored(UP), nf1 : UP, denf : UP,
                    gden1 : UP, gexp : Z, a : Z,
                    tt : F, f2 : UP, vlst : LF, ulst : LF, k : K, x : SY,
                    res : List SREC) : List SREC ==
        nffacs := factorList(fnf)
        lexp : List(Z) := [fac1.exponent for fac1 in nffacs]
        l1 := gcd(gexp, gcd(lexp))
        l1 = 1 => res
        lexp1 : List(N) := [(e1 quo l1)::N for e1 in lexp]
        nf2 := reduce(_*, [my_exp(fac1.factor, e1) for e1 in lexp1
                           for fac1 in nffacs], 1)$List(UP)
        gden1 : UP :=
            gexp = 1 => 1
            my_exp(gden1, (gexp quo l1)::N)
        uvu := get_uv(tt, vlst, ulst, k, x)
        uvu case "failed" => res
        uv := uvu@UV_REC
        unf := retract(unit(fnf))@F
        if D(unf, x) ~= 0 then
            ur := froot(unf, l1::N
                       )$PolynomialRoots(IndexedExponents K, K, R, P, F)
            if D(ur.radicand, x) ~= 0 then return res
            mu := l1 exquo ur.exponent
            if mu case "failed" then return res
            m1 := mu@Z
            nf2 := nf2*ur.coef
            unf := (ur.radicand)^m1
        cons([l1, nf2, unf, a, tt, f2, nf1/denf, nf2/gden1,
              uv.u_part, uv.exponent, uv.v_part, 0]$SREC, res)

    solve_for_ratios(lc : List(F)) : List(Z) ==
        n := #lc
        nlc : List(F) := []
        v := new(n, 1)$Vector(Integer)
        for i in 1..n for c in lc repeat
            retractIfCan(c)@Union(Q, "failed")
              case Q => (v(i) := 0)
            nlc := cons(c, nlc)
        n1 := #nlc
        n1 = 0 => []
        n1 = 1 => [v(1) for i in 1..n]
        c1 := first(nlc)
        nlc := rest(nlc)
        nn1 := (n1 - 1)::N
        m := zero(nn1, 2*nn1 + 1)$Matrix(F)
        for i in 1..nn1 for c in nlc repeat
            m(i, 1) := -c
            m(i, i + 1) := c1
            m(i, i + n1) := 1
        v0 := zero(nn1)$Vector(F)
        res := solveLinearlyOverQ(m, v0)$IntegerLinearDependence(F)
        bl := res.basis
        empty?(bl) => []
        s1 : Vector(Q) := first(bl)
        d := gcd([denom(s1(i))$Q for i in 1..(nn1 + 1)]$List(Z))
        j : N := 1
        rl : List(Integer) := []
        for i in n..1 by - 1 repeat
            li : Z :=
                v(i) = 0 => 0
                jj := j
                j := j + 1
                numer(d*s1(jj))$Q
            rl := cons(li, rl)
        rl

    M_rec ==> Record(coef : F, ratpart : Vector(Q))
    S_rec ==> Record(particular : Union(M_rec, "failed"),
                     basis : List(M_rec))

    possible_sk(w0 : F, f : F, tk : K, x : SY) : List(List(Q)) ==
        has?(operator tk, ALGOP)$BasicOperator => []
        wu := univariate(w0, tk)
        nwu := numer(wu)
        dwu := denom(wu)
        dnw := degree(nwu)
        ddw := degree(dwu)
        dnw = 0 and ddw = 0 => error "impossible"
        fu := univariate(f, tk)
        nfu := numer(fu)
        dfu := denom(fu)
        dnf := degree(nfu)
        ddf := degree(dfu)
        mindf : Z := 0
        mindw : Z := 0
        if is?(tk, 'exp) then
            mindw := minimumDegree(nwu)
            mindw :=
                mindw > 0 => mindw
                -minimumDegree(dwu)
            mind := minimumDegree(dfu)
            mindf :=
                mind > 0 => -mind
                minimumDegree(nfu)
            ddf > mind =>
                dfu := (dfu exquo monomial(1, mind))::UP
                ddf := qcoerce(ddf - mind)
                if (mind := minimumDegree(dwu)) > 0 then
                    dwu := (dwu exquo monomial(1, mind))::UP
                    ddw := degree(dwu)
                if (mind := minimumDegree(nwu)) > 0 then
                    nwu := (nwu exquo monomial(1, mind))::UP
                    dnw := degree(nwu)
            vq := vector([mindf::Q, (dnf - ddf)::Q])$Vector(Q)
            mq := matrix([[mindw::Q, (dnw - ddw)::Q], [1, 1]])$Matrix(Q)
            sol := solve(mq, vq)$LinearSystemMatrixPackage(
                         Q, Vector Q, Vector Q, Matrix Q)
            res : List(List(Q)) :=
                sol.particular case "failed" => []
                solq := (sol.particular)::Vector Q
                [parts(solq)]
            mindf < 0 and dnf - ddf > 0 => return res
            if mindf = 0 then
                nwu1 := nwu - monomial(coefficient(nwu, 0), 0)$UP
                vq(1) := minimumDegree(nwu1)::Q
            else if mindf > 0 then
                vq(1) := 0$Q
            else if dnf - ddf < 0 then
                vq(2) := 0$Q
            else -- dnf = ddf
                nfu1 := reductum(nfu)
                vq(2) := (degree(nfu1) - ddf)::Q
            sol := solve(mq, vq)$LinearSystemMatrixPackage(
                         Q, Vector Q, Vector Q, Matrix Q)
            sol.particular case "failed" => return res
            solq := (sol.particular)::Vector Q
            return cons(parts(solq), res)
        if dnf = 0 and ddf = 0 then
            is?(tk, 'log) => dnf := 1
            return []
        ddf > 0 =>
            g := gcd(dfu, dwu)
            s : Q :=
                degree(g) > 0 => ddf/ddw
                ddf/dnw
            [[s, mindf::Q - s*mindw]]
        ss : Z := 1
        if ddw > 0 then
            dnw > 0 => return []
            ss := -1
            dnw := ddw
            nwu := dwu
        [[ss*dnf/dnw, 0]]

    possible_w(f2 : F, w1 : F, x : SY) : List(F) ==
        tk := top_kernel(1, w1, x)
        w1u := univariate(w1, tk)
        f2u := univariate(f2, tk)
        nw1 := numer(w1u)
        dw1 := denom(w1u)
        nf2 := numer(f2u)
        df2 := denom(f2u)
        w2 : F
        not(df2 = dw1) =>
            []
        if degree(dw1) > 0 then
            r1 := nw1 rem dw1
            r2 := nf2 rem dw1
            w2u := r2 exquo r1
            w2u case "failed" => return []
            w2u2 := retractIfCan(w2u@UP)@Union(F, "failed")
            w2u2 case "failed" => return []
            w2 := w2u2
        else
            lc_nf2 := leadingCoefficient(nf2)
            lc_nw1 := leadingCoefficient(nw1)
            if degree(nw1) > 1 or not(is?(tk, 'log)) then
                w2 := lc_nf2/lc_nw1
            else
                -- FIXME unimplemented
                degree(nf2) = 1 and D(lc_nf2, x) ~= 0 =>
                    return []
                w2 := 1/lc_nw1
        w2 = 0 => []
        [w2]

    gen_irr_log_cands(a : F, f : F, f2 : F, w1 : F, vlst : List(F),
                      ulst : List(F), k : K, x : SY,
                      res : List SREC
                     ) : List SREC ==
        lw2 := possible_w(f2, w1, x)
        empty?(lw2) => res
        w2 := first(lw2)
        lw2' := D(w2, x)/w2
        v' := lw2' + D(w1, x)/w1
        dvvec := vector([D(vi, x) for vi in vlst])$Vector(F)
        su := particularSolutionOverQ(dvvec, v'
                                )$IntegerLinearDependence(F)
        su case "failed" => res
        sv := su@Vector(Q)
        cd := splitDenominator(sv)$InnerCommonDenominator(
                     Integer, Q, List(Integer), Vector(Q))
        d := cd.den
        d ~= 1 => res
        -- h1 := height(k)
        coeffs := cd.num
        a1 := a - 1
        w := -w1*w2
        tt := f + w
        wu := univariate(w, k)
        su := particularSolutionOverQ(dvvec, lw2'
                                     )$IntegerLinearDependence(F)
        su case "failed" => error "impossible"
        sv := su@Vector(Q)
        cd := splitDenominator(sv)$InnerCommonDenominator(
                     Integer, Q, List(Integer), Vector(Q))
        d := cd.den
        d ~= 1 => error "impossible"
        coeffs2 := cd.num
        w_cor := -w2
        for ki in coeffs for k2i in coeffs2 for vi in vlst
          for ui in ulst repeat
                tt := tt - (ki::F)*a1*vi
                w_cor := w_cor*(ui)^(-k2i)
        D(w_cor, x) ~= 0 => error "impossible"
        su := particularSolutionOverQ(dvvec, D(tt, x)
                                )$IntegerLinearDependence(F)
        su case "failed" => res
        sv := su@Vector(Q)
        cd := splitDenominator(sv)$InnerCommonDenominator(
                     Integer, Q, List(Integer), Vector(Q))
        d := cd.den
        d ~= 1 => res
        vli := cd.num
        uu : F := -1
        uu1 : F := 1
        gl := gcd(coeffs)
        if gl > 1 then
            for ki in coeffs for ui in ulst repeat
                uu1 := ui^(ki quo gl)*uu1
        for li in vli for vi in vlst for ui in ulst repeat
            tt := tt - (li::F)*vi
            uu := uu*ui^(-li)
        for i in 0..(gl-1) repeat
            ai := a - i::F/(gl::F)
            res := cons([0, 1, w_cor, 0, 0, 0, wu, 0, uu, 0, -tt, ai]$SREC,
                        res)
            uu := uu/uu1
        res

    irrational_gamma_candidates(nf : UP, denf : UP, vlst : List(F),
            ulst : List(F), k : K, x : SY, res : List SREC) : List SREC ==
        is?(k, 'log) and denf = 1 and degree(nf) =$Z 1 =>
            n1 := height(k)
            nlk := [kk for kk in kernels(coefficient(nf, 0)) |
                      D(kk::F, x) ~= 0 and height(kk) = n1]
            nlk := cons(k, nlk)
            f := multivariate(nf/denf, k)
            f2 := f
            cl : List(F) := []
            for kk in nlk repeat
                not(is?(kk, 'log)) =>
                    is?(kk, 'exp) => error "unimplemented"
                    return res
                nfr := univariate(f2, kk)
                denom(nfr) ~= 1 => return res
                nf1 := numer(nfr)
                degree(nf1) = 0 => error "impossible"
                degree(nf1) ~= 1 => return res
                ck := leadingCoefficient(nf1)
                f2 := coefficient(nf1, 0)
                D(ck, x) ~= 0 => return res
                cl := cons(ck, cl)
            cl := reverse!(cl)
            rl := solve_for_ratios(cl)
            every?(zero?, rl) => res
            w0 : F := 1
            for li in rl for kk in nlk repeat
                ak := argument(kk).1
                w0 := w0*ak^li
            tk := top_kernel(1, w0, x)
            lsk := possible_sk(w0, f2, tk, x)
            for sk in lsk repeat
                -- FIXME: need to handle unnormalized case
                (sq, kmq) := sk
                sq = 0 => "skip"
                denom(sq) ~= 1 => "skip"
                denom(kmq) ~= 1 => "skip"
                s := retract(sq)@Z
                km := retract(kmq)@Z
                -- FIXME: choose nonzero element of rl
                a := first(cl)
                a := a/(s*first(rl)::F) + 1
                w1 := w0^s*(tk::F)^km
                res := gen_irr_log_cands(a, f, f2, w1, vlst,
                               ulst, k, x, res)
            res
        res

    gammas1(nf : UP, a : Z, denf : UP, denf1 : UP, gexp : Z, n2 : Z,
            vlst : LF, ulst : LF,
            k : K, x : SY, res : List SREC) : List SREC ==
        t := kernel(new()$Symbol)
        nfp1 := differentiate(nf)*denf1 - (gexp::F)*nf*differentiate(denf1)
        r : F := resultant(nf + (t::F)::UP*denf, nfp1)
        f_res := factorPolynomial(numer(univariate(r, t))$RF
                                 )$ExpressionFactorPolynomial(R, F)
        rfacs := factorList(f_res)
        e_tt : List(F) :=
            degree(nf) = 0 => [0]
            degree(nf) = degree(denf) =>
                tt := -leadingCoefficient(nf)/leadingCoefficient(denf)
                nf1 :=  nf + tt::UP*denf
                degree(nf1) = 0 => [tt]
                []
            []
        if not(empty?(e_tt)) then
            tt := first(e_tt)
            nf1 :=  nf + tt::UP*denf
            fnf := makeFR(nf1, [])$Factored(UP)
            res := gamma_candidate(fnf, nf1, denf, denf1, gexp, a, tt,
                                   0, vlst, ulst, k, x, res)
        for fac in rfacs repeat
            f1 := fac.factor
            deg1 := degree(f1)
            deg1 = 1 =>
                tt := -coefficient(f1, 0)/coefficient(f1, 1)
                nf1 := nf + tt::UP*denf
                2*fac.exponent < degree(nf1) => iterate
                fnf := squareFree(nf1)
                res := gamma_candidate(fnf, nf1, denf, denf1, gexp, a, tt,
                                       0, vlst, ulst, k, x, res)
            -- no cancellation in leading term possible, so
            -- n2 = deg nf1
            fac.exponent < n2 => iterate
            if deg1 = 2 then
                tt := -coefficient(f1, 1)/(2::F*leadingCoefficient(f1))
                f2 := eval(f1, dummy, monomial(1, 1)$UP + tt::UP)
                not(every?((c : F) : Boolean +-> D(c, x) = 0,
                    coefficients(f2))$LF) => iterate
                cfac := rootOf(f2)
                nf1 := nf + (tt+cfac)::UP*denf
                fnf := squareFree(nf1)
                res := gamma_candidate(fnf, nf1, denf, denf1, gexp, a, tt,
                                       f2, vlst, ulst, k, x, res)
        res

    special_candidates(nf : UP, denf : UP, denf1 : UP, gexp : Z,
                       vlst : List(F), ulst : List(F), k : K, x : SY
                      ) : List SREC ==
        res1 : List SREC := special_ei_candidates(nf, denf, vlst, ulst, k, x)
        res1 := irrational_gamma_candidates(nf, denf, vlst, ulst, k, x, res1)
        a : Z := 0
        n1 := degree(nf)
        n2 := degree(denf)
        n1 = 1 => res1
        if n1 = n2 + 1 then
            not(is?(k, 'log)) => return res1
            lc := leadingCoefficient(nf)
            (rcu := retractIfCan(lc)@Union(Z, "failed")) case "failed" =>
                return res1
            a := rcu@Z
            nf := nf - monomial(a::F,1)$UP*denf
            n1 := n2
        n3 := max(n1, n2)
        l : Z := gcd(gexp, n3)
        l = 1 => res1
        ts := new()$Symbol
        t := kernel(ts)$K
        nfp1 := differentiate(nf)
        denfp1 := differentiate(denf1)
        n1 > n2 and is?(k, 'log) =>
            nnp := nfp1*denf1 - (gexp::F)*denfp1*nf + t::F::UP*denf*denf1
            odd?(l) =>
                nfp2 := differentiate(nfp1)
                denfp2 := differentiate(denfp1)
                denf2 := denf1*denf1
)if false
                -- as single expression takes too long to compile
                nnp2 := nfp2*denf2 - (2*gexp)::F*denfp1*nfp1*denf1 +
                        ((gexp+1)*gexp)::F*denfp1*denfp1*nf -
                          (gexp::F)*denfp2*nf*denf1
)else
                t1 := (2*gexp)::F*denfp1
                t1 := t1*nfp1*denf1
                ct1 := ((gexp+1)*gexp)::F
                t2 := ct1*denfp1
                t2 := t2*denfp1*nf
                t3 := (gexp::F)*denfp2
                t3 := t3*nf*denf1
                nnp2 := nfp2*denf2 - t1 + t2 - t3
)endif
                f_res1 := resultant(nnp, nnp2)
                ratl := get_rational_roots(f_res1, t
                                          )$FunctionSpaceRationalRoots(R, F)
                for r0 in ratl repeat
                    ru := retractIfCan(r0)@Union(Z, "failed")
                    ru case "failed" => iterate
                    ri := ru@Z
                    nn := nf + monomial(ri::F, 1)$UP * denf
                    res1 := gammas1(nn, ri, denf, denf1, gexp, n2,
                                    vlst, ulst, k, x, res1)
                res1
            bs := new()$SY
            bk := kernel(bs)
            tx_plus_b := monomial(t::F, 1)$UP + bk::F::UP
            f_res1 := resultant(nnp, nf + tx_plus_b*denf)$UP
            lsol := solve_poly_eqs([f_res1, D(f_res1, bs)], ts, bs)
            for sol in lsol repeat
               ru := retractIfCan(sol.ratpart)@Union(Z, "failed")
               ru case "failed" => iterate
               ri := ru@Z
               nn := nf + monomial(ri::F, 1)$UP * denf
               res1 := gammas1(nn, ri, denf, denf1, gexp, n2,
                               vlst, ulst, k, x, res1)
            res1
        gammas1(nf, a, denf, denf1, gexp, n2, vlst, ulst, k, x, res1)

    get_prims : (SREC, UP, UP, Z, K) -> List(F)

    finish_special_integrate2(rr1f : F, csol : Vector(F), a1p : F,
                              as : Vector(F), scands : List(SREC),
                              denf : UP, gexp : Z, k : K) : PSOL2 ==
        cand_prims : List(F) := []
        ansp : F := 0
        for i in 1..#as repeat
            if empty?(cand_prims) then
                cand_prims := get_prims(first(scands), denf, denf, gexp, k)
                scands := rest(scands)
            a1p := a1p - csol(i)*as(i)
            ansp := ansp + csol(i)*first(cand_prims)
            cand_prims := rest(cand_prims)
        [a1p, rr1f, ansp, true]

    finish_special_integrate(rr1f : F, a1p : F, vs : Vector(F),
                             as : Vector(F), scands : List(SREC),
                             denf : UP, gexp : Z,
                             k : K, x : Symbol) : PSOL2 ==
        csolu := particularSolutionOverConstants(vs, rr1f,
                   [(c : F) : F +-> D(c, x)])$ConstantLinearDependence(R, F)
        csolu case "failed" => [0, 0, 0, false]
        csol := csolu@Vector(F)
        finish_special_integrate2(rr1f, csol, a1p, as, scands, denf,
                                  gexp, k)


    SHER ==> Record(answer : RF, specpart : RF)

    special_Hermite : (RF, UP, UP, UP, UP -> UP) -> SHER

    Param_Rec_QF ==> Record(logands : List F, basis : List Vector Q)

    exp_lower_bound(nfp : UP, eta : F, rr1 : RF, scoeffs : List(RF),
                    x : SY, lk : List(K)) : Z ==
        logi := (x2 : List K, x3 : List F) : Param_Rec_QF +->
                  logextint(x, x2, x3)$ParametricIntegration(R, F)
        b := nfp::LaurentPolynomial(F, UP)
        c_degs := [degree(denom(scoeff)) for scoeff in scoeffs]
        c_degs := cons(degree(denom(rr1)), c_degs)
        max_deg : Z := reduce(max, c_degs)
        min_deg : Z := minimumDegree(nfp)
        exp_lower_bound(1$UP, b, min_deg, -max_deg, lk, eta, logi
                       )$ParametricRischDE(R, F)

    RSOL ==> Record(ans : UP, remainder : UP)
    Param_Rec_F ==> Record(ratpart : F, coeffs : Vector F)
    L_Param_F ==> List Param_Rec_F

    special_integrate2(nfp : UP, denfp : UP, denf: UP, ndenf : UP, f : F,
                       k : K, lk : List K, x : Symbol, rr1 : RF, a1 : F,
                       r1 : F, g : F, scoeffs : List(RF),
                       scands : List(SREC), gexp : Z) : PSOL2 ==
        lg1 := [multivariate(scoeff, k) for scoeff in scoeffs]
        g1 := multivariate(rr1, k)
        partu := do_param_RDE(f, g1, lg1, x, cons(k, lk)
                             )$ElementaryRischDEX2(R, F)
        partu case "failed" => [0, 0, 0, false]
        part := partu@Param_Rec_F
        as := new(#scoeffs, 0)$Vector(F)
        finish_special_integrate2(g1, part.coeffs, part.ratpart, as,
                                  scands, denf, gexp, k)

    PSOL3 ==> Record(ans : F, defect : F, sol? : Boolean)

    do_spde1(rr1 : RF, nfp : UP, k : K, der : UP -> UP) : PSOL3 ==
        spec1u := retractIfCan(rr1)@Union(UP, "failed")
        spec1u case "failed" => error "impossible 7"
        spec1p := spec1u@UP
        res1 := SPDE1(nfp, spec1p, der)$RDEaux(F)
        a1 := res1.ans
        r2 := der(a1) + nfp*a1
        af := multivariate(a1::RF, k)
        def_f := multivariate(rr1 - r2::RF, k)
        [af, def_f, res1.remainder = 0]

    -- Case when denf = 1 and nf is not linear in a primitive
    special_integrate1(nfp : UP, f : F, k : K, lk : List(K), x : Symbol,
                       rr1 : RF, a1 : F,
                       r1 : F, g : F, ansp : F, scoeffs : List(RF),
                       scands : List(SREC), gexp : Z, derivation : UP -> UP
                       ) : PSOL2 ==
        neg_deg : Z :=
            is?(k, 'exp) =>
                eta := retract((derivation(monomial(1, 1)) exquo
                                monomial(1, 1))::UP)@F
                -exp_lower_bound(nfp, eta, rr1, scoeffs, x, lk)
            0
        nfp1 : UP := nfp
        inv_den1 : F := 1
        if neg_deg > 0 then
            mm := monomial(1, neg_deg::N)$UP
            scoeffs := [mm*scoeff for scoeff in scoeffs]
            rr1 := mm*rr1
            ak := argument(k)(1)
            nfp1 := nfp - ((neg_deg::F)*D(ak, x))::UP
            inv_den1 := multivariate(1/mm::RF, k)
        res2 := do_spde1(rr1, nfp1, k, derivation)
        a1p := inv_den1*res2.ans
        res2.sol? => [a1 + a1p, g, ansp, true]
        rr1f := res2.defect
        vs := new(#scoeffs, 0)$Vector(F)
        as := new(#scoeffs, 0)$Vector(F)
        for scoeff in scoeffs for i in 1..#scoeffs repeat
            res2 := do_spde1(scoeff, nfp1, k, derivation)
            as(i) := inv_den1*res2.ans
            vs(i) := res2.defect
        res3 := finish_special_integrate(rr1f, a1p, vs, as, scands, 1,
                                         gexp, k, x)
        not(res3.sol?) =>
            neg_deg > 0 => [a1, r1, ansp, false]
            [a1 + a1p, g - rr1f, ansp, false]
        [a1 + res3.ans, g, ansp + res3.primpart, true]

    get_scoeffs : (List(SREC), UP, K, RF -> RF) -> List(RF)

    MP ==> SparseMultivariatePolynomial(R, K)
    MULTFACT ==> MultivariateFactorize(K, Exp, R, MP)
    Exp ==> IndexedExponents(K)

    mk_pow1(u : F, i : Z) : F ==
        u^i

    mk_pow(uu : F, ulst : LF, v : Vector(Z)) : F ==
        res := mk_pow1(uu, v(1))
        lp := [mk_pow1(ui, v(i)) for ui in ulst for i in 2..]
        res2 := reduce(_*$F, lp, 1)
        res*res2

    quadratic_log_erf_integrate(nf : UP, nfp : UP, rr1 : RF,
          a1 : F, g : F, ansp : F, derivation : UP -> UP,
          vlst : LF, ulst : LF, k : K, lk : List(K), x : SY) : PSOL2 ==
        spec1u := retractIfCan(rr1)@Union(UP, "failed")
        spec1u case "failed" => error "impossible 10"
        spec1p := spec1u@UP
        res2 := SPDE1(nfp, spec1p, derivation)$RDEaux(F)
        a1 := a1 + multivariate(res2.ans::RF, k)
        (spec1p := res2.remainder) = 0 => return [a1, g, ansp, true]
        degree(spec1p) >= degree(nfp) => error "impossible 8"
        r1 := g - multivariate(spec1p::RF, k)
        r := leadingCoefficient(nf)
        s := coefficient(nf, 1)
        t := coefficient(nf, 0)
        f_denr := factor(denom(r))$MULTFACT
        f_numr := factor(numer(r))$MULTFACT
        fl1 := [fac.factor for fac in factorList(f_denr) |
                odd?(fac.exponent)]
        fl2 := [fac.factor for fac in factorList(f_numr) |
                odd?(fac.exponent)]
        pip := reduce(_*, fl1, 1) *$P reduce(_*, fl2, 1)
        pif := pip::F
        l_pifp := D(pif, x)/pif
        uvu0 := get_uv(l_pifp, vlst, ulst, k, x)
        uvu0 case "failed" => [a1, r1, ansp, false]
        uv0 := uvu0@UV_REC
        fl1 := [(fac.factor)^(((fac.exponent - 1) quo 2)::N)
                for fac in factorList(f_denr)]
        fl2 := [(fac.factor)^(((fac.exponent + 1) quo 2)::N)
                for fac in factorList(f_numr)]
        G_den := reduce(_*, fl1, 1)
        G_num := reduce(_*, fl2, 1)
        G := G_den::F / G_num::F
        -- u_r : P := unit(f_numr)
        unG := retract(unit(f_numr))@R
        kf := k::F
        phi : F :=
            D(r, x) ~= 0 =>
                piv := D(G, x) - G*l_pifp/(2::F)
                coefficient(spec1p, 1)/piv
            piv := G*D(kf, x) + unG*D(s, x)/(2::F*G)
            coefficient(spec1p, 0)/piv
        vlst1 : LF := []
        ulst1 : LF := []
        for v in vlst for u in ulst repeat
            if v ~= kf then
                vlst1 := cons(v, vlst1)
                ulst1 := cons(u, ulst1)
        r4 : F := 4::F*r
        c0 := s^2/r4 - t
        c1 := s/r4
        c2 : F := 1/r4
        lv : LF :=
            D(r, x) ~= 0 => concat([-c1, -c2]$LF, vlst1)
            cons(-c1, vlst1)
        m1 := matrix([[D(v, x) for v in lv]])$Matrix(F)
        v1 := vector([D(c0, x)])$Vector(F)
        (m2 : Matrix(Z), v2 : Vector(Z)) := reducedSystem(m1, v1)
        sol_rec := diophantineSystem(m2, v2)$SmithNormalForm(Z,
                      Vector(Z), Vector(Z), Matrix(Z))
        sol_rec.particular case "failed" => [a1, r1, ansp, false]
        sol0 := sol_rec.particular::Vector(Z)
        empty?(sol_rec.basis)$List(Vector(Z)) =>
            if D(r, x) ~= 0 then
                if sol0(1)^2 ~= sol0(2) then return [a1, r1, ansp, false]
            rcu := (retractIfCan(sol0(1)::Q/(2::Q))$Q)@Union(Z, "failed")
            rcu case "failed" => [a1, r1, ansp, false]
            -- now we have a single candidate
            ri := rcu@Z
            nn := nf + monomial(ri::F, 1)
            cands := gammas1(nn, rcu::Z, 1, 1, 0, 1, vlst, ulst, k, x, [])
            derivation3 : RF -> RF := z1 +->
               (derivation(numer(z1))*denom(z1) -
                   numer(z1)*derivation(denom(z1)))/(denom(z1)*denom(z1))
            scoeffs := get_scoeffs(cands, 1, k, derivation3)
            rr1 := spec1p::RF
            f := multivariate(nf::RF, k)
            special_integrate1(nfp, f, k, lk, x, rr1, a1, r1, g,
                       ansp, scoeffs, cands, 0, derivation)
        solb1 := first(sol_rec.basis)
        D(r, x) = 0 =>
            -- single line of solutions
            #(sol_rec.basis) > 1 => error "impossible 11"
            uu := first(argument(k))
            u0 := mk_pow(uu, ulst1, sol0)
            u1 := mk_pow(uu, ulst1, solb1)
            cis := solve_u(phi/u0, u1, x)$DenominatorIntegration(R, F)
            cis case "failed" => [a1, r1, ansp, false]
            cil := cis@List(List(F))
            cndl := []$List(SREC)
            for ci in cil repeat
                ii := retract(second(ci))@Z
                rr := sol0(1) + ii*solb1(1)
                cndl := gammas1(nf + monomial(rr::F, 1)$UP, rr, 1, 1, 0,
                                0, vlst, ulst, k, x, cndl)
            cndl := reverse!(cndl)
            vc := vector([first(ci) for ci in cil])$Vector(F)
            as := new(#cil, 0)$Vector(F)
            finish_special_integrate2(r1, vc, a1, as, cndl, 1, 0, k)
        #(sol_rec.basis) = 1 =>
            -- FIXME: Unimplemented, at most two solutions
            [a1, r1, ansp, false]
        #(sol_rec.basis) > 2 => error "impossible 12"
        -- FIXME: Need to call quadratic solver
        [a1, r1, ansp, false]

    decompose1(num : UP, den : UP, eidens : List(UP)) : List(UP) ==
        f1 := (den exquo reduce(_*, eidens))::UP
        if f1 ~= 1 then
            eidens := cons(f1, eidens)
        res := decompose(num, eidens)$PartialFractionUtilities(F, UP)
        f1 ~= 1 => rest(res)
        res

    get_trace(fu : RF, k : K, rf1k : K, cfac : UP) : RF ==
        Sae := SimpleAlgebraicExtension(F, UP, cfac)
        f := multivariate(fu, k)
        fa := univariate(f, rf1k, cfac)
        resd := trace(reduce(fa)$Sae)$Sae
        univariate(resd, k)

    EI_RES ==> Record(primpart : F, logpart : RF, polypart : UP,
                      specpart : RF)

    solve_factor1(nf : UP, denf : UP, nfp : UP, denfp : UP, k : K, x : SY,
                  einum : UP, bpol : UP, er : EI_REC) : EI_RES ==
        eiden := er.factor
        r0 := er.ratpart
        v0 := er.v_part
        cfac := er.alg_part
        u0 := er.u_part
        u_exp := er.exponent
        vshift := er.shift_part
        rfc : F := 0
        rf1k : K
        -- bpol ~= eiden => [0, 0, 0, 0]
        if cfac ~= 0 then
            rfc := rootOf(cfac)
            rf1k := retract(rfc)@K
        eiarg_num := nf + (monomial(r0::F, 1) + v0::UP + rfc::UP)*denf
        ei_arg : RF := eiarg_num::RF/denf::RF
        numfp1 := nfp + D(v0 + (r0::F)*(k::F), x)*denfp
        coeff1 := numfp1*denf*u0
        coeff1_r : RF := (monomial(1,1)$UP::RF)^u_exp*
                                 (coeff1::RF)/(eiarg_num::RF)
        denf1 := denom(coeff1_r)
        coeff1_rs : RF := 0
        dvr := divide(numer(coeff1_r), denf1)
        lnum1 := dvr.remainder
        if is?(k, 'exp) and (min_deg := minimumDegree(denf1)) > 0 then
            spec_den := monomial(1, min_deg)$UP
            denf1 := (denf1 exquo spec_den)::UP
            auxl := decompose(lnum1, [denf1, spec_den]
                             )$PartialFractionUtilities(F, UP)
            lnum1 := auxl(1)
            coeff1_rs := auxl(2)::RF/spec_den::RF
        lnum0 := lnum1
        if bpol ~= eiden then
            reml := decompose1(lnum1, denf1, [bpol])
            lnum1 := first(reml)
        if cfac ~= 0 then
            einum := einum rem denf1
            ddf := differentiate(eiden) rem denf1
            ppu := extendedEuclidean(denf1, ddf, einum)
            ppu case "failed" => error "impossible 4"
            einum := (ppu@Record(coef1 : UP, coef2 : UP)).coef2
            einum := (einum*differentiate(denf1)) rem denf1
        cu := einum exquo lnum1
        cu case "failed" => [0, 0, 0, 0]
        cp := cu@UP
        cu1 := retractIfCan(cp)@Union(F, "failed")
        cu1 case "failed" => [0, 0, 0, 0]
        c := cu1@F
        differentiate(c, x) ~= 0 => [0, 0, 0, 0]
        ansp1 := c*exp(-vshift - rfc)*Ei(multivariate(ei_arg, k))
        zzl : List(F) :=
            cfac = 0 => [0]
            zerosOf(cfac)
        ansp : F := 0
        if cfac = 0 then
            ansp := ansp1
        else
            for zz in zzl repeat
                ansp := ansp + eval(ansp1, rf1k, zz)
        lp_cor : RF := c::UP*(lnum0::RF/denf1::RF)
        sp_cor := c::UP*coeff1_rs
        poly_cor := c::UP*dvr.quotient
        if cfac ~= 0 then
            lp_cor := get_trace(lp_cor, k, rf1k, cfac)
            sp_cor := get_trace(sp_cor, k, rf1k, cfac)
            poly_cor_rf := get_trace(poly_cor::RF, k, rf1k, cfac)
            poly_cor := retract(poly_cor_rf)@UP
        [ansp, lp_cor, poly_cor, sp_cor]

    p_power_in_q(q : UP, p : UP) : Z ==
        nn : Z := 0
        repeat
            nqu := q exquo p
            nqu case "failed" => break
            q := nqu@UP
            nn := nn + 1
        nn

    solve_linear_factor(nf : UP, denf : UP, nfp : UP, denfp : UP,
                        k : K, x : SY,
                        einum : UP, er : EI_REC2) : EI_RES ==
        cfac := er.alg_part
        rfc : F := 0
        rf1k : K
        if cfac ~= 0 then
            rfc := rootOf(cfac)
            rf1k := retract(rfc)@K
        r0 := er.ratpart
        v0 := er.v_part
        u0 := er.u_part
        lc0 := leadingCoefficient(nf) + r0::F
        -- XXX using lc0 only makes sense if deg(nf) = 1 and denf = 1
        -- fortunately, in other cases cfac = 0 and we need lc0
        -- only to handle nonzero cfac
        (degree(nf) ~= 1 or denf ~= 1) and cfac ~= 0 =>
            error "cfac ~= 0 in nonlinear case"
        eiarg_num := nf + (monomial(r0::F, 1) + (v0 + lc0*rfc)::UP)*denf
        fac1 := er.factor
        nn0 : Z :=
            degree(eiarg_num) > 1 => p_power_in_q(eiarg_num, fac1)
            1
        nn0 = 0 =>
            error "impossible, eiarg_num is not divisible by its factor"
        numfp1 := nfp + D(v0 + (r0::F)*(k::F), x)*denfp
        coeff1 := numfp1*denf*u0
        coeff1_r : RF := (coeff1::RF)/(eiarg_num::RF)
        denf1 := denom(coeff1_r)
        dvr := divide(numer(coeff1_r), denf1)
        if cfac ~= 0 then
            einum := einum rem denf1
            ddf := differentiate(er.factor) rem denf1
            ppu := extendedEuclidean(denf1, ddf, einum)
            ppu case "failed" => error "impossible 4"
            einum := (ppu@Record(coef1 : UP, coef2 : UP)).coef2
            einum := (einum*differentiate(denf1)) rem denf1
        lnum1 : F :=
            degree(fac1) > 1 => retract(dvr.remainder)@F
            reml := decompose1(dvr.remainder, denf1, [fac1])
            retract(first(reml))@F
        einum1 := retract(einum)@F
        u1 := er.u_part2
        res1u := solve_u(einum1/lnum1, u1, x)$DenominatorIntegration(R, F)
        res1u case "failed" => [0, 0, 0, 0]
        res1 := res1u@List(List(F))
        ansp1 : F := 0
        lp_cor : RF := 0
        eiarg_inc := (monomial((er.ratpart2)::F, 1)$UP + (er.v_part2)::UP)
                     *denf
        zzl : List(F) :=
            cfac = 0 => [0]
            zerosOf(cfac)
        for ck in res1 repeat
            ci := ck(1)
            kif := ck(2)
            ki := retract(kif)@Z
            einumi :=
                cfac = 0 => eiarg_num + kif*eiarg_inc
                ((lc0 + kif)/lc0)*eiarg_num
            nn : Z :=
                 degree(einumi) > 1 => p_power_in_q(einumi, fac1)
                 1
            if nn = 0 then
                    error "impossible, einumi not divisible by its factor"
            ci := (nn0::F)*ci/(nn::F)
            eiargi_r := (einumi::RF)/(denf::RF)
            eiargi :=  multivariate(eiargi_r, k)
            shifti :=
                cfac = 0 => er.shift_part + kif*er.shift_part2
                ((lc0 + kif)/lc0)*(er.shift_part + lc0*rfc)
            ansp1 := ansp1 + ci*exp(-shifti)*Ei(eiargi)
            coefi := ci*u0*u1^ki
            eiargp : RF := denfp*univariate(D(eiargi, x), k)
            lp_cor := lp_cor + (coefi::UP::RF)*eiargp/eiargi_r
        ansp : F := 0
        if cfac = 0 then
            ansp := ansp1
        else
            lp_cor := get_trace(lp_cor, k, rf1k, cfac)
            for zz in zzl repeat
                ansp := ansp + eval(ansp1, rf1k, zz)
        dvr2 := divide(numer(lp_cor), denom(lp_cor))
        [ansp, dvr2.remainder::RF/denom(lp_cor)::RF, dvr2.quotient, 0]

    linear_log_case(num : UP, den : UP, nf : UP, nfp : UP, k : K,
                    vlst : LF, ulst : LF, x : SY) : EI_RES ==
        al := lin_args(den, nf, k, vlst, ulst, x)
        empty?(al) => [0, 0, 0, 0]
        eidens := [er.factor for er in al]
        einums := decompose1(num, den, eidens)
        ansp : F := 0
        lp_cor : RF := 0
        sp_cor : RF := 0
        poly_cor : UP := 0
        for einum in einums for er in al repeat
            ar1 :=
                er.has_part2? =>
                    solve_linear_factor(nf, 1, nfp, 1, k, x, einum, er)
                solve_factor1(nf, 1, nfp, 1, k, x, einum, er.factor,
                     [er.factor, er.ratpart, er.v_part, er.alg_part,
                        er.u_part, er.exponent, er.shift_part]$EI_REC)
            ansp := ansp + ar1.primpart
            lp_cor := lp_cor + ar1.logpart
            sp_cor := sp_cor + ar1.specpart
            poly_cor := poly_cor + ar1.polypart
        [ansp, lp_cor, poly_cor, sp_cor]

    handle_nonlinear_log(num : UP, den : UP, nf : UP, denf : UP, nfp : UP,
                         denfp : UP, k : K, x : SY, vlst : LF, ulst : LF,
                         lei : List(EI_REC), dbasis : Vector(UP),
                         dtrans : Matrix(Integer)) : EI_RES ==
        n := #lei
        m := #dbasis
        bpols : List(UP) := []
        lei1 : List(EI_REC) := []
        j0 : N
        for i in 1..m repeat
            esum : Z := 0
            for j in 1..n repeat
                if dtrans(i, j) = 1 then
                    esum := esum + 1
                    esum = 2 => break
                    j0 := j
            if esum = 1 then
                bpols := cons(dbasis(i), bpols)
                lei1 := cons(lei(j0), lei1)
        ansp : F := 0
        lp_cor : RF := 0
        sp_cor : RF := 0
        poly_cor : UP := 0
        not(empty?(bpols)) =>
            einums := decompose1(num, den, bpols)
            for einum in einums for bpol in bpols for er in lei1 repeat
                ar1 := solve_factor1(nf, denf, nfp, denfp, k, x, einum,
                                 bpol, er)
                ansp := ansp + ar1.primpart
                lp_cor := lp_cor + ar1.logpart
                sp_cor := sp_cor + ar1.specpart
                poly_cor := poly_cor + ar1.polypart
            [ansp, lp_cor, poly_cor, sp_cor]
        bpol := dbasis(1)
        degree(bpol) ~= 1 => error "degree(bpol) ~= 1"
        for j in 1..n for er in lei repeat
            if dtrans(1, j) = 1 then
                lei1 := cons(er, lei1)
        er1 := first(lei1)
        r0 := er1.ratpart
        lei := rest(lei1)
        ril := [er.ratpart - r0 for er in lei]
        r1 := gcd(ril)
        v0 := er1.v_part
        v1 := first(lei).v_part - v0
        v1 := (r1::F/(first(lei).ratpart - r0)::F)*v1
        uvu := get_uv(v1, vlst, ulst, k, x)
        uvu case "failed" => error "impossible, get_uv failed"
        uv := uvu@UV_REC
        u1 := uv.u_part*(argument(k)(1))^r1
        er2 := [bpol, r0, v0, 0, er1.u_part, 0, er1.shift_part,
                true, r1, v1, u1, uv.v_part]$EI_REC2
        einums := decompose1(num, den, [bpol])
        ar1 := solve_linear_factor(nf, denf, nfp, denfp, k, x,
                                   first(einums), er2)
        ar1

    handle_logpart(num : UP, den : UP, nf : UP, denf : UP,
                   nfp : UP, denfp : UP, k : K,
                   vlst : LF, ulst : LF, x : SY) : EI_RES ==
        is?(k, 'log) and denf = 1 and degree(nf) <=$Z 1 =>
            linear_log_case(num, den, nf, nfp, k, vlst, ulst, x)
        denf = 1 and degree(nf) = 0 => [0, 0, 0, 0]
        lei := ei_args(den, nf, denf, k, vlst, ulst, x)
        empty?(lei) => [0, 0, 0, 0]
        eidens := [er.factor for er in lei]
        ansp : F := 0
        lp_cor : RF := 0
        sp_cor : RF := 0
        poly_cor : UP := 0
        is?(k, 'log) =>
            dr := gcdDecomposition(vector(eidens)$Vector(UP)
                                  )$GcdBasis(UP)
            dbasis := dr.basis
            dtrans := dr.transform
            lp := num::RF/den::RF
            repeat
                ar1 := handle_nonlinear_log(num, den, nf, denf, nfp, denfp,
                          k, x, vlst, ulst, lei, dbasis, dtrans)
                ansp := ansp + ar1.primpart
                lp_cor := lp_cor + ar1.logpart
                sp_cor := sp_cor + ar1.specpart
                poly_cor := poly_cor + ar1.polypart
                ar1.logpart = 0 => break
                lp := lp - ar1.logpart
                lp = 0 => break
                num := numer(lp)
                den := denom(lp)
                ndbl : List(UP) := []
                nidbl : List(Z) := []
                m := #dbasis
                for i in 1..m repeat
                    bi := dbasis(i)
                    if (den exquo bi) case UP then
                        ndbl := cons(bi, ndbl)
                        nidbl := cons(i, nidbl)
                #ndbl = m =>
                    error "no progress in handle_nonlinear_log"
                ndbl := reverse!(ndbl)
                nidbl := reverse!(nidbl)
                neidens : List(UP) := []
                ninds : List(Z) := []
                lei1 : List(EI_REC) := []
                n := #eidens
                for i in 1..n for eiden in eidens for er in lei repeat
                    if (den exquo eiden) case UP then
                        neidens := cons(eiden, neidens)
                        ninds := cons(i, ninds)
                        lei1 := cons(er, lei1)
                eidens := reverse!(neidens)
                ninds := reverse!(ninds)
                lei := reverse!(lei1)
                dbasis := vector(ndbl)$Vector(UP)
                dtrans := dtrans(nidbl, ninds)
            [ansp, lp_cor, poly_cor, sp_cor]
        einums := decompose1(num, den, eidens)
        for einum in einums for er in lei repeat
            ar1 := solve_factor1(nf, denf, nfp, denfp, k, x, einum,
                                 er.factor, er)
            ansp := ansp + ar1.primpart
            lp_cor := lp_cor + ar1.logpart
            sp_cor := sp_cor + ar1.specpart
            poly_cor := poly_cor + ar1.polypart
        [ansp, lp_cor, poly_cor, sp_cor]

    gamma_denominator(denf : UP, k : K
                     ) : Record(radicand : UP, exponent : Z)  ==
        s_deg : Z := 0
        denf1 := denf
        if is?(k, 'exp) then
            s_deg := minimumDegree(denf)
            denf1 := (denf exquo monomial(1, s_deg::N)$UP)::UP
        denf1 = 1 =>
            s_deg = 0 => [1, 0]
            [monomial(1, 1)$UP, s_deg]
        fnf := squareFree(denf1)
        nffacs := factorList(fnf)
        lexp : List Z := [fac1.exponent for fac1 in nffacs]
        l1 := gcd(cons(s_deg, lexp))
        l1 = 1 => [denf, 1]
        gden1 := reduce(_*, [my_exp(fac1.factor, (fac1.exponent quo l1)::N)
                              for fac1 in nffacs]$List(UP))
        if s_deg > 0 then
            gden1 := monomial(1, (s_deg quo l1)::N)$UP*gden1
        [gden1, l1]

    -- Remove normal part of denominator of r via integration
    -- by parts, leaving only special denominator
    -- normal prime factors of denom(r) must divide ndenf
    special_Hermite(r : RF, nfp : UP, ndenf : UP, denfp : UP,
                   deriv : UP -> UP) : SHER ==
        ad := denr := denom(r)
        numr := numer(r)
        an : UP := 0
        mult : UP := 1
        repeat
            hh1 := gcd(denr, ndenf)
            degree(hh1) = 0 => return [denfp*an/ad, numr/denr]
            ee := extendedEuclidean(nfp, hh1, numr
                                   )::Record(coef1 : UP, coef2 : UP)
            nn := ee.coef1 rem hh1
            an := an + mult*nn
            nfp1 := nfp - ((denfp*deriv(denr)) exquo denr)::UP
            numr := numr - nfp1*nn - deriv(denfp*nn)
            numr := (numr exquo hh1)::UP
            denr := (denr exquo hh1)::UP
            mult := mult*hh1

    ei_int_log(f : F, ng : UP, x : SY, k : K) : PSOL2 ==
        ngu := multivariate(ng::RF, k)
        lk := varselect(union(tower(f), tower(ngu)), x)
        lk := [k1 for k1 in lk | k1 ~= k]
        kf := k::F
        dk := D(kf, x)
        r1 : F := 0
        is_sol := true
        rh : F := 0
        am1 : F := 0
        for m in degree(ng)..1 by -1 repeat
            gm := - ((m+1)::F)*dk*am1
            if m = degree(ng) then
                gm := gm + leadingCoefficient(ng)
                ng := reductum(ng)
            res1 := do_risch_DE(f, gm, x, lk)$ElementaryRischDEX2(R, F)
            is_sol := is_sol and res1.sol?
            am1 := res1.ans
            kfm := kf^m
            r1 := r1 + kfm*am1
            rh := rh + kfm*res1.right + (m::F)*dk*am1
        gm := retract(ng)@F - dk*am1
        res2 := ei_int(1, f, gm, x)
        r1 := r1 + res2.ans
        rh := rh + res2.right
        is_sol := is_sol and res2.sol?
        [r1, rh, res2.primpart, is_sol]

    ei_int_exp(f : F, ng : UP, degs : N, x : SY, k : K) : PSOL2 ==
        ak := argument(k)(1)
        kf := k::F
        r1 : F := 0
        ansp : F := 0
        is_sol := true
        rh : F := 0
        while ng ~= 0 repeat
            m := degree(ng) - degs
            gm := leadingCoefficient(ng)
            ng := reductum(ng)
            f1 := f + (m::F)*ak
            res1 := ei_int(1, f1, gm, x)
            kfm := kf^m
            r1 := r1 + kfm*res1.ans
            ansp := ansp + res1.primpart
            rh := rh + kfm*res1.right
            is_sol := is_sol and res1.sol?
        [r1, rh, ansp, is_sol]

    get_scoeff(scand : SREC, denf : UP, k : K,
               der : RF -> RF, res : List(RF)) : List(RF) ==
        r0 := scand.ratpart
        cfac := scand.alg_part
        u0 := scand.u_part
        nf1 := scand.new_f
        if r0 ~= 0 then
            ak := argument(k)(1)
            u0 := u0*ak^r0
        u_exp := scand.exponent
        (d := scand.degree) = 0 =>
            nfp := der(nf1)
            coeff1 := u0::UP*nfp
            cons(coeff1, res)
        d = 1 =>
            nfp := der(nf1)
            scand.coeff ~= 1 or cfac ~= 0 => error "impossible coeff"
            coeff1 := u0::UP*nfp/nf1
            coeff1_r := (monomial(1,1)$UP::RF)^u_exp*coeff1
            cons(coeff1_r, res)
        d > 1 =>
            rf1k : K
            if cfac ~= 0 then
                rfc := rootOf(cfac)
                rf1k := retract(rfc)@K
            l1 := (d - 1)::N
            nf2 := scand.radicand
            gt : RF := der(nf2)*u0::UP*(monomial(1,1)$UP::RF)^u_exp
            for i in 1..l1 repeat
                gt1 :=
                    cfac = 0 => gt
                    get_trace(gt, k, rf1k, cfac)
                res := cons(gt1, res)
                gt := gt*nf2
            res
        error "unimplemented"

    get_scoeffs(scands : List(SREC), denf : UP,
                k : K, der : RF -> RF) : List(RF) ==
        res : List(RF) := []
        for scand in scands repeat
            res := get_scoeff(scand, denf, k, der, res)
        reverse!(res)

    mk_erf1(uc : F, ff : F) : F ==
        uc1 := sqrt(uc)
        sqrt(pi()$F)*erf(uc1*ff)/uc1

    CI ==> Complex(Integer)

    if R is CI then

        is_imaginary?(c : F) : Boolean ==
            nc := numer(c)
            dc := denom(c)
            nl := coefficients(nc) pretend List(CI)
            dl := coefficients(dc) pretend List(CI)
            every?((c1 : CI) : Boolean +-> imag(c1) = 0, dl)
              and every?((c1 : CI) : Boolean +-> real(c1) = 0, nl)

        mk_erf(uc : F, ff : F) : F ==
            not(is_imaginary?(uc)) => mk_erf1(uc, ff)
            im := (complex(0, 1)$CI pretend R)::F
            uc1 := -(2::F)*im*uc/pi()$F
            s : F := 1
            if smaller?(uc1, -uc1) then
                s := -1
                uc1 := -uc1
            uc2 := sqrt(uc1)
            (2::F)*(fresnelC(uc2*ff)-im*s*fresnelS(uc2*ff))/uc2

    else

        mk_erf(uc : F, ff : F) : F == mk_erf1(uc, ff)

    get_prims(scand : SREC, denf : UP, denf1 : UP, gexp : Z,
              k : K) : List(F) ==
        cfac := scand.alg_part
        vshift := scand.shift_part
        uc := scand.coeff
        nf : RF := scand.new_f
        f := multivariate(nf, k)
        (d := scand.degree) = 0 =>
            a := scand.a_val
            [exp(-vshift - (a - 1)*log(scand.coeff))*Gamma(a, f)]
        d = 1 => [exp(-vshift)*Ei(f)]
        d > 1 =>
            rfc : F := 0
            rf1k : K
            if cfac ~= 0 then
                rfc := rootOf(cfac)
                rf1k := retract(rfc)@K
            zzl : List(F) :=
                cfac = 0 => [0]
                zerosOf(cfac)
            l1 := (d - 1)::N
            lf := d::F
            res : List F := []
            nf2 := scand.radicand
            for j in 1..l1 repeat
                j1 := j::F/lf
                c1s := exp(-vshift)/lf
                f1 :=
                    2*j = d =>
                        ff := multivariate(nf2::RF, k)
                        c1s*mk_erf(-uc, ff^j)
                    -c1s*(-uc)^(-j1)*Gamma(j1, -f)
                if cfac ~= 0 then
                    f11 := f1
                    f1 := 0
                    for zz in zzl repeat
                        f1 := f1 + exp(-zz)*eval(f11, rf1k, zz)
                res := cons(f1, res)
            reverse!(res)
        error "unimplemented"

    ei_int_diff(f : F, g : F, gu : RF, x : SY, k : K, lk : LK) : PSOL2 ==
        fu := univariate(f, k)
        ngu := numer(gu)
        denom(gu) ~= 1 or degree(ngu) ~= 1 or fu ~= f::UP::RF =>
            [0, 0, 0, false]
        g1 := leadingCoefficient(ngu)
        args := argument(k)
        #args ~= 3 => error "internal error, k is not a diff"
        k1 := eval(args(1), retract(args(2))@K, args(3))
        dv := new()$Symbol
        dvf := dv::F
        f2 := eval(f, k1, dvf)
        g2 := eval(g1, k1, dvf)
        nres := ei_int(1, f2, g2, dv)
        not(nres.sol?) => [0, 0, 0, false]
        kdv := kernel(dv)
        k1f := k1::F
        a1 := eval(nres.ans, kdv, k1f)
        p1 := eval(nres.primpart, kdv, k1f)
        dp1 : F :=
            p1 = 0 => 0
            differentiate(p1, x)/exp(f)
        r1 := differentiate(a1, x) + a1*differentiate(f, x) + dp1
        g3 := g - r1
        g3 = 0 => [a1, g, p1, true]
        g3u := univariate(g3, k)
        g3u ~= g3::UP::RF =>
            print(message("Failed to differentiate nres in ei_int_diff"))
            [0, 0, 0, false]
        nres2 := ei_int(1, f, g3, x)
        [nres2.ans + a1, nres2.right + r1, nres2.primpart + p1, nres2.sol?]

    OPDIFF ==> '%diff

    ei_int(n : Z, f : F, g : F, x : SY) : PSOL2 ==
        lk := union(varselect(union(tower(f), tower(g)), x), [kernel(x)])
        f := n*f
        (fp := D(f, x)) = 0 =>
            do_risch_DE(f, g, x, lk)$ElementaryRischDEX2(R, F)
        k := top_kernel(f, g, x)
        lk := [kk for kk in lk | kk ~= k]
        has?(operator k, ALGOP)$BasicOperator =>
            ei_int_alg(f, g, x, k, lk)
        fpu := univariate(fp, k)
        gu := univariate(g, k)
        is?(k, OPDIFF) => ei_int_diff(f, g, gu, x, k, lk)
        dku := univariate(differentiate(k::F, x), k)
        denom(dku) ~= 1 => [0, 0, 0, false]
        dk := numer(dku)
        denfp := denom(fpu)
        nfp := numer(fpu)
        derivation : UP -> UP := z1 +-> denfp*differentiate(z1,
               (z2 : F) : F +-> differentiate(z2, x), dk)
        her := HermiteIntegrate(denfp*gu, derivation,
                nfp)$TranscendentalHermiteIntegration(F, UP)
        lp := her.logpart
        a1 := multivariate(her.answer, k)
        rr1 := lp + (her.polypart)::RF + her.specpart
        rr1 := rr1/(denfp::RF)
        r1 := g - multivariate(rr1, k)
        rr1 = 0 => [a1, g, 0, true]
        lden := denom(lp)
        lnum := numer(lp)
        fu := univariate(f, k)
        denf := denom(fu)
        nf := numer(fu)
        denf = 1 and degree(nf) = 0 and is?(k, 'exp) =>
            lp ~= 0 => [a1, r1, 0, false]
            ds := denom(her.specpart)
            reductum(ds) ~= 0 or leadingCoefficient(ds) ~= 1 =>
                error "unexpected special part"
            degs := degree(ds)
            ng := monomial(1, degs)$UP*her.polypart + numer(her.specpart)
            res5 := ei_int_exp(f, ng, degs, x, k)
            a1 := a1 + res5.ans
            r1 := r1 + res5.right
            [a1, r1, res5.primpart, res5.sol?]
        lk1 := [kk for kk in cons(k, lk) | is?(kk, 'log) or is?(kk, 'exp)]
        vlst := [(is?(kk, 'log) => kk::F; argument(kk).1) for kk in lk1]
        ulst := [(is?(kk, 'log) => argument(kk).1; kk::F) for kk in lk1]
        ar1 := handle_logpart(lnum, lden, nf, denf, nfp, denfp, k,
                              vlst, ulst, x)
        ansp := ar1.primpart
        lp := lp - ar1.logpart
        her.specpart := her.specpart - ar1.specpart
        her.polypart := her.polypart - ar1.polypart
        lp1u := retractIfCan(lp)@Union(UP, "failed")
        lp1u case "failed" => [a1, r1, 0, false]
        her.polypart := her.polypart + lp1u@UP
        her.polypart = 0 and her.specpart = 0 => [a1, g, ansp, true]
        denf = 1 and degree(nf) = 0 and (is?(k, 'log)
          or has?(operator k, PRIM)) =>
            lp ~= 0 => [a1, r1, 0, false]
            her.specpart ~= 0 => error "unexpected special part"
            res5 := ei_int_log(f, her.polypart, x, k)
            a1 := a1 + res5.ans
            r1 := r1 + res5.right
            [a1, r1, ansp + res5.primpart, res5.sol?]
        rr1 := her.polypart::RF + her.specpart
        rr1 := rr1/(denfp::RF)
        (gden1, gexp) := gamma_denominator(denf, k)
        derivation2 : UP -> UP := z1 +-> differentiate(z1,
               (z2 : F) : F +-> differentiate(z2, x), dk)
        denf_split := split(denf, derivation2)$MonomialExtensionTools(F, UP)
        ndenf := denf_split.normal
        her2 := special_Hermite(rr1, nfp, ndenf, denfp, derivation2)
        a1 := a1 + multivariate(her2.answer, k)
        rr1 := her2.specpart
        rr1 = 0 => [a1, g, ansp, true]
        denf = 1 and degree(nf) = 2 and is?(k, 'log) =>
            quadratic_log_erf_integrate(nf, nfp, rr1, a1, g, ansp,
                                        derivation, vlst, ulst, k, lk, x)
        scands := special_candidates(nf, denf, gden1, gexp, vlst, ulst, k, x)
        derivation3 : RF -> RF := z1 +->
            (derivation2(numer(z1))*denom(z1) -
                numer(z1)*derivation2(denom(z1)))/(denom(z1)*denom(z1))
        scoeffs := get_scoeffs(scands, denf, k, derivation3)
        rr1f := multivariate(rr1, k)
        r1 := g - rr1f
        log_recursion := false
        if denf = 1 and is?(k, 'log) and degree(nf) = 1 then
            lc := leadingCoefficient(nf)
            rcu1 := retractIfCan(lc)@Union(Q, "failed")
            log_recursion := rcu1 case Q
        not(log_recursion) =>
            res3 := special_integrate2(nfp, denfp, denf, ndenf, f, k, lk, x,
                      rr1, a1, r1, g, scoeffs, scands, gexp)
            [a1 + res3.ans, r1 + res3.right, ansp + res3.primpart, res3.sol?]
        if log_recursion then
            spec1u := retractIfCan(rr1)@Union(UP, "failed")
            spec1u case "failed" => error "impossible 9"
            spec1p := spec1u@UP
            d := degree(spec1p)
            aa : F := 0
            kf := k::F
            dkf := retract(dk)@F
            ff : F
            for j in d..0 by -1 repeat
                gj : F := 0
                if j = degree(spec1p) then
                    gj := leadingCoefficient(spec1p)
                    spec1p := reductum(spec1p)
                ff := gj - (j + 1)::F*dkf*aa
                j = 0 => break
                res7 := do_risch_DE(f, ff, x, cons(k, lk)
                                   )$ElementaryRischDEX2(R, F)
                aa := res7.ans
                a1 := a1 + aa*kf^j
                r1 := r1 + res7.right*kf^j
                not(res7.sol?) =>
                    return [a1, r1, ansp, res7.sol?]
            ff = 0 => return [a1, r1, ansp, true]
            rr1f := ff
            lc := leadingCoefficient(nf)
            (rcu1 := retractIfCan(lc)@Union(Q, "failed")) case "failed" =>
                "skip"
            rcq := rcu1@Q
            (rcu2 := retractIfCan(rcq)@Union(Z, "failed")) case "failed" =>
                "skip"
            rc := rcu2@Z
            u := (argument(k)(1))^rc
            f1 := coefficient(nf, 0)
            res4 := ei_int(1, f1, rr1f*u, x)
            a1 := a1 + res4.ans/u
            ansp := ansp + res4.primpart
            r1 := r1 + res4.right/u
            return [a1, r1, ansp, res4.sol?]
        r1 = g => [a1, g, ansp, true]
        res6 := do_risch_DE(f, rr1f, x, cons(k, lk))$ElementaryRischDEX2(R, F)
        [a1 + res6.ans, r1 + res6.right, ansp, res6.sol?]
)boot $tryRecompileArguments := true
