use crate::error::TriadError;
use crate::traits::{Relative, TriadRepr, TriadType};
use crate::triad::TriadBase;
use num_traits::{FromPrimitive, One};
use rstmt_core::{Dirac, PitchMod};
#[derive(
Clone,
Copy,
Debug,
Default,
Eq,
Hash,
Ord,
PartialEq,
PartialOrd,
strum::AsRefStr,
strum::Display,
strum::EnumCount,
strum::EnumIs,
strum::EnumIter,
strum::EnumString,
strum::VariantArray,
strum::VariantNames,
)]
#[cfg_attr(
feature = "serde",
derive(serde::Deserialize, serde::Serialize),
serde(rename_all = "lowercase")
)]
#[strum(serialize_all = "lowercase")]
pub enum LPR {
#[default]
#[cfg_attr(feature = "serde", serde(alias = "L", alias = "l", alias = "lead"))]
Leading = 0,
#[cfg_attr(feature = "serde", serde(alias = "P", alias = "p", alias = "par"))]
Parallel = 1,
#[cfg_attr(feature = "serde", serde(alias = "R", alias = "r", alias = "rel"))]
Relative = 2,
}
impl LPR {
pub const fn leading() -> Self {
LPR::Leading
}
pub const fn parallel() -> Self {
LPR::Parallel
}
pub const fn relative() -> Self {
LPR::Relative
}
pub fn iter() -> LPRIter {
use strum::IntoEnumIterator;
<LPR as IntoEnumIterator>::iter()
}
pub fn apply<X, Y>(self, triad: X) -> Y
where
Self: Dirac<X, Output = Y>,
{
<Self as Dirac<X>>::apply(self, triad)
}
fn dirac<S, T, K, R>(self, rhs: &TriadBase<S, K, T>) -> TriadBase<S, R, T>
where
K: TriadType<Rel = R>,
R: TriadType,
S: TriadRepr<Elem = T>,
T: Copy
+ FromPrimitive
+ One
+ PitchMod<Output = T>
+ core::ops::Add<Output = T>
+ core::ops::Sub<Output = T>,
{
let major = rhs.class().root() == 4;
let two = T::from_u8(2).unwrap();
let &x = rhs.chord().root();
let &y = rhs.chord().third();
let &z = rhs.chord().fifth();
let notes: [T; 3] = if major {
match self {
LPR::Leading => [y, z, (x - T::one()).pmod()],
LPR::Parallel => [x, (y - T::one()).pmod(), z],
LPR::Relative => [(z + two).pmod(), x, y],
}
} else {
match self {
LPR::Leading => [(z + T::one()).pmod(), x, y],
LPR::Parallel => [x, (y + T::one()).pmod(), z],
LPR::Relative => [y, z, (x - two).pmod()],
}
};
TriadBase {
chord: S::from_arr(notes),
class: <K as Relative>::rel(rhs.class()),
octave: *rhs.octave(),
}
}
}
impl<S, T, K> Dirac<TriadBase<S, K, T>> for LPR
where
K: TriadType,
K::Rel: TriadType,
S: TriadRepr<Elem = T>,
T: Copy
+ FromPrimitive
+ One
+ PitchMod<Output = T>
+ core::ops::Add<Output = T>
+ core::ops::Sub<Output = T>,
{
type Output = TriadBase<S, K::Rel, T>;
fn apply(self, rhs: TriadBase<S, K, T>) -> Self::Output {
self.dirac(&rhs)
}
}
impl<S, T, K> Dirac<TriadBase<S, K, T>> for &LPR
where
K: TriadType,
K::Rel: TriadType,
S: TriadRepr<Elem = T>,
T: Copy
+ FromPrimitive
+ One
+ PitchMod<Output = T>
+ core::ops::Add<Output = T>
+ core::ops::Sub<Output = T>,
{
type Output = TriadBase<S, K::Rel, T>;
fn apply(self, rhs: TriadBase<S, K, T>) -> Self::Output {
self.dirac(&rhs)
}
}
impl<S, T, K> Dirac<TriadBase<S, K, T>> for &mut LPR
where
K: TriadType,
K::Rel: TriadType,
S: TriadRepr<Elem = T>,
T: Copy
+ FromPrimitive
+ One
+ PitchMod<Output = T>
+ core::ops::Add<Output = T>
+ core::ops::Sub<Output = T>,
{
type Output = TriadBase<S, K::Rel, T>;
fn apply(self, rhs: TriadBase<S, K, T>) -> Self::Output {
self.dirac(&rhs)
}
}
impl<S, T, K> Dirac<&TriadBase<S, K, T>> for LPR
where
K: TriadType,
K::Rel: TriadType,
S: TriadRepr<Elem = T>,
T: Copy
+ FromPrimitive
+ One
+ PitchMod<Output = T>
+ core::ops::Add<Output = T>
+ core::ops::Sub<Output = T>,
{
type Output = TriadBase<S, K::Rel, T>;
fn apply(self, rhs: &TriadBase<S, K, T>) -> Self::Output {
self.dirac(rhs)
}
}
impl<S, T, K> Dirac<&TriadBase<S, K, T>> for &LPR
where
K: TriadType,
K::Rel: TriadType,
S: TriadRepr<Elem = T>,
T: Copy
+ FromPrimitive
+ One
+ PitchMod<Output = T>
+ core::ops::Add<Output = T>
+ core::ops::Sub<Output = T>,
{
type Output = TriadBase<S, K::Rel, T>;
fn apply(self, rhs: &TriadBase<S, K, T>) -> Self::Output {
self.dirac(rhs)
}
}
impl<S, T, K> Dirac<&mut TriadBase<S, K, T>> for LPR
where
K: TriadType,
K::Rel: TriadType,
S: TriadRepr<Elem = T>,
T: Copy
+ FromPrimitive
+ One
+ PitchMod<Output = T>
+ core::ops::Add<Output = T>
+ core::ops::Sub<Output = T>,
{
type Output = TriadBase<S, K::Rel, T>;
fn apply(self, rhs: &mut TriadBase<S, K, T>) -> Self::Output {
self.dirac(rhs)
}
}
impl TryFrom<char> for LPR {
type Error = TriadError;
fn try_from(value: char) -> Result<Self, Self::Error> {
use LPR::*;
match value.to_ascii_lowercase() {
'l' => Ok(Leading),
'p' => Ok(Parallel),
'r' => Ok(Relative),
v => Err(TriadError::TransformationParseCharError(v)),
}
}
}
macro_rules! impl_from_lpr {
($($T:ty),* $(,)?) => {
$(impl_from_lpr! { @impl $T })*
};
(@impl $T:ty) => {
impl From<LPR> for $T {
fn from(value: LPR) -> Self {
value as $T
}
}
impl From<$T> for LPR {
fn from(value: $T) -> Self {
match value % 3 {
0 => LPR::Leading,
1 => LPR::Parallel,
2 => LPR::Relative,
_ => unreachable!("Modular arithmetic failed"),
}
}
}
};
}
impl_from_lpr! { u8, u16, u32, u64, u128, usize, i8, i16, i32, i64, i128, isize }