1use ocas_domain::EuclideanDomain;
11
12use crate::dense::DenseUnivariatePolynomial;
13
14impl<D: EuclideanDomain> DenseUnivariatePolynomial<D> {
15 pub fn resultant(&self, other: &Self) -> D::Element {
37 let d = self.domain();
38
39 let (a, b, swapped) = match (self.degree(), other.degree()) {
41 (None, _) | (_, None) => return d.zero(),
42 (Some(da), Some(db)) => {
43 if da >= db {
44 (self.clone(), other.clone(), false)
45 } else {
46 (other.clone(), self.clone(), true)
47 }
48 }
49 };
50
51 let deg_a = a.degree().unwrap();
52 let deg_b = b.degree().unwrap();
53
54 if deg_b == 0 {
56 let val = b.constant();
57 let res = d.pow(&val, deg_a as u64);
58 if swapped && (deg_a * deg_b) % 2 == 1 {
60 return d.neg(&res);
61 }
62 return res;
63 }
64
65 let mut a_cur = a;
67 let mut b_cur = b;
68
69 let deg_diff = a_cur.degree().unwrap() - b_cur.degree().unwrap();
70 let neg_lc = d.neg(b_cur.leading_coeff().unwrap());
71 let mut beta = d.pow(&d.neg(&d.one()), (deg_diff + 1) as u64);
72 let mut psi = d.neg(&d.one());
73
74 let mut lcs: Vec<(D::Element, usize)> = Vec::new();
76 lcs.push((a_cur.lcoeff(), a_cur.degree().unwrap()));
77
78 let mut first = true;
79
80 while !b_cur.is_zero() {
81 let b_deg = b_cur.degree().unwrap();
82
83 if !first {
84 let cur_deg_diff = a_cur.degree().unwrap() - b_deg;
86 psi = if cur_deg_diff == 0 {
87 psi
88 } else if cur_deg_diff == 1 {
89 neg_lc.clone()
90 } else {
91 let a_part = d.pow(&neg_lc, cur_deg_diff as u64);
92 let psi_old = d.pow(&psi, (cur_deg_diff - 1) as u64);
93 d.div_rem(&a_part, &psi_old).unwrap().0
94 };
95
96 let new_deg_diff = a_cur.degree().unwrap() - b_deg;
97 beta = d.mul(&neg_lc, &d.pow(&psi, new_deg_diff as u64));
98 }
99 first = false;
100
101 let neg_lc_new = d.neg(b_cur.leading_coeff().unwrap());
102 let deg_diff_now = a_cur.degree().unwrap() - b_deg;
103
104 let factor = d.pow(&neg_lc_new, (deg_diff_now + 1) as u64);
106 let scaled = a_cur.mul_scalar(&factor);
107 let (_, mut r) = scaled.div_rem(&b_cur).unwrap();
108
109 if (deg_diff_now + 1) % 2 == 1 {
111 r = r.neg();
112 }
113
114 if !d.is_zero(&beta) {
116 let inv_beta = d.div_rem(&d.one(), &beta);
117 if let Some((q, rem)) = inv_beta
118 && d.is_zero(&rem)
119 {
120 r = r.mul_scalar(&q);
121 }
122 }
123
124 lcs.push((b_cur.lcoeff(), b_deg));
125
126 a_cur = b_cur;
127 b_cur = r;
128 }
129
130 if let Some(last_deg) = b_cur.degree()
133 && last_deg > 0
134 {
135 return d.zero();
136 }
137 if a_cur.degree().unwrap_or(0) > 0 {
139 return d.zero();
140 }
141
142 lcs.push((a_cur.lcoeff(), 0));
144
145 let mut rho = d.one();
146 let mut den = d.one();
147
148 for k in 1..lcs.len() {
149 let deg_k_prev = lcs[k - 1].1 as i64;
150 let deg_k = lcs[k].1 as i64;
151 #[allow(unused_variables)]
152 let deg_k_next = if k + 1 < lcs.len() {
153 lcs[k + 1].1 as i64
154 } else {
155 0
156 };
157
158 let mut exponent: i64 = deg_k_prev - deg_k;
159 for l in k..lcs.len() - 1 {
161 let dl = lcs[l].1 as i64;
162 let dl1 = lcs[l + 1].1 as i64;
163 exponent *= 1 - (dl - dl1);
164 }
165
166 if exponent > 0 {
167 let pow_val = d.pow(&lcs[k].0, exponent as u64);
168 rho = d.mul(&rho, &pow_val);
169 } else if exponent < 0 {
170 let pow_val = d.pow(&lcs[k].0, (-exponent) as u64);
171 den = d.mul(&den, &pow_val);
172 }
173 }
174
175 let result = d.div_rem(&rho, &den).unwrap().0;
176
177 if swapped && (deg_a * deg_b) % 2 == 1 {
179 d.neg(&result)
180 } else {
181 result
182 }
183 }
184}
185
186#[cfg(test)]
187mod tests {
188 use super::*;
189 use ocas_domain::{Integer, IntegerDomain};
190
191 fn int(i: i64) -> Integer {
192 Integer::from(i)
193 }
194
195 fn poly(coeffs: &[i64]) -> DenseUnivariatePolynomial<IntegerDomain> {
196 DenseUnivariatePolynomial::from_coeffs(
197 IntegerDomain,
198 coeffs.iter().map(|&c| int(c)).collect(),
199 )
200 }
201
202 #[test]
203 fn resultant_linear_different_roots() {
204 let a = poly(&[-1, 1]); let b = poly(&[-2, 1]); assert_eq!(a.resultant(&b), int(-1));
208 }
209
210 #[test]
211 fn resultant_common_root() {
212 let a = poly(&[-1, 0, 1]); let b = poly(&[-1, 1]); assert_eq!(a.resultant(&b), int(0));
216 }
217
218 #[test]
219 fn resultant_no_common_root() {
220 let a = poly(&[1, 0, 1]); let b = poly(&[1, 2, 1]); assert_eq!(a.resultant(&b), int(4));
224 }
225
226 #[test]
227 fn resultant_shared_factor() {
228 let a = poly(&[2, -3, 1]); let b = poly(&[3, -4, 1]); assert_eq!(a.resultant(&b), int(0));
232 }
233
234 #[test]
235 fn resultant_constant_poly() {
236 let a = poly(&[1, 0, 1]);
238 let b = poly(&[3]);
239 assert_eq!(a.resultant(&b), int(9));
240 }
241
242 #[test]
243 fn resultant_constant_constant() {
244 let a = poly(&[2]);
246 let b = poly(&[3]);
247 assert_eq!(a.resultant(&b), int(1));
248 }
249
250 #[test]
251 fn resultant_symmetric_up_to_sign() {
252 let a = poly(&[-1, 0, 1]); let b = poly(&[-2, 1]); let r1 = a.resultant(&b);
257 let r2 = b.resultant(&a);
258 assert_eq!(r1, r2);
259 }
260
261 #[test]
262 fn resultant_zero_poly() {
263 let a = poly(&[0]); let b = poly(&[1, 1]); assert_eq!(a.resultant(&b), int(0));
266 }
267}