Skip to main content

mig_assembly/
transaction_view.rs

1//! One transaction of a message, as if the message held no other.
2//!
3//! A UTILMD message repeats SG4, one per transaction (`IDE+24`), and the AHB
4//! states its conditions per transaction: "Wenn SG4 STS+7++ZG9 vorhanden" asks
5//! about the transaction the annotated field sits in. Asked of the whole
6//! message, another transaction answers. A list PID (55065, 55195, …) adds an
7//! SG4 of another kind, the list itself (`IDE+Z01`), which the transactions'
8//! conditions do refer to ("Wenn im SG4 IDE+Z01 … STS+E01 nicht vorhanden").
9//!
10//! [`TransactionView`] keeps the repetition of the transaction group asked
11//! about, first, and every repetition of another kind; it leaves out the other
12//! repetitions of the same kind.
13
14use std::collections::HashMap;
15
16use crate::assembler::{AssembledGroup, AssembledGroupInstance, AssembledSegment, AssembledTree};
17use crate::tokenize::OwnedSegment;
18
19/// [`AssembledTree::transaction_view`]'s result.
20#[derive(Debug, Clone)]
21pub struct TransactionView {
22    /// The message with the other transactions of the same kind left out. The
23    /// transaction itself is repetition 0 of its group.
24    pub tree: AssembledTree,
25    /// Segment numbers (`AssembledSegment::segment_number`) of everything left
26    /// out, sorted.
27    pub left_out: Vec<u32>,
28}
29
30/// The kind of a repetition: its entry segment's tag and qualifier
31/// (`IDE`, `24`).
32fn kind(instance: &AssembledGroupInstance) -> Option<(&str, &str)> {
33    let entry = instance.segments.first()?;
34    let qualifier = entry
35        .elements
36        .first()
37        .and_then(|e| e.first())
38        .map_or("", String::as_str);
39    Some((entry.tag.as_str(), qualifier))
40}
41
42fn segment_numbers(instance: &AssembledGroupInstance, out: &mut Vec<u32>) {
43    let own = instance.segments.iter().chain(&instance.skipped_segments);
44    out.extend(own.filter_map(|s: &AssembledSegment| s.segment_number));
45    for group in &instance.child_groups {
46        for repetition in &group.repetitions {
47            segment_numbers(repetition, out);
48        }
49    }
50}
51
52impl AssembledTree {
53    /// The message as transaction `index` of the top-level group `group_id`
54    /// sees it (see the [module](crate::transaction_view)). `None` when the
55    /// group has no such repetition.
56    pub fn transaction_view(&self, group_id: &str, index: usize) -> Option<TransactionView> {
57        let position = self.groups.iter().position(|g| g.group_id == group_id)?;
58        let group = &self.groups[position];
59        let own = group.repetitions.get(index)?;
60        let own_kind = kind(own);
61        let mut repetitions = vec![own.clone()];
62        let mut left_out = Vec::new();
63        for (i, repetition) in group.repetitions.iter().enumerate() {
64            if i == index {
65                continue;
66            }
67            if kind(repetition) == own_kind {
68                segment_numbers(repetition, &mut left_out);
69            } else {
70                repetitions.push(repetition.clone());
71            }
72        }
73        left_out.sort_unstable();
74        let mut groups = Vec::with_capacity(self.groups.len());
75        for (i, g) in self.groups.iter().enumerate() {
76            if i == position {
77                let mut view = g.clone_shallow();
78                view.repetitions = std::mem::take(&mut repetitions);
79                groups.push(view);
80            } else {
81                groups.push(g.clone());
82            }
83        }
84        Some(TransactionView {
85            tree: AssembledTree {
86                segments: self.segments.clone(),
87                groups,
88                post_group_start: self.post_group_start,
89                inter_group_segments: self.inter_group_segments.clone(),
90            },
91            left_out,
92        })
93    }
94}
95
96/// One transaction's view together with the message segments it keeps:
97/// [`TransactionViews::view`]'s result.
98#[derive(Debug, Clone)]
99pub struct TransactionSegmentsView {
100    /// As [`TransactionView::tree`].
101    pub tree: AssembledTree,
102    /// The message's segments without those of the other transactions of
103    /// the same kind, in message order.
104    pub segments: Vec<OwnedSegment>,
105}
106
107/// Every transaction view of a message, prepared once.
108///
109/// [`AssembledTree::transaction_view`] finds what a view leaves out by
110/// walking every other transaction, and the caller then filters the whole
111/// message by it: one view costs as much as the message, all of them its
112/// square — 1,600 UTILMD transactions took 1.6 s for the views alone. This
113/// sorts each segment once into what every view of a kind keeps and what
114/// only one transaction's view keeps, so a view costs its own transaction
115/// plus what the transactions share (the header, SG2, ...).
116#[derive(Debug)]
117pub struct TransactionViews<'t> {
118    tree: &'t AssembledTree,
119    segments: &'t [OwnedSegment],
120    groups: Vec<TransactionGroupIndex>,
121}
122
123#[derive(Debug)]
124struct TransactionGroupIndex {
125    /// The group's position in `AssembledTree::groups`.
126    position: usize,
127    /// Per repetition: its kind, an index into the `per_kind` vectors.
128    kind_of: Vec<usize>,
129    /// Per kind: the repetitions of the other kinds, in order.
130    others: Vec<Vec<usize>>,
131    /// Per kind: the indices into the message segments every view of that
132    /// kind keeps — no repetition of the kind holds them.
133    shared: Vec<Vec<usize>>,
134    /// Per repetition: the indices of the message segments it holds alone
135    /// among the repetitions of its kind.
136    own: Vec<Vec<usize>>,
137}
138
139impl AssembledTree {
140    /// Prepares the views [`transaction_view`](Self::transaction_view) gives,
141    /// of every top-level group `is_transaction_group` accepts, over the
142    /// message `segments` the tree was assembled from (matched by
143    /// `segment_number`).
144    pub fn transaction_views<'t>(
145        &'t self,
146        segments: &'t [OwnedSegment],
147        is_transaction_group: impl Fn(&str) -> bool,
148    ) -> TransactionViews<'t> {
149        let groups = self
150            .groups
151            .iter()
152            .enumerate()
153            .filter(|(_, g)| is_transaction_group(&g.group_id))
154            .map(|(position, g)| TransactionGroupIndex::new(position, g, segments))
155            .collect();
156        TransactionViews {
157            tree: self,
158            segments,
159            groups,
160        }
161    }
162}
163
164impl TransactionGroupIndex {
165    fn new(position: usize, group: &AssembledGroup, segments: &[OwnedSegment]) -> Self {
166        let mut kinds: Vec<Option<(&str, &str)>> = Vec::new();
167        let kind_of: Vec<usize> = group
168            .repetitions
169            .iter()
170            .map(|repetition| {
171                let k = kind(repetition);
172                kinds
173                    .iter()
174                    .position(|known| *known == k)
175                    .unwrap_or_else(|| {
176                        kinds.push(k);
177                        kinds.len() - 1
178                    })
179            })
180            .collect();
181        let others = (0..kinds.len())
182            .map(|k| (0..kind_of.len()).filter(|&r| kind_of[r] != k).collect())
183            .collect();
184
185        // Which repetitions hold each segment number.
186        let mut holders: HashMap<u32, Vec<usize>> = HashMap::new();
187        let mut numbers = Vec::new();
188        for (r, repetition) in group.repetitions.iter().enumerate() {
189            numbers.clear();
190            segment_numbers(repetition, &mut numbers);
191            for &n in &numbers {
192                let held = holders.entry(n).or_default();
193                if held.last() != Some(&r) {
194                    held.push(r);
195                }
196            }
197        }
198
199        // A view of repetition `r` keeps a segment unless another repetition
200        // of `r`'s kind holds it.
201        let mut shared = vec![Vec::new(); kinds.len()];
202        let mut own = vec![Vec::new(); kind_of.len()];
203        for (i, segment) in segments.iter().enumerate() {
204            let held = holders
205                .get(&segment.segment_number)
206                .map_or(&[][..], Vec::as_slice);
207            for (k, shared) in shared.iter_mut().enumerate() {
208                let mut of_kind = held.iter().filter(|&&r| kind_of[r] == k);
209                match of_kind.next() {
210                    None => shared.push(i),
211                    Some(&r) if of_kind.all(|&other| other == r) => own[r].push(i),
212                    // Held by two repetitions of the kind: every view of
213                    // the kind leaves it out.
214                    Some(_) => {}
215                }
216            }
217        }
218        Self {
219            position,
220            kind_of,
221            others,
222            shared,
223            own,
224        }
225    }
226}
227
228impl TransactionViews<'_> {
229    /// Transaction `index` of the top-level group `group_id`, as
230    /// [`AssembledTree::transaction_view`] gives it, with the message
231    /// segments it keeps. `None` when the group is no transaction group or
232    /// has no such repetition.
233    pub fn view(&self, group_id: &str, index: usize) -> Option<TransactionSegmentsView> {
234        let index_of = self
235            .groups
236            .iter()
237            .find(|g| self.tree.groups[g.position].group_id == group_id)?;
238        let group = &self.tree.groups[index_of.position];
239        let own = group.repetitions.get(index)?;
240        let k = index_of.kind_of[index];
241
242        let mut repetitions = Vec::with_capacity(1 + index_of.others[k].len());
243        repetitions.push(own.clone());
244        repetitions.extend(
245            index_of.others[k]
246                .iter()
247                .map(|&r| group.repetitions[r].clone()),
248        );
249        let groups = self
250            .tree
251            .groups
252            .iter()
253            .enumerate()
254            .map(|(i, g)| {
255                if i == index_of.position {
256                    AssembledGroup {
257                        group_id: g.group_id.clone(),
258                        repetitions: std::mem::take(&mut repetitions),
259                    }
260                } else {
261                    g.clone()
262                }
263            })
264            .collect();
265
266        // Both lists ascend; merged, they keep the message order.
267        let (shared, own) = (&index_of.shared[k], &index_of.own[index]);
268        let mut segments = Vec::with_capacity(shared.len() + own.len());
269        let (mut s, mut o) = (0, 0);
270        while s < shared.len() || o < own.len() {
271            let next = if o == own.len() || (s < shared.len() && shared[s] < own[o]) {
272                s += 1;
273                shared[s - 1]
274            } else {
275                o += 1;
276                own[o - 1]
277            };
278            segments.push(self.segments[next].clone());
279        }
280
281        Some(TransactionSegmentsView {
282            tree: AssembledTree {
283                segments: self.tree.segments.clone(),
284                groups,
285                post_group_start: self.tree.post_group_start,
286                inter_group_segments: self.tree.inter_group_segments.clone(),
287            },
288            segments,
289        })
290    }
291}
292
293impl AssembledGroup {
294    /// The group without its repetitions.
295    fn clone_shallow(&self) -> Self {
296        Self {
297            group_id: self.group_id.clone(),
298            repetitions: Vec::new(),
299        }
300    }
301}
302
303#[cfg(test)]
304mod tests {
305    use super::*;
306    use crate::assembler::AssembledGroup;
307
308    fn seg(text: &str, number: u32) -> AssembledSegment {
309        let mut parts = text.split('+');
310        AssembledSegment {
311            tag: parts.next().unwrap().to_string(),
312            elements: parts.map(|e| vec![e.to_string()]).collect(),
313            mig_number: None,
314            segment_number: Some(number),
315        }
316    }
317
318    fn inst(
319        segments: Vec<AssembledSegment>,
320        child_groups: Vec<AssembledGroup>,
321    ) -> AssembledGroupInstance {
322        AssembledGroupInstance {
323            segments,
324            child_groups,
325            entry_mig_number: None,
326            variant_mig_numbers: vec![],
327            skipped_segments: vec![],
328            skipped_positions: vec![],
329        }
330    }
331
332    fn sg8(seq: &str, number: u32) -> AssembledGroup {
333        AssembledGroup {
334            group_id: "SG8".into(),
335            repetitions: vec![inst(vec![seg(seq, number)], vec![])],
336        }
337    }
338
339    /// BGM, SG2, then SG4: a list (`IDE+Z01`) and three transactions.
340    fn message() -> AssembledTree {
341        AssembledTree {
342            segments: vec![seg("BGM+E01", 1)],
343            groups: vec![
344                AssembledGroup {
345                    group_id: "SG2".into(),
346                    repetitions: vec![inst(vec![seg("NAD+MS", 2)], vec![])],
347                },
348                AssembledGroup {
349                    group_id: "SG4".into(),
350                    repetitions: vec![
351                        inst(vec![seg("IDE+Z01", 3)], vec![]),
352                        inst(vec![seg("IDE+24", 4)], vec![sg8("SEQ+Z01", 5)]),
353                        inst(vec![seg("IDE+24", 6)], vec![sg8("SEQ+Z02", 7)]),
354                        inst(vec![seg("IDE+24", 8)], vec![sg8("SEQ+Z03", 9)]),
355                    ],
356                },
357            ],
358            post_group_start: 1,
359            inter_group_segments: Default::default(),
360        }
361    }
362
363    fn entries(tree: &AssembledTree) -> Vec<String> {
364        tree.groups[1]
365            .repetitions
366            .iter()
367            .map(|r| format!("{}+{}", r.segments[0].tag, r.segments[0].elements[0][0]))
368            .collect()
369    }
370
371    #[test]
372    fn a_transaction_sees_itself_first_and_the_list_but_no_other_transaction() {
373        let view = message().transaction_view("SG4", 2).unwrap();
374        assert_eq!(entries(&view.tree), ["IDE+24", "IDE+Z01"]);
375        assert_eq!(
376            view.tree.groups[1].repetitions[0].segments[0].segment_number,
377            Some(6)
378        );
379        assert_eq!(view.left_out, [4, 5, 8, 9]);
380        // What is not the transaction group is kept as it is.
381        assert_eq!(view.tree.groups[0], message().groups[0]);
382        assert_eq!(view.tree.segments, message().segments);
383    }
384
385    #[test]
386    fn the_list_sees_every_transaction() {
387        let view = message().transaction_view("SG4", 0).unwrap();
388        assert_eq!(
389            entries(&view.tree),
390            ["IDE+Z01", "IDE+24", "IDE+24", "IDE+24"]
391        );
392        assert!(view.left_out.is_empty());
393    }
394
395    /// The message's segments in order, numbered as the tree numbers them,
396    /// plus one no group holds (UNT).
397    fn segments(tree: &AssembledTree) -> Vec<OwnedSegment> {
398        fn collect(instance: &AssembledGroupInstance, out: &mut Vec<OwnedSegment>) {
399            for s in &instance.segments {
400                out.push(OwnedSegment {
401                    id: s.tag.clone(),
402                    elements: s.elements.clone(),
403                    segment_number: s.segment_number.unwrap(),
404                });
405            }
406            for g in &instance.child_groups {
407                g.repetitions.iter().for_each(|r| collect(r, out));
408            }
409        }
410        let mut out: Vec<OwnedSegment> = Vec::new();
411        let root = inst(tree.segments.clone(), tree.groups.clone());
412        collect(&root, &mut out);
413        out.push(OwnedSegment {
414            id: "UNT".into(),
415            elements: vec![],
416            segment_number: 10,
417        });
418        out.sort_by_key(|s| s.segment_number);
419        out
420    }
421
422    #[test]
423    fn prepared_views_are_the_views_with_the_segments_they_keep() {
424        let tree = message();
425        let all = segments(&tree);
426        let views = tree.transaction_views(&all, |g| g == "SG4");
427        for index in 0..4 {
428            let expected = tree.transaction_view("SG4", index).unwrap();
429            let view = views.view("SG4", index).unwrap();
430            assert_eq!(view.tree.groups, expected.tree.groups, "view {index}");
431            assert_eq!(view.tree.segments, expected.tree.segments);
432            let kept: Vec<OwnedSegment> = all
433                .iter()
434                .filter(|s| expected.left_out.binary_search(&s.segment_number).is_err())
435                .cloned()
436                .collect();
437            assert_eq!(view.segments, kept, "view {index}");
438        }
439        let numbers = |index| -> Vec<u32> {
440            let view = views.view("SG4", index).unwrap();
441            view.segments.iter().map(|s| s.segment_number).collect()
442        };
443        assert_eq!(numbers(2), [1, 2, 3, 6, 7, 10]);
444        assert_eq!(numbers(0), (1..=10).collect::<Vec<_>>());
445        assert!(views.view("SG4", 4).is_none());
446        assert!(views.view("SG2", 0).is_none());
447    }
448
449    #[test]
450    fn no_such_group_or_repetition_is_no_view() {
451        assert!(message().transaction_view("SG5", 0).is_none());
452        assert!(message().transaction_view("SG4", 4).is_none());
453    }
454}