1use std::collections::BTreeMap;
4
5use crate::complex::{C64, canonical};
6use crate::series::{Line, Rung};
7
8#[derive(Clone, Debug, PartialEq)]
11pub struct Run {
12 pub offset: f64,
13 pub step: f64,
14 pub first: i64,
15 pub amps: Vec<C64>,
16 pub mirror: Mirror,
17}
18
19#[derive(Clone, Debug, PartialEq)]
21pub enum Mirror {
22 None,
23 Conjugate,
24 Held(Vec<C64>),
25}
26
27impl Run {
28 pub fn of(lines: &[Line]) -> Vec<Run> {
30 let mut runs: Vec<Run> = Vec::new();
31 let mut open: BTreeMap<(u64, u64), usize> = BTreeMap::new();
32 for line in lines {
33 let rung = line.rung.unwrap_or(Rung {
34 offset: line.hz,
35 step: 0.0,
36 k: 0,
37 });
38 let key = (canonical(rung.offset), canonical(rung.step));
39 match open.get(&key) {
40 Some(&at) if rung.step != 0.0 && runs[at].next() == Some(rung.k) => {
41 runs[at].amps.push(line.amp);
42 }
43 _ => {
44 open.insert(key, runs.len());
45 runs.push(Run {
46 offset: rung.offset,
47 step: rung.step,
48 first: rung.k,
49 amps: vec![line.amp],
50 mirror: Mirror::None,
51 });
52 }
53 }
54 }
55 paired(runs)
56 }
57
58 pub fn len(&self) -> usize {
60 match &self.mirror {
61 Mirror::None => self.amps.len(),
62 Mirror::Conjugate => 2 * self.amps.len(),
63 Mirror::Held(amps) => self.amps.len() + amps.len(),
64 }
65 }
66
67 pub fn is_empty(&self) -> bool {
68 self.amps.is_empty()
69 }
70
71 pub fn mirrored(&self) -> Option<Vec<C64>> {
72 match &self.mirror {
73 Mirror::None => None,
74 Mirror::Conjugate => Some(self.amps.iter().map(|a| a.conj()).collect()),
75 Mirror::Held(amps) => Some(amps.clone()),
76 }
77 }
78
79 pub fn lines(&self) -> Vec<Line> {
80 let at = |i: usize, sign: f64, amp: C64| {
81 let k = self.first + i as i64;
82 let rung = Rung {
83 offset: sign * self.offset,
84 step: sign * self.step,
85 k,
86 };
87 Line {
88 hz: rung.offset + rung.step * k as f64,
89 amp,
90 rung: Some(rung),
91 }
92 };
93 let mut out: Vec<Line> = self
94 .amps
95 .iter()
96 .enumerate()
97 .map(|(i, a)| at(i, 1.0, *a))
98 .collect();
99 if let Some(mirror) = self.mirrored() {
100 out.extend(mirror.into_iter().enumerate().map(|(i, a)| at(i, -1.0, a)));
101 }
102 out
103 }
104
105 fn next(&self) -> Option<i64> {
106 self.first.checked_add(self.amps.len() as i64)
107 }
108
109 fn key(&self, sign: f64) -> (u64, u64, i64, usize) {
110 let (offset, step) = (sign * self.offset, sign * self.step);
111 (
112 canonical(offset),
113 canonical(step),
114 self.first,
115 self.amps.len(),
116 )
117 }
118}
119
120fn paired(runs: Vec<Run>) -> Vec<Run> {
122 let mut waiting: BTreeMap<(u64, u64, i64, usize), Vec<usize>> = BTreeMap::new();
123 for (at, run) in runs.iter().enumerate().rev() {
124 waiting.entry(run.key(1.0)).or_default().push(at);
125 }
126 let mut slots: Vec<Option<Run>> = runs.into_iter().map(Some).collect();
127 let mut out = Vec::with_capacity(slots.len());
128 for at in 0..slots.len() {
129 let Some(mut run) = slots[at].take() else {
130 continue;
131 };
132 let partner = waiting.get_mut(&run.key(-1.0)).and_then(|queue| {
133 while let Some(&next) = queue.last() {
134 match next > at && slots[next].is_some() {
135 true => return Some(next),
136 false => queue.pop(),
137 };
138 }
139 None
140 });
141 if let Some(other) = partner.and_then(|found| slots[found].take()) {
142 let pairs = run.amps.iter().zip(&other.amps);
143 run.mirror = match pairs.clone().all(|(a, b)| conjugate(*a, *b)) {
144 true => Mirror::Conjugate,
145 false => Mirror::Held(other.amps),
146 };
147 }
148 out.push(run);
149 }
150 out
151}
152
153fn conjugate(a: C64, b: C64) -> bool {
154 a.re.to_bits() == b.re.to_bits() && (-a.im).to_bits() == b.im.to_bits()
155}