use super::add::{read, read_small, write, write_small};
use super::cmp::parts;
use super::mul::mul_1_bytes;
use super::{ByteOrder, Number};
use core::cell::Cell;
use core::mem;
use core::ops::{Add, AddAssign, Div, DivAssign, Mul, MulAssign, Rem, RemAssign, Sub, SubAssign};
thread_local! {
static SCRATCH: Cell<Vec<u8>> = const { Cell::new(Vec::new()) };
}
#[inline(always)]
fn with_number<R>(negative: bool, m: u128, order: ByteOrder, f: impl FnOnce(&Number) -> R) -> R {
let mut value = SCRATCH.take();
value.clear();
let len = 16 - (m | 1).leading_zeros() as usize / 8;
match order {
ByteOrder::BigEndian => value.extend_from_slice(&m.to_be_bytes()[16 - len..]),
ByteOrder::LittleEndian => value.extend_from_slice(&m.to_le_bytes()[..len]),
}
let n = Number {
value,
negative: negative && m != 0,
byte_order: order,
};
let result = f(&n);
SCRATCH.set(n.value);
result
}
#[inline(always)]
fn significant_copy(a: &Number, spare: usize) -> Number {
let s = a.significant();
let bytes = if a.byte_order == ByteOrder::BigEndian {
&a.value[a.value.len() - s..]
} else {
&a.value[..s]
};
let mut value = Vec::with_capacity(s + spare);
value.extend_from_slice(bytes);
Number {
value,
negative: a.negative && s != 0,
byte_order: a.byte_order,
}
}
#[inline(never)]
fn add_in_place(a: &mut Number, negative: bool, m: u128) {
let s = a.significant();
let be = a.byte_order == ByteOrder::BigEndian;
if s <= 16 {
let start = if be { a.value.len() - s } else { 0 };
let x = unsafe { read_small(&a.value[start..start + s], a.byte_order) };
let negative_a = a.negative && s != 0;
let (magnitude, carry, sign) = if negative_a == negative {
let (sum, carry) = x.overflowing_add(m);
(sum, carry, negative_a)
} else if x >= m {
(x - m, false, negative_a)
} else {
(m - x, false, negative)
};
let len = if carry {
17
} else {
16 - (magnitude | 1).leading_zeros() as usize / 8
};
a.value.clear();
a.value.reserve(len);
unsafe {
write_small(
a.value.as_mut_ptr(),
a.byte_order,
magnitude as u64,
(magnitude >> 64) as u64,
len,
);
a.value.set_len(len);
}
a.negative = sign && (magnitude != 0 || carry);
return;
}
if s != a.value.len() {
super::sign::trim(a);
}
let subtract = a.negative != negative;
let v = &mut a.value;
let p = v.as_mut_ptr();
let (top_changed, carried) = unsafe {
let low = if be { p.add(s - 16) } else { p };
let x = if be {
u128::from_be_bytes(read(low))
} else {
u128::from_le_bytes(read(low))
};
let (y, mut c) = if subtract {
x.overflowing_sub(m)
} else {
x.overflowing_add(m)
};
write(low, if be { y.to_be_bytes() } else { y.to_le_bytes() });
let mut i = 16;
while c && i < s {
let b = if be { p.add(s - 1 - i) } else { p.add(i) };
(*b, c) = if subtract {
(*b).overflowing_sub(1)
} else {
(*b).overflowing_add(1)
};
i += 1;
}
(i >= s - 1, c)
};
if carried {
if be {
v.insert(0, 1);
} else {
v.push(1);
}
} else if subtract && top_changed {
super::sign::trim(a);
}
}
#[inline(always)]
unsafe fn small_product(x: &[u8], order: ByteOrder, m: u64) -> ([u64; 3], usize) {
let x = unsafe { read_small(x, order) };
let low = u128::from(x as u64) * u128::from(m);
let high = u128::from((x >> 64) as u64) * u128::from(m) + (low >> 64);
let limbs = [low as u64, high as u64, (high >> 64) as u64];
let len = if limbs[2] != 0 {
24 - limbs[2].leading_zeros() as usize / 8
} else {
16 - ((u128::from(limbs[1]) << 64) | u128::from(limbs[0]) | 1).leading_zeros() as usize / 8
};
(limbs, len)
}
#[inline(always)]
unsafe fn write_small_product(p: *mut u8, order: ByteOrder, [l0, l1, l2]: [u64; 3], len: usize) {
unsafe {
if len <= 16 {
write_small(p, order, l0, l1, len);
} else if order == ByteOrder::BigEndian {
write(p.add(len - 8), l0.to_be_bytes());
write(p.add(len - 16), l1.to_be_bytes());
write_small(p, order, l2, 0, len - 16);
} else {
write(p, l0.to_le_bytes());
write(p.add(8), l1.to_le_bytes());
write_small(p.add(16), order, l2, 0, len - 16);
}
}
}
#[inline(always)]
fn significant_bytes(a: &Number, s: usize) -> &[u8] {
if a.byte_order == ByteOrder::BigEndian {
&a.value[a.value.len() - s..]
} else {
&a.value[..s]
}
}
#[inline(always)]
unsafe fn limbs_le(dst: *mut u8, x: *const u8, s: usize, m: u64) -> u64 {
unsafe { mul_1_bytes(dst, x, s / 8, m) }
}
#[inline(always)]
fn top_le(x: &[u8], m: u64, carry: u64) -> (u128, usize) {
let top = x[x.len() / 8 * 8..]
.iter()
.rev()
.fold(0u64, |t, &b| (t << 8) | u64::from(b));
let top = u128::from(top) * u128::from(m) + u128::from(carry);
(top, (128 - top.leading_zeros() as usize).div_ceil(8))
}
#[inline(never)]
fn mul_in_place(a: &mut Number, negative: bool, m: u64, donor: Option<&mut Vec<u8>>) -> bool {
let s = a.significant();
let order = a.byte_order;
let sign = a.negative != negative;
if s == 0 || m == 0 {
place(a, donor, 1);
a.value.clear();
a.value.push(0);
a.negative = false;
return true;
}
if s <= 16 {
let (limbs, len) = unsafe { small_product(significant_bytes(a, s), order, m) };
place(a, donor, len);
a.value.clear();
unsafe {
write_small_product(a.value.as_mut_ptr(), order, limbs, len);
a.value.set_len(len);
}
a.negative = sign;
return true;
}
if order == ByteOrder::BigEndian {
return false;
}
a.value.truncate(s);
let p = a.value.as_mut_ptr();
let carry = unsafe { limbs_le(p, p, s, m) };
let (top, t) = top_le(&a.value, m, carry);
let full = s / 8 * 8;
place(a, donor, full + t);
a.value.truncate(full);
a.value.extend_from_slice(&top.to_le_bytes()[..t]);
a.negative = sign;
true
}
#[inline(never)]
fn mul_into(a: &Number, negative: bool, m: u64, donor: Option<&mut Vec<u8>>) -> Option<Number> {
let s = a.significant();
let order = a.byte_order;
if s == 0 || m == 0 || s <= 16 {
let (limbs, len) = if s == 0 || m == 0 {
([0; 3], 1)
} else {
unsafe { small_product(significant_bytes(a, s), order, m) }
};
let mut value = match donor {
Some(d) if d.capacity() >= len => mem::take(d),
_ => Vec::with_capacity(len),
};
value.clear();
unsafe {
write_small_product(value.as_mut_ptr(), order, limbs, len);
value.set_len(len);
}
return Some(Number {
value,
negative: (a.negative && s != 0) != negative && len > 0 && limbs != [0; 3],
byte_order: order,
});
}
if order == ByteOrder::BigEndian {
return None;
}
let x = significant_bytes(a, s);
let most = s + 8 - m.leading_zeros() as usize / 8;
let mut value = match donor {
Some(d) if d.capacity() >= s => mem::take(d),
_ => Vec::with_capacity(most),
};
value.clear();
let full = s / 8 * 8;
unsafe {
let carry = limbs_le(value.as_mut_ptr(), x.as_ptr(), s, m);
value.set_len(full);
let (top, t) = top_le(x, m, carry);
if value.capacity() < full + t {
let mut v = Vec::with_capacity(most);
v.extend_from_slice(&value);
value = v;
}
value.extend_from_slice(&top.to_le_bytes()[..t]);
}
Some(Number {
value,
negative: (a.negative && s != 0) != negative,
byte_order: order,
})
}
#[inline(always)]
fn place(a: &mut Number, donor: Option<&mut Vec<u8>>, len: usize) {
if a.value.capacity() >= len {
return;
}
match donor {
Some(d) if d.capacity() >= len => {
d.clear();
d.extend_from_slice(&a.value);
mem::swap(&mut a.value, d);
}
_ => a.value.reserve_exact(len - a.value.len()),
}
}
#[inline(always)]
pub(super) fn times_limb(
a: &mut Number,
negative: bool,
m: u64,
donor: Option<&mut Vec<u8>>,
) -> bool {
mul_in_place(a, negative, m, donor)
}
#[inline(always)]
pub(super) fn copy_times_limb(
a: &Number,
negative: bool,
m: u64,
donor: Option<&mut Vec<u8>>,
) -> Option<Number> {
mul_into(a, negative, m, donor)
}
#[inline(always)]
fn add_assign(a: &mut Number, negative: bool, m: u128) {
add_in_place(a, negative, m);
}
#[inline(always)]
fn add_ref(a: &Number, negative: bool, m: u128) -> Number {
let mut sum = significant_copy(a, 17);
add_in_place(&mut sum, negative, m);
sum
}
#[inline(always)]
fn sub_assign(a: &mut Number, negative: bool, m: u128) {
add_in_place(a, !negative, m);
}
#[inline(always)]
fn sub_ref(a: &Number, negative: bool, m: u128) -> Number {
add_ref(a, !negative, m)
}
#[inline(always)]
fn mul_assign(a: &mut Number, negative: bool, m: u128) {
if m >> 64 != 0 || !mul_in_place(a, negative, m as u64, None) {
with_number(negative, m, ByteOrder::LittleEndian, |b| *a *= b);
}
}
#[inline(always)]
fn mul_ref(a: &Number, negative: bool, m: u128) -> Number {
if m >> 64 == 0
&& let Some(product) = mul_into(a, negative, m as u64, None)
{
return product;
}
with_number(negative, m, ByteOrder::LittleEndian, |b| a * b)
}
#[inline(always)]
#[track_caller]
fn div_assign(a: &mut Number, negative: bool, m: u128) {
with_number(negative, m, ByteOrder::LittleEndian, |b| *a /= b);
}
#[inline(always)]
#[track_caller]
fn div_ref(a: &Number, negative: bool, m: u128) -> Number {
with_number(negative, m, ByteOrder::LittleEndian, |b| a / b)
}
#[inline(always)]
#[track_caller]
fn rem_assign(a: &mut Number, negative: bool, m: u128) {
with_number(negative, m, ByteOrder::LittleEndian, |b| *a %= b);
}
#[inline(always)]
#[track_caller]
fn rem_ref(a: &Number, negative: bool, m: u128) -> Number {
with_number(negative, m, ByteOrder::LittleEndian, |b| a % b)
}
macro_rules! right {
($op:ident $f:ident $assign:ident $f_assign:ident $sym:literal $assign_f:ident $ref_f:ident: $($t:ident),*) => {$(
impl $op<$t> for &Number {
type Output = Number;
#[doc = concat!("Returns `self ", $sym, " rhs`, as with `rhs` a number.")]
#[inline]
#[track_caller]
fn $f(self, rhs: $t) -> Number {
let (negative, m) = parts!(rhs, $t);
$ref_f(self, negative, m)
}
}
impl $op<$t> for Number {
type Output = Number;
#[doc = concat!("Returns `self ", $sym, " rhs`, as with `rhs` a number, in the vector of `self` when it has room.")]
#[inline]
#[track_caller]
fn $f(mut self, rhs: $t) -> Number {
let (negative, m) = parts!(rhs, $t);
$assign_f(&mut self, negative, m);
self
}
}
impl $assign<$t> for Number {
#[doc = concat!("Makes `self` `self ", $sym, " rhs`, as with `rhs` a number, in the vector of `self` when it has room.")]
#[inline]
#[track_caller]
fn $f_assign(&mut self, rhs: $t) {
let (negative, m) = parts!(rhs, $t);
$assign_f(self, negative, m);
}
}
)*};
}
macro_rules! left_commutative {
($op:ident $f:ident $sym:literal: $($t:ident),*) => {$(
impl $op<Number> for $t {
type Output = Number;
#[doc = concat!("Returns `self ", $sym, " rhs`, as `rhs ", $sym, " self`, in the vector of `rhs` when it has room.")]
#[inline]
fn $f(self, rhs: Number) -> Number {
rhs.$f(self)
}
}
impl $op<&Number> for $t {
type Output = Number;
#[doc = concat!("Returns `self ", $sym, " rhs`, as `rhs ", $sym, " self`.")]
#[inline]
fn $f(self, rhs: &Number) -> Number {
rhs.$f(self)
}
}
)*};
}
macro_rules! left {
($($t:ident),*) => {$(
impl Sub<Number> for $t {
type Output = Number;
#[inline]
fn sub(self, rhs: Number) -> Number {
-(rhs - self)
}
}
impl Sub<&Number> for $t {
type Output = Number;
#[inline]
fn sub(self, rhs: &Number) -> Number {
-(rhs - self)
}
}
impl Div<Number> for $t {
type Output = Number;
#[inline]
#[track_caller]
fn div(self, rhs: Number) -> Number {
let (negative, m) = parts!(self, $t);
let order = rhs.byte_order;
with_number(negative, m, order, |a| a / rhs)
}
}
impl Div<&Number> for $t {
type Output = Number;
#[inline]
#[track_caller]
fn div(self, rhs: &Number) -> Number {
let (negative, m) = parts!(self, $t);
with_number(negative, m, rhs.byte_order, |a| a / rhs)
}
}
impl Rem<Number> for $t {
type Output = Number;
#[inline]
#[track_caller]
fn rem(self, rhs: Number) -> Number {
let (negative, m) = parts!(self, $t);
let order = rhs.byte_order;
with_number(negative, m, order, |a| a % rhs)
}
}
impl Rem<&Number> for $t {
type Output = Number;
#[inline]
#[track_caller]
fn rem(self, rhs: &Number) -> Number {
let (negative, m) = parts!(self, $t);
with_number(negative, m, rhs.byte_order, |a| a % rhs)
}
}
)*};
}
macro_rules! all {
($($t:ident),*) => {
right!(Add add AddAssign add_assign "+" add_assign add_ref: $($t),*);
right!(Sub sub SubAssign sub_assign "-" sub_assign sub_ref: $($t),*);
right!(Mul mul MulAssign mul_assign "*" mul_assign mul_ref: $($t),*);
right!(Div div DivAssign div_assign "/" div_assign div_ref: $($t),*);
right!(Rem rem RemAssign rem_assign "%" rem_assign rem_ref: $($t),*);
left_commutative!(Add add "+": $($t),*);
left_commutative!(Mul mul "*": $($t),*);
left!($($t),*);
};
}
all!(
u8, u16, u32, u64, u128, usize, i8, i16, i32, i64, i128, isize
);