1pub use antecedent_core::NodeRef;
9
10use crate::error::GraphError;
11
12#[repr(transparent)]
14#[derive(Clone, Copy, Debug, Eq, PartialEq, Ord, PartialOrd, Hash)]
15pub struct DenseNodeId(u32);
16
17impl DenseNodeId {
18 #[must_use]
20 pub const fn from_raw(raw: u32) -> Self {
21 Self(raw)
22 }
23
24 pub fn try_from_usize(i: usize) -> Result<Self, GraphError> {
26 let raw = u32::try_from(i).map_err(|_| GraphError::TooManyNodes)?;
27 Ok(Self::from_raw(raw))
28 }
29
30 #[must_use]
32 pub const fn raw(self) -> u32 {
33 self.0
34 }
35
36 #[must_use]
38 pub const fn as_usize(self) -> usize {
39 self.0 as usize
40 }
41}
42
43#[derive(Clone, Copy, Debug, Eq, PartialEq, Hash)]
45pub enum Endpoint {
46 Tail,
48 Arrow,
50 Circle,
52 Conflict,
57}
58
59#[derive(Clone, Copy, Debug, Default, Eq, PartialEq, Hash)]
63pub enum MiddleMark {
64 Unknown,
66 Left,
68 Right,
70 Both,
72 #[default]
74 Empty,
75}
76
77impl MiddleMark {
78 #[must_use]
80 pub const fn is_definite(self) -> bool {
81 matches!(self, Self::Empty)
82 }
83
84 #[must_use]
86 pub const fn apply(self, update: Self) -> Self {
87 use MiddleMark::{Both, Empty, Left, Right, Unknown};
88 match (self, update) {
89 (Empty, _) | (_, Empty) => Empty,
90 (Both, _) | (_, Both) | (Left, Right) | (Right, Left) => Both,
91 (Unknown, other) | (other, Unknown) => other,
92 (Left, Left) => Left,
93 (Right, Right) => Right,
94 }
95 }
96}
97
98#[derive(Clone, Copy, Debug, Eq, PartialEq, Hash)]
100pub struct MarkedEdge {
101 pub a: DenseNodeId,
103 pub b: DenseNodeId,
105 pub at_a: Endpoint,
107 pub at_b: Endpoint,
109 pub middle: MiddleMark,
111}
112
113impl MarkedEdge {
114 #[must_use]
116 pub const fn directed(from: DenseNodeId, to: DenseNodeId) -> Self {
117 Self {
118 a: from,
119 b: to,
120 at_a: Endpoint::Tail,
121 at_b: Endpoint::Arrow,
122 middle: MiddleMark::Empty,
123 }
124 }
125
126 #[must_use]
128 pub fn undirected(a: DenseNodeId, b: DenseNodeId) -> Self {
129 if a.raw() <= b.raw() {
130 Self { a, b, at_a: Endpoint::Tail, at_b: Endpoint::Tail, middle: MiddleMark::Empty }
131 } else {
132 Self {
133 a: b,
134 b: a,
135 at_a: Endpoint::Tail,
136 at_b: Endpoint::Tail,
137 middle: MiddleMark::Empty,
138 }
139 }
140 }
141
142 #[must_use]
144 pub const fn is_dag_directed(self) -> bool {
145 matches!(
146 (self.at_a, self.at_b),
147 (Endpoint::Tail, Endpoint::Arrow) | (Endpoint::Arrow, Endpoint::Tail)
148 )
149 }
150
151 #[must_use]
153 pub const fn is_undirected(self) -> bool {
154 matches!((self.at_a, self.at_b), (Endpoint::Tail, Endpoint::Tail))
155 }
156
157 #[must_use]
159 pub const fn is_bidirected(self) -> bool {
160 matches!((self.at_a, self.at_b), (Endpoint::Arrow, Endpoint::Arrow))
161 }
162
163 #[must_use]
165 pub const fn is_conflict(self) -> bool {
166 matches!((self.at_a, self.at_b), (Endpoint::Conflict, Endpoint::Conflict))
167 }
168
169 #[must_use]
171 pub fn bidirected(a: DenseNodeId, b: DenseNodeId) -> Self {
172 if a.raw() <= b.raw() {
173 Self { a, b, at_a: Endpoint::Arrow, at_b: Endpoint::Arrow, middle: MiddleMark::Empty }
174 } else {
175 Self {
176 a: b,
177 b: a,
178 at_a: Endpoint::Arrow,
179 at_b: Endpoint::Arrow,
180 middle: MiddleMark::Empty,
181 }
182 }
183 }
184
185 #[must_use]
187 pub fn conflict(a: DenseNodeId, b: DenseNodeId) -> Self {
188 if a.raw() <= b.raw() {
189 Self {
190 a,
191 b,
192 at_a: Endpoint::Conflict,
193 at_b: Endpoint::Conflict,
194 middle: MiddleMark::Empty,
195 }
196 } else {
197 Self {
198 a: b,
199 b: a,
200 at_a: Endpoint::Conflict,
201 at_b: Endpoint::Conflict,
202 middle: MiddleMark::Empty,
203 }
204 }
205 }
206
207 #[must_use]
209 pub const fn with_middle(mut self, middle: MiddleMark) -> Self {
210 self.middle = middle;
211 self
212 }
213
214 #[must_use]
216 pub const fn is_cpdag_legal(self) -> bool {
217 matches!(
218 (self.at_a, self.at_b),
219 (Endpoint::Tail, Endpoint::Arrow | Endpoint::Tail)
220 | (Endpoint::Arrow, Endpoint::Tail)
221 | (Endpoint::Conflict, Endpoint::Conflict)
222 )
223 }
224
225 #[must_use]
227 pub const fn is_admg_legal(self) -> bool {
228 matches!(
229 (self.at_a, self.at_b),
230 (Endpoint::Tail | Endpoint::Arrow, Endpoint::Arrow) | (Endpoint::Arrow, Endpoint::Tail)
231 )
232 }
233
234 #[must_use]
236 pub fn parent_child(self) -> Option<(DenseNodeId, DenseNodeId)> {
237 match (self.at_a, self.at_b) {
238 (Endpoint::Tail, Endpoint::Arrow) => Some((self.a, self.b)),
239 (Endpoint::Arrow, Endpoint::Tail) => Some((self.b, self.a)),
240 _ => None,
241 }
242 }
243}