(fa, fd, DE, case='auto', z=None)
return (common_denom, u)
Checks if f can be written as the logarithmic derivative of a k(t)-radical.

    f in k(t) can be written as the logarithmic derivative of a k(t) radical if
    there exist n in ZZ and u in k(t) with n, u != 0 such that n*f == Du/u.
    Either returns (n, u) or None, which means that f cannot be written as the
    logarithmic derivative of a k(t)-radical.

    case is one of {'primitive', 'exp', 'tan', 'auto'} for the primitive,
    hyperexponential, and hypertangent cases, respectively.  If case is 'auto',
    it will attempt to determine the type of the derivation automatically.
Checks if f can be written as the logarithmic derivative of a k(t)-radical.
Checks if f can be written as the logarithmic derivative of a k ( t ) - radical .
def is_log_deriv_k_t_radical_in_field(fa, fd, DE, case='auto', z=None):
    """
    Checks if f can be written as the logarithmic derivative of a k(t)-radical.

    f in k(t) can be written as the logarithmic derivative of a k(t) radical if
    there exist n in ZZ and u in k(t) with n, u != 0 such that n*f == Du/u.
    Either returns (n, u) or None, which means that f cannot be written as the
    logarithmic derivative of a k(t)-radical.

    case is one of {'primitive', 'exp', 'tan', 'auto'} for the primitive,
    hyperexponential, and hypertangent cases, respectively.  If case is 'auto',
    it will attempt to determine the type of the derivation automatically.
    """
    fa, fd = fa.cancel(fd, include=True)

    # f must be simple
    n, s = splitfactor(fd, DE)
    if not s.is_one:
        pass
        #return None

    z = z or Dummy('z')
    H, b = residue_reduce(fa, fd, DE, z=z)
    if not b:
        # I will have to verify, but I believe that the answer should be
        # None in this case. This should never happen for the
        # functions given when solving the parametric logarithmic
        # derivative problem when integration elementary functions (see
        # Bronstein's book, page 255), so most likely this indicates a bug.
        return None

    roots = [(i, i.real_roots()) for i, _ in H]
    if not all(len(j) == i.degree() and all(k.is_Rational for k in j) for
               i, j in roots):
        # If f is the logarithmic derivative of a k(t)-radical, then all the
        # roots of the resultant must be rational numbers.
        return None

    # [(a, i), ...], where i*log(a) is a term in the log-part of the integral
    # of f
    respolys, residues = list(zip(*roots)) or [[], []]
    # Note: this might be empty, but everything below should work find in that
    # case (it should be the same as if it were [[1, 1]])
    residueterms = [(H[j][1].subs(z, i), i) for j in xrange(len(H)) for
        i in residues[j]]

    # TODO: finish writing this and write tests

    p = cancel(fa.as_expr()/fd.as_expr() - residue_reduce_derivation(H, DE, z))

    p = p.as_poly(DE.t)
    if p is None:
        # f - Dg will be in k[t] if f is the logarithmic derivative of a k(t)-radical
        return None

    if p.degree(DE.t) >= max(1, DE.d.degree(DE.t)):
        return None

    if case == 'auto':
        case = DE.case

    if case == 'exp':
        wa, wd = derivation(DE.t, DE).cancel(Poly(DE.t, DE.t), include=True)
        with DecrementLevel(DE):
            pa, pd = frac_in(p, DE.t, cancel=True)
            wa, wd = frac_in((wa, wd), DE.t)
            A = parametric_log_deriv(pa, pd, wa, wd, DE)
        if A is None:
            return None
        n, e, u = A
        u *= DE.t**e
#        raise NotImplementedError("The hyperexponential case is "
#            "not yet completely implemented for is_log_deriv_k_t_radical_in_field().")

    elif case == 'primitive':
        with DecrementLevel(DE):
            pa, pd = frac_in(p, DE.t)
            A = is_log_deriv_k_t_radical_in_field(pa, pd, DE, case='auto')
        if A is None:
            return None
        n, u = A

    elif case == 'base':
        # TODO: we can use more efficient residue reduction from ratint()
        if not fd.is_sqf or fa.degree() >= fd.degree():
            # f is the logarithmic derivative in the base case if and only if
            # f = fa/fd, fd is square-free, deg(fa) < deg(fd), and
            # gcd(fa, fd) == 1.  The last condition is handled by cancel() above.
            return None
        # Note: if residueterms = [], returns (1, 1)
        # f had better be 0 in that case.
        n = reduce(ilcm, [i.as_numer_denom()[1] for _, i in residueterms], S(1))
        u = Mul(*[Pow(i, j*n) for i, j in residueterms])
        return (n, u)

    elif case == 'tan':
        raise NotImplementedError("The hypertangent case is "
        "not yet implemented for is_log_deriv_k_t_radical_in_field()")

    elif case in ['other_linear', 'other_nonlinear']:
        # XXX: If these are supported by the structure theorems, change to NotImplementedError.
        raise ValueError("The %s case is not supported in this function." % case)

    else:
        raise ValueError("case must be one of {'primitive', 'exp', 'tan', "
        "'base', 'auto'}, not %s" % case)

    common_denom = reduce(ilcm, [i.as_numer_denom()[1] for i in [j for _, j in
        residueterms]] + [n], S(1))
    residueterms = [(i, j*common_denom) for i, j in residueterms]
    m = common_denom//n
    if common_denom != n*m:  # Verify exact division
        raise ValueError("Inexact division")
    u = cancel(u**m*Mul(*[Pow(i, j) for i, j in residueterms]))

    return (common_denom, u)
def is_log_deriv_k_t_radical_in_field ( fa , fd , DE , case = 'auto' , z = None ) : fa , fd = fa . cancel ( fd , include = True ) # f must be simple n , s = splitfactor ( fd , DE ) if not s . is_one : pass #return None z = z or Dummy ( 'z' ) H , b = residue_reduce ( fa , fd , DE , z = z ) if not b : # I will have to verify, but I believe that the answer should be # None in this case. This should never happen for the # functions given when solving the parametric logarithmic # derivative problem when integration elementary functions (see # Bronstein's book, page 255), so most likely this indicates a bug. return None roots = [ ( i , i . real_roots ( ) ) for i , _ in H ] if not all ( len ( j ) == i . degree ( ) and all ( k . is_Rational for k in j ) for i , j in roots ) : # If f is the logarithmic derivative of a k(t)-radical, then all the # roots of the resultant must be rational numbers. return None # [(a, i), ...], where i*log(a) is a term in the log-part of the integral # of f respolys , residues = list ( zip ( * roots ) ) or [ [ ] , [ ] ] # Note: this might be empty, but everything below should work find in that # case (it should be the same as if it were [[1, 1]]) residueterms = [ ( H [ j ] [ 1 ] . subs ( z , i ) , i ) for j in xrange ( len ( H ) ) for i in residues [ j ] ] # TODO: finish writing this and write tests p = cancel ( fa . as_expr ( ) / fd . as_expr ( ) - residue_reduce_derivation ( H , DE , z ) ) p = p . as_poly ( DE . t ) if p is None : # f - Dg will be in k[t] if f is the logarithmic derivative of a k(t)-radical return None if p . degree ( DE . t ) >= max ( 1 , DE . d . degree ( DE . t ) ) : return None if case == 'auto' : case = DE . case if case == 'exp' : wa , wd = derivation ( DE . t , DE ) . cancel ( Poly ( DE . t , DE . t ) , include = True ) with DecrementLevel ( DE ) : pa , pd = frac_in ( p , DE . t , cancel = True ) wa , wd = frac_in ( ( wa , wd ) , DE . t ) A = parametric_log_deriv ( pa , pd , wa , wd , DE ) if A is None : return None n , e , u = A u *= DE . t ** e #        raise NotImplementedError("The hyperexponential case is " #            "not yet completely implemented for is_log_deriv_k_t_radical_in_field().") elif case == 'primitive' : with DecrementLevel ( DE ) : pa , pd = frac_in ( p , DE . t ) A = is_log_deriv_k_t_radical_in_field ( pa , pd , DE , case = 'auto' ) if A is None : return None n , u = A elif case == 'base' : # TODO: we can use more efficient residue reduction from ratint() if not fd . is_sqf or fa . degree ( ) >= fd . degree ( ) : # f is the logarithmic derivative in the base case if and only if # f = fa/fd, fd is square-free, deg(fa) < deg(fd), and # gcd(fa, fd) == 1.  The last condition is handled by cancel() above. return None # Note: if residueterms = [], returns (1, 1) # f had better be 0 in that case. n = reduce ( ilcm , [ i . as_numer_denom ( ) [ 1 ] for _ , i in residueterms ] , S ( 1 ) ) u = Mul ( * [ Pow ( i , j * n ) for i , j in residueterms ] ) return ( n , u ) elif case == 'tan' : raise NotImplementedError ( "The hypertangent case is " "not yet implemented for is_log_deriv_k_t_radical_in_field()" ) elif case in [ 'other_linear' , 'other_nonlinear' ] : # XXX: If these are supported by the structure theorems, change to NotImplementedError. raise ValueError ( "The %s case is not supported in this function." % case ) else : raise ValueError ( "case must be one of {'primitive', 'exp', 'tan', " "'base', 'auto'}, not %s" % case ) common_denom = reduce ( ilcm , [ i . as_numer_denom ( ) [ 1 ] for i in [ j for _ , j in residueterms ] ] + [ n ] , S ( 1 ) ) residueterms = [ ( i , j * common_denom ) for i , j in residueterms ] m = common_denom // n if common_denom != n * m : # Verify exact division raise ValueError ( "Inexact division" ) u = cancel ( u ** m * Mul ( * [ Pow ( i , j ) for i , j in residueterms ] ) ) return ( common_denom , u )