| | 153 | |
| | 154 | def balanced_sum(x, z=None, Py_ssize_t recursion_cutoff = 5): |
| | 155 | """ |
| | 156 | Return the sum of the elements in the list x. If optional |
| | 157 | argument z is not given, start the sum with the first element |
| | 158 | of the list, otherwise use z. The empty product is the int 0 if z |
| | 159 | is not specified, and is z if given. |
| | 160 | |
| | 161 | This assumes that your multiplication is associative; we don't promise |
| | 162 | which end of the list we start at. |
| | 163 | |
| | 164 | EXAMPLES: |
| | 165 | sage: balanced_sum([1,2,34]) |
| | 166 | 37 |
| | 167 | sage: balanced_sum([2,3], 5) |
| | 168 | 10 |
| | 169 | sage: balanced_sum((1,2,3), 5) |
| | 170 | 11 |
| | 171 | |
| | 172 | AUTHORS: |
| | 173 | Joel B. Mohler (2007-10-03 -- Reimplemented in Cython and optimized) |
| | 174 | Robert Bradshaw (2007-10-26) -- Balanced product tree, other optimizations, (lazy) generator support |
| | 175 | """ |
| | 176 | if not PyList_CheckExact(x) and not PyTuple_CheckExact(x): |
| | 177 | |
| | 178 | if PyGen_Check(x): |
| | 179 | # lazy list, do lazy product |
| | 180 | try: |
| | 181 | sum = x.next() if z is None else z * x.next() |
| | 182 | for a in x: |
| | 183 | sum += a |
| | 184 | return sum |
| | 185 | except StopIteration: |
| | 186 | x = [] |
| | 187 | |
| | 188 | else: |
| | 189 | |
| | 190 | try: |
| | 191 | return x.sum() |
| | 192 | except AttributeError: |
| | 193 | pass |
| | 194 | |
| | 195 | x = list(x) |
| | 196 | |
| | 197 | cdef Py_ssize_t n = len(x) |
| | 198 | |
| | 199 | if n == 0: |
| | 200 | if z is None: |
| | 201 | import sage.rings.integer |
| | 202 | return sage.rings.integer.Integer(0) |
| | 203 | else: |
| | 204 | return z |
| | 205 | |
| | 206 | sum = balanced_list_sum(x, 0, n, recursion_cutoff) |
| | 207 | |
| | 208 | if z is not None: |
| | 209 | sum = z+sum |
| | 210 | |
| | 211 | return sum |
| | 212 | |
| | 213 | cdef balanced_list_sum(L, Py_ssize_t offset, Py_ssize_t count, Py_ssize_t cutoff): |
| | 214 | """ |
| | 215 | INPUT: |
| | 216 | L -- the terms (MUST be a tuple or list) |
| | 217 | off -- offset in the list from which to start |
| | 218 | count -- how many terms in the product |
| | 219 | cutoff -- the minimum count to recurse on |
| | 220 | |
| | 221 | OUTPUT: |
| | 222 | L[offset] + L[offset+1] + ... + L[offset+count-1] |
| | 223 | |
| | 224 | NOTE: The parameter cutoff must be at least 1, and there is no reason to |
| | 225 | ever make it less than 3. However, there are at least two advantages |
| | 226 | to setting it higher (and consequently not recursing all the way |
| | 227 | down the tree). First, one avoids the overhead of the function |
| | 228 | calls at the base of the tree (which is the majority of them) and |
| | 229 | second, it allows one to save on object creation if inplace |
| | 230 | operations are used. The asymptotic gains should usually be at the |
| | 231 | top of the tree anyway. |
| | 232 | """ |
| | 233 | cdef Py_ssize_t k |
| | 234 | if count <= cutoff: |
| | 235 | sum = <object>PySequence_Fast_GET_ITEM(L, offset) |
| | 236 | for k from offset < k < offset+count: |
| | 237 | sum += <object>PySequence_Fast_GET_ITEM(L, k) |
| | 238 | return sum |
| | 239 | else: |
| | 240 | k = (1+count) >> 1 |
| | 241 | return balanced_list_sum(L, offset, k, cutoff) + balanced_list_sum(L, offset+k, count-k, cutoff) |