malachite_base/num/arithmetic/div_round.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::{DivRound, DivRoundAssign, UnsignedAbs};
10use crate::num::basic::signeds::PrimitiveSigned;
11use crate::num::basic::unsigneds::PrimitiveUnsigned;
12use crate::num::conversion::traits::{ExactFrom, WrappingFrom};
13use crate::rounding_modes::RoundingMode::{self, *};
14use core::cmp::Ordering::{self, *};
15
16fn div_round_unsigned<T: PrimitiveUnsigned>(x: T, other: T, rm: RoundingMode) -> (T, Ordering) {
17 let quotient = x / other;
18 let remainder = x - quotient * other;
19 match rm {
20 _ if remainder == T::ZERO => (quotient, Equal),
21 Down | Floor => (quotient, Less),
22 Up | Ceiling => (quotient + T::ONE, Greater),
23 Nearest => {
24 let shifted_other = other >> 1;
25 if remainder > shifted_other
26 || remainder == shifted_other && other.even() && quotient.odd()
27 {
28 (quotient + T::ONE, Greater)
29 } else {
30 (quotient, Less)
31 }
32 }
33 Exact => {
34 panic!("Division is not exact: {x} / {other}");
35 }
36 }
37}
38
39macro_rules! impl_div_round_unsigned {
40 ($t:ident) => {
41 impl DivRound<$t> for $t {
42 type Output = $t;
43
44 /// Divides a value by another value and rounds according to a specified rounding mode.
45 /// An [`Ordering`] is also returned, indicating whether the returned value is less
46 /// than, equal to, or greater than the exact value.
47 ///
48 /// Let $q = \frac{x}{y}$, and let $g$ be the function that just returns the first
49 /// element of the pair, without the [`Ordering`]:
50 ///
51 /// $$
52 /// g(x, y, \mathrm{Down}) = g(x, y, \mathrm{Floor}) = \lfloor q \rfloor.
53 /// $$
54 ///
55 /// $$
56 /// g(x, y, \mathrm{Up}) = g(x, y, \mathrm{Ceiling}) = \lceil q \rceil.
57 /// $$
58 ///
59 /// $$
60 /// g(x, y, \mathrm{Nearest}) = \begin{cases}
61 /// \lfloor q \rfloor & \text{if} \\quad q - \lfloor q \rfloor < \frac{1}{2}, \\\\
62 /// \lceil q \rceil & \text{if} \\quad q - \lfloor q \rfloor > \frac{1}{2}, \\\\
63 /// \lfloor q \rfloor &
64 /// \text{if} \\quad q - \lfloor q \rfloor = \frac{1}{2}
65 /// \\ \text{and} \\ \lfloor q \rfloor \\ \text{is even}, \\\\
66 /// \lceil q \rceil &
67 /// \text{if} \\quad q - \lfloor q \rfloor = \frac{1}{2}
68 /// \\ \text{and} \\ \lfloor q \rfloor \\ \text{is odd.}
69 /// \end{cases}
70 /// $$
71 ///
72 /// $g(x, y, \mathrm{Exact}) = q$, but panics if $q \notin \N$.
73 ///
74 /// Then
75 ///
76 /// $f(x, y, r) = (g(x, y, r), \operatorname{cmp}(g(x, y, r), q))$.
77 ///
78 /// # Worst-case complexity
79 /// Constant time and additional memory.
80 ///
81 /// # Panics
82 /// Panics if `other` is zero, or if `rm` is `Exact` but `self` is not divisible by
83 /// `other`.
84 ///
85 /// # Examples
86 /// See [here](super::div_round#div_round).
87 #[inline]
88 fn div_round(self, other: $t, rm: RoundingMode) -> ($t, Ordering) {
89 div_round_unsigned(self, other, rm)
90 }
91 }
92
93 impl DivRoundAssign<$t> for $t {
94 /// Divides a value by another value in place and rounds according to a specified
95 /// rounding mode. An [`Ordering`] is returned, indicating whether the assigned value is
96 /// less than, equal to, or greater than the exact value.
97 ///
98 /// See the [`DivRound`] documentation for details.
99 ///
100 /// # Worst-case complexity
101 /// Constant time and additional memory.
102 ///
103 /// # Panics
104 /// Panics if `other` is zero, or if `rm` is `Exact` but `self` is not divisible by
105 /// `other`.
106 ///
107 /// # Examples
108 /// See [here](super::div_round#div_round_assign).
109 #[inline]
110 fn div_round_assign(&mut self, other: $t, rm: RoundingMode) -> Ordering {
111 let o;
112 (*self, o) = self.div_round(other, rm);
113 o
114 }
115 }
116 };
117}
118apply_to_unsigneds!(impl_div_round_unsigned);
119
120fn div_round_signed<
121 U: PrimitiveUnsigned,
122 S: ExactFrom<U> + PrimitiveSigned + UnsignedAbs<Output = U> + WrappingFrom<U>,
123>(
124 x: S,
125 other: S,
126 rm: RoundingMode,
127) -> (S, Ordering) {
128 if (x >= S::ZERO) == (other >= S::ZERO) {
129 let (q, o) = x.unsigned_abs().div_round(other.unsigned_abs(), rm);
130 (S::exact_from(q), o)
131 } else {
132 // Has to be wrapping so that (self, other) == (T::MIN, 1) works
133 let (q, o) = x.unsigned_abs().div_round(other.unsigned_abs(), -rm);
134 (S::wrapping_from(q).wrapping_neg(), o.reverse())
135 }
136}
137
138macro_rules! impl_div_round_signed {
139 ($t:ident) => {
140 impl DivRound<$t> for $t {
141 type Output = $t;
142
143 /// Divides a value by another value and rounds according to a specified rounding mode.
144 /// An [`Ordering`] is also returned, indicating whether the returned value is less
145 /// than, equal to, or greater than the exact value.
146 ///
147 /// Let $q = \frac{x}{y}$, and let $g$ be the function that just returns the first
148 /// element of the pair, without the [`Ordering`]:
149 ///
150 /// $$
151 /// g(x, y, \mathrm{Down}) = \operatorname{sgn}(q) \lfloor |q| \rfloor.
152 /// $$
153 ///
154 /// $$
155 /// g(x, y, \mathrm{Up}) = \operatorname{sgn}(q) \lceil |q| \rceil.
156 /// $$
157 ///
158 /// $$
159 /// g(x, y, \mathrm{Floor}) = \lfloor q \rfloor.
160 /// $$
161 ///
162 /// $$
163 /// g(x, y, \mathrm{Ceiling}) = \lceil q \rceil.
164 /// $$
165 ///
166 /// $$
167 /// g(x, y, \mathrm{Nearest}) = \begin{cases}
168 /// \lfloor q \rfloor & \text{if} \\quad q - \lfloor q \rfloor < \frac{1}{2}, \\\\
169 /// \lceil q \rceil & q - \lfloor q \rfloor > \frac{1}{2}, \\\\
170 /// \lfloor q \rfloor &
171 /// \text{if} \\quad q - \lfloor q \rfloor = \frac{1}{2} \\ \text{and}
172 /// \\ \lfloor q \rfloor \\ \text{is even}, \\\\
173 /// \lceil q \rceil &
174 /// \text{if} \\quad q - \lfloor q \rfloor = \frac{1}{2} \\ \text{and}
175 /// \\ \lfloor q \rfloor \\ \text{is odd.}
176 /// \end{cases}
177 /// $$
178 ///
179 /// $g(x, y, \mathrm{Exact}) = q$, but panics if $q \notin \Z$.
180 ///
181 /// Then
182 ///
183 /// $f(x, y, r) = (g(x, y, r), \operatorname{cmp}(g(x, y, r), q))$.
184 ///
185 /// # Worst-case complexity
186 /// Constant time and additional memory.
187 ///
188 /// # Panics
189 /// Panics if `other` is zero, if `self` is `Self::MIN` and `other` is `-1`, or if `rm`
190 /// is `Exact` but `self` is not divisible by `other`.
191 ///
192 /// # Examples
193 /// See [here](super::div_round#div_round).
194 fn div_round(self, other: $t, rm: RoundingMode) -> ($t, Ordering) {
195 div_round_signed(self, other, rm)
196 }
197 }
198
199 impl DivRoundAssign<$t> for $t {
200 /// Divides a value by another value in place and rounds according to a specified
201 /// rounding mode. An [`Ordering`] is returned, indicating whether the assigned value is
202 /// less than, equal to, or greater than the exact value.
203 ///
204 /// See the [`DivRound`] documentation for details.
205 ///
206 /// # Worst-case complexity
207 /// Constant time and additional memory.
208 ///
209 /// # Panics
210 /// Panics if `other` is zero, if `self` is `Self::MIN` and `other` is `-1`, or if `rm`
211 /// is `Exact` but `self` is not divisible by `other`.
212 ///
213 /// # Examples
214 /// See [here](super::div_round#div_round_assign).
215 #[inline]
216 fn div_round_assign(&mut self, other: $t, rm: RoundingMode) -> Ordering {
217 let o;
218 (*self, o) = self.div_round(other, rm);
219 o
220 }
221 }
222 };
223}
224apply_to_signeds!(impl_div_round_signed);