1use std::fmt;
13
14use crate::algebraic::{resultant_in_y, Algebraic};
15use crate::numeric::Rational;
16use crate::poly::Poly;
17
18#[derive(Clone, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)]
20pub struct ComplexAlgebraic {
21 pub re: Algebraic,
22 pub im: Algebraic,
23}
24
25impl ComplexAlgebraic {
26 pub fn new(re: Algebraic, im: Algebraic) -> ComplexAlgebraic {
27 ComplexAlgebraic { re, im }
28 }
29
30 pub fn real(re: Algebraic) -> ComplexAlgebraic {
31 ComplexAlgebraic {
32 re,
33 im: Algebraic::from_i64(0),
34 }
35 }
36
37 pub fn imag(im: Algebraic) -> ComplexAlgebraic {
38 ComplexAlgebraic {
39 re: Algebraic::from_i64(0),
40 im,
41 }
42 }
43
44 pub fn from_rational(r: Rational) -> ComplexAlgebraic {
45 ComplexAlgebraic::real(Algebraic::from_rational(r))
46 }
47
48 pub fn from_i64(n: i64) -> ComplexAlgebraic {
49 ComplexAlgebraic::real(Algebraic::from_i64(n))
50 }
51
52 pub fn zero() -> ComplexAlgebraic {
53 ComplexAlgebraic::from_i64(0)
54 }
55
56 pub fn one() -> ComplexAlgebraic {
57 ComplexAlgebraic::from_i64(1)
58 }
59
60 pub fn is_zero(&self) -> bool {
61 self.re.is_zero() && self.im.is_zero()
62 }
63
64 pub fn is_real(&self) -> bool {
65 self.im.is_zero()
66 }
67
68 pub fn add(&self, o: &ComplexAlgebraic) -> ComplexAlgebraic {
69 ComplexAlgebraic {
70 re: self.re.add(&o.re),
71 im: self.im.add(&o.im),
72 }
73 }
74
75 pub fn sub(&self, o: &ComplexAlgebraic) -> ComplexAlgebraic {
76 self.add(&o.neg())
77 }
78
79 pub fn neg(&self) -> ComplexAlgebraic {
80 ComplexAlgebraic {
81 re: self.re.neg(),
82 im: self.im.neg(),
83 }
84 }
85
86 pub fn conj(&self) -> ComplexAlgebraic {
87 ComplexAlgebraic {
88 re: self.re.clone(),
89 im: self.im.neg(),
90 }
91 }
92
93 pub fn mul(&self, o: &ComplexAlgebraic) -> ComplexAlgebraic {
94 if self.is_real() && o.is_real() {
95 return ComplexAlgebraic::real(self.re.mul(&o.re));
96 }
97 ComplexAlgebraic {
98 re: self.re.mul(&o.re).sub(&self.im.mul(&o.im)),
99 im: self.re.mul(&o.im).add(&self.im.mul(&o.re)),
100 }
101 }
102
103 pub fn scale(&self, r: &Rational) -> ComplexAlgebraic {
105 let r = Algebraic::from_rational(r.clone());
106 ComplexAlgebraic {
107 re: self.re.mul(&r),
108 im: self.im.mul(&r),
109 }
110 }
111
112 pub fn recip(&self) -> Option<ComplexAlgebraic> {
114 if self.is_real() {
115 return Some(ComplexAlgebraic::real(self.re.recip()?));
116 }
117 let norm = self.re.mul(&self.re).add(&self.im.mul(&self.im));
118 let inv = norm.recip()?;
119 Some(ComplexAlgebraic {
120 re: self.re.mul(&inv),
121 im: self.im.neg().mul(&inv),
122 })
123 }
124
125 pub fn div(&self, o: &ComplexAlgebraic) -> Option<ComplexAlgebraic> {
126 Some(self.mul(&o.recip()?))
127 }
128
129 pub fn pow(&self, e: u32) -> ComplexAlgebraic {
130 let mut acc = ComplexAlgebraic::one();
131 let mut base = self.clone();
132 let mut e = e;
133 while e > 0 {
134 if e & 1 == 1 {
135 acc = acc.mul(&base);
136 }
137 e >>= 1;
138 if e > 0 {
139 base = base.mul(&base);
140 }
141 }
142 acc
143 }
144
145 pub fn eval(p: &Poly, z: &ComplexAlgebraic) -> ComplexAlgebraic {
147 p.coeffs()
148 .iter()
149 .rev()
150 .fold(ComplexAlgebraic::zero(), |acc, c| {
151 acc.mul(z).add(&ComplexAlgebraic::from_rational(c.clone()))
152 })
153 }
154
155 pub fn to_f64(&self) -> (f64, f64) {
157 (self.re.to_f64(), self.im.to_f64())
158 }
159}
160
161impl fmt::Display for ComplexAlgebraic {
162 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
164 if self.is_real() {
165 return write!(f, "{}", self.re);
166 }
167 let magnitude = if self.im.signum() < 0 {
168 self.im.neg()
169 } else {
170 self.im.clone()
171 };
172 let unit = magnitude == Algebraic::from_i64(1);
173 let text = magnitude.to_string();
174 let imaginary = if unit {
175 "i".to_string()
176 } else if text.contains(' ') || text.contains('/') {
177 format!("({text})·i")
178 } else {
179 format!("{text}·i")
180 };
181 match (self.re.is_zero(), self.im.signum() < 0) {
182 (true, false) => write!(f, "{imaginary}"),
183 (true, true) => write!(f, "-{imaginary}"),
184 (false, false) => write!(f, "{} + {imaginary}", self.re),
185 (false, true) => write!(f, "{} - {imaginary}", self.re),
186 }
187 }
188}
189
190fn distinct_real_roots(p: &Poly) -> Vec<Algebraic> {
192 let mut out: Vec<Algebraic> = Vec::new();
193 for (factor, _) in p.factor_over_q() {
194 for k in 1..=factor.count_real_roots() {
195 if let Some(r) = Algebraic::root_of(&factor, k) {
196 if !out.contains(&r) {
197 out.push(r);
198 }
199 }
200 }
201 }
202 out.sort();
203 out
204}
205
206fn approximate_roots(p: &Poly) -> Vec<(f64, f64)> {
208 let degree = p.degree().unwrap_or(0);
209 let lead = p.coeff(degree).to_f64();
210 let c: Vec<f64> = (0..=degree).map(|i| p.coeff(i).to_f64() / lead).collect();
211 let eval = |x: (f64, f64)| {
212 let mut acc = (0.0f64, 0.0f64);
213 for k in (0..=degree).rev() {
214 acc = (acc.0 * x.0 - acc.1 * x.1 + c[k], acc.0 * x.1 + acc.1 * x.0);
215 }
216 acc
217 };
218 let seed = (0.4f64, 0.9f64);
219 let mut z: Vec<(f64, f64)> = Vec::with_capacity(degree);
220 let mut w = (1.0f64, 0.0f64);
221 for _ in 0..degree {
222 z.push(w);
223 w = (w.0 * seed.0 - w.1 * seed.1, w.0 * seed.1 + w.1 * seed.0);
224 }
225 for _ in 0..2000 {
226 let mut moved = 0.0f64;
227 for i in 0..degree {
228 let num = eval(z[i]);
229 let mut den = (1.0f64, 0.0f64);
230 for (j, zj) in z.iter().enumerate() {
231 if j != i {
232 let d = (z[i].0 - zj.0, z[i].1 - zj.1);
233 den = (den.0 * d.0 - den.1 * d.1, den.0 * d.1 + den.1 * d.0);
234 }
235 }
236 let norm = den.0 * den.0 + den.1 * den.1;
237 if norm == 0.0 {
238 continue;
239 }
240 let step = (
241 (num.0 * den.0 + num.1 * den.1) / norm,
242 (num.1 * den.0 - num.0 * den.1) / norm,
243 );
244 z[i] = (z[i].0 - step.0, z[i].1 - step.1);
245 moved = moved.max(step.0.abs() + step.1.abs());
246 }
247 if moved < 1e-15 {
248 break;
249 }
250 }
251 z
252}
253
254fn upper_roots(g: &Poly) -> Vec<ComplexAlgebraic> {
256 let degree = g.degree().unwrap_or(0);
257 let pairs = (degree - g.count_real_roots()) / 2;
258 if pairs == 0 {
259 return Vec::new();
260 }
261 let sums = resultant_in_y(
262 g,
263 |x0| g.compose(&Poly::from_coeffs(vec![x0.clone(), Rational::from_i64(-1)])),
264 degree * degree,
265 );
266 let differences = resultant_in_y(
267 g,
268 |x0| g.compose(&Poly::from_coeffs(vec![x0.clone(), Rational::from_i64(1)])),
269 degree * degree,
270 );
271 let half = Algebraic::from_rational(Rational::from_ratio_i64(1, 2).expect("2 ≠ 0"));
272 let real_parts: Vec<Algebraic> = distinct_real_roots(&sums).iter().map(|s| s.mul(&half)).collect();
273 let even: Vec<Rational> = differences.coeffs()[degree..]
274 .iter()
275 .step_by(2)
276 .cloned()
277 .collect();
278 let quarter = Algebraic::from_rational(Rational::from_ratio_i64(-1, 4).expect("4 ≠ 0"));
279 let imaginary_parts: Vec<Algebraic> = distinct_real_roots(&Poly::from_coeffs(even))
280 .into_iter()
281 .filter(|u| u.signum() < 0)
282 .filter_map(|u| u.mul(&quarter).sqrt())
283 .collect();
284 let mut found: Vec<ComplexAlgebraic> = Vec::new();
285 for (x, y) in approximate_roots(g).into_iter().filter(|(_, y)| *y > 0.0) {
286 let mut candidates: Vec<(f64, &Algebraic, &Algebraic)> = Vec::new();
287 for a in &real_parts {
288 for b in &imaginary_parts {
289 let (da, db) = (a.to_f64() - x, b.to_f64() - y);
290 candidates.push((da * da + db * db, a, b));
291 }
292 }
293 candidates.sort_by(|p, q| p.0.total_cmp(&q.0));
294 for (_, a, b) in candidates {
295 let z = ComplexAlgebraic::new(a.clone(), b.clone());
296 if found.contains(&z) {
297 continue;
298 }
299 if ComplexAlgebraic::eval(g, &z).is_zero() {
300 found.push(z);
301 break;
302 }
303 }
304 if found.len() == pairs {
305 break;
306 }
307 }
308 assert_eq!(found.len(), pairs, "every non-real root of {g} is found and checked exactly");
309 found
310}
311
312pub fn complex_roots(p: &Poly) -> Vec<ComplexAlgebraic> {
315 let mut real = Vec::new();
316 let mut pairs = Vec::new();
317 for (g, _) in p.factor_over_q() {
318 for k in 1..=g.count_real_roots() {
319 real.push(Algebraic::root_of(&g, k).expect("a real root of its own polynomial"));
320 }
321 pairs.extend(upper_roots(&g));
322 }
323 real.sort();
324 pairs.sort();
325 let mut out: Vec<ComplexAlgebraic> = real.into_iter().map(ComplexAlgebraic::real).collect();
326 for z in pairs {
327 let lower = z.conj();
328 out.push(z);
329 out.push(lower);
330 }
331 out
332}
333
334#[cfg(test)]
335#[path = "../../logicaffeine_tests/unit/logicaffeine_base/complex_algebraic/tests.rs"]
336mod tests;