# HG changeset patch
# User dcoudert <david.coudert@inria.fr>
# Date 1376770316 -7200
#      Sat Aug 17 22:11:56 2013 +0200
# Node ID b2d0906863b66f1e04a5d5bfb3b347c278b2f133
# Parent  707e593beb0b04aeba1b42108d62ede5cf16d16c
trac #13352 -- Running time improvement of the bitset_len method

diff --git a/module_list.py b/module_list.py
--- a/module_list.py
+++ b/module_list.py
@@ -1220,6 +1220,12 @@
     Extension('sage.misc.pickle_old',
               sources = ['sage/misc/pickle_old.pyx']),
 
+    Extension('sage.misc.popcount',
+              sources = ['sage/misc/popcount.pyx','sage/misc/popcount.cc'],
+              extra_compile_args = [],
+#              libraries = ["popcount"],
+              language = 'c++'),
+
     Extension('sage.misc.randstate',
               sources = ['sage/misc/randstate.pyx'],
               libraries = ['gmp']),
@@ -1236,9 +1242,9 @@
 
     Extension('sage.misc.sage_timeit_class',
               sources = ['sage/misc/sage_timeit_class.pyx']),
-    
-    Extension('sage.misc.classcall_metaclass', 
-              sources = ['sage/misc/classcall_metaclass.pyx']), 
+
+    Extension('sage.misc.classcall_metaclass',
+              sources = ['sage/misc/classcall_metaclass.pyx']),
 
     Extension('sage.misc.fast_methods',
               sources = ['sage/misc/fast_methods.pyx']),
@@ -2082,19 +2088,19 @@
         )
 
 
-if (os.path.isfile(SAGE_INC + "/cplex.h") and                                                                                                                                                                                   
+if (os.path.isfile(SAGE_INC + "/cplex.h") and
     os.path.isfile(SAGE_LOCAL + "/lib/libcplex.a")):
     ext_modules.append(
-        Extension("sage.numerical.backends.cplex_backend",                                                                                                                                                                                    
-                  ["sage/numerical/backends/cplex_backend.pyx"],                                                                                                                                                                              
-                  include_dirs = [SAGE_INC, "sage/c_lib/include/"],                                                                                                                                                        
-                  language = 'c',                                                                                                                                                                                                             
-                  libraries = ["csage", "stdc++", "cplex"]) 
+        Extension("sage.numerical.backends.cplex_backend",
+                  ["sage/numerical/backends/cplex_backend.pyx"],
+                  include_dirs = [SAGE_INC, "sage/c_lib/include/"],
+                  language = 'c',
+                  libraries = ["csage", "stdc++", "cplex"])
         )
 
 if is_package_installed('cbc'):
     ext_modules.append(
-        Extension("sage.numerical.backends.coin_backend",                                                                                                                                                                                     
+        Extension("sage.numerical.backends.coin_backend",
                   ["sage/numerical/backends/coin_backend.pyx"],
                   include_dirs = [SAGE_INC, "sage/c_lib/include/"],
                   language = 'c++',
diff --git a/sage/misc/bitset.pxd b/sage/misc/bitset.pxd
--- a/sage/misc/bitset.pxd
+++ b/sage/misc/bitset.pxd
@@ -9,7 +9,7 @@
     cpdef bint issubset(self, FrozenBitset other)
     cpdef bint issuperset(self, FrozenBitset other)
     cpdef bint isdisjoint(self, FrozenBitset other)
-    cpdef _union(self, FrozenBitset other) 
+    cpdef _union(self, FrozenBitset other)
     cpdef intersection(self, FrozenBitset other)
     cpdef difference(self, FrozenBitset other)
     cpdef symmetric_difference(self, FrozenBitset other)
diff --git a/sage/misc/bitset.pxi b/sage/misc/bitset.pxi
--- a/sage/misc/bitset.pxi
+++ b/sage/misc/bitset.pxi
@@ -438,10 +438,10 @@
     Calculate the number of items in the set (i.e., the number of nonzero bits).
     """
     cdef long len = 0
-    cdef long i = bitset_first(bits)
-    while i >= 0:
-        len += 1
-        i = bitset_next(bits, i + 1)
+    cdef long i
+    for i from 0 <= i < bits.limbs:
+        if bits.bits[i]:
+            len += 1#popcountl(bits.bits[i])
     return len
 
 cdef inline long bitset_hash(bitset_t bits):
diff --git a/sage/misc/bitset_pxd.pxi b/sage/misc/bitset_pxd.pxi
--- a/sage/misc/bitset_pxd.pxi
+++ b/sage/misc/bitset_pxd.pxi
@@ -16,6 +16,7 @@
 # This is a .pxi file so that one can inline functions. Doctests in misc_c.
 
 include "sage/ext/stdsage.pxi"
+#from sage.misc.popcount cimport popcount, popcountl
 
 cdef extern from *:
     void *memset(void *, int, size_t)
diff --git a/sage/misc/popcount.cc b/sage/misc/popcount.cc
new file mode 100644
--- /dev/null
+++ b/sage/misc/popcount.cc
@@ -0,0 +1,114 @@
+/*
+  Fast methods for counting bits.
+
+  Implements some well known methods for counting the number of 1-bits in a 32
+  and 64-bits integer. When available, it uses the __builtin_popcount or
+  __builtin_popcountl methods.  Otherwise, it uses the MIT Hakmem 169 method.
+  See e.g.  http://en.wikipedia.org/wiki/HAKMEM
+
+  Usage:
+  1) Add this into a .pxi of .pyx file
+
+  cdef extern from "sage/misc/popcount.h":
+      unsigned int popcount(unsigned int)
+      unsigned int popcountl(unsigned long)
+
+  2) Then use popcount(i) for counting '1' bits in an unsigned int and
+  popcountl(l) for counting '1' bits in an unsigned long
+
+*/
+
+//******************************************************************************
+//    Copyright (C) 2013 David Coudert <david.coudert@inria.fr>
+//                       Nathann Cohen <nathann.cohen@gmail.com>
+//
+//   Distributed under the terms of the GNU General Public License (GPL)
+//
+//    This code is distributed in the hope that it will be useful,
+//    but WITHOUT ANY WARRANTY; without even the implied warranty of
+//    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+//    General Public License for more details.
+//
+//  The full text of the GPL is available at:
+//
+//                  http://www.gnu.org/licenses/
+//******************************************************************************
+
+#include <stdio.h>
+#include <inttypes.h>
+//#ifdef __i386__
+#if (__GNUC__ > 4) || (__GNUC__ == 4 && __GNUC_MINOR__ >= 8)
+#define __USE_MULTIVERSIONNING 1
+#endif
+//#endif
+
+// MIT HAKMEM 169
+int popcount32(uint32_t i){
+  // Returns the number of '1' bits in a 32-bits integer.
+  i = i - ((i >> 1) & 0x55555555);
+  i = (i & 0x33333333) + ((i >> 2) & 0x33333333);
+  return( ((i + (i >> 4) & 0x0F0F0F0F) * 0x01010101) >> 24 );
+}
+
+int popcount64(uint64_t i){
+  // Returns the number of '1' bits in a 64-bits integer.
+  i = i - ((i >> 1) & 0x5555555555555555ULL);
+  i = (i & 0x3333333333333333ULL) + ((i >> 2) & 0x3333333333333333ULL);
+  return( ( ((i + (i >> 4)) & 0x0f0f0f0f0f0f0f0fULL) * 0x0101010101010101ULL ) >> 56 );
+}
+
+/*
+  If GCC's version is >= 4.8 and arch is i386, we use multiversionning to use
+  the popcount instruction whenever available.
+
+  See :
+  * http://gcc.gnu.org/wiki/FunctionMultiVersioning
+  * http://gcc.gnu.org/onlinedocs/gcc/Function-Multiversioning.html
+ */
+#ifdef __USE_MULTIVERSIONNING
+
+__attribute__ ((target ("default")))
+int popcountl(unsigned long i){
+  printf("Good\n");
+  return sizeof(long)==4? popcount32((uint32_t) i):popcount64((uint64_t) i);
+}
+
+__attribute__ ((target ("default")))
+int popcount(unsigned int i){
+  printf("Good\n");
+  return sizeof(int)==4? popcount32((uint32_t) i):popcount64((uint64_t) i);
+}
+
+__attribute__ ((target ("popcnt")))
+int popcount(unsigned int i){
+  printf("Excellent\n");
+  return __builtin_popcount(i);
+}
+__attribute__ ((target ("popcnt")))
+int popcountl(unsigned long i){
+  printf("Excellent\n");
+  return __builtin_popcountl(i);
+}
+
+/*
+ * Tricks to decide at first loading time if __builtin_popcount is available.
+ * Otherwise, we use the MIT HAKMEM methods.
+ *
+ * This is based on a discussion initiated on a patch proposed by Cristian
+ * Rodriguez <crrodriguez@opensuse.org> for lib/ext2fs/bitops.c. The discussion
+ * can be found at http://patchwork.ozlabs.org/patch/209776/
+ * Further explanation can be found on the online doc of gcc
+ * http://gcc.gnu.org/onlinedocs/gcc/X86-Built_002din-Functions.html
+ */
+#else
+
+int popcount(unsigned int i){
+  printf("Bad\n");
+  return sizeof(int)==4? popcount32(i):popcount64(i);
+}
+int popcountl(unsigned long i){
+  printf("Bad\n");
+  return sizeof(long)==4? popcount32(i):popcount64(i);
+}
+
+#endif
diff --git a/sage/misc/popcount.hpp b/sage/misc/popcount.hpp
new file mode 100644
--- /dev/null
+++ b/sage/misc/popcount.hpp
@@ -0,0 +1,4 @@
+int popcount32(uint32_t i);
+int popcount64(uint64_t i);
+int popcountl(unsigned long i);
+int popcount(unsigned int i);
diff --git a/sage/misc/popcount.pxd b/sage/misc/popcount.pxd
new file mode 100644
--- /dev/null
+++ b/sage/misc/popcount.pxd
@@ -0,0 +1,4 @@
+cdef extern from *:
+    int popcountl(unsigned long)
+    int popcount(unsigned int)
+
diff --git a/sage/misc/popcount.pyx b/sage/misc/popcount.pyx
new file mode 100644
--- /dev/null
+++ b/sage/misc/popcount.pyx
@@ -0,0 +1,6 @@
+r"""
+This is an empty Cython wrapper for popcount functions from popcount.cc
+"""
+def test(n):
+    cdef int nn = n
+    print popcount(nn)
