Skip to main content

sim_lib_pitch_serial/
operation.rs

1//! Total prime, inversion, retrograde, and retrograde-inversion operations.
2
3use std::fmt::{Display, Formatter};
4
5/// One of the four classical twelve-tone row-operation families.
6#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
7pub enum RowFamily {
8    /// Prime order under transposition.
9    P,
10    /// Inversion order under transposition.
11    I,
12    /// Retrograde of the prime order under transposition.
13    R,
14    /// Retrograde of the inversion order under transposition.
15    RI,
16}
17
18impl RowFamily {
19    /// Returns the conventional short family token.
20    pub const fn as_str(self) -> &'static str {
21        match self {
22            Self::P => "P",
23            Self::I => "I",
24            Self::R => "R",
25            Self::RI => "RI",
26        }
27    }
28
29    pub(crate) const fn is_inverted(self) -> bool {
30        matches!(self, Self::I | Self::RI)
31    }
32
33    pub(crate) const fn is_retrograde(self) -> bool {
34        matches!(self, Self::R | Self::RI)
35    }
36}
37
38impl Display for RowFamily {
39    fn fmt(&self, formatter: &mut Formatter<'_>) -> std::fmt::Result {
40        formatter.write_str(self.as_str())
41    }
42}
43
44/// A total affine/reversal operation on a strict tone row.
45///
46/// The `addend` is the affine constant in `x -> x + addend` for P/R and
47/// `x -> -x + addend` for I/RI. Values are reduced modulo twelve when the
48/// operation is applied, so even a struct literal containing an arbitrary `u8`
49/// remains total.
50#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
51pub struct RowOperation {
52    /// Prime, inversion, retrograde, or retrograde-inversion family.
53    pub family: RowFamily,
54    /// Affine addend, interpreted modulo twelve.
55    pub addend: u8,
56}
57
58impl RowOperation {
59    /// Constructs a row operation with a canonical modulo-twelve addend.
60    pub const fn new(family: RowFamily, addend: u8) -> Self {
61        Self {
62            family,
63            addend: addend % 12,
64        }
65    }
66
67    /// Returns the canonical operation identity with its addend reduced modulo twelve.
68    pub const fn normalized(self) -> Self {
69        Self::new(self.family, self.addend)
70    }
71
72    /// Returns the exact inverse operation.
73    pub const fn inverse(self) -> Self {
74        let operation = self.normalized();
75        let addend = match operation.family {
76            RowFamily::P | RowFamily::R => (12 - operation.addend) % 12,
77            RowFamily::I | RowFamily::RI => operation.addend,
78        };
79        Self::new(operation.family, addend)
80    }
81}
82
83impl Display for RowOperation {
84    fn fmt(&self, formatter: &mut Formatter<'_>) -> std::fmt::Result {
85        let operation = self.normalized();
86        write!(formatter, "{}{}", operation.family, operation.addend)
87    }
88}