mpdpf.rs 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601
  1. use core::fmt;
  2. use core::fmt::Debug;
  3. use core::marker::PhantomData;
  4. use core::ops::{Add, AddAssign};
  5. use num::traits::Zero;
  6. use crate::spdpf::SinglePointDpf;
  7. use cuckoo::{
  8. cuckoo::Hasher as CuckooHasher, cuckoo::Parameters as CuckooParameters,
  9. cuckoo::NUMBER_HASH_FUNCTIONS as CUCKOO_NUMBER_HASH_FUNCTIONS, hash::HashFunction,
  10. };
  11. pub trait MultiPointDpfKey: Clone + Debug {
  12. fn get_party_id(&self) -> usize;
  13. fn get_domain_size(&self) -> usize;
  14. fn get_number_points(&self) -> usize;
  15. }
  16. pub trait MultiPointDpf {
  17. type Key: MultiPointDpfKey;
  18. type Value: Add<Output = Self::Value> + Copy + Debug + Eq + Zero;
  19. fn new(domain_size: usize, number_points: usize) -> Self;
  20. fn get_domain_size(&self) -> usize;
  21. fn get_number_points(&self) -> usize;
  22. fn precompute(&mut self) {}
  23. fn generate_keys(&self, alphas: &[u64], betas: &[Self::Value]) -> (Self::Key, Self::Key);
  24. fn evaluate_at(&self, key: &Self::Key, index: u64) -> Self::Value;
  25. fn evaluate_domain(&self, key: &Self::Key) -> Vec<Self::Value> {
  26. (0..key.get_domain_size())
  27. .map(|x| self.evaluate_at(&key, x as u64))
  28. .collect()
  29. }
  30. }
  31. #[derive(Clone, Debug)]
  32. pub struct DummyMpDpfKey<V: Copy + Debug> {
  33. party_id: usize,
  34. domain_size: usize,
  35. number_points: usize,
  36. alphas: Vec<u64>,
  37. betas: Vec<V>,
  38. }
  39. impl<V> MultiPointDpfKey for DummyMpDpfKey<V>
  40. where
  41. V: Copy + Debug,
  42. {
  43. fn get_party_id(&self) -> usize {
  44. self.party_id
  45. }
  46. fn get_domain_size(&self) -> usize {
  47. self.domain_size
  48. }
  49. fn get_number_points(&self) -> usize {
  50. self.number_points
  51. }
  52. }
  53. pub struct DummyMpDpf<V>
  54. where
  55. V: Add<Output = V> + Copy + Debug + Eq + Zero,
  56. {
  57. domain_size: usize,
  58. number_points: usize,
  59. phantom: PhantomData<V>,
  60. }
  61. impl<V> MultiPointDpf for DummyMpDpf<V>
  62. where
  63. V: Add<Output = V> + Copy + Debug + Eq + Zero,
  64. {
  65. type Key = DummyMpDpfKey<V>;
  66. type Value = V;
  67. fn new(domain_size: usize, number_points: usize) -> Self {
  68. Self {
  69. domain_size,
  70. number_points,
  71. phantom: PhantomData,
  72. }
  73. }
  74. fn get_domain_size(&self) -> usize {
  75. self.domain_size
  76. }
  77. fn get_number_points(&self) -> usize {
  78. self.number_points
  79. }
  80. fn generate_keys(&self, alphas: &[u64], betas: &[V]) -> (Self::Key, Self::Key) {
  81. assert_eq!(
  82. alphas.len(),
  83. self.number_points,
  84. "number of points does not match constructor argument"
  85. );
  86. assert_eq!(
  87. alphas.len(),
  88. betas.len(),
  89. "alphas and betas must be the same size"
  90. );
  91. assert!(
  92. alphas
  93. .iter()
  94. .all(|&alpha| alpha < (self.domain_size as u64)),
  95. "all alphas must be in the domain"
  96. );
  97. assert!(alphas.windows(2).all(|w| w[0] <= w[1]));
  98. (
  99. DummyMpDpfKey {
  100. party_id: 0,
  101. domain_size: self.domain_size,
  102. number_points: self.number_points,
  103. alphas: alphas.iter().copied().collect(),
  104. betas: betas.iter().copied().collect(),
  105. },
  106. DummyMpDpfKey {
  107. party_id: 1,
  108. domain_size: self.domain_size,
  109. number_points: self.number_points,
  110. alphas: alphas.iter().copied().collect(),
  111. betas: betas.iter().copied().collect(),
  112. },
  113. )
  114. }
  115. fn evaluate_at(&self, key: &Self::Key, index: u64) -> V {
  116. assert_eq!(self.domain_size, key.domain_size);
  117. assert_eq!(self.number_points, key.number_points);
  118. if key.get_party_id() == 0 {
  119. match key.alphas.binary_search(&index) {
  120. Ok(i) => key.betas[i],
  121. Err(_) => V::zero(),
  122. }
  123. } else {
  124. V::zero()
  125. }
  126. }
  127. }
  128. pub struct SmartMpDpfKey<SPDPF, H>
  129. where
  130. SPDPF: SinglePointDpf,
  131. H: HashFunction<u16>,
  132. {
  133. party_id: usize,
  134. domain_size: usize,
  135. number_points: usize,
  136. spdpf_keys: Vec<Option<SPDPF::Key>>,
  137. cuckoo_parameters: CuckooParameters<H, u16>,
  138. }
  139. impl<SPDPF, H> Debug for SmartMpDpfKey<SPDPF, H>
  140. where
  141. SPDPF: SinglePointDpf,
  142. H: HashFunction<u16>,
  143. {
  144. fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
  145. let (newline, indentation) = if f.alternate() {
  146. ("\n", " ")
  147. } else {
  148. (" ", "")
  149. };
  150. write!(f, "SmartMpDpfKey<SPDPF, H>{{{}", newline)?;
  151. write!(
  152. f,
  153. "{}party_id: {:?},{}",
  154. indentation, self.party_id, newline
  155. )?;
  156. write!(
  157. f,
  158. "{}domain_size: {:?},{}",
  159. indentation, self.domain_size, newline
  160. )?;
  161. write!(
  162. f,
  163. "{}number_points: {:?},{}",
  164. indentation, self.number_points, newline
  165. )?;
  166. if f.alternate() {
  167. write!(f, " spdpf_keys:\n")?;
  168. for (i, k) in self.spdpf_keys.iter().enumerate() {
  169. write!(f, " spdpf_keys[{}]: {:?}\n", i, k)?;
  170. }
  171. } else {
  172. write!(f, " spdpf_keys: {:?},", self.spdpf_keys)?;
  173. }
  174. write!(
  175. f,
  176. "{}cuckoo_parameters: {:?}{}",
  177. indentation, self.cuckoo_parameters, newline
  178. )?;
  179. write!(f, "}}")?;
  180. Ok(())
  181. }
  182. }
  183. impl<SPDPF, H> Clone for SmartMpDpfKey<SPDPF, H>
  184. where
  185. SPDPF: SinglePointDpf,
  186. H: HashFunction<u16>,
  187. {
  188. fn clone(&self) -> Self {
  189. Self {
  190. party_id: self.party_id,
  191. domain_size: self.domain_size,
  192. number_points: self.number_points,
  193. spdpf_keys: self.spdpf_keys.clone(),
  194. cuckoo_parameters: self.cuckoo_parameters.clone(),
  195. }
  196. }
  197. }
  198. impl<SPDPF, H> MultiPointDpfKey for SmartMpDpfKey<SPDPF, H>
  199. where
  200. SPDPF: SinglePointDpf,
  201. H: HashFunction<u16>,
  202. {
  203. fn get_party_id(&self) -> usize {
  204. self.party_id
  205. }
  206. fn get_domain_size(&self) -> usize {
  207. self.domain_size
  208. }
  209. fn get_number_points(&self) -> usize {
  210. self.number_points
  211. }
  212. }
  213. struct SmartMpDpfPrecomputationData<H: HashFunction<u16>> {
  214. pub cuckoo_parameters: CuckooParameters<H, u16>,
  215. pub hasher: CuckooHasher<H, u16>,
  216. pub hashes: [Vec<u16>; CUCKOO_NUMBER_HASH_FUNCTIONS],
  217. pub simple_htable: Vec<Vec<u64>>,
  218. pub bucket_sizes: Vec<usize>,
  219. pub position_map_lookup_table: Vec<[(usize, usize); 3]>,
  220. }
  221. pub struct SmartMpDpf<V, SPDPF, H>
  222. where
  223. V: Add<Output = V> + AddAssign + Copy + Debug + Eq + Zero,
  224. SPDPF: SinglePointDpf<Value = V>,
  225. H: HashFunction<u16>,
  226. {
  227. domain_size: usize,
  228. number_points: usize,
  229. precomputation_data: Option<SmartMpDpfPrecomputationData<H>>,
  230. phantom_v: PhantomData<V>,
  231. phantom_s: PhantomData<SPDPF>,
  232. phantom_h: PhantomData<H>,
  233. }
  234. impl<V, SPDPF, H> SmartMpDpf<V, SPDPF, H>
  235. where
  236. V: Add<Output = V> + AddAssign + Copy + Debug + Eq + Zero,
  237. SPDPF: SinglePointDpf<Value = V>,
  238. H: HashFunction<u16>,
  239. {
  240. fn precompute_hashes(
  241. domain_size: usize,
  242. number_points: usize,
  243. ) -> SmartMpDpfPrecomputationData<H> {
  244. let seed: [u8; 32] = [
  245. 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42,
  246. 42, 42, 42, 42, 42, 42, 42, 42, 42, 42,
  247. ];
  248. let cuckoo_parameters = CuckooParameters::from_seed(number_points, seed);
  249. assert!(
  250. cuckoo_parameters.get_number_buckets() < (1 << u16::BITS),
  251. "too many buckets, use larger type for hash values"
  252. );
  253. let hasher = CuckooHasher::<H, u16>::new(cuckoo_parameters);
  254. let hashes = hasher.hash_domain(domain_size as u64);
  255. let simple_htable =
  256. hasher.hash_domain_into_buckets_given_hashes(domain_size as u64, &hashes);
  257. let bucket_sizes = CuckooHasher::<H, u16>::compute_bucket_sizes(&simple_htable);
  258. let position_map_lookup_table =
  259. CuckooHasher::<H, u16>::compute_pos_lookup_table(domain_size as u64, &simple_htable);
  260. SmartMpDpfPrecomputationData {
  261. cuckoo_parameters,
  262. hasher,
  263. hashes,
  264. simple_htable,
  265. bucket_sizes,
  266. position_map_lookup_table,
  267. }
  268. }
  269. }
  270. impl<V, SPDPF, H> MultiPointDpf for SmartMpDpf<V, SPDPF, H>
  271. where
  272. V: Add<Output = V> + AddAssign + Copy + Debug + Eq + Zero,
  273. SPDPF: SinglePointDpf<Value = V>,
  274. H: HashFunction<u16>,
  275. {
  276. type Key = SmartMpDpfKey<SPDPF, H>;
  277. type Value = V;
  278. fn new(domain_size: usize, number_points: usize) -> Self {
  279. assert!(domain_size < (1 << u32::BITS));
  280. Self {
  281. domain_size,
  282. number_points,
  283. precomputation_data: None,
  284. phantom_v: PhantomData,
  285. phantom_s: PhantomData,
  286. phantom_h: PhantomData,
  287. }
  288. }
  289. fn get_domain_size(&self) -> usize {
  290. self.domain_size
  291. }
  292. fn get_number_points(&self) -> usize {
  293. self.domain_size
  294. }
  295. fn precompute(&mut self) {
  296. self.precomputation_data = Some(Self::precompute_hashes(
  297. self.domain_size,
  298. self.number_points,
  299. ));
  300. }
  301. fn generate_keys(&self, alphas: &[u64], betas: &[Self::Value]) -> (Self::Key, Self::Key) {
  302. assert_eq!(alphas.len(), betas.len());
  303. debug_assert!(alphas.windows(2).all(|w| w[0] < w[1]));
  304. debug_assert!(alphas.iter().all(|&alpha| alpha < self.domain_size as u64));
  305. let number_points = alphas.len();
  306. // if not data is precomputed, do it now
  307. let mut precomputation_data_fresh: Option<SmartMpDpfPrecomputationData<H>> = None;
  308. if self.precomputation_data.is_none() {
  309. precomputation_data_fresh = Some(Self::precompute_hashes(
  310. self.domain_size,
  311. self.number_points,
  312. ));
  313. }
  314. // select either the precomputed or the freshly computed data
  315. let precomputation_data = self
  316. .precomputation_data
  317. .as_ref()
  318. .unwrap_or_else(|| precomputation_data_fresh.as_ref().unwrap());
  319. let cuckoo_parameters = &precomputation_data.cuckoo_parameters;
  320. let hasher = &precomputation_data.hasher;
  321. let (cuckoo_table_items, cuckoo_table_indices) = hasher.cuckoo_hash_items(alphas);
  322. let position_map_lookup_table = &precomputation_data.position_map_lookup_table;
  323. let pos = |bucket_i: usize, item: u64| -> u64 {
  324. CuckooHasher::<H, u16>::pos_lookup(position_map_lookup_table, bucket_i, item)
  325. };
  326. let number_buckets = hasher.get_parameters().get_number_buckets();
  327. let bucket_sizes = &precomputation_data.bucket_sizes;
  328. let mut keys_0 = Vec::<Option<SPDPF::Key>>::with_capacity(number_buckets);
  329. let mut keys_1 = Vec::<Option<SPDPF::Key>>::with_capacity(number_buckets);
  330. for bucket_i in 0..number_buckets {
  331. // if bucket is empty, add invalid dummy keys to the arrays to make the
  332. // indices work
  333. if bucket_sizes[bucket_i] == 0 {
  334. keys_0.push(None);
  335. keys_1.push(None);
  336. continue;
  337. }
  338. let (alpha, beta) =
  339. if cuckoo_table_items[bucket_i] != CuckooHasher::<H, u16>::UNOCCUPIED {
  340. let alpha = pos(bucket_i, cuckoo_table_items[bucket_i]);
  341. let beta = betas[cuckoo_table_indices[bucket_i]];
  342. (alpha, beta)
  343. } else {
  344. (0, V::zero())
  345. };
  346. let (key_0, key_1) = SPDPF::generate_keys(bucket_sizes[bucket_i], alpha, beta);
  347. keys_0.push(Some(key_0));
  348. keys_1.push(Some(key_1));
  349. }
  350. (
  351. SmartMpDpfKey::<SPDPF, H> {
  352. party_id: 0,
  353. domain_size: self.domain_size,
  354. number_points,
  355. spdpf_keys: keys_0,
  356. cuckoo_parameters: cuckoo_parameters.clone(),
  357. },
  358. SmartMpDpfKey::<SPDPF, H> {
  359. party_id: 1,
  360. domain_size: self.domain_size,
  361. number_points,
  362. spdpf_keys: keys_1,
  363. cuckoo_parameters: cuckoo_parameters.clone(),
  364. },
  365. )
  366. }
  367. fn evaluate_at(&self, key: &Self::Key, index: u64) -> Self::Value {
  368. assert_eq!(self.domain_size, key.domain_size);
  369. assert_eq!(self.number_points, key.number_points);
  370. assert_eq!(key.domain_size, self.domain_size);
  371. assert!(index < self.domain_size as u64);
  372. let hasher = CuckooHasher::<H, u16>::new(key.cuckoo_parameters);
  373. let hashes = hasher.hash_items(&[index]);
  374. let simple_htable = hasher.hash_domain_into_buckets(self.domain_size as u64);
  375. let pos = |bucket_i: usize, item: u64| -> u64 {
  376. let idx = simple_htable[bucket_i].partition_point(|x| x < &item);
  377. debug_assert!(idx != simple_htable[bucket_i].len());
  378. debug_assert_eq!(item, simple_htable[bucket_i][idx]);
  379. debug_assert!(idx == 0 || simple_htable[bucket_i][idx - 1] != item);
  380. idx as u64
  381. };
  382. let mut output = {
  383. let hash = H::hash_value_as_usize(hashes[0][0]);
  384. debug_assert!(key.spdpf_keys[hash].is_some());
  385. let sp_key = key.spdpf_keys[hash].as_ref().unwrap();
  386. debug_assert_eq!(simple_htable[hash][pos(hash, index) as usize], index);
  387. SPDPF::evaluate_at(&sp_key, pos(hash, index))
  388. };
  389. // prevent adding the same term multiple times when we have collisions
  390. let mut hash_bit_map = [0u8; 2];
  391. if hashes[0][0] != hashes[1][0] {
  392. // hash_bit_map[i] |= 1;
  393. hash_bit_map[0] = 1;
  394. }
  395. if hashes[0][0] != hashes[2][0] && hashes[1][0] != hashes[2][0] {
  396. // hash_bit_map[i] |= 2;
  397. hash_bit_map[1] = 1;
  398. }
  399. for j in 1..CUCKOO_NUMBER_HASH_FUNCTIONS {
  400. if hash_bit_map[j - 1] == 0 {
  401. continue;
  402. }
  403. let hash = H::hash_value_as_usize(hashes[j][0]);
  404. debug_assert!(key.spdpf_keys[hash].is_some());
  405. let sp_key = key.spdpf_keys[hash].as_ref().unwrap();
  406. debug_assert_eq!(simple_htable[hash][pos(hash, index) as usize], index);
  407. output += SPDPF::evaluate_at(&sp_key, pos(hash, index));
  408. }
  409. output
  410. }
  411. fn evaluate_domain(&self, key: &Self::Key) -> Vec<Self::Value> {
  412. assert_eq!(self.domain_size, key.domain_size);
  413. assert_eq!(self.number_points, key.number_points);
  414. let domain_size = self.domain_size as u64;
  415. // if not data is precomputed, do it now
  416. let mut precomputation_data_fresh: Option<SmartMpDpfPrecomputationData<H>> = None;
  417. if self.precomputation_data.is_none() {
  418. precomputation_data_fresh = Some(Self::precompute_hashes(
  419. self.domain_size,
  420. self.number_points,
  421. ));
  422. }
  423. // select either the precomputed or the freshly computed data
  424. let precomputation_data = self
  425. .precomputation_data
  426. .as_ref()
  427. .unwrap_or_else(|| precomputation_data_fresh.as_ref().unwrap());
  428. let hashes = &precomputation_data.hashes;
  429. let simple_htable = &precomputation_data.simple_htable;
  430. let position_map_lookup_table = &precomputation_data.position_map_lookup_table;
  431. let pos = |bucket_i: usize, item: u64| -> u64 {
  432. CuckooHasher::<H, u16>::pos_lookup(position_map_lookup_table, bucket_i, item)
  433. };
  434. let mut outputs = Vec::<Self::Value>::with_capacity(domain_size as usize);
  435. let sp_dpf_full_domain_evaluations: Vec<Vec<V>> = key
  436. .spdpf_keys
  437. .iter()
  438. .map(|sp_key_opt| {
  439. sp_key_opt
  440. .as_ref()
  441. .map_or(vec![], |sp_key| SPDPF::evaluate_domain(&sp_key))
  442. })
  443. .collect();
  444. let spdpf_evaluate_at =
  445. |hash: usize, index| sp_dpf_full_domain_evaluations[hash][pos(hash, index) as usize];
  446. for index in 0..domain_size {
  447. outputs.push({
  448. let hash = H::hash_value_as_usize(hashes[0][index as usize]);
  449. debug_assert!(key.spdpf_keys[hash].is_some());
  450. debug_assert_eq!(simple_htable[hash][pos(hash, index) as usize], index);
  451. spdpf_evaluate_at(hash, index)
  452. });
  453. // prevent adding the same term multiple times when we have collisions
  454. let mut hash_bit_map = [0u8; 2];
  455. if hashes[0][index as usize] != hashes[1][index as usize] {
  456. hash_bit_map[0] = 1;
  457. }
  458. if hashes[0][index as usize] != hashes[2][index as usize]
  459. && hashes[1][index as usize] != hashes[2][index as usize]
  460. {
  461. hash_bit_map[1] = 1;
  462. }
  463. for j in 1..CUCKOO_NUMBER_HASH_FUNCTIONS {
  464. if hash_bit_map[j - 1] == 0 {
  465. continue;
  466. }
  467. let hash = H::hash_value_as_usize(hashes[j][index as usize]);
  468. debug_assert!(key.spdpf_keys[hash].is_some());
  469. debug_assert_eq!(simple_htable[hash][pos(hash, index) as usize], index);
  470. outputs[index as usize] += spdpf_evaluate_at(hash, index)
  471. }
  472. }
  473. outputs
  474. }
  475. }
  476. #[cfg(test)]
  477. mod tests {
  478. use super::*;
  479. use crate::spdpf::DummySpDpf;
  480. use cuckoo::hash::AesHashFunction;
  481. use rand::distributions::{Distribution, Standard};
  482. use rand::{thread_rng, Rng};
  483. use std::num::Wrapping;
  484. fn test_mpdpf_with_param<MPDPF: MultiPointDpf>(
  485. log_domain_size: u32,
  486. number_points: usize,
  487. precomputation: bool,
  488. ) where
  489. Standard: Distribution<MPDPF::Value>,
  490. {
  491. let domain_size = (1 << log_domain_size) as u64;
  492. assert!(number_points <= domain_size as usize);
  493. let alphas = {
  494. let mut alphas = Vec::<u64>::with_capacity(number_points);
  495. while alphas.len() < number_points {
  496. let x = thread_rng().gen_range(0..domain_size);
  497. match alphas.as_slice().binary_search(&x) {
  498. Ok(_) => continue,
  499. Err(i) => alphas.insert(i, x),
  500. }
  501. }
  502. alphas
  503. };
  504. let betas: Vec<MPDPF::Value> = (0..number_points).map(|_| thread_rng().gen()).collect();
  505. let mut mpdpf = MPDPF::new(domain_size as usize, number_points);
  506. if precomputation {
  507. mpdpf.precompute();
  508. }
  509. let (key_0, key_1) = mpdpf.generate_keys(&alphas, &betas);
  510. let out_0 = mpdpf.evaluate_domain(&key_0);
  511. let out_1 = mpdpf.evaluate_domain(&key_1);
  512. for i in 0..domain_size {
  513. let value = mpdpf.evaluate_at(&key_0, i) + mpdpf.evaluate_at(&key_1, i);
  514. assert_eq!(value, out_0[i as usize] + out_1[i as usize]);
  515. let expected_result = match alphas.binary_search(&i) {
  516. Ok(i) => betas[i],
  517. Err(_) => MPDPF::Value::zero(),
  518. };
  519. assert_eq!(value, expected_result, "wrong value at index {}", i);
  520. }
  521. }
  522. #[test]
  523. fn test_dummy_mpdpf() {
  524. type Value = Wrapping<u64>;
  525. for log_domain_size in 5..10 {
  526. for log_number_points in 0..5 {
  527. test_mpdpf_with_param::<DummyMpDpf<Value>>(
  528. log_domain_size,
  529. 1 << log_number_points,
  530. false,
  531. );
  532. }
  533. }
  534. }
  535. #[test]
  536. fn test_smart_mpdpf() {
  537. type Value = Wrapping<u64>;
  538. for log_domain_size in 5..7 {
  539. for log_number_points in 0..5 {
  540. for precomputation in [false, true] {
  541. test_mpdpf_with_param::<
  542. SmartMpDpf<Value, DummySpDpf<Value>, AesHashFunction<u16>>,
  543. >(log_domain_size, 1 << log_number_points, precomputation);
  544. }
  545. }
  546. }
  547. }
  548. }