malachite_nz/natural/arithmetic/lcm.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::natural::Natural;
10use malachite_base::num::arithmetic::traits::{DivExact, DivExactAssign, Gcd, Lcm, LcmAssign};
11use malachite_base::num::basic::traits::Zero;
12
13impl Lcm<Natural> for Natural {
14    type Output = Natural;
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: Natural) -> Natural {
39        self.lcm_assign(other);
40        self
41    }
42}
43
44impl<'a> Lcm<&'a Natural> for Natural {
45    type Output = Natural;
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 Natural) -> Natural {
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<Natural> 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: Natural) {
183        if *self == 0 {
184            return;
185        } else if other == 0 {
186            *self = Natural::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 Natural> 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 Natural) {
230        if *self == 0 {
231            return;
232        } else if *other == 0 {
233            *self = Natural::ZERO;
234            return;
235        }
236        self.div_exact_assign((&*self).gcd(other));
237        *self *= other;
238    }
239}