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
122
123
124
125
126
127
128
129
130
131
132
//! Number types useful for counting satisfying assignments

use std::ops::AddAssign;
use std::ops::ShlAssign;
use std::ops::ShrAssign;
use std::ops::SubAssign;

use crate::util::IsFloatingPoint;

/// Natural numbers with saturating arithmetic
///
/// In contrast to [`std::num::Saturating`], `T::MAX` represents an
/// out-of-bounds value, and a subsequent subtraction or right shift does not
/// change this value.
#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct Saturating<T>(pub T);

impl<T> IsFloatingPoint for Saturating<T> {
    const FLOATING_POINT: bool = false;
}

impl<T: From<u32>> From<u32> for Saturating<T> {
    #[inline]
    fn from(value: u32) -> Self {
        Self(T::from(value))
    }
}

impl<'a> AddAssign<&'a Self> for Saturating<u64> {
    #[inline]
    fn add_assign(&mut self, rhs: &'a Self) {
        self.0 = self.0.saturating_add(rhs.0);
    }
}
impl<'a> AddAssign<&'a Self> for Saturating<u128> {
    #[inline]
    fn add_assign(&mut self, rhs: &'a Self) {
        self.0 = self.0.saturating_add(rhs.0);
    }
}

impl<'a> SubAssign<&'a Self> for Saturating<u64> {
    #[inline]
    fn sub_assign(&mut self, rhs: &'a Self) {
        if self.0 != u64::MAX {
            self.0 -= rhs.0;
        }
    }
}
impl<'a> SubAssign<&'a Self> for Saturating<u128> {
    #[inline]
    fn sub_assign(&mut self, rhs: &'a Self) {
        if self.0 != u128::MAX {
            self.0 -= rhs.0;
        }
    }
}

impl ShrAssign<u32> for Saturating<u64> {
    #[inline]
    fn shr_assign(&mut self, rhs: u32) {
        if self.0 != u64::MAX {
            self.0 >>= rhs;
        }
    }
}
impl ShrAssign<u32> for Saturating<u128> {
    #[inline]
    fn shr_assign(&mut self, rhs: u32) {
        if self.0 != u128::MAX {
            self.0 >>= rhs;
        }
    }
}

impl ShlAssign<u32> for Saturating<u64> {
    #[inline]
    fn shl_assign(&mut self, rhs: u32) {
        self.0 = match self.0.checked_shl(rhs) {
            Some(v) => v,
            None => u64::MAX,
        }
    }
}
impl ShlAssign<u32> for Saturating<u128> {
    #[inline]
    fn shl_assign(&mut self, rhs: u32) {
        self.0 = match self.0.checked_shl(rhs) {
            Some(v) => v,
            None => u128::MAX,
        }
    }
}

/// Floating point number like [`f64`], but with [`ShlAssign<u32>`] and
/// [`ShrAssign<u32>`].
#[derive(Clone, Copy, PartialEq, PartialOrd)]
pub struct F64(pub f64);

impl From<u32> for F64 {
    fn from(value: u32) -> Self {
        Self(value as f64)
    }
}

impl IsFloatingPoint for F64 {
    const FLOATING_POINT: bool = true;
}

impl<'a> AddAssign<&'a Self> for F64 {
    fn add_assign(&mut self, rhs: &'a Self) {
        self.0 += rhs.0;
    }
}
impl<'a> SubAssign<&'a Self> for F64 {
    fn sub_assign(&mut self, rhs: &'a Self) {
        self.0 -= rhs.0;
    }
}

impl ShlAssign<u32> for F64 {
    #[allow(clippy::suspicious_op_assign_impl)]
    fn shl_assign(&mut self, rhs: u32) {
        self.0 *= (rhs as f64).exp2()
    }
}
impl ShrAssign<u32> for F64 {
    #[allow(clippy::suspicious_op_assign_impl)]
    fn shr_assign(&mut self, rhs: u32) {
        self.0 /= (rhs as f64).exp2()
    }
}