1use crate::{EuclideanError, Polynomial};
7
8pub fn euclidean(
34 syndromes: &Polynomial,
35 t: u8,
36) -> Result<(Polynomial, Polynomial), EuclideanError> {
37 let two_t = usize::from(t) * 2;
38
39 if two_t > usize::from(Polynomial::MAX_DEGREE) {
40 return Err(EuclideanError::CapabilityTooHigh { t });
41 }
42
43 let mut r: [Polynomial; 2] = [Polynomial::default(), Polynomial::default()];
45 let mut t_poly: [Polynomial; 2] = [Polynomial::default(), Polynomial::default()];
46
47 #[allow(clippy::cast_possible_truncation)]
49 r[0].set(two_t as u8, 1);
50
51 r[1] = *syndromes;
53
54 t_poly[1].set(0, 1);
57
58 let mut q = Polynomial::default();
60
61 let mut idx: usize = 1;
63
64 while r[idx].degree() >= t {
65 let (r_left, r_right) = r.split_at_mut(1);
68 let (r_prev, r_curr) = if idx == 1 {
69 (&mut r_left[0], &r_right[0])
70 } else {
71 (&mut r_right[0], &r_left[0])
72 };
73
74 let (t_left, t_right) = t_poly.split_at_mut(1);
75 let (t_prev, t_curr) = if idx == 1 {
76 (&mut t_left[0], &t_right[0])
77 } else {
78 (&mut t_right[0], &t_left[0])
79 };
80
81 r_prev.div_rem_inplace(r_curr, &mut q)?;
83
84 t_prev.mul_xor_assign(&q, t_curr)?;
86
87 idx ^= 1;
89 }
90
91 Ok((t_poly[idx], r[idx]))
93}
94
95#[cfg(test)]
96#[allow(clippy::expect_used)]
97mod tests {
98 use super::*;
99 use crate::finite_field::{inv, mul, ANTILOG_TABLE};
100
101 #[test]
102 fn euclidean_zero_syndromes() {
103 let syndromes: Polynomial = [0u8; 4].into_iter().collect();
105 let t = 2;
106
107 let (sigma, omega) = euclidean(&syndromes, t).expect("should succeed");
108
109 assert_eq!(sigma.degree(), 0);
111 assert_eq!(omega.coefficients(), &[0]);
113 }
114
115 #[test]
116 fn euclidean_single_error_sigma_has_one_root() {
117 let syndromes: Polynomial = [1u8; 4].into_iter().collect(); let t = 2;
122
123 let (sigma, _omega) = euclidean(&syndromes, t).expect("should succeed");
124
125 assert!(sigma.degree() <= 1);
127
128 let x = ANTILOG_TABLE[255 % 255].get(); let sigma_at_x = Polynomial::eval_coefficients_at(sigma.coefficients(), x);
133
134 let scale = inv(sigma[0u8]).expect("sigma[0] should be non-zero").get();
137 let _normalized_eval = mul(sigma_at_x, scale);
138
139 assert_eq!(
142 Polynomial::eval_coefficients_at(sigma.coefficients(), 1),
143 0,
144 "sigma should have a root at alpha^0 for error at position 0"
145 );
146 }
147
148 #[test]
149 fn euclidean_verifies_degree_bounds() {
150 let syndromes: Polynomial = [3u8, 5, 7, 11].into_iter().collect(); let t = 2;
155
156 let (sigma, omega) = euclidean(&syndromes, t).expect("should succeed");
157
158 assert!(
160 sigma.degree() <= t,
161 "sigma degree {} exceeds t={}",
162 sigma.degree(),
163 t
164 );
165
166 assert!(
168 omega.degree() < t || omega.is_zero(),
169 "omega degree {} should be < t={}",
170 omega.degree(),
171 t
172 );
173 }
174
175 #[test]
176 fn euclidean_with_t_equals_one() {
177 let syndromes: Polynomial = [42u8, 0].into_iter().collect(); let t = 1;
180
181 let (sigma, omega) = euclidean(&syndromes, t).expect("should succeed");
182
183 assert!(sigma.degree() <= t);
184 assert!(omega.degree() < t || omega.is_zero());
185 }
186
187 #[test]
188 fn euclidean_with_large_t() {
189 let syndromes: Polynomial = [1u8; 20].into_iter().collect(); let t = 10;
192
193 let (sigma, omega) = euclidean(&syndromes, t).expect("should succeed");
194
195 assert!(sigma.degree() <= t);
196 assert!(omega.degree() < t || omega.is_zero());
197 }
198
199 #[test]
200 fn euclidean_minimal_valid_case() {
201 let syndromes: Polynomial = [42u8, 17].into_iter().collect();
203 let t = 1;
204
205 let (sigma, omega) = euclidean(&syndromes, t).expect("should succeed");
206
207 assert!(sigma.degree() <= t);
208 assert!(omega.degree() < t || omega.is_zero());
209 }
210
211 #[test]
212 fn euclidean_syndrome_with_trailing_zeros() {
213 let syndromes: Polynomial = [1u8, 2, 0, 0].into_iter().collect(); let t = 2;
216
217 let (sigma, omega) = euclidean(&syndromes, t).expect("should succeed");
218
219 assert!(sigma.degree() <= t);
221
222 assert!(
224 omega.degree() < t || omega.is_zero(),
225 "omega degree {} should be < t={}",
226 omega.degree(),
227 t
228 );
229 }
230
231 #[test]
232 fn euclidean_rejects_capability_above_127() {
233 let syndromes: Polynomial = [1u8; 4].into_iter().collect();
236
237 for t in [128, 200, 255] {
238 assert_eq!(
239 euclidean(&syndromes, t),
240 Err(EuclideanError::CapabilityTooHigh { t })
241 );
242 }
243 }
244
245 #[test]
246 fn euclidean_max_valid_length() {
247 let syndromes: Polynomial = [0u8; 255].into_iter().collect();
249 let t = 127;
250
251 let result = euclidean(&syndromes, t);
252
253 assert!(result.is_ok());
254 }
255
256 #[test]
257 fn euclidean_output_is_deterministic() {
258 let syndromes: Polynomial = [7u8, 13, 19, 23, 29, 31].into_iter().collect();
260 let t = 3;
261
262 let (sigma1, omega1) = euclidean(&syndromes, t).expect("should succeed");
263 let (sigma2, omega2) = euclidean(&syndromes, t).expect("should succeed");
264
265 assert_eq!(sigma1, sigma2);
266 assert_eq!(omega1, omega2);
267 }
268
269 #[test]
270 fn euclidean_integration_with_rs_syndromes() {
271 use crate::ReedSolomon;
274
275 let rs = ReedSolomon::new(2).expect("valid RS");
276
277 let message = b"test";
279 let mut encoded = rs.encode(message).expect("encoding succeeds");
280
281 encoded[0] ^= 0x42; let syndromes = ReedSolomon::compute_syndromes(rs.parity_bytes(), &encoded);
286
287 let t = rs.parity_bytes() / 2;
289 let (sigma, omega) = euclidean(&syndromes, t).expect("euclidean succeeds");
290
291 assert!(sigma.degree() <= t, "sigma degree should be <= t");
293
294 assert!(
296 omega.degree() < t || omega.is_zero(),
297 "omega degree {} should be < t={}",
298 omega.degree(),
299 t
300 );
301 }
302}