Skip to main content

rustymath_core/recipe/
integer.rs

1//! Integer instantiation of the shared arithmetic formulas.
2use super::{Binary, Cmp, Integer, Ops, Overflowing, Unary};
3use crate::buffer::FloatWidth;
4
5pub struct IntegerOps<T> { pub variant: crate::domain::Variant, item: std::marker::PhantomData<T> }
6impl<T> IntegerOps<T> {
7    #[inline(always)]
8    pub fn new(variant: crate::domain::Variant) -> Self { Self { variant, item: std::marker::PhantomData } }
9}
10impl<T: Overflowing> IntegerOps<T> {
11    #[inline(always)]
12    pub fn result(&self, value: i64) -> T {
13        if self.variant.fails_on_overflow() && matches!(self.variant.dtype, crate::buffer::DType::Integer(w) if !w.holds(value)) {
14            (crate::error::Kind::Overflow.reporter())();
15        }
16        T::low(value)
17    }
18}
19impl<T: Overflowing> Ops<f64> for IntegerOps<T> {
20    type V = T;
21    type B = bool;
22    type I = u64;
23    type W = f64;
24    #[inline(always)]
25    fn width(&self) -> FloatWidth { FloatWidth::F64 }
26    #[inline(always)]
27    fn fused(&self) -> bool { false }
28    #[inline(always)]
29    fn integral(&self) -> bool { true }
30    #[inline(always)]
31    fn widen(&self, x: T) -> f64 { Into::<i64>::into(x) as f64 }
32    #[inline(always)]
33    fn wide_number(&self, x: f64) -> f64 { x }
34    #[inline(always)]
35    fn wide_multiply_add(&self, x: f64, y: f64, z: f64) -> f64 { x * y + z }
36    #[inline(always)]
37    fn narrow(&self, x: f64) -> T { T::low(x as i64) }
38    u64_integers!(8 * std::mem::size_of::<T>() as u32);
39    #[inline(always)]
40    fn to_integer(&self, x: T) -> u64 { Into::<i64>::into(x) as u64 }
41    #[inline(always)]
42    fn from_integer(&self, x: u64) -> T { self.from_bits(x) }
43    #[inline(always)]
44    fn to_bits(&self, x: T) -> u64 { Into::<i64>::into(x) as u64 }
45    #[inline(always)]
46    fn from_bits(&self, x: u64) -> T { T::low(if self.variant.dtype == crate::buffer::DType::Boolean { (x & 1) as i64 } else { x as i64 }) }
47    #[inline(always)]
48    fn number(&self, x: f64) -> T { T::low(x as i64) }
49    #[inline(always)]
50    fn unary(&self, op: Unary, x: T) -> T {
51        match op {
52            Unary::Negate => T::low(0).minus(x).0, Unary::Magnitude => x.magnitude().0, Unary::Floor => x,
53            Unary::Sqrt => { let x = Into::<i64>::into(x); T::low(if x >= 0 { x.isqrt() } else { 0 }) } Unary::Not => T::low(1).minus(x).0,
54            _ => unreachable!("non-integer instruction in an integer formula")
55        }
56    }
57    #[inline(always)]
58    fn binary(&self, op: Binary, x: T, y: T) -> T {
59        match op {
60            Binary::Add => x.plus(y).0,
61            Binary::Subtract => x.minus(y).0,
62            Binary::Multiply => x.times(y).0,
63            // A zero divisor gives the limit of the dividend's sign, or 0 for 0 รท 0. Under dynamic kinds that's i64's limit, which a narrower width reports as an overflow (`result`), so the call widens to i64.
64            Binary::Divide => {
65                let (x, y): (i64, i64) = (x.into(), y.into());
66                if y != 0 { T::low(x.wrapping_div(y)) }
67                else if self.variant.fails_on_overflow() {
68                    self.result(if x == 0 { 0 } else if x > 0 { i64::MAX } else { i64::MIN })
69                } else { T::limit(x) }
70            }
71            Binary::Larger => x.max(y),
72            Binary::Smaller => x.min(y),
73            Binary::BitAnd => T::low(Into::<i64>::into(x) & Into::<i64>::into(y)),
74            _ => unreachable!("non-integer instruction in an integer formula"),
75        }
76    }
77    #[inline(always)]
78    fn overflow(&self, op: Binary, x: T, y: T) -> bool {
79        !match op {
80            Binary::Add => x.plus(y).1,
81            Binary::Subtract => x.minus(y).1,
82            Binary::Multiply => x.times(y).1,
83            // A quotient overflows only as the least value divided by -1 does. A zero divisor's limit reports its own overflow (`binary`).
84            Binary::Divide => {
85                let (x, y): (i64, i64) = (x.into(), y.into());
86                x.checked_div(y).map_or(y == 0, |q| Into::<i64>::into(T::low(q)) == q)
87            }
88            _ => true,
89        }
90    }
91    plain_logic!(T);
92    #[inline(always)]
93    fn multiply_add(&self, x: T, y: T, z: T) -> T { self.binary(Binary::Add, self.binary(Binary::Multiply, x, y), z) }
94}