1use core::cmp::Ordering;
28use core::fmt::{Debug, Display, Formatter, Result};
29use core::ops::{Add, Div, Mul, Neg, Sub};
30
31pub type Frequency = NonNegF32;
33pub type FrequencyValue = FiniteF32;
41
42#[derive(Copy, Clone, Default)]
62#[repr(transparent)]
63pub struct FiniteF32(f32);
64
65impl FiniteF32 {
66 #[inline]
68 #[must_use]
69 pub const fn try_new(val: f32) -> Option<Self> {
70 if val.is_finite() {
71 Some(Self(val))
72 } else {
73 None
74 }
75 }
76
77 #[inline]
79 #[must_use]
80 pub const fn val(self) -> f32 {
81 self.0
82 }
83}
84
85impl From<f32> for FiniteF32 {
86 #[inline]
89 fn from(val: f32) -> Self {
90 Self::try_new(val).expect("value should be finite")
91 }
92}
93
94impl From<FiniteF32> for f32 {
95 #[inline]
96 fn from(val: FiniteF32) -> Self {
97 val.0
98 }
99}
100
101impl Display for FiniteF32 {
102 fn fmt(&self, f: &mut Formatter<'_>) -> Result {
103 write!(f, "{}", self.0)
104 }
105}
106
107impl Debug for FiniteF32 {
108 fn fmt(&self, f: &mut Formatter<'_>) -> Result {
109 write!(f, "{:?}", self.0)
110 }
111}
112
113impl Ord for FiniteF32 {
114 #[inline]
115 fn cmp(&self, other: &Self) -> Ordering {
116 if self.0 < other.0 {
117 Ordering::Less
118 } else if self.0 == other.0 {
119 Ordering::Equal
120 } else {
121 Ordering::Greater
122 }
123 }
124}
125
126impl Eq for FiniteF32 {}
127
128impl PartialEq for FiniteF32 {
129 #[inline]
130 fn eq(&self, other: &Self) -> bool {
131 self.0 == other.0
132 }
133}
134
135impl PartialOrd for FiniteF32 {
136 #[inline]
137 fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
138 Some(self.cmp(other))
139 }
140}
141
142impl PartialEq<f32> for FiniteF32 {
143 #[inline]
144 fn eq(&self, other: &f32) -> bool {
145 self.0 == *other
146 }
147}
148
149impl PartialEq<FiniteF32> for f32 {
150 #[inline]
151 fn eq(&self, other: &FiniteF32) -> bool {
152 *self == other.0
153 }
154}
155
156impl PartialOrd<f32> for FiniteF32 {
157 #[inline]
158 fn partial_cmp(&self, other: &f32) -> Option<Ordering> {
159 self.0.partial_cmp(other)
160 }
161}
162
163impl PartialOrd<FiniteF32> for f32 {
164 #[inline]
165 fn partial_cmp(&self, other: &FiniteF32) -> Option<Ordering> {
166 self.partial_cmp(&other.0)
167 }
168}
169
170impl Neg for FiniteF32 {
171 type Output = Self;
172
173 #[inline]
175 fn neg(self) -> Self::Output {
176 Self(-self.0)
177 }
178}
179
180macro_rules! impl_op {
184 ($trait:ident, $method:ident) => {
185 impl $trait for FiniteF32 {
186 type Output = Self;
187
188 #[inline]
191 fn $method(self, rhs: Self) -> Self::Output {
192 Self::from($trait::$method(self.0, rhs.0))
193 }
194 }
195
196 impl $trait<f32> for FiniteF32 {
197 type Output = f32;
198
199 #[inline]
200 fn $method(self, rhs: f32) -> Self::Output {
201 $trait::$method(self.0, rhs)
202 }
203 }
204
205 impl $trait<FiniteF32> for f32 {
206 type Output = Self;
207
208 #[inline]
209 fn $method(self, rhs: FiniteF32) -> Self::Output {
210 $trait::$method(self, rhs.0)
211 }
212 }
213 };
214}
215
216impl_op!(Add, add);
217impl_op!(Sub, sub);
218impl_op!(Mul, mul);
219impl_op!(Div, div);
220
221#[derive(Copy, Clone, Default)]
242#[repr(transparent)]
243pub struct NonNegF32(FiniteF32);
244
245impl NonNegF32 {
246 #[inline]
248 #[must_use]
249 pub const fn try_new(val: f32) -> Option<Self> {
250 match FiniteF32::try_new(val) {
251 Some(val) if val.val() >= 0.0 => Some(Self(val)),
252 _ => None,
253 }
254 }
255
256 #[inline]
258 #[must_use]
259 pub const fn val(self) -> f32 {
260 self.0.val()
261 }
262}
263
264impl From<f32> for NonNegF32 {
265 #[inline]
268 fn from(val: f32) -> Self {
269 Self::try_new(val).expect("value should be finite and not negative")
270 }
271}
272
273impl From<FiniteF32> for NonNegF32 {
274 #[inline]
277 fn from(val: FiniteF32) -> Self {
278 Self::try_new(val.val()).expect("value should not be negative")
279 }
280}
281
282impl From<NonNegF32> for FiniteF32 {
283 #[inline]
284 fn from(val: NonNegF32) -> Self {
285 val.0
286 }
287}
288
289impl From<NonNegF32> for f32 {
290 #[inline]
291 fn from(val: NonNegF32) -> Self {
292 val.val()
293 }
294}
295
296impl Display for NonNegF32 {
297 fn fmt(&self, f: &mut Formatter<'_>) -> Result {
298 Display::fmt(&self.0, f)
299 }
300}
301
302impl Debug for NonNegF32 {
303 fn fmt(&self, f: &mut Formatter<'_>) -> Result {
304 Debug::fmt(&self.0, f)
305 }
306}
307
308impl Ord for NonNegF32 {
309 #[inline]
310 fn cmp(&self, other: &Self) -> Ordering {
311 self.0.cmp(&other.0)
312 }
313}
314
315impl Eq for NonNegF32 {}
316
317impl PartialEq for NonNegF32 {
318 #[inline]
319 fn eq(&self, other: &Self) -> bool {
320 self.0 == other.0
321 }
322}
323
324impl PartialOrd for NonNegF32 {
325 #[inline]
326 fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
327 Some(self.cmp(other))
328 }
329}
330
331impl PartialEq<f32> for NonNegF32 {
332 #[inline]
333 fn eq(&self, other: &f32) -> bool {
334 self.0 == *other
335 }
336}
337
338impl PartialEq<NonNegF32> for f32 {
339 #[inline]
340 fn eq(&self, other: &NonNegF32) -> bool {
341 *self == other.0
342 }
343}
344
345impl PartialOrd<f32> for NonNegF32 {
346 #[inline]
347 fn partial_cmp(&self, other: &f32) -> Option<Ordering> {
348 self.0.partial_cmp(other)
349 }
350}
351
352impl PartialOrd<NonNegF32> for f32 {
353 #[inline]
354 fn partial_cmp(&self, other: &NonNegF32) -> Option<Ordering> {
355 self.partial_cmp(&other.0)
356 }
357}
358
359impl Neg for NonNegF32 {
360 type Output = FiniteF32;
361
362 #[inline]
364 fn neg(self) -> Self::Output {
365 -self.0
366 }
367}
368
369impl Sub for NonNegF32 {
370 type Output = FiniteF32;
371
372 #[inline]
376 fn sub(self, rhs: Self) -> Self::Output {
377 FiniteF32::from(self.val() - rhs.val())
378 }
379}
380
381macro_rules! impl_non_neg_op {
383 ($trait:ident, $method:ident) => {
384 impl $trait for NonNegF32 {
385 type Output = Self;
386
387 #[inline]
390 fn $method(self, rhs: Self) -> Self::Output {
391 Self::from($trait::$method(self.val(), rhs.val()))
392 }
393 }
394
395 impl $trait<f32> for NonNegF32 {
396 type Output = f32;
397
398 #[inline]
399 fn $method(self, rhs: f32) -> Self::Output {
400 $trait::$method(self.val(), rhs)
401 }
402 }
403
404 impl $trait<NonNegF32> for f32 {
405 type Output = Self;
406
407 #[inline]
408 fn $method(self, rhs: NonNegF32) -> Self::Output {
409 $trait::$method(self, rhs.val())
410 }
411 }
412 };
413}
414
415impl_non_neg_op!(Add, add);
416impl_non_neg_op!(Mul, mul);
417impl_non_neg_op!(Div, div);
418
419impl Sub<f32> for NonNegF32 {
420 type Output = f32;
421
422 #[inline]
423 fn sub(self, rhs: f32) -> Self::Output {
424 self.val() - rhs
425 }
426}
427
428impl Sub<NonNegF32> for f32 {
429 type Output = Self;
430
431 #[inline]
432 fn sub(self, rhs: NonNegF32) -> Self::Output {
433 self - rhs.val()
434 }
435}
436
437#[cfg(test)]
438mod tests {
439 use super::*;
440
441 #[test]
442 fn test_finite_f32_construction() {
443 assert_eq!(Some(FiniteF32(0.5)), FiniteF32::try_new(0.5));
444 assert_eq!(None, FiniteF32::try_new(f32::NAN));
445 assert_eq!(None, FiniteF32::try_new(f32::INFINITY));
446 assert_eq!(None, FiniteF32::try_new(f32::NEG_INFINITY));
447 assert_eq!(0.5, f32::from(FiniteF32::from(0.5)));
448 }
449
450 #[test]
451 #[should_panic(expected = "value should be finite")]
452 fn test_finite_f32_rejects_nan() {
453 let _ = FiniteF32::from(f32::NAN);
454 }
455
456 #[test]
457 fn test_finite_f32_compares_with_f32() {
458 let val = FiniteF32::from(0.5);
459
460 assert_eq!(val, 0.5);
461 assert_eq!(0.5, val);
462 assert!(val > 0.25);
463 assert!(0.75 > val);
464 assert!(val < 0.75);
465 }
466
467 #[test]
468 fn test_finite_f32_arithmetic() {
469 let a = FiniteF32::from(3.0);
470 let b = FiniteF32::from(2.0);
471
472 assert_eq!(FiniteF32::from(5.0), a + b);
474 assert_eq!(FiniteF32::from(1.0), a - b);
475 assert_eq!(FiniteF32::from(6.0), a * b);
476 assert_eq!(FiniteF32::from(1.5), a / b);
477
478 assert_eq!(4.0_f32, a + 1.0);
480 assert_eq!(4.0_f32, 1.0 + a);
481 assert_eq!(-3.0_f32, (-a).val());
482
483 assert!((f32::MAX + FiniteF32::from(f32::MAX)).is_infinite());
485 assert!((FiniteF32::from(0.0) / 0.0).is_nan());
486 }
487
488 #[test]
489 fn test_non_neg_f32_construction() {
490 assert_eq!(Some(NonNegF32::from(0.0)), NonNegF32::try_new(0.0));
491 assert_eq!(None, NonNegF32::try_new(-0.5));
492 assert_eq!(None, NonNegF32::try_new(f32::NAN));
493 assert_eq!(0.5, f32::from(NonNegF32::from(0.5)));
494 assert_eq!(FiniteF32::from(0.5), FiniteF32::from(NonNegF32::from(0.5)));
495 }
496
497 #[test]
498 #[should_panic(expected = "value should be finite and not negative")]
499 fn test_non_neg_f32_rejects_negative() {
500 let _ = NonNegF32::from(-0.5);
501 }
502
503 #[test]
504 fn test_non_neg_f32_arithmetic() {
505 let a = NonNegF32::from(3.0);
506 let b = NonNegF32::from(2.0);
507
508 assert_eq!(NonNegF32::from(5.0), a + b);
510 assert_eq!(NonNegF32::from(6.0), a * b);
511 assert_eq!(NonNegF32::from(1.5), a / b);
512
513 assert_eq!(FiniteF32::from(-1.0), b - a);
515 assert_eq!(FiniteF32::from(-3.0), -a);
516
517 assert_eq!(1.0_f32, a - 2.0);
519 assert_eq!(2.0_f32, 5.0 - a);
520 }
521
522 #[test]
523 #[should_panic(expected = "value should be finite")]
524 fn test_finite_f32_arithmetic_overflow_panics() {
525 let max = FiniteF32::from(f32::MAX);
526 let _ = max + max;
527 }
528}