1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
//! mulx

/// Unsigned multiply without affecting flags.
pub trait Mulx: crate::marker::Sized {
    /// Unsigned multiply without affecting flags.
    ///
    /// Unsigned multiplication of `x` with `y` returning a pair `(lo, hi)`
    /// with the low half and the high half of the result.
    ///
    /// # Instructions
    ///
    /// - [`MULX`](http://www.felixcloutier.com/x86/MULX.html):
    ///   - Description: Unsigned multiply without affecting flags.
    ///   - Architecture: x86.
    ///   - Instruction set: BMI2.
    ///   - Registers: 32/64 bit.
    ///
    /// # Example
    ///
    /// ```
    /// # use bitintr::*;
    /// { // 8-bit
    ///   let a: u8 = 128;
    ///   let b: u8 = 128;
    ///   let (lo, hi): (u8, u8) = a.mulx(b);
    ///   // result = 16384 = 0b0100_0000_0000_0000u16
    ///   //                    ^~hi~~~~~ ^~lo~~~~~
    ///   assert_eq!(lo, 0b0000_0000);
    ///   assert_eq!(hi, 0b0100_0000);
    /// }
    /// { // 16-bit
    ///   let a: u16 = 65_500;
    ///   let b: u16 = 65_500;
    ///   let (lo, hi): (u16, u16)  = a.mulx(b);
    ///   // result = 4290250000 = 0b1111_1111_1011_1000_0000_0101_0001_0000u32
    ///   //                         ^~hi~~~~~~~~~~~~~~~ ^~lo~~~~~~~~~~~~~~~
    ///   assert_eq!(lo, 0b0000_0101_0001_0000);
    ///   assert_eq!(hi, 0b1111_1111_1011_1000);
    /// }
    /// { // 32-bit
    ///   let a: u32 = 4_294_967_200;
    ///   let b: u32 = 2;
    ///   let (lo, hi): (u32, u32)  = a.mulx(b);
    ///   // result = 8589934400
    ///   //        = 0b0001_1111_1111_1111_1111_1111_1111_0100_0000u64
    ///   //            ^~hi ^~lo~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    ///   assert_eq!(lo, 0b1111_1111_1111_1111_1111_1111_0100_0000u32);
    ///   assert_eq!(hi, 0b0001u32);
    /// }
    /// { // 64-bit
    ///   let a: u64 = 9_223_372_036_854_775_800;
    ///   let b: u64 = 100;
    ///   let (lo, hi): (u64, u64)  = a.mulx(b);
    ///   // result = 922337203685477580000
    ///   //        = 0b00110001_11111111_11111111_11111111_11111111_11111111_11111111_11111100_11100000u128
    ///   //            ^~hi~~~~ ^~lo~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    ///   assert_eq!(lo, 0b11111111_11111111_11111111_11111111_11111111_11111111_11111100_11100000u64);
    ///   assert_eq!(hi, 0b00110001u64);
    /// }
    /// { // 8-bit
    ///   let a: i8 = 128u8 as i8;
    ///   let b: i8 = 128u8 as i8;
    ///   let (lo, hi): (i8, i8) = a.mulx(b);
    ///   // result = _____ = 0b0100_0000_0000_0000u16
    ///   //                    ^~hi~~~~~ ^~lo~~~~~
    ///   assert_eq!(lo, 0b0000_0000);
    ///   assert_eq!(hi, 0b0100_0000);
    /// }
    /// { // 16-bit
    ///   let a: i16 = 65_500u16 as i16;
    ///   let b: i16 = 65_500u16 as i16;
    ///   let (lo, hi): (i16, i16)  = a.mulx(b);
    ///   // result = 4290250000 = 0b1111_1111_1011_1000_0000_0101_0001_0000u32
    ///   //                         ^~hi~~~~~~~~~~~~~~~ ^~lo~~~~~~~~~~~~~~~
    ///   assert_eq!(lo, 0b0000_0101_0001_0000u16 as i16);
    ///   assert_eq!(hi, 0b1111_1111_1011_1000u16 as i16);
    /// }
    /// ```
    fn mulx(self, y: Self) -> (Self, Self);
}

macro_rules! impl_umulx {
    ($id:ident, $id_l:ident) => {
        #[allow(clippy::use_self)]
        impl Mulx for $id {
            #[inline]
            fn mulx(self, y: Self) -> (Self, Self) {
                const BIT_WIDTH: $id_l =
                    (crate::mem::size_of::<$id>() * 8) as $id_l;
                let x = self;
                let result: $id_l = (x as $id_l) * (y as $id_l);
                let hi = (result >> BIT_WIDTH) as Self;
                (result as Self, hi)
            }
        }
    };
}

impl_umulx!(u8, u16);
impl_umulx!(u16, u32);
impl_umulx!(u32, u64);
impl_umulx!(u64, u128);

macro_rules! impl_smulx {
    ($id:ident, $uid:ident) => {
        impl Mulx for $id {
            #[inline]
            fn mulx(self, y: Self) -> (Self, Self) {
                let ux = self as $uid;
                let uy = y as $uid;
                let (rx, ry) = ux.mulx(uy);
                (rx as _, ry as _)
            }
        }
    };
}

impl_smulx!(i8, u8);
impl_smulx!(i16, u16);
impl_smulx!(i32, u32);
impl_smulx!(i64, u64);