crypto_bigint/uint/
sqrt.rs
1use super::UInt;
4use crate::{Limb, Word};
5use subtle::{ConstantTimeEq, CtOption};
6
7impl<const LIMBS: usize> UInt<LIMBS> {
8 pub const fn sqrt(&self) -> Self {
13 let max_bits = (self.bits_vartime() + 1) >> 1;
14 let cap = Self::ONE.shl_vartime(max_bits);
15 let mut guess = cap; let mut xn = {
17 let q = self.wrapping_div(&guess);
18 let t = guess.wrapping_add(&q);
19 t.shr_vartime(1)
20 };
21
22 while guess.ct_cmp(&xn) == -1 {
25 let res = Limb::ct_cmp(Limb(xn.bits_vartime() as Word), Limb(max_bits as Word)) - 1;
29 let le = Limb::is_nonzero(Limb(res as Word));
30 guess = Self::ct_select(cap, xn, le);
31 xn = {
32 let q = self.wrapping_div(&guess);
33 let t = guess.wrapping_add(&q);
34 t.shr_vartime(1)
35 };
36 }
37
38 while guess.ct_cmp(&xn) == 1 && xn.ct_is_nonzero() == Word::MAX {
40 guess = xn;
41 xn = {
42 let q = self.wrapping_div(&guess);
43 let t = guess.wrapping_add(&q);
44 t.shr_vartime(1)
45 };
46 }
47
48 Self::ct_select(Self::ZERO, guess, self.ct_is_nonzero())
49 }
50
51 pub const fn wrapping_sqrt(&self) -> Self {
55 self.sqrt()
56 }
57
58 pub fn checked_sqrt(&self) -> CtOption<Self> {
61 let r = self.sqrt();
62 let s = r.wrapping_mul(&r);
63 CtOption::new(r, self.ct_eq(&s))
64 }
65}
66
67#[cfg(test)]
68mod tests {
69 use crate::{Limb, U256};
70
71 #[cfg(feature = "rand")]
72 use {
73 crate::{CheckedMul, Random, U512},
74 rand_chacha::ChaChaRng,
75 rand_core::{RngCore, SeedableRng},
76 };
77
78 #[test]
79 fn edge() {
80 assert_eq!(U256::ZERO.sqrt(), U256::ZERO);
81 assert_eq!(U256::ONE.sqrt(), U256::ONE);
82 let mut half = U256::ZERO;
83 for i in 0..half.limbs.len() / 2 {
84 half.limbs[i] = Limb::MAX;
85 }
86 assert_eq!(U256::MAX.sqrt(), half,);
87 }
88
89 #[test]
90 fn simple() {
91 let tests = [
92 (4u8, 2u8),
93 (9, 3),
94 (16, 4),
95 (25, 5),
96 (36, 6),
97 (49, 7),
98 (64, 8),
99 (81, 9),
100 (100, 10),
101 (121, 11),
102 (144, 12),
103 (169, 13),
104 ];
105 for (a, e) in &tests {
106 let l = U256::from(*a);
107 let r = U256::from(*e);
108 assert_eq!(l.sqrt(), r);
109 assert_eq!(l.checked_sqrt().is_some().unwrap_u8(), 1u8);
110 }
111 }
112
113 #[test]
114 fn nonsquares() {
115 assert_eq!(U256::from(2u8).sqrt(), U256::from(1u8));
116 assert_eq!(U256::from(2u8).checked_sqrt().is_some().unwrap_u8(), 0);
117 assert_eq!(U256::from(3u8).sqrt(), U256::from(1u8));
118 assert_eq!(U256::from(3u8).checked_sqrt().is_some().unwrap_u8(), 0);
119 assert_eq!(U256::from(5u8).sqrt(), U256::from(2u8));
120 assert_eq!(U256::from(6u8).sqrt(), U256::from(2u8));
121 assert_eq!(U256::from(7u8).sqrt(), U256::from(2u8));
122 assert_eq!(U256::from(8u8).sqrt(), U256::from(2u8));
123 assert_eq!(U256::from(10u8).sqrt(), U256::from(3u8));
124 }
125
126 #[cfg(feature = "rand")]
127 #[test]
128 fn fuzz() {
129 let mut rng = ChaChaRng::from_seed([7u8; 32]);
130 for _ in 0..50 {
131 let t = rng.next_u32() as u64;
132 let s = U256::from(t);
133 let s2 = s.checked_mul(&s).unwrap();
134 assert_eq!(s2.sqrt(), s);
135 assert_eq!(s2.checked_sqrt().is_some().unwrap_u8(), 1);
136 }
137
138 for _ in 0..50 {
139 let s = U256::random(&mut rng);
140 let mut s2 = U512::ZERO;
141 s2.limbs[..s.limbs.len()].copy_from_slice(&s.limbs);
142 assert_eq!(s.square().sqrt(), s2);
143 }
144 }
145}