Skip to main content

linnet/half_edge/subgraph/
contracted.rs

1use std::ops::{Range, RangeFrom, RangeInclusive, RangeTo, RangeToInclusive};
2
3use crate::half_edge::builder::HedgeNodeBuilder;
4use crate::half_edge::involution::HedgePair;
5use crate::half_edge::nodestore::NodeStorageOps;
6use crate::half_edge::subgraph::{SuBitGraph, SubGraphLike, SubGraphOps};
7use crate::half_edge::{Hedge, HedgeGraph};
8
9use super::{internal::InternalSubGraph, SubSetLike, SubSetOps};
10use super::{Inclusion, ModifySubSet, SubSetIter};
11
12#[derive(Clone, Debug, PartialEq, Eq, Hash)]
13#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
14#[cfg_attr(feature = "bincode", derive(bincode::Encode, bincode::Decode))]
15/// Represents a subgraph that may have an "internal" component (a set of fully contained edges)
16/// and an "external" component (a set of incident half-edges or "hairs" connecting to the rest of the graph).
17///
18/// This structure is useful for representing nodes in a quotient graph or for graph contraction,
19/// where a complex subgraph is treated as a single entity with defined connection points.
20///
21/// The `SubGraphOps` (like union, intersection) for `ContractedSubGraph` have specific semantics
22/// that differentiate between the `internal_graph` and `allhedges` components. For example,
23/// intersection might merge internal parts while intersecting the external connections.
24pub struct ContractedSubGraph {
25    /// Represents the set of edges that are considered fully internal to this contracted region.
26    /// This `InternalSubGraph` itself should not contain any dangling or unpaired half-edges
27    /// with respect to its own definition.
28    pub internal_graph: InternalSubGraph, // cannot have any external hedges (i.e. unpaired hedges)
29    /// A bitmask representing all half-edges associated with this `ContractedSubGraph`.
30    /// This includes all hedges in `internal_graph` plus any "hairs" or external
31    /// half-edges that connect this contracted region to the rest of the main graph.
32    pub allhedges: SuBitGraph, // all hedges , including that are in the internal graph.
33}
34
35impl Inclusion<Hedge> for ContractedSubGraph {
36    fn includes(&self, hedge_id: &Hedge) -> bool {
37        self.internal_graph.includes(hedge_id) || self.allhedges.includes(hedge_id)
38    }
39
40    fn intersects(&self, other: &Hedge) -> bool {
41        self.includes(other)
42    }
43}
44
45impl Inclusion<HedgePair> for ContractedSubGraph {
46    fn includes(&self, hedge_id: &HedgePair) -> bool {
47        self.internal_graph.includes(hedge_id) || self.allhedges.includes(hedge_id)
48    }
49
50    fn intersects(&self, other: &HedgePair) -> bool {
51        self.includes(other)
52    }
53}
54
55impl Inclusion<ContractedSubGraph> for ContractedSubGraph {
56    fn includes(&self, other: &ContractedSubGraph) -> bool {
57        self.internal_graph.includes(&other.internal_graph)
58    }
59
60    fn intersects(&self, other: &ContractedSubGraph) -> bool {
61        self.allhedges.intersects(&other.allhedges)
62    }
63}
64
65impl Inclusion<SuBitGraph> for ContractedSubGraph {
66    fn includes(&self, other: &SuBitGraph) -> bool {
67        self.internal_graph.includes(other) || self.allhedges.includes(other)
68    }
69
70    fn intersects(&self, other: &SuBitGraph) -> bool {
71        self.allhedges.intersects(other)
72    }
73}
74
75impl Inclusion<Range<Hedge>> for ContractedSubGraph {
76    fn includes(&self, other: &Range<Hedge>) -> bool {
77        self.allhedges.includes(other)
78    }
79
80    fn intersects(&self, other: &Range<Hedge>) -> bool {
81        self.allhedges.intersects(other)
82    }
83}
84
85impl Inclusion<RangeTo<Hedge>> for ContractedSubGraph {
86    fn includes(&self, other: &RangeTo<Hedge>) -> bool {
87        self.allhedges.includes(other)
88    }
89
90    fn intersects(&self, other: &RangeTo<Hedge>) -> bool {
91        (0..other.end.0).any(|a| self.includes(&Hedge(a)))
92    }
93}
94
95impl Inclusion<RangeToInclusive<Hedge>> for ContractedSubGraph {
96    fn includes(&self, other: &RangeToInclusive<Hedge>) -> bool {
97        self.allhedges.includes(other)
98    }
99
100    fn intersects(&self, other: &RangeToInclusive<Hedge>) -> bool {
101        (0..=other.end.0).any(|a| self.includes(&Hedge(a)))
102    }
103}
104
105impl Inclusion<RangeFrom<Hedge>> for ContractedSubGraph {
106    fn includes(&self, other: &RangeFrom<Hedge>) -> bool {
107        self.allhedges.includes(other)
108    }
109
110    fn intersects(&self, other: &RangeFrom<Hedge>) -> bool {
111        (other.start.0..).any(|a| self.includes(&Hedge(a)))
112    }
113}
114
115impl Inclusion<RangeInclusive<Hedge>> for ContractedSubGraph {
116    fn includes(&self, other: &RangeInclusive<Hedge>) -> bool {
117        self.allhedges.includes(other)
118    }
119
120    fn intersects(&self, other: &RangeInclusive<Hedge>) -> bool {
121        (other.start().0..=other.end().0).any(|a| self.includes(&Hedge(a)))
122    }
123}
124impl SubGraphLike for ContractedSubGraph {
125    fn nedges<E, V, H, N: NodeStorageOps<NodeData = V>>(
126        &self,
127        graph: &HedgeGraph<E, V, H, N>,
128    ) -> usize {
129        self.allhedges.nedges(graph)
130    }
131}
132impl SubSetLike for ContractedSubGraph {
133    type Base = SuBitGraph;
134    type BaseIter<'a> = SubSetIter<'a>;
135    fn n_included(&self) -> usize {
136        self.allhedges.n_included()
137    }
138
139    fn size(&self) -> usize {
140        self.allhedges.size()
141    }
142
143    fn has_greater(&self, hedge: Hedge) -> bool {
144        self.allhedges.has_greater(hedge)
145    }
146
147    fn join_mut(&mut self, other: Self) {
148        self.internal_graph.join_mut(other.internal_graph);
149        self.allhedges.join_mut(other.allhedges);
150    }
151
152    fn included(&self) -> &SuBitGraph {
153        self.allhedges.included()
154    }
155
156    fn included_iter(&self) -> Self::BaseIter<'_> {
157        self.allhedges.included_iter()
158    }
159
160    // fn hairs(&self, node: &HedgeNode) -> SuBitGraph {
161    //     let mut hairs = self.allhedges.intersection(&node.hairs);
162    //     hairs.subtract_with(&self.internal_graph.filter);
163    //     hairs
164    // }
165
166    fn string_label(&self) -> String {
167        self.allhedges.string_label() + "⊛" + self.internal_graph.string_label().as_str()
168    }
169
170    fn is_empty(&self) -> bool {
171        self.allhedges.is_empty()
172    }
173    fn empty(size: usize) -> Self {
174        Self {
175            internal_graph: InternalSubGraph::empty(size),
176            allhedges: SuBitGraph::empty(size),
177        }
178    }
179
180    fn from_base62(label: &str, size: usize) -> Option<Self> {
181        let allhedges = SuBitGraph::from_base62(label, size)?;
182        Some(Self {
183            internal_graph: InternalSubGraph::empty(size),
184            allhedges,
185        })
186    }
187}
188
189impl SubGraphOps for ContractedSubGraph {
190    fn complement<E, V, H, N: NodeStorageOps<NodeData = V>>(
191        &self,
192        graph: &HedgeGraph<E, V, H, N>,
193    ) -> Self {
194        let externalhedges =
195            (!self.allhedges.clone()).intersection(&!self.internal_graph.filter.clone());
196
197        Self {
198            internal_graph: InternalSubGraph::empty(graph.n_hedges()),
199            allhedges: externalhedges,
200        }
201    }
202}
203impl SubSetOps for ContractedSubGraph {
204    fn union_with_iter(&mut self, other: impl Iterator<Item = Hedge>) {
205        for h in other {
206            self.allhedges.add(h);
207        }
208    }
209
210    fn union_with(&mut self, other: &Self) {
211        // union is the intersection of the internal graphs, and the union of the external graph.
212        self.internal_graph.intersect_with(&other.internal_graph);
213        self.allhedges.union_with(&other.allhedges);
214    }
215
216    fn intersect_with(&mut self, other: &Self) {
217        // intersection is the union of the internal graphs, and the intersection of the external graph.
218        self.internal_graph.union_with(&other.internal_graph);
219        self.allhedges.intersect_with(&other.allhedges);
220    }
221
222    fn sym_diff_with(&mut self, other: &Self) {
223        // external hedges that are only present in one of the two graphs.
224        // contracted parts unioned
225        self.internal_graph.union_with(&other.internal_graph);
226        self.allhedges.sym_diff_with(&other.allhedges);
227    }
228
229    fn empty_intersection(&self, other: &Self) -> bool {
230        self.allhedges.empty_intersection(&other.allhedges)
231    }
232
233    fn empty_union(&self, other: &Self) -> bool {
234        self.allhedges.empty_union(&other.allhedges)
235    }
236
237    fn subtract_with(&mut self, other: &Self) {
238        self.internal_graph.union_with(&other.internal_graph);
239        self.allhedges.subtract_with(&other.allhedges);
240    }
241}
242
243impl ContractedSubGraph {
244    pub fn all_edges(&self) -> SuBitGraph {
245        self.internal_graph.filter.union(&self.allhedges)
246    }
247
248    pub fn weakly_disjoint(&self, other: &ContractedSubGraph) -> bool {
249        let internals = self
250            .internal_graph
251            .filter
252            .intersection(&other.internal_graph.filter);
253
254        internals.is_empty()
255    }
256
257    pub fn strongly_disjoint(&self, other: &ContractedSubGraph) -> bool {
258        let internals = self
259            .internal_graph
260            .filter
261            .intersection(&other.internal_graph.filter);
262
263        let externals_in_self = self.internal_graph.filter.intersection(&other.allhedges);
264        let externals_in_other = self.allhedges.intersection(&other.internal_graph.filter);
265
266        internals.is_empty() && externals_in_self.is_empty() && externals_in_other.is_empty()
267    }
268
269    pub fn internal_graph_union(&self, other: &ContractedSubGraph) -> InternalSubGraph {
270        InternalSubGraph {
271            filter: self
272                .internal_graph
273                .filter
274                .union(&other.internal_graph.filter),
275            loopcount: None,
276        }
277    }
278
279    pub fn from_builder<V>(builder: &HedgeNodeBuilder<V>, len: usize) -> Self {
280        let internal_graph = InternalSubGraph::empty(len);
281        let mut externalhedges = SuBitGraph::empty(len);
282
283        for hedge in &builder.hedges {
284            externalhedges.add(*hedge);
285        }
286
287        ContractedSubGraph {
288            internal_graph,
289            allhedges: externalhedges,
290        }
291    }
292
293    pub fn is_node(&self) -> bool {
294        self.internal_graph.is_empty()
295    }
296
297    pub fn is_subgraph(&self) -> bool {
298        !self.is_node()
299    }
300}