[no description]
Diffstat (limited to 'src/xp.rs')
| -rw-r--r-- | src/xp.rs | 462 |
1 files changed, 462 insertions, 0 deletions
diff --git a/src/xp.rs b/src/xp.rs new file mode 100644 index 0000000..10759f3 --- /dev/null +++ b/src/xp.rs @@ -0,0 +1,462 @@ +//! C Interfaces and Implementations: Techniques for Creating Reusable Software [Hanson 1996-08-30] +//! + +use std::cmp::Ordering; +#[cfg(test)] +mod test; +/// An extended-iprecision unsigned integer is represented in base by an +/// array of n digits, least significant digit first. Most XP functions take n as +/// an argument along with source and destination Ts; it is SAD for n<1 or +/// for n not to be the length of the corresponding Ts. It is SAD to pass a +/// null T or a T that is too small to any XP function. +pub type XP = [u8]; + +macro_rules! base { + () => { + 256 + }; +} +/// sets z[0..n-1] to u mod 2⁸ⁿ and returns u/2⁸ⁿ. +pub const fn from_int(n: u32, z: &mut XP, mut u: u128) -> u128 { + let mut i = 0; + let n = n as usize; + while { + z[i] = (u % base!()) as u8; + i += 1; + + u /= base!(); + u > 0 && i < n + } {} + while i < n { + z[i] = 0; + i += 1; + } + u +} +/// returns x mod (ULONG_MAX+1). +pub const fn to_int(n: u32, x: &XP) -> u128 { + let mut u = 0; + let mut i = size_of::<u128>().min(n as usize); + + while i > 0 { + i -= 1; + u = base!() * u + x[i] as u128; + } + u +} +/// returns the length of x; that is, the index plus one of the most signifi- +/// cant nonzero digit in x[0..n-1]. +pub const fn len(mut n: u32, x: &XP) -> u32 { + while (n > 1 && x[n as usize - 1] == 0) { + n -= 1; + } + n +} +macro_rules! u1 { + ($x:expr) => { + match $x { + 0 => false, + 1 => true, + _ => panic!("not u1"), + } + }; +} + +/// sets z[0..n-1] to x + y + carry and returns the carry out of z[n-1]. +pub const fn add(n: u32, z: &mut XP, x: &XP, y: &XP, carry: bool) -> bool { + let mut carry = carry as usize; + let mut i = 0; + while i < n as usize { + carry += x[i] as usize + y[i] as usize; + z[i] = (carry % base!()) as u8; + carry /= base!(); + + i += 1; + } + u1!(carry) +} +/// sets z[0..n-1] to x − y − borrow and returns the borrow into z[n-1]. +pub const fn sub( + n: u32, + z: &mut XP, + x: &XP, + y: &XP, + borrow: bool, +) -> bool { + let mut i = 0; + let mut borrow = borrow as u32; + while i < n as usize { + let mut d = (x[i] as u32 + base!()) - borrow - y[i] as u32; + z[i] = (d % base!()) as u8; + borrow = 1 - (d / base!()); + i += 1; + } + u1!(borrow) +} +#[doc(hidden)] +pub const unsafe fn subp( + n: u32, + z: *mut u8, + x: *const u8, + y: *const u8, + borrow: bool, +) -> bool { + let mut i = 0; + let mut borrow = borrow as u32; + while i < n as usize { + let mut d = + (*x.add(i) as u32 + base!()) - borrow - *y.add(i) as u32; + *z.add(i) = (d % base!()) as u8; + borrow = 1 - (d / base!()); + i += 1; + } + u1!(borrow) +} +/// sets z[0..n-1] to x + y, where y is a single digit, and returns the carry-out of z[n-1]. +pub const fn sum(n: u32, z: &mut XP, x: &XP, mut y: u8) -> u8 { + unsafe { sum_p(n, z.as_mut_ptr(), x.as_ptr(), y) } +} +#[doc(hidden)] +pub const unsafe fn sum_p( + n: u32, + z: *mut u8, + x: *const u8, + mut y: u8, +) -> u8 { + let mut i = 0; + let mut y = y as u32; + while i < n as usize { + y += *x.add(i) as u32; + *z.add(i) = (y % base!()) as u8; + y /= base!(); + i += 1; + } + y as u8 +} +/// sets z[0..n-1] to x − y, where y is a single digit, and returns the borrow into z[n-1]. +pub const fn diff(n: u32, z: &mut XP, x: &XP, y: u8) -> u8 { + let mut y = y as u32; + let mut i = 0; + while i < n as usize { + let d = (x[i] as u32 + base!()) - y; + z[i] = (d % base!()) as u8; + y = 1 - d / base!(); + i += 1; + } + y as u8 +} +/// sets z[0..n-1] to !x + carry, where carry is zero or one, and returns +/// the carry-out of z[n-1]. +pub const fn neg(n: u32, z: &mut XP, x: &XP, carry: bool) -> bool { + let mut carry = carry as u32; + let mut i = 0; + while i < n as usize { + carry += (!x[i]) as u32; + z[i] = (carry % base!()) as u8; + carry /= base!(); + i += 1; + } + u1!(carry) +} +/// adds x[0..n-1]•y[0..m-1] to z[0..n+m-1] and returns the carry-out of +/// z[n+m-1]. If z=0, Xmul computes x•y. It is SAD for z to be the same T as x or y. +pub const fn mul(z: &mut XP, n: u32, x: &XP, m: u32, y: &XP) -> u32 { + let (mut i, mut carryout) = (0, 0); + while i < n as usize { + let mut carry = 0; + let mut j = 0; + while j < m as usize { + carry += x[i as usize] as u32 * y[j as usize] as u32 + + z[i + j] as u32; + z[i + j] = (carry % base!()) as u8; + carry /= base!(); + j += 1; + } + while j < (n as usize + m as usize - i) { + carry += z[i + j] as u32; + z[i + j] = (carry % base!()) as u8; + carry /= base!(); + j += 1; + } + assert!(carry == 0 || carry == 1); + carryout |= carry; + i += 1; + } + carryout +} + +const unsafe fn quot_p(n: u32, z: *mut u8, x: *const u8, y: u8) -> u8 { + let mut i = n as isize - 1; + let mut carry = 0; + + while i >= 0 { + carry = carry * base!() + (*x.add(i as _)) as u32; + *z.add(i as _) = (carry / y as u32) as _; + carry %= y as u32; + i -= 1; + } + carry as u8 +} +/// sets z[0..n-1] to x/y, where y is a single digit, and returns x mod y. It +/// is SAD for y=0 +pub const fn quot(n: u32, z: &mut XP, x: &XP, y: u8) -> u8 { + unsafe { quot_p(n, z.as_mut_ptr(), x.as_ptr(), y) } +} +/// XP_product sets z[0..n-1] to x•y and returns the carry-out of the +/// most significant digit; the carry can be as large as 2^8- XP_quotient +/// sets z[0..n-1] to x/y and returns the remainder, x mod y; the remainder +/// can be as large as . For XP_product and XP_quotient, y must not +/// exceed − 1. +pub const fn product(n: u32, z: &mut XP, x: &XP, y: u8) -> u8 { + unsafe { product_p(n, z.as_mut_ptr(), x.as_ptr(), y) } +} +const unsafe fn product_p(n: u32, z: *mut u8, x: *const u8, y: u8) -> u8 { + let mut i = 0; + let mut carry = 0; + while i < n as usize { + carry += *x.add(i) as u32 * y as u32; + *z.add(i) = (carry % base!()) as u8; + carry /= base!(); + i += 1; + } + carry.try_into().ok().unwrap() +} +/// sets q[0..n-1] to x[0..n-1]/y[0..m-1], r[0..m-1] to x[0..n-1] mod +/// y[0..m-1]. If y=0, panics. +/// tmp must hold at least n+m+2 digits. It is unexpected +/// for q or r to be one of x or y, for q and r to be the same T, or for +/// tmp to be too small +pub const fn div( + mut n: u32, + q: &mut XP, + x: &XP, + mut m: u32, + y: &XP, + r: &mut XP, + tmp: &mut XP, +) { + let nx = n; + let my = m; + n = len(n, x); + m = len(m, y); + if m == 1 { + if (y[0] == 0) { + panic!() + } + r[0] = quot(nx, q, x, y[0] as _); + let mut i = 0; + while i < my as usize - 1 { + r[1 + i] = 0; + i += 1; + } + } else if m > n { + let mut i = 0; + while i < nx as usize { + q[i] = 0; + i += 1; + } + r[..n as usize].copy_from_slice(&x[..n as usize]); + let mut i = 0; + while i < my - n { + r[(n + i) as usize] = 0; + i += 1; + } + } else { + let (rem, dq) = tmp.split_at_mut(n as usize + 1); + // let rem = &mut tmp[..n as usize]; + // let dq = &mut tmp[n as usize + 1..]; + assert!(2 <= m && m <= n); + rem[..n as usize].copy_from_slice(&x[..n as usize]); + rem[n as usize] = 0; + let mut k = (n - m) as i32; + while k >= 0 { + let k_ = k as u32; + let qk = { + let i = 0; + assert!(2 <= m && m <= k_ + m && k_ + m <= n); + + let mut qk = { + let km = (k_ + m) as usize; + let y2 = y[m as usize - 1] as u64 * base!() as u64 + + y[m as usize - 2] as u64; + let r3 = rem[km] as u64 * (base!() * base!()) + + rem[km - 1] as u64 * base!() + + rem[km - 2] as u64; + let mut qk = r3 / y2; + if (qk >= base!()) { + qk = base!() - 1 + }; + qk as u8 + }; + // qk y[m-2..m-1]/rem[k+m-2..k+m] 314 + dq[m as usize] = product(m, dq, y, qk) as _; + let mut i = m as i32; + // ch + while i > 0 && rem[(i + k) as usize] == dq[i as usize] { + i -= 1; + } + + if (rem[(i + k) as usize] < dq[i as usize]) { + qk -= 1; + dq[m as usize] = product(m, dq, y, qk) as _; + } + qk + }; + q[k as usize] = qk; + { + assert!(0 <= k && k <= k + m as i32); + let borrow = unsafe { + subp( + m + 1, + rem.as_mut_ptr().add(k as _), + rem.as_ptr().add(k as _), + dq.as_ptr(), + false, + ) + }; + assert!(!borrow); + } + k -= 1; + } + r[..m as usize].copy_from_slice(&rem[..m as usize]); + let mut i = n - m + 1; + while i < nx { + q[i as usize] = 0; + i += 1; + } + let mut i = m; + while i < my { + r[i as usize] = 0; + i += 1; + } + r[..m as usize].copy_from_slice(&rem[..m as usize]); + // memcpy(r, rem, m); + } +} + +/// sets z[0..n-1] to x[0..m-1] shifted left by s bits, and fills the vacated +/// bits with fill, which must be zero or one. It is SADk for s<0. +pub const fn shl( + n: u32, + z: &mut XP, + m: u32, + x: &XP, + mut s: u32, + fill: u8, +) { + assert!(matches!(fill, 0 | 0xff)); + let mut j = n as i32 - 1; + let mut i = (if (n > m) { m - 1 } else { n - s / 8 - 1 }) as i32; + while j >= (m + s / 8) as _ { + z[j as usize] = 0; + j -= 1; + } + //const fill when + while i >= 0 { + z[j as usize] = x[i as usize]; + j -= 1; + i -= 1; + } + while j >= 0 { + z[j as usize] = fill; + j -= 1; + } + s %= 8; + if s > 0 { + unsafe { product_p(n, z.as_mut_ptr(), z.as_ptr(), 1 << s) }; + z[0] |= fill >> (8 - s); + } +} +//// shifts right; see [`shl`]. If n>m, the excess bits are treated as if +/// they were equal to fill +pub const fn shr( + n: u32, + z: &mut XP, + m: u32, + x: &XP, + s: u32, + fill: u8, +) -> u8 { + assert!(matches!(fill, 0 | 0xff)); + let mut i = s / 8; + let mut j = 0; + while i < m && j < n { + z[j as usize] = x[i as usize]; + j += 1; + i += 1; + } + while j < n { + z[j as usize] = fill; + j += 1; + } + let s = s % 8; + if s > 0 { + let r = unsafe { quot_p(n, z.as_mut_ptr(), z.as_ptr(), 1 << s) }; + z[n as usize - 1] |= fill << (8 - s); + r + } else { + 0 + } +} +/// interprets str as an unsigned integer in base using z[0..n-1] as the +/// initial value in the conversion, and returns the first nonzero carry-out +/// of the conversion step. If end≠null, *end points to the character in +/// str that terminated the scan or produced a nonzero carry. +fn from_str(n: u32, z: &mut XP, base: u8, str: &[u8]) -> u8 { + assert!(base < 36); + let mut i = 0; + while i < str.len() { + let b = str[i]; + let b = match b { + b'0'..=b'9' => b - b'0', + b'a'..=b'z' => b - b'a', + _ => return 255, + }; + if b > base { + return 255; + } + let c = unsafe { product_p(n, z.as_mut_ptr(), z.as_ptr(), base) }; + unsafe { sum_p(n, z.as_mut_ptr(), z.as_ptr(), b as _) }; + + if c != 0 { + return c; + } + + i += 1; + } + 0 +} +/// fills str[0..size-1] with the character representation of x in base, +/// sets x to zero, and returns str. It is a panic for size to be +/// too small, or for base<2 or base>36. +pub const fn to_str<'a>( + put_in: &'a mut Vec<u8>, + base: u8, + mut n: u32, + x: &mut XP, +) -> &'a [u8] { + let mut i = 0; + assert!(base >= 2 && base <= 36); + while { + let r = unsafe { quot_p(n, x.as_mut_ptr(), x.as_ptr(), base) }; + put_in.push(b"0123456789abcdefghijklmnopqrstuvxyz"[r as usize]); + i += 1; + // str[i] = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"[r]; + while n > 1 && x[n as usize - 1] == 0 { + n -= 1; + } + n > 1 || x[0] != 0 + } {} + + put_in.as_mut_slice().reverse(); + put_in.as_slice() +} + +/// x[..n] <=> y[..n] +pub const fn cmp(n: u32, x: &XP, y: &XP) -> Ordering { + let mut i = n as i32 - 1; + while (i > 0 && x[i as usize] == y[i as usize]) { + i -= 1 + } + x[i as usize].cmp(&y[i as usize]) +} |