crypto_bigint/uint/
sqrt.rs1use super::Uint;
4use crate::{Limb, Word};
5use subtle::{ConstantTimeEq, CtOption};
6
7impl<const LIMBS: usize> Uint<LIMBS> {
8    #[deprecated(
10        since = "0.5.3",
11        note = "This functionality will be moved to `sqrt_vartime` in a future release."
12    )]
13    pub const fn sqrt(&self) -> Self {
14        self.sqrt_vartime()
15    }
16
17    pub const fn sqrt_vartime(&self) -> Self {
22        let max_bits = (self.bits_vartime() + 1) >> 1;
23        let cap = Self::ONE.shl_vartime(max_bits);
24        let mut guess = cap; let mut xn = {
26            let q = self.wrapping_div(&guess);
27            let t = guess.wrapping_add(&q);
28            t.shr_vartime(1)
29        };
30
31        while Uint::ct_lt(&guess, &xn).is_true_vartime() {
34            let le = Limb::ct_le(Limb(xn.bits_vartime() as Word), Limb(max_bits as Word));
38            guess = Self::ct_select(&cap, &xn, le);
39            xn = {
40                let q = self.wrapping_div(&guess);
41                let t = guess.wrapping_add(&q);
42                t.shr_vartime(1)
43            };
44        }
45
46        while Uint::ct_gt(&guess, &xn).is_true_vartime() && xn.ct_is_nonzero().is_true_vartime() {
48            guess = xn;
49            xn = {
50                let q = self.wrapping_div(&guess);
51                let t = guess.wrapping_add(&q);
52                t.shr_vartime(1)
53            };
54        }
55
56        Self::ct_select(&Self::ZERO, &guess, self.ct_is_nonzero())
57    }
58
59    #[deprecated(
61        since = "0.5.3",
62        note = "This functionality will be moved to `wrapping_sqrt_vartime` in a future release."
63    )]
64    pub const fn wrapping_sqrt(&self) -> Self {
65        self.wrapping_sqrt_vartime()
66    }
67
68    pub const fn wrapping_sqrt_vartime(&self) -> Self {
72        self.sqrt_vartime()
73    }
74
75    #[deprecated(
77        since = "0.5.3",
78        note = "This functionality will be moved to `checked_sqrt_vartime` in a future release."
79    )]
80    pub fn checked_sqrt(&self) -> CtOption<Self> {
81        self.checked_sqrt_vartime()
82    }
83
84    pub fn checked_sqrt_vartime(&self) -> CtOption<Self> {
87        let r = self.sqrt_vartime();
88        let s = r.wrapping_mul(&r);
89        CtOption::new(r, ConstantTimeEq::ct_eq(self, &s))
90    }
91}
92
93#[cfg(test)]
94mod tests {
95    use crate::{Limb, U256};
96
97    #[cfg(feature = "rand")]
98    use {
99        crate::{CheckedMul, Random, U512},
100        rand_chacha::ChaChaRng,
101        rand_core::{RngCore, SeedableRng},
102    };
103
104    #[test]
105    fn edge() {
106        assert_eq!(U256::ZERO.sqrt_vartime(), U256::ZERO);
107        assert_eq!(U256::ONE.sqrt_vartime(), U256::ONE);
108        let mut half = U256::ZERO;
109        for i in 0..half.limbs.len() / 2 {
110            half.limbs[i] = Limb::MAX;
111        }
112        assert_eq!(U256::MAX.sqrt_vartime(), half,);
113    }
114
115    #[test]
116    fn simple() {
117        let tests = [
118            (4u8, 2u8),
119            (9, 3),
120            (16, 4),
121            (25, 5),
122            (36, 6),
123            (49, 7),
124            (64, 8),
125            (81, 9),
126            (100, 10),
127            (121, 11),
128            (144, 12),
129            (169, 13),
130        ];
131        for (a, e) in &tests {
132            let l = U256::from(*a);
133            let r = U256::from(*e);
134            assert_eq!(l.sqrt_vartime(), r);
135            assert_eq!(l.checked_sqrt_vartime().is_some().unwrap_u8(), 1u8);
136        }
137    }
138
139    #[test]
140    fn nonsquares() {
141        assert_eq!(U256::from(2u8).sqrt_vartime(), U256::from(1u8));
142        assert_eq!(
143            U256::from(2u8).checked_sqrt_vartime().is_some().unwrap_u8(),
144            0
145        );
146        assert_eq!(U256::from(3u8).sqrt_vartime(), U256::from(1u8));
147        assert_eq!(
148            U256::from(3u8).checked_sqrt_vartime().is_some().unwrap_u8(),
149            0
150        );
151        assert_eq!(U256::from(5u8).sqrt_vartime(), U256::from(2u8));
152        assert_eq!(U256::from(6u8).sqrt_vartime(), U256::from(2u8));
153        assert_eq!(U256::from(7u8).sqrt_vartime(), U256::from(2u8));
154        assert_eq!(U256::from(8u8).sqrt_vartime(), U256::from(2u8));
155        assert_eq!(U256::from(10u8).sqrt_vartime(), U256::from(3u8));
156    }
157
158    #[cfg(feature = "rand")]
159    #[test]
160    fn fuzz() {
161        let mut rng = ChaChaRng::from_seed([7u8; 32]);
162        for _ in 0..50 {
163            let t = rng.next_u32() as u64;
164            let s = U256::from(t);
165            let s2 = s.checked_mul(&s).unwrap();
166            assert_eq!(s2.sqrt_vartime(), s);
167            assert_eq!(s2.checked_sqrt_vartime().is_some().unwrap_u8(), 1);
168        }
169
170        for _ in 0..50 {
171            let s = U256::random(&mut rng);
172            let mut s2 = U512::ZERO;
173            s2.limbs[..s.limbs.len()].copy_from_slice(&s.limbs);
174            assert_eq!(s.square().sqrt_vartime(), s2);
175        }
176    }
177}