rstmt_core/pitch/impls/
impl_pclass_ops.rs

1/*
2    Appellation: impl_pclass_ops <module>
3    Created At: 2025.12.23:14:33:56
4    Contrib: @FL03
5*/
6use crate::pitch::pitch_class::PitchClass;
7use crate::pitch::traits::{RawAccidental, RawPitchClass};
8use rstmt_traits::PitchMod;
9
10/// Add two pitch-classes producing a wrapped semitone count in the tonal space.
11///
12/// Note: returning a compile-time `PitchClass` subtype from an operator requires a
13/// type-level index; that's not possible for arbitrary runtime sums. We therefore
14/// return an `i32` semitone value (0..11) using `PitchMod::pmod`. Callers can
15/// convert that numeric result into any runtime/compile-time representation they
16/// prefer.
17impl<T1, A1, T2, A2> core::ops::Add<PitchClass<T2, A2>> for PitchClass<T1, A1>
18where
19    T1: RawPitchClass<Tag = A1>,
20    A1: RawAccidental,
21    T2: RawPitchClass<Tag = A2>,
22    A2: RawAccidental,
23{
24    type Output = isize;
25
26    fn add(self, rhs: PitchClass<T2, A2>) -> Self::Output {
27        (self.get().index() + rhs.get().index()).pmod()
28    }
29}
30
31impl<T1, A1, T2, A2> core::ops::Sub<PitchClass<T2, A2>> for PitchClass<T1, A1>
32where
33    T1: RawPitchClass<Tag = A1>,
34    A1: RawAccidental,
35    T2: RawPitchClass<Tag = A2>,
36    A2: RawAccidental,
37{
38    /// Subtract two pitch-classes producing a wrapped signed semitone interval.
39    type Output = isize;
40
41    fn sub(self, rhs: PitchClass<T2, A2>) -> Self::Output {
42        (self.get().index() - rhs.get().index()).pmod()
43    }
44}