mirror of
https://github.com/c-cube/ocaml-containers.git
synced 2026-05-05 17:04:25 -04:00
tests for hashes
This commit is contained in:
parent
7fdee4a17e
commit
fe5231a376
1 changed files with 60 additions and 1 deletions
|
|
@ -16,4 +16,63 @@ t @@ fun () -> string "abc" <> string "abcd";;
|
||||||
|
|
||||||
q Q.int (fun i ->
|
q Q.int (fun i ->
|
||||||
Q.assume (i >= 0);
|
Q.assume (i >= 0);
|
||||||
int i = int64 (Int64.of_int i))
|
int i = int64 (Int64.of_int i));;
|
||||||
|
|
||||||
|
(* --- stress tests -------------------------------------------------------- *)
|
||||||
|
|
||||||
|
(* Chi-squared distribution test over [count] consecutive integers in [buckets] buckets.
|
||||||
|
A uniform hash gives chi2 ~ buckets-1; we allow 4 standard deviations of slack. *)
|
||||||
|
t ~name:"int hash distribution chi2" @@ fun () ->
|
||||||
|
let count = 50_000 and buckets = 500 in
|
||||||
|
let counts = Array.make buckets 0 in
|
||||||
|
for i = 0 to count - 1 do
|
||||||
|
let b = CCHash.int i mod buckets in
|
||||||
|
counts.(b) <- counts.(b) + 1
|
||||||
|
done;
|
||||||
|
let expected = float count /. float buckets in
|
||||||
|
let c2 =
|
||||||
|
Array.fold_left
|
||||||
|
(fun acc c -> acc +. ((float c -. expected) ** 2.0 /. expected))
|
||||||
|
0.0 counts
|
||||||
|
in
|
||||||
|
let df = float (buckets - 1) in
|
||||||
|
c2 < df +. 4.0 *. sqrt (2.0 *. df);;
|
||||||
|
|
||||||
|
(* Strict avalanche criterion: flip one input bit, expect ~50% output bits to change. *)
|
||||||
|
t ~name:"int hash avalanche" @@ fun () ->
|
||||||
|
let bits = Sys.int_size - 1 in
|
||||||
|
let total_flips = ref 0 in
|
||||||
|
let total = ref 0 in
|
||||||
|
let rng = Random.State.make [| 42; 17; 99 |] in
|
||||||
|
for _ = 1 to 300 do
|
||||||
|
let x = Random.State.bits rng in
|
||||||
|
let hx = CCHash.int x in
|
||||||
|
for b = 0 to bits - 1 do
|
||||||
|
let hx' = CCHash.int (x lxor (1 lsl b)) in
|
||||||
|
total_flips := !total_flips + CCInt.popcount (hx lxor hx');
|
||||||
|
total := !total + bits
|
||||||
|
done
|
||||||
|
done;
|
||||||
|
let frac = float !total_flips /. float !total in
|
||||||
|
frac >= 0.45 && frac <= 0.55;;
|
||||||
|
|
||||||
|
(* String hash: no collisions among distinct keys. *)
|
||||||
|
t ~name:"string hash no collisions" @@ fun () ->
|
||||||
|
let n = 50_000 in
|
||||||
|
let tbl = Hashtbl.create n in
|
||||||
|
let ok = ref true in
|
||||||
|
for i = 0 to n - 1 do
|
||||||
|
let h = CCHash.string (Printf.sprintf "key:%d" i) in
|
||||||
|
if Hashtbl.mem tbl h then ok := false;
|
||||||
|
Hashtbl.replace tbl h ()
|
||||||
|
done;
|
||||||
|
!ok;;
|
||||||
|
|
||||||
|
(* CCHash64 pipeline matches CCHash.pair combiner. *)
|
||||||
|
q Q.int (fun i ->
|
||||||
|
let j = i lxor 0xdeadbeef in
|
||||||
|
let h_pair = CCHash.pair CCHash.int CCHash.int (i, j) in
|
||||||
|
let h_manual =
|
||||||
|
CCHash64.(finalize (int (int seed (CCHash.int i)) (CCHash.int j)))
|
||||||
|
in
|
||||||
|
h_pair = h_manual)
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue