feanor_math/algorithms/poly_gcd/
mod.rs1use gcd::poly_gcd_local;
2use finite::poly_power_decomposition_finite_field;
3use local::PolyGCDLocallyDomain;
4use squarefree_part::poly_power_decomposition_local;
5
6use crate::computation::DontObserve;
7use crate::divisibility::*;
8use crate::homomorphism::*;
9use crate::integer::*;
10use crate::pid::*;
11use crate::rings::field::*;
12use crate::ring::*;
13use crate::delegate::DelegateRing;
14use crate::rings::poly::dense_poly::*;
15use crate::rings::poly::*;
16use crate::rings::finite::*;
17use crate::field::*;
18use crate::specialization::FiniteRingOperation;
19
20use super::eea::gcd;
21
22pub mod finite;
23pub mod local;
24pub mod hensel;
25pub mod squarefree_part;
26pub mod gcd;
27pub mod factor;
28
29const INCREASE_EXPONENT_PER_ATTEMPT_CONSTANT: f64 = 1.5;
30
31pub trait PolyTFracGCDRing {
68
69 fn squarefree_part<P>(poly_ring: P, poly: &El<P>) -> El<P>
90 where P: RingStore + Copy,
91 P::Type: PolyRing + DivisibilityRing,
92 <P::Type as RingExtension>::BaseRing: RingStore<Type = Self>
93 {
94 poly_ring.prod(Self::power_decomposition(poly_ring, poly).into_iter().map(|(f, _)| f))
95 }
96
97 fn power_decomposition<P>(poly_ring: P, poly: &El<P>) -> Vec<(El<P>, usize)>
106 where P: RingStore + Copy,
107 P::Type: PolyRing + DivisibilityRing,
108 <P::Type as RingExtension>::BaseRing: RingStore<Type = Self>;
109
110 fn gcd<P>(poly_ring: P, lhs: &El<P>, rhs: &El<P>) -> El<P>
137 where P: RingStore + Copy,
138 P::Type: PolyRing + DivisibilityRing,
139 <P::Type as RingExtension>::BaseRing: RingStore<Type = Self>;
140}
141
142fn evaluate_aX<P>(poly_ring: P, f: &El<P>, a: &El<<P::Type as RingExtension>::BaseRing>) -> El<P>
150 where P: RingStore,
151 P::Type: PolyRing,
152 <<P::Type as RingExtension>::BaseRing as RingStore>::Type: DivisibilityRing + Domain
153{
154 if poly_ring.is_zero(f) {
155 return poly_ring.zero();
156 }
157 let ring = poly_ring.base_ring();
158 let d = poly_ring.degree(&f).unwrap();
159 let result = poly_ring.from_terms(poly_ring.terms(f).map(|(c, i)| if i == d { (ring.checked_div(c, a).unwrap(), d) } else { (ring.mul_ref_fst(c, ring.pow(ring.clone_el(a), d - i - 1)), i) }));
160 return result;
161}
162
163fn unevaluate_aX<P>(poly_ring: P, g: &El<P>, a: &El<<P::Type as RingExtension>::BaseRing>) -> El<P>
167 where P: RingStore,
168 P::Type: PolyRing,
169 <<P::Type as RingExtension>::BaseRing as RingStore>::Type: DivisibilityRing + Domain
170{
171 if poly_ring.is_zero(g) {
172 return poly_ring.zero();
173 }
174 let ring = poly_ring.base_ring();
175 let d = poly_ring.degree(&g).unwrap();
176 let result = poly_ring.from_terms(poly_ring.terms(g).map(|(c, i)| if i == d { (ring.clone_el(a), d) } else { (ring.checked_div(c, &ring.pow(ring.clone_el(a), d - i - 1)).unwrap(), i) }));
177 return result;
178}
179
180#[stability::unstable(feature = "enable")]
185pub fn make_primitive<P>(poly_ring: P, f: &El<P>) -> (El<P>, El<<P::Type as RingExtension>::BaseRing>)
186 where P: RingStore,
187 P::Type: PolyRing,
188 <<P::Type as RingExtension>::BaseRing as RingStore>::Type: PrincipalIdealRing + Domain
189{
190 if poly_ring.is_zero(f) {
191 return (poly_ring.zero(), poly_ring.base_ring().one());
192 }
193 let ring = poly_ring.base_ring();
194 let content = poly_ring.terms(f).map(|(c, _)| c).fold(ring.zero(), |a, b| ring.ideal_gen(&a, b));
195 let result = poly_ring.from_terms(poly_ring.terms(f).map(|(c, i)| (ring.checked_div(c, &content).unwrap(), i)));
196 return (result, content);
197}
198
199#[stability::unstable(feature = "enable")]
217pub fn poly_root<P>(poly_ring: P, f: &El<P>, k: usize) -> Option<El<P>>
218 where P: RingStore,
219 P::Type: PolyRing,
220 <<P::Type as RingExtension>::BaseRing as RingStore>::Type: DivisibilityRing + Domain
221{
222 assert!(poly_ring.degree(&f).unwrap() % k == 0);
223 let d = poly_ring.degree(&f).unwrap() / k;
224 let ring = poly_ring.base_ring();
225 let k_in_ring = ring.int_hom().map(k as i32);
226
227 let mut result_reversed = Vec::new();
228 result_reversed.push(ring.one());
229 for i in 1..=d {
230 let g = poly_ring.pow(poly_ring.from_terms((0..i).map(|j| (ring.clone_el(&result_reversed[j]), j))), k);
231 let partition_sum = poly_ring.coefficient_at(&g, i);
232 let next_coeff = ring.checked_div(&ring.sub_ref(poly_ring.coefficient_at(&f, k * d - i), partition_sum), &k_in_ring)?;
233 result_reversed.push(next_coeff);
234 }
235
236 let result = poly_ring.from_terms(result_reversed.into_iter().enumerate().map(|(i, c)| (c, d - i)));
237 if poly_ring.eq_el(&f, &poly_ring.pow(poly_ring.clone_el(&result), k)) {
238 return Some(result);
239 } else {
240 return None;
241 }
242}
243
244
245impl<R> PolyTFracGCDRing for R
246 where R: ?Sized + PolyGCDLocallyDomain + SelfIso
247{
248 default fn power_decomposition<P>(poly_ring: P, poly: &El<P>) -> Vec<(El<P>, usize)>
249 where P: RingStore + Copy,
250 P::Type: PolyRing + DivisibilityRing,
251 <P::Type as RingExtension>::BaseRing: RingStore<Type = Self>
252 {
253 struct PowerDecompositionIfFiniteField<'a, P>(P, &'a El<P>)
254 where P: RingStore + Copy,
255 P::Type: PolyRing,
256 <<P::Type as RingExtension>::BaseRing as RingStore>::Type: DivisibilityRing + SelfIso;
257
258 impl<'a, P> FiniteRingOperation<<<P::Type as RingExtension>::BaseRing as RingStore>::Type> for PowerDecompositionIfFiniteField<'a, P>
259 where P: RingStore + Copy,
260 P::Type: PolyRing,
261 <<P::Type as RingExtension>::BaseRing as RingStore>::Type: DivisibilityRing + SelfIso
262 {
263 type Output = Vec<(El<P>, usize)>;
264
265 fn execute(self) -> Vec<(El<P>, usize)>
266 where <<P::Type as RingExtension>::BaseRing as RingStore>::Type: FiniteRing
267 {
268 static_assert_impls!(<<P::Type as RingExtension>::BaseRing as RingStore>::Type: SelfIso);
269
270 let new_poly_ring = DensePolyRing::new(AsField::from(AsFieldBase::promise_is_perfect_field(self.0.base_ring())), "X");
271 let new_poly = new_poly_ring.from_terms(self.0.terms(&self.1).map(|(c, i)| (new_poly_ring.base_ring().get_ring().rev_delegate(self.0.base_ring().clone_el(c)), i)));
272 poly_power_decomposition_finite_field(&new_poly_ring, &new_poly).into_iter().map(|(f, k)|
273 (self.0.from_terms(new_poly_ring.terms(&f).map(|(c, i)| (new_poly_ring.base_ring().get_ring().unwrap_element(new_poly_ring.base_ring().clone_el(c)), i))), k)
274 ).collect()
275 }
276 }
277
278 if let Ok(result) = R::specialize(PowerDecompositionIfFiniteField(poly_ring, poly)) {
279 return result;
280 } else {
281 poly_power_decomposition_local(poly_ring, poly_ring.clone_el(poly), DontObserve)
282 }
283 }
284
285 default fn gcd<P>(poly_ring: P, lhs: &El<P>, rhs: &El<P>) -> El<P>
286 where P: RingStore + Copy,
287 P::Type: PolyRing + DivisibilityRing,
288 <P::Type as RingExtension>::BaseRing: RingStore<Type = Self>
289 {
290 struct PolyGCDIfFiniteField<'a, P>(P, &'a El<P>, &'a El<P>)
291 where P: RingStore + Copy,
292 P::Type: PolyRing,
293 <<P::Type as RingExtension>::BaseRing as RingStore>::Type: DivisibilityRing + SelfIso;
294
295 impl<'a, P> FiniteRingOperation<<<P::Type as RingExtension>::BaseRing as RingStore>::Type> for PolyGCDIfFiniteField<'a, P>
296 where P: RingStore + Copy,
297 P::Type: PolyRing,
298 <<P::Type as RingExtension>::BaseRing as RingStore>::Type: DivisibilityRing + SelfIso
299 {
300 type Output = El<P>;
301
302 fn execute(self) -> El<P>
303 where <<P::Type as RingExtension>::BaseRing as RingStore>::Type: FiniteRing
304 {
305 static_assert_impls!(<<P::Type as RingExtension>::BaseRing as RingStore>::Type: SelfIso);
306
307 let new_poly_ring = DensePolyRing::new(AsField::from(AsFieldBase::promise_is_perfect_field(self.0.base_ring())), "X");
308 let new_lhs = new_poly_ring.from_terms(self.0.terms(&self.1).map(|(c, i)| (new_poly_ring.base_ring().get_ring().rev_delegate(self.0.base_ring().clone_el(c)), i)));
309 let new_rhs = new_poly_ring.from_terms(self.0.terms(&self.2).map(|(c, i)| (new_poly_ring.base_ring().get_ring().rev_delegate(self.0.base_ring().clone_el(c)), i)));
310 let result = gcd(new_lhs, new_rhs, &new_poly_ring);
311 return self.0.from_terms(new_poly_ring.terms(&result).map(|(c, i)| (new_poly_ring.base_ring().get_ring().unwrap_element(new_poly_ring.base_ring().clone_el(c)), i)));
312 }
313 }
314
315 if let Ok(result) = R::specialize(PolyGCDIfFiniteField(poly_ring, lhs, rhs)) {
316 return result;
317 } else {
318 poly_gcd_local(poly_ring, poly_ring.clone_el(lhs), poly_ring.clone_el(rhs), DontObserve)
319 }
320 }
321}
322
323#[test]
324fn test_poly_root() {
325 let ring = BigIntRing::RING;
326 let poly_ring = DensePolyRing::new(ring, "X");
327 let [f] = poly_ring.with_wrapped_indeterminate(|X| [X.pow_ref(7) + X.pow_ref(6) + X.pow_ref(5) + X.pow_ref(4) + X.pow_ref(3) + X.pow_ref(2) + X + 1]);
328 for k in 1..5 {
329 assert_el_eq!(&poly_ring, &f, poly_root(&poly_ring, &poly_ring.pow(poly_ring.clone_el(&f), k), k).unwrap());
330 }
331
332 let [f] = poly_ring.with_wrapped_indeterminate(|X| [X.pow_ref(5) + 2 * X.pow_ref(4) + 3 * X.pow_ref(3) + 4 * X.pow_ref(2) + 5 * X + 6]);
333 for k in 1..5 {
334 assert_el_eq!(&poly_ring, &f, poly_root(&poly_ring, &poly_ring.pow(poly_ring.clone_el(&f), k), k).unwrap());
335 }
336}
337
338#[cfg(test)]
339use crate::rings::extension::galois_field::GaloisField;
340#[cfg(test)]
341use crate::rings::zn::zn_64;
342#[cfg(test)]
343use crate::rings::zn::ZnRingStore;
344
345#[test]
346fn test_poly_gcd_galois_field() {
347 let field = GaloisField::new(5, 3);
348 let poly_ring = DensePolyRing::new(&field, "X");
349 let [f, g, f_g_gcd] = poly_ring.with_wrapped_indeterminate(|X| [(X.pow_ref(2) + 2) * (X.pow_ref(5) + 1), (X.pow_ref(2) + 2) * (X + 1) * (X + 2), (X.pow_ref(2) + 2) * (X + 1)]);
350 assert_el_eq!(&poly_ring, &f_g_gcd, <_ as PolyTFracGCDRing>::gcd(&poly_ring, &f, &g));
351}
352
353#[test]
354fn test_poly_gcd_prime_field() {
355 let field = zn_64::Zn::new(5).as_field().ok().unwrap();
356 let poly_ring = DensePolyRing::new(&field, "X");
357 let [f, g, f_g_gcd] = poly_ring.with_wrapped_indeterminate(|X| [(X.pow_ref(2) + 2) * (X.pow_ref(5) + 1), (X.pow_ref(2) + 2) * (X + 1) * (X + 2), (X.pow_ref(2) + 2) * (X + 1)]);
358 assert_el_eq!(&poly_ring, &f_g_gcd, <_ as PolyTFracGCDRing>::gcd(&poly_ring, &f, &g));
359}