1use gcd::poly_gcd_local;
2use finite::{poly_power_decomposition_finite_field, fast_poly_eea};
3use squarefree_part::poly_power_decomposition_local;
4
5use crate::computation::*;
6use crate::divisibility::*;
7use crate::reduce_lift::poly_factor_gcd::*;
8use crate::homomorphism::*;
9use crate::pid::*;
10use crate::rings::field::*;
11use crate::ring::*;
12use crate::delegate::DelegateRing;
13use crate::rings::poly::dense_poly::*;
14use crate::rings::poly::*;
15use crate::rings::finite::*;
16use crate::specialization::FiniteRingOperation;
17
18pub mod finite;
22pub mod hensel;
27pub mod squarefree_part;
32pub mod gcd;
36
37const INCREASE_EXPONENT_PER_ATTEMPT_CONSTANT: f64 = 1.5;
38
39pub trait PolyTFracGCDRing {
76
77 fn squarefree_part<P>(poly_ring: P, poly: &El<P>) -> El<P>
98 where P: RingStore + Copy,
99 P::Type: PolyRing + DivisibilityRing,
100 <P::Type as RingExtension>::BaseRing: RingStore<Type = Self>
101 {
102 poly_ring.prod(Self::power_decomposition(poly_ring, poly).into_iter().map(|(f, _)| f))
103 }
104
105 fn power_decomposition<P>(poly_ring: P, poly: &El<P>) -> Vec<(El<P>, usize)>
114 where P: RingStore + Copy,
115 P::Type: PolyRing + DivisibilityRing,
116 <P::Type as RingExtension>::BaseRing: RingStore<Type = Self>;
117
118 fn power_decomposition_with_controller<P, Controller>(poly_ring: P, poly: &El<P>, _: Controller) -> Vec<(El<P>, usize)>
124 where P: RingStore + Copy,
125 P::Type: PolyRing + DivisibilityRing,
126 <P::Type as RingExtension>::BaseRing: RingStore<Type = Self>,
127 Controller: ComputationController
128 {
129 Self::power_decomposition(poly_ring, poly)
130 }
131
132 fn gcd<P>(poly_ring: P, lhs: &El<P>, rhs: &El<P>) -> El<P>
159 where P: RingStore + Copy,
160 P::Type: PolyRing + DivisibilityRing,
161 <P::Type as RingExtension>::BaseRing: RingStore<Type = Self>;
162
163 fn gcd_with_controller<P, Controller>(poly_ring: P, lhs: &El<P>, rhs: &El<P>, _: Controller) -> El<P>
169 where P: RingStore + Copy,
170 P::Type: PolyRing + DivisibilityRing,
171 <P::Type as RingExtension>::BaseRing: RingStore<Type = Self>,
172 Controller: ComputationController
173 {
174 Self::gcd(poly_ring, lhs, rhs)
175 }
176}
177
178#[stability::unstable(feature = "enable")]
186pub fn evaluate_aX<P>(poly_ring: P, f: &El<P>, a: &El<<P::Type as RingExtension>::BaseRing>) -> El<P>
187 where P: RingStore,
188 P::Type: PolyRing,
189 <<P::Type as RingExtension>::BaseRing as RingStore>::Type: DivisibilityRing + Domain
190{
191 if poly_ring.is_zero(f) {
192 return poly_ring.zero();
193 }
194 let ring = poly_ring.base_ring();
195 let d = poly_ring.degree(&f).unwrap();
196 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) }));
197 return result;
198}
199
200#[stability::unstable(feature = "enable")]
204pub fn unevaluate_aX<P>(poly_ring: P, g: &El<P>, a: &El<<P::Type as RingExtension>::BaseRing>) -> El<P>
205 where P: RingStore,
206 P::Type: PolyRing,
207 <<P::Type as RingExtension>::BaseRing as RingStore>::Type: DivisibilityRing + Domain
208{
209 if poly_ring.is_zero(g) {
210 return poly_ring.zero();
211 }
212 let ring = poly_ring.base_ring();
213 let d = poly_ring.degree(&g).unwrap();
214 let result = poly_ring.from_terms(poly_ring.terms(g).map(|(c, i)| if i == d { (ring.mul_ref(c, a), d) } else { (ring.checked_div(c, &ring.pow(ring.clone_el(a), d - i - 1)).unwrap(), i) }));
215 return result;
216}
217
218#[stability::unstable(feature = "enable")]
223pub fn make_primitive<P>(poly_ring: P, f: &El<P>) -> (El<P>, El<<P::Type as RingExtension>::BaseRing>)
224 where P: RingStore,
225 P::Type: PolyRing,
226 <<P::Type as RingExtension>::BaseRing as RingStore>::Type: PrincipalIdealRing + Domain
227{
228 if poly_ring.is_zero(f) {
229 return (poly_ring.zero(), poly_ring.base_ring().one());
230 }
231 let ring = poly_ring.base_ring();
232 let content = poly_ring.terms(f).map(|(c, _)| c).fold(ring.zero(), |a, b| ring.ideal_gen(&a, b));
233 let result = poly_ring.from_terms(poly_ring.terms(f).map(|(c, i)| (ring.checked_div(c, &content).unwrap(), i)));
234 return (result, content);
235}
236
237#[stability::unstable(feature = "enable")]
255pub fn poly_root<P>(poly_ring: P, f: &El<P>, k: usize) -> Option<El<P>>
256 where P: RingStore,
257 P::Type: PolyRing,
258 <<P::Type as RingExtension>::BaseRing as RingStore>::Type: DivisibilityRing + Domain
259{
260 assert!(poly_ring.degree(&f).unwrap() % k == 0);
261 let d = poly_ring.degree(&f).unwrap() / k;
262 let ring = poly_ring.base_ring();
263 let k_in_ring = ring.int_hom().map(k.try_into().unwrap());
264
265 let mut result_reversed = Vec::new();
266 result_reversed.push(ring.one());
267 for i in 1..=d {
268 let g = poly_ring.pow(poly_ring.from_terms((0..i).map(|j| (ring.clone_el(&result_reversed[j]), j))), k);
269 let partition_sum = poly_ring.coefficient_at(&g, i);
270 let next_coeff = ring.checked_div(&ring.sub_ref(poly_ring.coefficient_at(&f, k * d - i), partition_sum), &k_in_ring)?;
271 result_reversed.push(next_coeff);
272 }
273
274 let result = poly_ring.from_terms(result_reversed.into_iter().enumerate().map(|(i, c)| (c, d - i)));
275 if poly_ring.eq_el(&f, &poly_ring.pow(poly_ring.clone_el(&result), k)) {
276 return Some(result);
277 } else {
278 return None;
279 }
280}
281
282
283impl<R> PolyTFracGCDRing for R
284 where R: ?Sized + PolyGCDLocallyDomain + SelfIso
285{
286 default fn power_decomposition<P>(poly_ring: P, poly: &El<P>) -> Vec<(El<P>, usize)>
287 where P: RingStore + Copy,
288 P::Type: PolyRing + DivisibilityRing,
289 <P::Type as RingExtension>::BaseRing: RingStore<Type = Self>
290 {
291 Self::power_decomposition_with_controller(poly_ring, poly, DontObserve)
292 }
293
294 default fn power_decomposition_with_controller<P, Controller>(poly_ring: P, poly: &El<P>, controller: Controller) -> Vec<(El<P>, usize)>
295 where P: RingStore + Copy,
296 P::Type: PolyRing + DivisibilityRing,
297 <P::Type as RingExtension>::BaseRing: RingStore<Type = Self>,
298 Controller: ComputationController
299 {
300 struct PowerDecompositionOperation<'a, P, Controller>(P, &'a El<P>, Controller)
301 where P: RingStore + Copy,
302 P::Type: PolyRing,
303 <<P::Type as RingExtension>::BaseRing as RingStore>::Type: DivisibilityRing + SelfIso,
304 Controller: ComputationController;
305
306 impl<'a, P, Controller> FiniteRingOperation<<<P::Type as RingExtension>::BaseRing as RingStore>::Type> for PowerDecompositionOperation<'a, P, Controller>
307 where P: RingStore + Copy,
308 P::Type: PolyRing + DivisibilityRing,
309 <<P::Type as RingExtension>::BaseRing as RingStore>::Type: PolyGCDLocallyDomain + DivisibilityRing + SelfIso,
310 Controller: ComputationController
311 {
312 type Output = Vec<(El<P>, usize)>;
313
314 fn execute(self) -> Vec<(El<P>, usize)>
315 where <<P::Type as RingExtension>::BaseRing as RingStore>::Type: FiniteRing
316 {
317 static_assert_impls!(<<P::Type as RingExtension>::BaseRing as RingStore>::Type: SelfIso);
318
319 let new_poly_ring = DensePolyRing::new(AsField::from(AsFieldBase::promise_is_perfect_field(self.0.base_ring())), "X");
320 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)));
321 poly_power_decomposition_finite_field(&new_poly_ring, &new_poly, self.2).into_iter().map(|(f, k)|
322 (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)
323 ).collect()
324 }
325
326 fn fallback(self) -> Self::Output {
327 let poly_ring = self.0;
328 poly_power_decomposition_local(poly_ring, poly_ring.clone_el(self.1), self.2)
329 }
330 }
331
332 R::specialize(PowerDecompositionOperation(poly_ring, poly, controller))
333 }
334
335 default fn gcd<P>(poly_ring: P, lhs: &El<P>, rhs: &El<P>) -> El<P>
336 where P: RingStore + Copy,
337 P::Type: PolyRing + DivisibilityRing,
338 <P::Type as RingExtension>::BaseRing: RingStore<Type = Self>
339 {
340 Self::gcd_with_controller(poly_ring, lhs, rhs, DontObserve)
341 }
342
343 fn gcd_with_controller<P, Controller>(poly_ring: P, lhs: &El<P>, rhs: &El<P>, controller: Controller) -> El<P>
344 where P: RingStore + Copy,
345 P::Type: PolyRing + DivisibilityRing,
346 <P::Type as RingExtension>::BaseRing: RingStore<Type = Self>,
347 Controller: ComputationController
348 {
349 struct PolyGCDOperation<'a, P, Controller>(P, &'a El<P>, &'a El<P>, Controller)
350 where P: RingStore + Copy,
351 P::Type: PolyRing,
352 <<P::Type as RingExtension>::BaseRing as RingStore>::Type: DivisibilityRing + SelfIso,
353 Controller: ComputationController;
354
355 impl<'a, P, Controller> FiniteRingOperation<<<P::Type as RingExtension>::BaseRing as RingStore>::Type> for PolyGCDOperation<'a, P, Controller>
356 where P: RingStore + Copy,
357 P::Type: PolyRing + DivisibilityRing,
358 <<P::Type as RingExtension>::BaseRing as RingStore>::Type: PolyGCDLocallyDomain + DivisibilityRing + SelfIso,
359 Controller: ComputationController
360 {
361 type Output = El<P>;
362
363 fn execute(self) -> El<P>
364 where <<P::Type as RingExtension>::BaseRing as RingStore>::Type: FiniteRing
365 {
366 static_assert_impls!(<<P::Type as RingExtension>::BaseRing as RingStore>::Type: SelfIso);
367
368 let new_poly_ring = DensePolyRing::new(AsField::from(AsFieldBase::promise_is_perfect_field(self.0.base_ring())), "X");
369 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)));
370 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)));
371 let result = new_poly_ring.normalize(fast_poly_eea(&new_poly_ring, new_lhs, new_rhs, self.3).2);
372 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)));
373 }
374
375 fn fallback(self) -> Self::Output {
376 let poly_ring = self.0;
377 poly_gcd_local(poly_ring, poly_ring.clone_el(self.1), poly_ring.clone_el(self.2), self.3)
378 }
379 }
380
381 R::specialize(PolyGCDOperation(poly_ring, lhs, rhs, controller))
382 }
383}
384
385#[cfg(test)]
386use crate::rings::extension::galois_field::GaloisField;
387#[cfg(test)]
388use crate::rings::zn::zn_64;
389#[cfg(test)]
390use crate::rings::zn::ZnRingStore;
391#[cfg(test)]
392use crate::integer::*;
393
394#[test]
395fn test_poly_root() {
396 let ring = BigIntRing::RING;
397 let poly_ring = DensePolyRing::new(ring, "X");
398 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]);
399 for k in 1..5 {
400 assert_el_eq!(&poly_ring, &f, poly_root(&poly_ring, &poly_ring.pow(poly_ring.clone_el(&f), k), k).unwrap());
401 }
402
403 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]);
404 for k in 1..5 {
405 assert_el_eq!(&poly_ring, &f, poly_root(&poly_ring, &poly_ring.pow(poly_ring.clone_el(&f), k), k).unwrap());
406 }
407}
408
409#[test]
410fn test_poly_gcd_galois_field() {
411 let field = GaloisField::new(5, 3);
412 let poly_ring = DensePolyRing::new(&field, "X");
413 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)]);
414 assert_el_eq!(&poly_ring, &f_g_gcd, <_ as PolyTFracGCDRing>::gcd(&poly_ring, &f, &g));
415}
416
417#[test]
418fn test_poly_gcd_prime_field() {
419 let field = zn_64::Zn::new(5).as_field().ok().unwrap();
420 let poly_ring = DensePolyRing::new(&field, "X");
421 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)]);
422 assert_el_eq!(&poly_ring, &f_g_gcd, <_ as PolyTFracGCDRing>::gcd(&poly_ring, &f, &g));
423}