88
99#![ allow( clippy:: needless_range_loop) ]
1010
11+ use crate :: Word ;
12+
1113/// Type of the modular multiplicative inverter based on the Bernstein-Yang method.
1214/// The inverter can be created for a specified modulus M and adjusting parameter A
1315/// to compute the adjusted multiplicative inverses of positive integers, i.e. for
@@ -48,19 +50,20 @@ type Matrix = [[i64; 2]; 2];
4850
4951impl < const L : usize > BernsteinYangInverter < L > {
5052 /// Creates the inverter for specified modulus and adjusting parameter
51- pub const fn new ( modulus : & [ u64 ] , adjuster : & [ u64 ] ) -> Self {
53+ #[ allow( trivial_numeric_casts) ]
54+ pub const fn new ( modulus : & [ Word ] , adjuster : & [ Word ] ) -> Self {
5255 Self {
53- modulus : CInt :: < 62 , L > ( Self :: convert :: < 64 , 62 , L > ( modulus) ) ,
54- adjuster : CInt :: < 62 , L > ( Self :: convert :: < 64 , 62 , L > ( adjuster) ) ,
55- inverse : Self :: inv ( modulus[ 0 ] ) ,
56+ modulus : CInt :: < 62 , L > ( convert_in :: < { Word :: BITS as usize } , 62 , L > ( modulus) ) ,
57+ adjuster : CInt :: < 62 , L > ( convert_in :: < { Word :: BITS as usize } , 62 , L > ( adjuster) ) ,
58+ inverse : Self :: inv ( modulus[ 0 ] as u64 ) ,
5659 }
5760 }
5861
5962 /// Returns either the adjusted modular multiplicative inverse for the argument or None
6063 /// depending on invertibility of the argument, i.e. its coprimality with the modulus
61- pub const fn invert < const S : usize > ( & self , value : & [ u64 ] ) -> Option < [ u64 ; S ] > {
64+ pub const fn invert < const S : usize > ( & self , value : & [ Word ] ) -> Option < [ Word ; S ] > {
6265 let ( mut d, mut e) = ( CInt :: ZERO , self . adjuster ) ;
63- let mut g = CInt :: < 62 , L > ( Self :: convert :: < 64 , 62 , L > ( value) ) ;
66+ let mut g = CInt :: < 62 , L > ( convert_in :: < { Word :: BITS as usize } , 62 , L > ( value) ) ;
6467 let ( mut delta, mut f) = ( 1 , self . modulus ) ;
6568 let mut matrix;
6669
@@ -76,7 +79,9 @@ impl<const L: usize> BernsteinYangInverter<L> {
7679 if !f. eq ( & CInt :: ONE ) && !antiunit {
7780 return None ;
7881 }
79- Some ( Self :: convert :: < 62 , 64 , S > ( & self . norm ( d, antiunit) . 0 ) )
82+ Some ( convert_out :: < 62 , { Word :: BITS as usize } , S > (
83+ & self . norm ( d, antiunit) . 0 ,
84+ ) )
8085 }
8186
8287 /// Returns the Bernstein-Yang transition matrix multiplied by 2^62 and the new value
@@ -182,10 +187,26 @@ impl<const L: usize> BernsteinYangInverter<L> {
182187 value
183188 }
184189
185- /// Returns a big unsigned integer as an array of O-bit chunks, which is equal modulo
186- /// 2 ^ (O * S) to the input big unsigned integer stored as an array of I-bit chunks.
187- /// The ordering of the chunks in these arrays is little-endian
188- const fn convert < const I : usize , const O : usize , const S : usize > ( input : & [ u64 ] ) -> [ u64 ; S ] {
190+ /// Returns the multiplicative inverse of the argument modulo 2^62. The implementation is based
191+ /// on the Hurchalla's method for computing the multiplicative inverse modulo a power of two.
192+ /// For better understanding the implementation, the following paper is recommended:
193+ /// J. Hurchalla, "An Improved Integer Multiplicative Inverse (modulo 2^w)",
194+ /// https://arxiv.org/pdf/2204.04342.pdf
195+ const fn inv ( value : u64 ) -> i64 {
196+ let x = value. wrapping_mul ( 3 ) ^ 2 ;
197+ let y = 1u64 . wrapping_sub ( x. wrapping_mul ( value) ) ;
198+ let ( x, y) = ( x. wrapping_mul ( y. wrapping_add ( 1 ) ) , y. wrapping_mul ( y) ) ;
199+ let ( x, y) = ( x. wrapping_mul ( y. wrapping_add ( 1 ) ) , y. wrapping_mul ( y) ) ;
200+ let ( x, y) = ( x. wrapping_mul ( y. wrapping_add ( 1 ) ) , y. wrapping_mul ( y) ) ;
201+ ( x. wrapping_mul ( y. wrapping_add ( 1 ) ) & CInt :: < 62 , L > :: MASK ) as i64
202+ }
203+ }
204+
205+ /// Write an impl of a `convert_*` function.
206+ ///
207+ /// Workaround for making this function generic while still allowing it to be `const fn`.
208+ macro_rules! impl_convert {
209+ ( $input_type: ty, $output_type: ty, $input: expr) => { {
189210 // This function is defined because the method "min" of the usize type is not constant
190211 const fn min( a: usize , b: usize ) -> usize {
191212 if a > b {
@@ -195,15 +216,17 @@ impl<const L: usize> BernsteinYangInverter<L> {
195216 }
196217 }
197218
198- let ( total, mut output, mut bits) = ( min ( input. len ( ) * I , S * O ) , [ 0 ; S ] , 0 ) ;
219+ let total = min( $input. len( ) * I , S * O ) ;
220+ let mut output = [ 0 as $output_type; S ] ;
221+ let mut bits = 0 ;
199222
200223 while bits < total {
201224 let ( i, o) = ( bits % I , bits % O ) ;
202- output[ bits / O ] |= ( input[ bits / I ] >> i) << o;
225+ output[ bits / O ] |= ( $ input[ bits / I ] >> i) as $output_type << o;
203226 bits += min( I - i, O - o) ;
204227 }
205228
206- let mask = u64 :: MAX >> ( 64 - O ) ;
229+ let mask = ( <$output_type> :: MAX as $output_type ) >> ( <$output_type> :: BITS as usize - O ) ;
207230 let mut filled = total / O + if total % O > 0 { 1 } else { 0 } ;
208231
209232 while filled > 0 {
@@ -212,21 +235,23 @@ impl<const L: usize> BernsteinYangInverter<L> {
212235 }
213236
214237 output
215- }
238+ } } ;
239+ }
216240
217- /// Returns the multiplicative inverse of the argument modulo 2^62. The implementation is based
218- /// on the Hurchalla's method for computing the multiplicative inverse modulo a power of two.
219- /// For better understanding the implementation, the following paper is recommended:
220- /// J. Hurchalla, "An Improved Integer Multiplicative Inverse (modulo 2^w)",
221- /// https://arxiv.org/pdf/2204.04342.pdf
222- const fn inv ( value : u64 ) -> i64 {
223- let x = value. wrapping_mul ( 3 ) ^ 2 ;
224- let y = 1u64 . wrapping_sub ( x. wrapping_mul ( value) ) ;
225- let ( x, y) = ( x. wrapping_mul ( y. wrapping_add ( 1 ) ) , y. wrapping_mul ( y) ) ;
226- let ( x, y) = ( x. wrapping_mul ( y. wrapping_add ( 1 ) ) , y. wrapping_mul ( y) ) ;
227- let ( x, y) = ( x. wrapping_mul ( y. wrapping_add ( 1 ) ) , y. wrapping_mul ( y) ) ;
228- ( x. wrapping_mul ( y. wrapping_add ( 1 ) ) & CInt :: < 62 , L > :: MASK ) as i64
229- }
241+ /// Returns a big unsigned integer as an array of O-bit chunks, which is equal modulo
242+ /// 2 ^ (O * S) to the input big unsigned integer stored as an array of I-bit chunks.
243+ /// The ordering of the chunks in these arrays is little-endian
244+ #[ allow( trivial_numeric_casts) ]
245+ const fn convert_in < const I : usize , const O : usize , const S : usize > ( input : & [ Word ] ) -> [ u64 ; S ] {
246+ impl_convert ! ( Word , u64 , input)
247+ }
248+
249+ /// Returns a big unsigned integer as an array of O-bit chunks, which is equal modulo
250+ /// 2 ^ (O * S) to the input big unsigned integer stored as an array of I-bit chunks.
251+ /// The ordering of the chunks in these arrays is little-endian
252+ #[ allow( trivial_numeric_casts) ]
253+ const fn convert_out < const I : usize , const O : usize , const S : usize > ( input : & [ u64 ] ) -> [ Word ; S ] {
254+ impl_convert ! ( u64 , Word , input)
230255}
231256
232257/// Big signed (B * L)-bit integer type, whose variables store
0 commit comments