use core::fmt;
use bincode_next::{Decode, Encode};
use super::metadata::NodeMetadata;
use super::symbol::SymbolKind;
#[derive(Clone, Copy, PartialEq, Eq, Hash, Encode, Decode)]
pub struct DagNodeId(pub(crate) u32);
impl DagNodeId {
#[must_use]
pub const fn new(id: u32) -> Self {
Self(id)
}
#[must_use]
pub const fn value(self) -> u32 {
self.0
}
pub const NONE: Self = Self(u32::MAX);
#[must_use]
pub const fn index(self) -> usize {
self.0 as usize
}
#[must_use]
pub const fn is_none(self) -> bool {
self.0 == u32::MAX
}
}
impl fmt::Debug for DagNodeId {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
if self.is_none() {
f.write_str("DagNodeId(NONE)")
} else {
write!(f, "DagNodeId({})", self.0)
}
}
}
impl fmt::Display for DagNodeId {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
if self.is_none() {
f.write_str("∅")
} else {
write!(f, "#{}", self.0)
}
}
}
const INLINE_CHILDREN: usize = 4;
#[derive(Debug, Clone, PartialEq, Eq, Hash, Encode, Decode)]
pub enum ChildList {
Empty,
One([DagNodeId; 1]),
Two([DagNodeId; 2]),
Three([DagNodeId; 3]),
Four([DagNodeId; 4]),
Many(Vec<DagNodeId>),
}
impl ChildList {
#[must_use]
pub fn from_slice(ids: &[DagNodeId]) -> Self {
match ids.len() {
0 => Self::Empty,
1 => Self::One([ids[0]]),
2 => Self::Two([ids[0], ids[1]]),
3 => Self::Three([ids[0], ids[1], ids[2]]),
4 => Self::Four([ids[0], ids[1], ids[2], ids[3]]),
_ => Self::Many(ids.to_vec()),
}
}
#[must_use]
pub const fn len(&self) -> usize {
match self {
Self::Empty => 0,
Self::One(_) => 1,
Self::Two(_) => 2,
Self::Three(_) => 3,
Self::Four(_) => INLINE_CHILDREN,
Self::Many(v) => v.len(),
}
}
#[must_use]
pub const fn is_empty(&self) -> bool {
matches!(self, Self::Empty)
}
#[must_use]
pub fn as_slice(&self) -> &[DagNodeId] {
match self {
Self::Empty => &[],
Self::One(a) => a,
Self::Two(a) => a,
Self::Three(a) => a,
Self::Four(a) => a,
Self::Many(v) => v,
}
}
pub fn iter(&self) -> impl Iterator<Item = DagNodeId> + '_ {
self.as_slice().iter().copied()
}
}
#[derive(Debug, Clone, Encode, Decode)]
pub struct DagNode {
pub kind: SymbolKind,
pub meta: NodeMetadata,
pub children: ChildList,
}
impl DagNode {
#[must_use]
pub const fn variable(kind: SymbolKind, meta: NodeMetadata) -> Self {
Self {
kind,
meta,
children: ChildList::Empty,
}
}
#[must_use]
pub const fn constant(val: f64, meta: NodeMetadata) -> Self {
Self {
kind: SymbolKind::Constant(val),
meta,
children: ChildList::Empty,
}
}
#[must_use]
pub const fn operator(kind: SymbolKind, meta: NodeMetadata, children: ChildList) -> Self {
Self {
kind,
meta,
children,
}
}
#[must_use]
pub const fn is_leaf(&self) -> bool {
self.children.is_empty()
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::dag::metadata::{NodeFlags, NodeHash};
use crate::dag::symbol::{OpKind, SymbolId};
#[test]
fn child_list_inline() {
let ids = [DagNodeId(0), DagNodeId(1)];
let cl = ChildList::from_slice(&ids);
assert_eq!(cl.len(), 2);
assert_eq!(cl.as_slice(), &ids);
}
#[test]
fn child_list_many() {
let ids: Vec<DagNodeId> = (0..10).map(DagNodeId).collect();
let cl = ChildList::from_slice(&ids);
assert_eq!(cl.len(), 10);
assert_eq!(cl.as_slice(), &ids);
}
#[test]
fn variable_node() {
let meta = NodeMetadata::leaf(NodeHash(100));
let node = DagNode::variable(SymbolKind::Variable(SymbolId(0)), meta);
assert!(node.is_leaf());
assert!(!matches!(node.kind, SymbolKind::Constant(_)));
}
#[test]
fn constant_node() {
let meta = NodeMetadata::leaf(NodeHash(200));
let node = DagNode::constant(3.14, meta);
assert!(node.is_leaf());
assert!(matches!(node.kind, SymbolKind::Constant(v) if (v - 3.14).abs() < f64::EPSILON));
}
#[test]
fn operator_node() {
let meta = NodeMetadata::operator(NodeHash(300), NodeFlags::commutative_associative());
let children = ChildList::from_slice(&[DagNodeId(0), DagNodeId(1)]);
let node = DagNode::operator(SymbolKind::Operator(OpKind::Add), meta, children);
assert!(!node.is_leaf());
assert_eq!(node.children.len(), 2);
assert!(node.meta.flags.is_commutative());
}
}