Skip to main content

ixa/entity/entity_set/
entity_set_iterator.rs

1//! Iterator implementation for [`EntitySet`].
2//!
3//! `EntitySetIterator` mirrors an `EntitySet` expression tree and evaluates nodes lazily.
4//! - `Source` iterates a concrete backing source (`Population`, contiguous
5//!   `PopulationRange`, index set, or property-backed source).
6//! - `Intersection` and `Difference` drive iteration from one branch and filter candidates using
7//!   membership checks.
8//! - `Union` yields the left branch first, then lazily activates the right branch (`Pending` to
9//!   `Active`) and filters out any IDs already present in the left branch via membership checks.
10//!
11//! The iterator is created through `EntitySet::into_iter()`.
12
13use crate::entity::entity_set::entity_set::{EntitySet, EntitySetInner};
14use crate::entity::entity_set::source_iterator::{PopulationRangeIterator, SourceIterator};
15use crate::entity::entity_set::source_set::SourceSet;
16use crate::entity::{Entity, EntityId, PopulationIterator};
17use crate::hashing::IndexSet;
18
19enum EntitySetIteratorInner<'a, E: Entity> {
20    Source(SourceIterator<'a, E>),
21    // The `IntersectionSources` variant is a micro-optimization to avoid recursive
22    // `EntitySet` membership checks in the most common case, improving tight-loop
23    // benchmark performance by 5%-15%.
24    IntersectionSources {
25        driver: SourceIterator<'a, E>,
26        filters: Vec<SourceSet<'a, E>>,
27    },
28    Intersection {
29        driver: Box<EntitySetIteratorInner<'a, E>>,
30        filters: Vec<EntitySet<'a, E>>,
31    },
32    Difference {
33        left: Box<EntitySetIteratorInner<'a, E>>,
34        right: EntitySet<'a, E>,
35    },
36    Union {
37        left: Box<EntitySetIteratorInner<'a, E>>,
38        right: UnionRightState<'a, E>,
39    },
40}
41
42enum UnionRightState<'a, E: Entity> {
43    Pending(Option<EntitySet<'a, E>>),
44    Active(Box<EntitySetIteratorInner<'a, E>>),
45}
46
47impl<'a, E: Entity> EntitySetIteratorInner<'a, E> {
48    fn empty_source() -> Self {
49        Self::Source(SourceIterator::PopulationRange(
50            PopulationRangeIterator::new(0..0),
51        ))
52    }
53
54    fn from_entity_set(set: EntitySet<'a, E>) -> Self {
55        match set.into_inner() {
56            EntitySetInner::Source(source) => {
57                if source.try_len() == Some(0) {
58                    Self::empty_source()
59                } else {
60                    Self::Source(source.into_iter())
61                }
62            }
63            EntitySetInner::Intersection(mut sets) => {
64                if sets.is_empty() {
65                    return Self::empty_source();
66                }
67                if sets.len() == 1 {
68                    return Self::from_entity_set(sets.pop().unwrap());
69                }
70
71                if sets.iter().all(EntitySet::is_source_leaf) {
72                    // `EntitySet::Intersection` stores operands sorted ascending by `cost_hint`.
73                    // Keep that order so membership checks short-circuit early on small filters.
74                    let mut set_iter = sets.into_iter();
75
76                    let first = set_iter.next().unwrap().into_source_leaf().unwrap();
77                    let driver = first.into_iter();
78
79                    let filters = set_iter
80                        .map(|set| set.into_source_leaf().unwrap())
81                        .collect();
82
83                    return Self::IntersectionSources { driver, filters };
84                }
85
86                // `EntitySet::Intersection` stores operands sorted ascending by `cost_hint`.
87                // Use the first set as the iteration driver and keep the remaining filters in
88                // ascending order for short-circuit-friendly `contains` checks.
89                let mut set_iter = sets.into_iter();
90                let driver = Box::new(Self::from_entity_set(set_iter.next().unwrap()));
91                Self::Intersection {
92                    driver,
93                    filters: set_iter.collect(),
94                }
95            }
96            EntitySetInner::Difference(left, right) => Self::Difference {
97                left: Box::new(Self::from_entity_set(*left)),
98                right: *right,
99            },
100            EntitySetInner::Union(left, right) => Self::Union {
101                left: Box::new(Self::from_entity_set(*left)),
102                right: UnionRightState::Pending(Some(*right)),
103            },
104        }
105    }
106
107    fn contains(&self, entity_id: EntityId<E>) -> bool {
108        match self {
109            Self::Source(iter) => iter.contains(entity_id),
110            Self::IntersectionSources { driver, filters } => {
111                driver.contains(entity_id) && filters.iter().all(|set| set.contains(entity_id))
112            }
113            Self::Intersection { driver, filters } => {
114                driver.contains(entity_id) && filters.iter().all(|set| set.contains(entity_id))
115            }
116            Self::Difference { left, right } => {
117                left.contains(entity_id) && !right.contains(entity_id)
118            }
119            Self::Union { left, right } => left.contains(entity_id) || right.contains(entity_id),
120        }
121    }
122}
123
124impl<'a, E: Entity> UnionRightState<'a, E> {
125    fn contains(&self, entity_id: EntityId<E>) -> bool {
126        match self {
127            Self::Pending(Some(set)) => set.contains(entity_id),
128            Self::Pending(None) => false,
129            Self::Active(iter) => iter.contains(entity_id),
130        }
131    }
132}
133
134impl<'a, E: Entity> EntitySetIteratorInner<'a, E> {
135    #[inline]
136    fn next_inner(&mut self) -> Option<EntityId<E>> {
137        match self {
138            Self::Source(source) => source.next(),
139            Self::IntersectionSources { driver, filters } => driver
140                .by_ref()
141                .find(|&entity_id| filters.iter().all(|filter| filter.contains(entity_id))),
142            Self::Intersection { driver, filters } => {
143                while let Some(entity_id) = driver.next_inner() {
144                    if filters.iter().all(|filter| filter.contains(entity_id)) {
145                        return Some(entity_id);
146                    }
147                }
148                None
149            }
150            Self::Difference { left, right } => {
151                while let Some(entity_id) = left.next_inner() {
152                    if !right.contains(entity_id) {
153                        return Some(entity_id);
154                    }
155                }
156                None
157            }
158            Self::Union { left, right } => loop {
159                if let Some(entity_id) = left.next_inner() {
160                    return Some(entity_id);
161                }
162
163                match right {
164                    UnionRightState::Pending(maybe_set) => {
165                        if let Some(set) = maybe_set.take() {
166                            *right = UnionRightState::Active(Box::new(Self::from_entity_set(set)));
167                        }
168                        continue;
169                    }
170                    UnionRightState::Active(right_iter) => {
171                        while let Some(entity_id) = right_iter.next_inner() {
172                            if !left.contains(entity_id) {
173                                return Some(entity_id);
174                            }
175                        }
176                        return None;
177                    }
178                }
179            },
180        }
181    }
182
183    #[inline]
184    fn size_hint_inner(&self) -> (usize, Option<usize>) {
185        match self {
186            Self::Source(source) => source.size_hint(),
187            Self::IntersectionSources { driver, .. } => {
188                let (_, upper) = driver.size_hint();
189                (0, upper)
190            }
191            Self::Intersection { driver, .. } => {
192                let (_, upper) = driver.size_hint_inner();
193                (0, upper)
194            }
195            Self::Difference { left, .. } => {
196                let (_, upper) = left.size_hint_inner();
197                (0, upper)
198            }
199            Self::Union { left, right } => {
200                let (_, left_upper) = left.size_hint_inner();
201                let right_upper = match right {
202                    UnionRightState::Pending(_) => None,
203                    UnionRightState::Active(right_iter) => right_iter.size_hint_inner().1,
204                };
205                let upper = match (left_upper, right_upper) {
206                    (Some(a), Some(b)) => Some(a.saturating_add(b)),
207                    _ => None,
208                };
209                (0, upper)
210            }
211        }
212    }
213}
214
215/// An iterator over the IDs in an entity set, producing `EntityId<E>`s until exhausted.
216pub struct EntitySetIterator<'c, E: Entity> {
217    inner: EntitySetIteratorInner<'c, E>,
218}
219
220impl<'c, E: Entity> EntitySetIterator<'c, E> {
221    pub(crate) fn empty() -> EntitySetIterator<'c, E> {
222        EntitySetIterator {
223            inner: EntitySetIteratorInner::empty_source(),
224        }
225    }
226
227    pub(crate) fn from_population_iterator(iter: PopulationIterator<E>) -> Self {
228        EntitySetIterator {
229            inner: EntitySetIteratorInner::Source(SourceIterator::PopulationRange(
230                PopulationRangeIterator::from_population_iterator(iter),
231            )),
232        }
233    }
234
235    pub(crate) fn from_sources(mut sources: Vec<SourceSet<'c, E>>) -> Self {
236        if sources.is_empty() {
237            return Self::empty();
238        }
239        if sources.len() == 1 {
240            return EntitySetIterator {
241                inner: EntitySetIteratorInner::Source(sources.pop().unwrap().into_iter()),
242            };
243        }
244
245        // This path constructs intersections from raw source vectors, so we sort here.
246        // We keep ascending order so filters checked by `all()` are smallest-first.
247        sources.sort_unstable_by_key(SourceSet::sort_key);
248        let mut source_iter = sources.into_iter();
249        let driver = source_iter.next().unwrap().into_iter();
250        EntitySetIterator {
251            inner: EntitySetIteratorInner::IntersectionSources {
252                driver,
253                filters: source_iter.collect(),
254            },
255        }
256    }
257
258    pub(crate) fn from_index_set(set: &'c IndexSet<EntityId<E>>) -> EntitySetIterator<'c, E> {
259        EntitySetIterator {
260            inner: EntitySetIteratorInner::Source(SourceSet::IndexSet(set).into_iter()),
261        }
262    }
263
264    pub(super) fn new(set: EntitySet<'c, E>) -> Self {
265        EntitySetIterator {
266            inner: EntitySetIteratorInner::from_entity_set(set),
267        }
268    }
269}
270
271impl<'a, E: Entity> Iterator for EntitySetIterator<'a, E> {
272    type Item = EntityId<E>;
273
274    #[inline]
275    fn next(&mut self) -> Option<Self::Item> {
276        self.inner.next_inner()
277    }
278
279    #[inline]
280    fn size_hint(&self) -> (usize, Option<usize>) {
281        self.inner.size_hint_inner()
282    }
283
284    fn count(self) -> usize {
285        let EntitySetIterator { inner } = self;
286        match inner {
287            EntitySetIteratorInner::Source(source) => source.count(),
288            EntitySetIteratorInner::IntersectionSources {
289                mut driver,
290                filters,
291            } => driver
292                .by_ref()
293                .filter(|&entity_id| filters.iter().all(|filter| filter.contains(entity_id)))
294                .count(),
295            other => {
296                let mut it = EntitySetIterator { inner: other };
297                let mut n = 0usize;
298                while it.next().is_some() {
299                    n += 1;
300                }
301                n
302            }
303        }
304    }
305
306    #[inline]
307    fn nth(&mut self, n: usize) -> Option<Self::Item> {
308        match &mut self.inner {
309            EntitySetIteratorInner::Source(source) => source.nth(n),
310            EntitySetIteratorInner::IntersectionSources { driver, filters } => driver
311                .by_ref()
312                .filter(|&entity_id| filters.iter().all(|filter| filter.contains(entity_id)))
313                .nth(n),
314            _ => {
315                for _ in 0..n {
316                    self.next()?;
317                }
318                self.next()
319            }
320        }
321    }
322
323    fn for_each<F>(self, mut f: F)
324    where
325        F: FnMut(Self::Item),
326    {
327        let EntitySetIterator { inner } = self;
328        match inner {
329            EntitySetIteratorInner::Source(source) => source.for_each(f),
330            other => {
331                let it = EntitySetIterator { inner: other };
332                for item in it {
333                    f(item);
334                }
335            }
336        }
337    }
338
339    fn fold<B, F>(self, init: B, mut f: F) -> B
340    where
341        F: FnMut(B, Self::Item) -> B,
342    {
343        let EntitySetIterator { inner } = self;
344        match inner {
345            EntitySetIteratorInner::Source(source) => source.fold(init, f),
346            other => {
347                let it = EntitySetIterator { inner: other };
348                let mut acc = init;
349                for item in it {
350                    acc = f(acc, item);
351                }
352                acc
353            }
354        }
355    }
356}
357
358impl<'c, E: Entity> std::iter::FusedIterator for EntitySetIterator<'c, E> {}
359
360#[cfg(test)]
361mod tests {
362    /*!
363    ## Test Matrix
364
365    | Test # | PropertyInitializationKind | is_default_value | Indexed | Initial Source Position |
366    | ------ | -------------------------- | ---------------- | ------- | ----------------------- |
367    | 1      | Explicit                   | N/A              | No      | Yes                     |
368    | 2      | Explicit                   | N/A              | Yes     | Yes                     |
369    | 3      | Constant                   | true             | No      | Yes                     |
370    | 4      | Constant                   | true             | Yes     | Yes                     |
371    | 5      | Constant                   | false            | No      | Yes                     |
372    | 6      | Constant                   | false            | Yes     | Yes                     |
373    | 7      | Derived                    | N/A              | No      | Yes                     |
374    | 8      | Derived                    | N/A              | Yes     | Yes                     |
375    | 9      | Explicit                   | N/A              | No      | No                      |
376    | 10     | Explicit                   | N/A              | Yes     | No                      |
377    | 11     | Constant                   | true             | No      | No                      |
378    | 12     | Constant                   | false            | Yes     | No                      |
379    | 13     | Derived                    | N/A              | No      | No                      |
380    | 14     | Derived                    | N/A              | Yes     | No                      |
381
382    The tests use multi-property queries (tests 9-14) where one property
383    is indexed with fewer results to ensure it becomes the "smallest source
384    set", making the tested property NOT the initial source position.
385    */
386
387    use indexmap::IndexSet;
388
389    use crate::entity::entity_set::{EntitySet, SourceSet};
390    use crate::hashing::IndexSet as FxIndexSet;
391    use crate::prelude::*;
392    use crate::{define_derived_property, define_property, with};
393
394    define_entity!(Person);
395
396    // Test properties covering different initialization kinds
397
398    // Explicit (Normal) property - no default, requires explicit initialization
399    define_property!(struct ExplicitProp(u8), Person);
400
401    // Constant property - has a constant default value
402    define_property!(struct ConstantProp(u8), Person, default_const = ConstantProp(42));
403
404    // Derived property - computed from other properties
405    define_derived_property!(struct DerivedProp(bool), Person, [ExplicitProp], |explicit| {
406        DerivedProp(explicit.0 % 2 == 0)
407    });
408
409    // Additional properties for multi-property queries
410    define_property!(struct ConstantProp2(u16), Person, default_const = ConstantProp2(100));
411    define_property!(struct ExplicitProp2(bool), Person);
412
413    define_property!(struct Age(u8), Person, default_const = Age(0));
414    define_property!(struct Alive(bool), Person, default_const = Alive(true));
415
416    define_derived_property!(
417        enum AgeGroupRisk {
418            NewBorn,
419            General,
420            OldAdult,
421        },
422        Person,
423        [Age],
424        [],
425        |age| {
426            if age.0 <= 1 {
427                AgeGroupRisk::NewBorn
428            } else if age.0 <= 65 {
429                AgeGroupRisk::General
430            } else {
431                AgeGroupRisk::OldAdult
432            }
433        }
434    );
435
436    // Helper function to create a population for testing
437    fn setup_test_population(context: &mut Context, size: usize) -> Vec<EntityId<Person>> {
438        let mut people = Vec::new();
439        for i in 0..size {
440            let person = context
441                .add_entity(with!(
442                    Person,
443                    ExplicitProp((i % 20) as u8),
444                    ExplicitProp2(i % 2 == 0)
445                ))
446                .unwrap();
447            people.push(person);
448        }
449        people
450    }
451
452    // region: Test Matrix Tests
453
454    // Test 1: Explicit property, non-default value, not indexed, initial source position = Yes
455    #[test]
456    fn test_explicit_non_default_not_indexed_initial_source_yes() {
457        let mut context = Context::new();
458        setup_test_population(&mut context, 100);
459
460        let results = context
461            .query_result_iterator(with!(Person, ExplicitProp(5)))
462            .collect::<Vec<_>>();
463
464        assert_eq!(results.len(), 5); // 5, 25, 45, 65, 85
465        for person in results {
466            assert_eq!(
467                context.get_property::<_, ExplicitProp>(person),
468                ExplicitProp(5)
469            );
470        }
471    }
472
473    // Test 2: Explicit property, non-default value, indexed, initial source position = Yes
474    #[test]
475    fn test_explicit_non_default_indexed_initial_source_yes() {
476        let mut context = Context::new();
477        context.index_property::<Person, ExplicitProp>();
478        setup_test_population(&mut context, 100);
479
480        let results = context
481            .query_result_iterator(with!(Person, ExplicitProp(7)))
482            .collect::<Vec<_>>();
483
484        assert_eq!(results.len(), 5); // 7, 27, 47, 67, 87
485        for person in results {
486            assert_eq!(
487                context.get_property::<_, ExplicitProp>(person),
488                ExplicitProp(7)
489            );
490        }
491    }
492
493    // Test 3: Constant property, default value, not indexed, initial source position = Yes
494    #[test]
495    fn test_constant_default_not_indexed_initial_source_yes() {
496        let mut context = Context::new();
497        // Create people without setting ConstantProp - they'll use default value 42
498        for _ in 0..50 {
499            context
500                .add_entity(with!(Person, ExplicitProp(1), ExplicitProp2(false)))
501                .unwrap();
502        }
503
504        let results = context
505            .query_result_iterator(with!(Person, ConstantProp(42), ExplicitProp2(false)))
506            .collect::<Vec<_>>();
507
508        assert_eq!(results.len(), 50);
509        for person in results {
510            assert_eq!(
511                context.get_property::<_, ConstantProp>(person),
512                ConstantProp(42)
513            );
514        }
515    }
516
517    // Test 4: Constant property, default value, indexed, initial source position = Yes
518    #[test]
519    fn test_constant_default_indexed_initial_source_yes() {
520        let mut context = Context::new();
521        context.index_property::<Person, ConstantProp>();
522
523        for _ in 0..50 {
524            context
525                .add_entity(with!(Person, ExplicitProp(1), ExplicitProp2(false)))
526                .unwrap();
527        }
528
529        let results = context
530            .query_result_iterator(with!(Person, ConstantProp(42)))
531            .collect::<Vec<_>>();
532        assert_eq!(results.len(), 50);
533    }
534
535    // Test 5: Constant property, non-default value, not indexed, initial source position = Yes
536    #[test]
537    fn test_constant_non_default_not_indexed_initial_source_yes() {
538        let mut context = Context::new();
539
540        for i in 0..50 {
541            if i < 10 {
542                context
543                    .add_entity(with!(
544                        Person,
545                        ExplicitProp(1),
546                        ExplicitProp2(false),
547                        ConstantProp(99)
548                    ))
549                    .unwrap();
550            } else {
551                context
552                    .add_entity(with!(Person, ExplicitProp(1), ExplicitProp2(false)))
553                    .unwrap();
554            }
555        }
556
557        let results = context
558            .query_result_iterator(with!(Person, ConstantProp(99)))
559            .collect::<Vec<_>>();
560
561        assert_eq!(results.len(), 10);
562        for person in results {
563            assert_eq!(
564                context.get_property::<_, ConstantProp>(person),
565                ConstantProp(99)
566            );
567        }
568    }
569
570    // Test 6: Constant property, non-default value, indexed, initial source position = Yes
571    #[test]
572    fn test_constant_non_default_indexed_initial_source_yes() {
573        let mut context = Context::new();
574        context.index_property::<Person, ConstantProp>();
575
576        for i in 0..50 {
577            if i < 10 {
578                context
579                    .add_entity(with!(
580                        Person,
581                        ExplicitProp(1),
582                        ExplicitProp2(false),
583                        ConstantProp(99)
584                    ))
585                    .unwrap();
586            } else {
587                context
588                    .add_entity(with!(Person, ExplicitProp(1), ExplicitProp2(false)))
589                    .unwrap();
590            }
591        }
592
593        let results = context
594            .query_result_iterator(with!(Person, ConstantProp(99)))
595            .collect::<Vec<_>>();
596
597        assert_eq!(results.len(), 10);
598    }
599
600    // Test 7: Derived property, not indexed, initial source position = Yes
601    #[test]
602    fn test_derived_not_indexed_initial_source_yes() {
603        let mut context = Context::new();
604
605        for i in 0..100 {
606            context
607                .add_entity(with!(Person, ExplicitProp(i as u8), ExplicitProp2(false)))
608                .unwrap();
609        }
610
611        let results = context
612            .query_result_iterator(with!(Person, DerivedProp(true)))
613            .collect::<Vec<_>>();
614
615        // DerivedProp is true when ExplicitProp is even
616        assert_eq!(results.len(), 50);
617        for person in results {
618            assert_eq!(
619                context.get_property::<Person, DerivedProp>(person),
620                DerivedProp(true)
621            );
622        }
623    }
624
625    // Test 8: Derived property, indexed, initial source position = Yes
626    #[test]
627    fn test_derived_indexed_initial_source_yes() {
628        let mut context = Context::new();
629        context.index_property::<Person, DerivedProp>();
630
631        for i in 0..100 {
632            context
633                .add_entity(with!(Person, ExplicitProp(i as u8), ExplicitProp2(false)))
634                .unwrap();
635        }
636
637        let results = context
638            .query_result_iterator(with!(Person, DerivedProp(false)))
639            .collect::<Vec<_>>();
640
641        // DerivedProp is false when ExplicitProp is odd
642        assert_eq!(results.len(), 50);
643        for person in results {
644            assert_eq!(
645                context.get_property::<Person, DerivedProp>(person),
646                DerivedProp(false)
647            );
648        }
649    }
650
651    // Test 9-14: Initial source position = No (multi-property queries where property is NOT the smallest source)
652
653    // Test 9: Explicit property, non-default, not indexed, initial source = No
654    #[test]
655    fn test_explicit_non_default_not_indexed_initial_source_no() {
656        let mut context = Context::new();
657        context.index_property::<Person, ExplicitProp2>(); // Index the other property so it's the smallest
658
659        for i in 0..100 {
660            context
661                .add_entity(with!(
662                    Person,
663                    ExplicitProp((i % 20) as u8),
664                    ExplicitProp2(i % 2 == 0)
665                ))
666                .unwrap();
667        }
668
669        // ExplicitProp2 has only 2 values, so it will be the smaller source
670        let results = context
671            .query_result_iterator(with!(Person, ExplicitProp(5), ExplicitProp2(false)))
672            .collect::<Vec<_>>();
673
674        // Looking for ExplicitProp=5 AND ExplicitProp2=true
675        // ExplicitProp cycles 0-19, ExplicitProp2 alternates
676        // Matches: 4 (since 5,25,45,65,85 but only even indices)
677        let expected = results.len();
678        assert!(expected > 0);
679        for person in results {
680            assert_eq!(
681                context.get_property::<Person, ExplicitProp>(person),
682                ExplicitProp(5)
683            );
684            assert_eq!(
685                context.get_property::<Person, ExplicitProp2>(person),
686                ExplicitProp2(false)
687            );
688        }
689    }
690
691    // Test 10: Explicit property, non-default, indexed, initial source = No
692    #[test]
693    fn test_explicit_non_default_indexed_initial_source_no() {
694        let mut context = Context::new();
695        context.index_property::<Person, ExplicitProp>();
696        context.index_property::<Person, ConstantProp2>(); // ConstantProp2 will likely be the smaller source
697
698        for i in 0..100 {
699            if i < 10 {
700                context
701                    .add_entity(with!(
702                        Person,
703                        ExplicitProp(7),
704                        ExplicitProp2(false),
705                        ConstantProp2(200), // Non-default for smaller source
706                    ))
707                    .unwrap();
708            } else {
709                context
710                    .add_entity(with!(
711                        Person,
712                        ExplicitProp((i % 20) as u8),
713                        ExplicitProp2(false)
714                    ))
715                    .unwrap();
716            }
717        }
718
719        let results = context
720            .query_result_iterator(with!(Person, ExplicitProp(7), ConstantProp2(200)))
721            .collect::<Vec<_>>();
722
723        assert_eq!(results.len(), 10);
724    }
725
726    // Test 11: Constant property, default value, not indexed, initial source = No
727    #[test]
728    fn test_constant_default_not_indexed_initial_source_no() {
729        let mut context = Context::new();
730        context.index_property::<Person, ExplicitProp>(); // Make ExplicitProp the smaller source
731
732        for i in 0..100 {
733            if i < 5 {
734                context
735                    .add_entity(with!(Person, ExplicitProp(99), ExplicitProp2(false)))
736                    .unwrap(); // ConstantProp uses default
737            } else {
738                context
739                    .add_entity(with!(
740                        Person,
741                        ExplicitProp((i % 20) as u8),
742                        ExplicitProp2(false)
743                    ))
744                    .unwrap();
745            }
746        }
747
748        let results = context
749            .query_result_iterator(with!(Person, ExplicitProp(99), ConstantProp(42)))
750            .collect::<Vec<_>>();
751
752        assert_eq!(results.len(), 5);
753        for person in results {
754            assert_eq!(
755                context.get_property::<Person, ConstantProp>(person),
756                ConstantProp(42)
757            );
758        }
759    }
760
761    // Test 12: Constant property, non-default, indexed, initial source = No
762    #[test]
763    fn test_constant_non_default_indexed_initial_source_no() {
764        let mut context = Context::new();
765        context.index_property::<Person, ConstantProp>();
766        context.index_property::<Person, ExplicitProp2>();
767
768        for i in 0..100 {
769            if i < 10 {
770                context
771                    .add_entity(with!(
772                        Person,
773                        ConstantProp(99),
774                        ExplicitProp(0),
775                        ExplicitProp2(true)
776                    ))
777                    .unwrap();
778            } else {
779                context
780                    .add_entity(with!(Person, ExplicitProp(0), ExplicitProp2(false)))
781                    .unwrap();
782            }
783        }
784
785        let results = context
786            .query_result_iterator(with!(Person, ConstantProp(99), ExplicitProp2(true)))
787            .collect::<Vec<_>>();
788
789        assert_eq!(results.len(), 10);
790    }
791
792    // Test 13: Derived property, not indexed, initial source = No
793    #[test]
794    fn test_derived_not_indexed_initial_source_no() {
795        let mut context = Context::new();
796        context.index_property::<Person, ExplicitProp2>();
797
798        for i in 0..100 {
799            context
800                .add_entity(with!(Person, ExplicitProp(i as u8), ExplicitProp2(i < 50)))
801                .unwrap();
802        }
803
804        let results = context
805            .query_result_iterator(with!(Person, ExplicitProp2(true), DerivedProp(true)))
806            .collect::<Vec<_>>();
807
808        // ExplicitProp2=true for i<50, DerivedProp=true when ExplicitProp is even
809        // So we want i<50 AND i is even: 0,2,4,...,48 = 25 people
810        assert_eq!(results.len(), 25);
811    }
812
813    // Test 14: Derived property, indexed, initial source = No
814    #[test]
815    fn test_derived_indexed_initial_source_no() {
816        let mut context = Context::new();
817        context.index_property::<Person, DerivedProp>();
818        context.index_property::<Person, ExplicitProp2>();
819
820        for i in 0..100 {
821            context
822                .add_entity(with!(Person, ExplicitProp(i as u8), ExplicitProp2(i < 30)))
823                .unwrap();
824        }
825
826        let results = context
827            .query_result_iterator(with!(Person, ExplicitProp2(true), DerivedProp(false)))
828            .collect::<Vec<_>>();
829
830        // ExplicitProp2=true for i<30, DerivedProp=false when ExplicitProp is odd
831        // So we want i < 30 AND i is odd: 1,3,5,...,29 = 15 people
832        assert_eq!(results.len(), 15);
833    }
834
835    // endregion Test Matrix Tests
836
837    #[test]
838    fn test_multiple_query_result_iterators() {
839        let mut context = Context::new();
840        context.index_property::<Person, Age>();
841
842        for age in 0..100 {
843            context
844                .add_entity(with!(
845                    Person,
846                    Age(age),
847                    ExplicitProp(age.wrapping_mul(7) % 100),
848                    ExplicitProp2(false),
849                ))
850                .unwrap();
851        }
852        for age in 0..100 {
853            context
854                .add_entity(with!(
855                    Person,
856                    Age(age),
857                    ExplicitProp(age.wrapping_mul(14) % 100),
858                    ExplicitProp2(false),
859                ))
860                .unwrap();
861        }
862
863        // Since both queries include `Age`, both will attempt to index unindexed entities. This tests that there is
864        // no double borrow error.
865        let results = context.query_result_iterator(with!(Person, Age(25)));
866        let more_results = context.query_result_iterator(with!(Person, Age(25), ExplicitProp(75)));
867
868        let collected_results = results.collect::<IndexSet<_>>();
869        let other_collected_results = more_results.collect::<IndexSet<_>>();
870        let intersection_count = collected_results
871            .intersection(&other_collected_results)
872            .count();
873        assert_eq!(intersection_count, 1);
874    }
875
876    #[test]
877    fn test_expression_intersection_iteration() {
878        let set = EntitySet::from_source(SourceSet::<Person>::PopulationRange(0..10)).intersection(
879            EntitySet::from_source(SourceSet::<Person>::PopulationRange(0..6)),
880        );
881
882        let ids = set.into_iter().collect::<Vec<_>>();
883        let expected = (0..6).map(EntityId::new).collect::<Vec<_>>();
884        assert_eq!(ids, expected);
885    }
886
887    #[test]
888    fn test_expression_difference_iteration() {
889        let set = EntitySet::from_source(SourceSet::<Person>::PopulationRange(0..5)).difference(
890            EntitySet::from_source(SourceSet::<Person>::PopulationRange(2..3)),
891        );
892
893        let ids = set.into_iter().collect::<Vec<_>>();
894        let expected = vec![
895            EntityId::new(0),
896            EntityId::new(1),
897            EntityId::new(3),
898            EntityId::new(4),
899        ];
900        assert_eq!(ids, expected);
901    }
902
903    #[test]
904    fn test_expression_union_deduplicates() {
905        let left = EntitySet::from_source(SourceSet::<Person>::PopulationRange(0..3)).difference(
906            EntitySet::from_source(SourceSet::<Person>::PopulationRange(99..100)),
907        );
908        let right = EntitySet::from_source(SourceSet::<Person>::PopulationRange(0..5)).difference(
909            EntitySet::from_source(SourceSet::<Person>::PopulationRange(99..100)),
910        );
911        let set = left.union(right);
912
913        let ids = set.into_iter().collect::<Vec<_>>();
914        let expected = (0..5).map(EntityId::new).collect::<Vec<_>>();
915        assert_eq!(ids, expected);
916    }
917
918    #[test]
919    fn test_expression_union_overlap_no_duplicates() {
920        let left = EntitySet::from_source(SourceSet::<Person>::PopulationRange(0..5)).difference(
921            EntitySet::from_source(SourceSet::<Person>::PopulationRange(4..5)),
922        );
923        let right = EntitySet::from_source(SourceSet::<Person>::PopulationRange(0..7)).difference(
924            EntitySet::from_source(SourceSet::<Person>::PopulationRange(0..1)),
925        );
926
927        let ids = left.union(right).into_iter().collect::<IndexSet<_>>();
928        let expected = (0..7).map(EntityId::new).collect::<IndexSet<_>>();
929        assert_eq!(ids, expected);
930    }
931
932    #[test]
933    fn test_expression_intersection_of_unions() {
934        let ab = EntitySet::from_source(SourceSet::<Person>::PopulationRange(1..2))
935            .union(EntitySet::from_source(
936                SourceSet::<Person>::PopulationRange(2..3),
937            ))
938            .union(EntitySet::from_source(
939                SourceSet::<Person>::PopulationRange(3..4),
940            ));
941        let cd = EntitySet::from_source(SourceSet::<Person>::PopulationRange(2..3))
942            .union(EntitySet::from_source(
943                SourceSet::<Person>::PopulationRange(3..4),
944            ))
945            .union(EntitySet::from_source(
946                SourceSet::<Person>::PopulationRange(4..5),
947            ));
948
949        let ids = ab.intersection(cd).into_iter().collect::<Vec<_>>();
950        assert_eq!(ids, vec![EntityId::new(2), EntityId::new(3)]);
951    }
952
953    #[test]
954    fn test_expression_difference_not_commutative() {
955        let left = EntitySet::from_source(SourceSet::<Person>::PopulationRange(1..2))
956            .union(EntitySet::from_source(
957                SourceSet::<Person>::PopulationRange(2..3),
958            ))
959            .union(EntitySet::from_source(
960                SourceSet::<Person>::PopulationRange(3..4),
961            ));
962        let right = EntitySet::from_source(SourceSet::<Person>::PopulationRange(2..3))
963            .union(EntitySet::from_source(
964                SourceSet::<Person>::PopulationRange(3..4),
965            ))
966            .union(EntitySet::from_source(
967                SourceSet::<Person>::PopulationRange(4..5),
968            ));
969
970        let left_minus_right = left.difference(right).into_iter().collect::<Vec<_>>();
971        let right_minus_left = EntitySet::from_source(SourceSet::<Person>::PopulationRange(2..3))
972            .union(EntitySet::from_source(
973                SourceSet::<Person>::PopulationRange(3..4),
974            ))
975            .union(EntitySet::from_source(
976                SourceSet::<Person>::PopulationRange(4..5),
977            ))
978            .difference(
979                EntitySet::from_source(SourceSet::<Person>::PopulationRange(1..2))
980                    .union(EntitySet::from_source(
981                        SourceSet::<Person>::PopulationRange(2..3),
982                    ))
983                    .union(EntitySet::from_source(
984                        SourceSet::<Person>::PopulationRange(3..4),
985                    )),
986            )
987            .into_iter()
988            .collect::<Vec<_>>();
989
990        assert_eq!(left_minus_right, vec![EntityId::new(1)]);
991        assert_eq!(right_minus_left, vec![EntityId::new(4)]);
992    }
993
994    #[test]
995    fn test_union_size_hint_pending_right_is_unknown() {
996        let left = EntitySet::from_source(SourceSet::<Person>::PopulationRange(0..2)).difference(
997            EntitySet::from_source(SourceSet::<Person>::PopulationRange(99..100)),
998        );
999        let right = EntitySet::from_source(SourceSet::<Person>::PopulationRange(0..4)).difference(
1000            EntitySet::from_source(SourceSet::<Person>::PopulationRange(98..99)),
1001        );
1002        let iter = left.union(right).into_iter();
1003
1004        assert_eq!(iter.size_hint(), (4, Some(4)));
1005    }
1006
1007    #[test]
1008    fn test_nth_and_count_on_source() {
1009        let mut iter =
1010            EntitySet::from_source(SourceSet::<Person>::PopulationRange(0..5)).into_iter();
1011        assert_eq!(iter.nth(2), Some(EntityId::new(2)));
1012
1013        let remaining = iter.count();
1014        assert_eq!(remaining, 2);
1015    }
1016
1017    #[test]
1018    fn population_range_source_iteration_and_size_hint() {
1019        let mut iter =
1020            EntitySet::from_source(SourceSet::<Person>::PopulationRange(3..7)).into_iter();
1021        assert_eq!(iter.size_hint(), (4, Some(4)));
1022        assert_eq!(iter.next(), Some(EntityId::new(3)));
1023        assert_eq!(iter.size_hint(), (3, Some(3)));
1024        assert_eq!(
1025            iter.collect::<Vec<_>>(),
1026            vec![EntityId::new(4), EntityId::new(5), EntityId::new(6)]
1027        );
1028    }
1029
1030    #[test]
1031    fn optimized_range_results_iterate_as_single_source() {
1032        let union = EntitySet::from_source(SourceSet::<Person>::PopulationRange(0..3)).union(
1033            EntitySet::from_source(SourceSet::<Person>::PopulationRange(3..5)),
1034        );
1035        assert_eq!(
1036            union.into_iter().collect::<Vec<_>>(),
1037            vec![
1038                EntityId::new(0),
1039                EntityId::new(1),
1040                EntityId::new(2),
1041                EntityId::new(3),
1042                EntityId::new(4),
1043            ]
1044        );
1045
1046        let intersection = EntitySet::from_source(SourceSet::<Person>::PopulationRange(2..8))
1047            .intersection(EntitySet::from_source(
1048                SourceSet::<Person>::PopulationRange(4..6),
1049            ));
1050        assert_eq!(
1051            intersection.into_iter().collect::<Vec<_>>(),
1052            vec![EntityId::new(4), EntityId::new(5)]
1053        );
1054    }
1055
1056    #[test]
1057    fn split_difference_still_uses_generic_iteration_path() {
1058        let split = EntitySet::from_source(SourceSet::<Person>::PopulationRange(2..8)).difference(
1059            EntitySet::from_source(SourceSet::<Person>::PopulationRange(4..6)),
1060        );
1061        assert_eq!(
1062            split.into_iter().collect::<Vec<_>>(),
1063            vec![
1064                EntityId::new(2),
1065                EntityId::new(3),
1066                EntityId::new(6),
1067                EntityId::new(7)
1068            ]
1069        );
1070    }
1071
1072    fn finite_set(ids: &[usize]) -> FxIndexSet<EntityId<Person>> {
1073        ids.iter()
1074            .copied()
1075            .map(EntityId::new)
1076            .collect::<FxIndexSet<_>>()
1077    }
1078
1079    fn as_entity_set(set: &FxIndexSet<EntityId<Person>>) -> EntitySet<Person> {
1080        EntitySet::from_source(SourceSet::IndexSet(set))
1081    }
1082
1083    #[test]
1084    fn prototype_empty_entity_set_yields_nothing() {
1085        let ids = EntitySet::<Person>::empty().into_iter().collect::<Vec<_>>();
1086        assert!(ids.is_empty());
1087    }
1088
1089    #[test]
1090    fn prototype_iter_union_disjoint() {
1091        let a = finite_set(&[1, 2]);
1092        let b = finite_set(&[3, 4]);
1093        let ids = as_entity_set(&a)
1094            .union(as_entity_set(&b))
1095            .into_iter()
1096            .collect::<IndexSet<_>>();
1097        let expected = [1usize, 2, 3, 4]
1098            .into_iter()
1099            .map(EntityId::new)
1100            .collect::<IndexSet<_>>();
1101        assert_eq!(ids, expected);
1102    }
1103
1104    #[test]
1105    fn prototype_iter_union_overlapping() {
1106        let a = finite_set(&[1, 2, 3]);
1107        let b = finite_set(&[2, 3, 4]);
1108        let ids = as_entity_set(&a)
1109            .union(as_entity_set(&b))
1110            .into_iter()
1111            .collect::<Vec<_>>();
1112        let unique = ids.iter().copied().collect::<IndexSet<_>>();
1113        assert_eq!(ids.len(), unique.len());
1114        assert!(unique.contains(&EntityId::new(1)));
1115        assert!(unique.contains(&EntityId::new(4)));
1116    }
1117
1118    #[test]
1119    fn prototype_iter_intersection_disjoint() {
1120        let a = finite_set(&[1, 2]);
1121        let b = finite_set(&[3, 4]);
1122        let ids = as_entity_set(&a)
1123            .intersection(as_entity_set(&b))
1124            .into_iter()
1125            .collect::<Vec<_>>();
1126        assert!(ids.is_empty());
1127    }
1128
1129    #[test]
1130    fn prototype_iter_difference_basic() {
1131        let a = finite_set(&[1, 2, 3, 4]);
1132        let b = finite_set(&[3, 4, 5, 6]);
1133        let ids = as_entity_set(&a)
1134            .difference(as_entity_set(&b))
1135            .into_iter()
1136            .collect::<IndexSet<_>>();
1137        assert_eq!(ids.len(), 2);
1138        assert!(ids.contains(&EntityId::new(1)));
1139        assert!(ids.contains(&EntityId::new(2)));
1140    }
1141
1142    #[test]
1143    fn prototype_iter_matches_contains_compound() {
1144        let a = finite_set(&[1, 2, 3, 4]);
1145        let b = finite_set(&[3, 4, 5]);
1146        let c = finite_set(&[7, 8, 9, 10]);
1147        let d = finite_set(&[9, 10, 11]);
1148        let left = as_entity_set(&a).intersection(as_entity_set(&b));
1149        let right = as_entity_set(&c).difference(as_entity_set(&d));
1150        let iterated = left.union(right).into_iter().collect::<IndexSet<_>>();
1151
1152        let a2 = finite_set(&[1, 2, 3, 4]);
1153        let b2 = finite_set(&[3, 4, 5]);
1154        let c2 = finite_set(&[7, 8, 9, 10]);
1155        let d2 = finite_set(&[9, 10, 11]);
1156        let check = as_entity_set(&a2)
1157            .intersection(as_entity_set(&b2))
1158            .union(as_entity_set(&c2).difference(as_entity_set(&d2)));
1159
1160        for value in 0..15 {
1161            let entity = EntityId::new(value);
1162            assert_eq!(iterated.contains(&entity), check.contains(entity));
1163        }
1164    }
1165
1166    #[test]
1167    fn prototype_size_hint_single_source_and_partial_consume() {
1168        let mut iter =
1169            EntitySet::from_source(SourceSet::<Person>::PopulationRange(0..5)).into_iter();
1170        assert_eq!(iter.size_hint(), (5, Some(5)));
1171        iter.next();
1172        assert_eq!(iter.size_hint(), (4, Some(4)));
1173    }
1174}