<commit_before>"""Holonomic Functions and Differential Operators"""

from __future__ import print_function, division

from sympy import symbols, Symbol, diff, S, Mul
from sympy.polys.polytools import lcm, gcd
from sympy.core.expr import Expr
from sympy.printing import sstr
from sympy.matrices import Matrix
from sympy.core.compatibility import range
from sympy.polys.polytools import DMP
from sympy.core.function import Function


def DiffOperatorAlgebra(base, generator):
    """ A function to create a Differential Operator Algebra.
    The first arguments need to be the base polynomial ring for the algebra
    and the second argument must be a generator.
    Returns the class representing the operator algebra and
    the standard operator for differentiation i.e. the `Dx` operator.

    Examples
    =======
    >>> from sympy.polys.domains import ZZ
    >>> from sympy import symbols
    >>> from sympy.functions.special.holonomic import DiffOperatorAlgebra
    >>> x = symbols('x')
    >>> R, Dx = DiffOperatorAlgebra(ZZ.old_poly_ring(x), 'Dx')
    """

    ring = DifferentialOperatorAlgebra(base, generator)
    return (ring, ring.derivative_operator)


class DifferentialOperatorAlgebra:
    """ The class representing Differential Operator Algebra.
    Defined by the base polynomial ring and the generator.

    The attribute derivative_operator is the operator `Dx` which when acted
    on a function does differentiation.
    """

    def __init__(self, base, generator):
        # the base ring for the algebra
        self.base = base
        # the operator representing differentiation i.e. `Dx`
        self.derivative_operator = DifferentialOperator(
            [base.zero, base.one], self)

        if generator is None:
            self.gen_symbol = symbols('Dx', commutative=False)
        else:
            if isinstance(generator, str):
                self.gen_symbol = symbols(generator, commutative=False)
            elif isinstance(generator, Symbol):
                self.gen_symbol = generator

    def __str__(self):

        string = 'Univariate Differential Operator Algebra in intermediate '\
            + sstr(self.gen_symbol) + ' over the base ring ' + \
            (self.base).__str__()

        return string

    __repr__ = __str__


class DifferentialOperator(Expr):
    """
    The operator can be created by providing a list of polynomials for each power of Dx
    and the parent ring which must be an instance of DifferentialOperatorAlgebra.

    An Operator can also be created easily using the operator `Dx`. See examples below.

    Examples
    ========

    >>> from sympy.functions.special.holonomic import DifferentialOperator, DiffOperatorAlgebra
    >>> from sympy.polys.domains import ZZ, QQ
    >>> from sympy import symbols
    >>> x = symbols('x')
    >>> R, Dx = DiffOperatorAlgebra(ZZ.old_poly_ring(x),'Dx')

    >>> DifferentialOperator([0,1,x**2], R)
    (1)Dx + (x**2)Dx**2

    One can use the `Dx` and represent differential operators more conveniently this way also.
    >>> (x*Dx*x + 1 - Dx**2)**2
    (2*x**2 + 2*x + 1) + (4*x**3 + 2*x**2 - 4)Dx + (x**4 - 6*x - 2)Dx**2 + (-2*x**2)Dx**3 + (1)Dx**4

    """

    _op_priority = 30

    def __init__(self, list_of_poly, parent):
        # the parent ring for this operator
        # must be an DifferentialOperatorAlgebra object
        self.parent = parent
        # sequence of polynomials in x for each power of Dx
        # represents the operator
        # convert the expressions into ring elements using from_sympy
        if isinstance(list_of_poly, list):
            for i, j in enumerate(list_of_poly):
                if isinstance(j, int):
                    list_of_poly[i] = (
                        (self.parent).base).from_sympy(S(j))
                elif not isinstance(j, self.parent.base.dtype):
                    list_of_poly[i] = (
                        (self.parent).base).from_sympy(j)

            self.listofpoly = list_of_poly
        self.order = len(self.listofpoly) - 1

    def args(self):

        return self.gen_symbol

    def __mul__(self, other):
        """
        Multiplies two DifferentialOperator and returns another
        DifferentialOperator isinstance using the commutation rule
        Dx*a = a*Dx + a'
        """

        if isinstance(other, DifferentialOperator):
            # if other is `Dx`
            if other.listofpoly == other.parent.derivative_operator.listofpoly:
                sol = []
                sol.append(other.parent.base.zero)
                for i in self.listofpoly:
                    sol.append(i)

                return DifferentialOperator(sol, self.parent)

        gen = self.parent.derivative_operator
        listofpoly = self.listofpoly
        for i, j in enumerate(listofpoly):
            if isinstance(j, self.parent.base.dtype):
                listofpoly[i] = self.parent.base.to_sympy(j)
        sol = (listofpoly[0] * other)
        # using the commutation rule gen*b = b*gen + b'

        def _diff_n_times(b):
            expr = b * gen + b.diff()
            return expr

        for i in range(1, len(listofpoly)):
            other = _diff_n_times(other)
            sol += (listofpoly[i] * other)<commit_msg>Using only polynomials in DifferentialOperator.__mul__<commit_after>"""Holonomic Functions and Differential Operators"""

from __future__ import print_function, division

from sympy import symbols, Symbol, diff, S, Mul
from sympy.polys.polytools import lcm, gcd
from sympy.core.expr import Expr
from sympy.printing import sstr
from sympy.matrices import Matrix
from sympy.core.compatibility import range
from sympy.polys.polytools import DMP
from sympy.core.function import Function


def DiffOperatorAlgebra(base, generator):
    """ A function to create a Differential Operator Algebra.
    The first arguments need to be the base polynomial ring for the algebra
    and the second argument must be a generator.
    Returns the class representing the operator algebra and
    the standard operator for differentiation i.e. the `Dx` operator.

    Examples
    =======
    >>> from sympy.polys.domains import ZZ
    >>> from sympy import symbols
    >>> from sympy.functions.special.holonomic import DiffOperatorAlgebra
    >>> x = symbols('x')
    >>> R, Dx = DiffOperatorAlgebra(ZZ.old_poly_ring(x), 'Dx')
    """

    ring = DifferentialOperatorAlgebra(base, generator)
    return (ring, ring.derivative_operator)


class DifferentialOperatorAlgebra:
    """ The class representing Differential Operator Algebra.
    Defined by the base polynomial ring and the generator.

    The attribute derivative_operator is the operator `Dx` which when acted
    on a function does differentiation.
    """

    def __init__(self, base, generator):
        # the base ring for the algebra
        self.base = base
        # the operator representing differentiation i.e. `Dx`
        self.derivative_operator = DifferentialOperator(
            [base.zero, base.one], self)

        if generator is None:
            self.gen_symbol = symbols('Dx', commutative=False)
        else:
            if isinstance(generator, str):
                self.gen_symbol = symbols(generator, commutative=False)
            elif isinstance(generator, Symbol):
                self.gen_symbol = generator

    def __str__(self):

        string = 'Univariate Differential Operator Algebra in intermediate '\
            + sstr(self.gen_symbol) + ' over the base ring ' + \
            (self.base).__str__()

        return string

    __repr__ = __str__


class DifferentialOperator(Expr):
    """
    The operator can be created by providing a list of polynomials for each power of Dx
    and the parent ring which must be an instance of DifferentialOperatorAlgebra.

    An Operator can also be created easily using the operator `Dx`. See examples below.

    Examples
    ========

    >>> from sympy.functions.special.holonomic import DifferentialOperator, DiffOperatorAlgebra
    >>> from sympy.polys.domains import ZZ, QQ
    >>> from sympy import symbols
    >>> x = symbols('x')
    >>> R, Dx = DiffOperatorAlgebra(ZZ.old_poly_ring(x),'Dx')

    >>> DifferentialOperator([0,1,x**2], R)
    (1)Dx + (x**2)Dx**2

    One can use the `Dx` and represent differential operators more conveniently this way also.
    >>> (x*Dx*x + 1 - Dx**2)**2
    (2*x**2 + 2*x + 1) + (4*x**3 + 2*x**2 - 4)Dx + (x**4 - 6*x - 2)Dx**2 + (-2*x**2)Dx**3 + (1)Dx**4

    """

    _op_priority = 30

    def __init__(self, list_of_poly, parent):
        # the parent ring for this operator
        # must be an DifferentialOperatorAlgebra object
        self.parent = parent
        # sequence of polynomials in x for each power of Dx
        # represents the operator
        # convert the expressions into ring elements using from_sympy
        if isinstance(list_of_poly, list):
            for i, j in enumerate(list_of_poly):
                if isinstance(j, int):
                    list_of_poly[i] = (
                        (self.parent).base).from_sympy(S(j))
                elif not isinstance(j, self.parent.base.dtype):
                    list_of_poly[i] = (
                        (self.parent).base).from_sympy(j)

            self.listofpoly = list_of_poly
        self.order = len(self.listofpoly) - 1

    def args(self):

        return self.gen_symbol

    def __mul__(self, other):
        """
        Multiplies two DifferentialOperator and returns another
        DifferentialOperator isinstance using the commutation rule
        Dx*a = a*Dx + a'
        """

        if isinstance(other, DifferentialOperator):
            # if other is `Dx`
            if other.listofpoly == other.parent.derivative_operator.listofpoly:
                sol = []
                sol.append(other.parent.base.zero)
                for i in self.listofpoly:
                    sol.append(i)

                return DifferentialOperator(sol, self.parent)

        gen = self.parent.derivative_operator
        listofpoly = self.listofpoly

        def _mul_dmp_diffop(self, other):

            if isinstance(other, DifferentialOperator):
                sol = []
                for i in other.listofpoly:
                    sol.append(i*self)
                return DifferentialOperator(sol, other.parent)
            else:
                return self*other

        sol = _mul_dmp_diffop(listofpoly[0], other)
        # using the commutation rule gen*b = b*gen + b'

        def _diff_n_times(b):
            expr = b * gen + b.diff()
            return expr

        for i in range(1, len(listofpoly)):
            other = _diff_n_times(other)
            sol += _mul_dmp_diffop(listofpoly[i], other)
{'fraction_non_alphanumeric': 0.09286914317715139, 'fraction_numerical': 0.005973492626470039, 'mean_word_length': 2.4200446856048514, 'pattern_counts': {'":': 0, '<': 3, '<?xml version=': 0, '>': 75, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '3852303', 'n_tokens_mistral': 3164, 'n_tokens_neox': 2920, 'n_words': 925}