malachite_nz/natural/arithmetic/lcm.rs
1// Copyright © 2026 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::natural::Natural;
10use malachite_base::num::arithmetic::traits::{DivExact, DivExactAssign, Gcd, Lcm, LcmAssign};
11use malachite_base::num::basic::traits::Zero;
12
13impl Lcm<Self> for Natural {
14 type Output = Self;
15
16 /// Computes the LCM (least common multiple) of two [`Natural`]s, taking both by value.
17 ///
18 /// $$
19 /// f(x, y) = \operatorname{lcm}(x, y).
20 /// $$
21 ///
22 /// # Worst-case complexity
23 /// $T(n) = O(n (\log n)^2 \log\log n)$
24 ///
25 /// $M(n) = O(n \log n)$
26 ///
27 /// where $T$ is time, $M$ is additional memory, and $n$ is `max(self.significant_bits(),
28 /// other.significant_bits())`.
29 ///
30 /// # Examples
31 /// ```
32 /// use malachite_base::num::arithmetic::traits::Lcm;
33 /// use malachite_nz::natural::Natural;
34 ///
35 /// assert_eq!(Natural::from(3u32).lcm(Natural::from(5u32)), 15);
36 /// assert_eq!(Natural::from(12u32).lcm(Natural::from(90u32)), 180);
37 /// ```
38 fn lcm(mut self, other: Self) -> Self {
39 self.lcm_assign(other);
40 self
41 }
42}
43
44impl<'a> Lcm<&'a Self> for Natural {
45 type Output = Self;
46
47 /// Computes the LCM (least common multiple) of two [`Natural`]s, taking the first by value and
48 /// the second by reference.
49 ///
50 /// $$
51 /// f(x, y) = \operatorname{lcm}(x, y).
52 /// $$
53 ///
54 /// # Worst-case complexity
55 /// $T(n) = O(n (\log n)^2 \log\log n)$
56 ///
57 /// $M(n) = O(n \log n)$
58 ///
59 /// where $T$ is time, $M$ is additional memory, and $n$ is `max(self.significant_bits(),
60 /// other.significant_bits())`.
61 ///
62 /// # Examples
63 /// ```
64 /// use malachite_base::num::arithmetic::traits::Lcm;
65 /// use malachite_nz::natural::Natural;
66 ///
67 /// assert_eq!(Natural::from(3u32).lcm(&Natural::from(5u32)), 15);
68 /// assert_eq!(Natural::from(12u32).lcm(&Natural::from(90u32)), 180);
69 /// ```
70 #[inline]
71 fn lcm(mut self, other: &'a Self) -> Self {
72 self.lcm_assign(other);
73 self
74 }
75}
76
77impl Lcm<Natural> for &Natural {
78 type Output = Natural;
79
80 /// Computes the LCM (least common multiple) of two [`Natural`]s, taking the first by reference
81 /// and the second by value.
82 ///
83 /// $$
84 /// f(x, y) = \operatorname{lcm}(x, y).
85 /// $$
86 ///
87 /// # Worst-case complexity
88 /// $T(n) = O(n (\log n)^2 \log\log n)$
89 ///
90 /// $M(n) = O(n \log n)$
91 ///
92 /// where $T$ is time, $M$ is additional memory, and $n$ is `max(self.significant_bits(),
93 /// other.significant_bits())`.
94 ///
95 /// # Examples
96 /// ```
97 /// use malachite_base::num::arithmetic::traits::Lcm;
98 /// use malachite_nz::natural::Natural;
99 ///
100 /// assert_eq!((&Natural::from(3u32)).lcm(Natural::from(5u32)), 15);
101 /// assert_eq!((&Natural::from(12u32)).lcm(Natural::from(90u32)), 180);
102 /// ```
103 #[inline]
104 fn lcm(self, mut other: Natural) -> Natural {
105 other.lcm_assign(self);
106 other
107 }
108}
109
110impl Lcm<&Natural> for &Natural {
111 type Output = Natural;
112
113 /// Computes the LCM (least common multiple) of two [`Natural`]s, taking both by reference.
114 ///
115 /// $$
116 /// f(x, y) = \operatorname{lcm}(x, y).
117 /// $$
118 ///
119 /// # Worst-case complexity
120 /// $T(n) = O(n (\log n)^2 \log\log n)$
121 ///
122 /// $M(n) = O(n \log n)$
123 ///
124 /// where $T$ is time, $M$ is additional memory, and $n$ is `max(self.significant_bits(),
125 /// other.significant_bits())`.
126 ///
127 /// # Examples
128 /// ```
129 /// use malachite_base::num::arithmetic::traits::Lcm;
130 /// use malachite_nz::natural::Natural;
131 ///
132 /// assert_eq!((&Natural::from(3u32)).lcm(&Natural::from(5u32)), 15);
133 /// assert_eq!((&Natural::from(12u32)).lcm(&Natural::from(90u32)), 180);
134 /// ```
135 #[inline]
136 fn lcm(self, other: &Natural) -> Natural {
137 if *self == 0 || *other == 0 {
138 return Natural::ZERO;
139 }
140 let gcd = self.gcd(other);
141 // Division is slower than multiplication, so we choose the arguments to div_exact to be as
142 // small as possible. This also allows the special case of lcm(x, y) when x is a multiple of
143 // y to be quickly reduced to x.
144 if self >= other {
145 self * other.div_exact(gcd)
146 } else {
147 other * self.div_exact(gcd)
148 }
149 }
150}
151
152impl LcmAssign<Self> for Natural {
153 /// Replaces a [`Natural`] by its LCM (least common multiple) with another [`Natural`], taking
154 /// the [`Natural`] on the right-hand side by value.
155 ///
156 /// $$
157 /// x \gets \operatorname{lcm}(x, y).
158 /// $$
159 ///
160 /// # Worst-case complexity
161 /// $T(n) = O(n (\log n)^2 \log\log n)$
162 ///
163 /// $M(n) = O(n \log n)$
164 ///
165 /// where $T$ is time, $M$ is additional memory, and $n$ is `max(self.significant_bits(),
166 /// other.significant_bits())`.
167 ///
168 /// # Examples
169 /// ```
170 /// use malachite_base::num::arithmetic::traits::LcmAssign;
171 /// use malachite_nz::natural::Natural;
172 ///
173 /// let mut x = Natural::from(3u32);
174 /// x.lcm_assign(Natural::from(5u32));
175 /// assert_eq!(x, 15);
176 ///
177 /// let mut x = Natural::from(12u32);
178 /// x.lcm_assign(Natural::from(90u32));
179 /// assert_eq!(x, 180);
180 /// ```
181 #[inline]
182 fn lcm_assign(&mut self, mut other: Self) {
183 if *self == 0 {
184 return;
185 } else if other == 0 {
186 *self = Self::ZERO;
187 return;
188 }
189 let gcd = (&*self).gcd(&other);
190 if *self >= other {
191 other.div_exact_assign(gcd);
192 } else {
193 self.div_exact_assign(gcd);
194 }
195 *self *= other;
196 }
197}
198
199impl<'a> LcmAssign<&'a Self> for Natural {
200 /// Replaces a [`Natural`] by its LCM (least common multiple) with another [`Natural`], taking
201 /// the [`Natural`] on the right-hand side by reference.
202 ///
203 /// $$
204 /// x \gets \operatorname{lcm}(x, y).
205 /// $$
206 ///
207 /// # Worst-case complexity
208 /// $T(n) = O(n (\log n)^2 \log\log n)$
209 ///
210 /// $M(n) = O(n \log n)$
211 ///
212 /// where $T$ is time, $M$ is additional memory, and $n$ is `max(self.significant_bits(),
213 /// other.significant_bits())`.
214 ///
215 /// # Examples
216 /// ```
217 /// use malachite_base::num::arithmetic::traits::LcmAssign;
218 /// use malachite_nz::natural::Natural;
219 ///
220 /// let mut x = Natural::from(3u32);
221 /// x.lcm_assign(&Natural::from(5u32));
222 /// assert_eq!(x, 15);
223 ///
224 /// let mut x = Natural::from(12u32);
225 /// x.lcm_assign(&Natural::from(90u32));
226 /// assert_eq!(x, 180);
227 /// ```
228 #[inline]
229 fn lcm_assign(&mut self, other: &'a Self) {
230 if *self == 0 {
231 return;
232 } else if *other == 0 {
233 *self = Self::ZERO;
234 return;
235 }
236 self.div_exact_assign((&*self).gcd(other));
237 *self *= other;
238 }
239}