1use std::hash::{BuildHasher, BuildHasherDefault, Hash, RandomState};
4use std::ops::Range;
5
6use derivative::Derivative;
7use ref_cast::RefCast;
8use ustr::{IdentityHasher, Ustr};
9
10use crate::dbl::{category::*, graph::ProedgeGraph, tree::DblTree};
11use crate::one::{Graph, Path};
12use crate::zero::*;
13
14#[derive(Clone, Debug, PartialEq, Eq, Hash)]
18pub enum TabObType<V, E> {
19 Basic(V),
21
22 Tabulator(Box<TabMorType<V, E>>),
24}
25
26#[derive(Clone, Debug, PartialEq, Eq, Hash)]
28pub enum TabMorType<V, E> {
29 Basic(E),
31
32 Hom(Box<TabObType<V, E>>),
34}
35
36#[derive(Clone, Debug, PartialEq, Eq)]
38pub enum TabObProj<V, E> {
39 Src(TabMorType<V, E>),
41
42 Tgt(TabMorType<V, E>),
44}
45
46impl<V, E> TabObProj<V, E> {
47 pub fn mor_type(&self) -> &TabMorType<V, E> {
49 match self {
50 TabObProj::Src(m) | TabObProj::Tgt(m) => m,
51 }
52 }
53}
54
55pub type TabObOp<V, E> = Path<TabObType<V, E>, TabObProj<V, E>>;
57
58#[derive(Clone, Debug, PartialEq, Eq)]
60pub enum TabMorProj<V, E> {
61 Cone(TabMorType<V, E>),
63
64 Src(TabMorType<V, E>),
66
67 Tgt(TabMorType<V, E>),
69}
70
71impl<V, E> TabMorProj<V, E> {
72 pub fn mor_type(&self) -> &TabMorType<V, E> {
74 match self {
75 TabMorProj::Cone(m) | TabMorProj::Src(m) | TabMorProj::Tgt(m) => m,
76 }
77 }
78
79 fn src(self) -> TabObProj<V, E> {
81 match self {
82 TabMorProj::Cone(m) | TabMorProj::Src(m) => TabObProj::Src(m),
83 TabMorProj::Tgt(m) => TabObProj::Tgt(m),
84 }
85 }
86
87 fn tgt(self) -> TabObProj<V, E> {
89 match self {
90 TabMorProj::Src(m) => TabObProj::Src(m),
91 TabMorProj::Cone(m) | TabMorProj::Tgt(m) => TabObProj::Tgt(m),
92 }
93 }
94}
95
96#[derive(Clone, Debug, PartialEq, Eq)]
98pub struct TabMorOp<V, E> {
99 dom: Path<TabObType<V, E>, TabMorType<V, E>>,
100 projections: Vec<TabMorProj<V, E>>,
101}
102
103#[derive(Clone, Derivative)]
115#[derivative(Default(bound = "S: Default"))]
116pub struct DiscreteTabTheory<V, E, S = RandomState> {
117 ob_types: HashFinSet<V, S>,
118 mor_types: HashFinSet<E, S>,
119 src: HashColumn<E, TabObType<V, E>, S>,
120 tgt: HashColumn<E, TabObType<V, E>, S>,
121 compose_map: HashColumn<(E, E), TabMorType<V, E>>,
122}
123
124pub type UstrDiscreteTabTheory = DiscreteTabTheory<Ustr, Ustr, BuildHasherDefault<IdentityHasher>>;
126
127impl<V, E, S> DiscreteTabTheory<V, E, S>
128where
129 V: Eq + Clone + Hash,
130 E: Eq + Clone + Hash,
131 S: BuildHasher,
132{
133 pub fn new() -> Self
135 where
136 S: Default,
137 {
138 Default::default()
139 }
140
141 pub fn tabulator(&self, m: TabMorType<V, E>) -> TabObType<V, E> {
143 TabObType::Tabulator(Box::new(m))
144 }
145
146 pub fn unary_projection(&self, proj: TabMorProj<V, E>) -> TabMorOp<V, E> {
148 TabMorOp {
149 dom: self.unit(self.tabulator(proj.mor_type().clone())).unwrap().into(),
150 projections: vec![proj],
151 }
152 }
153
154 pub fn add_ob_type(&mut self, v: V) -> bool {
156 self.ob_types.insert(v)
157 }
158
159 pub fn add_mor_type(&mut self, e: E, src: TabObType<V, E>, tgt: TabObType<V, E>) -> bool {
161 self.src.set(e.clone(), src);
162 self.tgt.set(e.clone(), tgt);
163 self.make_mor_type(e)
164 }
165
166 pub fn make_mor_type(&mut self, e: E) -> bool {
168 self.mor_types.insert(e)
169 }
170}
171
172#[derive(RefCast)]
174#[repr(transparent)]
175struct DiscTabTheoryProjGraph<V, E, S>(DiscreteTabTheory<V, E, S>);
176
177impl<V, E, S> Graph for DiscTabTheoryProjGraph<V, E, S>
178where
179 V: Eq + Clone + Hash,
180 E: Eq + Clone + Hash,
181 S: BuildHasher,
182{
183 type V = TabObType<V, E>;
184 type E = TabObProj<V, E>;
185
186 fn has_vertex(&self, x: &Self::V) -> bool {
187 self.0.has_ob(x)
188 }
189 fn has_edge(&self, proj: &Self::E) -> bool {
190 self.0.has_proarrow(proj.mor_type())
191 }
192
193 fn src(&self, proj: &Self::E) -> Self::V {
194 TabObType::Tabulator(Box::new(proj.mor_type().clone()))
195 }
196 fn tgt(&self, proj: &Self::E) -> Self::V {
197 match proj {
198 TabObProj::Src(m) => self.0.src(m),
199 TabObProj::Tgt(m) => self.0.tgt(m),
200 }
201 }
202}
203
204impl<V, E, S> VDblCategory for DiscreteTabTheory<V, E, S>
205where
206 V: Eq + Clone + Hash,
207 E: Eq + Clone + Hash,
208 S: BuildHasher,
209{
210 type Ob = TabObType<V, E>;
211 type Arr = TabObOp<V, E>;
212 type Pro = TabMorType<V, E>;
213 type Cell = TabMorOp<V, E>;
214
215 fn has_ob(&self, ob: &Self::Ob) -> bool {
216 match ob {
217 TabObType::Basic(v) => self.ob_types.contains(v),
218 TabObType::Tabulator(m) => self.has_proarrow(m),
219 }
220 }
221 fn has_arrow(&self, path: &Self::Arr) -> bool {
222 path.contained_in(DiscTabTheoryProjGraph::ref_cast(self))
223 }
224 fn has_proarrow(&self, pro: &Self::Pro) -> bool {
225 match pro {
226 TabMorType::Basic(e) => self.mor_types.contains(e),
227 TabMorType::Hom(x) => self.has_ob(x),
228 }
229 }
230 fn has_cell(&self, cell: &Self::Cell) -> bool {
231 let graph = ProedgeGraph::ref_cast(UnderlyingDblGraph::ref_cast(self));
232 if !cell.dom.contained_in(graph) {
233 return false;
234 }
235 let (src, tgt) = (self.cell_src(cell), self.cell_tgt(cell));
236 self.has_arrow(&src)
237 && self.has_arrow(&tgt)
238 && cell.dom.src(graph) == self.dom(&src)
239 && cell.dom.tgt(graph) == self.dom(&tgt)
240 }
241
242 fn dom(&self, path: &Self::Arr) -> Self::Ob {
243 path.src(DiscTabTheoryProjGraph::ref_cast(self))
244 }
245 fn cod(&self, path: &Self::Arr) -> Self::Ob {
246 path.tgt(DiscTabTheoryProjGraph::ref_cast(self))
247 }
248 fn src(&self, m: &Self::Pro) -> Self::Ob {
249 match m {
250 TabMorType::Basic(e) => {
251 self.src.apply_to_ref(e).expect("Source of morphism type should be defined")
252 }
253 TabMorType::Hom(x) => (**x).clone(),
254 }
255 }
256 fn tgt(&self, m: &Self::Pro) -> Self::Ob {
257 match m {
258 TabMorType::Basic(e) => {
259 self.tgt.apply_to_ref(e).expect("Target of morphism type should be defined")
260 }
261 TabMorType::Hom(x) => (**x).clone(),
262 }
263 }
264
265 fn cell_dom(&self, cell: &Self::Cell) -> Path<Self::Ob, Self::Pro> {
266 cell.dom.clone()
267 }
268 fn cell_cod(&self, cell: &Self::Cell) -> Self::Pro {
269 self.composite(cell.dom.clone()).expect("Path should have a composite")
270 }
271 fn cell_src(&self, cell: &Self::Cell) -> Self::Arr {
272 let graph = ProedgeGraph::ref_cast(UnderlyingDblGraph::ref_cast(self));
273 Path::collect(cell.projections.iter().cloned().map(|proj| proj.src()))
274 .unwrap_or_else(|| Path::empty(cell.dom.src(graph)))
275 }
276 fn cell_tgt(&self, cell: &Self::Cell) -> Self::Arr {
277 let graph = ProedgeGraph::ref_cast(UnderlyingDblGraph::ref_cast(self));
278 Path::collect(cell.projections.iter().cloned().map(|proj| proj.tgt()))
279 .unwrap_or_else(|| Path::empty(cell.dom.tgt(graph)))
280 }
281
282 fn compose(&self, path: Path<Self::Ob, Self::Arr>) -> Self::Arr {
283 path.flatten()
284 }
285
286 fn compose_cells(&self, tree: DblTree<Self::Arr, Self::Pro, Self::Cell>) -> Self::Cell {
287 let graph = UnderlyingDblGraph::ref_cast(self);
288 let dom = tree.dom(graph);
289 let src = self.compose(tree.src(graph));
290 let tgt = self.compose(tree.tgt(graph));
291 assert_eq!(src.len(), tgt.len(), "Source/target boundaries should have equal length");
292 let projections = std::iter::zip(src, tgt)
293 .map(|pair| match pair {
294 (TabObProj::Src(m), TabObProj::Tgt(n)) if m == n => TabMorProj::Cone(m),
295 (TabObProj::Src(m), TabObProj::Src(n)) if m == n => TabMorProj::Src(m),
296 (TabObProj::Tgt(m), TabObProj::Tgt(n)) if m == n => TabMorProj::Tgt(m),
297 _ => panic!("Projection cells should have compatible source/target boundaries"),
298 })
299 .collect();
300 TabMorOp { dom, projections }
301 }
302}
303
304impl<V, E, S> VDCWithComposites for DiscreteTabTheory<V, E, S>
305where
306 V: Eq + Clone + Hash,
307 E: Eq + Clone + Hash,
308 S: BuildHasher,
309{
310 fn composite2(&self, m: Self::Pro, n: Self::Pro) -> Option<Self::Pro> {
311 let mn = match (m, n) {
312 (m, TabMorType::Hom(y)) if self.tgt(&m) == *y => m,
313 (TabMorType::Hom(x), n) if self.src(&n) == *x => n,
314 (TabMorType::Basic(d), TabMorType::Basic(e)) => {
315 self.compose_map.apply((d, e)).expect("Composition should be defined")
316 }
317 _ => panic!("Ill-typed composite of morphism types in discrete tabulator theory"),
318 };
319 Some(mn)
320 }
321 fn unit(&self, x: Self::Ob) -> Option<Self::Pro> {
322 Some(TabMorType::Hom(Box::new(x)))
323 }
324 fn composite(&self, path: Path<Self::Ob, Self::Pro>) -> Option<Self::Pro> {
325 Some(path.reduce(|x| self.unit(x).unwrap(), |m, n| self.composite2(m, n).unwrap()))
326 }
327
328 fn composite_ext(&self, path: Path<Self::Ob, Self::Pro>) -> Option<Self::Cell> {
329 Some(TabMorOp {
330 dom: path,
331 projections: vec![],
332 })
333 }
334
335 fn through_composite(&self, cell: Self::Cell, range: Range<usize>) -> Option<Self::Cell> {
336 let graph = ProedgeGraph::ref_cast(UnderlyingDblGraph::ref_cast(self));
337 let TabMorOp { dom, projections } = cell;
338 Some(TabMorOp {
339 dom: dom.replace_subpath(graph, range, |sub| self.composite(sub).unwrap().into()),
340 projections,
341 })
342 }
343}
344
345#[cfg(test)]
346mod tests {
347 use super::*;
348 use crate::dbl::theory::DblTheory;
349
350 #[test]
351 fn theory_interface() {
352 let mut th = DiscreteTabTheory::<char, char>::new();
353 th.add_ob_type('*');
354 let x = TabObType::Basic('*');
355 assert!(th.has_ob_type(&x));
356 let tab = th.tabulator(th.hom_type(x.clone()));
357 assert!(th.has_ob_type(&tab));
358 assert!(th.has_mor_type(&th.hom_type(tab.clone())));
359
360 th.add_mor_type('m', x.clone(), tab.clone());
361 let m = TabMorType::Basic('m');
362 assert!(th.has_mor_type(&m));
363 assert_eq!(th.src_type(&m), x);
364 assert_eq!(th.tgt_type(&m), tab);
365
366 let proj = th.unary_projection(TabMorProj::Cone(th.hom_type(x.clone())));
367 let cell = th.compose_cells2(
368 [th.composite2_ext(th.hom_type(tab.clone()), th.hom_type(tab.clone())).unwrap()],
369 proj.clone(),
370 );
371 assert!(th.has_mor_op(&cell));
372 assert!(matches!(th.src_op(&cell).only(), Some(TabObProj::Src(_))));
373 assert!(matches!(th.tgt_op(&cell).only(), Some(TabObProj::Tgt(_))));
374
375 let proj_src = th.unary_projection(TabMorProj::Src(th.hom_type(x.clone())));
376 let cell_alt = th.compose_cells2(
377 [proj_src, proj],
378 th.composite2_ext(th.hom_type(x.clone()), th.hom_type(x.clone())).unwrap(),
379 );
380 assert!(th.has_mor_op(&cell_alt));
381 assert_eq!(cell, cell_alt);
382 }
383}