1use dune_raw::{Data, VarType};
11use num_complex::Complex64;
12
13use crate::circuit::Circuit;
14use crate::engine::{Engine, PzSetup};
15use crate::error::Error;
16use crate::matrix::{AnKind, InitMode, Var};
17use crate::options::Options;
18use crate::solution::Solution;
19
20#[derive(Clone, Copy, Debug, PartialEq, Eq)]
22pub enum PzInput {
23 Current,
24 Voltage,
25}
26
27#[derive(Clone, Copy, Debug, PartialEq, Eq)]
29pub enum PzWhich {
30 Poles,
31 Zeros,
32 Both,
33}
34
35#[derive(Clone, Debug)]
37pub struct Pz {
38 pub solution: Solution,
39 pub notes: Vec<String>,
40}
41
42impl Circuit {
43 pub fn pz(
45 &self,
46 opts: &Options,
47 nodes: [&str; 4],
48 input: PzInput,
49 which: PzWhich,
50 ) -> Result<Pz, Error> {
51 let built = self.build(opts)?;
52 let notes = built.notes.clone();
53 let mut engine = Engine::new(built);
54
55 let var_of = |name: &str| -> Result<Var, Error> {
56 let name = name.to_ascii_lowercase();
57 if name == "0" {
58 return Ok(0);
59 }
60 engine
61 .vars
62 .names
63 .iter()
64 .position(|n| *n == name)
65 .ok_or_else(|| Error::Build(format!(".pz names unknown node `{name}`")))
66 };
67 let in_pos = var_of(nodes[0])?;
68 let in_neg = var_of(nodes[1])?;
69 let out_pos = var_of(nodes[2])?;
70 let out_neg = var_of(nodes[3])?;
71
72 if in_pos == in_neg {
74 return Err(Error::Build(".pz: input is shorted".into()));
75 }
76 if out_pos == out_neg {
77 return Err(Error::Build(".pz: output is shorted".into()));
78 }
79 if input == PzInput::Voltage {
80 if in_pos == out_pos && in_neg == out_neg {
81 return Err(Error::Build(".pz: transfer function is unity".into()));
82 }
83 if in_pos == out_neg && in_neg == out_pos {
84 return Err(Error::Build(".pz: transfer function is -1".into()));
85 }
86 }
87
88 engine.set_kind(AnKind::DcOp, false);
90 engine.op(opts)?;
91 engine.load_all_public(InitMode::Smsig, opts);
92
93 let vol = input == PzInput::Voltage;
94 let mut poles: Vec<(Complex64, i32)> = Vec::new();
95 let mut zeros: Vec<(Complex64, i32)> = Vec::new();
96 if which != PzWhich::Zeros {
97 let mut setup = engine.pz_setup(in_pos, in_neg, out_pos, out_neg, vol, false);
98 poles = find_zeros(&mut engine, &mut setup)?;
99 }
100 if which != PzWhich::Poles {
101 let mut setup = engine.pz_setup(in_pos, in_neg, out_pos, out_neg, vol, true);
102 zeros = find_zeros(&mut engine, &mut setup)?;
103 }
104
105 let mut variables = Vec::new();
108 let mut columns: Vec<Vec<Complex64>> = Vec::new();
109 let mut emit = |list: &[(Complex64, i32)], label: &str| {
110 let mut k = 0usize;
111 for &(s, mult) in list {
112 for _ in 0..mult {
113 k += 1;
114 variables.push((format!("v({label}({k}))"), VarType::Voltage));
115 columns.push(vec![s]);
116 if s.im != 0.0 {
117 k += 1;
118 variables.push((format!("v({label}({k}))"), VarType::Voltage));
119 columns.push(vec![s.conj()]);
120 }
121 }
122 }
123 };
124 emit(&poles, "pole");
125 emit(&zeros, "zero");
126 if variables.is_empty() {
127 return Err(Error::Build(".pz found no poles or zeros".into()));
128 }
129
130 Ok(Pz {
131 solution: Solution {
132 plotname: "Pole-Zero Analysis".to_string(),
133 variables,
134 data: Data::Complex(columns),
135 scale: None,
136 },
137 notes,
138 })
139 }
140}
141
142const NITER_LIM: usize = 200;
150
151const SHIFT_LEFT: i32 = 2;
153const SHIFT_RIGHT: i32 = 3;
154const SKIP_LEFT: i32 = 4;
155const SKIP_RIGHT: i32 = 5;
156const INIT: i32 = 6;
157const GUESS: i32 = 7;
158const SPLIT_LEFT: i32 = 8;
159const SPLIT_RIGHT: i32 = 9;
160const MULLER: i32 = 10;
161const SYM: i32 = 11;
162const SYM2: i32 = 12;
163const COMPLEX_INIT: i32 = 13;
164const COMPLEX_GUESS: i32 = 14;
165
166const NEAR_LEFT: i32 = 4;
168const MID_LEFT: i32 = 5;
169const FAR_LEFT: i32 = 6;
170const NEAR_RIGHT: i32 = 7;
171const FAR_RIGHT: i32 = 8;
172const MID_RIGHT: i32 = 9;
173
174const ISAROOT: u8 = 2;
175#[allow(dead_code)]
176const ISAREPEAT: u8 = 4;
177const ISAMINIMA: u8 = 16;
178
179fn sgn(x: f64) -> i32 {
180 if x < 0.0 {
181 -1
182 } else if x == 0.0 {
183 0
184 } else {
185 1
186 }
187}
188
189fn zaddeq(x: f64, xmag: i32, y: f64, ymag: i32) -> (f64, i32) {
191 let (mut x, mut y) = (x, y);
192 let amag;
193 if xmag > ymag {
194 amag = xmag;
195 if xmag > 50 + ymag {
196 y = 0.0;
197 } else {
198 let mut d = xmag - ymag;
199 while d > 0 {
200 y /= 2.0;
201 d -= 1;
202 }
203 }
204 } else {
205 amag = ymag;
206 if ymag > 50 + xmag {
207 x = 0.0;
208 } else {
209 let mut d = ymag - xmag;
210 while d > 0 {
211 x /= 2.0;
212 d -= 1;
213 }
214 }
215 }
216 let mut a = x + y;
217 let mut m = amag;
218 if a == 0.0 {
219 m = 0;
220 } else {
221 while a.abs() > 1.0 {
222 a /= 2.0;
223 m += 1;
224 }
225 while a.abs() < 0.5 {
226 a *= 2.0;
227 m -= 1;
228 }
229 }
230 (a, m)
231}
232
233fn r_norm(a: &mut f64, b: &mut i32) {
235 if *a == 0.0 {
236 *b = 0;
237 } else {
238 while a.abs() > 1.0 {
239 *b += 1;
240 *a /= 2.0;
241 }
242 while a.abs() < 0.5 {
243 *b -= 1;
244 *a *= 2.0;
245 }
246 }
247}
248
249fn c_norm(a: &mut Complex64, b: &mut i32) {
251 if a.re == 0.0 && a.im == 0.0 {
252 *b = 0;
253 } else {
254 while a.re.abs() > 1.0 || a.im.abs() > 1.0 {
255 *b += 1;
256 a.re /= 2.0;
257 a.im /= 2.0;
258 }
259 while a.re.abs() <= 0.5 && a.im.abs() <= 0.5 {
260 *b -= 1;
261 a.re *= 2.0;
262 a.im *= 2.0;
263 }
264 }
265}
266
267fn c_sqrt(a: &mut Complex64) {
269 if a.im == 0.0 {
270 if a.re < 0.0 {
271 a.im = (-a.re).sqrt();
272 a.re = 0.0;
273 } else {
274 a.re = a.re.sqrt();
275 a.im = 0.0;
276 }
277 } else {
278 let mag = (a.re * a.re + a.im * a.im).sqrt();
279 let half = (mag - a.re) / 2.0;
280 if half <= 0.0 {
281 a.re = mag.sqrt();
282 a.im /= 2.0 * a.re;
283 } else {
284 let half = half.sqrt();
285 a.re = a.im / (2.0 * half);
286 a.im = half;
287 }
288 }
289}
290
291#[derive(Clone)]
292struct Trial {
293 s: Complex64,
294 f_raw: Complex64,
295 mag_raw: i32,
296 f_def: Complex64,
297 mag_def: i32,
298 multiplicity: i32,
299 count: i32,
300 #[allow(dead_code)]
301 seq: i32,
302 flags: u8,
303 prev: Option<usize>,
304 next: Option<usize>,
305 dead: bool,
306}
307
308struct Finder<'a> {
309 eng: &'a mut Engine,
310 setup: &'a mut PzSetup,
311 nodes: Vec<Trial>,
312 head: Option<usize>,
313 zero_trial: Option<usize>,
314 nzeros: usize,
315 nflat: i32,
316 max_zeros: usize,
317 seq: i32,
318 guess_param: f64,
319 high_guess: f64,
320 low_guess: f64,
321 last_move: i32,
322 consec_moves: i32,
323 niter: usize,
324 ntrials: i32,
325 trapped: i32,
326 aberr: i32,
327 nipzk: f64,
328 nipzk_mag: i32,
329}
330
331struct Cand {
333 s: Complex64,
334 seq: i32,
335}
336
337type Set = [Option<usize>; 3];
338
339fn find_zeros(eng: &mut Engine, setup: &mut PzSetup) -> Result<Vec<(Complex64, i32)>, Error> {
342 let max_zeros = eng.vars.len() - 1;
343 let mut f = Finder {
344 eng,
345 setup,
346 nodes: Vec::new(),
347 head: None,
348 zero_trial: None,
349 nzeros: 0,
350 nflat: 0,
351 max_zeros,
352 seq: 1,
353 guess_param: 0.0,
354 high_guess: -1.0,
355 low_guess: 1.0,
356 last_move: 0,
357 consec_moves: 0,
358 niter: 0,
359 ntrials: 0,
360 trapped: 0,
361 aberr: 0,
362 nipzk: 0.0,
363 nipzk_mag: 0,
364 };
365
366 let mut set: Set = [None; 3];
367 f.reset(&mut set);
368
369 loop {
370 let mut strat;
371 loop {
372 strat = f.strat(&set);
373 if !(strat < GUESS && f.trapped == 0) {
374 break;
375 }
376 if !f.step(strat, &mut set) {
377 strat = GUESS;
378 break;
379 }
380 }
381
382 f.niter += 1;
383
384 let cand = f.eval(strat, &set)?;
385 let idx = f.run_trial(cand, &set)?;
386
387 if f.nodes[idx].flags & ISAROOT != 0 {
388 f.verify(idx);
389 f.niter = 0;
390 f.reset(&mut set);
391 } else if f.nodes[idx].flags & ISAMINIMA != 0 {
392 set[0] = None;
393 set[1] = Some(idx);
394 set[2] = None;
395 } else {
396 f.update_set(&mut set, idx);
397 }
398
399 let spread_ok = f.high_guess - f.low_guess < 1e35;
401 let window_ok = match (set[0], set[2]) {
402 (Some(a), Some(c)) => f.trapped != 0 || f.nodes[c].s.re - f.nodes[a].s.re < 1e22,
403 _ => true,
404 };
405 if !(spread_ok && f.nzeros < f.max_zeros && f.niter < NITER_LIM && f.aberr < 3 && window_ok)
406 {
407 break;
408 }
409 }
410
411 if f.nzeros as i32 >= f.seq - 1 {
412 return Err(Error::Build(
413 ".pz: the input signal is shorted on the way to the output".into(),
414 ));
415 }
416
417 let mut out = Vec::new();
419 let mut cur = f.head;
420 while let Some(i) = cur {
421 let t = &f.nodes[i];
422 if !t.dead && t.flags & ISAROOT != 0 {
423 out.push((t.s, t.multiplicity));
424 }
425 cur = t.next;
426 }
427 Ok(out)
428}
429
430impl Finder<'_> {
431 fn t(&self, i: usize) -> &Trial {
432 &self.nodes[i]
433 }
434
435 fn seek(&mut self, from: Option<usize>, dir: i32) -> Option<usize> {
437 self.guess_param = dir as f64;
438 let mut t = from?;
439 if dir == 0 && self.t(t).flags & (ISAROOT | ISAMINIMA) == 0 {
440 return Some(t);
441 }
442 loop {
443 let next = if dir >= 0 {
444 self.t(t).next
445 } else {
446 self.t(t).prev
447 };
448 t = next?;
449 if self.t(t).flags & (ISAROOT | ISAMINIMA) == 0 {
450 return Some(t);
451 }
452 }
453 }
454
455 fn reset(&mut self, set: &mut Set) {
457 self.trapped = 0;
458 self.consec_moves = 0;
459 set[1] = self.seek(self.zero_trial, 0);
460 if set[1].is_some() {
461 set[0] = self.seek(set[1], -1);
462 set[2] = self.seek(set[1], 1);
463 } else {
464 set[0] = None;
465 set[2] = None;
466 }
467 }
468
469 fn step(&mut self, strat: i32, set: &mut Set) -> bool {
471 match strat {
472 INIT => {
473 if set[1].is_none() {
474 set[1] = self.seek(self.zero_trial, 0);
475 } else if set[2].is_none() {
476 set[2] = self.seek(set[1], 1);
477 } else if set[0].is_none() {
478 set[0] = self.seek(set[1], -1);
479 }
480 }
481 SKIP_LEFT => set[0] = self.seek(set[0], -1),
482 SKIP_RIGHT => set[2] = self.seek(set[2], 1),
483 SHIFT_LEFT => {
484 set[2] = set[1];
485 set[1] = set[0];
486 set[0] = self.seek(set[0], -1);
487 }
488 SHIFT_RIGHT => {
489 set[0] = set[1];
490 set[1] = set[2];
491 set[2] = self.seek(set[2], 1);
492 }
493 _ => {}
494 }
495 set[0].is_some() && set[1].is_some() && set[2].is_some()
496 }
497
498 fn strat(&mut self, set: &Set) -> i32 {
500 let mut new_trap = 0;
501 let suggestion;
502
503 if set[1].is_some() && self.t(set[1].unwrap()).flags & ISAMINIMA != 0 {
504 suggestion = COMPLEX_INIT;
505 } else if set[0].is_some() && self.t(set[0].unwrap()).s.im != 0.0 {
506 if set[1].is_none() || set[2].is_none() {
507 suggestion = COMPLEX_GUESS;
508 } else {
509 suggestion = MULLER;
510 }
511 } else if set[0].is_none() || set[1].is_none() || set[2].is_none() {
512 suggestion = INIT;
513 } else {
514 let t0 = self.t(set[0].unwrap()).clone();
515 let t1 = self.t(set[1].unwrap()).clone();
516 let t2 = self.t(set[2].unwrap()).clone();
517 let mut sug;
518 if sgn(t0.f_def.re) != sgn(t1.f_def.re) {
519 new_trap = 1;
520 sug = SYM2;
521 } else if sgn(t1.f_def.re) != sgn(t2.f_def.re) {
522 new_trap = 2;
523 sug = SYM2;
524 } else {
525 let (a, a_mag) = zaddeq(t1.f_def.re, t1.mag_def, -t0.f_def.re, t0.mag_def);
526 let (b, b_mag) = zaddeq(t2.f_def.re, t2.mag_def, -t1.f_def.re, t1.mag_def);
527 if self.trapped == 0 {
528 let k1 = t1.s.re - t0.s.re;
529 let k2 = t2.s.re - t1.s.re;
530 if a_mag + 10 < t0.mag_def
531 && a_mag + 10 < t1.mag_def
532 && b_mag + 10 < t1.mag_def
533 && b_mag + 10 < t2.mag_def
534 {
535 sug = if k1 > k2 { SKIP_RIGHT } else { SKIP_LEFT };
536 } else if sgn(a) != -sgn(b) {
537 if a == 0.0 {
538 sug = SKIP_LEFT;
539 } else if b == 0.0 {
540 sug = SKIP_RIGHT;
541 } else if sgn(a) == sgn(t1.f_def.re) {
542 sug = SHIFT_LEFT;
543 } else {
544 sug = SHIFT_RIGHT;
545 }
546 } else if sgn(a) == -sgn(t1.f_def.re) {
547 new_trap = 3;
548 sug = SYM;
549 } else if k1 > k2 {
550 sug = SKIP_RIGHT;
551 } else {
552 sug = SKIP_LEFT;
553 }
554 } else {
555 new_trap = 3;
556 if sgn(a) != sgn(b) {
557 sug = SYM;
558 } else if a_mag > b_mag || (a_mag == b_mag && a.abs() > b.abs()) {
559 sug = SPLIT_LEFT;
560 } else {
561 sug = SPLIT_RIGHT;
562 }
563 }
564 }
565 if self.consec_moves >= 3 && self.trapped == new_trap {
566 if self.last_move == MID_LEFT || self.last_move == NEAR_RIGHT {
567 sug = SPLIT_LEFT;
568 } else if self.last_move == MID_RIGHT || self.last_move == NEAR_LEFT {
569 sug = SPLIT_RIGHT;
570 }
571 self.consec_moves = 0;
573 }
574 suggestion = sug;
575 }
576
577 self.trapped = new_trap;
578 suggestion
579 }
580
581 fn eval(&mut self, strat: i32, set: &Set) -> Result<Cand, Error> {
583 let seq = self.seq;
584 self.seq += 1;
585 let mut s = Complex64::new(0.0, 0.0);
586
587 match strat {
588 GUESS => {
589 if self.high_guess < self.low_guess {
590 self.guess_param = 0.0;
591 } else if self.guess_param > 0.0 {
592 if self.high_guess > 0.0 {
593 self.guess_param = self.high_guess * 10.0;
594 } else {
595 self.guess_param = 1.0;
596 }
597 } else if self.low_guess < 0.0 {
598 self.guess_param = self.low_guess * 10.0;
599 } else {
600 self.guess_param = -1.0;
601 }
602 if self.guess_param > self.high_guess {
603 self.high_guess = self.guess_param;
604 }
605 if self.guess_param < self.low_guess {
606 self.low_guess = self.guess_param;
607 }
608 s.re = self.guess_param;
609 s.im = set[1].map_or(0.0, |i| self.t(i).s.im);
610 }
611 SYM | SYM2 => {
612 s = self.sym2(set);
613 let (s0, s1, s2) = (
615 set[0].map(|i| self.t(i).s),
616 set[1].map(|i| self.t(i).s),
617 set[2].map(|i| self.t(i).s),
618 );
619 if self.trapped == 1 {
620 let (a, b) = (s0.unwrap(), s1.unwrap());
621 if s.re < a.re || s.re > b.re {
622 s.re = (a.re + b.re) / 2.0;
623 }
624 } else if self.trapped == 2 {
625 let (b, c) = (s1.unwrap(), s2.unwrap());
626 if s.re < b.re || s.re > c.re {
627 s.re = (b.re + c.re) / 2.0;
628 }
629 } else if self.trapped == 3 {
630 let (a, b, c) = (s0.unwrap(), s1.unwrap(), s2.unwrap());
631 if s.re <= a.re || (s.re == b.re && s.im == b.im) || s.re >= c.re {
632 s.re = (a.re + c.re) / 2.0;
633 if s.re == b.re {
634 if self.last_move == MID_LEFT || self.last_move == NEAR_RIGHT {
635 s.re = (a.re + b.re) / 2.0;
636 } else {
637 s.re = (b.re + c.re) / 2.0;
638 }
639 }
640 }
641 }
642 }
643 COMPLEX_INIT => {
644 s.re = self.t(set[1].unwrap()).s.re;
645 if self.nipzk != 0.0 && self.nipzk_mag > -10 {
646 while self.nipzk_mag > 0 {
647 self.nipzk *= 2.0;
648 self.nipzk_mag -= 1;
649 }
650 while self.nipzk_mag < 0 {
651 self.nipzk /= 2.0;
652 self.nipzk_mag += 1;
653 }
654 s.im = self.nipzk;
655 } else {
656 s.im = 10000.0;
657 }
658 self.nipzk = 0.0;
659 self.nipzk_mag = 0;
660 }
661 COMPLEX_GUESS => {
662 s.re = self.t(set[0].unwrap()).s.re;
663 s.im = if set[2].is_none() { 1.0e8 } else { 1.0e12 };
664 }
665 MULLER => s = self.muller(set),
666 SPLIT_LEFT => {
667 s.re = (self.t(set[0].unwrap()).s.re + 2.0 * self.t(set[1].unwrap()).s.re) / 3.0;
668 }
669 SPLIT_RIGHT => {
670 s.re = (self.t(set[2].unwrap()).s.re + 2.0 * self.t(set[1].unwrap()).s.re) / 3.0;
671 }
672 _ => return Err(Error::Build(".pz: internal — unknown step type".into())),
673 }
674
675 Ok(Cand { s, seq })
676 }
677
678 #[allow(clippy::many_single_char_names)]
682 #[allow(clippy::if_same_then_else)] fn sym2(&mut self, set: &Set) -> Complex64 {
684 let t0 = self.t(set[0].unwrap()).clone();
685 let t1 = self.t(set[1].unwrap()).clone();
686 let t2 = self.t(set[2].unwrap()).clone();
687
688 let mut dx0 = t1.s.re - t0.s.re;
689 let mut dx1 = t2.s.re - t1.s.re;
690 let x0 = (t0.s.re + t1.s.re) / 2.0;
691 let mut d2x = (t2.s.re - t0.s.re) / 2.0;
692
693 let (mut a, mut a_mag) = zaddeq(t1.f_def.re, t1.mag_def, -t0.f_def.re, t0.mag_def);
694 let mut tmag = 0;
695 r_norm(&mut dx0, &mut tmag);
696 a /= dx0;
697 a_mag -= tmag;
698 r_norm(&mut a, &mut a_mag);
699
700 let (mut b, mut b_mag) = zaddeq(t2.f_def.re, t2.mag_def, -t1.f_def.re, t1.mag_def);
701 tmag = 0;
702 r_norm(&mut dx1, &mut tmag);
703 b /= dx1;
704 b_mag -= tmag;
705 r_norm(&mut b, &mut b_mag);
706
707 let (mut c, mut c_mag) = zaddeq(b, b_mag, -a, a_mag);
708 tmag = 0;
709 r_norm(&mut d2x, &mut tmag);
710 c /= d2x;
711 c_mag -= tmag;
712 r_norm(&mut c, &mut c_mag);
713
714 let mut out = Complex64::new(0.0, 0.0);
715
716 let first = a == 0.0 || c_mag < a_mag - 40;
720 let second = if first {
721 b = if c_mag < b_mag - 40 { 1.0 } else { 0.0 };
722 b != 0.0
723 } else {
724 false
725 };
726 if c == 0.0 || (first && second) {
727 if a == 0.0 {
729 a = b;
730 a_mag = b_mag;
731 }
732 if a != 0.0 {
733 out.re = -t1.f_def.re / a;
734 let mut m = a_mag - t1.mag_def;
735 while m > 0 {
738 out.re /= 2.0;
739 m -= 1;
740 }
741 while m < 0 {
742 out.re *= 2.0;
743 m += 1;
744 }
745 out.re += t1.s.re;
746 } else {
747 out.re = t1.s.re;
748 }
749 } else {
750 a /= c;
752 r_norm(&mut a, &mut a_mag);
753 a_mag -= c_mag;
754
755 let mut diff = t1.s.re - x0;
756 tmag = 0;
757 r_norm(&mut diff, &mut tmag);
758 let (na, na_mag) = zaddeq(a, a_mag, diff, tmag);
759 a = na;
760 a_mag = na_mag;
761
762 let mut bq = 2.0 * t1.f_def.re / c;
763 let mut bq_mag = t1.mag_def - c_mag;
764 r_norm(&mut bq, &mut bq_mag);
765
766 let mut disc = a * a;
767 let mut disc_mag = 2 * a_mag;
768 let (nd, nd_mag) = zaddeq(disc, disc_mag, -bq, bq_mag);
769 disc = nd;
770 disc_mag = nd_mag;
771
772 let mut new_mag = 0;
775 if disc < 0.0 {
776 disc = -disc;
777 new_mag = 1;
778 }
779 if disc_mag % 2 == 0 {
780 disc = disc.sqrt();
781 } else {
782 disc = (2.0 * disc).sqrt();
783 disc_mag -= 1;
784 }
785 disc_mag /= 2;
786
787 if new_mag != 0 {
788 if self.nipzk == 0.0 {
789 self.nipzk = disc;
790 self.nipzk_mag = disc_mag;
791 }
792 disc = 0.0;
793 disc_mag = 0;
794 }
795
796 let (mut cc, mut cc_mag);
797 if a * disc >= 0.0 {
798 let r = zaddeq(a, a_mag, disc, disc_mag);
799 cc = r.0;
800 cc_mag = r.1;
801 } else {
802 let r = zaddeq(a, a_mag, -disc, disc_mag);
803 cc = r.0;
804 cc_mag = r.1;
805 }
806
807 if cc != 0.0 {
809 bq /= cc;
810 bq_mag -= cc_mag;
811 } else {
812 bq = 0.0;
813 }
815
816 let (mut rb, mut rb_mag) = zaddeq(t1.s.re, 0, -bq, bq_mag);
817 let r = zaddeq(t1.s.re, 0, -cc, cc_mag);
818 cc = r.0;
819 cc_mag = r.1;
820
821 while rb_mag > 0 {
822 rb *= 2.0;
823 rb_mag -= 1;
824 }
825 while rb_mag < 0 {
826 rb /= 2.0;
827 rb_mag += 1;
828 }
829 while cc_mag > 0 {
830 cc *= 2.0;
831 cc_mag -= 1;
832 }
833 while cc_mag < 0 {
834 cc /= 2.0;
835 cc_mag += 1;
836 }
837
838 let in_range = |x: f64| x >= t0.s.re && x <= t2.s.re;
839 if !in_range(rb) {
840 if !in_range(cc) {
841 if self.trapped == 1 {
842 out.re = (t0.s.re + t1.s.re) / 2.0;
843 } else if self.trapped == 2 {
844 out.re = (t1.s.re + t2.s.re) / 2.0;
845 } else if self.trapped == 3 {
846 if (t1.s.re - cc).abs() < (t1.s.re - rb).abs() {
847 out.re = (t1.s.re + cc) / 2.0;
848 } else {
849 out.re = (t1.s.re + rb) / 2.0;
850 }
851 } else {
852 out.re = (t0.s.re + t2.s.re) / 2.0;
855 }
856 } else {
857 out.re = cc;
858 }
859 } else if !in_range(cc) {
860 out.re = rb;
861 } else if a > 0.0 {
862 out.re = rb;
863 } else {
864 out.re = cc;
865 }
866 }
867
868 out.im = 0.0;
869 out
870 }
871
872 fn muller(&mut self, set: &Set) -> Complex64 {
874 let t: Vec<Trial> = (0..3).map(|i| self.t(set[i].unwrap()).clone()).collect();
875
876 let mut min = -999999i32;
877 let mut j = 0i32;
878 let mut total = 0i32;
879 for ti in &t {
880 if ti.f_def.re != 0.0 || ti.f_def.im != 0.0 {
881 if min < ti.mag_def - 50 {
882 min = ti.mag_def - 50;
883 }
884 total += ti.mag_def;
885 j += 1;
886 }
887 }
888 let mut magx = if j != 0 { total / j } else { 0 };
889 if magx < min {
890 magx = min;
891 }
892
893 let mut scale = [1.0f64; 3];
894 for i in 0..3 {
895 let mut mag = t[i].mag_def - magx;
896 scale[i] = 1.0;
897 while mag > 0 {
898 scale[i] *= 2.0;
899 mag -= 1;
900 }
901 if mag < -90 {
902 scale[i] = 0.0;
903 } else {
904 while mag < 0 {
905 scale[i] /= 2.0;
906 mag += 1;
907 }
908 }
909 }
910
911 let h0 = t[0].s - t[1].s;
912 let h1 = t[1].s - t[2].s;
913 let lambda_i = h0 / h1;
916 let delta_i = lambda_i + 1.0;
917
918 let f0 = scale[0] * t[0].f_def;
919 let f1 = scale[1] * t[1].f_def;
920 let f2 = scale[2] * t[2].f_def;
921
922 let mut a = (f2 * lambda_i - f1 * delta_i + f0) * lambda_i;
923 let mut b = f2 * lambda_i * lambda_i - f1 * delta_i * delta_i + f0 * (lambda_i + delta_i);
924 let mut c = delta_i * f0;
925
926 while a.re.abs() > 1.0
927 || a.im.abs() > 1.0
928 || b.re.abs() > 1.0
929 || b.im.abs() > 1.0
930 || c.re.abs() > 1.0
931 || c.im.abs() > 1.0
932 {
933 a /= 2.0;
934 b /= 2.0;
935 c /= 2.0;
936 }
937
938 let mut d = b * b - 4.0 * a * c;
939 c_sqrt(&mut d);
940
941 let q = b.re * d.re + b.im * d.im;
943 if q > 0.0 {
944 b += d;
945 } else {
946 b -= d;
947 }
948
949 let lambda = c / (-0.5 * b);
950 t[0].s + h0 * lambda
951 }
952
953 fn run_trial(&mut self, cand: Cand, set: &Set) -> Result<usize, Error> {
957 let mut s = cand.s;
958 if s.im < 0.0 {
959 s.im = -s.im;
960 }
961
962 let mut pretest = false;
963 let mut shifted = false;
964 let mut def_mag: i32;
965 let mut def_frac: Complex64;
966 let mut prev: Option<usize>;
967 let mut matched: Option<usize>;
968 let mut hit: Option<usize>;
969
970 loop {
971 def_mag = 0;
972 def_frac = Complex64::new(1.0, 0.0);
973 let was_shifted = shifted;
974 shifted = false;
975 prev = None;
976 matched = None;
977 hit = None;
978
979 let mut cur = self.head;
980 while let Some(i) = cur {
981 let p = self.nodes[i].clone();
982 cur = p.next;
983
984 let diff = p.s - s;
985 if diff.re < 0.0 || (diff.re == 0.0 && diff.im < 0.0) {
986 prev = Some(i);
987 }
988
989 let (abstol, reltol) = if p.flags & ISAROOT != 0 {
990 (1e-5, 1e-6)
991 } else {
992 (1e-20, 1e-12)
993 };
994
995 if diff.im == 0.0 && diff.re.abs() / (p.s.re.abs() + abstol / reltol) < reltol {
996 if was_shifted || p.count >= 3 || !self.alter(&mut s, set[1], abstol, reltol) {
997 self.nodes[i].count = 0;
998 pretest = true;
999 hit = Some(i);
1000 break;
1001 } else {
1002 self.nodes[i].count += 1;
1003 }
1004 shifted = true;
1005 break;
1006 } else {
1007 if self.trapped == 0 {
1008 self.nodes[i].count = 0;
1009 }
1010 if p.flags & ISAROOT != 0 {
1011 let mut diff_mag = 0i32;
1012 let mut df = diff;
1013 c_norm(&mut df, &mut diff_mag);
1014 if df.im != 0.0 {
1015 df = Complex64::new(df.re * df.re + df.im * df.im, 0.0);
1016 diff_mag *= 2;
1017 }
1018 c_norm(&mut df, &mut diff_mag);
1019 for _ in 0..p.multiplicity {
1020 def_frac *= df;
1021 def_mag += diff_mag;
1022 c_norm(&mut def_frac, &mut def_mag);
1023 }
1024 } else if matched.is_none() {
1025 matched = Some(i);
1026 }
1027 }
1028 }
1029
1030 if !shifted {
1031 break;
1032 }
1033 }
1034
1035 let mut singular = false;
1036 if pretest {
1037 let i = hit.unwrap();
1038 let hflags = self.nodes[i].flags;
1039 if hflags & ISAROOT == 0
1040 && self.trapped == 3
1041 && self.nipzk != 0.0
1042 && self.nipzk_mag > -10
1043 {
1044 self.nodes[i].flags |= ISAMINIMA;
1045 return Ok(i);
1046 } else if hflags & ISAROOT != 0 {
1047 self.nodes[i].flags |= ISAREPEAT;
1048 self.nodes[i].multiplicity += 1;
1049 return Ok(i);
1050 } else {
1051 singular = true;
1053 }
1054 }
1055
1056 let mut f_raw = Complex64::new(0.0, 0.0);
1059 let mut mag_raw = 0i32;
1060 if !pretest {
1061 let (det, mag) = self.eng.pz_det(self.setup, s)?;
1062 f_raw = det;
1063 mag_raw = mag;
1064 }
1065
1066 let mut trial = Trial {
1067 s,
1068 f_raw,
1069 mag_raw,
1070 f_def: f_raw,
1071 mag_def: mag_raw,
1072 multiplicity: 0,
1073 count: 0,
1074 seq: cand.seq,
1075 flags: 0,
1076 prev: None,
1077 next: None,
1078 dead: false,
1079 };
1080
1081 if singular || (trial.f_raw.re == 0.0 && trial.f_raw.im == 0.0) {
1082 trial.f_raw = Complex64::new(0.0, 0.0);
1083 trial.mag_raw = 0;
1084 trial.f_def = Complex64::new(0.0, 0.0);
1085 trial.mag_def = 0;
1086 trial.flags = ISAROOT;
1087 } else {
1088 trial.f_def = trial.f_raw / def_frac;
1090 trial.mag_def = trial.mag_raw - def_mag;
1091 c_norm(&mut trial.f_def, &mut trial.mag_def);
1092 }
1093
1094 let idx = self.nodes.len();
1096 let is_root = trial.flags & ISAROOT != 0;
1097 match prev {
1098 Some(p) => {
1099 trial.next = self.nodes[p].next;
1100 trial.prev = Some(p);
1101 if let Some(nx) = self.nodes[p].next {
1102 self.nodes[nx].prev = Some(idx);
1103 }
1104 self.nodes.push(trial);
1105 self.nodes[p].next = Some(idx);
1106 }
1107 None => {
1108 trial.next = self.head;
1109 trial.prev = None;
1110 if let Some(h) = self.head {
1111 self.nodes[h].prev = Some(idx);
1112 } else {
1113 self.zero_trial = Some(idx);
1114 }
1115 self.nodes.push(trial);
1116 self.head = Some(idx);
1117 }
1118 }
1119 self.ntrials += 1;
1120
1121 if !is_root {
1122 if let Some(m) = matched {
1123 self.check_flat(m, idx);
1124 } else {
1125 self.nflat = 1;
1126 }
1127 }
1128
1129 Ok(idx)
1130 }
1131
1132 fn verify(&mut self, root: usize) {
1135 self.nzeros += 1;
1136 if self.nodes[root].s.im != 0.0 {
1137 self.nzeros += 1;
1138 }
1139 self.nflat = 0;
1140
1141 if self.nodes[root].multiplicity == 0 {
1142 self.nodes[root].flags |= ISAROOT;
1143 self.nodes[root].multiplicity = 1;
1144 }
1145
1146 let root_s = self.nodes[root].s;
1147 let mut prev: Option<usize> = None;
1148 let mut cur = self.head;
1149 while let Some(i) = cur {
1150 let next = self.nodes[i].next;
1151 if self.nodes[i].flags & ISAROOT != 0 {
1152 prev = Some(i);
1153 cur = next;
1154 continue;
1155 }
1156
1157 let mut diff = root_s - self.nodes[i].s;
1158 if root_s.im != 0.0 {
1159 diff = Complex64::new(diff.re * diff.re + diff.im * diff.im, 0.0);
1160 }
1161 let tdiff = diff.re;
1162 if diff.re != 0.0 {
1163 let mut diff_mag = 0i32;
1164 c_norm(&mut diff, &mut diff_mag);
1165 diff_mag = -diff_mag;
1166 let f = self.nodes[i].f_def / diff;
1167 let mut f = f;
1168 let mut m = diff_mag;
1169 c_norm(&mut f, &mut m);
1170 self.nodes[i].f_def = f;
1171 self.nodes[i].mag_def += m;
1172 }
1173
1174 if self.nodes[i].s.im != 0.0 || tdiff.abs() / (root_s.re.abs() + 200.0) < 0.005 {
1175 if let Some(p) = prev {
1177 self.nodes[p].next = next;
1178 }
1179 if let Some(nx) = next {
1180 self.nodes[nx].prev = prev;
1181 }
1182 self.ntrials -= 1;
1183 if self.zero_trial == Some(i) {
1184 self.zero_trial = next.or(self.nodes[i].prev);
1185 }
1186 if self.head == Some(i) {
1187 self.head = next;
1188 }
1189 self.nodes[i].dead = true;
1190 } else {
1191 if let Some(p) = prev {
1192 self.check_flat(p, i);
1193 } else {
1194 self.nflat = 1;
1195 }
1196 self.nodes[i].flags &= !ISAMINIMA;
1197 prev = Some(i);
1198 }
1199 cur = next;
1200 }
1201 }
1202
1203 fn update_set(&mut self, set: &mut Set, new: usize) {
1205 let mut this_move = 0;
1206 let n = self.nodes[new].clone();
1207
1208 if n.s.im != 0.0 {
1209 set[2] = set[1];
1210 set[1] = set[0];
1211 set[0] = Some(new);
1212 } else if set[1].is_none() {
1213 set[1] = Some(new);
1214 } else if set[2].is_none() && n.s.re > self.t(set[1].unwrap()).s.re {
1215 set[2] = Some(new);
1216 } else if set[0].is_none() {
1217 set[0] = Some(new);
1218 } else if n.flags & ISAMINIMA != 0 {
1219 set[1] = Some(new);
1220 } else if n.s.re < self.t(set[0].unwrap()).s.re {
1221 set[2] = set[1];
1222 set[1] = set[0];
1223 set[0] = Some(new);
1224 this_move = FAR_LEFT;
1225 } else if n.s.re < self.t(set[1].unwrap()).s.re {
1226 let t1 = self.t(set[1].unwrap());
1227 if self.trapped == 0
1228 || n.mag_def < t1.mag_def
1229 || (n.mag_def == t1.mag_def && n.f_def.re.abs() < t1.f_def.re.abs())
1230 {
1231 set[2] = set[1];
1232 set[1] = Some(new);
1233 this_move = MID_LEFT;
1234 } else {
1235 set[0] = Some(new);
1236 this_move = NEAR_LEFT;
1237 }
1238 } else if set[2].is_some() && n.s.re < self.t(set[2].unwrap()).s.re {
1239 let t1 = self.t(set[1].unwrap());
1240 if self.trapped == 0
1241 || n.mag_def < t1.mag_def
1242 || (n.mag_def == t1.mag_def && n.f_def.re.abs() < t1.f_def.re.abs())
1243 {
1244 set[0] = set[1];
1245 set[1] = Some(new);
1246 this_move = MID_RIGHT;
1247 } else {
1248 set[2] = Some(new);
1249 this_move = NEAR_RIGHT;
1250 }
1251 } else {
1252 set[0] = set[1];
1253 set[1] = set[2];
1254 set[2] = Some(new);
1255 this_move = FAR_RIGHT;
1256 }
1257
1258 if self.trapped != 0 && this_move == self.last_move {
1259 self.consec_moves += 1;
1260 } else {
1261 self.consec_moves = 0;
1262 }
1263 self.last_move = this_move;
1264 }
1265
1266 fn alter(
1268 &mut self,
1269 s: &mut Complex64,
1270 nearto: Option<usize>,
1271 abstol: f64,
1272 reltol: f64,
1273 ) -> bool {
1274 let Some(nt) = nearto else {
1275 return false;
1276 };
1277 let near = self.nodes[nt].clone();
1278
1279 let p1 = if self.trapped != 2 {
1280 let mut p1 = near.s.re;
1281 if near.flags & ISAROOT != 0 {
1282 p1 -= 1e-6 * near.s.re + 1e-5;
1283 }
1284 if let Some(pv) = near.prev {
1285 p1 += self.nodes[pv].s.re;
1286 } else {
1287 p1 -= 10.0 * (p1.abs() + 1.0);
1288 }
1289 p1 / 2.0
1290 } else {
1291 near.s.re
1292 };
1293
1294 let p2 = if self.trapped != 1 {
1295 let mut p2 = near.s.re;
1296 if near.flags & ISAROOT != 0 {
1297 p2 += 1e-6 * near.s.re + 1e-5;
1298 }
1299 if let Some(nx) = near.next {
1300 p2 += self.nodes[nx].s.re;
1301 } else {
1302 p2 += 10.0 * (p2.abs() + 1.0);
1303 }
1304 p2 / 2.0
1305 } else {
1306 near.s.re
1307 };
1308
1309 let close =
1310 |x: f64, other: f64| (x - other).abs() / (other.abs() + abstol / reltol) < reltol;
1311 let bail_prev = near.prev.is_some_and(|pv| close(p1, self.nodes[pv].s.re));
1312 let bail_next = near.next.is_some_and(|nx| close(p2, self.nodes[nx].s.re));
1313 if bail_prev || bail_next {
1314 return false;
1315 }
1316
1317 if self.trapped != 2 && near.s.re - p1 > p2 - near.s.re {
1318 s.re = p1;
1319 } else {
1320 s.re = p2;
1321 }
1322 true
1323 }
1324
1325 fn check_flat(&mut self, a: usize, b: usize) {
1327 let ta = &self.nodes[a];
1328 let tb = &self.nodes[b];
1329 let diff_mag = ta.mag_def - tb.mag_def;
1330 if diff_mag.abs() <= 1 {
1331 let mult = match diff_mag {
1332 1 => 2.0,
1333 -1 => 0.5,
1334 _ => 1.0,
1335 };
1336 let diff = mult * ta.f_def - tb.f_def;
1337 let mag2 = diff.re * diff.re + diff.im * diff.im;
1338 if mag2 < 1.0e-20 {
1339 self.nflat += 1;
1340 }
1341 }
1342 }
1343}