ebi/
fraction_f64.rs

1use core::f64;
2use std::{
3    borrow::Borrow,
4    cmp::Ordering,
5    fmt::Display,
6    hash::Hash,
7    iter::Sum,
8    ops::{Add, AddAssign, Div, DivAssign, Mul, MulAssign, Neg, Sub, SubAssign},
9    str::FromStr,
10    sync::Arc,
11};
12
13use anyhow::{Error, Result, anyhow};
14
15use crate::{exact::MaybeExact, fraction::EPSILON};
16
17use super::traits::{One, Signed, Zero};
18
19#[derive(Debug, Clone, Copy)]
20pub struct FractionF64(pub f64);
21
22impl FractionF64 {
23    pub fn two() -> Self {
24        Self(2.0)
25    }
26
27    pub fn one_minus(self) -> Self {
28        Self(1.0 - self.0)
29    }
30
31    pub fn is_sign_negative(&self) -> bool {
32        self.0.is_sign_negative()
33    }
34
35    pub fn is_sign_positive(&self) -> bool {
36        self.0.is_sign_positive()
37    }
38
39    /// Returns true if the value is Infinity (does not matter positive or negative)
40    pub fn is_infinite(&self) -> bool {
41        self.0.is_infinite()
42    }
43
44    pub fn is_nan(&self) -> bool {
45        self.0.is_nan()
46    }
47
48    pub fn infinity() -> Self {
49        Self(f64::INFINITY)
50    }
51
52    pub fn neg_infinity() -> Self {
53        Self(f64::NEG_INFINITY)
54    }
55
56    pub fn nan() -> Self {
57        Self(f64::NAN)
58    }
59
60    pub fn sqrt_abs(&self, _decimal_places: u32) -> FractionF64 {
61        Self(self.0.abs().sqrt())
62    }
63
64    /**
65     * 1/self
66     */
67    pub fn recip(&self) -> Self {
68        Self(self.0.recip())
69    }
70}
71
72impl MaybeExact for FractionF64 {
73    type Approximate = f64;
74    type Exact = fraction::BigFraction;
75
76    fn is_exact(&self) -> bool {
77        false
78    }
79
80    fn extract_approx(&self) -> Result<f64> {
81        Ok(self.0)
82    }
83
84    fn extract_exact(&self) -> Result<&fraction::BigFraction> {
85        Err(anyhow!("cannot extract a fraction from a float"))
86    }
87}
88
89impl One for FractionF64 {
90    fn one() -> Self {
91        Self(1.0)
92    }
93
94    fn is_one(&self) -> bool {
95        (self.0 - 1.0).abs() - &EPSILON < 0.0
96    }
97}
98
99impl Zero for FractionF64 {
100    fn zero() -> Self {
101        Self(0.0)
102    }
103
104    fn is_zero(&self) -> bool {
105        self.0.abs() - &EPSILON < 0.0
106    }
107}
108
109impl Signed for FractionF64 {
110    fn abs(&self) -> Self {
111        Self(self.0.abs())
112    }
113
114    fn is_positive(&self) -> bool {
115        self.0 != 0f64 && self.0 > EPSILON
116    }
117
118    fn is_negative(&self) -> bool {
119        self.0 != 0f64 && self.0 < -EPSILON
120    }
121
122    fn is_not_negative(&self) -> bool {
123        self.0.is_not_negative()
124    }
125
126    fn is_not_positive(&self) -> bool {
127        self.0.is_not_positive()
128    }
129}
130
131impl PartialEq for FractionF64 {
132    fn eq(&self, other: &Self) -> bool {
133        match (self, other) {
134            (FractionF64(l0), FractionF64(r0)) => l0 - EPSILON <= *r0 && *r0 <= l0 + EPSILON,
135        }
136    }
137}
138
139impl Display for FractionF64 {
140    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
141        self.0.fmt(f)
142    }
143}
144
145impl From<f64> for FractionF64 {
146    fn from(value: f64) -> Self {
147        Self(value)
148    }
149}
150
151impl From<&FractionF64> for FractionF64 {
152    fn from(value: &FractionF64) -> Self {
153        value.clone()
154    }
155}
156
157impl From<Arc<FractionF64>> for FractionF64 {
158    fn from(value: Arc<FractionF64>) -> Self {
159        value.as_ref().clone()
160    }
161}
162
163impl From<&Arc<FractionF64>> for FractionF64 {
164    fn from(value: &Arc<FractionF64>) -> Self {
165        value.as_ref().clone()
166    }
167}
168
169impl FromStr for FractionF64 {
170    type Err = Error;
171
172    fn from_str(s: &str) -> std::result::Result<Self, Self::Err> {
173        match f64::from_str(s) {
174            Ok(f) => Ok(Self(f)),
175            Err(_) => match fraction::Fraction::from_str(s) {
176                Ok(f) => Ok(Self(format!("{:.20}", f).parse::<f64>()?)),
177                Err(e) => Err(e.into()),
178            },
179        }
180    }
181}
182
183impl Eq for FractionF64 {}
184
185impl PartialOrd for FractionF64 {
186    fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
187        self.0.partial_cmp(&other.0)
188    }
189}
190
191impl Hash for FractionF64 {
192    /**
193     * For good reasons, Rust does not support hashing of doubles. However, we need it to store distributions in a hashmap.
194     * Approximate arithmetic is discouraged
195     */
196    fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
197        f64::to_bits(self.0).hash(state)
198    }
199}
200
201impl Ord for FractionF64 {
202    fn cmp(&self, other: &Self) -> Ordering {
203        if self.0.is_nan() && other.0.is_nan() {
204            Ordering::Equal
205        } else if self.0.is_nan() {
206            Ordering::Less
207        } else if other.0.is_nan() {
208            Ordering::Greater
209        } else if self.0 == f64::INFINITY {
210            if other.0 == f64::INFINITY {
211                Ordering::Equal
212            } else {
213                Ordering::Greater
214            }
215        } else if other.0 == f64::INFINITY {
216            Ordering::Less
217        } else if self.0 == f64::NEG_INFINITY {
218            if other.0 == f64::NEG_INFINITY {
219                Ordering::Equal
220            } else {
221                Ordering::Less
222            }
223        } else if other.0 == f64::NEG_INFINITY {
224            Ordering::Greater
225        } else {
226            self.0.partial_cmp(&other.0).unwrap()
227        }
228    }
229}
230
231impl Sum for FractionF64 {
232    fn sum<I: Iterator<Item = Self>>(iter: I) -> Self {
233        iter.fold(Self::zero(), |sum, f| &sum + &f)
234    }
235}
236
237impl<'a> Sum<&'a FractionF64> for FractionF64 {
238    fn sum<I: Iterator<Item = &'a FractionF64>>(iter: I) -> Self {
239        iter.fold(FractionF64::zero(), |sum, f| &sum + f)
240    }
241}
242
243impl Neg for FractionF64 {
244    type Output = FractionF64;
245
246    fn neg(self) -> Self::Output {
247        Self(self.0.neg())
248    }
249}
250
251impl<'a> Neg for &'a FractionF64 {
252    type Output = FractionF64;
253
254    fn neg(self) -> Self::Output {
255        FractionF64(self.0.neg())
256    }
257}
258
259impl Add<&FractionF64> for &FractionF64 {
260    type Output = FractionF64;
261
262    fn add(self, rhs: &FractionF64) -> Self::Output {
263        FractionF64(self.0.add(rhs.0))
264    }
265}
266
267impl<T> AddAssign<T> for FractionF64
268where
269    T: Borrow<FractionF64>,
270{
271    fn add_assign(&mut self, rhs: T) {
272        let rhs = rhs.borrow();
273        self.0.add_assign(rhs.0)
274    }
275}
276
277impl Sub<&FractionF64> for &FractionF64 {
278    type Output = FractionF64;
279
280    fn sub(self, rhs: &FractionF64) -> Self::Output {
281        FractionF64(self.0.sub(rhs.0))
282    }
283}
284
285impl<T> SubAssign<T> for FractionF64
286where
287    T: Borrow<FractionF64>,
288{
289    fn sub_assign(&mut self, rhs: T) {
290        let rhs = rhs.borrow();
291        self.0.sub_assign(rhs.0)
292    }
293}
294
295impl Mul<&FractionF64> for &FractionF64 {
296    type Output = FractionF64;
297
298    fn mul(self, rhs: &FractionF64) -> Self::Output {
299        FractionF64(self.0.mul(rhs.0))
300    }
301}
302
303impl<T> MulAssign<T> for FractionF64
304where
305    T: Borrow<FractionF64>,
306{
307    fn mul_assign(&mut self, rhs: T) {
308        let rhs = rhs.borrow();
309        self.0.mul_assign(rhs.0)
310    }
311}
312
313impl Div<&FractionF64> for &FractionF64 {
314    type Output = FractionF64;
315
316    fn div(self, rhs: &FractionF64) -> Self::Output {
317        FractionF64(self.0.div(rhs.0))
318    }
319}
320
321impl<T> DivAssign<T> for FractionF64
322where
323    T: Borrow<FractionF64>,
324{
325    fn div_assign(&mut self, rhs: T) {
326        let rhs = rhs.borrow();
327        self.0.div_assign(rhs.0)
328    }
329}
330
331//======================== primitive types ========================//
332
333impl Mul<f64> for FractionF64 {
334    type Output = FractionF64;
335
336    fn mul(self, rhs: f64) -> Self::Output {
337        Self(self.0 * rhs)
338    }
339}
340
341impl Div<f64> for FractionF64 {
342    type Output = FractionF64;
343
344    fn div(self, rhs: f64) -> Self::Output {
345        Self(self.0 / rhs)
346    }
347}
348
349impl Add<f64> for FractionF64 {
350    type Output = FractionF64;
351
352    fn add(self, rhs: f64) -> Self::Output {
353        Self(self.0 + rhs)
354    }
355}
356
357impl Sub<f64> for FractionF64 {
358    type Output = FractionF64;
359
360    fn sub(self, rhs: f64) -> Self::Output {
361        Self(self.0 - rhs)
362    }
363}
364
365macro_rules! from {
366    ($t:ident) => {
367        impl From<$t> for FractionF64 {
368            fn from(value: $t) -> Self {
369                Self(value as f64)
370            }
371        }
372    };
373}
374
375macro_rules! from_signed {
376    ($t:ident) => {
377        impl From<$t> for FractionF64 {
378            fn from(value: $t) -> Self {
379                Self(value as f64)
380            }
381        }
382    };
383}
384
385macro_rules! from_tuple_u_u {
386    ($t:ident,$tt:ident) => {
387        impl From<($t, $tt)> for FractionF64 {
388            fn from(value: ($t, $tt)) -> Self {
389                Self(value.0 as f64 / value.1 as f64)
390            }
391        }
392    };
393}
394
395macro_rules! from_tuple_u_i {
396    ($t:ident,$tt:ident) => {
397        impl From<($t, $tt)> for FractionF64 {
398            fn from(value: ($t, $tt)) -> Self {
399                Self(value.0 as f64 / value.1 as f64)
400            }
401        }
402    };
403}
404
405macro_rules! from_tuple_i_u {
406    ($t:ident,$tt:ident) => {
407        impl From<($t, $tt)> for FractionF64 {
408            fn from(value: ($t, $tt)) -> Self {
409                Self(value.0 as f64 / value.1 as f64)
410            }
411        }
412    };
413}
414
415macro_rules! from_tuple_i_i {
416    ($t:ident,$tt:ident) => {
417        impl From<($t, $tt)> for FractionF64 {
418            fn from(value: ($t, $tt)) -> Self {
419                Self(value.0 as f64 / value.1 as f64)
420            }
421        }
422    };
423}
424
425macro_rules! add {
426    ($t:ident) => {
427        impl<'a> Add<$t> for &'a FractionF64 {
428            type Output = FractionF64;
429
430            fn add(self, rhs: $t) -> Self::Output {
431                let rhs: FractionF64 = rhs.into();
432                self.add(&rhs)
433            }
434        }
435    };
436}
437
438macro_rules! add_assign {
439    ($t:ident) => {
440        impl AddAssign<$t> for FractionF64 {
441            fn add_assign(&mut self, rhs: $t) {
442                let rhs: FractionF64 = rhs.into();
443                self.add_assign(rhs)
444            }
445        }
446    };
447}
448
449macro_rules! sub {
450    ($t:ident) => {
451        impl<'a> Sub<$t> for &'a FractionF64 {
452            type Output = FractionF64;
453
454            fn sub(self, rhs: $t) -> Self::Output {
455                let rhs: FractionF64 = rhs.into();
456                self.sub(&rhs)
457            }
458        }
459    };
460}
461
462macro_rules! sub_assign {
463    ($t:ident) => {
464        impl SubAssign<$t> for FractionF64 {
465            fn sub_assign(&mut self, rhs: $t) {
466                let rhs: FractionF64 = rhs.into();
467                self.sub_assign(rhs)
468            }
469        }
470    };
471}
472
473macro_rules! mul {
474    ($t:ident) => {
475        impl<'a> Mul<$t> for &'a FractionF64 {
476            type Output = FractionF64;
477
478            fn mul(self, rhs: $t) -> Self::Output {
479                let rhs: FractionF64 = rhs.into();
480                self.mul(&rhs)
481            }
482        }
483    };
484}
485
486macro_rules! mul_assign {
487    ($t:ident) => {
488        impl MulAssign<$t> for FractionF64 {
489            fn mul_assign(&mut self, rhs: $t) {
490                let rhs: FractionF64 = rhs.into();
491                self.mul_assign(rhs)
492            }
493        }
494    };
495}
496
497macro_rules! div {
498    ($t:ident) => {
499        impl<'a> Div<$t> for &'a FractionF64 {
500            type Output = FractionF64;
501
502            fn div(self, rhs: $t) -> Self::Output {
503                let rhs: FractionF64 = rhs.into();
504                self.div(&rhs)
505            }
506        }
507    };
508}
509
510macro_rules! div_assign {
511    ($t:ident) => {
512        impl DivAssign<$t> for FractionF64 {
513            fn div_assign(&mut self, rhs: $t) {
514                let rhs: FractionF64 = rhs.into();
515                self.div_assign(rhs)
516            }
517        }
518    };
519}
520
521macro_rules! ttype_tuple {
522    ($t:ident) => {
523        from_tuple_u_u!($t, usize);
524        from_tuple_u_u!($t, u128);
525        from_tuple_u_u!($t, u64);
526        from_tuple_u_u!($t, u32);
527        from_tuple_u_u!($t, u16);
528        from_tuple_u_u!($t, u8);
529        from_tuple_u_i!($t, i128);
530        from_tuple_u_i!($t, i64);
531        from_tuple_u_i!($t, i32);
532        from_tuple_u_i!($t, i16);
533        from_tuple_u_i!($t, i8);
534    };
535}
536
537macro_rules! ttype_tuple_signed {
538    ($t:ident) => {
539        from_tuple_i_u!($t, usize);
540        from_tuple_i_u!($t, u128);
541        from_tuple_i_u!($t, u64);
542        from_tuple_i_u!($t, u32);
543        from_tuple_i_u!($t, u16);
544        from_tuple_i_u!($t, u8);
545        from_tuple_i_i!($t, i64);
546        from_tuple_i_i!($t, i32);
547        from_tuple_i_i!($t, i16);
548        from_tuple_i_i!($t, i8);
549    };
550}
551
552macro_rules! ttype {
553    ($t:ident) => {
554        from!($t);
555        ttype_tuple!($t);
556        add!($t);
557        add_assign!($t);
558        sub!($t);
559        sub_assign!($t);
560        mul!($t);
561        mul_assign!($t);
562        div!($t);
563        div_assign!($t);
564    };
565}
566
567macro_rules! ttype_signed {
568    ($t:ident) => {
569        from_signed!($t);
570        ttype_tuple_signed!($t);
571        add!($t);
572        add_assign!($t);
573        sub!($t);
574        sub_assign!($t);
575        mul!($t);
576        mul_assign!($t);
577        div!($t);
578        div_assign!($t);
579    };
580}
581
582ttype!(usize);
583ttype!(u128);
584ttype!(u64);
585ttype!(u32);
586ttype!(u16);
587ttype!(u8);
588ttype_signed!(i128);
589ttype_signed!(i64);
590ttype_signed!(i32);
591ttype_signed!(i16);
592ttype_signed!(i8);
593
594#[cfg(test)]
595mod tests {
596    use std::ops::Neg;
597
598    use crate::{fraction_f64::FractionF64, traits::{One, Signed, Zero}};
599
600    #[test]
601    fn fraction_neg() {
602        let one = FractionF64::one();
603        assert!(one.is_positive());
604        let one = one.neg();
605        assert!(one.is_negative());
606    }
607
608    #[test]
609    fn fraction_exact() {
610        let zero = FractionF64::one().one_minus();
611
612        assert!(zero.is_zero());
613    }
614}