1use crate::ext::xmpq;
18use crate::ext::xmpq::OptRational;
19use crate::integer::MiniInteger;
20use crate::integer::arith::AsLong;
21use crate::ops::{AddFrom, DivFrom, MulFrom, NegAssign, Pow, PowAssign, SubFrom};
22use crate::rational::MiniRational;
23use crate::{Assign, Complete, Integer, Rational};
24use az::{CheckedAs, CheckedCast};
25use core::ffi::{c_long, c_ulong};
26use core::iter::{Product, Sum};
27use core::ops::{
28 Add, AddAssign, Div, DivAssign, Mul, MulAssign, Neg, Shl, ShlAssign, Shr, ShrAssign, Sub,
29 SubAssign,
30};
31
32arith_unary! {
33 Rational;
34 xmpq::neg;
35 Neg { neg }
36 NegAssign { neg_assign }
37 NegIncomplete
38}
39arith_binary_self! {
40 Rational;
41 xmpq::add;
42 Add { add }
43 AddAssign { add_assign }
44 AddFrom { add_from }
45 AddIncomplete;
46 rhs_has_more_alloc
47}
48arith_binary_self! {
49 Rational;
50 xmpq::sub;
51 Sub { sub }
52 SubAssign { sub_assign }
53 SubFrom { sub_from }
54 SubIncomplete;
55 rhs_has_more_alloc
56}
57arith_binary_self! {
58 Rational;
59 xmpq::mul;
60 Mul { mul }
61 MulAssign { mul_assign }
62 MulFrom { mul_from }
63 MulIncomplete;
64 rhs_has_more_alloc
65}
66arith_binary_self! {
67 Rational;
68 xmpq::div;
69 Div { div }
70 DivAssign { div_assign }
71 DivFrom { div_from }
72 DivIncomplete;
73 rhs_has_more_alloc
74}
75
76arith_mini_commut! {
77 Rational;
78 Add { add }
79 AddAssign { add_assign }
80 AddFrom { add_from }
81 MiniRational;
82 AddMiniIncomplete, AddOwnedMiniIncomplete
83}
84arith_mini_noncommut! {
85 Rational;
86 Sub { sub }
87 SubAssign { sub_assign }
88 SubFrom { sub_from }
89 MiniRational;
90 SubMiniIncomplete, SubOwnedMiniIncomplete;
91 SubFromMiniIncomplete, SubFromOwnedMiniIncomplete
92}
93arith_mini_commut! {
94 Rational;
95 Mul { mul }
96 MulAssign { mul_assign }
97 MulFrom { mul_from }
98 MiniRational;
99 MulMiniIncomplete, MulOwnedMiniIncomplete
100}
101arith_mini_noncommut! {
102 Rational;
103 Div { div }
104 DivAssign { div_assign }
105 DivFrom { div_from }
106 MiniRational;
107 DivMiniIncomplete, DivOwnedMiniIncomplete;
108 DivFromMiniIncomplete, DivFromOwnedMiniIncomplete
109}
110
111arith_commut! {
112 Rational;
113 xmpq::add_z;
114 Add { add }
115 AddAssign { add_assign }
116 AddFrom { add_from }
117 Integer;
118 AddIntegerIncomplete, AddOwnedIntegerIncomplete
119}
120arith_noncommut! {
121 Rational;
122 xmpq::sub_z;
123 xmpq::z_sub;
124 Sub { sub }
125 SubAssign { sub_assign }
126 SubFrom { sub_from }
127 Integer;
128 SubIntegerIncomplete, SubOwnedIntegerIncomplete;
129 SubFromIntegerIncomplete, SubFromOwnedIntegerIncomplete
130}
131arith_commut! {
132 Rational;
133 xmpq::mul_z;
134 Mul { mul }
135 MulAssign { mul_assign }
136 MulFrom { mul_from }
137 Integer;
138 MulIntegerIncomplete, MulOwnedIntegerIncomplete
139}
140arith_noncommut! {
141 Rational;
142 xmpq::div_z;
143 xmpq::z_div;
144 Div { div }
145 DivAssign { div_assign }
146 DivFrom { div_from }
147 Integer;
148 DivIntegerIncomplete, DivOwnedIntegerIncomplete;
149 DivFromIntegerIncomplete, DivFromOwnedIntegerIncomplete
150}
151
152arith_prim_commut! {
153 Rational;
154 PrimOps::add;
155 Add { add }
156 AddAssign { add_assign }
157 AddFrom { add_from }
158 i8, AddI8Incomplete;
159 i16, AddI16Incomplete;
160 i32, AddI32Incomplete;
161 i64, AddI64Incomplete;
162 i128, AddI128Incomplete;
163 isize, AddIsizeIncomplete;
164 u8, AddU8Incomplete;
165 u16, AddU16Incomplete;
166 u32, AddU32Incomplete;
167 u64, AddU64Incomplete;
168 u128, AddU128Incomplete;
169 usize, AddUsizeIncomplete;
170}
171arith_prim_noncommut! {
172 Rational;
173 PrimOps::sub, PrimOps::sub_from;
174 Sub { sub }
175 SubAssign { sub_assign }
176 SubFrom { sub_from }
177 i8, SubI8Incomplete, SubFromI8Incomplete;
178 i16, SubI16Incomplete, SubFromI16Incomplete;
179 i32, SubI32Incomplete, SubFromI32Incomplete;
180 i64, SubI64Incomplete, SubFromI64Incomplete;
181 i128, SubI128Incomplete, SubFromI128Incomplete;
182 isize, SubIsizeIncomplete, SubFromIsizeIncomplete;
183 u8, SubU8Incomplete, SubFromU8Incomplete;
184 u16, SubU16Incomplete, SubFromU16Incomplete;
185 u32, SubU32Incomplete, SubFromU32Incomplete;
186 u64, SubU64Incomplete, SubFromU64Incomplete;
187 u128, SubU128Incomplete, SubFromU128Incomplete;
188 usize, SubUsizeIncomplete, SubFromUsizeIncomplete;
189}
190arith_prim_commut! {
191 Rational;
192 PrimOps::mul;
193 Mul { mul }
194 MulAssign { mul_assign }
195 MulFrom { mul_from }
196 i8, MulI8Incomplete;
197 i16, MulI16Incomplete;
198 i32, MulI32Incomplete;
199 i64, MulI64Incomplete;
200 i128, MulI128Incomplete;
201 isize, MulIsizeIncomplete;
202 u8, MulU8Incomplete;
203 u16, MulU16Incomplete;
204 u32, MulU32Incomplete;
205 u64, MulU64Incomplete;
206 u128, MulU128Incomplete;
207 usize, MulUsizeIncomplete;
208}
209arith_prim_noncommut! {
210 Rational;
211 PrimOps::div, PrimOps::div_from;
212 Div { div }
213 DivAssign { div_assign }
214 DivFrom { div_from }
215 i8, DivI8Incomplete, DivFromI8Incomplete;
216 i16, DivI16Incomplete, DivFromI16Incomplete;
217 i32, DivI32Incomplete, DivFromI32Incomplete;
218 i64, DivI64Incomplete, DivFromI64Incomplete;
219 i128, DivI128Incomplete, DivFromI128Incomplete;
220 isize, DivIsizeIncomplete, DivFromIsizeIncomplete;
221 u8, DivU8Incomplete, DivFromU8Incomplete;
222 u16, DivU16Incomplete, DivFromU16Incomplete;
223 u32, DivU32Incomplete, DivFromU32Incomplete;
224 u64, DivU64Incomplete, DivFromU64Incomplete;
225 u128, DivU128Incomplete, DivFromU128Incomplete;
226 usize, DivUsizeIncomplete, DivFromUsizeIncomplete;
227}
228
229arith_prim! {
230 Rational;
231 xmpq::shl_i32;
232 Shl { shl }
233 ShlAssign { shl_assign }
234 i32, ShlI32Incomplete;
235}
236arith_prim! {
237 Rational;
238 xmpq::shr_i32;
239 Shr { shr }
240 ShrAssign { shr_assign }
241 i32, ShrI32Incomplete;
242}
243arith_prim! {
244 Rational;
245 xmpq::shl_u32;
246 Shl { shl }
247 ShlAssign { shl_assign }
248 u32, ShlU32Incomplete;
249}
250arith_prim! {
251 Rational;
252 xmpq::shr_u32;
253 Shr { shr }
254 ShrAssign { shr_assign }
255 u32, ShrU32Incomplete;
256}
257arith_prim! {
258 Rational;
259 xmpq::shl_isize;
260 Shl { shl }
261 ShlAssign { shl_assign }
262 isize, ShlIsizeIncomplete;
263}
264arith_prim! {
265 Rational;
266 xmpq::shr_isize;
267 Shr { shr }
268 ShrAssign { shr_assign }
269 isize, ShrIsizeIncomplete;
270}
271arith_prim! {
272 Rational;
273 xmpq::shl_usize;
274 Shl { shl }
275 ShlAssign { shl_assign }
276 usize, ShlUsizeIncomplete;
277}
278arith_prim! {
279 Rational;
280 xmpq::shr_usize;
281 Shr { shr }
282 ShrAssign { shr_assign }
283 usize, ShrUsizeIncomplete;
284}
285arith_prim! {
286 Rational;
287 xmpq::pow_i32;
288 Pow { pow }
289 PowAssign { pow_assign }
290 i32, PowI32Incomplete;
291}
292arith_prim! {
293 Rational;
294 xmpq::pow_u32;
295 Pow { pow }
296 PowAssign { pow_assign }
297 u32, PowU32Incomplete;
298}
299
300trait PrimOps<Long>: AsLong {
301 fn add<O: OptRational>(rop: &mut Rational, op1: O, op2: Self);
302 fn sub<O: OptRational>(rop: &mut Rational, op1: O, op2: Self);
303 fn sub_from<O: OptRational>(rop: &mut Rational, op1: Self, op2: O);
304 fn mul<O: OptRational>(rop: &mut Rational, op1: O, op2: Self);
305 fn div<O: OptRational>(rop: &mut Rational, op1: O, op2: Self);
306 fn div_from<O: OptRational>(rop: &mut Rational, op1: Self, op2: O);
307}
308
309macro_rules! forward {
310 (fn $fn:ident() -> $deleg_long:path, $deleg:path) => {
311 #[inline]
312 fn $fn<O: OptRational>(rop: &mut Rational, op1: O, op2: Self) {
313 if let Some(op2) = op2.checked_as() {
314 $deleg_long(rop, op1, op2);
315 } else {
316 let mut small: MiniInteger = op2.into();
317 $deleg(rop, op1, &*small.borrow_excl());
318 }
319 }
320 };
321}
322macro_rules! reverse {
323 (fn $fn:ident() -> $deleg_long:path, $deleg:path) => {
324 #[inline]
325 fn $fn<O: OptRational>(rop: &mut Rational, op1: Self, op2: O) {
326 if let Some(op1) = op1.checked_as() {
327 $deleg_long(rop, op1, op2);
328 } else {
329 let mut small: MiniInteger = op1.into();
330 $deleg(rop, &*small.borrow_excl(), op2);
331 }
332 }
333 };
334}
335
336impl<T> PrimOps<c_long> for T
337where
338 T: AsLong<Long = c_long> + CheckedCast<c_long> + Into<MiniInteger>,
339{
340 forward! { fn add() -> xmpq::add_si, xmpq::add_z }
341 forward! { fn sub() -> xmpq::sub_si, xmpq::sub_z }
342 reverse! { fn sub_from() -> xmpq::si_sub, xmpq::z_sub }
343 forward! { fn mul() -> xmpq::mul_si, xmpq::mul_z }
344 forward! { fn div() -> xmpq::div_si, xmpq::div_z }
345 reverse! { fn div_from() -> xmpq::si_div, xmpq::z_div }
346}
347
348impl<T> PrimOps<c_ulong> for T
349where
350 T: AsLong<Long = c_ulong> + CheckedCast<c_ulong> + Into<MiniInteger>,
351{
352 forward! { fn add() -> xmpq::add_ui, xmpq::add_z }
353 forward! { fn sub() -> xmpq::sub_ui, xmpq::sub_z }
354 reverse! { fn sub_from() -> xmpq::ui_sub, xmpq::z_sub }
355 forward! { fn mul() -> xmpq::mul_ui, xmpq::mul_z }
356 forward! { fn div() -> xmpq::div_ui, xmpq::div_z }
357 reverse! { fn div_from() -> xmpq::ui_div, xmpq::z_div }
358}
359
360impl<T> Sum<T> for Rational
361where
362 Rational: AddAssign<T>,
363{
364 fn sum<I>(iter: I) -> Rational
365 where
366 I: Iterator<Item = T>,
367 {
368 let mut ret = Rational::new();
369 for i in iter {
370 ret.add_assign(i);
371 }
372 ret
373 }
374}
375
376impl<T> Product<T> for Rational
377where
378 Rational: MulAssign<T>,
379{
380 fn product<I>(iter: I) -> Rational
381 where
382 I: Iterator<Item = T>,
383 {
384 let mut ret = Rational::from(1);
385 for i in iter {
386 ret.mul_assign(i);
387 }
388 ret
389 }
390}
391
392#[inline]
393fn rhs_has_more_alloc(lhs: &Rational, rhs: &Rational) -> bool {
394 lhs.numer().inner().alloc - rhs.denom().inner().alloc
400 < rhs.numer().inner().alloc - lhs.denom().inner().alloc
401}
402
403#[cfg(test)]
404mod tests {
405 use crate::ops::Pow;
406 use crate::ops::{AddFrom, SubFrom};
407 use crate::rational::MiniRational;
408 use crate::{Assign, Complete, Integer, Rational};
409
410 macro_rules! test_ref_op {
411 ($first:expr, $second:expr) => {
412 assert_eq!(
413 Rational::from($first),
414 $second,
415 "({}) != ({})",
416 stringify!($first),
417 stringify!($second)
418 );
419 };
420 }
421
422 #[test]
423 fn check_ref_op() {
424 let lhs = Rational::from((-13, 27));
425 let rhs = Rational::from((15, 101));
426 let pu = 30_u32;
427 let pi = -15_i32;
428 test_ref_op!(-&lhs, -lhs.clone());
429 test_ref_op!(&lhs + &rhs, lhs.clone() + &rhs);
430 test_ref_op!(&lhs - &rhs, lhs.clone() - &rhs);
431 test_ref_op!(&lhs * &rhs, lhs.clone() * &rhs);
432 test_ref_op!(&lhs / &rhs, lhs.clone() / &rhs);
433
434 test_ref_op!(&lhs << pu, lhs.clone() << pu);
435 test_ref_op!(&lhs >> pu, lhs.clone() >> pu);
436 test_ref_op!((&lhs).pow(pu), lhs.clone().pow(pu));
437
438 test_ref_op!(&lhs << pi, lhs.clone() << pi);
439 test_ref_op!(&lhs >> pi, lhs.clone() >> pi);
440 test_ref_op!((&lhs).pow(pi), lhs.clone().pow(pi));
441 }
442
443 macro_rules! test_numer_denom {
444 ($first:expr, $second:expr) => {{
445 let a = $first;
446 let b = $second;
447 assert_eq!(
448 a.numer(),
449 b.numer(),
450 "({}).numer() != ({}).numer()",
451 stringify!($first),
452 stringify!($second)
453 );
454 assert_eq!(
455 a.denom(),
456 b.denom(),
457 "({}).denom() != ({}).denom()",
458 stringify!($first),
459 stringify!($second)
460 );
461 }};
462 }
463
464 #[test]
465 fn check_arith_int() {
466 let r = &Rational::from((-15, 128));
467 let zero = &Integer::new();
468 let minus_100 = &Integer::from(-100);
469
470 test_numer_denom!(r.clone() + zero, r + Rational::from(zero));
471 test_numer_denom!(r.clone() - zero, r - Rational::from(zero));
472 test_numer_denom!(r.clone() * zero, r * Rational::from(zero));
473 test_numer_denom!(r.clone() + minus_100, r + Rational::from(minus_100));
474 test_numer_denom!(r.clone() - minus_100, r - Rational::from(minus_100));
475 test_numer_denom!(r.clone() * minus_100, r * Rational::from(minus_100));
476 test_numer_denom!(r.clone() / minus_100, r / Rational::from(minus_100));
477
478 test_numer_denom!(zero + r.clone(), Rational::from(zero) + r);
479 test_numer_denom!(zero - r.clone(), Rational::from(zero) - r);
480 test_numer_denom!(zero * r.clone(), Rational::from(zero) * r);
481 test_numer_denom!(zero / r.clone(), Rational::from(zero) / r);
482 test_numer_denom!(minus_100 + r.clone(), Rational::from(minus_100) + r);
483 test_numer_denom!(minus_100 - r.clone(), Rational::from(minus_100) - r);
484 test_numer_denom!(minus_100 * r.clone(), Rational::from(minus_100) * r);
485 test_numer_denom!(minus_100 / r.clone(), Rational::from(minus_100) / r);
486 }
487
488 #[test]
489 fn check_neg_pow() {
490 let a = Rational::from((-12, 7));
491 let pow_pos = a.clone().pow(3i32);
492 assert_eq!(
493 pow_pos,
494 MiniRational::from(((-12i32).pow(3), 7i32.pow(3u32)))
495 );
496 let pow_neg = a.pow(-3i32);
497 assert_eq!(pow_neg, MiniRational::from(((-7i32).pow(3), 12i32.pow(3))));
498 }
499
500 macro_rules! check_u_s {
501 ($list:expr, $against:expr) => {
502 for &op in $list {
503 let rat_op = Rational::from(op);
504 for b in &$against {
505 test_numer_denom!(b.clone() + op, b.clone() + &rat_op);
506 test_numer_denom!(b.clone() - op, b.clone() - &rat_op);
507 test_numer_denom!(b.clone() * op, b.clone() * &rat_op);
508 if op != 0 {
509 test_numer_denom!(b.clone() / op, b.clone() / &rat_op);
510 }
511 test_numer_denom!(op + b.clone(), rat_op.clone() + b);
512 test_numer_denom!(op - b.clone(), rat_op.clone() - b);
513 test_numer_denom!(op * b.clone(), rat_op.clone() * b);
514 if !b.is_zero() {
515 test_numer_denom!(op / b.clone(), rat_op.clone() / b);
516 }
517 }
518 }
519 };
520 }
521
522 fn num_den<T>(s: &[T]) -> impl Iterator<Item = Rational> + '_
523 where
524 T: Copy + Eq + From<bool>,
525 Rational: From<(T, T)>,
526 {
527 let zero = T::from(false);
528 let one = T::from(true);
529 s.iter()
530 .map(move |&den| if den == zero { one } else { den })
531 .flat_map(move |den| s.iter().map(move |&num| Rational::from((num, den))))
532 }
533
534 #[test]
535 fn check_arith_u_s() {
536 use crate::tests::{I8, I16, I32, I64, I128, ISIZE, U8, U16, U32, U64, U128, USIZE};
537 let large = [(1, 3, 100), (-11, 5, 200), (33, 79, -150)];
538 let against = (large.iter().map(|&(n, d, s)| Rational::from((n, d)) << s))
539 .chain(num_den(I32))
540 .chain(num_den(U32))
541 .chain(num_den(I64))
542 .chain(num_den(U64))
543 .chain(num_den(I128))
544 .chain(num_den(U128))
545 .chain(num_den(ISIZE))
546 .chain(num_den(USIZE))
547 .collect::<Vec<Rational>>();
548
549 check_u_s!(I8, against);
550 check_u_s!(I16, against);
551 check_u_s!(I32, against);
552 check_u_s!(I64, against);
553 check_u_s!(I128, against);
554 check_u_s!(ISIZE, against);
555 check_u_s!(U8, against);
556 check_u_s!(U16, against);
557 check_u_s!(U32, against);
558 check_u_s!(U64, against);
559 check_u_s!(USIZE, against);
560 }
561
562 #[test]
563 #[allow(clippy::op_ref)]
564 fn check_mini_ops() {
565 let big = Rational::from((10, 3));
566 let mini = MiniRational::from((3, 2));
567 let mut bm = Rational::new();
568
569 let sum = MiniRational::from((29, 6));
571 assert_eq!(big.clone() + mini, sum);
572 assert_eq!(big.clone() + &mini, sum);
573 assert_eq!((&big + mini).complete(), sum);
574 assert_eq!((&big + &mini).complete(), sum);
575
576 bm.assign(&big);
577 bm += mini;
578 assert_eq!(bm, sum);
579 bm.assign(&big);
580 bm += &mini;
581 assert_eq!(bm, sum);
582
583 assert_eq!(mini + big.clone(), sum);
584 assert_eq!(&mini + big.clone(), sum);
585 assert_eq!((mini + &big).complete(), sum);
586 assert_eq!((&mini + &big).complete(), sum);
587
588 bm.assign(&big);
589 bm.add_from(mini);
590 assert_eq!(bm, sum);
591 bm.assign(&big);
592 bm.add_from(&mini);
593 assert_eq!(bm, sum);
594
595 let diff1 = MiniRational::from((11, 6));
597 assert_eq!(big.clone() - mini, diff1);
598 assert_eq!(big.clone() - &mini, diff1);
599 assert_eq!((&big - mini).complete(), diff1);
600 assert_eq!((&big - &mini).complete(), diff1);
601
602 bm.assign(&big);
603 bm -= mini;
604 assert_eq!(bm, diff1);
605 bm.assign(&big);
606 bm -= &mini;
607 assert_eq!(bm, diff1);
608
609 let diff2 = MiniRational::from((-11, 6));
610 assert_eq!(mini - big.clone(), diff2);
611 assert_eq!(&mini - big.clone(), diff2);
612 assert_eq!((mini - &big).complete(), diff2);
613 assert_eq!((&mini - &big).complete(), diff2);
614
615 bm.assign(&big);
616 bm.sub_from(mini);
617 assert_eq!(bm, diff2);
618 bm.assign(&big);
619 bm.sub_from(&mini);
620 assert_eq!(bm, diff2);
621 }
622
623 #[test]
624 fn check_shift_u_s() {
625 let pos = &(Rational::from((13, 7)) >> 100u32);
626 let neg = &(Rational::from((19, 101)) << 200u32);
627
628 assert_eq!(pos.clone() << 10u32, pos.clone() << 10i32);
629 assert_eq!(pos.clone() << 10u32, pos.clone() >> -10i32);
630 assert_eq!(pos.clone() >> 10u32, pos.clone() >> 10i32);
631 assert_eq!(pos.clone() >> 10u32, pos.clone() << -10i32);
632
633 assert_eq!(neg.clone() << 10u32, neg.clone() << 10i32);
634 assert_eq!(neg.clone() << 10u32, neg.clone() >> -10i32);
635 assert_eq!(neg.clone() >> 10u32, neg.clone() >> 10i32);
636 assert_eq!(neg.clone() >> 10u32, neg.clone() << -10i32);
637
638 assert_eq!(pos.clone() << 10u32, pos.clone() << 10usize);
639 assert_eq!(pos.clone() << 10u32, pos.clone() << 10isize);
640 assert_eq!(pos.clone() << 10u32, pos.clone() >> -10isize);
641 assert_eq!(pos.clone() >> 10u32, pos.clone() >> 10usize);
642 assert_eq!(pos.clone() >> 10u32, pos.clone() >> 10isize);
643 assert_eq!(pos.clone() >> 10u32, pos.clone() << -10isize);
644
645 assert_eq!(neg.clone() << 10u32, neg.clone() << 10usize);
646 assert_eq!(neg.clone() << 10u32, neg.clone() << 10isize);
647 assert_eq!(neg.clone() << 10u32, neg.clone() >> -10isize);
648 assert_eq!(neg.clone() >> 10u32, neg.clone() >> 10usize);
649 assert_eq!(neg.clone() >> 10u32, neg.clone() >> 10isize);
650 assert_eq!(neg.clone() >> 10u32, neg.clone() << -10isize);
651 }
652}