Skip to main content

oxinum_rational/
lib.rs

1#![forbid(unsafe_code)]
2//! Exact rational arithmetic for the OxiNum ecosystem.
3//!
4//! Provides `RBig` and `Relaxed` re-exports from `dashu-ratio`, plus
5//! additional functions: continued fraction expansion, best rational
6//! approximation, decimal string conversion, mediant, mixed number
7//! representation, and floor/ceil/round/truncate operations.
8
9pub use dashu_ratio::{RBig, Relaxed};
10pub use oxinum_core::{OxiNumError, OxiNumResult};
11
12// Re-export IBig/UBig for convenience (needed to construct RBig)
13pub use dashu_int::{IBig, UBig};
14
15/// Type alias for clarity.
16pub type BigRational = RBig;
17
18mod convert;
19mod enumerate;
20mod ops;
21
22/// Native arbitrary-precision rational implementation built on
23/// [`oxinum_int::native::BigInt`] / [`oxinum_int::native::BigUint`].
24///
25/// Access via `oxinum_rational::native::BigRational` — intentionally NOT
26/// re-exported at the crate root to avoid clashing with the `BigRational`
27/// type alias for `dashu_ratio::RBig`.
28pub mod native;
29
30pub use convert::{from_f32, from_f64, parse_mixed, to_f64, to_f64_exact, MixedNumber};
31pub use enumerate::{farey_sequence, from_stern_brocot_path, stern_brocot_path};
32pub use ops::{
33    best_rational_approximation, continued_fraction, from_continued_fraction, mediant,
34    mixed_number, rational_abs, rational_ceil, rational_floor, rational_from_integer,
35    rational_is_integer, rational_pow, rational_reciprocal, rational_round, rational_signum,
36    rational_to_integer, rational_truncate, to_decimal_string,
37};
38
39// ---------------------------------------------------------------------------
40// Tests
41// ---------------------------------------------------------------------------
42
43#[cfg(test)]
44mod tests {
45    use super::*;
46
47    #[test]
48    fn rbig_from_u32() {
49        let n = RBig::from(42u32);
50        assert_eq!(n.to_string(), "42");
51    }
52
53    #[test]
54    fn relaxed_from_u32() {
55        let n: Relaxed = Relaxed::from(42u32);
56        assert_eq!(n.to_string(), "42");
57    }
58
59    #[test]
60    fn rbig_from_parts_pi_approx() {
61        let r = RBig::from_parts(IBig::from(355), UBig::from(113u32));
62        assert_eq!(r.numerator(), &IBig::from(355));
63        assert_eq!(r.denominator(), &UBig::from(113u32));
64        assert_eq!(r.to_string(), "355/113");
65    }
66
67    #[test]
68    fn rbig_add_fractions() {
69        // 1/2 + 1/3 = 5/6
70        let half = RBig::from_parts(IBig::from(1), UBig::from(2u32));
71        let third = RBig::from_parts(IBig::from(1), UBig::from(3u32));
72        let sum = half + third;
73        assert_eq!(sum.numerator(), &IBig::from(5));
74        assert_eq!(sum.denominator(), &UBig::from(6u32));
75    }
76
77    #[test]
78    fn rbig_sub_fractions() {
79        // 3/4 - 1/4 = 1/2
80        let three_quarters = RBig::from_parts(IBig::from(3), UBig::from(4u32));
81        let one_quarter = RBig::from_parts(IBig::from(1), UBig::from(4u32));
82        let diff = three_quarters - one_quarter;
83        assert_eq!(diff.numerator(), &IBig::from(1));
84        assert_eq!(diff.denominator(), &UBig::from(2u32));
85    }
86
87    #[test]
88    fn rbig_mul() {
89        // 2/3 * 3/4 = 1/2
90        let a = RBig::from_parts(IBig::from(2), UBig::from(3u32));
91        let b = RBig::from_parts(IBig::from(3), UBig::from(4u32));
92        let product = a * b;
93        assert_eq!(product.numerator(), &IBig::from(1));
94        assert_eq!(product.denominator(), &UBig::from(2u32));
95    }
96
97    #[test]
98    fn rbig_div() {
99        // (1/2) / (1/3) = 3/2
100        let half = RBig::from_parts(IBig::from(1), UBig::from(2u32));
101        let third = RBig::from_parts(IBig::from(1), UBig::from(3u32));
102        let quotient = half / third;
103        assert_eq!(quotient.numerator(), &IBig::from(3));
104        assert_eq!(quotient.denominator(), &UBig::from(2u32));
105    }
106
107    #[test]
108    fn relaxed_canonicalize() {
109        let r = Relaxed::from_parts(IBig::from(-15), UBig::from(6u32));
110        assert_eq!(r.numerator(), &IBig::from(-15));
111        assert_eq!(r.denominator(), &UBig::from(6u32));
112        let canonical = r.canonicalize();
113        assert_eq!(canonical.numerator(), &IBig::from(-5));
114        assert_eq!(canonical.denominator(), &UBig::from(2u32));
115    }
116
117    #[test]
118    fn rbig_automatic_simplification() {
119        // 6/4 should auto-reduce to 3/2
120        let r = RBig::from_parts(IBig::from(6), UBig::from(4u32));
121        assert_eq!(r.numerator(), &IBig::from(3));
122        assert_eq!(r.denominator(), &UBig::from(2u32));
123    }
124
125    #[test]
126    fn rbig_is_integer() {
127        let whole = RBig::from_parts(IBig::from(10), UBig::from(5u32));
128        assert_eq!(*whole.denominator(), UBig::ONE);
129
130        let frac = RBig::from_parts(IBig::from(1), UBig::from(3u32));
131        assert_ne!(*frac.denominator(), UBig::ONE);
132    }
133
134    #[test]
135    fn rbig_from_integer() {
136        let r = RBig::from(42u32);
137        assert_eq!(r.numerator(), &IBig::from(42));
138        assert_eq!(r.denominator(), &UBig::ONE);
139    }
140
141    #[test]
142    fn rbig_negation() {
143        let r = RBig::from_parts(IBig::from(3), UBig::from(4u32));
144        let neg = -r;
145        assert_eq!(neg.numerator(), &IBig::from(-3));
146        assert_eq!(neg.denominator(), &UBig::from(4u32));
147    }
148}