use std::{
fmt, hash,
collections::{BTreeMap, BTreeSet, HashMap},
};
use crate::{
Face, ID, NodeID, Context, Contextual, ExclusivelyContextual, InContext, AcesError,
AcesErrorKind, sat,
};
pub type AtomID = ID;
#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)]
#[repr(transparent)]
pub struct PortID(pub(crate) AtomID);
impl PortID {
#[inline]
pub const fn get(self) -> AtomID {
self.0
}
}
impl From<AtomID> for PortID {
#[inline]
fn from(id: AtomID) -> Self {
PortID(id)
}
}
impl From<PortID> for AtomID {
#[inline]
fn from(id: PortID) -> Self {
id.0
}
}
impl ExclusivelyContextual for PortID {
fn format_locked(&self, ctx: &Context) -> Result<String, AcesError> {
let port = ctx
.get_port(*self)
.ok_or_else(|| AcesError::from(AcesErrorKind::PortMissingForID(*self)))?;
port.format_locked(ctx)
}
}
#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Debug)]
#[repr(transparent)]
pub struct LinkID(pub(crate) AtomID);
impl LinkID {
#[inline]
pub const fn get(self) -> AtomID {
self.0
}
}
impl From<AtomID> for LinkID {
#[inline]
fn from(id: AtomID) -> Self {
LinkID(id)
}
}
impl From<LinkID> for AtomID {
#[inline]
fn from(id: LinkID) -> Self {
id.0
}
}
impl ExclusivelyContextual for LinkID {
fn format_locked(&self, ctx: &Context) -> Result<String, AcesError> {
let link = ctx
.get_link(*self)
.ok_or_else(|| AcesError::from(AcesErrorKind::LinkMissingForID(*self)))?;
link.format_locked(ctx)
}
}
#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Debug)]
#[repr(transparent)]
pub struct ForkID(pub(crate) AtomID);
impl ForkID {
#[inline]
pub const fn get(self) -> AtomID {
self.0
}
}
impl From<AtomID> for ForkID {
#[inline]
fn from(id: AtomID) -> Self {
ForkID(id)
}
}
impl From<ForkID> for AtomID {
#[inline]
fn from(id: ForkID) -> Self {
id.0
}
}
impl ExclusivelyContextual for ForkID {
fn format_locked(&self, ctx: &Context) -> Result<String, AcesError> {
let fork = ctx
.get_fork(*self)
.ok_or_else(|| AcesError::from(AcesErrorKind::ForkMissingForID(*self)))?;
fork.format_locked(ctx)
}
}
#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Debug)]
#[repr(transparent)]
pub struct JoinID(pub(crate) AtomID);
impl JoinID {
#[inline]
pub const fn get(self) -> AtomID {
self.0
}
}
impl From<AtomID> for JoinID {
#[inline]
fn from(id: AtomID) -> Self {
JoinID(id)
}
}
impl From<JoinID> for AtomID {
#[inline]
fn from(id: JoinID) -> Self {
id.0
}
}
impl ExclusivelyContextual for JoinID {
fn format_locked(&self, ctx: &Context) -> Result<String, AcesError> {
let join = ctx
.get_join(*self)
.ok_or_else(|| AcesError::from(AcesErrorKind::JoinMissingForID(*self)))?;
join.format_locked(ctx)
}
}
#[derive(Clone, Debug)]
pub(crate) struct AtomSpace {
atoms: Vec<Atom>,
atom_ids: HashMap<Atom, AtomID>,
source_nodes: BTreeMap<NodeID, PortID>,
sink_nodes: BTreeMap<NodeID, PortID>,
internal_nodes: BTreeMap<NodeID, (PortID, PortID)>,
}
impl Default for AtomSpace {
fn default() -> Self {
Self {
atoms: vec![Atom::Bottom],
atom_ids: Default::default(),
source_nodes: Default::default(),
sink_nodes: Default::default(),
internal_nodes: Default::default(),
}
}
}
impl AtomSpace {
fn do_share_atom(&mut self, mut new_atom: Atom) -> AtomID {
if let Some(old_atom_id) = self.get_atom_id(&new_atom) {
if new_atom.get_atom_id().is_none() {
trace!("Resharing: {:?}", new_atom);
old_atom_id
} else {
panic!("Attempt to reset ID of atom {:?}", new_atom);
}
} else {
let atom_id = unsafe { AtomID::new_unchecked(self.atoms.len()) };
new_atom.set_atom_id(atom_id);
trace!("New share: {:?}", new_atom);
self.atoms.push(new_atom.clone());
self.atom_ids.insert(new_atom, atom_id);
atom_id
}
}
pub(crate) fn share_port(&mut self, port: &mut Port) -> PortID {
let host = port.node_id;
match port.face {
Face::Tx => {
let atom_id = self.do_share_atom(Atom::Tx(port.clone()));
port.atom_id = Some(atom_id);
let pid = PortID(atom_id);
if let Some(&rx_id) = self.sink_nodes.get(&host) {
self.sink_nodes.remove(&host);
self.internal_nodes.insert(host, (pid, rx_id));
} else {
self.source_nodes.insert(host, pid);
}
pid
}
Face::Rx => {
let atom_id = self.do_share_atom(Atom::Rx(port.clone()));
port.atom_id = Some(atom_id);
let pid = PortID(atom_id);
if let Some(&tx_id) = self.source_nodes.get(&host) {
self.source_nodes.remove(&host);
self.internal_nodes.insert(host, (tx_id, pid));
} else {
self.sink_nodes.insert(host, pid);
}
pid
}
}
}
#[inline]
pub(crate) fn share_link(&mut self, link: &mut Link) -> LinkID {
let atom_id = self.do_share_atom(Atom::Link(link.clone()));
link.atom_id = Some(atom_id);
LinkID(atom_id)
}
#[inline]
pub(crate) fn share_fork(&mut self, fork: &mut Fork) -> ForkID {
let atom_id = self.do_share_atom(Atom::Fork(fork.clone()));
fork.atom_id = Some(atom_id);
ForkID(atom_id)
}
#[inline]
pub(crate) fn share_join(&mut self, join: &mut Join) -> JoinID {
let atom_id = self.do_share_atom(Atom::Join(join.clone()));
join.atom_id = Some(atom_id);
JoinID(atom_id)
}
#[inline]
pub(crate) fn get_atom(&self, atom_id: AtomID) -> Option<&Atom> {
self.atoms.get(atom_id.get())
}
#[inline]
pub(crate) fn get_atom_id(&self, atom: &Atom) -> Option<AtomID> {
self.atom_ids.get(atom).copied()
}
#[inline]
pub(crate) fn is_port(&self, atom_id: AtomID) -> bool {
match self.get_atom(atom_id) {
Some(Atom::Tx(_)) | Some(Atom::Rx(_)) => true,
_ => false,
}
}
#[inline]
pub(crate) fn get_port(&self, pid: PortID) -> Option<&Port> {
match self.get_atom(pid.into()) {
Some(Atom::Tx(a)) => Some(a),
Some(Atom::Rx(a)) => Some(a),
_ => None,
}
}
#[inline]
pub(crate) fn is_link(&self, atom_id: AtomID) -> bool {
match self.get_atom(atom_id) {
Some(Atom::Link(_)) => true,
_ => false,
}
}
#[inline]
pub(crate) fn get_link(&self, lid: LinkID) -> Option<&Link> {
match self.get_atom(lid.into()) {
Some(Atom::Link(a)) => Some(a),
_ => None,
}
}
#[inline]
pub(crate) fn is_harc(&self, atom_id: AtomID) -> bool {
match self.get_atom(atom_id) {
Some(Atom::Fork(_)) | Some(Atom::Join(_)) => true,
_ => false,
}
}
#[inline]
pub(crate) fn get_harc(&self, aid: AtomID) -> Option<&Harc> {
match self.get_atom(aid) {
Some(Atom::Fork(a)) => Some(a),
Some(Atom::Join(a)) => Some(a),
_ => None,
}
}
#[inline]
pub(crate) fn is_fork(&self, atom_id: AtomID) -> bool {
match self.get_atom(atom_id) {
Some(Atom::Fork(_)) => true,
_ => false,
}
}
#[inline]
pub(crate) fn get_fork(&self, fid: ForkID) -> Option<&Fork> {
match self.get_atom(fid.into()) {
Some(Atom::Fork(a)) => Some(a),
_ => None,
}
}
#[inline]
pub(crate) fn is_join(&self, atom_id: AtomID) -> bool {
match self.get_atom(atom_id) {
Some(Atom::Join(_)) => true,
_ => false,
}
}
#[inline]
pub(crate) fn get_join(&self, jid: JoinID) -> Option<&Join> {
match self.get_atom(jid.into()) {
Some(Atom::Join(a)) => Some(a),
_ => None,
}
}
pub fn get_antiport_id(&self, pid: PortID) -> Option<PortID> {
if let Some(port) = self.get_port(pid) {
if let Some(&(tx_id, rx_id)) = self.internal_nodes.get(&port.node_id) {
match port.face {
Face::Tx => {
if tx_id == pid {
return Some(rx_id)
} else {
panic!("Corrupt atom space")
}
}
Face::Rx => {
if rx_id == pid {
return Some(tx_id)
} else {
panic!("Corrupt atom space")
}
}
}
}
}
None
}
}
#[derive(Clone, Eq, Debug)]
pub(crate) enum Atom {
Tx(Port),
Rx(Port),
Link(Link),
Fork(Fork),
Join(Join),
Bottom,
}
impl Atom {
fn set_atom_id(&mut self, atom_id: AtomID) {
use Atom::*;
let prev_id = match self {
Tx(p) => &mut p.atom_id,
Rx(p) => &mut p.atom_id,
Link(l) => &mut l.atom_id,
Fork(f) => &mut f.atom_id,
Join(j) => &mut j.atom_id,
Bottom => panic!("Attempt to set ID of the bottom atom"),
};
if *prev_id == None {
*prev_id = Some(atom_id);
} else {
panic!("Attempt to reset ID of atom {:?}", self);
}
}
fn get_atom_id(&self) -> Option<AtomID> {
use Atom::*;
match self {
Tx(p) => p.atom_id,
Rx(p) => p.atom_id,
Link(l) => l.atom_id,
Fork(f) => f.atom_id,
Join(j) => j.atom_id,
Bottom => panic!("Attempt to get ID of the bottom atom"),
}
}
}
impl PartialEq for Atom {
#[rustfmt::skip]
fn eq(&self, other: &Self) -> bool {
use Atom::*;
match self {
Tx(p) => if let Tx(o) = other { p == o } else { false },
Rx(p) => if let Rx(o) = other { p == o } else { false },
Link(l) => if let Link(o) = other { l == o } else { false },
Fork(f) => if let Fork(o) = other { f == o } else { false },
Join(j) => if let Join(o) = other { j == o } else { false },
Bottom => panic!("Attempt to access the bottom atom"),
}
}
}
impl hash::Hash for Atom {
fn hash<H: hash::Hasher>(&self, state: &mut H) {
use Atom::*;
match self {
Tx(p) | Rx(p) => p.hash(state),
Link(l) => l.hash(state),
Fork(f) => f.hash(state),
Join(j) => j.hash(state),
Bottom => panic!("Attempt to access the bottom atom"),
}
}
}
#[derive(Clone, Eq, Debug)]
pub struct Port {
face: Face,
atom_id: Option<AtomID>,
node_id: NodeID,
}
impl Port {
pub(crate) fn new(face: Face, node_id: NodeID) -> Self {
Self { face, atom_id: None, node_id }
}
pub(crate) fn get_face(&self) -> Face {
self.face
}
pub fn get_atom_id(&self) -> AtomID {
self.atom_id.expect("Attempt to access an uninitialized port")
}
pub fn get_node_id(&self) -> NodeID {
self.node_id
}
}
impl PartialEq for Port {
fn eq(&self, other: &Self) -> bool {
self.node_id == other.node_id
}
}
impl hash::Hash for Port {
fn hash<H: hash::Hasher>(&self, state: &mut H) {
self.face.hash(state);
self.node_id.hash(state);
}
}
impl ExclusivelyContextual for Port {
fn format_locked(&self, ctx: &Context) -> Result<String, AcesError> {
let node_name = ctx
.get_node_name(self.get_node_id())
.ok_or_else(|| AcesError::from(AcesErrorKind::NodeMissingForPort(self.get_face())))?;
Ok(format!("[{} {}]", node_name, self.get_face()))
}
}
#[derive(Clone, Eq, Debug)]
pub struct Link {
atom_id: Option<AtomID>,
tx_port_id: PortID,
tx_node_id: NodeID,
rx_port_id: PortID,
rx_node_id: NodeID,
}
impl Link {
pub fn new(
tx_port_id: PortID,
tx_node_id: NodeID,
rx_port_id: PortID,
rx_node_id: NodeID,
) -> Self {
Self { atom_id: None, tx_port_id, tx_node_id, rx_port_id, rx_node_id }
}
pub fn get_atom_id(&self) -> AtomID {
self.atom_id.expect("Attempt to access an uninitialized link")
}
pub fn get_link_id(&self) -> LinkID {
LinkID(self.get_atom_id())
}
pub fn get_port_id(&self, face: Face) -> PortID {
if face == Face::Rx {
self.rx_port_id
} else {
self.tx_port_id
}
}
pub fn get_node_id(&self, face: Face) -> NodeID {
if face == Face::Rx {
self.rx_node_id
} else {
self.tx_node_id
}
}
pub fn get_tx_port_id(&self) -> PortID {
self.tx_port_id
}
pub fn get_tx_node_id(&self) -> NodeID {
self.tx_node_id
}
pub fn get_rx_port_id(&self) -> PortID {
self.rx_port_id
}
pub fn get_rx_node_id(&self) -> NodeID {
self.rx_node_id
}
}
impl PartialEq for Link {
fn eq(&self, other: &Self) -> bool {
self.tx_port_id == other.tx_port_id && self.rx_node_id == other.rx_node_id
}
}
impl hash::Hash for Link {
fn hash<H: hash::Hasher>(&self, state: &mut H) {
self.tx_port_id.hash(state);
self.tx_node_id.hash(state);
self.rx_port_id.hash(state);
self.rx_node_id.hash(state);
}
}
impl ExclusivelyContextual for Link {
fn format_locked(&self, ctx: &Context) -> Result<String, AcesError> {
let tx_node_name = ctx
.get_node_name(self.get_tx_node_id())
.ok_or_else(|| AcesError::from(AcesErrorKind::NodeMissingForLink(Face::Tx)))?;
let rx_node_name = ctx
.get_node_name(self.get_rx_node_id())
.ok_or_else(|| AcesError::from(AcesErrorKind::NodeMissingForLink(Face::Rx)))?;
Ok(format!("({} > {})", tx_node_name, rx_node_name))
}
}
#[derive(Clone, Eq)]
pub struct Harc {
atom_id: Option<AtomID>,
face: Face,
host_id: NodeID,
suit_ids: Vec<NodeID>,
}
impl Harc {
fn new_unchecked(face: Face, host_id: NodeID, suit_ids: Vec<NodeID>) -> Self {
if cfg!(debug_assertions) {
let mut sit = suit_ids.iter();
if let Some(nid) = sit.next() {
let mut prev_nid = *nid;
for &nid in sit {
assert!(prev_nid < nid, "Unordered suit");
prev_nid = nid;
}
} else {
panic!("Empty suit")
}
}
Harc { atom_id: None, face, host_id, suit_ids }
}
pub fn new_fork<I>(host_id: NodeID, suit_ids: I) -> Self
where
I: IntoIterator<Item = NodeID>,
{
let suit_ids: BTreeSet<_> = suit_ids.into_iter().collect();
if suit_ids.is_empty() {
}
Self::new_fork_unchecked(host_id, suit_ids)
}
pub fn new_join<I>(host_id: NodeID, suit_ids: I) -> Self
where
I: IntoIterator<Item = NodeID>,
{
let suit_ids: BTreeSet<_> = suit_ids.into_iter().collect();
if suit_ids.is_empty() {
}
Self::new_join_unchecked(host_id, suit_ids)
}
pub fn new_fork_unchecked<I>(host_id: NodeID, suit_ids: I) -> Self
where
I: IntoIterator<Item = NodeID>,
{
let suit_ids: Vec<_> = suit_ids.into_iter().collect();
trace!("New fork: {:?} -> {:?}", host_id, suit_ids);
Harc::new_unchecked(Face::Tx, host_id, suit_ids)
}
pub fn new_join_unchecked<I>(host_id: NodeID, suit_ids: I) -> Self
where
I: IntoIterator<Item = NodeID>,
{
let suit_ids: Vec<_> = suit_ids.into_iter().collect();
trace!("New join: {:?} <- {:?}", host_id, suit_ids);
Harc::new_unchecked(Face::Rx, host_id, suit_ids)
}
pub fn get_atom_id(&self) -> AtomID {
match self.face {
Face::Tx => self.atom_id.expect("Attempt to access an uninitialized fork"),
Face::Rx => self.atom_id.expect("Attempt to access an uninitialized join"),
}
}
pub fn get_fork_id(&self) -> Option<ForkID> {
match self.face {
Face::Tx => Some(ForkID(self.get_atom_id())),
Face::Rx => None,
}
}
pub fn get_join_id(&self) -> Option<JoinID> {
match self.face {
Face::Tx => None,
Face::Rx => Some(JoinID(self.get_atom_id())),
}
}
pub fn get_host_id(&self) -> NodeID {
self.host_id
}
pub fn get_suit_ids(&self) -> &[NodeID] {
self.suit_ids.as_slice()
}
}
impl fmt::Debug for Harc {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(
f,
"{} {{ atom_id: ",
match self.face {
Face::Tx => "Fork",
Face::Rx => "Join",
}
)?;
self.atom_id.fmt(f)?;
write!(f, ", host_id: ")?;
self.host_id.fmt(f)?;
write!(f, ", suit_ids: ")?;
self.suit_ids.fmt(f)?;
write!(f, " }}")
}
}
impl PartialEq for Harc {
#[inline]
fn eq(&self, other: &Self) -> bool {
self.face == other.face && self.host_id == other.host_id && self.suit_ids == other.suit_ids
}
}
impl hash::Hash for Harc {
fn hash<H: hash::Hasher>(&self, state: &mut H) {
self.face.hash(state);
self.host_id.hash(state);
self.suit_ids.hash(state);
}
}
impl ExclusivelyContextual for Harc {
fn format_locked(&self, ctx: &Context) -> Result<String, AcesError> {
let host_name = ctx.get_node_name(self.get_host_id()).ok_or(match self.face {
Face::Tx => AcesError::from(AcesErrorKind::NodeMissingForFork(Face::Tx)),
Face::Rx => AcesError::from(AcesErrorKind::NodeMissingForJoin(Face::Rx)),
})?;
let suit_names: Result<Vec<_>, AcesError> = self
.get_suit_ids()
.iter()
.map(|&node_id| {
ctx.get_node_name(node_id).ok_or(match self.face {
Face::Tx => AcesError::from(AcesErrorKind::NodeMissingForFork(Face::Rx)),
Face::Rx => AcesError::from(AcesErrorKind::NodeMissingForJoin(Face::Tx)),
})
})
.collect();
match self.face {
Face::Tx => Ok(format!("({} > {:?})", host_name, suit_names?)),
Face::Rx => Ok(format!("({:?} > {})", suit_names?, host_name)),
}
}
}
pub type Fork = Harc;
pub type Join = Harc;
pub trait Atomic:
From<AtomID> + Into<AtomID> + Contextual + Copy + PartialEq + Eq + PartialOrd + Ord
{
fn into_node_id(this: InContext<Self>) -> Option<NodeID>;
fn into_node_id_docked(this: InContext<Self>, _dock: Face) -> Option<NodeID> {
Self::into_node_id(this)
}
fn into_sat_literal(self, negated: bool) -> sat::Literal;
}
impl Atomic for PortID {
fn into_node_id(this: InContext<Self>) -> Option<NodeID> {
this.using_context(|pid, ctx| ctx.get_port(*pid).map(|port| port.get_node_id()))
}
#[inline]
fn into_sat_literal(self, negated: bool) -> sat::Literal {
sat::Literal::from_atom_id(self.get(), negated)
}
}
impl Atomic for LinkID {
fn into_node_id(_this: InContext<Self>) -> Option<NodeID> {
None
}
fn into_node_id_docked(this: InContext<Self>, dock: Face) -> Option<NodeID> {
this.using_context(|lid, ctx| ctx.get_link(*lid).map(|link| link.get_node_id(dock)))
}
#[inline]
fn into_sat_literal(self, negated: bool) -> sat::Literal {
sat::Literal::from_atom_id(self.get(), negated)
}
}
impl Atomic for ForkID {
fn into_node_id(this: InContext<Self>) -> Option<NodeID> {
this.using_context(|fid, ctx| ctx.get_fork(*fid).map(|fork| fork.get_host_id()))
}
#[inline]
fn into_sat_literal(self, negated: bool) -> sat::Literal {
sat::Literal::from_atom_id(self.get(), negated)
}
}
impl Atomic for JoinID {
fn into_node_id(this: InContext<Self>) -> Option<NodeID> {
this.using_context(|jid, ctx| ctx.get_join(*jid).map(|join| join.get_host_id()))
}
#[inline]
fn into_sat_literal(self, negated: bool) -> sat::Literal {
sat::Literal::from_atom_id(self.get(), negated)
}
}
#[cfg(test)]
mod tests {
use super::*;
fn new_tx_port(id: usize) -> Port {
Port::new(Face::Tx, NodeID(unsafe { ID::new_unchecked(id) }))
}
#[test]
#[should_panic(expected = "uninitialized")]
fn test_atom_uninitialized() {
let atom = Atom::Tx(new_tx_port(1));
let _ = atom.get_atom_id().expect("uninitialized");
}
#[test]
#[should_panic(expected = "bottom")]
fn test_atom_bottom() {
let mut atoms = AtomSpace::default();
let atom = Atom::Bottom;
let _ = atoms.do_share_atom(atom);
}
#[test]
#[should_panic(expected = "reset")]
fn test_atom_reset_id() {
let mut atoms = AtomSpace::default();
let mut atom = Atom::Tx(new_tx_port(1));
atom.set_atom_id(unsafe { AtomID::new_unchecked(1) });
let _ = atoms.do_share_atom(atom);
}
#[test]
fn test_atom_id() {
let mut atoms = AtomSpace::default();
let atom = Atom::Tx(new_tx_port(1));
let atom_id = atoms.do_share_atom(atom);
let atom = atoms.get_atom(atom_id).unwrap();
assert_eq!(atom.get_atom_id().unwrap(), atom_id);
}
}