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