holos-tda 0.9.0

Vietoris-Rips persistence and checked degree-Rips modules
Documentation
use crate::{
    ContinuationKind, Diagram, EdgeKey, ProgramArtifactError, ProgramEventKind, ProgramUpdateMode,
};

use super::model::ProgramTraceError;

pub(crate) fn mode_tag(mode: ProgramUpdateMode) -> u8 {
    match mode {
        ProgramUpdateMode::Reused => 0,
        ProgramUpdateMode::Repaired => 1,
        ProgramUpdateMode::Recompiled => 2,
    }
}

pub(crate) fn decode_mode(tag: u8) -> std::result::Result<ProgramUpdateMode, ProgramTraceError> {
    match tag {
        0 => Ok(ProgramUpdateMode::Reused),
        1 => Ok(ProgramUpdateMode::Repaired),
        2 => Ok(ProgramUpdateMode::Recompiled),
        _ => Err(ProgramTraceError::new(format!(
            "unknown update-mode tag {tag}"
        ))),
    }
}

pub(crate) fn event_kind_tag(kind: ProgramEventKind) -> u8 {
    match kind {
        ProgramEventKind::VertexSetChanged => 0,
        ProgramEventKind::EdgeSetChanged => 1,
        ProgramEventKind::ThresholdCrossing => 2,
        ProgramEventKind::GuardFailed => 3,
        ProgramEventKind::AtomRebuilt => 4,
        ProgramEventKind::ReductionSuffixRepaired => 5,
        ProgramEventKind::SeparatorContractChanged => 6,
    }
}

pub(crate) fn decode_event_kind(
    tag: u8,
) -> std::result::Result<ProgramEventKind, ProgramTraceError> {
    match tag {
        0 => Ok(ProgramEventKind::VertexSetChanged),
        1 => Ok(ProgramEventKind::EdgeSetChanged),
        2 => Ok(ProgramEventKind::ThresholdCrossing),
        3 => Ok(ProgramEventKind::GuardFailed),
        4 => Ok(ProgramEventKind::AtomRebuilt),
        5 => Ok(ProgramEventKind::ReductionSuffixRepaired),
        6 => Ok(ProgramEventKind::SeparatorContractChanged),
        _ => Err(ProgramTraceError::new(format!(
            "unknown event-kind tag {tag}"
        ))),
    }
}

pub(crate) fn continuation_kind_tag(kind: ContinuationKind) -> u8 {
    match kind {
        ContinuationKind::Isomorphism => 0,
        ContinuationKind::Split => 1,
        ContinuationKind::Merge => 2,
        ContinuationKind::Mixing => 3,
        ContinuationKind::Birth => 4,
        ContinuationKind::Death => 5,
        ContinuationKind::Ambiguous => 6,
    }
}

pub(crate) fn decode_continuation_kind(
    tag: u8,
) -> std::result::Result<ContinuationKind, ProgramTraceError> {
    match tag {
        0 => Ok(ContinuationKind::Isomorphism),
        1 => Ok(ContinuationKind::Split),
        2 => Ok(ContinuationKind::Merge),
        3 => Ok(ContinuationKind::Mixing),
        4 => Ok(ContinuationKind::Birth),
        5 => Ok(ContinuationKind::Death),
        6 => Ok(ContinuationKind::Ambiguous),
        _ => Err(ProgramTraceError::new(format!(
            "unknown continuation-kind tag {tag}"
        ))),
    }
}

pub(crate) fn bounded_sum(
    current: usize,
    added: usize,
    limit: usize,
    label: &str,
) -> std::result::Result<usize, ProgramTraceError> {
    let next = current
        .checked_add(added)
        .ok_or_else(|| ProgramTraceError::new(format!("{label} overflow usize")))?;
    if next > limit {
        return Err(ProgramTraceError::new(format!(
            "{next} {label} exceed the limit {limit}"
        )));
    }
    Ok(next)
}

pub(crate) fn program_artifact_error(error: ProgramArtifactError) -> ProgramTraceError {
    ProgramTraceError::new(error.to_string())
}

pub(crate) fn diagram_bits_equal(a: &Diagram, b: &Diagram) -> bool {
    a.bars.len() == b.bars.len()
        && a.bars.iter().zip(&b.bars).all(|(a, b)| {
            a.dim == b.dim
                && a.birth.to_bits() == b.birth.to_bits()
                && a.death.to_bits() == b.death.to_bits()
        })
}

pub(crate) fn put_u16(out: &mut Vec<u8>, value: u16) {
    out.extend_from_slice(&value.to_be_bytes());
}

pub(crate) fn put_u32(out: &mut Vec<u8>, value: u32) {
    out.extend_from_slice(&value.to_be_bytes());
}

pub(crate) fn put_u64(out: &mut Vec<u8>, value: u64) {
    out.extend_from_slice(&value.to_be_bytes());
}

pub(crate) fn put_usize(
    out: &mut Vec<u8>,
    value: usize,
    label: &str,
) -> std::result::Result<(), ProgramTraceError> {
    let value = u64::try_from(value)
        .map_err(|_| ProgramTraceError::new(format!("{label} does not fit the wire format")))?;
    put_u64(out, value);
    Ok(())
}

pub(crate) fn put_optional_usize(
    out: &mut Vec<u8>,
    value: Option<usize>,
    label: &str,
) -> std::result::Result<(), ProgramTraceError> {
    match value {
        None => out.push(0),
        Some(value) => {
            out.push(1);
            put_usize(out, value, label)?;
        }
    }
    Ok(())
}

pub(crate) fn put_optional_edge(
    out: &mut Vec<u8>,
    edge: Option<EdgeKey>,
) -> std::result::Result<(), ProgramTraceError> {
    match edge {
        None => out.push(0),
        Some(edge) => {
            out.push(1);
            put_usize(out, edge.u, "event edge endpoint")?;
            put_usize(out, edge.v, "event edge endpoint")?;
        }
    }
    Ok(())
}

pub(crate) struct Reader<'a> {
    bytes: &'a [u8],
    position: usize,
}

impl<'a> Reader<'a> {
    pub(crate) fn new(bytes: &'a [u8]) -> Self {
        Self { bytes, position: 0 }
    }

    pub(crate) fn remaining(&self) -> usize {
        self.bytes.len() - self.position
    }

    pub(crate) fn take(
        &mut self,
        count: usize,
    ) -> std::result::Result<&'a [u8], ProgramTraceError> {
        let end = self
            .position
            .checked_add(count)
            .ok_or_else(|| ProgramTraceError::new("byte position overflows usize"))?;
        if end > self.bytes.len() {
            return Err(ProgramTraceError::new("truncated envelope"));
        }
        let value = &self.bytes[self.position..end];
        self.position = end;
        Ok(value)
    }

    pub(crate) fn u8(&mut self) -> std::result::Result<u8, ProgramTraceError> {
        Ok(self.take(1)?[0])
    }

    pub(crate) fn u16(&mut self) -> std::result::Result<u16, ProgramTraceError> {
        Ok(u16::from_be_bytes(
            self.take(2)?.try_into().expect("two-byte slice"),
        ))
    }

    pub(crate) fn u32(&mut self) -> std::result::Result<u32, ProgramTraceError> {
        Ok(u32::from_be_bytes(
            self.take(4)?.try_into().expect("four-byte slice"),
        ))
    }

    pub(crate) fn u64(&mut self) -> std::result::Result<u64, ProgramTraceError> {
        Ok(u64::from_be_bytes(
            self.take(8)?.try_into().expect("eight-byte slice"),
        ))
    }

    pub(crate) fn usize(&mut self) -> std::result::Result<usize, ProgramTraceError> {
        usize::try_from(self.u64()?)
            .map_err(|_| ProgramTraceError::new("wire integer does not fit usize"))
    }

    pub(crate) fn bounded_usize(
        &mut self,
        label: &str,
        limit: usize,
    ) -> std::result::Result<usize, ProgramTraceError> {
        let value = self.usize()?;
        if value > limit {
            return Err(ProgramTraceError::new(format!(
                "{label} {value} exceeds the limit {limit}"
            )));
        }
        Ok(value)
    }

    pub(crate) fn optional_usize(
        &mut self,
    ) -> std::result::Result<Option<usize>, ProgramTraceError> {
        match self.u8()? {
            0 => Ok(None),
            1 => Ok(Some(self.usize()?)),
            tag => Err(ProgramTraceError::new(format!(
                "unknown optional-integer tag {tag}"
            ))),
        }
    }

    pub(crate) fn array32(&mut self) -> std::result::Result<[u8; 32], ProgramTraceError> {
        Ok(self.take(32)?.try_into().expect("32-byte slice"))
    }
}