#[allow(clippy::useless_attribute)]
#[allow(clippy::wildcard_imports)]
use std::prelude::v1::*;
use crate::{
algorithms::div_2_1,
arch::{divrem_nby1, divrem_nbym, inv_mod},
noncommutative_binop, Binary, DivRem, InvMod, U256,
};
use num_traits::Inv;
use std::{
num::Wrapping,
ops::{Div, DivAssign, Rem, RemAssign},
u64,
};
impl InvMod for U256 {
#[inline(always)]
fn inv_mod(&self, modulus: &Self) -> Option<Self> {
inv_mod(modulus, self)
}
}
impl DivRem<u64> for U256 {
type Quotient = Self;
type Remainder = u64;
fn div_rem(&self, rhs: u64) -> Option<(Self, u64)> {
if rhs == 0 {
None
} else {
let (q3, r) = div_2_1(self.limb(3), 0, rhs);
let (q2, r) = div_2_1(self.limb(2), r, rhs);
let (q1, r) = div_2_1(self.limb(1), r, rhs);
let (q0, r) = div_2_1(self.limb(0), r, rhs);
Some((Self::from_limbs([q0, q1, q2, q3]), r))
}
}
}
impl DivRem<&Self> for U256 {
type Quotient = Self;
type Remainder = Self;
fn div_rem(&self, rhs: &Self) -> Option<(Self, Self)> {
let mut numerator = [self.limb(0), self.limb(1), self.limb(2), self.limb(3), 0];
if rhs.limb(3) > 0 {
divrem_nbym(&mut numerator, &mut [
rhs.limb(0),
rhs.limb(1),
rhs.limb(2),
rhs.limb(3),
]);
Some((
Self::from_limbs([numerator[4], 0, 0, 0]),
Self::from_limbs([numerator[0], numerator[1], numerator[2], numerator[3]]),
))
} else if rhs.limb(2) > 0 {
divrem_nbym(&mut numerator, &mut [rhs.limb(0), rhs.limb(1), rhs.limb(2)]);
Some((
Self::from_limbs([numerator[3], numerator[4], 0, 0]),
Self::from_limbs([numerator[0], numerator[1], numerator[2], 0]),
))
} else if rhs.limb(1) > 0 {
divrem_nbym(&mut numerator, &mut [rhs.limb(0), rhs.limb(1)]);
Some((
Self::from_limbs([numerator[2], numerator[3], numerator[4], 0]),
Self::from_limbs([numerator[0], numerator[1], 0, 0]),
))
} else if rhs.limb(0) > 0 {
let remainder = divrem_nby1(&mut numerator, rhs.limb(0));
Some((
Self::from_limbs([numerator[0], numerator[1], numerator[2], numerator[3]]),
Self::from_limbs([remainder, 0, 0, 0]),
))
} else {
None
}
}
}
impl Inv for &U256 {
type Output = Option<U256>;
fn inv(self) -> Self::Output {
if self.bit(0) {
let c = Wrapping(self.limb(0));
let mut r: Wrapping<u64> = (Wrapping(3) * c) ^ Wrapping(2); r *= Wrapping(2) - c * r; r *= Wrapping(2) - c * r; r *= Wrapping(2) - c * r; r *= Wrapping(2) - c * r; let mut r = Wrapping(u128::from(r.0));
r *= Wrapping(2) - Wrapping(self.as_u128()) * r; let mut r = U256::from(r.0);
r *= &(U256::from(2_u64) - &(r.clone() * self)); Some(r)
} else {
None
}
}
}
impl DivAssign<&U256> for U256 {
#[inline(always)]
fn div_assign(&mut self, rhs: &Self) {
let (q, _r) = self.div_rem(rhs).unwrap();
*self = q;
}
}
impl RemAssign<&U256> for U256 {
#[inline(always)]
fn rem_assign(&mut self, rhs: &Self) {
let (_q, r) = self.div_rem(rhs).unwrap();
*self = r;
}
}
noncommutative_binop!(U256, Div, div, DivAssign, div_assign);
noncommutative_binop!(U256, Rem, rem, RemAssign, rem_assign);
#[allow(clippy::unreadable_literal)]
#[cfg(test)]
mod tests {
use super::*;
use num_traits::identities::{One, Zero};
use proptest::prelude::*;
#[test]
fn test_invmod256() {
let a = U256::from_limbs([
0xf80aa815a36a7e47,
0x090be90cfa96712a,
0xf52ec0a4083d2c14,
0x05405dfd1d1c1a97,
]);
let e = U256::from_limbs([
0xf0a9a0091b3bcb77,
0x42d3eba6084ca0de,
0x60d848b6513392d7,
0xdf45026654d086d6,
]);
let r = a.inv().unwrap();
assert_eq!(r, e);
}
#[test]
fn test_invmod_small() {
let n = U256::from_limbs([271, 0, 0, 0]);
let m = U256::from_limbs([383, 0, 0, 0]);
let i = U256::from_limbs([106, 0, 0, 0]);
let r = n.inv_mod(&m).unwrap();
assert_eq!(i, r);
}
#[test]
fn test_invmod() {
let m = U256::from_limbs([
0x0000000000000001,
0x0000000000000000,
0x0000000000000000,
0x0800000000000011,
]);
let n = U256::from_limbs([
0x1717f47973471ed5,
0xe106229070982941,
0xd82120c54277c73e,
0x07717a21e77894e8,
]);
let i = U256::from_limbs([
0xbda5eaad406f66d1,
0xfac4d8e66130d944,
0x97c88939cbce8317,
0x001752ce51d19c97,
]);
let r = n.inv_mod(&m).unwrap();
assert_eq!(i, r);
}
proptest!(
#[test]
fn test_divrem_u64(a: U256, b: u64) {
let result = a.div_rem(b);
match result {
None => prop_assert!(b.is_zero()),
Some((q, r)) => {
prop_assert!(r < b);
prop_assert_eq!(q * U256::from(b) + U256::from(r), a)
}
}
}
#[test]
fn test_divrem(a: U256, b: U256) {
let result = a.div_rem(&b);
match result {
None => prop_assert!(b.is_zero()),
Some((q, r)) => {
prop_assert!(r < b);
prop_assert_eq!(q * b + r, a)
}
}
}
#[test]
fn invmod_us256(a: U256) {
let result = a.inv();
match result {
None => prop_assert!((a % U256::from(2)).is_zero()),
Some(i) => prop_assert!((a * &i).is_one()),
}
}
);
}