| | 278 | from copy import copy |
| | 279 | |
| | 280 | def balanced_sum(x, z=None, Py_ssize_t recursion_cutoff = 5): |
| | 281 | """ |
| | 282 | Return the sum of the elements in the list x. If optional |
| | 283 | argument z is not given, start the sum with the first element of |
| | 284 | the list, otherwise use z. The empty product is the int 0 if z is |
| | 285 | not specified, and is z if given. The sum is computed |
| | 286 | recursively, where the sum is split up if the list is greater than |
| | 287 | recursion_cutoff. recursion_cutoff must be at least 3. |
| | 288 | |
| | 289 | This assumes that your addition is associative; we don't promise |
| | 290 | which end of the list we start at. |
| | 291 | |
| | 292 | |
| | 293 | EXAMPLES: |
| | 294 | sage: balanced_sum([1,2,34]) |
| | 295 | 37 |
| | 296 | sage: balanced_sum([2,3], 5) |
| | 297 | 10 |
| | 298 | sage: balanced_sum((1,2,3), 5) |
| | 299 | 11 |
| | 300 | |
| | 301 | Order should be preserved:: |
| | 302 | |
| | 303 | sage: balanced_sum([[i] for i in range(10)], [], recursion_cutoff=3) |
| | 304 | [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] |
| | 305 | |
| | 306 | We make copies when appropriate so that we don't accidentally modify the arguments. |
| | 307 | |
| | 308 | sage: range(10e4)==balanced_sum([[i] for i in range(10e4)], []) |
| | 309 | True |
| | 310 | sage: range(10e4)==balanced_sum([[i] for i in range(10e4)], []) |
| | 311 | True |
| | 312 | |
| | 313 | TESTS: |
| | 314 | sage: balanced_sum((1..3)) # nonempty, z=None |
| | 315 | 6 |
| | 316 | sage: balanced_sum((1..-1)) # empty, z=None |
| | 317 | 0 |
| | 318 | sage: balanced_sum((1..3), 5) # nonempty, z is not None |
| | 319 | 11 |
| | 320 | sage: balanced_sum((1..-1), 5) # empty, z is not None |
| | 321 | 5 |
| | 322 | |
| | 323 | AUTHORS: |
| | 324 | Joel B. Mohler (2007-10-03 -- Reimplemented in Cython and optimized) |
| | 325 | Robert Bradshaw (2007-10-26) -- Balanced product tree, other optimizations, (lazy) generator support |
| | 326 | """ |
| | 327 | if recursion_cutoff<3: |
| | 328 | raise ValueError, "recursion_cutoff must be at least 3" |
| | 329 | |
| | 330 | if not PyList_CheckExact(x) and not PyTuple_CheckExact(x): |
| | 331 | |
| | 332 | if PyGen_Check(x): |
| | 333 | # lazy list, do lazy product |
| | 334 | try: |
| | 335 | sum = copy(x.next()) if z is None else z + x.next() |
| | 336 | for a in x: |
| | 337 | sum += a |
| | 338 | return sum |
| | 339 | except StopIteration: |
| | 340 | x = [] |
| | 341 | else: |
| | 342 | try: |
| | 343 | return x.sum() |
| | 344 | except AttributeError: |
| | 345 | pass |
| | 346 | |
| | 347 | x = list(x) |
| | 348 | |
| | 349 | cdef Py_ssize_t n = len(x) |
| | 350 | |
| | 351 | if n == 0: |
| | 352 | if z is None: |
| | 353 | import sage.rings.integer |
| | 354 | return sage.rings.integer.Integer(0) |
| | 355 | else: |
| | 356 | return z |
| | 357 | |
| | 358 | sum = balanced_list_sum(x, 0, n, recursion_cutoff) |
| | 359 | |
| | 360 | if z is not None: |
| | 361 | sum = z+sum |
| | 362 | |
| | 363 | return sum |
| | 364 | |
| | 365 | cdef balanced_list_sum(L, Py_ssize_t offset, Py_ssize_t count, Py_ssize_t cutoff): |
| | 366 | """ |
| | 367 | INPUT: |
| | 368 | L -- the terms (MUST be a tuple or list) |
| | 369 | off -- offset in the list from which to start |
| | 370 | count -- how many terms in the product |
| | 371 | cutoff -- the minimum count to recurse on. Must be at least 2 |
| | 372 | |
| | 373 | OUTPUT: |
| | 374 | L[offset] + L[offset+1] + ... + L[offset+count-1] |
| | 375 | |
| | 376 | NOTE: The parameter cutoff must be at least 3. However, there are |
| | 377 | at least two advantages to setting it higher (and |
| | 378 | consequently not recursing all the way down the |
| | 379 | tree). First, one avoids the overhead of the function calls |
| | 380 | at the base of the tree (which is the majority of them) and |
| | 381 | second, it allows one to save on object creation if inplace |
| | 382 | operations are used. The asymptotic gains should usually be |
| | 383 | at the top of the tree anyway. |
| | 384 | """ |
| | 385 | cdef Py_ssize_t k |
| | 386 | if count <= cutoff: |
| | 387 | sum = <object>PySequence_Fast_GET_ITEM(L, offset)+<object>PySequence_Fast_GET_ITEM(L, offset+1) |
| | 388 | for k from offset+1 < k < offset+count: |
| | 389 | sum += <object>PySequence_Fast_GET_ITEM(L, k) |
| | 390 | return sum |
| | 391 | else: |
| | 392 | k = (1+count) >> 1 |
| | 393 | return balanced_list_sum(L, offset, k, cutoff) + balanced_list_sum(L, offset+k, count-k, cutoff) |
| | 394 | |