use core::cmp::Ordering;
#[derive(Debug, PartialEq, Clone, Copy)]
pub struct Signed<T> {
value: T,
negative: bool,
}
impl<T> From<T> for Signed<T> {
fn from(value: T) -> Self {
Self {
value,
negative: false,
}
}
}
impl<T> Signed<T> {
pub fn new(value: T, negative: bool) -> Self
where
T: PartialEq + num_traits::Zero,
{
let is_zero = value == T::zero();
Self {
value,
negative: if is_zero { false } else { negative },
}
}
pub(crate) fn new_unchecked(value: T, negative: bool) -> Self {
Self { value, negative }
}
pub fn into_inner(self) -> T {
self.value
}
}
impl<T> PartialOrd for Signed<T>
where
T: PartialOrd,
{
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
match (self.negative, other.negative) {
(false, true) => Some(Ordering::Greater),
(true, false) => Some(Ordering::Less),
(true, true) => other.value.partial_cmp(&self.value), (false, false) => self.value.partial_cmp(&other.value),
}
}
}
impl<T> core::ops::Add<&Signed<T>> for Signed<T>
where
for<'a> T: core::ops::Add<&'a T, Output = T>
+ core::ops::Sub<&'a T, Output = T>
+ core::cmp::PartialOrd
+ PartialEq
+ num_traits::Zero,
for<'a> &'a T: core::ops::Sub<T, Output = T>,
{
type Output = Self;
fn add(self, other: &Signed<T>) -> Self::Output {
let mut result = match (self.negative, other.negative) {
(false, false) => Self::new_unchecked(self.value + &other.value, false),
(true, true) => Self::new_unchecked(self.value + &other.value, true),
(false, true) | (true, false) => {
if self.value >= other.value {
Self::new_unchecked(self.value - &other.value, self.negative)
} else {
Self::new_unchecked(&other.value - self.value, other.negative)
}
}
};
if result.value == T::zero() {
result.negative = false;
}
result
}
}
impl<'a, T> core::ops::AddAssign<&'a T> for Signed<T>
where
T: core::ops::AddAssign<&'a T>
+ core::ops::SubAssign<&'a T>
+ PartialEq
+ PartialOrd
+ num_traits::Zero
+ Clone,
for<'b> &'b T: core::ops::Sub<T, Output = T>,
{
fn add_assign(&mut self, other: &'a T) {
match self.negative {
false => self.value += other,
true => {
if &self.value >= other {
self.value -= other;
} else {
self.value = other - self.value.clone();
self.negative = false; }
}
}
if self.value == T::zero() {
self.negative = false;
}
}
}
impl<T> core::ops::Add<&T> for Signed<T>
where
for<'a> T: core::ops::Add<&'a T, Output = T>
+ core::ops::Sub<&'a T, Output = T>
+ core::cmp::PartialOrd
+ num_traits::Zero
+ PartialEq,
for<'a> &'a T: core::ops::Sub<T, Output = T>,
{
type Output = Self;
fn add(self, other: &T) -> Self::Output {
let mut result = match self.negative {
false => Self::new_unchecked(self.value + other, self.negative),
true => {
if self.value >= *other {
Self::new_unchecked(self.value - other, self.negative)
} else {
Self::new_unchecked(other - self.value, !self.negative)
}
}
};
if result.value == T::zero() {
result.negative = false;
}
result
}
}
impl<T> core::ops::Add for Signed<T>
where
T: core::ops::Add<Output = T> + core::ops::Sub<Output = T> + core::cmp::PartialOrd,
{
type Output = Self;
fn add(self, rhs: Self) -> Self::Output {
match (self.negative, rhs.negative) {
(false, false) => Self::new_unchecked(self.value + rhs.value, false),
(true, true) => Self::new_unchecked(self.value + rhs.value, true),
(false, true) | (true, false) => {
if self.value >= rhs.value {
Self::new_unchecked(self.value - rhs.value, self.negative)
} else {
Self::new_unchecked(rhs.value - self.value, rhs.negative)
}
}
}
}
}
impl<T> core::ops::Sub for Signed<T>
where
T: core::ops::Add<Output = T> + core::ops::Sub<Output = T> + core::cmp::PartialOrd,
{
type Output = Self;
fn sub(self, rhs: Self) -> Self::Output {
self + Self::new_unchecked(rhs.value, !rhs.negative)
}
}
impl<T> core::ops::Mul for Signed<T>
where
T: core::ops::Mul<Output = T>,
{
type Output = Self;
fn mul(self, other: Self) -> Self::Output {
Self {
value: self.value * other.value,
negative: self.negative != other.negative,
}
}
}
impl<T> core::ops::Mul<T> for Signed<T>
where
T: core::ops::Mul<Output = T>,
{
type Output = Self;
fn mul(self, other: T) -> Self::Output {
Self {
value: self.value * other,
negative: self.negative,
}
}
}
impl<'a, T> core::ops::Mul<&'a T> for Signed<T>
where
T: core::ops::Mul<&'a T, Output = T> + 'a,
{
type Output = Self;
fn mul(self, other: &'a T) -> Self::Output {
Self {
value: self.value * other,
negative: self.negative,
}
}
}
impl<T> core::ops::Div for Signed<T>
where
T: core::ops::Div<Output = T>,
{
type Output = Self;
fn div(self, other: Self) -> Self::Output {
Self {
value: self.value / other.value,
negative: self.negative != other.negative,
}
}
}
#[cfg(test)]
mod signed_tests {
use super::Signed;
#[test]
fn test_signed_test_greater_than() {
fn check(a: u32, a_neg: bool, b: u32, b_neg: bool, expected: bool) {
let a = Signed::new(a, a_neg);
let b = Signed::new(b, b_neg);
let a_greater_than_b = a > b;
assert_eq!(a_greater_than_b, expected);
}
check(1, false, 1, false, false);
check(1, false, 2, false, false);
check(1, true, 2, false, false);
check(1, false, 2, true, true);
check(1, true, 2, true, true);
check(2, false, 1, false, true);
check(2, true, 1, false, false);
check(2, false, 1, true, true);
check(2, true, 1, true, false);
}
#[test]
fn test_signed_add() {
fn check(a: u32, a_neg: bool, b: u32, b_neg: bool, expected_val: u32, expected_sign: bool) {
let a = Signed::new(a, a_neg);
let b = Signed::new(b, b_neg);
let result = a + b;
assert_eq!(result.value, expected_val);
assert_eq!(result.negative, expected_sign);
}
check(1, false, 2, false, 3, false);
check(1, false, 2, true, 1, true);
check(1, true, 2, false, 1, false);
check(1, true, 2, true, 3, true);
check(2, true, 1, false, 1, true);
check(2, true, 1, true, 3, true);
check(2, false, 1, false, 3, false);
check(2, false, 1, true, 1, false);
}
#[test]
fn test_signed_sub() {
fn check(a: u32, a_neg: bool, b: u32, b_neg: bool, expected_val: u32, expected_sign: bool) {
let a = Signed::new(a, a_neg);
let b = Signed::new(b, b_neg);
let result = a - b;
assert_eq!(result.value, expected_val);
assert_eq!(result.negative, expected_sign);
}
check(1, false, 2, false, 1, true);
check(1, false, 2, true, 3, false);
check(1, true, 2, false, 3, true);
check(1, true, 2, true, 1, false);
check(2, true, 1, false, 3, true);
check(2, true, 1, true, 1, true);
check(2, false, 1, false, 1, false);
check(2, false, 1, true, 3, false);
}
#[test]
fn test_signed_div() {
fn check(a: u32, a_neg: bool, b: u32, b_neg: bool, expected_val: u32, expected_sign: bool) {
let a = Signed::new(a, a_neg);
let b = Signed::new(b, b_neg);
let result = a / b;
assert_eq!(result.value, expected_val);
assert_eq!(result.negative, expected_sign);
}
check(1, false, 2, false, 0, false);
check(1, false, 2, true, 0, true);
check(1, true, 2, false, 0, true);
}
#[test]
fn test_signed_from_and_into_inner() {
let signed_from_42 = Signed::from(42u32);
assert_eq!(signed_from_42.value, 42u32);
assert_eq!(signed_from_42.negative, false);
let signed_value = Signed::new(123u32, true);
assert_eq!(signed_value.into_inner(), 123u32);
}
#[test]
fn test_signed_new_zero_canonicalization() {
let zero_positive = Signed::new(0u32, false);
assert_eq!(zero_positive.value, 0u32);
assert_eq!(zero_positive.negative, false);
let zero_negative_input = Signed::new(0u32, true);
assert_eq!(zero_negative_input.value, 0u32);
assert_eq!(zero_negative_input.negative, false);
let positive_five = Signed::new(5u32, false);
assert_eq!(positive_five.value, 5u32);
assert_eq!(positive_five.negative, false);
let negative_three = Signed::new(3u32, true);
assert_eq!(negative_three.value, 3u32);
assert_eq!(negative_three.negative, true); }
#[test]
fn test_signed_add_with_reference() {
let a = Signed::new(5u32, false); let b = Signed::new(3u32, true); let result = a + &b; assert_eq!(result.value, 2u32);
assert_eq!(result.negative, false);
let a = Signed::new(2u32, false); let b = Signed::new(5u32, true); let result = a + &b; assert_eq!(result.value, 3u32);
assert_eq!(result.negative, true);
let a = Signed::new(4u32, true); let b = Signed::new(6u32, true); let result = a + &b; assert_eq!(result.value, 10u32);
assert_eq!(result.negative, true);
let a = Signed::new(5u32, false); let b = Signed::new(5u32, true); let result = a + &b; assert_eq!(result.value, 0u32);
assert_eq!(result.negative, false);
let a = Signed::new(7u32, true); let b = Signed::new(7u32, false); let result = a + &b; assert_eq!(result.value, 0u32);
assert_eq!(result.negative, false);
let a = Signed::new(12u32, false); let b = Signed::new(12u32, true); let result = a + &b; assert_eq!(result.value, 0u32);
assert_eq!(result.negative, false);
let a = Signed::new(25u32, true); let b = Signed::new(25u32, false); let result = a + &b; assert_eq!(result.value, 0u32);
assert_eq!(result.negative, false);
let a = Signed::new(100u32, false); let b = Signed::new(100u32, true); let result = a + &b; assert_eq!(result.value, 0u32);
assert_eq!(result.negative, false);
let a = Signed::new(1u32, false); let b = Signed::new(1u32, true); let result = a + &b; assert_eq!(result.value, 0u32);
assert_eq!(result.negative, false);
let a = Signed::new(u32::MAX, false); let b = Signed::new(u32::MAX, true); let result = a + &b; assert_eq!(result.value, 0u32);
assert_eq!(result.negative, false); }
#[test]
fn test_signed_add_with_t_reference() {
let a = Signed::new(7u32, false); let result = a + &3u32; assert_eq!(result.value, 10u32);
assert_eq!(result.negative, false);
let a = Signed::new(8u32, true); let result = a + &3u32; assert_eq!(result.value, 5u32);
assert_eq!(result.negative, true);
let a = Signed::new(2u32, true); let result = a + &5u32; assert_eq!(result.value, 3u32);
assert_eq!(result.negative, false);
}
#[test]
fn test_signed_add_assign() {
let mut a = Signed::new(10u32, false); a += &3u32; assert_eq!(a.value, 13u32);
assert_eq!(a.negative, false);
let mut a = Signed::new(15u32, true); a += &5u32; assert_eq!(a.value, 10u32);
assert_eq!(a.negative, true);
let mut a = Signed::new(7u32, true); a += &7u32; assert_eq!(a.value, 0u32);
assert_eq!(a.negative, false);
let mut a = Signed::new(20u32, true); a += &15u32; assert_eq!(a.value, 5u32);
assert_eq!(a.negative, true);
let mut a = Signed::new(5u32, false); a += &0u32; assert_eq!(a.value, 5u32);
assert_eq!(a.negative, false);
let mut a = Signed::new(8u32, true); a += &0u32; assert_eq!(a.value, 8u32);
assert_eq!(a.negative, true);
let mut zero_case = Signed::new(5u32, true); zero_case += &5u32; assert_eq!(zero_case.value, 0u32);
assert_eq!(zero_case.negative, false);
let mut multiple_zero = Signed::new(3u32, true); multiple_zero += &3u32; assert_eq!(multiple_zero.value, 0u32);
assert_eq!(multiple_zero.negative, false);
let mut large_cancel = Signed::new(100u32, true); large_cancel += &100u32; assert_eq!(large_cancel.value, 0u32);
assert_eq!(large_cancel.negative, false);
let mut zero_to_zero = Signed::new(0u32, true); zero_to_zero += &0u32; assert_eq!(zero_to_zero.value, 0u32);
assert_eq!(zero_to_zero.negative, false);
let mut sign_flip1 = Signed::new(8u32, true); sign_flip1 += &12u32; assert_eq!(sign_flip1.value, 4u32);
assert_eq!(sign_flip1.negative, false);
let mut sign_flip2 = Signed::new(3u32, true); sign_flip2 += &7u32; assert_eq!(sign_flip2.value, 4u32);
assert_eq!(sign_flip2.negative, false);
let mut sign_flip3 = Signed::new(1u32, true); sign_flip3 += &10u32; assert_eq!(sign_flip3.value, 9u32);
assert_eq!(sign_flip3.negative, false); }
#[test]
fn test_signed_add_assign_sign_flip_fixed() {
let mut a = Signed::new(8u32, true); a += &12u32; assert_eq!(a.value, 4u32);
assert_eq!(a.negative, false); }
#[test]
fn test_signed_add_assign_sign_flip_negative_to_positive_fixed() {
let mut a = Signed::new(3u32, true); a += &7u32; assert_eq!(a.value, 4u32);
assert_eq!(a.negative, false); }
#[test]
fn test_signed_add_assign_small_negative_large_positive_fixed() {
let mut a = Signed::new(1u32, true); a += &10u32; assert_eq!(a.value, 9u32);
assert_eq!(a.negative, false); }
#[test]
fn test_signed_add_assign_edge_case_maximum_flip_fixed() {
let mut a = Signed::new(1u32, true); a += &u32::MAX; assert_eq!(a.value, u32::MAX - 1);
assert_eq!(a.negative, false); }
#[test]
fn test_signed_add_t_reference_correct_behavior() {
let a = Signed::new(8u32, true); let result = a + &12u32; assert_eq!(result.value, 4u32);
assert_eq!(result.negative, false);
let a = Signed::new(3u32, true); let result = a + &7u32;
assert_eq!(result.value, 4u32);
assert_eq!(result.negative, false);
let a = Signed::new(1u32, true); let result = a + &10u32;
assert_eq!(result.value, 9u32);
assert_eq!(result.negative, false);
let a = Signed::new(5u32, true); let result = a + &5u32;
assert_eq!(result.value, 0u32);
assert_eq!(result.negative, false);
let a = Signed::new(7u32, true); let result = a + &7u32; assert_eq!(result.value, 0u32);
assert_eq!(result.negative, false);
let a = Signed::new(3u32, false); let result = a + &5u32; assert_eq!(result.value, 8u32);
assert_eq!(result.negative, false); }
#[test]
fn test_signed_multiplication() {
let a = Signed::new(4u32, false); let b = Signed::new(3u32, false); let result = a * b; assert_eq!(result.value, 12u32);
assert_eq!(result.negative, false);
let a = Signed::new(5u32, false); let b = Signed::new(2u32, true); let result = a * b; assert_eq!(result.value, 10u32);
assert_eq!(result.negative, true);
let a = Signed::new(6u32, true); let b = Signed::new(4u32, true); let result = a * b; assert_eq!(result.value, 24u32);
assert_eq!(result.negative, false);
let a = Signed::new(7u32, false); let result = a * 2u32; assert_eq!(result.value, 14u32);
assert_eq!(result.negative, false);
let a = Signed::new(3u32, true); let result = a * 5u32; assert_eq!(result.value, 15u32);
assert_eq!(result.negative, true);
let a = Signed::new(8u32, false); let result = a * &3u32; assert_eq!(result.value, 24u32);
assert_eq!(result.negative, false);
let a = Signed::new(9u32, true); let result = a * &2u32; assert_eq!(result.value, 18u32);
assert_eq!(result.negative, true);
}
#[test]
fn test_signed_partial_cmp_edge_cases() {
use core::cmp::Ordering;
let a = Signed::new(5u32, false);
let b = Signed::new(5u32, false);
assert_eq!(a.partial_cmp(&b), Some(Ordering::Equal));
let a = Signed::new(5u32, true);
let b = Signed::new(5u32, true);
assert_eq!(a.partial_cmp(&b), Some(Ordering::Equal));
let a = Signed::new(10u32, true); let b = Signed::new(1u32, false); assert_eq!(a.partial_cmp(&b), Some(Ordering::Less));
let a = Signed::new(1u32, false); let b = Signed::new(10u32, true); assert_eq!(a.partial_cmp(&b), Some(Ordering::Greater));
let a = Signed::new(3u32, true); let b = Signed::new(7u32, true); assert_eq!(a.partial_cmp(&b), Some(Ordering::Greater)); }
}