Skip to main content

sva_formula/
run.rs

1// Concern: gathers lines on one ladder into runs, each summed as a unit | Non-concern: evaluating a run or bounding its rounding (sva-samples) | IO: (&[Line]) -> Vec<Run>
2
3use std::collections::BTreeMap;
4
5use crate::complex::{C64, canonical};
6use crate::series::{Line, Rung};
7
8/// Lines at exactly `offset + step*k` Hz for `k` in `first..first + amps.len()`, and where
9/// `mirror` holds one, the same ladder negated.
10#[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/// `Conjugate` where each amplitude is bit for bit its partner's conjugate.
20#[derive(Clone, Debug, PartialEq)]
21pub enum Mirror {
22    None,
23    Conjugate,
24    Held(Vec<C64>),
25}
26
27impl Run {
28    /// Loose lines are runs of one; a ladder breaks wherever its index skips.
29    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    /// Lines held, the mirror's included.
59    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
120/// Each run folds in the first later one on its negated ladder over the same indices.
121fn 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}