malachite_base/num/arithmetic/
saturating_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::{SaturatingSubMul, SaturatingSubMulAssign, UnsignedAbs};
10use crate::num::basic::signeds::PrimitiveSigned;
11use crate::num::basic::unsigneds::PrimitiveUnsigned;
12use crate::num::conversion::traits::WrappingFrom;
13
14fn saturating_sub_mul_unsigned<T: PrimitiveUnsigned>(x: T, y: T, z: T) -> T {
15    x.saturating_sub(y.saturating_mul(z))
16}
17
18fn saturating_sub_mul_assign_unsigned<T: PrimitiveUnsigned>(x: &mut T, y: T, z: T) {
19    x.saturating_sub_assign(y.saturating_mul(z));
20}
21
22macro_rules! impl_saturating_sub_mul_unsigned {
23    ($t:ident) => {
24        impl SaturatingSubMul<$t> for $t {
25            type Output = $t;
26
27            /// Subtracts a number by the product of two other numbers, saturating at the numeric
28            /// bounds instead of overflowing.
29            ///
30            /// $$
31            /// f(x, y, z) = \\begin{cases}
32            ///     x - yz & \text{if} \\quad m \leq x - yz \leq M, \\\\
33            ///     M & \text{if} \\quad x - yz > M, \\\\
34            ///     m & \text{if} \\quad x - yz < m,
35            /// \\end{cases}
36            /// $$
37            /// where $m$ is `Self::MIN` and $M$ is `Self::MAX`.
38            ///
39            /// # Worst-case complexity
40            /// Constant time and additional memory.
41            ///
42            /// # Examples
43            /// See [here](super::saturating_sub_mul#saturating_sub_mul_assign).
44            #[inline]
45            fn saturating_sub_mul(self, y: $t, z: $t) -> $t {
46                saturating_sub_mul_unsigned(self, y, z)
47            }
48        }
49
50        impl SaturatingSubMulAssign<$t> for $t {
51            /// Subtracts a number by the product of two other numbers in place, saturating at the
52            /// numeric bounds instead of overflowing.
53            ///
54            /// $$
55            /// x \gets \\begin{cases}
56            ///     x - yz & \text{if} \\quad m \leq x - yz \leq M, \\\\
57            ///     M & \text{if} \\quad x - yz > M, \\\\
58            ///     m & \text{if} \\quad x - yz < m,
59            /// \\end{cases}
60            /// $$
61            /// where $m$ is `Self::MIN` and $M$ is `Self::MAX`.
62            ///
63            /// # Worst-case complexity
64            /// Constant time and additional memory.
65            ///
66            /// # Examples
67            /// See [here](super::saturating_sub_mul#saturating_sub_mul_assign).
68            #[inline]
69            fn saturating_sub_mul_assign(&mut self, y: $t, z: $t) {
70                saturating_sub_mul_assign_unsigned(self, y, z);
71            }
72        }
73    };
74}
75apply_to_unsigneds!(impl_saturating_sub_mul_unsigned);
76
77fn saturating_sub_mul_signed<
78    U: PrimitiveUnsigned,
79    S: PrimitiveSigned + UnsignedAbs<Output = U> + WrappingFrom<U>,
80>(
81    x: S,
82    y: S,
83    z: S,
84) -> S {
85    if y == S::ZERO || z == S::ZERO {
86        return x;
87    }
88    let x_sign = x >= S::ZERO;
89    if x_sign == ((y >= S::ZERO) != (z >= S::ZERO)) {
90        x.saturating_sub(y.saturating_mul(z))
91    } else {
92        let x = x.unsigned_abs();
93        let product = if let Some(product) = y.unsigned_abs().checked_mul(z.unsigned_abs()) {
94            product
95        } else {
96            return if x_sign { S::MIN } else { S::MAX };
97        };
98        let result = S::wrapping_from(if x_sign {
99            x.wrapping_sub(product)
100        } else {
101            product.wrapping_sub(x)
102        });
103        if x >= product || (x_sign == (result < S::ZERO)) {
104            result
105        } else if x_sign {
106            S::MIN
107        } else {
108            S::MAX
109        }
110    }
111}
112
113macro_rules! impl_saturating_sub_mul_signed {
114    ($t:ident) => {
115        impl SaturatingSubMul<$t> for $t {
116            type Output = $t;
117
118            /// Subtracts a number by the product of two other numbers, saturating at the numeric
119            /// bounds instead of overflowing.
120            ///
121            /// $$
122            /// f(x, y, z) = \\begin{cases}
123            ///     x - yz & \text{if} \\quad m \leq x - yz \leq M, \\\\
124            ///     M & \text{if} \\quad x - yz > M, \\\\
125            ///     m & \text{if} \\quad x - yz < m,
126            /// \\end{cases}
127            /// $$
128            /// where $m$ is `Self::MIN` and $M$ is `Self::MAX`.
129            ///
130            /// # Worst-case complexity
131            /// Constant time and additional memory.
132            ///
133            /// # Examples
134            /// See [here](super::saturating_sub_mul#saturating_sub_mul).
135            #[inline]
136            fn saturating_sub_mul(self, y: $t, z: $t) -> $t {
137                saturating_sub_mul_signed(self, y, z)
138            }
139        }
140
141        impl SaturatingSubMulAssign<$t> for $t {
142            /// Subtracts a number by the product of two other numbers in place, saturating at the
143            /// numeric bounds instead of overflowing.
144            ///
145            /// $$
146            /// x \gets \\begin{cases}
147            ///     x - yz & \text{if} \\quad m \leq x - yz \leq M, \\\\
148            ///     M & \text{if} \\quad x - yz > M, \\\\
149            ///     m & \text{if} \\quad x - yz < m,
150            /// \\end{cases}
151            /// $$
152            /// where $m$ is `Self::MIN` and $M$ is `Self::MAX`.
153            ///
154            /// # Worst-case complexity
155            /// Constant time and additional memory.
156            ///
157            /// # Examples
158            /// See [here](super::saturating_sub_mul#saturating_sub_mul_assign).
159            #[inline]
160            fn saturating_sub_mul_assign(&mut self, y: $t, z: $t) {
161                *self = self.saturating_sub_mul(y, z);
162            }
163        }
164    };
165}
166apply_to_signeds!(impl_saturating_sub_mul_signed);