oxidd_core/util/
num.rs

1//! Number types useful for counting satisfying assignments
2
3use std::ops::AddAssign;
4use std::ops::ShlAssign;
5use std::ops::ShrAssign;
6use std::ops::SubAssign;
7
8use crate::util::IsFloatingPoint;
9
10/// Natural numbers with saturating arithmetic
11///
12/// In contrast to [`std::num::Saturating`], `T::MAX` represents an
13/// out-of-bounds value, and a subsequent subtraction or right shift does not
14/// change this value.
15#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
16pub struct Saturating<T>(pub T);
17
18/// cbindgen:ignore
19impl<T> IsFloatingPoint for Saturating<T> {
20    const FLOATING_POINT: bool = false;
21    const MIN_EXP: i32 = 0;
22}
23
24impl<T: From<u32>> From<u32> for Saturating<T> {
25    #[inline]
26    fn from(value: u32) -> Self {
27        Self(T::from(value))
28    }
29}
30
31impl<'a> AddAssign<&'a Self> for Saturating<u64> {
32    #[inline]
33    fn add_assign(&mut self, rhs: &'a Self) {
34        self.0 = self.0.saturating_add(rhs.0);
35    }
36}
37impl<'a> AddAssign<&'a Self> for Saturating<u128> {
38    #[inline]
39    fn add_assign(&mut self, rhs: &'a Self) {
40        self.0 = self.0.saturating_add(rhs.0);
41    }
42}
43
44impl<'a> SubAssign<&'a Self> for Saturating<u64> {
45    #[inline]
46    fn sub_assign(&mut self, rhs: &'a Self) {
47        if self.0 != u64::MAX {
48            self.0 -= rhs.0;
49        }
50    }
51}
52impl<'a> SubAssign<&'a Self> for Saturating<u128> {
53    #[inline]
54    fn sub_assign(&mut self, rhs: &'a Self) {
55        if self.0 != u128::MAX {
56            self.0 -= rhs.0;
57        }
58    }
59}
60
61impl ShrAssign<u32> for Saturating<u64> {
62    #[inline]
63    fn shr_assign(&mut self, rhs: u32) {
64        if self.0 != u64::MAX {
65            self.0 >>= rhs;
66        }
67    }
68}
69impl ShrAssign<u32> for Saturating<u128> {
70    #[inline]
71    fn shr_assign(&mut self, rhs: u32) {
72        if self.0 != u128::MAX {
73            self.0 >>= rhs;
74        }
75    }
76}
77
78impl ShlAssign<u32> for Saturating<u64> {
79    #[inline]
80    fn shl_assign(&mut self, rhs: u32) {
81        self.0 = match self.0.checked_shl(rhs) {
82            Some(v) => v,
83            None => u64::MAX,
84        }
85    }
86}
87impl ShlAssign<u32> for Saturating<u128> {
88    #[inline]
89    fn shl_assign(&mut self, rhs: u32) {
90        self.0 = match self.0.checked_shl(rhs) {
91            Some(v) => v,
92            None => u128::MAX,
93        }
94    }
95}
96
97/// Floating point number like [`f64`], but with [`ShlAssign<u32>`] and
98/// [`ShrAssign<u32>`].
99#[derive(Clone, Copy, PartialEq, PartialOrd)]
100#[repr(transparent)]
101pub struct F64(pub f64);
102
103impl From<u32> for F64 {
104    fn from(value: u32) -> Self {
105        Self(value as f64)
106    }
107}
108
109/// cbindgen:ignore
110impl IsFloatingPoint for F64 {
111    const FLOATING_POINT: bool = true;
112    const MIN_EXP: i32 = f64::MIN_EXP;
113}
114
115impl<'a> AddAssign<&'a Self> for F64 {
116    fn add_assign(&mut self, rhs: &'a Self) {
117        self.0 += rhs.0;
118    }
119}
120impl<'a> SubAssign<&'a Self> for F64 {
121    fn sub_assign(&mut self, rhs: &'a Self) {
122        self.0 -= rhs.0;
123    }
124}
125
126impl ShlAssign<u32> for F64 {
127    #[allow(clippy::suspicious_op_assign_impl)]
128    fn shl_assign(&mut self, rhs: u32) {
129        self.0 *= (rhs as f64).exp2()
130    }
131}
132impl ShrAssign<u32> for F64 {
133    #[allow(clippy::suspicious_op_assign_impl)]
134    fn shr_assign(&mut self, rhs: u32) {
135        self.0 *= (-(rhs as f64)).exp2()
136    }
137}