spectrum_analyzer/
frequency.rs

1/*
2MIT License
3
4Copyright (c) 2023 Philipp Schuster
5
6Permission is hereby granted, free of charge, to any person obtaining a copy
7of this software and associated documentation files (the "Software"), to deal
8in the Software without restriction, including without limitation the rights
9to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10copies of the Software, and to permit persons to whom the Software is
11furnished to do so, subject to the following conditions:
12
13The above copyright notice and this permission notice shall be included in all
14copies or substantial portions of the Software.
15
16THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22SOFTWARE.
23*/
24//! Module for the struct [`OrderableF32`] and the two
25//! convenient type definitions [`Frequency`] and [`FrequencyValue`].
26
27use core::cmp::Ordering;
28use core::fmt::{Display, Formatter, Result};
29use core::ops::{Add, Div, Mul, Sub};
30
31/// A frequency in Hertz. A convenient wrapper type around `f32`.
32pub type Frequency = OrderableF32;
33/// The value of a [`Frequency`] in a frequency spectrum. Also called the
34/// magnitude.
35pub type FrequencyValue = OrderableF32;
36
37/// Wrapper around [`f32`] that guarantees a valid number, hence, the number is
38/// neither `NaN` or `infinite`. This makes the number orderable and sortable.
39#[derive(Debug, Copy, Clone, Default)]
40pub struct OrderableF32(f32);
41
42impl OrderableF32 {
43    #[inline]
44    pub const fn val(&self) -> f32 {
45        self.0
46    }
47}
48
49impl From<f32> for OrderableF32 {
50    #[inline]
51    fn from(val: f32) -> Self {
52        debug_assert!(!val.is_nan(), "NaN-values are not supported!");
53        debug_assert!(!val.is_infinite(), "Infinite-values are not supported!");
54        Self(val)
55    }
56}
57
58impl Display for OrderableF32 {
59    fn fmt(&self, f: &mut Formatter<'_>) -> Result {
60        write!(f, "{}", self.0)
61    }
62}
63
64impl Ord for OrderableF32 {
65    #[inline]
66    fn cmp(&self, other: &Self) -> Ordering {
67        if self.val() < other.val() {
68            Ordering::Less
69        } else if self.val() == other.val() {
70            Ordering::Equal
71        } else {
72            Ordering::Greater
73        }
74    }
75}
76
77impl Eq for OrderableF32 {}
78
79impl PartialEq for OrderableF32 {
80    #[inline]
81    fn eq(&self, other: &Self) -> bool {
82        matches!(self.cmp(other), Ordering::Equal)
83    }
84}
85
86impl PartialOrd for OrderableF32 {
87    #[allow(clippy::float_cmp)]
88    #[inline]
89    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
90        Some(self.cmp(other))
91    }
92}
93
94impl Add for OrderableF32 {
95    type Output = Self;
96
97    #[inline]
98    fn add(self, other: Self) -> Self::Output {
99        (self.val() + other.val()).into()
100    }
101}
102
103impl Sub for OrderableF32 {
104    type Output = Self;
105
106    #[inline]
107    fn sub(self, other: Self) -> Self::Output {
108        (self.val() - other.val()).into()
109    }
110}
111
112impl Mul for OrderableF32 {
113    type Output = Self;
114
115    #[inline]
116    fn mul(self, other: Self) -> Self::Output {
117        (self.val() * other.val()).into()
118    }
119}
120
121impl Div for OrderableF32 {
122    type Output = Self;
123
124    #[inline]
125    fn div(self, other: Self) -> Self::Output {
126        let quotient = self.val() / other.val();
127        debug_assert!(!quotient.is_nan(), "NaN is not allowed");
128        debug_assert!(!quotient.is_infinite(), "INFINITY is not allowed");
129        quotient.into()
130    }
131}
132
133#[cfg(test)]
134mod tests {
135    use super::*;
136
137    #[test]
138    fn test_orderablef32() {
139        let f1: OrderableF32 = (2.0_f32).into();
140        let f2: OrderableF32 = (-7.0_f32).into();
141
142        let f3 = f1 + f2;
143        let f4 = f1 - f2;
144
145        assert_eq!(-5.0, f3.val(), "add must work");
146        assert_eq!(9.0, f4.val(), "add must work");
147        assert!(f2 < f1, "Compare must work");
148        assert!(f1 > f2, "Compare must work");
149        #[allow(clippy::eq_op)]
150        {
151            assert_eq!(f1, f1, "Equal must work");
152        }
153    }
154}