Skip to main content

use_arithmetic/
lcm.rs

1use crate::gcd;
2
3/// Computes the least common multiple of two unsigned integers.
4///
5/// Returns `None` when the exact result does not fit in `u64`.
6///
7/// # Examples
8///
9/// ```rust
10/// use use_arithmetic::checked_lcm;
11///
12/// assert_eq!(checked_lcm(6, 15), Some(30));
13/// assert_eq!(checked_lcm(0, 15), Some(0));
14/// ```
15#[must_use]
16pub const fn checked_lcm(left: u64, right: u64) -> Option<u64> {
17    if left == 0 || right == 0 {
18        return Some(0);
19    }
20
21    let divisor = gcd(left, right);
22    (left / divisor).checked_mul(right)
23}
24
25/// Computes the least common multiple of two unsigned integers.
26///
27/// `lcm(a, 0)` returns `0`.
28///
29/// # Panics
30///
31/// Panics when the exact least common multiple does not fit in `u64`.
32///
33/// # Examples
34///
35/// ```rust
36/// use use_arithmetic::lcm;
37///
38/// assert_eq!(lcm(6, 15), 30);
39/// assert_eq!(lcm(0, 15), 0);
40/// ```
41#[must_use]
42pub fn lcm(left: u64, right: u64) -> u64 {
43    checked_lcm(left, right).unwrap_or_else(|| panic!("lcm overflowed u64"))
44}
45
46#[cfg(test)]
47mod tests {
48    use super::{checked_lcm, lcm};
49
50    #[test]
51    fn handles_zero_and_positive_cases() {
52        assert_eq!(checked_lcm(0, 0), Some(0));
53        assert_eq!(checked_lcm(0, 15), Some(0));
54        assert_eq!(checked_lcm(6, 15), Some(30));
55        assert_eq!(lcm(21, 6), 42);
56    }
57
58    #[test]
59    fn reports_overflow_through_checked_variant() {
60        assert_eq!(checked_lcm(u64::MAX, u64::MAX - 1), None);
61    }
62
63    #[test]
64    #[should_panic(expected = "lcm overflowed u64")]
65    fn plain_lcm_panics_on_overflow() {
66        let _ = lcm(u64::MAX, u64::MAX - 1);
67    }
68}