malachite_base/num/arithmetic/
overflowing_sub_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    OverflowingSubAssign, OverflowingSubMul, OverflowingSubMulAssign, UnsignedAbs,
11};
12use crate::num::basic::signeds::PrimitiveSigned;
13use crate::num::basic::unsigneds::PrimitiveUnsigned;
14
15fn overflowing_sub_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_sub(product);
18    (result, overflow_1 | overflow_2)
19}
20
21macro_rules! impl_overflowing_sub_mul_unsigned {
22    ($t:ident) => {
23        impl OverflowingSubMul<$t> for $t {
24            type Output = $t;
25
26            /// Subtracts a number by the product of two other numbers.
27            ///
28            /// Returns a tuple containing the result and a boolean indicating whether an arithmetic
29            /// overflow would occur. If an overflow would have occurred, then the wrapped value is
30            /// returned.
31            ///
32            /// # Worst-case complexity
33            /// Constant time and additional memory.
34            ///
35            /// # Examples
36            /// See [here](super::overflowing_sub_mul#overflowing_sub_mul).
37            #[inline]
38            fn overflowing_sub_mul(self, y: $t, z: $t) -> ($t, bool) {
39                overflowing_sub_mul_unsigned(self, y, z)
40            }
41        }
42
43        impl OverflowingSubMulAssign<$t> for $t {
44            /// Subtracts a number by the product of two other numbers, in place.
45            ///
46            /// Returns a boolean indicating whether an arithmetic overflow would occur. If an
47            /// overflow would have occurred, then the wrapped value is assigned.
48            ///
49            /// # Worst-case complexity
50            /// Constant time and additional memory.
51            ///
52            /// # Examples
53            /// See [here](super::overflowing_sub_mul#overflowing_sub_mul_assign).
54            #[inline]
55            fn overflowing_sub_mul_assign(&mut self, y: $t, z: $t) -> bool {
56                let (product, overflow) = y.overflowing_mul(z);
57                self.overflowing_sub_assign(product) | overflow
58            }
59        }
60    };
61}
62apply_to_unsigneds!(impl_overflowing_sub_mul_unsigned);
63
64fn overflowing_sub_mul<U: PrimitiveUnsigned, S: PrimitiveSigned + UnsignedAbs<Output = U>>(
65    x: S,
66    y: S,
67    z: S,
68) -> (S, bool) {
69    if y == S::ZERO || z == S::ZERO {
70        return (x, false);
71    }
72    let x_sign = x >= S::ZERO;
73    if x_sign == ((y >= S::ZERO) != (z >= S::ZERO)) {
74        let (product, overflow_1) = y.overflowing_mul(z);
75        let (result, overflow_2) = x.overflowing_sub(product);
76        (result, overflow_1 | overflow_2)
77    } else {
78        let result = x.wrapping_sub(y.wrapping_mul(z));
79        let overflow = {
80            let x = x.unsigned_abs();
81            match y.unsigned_abs().checked_mul(z.unsigned_abs()) {
82                Some(product) => {
83                    x < product
84                        && if x_sign {
85                            !x.wrapping_sub(product).get_highest_bit()
86                        } else {
87                            product.wrapping_sub(x).get_highest_bit()
88                        }
89                }
90                None => true,
91            }
92        };
93        (result, overflow)
94    }
95}
96
97macro_rules! impl_overflowing_sub_mul_signed {
98    ($t:ident) => {
99        impl OverflowingSubMul<$t> for $t {
100            type Output = $t;
101
102            /// Subtracts a number by the product of two other numbers.
103            ///
104            /// Returns a tuple containing the result and a boolean indicating whether an arithmetic
105            /// overflow occurred. If an overflow occurred, then the wrapped value is returned.
106            ///
107            /// # Worst-case complexity
108            /// Constant time and additional memory.
109            ///
110            /// # Examples
111            /// See [here](super::overflowing_sub_mul#overflowing_sub_mul).
112            #[inline]
113            fn overflowing_sub_mul(self, y: $t, z: $t) -> ($t, bool) {
114                overflowing_sub_mul(self, y, z)
115            }
116        }
117
118        impl OverflowingSubMulAssign<$t> for $t {
119            /// Subtracts a number by the product of two other numbers, in place.
120            ///
121            /// Returns a boolean indicating whether an arithmetic overflow would occur. If an
122            /// overflow would have occurred, then the wrapped value is assigned.
123            ///
124            /// # Worst-case complexity
125            /// Constant time and additional memory.
126            ///
127            /// # Examples
128            /// See [here](super::overflowing_sub_mul#overflowing_sub_mul_assign).
129            #[inline]
130            fn overflowing_sub_mul_assign(&mut self, y: $t, z: $t) -> bool {
131                let overflow;
132                (*self, overflow) = self.overflowing_sub_mul(y, z);
133                overflow
134            }
135        }
136    };
137}
138apply_to_signeds!(impl_overflowing_sub_mul_signed);