1use std::hash::{BuildHasher, BuildHasherDefault, Hash, RandomState};
4use std::rc::Rc;
5
6use derivative::Derivative;
7use ustr::{IdentityHasher, Ustr};
8
9use super::theory::*;
10use crate::dbl::{category::*, model::*, theory::DblTheory};
11use crate::validate::{self, Validate};
12use crate::{one::*, zero::*};
13
14#[derive(Clone, PartialEq, Eq)]
16pub enum TabOb<V, E> {
17 Basic(V),
19
20 Tabulated(Box<TabMor<V, E>>),
22}
23
24impl<V, E> From<V> for TabOb<V, E> {
25 fn from(value: V) -> Self {
26 TabOb::Basic(value)
27 }
28}
29
30impl<V, E> TabOb<V, E> {
31 pub fn basic(self) -> Option<V> {
33 match self {
34 TabOb::Basic(v) => Some(v),
35 _ => None,
36 }
37 }
38
39 pub fn tabulated(self) -> Option<TabMor<V, E>> {
41 match self {
42 TabOb::Tabulated(mor) => Some(*mor),
43 _ => None,
44 }
45 }
46
47 pub fn unwrap_basic(self) -> V {
49 self.basic().expect("Object should be a basic object")
50 }
51
52 pub fn unwrap_tabulated(self) -> TabMor<V, E> {
54 self.tabulated().expect("Object should be a tabulated morphism")
55 }
56}
57
58#[derive(Clone, PartialEq, Eq)]
63pub enum TabEdge<V, E> {
64 Basic(E),
66
67 Square {
69 dom: Box<TabMor<V, E>>,
71
72 cod: Box<TabMor<V, E>>,
74
75 pre: Box<TabEdge<V, E>>,
77
78 post: Box<TabEdge<V, E>>,
80 },
81}
82
83impl<V, E> From<E> for TabEdge<V, E> {
84 fn from(value: E) -> Self {
85 TabEdge::Basic(value)
86 }
87}
88
89pub type TabMor<V, E> = Path<TabOb<V, E>, TabEdge<V, E>>;
91
92impl<V, E> From<E> for TabMor<V, E> {
93 fn from(value: E) -> Self {
94 Path::single(value.into())
95 }
96}
97
98#[derive(Clone, Derivative)]
99#[derivative(Default(bound = ""))]
100#[derivative(PartialEq(bound = "V: Eq + Hash, E: Eq + Hash"))]
101#[derivative(Eq(bound = "V: Eq + Hash, E: Eq + Hash"))]
102struct DiscreteTabGenerators<V, E> {
103 objects: HashFinSet<V>,
104 morphisms: HashFinSet<E>,
105 dom: HashColumn<E, TabOb<V, E>>,
106 cod: HashColumn<E, TabOb<V, E>>,
107}
108
109impl<V, E> Graph for DiscreteTabGenerators<V, E>
110where
111 V: Eq + Clone + Hash,
112 E: Eq + Clone + Hash,
113{
114 type V = TabOb<V, E>;
115 type E = TabEdge<V, E>;
116
117 fn has_vertex(&self, ob: &Self::V) -> bool {
118 match ob {
119 TabOb::Basic(v) => self.objects.contains(v),
120 TabOb::Tabulated(p) => (*p).contained_in(self),
121 }
122 }
123
124 fn has_edge(&self, edge: &Self::E) -> bool {
125 match edge {
126 TabEdge::Basic(e) => {
127 self.morphisms.contains(e) && self.dom.is_set(e) && self.cod.is_set(e)
128 }
129 TabEdge::Square {
130 dom,
131 cod,
132 pre,
133 post,
134 } => {
135 if !(dom.contained_in(self) && cod.contained_in(self)) {
136 return false;
137 }
138 let path1 = dom.clone().concat_in(self, Path::single(*post.clone()));
139 let path2 = Path::single(*pre.clone()).concat_in(self, *cod.clone());
140 path1.is_some() && path2.is_some() && path1 == path2
141 }
142 }
143 }
144
145 fn src(&self, edge: &Self::E) -> Self::V {
146 match edge {
147 TabEdge::Basic(e) => {
148 self.dom.apply_to_ref(e).expect("Domain of morphism should be defined")
149 }
150 TabEdge::Square { dom, .. } => TabOb::Tabulated(dom.clone()),
151 }
152 }
153
154 fn tgt(&self, edge: &Self::E) -> Self::V {
155 match edge {
156 TabEdge::Basic(e) => {
157 self.cod.apply_to_ref(e).expect("Codomain of morphism should be defined")
158 }
159 TabEdge::Square { cod, .. } => TabOb::Tabulated(cod.clone()),
160 }
161 }
162}
163
164#[derive(Clone, Derivative)]
172#[derivative(PartialEq(bound = "Id: Eq + Hash, ThId: Eq + Hash"))]
173#[derivative(Eq(bound = "Id: Eq + Hash, ThId: Eq + Hash"))]
174pub struct DiscreteTabModel<Id, ThId, S = RandomState> {
175 #[derivative(PartialEq(compare_with = "Rc::ptr_eq"))]
176 theory: Rc<DiscreteTabTheory<ThId, ThId, S>>,
177 generators: DiscreteTabGenerators<Id, Id>,
178 ob_types: IndexedHashColumn<Id, TabObType<ThId, ThId>>,
180 mor_types: IndexedHashColumn<Id, TabMorType<ThId, ThId>>,
181}
182
183pub type UstrDiscreteTabModel = DiscreteTabModel<Ustr, Ustr, BuildHasherDefault<IdentityHasher>>;
186
187impl<Id, ThId, S> DiscreteTabModel<Id, ThId, S>
188where
189 Id: Eq + Clone + Hash,
190 ThId: Eq + Clone + Hash,
191 S: BuildHasher,
192{
193 pub fn new(theory: Rc<DiscreteTabTheory<ThId, ThId, S>>) -> Self {
195 Self {
196 theory,
197 generators: Default::default(),
198 ob_types: Default::default(),
199 mor_types: Default::default(),
200 }
201 }
202
203 pub fn tabulated(&self, mor: TabMor<Id, Id>) -> TabOb<Id, Id> {
205 TabOb::Tabulated(Box::new(mor))
206 }
207
208 pub fn tabulated_gen(&self, f: Id) -> TabOb<Id, Id> {
210 self.tabulated(Path::single(TabEdge::Basic(f)))
211 }
212
213 pub fn iter_invalid(&self) -> impl Iterator<Item = InvalidDblModel<Id>> + '_ {
215 type Invalid<Id> = InvalidDblModel<Id>;
216 let ob_errors = self.generators.objects.iter().filter_map(|x| {
217 if self.ob_types.get(&x).is_some_and(|typ| self.theory.has_ob_type(typ)) {
218 None
219 } else {
220 Some(Invalid::ObType(x))
221 }
222 });
223 let mor_errors = self.generators.morphisms.iter().flat_map(|e| {
224 let mut errs = Vec::new();
225 let dom = self.generators.dom.get(&e).filter(|x| self.has_ob(x));
226 let cod = self.generators.cod.get(&e).filter(|x| self.has_ob(x));
227 if dom.is_none() {
228 errs.push(Invalid::Dom(e.clone()));
229 }
230 if cod.is_none() {
231 errs.push(Invalid::Cod(e.clone()));
232 }
233 if let Some(mor_type) =
234 self.mor_types.get(&e).filter(|typ| self.theory.has_mor_type(typ))
235 {
236 if dom.is_some_and(|x| self.ob_type(x) != self.theory.src(mor_type)) {
237 errs.push(Invalid::DomType(e.clone()));
238 }
239 if cod.is_some_and(|x| self.ob_type(x) != self.theory.tgt(mor_type)) {
240 errs.push(Invalid::CodType(e.clone()));
241 }
242 } else {
243 errs.push(Invalid::MorType(e));
244 }
245 errs.into_iter()
246 });
247 ob_errors.chain(mor_errors)
248 }
249}
250
251impl<Id, ThId, S> Category for DiscreteTabModel<Id, ThId, S>
252where
253 Id: Eq + Clone + Hash,
254{
255 type Ob = TabOb<Id, Id>;
256 type Mor = TabMor<Id, Id>;
257
258 fn has_ob(&self, x: &Self::Ob) -> bool {
259 self.generators.has_vertex(x)
260 }
261 fn has_mor(&self, path: &Self::Mor) -> bool {
262 path.contained_in(&self.generators)
263 }
264 fn dom(&self, path: &Self::Mor) -> Self::Ob {
265 path.src(&self.generators)
266 }
267 fn cod(&self, path: &Self::Mor) -> Self::Ob {
268 path.tgt(&self.generators)
269 }
270
271 fn compose(&self, path: Path<Self::Ob, Self::Mor>) -> Self::Mor {
272 path.flatten_in(&self.generators).expect("Paths should be composable")
273 }
274}
275
276impl<Id, ThId, S> FgCategory for DiscreteTabModel<Id, ThId, S>
277where
278 Id: Eq + Clone + Hash,
279{
280 type ObGen = Id;
281 type MorGen = Id;
282
283 fn ob_generators(&self) -> impl Iterator<Item = Self::ObGen> {
284 self.generators.objects.iter()
285 }
286 fn mor_generators(&self) -> impl Iterator<Item = Self::MorGen> {
287 self.generators.morphisms.iter()
288 }
289
290 fn mor_generator_dom(&self, f: &Self::MorGen) -> Self::Ob {
291 self.generators.dom.apply_to_ref(f).expect("Domain should be defined")
292 }
293 fn mor_generator_cod(&self, f: &Self::MorGen) -> Self::Ob {
294 self.generators.cod.apply_to_ref(f).expect("Codomain should be defined")
295 }
296}
297
298impl<Id, ThId, S> DblModel for DiscreteTabModel<Id, ThId, S>
299where
300 Id: Eq + Clone + Hash,
301 ThId: Eq + Clone + Hash,
302 S: BuildHasher,
303{
304 type ObType = TabObType<ThId, ThId>;
305 type MorType = TabMorType<ThId, ThId>;
306 type ObOp = TabObOp<ThId, ThId>;
307 type MorOp = TabMorOp<ThId, ThId>;
308 type Theory = DiscreteTabTheory<ThId, ThId, S>;
309
310 fn theory(&self) -> &Self::Theory {
311 &self.theory
312 }
313
314 fn ob_type(&self, ob: &Self::Ob) -> Self::ObType {
315 match ob {
316 TabOb::Basic(x) => self.ob_generator_type(x),
317 TabOb::Tabulated(m) => TabObType::Tabulator(Box::new(self.mor_type(m))),
318 }
319 }
320
321 fn mor_type(&self, mor: &Self::Mor) -> Self::MorType {
322 let types = mor.clone().map(
323 |x| self.ob_type(&x),
324 |edge| match edge {
325 TabEdge::Basic(f) => self.mor_generator_type(&f),
326 TabEdge::Square { dom, .. } => {
327 let typ = self.mor_type(&dom); TabMorType::Hom(Box::new(TabObType::Tabulator(Box::new(typ))))
329 }
330 },
331 );
332 self.theory.compose_types(types).expect("Morphism types should have composite")
333 }
334
335 fn ob_act(&self, _ob: Self::Ob, _op: &Self::ObOp) -> Self::Ob {
336 panic!("Action on objects not implemented")
337 }
338
339 fn mor_act(&self, _mor: Self::Mor, _op: &Self::MorOp) -> Self::Mor {
340 panic!("Action on morphisms not implemented")
341 }
342}
343
344impl<Id, ThId, S> FgDblModel for DiscreteTabModel<Id, ThId, S>
345where
346 Id: Eq + Clone + Hash,
347 ThId: Eq + Clone + Hash,
348 S: BuildHasher,
349{
350 fn ob_generator_type(&self, ob: &Self::ObGen) -> Self::ObType {
351 self.ob_types.apply_to_ref(ob).expect("Object should have type")
352 }
353 fn mor_generator_type(&self, mor: &Self::MorGen) -> Self::MorType {
354 self.mor_types.apply_to_ref(mor).expect("Morphism should have type")
355 }
356
357 fn ob_generators_with_type(&self, obtype: &Self::ObType) -> impl Iterator<Item = Self::ObGen> {
358 self.ob_types.preimage(obtype)
359 }
360 fn mor_generators_with_type(
361 &self,
362 mortype: &Self::MorType,
363 ) -> impl Iterator<Item = Self::MorGen> {
364 self.mor_types.preimage(mortype)
365 }
366}
367
368impl<Id, ThId, S> MutDblModel for DiscreteTabModel<Id, ThId, S>
369where
370 Id: Eq + Clone + Hash,
371 ThId: Eq + Clone + Hash,
372 S: BuildHasher,
373{
374 fn add_ob(&mut self, x: Self::ObGen, ob_type: Self::ObType) {
375 self.ob_types.set(x.clone(), ob_type);
376 self.generators.objects.insert(x);
377 }
378
379 fn make_mor(&mut self, f: Self::MorGen, mor_type: Self::MorType) {
380 self.mor_types.set(f.clone(), mor_type);
381 self.generators.morphisms.insert(f);
382 }
383
384 fn get_dom(&self, f: &Self::MorGen) -> Option<&Self::Ob> {
385 self.generators.dom.get(f)
386 }
387 fn get_cod(&self, f: &Self::MorGen) -> Option<&Self::Ob> {
388 self.generators.cod.get(f)
389 }
390 fn set_dom(&mut self, f: Self::MorGen, x: Self::Ob) {
391 self.generators.dom.set(f, x);
392 }
393 fn set_cod(&mut self, f: Self::MorGen, x: Self::Ob) {
394 self.generators.cod.set(f, x);
395 }
396}
397
398impl<Id, ThId, S> Validate for DiscreteTabModel<Id, ThId, S>
399where
400 Id: Eq + Clone + Hash,
401 ThId: Eq + Clone + Hash,
402 S: BuildHasher,
403{
404 type ValidationError = InvalidDblModel<Id>;
405
406 fn validate(&self) -> Result<(), nonempty::NonEmpty<Self::ValidationError>> {
407 validate::wrap_errors(self.iter_invalid())
408 }
409}
410
411#[cfg(test)]
412mod tests {
413 use nonempty::nonempty;
414 use ustr::ustr;
415
416 use super::*;
417 use crate::stdlib::theories::*;
418
419 #[test]
420 fn validate() {
421 let th = Rc::new(th_category_links());
422 let mut model = DiscreteTabModel::new(th);
423 let (x, f) = (ustr("x"), ustr("f"));
424 model.add_ob(x, TabObType::Basic(ustr("Object")));
425 model.add_mor(f, TabOb::Basic(x), TabOb::Basic(x), TabMorType::Basic(ustr("Link")));
426 assert_eq!(model.validate(), Err(nonempty![InvalidDblModel::CodType(f)]));
427 }
428}