1use derive_more::From;
31use ego_tree::NodeRef;
32use itertools::{EitherOrBoth::Both, Itertools, zip_eq};
33
34use super::graph::VDblGraph;
35use crate::one::{path::Path, tree::*, tree_algorithms::*};
36
37#[derive(Clone, Debug, PartialEq, Eq)]
41pub enum DblNode<E, Sq> {
42 Cell(Sq),
44
45 Spine(E),
52}
53
54impl<E, Sq> DblNode<E, Sq> {
55 pub fn dom<V, ProE>(
59 &self,
60 graph: &impl VDblGraph<V = V, E = E, ProE = ProE, Sq = Sq>,
61 ) -> Path<V, ProE> {
62 match self {
63 DblNode::Cell(sq) => graph.square_dom(sq),
64 DblNode::Spine(e) => Path::empty(graph.dom(e)),
65 }
66 }
67
68 pub fn cod<V, ProE>(
72 &self,
73 graph: &impl VDblGraph<V = V, E = E, ProE = ProE, Sq = Sq>,
74 ) -> Path<V, ProE> {
75 match self {
76 DblNode::Cell(sq) => graph.square_cod(sq).into(),
77 DblNode::Spine(e) => Path::empty(graph.cod(e)),
78 }
79 }
80
81 pub fn src(&self, graph: &impl VDblGraph<E = E, Sq = Sq>) -> E
83 where
84 E: Clone,
85 {
86 match self {
87 DblNode::Cell(sq) => graph.square_src(sq),
88 DblNode::Spine(e) => e.clone(),
89 }
90 }
91
92 pub fn tgt(&self, graph: &impl VDblGraph<E = E, Sq = Sq>) -> E
94 where
95 E: Clone,
96 {
97 match self {
98 DblNode::Cell(sq) => graph.square_tgt(sq),
99 DblNode::Spine(e) => e.clone(),
100 }
101 }
102
103 pub fn arity(&self, graph: &impl VDblGraph<E = E, Sq = Sq>) -> usize {
105 match self {
106 DblNode::Cell(sq) => graph.arity(sq),
107 DblNode::Spine(_) => 0,
108 }
109 }
110
111 pub fn contained_in(&self, graph: &impl VDblGraph<E = E, Sq = Sq>) -> bool {
113 match self {
114 DblNode::Cell(sq) => graph.has_square(sq),
115 DblNode::Spine(e) => graph.has_edge(e),
116 }
117 }
118}
119
120#[derive(Clone, Debug, From, PartialEq, Eq)]
128pub struct DblTree<E, ProE, Sq>(pub OpenTree<ProE, DblNode<E, Sq>>);
129
130impl<E, ProE, Sq> DblTree<E, ProE, Sq> {
131 pub fn empty(p: ProE) -> Self {
133 OpenTree::empty(p).into()
134 }
135
136 pub fn single(sq: Sq, graph: &impl VDblGraph<E = E, ProE = ProE, Sq = Sq>) -> Self {
138 let n = graph.arity(&sq);
139 OpenTree::single(DblNode::Cell(sq), n).into()
140 }
141
142 pub fn two_level(
144 squares: impl IntoIterator<Item = Sq>,
145 base: Sq,
146 graph: &impl VDblGraph<E = E, ProE = ProE, Sq = Sq>,
147 ) -> Self {
148 let subtrees = squares.into_iter().map(|sq| {
149 let n = graph.arity(&sq);
150 OpenTree::single(DblNode::Cell(sq), n)
151 });
152 OpenTree::graft(subtrees, DblNode::Cell(base)).into()
153 }
154
155 pub fn dom<V>(
157 &self,
158 graph: &impl VDblGraph<V = V, E = E, ProE = ProE, Sq = Sq>,
159 ) -> Path<V, ProE>
160 where
161 ProE: Clone,
162 {
163 fn dom_at<V, E, ProE, Sq>(
165 node: NodeRef<'_, Option<DblNode<E, Sq>>>,
166 graph: &impl VDblGraph<V = V, E = E, ProE = ProE, Sq = Sq>,
167 ) -> Path<V, ProE> {
168 let path = node.get_value().unwrap().dom(graph);
169 if node.children().all(|node| node.is_boundary()) {
170 return path;
172 }
173 if path.is_empty() && node.has_children() {
174 let child = node.children().exactly_one().ok().expect("Invalid nullary composite");
176 return dom_at(child, graph);
177 }
178 let paths = zip_eq(node.children(), path)
180 .map(|(child, proedge)| {
181 if child.is_boundary() {
182 Path::single(proedge)
183 } else {
184 dom_at(child, graph)
185 }
186 })
187 .collect_vec();
188 Path::collect(paths).unwrap().flatten()
189 }
190
191 match &self.0 {
192 OpenTree::Id(p) => p.clone().into(),
193 OpenTree::Comp(tree) => dom_at(tree.root(), graph),
194 }
195 }
196
197 pub fn cod(&self, graph: &impl VDblGraph<E = E, ProE = ProE, Sq = Sq>) -> ProE
199 where
200 ProE: Clone,
201 {
202 match &self.0 {
203 OpenTree::Id(p) => p.clone(),
204 OpenTree::Comp(tree) => tree
205 .root()
206 .get_value()
207 .expect("Root of a double tree should not be null")
208 .cod(graph)
209 .only()
210 .expect("Root of a double tree should not be a spine"),
211 }
212 }
213
214 pub fn src<V>(&self, graph: &impl VDblGraph<V = V, E = E, ProE = ProE, Sq = Sq>) -> Path<V, E>
216 where
217 E: Clone,
218 {
219 match &self.0 {
220 OpenTree::Id(p) => Path::empty(graph.src(p)),
221 OpenTree::Comp(tree) => {
222 let mut edges = tree
223 .root()
224 .left_boundary()
225 .filter_map(|node| node.get_value().map(|dn| dn.src(graph)))
226 .collect_vec();
227 edges.reverse();
228 Path::from_vec(edges).unwrap()
229 }
230 }
231 }
232
233 pub fn tgt<V>(&self, graph: &impl VDblGraph<V = V, E = E, ProE = ProE, Sq = Sq>) -> Path<V, E>
235 where
236 E: Clone,
237 {
238 match &self.0 {
239 OpenTree::Id(p) => Path::empty(graph.tgt(p)),
240 OpenTree::Comp(tree) => {
241 let mut edges = tree
242 .root()
243 .right_boundary()
244 .filter_map(|node| node.get_value().map(|dn| dn.tgt(graph)))
245 .collect_vec();
246 edges.reverse();
247 Path::from_vec(edges).unwrap()
248 }
249 }
250 }
251
252 pub fn arity(&self, graph: &impl VDblGraph<E = E, ProE = ProE, Sq = Sq>) -> usize {
257 match &self.0 {
258 OpenTree::Id(_) => 1,
259 OpenTree::Comp(tree) => tree
260 .root()
261 .boundary()
262 .filter(|node| node.parent_value().unwrap().arity(graph) != 0)
263 .count(),
264 }
265 }
266
267 pub fn only(self) -> Option<Sq> {
271 self.0.only().and_then(|node| {
272 match node {
273 DblNode::Cell(sq) => Some(sq),
274 DblNode::Spine(_) => None,
276 }
277 })
278 }
279
280 pub fn contained_in(&self, graph: &impl VDblGraph<E = E, ProE = ProE, Sq = Sq>) -> bool
285 where
286 E: Eq + Clone,
287 ProE: Eq + Clone,
288 {
289 let tree = match &self.0 {
290 OpenTree::Id(p) => return graph.has_proedge(p),
291 OpenTree::Comp(tree) => tree,
292 };
293 let root = tree.root();
294 let mut traverse = root.bfs();
295 while let Some(node) = traverse.next() {
296 let Some(dn) = node.value() else {
297 continue;
298 };
299 if !dn.contained_in(graph) {
301 return false;
302 }
303 if !traverse
305 .peek_at_same_level()
306 .is_none_or(|next| Some(dn.tgt(graph)) == next.get_value().map(|dn| dn.src(graph)))
307 {
308 return false;
309 }
310
311 let path = dn.dom(graph);
313 if path.is_empty() && node.has_children() {
314 if node
316 .children()
317 .exactly_one()
318 .ok()
319 .is_none_or(|child| child.get_value().is_some_and(|dn| dn.cod(graph) != path))
320 {
321 return false;
322 }
323 continue;
324 }
325 for pair in node.children().zip_longest(path) {
327 let Both(child, proedge) = pair else {
328 return false;
329 };
330 if child.get_value().is_some_and(|cn| cn.cod(graph) != Path::single(proedge)) {
331 return false;
332 }
333 }
334 }
335 true
336 }
337
338 pub fn is_isomorphic_to(&self, other: &Self) -> bool
343 where
344 E: Eq,
345 ProE: Eq,
346 Sq: Eq,
347 {
348 self.0.is_isomorphic_to(&other.0)
349 }
350
351 pub fn map<CodE, CodSq>(
353 self,
354 mut fn_e: impl FnMut(E) -> CodE,
355 mut fn_sq: impl FnMut(Sq) -> CodSq,
356 ) -> DblTree<CodE, ProE, CodSq> {
357 self.0
358 .map(|dn| match dn {
359 DblNode::Cell(sq) => DblNode::Cell(fn_sq(sq)),
360 DblNode::Spine(e) => DblNode::Spine(fn_e(e)),
361 })
362 .into()
363 }
364}
365
366impl<V, E, ProE, Sq> DblTree<Path<V, E>, ProE, DblTree<E, ProE, Sq>> {
367 pub fn flatten(self) -> DblTree<E, ProE, Sq> {
369 let tree = self.0.map(|dn| match dn {
370 DblNode::Cell(tree) => tree.0,
371 DblNode::Spine(path) => OpenTree::linear(path.into_iter().map(DblNode::Spine))
372 .expect("Spine should be a non-empty path"),
373 });
374 tree.flatten().into()
375 }
376}
377
378impl<Arr, ProE, Sq> DblTree<Arr, ProE, DblTree<Arr, ProE, Sq>> {
379 pub fn flatten(self) -> DblTree<Arr, ProE, Sq> {
381 let tree = self.0.map(|dn| match dn {
382 DblNode::Cell(tree) => tree.0,
383 DblNode::Spine(f) => OpenTree::single(DblNode::Spine(f), 1),
384 });
385 tree.flatten().into()
386 }
387}
388
389#[cfg(test)]
390mod tests {
391 use ego_tree::tree;
392 use nonempty::nonempty;
393
394 use super::super::category::{WalkingBimodule as Bimod, WalkingFunctor as Funct, *};
395 use super::*;
396
397 #[test]
398 fn tree_dom_cod() {
399 let bimod = Bimod::Main();
400 let graph = UnderlyingDblGraph(Bimod::Main());
401 let path = Path::Seq(nonempty![Bimod::Pro::Left, Bimod::Pro::Middle, Bimod::Pro::Right]);
402 let mid = bimod.composite_ext(path).unwrap();
403 let tree = DblTree::two_level(
404 vec![bimod.id_cell(Bimod::Pro::Left), mid.clone(), bimod.id_cell(Bimod::Pro::Right)],
405 mid.clone(),
406 &graph,
407 );
408 let tree_alt = tree!(
409 Some(mid.clone()) => {
410 Some(bimod.id_cell(Bimod::Pro::Left)) => { None },
411 Some(mid.clone()) => { None, None, None },
412 Some(bimod.id_cell(Bimod::Pro::Right)) => { None }
413 }
414 );
415 let tree_alt = DblTree(OpenTree::from(tree_alt).map(DblNode::Cell));
416 assert_eq!(tree, tree_alt);
417 assert!(tree.contained_in(&graph));
418
419 assert_eq!(tree.arity(&graph), 5);
420 assert_eq!(
421 tree.dom(&graph),
422 Path::Seq(nonempty![
423 Bimod::Pro::Left,
424 Bimod::Pro::Left,
425 Bimod::Pro::Middle,
426 Bimod::Pro::Right,
427 Bimod::Pro::Right
428 ])
429 );
430 assert_eq!(tree.cod(&graph), Bimod::Pro::Middle);
431
432 let tree = tree!(
434 Some(mid.clone()) => {
435 Some(bimod.id_cell(Bimod::Pro::Left)) => { None },
436 Some(mid.clone()) => { None, None, None }
437 }
438 );
439 assert!(!DblTree(OpenTree::from(tree).map(DblNode::Cell)).contained_in(&graph));
440 let tree = tree!(
441 Some(mid.clone()) => {
442 Some(bimod.id_cell(Bimod::Pro::Right)) => { None },
443 Some(mid.clone()) => { None, None, None },
444 Some(bimod.id_cell(Bimod::Pro::Left)) => { None }
445 }
446 );
447 assert!(!DblTree(OpenTree::from(tree).map(DblNode::Cell)).contained_in(&graph));
448 }
449
450 #[test]
451 fn tree_src_tgt() {
452 let funct = Funct::Main();
453 let graph = UnderlyingDblGraph(Funct::Main());
454 let f = Funct::Arr::Arrow;
455 let unit1 = funct.unit_ext(Funct::Ob::One).unwrap();
456 let tree =
457 DblTree(OpenTree::linear(vec![DblNode::Spine(f), DblNode::Cell(unit1)]).unwrap());
458 let tree_alt = DblTree(
459 tree!(
460 Some(DblNode::Cell(unit1)) => { Some(DblNode::Spine(f)) => { None } }
461 )
462 .into(),
463 );
464 assert_eq!(tree, tree_alt);
465 assert!(tree.contained_in(&graph));
466
467 assert_eq!(tree.src(&graph), Path::pair(f, Funct::Arr::One));
468 assert_eq!(tree.tgt(&graph), Path::pair(f, Funct::Arr::One));
469 assert!(tree.dom(&graph).is_empty());
470
471 let comp = funct.composite2_ext(Funct::Ob::One, Funct::Ob::One).unwrap();
473 let tree = DblTree(
474 tree!(
475 Some(DblNode::Cell(comp)) => {
476 Some(DblNode::Cell(unit1)) => {
477 Some(DblNode::Spine(Funct::Arr::One)) => { None }
478 },
479 Some(DblNode::Cell(unit1)) => {
480 Some(DblNode::Spine(f)) => { None }
481 },
482 }
483 )
484 .into(),
485 );
486 assert!(!tree.contained_in(&graph));
487 }
488
489 #[test]
490 fn flatten_tree() {
491 let bimod = Bimod::Main();
492 let graph = UnderlyingDblGraph(Bimod::Main());
493 let path = Path::Seq(nonempty![Bimod::Pro::Left, Bimod::Pro::Middle, Bimod::Pro::Right]);
494 let unitl = bimod.unit_ext(Bimod::Ob::Left).unwrap();
495 let unitr = bimod.unit_ext(Bimod::Ob::Right).unwrap();
496 let mid = bimod.composite_ext(path).unwrap();
497 let tree = tree!(
498 Some(mid.clone()) => {
499 Some(bimod.id_cell(Bimod::Pro::Left)) => {
500 Some(unitl.clone())
501 },
502 Some(mid) => {
503 Some(unitl),
504 Some(bimod.id_cell(Bimod::Pro::Middle)) => { None },
505 Some(unitr.clone())
506 },
507 Some(bimod.id_cell(Bimod::Pro::Right)) => {
508 Some(unitr),
509 }
510 }
511 );
512 let tree = DblTree(OpenTree::from(tree).map(DblNode::Cell));
513 assert_eq!(tree.dom(&graph), Path::single(Bimod::Pro::Middle));
514 assert_eq!(tree.cod(&graph), Bimod::Pro::Middle);
515
516 let outer: DblTree<Path<Bimod::Ob, Bimod::Ob>, _, _> =
518 OpenTree::single(DblNode::Cell(tree.clone()), tree.arity(&graph)).into();
519 assert_eq!(outer.flatten(), tree);
520
521 let outer: DblTree<Path<Bimod::Ob, Bimod::Ob>, _, _> =
523 tree.clone().map(Path::single, |dn| DblTree::single(dn, &graph));
524 let result = outer.flatten();
525 assert!(result.is_isomorphic_to(&tree));
526 }
527}