1use crate::error::TriadError;
7use crate::traits::{Relative, TriadRepr, TriadType};
8use crate::triad::TriadBase;
9use num_traits::{FromPrimitive, One};
10use rstmt_core::{Dirac, PitchMod};
11
12#[derive(
35 Clone,
36 Copy,
37 Debug,
38 Default,
39 Eq,
40 Hash,
41 Ord,
42 PartialEq,
43 PartialOrd,
44 strum::AsRefStr,
45 strum::Display,
46 strum::EnumCount,
47 strum::EnumIs,
48 strum::EnumIter,
49 strum::EnumString,
50 strum::VariantArray,
51 strum::VariantNames,
52)]
53#[cfg_attr(
54 feature = "serde",
55 derive(serde::Deserialize, serde::Serialize),
56 serde(rename_all = "lowercase")
57)]
58#[strum(serialize_all = "lowercase")]
59pub enum LPR {
60 #[default]
62 #[cfg_attr(feature = "serde", serde(alias = "L", alias = "l", alias = "lead"))]
63 Leading = 0,
64 #[cfg_attr(feature = "serde", serde(alias = "P", alias = "p", alias = "par"))]
66 Parallel = 1,
67 #[cfg_attr(feature = "serde", serde(alias = "R", alias = "r", alias = "rel"))]
69 Relative = 2,
70}
71
72impl LPR {
73 pub const fn leading() -> Self {
75 LPR::Leading
76 }
77 pub const fn parallel() -> Self {
79 LPR::Parallel
80 }
81 pub const fn relative() -> Self {
83 LPR::Relative
84 }
85 pub fn iter() -> LPRIter {
86 use strum::IntoEnumIterator;
87 <LPR as IntoEnumIterator>::iter()
88 }
89 pub fn apply<X, Y>(self, triad: X) -> Y
91 where
92 Self: Dirac<X, Output = Y>,
93 {
94 <Self as Dirac<X>>::apply(self, triad)
95 }
96
97 fn dirac<S, T, K, R>(self, rhs: &TriadBase<S, K, T>) -> TriadBase<S, R, T>
98 where
99 K: TriadType<Rel = R>,
100 R: TriadType,
101 S: TriadRepr<Elem = T>,
102 T: Copy
103 + FromPrimitive
104 + One
105 + PitchMod<Output = T>
106 + core::ops::Add<Output = T>
107 + core::ops::Sub<Output = T>,
108 {
109 let major = rhs.class().root() == 4;
110 let two = T::from_u8(2).unwrap();
111
112 let &x = rhs.chord().root();
113 let &y = rhs.chord().third();
114 let &z = rhs.chord().fifth();
115
116 let notes: [T; 3] = if major {
117 match self {
118 LPR::Leading => [y, z, (x - T::one()).pmod()],
119 LPR::Parallel => [x, (y - T::one()).pmod(), z],
120 LPR::Relative => [(z + two).pmod(), x, y],
121 }
122 } else {
123 match self {
124 LPR::Leading => [(z + T::one()).pmod(), x, y],
125 LPR::Parallel => [x, (y + T::one()).pmod(), z],
126 LPR::Relative => [y, z, (x - two).pmod()],
127 }
128 };
129
130 TriadBase {
131 chord: S::from_arr(notes),
132 class: <K as Relative>::rel(&rhs.class()),
133 octave: *rhs.octave(),
134 }
135 }
136}
137
138impl<S, T, K> Dirac<TriadBase<S, K, T>> for LPR
139where
140 K: TriadType,
141 K::Rel: TriadType,
142 S: TriadRepr<Elem = T>,
143 T: Copy
144 + FromPrimitive
145 + One
146 + PitchMod<Output = T>
147 + core::ops::Add<Output = T>
148 + core::ops::Sub<Output = T>,
149{
150 type Output = TriadBase<S, K::Rel, T>;
151
152 fn apply(self, rhs: TriadBase<S, K, T>) -> Self::Output {
153 self.dirac(&rhs)
154 }
155}
156
157impl<S, T, K> Dirac<TriadBase<S, K, T>> for &LPR
158where
159 K: TriadType,
160 K::Rel: TriadType,
161 S: TriadRepr<Elem = T>,
162 T: Copy
163 + FromPrimitive
164 + One
165 + PitchMod<Output = T>
166 + core::ops::Add<Output = T>
167 + core::ops::Sub<Output = T>,
168{
169 type Output = TriadBase<S, K::Rel, T>;
170
171 fn apply(self, rhs: TriadBase<S, K, T>) -> Self::Output {
172 self.dirac(&rhs)
173 }
174}
175
176impl<S, T, K> Dirac<TriadBase<S, K, T>> for &mut LPR
177where
178 K: TriadType,
179 K::Rel: TriadType,
180 S: TriadRepr<Elem = T>,
181 T: Copy
182 + FromPrimitive
183 + One
184 + PitchMod<Output = T>
185 + core::ops::Add<Output = T>
186 + core::ops::Sub<Output = T>,
187{
188 type Output = TriadBase<S, K::Rel, T>;
189
190 fn apply(self, rhs: TriadBase<S, K, T>) -> Self::Output {
191 self.dirac(&rhs)
192 }
193}
194
195impl<S, T, K> Dirac<&TriadBase<S, K, T>> for LPR
196where
197 K: TriadType,
198 K::Rel: TriadType,
199 S: TriadRepr<Elem = T>,
200 T: Copy
201 + FromPrimitive
202 + One
203 + PitchMod<Output = T>
204 + core::ops::Add<Output = T>
205 + core::ops::Sub<Output = T>,
206{
207 type Output = TriadBase<S, K::Rel, T>;
208
209 fn apply(self, rhs: &TriadBase<S, K, T>) -> Self::Output {
210 self.dirac(rhs)
211 }
212}
213
214impl<S, T, K> Dirac<&TriadBase<S, K, T>> for &LPR
215where
216 K: TriadType,
217 K::Rel: TriadType,
218 S: TriadRepr<Elem = T>,
219 T: Copy
220 + FromPrimitive
221 + One
222 + PitchMod<Output = T>
223 + core::ops::Add<Output = T>
224 + core::ops::Sub<Output = T>,
225{
226 type Output = TriadBase<S, K::Rel, T>;
227
228 fn apply(self, rhs: &TriadBase<S, K, T>) -> Self::Output {
229 self.dirac(rhs)
230 }
231}
232
233impl<S, T, K> Dirac<&mut TriadBase<S, K, T>> for LPR
234where
235 K: TriadType,
236 K::Rel: TriadType,
237 S: TriadRepr<Elem = T>,
238 T: Copy
239 + FromPrimitive
240 + One
241 + PitchMod<Output = T>
242 + core::ops::Add<Output = T>
243 + core::ops::Sub<Output = T>,
244{
245 type Output = TriadBase<S, K::Rel, T>;
246
247 fn apply(self, rhs: &mut TriadBase<S, K, T>) -> Self::Output {
248 self.dirac(rhs)
249 }
250}
251
252impl TryFrom<char> for LPR {
253 type Error = TriadError;
254 fn try_from(value: char) -> Result<Self, Self::Error> {
255 use LPR::*;
256 match value.to_ascii_lowercase() {
257 'l' => Ok(Leading),
258 'p' => Ok(Parallel),
259 'r' => Ok(Relative),
260 v => Err(TriadError::TransformationParseCharError(v)),
261 }
262 }
263}
264
265macro_rules! impl_from_lpr {
266 ($($T:ty),* $(,)?) => {
267 $(impl_from_lpr! { @impl $T })*
268 };
269 (@impl $T:ty) => {
270 impl From<LPR> for $T {
271 fn from(value: LPR) -> Self {
272 value as $T
273 }
274 }
275
276 impl From<$T> for LPR {
277 fn from(value: $T) -> Self {
278 match value % 3 {
279 0 => LPR::Leading,
280 1 => LPR::Parallel,
281 2 => LPR::Relative,
282 _ => unreachable!("Modular arithmetic failed"),
283 }
284 }
285 }
286 };
287}
288
289impl_from_lpr! { u8, u16, u32, u64, u128, usize, i8, i16, i32, i64, i128, isize }