use super::*;
use crate::parse::SpineRow;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum TurnStamp {
Live(usize),
Abandoned { root_line: usize },
}
impl TurnStamp {
#[must_use]
pub fn index(self) -> Option<usize> {
match self {
TurnStamp::Live(n) => Some(n),
TurnStamp::Abandoned { .. } => None,
}
}
#[must_use]
pub fn is_abandoned(self) -> bool {
matches!(self, TurnStamp::Abandoned { .. })
}
#[must_use]
pub fn root_line(self) -> Option<usize> {
match self {
TurnStamp::Live(_) => None,
TurnStamp::Abandoned { root_line } => Some(root_line),
}
}
#[must_use]
pub fn text(self) -> String {
match self {
TurnStamp::Live(n) => n.to_string(),
TurnStamp::Abandoned { root_line } => format!("abandoned (root L{root_line})"),
}
}
}
#[derive(Debug, Default)]
pub struct ChainView {
survival: Vec<Survival>,
stamp: Vec<TurnStamp>,
order_turn: Vec<usize>,
turns: Vec<Vec<usize>>,
branches: Vec<(usize, Vec<usize>)>,
abandoned_openers: Vec<usize>,
}
impl ChainView {
#[must_use]
pub fn build(records: &[(usize, Record)], spine: &[SpineRow]) -> ChainView {
let n = records.len();
let total = n + spine.len();
let mut combined: Vec<ChainNode<'_>> = Vec::with_capacity(total);
let mut combined_line: Vec<usize> = Vec::with_capacity(total);
let mut origin: Vec<Option<usize>> = Vec::with_capacity(total);
let (mut a, mut b) = (0usize, 0usize);
while a < n || b < spine.len() {
let take_record = match (records.get(a), spine.get(b)) {
(Some((la, _)), Some(sb)) => *la <= sb.line(),
(Some(_), None) => true,
_ => false,
};
if take_record {
combined.push(ChainNode::Full(&records[a].1));
combined_line.push(records[a].0);
origin.push(Some(a));
a += 1;
} else {
combined.push(ChainNode::Spine(&spine[b]));
combined_line.push(spine[b].line());
origin.push(None);
b += 1;
}
}
let chain = Chain::build_by(&combined, |r| *r, None);
let index_turns = group_turn_indices_chained(&combined, |r| *r, &chain);
let mut survival = vec![Survival::Live; n];
let mut stamp = vec![TurnStamp::Live(0); n];
let mut order_turn = vec![0usize; n];
let mut turns: Vec<Vec<usize>> = Vec::with_capacity(index_turns.len());
let mut turn_of_combined: Vec<Option<usize>> = vec![None; combined.len()];
for (ti, group) in index_turns.iter().enumerate() {
let mut own: Vec<usize> = Vec::new();
for &c in group {
turn_of_combined[c] = Some(ti);
if let Some(i) = origin[c] {
own.push(i);
stamp[i] = TurnStamp::Live(ti);
}
}
turns.push(own);
}
let mut branch_by_root: std::collections::BTreeMap<usize, Vec<usize>> =
std::collections::BTreeMap::new();
let mut abandoned_openers: Vec<usize> = Vec::new();
let mut running = 0usize;
for c in 0..combined.len() {
if let Some(t) = turn_of_combined[c] {
running = t;
}
let sv = chain.survival(c);
if chain.kind(c).is_some() {
abandoned_openers.push(combined_line[c]);
}
let Some(i) = origin[c] else { continue };
survival[i] = sv;
order_turn[i] = running;
if let Some(root) = chain.abandoned_root(c) {
let root_line = combined_line.get(root).copied().unwrap_or(combined_line[c]);
stamp[i] = TurnStamp::Abandoned { root_line };
branch_by_root.entry(root_line).or_default().push(i);
}
}
ChainView {
survival,
stamp,
order_turn,
turns,
branches: branch_by_root.into_iter().collect(),
abandoned_openers,
}
}
#[must_use]
pub fn survival(&self, i: usize) -> Survival {
self.survival.get(i).copied().unwrap_or_default()
}
#[must_use]
pub fn stamp(&self, i: usize) -> TurnStamp {
self.stamp.get(i).copied().unwrap_or(TurnStamp::Live(0))
}
#[must_use]
pub fn order_turn(&self, i: usize) -> usize {
self.order_turn.get(i).copied().unwrap_or(0)
}
#[must_use]
pub fn turns(&self) -> &[Vec<usize>] {
&self.turns
}
#[must_use]
pub fn turn_count(&self) -> usize {
self.turns.len()
}
#[must_use]
pub fn groups(&self) -> Vec<(TurnStamp, &[usize])> {
let mut out: Vec<(TurnStamp, &[usize])> = self
.turns
.iter()
.enumerate()
.map(|(ti, g)| (TurnStamp::Live(ti), g.as_slice()))
.collect();
out.extend(self.branches.iter().map(|(root_line, g)| {
(
TurnStamp::Abandoned {
root_line: *root_line,
},
g.as_slice(),
)
}));
out
}
#[must_use]
pub fn abandoned_openers(&self) -> &[usize] {
&self.abandoned_openers
}
}