malachite_base/num/arithmetic/overflowing_add_mul.rs
1// Copyright © 2025 Mikhail Hogrefe
2//
3// This file is part of Malachite.
4//
5// Malachite is free software: you can redistribute it and/or modify it under the terms of the GNU
6// Lesser General Public License (LGPL) as published by the Free Software Foundation; either version
7// 3 of the License, or (at your option) any later version. See <https://www.gnu.org/licenses/>.
8
9use crate::num::arithmetic::traits::{
10 OverflowingAddAssign, OverflowingAddMul, OverflowingAddMulAssign, UnsignedAbs,
11};
12use crate::num::basic::signeds::PrimitiveSigned;
13use crate::num::basic::unsigneds::PrimitiveUnsigned;
14
15fn overflowing_add_mul_unsigned<T: PrimitiveUnsigned>(x: T, y: T, z: T) -> (T, bool) {
16 let (product, overflow_1) = y.overflowing_mul(z);
17 let (result, overflow_2) = x.overflowing_add(product);
18 (result, overflow_1 | overflow_2)
19}
20
21macro_rules! impl_overflowing_add_mul_unsigned {
22 ($t:ident) => {
23 impl OverflowingAddMul<$t> for $t {
24 type Output = $t;
25
26 /// Adds a number and the product of two other numbers.
27 ///
28 /// Returns a tuple containing the result and a boolean indicating whether an arithmetic
29 /// overflow occured. If an overflow occurred, then the wrapped value is returned.
30 ///
31 /// # Worst-case complexity
32 /// Constant time and additional memory.
33 ///
34 /// # Examples
35 /// See [here](super::overflowing_add_mul#overflowing_add_mul).
36 #[inline]
37 fn overflowing_add_mul(self, y: $t, z: $t) -> ($t, bool) {
38 overflowing_add_mul_unsigned(self, y, z)
39 }
40 }
41
42 impl OverflowingAddMulAssign<$t> for $t {
43 /// Adds a number and the product of two other numbers, in place.
44 ///
45 /// Returns a boolean indicating whether an arithmetic overflow occurred. If an overflow
46 /// occurred, then the wrapped value is assigned.
47 ///
48 /// # Worst-case complexity
49 /// Constant time and additional memory.
50 ///
51 /// # Examples
52 /// See [here](super::overflowing_add_mul#overflowing_add_mul_assign).
53 #[inline]
54 fn overflowing_add_mul_assign(&mut self, y: $t, z: $t) -> bool {
55 let (product, overflow) = y.overflowing_mul(z);
56 self.overflowing_add_assign(product) | overflow
57 }
58 }
59 };
60}
61apply_to_unsigneds!(impl_overflowing_add_mul_unsigned);
62
63fn overflowing_add_mul_signed<
64 U: PrimitiveUnsigned,
65 S: PrimitiveSigned + UnsignedAbs<Output = U>,
66>(
67 x: S,
68 y: S,
69 z: S,
70) -> (S, bool) {
71 if y == S::ZERO || z == S::ZERO {
72 return (x, false);
73 }
74 let x_sign = x >= S::ZERO;
75 if x_sign == ((y >= S::ZERO) == (z >= S::ZERO)) {
76 let (product, overflow_1) = y.overflowing_mul(z);
77 let (result, overflow_2) = x.overflowing_add(product);
78 (result, overflow_1 | overflow_2)
79 } else {
80 let result = x.wrapping_add(y.wrapping_mul(z));
81 let overflow = {
82 let x = x.unsigned_abs();
83 match y.unsigned_abs().checked_mul(z.unsigned_abs()) {
84 Some(product) => {
85 x < product
86 && if x_sign {
87 !x.wrapping_sub(product).get_highest_bit()
88 } else {
89 product.wrapping_sub(x).get_highest_bit()
90 }
91 }
92 None => true,
93 }
94 };
95 (result, overflow)
96 }
97}
98
99macro_rules! impl_overflowing_add_mul_signed {
100 ($t:ident) => {
101 impl OverflowingAddMul<$t> for $t {
102 type Output = $t;
103
104 /// Adds a number and the product of two other numbers.
105 ///
106 /// Returns a tuple containing the result and a boolean indicating whether an arithmetic
107 /// overflow occurred. If an overflow occurred, then the wrapped value is returned.
108 ///
109 /// # Worst-case complexity
110 /// Constant time and additional memory.
111 ///
112 /// # Examples
113 /// See [here](super::overflowing_add_mul#overflowing_add_mul).
114 #[inline]
115 fn overflowing_add_mul(self, y: $t, z: $t) -> ($t, bool) {
116 overflowing_add_mul_signed(self, y, z)
117 }
118 }
119
120 impl OverflowingAddMulAssign<$t> for $t {
121 /// Adds a number and the product of two other numbers, in place.
122 ///
123 /// Returns a boolean indicating whether an arithmetic overflow occurred. If an overflow
124 /// occurred, then the wrapped value is assigned.
125 ///
126 /// # Worst-case complexity
127 /// Constant time and additional memory.
128 ///
129 /// # Examples
130 /// See [here](super::overflowing_add_mul#overflowing_add_mul_assign).
131 #[inline]
132 fn overflowing_add_mul_assign(&mut self, y: $t, z: $t) -> bool {
133 let overflow;
134 (*self, overflow) = self.overflowing_add_mul(y, z);
135 overflow
136 }
137 }
138 };
139}
140apply_to_signeds!(impl_overflowing_add_mul_signed);