1use super::integer::{Integer, big_gcd, big_sign, gcd_u64};
8use num_bigint::BigInt;
9use std::cmp::Ordering;
10use std::fmt;
11use std::hash::{Hash, Hasher};
12
13#[derive(Clone, Debug, PartialEq, Eq)]
14enum Repr {
15 Small { n: i64, d: u64 },
16 Big(Box<(BigInt, BigInt)>),
17}
18
19#[derive(Clone, Debug, PartialEq, Eq)]
21pub struct Rational(Repr);
22
23impl Rational {
24 pub fn zero() -> Self {
25 Rational(Repr::Small { n: 0, d: 1 })
26 }
27
28 pub fn one() -> Self {
29 Rational(Repr::Small { n: 1, d: 1 })
30 }
31
32 pub fn from_integer(v: &Integer) -> Self {
33 match v.to_i64() {
34 Some(n) => Rational(Repr::Small { n, d: 1 }),
35 None => Rational(Repr::Big(Box::new((v.to_bigint(), BigInt::from(1))))),
36 }
37 }
38
39 pub fn from_ints(n: &Integer, d: &Integer) -> Option<Self> {
41 if d.is_zero() {
42 return None;
43 }
44 let (n, d) = if d.is_negative() {
46 (n.neg(), d.abs())
47 } else {
48 (n.clone(), d.clone())
49 };
50 if n.is_zero() {
51 return Some(Rational::zero());
52 }
53 let normalized = match (n.to_i64(), d.to_i64()) {
54 (Some(sn), Some(sd)) => {
55 let sd = sd as u64;
56 let g = gcd_u64(sn.unsigned_abs(), sd);
57 let nn = (sn as i128 / g as i128) as i64;
58 Rational(Repr::Small { n: nn, d: sd / g })
59 }
60 _ => Self::from_big_parts(n.to_bigint(), d.to_bigint()),
61 };
62 Some(normalized)
63 }
64
65 pub fn is_zero(&self) -> bool {
66 matches!(self.0, Repr::Small { n: 0, .. })
67 }
68
69 pub fn is_one(&self) -> bool {
70 matches!(self.0, Repr::Small { n: 1, d: 1 })
71 }
72
73 pub fn is_negative(&self) -> bool {
74 self.sign() < 0
75 }
76
77 pub fn sign(&self) -> i32 {
78 match &self.0 {
79 Repr::Small { n, .. } => n.signum() as i32,
80 Repr::Big(p) => match big_sign(&p.0) {
81 Ordering::Less => -1,
82 Ordering::Equal => 0,
83 Ordering::Greater => 1,
84 },
85 }
86 }
87
88 pub fn num(&self) -> Integer {
89 match &self.0 {
90 Repr::Small { n, .. } => Integer::from_i64(*n),
91 Repr::Big(p) => Integer::from_bigint(p.0.clone()),
92 }
93 }
94
95 pub fn den(&self) -> Integer {
96 match &self.0 {
97 Repr::Small { d, .. } => Integer::from_u64(*d),
99 Repr::Big(p) => Integer::from_bigint(p.1.clone()),
100 }
101 }
102
103 pub fn neg(&self) -> Self {
104 match &self.0 {
105 Repr::Small { n, d } => Rational(Repr::Small { n: -n, d: *d }),
106 Repr::Big(p) => Self::from_big_parts(-p.0.clone(), p.1.clone()),
107 }
108 }
109
110 pub fn abs(&self) -> Self {
111 if self.is_negative() {
112 self.neg()
113 } else {
114 self.clone()
115 }
116 }
117
118 pub fn inv(&self) -> Option<Self> {
120 Self::from_ints(&self.den(), &self.num())
121 }
122
123 pub fn add(&self, other: &Self) -> Self {
124 match (&self.0, &other.0) {
125 (Repr::Small { n: n1, d: d1 }, Repr::Small { n: n2, d: d2 }) => {
126 let n = (*n1 as i128) * (*d2 as i128) + (*n2 as i128) * (*d1 as i128);
127 let d = (*d1 as u128) * (*d2 as u128);
128 Self::from_parts_wide(n, d)
129 }
130 _ => Self::from_big_parts(
131 self.n_big() * other.d_big() + other.n_big() * self.d_big(),
132 self.d_big() * other.d_big(),
133 ),
134 }
135 }
136
137 pub fn sub(&self, other: &Self) -> Self {
138 self.add(&other.neg())
139 }
140
141 pub fn mul(&self, other: &Self) -> Self {
142 match (&self.0, &other.0) {
143 (Repr::Small { n: n1, d: d1 }, Repr::Small { n: n2, d: d2 }) => {
144 let n = (*n1 as i128) * (*n2 as i128);
145 let d = (*d1 as u128) * (*d2 as u128);
146 Self::from_parts_wide(n, d)
147 }
148 _ => Self::from_big_parts(self.n_big() * other.n_big(), self.d_big() * other.d_big()),
149 }
150 }
151
152 pub fn div(&self, other: &Self) -> Option<Self> {
154 Some(self.mul(&other.inv()?))
155 }
156
157 pub fn pow(&self, exp: u32) -> Self {
159 Self::from_ints(&self.num().pow(exp), &self.den().pow(exp)).expect("分母非零")
160 }
161
162 pub fn pow_reduced(&self, exp: u32) -> Self {
169 let n = self.num().pow(exp);
170 let d = self.den().pow(exp);
171 match (n.to_i64(), d.to_u64()) {
172 (Some(sn), Some(sd)) => Rational(Repr::Small { n: sn, d: sd }),
173 _ => Rational(Repr::Big(Box::new((n.to_bigint(), d.to_bigint())))),
174 }
175 }
176
177 pub fn to_f64(&self) -> f64 {
179 match &self.0 {
180 Repr::Small { n, d } => (*n as f64) / (*d as f64),
181 Repr::Big(p) => {
182 let n: f64 = p.0.to_string().parse().unwrap_or(f64::INFINITY);
183 let d: f64 = p.1.to_string().parse().unwrap_or(f64::INFINITY);
184 n / d
185 }
186 }
187 }
188
189 pub fn inv_reduced(&self) -> Option<Self> {
192 match &self.0 {
193 Repr::Small { n: 0, .. } => None,
194 Repr::Small { n, d } => {
195 if *d <= i64::MAX as u64 {
197 let num = *d as i64;
198 Some(Rational(Repr::Small {
199 n: if *n < 0 { -num } else { num },
200 d: n.unsigned_abs(),
201 }))
202 } else {
203 let num = Integer::from_u64(*d);
204 let num = if *n < 0 { num.neg() } else { num };
205 Some(Rational(Repr::Big(Box::new((
206 num.to_bigint(),
207 BigInt::from(n.unsigned_abs()),
208 )))))
209 }
210 }
211 Repr::Big(p) => {
212 let (num, den) = if p.0.sign() == num_bigint::Sign::Minus {
213 (-p.1.clone(), -p.0.clone())
214 } else {
215 (p.1.clone(), p.0.clone())
216 };
217 Some(Rational(Repr::Big(Box::new((num, den)))))
218 }
219 }
220 }
221
222 pub fn cmp_integer(&self, o: &Integer) -> Ordering {
224 match (&self.0, o.to_i64()) {
225 (Repr::Small { n, d }, Some(i)) => (*n as i128).cmp(&((i as i128) * (*d as i128))),
226 _ => self.n_big().cmp(&(o.to_bigint() * self.d_big())),
227 }
228 }
229
230 fn from_parts_wide(n: i128, d: u128) -> Self {
231 debug_assert!(d > 0);
232 if n == 0 {
233 return Rational::zero();
234 }
235 let g = gcd_u128(n.unsigned_abs(), d);
236 let (n, d) = (n / g as i128, d / g);
237 match (i64::try_from(n), u64::try_from(d)) {
238 (Ok(sn), Ok(sd)) => Rational(Repr::Small { n: sn, d: sd }),
239 _ => Self::from_big_parts(BigInt::from(n), BigInt::from(d)),
240 }
241 }
242
243 fn from_big_parts(n: BigInt, d: BigInt) -> Self {
244 debug_assert!(d.sign() != num_bigint::Sign::Minus);
245 let g = big_gcd(&n, &d);
246 let (n, d) = if g == BigInt::from(1) {
247 (n, d)
248 } else {
249 (n / &g, d / &g)
250 };
251 match (i64::try_from(&n), u64::try_from(&d)) {
253 (Ok(sn), Ok(sd)) => Rational(Repr::Small { n: sn, d: sd }),
254 _ => Rational(Repr::Big(Box::new((n, d)))),
255 }
256 }
257
258 fn n_big(&self) -> BigInt {
259 match &self.0 {
260 Repr::Small { n, .. } => BigInt::from(*n),
261 Repr::Big(p) => p.0.clone(),
262 }
263 }
264
265 fn d_big(&self) -> BigInt {
266 match &self.0 {
267 Repr::Small { d, .. } => BigInt::from(*d),
268 Repr::Big(p) => p.1.clone(),
269 }
270 }
271}
272
273fn gcd_u128(mut a: u128, mut b: u128) -> u128 {
274 while b != 0 {
275 let r = a % b;
276 a = b;
277 b = r;
278 }
279 a
280}
281
282impl Ord for Rational {
283 fn cmp(&self, other: &Self) -> Ordering {
284 match (&self.0, &other.0) {
285 (Repr::Small { n: n1, d: d1 }, Repr::Small { n: n2, d: d2 }) => {
286 let l = (*n1 as i128) * (*d2 as i128);
287 let r = (*n2 as i128) * (*d1 as i128);
288 l.cmp(&r)
289 }
290 _ => (self.n_big() * other.d_big()).cmp(&(other.n_big() * self.d_big())),
291 }
292 }
293}
294
295impl PartialOrd for Rational {
296 fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
297 Some(self.cmp(other))
298 }
299}
300
301impl Hash for Rational {
302 fn hash<H: Hasher>(&self, state: &mut H) {
303 match &self.0 {
304 Repr::Small { n, d } => {
305 n.hash(state);
306 d.hash(state);
307 }
308 Repr::Big(p) => {
309 p.0.hash(state);
310 p.1.hash(state);
311 }
312 }
313 }
314}
315
316impl fmt::Display for Rational {
317 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
318 match &self.0 {
319 Repr::Small { n, d: 1 } => write!(f, "{n}"),
320 Repr::Small { n, d } => write!(f, "{n}/{d}"),
321 Repr::Big(p) if p.1 == BigInt::from(1) => write!(f, "{}", p.0),
322 Repr::Big(p) => write!(f, "{}/{}", p.0, p.1),
323 }
324 }
325}
326
327#[cfg(test)]
328mod tests {
329 use super::*;
330
331 fn r(n: i64, d: i64) -> Rational {
332 Rational::from_ints(&Integer::from_i64(n), &Integer::from_i64(d)).unwrap()
333 }
334
335 #[test]
336 fn 规范化() {
337 assert_eq!(r(4, 8), r(1, 2));
338 assert_eq!(r(-6, 4), r(-3, 2));
339 assert_eq!(r(6, -4), r(-3, 2));
340 assert_eq!(r(0, 7), Rational::zero());
341 assert_eq!(r(9, 3).to_string(), "3");
342 assert_eq!(r(7, 3).to_string(), "7/3");
343 assert_eq!(
344 Rational::from_ints(&Integer::from_i64(1), &Integer::zero()),
345 None
346 );
347 }
348
349 #[test]
350 fn 四则() {
351 assert_eq!(r(1, 2).add(&r(1, 3)), r(5, 6));
352 assert_eq!(r(1, 2).sub(&r(1, 3)), r(1, 6));
353 assert_eq!(r(2, 3).mul(&r(3, 4)), r(1, 2));
354 assert_eq!(r(1, 2).div(&r(3, 4)), Some(r(2, 3)));
355 assert_eq!(r(1, 2).div(&Rational::zero()), None);
356 assert_eq!(r(1, 3).mul(&r(3, 1)), Rational::one());
358 }
359
360 #[test]
361 fn 大数路径() {
362 let big = Integer::parse("9223372036854775808").unwrap(); let q = Rational::from_ints(&big, &Integer::from_i64(2)).unwrap();
364 assert_eq!(q.to_string(), "4611686018427387904");
365 assert!(q.cmp(&r(1, 1)) == Ordering::Greater);
367 assert_eq!(q.mul(&r(1, 2)).to_string(), "2305843009213693952");
369 let m = Integer::from_i64(i64::MIN);
371 let qm = Rational::from_ints(&m, &Integer::from_i64(2)).unwrap();
372 assert_eq!(qm.to_string(), "-4611686018427387904");
373 }
374
375 #[test]
376 fn 幂与逆元() {
377 assert_eq!(r(2, 3).pow(3), r(8, 27));
378 assert_eq!(r(3, 2).inv(), Some(r(2, 3)));
379 assert_eq!(r(-3, 2).inv(), Some(r(-2, 3)));
380 assert_eq!(Rational::zero().inv(), None);
381 }
382
383 #[test]
384 fn 与整数比较() {
385 assert_eq!(
386 r(5, 2).cmp_integer(&Integer::from_i64(2)),
387 Ordering::Greater
388 );
389 assert_eq!(r(5, 2).cmp_integer(&Integer::from_i64(3)), Ordering::Less);
390 assert_eq!(r(4, 2).cmp_integer(&Integer::from_i64(2)), Ordering::Equal);
391 let big = Integer::parse("99999999999999999999").unwrap();
392 assert_eq!(r(1, 1).cmp_integer(&big), Ordering::Less);
393 }
394
395 #[test]
396 fn 快速路径落位一致性() {
397 let d13 = Integer::parse("10260628712958602189").unwrap();
401 let r = Rational::from_ints(&Integer::from_i64(-2), &Integer::from_i64(29)).unwrap();
402 let p = r.pow_reduced(13);
403 let expect = Rational::from_ints(&Integer::from_i64(-8192), &d13).unwrap();
404 assert_eq!(p, expect); assert_eq!(p.to_string(), "-8192/10260628712958602189");
406
407 let q = Rational::from_ints(&Integer::from_i64(3), &d13).unwrap();
409 let inv = q.inv_reduced().unwrap();
410 let expect_inv = Rational::from_ints(&d13, &Integer::from_i64(3)).unwrap();
411 assert_eq!(inv, expect_inv);
412 }
413}