1
Fork 0
mirror of https://github.com/RGBCube/uutils-coreutils synced 2025-08-04 23:17:46 +00:00

factor: combine Montgomery and Montgomery32

This commit is contained in:
Alex Lyon 2020-06-24 03:20:49 -05:00 committed by nicoo
parent a440807e6c
commit 4d28f48ad9
3 changed files with 121 additions and 142 deletions

View file

@ -6,14 +6,14 @@ pub(crate) trait Basis {
const BASIS: &'static [u64];
}
impl Basis for Montgomery {
impl Basis for Montgomery<u64> {
// Small set of bases for the Miller-Rabin prime test, valid for all 64b integers;
// discovered by Jim Sinclair on 2011-04-20, see miller-rabin.appspot.com
#[allow(clippy::unreadable_literal)]
const BASIS: &'static [u64] = &[2, 325, 9375, 28178, 450775, 9780504, 1795265022];
}
impl Basis for Montgomery32 {
impl Basis for Montgomery<u32> {
// Small set of bases for the Miller-Rabin prime test, valid for all 32b integers;
// discovered by Steve Worley on 2013-05-27, see miller-rabin.appspot.com
#[allow(clippy::unreadable_literal)]
@ -104,7 +104,7 @@ pub(crate) fn test<A: Arithmetic + Basis>(m: A) -> Result {
// Used by build.rs' tests
#[allow(dead_code)]
pub(crate) fn is_prime(n: u64) -> bool {
test::<Montgomery>(Montgomery::new(n)).is_prime()
test::<Montgomery<u64>>(Montgomery::new(n)).is_prime()
}
#[cfg(test)]

View file

@ -7,10 +7,12 @@
// * that was distributed with this source code.
use num_traits::{
cast::AsPrimitive,
identities::{One, Zero},
int::PrimInt,
ops::wrapping::{WrappingMul, WrappingSub},
ops::wrapping::{WrappingMul, WrappingNeg, WrappingSub},
};
use std::fmt::Debug;
use std::fmt::{Debug, Display};
use std::mem::swap;
// This is incorrectly reported as dead code,
@ -71,63 +73,78 @@ pub(crate) trait Arithmetic: Copy + Sized {
}
#[derive(Clone, Copy, Debug)]
pub(crate) struct Montgomery {
a: u64,
n: u64,
pub(crate) struct Montgomery<T: Int> {
a: T,
n: T,
}
impl Montgomery {
impl<T: Int> Montgomery<T> {
/// computes x/R mod n efficiently
fn reduce(&self, x: u128) -> u64 {
debug_assert!(x < (self.n as u128) << 64);
fn reduce(&self, x: T::Intermediate) -> T {
let t_bits = T::zero().count_zeros() as usize;
debug_assert!(x < (self.n.as_intermediate()) << t_bits);
// TODO: optimiiiiiiise
let Montgomery { a, n } = self;
let m = (x as u64).wrapping_mul(*a);
let nm = (*n as u128) * (m as u128);
let (xnm, overflow) = (x as u128).overflowing_add(nm); // x + n*m
debug_assert_eq!(xnm % (1 << 64), 0);
let m = T::from_intermediate(x).wrapping_mul(a);
let nm = (n.as_intermediate()) * (m.as_intermediate());
let (xnm, overflow) = x.overflowing_add_(nm); // x + n*m
debug_assert_eq!(
xnm % (T::Intermediate::one() << T::zero().count_zeros() as usize),
T::Intermediate::zero()
);
// (x + n*m) / R
// in case of overflow, this is (2¹²⁸ + xnm)/2⁶⁴ - n = xnm/2⁶⁴ + (2⁶⁴ - n)
let y = (xnm >> 64) as u64 + if !overflow { 0 } else { n.wrapping_neg() };
let y = T::from_intermediate(xnm >> t_bits)
+ if !overflow {
T::zero()
} else {
n.wrapping_neg()
};
if y >= *n {
y - n
y - *n
} else {
y
}
}
}
impl Arithmetic for Montgomery {
impl<T: Int> Arithmetic for Montgomery<T> {
// Montgomery transform, R=2⁶⁴
// Provides fast arithmetic mod n (n odd, u64)
type I = u64;
type I = T;
fn new(n: u64) -> Self {
debug_assert!(T::zero().count_zeros() >= 64 || n < (1 << T::zero().count_zeros() as usize));
let n = T::from_u64(n);
let a = modular_inverse(n).wrapping_neg();
debug_assert_eq!(n.wrapping_mul(a), 1_u64.wrapping_neg());
debug_assert_eq!(n.wrapping_mul(&a), T::one().wrapping_neg());
Montgomery { a, n }
}
fn modulus(&self) -> u64 {
self.n
self.n.as_()
}
fn from_u64(&self, x: u64) -> Self::I {
// TODO: optimise!
assert!(x < self.n);
let r = (((x as u128) << 64) % self.n as u128) as u64;
assert!(x < self.n.as_());
let r = T::from_intermediate(
((T::Intermediate::from(x)) << T::zero().count_zeros() as usize)
% self.n.as_intermediate(),
);
debug_assert_eq!(x, self.to_u64(r));
r
}
fn to_u64(&self, n: Self::I) -> u64 {
self.reduce(n as u128)
self.reduce(n.as_intermediate()).as_()
}
fn add(&self, a: Self::I, b: Self::I) -> Self::I {
let (r, overflow) = a.overflowing_add(b);
let (r, overflow) = a.overflowing_add_(b);
// In case of overflow, a+b = 2⁶⁴ + r = (2⁶⁴ - n) + r (working mod n)
let r = if !overflow {
@ -146,7 +163,8 @@ impl Arithmetic for Montgomery {
let a_r = self.to_u64(a);
let b_r = self.to_u64(b);
let r_r = self.to_u64(r);
let r_2 = (((a_r as u128) + (b_r as u128)) % (self.n as u128)) as u64;
let r_2 =
(((a_r as u128) + (b_r as u128)) % <T as AsPrimitive<u128>>::as_(self.n)) as u64;
debug_assert_eq!(
r_r, r_2,
"[{}] = {} ≠ {} = {} + {} = [{}] + [{}] mod {}; a = {}",
@ -157,7 +175,7 @@ impl Arithmetic for Montgomery {
}
fn mul(&self, a: Self::I, b: Self::I) -> Self::I {
let r = self.reduce((a as u128) * (b as u128));
let r = self.reduce(a.as_intermediate() * b.as_intermediate());
// Check that r (reduced back to the usual representation) equals
// a*b % n
@ -166,7 +184,9 @@ impl Arithmetic for Montgomery {
let a_r = self.to_u64(a);
let b_r = self.to_u64(b);
let r_r = self.to_u64(r);
let r_2 = (((a_r as u128) * (b_r as u128)) % (self.n as u128)) as u64;
let r_2: u64 = ((T::Intermediate::from(a_r) * T::Intermediate::from(b_r))
% self.n.as_intermediate())
.as_();
debug_assert_eq!(
r_r, r_2,
"[{}] = {} ≠ {} = {} * {} = [{}] * [{}] mod {}; a = {}",
@ -177,9 +197,76 @@ impl Arithmetic for Montgomery {
}
}
pub(crate) trait Int: Debug + PrimInt + WrappingSub + WrappingMul {}
impl Int for u64 {}
impl Int for u32 {}
pub(crate) trait OverflowingAdd: Sized {
fn overflowing_add_(self, n: Self) -> (Self, bool);
}
impl OverflowingAdd for u32 {
fn overflowing_add_(self, n: Self) -> (Self, bool) {
self.overflowing_add(n)
}
}
impl OverflowingAdd for u64 {
fn overflowing_add_(self, n: Self) -> (Self, bool) {
self.overflowing_add(n)
}
}
impl OverflowingAdd for u128 {
fn overflowing_add_(self, n: Self) -> (Self, bool) {
self.overflowing_add(n)
}
}
pub(crate) trait Int:
AsPrimitive<u64>
+ AsPrimitive<u128>
+ Display
+ Debug
+ PrimInt
+ OverflowingAdd
+ WrappingNeg
+ WrappingSub
+ WrappingMul
{
type Intermediate: From<u64>
+ AsPrimitive<u64>
+ Display
+ Debug
+ PrimInt
+ OverflowingAdd
+ WrappingNeg
+ WrappingSub
+ WrappingMul;
fn as_intermediate(self) -> Self::Intermediate;
fn from_intermediate(n: Self::Intermediate) -> Self;
fn from_u64(n: u64) -> Self;
}
impl Int for u64 {
type Intermediate = u128;
fn as_intermediate(self) -> u128 {
self as _
}
fn from_intermediate(n: u128) -> u64 {
n as _
}
fn from_u64(n: u64) -> u64 {
n
}
}
impl Int for u32 {
type Intermediate = u64;
fn as_intermediate(self) -> u64 {
self as _
}
fn from_intermediate(n: u64) -> u32 {
n as _
}
fn from_u64(n: u64) -> u32 {
n as _
}
}
// extended Euclid algorithm
// precondition: a is odd
@ -216,114 +303,6 @@ pub(crate) fn modular_inverse<T: Int>(a: T) -> T {
t
}
#[derive(Clone, Copy, Debug)]
pub(crate) struct Montgomery32 {
a: u32,
n: u32,
}
impl Montgomery32 {
/// computes x/R mod n efficiently
fn reduce(&self, x: u64) -> u32 {
debug_assert!(x < (self.n as u64) << 32);
// TODO: optimiiiiiiise
let Montgomery32 { a, n } = self;
let m = (x as u32).wrapping_mul(*a);
let nm = (*n as u64) * (m as u64);
let (xnm, overflow) = x.overflowing_add(nm); // x + n*m
debug_assert_eq!(xnm % (1 << 32), 0);
// (x + n*m) / R
// in case of overflow, this is (2¹²⁸ + xnm)/2⁶⁴ - n = xnm/2⁶⁴ + (2⁶⁴ - n)
let y = (xnm >> 32) as u32 + if !overflow { 0 } else { n.wrapping_neg() };
if y >= *n {
y - n
} else {
y
}
}
}
impl Arithmetic for Montgomery32 {
// Montgomery transform, R=2⁶⁴
// Provides fast arithmetic mod n (n odd, u64)
type I = u32;
fn new(n: u64) -> Self {
debug_assert!(n < (1 << 32));
let n = n as u32;
let a = modular_inverse(n).wrapping_neg();
debug_assert_eq!(n.wrapping_mul(a), 1_u32.wrapping_neg());
Montgomery32 { a, n }
}
fn modulus(&self) -> u64 {
self.n as u64
}
fn from_u64(&self, x: u64) -> Self::I {
assert!(x < self.n as u64);
let r = ((x << 32) % self.n as u64) as u32;
debug_assert_eq!(x, self.to_u64(r));
r
}
fn to_u64(&self, n: Self::I) -> u64 {
self.reduce(n as u64) as u64
}
fn add(&self, a: Self::I, b: Self::I) -> Self::I {
let (r, overflow) = a.overflowing_add(b);
// In case of overflow, a+b = 2⁶⁴ + r = (2⁶⁴ - n) + r (working mod n)
let r = if !overflow {
r
} else {
r + self.n.wrapping_neg()
};
// Normalise to [0; n[
let r = if r < self.n { r } else { r - self.n };
// Check that r (reduced back to the usual representation) equals
// a+b % n
#[cfg(debug_assertions)]
{
let a_r = self.to_u64(a);
let b_r = self.to_u64(b);
let r_r = self.to_u64(r);
let r_2 = (((a_r as u128) + (b_r as u128)) % (self.n as u128)) as u64;
debug_assert_eq!(
r_r, r_2,
"[{}] = {} ≠ {} = {} + {} = [{}] + [{}] mod {}; a = {}",
r, r_r, r_2, a_r, b_r, a, b, self.n, self.a
);
}
r
}
fn mul(&self, a: Self::I, b: Self::I) -> Self::I {
let r = self.reduce((a as u64) * (b as u64));
// Check that r (reduced back to the usual representation) equals
// a*b % n
#[cfg(debug_assertions)]
{
let a_r = self.to_u64(a);
let b_r = self.to_u64(b);
let r_r = self.to_u64(r);
let r_2 = (a_r * b_r) % (self.n as u64);
debug_assert_eq!(
r_r, r_2,
"[{}] = {} ≠ {} = {} * {} = [{}] * [{}] mod {}; a = {}",
r, r_r, r_2, a_r, b_r, a, b, self.n, self.a
);
}
r
}
}
#[cfg(test)]
mod tests {
use super::*;

View file

@ -52,7 +52,7 @@ fn _factor<A: Arithmetic + miller_rabin::Basis>(num: u64) -> Factors {
// Shadow the name, so the recursion automatically goes from “Big” arithmetic to small.
let _factor = |n| {
if n < (1 << 32) {
_factor::<Montgomery32>(n)
_factor::<Montgomery<u32>>(n)
} else {
_factor::<A>(n)
}
@ -79,8 +79,8 @@ fn _factor<A: Arithmetic + miller_rabin::Basis>(num: u64) -> Factors {
pub(crate) fn factor(n: u64) -> Factors {
if n < (1 << 32) {
_factor::<Montgomery32>(n)
_factor::<Montgomery<u32>>(n)
} else {
_factor::<Montgomery>(n)
_factor::<Montgomery<u64>>(n)
}
}