cuckoo.rs 23 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600
  1. use crate::hash::{HashFunction, HashFunctionValue};
  2. use bincode;
  3. use core::array;
  4. use libm::erf;
  5. use rand::{Rng, SeedableRng};
  6. use rand_chacha::ChaCha12Rng;
  7. use std::f64::consts::SQRT_2;
  8. use std::fmt;
  9. use std::fmt::Debug;
  10. pub const NUMBER_HASH_FUNCTIONS: usize = 3;
  11. pub struct Parameters<H: HashFunction<Value>, Value: HashFunctionValue>
  12. where
  13. <Value as TryInto<usize>>::Error: Debug,
  14. {
  15. number_inputs: usize,
  16. number_buckets: usize,
  17. hash_function_descriptions: [H::Description; 3],
  18. }
  19. impl<H, Value> Debug for Parameters<H, Value>
  20. where
  21. H: HashFunction<Value>,
  22. Value: HashFunctionValue,
  23. <Value as TryInto<usize>>::Error: Debug,
  24. {
  25. fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
  26. write!(f, "Parameters<H>{{")?;
  27. write!(f, "number_inputs: {:?}, ", self.number_inputs)?;
  28. write!(f, "number_buckets: {:?}, ", self.number_buckets)?;
  29. write!(
  30. f,
  31. "hash_function_descriptions: {:?}",
  32. self.hash_function_descriptions
  33. )?;
  34. write!(f, "}}")?;
  35. Ok(())
  36. }
  37. }
  38. impl<H: HashFunction<Value>, Value> Copy for Parameters<H, Value>
  39. where
  40. Value: HashFunctionValue,
  41. <Value as TryInto<usize>>::Error: Debug,
  42. {
  43. }
  44. impl<H: HashFunction<Value>, Value> Clone for Parameters<H, Value>
  45. where
  46. Value: HashFunctionValue,
  47. <Value as TryInto<usize>>::Error: Debug,
  48. {
  49. fn clone(&self) -> Self {
  50. *self
  51. }
  52. }
  53. impl<H: HashFunction<Value>, Value> bincode::Encode for Parameters<H, Value>
  54. where
  55. Value: HashFunctionValue,
  56. <Value as TryInto<usize>>::Error: Debug,
  57. H::Description: bincode::Encode,
  58. {
  59. fn encode<E: bincode::enc::Encoder>(
  60. &self,
  61. encoder: &mut E,
  62. ) -> core::result::Result<(), bincode::error::EncodeError> {
  63. bincode::Encode::encode(&self.number_inputs, encoder)?;
  64. bincode::Encode::encode(&self.number_buckets, encoder)?;
  65. bincode::Encode::encode(&self.hash_function_descriptions, encoder)?;
  66. Ok(())
  67. }
  68. }
  69. impl<H: HashFunction<Value>, Value> bincode::Decode for Parameters<H, Value>
  70. where
  71. Value: HashFunctionValue,
  72. <Value as TryInto<usize>>::Error: Debug,
  73. H::Description: bincode::Decode + 'static,
  74. {
  75. fn decode<D: bincode::de::Decoder>(
  76. decoder: &mut D,
  77. ) -> core::result::Result<Self, bincode::error::DecodeError> {
  78. Ok(Self {
  79. number_inputs: bincode::Decode::decode(decoder)?,
  80. number_buckets: bincode::Decode::decode(decoder)?,
  81. hash_function_descriptions: bincode::Decode::decode(decoder)?,
  82. })
  83. }
  84. }
  85. impl<H: HashFunction<Value>, Value> Parameters<H, Value>
  86. where
  87. Value: HashFunctionValue,
  88. <Value as TryInto<usize>>::Error: Debug,
  89. {
  90. /// Samples three hash functions from given seed
  91. pub fn from_seed(number_inputs: usize, seed: [u8; 32]) -> Self {
  92. let number_buckets = Self::compute_number_buckets(number_inputs);
  93. let mut rng = ChaCha12Rng::from_seed(seed);
  94. let hash_function_descriptions =
  95. array::from_fn(|_| H::from_seed(number_buckets, rng.gen()).to_description());
  96. Parameters::<H, Value> {
  97. number_inputs,
  98. number_buckets,
  99. hash_function_descriptions,
  100. }
  101. }
  102. /// Samples three hash functions randomly
  103. pub fn sample(number_inputs: usize) -> Self {
  104. let number_buckets = Self::compute_number_buckets(number_inputs);
  105. let hash_function_descriptions =
  106. array::from_fn(|_| H::sample(number_buckets).to_description());
  107. Parameters::<H, Value> {
  108. number_inputs,
  109. number_buckets,
  110. hash_function_descriptions,
  111. }
  112. }
  113. /// Compute how many buckets we need for the cuckoo table
  114. fn compute_number_buckets(number_inputs: usize) -> usize {
  115. assert_ne!(number_inputs, 0);
  116. let statistical_security_parameter = 40;
  117. // computation taken from here:
  118. // https://github.com/ladnir/cryptoTools/blob/85da63e335c3ad3019af3958b48d3ff6750c3d92/cryptoTools/Common/CuckooIndex.cpp#L129-L150
  119. let log_number_inputs = (number_inputs as f64).log2().ceil();
  120. let a_max = 123.5;
  121. let b_max = -130.0;
  122. let a_sd = 2.3;
  123. let b_sd = 2.18;
  124. let a_mean = 6.3;
  125. let b_mean = 6.45;
  126. let a = a_max / 2.0 * (1.0 + erf((log_number_inputs - a_mean) / (a_sd * SQRT_2)));
  127. let b = b_max / 2.0 * (1.0 + erf((log_number_inputs - b_mean) / (b_sd * SQRT_2)))
  128. - log_number_inputs;
  129. let e = (statistical_security_parameter as f64 - b) / a + 0.3;
  130. (e * number_inputs as f64).ceil() as usize
  131. }
  132. /// Return the number of buckets
  133. pub fn get_number_buckets(&self) -> usize {
  134. self.number_buckets
  135. }
  136. /// Return the number of inputs these parameters are specified for
  137. pub fn get_number_inputs(&self) -> usize {
  138. self.number_inputs
  139. }
  140. }
  141. pub struct Hasher<H: HashFunction<Value>, Value: HashFunctionValue>
  142. where
  143. <Value as TryInto<usize>>::Error: Debug,
  144. {
  145. parameters: Parameters<H, Value>,
  146. hash_functions: [H; 3],
  147. }
  148. impl<H: HashFunction<Value>, Value: HashFunctionValue> Hasher<H, Value>
  149. where
  150. <Value as TryInto<usize>>::Error: Debug,
  151. {
  152. pub const UNOCCUPIED: u64 = u64::MAX;
  153. /// Create `Hasher` object with given parameters
  154. pub fn new(parameters: Parameters<H, Value>) -> Self {
  155. let hash_functions =
  156. array::from_fn(|i| H::from_description(parameters.hash_function_descriptions[i]));
  157. Hasher {
  158. parameters,
  159. hash_functions,
  160. }
  161. }
  162. /// Return the parameters
  163. pub fn get_parameters(&self) -> &Parameters<H, Value> {
  164. &self.parameters
  165. }
  166. /// Hash a single item with the given hash function
  167. pub fn hash_single(&self, hash_function_index: usize, item: u64) -> Value {
  168. assert!(hash_function_index < NUMBER_HASH_FUNCTIONS);
  169. self.hash_functions[hash_function_index].hash_single(item)
  170. }
  171. /// Hash the whole domain [0, domain_size) with all three hash functions
  172. pub fn hash_domain(&self, domain_size: u64) -> [Vec<Value>; NUMBER_HASH_FUNCTIONS] {
  173. array::from_fn(|i| self.hash_functions[i].hash_range(0..domain_size))
  174. }
  175. /// Hash the given items with all three hash functions
  176. pub fn hash_items(&self, items: &[u64]) -> [Vec<Value>; NUMBER_HASH_FUNCTIONS] {
  177. array::from_fn(|i| self.hash_functions[i].hash_slice(items))
  178. }
  179. /// Hash the whole domain [0, domain_size) into buckets with all three hash functions
  180. /// using precomputed hashes
  181. pub fn hash_domain_into_buckets_given_hashes(
  182. &self,
  183. domain_size: u64,
  184. hashes: &[Vec<Value>; NUMBER_HASH_FUNCTIONS],
  185. ) -> Vec<Vec<u64>> {
  186. debug_assert!(hashes.iter().all(|v| v.len() as u64 == domain_size));
  187. debug_assert_eq!(hashes.len(), NUMBER_HASH_FUNCTIONS);
  188. let mut hash_table = vec![Vec::new(); self.parameters.number_buckets];
  189. for x in 0..domain_size {
  190. for hash_function_values in hashes.iter() {
  191. let h = hash_function_values[x as usize];
  192. hash_table[H::hash_value_as_usize(h)].push(x);
  193. }
  194. }
  195. hash_table
  196. }
  197. /// Hash the whole domain [0, domain_size) into buckets with all three hash functions
  198. pub fn hash_domain_into_buckets(&self, domain_size: u64) -> Vec<Vec<u64>> {
  199. self.hash_domain_into_buckets_given_hashes(domain_size, &self.hash_domain(domain_size))
  200. }
  201. /// Hash the given items into buckets all three hash functions
  202. pub fn hash_items_into_buckets(&self, items: &[u64]) -> Vec<Vec<u64>> {
  203. let mut hash_table = vec![Vec::new(); self.parameters.number_buckets];
  204. let hashes = self.hash_items(items);
  205. debug_assert_eq!(hashes.len(), NUMBER_HASH_FUNCTIONS);
  206. for (i, &x) in items.iter().enumerate() {
  207. for hash_function_values in hashes.iter() {
  208. let h = hash_function_values[i];
  209. hash_table[H::hash_value_as_usize(h)].push(x);
  210. }
  211. }
  212. hash_table
  213. }
  214. /// Compute a vector of the sizes of all buckets
  215. pub fn compute_bucket_sizes(hash_table: &[Vec<u64>]) -> Vec<usize> {
  216. hash_table.iter().map(|v| v.len()).collect()
  217. }
  218. /// Compute a lookup table for the position map:
  219. /// bucket_i x item_j |-> index of item_j in bucket_i
  220. /// The table stores three (bucket, index) pairs for every item of the domain, since each item
  221. /// is placed into buckets using three hash functions.
  222. pub fn compute_pos_lookup_table(
  223. domain_size: u64,
  224. hash_table: &[Vec<u64>],
  225. ) -> Vec<[(usize, usize); 3]> {
  226. let mut lookup_table = vec![[(usize::MAX, usize::MAX); 3]; domain_size as usize];
  227. for (bucket_i, bucket) in hash_table.iter().enumerate() {
  228. for (item_j, &item) in bucket.iter().enumerate() {
  229. debug_assert!(item < domain_size);
  230. for k in 0..NUMBER_HASH_FUNCTIONS {
  231. if lookup_table[item as usize][k] == (usize::MAX, usize::MAX) {
  232. lookup_table[item as usize][k] = (bucket_i, item_j);
  233. break;
  234. }
  235. }
  236. }
  237. }
  238. lookup_table
  239. }
  240. /// Use the lookup table for the position map
  241. pub fn pos_lookup(lookup_table: &[[(usize, usize); 3]], bucket_i: usize, item: u64) -> u64 {
  242. for k in 0..NUMBER_HASH_FUNCTIONS {
  243. if lookup_table[item as usize][k].0 == bucket_i {
  244. return lookup_table[item as usize][k].1 as u64;
  245. }
  246. }
  247. panic!("logic error");
  248. }
  249. /// Perform cuckoo hashing to place the given items into a vector
  250. /// NB: number of items must match the number of items used to generate the parameters
  251. pub fn cuckoo_hash_items(&self, items: &[u64]) -> (Vec<u64>, Vec<usize>) {
  252. let number_inputs = self.parameters.number_inputs;
  253. let number_buckets = self.parameters.number_buckets;
  254. assert_eq!(
  255. items.len(),
  256. number_inputs,
  257. "#items must match number inputs specified in the parameters"
  258. );
  259. // create cuckoo hash table to store all inputs
  260. // we use u64::MAX to denote an empty entry
  261. let mut cuckoo_table = vec![Self::UNOCCUPIED; self.parameters.number_buckets];
  262. // store the indices of the items mapped into each bucket
  263. let mut cuckoo_table_indices = vec![0usize; number_buckets];
  264. let hashes = self.hash_items(items);
  265. // keep track of which hash function we need to use next for an item
  266. let mut next_hash_function = vec![0usize; number_buckets];
  267. // if we need more than this number of steps to insert an item, we have found
  268. // a cycle (this should only happen with negligible probability if the
  269. // parameters are chosen correctly)
  270. // const auto max_number_tries = NUMBER_HASH_FUNCTIONS * number_inputs_;
  271. let max_number_tries = number_inputs + 1;
  272. for input_j in 0..number_inputs {
  273. let mut index = input_j;
  274. let mut item = items[index];
  275. let mut try_k = 0;
  276. while try_k < max_number_tries {
  277. // try to (re)insert item with current index
  278. let hash: usize = H::hash_value_as_usize(hashes[next_hash_function[index]][index]);
  279. // increment hash function counter for this item s.t. we use the next hash
  280. // function next time
  281. next_hash_function[index] = (next_hash_function[index] + 1) % NUMBER_HASH_FUNCTIONS;
  282. if cuckoo_table[hash] == Self::UNOCCUPIED {
  283. // the bucket was free, so we can insert the item
  284. cuckoo_table[hash] = item;
  285. cuckoo_table_indices[hash] = index;
  286. break;
  287. }
  288. // the bucket was occupied, so we evict the item in the table and insert
  289. // it with the next hash function
  290. (cuckoo_table[hash], item) = (item, cuckoo_table[hash]);
  291. (cuckoo_table_indices[hash], index) = (index, cuckoo_table_indices[hash]);
  292. try_k += 1;
  293. }
  294. if try_k >= max_number_tries {
  295. panic!("cycle detected"); // TODO: error handling
  296. }
  297. }
  298. (cuckoo_table, cuckoo_table_indices)
  299. }
  300. }
  301. #[cfg(test)]
  302. mod tests {
  303. use super::*;
  304. use crate::hash::AesHashFunction;
  305. use rand::{seq::SliceRandom, thread_rng, Rng};
  306. fn gen_random_numbers(n: usize) -> Vec<u64> {
  307. (0..n).map(|_| thread_rng().gen()).collect()
  308. }
  309. fn create_hasher<H: HashFunction<Value>, Value: HashFunctionValue>(
  310. number_inputs: usize,
  311. ) -> Hasher<H, Value>
  312. where
  313. <Value as TryInto<usize>>::Error: Debug,
  314. {
  315. let params = Parameters::<H, Value>::sample(number_inputs);
  316. Hasher::<H, Value>::new(params)
  317. }
  318. fn test_hash_cuckoo_with_param<H: HashFunction<Value>, Value: HashFunctionValue>(
  319. log_number_inputs: usize,
  320. ) where
  321. <Value as TryInto<usize>>::Error: Debug,
  322. {
  323. let number_inputs = 1 << log_number_inputs;
  324. let inputs = gen_random_numbers(number_inputs);
  325. let cuckoo = create_hasher::<H, Value>(number_inputs);
  326. let (cuckoo_table_items, cuckoo_table_indices) = cuckoo.cuckoo_hash_items(&inputs);
  327. let number_buckets = cuckoo.get_parameters().get_number_buckets();
  328. // check dimensions
  329. assert_eq!(cuckoo_table_items.len(), number_buckets);
  330. assert_eq!(cuckoo_table_indices.len(), number_buckets);
  331. // check that we have the right number of things in the table
  332. let num_unoccupied_entries = cuckoo_table_items
  333. .iter()
  334. .copied()
  335. .filter(|&x| x == Hasher::<H, Value>::UNOCCUPIED)
  336. .count();
  337. assert_eq!(number_buckets - num_unoccupied_entries, number_inputs);
  338. // keep track of which items we have seen in the cuckoo table
  339. let mut found_inputs_in_table = vec![false; number_inputs];
  340. for bucket_i in 0..number_buckets {
  341. if cuckoo_table_items[bucket_i] != Hasher::<H, Value>::UNOCCUPIED {
  342. let index = cuckoo_table_indices[bucket_i];
  343. // check that the right item is here
  344. assert_eq!(cuckoo_table_items[bucket_i], inputs[index]);
  345. // check that we have not yet seen this item
  346. assert!(!found_inputs_in_table[index]);
  347. // remember that we have seen this item
  348. found_inputs_in_table[index] = true;
  349. }
  350. }
  351. // check that we have found all inputs in the cuckoo table
  352. assert!(found_inputs_in_table.iter().all(|&x| x));
  353. }
  354. fn test_hash_domain_into_buckets_with_param<H: HashFunction<Value>, Value: HashFunctionValue>(
  355. log_number_inputs: usize,
  356. ) where
  357. <Value as TryInto<usize>>::Error: Debug,
  358. {
  359. let number_inputs = 1 << log_number_inputs;
  360. let cuckoo = create_hasher::<H, Value>(number_inputs);
  361. let domain_size = 1 << 10;
  362. let number_buckets = cuckoo.get_parameters().get_number_buckets();
  363. let hash_table = cuckoo.hash_domain_into_buckets(domain_size);
  364. assert_eq!(hash_table.len(), number_buckets);
  365. for bucket in &hash_table {
  366. // Check that items inside each bucket are sorted
  367. // assert!(bucket.iter().is_sorted()); // `is_sorted` is currently nightly only
  368. assert!(bucket.windows(2).all(|w| w[0] <= w[1]))
  369. }
  370. // Check that hashing is deterministic
  371. let hash_table2 = cuckoo.hash_domain_into_buckets(domain_size);
  372. assert_eq!(hash_table, hash_table2);
  373. let hashes = cuckoo.hash_domain(domain_size);
  374. for element in 0..domain_size {
  375. if hashes[0][element as usize] == hashes[1][element as usize]
  376. && hashes[0][element as usize] == hashes[2][element as usize]
  377. {
  378. let hash = H::hash_value_as_usize(hashes[0][element as usize]);
  379. let idx_start = hash_table[hash]
  380. .as_slice()
  381. .partition_point(|x| x < &element);
  382. let idx_end = hash_table[hash]
  383. .as_slice()
  384. .partition_point(|x| x <= &element);
  385. // check that the element is in the bucket
  386. assert_ne!(idx_start, hash_table[hash].len());
  387. assert_eq!(hash_table[hash][idx_start], element);
  388. // check that the element occurs three times
  389. assert_eq!(idx_end - idx_start, 3);
  390. } else if hashes[0][element as usize] == hashes[1][element as usize] {
  391. let hash = H::hash_value_as_usize(hashes[0][element as usize]);
  392. let idx_start = hash_table[hash]
  393. .as_slice()
  394. .partition_point(|x| x < &element);
  395. let idx_end = hash_table[hash]
  396. .as_slice()
  397. .partition_point(|x| x <= &element);
  398. // check that the element is in the bucket
  399. assert_ne!(idx_start, hash_table[hash].len());
  400. assert_eq!(hash_table[hash][idx_start], element);
  401. // check that the element occurs two times
  402. assert_eq!(idx_end - idx_start, 2);
  403. let hash_other = H::hash_value_as_usize(hashes[2][element as usize]);
  404. assert!(hash_table[hash_other]
  405. .as_slice()
  406. .binary_search(&element)
  407. .is_ok());
  408. } else if hashes[0][element as usize] == hashes[2][element as usize] {
  409. let hash = H::hash_value_as_usize(hashes[0][element as usize]);
  410. let idx_start = hash_table[hash]
  411. .as_slice()
  412. .partition_point(|x| x < &element);
  413. let idx_end = hash_table[hash]
  414. .as_slice()
  415. .partition_point(|x| x <= &element);
  416. // check that the element is in the bucket
  417. assert_ne!(idx_start, hash_table[hash].len());
  418. assert_eq!(hash_table[hash][idx_start], element);
  419. // check that the element occurs two times
  420. assert_eq!(idx_end - idx_start, 2);
  421. let hash_other = H::hash_value_as_usize(hashes[1][element as usize]);
  422. assert!(hash_table[hash_other]
  423. .as_slice()
  424. .binary_search(&element)
  425. .is_ok());
  426. } else if hashes[1][element as usize] == hashes[2][element as usize] {
  427. let hash = H::hash_value_as_usize(hashes[1][element as usize]);
  428. let idx_start = hash_table[hash]
  429. .as_slice()
  430. .partition_point(|x| x < &element);
  431. let idx_end = hash_table[hash]
  432. .as_slice()
  433. .partition_point(|x| x <= &element);
  434. // check that the element is in the bucket
  435. assert_ne!(idx_start, hash_table[hash].len());
  436. assert_eq!(hash_table[hash][idx_start], element);
  437. // check that the element occurs two times
  438. assert_eq!(idx_end - idx_start, 2);
  439. let hash_other = H::hash_value_as_usize(hashes[0][element as usize]);
  440. assert!(hash_table[hash_other]
  441. .as_slice()
  442. .binary_search(&element)
  443. .is_ok());
  444. } else {
  445. for hash_j in 0..NUMBER_HASH_FUNCTIONS {
  446. let hash = H::hash_value_as_usize(hashes[hash_j][element as usize]);
  447. assert!(hash_table[hash].as_slice().binary_search(&element).is_ok());
  448. }
  449. }
  450. }
  451. let num_items_in_hash_table: usize = hash_table.iter().map(|v| v.len()).sum();
  452. assert_eq!(num_items_in_hash_table as u64, 3 * domain_size);
  453. }
  454. fn test_position_map_precomputation_with_param<
  455. H: HashFunction<Value>,
  456. Value: HashFunctionValue,
  457. >(
  458. log_number_inputs: usize,
  459. ) where
  460. <Value as TryInto<usize>>::Error: Debug,
  461. {
  462. let number_inputs = 1 << log_number_inputs;
  463. let cuckoo = create_hasher::<H, Value>(number_inputs);
  464. let domain_size = 1 << 10;
  465. let hash_table = cuckoo.hash_domain_into_buckets(domain_size);
  466. let lookup_table = Hasher::<H, Value>::compute_pos_lookup_table(domain_size, &hash_table);
  467. let pos = |bucket_i: usize, item: u64| -> u64 {
  468. let idx = hash_table[bucket_i].partition_point(|x| x < &item);
  469. assert!(idx != hash_table[bucket_i].len());
  470. assert_eq!(item, hash_table[bucket_i][idx]);
  471. assert!(idx == 0 || hash_table[bucket_i][idx - 1] != item);
  472. idx as u64
  473. };
  474. for (bucket_i, bucket) in hash_table.iter().enumerate() {
  475. for &item in bucket.iter() {
  476. assert_eq!(
  477. Hasher::<H, Value>::pos_lookup(&lookup_table, bucket_i, item),
  478. pos(bucket_i, item)
  479. );
  480. }
  481. }
  482. }
  483. fn test_buckets_cuckoo_consistency_with_param<
  484. H: HashFunction<Value>,
  485. Value: HashFunctionValue,
  486. >(
  487. number_inputs: usize,
  488. ) where
  489. <Value as TryInto<usize>>::Error: Debug,
  490. {
  491. let domain_size = 1 << 10;
  492. let cuckoo = create_hasher::<H, Value>(number_inputs);
  493. // To generate random numbers in the domain, we generate the entire domain and do a random shuffle
  494. let shuffled_domain = {
  495. let mut vec: Vec<u64> = (0..domain_size).collect();
  496. vec.shuffle(&mut thread_rng());
  497. vec
  498. };
  499. // Checking that every element in a cuckoo bucket exists in the corresponding bucket of HashSimpleDomain
  500. let hash_table = cuckoo.hash_domain_into_buckets(domain_size);
  501. let (cuckoo_table_items, _) = cuckoo.cuckoo_hash_items(&shuffled_domain[..number_inputs]);
  502. let number_buckets = cuckoo.get_parameters().get_number_buckets();
  503. for bucket_i in 0..number_buckets {
  504. if cuckoo_table_items[bucket_i] != Hasher::<H, Value>::UNOCCUPIED {
  505. assert!(hash_table[bucket_i]
  506. .as_slice()
  507. .binary_search(&cuckoo_table_items[bucket_i])
  508. .is_ok());
  509. }
  510. }
  511. }
  512. #[test]
  513. fn test_hash_cuckoo() {
  514. for n in 5..10 {
  515. test_hash_cuckoo_with_param::<AesHashFunction<u32>, u32>(n);
  516. }
  517. }
  518. #[test]
  519. fn test_hash_domain_into_buckets() {
  520. for n in 5..10 {
  521. test_hash_domain_into_buckets_with_param::<AesHashFunction<u32>, u32>(n);
  522. }
  523. }
  524. #[test]
  525. fn test_position_map_precomputation() {
  526. for n in 5..10 {
  527. test_position_map_precomputation_with_param::<AesHashFunction<u32>, u32>(n);
  528. }
  529. }
  530. #[test]
  531. fn test_buckets_cuckoo_consistency() {
  532. for n in 5..10 {
  533. test_buckets_cuckoo_consistency_with_param::<AesHashFunction<u32>, u32>(n);
  534. }
  535. }
  536. }