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 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 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 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
331impl 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}