1use core::{fmt, ops::Neg};
7
8#[derive(Copy, Clone, Debug, Default, PartialEq, Eq, Hash)]
13pub enum Sign {
14 #[default]
16 Pos,
17 Neg,
19}
20
21impl Sign {
22 #[inline]
24 #[must_use]
25 pub const fn is_pos(&self) -> bool {
26 matches!(self, Self::Pos)
27 }
28
29 #[inline]
31 #[must_use]
32 pub const fn is_neg(&self) -> bool {
33 matches!(self, Self::Neg)
34 }
35}
36
37impl Neg for Sign {
38 type Output = Self;
39
40 fn neg(self) -> Self::Output {
41 match self {
42 Self::Pos => Self::Neg,
43 Self::Neg => Self::Pos,
44 }
45 }
46}
47
48impl fmt::Display for Sign {
49 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
50 match self {
51 Self::Pos => write!(f, "+"),
52 Self::Neg => write!(f, "-"),
53 }
54 }
55}
56
57pub trait Magnitude: Copy + PartialEq + PartialOrd {
62 const ZERO: Self;
64
65 fn is_zero(self) -> bool {
67 self == Self::ZERO
68 }
69
70 #[must_use]
72 fn abs_diff(self, other: Self) -> Self;
73
74 fn checked_add(self, other: Self) -> Option<Self>;
76
77 fn checked_sub(self, other: Self) -> Option<Self>;
79}
80
81#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
89pub struct Signed<T: Magnitude> {
90 magnitude: T,
91 sign: Sign,
92}
93
94impl<T: Magnitude> Default for Signed<T> {
95 fn default() -> Self {
96 Self::zero()
97 }
98}
99
100impl<T: Magnitude> Signed<T> {
101 fn normalize(magnitude: T, sign: Sign) -> Self {
103 let sign = if magnitude.is_zero() { Sign::Pos } else { sign };
104 Self { magnitude, sign }
105 }
106
107 #[must_use]
111 pub fn new(magnitude: T, sign: Sign) -> Self {
112 Self::normalize(magnitude, sign)
113 }
114
115 #[must_use]
119 pub fn pos(magnitude: T) -> Self {
120 Self::normalize(magnitude, Sign::Pos)
121 }
122
123 #[must_use]
127 pub fn neg(magnitude: T) -> Self {
128 Self::normalize(magnitude, Sign::Neg)
129 }
130
131 #[must_use]
133 pub const fn zero() -> Self {
134 Self {
135 magnitude: T::ZERO,
136 sign: Sign::Pos,
137 }
138 }
139
140 #[must_use]
142 pub const fn magnitude(&self) -> T {
143 self.magnitude
144 }
145
146 #[must_use]
148 pub const fn sign(&self) -> Sign {
149 self.sign
150 }
151
152 #[must_use]
154 pub fn is_zero(&self) -> bool {
155 self.magnitude.is_zero()
156 }
157
158 #[must_use]
160 pub const fn is_pos(&self) -> bool {
161 self.sign.is_pos()
162 }
163
164 #[must_use]
166 pub const fn is_neg(&self) -> bool {
167 self.sign.is_neg()
168 }
169
170 #[must_use]
174 pub fn checked_add(self, other: Self) -> Option<Self> {
175 if self.sign == other.sign {
176 let magnitude = self.magnitude.checked_add(other.magnitude)?;
178 Some(Self::normalize(magnitude, self.sign))
179 } else {
180 let (magnitude, sign) = if self.magnitude >= other.magnitude {
182 (self.magnitude.abs_diff(other.magnitude), self.sign)
183 } else {
184 (other.magnitude.abs_diff(self.magnitude), other.sign)
185 };
186 Some(Self::normalize(magnitude, sign))
187 }
188 }
189
190 #[must_use]
194 pub fn checked_sub(self, other: Self) -> Option<Self> {
195 self.checked_add(-other)
196 }
197}
198
199impl<T: Magnitude + fmt::Display> fmt::Display for Signed<T> {
200 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
201 if self.sign.is_neg() {
202 write!(f, "-{}", self.magnitude)
203 } else {
204 write!(f, "{}", self.magnitude)
205 }
206 }
207}
208
209impl<T: Magnitude> Neg for Signed<T> {
210 type Output = Self;
211
212 fn neg(self) -> Self::Output {
213 Self::normalize(self.magnitude, -self.sign)
214 }
215}
216
217macro_rules! impl_number {
222 ($name:ident, $doc:expr) => {
223 #[doc = $doc]
224 #[derive(Copy, Clone, Debug, Default, PartialEq, Eq, PartialOrd, Ord, Hash)]
225 pub struct $name(u64);
226
227 impl $name {
228 pub const MIN: Self = Self(0);
230
231 pub const MAX: Self = Self(u64::MAX);
233
234 #[must_use]
236 pub const fn new(value: u64) -> Self {
237 Self(value)
238 }
239
240 #[must_use]
242 pub const fn inner(&self) -> u64 {
243 self.0
244 }
245 }
246
247 impl Magnitude for $name {
248 const ZERO: Self = Self(0);
249
250 fn abs_diff(self, other: Self) -> Self {
251 Self(self.0.abs_diff(other.0))
252 }
253
254 fn checked_add(self, other: Self) -> Option<Self> {
255 self.0.checked_add(other.0).map(Self)
256 }
257
258 fn checked_sub(self, other: Self) -> Option<Self> {
259 self.0.checked_sub(other.0).map(Self)
260 }
261 }
262
263 impl From<u64> for $name {
264 fn from(value: u64) -> Self {
265 Self(value)
266 }
267 }
268
269 impl From<$name> for u64 {
270 fn from(value: $name) -> u64 {
271 value.0
272 }
273 }
274
275 impl fmt::Display for $name {
276 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
277 write!(f, "{}", self.0)
278 }
279 }
280 };
281}
282
283impl_number!(
284 Amount,
285 "An amount of currency in nanomina (1 MINA = 1e9 nanomina).\n\n\
286 This type represents positive currency amounts used in transactions,\n\
287 account balances, and coinbase rewards."
288);
289
290impl_number!(
291 Fee,
292 "A transaction fee in nanomina (1 MINA = 1e9 nanomina).\n\n\
293 This type represents fees paid for transaction processing."
294);
295
296impl From<Fee> for Amount {
297 fn from(fee: Fee) -> Self {
298 Self::new(fee.inner())
299 }
300}