graphrecords-query 0.5.0

High-performance graph-based data records
Documentation
use super::stats::Stats;
pub use graphrecords_macros::PhaseLabel;
use std::{
    any::Any,
    fmt::{self, Debug, Display, Formatter},
    hash::{Hash, Hasher},
    sync::Arc,
};

pub trait PhaseLabel: Any + Send + Sync {
    fn dyn_eq(&self, other: &dyn PhaseLabel) -> bool;

    fn dyn_hash(&self, state: &mut dyn Hasher);

    fn dyn_debug(&self, formatter: &mut Formatter<'_>) -> fmt::Result;

    fn as_any(&self) -> &dyn Any;
}

pub struct PhaseId(Arc<dyn PhaseLabel>);

impl PhaseId {
    #[must_use]
    pub fn new(label: impl PhaseLabel) -> Self {
        Self(Arc::new(label))
    }
}

impl Clone for PhaseId {
    fn clone(&self) -> Self {
        Self(Arc::clone(&self.0))
    }
}

impl PartialEq for PhaseId {
    fn eq(&self, other: &Self) -> bool {
        self.0.dyn_eq(other.0.as_ref())
    }
}

impl Eq for PhaseId {}

impl Hash for PhaseId {
    fn hash<H: Hasher>(&self, state: &mut H) {
        self.0.dyn_hash(state);
    }
}

impl Debug for PhaseId {
    fn fmt(&self, formatter: &mut Formatter<'_>) -> fmt::Result {
        self.0.dyn_debug(formatter)
    }
}

pub const DEFAULT_MAX_ITERATIONS: usize = 100;

#[derive(Clone, Copy, PartialEq, Eq, Debug)]
pub enum FixpointPolicy {
    Once,
    Fixpoint { max_iterations: usize },
}

impl FixpointPolicy {
    #[must_use]
    pub const fn once() -> Self {
        Self::Once
    }

    #[must_use]
    pub const fn fixpoint() -> Self {
        Self::Fixpoint {
            max_iterations: DEFAULT_MAX_ITERATIONS,
        }
    }

    #[must_use]
    pub const fn fixpoint_with(max_iterations: usize) -> Self {
        Self::Fixpoint { max_iterations }
    }
}

#[derive(Clone, PartialEq, Eq, Debug)]
pub enum StopReason {
    CompletedOnce,
    Converged { iterations: usize },
    Oscillation { iterations: usize },
    IterationLimit { iterations: usize },
    Skipped,
    Empty,
}

#[derive(Clone, Debug)]
pub struct PhaseOutcome {
    pub label: PhaseId,
    pub stop: StopReason,
}

#[derive(Clone, Debug, Default)]
pub struct OptimizationReport {
    pub phases: Vec<PhaseOutcome>,
}

impl OptimizationReport {
    #[must_use]
    pub const fn display(&self) -> ReportDisplay<'_> {
        ReportDisplay(self)
    }
}

pub struct ReportDisplay<'a>(&'a OptimizationReport);

impl Display for ReportDisplay<'_> {
    fn fmt(&self, formatter: &mut Formatter<'_>) -> fmt::Result {
        for (index, outcome) in self.0.phases.iter().enumerate() {
            if index > 0 {
                writeln!(formatter)?;
            }

            write!(formatter, "{:?}: ", outcome.label)?;

            match &outcome.stop {
                StopReason::Converged { iterations } => {
                    write!(formatter, "converged ({iterations} iterations)")?;
                }
                StopReason::CompletedOnce => formatter.write_str("completed once")?,
                StopReason::Oscillation { iterations } => {
                    write!(formatter, "oscillation after {iterations} iterations")?;
                }
                StopReason::IterationLimit { iterations } => {
                    write!(formatter, "did not converge within {iterations} iterations")?;
                }
                StopReason::Skipped => formatter.write_str("skipped (run condition false)")?,
                StopReason::Empty => formatter.write_str("skipped (no rules)")?,
            }
        }

        Ok(())
    }
}

pub(super) type RunCondition = Box<dyn Fn(&Stats<'_>) -> bool + Send + Sync>;