malachite_base/num/arithmetic/
x_mul_y_to_zz.rs

1// Copyright © 2025 Mikhail Hogrefe
2//
3// Uses code adopted from the GNU MP Library.
4//
5//      Copyright © 1991-1994, 1996, 1997, 1999-2005, 2007-2009, 2011-2020 Free Software
6//      Foundation, Inc.
7//
8// This file is part of Malachite.
9//
10// Malachite is free software: you can redistribute it and/or modify it under the terms of the GNU
11// Lesser General Public License (LGPL) as published by the Free Software Foundation; either version
12// 3 of the License, or (at your option) any later version. See <https://www.gnu.org/licenses/>.
13
14use crate::num::arithmetic::traits::XMulYToZZ;
15use crate::num::basic::integers::USIZE_IS_U32;
16use crate::num::basic::unsigneds::PrimitiveUnsigned;
17use crate::num::conversion::half::{wide_join_halves, wide_split_in_half, wide_upper_half};
18use crate::num::conversion::traits::{HasHalf, SplitInHalf, WrappingFrom};
19
20fn implicit_x_mul_y_to_zz<T, DT: From<T> + HasHalf<Half = T> + PrimitiveUnsigned + SplitInHalf>(
21    x: T,
22    y: T,
23) -> (T, T) {
24    (DT::from(x) * DT::from(y)).split_in_half()
25}
26
27pub_test! {explicit_x_mul_y_to_zz<T: PrimitiveUnsigned>(x: T, y: T) -> (T, T) {
28    let (x_1, x_0) = wide_split_in_half(x);
29    let (y_1, y_0) = wide_split_in_half(y);
30    let x_0_y_0 = x_0 * y_0;
31    let mut x_0_y_1 = x_0 * y_1;
32    let x_1_y_0 = x_1 * y_0;
33    let mut x_1_y_1 = x_1 * y_1;
34    let (x_0_y_0_1, x_0_y_0_0) = wide_split_in_half(x_0_y_0);
35    x_0_y_1.wrapping_add_assign(x_0_y_0_1);
36    if x_0_y_1.overflowing_add_assign(x_1_y_0) {
37        x_1_y_1.wrapping_add_assign(T::power_of_2(T::WIDTH >> 1));
38    }
39    let z_1 = x_1_y_1.wrapping_add(wide_upper_half(x_0_y_1));
40    let z_0 = wide_join_halves(x_0_y_1, x_0_y_0_0);
41    (z_1, z_0)
42}}
43
44macro_rules! implicit_x_mul_y_to_zz {
45    ($t:ident, $dt:ident) => {
46        impl XMulYToZZ for $t {
47            /// Multiplies two numbers, returning the product as a pair of `Self` values.
48            ///
49            /// The more significant value always comes first.
50            ///
51            /// $$
52            /// f(x, y) = (z_1, z_0),
53            /// $$
54            /// where $W$ is `Self::WIDTH`,
55            ///
56            /// $x, y, z_1, z_0 < 2^W$, and
57            /// $$
58            /// xy = 2^Wz_1 + z_0.
59            /// $$
60            ///
61            /// # Worst-case complexity
62            /// Constant time and additional memory.
63            ///
64            /// # Examples
65            /// See [here](super::x_mul_y_to_zz#x_mul_y_to_zz).
66            ///
67            /// This is equivalent to `umul_ppmm` from `longlong.h`, GMP 6.2.1, where `(w1, w0)` is
68            /// returned.
69            #[inline]
70            fn x_mul_y_to_zz(x: $t, y: $t) -> ($t, $t) {
71                implicit_x_mul_y_to_zz::<$t, $dt>(x, y)
72            }
73        }
74    };
75}
76
77implicit_x_mul_y_to_zz!(u8, u16);
78implicit_x_mul_y_to_zz!(u16, u32);
79implicit_x_mul_y_to_zz!(u32, u64);
80implicit_x_mul_y_to_zz!(u64, u128);
81
82impl XMulYToZZ for usize {
83    /// Multiplies two numbers, returning the product as a pair of [`usize`] values.
84    ///
85    /// The more significant value always comes first.
86    ///
87    /// $$
88    /// f(x, y) = (z_1, z_0),
89    /// $$
90    /// where $W$ is `Self::WIDTH`,
91    ///
92    /// $x, y, z_1, z_0 < 2^W$, and
93    /// $$
94    /// xy = 2^Wz_1 + z_0.
95    /// $$
96    ///
97    /// # Worst-case complexity
98    /// Constant time and additional memory.
99    ///
100    /// # Examples
101    /// See [here](super::x_mul_y_to_zz#x_mul_y_to_zz).
102    ///
103    /// This is equivalent to `umul_ppmm` from `longlong.h`, GMP 6.2.1, where `(w1, w0)` is
104    /// returned.
105    fn x_mul_y_to_zz(x: usize, y: usize) -> (usize, usize) {
106        if USIZE_IS_U32 {
107            let (z_1, z_0) = u32::x_mul_y_to_zz(u32::wrapping_from(x), u32::wrapping_from(y));
108            (usize::wrapping_from(z_1), usize::wrapping_from(z_0))
109        } else {
110            let (z_1, z_0) = u64::x_mul_y_to_zz(u64::wrapping_from(x), u64::wrapping_from(y));
111            (usize::wrapping_from(z_1), usize::wrapping_from(z_0))
112        }
113    }
114}
115
116impl XMulYToZZ for u128 {
117    /// Multiplies two numbers, returning the product as a pair of [`u128`] values.
118    ///
119    /// The more significant value always comes first.
120    ///
121    /// $$
122    /// f(x, y) = (z_1, z_0),
123    /// $$
124    /// where $W$ is `Self::WIDTH`,
125    ///
126    /// $x, y, z_1, z_0 < 2^W$, and
127    /// $$
128    /// xy = 2^Wz_1 + z_0.
129    /// $$
130    ///
131    /// # Worst-case complexity
132    /// Constant time and additional memory.
133    ///
134    /// # Examples
135    /// See [here](super::x_mul_y_to_zz#x_mul_y_to_zz).
136    ///
137    /// This is equivalent to `umul_ppmm` from `longlong.h`, GMP 6.2.1, where `(w1, w0)` is
138    /// returned.
139    #[inline]
140    fn x_mul_y_to_zz(x: u128, y: u128) -> (u128, u128) {
141        explicit_x_mul_y_to_zz(x, y)
142    }
143}