1
2use std::mem::swap;
3
4use crate::delegate::{UnwrapHom, WrapHom};
5use crate::reduce_lift::poly_eval::{EvalPolyLocallyRing, EvaluatePolyLocallyReductionMap};
6use crate::divisibility::{DivisibilityRingStore, Domain};
7use crate::pid::{EuclideanRing, PrincipalIdealRing, PrincipalIdealRingStore};
8use crate::algorithms::eea::signed_lcm;
9use crate::rings::field::{AsField, AsFieldBase};
10use crate::rings::fraction::FractionFieldStore;
11use crate::rings::poly::*;
12use crate::rings::finite::*;
13use crate::pid::EuclideanRingStore;
14use crate::specialization::FiniteRingOperation;
15use crate::algorithms;
16use crate::integer::*;
17use crate::rings::rational::*;
18use crate::homomorphism::*;
19use crate::ring::*;
20use crate::rings::poly::dense_poly::DensePolyRing;
21
22#[deprecated]
54pub fn resultant_global<P>(ring: P, mut f: El<P>, mut g: El<P>) -> El<<P::Type as RingExtension>::BaseRing>
55 where P: RingStore + Copy,
56 P::Type: PolyRing,
57 <<P::Type as RingExtension>::BaseRing as RingStore>::Type: Domain + PrincipalIdealRing
58{
59 let base_ring = ring.base_ring();
60 if ring.is_zero(&g) || ring.is_zero(&f) {
61 return base_ring.zero();
62 }
63 let mut scale_den = base_ring.one();
64 let mut scale_num = base_ring.one();
65
66 if ring.degree(&g).unwrap() < ring.degree(&f).unwrap() {
67 if ring.degree(&g).unwrap() % 2 != 0 && ring.degree(&f).unwrap() % 2 != 0 {
68 base_ring.negate_inplace(&mut scale_num);
69 }
70 std::mem::swap(&mut f, &mut g);
71 }
72
73 while ring.degree(&f).unwrap_or(0) >= 1 {
74
75 let balance_factor = ring.get_ring().balance_poly(&mut f);
76 if let Some(balance_factor) = balance_factor {
77 base_ring.mul_assign(&mut scale_num, base_ring.pow(balance_factor, ring.degree(&g).unwrap()));
78 }
79
80 let deg_g = ring.degree(&g).unwrap();
82 let (_q, r, a) = algorithms::poly_div::poly_div_rem_domain(ring, g, &f);
83 let deg_r = ring.degree(&r).unwrap_or(0);
84
85 base_ring.mul_assign(&mut scale_den, base_ring.pow(a, ring.degree(&f).unwrap()));
87 base_ring.mul_assign(&mut scale_num, base_ring.pow(base_ring.clone_el(ring.lc(&f).unwrap()), deg_g - deg_r));
88 let gcd = base_ring.ideal_gen(&scale_den, &scale_num);
89 scale_den = base_ring.checked_div(&scale_den, &gcd).unwrap();
90 scale_num = base_ring.checked_div(&scale_num, &gcd).unwrap();
91
92 g = f;
93 f = r;
94
95 if ring.degree(&g).unwrap() % 2 != 0 && ring.degree(&f).unwrap_or(0) % 2 != 0 {
96 base_ring.negate_inplace(&mut scale_num);
97 }
98 }
99
100 if ring.is_zero(&f) {
101 return base_ring.zero();
102 } else {
103 let mut result = base_ring.clone_el(&ring.coefficient_at(&f, 0));
104 result = base_ring.pow(result, ring.degree(&g).unwrap());
105 base_ring.mul_assign(&mut result, scale_num);
106 return base_ring.checked_div(&result, &scale_den).unwrap();
107 }
108}
109
110#[stability::unstable(feature = "enable")]
117pub fn resultant_finite_field<P>(ring: P, mut f: El<P>, mut g: El<P>) -> El<<P::Type as RingExtension>::BaseRing>
118 where P: RingStore + Copy,
119 P::Type: PolyRing + EuclideanRing,
120 <<P::Type as RingExtension>::BaseRing as RingStore>::Type: Domain + FiniteRing
121{
122 let base_ring = ring.base_ring();
123 if ring.is_zero(&g) || ring.is_zero(&f) {
124 return base_ring.zero();
125 }
126 let mut scale = base_ring.one();
127 if ring.degree(&g).unwrap() < ring.degree(&f).unwrap() {
128 if ring.degree(&g).unwrap() % 2 != 0 && ring.degree(&f).unwrap() % 2 != 0 {
129 base_ring.negate_inplace(&mut scale);
130 }
131 swap(&mut f, &mut g);
132 }
133
134 while ring.degree(&f).unwrap_or(0) >= 1 {
135 assert!(ring.degree(&g).unwrap() >= ring.degree(&f).unwrap());
136 let deg_g = ring.degree(&g).unwrap();
137 let r = ring.euclidean_rem(g, &f);
138 g = r;
139 base_ring.mul_assign(&mut scale, base_ring.pow(base_ring.clone_el(ring.lc(&f).unwrap()), deg_g - ring.degree(&g).unwrap_or(0)));
140
141 swap(&mut f, &mut g);
142 if ring.degree(&g).unwrap() % 2 != 0 && ring.degree(&f).unwrap_or(0) % 2 != 0 {
143 base_ring.negate_inplace(&mut scale);
144 }
145 }
146
147 if ring.is_zero(&f) {
148 return base_ring.zero();
149 } else {
150 let mut result = base_ring.clone_el(&ring.coefficient_at(&f, 0));
151 result = base_ring.pow(result, ring.degree(&g).unwrap());
152 base_ring.mul_assign(&mut result, scale);
153 return result;
154 }
155}
156
157#[stability::unstable(feature = "enable")]
162pub trait ComputeResultantRing: RingBase {
163
164 fn resultant<P>(poly_ring: P, f: El<P>, g: El<P>) -> Self::Element
192 where P: RingStore + Copy,
193 P::Type: PolyRing,
194 <P::Type as RingExtension>::BaseRing: RingStore<Type = Self>;
195}
196
197impl<R: ?Sized + EvalPolyLocallyRing + PrincipalIdealRing + Domain + SelfIso> ComputeResultantRing for R {
198
199 default fn resultant<P>(ring: P, f: El<P>, g: El<P>) -> El<<P::Type as RingExtension>::BaseRing>
200 where P: RingStore + Copy,
201 P::Type: PolyRing,
202 <P::Type as RingExtension>::BaseRing: RingStore<Type = R>
203 {
204 struct ComputeResultant<P>
205 where P: RingStore + Copy,
206 P::Type: PolyRing,
207 <<P::Type as RingExtension>::BaseRing as RingStore>::Type: EvalPolyLocallyRing + PrincipalIdealRing + Domain + SelfIso
208 {
209 ring: P,
210 f: El<P>,
211 g: El<P>
212 }
213 impl<P> FiniteRingOperation<<<P::Type as RingExtension>::BaseRing as RingStore>::Type> for ComputeResultant<P>
214 where P: RingStore + Copy,
215 P::Type: PolyRing,
216 <<P::Type as RingExtension>::BaseRing as RingStore>::Type: EvalPolyLocallyRing + PrincipalIdealRing + Domain + SelfIso
217 {
218 type Output = El<<P::Type as RingExtension>::BaseRing>;
219
220 fn execute(self) -> Self::Output
221 where <<P::Type as RingExtension>::BaseRing as RingStore>::Type: FiniteRing
222 {
223 let new_poly_ring = DensePolyRing::new(AsField::from(AsFieldBase::promise_is_perfect_field(self.ring.base_ring())), "X");
224 let hom = new_poly_ring.lifted_hom(&self.ring, WrapHom::new(new_poly_ring.base_ring().get_ring()));
225 let result = resultant_finite_field(&new_poly_ring, hom.map(self.f), hom.map(self.g));
226 return UnwrapHom::new(new_poly_ring.base_ring().get_ring()).map(result);
227 }
228
229 fn fallback(self) -> Self::Output {
230 let ring = self.ring;
231 let f = self.f;
232 let g = self.g;
233 let base_ring = ring.base_ring();
234 if ring.is_zero(&f) || ring.is_zero(&g) {
235 return base_ring.zero();
236 }
237 let n = ring.degree(&f).unwrap() + ring.degree(&g).unwrap();
238 let coeff_bound_ln = ring.terms(&f).chain(ring.terms(&g)).map(|(c, _)| base_ring.get_ring().ln_pseudo_norm(c)).max_by(f64::total_cmp).unwrap();
239 let ln_max_norm = coeff_bound_ln * n as f64 + base_ring.get_ring().ln_pseudo_norm(&base_ring.int_hom().map(n as i32)) * n as f64 / 2.;
240
241 let work_locally = base_ring.get_ring().local_computation(ln_max_norm);
242 let mut resultants = Vec::new();
243 for i in 0..base_ring.get_ring().local_ring_count(&work_locally) {
244 let embedding = EvaluatePolyLocallyReductionMap::new(base_ring.get_ring(), &work_locally, i);
245 let new_poly_ring = DensePolyRing::new(embedding.codomain(), "X");
246 let poly_ring_embedding = new_poly_ring.lifted_hom(ring, &embedding);
247 let local_f = poly_ring_embedding.map_ref(&f);
248 let local_g = poly_ring_embedding.map_ref(&g);
249 let local_resultant = <_ as ComputeResultantRing>::resultant(&new_poly_ring, local_f, local_g);
250 resultants.push(local_resultant);
251 }
252 return base_ring.get_ring().lift_combine(&work_locally, &resultants);
253 }
254 }
255 R::specialize(ComputeResultant { ring, f, g })
256 }
257}
258
259impl<I> ComputeResultantRing for RationalFieldBase<I>
260 where I: RingStore,
261 I::Type: IntegerRing
262{
263 fn resultant<P>(ring: P, f: El<P>, g: El<P>) -> El<<P::Type as RingExtension>::BaseRing>
264 where P: RingStore + Copy,
265 P::Type: PolyRing,
266 <P::Type as RingExtension>::BaseRing: RingStore<Type = Self>
267 {
268 if ring.is_zero(&g) || ring.is_zero(&f) {
269 return ring.base_ring().zero();
270 }
271 let QQ = ring.base_ring();
272 let ZZ = QQ.base_ring();
273 let f_deg = ring.degree(&f).unwrap();
274 let g_deg = ring.degree(&g).unwrap();
275 let f_den_lcm = ring.terms(&f).map(|(c, _)| ZZ.clone_el(QQ.get_ring().den(c))).fold(ZZ.one(), |a, b| signed_lcm(a, b, ZZ));
276 let g_den_lcm = ring.terms(&g).map(|(c, _)| ZZ.clone_el(QQ.get_ring().den(c))).fold(ZZ.one(), |a, b| signed_lcm(a, b, ZZ));
277 let ZZX = DensePolyRing::new(ZZ, "X");
278 let f_int = ZZX.from_terms(ring.terms(&f).map(|(c, i)| { let (a, b) = (QQ.get_ring().num(c), QQ.get_ring().den(c)); (ZZ.checked_div(&ZZ.mul_ref(&f_den_lcm, a), b).unwrap(), i) }));
279 let g_int = ZZX.from_terms(ring.terms(&g).map(|(c, i)| { let (a, b) = (QQ.get_ring().num(c), QQ.get_ring().den(c)); (ZZ.checked_div(&ZZ.mul_ref(&f_den_lcm, a), b).unwrap(), i) }));
280 let result_unscaled = <_ as ComputeResultantRing>::resultant(&ZZX, f_int, g_int);
281 return QQ.from_fraction(result_unscaled, ZZ.mul(ZZ.pow(f_den_lcm, g_deg), ZZ.pow(g_den_lcm, f_deg)));
282 }
283}
284
285#[cfg(test)]
286use crate::primitive_int::StaticRing;
287#[cfg(test)]
288use crate::rings::multivariate::multivariate_impl::MultivariatePolyRingImpl;
289#[cfg(test)]
290use crate::rings::multivariate::*;
291#[cfg(test)]
292use crate::algorithms::buchberger::buchberger_simple;
293#[cfg(test)]
294use crate::integer::BigIntRing;
295#[cfg(test)]
296use crate::algorithms::poly_gcd::PolyTFracGCDRing;
297#[cfg(test)]
298use crate::rings::zn::ZnRingStore;
299#[cfg(test)]
300use crate::rings::zn::zn_64::Zn;
301
302#[test]
303#[allow(deprecated)]
304fn test_resultant_global() {
305 let ZZ = StaticRing::<i64>::RING;
306 let ZZX = DensePolyRing::new(ZZ, "X");
307
308 let f = ZZX.from_terms([(3, 0), (-5, 1), (1, 2)].into_iter());
310 let g = ZZX.from_terms([(-5, 0), (2, 1)].into_iter());
311
312 assert_el_eq!(ZZ, -13, resultant_global(&ZZX, ZZX.clone_el(&f), ZZX.clone_el(&g)));
313 assert_el_eq!(ZZ, -13, resultant_global(&ZZX, g, f));
314
315 let f = ZZX.from_terms([(1, 0), (-2, 1), (1, 2)].into_iter());
317 let g = ZZX.from_terms([(-1, 0), (1, 2)].into_iter());
318 assert_el_eq!(ZZ, 0, resultant_global(&ZZX, f, g));
319
320 let f = ZZX.from_terms([(5, 0), (-1, 1), (3, 2), (1, 4)].into_iter());
322 let g = ZZX.from_terms([(-1, 0), (-1, 2), (1, 3), (4, 5)].into_iter());
323 assert_el_eq!(ZZ, 642632, resultant_global(&ZZX, f, g));
324
325 let Fp = Zn::new(512409557603043077).as_field().ok().unwrap();
326 let FpX = DensePolyRing::new(Fp, "X");
327 let f = FpX.from_terms([(Fp.one(), 4), (Fp.int_hom().map(-2), 1), (Fp.int_hom().map(2), 0)].into_iter());
328 let g = FpX.from_terms([(Fp.one(), 64), (Fp.one(), 0)].into_iter());
329 assert_el_eq!(Fp, Fp.coerce(&StaticRing::<i64>::RING, 148105674794572153), resultant_global(&FpX, f, g));
330}
331
332#[test]
333#[allow(deprecated)]
334fn test_resultant_finite_field() {
335 let Fp = Zn::new(17).as_field().ok().unwrap();
336 let FpX = DensePolyRing::new(Fp, "X");
337
338 let [f, g] = FpX.with_wrapped_indeterminate(|X| [X.pow_ref(5) + 13 * X.pow_ref(4) + 4 * X.pow_ref(3) + X.pow_ref(2) + 14 * X + 7, X.pow_ref(3) + 1]);
339 assert_el_eq!(Fp, Fp.int_hom().map(8), resultant_finite_field(&FpX, FpX.clone_el(&f), FpX.clone_el(&g)));
340 assert_el_eq!(Fp, Fp.int_hom().map(9), resultant_finite_field(&FpX, FpX.clone_el(&g), FpX.clone_el(&f)));
341 assert_el_eq!(Fp, Fp.int_hom().map(8), resultant_global(&FpX, FpX.clone_el(&f), FpX.clone_el(&g)));
342 assert_el_eq!(Fp, Fp.int_hom().map(9), resultant_global(&FpX, FpX.clone_el(&g), FpX.clone_el(&f)));
343
344 let [f, g] = FpX.with_wrapped_indeterminate(|X| [X.pow_ref(4) + 4 * X.pow_ref(3) + X.pow_ref(2) + 14 * X + 7, X.pow_ref(3) + 1]);
345 assert_el_eq!(Fp, Fp.int_hom().map(5), resultant_finite_field(&FpX, FpX.clone_el(&f), FpX.clone_el(&g)));
346 assert_el_eq!(Fp, Fp.int_hom().map(5), resultant_finite_field(&FpX, FpX.clone_el(&g), FpX.clone_el(&f)));
347 assert_el_eq!(Fp, Fp.int_hom().map(5), resultant_global(&FpX, FpX.clone_el(&f), FpX.clone_el(&g)));
348 assert_el_eq!(Fp, Fp.int_hom().map(5), resultant_global(&FpX, FpX.clone_el(&g), FpX.clone_el(&f)));
349}
350
351#[test]
352#[allow(deprecated)]
353fn test_resultant_local_polynomial() {
354 let ZZ = BigIntRing::RING;
355 let QQ = RationalField::new(ZZ);
356 static_assert_impls!(RationalFieldBase<BigIntRing>: PolyTFracGCDRing);
357 let QQX = DensePolyRing::new(&QQ, "X");
359 let QQXY = DensePolyRing::new(&QQX, "Y");
360 let ZZ_to_QQ = QQ.int_hom();
361
362 let f= QQXY.from_terms([
364 (vec![(1, 0), (1, 2)], 0),
365 (vec![(2, 0)], 1),
366 (vec![(1, 0), (1, 1)], 2)
367 ].into_iter().map(|(v, i)| (QQX.from_terms(v.into_iter().map(|(c, j)| (ZZ_to_QQ.map(c), j))), i)));
368
369 let g = QQXY.from_terms([
371 (vec![(3, 0), (1, 1)], 0),
372 (vec![(2, 0), (1, 1)], 1),
373 (vec![(1, 0), (1, 1), (1, 2)], 2)
374 ].into_iter().map(|(v, i)| (QQX.from_terms(v.into_iter().map(|(c, j)| (ZZ_to_QQ.map(c), j))), i)));
375
376 let actual = QQX.normalize(resultant_global(&QQXY, QQXY.clone_el(&f), QQXY.clone_el(&g)));
377 let actual_local = <_ as ComputeResultantRing>::resultant(&QQXY, QQXY.clone_el(&f), QQXY.clone_el(&g));
378 assert_el_eq!(&QQX, &actual, &actual_local);
379
380 let QQYX = MultivariatePolyRingImpl::new(&QQ, 2);
381 let [f, g] = QQYX.with_wrapped_indeterminates(|[Y, X]| [ 1 + X.pow_ref(2) + 2 * Y + (1 + X) * Y.pow_ref(2), 3 + X + (2 + X) * Y + (1 + X + X.pow_ref(2)) * Y.pow_ref(2) ]);
383
384 let gb = buchberger_simple::<_, _>(&QQYX, vec![f, g], Lex);
385 let expected = gb.into_iter().filter(|poly| QQYX.appearing_indeterminates(&poly).len() == 1).collect::<Vec<_>>();
386 assert!(expected.len() == 1);
387 let expected = QQX.normalize(QQX.from_terms(QQYX.terms(&expected[0]).map(|(c, m)| (QQ.clone_el(c), QQYX.exponent_at(m, 1)))));
388
389 assert_el_eq!(QQX, expected, actual);
390}
391
392#[test]
393fn test_resultant_local_integer() {
394 let ZZ = BigIntRing::RING;
395 let ZZX = DensePolyRing::new(ZZ, "X");
396
397 let [f, g] = ZZX.with_wrapped_indeterminate(|X| [
398 X.pow_ref(32) + 1,
399 X.pow_ref(2) - X - 1
400 ]);
401 assert_el_eq!(ZZ, ZZ.int_hom().map(4870849), <_ as ComputeResultantRing>::resultant(&ZZX, f, g));
402
403 let [f, g] = ZZX.with_wrapped_indeterminate(|X| [
404 X.pow_ref(4) - 2 * X + 2,
405 X.pow_ref(64) + 1
406 ]);
407 assert_el_eq!(ZZ, ZZ.parse("381380816531458621441", 10).unwrap(), <_ as ComputeResultantRing>::resultant(&ZZX, f, g));
408
409 let [f, g] = ZZX.with_wrapped_indeterminate(|X| [
410 X.pow_ref(32) - 2 * X.pow_ref(8) + 2,
411 X.pow_ref(512) + 1
412 ]);
413 assert_el_eq!(ZZ, ZZ.pow(ZZ.parse("381380816531458621441", 10).unwrap(), 8), <_ as ComputeResultantRing>::resultant(&ZZX, f, g));
414}
415
416#[test]
417#[ignore]
418fn test_resultant_large() {
419 let ZZ = BigIntRing::RING;
420 let ZZX = DensePolyRing::new(ZZ, "X");
421
422 let [f] = ZZX.with_wrapped_indeterminate(|X| [X.pow_ref(4) - 2 * X + 2]);
423 let g = ZZX.from_terms([(ZZ.one(), 1 << 14), (ZZ.one(), 0)]);
424 println!("start");
425 let result = BigIntRingBase::resultant(&ZZX, f, g);
426 println!("{}", ZZ.format(&result))
427}