machine_check/types/
signed.rs

1use std::{
2    fmt::Debug,
3    ops::{Add, BitAnd, BitOr, BitXor, Div, Mul, Not, Rem, Shl, Shr, Sub},
4};
5
6use mck::{
7    concr::{self, IntoMck},
8    misc::{CBound, PANIC_MSG_DIV_BY_ZERO, PANIC_MSG_REM_BY_ZERO},
9};
10
11use crate::{traits::Ext, Bitvector, Unsigned};
12
13/// Signed bitvector.
14///
15/// The width (number of bits) is specified in the generic parameter W.
16/// Signed bitvectors support bitwise operations and wrapping-arithmetic operations.
17/// Arithmetic bit extension is also possible (the sign bit is copied into any bits above it).
18/// Signed bitvectors can be converted into [`Unsigned`] or [`Bitvector`].
19///
20#[derive(Clone, Copy, Hash, PartialEq, Eq)]
21pub struct Signed<const W: u32>(pub(super) concr::SignedBitvector<CBound<W>>);
22
23impl<const W: u32> Signed<W> {
24    pub fn new(value: i64) -> Self {
25        Self(concr::SignedBitvector::new(value, CBound))
26    }
27}
28
29// --- BITWISE OPERATIONS ---
30
31impl<const W: u32> Not for Signed<W> {
32    type Output = Self;
33
34    /// Performs bitwise NOT.
35    fn not(self) -> Self::Output {
36        Self(!self.0)
37    }
38}
39
40impl<const W: u32> BitAnd<Signed<W>> for Signed<W> {
41    type Output = Self;
42
43    /// Performs bitwise AND.
44    fn bitand(self, rhs: Signed<W>) -> Self::Output {
45        Self(self.0 & rhs.0)
46    }
47}
48impl<const W: u32> BitOr<Signed<W>> for Signed<W> {
49    type Output = Self;
50
51    /// Performs bitwise OR.
52    fn bitor(self, rhs: Signed<W>) -> Self::Output {
53        Self(self.0 | rhs.0)
54    }
55}
56impl<const W: u32> BitXor<Signed<W>> for Signed<W> {
57    type Output = Self;
58
59    /// Performs bitwise XOR.
60    fn bitxor(self, rhs: Signed<W>) -> Self::Output {
61        Self(self.0 ^ rhs.0)
62    }
63}
64
65// --- ARITHMETIC OPERATIONS ---
66
67impl<const W: u32> Add<Signed<W>> for Signed<W> {
68    type Output = Self;
69
70    /// Performs wrapping addition.
71    fn add(self, rhs: Signed<W>) -> Self::Output {
72        Self(self.0.add(rhs.0))
73    }
74}
75
76impl<const W: u32> Sub<Signed<W>> for Signed<W> {
77    type Output = Self;
78
79    /// Performs wrapping subtraction.
80    fn sub(self, rhs: Signed<W>) -> Self::Output {
81        Self(self.0.sub(rhs.0))
82    }
83}
84
85impl<const W: u32> Mul<Signed<W>> for Signed<W> {
86    type Output = Self;
87
88    /// Performs wrapping multiplication.
89    fn mul(self, rhs: Signed<W>) -> Self::Output {
90        Self(self.0.mul(rhs.0))
91    }
92}
93
94impl<const W: u32> Div<Signed<W>> for Signed<W> {
95    type Output = Self;
96
97    /// Performs wrapping signed division.
98    ///
99    /// This behaves as Rust `wrapping_div`, where the result of `MIN / -1`,
100    /// which would be unrepresentable `-MIN`, is wrapped to `MIN`.
101    ///
102    /// # Panics
103    ///
104    /// Panics if `rhs` is zero.
105    fn div(self, rhs: Signed<W>) -> Self::Output {
106        let panic_result = self.0.div(rhs.0);
107        if panic_result.panic.is_nonzero() {
108            panic!("{}", PANIC_MSG_DIV_BY_ZERO)
109        }
110        Self(panic_result.result)
111    }
112}
113
114impl<const W: u32> Rem<Signed<W>> for Signed<W> {
115    type Output = Self;
116
117    /// Performs wrapping signed division.
118    ///
119    /// This behaves as Rust `wrapping_rem`, where the result of `MIN % -1`,
120    /// is defined as 0.
121    ///
122    /// # Panics
123    ///
124    /// Panics if `rhs` is zero.
125    fn rem(self, rhs: Signed<W>) -> Self::Output {
126        let panic_result = self.0.rem(rhs.0);
127        if panic_result.panic.is_nonzero() {
128            panic!("{}", PANIC_MSG_REM_BY_ZERO)
129        }
130        Self(panic_result.result)
131    }
132}
133
134impl<const W: u32> Shl<Signed<W>> for Signed<W> {
135    type Output = Self;
136
137    /// Performs a left shift.
138    ///
139    /// Unlike a right shift, where the behaviour is dependent on signedness,
140    /// the left shift has the same behaviour: shifted-out bits on the left
141    /// are discarded and zeros are shifted in on the right.
142    ///
143    /// The right-hand side operand is interpreted as unsigned and if it
144    /// is equal or greater to the bit-width, the result is all-zeros,
145    /// as in Rust `unbounded_shl`. It is planned to restrict the bit-width
146    /// in the future so that this edge case can never occur.
147    ///
148    /// Note that this means that shifting left with a negative right operand
149    /// produces an all-zeros value.
150    fn shl(self, rhs: Signed<W>) -> Self::Output {
151        Self(self.0.shl(rhs.0))
152    }
153}
154
155impl<const W: u32> Shr<Signed<W>> for Signed<W> {
156    type Output = Self;
157
158    /// Performs an arithmetic right shift.
159    ///
160    /// The right-hand side operand is interpreted as unsigned and if it
161    /// is equal or greater to the bit-width, the result is all-zeros or
162    /// all-ones depending on the original sign bit, as in Rust `unbounded_shr`
163    /// on signed primitives.
164    /// It is planned to restrict the bit-width in the future so that this edge
165    /// case can never occur.
166    ///
167    /// Note that this means that shifting right with a negative right operand
168    /// produces an all-zeros or all-ones value, depending on the original sign bit.
169    fn shr(self, rhs: Signed<W>) -> Self::Output {
170        Self(self.0.shr(rhs.0))
171    }
172}
173
174// --- EXTENSION ---
175impl<const W: u32, const X: u32> Ext<X> for Signed<W> {
176    type Output = Signed<X>;
177
178    /// Extends or narrows the bit-vector.
179    ///
180    /// If an extension is performed, the upper bits
181    /// are the copy of the original sign bit.
182    fn ext(self) -> Self::Output {
183        Signed(self.0.ext(CBound::<X>))
184    }
185}
186
187// --- ORDERING ---
188
189impl<const W: u32> PartialOrd for Signed<W> {
190    fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
191        Some(self.cmp(other))
192    }
193}
194
195impl<const W: u32> Ord for Signed<W> {
196    fn cmp(&self, other: &Self) -> std::cmp::Ordering {
197        self.0.cmp(&other.0)
198    }
199}
200
201// --- CONVERSION ---
202impl<const W: u32> From<Unsigned<W>> for Signed<W> {
203    /// Converts the signedness information from `Unsigned` to `Signed`.
204    fn from(value: Unsigned<W>) -> Self {
205        Self(value.0.cast_bitvector().as_signed())
206    }
207}
208
209impl<const W: u32> From<Bitvector<W>> for Signed<W> {
210    /// Adds signedness information to `Bitvector`.
211    fn from(value: Bitvector<W>) -> Self {
212        Self(value.0.as_signed())
213    }
214}
215
216// --- MISC ---
217
218impl<const W: u32> Debug for Signed<W> {
219    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
220        std::fmt::Debug::fmt(&self.0, f)
221    }
222}
223
224// --- INTERNAL IMPLEMENTATIONS ---
225
226#[doc(hidden)]
227impl<const W: u32> IntoMck for Signed<W> {
228    type Type = mck::concr::Bitvector<W>;
229
230    fn into_mck(self) -> Self::Type {
231        self.0.cast_bitvector()
232    }
233}