bellframe/row/same_stage_vec.rs
1use std::{
2 fmt::{Debug, Display, Formatter},
3 ops::{Index, Range, RangeBounds},
4};
5
6use itertools::Itertools;
7
8use crate::{utils, Bell, Block, InvalidRowError, Row, RowBuf, Stage};
9
10use super::DbgRow;
11
12/// A heap-allocated buffer of [`Row`]s which are required to have the same [`Stage`]. Collecting
13/// [`Row`]s of the same [`Stage`] is nearly always what we want, and having a type to enforce this
14/// is convenient.
15///
16/// Requiring matching [`Stage`]s has useful performance benefits too. These stem from the fact
17/// that the [`Row`]s can simply be stored sequentially in one [`Vec`] without any indirection or
18/// extra allocations. This is both more cache-friendly and uses less memory.
19///
20/// Additionally, having a linear memory layout makes `SameStageVec`s extremely amenable to SIMD
21/// optimisations (which haven't yet been implemented). This would allow us to do common
22/// operations (like permuting all the [`Row`]s or finding the path of a [`Bell`]) extremely
23/// quickly. For example, SIMD can perform permutation at 16 bells per CPU cycle (or 32 on very
24/// new CPUs).
25///
26/// # Example
27///
28/// ```
29/// use bellframe::{RowBuf, SameStageVec, Stage};
30///
31/// // Create a new `SameStageVec` for 6-bell rows
32/// let mut new_buffer = SameStageVec::new(Stage::MINOR);
33/// // Add some nice rows
34/// new_buffer.push(&RowBuf::rounds(Stage::MINOR));
35/// new_buffer.push(&RowBuf::queens(Stage::MINOR));
36/// new_buffer.push(&RowBuf::backrounds(Stage::MINOR));
37/// // `new_buffer` now contains 3 rows
38/// assert_eq!(new_buffer.len(), 3);
39/// // The 3rd row is backrounds
40/// assert!(new_buffer[2].is_backrounds());
41/// ```
42#[derive(Clone, PartialEq, Eq, Hash)]
43pub struct SameStageVec {
44 /// A contiguous chunk of [`Bell`]s, containing all the [`Row`]s concatenated together. For
45 /// example, the start of a plain course of Cambridge Surprise Minor would be stored as
46 /// `123456214365124635216453261435624153621435...`
47 /// or, with spaces inserted between rows for ease of display:
48 /// `123456 214365 124635 216453 261435 624153 621435 ...`
49 ///
50 /// **Invariant**: Each [`Stage`]-aligned segment of `bells` must form a valid [`Row`]
51 /// according to the Framework (see [`Row`]'s docs). A sub-slice is [`Stage`]-aligned if its
52 /// length is `self.stage` and its start index is an integer multiple of `self.stage`.
53 ///
54 /// **Invariant**: `self.bells.len()` is an integer multiple of `self.stage`.
55 bells: Vec<Bell>,
56 /// The [`Stage`] of all the [`Row`]s in this buffer.
57 stage: Stage,
58}
59
60impl SameStageVec {
61 //////////////////
62 // CONSTRUCTORS //
63 //////////////////
64
65 /// Creates a new `SameStageVec` containing no [`Row`]s, expecting to be given [`Row`]s of a
66 /// given [`Stage`]. Identical to [`SameStageVec::empty`].
67 #[inline]
68 pub fn new(stage: Stage) -> Self {
69 Self::empty(stage)
70 }
71
72 /// Creates a new `SameStageVec` containing no [`Row`]s, expecting to be given [`Row`]s of a
73 /// given [`Stage`]. Identical to [`SameStageVec::new`].
74 #[inline]
75 pub fn empty(stage: Stage) -> Self {
76 Self {
77 bells: Vec::new(),
78 stage,
79 }
80 }
81
82 /// Creates a new `SameStageVec` that can hold `capacity` [`Row`]s without re-allocating, and
83 /// all the [`Row`]s are expected to have a given [`Stage`].
84 ///
85 /// # Panics
86 ///
87 /// Panics if `Stage::ZERO` is passed.
88 #[inline]
89 pub fn with_capacity(stage: Stage, capacity: usize) -> Self {
90 Self {
91 bells: Vec::with_capacity(capacity * stage.num_bells()),
92 stage,
93 }
94 }
95
96 /// Creates a `SameStageVec` containing exactly one row, reusing the allocation from an
97 /// existing [`RowBuf`].
98 #[inline]
99 pub fn from_row_buf(row: RowBuf) -> Self {
100 let stage = row.stage();
101 // SAFETY: `row.to_bell_vec()` generates a Vec containing the `Bell`s of exactly one valid `Row`
102 unsafe { Self::from_bell_vec_unchecked(row.into_bell_vec(), stage) }
103 }
104
105 /// Parse a `SameStageVec` from a [`str`]ing, where each line is parsed into a [`Row`].
106 pub fn parse(s: &str) -> Result<Self, ParseError> {
107 // Create an iterator of (line_num, line) pairs, where `line_number` is indexed from 1
108 let mut line_iter = s.lines().enumerate().map(|(idx, v)| (idx + 1, v));
109
110 // Parse the first line before creating the `SameStageVec` (since we need to know upfront
111 // what the stage is going to be).
112 let (_one, first_line) = line_iter.next().unwrap(); // strings always contain at least one line
113 let first_row =
114 RowBuf::parse(first_line).map_err(|err| ParseError::InvalidRow { line_num: 1, err })?;
115 // Create a `SameStageVec`, which starts out containing the first row
116 let mut new_vec = SameStageVec::from_row_buf(first_row.clone());
117
118 // Now parse the rest of the lines into the new `SameStageVec`
119 for (line_num, line) in line_iter {
120 // Parse the line into a Row, and fail if its either invalid or doesn't match the stage
121 let parsed_row =
122 RowBuf::parse(line).map_err(|err| ParseError::InvalidRow { line_num, err })?;
123 if parsed_row.stage() != first_row.stage() {
124 return Err(ParseError::IncompatibleStages {
125 line_num,
126 first_stage: first_row.stage(),
127 different_stage: parsed_row.stage(),
128 });
129 }
130 new_vec.push(&parsed_row);
131 }
132
133 Ok(new_vec)
134 }
135
136 /// Creates a `SameStageVec` from a [`Vec`] of [`Bell`]s (containing the [`Row`]s concatenated
137 /// together), without checking that they correspond to valid [`Row`]s.
138 ///
139 /// # Safety
140 ///
141 /// This function is safe if `bells` is formed of a sequence of valid [`Row`]s (each of the
142 /// provided [`Stage`]) concatenated together.
143 #[inline]
144 pub unsafe fn from_bell_vec_unchecked(bells: Vec<Bell>, stage: Stage) -> Self {
145 Self { bells, stage }
146 }
147
148 //////////////////
149 // SIZE GETTERS //
150 //////////////////
151
152 /// The [`Stage`] of every [`Row`] in this `SameStageVec`.
153 #[inline]
154 pub fn stage(&self) -> Stage {
155 self.stage
156 }
157
158 /// Gets the number of [`Row`]s contained in this `SameStageVec`
159 #[inline]
160 pub fn len(&self) -> usize {
161 // In debug builds, check that `bells` contains a whole number of rows
162 debug_assert!(self.bells.len() % self.stage.num_bells() == 0);
163 // Calculate the length
164 self.bells.len() / self.stage.num_bells()
165 }
166
167 /// Returns `true` if this `SameStageVec` contains no [`Row`]s.
168 #[inline]
169 pub fn is_empty(&self) -> bool {
170 self.bells.is_empty()
171 }
172
173 /////////////////
174 // ROW GETTERS //
175 /////////////////
176
177 /// Gets a reference to the [`Row`] at a given `index`, or `None` if `index >= self.len()`.
178 pub fn get(&self, index: usize) -> Option<&Row> {
179 let bell_slice = self.bells.get(self.get_range_of_row(index))?;
180 // SAFETY: by invariant, each stage-aligned segment of `self.bells` is a valid `Row`
181 Some(unsafe { Row::from_slice_unchecked(bell_slice) })
182 }
183
184 /// Gets a mutable reference to the [`Row`] at a given `index`, or `None` if
185 /// `index >= self.len()`.
186 pub fn get_mut(&mut self, index: usize) -> Option<&mut Row> {
187 let range = self.get_range_of_row(index);
188 let bell_slice = self.bells.get_mut(range)?;
189 // SAFETY: by invariant, each stage-aligned segment of `self.bells` is a valid `Row`
190 Some(unsafe { Row::from_mut_slice_unchecked(bell_slice) })
191 }
192
193 /// Returns a [`RowSlice`] containing all the rows [`Row`] in this [`SameStageVec`].
194 pub fn as_slice(&self) -> RowSlice {
195 RowSlice {
196 bells: &self.bells,
197 stage: self.stage,
198 }
199 }
200
201 /// Returns a [`RowSlice`] containing all the rows [`Row`] in this [`SameStageVec`].
202 pub fn as_slice_range(&self, range: impl RangeBounds<usize>) -> RowSlice {
203 let inclusive_start = match range.start_bound() {
204 std::ops::Bound::Included(&s) => s,
205 std::ops::Bound::Excluded(&s) => s + 1,
206 std::ops::Bound::Unbounded => 0,
207 };
208 let exclusive_end = match range.end_bound() {
209 std::ops::Bound::Included(&s) => s + 1,
210 std::ops::Bound::Excluded(&s) => s,
211 std::ops::Bound::Unbounded => self.len(),
212 };
213 let bell_range =
214 (inclusive_start * self.stage.num_bells())..(exclusive_end * self.stage.num_bells());
215 RowSlice {
216 bells: &self.bells[bell_range],
217 stage: self.stage,
218 }
219 }
220
221 /// Gets a reference to the first [`Row`], if it exists.
222 pub fn first(&self) -> Option<&Row> {
223 self.get(0)
224 }
225
226 /// Gets a mutable reference to the first [`Row`], if it exists.
227 pub fn first_mut(&mut self) -> Option<&mut Row> {
228 self.get_mut(0)
229 }
230
231 /// Gets a reference to the last [`Row`], if it exists.
232 pub fn last(&self) -> Option<&Row> {
233 self.get(self.len().checked_sub(1)?)
234 }
235
236 /// Gets a mutable reference to the last [`Row`], if it exists.
237 pub fn last_mut(&mut self) -> Option<&mut Row> {
238 self.get_mut(self.len().checked_sub(1)?)
239 }
240
241 /// Returns an [`Iterator`] over the [`Row`]s in this buffer.
242 #[inline]
243 pub fn iter(&self) -> Iter {
244 Iter {
245 // The invariant on `bells_left` follows because there is an identical invariant on
246 // `SameStageVec.bells`
247 bells_left: &self.bells,
248 stage: self.stage,
249 }
250 }
251
252 /// Returns a [`Vec`] containing the place of a [`Bell`] in each [`Row`] in this
253 /// `SameStageVec`. Returns `None` if the [`Bell`] exceeds the [`Stage`] of `self`.
254 pub fn path_of(&self, bell: Bell) -> Vec<usize> {
255 self.iter().map(|r| r.place_of(bell).unwrap()).collect_vec()
256 }
257
258 ////////////////
259 // OPERATIONS //
260 ////////////////
261
262 /// Mutates `self` to become empty
263 #[inline]
264 pub fn clear(&mut self) {
265 self.bells.clear();
266 }
267
268 /// Adds a new [`Row`] to the end of the buffer, checking that its [`Stage`] is as expected.
269 #[inline]
270 #[track_caller]
271 pub fn push(&mut self, row: &Row) {
272 self.check_stage("row", row.stage());
273 self.bells.extend(row.bell_iter());
274 }
275
276 /// Removes the last [`Row`] from `self` and returning if it. Returns `None` if `self`
277 /// contains no elements.
278 #[inline]
279 pub fn pop(&mut self) -> Option<RowBuf> {
280 // Compute the index of the first `Bell` in the last `Row`, and return `None` if that index
281 // would be negative
282 let start_index = self.bells.len().checked_sub(self.stage.num_bells())?;
283 debug_assert_eq!(self.bells.len() % self.stage.num_bells(), 0);
284 debug_assert_eq!(start_index % self.stage.num_bells(), 0);
285 // SAFETY: `start_index` and `self.bells.len()` are multiples of `self.stage.num_bells()`,
286 // so `self.bells[start_index..]` is 'stage-aligned'. By invariant, every stage-aligned
287 // segment of `self.bells` is a valid `Row`.
288 Some(unsafe { RowBuf::from_bell_iter_unchecked(self.bells.drain(start_index..)) })
289 }
290
291 /// Extends this buffer with the [`Row`]s yielded by an [`Iterator`]. If any of the [`Row`]s
292 /// cause a [`Stage`] mismatch, an error is returned and the buffer is left unchanged.
293 pub fn extend<T: AsRef<Row>>(&mut self, row_iter: impl IntoIterator<Item = T>) {
294 for r in row_iter.into_iter() {
295 self.bells.extend(r.as_ref().bell_iter());
296 }
297 }
298
299 /// Extend `self` with the [`Row`]s from `other`, returning an error if the [`Stage`]s don't
300 /// match. `x.extend_from_buf(y)` has the same effect as `x.extend(y)` (because
301 /// `&SameStageVec` implements [`IntoIterator`]) but `extend_from_buf` is faster since the
302 /// [`Stage`] comparison is only performed once.
303 #[inline]
304 #[track_caller]
305 pub fn extend_from_buf(&mut self, other: &Self) {
306 self.check_stage("other block", other.stage());
307 self.bells.extend(other.bells.iter().copied()); // Copy bells across in one go
308 }
309
310 /// Extend `self` with the [`Row`]s from another [`SameStageVec`], pre-multiplying them all by
311 /// a given [`Row`].
312 #[inline]
313 #[track_caller]
314 pub fn extend_transposed(&mut self, transposition: &Row, other: &Self) {
315 self.extend_range_transposed(transposition, other, ..)
316 }
317
318 /// Extend `self` with the [`Row`]s from a region of another [`SameStageVec`], pre-multiplying
319 /// them all by a given [`Row`].
320 #[inline]
321 #[track_caller]
322 pub fn extend_range_transposed(
323 &mut self,
324 transposition: &Row,
325 other: &Self,
326 range: impl RangeBounds<usize>,
327 ) {
328 self.check_stage("transposition", transposition.stage());
329 // TODO: Write a vectorised routine for this
330 self.bells.extend(
331 other.bells[other.to_bell_range(range)]
332 .iter()
333 .map(|b| transposition[b.index()]),
334 );
335 }
336
337 /// Take a range of `self`, pre-multiply all the `Row`s and extend `self` with them.
338 #[inline]
339 #[track_caller]
340 pub fn extend_transposed_from_within(
341 &mut self,
342 range: impl RangeBounds<usize>,
343 transposition: &Row,
344 ) {
345 self.check_stage("transposition", transposition.stage());
346 // Copy the bells one-by-one, because otherwise we'd have to borrow `self.bells` twice and
347 // the borrow checker doesn't like that.
348 for i in self.to_bell_range(range) {
349 let transposed_bell = transposition[self.bells[i].index()];
350 self.bells.push(transposed_bell);
351 }
352 }
353
354 /// Pre-multiplies every [`Row`] in this `Block` in-place by another [`Row`].
355 #[track_caller]
356 pub fn pre_multiply(&mut self, lhs_row: &Row) {
357 self.check_stage("LHS row", lhs_row.stage());
358 // TODO: Write vectorised routine for this
359 for b in &mut self.bells {
360 // SAFETY:
361 // - We know that `self.stage() == lhs_row.stage()`
362 // - Because of the `Row` invariants, `b.index() < self.stage()` for all `b` in
363 // `self.bells`
364 // => `b.index() < lhs_row.stage()` for every `b`
365 *b = unsafe { lhs_row.get_bell_unchecked(b.index()) };
366 }
367 }
368
369 /// Creates a copy of `self`, where every [`Row`] has been pre-multiplied by another [`Row`].
370 pub fn pre_multiplied(&self, lhs_row: &Row) -> Self {
371 let mut new_vec = Self::new(self.stage);
372 new_vec.extend_transposed(lhs_row, self);
373 new_vec
374 }
375
376 /// Splits `self` into two `SameStageVec`s (`a` and `b`) where:
377 /// - `a.len() == index`, and
378 /// - `self == a.extend_from_buf(&b)` (i.e. no rows are created or destroyed)
379 ///
380 /// Returns `None` if `index > self.len()`
381 pub fn split(self, index: usize) -> Option<(Self, Self)> {
382 let (left_bells, right_bells) =
383 utils::split_vec(self.bells, index * self.stage.num_bells())?;
384 Some((
385 // SAFETY: `self.bells` is split at an integer multiple of `self.stage`, which must be
386 // a row boundary, so each side is a valid sequence of `Row`s
387 unsafe { Self::from_bell_vec_unchecked(left_bells, self.stage) },
388 unsafe { Self::from_bell_vec_unchecked(right_bells, self.stage) },
389 ))
390 }
391
392 //////////////////////
393 // HELPER FUNCTIONS //
394 //////////////////////
395
396 /// Converts a generic [`RangeBounds`] **over [`Row`]s** into a [`Range`] **over [`Bell`]s**
397 /// which explicitly refers to the same region of `self`.
398 #[allow(clippy::let_and_return)]
399 fn to_bell_range(&self, range: impl RangeBounds<usize>) -> Range<usize> {
400 let row_range = utils::clamp_range(range, self.len());
401 let bell_range =
402 row_range.start * self.stage.num_bells()..row_range.end * self.stage.num_bells();
403 bell_range
404 }
405
406 /// Gets the [`Range`] of `self.bells` which would contain the [`Row`] at a given `index`.
407 fn get_range_of_row(&self, index: usize) -> Range<usize> {
408 let s = self.stage.num_bells();
409 index * s..(index + 1) * s
410 }
411
412 #[track_caller]
413 fn check_stage(&self, name: &str, rhs_stage: Stage) {
414 assert_eq!(
415 self.stage, rhs_stage,
416 "Stage mismatch: `Block` has stage {:?} but {:?} has stage {:?}",
417 self.stage, name, rhs_stage,
418 );
419 }
420}
421
422/// A slice of [`Row`]s of the same [`Stage`], stored consecutively in memory.
423#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
424pub struct RowSlice<'a> {
425 pub bells: &'a [Bell],
426 pub stage: Stage,
427}
428
429impl<'r> RowSlice<'r> {
430 pub fn from_row(row: &'r Row) -> RowSlice<'r> {
431 Self {
432 bells: row.bells(),
433 stage: row.stage(),
434 }
435 }
436}
437
438impl<'r> From<&'r Row> for RowSlice<'r> {
439 fn from(row: &'r Row) -> Self {
440 Self::from_row(row)
441 }
442}
443
444impl<'r> From<&'r RowBuf> for RowSlice<'r> {
445 fn from(row: &'r RowBuf) -> Self {
446 Self::from_row(row.as_row())
447 }
448}
449
450impl<'r> From<&'r SameStageVec> for RowSlice<'r> {
451 fn from(vec: &'r SameStageVec) -> Self {
452 vec.as_slice()
453 }
454}
455
456impl<'r, T> From<&'r Block<T>> for RowSlice<'r> {
457 fn from(block: &'r Block<T>) -> Self {
458 block.row_vec().as_slice_range(..block.len())
459 }
460}
461
462impl Index<usize> for SameStageVec {
463 type Output = Row;
464
465 #[track_caller]
466 fn index(&self, index: usize) -> &Self::Output {
467 self.get(index).unwrap_or_else(|| {
468 panic!(
469 "Index of `SameStageVec` is out of range (index was {} but there are only {} rows)",
470 index,
471 self.len()
472 )
473 })
474 }
475}
476
477impl Debug for SameStageVec {
478 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
479 let mut formatter = f.debug_tuple("SameStageVec");
480 for row in self {
481 formatter.field(&DbgRow(row)); // Only display the bells
482 }
483 formatter.finish()
484 }
485}
486
487impl<'v> IntoIterator for &'v SameStageVec {
488 type Item = &'v Row;
489
490 type IntoIter = Iter<'v>;
491
492 fn into_iter(self) -> Self::IntoIter {
493 self.iter()
494 }
495}
496
497/// An [`Iterator`] which yields [`Row`]s from a [`SameStageVec`]
498#[derive(Debug, Clone)]
499pub struct Iter<'v> {
500 /// Invariant: Just like [`SameStageVec`], every [`Stage`]-aligned segment of `bells_left` must
501 /// form a valid row according to the Framework.
502 bells_left: &'v [Bell],
503 stage: Stage,
504}
505
506impl<'v> Iterator for Iter<'v> {
507 type Item = &'v Row;
508
509 fn next(&mut self) -> Option<Self::Item> {
510 if self.bells_left.is_empty() {
511 return None;
512 }
513 // Remove the first `self.stage` elements from `self.bells_left`. Since we've removed
514 // `self.stage` items, this means that the stage-aligned segments haven't changed, so the
515 // invariant on `self.bells_left` is still satisfied.
516 let (next_row, future_rows) = self.bells_left.split_at(self.stage.num_bells());
517 self.bells_left = future_rows;
518 // SAFETY: the invariant on `self.bells_left` requires that `next_row` (a stage-aligned
519 // segment of `self.bells_left`) forms a valid `Row`.
520 Some(unsafe { Row::from_slice_unchecked(next_row) })
521 }
522
523 // PERF: Implement better routines for methods such as `nth`, etc. for which repeatedly calling
524 // `next()` is unnecessarily slow.
525}
526
527impl<'v> DoubleEndedIterator for Iter<'v> {
528 fn next_back(&mut self) -> Option<Self::Item> {
529 if self.bells_left.is_empty() {
530 return None;
531 }
532 // Remove the last `self.stage` elements from `self.bells_left`. Since we've removed
533 // `self.stage` items, this means that the stage-aligned segments haven't changed, so the
534 // invariant on `self.bells_left` is still satisfied.
535 let (future_rows, next_row) = self
536 .bells_left
537 .split_at(self.bells_left.len() - self.stage.num_bells());
538 self.bells_left = future_rows;
539 // SAFETY: the invariant on `self.bells_left` requires that `next_row` (a stage-aligned
540 // segment of `self.bells_left`) forms a valid `Row`.
541 Some(unsafe { Row::from_slice_unchecked(next_row) })
542 }
543}
544
545impl<'v> ExactSizeIterator for Iter<'v> {
546 #[inline]
547 fn len(&self) -> usize {
548 self.bells_left.len() / self.stage.num_bells()
549 }
550}
551
552/// The possible ways that [`SameStageVec::parse`] could fail
553#[derive(Debug, Clone)]
554pub enum ParseError {
555 ZeroStage,
556 InvalidRow {
557 line_num: usize,
558 err: InvalidRowError,
559 },
560 IncompatibleStages {
561 line_num: usize,
562 first_stage: Stage,
563 different_stage: Stage,
564 },
565}
566
567impl Display for ParseError {
568 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
569 match self {
570 ParseError::ZeroStage => write!(f, "Can't make a `SameStageVec` with stage 0"),
571 ParseError::InvalidRow {
572 line_num: line,
573 err,
574 } => {
575 write!(f, "Error parsing row on line {}: {}", line, err)
576 }
577 ParseError::IncompatibleStages {
578 line_num: line,
579 first_stage,
580 different_stage,
581 } => {
582 write!(
583 f,
584 "Row on line {} has different stage ({}) to the first stage ({})",
585 line, different_stage, first_stage
586 )
587 }
588 }
589 }
590}
591
592impl std::error::Error for ParseError {}