rust_fixed_point_decimal/binops/
mul.rs

1// ---------------------------------------------------------------------------
2// Copyright:   (c) 2021 ff. Michael Amrhein (michael@adrhinum.de)
3// License:     This program is part of a larger application. For license
4//              details please read the file LICENSE.TXT provided together
5//              with the application.
6// ---------------------------------------------------------------------------
7// $Source: src/binops/mul.rs $
8// $Revision: 2021-10-24T22:36:24+02:00 $
9
10use std::ops::{Mul, MulAssign};
11
12use crate::{
13    binops::const_sum_u8,
14    prec_constraints::{PrecLimitCheck, True},
15    Decimal, MAX_PREC,
16};
17
18// The trait One requires Mul<Self, Output = Self>. This is only satisfied for
19// Decimal<0>. All other Decimal<P> are Mul<Self, Output = Decimal<P+P>.
20// So, the corresponding functions are implemented separately.
21
22impl<const P: u8> Decimal<P>
23where
24    // PrecLimitCheck<{ 0 < P }>: True,
25    PrecLimitCheck<{ P <= MAX_PREC }>: True,
26{
27    /// Returns the multiplicative identity element of Self, Self::ONE.
28    #[inline(always)]
29    pub fn one() -> Self {
30        Self::ONE
31    }
32
33    /// Returns true if self is equal to the multiplicative identity.
34    #[inline(always)]
35    pub fn is_one(&self) -> bool {
36        self.eq_one()
37    }
38}
39
40#[cfg(test)]
41mod one_tests {
42    use super::*;
43
44    #[test]
45    fn test_one() {
46        assert!(Decimal::<0>::is_one(&Decimal::<0>::one()));
47        assert!(Decimal::<1>::is_one(&Decimal::<1>::one()));
48        assert!(Decimal::<2>::is_one(&Decimal::<2>::one()));
49        assert!(Decimal::<3>::is_one(&Decimal::<3>::one()));
50        assert!(Decimal::<4>::is_one(&Decimal::<4>::one()));
51        assert!(Decimal::<5>::is_one(&Decimal::<5>::one()));
52        assert!(Decimal::<6>::is_one(&Decimal::<6>::one()));
53        assert!(Decimal::<7>::is_one(&Decimal::<7>::one()));
54        assert!(Decimal::<8>::is_one(&Decimal::<8>::one()));
55        assert!(Decimal::<9>::is_one(&Decimal::<9>::one()));
56    }
57}
58
59impl<const P: u8, const Q: u8> Mul<Decimal<Q>> for Decimal<P>
60where
61    PrecLimitCheck<{ P <= MAX_PREC }>: True,
62    PrecLimitCheck<{ Q <= MAX_PREC }>: True,
63    PrecLimitCheck<{ (const_sum_u8(P, Q)) <= MAX_PREC }>: True,
64{
65    type Output = Decimal<{ const_sum_u8(P, Q) }>;
66
67    #[inline(always)]
68    fn mul(self, other: Decimal<Q>) -> Self::Output {
69        Self::Output {
70            coeff: self.coeff * other.coeff,
71        }
72    }
73}
74
75forward_ref_binop!(impl Mul, mul);
76
77#[cfg(test)]
78mod mul_decimal_tests {
79    use super::*;
80
81    #[test]
82    fn test_mul_same_prec() {
83        let x = Decimal::<4>::new_raw(1234567890);
84        let y = x * x;
85        assert_eq!(y.precision(), 8);
86        assert_eq!(y.coeff, x.coeff * x.coeff);
87        let z = x * Decimal::<4>::NEG_ONE;
88        assert_eq!(z.precision(), 8);
89        assert_eq!(z.coeff, -x.coeff * 10000);
90    }
91
92    #[test]
93    fn test_mul_different_prec() {
94        let x = Decimal::<5>::new_raw(1234567890);
95        let y = Decimal::<1>::new_raw(890);
96        let z = x * y;
97        assert_eq!(z.precision(), 6);
98        assert_eq!(z.coeff, x.coeff * y.coeff);
99        let z = y * x;
100        assert_eq!(z.precision(), 6);
101        assert_eq!(z.coeff, x.coeff * y.coeff);
102        let z = x * Decimal::<3>::NEG_ONE;
103        assert_eq!(z.precision(), 8);
104        assert_eq!(z.coeff, -x.coeff * 1000);
105    }
106
107    #[test]
108    #[should_panic]
109    fn test_mul_pos_overflow() {
110        let x = Decimal::<4>::new_raw(i128::MAX / 4 + 1);
111        let _y = x * Decimal::<4>::TWO;
112    }
113
114    #[test]
115    #[should_panic]
116    fn test_mul_neg_overflow() {
117        let x = Decimal::<2>::new_raw(i128::MIN);
118        let _y = x * Decimal::<2>::NEG_ONE;
119    }
120
121    #[test]
122    fn test_mul_ref() {
123        let x = Decimal::<3>::new_raw(12345);
124        let y = Decimal::<1>::new_raw(12345);
125        let z = x * y;
126        assert_eq!(z.coeff, (&x * y).coeff);
127        assert_eq!(z.coeff, (x * &y).coeff);
128        assert_eq!(z.coeff, (&x * &y).coeff);
129    }
130}
131
132macro_rules! impl_mul_decimal_and_int {
133    () => {
134        impl_mul_decimal_and_int!(u8, i8, u16, i16, u32, i32, u64, i64, i128);
135    };
136    ($($t:ty),*) => {
137        $(
138        impl<const P: u8> Mul<$t> for Decimal<P>
139        where
140            PrecLimitCheck<{ P <= MAX_PREC }>: True,
141        {
142            type Output = Decimal<P>;
143
144            #[inline(always)]
145            fn mul(self, other: $t) -> Self::Output {
146                Self::Output{
147                    coeff: self.coeff * other as i128
148                }
149            }
150        }
151
152        impl<const P: u8> Mul<Decimal<P>> for $t
153        where
154            PrecLimitCheck<{ P <= MAX_PREC }>: True,
155        {
156            type Output = Decimal<P>;
157
158            #[inline(always)]
159            fn mul(self, other: Decimal<P>) -> Self::Output {
160                Self::Output{
161                    coeff: self as i128 * other.coeff
162                }
163            }
164        }
165        )*
166    }
167}
168
169impl_mul_decimal_and_int!();
170forward_ref_binop_decimal_int!(impl Mul, mul);
171
172#[cfg(test)]
173#[allow(clippy::neg_multiply)]
174mod mul_integer_tests {
175    use super::*;
176
177    macro_rules! gen_mul_integer_tests {
178        ($func:ident, $t:ty, $p:expr, $coeff:expr) => {
179            #[test]
180            fn $func() {
181                let d = Decimal::<$p>::new_raw($coeff);
182                let i = <$t>::MAX;
183                let r = d * i;
184                assert_eq!(r.precision(), d.precision());
185                assert_eq!(r.coeff, i as i128 * $coeff);
186                assert_eq!(r.coeff, (&d * i).coeff);
187                assert_eq!(r.coeff, (d * &i).coeff);
188                assert_eq!(r.coeff, (&d * &i).coeff);
189                let z = i * d;
190                assert_eq!(z.precision(), r.precision());
191                assert_eq!(z.coeff, r.coeff);
192                assert_eq!(z.coeff, (&i * d).coeff);
193                assert_eq!(z.coeff, (i * &d).coeff);
194                assert_eq!(z.coeff, (&i * &d).coeff);
195            }
196        };
197    }
198
199    gen_mul_integer_tests!(test_mul_u8, u8, 2, -1);
200    gen_mul_integer_tests!(test_mul_i8, i8, 0, 123);
201    gen_mul_integer_tests!(test_mul_u16, u16, 4, 11);
202    gen_mul_integer_tests!(test_mul_i16, i16, 4, 1234567);
203    gen_mul_integer_tests!(test_mul_u32, u32, 1, 0);
204    gen_mul_integer_tests!(test_mul_i32, i32, 9, -1234);
205    gen_mul_integer_tests!(test_mul_u64, u64, 3, 321);
206    gen_mul_integer_tests!(test_mul_i64, i64, 7, -12345678901234567890);
207
208    #[test]
209    fn test_mul_i128() {
210        let coeff = 748_i128;
211        let d = Decimal::<2>::new_raw(coeff);
212        let i = 12345_i128;
213        let r = d * i;
214        assert_eq!(r.precision(), d.precision());
215        assert_eq!(r.coeff, i * coeff);
216        assert_eq!(r.coeff, (&d * i).coeff);
217        assert_eq!(r.coeff, (d * &i).coeff);
218        assert_eq!(r.coeff, (&d * &i).coeff);
219        let z = i * d;
220        assert_eq!(z.precision(), r.precision());
221        assert_eq!(z.coeff, r.coeff);
222        assert_eq!(z.coeff, (&i * d).coeff);
223        assert_eq!(z.coeff, (i * &d).coeff);
224        assert_eq!(z.coeff, (&i * &d).coeff);
225    }
226}
227
228forward_op_assign!(impl MulAssign, mul_assign, Mul, mul);
229
230#[cfg(test)]
231mod mul_assign_tests {
232    use super::*;
233
234    #[test]
235    fn test_mul_assign_decimal() {
236        let mut x = Decimal::<2>::new_raw(123456);
237        let y = Decimal::<0>::TWO;
238        x *= y;
239        assert_eq!(x.coeff, 123456_i128 * 2);
240        let z = &y;
241        x *= z;
242        assert_eq!(x.coeff, 123456_i128 * 4);
243    }
244
245    #[test]
246    fn test_mul_assign_int() {
247        let mut x = Decimal::<2>::new_raw(123456);
248        x *= 2_i8;
249        assert_eq!(x.coeff, 123456_i128 * 2);
250        x *= &-1234567890_i128;
251        assert_eq!(x.coeff, 123456_i128 * 2 * -1234567890_i128);
252    }
253
254    #[test]
255    #[should_panic]
256    fn test_mul_assign_pos_overflow() {
257        let mut x = Decimal::<4>::new_raw(i128::MAX / 2 + 1);
258        x *= 2;
259    }
260
261    #[test]
262    #[should_panic]
263    fn test_mul_assign_neg_overflow() {
264        let mut x = Decimal::<2>::new_raw(i128::MIN / 5 - 1);
265        x *= 5;
266    }
267}