ps_ecc/polynomial/methods/
div_rem.rs1use crate::{
2 error::PolynomialDivError,
3 finite_field::{inv, mul, sub},
4 Polynomial,
5};
6
7impl Polynomial {
8 pub fn div_rem(&self, divisor: impl AsRef<[u8]>) -> Result<(Self, Self), PolynomialDivError> {
17 let divisor = divisor.as_ref();
18
19 let Some(divisor_degree) = divisor.iter().rposition(|&c| c != 0) else {
20 return Err(PolynomialDivError::ZeroDivisor);
21 };
22
23 let divisor_lc_inv = inv(divisor[divisor_degree])?.get();
24
25 let Some(dividend_degree) = self.coefficients().iter().rposition(|&c| c != 0) else {
26 return Ok((Self::default(), Self::default()));
27 };
28
29 if dividend_degree < divisor_degree {
30 return Ok((Self::default(), *self));
31 }
32
33 let quot_degree = dividend_degree - divisor_degree;
34
35 #[allow(clippy::cast_possible_truncation)]
36 let mut quot = Self {
37 degree: quot_degree as u8,
38 ..Self::default()
39 };
40
41 let mut rem = *self;
42 let mut deg = dividend_degree;
43
44 while deg >= divisor_degree {
45 let lead_coef = rem.coefficients[deg];
46
47 if lead_coef == 0 {
48 if deg == 0 {
49 break;
50 }
51 deg -= 1;
52 continue;
53 }
54
55 let ratio = mul(lead_coef, divisor_lc_inv);
56 let shift = deg - divisor_degree;
57
58 quot.coefficients[shift] = ratio;
59 rem.coefficients[deg] = 0;
60
61 for (i, &divisor_coef) in divisor[..divisor_degree].iter().enumerate() {
62 rem.coefficients[shift + i] =
63 sub(rem.coefficients[shift + i], mul(ratio, divisor_coef));
64 }
65
66 if deg == 0 {
67 break;
68 }
69 deg -= 1;
70 }
71
72 quot.trim_degree();
73 rem.trim_degree();
74
75 Ok((quot, rem))
76 }
77}
78
79#[cfg(test)]
80#[allow(clippy::expect_used)]
81mod tests {
82 use std::ops::Mul;
83
84 use crate::{error::PolynomialDivError, Polynomial};
85
86 #[test]
87 fn div_rem_basic() {
88 let a = Polynomial::try_from(&[1u8, 0, 1][..]).expect("valid polynomial");
92
93 let (quot, rem) = a.div_rem([1, 1]).expect("division succeeds");
94
95 assert_eq!(quot.coefficients(), &[1, 1]);
96 assert_eq!(rem.coefficients(), &[0]);
97 }
98
99 #[test]
100 fn div_rem_by_one() {
101 let a = Polynomial::try_from(&[5u8, 10, 15][..]).expect("valid polynomial");
102
103 let (quot, rem) = a.div_rem([1]).expect("division succeeds");
104
105 assert_eq!(quot.coefficients(), &[5, 10, 15]);
106 assert_eq!(rem.coefficients(), &[0]);
107 }
108
109 #[test]
110 fn div_rem_by_zero() {
111 let a = Polynomial::try_from(&[1u8, 2, 3][..]).expect("valid polynomial");
112
113 let result = a.div_rem([0]);
114
115 assert_eq!(result, Err(PolynomialDivError::ZeroDivisor));
116 }
117
118 #[test]
119 fn div_rem_by_empty_slice() {
120 let a = Polynomial::try_from(&[1u8, 2, 3][..]).expect("valid polynomial");
121
122 let result = a.div_rem([]);
123
124 assert_eq!(result, Err(PolynomialDivError::ZeroDivisor));
125 }
126
127 #[test]
128 fn div_rem_zero_by_nonzero() {
129 let zero = Polynomial::default();
130
131 let (quot, rem) = zero.div_rem([1, 2]).expect("division succeeds");
132
133 assert_eq!(quot.degree(), 0);
134 assert_eq!(quot.coefficients(), &[0]);
135 assert_eq!(rem.coefficients(), &[0]);
136 }
137
138 #[test]
139 fn div_rem_smaller_by_larger() {
140 let a = Polynomial::try_from(&[1u8, 2][..]).expect("valid polynomial");
142
143 let (quot, rem) = a.div_rem([1, 2, 3]).expect("division succeeds");
144
145 assert_eq!(quot.degree(), 0);
146 assert_eq!(quot.coefficients(), &[0]);
147 assert_eq!(rem.coefficients(), &[1, 2]);
148 }
149
150 #[test]
151 fn div_rem_mul_roundtrip() {
152 let a = Polynomial::try_from(&[3u8, 5, 7][..]).expect("valid polynomial");
154 let b: &[u8] = &[2, 4];
155
156 let product = (&a).mul(b).expect("multiplication succeeds");
157 let (quot, rem) = product.div_rem(b).expect("division succeeds");
158
159 assert_eq!(quot, a);
160 assert_eq!(rem.coefficients(), &[0]);
161 }
162
163 #[test]
164 fn div_rem_with_remainder() {
165 let a = Polynomial::try_from(&[1u8, 1, 1][..]).expect("valid polynomial");
167 let b: &[u8] = &[1, 1];
168
169 let (quot, rem) = a.div_rem(b).expect("division succeeds");
170
171 let product = quot.mul(b).expect("multiplication succeeds");
173 let reconstructed = (product + rem).expect("addition succeeds");
174
175 assert_eq!(reconstructed, a);
176 }
177
178 #[test]
179 fn div_rem_by_constant() {
180 let a = Polynomial::try_from(&[6u8, 12, 18][..]).expect("valid polynomial");
182
183 let (quot, rem) = a.div_rem([2]).expect("division succeeds");
184
185 assert_eq!(rem.coefficients(), &[0]);
186
187 let check = quot.mul([2u8]).expect("multiplication succeeds");
190
191 assert_eq!(check.coefficients(), &[6, 12, 18]);
192 }
193
194 #[test]
195 fn div_rem_with_polynomial() {
196 let a = Polynomial::try_from(&[1u8, 0, 1][..]).expect("valid polynomial");
198 let b = Polynomial::try_from(&[1u8, 1][..]).expect("valid polynomial");
199
200 let (quot, rem) = a.div_rem(b).expect("division succeeds");
201
202 assert_eq!(quot.coefficients(), &[1, 1]);
203 assert_eq!(rem.coefficients(), &[0]);
204 }
205
206 #[test]
207 fn div_rem_trailing_zeros_ignored() {
208 let a = Polynomial::try_from(&[1u8, 0, 1][..]).expect("valid polynomial");
210
211 let (quot1, rem1) = a.div_rem([1, 1]).expect("division succeeds");
212 let (quot2, rem2) = a.div_rem([1, 1, 0, 0, 0]).expect("division succeeds");
213
214 assert_eq!(quot1, quot2);
215 assert_eq!(rem1, rem2);
216 }
217
218 #[test]
219 fn div_rem_same_degree() {
220 let a = Polynomial::try_from(&[3u8, 5, 7][..]).expect("valid polynomial");
222 let b = Polynomial::try_from(&[2u8, 4, 6][..]).expect("valid polynomial");
223
224 let (quot, rem) = a.div_rem(b).expect("division succeeds");
225
226 assert_eq!(quot.degree(), 0);
228
229 let product = quot.mul(&b).expect("multiplication succeeds");
231 let reconstructed = (product + rem).expect("addition succeeds");
232
233 assert_eq!(reconstructed, a);
234 }
235
236 #[test]
237 fn div_rem_self_division() {
238 let a = Polynomial::try_from(&[3u8, 5, 7, 11][..]).expect("valid polynomial");
240
241 let (quot, rem) = a.div_rem(a).expect("division succeeds");
242
243 assert_eq!(quot.coefficients(), &[1]);
244 assert_eq!(rem.coefficients(), &[0]);
245 }
246
247 #[test]
248 fn div_rem_divisor_with_internal_zeros() {
249 let a = Polynomial::try_from(&[1u8, 2, 3, 4, 5][..]).expect("valid polynomial");
251 let b: &[u8] = &[1, 0, 0, 1];
252
253 let (quot, rem) = a.div_rem(b).expect("division succeeds");
254
255 let product = quot.mul(b).expect("multiplication succeeds");
257 let reconstructed = (product + rem).expect("addition succeeds");
258
259 assert_eq!(reconstructed, a);
260 }
261
262 #[test]
263 fn div_rem_high_degree() {
264 let mut a_coeffs = [0u8; 100];
266 let mut b_coeffs = [0u8; 30];
267
268 for (i, c) in a_coeffs.iter_mut().enumerate() {
269 *c = u8::try_from(i + 1).expect("index fits in u8");
270 }
271 for (i, c) in b_coeffs.iter_mut().enumerate() {
272 *c = u8::try_from(i + 5).expect("index fits in u8");
273 }
274
275 let a = Polynomial::try_from(&a_coeffs[..]).expect("valid polynomial");
276 let b = Polynomial::try_from(&b_coeffs[..]).expect("valid polynomial");
277
278 let (quot, rem) = a.div_rem(b).expect("division succeeds");
279
280 let product = quot.mul(&b).expect("multiplication succeeds");
282 let reconstructed = (product + rem).expect("addition succeeds");
283
284 assert_eq!(reconstructed, a);
285 }
286}