# HG changeset patch
# User Mike Hansen <mhansen@gmail.com>
# Date 1206963896 25200
# Node ID 05d044a79146ec63c45153a0e9bc1e60a466634f
# Parent  258da00fdef8602f670a0a7065b7256f44628be8
[mq]: fast_sum

diff -r 258da00fdef8 -r 05d044a79146 sage/misc/all.py
--- a/sage/misc/all.py	Mon Mar 31 03:49:20 2008 -0700
+++ b/sage/misc/all.py	Mon Mar 31 04:44:56 2008 -0700
@@ -3,7 +3,7 @@ from misc import (alarm, ellipsis_range,
                   get_verbose_files, unset_verbose_files, get_verbose, 
                   version, banner, add, union, uniq, powerset, subsets,
                   exists, forall, 
-                  random_sublist, mul, prod, walltime, generic_cmp,
+                  random_sublist, mul, prod, balanced_sum, walltime, generic_cmp,
                   repr_lincomb, tmp_dir, tmp_filename,
                   pad_zeros,
                   DOT_SAGE, SAGE_ROOT, SAGE_URL, SAGE_DB, SAGE_TMP,
diff -r 258da00fdef8 -r 05d044a79146 sage/misc/misc.py
--- a/sage/misc/misc.py	Mon Mar 31 03:49:20 2008 -0700
+++ b/sage/misc/misc.py	Mon Mar 31 04:44:56 2008 -0700
@@ -361,7 +361,7 @@ def cmp_props(left, right, props):
         if c: return c
     return 0
 
-from sage.misc.misc_c import prod, running_total
+from sage.misc.misc_c import prod, running_total, balanced_sum
 
 # alternative name for prod
 mul = prod
diff -r 258da00fdef8 -r 05d044a79146 sage/misc/misc_c.pyx
--- a/sage/misc/misc_c.pyx	Mon Mar 31 03:49:20 2008 -0700
+++ b/sage/misc/misc_c.pyx	Mon Mar 31 04:44:56 2008 -0700
@@ -117,7 +117,8 @@ def prod(x, z=None, Py_ssize_t recursion
         prod = z*prod
         
     return prod
-    
+
+
 
 cdef balanced_list_prod(L, Py_ssize_t offset, Py_ssize_t count, Py_ssize_t cutoff):
     """
@@ -149,6 +150,95 @@ cdef balanced_list_prod(L, Py_ssize_t of
         k = (1+count) >> 1
         return balanced_list_prod(L, offset, k, cutoff) * balanced_list_prod(L, offset+k, count-k, cutoff)
 
+
+def balanced_sum(x, z=None, Py_ssize_t recursion_cutoff = 5):
+    """
+    Return the sum of the elements in the list x.  If optional
+    argument z is not given, start the sum with the first element
+    of the list, otherwise use z.  The empty product is the int 0 if z
+    is not specified, and is z if given.
+
+    This assumes that your multiplication is associative; we don't promise 
+    which end of the list we start at.
+
+    EXAMPLES:
+        sage: balanced_sum([1,2,34])
+        37
+        sage: balanced_sum([2,3], 5)
+        10
+        sage: balanced_sum((1,2,3), 5)
+        11
+
+    AUTHORS:
+        Joel B. Mohler (2007-10-03 -- Reimplemented in Cython and optimized)
+        Robert Bradshaw (2007-10-26) -- Balanced product tree, other optimizations, (lazy) generator support
+    """
+    if not PyList_CheckExact(x) and not PyTuple_CheckExact(x):
+    
+        if PyGen_Check(x):
+            # lazy list, do lazy product
+            try:
+                sum = x.next() if z is None else z * x.next()
+                for a in x:
+                    sum += a
+                return sum
+            except StopIteration:
+                x = []
+                
+        else:
+    
+            try:
+                return x.sum()
+            except AttributeError:
+                pass
+
+            x = list(x)
+
+    cdef Py_ssize_t n = len(x)
+
+    if n == 0:
+        if z is None:
+            import sage.rings.integer
+            return sage.rings.integer.Integer(0)
+        else:
+            return z
+    
+    sum = balanced_list_sum(x, 0, n, recursion_cutoff)
+    
+    if z is not None:
+        sum = z+sum
+        
+    return sum
+
+cdef balanced_list_sum(L, Py_ssize_t offset, Py_ssize_t count, Py_ssize_t cutoff):
+    """
+    INPUT: 
+        L      -- the terms (MUST be a tuple or list)
+        off    -- offset in the list from which to start
+        count  -- how many terms in the product
+        cutoff -- the minimum count to recurse on
+        
+    OUTPUT: 
+        L[offset] + L[offset+1] + ... + L[offset+count-1]
+    
+    NOTE: The parameter cutoff must be at least 1, and there is no reason to
+          ever make it less than 3. However, there are at least two advantages
+          to setting it higher (and consequently not recursing all the way 
+          down the tree). First, one avoids the overhead of the function 
+          calls at the base of the tree (which is the majority of them) and 
+          second, it allows one to save on object creation if inplace
+          operations are used. The asymptotic gains should usually be at the 
+          top of the tree anyway.
+    """
+    cdef Py_ssize_t k
+    if count <= cutoff:
+        sum = <object>PySequence_Fast_GET_ITEM(L, offset)
+        for k from offset < k < offset+count:
+            sum += <object>PySequence_Fast_GET_ITEM(L, k)
+        return sum
+    else:
+        k = (1+count) >> 1
+        return balanced_list_sum(L, offset, k, cutoff) + balanced_list_sum(L, offset+k, count-k, cutoff)
 
 class NonAssociative:
     """
@@ -205,4 +295,4 @@ class NonAssociative:
             (a*(b*c))
         """
         return NonAssociative(self, other)
-        
\ No newline at end of file
+        
