use super::types::{EncodingRefusal, InterleavedSequence, Interleaving, Realization, StrandSet};
use std::collections::VecDeque;
#[must_use]
pub fn interpreted<Command: Clone>(
set: &StrandSet<Command>,
material: &[u8],
) -> InterleavedSequence<Command> {
let realization = realized(set, material);
InterleavedSequence::realized(
Interleaving::declared(realization.choices),
realization.commands,
)
}
pub fn encoded<Command>(
set: &StrandSet<Command>,
interleaving: &Interleaving,
) -> Result<Vec<u8>, EncodingRefusal> {
if interleaving.choices().len() != set.steps() {
return Err(EncodingRefusal::StepsMismatch {
declared: interleaving.choices().len(),
steps: set.steps(),
});
}
let mut remaining: Vec<usize> = set
.strands()
.iter()
.map(|strand| strand.commands().len())
.collect();
let mut material = Vec::with_capacity(set.steps());
for (at, &choice) in interleaving.choices().iter().enumerate() {
let Some(stock) = remaining.get(usize::from(choice)).copied() else {
return Err(EncodingRefusal::ChoiceOutsideStrands { at, choice });
};
if stock == 0usize {
return Err(EncodingRefusal::StrandExhausted { at, choice });
}
let position = remaining
.iter()
.take(usize::from(choice))
.filter(|&&held| held > 0usize)
.count();
material.push(u8::try_from(position).unwrap_or(u8::MAX));
if let Some(held) = remaining.get_mut(usize::from(choice)) {
*held = stock.saturating_sub(1usize);
}
}
Ok(material)
}
struct Taken<Command> {
choice: u8,
command: Command,
radix: usize,
pick: usize,
strand: StrandAfterTake,
}
enum StrandAfterTake {
Live,
Exhausted,
}
pub(super) fn realized<Command: Clone>(
set: &StrandSet<Command>,
material: &[u8],
) -> Realization<Command> {
let mut live: Vec<(u8, VecDeque<Command>)> = set
.strands()
.iter()
.enumerate()
.map(|(ordinal, strand)| {
(
u8::try_from(ordinal).unwrap_or(u8::MAX),
strand.commands().iter().cloned().collect(),
)
})
.collect();
let mut choices = Vec::with_capacity(set.steps());
let mut commands = Vec::with_capacity(set.steps());
let mut radixes = Vec::with_capacity(set.steps());
let mut fuel = material.iter().copied();
while !live.is_empty() {
let Some(taken) = taken(&mut live, fuel.next().unwrap_or(0u8)) else {
break;
};
choices.push(taken.choice);
commands.push(taken.command);
radixes.push(taken.radix);
if matches!(taken.strand, StrandAfterTake::Exhausted) {
live.remove(taken.pick);
}
}
Realization {
choices,
commands,
radixes,
}
}
fn taken<Command>(live: &mut [(u8, VecDeque<Command>)], raw: u8) -> Option<Taken<Command>> {
let radix = live.len();
let pick = usize::from(raw).checked_rem(radix)?;
let (ordinal, queue) = live.get_mut(pick)?;
let choice = *ordinal;
let command = queue.pop_front()?;
let strand = if queue.is_empty() {
StrandAfterTake::Exhausted
} else {
StrandAfterTake::Live
};
Some(Taken {
choice,
command,
radix,
pick,
strand,
})
}