| 1 | ############################################################################### |
| 2 | # Sage: Open Source Mathematical Software |
| 3 | # Copyright (C) 2011 Burcin Erocal <burcin@erocal.org> |
| 4 | # Distributed under the terms of the GNU General Public License (GPL), |
| 5 | # version 2 or any later version. The full text of the GPL is available at: |
| 6 | # http://www.gnu.org/licenses/ |
| 7 | ############################################################################### |
| 8 | from sage.symbolic.expression cimport new_Expression_from_GEx |
| 9 | |
| 10 | cdef inline int normalize_index(object arg, int nops, object err_msg) except -1: |
| 11 | """ |
| 12 | Given an index ``arg`` and the number of operands ``nops`` return |
| 13 | an integer between 0 and ``nops`` which will be used as the index to fetch |
| 14 | the data from the underlying vector. |
| 15 | |
| 16 | TESTS:: |
| 17 | |
| 18 | sage: from sage.symbolic.getitem import normalize_index_for_doctests as normalize_index |
| 19 | sage: normalize_index(-1, 4) |
| 20 | 3 |
| 21 | sage: normalize_index(1, 4) |
| 22 | 1 |
| 23 | sage: normalize_index(1.5, 4) |
| 24 | Traceback (most recent call last): |
| 25 | ... |
| 26 | TypeError: some error message |
| 27 | sage: normalize_index(-5, 4) |
| 28 | Traceback (most recent call last): |
| 29 | ... |
| 30 | IndexError: operand index out of range, got -5, expect between -4 and 3 |
| 31 | sage: normalize_index(5, 4) |
| 32 | Traceback (most recent call last): |
| 33 | ... |
| 34 | IndexError: operand index out of range, got 5, expect between -4 and 3 |
| 35 | """ |
| 36 | cdef int i |
| 37 | i = arg |
| 38 | if i != arg: |
| 39 | raise TypeError(err_msg) |
| 40 | if i < 0: |
| 41 | i = nops + i |
| 42 | if i >= nops or i < 0: |
| 43 | raise IndexError("operand index out of range, got %s, expect between %s and %s"%(arg,-nops,nops-1)) |
| 44 | return i |
| 45 | |
| 46 | def normalize_index_for_doctests(arg, nops): |
| 47 | """ |
| 48 | Wrapper function to test ``normalize_index``. |
| 49 | |
| 50 | TESTS:: |
| 51 | |
| 52 | sage: from sage.symbolic.getitem import normalize_index_for_doctests |
| 53 | sage: normalize_index_for_doctests(-1, 4) |
| 54 | 3 |
| 55 | """ |
| 56 | return normalize_index(arg, nops, "some error message") |
| 57 | |
| 58 | cdef class OperandsWrapper(SageObject): |
| 59 | """ |
| 60 | Operands wrapper for symbolic expressions. |
| 61 | |
| 62 | EXAMPLES:: |
| 63 | |
| 64 | sage: x,y,z = var('x,y,z') |
| 65 | sage: e = x + x*y + z^y + 3*y*z; e |
| 66 | x*y + 3*y*z + z^y + x |
| 67 | sage: e.op[1] |
| 68 | 3*y*z |
| 69 | sage: e.op[1,1] |
| 70 | z |
| 71 | sage: e.op[-1] |
| 72 | x |
| 73 | sage: e.op[1:] |
| 74 | [3*y*z, z^y, x] |
| 75 | sage: e.op[:2] |
| 76 | [x*y, 3*y*z] |
| 77 | sage: e.op[-2:] |
| 78 | [z^y, x] |
| 79 | sage: e.op[:-2] |
| 80 | [x*y, 3*y*z] |
| 81 | sage: e.op[-5] |
| 82 | Traceback (most recent call last): |
| 83 | ... |
| 84 | IndexError: operand index out of range, got -5, expect between -4 and 3 |
| 85 | sage: e.op[5] |
| 86 | Traceback (most recent call last): |
| 87 | ... |
| 88 | IndexError: operand index out of range, got 5, expect between -4 and 3 |
| 89 | sage: e.op[1,1,0] |
| 90 | Traceback (most recent call last): |
| 91 | ... |
| 92 | TypeError: expressions containing only a numeric coefficient, constant or symbol have no operands |
| 93 | sage: e.op[:1.5] |
| 94 | Traceback (most recent call last): |
| 95 | ... |
| 96 | TypeError: slice indices must be integers or None or have an __index__ method |
| 97 | sage: e.op[:2:1.5] |
| 98 | Traceback (most recent call last): |
| 99 | ... |
| 100 | ValueError: step value must be an integer |
| 101 | """ |
| 102 | def __getitem__(self, arg): |
| 103 | """ |
| 104 | TESTS:: |
| 105 | |
| 106 | sage: t = 1+x+x^2 |
| 107 | sage: t.op[1:] |
| 108 | [x, 1] |
| 109 | """ |
| 110 | cdef int ind, nops |
| 111 | cdef int bind, eind, step |
| 112 | cdef GEx cur_ex |
| 113 | if isinstance(arg, slice): |
| 114 | nops = self._expr._gobj.nops() |
| 115 | |
| 116 | slice_err_msg = "slice indices must be integers or None or have an __index__ method" |
| 117 | if arg.start: |
| 118 | bind = normalize_index(arg.start, nops, slice_err_msg) |
| 119 | else: |
| 120 | bind = 0 |
| 121 | if arg.stop: |
| 122 | eind = normalize_index(arg.stop, nops, slice_err_msg) |
| 123 | else: |
| 124 | eind = nops |
| 125 | if arg.step: |
| 126 | step = arg.step |
| 127 | if step != arg.step: |
| 128 | raise ValueError, "step value must be an integer" |
| 129 | else: |
| 130 | step = 1 |
| 131 | return [new_Expression_from_GEx(self._expr._parent, |
| 132 | self._expr._gobj.op(ind)) for ind in xrange(bind, eind, step)] |
| 133 | |
| 134 | |
| 135 | ind_err_msg = "index should either be a slice object, an integer or a list of integers" |
| 136 | if isinstance(arg, (list, tuple)): |
| 137 | # handle nested index |
| 138 | if len(arg) == 0: # or not all(lambda x: x in ZZ for t in args): |
| 139 | raise TypeError, ind_err_msg |
| 140 | GEx_construct_ex(&cur_ex, self._expr._gobj) |
| 141 | for x in arg: |
| 142 | nops = cur_ex.nops() |
| 143 | if nops == 0: |
| 144 | raise TypeError, "expressions containing only a numeric coefficient, constant or symbol have no operands" |
| 145 | ind = normalize_index(x, nops, ind_err_msg) |
| 146 | cur_ex = cur_ex.op(ind) |
| 147 | return new_Expression_from_GEx(self._expr._parent, cur_ex) |
| 148 | |
| 149 | ind = normalize_index(arg, self._expr._gobj.nops(), ind_err_msg) |
| 150 | return new_Expression_from_GEx(self._expr._parent, |
| 151 | self._expr._gobj.op(ind)) |
| 152 | |
| 153 | def _repr_(self): |
| 154 | """ |
| 155 | TESTS:: |
| 156 | |
| 157 | sage: (x^2).op |
| 158 | Operands of x^2 |
| 159 | """ |
| 160 | return "Operands of %s"%(self._expr) |
| 161 | |
| 162 | def _latex_(self): |
| 163 | r""" |
| 164 | TESTS:: |
| 165 | |
| 166 | sage: latex((x^2).op) |
| 167 | \text{Operands wrapper for expression }x^{2} |
| 168 | """ |
| 169 | return r"\text{Operands wrapper for expression }%s"%(self._expr._latex_()) |
| 170 | |
| 171 | def __reduce__(self): |
| 172 | """ |
| 173 | TESTS:: |
| 174 | |
| 175 | sage: (x^2).op.__reduce__() |
| 176 | (<built-in function restore_op_wrapper>, (x^2,)) |
| 177 | sage: loads(dumps((x^2).op)) |
| 178 | Operands of x^2 |
| 179 | """ |
| 180 | return restore_op_wrapper, (self._expr,) |
| 181 | |
| 182 | def restore_op_wrapper(expr): |
| 183 | """ |
| 184 | TESTS:: |
| 185 | |
| 186 | sage: from sage.symbolic.getitem import restore_op_wrapper |
| 187 | sage: restore_op_wrapper(x^2) |
| 188 | Operands of x^2 |
| 189 | """ |
| 190 | return expr.op |