diff --git a/src/core/CCInt.ml b/src/core/CCInt.ml index d1887bb4..547ecee9 100644 --- a/src/core/CCInt.ml +++ b/src/core/CCInt.ml @@ -293,3 +293,69 @@ let range_by ~step i j yield = (CCInt.range_by ~step:1 i j |> Iter.to_list) \ (CCInt.range i j |> Iter.to_list) ) *) + +(* + from https://en.wikipedia.org/wiki/Hamming_weight + + //This uses fewer arithmetic operations than any other known + //implementation on machines with slow multiplication. + //It uses 17 arithmetic operations. + int popcount_2(uint64_t x) { + x -= (x >> 1) & m1; //put count of each 2 bits into those 2 bits + x = (x & m2) + ((x >> 2) & m2); //put count of each 4 bits into those 4 bits + x = (x + (x >> 4)) & m4; //put count of each 8 bits into those 8 bits + x += x >> 8; //put count of each 16 bits into their lowest 8 bits + x += x >> 16; //put count of each 32 bits into their lowest 8 bits + x += x >> 32; //put count of each 64 bits into their lowest 8 bits + return x & 0x7f; + } + + m1 = 0x5555555555555555 + m2 = 0x3333333333333333 + m4 = 0x0f0f0f0f0f0f0f0f +*) +let popcount (b:int) : int = + let b = b - ((b lsr 1) land 0x5555555555555555) in + let b = (b land 0x3333333333333333) + ((b lsr 2) land 0x3333333333333333) in + let b = (b + (b lsr 4)) land 0x0f0f0f0f0f0f0f0f in + let b = b + (b lsr 8) in + let b = b + (b lsr 16) in + let b = b + (b lsr 32) in + b land 0x7f + +(*$= + 0 (popcount 0) + 1 (popcount 1) + (Sys.word_size-2) (popcount max_int) + 1 (popcount min_int) + 5 (popcount 0b1101110000000000) +*) + +(*$inject + let simple_popcnt i = + let rec loop n i = + if i=0 then n + else if i land 0b1 = 1 then loop (n+1) (i lsr 1) + else loop n (i lsr 1) + in + loop 0 i +*) + +(*$= + 0 (simple_popcnt 0) + 1 (simple_popcnt 1) + (Sys.word_size-2) (simple_popcnt max_int) + 1 (simple_popcnt min_int) + 5 (simple_popcnt 0b1101110000000000) +*) + +(*$QR & ~count:3_000 ~long_factor:10 + Q.(let g = int in + set_gen (Gen.graft_corners g.gen [min_int; max_int; 0; -1; 1] ()) g) + (fun i -> + if simple_popcnt i <> popcount i then ( + Q.Test.fail_reportf "on %d: simple-popcount=%d, popcount=%d" + i (simple_popcnt i) (popcount i) + ); + true) + *) diff --git a/src/core/CCInt.mli b/src/core/CCInt.mli index 75b4b4a9..688d6b0f 100644 --- a/src/core/CCInt.mli +++ b/src/core/CCInt.mli @@ -105,6 +105,10 @@ val range' : t -> t -> t iter For instance [range' 0 5 = Iter.of_list [0;1;2;3;4]]. @since 1.2 *) +val popcount : t -> int +(** Number of bits set to 1 + @since NEXT_RELEASE *) + (** {2 Infix Operators} @since 0.17 *)