1use crate::{Limb, NonZero, SubMod, Uint};
4
5impl<const LIMBS: usize> Uint<LIMBS> {
6 #[must_use]
10 pub const fn sub_mod(&self, rhs: &Self, p: &NonZero<Self>) -> Self {
11 let (out, mask) = self.borrowing_sub(rhs, Limb::ZERO);
12
13 out.wrapping_add(&p.as_ref().bitand_limb(mask))
16 }
17
18 #[inline(always)]
21 pub(crate) const fn try_sub_with_carry(&self, mut carry: Limb, rhs: &Self) -> (Self, Limb) {
22 let (out, borrow) = self.borrowing_sub(rhs, Limb::ZERO);
23 let revert = borrow.lsb_to_choice().and(carry.is_zero());
24 (carry, _) = carry.borrowing_sub(Limb::ZERO, Limb::select(borrow, Limb::ZERO, revert));
25 (Uint::select(&out, self, revert), carry)
26 }
27
28 #[must_use]
33 pub const fn sub_mod_special(&self, rhs: &Self, c: Limb) -> Self {
34 let (out, borrow) = self.borrowing_sub(rhs, Limb::ZERO);
35
36 let l = borrow.0 & c.0;
40 out.wrapping_sub(&Self::from_word(l))
41 }
42}
43
44impl<const LIMBS: usize> SubMod for Uint<LIMBS> {
45 type Output = Self;
46
47 fn sub_mod(&self, rhs: &Self, p: &NonZero<Self>) -> Self {
48 debug_assert!(self < p.as_ref());
49 debug_assert!(rhs < p.as_ref());
50 self.sub_mod(rhs, p)
51 }
52}
53
54#[cfg(test)]
55mod tests {
56 use crate::{Limb, U128, U256};
57
58 #[cfg(feature = "rand_core")]
59 use crate::{NonZero, Random, RandomMod, Uint};
60 #[cfg(feature = "rand_core")]
61 use rand_core::SeedableRng;
62
63 #[test]
64 fn sub_mod_nist_p256() {
65 let a =
66 U256::from_be_hex("1a2472fde50286541d97ca6a3592dd75beb9c9646e40c511b82496cfc3926956");
67 let b =
68 U256::from_be_hex("d5777c45019673125ad240f83094d4252d829516fac8601ed01979ec1ec1a251");
69 let n =
70 U256::from_be_hex("ffffffff00000000ffffffffffffffffbce6faada7179e84f3b9cac2fc632551")
71 .to_nz()
72 .unwrap();
73
74 let actual = a.sub_mod(&b, &n);
75 let expected =
76 U256::from_be_hex("44acf6b7e36c1342c2c5897204fe09504e1e2efb1a900377dbc4e7a6a133ec56");
77
78 assert_eq!(expected, actual);
79 }
80
81 #[test]
82 fn try_sub_with_carry() {
83 let three = U128::from_u8(3);
84 let five = U128::from_u8(5);
85
86 assert_eq!(
88 five.try_sub_with_carry(Limb::ZERO, &three),
89 (U128::from_u8(2), Limb::ZERO)
90 );
91 assert_eq!(
93 three.try_sub_with_carry(Limb::ZERO, &five),
94 (three, Limb::ZERO)
95 );
96 assert_eq!(
98 three.try_sub_with_carry(Limb::ONE, &five),
99 (U128::MAX.wrapping_sub(&U128::ONE), Limb::ZERO)
100 );
101 assert_eq!(
103 U128::MAX.try_sub_with_carry(Limb::ONE, &U128::MAX.shr1()),
104 (U128::ONE.shl_vartime(127), Limb::ONE)
105 );
106 }
107
108 #[test]
109 #[cfg(feature = "rand_core")]
110 fn sub_mod() {
111 fn test_size<const LIMBS: usize>() {
112 let mut rng = chacha20::ChaCha8Rng::seed_from_u64(1);
113 let moduli = [
114 NonZero::<Uint<LIMBS>>::random_from_rng(&mut rng),
115 NonZero::<Uint<LIMBS>>::random_from_rng(&mut rng),
116 ];
117
118 for p in &moduli {
119 let base_cases = [
120 (1u64, 0u64, 1u64.into()),
121 (0, 1, p.wrapping_sub(&1u64.into())),
122 (0, 0, 0u64.into()),
123 ];
124 for (a, b, c) in &base_cases {
125 let a: Uint<LIMBS> = (*a).into();
126 let b: Uint<LIMBS> = (*b).into();
127
128 let x = a.sub_mod(&b, p);
129 assert_eq!(*c, x, "{} - {} mod {} = {} != {}", a, b, p, x, c);
130 }
131
132 if LIMBS > 1 {
133 for _i in 0..100 {
134 let a: Uint<LIMBS> = Limb::random_from_rng(&mut rng).into();
135 let b: Uint<LIMBS> = Limb::random_from_rng(&mut rng).into();
136 let (a, b) = if a < b { (b, a) } else { (a, b) };
137
138 let c = a.sub_mod(&b, p);
139 assert!(c < **p, "not reduced");
140 assert_eq!(c, a.wrapping_sub(&b), "result incorrect");
141 }
142 }
143
144 for _i in 0..100 {
145 let a = Uint::<LIMBS>::random_mod_vartime(&mut rng, p);
146 let b = Uint::<LIMBS>::random_mod_vartime(&mut rng, p);
147
148 let c = a.sub_mod(&b, p);
149 assert!(c < **p, "not reduced: {} >= {} ", c, p);
150
151 let x = a.wrapping_sub(&b);
152 if a >= b && x < **p {
153 assert_eq!(c, x, "incorrect result");
154 }
155 }
156 }
157 }
158
159 cpubits::cpubits! {
161 64 => { test_size::<1>(); }
162 }
163
164 test_size::<2>();
165 test_size::<3>();
166 if cfg!(not(miri)) {
167 test_size::<4>();
168 test_size::<8>();
169 test_size::<16>();
170 }
171 }
172
173 #[cfg(feature = "rand_core")]
174 #[test]
175 fn sub_mod_special() {
176 fn test_size<const LIMBS: usize>() {
177 let mut rng = chacha20::ChaCha8Rng::seed_from_u64(1);
178 let moduli = [
179 NonZero::<Limb>::random_from_rng(&mut rng),
180 NonZero::<Limb>::random_from_rng(&mut rng),
181 ];
182
183 for special in &moduli {
184 let p = &NonZero::new(Uint::ZERO.wrapping_sub(&Uint::from(special.get()))).unwrap();
185
186 let minus_one = p.wrapping_sub(&Uint::ONE);
187
188 let base_cases = [
189 (Uint::ZERO, Uint::ZERO, Uint::ZERO),
190 (Uint::ONE, Uint::ZERO, Uint::ONE),
191 (Uint::ZERO, Uint::ONE, minus_one),
192 (minus_one, minus_one, Uint::ZERO),
193 (Uint::ZERO, minus_one, Uint::ONE),
194 ];
195 for (a, b, c) in &base_cases {
196 let x = a.sub_mod_special(b, *special.as_ref());
197 assert_eq!(*c, x, "{} - {} mod {} = {} != {}", a, b, p, x, c);
198 }
199
200 for _i in 0..100 {
201 let a = Uint::<LIMBS>::random_mod_vartime(&mut rng, p);
202 let b = Uint::<LIMBS>::random_mod_vartime(&mut rng, p);
203
204 let c = a.sub_mod_special(&b, *special.as_ref());
205 assert!(c < **p, "not reduced: {} >= {} ", c, p);
206
207 let expected = a.sub_mod(&b, p);
208 assert_eq!(c, expected, "incorrect result");
209 }
210 }
211 }
212
213 test_size::<1>();
214 test_size::<2>();
215 test_size::<3>();
216 if cfg!(not(miri)) {
217 test_size::<4>();
218 test_size::<8>();
219 test_size::<16>();
220 }
221 }
222}