(eq, func, order, match,
        returns='sol')
r"""
    Solves an `n`\th order linear homogeneous differential equation with
    constant coefficients.

    This is an equation of the form

    .. math:: a_n f^{(n)}(x) + a_{n-1} f^{(n-1)}(x) + \cdots + a_1 f'(x)
                + a_0 f(x) = 0\text{.}

    These equations can be solved in a general manner, by taking the roots of
    the characteristic equation `a_n m^n + a_{n-1} m^{n-1} + \cdots + a_1 m +
    a_0 = 0`.  The solution will then be the sum of `C_n x^i e^{r x}` terms,
    for each where `C_n` is an arbitrary constant, `r` is a root of the
    characteristic equation and `i` is one of each from 0 to the multiplicity
    of the root - 1 (for example, a root 3 of multiplicity 2 would create the
    terms `C_1 e^{3 x} + C_2 x e^{3 x}`).  The exponential is usually expanded
    for complex roots using Euler's equation `e{I x} = \cos(x) + I \sin(x)`.
    Complex roots always come in conjugate pairs in polynomials with real
    coefficients, so the two roots will be represented (after simplifying the
    constants) as `e^{a x} \left(C_1 \cos(b x) + C_2 \sin(b x)\right)`.

    If SymPy cannot find exact roots to the characteristic equation, a
    :py:class:`~sympy.polys.rootoftools.RootOf` instance will be return
    instead.

    >>> from sympy import Function, dsolve, Eq
    >>> from sympy.abc import x
    >>> f = Function('f')
    >>> dsolve(f(x).diff(x, 5) + 10*f(x).diff(x) - 2*f(x), f(x),
    ... hint='nth_linear_constant_coeff_homogeneous')
    ... # doctest: +NORMALIZE_WHITESPACE
    f(x) == C1*exp(x*RootOf(_x**5 + 10*_x - 2, 0)) +
    C2*exp(x*RootOf(_x**5 + 10*_x - 2, 1)) +
    C3*exp(x*RootOf(_x**5 + 10*_x - 2, 2)) +
    C4*exp(x*RootOf(_x**5 + 10*_x - 2, 3)) +
    C5*exp(x*RootOf(_x**5 + 10*_x - 2, 4))

    Note that because this method does not involve integration, there is no
    ``nth_linear_constant_coeff_homogeneous_Integral`` hint.

    The following is for internal use:

    - ``returns = 'sol'`` returns the solution to the ODE.
    - ``returns = 'list'`` returns a list of linearly independent solutions,
      for use with non homogeneous solution methods like variation of
      parameters and undetermined coefficients.  Note that, though the
      solutions should be linearly independent, this function does not
      explicitly check that.  You can do ``assert simplify(wronskian(sollist))
      != 0`` to check for linear independence.  Also, ``assert len(sollist) ==
      order`` will need to pass.
    - ``returns = 'both'``, return a dictionary ``{'sol': <solution to ODE>,
      'list': <list of linearly independent solutions>}``.

    Examples
    ========

    >>> from sympy import Function, dsolve, pprint
    >>> from sympy.abc import x
    >>> f = Function('f')
    >>> pprint(dsolve(f(x).diff(x, 4) + 2*f(x).diff(x, 3) -
    ... 2*f(x).diff(x, 2) - 6*f(x).diff(x) + 5*f(x), f(x),
    ... hint='nth_linear_constant_coeff_homogeneous'))
                        x                            -2*x
    f(x) = (C1 + C2*x)*e  + (C3*sin(x) + C4*cos(x))*e

    References
    ==========

    - http://en.wikipedia.org/wiki/Linear_differential_equation section:
      Nonhomogeneous_equation_with_constant_coefficients
    - M. Tenenbaum & H. Pollard, "Ordinary Differential Equations",
      Dover 1963, pp. 211

    # indirect doctest
r"""
    Solves an `n`\th order linear homogeneous differential equation with
    constant coefficients.
r Solves an n \ th order linear homogeneous differential equation with constant coefficients .
def ode_nth_linear_constant_coeff_homogeneous(eq, func, order, match,
        returns='sol'):
    r"""
    Solves an `n`\th order linear homogeneous differential equation with
    constant coefficients.

    This is an equation of the form

    .. math:: a_n f^{(n)}(x) + a_{n-1} f^{(n-1)}(x) + \cdots + a_1 f'(x)
                + a_0 f(x) = 0\text{.}

    These equations can be solved in a general manner, by taking the roots of
    the characteristic equation `a_n m^n + a_{n-1} m^{n-1} + \cdots + a_1 m +
    a_0 = 0`.  The solution will then be the sum of `C_n x^i e^{r x}` terms,
    for each where `C_n` is an arbitrary constant, `r` is a root of the
    characteristic equation and `i` is one of each from 0 to the multiplicity
    of the root - 1 (for example, a root 3 of multiplicity 2 would create the
    terms `C_1 e^{3 x} + C_2 x e^{3 x}`).  The exponential is usually expanded
    for complex roots using Euler's equation `e{I x} = \cos(x) + I \sin(x)`.
    Complex roots always come in conjugate pairs in polynomials with real
    coefficients, so the two roots will be represented (after simplifying the
    constants) as `e^{a x} \left(C_1 \cos(b x) + C_2 \sin(b x)\right)`.

    If SymPy cannot find exact roots to the characteristic equation, a
    :py:class:`~sympy.polys.rootoftools.RootOf` instance will be return
    instead.

    >>> from sympy import Function, dsolve, Eq
    >>> from sympy.abc import x
    >>> f = Function('f')
    >>> dsolve(f(x).diff(x, 5) + 10*f(x).diff(x) - 2*f(x), f(x),
    ... hint='nth_linear_constant_coeff_homogeneous')
    ... # doctest: +NORMALIZE_WHITESPACE
    f(x) == C1*exp(x*RootOf(_x**5 + 10*_x - 2, 0)) +
    C2*exp(x*RootOf(_x**5 + 10*_x - 2, 1)) +
    C3*exp(x*RootOf(_x**5 + 10*_x - 2, 2)) +
    C4*exp(x*RootOf(_x**5 + 10*_x - 2, 3)) +
    C5*exp(x*RootOf(_x**5 + 10*_x - 2, 4))

    Note that because this method does not involve integration, there is no
    ``nth_linear_constant_coeff_homogeneous_Integral`` hint.

    The following is for internal use:

    - ``returns = 'sol'`` returns the solution to the ODE.
    - ``returns = 'list'`` returns a list of linearly independent solutions,
      for use with non homogeneous solution methods like variation of
      parameters and undetermined coefficients.  Note that, though the
      solutions should be linearly independent, this function does not
      explicitly check that.  You can do ``assert simplify(wronskian(sollist))
      != 0`` to check for linear independence.  Also, ``assert len(sollist) ==
      order`` will need to pass.
    - ``returns = 'both'``, return a dictionary ``{'sol': <solution to ODE>,
      'list': <list of linearly independent solutions>}``.

    Examples
    ========

    >>> from sympy import Function, dsolve, pprint
    >>> from sympy.abc import x
    >>> f = Function('f')
    >>> pprint(dsolve(f(x).diff(x, 4) + 2*f(x).diff(x, 3) -
    ... 2*f(x).diff(x, 2) - 6*f(x).diff(x) + 5*f(x), f(x),
    ... hint='nth_linear_constant_coeff_homogeneous'))
                        x                            -2*x
    f(x) = (C1 + C2*x)*e  + (C3*sin(x) + C4*cos(x))*e

    References
    ==========

    - http://en.wikipedia.org/wiki/Linear_differential_equation section:
      Nonhomogeneous_equation_with_constant_coefficients
    - M. Tenenbaum & H. Pollard, "Ordinary Differential Equations",
      Dover 1963, pp. 211

    # indirect doctest

    """
    x = func.args[0]
    f = func.func
    r = match

    # First, set up characteristic equation.
    chareq, symbol = S.Zero, Dummy('x')

    for i in r.keys():
        if type(i) == str or i < 0:
            pass
        else:
            chareq += r[i]*symbol**i

    chareq = Poly(chareq, symbol)
    chareqroots = [RootOf(chareq, k) for k in range(chareq.degree())]
    chareq_is_complex = not all([i.is_real for i in chareq.all_coeffs()])

    # A generator of constants
    constants = list(get_numbered_constants(eq, num=chareq.degree()*2))

    # Create a dict root: multiplicity or charroots
    charroots = defaultdict(int)
    for root in chareqroots:
        charroots[root] += 1
    gsol = S(0)
    # We need to keep track of terms so we can run collect() at the end.
    # This is necessary for constantsimp to work properly.
    global collectterms
    collectterms = []
    gensols = []
    conjugate_roots = [] # used to prevent double-use of conjugate roots
    for root, multiplicity in charroots.items():
        for i in range(multiplicity):
            if isinstance(root, RootOf):
                gensols.append(exp(root*x))
                if multiplicity != 1:
                    raise ValueError("Value should be 1")
                # This ordering is important
                collectterms = [(0, root, 0)] + collectterms
            else:
                if chareq_is_complex:
                    gensols.append(x**i*exp(root*x))
                    collectterms = [(i, root, 0)] + collectterms
                    continue
                reroot = re(root)
                imroot = im(root)
                if imroot.has(atan2) and reroot.has(atan2):
                    # Remove this condition when re and im stop returning
                    # circular atan2 usages.
                    gensols.append(x**i*exp(root*x))
                    collectterms = [(i, root, 0)] + collectterms
                else:
                    if root in conjugate_roots:
                        collectterms = [(i, reroot, imroot)] + collectterms
                        continue
                    if imroot == 0:
                        gensols.append(x**i*exp(reroot*x))
                        collectterms = [(i, reroot, 0)] + collectterms
                        continue
                    conjugate_roots.append(conjugate(root))
                    gensols.append(x**i*exp(reroot*x) * sin(abs(imroot) * x))
                    gensols.append(x**i*exp(reroot*x) * cos(    imroot  * x))

                    # This ordering is important
                    collectterms = [(i, reroot, imroot)] + collectterms
    if returns == 'list':
        return gensols
    elif returns in ('sol' 'both'):
        gsol = Add(*[i*j for (i,j) in zip(constants, gensols)])
        if returns == 'sol':
            return Eq(f(x), gsol)
        else:
            return {'sol': Eq(f(x), gsol), 'list': gensols}
    else:
        raise ValueError('Unknown value for key "returns".')
def ode_nth_linear_constant_coeff_homogeneous ( eq , func , order , match , returns = 'sol' ) : x = func . args [ 0 ] f = func . func r = match # First, set up characteristic equation. chareq , symbol = S . Zero , Dummy ( 'x' ) for i in r . keys ( ) : if type ( i ) == str or i < 0 : pass else : chareq += r [ i ] * symbol ** i chareq = Poly ( chareq , symbol ) chareqroots = [ RootOf ( chareq , k ) for k in range ( chareq . degree ( ) ) ] chareq_is_complex = not all ( [ i . is_real for i in chareq . all_coeffs ( ) ] ) # A generator of constants constants = list ( get_numbered_constants ( eq , num = chareq . degree ( ) * 2 ) ) # Create a dict root: multiplicity or charroots charroots = defaultdict ( int ) for root in chareqroots : charroots [ root ] += 1 gsol = S ( 0 ) # We need to keep track of terms so we can run collect() at the end. # This is necessary for constantsimp to work properly. global collectterms collectterms = [ ] gensols = [ ] conjugate_roots = [ ] # used to prevent double-use of conjugate roots for root , multiplicity in charroots . items ( ) : for i in range ( multiplicity ) : if isinstance ( root , RootOf ) : gensols . append ( exp ( root * x ) ) if multiplicity != 1 : raise ValueError ( "Value should be 1" ) # This ordering is important collectterms = [ ( 0 , root , 0 ) ] + collectterms else : if chareq_is_complex : gensols . append ( x ** i * exp ( root * x ) ) collectterms = [ ( i , root , 0 ) ] + collectterms continue reroot = re ( root ) imroot = im ( root ) if imroot . has ( atan2 ) and reroot . has ( atan2 ) : # Remove this condition when re and im stop returning # circular atan2 usages. gensols . append ( x ** i * exp ( root * x ) ) collectterms = [ ( i , root , 0 ) ] + collectterms else : if root in conjugate_roots : collectterms = [ ( i , reroot , imroot ) ] + collectterms continue if imroot == 0 : gensols . append ( x ** i * exp ( reroot * x ) ) collectterms = [ ( i , reroot , 0 ) ] + collectterms continue conjugate_roots . append ( conjugate ( root ) ) gensols . append ( x ** i * exp ( reroot * x ) * sin ( abs ( imroot ) * x ) ) gensols . append ( x ** i * exp ( reroot * x ) * cos ( imroot * x ) ) # This ordering is important collectterms = [ ( i , reroot , imroot ) ] + collectterms if returns == 'list' : return gensols elif returns in ( 'sol' 'both' ) : gsol = Add ( * [ i * j for ( i , j ) in zip ( constants , gensols ) ] ) if returns == 'sol' : return Eq ( f ( x ) , gsol ) else : return { 'sol' : Eq ( f ( x ) , gsol ) , 'list' : gensols } else : raise ValueError ( 'Unknown value for key "returns".' )