linnet/half_edge/subgraph/
full.rs

1use std::ops::{Range, RangeFrom, RangeInclusive, RangeTo, RangeToInclusive};
2
3use bitvec::vec::BitVec;
4
5use crate::half_edge::{
6    involution::{Hedge, HedgePair},
7    nodestore::NodeStorageOps,
8    HedgeGraph,
9};
10
11use super::{Inclusion, SubGraph};
12
13#[derive(Clone, Debug, Copy, Hash, PartialEq, Eq, PartialOrd, Ord)]
14/// Represents a subgraph that is either completely full (contains all possible half-edges
15/// in the graph context) or entirely empty.
16///
17/// This struct provides a lightweight way to specify these two common boundary conditions
18/// for subgraph operations. The state (full or empty) is determined by the sign
19/// of the `size` field.
20pub struct FullOrEmpty {
21    /// Stores the size of the graph context.
22    /// If `size` is positive, it represents a full subgraph of that many half-edges.
23    /// If `size` is negative or zero, it represents an empty subgraph; the absolute
24    /// value can still indicate the size of the parent graph context.
25    size: isize,
26}
27
28impl FullOrEmpty {
29    pub fn full(size: usize) -> FullOrEmpty {
30        FullOrEmpty {
31            size: size as isize,
32        }
33    }
34    pub fn empty(size: usize) -> FullOrEmpty {
35        FullOrEmpty {
36            size: -(size as isize),
37        }
38    }
39}
40
41impl Inclusion<Hedge> for FullOrEmpty {
42    fn includes(&self, hedge_id: &Hedge) -> bool {
43        (hedge_id.0 as isize) < self.size
44    }
45
46    fn intersects(&self, other: &Hedge) -> bool {
47        (other.0 as isize) < self.size
48    }
49}
50
51impl Inclusion<HedgePair> for FullOrEmpty {
52    fn includes(&self, hedge_id: &HedgePair) -> bool {
53        match hedge_id {
54            HedgePair::Split { source, sink, .. } | HedgePair::Paired { source, sink } => {
55                (source.0 as isize) < self.size && (sink.0 as isize) < self.size
56            }
57            HedgePair::Unpaired { hedge, .. } => (hedge.0 as isize) < self.size,
58        }
59    }
60
61    fn intersects(&self, other: &HedgePair) -> bool {
62        match other {
63            HedgePair::Split { source, sink, .. } | HedgePair::Paired { source, sink } => {
64                (source.0 as isize) < self.size && (sink.0 as isize) < self.size
65            }
66            HedgePair::Unpaired { hedge, .. } => (hedge.0 as isize) < self.size,
67        }
68    }
69}
70
71impl Inclusion<FullOrEmpty> for FullOrEmpty {
72    fn includes(&self, other: &FullOrEmpty) -> bool {
73        // true
74        self.size == other.size
75    }
76
77    fn intersects(&self, other: &FullOrEmpty) -> bool {
78        // true
79        self.size == other.size
80    }
81}
82
83impl Inclusion<BitVec> for FullOrEmpty {
84    fn includes(&self, other: &BitVec) -> bool {
85        self.size == other.size() as isize
86    }
87
88    fn intersects(&self, other: &BitVec) -> bool {
89        self.size == other.size() as isize
90    }
91}
92
93impl Inclusion<Range<Hedge>> for FullOrEmpty {
94    fn includes(&self, other: &Range<Hedge>) -> bool {
95        if let Ok(size) = usize::try_from(self.size) {
96            size >= other.end.0
97        } else {
98            other.start >= other.end
99        }
100    }
101
102    fn intersects(&self, other: &Range<Hedge>) -> bool {
103        if let Ok(size) = usize::try_from(self.size) {
104            size >= other.end.0
105        } else {
106            other.start >= other.end
107        }
108    }
109}
110
111impl Inclusion<RangeTo<Hedge>> for FullOrEmpty {
112    fn includes(&self, other: &RangeTo<Hedge>) -> bool {
113        if let Ok(size) = usize::try_from(self.size) {
114            size >= other.end.0
115        } else {
116            false
117        }
118    }
119
120    fn intersects(&self, other: &RangeTo<Hedge>) -> bool {
121        if let Ok(size) = usize::try_from(self.size) {
122            size >= other.end.0
123        } else {
124            false
125        }
126    }
127}
128
129impl Inclusion<RangeToInclusive<Hedge>> for FullOrEmpty {
130    fn includes(&self, other: &RangeToInclusive<Hedge>) -> bool {
131        if let Ok(size) = usize::try_from(self.size) {
132            size > other.end.0
133        } else {
134            false
135        }
136    }
137
138    fn intersects(&self, other: &RangeToInclusive<Hedge>) -> bool {
139        if let Ok(size) = usize::try_from(self.size) {
140            size > other.end.0
141        } else {
142            false
143        }
144    }
145}
146
147impl Inclusion<RangeFrom<Hedge>> for FullOrEmpty {
148    fn includes(&self, other: &RangeFrom<Hedge>) -> bool {
149        if let Ok(size) = usize::try_from(self.size) {
150            size > other.start.0
151        } else {
152            false
153        }
154    }
155
156    fn intersects(&self, other: &RangeFrom<Hedge>) -> bool {
157        if let Ok(size) = usize::try_from(self.size) {
158            size > other.start.0
159        } else {
160            false
161        }
162    }
163}
164
165impl Inclusion<RangeInclusive<Hedge>> for FullOrEmpty {
166    fn includes(&self, other: &RangeInclusive<Hedge>) -> bool {
167        if let Ok(size) = usize::try_from(self.size) {
168            size > other.end().0
169        } else {
170            other.start() > other.end()
171        }
172    }
173
174    fn intersects(&self, other: &RangeInclusive<Hedge>) -> bool {
175        if let Ok(size) = usize::try_from(self.size) {
176            size > other.end().0
177        } else {
178            other.start() > other.end()
179        }
180    }
181}
182
183/// An iterator that yields [`Hedge`] identifiers over a given numerical range.
184///
185/// This is used as the `BaseIter` for the [`FullOrEmpty`] subgraph type when it
186/// represents a full graph, allowing iteration over all hedges from `0` up to `size-1`.
187pub struct RangeHedgeIter {
188    /// The underlying `Range<usize>` that this iterator traverses.
189    iter: Range<usize>,
190}
191
192impl From<Range<Hedge>> for RangeHedgeIter {
193    fn from(value: Range<Hedge>) -> Self {
194        RangeHedgeIter {
195            iter: value.start.0..value.end.0,
196        }
197    }
198}
199
200impl Iterator for RangeHedgeIter {
201    type Item = Hedge;
202
203    fn next(&mut self) -> Option<Self::Item> {
204        self.iter.next().map(Hedge)
205    }
206}
207
208impl SubGraph for FullOrEmpty {
209    type Base = FullOrEmpty;
210    type BaseIter<'a> = RangeHedgeIter;
211    fn nedges<E, V, H, N: NodeStorageOps<NodeData = V>>(
212        &self,
213        graph: &HedgeGraph<E, V, H, N>,
214    ) -> usize {
215        let mut count = 0;
216        for i in self.included_iter() {
217            if i != graph.inv(i) && self.includes(&graph.inv(i)) {
218                count += 1;
219            }
220        }
221        count / 2
222    }
223
224    fn has_greater(&self, _hedge: Hedge) -> bool {
225        false
226    }
227
228    fn size(&self) -> usize {
229        self.size.unsigned_abs()
230    }
231
232    fn join_mut(&mut self, other: Self) {
233        if self.is_empty() && other.is_empty() {
234            self.size -= other.size
235        } else if !self.is_empty() && !other.is_empty() {
236            self.size += other.size
237        }
238    }
239
240    fn included_iter(&self) -> Self::BaseIter<'_> {
241        if self.is_empty() {
242            (Hedge(1)..Hedge(0)).into()
243        } else {
244            (Hedge(0)..Hedge(self.size as usize)).into()
245        }
246    }
247
248    fn nhedges(&self) -> usize {
249        self.size.try_into().unwrap_or(0)
250    }
251
252    fn empty(size: usize) -> Self {
253        FullOrEmpty {
254            size: -(size as isize),
255        }
256    }
257
258    fn included(&self) -> &FullOrEmpty {
259        self
260    }
261
262    fn string_label(&self) -> String {
263        if self.is_empty() {
264            "∅".into()
265        } else {
266            "full".into()
267        }
268    }
269    fn is_empty(&self) -> bool {
270        self.size <= 0
271    }
272}