use std::arch::asm;
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) {
#[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
fn inner(left_high: usize, left_low: usize, right_high: usize, right_low: usize) -> (usize, usize) {
let mut high_out;
let mut low_out;
unsafe {
asm!(
"add {0}, {right_low}",
"adc {1}, {right_high}",
inout(reg) left_low => low_out,
inout(reg) left_high => high_out,
right_low = in(reg) right_low,
right_high = in(reg) right_high,
);
}
(high_out, low_out)
}
inner(left_high, left_low, right_high, right_low)
}
#[must_use]
#[inline]
pub fn sub_2(left_high: usize, left_low: usize, right_high: usize, right_low: usize) -> (usize, usize) {
#[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
fn inner(left_high: usize, left_low: usize, right_high: usize, right_low: usize) -> (usize, usize) {
let mut high_out;
let mut low_out;
unsafe {
asm!(
"sub {0}, {right_low}",
"sbb {1}, {right_high}",
inout(reg) left_low => low_out,
inout(reg) left_high => high_out,
right_low = in(reg) right_low,
right_high = in(reg) right_high,
);
}
(high_out, low_out)
}
inner(left_high, left_low, right_high, right_low)
}
#[inline]
#[allow(dead_code)]
pub unsafe fn add_n(target: &mut [usize], left: &[usize], right: &[usize], size: i32) -> usize {
extern "C" {
fn ramp_add_n(wp: *mut usize, xp: *const usize, yp: *const usize, n: i32) -> usize;
}
debug_assert!(size >= 1);
ramp_add_n(target.as_mut_ptr(), left.as_ptr(), right.as_ptr(), size)
}
#[inline]
pub unsafe fn sub_n(wp: &mut [usize], xp: &[usize], yp: &[usize], n: i32) -> usize {
extern "C" {
fn ramp_sub_n(wp: *mut usize, xp: *const usize, yp: *const usize, n: i32) -> usize;
}
debug_assert!(n >= 1);
ramp_sub_n(wp.as_mut_ptr(), xp.as_ptr(), yp.as_ptr(), n)
}
#[inline]
pub unsafe fn mul_1(wp: &mut [usize], xp: &[usize], vl: usize) -> usize {
extern "C" {
fn ramp_mul_1(wp: *mut usize, xp: *const usize, n: i32, vl: usize) -> usize;
}
ramp_mul_1(wp.as_mut_ptr(), xp.as_ptr(), xp.len() as i32, vl)
}
#[inline]
pub unsafe fn addmul_1(wp: &mut [usize], xp: &[usize], n: i32, vl: usize) -> usize {
extern "C" {
fn ramp_addmul_1(wp: *mut usize, xp: *const usize, n: i32, vl: usize) -> usize;
}
ramp_addmul_1(wp.as_mut_ptr(), xp.as_ptr(), n, vl)
}
#[inline]
pub unsafe fn submul_slice(value: &mut [usize], rhs: &[usize], rhs_value: usize) -> usize {
debug_assert_eq!(value.len(), rhs.len());
submul_1(value, rhs, value.len() as i32, rhs_value)
}
#[inline]
pub unsafe fn submul_1(wp: &mut [usize], xp: &[usize], n: i32, vl: usize) -> usize {
extern "C" {
fn ramp_submul_1(wp: *mut usize, xp: *const usize, n: i32, vl: usize) -> usize;
}
ramp_submul_1(wp.as_mut_ptr(), xp.as_ptr(), n, vl)
}
#[inline]
pub fn to_twos_complement<const S: usize>(values: &mut SmallVec<[usize; S]>) {
let mut carry = true;
for value in values.iter_mut() {
borrowing_sub_mut(value, 0, &mut carry);
*value = !*value;
}
if carry {
values.push(0);
} else {
while let Some(0) = values.last() {
values.pop();
}
}
}
#[must_use]
#[inline]
pub unsafe fn add_assign_slice(values: &mut [usize], rhs: &[usize]) -> bool {
debug_assert_eq!(values.len(), rhs.len());
let mut carry = false;
for (value, rhs_value) in values.iter_mut().zip(rhs.iter()) {
carrying_add_mut(value, *rhs_value, &mut carry);
}
carry
}
#[inline]
pub unsafe fn sub_assign_slice(values: &mut [usize], rhs: &[usize]) -> bool {
debug_assert_eq!(values.len(), rhs.len());
let mut carry = false;
for (value, rhs_value) in values.iter_mut().zip(rhs.iter()) {
borrowing_sub_mut(value, *rhs_value, &mut carry);
}
carry
}
#[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, is_well_formed, sub_n, to_twos_complement};
#[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));
}
#[test]
fn test_sub_n() {
type SV = SmallVec<[usize; 4]>;
let mut x: SV = smallvec![0, 0];
let carry = unsafe { 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 = unsafe { 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 = unsafe { 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 = unsafe { 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);
}
#[test]
fn test_to_twos_complement() {
type SV = SmallVec<[usize; 4]>;
let mut value: SV = smallvec![0xffffffffffffffff];
to_twos_complement(&mut value);
let expected: SV = smallvec![1];
assert_eq!(value, expected);
let mut value: SV = smallvec![0xfffffffffffffffe];
to_twos_complement(&mut value);
let expected: SV = smallvec![2];
assert_eq!(value, expected);
let mut value: SV = smallvec![0xfffffffffffffffd, 0xffffffffffffffff];
to_twos_complement(&mut value);
let expected: SV = smallvec![3];
assert_eq!(value, expected);
let mut value: SV = smallvec![0xfffffffffffffffc, 0xffffffffffffffff, 0xffffffffffffffff];
to_twos_complement(&mut value);
let expected: SV = smallvec![4];
assert_eq!(value, expected);
}
}