use std::collections::HashMap;
use crate::assembler::{AssembledGroup, AssembledGroupInstance, AssembledSegment, AssembledTree};
use crate::tokenize::OwnedSegment;
#[derive(Debug, Clone)]
pub struct TransactionView {
pub tree: AssembledTree,
pub left_out: Vec<u32>,
}
fn kind(instance: &AssembledGroupInstance) -> Option<(&str, &str)> {
let entry = instance.segments.first()?;
let qualifier = entry
.elements
.first()
.and_then(|e| e.first())
.map_or("", String::as_str);
Some((entry.tag.as_str(), qualifier))
}
fn segment_numbers(instance: &AssembledGroupInstance, out: &mut Vec<u32>) {
let own = instance.segments.iter().chain(&instance.skipped_segments);
out.extend(own.filter_map(|s: &AssembledSegment| s.segment_number));
for group in &instance.child_groups {
for repetition in &group.repetitions {
segment_numbers(repetition, out);
}
}
}
impl AssembledTree {
pub fn transaction_view(&self, group_id: &str, index: usize) -> Option<TransactionView> {
let position = self.groups.iter().position(|g| g.group_id == group_id)?;
let group = &self.groups[position];
let own = group.repetitions.get(index)?;
let own_kind = kind(own);
let mut repetitions = vec![own.clone()];
let mut left_out = Vec::new();
for (i, repetition) in group.repetitions.iter().enumerate() {
if i == index {
continue;
}
if kind(repetition) == own_kind {
segment_numbers(repetition, &mut left_out);
} else {
repetitions.push(repetition.clone());
}
}
left_out.sort_unstable();
let mut groups = Vec::with_capacity(self.groups.len());
for (i, g) in self.groups.iter().enumerate() {
if i == position {
let mut view = g.clone_shallow();
view.repetitions = std::mem::take(&mut repetitions);
groups.push(view);
} else {
groups.push(g.clone());
}
}
Some(TransactionView {
tree: AssembledTree {
segments: self.segments.clone(),
groups,
post_group_start: self.post_group_start,
inter_group_segments: self.inter_group_segments.clone(),
},
left_out,
})
}
}
#[derive(Debug, Clone)]
pub struct TransactionSegmentsView {
pub tree: AssembledTree,
pub segments: Vec<OwnedSegment>,
}
#[derive(Debug)]
pub struct TransactionViews<'t> {
tree: &'t AssembledTree,
segments: &'t [OwnedSegment],
groups: Vec<TransactionGroupIndex>,
}
#[derive(Debug)]
struct TransactionGroupIndex {
position: usize,
kind_of: Vec<usize>,
others: Vec<Vec<usize>>,
shared: Vec<Vec<usize>>,
own: Vec<Vec<usize>>,
}
impl AssembledTree {
pub fn transaction_views<'t>(
&'t self,
segments: &'t [OwnedSegment],
is_transaction_group: impl Fn(&str) -> bool,
) -> TransactionViews<'t> {
let groups = self
.groups
.iter()
.enumerate()
.filter(|(_, g)| is_transaction_group(&g.group_id))
.map(|(position, g)| TransactionGroupIndex::new(position, g, segments))
.collect();
TransactionViews {
tree: self,
segments,
groups,
}
}
}
impl TransactionGroupIndex {
fn new(position: usize, group: &AssembledGroup, segments: &[OwnedSegment]) -> Self {
let mut kinds: Vec<Option<(&str, &str)>> = Vec::new();
let kind_of: Vec<usize> = group
.repetitions
.iter()
.map(|repetition| {
let k = kind(repetition);
kinds
.iter()
.position(|known| *known == k)
.unwrap_or_else(|| {
kinds.push(k);
kinds.len() - 1
})
})
.collect();
let others = (0..kinds.len())
.map(|k| (0..kind_of.len()).filter(|&r| kind_of[r] != k).collect())
.collect();
let mut holders: HashMap<u32, Vec<usize>> = HashMap::new();
let mut numbers = Vec::new();
for (r, repetition) in group.repetitions.iter().enumerate() {
numbers.clear();
segment_numbers(repetition, &mut numbers);
for &n in &numbers {
let held = holders.entry(n).or_default();
if held.last() != Some(&r) {
held.push(r);
}
}
}
let mut shared = vec![Vec::new(); kinds.len()];
let mut own = vec![Vec::new(); kind_of.len()];
for (i, segment) in segments.iter().enumerate() {
let held = holders
.get(&segment.segment_number)
.map_or(&[][..], Vec::as_slice);
for (k, shared) in shared.iter_mut().enumerate() {
let mut of_kind = held.iter().filter(|&&r| kind_of[r] == k);
match of_kind.next() {
None => shared.push(i),
Some(&r) if of_kind.all(|&other| other == r) => own[r].push(i),
Some(_) => {}
}
}
}
Self {
position,
kind_of,
others,
shared,
own,
}
}
}
impl TransactionViews<'_> {
pub fn view(&self, group_id: &str, index: usize) -> Option<TransactionSegmentsView> {
let index_of = self
.groups
.iter()
.find(|g| self.tree.groups[g.position].group_id == group_id)?;
let group = &self.tree.groups[index_of.position];
let own = group.repetitions.get(index)?;
let k = index_of.kind_of[index];
let mut repetitions = Vec::with_capacity(1 + index_of.others[k].len());
repetitions.push(own.clone());
repetitions.extend(
index_of.others[k]
.iter()
.map(|&r| group.repetitions[r].clone()),
);
let groups = self
.tree
.groups
.iter()
.enumerate()
.map(|(i, g)| {
if i == index_of.position {
AssembledGroup {
group_id: g.group_id.clone(),
repetitions: std::mem::take(&mut repetitions),
}
} else {
g.clone()
}
})
.collect();
let (shared, own) = (&index_of.shared[k], &index_of.own[index]);
let mut segments = Vec::with_capacity(shared.len() + own.len());
let (mut s, mut o) = (0, 0);
while s < shared.len() || o < own.len() {
let next = if o == own.len() || (s < shared.len() && shared[s] < own[o]) {
s += 1;
shared[s - 1]
} else {
o += 1;
own[o - 1]
};
segments.push(self.segments[next].clone());
}
Some(TransactionSegmentsView {
tree: AssembledTree {
segments: self.tree.segments.clone(),
groups,
post_group_start: self.tree.post_group_start,
inter_group_segments: self.tree.inter_group_segments.clone(),
},
segments,
})
}
}
impl AssembledGroup {
fn clone_shallow(&self) -> Self {
Self {
group_id: self.group_id.clone(),
repetitions: Vec::new(),
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::assembler::AssembledGroup;
fn seg(text: &str, number: u32) -> AssembledSegment {
let mut parts = text.split('+');
AssembledSegment {
tag: parts.next().unwrap().to_string(),
elements: parts.map(|e| vec![e.to_string()]).collect(),
mig_number: None,
segment_number: Some(number),
}
}
fn inst(
segments: Vec<AssembledSegment>,
child_groups: Vec<AssembledGroup>,
) -> AssembledGroupInstance {
AssembledGroupInstance {
segments,
child_groups,
entry_mig_number: None,
variant_mig_numbers: vec![],
skipped_segments: vec![],
skipped_positions: vec![],
}
}
fn sg8(seq: &str, number: u32) -> AssembledGroup {
AssembledGroup {
group_id: "SG8".into(),
repetitions: vec![inst(vec![seg(seq, number)], vec![])],
}
}
fn message() -> AssembledTree {
AssembledTree {
segments: vec![seg("BGM+E01", 1)],
groups: vec![
AssembledGroup {
group_id: "SG2".into(),
repetitions: vec![inst(vec![seg("NAD+MS", 2)], vec![])],
},
AssembledGroup {
group_id: "SG4".into(),
repetitions: vec![
inst(vec![seg("IDE+Z01", 3)], vec![]),
inst(vec![seg("IDE+24", 4)], vec![sg8("SEQ+Z01", 5)]),
inst(vec![seg("IDE+24", 6)], vec![sg8("SEQ+Z02", 7)]),
inst(vec![seg("IDE+24", 8)], vec![sg8("SEQ+Z03", 9)]),
],
},
],
post_group_start: 1,
inter_group_segments: Default::default(),
}
}
fn entries(tree: &AssembledTree) -> Vec<String> {
tree.groups[1]
.repetitions
.iter()
.map(|r| format!("{}+{}", r.segments[0].tag, r.segments[0].elements[0][0]))
.collect()
}
#[test]
fn a_transaction_sees_itself_first_and_the_list_but_no_other_transaction() {
let view = message().transaction_view("SG4", 2).unwrap();
assert_eq!(entries(&view.tree), ["IDE+24", "IDE+Z01"]);
assert_eq!(
view.tree.groups[1].repetitions[0].segments[0].segment_number,
Some(6)
);
assert_eq!(view.left_out, [4, 5, 8, 9]);
assert_eq!(view.tree.groups[0], message().groups[0]);
assert_eq!(view.tree.segments, message().segments);
}
#[test]
fn the_list_sees_every_transaction() {
let view = message().transaction_view("SG4", 0).unwrap();
assert_eq!(
entries(&view.tree),
["IDE+Z01", "IDE+24", "IDE+24", "IDE+24"]
);
assert!(view.left_out.is_empty());
}
fn segments(tree: &AssembledTree) -> Vec<OwnedSegment> {
fn collect(instance: &AssembledGroupInstance, out: &mut Vec<OwnedSegment>) {
for s in &instance.segments {
out.push(OwnedSegment {
id: s.tag.clone(),
elements: s.elements.clone(),
segment_number: s.segment_number.unwrap(),
});
}
for g in &instance.child_groups {
g.repetitions.iter().for_each(|r| collect(r, out));
}
}
let mut out: Vec<OwnedSegment> = Vec::new();
let root = inst(tree.segments.clone(), tree.groups.clone());
collect(&root, &mut out);
out.push(OwnedSegment {
id: "UNT".into(),
elements: vec![],
segment_number: 10,
});
out.sort_by_key(|s| s.segment_number);
out
}
#[test]
fn prepared_views_are_the_views_with_the_segments_they_keep() {
let tree = message();
let all = segments(&tree);
let views = tree.transaction_views(&all, |g| g == "SG4");
for index in 0..4 {
let expected = tree.transaction_view("SG4", index).unwrap();
let view = views.view("SG4", index).unwrap();
assert_eq!(view.tree.groups, expected.tree.groups, "view {index}");
assert_eq!(view.tree.segments, expected.tree.segments);
let kept: Vec<OwnedSegment> = all
.iter()
.filter(|s| expected.left_out.binary_search(&s.segment_number).is_err())
.cloned()
.collect();
assert_eq!(view.segments, kept, "view {index}");
}
let numbers = |index| -> Vec<u32> {
let view = views.view("SG4", index).unwrap();
view.segments.iter().map(|s| s.segment_number).collect()
};
assert_eq!(numbers(2), [1, 2, 3, 6, 7, 10]);
assert_eq!(numbers(0), (1..=10).collect::<Vec<_>>());
assert!(views.view("SG4", 4).is_none());
assert!(views.view("SG2", 0).is_none());
}
#[test]
fn no_such_group_or_repetition_is_no_view() {
assert!(message().transaction_view("SG5", 0).is_none());
assert!(message().transaction_view("SG4", 4).is_none());
}
}