1#[cfg(test)]
2mod tests;
3pub(super) mod types;
4
5#[cfg(not(feature = "std"))]
6extern crate alloc;
7
8use crate::chiapos::Seed;
9use crate::chiapos::constants::{PARAM_B, PARAM_BC, PARAM_C, PARAM_EXT, PARAM_M};
10use crate::chiapos::table::types::{Metadata, Position, X, Y};
11use crate::chiapos::utils::EvaluatableUsize;
12#[cfg(not(feature = "std"))]
13use alloc::vec;
14#[cfg(not(feature = "std"))]
15use alloc::vec::Vec;
16use chacha20::cipher::{KeyIvInit, StreamCipher, StreamCipherSeek};
17use chacha20::{ChaCha8, Key, Nonce};
18use core::array;
19use core::simd::Simd;
20use core::simd::num::SimdUint;
21#[cfg(all(feature = "std", any(feature = "parallel", test)))]
22use parking_lot::Mutex;
23#[cfg(any(feature = "parallel", test))]
24use rayon::prelude::*;
25use seq_macro::seq;
26#[cfg(all(not(feature = "std"), any(feature = "parallel", test)))]
27use spin::Mutex;
28
29pub(super) const COMPUTE_F1_SIMD_FACTOR: usize = 8;
30pub(super) const FIND_MATCHES_AND_COMPUTE_UNROLL_FACTOR: usize = 8;
31
32pub(super) const fn y_size_bits(k: u8) -> usize {
34 k as usize + PARAM_EXT as usize
35}
36
37pub const fn metadata_size_bytes(k: u8, table_number: u8) -> usize {
39 metadata_size_bits(k, table_number).div_ceil(u8::BITS as usize)
40}
41
42pub(super) const fn metadata_size_bits(k: u8, table_number: u8) -> usize {
44 k as usize
45 * match table_number {
46 1 => 1,
47 2 => 2,
48 3 | 4 => 4,
49 5 => 3,
50 6 => 2,
51 7 => 0,
52 _ => unreachable!(),
53 }
54}
55
56fn partial_ys<const K: u8>(seed: Seed) -> Vec<u8> {
59 let output_len_bits = usize::from(K) * (1 << K);
60 let mut output = vec![0; output_len_bits.div_ceil(u8::BITS as usize)];
61
62 let key = Key::from(seed);
63 let nonce = Nonce::default();
64
65 let mut cipher = ChaCha8::new(&key, &nonce);
66
67 cipher.apply_keystream(&mut output);
68
69 output
70}
71
72pub(super) fn partial_y<const K: u8>(
76 seed: Seed,
77 x: X,
78) -> ([u8; (K as usize * 2).div_ceil(u8::BITS as usize)], usize) {
79 let skip_bits = usize::from(K) * usize::from(x);
80 let skip_bytes = skip_bits / u8::BITS as usize;
81 let skip_bits = skip_bits % u8::BITS as usize;
82
83 let mut output = [0; (K as usize * 2).div_ceil(u8::BITS as usize)];
84
85 let key = Key::from(seed);
86 let nonce = Nonce::default();
87
88 let mut cipher = ChaCha8::new(&key, &nonce);
89
90 cipher.seek(skip_bytes);
91 cipher.apply_keystream(&mut output);
92
93 (output, skip_bits)
94}
95
96#[derive(Debug, Clone)]
97struct LeftTargets {
98 left_targets: Vec<Position>,
99}
100
101fn calculate_left_targets() -> LeftTargets {
102 let mut left_targets = Vec::with_capacity(2 * usize::from(PARAM_BC) * usize::from(PARAM_M));
103
104 let param_b = u32::from(PARAM_B);
105 let param_c = u32::from(PARAM_C);
106
107 for parity in 0..=1u32 {
108 for r in 0..u32::from(PARAM_BC) {
109 let c = r / param_c;
110
111 for m in 0..u32::from(PARAM_M) {
112 let target = ((c + m) % param_b) * param_c
113 + (((2 * m + parity) * (2 * m + parity) + r) % param_c);
114 left_targets.push(Position::from(target));
115 }
116 }
117 }
118
119 LeftTargets { left_targets }
120}
121
122fn calculate_left_target_on_demand(parity: u32, r: u32, m: u32) -> u32 {
123 let param_b = u32::from(PARAM_B);
124 let param_c = u32::from(PARAM_C);
125
126 let c = r / param_c;
127
128 ((c + m) % param_b) * param_c + (((2 * m + parity) * (2 * m + parity) + r) % param_c)
129}
130
131#[derive(Debug, Clone)]
133pub struct TablesCache<const K: u8> {
134 buckets: Vec<Bucket>,
135 rmap_scratch: Vec<RmapItem>,
136 left_targets: LeftTargets,
137}
138
139impl<const K: u8> Default for TablesCache<K> {
140 fn default() -> Self {
142 Self {
143 buckets: Vec::new(),
144 rmap_scratch: Vec::new(),
145 left_targets: calculate_left_targets(),
146 }
147 }
148}
149
150#[derive(Debug)]
151struct Match {
152 left_position: Position,
153 left_y: Y,
154 right_position: Position,
155}
156
157#[derive(Debug, Default, Copy, Clone, Eq, PartialEq)]
158struct Bucket {
159 bucket_index: u32,
161 start_position: Position,
163 size: Position,
165}
166
167#[derive(Debug, Default, Copy, Clone)]
168pub(super) struct RmapItem {
169 count: Position,
170 start_position: Position,
171}
172
173pub(super) fn compute_f1<const K: u8>(x: X, partial_y: &[u8], partial_y_offset: usize) -> Y {
175 let partial_y_length =
176 (partial_y_offset % u8::BITS as usize + usize::from(K)).div_ceil(u8::BITS as usize);
177 let mut pre_y_bytes = 0u64.to_be_bytes();
178 pre_y_bytes[..partial_y_length]
179 .copy_from_slice(&partial_y[partial_y_offset / u8::BITS as usize..][..partial_y_length]);
180 let pre_y = u64::from_be_bytes(pre_y_bytes)
183 >> (u64::BITS as usize - usize::from(K + PARAM_EXT) - partial_y_offset % u8::BITS as usize);
184 let pre_y = pre_y as u32;
185 let pre_y_mask = (u32::MAX << usize::from(PARAM_EXT))
187 & (u32::MAX >> (u32::BITS as usize - usize::from(K + PARAM_EXT)));
188
189 let pre_ext = u32::from(x) >> (usize::from(K - PARAM_EXT));
192 let pre_ext_mask = u32::MAX >> (u32::BITS as usize - usize::from(PARAM_EXT));
194
195 Y::from((pre_y & pre_y_mask) | (pre_ext & pre_ext_mask))
198}
199
200pub(super) fn compute_f1_simd<const K: u8>(
201 xs: [u32; COMPUTE_F1_SIMD_FACTOR],
202 partial_ys: &[u8; K as usize * COMPUTE_F1_SIMD_FACTOR / u8::BITS as usize],
203) -> [Y; COMPUTE_F1_SIMD_FACTOR] {
204 let pre_ys_bytes = array::from_fn(|i| {
207 let partial_y_offset = i * usize::from(K);
208 let partial_y_length =
209 (partial_y_offset % u8::BITS as usize + usize::from(K)).div_ceil(u8::BITS as usize);
210 let mut pre_y_bytes = 0u64.to_be_bytes();
211 pre_y_bytes[..partial_y_length].copy_from_slice(
212 &partial_ys[partial_y_offset / u8::BITS as usize..][..partial_y_length],
213 );
214
215 u64::from_be_bytes(pre_y_bytes)
216 });
217 let pre_ys_right_offset = array::from_fn(|i| {
218 let partial_y_offset = i as u32 * u32::from(K);
219 u64::from(u64::BITS - u32::from(K + PARAM_EXT) - partial_y_offset % u8::BITS)
220 });
221 let pre_ys = Simd::from_array(pre_ys_bytes) >> Simd::from_array(pre_ys_right_offset);
222
223 let pre_ys_mask = Simd::splat(
225 (u32::MAX << usize::from(PARAM_EXT))
226 & (u32::MAX >> (u32::BITS as usize - usize::from(K + PARAM_EXT))),
227 );
228
229 let pre_exts = Simd::from_array(xs) >> Simd::splat(u32::from(K - PARAM_EXT));
232
233 let ys = (pre_ys.cast() & pre_ys_mask) | pre_exts;
236
237 Y::array_from_repr(ys.to_array())
238}
239
240#[allow(clippy::too_many_arguments)]
246fn find_matches<T, Map>(
247 left_bucket_ys: &[Y],
248 left_bucket_start_position: Position,
249 right_bucket_ys: &[Y],
250 right_bucket_start_position: Position,
251 rmap_scratch: &mut Vec<RmapItem>,
252 left_targets: &LeftTargets,
253 map: Map,
254 output: &mut Vec<T>,
255) where
256 Map: Fn(Match) -> T,
257{
258 rmap_scratch.clear();
260 rmap_scratch.resize_with(usize::from(PARAM_BC), RmapItem::default);
261 let rmap = rmap_scratch;
262
263 let Some(&first_left_bucket_y) = left_bucket_ys.first() else {
265 return;
266 };
267 let Some(&first_right_bucket_y) = right_bucket_ys.first() else {
268 return;
269 };
270 let base = (usize::from(first_right_bucket_y) / usize::from(PARAM_BC)) * usize::from(PARAM_BC);
274 for (&y, right_position) in right_bucket_ys.iter().zip(right_bucket_start_position..) {
275 let r = usize::from(y) - base;
276
277 if rmap[r].count == Position::ZERO {
281 rmap[r].start_position = right_position;
282 }
283 rmap[r].count += Position::ONE;
284 }
285 let rmap = rmap.as_slice();
286
287 let base = base - usize::from(PARAM_BC);
290 let parity = (usize::from(first_left_bucket_y) / usize::from(PARAM_BC)) % 2;
291 let left_targets_parity = {
292 let (a, b) = left_targets
293 .left_targets
294 .split_at(left_targets.left_targets.len() / 2);
295 if parity == 0 { a } else { b }
296 };
297
298 for (&y, left_position) in left_bucket_ys.iter().zip(left_bucket_start_position..) {
299 let r = usize::from(y) - base;
300 let left_targets_r = left_targets_parity
301 .chunks_exact(left_targets_parity.len() / usize::from(PARAM_BC))
302 .nth(r)
303 .expect("r is valid");
304
305 const _: () = {
306 assert!(PARAM_M as usize % FIND_MATCHES_AND_COMPUTE_UNROLL_FACTOR == 0);
307 };
308
309 for r_targets in left_targets_r
310 .array_chunks::<{ FIND_MATCHES_AND_COMPUTE_UNROLL_FACTOR }>()
311 .take(usize::from(PARAM_M) / FIND_MATCHES_AND_COMPUTE_UNROLL_FACTOR)
312 {
313 let _: [(); FIND_MATCHES_AND_COMPUTE_UNROLL_FACTOR] = seq!(N in 0..8 {
314 [
315 #(
316 {
317 let rmap_item = rmap[usize::from(r_targets[N])];
318
319 for right_position in
320 rmap_item.start_position..rmap_item.start_position + rmap_item.count
321 {
322 let m = Match {
323 left_position,
324 left_y: y,
325 right_position,
326 };
327 output.push(map(m));
328 }
329 },
330 )*
331 ]
332 });
333 }
334 }
335}
336
337pub(super) fn has_match(left_y: Y, right_y: Y) -> bool {
339 let right_r = u32::from(right_y) % u32::from(PARAM_BC);
340 let parity = (u32::from(left_y) / u32::from(PARAM_BC)) % 2;
341 let left_r = u32::from(left_y) % u32::from(PARAM_BC);
342
343 let r_targets = array::from_fn::<_, { PARAM_M as usize }, _>(|i| {
344 calculate_left_target_on_demand(parity, left_r, i as u32)
345 });
346
347 r_targets.contains(&right_r)
348}
349
350pub(super) fn compute_fn<const K: u8, const TABLE_NUMBER: u8, const PARENT_TABLE_NUMBER: u8>(
351 y: Y,
352 left_metadata: Metadata<K, PARENT_TABLE_NUMBER>,
353 right_metadata: Metadata<K, PARENT_TABLE_NUMBER>,
354) -> (Y, Metadata<K, TABLE_NUMBER>)
355where
356 EvaluatableUsize<{ metadata_size_bytes(K, PARENT_TABLE_NUMBER) }>: Sized,
357 EvaluatableUsize<{ metadata_size_bytes(K, TABLE_NUMBER) }>: Sized,
358{
359 let left_metadata = u128::from(left_metadata);
360 let right_metadata = u128::from(right_metadata);
361
362 let parent_metadata_bits = metadata_size_bits(K, PARENT_TABLE_NUMBER);
363
364 let hash = {
367 let num_bytes_with_data = (y_size_bits(K) + metadata_size_bits(K, PARENT_TABLE_NUMBER) * 2)
369 .div_ceil(u8::BITS as usize);
370
371 let y_bits = u128::from(y) << (u128::BITS as usize - y_size_bits(K));
373
374 let left_metadata_bits =
376 left_metadata << (u128::BITS as usize - parent_metadata_bits - y_size_bits(K));
377
378 let y_and_left_bits = y_size_bits(K) + parent_metadata_bits;
380 let right_bits_start_offset = u128::BITS as usize - parent_metadata_bits;
381
382 if right_bits_start_offset < y_and_left_bits {
385 let right_bits_pushed_into_input_b = y_and_left_bits - right_bits_start_offset;
386 let right_bits_a = right_metadata >> right_bits_pushed_into_input_b;
389 let input_a = y_bits | left_metadata_bits | right_bits_a;
390 let input_b = right_metadata << (u128::BITS as usize - right_bits_pushed_into_input_b);
392
393 let input = [input_a.to_be_bytes(), input_b.to_be_bytes()];
394 let input_len =
395 size_of::<u128>() + right_bits_pushed_into_input_b.div_ceil(u8::BITS as usize);
396 blake3::hash(&input.as_flattened()[..input_len])
397 } else {
398 let right_bits_a = right_metadata << (right_bits_start_offset - y_and_left_bits);
399 let input_a = y_bits | left_metadata_bits | right_bits_a;
400
401 blake3::hash(&input_a.to_be_bytes()[..num_bytes_with_data])
402 }
403 };
404 let hash = <[u8; 32]>::from(hash);
405
406 let y_output = Y::from(
407 u32::from_be_bytes([hash[0], hash[1], hash[2], hash[3]])
408 >> (u32::BITS as usize - y_size_bits(K)),
409 );
410
411 let metadata_size_bits = metadata_size_bits(K, TABLE_NUMBER);
412
413 let metadata = if TABLE_NUMBER < 4 {
414 (left_metadata << parent_metadata_bits) | right_metadata
415 } else if metadata_size_bits > 0 {
416 let metadata = u128::from_be_bytes(
420 hash[y_size_bits(K) / u8::BITS as usize..][..size_of::<u128>()]
421 .try_into()
422 .expect("Always enough bits for any K; qed"),
423 );
424 let metadata = metadata << (y_size_bits(K) % u8::BITS as usize);
426 metadata >> (u128::BITS as usize - metadata_size_bits)
428 } else {
429 0
430 };
431
432 (y_output, Metadata::from(metadata))
433}
434
435fn match_to_result<const K: u8, const TABLE_NUMBER: u8, const PARENT_TABLE_NUMBER: u8>(
436 last_table: &Table<K, PARENT_TABLE_NUMBER>,
437 m: Match,
438) -> (Y, [Position; 2], Metadata<K, TABLE_NUMBER>)
439where
440 EvaluatableUsize<{ metadata_size_bytes(K, PARENT_TABLE_NUMBER) }>: Sized,
441 EvaluatableUsize<{ metadata_size_bytes(K, TABLE_NUMBER) }>: Sized,
442{
443 let left_metadata = last_table
444 .metadata(m.left_position)
445 .expect("Position resulted from matching is correct; qed");
446 let right_metadata = last_table
447 .metadata(m.right_position)
448 .expect("Position resulted from matching is correct; qed");
449
450 let (y, metadata) =
451 compute_fn::<K, TABLE_NUMBER, PARENT_TABLE_NUMBER>(m.left_y, left_metadata, right_metadata);
452
453 (y, [m.left_position, m.right_position], metadata)
454}
455
456fn match_and_compute_fn<'a, const K: u8, const TABLE_NUMBER: u8, const PARENT_TABLE_NUMBER: u8>(
457 last_table: &'a Table<K, PARENT_TABLE_NUMBER>,
458 left_bucket: Bucket,
459 right_bucket: Bucket,
460 rmap_scratch: &'a mut Vec<RmapItem>,
461 left_targets: &'a LeftTargets,
462 results_table: &mut Vec<(Y, [Position; 2], Metadata<K, TABLE_NUMBER>)>,
463) where
464 EvaluatableUsize<{ metadata_size_bytes(K, PARENT_TABLE_NUMBER) }>: Sized,
465 EvaluatableUsize<{ metadata_size_bytes(K, TABLE_NUMBER) }>: Sized,
466{
467 find_matches(
468 &last_table.ys()[usize::from(left_bucket.start_position)..]
469 [..usize::from(left_bucket.size)],
470 left_bucket.start_position,
471 &last_table.ys()[usize::from(right_bucket.start_position)..]
472 [..usize::from(right_bucket.size)],
473 right_bucket.start_position,
474 rmap_scratch,
475 left_targets,
476 |m| match_to_result(last_table, m),
477 results_table,
478 )
479}
480
481#[derive(Debug)]
482pub(super) enum Table<const K: u8, const TABLE_NUMBER: u8>
483where
484 EvaluatableUsize<{ metadata_size_bytes(K, TABLE_NUMBER) }>: Sized,
485{
486 First {
488 ys: Vec<Y>,
490 xs: Vec<X>,
492 },
493 Other {
495 ys: Vec<Y>,
497 positions: Vec<[Position; 2]>,
499 metadatas: Vec<Metadata<K, TABLE_NUMBER>>,
501 },
502}
503
504impl<const K: u8> Table<K, 1>
505where
506 EvaluatableUsize<{ metadata_size_bytes(K, 1) }>: Sized,
507{
508 pub(super) fn create(seed: Seed) -> Self
510 where
511 EvaluatableUsize<{ K as usize * COMPUTE_F1_SIMD_FACTOR / u8::BITS as usize }>: Sized,
512 {
513 let partial_ys = partial_ys::<K>(seed);
514
515 let mut t_1 = Vec::with_capacity(1_usize << K);
516 for (x_batch, partial_ys) in partial_ys
517 .array_chunks::<{ K as usize * COMPUTE_F1_SIMD_FACTOR / u8::BITS as usize }>()
518 .copied()
519 .enumerate()
520 {
521 let xs = array::from_fn::<_, COMPUTE_F1_SIMD_FACTOR, _>(|i| {
522 (x_batch * COMPUTE_F1_SIMD_FACTOR + i) as u32
523 });
524 let ys = compute_f1_simd::<K>(xs, &partial_ys);
525 t_1.extend(ys.into_iter().zip(X::array_from_repr(xs)));
526 }
527
528 t_1.sort_unstable();
529
530 let (ys, xs) = t_1.into_iter().unzip();
531
532 Self::First { ys, xs }
533 }
534
535 #[cfg(any(feature = "parallel", test))]
537 pub(super) fn create_parallel(seed: Seed) -> Self
538 where
539 EvaluatableUsize<{ K as usize * COMPUTE_F1_SIMD_FACTOR / u8::BITS as usize }>: Sized,
540 {
541 let partial_ys = partial_ys::<K>(seed);
542
543 let mut t_1 = Vec::with_capacity(1_usize << K);
544 for (x_batch, partial_ys) in partial_ys
545 .array_chunks::<{ K as usize * COMPUTE_F1_SIMD_FACTOR / u8::BITS as usize }>()
546 .copied()
547 .enumerate()
548 {
549 let xs = array::from_fn::<_, COMPUTE_F1_SIMD_FACTOR, _>(|i| {
550 (x_batch * COMPUTE_F1_SIMD_FACTOR + i) as u32
551 });
552 let ys = compute_f1_simd::<K>(xs, &partial_ys);
553 t_1.extend(ys.into_iter().zip(X::array_from_repr(xs)));
554 }
555
556 t_1.par_sort_unstable();
557
558 let (ys, xs) = t_1.into_iter().unzip();
559
560 Self::First { ys, xs }
561 }
562
563 pub(super) fn xs(&self) -> &[X] {
565 match self {
566 Table::First { xs, .. } => xs,
567 _ => {
568 unreachable!()
569 }
570 }
571 }
572}
573
574mod private {
575 pub(in super::super) trait SupportedOtherTables {}
576}
577
578impl<const K: u8> private::SupportedOtherTables for Table<K, 2> where
579 EvaluatableUsize<{ metadata_size_bytes(K, 2) }>: Sized
580{
581}
582
583impl<const K: u8> private::SupportedOtherTables for Table<K, 3> where
584 EvaluatableUsize<{ metadata_size_bytes(K, 3) }>: Sized
585{
586}
587
588impl<const K: u8> private::SupportedOtherTables for Table<K, 4> where
589 EvaluatableUsize<{ metadata_size_bytes(K, 4) }>: Sized
590{
591}
592
593impl<const K: u8> private::SupportedOtherTables for Table<K, 5> where
594 EvaluatableUsize<{ metadata_size_bytes(K, 5) }>: Sized
595{
596}
597
598impl<const K: u8> private::SupportedOtherTables for Table<K, 6> where
599 EvaluatableUsize<{ metadata_size_bytes(K, 6) }>: Sized
600{
601}
602
603impl<const K: u8> private::SupportedOtherTables for Table<K, 7> where
604 EvaluatableUsize<{ metadata_size_bytes(K, 7) }>: Sized
605{
606}
607
608impl<const K: u8, const TABLE_NUMBER: u8> Table<K, TABLE_NUMBER>
609where
610 Self: private::SupportedOtherTables,
611 EvaluatableUsize<{ metadata_size_bytes(K, TABLE_NUMBER) }>: Sized,
612{
613 pub(super) fn create<const PARENT_TABLE_NUMBER: u8>(
616 last_table: &Table<K, PARENT_TABLE_NUMBER>,
617 cache: &mut TablesCache<K>,
618 ) -> Self
619 where
620 EvaluatableUsize<{ metadata_size_bytes(K, PARENT_TABLE_NUMBER) }>: Sized,
621 {
622 let buckets = &mut cache.buckets;
623 let rmap_scratch = &mut cache.rmap_scratch;
624 let left_targets = &cache.left_targets;
625
626 let mut bucket = Bucket {
627 bucket_index: 0,
628 start_position: Position::ZERO,
629 size: Position::ZERO,
630 };
631
632 let last_y = *last_table
633 .ys()
634 .last()
635 .expect("List of y values is never empty; qed");
636 buckets.clear();
637 buckets.reserve(1 + usize::from(last_y) / usize::from(PARAM_BC));
638 last_table
639 .ys()
640 .iter()
641 .zip(Position::ZERO..)
642 .for_each(|(&y, position)| {
643 let bucket_index = u32::from(y) / u32::from(PARAM_BC);
644
645 if bucket_index == bucket.bucket_index {
646 bucket.size += Position::ONE;
647 return;
648 }
649
650 buckets.push(bucket);
651
652 bucket = Bucket {
653 bucket_index,
654 start_position: position,
655 size: Position::ONE,
656 };
657 });
658 buckets.push(bucket);
660
661 let num_values = 1 << K;
662 let mut t_n = Vec::with_capacity(num_values);
663 buckets
664 .array_windows::<2>()
665 .for_each(|&[left_bucket, right_bucket]| {
666 match_and_compute_fn::<K, TABLE_NUMBER, PARENT_TABLE_NUMBER>(
667 last_table,
668 left_bucket,
669 right_bucket,
670 rmap_scratch,
671 left_targets,
672 &mut t_n,
673 );
674 });
675
676 t_n.sort_unstable();
677
678 let mut ys = Vec::with_capacity(t_n.len());
679 let mut positions = Vec::with_capacity(t_n.len());
680 let mut metadatas = Vec::with_capacity(t_n.len());
681
682 for (y, [left_position, right_position], metadata) in t_n {
683 ys.push(y);
684 positions.push([left_position, right_position]);
685 if metadata_size_bits(K, TABLE_NUMBER) > 0 {
687 metadatas.push(metadata);
688 }
689 }
690
691 Self::Other {
692 ys,
693 positions,
694 metadatas,
695 }
696 }
697
698 #[cfg(any(feature = "parallel", test))]
702 pub(super) fn create_parallel<const PARENT_TABLE_NUMBER: u8>(
703 last_table: &Table<K, PARENT_TABLE_NUMBER>,
704 cache: &mut TablesCache<K>,
705 ) -> Self
706 where
707 EvaluatableUsize<{ metadata_size_bytes(K, PARENT_TABLE_NUMBER) }>: Sized,
708 {
709 let left_targets = &cache.left_targets;
710
711 let mut first_bucket = Bucket {
712 bucket_index: u32::from(last_table.ys()[0]) / u32::from(PARAM_BC),
713 start_position: Position::ZERO,
714 size: Position::ZERO,
715 };
716 for &y in last_table.ys() {
717 let bucket_index = u32::from(y) / u32::from(PARAM_BC);
718
719 if bucket_index == first_bucket.bucket_index {
720 first_bucket.size += Position::ONE;
721 } else {
722 break;
723 }
724 }
725
726 let previous_bucket = Mutex::new(first_bucket);
727
728 let t_n = rayon::broadcast(|_ctx| {
729 let mut entries = Vec::new();
730 let mut rmap_scratch = Vec::new();
731
732 loop {
733 let left_bucket;
734 let right_bucket;
735 {
736 let mut previous_bucket = previous_bucket.lock();
737
738 let right_bucket_start_position =
739 previous_bucket.start_position + previous_bucket.size;
740 let right_bucket_index = match last_table
741 .ys()
742 .get(usize::from(right_bucket_start_position))
743 {
744 Some(&y) => u32::from(y) / u32::from(PARAM_BC),
745 None => {
746 break;
747 }
748 };
749 let mut right_bucket_size = Position::ZERO;
750
751 for &y in &last_table.ys()[usize::from(right_bucket_start_position)..] {
752 let bucket_index = u32::from(y) / u32::from(PARAM_BC);
753
754 if bucket_index == right_bucket_index {
755 right_bucket_size += Position::ONE;
756 } else {
757 break;
758 }
759 }
760
761 right_bucket = Bucket {
762 bucket_index: right_bucket_index,
763 start_position: right_bucket_start_position,
764 size: right_bucket_size,
765 };
766
767 left_bucket = *previous_bucket;
768 *previous_bucket = right_bucket;
769 }
770
771 match_and_compute_fn::<K, TABLE_NUMBER, PARENT_TABLE_NUMBER>(
772 last_table,
773 left_bucket,
774 right_bucket,
775 &mut rmap_scratch,
776 left_targets,
777 &mut entries,
778 );
779 }
780
781 entries
782 });
783
784 let mut t_n = t_n.into_iter().flatten().collect::<Vec<_>>();
785 t_n.par_sort_unstable();
786
787 let mut ys = Vec::with_capacity(t_n.len());
788 let mut positions = Vec::with_capacity(t_n.len());
789 let mut metadatas = Vec::with_capacity(t_n.len());
790
791 for (y, [left_position, right_position], metadata) in t_n.drain(..) {
792 ys.push(y);
793 positions.push([left_position, right_position]);
794 if metadata_size_bits(K, TABLE_NUMBER) > 0 {
796 metadatas.push(metadata);
797 }
798 }
799
800 rayon::spawn(move || {
802 drop(t_n);
803 });
804
805 Self::Other {
806 ys,
807 positions,
808 metadatas,
809 }
810 }
811}
812
813impl<const K: u8, const TABLE_NUMBER: u8> Table<K, TABLE_NUMBER>
814where
815 EvaluatableUsize<{ metadata_size_bytes(K, TABLE_NUMBER) }>: Sized,
816{
817 pub(super) fn ys(&self) -> &[Y] {
819 let (Table::First { ys, .. } | Table::Other { ys, .. }) = self;
820 ys
821 }
822
823 pub(super) fn position(&self, position: Position) -> Option<[Position; 2]> {
826 match self {
827 Table::First { .. } => None,
828 Table::Other { positions, .. } => positions.get(usize::from(position)).copied(),
829 }
830 }
831
832 pub(super) fn metadata(&self, position: Position) -> Option<Metadata<K, TABLE_NUMBER>> {
834 match self {
835 Table::First { xs, .. } => xs.get(usize::from(position)).map(|&x| Metadata::from(x)),
836 Table::Other { metadatas, .. } => metadatas.get(usize::from(position)).copied(),
837 }
838 }
839}