crypto_bigint/modular/safegcd/
boxed.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//! See parent module for more information.
5
6use super::{inv_mod2_62, iterations, jump, Matrix};
7use crate::{BoxedUint, Inverter, Limb, Odd, Word};
8use alloc::boxed::Box;
9use core::{
10    cmp::max,
11    ops::{AddAssign, Mul},
12};
13use subtle::{Choice, ConditionallySelectable, ConstantTimeEq, ConstantTimeGreater, CtOption};
14
15/// Modular multiplicative inverter based on the Bernstein-Yang method.
16///
17/// See [`super::SafeGcdInverter`] for more information.
18#[derive(Clone, Debug)]
19pub struct BoxedSafeGcdInverter {
20    /// Modulus
21    pub(crate) modulus: BoxedUnsatInt,
22
23    /// Adjusting parameter (see toplevel documentation).
24    adjuster: BoxedUnsatInt,
25
26    /// Multiplicative inverse of the modulus modulo 2^62
27    inverse: i64,
28}
29
30impl BoxedSafeGcdInverter {
31    /// Creates the inverter for specified modulus and adjusting parameter.
32    ///
33    /// Modulus must be odd. Returns `None` if it is not.
34    pub fn new(modulus: &Odd<BoxedUint>, adjuster: &BoxedUint) -> Self {
35        Self {
36            modulus: BoxedUnsatInt::from(&modulus.0),
37            adjuster: BoxedUnsatInt::from(&adjuster.widen(modulus.bits_precision())),
38            inverse: inv_mod2_62(modulus.0.as_words()),
39        }
40    }
41
42    /// Returns either "value (mod M)" or "-value (mod M)", where M is the modulus the inverter
43    /// was created for, depending on "negate", which determines the presence of "-" in the used
44    /// formula. The input integer lies in the interval (-2 * M, M).
45    fn norm(&self, mut value: BoxedUnsatInt, negate: Choice) -> BoxedUnsatInt {
46        value.conditional_add(&self.modulus, value.is_negative());
47        value.conditional_assign(&value.neg(), negate);
48        value.conditional_add(&self.modulus, value.is_negative());
49        value
50    }
51}
52
53impl Inverter for BoxedSafeGcdInverter {
54    type Output = BoxedUint;
55
56    fn invert(&self, value: &BoxedUint) -> CtOption<Self::Output> {
57        let mut d = BoxedUnsatInt::zero(self.modulus.nlimbs());
58        let mut g = BoxedUnsatInt::from(value).widen(d.nlimbs());
59        let f = divsteps(&mut d, &self.adjuster, &self.modulus, &mut g, self.inverse);
60
61        // At this point the absolute value of "f" equals the greatest common divisor of the
62        // integer to be inverted and the modulus the inverter was created for.
63        // Thus, if "f" is neither 1 nor -1, then the sought inverse does not exist.
64        let antiunit = f.is_minus_one();
65        let ret = self.norm(d, antiunit);
66        let is_some = f.is_one() | antiunit;
67
68        CtOption::new(ret.to_uint(value.bits_precision()), is_some)
69    }
70
71    fn invert_vartime(&self, value: &BoxedUint) -> CtOption<Self::Output> {
72        let mut d = BoxedUnsatInt::zero(self.modulus.nlimbs());
73        let mut g = BoxedUnsatInt::from(value).widen(d.nlimbs());
74        let f = divsteps_vartime(&mut d, &self.adjuster, &self.modulus, &mut g, self.inverse);
75
76        // At this point the absolute value of "f" equals the greatest common divisor of the
77        // integer to be inverted and the modulus the inverter was created for.
78        // Thus, if "f" is neither 1 nor -1, then the sought inverse does not exist.
79        let antiunit = f.is_minus_one();
80        let ret = self.norm(d, antiunit);
81        let is_some = f.is_one() | antiunit;
82
83        CtOption::new(ret.to_uint(value.bits_precision()), is_some)
84    }
85}
86
87/// Compute the number of unsaturated limbs needed to represent a saturated integer with the given
88/// number of saturated limbs.
89fn unsat_nlimbs_for_sat_nlimbs(saturated_nlimbs: usize) -> usize {
90    let saturated_nlimbs = if Word::BITS == 32 && saturated_nlimbs == 1 {
91        2
92    } else {
93        saturated_nlimbs
94    };
95
96    safegcd_nlimbs!(saturated_nlimbs * Limb::BITS as usize)
97}
98
99/// Returns the greatest common divisor (GCD) of the two given numbers.
100pub(crate) fn gcd(f: &BoxedUint, g: &BoxedUint) -> BoxedUint {
101    let nlimbs = unsat_nlimbs_for_sat_nlimbs(max(f.nlimbs(), g.nlimbs()));
102    let bits_precision = f.bits_precision();
103
104    let inverse = inv_mod2_62(f.as_words());
105    let f = BoxedUnsatInt::from_uint_widened(f, nlimbs);
106    let mut g = BoxedUnsatInt::from_uint_widened(g, nlimbs);
107    let mut d = BoxedUnsatInt::zero(nlimbs);
108    let e = BoxedUnsatInt::one(nlimbs);
109
110    let mut f = divsteps(&mut d, &e, &f, &mut g, inverse);
111    f.conditional_negate(f.is_negative());
112    f.to_uint(bits_precision)
113}
114
115/// Returns the greatest common divisor (GCD) of the two given numbers.
116///
117/// Variable time with respect to `g`.
118pub(crate) fn gcd_vartime(f: &BoxedUint, g: &BoxedUint) -> BoxedUint {
119    let nlimbs = unsat_nlimbs_for_sat_nlimbs(max(f.nlimbs(), g.nlimbs()));
120    let bits_precision = f.bits_precision();
121
122    let inverse = inv_mod2_62(f.as_words());
123    let f = BoxedUnsatInt::from_uint_widened(f, nlimbs);
124    let mut g = BoxedUnsatInt::from_uint_widened(g, nlimbs);
125    let mut d = BoxedUnsatInt::zero(nlimbs);
126    let e = BoxedUnsatInt::one(nlimbs);
127
128    let mut f = divsteps_vartime(&mut d, &e, &f, &mut g, inverse);
129    f.conditional_negate(f.is_negative());
130    f.to_uint(bits_precision)
131}
132
133/// Algorithm `divsteps2` to compute (δₙ, fₙ, gₙ) = divstepⁿ(δ, f, g) as described in Figure 10.1
134/// of <https://eprint.iacr.org/2019/266.pdf>.
135///
136/// This version is variable-time with respect to `g`.
137fn divsteps(
138    d: &mut BoxedUnsatInt,
139    e: &BoxedUnsatInt,
140    f_0: &BoxedUnsatInt,
141    g: &mut BoxedUnsatInt,
142    inverse: i64,
143) -> BoxedUnsatInt {
144    debug_assert_eq!(f_0.nlimbs(), g.nlimbs());
145
146    let mut e = e.clone();
147    let mut f = f_0.clone();
148    let mut delta = 1;
149    let mut matrix;
150
151    for _ in 0..iterations(f_0.bits(), g.bits()) {
152        (delta, matrix) = jump(&f.0, &g.0, delta);
153        fg(&mut f, g, matrix);
154        de(f_0, inverse, matrix, d, &mut e);
155    }
156
157    f
158}
159
160/// Algorithm `divsteps2` to compute (δₙ, fₙ, gₙ) = divstepⁿ(δ, f, g) as described in Figure 10.1
161/// of <https://eprint.iacr.org/2019/266.pdf>.
162///
163/// This version runs in a fixed number of iterations relative to the highest bit of `f` or `g`
164/// as described in Figure 11.1.
165fn divsteps_vartime(
166    d: &mut BoxedUnsatInt,
167    e: &BoxedUnsatInt,
168    f_0: &BoxedUnsatInt,
169    g: &mut BoxedUnsatInt,
170    inverse: i64,
171) -> BoxedUnsatInt {
172    debug_assert_eq!(f_0.nlimbs(), g.nlimbs());
173
174    let mut e = e.clone();
175    let mut f = f_0.clone();
176    let mut delta = 1;
177    let mut matrix;
178
179    while !bool::from(g.is_zero()) {
180        (delta, matrix) = jump(&f.0, &g.0, delta);
181        fg(&mut f, g, matrix);
182        de(f_0, inverse, matrix, d, &mut e);
183    }
184
185    f
186}
187
188/// Returns the updated values of the variables f and g for specified initial ones and
189/// Bernstein-Yang transition matrix multiplied by 2^62.
190///
191/// The returned vector is "matrix * (f, g)' / 2^62", where "'" is the transpose operator.
192fn fg(f: &mut BoxedUnsatInt, g: &mut BoxedUnsatInt, t: Matrix) {
193    // TODO(tarcieri): reduce allocations
194    let mut f2 = &*f * t[0][0];
195    f2 += &*g * t[0][1];
196    f2.shr_assign();
197
198    let mut g2 = &*f * t[1][0];
199    g2 += &*g * t[1][1];
200    g2.shr_assign();
201
202    *f = f2;
203    *g = g2;
204}
205
206/// Returns the updated values of the variables d and e for specified initial ones and
207/// Bernstein-Yang transition matrix multiplied by 2^62.
208///
209/// The returned vector is congruent modulo M to "matrix * (d, e)' / 2^62 (mod M)", where M is the
210/// modulus the inverter was created for and "'" stands for the transpose operator.
211///
212/// Both the input and output values lie in the interval (-2 * M, M).
213fn de(
214    modulus: &BoxedUnsatInt,
215    inverse: i64,
216    t: Matrix,
217    d: &mut BoxedUnsatInt,
218    e: &mut BoxedUnsatInt,
219) {
220    let mask = BoxedUnsatInt::MASK as i64;
221    let mut md =
222        t[0][0] * d.is_negative().unwrap_u8() as i64 + t[0][1] * e.is_negative().unwrap_u8() as i64;
223    let mut me =
224        t[1][0] * d.is_negative().unwrap_u8() as i64 + t[1][1] * e.is_negative().unwrap_u8() as i64;
225
226    let cd = t[0][0]
227        .wrapping_mul(d.lowest() as i64)
228        .wrapping_add(t[0][1].wrapping_mul(e.lowest() as i64))
229        & mask;
230
231    let ce = t[1][0]
232        .wrapping_mul(d.lowest() as i64)
233        .wrapping_add(t[1][1].wrapping_mul(e.lowest() as i64))
234        & mask;
235
236    md -= (inverse.wrapping_mul(cd).wrapping_add(md)) & mask;
237    me -= (inverse.wrapping_mul(ce).wrapping_add(me)) & mask;
238
239    let mut cd = d.mul(t[0][0]);
240    cd += &e.mul(t[0][1]);
241    cd += &modulus.mul(md);
242    cd.shr_assign();
243
244    let mut ce = d.mul(t[1][0]);
245    ce += &e.mul(t[1][1]);
246    ce += &modulus.mul(me);
247    ce.shr_assign();
248
249    *d = cd;
250    *e = ce;
251}
252
253/// "Bigint"-like (62 * LIMBS)-bit signed integer type, whose variables store numbers in the two's
254/// complement code as arrays of 62-bit limbs in little endian order.
255///
256/// The arithmetic operations for this type are wrapping ones.
257#[derive(Clone, Debug)]
258pub(crate) struct BoxedUnsatInt(Box<[u64]>);
259
260/// Convert from 32/64-bit saturated representation used by `Uint` to the 62-bit unsaturated
261/// representation used by `BoxedUnsatInt`.
262///
263/// Returns a big unsigned integer as an array of 62-bit chunks, which is equal modulo
264/// 2 ^ (62 * S) to the input big unsigned integer stored as an array of 64-bit chunks.
265///
266/// The ordering of the chunks in these arrays is little-endian.
267impl From<&BoxedUint> for BoxedUnsatInt {
268    fn from(input: &BoxedUint) -> BoxedUnsatInt {
269        Self::from_uint_widened(input, unsat_nlimbs_for_sat_nlimbs(input.nlimbs()))
270    }
271}
272
273impl BoxedUnsatInt {
274    /// Number of bits in each limb.
275    pub const LIMB_BITS: usize = 62;
276
277    /// Mask, in which the 62 lowest bits are 1.
278    pub const MASK: u64 = u64::MAX >> (64 - Self::LIMB_BITS);
279
280    /// Convert from 32/64-bit saturated representation used by `BoxedUint` to the 62-bit
281    /// unsaturated representation used by `BoxedUnsatInt`.
282    ///
283    /// Returns a big unsigned integer as an array of 62-bit chunks, which is equal modulo
284    /// 2 ^ (62 * S) to the input big unsigned integer stored as an array of 64-bit chunks.
285    ///
286    /// The ordering of the chunks in these arrays is little-endian.
287    ///
288    /// The `nlimbs` parameter defines the number of unsaturated limbs in the output.
289    /// It's provided explicitly so multiple values can be padded to the same size.
290    #[allow(trivial_numeric_casts)]
291    fn from_uint_widened(input: &BoxedUint, nlimbs: usize) -> BoxedUnsatInt {
292        debug_assert!(nlimbs >= unsat_nlimbs_for_sat_nlimbs(input.nlimbs()));
293
294        // Workaround for 32-bit platforms: if the input is a single limb, it will be smaller input
295        // than is usable for Bernstein-Yang with is currently natively 64-bits on all targets
296        let mut tmp: [Word; 2] = [0; 2];
297
298        let input = if Word::BITS == 32 && input.nlimbs() == 1 {
299            tmp[0] = input.limbs[0].0;
300            &tmp
301        } else {
302            input.as_words()
303        };
304
305        let mut output = vec![0u64; nlimbs];
306        impl_limb_convert!(Word, Word::BITS as usize, input, u64, 62, output);
307        Self(output.into())
308    }
309
310    /// Convert to a `BoxedUint` of the given precision.
311    #[allow(trivial_numeric_casts)]
312    fn to_uint(&self, mut bits_precision: u32) -> BoxedUint {
313        // The current Bernstein-Yang implementation is natively 64-bit on all targets
314        if bits_precision == 32 {
315            bits_precision = 64;
316        }
317
318        debug_assert_eq!(self.nlimbs(), safegcd_nlimbs!(bits_precision as usize));
319        assert!(
320            !bool::from(self.is_negative()),
321            "can't convert negative number to BoxedUint"
322        );
323
324        let mut ret = BoxedUint {
325            limbs: vec![Limb::ZERO; nlimbs!(bits_precision)].into(),
326        };
327
328        impl_limb_convert!(
329            u64,
330            62,
331            &self.0,
332            Word,
333            Word::BITS as usize,
334            ret.as_words_mut()
335        );
336
337        ret
338    }
339
340    /// Conditionally add the given value to this one depending on the given [`Choice`].
341    pub fn conditional_add(&mut self, other: &Self, choice: Choice) {
342        debug_assert_eq!(self.nlimbs(), other.nlimbs());
343        let mut carry = 0;
344
345        for i in 0..self.nlimbs() {
346            let addend = u64::conditional_select(&0, &other.0[i], choice);
347            let sum = self.0[i] + addend + carry;
348            self.0[i] = sum & Self::MASK;
349            carry = sum >> Self::LIMB_BITS;
350        }
351    }
352
353    /// Conditionally assign a value to this one depending on the given [`Choice`].
354    // NOTE: we can't impl `subtle::ConditionallySelectable` due to its `Copy` bound.
355    pub fn conditional_assign(&mut self, other: &Self, choice: Choice) {
356        for i in 0..self.nlimbs() {
357            self.0[i] = u64::conditional_select(&self.0[i], &other.0[i], choice);
358        }
359    }
360
361    /// Conditionally negate this value depending on the given [`Choice`].
362    // NOTE: we can't impl `subtle::ConditionallyNegatable` due to its `Copy` bound.
363    pub fn conditional_negate(&mut self, choice: Choice) {
364        // TODO(tarcieri): avoid allocations
365        self.conditional_assign(&self.neg(), choice);
366    }
367
368    /// Negate this value.
369    ///
370    /// This is an inherent method rather than a `Neg` trait impl so it can borrow.
371    pub fn neg(&self) -> Self {
372        // For the two's complement code the additive negation is the result of adding 1 to the
373        // bitwise inverted argument's representation.
374        let nlimbs = self.nlimbs();
375        let mut ret = Self::zero(nlimbs);
376        let mut carry = 1;
377
378        for i in 0..nlimbs {
379            let sum = (self.0[i] ^ Self::MASK) + carry;
380            ret.0[i] = sum & Self::MASK;
381            carry = sum >> Self::LIMB_BITS;
382        }
383
384        ret
385    }
386
387    /// Apply 62-bit right arithmetical shift in-place.
388    pub fn shr_assign(&mut self) {
389        let is_negative = self.is_negative();
390
391        for i in 0..(self.nlimbs() - 1) {
392            self.0[i] = self.0[i + 1];
393        }
394
395        self.0[self.nlimbs() - 1] = u64::conditional_select(&0, &Self::MASK, is_negative);
396    }
397
398    /// Get the value zero for the given number of limbs.
399    pub fn zero(nlimbs: usize) -> Self {
400        Self(vec![0; nlimbs].into())
401    }
402
403    /// Get the value one for the given number of limbs.
404    pub fn one(nlimbs: usize) -> Self {
405        let mut ret = Self::zero(nlimbs);
406        ret.0[0] = 1;
407        ret
408    }
409
410    /// Widen self to the given number of limbs.
411    pub fn widen(self, nlimbs: usize) -> Self {
412        debug_assert!(nlimbs >= self.nlimbs(),);
413        let mut limbs = self.0.into_vec();
414        limbs.resize(nlimbs, 0);
415        Self(limbs.into())
416    }
417
418    /// Is the current value -1?
419    pub fn is_minus_one(&self) -> Choice {
420        self.0
421            .iter()
422            .fold(Choice::from(1), |acc, &limb| acc & limb.ct_eq(&Self::MASK))
423    }
424
425    /// Returns "true" iff the current number is negative.
426    pub fn is_negative(&self) -> Choice {
427        self.0[self.nlimbs() - 1].ct_gt(&(Self::MASK >> 1))
428    }
429
430    /// Is the current value zero?
431    pub fn is_zero(&self) -> Choice {
432        self.0
433            .iter()
434            .fold(Choice::from(1), |acc, &limb| acc & limb.ct_eq(&0))
435    }
436
437    /// Is the current value one?
438    pub fn is_one(&self) -> Choice {
439        self.0[1..]
440            .iter()
441            .fold(self.lowest().ct_eq(&1), |acc, &limb| acc & limb.ct_eq(&0))
442    }
443
444    /// Returns the lowest 62 bits of the current number.
445    pub fn lowest(&self) -> u64 {
446        self.0[0]
447    }
448
449    /// Returns the number of limbs used by this integer.
450    pub fn nlimbs(&self) -> usize {
451        self.0.len()
452    }
453
454    /// Calculate the number of leading zeros in the binary representation of this number.
455    pub fn leading_zeros(&self) -> u32 {
456        let mut count = 0;
457        let mut nonzero_limb_not_encountered = Choice::from(1);
458
459        for l in self.0.iter() {
460            let z = l.leading_zeros() - 2;
461            count += u32::conditional_select(&0, &z, nonzero_limb_not_encountered);
462            nonzero_limb_not_encountered &= !l.ct_eq(&0);
463        }
464
465        count
466    }
467
468    /// Calculate the number of bits in this value (i.e. index of the highest bit) in constant time.
469    pub fn bits(&self) -> u32 {
470        (self.0.len() as u32 * 62) - self.leading_zeros()
471    }
472}
473
474impl AddAssign<BoxedUnsatInt> for BoxedUnsatInt {
475    fn add_assign(&mut self, rhs: BoxedUnsatInt) {
476        self.add_assign(&rhs);
477    }
478}
479
480impl AddAssign<&BoxedUnsatInt> for BoxedUnsatInt {
481    fn add_assign(&mut self, rhs: &BoxedUnsatInt) {
482        debug_assert_eq!(self.nlimbs(), rhs.nlimbs());
483        let mut carry = 0;
484
485        for i in 0..self.nlimbs() {
486            let sum = self.0[i] + rhs.0[i] + carry;
487            self.0[i] = sum & Self::MASK;
488            carry = sum >> Self::LIMB_BITS;
489        }
490    }
491}
492
493impl Mul<i64> for &BoxedUnsatInt {
494    type Output = BoxedUnsatInt;
495
496    fn mul(self, other: i64) -> BoxedUnsatInt {
497        let nlimbs = self.nlimbs();
498        let mut ret = BoxedUnsatInt::zero(nlimbs);
499
500        // If the short multiplicand is non-negative, the standard multiplication algorithm is
501        // performed. Otherwise, the product of the additively negated multiplicands is found as
502        // follows.
503        //
504        // Since for the two's complement code the additive negation is the result of adding 1 to
505        // the bitwise inverted argument's representation, for any encoded integers x and y we have
506        // x * y = (-x) * (-y) = (!x + 1) * (-y) = !x * (-y) + (-y), where "!" is the bitwise
507        // inversion and arithmetic operations are performed according to the rules of the code.
508        //
509        // If the short multiplicand is negative, the algorithm below uses this formula by
510        // substituting the short multiplicand for y and turns into the modified standard
511        // multiplication algorithm, where the carry flag is initialized with the additively
512        // negated short multiplicand and the chunks of the long multiplicand are bitwise inverted.
513        let (other, mut carry, mask) = if other < 0 {
514            (-other, -other as u64, BoxedUnsatInt::MASK)
515        } else {
516            (other, 0, 0)
517        };
518
519        for i in 0..nlimbs {
520            let sum = (carry as u128) + ((self.0[i] ^ mask) as u128) * (other as u128);
521            ret.0[i] = sum as u64 & BoxedUnsatInt::MASK;
522            carry = (sum >> BoxedUnsatInt::LIMB_BITS) as u64;
523        }
524
525        ret
526    }
527}
528
529#[cfg(test)]
530mod tests {
531    use super::BoxedUnsatInt;
532    use crate::{BoxedUint, Inverter, PrecomputeInverter, U256};
533    use proptest::prelude::*;
534    use subtle::ConstantTimeEq;
535
536    #[cfg(not(miri))]
537    use crate::modular::safegcd::UnsatInt;
538
539    impl PartialEq for BoxedUnsatInt {
540        fn eq(&self, other: &Self) -> bool {
541            self.0.ct_eq(&other.0).into()
542        }
543    }
544
545    #[test]
546    fn invert() {
547        let g = BoxedUint::from_be_hex(
548            "00000000CBF9350842F498CE441FC2DC23C7BF47D3DE91C327B2157C5E4EED77",
549            256,
550        )
551        .unwrap();
552        let modulus = BoxedUint::from_be_hex(
553            "FFFFFFFF00000000FFFFFFFFFFFFFFFFBCE6FAADA7179E84F3B9CAC2FC632551",
554            256,
555        )
556        .unwrap()
557        .to_odd()
558        .unwrap();
559        let inverter = modulus.precompute_inverter();
560        let result = inverter.invert(&g).unwrap();
561        assert_eq!(
562            BoxedUint::from_be_hex(
563                "FB668F8F509790BC549B077098918604283D42901C92981062EB48BC723F617B",
564                256
565            )
566            .unwrap(),
567            result
568        );
569    }
570
571    #[test]
572    fn de() {
573        let modulus = BoxedUnsatInt(
574            vec![
575                3727233105432618321,
576                3718823987352861203,
577                4611686018427387899,
578                4611685743549481023,
579                255,
580                0,
581            ]
582            .into(),
583        );
584        let inverse = 3687945983376704433;
585        let mut d = BoxedUnsatInt(
586            vec![
587                3490544662636853909,
588                2211268325417683828,
589                992023446686701852,
590                4539270294123539695,
591                4611686018427387762,
592                4611686018427387903,
593            ]
594            .into(),
595        );
596        let mut e = BoxedUnsatInt(
597            vec![
598                4004071259428196451,
599                1262234674432503659,
600                4060414504149367846,
601                1804121722707079191,
602                4611686018427387712,
603                4611686018427387903,
604            ]
605            .into(),
606        );
607        let t = [[-45035996273704960, 409827566090715136], [-14, 25]];
608
609        super::de(&modulus, inverse, t, &mut d, &mut e);
610        assert_eq!(
611            d,
612            BoxedUnsatInt(
613                vec![
614                    1211048314408256470,
615                    1344008336933394898,
616                    3913497193346473913,
617                    2764114971089162538,
618                    4,
619                    0,
620                ]
621                .into()
622            )
623        );
624
625        assert_eq!(e, BoxedUnsatInt(vec![0, 0, 0, 0, 0, 0].into()));
626    }
627
628    #[test]
629    fn boxed_unsatint_is_zero() {
630        let zero = BoxedUnsatInt::from(&U256::ZERO.into());
631        assert!(bool::from(zero.is_zero()));
632
633        let one = BoxedUnsatInt::from(&U256::ONE.into());
634        assert!(!bool::from(one.is_zero()));
635    }
636
637    #[test]
638    fn boxed_unsatint_is_one() {
639        let zero = BoxedUnsatInt::from(&U256::ZERO.into());
640        assert!(!bool::from(zero.is_one()));
641
642        let one = BoxedUnsatInt::from(&U256::ONE.into());
643        assert!(bool::from(one.is_one()));
644    }
645
646    #[test]
647    fn unsatint_shr_assign() {
648        let mut n = BoxedUnsatInt(
649            vec![
650                0,
651                1211048314408256470,
652                1344008336933394898,
653                3913497193346473913,
654                2764114971089162538,
655                4,
656            ]
657            .into(),
658        );
659        n.shr_assign();
660
661        assert_eq!(
662            &*n.0,
663            &[
664                1211048314408256470,
665                1344008336933394898,
666                3913497193346473913,
667                2764114971089162538,
668                4,
669                0
670            ]
671        );
672    }
673
674    prop_compose! {
675        fn u256()(bytes in any::<[u8; 32]>()) -> U256 {
676            U256::from_le_slice(&bytes)
677        }
678    }
679
680    proptest! {
681        #[test]
682        #[cfg(not(miri))]
683        fn boxed_unsatint_add(x in u256(), y in u256()) {
684            let x_ref = UnsatInt::<{ safegcd_nlimbs!(256usize) }>::from_uint(&x);
685            let y_ref = UnsatInt::<{ safegcd_nlimbs!(256usize) }>::from_uint(&y);
686            let mut x_boxed = BoxedUnsatInt::from(&x.into());
687            let y_boxed = BoxedUnsatInt::from(&y.into());
688
689            let expected = x_ref.add(&y_ref);
690            x_boxed += &y_boxed;
691            prop_assert_eq!(&expected.0, &*x_boxed.0);
692        }
693
694        #[test]
695        #[cfg(not(miri))]
696        fn boxed_unsatint_mul(x in u256(), y in any::<i64>()) {
697            let x_ref = UnsatInt::<{ safegcd_nlimbs!(256usize) }>::from_uint(&x);
698            let x_boxed = BoxedUnsatInt::from(&x.into());
699
700            let expected = x_ref.mul(y);
701            let actual = &x_boxed * y;
702            prop_assert_eq!(&expected.0, &*actual.0);
703        }
704
705        #[test]
706        #[cfg(not(miri))]
707        fn boxed_unsatint_neg(x in u256()) {
708            let x_ref = UnsatInt::<{ safegcd_nlimbs!(256usize) }>::from_uint(&x);
709            let x_boxed = BoxedUnsatInt::from(&x.into());
710
711            let expected = x_ref.neg();
712            let actual = x_boxed.neg();
713            prop_assert_eq!(&expected.0, &*actual.0);
714        }
715
716        #[test]
717        #[cfg(not(miri))]
718        fn boxed_unsatint_shr(x in u256()) {
719            let x_ref = UnsatInt::<{ safegcd_nlimbs!(256usize) }>::from_uint(&x);
720            let mut x_boxed = BoxedUnsatInt::from(&x.into());
721            x_boxed.shr_assign();
722
723            let expected = x_ref.shr();
724            prop_assert_eq!(&expected.0, &*x_boxed.0);
725        }
726
727        #[test]
728                #[cfg(not(miri))]
729
730        fn boxed_unsatint_is_negative(x in u256()) {
731            let x_ref = UnsatInt::<{ safegcd_nlimbs!(256usize) }>::from_uint(&x);
732            let x_boxed = BoxedUnsatInt::from(&x.into());
733            assert_eq!(x_ref.is_negative().to_bool_vartime(), bool::from(x_boxed.is_negative()));
734        }
735
736        #[test]
737                #[cfg(not(miri))]
738
739        fn boxed_unsatint_is_minus_one(x in u256()) {
740            let x_ref = UnsatInt::<{ safegcd_nlimbs!(256usize) }>::from_uint(&x);
741            let x_boxed = BoxedUnsatInt::from(&x.into());
742            assert!(bool::from(x_boxed.is_minus_one().ct_eq(&x_ref.eq(&UnsatInt::MINUS_ONE).into())));
743        }
744    }
745}