{"action":"created","comment":{"url":"https://api.github.com/repos/sympy/sympy/pulls/comments/31821690","id":31821690,"diff_hunk":"@@ -0,0 +1,1123 @@\n+from __future__ import print_function, division\n+\n+from sympy.core.expr import Expr\n+from sympy.core.singleton import S, Singleton\n+from sympy.core.symbol import Dummy\n+from sympy.core.function import Lambda\n+from sympy.core.add import Add\n+from sympy.core.mul import Mul\n+from sympy.core.compatibility import (range, integer_types, with_metaclass,\\\n+                                      is_sequence, iterable, ordered)\n+from sympy.core.cache import cacheit\n+from sympy.core.sympify import sympify\n+from sympy.core.containers import Tuple\n+from sympy.core.evaluate import global_evaluate\n+from sympy.functions.elementary.integers import ceiling\n+from sympy.functions.elementary.miscellaneous import Min, Max\n+from sympy.polys import lcm\n+from sympy.sets.sets import Interval, Set, Intersection\n+from sympy.utilities.iterables import flatten\n+\n+\n+################################################################################\n+#                            SEQUENCES                                         #\n+################################################################################\n+\n+\n+class SeqBase(Expr):\n+    \"\"\"Base class for sequences\"\"\"\n+\n+    @staticmethod\n+    def _start_key(expr):\n+        \"\"\"\n+        Return start (if possible) else S.Infinity.\n+        adapted from Set._infimum_key\n+        \"\"\"\n+        try:\n+            start = expr.start\n+        except (NotImplementedError,\n+                AttributeError, ValueError):\n+            start = S.Infinity\n+        return start\n+\n+    def _intersect_interval(self, other):\n+        \"\"\"\n+        returns the start, stop and step\n+        takes intersection over the two intervals\n+        \"\"\"\n+        interval = Intersection(self.interval, other.interval)\n+        step = Max(self.step, other.step)\n+        return (interval, step)\n+\n+    @property\n+    def gen(self):\n+        \"\"\"Returns the generator for the sequence\"\"\"\n+        raise NotImplementedError(\"(%s).gen\" % self)\n+\n+    @property\n+    def interval(self):\n+        \"\"\"The interval on which the sequence is defined\"\"\"\n+        raise NotImplementedError(\"(%s).interval\" % self)\n+\n+    @property\n+    def start(self):\n+        \"\"\"The starting point of the sequence. This point is included\"\"\"\n+        raise NotImplementedError(\"(%s).start\" % self)\n+\n+    @property\n+    def stop(self):\n+        \"\"\"The ending point of the sequence. This point is included\"\"\"\n+        raise NotImplementedError(\"(%s).stop\" % self)\n+\n+    @property\n+    def step(self):\n+        \"\"\"Increase points by step\"\"\"\n+        raise NotImplementedError(\"(%s).step\" % self)\n+\n+    @property\n+    def length(self):\n+        \"\"\"Length of the sequence\"\"\"\n+        raise NotImplementedError(\"(%s).length\" % self)\n+\n+    @property\n+    def variables(self):\n+        \"\"\"Returns a tuple of variables that are bounded\"\"\"\n+        return ()\n+\n+    @property\n+    def free_symbols(self):\n+        \"\"\"\n+        This method returns the symbols in the object, excluding those\n+        that take on a specific value (i.e. the dummy symbols).\n+\n+        Examples\n+        ========\n+\n+        >>> from sympy import SeqFormula\n+        >>> from sympy.abc import n, m\n+        >>> SeqFormula((m*n**2, n), (0, 5)).free_symbols\n+        set([m])\n+        \"\"\"\n+        fsyms = set().union(*[a.free_symbols for a in self.args])\n+        for d in self.variables:\n+            if d in fsyms:\n+                fsyms.remove(d)\n+        return fsyms\n+\n+    @cacheit\n+    def coeff(self, pt):\n+        \"\"\"Returns the coefficient at point pt\"\"\"\n+        if pt < self.start or pt > self.stop:\n+            raise IndexError(\"Index %s out of bounds %s\" %(pt, self.interval))\n+        return self._eval_coeff(pt)\n+\n+    def _eval_coeff(self, pt):\n+        raise NotImplementedError(\"The _eval_coeff method should be added to\"\n+                                  \" %s to return coefficient so it is available\"\n+                                  \" when coeff calls it.\"\n+                                  % self.func)\n+\n+    def _ith_point(self, i, step=None):\n+        \"\"\"\n+        Returns the i'th point of a sequence\n+        If start point is negative infinity, point is returned from the end.\n+        Assumes the first point to be indexed zero.\n+\n+        Examples\n+        =========\n+\n+        >>> from sympy import oo\n+        >>> from sympy.series.sequences import SeqExpr\n+\n+        bounded\n+\n+        >>> SeqExpr((1, 2, 3), (-10, 10))._ith_point(0)\n+        -10\n+        >>> SeqExpr((1, 2, 3), (-10, 10))._ith_point(5)\n+        -5\n+        >>> SeqExpr((1, 2, 3), (-10, 10, 2))._ith_point(5)\n+        0\n+\n+        End is at infinity\n+\n+        >>> SeqExpr((1, 2, 3), (0, oo))._ith_point(5)\n+        5\n+        >>> SeqExpr((1, 2, 3), (0, oo, 2))._ith_point(5)\n+        10\n+\n+        Starts at negative infinity\n+\n+        >>> SeqExpr((1, 2, 3), (-oo, 0))._ith_point(5)\n+        -5\n+        >>> SeqExpr((1, 2, 3), (-oo, 0, 2))._ith_point(5)\n+        -10\n+        \"\"\"\n+        if self.start is S.NegativeInfinity:\n+            initial = self.stop\n+        else:\n+            initial = self.start\n+\n+        if step is None:\n+            if self.start is S.NegativeInfinity:\n+                step = -self.step\n+            else:\n+                step = self.step\n+\n+        return initial + i*step\n+\n+    def add(self, other):\n+        \"\"\"\n+        Returns the term-wise addition of 'self' and 'other'.\n+        ``other`` should be a sequence.\n+\n+        Examples\n+        ========\n+\n+        As a shortcut it is possible to use the '+' operator:\n+\n+        >>> from sympy import S, oo, SeqFormula\n+        >>> from sympy.abc import n\n+        >>> SeqFormula(n**2).add(SeqFormula(n**3))\n+        SeqFormula((n**3 + n**2, n), ([0, oo), 1))\n+        \"\"\"\n+        return SeqAdd(self, other)\n+\n+    def sub(self, other):\n+        \"\"\"\n+        Returns the term-wise subtraction of 'self' and 'other'.\n+        ``other`` should be a sequence.\n+\n+        Examples\n+        ========\n+\n+        As a shortcut it is possible to use the '-' operator:\n+\n+        >>> from sympy import S, oo, SeqFormula\n+        >>> from sympy.abc import n\n+        >>> SeqFormula(n**2).sub(SeqFormula(n))\n+        SeqFormula((n**2 - n, n), ([0, oo), 1))\n+        \"\"\"\n+        return self.add(-other)\n+\n+    def mul(self, other):\n+        \"\"\"\n+        Returns the term-wise multiplication of 'self' and 'other'.\n+        ``other`` should be a sequence. For ``other`` not being a\n+        sequence see ``coeff_mul`` method.\n+\n+        Examples\n+        ========\n+\n+        As a shortcut it is possible to use the '*' operator:\n+\n+        >>> from sympy import S, oo, SeqFormula\n+        >>> from sympy.abc import n\n+\n+        >>> SeqFormula(n**2).mul(SeqFormula(n))\n+        SeqFormula((n**3, n), ([0, oo), 1))\n+        \"\"\"\n+        if isinstance(other, SeqBase):\n+            return SeqMul(self, other)\n+\n+    def coeff_mul(self, other):\n+        \"\"\"\n+        Should be used when ``other`` is not a sequence. Should be\n+        defined to define custom behaviour.\n+\n+        Examples\n+        ========\n+\n+        >>> from sympy import S, oo, SeqFormula\n+        >>> from sympy.abc import n\n+\n+        >>> SeqFormula(n**2).coeff_mul(2)\n+        SeqFormula((2*n**2, n), ([0, oo), 1))\n+\n+        Notes\n+        =====\n+\n+        '*' defines multiplication of sequences with sequences only.\n+        For multiplying sequences use ``mul`` method.\n+        \"\"\"\n+        return Mul(self, coeff)\n+\n+    def _add(self, other):\n+        \"\"\"\n+        Should only be used internally\n+\n+        self._add(other) returns a new, term-wise added sequence if self\n+        knows how to add with other, otherwise it returns ``None``.\n+\n+        ``other`` should only be a sequence object.\n+\n+        Used within :class:`SeqAdd` class\n+        \"\"\"\n+        return None\n+\n+    def _mul(self, other):\n+        \"\"\"\n+        Should only be used internally\n+\n+        self._mul(other) returns a new, term-wise multiplied sequence if self\n+        knows how to multiply with other, otherwise it returns ``None``.\n+\n+        ``other`` should only be a sequence object.\n+\n+        Used within :class:`SeqMul` class\n+        \"\"\"\n+        return None\n+\n+    def __add__(self, other):\n+        return self.add(other)\n+\n+    def __neg__(self):\n+        return self.coeff_mul(-1)\n+\n+    def __sub__(self, other):\n+        return self.sub(other)\n+\n+    def __mul__(self, other):\n+        return self.mul(other)\n+\n+    def __iter__(self):\n+        i = 0\n+        while(i < self.length):\n+            pt = self._ith_point(i)\n+            yield self.coeff(pt)\n+            i += 1\n+\n+    def __getitem__(self, index):\n+        if isinstance(index, integer_types):\n+            index = self._ith_point(index)\n+            return self.coeff(index)\n+        elif isinstance(index, slice):\n+            start, stop = index.start, index.stop\n+            if start is None:\n+                start = 0\n+            if stop is None:\n+                stop = self.length\n+            return [self.coeff(self._ith_point(i, index.step)) for i in\\\n+                               range(start, stop)]\n+\n+\n+class EmptySequence(with_metaclass(Singleton, SeqBase)):\n+    \"\"\"\n+    Represents an empty sequence. The empty sequence is available as a\n+    singleton as S.EmptySequence.\n+\n+    Examples\n+    ========\n+\n+    >>> from sympy import S, SeqPer, oo\n+    >>> S.EmptySequence\n+    EmptySequence()\n+\n+    >>> SeqPer((1, 2)).add(S.EmptySequence)\n+    SeqPer((1, 2), ([0, oo), 1))\n+    >>> SeqPer((1, 2)).mul(S.EmptySequence)\n+    EmptySequence()\n+    >>> S.EmptySequence.coeff_mul(-1)\n+    EmptySequence()\n+    \"\"\"\n+\n+    @property\n+    def interval(self):\n+        return S.EmptySet\n+\n+    @property\n+    def length(self):\n+        return S.Zero\n+\n+    def coeff_mul(self, coeff):\n+        \"\"\"See docstring of SeqBase.coeff_mul\"\"\"\n+        return self\n+\n+    def __iter__(self):\n+        return iter([])\n+\n+\n+def _parse_interval(interval):\n+    \"\"\"\n+    interval should be of the form (start, step) or (start, stop, step)\n+    Both start and stop cannot be unbounded\n+    step cannot be unbounded\n+\n+    Allowed:\n+    * Any instance of set\n+    * (Set, step)\n+    * (start, stop)\n+    * (start, stop, step)\n+\n+    returns an Interval object and a step value\n+\n+    Examples\n+    ========\n+\n+    >>> from sympy.series.sequences import _parse_interval as pari\n+    >>> from sympy import Interval\n+    >>> pari(Interval(0, 5))\n+    ([0, 5], 1)\n+    >>> pari((Interval(0, 5), 2))\n+    ([0, 5], 2)\n+    >>> pari((0, 5))\n+    ([0, 5], 1)\n+    >>> pari((0, 5, 2))\n+    ([0, 5], 2)\n+    \"\"\"\n+    start, stop, step = None, None, None\n+    if isinstance(interval, Set):\n+        start, stop = interval.inf, interval.sup\n+    elif is_sequence(interval, Tuple):\n+        if len(interval) == 2:\n+            if isinstance(interval[0], Set):\n+                start, stop = interval[0].inf, interval[0].sup\n+                step = interval[1]\n+            else:\n+                start, stop = interval\n+        elif len(interval) == 3:\n+            start, stop, step = interval\n+\n+    if step == None:\n+        step = 1 # default\n+\n+    if start == None or stop == None:\n+        raise ValueError('Invalid limits given: %s' % str(interval))\n+\n+    if start is S.NegativeInfinity and stop is S.Infinity:\n+            raise ValueError(\"Both the start and end value\"\n+                             \" cannot be unbounded\")\n+\n+    if step in [S.NegativeInfinity, S.Infinity]:\n+        raise ValueError(\"step cannot be unbounded\")\n+\n+    return (Interval(start, stop), sympify(step))\n+\n+\n+class SeqExpr(SeqBase):\n+    \"\"\"Sequence expression class\n+    Various sequences (SeqPer, SeqFormula, SeqFunc...) should inherit from\n+    this class\n+\n+    Examples\n+    ========\n+\n+    >>> from sympy.series.sequences import SeqExpr\n+    >>> s = SeqExpr((1, 2, 3), (0, 10))\n+    >>> s.gen\n+    (1, 2, 3)\n+    >>> s.interval\n+    [0, 10]\n+    >>> s.length\n+    11\n+\n+    changing the step size\n+\n+    >>> SeqExpr((1, 2, 3), (0, 10, 2)).length\n+    6\n+\n+    See Also\n+    ========\n+\n+    sympy.series.sequences.SeqPer\n+    sympy.series.sequences.SeqFormula\n+    sympy.series.sequences.SeqFunc\n+    \"\"\"\n+\n+    def __new__(cls, gen, interval=(0, S.Infinity, 1)):\n+        bounds, step = _parse_interval(interval)\n+        if bounds is S.EmptySet:\n+            return S.EmptySequence\n+        gen = cls._validate(gen)\n+        interval = Tuple(bounds, step)\n+        return Expr.__new__(cls, gen, interval)\n+\n+    @staticmethod\n+    def _validate(gen):\n+        \"\"\"Validates the generator\n+        Should return unnchanged if no change required\n+        \"\"\"\n+        return sympify(gen)\n+\n+    @property\n+    def gen(self):\n+        return self.args[0]\n+\n+    @property\n+    def interval(self):\n+        return self.args[1][0]\n+\n+    @property\n+    def start(self):\n+        return self.interval.inf\n+\n+    @property\n+    def stop(self):\n+        return self.interval.sup\n+\n+    @property\n+    def step(self):\n+        return self.args[1][1]\n+\n+    @property\n+    def length(self):\n+        return ceiling((self.stop - self.start + 1) / self.step)\n+\n+\n+class SeqPer(SeqExpr):\n+    \"\"\"Represents a periodic sequence\n+\n+    The elements are repeated after a given period.\n+\n+    Examples\n+    ========\n+\n+    >>> from sympy import SeqPer, oo\n+    >>> s = SeqPer((1, 2, 3), (0, 5))\n+    >>> s.periodical\n+    (1, 2, 3)\n+    >>> s.period\n+    3\n+\n+    For value at a particular point\n+\n+    >>> s.coeff(3)\n+    1\n+\n+    supports slicing\n+\n+    >>> s[:]\n+    [1, 2, 3, 1, 2, 3]\n+\n+    iterable\n+\n+    >>> list(s)\n+    [1, 2, 3, 1, 2, 3]\n+\n+    changing step size\n+\n+    >>> SeqPer((1, 2, 3), (0, 5, 2))[:]\n+    [1, 3, 2]\n+\n+    sequence starts from negative infinity\n+\n+    >>> SeqPer((1, 2, 3), (-oo, 0))[0:6]\n+    [1, 2, 3, 1, 2, 3]\n+\n+    See Also\n+    ========\n+\n+    sympy.series.sequences.SeqFormula\n+    sympy.series.sequences.SeqFunc\n+    \"\"\"\n+\n+    @staticmethod\n+    def _validate(periodical):\n+        \"\"\"\n+        Examples\n+        ========\n+\n+        >>> from sympy.series.sequences import SeqPer as seq\n+        >>> seq._validate((1, 2, 3))\n+        (1, 2, 3)\n+        >>> seq._validate([1, 2, 3])\n+        (1, 2, 3)\n+        >>> seq._validate([1, (2, 3)])\n+        (1, 2, 3)\n+        \"\"\"\n+        if is_sequence(periodical, Tuple):\n+            periodical = tuple(flatten(periodical))\n+            return sympify(periodical)\n+        raise ValueError(\"invalid period %s should be something \"\n+                         \"like e.g (1, 2) \" % periodical)\n+\n+    @property\n+    def period(self):\n+        return len(self.gen)\n+\n+    @property\n+    def periodical(self):\n+        return self.gen\n+\n+    def _eval_coeff(self, pt):\n+        if self.start is S.NegativeInfinity:\n+            idx = (self.stop - pt) % self.period\n+        else:\n+            idx = (pt - self.start) % self.period\n+        return self.periodical[idx]\n+\n+    def _add(self, other):\n+        \"\"\"See docstring of SeqBase._add\"\"\"\n+        if isinstance(other, SeqPer):\n+            per1, lper1 = self.periodical, self.period\n+            per2, lper2 = other.periodical, other.period\n+\n+            per_length = lcm(lper1, lper2)\n+\n+            new_per = []\n+            for x in range(per_length):\n+                ele1 = per1[x % lper1]\n+                ele2 = per2[x % lper2]\n+                new_per.append(ele1 + ele2)\n+            return SeqPer(new_per, self._intersect_interval(other))\n+\n+    def _mul(self, other):\n+        \"\"\"See docstring of SeqBase._mul\"\"\"\n+        if isinstance(other, SeqPer):\n+            per1, lper1 = self.periodical, self.period\n+            per2, lper2 = other.periodical, other.period\n+\n+            per_length = lcm(lper1, lper2)\n+\n+            new_per = []\n+            for x in range(per_length):\n+                ele1 = per1[x % lper1]\n+                ele2 = per2[x % lper2]\n+                new_per.append(ele1 * ele2)\n+\n+            return SeqPer(new_per, self._intersect_interval(other))\n+\n+    def coeff_mul(self, coeff):\n+        \"\"\"See docstring of SeqBase.coeff_mul\"\"\"\n+        coeff = sympify(coeff)\n+        per = [x * coeff for x in self.periodical]\n+        return SeqPer(per, (self.interval, self.step))\n+\n+\n+class SeqFormula(SeqExpr):\n+    \"\"\"Represents sequence based on a formula\n+\n+    Elements are generated using a formula\n+\n+    Examples\n+    ========\n+\n+    >>> from sympy import SeqFormula, oo, Symbol\n+    >>> n = Symbol('n')\n+    >>> s = SeqFormula((n**2, n), (0, 5))\n+    >>> s.formula\n+    n**2\n+\n+    For value at a particular point\n+\n+    >>> s.coeff(3)\n+    9\n+\n+    supports slicing\n+\n+    >>> s[:]\n+    [0, 1, 4, 9, 16, 25]\n+\n+    iterable\n+\n+    >>> list(s)\n+    [0, 1, 4, 9, 16, 25]\n+\n+    changing step size\n+\n+    >>> SeqFormula(n**2, (0, 5, 2))[:]\n+    [0, 4, 16]\n+\n+    sequence starts from negative infinity\n+\n+    >>> SeqFormula(n**2, (-oo, 0))[0:6]\n+    [0, 1, 4, 9, 16, 25]\n+\n+    See Also\n+    ========\n+\n+    sympy.series.sequences.SeqPer\n+    sympy.series.sequences.SeqFunc\n+    \"\"\"\n+\n+    @staticmethod\n+    def _validate(formula):\n+        \"\"\"\n+        Examples\n+        ========\n+\n+        >>> from sympy.series.sequences import SeqFormula as seq\n+        >>> from sympy.abc import n, m\n+        >>> seq._validate((n**2, n))\n+        (n**2, n)\n+        >>> seq._validate(n**2)\n+        (n**2, n)\n+        >>> seq._validate((m*n**2, n))\n+        (m*n**2, n)\n+        >>> seq._validate(1)\n+        (1, _k)\n+        \"\"\"\n+        formula = sympify(formula)\n+        if not is_sequence(formula, Tuple):\n+            free = formula.free_symbols\n+            if len(free) == 0:\n+                k = Dummy('k')\n+                formula = Tuple(formula, k)\n+            elif len(free) == 1:\n+                formula = Tuple(formula, free.pop())\n+            else:\n+                raise ValueError(\n+                    \" specify dummy variables for %s. If the formula contains\"\n+                    \" more than one free symbol, a dummy variable should be\"\n+                    \" supplied explicitly e.g., SeqFormula((m*n**2, n), (0, 5))\"\n+                    % formula)\n+        return formula\n+\n+    @property\n+    def formula(self):\n+        return self.gen[0]\n+\n+    @property\n+    def variables(self):\n+        \"\"\"Bounded variables\n+        In the case of SeqFormula\n+        it will be a tuple with only one symbol\n+        \"\"\"\n+        return (self.gen[1],)\n+\n+    def _eval_coeff(self, pt):\n+        d = self.variables[0]\n+        return self.formula.subs(d, pt)\n+\n+    def _add(self, other):\n+        \"\"\"See docstring of SeqBase._add\"\"\"\n+        if isinstance(other, SeqFormula):\n+            form1, v1 = self.formula, self.variables[0]\n+            form2, v2 = other.formula, other.variables[0]\n+            formula = (form1 + form2.subs(v2, v1), v1)\n+            return SeqFormula(formula, self._intersect_interval(other))\n+\n+\n+    def _mul(self, other):\n+        \"\"\"See docstring of SeqBase._mul\"\"\"\n+        if isinstance(other, SeqFormula):\n+            form1, v1 = self.formula, self.variables[0]\n+            form2, v2 = other.formula, other.variables[0]\n+            formula = (form1 * form2.subs(v2, v1), v1)\n+            return SeqFormula(formula, self._intersect_interval(other))\n+\n+    def coeff_mul(self, coeff):\n+        \"\"\"See docstring of SeqBase.coeff_mul\"\"\"\n+        coeff = sympify(coeff)\n+        formula = self.formula * coeff\n+        return SeqFormula((formula, self.variables[0]), \\\n+                          (self.interval, self.step))\n+\n+\n+class SeqFunc(SeqExpr):\n+    \"\"\"Represents sequence based on a function\n+\n+    Elements are generated by calling a function.\n+    Only single argument functions are allowed.\n+    Only SymPy's :class:`Lambda` functions are permitted.\n+\n+    Examples\n+    ========\n+\n+    >>> from sympy import SeqFunc, oo, Lambda, Symbol\n+    >>> n = Symbol('n')\n+    >>> s = SeqFunc(Lambda(n, n**2), (0, 5))\n+    >>> s.function\n+    Lambda(n, n**2)\n+\n+    For value at a particular point\n+\n+    >>> s.coeff(3)\n+    9\n+\n+    supports slicing\n+\n+    >>> s[:]\n+    [0, 1, 4, 9, 16, 25]\n+\n+    iterable\n+\n+    >>> list(s)\n+    [0, 1, 4, 9, 16, 25]\n+\n+    changing step size\n+\n+    >>> SeqFunc(Lambda(n, n**2), (0, 5, 2))[:]\n+    [0, 4, 16]\n+\n+    sequence starts from negative infinity\n+\n+    >>> SeqFunc(Lambda(n, n**2), (-oo, 0))[0:6]\n+    [0, 1, 4, 9, 16, 25]\n+\n+    See Also\n+    ========\n+\n+    sympy.series.sequences.SeqPer\n+    sympy.series.sequences.SeqFormula\n+    sympy.core.function.Lambda\n+    \"\"\"\n+\n+    @staticmethod\n+    def _validate(function):\n+        if len(function.variables) != 1:\n+            raise ValueError(\"Only single argument functions are allowed\")\n+        return sympify(function)\n+\n+    @property\n+    def function(self):\n+        return self.gen\n+\n+    @property\n+    def variables(self):\n+        return self.gen.variables\n+\n+    def _eval_coeff(self, pt):\n+        return self.function(pt)\n+\n+    def _add(self, other):\n+        \"\"\"See docstring of SeqBase._add\"\"\"\n+        if isinstance(other, SeqFunc)\n+            func1, v1 = self.function, self.variables[0]\n+            func2, v2 = other.function, other.variables[0]\n+            function = Lambda(v1, func1.expr + func2.expr.subs(v2, v1))\n+            return SeqFunc(function, self._intersect_interval(other))\n+\n+    def _mul(self, other):\n+        \"\"\"See docstring of SeqBase._mul\"\"\"\n+        if isinstance(other, SeqFunc)\n+            func1, v1 = self.function, self.variables[0]\n+            func2, v2 = other.function, other.variables[0]\n+            function = Lambda(v1, func1.expr * func2.expr.subs(v2, v1))\n+            return SeqFunc(function, self._intersect_interval(other))\n+\n+    def coeff_mul(self, coeff):\n+        \"\"\"See docstring of SeqBase.coeff_mul\"\"\"\n+        coeff = sympify(coeff)\n+        expr = self.function.expr * coeff\n+        return SeqFunc(Lambda(self.variables[0], expr), \\\n+                          (self.interval, self.step))\n+\n+\n+def sequence(**kwargs):\n+    \"\"\"Returns appropriate sequence object.\n+\n+    Examples\n+    ========\n+\n+    >>> from sympy import sequence, SeqPer, SeqFunc, SeqFormula, Lambda\n+    >>> from sympy.abc import n\n+\n+    >>> sequence(formula=(n**2, n), interval=(0, 5))\n+    SeqFormula((n**2, n), ([0, 5], 1))\n+\n+    >>> sequence(periodical=(1, 2, 3), interval=(0, 5))\n+    SeqPer((1, 2, 3), ([0, 5], 1))\n+\n+    >>> sequence(func=Lambda(n, n**2), interval=(0, 5))\n+    SeqFunc(Lambda(n, n**2), ([0, 5], 1))\n+\n+    See Also\n+    ========\n+\n+    sympy.series.sequences.SeqPer\n+    sympy.series.sequences.SeqFormula\n+    sympy.series.sequences.SeqFunc\n+    \"\"\"\n+    interval = kwargs.pop('interval', None)\n+    if interval is None:\n+        interval = (0, S.Infinity, 1)\n+\n+    key = kwargs.pop('periodical', None)\n+    if key is not None:\n+        return SeqPer(key, interval)\n+\n+    key = kwargs.pop('func', None)\n+    if key is not None:\n+        return SeqFunc(key, interval)\n+\n+    key = kwargs.pop('formula', None)\n+    if key is not None:\n+        return SeqFormula(key, interval)\n+\n+    raise ValueError('Invalid Arguments')\n+\n+\n+################################################################################\n+#                            OPERATIONS                                        #\n+################################################################################\n+\n+\n+class SeqExprOp(SeqBase):\n+    \"\"\"Base class for operations on sequences\n+\n+    Examples\n+    ========\n+\n+    >>> from sympy.series.sequences import SeqExprOp, sequence\n+    >>> from sympy.abc import n\n+    >>> s1 = sequence(formula=(n**2, n), interval=(0, 10))\n+    >>> s2 = sequence(periodical=(1, 2, 3), interval=(5, 10))\n+    >>> s = SeqExprOp(s1, s2)\n+    >>> s.gen\n+    ((n**2, n), (1, 2, 3))\n+    >>> s.interval\n+    [5, 10]\n+    >>> s.length\n+    6\n+\n+    See Also\n+    ========\n+\n+    sympy.series.sequences.SeqAdd\n+    sympy.series.sequences.SeqMul\n+    \"\"\"\n+    @property\n+    def gen(self):\n+        \"\"\"Generator for the sequence\n+        returns a tuple of generators of all the argument sequences\n+        \"\"\"\n+        return tuple([a.gen for a in self.args])\n+\n+    @property\n+    def interval(self):\n+        \"\"\"Sequence is defined on the intersection\n+        of all the intervals of respective sequences\n+        \"\"\"\n+        return Intersection(*[a.interval for a in self.args])\n+\n+    @property\n+    def start(self):\n+        return self.interval.inf\n+\n+    @property\n+    def stop(self):\n+        return self.interval.sup\n+\n+    @property\n+    def variables(self):\n+        \"\"\"Cumulative of all the bound variables\"\"\"\n+        return tuple(flatten([a.variables for a in self.args]))\n+\n+    @property\n+    def step(self):\n+        \"\"\"By default maximum step size is taken as the step size\"\"\"\n+        return max(a.step for a in self.args)\n+\n+    @property\n+    def length(self):\n+        return ceiling((self.stop - self.start + 1) / self.step)\n+\n+\n+class SeqAdd(Add, SeqExprOp):\n+    \"\"\"\n+    Represents term-wise addition of sequences\n+\n+    Rules:\n+        * The interval on which sequence is defined is the intersection\n+        of respective intervals of sequences.\n+        * Anything + EmptySequence, remains unchanged\n+        * Other rules are defined in _add methods of sequence classes\n+\n+    Examples\n+    ========\n+\n+    >>> from sympy import S, oo, SeqAdd, SeqPer, SeqFormula\n+    >>> from sympy.abc import n\n+    >>> SeqAdd(SeqPer((1, 2)), S.EmptySequence)\n+    SeqPer((1, 2), ([0, oo), 1))\n+    >>> SeqAdd(SeqPer((1, 2), (0, 5)), SeqPer((1, 2), (6, 10)))\n+    EmptySequence()\n+    >>> SeqAdd(SeqPer((1, 2)), SeqFormula(n**2))\n+    SeqFormula((n**2, n), ([0, oo), 1)) + SeqPer((1, 2), ([0, oo), 1))\n+    >>> SeqAdd(SeqFormula(n**3), SeqFormula(n**2))\n+    SeqFormula((n**3 + n**2, n), ([0, oo), 1))\n+\n+    See Also\n+    ========\n+\n+    sympy.series.sequences.SeqMul\n+    \"\"\"\n+\n+    def __new__(cls, *args, **kwargs):\n+        evaluate = kwargs.get('evaluate', global_evaluate[0])\n+\n+        # flatten inputs\n+        args = list(args)\n+\n+        # adapted from sympy.sets.sets.Union\n+        def _flatten(arg):\n+            if isinstance(arg, SeqBase):\n+                if isinstance(arg, SeqAdd):\n+                    return sum(map(_flatten, arg.args), [])\n+                else:\n+                    return [arg]\n+            if iterable(arg):\n+                return sum(map(_flatten, arg), [])\n+            raise TypeError(\"Input must be Sequences or \"\n+                            \" iterables of Sequences\")\n+        args = _flatten(args)\n+\n+        args = [a for a in args if not a is S.EmptySequence]\n+\n+        # Addition of no sequences is EmptySequence\n+        if len(args) == 0:\n+            return S.EmptySequence\n+\n+        if Intersection(*[a.interval for a in args]) is S.EmptySet:\n+            return S.EmptySequence\n+\n+        # reduce using known rules\n+        if evaluate:\n+            return SeqAdd.reduce(args)\n+\n+        args = list(ordered(args, SeqBase._start_key))\n+\n+        return Expr.__new__(cls, *args)\n+\n+    @staticmethod\n+    def reduce(args):\n+        \"\"\"\n+        Simplify a :class:`SeqAdd` using known rules\n+\n+        Then we iterate through all pairs and ask the constituent\n+        sequences if they can simplify themselves with any other constituent\n+\n+        Notes\n+        =====\n+        adapted from ``Union.reduce``\n+\n+        \"\"\"\n+        new_args = True\n+        while(new_args):\n+            for id1, s in enumerate(args):\n+                new_args = False\n+                for id2, t in enumerate(args):\n+                    if id1 == id2:\n+                        continue\n+                    new_seq = s._add(t)\n+                    # This returns None if s does not know how to add\n+                    # with t. Returns the newly added sequence otherwise\n+                    if new_seq is not None:\n+                        new_args = [a for a in args if not a in (s, t)]\n+                        new_args.append(new_seq)\n+                        break\n+                if new_args:\n+                    args = new_args\n+                    break\n+\n+        if len(args) == 1:\n+            return args.pop()\n+        else:\n+            return SeqAdd(args, evaluate=False)\n+\n+    def _eval_coeff(self, pt):\n+        \"\"\"adds up the coefficients of all the sequences at point pt\"\"\"\n+        val = 0\n+        for a in self.args:\n+            val += a.coeff(pt)\n+        return val","path":"sympy/series/sequences.py","position":1013,"original_position":1013,"commit_id":"c21456bd70ff55b5f3e183d9ae84ba28c5780d4e","original_commit_id":"c21456bd70ff55b5f3e183d9ae84ba28c5780d4e","user":{"login":"jcrist","id":2783717,"avatar_url":"https://avatars.githubusercontent.com/u/2783717?v=3","gravatar_id":"","url":"https://api.github.com/users/jcrist","html_url":"https://github.com/jcrist","followers_url":"https://api.github.com/users/jcrist/followers","following_url":"https://api.github.com/users/jcrist/following{/other_user}","gists_url":"https://api.github.com/users/jcrist/gists{/gist_id}","starred_url":"https://api.github.com/users/jcrist/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/jcrist/subscriptions","organizations_url":"https://api.github.com/users/jcrist/orgs","repos_url":"https://api.github.com/users/jcrist/repos","events_url":"https://api.github.com/users/jcrist/events{/privacy}","received_events_url":"https://api.github.com/users/jcrist/received_events","type":"User","site_admin":false},"body":"`sum(a.coeff(pt) for a in self.args)`","created_at":"2015-06-05T15:20:28Z","updated_at":"2015-06-05T15:20:28Z","html_url":"https://github.com/sympy/sympy/pull/9435#discussion_r31821690","pull_request_url":"https://api.github.com/repos/sympy/sympy/pulls/9435","_links":{"self":{"href":"https://api.github.com/repos/sympy/sympy/pulls/comments/31821690"},"html":{"href":"https://github.com/sympy/sympy/pull/9435#discussion_r31821690"},"pull_request":{"href":"https://api.github.com/repos/sympy/sympy/pulls/9435"}}},"pull_request":{"url":"https://api.github.com/repos/sympy/sympy/pulls/9435","id":36337749,"html_url":"https://github.com/sympy/sympy/pull/9435","diff_url":"https://github.com/sympy/sympy/pull/9435.diff","patch_url":"https://github.com/sympy/sympy/pull/9435.patch","issue_url":"https://api.github.com/repos/sympy/sympy/issues/9435","number":9435,"state":"open","locked":false,"title":"Implementing Sequences","user":{"login":"leosartaj","id":6217976,"avatar_url":"https://avatars.githubusercontent.com/u/6217976?v=3","gravatar_id":"","url":"https://api.github.com/users/leosartaj","html_url":"https://github.com/leosartaj","followers_url":"https://api.github.com/users/leosartaj/followers","following_url":"https://api.github.com/users/leosartaj/following{/other_user}","gists_url":"https://api.github.com/users/leosartaj/gists{/gist_id}","starred_url":"https://api.github.com/users/leosartaj/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/leosartaj/subscriptions","organizations_url":"https://api.github.com/users/leosartaj/orgs","repos_url":"https://api.github.com/users/leosartaj/repos","events_url":"https://api.github.com/users/leosartaj/events{/privacy}","received_events_url":"https://api.github.com/users/leosartaj/received_events","type":"User","site_admin":false},"body":"A **Sequence** is finite or infinite lazily evaluated list. Coefficients will only be evaluated when required.\r\n\r\n### TODO\r\n- [x] SeqBase Class (abstract class)\r\n- [x] EmptySequence\r\n- [x] SeqExpr class (SeqPer, SeqFunc, SeqFormula inherit from it)\r\n- [x] SeqPer class\r\n- [x] SeqFormula class\r\n- [x] SeqFunc class\r\n- [x] Wrapper function for the above sequences\r\n- [x] Implement SeqExprOp class\r\n- [x] Term-wise Addition/Subtraction\r\n- [x] Term-wise Multiplication\r\n- [x] printing\r\n\r\nPing @jcrist @flacjacket @certik ","created_at":"2015-05-27T19:08:28Z","updated_at":"2015-06-05T15:20:28Z","closed_at":null,"merged_at":null,"merge_commit_sha":"6775f67b76b0dbe91048762c536b7224c5fb3bfe","assignee":null,"milestone":null,"commits_url":"https://api.github.com/repos/sympy/sympy/pulls/9435/commits","review_comments_url":"https://api.github.com/repos/sympy/sympy/pulls/9435/comments","review_comment_url":"https://api.github.com/repos/sympy/sympy/pulls/comments{/number}","comments_url":"https://api.github.com/repos/sympy/sympy/issues/9435/comments","statuses_url":"https://api.github.com/repos/sympy/sympy/statuses/c21456bd70ff55b5f3e183d9ae84ba28c5780d4e","head":{"label":"leosartaj:sequences","ref":"sequences","sha":"c21456bd70ff55b5f3e183d9ae84ba28c5780d4e","user":{"login":"leosartaj","id":6217976,"avatar_url":"https://avatars.githubusercontent.com/u/6217976?v=3","gravatar_id":"","url":"https://api.github.com/users/leosartaj","html_url":"https://github.com/leosartaj","followers_url":"https://api.github.com/users/leosartaj/followers","following_url":"https://api.github.com/users/leosartaj/following{/other_user}","gists_url":"https://api.github.com/users/leosartaj/gists{/gist_id}","starred_url":"https://api.github.com/users/leosartaj/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/leosartaj/subscriptions","organizations_url":"https://api.github.com/users/leosartaj/orgs","repos_url":"https://api.github.com/users/leosartaj/repos","events_url":"https://api.github.com/users/leosartaj/events{/privacy}","received_events_url":"https://api.github.com/users/leosartaj/received_events","type":"User","site_admin":false},"repo":{"id":29408196,"name":"sympy","full_name":"leosartaj/sympy","owner":{"login":"leosartaj","id":6217976,"avatar_url":"https://avatars.githubusercontent.com/u/6217976?v=3","gravatar_id":"","url":"https://api.github.com/users/leosartaj","html_url":"https://github.com/leosartaj","followers_url":"https://api.github.com/users/leosartaj/followers","following_url":"https://api.github.com/users/leosartaj/following{/other_user}","gists_url":"https://api.github.com/users/leosartaj/gists{/gist_id}","starred_url":"https://api.github.com/users/leosartaj/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/leosartaj/subscriptions","organizations_url":"https://api.github.com/users/leosartaj/orgs","repos_url":"https://api.github.com/users/leosartaj/repos","events_url":"https://api.github.com/users/leosartaj/events{/privacy}","received_events_url":"https://api.github.com/users/leosartaj/received_events","type":"User","site_admin":false},"private":false,"html_url":"https://github.com/leosartaj/sympy","description":"A computer algebra system written in pure Python","fork":true,"url":"https://api.github.com/repos/leosartaj/sympy","forks_url":"https://api.github.com/repos/leosartaj/sympy/forks","keys_url":"https://api.github.com/repos/leosartaj/sympy/keys{/key_id}","collaborators_url":"https://api.github.com/repos/leosartaj/sympy/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/leosartaj/sympy/teams","hooks_url":"https://api.github.com/repos/leosartaj/sympy/hooks","issue_events_url":"https://api.github.com/repos/leosartaj/sympy/issues/events{/number}","events_url":"https://api.github.com/repos/leosartaj/sympy/events","assignees_url":"https://api.github.com/repos/leosartaj/sympy/assignees{/user}","branches_url":"https://api.github.com/repos/leosartaj/sympy/branches{/branch}","tags_url":"https://api.github.com/repos/leosartaj/sympy/tags","blobs_url":"https://api.github.com/repos/leosartaj/sympy/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/leosartaj/sympy/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/leosartaj/sympy/git/refs{/sha}","trees_url":"https://api.github.com/repos/leosartaj/sympy/git/trees{/sha}","statuses_url":"https://api.github.com/repos/leosartaj/sympy/statuses/{sha}","languages_url":"https://api.github.com/repos/leosartaj/sympy/languages","stargazers_url":"https://api.github.com/repos/leosartaj/sympy/stargazers","contributors_url":"https://api.github.com/repos/leosartaj/sympy/contributors","subscribers_url":"https://api.github.com/repos/leosartaj/sympy/subscribers","subscription_url":"https://api.github.com/repos/leosartaj/sympy/subscription","commits_url":"https://api.github.com/repos/leosartaj/sympy/commits{/sha}","git_commits_url":"https://api.github.com/repos/leosartaj/sympy/git/commits{/sha}","comments_url":"https://api.github.com/repos/leosartaj/sympy/comments{/number}","issue_comment_url":"https://api.github.com/repos/leosartaj/sympy/issues/comments{/number}","contents_url":"https://api.github.com/repos/leosartaj/sympy/contents/{+path}","compare_url":"https://api.github.com/repos/leosartaj/sympy/compare/{base}...{head}","merges_url":"https://api.github.com/repos/leosartaj/sympy/merges","archive_url":"https://api.github.com/repos/leosartaj/sympy/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/leosartaj/sympy/downloads","issues_url":"https://api.github.com/repos/leosartaj/sympy/issues{/number}","pulls_url":"https://api.github.com/repos/leosartaj/sympy/pulls{/number}","milestones_url":"https://api.github.com/repos/leosartaj/sympy/milestones{/number}","notifications_url":"https://api.github.com/repos/leosartaj/sympy/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/leosartaj/sympy/labels{/name}","releases_url":"https://api.github.com/repos/leosartaj/sympy/releases{/id}","created_at":"2015-01-17T22:39:40Z","updated_at":"2015-05-26T14:11:50Z","pushed_at":"2015-06-05T12:39:19Z","git_url":"git://github.com/leosartaj/sympy.git","ssh_url":"git@github.com:leosartaj/sympy.git","clone_url":"https://github.com/leosartaj/sympy.git","svn_url":"https://github.com/leosartaj/sympy","homepage":"http://sympy.org/","size":69933,"stargazers_count":2,"watchers_count":2,"language":"Python","has_issues":false,"has_downloads":true,"has_wiki":true,"has_pages":false,"forks_count":0,"mirror_url":null,"open_issues_count":0,"forks":0,"open_issues":0,"watchers":2,"default_branch":"master"}},"base":{"label":"sympy:master","ref":"master","sha":"3766b9ca505bb3645e81912b3cc883df73c7a32b","user":{"login":"sympy","id":260832,"avatar_url":"https://avatars.githubusercontent.com/u/260832?v=3","gravatar_id":"","url":"https://api.github.com/users/sympy","html_url":"https://github.com/sympy","followers_url":"https://api.github.com/users/sympy/followers","following_url":"https://api.github.com/users/sympy/following{/other_user}","gists_url":"https://api.github.com/users/sympy/gists{/gist_id}","starred_url":"https://api.github.com/users/sympy/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/sympy/subscriptions","organizations_url":"https://api.github.com/users/sympy/orgs","repos_url":"https://api.github.com/users/sympy/repos","events_url":"https://api.github.com/users/sympy/events{/privacy}","received_events_url":"https://api.github.com/users/sympy/received_events","type":"Organization","site_admin":false},"repo":{"id":640534,"name":"sympy","full_name":"sympy/sympy","owner":{"login":"sympy","id":260832,"avatar_url":"https://avatars.githubusercontent.com/u/260832?v=3","gravatar_id":"","url":"https://api.github.com/users/sympy","html_url":"https://github.com/sympy","followers_url":"https://api.github.com/users/sympy/followers","following_url":"https://api.github.com/users/sympy/following{/other_user}","gists_url":"https://api.github.com/users/sympy/gists{/gist_id}","starred_url":"https://api.github.com/users/sympy/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/sympy/subscriptions","organizations_url":"https://api.github.com/users/sympy/orgs","repos_url":"https://api.github.com/users/sympy/repos","events_url":"https://api.github.com/users/sympy/events{/privacy}","received_events_url":"https://api.github.com/users/sympy/received_events","type":"Organization","site_admin":false},"private":false,"html_url":"https://github.com/sympy/sympy","description":"A computer algebra system written in pure Python","fork":false,"url":"https://api.github.com/repos/sympy/sympy","forks_url":"https://api.github.com/repos/sympy/sympy/forks","keys_url":"https://api.github.com/repos/sympy/sympy/keys{/key_id}","collaborators_url":"https://api.github.com/repos/sympy/sympy/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/sympy/sympy/teams","hooks_url":"https://api.github.com/repos/sympy/sympy/hooks","issue_events_url":"https://api.github.com/repos/sympy/sympy/issues/events{/number}","events_url":"https://api.github.com/repos/sympy/sympy/events","assignees_url":"https://api.github.com/repos/sympy/sympy/assignees{/user}","branches_url":"https://api.github.com/repos/sympy/sympy/branches{/branch}","tags_url":"https://api.github.com/repos/sympy/sympy/tags","blobs_url":"https://api.github.com/repos/sympy/sympy/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/sympy/sympy/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/sympy/sympy/git/refs{/sha}","trees_url":"https://api.github.com/repos/sympy/sympy/git/trees{/sha}","statuses_url":"https://api.github.com/repos/sympy/sympy/statuses/{sha}","languages_url":"https://api.github.com/repos/sympy/sympy/languages","stargazers_url":"https://api.github.com/repos/sympy/sympy/stargazers","contributors_url":"https://api.github.com/repos/sympy/sympy/contributors","subscribers_url":"https://api.github.com/repos/sympy/sympy/subscribers","subscription_url":"https://api.github.com/repos/sympy/sympy/subscription","commits_url":"https://api.github.com/repos/sympy/sympy/commits{/sha}","git_commits_url":"https://api.github.com/repos/sympy/sympy/git/commits{/sha}","comments_url":"https://api.github.com/repos/sympy/sympy/comments{/number}","issue_comment_url":"https://api.github.com/repos/sympy/sympy/issues/comments{/number}","contents_url":"https://api.github.com/repos/sympy/sympy/contents/{+path}","compare_url":"https://api.github.com/repos/sympy/sympy/compare/{base}...{head}","merges_url":"https://api.github.com/repos/sympy/sympy/merges","archive_url":"https://api.github.com/repos/sympy/sympy/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/sympy/sympy/downloads","issues_url":"https://api.github.com/repos/sympy/sympy/issues{/number}","pulls_url":"https://api.github.com/repos/sympy/sympy/pulls{/number}","milestones_url":"https://api.github.com/repos/sympy/sympy/milestones{/number}","notifications_url":"https://api.github.com/repos/sympy/sympy/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/sympy/sympy/labels{/name}","releases_url":"https://api.github.com/repos/sympy/sympy/releases{/id}","created_at":"2010-04-30T20:37:14Z","updated_at":"2015-06-05T00:07:57Z","pushed_at":"2015-06-05T14:24:32Z","git_url":"git://github.com/sympy/sympy.git","ssh_url":"git@github.com:sympy/sympy.git","clone_url":"https://github.com/sympy/sympy.git","svn_url":"https://github.com/sympy/sympy","homepage":"http://sympy.org/","size":161150,"stargazers_count":2173,"watchers_count":2173,"language":"Python","has_issues":true,"has_downloads":true,"has_wiki":true,"has_pages":false,"forks_count":1071,"mirror_url":null,"open_issues_count":1921,"forks":1071,"open_issues":1921,"watchers":2173,"default_branch":"master"}},"_links":{"self":{"href":"https://api.github.com/repos/sympy/sympy/pulls/9435"},"html":{"href":"https://github.com/sympy/sympy/pull/9435"},"issue":{"href":"https://api.github.com/repos/sympy/sympy/issues/9435"},"comments":{"href":"https://api.github.com/repos/sympy/sympy/issues/9435/comments"},"review_comments":{"href":"https://api.github.com/repos/sympy/sympy/pulls/9435/comments"},"review_comment":{"href":"https://api.github.com/repos/sympy/sympy/pulls/comments{/number}"},"commits":{"href":"https://api.github.com/repos/sympy/sympy/pulls/9435/commits"},"statuses":{"href":"https://api.github.com/repos/sympy/sympy/statuses/c21456bd70ff55b5f3e183d9ae84ba28c5780d4e"}}}}