1use super::unsigned::UnsignedNumeric;
2use core::ops::{Add, Sub, Mul, Div};
3
4#[cfg(not(feature = "std"))]
5use alloc::{format, string::String};
6
7#[derive(Clone, Debug, PartialEq)]
33pub struct SignedNumeric {
34 pub value: UnsignedNumeric,
35 pub is_negative: bool,
36}
37
38impl SignedNumeric {
39 pub fn new(value: i128) -> Self {
40 let abs_value = value.unsigned_abs();
41 let is_negative = value < 0;
42 Self {
43 value: UnsignedNumeric::new(abs_value),
44 is_negative,
45 }
46 }
47 pub fn negate(&self) -> SignedNumeric {
48 SignedNumeric {
49 value: self.value.clone(),
50 is_negative: !self.is_negative,
51 }
52 }
53
54 pub fn checked_mul(&self, rhs: &Self) -> Option<SignedNumeric> {
55 Some(SignedNumeric {
56 value: self.value.checked_mul(&rhs.value)?,
57 is_negative: (self.is_negative || rhs.is_negative)
58 && !(self.is_negative && rhs.is_negative),
59 })
60 }
61
62 pub fn checked_div(&self, rhs: &Self) -> Option<SignedNumeric> {
63 Some(SignedNumeric {
64 value: self.value.checked_div(&rhs.value)?,
65 is_negative: (self.is_negative || rhs.is_negative)
66 && !(self.is_negative && rhs.is_negative),
67 })
68 }
69
70 pub fn checked_add(&self, rhs: &Self) -> Option<SignedNumeric> {
71 let lhs_negative = self.is_negative;
72 let rhs_negative = rhs.is_negative;
73
74 if rhs_negative && lhs_negative {
75 Some(Self {
76 value: self.value.checked_add(&rhs.value)?,
77 is_negative: true,
78 })
79 } else if rhs_negative {
80 if rhs.value.greater_than(&self.value) {
81 Some(Self {
82 value: rhs.value.checked_sub(&self.value)?,
83 is_negative: true,
84 })
85 } else {
86 Some(Self {
87 value: self.value.checked_sub(&rhs.value)?,
88 is_negative: false,
89 })
90 }
91 } else if lhs_negative {
92 if self.value.greater_than(&rhs.value) {
93 Some(Self {
94 value: self.value.checked_sub(&rhs.value)?,
95 is_negative: true,
96 })
97 } else {
98 Some(Self {
99 value: rhs.value.checked_sub(&self.value)?,
100 is_negative: false,
101 })
102 }
103 } else {
104 Some(Self {
105 value: self.value.checked_add(&rhs.value)?,
106 is_negative: false,
107 })
108 }
109 }
110
111 pub fn checked_sub(&self, rhs: &Self) -> Option<SignedNumeric> {
112 self.checked_add(&rhs.clone().negate())
113 }
114
115 pub fn floor(&self) -> Option<SignedNumeric> {
116 Some(Self {
117 value: self.value.floor()?,
118 is_negative: self.is_negative,
119 })
120 }
121
122 pub fn to_string(&self) -> String {
123 let sign = if self.is_negative { "-" } else { "" };
124 format!("{}{}", sign, self.value.to_string())
125 }
126}
127
128impl Add for SignedNumeric {
130 type Output = Self;
131
132 fn add(self, rhs: Self) -> Self::Output {
133 self.checked_add(&rhs).unwrap()
134 }
135}
136
137impl Add<&SignedNumeric> for SignedNumeric {
138 type Output = Self;
139
140 fn add(self, rhs: &Self) -> Self::Output {
141 self.checked_add(rhs).unwrap()
142 }
143}
144
145impl Add<SignedNumeric> for &SignedNumeric {
146 type Output = SignedNumeric;
147
148 fn add(self, rhs: SignedNumeric) -> Self::Output {
149 self.checked_add(&rhs).unwrap()
150 }
151}
152
153impl Add<&SignedNumeric> for &SignedNumeric {
154 type Output = SignedNumeric;
155
156 fn add(self, rhs: &SignedNumeric) -> Self::Output {
157 self.checked_add(rhs).unwrap()
158 }
159}
160
161impl Sub for SignedNumeric {
162 type Output = Self;
163
164 fn sub(self, rhs: Self) -> Self::Output {
165 self.checked_sub(&rhs).unwrap()
166 }
167}
168
169impl Sub<&SignedNumeric> for SignedNumeric {
170 type Output = Self;
171
172 fn sub(self, rhs: &Self) -> Self::Output {
173 self.checked_sub(rhs).unwrap()
174 }
175}
176
177impl Sub<SignedNumeric> for &SignedNumeric {
178 type Output = SignedNumeric;
179
180 fn sub(self, rhs: SignedNumeric) -> Self::Output {
181 self.checked_sub(&rhs).unwrap()
182 }
183}
184
185impl Sub<&SignedNumeric> for &SignedNumeric {
186 type Output = SignedNumeric;
187
188 fn sub(self, rhs: &SignedNumeric) -> Self::Output {
189 self.checked_sub(rhs).unwrap()
190 }
191}
192
193impl Mul for SignedNumeric {
194 type Output = Self;
195
196 fn mul(self, rhs: Self) -> Self::Output {
197 self.checked_mul(&rhs).unwrap()
198 }
199}
200
201impl Mul<&SignedNumeric> for SignedNumeric {
202 type Output = Self;
203
204 fn mul(self, rhs: &Self) -> Self::Output {
205 self.checked_mul(rhs).unwrap()
206 }
207}
208
209impl Mul<SignedNumeric> for &SignedNumeric {
210 type Output = SignedNumeric;
211
212 fn mul(self, rhs: SignedNumeric) -> Self::Output {
213 self.checked_mul(&rhs).unwrap()
214 }
215}
216
217impl Mul<&SignedNumeric> for &SignedNumeric {
218 type Output = SignedNumeric;
219
220 fn mul(self, rhs: &SignedNumeric) -> Self::Output {
221 self.checked_mul(rhs).unwrap()
222 }
223}
224
225impl Div for SignedNumeric {
226 type Output = Self;
227
228 fn div(self, rhs: Self) -> Self::Output {
229 self.checked_div(&rhs).unwrap()
230 }
231}
232
233impl Div<&SignedNumeric> for SignedNumeric {
234 type Output = Self;
235
236 fn div(self, rhs: &Self) -> Self::Output {
237 self.checked_div(rhs).unwrap()
238 }
239}
240
241impl Div<SignedNumeric> for &SignedNumeric {
242 type Output = SignedNumeric;
243
244 fn div(self, rhs: SignedNumeric) -> Self::Output {
245 self.checked_div(&rhs).unwrap()
246 }
247}
248
249impl Div<&SignedNumeric> for &SignedNumeric {
250 type Output = SignedNumeric;
251
252 fn div(self, rhs: &SignedNumeric) -> Self::Output {
253 self.checked_div(rhs).unwrap()
254 }
255}
256
257#[cfg(test)]
258mod tests {
259 use super::*;
260 use crate::consts::*;
261 use crate::InnerUint;
262
263 fn signed(val: u128, is_negative: bool) -> SignedNumeric {
264 SignedNumeric {
265 value: UnsignedNumeric::new(val),
266 is_negative,
267 }
268 }
269
270 #[test]
271 fn test_negate() {
272 let a = signed(5, false);
273 let b = a.negate();
274 assert_eq!(b.value, a.value);
275 assert_eq!(b.is_negative, true);
276 assert_eq!(b.negate(), a);
277 }
278
279 #[test]
280 fn test_add_same_sign_positive() {
281 let a = signed(3, false);
282 let b = signed(2, false);
283 let sum = a.checked_add(&b).unwrap();
284 assert_eq!(sum.value.to_imprecise().unwrap(), 5);
285 assert!(!sum.is_negative);
286 }
287
288 #[test]
289 fn test_add_same_sign_negative() {
290 let a = signed(3, true);
291 let b = signed(2, true);
292 let sum = a.checked_add(&b).unwrap();
293 assert_eq!(sum.value.to_imprecise().unwrap(), 5);
294 assert!(sum.is_negative);
295 }
296
297 #[test]
298 fn test_add_opposite_sign_larger_positive() {
299 let a = signed(5, false);
300 let b = signed(3, true);
301 let sum = a.checked_add(&b).unwrap();
302 assert_eq!(sum.value.to_imprecise().unwrap(), 2);
303 assert!(!sum.is_negative);
304 }
305
306 #[test]
307 fn test_add_opposite_sign_larger_negative() {
308 let a = signed(3, false);
309 let b = signed(5, true);
310 let sum = a.checked_add(&b).unwrap();
311 assert_eq!(sum.value.to_imprecise().unwrap(), 2);
312 assert!(sum.is_negative);
313 }
314
315 #[test]
316 fn test_add_opposite_sign_equal() {
317 let a = signed(4, false);
318 let b = signed(4, true);
319 let sum = a.checked_add(&b).unwrap();
320 assert_eq!(sum.value.to_imprecise().unwrap(), 0);
321 assert!(!sum.is_negative);
322 }
323
324 #[test]
325 fn test_sub_positive() {
326 let a = signed(10, false);
327 let b = signed(3, false);
328 let diff = a.checked_sub(&b).unwrap();
329 assert_eq!(diff.value.to_imprecise().unwrap(), 7);
330 assert!(!diff.is_negative);
331 }
332
333 #[test]
334 fn test_sub_negative() {
335 let a = signed(3, false);
336 let b = signed(10, false);
337 let diff = a.checked_sub(&b).unwrap();
338 assert_eq!(diff.value.to_imprecise().unwrap(), 7);
339 assert!(diff.is_negative);
340 }
341
342 #[test]
343 fn test_sub_negative_minued() {
344 let a = signed(3, true);
345 let b = signed(10, false);
346 let diff = a.checked_sub(&b).unwrap();
347 assert_eq!(diff.value.to_imprecise().unwrap(), 13);
348 assert!(diff.is_negative);
349 }
350
351 #[test]
352 fn test_mul_signs() {
353 let a = signed(3, false);
354 let b = signed(2, false);
355 let result = a.checked_mul(&b).unwrap();
356 assert_eq!(result.value.to_imprecise().unwrap(), 6);
357 assert!(!result.is_negative);
358
359 let result = a.checked_mul(&b.negate()).unwrap();
360 assert_eq!(result.value.to_imprecise().unwrap(), 6);
361 assert!(result.is_negative);
362
363 let result = a.negate().checked_mul(&b.negate()).unwrap();
364 assert_eq!(result.value.to_imprecise().unwrap(), 6);
365 assert!(!result.is_negative);
366 }
367
368 #[test]
369 fn test_div_signs() {
370 let a = signed(6, false);
371 let b = signed(2, false);
372 let result = a.checked_div(&b).unwrap();
373 assert_eq!(result.value.to_imprecise().unwrap(), 3);
374 assert!(!result.is_negative);
375
376 let result = a.checked_div(&b.negate()).unwrap();
377 assert_eq!(result.value.to_imprecise().unwrap(), 3);
378 assert!(result.is_negative);
379
380 let result = a.negate().checked_div(&b.negate()).unwrap();
381 assert_eq!(result.value.to_imprecise().unwrap(), 3);
382 assert!(!result.is_negative);
383 }
384
385 #[test]
386 fn test_floor_behavior() {
387 let base = signed(2, false);
388 let mut with_decimals = base.clone();
389 with_decimals.value.value = with_decimals
390 .value
391 .value
392 .checked_add(InnerUint::from(ONE / 3))
393 .unwrap();
394 let floored = with_decimals.floor().unwrap();
395 assert_eq!(floored.value, base.value);
396 assert_eq!(floored.is_negative, false);
397
398 let base_neg = base.negate();
399 let mut neg_with_decimals = base_neg.clone();
400 neg_with_decimals.value.value = neg_with_decimals
401 .value
402 .value
403 .checked_add(InnerUint::from(ONE / 2))
404 .unwrap();
405 let floored = neg_with_decimals.floor().unwrap();
406 assert_eq!(floored.value, base.value);
407 assert_eq!(floored.is_negative, true);
408 }
409
410 #[test]
411 fn test_to_string_exact() {
412 let n = signed(3, false);
413 assert_eq!(n.to_string(), "3.000000000000000000");
414
415 let n_neg = signed(3, true);
416 assert_eq!(n_neg.to_string(), "-3.000000000000000000");
417 }
418
419 #[test]
420 fn test_to_string_fractional() {
421 let mut n = signed(3, false);
422 n.value.value += InnerUint::from(250_000_000_000_000_000u128); assert_eq!(n.to_string(), "3.250000000000000000");
424
425 let mut n_neg = signed(3, true);
426 n_neg.value.value += InnerUint::from(250_000_000_000_000_000u128); assert_eq!(n_neg.to_string(), "-3.250000000000000000");
428 }
429
430 #[test]
431 fn test_arithmetic_operators() {
432 let a = signed(5, false);
433 let b = signed(3, false);
434 let c = signed(2, true);
435
436 let sum1 = a.clone() + b.clone();
438 assert_eq!(sum1.value.to_imprecise().unwrap(), 8);
439 assert!(!sum1.is_negative);
440
441 let sum2 = a.clone() + c.clone();
442 assert_eq!(sum2.value.to_imprecise().unwrap(), 3);
443 assert!(!sum2.is_negative);
444
445 let diff1 = a.clone() - b.clone();
447 assert_eq!(diff1.value.to_imprecise().unwrap(), 2);
448 assert!(!diff1.is_negative);
449
450 let diff2 = b.clone() - a.clone();
451 assert_eq!(diff2.value.to_imprecise().unwrap(), 2);
452 assert!(diff2.is_negative);
453
454 let product1 = a.clone() * b.clone();
456 assert_eq!(product1.value.to_imprecise().unwrap(), 15);
457 assert!(!product1.is_negative);
458
459 let product2 = a.clone() * c.clone();
460 assert_eq!(product2.value.to_imprecise().unwrap(), 10);
461 assert!(product2.is_negative);
462
463 let quotient1 = a.clone() / b.clone();
465 assert!(quotient1.value.almost_eq(&UnsignedNumeric::from_scaled_u128(1_666_666_666_666_666_666), InnerUint::from(1_000_000)));
466 assert!(!quotient1.is_negative);
467
468 let quotient2 = a.clone() / c.clone();
469 assert_eq!(quotient2.value.to_imprecise().unwrap(), 3);
470 assert!(quotient2.is_negative);
471 }
472
473 #[test]
474 fn test_arithmetic_operators_with_references() {
475 let a = signed(10, false);
476 let b = signed(4, false);
477 let c = signed(3, true);
478
479 let sum = &a + &b;
481 assert_eq!(sum.value.to_imprecise().unwrap(), 14);
482 assert!(!sum.is_negative);
483
484 let diff = &a - &b;
485 assert_eq!(diff.value.to_imprecise().unwrap(), 6);
486 assert!(!diff.is_negative);
487
488 let product = &a * &c;
489 assert_eq!(product.value.to_imprecise().unwrap(), 30);
490 assert!(product.is_negative);
491
492 let quotient = &a / &b;
493 assert_eq!(quotient.value.to_imprecise().unwrap(), 3);
494 assert!(!quotient.is_negative);
495 }
496
497 #[test]
498 fn test_arithmetic_operators_mixed_references() {
499 let a = signed(6, false);
500 let b = signed(2, true);
501
502 let sum1 = a.clone() + &b;
504 let sum2 = &a + b.clone();
505 assert_eq!(sum1.value.to_imprecise().unwrap(), 4);
506 assert!(!sum1.is_negative);
507 assert_eq!(sum2.value.to_imprecise().unwrap(), 4);
508 assert!(!sum2.is_negative);
509
510 let product1 = a.clone() * &b;
511 let product2 = &a * b.clone();
512 assert_eq!(product1.value.to_imprecise().unwrap(), 12);
513 assert!(product1.is_negative);
514 assert_eq!(product2.value.to_imprecise().unwrap(), 12);
515 assert!(product2.is_negative);
516 }
517
518 #[test]
519 fn test_negative_arithmetic() {
520 let a = signed(5, true); let b = signed(3, true); let sum = a.clone() + b.clone();
525 assert_eq!(sum.value.to_imprecise().unwrap(), 8);
526 assert!(sum.is_negative);
527
528 let product = a.clone() * b.clone();
530 assert_eq!(product.value.to_imprecise().unwrap(), 15);
531 assert!(!product.is_negative); let quotient = a.clone() / b.clone();
535 assert!(quotient.value.almost_eq(&UnsignedNumeric::from_scaled_u128(1_666_666_666_666_666_666), InnerUint::from(1_000_000)));
536 assert!(!quotient.is_negative); }
538}