use smallvec::SmallVec;
use crate::backend::VocabId;
use crate::semiring::Semiring;
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)]
pub struct NodeId(pub u32);
impl NodeId {
#[inline]
pub const fn new(id: u32) -> Self {
Self(id)
}
#[inline]
pub const fn value(self) -> u32 {
self.0
}
}
impl From<u32> for NodeId {
fn from(id: u32) -> Self {
Self(id)
}
}
impl From<usize> for NodeId {
fn from(id: usize) -> Self {
Self(id as u32)
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)]
pub struct EdgeId(pub u32);
impl EdgeId {
#[inline]
pub const fn new(id: u32) -> Self {
Self(id)
}
#[inline]
pub const fn value(self) -> u32 {
self.0
}
}
impl From<u32> for EdgeId {
fn from(id: u32) -> Self {
Self(id)
}
}
impl From<usize> for EdgeId {
fn from(id: usize) -> Self {
Self(id as u32)
}
}
#[derive(Clone, Debug)]
pub struct Node {
pub id: NodeId,
pub outgoing: SmallVec<[EdgeId; 8]>,
pub incoming: SmallVec<[EdgeId; 8]>,
pub position: Option<usize>,
}
impl Node {
#[inline]
pub fn new(id: NodeId) -> Self {
Self {
id,
outgoing: SmallVec::new(),
incoming: SmallVec::new(),
position: None,
}
}
#[inline]
pub fn with_position(id: NodeId, position: usize) -> Self {
Self {
id,
outgoing: SmallVec::new(),
incoming: SmallVec::new(),
position: Some(position),
}
}
#[inline]
pub fn has_outgoing(&self) -> bool {
!self.outgoing.is_empty()
}
#[inline]
pub fn has_incoming(&self) -> bool {
!self.incoming.is_empty()
}
#[inline]
pub fn out_degree(&self) -> usize {
self.outgoing.len()
}
#[inline]
pub fn in_degree(&self) -> usize {
self.incoming.len()
}
}
#[derive(Clone, Debug)]
pub struct Edge<W: Semiring> {
pub id: EdgeId,
pub source: NodeId,
pub target: NodeId,
pub label: VocabId,
pub weight: W,
pub metadata: EdgeMetadata,
}
impl<W: Semiring> Edge<W> {
#[inline]
pub fn new(
id: EdgeId,
source: NodeId,
target: NodeId,
label: VocabId,
weight: W,
metadata: EdgeMetadata,
) -> Self {
Self {
id,
source,
target,
label,
weight,
metadata,
}
}
#[inline]
pub fn simple(id: EdgeId, source: NodeId, target: NodeId, label: VocabId, weight: W) -> Self {
Self::new(id, source, target, label, weight, EdgeMetadata::default())
}
}
#[derive(Clone, Debug, Default, PartialEq, Eq)]
pub struct EdgeMetadata {
pub edit_distance: Option<u8>,
pub is_phonetic: bool,
pub rule_id: Option<u32>,
pub is_original: bool,
pub source_layer: Option<u8>,
}
impl EdgeMetadata {
#[inline]
pub fn original() -> Self {
Self {
is_original: true,
edit_distance: Some(0),
..Default::default()
}
}
#[inline]
pub fn correction(edit_distance: u8) -> Self {
Self {
edit_distance: Some(edit_distance),
is_original: false,
..Default::default()
}
}
#[inline]
pub fn phonetic() -> Self {
Self {
is_phonetic: true,
is_original: false,
..Default::default()
}
}
#[inline]
pub fn grammar_rule(rule_id: u32) -> Self {
Self {
rule_id: Some(rule_id),
is_original: false,
..Default::default()
}
}
#[inline]
pub fn with_layer(mut self, layer: u8) -> Self {
self.source_layer = Some(layer);
self
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::semiring::TropicalWeight;
#[test]
fn test_node_id() {
let id = NodeId::new(42);
assert_eq!(id.value(), 42);
assert_eq!(id, NodeId::from(42u32));
assert_eq!(id, NodeId::from(42usize));
}
#[test]
fn test_edge_id() {
let id = EdgeId::new(42);
assert_eq!(id.value(), 42);
assert_eq!(id, EdgeId::from(42u32));
assert_eq!(id, EdgeId::from(42usize));
}
#[test]
fn test_node_creation() {
let node = Node::new(NodeId::new(0));
assert_eq!(node.id, NodeId::new(0));
assert!(!node.has_outgoing());
assert!(!node.has_incoming());
assert_eq!(node.out_degree(), 0);
assert_eq!(node.in_degree(), 0);
assert_eq!(node.position, None);
}
#[test]
fn test_node_with_position() {
let node = Node::with_position(NodeId::new(1), 5);
assert_eq!(node.id, NodeId::new(1));
assert_eq!(node.position, Some(5));
}
#[test]
fn test_edge_creation() {
let edge: Edge<TropicalWeight> = Edge::new(
EdgeId::new(0),
NodeId::new(0),
NodeId::new(1),
42,
TropicalWeight::new(1.0),
EdgeMetadata::default(),
);
assert_eq!(edge.id, EdgeId::new(0));
assert_eq!(edge.source, NodeId::new(0));
assert_eq!(edge.target, NodeId::new(1));
assert_eq!(edge.label, 42);
assert_eq!(edge.weight.value(), 1.0);
}
#[test]
fn test_edge_simple() {
let edge: Edge<TropicalWeight> = Edge::simple(
EdgeId::new(0),
NodeId::new(0),
NodeId::new(1),
42,
TropicalWeight::new(1.0),
);
assert!(!edge.metadata.is_original);
assert_eq!(edge.metadata.edit_distance, None);
}
#[test]
fn test_edge_metadata_original() {
let meta = EdgeMetadata::original();
assert!(meta.is_original);
assert_eq!(meta.edit_distance, Some(0));
assert!(!meta.is_phonetic);
}
#[test]
fn test_edge_metadata_correction() {
let meta = EdgeMetadata::correction(2);
assert!(!meta.is_original);
assert_eq!(meta.edit_distance, Some(2));
}
#[test]
fn test_edge_metadata_phonetic() {
let meta = EdgeMetadata::phonetic();
assert!(!meta.is_original);
assert!(meta.is_phonetic);
}
#[test]
fn test_edge_metadata_grammar_rule() {
let meta = EdgeMetadata::grammar_rule(42);
assert!(!meta.is_original);
assert_eq!(meta.rule_id, Some(42));
}
#[test]
fn test_edge_metadata_with_layer() {
let meta = EdgeMetadata::correction(1).with_layer(2);
assert_eq!(meta.edit_distance, Some(1));
assert_eq!(meta.source_layer, Some(2));
}
}