qfall_math/integer/z/
lcm.rs

1// Copyright © 2023 Niklas Siemer
2//
3// This file is part of qFALL-math.
4//
5// qFALL-math is free software: you can redistribute it and/or modify it under
6// the terms of the Mozilla Public License Version 2.0 as published by the
7// Mozilla Foundation. See <https://mozilla.org/en-US/MPL/2.0/>.
8
9//! This module contains the implementation of the [`Lcm`] trait for [`Z`].
10
11use super::Z;
12use crate::traits::Lcm;
13use flint_sys::fmpz::fmpz_lcm;
14
15impl<Integer: Into<Z>> Lcm<Integer> for Z {
16    type Output = Z;
17
18    /// Outputs the least common multiple (lcm) of the two given values
19    /// with `lcm(a, 0) = 0`.
20    ///
21    /// Parameters:
22    /// - `other`: specifies one of the values of which the `lcm` is computed
23    ///
24    /// Returns the least common multiple of `self` and `other` as
25    /// a new [`Z`] instance.
26    ///
27    /// # Examples
28    /// ```
29    /// use qfall_math::integer::Z;
30    /// use qfall_math::traits::*;
31    ///
32    /// let val_1 = Z::from(10);
33    /// let val_2 = Z::from(15);
34    ///
35    /// let lcm = val_1.lcm(&val_2);
36    ///
37    /// assert_eq!(Z::from(30), lcm);
38    /// ```
39    fn lcm(&self, other: Integer) -> Self::Output {
40        let other = other.into();
41        let mut out = Z::ZERO;
42        unsafe { fmpz_lcm(&mut out.value, &self.value, &other.value) };
43        out
44    }
45}
46
47#[cfg(test)]
48mod test_lcm {
49    use super::{Lcm, Z};
50
51    /// Ensures that the lcm is correctly computed for small [`Z`] instances
52    /// and ensures properties: `lcm(a, b) == lcm(b, a)` and `lcm(a, a) == a`
53    #[test]
54    fn small() {
55        let pos_1 = Z::from(10);
56        let pos_2 = Z::from(15);
57        let neg_1 = Z::from(-10);
58        let neg_2 = Z::from(-15);
59
60        let lcm_pos_1 = pos_1.lcm(&pos_2);
61        let lcm_pos_2 = pos_2.lcm(&pos_1);
62        let lcm_pos_eq = pos_1.lcm(&pos_1);
63        let lcm_mix_1 = pos_1.lcm(&neg_1);
64        let lcm_mix_2 = neg_2.lcm(&pos_1);
65        let lcm_neg_1 = neg_1.lcm(&neg_2);
66        let lcm_neg_2 = neg_2.lcm(&neg_1);
67        let lcm_neg_eq = neg_2.lcm(&neg_2);
68
69        assert_eq!(Z::from(30), lcm_pos_1);
70        assert_eq!(Z::from(30), lcm_pos_2);
71        assert_eq!(Z::from(10), lcm_pos_eq);
72        assert_eq!(Z::from(10), lcm_mix_1);
73        assert_eq!(Z::from(30), lcm_mix_2);
74        assert_eq!(Z::from(30), lcm_neg_1);
75        assert_eq!(Z::from(30), lcm_neg_2);
76        assert_eq!(Z::from(15), lcm_neg_eq);
77    }
78
79    /// Ensures that the lcm is correctly computed for small [`Z`] instances
80    /// and ensures properties: `lcm(a, b) == lcm(b, a)`, `lcm(a, a) == a`, and
81    /// `lcm(a, 0) == 0`
82    #[test]
83    fn large() {
84        let pos = Z::from(i64::MAX);
85        let zero = Z::ZERO;
86        let neg = Z::from(i64::MIN);
87        let abs_neg = Z::MINUS_ONE * &neg;
88
89        let lcm_1 = pos.lcm(&zero);
90        let lcm_2 = pos.lcm(&pos);
91        let lcm_3 = neg.lcm(&zero);
92        let lcm_4 = neg.lcm(&neg);
93        let lcm_5 = pos.lcm(&neg);
94
95        assert_eq!(zero, lcm_1);
96        assert_eq!(pos, lcm_2);
97        assert_eq!(zero, lcm_3);
98        assert_eq!(abs_neg, lcm_4);
99        assert_eq!(pos * abs_neg, lcm_5);
100    }
101
102    /// Ensure that lcm trait/ implementation is available for other types
103    #[test]
104    fn availability() {
105        let z = Z::ONE;
106
107        let _ = z.lcm(z.clone());
108        let _ = z.lcm(4_u8);
109        let _ = z.lcm(4_u16);
110        let _ = z.lcm(4_u32);
111        let _ = z.lcm(4_u64);
112        let _ = z.lcm(4_i8);
113        let _ = z.lcm(4_i16);
114        let _ = z.lcm(4_i32);
115        let _ = z.lcm(4_i64);
116    }
117}