use smallvec::SmallVec;
#[must_use]
pub fn is_well_formed(values: &[usize]) -> bool {
match values.last() {
None => true,
Some(&value) => value != 0,
}
}
#[must_use]
pub fn is_well_formed_non_zero(values: &[usize]) -> bool {
values.last().is_some_and(|&last| last != 0)
}
#[must_use]
#[inline]
pub fn add_2(left_high: usize, left_low: usize, right_high: usize, right_low: usize) -> (usize, usize) {
let (low, carry) = left_low.overflowing_add(right_low);
let (high, _) = left_high.carrying_add(right_high, carry);
(high, low)
}
#[must_use]
#[inline]
pub fn sub_2(left_high: usize, left_low: usize, right_high: usize, right_low: usize) -> (usize, usize) {
let (low, borrow) = left_low.overflowing_sub(right_low);
let (high, _) = left_high.borrowing_sub(right_high, borrow);
(high, low)
}
const BLOCK: usize = 8;
#[cfg(target_arch = "x86_64")]
type Flag = u8;
#[cfg(not(target_arch = "x86_64"))]
type Flag = bool;
#[cfg(target_arch = "x86_64")]
#[inline(always)]
fn sub_word(left: usize, right: usize, borrow: Flag) -> (usize, Flag) {
let mut value = 0;
#[allow(unused_unsafe)]
let borrow = unsafe { std::arch::x86_64::_subborrow_u64(borrow, left as u64, right as u64, &mut value) };
(value as usize, borrow)
}
#[cfg(not(target_arch = "x86_64"))]
#[inline(always)]
fn sub_word(left: usize, right: usize, borrow: Flag) -> (usize, Flag) {
left.borrowing_sub(right, borrow)
}
#[cfg(target_arch = "x86_64")]
#[inline(always)]
fn add_word(left: usize, right: usize, carry: Flag) -> (usize, Flag) {
let mut value = 0;
#[allow(unused_unsafe)]
let carry = unsafe { std::arch::x86_64::_addcarry_u64(carry, left as u64, right as u64, &mut value) };
(value as usize, carry)
}
#[cfg(not(target_arch = "x86_64"))]
#[inline(always)]
fn add_word(left: usize, right: usize, carry: Flag) -> (usize, Flag) {
left.carrying_add(right, carry)
}
#[inline]
pub fn sub_n(wp: &mut [usize], xp: &[usize], yp: &[usize], n: usize) -> usize {
assert!(n >= 1, "should not be called on an empty operand");
assert!(wp.len() >= n && xp.len() >= n && yp.len() >= n, "operand too short");
let (wp, xp, yp) = (&mut wp[..n], &xp[..n], &yp[..n]);
let mut borrow = Flag::default();
if n < BLOCK {
for ((target, &left), &right) in wp.iter_mut().zip(xp).zip(yp) {
(*target, borrow) = sub_word(left, right, borrow);
}
return usize::from(borrow);
}
let (w_blocks, w_tail) = wp.as_chunks_mut::<BLOCK>();
let (x_blocks, x_tail) = xp.as_chunks::<BLOCK>();
let (y_blocks, y_tail) = yp.as_chunks::<BLOCK>();
for ((target, left), right) in w_blocks.iter_mut().zip(x_blocks).zip(y_blocks) {
for word in 0..BLOCK {
(target[word], borrow) = sub_word(left[word], right[word], borrow);
}
}
for ((target, &left), &right) in w_tail.iter_mut().zip(x_tail).zip(y_tail) {
(*target, borrow) = sub_word(left, right, borrow);
}
usize::from(borrow)
}
#[inline]
pub fn mul_1(wp: &mut [usize], xp: &[usize], vl: usize) -> usize {
let n = xp.len();
assert!(n >= 1, "should not be called on an empty operand");
assert!(wp.len() >= n, "operand too short");
let wp = &mut wp[..n];
if n < BLOCK {
return mul_1_words(wp, xp, vl, 0);
}
mul_1_long(wp, xp, vl)
}
fn mul_1_long(wp: &mut [usize], xp: &[usize], vl: usize) -> usize {
#[cfg(target_arch = "x86_64")]
if has_wide_multiply() {
return unsafe { mul_1_wide(wp, xp, vl) };
}
mul_1_blocks(wp, xp, vl)
}
#[inline(always)]
fn mul_1_words(wp: &mut [usize], xp: &[usize], vl: usize, mut carry: usize) -> usize {
for (target, &value) in wp.iter_mut().zip(xp) {
(*target, carry) = value.carrying_mul(vl, carry);
}
carry
}
#[inline(always)]
fn mul_1_blocks(wp: &mut [usize], xp: &[usize], vl: usize) -> usize {
let (w_blocks, w_tail) = wp.as_chunks_mut::<BLOCK>();
let (x_blocks, x_tail) = xp.as_chunks::<BLOCK>();
let mut carry = 0;
for (target, value) in w_blocks.iter_mut().zip(x_blocks) {
carry = mul_1_words(target, value, vl, carry);
}
mul_1_words(w_tail, x_tail, vl, carry)
}
#[inline]
pub fn addmul_1(wp: &mut [usize], xp: &[usize], vl: usize) -> usize {
let n = xp.len();
assert!(n >= 1, "should not be called on an empty operand");
assert!(wp.len() >= n, "operand too short");
let wp = &mut wp[..n];
if n < BLOCK {
return addmul_1_words(wp, xp, vl, 0);
}
let (w_blocks, w_tail) = wp.as_chunks_mut::<BLOCK>();
let (x_blocks, x_tail) = xp.as_chunks::<BLOCK>();
let mut carry = 0;
for (target, value) in w_blocks.iter_mut().zip(x_blocks) {
carry = addmul_1_words(target, value, vl, carry);
}
addmul_1_words(w_tail, x_tail, vl, carry)
}
#[inline]
fn addmul_1_words(wp: &mut [usize], xp: &[usize], vl: usize, mut carry: usize) -> usize {
for (target, &value) in wp.iter_mut().zip(xp) {
(*target, carry) = value.carrying_mul_add(vl, *target, carry);
}
carry
}
#[inline]
pub fn submul_slice(value: &mut [usize], rhs: &[usize], rhs_value: usize) -> usize {
debug_assert_eq!(value.len(), rhs.len());
submul_1(value, rhs, rhs_value)
}
#[inline]
pub fn submul_1(wp: &mut [usize], xp: &[usize], vl: usize) -> usize {
let n = xp.len();
assert!(n >= 1, "should not be called on an empty operand");
assert!(wp.len() >= n, "operand too short");
let wp = &mut wp[..n];
if n < BLOCK {
return submul_1_words(wp, xp, vl, 0);
}
let (w_blocks, w_tail) = wp.as_chunks_mut::<BLOCK>();
let (x_blocks, x_tail) = xp.as_chunks::<BLOCK>();
let mut borrow = 0;
for (target, value) in w_blocks.iter_mut().zip(x_blocks) {
borrow = submul_1_words(target, value, vl, borrow);
}
submul_1_words(w_tail, x_tail, vl, borrow)
}
#[inline]
fn submul_1_words(wp: &mut [usize], xp: &[usize], vl: usize, mut borrow: usize) -> usize {
for (target, &value) in wp.iter_mut().zip(xp) {
let (low, high) = value.carrying_mul(vl, borrow);
let (value, underflow) = target.overflowing_sub(low);
*target = value;
borrow = high + underflow as usize;
}
borrow
}
#[cfg(target_arch = "x86_64")]
#[inline]
fn has_wide_multiply() -> bool {
std::arch::is_x86_feature_detected!("bmi2")
}
#[cfg(target_arch = "x86_64")]
#[target_feature(enable = "bmi2")]
fn mul_1_wide(wp: &mut [usize], xp: &[usize], vl: usize) -> usize {
mul_1_blocks(wp, xp, vl)
}
#[inline]
pub fn to_twos_complement<const S: usize>(values: &mut SmallVec<[usize; S]>) {
let Some(lowest) = values.iter().position(|&value| value != 0) else {
debug_assert!(false, "should not be called on a zero value");
values.clear();
return;
};
values[lowest] = values[lowest].wrapping_neg();
for value in &mut values[lowest + 1..] {
*value = !*value;
}
while let Some(0) = values.last() {
values.pop();
}
}
#[must_use]
#[inline]
pub fn add_assign_slice(values: &mut [usize], rhs: &[usize]) -> bool {
debug_assert_eq!(values.len(), rhs.len());
let shared = values.len().min(rhs.len());
let (values, rhs) = (&mut values[..shared], &rhs[..shared]);
let mut carry = Flag::default();
if shared < BLOCK {
for (value, &rhs_value) in values.iter_mut().zip(rhs) {
(*value, carry) = add_word(*value, rhs_value, carry);
}
return usize::from(carry) == 1;
}
let (v_blocks, v_tail) = values.as_chunks_mut::<BLOCK>();
let (r_blocks, r_tail) = rhs.as_chunks::<BLOCK>();
for (value, rhs_value) in v_blocks.iter_mut().zip(r_blocks) {
for word in 0..BLOCK {
(value[word], carry) = add_word(value[word], rhs_value[word], carry);
}
}
for (value, &rhs_value) in v_tail.iter_mut().zip(r_tail) {
(*value, carry) = add_word(*value, rhs_value, carry);
}
usize::from(carry) == 1
}
#[inline]
pub fn sub_assign_slice(values: &mut [usize], rhs: &[usize]) -> bool {
debug_assert_eq!(values.len(), rhs.len());
let shared = values.len().min(rhs.len());
let (values, rhs) = (&mut values[..shared], &rhs[..shared]);
let mut borrow = Flag::default();
if shared < BLOCK {
for (value, &rhs_value) in values.iter_mut().zip(rhs) {
(*value, borrow) = sub_word(*value, rhs_value, borrow);
}
return usize::from(borrow) == 1;
}
let (v_blocks, v_tail) = values.as_chunks_mut::<BLOCK>();
let (r_blocks, r_tail) = rhs.as_chunks::<BLOCK>();
for (value, rhs_value) in v_blocks.iter_mut().zip(r_blocks) {
for word in 0..BLOCK {
(value[word], borrow) = sub_word(value[word], rhs_value[word], borrow);
}
}
for (value, &rhs_value) in v_tail.iter_mut().zip(r_tail) {
(*value, borrow) = sub_word(*value, rhs_value, borrow);
}
usize::from(borrow) == 1
}
#[inline]
pub fn sub_from_slice(values: &mut [usize], rhs: &[usize]) -> bool {
debug_assert_eq!(values.len(), rhs.len());
let shared = values.len().min(rhs.len());
let (values, rhs) = (&mut values[..shared], &rhs[..shared]);
let mut borrow = Flag::default();
if shared < BLOCK {
for (value, &rhs_value) in values.iter_mut().zip(rhs) {
(*value, borrow) = sub_word(rhs_value, *value, borrow);
}
return usize::from(borrow) == 1;
}
let (v_blocks, v_tail) = values.as_chunks_mut::<BLOCK>();
let (r_blocks, r_tail) = rhs.as_chunks::<BLOCK>();
for (value, rhs_value) in v_blocks.iter_mut().zip(r_blocks) {
for word in 0..BLOCK {
(value[word], borrow) = sub_word(rhs_value[word], value[word], borrow);
}
}
for (value, &rhs_value) in v_tail.iter_mut().zip(r_tail) {
(*value, borrow) = sub_word(rhs_value, *value, borrow);
}
usize::from(borrow) == 1
}
#[inline]
pub fn carrying_add_mut(value: &mut usize, rhs: usize, carry: &mut bool) {
let (new_value, new_carry) = value.carrying_add(rhs, *carry);
*value = new_value;
*carry = new_carry;
}
#[inline]
pub fn borrowing_sub_mut(value: &mut usize, rhs: usize, carry: &mut bool) {
let (new_value, new_carry) = value.borrowing_sub(rhs, *carry);
*value = new_value;
*carry = new_carry;
}
#[cfg(test)]
mod test {
use smallvec::{smallvec, SmallVec};
use crate::integer::big::ops::building_blocks::{add_2, addmul_1, is_well_formed, mul_1, sub_2, sub_n, submul_1, to_twos_complement};
const _: () = assert!(usize::BITS <= 64);
#[test]
fn test_is_well_formed() {
pub type SV = SmallVec<[usize; 8]>;
let x: SV = smallvec![];
assert!(is_well_formed(&x));
let x: SV = smallvec![0, 1];
assert!(is_well_formed(&x));
let x: SV = smallvec![648, 64884, 1];
assert!(is_well_formed(&x));
let x: SV = smallvec![564, 6448, 84, 0];
assert!(!is_well_formed(&x));
let x: SV = smallvec![0];
assert!(!is_well_formed(&x));
let x: SV = smallvec![0, 0, 0, 0];
assert!(!is_well_formed(&x));
}
#[test]
fn test_add_2() {
assert_eq!(add_2(0, 0, 0, 0), (0, 0));
assert_eq!(add_2(1, 2, 3, 4), (4, 6));
assert_eq!(add_2(0, usize::MAX, 0, 1), (1, 0));
assert_eq!(add_2(3, usize::MAX, 4, usize::MAX), (8, usize::MAX - 1));
assert_eq!(add_2(usize::MAX, 0, 1, 0), (0, 0));
assert_eq!(add_2(usize::MAX, usize::MAX, 0, 1), (0, 0));
assert_eq!(add_2(usize::MAX, usize::MAX, usize::MAX, usize::MAX), (usize::MAX, usize::MAX - 1));
}
#[test]
fn test_sub_2() {
assert_eq!(sub_2(0, 0, 0, 0), (0, 0));
assert_eq!(sub_2(4, 6, 1, 2), (3, 4));
assert_eq!(sub_2(1, 0, 0, 1), (0, usize::MAX));
assert_eq!(sub_2(8, 0, 4, usize::MAX), (3, 1));
assert_eq!(sub_2(0, 0, 0, 1), (usize::MAX, usize::MAX));
assert_eq!(sub_2(0, 0, 1, 0), (usize::MAX, 0));
}
#[test]
fn test_sub_n() {
type SV = SmallVec<[usize; 4]>;
let mut x: SV = smallvec![0, 0];
let carry = sub_n(&mut x, &[2, 3], &[1, 1], 2);
assert_eq!(carry, 0);
let expected: SV = smallvec![1, 2];
assert_eq!(x, expected);
let mut x: SV = smallvec![0, 0];
let carry = sub_n(&mut x, &[2, 3], &[4, 1], 2);
assert_eq!(carry, 0);
let expected: SV = smallvec![usize::MAX - 1, 1];
assert_eq!(x, expected);
let mut x: SV = smallvec![0, 0];
let carry = sub_n(&mut x, &[4, 1], &[4, 1], 2);
assert_eq!(carry, 0);
let expected: SV = smallvec![0, 0];
assert_eq!(x, expected);
let mut x: SV = smallvec![0];
let carry = sub_n(&mut x, &[0], &[1], 1);
assert_eq!(carry, 1);
let expected: SV = smallvec![usize::MAX];
assert_eq!(x, expected);
to_twos_complement(&mut x);
let expected: SV = smallvec![1];
assert_eq!(x, expected);
let mut x: SV = smallvec![0; 3];
assert_eq!(sub_n(&mut x, &[0, 0, 0], &[1, 0, 0], 3), 1);
let expected: SV = smallvec![usize::MAX; 3];
assert_eq!(x, expected);
let mut x: SV = smallvec![7, 7, 7];
assert_eq!(sub_n(&mut x, &[5, 5, 5], &[1, 1, 1], 2), 0);
let expected: SV = smallvec![4, 4, 7];
assert_eq!(x, expected);
}
#[test]
fn test_to_twos_complement() {
type SV = SmallVec<[usize; 4]>;
let mut value: SV = smallvec![usize::MAX];
to_twos_complement(&mut value);
let expected: SV = smallvec![1];
assert_eq!(value, expected);
let mut value: SV = smallvec![usize::MAX - 1];
to_twos_complement(&mut value);
let expected: SV = smallvec![2];
assert_eq!(value, expected);
let mut value: SV = smallvec![usize::MAX - 2, usize::MAX];
to_twos_complement(&mut value);
let expected: SV = smallvec![3];
assert_eq!(value, expected);
let mut value: SV = smallvec![usize::MAX - 3, usize::MAX, usize::MAX];
to_twos_complement(&mut value);
let expected: SV = smallvec![4];
assert_eq!(value, expected);
let mut value: SV = smallvec![0, 0, 1];
to_twos_complement(&mut value);
let expected: SV = smallvec![0, 0, usize::MAX];
assert_eq!(value, expected);
}
fn cross_check_operands() -> Vec<(Vec<usize>, Vec<usize>)> {
let patterns: Vec<Vec<usize>> = vec![
vec![0],
vec![1],
vec![usize::MAX],
vec![0, 0],
vec![1, 0],
vec![0, 1],
vec![usize::MAX, usize::MAX],
vec![usize::MAX, 0],
vec![0, usize::MAX],
vec![1, 2, 3],
vec![usize::MAX, usize::MAX, usize::MAX],
vec![usize::MAX, 0, usize::MAX],
vec![0, usize::MAX, 0],
vec![1, usize::MAX, usize::MAX, usize::MAX],
vec![usize::MAX, usize::MAX, usize::MAX, usize::MAX],
vec![usize::MAX; 5],
vec![usize::MAX; 6],
vec![usize::MAX; 7],
(1..=8).collect(),
(1..=9).map(|i| usize::MAX - i).collect(),
vec![usize::MAX; 11],
(0..17).map(|i| usize::MAX / (i + 1)).collect(),
];
let mut combinations = Vec::new();
for left in &patterns {
for right in &patterns {
if left.len() == right.len() {
combinations.push((left.clone(), right.clone()));
}
}
}
combinations
}
fn cross_check_multipliers() -> Vec<usize> {
vec![0, 1, 2, usize::MAX / 2, usize::MAX - 1, usize::MAX]
}
fn cross_check_targets(n: usize) -> Vec<Vec<usize>> {
vec![
vec![0; n],
vec![usize::MAX; n],
(0..n).map(|i| i * 7 + 1).collect(),
(0..n).map(|i| usize::MAX - i).collect(),
]
}
#[test]
fn test_mul_1() {
let mut x = vec![0; 3];
assert_eq!(mul_1(&mut x, &[1, 2, 3], 0), 0);
assert_eq!(x, vec![0, 0, 0]);
assert_eq!(mul_1(&mut x, &[1, 2, 3], 1), 0);
assert_eq!(x, vec![1, 2, 3]);
assert_eq!(mul_1(&mut x, &[usize::MAX, usize::MAX, usize::MAX], usize::MAX), usize::MAX - 1);
assert_eq!(x, vec![1, usize::MAX, usize::MAX]);
let mut x = vec![0; 1];
assert_eq!(mul_1(&mut x, &[usize::MAX], 2), 1);
assert_eq!(x, vec![usize::MAX - 1]);
}
#[test]
fn test_addmul_1() {
let mut x = vec![usize::MAX; 3];
assert_eq!(addmul_1(&mut x, &[1, 0, 0], 1), 1);
assert_eq!(x, vec![0, 0, 0]);
let mut x = vec![1, 2, 3];
assert_eq!(addmul_1(&mut x, &[1, 1, 1], 0), 0);
assert_eq!(x, vec![1, 2, 3]);
let mut x = vec![usize::MAX; 2];
assert_eq!(addmul_1(&mut x, &[usize::MAX, usize::MAX], usize::MAX), usize::MAX);
assert_eq!(x, vec![0, usize::MAX]);
}
#[test]
fn test_submul_1() {
let mut x = vec![0; 3];
assert_eq!(submul_1(&mut x, &[1, 0, 0], 1), 1);
assert_eq!(x, vec![usize::MAX; 3]);
let mut x = vec![1, 2, 3];
assert_eq!(submul_1(&mut x, &[1, 1, 1], 0), 0);
assert_eq!(x, vec![1, 2, 3]);
let mut x = vec![usize::MAX; 3];
assert_eq!(submul_1(&mut x, &[usize::MAX, usize::MAX, usize::MAX], 1), 0);
assert_eq!(x, vec![0, 0, 0]);
}
#[test]
#[cfg(target_pointer_width = "64")]
fn test_against_model() {
fn to_u128(words: &[usize]) -> u128 {
words.iter().rev().fold(0_u128, |total, &word| (total << 64) | word as u128)
}
let words = [0_usize, 1, 2, 3, usize::MAX / 3, usize::MAX / 2, usize::MAX - 1, usize::MAX];
for &left_low in &words {
for &left_high in &words {
for &right_low in &words {
for &right_high in &words {
let left = [left_low, left_high];
let right = [right_low, right_high];
for n in 1..=2 {
let mut target = vec![0; n];
let borrow = sub_n(&mut target, &left, &right, n);
let mask = if n == 1 { u64::MAX as u128 } else { u128::MAX };
let (left, right) = (to_u128(&left[..n]), to_u128(&right[..n]));
assert_eq!(to_u128(&target), left.wrapping_sub(right) & mask);
assert_eq!(borrow, (left < right) as usize);
}
}
}
}
}
for &value in &words {
for &multiplier in &words {
for &initial in &words {
let mut target = [0];
let high = mul_1(&mut target, &[value], multiplier);
assert_eq!(
to_u128(&target) + ((high as u128) << 64),
value as u128 * multiplier as u128,
);
let mut target = [initial];
let carry = addmul_1(&mut target, &[value], multiplier);
assert_eq!(
to_u128(&target) + ((carry as u128) << 64),
initial as u128 + value as u128 * multiplier as u128,
);
let mut target = [initial];
let borrow = submul_1(&mut target, &[value], multiplier);
assert_eq!(
initial as u128 + ((borrow as u128) << 64),
to_u128(&target) + value as u128 * multiplier as u128,
);
}
}
}
}
mod against_reference {
use crate::integer::big::ops::building_blocks::{add_assign_slice, addmul_1, mul_1, sub_assign_slice, sub_from_slice, sub_n, submul_1};
use super::{cross_check_multipliers, cross_check_operands, cross_check_targets};
fn sub_n_reference(wp: &mut [usize], xp: &[usize], yp: &[usize], n: usize) -> usize {
let mut borrow = false;
for ((target, &left), &right) in wp[..n].iter_mut().zip(&xp[..n]).zip(&yp[..n]) {
(*target, borrow) = left.borrowing_sub(right, borrow);
}
borrow as usize
}
fn add_assign_reference(values: &mut [usize], rhs: &[usize]) -> bool {
let mut carry = false;
for (value, &rhs_value) in values.iter_mut().zip(rhs) {
(*value, carry) = value.carrying_add(rhs_value, carry);
}
carry
}
fn sub_assign_reference(values: &mut [usize], rhs: &[usize]) -> bool {
let mut borrow = false;
for (value, &rhs_value) in values.iter_mut().zip(rhs) {
(*value, borrow) = value.borrowing_sub(rhs_value, borrow);
}
borrow
}
fn mul_1_reference(wp: &mut [usize], xp: &[usize], vl: usize) -> usize {
let mut carry = 0;
for (target, &value) in wp.iter_mut().zip(xp) {
(*target, carry) = value.carrying_mul(vl, carry);
}
carry
}
fn addmul_1_reference(wp: &mut [usize], xp: &[usize], vl: usize) -> usize {
let mut carry = 0;
for (target, &value) in wp.iter_mut().zip(xp) {
let (low, high) = value.carrying_mul(vl, carry);
let (value, overflow) = target.overflowing_add(low);
*target = value;
carry = high + overflow as usize;
}
carry
}
fn submul_1_reference(wp: &mut [usize], xp: &[usize], vl: usize) -> usize {
let mut borrow = 0;
for (target, &value) in wp.iter_mut().zip(xp) {
let (low, high) = value.carrying_mul(vl, borrow);
let (value, underflow) = target.overflowing_sub(low);
*target = value;
borrow = high + underflow as usize;
}
borrow
}
#[test]
fn test_sub_n() {
for (left, right) in cross_check_operands() {
for n in 1..=left.len() {
let mut from_routine = vec![0x5a; left.len() + 3];
let mut from_reference = vec![0x5a; left.len() + 3];
let routine = sub_n(&mut from_routine, &left, &right, n);
let reference = sub_n_reference(&mut from_reference, &left, &right, n);
assert_eq!(routine, reference, "{left:?} - {right:?}, n = {n}");
assert_eq!(from_routine, from_reference, "{left:?} - {right:?}, n = {n}");
}
}
}
#[test]
fn test_add_assign_slice() {
for (left, right) in cross_check_operands() {
for n in 1..=left.len() {
let mut from_routine = left.clone();
let mut from_reference = left.clone();
let routine = add_assign_slice(&mut from_routine[..n], &right[..n]);
let reference = add_assign_reference(&mut from_reference[..n], &right[..n]);
assert_eq!(routine, reference, "{left:?} += {right:?}, n = {n}");
assert_eq!(from_routine, from_reference, "{left:?} += {right:?}, n = {n}");
}
}
}
#[test]
fn test_sub_assign_slice() {
for (left, right) in cross_check_operands() {
for n in 1..=left.len() {
let mut from_routine = left.clone();
let mut from_reference = left.clone();
let routine = sub_assign_slice(&mut from_routine[..n], &right[..n]);
let reference = sub_assign_reference(&mut from_reference[..n], &right[..n]);
assert_eq!(routine, reference, "{left:?} -= {right:?}, n = {n}");
assert_eq!(from_routine, from_reference, "{left:?} -= {right:?}, n = {n}");
}
}
}
#[test]
fn test_sub_from_slice() {
for (left, right) in cross_check_operands() {
for n in 1..=left.len() {
let mut from_routine = left.clone();
let mut from_reference = right.clone();
let routine = sub_from_slice(&mut from_routine[..n], &right[..n]);
let reference = sub_assign_reference(&mut from_reference[..n], &left[..n]);
assert_eq!(routine, reference, "{right:?} - {left:?}, n = {n}");
assert_eq!(from_routine[..n], from_reference[..n], "{right:?} - {left:?}, n = {n}");
}
}
}
#[test]
fn test_mul_1() {
for (left, _) in cross_check_operands() {
for multiplier in cross_check_multipliers() {
for n in 1..=left.len() {
let mut from_routine = vec![0x5a; left.len() + 3];
let mut from_reference = vec![0x5a; left.len() + 3];
let routine = mul_1(&mut from_routine, &left[..n], multiplier);
let reference = mul_1_reference(&mut from_reference[..n], &left[..n], multiplier);
assert_eq!(routine, reference, "{left:?} * {multiplier}, n = {n}");
assert_eq!(from_routine, from_reference, "{left:?} * {multiplier}, n = {n}");
}
}
}
}
#[test]
fn test_addmul_1() {
for (left, _) in cross_check_operands() {
for multiplier in cross_check_multipliers() {
for n in 1..=left.len() {
for initial in cross_check_targets(left.len() + 3) {
let mut from_routine = initial.clone();
let mut from_reference = initial.clone();
let routine = addmul_1(&mut from_routine, &left[..n], multiplier);
let reference = addmul_1_reference(&mut from_reference[..n], &left[..n], multiplier);
assert_eq!(routine, reference, "{initial:?} += {left:?} * {multiplier}, n = {n}");
assert_eq!(from_routine, from_reference, "{initial:?} += {left:?} * {multiplier}, n = {n}");
}
}
}
}
}
#[test]
fn test_submul_1() {
for (left, _) in cross_check_operands() {
for multiplier in cross_check_multipliers() {
for n in 1..=left.len() {
for initial in cross_check_targets(left.len() + 3) {
let mut from_routine = initial.clone();
let mut from_reference = initial.clone();
let routine = submul_1(&mut from_routine, &left[..n], multiplier);
let reference = submul_1_reference(&mut from_reference[..n], &left[..n], multiplier);
assert_eq!(routine, reference, "{initial:?} -= {left:?} * {multiplier}, n = {n}");
assert_eq!(from_routine, from_reference, "{initial:?} -= {left:?} * {multiplier}, n = {n}");
}
}
}
}
}
}
}