qsym2/chartab/
unityroot.rs1use std::fmt;
4use std::ops::Mul;
5
6use derive_builder::Builder;
7use fraction::{self, ToPrimitive};
8use num::Complex;
9use num_traits::Pow;
10use serde::{Deserialize, Serialize};
11
12type F = fraction::GenericFraction<u32>;
13
14#[cfg(test)]
15#[path = "unityroot_tests.rs"]
16mod unityroot_tests;
17
18#[derive(Builder, Clone, PartialOrd, PartialEq, Eq, Hash, Serialize, Deserialize)]
23pub struct UnityRoot {
24 #[builder(setter(custom))]
27 pub(crate) fraction: F,
28}
29
30impl UnityRootBuilder {
31 fn fraction(&mut self, frac: F) -> &mut Self {
32 self.fraction = if F::from(0) <= frac && frac < F::from(1) {
33 Some(frac)
34 } else {
35 let numer = frac
36 .numer()
37 .unwrap_or_else(|| panic!("The numerator of {frac} cannot be extracted."));
38 let denom = frac
39 .denom()
40 .unwrap_or_else(|| panic!("The denominator of {frac} cannot be extracted."));
41 Some(F::new(numer.rem_euclid(*denom), *denom))
42 };
43 self
44 }
45}
46
47impl UnityRoot {
48 fn builder() -> UnityRootBuilder {
54 UnityRootBuilder::default()
55 }
56
57 #[must_use]
67 pub fn new(index: u32, order: u32) -> Self {
68 Self::builder()
69 .fraction(F::new(index, order))
70 .build()
71 .expect("Unable to construct a unity root.")
72 }
73
74 fn order(&self) -> &u32 {
80 self.fraction
81 .denom()
82 .expect("Unable to obtain the order of the root.")
83 }
84
85 fn index(&self) -> &u32 {
92 self.fraction
93 .numer()
94 .expect("Unable to obtain the index of the root.")
95 }
96
97 #[must_use]
103 pub fn complex_value(&self) -> Complex<f64> {
104 let theta = self
105 .fraction
106 .to_f64()
107 .expect("Unable to convert a fraction to `f64`.")
108 * std::f64::consts::PI
109 * 2.0;
110 Complex::<f64>::from_polar(1.0, theta)
111 }
112
113 #[must_use]
123 pub fn complex_conjugate(&self) -> Self {
124 Self::new(
125 self.order().checked_sub(*self.index()).unwrap_or_else(|| {
126 panic!(
127 "Unable to perform the subtraction `{} - {}` correctly.",
128 self.order(),
129 self.index()
130 )
131 }),
132 *self.order(),
133 )
134 }
135}
136
137impl<'a, 'b> Mul<&'a UnityRoot> for &'b UnityRoot {
138 type Output = UnityRoot;
139
140 fn mul(self, rhs: &'a UnityRoot) -> Self::Output {
141 #[allow(clippy::suspicious_arithmetic_impl)]
142 let fract_sum = self.fraction + rhs.fraction;
143 Self::Output::builder()
144 .fraction(fract_sum)
145 .build()
146 .unwrap_or_else(|_| {
147 panic!("Unable to construct a unity root with fraction {fract_sum}.")
148 })
149 }
150}
151
152impl Pow<i32> for &UnityRoot {
153 type Output = UnityRoot;
154
155 fn pow(self, rhs: i32) -> Self::Output {
156 Self::Output::new(
157 u32::try_from(
158 (i32::try_from(*self.index())
159 .unwrap_or_else(|_| panic!("Unable to convert `{}` to `i32`.", self.index()))
160 * rhs)
161 .rem_euclid(i32::try_from(*self.order()).unwrap_or_else(|_| {
162 panic!("Unable to convert `{}` to `i32`.", self.order())
163 })),
164 )
165 .expect("Unexpected negative remainder."),
166 *self.order(),
167 )
168 }
169}
170
171impl fmt::Display for UnityRoot {
172 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
173 if self.fraction == F::new(0u32, 4u32) {
174 write!(f, "1")
175 } else if self.fraction == F::new(1u32, 4u32) {
176 write!(f, "i")
177 } else if self.fraction == F::new(2u32, 4u32) {
178 write!(f, "-1")
179 } else if self.fraction == F::new(3u32, 4u32) {
180 write!(f, "-i")
181 } else if *self.index() == 1u32 {
182 write!(f, "E{}", self.order())
183 } else {
184 write!(f, "(E{})^{}", self.order(), self.index())
185 }
186 }
187}
188
189impl fmt::Debug for UnityRoot {
190 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
191 if self.fraction == F::new(0u32, 4u32) {
192 write!(f, "1")
193 } else if *self.index() == 1u32 {
194 write!(f, "E{}", self.order())
195 } else {
196 write!(f, "(E{})^{}", self.order(), self.index())
197 }
198 }
199}