cuckoo.rs 23 KB

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