total_space/
models.rs

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
22// BEGIN MAYBE TESTED
23
24thread_local! {
25    /// The mast of configurations that can reach back to the initial state.
26    static REACHABLE_CONFIGURATIONS_MASK: RefCell<Vec<bool>> = RefCell::new(vec![]);
27
28    /// The error configuration.
29    static ERROR_CONFIGURATION_ID: RefCell<usize> = RefCell::new(usize::max_value());
30}
31
32/// A transition from a given configuration.
33#[derive(Copy, Clone, Debug)]
34pub(crate) struct Outgoing<MessageId: IndexLike, ConfigurationId: IndexLike> {
35    /// The identifier of the target configuration.
36    to_configuration_id: ConfigurationId,
37
38    /// The identifier of the message that was delivered to its target agent to reach the target
39    /// configuration.
40    delivered_message_id: MessageId,
41}
42
43/// A transition to a given configuration.
44#[derive(Copy, Clone, Debug)]
45pub(crate) struct Incoming<MessageId: IndexLike, ConfigurationId: IndexLike> {
46    /// The identifier of the source configuration.
47    from_configuration_id: ConfigurationId,
48
49    /// The identifier of the message that was delivered to its target agent to reach the target
50    /// configuration.
51    delivered_message_id: MessageId,
52}
53
54// END MAYBE TESTED
55
56/// A function to identify specific configurations.
57pub(crate) enum ConditionFunction<Model: MetaModel> {
58    /// Using just the configuration identifier.
59    ById(fn(&Model, Model::ConfigurationId) -> bool),
60
61    /// Using the configuration itself.
62    ByConfig(fn(&Model, &Model::Configuration) -> bool),
63}
64
65impl<Model: MetaModel> Clone for ConditionFunction<Model> {
66    /// Clone this (somehow can't be derived or declared as implemented).
67    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
75/// A path step (possibly negated named condition).
76pub(crate) struct PathStep<Model: MetaModel> {
77    /// The condition function.
78    function: ConditionFunction<Model>,
79
80    /// Whether to negate the condition.
81    is_negated: bool,
82
83    /// The name of the step.
84    name: String,
85}
86
87impl<Model: MetaModel> PathStep<Model> {
88    /// Clone this (can't be derived due to ConditionFunction).
89    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/// A transition between configurations along a path.
99#[derive(Debug)]
100pub(crate) struct PathTransition<
101    MessageId: IndexLike,
102    ConfigurationId: IndexLike,
103    const MAX_MESSAGES: usize,
104> {
105    /// The source configuration identifier.
106    pub(crate) from_configuration_id: ConfigurationId,
107
108    /// The identifier of the delivered message, if any.
109    delivered_message_id: MessageId,
110
111    /// The agent that received the message.
112    agent_index: usize,
113
114    /// The target configuration identifier.
115    pub(crate) to_configuration_id: ConfigurationId,
116
117    /// The name of the condition the target configuration satisfies.
118    to_condition_name: Option<String>,
119}
120
121/// Identify related set of transition between agent states in the diagram.
122#[derive(PartialEq, Eq, PartialOrd, Ord, Hash, Copy, Clone, Debug)]
123pub(crate) struct AgentStateTransitionContext<StateId: IndexLike> {
124    /// The source configuration identifier.
125    from_state_id: StateId,
126
127    /// Whether the agent starting state was deferring.
128    from_is_deferring: bool,
129
130    /// The target configuration identifier.
131    to_state_id: StateId,
132
133    /// Whether the agent end state was deferring.
134    to_is_deferring: bool,
135}
136
137// BEGIN MAYBE TESTED
138
139/// The context for processing event handling by an agent.
140#[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    /// The index of the message of the source configuration that was delivered to its target agent
151    /// to reach the target configuration.
152    delivered_message_index: MessageIndex,
153
154    /// The identifier of the message that the agent received, or `None` if the agent received an
155    /// activity event.
156    delivered_message_id: MessageId,
157
158    /// Whether the delivered message was an immediate message.
159    is_immediate: bool,
160
161    /// The index of the agent that reacted to the event.
162    agent_index: usize,
163
164    /// The type of the agent that reacted to the event.
165    agent_type: Rc<dyn AgentType<StateId, Payload>>,
166
167    /// The index of the source agent in its type.
168    agent_instance: usize,
169
170    /// The identifier of the state of the agent when handling the event.
171    agent_from_state_id: StateId,
172
173    /// The incoming transition into the new configuration to be generated.
174    incoming: Incoming<MessageId, ConfigurationId>,
175
176    /// The configuration when delivering the event.
177    from_configuration: Configuration<StateId, MessageId, InvalidId, MAX_AGENTS, MAX_MESSAGES>,
178
179    /// Incrementally updated to become the target configuration.
180    to_configuration: Configuration<StateId, MessageId, InvalidId, MAX_AGENTS, MAX_MESSAGES>,
181}
182
183// END MAYBE TESTED
184
185/// A complete model.
186pub 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    /// The type of each agent.
196    agent_types: Vec<<Self as MetaModel>::AgentTypeRc>,
197
198    /// The label of each agent.
199    agent_labels: Vec<Rc<String>>,
200
201    /// The first index of the same type of each agent.
202    first_indices: Vec<usize>,
203
204    /// Validation functions for the configuration.
205    validator: Option<<Self as MetaModel>::Validator>,
206
207    /// Memoization of the configurations.
208    configurations: Memoize<<Self as MetaModel>::Configuration, ConfigurationId>,
209
210    /// The total number of transitions.
211    transitions_count: usize,
212
213    /// Memoization of the in-flight messages.
214    messages: Memoize<Message<Payload>, MessageId>,
215
216    /// The label (display) of each message (to speed printing).
217    label_of_message: Vec<String>,
218
219    /// Map the full message identifier to the terse message identifier.
220    terse_of_message_id: Vec<MessageId>,
221
222    /// Map the terse message identifier to the full message identifier.
223    message_of_terse_id: Vec<MessageId>,
224
225    /// Map ordered message identifiers to their smaller order.
226    decr_order_messages: HashMap<MessageId, MessageId>,
227
228    /// Map ordered message identifiers to their larger order.
229    incr_order_messages: HashMap<MessageId, MessageId>,
230
231    /// Memoization of the invalid conditions.
232    invalids: Memoize<<Self as MetaModel>::Invalid, InvalidId>,
233
234    /// The label (display) of each invalid condition (to speed printing).
235    label_of_invalid: Vec<String>,
236
237    /// For each configuration, which configuration is reachable from it.
238    outgoings: Vec<Vec<<Self as ModelTypes>::Outgoing>>,
239
240    /// For each configuration, which configuration can reach it.
241    incomings: Vec<Vec<<Self as ModelTypes>::Incoming>>,
242
243    /// The maximal message string size we have seen so far.
244    max_message_string_size: RefCell<usize>,
245
246    /// The maximal invalid condition string size we have seen so far.
247    max_invalid_string_size: RefCell<usize>,
248
249    /// The maximal configuration string size we have seen so far.
250    max_configuration_string_size: RefCell<usize>,
251
252    /// Whether to print each new configuration as we reach it.
253    pub(crate) print_progress_every: usize,
254
255    /// Whether we'll be testing if the initial configuration is reachable from every configuration.
256    pub(crate) ensure_init_is_reachable: bool,
257
258    /// Whether to allow for invalid configurations.
259    pub(crate) allow_invalid_configurations: bool,
260
261    /// A step that, if reached, we can abort the computation.
262    early_abort_step: Option<PathStep<Self>>,
263
264    /// Whether we have reached the goal step and need to abort the computation.
265    early_abort: bool,
266
267    /// Named conditions on a configuration.
268    conditions: HashMap<String, (ConditionFunction<Self>, &'static str)>,
269
270    /// Mask of configurations that can reach back to the initial state.
271    reachable_configurations_mask: Vec<bool>,
272
273    /// Count of configurations that can reach back to the initial state.
274    reachable_configurations_count: usize,
275
276    /// The configurations we need to process.
277    pending_configuration_ids: VecDeque<ConfigurationId>,
278}
279
280/// Allow querying the model's meta-parameters for public types.
281pub trait MetaModel {
282    /// The type of state identifiers.
283    type StateId;
284
285    /// The type of message identifiers.
286    type MessageId;
287
288    /// The type of invalid condition identifiers.
289    type InvalidId;
290
291    /// The type of configuration identifiers.
292    type ConfigurationId;
293
294    /// The type of message payloads.
295    type Payload;
296
297    /// The maximal number of agents.
298    const MAX_AGENTS: usize;
299
300    /// The maximal number of in-flight messages.
301    const MAX_MESSAGES: usize;
302
303    /// The type of  boxed agent type.
304    type AgentTypeRc;
305
306    /// The type of in-flight messages.
307    type Message;
308
309    /// The type of a event handling by an agent.
310    type Reaction;
311
312    /// The type of an action from an agent.
313    type Action;
314
315    /// The type of an emitted messages.
316    type Emit;
317
318    /// The type of invalid conditions.
319    type Invalid;
320
321    /// The type of the included configurations.
322    type Configuration;
323
324    /// The type of a hash table entry for the configurations (should be cache-friendly).
325    type ConfigurationHashEntry;
326
327    /// The type of a configuration validation function.
328    type Validator;
329
330    /// A condition on model configurations.
331    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
372/// Allow querying the model's meta-parameters for private types.
373pub(crate) trait ModelTypes: MetaModel {
374    /// The type of the incoming transitions.
375    type Incoming;
376
377    /// The type of the outgoing transitions.
378    type Outgoing;
379
380    /// The context for processing event handling by an agent.
381    type Context;
382
383    /// A path step (possibly negated named condition).
384    type PathStep;
385
386    /// A transition along a path between configurations.
387    type SequenceStep;
388
389    /// A transition along a path between configurations.
390    type PathTransition;
391
392    /// Identify a related set of transitions between agent states in the diagram.
393    type AgentStateTransitionContext;
394
395    /// The collection of all state transitions in the states diagram with the sent messages.
396    type AgentStateTransitions;
397
398    /// The sent messages indexed by the delivered messages for a transitions between two agent
399    /// states.
400    type AgentStateSentByDelivered;
401
402    /// The state of a sequence diagram.
403    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
431// Model creation:
432
433impl<
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    /// Create a new model, without computing anything yet, with a validation function.
444    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    /// Create a new model, without a validation function.
453    ///
454    /// This allows querying the model for the `agent_index` of all the agents to use the results as
455    /// a target for messages.
456    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() // NOT TESTED
470        );
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() // NOT TESTED
555        );
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
654/// Parse the model size parameter.
655pub 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
664// Conditions:
665
666impl<
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    // BEGIN NOT TESTED
681    fn is_valid_condition(
682        _model: &Self,
683        configuration: &<Self as MetaModel>::Configuration,
684    ) -> bool {
685        configuration.invalid_id.is_valid()
686    }
687    // END NOT TESTED
688
689    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    // BEGIN NOT TESTED
714    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    // END NOT TESTED
728
729    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
742// Model accessors:
743
744impl<
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    /// Add a named condition for defining paths through the configuration space.
755    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    /// Return the total number of agents.
768    pub fn agents_count(&self) -> usize {
769        self.agent_labels.len()
770    }
771
772    /// Return the index of the agent with the specified label.
773    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    /// Return the index of the agent instance within its type.
780    pub fn agent_instance(&self, agent_index: usize) -> usize {
781        agent_index - self.first_indices[agent_index]
782    }
783
784    /// Return the label (short name) of an agent.
785    pub fn agent_label(&self, agent_index: usize) -> &str {
786        &self.agent_labels[agent_index]
787    }
788
789    /// Return whether all the reachable configurations are valid.
790    pub fn is_valid(&self) -> bool {
791        self.invalids.is_empty()
792    }
793
794    /// Access a configuration by its identifier.
795    pub fn get_configuration(
796        &self,
797        configuration_id: ConfigurationId,
798    ) -> <Self as MetaModel>::Configuration {
799        self.configurations.get(configuration_id)
800    }
801
802    /// Access a message by its identifier.
803    pub fn get_message(&self, message_id: MessageId) -> <Self as MetaModel>::Message {
804        self.messages.get(message_id)
805    }
806}
807
808// Model computation:
809
810impl<
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    /// Compute all the configurations of the model.
821    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            // BEGIN NOT TESTED
850            let configuration_label = self.display_configuration(&context.to_configuration);
851            self.error(
852                &context,
853                &format!("reached an invalid configuration:{}", configuration_label),
854            );
855            // END NOT TESTED
856        }
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 // MAYBE TESTED
1068        {
1069            Reaction::Unexpected => self.error(&context, "unexpected message"), // MAYBE TESTED
1070            Reaction::Defer => self.defer_message(context),
1071            Reaction::Ignore => self.ignore_message(context),
1072            Reaction::Do1(action) => self.perform_action(context, action),
1073
1074            // BEGIN NOT TESTED
1075            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            // END NOT TESTED
1086        }
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), // NOT TESTED
1101
1102            Action::Change(agent_to_state_id) => {
1103                if *agent_to_state_id == context.agent_from_state_id {
1104                    self.ignore_message(context); // NOT TESTED
1105                } 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            // BEGIN NOT TESTED
1123            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            // END NOT TESTED
1136            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); // NOT TESTED
1151                } 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            // BEGIN NOT TESTED
1174            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            // END NOT TESTED
1178        }
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            // BEGIN NOT TESTED
1257            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            } // END NOT TESTED
1274        }
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 // MAYBE TESTED
1313            {
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 // NOT TESTED
1327                    }
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                    // BEGIN NOT TESTED
1372                    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                    // END NOT TESTED
1385                } 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                // BEGIN NOT TESTED
1402                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                // END NOT TESTED
1411            }
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                // BEGIN NOT TESTED
1508                let message_label = self.display_message(&message);
1509                self.error(
1510                    context,
1511                    &format!("sending a duplicate message {}", message_label),
1512                );
1513                // END NOT TESTED
1514            }
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            // BEGIN NOT TESTED
1530            self.error(
1531                &context,
1532                &format!(
1533                    "the agent {} is deferring an activity",
1534                    self.agent_labels[context.agent_index]
1535                ),
1536            );
1537            // END NOT TESTED
1538        } else {
1539            if !context.agent_type.state_is_deferring(
1540                context.agent_instance,
1541                &context.from_configuration.state_ids,
1542            ) {
1543                // BEGIN NOT TESTED
1544                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                // END NOT TESTED
1552            }
1553
1554            if context.is_immediate {
1555                // BEGIN NOT TESTED
1556                self.error(
1557                    &context,
1558                    &format!(
1559                        "the agent {} is deferring an immediate message",
1560                        self.agent_labels[context.agent_index]
1561                    ),
1562                );
1563                // END NOT TESTED
1564            }
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                    // BEGIN NOT TESTED
1582                    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                    // END NOT TESTED
1595                }
1596            }
1597        }
1598
1599        if let Some(validator) = self.validator {
1600            // BEGIN NOT TESTED
1601            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            // END NOT TESTED
1607        }
1608    }
1609
1610    // BEGIN NOT TESTED
1611
1612    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    // END NOT TESTED
1622
1623    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
1654// Print output and progress:
1655
1656impl<
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            // BEGIN NOT TESTED
1670                && progress > 0
1671                && progress % self.print_progress_every == 0)
1672        // END NOT TESTED
1673    }
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
1791// Errors and related output:
1792
1793impl<
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    // BEGIN NOT TESTED
1804    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    // END NOT TESTED
1850}
1851
1852// Display data:
1853
1854impl<
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    /// Display a message by its identifier.
1865    pub fn display_message_id(&self, message_id: MessageId) -> &str {
1866        &self.label_of_message[message_id.to_usize()]
1867    }
1868
1869    /// Display a message.
1870    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    /// Display a message in the sequence diagram.
1893    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    /// Display a message.
1906    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    // BEGIN NOT TESTED
1940    /// Display an invalid condition by its identifier.
1941    fn display_invalid_id(&self, invalid_id: InvalidId) -> &str {
1942        &self.label_of_invalid[invalid_id.to_usize()]
1943    }
1944
1945    /// Display an invalid condition.
1946    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    /// Display a configuration by its identifier.
1978    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    /// Display a configuration.
1984    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    // END NOT TESTED
1995
1996    /// Display a configuration.
1997    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    /// Display a configuration.
2003    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            // BEGIN NOT TESTED
2039            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            // END NOT TESTED
2043        }
2044
2045        write!(stdout, "\n# {:016X}", hash).unwrap();
2046    }
2047}
2048
2049// INIT reachability:
2050
2051impl<
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            // BEGIN NOT TESTED
2085            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            // END NOT TESTED
2103        }
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
2141// Path computation:
2142
2143impl<
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 // MAYBE TESTED
2159        {
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        // BEGIN NOT TESTED
2213        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        // END NOT TESTED
2221    }
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                // BEGIN NOT TESTED
2232                panic!(
2233                    "the {} command requires at least two configuration conditions, got none",
2234                    subcommand_name
2235                );
2236                // END NOT TESTED
2237            })
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); // NOT TESTED
2251                }
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                &current_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
2399// Condensing diagrams:
2400
2401impl<
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
2490// States diagram:
2491
2492impl<
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                    // BEGIN NOT TESTED
2580                    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                    // END NOT TESTED
2595                }
2596
2597                if is_intersecting {
2598                    // BEGIN NOT TESTED
2599                    intersecting_delivered_message_ids.push(delivered_message_ids.to_vec());
2600                    // END NOT TESTED
2601                } 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            // BEGIN NOT TESTED
2772            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            // END NOT TESTED
2781        }
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            // BEGIN NOT TESTED
2880            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            // END NOT TESTED
2890        }
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 // NOT TESTED
2989        };
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            // BEGIN NOT TESTED
3047            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            // END NOT TESTED
3058        }
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 // NOT TESTED
3065        };
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
3091// Sequence diagrams:
3092
3093impl<
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            // BEGIN NOT TESTED
3191            eprintln!(
3192                "sequence_steps {}",
3193                format!("{:?}", sequence_steps)
3194                    .replace("}, ", "},\n")
3195                    .replace("NoStep, ", "")
3196                    .replace("[", "[\n")
3197                    .replace("]", "\n]")
3198            );
3199            // END NOT TESTED
3200        }
3201        self.move_immediates_up(sequence_steps);
3202        if trace {
3203            // BEGIN NOT TESTED
3204            eprintln!(
3205                "move_immediates_up {}",
3206                format!("{:?}", sequence_steps)
3207                    .replace("}, ", "},\n")
3208                    .replace("NoStep, ", "")
3209                    .replace("[", "[\n")
3210                    .replace("]", "\n]")
3211            );
3212            // END NOT TESTED
3213        }
3214        self.merge_message_steps(sequence_steps);
3215        if trace {
3216            // BEGIN NOT TESTED
3217            eprintln!(
3218                "merge_message_steps {}",
3219                format!("{:?}", sequence_steps)
3220                    .replace("}, ", "},\n")
3221                    .replace("NoStep, ", "")
3222                    .replace("[", "[\n")
3223                    .replace("]", "\n]")
3224            );
3225            // END NOT TESTED
3226        }
3227        self.merge_state_steps(sequence_steps);
3228        if trace {
3229            // BEGIN NOT TESTED
3230            eprintln!(
3231                "merge_state_steps {}",
3232                format!("{:?}", sequence_steps)
3233                    .replace("}, ", "},\n")
3234                    .replace("NoStep, ", "")
3235                    .replace("[", "[\n")
3236                    .replace("]", "\n]")
3237            );
3238            // END NOT TESTED
3239        }
3240        self.move_passed_down(sequence_steps);
3241        if trace {
3242            // BEGIN NOT TESTED
3243            eprintln!(
3244                "move_passed_down {}",
3245                format!("{:?}", sequence_steps)
3246                    .replace("}, ", "},\n")
3247                    .replace("NoStep, ", "")
3248                    .replace("[", "[\n")
3249                    .replace("]", "\n]")
3250            );
3251            // END NOT TESTED
3252        }
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                        // BEGIN MAYBE TESTED
3366                        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                        // END MAYBE TESTED
3392                        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; // NOT TESTED
3431                        }
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                        // BEGIN NOT TESTED
3453                        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                        // END NOT TESTED
3463                        SequenceStep::NewStates {
3464                            first_agent_index,
3465                            second_agent_index,
3466                            ..
3467                        } if first_agent_index != first_source_index
3468                            // BEGIN NOT TESTED
3469                            && 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                        // END NOT TESTED
3477                        _ => {
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            // BEGIN NOT TESTED
3536            writeln!(
3537                stdout,
3538                "== {} ==",
3539                self.display_invalid_id(last_configuration.invalid_id)
3540            )
3541            .unwrap();
3542            // END NOT TESTED
3543        }
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                    // BEGIN NOT TESTED
3571                    writeln!(stdout, "activate A{} #MediumPurple", agent_index).unwrap();
3572                    // END NOT TESTED
3573                } 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            // BEGIN NOT TESTED
3591            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            // END NOT TESTED
3596        }
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            // BEGIN NOT TESTED
3643            sequence_state.timelines[timeline_index] = Some(first_message_id);
3644            return timeline_index;
3645            // END NOT TESTED
3646        }
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()) // MAYBE TESTED
3707            .for_each(|(timeline_index, message_id)| {
3708                // BEGIN NOT TESTED
3709                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                // END NOT TESTED
3718            });
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 // MAYBE TESTED
3803                {
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]>", // NOT TESTED
3873                };
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                    // BEGIN NOT TESTED
3924                    writeln!(stdout, "activate A{} #MediumPurple", first_agent_index).unwrap();
3925                    // END NOT TESTED
3926                } 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(); // NOT TESTED
3983            }
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}