sim_lib_machine/
shuffle.rs1use crate::{StackError, UnitStack, ValueWidthPolicy};
2
3#[derive(Clone, Debug, PartialEq, Eq)]
5pub enum ShuffleError {
6 ZeroWidthGroup {
8 group: usize,
10 },
11 UnknownUnit {
13 unit: usize,
15 },
16 SplitGroup {
18 group: usize,
20 },
21 LayoutMismatch {
23 group: usize,
25 expected: usize,
27 actual: usize,
29 },
30 Stack(StackError),
32}
33
34#[derive(Clone, Debug, PartialEq, Eq)]
40pub struct ShufflePlan {
41 input_widths: Vec<usize>,
42 output_groups: Vec<usize>,
43}
44
45impl ShufflePlan {
46 pub fn new(
51 input_widths: impl IntoIterator<Item = usize>,
52 output_units: impl IntoIterator<Item = usize>,
53 ) -> Result<Self, ShuffleError> {
54 let input_widths: Vec<_> = input_widths.into_iter().collect();
55 let mut unit_to_group = Vec::new();
56 let mut starts = Vec::with_capacity(input_widths.len());
57 for (group, &width) in input_widths.iter().enumerate() {
58 if width == 0 {
59 return Err(ShuffleError::ZeroWidthGroup { group });
60 }
61 starts.push(unit_to_group.len());
62 unit_to_group.extend(std::iter::repeat_n(group, width));
63 }
64
65 let output_units: Vec<_> = output_units.into_iter().collect();
66 let mut output_groups = Vec::new();
67 let mut cursor = 0;
68 while cursor < output_units.len() {
69 let unit = output_units[cursor];
70 let Some(&group) = unit_to_group.get(unit) else {
71 return Err(ShuffleError::UnknownUnit { unit });
72 };
73 let start = starts[group];
74 let width = input_widths[group];
75 let end = cursor.saturating_add(width);
76 let whole_group = output_units
77 .get(cursor..end)
78 .is_some_and(|units| units.iter().copied().eq(start..start + width));
79 if !whole_group {
80 return Err(ShuffleError::SplitGroup { group });
81 }
82 output_groups.push(group);
83 cursor = end;
84 }
85
86 Ok(Self {
87 input_widths,
88 output_groups,
89 })
90 }
91
92 pub fn execute<P>(&self, stack: &mut UnitStack<P>) -> Result<(), ShuffleError>
94 where
95 P: ValueWidthPolicy,
96 P::Value: Clone,
97 {
98 let common = self.input_widths.len().min(stack.values.len());
99 for group in 0..common {
100 let actual = P::width(&stack.values[group]);
101 let expected = self.input_widths[group];
102 if actual != expected {
103 return Err(ShuffleError::LayoutMismatch {
104 group,
105 expected,
106 actual,
107 });
108 }
109 }
110 if self.input_widths.len() != stack.values.len() {
111 let group = common;
112 return Err(ShuffleError::LayoutMismatch {
113 group,
114 expected: self.input_widths.get(group).copied().unwrap_or(0),
115 actual: stack.values.get(group).map(P::width).unwrap_or(0),
116 });
117 }
118
119 let mut staged = UnitStack::<P>::new(stack.limit);
120 for &group in &self.output_groups {
121 staged
122 .push(stack.values[group].clone())
123 .map_err(ShuffleError::Stack)?;
124 }
125 *stack = staged;
126 Ok(())
127 }
128}