1use anyhow::{Error, Result, anyhow};
2use fraction::{BigFraction, BigUint, GenericFraction, Sign};
3use num::{BigInt, One as NumOne, Zero as NumZero};
4use num_bigint::ToBigUint;
5use num_rational::Ratio;
6use std::{
7 borrow::Borrow,
8 cmp::Ordering,
9 f64,
10 hash::Hash,
11 iter::Sum,
12 ops::{Add, AddAssign, Div, DivAssign, Mul, MulAssign, Neg, Sub, SubAssign},
13 str::FromStr,
14 sync::Arc,
15};
16
17use crate::{
18 exact::MaybeExact,
19 fraction::UInt,
20 traits::{One, Signed, Zero},
21};
22
23#[derive(Clone)]
24pub struct FractionExact(pub fraction::BigFraction);
25
26impl FractionExact {
27 pub fn sqrt_abs(&self, decimal_places: u32) -> FractionExact {
28 Self(self.0.sqrt_abs(decimal_places))
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(BigFraction::infinity())
50 }
51
52 pub fn neg_infinity() -> Self {
53 Self(BigFraction::neg_infinity())
54 }
55
56 pub fn nan() -> Self {
57 Self(BigFraction::nan())
58 }
59
60 pub fn sign(&self) -> Option<Sign> {
61 self.0.sign()
62 }
63
64 pub fn recip(&self) -> Self {
68 Self(self.0.recip())
69 }
70
71 pub fn one_minus(mut self) -> Self {
72 self.0 = self.0.neg();
73 self.0.add_assign(1.to_biguint().unwrap());
74 self
75 }
76
77 pub fn two() -> FractionExact {
78 Self(GenericFraction::Rational(
79 Sign::Plus,
80 Ratio::new_raw(UInt::from(2u32), UInt::from(1u32)),
81 ))
82 }
83}
84
85impl MaybeExact for FractionExact {
86 type Approximate = f64;
87 type Exact = fraction::BigFraction;
88
89 fn is_exact(&self) -> bool {
90 true
91 }
92
93 fn extract_approx(&self) -> Result<f64> {
94 Err(anyhow!("cannot extract a float from a fraction"))
95 }
96
97 fn extract_exact(&self) -> Result<&GenericFraction<BigUint>> {
102 Ok(&self.0)
103 }
104}
105
106impl One for FractionExact {
107 fn one() -> Self {
108 Self(GenericFraction::Rational(Sign::Plus, NumOne::one()))
109 }
110
111 fn is_one(&self) -> bool {
112 fraction::One::is_one(&self.0)
113 }
114}
115
116impl Zero for FractionExact {
117 fn zero() -> Self {
118 Self(GenericFraction::Rational(Sign::Plus, NumZero::zero()))
119 }
120
121 fn is_zero(&self) -> bool {
122 fraction::Zero::is_zero(&self.0)
123 }
124}
125
126impl Signed for FractionExact {
127 fn abs(&self) -> Self {
128 Self(self.0.abs())
129 }
130
131 fn is_positive(&self) -> bool {
132 !Zero::is_zero(&self.0) && fraction::Signed::is_positive(&self.0)
133 }
134
135 fn is_negative(&self) -> bool {
136 fraction::Signed::is_negative(&self.0)
137 }
138
139 fn is_not_negative(&self) -> bool {
140 !self.is_negative()
141 }
142
143 fn is_not_positive(&self) -> bool {
144 !self.is_positive()
145 }
146}
147
148impl FromStr for FractionExact {
149 type Err = Error;
150
151 fn from_str(s: &str) -> std::prelude::v1::Result<Self, Self::Err> {
152 Ok(Self(BigFraction::from_str(s)?))
153 }
154}
155
156impl From<&FractionExact> for FractionExact {
157 fn from(value: &FractionExact) -> Self {
158 value.clone()
159 }
160}
161
162impl From<Arc<FractionExact>> for FractionExact {
163 fn from(value: Arc<FractionExact>) -> Self {
164 Self(value.0.clone())
165 }
166}
167
168impl From<&Arc<FractionExact>> for FractionExact {
169 fn from(value: &Arc<FractionExact>) -> Self {
170 match value.as_ref() {
171 FractionExact(f) => FractionExact(f.clone()),
172 }
173 }
174}
175
176impl TryFrom<BigUint> for FractionExact {
177 type Error = Error;
178
179 fn try_from(value: BigUint) -> std::prelude::v1::Result<Self, Self::Error> {
180 Ok(Self(GenericFraction::Rational(
181 Sign::Plus,
182 Ratio::new(value, UInt::from(1u32)),
183 )))
184 }
185}
186
187impl TryFrom<&BigUint> for FractionExact {
188 type Error = Error;
189
190 fn try_from(value: &BigUint) -> std::prelude::v1::Result<Self, Self::Error> {
191 Ok(Self(GenericFraction::Rational(
192 Sign::Plus,
193 Ratio::new(value.clone(), UInt::from(1u32)),
194 )))
195 }
196}
197
198impl TryFrom<BigInt> for FractionExact {
199 type Error = Error;
200
201 fn try_from(value: BigInt) -> std::prelude::v1::Result<Self, Self::Error> {
202 Ok(Self(GenericFraction::Rational(
203 if value.is_negative() {
204 Sign::Minus
205 } else {
206 Sign::Plus
207 },
208 Ratio::new(value.abs().to_biguint().unwrap(), UInt::from(1u32)),
209 )))
210 }
211}
212
213impl TryFrom<(BigUint, BigUint)> for FractionExact {
214 type Error = Error;
215
216 fn try_from(value: (BigUint, BigUint)) -> std::prelude::v1::Result<Self, Self::Error> {
217 Ok(Self(GenericFraction::Rational(
218 Sign::Plus,
219 Ratio::new(value.0, value.1),
220 )))
221 }
222}
223
224impl std::fmt::Display for FractionExact {
225 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
226 std::fmt::Display::fmt(&self.0, f)
227 }
228}
229
230impl std::fmt::Debug for FractionExact {
231 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
232 f.debug_tuple("Exact ").field(&self.0).finish()
233 }
234}
235
236impl Add<&FractionExact> for &FractionExact {
237 type Output = FractionExact;
238
239 fn add(self, rhs: &FractionExact) -> Self::Output {
240 match (self, rhs) {
241 (FractionExact(x), FractionExact(y)) => FractionExact(x.add(y)),
242 }
243 }
244}
245
246impl<T> AddAssign<T> for FractionExact
247where
248 T: Borrow<FractionExact>,
249{
250 fn add_assign(&mut self, rhs: T) {
251 let rhs = rhs.borrow();
252 match (self, rhs) {
253 (FractionExact(x), FractionExact(y)) => x.add_assign(y),
254 }
255 }
256}
257
258impl AddAssign<&Arc<FractionExact>> for FractionExact {
259 fn add_assign(&mut self, rhs: &Arc<FractionExact>) {
260 let rhs = rhs.borrow();
261 match (self, rhs) {
262 (FractionExact(x), FractionExact(y)) => x.add_assign(y),
263 }
264 }
265}
266
267impl Sub<&FractionExact> for &FractionExact {
268 type Output = FractionExact;
269
270 fn sub(self, rhs: &FractionExact) -> Self::Output {
271 match (self, rhs) {
272 (FractionExact(x), FractionExact(y)) => FractionExact(x.sub(y)),
273 }
274 }
275}
276
277impl<T> SubAssign<T> for FractionExact
278where
279 T: Borrow<FractionExact>,
280{
281 fn sub_assign(&mut self, rhs: T) {
282 let rhs = rhs.borrow();
283 match (self, rhs) {
284 (FractionExact(x), FractionExact(y)) => x.sub_assign(y),
285 }
286 }
287}
288
289impl Mul<&FractionExact> for &FractionExact {
290 type Output = FractionExact;
291
292 fn mul(self, rhs: &FractionExact) -> Self::Output {
293 match (self, rhs) {
294 (FractionExact(x), FractionExact(y)) => FractionExact(x.mul(y)),
295 }
296 }
297}
298
299impl<T> MulAssign<T> for FractionExact
300where
301 T: Borrow<FractionExact>,
302{
303 fn mul_assign(&mut self, rhs: T) {
304 let rhs = rhs.borrow();
305 match (self, rhs) {
306 (FractionExact(x), FractionExact(y)) => x.mul_assign(y),
307 }
308 }
309}
310
311impl Div<&FractionExact> for &FractionExact {
312 type Output = FractionExact;
313
314 fn div(self, rhs: &FractionExact) -> Self::Output {
315 match (self, rhs) {
316 (FractionExact(x), FractionExact(y)) => FractionExact(x.div(y)),
317 }
318 }
319}
320
321impl<T> DivAssign<T> for FractionExact
322where
323 T: Borrow<FractionExact>,
324{
325 fn div_assign(&mut self, rhs: T) {
326 let rhs = rhs.borrow();
327 match (self, rhs) {
328 (FractionExact(x), FractionExact(y)) => x.div_assign(y),
329 }
330 }
331}
332
333impl Neg for FractionExact {
334 type Output = FractionExact;
335
336 fn neg(self) -> Self::Output {
337 FractionExact(self.0.neg())
338 }
339}
340
341impl<'a> Neg for &'a FractionExact {
342 type Output = FractionExact;
343
344 fn neg(self) -> Self::Output {
345 match self {
346 FractionExact(f) => FractionExact(f.neg()),
347 }
348 }
349}
350
351impl PartialEq for FractionExact {
352 fn eq(&self, other: &Self) -> bool {
353 match (self, other) {
354 (FractionExact(x), FractionExact(y)) => x == y,
355 }
356 }
357}
358
359impl Eq for FractionExact {}
360
361impl PartialOrd for FractionExact {
362 fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
366 match (self, other) {
367 (FractionExact(x), FractionExact(y)) => x.partial_cmp(y),
368 }
369 }
370}
371
372impl Ord for FractionExact {
373 fn cmp(&self, other: &Self) -> Ordering {
374 self.0.cmp(&other.0)
375 }
376}
377
378impl Hash for FractionExact {
379 fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
384 match self {
385 FractionExact(f) => f.hash(state),
386 }
387 }
388}
389
390impl Sum for FractionExact {
391 fn sum<I: Iterator<Item = Self>>(iter: I) -> Self {
392 iter.fold(Self::zero(), |sum, f| &sum + &f)
393 }
394}
395
396impl<'a> Sum<&'a FractionExact> for FractionExact {
397 fn sum<I: Iterator<Item = &'a FractionExact>>(iter: I) -> Self {
398 iter.fold(FractionExact::zero(), |sum, f| &sum + f)
399 }
400}
401
402macro_rules! from {
405 ($t:ident) => {
406 impl From<$t> for FractionExact {
407 fn from(value: $t) -> Self {
408 Self(GenericFraction::Rational(
409 Sign::Plus,
410 Ratio::new(value.to_biguint().unwrap(), UInt::from(1u32)),
411 ))
412 }
413 }
414 };
415}
416
417macro_rules! from_signed {
418 ($t:ident) => {
419 impl From<$t> for FractionExact {
420 fn from(value: $t) -> Self {
421 Self(GenericFraction::Rational(
422 if value.is_negative() {
423 Sign::Minus
424 } else {
425 Sign::Plus
426 },
427 Ratio::new(value.abs().to_biguint().unwrap(), UInt::from(1u32)),
428 ))
429 }
430 }
431 };
432}
433
434macro_rules! from_tuple_u_u {
435 ($t:ident,$tt:ident) => {
436 impl From<($t, $tt)> for FractionExact {
437 fn from(value: ($t, $tt)) -> Self {
438 FractionExact(GenericFraction::Rational(
439 Sign::Plus,
440 Ratio::new(UInt::from(value.0), UInt::from(value.1)),
441 ))
442 }
443 }
444 };
445}
446
447macro_rules! from_tuple_u_i {
448 ($t:ident,$tt:ident) => {
449 impl From<($t, $tt)> for FractionExact {
450 fn from(value: ($t, $tt)) -> Self {
451 let s1 = if value.1.is_negative() {
452 Sign::Minus
453 } else {
454 Sign::Plus
455 };
456 FractionExact(GenericFraction::Rational(
457 s1,
458 Ratio::new(UInt::from(value.0), UInt::from(value.1.abs() as u128)),
459 ))
460 }
461 }
462 };
463}
464
465macro_rules! from_tuple_i_u {
466 ($t:ident,$tt:ident) => {
467 impl From<($t, $tt)> for FractionExact {
468 fn from(value: ($t, $tt)) -> Self {
469 let s1 = if value.0.is_negative() {
470 Sign::Minus
471 } else {
472 Sign::Plus
473 };
474 Self(GenericFraction::Rational(
475 s1,
476 Ratio::new(UInt::from(value.0.abs() as u128), UInt::from(value.1)),
477 ))
478 }
479 }
480 };
481}
482
483macro_rules! from_tuple_i_i {
484 ($t:ident,$tt:ident) => {
485 impl From<($t, $tt)> for FractionExact {
486 fn from(value: ($t, $tt)) -> Self {
487 let s0 = if value.0.is_negative() {
488 Sign::Minus
489 } else {
490 Sign::Plus
491 };
492 let s1 = if value.1.is_negative() {
493 Sign::Minus
494 } else {
495 Sign::Plus
496 };
497 Self(GenericFraction::Rational(
498 s0 * s1,
499 Ratio::new(
500 UInt::from(value.0.abs() as u128),
501 UInt::from(value.1.abs() as u128),
502 ),
503 ))
504 }
505 }
506 };
507}
508
509macro_rules! add {
510 ($t:ident) => {
511 impl<'a> Add<$t> for &'a FractionExact {
512 type Output = FractionExact;
513
514 fn add(self, rhs: $t) -> Self::Output {
515 let rhs = rhs.into();
516 match (self, rhs) {
517 (FractionExact(x), FractionExact(y)) => FractionExact(x.add(y)),
518 }
519 }
520 }
521 };
522}
523
524macro_rules! add_assign {
525 ($t:ident) => {
526 impl AddAssign<$t> for FractionExact {
527 fn add_assign(&mut self, rhs: $t) {
528 let rhs = rhs.into();
529 match (self, rhs) {
530 (FractionExact(x), FractionExact(y)) => x.add_assign(y),
531 }
532 }
533 }
534 };
535}
536
537macro_rules! sub {
538 ($t:ident) => {
539 impl<'a> Sub<$t> for &'a FractionExact {
540 type Output = FractionExact;
541
542 fn sub(self, rhs: $t) -> Self::Output {
543 let rhs = rhs.into();
544 match (self, rhs) {
545 (FractionExact(x), FractionExact(y)) => FractionExact(x.sub(y)),
546 }
547 }
548 }
549 };
550}
551
552macro_rules! sub_assign {
553 ($t:ident) => {
554 impl SubAssign<$t> for FractionExact {
555 fn sub_assign(&mut self, rhs: $t) {
556 let rhs = rhs.into();
557 match (self, rhs) {
558 (FractionExact(x), FractionExact(y)) => x.sub_assign(y),
559 }
560 }
561 }
562 };
563}
564
565macro_rules! mul {
566 ($t:ident) => {
567 impl<'a> Mul<$t> for &'a FractionExact {
568 type Output = FractionExact;
569
570 fn mul(self, rhs: $t) -> Self::Output {
571 let rhs = rhs.into();
572 match (self, rhs) {
573 (FractionExact(x), FractionExact(y)) => FractionExact(x.mul(y)),
574 }
575 }
576 }
577 };
578}
579
580macro_rules! mul_assign {
581 ($t:ident) => {
582 impl MulAssign<$t> for FractionExact {
583 fn mul_assign(&mut self, rhs: $t) {
584 let rhs = rhs.into();
585 match (self, rhs) {
586 (FractionExact(x), FractionExact(y)) => x.mul_assign(y),
587 }
588 }
589 }
590 };
591}
592
593macro_rules! div {
594 ($t:ident) => {
595 impl<'a> Div<$t> for &'a FractionExact {
596 type Output = FractionExact;
597
598 fn div(self, rhs: $t) -> Self::Output {
599 let rhs = rhs.into();
600 match (self, rhs) {
601 (FractionExact(x), FractionExact(y)) => FractionExact(x.div(y)),
602 }
603 }
604 }
605 };
606}
607
608macro_rules! div_assign {
609 ($t:ident) => {
610 impl DivAssign<$t> for FractionExact {
611 fn div_assign(&mut self, rhs: $t) {
612 let rhs = rhs.into();
613 match (self, rhs) {
614 (FractionExact(x), FractionExact(y)) => x.div_assign(y),
615 }
616 }
617 }
618 };
619}
620
621macro_rules! ttype_tuple {
622 ($t:ident) => {
623 from_tuple_u_u!($t, usize);
624 from_tuple_u_u!($t, u128);
625 from_tuple_u_u!($t, u64);
626 from_tuple_u_u!($t, u32);
627 from_tuple_u_u!($t, u16);
628 from_tuple_u_u!($t, u8);
629 from_tuple_u_i!($t, i128);
630 from_tuple_u_i!($t, i64);
631 from_tuple_u_i!($t, i32);
632 from_tuple_u_i!($t, i16);
633 from_tuple_u_i!($t, i8);
634 };
635}
636
637macro_rules! ttype_tuple_signed {
638 ($t:ident) => {
639 from_tuple_i_u!($t, usize);
640 from_tuple_i_u!($t, u128);
641 from_tuple_i_u!($t, u64);
642 from_tuple_i_u!($t, u32);
643 from_tuple_i_u!($t, u16);
644 from_tuple_i_u!($t, u8);
645 from_tuple_i_i!($t, i64);
646 from_tuple_i_i!($t, i32);
647 from_tuple_i_i!($t, i16);
648 from_tuple_i_i!($t, i8);
649 };
650}
651
652macro_rules! ttype {
653 ($t:ident) => {
654 from!($t);
655 ttype_tuple!($t);
656 add!($t);
657 add_assign!($t);
658 sub!($t);
659 sub_assign!($t);
660 mul!($t);
661 mul_assign!($t);
662 div!($t);
663 div_assign!($t);
664 };
665}
666
667macro_rules! ttype_signed {
668 ($t:ident) => {
669 from_signed!($t);
670 ttype_tuple_signed!($t);
671 add!($t);
672 add_assign!($t);
673 sub!($t);
674 sub_assign!($t);
675 mul!($t);
676 mul_assign!($t);
677 div!($t);
678 div_assign!($t);
679 };
680}
681
682ttype!(usize);
683ttype!(u128);
684ttype!(u64);
685ttype!(u32);
686ttype!(u16);
687ttype!(u8);
688ttype_signed!(i128);
689ttype_signed!(i64);
690ttype_signed!(i32);
691ttype_signed!(i16);
692ttype_signed!(i8);
693
694#[cfg(test)]
695mod tests {
696 use std::ops::Neg;
697
698 use crate::{fraction_exact::FractionExact, traits::{One, Signed, Zero}};
699
700 #[test]
701 fn fraction_neg() {
702 let one = FractionExact::one();
703 assert!(one.is_positive());
704 let one = one.neg();
705 assert!(one.is_negative());
706 }
707
708 #[test]
709 fn fraction_exact() {
710 let zero = FractionExact::one().one_minus();
711
712 assert!(zero.is_zero());
713 }
714}