1use crate::people::index::{
2 get_and_register_multi_property_index, get_multi_property_hash, Index, IndexValue,
3};
4use crate::people::query::Query;
5use crate::people::{index, InitializationList, PeoplePlugin, PersonPropertyHolder};
6use crate::{
7 Context, ContextRandomExt, IxaError, PersonCreatedEvent, PersonId, PersonProperty,
8 PersonPropertyChangeEvent, RngId, Tabulator,
9};
10use crate::{HashMap, HashMapExt, HashSet, HashSetExt};
11use rand::Rng;
12use std::any::TypeId;
13use std::cell::Ref;
14
15use crate::people::methods::Methods;
16
17pub trait ContextPeopleExt {
20 fn get_current_population(&self) -> usize;
22
23 fn add_person<T: InitializationList>(&mut self, props: T) -> Result<PersonId, IxaError>;
33
34 fn get_person_property<T: PersonProperty>(&self, person_id: PersonId, _property: T)
40 -> T::Value;
41
42 #[doc(hidden)]
43 fn register_property<T: PersonProperty>(&self);
44
45 fn set_person_property<T: PersonProperty>(
48 &mut self,
49 person_id: PersonId,
50 _property: T,
51 value: T::Value,
52 );
53
54 fn index_property<T: PersonProperty>(&mut self, property: T);
62
63 fn query_people<T: Query>(&self, q: T) -> Vec<PersonId>;
71
72 fn query_people_count<T: Query>(&self, q: T) -> usize;
84
85 fn match_person<T: Query>(&self, person_id: PersonId, q: T) -> bool;
89
90 fn filter_people<T: Query>(&self, people: &mut Vec<PersonId>, q: T);
96 fn tabulate_person_properties<T: Tabulator, F>(&self, tabulator: &T, print_fn: F)
97 where
98 F: Fn(&Context, &[String], usize);
99
100 fn sample_person<R: RngId + 'static, T: Query>(&self, rng_id: R, query: T) -> Option<PersonId>
106 where
107 R::RngType: Rng;
108
109 fn sample_people<R: RngId + 'static, T: Query>(
114 &self,
115 rng_id: R,
116 query: T,
117 n: usize,
118 ) -> Vec<PersonId>
119 where
120 R::RngType: Rng;
121}
122impl ContextPeopleExt for Context {
123 fn get_current_population(&self) -> usize {
124 self.get_data_container(PeoplePlugin)
125 .map_or(0, |data_container| data_container.current_population)
126 }
127
128 fn add_person<T: InitializationList>(&mut self, props: T) -> Result<PersonId, IxaError> {
129 let data_container = self.get_data_container_mut(PeoplePlugin);
130 data_container.check_initialization_list(&props)?;
133
134 let person_id = data_container.add_person();
137
138 data_container.is_initializing = true;
141 props.set_properties(self, person_id);
142 let data_container = self.get_data_container_mut(PeoplePlugin);
143 data_container.is_initializing = false;
144
145 self.emit_event(PersonCreatedEvent { person_id });
146 Ok(person_id)
147 }
148
149 fn get_person_property<T: PersonProperty>(&self, person_id: PersonId, property: T) -> T::Value {
150 let data_container = self.get_data_container(PeoplePlugin)
151 .expect("PeoplePlugin is not initialized; make sure you add a person before accessing properties");
152 self.register_property::<T>();
153
154 if T::is_derived() {
155 return T::compute(self, person_id);
156 }
157
158 if let Some(value) = *data_container.get_person_property_ref(person_id, property) {
160 return value;
161 }
162
163 let initialized_value = T::compute(self, person_id);
165 data_container.set_person_property(person_id, property, initialized_value);
166
167 initialized_value
168 }
169
170 #[allow(clippy::single_match_else)]
171 fn set_person_property<T: PersonProperty>(
172 &mut self,
173 person_id: PersonId,
174 property: T,
175 value: T::Value,
176 ) {
177 self.register_property::<T>();
178
179 assert!(!T::is_derived(), "Cannot set a derived property");
180
181 let initializing = self
198 .get_data_container(PeoplePlugin)
199 .unwrap()
200 .is_initializing;
201
202 let (previous_value, deps_temp) = if initializing {
203 (None, None)
204 } else {
205 let previous_value = self.get_person_property(person_id, property);
206 if previous_value != value {
207 self.remove_from_index_maybe(person_id, property);
208 }
209
210 (
211 Some(previous_value),
212 self.get_data_container(PeoplePlugin)
213 .unwrap()
214 .dependency_map
215 .borrow_mut()
216 .get_mut(&TypeId::of::<T>())
217 .map(std::mem::take),
218 )
219 };
220
221 let mut dependency_event_callbacks = Vec::new();
222 if let Some(mut deps) = deps_temp {
223 for dep in &mut deps {
226 dep.dependency_changed(self, person_id, &mut dependency_event_callbacks);
227 }
228
229 let data_container = self.get_data_container(PeoplePlugin).unwrap();
231 let mut dependencies = data_container.dependency_map.borrow_mut();
232 dependencies.insert(TypeId::of::<T>(), deps);
233 }
234
235 let data_container = self.get_data_container(PeoplePlugin).unwrap();
237 data_container.set_person_property(person_id, property, value);
238
239 if !initializing {
240 if previous_value.unwrap() != value {
241 self.add_to_index_maybe(person_id, property);
242 }
243
244 let change_event: PersonPropertyChangeEvent<T> = PersonPropertyChangeEvent {
245 person_id,
246 current: value,
247 previous: previous_value.unwrap(), };
249 self.emit_event(change_event);
250 }
251
252 for callback in dependency_event_callbacks {
253 callback(self);
254 }
255 }
256
257 fn index_property<T: PersonProperty>(&mut self, _property: T) {
258 {
260 let _ = self.get_data_container_mut(PeoplePlugin);
261 }
262
263 self.register_property::<T>();
264
265 let data_container = self.get_data_container(PeoplePlugin).unwrap();
266 let mut index = data_container
267 .get_index_ref_mut_by_prop(T::get_instance())
268 .unwrap();
269 if index.lookup.is_none() {
270 index.lookup = Some(HashMap::new());
271 }
272 }
273
274 fn query_people<T: Query>(&self, q: T) -> Vec<PersonId> {
275 if self.get_data_container(PeoplePlugin).is_none() {
277 return Vec::new();
278 }
279
280 T::setup(&q, self);
281 let mut result = Vec::new();
282 self.query_people_internal(
283 |person| {
284 result.push(person);
285 },
286 q.get_query(),
287 );
288 result
289 }
290
291 fn query_people_count<T: Query>(&self, q: T) -> usize {
292 if self.get_data_container(PeoplePlugin).is_none() {
294 return 0;
295 }
296
297 T::setup(&q, self);
298 let mut count: usize = 0;
299 self.query_people_internal(
300 |_person| {
301 count += 1;
302 },
303 q.get_query(),
304 );
305 count
306 }
307
308 fn match_person<T: Query>(&self, person_id: PersonId, q: T) -> bool {
309 T::setup(&q, self);
310 let data_container = self.get_data_container(PeoplePlugin).unwrap();
312
313 let query = q.get_query();
314
315 for (t, hash) in &query {
316 let methods = data_container.get_methods(*t);
317 if *hash != (*methods.indexer)(self, person_id) {
318 return false;
319 }
320 }
321 true
322 }
323
324 fn filter_people<T: Query>(&self, people: &mut Vec<PersonId>, q: T) {
325 T::setup(&q, self);
326 let data_container = self.get_data_container(PeoplePlugin).unwrap();
327 for (t, hash) in q.get_query() {
328 let methods = data_container.get_methods(t);
329 people.retain(|person_id| hash == (*methods.indexer)(self, *person_id));
330 if people.is_empty() {
331 break;
332 }
333 }
334 }
335
336 fn register_property<T: PersonProperty>(&self) {
337 let data_container = self.get_data_container(PeoplePlugin).
338 expect("PeoplePlugin is not initialized; make sure you add a person before accessing properties");
339 if data_container
340 .registered_properties
341 .borrow()
342 .contains(&TypeId::of::<T>())
343 {
344 return;
345 }
346
347 let instance = T::get_instance();
348
349 if instance.is_derived() {
351 T::register_dependencies(self);
352
353 let dependencies = instance.non_derived_dependencies();
354 for dependency in dependencies {
355 let mut dependency_map = data_container.dependency_map.borrow_mut();
356 let derived_prop_list = dependency_map.entry(dependency).or_default();
357 derived_prop_list.push(Box::new(instance));
358 }
359 }
360
361 data_container
365 .methods
366 .borrow_mut()
367 .insert(TypeId::of::<T>(), Methods::new::<T>());
368 data_container
369 .people_types
370 .borrow_mut()
371 .insert(T::name().to_string(), TypeId::of::<T>());
372 data_container
373 .registered_properties
374 .borrow_mut()
375 .insert(TypeId::of::<T>());
376
377 self.register_indexer::<T>();
378 }
379
380 fn tabulate_person_properties<T: Tabulator, F>(&self, tabulator: &T, print_fn: F)
381 where
382 F: Fn(&Context, &[String], usize),
383 {
384 let type_ids = tabulator.get_typelist();
385 tabulator.setup(self).unwrap();
386
387 if let Some(data_container) = self.get_data_container(PeoplePlugin) {
388 for type_id in &type_ids {
389 let mut index = data_container.get_index_ref_mut(*type_id).unwrap();
390 if index.lookup.is_none() {
391 index.lookup = Some(HashMap::new());
394 }
395
396 let methods = data_container.get_methods(*type_id);
397 index.index_unindexed_people(self, &methods);
398 }
399 } else {
400 return;
402 }
403
404 let index_container = self
406 .get_data_container(PeoplePlugin)
407 .unwrap()
408 .property_indexes
409 .borrow();
410
411 let indices = type_ids
412 .iter()
413 .filter_map(|t| index_container.get(t))
414 .collect::<Vec<&Index>>();
415
416 index::process_indices(
417 self,
418 indices.as_slice(),
419 &mut Vec::new(),
420 &HashSet::new(),
421 &print_fn,
422 );
423 }
424
425 #[allow(clippy::cast_sign_loss, clippy::cast_possible_truncation)]
426 fn sample_people<R: RngId + 'static, T: Query>(
427 &self,
428 rng_id: R,
429 query: T,
430 n: usize,
431 ) -> Vec<PersonId>
432 where
433 R::RngType: Rng,
434 {
435 if self.get_current_population() == 0 {
436 return Vec::new();
437 }
438 let requested = std::cmp::min(n, self.get_current_population());
439 if query.get_query().is_empty() {
441 let mut selected = HashSet::new();
442 while selected.len() < requested {
443 let result = self.sample_range(rng_id, 0..self.get_current_population());
444 selected.insert(PersonId(result));
445 }
446 return selected.into_iter().collect();
447 }
448
449 T::setup(&query, self);
450
451 let mut w: f64 = self.sample_range(rng_id, 0.0..1.0);
456 let mut ctr: usize = 0;
457 let mut i: usize = 1;
458 let mut selected = Vec::new();
459
460 self.query_people_internal(
461 |person| {
462 ctr += 1;
463 if i == ctr {
464 if selected.len() == requested {
465 let to_remove = self.sample_range(rng_id, 0..selected.len());
466 selected.swap_remove(to_remove);
467 }
468 selected.push(person);
469 if selected.len() == requested {
470 i += (f64::ln(self.sample_range(rng_id, 0.0..1.0)) / f64::ln(1.0 - w))
471 .floor() as usize
472 + 1;
473 w *= self.sample_range(rng_id, 0.0..1.0);
474 } else {
475 i += 1;
476 }
477 }
478 },
479 query.get_query(),
480 );
481
482 selected
483 }
484
485 #[allow(clippy::cast_sign_loss, clippy::cast_possible_truncation)]
486 fn sample_person<R: RngId + 'static, T: Query>(&self, rng_id: R, query: T) -> Option<PersonId>
487 where
488 R::RngType: Rng,
489 {
490 if self.get_current_population() == 0 {
491 return None;
492 }
493
494 if query.get_query().is_empty() {
496 let result = self.sample_range(rng_id, 0..self.get_current_population());
497 return Some(PersonId(result));
498 }
499
500 T::setup(&query, self);
501
502 let mut selected: Option<PersonId> = None;
507 let mut w: f64 = self.sample_range(rng_id, 0.0..1.0);
508 let mut ctr: usize = 0;
509 let mut i: usize = 1;
510
511 self.query_people_internal(
512 |person| {
513 ctr += 1;
514 if i == ctr {
515 selected = Some(person);
516 i += (f64::ln(self.sample_range(rng_id, 0.0..1.0)) / f64::ln(1.0 - w)).floor()
517 as usize
518 + 1;
519 w *= self.sample_range(rng_id, 0.0..1.0);
520 }
521 },
522 query.get_query(),
523 );
524
525 selected
526 }
527}
528
529pub trait ContextPeopleExtInternal {
530 fn register_indexer<T: PersonProperty>(&self);
531 fn add_to_index_maybe<T: PersonProperty>(&mut self, person_id: PersonId, property: T);
532 fn remove_from_index_maybe<T: PersonProperty>(&mut self, person_id: PersonId, property: T);
533 fn query_people_internal(
534 &self,
535 accumulator: impl FnMut(PersonId),
536 property_hashes: Vec<(TypeId, IndexValue)>,
537 );
538}
539
540impl ContextPeopleExtInternal for Context {
541 fn register_indexer<T: PersonProperty>(&self) {
542 {
543 let data_container = self.get_data_container(PeoplePlugin).unwrap();
544
545 let property_indexes = data_container.property_indexes.borrow_mut();
546 if property_indexes.contains_key(&TypeId::of::<T>()) {
547 return; }
549 }
550
551 let index = Index::new(self, T::get_instance());
553 let data_container = self.get_data_container(PeoplePlugin).unwrap();
554 let mut property_indexes = data_container.property_indexes.borrow_mut();
555 property_indexes.insert(TypeId::of::<T>(), index);
556 }
557
558 fn add_to_index_maybe<T: PersonProperty>(&mut self, person_id: PersonId, property: T) {
559 let data_container = self.get_data_container(PeoplePlugin).unwrap();
560
561 if let Some(mut index) = data_container.get_index_ref_mut_by_prop(property) {
562 let methods = data_container.get_methods(TypeId::of::<T>());
563 if index.lookup.is_some() {
564 index.add_person(self, &methods, person_id);
565 }
566 }
567 }
568
569 fn remove_from_index_maybe<T: PersonProperty>(&mut self, person_id: PersonId, property: T) {
570 let data_container = self.get_data_container(PeoplePlugin).unwrap();
571
572 if let Some(mut index) = data_container.get_index_ref_mut_by_prop(property) {
573 let methods = data_container.get_methods(TypeId::of::<T>());
574 if index.lookup.is_some() {
575 index.remove_person(self, &methods, person_id);
576 }
577 }
578 }
579
580 fn query_people_internal(
581 &self,
582 mut accumulator: impl FnMut(PersonId),
583 property_hashes: Vec<(TypeId, IndexValue)>,
584 ) {
585 let mut indexes = Vec::<Ref<HashSet<PersonId>>>::new();
586 let mut unindexed = Vec::<(TypeId, IndexValue)>::new();
587 let data_container = self.get_data_container(PeoplePlugin)
588 .expect("PeoplePlugin is not initialized; make sure you add a person before accessing properties");
589
590 let mut property_hashs_working_set = property_hashes.clone();
591 if property_hashs_working_set.len() > 1 {
593 if let Some(combined_index) =
594 get_and_register_multi_property_index(&property_hashes, self)
595 {
596 let combined_hash = get_multi_property_hash(&property_hashes);
597 property_hashs_working_set = vec![(combined_index, combined_hash)];
598 }
599 }
600
601 for (t, _) in &property_hashs_working_set {
603 let mut index = data_container.get_index_ref_mut(*t).unwrap();
604 let methods = data_container.get_methods(*t);
605 index.index_unindexed_people(self, &methods);
606 }
607
608 for (t, hash) in property_hashs_working_set {
610 let index = data_container.get_index_ref(t).unwrap();
611 if let Ok(lookup) = Ref::filter_map(index, |x| x.lookup.as_ref()) {
612 if let Ok(matching_people) =
613 Ref::filter_map(lookup, |x| x.get(&hash).map(|entry| &entry.1))
614 {
615 indexes.push(matching_people);
616 } else {
617 return;
620 }
621 } else {
622 unindexed.push((t, hash));
624 }
625 }
626
627 let holder: Ref<HashSet<PersonId>>;
632 let to_check: Box<dyn Iterator<Item = PersonId>> = if indexes.is_empty() {
633 Box::new(data_container.people_iterator())
634 } else {
635 indexes.sort_by_key(|x| x.len());
636
637 holder = indexes.remove(0);
638 Box::new(holder.iter().copied())
639 };
640
641 'outer: for person in to_check {
646 for index in &indexes {
648 if !index.contains(&person) {
649 continue 'outer;
650 }
651 }
652
653 for (t, hash) in &unindexed {
655 let methods = data_container.get_methods(*t);
656 if *hash != (*methods.indexer)(self, person) {
657 continue 'outer;
658 }
659 }
660
661 accumulator(person);
663 }
664 }
665}
666
667#[cfg(test)]
668mod tests {
669 use crate::people::{PeoplePlugin, PersonPropertyHolder};
670 use crate::random::{define_rng, ContextRandomExt};
671 use crate::{
672 define_derived_property, define_global_property, define_person_property,
673 define_person_property_with_default, Context, ContextGlobalPropertiesExt, ContextPeopleExt,
674 IxaError, PersonId, PersonPropertyChangeEvent,
675 };
676 use serde_derive::Serialize;
677 use std::any::TypeId;
678 use std::cell::RefCell;
679 use std::rc::Rc;
680
681 define_person_property!(Age, u8);
682 #[derive(Serialize, Copy, Clone, Debug, PartialEq, Eq)]
683 pub enum AgeGroupValue {
684 Child,
685 Adult,
686 }
687 define_global_property!(ThresholdP, u8);
688 define_derived_property!(IsEligible, bool, [Age], [ThresholdP], |age, threshold| {
689 age >= threshold
690 });
691
692 define_derived_property!(
694 NotUsed,
695 bool,
696 [Age],
697 [ThresholdP, ThresholdP],
698 |age, threshold, threshold2| { age >= threshold && age <= threshold2 }
699 );
700
701 define_derived_property!(AgeGroup, AgeGroupValue, [Age], |age| {
702 if age < 18 {
703 AgeGroupValue::Child
704 } else {
705 AgeGroupValue::Adult
706 }
707 });
708
709 #[derive(Serialize, Copy, Clone, PartialEq, Eq, Debug)]
710 pub enum RiskCategoryValue {
711 High,
712 Low,
713 }
714
715 define_person_property!(RiskCategory, RiskCategoryValue);
716 define_person_property_with_default!(IsRunner, bool, false);
717 define_person_property!(RunningShoes, u8, |context: &Context, person: PersonId| {
718 let is_runner = context.get_person_property(person, IsRunner);
719 if is_runner {
720 4
721 } else {
722 0
723 }
724 });
725 define_derived_property!(AdultRunner, bool, [IsRunner, Age], |is_runner, age| {
726 is_runner && age >= 18
727 });
728 define_derived_property!(
729 SeniorRunner,
730 bool,
731 [AdultRunner, Age],
732 |adult_runner, age| { adult_runner && age >= 65 }
733 );
734 define_person_property_with_default!(IsSwimmer, bool, false);
735 define_derived_property!(AdultSwimmer, bool, [IsSwimmer, Age], |is_swimmer, age| {
736 is_swimmer && age >= 18
737 });
738 define_derived_property!(
739 AdultAthlete,
740 bool,
741 [AdultRunner, AdultSwimmer],
742 |adult_runner, adult_swimmer| { adult_runner || adult_swimmer }
743 );
744
745 #[test]
746 fn set_get_properties() {
747 let mut context = Context::new();
748
749 let person = context.add_person((Age, 42)).unwrap();
750 assert_eq!(context.get_person_property(person, Age), 42);
751 }
752
753 #[allow(clippy::should_panic_without_expect)]
754 #[test]
755 #[should_panic]
756 fn get_uninitialized_property_panics() {
757 let mut context = Context::new();
758 let person = context.add_person(()).unwrap();
759 context.get_person_property(person, Age);
760 }
761
762 #[test]
763 fn get_current_population() {
764 let mut context = Context::new();
765 assert_eq!(context.get_current_population(), 0);
766 for _ in 0..3 {
767 context.add_person(()).unwrap();
768 }
769 assert_eq!(context.get_current_population(), 3);
770 }
771
772 #[test]
773 fn add_person() {
774 let mut context = Context::new();
775
776 let person_id = context
777 .add_person(((Age, 42), (RiskCategory, RiskCategoryValue::Low)))
778 .unwrap();
779 assert_eq!(context.get_person_property(person_id, Age), 42);
780 assert_eq!(
781 context.get_person_property(person_id, RiskCategory),
782 RiskCategoryValue::Low
783 );
784 }
785
786 #[test]
787 fn add_person_with_initialize() {
788 let mut context = Context::new();
789
790 let person_id = context
791 .add_person(((Age, 42), (RiskCategory, RiskCategoryValue::Low)))
792 .unwrap();
793 assert_eq!(context.get_person_property(person_id, Age), 42);
794 assert_eq!(
795 context.get_person_property(person_id, RiskCategory),
796 RiskCategoryValue::Low
797 );
798 }
799
800 #[test]
801 fn add_person_with_initialize_missing() {
802 let mut context = Context::new();
803
804 context.add_person((Age, 10)).unwrap();
805 assert!(matches!(context.add_person(()), Err(IxaError::IxaError(_))));
807 }
808
809 #[test]
810 fn add_person_with_initialize_missing_first() {
811 let mut context = Context::new();
812
813 context.add_person(()).unwrap();
816 }
817
818 #[test]
819 fn add_person_with_initialize_missing_with_default() {
820 let mut context = Context::new();
821
822 context.add_person((IsRunner, true)).unwrap();
823 context.add_person(()).unwrap();
825 }
826
827 #[test]
828 fn person_debug_display() {
829 let mut context = Context::new();
830
831 let person_id = context.add_person(()).unwrap();
832 assert_eq!(format!("{person_id}"), "0");
833 assert_eq!(format!("{person_id:?}"), "Person 0");
834 }
835
836 #[test]
837 fn add_person_initializers() {
838 let mut context = Context::new();
839 let person_id = context.add_person(()).unwrap();
840
841 assert_eq!(context.get_person_property(person_id, RunningShoes), 0);
842 assert!(!context.get_person_property(person_id, IsRunner));
843 }
844
845 #[test]
846 fn property_initialization_is_lazy() {
847 let mut context = Context::new();
848 let person = context.add_person((IsRunner, true)).unwrap();
849 let people_data = context.get_data_container_mut(PeoplePlugin);
850
851 let has_value = *people_data.get_person_property_ref(person, RunningShoes);
853 assert!(has_value.is_none());
854
855 let value = context.get_person_property(person, RunningShoes);
857 assert_eq!(value, 4);
858 }
859
860 #[test]
861 fn initialize_without_initializer_succeeds() {
862 let mut context = Context::new();
863 context
864 .add_person((RiskCategory, RiskCategoryValue::High))
865 .unwrap();
866 }
867
868 #[test]
869 #[should_panic(expected = "Property not initialized when person created")]
870 fn set_without_initializer_panics() {
871 let mut context = Context::new();
872 let person_id = context.add_person(()).unwrap();
873 context.set_person_property(person_id, RiskCategory, RiskCategoryValue::High);
874 }
875
876 #[test]
877 #[should_panic(expected = "Property not initialized when person created")]
878 fn get_without_initializer_panics() {
879 let mut context = Context::new();
880 let person_id = context.add_person(()).unwrap();
881 context.get_person_property(person_id, RiskCategory);
882 }
883
884 #[test]
885 fn get_person_property_returns_correct_value() {
886 let mut context = Context::new();
887 let person = context.add_person((Age, 10)).unwrap();
888 assert_eq!(
889 context.get_person_property(person, AgeGroup),
890 AgeGroupValue::Child
891 );
892 }
893
894 #[test]
895 fn get_person_property_changes_correctly() {
896 let mut context = Context::new();
897 let person = context.add_person((Age, 17)).unwrap();
898 assert_eq!(
899 context.get_person_property(person, AgeGroup),
900 AgeGroupValue::Child
901 );
902 context.set_person_property(person, Age, 18);
903 assert_eq!(
904 context.get_person_property(person, AgeGroup),
905 AgeGroupValue::Adult
906 );
907 }
908
909 #[test]
910 fn get_derived_property_multiple_deps() {
911 let mut context = Context::new();
912 let person = context.add_person(((Age, 17), (IsRunner, true))).unwrap();
913 let flag = Rc::new(RefCell::new(false));
914 let flag_clone = flag.clone();
915 context.subscribe_to_event(
916 move |_context, event: PersonPropertyChangeEvent<AdultRunner>| {
917 assert_eq!(event.person_id.0, 0);
918 assert!(!event.previous);
919 assert!(event.current);
920 *flag_clone.borrow_mut() = true;
921 },
922 );
923 context.set_person_property(person, Age, 18);
924 context.execute();
925 assert!(*flag.borrow());
926 }
927
928 #[test]
929 fn register_derived_only_once() {
930 let mut context = Context::new();
931 let person = context.add_person(((Age, 17), (IsRunner, true))).unwrap();
932
933 let flag = Rc::new(RefCell::new(0));
934 let flag_clone = flag.clone();
935 context.subscribe_to_event(
936 move |_context, _event: PersonPropertyChangeEvent<AdultRunner>| {
937 *flag_clone.borrow_mut() += 1;
938 },
939 );
940 context.subscribe_to_event(
941 move |_context, _event: PersonPropertyChangeEvent<AdultRunner>| {
942 },
944 );
945 context.set_person_property(person, Age, 18);
946 context.execute();
947 assert_eq!(*flag.borrow(), 1);
948 }
949
950 #[test]
951 fn test_resolve_dependencies() {
952 let mut actual = SeniorRunner.non_derived_dependencies();
953 let mut expected = vec![TypeId::of::<Age>(), TypeId::of::<IsRunner>()];
954 actual.sort();
955 expected.sort();
956 assert_eq!(actual, expected);
957 }
958
959 #[test]
960 fn get_derived_property_dependent_on_another_derived() {
961 let mut context = Context::new();
962 let person = context.add_person(((Age, 88), (IsRunner, false))).unwrap();
963 let flag = Rc::new(RefCell::new(0));
964 let flag_clone = flag.clone();
965 assert!(!context.get_person_property(person, SeniorRunner));
966 context.subscribe_to_event(
967 move |_context, event: PersonPropertyChangeEvent<SeniorRunner>| {
968 assert_eq!(event.person_id.0, 0);
969 assert!(!event.previous);
970 assert!(event.current);
971 *flag_clone.borrow_mut() += 1;
972 },
973 );
974 context.set_person_property(person, IsRunner, true);
975 context.execute();
976 assert_eq!(*flag.borrow(), 1);
977 }
978
979 #[test]
980 fn get_derived_property_diamond_dependencies() {
981 let mut context = Context::new();
982 let person = context.add_person(((Age, 17), (IsSwimmer, true))).unwrap();
983
984 let flag = Rc::new(RefCell::new(0));
985 let flag_clone = flag.clone();
986 assert!(!context.get_person_property(person, AdultAthlete));
987 context.subscribe_to_event(
988 move |_context, event: PersonPropertyChangeEvent<AdultAthlete>| {
989 assert_eq!(event.person_id.0, 0);
990 assert!(!event.previous);
991 assert!(event.current);
992 *flag_clone.borrow_mut() += 1;
993 },
994 );
995 context.set_person_property(person, Age, 18);
996 context.execute();
997 assert_eq!(*flag.borrow(), 1);
998 }
999
1000 #[test]
1001 fn get_derived_property_with_globals() {
1002 let mut context = Context::new();
1003 context.set_global_property_value(ThresholdP, 18).unwrap();
1004 let child = context.add_person((Age, 17)).unwrap();
1005 let adult = context.add_person((Age, 19)).unwrap();
1006 assert!(!context.get_person_property(child, IsEligible));
1007 assert!(context.get_person_property(adult, IsEligible));
1008 }
1009
1010 #[test]
1011 fn text_match_person() {
1012 let mut context = Context::new();
1013 let person = context
1014 .add_person(((Age, 42), (RiskCategory, RiskCategoryValue::High)))
1015 .unwrap();
1016 assert!(context.match_person(person, ((Age, 42), (RiskCategory, RiskCategoryValue::High))));
1017 assert!(!context.match_person(person, ((Age, 43), (RiskCategory, RiskCategoryValue::High))));
1018 assert!(!context.match_person(person, ((Age, 42), (RiskCategory, RiskCategoryValue::Low))));
1019 }
1020
1021 #[test]
1022 fn test_filter_people() {
1023 let mut context = Context::new();
1024 let _ = context.add_person((Age, 40)).unwrap();
1025 let _ = context.add_person((Age, 42)).unwrap();
1026 let _ = context.add_person((Age, 42)).unwrap();
1027 let mut all_people = context.query_people(());
1028
1029 let mut result = all_people.clone();
1030 context.filter_people(&mut result, (Age, 42));
1031 assert_eq!(result.len(), 2);
1032
1033 context.filter_people(&mut all_people, (Age, 43));
1034 assert!(all_people.is_empty());
1035 }
1036
1037 #[test]
1038 fn test_sample_person_simple() {
1039 define_rng!(SampleRng1);
1040 let mut context = Context::new();
1041 context.init_random(42);
1042 assert!(context.sample_person(SampleRng1, ()).is_none());
1043 let person = context.add_person(()).unwrap();
1044 assert_eq!(context.sample_person(SampleRng1, ()).unwrap(), person);
1045 }
1046
1047 #[test]
1048 fn test_sample_person_distribution() {
1049 define_rng!(SampleRng2);
1050
1051 let mut context = Context::new();
1052 context.init_random(42);
1053
1054 assert!(context.sample_person(SampleRng2, ()).is_none());
1056 let person1 = context.add_person((Age, 10)).unwrap();
1057 let person2 = context.add_person((Age, 10)).unwrap();
1058 let person3 = context.add_person((Age, 10)).unwrap();
1059 let person4 = context.add_person((Age, 30)).unwrap();
1060
1061 assert!(context.sample_person(SampleRng2, (Age, 50)).is_none());
1063
1064 for _ in 0..10 {
1066 assert_eq!(
1067 context.sample_person(SampleRng2, (Age, 30)).unwrap(),
1068 person4
1069 );
1070 }
1071
1072 let mut count_p1: usize = 0;
1073 let mut count_p2: usize = 0;
1074 let mut count_p3: usize = 0;
1075 for _ in 0..30000 {
1076 let p = context.sample_person(SampleRng2, (Age, 10)).unwrap();
1077 if p == person1 {
1078 count_p1 += 1;
1079 } else if p == person2 {
1080 count_p2 += 1;
1081 } else if p == person3 {
1082 count_p3 += 1;
1083 } else {
1084 panic!("Unexpected person");
1085 }
1086 }
1087
1088 assert!(count_p1 >= 8700);
1090 assert!(count_p2 >= 8700);
1091 assert!(count_p3 >= 8700);
1092 }
1093
1094 #[test]
1095 fn test_sample_people_distribution() {
1096 define_rng!(SampleRng5);
1097
1098 let mut context = Context::new();
1099 context.init_random(66);
1100
1101 assert!(context.sample_person(SampleRng5, ()).is_none());
1103 let person1 = context.add_person((Age, 10)).unwrap();
1104 let person2 = context.add_person((Age, 10)).unwrap();
1105 let person3 = context.add_person((Age, 10)).unwrap();
1106 let person4 = context.add_person((Age, 44)).unwrap();
1107 let person5 = context.add_person((Age, 10)).unwrap();
1108 let person6 = context.add_person((Age, 10)).unwrap();
1109 let person7 = context.add_person((Age, 22)).unwrap();
1110 let person8 = context.add_person((Age, 10)).unwrap();
1111
1112 let mut count_p1: usize = 0;
1113 let mut count_p2: usize = 0;
1114 let mut count_p3: usize = 0;
1115 let mut count_p5: usize = 0;
1116 let mut count_p6: usize = 0;
1117 let mut count_p8: usize = 0;
1118 for _ in 0..60000 {
1119 let p = context.sample_people(SampleRng5, (Age, 10), 2);
1120 if p.contains(&person1) {
1121 count_p1 += 1;
1122 }
1123 if p.contains(&person2) {
1124 count_p2 += 1;
1125 }
1126 if p.contains(&person3) {
1127 count_p3 += 1;
1128 }
1129 if p.contains(&person5) {
1130 count_p5 += 1;
1131 }
1132 if p.contains(&person6) {
1133 count_p6 += 1;
1134 }
1135 if p.contains(&person8) {
1136 count_p8 += 1;
1137 }
1138 if p.contains(&person4) || p.contains(&person7) {
1139 println!("Unexpected person in sample: {:?}", p);
1140 panic!("Unexpected person");
1141 }
1142 }
1143
1144 assert!(count_p1 >= 8700);
1146 assert!(count_p2 >= 8700);
1147 assert!(count_p3 >= 8700);
1148 assert!(count_p5 >= 8700);
1149 assert!(count_p6 >= 8700);
1150 assert!(count_p8 >= 8700);
1151 }
1152
1153 #[test]
1154 fn test_sample_people_simple() {
1155 define_rng!(SampleRng3);
1156 let mut context = Context::new();
1157 context.init_random(42);
1158 let people0 = context.sample_people(SampleRng3, (), 1);
1159 assert_eq!(people0.len(), 0);
1160 let person1 = context.add_person(()).unwrap();
1161 let person2 = context.add_person(()).unwrap();
1162 let person3 = context.add_person(()).unwrap();
1163
1164 let people1 = context.sample_people(SampleRng3, (), 1);
1165 assert_eq!(people1.len(), 1);
1166 assert!(
1167 people1.contains(&person1) || people1.contains(&person2) || people1.contains(&person3)
1168 );
1169
1170 let people2 = context.sample_people(SampleRng3, (), 2);
1171 assert_eq!(people2.len(), 2);
1172
1173 let people3 = context.sample_people(SampleRng3, (), 3);
1174 assert_eq!(people3.len(), 3);
1175
1176 let people4 = context.sample_people(SampleRng3, (), 4);
1177 assert_eq!(people4.len(), 3);
1178 }
1179
1180 #[test]
1181 fn test_sample_people() {
1182 define_rng!(SampleRng4);
1183 let mut context = Context::new();
1184 context.init_random(42);
1185 let person1 = context
1186 .add_person(((Age, 40), (RiskCategory, RiskCategoryValue::High)))
1187 .unwrap();
1188 let _ = context
1189 .add_person(((Age, 42), (RiskCategory, RiskCategoryValue::High)))
1190 .unwrap();
1191 let person3 = context
1192 .add_person(((Age, 42), (RiskCategory, RiskCategoryValue::Low)))
1193 .unwrap();
1194 let _ = context
1195 .add_person(((Age, 42), (RiskCategory, RiskCategoryValue::High)))
1196 .unwrap();
1197 let _ = context
1198 .add_person(((Age, 42), (RiskCategory, RiskCategoryValue::High)))
1199 .unwrap();
1200 let person6 = context
1201 .add_person(((Age, 42), (RiskCategory, RiskCategoryValue::Low)))
1202 .unwrap();
1203
1204 assert!(context.sample_people(SampleRng4, (Age, 50), 1).is_empty());
1206
1207 for _ in 0..10 {
1209 assert!(context
1210 .sample_people(SampleRng4, (Age, 40), 1)
1211 .contains(&person1));
1212 }
1213
1214 let people1 = context.sample_people(SampleRng4, (Age, 40), 2);
1215 assert_eq!(people1.len(), 1);
1216 assert!(people1.contains(&person1));
1217
1218 let people2 = context.sample_people(SampleRng4, (Age, 42), 2);
1219 assert_eq!(people2.len(), 2);
1220 assert!(!people2.contains(&person1));
1221
1222 let people3 = context.sample_people(
1223 SampleRng4,
1224 ((Age, 42), (RiskCategory, RiskCategoryValue::High)),
1225 2,
1226 );
1227 assert_eq!(people3.len(), 2);
1228 assert!(
1229 !people3.contains(&person1)
1230 && !people3.contains(&person3)
1231 && !people3.contains(&person6)
1232 );
1233 }
1234
1235 mod property_initialization_queries {
1236 use super::*;
1237
1238 define_rng!(PropertyInitRng);
1239 define_person_property_with_default!(SimplePropWithDefault, u8, 1);
1240 define_derived_property!(DerivedOnce, u8, [SimplePropWithDefault], |n| n * 2);
1241 define_derived_property!(DerivedTwice, bool, [DerivedOnce], |n| n == 2);
1242
1243 #[test]
1244 fn test_query_derived_property_not_initialized() {
1245 let mut context = Context::new();
1246 context.init_random(42);
1247 let person = context.add_person(()).unwrap();
1248 assert_eq!(
1249 context
1250 .sample_person(PropertyInitRng, (DerivedOnce, 2))
1251 .unwrap(),
1252 person
1253 );
1254 }
1255
1256 #[test]
1257 fn test_query_derived_property_not_initialized_two_levels() {
1258 let mut context = Context::new();
1259 context.init_random(42);
1260 let person = context.add_person(()).unwrap();
1261 assert_eq!(
1262 context
1263 .sample_person(PropertyInitRng, (DerivedTwice, true))
1264 .unwrap(),
1265 person
1266 );
1267 }
1268 }
1269}