#[cfg(test)]
extern crate alloc;
use super::limb::{
CtLDivisor, DoubleLimb, LDivisorPrivate, LIMB_BITS, LIMB_BYTES, LimbChoice, LimbType,
ct_add_l_l, ct_eq_l_l, ct_find_last_set_bit_l, ct_find_last_set_byte_l, ct_gt_l_l,
ct_is_nonzero_l, ct_lsb_mask_l, ct_mul_l_l, ct_mul_sub_l_l_l_b, ct_sub_l_l,
};
use super::limbs::{
CompositeLimbsBuffer, MpMutNativeEndianUIntLimbsSlice, MpMutUInt, MpMutUIntSlice, MpUIntCommon,
ct_mp_nlimbs, find_last_set_byte_mp,
};
use super::shift::ct_lshift_mp;
use super::usize_ct_cmp::ct_is_zero_usize;
#[cfg(test)]
use alloc::vec;
pub struct CtMpDivisor<'a, VT: MpUIntCommon> {
v: &'a VT,
v_decrement: Option<LimbType>,
v_len: usize,
scaling_shift: u32,
scaling_low_src_rshift: u32,
scaling_low_src_mask: LimbType,
scaled_v_head_divisor: CtLDivisor,
scaled_v_head: LimbType,
scaled_v_tail_head: LimbType,
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum CtMpDivisorError {
DivisorIsZero,
}
impl<'a, VT: MpUIntCommon> CtMpDivisor<'a, VT> {
pub fn new(v: &'a VT, v_decrement: Option<LimbType>) -> Result<Self, CtMpDivisorError> {
if v.is_empty() {
return Err(CtMpDivisorError::DivisorIsZero);
}
let (v_len, v_head) = if let Some(v_decrement) = v_decrement {
let mut v_decrement_borrow = v_decrement;
let mut v_len = 0;
let mut v_head = 0;
for i in 0..v.nlimbs() {
let v_val;
(v_decrement_borrow, v_val) = ct_sub_l_l(v.load_l(i), v_decrement_borrow);
let v_val_is_zero = ct_eq_l_l(v_val, 0);
v_len = v_val_is_zero
.select_usize(i * LIMB_BYTES + ct_find_last_set_byte_l(v_val), v_len);
v_head = v_val_is_zero.select(v_val, v_head);
}
if v_decrement_borrow != 0 {
return Err(CtMpDivisorError::DivisorIsZero);
}
if ct_is_zero_usize(v_len) != 0 {
return Err(CtMpDivisorError::DivisorIsZero);
}
(v_len, v_head)
} else {
let v_len = find_last_set_byte_mp(v);
if ct_is_zero_usize(v_len) != 0 {
return Err(CtMpDivisorError::DivisorIsZero);
}
let v_head = v.load_l(ct_mp_nlimbs(v_len) - 1);
(v_len, v_head)
};
let v_nlimbs = ct_mp_nlimbs(v_len);
let v_head_width = ct_find_last_set_bit_l(v_head);
debug_assert_ne!(v_head_width, 0);
let scaling_shift = LIMB_BITS - v_head_width as u32;
let scaling_low_src_rshift = (LIMB_BITS - scaling_shift) % LIMB_BITS;
let scaling_low_src_mask = ct_lsb_mask_l(scaling_shift);
let mut v_decrement_borrow = if let Some(v_decrement) = v_decrement {
let mut v_decrement_borrow = v_decrement;
for i in 0..v_nlimbs - 3.min(v_nlimbs) {
(v_decrement_borrow, _) = ct_sub_l_l(v.load_l_full(i), v_decrement_borrow);
}
v_decrement_borrow
} else {
0
};
let mut scaled_v_carry = 0;
let mut scaled_v_tail_head = 0; let mut scaled_v_head = 0;
for i in v_nlimbs - 3.min(v_nlimbs)..v_nlimbs {
scaled_v_tail_head = scaled_v_head;
let v_val;
(v_decrement_borrow, v_val) = ct_sub_l_l(v.load_l(i), v_decrement_borrow);
(scaled_v_carry, scaled_v_head) = Self::_scale_val(
v_val,
scaling_shift,
scaling_low_src_rshift,
scaling_low_src_mask,
scaled_v_carry,
);
}
debug_assert_eq!(scaled_v_carry, 0);
let scaled_v_head_divisor = CtLDivisor::new(scaled_v_head).unwrap();
Ok(Self {
v,
v_decrement,
v_len,
scaling_shift,
scaling_low_src_rshift,
scaling_low_src_mask,
scaled_v_head_divisor,
scaled_v_head,
scaled_v_tail_head,
})
}
fn make_q_estimate(&self, u_head: &[LimbType; 3]) -> LimbType {
let (q_high_is_zero, u_high) = ct_sub_l_l(u_head[0], self.scaled_v_head);
let q_high_is_zero = LimbChoice::from(q_high_is_zero);
let u_high = q_high_is_zero.select(u_high, u_head[0]);
debug_assert!(u_high < self.scaled_v_head);
let (q_low, r) = self
.scaled_v_head_divisor
.do_div(&DoubleLimb::new(u_high, u_head[1]));
debug_assert!(q_high_is_zero.unwrap() != 0 || u_head[0] == self.scaled_v_head);
let ov = !q_high_is_zero;
let q = ov.select(q_low, !0);
let (r_carry_on_ov, r_on_ov) = ct_add_l_l(u_head[1], self.scaled_v_head);
let r = ov.select(r, r_on_ov);
debug_assert_eq!(r_carry_on_ov & !1, 0); let r_carry = ov & LimbChoice::from(r_carry_on_ov);
let qv_head_low: DoubleLimb = ct_mul_l_l(self.scaled_v_tail_head, q);
let over_estimated = !r_carry
& (ct_gt_l_l(qv_head_low.high(), r)
| (ct_eq_l_l(qv_head_low.high(), r) & ct_gt_l_l(qv_head_low.low(), u_head[2])));
q - over_estimated.select(0, 1)
}
fn _scale_val(
val: LimbType,
scaling_shift: u32,
scaling_low_src_rshift: u32,
scaling_low_src_mask: LimbType,
carry: LimbType,
) -> (LimbType, LimbType) {
debug_assert!(carry <= (1 << scaling_shift));
let (carry, scaled_val) = ct_add_l_l(val << scaling_shift, carry);
debug_assert!(carry == 0 || scaled_val == 0);
let carry = ((val >> scaling_low_src_rshift) & scaling_low_src_mask) + carry;
debug_assert!(carry <= (1 << scaling_shift));
(carry, scaled_val)
}
fn scale_val(&self, val: LimbType, carry: LimbType) -> (LimbType, LimbType) {
Self::_scale_val(
val,
self.scaling_shift,
self.scaling_low_src_rshift,
self.scaling_low_src_mask,
carry,
)
}
fn scaled_v_val(
&self,
i: usize,
v_decrement_borrow: LimbType,
carry: LimbType,
) -> (LimbType, LimbType, LimbType) {
let (v_decrement_borrow, v_val) = ct_sub_l_l(self.v.load_l(i), v_decrement_borrow);
let (carry, scaled_v_val) = self.scale_val(v_val, carry);
(v_decrement_borrow, carry, scaled_v_val)
}
fn unscale_val(&self, val: LimbType, last_higher_val: LimbType) -> LimbType {
let dst_high_lshift = self.scaling_low_src_rshift;
let dst_high_mask = self.scaling_low_src_mask;
let val_low = val >> self.scaling_shift;
let val_high = (last_higher_val & dst_high_mask) << dst_high_lshift;
val_high | val_low
}
fn add_scaled_v_val_cond(
&self,
op0: LimbType,
i: usize,
v_decrement_borrow: LimbType,
carry: LimbType,
cond: LimbChoice,
) -> (LimbType, LimbType, LimbType) {
let (v_decrement_borrow, carry0, scaled_v_val) =
self.scaled_v_val(i, v_decrement_borrow, carry);
let carry0 = cond.select(0, carry0);
let scaled_v_val = cond.select(0, scaled_v_val);
let (carry1, result) = ct_add_l_l(op0, scaled_v_val);
let carry = carry0 + carry1;
(v_decrement_borrow, carry, result)
}
fn sub_scaled_qv_val(
&self,
op0: LimbType,
i: usize,
q: LimbType,
v_decrement_borrow: LimbType,
scaled_v_carry: LimbType,
borrow: LimbType,
) -> (LimbType, LimbType, LimbType, LimbType) {
let (v_decrement_borrow, scaled_v_carry, scaled_v_val) =
self.scaled_v_val(i, v_decrement_borrow, scaled_v_carry);
let (borrow, result) = ct_mul_sub_l_l_l_b(op0, q, scaled_v_val, borrow);
(v_decrement_borrow, scaled_v_carry, borrow, result)
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum CtDivMpMpError {
InsufficientQuotientSpace,
}
pub fn ct_div_mp_mp<UT: MpMutUIntSlice, VT: MpUIntCommon, QT: MpMutUInt>(
u_h: Option<&mut UT>,
u_l: &mut UT,
v: &CtMpDivisor<VT>,
mut q_out: Option<&mut QT>,
) -> Result<(), CtDivMpMpError> {
let mut __u_h: [UT::BackingSliceElementType; 0] = [];
let u_h: UT::SelfT<'_> = match u_h {
Some(u_h) => u_h.coerce_lifetime(),
None => UT::from_slice(__u_h.as_mut()).unwrap(),
};
let u_len = u_l.len() + u_h.len();
let u_nlimbs = ct_mp_nlimbs(u_len);
let v_len = v.v_len;
let v_nlimbs = ct_mp_nlimbs(v_len);
if let Some(q_out) = &mut q_out {
if q_out.len() + v_len < u_len {
return Err(CtDivMpMpError::InsufficientQuotientSpace);
}
};
if u_len < v_len {
if let Some(q_out) = q_out {
q_out.clear_bytes_above(0);
}
return Ok(());
}
let q_out_max_len = u_len + 1 - v_len;
let q_out_max_nlimbs = ct_mp_nlimbs(q_out_max_len);
if let Some(q_out) = &mut q_out {
q_out.clear_bytes_above(q_out_max_len);
}
let u_pad_len = if u_len.is_multiple_of(LIMB_BYTES) {
0
} else {
LIMB_BYTES - u_len % LIMB_BYTES
};
let mut _u_pad = [0 as LimbType; 2];
let _u_pad = {
use core::{mem, slice};
let _u_pad_size = mem::size_of_val(&_u_pad);
let _u_pad = _u_pad.as_mut_ptr() as *mut UT::BackingSliceElementType;
let _u_pad = unsafe {
slice::from_raw_parts_mut(
_u_pad,
_u_pad_size / mem::size_of::<UT::BackingSliceElementType>(),
)
};
&mut _u_pad[..UT::n_backing_elements_for_len(LIMB_BYTES + u_pad_len)]
};
let u_pad = UT::from_slice(_u_pad).unwrap();
let u_l = u_l.coerce_lifetime();
let mut u_parts = CompositeLimbsBuffer::new([u_l, u_h, u_pad]);
let mut carry = 0;
for i in 0..u_nlimbs + 1 {
let mut u_val = u_parts.load(i);
(carry, u_val) = v.scale_val(u_val, carry);
u_parts.store(i, u_val);
}
debug_assert_eq!(carry, 0);
let mut j = q_out_max_nlimbs;
while j > 0 {
j -= 1;
let q = {
let u_h = u_parts.load(v_nlimbs + j);
let u_l = u_parts.load(v_nlimbs + j - 1);
let u_tail_high = if v_nlimbs + j >= 2 {
u_parts.load(v_nlimbs + j - 2)
} else {
0
};
let cur_u_head: [LimbType; 3] = [u_h, u_l, u_tail_high];
v.make_q_estimate(&cur_u_head)
};
let mut v_decrement_borrow = v.v_decrement.unwrap_or(0);
let mut scaled_v_carry = 0;
let mut borrow = 0;
for i in 0..v_nlimbs {
let mut u_val = u_parts.load(j + i);
(v_decrement_borrow, scaled_v_carry, borrow, u_val) =
v.sub_scaled_qv_val(u_val, i, q, v_decrement_borrow, scaled_v_carry, borrow);
u_parts.store(j + i, u_val);
}
debug_assert_eq!(scaled_v_carry, 0);
let u_val = u_parts.load(j + v_nlimbs);
let (borrow, u_val) = ct_sub_l_l(u_val, borrow);
u_parts.store(j + v_nlimbs, u_val);
let over_estimated = LimbChoice::from(borrow);
if let Some(q_out) = &mut q_out {
let q = q - over_estimated.select(0, 1);
if j + 1 < q_out.nlimbs() {
q_out.store_l_full(j, q);
} else if j + 1 == q_out.nlimbs() {
if ct_is_nonzero_l(q & !q_out.partial_high_mask()) != 0 {
return Err(CtDivMpMpError::InsufficientQuotientSpace);
}
q_out.store_l(j, q);
} else {
debug_assert_eq!(j, q_out.nlimbs());
if ct_is_nonzero_l(q) != 0 {
return Err(CtDivMpMpError::InsufficientQuotientSpace);
}
}
}
let mut v_decrement_borrow = v.v_decrement.unwrap_or(0);
let mut carry = 0;
for i in 0..v_nlimbs {
let mut u_val = u_parts.load(j + i);
(v_decrement_borrow, carry, u_val) =
v.add_scaled_v_val_cond(u_val, i, v_decrement_borrow, carry, over_estimated);
u_parts.store(j + i, u_val);
}
let u_val = u_parts.load(j + v_nlimbs);
let (_, u_val) = ct_add_l_l(u_val, carry);
u_parts.store(j + v_nlimbs, u_val);
}
let mut u_h = 0;
for j in v_nlimbs..u_nlimbs + 1 {
debug_assert_eq!(u_parts.load(j), 0);
}
let mut j = v_nlimbs;
while j > 0 {
j -= 1;
let u_l = u_parts.load(j);
let u_val = v.unscale_val(u_l, u_h);
u_h = u_l;
u_parts.store(j, u_val);
}
Ok(())
}
#[cfg(test)]
fn test_limbs_from_be_bytes<DT: MpMutUIntSlice, const N: usize>(
bytes: [u8; N],
) -> vec::Vec<DT::BackingSliceElementType> {
use super::limbs::{MpBigEndianUIntByteSlice, MpUIntSlicePriv as _};
let mut limbs = tst_mk_mp_backing_vec!(DT, N);
let mut dst = DT::from_slice(limbs.as_mut_slice()).unwrap();
dst.copy_from(&MpBigEndianUIntByteSlice::from_slice(bytes.as_slice()).unwrap());
drop(dst);
limbs
}
#[cfg(test)]
fn test_ct_div_mp_mp<UT: MpMutUIntSlice, VT: MpMutUIntSlice, QT: MpMutUIntSlice>() {
use super::cmp::ct_eq_mp_mp;
fn div_and_check<UT: MpMutUIntSlice, VT: MpMutUIntSlice, QT: MpMutUIntSlice>(
u: &UT::SelfT<'_>,
v: &VT::SelfT<'_>,
v_decrement: Option<LimbType>,
split_u: bool,
) {
use super::add::{ct_add_mp_mp, ct_sub_mp_l};
use super::mul::ct_mul_trunc_mp_mp;
let mut decremented_v = tst_mk_mp_backing_vec!(VT, v.len());
let mut decremented_v = VT::from_slice(&mut decremented_v).unwrap();
decremented_v.copy_from(v);
if let Some(v_decrement) = v_decrement {
ct_sub_mp_l(&mut decremented_v, v_decrement);
}
let v_len = find_last_set_byte_mp(&decremented_v);
let q_len = if u.len() >= v_len {
u.len() - v_len + 1
} else {
0
};
let mut q = tst_mk_mp_backing_vec!(QT, q_len);
q.fill(0xffu8.into());
let mut q = QT::from_slice(&mut q).unwrap();
let divisor = CtMpDivisor::new(v, v_decrement).unwrap();
let mut rem_buf = if split_u {
let split_point = if UT::SUPPORTS_UNALIGNED_BUFFER_LENGTHS {
LIMB_BYTES + 1
} else {
LIMB_BYTES
};
let split_point = split_point.min(u.len());
let mut rem_l_buf = tst_mk_mp_backing_vec!(UT, split_point);
let mut rem_h_buf = tst_mk_mp_backing_vec!(UT, u.len() - split_point);
let mut rem_composite = CompositeLimbsBuffer::new([
UT::from_slice(&mut rem_l_buf).unwrap(),
UT::from_slice(&mut rem_h_buf).unwrap(),
]);
for i in 0..u.nlimbs() {
rem_composite.store(i, u.load_l(i));
}
drop(rem_composite);
ct_div_mp_mp(
Some(&mut UT::from_slice(&mut rem_h_buf).unwrap()),
&mut UT::from_slice(&mut rem_l_buf).unwrap(),
&divisor,
Some(&mut q),
)
.unwrap();
let rem_composite = CompositeLimbsBuffer::new([
UT::from_slice(&mut rem_l_buf).unwrap(),
UT::from_slice(&mut rem_h_buf).unwrap(),
]);
let mut rem_buf = tst_mk_mp_backing_vec!(UT, u.len());
let mut rem = UT::from_slice(&mut rem_buf).unwrap();
for i in 0..u.nlimbs() {
rem.store_l(i, rem_composite.load(i));
}
drop(rem);
rem_buf
} else {
let mut rem_buf = tst_mk_mp_backing_vec!(UT, u.len());
let mut rem = UT::from_slice(&mut rem_buf).unwrap();
rem.copy_from(u);
ct_div_mp_mp(None, &mut rem, &divisor, Some(&mut q)).unwrap();
drop(rem);
rem_buf
};
let rem = UT::from_slice(&mut rem_buf).unwrap();
let mut result = tst_mk_mp_backing_vec!(UT, u.len() + LIMB_BYTES);
let mut result = UT::from_slice(&mut result).unwrap();
result.copy_from(&q);
ct_mul_trunc_mp_mp(&mut result, q_len, &decremented_v);
let carry = ct_add_mp_mp(&mut result, &rem);
assert_eq!(carry, 0);
assert_eq!(ct_eq_mp_mp(u, &result).unwrap(), 1);
}
let mut u = test_limbs_from_be_bytes::<UT, 2>([1, 0]);
let u = UT::from_slice(u.as_mut_slice()).unwrap();
let mut v = test_limbs_from_be_bytes::<VT, 1>([1]);
let v = VT::from_slice(v.as_mut_slice()).unwrap();
div_and_check::<UT, VT, QT>(&u, &v, None, false);
div_and_check::<UT, VT, QT>(&u, &v, None, true);
let mut u = test_limbs_from_be_bytes::<UT, 2>([1, 0]);
let u = UT::from_slice(u.as_mut_slice()).unwrap();
let mut v = test_limbs_from_be_bytes::<VT, 1>([3]);
let v = VT::from_slice(v.as_mut_slice()).unwrap();
div_and_check::<UT, VT, QT>(&u, &v, None, false);
div_and_check::<UT, VT, QT>(&u, &v, None, true);
div_and_check::<UT, VT, QT>(&u, &v, Some(1), false);
let mut u = test_limbs_from_be_bytes::<UT, 6>([!0, !0, !1, !0, !0, !0]);
let u = UT::from_slice(u.as_mut_slice()).unwrap();
let mut v = test_limbs_from_be_bytes::<VT, 3>([!0, !0, !0]);
let v = VT::from_slice(v.as_mut_slice()).unwrap();
div_and_check::<UT, VT, QT>(&u, &v, None, false);
div_and_check::<UT, VT, QT>(&u, &v, None, true);
div_and_check::<UT, VT, QT>(&u, &v, Some(1), false);
let mut u = test_limbs_from_be_bytes::<UT, 6>([!0, !0, !1, !0, !0, !1]);
let u = UT::from_slice(u.as_mut_slice()).unwrap();
let mut v = test_limbs_from_be_bytes::<VT, 3>([!0, !0, !0]);
let v = VT::from_slice(v.as_mut_slice()).unwrap();
div_and_check::<UT, VT, QT>(&u, &v, None, false);
div_and_check::<UT, VT, QT>(&u, &v, None, true);
div_and_check::<UT, VT, QT>(&u, &v, Some(1), false);
let mut u = test_limbs_from_be_bytes::<UT, 6>([0, 0, 0, 0, 0, 0]);
let u = UT::from_slice(u.as_mut_slice()).unwrap();
let mut v = test_limbs_from_be_bytes::<VT, 3>([!0, !0, !0]);
let v = VT::from_slice(v.as_mut_slice()).unwrap();
div_and_check::<UT, VT, QT>(&u, &v, None, false);
div_and_check::<UT, VT, QT>(&u, &v, None, true);
div_and_check::<UT, VT, QT>(&u, &v, Some(1), false);
let mut u = test_limbs_from_be_bytes::<UT, 6>([0, 0, 0, 0, 0, !1]);
let u = UT::from_slice(u.as_mut_slice()).unwrap();
let mut v = test_limbs_from_be_bytes::<VT, 3>([!0, !0, !0]);
let v = VT::from_slice(v.as_mut_slice()).unwrap();
div_and_check::<UT, VT, QT>(&u, &v, None, false);
div_and_check::<UT, VT, QT>(&u, &v, None, true);
div_and_check::<UT, VT, QT>(&u, &v, Some(1), false);
let mut u = test_limbs_from_be_bytes::<UT, 6>([!0, !0, !0, !0, !0, 0]);
let u = UT::from_slice(u.as_mut_slice()).unwrap();
let mut v = test_limbs_from_be_bytes::<VT, 3>([0, 1, 0]);
let v = VT::from_slice(v.as_mut_slice()).unwrap();
div_and_check::<UT, VT, QT>(&u, &v, None, false);
div_and_check::<UT, VT, QT>(&u, &v, None, true);
div_and_check::<UT, VT, QT>(&u, &v, Some(1), false);
let mut u = test_limbs_from_be_bytes::<UT, 6>([!0, !0, !0, !0, !1, 0]);
let u = UT::from_slice(u.as_mut_slice()).unwrap();
let mut v = test_limbs_from_be_bytes::<VT, 3>([0, 2, 0]);
let v = VT::from_slice(v.as_mut_slice()).unwrap();
div_and_check::<UT, VT, QT>(&u, &v, None, false);
div_and_check::<UT, VT, QT>(&u, &v, None, true);
div_and_check::<UT, VT, QT>(&u, &v, Some(1), false);
const N_MAX_LIMBS: u32 = 3;
for i in 0..N_MAX_LIMBS * LIMB_BITS + 1 {
let u_len = (i as usize).div_ceil(8);
let mut u = tst_mk_mp_backing_vec!(UT, u_len);
let mut u = UT::from_slice(&mut u).unwrap();
if i != 0 {
let u_nlimbs = ct_mp_nlimbs(u_len);
for k in 0..u_nlimbs - 1 {
u.store_l(k, !0);
}
if i % LIMB_BITS != 0 {
let i = i % LIMB_BITS;
u.store_l(u_nlimbs - 1, !0 >> (LIMB_BITS - i));
} else {
u.store_l(u_nlimbs - 1, !0);
}
}
for j1 in 0..i + 1 {
for j2 in 0..j1 + 1 {
let v_len = ((j1 + 1) as usize).div_ceil(8);
let mut v = tst_mk_mp_backing_vec!(VT, v_len);
let mut v = VT::from_slice(&mut v).unwrap();
v.set_bit_to(j1 as usize, true);
v.set_bit_to(j2 as usize, true);
div_and_check::<UT, VT, QT>(&u, &v, None, false);
div_and_check::<UT, VT, QT>(&u, &v, None, true);
if j1 != 0 {
div_and_check::<UT, VT, QT>(&u, &v, Some(1), false);
}
}
}
}
}
#[test]
fn test_ct_div_be_be_be() {
use super::limbs::MpMutBigEndianUIntByteSlice;
test_ct_div_mp_mp::<
MpMutBigEndianUIntByteSlice,
MpMutBigEndianUIntByteSlice,
MpMutBigEndianUIntByteSlice,
>()
}
#[test]
fn test_ct_div_le_le_le() {
use super::limbs::MpMutLittleEndianUIntByteSlice;
test_ct_div_mp_mp::<
MpMutLittleEndianUIntByteSlice,
MpMutLittleEndianUIntByteSlice,
MpMutLittleEndianUIntByteSlice,
>()
}
#[test]
fn test_ct_div_ne_ne_ne() {
use super::limbs::MpMutNativeEndianUIntLimbsSlice;
test_ct_div_mp_mp::<
MpMutNativeEndianUIntLimbsSlice,
MpMutNativeEndianUIntLimbsSlice,
MpMutNativeEndianUIntLimbsSlice,
>()
}
pub fn ct_mod_mp_mp<UT: MpMutUIntSlice, VT: MpUIntCommon>(
u_h: Option<&mut UT>,
u_l: &mut UT,
v: &CtMpDivisor<VT>,
) {
ct_div_mp_mp::<_, _, MpMutNativeEndianUIntLimbsSlice>(u_h, u_l, v, None).unwrap()
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum CtDivPow2MpError {
InsufficientQuotientSpace,
InsufficientRemainderSpace,
}
pub fn ct_div_pow2_mp<RT: MpMutUInt, VT: MpUIntCommon, QT: MpMutUInt>(
u_pow2_exp: usize,
r_out: &mut RT,
v: &CtMpDivisor<VT>,
mut q_out: Option<&mut QT>,
) -> Result<(), CtDivPow2MpError> {
let virtual_u_len = (u_pow2_exp + 1).div_ceil(8);
let virtual_u_nlimbs = ct_mp_nlimbs(virtual_u_len);
let v_len = v.v_len;
let v_nlimbs = ct_mp_nlimbs(v_len);
if let Some(q_out) = &mut q_out {
if q_out.len() + v_len < virtual_u_len {
return Err(CtDivPow2MpError::InsufficientQuotientSpace);
}
};
if r_out.len() < v_len.min(virtual_u_len) {
return Err(CtDivPow2MpError::InsufficientRemainderSpace);
}
r_out.clear_bytes_above(0);
let u_high_shift = (u_pow2_exp % LIMB_BITS as usize) as u32;
let u_high = (1 as LimbType) << u_high_shift;
if virtual_u_len < v_len {
r_out.store_l(virtual_u_nlimbs - 1, u_high);
if let Some(q_out) = q_out {
q_out.clear_bytes_above(0);
}
return Ok(());
}
let q_out_max_len = virtual_u_len + 1 - v_len;
let q_out_max_nlimbs = ct_mp_nlimbs(q_out_max_len);
if let Some(q_out) = &mut q_out {
q_out.clear_bytes_above(q_out_max_len);
};
let (scaled_u_head_carry, scaled_u_head_low) = v.scale_val(u_high, 0);
let (scaled_u_head_carry, scaled_u_head_high) = v.scale_val(0, scaled_u_head_carry);
debug_assert_eq!(scaled_u_head_carry, 0);
let mut r_out_head_shadow: [LimbType; 2] = [scaled_u_head_low, scaled_u_head_high];
debug_assert!(
v_nlimbs + q_out_max_nlimbs == virtual_u_nlimbs + 1
|| v_nlimbs + q_out_max_nlimbs == virtual_u_nlimbs
);
if v_nlimbs + q_out_max_nlimbs == virtual_u_nlimbs {
debug_assert_eq!(r_out_head_shadow[1], 0);
r_out_head_shadow[1] = r_out_head_shadow[0];
r_out_head_shadow[0] = 0;
}
let mut j = q_out_max_nlimbs;
while j > 0 {
j -= 1;
let q = {
let u_tail_high = if v_nlimbs > 2 {
r_out.load_l_full(v_nlimbs - 2 - 1)
} else {
0
};
let cur_u_head: [LimbType; 3] =
[r_out_head_shadow[1], r_out_head_shadow[0], u_tail_high];
v.make_q_estimate(&cur_u_head)
};
let mut v_decrement_borrow = v.v_decrement.unwrap_or(0);
let mut scaled_v_carry = 0;
let mut borrow = 0;
let mut i = 0;
let mut u_val = if v_nlimbs >= 2 {
let mut next_u_val = 0; while i + 2 < v_nlimbs {
let mut u_val = next_u_val;
next_u_val = r_out.load_l_full(i);
(v_decrement_borrow, scaled_v_carry, borrow, u_val) =
v.sub_scaled_qv_val(u_val, i, q, v_decrement_borrow, scaled_v_carry, borrow);
r_out.store_l_full(i, u_val);
i += 1;
}
{
let mut u_val = next_u_val;
(v_decrement_borrow, scaled_v_carry, borrow, u_val) =
v.sub_scaled_qv_val(u_val, i, q, v_decrement_borrow, scaled_v_carry, borrow);
i += 1;
u_val
}
} else {
0
};
debug_assert_eq!(i + 1, v_nlimbs);
{
let cur_u_val = r_out_head_shadow[0];
r_out_head_shadow[0] = u_val;
(_, scaled_v_carry, borrow, u_val) =
v.sub_scaled_qv_val(cur_u_val, i, q, v_decrement_borrow, scaled_v_carry, borrow);
debug_assert_eq!(scaled_v_carry, 0);
let cur_u_val = r_out_head_shadow[1];
r_out_head_shadow[1] = u_val;
(borrow, u_val) = ct_sub_l_l(cur_u_val, borrow);
debug_assert!(borrow != 0 || u_val == 0);
}
let over_estimated = LimbChoice::from(borrow);
if let Some(q_out) = &mut q_out {
let q = q - over_estimated.select(0, 1);
if j + 1 < q_out.nlimbs() {
q_out.store_l_full(j, q);
} else if j + 1 == q_out.nlimbs() {
if ct_is_nonzero_l(q & !q_out.partial_high_mask()) != 0 {
return Err(CtDivPow2MpError::InsufficientQuotientSpace);
}
q_out.store_l(j, q);
} else {
debug_assert_eq!(j, q_out.nlimbs());
if ct_is_nonzero_l(q) != 0 {
return Err(CtDivPow2MpError::InsufficientQuotientSpace);
}
}
}
let mut v_decrement_borrow = v.v_decrement.unwrap_or(0);
let mut carry = 0;
let mut i = 0;
while i + 2 < v_nlimbs {
let mut u_val = r_out.load_l_full(i);
(v_decrement_borrow, carry, u_val) =
v.add_scaled_v_val_cond(u_val, i, v_decrement_borrow, carry, over_estimated);
r_out.store_l_full(i, u_val);
i += 1;
}
let r_out_head_shadow_cur_sliding_window_overlap = v_nlimbs.min(2);
for k in 0..r_out_head_shadow_cur_sliding_window_overlap {
let k = 2 - r_out_head_shadow_cur_sliding_window_overlap + k;
let mut u_val = r_out_head_shadow[k];
(v_decrement_borrow, carry, u_val) =
v.add_scaled_v_val_cond(u_val, i, v_decrement_borrow, carry, over_estimated);
r_out_head_shadow[k] = u_val;
i += 1;
}
debug_assert_eq!(i, v_nlimbs);
}
let mut u_h = 0;
debug_assert!(v_nlimbs > 1 || r_out_head_shadow[0] == 0);
for k in 0..v_nlimbs.min(2) {
let u_l = r_out_head_shadow[2 - 1 - k];
let u_val = v.unscale_val(u_l, u_h);
u_h = u_l;
r_out.store_l(v_nlimbs - 1 - k, u_val);
}
let mut j = v_nlimbs;
while j > 2 {
j -= 1;
let u_l = r_out.load_l_full(j - 2);
let u_val = v.unscale_val(u_l, u_h);
u_h = u_l;
r_out.store_l_full(j - 2, u_val);
}
Ok(())
}
#[cfg(test)]
fn test_ct_div_pow2_mp<RT: MpMutUIntSlice, VT: MpMutUIntSlice, QT: MpMutUIntSlice>() {
fn div_and_check<RT: MpMutUIntSlice, VT: MpMutUIntSlice, QT: MpMutUIntSlice>(
u_pow2_exp: usize,
v: &VT::SelfT<'_>,
v_decrement: Option<LimbType>,
) {
use super::add::{ct_add_mp_mp, ct_sub_mp_l};
use super::mul::ct_mul_trunc_mp_mp;
let mut decremented_v = tst_mk_mp_backing_vec!(VT, v.len());
let mut decremented_v = VT::from_slice(&mut decremented_v).unwrap();
decremented_v.copy_from(v);
if let Some(v_decrement) = v_decrement {
ct_sub_mp_l(&mut decremented_v, v_decrement);
}
let u_len = (u_pow2_exp + 1).div_ceil(8);
let v_len = find_last_set_byte_mp(&decremented_v);
let q_len = if u_len >= v_len { u_len - v_len + 1 } else { 0 };
let mut q = tst_mk_mp_backing_vec!(QT, q_len);
q.fill(0xffu8.into());
let mut q = QT::from_slice(&mut q).unwrap();
let mut rem = tst_mk_mp_backing_vec!(RT, v_len);
rem.fill(0xffu8.into());
let mut rem = RT::from_slice(&mut rem).unwrap();
ct_div_pow2_mp(
u_pow2_exp,
&mut rem,
&CtMpDivisor::new(v, v_decrement).unwrap(),
Some(&mut q),
)
.unwrap();
let mut result = tst_mk_mp_backing_vec!(QT, u_len.max(v_len) + LIMB_BYTES);
result.fill(0xffu8.into());
let mut result = QT::from_slice(&mut result).unwrap();
result.copy_from(&q);
ct_mul_trunc_mp_mp(&mut result, q_len, &decremented_v);
let carry = ct_add_mp_mp(&mut result, &rem);
assert_eq!(carry, 0);
let u_nlimbs = ct_mp_nlimbs(u_len);
for i in 0..u_nlimbs - 1 {
assert_eq!(result.load_l_full(i), 0);
}
let expected_high = (1 as LimbType) << (u_pow2_exp % (LIMB_BITS as usize));
assert_eq!(result.load_l_full(u_nlimbs - 1), expected_high);
assert_eq!(result.load_l(u_nlimbs), 0);
}
let mut v = tst_mk_mp_backing_vec!(VT, LIMB_BYTES);
for v0 in [1 as LimbType, 7, 13, 17, 251] {
for k in 0..LIMB_BYTES {
let v0 = v0 << (8 * k);
let mut v = VT::from_slice(v.as_mut_slice()).unwrap();
v.store_l(0, v0);
for i in 0..5 * LIMB_BITS as usize {
div_and_check::<RT, VT, QT>(i, &v, None);
if v0 != 1 {
div_and_check::<RT, VT, QT>(i, &v, Some(1));
}
}
}
}
let mut v = tst_mk_mp_backing_vec!(VT, 2 * LIMB_BYTES);
for v_h in [0 as LimbType, 1, 7, 13, 17, 251] {
for v_l in [0 as LimbType, 1, 7, 13, 17, 251] {
if v_h == 0 && v_l == 0 {
continue;
}
for k in 0..LIMB_BYTES {
let v_h = v_h << (8 * k);
let mut v = VT::from_slice(v.as_mut_slice()).unwrap();
v.store_l(0, v_l);
v.store_l(1, v_h);
for i in 0..6 * LIMB_BITS as usize {
div_and_check::<RT, VT, QT>(i, &v, None);
if v_l != 0 && v_h != 0 {
div_and_check::<RT, VT, QT>(i, &v, Some(v_l + 1));
}
}
}
}
}
}
#[test]
fn test_ct_div_pow2_be_be_be() {
use super::limbs::MpMutBigEndianUIntByteSlice;
test_ct_div_pow2_mp::<
MpMutBigEndianUIntByteSlice,
MpMutBigEndianUIntByteSlice,
MpMutBigEndianUIntByteSlice,
>()
}
#[test]
fn test_ct_div_pow2_le_le_le() {
use super::limbs::MpMutLittleEndianUIntByteSlice;
test_ct_div_pow2_mp::<
MpMutLittleEndianUIntByteSlice,
MpMutLittleEndianUIntByteSlice,
MpMutLittleEndianUIntByteSlice,
>()
}
#[test]
fn test_ct_div_pow2_ne_ne_ne() {
use super::limbs::MpMutNativeEndianUIntLimbsSlice;
test_ct_div_pow2_mp::<
MpMutNativeEndianUIntLimbsSlice,
MpMutNativeEndianUIntLimbsSlice,
MpMutNativeEndianUIntLimbsSlice,
>()
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum CtModPow2MpError {
InsufficientRemainderSpace,
}
pub fn ct_mod_pow2_mp<RT: MpMutUInt, VT: MpUIntCommon>(
u_pow2_exp: usize,
r_out: &mut RT,
v: &CtMpDivisor<VT>,
) -> Result<(), CtModPow2MpError> {
ct_div_pow2_mp::<_, _, MpMutNativeEndianUIntLimbsSlice>(u_pow2_exp, r_out, v, None).map_err(
|e| {
match e {
CtDivPow2MpError::InsufficientRemainderSpace => {
CtModPow2MpError::InsufficientRemainderSpace
}
CtDivPow2MpError::InsufficientQuotientSpace => {
unreachable!()
}
}
},
)
}
pub type CtDivLshiftedMpMpError = CtDivPow2MpError;
pub fn ct_div_lshifted_mp_mp<UT: MpMutUInt, VT: MpUIntCommon, QT: MpMutUInt>(
u: &mut UT,
u_in_len: usize,
u_lshift_len: usize,
v: &CtMpDivisor<VT>,
mut q_out: Option<&mut QT>,
) -> Result<(), CtDivLshiftedMpMpError> {
debug_assert!(u_in_len <= u.len());
let virtual_u_in_len = u_in_len + u_lshift_len;
let v_len = v.v_len;
let v_nlimbs = ct_mp_nlimbs(v_len);
if let Some(q_out) = &mut q_out {
if q_out.len() + v_len < virtual_u_in_len {
return Err(CtDivLshiftedMpMpError::InsufficientQuotientSpace);
}
}
if u.len() < v_len.min(virtual_u_in_len) {
return Err(CtDivLshiftedMpMpError::InsufficientRemainderSpace);
}
if virtual_u_in_len < v_len {
ct_lshift_mp(u, 8 * u_lshift_len);
if let Some(q_out) = q_out {
q_out.clear_bytes_above(0);
}
return Ok(());
}
let q_out_max_len = virtual_u_in_len + 1 - v_len;
let q_out_max_nlimbs = ct_mp_nlimbs(q_out_max_len);
if let Some(q_out) = &mut q_out {
q_out.clear_bytes_above(q_out_max_len);
}
let u_lshift_head_len = u_lshift_len % LIMB_BYTES;
let u_lshift_tail_len = u_lshift_len - u_lshift_head_len;
let u_lshift_head_len = ((
ct_mp_nlimbs(
(u.len() - u_in_len + LIMB_BYTES - u_lshift_head_len) + 1, ) - 1
) * LIMB_BYTES)
.min(u_lshift_tail_len)
+ u_lshift_head_len;
debug_assert_eq!(u_lshift_head_len % LIMB_BYTES, u_lshift_len % LIMB_BYTES);
let u_lshift_tail_len = u_lshift_len - u_lshift_head_len;
let mut u_head_high_shadow: [LimbType; 3] = [0; 3];
u_head_high_shadow[0] = ct_lshift_mp(u, 8 * u_lshift_head_len);
let u_head_high_partial_len = u.len() % LIMB_BYTES;
let u_nlimbs = ct_mp_nlimbs(u.len());
if u_head_high_partial_len != 0 {
u_head_high_shadow[1] =
u_head_high_shadow[0] >> (8 * (LIMB_BYTES - (u_head_high_partial_len)));
u_head_high_shadow[0] <<= 8 * u_head_high_partial_len;
u_head_high_shadow[0] |= u.load_l(u_nlimbs - 1);
} else {
u_head_high_shadow[1] = u_head_high_shadow[0];
u_head_high_shadow[0] = u.load_l(u_nlimbs - 1);
}
let mut carry = 0;
for i in 0..u_nlimbs - 1 {
let mut u_val = u.load_l_full(i);
(carry, u_val) = v.scale_val(u_val, carry);
u.store_l_full(i, u_val);
}
for u_val in u_head_high_shadow.iter_mut().take(3 - 1) {
(carry, *u_val) = v.scale_val(*u_val, carry);
}
u_head_high_shadow[2] = carry;
let u_lshift_tail_nlimbs = u_lshift_tail_len / LIMB_BYTES; let q_out_head_nlimbs = q_out_max_nlimbs - u_lshift_tail_nlimbs;
let mut j = q_out_head_nlimbs;
while j > 0 {
j -= 1;
let q = {
let mut cur_u_head: [LimbType; 3] = [0; 3];
let mut i = 3;
while i > 0 && v_nlimbs + j + i >= u_nlimbs - 1 + 3 {
i -= 1;
cur_u_head[3 - i - 1] = u_head_high_shadow[(v_nlimbs + j + i - 2) - (u_nlimbs - 1)];
}
while i > 0 && v_nlimbs + j + i >= 3 {
i -= 1;
cur_u_head[3 - i - 1] = u.load_l_full(v_nlimbs + j + i - 2);
}
v.make_q_estimate(&cur_u_head)
};
let mut v_decrement_borrow = v.v_decrement.unwrap_or(0);
let mut scaled_v_carry = 0;
let mut borrow = 0;
let mut i = 0;
while i < v_nlimbs && j + i < u_nlimbs - 1 {
let mut u_val = u.load_l_full(j + i);
(v_decrement_borrow, scaled_v_carry, borrow, u_val) =
v.sub_scaled_qv_val(u_val, i, q, v_decrement_borrow, scaled_v_carry, borrow);
u.store_l_full(j + i, u_val);
i += 1;
}
while i < v_nlimbs {
let mut u_val = u_head_high_shadow[j + i - (u_nlimbs - 1)];
(v_decrement_borrow, scaled_v_carry, borrow, u_val) =
v.sub_scaled_qv_val(u_val, i, q, v_decrement_borrow, scaled_v_carry, borrow);
u_head_high_shadow[j + i - (u_nlimbs - 1)] = u_val;
i += 1;
}
debug_assert_eq!(scaled_v_carry, 0);
assert_eq!(i, v_nlimbs);
if j + i < u_nlimbs - 1 {
let mut u_val = u.load_l_full(j + i);
(borrow, u_val) = ct_sub_l_l(u_val, borrow);
u.store_l_full(j + i, u_val);
} else {
let mut u_val = u_head_high_shadow[j + i - (u_nlimbs - 1)];
(borrow, u_val) = ct_sub_l_l(u_val, borrow);
u_head_high_shadow[j + i - (u_nlimbs - 1)] = u_val;
}
let over_estimated = LimbChoice::from(borrow);
if let Some(q_out) = &mut q_out {
let q = q - over_estimated.select(0, 1);
if u_lshift_tail_nlimbs + j + 1 < q_out.nlimbs() {
q_out.store_l_full(u_lshift_tail_nlimbs + j, q);
} else if u_lshift_tail_nlimbs + j + 1 == q_out.nlimbs() {
if ct_is_nonzero_l(q & !q_out.partial_high_mask()) != 0 {
return Err(CtDivLshiftedMpMpError::InsufficientQuotientSpace);
}
q_out.store_l(u_lshift_tail_nlimbs + j, q);
} else {
debug_assert_eq!(u_lshift_tail_nlimbs + j, q_out.nlimbs());
if ct_is_nonzero_l(q) != 0 {
return Err(CtDivLshiftedMpMpError::InsufficientQuotientSpace);
}
}
}
let mut v_decrement_borrow = v.v_decrement.unwrap_or(0);
let mut carry = 0;
let mut i = 0;
while i < v_nlimbs && j + i < u_nlimbs - 1 {
let mut u_val = u.load_l_full(j + i);
(v_decrement_borrow, carry, u_val) =
v.add_scaled_v_val_cond(u_val, i, v_decrement_borrow, carry, over_estimated);
u.store_l_full(j + i, u_val);
i += 1;
}
while i < v_nlimbs {
let mut u_val = u_head_high_shadow[j + i - (u_nlimbs - 1)];
(v_decrement_borrow, carry, u_val) =
v.add_scaled_v_val_cond(u_val, i, v_decrement_borrow, carry, over_estimated);
u_head_high_shadow[j + i - (u_nlimbs - 1)] = u_val;
i += 1;
}
assert_eq!(i, v_nlimbs);
if j + i < u_nlimbs - 1 {
let mut u_val = u.load_l_full(j + i);
(_, u_val) = ct_add_l_l(u_val, carry);
u.store_l_full(j + i, u_val);
} else {
let mut u_val = u_head_high_shadow[j + i - (u_nlimbs - 1)];
(_, u_val) = ct_add_l_l(u_val, carry);
u_head_high_shadow[j + i - (u_nlimbs - 1)] = u_val;
}
}
debug_assert_eq!(u_head_high_shadow[2], 0);
debug_assert_eq!(u_head_high_shadow[1], 0);
if u_nlimbs > v_nlimbs {
debug_assert_eq!(u_head_high_shadow[0], 0);
u.store_l(u_nlimbs - 1, 0);
}
for i in v_nlimbs..u_nlimbs - 1 {
debug_assert_eq!(u.load_l_full(i), 0);
}
let mut u_high_shadow = if v_nlimbs - 1 == u_nlimbs - 1 {
u_head_high_shadow[0]
} else {
u.load_l_full(v_nlimbs - 1)
};
let mut j = u_lshift_tail_nlimbs;
while j > 0 {
j -= 1;
let q = {
let mut cur_u_head: [LimbType; 3] = [0; 3];
cur_u_head[0] = u_high_shadow;
cur_u_head[1] = if v_nlimbs >= 2 {
u.load_l_full(v_nlimbs - 2)
} else {
0 };
cur_u_head[2] = if v_nlimbs >= 3 {
u.load_l_full(v_nlimbs - 3)
} else {
0 };
v.make_q_estimate(&cur_u_head)
};
let mut next_u_val = 0; let mut v_decrement_borrow = v.v_decrement.unwrap_or(0);
let mut scaled_v_carry = 0;
let mut borrow = 0;
for i in 0..v_nlimbs - 1 {
let mut u_val = next_u_val;
next_u_val = u.load_l_full(i);
(v_decrement_borrow, scaled_v_carry, borrow, u_val) =
v.sub_scaled_qv_val(u_val, i, q, v_decrement_borrow, scaled_v_carry, borrow);
u.store_l_full(i, u_val);
}
{
let i = v_nlimbs - 1;
let mut u_val = next_u_val;
next_u_val = u_high_shadow;
(_, scaled_v_carry, borrow, u_val) =
v.sub_scaled_qv_val(u_val, i, q, v_decrement_borrow, scaled_v_carry, borrow);
debug_assert_eq!(scaled_v_carry, 0);
u_high_shadow = u_val;
}
(borrow, _) = ct_sub_l_l(next_u_val, borrow);
let over_estimated = LimbChoice::from(borrow);
if let Some(q_out) = &mut q_out {
let q = q - over_estimated.select(0, 1);
if j + 1 < q_out.nlimbs() {
q_out.store_l_full(j, q);
} else if j + 1 == q_out.nlimbs() {
if ct_is_nonzero_l(q & !q_out.partial_high_mask()) != 0 {
return Err(CtDivLshiftedMpMpError::InsufficientQuotientSpace);
}
q_out.store_l(j, q);
} else {
debug_assert_eq!(j, q_out.nlimbs());
if ct_is_nonzero_l(q) != 0 {
return Err(CtDivLshiftedMpMpError::InsufficientQuotientSpace);
}
}
}
let mut v_decrement_borrow = v.v_decrement.unwrap_or(0);
let mut carry = 0;
for i in 0..v_nlimbs - 1 {
let mut u_val = u.load_l_full(i);
(v_decrement_borrow, carry, u_val) =
v.add_scaled_v_val_cond(u_val, i, v_decrement_borrow, carry, over_estimated);
u.store_l_full(i, u_val);
}
{
let i = v_nlimbs - 1;
let mut u_val = u_high_shadow;
(_, _, u_val) =
v.add_scaled_v_val_cond(u_val, i, v_decrement_borrow, carry, over_estimated);
u_high_shadow = u_val;
}
}
let mut u_h = 0;
{
let u_l = u_high_shadow;
let u_val = v.unscale_val(u_l, u_h);
u_h = u_l;
u.store_l(v_nlimbs - 1, u_val);
}
let mut j = v_nlimbs - 1;
while j > 0 {
j -= 1;
let u_l = u.load_l_full(j);
let u_val = v.unscale_val(u_l, u_h);
u_h = u_l;
u.store_l(j, u_val);
}
Ok(())
}
#[cfg(test)]
fn test_ct_div_lshifted_mp_mp<UT: MpMutUIntSlice, VT: MpMutUIntSlice, QT: MpMutUIntSlice>() {
fn div_and_check<UT: MpMutUIntSlice, VT: MpMutUIntSlice, QT: MpMutUIntSlice>(
u: &UT::SelfT<'_>,
u_in_len: usize,
u_lshift_len: usize,
v: &VT::SelfT<'_>,
v_decrement: Option<LimbType>,
) {
use super::add::{ct_add_mp_mp, ct_sub_mp_l};
use super::cmp::ct_eq_mp_mp;
use super::mul::ct_mul_trunc_mp_mp;
use super::shift::ct_rshift_mp;
let mut decremented_v = tst_mk_mp_backing_vec!(VT, v.len());
let mut decremented_v = VT::from_slice(&mut decremented_v).unwrap();
decremented_v.copy_from(v);
if let Some(v_decrement) = v_decrement {
ct_sub_mp_l(&mut decremented_v, v_decrement);
}
let v_len = find_last_set_byte_mp(&decremented_v);
let virtual_u_len = u_in_len + u_lshift_len;
let q_len = virtual_u_len + 1 - v_len;
let mut q = tst_mk_mp_backing_vec!(QT, q_len);
q.fill(0xffu8.into());
let mut q = QT::from_slice(q.as_mut_slice()).unwrap();
let mut rem = tst_mk_mp_backing_vec!(UT, u.len());
let mut rem = UT::from_slice(&mut rem).unwrap();
rem.copy_from(u);
ct_div_lshifted_mp_mp(
&mut rem,
u_in_len,
u_lshift_len,
&CtMpDivisor::new(v, v_decrement).unwrap(),
Some(&mut q),
)
.unwrap();
let mut result = tst_mk_mp_backing_vec!(UT, virtual_u_len + LIMB_BYTES);
result.fill(0xffu8.into());
let mut result = UT::from_slice(&mut result).unwrap();
result.copy_from(&q);
ct_mul_trunc_mp_mp(&mut result, q_len, &decremented_v);
let carry = ct_add_mp_mp(&mut result, &rem);
assert_eq!(carry, 0);
for i in 0..ct_mp_nlimbs(u_lshift_len + 1) - 1 {
assert_eq!(result.load_l(i), 0);
}
if !u_lshift_len.is_multiple_of(LIMB_BYTES) {
let u_val = result.load_l(ct_mp_nlimbs(u_lshift_len + 1) - 1);
assert_eq!(
u_val & ct_lsb_mask_l(8 * (u_lshift_len % LIMB_BYTES) as u32),
0
);
}
assert_eq!(result.load_l(ct_mp_nlimbs(virtual_u_len)), 0);
ct_rshift_mp(&mut result, 8 * u_lshift_len);
assert_eq!(ct_eq_mp_mp(u, &result).unwrap(), 1);
}
const N_MAX_LIMBS: u32 = 3;
for i in 0..N_MAX_LIMBS * LIMB_BITS + 1 {
let u_len = (i as usize).div_ceil(8);
for j1 in 0..i + 1 {
for j2 in 0..j1 + 1 {
let v_len = ((j1 + 1) as usize).div_ceil(8);
for u_lshift_len in [
0,
LIMB_BYTES - 1,
LIMB_BYTES,
LIMB_BYTES + 1,
2 * LIMB_BYTES - 1,
2 * LIMB_BYTES,
] {
let mut u = tst_mk_mp_backing_vec!(UT, u_len.max(v_len));
let mut u = UT::from_slice(&mut u).unwrap();
if i != 0 {
let u_nlimbs = ct_mp_nlimbs(u_len);
for k in 0..u_nlimbs - 1 {
u.store_l(k, !0);
}
if i % LIMB_BITS != 0 {
let i = i % LIMB_BITS;
u.store_l(u_nlimbs - 1, !0 >> (LIMB_BITS - i));
} else {
u.store_l(u_nlimbs - 1, !0);
}
}
let mut v = tst_mk_mp_backing_vec!(VT, v_len);
let mut v = VT::from_slice(&mut v).unwrap();
v.store_l((j1 / LIMB_BITS) as usize, 1 << (j1 % LIMB_BITS));
v.store_l(
(j2 / LIMB_BITS) as usize,
v.load_l((j2 / LIMB_BITS) as usize) | 1 << (j2 % LIMB_BITS),
);
div_and_check::<UT, VT, QT>(&u, u_len, u_lshift_len, &v, None);
if j1 != 0 {
div_and_check::<UT, VT, QT>(&u, u_len, u_lshift_len, &v, Some(1));
}
}
}
}
}
}
#[test]
fn test_ct_div_lshifted_be_be_be() {
use super::limbs::MpMutBigEndianUIntByteSlice;
test_ct_div_lshifted_mp_mp::<
MpMutBigEndianUIntByteSlice,
MpMutBigEndianUIntByteSlice,
MpMutBigEndianUIntByteSlice,
>()
}
#[test]
fn test_ct_div_lshifted_le_le_le() {
use super::limbs::MpMutLittleEndianUIntByteSlice;
test_ct_div_lshifted_mp_mp::<
MpMutLittleEndianUIntByteSlice,
MpMutLittleEndianUIntByteSlice,
MpMutLittleEndianUIntByteSlice,
>()
}
#[test]
fn test_ct_div_lshifted_ne_ne_ne() {
use super::limbs::MpMutNativeEndianUIntLimbsSlice;
test_ct_div_lshifted_mp_mp::<
MpMutNativeEndianUIntLimbsSlice,
MpMutNativeEndianUIntLimbsSlice,
MpMutNativeEndianUIntLimbsSlice,
>()
}
pub type CtModLshiftedMpMpError = CtModPow2MpError;
pub fn ct_mod_lshifted_mp_mp<UT: MpMutUInt, VT: MpUIntCommon>(
u: &mut UT,
u_in_len: usize,
u_lshift_len: usize,
v: &CtMpDivisor<VT>,
) -> Result<(), CtModLshiftedMpMpError> {
ct_div_lshifted_mp_mp::<_, _, MpMutNativeEndianUIntLimbsSlice>(
u,
u_in_len,
u_lshift_len,
v,
None,
)
.map_err(|e| match e {
CtDivLshiftedMpMpError::InsufficientRemainderSpace => {
CtModLshiftedMpMpError::InsufficientRemainderSpace
}
CtDivLshiftedMpMpError::InsufficientQuotientSpace => {
unreachable!()
}
})
}
pub type CtDivMpLError = CtDivMpMpError;
pub fn ct_div_mp_l<UT: MpUIntCommon, QT: MpMutUInt>(
u: &UT,
v: &CtLDivisor,
mut q_out: Option<&mut QT>,
) -> Result<LimbType, CtDivMpLError> {
let u_nlimbs = ct_mp_nlimbs(u.len());
if u_nlimbs == 0 {
return Ok(0);
}
if let Some(q_out) = &mut q_out {
let v_len = ct_find_last_set_byte_l(v.get_v());
if q_out.len() + v_len < u.len() + 1 {
return Err(CtDivMpLError::InsufficientQuotientSpace);
}
if u.len() < v_len {
q_out.clear_bytes_above(0);
return Ok(u.load_l(0));
}
let q_out_len = u.len() - v_len + 1;
q_out.clear_bytes_above(q_out_len);
}
let mut u_h = 0;
let mut j = u_nlimbs;
while j > 0 {
j -= 1;
let u_l = u.load_l(j);
let (q_val, r) = v.do_div(&DoubleLimb::new(u_h, u_l));
if let Some(q_out) = &mut q_out {
if j + 1 < q_out.nlimbs() {
q_out.store_l_full(j, q_val);
} else if j + 1 == q_out.nlimbs() {
q_out.store_l(j, q_val);
} else {
debug_assert_eq!(q_val, 0);
}
}
u_h = r;
}
Ok(u_h)
}
pub fn ct_mod_mp_l<UT: MpUIntCommon>(u: &UT, v: &CtLDivisor) -> LimbType {
ct_div_mp_l::<_, MpMutNativeEndianUIntLimbsSlice>(u, v, None)
.map_err(|e| match e {
CtDivMpLError::InsufficientQuotientSpace => {
unreachable!()
}
})
.unwrap()
}
#[cfg(test)]
fn test_ct_div_q_out_partial_high_limb<
UT: MpMutUIntSlice,
VT: MpMutUIntSlice,
QT: MpMutUIntSlice,
>() {
use super::cmp::ct_is_zero_mp;
use super::limbs::MpUIntCommonPriv as _;
let v_len = 2;
let u_len = 2 * LIMB_BYTES + 1 + v_len;
let q_len = u_len - v_len;
debug_assert_eq!(ct_mp_nlimbs(q_len), ct_mp_nlimbs(q_len + 1));
let mut v = tst_mk_mp_backing_vec!(VT, v_len);
let mut v = VT::from_slice(&mut v).unwrap();
v.store_l(0, 0x100);
let v = CtMpDivisor::new(&v, None).unwrap();
for u_high_is_zero in [false, true] {
let mut u = tst_mk_mp_backing_vec!(UT, u_len);
u.fill(0xffu8.into());
let mut u = UT::from_slice(&mut u).unwrap();
if u_high_is_zero {
u.clear_bytes_above(u_len - 1);
}
let mut q = tst_mk_mp_backing_vec!(QT, q_len);
let mut q = QT::from_slice(&mut q).unwrap();
let result = ct_div_mp_mp(None, &mut u, &v, Some(&mut q));
if u_high_is_zero {
result.unwrap();
assert_eq!(u.load_l(0), 0xff);
u.store_l(0, 0);
assert_ne!(ct_is_zero_mp(&u).unwrap(), 0);
for i in 0..q.nlimbs() - 1 {
assert_eq!(q.load_l_full(i), !0);
}
assert_eq!(q.load_l(q.nlimbs() - 1), q.partial_high_mask());
} else {
assert!(matches!(
result,
Err(CtDivMpMpError::InsufficientQuotientSpace)
));
}
let u_in_len = LIMB_BYTES + 2;
let u_lshift_len = u_len - u_in_len;
let mut u = tst_mk_mp_backing_vec!(UT, u_in_len);
u.fill(0xffu8.into());
let mut u = UT::from_slice(&mut u).unwrap();
if u_high_is_zero {
u.clear_bytes_above(u_in_len - 1);
}
let mut q = tst_mk_mp_backing_vec!(QT, q_len);
let mut q = QT::from_slice(&mut q).unwrap();
let result = ct_div_lshifted_mp_mp(&mut u, u_in_len, u_lshift_len, &v, Some(&mut q));
if u_high_is_zero {
result.unwrap();
} else {
assert!(matches!(
result,
Err(CtDivLshiftedMpMpError::InsufficientQuotientSpace)
));
}
}
let mut r = tst_mk_mp_backing_vec!(UT, v_len);
let mut r = UT::from_slice(&mut r).unwrap();
let mut q = tst_mk_mp_backing_vec!(QT, q_len);
let mut q = QT::from_slice(&mut q).unwrap();
assert!(matches!(
ct_div_pow2_mp(8 * (u_len - 1), &mut r, &v, Some(&mut q)),
Err(CtDivPow2MpError::InsufficientQuotientSpace)
));
let mut v = tst_mk_mp_backing_vec!(VT, v_len);
let mut v = VT::from_slice(&mut v).unwrap();
v.store_l(0, 0x101);
let v = CtMpDivisor::new(&v, None).unwrap();
ct_div_pow2_mp(8 * (u_len - 1), &mut r, &v, Some(&mut q)).unwrap();
}
#[test]
fn test_ct_div_q_out_partial_high_limb_be_be_be() {
use super::limbs::MpMutBigEndianUIntByteSlice;
test_ct_div_q_out_partial_high_limb::<
MpMutBigEndianUIntByteSlice,
MpMutBigEndianUIntByteSlice,
MpMutBigEndianUIntByteSlice,
>()
}
#[test]
fn test_ct_div_q_out_partial_high_limb_le_le_le() {
use super::limbs::MpMutLittleEndianUIntByteSlice;
test_ct_div_q_out_partial_high_limb::<
MpMutLittleEndianUIntByteSlice,
MpMutLittleEndianUIntByteSlice,
MpMutLittleEndianUIntByteSlice,
>()
}
#[cfg(test)]
fn test_ct_div_mp_l<UT: MpMutUIntSlice, QT: MpMutUIntSlice>() {
let u_len = 2 * LIMB_BYTES + 1;
let v = CtLDivisor::new(1 << (LIMB_BITS - 1)).unwrap();
let q_len = u_len - LIMB_BYTES + 1;
assert!(ct_mp_nlimbs(q_len) < ct_mp_nlimbs(u_len));
for r_expected in [0, 5] {
let mut u = tst_mk_mp_backing_vec!(UT, u_len);
let mut u = UT::from_slice(&mut u).unwrap();
u.store_l(0, r_expected);
u.store_l(2, 1);
let mut q = tst_mk_mp_backing_vec!(QT, q_len);
let mut q = QT::from_slice(&mut q).unwrap();
let r = ct_div_mp_l(&u, &v, Some(&mut q)).unwrap();
assert_eq!(r, r_expected);
assert_eq!(q.load_l(0), 0);
assert_eq!(q.load_l(1), 2);
assert_eq!(ct_mod_mp_l(&u, &v), r_expected);
}
}
#[test]
fn test_ct_div_mp_l_be_be() {
use super::limbs::MpMutBigEndianUIntByteSlice;
test_ct_div_mp_l::<MpMutBigEndianUIntByteSlice, MpMutBigEndianUIntByteSlice>()
}
#[test]
fn test_ct_div_mp_l_le_le() {
use super::limbs::MpMutLittleEndianUIntByteSlice;
test_ct_div_mp_l::<MpMutLittleEndianUIntByteSlice, MpMutLittleEndianUIntByteSlice>()
}