1use anyhow::{Error, Result, anyhow};
2use fraction::{BigFraction, BigUint, GenericFraction, Sign};
3use num::{BigInt, One as NumOne};
4use num_bigint::{ToBigInt, ToBigUint};
5use num_rational::Ratio;
6use num_traits::ToPrimitive;
7use std::{
8 borrow::Borrow,
9 cmp::Ordering,
10 f64,
11 hash::Hash,
12 iter::Sum,
13 ops::{Add, AddAssign, Div, DivAssign, Mul, MulAssign, Neg, Sub, SubAssign},
14 str::FromStr,
15 sync::Arc,
16};
17
18use crate::{
19 exact::{is_exact_globally, MaybeExact},
20 fraction::{UInt, EPSILON},
21 traits::Infinite,
22};
23
24use super::traits::{One, Signed, Zero};
25
26#[derive(Clone)]
27pub enum FractionEnum {
28 Exact(fraction::BigFraction),
29 Approx(f64),
30 CannotCombineExactAndApprox,
31}
32
33impl FractionEnum {
34 pub(crate) fn matches(&self, rhs: &Self) -> bool {
38 match (self, rhs) {
39 (FractionEnum::Exact(_), FractionEnum::Exact(_)) => true,
40 (FractionEnum::Approx(_), FractionEnum::Approx(_)) => true,
41 _ => false,
42 }
43 }
44
45 pub fn sqrt_abs(&self, decimal_places: u32) -> FractionEnum {
46 match self {
47 FractionEnum::Exact(f) => FractionEnum::Exact(f.sqrt_abs(decimal_places)),
48 FractionEnum::Approx(f) => FractionEnum::Approx(f.abs().sqrt()),
49 FractionEnum::CannotCombineExactAndApprox => self.clone(),
50 }
51 }
52
53 pub fn is_sign_negative(&self) -> bool {
54 match self {
55 FractionEnum::Exact(f) => f.is_sign_negative(),
56 FractionEnum::Approx(f) => f.is_sign_negative(),
57 FractionEnum::CannotCombineExactAndApprox => true,
58 }
59 }
60
61 pub fn is_sign_positive(&self) -> bool {
62 match self {
63 FractionEnum::Exact(f) => f.is_sign_positive(),
64 FractionEnum::Approx(f) => f.is_sign_positive(),
65 FractionEnum::CannotCombineExactAndApprox => false,
66 }
67 }
68
69 pub fn is_infinite(&self) -> bool {
71 match self {
72 FractionEnum::Exact(f) => f.is_infinite(),
73 FractionEnum::Approx(f) => f.is_infinite(),
74 FractionEnum::CannotCombineExactAndApprox => false,
75 }
76 }
77
78 pub fn is_nan(&self) -> bool {
79 match self {
80 FractionEnum::Exact(f) => f.is_nan(),
81 FractionEnum::Approx(f) => f.is_nan(),
82 FractionEnum::CannotCombineExactAndApprox => true,
83 }
84 }
85
86 pub fn infinity() -> Self {
87 if is_exact_globally() {
88 FractionEnum::Exact(BigFraction::infinity())
89 } else {
90 FractionEnum::Approx(f64::INFINITY)
91 }
92 }
93
94 pub fn neg_infinity() -> Self {
95 if is_exact_globally() {
96 FractionEnum::Exact(BigFraction::neg_infinity())
97 } else {
98 FractionEnum::Approx(f64::NEG_INFINITY)
99 }
100 }
101
102 pub fn nan() -> Self {
103 if is_exact_globally() {
104 FractionEnum::Exact(BigFraction::nan())
105 } else {
106 FractionEnum::Approx(f64::NAN)
107 }
108 }
109
110 pub fn sign(&self) -> Option<Sign> {
111 match self {
112 FractionEnum::Exact(f) => f.sign(),
113 FractionEnum::Approx(f) => {
114 if f.is_nan() {
115 None
116 } else if <f64 as Zero>::is_zero(f) {
117 Some(Sign::Plus)
118 } else if f.is_sign_positive() {
119 Some(Sign::Plus)
120 } else {
121 Some(Sign::Minus)
122 }
123 }
124 FractionEnum::CannotCombineExactAndApprox => None,
125 }
126 }
127
128 pub fn recip(&self) -> Self {
132 match self {
133 FractionEnum::Exact(f) => FractionEnum::Exact(f.recip()),
134 FractionEnum::Approx(f) => FractionEnum::Approx(f.recip()),
135 FractionEnum::CannotCombineExactAndApprox => self.clone(),
136 }
137 }
138
139 pub fn one_minus(self) -> Self {
140 match self {
141 FractionEnum::Exact(mut f) => {
142 f = f.neg();
143 f.add_assign(1.to_biguint().unwrap());
144 FractionEnum::Exact(f)
145 }
146 FractionEnum::Approx(f) => FractionEnum::Approx(1.0 - f),
147 Self::CannotCombineExactAndApprox => self,
148 }
149 }
150
151 pub fn two() -> FractionEnum {
152 if is_exact_globally() {
153 FractionEnum::Exact(GenericFraction::Rational(
154 Sign::Plus,
155 Ratio::new_raw(UInt::from(2u32), UInt::from(1u32)),
156 ))
157 } else {
158 FractionEnum::Approx(2.0)
159 }
160 }
161}
162
163impl MaybeExact for FractionEnum {
164 type Approximate = f64;
165 type Exact = BigFraction;
166
167 fn is_exact(&self) -> bool {
168 match self {
169 FractionEnum::Exact(_) => true,
170 FractionEnum::Approx(_) => false,
171 FractionEnum::CannotCombineExactAndApprox => false,
172 }
173 }
174
175 fn extract_approx(&self) -> Result<f64> {
176 match self {
177 FractionEnum::Exact(_) => Err(anyhow!("cannot extract a float from a fraction")),
178 FractionEnum::Approx(f) => Ok(*f),
179 FractionEnum::CannotCombineExactAndApprox => {
180 Err(anyhow!("cannot combine exact and approximate arithmetic"))
181 }
182 }
183 }
184
185 fn extract_exact(&self) -> Result<&GenericFraction<BigUint>> {
186 match self {
187 FractionEnum::Exact(generic_fraction) => Ok(generic_fraction),
188 FractionEnum::Approx(_) => Err(anyhow!("cannot extract a fraction from a float")),
189 FractionEnum::CannotCombineExactAndApprox => {
190 Err(anyhow!("cannot combine exact and approximate arithmetic"))
191 }
192 }
193 }
194}
195
196impl One for FractionEnum {
197 fn one() -> Self {
198 if is_exact_globally() {
199 FractionEnum::Exact(GenericFraction::Rational(Sign::Plus, Ratio::one()))
200 } else {
201 FractionEnum::Approx(1.0)
202 }
203 }
204
205 fn is_one(&self) -> bool {
206 match self {
207 FractionEnum::Exact(f) => fraction::One::is_one(f),
208 FractionEnum::Approx(f) => (f - 1.0).abs() < EPSILON,
209 Self::CannotCombineExactAndApprox => false,
210 }
211 }
212
213 fn set_one(&mut self) {
214 match self {
215 FractionEnum::Exact(f) => num::One::set_one(f),
216 FractionEnum::Approx(f) => *f = 1.0,
217 FractionEnum::CannotCombineExactAndApprox => {}
218 }
219 }
220}
221
222impl Zero for FractionEnum {
223 fn zero() -> Self {
224 if is_exact_globally() {
225 FractionEnum::Exact(GenericFraction::Rational(Sign::Plus, num::Zero::zero()))
226 } else {
227 FractionEnum::Approx(<f64 as Zero>::zero())
228 }
229 }
230
231 fn is_zero(&self) -> bool {
232 match self {
233 FractionEnum::Exact(f) => fraction::Zero::is_zero(f),
234 FractionEnum::Approx(f) => f.abs() - &EPSILON < 0.0,
235 Self::CannotCombineExactAndApprox => false,
236 }
237 }
238
239 fn set_zero(&mut self) {
240 match self {
241 FractionEnum::Exact(f) => num::Zero::set_zero(f),
242 FractionEnum::Approx(f) => *f = 0.0,
243 FractionEnum::CannotCombineExactAndApprox => {}
244 }
245 }
246}
247
248impl Signed for FractionEnum {
249 fn abs(&self) -> Self {
250 match self {
251 FractionEnum::Exact(f) => FractionEnum::Exact(f.abs()),
252 FractionEnum::Approx(f) => FractionEnum::Approx(f.abs()),
253 FractionEnum::CannotCombineExactAndApprox => self.clone(),
254 }
255 }
256
257 fn is_positive(&self) -> bool {
258 match self {
259 FractionEnum::Exact(f) => !num::Zero::is_zero(f) && fraction::Signed::is_positive(f),
260 FractionEnum::Approx(f) => f.is_positive(),
261 FractionEnum::CannotCombineExactAndApprox => false,
262 }
263 }
264
265 fn is_negative(&self) -> bool {
266 match self {
267 FractionEnum::Exact(f) => fraction::Signed::is_negative(f),
268 FractionEnum::Approx(f) => f.is_negative(),
269 FractionEnum::CannotCombineExactAndApprox => false,
270 }
271 }
272
273 fn is_not_negative(&self) -> bool {
274 match self {
275 FractionEnum::Exact(_) => !self.is_negative(),
276 FractionEnum::Approx(f) => f.is_not_negative(),
277 FractionEnum::CannotCombineExactAndApprox => false,
278 }
279 }
280
281 fn is_not_positive(&self) -> bool {
282 match self {
283 FractionEnum::Exact(_) => !self.is_positive(),
284 FractionEnum::Approx(f) => f.is_not_positive(),
285 FractionEnum::CannotCombineExactAndApprox => false,
286 }
287 }
288}
289
290impl Infinite for FractionEnum {
291 fn is_infinite(&self) -> bool {
292 match self {
293 FractionEnum::Exact(f) => f.is_infinite(),
294 FractionEnum::Approx(f) => f.is_infinite(),
295 FractionEnum::CannotCombineExactAndApprox => false,
296 }
297 }
298}
299
300impl FromStr for FractionEnum {
301 type Err = Error;
302
303 fn from_str(s: &str) -> std::prelude::v1::Result<Self, Self::Err> {
304 if is_exact_globally() {
305 Ok(FractionEnum::Exact(BigFraction::from_str(s)?))
306 } else {
307 if let Ok(float) = f64::from_str(s) {
308 Ok(FractionEnum::Approx(float))
309 } else {
310 let fraction = BigFraction::from_str(s)?;
311 match fraction.to_f64() {
312 Some(f) => Ok(FractionEnum::Approx(f)),
313 None => Err(anyhow!("could not read fraction {} as float", s)),
314 }
315 }
316 }
317 }
318}
319
320
321
322
323impl From<&FractionEnum> for FractionEnum {
324 fn from(value: &FractionEnum) -> Self {
325 match value {
326 FractionEnum::Exact(_) => value.clone(),
327 FractionEnum::Approx(_) => value.clone(),
328 FractionEnum::CannotCombineExactAndApprox => value.clone(),
329 }
330 }
331}
332
333impl From<Arc<FractionEnum>> for FractionEnum {
334 fn from(value: Arc<FractionEnum>) -> Self {
335 match value.as_ref() {
336 FractionEnum::Exact(f) => FractionEnum::Exact(f.clone()),
337 FractionEnum::Approx(f) => FractionEnum::Approx(f.clone()),
338 FractionEnum::CannotCombineExactAndApprox => FractionEnum::CannotCombineExactAndApprox,
339 }
340 }
341}
342
343impl From<&Arc<FractionEnum>> for FractionEnum {
344 fn from(value: &Arc<FractionEnum>) -> Self {
345 match value.as_ref() {
346 FractionEnum::Exact(f) => FractionEnum::Exact(f.clone()),
347 FractionEnum::Approx(f) => FractionEnum::Approx(f.clone()),
348 FractionEnum::CannotCombineExactAndApprox => FractionEnum::CannotCombineExactAndApprox,
349 }
350 }
351}
352
353impl TryFrom<BigUint> for FractionEnum {
354 type Error = Error;
355
356 fn try_from(value: BigUint) -> std::prelude::v1::Result<Self, Self::Error> {
357 if is_exact_globally() {
358 Ok(FractionEnum::Exact(GenericFraction::Rational(
359 Sign::Plus,
360 Ratio::new(value, UInt::from(1u32)),
361 )))
362 } else {
363 if value < u64::MAX.to_biguint().unwrap() {
364 Ok(FractionEnum::Approx(value.to_f64().unwrap()))
365 } else {
366 Err(anyhow!("value too large for approximate arithmetic"))
367 }
368 }
369 }
370}
371
372impl TryFrom<&BigUint> for FractionEnum {
373 type Error = Error;
374
375 fn try_from(value: &BigUint) -> std::prelude::v1::Result<Self, Self::Error> {
376 if is_exact_globally() {
377 Ok(FractionEnum::Exact(GenericFraction::Rational(
378 Sign::Plus,
379 Ratio::new(value.clone(), UInt::from(1u32)),
380 )))
381 } else {
382 if value < &u64::MAX.to_biguint().unwrap() {
383 Ok(FractionEnum::Approx(value.to_f64().unwrap()))
384 } else {
385 Err(anyhow!("value too large for approximate arithmetic"))
386 }
387 }
388 }
389}
390
391impl TryFrom<BigInt> for FractionEnum {
392 type Error = Error;
393
394 fn try_from(value: BigInt) -> std::prelude::v1::Result<Self, Self::Error> {
395 if is_exact_globally() {
396 Ok(FractionEnum::Exact(GenericFraction::Rational(
397 if value.is_negative() {
398 Sign::Minus
399 } else {
400 Sign::Plus
401 },
402 Ratio::new(value.abs().to_biguint().unwrap(), UInt::from(1u32)),
403 )))
404 } else {
405 if value < u64::MAX.to_bigint().unwrap() {
406 Ok(FractionEnum::Approx(value.to_f64().unwrap()))
407 } else {
408 Err(anyhow!("value too large for approximate arithmetic"))
409 }
410 }
411 }
412}
413
414impl TryFrom<(BigUint, BigUint)> for FractionEnum {
415 type Error = Error;
416
417 fn try_from(value: (BigUint, BigUint)) -> std::prelude::v1::Result<Self, Self::Error> {
418 if is_exact_globally() {
419 Ok(FractionEnum::Exact(GenericFraction::Rational(
420 Sign::Plus,
421 Ratio::new(value.0, value.1),
422 )))
423 } else {
424 if let (Some(numer), Some(denom)) = (value.0.to_u64(), value.1.to_u64()) {
425 Ok(FractionEnum::Approx(numer as f64 / denom as f64))
426 } else {
427 Err(anyhow!("numbers too large for approximate arithmetic"))
428 }
429 }
430 }
431}
432
433impl std::fmt::Display for FractionEnum {
434 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
435 match self {
436 FractionEnum::Exact(fr) => std::fmt::Display::fmt(&fr, f),
437 FractionEnum::Approx(fr) => std::fmt::Display::fmt(&fr, f),
438 FractionEnum::CannotCombineExactAndApprox => {
439 write!(f, "cannot combine exact and approximate arithmatic")
440 }
441 }
442 }
443}
444
445impl std::fmt::Debug for FractionEnum {
446 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
447 match self {
448 Self::Exact(arg0) => f.debug_tuple("Exact ").field(arg0).finish(),
449 Self::Approx(arg0) => f.debug_tuple("Approx ").field(arg0).finish(),
450 Self::CannotCombineExactAndApprox => {
451 write!(f, "cannot combine exact and approximate arithmatic")
452 }
453 }
454 }
455}
456
457impl Add<&FractionEnum> for &FractionEnum {
458 type Output = FractionEnum;
459
460 fn add(self, rhs: &FractionEnum) -> Self::Output {
461 match (self, rhs) {
462 (FractionEnum::Exact(x), FractionEnum::Exact(y)) => FractionEnum::Exact(x.add(y)),
463 (FractionEnum::Approx(x), FractionEnum::Approx(y)) => FractionEnum::Approx(x.add(y)),
464 _ => FractionEnum::CannotCombineExactAndApprox,
465 }
466 }
467}
468
469impl Add<FractionEnum> for FractionEnum {
470 type Output = FractionEnum;
471
472 fn add(self, rhs: FractionEnum) -> Self::Output {
473 match (self, rhs) {
474 (FractionEnum::Exact(x), FractionEnum::Exact(y)) => FractionEnum::Exact(x.add(y)),
475 (FractionEnum::Approx(x), FractionEnum::Approx(y)) => FractionEnum::Approx(x.add(y)),
476 _ => FractionEnum::CannotCombineExactAndApprox,
477 }
478 }
479}
480
481impl<T> AddAssign<T> for FractionEnum
482where
483 T: Borrow<FractionEnum>,
484{
485 fn add_assign(&mut self, rhs: T) {
486 let rhs = rhs.borrow();
487
488 if self.matches(&rhs) {
489 match (self, rhs) {
490 (FractionEnum::Exact(x), FractionEnum::Exact(y)) => x.add_assign(y),
491 (FractionEnum::Approx(x), FractionEnum::Approx(y)) => x.add_assign(y),
492 _ => {}
493 };
494 } else {
495 *self = FractionEnum::CannotCombineExactAndApprox
496 }
497 }
498}
499
500impl AddAssign<&Arc<FractionEnum>> for FractionEnum {
501 fn add_assign(&mut self, rhs: &Arc<FractionEnum>) {
502 let rhs = rhs.borrow();
503
504 if self.matches(&rhs) {
505 match (self, rhs) {
506 (FractionEnum::Exact(x), FractionEnum::Exact(y)) => x.add_assign(y),
507 (FractionEnum::Approx(x), FractionEnum::Approx(y)) => x.add_assign(y),
508 _ => {}
509 };
510 } else {
511 *self = FractionEnum::CannotCombineExactAndApprox
512 }
513 }
514}
515
516impl Sub<&FractionEnum> for &FractionEnum {
517 type Output = FractionEnum;
518
519 fn sub(self, rhs: &FractionEnum) -> Self::Output {
520 match (self, rhs) {
521 (FractionEnum::Exact(x), FractionEnum::Exact(y)) => FractionEnum::Exact(x.sub(y)),
522 (FractionEnum::Approx(x), FractionEnum::Approx(y)) => FractionEnum::Approx(x.sub(y)),
523 _ => FractionEnum::CannotCombineExactAndApprox,
524 }
525 }
526}
527
528impl<T> SubAssign<T> for FractionEnum
529where
530 T: Borrow<FractionEnum>,
531{
532 fn sub_assign(&mut self, rhs: T) {
533 let rhs = rhs.borrow();
534 if self.matches(&rhs) {
535 match (self, rhs) {
536 (FractionEnum::Exact(x), FractionEnum::Exact(y)) => x.sub_assign(y),
537 (FractionEnum::Approx(x), FractionEnum::Approx(y)) => x.sub_assign(y),
538 _ => {}
539 }
540 } else {
541 *self = FractionEnum::CannotCombineExactAndApprox;
542 }
543 }
544}
545
546impl Mul<&FractionEnum> for &FractionEnum {
547 type Output = FractionEnum;
548
549 fn mul(self, rhs: &FractionEnum) -> Self::Output {
550 match (self, rhs) {
551 (FractionEnum::Exact(x), FractionEnum::Exact(y)) => FractionEnum::Exact(x.mul(y)),
552 (FractionEnum::Approx(x), FractionEnum::Approx(y)) => FractionEnum::Approx(x.mul(y)),
553 _ => FractionEnum::CannotCombineExactAndApprox,
554 }
555 }
556}
557
558impl<T> MulAssign<T> for FractionEnum
559where
560 T: Borrow<FractionEnum>,
561{
562 fn mul_assign(&mut self, rhs: T) {
563 let rhs = rhs.borrow();
564 if self.matches(&rhs) {
565 match (self, rhs) {
566 (FractionEnum::Exact(x), FractionEnum::Exact(y)) => x.mul_assign(y),
567 (FractionEnum::Approx(x), FractionEnum::Approx(y)) => x.mul_assign(y),
568 _ => {}
569 }
570 } else {
571 *self = FractionEnum::CannotCombineExactAndApprox
572 }
573 }
574}
575
576impl Div<&FractionEnum> for &FractionEnum {
577 type Output = FractionEnum;
578
579 fn div(self, rhs: &FractionEnum) -> Self::Output {
580 match (self, rhs) {
581 (FractionEnum::Exact(x), FractionEnum::Exact(y)) => FractionEnum::Exact(x.div(y)),
582 (FractionEnum::Approx(x), FractionEnum::Approx(y)) => FractionEnum::Approx(x.div(y)),
583 _ => FractionEnum::CannotCombineExactAndApprox,
584 }
585 }
586}
587
588impl<T> DivAssign<T> for FractionEnum
589where
590 T: Borrow<FractionEnum>,
591{
592 fn div_assign(&mut self, rhs: T) {
593 let rhs = rhs.borrow();
594 if self.matches(&rhs) {
595 match (self, rhs) {
596 (FractionEnum::Exact(x), FractionEnum::Exact(y)) => x.div_assign(y),
597 (FractionEnum::Approx(x), FractionEnum::Approx(y)) => x.div_assign(y),
598 _ => {}
599 }
600 } else {
601 *self = FractionEnum::CannotCombineExactAndApprox
602 }
603 }
604}
605
606impl Neg for FractionEnum {
607 type Output = FractionEnum;
608
609 fn neg(self) -> Self::Output {
610 match self {
611 FractionEnum::Exact(f) => FractionEnum::Exact(f.neg()),
612 FractionEnum::Approx(f) => FractionEnum::Approx(f.neg()),
613 Self::CannotCombineExactAndApprox => self.clone(),
614 }
615 }
616}
617
618impl<'a> Neg for &'a FractionEnum {
619 type Output = FractionEnum;
620
621 fn neg(self) -> Self::Output {
622 match self {
623 FractionEnum::Exact(f) => FractionEnum::Exact(f.neg()),
624 FractionEnum::Approx(f) => FractionEnum::Approx(f.neg()),
625 FractionEnum::CannotCombineExactAndApprox => self.clone(),
626 }
627 }
628}
629
630impl PartialEq for FractionEnum {
631 fn eq(&self, other: &Self) -> bool {
632 match (self, other) {
633 (Self::Exact(l0), Self::Exact(r0)) => l0 == r0,
634 (Self::Approx(l0), Self::Approx(r0)) => l0 - EPSILON <= *r0 && *r0 <= l0 + EPSILON,
635 _ => false,
636 }
637 }
638}
639
640impl Eq for FractionEnum {}
641
642impl PartialOrd for FractionEnum {
643 fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
647 if !self.matches(other) {
648 panic!("cannot compare exact and inexact arithmethic");
649 }
650 match (self, other) {
651 (FractionEnum::Exact(x), FractionEnum::Exact(y)) => x.partial_cmp(y),
652 (FractionEnum::Approx(x), FractionEnum::Approx(y)) => x.partial_cmp(y),
653 _ => None,
654 }
655 }
656}
657
658impl Ord for FractionEnum {
659 fn cmp(&self, other: &Self) -> Ordering {
663 if !self.matches(other) {
664 panic!("cannot compare exact and inexact arithmethic");
665 }
666 match (self, other) {
667 (FractionEnum::Exact(x), FractionEnum::Exact(y)) => x.cmp(y),
668 (FractionEnum::Approx(x), FractionEnum::Approx(y)) => {
669 if x.is_nan() && y.is_nan() {
670 Ordering::Equal
671 } else if x.is_nan() {
672 Ordering::Less
673 } else if y.is_nan() {
674 Ordering::Greater
675 } else if x == &f64::INFINITY {
676 if y == &f64::INFINITY {
677 Ordering::Equal
678 } else {
679 Ordering::Greater
680 }
681 } else if y == &f64::INFINITY {
682 Ordering::Less
683 } else if x == &f64::NEG_INFINITY {
684 if y == &f64::NEG_INFINITY {
685 Ordering::Equal
686 } else {
687 Ordering::Less
688 }
689 } else if y == &f64::NEG_INFINITY {
690 Ordering::Greater
691 } else {
692 x.partial_cmp(y).unwrap()
693 }
694 }
695 (FractionEnum::Exact(_), FractionEnum::Approx(_)) => Ordering::Greater,
696 (FractionEnum::Exact(_), FractionEnum::CannotCombineExactAndApprox) => {
697 Ordering::Greater
698 }
699 (FractionEnum::Approx(_), FractionEnum::Exact(_)) => Ordering::Less,
700 (FractionEnum::Approx(_), FractionEnum::CannotCombineExactAndApprox) => {
701 Ordering::Greater
702 }
703 (FractionEnum::CannotCombineExactAndApprox, FractionEnum::Exact(_)) => Ordering::Less,
704 (FractionEnum::CannotCombineExactAndApprox, FractionEnum::Approx(_)) => Ordering::Less,
705 (
706 FractionEnum::CannotCombineExactAndApprox,
707 FractionEnum::CannotCombineExactAndApprox,
708 ) => Ordering::Less,
709 }
710 }
711}
712
713impl Hash for FractionEnum {
714 fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
719 match self {
720 FractionEnum::Exact(f) => f.hash(state),
721 FractionEnum::Approx(f) => f64::to_bits(*f).hash(state),
722 Self::CannotCombineExactAndApprox => "cceaa".hash(state),
723 }
724 }
725}
726
727impl Sum for FractionEnum {
728 fn sum<I: Iterator<Item = Self>>(iter: I) -> Self {
729 iter.fold(Self::zero(), |sum, f| &sum + &f)
730 }
731}
732
733impl<'a> Sum<&'a FractionEnum> for FractionEnum {
734 fn sum<I: Iterator<Item = &'a FractionEnum>>(iter: I) -> Self {
735 iter.fold(FractionEnum::zero(), |sum, f| &sum + f)
736 }
737}
738
739macro_rules! from {
742 ($t:ident) => {
743 impl From<$t> for FractionEnum {
744 fn from(value: $t) -> Self {
745 if is_exact_globally() {
746 FractionEnum::Exact(GenericFraction::Rational(
747 Sign::Plus,
748 Ratio::new(value.to_biguint().unwrap(), UInt::from(1u32)),
749 ))
750 } else {
751 FractionEnum::Approx(value as f64)
752 }
753 }
754 }
755 };
756}
757
758macro_rules! from_signed {
759 ($t:ident) => {
760 impl From<$t> for FractionEnum {
761 fn from(value: $t) -> Self {
762 if is_exact_globally() {
763 FractionEnum::Exact(GenericFraction::Rational(
764 if value.is_negative() {
765 Sign::Minus
766 } else {
767 Sign::Plus
768 },
769 Ratio::new(value.abs().to_biguint().unwrap(), UInt::from(1u32)),
770 ))
771 } else {
772 FractionEnum::Approx(value as f64)
773 }
774 }
775 }
776 };
777}
778
779macro_rules! from_tuple_u_u {
780 ($t:ident,$tt:ident) => {
781 impl From<($t, $tt)> for FractionEnum {
782 fn from(value: ($t, $tt)) -> Self {
783 if is_exact_globally() {
784 FractionEnum::Exact(GenericFraction::Rational(
785 Sign::Plus,
786 Ratio::new(UInt::from(value.0), UInt::from(value.1)),
787 ))
788 } else {
789 FractionEnum::Approx(value.0 as f64 / value.1 as f64)
790 }
791 }
792 }
793 };
794}
795
796macro_rules! from_tuple_u_i {
797 ($t:ident,$tt:ident) => {
798 impl From<($t, $tt)> for FractionEnum {
799 fn from(value: ($t, $tt)) -> Self {
800 if is_exact_globally() {
801 let s1 = if value.1.is_negative() {
802 Sign::Minus
803 } else {
804 Sign::Plus
805 };
806 FractionEnum::Exact(GenericFraction::Rational(
807 s1,
808 Ratio::new(UInt::from(value.0), UInt::from(value.1.abs() as u128)),
809 ))
810 } else {
811 FractionEnum::Approx(value.0 as f64 / value.1 as f64)
812 }
813 }
814 }
815 };
816}
817
818macro_rules! from_tuple_i_u {
819 ($t:ident,$tt:ident) => {
820 impl From<($t, $tt)> for FractionEnum {
821 fn from(value: ($t, $tt)) -> Self {
822 if is_exact_globally() {
823 let s1 = if value.0.is_negative() {
824 Sign::Minus
825 } else {
826 Sign::Plus
827 };
828 FractionEnum::Exact(GenericFraction::Rational(
829 s1,
830 Ratio::new(UInt::from(value.0.abs() as u128), UInt::from(value.1)),
831 ))
832 } else {
833 FractionEnum::Approx(value.0 as f64 / value.1 as f64)
834 }
835 }
836 }
837 };
838}
839
840macro_rules! from_tuple_i_i {
841 ($t:ident,$tt:ident) => {
842 impl From<($t, $tt)> for FractionEnum {
843 fn from(value: ($t, $tt)) -> Self {
844 if is_exact_globally() {
845 let s0 = if value.0.is_negative() {
846 Sign::Minus
847 } else {
848 Sign::Plus
849 };
850 let s1 = if value.1.is_negative() {
851 Sign::Minus
852 } else {
853 Sign::Plus
854 };
855 FractionEnum::Exact(GenericFraction::Rational(
856 s0 * s1,
857 Ratio::new(
858 UInt::from(value.0.abs() as u128),
859 UInt::from(value.1.abs() as u128),
860 ),
861 ))
862 } else {
863 FractionEnum::Approx(value.0 as f64 / value.1 as f64)
864 }
865 }
866 }
867 };
868}
869
870macro_rules! add {
871 ($t:ident) => {
872 impl<'a> Add<$t> for &'a FractionEnum {
873 type Output = FractionEnum;
874
875 fn add(self, rhs: $t) -> Self::Output {
876 let rhs = rhs.into();
877 match (self, rhs) {
878 (FractionEnum::Exact(x), FractionEnum::Exact(y)) => {
879 FractionEnum::Exact(x.add(y))
880 }
881 (FractionEnum::Approx(x), FractionEnum::Approx(y)) => {
882 FractionEnum::Approx(x.add(y))
883 }
884 _ => FractionEnum::CannotCombineExactAndApprox,
885 }
886 }
887 }
888 };
889}
890
891macro_rules! add_assign {
892 ($t:ident) => {
893 impl AddAssign<$t> for FractionEnum {
894 fn add_assign(&mut self, rhs: $t) {
895 let rhs = rhs.into();
896 if self.matches(&rhs) {
897 match (self, rhs) {
898 (FractionEnum::Exact(x), FractionEnum::Exact(y)) => x.add_assign(y),
899 (FractionEnum::Approx(x), FractionEnum::Approx(y)) => x.add_assign(y),
900 _ => {}
901 };
902 } else {
903 *self = FractionEnum::CannotCombineExactAndApprox
904 }
905 }
906 }
907 };
908}
909
910macro_rules! sub {
911 ($t:ident) => {
912 impl<'a> Sub<$t> for &'a FractionEnum {
913 type Output = FractionEnum;
914
915 fn sub(self, rhs: $t) -> Self::Output {
916 let rhs = rhs.into();
917 match (self, rhs) {
918 (FractionEnum::Exact(x), FractionEnum::Exact(y)) => {
919 FractionEnum::Exact(x.sub(y))
920 }
921 (FractionEnum::Approx(x), FractionEnum::Approx(y)) => {
922 FractionEnum::Approx(x.sub(y))
923 }
924 _ => FractionEnum::CannotCombineExactAndApprox,
925 }
926 }
927 }
928 };
929}
930
931macro_rules! sub_assign {
932 ($t:ident) => {
933 impl SubAssign<$t> for FractionEnum {
934 fn sub_assign(&mut self, rhs: $t) {
935 let rhs = rhs.into();
936 if self.matches(&rhs) {
937 match (self, rhs) {
938 (FractionEnum::Exact(x), FractionEnum::Exact(y)) => x.sub_assign(y),
939 (FractionEnum::Approx(x), FractionEnum::Approx(y)) => x.sub_assign(y),
940 _ => {}
941 };
942 } else {
943 *self = FractionEnum::CannotCombineExactAndApprox
944 }
945 }
946 }
947 };
948}
949
950macro_rules! mul {
951 ($t:ident) => {
952 impl<'a> Mul<$t> for &'a FractionEnum {
953 type Output = FractionEnum;
954
955 fn mul(self, rhs: $t) -> Self::Output {
956 let rhs = rhs.into();
957 match (self, rhs) {
958 (FractionEnum::Exact(x), FractionEnum::Exact(y)) => {
959 FractionEnum::Exact(x.mul(y))
960 }
961 (FractionEnum::Approx(x), FractionEnum::Approx(y)) => {
962 FractionEnum::Approx(x.mul(y))
963 }
964 _ => FractionEnum::CannotCombineExactAndApprox,
965 }
966 }
967 }
968 };
969}
970
971macro_rules! mul_assign {
972 ($t:ident) => {
973 impl MulAssign<$t> for FractionEnum {
974 fn mul_assign(&mut self, rhs: $t) {
975 let rhs = rhs.into();
976 if self.matches(&rhs) {
977 match (self, rhs) {
978 (FractionEnum::Exact(x), FractionEnum::Exact(y)) => x.mul_assign(y),
979 (FractionEnum::Approx(x), FractionEnum::Approx(y)) => x.mul_assign(y),
980 _ => {}
981 };
982 } else {
983 *self = FractionEnum::CannotCombineExactAndApprox
984 }
985 }
986 }
987 };
988}
989
990macro_rules! div {
991 ($t:ident) => {
992 impl<'a> Div<$t> for &'a FractionEnum {
993 type Output = FractionEnum;
994
995 fn div(self, rhs: $t) -> Self::Output {
996 let rhs = rhs.into();
997 match (self, rhs) {
998 (FractionEnum::Exact(x), FractionEnum::Exact(y)) => {
999 FractionEnum::Exact(x.div(y))
1000 }
1001 (FractionEnum::Approx(x), FractionEnum::Approx(y)) => {
1002 FractionEnum::Approx(x.div(y))
1003 }
1004 _ => FractionEnum::CannotCombineExactAndApprox,
1005 }
1006 }
1007 }
1008 };
1009}
1010
1011macro_rules! div_assign {
1012 ($t:ident) => {
1013 impl DivAssign<$t> for FractionEnum {
1014 fn div_assign(&mut self, rhs: $t) {
1015 let rhs = rhs.into();
1016 if self.matches(&rhs) {
1017 match (self, rhs) {
1018 (FractionEnum::Exact(x), FractionEnum::Exact(y)) => x.div_assign(y),
1019 (FractionEnum::Approx(x), FractionEnum::Approx(y)) => x.div_assign(y),
1020 _ => {}
1021 };
1022 } else {
1023 *self = FractionEnum::CannotCombineExactAndApprox
1024 }
1025 }
1026 }
1027 };
1028}
1029
1030macro_rules! ttype_tuple {
1031 ($t:ident) => {
1032 from_tuple_u_u!($t, usize);
1033 from_tuple_u_u!($t, u128);
1034 from_tuple_u_u!($t, u64);
1035 from_tuple_u_u!($t, u32);
1036 from_tuple_u_u!($t, u16);
1037 from_tuple_u_u!($t, u8);
1038 from_tuple_u_i!($t, i128);
1039 from_tuple_u_i!($t, i64);
1040 from_tuple_u_i!($t, i32);
1041 from_tuple_u_i!($t, i16);
1042 from_tuple_u_i!($t, i8);
1043 };
1044}
1045
1046macro_rules! ttype_tuple_signed {
1047 ($t:ident) => {
1048 from_tuple_i_u!($t, usize);
1049 from_tuple_i_u!($t, u128);
1050 from_tuple_i_u!($t, u64);
1051 from_tuple_i_u!($t, u32);
1052 from_tuple_i_u!($t, u16);
1053 from_tuple_i_u!($t, u8);
1054 from_tuple_i_i!($t, i64);
1055 from_tuple_i_i!($t, i32);
1056 from_tuple_i_i!($t, i16);
1057 from_tuple_i_i!($t, i8);
1058 };
1059}
1060
1061macro_rules! ttype {
1062 ($t:ident) => {
1063 from!($t);
1064 ttype_tuple!($t);
1065 add!($t);
1066 add_assign!($t);
1067 sub!($t);
1068 sub_assign!($t);
1069 mul!($t);
1070 mul_assign!($t);
1071 div!($t);
1072 div_assign!($t);
1073 };
1074}
1075
1076macro_rules! ttype_signed {
1077 ($t:ident) => {
1078 from_signed!($t);
1079 ttype_tuple_signed!($t);
1080 add!($t);
1081 add_assign!($t);
1082 sub!($t);
1083 sub_assign!($t);
1084 mul!($t);
1085 mul_assign!($t);
1086 div!($t);
1087 div_assign!($t);
1088 };
1089}
1090
1091ttype!(usize);
1092ttype!(u128);
1093ttype!(u64);
1094ttype!(u32);
1095ttype!(u16);
1096ttype!(u8);
1097ttype_signed!(i128);
1098ttype_signed!(i64);
1099ttype_signed!(i32);
1100ttype_signed!(i16);
1101ttype_signed!(i8);
1102
1103#[cfg(test)]
1104mod tests {
1105 use std::ops::Neg;
1106
1107 use crate::{fraction_enum::FractionEnum, traits::{One, Signed, Zero}};
1108
1109 #[test]
1110 fn fraction_neg() {
1111 let one = FractionEnum::one();
1112 assert!(one.is_positive());
1113 let one = one.neg();
1114 assert!(one.is_negative());
1115 }
1116
1117 #[test]
1118 fn fraction_exact() {
1119 let zero = FractionEnum::one().one_minus();
1120
1121 assert!(zero.is_zero());
1122 }
1123}