Skip to main content

rustymath_core/
arith.rs

1//! Arithmetic as formula objects, which `rustymath::operation::Kernel` runs through their [`Formula`](crate::facts::Formula) rows. Addition, subtraction, multiplication and negation take every float and complex type and the integer types of compact buffers, `u8`, `i16`, `i32` and `i64`. Division, reciprocals and multiples of π take every float and complex type, and squares take floats. Reciprocals also take the integer types, where they floor, as integer division of 1 does. Bitwise and logical functions take them and Booleans. A float or complex result never fails. An integer sum, difference, product or negation that doesn't fit its type fails, and holds the result wrapped to the type, as NumPy gives it. Any other integer result that the type can't hold fails.
2use crate::float::sealed::Sealed;
3
4/// x + y.
5#[derive(Clone, Copy, Debug)]
6pub struct Add;
7/// x - y.
8#[derive(Clone, Copy, Debug)]
9pub struct Subtract;
10/// x × y.
11#[derive(Clone, Copy, Debug)]
12pub struct Multiply;
13/// x ÷ y, at every float and complex type. Complex numbers divide by [`divide`](crate::divide).
14#[derive(Clone, Copy, Debug)]
15pub struct Divide;
16
17/// Division with zero divided by zero defined as zero.
18#[derive(Clone, Copy, Debug)]
19pub struct MathishDivide;
20
21#[inline]
22pub(crate) fn both_zero<T: num_traits::Zero>(x: T, y: T) -> bool { x.is_zero() & y.is_zero() }
23
24/// Bitwise AND, or logical AND of Booleans.
25#[derive(Clone, Copy, Debug)]
26pub struct BitAnd;
27/// Bitwise OR, or logical OR of Booleans.
28#[derive(Clone, Copy, Debug)]
29pub struct BitOr;
30
31/// Left shift, discarding high bits. A negative count shifts right, and a count at least the item width saturates to 0.
32#[derive(Clone, Copy, Debug)]
33pub struct LeftShift;
34/// Right shift, retaining the sign bit for signed integers. A negative count shifts left, and a count at least the item width saturates to the sign.
35#[derive(Clone, Copy, Debug)]
36pub struct RightShift;
37
38/// -x. A float or complex result never fails. An integer result fails where it doesn't fit its type, as the negation of a signed type's least value or of an unsigned byte other than 0 doesn't, and holds the result wrapped to the type.
39#[derive(Clone, Copy, Debug)]
40pub struct Negate;
41/// 1 ÷ x. A float or complex result never fails. An integer's reciprocal floors, as integer [`FloorDivide`](crate::tolerant::FloorDivide) of 1 does: 1 gives 1, 2 or more give 0, -1 or less give -1, and 0 gives the type's greatest value.
42#[derive(Clone, Copy, Debug)]
43pub struct Reciprocal;
44/// π × x, or π × (x ÷ y) of two arguments, at every float and complex type.
45#[derive(Clone, Copy, Debug)]
46pub struct PiTimes;
47
48/// Applies the Boolean function whose truth table is `table`, indexed by `2*x+y`, to every bit of `x` and `y`, the leading sign bits included. It computes the table's algebraic normal form, `a0 ^ (a1 & x) ^ (a2 & y) ^ (a3 & x & y)`, with each coefficient spread to every bit, so every table runs the same code. A `match` on the table would put 16 paths into each loop that inlines it. It takes any integer type, exact ones included.
49#[inline]
50pub fn bitwise<T>(table: u8, x: T, y: T) -> T where
51    T: Clone + std::ops::BitAnd<Output = T> + std::ops::BitOr<Output = T> + std::ops::BitXor<Output = T> + std::ops::Not<Output = T> {
52    bitwise_with(table, x.clone(), y, x.clone() | !x, |a, b| a & b, |a, b| a ^ b)
53}
54
55pub(crate) fn bitwise_with<T: Clone>(table: u8, x: T, y: T, ones: T, and: impl Fn(T, T) -> T, xor: impl Fn(T, T) -> T) -> T {
56    let zero = xor(ones.clone(), ones.clone());
57    let spread = |bit: u8| if bit & 1 == 1 { ones.clone() } else { zero.clone() };
58    let entry = |i: u8| table >> i;
59    let (a0, a1, a2, a3) = (spread(entry(0)), spread(entry(0) ^ entry(2)), spread(entry(0) ^ entry(1)), spread(entry(0) ^ entry(1) ^ entry(2) ^ entry(3)));
60    xor(xor(a0, and(x, xor(a1, and(y.clone(), a3)))), and(y, a2))
61}
62
63/// [`bitwise`] with the truth table it holds, as a function object. One argument is the second, with 0 as the first. Booleans and the signed types hold every result, because each leading sign bit takes the same entry of the table. An unsigned byte computes at `i64`, where its leading zeros can become ones, and fails where the result doesn't fit.
64///
65/// A float argument computes at `i64`, as the whole number it equals within the default tolerance (`operation::with_projected`). Any other float is DOMAIN. BPL's `ᵇ` and the Python wrapper's `bitwise_and`, `bitwise_or`, `bitwise_xor` and `bitwise_not` run its kernels.
66#[derive(Clone, Copy, Debug)]
67pub struct Bitwise(pub u8);
68
69/// Logical NOT, NAND and NOR, of Booleans, and of integers that are 0 or 1, which give Booleans. Any other integer fails.
70#[derive(Clone, Copy, Debug)]
71pub struct Not;
72/// See [`Not`].
73#[derive(Clone, Copy, Debug)]
74pub struct Nand;
75/// See [`Not`].
76#[derive(Clone, Copy, Debug)]
77pub struct Nor;
78
79/// An integer type's arithmetic. Each operation that can overflow gives its result wrapped to the type, and whether that is the exact result, so that a caller with exact integers can take another path. The tests have no branch, so a loop of them vectorizes. The one exception is a 64-bit product, because no vector instruction gives its 128-bit result. Integer kernels (`recipe::IntegerOps`) and integer folds run these.
80pub trait Overflowing: crate::buffer::Cast + Ord + Into<i64> + Sealed {
81    fn low(x: i64) -> Self;
82    /// The type's greatest value for a positive `sign`, its least for a negative one, and 0 for 0: the floor of a quotient by 0, and of a negative power of 0 where `sign` is 1.
83    fn limit(sign: i64) -> Self;
84    /// x + y.
85    fn plus(self, y: Self) -> (Self, bool);
86    /// x - y.
87    fn minus(self, y: Self) -> (Self, bool);
88    /// x × y.
89    fn times(self, y: Self) -> (Self, bool);
90    /// |x|. A signed type's least value has no magnitude of its type.
91    fn magnitude(self) -> (Self, bool);
92}
93
94/// The product of `$x` and `$y` of type `$t`, computed exactly at `$wide`, the next width up, and wrapped to `$t`.
95macro_rules! wide_product {
96    ($t:ty, $wide:ty, $x:ident, $y:ident) => {{
97        let p = <$wide>::from($x) * <$wide>::from($y);
98        (p as $t, (p >= <$t>::MIN as $wide) & (p <= <$t>::MAX as $wide))
99    }};
100}
101
102/// The signed types' arithmetic. A sum overflows where its sign differs from both arguments'. A difference overflows where the arguments' signs differ and its sign differs from the first argument's.
103macro_rules! signed {
104    ($($t:ty: |$x:ident, $y:ident| $times:expr),+) => {$(
105        impl Overflowing for $t {
106            #[inline]
107            fn low(x: i64) -> Self { x as $t }
108            #[inline(always)]
109            fn limit(sign: i64) -> Self { match sign.signum() { 1 => <$t>::MAX, -1 => <$t>::MIN, _ => 0 } }
110            #[inline(always)]
111            fn plus(self, y: Self) -> (Self, bool) { let s = self.wrapping_add(y); (s, ((self ^ s) & (y ^ s)) >= 0) }
112            #[inline(always)]
113            fn minus(self, y: Self) -> (Self, bool) { let d = self.wrapping_sub(y); (d, ((self ^ y) & (self ^ d)) >= 0) }
114            #[inline(always)]
115            fn times(self, $y: Self) -> (Self, bool) { let $x = self; $times }
116            #[inline(always)]
117            fn magnitude(self) -> (Self, bool) { (self.wrapping_abs(), self != <$t>::MIN) }
118        }
119    )+};
120}
121signed!(
122    i16: |x, y| wide_product!(i16, i32, x, y), i32: |x, y| wide_product!(i32, i64, x, y), i64: |x, y| { let (p, over) = x.overflowing_mul(y); (p, !over) }
123);
124
125/// The unsigned types' arithmetic. A sum overflows where it wraps below an argument, and a difference where it would be negative.
126macro_rules! unsigned {
127    ($($t:ty: |$x:ident, $y:ident| $times:expr),+) => {$(
128        impl Overflowing for $t {
129            #[inline]
130            fn low(x: i64) -> Self { x as $t }
131            #[inline(always)]
132            fn limit(sign: i64) -> Self { match sign.signum() { 1 => <$t>::MAX, -1 => <$t>::MIN, _ => 0 } }
133            #[inline(always)]
134            fn plus(self, y: Self) -> (Self, bool) { let s = self.wrapping_add(y); (s, s >= self) }
135            #[inline(always)]
136            fn minus(self, y: Self) -> (Self, bool) { (self.wrapping_sub(y), self >= y) }
137            #[inline(always)]
138            fn times(self, $y: Self) -> (Self, bool) { let $x = self; $times }
139            #[inline(always)]
140            fn magnitude(self) -> (Self, bool) { (self, true) }
141        }
142    )+};
143}
144unsigned!(u8: |x, y| wide_product!(u8, u16, x, y));