crypto_bigint/modular/
safegcd.rs

1//! Implementation of Bernstein-Yang modular inversion and GCD algorithm (a.k.a. safegcd)
2//! as described in: <https://eprint.iacr.org/2019/266>.
3//!
4//! Adapted from the Apache 2.0+MIT licensed implementation originally from:
5//! <https://github.com/taikoxyz/halo2curves/pull/2>
6//! <https://github.com/privacy-scaling-explorations/halo2curves/pull/83>
7//!
8//! Copyright (c) 2023 Privacy Scaling Explorations Team
9
10// TODO(tarcieri): optimized implementation for 32-bit platforms (#380)
11// TODO(tarcieri): optimize using safegcd-bounds (#634)
12
13#![allow(clippy::needless_range_loop)]
14
15#[macro_use]
16mod macros;
17
18#[cfg(feature = "alloc")]
19pub(crate) mod boxed;
20
21use crate::{ConstChoice, ConstCtOption, Inverter, Limb, Odd, Uint, Word};
22use subtle::CtOption;
23
24/// Modular multiplicative inverter based on the Bernstein-Yang method.
25///
26/// The inverter can be created for a specified modulus M and adjusting parameter A to compute
27/// the adjusted multiplicative inverses of positive integers, i.e. for computing
28/// (1 / x) * A (mod M) for a positive integer x.
29///
30/// The adjusting parameter allows computing the multiplicative inverses in the case of using the
31/// Montgomery representation for the input or the expected output. If R is the Montgomery
32/// factor, the multiplicative inverses in the appropriate representation can be computed
33/// provided that the value of A is chosen as follows:
34/// - A = 1, if both the input and the expected output are in the standard form
35/// - A = R^2 mod M, if both the input and the expected output are in the Montgomery form
36/// - A = R mod M, if either the input or the expected output is in the Montgomery form,
37///   but not both of them
38///
39/// The public methods of this type receive and return unsigned big integers as arrays of 64-bit
40/// chunks, the ordering of which is little-endian. Both the modulus and the integer to be
41/// inverted should not exceed 2 ^ (62 * L - 64).
42///
43/// For better understanding the implementation, the following resources are recommended:
44/// - D. Bernstein, B.-Y. Yang, "Fast constant-time gcd computation and modular inversion",
45///   <https://gcd.cr.yp.to/safegcd-20190413.pdf>
46/// - P. Wuille, "The safegcd implementation in libsecp256k1 explained",
47///   <https://github.com/bitcoin-core/secp256k1/blob/master/doc/safegcd_implementation.md>
48#[derive(Clone, Debug)]
49pub struct SafeGcdInverter<const SAT_LIMBS: usize, const UNSAT_LIMBS: usize> {
50    /// Modulus
51    pub(super) modulus: UnsatInt<UNSAT_LIMBS>,
52
53    /// Adjusting parameter (see toplevel documentation).
54    adjuster: UnsatInt<UNSAT_LIMBS>,
55
56    /// Multiplicative inverse of the modulus modulo 2^62
57    inverse: i64,
58}
59
60/// Type of the Bernstein-Yang transition matrix multiplied by 2^62
61type Matrix = [[i64; 2]; 2];
62
63impl<const SAT_LIMBS: usize, const UNSAT_LIMBS: usize> SafeGcdInverter<SAT_LIMBS, UNSAT_LIMBS> {
64    /// Creates the inverter for specified modulus and adjusting parameter.
65    ///
66    /// Modulus must be odd. Returns `None` if it is not.
67    pub const fn new(modulus: &Odd<Uint<SAT_LIMBS>>, adjuster: &Uint<SAT_LIMBS>) -> Self {
68        Self {
69            modulus: UnsatInt::from_uint(&modulus.0),
70            adjuster: UnsatInt::from_uint(adjuster),
71            inverse: inv_mod2_62(modulus.0.as_words()),
72        }
73    }
74
75    /// Returns either the adjusted modular multiplicative inverse for the argument or `None`
76    /// depending on invertibility of the argument, i.e. its coprimality with the modulus.
77    pub const fn inv(&self, value: &Uint<SAT_LIMBS>) -> ConstCtOption<Uint<SAT_LIMBS>> {
78        let (d, f) = divsteps(
79            self.adjuster,
80            self.modulus,
81            UnsatInt::from_uint(value),
82            self.inverse,
83        );
84
85        // At this point the absolute value of "f" equals the greatest common divisor of the
86        // integer to be inverted and the modulus the inverter was created for.
87        // Thus, if "f" is neither 1 nor -1, then the sought inverse does not exist.
88        let antiunit = f.eq(&UnsatInt::MINUS_ONE);
89        let ret = self.norm(d, antiunit);
90        let is_some = f.eq(&UnsatInt::ONE).or(antiunit);
91        ConstCtOption::new(ret.to_uint(), is_some)
92    }
93
94    /// Returns either the adjusted modular multiplicative inverse for the argument or `None`
95    /// depending on invertibility of the argument, i.e. its coprimality with the modulus.
96    ///
97    /// This version is variable-time with respect to `value`.
98    pub const fn inv_vartime(&self, value: &Uint<SAT_LIMBS>) -> ConstCtOption<Uint<SAT_LIMBS>> {
99        let (d, f) = divsteps_vartime(
100            self.adjuster,
101            self.modulus,
102            UnsatInt::from_uint(value),
103            self.inverse,
104        );
105
106        // At this point the absolute value of "f" equals the greatest common divisor of the
107        // integer to be inverted and the modulus the inverter was created for.
108        // Thus, if "f" is neither 1 nor -1, then the sought inverse does not exist.
109        let antiunit = f.eq(&UnsatInt::MINUS_ONE);
110        let ret = self.norm(d, antiunit);
111        let is_some = f.eq(&UnsatInt::ONE).or(antiunit);
112        ConstCtOption::new(ret.to_uint(), is_some)
113    }
114
115    /// Returns the greatest common divisor (GCD) of the two given numbers.
116    ///
117    /// This is defined on this type to piggyback on the definitions for `SAT_LIMBS` and
118    /// `UNSAT_LIMBS` which are computed when defining `PrecomputeInverter::Inverter` for various
119    /// `Uint` limb sizes.
120    pub(crate) const fn gcd(f: &Uint<SAT_LIMBS>, g: &Uint<SAT_LIMBS>) -> Uint<SAT_LIMBS> {
121        let inverse = inv_mod2_62(f.as_words());
122        let e = UnsatInt::<UNSAT_LIMBS>::ONE;
123        let f = UnsatInt::from_uint(f);
124        let g = UnsatInt::from_uint(g);
125        let (_, mut f) = divsteps(e, f, g, inverse);
126        f = UnsatInt::select(&f, &f.neg(), f.is_negative());
127        f.to_uint()
128    }
129
130    /// Returns the greatest common divisor (GCD) of the two given numbers.
131    ///
132    /// This version is variable-time with respect to `g`.
133    pub(crate) const fn gcd_vartime(f: &Uint<SAT_LIMBS>, g: &Uint<SAT_LIMBS>) -> Uint<SAT_LIMBS> {
134        let inverse = inv_mod2_62(f.as_words());
135        let e = UnsatInt::<UNSAT_LIMBS>::ONE;
136        let f = UnsatInt::from_uint(f);
137        let g = UnsatInt::from_uint(g);
138        let (_, mut f) = divsteps_vartime(e, f, g, inverse);
139        f = UnsatInt::select(&f, &f.neg(), f.is_negative());
140        f.to_uint()
141    }
142
143    /// Returns either "value (mod M)" or "-value (mod M)", where M is the modulus the inverter
144    /// was created for, depending on "negate", which determines the presence of "-" in the used
145    /// formula. The input integer lies in the interval (-2 * M, M).
146    const fn norm(
147        &self,
148        mut value: UnsatInt<UNSAT_LIMBS>,
149        negate: ConstChoice,
150    ) -> UnsatInt<UNSAT_LIMBS> {
151        value = UnsatInt::select(&value, &value.add(&self.modulus), value.is_negative());
152        value = UnsatInt::select(&value, &value.neg(), negate);
153        value = UnsatInt::select(&value, &value.add(&self.modulus), value.is_negative());
154        value
155    }
156}
157
158impl<const SAT_LIMBS: usize, const UNSAT_LIMBS: usize> Inverter
159    for SafeGcdInverter<SAT_LIMBS, UNSAT_LIMBS>
160{
161    type Output = Uint<SAT_LIMBS>;
162
163    fn invert(&self, value: &Uint<SAT_LIMBS>) -> CtOption<Self::Output> {
164        self.inv(value).into()
165    }
166
167    fn invert_vartime(&self, value: &Uint<SAT_LIMBS>) -> CtOption<Self::Output> {
168        self.inv_vartime(value).into()
169    }
170}
171
172/// Returns the multiplicative inverse of the argument modulo 2^62. The implementation is based
173/// on the Hurchalla's method for computing the multiplicative inverse modulo a power of two.
174///
175/// For better understanding the implementation, the following paper is recommended:
176/// J. Hurchalla, "An Improved Integer Multiplicative Inverse (modulo 2^w)",
177/// <https://arxiv.org/pdf/2204.04342.pdf>
178///
179/// Variable time with respect to the number of words in `value`, however that number will be
180/// fixed for a given integer size.
181const fn inv_mod2_62(value: &[Word]) -> i64 {
182    let value = {
183        #[cfg(target_pointer_width = "32")]
184        {
185            debug_assert!(value.len() >= 1);
186            let mut ret = value[0] as u64;
187
188            if value.len() >= 2 {
189                ret |= (value[1] as u64) << 32;
190            }
191
192            ret
193        }
194
195        #[cfg(target_pointer_width = "64")]
196        {
197            value[0]
198        }
199    };
200
201    let x = value.wrapping_mul(3) ^ 2;
202    let y = 1u64.wrapping_sub(x.wrapping_mul(value));
203    let (x, y) = (x.wrapping_mul(y.wrapping_add(1)), y.wrapping_mul(y));
204    let (x, y) = (x.wrapping_mul(y.wrapping_add(1)), y.wrapping_mul(y));
205    let (x, y) = (x.wrapping_mul(y.wrapping_add(1)), y.wrapping_mul(y));
206    (x.wrapping_mul(y.wrapping_add(1)) & (u64::MAX >> 2)) as i64
207}
208
209/// Algorithm `divsteps2` to compute (δₙ, fₙ, gₙ) = divstepⁿ(δ, f, g) as described in Figure 10.1
210/// of <https://eprint.iacr.org/2019/266.pdf>.
211///
212/// This version runs in a fixed number of iterations relative to the highest bit of `f` or `g`
213/// as described in Figure 11.1.
214const fn divsteps<const LIMBS: usize>(
215    mut e: UnsatInt<LIMBS>,
216    f_0: UnsatInt<LIMBS>,
217    mut g: UnsatInt<LIMBS>,
218    inverse: i64,
219) -> (UnsatInt<LIMBS>, UnsatInt<LIMBS>) {
220    let mut d = UnsatInt::ZERO;
221    let mut f = f_0;
222    let mut delta = 1;
223    let mut matrix;
224    let mut i = 0;
225    let m = iterations(f_0.bits(), g.bits());
226
227    while i < m {
228        (delta, matrix) = jump(&f.0, &g.0, delta);
229        (f, g) = fg(f, g, matrix);
230        (d, e) = de(&f_0, inverse, matrix, d, e);
231        i += 1;
232    }
233
234    debug_assert!(g.eq(&UnsatInt::ZERO).to_bool_vartime());
235    (d, f)
236}
237
238/// Algorithm `divsteps2` to compute (δₙ, fₙ, gₙ) = divstepⁿ(δ, f, g) as described in Figure 10.1
239/// of <https://eprint.iacr.org/2019/266.pdf>.
240///
241/// This version is variable-time with respect to `g`.
242const fn divsteps_vartime<const LIMBS: usize>(
243    mut e: UnsatInt<LIMBS>,
244    f_0: UnsatInt<LIMBS>,
245    mut g: UnsatInt<LIMBS>,
246    inverse: i64,
247) -> (UnsatInt<LIMBS>, UnsatInt<LIMBS>) {
248    let mut d = UnsatInt::ZERO;
249    let mut f = f_0;
250    let mut delta = 1;
251    let mut matrix;
252
253    while !g.eq(&UnsatInt::ZERO).to_bool_vartime() {
254        (delta, matrix) = jump(&f.0, &g.0, delta);
255        (f, g) = fg(f, g, matrix);
256        (d, e) = de(&f_0, inverse, matrix, d, e);
257    }
258
259    (d, f)
260}
261
262/// Returns the Bernstein-Yang transition matrix multiplied by 2^62 and the new value of the
263/// delta variable for the 62 basic steps of the Bernstein-Yang method, which are to be
264/// performed sequentially for specified initial values of f, g and delta
265const fn jump(f: &[u64], g: &[u64], mut delta: i64) -> (i64, Matrix) {
266    // This function is defined because the method "min" of the i64 type is not constant
267    const fn min(a: i64, b: i64) -> i64 {
268        if a > b {
269            b
270        } else {
271            a
272        }
273    }
274
275    let (mut steps, mut f, mut g) = (62, f[0] as i64, g[0] as i128);
276    let mut t: Matrix = [[1, 0], [0, 1]];
277
278    loop {
279        let zeros = min(steps, g.trailing_zeros() as i64);
280        (steps, delta, g) = (steps - zeros, delta + zeros, g >> zeros);
281        t[0] = [t[0][0] << zeros, t[0][1] << zeros];
282
283        if steps == 0 {
284            break;
285        }
286        if delta > 0 {
287            (delta, f, g) = (-delta, g as i64, -f as i128);
288            (t[0], t[1]) = (t[1], [-t[0][0], -t[0][1]]);
289        }
290
291        // The formula (3 * x) xor 28 = -1 / x (mod 32) for an odd integer x in the two's
292        // complement code has been derived from the formula (3 * x) xor 2 = 1 / x (mod 32)
293        // attributed to Peter Montgomery.
294        let mask = (1 << min(min(steps, 1 - delta), 5)) - 1;
295        let w = (g as i64).wrapping_mul(f.wrapping_mul(3) ^ 28) & mask;
296
297        t[1] = [t[0][0] * w + t[1][0], t[0][1] * w + t[1][1]];
298        g += w as i128 * f as i128;
299    }
300
301    (delta, t)
302}
303
304/// Returns the updated values of the variables f and g for specified initial ones and
305/// Bernstein-Yang transition matrix multiplied by 2^62.
306///
307/// The returned vector is "matrix * (f, g)' / 2^62", where "'" is the transpose operator.
308const fn fg<const LIMBS: usize>(
309    f: UnsatInt<LIMBS>,
310    g: UnsatInt<LIMBS>,
311    t: Matrix,
312) -> (UnsatInt<LIMBS>, UnsatInt<LIMBS>) {
313    (
314        f.mul(t[0][0]).add(&g.mul(t[0][1])).shr(),
315        f.mul(t[1][0]).add(&g.mul(t[1][1])).shr(),
316    )
317}
318
319/// Returns the updated values of the variables d and e for specified initial ones and
320/// Bernstein-Yang transition matrix multiplied by 2^62.
321///
322/// The returned vector is congruent modulo M to "matrix * (d, e)' / 2^62 (mod M)", where M is the
323/// modulus the inverter was created for and "'" stands for the transpose operator.
324///
325/// Both the input and output values lie in the interval (-2 * M, M).
326const fn de<const LIMBS: usize>(
327    modulus: &UnsatInt<LIMBS>,
328    inverse: i64,
329    t: Matrix,
330    d: UnsatInt<LIMBS>,
331    e: UnsatInt<LIMBS>,
332) -> (UnsatInt<LIMBS>, UnsatInt<LIMBS>) {
333    let mask = UnsatInt::<LIMBS>::MASK as i64;
334    let mut md =
335        t[0][0] * d.is_negative().to_u8() as i64 + t[0][1] * e.is_negative().to_u8() as i64;
336    let mut me =
337        t[1][0] * d.is_negative().to_u8() as i64 + t[1][1] * e.is_negative().to_u8() as i64;
338
339    let cd = t[0][0]
340        .wrapping_mul(d.lowest() as i64)
341        .wrapping_add(t[0][1].wrapping_mul(e.lowest() as i64))
342        & mask;
343
344    let ce = t[1][0]
345        .wrapping_mul(d.lowest() as i64)
346        .wrapping_add(t[1][1].wrapping_mul(e.lowest() as i64))
347        & mask;
348
349    md -= (inverse.wrapping_mul(cd).wrapping_add(md)) & mask;
350    me -= (inverse.wrapping_mul(ce).wrapping_add(me)) & mask;
351
352    let cd = d.mul(t[0][0]).add(&e.mul(t[0][1])).add(&modulus.mul(md));
353    let ce = d.mul(t[1][0]).add(&e.mul(t[1][1])).add(&modulus.mul(me));
354
355    (cd.shr(), ce.shr())
356}
357
358/// Compute the number of iterations required to compute Bernstein-Yang on the two values.
359///
360/// Adapted from Fig 11.1 of <https://eprint.iacr.org/2019/266.pdf>
361///
362/// The paper proves that the algorithm will converge (i.e. `g` will be `0`) in all cases when
363/// the algorithm runs this particular number of iterations.
364///
365/// Once `g` reaches `0`, continuing to run the algorithm will have no effect.
366// TODO(tarcieri): improved bounds using https://github.com/sipa/safegcd-bounds
367pub(crate) const fn iterations(f_bits: u32, g_bits: u32) -> usize {
368    // Select max of `f_bits`, `g_bits`
369    let d = ConstChoice::from_u32_lt(f_bits, g_bits).select_u32(f_bits, g_bits);
370    let addend = ConstChoice::from_u32_lt(d, 46).select_u32(57, 80);
371    ((49 * d + addend) / 17) as usize
372}
373
374/// "Bigint"-like (62 * LIMBS)-bit signed integer type, whose variables store numbers in the two's
375/// complement code as arrays of 62-bit limbs in little endian order.
376///
377/// The arithmetic operations for this type are wrapping ones.
378#[derive(Clone, Copy, Debug)]
379pub(super) struct UnsatInt<const LIMBS: usize>(pub [u64; LIMBS]);
380
381impl<const LIMBS: usize> UnsatInt<LIMBS> {
382    /// Number of bits in each limb.
383    pub const LIMB_BITS: usize = 62;
384
385    /// Mask, in which the 62 lowest bits are 1.
386    pub const MASK: u64 = u64::MAX >> (64 - Self::LIMB_BITS);
387
388    /// Representation of -1.
389    pub const MINUS_ONE: Self = Self([Self::MASK; LIMBS]);
390
391    /// Representation of 0.
392    pub const ZERO: Self = Self([0; LIMBS]);
393
394    /// Representation of 1.
395    pub const ONE: Self = {
396        let mut ret = Self::ZERO;
397        ret.0[0] = 1;
398        ret
399    };
400
401    /// Convert from 32/64-bit saturated representation used by `Uint` to the 62-bit unsaturated
402    /// representation used by `UnsatInt`.
403    ///
404    /// Returns a big unsigned integer as an array of 62-bit chunks, which is equal modulo
405    /// 2 ^ (62 * S) to the input big unsigned integer stored as an array of 64-bit chunks.
406    ///
407    /// The ordering of the chunks in these arrays is little-endian.
408    #[allow(trivial_numeric_casts)]
409    pub const fn from_uint<const SAT_LIMBS: usize>(input: &Uint<SAT_LIMBS>) -> Self {
410        if LIMBS != safegcd_nlimbs!(SAT_LIMBS * Limb::BITS as usize) {
411            panic!("incorrect number of limbs");
412        }
413
414        let mut output = [0; LIMBS];
415        impl_limb_convert!(Word, Word::BITS as usize, input.as_words(), u64, 62, output);
416
417        Self(output)
418    }
419
420    /// Convert from 62-bit unsaturated representation used by `UnsatInt` to the 32/64-bit saturated
421    /// representation used by `Uint`.
422    ///
423    /// Returns a big unsigned integer as an array of 32/64-bit chunks, which is equal modulo
424    /// 2 ^ (64 * S) to the input big unsigned integer stored as an array of 62-bit chunks.
425    ///
426    /// The ordering of the chunks in these arrays is little-endian.
427    #[allow(trivial_numeric_casts, clippy::wrong_self_convention)]
428    pub const fn to_uint<const SAT_LIMBS: usize>(&self) -> Uint<SAT_LIMBS> {
429        debug_assert!(
430            !self.is_negative().to_bool_vartime(),
431            "can't convert negative number to Uint"
432        );
433
434        if LIMBS != safegcd_nlimbs!(SAT_LIMBS * Limb::BITS as usize) {
435            panic!("incorrect number of limbs");
436        }
437
438        let mut ret = [0 as Word; SAT_LIMBS];
439        impl_limb_convert!(u64, 62, &self.0, Word, Word::BITS as usize, ret);
440        Uint::from_words(ret)
441    }
442
443    /// Const fn equivalent for `Add::add`.
444    pub const fn add(&self, other: &Self) -> Self {
445        let (mut ret, mut carry) = (Self::ZERO, 0);
446        let mut i = 0;
447
448        while i < LIMBS {
449            let sum = self.0[i] + other.0[i] + carry;
450            ret.0[i] = sum & Self::MASK;
451            carry = sum >> Self::LIMB_BITS;
452            i += 1;
453        }
454
455        ret
456    }
457
458    /// Const fn equivalent for `Mul::<i64>::mul`.
459    pub const fn mul(&self, other: i64) -> Self {
460        let mut ret = Self::ZERO;
461        // If the short multiplicand is non-negative, the standard multiplication algorithm is
462        // performed. Otherwise, the product of the additively negated multiplicands is found as
463        // follows.
464        //
465        // Since for the two's complement code the additive negation is the result of adding 1 to
466        // the bitwise inverted argument's representation, for any encoded integers x and y we have
467        // x * y = (-x) * (-y) = (!x + 1) * (-y) = !x * (-y) + (-y), where "!" is the bitwise
468        // inversion and arithmetic operations are performed according to the rules of the code.
469        //
470        // If the short multiplicand is negative, the algorithm below uses this formula by
471        // substituting the short multiplicand for y and turns into the modified standard
472        // multiplication algorithm, where the carry flag is initialized with the additively
473        // negated short multiplicand and the chunks of the long multiplicand are bitwise inverted.
474        let (other, mut carry, mask) = if other < 0 {
475            (-other, -other as u64, Self::MASK)
476        } else {
477            (other, 0, 0)
478        };
479
480        let mut i = 0;
481        while i < LIMBS {
482            let sum = (carry as u128) + ((self.0[i] ^ mask) as u128) * (other as u128);
483            ret.0[i] = sum as u64 & Self::MASK;
484            carry = (sum >> Self::LIMB_BITS) as u64;
485            i += 1;
486        }
487
488        ret
489    }
490
491    /// Const fn equivalent for `Neg::neg`.
492    pub const fn neg(&self) -> Self {
493        // For the two's complement code the additive negation is the result of adding 1 to the
494        // bitwise inverted argument's representation.
495        let (mut ret, mut carry) = (Self::ZERO, 1);
496        let mut i = 0;
497
498        while i < LIMBS {
499            let sum = (self.0[i] ^ Self::MASK) + carry;
500            ret.0[i] = sum & Self::MASK;
501            carry = sum >> Self::LIMB_BITS;
502            i += 1;
503        }
504
505        ret
506    }
507
508    /// Returns the result of applying 62-bit right arithmetical shift to the current number.
509    pub const fn shr(&self) -> Self {
510        let mut ret = Self::ZERO;
511        ret.0[LIMBS - 1] = self.is_negative().select_u64(ret.0[LIMBS - 1], Self::MASK);
512
513        let mut i = 0;
514        while i < LIMBS - 1 {
515            ret.0[i] = self.0[i + 1];
516            i += 1;
517        }
518
519        ret
520    }
521
522    /// Const fn equivalent for `PartialEq::eq`.
523    pub const fn eq(&self, other: &Self) -> ConstChoice {
524        let mut ret = ConstChoice::TRUE;
525        let mut i = 0;
526
527        while i < LIMBS {
528            ret = ret.and(ConstChoice::from_u64_eq(self.0[i], other.0[i]));
529            i += 1;
530        }
531
532        ret
533    }
534
535    /// Returns "true" iff the current number is negative.
536    pub const fn is_negative(&self) -> ConstChoice {
537        ConstChoice::from_u64_gt(self.0[LIMBS - 1], Self::MASK >> 1)
538    }
539
540    /// Returns the lowest 62 bits of the current number.
541    pub const fn lowest(&self) -> u64 {
542        self.0[0]
543    }
544
545    /// Select between two [`UnsatInt`] values in constant time.
546    pub const fn select(a: &Self, b: &Self, choice: ConstChoice) -> Self {
547        let mut ret = Self::ZERO;
548        let mut i = 0;
549
550        while i < LIMBS {
551            ret.0[i] = choice.select_u64(a.0[i], b.0[i]);
552            i += 1;
553        }
554
555        ret
556    }
557
558    /// Calculate the number of leading zeros in the binary representation of this number.
559    pub const fn leading_zeros(&self) -> u32 {
560        let mut count = 0;
561        let mut i = LIMBS;
562        let mut nonzero_limb_not_encountered = ConstChoice::TRUE;
563
564        while i > 0 {
565            i -= 1;
566            let l = self.0[i];
567            let z = l.leading_zeros() - 2;
568            count += nonzero_limb_not_encountered.if_true_u32(z);
569            nonzero_limb_not_encountered =
570                nonzero_limb_not_encountered.and(ConstChoice::from_u64_nonzero(l).not());
571        }
572
573        count
574    }
575
576    /// Calculate the number of bits in this value (i.e. index of the highest bit) in constant time.
577    pub const fn bits(&self) -> u32 {
578        (LIMBS as u32 * 62) - self.leading_zeros()
579    }
580}
581
582#[cfg(test)]
583mod tests {
584    use super::iterations;
585    use crate::{Inverter, PrecomputeInverter, U256};
586
587    type UnsatInt = super::UnsatInt<4>;
588
589    impl<const LIMBS: usize> PartialEq for crate::modular::safegcd::UnsatInt<LIMBS> {
590        fn eq(&self, other: &Self) -> bool {
591            self.eq(other).to_bool_vartime()
592        }
593    }
594
595    #[test]
596    fn invert() {
597        let g =
598            U256::from_be_hex("00000000CBF9350842F498CE441FC2DC23C7BF47D3DE91C327B2157C5E4EED77");
599        let modulus =
600            U256::from_be_hex("FFFFFFFF00000000FFFFFFFFFFFFFFFFBCE6FAADA7179E84F3B9CAC2FC632551")
601                .to_odd()
602                .unwrap();
603        let inverter = modulus.precompute_inverter();
604        let result = inverter.invert(&g).unwrap();
605        assert_eq!(
606            U256::from_be_hex("FB668F8F509790BC549B077098918604283D42901C92981062EB48BC723F617B"),
607            result
608        );
609    }
610
611    #[test]
612    fn iterations_boundary_conditions() {
613        assert_eq!(iterations(0, 0), 4);
614        assert_eq!(iterations(0, 45), 134);
615        assert_eq!(iterations(0, 46), 135);
616    }
617
618    #[test]
619    fn unsatint_add() {
620        assert_eq!(UnsatInt::ZERO, UnsatInt::ZERO.add(&UnsatInt::ZERO));
621        assert_eq!(UnsatInt::ONE, UnsatInt::ONE.add(&UnsatInt::ZERO));
622        assert_eq!(UnsatInt::ZERO, UnsatInt::MINUS_ONE.add(&UnsatInt::ONE));
623    }
624
625    #[test]
626    fn unsatint_mul() {
627        assert_eq!(UnsatInt::ZERO, UnsatInt::ZERO.mul(0));
628        assert_eq!(UnsatInt::ZERO, UnsatInt::ZERO.mul(1));
629        assert_eq!(UnsatInt::ZERO, UnsatInt::ONE.mul(0));
630        assert_eq!(UnsatInt::ZERO, UnsatInt::MINUS_ONE.mul(0));
631        assert_eq!(UnsatInt::ONE, UnsatInt::ONE.mul(1));
632        assert_eq!(UnsatInt::MINUS_ONE, UnsatInt::MINUS_ONE.mul(1));
633    }
634
635    #[test]
636    fn unsatint_neg() {
637        assert_eq!(UnsatInt::ZERO, UnsatInt::ZERO.neg());
638        assert_eq!(UnsatInt::MINUS_ONE, UnsatInt::ONE.neg());
639        assert_eq!(UnsatInt::ONE, UnsatInt::MINUS_ONE.neg());
640    }
641
642    #[test]
643    fn unsatint_is_negative() {
644        assert!(!UnsatInt::ZERO.is_negative().to_bool_vartime());
645        assert!(!UnsatInt::ONE.is_negative().to_bool_vartime());
646        assert!(UnsatInt::MINUS_ONE.is_negative().to_bool_vartime());
647    }
648
649    #[test]
650    fn unsatint_shr() {
651        let n = super::UnsatInt([
652            0,
653            1211048314408256470,
654            1344008336933394898,
655            3913497193346473913,
656            2764114971089162538,
657            4,
658        ]);
659
660        assert_eq!(
661            &n.shr().0,
662            &[
663                1211048314408256470,
664                1344008336933394898,
665                3913497193346473913,
666                2764114971089162538,
667                4,
668                0
669            ]
670        );
671    }
672}