1use crate::agents::*;
2use crate::configurations::*;
3use crate::diagrams::*;
4use crate::memoize::*;
5use crate::messages::*;
6use crate::reactions::*;
7use crate::utilities::*;
8
9use clap::ArgMatches;
10use hashbrown::hash_map::Entry;
11use hashbrown::HashMap;
12use std::cell::RefCell;
13use std::cmp::max;
14use std::collections::VecDeque;
15use std::fmt::Debug;
16use std::io::stderr;
17use std::io::Write;
18use std::mem::swap;
19use std::rc::Rc;
20use std::str::FromStr;
21
22thread_local! {
25 static REACHABLE_CONFIGURATIONS_MASK: RefCell<Vec<bool>> = RefCell::new(vec![]);
27
28 static ERROR_CONFIGURATION_ID: RefCell<usize> = RefCell::new(usize::max_value());
30}
31
32#[derive(Copy, Clone, Debug)]
34pub(crate) struct Outgoing<MessageId: IndexLike, ConfigurationId: IndexLike> {
35 to_configuration_id: ConfigurationId,
37
38 delivered_message_id: MessageId,
41}
42
43#[derive(Copy, Clone, Debug)]
45pub(crate) struct Incoming<MessageId: IndexLike, ConfigurationId: IndexLike> {
46 from_configuration_id: ConfigurationId,
48
49 delivered_message_id: MessageId,
52}
53
54pub(crate) enum ConditionFunction<Model: MetaModel> {
58 ById(fn(&Model, Model::ConfigurationId) -> bool),
60
61 ByConfig(fn(&Model, &Model::Configuration) -> bool),
63}
64
65impl<Model: MetaModel> Clone for ConditionFunction<Model> {
66 fn clone(&self) -> Self {
68 match *self {
69 ConditionFunction::ById(by_id) => ConditionFunction::ById(by_id),
70 ConditionFunction::ByConfig(by_config) => ConditionFunction::ByConfig(by_config),
71 }
72 }
73}
74
75pub(crate) struct PathStep<Model: MetaModel> {
77 function: ConditionFunction<Model>,
79
80 is_negated: bool,
82
83 name: String,
85}
86
87impl<Model: MetaModel> PathStep<Model> {
88 fn clone(&self) -> Self {
90 PathStep {
91 function: self.function.clone(),
92 is_negated: self.is_negated,
93 name: self.name.clone(),
94 }
95 }
96}
97
98#[derive(Debug)]
100pub(crate) struct PathTransition<
101 MessageId: IndexLike,
102 ConfigurationId: IndexLike,
103 const MAX_MESSAGES: usize,
104> {
105 pub(crate) from_configuration_id: ConfigurationId,
107
108 delivered_message_id: MessageId,
110
111 agent_index: usize,
113
114 pub(crate) to_configuration_id: ConfigurationId,
116
117 to_condition_name: Option<String>,
119}
120
121#[derive(PartialEq, Eq, PartialOrd, Ord, Hash, Copy, Clone, Debug)]
123pub(crate) struct AgentStateTransitionContext<StateId: IndexLike> {
124 from_state_id: StateId,
126
127 from_is_deferring: bool,
129
130 to_state_id: StateId,
132
133 to_is_deferring: bool,
135}
136
137#[derive(Clone)]
141pub(crate) struct Context<
142 StateId: IndexLike,
143 MessageId: IndexLike,
144 InvalidId: IndexLike,
145 ConfigurationId: IndexLike,
146 Payload: DataLike,
147 const MAX_AGENTS: usize,
148 const MAX_MESSAGES: usize,
149> {
150 delivered_message_index: MessageIndex,
153
154 delivered_message_id: MessageId,
157
158 is_immediate: bool,
160
161 agent_index: usize,
163
164 agent_type: Rc<dyn AgentType<StateId, Payload>>,
166
167 agent_instance: usize,
169
170 agent_from_state_id: StateId,
172
173 incoming: Incoming<MessageId, ConfigurationId>,
175
176 from_configuration: Configuration<StateId, MessageId, InvalidId, MAX_AGENTS, MAX_MESSAGES>,
178
179 to_configuration: Configuration<StateId, MessageId, InvalidId, MAX_AGENTS, MAX_MESSAGES>,
181}
182
183pub struct Model<
187 StateId: IndexLike,
188 MessageId: IndexLike,
189 InvalidId: IndexLike,
190 ConfigurationId: IndexLike,
191 Payload: DataLike,
192 const MAX_AGENTS: usize,
193 const MAX_MESSAGES: usize,
194> {
195 agent_types: Vec<<Self as MetaModel>::AgentTypeRc>,
197
198 agent_labels: Vec<Rc<String>>,
200
201 first_indices: Vec<usize>,
203
204 validator: Option<<Self as MetaModel>::Validator>,
206
207 configurations: Memoize<<Self as MetaModel>::Configuration, ConfigurationId>,
209
210 transitions_count: usize,
212
213 messages: Memoize<Message<Payload>, MessageId>,
215
216 label_of_message: Vec<String>,
218
219 terse_of_message_id: Vec<MessageId>,
221
222 message_of_terse_id: Vec<MessageId>,
224
225 decr_order_messages: HashMap<MessageId, MessageId>,
227
228 incr_order_messages: HashMap<MessageId, MessageId>,
230
231 invalids: Memoize<<Self as MetaModel>::Invalid, InvalidId>,
233
234 label_of_invalid: Vec<String>,
236
237 outgoings: Vec<Vec<<Self as ModelTypes>::Outgoing>>,
239
240 incomings: Vec<Vec<<Self as ModelTypes>::Incoming>>,
242
243 max_message_string_size: RefCell<usize>,
245
246 max_invalid_string_size: RefCell<usize>,
248
249 max_configuration_string_size: RefCell<usize>,
251
252 pub(crate) print_progress_every: usize,
254
255 pub(crate) ensure_init_is_reachable: bool,
257
258 pub(crate) allow_invalid_configurations: bool,
260
261 early_abort_step: Option<PathStep<Self>>,
263
264 early_abort: bool,
266
267 conditions: HashMap<String, (ConditionFunction<Self>, &'static str)>,
269
270 reachable_configurations_mask: Vec<bool>,
272
273 reachable_configurations_count: usize,
275
276 pending_configuration_ids: VecDeque<ConfigurationId>,
278}
279
280pub trait MetaModel {
282 type StateId;
284
285 type MessageId;
287
288 type InvalidId;
290
291 type ConfigurationId;
293
294 type Payload;
296
297 const MAX_AGENTS: usize;
299
300 const MAX_MESSAGES: usize;
302
303 type AgentTypeRc;
305
306 type Message;
308
309 type Reaction;
311
312 type Action;
314
315 type Emit;
317
318 type Invalid;
320
321 type Configuration;
323
324 type ConfigurationHashEntry;
326
327 type Validator;
329
330 type Condition;
332}
333
334impl<
335 StateId: IndexLike,
336 MessageId: IndexLike,
337 InvalidId: IndexLike,
338 ConfigurationId: IndexLike,
339 Payload: DataLike,
340 const MAX_AGENTS: usize,
341 const MAX_MESSAGES: usize,
342 > MetaModel
343 for Model<StateId, MessageId, InvalidId, ConfigurationId, Payload, MAX_AGENTS, MAX_MESSAGES>
344{
345 type StateId = StateId;
346 type MessageId = MessageId;
347 type InvalidId = InvalidId;
348 type ConfigurationId = ConfigurationId;
349 type Payload = Payload;
350 const MAX_AGENTS: usize = MAX_AGENTS;
351 const MAX_MESSAGES: usize = MAX_MESSAGES;
352
353 type AgentTypeRc = Rc<dyn AgentType<StateId, Payload>>;
354 type Message = Message<Payload>;
355 type Reaction = Reaction<StateId, Payload>;
356 type Action = Action<StateId, Payload>;
357 type Emit = Emit<Payload>;
358 type Invalid = Invalid<MessageId>;
359 type Configuration = Configuration<StateId, MessageId, InvalidId, MAX_AGENTS, MAX_MESSAGES>;
360 type ConfigurationHashEntry = (
361 Configuration<StateId, MessageId, InvalidId, MAX_AGENTS, MAX_MESSAGES>,
362 ConfigurationId,
363 );
364 type Validator = fn(
365 &Self,
366 &Configuration<StateId, MessageId, InvalidId, MAX_AGENTS, MAX_MESSAGES>,
367 ) -> Option<&'static str>;
368 type Condition =
369 fn(&Self, &Configuration<StateId, MessageId, InvalidId, MAX_AGENTS, MAX_MESSAGES>) -> bool;
370}
371
372pub(crate) trait ModelTypes: MetaModel {
374 type Incoming;
376
377 type Outgoing;
379
380 type Context;
382
383 type PathStep;
385
386 type SequenceStep;
388
389 type PathTransition;
391
392 type AgentStateTransitionContext;
394
395 type AgentStateTransitions;
397
398 type AgentStateSentByDelivered;
401
402 type SequenceState;
404}
405
406impl<
407 StateId: IndexLike,
408 MessageId: IndexLike,
409 InvalidId: IndexLike,
410 ConfigurationId: IndexLike,
411 Payload: DataLike,
412 const MAX_AGENTS: usize,
413 const MAX_MESSAGES: usize,
414 > ModelTypes
415 for Model<StateId, MessageId, InvalidId, ConfigurationId, Payload, MAX_AGENTS, MAX_MESSAGES>
416{
417 type Incoming = Incoming<MessageId, ConfigurationId>;
418 type Outgoing = Outgoing<MessageId, ConfigurationId>;
419 type Context =
420 Context<StateId, MessageId, InvalidId, ConfigurationId, Payload, MAX_AGENTS, MAX_MESSAGES>;
421 type PathStep = PathStep<Self>;
422 type SequenceStep = SequenceStep<StateId, MessageId>;
423 type PathTransition = PathTransition<MessageId, ConfigurationId, MAX_MESSAGES>;
424 type AgentStateTransitionContext = AgentStateTransitionContext<StateId>;
425 type AgentStateTransitions =
426 HashMap<AgentStateTransitionContext<StateId>, HashMap<Vec<MessageId>, Vec<MessageId>>>;
427 type AgentStateSentByDelivered = HashMap<Vec<MessageId>, Vec<Vec<MessageId>>>;
428 type SequenceState = SequenceState<MessageId, MAX_AGENTS, MAX_MESSAGES>;
429}
430
431impl<
434 StateId: IndexLike,
435 MessageId: IndexLike,
436 InvalidId: IndexLike,
437 ConfigurationId: IndexLike,
438 Payload: DataLike,
439 const MAX_AGENTS: usize,
440 const MAX_MESSAGES: usize,
441 > Model<StateId, MessageId, InvalidId, ConfigurationId, Payload, MAX_AGENTS, MAX_MESSAGES>
442{
443 pub fn with_validator(
445 size: usize,
446 last_agent_type: Rc<dyn AgentType<StateId, Payload>>,
447 validator: <Self as MetaModel>::Validator,
448 ) -> Self {
449 Self::create(size, last_agent_type, Some(validator))
450 }
451
452 pub fn new(size: usize, last_agent_type: Rc<dyn AgentType<StateId, Payload>>) -> Self {
457 Self::create(size, last_agent_type, None)
458 }
459
460 fn create(
461 size: usize,
462 last_agent_type: Rc<dyn AgentType<StateId, Payload>>,
463 validator: Option<<Self as MetaModel>::Validator>,
464 ) -> Self {
465 assert!(
466 MAX_MESSAGES < MessageIndex::invalid().to_usize(),
467 "MAX_MESSAGES {} is too large, must be less than {}",
468 MAX_MESSAGES,
469 MessageIndex::invalid() );
471
472 let mut agent_types: Vec<<Self as MetaModel>::AgentTypeRc> = vec![];
473 let mut first_indices: Vec<usize> = vec![];
474 let mut agent_labels: Vec<Rc<String>> = vec![];
475
476 Self::collect_agent_types(
477 last_agent_type,
478 &mut agent_types,
479 &mut first_indices,
480 &mut agent_labels,
481 );
482
483 let mut model = Self {
484 agent_types,
485 agent_labels,
486 first_indices,
487 validator,
488 configurations: Memoize::with_capacity(usize::max_value(), size),
489 transitions_count: 0,
490 messages: Memoize::new(MessageId::invalid().to_usize()),
491 label_of_message: vec![],
492 terse_of_message_id: vec![],
493 message_of_terse_id: vec![],
494 decr_order_messages: HashMap::with_capacity(MessageId::invalid().to_usize()),
495 incr_order_messages: HashMap::with_capacity(MessageId::invalid().to_usize()),
496 invalids: Memoize::new(InvalidId::invalid().to_usize()),
497 label_of_invalid: vec![],
498 outgoings: vec![],
499 incomings: vec![],
500 max_message_string_size: RefCell::new(0),
501 max_invalid_string_size: RefCell::new(0),
502 max_configuration_string_size: RefCell::new(0),
503 print_progress_every: 0,
504 ensure_init_is_reachable: false,
505 early_abort_step: None,
506 early_abort: false,
507 allow_invalid_configurations: false,
508 conditions: HashMap::with_capacity(128),
509 reachable_configurations_mask: Vec::new(),
510 reachable_configurations_count: 0,
511 pending_configuration_ids: VecDeque::new(),
512 };
513
514 model.add_standard_conditions();
515
516 let mut initial_configuration = Configuration {
517 state_ids: [StateId::invalid(); MAX_AGENTS],
518 message_counts: [MessageIndex::from_usize(0); MAX_AGENTS],
519 message_ids: [MessageId::invalid(); MAX_MESSAGES],
520 invalid_id: InvalidId::invalid(),
521 };
522
523 assert!(model.agents_count() > 0);
524 for agent_index in 0..model.agents_count() {
525 initial_configuration.state_ids[agent_index] = StateId::from_usize(0);
526 }
527
528 let stored = model.store_configuration(initial_configuration);
529 assert!(stored.is_new);
530 assert!(stored.id.to_usize() == 0);
531
532 model
533 }
534
535 fn collect_agent_types(
536 last_agent_type: Rc<dyn AgentType<StateId, Payload>>,
537 mut agent_types: &mut Vec<<Self as MetaModel>::AgentTypeRc>,
538 mut first_indices: &mut Vec<usize>,
539 mut agent_labels: &mut Vec<Rc<String>>,
540 ) {
541 if let Some(prev_agent_type) = last_agent_type.prev_agent_type() {
542 Self::collect_agent_types(
543 prev_agent_type,
544 &mut agent_types,
545 &mut first_indices,
546 &mut agent_labels,
547 );
548 }
549
550 let count = last_agent_type.instances_count();
551 assert!(
552 count > 0,
553 "zero instances requested for the type {}",
554 last_agent_type.name() );
556
557 let first_index = first_indices.len();
558 assert!(first_index == last_agent_type.first_index());
559
560 for instance in 0..count {
561 first_indices.push(first_index);
562 agent_types.push(last_agent_type.clone());
563 let agent_label = if last_agent_type.is_singleton() {
564 debug_assert!(instance == 0);
565 last_agent_type.name().to_string()
566 } else {
567 format!("{}({})", last_agent_type.name(), instance)
568 };
569 agent_labels.push(Rc::new(agent_label));
570 }
571 }
572
573 fn add_standard_conditions(&mut self) {
574 self.conditions.insert(
575 "INIT".to_string(),
576 (
577 ConditionFunction::ById(Self::is_init_condition),
578 "matches the initial configuration",
579 ),
580 );
581 self.add_condition(
582 "VALID",
583 Self::is_valid_condition,
584 "matches any valid configuration (is typically negated)",
585 );
586 self.add_condition(
587 "IMMEDIATE_REPLACEMENT",
588 Self::has_immediate_replacement,
589 "matches a configuration with a message replaced by an immediate message",
590 );
591 self.add_condition(
592 "UNORDERED_REPLACEMENT",
593 Self::has_unordered_replacement,
594 "matches a configuration with a message replaced by an unordered message",
595 );
596 self.add_condition(
597 "ORDERED_REPLACEMENT",
598 Self::has_ordered_replacement,
599 "matches a configuration with a message replaced by an ordered message",
600 );
601 self.add_condition(
602 "0MSG",
603 Self::has_messages_count::<0>,
604 "matches any configuration with no in-flight messages",
605 );
606 self.add_condition(
607 "1MSG",
608 Self::has_messages_count::<1>,
609 "matches any configuration with a single in-flight message",
610 );
611 self.add_condition(
612 "2MSG",
613 Self::has_messages_count::<2>,
614 "matches any configuration with 2 in-flight messages",
615 );
616 self.add_condition(
617 "3MSG",
618 Self::has_messages_count::<3>,
619 "matches any configuration with 3 in-flight messages",
620 );
621 self.add_condition(
622 "4MSG",
623 Self::has_messages_count::<4>,
624 "matches any configuration with 4 in-flight messages",
625 );
626 self.add_condition(
627 "5MSG",
628 Self::has_messages_count::<5>,
629 "matches any configuration with 5 in-flight messages",
630 );
631 self.add_condition(
632 "6MSG",
633 Self::has_messages_count::<6>,
634 "matches any configuration with 6 in-flight messages",
635 );
636 self.add_condition(
637 "7MSG",
638 Self::has_messages_count::<7>,
639 "matches any configuration with 7 in-flight messages",
640 );
641 self.add_condition(
642 "8MSG",
643 Self::has_messages_count::<8>,
644 "matches any configuration with 8 in-flight messages",
645 );
646 self.add_condition(
647 "9MSG",
648 Self::has_messages_count::<9>,
649 "matches any configuration with 9 in-flight messages",
650 );
651 }
652}
653
654pub fn model_size(arg_matches: &ArgMatches, auto: usize) -> usize {
656 let size = arg_matches.value_of("size").unwrap();
657 if size == "AUTO" {
658 auto
659 } else {
660 usize::from_str(size).expect("invalid model size")
661 }
662}
663
664impl<
667 StateId: IndexLike,
668 MessageId: IndexLike,
669 InvalidId: IndexLike,
670 ConfigurationId: IndexLike,
671 Payload: DataLike,
672 const MAX_AGENTS: usize,
673 const MAX_MESSAGES: usize,
674 > Model<StateId, MessageId, InvalidId, ConfigurationId, Payload, MAX_AGENTS, MAX_MESSAGES>
675{
676 fn is_init_condition(_model: &Self, configuration_id: ConfigurationId) -> bool {
677 configuration_id.to_usize() == 0
678 }
679
680 fn is_valid_condition(
682 _model: &Self,
683 configuration: &<Self as MetaModel>::Configuration,
684 ) -> bool {
685 configuration.invalid_id.is_valid()
686 }
687 fn has_immediate_replacement(
690 model: &Self,
691 configuration: &<Self as MetaModel>::Configuration,
692 ) -> bool {
693 configuration
694 .message_ids
695 .iter()
696 .take_while(|message_id| message_id.is_valid())
697 .map(|message_id| model.get_message(*message_id))
698 .any(|message| message.replaced.is_some() && message.order == MessageOrder::Immediate)
699 }
700
701 fn has_unordered_replacement(
702 model: &Self,
703 configuration: &<Self as MetaModel>::Configuration,
704 ) -> bool {
705 configuration
706 .message_ids
707 .iter()
708 .take_while(|message_id| message_id.is_valid())
709 .map(|message_id| model.get_message(*message_id))
710 .any(|message| message.replaced.is_some() && message.order == MessageOrder::Unordered)
711 }
712
713 fn has_ordered_replacement(
715 model: &Self,
716 configuration: &<Self as MetaModel>::Configuration,
717 ) -> bool {
718 configuration
719 .message_ids
720 .iter()
721 .take_while(|message_id| message_id.is_valid())
722 .map(|message_id| model.get_message(*message_id))
723 .any(|message| {
724 message.replaced.is_some() && matches!(message.order, MessageOrder::Ordered(_))
725 })
726 }
727 fn has_messages_count<const MESSAGES_COUNT: usize>(
730 _model: &Self,
731 configuration: &<Self as MetaModel>::Configuration,
732 ) -> bool {
733 configuration
734 .message_ids
735 .iter()
736 .take_while(|message_id| message_id.is_valid())
737 .count()
738 == MESSAGES_COUNT
739 }
740}
741
742impl<
745 StateId: IndexLike,
746 MessageId: IndexLike,
747 InvalidId: IndexLike,
748 ConfigurationId: IndexLike,
749 Payload: DataLike,
750 const MAX_AGENTS: usize,
751 const MAX_MESSAGES: usize,
752 > Model<StateId, MessageId, InvalidId, ConfigurationId, Payload, MAX_AGENTS, MAX_MESSAGES>
753{
754 pub fn add_condition(
756 &mut self,
757 name: &'static str,
758 condition: <Self as MetaModel>::Condition,
759 help: &'static str,
760 ) {
761 self.conditions.insert(
762 name.to_string(),
763 (ConditionFunction::ByConfig(condition), help),
764 );
765 }
766
767 pub fn agents_count(&self) -> usize {
769 self.agent_labels.len()
770 }
771
772 pub fn agent_label_index(&self, agent_label: &str) -> Option<usize> {
774 self.agent_labels
775 .iter()
776 .position(|label| **label == agent_label)
777 }
778
779 pub fn agent_instance(&self, agent_index: usize) -> usize {
781 agent_index - self.first_indices[agent_index]
782 }
783
784 pub fn agent_label(&self, agent_index: usize) -> &str {
786 &self.agent_labels[agent_index]
787 }
788
789 pub fn is_valid(&self) -> bool {
791 self.invalids.is_empty()
792 }
793
794 pub fn get_configuration(
796 &self,
797 configuration_id: ConfigurationId,
798 ) -> <Self as MetaModel>::Configuration {
799 self.configurations.get(configuration_id)
800 }
801
802 pub fn get_message(&self, message_id: MessageId) -> <Self as MetaModel>::Message {
804 self.messages.get(message_id)
805 }
806}
807
808impl<
811 StateId: IndexLike,
812 MessageId: IndexLike,
813 InvalidId: IndexLike,
814 ConfigurationId: IndexLike,
815 Payload: DataLike,
816 const MAX_AGENTS: usize,
817 const MAX_MESSAGES: usize,
818 > Model<StateId, MessageId, InvalidId, ConfigurationId, Payload, MAX_AGENTS, MAX_MESSAGES>
819{
820 pub(crate) fn compute(&mut self) {
822 assert!(self.configurations.len() == 1);
823
824 if self.ensure_init_is_reachable {
825 assert!(self.incomings.is_empty());
826 self.incomings.reserve(self.outgoings.capacity());
827 while self.incomings.len() < self.outgoings.len() {
828 self.incomings.push(vec![]);
829 }
830 }
831
832 assert!(self.pending_configuration_ids.is_empty());
833 self.pending_configuration_ids
834 .push_back(ConfigurationId::from_usize(0));
835
836 while let Some(configuration_id) = self.pending_configuration_ids.pop_front() {
837 self.explore_configuration(configuration_id);
838 }
839
840 if self.print_progress_every > 0 {
841 eprintln!("total {} configurations", self.configurations.len());
842 }
843 }
844
845 fn reach_configuration(&mut self, mut context: <Self as ModelTypes>::Context) {
846 self.validate_configuration(&mut context);
847
848 if !self.allow_invalid_configurations && context.to_configuration.invalid_id.is_valid() {
849 let configuration_label = self.display_configuration(&context.to_configuration);
851 self.error(
852 &context,
853 &format!("reached an invalid configuration:{}", configuration_label),
854 );
855 }
857
858 let stored = self.store_configuration(context.to_configuration);
859 let to_configuration_id = stored.id;
860
861 if to_configuration_id == context.incoming.from_configuration_id {
862 return;
863 }
864
865 if self.ensure_init_is_reachable {
866 self.incomings[to_configuration_id.to_usize()].push(context.incoming);
867 }
868
869 let from_configuration_id = context.incoming.from_configuration_id;
870 let outgoing = Outgoing {
871 to_configuration_id,
872 delivered_message_id: context.incoming.delivered_message_id,
873 };
874
875 self.outgoings[from_configuration_id.to_usize()].push(outgoing);
876 self.transitions_count += 1;
877
878 if stored.is_new {
879 if !self.ensure_init_is_reachable {
880 if let Some(ref step) = self.early_abort_step {
881 if self.step_matches_configuration(&step, to_configuration_id) {
882 eprintln!("reached {} - aborting further exploration", step.name);
883 self.pending_configuration_ids.clear();
884 self.early_abort = true;
885 return;
886 }
887 }
888 }
889
890 self.pending_configuration_ids.push_back(stored.id);
891 }
892 }
893
894 fn explore_configuration(&mut self, configuration_id: ConfigurationId) {
895 let configuration = self.configurations.get(configuration_id);
896
897 let mut immediate_message_index = usize::max_value();
898 let mut immediate_message_id = MessageId::invalid();
899 let mut immediate_message_target_index = usize::max_value();
900 configuration
901 .message_ids
902 .iter()
903 .take_while(|message_id| message_id.is_valid())
904 .enumerate()
905 .for_each(|(message_index, message_id)| {
906 let message = self.messages.get(*message_id);
907 if message.order == MessageOrder::Immediate
908 && message.target_index < immediate_message_target_index
909 {
910 immediate_message_index = message_index;
911 immediate_message_id = *message_id;
912 immediate_message_target_index = message.target_index;
913 }
914 });
915
916 if immediate_message_id.is_valid() {
917 self.message_event(
918 configuration_id,
919 configuration,
920 MessageIndex::from_usize(immediate_message_index),
921 immediate_message_id,
922 );
923 } else {
924 for agent_index in 0..self.agents_count() {
925 self.activity_event(configuration_id, configuration, agent_index);
926 }
927 configuration
928 .message_ids
929 .iter()
930 .take_while(|message_id| message_id.is_valid())
931 .enumerate()
932 .for_each(|(message_index, message_id)| {
933 self.message_event(
934 configuration_id,
935 configuration,
936 MessageIndex::from_usize(message_index),
937 *message_id,
938 )
939 })
940 }
941 }
942
943 fn activity_event(
944 &mut self,
945 from_configuration_id: ConfigurationId,
946 from_configuration: <Self as MetaModel>::Configuration,
947 agent_index: usize,
948 ) {
949 let activity = {
950 let agent_type = &self.agent_types[agent_index];
951 let agent_instance = self.agent_instance(agent_index);
952 agent_type.activity(agent_instance, &from_configuration.state_ids)
953 };
954
955 match activity {
956 Activity::Passive => {}
957
958 Activity::Process1(payload1) => {
959 self.activity_message(
960 from_configuration_id,
961 from_configuration,
962 agent_index,
963 payload1,
964 );
965 }
966
967 Activity::Process1Of(payloads) => {
968 for payload in payloads.iter().flatten() {
969 self.activity_message(
970 from_configuration_id,
971 from_configuration,
972 agent_index,
973 *payload,
974 );
975 }
976 }
977 }
978 }
979
980 fn activity_message(
981 &mut self,
982 from_configuration_id: ConfigurationId,
983 from_configuration: <Self as MetaModel>::Configuration,
984 agent_index: usize,
985 payload: Payload,
986 ) {
987 let delivered_message = Message {
988 order: MessageOrder::Unordered,
989 source_index: usize::max_value(),
990 target_index: agent_index,
991 payload,
992 replaced: None,
993 };
994
995 let delivered_message_id = self.store_message(delivered_message).id;
996
997 self.message_event(
998 from_configuration_id,
999 from_configuration,
1000 MessageIndex::invalid(),
1001 delivered_message_id,
1002 );
1003 }
1004
1005 fn message_event(
1006 &mut self,
1007 from_configuration_id: ConfigurationId,
1008 from_configuration: <Self as MetaModel>::Configuration,
1009 delivered_message_index: MessageIndex,
1010 delivered_message_id: MessageId,
1011 ) {
1012 let (source_index, target_index, payload, is_immediate) = {
1013 let message = self.messages.get(delivered_message_id);
1014 if let MessageOrder::Ordered(order) = message.order {
1015 if order.to_usize() > 0 {
1016 return;
1017 }
1018 }
1019 (
1020 message.source_index,
1021 message.target_index,
1022 message.payload,
1023 message.order == MessageOrder::Immediate,
1024 )
1025 };
1026
1027 let target_instance = self.agent_instance(target_index);
1028 let target_from_state_id = from_configuration.state_ids[target_index];
1029 let target_type = self.agent_types[target_index].clone();
1030 let reaction =
1031 target_type.reaction(target_instance, &from_configuration.state_ids, &payload);
1032
1033 let incoming = Incoming {
1034 from_configuration_id,
1035 delivered_message_id,
1036 };
1037
1038 let mut to_configuration = from_configuration;
1039 if delivered_message_index.is_valid() {
1040 self.remove_message(
1041 &mut to_configuration,
1042 source_index,
1043 delivered_message_index.to_usize(),
1044 );
1045 }
1046
1047 let context = Context {
1048 delivered_message_index,
1049 delivered_message_id,
1050 is_immediate,
1051 agent_index: target_index,
1052 agent_type: target_type,
1053 agent_instance: target_instance,
1054 agent_from_state_id: target_from_state_id,
1055 incoming,
1056 from_configuration,
1057 to_configuration,
1058 };
1059 self.process_reaction(context, &reaction);
1060 }
1061
1062 fn process_reaction(
1063 &mut self,
1064 context: <Self as ModelTypes>::Context,
1065 reaction: &<Self as MetaModel>::Reaction,
1066 ) {
1067 match reaction {
1069 Reaction::Unexpected => self.error(&context, "unexpected message"), Reaction::Defer => self.defer_message(context),
1071 Reaction::Ignore => self.ignore_message(context),
1072 Reaction::Do1(action) => self.perform_action(context, action),
1073
1074 Reaction::Do1Of(actions) => {
1076 let mut did_action = false;
1077 for action in actions.iter().flatten() {
1078 self.perform_action(context.clone(), action);
1079 did_action = true;
1080 }
1081 if !did_action {
1082 self.ignore_message(context);
1083 }
1084 }
1085 }
1087 }
1088
1089 fn perform_action(
1090 &mut self,
1091 mut context: <Self as ModelTypes>::Context,
1092 action: &<Self as MetaModel>::Action,
1093 ) {
1094 if self.early_abort {
1095 return;
1096 }
1097
1098 match action {
1099 Action::Defer => self.defer_message(context),
1100 Action::Ignore => self.ignore_message(context), Action::Change(agent_to_state_id) => {
1103 if *agent_to_state_id == context.agent_from_state_id {
1104 self.ignore_message(context); } else {
1106 self.change_state(&mut context, *agent_to_state_id);
1107 self.reach_configuration(context);
1108 }
1109 }
1110 Action::Send1(emit) => {
1111 self.collect_emit(&mut context, emit);
1112 self.reach_configuration(context);
1113 }
1114 Action::ChangeAndSend1(agent_to_state_id, emit) => {
1115 if *agent_to_state_id != context.agent_from_state_id {
1116 self.change_state(&mut context, *agent_to_state_id);
1117 }
1118 self.collect_emit(&mut context, emit);
1119 self.reach_configuration(context);
1120 }
1121
1122 Action::Sends(emits) => {
1124 let mut did_emit = false;
1125 for emit in emits.iter().flatten() {
1126 self.collect_emit(&mut context, emit);
1127 did_emit = true;
1128 }
1129 if did_emit {
1130 self.reach_configuration(context);
1131 } else {
1132 self.ignore_message(context);
1133 }
1134 }
1135 Action::ChangeAndSends(agent_to_state_id, emits) => {
1137 let mut did_react = false;
1138
1139 if *agent_to_state_id != context.agent_from_state_id {
1140 self.change_state(&mut context, *agent_to_state_id);
1141 did_react = true;
1142 }
1143
1144 for emit in emits.iter().flatten() {
1145 self.collect_emit(&mut context, emit);
1146 did_react = true;
1147 }
1148
1149 if !did_react {
1150 self.ignore_message(context); } else {
1152 self.reach_configuration(context);
1153 }
1154 }
1155 }
1156 }
1157
1158 fn change_state(
1159 &mut self,
1160 mut context: &mut <Self as ModelTypes>::Context,
1161 agent_to_state_id: StateId,
1162 ) {
1163 context
1164 .to_configuration
1165 .change_state(context.agent_index, agent_to_state_id);
1166
1167 assert!(!context.to_configuration.invalid_id.is_valid());
1168
1169 if let Some(reason) = context
1170 .agent_type
1171 .state_invalid_because(context.agent_instance, &context.to_configuration.state_ids)
1172 {
1173 let invalid = Invalid::Agent(context.agent_index, reason);
1175 let invalid_id = self.store_invalid(invalid).id;
1176 context.to_configuration.invalid_id = invalid_id;
1177 }
1179 }
1180
1181 fn collect_emit(
1182 &mut self,
1183 mut context: &mut <Self as ModelTypes>::Context,
1184 emit: &<Self as MetaModel>::Emit,
1185 ) {
1186 match *emit {
1187 Emit::Immediate(payload, target_index) => {
1188 let message = Message {
1189 order: MessageOrder::Immediate,
1190 source_index: context.agent_index,
1191 target_index,
1192 payload,
1193 replaced: None,
1194 };
1195 self.emit_message(context, message);
1196 }
1197
1198 Emit::Unordered(payload, target_index) => {
1199 let message = Message {
1200 order: MessageOrder::Unordered,
1201 source_index: context.agent_index,
1202 target_index,
1203 payload,
1204 replaced: None,
1205 };
1206 self.emit_message(context, message);
1207 }
1208
1209 Emit::Ordered(payload, target_index) => {
1210 let message = self.ordered_message(
1211 &context.to_configuration,
1212 context.agent_index,
1213 target_index,
1214 payload,
1215 None,
1216 );
1217 self.emit_message(context, message);
1218 }
1219
1220 Emit::ImmediateReplacement(callback, payload, target_index) => {
1221 let replaced = self.replace_message(
1222 &mut context,
1223 callback,
1224 MessageOrder::Immediate,
1225 &payload,
1226 target_index,
1227 );
1228 let message = Message {
1229 order: MessageOrder::Immediate,
1230 source_index: context.agent_index,
1231 target_index,
1232 payload,
1233 replaced,
1234 };
1235 self.emit_message(context, message);
1236 }
1237
1238 Emit::UnorderedReplacement(callback, payload, target_index) => {
1239 let replaced = self.replace_message(
1240 &mut context,
1241 callback,
1242 MessageOrder::Unordered,
1243 &payload,
1244 target_index,
1245 );
1246 let message = Message {
1247 order: MessageOrder::Unordered,
1248 source_index: context.agent_index,
1249 target_index,
1250 payload,
1251 replaced,
1252 };
1253 self.emit_message(context, message);
1254 }
1255
1256 Emit::OrderedReplacement(callback, payload, target_index) => {
1258 let replaced = self.replace_message(
1259 &mut context,
1260 callback,
1261 MessageOrder::Ordered(MessageIndex::from_usize(0)),
1262 &payload,
1263 target_index,
1264 );
1265 let message = self.ordered_message(
1266 &context.to_configuration,
1267 context.agent_index,
1268 target_index,
1269 payload,
1270 replaced,
1271 );
1272 self.emit_message(context, message);
1273 } }
1275 }
1276
1277 fn ordered_message(
1278 &mut self,
1279 to_configuration: &<Self as MetaModel>::Configuration,
1280 source_index: usize,
1281 target_index: usize,
1282 payload: Payload,
1283 replaced: Option<Payload>,
1284 ) -> <Self as MetaModel>::Message {
1285 let mut order = {
1286 to_configuration
1287 .message_ids
1288 .iter()
1289 .take_while(|message_id| message_id.is_valid())
1290 .map(|message_id| self.messages.get(*message_id))
1291 .filter(|message| {
1292 message.source_index == source_index
1293 && message.target_index == target_index
1294 && matches!(message.order, MessageOrder::Ordered(_))
1295 })
1296 .fold(0, |count, _message| count + 1)
1297 };
1298
1299 let message = Message {
1300 order: MessageOrder::Ordered(MessageIndex::from_usize(order)),
1301 source_index,
1302 target_index,
1303 payload,
1304 replaced,
1305 };
1306
1307 let mut next_message = message;
1308 let mut next_message_id = self.store_message(next_message).id;
1309 while order > 0 {
1310 order -= 1;
1311 let entry = self.decr_order_messages.entry(next_message_id);
1312 let new_message = match entry {
1314 Entry::Occupied(_) => break,
1315 Entry::Vacant(entry) => {
1316 next_message.order = MessageOrder::Ordered(MessageIndex::from_usize(order));
1317 let stored = self.messages.store(next_message);
1318 let decr_message_id = stored.id;
1319 entry.insert(decr_message_id);
1320 self.incr_order_messages
1321 .insert(decr_message_id, next_message_id);
1322 next_message_id = decr_message_id;
1323 if stored.is_new {
1324 Some(next_message)
1325 } else {
1326 None }
1328 }
1329 };
1330
1331 if let Some(message) = new_message {
1332 self.label_of_message.push(self.display_message(&message));
1333 }
1334 }
1335
1336 message
1337 }
1338
1339 fn replace_message(
1340 &mut self,
1341 context: &mut <Self as ModelTypes>::Context,
1342 callback: fn(Option<Payload>) -> bool,
1343 order: MessageOrder,
1344 payload: &Payload,
1345 target_index: usize,
1346 ) -> Option<Payload> {
1347 let mut replaced_message_index: Option<usize> = None;
1348 let mut replaced_message: Option<<Self as MetaModel>::Message> = None;
1349
1350 let replacement_message = Message {
1351 order,
1352 source_index: context.agent_index,
1353 target_index,
1354 payload: *payload,
1355 replaced: None,
1356 };
1357
1358 for (message_index, message_id) in context
1359 .to_configuration
1360 .message_ids
1361 .iter()
1362 .take_while(|message_id| message_id.is_valid())
1363 .enumerate()
1364 {
1365 let message = self.messages.get(*message_id);
1366 if message.source_index == context.agent_index
1367 && message.target_index == target_index
1368 && callback(Some(message.payload))
1369 {
1370 if let Some(conflict_message) = replaced_message {
1371 let conflict_label = self.display_message(&conflict_message);
1373 let message_label = self.display_message(&message);
1374 let replacement_label = self.display_message(&replacement_message);
1375 self.error(
1376 context,
1377 &format!(
1378 "both the message {}\n\
1379 and the message {}\n\
1380 can be replaced by the ambiguous replacement message {}",
1381 conflict_label, message_label, replacement_label,
1382 ),
1383 );
1384 } else {
1386 replaced_message_index = Some(message_index);
1387 replaced_message = Some(message);
1388 }
1389 }
1390 }
1391
1392 if let Some(message) = replaced_message {
1393 self.remove_message(
1394 &mut context.to_configuration,
1395 context.agent_index,
1396 replaced_message_index.unwrap(),
1397 );
1398 Some(message.payload)
1399 } else {
1400 if !callback(None) {
1401 let replacement_label = self.display_message(&replacement_message);
1403 self.error(
1404 context,
1405 &format!(
1406 "nothing was replaced by the required replacement message {}",
1407 replacement_label
1408 ),
1409 );
1410 }
1412 None
1413 }
1414 }
1415
1416 fn remove_message(
1417 &self,
1418 configuration: &mut <Self as MetaModel>::Configuration,
1419 source: usize,
1420 message_index: usize,
1421 ) {
1422 let removed_message_id = configuration.message_ids[message_index];
1423 let (removed_source_index, removed_target_index, removed_order) = {
1424 let removed_message = self.messages.get(removed_message_id);
1425 if let MessageOrder::Ordered(removed_order) = removed_message.order {
1426 (
1427 removed_message.source_index,
1428 removed_message.target_index,
1429 Some(removed_order),
1430 )
1431 } else {
1432 (
1433 removed_message.source_index,
1434 removed_message.target_index,
1435 None,
1436 )
1437 }
1438 };
1439
1440 configuration.remove_message(source, message_index);
1441
1442 if let Some(removed_message_order) = removed_order {
1443 let mut did_modify = false;
1444 for message_index in 0..MAX_MESSAGES {
1445 let message_id = configuration.message_ids[message_index];
1446 if !message_id.is_valid() {
1447 break;
1448 }
1449
1450 if message_id == removed_message_id {
1451 continue;
1452 }
1453
1454 let message = self.messages.get(message_id);
1455 if message.source_index != removed_source_index
1456 || message.target_index != removed_target_index
1457 {
1458 continue;
1459 }
1460
1461 if let MessageOrder::Ordered(message_order) = message.order {
1462 if message_order > removed_message_order {
1463 configuration.message_ids[message_index] =
1464 self.decr_message_id(message_id).unwrap();
1465 did_modify = true;
1466 }
1467 }
1468 }
1469
1470 if did_modify {
1471 configuration.message_ids.sort();
1472 }
1473 }
1474 }
1475
1476 fn decr_message_id(&self, message_id: MessageId) -> Option<MessageId> {
1477 self.decr_order_messages.get(&message_id).copied()
1478 }
1479
1480 fn first_message_id(&self, mut message_id: MessageId) -> MessageId {
1481 while let Some(decr_message_id) = self.decr_message_id(message_id) {
1482 message_id = decr_message_id;
1483 }
1484 message_id
1485 }
1486
1487 fn incr_message_id(&self, message_id: MessageId) -> Option<MessageId> {
1488 self.incr_order_messages.get(&message_id).copied()
1489 }
1490
1491 fn emit_message(
1492 &mut self,
1493 context: &mut <Self as ModelTypes>::Context,
1494 message: <Self as MetaModel>::Message,
1495 ) {
1496 for to_message_id in context
1497 .to_configuration
1498 .message_ids
1499 .iter()
1500 .take_while(|to_message_id| to_message_id.is_valid())
1501 {
1502 let to_message = self.messages.get(*to_message_id);
1503 if to_message.source_index == message.source_index
1504 && to_message.target_index == message.target_index
1505 && to_message.payload == message.payload
1506 {
1507 let message_label = self.display_message(&message);
1509 self.error(
1510 context,
1511 &format!("sending a duplicate message {}", message_label),
1512 );
1513 }
1515 }
1516
1517 let message_id = self.store_message(message).id;
1518 context
1519 .to_configuration
1520 .add_message(context.agent_index, message_id);
1521 }
1522
1523 fn ignore_message(&mut self, context: <Self as ModelTypes>::Context) {
1524 self.reach_configuration(context);
1525 }
1526
1527 fn defer_message(&mut self, context: <Self as ModelTypes>::Context) {
1528 if !context.delivered_message_index.is_valid() {
1529 self.error(
1531 &context,
1532 &format!(
1533 "the agent {} is deferring an activity",
1534 self.agent_labels[context.agent_index]
1535 ),
1536 );
1537 } else {
1539 if !context.agent_type.state_is_deferring(
1540 context.agent_instance,
1541 &context.from_configuration.state_ids,
1542 ) {
1543 self.error(
1545 &context,
1546 &format!(
1547 "the agent {} is deferring while in a non-deferring state",
1548 self.agent_labels[context.agent_index]
1549 ),
1550 );
1551 }
1553
1554 if context.is_immediate {
1555 self.error(
1557 &context,
1558 &format!(
1559 "the agent {} is deferring an immediate message",
1560 self.agent_labels[context.agent_index]
1561 ),
1562 );
1563 }
1565 }
1566 }
1567
1568 fn validate_configuration(&mut self, context: &mut <Self as ModelTypes>::Context) {
1569 if context.to_configuration.invalid_id.is_valid() {
1570 return;
1571 }
1572
1573 if context.agent_index != usize::max_value() {
1574 if let Some(max_in_flight_messages) = context.agent_type.state_max_in_flight_messages(
1575 context.agent_instance,
1576 &context.from_configuration.state_ids,
1577 ) {
1578 let in_flight_messages =
1579 context.to_configuration.message_counts[context.agent_index].to_usize();
1580 if in_flight_messages > max_in_flight_messages {
1581 let configuration_label = self.display_configuration(&context.to_configuration);
1583 self.error(
1584 context,
1585 &format!(
1586 "the agent {} is sending too more messages {} than allowed {}\n\
1587 in the reached configuration:{}",
1588 self.agent_labels[context.agent_index],
1589 in_flight_messages,
1590 max_in_flight_messages,
1591 configuration_label
1592 ),
1593 );
1594 }
1596 }
1597 }
1598
1599 if let Some(validator) = self.validator {
1600 if let Some(reason) = validator(self, &context.to_configuration) {
1602 let invalid = <Self as MetaModel>::Invalid::Configuration(reason);
1603 context.to_configuration.invalid_id = self.store_invalid(invalid).id;
1604 return;
1605 }
1606 }
1608 }
1609
1610 fn store_invalid(&mut self, invalid: <Self as MetaModel>::Invalid) -> Stored<InvalidId> {
1613 let stored = self.invalids.store(invalid);
1614 if stored.is_new {
1615 debug_assert!(self.label_of_invalid.len() == stored.id.to_usize());
1616 self.label_of_invalid.push(self.display_invalid(&invalid));
1617 }
1618 stored
1619 }
1620
1621 fn store_message(&mut self, message: <Self as MetaModel>::Message) -> Stored<MessageId> {
1624 let stored = self.messages.store(message);
1625 if stored.is_new {
1626 debug_assert!(self.label_of_message.len() == stored.id.to_usize());
1627 self.label_of_message.push(self.display_message(&message));
1628 }
1629 stored
1630 }
1631
1632 fn store_configuration(
1633 &mut self,
1634 configuration: <Self as MetaModel>::Configuration,
1635 ) -> Stored<ConfigurationId> {
1636 let stored = self.configurations.store(configuration);
1637
1638 if stored.is_new {
1639 if self.print_progress(stored.id.to_usize()) {
1640 eprintln!("computed {} configurations", stored.id.to_usize());
1641 }
1642
1643 self.outgoings.push(vec![]);
1644
1645 if self.ensure_init_is_reachable {
1646 self.incomings.push(vec![]);
1647 }
1648 }
1649
1650 stored
1651 }
1652}
1653
1654impl<
1657 StateId: IndexLike,
1658 MessageId: IndexLike,
1659 InvalidId: IndexLike,
1660 ConfigurationId: IndexLike,
1661 Payload: DataLike,
1662 const MAX_AGENTS: usize,
1663 const MAX_MESSAGES: usize,
1664 > Model<StateId, MessageId, InvalidId, ConfigurationId, Payload, MAX_AGENTS, MAX_MESSAGES>
1665{
1666 fn print_progress(&self, progress: usize) -> bool {
1667 self.print_progress_every == 1
1668 || (self.print_progress_every > 0
1669 && progress > 0
1671 && progress % self.print_progress_every == 0)
1672 }
1674
1675 pub(crate) fn print_agents(&self, stdout: &mut dyn Write) {
1676 let mut names: Vec<String> = self
1677 .agent_labels
1678 .iter()
1679 .map(|agent_label| agent_label.to_string())
1680 .collect();
1681 names.sort();
1682 names.iter().for_each(|name| {
1683 writeln!(stdout, "{}", name).unwrap();
1684 });
1685 }
1686
1687 pub(crate) fn print_conditions(&self, stdout: &mut dyn Write) {
1688 let mut names: Vec<&String> = self.conditions.iter().map(|(key, _value)| key).collect();
1689 names.sort();
1690 names
1691 .iter()
1692 .map(|name| (*name, self.conditions.get(*name).unwrap().1))
1693 .for_each(|(name, about)| {
1694 writeln!(stdout, "{}: {}", name, about).unwrap();
1695 });
1696 }
1697
1698 pub(crate) fn print_stats(&self, stdout: &mut dyn Write) {
1699 let mut states_count = 0;
1700 for (agent_index, agent_type) in self.agent_types.iter().enumerate() {
1701 if agent_index == agent_type.first_index() {
1702 states_count += agent_type.states_count();
1703 }
1704 }
1705
1706 writeln!(stdout, "agents: {}", self.agents_count()).unwrap();
1707 writeln!(stdout, "states: {}", states_count).unwrap();
1708 writeln!(stdout, "messages: {}", self.messages.len()).unwrap();
1709 writeln!(stdout, "configurations: {}", self.configurations.len()).unwrap();
1710 writeln!(stdout, "transitions: {}", self.transitions_count).unwrap();
1711 }
1712
1713 pub(crate) fn print_configurations(&mut self, stdout: &mut dyn Write) {
1714 (0..self.configurations.len())
1715 .map(ConfigurationId::from_usize)
1716 .for_each(|configuration_id| {
1717 if self.print_progress(configuration_id.to_usize()) {
1718 eprintln!(
1719 "printed {} out of {} configurations ({}%)",
1720 configuration_id.to_usize(),
1721 self.configurations.len(),
1722 (100 * configuration_id.to_usize()) / self.configurations.len(),
1723 );
1724 }
1725
1726 self.print_configuration_id(configuration_id, stdout);
1727 writeln!(stdout).unwrap();
1728 });
1729 }
1730
1731 pub(crate) fn print_transitions(&self, stdout: &mut dyn Write) {
1732 self.outgoings
1733 .iter()
1734 .enumerate()
1735 .take(self.configurations.len())
1736 .for_each(|(from_configuration_id, outgoings)| {
1737 if self.print_progress(from_configuration_id) {
1738 eprintln!(
1739 "printed {} out of {} configurations ({}%)",
1740 from_configuration_id,
1741 self.configurations.len(),
1742 (100 * from_configuration_id) / self.configurations.len(),
1743 );
1744 }
1745
1746 write!(stdout, "FROM:").unwrap();
1747 self.print_configuration_id(
1748 ConfigurationId::from_usize(from_configuration_id),
1749 stdout,
1750 );
1751 write!(stdout, "\n\n").unwrap();
1752
1753 outgoings.iter().for_each(|outgoing| {
1754 let delivered_label = self.display_message_id(outgoing.delivered_message_id);
1755 write!(stdout, "BY: {}\nTO:", delivered_label).unwrap();
1756 self.print_configuration_id(outgoing.to_configuration_id, stdout);
1757 write!(stdout, "\n\n").unwrap();
1758 });
1759 });
1760 }
1761
1762 pub(crate) fn print_path(
1763 &mut self,
1764 path: &[<Self as ModelTypes>::PathTransition],
1765 stdout: &mut dyn Write,
1766 ) {
1767 path.iter().for_each(|transition| {
1768 let is_first = transition.to_configuration_id == transition.from_configuration_id;
1769 if !is_first {
1770 let delivered_label = self.display_message_id(transition.delivered_message_id);
1771 writeln!(stdout, "BY: {}", delivered_label).unwrap();
1772 }
1773
1774 if is_first {
1775 write!(stdout, "FROM").unwrap();
1776 } else {
1777 write!(stdout, "TO").unwrap();
1778 }
1779
1780 if let Some(condition_name) = &transition.to_condition_name {
1781 write!(stdout, " {}", condition_name).unwrap();
1782 }
1783
1784 write!(stdout, ":").unwrap();
1785 self.print_configuration_id(transition.to_configuration_id, stdout);
1786 write!(stdout, "\n\n").unwrap();
1787 });
1788 }
1789}
1790
1791impl<
1794 StateId: IndexLike,
1795 MessageId: IndexLike,
1796 InvalidId: IndexLike,
1797 ConfigurationId: IndexLike,
1798 Payload: DataLike,
1799 const MAX_AGENTS: usize,
1800 const MAX_MESSAGES: usize,
1801 > Model<StateId, MessageId, InvalidId, ConfigurationId, Payload, MAX_AGENTS, MAX_MESSAGES>
1802{
1803 fn error(&mut self, context: &<Self as ModelTypes>::Context, reason: &str) -> ! {
1805 let error_configuration_id = context.incoming.from_configuration_id;
1806 eprintln!(
1807 "ERROR: {}\n\
1808 when delivering the message: {}\n\
1809 in the configuration:{}\n\
1810 reached by path:\n",
1811 reason,
1812 self.display_message_id(context.delivered_message_id),
1813 self.display_configuration_id(error_configuration_id),
1814 );
1815
1816 ERROR_CONFIGURATION_ID.with(|global_error_configuration_id| {
1817 *global_error_configuration_id.borrow_mut() = error_configuration_id.to_usize()
1818 });
1819
1820 let is_error = move |_model: &Self, configuration_id: ConfigurationId| {
1821 ERROR_CONFIGURATION_ID.with(|global_error_configuration_id| {
1822 configuration_id
1823 == ConfigurationId::from_usize(*global_error_configuration_id.borrow())
1824 })
1825 };
1826
1827 let error_path_step = PathStep {
1828 function: ConditionFunction::ById(is_error),
1829 is_negated: false,
1830 name: "ERROR".to_string(),
1831 };
1832
1833 self.error_path(error_path_step);
1834 }
1835
1836 fn error_path(&mut self, error_path_step: PathStep<Self>) -> ! {
1837 let init_path_step = PathStep {
1838 function: ConditionFunction::ById(Self::is_init_condition),
1839 is_negated: false,
1840 name: "INIT".to_string(),
1841 };
1842
1843 self.pending_configuration_ids.clear();
1844 let path = self.collect_path(vec![init_path_step, error_path_step]);
1845 self.print_path(&path, &mut stderr());
1846
1847 panic!("ABORTING");
1848 }
1849 }
1851
1852impl<
1855 StateId: IndexLike,
1856 MessageId: IndexLike,
1857 InvalidId: IndexLike,
1858 ConfigurationId: IndexLike,
1859 Payload: DataLike,
1860 const MAX_AGENTS: usize,
1861 const MAX_MESSAGES: usize,
1862 > Model<StateId, MessageId, InvalidId, ConfigurationId, Payload, MAX_AGENTS, MAX_MESSAGES>
1863{
1864 pub fn display_message_id(&self, message_id: MessageId) -> &str {
1866 &self.label_of_message[message_id.to_usize()]
1867 }
1868
1869 pub fn display_message(&self, message: &<Self as MetaModel>::Message) -> String {
1871 let mut max_message_string_size = self.max_message_string_size.borrow_mut();
1872 let mut string = String::with_capacity(*max_message_string_size);
1873
1874 if message.source_index != usize::max_value() {
1875 string.push_str(&*self.agent_labels[message.source_index]);
1876 } else {
1877 string.push_str("Activity");
1878 }
1879 string.push_str(" -> ");
1880
1881 self.push_message_payload(message, false, false, &mut string);
1882
1883 string.push_str(" -> ");
1884 string.push_str(&*self.agent_labels[message.target_index]);
1885
1886 string.shrink_to_fit();
1887 *max_message_string_size = max(*max_message_string_size, string.len());
1888
1889 string
1890 }
1891
1892 fn display_sequence_message(
1894 &self,
1895 message: &<Self as MetaModel>::Message,
1896 is_final: bool,
1897 ) -> String {
1898 let max_message_string_size = self.max_message_string_size.borrow();
1899 let mut string = String::with_capacity(*max_message_string_size);
1900 self.push_message_payload(message, true, is_final, &mut string);
1901 string.shrink_to_fit();
1902 string
1903 }
1904
1905 fn push_message_payload(
1907 &self,
1908 message: &<Self as MetaModel>::Message,
1909 is_sequence: bool,
1910 is_final: bool,
1911 string: &mut String,
1912 ) {
1913 match message.order {
1914 MessageOrder::Immediate => {
1915 if !is_sequence {
1916 string.push_str("* ");
1917 }
1918 }
1919 MessageOrder::Unordered => {}
1920 MessageOrder::Ordered(order) => {
1921 if !is_sequence {
1922 string.push_str(&format!("@{} ", order));
1923 }
1924 }
1925 }
1926
1927 if let Some(ref replaced) = message.replaced {
1928 if !is_sequence {
1929 string.push_str(&format!("{} => ", replaced));
1930 } else if !is_final {
1931 string.push_str(RIGHT_DOUBLE_ARROW);
1932 string.push(' ');
1933 }
1934 }
1935
1936 string.push_str(&format!("{}", message.payload));
1937 }
1938
1939 fn display_invalid_id(&self, invalid_id: InvalidId) -> &str {
1942 &self.label_of_invalid[invalid_id.to_usize()]
1943 }
1944
1945 fn display_invalid(&self, invalid: &<Self as MetaModel>::Invalid) -> String {
1947 let mut max_invalid_string_size = self.max_invalid_string_size.borrow_mut();
1948 let mut string = String::with_capacity(*max_invalid_string_size);
1949
1950 match invalid {
1951 Invalid::Configuration(reason) => {
1952 string.push_str("configuration is invalid because ");
1953 string.push_str(reason);
1954 }
1955
1956 Invalid::Agent(agent_index, reason) => {
1957 string.push_str("agent ");
1958 string.push_str(&*self.agent_labels[*agent_index]);
1959 string.push_str(" because ");
1960 string.push_str(reason);
1961 }
1962
1963 Invalid::Message(message_id, reason) => {
1964 string.push_str("message ");
1965 string.push_str(&self.display_message_id(*message_id));
1966 string.push_str(" because ");
1967 string.push_str(reason);
1968 }
1969 }
1970
1971 string.shrink_to_fit();
1972 *max_invalid_string_size = max(string.len(), *max_invalid_string_size);
1973
1974 string
1975 }
1976
1977 pub fn display_configuration_id(&self, configuration_id: ConfigurationId) -> String {
1979 let configuration = self.configurations.get(configuration_id);
1980 self.display_configuration(&configuration)
1981 }
1982
1983 fn display_configuration(&self, configuration: &<Self as MetaModel>::Configuration) -> String {
1985 let mut max_configuration_string_size = self.max_configuration_string_size.borrow_mut();
1986 let mut buffer = Vec::<u8>::with_capacity(*max_configuration_string_size);
1987 self.print_configuration(configuration, &mut buffer);
1988 let mut string = String::from_utf8(buffer).unwrap();
1989 string.shrink_to_fit();
1990 *max_configuration_string_size = max(string.len(), *max_configuration_string_size);
1991 string
1992 }
1993
1994 fn print_configuration_id(&self, configuration_id: ConfigurationId, stdout: &mut dyn Write) {
1998 let configuration = self.configurations.get(configuration_id);
1999 self.print_configuration(&configuration, stdout)
2000 }
2001
2002 fn print_configuration(
2004 &self,
2005 configuration: &<Self as MetaModel>::Configuration,
2006 stdout: &mut dyn Write,
2007 ) {
2008 let mut hash: u64 = 0;
2009
2010 let mut prefix = "\n- ";
2011 (0..self.agents_count()).for_each(|agent_index| {
2012 let agent_type = &self.agent_types[agent_index];
2013 let agent_label = &self.agent_labels[agent_index];
2014 let agent_state_id = configuration.state_ids[agent_index];
2015 let agent_state = agent_type.display_state(agent_state_id);
2016
2017 hash ^= calculate_strings_hash(agent_label, &*agent_state);
2018
2019 if !agent_state.is_empty() {
2020 write!(stdout, "{}{}:{}", prefix, agent_label, agent_state).unwrap();
2021 prefix = "\n& ";
2022 }
2023 });
2024
2025 prefix = "\n| ";
2026 configuration
2027 .message_ids
2028 .iter()
2029 .take_while(|message_id| message_id.is_valid())
2030 .for_each(|message_id| {
2031 let message_label = self.display_message_id(*message_id);
2032 hash ^= calculate_string_hash(&message_label);
2033 write!(stdout, "{}{}", prefix, message_label).unwrap();
2034 prefix = "\n& ";
2035 });
2036
2037 if configuration.invalid_id.is_valid() {
2038 let invalid_label = self.display_invalid_id(configuration.invalid_id);
2040 hash ^= calculate_string_hash(&invalid_label);
2041 write!(stdout, "\n! {}", invalid_label).unwrap();
2042 }
2044
2045 write!(stdout, "\n# {:016X}", hash).unwrap();
2046 }
2047}
2048
2049impl<
2052 StateId: IndexLike,
2053 MessageId: IndexLike,
2054 InvalidId: IndexLike,
2055 ConfigurationId: IndexLike,
2056 Payload: DataLike,
2057 const MAX_AGENTS: usize,
2058 const MAX_MESSAGES: usize,
2059 > Model<StateId, MessageId, InvalidId, ConfigurationId, Payload, MAX_AGENTS, MAX_MESSAGES>
2060{
2061 pub(crate) fn assert_init_is_reachable(&mut self) {
2062 assert!(self.reachable_configurations_count == 0);
2063 assert!(self.reachable_configurations_mask.is_empty());
2064
2065 self.reachable_configurations_mask
2066 .resize(self.configurations.len(), false);
2067
2068 assert!(self.pending_configuration_ids.is_empty());
2069 self.pending_configuration_ids
2070 .push_back(ConfigurationId::from_usize(0));
2071 while let Some(configuration_id) = self.pending_configuration_ids.pop_front() {
2072 self.reachable_configuration(configuration_id);
2073 }
2074
2075 REACHABLE_CONFIGURATIONS_MASK.with(|reachable_configurations_mask| {
2076 swap(
2077 &mut *reachable_configurations_mask.borrow_mut(),
2078 &mut self.reachable_configurations_mask,
2079 )
2080 });
2081
2082 let unreachable_count = self.configurations.len() - self.reachable_configurations_count;
2083 if unreachable_count > 0 {
2084 eprintln!(
2086 "ERROR: there is no path back to initial state from {} configurations\n",
2087 unreachable_count
2088 );
2089
2090 let is_reachable = move |_model: &Self, configuration_id: ConfigurationId| {
2091 REACHABLE_CONFIGURATIONS_MASK.with(|reachable_configurations_mask| {
2092 reachable_configurations_mask.borrow()[configuration_id.to_usize()]
2093 })
2094 };
2095 let error_path_step = PathStep {
2096 function: ConditionFunction::ById(is_reachable),
2097 is_negated: true,
2098 name: "DEADEND".to_string(),
2099 };
2100
2101 self.error_path(error_path_step);
2102 }
2104 }
2105
2106 fn reachable_configuration(&mut self, configuration_id: ConfigurationId) {
2107 if self.reachable_configurations_mask[configuration_id.to_usize()] {
2108 return;
2109 }
2110
2111 {
2112 let reachable_configuration =
2113 &mut self.reachable_configurations_mask[configuration_id.to_usize()];
2114 if *reachable_configuration {
2115 return;
2116 }
2117 *reachable_configuration = true;
2118
2119 self.reachable_configurations_count += 1;
2120
2121 if self.print_progress(self.reachable_configurations_count) {
2122 eprintln!(
2123 "reached {} out of {} configurations ({}%)",
2124 self.reachable_configurations_count,
2125 self.configurations.len(),
2126 (100 * self.reachable_configurations_count) / self.configurations.len(),
2127 );
2128 }
2129 }
2130
2131 for next_configuration_id in self.incomings[configuration_id.to_usize()]
2132 .iter()
2133 .map(|incoming| incoming.from_configuration_id)
2134 {
2135 self.pending_configuration_ids
2136 .push_back(next_configuration_id);
2137 }
2138 }
2139}
2140
2141impl<
2144 StateId: IndexLike,
2145 MessageId: IndexLike,
2146 InvalidId: IndexLike,
2147 ConfigurationId: IndexLike,
2148 Payload: DataLike,
2149 const MAX_AGENTS: usize,
2150 const MAX_MESSAGES: usize,
2151 > Model<StateId, MessageId, InvalidId, ConfigurationId, Payload, MAX_AGENTS, MAX_MESSAGES>
2152{
2153 fn step_matches_configuration(
2154 &self,
2155 step: &<Self as ModelTypes>::PathStep,
2156 configuration_id: ConfigurationId,
2157 ) -> bool {
2158 let mut is_match = match step.function {
2160 ConditionFunction::ById(by_id) => by_id(self, configuration_id),
2161 ConditionFunction::ByConfig(by_config) => {
2162 by_config(self, &self.get_configuration(configuration_id))
2163 }
2164 };
2165
2166 if step.is_negated {
2167 is_match = !is_match
2168 }
2169
2170 is_match
2171 }
2172
2173 fn find_closest_configuration_id(
2174 &mut self,
2175 from_configuration_id: ConfigurationId,
2176 from_name: &str,
2177 to_step: &<Self as ModelTypes>::PathStep,
2178 prev_configuration_ids: &mut [ConfigurationId],
2179 ) -> ConfigurationId {
2180 assert!(self.pending_configuration_ids.is_empty());
2181 self.pending_configuration_ids
2182 .push_back(from_configuration_id);
2183
2184 prev_configuration_ids.fill(ConfigurationId::invalid());
2185
2186 while let Some(next_configuration_id) = self.pending_configuration_ids.pop_front() {
2187 for outgoing in self.outgoings[next_configuration_id.to_usize()].iter() {
2188 let to_configuration_id = outgoing.to_configuration_id;
2189 if prev_configuration_ids[to_configuration_id.to_usize()].is_valid() {
2190 continue;
2191 }
2192 prev_configuration_ids[to_configuration_id.to_usize()] = next_configuration_id;
2193
2194 let mut is_condition = false;
2195
2196 if next_configuration_id != from_configuration_id
2197 || to_configuration_id != from_configuration_id
2198 {
2199 is_condition = self.step_matches_configuration(to_step, to_configuration_id);
2200 }
2201
2202 if is_condition {
2203 self.pending_configuration_ids.clear();
2204 return to_configuration_id;
2205 }
2206
2207 self.pending_configuration_ids
2208 .push_back(to_configuration_id);
2209 }
2210 }
2211
2212 panic!(
2214 "could not find a path from the condition {} to the condition {}\n\
2215 starting from the configuration:{}",
2216 from_name,
2217 to_step.name,
2218 self.display_configuration_id(from_configuration_id)
2219 );
2220 }
2222
2223 pub(crate) fn collect_steps(
2224 &mut self,
2225 subcommand_name: &str,
2226 matches: &ArgMatches,
2227 ) -> Vec<<Self as ModelTypes>::PathStep> {
2228 let steps: Vec<<Self as ModelTypes>::PathStep> = matches
2229 .values_of("CONDITION")
2230 .unwrap_or_else(|| {
2231 panic!(
2233 "the {} command requires at least two configuration conditions, got none",
2234 subcommand_name
2235 );
2236 })
2238 .map(|name| {
2239 let (key, is_negated) = match name.strip_prefix("!") {
2240 None => (name, false),
2241 Some(suffix) => (suffix, true),
2242 };
2243 if let Some(condition) = self.conditions.get(&key.to_string()) {
2244 PathStep {
2245 function: condition.0.clone(),
2246 is_negated,
2247 name: name.to_string(),
2248 }
2249 } else {
2250 panic!("unknown configuration condition {}", name); }
2252 })
2253 .collect();
2254
2255 assert!(
2256 steps.len() > 1,
2257 "the {} command requires at least two configuration conditions, got only one",
2258 subcommand_name
2259 );
2260
2261 self.early_abort = false;
2262 if steps.len() == 2
2263 && self.step_matches_configuration(&steps[0], ConfigurationId::from_usize(0))
2264 {
2265 self.early_abort_step = Some(steps[1].clone());
2266 } else {
2267 self.early_abort_step = None;
2268 }
2269
2270 steps
2271 }
2272
2273 pub(crate) fn collect_path(
2274 &mut self,
2275 mut steps: Vec<<Self as ModelTypes>::PathStep>,
2276 ) -> Vec<<Self as ModelTypes>::PathTransition> {
2277 let mut prev_configuration_ids =
2278 vec![ConfigurationId::invalid(); self.configurations.len()];
2279
2280 let initial_configuration_id = ConfigurationId::from_usize(0);
2281
2282 let start_at_init = self.step_matches_configuration(&steps[0], initial_configuration_id);
2283
2284 let mut current_configuration_id = initial_configuration_id;
2285 let mut current_name = steps[0].name.to_string();
2286
2287 if start_at_init {
2288 steps.remove(0);
2289 } else {
2290 current_configuration_id = self.find_closest_configuration_id(
2291 initial_configuration_id,
2292 "INIT",
2293 &steps[0],
2294 &mut prev_configuration_ids,
2295 );
2296 }
2297
2298 let mut path = vec![PathTransition {
2299 from_configuration_id: current_configuration_id,
2300 delivered_message_id: MessageId::invalid(),
2301 agent_index: usize::max_value(),
2302 to_configuration_id: current_configuration_id,
2303 to_condition_name: Some(current_name.to_string()),
2304 }];
2305
2306 steps.iter().for_each(|step| {
2307 let next_configuration_id = self.find_closest_configuration_id(
2308 current_configuration_id,
2309 ¤t_name,
2310 step,
2311 &mut prev_configuration_ids,
2312 );
2313 self.collect_path_step(
2314 current_configuration_id,
2315 next_configuration_id,
2316 Some(&step.name),
2317 &prev_configuration_ids,
2318 &mut path,
2319 );
2320 current_configuration_id = next_configuration_id;
2321 current_name = step.name.to_string();
2322 });
2323
2324 path
2325 }
2326
2327 fn collect_path_step(
2328 &mut self,
2329 from_configuration_id: ConfigurationId,
2330 to_configuration_id: ConfigurationId,
2331 to_name: Option<&str>,
2332 prev_configuration_ids: &[ConfigurationId],
2333 path: &mut Vec<<Self as ModelTypes>::PathTransition>,
2334 ) {
2335 let mut configuration_ids: Vec<ConfigurationId> = vec![to_configuration_id];
2336
2337 let mut prev_configuration_id = to_configuration_id;
2338 loop {
2339 prev_configuration_id = prev_configuration_ids[prev_configuration_id.to_usize()];
2340 assert!(prev_configuration_id.is_valid());
2341 configuration_ids.push(prev_configuration_id);
2342 if prev_configuration_id == from_configuration_id {
2343 break;
2344 }
2345 }
2346
2347 configuration_ids.reverse();
2348
2349 for (prev_configuration_id, next_configuration_id) in configuration_ids
2350 [..configuration_ids.len() - 1]
2351 .iter()
2352 .zip(configuration_ids[1..].iter())
2353 {
2354 let next_name = if *next_configuration_id == to_configuration_id {
2355 to_name
2356 } else {
2357 None
2358 };
2359
2360 self.collect_small_path_step(
2361 *prev_configuration_id,
2362 *next_configuration_id,
2363 next_name,
2364 path,
2365 );
2366 }
2367 }
2368
2369 fn collect_small_path_step(
2370 &mut self,
2371 from_configuration_id: ConfigurationId,
2372 to_configuration_id: ConfigurationId,
2373 to_name: Option<&str>,
2374 path: &mut Vec<<Self as ModelTypes>::PathTransition>,
2375 ) {
2376 let from_outgoings = &self.outgoings[from_configuration_id.to_usize()];
2377 let outgoing_index = from_outgoings
2378 .iter()
2379 .position(|outgoing| outgoing.to_configuration_id == to_configuration_id)
2380 .unwrap();
2381 let outgoing = from_outgoings[outgoing_index];
2382
2383 let agent_index = self
2384 .messages
2385 .get(outgoing.delivered_message_id)
2386 .target_index;
2387 let delivered_message_id = outgoing.delivered_message_id;
2388
2389 path.push(PathTransition {
2390 from_configuration_id,
2391 delivered_message_id,
2392 agent_index,
2393 to_configuration_id,
2394 to_condition_name: to_name.map(str::to_string),
2395 });
2396 }
2397}
2398
2399impl<
2402 StateId: IndexLike,
2403 MessageId: IndexLike,
2404 InvalidId: IndexLike,
2405 ConfigurationId: IndexLike,
2406 Payload: DataLike,
2407 const MAX_AGENTS: usize,
2408 const MAX_MESSAGES: usize,
2409 > Model<StateId, MessageId, InvalidId, ConfigurationId, Payload, MAX_AGENTS, MAX_MESSAGES>
2410{
2411 fn compute_terse(&mut self, condense: &Condense) {
2412 assert!(self.terse_of_message_id.is_empty());
2413 assert!(self.message_of_terse_id.is_empty());
2414
2415 if condense.names_only {
2416 for (agent_index, agent_type) in self.agent_types.iter().enumerate() {
2417 if agent_index == agent_type.first_index() {
2418 agent_type.compute_terse();
2419 }
2420 }
2421 }
2422
2423 let mut seen_messages: HashMap<TerseMessage, usize> = HashMap::new();
2424 seen_messages.reserve(self.messages.len());
2425 self.terse_of_message_id.reserve(self.messages.len());
2426 self.message_of_terse_id.reserve(self.messages.len());
2427
2428 for message_id in 0..self.messages.len() {
2429 let message = self.messages.get(MessageId::from_usize(message_id));
2430
2431 let source_index =
2432 if message.source_index != usize::max_value() && condense.merge_instances {
2433 self.agent_types[message.source_index].first_index()
2434 } else {
2435 message.source_index
2436 };
2437
2438 let target_index = if condense.merge_instances {
2439 self.agent_types[message.target_index].first_index()
2440 } else {
2441 message.target_index
2442 };
2443
2444 let payload = if condense.names_only {
2445 message.payload.name()
2446 } else {
2447 message.payload.to_string()
2448 };
2449
2450 let replaced = if message.replaced.is_none() || condense.final_replaced {
2451 None
2452 } else if condense.names_only {
2453 Some(message.replaced.unwrap().name())
2454 } else {
2455 Some(message.replaced.unwrap().to_string())
2456 };
2457
2458 let order = if condense.final_replaced {
2459 MessageOrder::Unordered
2460 } else {
2461 match message.order {
2462 MessageOrder::Ordered(_) => MessageOrder::Ordered(MessageIndex::invalid()),
2463 order => order,
2464 }
2465 };
2466
2467 let terse_message = TerseMessage {
2468 order,
2469 source_index,
2470 target_index,
2471 payload,
2472 replaced,
2473 };
2474
2475 let terse_id = *seen_messages.entry(terse_message).or_insert_with(|| {
2476 let next_terse_id = self.message_of_terse_id.len();
2477 self.message_of_terse_id
2478 .push(MessageId::from_usize(message_id));
2479 next_terse_id
2480 });
2481
2482 self.terse_of_message_id
2483 .push(MessageId::from_usize(terse_id));
2484 }
2485
2486 self.message_of_terse_id.shrink_to_fit();
2487 }
2488}
2489
2490impl<
2493 StateId: IndexLike,
2494 MessageId: IndexLike,
2495 InvalidId: IndexLike,
2496 ConfigurationId: IndexLike,
2497 Payload: DataLike,
2498 const MAX_AGENTS: usize,
2499 const MAX_MESSAGES: usize,
2500 > Model<StateId, MessageId, InvalidId, ConfigurationId, Payload, MAX_AGENTS, MAX_MESSAGES>
2501{
2502 pub(crate) fn print_states_diagram(
2503 &mut self,
2504 condense: &Condense,
2505 agent_index: usize,
2506 stdout: &mut dyn Write,
2507 ) {
2508 let mut emitted_states = vec![false; self.agent_types[agent_index].states_count()];
2509
2510 writeln!(stdout, "digraph {{").unwrap();
2511 writeln!(stdout, "color=white;").unwrap();
2512 writeln!(stdout, "graph [ fontname=\"sans-serif\" ];").unwrap();
2513 writeln!(stdout, "node [ fontname=\"sans-serif\" ];").unwrap();
2514 writeln!(stdout, "edge [ fontname=\"sans-serif\" ];").unwrap();
2515
2516 let mut state_transition_index: usize = 0;
2517
2518 self.compute_terse(condense);
2519
2520 let state_transitions = self.collect_agent_state_transitions(condense, agent_index);
2521 let mut contexts: Vec<&<Self as ModelTypes>::AgentStateTransitionContext> =
2522 state_transitions.keys().collect();
2523 contexts.sort();
2524
2525 for context in contexts.iter() {
2526 let related_state_transitions = &state_transitions[context];
2527
2528 let mut sent_keys: Vec<&Vec<MessageId>> = related_state_transitions.keys().collect();
2529 sent_keys.sort();
2530
2531 let mut sent_by_delivered = <Self as ModelTypes>::AgentStateSentByDelivered::new();
2532
2533 for sent_message_ids_key in sent_keys.iter() {
2534 let sent_message_ids: &Vec<MessageId> = sent_message_ids_key;
2535
2536 let delivered_message_ids: &Vec<MessageId> =
2537 &related_state_transitions.get(sent_message_ids).unwrap();
2538
2539 if !sent_by_delivered.contains_key(delivered_message_ids) {
2540 sent_by_delivered.insert(delivered_message_ids.to_vec(), vec![]);
2541 }
2542
2543 sent_by_delivered
2544 .get_mut(delivered_message_ids)
2545 .unwrap()
2546 .push(sent_message_ids.to_vec());
2547 }
2548
2549 let mut delivered_keys: Vec<&Vec<MessageId>> = sent_by_delivered.keys().collect();
2550 delivered_keys.sort();
2551
2552 let mut intersecting_delivered_message_ids: Vec<Vec<MessageId>> = vec![];
2553 let mut distinct_delivered_message_ids: Vec<Vec<MessageId>> = vec![];
2554 for delivered_message_ids_key in delivered_keys.iter() {
2555 let delivered_message_ids: &Vec<MessageId> = delivered_message_ids_key;
2556 let delivered_sent_message_ids =
2557 sent_by_delivered.get(delivered_message_ids).unwrap();
2558
2559 assert!(!delivered_sent_message_ids.is_empty());
2560 if delivered_sent_message_ids.len() == 1 {
2561 intersecting_delivered_message_ids.push(delivered_message_ids.to_vec());
2562 continue;
2563 }
2564
2565 if intersecting_delivered_message_ids
2566 .iter()
2567 .any(|message_ids| message_ids == delivered_message_ids)
2568 {
2569 continue;
2570 }
2571
2572 let mut is_intersecting: bool = false;
2573 for other_delivered_message_ids_key in delivered_keys.iter() {
2574 let other_delivered_message_ids: &Vec<MessageId> =
2575 other_delivered_message_ids_key;
2576 if delivered_message_ids == other_delivered_message_ids {
2577 continue;
2578 }
2579 for delivered_message_id in delivered_message_ids {
2581 if other_delivered_message_ids
2582 .iter()
2583 .any(|message_id| message_id == delivered_message_id)
2584 {
2585 is_intersecting = true;
2586 break;
2587 }
2588 }
2589 if is_intersecting {
2590 intersecting_delivered_message_ids
2591 .push(other_delivered_message_ids.to_vec());
2592 break;
2593 }
2594 }
2596
2597 if is_intersecting {
2598 intersecting_delivered_message_ids.push(delivered_message_ids.to_vec());
2600 } else {
2602 distinct_delivered_message_ids.push(delivered_message_ids.to_vec());
2603 }
2604 }
2605
2606 for delivered_message_ids_key in intersecting_delivered_message_ids.iter() {
2607 let delivered_message_ids: &Vec<MessageId> = delivered_message_ids_key;
2608 let mut delivered_sent_keys = sent_by_delivered[delivered_message_ids].clone();
2609 delivered_sent_keys.sort();
2610
2611 for sent_message_ids_key in delivered_sent_keys.iter() {
2612 let sent_message_ids: &Vec<MessageId> = sent_message_ids_key;
2613 self.print_agent_transition_cluster(
2614 condense,
2615 &mut emitted_states,
2616 agent_index,
2617 context,
2618 delivered_message_ids,
2619 state_transition_index,
2620 false,
2621 stdout,
2622 );
2623
2624 self.print_agent_transition_sent_edges(
2625 condense,
2626 &sent_message_ids,
2627 context.to_state_id,
2628 context.to_is_deferring,
2629 state_transition_index,
2630 None,
2631 stdout,
2632 );
2633
2634 writeln!(stdout, "}}").unwrap();
2635 state_transition_index += 1;
2636 }
2637 }
2638
2639 for delivered_message_ids_key in distinct_delivered_message_ids.iter() {
2640 let mut delivered_sent_keys: Vec<&Vec<MessageId>> =
2641 related_state_transitions.keys().collect();
2642 let delivered_message_ids: &Vec<MessageId> = delivered_message_ids_key;
2643 delivered_sent_keys.sort();
2644
2645 self.print_agent_transition_cluster(
2646 condense,
2647 &mut emitted_states,
2648 agent_index,
2649 context,
2650 delivered_message_ids,
2651 state_transition_index,
2652 true,
2653 stdout,
2654 );
2655
2656 for (alternative_index, sent_message_ids_key) in
2657 delivered_sent_keys.iter().enumerate()
2658 {
2659 let sent_message_ids: &Vec<MessageId> = sent_message_ids_key;
2660
2661 self.print_agent_transition_sent_edges(
2662 condense,
2663 &sent_message_ids,
2664 context.to_state_id,
2665 context.to_is_deferring,
2666 state_transition_index,
2667 Some(alternative_index),
2668 stdout,
2669 );
2670 }
2671
2672 writeln!(stdout, "}}").unwrap();
2673 state_transition_index += 1;
2674 }
2675 }
2676
2677 writeln!(stdout, "}}").unwrap();
2678 }
2679
2680 #[allow(clippy::too_many_arguments)]
2681 fn print_agent_transition_cluster(
2682 &self,
2683 condense: &Condense,
2684 emitted_states: &mut [bool],
2685 agent_index: usize,
2686 context: &<Self as ModelTypes>::AgentStateTransitionContext,
2687 delivered_message_ids: &[MessageId],
2688 state_transition_index: usize,
2689 has_alternatives: bool,
2690 stdout: &mut dyn Write,
2691 ) {
2692 if !emitted_states[context.from_state_id.to_usize()] {
2693 self.print_agent_state_node(
2694 condense,
2695 agent_index,
2696 context.from_state_id,
2697 context.from_is_deferring,
2698 stdout,
2699 );
2700 emitted_states[context.from_state_id.to_usize()] = true;
2701 }
2702
2703 if !emitted_states[context.to_state_id.to_usize()] {
2704 self.print_agent_state_node(
2705 condense,
2706 agent_index,
2707 context.to_state_id,
2708 context.to_is_deferring,
2709 stdout,
2710 );
2711 emitted_states[context.to_state_id.to_usize()] = true;
2712 }
2713
2714 writeln!(stdout, "subgraph cluster_{} {{", state_transition_index).unwrap();
2715 Self::print_state_transition_node(state_transition_index, has_alternatives, stdout);
2716
2717 Self::print_state_transition_edge(
2718 context.from_state_id,
2719 context.from_is_deferring,
2720 state_transition_index,
2721 stdout,
2722 );
2723
2724 Self::print_transition_state_edge(
2725 state_transition_index,
2726 context.to_state_id,
2727 context.to_is_deferring,
2728 stdout,
2729 );
2730
2731 for delivered_message_id in delivered_message_ids.iter() {
2732 self.print_message_node(
2733 condense,
2734 state_transition_index,
2735 None,
2736 Some(*delivered_message_id),
2737 "D",
2738 stdout,
2739 );
2740 self.print_message_transition_edge(
2741 condense,
2742 *delivered_message_id,
2743 state_transition_index,
2744 stdout,
2745 );
2746 }
2747 }
2748
2749 #[allow(clippy::too_many_arguments)]
2750 fn print_agent_transition_sent_edges(
2751 &self,
2752 condense: &Condense,
2753 sent_message_ids: &[MessageId],
2754 to_state_id: StateId,
2755 to_is_deferring: bool,
2756 state_transition_index: usize,
2757 mut alternative_index: Option<usize>,
2758 stdout: &mut dyn Write,
2759 ) {
2760 if let Some(alternative_index) = alternative_index {
2761 if sent_message_ids.len() > 1 {
2762 Self::print_state_alternative_node_and_edge(
2763 state_transition_index,
2764 alternative_index,
2765 stdout,
2766 );
2767 }
2768 }
2769
2770 if alternative_index.is_some() && sent_message_ids.is_empty() {
2771 self.print_message_node(condense, state_transition_index, None, None, "S", stdout);
2773 self.print_transition_message_edge(
2774 condense,
2775 state_transition_index,
2776 None,
2777 None,
2778 stdout,
2779 );
2780 }
2782
2783 if sent_message_ids.len() < 2 {
2784 alternative_index = None;
2785 }
2786
2787 for sent_message_id in sent_message_ids.iter() {
2788 self.print_message_node(
2789 condense,
2790 state_transition_index,
2791 alternative_index,
2792 Some(*sent_message_id),
2793 "S",
2794 stdout,
2795 );
2796 self.print_transition_message_edge(
2797 condense,
2798 state_transition_index,
2799 alternative_index,
2800 Some(*sent_message_id),
2801 stdout,
2802 );
2803 writeln!(
2804 stdout,
2805 "S_{}_{}_{} -> A_{}_{} [ style=invis ];",
2806 state_transition_index,
2807 alternative_index.unwrap_or(usize::max_value()),
2808 sent_message_id.to_usize(),
2809 to_state_id.to_usize(),
2810 to_is_deferring,
2811 )
2812 .unwrap();
2813 }
2814 }
2815
2816 fn print_agent_state_node(
2817 &self,
2818 condense: &Condense,
2819 agent_index: usize,
2820 state_id: StateId,
2821 is_deferring: bool,
2822 stdout: &mut dyn Write,
2823 ) {
2824 let shape = if is_deferring { "octagon" } else { "ellipse" };
2825 let state = if condense.names_only {
2826 self.agent_types[agent_index].display_terse(state_id)
2827 } else {
2828 self.agent_types[agent_index]
2829 .display_state(state_id)
2830 .to_string()
2831 };
2832
2833 writeln!(
2834 stdout,
2835 "A_{}_{} [ label=\"{}\", shape={} ];",
2836 state_id.to_usize(),
2837 is_deferring,
2838 state,
2839 shape
2840 )
2841 .unwrap();
2842 }
2843
2844 fn print_state_transition_node(
2845 state_transition_index: usize,
2846 has_alternatives: bool,
2847 stdout: &mut dyn Write,
2848 ) {
2849 if has_alternatives {
2850 writeln!(
2851 stdout,
2852 "T_{}_{} [ shape=diamond, label=\"\", fontsize=0, \
2853 width=0.2, height=0.2, style=filled, color=black ];",
2854 state_transition_index,
2855 usize::max_value()
2856 )
2857 .unwrap();
2858 } else {
2859 writeln!(
2860 stdout,
2861 "T_{}_{} [ shape=point, height=0.015, width=0.015 ];",
2862 state_transition_index,
2863 usize::max_value()
2864 )
2865 .unwrap();
2866 }
2867 }
2868
2869 fn print_message_node(
2870 &self,
2871 condense: &Condense,
2872 state_transition_index: usize,
2873 alternative_index: Option<usize>,
2874 message_id: Option<MessageId>,
2875 prefix: &str,
2876 stdout: &mut dyn Write,
2877 ) {
2878 if message_id.is_none() {
2879 writeln!(
2881 stdout,
2882 "{}_{}_{} [ label=\" \", shape=plain ];",
2883 prefix,
2884 state_transition_index,
2885 alternative_index.unwrap_or(usize::max_value()),
2886 )
2887 .unwrap();
2888 return;
2889 }
2891
2892 let mut message_id = message_id.unwrap();
2893 write!(
2894 stdout,
2895 "{}_{}_{}_{} [ label=\"",
2896 prefix,
2897 state_transition_index,
2898 alternative_index.unwrap_or(usize::max_value()),
2899 message_id.to_usize()
2900 )
2901 .unwrap();
2902
2903 message_id = self.message_of_terse_id[message_id.to_usize()];
2904 let message = self.messages.get(message_id);
2905 if prefix == "D" {
2906 let source = if message.source_index == usize::max_value() {
2907 "Activity".to_string()
2908 } else if condense.merge_instances {
2909 self.agent_types[message.source_index].name()
2910 } else {
2911 self.agent_labels[message.source_index].to_string()
2912 };
2913 write!(stdout, "{} {}\\n", source, RIGHT_ARROW).unwrap();
2914 }
2915
2916 if !condense.final_replaced {
2917 if let Some(replaced) = message.replaced {
2918 if condense.names_only {
2919 write!(stdout, "{} {}\\n", replaced.name(), RIGHT_DOUBLE_ARROW).unwrap();
2920 } else {
2921 write!(stdout, "{} {}\\n", replaced, RIGHT_DOUBLE_ARROW).unwrap();
2922 }
2923 }
2924 }
2925
2926 if condense.names_only {
2927 write!(stdout, "{}", message.payload.name()).unwrap();
2928 } else {
2929 write!(stdout, "{}", message.payload).unwrap();
2930 }
2931
2932 if prefix == "S" {
2933 let target = if condense.merge_instances {
2934 self.agent_types[message.target_index].name()
2935 } else {
2936 self.agent_labels[message.target_index].to_string()
2937 };
2938 write!(stdout, "\\n{} {}", RIGHT_ARROW, target).unwrap();
2939 }
2940
2941 writeln!(stdout, "\", shape=plain ];").unwrap();
2942 }
2943
2944 fn print_state_transition_edge(
2945 from_state_id: StateId,
2946 from_is_deferring: bool,
2947 to_state_transition_index: usize,
2948 stdout: &mut dyn Write,
2949 ) {
2950 writeln!(
2951 stdout,
2952 "A_{}_{} -> T_{}_{} [ arrowhead=none, direction=forward ];",
2953 from_state_id.to_usize(),
2954 from_is_deferring,
2955 to_state_transition_index,
2956 usize::max_value()
2957 )
2958 .unwrap();
2959 }
2960
2961 fn print_transition_state_edge(
2962 from_state_transition_index: usize,
2963 to_state_id: StateId,
2964 to_is_deferring: bool,
2965 stdout: &mut dyn Write,
2966 ) {
2967 writeln!(
2968 stdout,
2969 "T_{}_{} -> A_{}_{};",
2970 from_state_transition_index,
2971 usize::max_value(),
2972 to_state_id.to_usize(),
2973 to_is_deferring,
2974 )
2975 .unwrap();
2976 }
2977
2978 fn print_message_transition_edge(
2979 &self,
2980 condense: &Condense,
2981 from_message_id: MessageId,
2982 to_state_transition_index: usize,
2983 stdout: &mut dyn Write,
2984 ) {
2985 let show_message_id = if from_message_id.is_valid() {
2986 self.message_of_terse_id[from_message_id.to_usize()]
2987 } else {
2988 from_message_id };
2990
2991 let color = if !condense.final_replaced && show_message_id.is_valid() {
2992 match self.messages.get(show_message_id).order {
2993 MessageOrder::Ordered(_) => "Blue",
2994 MessageOrder::Unordered => "Black",
2995 MessageOrder::Immediate => "Crimson",
2996 }
2997 } else {
2998 "Black"
2999 };
3000
3001 writeln!(
3002 stdout,
3003 "D_{}_{}_{} -> T_{}_{} [ color={}, style=dashed ];",
3004 to_state_transition_index,
3005 usize::max_value(),
3006 from_message_id.to_usize(),
3007 to_state_transition_index,
3008 usize::max_value(),
3009 color
3010 )
3011 .unwrap();
3012 }
3013
3014 fn print_state_alternative_node_and_edge(
3015 state_transition_index: usize,
3016 alternative_index: usize,
3017 stdout: &mut dyn Write,
3018 ) {
3019 writeln!(
3020 stdout,
3021 "T_{}_{} [ shape=point, height=0.015, width=0.015, style=filled ];",
3022 state_transition_index, alternative_index,
3023 )
3024 .unwrap();
3025
3026 writeln!(
3027 stdout,
3028 "T_{}_{} -> T_{}_{} [ arrowhead=none, direction=forward, style=dashed ];",
3029 state_transition_index,
3030 usize::max_value(),
3031 state_transition_index,
3032 alternative_index,
3033 )
3034 .unwrap();
3035 }
3036
3037 fn print_transition_message_edge(
3038 &self,
3039 condense: &Condense,
3040 from_state_transition_index: usize,
3041 from_alternative_index: Option<usize>,
3042 to_message_id: Option<MessageId>,
3043 stdout: &mut dyn Write,
3044 ) {
3045 if to_message_id.is_none() {
3046 writeln!(
3048 stdout,
3049 "T_{}_{} -> S_{}_{} [ arrowhead=dot, direction=forward, style=dashed ];",
3050 from_state_transition_index,
3051 from_alternative_index.unwrap_or(usize::max_value()),
3052 from_state_transition_index,
3053 from_alternative_index.unwrap_or(usize::max_value()),
3054 )
3055 .unwrap();
3056 return;
3057 }
3059
3060 let to_message_id = to_message_id.unwrap();
3061 let show_message_id = if to_message_id.is_valid() {
3062 self.message_of_terse_id[to_message_id.to_usize()]
3063 } else {
3064 to_message_id };
3066
3067 let color = if !condense.final_replaced && show_message_id.is_valid() {
3068 match self.messages.get(show_message_id).order {
3069 MessageOrder::Ordered(_) => "Blue",
3070 MessageOrder::Unordered => "Black",
3071 MessageOrder::Immediate => "Crimson",
3072 }
3073 } else {
3074 "Black"
3075 };
3076
3077 writeln!(
3078 stdout,
3079 "T_{}_{} -> S_{}_{}_{} [ color={}, style=dashed ];",
3080 from_state_transition_index,
3081 from_alternative_index.unwrap_or(usize::max_value()),
3082 from_state_transition_index,
3083 from_alternative_index.unwrap_or(usize::max_value()),
3084 to_message_id.to_usize(),
3085 color
3086 )
3087 .unwrap();
3088 }
3089}
3090
3091impl<
3094 StateId: IndexLike,
3095 MessageId: IndexLike,
3096 InvalidId: IndexLike,
3097 ConfigurationId: IndexLike,
3098 Payload: DataLike,
3099 const MAX_AGENTS: usize,
3100 const MAX_MESSAGES: usize,
3101 > Model<StateId, MessageId, InvalidId, ConfigurationId, Payload, MAX_AGENTS, MAX_MESSAGES>
3102{
3103 pub(crate) fn collect_sequence_steps(
3104 &mut self,
3105 path: &[<Self as ModelTypes>::PathTransition],
3106 ) -> Vec<<Self as ModelTypes>::SequenceStep> {
3107 let mut sequence_steps: Vec<<Self as ModelTypes>::SequenceStep> = vec![];
3108 for path_transition in path {
3109 let agent_index = path_transition.agent_index;
3110 let from_configuration_id = path_transition.from_configuration_id;
3111 let from_configuration = self.configurations.get(from_configuration_id);
3112 let to_configuration_id = path_transition.to_configuration_id;
3113 let to_configuration = self.configurations.get(to_configuration_id);
3114 let did_change_state = to_configuration.state_ids[agent_index]
3115 != from_configuration.state_ids[agent_index];
3116 let from_outgoings = &self.outgoings[from_configuration_id.to_usize()];
3117 let outgoing_index = from_outgoings
3118 .iter()
3119 .position(|outgoing| outgoing.to_configuration_id == to_configuration_id)
3120 .unwrap();
3121 let outgoing = from_outgoings[outgoing_index];
3122 let delivered_message = self.messages.get(path_transition.delivered_message_id);
3123 let is_activity = delivered_message.source_index == usize::max_value();
3124
3125 sequence_steps.push(SequenceStep::Received {
3126 source_index: delivered_message.source_index,
3127 target_index: delivered_message.target_index,
3128 did_change_state,
3129 is_activity,
3130 message_id: path_transition.delivered_message_id,
3131 });
3132
3133 to_configuration
3134 .message_ids
3135 .iter()
3136 .take_while(|to_message_id| to_message_id.is_valid())
3137 .filter(|to_message_id| {
3138 !self.to_message_kept_in_transition(
3139 **to_message_id,
3140 &from_configuration,
3141 outgoing.delivered_message_id,
3142 )
3143 })
3144 .for_each(|to_message_id| {
3145 let to_message = self.messages.get(*to_message_id);
3146 assert!(agent_index == to_message.source_index);
3147
3148 let replaced = to_message.replaced.map(|replaced_payload| {
3149 *from_configuration
3150 .message_ids
3151 .iter()
3152 .take_while(|from_message_id| from_message_id.is_valid())
3153 .find(|from_message_id| {
3154 let from_message = self.messages.get(**from_message_id);
3155 from_message.source_index == to_message.source_index
3156 && from_message.target_index == to_message.target_index
3157 && from_message.payload == replaced_payload
3158 })
3159 .unwrap()
3160 });
3161 sequence_steps.push(SequenceStep::Emitted {
3162 source_index: to_message.source_index,
3163 target_index: to_message.target_index,
3164 message_id: *to_message_id,
3165 replaced,
3166 is_immediate: to_message.order == MessageOrder::Immediate,
3167 });
3168 });
3169
3170 if did_change_state {
3171 let agent_type = &self.agent_types[agent_index];
3172 let agent_instance = self.agent_instance(agent_index);
3173 let state_id = to_configuration.state_ids[agent_index];
3174 let is_deferring =
3175 agent_type.state_is_deferring(agent_instance, &to_configuration.state_ids);
3176 sequence_steps.push(SequenceStep::NewState {
3177 agent_index,
3178 state_id,
3179 is_deferring,
3180 });
3181 }
3182 }
3183
3184 sequence_steps
3185 }
3186
3187 fn patch_sequence_steps(&self, sequence_steps: &mut [<Self as ModelTypes>::SequenceStep]) {
3188 let trace = false;
3189 if trace {
3190 eprintln!(
3192 "sequence_steps {}",
3193 format!("{:?}", sequence_steps)
3194 .replace("}, ", "},\n")
3195 .replace("NoStep, ", "")
3196 .replace("[", "[\n")
3197 .replace("]", "\n]")
3198 );
3199 }
3201 self.move_immediates_up(sequence_steps);
3202 if trace {
3203 eprintln!(
3205 "move_immediates_up {}",
3206 format!("{:?}", sequence_steps)
3207 .replace("}, ", "},\n")
3208 .replace("NoStep, ", "")
3209 .replace("[", "[\n")
3210 .replace("]", "\n]")
3211 );
3212 }
3214 self.merge_message_steps(sequence_steps);
3215 if trace {
3216 eprintln!(
3218 "merge_message_steps {}",
3219 format!("{:?}", sequence_steps)
3220 .replace("}, ", "},\n")
3221 .replace("NoStep, ", "")
3222 .replace("[", "[\n")
3223 .replace("]", "\n]")
3224 );
3225 }
3227 self.merge_state_steps(sequence_steps);
3228 if trace {
3229 eprintln!(
3231 "merge_state_steps {}",
3232 format!("{:?}", sequence_steps)
3233 .replace("}, ", "},\n")
3234 .replace("NoStep, ", "")
3235 .replace("[", "[\n")
3236 .replace("]", "\n]")
3237 );
3238 }
3240 self.move_passed_down(sequence_steps);
3241 if trace {
3242 eprintln!(
3244 "move_passed_down {}",
3245 format!("{:?}", sequence_steps)
3246 .replace("}, ", "},\n")
3247 .replace("NoStep, ", "")
3248 .replace("[", "[\n")
3249 .replace("]", "\n]")
3250 );
3251 }
3253 }
3254
3255 fn move_immediates_up(&self, sequence_steps: &mut [<Self as ModelTypes>::SequenceStep]) {
3256 let mut first_index: usize = 0;
3257 while first_index < sequence_steps.len() - 1 {
3258 let second_index = first_index + 1;
3259
3260 let to_swap = matches!((sequence_steps[first_index], sequence_steps[second_index]),
3261 (
3262 SequenceStep::Emitted {
3263 source_index: first_source_index,
3264 is_immediate: false,
3265 ..
3266 },
3267 SequenceStep::Emitted {
3268 source_index: second_source_index,
3269 is_immediate: true,
3270 ..
3271 },
3272 ) if first_source_index == second_source_index);
3273
3274 if to_swap {
3275 sequence_steps.swap(first_index, second_index);
3276 } else {
3277 first_index = second_index;
3278 }
3279 }
3280 }
3281
3282 fn merge_message_steps(&self, sequence_steps: &mut [<Self as ModelTypes>::SequenceStep]) {
3283 for first_index in 0..(sequence_steps.len() - 1) {
3284 if let SequenceStep::Emitted {
3285 source_index: first_source_index,
3286 target_index: first_target_index,
3287 message_id: first_message_id,
3288 replaced,
3289 ..
3290 } = sequence_steps[first_index]
3291 {
3292 for second_index in (first_index + 1)..sequence_steps.len() {
3293 match sequence_steps[second_index] {
3294 SequenceStep::NoStep => {
3295 continue;
3296 }
3297
3298 SequenceStep::NewState {
3299 agent_index: state_agent_index,
3300 ..
3301 } if state_agent_index == first_target_index => {
3302 break;
3303 }
3304
3305 SequenceStep::NewState { .. } => {
3306 continue;
3307 }
3308
3309 SequenceStep::Emitted {
3310 source_index: second_source_index,
3311 ..
3312 } if second_source_index == first_source_index => {
3313 continue;
3314 }
3315
3316 SequenceStep::Received {
3317 message_id: second_message_id,
3318 did_change_state,
3319 is_activity: false,
3320 ..
3321 } if second_message_id == first_message_id => {
3322 sequence_steps[first_index] = SequenceStep::Passed {
3323 source_index: first_source_index,
3324 target_index: first_target_index,
3325 target_did_change_state: did_change_state,
3326 message_id: first_message_id,
3327 replaced,
3328 };
3329 sequence_steps[second_index] = SequenceStep::NoStep;
3330 break;
3331 }
3332
3333 SequenceStep::Received {
3334 source_index: second_source_index,
3335 ..
3336 } if second_source_index == first_source_index => {
3337 continue;
3338 }
3339
3340 _ => {
3341 break;
3342 }
3343 }
3344 }
3345 }
3346 }
3347 }
3348
3349 fn merge_state_steps(&self, sequence_steps: &mut [<Self as ModelTypes>::SequenceStep]) {
3350 for first_index in 0..(sequence_steps.len() - 1) {
3351 if let SequenceStep::NewState {
3352 agent_index: first_agent_index,
3353 state_id: first_state_id,
3354 is_deferring: first_is_deferring,
3355 } = sequence_steps[first_index]
3356 {
3357 let mut touched_agent_indices: [bool; MAX_AGENTS] = [false; MAX_AGENTS];
3358 touched_agent_indices[first_agent_index] = true;
3359 for second_index in (first_index + 1)..sequence_steps.len() {
3360 match sequence_steps[second_index] {
3361 SequenceStep::NoStep => {
3362 continue;
3363 }
3364
3365 SequenceStep::Emitted {
3367 source_index: second_source_index,
3368 target_index: second_target_index,
3369 ..
3370 } => {
3371 if second_source_index == first_agent_index {
3372 touched_agent_indices[second_target_index] = true;
3373 continue;
3374 } else {
3375 break;
3376 }
3377 }
3378
3379 SequenceStep::Passed {
3380 source_index: second_source_index,
3381 target_index: second_target_index,
3382 ..
3383 } => {
3384 if second_source_index == first_agent_index {
3385 touched_agent_indices[second_target_index] = true;
3386 continue;
3387 } else {
3388 break;
3389 }
3390 }
3391 SequenceStep::Received {
3393 target_index: second_target_index,
3394 ..
3395 } => {
3396 if second_target_index == first_agent_index {
3397 break;
3398 } else {
3399 touched_agent_indices[second_target_index] = true;
3400 continue;
3401 }
3402 }
3403
3404 SequenceStep::NewState {
3405 agent_index: second_agent_index,
3406 state_id: second_state_id,
3407 is_deferring: second_is_deferring,
3408 } if !touched_agent_indices[second_agent_index] => {
3409 sequence_steps[first_index] = SequenceStep::NewStates {
3410 first_agent_index,
3411 first_state_id,
3412 first_is_deferring,
3413 second_agent_index,
3414 second_state_id,
3415 second_is_deferring,
3416 };
3417 sequence_steps[second_index] = SequenceStep::NoStep;
3418 break;
3419 }
3420
3421 SequenceStep::NewState {
3422 agent_index: second_agent_index,
3423 ..
3424 } => {
3425 touched_agent_indices[second_agent_index] = true;
3426 continue;
3427 }
3428
3429 _ => {
3430 break; }
3432 }
3433 }
3434 }
3435 }
3436 }
3437
3438 fn move_passed_down(&self, sequence_steps: &mut [<Self as ModelTypes>::SequenceStep]) {
3439 for first_index in 0..(sequence_steps.len() - 1) {
3440 if let SequenceStep::Passed {
3441 target_index: first_target_index,
3442 source_index: first_source_index,
3443 ..
3444 } = sequence_steps[first_index]
3445 {
3446 for second_index in (first_index + 1)..sequence_steps.len() {
3447 match sequence_steps[second_index] {
3448 SequenceStep::NoStep => {
3449 continue;
3450 }
3451
3452 SequenceStep::NewState {
3454 agent_index: second_agent_index,
3455 ..
3456 } if second_agent_index != first_source_index
3457 && second_agent_index != first_target_index =>
3458 {
3459 sequence_steps.swap(first_index, second_index);
3460 break;
3461 }
3462 SequenceStep::NewStates {
3464 first_agent_index,
3465 second_agent_index,
3466 ..
3467 } if first_agent_index != first_source_index
3468 && first_agent_index != first_target_index
3470 && second_agent_index != first_source_index
3471 && second_agent_index != first_target_index =>
3472 {
3473 sequence_steps.swap(first_index, second_index);
3474 break;
3475 }
3476 _ => {
3478 break;
3479 }
3480 }
3481 }
3482 }
3483 }
3484 }
3485
3486 pub(crate) fn print_sequence_diagram(
3487 &mut self,
3488 first_configuration_id: ConfigurationId,
3489 last_configuration_id: ConfigurationId,
3490 mut sequence_steps: &mut [<Self as ModelTypes>::SequenceStep],
3491 stdout: &mut dyn Write,
3492 ) {
3493 self.patch_sequence_steps(&mut sequence_steps);
3494
3495 let first_configuration = self.configurations.get(first_configuration_id);
3496 let last_configuration = self.configurations.get(last_configuration_id);
3497
3498 writeln!(stdout, "@startuml").unwrap();
3499 writeln!(stdout, "autonumber \" <b>#</b> \"").unwrap();
3500 writeln!(stdout, "skinparam shadowing false").unwrap();
3501 writeln!(stdout, "skinparam sequence {{").unwrap();
3502 writeln!(stdout, "ArrowColor Black").unwrap();
3503 writeln!(stdout, "ActorBorderColor Black").unwrap();
3504 writeln!(stdout, "LifeLineBorderColor Black").unwrap();
3505 writeln!(stdout, "LifeLineBackgroundColor Black").unwrap();
3506 writeln!(stdout, "ParticipantBorderColor Black").unwrap();
3507 writeln!(stdout, "}}").unwrap();
3508 writeln!(stdout, "skinparam ControlBorderColor White").unwrap();
3509 writeln!(stdout, "skinparam ControlBackgroundColor White").unwrap();
3510
3511 let agents_timelines = vec![
3512 AgentTimelines {
3513 left: vec![],
3514 right: vec![]
3515 };
3516 self.agents_count()
3517 ];
3518
3519 let mut sequence_state = SequenceState {
3520 timelines: vec![],
3521 message_timelines: HashMap::new(),
3522 agents_timelines,
3523 has_reactivation_message: false,
3524 };
3525
3526 self.print_sequence_participants(&first_configuration, stdout);
3527 self.print_first_timelines(&mut sequence_state, &first_configuration, stdout);
3528 self.print_sequence_first_notes(&sequence_state, &first_configuration, stdout);
3529
3530 for sequence_step in sequence_steps.iter() {
3531 self.print_sequence_step(&mut sequence_state, *sequence_step, stdout);
3532 }
3533
3534 if last_configuration.invalid_id.is_valid() {
3535 writeln!(
3537 stdout,
3538 "== {} ==",
3539 self.display_invalid_id(last_configuration.invalid_id)
3540 )
3541 .unwrap();
3542 }
3544
3545 self.print_sequence_final(&mut sequence_state, stdout);
3546
3547 writeln!(stdout, "@enduml").unwrap();
3548 }
3549
3550 fn print_sequence_participants(
3551 &self,
3552 first_configuration: &<Self as MetaModel>::Configuration,
3553 stdout: &mut dyn Write,
3554 ) {
3555 self.agent_labels
3556 .iter()
3557 .enumerate()
3558 .for_each(|(agent_index, agent_label)| {
3559 let agent_type = &self.agent_types[agent_index];
3560 let agent_instance = self.agent_instance(agent_index);
3561 writeln!(
3562 stdout,
3563 "participant \"{}\" as A{} order {}",
3564 agent_label,
3565 agent_index,
3566 self.agent_scaled_order(agent_index),
3567 )
3568 .unwrap();
3569 if agent_type.state_is_deferring(agent_instance, &first_configuration.state_ids) {
3570 writeln!(stdout, "activate A{} #MediumPurple", agent_index).unwrap();
3572 } else {
3574 writeln!(stdout, "activate A{} #CadetBlue", agent_index).unwrap();
3575 }
3576 });
3577 }
3578
3579 fn print_first_timelines(
3580 &self,
3581 mut sequence_state: &mut <Self as ModelTypes>::SequenceState,
3582 first_configuration: &<Self as MetaModel>::Configuration,
3583 stdout: &mut dyn Write,
3584 ) {
3585 for message_id in first_configuration
3586 .message_ids
3587 .iter()
3588 .take_while(|message_id| message_id.is_valid())
3589 {
3590 self.reactivate(&mut sequence_state, stdout);
3592 let timeline_index =
3593 self.find_sequence_timeline(&mut sequence_state, *message_id, stdout);
3594 writeln!(stdout, "activate T{} #Silver", timeline_index).unwrap();
3595 }
3597 }
3598
3599 fn agent_scaled_order(&self, agent_index: usize) -> usize {
3600 let agent_type = &self.agent_types[agent_index];
3601 let agent_instance = self.agent_instance(agent_index);
3602 let agent_order = agent_type.instance_order(agent_instance);
3603 ((agent_order + 1) * 100 + agent_index + 1) * 100
3604 }
3605
3606 fn is_rightwards_message(&self, message: &<Self as MetaModel>::Message) -> bool {
3607 let source_scaled_order = self.agent_scaled_order(message.source_index);
3608 let target_scaled_order = self.agent_scaled_order(message.target_index);
3609 source_scaled_order < target_scaled_order
3610 }
3611
3612 fn find_sequence_timeline(
3613 &self,
3614 sequence_state: &mut <Self as ModelTypes>::SequenceState,
3615 message_id: MessageId,
3616 stdout: &mut dyn Write,
3617 ) -> usize {
3618 let message = self.messages.get(message_id);
3619 let is_rightwards_message = self.is_rightwards_message(&message);
3620 let empty_timeline_index = if is_rightwards_message {
3621 sequence_state.agents_timelines[message.source_index]
3622 .right
3623 .iter()
3624 .copied()
3625 .find(|timeline_index| sequence_state.timelines[*timeline_index].is_none())
3626 } else {
3627 sequence_state.agents_timelines[message.source_index]
3628 .left
3629 .iter()
3630 .copied()
3631 .find(|timeline_index| sequence_state.timelines[*timeline_index].is_none())
3632 };
3633
3634 let timeline_index = empty_timeline_index.unwrap_or_else(|| sequence_state.timelines.len());
3635
3636 let first_message_id = self.first_message_id(message_id);
3637 sequence_state
3638 .message_timelines
3639 .insert(first_message_id, timeline_index);
3640
3641 if empty_timeline_index.is_some() {
3642 sequence_state.timelines[timeline_index] = Some(first_message_id);
3644 return timeline_index;
3645 }
3647 sequence_state.timelines.push(Some(first_message_id));
3648
3649 let message = self.messages.get(message_id);
3650 let timeline_order = if is_rightwards_message {
3651 sequence_state.agents_timelines[message.source_index]
3652 .right
3653 .push(timeline_index);
3654 self.agent_scaled_order(message.source_index)
3655 + sequence_state.agents_timelines[message.source_index]
3656 .right
3657 .len()
3658 } else {
3659 sequence_state.agents_timelines[message.source_index]
3660 .left
3661 .push(timeline_index);
3662 self.agent_scaled_order(message.source_index)
3663 - sequence_state.agents_timelines[message.source_index]
3664 .left
3665 .len()
3666 };
3667
3668 writeln!(
3669 stdout,
3670 "control \" \" as T{} order {}",
3671 timeline_index, timeline_order
3672 )
3673 .unwrap();
3674
3675 timeline_index
3676 }
3677
3678 fn print_sequence_first_notes(
3679 &self,
3680 sequence_state: &<Self as ModelTypes>::SequenceState,
3681 first_configuration: &<Self as MetaModel>::Configuration,
3682 stdout: &mut dyn Write,
3683 ) {
3684 self.agent_types
3685 .iter()
3686 .enumerate()
3687 .map(|(agent_index, agent_type)| {
3688 (
3689 agent_index,
3690 agent_type.display_state(first_configuration.state_ids[agent_index]),
3691 )
3692 })
3693 .filter(|(_agent_index, agent_state)| !agent_state.is_empty())
3694 .enumerate()
3695 .for_each(|(note_index, (agent_index, agent_state))| {
3696 if note_index > 0 {
3697 write!(stdout, "/ ").unwrap();
3698 }
3699 writeln!(stdout, "rnote over A{} : {}", agent_index, agent_state,).unwrap();
3700 });
3701
3702 sequence_state
3703 .timelines
3704 .iter()
3705 .enumerate()
3706 .filter(|(_timeline_index, message_id)| message_id.is_some()) .for_each(|(timeline_index, message_id)| {
3708 let message = self.messages.get(message_id.unwrap());
3710 writeln!(
3711 stdout,
3712 "/ rnote over T{} : {}",
3713 timeline_index,
3714 self.display_sequence_message(&message, false)
3715 )
3716 .unwrap();
3717 });
3719 }
3720
3721 fn print_sequence_step(
3722 &self,
3723 mut sequence_state: &mut <Self as ModelTypes>::SequenceState,
3724 sequence_step: <Self as ModelTypes>::SequenceStep,
3725 stdout: &mut dyn Write,
3726 ) {
3727 match sequence_step {
3728 SequenceStep::NoStep => {}
3729
3730 SequenceStep::Received {
3731 target_index,
3732 did_change_state,
3733 is_activity: true,
3734 message_id,
3735 ..
3736 } => {
3737 let message = self.messages.get(message_id);
3738
3739 if did_change_state {
3740 sequence_state.has_reactivation_message = false;
3741 self.reactivate(&mut sequence_state, stdout);
3742 writeln!(stdout, "deactivate A{}", target_index).unwrap();
3743 sequence_state.has_reactivation_message = false;
3744 }
3745
3746 writeln!(
3747 stdout,
3748 "note over A{} : {}",
3749 target_index,
3750 self.display_sequence_message(&message, true)
3751 )
3752 .unwrap();
3753 }
3754
3755 SequenceStep::Received {
3756 target_index,
3757 did_change_state,
3758 is_activity: false,
3759 message_id,
3760 ..
3761 } => {
3762 let message = self.messages.get(message_id);
3763 let first_message_id = self.first_message_id(message_id);
3764 let timeline_index = *sequence_state
3765 .message_timelines
3766 .get(&first_message_id)
3767 .unwrap();
3768
3769 let arrow = match message.order {
3770 MessageOrder::Immediate => "-[#Crimson]>",
3771 MessageOrder::Unordered => "->",
3772 MessageOrder::Ordered(_) => "-[#Blue]>",
3773 };
3774
3775 writeln!(
3776 stdout,
3777 "T{} {} A{} : {}",
3778 timeline_index,
3779 arrow,
3780 message.target_index,
3781 self.display_sequence_message(&message, true)
3782 )
3783 .unwrap();
3784 sequence_state.has_reactivation_message = true;
3785
3786 writeln!(stdout, "deactivate T{}", timeline_index).unwrap();
3787
3788 sequence_state.message_timelines.remove(&first_message_id);
3789 sequence_state.timelines[timeline_index] = None;
3790 if did_change_state {
3791 writeln!(stdout, "deactivate A{}", target_index).unwrap();
3792 sequence_state.has_reactivation_message = false;
3793 }
3794 }
3795
3796 SequenceStep::Emitted {
3797 source_index,
3798 message_id,
3799 replaced,
3800 ..
3801 } => {
3802 let timeline_index = match replaced {
3804 Some(replaced_message_id) => {
3805 let replaced_first_message_id = self.first_message_id(replaced_message_id);
3806 let timeline_index = *sequence_state
3807 .message_timelines
3808 .get(&replaced_first_message_id)
3809 .unwrap();
3810 sequence_state
3811 .message_timelines
3812 .remove(&replaced_first_message_id);
3813 let first_message_id = self.first_message_id(message_id);
3814 sequence_state
3815 .message_timelines
3816 .insert(first_message_id, timeline_index);
3817 sequence_state.timelines[timeline_index] = Some(first_message_id);
3818 timeline_index
3819 }
3820 None => self.find_sequence_timeline(&mut sequence_state, message_id, stdout),
3821 };
3822 let message = self.messages.get(message_id);
3823 let arrow = match message.order {
3824 MessageOrder::Immediate => "-[#Crimson]>",
3825 MessageOrder::Unordered => "->",
3826 MessageOrder::Ordered(_) => "-[#Blue]>",
3827 };
3828 writeln!(
3829 stdout,
3830 "A{} {} T{} : {}",
3831 source_index,
3832 arrow,
3833 timeline_index,
3834 self.display_sequence_message(&message, false)
3835 )
3836 .unwrap();
3837 if replaced.is_none() {
3838 writeln!(stdout, "activate T{} #Silver", timeline_index).unwrap();
3839 }
3840 sequence_state.has_reactivation_message = true;
3841 }
3842
3843 SequenceStep::Passed {
3844 source_index,
3845 target_index,
3846 target_did_change_state,
3847 message_id,
3848 replaced,
3849 ..
3850 } => {
3851 let replaced_timeline_index: Option<usize> =
3852 if let Some(replaced_message_id) = replaced {
3853 let replaced_first_message_id = self.first_message_id(replaced_message_id);
3854 sequence_state
3855 .message_timelines
3856 .get(&replaced_first_message_id)
3857 .copied()
3858 .map(|timeline_index| {
3859 sequence_state
3860 .message_timelines
3861 .remove(&replaced_first_message_id);
3862 sequence_state.timelines[timeline_index] = None;
3863 timeline_index
3864 })
3865 } else {
3866 None
3867 };
3868 let message = self.messages.get(message_id);
3869 let arrow = match message.order {
3870 MessageOrder::Immediate => "-[#Crimson]>",
3871 MessageOrder::Unordered => "->",
3872 MessageOrder::Ordered(_) => "-[#Blue]>", };
3874 writeln!(
3875 stdout,
3876 "A{} {} A{} : {}",
3877 source_index,
3878 arrow,
3879 target_index,
3880 self.display_sequence_message(&message, false)
3881 )
3882 .unwrap();
3883 sequence_state.has_reactivation_message = true;
3884
3885 if let Some(timeline_index) = replaced_timeline_index {
3886 writeln!(stdout, "deactivate T{}", timeline_index).unwrap();
3887 }
3888
3889 if target_did_change_state {
3890 writeln!(stdout, "deactivate A{}", target_index).unwrap();
3891 sequence_state.has_reactivation_message = false;
3892 }
3893 }
3894
3895 SequenceStep::NewState {
3896 agent_index,
3897 state_id,
3898 is_deferring,
3899 } => {
3900 self.reactivate(&mut sequence_state, stdout);
3901 if is_deferring {
3902 writeln!(stdout, "activate A{} #MediumPurple", agent_index).unwrap();
3903 } else {
3904 writeln!(stdout, "activate A{} #CadetBlue", agent_index).unwrap();
3905 }
3906 let agent_type = &self.agent_types[agent_index];
3907 let agent_state = agent_type.display_state(state_id);
3908 writeln!(stdout, "rnote over A{} : {}", agent_index, agent_state).unwrap();
3909 sequence_state.has_reactivation_message = false;
3910 }
3911
3912 SequenceStep::NewStates {
3913 first_agent_index,
3914 first_state_id,
3915 first_is_deferring,
3916 second_agent_index,
3917 second_state_id,
3918 second_is_deferring,
3919 } => {
3920 self.reactivate(&mut sequence_state, stdout);
3921
3922 if first_is_deferring {
3923 writeln!(stdout, "activate A{} #MediumPurple", first_agent_index).unwrap();
3925 } else {
3927 writeln!(stdout, "activate A{} #CadetBlue", first_agent_index).unwrap();
3928 }
3929
3930 if second_is_deferring {
3931 writeln!(stdout, "activate A{} #MediumPurple", second_agent_index).unwrap();
3932 } else {
3933 writeln!(stdout, "activate A{} #CadetBlue", second_agent_index).unwrap();
3934 }
3935
3936 let first_agent_type = &self.agent_types[first_agent_index];
3937 let first_agent_state = first_agent_type.display_state(first_state_id);
3938 writeln!(
3939 stdout,
3940 "rnote over A{} : {}",
3941 first_agent_index, first_agent_state
3942 )
3943 .unwrap();
3944
3945 let second_agent_type = &self.agent_types[second_agent_index];
3946 let second_agent_state = second_agent_type.display_state(second_state_id);
3947 writeln!(
3948 stdout,
3949 "/ rnote over A{} : {}",
3950 second_agent_index, second_agent_state
3951 )
3952 .unwrap();
3953 }
3954 }
3955 }
3956
3957 fn reactivate(
3958 &self,
3959 mut sequence_state: &mut <Self as ModelTypes>::SequenceState,
3960 stdout: &mut dyn Write,
3961 ) {
3962 if !sequence_state.has_reactivation_message {
3963 writeln!(stdout, "autonumber stop").unwrap();
3964 writeln!(stdout, "[<[#White]-- A0").unwrap();
3965 writeln!(stdout, "autonumber resume").unwrap();
3966 sequence_state.has_reactivation_message = true;
3967 }
3968 }
3969
3970 fn print_sequence_final(
3971 &self,
3972 mut sequence_state: &mut <Self as ModelTypes>::SequenceState,
3973 stdout: &mut dyn Write,
3974 ) {
3975 sequence_state.has_reactivation_message = false;
3976 self.reactivate(&mut sequence_state, stdout);
3977 for agent_index in 0..self.agents_count() {
3978 writeln!(stdout, "deactivate A{}", agent_index).unwrap();
3979 }
3980 for (timeline_index, message_id) in sequence_state.timelines.iter().enumerate() {
3981 if message_id.is_some() {
3982 writeln!(stdout, "deactivate T{}", timeline_index).unwrap(); }
3984 }
3985 }
3986
3987 fn collect_agent_state_transitions(
3988 &self,
3989 condense: &Condense,
3990 agent_index: usize,
3991 ) -> <Self as ModelTypes>::AgentStateTransitions {
3992 let mut state_transitions = <Self as ModelTypes>::AgentStateTransitions::default();
3993 self.outgoings
3994 .iter()
3995 .take(self.configurations.len())
3996 .enumerate()
3997 .for_each(|(from_configuration_id, outgoings)| {
3998 if self.print_progress(from_configuration_id) {
3999 eprintln!(
4000 "collected {} out of {} configurations ({}%)",
4001 from_configuration_id,
4002 self.configurations.len(),
4003 (100 * from_configuration_id) / self.configurations.len(),
4004 );
4005 }
4006 let from_configuration = self
4007 .configurations
4008 .get(ConfigurationId::from_usize(from_configuration_id));
4009 outgoings.iter().for_each(|outgoing| {
4010 let to_configuration = self.configurations.get(outgoing.to_configuration_id);
4011 self.collect_agent_state_transition(
4012 condense,
4013 agent_index,
4014 &from_configuration,
4015 &to_configuration,
4016 outgoing.delivered_message_id,
4017 &mut state_transitions,
4018 );
4019 });
4020 });
4021 state_transitions
4022 }
4023
4024 fn collect_agent_state_transition(
4025 &self,
4026 condense: &Condense,
4027 agent_index: usize,
4028 from_configuration: &<Self as MetaModel>::Configuration,
4029 to_configuration: &<Self as MetaModel>::Configuration,
4030 mut delivered_message_id: MessageId,
4031 state_transitions: &mut <Self as ModelTypes>::AgentStateTransitions,
4032 ) {
4033 let agent_type = &self.agent_types[agent_index];
4034 let agent_instance = self.agent_instance(agent_index);
4035
4036 let mut context = AgentStateTransitionContext {
4037 from_state_id: from_configuration.state_ids[agent_index],
4038 from_is_deferring: agent_type
4039 .state_is_deferring(agent_instance, &from_configuration.state_ids),
4040 to_state_id: to_configuration.state_ids[agent_index],
4041 to_is_deferring: agent_type
4042 .state_is_deferring(agent_instance, &to_configuration.state_ids),
4043 };
4044
4045 if condense.names_only {
4046 context.from_state_id = agent_type.terse_id(context.from_state_id);
4047 context.to_state_id = agent_type.terse_id(context.to_state_id);
4048 }
4049
4050 let mut sent_message_ids: Vec<MessageId> = vec![];
4051
4052 to_configuration
4053 .message_ids
4054 .iter()
4055 .take_while(|to_message_id| to_message_id.is_valid())
4056 .map(|to_message_id| (to_message_id, self.messages.get(*to_message_id)))
4057 .filter(|(_, to_message)| to_message.source_index == agent_index)
4058 .for_each(|(to_message_id, _)| {
4059 if !self.to_message_kept_in_transition(
4060 *to_message_id,
4061 &from_configuration,
4062 delivered_message_id,
4063 ) {
4064 let message_id = self.terse_of_message_id[to_message_id.to_usize()];
4065 sent_message_ids.push(message_id);
4066 sent_message_ids.sort();
4067 }
4068 });
4069
4070 let delivered_message = self.messages.get(delivered_message_id);
4071 let is_delivered_to_us = delivered_message.target_index == agent_index;
4072 if !is_delivered_to_us
4073 && context.from_state_id == context.to_state_id
4074 && sent_message_ids.is_empty()
4075 {
4076 return;
4077 }
4078
4079 state_transitions
4080 .entry(context)
4081 .or_insert_with(HashMap::new);
4082
4083 let state_delivered_message_ids: &mut HashMap<Vec<MessageId>, Vec<MessageId>> =
4084 state_transitions.get_mut(&context).unwrap();
4085
4086 state_delivered_message_ids
4087 .entry(sent_message_ids.to_vec())
4088 .or_insert_with(Vec::new);
4089
4090 let delivered_message_ids: &mut Vec<MessageId> = state_delivered_message_ids
4091 .get_mut(&sent_message_ids.to_vec())
4092 .unwrap();
4093
4094 delivered_message_id = self.terse_of_message_id[delivered_message_id.to_usize()];
4095
4096 if !delivered_message_ids
4097 .iter()
4098 .any(|message_id| *message_id == delivered_message_id)
4099 {
4100 delivered_message_ids.push(delivered_message_id);
4101 delivered_message_ids.sort();
4102 }
4103 }
4104
4105 fn to_message_kept_in_transition(
4106 &self,
4107 to_message_id: MessageId,
4108 from_configuration: &<Self as MetaModel>::Configuration,
4109 delivered_message_id: MessageId,
4110 ) -> bool {
4111 if self.message_exists_in_configuration(
4112 to_message_id,
4113 from_configuration,
4114 Some(delivered_message_id),
4115 ) {
4116 return true;
4117 }
4118
4119 match self.incr_message_id(to_message_id) {
4120 None => false,
4121 Some(incr_message_id) => self.message_exists_in_configuration(
4122 incr_message_id,
4123 from_configuration,
4124 Some(delivered_message_id),
4125 ),
4126 }
4127 }
4128
4129 fn message_exists_in_configuration(
4130 &self,
4131 message_id: MessageId,
4132 configuration: &<Self as MetaModel>::Configuration,
4133 exclude_message_id: Option<MessageId>,
4134 ) -> bool {
4135 configuration
4136 .message_ids
4137 .iter()
4138 .take_while(|configuration_message_id| configuration_message_id.is_valid())
4139 .filter(|configuration_message_id| {
4140 Some(**configuration_message_id) != exclude_message_id
4141 })
4142 .any(|configuration_message_id| *configuration_message_id == message_id)
4143 }
4144}