use crate::{Choice, CtAssign, CtSelect};
use core::num::{
NonZeroI8, NonZeroI16, NonZeroI32, NonZeroI64, NonZeroI128, NonZeroIsize, NonZeroU8,
NonZeroU16, NonZeroU32, NonZeroU64, NonZeroU128, NonZeroUsize,
};
pub trait CtNeg: Sized {
#[must_use]
fn ct_neg(&self, choice: Choice) -> Self;
fn ct_neg_assign(&mut self, choice: Choice) {
*self = self.ct_neg(choice);
}
}
macro_rules! impl_signed_ct_neg {
( $($int:ty),+ ) => {
$(
impl CtNeg for $int {
#[inline]
fn ct_neg(&self, choice: Choice) -> Self {
self.ct_select(&self.wrapping_neg(), choice)
}
#[inline]
fn ct_neg_assign(&mut self, choice: Choice) {
self.ct_assign(&self.wrapping_neg(), choice)
}
}
)+
};
}
macro_rules! impl_unsigned_ct_neg {
( $($uint:ty),+ ) => {
$(
impl CtNeg for $uint {
#[inline]
fn ct_neg(&self, choice: Choice) -> Self {
self.ct_select(&self.wrapping_neg(), choice)
}
#[inline]
fn ct_neg_assign(&mut self, choice: Choice) {
self.ct_assign(&self.wrapping_neg(), choice)
}
}
)+
};
}
impl_signed_ct_neg!(
i8,
i16,
i32,
i64,
i128,
isize,
NonZeroI8,
NonZeroI16,
NonZeroI32,
NonZeroI64,
NonZeroI128,
NonZeroIsize
);
impl_unsigned_ct_neg!(u8, u16, u32, u64, u128, usize);
macro_rules! impl_ct_neg_for_unsigned_nonzero {
( $($nzuint:ident),+ ) => {
$(
impl CtNeg for $nzuint {
#[inline]
fn ct_neg(&self, choice: Choice) -> Self {
let n = self.get().ct_select(&self.get().wrapping_neg(), choice);
$nzuint::new(n).expect("should be non-zero")
}
}
)+
};
}
impl_ct_neg_for_unsigned_nonzero!(
NonZeroU8,
NonZeroU16,
NonZeroU32,
NonZeroU64,
NonZeroU128,
NonZeroUsize
);
#[cfg(test)]
mod tests {
macro_rules! signed_ct_neg_tests {
( $($int:ident),+ ) => {
$(
mod $int {
use crate::{Choice, CtNeg};
#[test]
fn ct_neg() {
let n: $int = 42;
assert_eq!(n, n.ct_neg(Choice::FALSE));
assert_eq!(-n, n.ct_neg(Choice::TRUE));
}
#[test]
fn ct_neg_assign() {
let n: $int = 42;
let mut x = n;
x.ct_neg_assign(Choice::FALSE);
assert_eq!(n, x);
x.ct_neg_assign(Choice::TRUE);
assert_eq!(-n, x);
}
#[test]
fn ct_neg_min() {
let min = <$int>::MIN;
for choice in [Choice::FALSE, Choice::TRUE] {
assert_eq!(min.ct_neg(choice), min);
let mut value = min;
value.ct_neg_assign(choice);
assert_eq!(value, min);
}
}
}
)+
};
}
macro_rules! unsigned_ct_neg_tests {
( $($uint:ident),+ ) => {
$(
mod $uint {
use crate::{Choice, CtNeg};
#[test]
fn ct_neg() {
let n: $uint = 42;
assert_eq!(n, n.ct_neg(Choice::FALSE));
assert_eq!(<$uint>::MAX - n + 1, n.ct_neg(Choice::TRUE));
}
#[test]
fn ct_neg_assign() {
let n: $uint = 42;
let mut x = n;
x.ct_neg_assign(Choice::FALSE);
assert_eq!(n, x);
x.ct_neg_assign(Choice::TRUE);
assert_eq!(<$uint>::MAX - n + 1, x);
}
}
)+
};
}
signed_ct_neg_tests!(i8, i16, i32, i64, i128, isize);
unsigned_ct_neg_tests!(u8, u16, u32, u64, u128, usize);
macro_rules! nonzero_signed_ct_neg_tests {
($(($test:ident, $ty:ty, $int:ty)),+ $(,)?) => {
$(
#[test]
fn $test() {
use crate::{Choice, CtNeg};
let min = <$ty>::new(<$int>::MIN).expect("MIN is non-zero");
for choice in [Choice::FALSE, Choice::TRUE] {
assert_eq!(min.ct_neg(choice), min);
let mut value = min;
value.ct_neg_assign(choice);
assert_eq!(value, min);
}
}
)+
};
}
nonzero_signed_ct_neg_tests!(
(nonzero_i8_min, core::num::NonZeroI8, i8),
(nonzero_i16_min, core::num::NonZeroI16, i16),
(nonzero_i32_min, core::num::NonZeroI32, i32),
(nonzero_i64_min, core::num::NonZeroI64, i64),
(nonzero_i128_min, core::num::NonZeroI128, i128),
(nonzero_isize_min, core::num::NonZeroIsize, isize),
);
}