extern crate alloc;
use alloc::boxed::Box;
use alloc::vec::Vec;
use crate::metis::dot::{Dot, RawDot};
use crate::metis::{
Adopted, Cut, DotSet, Dotted, Metatheses, Recension, RefoundMap, Rhapsody, VersionVector,
};
use super::{Consigned, EpochAddress, SealedEpoch};
#[cfg(feature = "instrumentation")]
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
#[non_exhaustive]
pub struct EpochShadowProfile {
pub text: crate::metis::RhapsodyProfile,
pub text_context_stations: usize,
pub text_context_exceptions: usize,
pub testimonies: usize,
pub movement_context_stations: usize,
pub movement_context_exceptions: usize,
pub mapped_dots: usize,
pub mapped_ceiling_stations: usize,
pub mapped_live_stations: usize,
pub recension: crate::metis::RecensionProfile,
pub projected_dots: usize,
pub projected_capacity: usize,
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct EpochProjection {
epoch: EpochAddress,
order: Vec<Dot>,
}
impl EpochProjection {
#[must_use]
pub const fn epoch(&self) -> EpochAddress {
self.epoch
}
#[must_use]
pub fn order(&self) -> &[Dot] {
&self.order
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, thiserror::Error)]
#[error("epoch shadow cannot project swept identity {dot}")]
pub struct SweptIdentity {
pub dot: Dot,
}
#[derive(Debug)]
pub struct EpochConsignment {
text: Dotted<Rhapsody>,
moves: Dotted<Metatheses>,
map: RefoundMap,
}
impl EpochConsignment {
#[must_use]
pub const fn text(&self) -> &Dotted<Rhapsody> {
&self.text
}
#[must_use]
pub const fn moves(&self) -> &Dotted<Metatheses> {
&self.moves
}
#[must_use]
pub const fn map(&self) -> &RefoundMap {
&self.map
}
#[must_use]
pub fn into_parts(self) -> (Dotted<Rhapsody>, Dotted<Metatheses>, RefoundMap) {
(self.text, self.moves, self.map)
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, thiserror::Error)]
pub enum EpochConsignmentError {
#[error("seal witness names epoch {sealed:?}, not this shadow's {shadow:?}")]
ForeignSeal {
sealed: EpochAddress,
shadow: EpochAddress,
},
#[error("the shadow is missing sealed-window delivery {dot}")]
Incomplete {
dot: RawDot,
},
#[error("the shadow carries post-seal identity {dot}")]
BeyondSeal {
dot: RawDot,
},
#[error("the sealed window carries unfoldable state at {dot}")]
Unfoldable {
dot: Dot,
},
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, thiserror::Error)]
pub enum EpochStratumError {
#[error("epoch stratum is incomplete at {dot}")]
Incomplete {
dot: Dot,
},
#[error("epoch stratum identity {dot} lies above the adopted cut")]
BeyondCut {
dot: Dot,
},
#[error("epoch stratum is not sealed at {dot}")]
Unsealed {
dot: Dot,
},
}
#[derive(Debug)]
pub struct EpochStratum {
epoch: EpochAddress,
text: Dotted<Rhapsody>,
moves: Dotted<Metatheses>,
pristine: Rhapsody,
map: RefoundMap,
recension: Recension,
projection: Option<EpochProjection>,
}
impl EpochStratum {
pub fn new(
adopted: &Adopted,
text: Dotted<Rhapsody>,
moves: Dotted<Metatheses>,
) -> Result<Self, EpochStratumError> {
let cut = adopted.cut().as_vector();
let coverage = text.context().merge(moves.context());
if let Some(dot) = adopted.cut().to_have_set().difference(&coverage).next() {
return Err(EpochStratumError::Incomplete { dot });
}
let beyond = text
.context()
.dots()
.chain(text.store().woven().dots())
.chain(moves.context().dots())
.chain(moves.store().iter().map(|(dot, _)| dot))
.find(|dot| dot.counter() > cut.get(dot.station()));
if let Some(dot) = beyond {
return Err(EpochStratumError::BeyondCut { dot });
}
let recension = text.store().recension(moves.store());
let refounded = text
.store()
.refound_recension(&recension)
.map_err(|error| EpochStratumError::Unsealed { dot: error.dot })?;
let (pristine, mut map) = refounded.into_parts();
map.bind_cut(adopted.cut());
let projection = EpochShadow::project(adopted.address(), &map, &recension)
.expect("the frozen map and the projection derive from one recension");
Ok(Self {
epoch: adopted.address(),
text,
moves,
pristine,
map,
recension,
projection: Some(projection),
})
}
#[must_use]
pub const fn pristine(&self) -> &Rhapsody {
&self.pristine
}
#[must_use]
pub fn into_transition(self) -> EpochTransition {
EpochTransition {
native: self.pristine,
shadow: EpochShadow {
epoch: self.epoch,
text: self.text,
moves: self.moves,
map: self.map,
recension: self.recension,
projection: self.projection,
},
}
}
}
#[derive(Debug)]
pub struct EpochTransition {
pub native: Rhapsody,
pub shadow: EpochShadow,
}
#[derive(Debug)]
pub struct EpochShadow {
epoch: EpochAddress,
text: Dotted<Rhapsody>,
moves: Dotted<Metatheses>,
map: RefoundMap,
recension: Recension,
projection: Option<EpochProjection>,
}
impl EpochShadow {
pub fn deliver_text(&mut self, delta: &Dotted<Rhapsody>) -> Result<(), SweptIdentity> {
if let Some(dot) = Self::swept_text(&self.map, &self.text, delta) {
return Err(SweptIdentity { dot });
}
self.text.merge_from(delta);
self.refresh();
Ok(())
}
pub fn deliver_moves(&mut self, delta: &Dotted<Metatheses>) -> Result<(), SweptIdentity> {
if let Some(dot) = Self::swept_moves(&self.map, &self.moves, delta) {
return Err(SweptIdentity { dot });
}
self.moves.merge_from(delta);
self.refresh();
Ok(())
}
pub fn deliver_text_batch<'a, I>(&mut self, deltas: I) -> Result<(), SweptIdentity>
where
I: IntoIterator<Item = &'a Dotted<Rhapsody>>,
{
let mut folded = false;
for delta in deltas {
if let Some(dot) = Self::swept_text(&self.map, &self.text, delta) {
if folded {
self.refresh();
}
return Err(SweptIdentity { dot });
}
self.text.merge_from(delta);
folded = true;
}
if folded {
self.refresh();
}
Ok(())
}
pub fn deliver_moves_batch<'a, I>(&mut self, deltas: I) -> Result<(), SweptIdentity>
where
I: IntoIterator<Item = &'a Dotted<Metatheses>>,
{
let mut folded = false;
for delta in deltas {
if let Some(dot) = Self::swept_moves(&self.map, &self.moves, delta) {
if folded {
self.refresh();
}
return Err(SweptIdentity { dot });
}
self.moves.merge_from(delta);
folded = true;
}
if folded {
self.refresh();
}
Ok(())
}
fn swept_text(
map: &RefoundMap,
text: &Dotted<Rhapsody>,
delta: &Dotted<Rhapsody>,
) -> Option<Dot> {
delta
.store()
.woven()
.dots()
.find(|&dot| map.translate(dot).is_none() && !text.context().contains(dot))
}
fn swept_moves(
map: &RefoundMap,
moves: &Dotted<Metatheses>,
delta: &Dotted<Metatheses>,
) -> Option<Dot> {
delta
.store()
.iter()
.map(|(dot, _)| dot)
.find(|&dot| map.translate(dot).is_none() && !moves.context().contains(dot))
}
#[must_use]
pub fn projection(&mut self) -> &EpochProjection {
if self.projection.is_none() {
self.projection = Some(
Self::project(self.epoch, &self.map, &self.recension)
.expect("validated window identities remain projectable"),
);
}
self.projection.as_ref().expect("projection materialized")
}
#[must_use]
pub const fn recension(&self) -> &Recension {
&self.recension
}
#[cfg(feature = "instrumentation")]
#[must_use]
pub fn profile(&self) -> EpochShadowProfile {
let (mapped_dots, mapped_ceiling_stations, mapped_live_stations) =
self.map.structural_counts();
EpochShadowProfile {
text: self.text.store().profile(),
text_context_stations: self.text.context().station_count(),
text_context_exceptions: self.text.context().exceptions_len(),
testimonies: self.moves.store().len(),
movement_context_stations: self.moves.context().station_count(),
movement_context_exceptions: self.moves.context().exceptions_len(),
mapped_dots,
mapped_ceiling_stations,
mapped_live_stations,
recension: self.recension.profile(),
projected_dots: self
.projection
.as_ref()
.map_or(0, |value| value.order.len()),
projected_capacity: self
.projection
.as_ref()
.map_or(0, |value| value.order.capacity()),
}
}
pub fn consign(
self,
sealed: &SealedEpoch,
) -> Result<(EpochConsignment, Consigned), (Box<Self>, EpochConsignmentError)> {
if sealed.declaration() != self.epoch {
let refusal = EpochConsignmentError::ForeignSeal {
sealed: sealed.declaration(),
shadow: self.epoch,
};
return Err((Box::new(self), refusal));
}
let join = sealed.sealed_join();
let mut protocol = DotSet::new();
for dot in sealed.declaration_dots() {
let _ = protocol.insert(dot);
}
let coverage = self
.text
.context()
.merge(self.moves.context())
.merge(&protocol)
.floor();
for (station, counter) in join {
if coverage.get(station) < counter {
let dot = RawDot::new(station, coverage.get(station) + 1);
return Err((Box::new(self), EpochConsignmentError::Incomplete { dot }));
}
}
let beyond = self
.text
.context()
.high_water()
.merge(&self.moves.context().high_water())
.into_iter()
.find(|&(station, high)| high > join.get(station));
if let Some(dot) = beyond {
return Err((
Box::new(self),
EpochConsignmentError::BeyondSeal { dot: dot.into() },
));
}
let store = match self.recension.consign_store(&self.map) {
Ok(store) => store,
Err(unfoldable) => {
let dot = unfoldable.dot;
return Err((Box::new(self), EpochConsignmentError::Unfoldable { dot }));
}
};
let context = Self::translate_context(
&self
.text
.context()
.merge(self.moves.context())
.merge(&protocol),
&self.map,
);
let text = Dotted::try_new(store, context.clone())
.expect("every spelled identity rides the same frozen map as its context image");
Ok((
EpochConsignment {
text,
moves: Dotted::from_context(context),
map: self.map,
},
Consigned::accepted(sealed.clone()),
))
}
fn translate_context(context: &DotSet, map: &RefoundMap) -> DotSet {
let mut prefix = VersionVector::new();
let floor = context.floor();
for (station, contextual) in &floor {
let window = contextual.saturating_sub(map.ceiling_of(station));
let total = map.live_of(station) + window;
if total > 0 {
prefix.observe(station, total);
}
}
let mut translated = DotSet::from_witnessed(&Cut::from_witnessed(prefix));
for dot in context.exceptions() {
if let Some(image) = map.translate(dot) {
let _ = translated.insert(image);
}
}
translated
}
fn refresh(&mut self) {
self.recension
.collate(self.text.store(), self.moves.store());
self.projection = None;
}
fn project(
epoch: EpochAddress,
map: &RefoundMap,
recension: &Recension,
) -> Result<EpochProjection, SweptIdentity> {
let order = recension
.order()
.into_iter()
.map(|dot| map.translate(dot).ok_or(SweptIdentity { dot }))
.collect::<Result<_, _>>()?;
Ok(EpochProjection { epoch, order })
}
}