mCRL2
Loading...
Searching...
No Matches
liblts_bisim_gj.h
Go to the documentation of this file.
1// Author(s): Jan Friso Groote (with edits by David N. Jansen)
2//
3// Copyright: see the accompanying file COPYING or copy at
4// https://github.com/mCRL2org/mCRL2/blob/master/COPYING
5//
6// Distributed under the Boost Software License, Version 1.0.
7// (See accompanying file LICENSE_1_0.txt or copy at
8// http://www.boost.org/LICENSE_1_0.txt)
9
16
17#ifndef LIBLTS_BISIM_GJ_H
18#define LIBLTS_BISIM_GJ_H
19
20#include <iomanip> // for std::fixed, std::setprecision(), std::setw()
21#include <ctime> // for std::clock_t, std::clock()
22//#include "mcrl2/utilities/hash_utility.h"
28#define linked_list simple_list
29
30// If `USE_FOUR_WAY_SPLIT` is defined, then splitting a block will use a
31// four-way split function that can split under a main and a co-splitter at the
32// same time, together with creating the new bottom state block.
33#define USE_FOUR_WAY_SPLIT
34
35namespace mcrl2
36{
37namespace lts
38{
39namespace detail
40{
41
42template <class LTS_TYPE> class bisim_partitioner_gj;
43
44namespace bisimulation_gj
45{
46
47// Forward declaration.
48struct state_type_gj;
49struct block_type;
51struct transition_type;
53
54typedef std::size_t state_index;
55typedef std::size_t transition_index;
56
57
58typedef std::size_t label_index;
63
68constexpr block_type* null_block=nullptr;
72
73#ifdef USE_FOUR_WAY_SPLIT
79 (std::numeric_limits<transition_index>::max()-2)/3;
80
81 enum subblocks { ReachAlw=0,// states that can reach always all splitters
82 MissMain, // states that cannot inertly reach the main
83 // splitter (while it is not empty)
84 XcludeCo, // states that cannot inertly reach the
85 // co-splitter (while it is not empty)
86 MultiSub}; // states that can inertly reach multiple of
87 // the above subblocks
88 // The following values are used only for temporary marking
89 // and are not really associated with a subblock:
90 // Hit_Main -- states that can (non-inertly) reach the main
91 // splitter; they can be in any subblock except
92 // MissMain. Necessary for correctness.
93
97 static inline constexpr transition_index marked(enum subblocks subblock)
98 {
99 return
100 assert(ReachAlw==subblock || MissMain==subblock ||
101 XcludeCo==subblock || MultiSub==subblock),
102 marked_range*subblock+1;
103 }
104
105 constexpr transition_index marked_MultiSub=marked(MultiSub); static_assert(marked_MultiSub<std::numeric_limits<transition_index>::max());
107
109 static inline constexpr bool is_in_marked_range_of
110 (transition_index counter, enum subblocks subblock)
111 {
112 return assert(ReachAlw==subblock || MissMain==subblock || XcludeCo==subblock),
113 counter-marked(subblock)<marked_range;
114 }
115#endif
116
119template <class CONTAINER>
120static inline void clear(CONTAINER& c)
121{
122 if (c.size()>1000) { c=CONTAINER(); } else { c.clear(); }
123}
124
125// The struct below facilitates to walk through a LBC_list starting from an
126// arbitrary transition.
127typedef transition_index* BLC_list_iterator; // should not be nullptr
129typedef const transition_index* BLC_list_const_iterator; // should not be nullptr
130
133{
136 {
143 : transitions()
144 {}
147 {
149 }
151 ~iterator_or_counter() { BLC_transitions.~BLC_list_iterator(); }
153
159
160 // The default initialiser does not initialize the fields of this struct.
162 {}
163
165 : ref(),
166 start_same_saC(sssaC)
167 {}
168};
169
172{
174 : ref_state(new_ref_state)
175 {}
176
178 {}
179
181
182 bool operator==(const state_in_block_pointer& other) const
183 {
184 return ref_state==other.ref_state;
185 }
186
187 bool operator!=(const state_in_block_pointer& other) const
188 {
189 return ref_state!=other.ref_state;
190 }
191};
192
196{
197 std::size_t m_todo_indicator=0;
198 std::vector<state_in_block_pointer> m_vec;
199
200 public:
201 typedef std::vector<state_in_block_pointer>::const_iterator const_iterator;
202 #ifndef NDEBUG
203 bool find(const state_in_block_pointer s) const
204 {
205 return std::find(m_vec.begin(), m_vec.end(), s)!=m_vec.end();
206 }
207 #endif
209 { assert(!find(s));
210 m_vec.push_back(s);
211 }
212
213 std::size_t todo_is_empty() const
214 {
215 return m_vec.size()==m_todo_indicator;
216 }
217
218 // Move a state from the todo part to the definitive vector.
220 { assert(!todo_is_empty());
223 return result;
224 }
225
226 void swap_vec(std::vector<state_in_block_pointer>& other_vec)
227 {
228 m_vec.swap(other_vec);
230 }
231
232 std::size_t size() const
233 {
234 return m_vec.size();
235 }
236
237 std::size_t empty() const
238 {
239 return m_vec.empty();
240 }
241
243 {
244 return m_vec.begin();
245 }
246
248 {
249 return m_vec.end();
250 }
251
253 {
254 return m_vec.data();
255 }
256
258 {
259 return m_vec.data() + m_vec.size();
260 }
261
263 {
264 return m_vec.front();
265 }
266
267 //const state_in_block_pointer& back() const
268 //{
269 // return m_vec.back();
270 //}
271
272#ifdef USE_FOUR_WAY_SPLIT
273 void reserve(std::vector<state_in_block_pointer>::size_type new_cap)
274 {
275 m_vec.reserve(new_cap);
276 }
277
278 typedef std::vector<state_in_block_pointer>::iterator iterator;
279
281 {
282 return m_vec.begin();
283 }
284
286 {
287 return m_vec.end();
288 }
289
290 // add all elements in [begin, end) to the vector
292 {
293 m_vec.insert(m_vec.end(), begin, end);
294 }
295#endif
296
297 void clear()
298 {
301 }
302};
303
304
305
306// Below the four main data structures are listed.
309{
313 std::vector<transition>::iterator start_incoming_transitions;
329 #ifndef NDEBUG
331 template<class LTS_TYPE>
332 std::string debug_id_short(const bisim_partitioner_gj<LTS_TYPE>& partitioner) const
333 {
334 assert(partitioner.m_states.data()<=this);
335 assert(this<partitioner.m_states.data_end());
336 return std::to_string(this-partitioner.m_states.data());
337 }
338
340 template<class LTS_TYPE>
341 std::string debug_id(const bisim_partitioner_gj<LTS_TYPE>& partitioner) const
342 {
343 return "state " + debug_id_short(partitioner);
344 }
345 #endif
346 #if !defined(NDEBUG) || defined(COUNT_WORK_BALANCE)
348 #endif
349};
350
354{
356
357 // If the source block of the BLC_indicator has new bottom states,
358 // it is undefined whether the BLC_indicator should be regarded as stable or
359 // unstable. Otherwise, the BLC_indicator is regarded as stable if and only
360 // if start_marked_BLC is ==nullptr.
363
365 : start_same_BLC(start),
366 start_marked_BLC(is_stable ? nullptr : end),
367 end_same_BLC(end)
368 { assert(nullptr!=start_same_BLC); assert(nullptr!=end_same_BLC);
370 }
371
372 bool is_stable() const
373 { assert(nullptr!=start_same_BLC); assert(nullptr!=end_same_BLC);
377 return nullptr==start_marked_BLC;
378 }
379
383 {
384 if (is_stable())
385 {
386 return false;
390 }
391
393 { assert(!is_stable());
394 start_marked_BLC=nullptr;
395 }
396
398 { assert(is_stable());
400 }
401
402 bool operator==(const BLC_indicators& other) const
403 {
404 return start_same_BLC==other.start_same_BLC &&
407 }
408
409 bool operator!=(const BLC_indicators& other) const
410 {
411 return !operator==(other);
412 }
413 #ifndef NDEBUG
416 template<class LTS_TYPE>
417 std::string debug_id(const bisim_partitioner_gj<LTS_TYPE>& partitioner,
418 const block_type* from_block=nullptr) const
419 {
420 assert(partitioner.m_BLC_transitions.data()<=start_same_BLC);
424 assert(end_same_BLC<=partitioner.m_BLC_transitions.data_end());
425 std::string result("BLC set ["+std::to_string(std::distance<BLC_list_const_iterator>(&*partitioner.m_BLC_transitions.begin(), start_same_BLC))+","+std::to_string(std::distance<BLC_list_const_iterator>(&*partitioner.m_BLC_transitions.begin(), end_same_BLC))+")");
427 {
428 return "Empty "+result;
429 }
430 result += " from "+(nullptr==from_block ? partitioner.m_states[partitioner.m_aut.get_transitions()[*start_same_BLC].from()].block : from_block)->debug_id(partitioner);
431 result += " to ";
432 result += partitioner.m_states[partitioner.m_aut.get_transitions()[*start_same_BLC].to()].block->c.onstellation->debug_id(partitioner);
433 result += " containing the ";
434 if (std::distance(start_same_BLC, end_same_BLC)>1)
435 {
436 result+=std::to_string(std::distance(start_same_BLC, end_same_BLC));
437 result += " transitions ";
438 }
439 else
440 {
441 result += "transition ";
442 }
444 if (start_marked_BLC == iter)
445 {
446 result += "| ";
447 }
448 result += partitioner.m_transitions[*iter].debug_id_short(partitioner);
449 if (std::distance(start_same_BLC, end_same_BLC)>4)
450 {
451 ++iter;
452 result += start_marked_BLC == iter ? " | " : ", ";
453 result += partitioner.m_transitions[*iter].debug_id_short(partitioner);
454 result += std::next(iter) == start_marked_BLC ? " | ..."
455 : (!is_stable() && start_marked_BLC>std::next(iter) && start_marked_BLC<=end_same_BLC-3 ? ", ..|.." : ", ...");
456 iter = end_same_BLC-3;
457 }
458 while (++iter!=end_same_BLC)
459 {
460 result += start_marked_BLC == iter ? " | " : ", ";
461 result += partitioner.m_transitions[*iter].debug_id_short(partitioner);
462 }
463 if (start_marked_BLC == iter)
464 {
465 result += " |";
466 }
467 return result;
468 }
469 #endif
470 #if !defined(NDEBUG) || defined(COUNT_WORK_BALANCE)
472 #endif
473};
474
479{
480 // The position of the transition type corresponds to m_aut.get_transitions().
481 // std::size_t from, label, to are found in m_aut.get_transitions().
482 linked_list<BLC_indicators>::iterator transitions_per_block_to_constellation;
483 outgoing_transitions_it ref_outgoing_transitions; // This refers to the position of this transition in m_outgoing_transitions.
484 // During initialisation m_outgoing_transitions contains the indices of this
485 // transition. After initialisation m_outgoing_transitions refers to the corresponding
486 // entry in m_BLC_transitions, of which the field transition contains the index
487 // of this transition.
488 #ifndef NDEBUG
491 template<class LTS_TYPE>
492 std::string debug_id_short(const bisim_partitioner_gj<LTS_TYPE>& partitioner) const
493 {
494 assert(partitioner.m_transitions.data()<=this);
495 assert(this<partitioner.m_transitions.data_end());
496 const transition& t=partitioner.m_aut.get_transitions()
497 [this-partitioner.m_transitions.data()];
498 return partitioner.m_states[t.from()].debug_id_short(partitioner) + " -" +
499 pp(partitioner.m_aut.action_label(t.label())) + "-> " +
500 partitioner.m_states[t.to()].debug_id_short(partitioner);
501 }
502
505 template<class LTS_TYPE>
506 std::string debug_id(const bisim_partitioner_gj<LTS_TYPE>& partitioner) const
507 {
508 return "transition " + debug_id_short(partitioner);
509 }
510 #endif
511 #if !defined(NDEBUG) || defined(COUNT_WORK_BALANCE)
513 #endif
514};
515
529{
531 {
534
537
539 :onstellation(new_c)
540 {}
541 } c;
542
547
549 {
556
563
566 {}
568
571
572 union btc_R
573 {
582 linked_list<BLC_indicators> to_constellation; static_assert(std::is_trivially_destructible
583 <linked_list<BLC_indicators> >::value);
597 std::vector<state_in_block_pointer>* R;
598
603 : R(nullptr)
604 {}
605
613 {
614 btc_R test_should_be_empty_BLC_list=btc_R(); assert(nullptr==test_should_be_empty_BLC_list.R);
615 if constexpr (sizeof(test_should_be_empty_BLC_list.R)!=
616 sizeof(test_should_be_empty_BLC_list.to_constellation))
617 {
618 return false;
619 }
620 if (test_should_be_empty_BLC_list.to_constellation.empty() &&
621 test_should_be_empty_BLC_list.to_constellation==
622 linked_list<BLC_indicators>())
623 {
624 // no need to change `test_should_be_empty_BLC_list` from a pointer
625 // to a linked_list explicitly, as the two seem to have the same bit
626 // pattern; the destructor will work fine.
627 return true;
628 }
629 // The destructor expects a linked list:
630 new (&test_should_be_empty_BLC_list) linked_list<BLC_indicators>();
631 return false;
632 }
634
636 block_type(const block_type& other)
637 : c(other.c.onstellation),
639 sta(other.sta.rt_non_bottom_states),
640 end_states(other.end_states),
641 block(other.block),
643 {}
644
650
651#ifndef USE_FOUR_WAY_SPLIT
652 block_type(state_in_block_pointer* beginning_of_states,
653 constellation_type* new_c)
654 : c(new_c),
655 start_bottom_states(beginning_of_states),
656 sta(beginning_of_states),
657 end_states(beginning_of_states),
658 block(),
660 {}
661#endif
662
665 state_in_block_pointer* start_non_bottom,
667 constellation_type* new_c)
668 : c(new_c),
669 start_bottom_states(start_bottom),
670 sta(start_non_bottom),
671 end_states(end),
672 block(),
674 { assert(start_bottom<=start_non_bottom); assert(start_non_bottom<=end);
675 }
676 #ifndef NDEBUG
678 template<class LTS_TYPE>
679 std::string debug_id(const bisim_partitioner_gj<LTS_TYPE>& partitioner) const
680 { assert(partitioner.m_states_in_blocks.data()<=start_bottom_states);
683 assert(end_states<=partitioner.m_states_in_blocks.data_end());
684 return"block ["+std::to_string
685 (std::distance<const state_in_block_pointer*>
686 (partitioner.m_states_in_blocks.data(), start_bottom_states))+","+
687 std::to_string
688 (std::distance<const state_in_block_pointer*>
689 (partitioner.m_states_in_blocks.data(), end_states))+")";
690 }
691 #endif
692 #if !defined(NDEBUG) || defined(COUNT_WORK_BALANCE)
694 #endif
695};
696
699{
702
705
707 state_in_block_pointer* const new_end)
708 : start_const_states(new_start),
709 end_const_states(new_end)
710 {}
711 #ifndef NDEBUG
713 template<class LTS_TYPE>
714 std::string debug_id(const bisim_partitioner_gj<LTS_TYPE>& partitioner) const
715 { assert(partitioner.m_states_in_blocks.data()<=start_const_states);
717 assert(end_const_states<=partitioner.m_states_in_blocks.data_end());
718 return "constellation ["+std::to_string
719 (std::distance<const state_in_block_pointer*>
720 (partitioner.m_states_in_blocks.data(), start_const_states))+","+
721 std::to_string
722 (std::distance<const state_in_block_pointer*>
723 (partitioner.m_states_in_blocks.data(), end_const_states))+")";
724 }
725 #endif
726};
727
728} // end namespace bisimulation_gj
729
730
731/*=============================================================================
732= main class =
733=============================================================================*/
734
735
737
740template <class LTS_TYPE>
742{
743 private:
744
745 typedef std::unordered_set<state_index> set_of_states_type;
746 typedef std::unordered_set<transition_index> set_of_transitions_type;
747 #ifndef NDEBUG
748 public: // needed for the debugging functions, e.g. debug_id().
749 #endif
751 LTS_TYPE& m_aut;
752
753 // Generic data structures.
756
763 // During refining this contains the index in m_BLC_transition, of which
764 // the transition field contains the index of the transition.
770 private:
771 std::vector<block_type*> m_blocks_with_new_bottom_states;
772
773#ifndef USE_FOUR_WAY_SPLIT
777 todo_state_vector m_R, m_U;
778 std::vector<state_in_block_pointer> m_U_counter_reset_vector;
779#endif
780
782 std::vector<constellation_type*> m_non_trivial_constellations;
783
784 std::vector<linked_list<BLC_indicators>::iterator>
786
787#ifndef USE_FOUR_WAY_SPLIT
791 std::vector<std::pair<BLC_list_iterator, block_type*> >
792 m_co_splitters_to_be_checked;
793#endif
794
796 const bool m_branching;
797
804
808 static typename LTS_TYPE::labels_size_type m_aut_apply_hidden_label_map
809 (typename LTS_TYPE::labels_size_type l)
810 {
811 return l;
812 }
813
817 { assert(m_branching);
818 return m_aut.is_tau(m_aut_apply_hidden_label_map(t.label())) &&
819 (!m_preserve_divergence || t.from() != t.to());
820 }
821
824 bool is_inert_during_init(const transition& t) const
825 {
827 }
828
833 const label_index divergent_label=-2
834 /* different from null_action */) const
835 {
836 label_index result = m_aut_apply_hidden_label_map(t.label()); assert(divergent_label!=result); assert(null_action!=divergent_label);
837 if (m_preserve_divergence && ( assert(m_branching),
838 t.from() == t.to()) &&
839 m_aut.is_tau(result))
840 {
841 return divergent_label;
842 }
843 return result;
844 }
845 #ifndef NDEBUG
863 void check_transitions(const bool initialisation,
864 const bool check_temporary_complexity_counters,
865 const bool check_block_to_constellation = true) const
866 {
867 for(transition_index ti=0; ti<m_aut.num_transitions(); ++ti)
868 {
869 const BLC_list_const_iterator btc_ti=
870 m_transitions[ti].ref_outgoing_transitions->ref.BLC_transitions;
871 assert(*btc_ti==ti);
872
873 const transition& t=m_aut.get_transitions()[ti];
874 assert(&*m_states[t.to()].start_incoming_transitions<=&t);
875 if (t.to()+1!=m_aut.num_states())
876 {
877 assert(&t<=&*std::prev(m_states[t.to()+1].start_incoming_transitions));
878 }
879 else
880 {
881 assert(&t<=&m_aut.get_transitions().back());
882 }
883
884 assert(m_states[t.from()].start_outgoing_transitions<=
885 m_transitions[ti].ref_outgoing_transitions);
886 if (t.from()+1==m_aut.num_states())
887 {
888 assert(m_transitions[ti].ref_outgoing_transitions<
890 }
891 else
892 {
893 assert(m_transitions[ti].ref_outgoing_transitions<
894 m_states[t.from() + 1].start_outgoing_transitions);
895 }
896
897 assert(m_transitions[ti].
898 transitions_per_block_to_constellation->start_same_BLC<=btc_ti);
899 assert(btc_ti<m_transitions[ti].
900 transitions_per_block_to_constellation->end_same_BLC);
901
902 if (!check_block_to_constellation)
903 continue;
904
905 block_type* const b=m_states[t.from()].block;
906
907 const label_index t_label = label_or_divergence(t);
908 bool found=false;
909 for(const BLC_indicators& blc: b->block.to_constellation)
910 {
911 if (!blc.is_stable())
912 {
913 assert(blc.start_same_BLC<=blc.start_marked_BLC);
914 assert(blc.start_marked_BLC<=blc.end_same_BLC);
915 }
916 assert(blc.start_same_BLC<blc.end_same_BLC);
917 transition& first_t = m_aut.get_transitions()[*blc.start_same_BLC];
918 assert(b == m_states[first_t.from()].block);
919 if (t_label == label_or_divergence(first_t) &&
920 m_states[first_t.to()].block->c.onstellation ==
921 m_states[t.to()].block->c.onstellation)
922 {
923// if (found) { std::cerr << "Found multiple BLC sets with transitions (" << b->debug_id(*this) << " -" << m_aut.action_label(t.label()) << "-> " << m_states[t.to()].block->c.onstellation->debug_id(*this) << ")\n"; }
924 assert(!found);
925 assert(blc.start_same_BLC <= btc_ti);
926 assert(btc_ti<blc.end_same_BLC);
927 assert(&blc == &*m_transitions[ti].transitions_per_block_to_constellation);
928 found = true;
929 }
930 }
931 assert(found);
932 if (check_temporary_complexity_counters)
933 {
934 block_type* const targetb = m_states[t.to()].block;
935 const unsigned max_sourceB = check_complexity::log_n-
937 const unsigned max_targetC = check_complexity::log_n-
939 (*targetb->c.onstellation));
940 const unsigned max_targetB = check_complexity::log_n-
943 no_temporary_work(max_sourceB, max_targetC, max_targetB,
944 !initialisation &&
945 0==m_states[t.from()].no_of_outgoing_block_inert_transitions),
946 *this);
947 }
948 }
949 }
950
957 [[nodiscard]]
958 bool check_data_structures(const std::string& tag, const bool initialisation=false, const bool check_temporary_complexity_counters=true) const
959 {
960 mCRL2log(log::debug) << "Check data structures: " << tag << ".\n";
961 assert(m_states.size()==m_aut.num_states());
962 assert(m_states_in_blocks.size()==m_aut.num_states());
963 assert(m_transitions.size()==m_aut.num_transitions());
964 assert(m_outgoing_transitions.size()==m_aut.num_transitions());
965 assert(m_BLC_transitions.size()==m_aut.num_transitions());
966
967 // Check that the elements in m_states are well formed.
969 const_cast<fixed_vector<state_type_gj>&>(m_states).begin();
970 si<m_states.cend(); si++)
971 {
972 const state_type_gj& s=*si;
973
974 assert(s.counter==undefined);
977
978 // In the following line we need that si is an iterator (not a const_iterator)
979 assert(std::find(s.block->start_bottom_states,
980 s.block->end_states,
982
983 assert(s.ref_states_in_blocks->ref_state==si);
984
985 // ensure that in the incoming transitions we first have the transitions
986 // with label tau, and then the other transitions:
987 bool maybe_tau=true;
988 const std::vector<transition>::const_iterator end_it1=
989 std::next(si)>=m_states.end() ? m_aut.get_transitions().end()
990 : std::next(si)->start_incoming_transitions;
991 for (std::vector<transition>::const_iterator
992 it=s.start_incoming_transitions; it!=end_it1; ++it)
993 {
994 const transition& t=*it;
996 {
997 assert(maybe_tau);
998 }
999 else
1000 {
1001 maybe_tau=false;
1002 }
1003 // potentially we might test that the transitions are grouped per label
1004 }
1005
1006 // Check that for each state the outgoing transitions satisfy the
1007 // following invariant: First there are (originally) inert transitions
1008 // (inert transitions may be separated over multiple constellations, so
1009 // we cannot require that the inert transitions come before other
1010 // tau-transitions). Then there are other transitions sorted per label
1011 // and constellation.
1012 std::unordered_set<std::pair<label_index, const constellation_type*> >
1013 constellations_seen;
1014
1015 maybe_tau=true;
1016 // The construction below is to enable translation on Windows.
1017 const outgoing_transitions_const_it end_it2=
1018 std::next(si)>=m_states.end() ? m_outgoing_transitions.cend()
1019 : std::next(si)->start_outgoing_transitions;
1021 it!=end_it2; ++it)
1022 {
1023 const transition& t=m_aut.get_transitions()[!initialisation
1024 ? *it->ref.BLC_transitions : it->ref.transitions];
1025// if (t.from() != si) { std::cerr << m_transitions[*it->ref.BLC_transitions].debug_id(*this) << " is an outgoing transition of state " << si << "!\n"; }
1026 assert(m_states.cbegin()+t.from()==si);
1027 assert(m_transitions[!initialisation ? *it->ref.BLC_transitions
1028 : it->ref.transitions].ref_outgoing_transitions==it);
1029 assert((it->start_same_saC>it &&
1030 it->start_same_saC<m_outgoing_transitions.end() &&
1031 ((it+1)->start_same_saC==it->start_same_saC ||
1032 (it+1)->start_same_saC<=it)) ||
1033 (it->start_same_saC<=it &&
1034 (it+1==m_outgoing_transitions.end() ||
1035 (it+1)->start_same_saC>it)));
1036// if (it->start_same_saC < it->start_same_saC->start_same_saC) { std::cerr << "Now checking transitions " << m_transitions[*it->start_same_saC->ref.BLC_transitions].debug_id_short(*this) << " ... " << m_transitions[*it->start_same_saC->start_same_saC->ref.BLC_transitions].debug_id_short(*this) << '\n'; }
1037 const label_index t_label = label_or_divergence(t);
1038 // The following for loop is only executed if it is the last transition in the saC-slice.
1039 for(outgoing_transitions_const_it itt=it->start_same_saC;
1040 itt<it->start_same_saC->start_same_saC; ++itt)
1041 {
1042 const transition& t1=m_aut.get_transitions()[!initialisation
1043 ? *itt->ref.BLC_transitions : itt->ref.transitions];
1044// if (t1.from()!=si) { assert(!initialisation); std::cerr << m_transitions[*itt->ref.BLC_transitions].debug_id(*this) << " does not start in state " << si << '\n'; }
1045 assert(m_states.cbegin()+t1.from()==si);
1046 assert(label_or_divergence(t1) == t_label);
1047 assert(m_states[t.to()].block->c.onstellation==
1048 m_states[t1.to()].block->c.onstellation);
1049 }
1050
1052 // Check that if the target constellation, if not new, is equal to the
1053 // target constellation of the previous outgoing transition.
1054 const constellation_type* t_to_constellation=
1055 m_states[t.to()].block->c.onstellation;
1056 if (constellations_seen.count(std::pair(label,t_to_constellation))>0)
1057 {
1058 assert(it!=s.start_outgoing_transitions);
1059 const transition& old_t=m_aut.get_transitions()[!initialisation
1060 ? *std::prev(it)->ref.BLC_transitions
1061 : std::prev(it)->ref.transitions];
1062 assert(label_or_divergence(old_t)==label);
1063 assert(t_to_constellation==
1064 m_states[old_t.to()].block->c.onstellation);
1065 }
1066 else
1067 {
1068 if (m_branching && m_aut.is_tau(label))
1069 {
1070//if (!maybe_tau) { std::cerr << "maybe_tau==false for " << m_transitions[!initialisation ? *it->ref.BLC_transitions : it->ref.transitions].debug_id(*this) << '\n'; }
1071 assert(maybe_tau);
1072 }
1073 else
1074 {
1075 maybe_tau=false;
1076 }
1077 constellations_seen.emplace(label,t_to_constellation);
1078 }
1079 }
1080 }
1081 // Check that the elements in m_transitions are well formed.
1082 if (!initialisation)
1083 {
1084 check_transitions(initialisation, check_temporary_complexity_counters);
1085 }
1086 // Check that the elements in m_blocks are well formed.
1087 {
1088 set_of_transitions_type all_transitions;
1089 transition_index actual_no_of_non_constellation_inert_BLC_sets=0;
1090 for (const state_in_block_pointer* si=m_states_in_blocks.data();
1091 m_states_in_blocks.data_end()!=si; si=si->ref_state->block->end_states)
1092 {
1093 const block_type& b=*si->ref_state->block;
1094 const constellation_type& c=*b.c.onstellation;
1095 assert(m_states_in_blocks.data()<=c.start_const_states);
1098 assert(b.sta.rt_non_bottom_states<=b.end_states);
1099 assert(b.end_states<=c.end_const_states);
1100 assert(c.end_const_states<=m_states_in_blocks.data_end());
1101
1102 unsigned char const max_B=check_complexity::log_n-
1104 unsigned char const max_C=check_complexity::log_n-check_complexity::
1106 for (const state_in_block_pointer*
1108 {
1109 assert(is->ref_state->block==&b);
1110 assert(is->ref_state->no_of_outgoing_block_inert_transitions==0);
1111 if (check_temporary_complexity_counters)
1112 {
1113 // During initialisation, new bottom state counters must remain 0
1114 mCRL2complexity(is->ref_state, no_temporary_work(max_B,
1115 !initialisation), *this);
1116 }
1117 }
1118 for (const state_in_block_pointer*
1119 is=b.sta.rt_non_bottom_states; is!=b.end_states; ++is)
1120 {
1121 assert(is->ref_state->block==&b);
1122 assert(is->ref_state->no_of_outgoing_block_inert_transitions>0);
1123 // Because there cannot be new bottom states among non-bottom states,
1124 // we can always check the temporary work of non-bottom states:
1125 mCRL2complexity(is->ref_state,no_temporary_work(max_B,false),*this);
1126 }
1127 // Because a block has no temporary or new-bottom-state-related
1128 // counters, we can always check its temporary work:
1129 mCRL2complexity(&b, no_temporary_work(max_C, max_B), *this);
1130
1131 if (!initialisation)
1132 {
1133 assert(b.block.to_constellation.check_linked_list());
1134 for (linked_list<BLC_indicators>::const_iterator
1135 ind=b.block.to_constellation.begin();
1136 ind!=b.block.to_constellation.end(); ++ind)
1137 {
1138 assert(ind->start_same_BLC<ind->end_same_BLC);
1139 const transition& first_transition=
1140 m_aut.get_transitions()[*(ind->start_same_BLC)];
1141 const label_index first_transition_label=
1142 label_or_divergence(first_transition);
1143 if(!is_inert_during_init(first_transition) ||
1144 m_states[first_transition.from()].block->c.onstellation!=
1145 m_states[first_transition.to()].block->c.onstellation)
1146 {
1147 ++actual_no_of_non_constellation_inert_BLC_sets;
1148 }
1149 for(BLC_list_const_iterator i=ind->start_same_BLC;
1150 i<ind->end_same_BLC; ++i)
1151 {
1152 const transition& t=m_aut.get_transitions()[*i];
1153 assert(m_transitions[*i].transitions_per_block_to_constellation==
1154 ind);
1155 all_transitions.emplace(*i);
1156 assert(m_states[t.from()].block==&b);
1157 assert(m_states[t.to()].block->c.onstellation==
1158 m_states[first_transition.to()].block->c.onstellation);
1159 assert(label_or_divergence(t)==first_transition_label);
1160 if (is_inert_during_init(t) && b.c.onstellation==
1161 m_states[t.to()].block->c.onstellation)
1162 {
1163 // The inert transitions should be in the first element of
1164 // `block.to_constellation`:
1165 assert(b.block.to_constellation.begin()==ind);
1166 }
1167 }
1168 if (check_temporary_complexity_counters)
1169 {
1170 mCRL2complexity(ind, no_temporary_work(max_C,
1173 [first_transition.to()].block->c.onstellation))), *this);
1174 }
1175 }
1176 }
1177 }
1178 if (!initialisation) {
1179 assert(all_transitions.size()==m_aut.num_transitions());
1180 assert(actual_no_of_non_constellation_inert_BLC_sets==
1182 }
1183 // destruct `all_transitions` here
1184 }
1185
1186 // TODO: Check that the elements in m_constellations are well formed.
1187
1188 // Check that the states in m_states_in_blocks refer to with ref_states_in_block to the right position.
1189 // and that a state is correctly designated as a (non-)bottom state.
1190 for (const state_in_block_pointer*
1191 si=m_states_in_blocks.data(); si<m_states_in_blocks.data_end(); ++si)
1192 {
1193 assert(si==si->ref_state->ref_states_in_blocks);
1194 }
1195
1196 // Check that the blocks in m_blocks_with_new_bottom_states are bottom states.
1198 {
1199 assert(bi->contains_new_bottom_states);
1200 }
1201
1202 // Check that the non-trivial constellations are non trivial.
1204 {
1205 // There are at least two blocks in a non-trivial constellation.
1206 const block_type* const first_bi=ci->start_const_states->ref_state->block;
1207 const block_type* const last_bi=std::prev(ci->end_const_states)->ref_state->block;
1208 assert(first_bi != last_bi);
1209 }
1210 return true;
1211 }
1212
1239 [[nodiscard]]
1240 bool check_stability(const std::string& tag,
1241 const std::vector<std::pair<BLC_list_iterator, BLC_list_iterator> >*
1242 calM=nullptr,
1243 const std::pair<BLC_list_iterator,BLC_list_iterator>* calM_elt=nullptr,
1244 const constellation_type* const old_constellation=null_constellation,
1245 const constellation_type* const new_constellation=null_constellation)
1246 const
1247 {
1248 assert((old_constellation==null_constellation &&
1249 new_constellation==null_constellation ) ||
1250 (old_constellation!=null_constellation &&
1251 new_constellation!=null_constellation &&
1252 old_constellation!=new_constellation ));
1253 mCRL2log(log::debug) << "Check stability: " << tag << ".\n";
1254 for (const state_in_block_pointer* si=m_states_in_blocks.data();
1255 m_states_in_blocks.data_end()!=si; si=si->ref_state->block->end_states)
1256 {
1257 const block_type& b=*si->ref_state->block;
1258 bool previous_stable=true;
1259 for (linked_list<BLC_indicators>::const_iterator
1260 ind=b.block.to_constellation.begin();
1261 ind!=b.block.to_constellation.end(); ++ind)
1262 {
1263 set_of_states_type all_source_bottom_states;
1264
1265 assert(ind->start_same_BLC<ind->end_same_BLC);
1266 const transition&first_t=m_aut.get_transitions()[*ind->start_same_BLC];
1267 const label_index first_t_label=label_or_divergence(first_t);
1268 const bool all_transitions_in_BLC_are_inert =
1269 is_inert_during_init(first_t) && b.c.onstellation==
1270 m_states[first_t.to()].block->c.onstellation;
1271 assert(!all_transitions_in_BLC_are_inert ||
1272 b.block.to_constellation.begin()==ind);
1273 for (BLC_list_const_iterator i=ind->start_same_BLC;
1274 i<ind->end_same_BLC; ++i)
1275 {
1276 assert(m_BLC_transitions.data()<=i);
1277 assert(i<m_BLC_transitions.data_end());
1278 const transition& t=m_aut.get_transitions()[*i];
1279// if (m_states[t.from()].block != bi) { std::cerr << m_transitions[*ind->start_same_BLC].debug_id(*this) << " should start in block " << bi << '\n'; }
1280 assert(m_states[t.from()].block == &b);
1281 assert(label_or_divergence(t) == first_t_label);
1282 assert(m_states[t.to()].block->c.onstellation==
1283 m_states[first_t.to()].block->c.onstellation);
1284 if (is_inert_during_init(t) && b.c.onstellation==
1285 m_states[t.to()].block->c.onstellation)
1286 {
1287 assert(all_transitions_in_BLC_are_inert);
1288 }
1289 else
1290 {
1291 // This is a constellation-non-inert transition.
1292 assert(!all_transitions_in_BLC_are_inert);
1293 if (0 == m_states[t.from()].no_of_outgoing_block_inert_transitions)
1294 {
1295 assert(b.start_bottom_states<=
1296 m_states[t.from()].ref_states_in_blocks);
1297 assert(m_states[t.from()].ref_states_in_blocks<
1299 all_source_bottom_states.emplace(t.from());
1300 }
1301 else
1302 {
1303 assert(b.sta.rt_non_bottom_states<=
1304 m_states[t.from()].ref_states_in_blocks);
1305 assert(m_states[t.from()].ref_states_in_blocks < b.end_states);
1306 }
1307 }
1308 }
1309 assert(all_source_bottom_states.size()<=static_cast<std::size_t>
1310 (std::distance(b.start_bottom_states, b.sta.rt_non_bottom_states)));
1311 // check that every bottom state has a transition in this BLC entry:
1312 bool eventual_instability_is_ok = true;
1313 bool eventual_marking_is_ok = true;
1314 if (!all_transitions_in_BLC_are_inert &&
1315 all_source_bottom_states.size()!=static_cast<std::size_t>
1316 (std::distance(b.start_bottom_states, b.sta.rt_non_bottom_states)))
1317 {
1318 // only splitters should be instable.
1319 mCRL2log(log::debug) << "Not all "
1320 << std::distance(b.start_bottom_states, b.sta.rt_non_bottom_states)
1321 << (m_branching ? " bottom states have a transition in the "
1322 : " states have a transition in the ")
1323 << ind->debug_id(*this) << ": transitions found from states";
1324 for (set_of_states_type::iterator
1325 asbc_it=all_source_bottom_states.begin();
1326 asbc_it!=all_source_bottom_states.end() ; ++asbc_it)
1327 { mCRL2log(log::debug) << ' ' << *asbc_it; }
1328 mCRL2log(log::debug) << '\n';
1329 eventual_instability_is_ok = false;
1330 }
1331 if (!ind->is_stable())
1332 {
1333 // only splitters should contain marked transitions.
1334 mCRL2log(log::debug) << ind->debug_id(*this) << " contains " << std::distance(ind->start_marked_BLC, ind->end_same_BLC) << " marked transitions.\n";
1335 eventual_marking_is_ok = false;
1336 }
1338 {
1339 /* I would like the following to check more closely because in a
1340 block with new bottom states, one should have...
1341 if (!eventual_marking_is_ok)
1342 {
1343 eventual_marking_is_ok = true;
1344 for (BLC_list_const_iterator i=ind->start_marked_BLC; i<ind->end_same_BLC; ++i)
1345 {
1346 const state_index from = m_aut.get_transitions()[*i].from();
1347 // assert(m_states[from].block == bi); -- already checked earlier
1348 if (0 != m_states[from].no_of_outgoing_block_inert_transitions)
1349 {
1350 // the state is a non-bottom state
1351 eventual_marking_is_ok = false;
1352 break;
1353 }
1354 }
1355 if (eventual_marking_is_ok)
1356 {
1357 mCRL2log(log::debug) << " This is ok because all marked transitions begin in new bottom states of " << bi->debug_id(*this) << ".\n";
1358 eventual_instability_is_ok = true;
1359 }
1360 } */
1361 if (!(eventual_instability_is_ok && eventual_marking_is_ok))
1362 {
1363 mCRL2log(log::debug) << " This is ok because " << b.debug_id(*this) << " contains new bottom states.\n";
1364 eventual_instability_is_ok = true;
1365 eventual_marking_is_ok = true;
1366 }
1367 }
1368 if (!(eventual_instability_is_ok && eventual_marking_is_ok) && nullptr != calM && calM->begin() != calM->end())
1369 {
1370 std::vector<std::pair<BLC_list_iterator, BLC_list_iterator> >::const_iterator calM_iter = calM->begin();
1371 if (nullptr != calM_elt)
1372 {
1373 for(;;)
1374 {
1375 assert(calM->end() != calM_iter);
1376 if (calM_iter->first <= calM_elt->first && calM_elt->second <= calM_iter->second)
1377 {
1378 break;
1379 }
1380 ++calM_iter;
1381 }
1382 if (calM_elt->first<=ind->start_same_BLC && ind->end_same_BLC<=calM_elt->second)
1383 {
1384 mCRL2log(log::debug) <<" This is ok because the BLC set ("
1385 << b.debug_id(*this) << " -" << m_aut.action_label(first_t.label())
1386 << "-> " << m_states[first_t.to()].
1387 block->c.onstellation->debug_id(*this)
1388 << ") is soon going to be a main splitter.\n";
1389 eventual_instability_is_ok = true;
1390 eventual_marking_is_ok = true;
1391 }
1392 else
1393 {
1394 if (old_constellation==
1395 m_states[first_t.to()].block->c.onstellation)
1396 {
1397 const linked_list<BLC_indicators>::const_iterator main_splitter=b.block.to_constellation.next(ind);
1398 if (main_splitter!=b.block.to_constellation.end())
1399 {
1400 assert(main_splitter->start_same_BLC < main_splitter->end_same_BLC);
1401 const transition& main_t = m_aut.get_transitions()[*main_splitter->start_same_BLC];
1402 assert(m_states[main_t.from()].block == &b);
1403 if(label_or_divergence(first_t)==label_or_divergence(main_t)
1404 && m_states[main_t.to()].block->c.onstellation==
1405 new_constellation)
1406 {
1407//std::cerr << "Corresponding main splitter: " << main_splitter->debug_id(*this) << '\n';
1408 if (calM_elt->first<=main_splitter->start_same_BLC && main_splitter->end_same_BLC<=calM_elt->second)
1409 {
1410 assert(new_constellation==
1411 m_states[main_t.to()].block->c.onstellation);
1412 mCRL2log(log::debug) << " This is ok because the BLC set (" << b.debug_id(*this) << " -" << m_aut.action_label(first_t.label()) << "-> " << old_constellation->debug_id(*this) << ") is soon going to be a co-splitter.\n";
1413 eventual_instability_is_ok = true;
1414 eventual_marking_is_ok = true;
1415 }
1416//else { std::cerr << "Main splitter is not in calM_elt = [" << std::distance(m_BLC_transitions.begin(), calM_elt->first) << ',' << std::distance(m_BLC_transitions.begin(), calM_elt->second) << ")\n" };
1417 }
1418 }
1419 }
1420 }
1421 ++calM_iter;
1422 }
1423 for(; !(eventual_instability_is_ok && eventual_marking_is_ok) && calM->end() != calM_iter; ++calM_iter)
1424 {
1425 if (calM_iter->first<=ind->start_same_BLC && ind->end_same_BLC<=calM_iter->second)
1426 {
1427 mCRL2log(log::debug) <<" This is ok because the BLC set ("
1428 << b.debug_id(*this) << " -" << m_aut.action_label(first_t.label())
1429 << "-> "
1430 << m_states[first_t.to()].block->c.onstellation->debug_id(*this)
1431 << ") is going to be a main splitter later.\n";
1432 eventual_instability_is_ok = true;
1433 eventual_marking_is_ok = true;
1434 }
1435 else
1436 {
1437 if (old_constellation==
1438 m_states[first_t.to()].block->c.onstellation)
1439 {
1440 const linked_list<BLC_indicators>::const_iterator main_splitter=b.block.to_constellation.next(ind);
1441 if (main_splitter != b.block.to_constellation.end())
1442 {
1443 assert(main_splitter->start_same_BLC < main_splitter->end_same_BLC);
1444 const transition& main_t = m_aut.get_transitions()[*main_splitter->start_same_BLC];
1445 assert(m_states[main_t.from()].block == &b);
1446 if(label_or_divergence(first_t)==label_or_divergence(main_t)
1447 && m_states[main_t.to()].block->c.onstellation==
1448 new_constellation)
1449 {
1450 if (calM_iter->first<=main_splitter->start_same_BLC && main_splitter->end_same_BLC<=calM_iter->second)
1451 {
1452 assert(new_constellation==
1453 m_states[main_t.to()].block->c.onstellation);
1454 mCRL2log(log::debug) << " This is ok because the BLC "
1455 "set (" << b.debug_id(*this) << " -"
1456 << m_aut.action_label(first_t.label())
1457 << "-> " << old_constellation->debug_id(*this)
1458 << ") is going to be a co-splitter later.\n";
1459 eventual_instability_is_ok = true;
1460 eventual_marking_is_ok = true;
1461 }
1462//else { std::cerr << "Main splitter is not in calM_iter = [" << std::distance(m_BLC_transitions.begin(), calM_iter->first) << ',' << std::distance(m_BLC_transitions.begin(), calM_iter->second) << ")\n" };
1463 }
1464 }
1465 }
1466 }
1467 }
1468 }
1469 if (1>=number_of_states_in_block(b))
1470 {
1471 if (!eventual_marking_is_ok)
1472 {
1473 mCRL2log(log::debug) << " (This is ok because the source block contains only 1 state.)\n";
1474 eventual_marking_is_ok = true;
1475 }
1476 }
1477 else if (1<no_of_constellations /* i.e. !initialisation */ &&
1479 {
1480 assert(eventual_marking_is_ok);
1481 assert(eventual_instability_is_ok);
1482 if (null_constellation==old_constellation && ind->is_stable()) {
1483 assert(previous_stable);
1484 }
1485 else
1486 {
1487 previous_stable=false;
1488 }
1489 }
1490 }
1491 }
1492 mCRL2log(log::debug) << "Check stability finished: " << tag << ".\n";
1493 return true;
1494 }
1495
1497 void display_BLC_list(const block_type* const bi) const
1498 {
1499 mCRL2log(log::debug) << "\n BLC_List\n";
1500 for(const BLC_indicators& blc_it: bi->block.to_constellation)
1501 {
1502 const transition& first_t=m_aut.get_transitions()[*blc_it.start_same_BLC];
1504 mCRL2log(log::debug) << "\n BLC set "
1505 << std::distance<BLC_list_const_iterator>(m_BLC_transitions.data(),
1506 blc_it.start_same_BLC) << " -- "
1507 << std::distance<BLC_list_const_iterator>(m_BLC_transitions.data(),
1508 blc_it.end_same_BLC)
1509 << " of " << (null_action==l ? "divergent self-loop " : pp(m_aut.action_label(l))+"-")
1510 << "transitions to " << m_states[first_t.to()].block->c.onstellation->debug_id(*this) << ":\n";
1511 for (BLC_list_const_iterator i=blc_it.start_same_BLC; ; ++i)
1512 {
1513 if (i == blc_it.start_marked_BLC)
1514 {
1515 mCRL2log(log::debug) << " (The BLC set is unstable, and the "
1516 " following transitions are marked.)\n";
1517 }
1518 if (i>=blc_it.end_same_BLC)
1519 {
1520 break;
1521 }
1522 const transition& t=m_aut.get_transitions()[*i];
1523 mCRL2log(log::debug) << " " << t.from() << " -"
1524 << m_aut.action_label(t.label()) << "-> " << t.to();
1525 if (is_inert_during_init(t) &&
1526 m_states[t.from()].block==m_states[t.to()].block)
1527 {
1528 mCRL2log(log::debug) << " (block-inert)";
1529 }
1530 else if (is_inert_during_init(t) &&
1531 m_states[t.from()].block->c.onstellation==
1532 m_states[t.to()].block->c.onstellation)
1533 {
1534 mCRL2log(log::debug) << " (constellation-inert)";
1535 }
1536 mCRL2log(log::debug) << '\n';
1537 }
1538 }
1539 mCRL2log(log::debug) << " BLC_List end\n";
1540 }
1541
1543 void print_data_structures(const std::string& header,
1544 const bool initialisation=false) const
1545 {
1546 if (!mCRL2logEnabled(log::debug)) { return; }
1547 mCRL2log(log::debug) << "========= PRINT DATASTRUCTURE: " << header << " =======================================\n"
1548 "++++++++++++++++++++ States ++++++++++++++++++++++++++++\n";
1549 for(state_index si=0; si<m_aut.num_states(); ++si)
1550 {
1551 mCRL2log(log::debug) << "State " << si <<" (" << m_states[si].block->debug_id(*this) << "):\n"
1552 " #Inert outgoing transitions: " << m_states[si].no_of_outgoing_block_inert_transitions << "\n"
1553
1554 " Incoming transitions:\n";
1555 std::vector<transition>::const_iterator end=(si+1==m_aut.num_states()?m_aut.get_transitions().end():m_states[si+1].start_incoming_transitions);
1556 for(std::vector<transition>::const_iterator it=m_states[si].start_incoming_transitions; it!=end; ++it)
1557 {
1558 mCRL2log(log::debug) << " " << ptr(*it) << "\n";
1559 }
1560
1561 mCRL2log(log::debug) << " Outgoing transitions:\n";
1562 label_index t_label=m_aut.tau_label_index();
1563 const constellation_type* to_constln=null_constellation;
1564 for(outgoing_transitions_const_it it=m_states[si].start_outgoing_transitions;
1565 it!=m_outgoing_transitions.end() &&
1566 (si+1>=m_aut.num_states() || it!=m_states[si+1].start_outgoing_transitions);
1567 ++it)
1568 {
1569 const transition& t=m_aut.get_transitions()[!initialisation
1570 ? *it->ref.BLC_transitions : it->ref.transitions];
1571 bool start_same_saC_valid=
1572 m_outgoing_transitions.cbegin()<=it->start_same_saC &&
1573 it->start_same_saC<m_outgoing_transitions.end();
1574 if (start_same_saC_valid &&
1575 it->start_same_saC->start_same_saC==it &&
1576 it->start_same_saC >= it)
1577 {
1578 // it is at the beginning of a saC slice
1579 const label_index old_t_label=t_label;
1580 t_label=label_or_divergence(t, null_action);
1581 to_constln=m_states[t.to()].block->c.onstellation;
1582 mCRL2log(log::debug) << " - - - - saC slice of "
1583 << (null_action==t_label ? "divergent self-loop " : pp(m_aut.action_label(t_label))+"-")
1584 << "transitions to " << to_constln->debug_id(*this)
1585 << (m_aut.is_tau(t_label) && !m_aut.is_tau(old_t_label)
1586 ? " -- error: tau-transitions should come first\n"
1587 : ":\n");
1588 }
1589 mCRL2log(log::debug) << " " << ptr(t);
1590 if (start_same_saC_valid)
1591 {
1592 if (label_or_divergence(t, null_action)!=t_label)
1593 {
1594 mCRL2log(log::debug) << " -- error: different label";
1595 }
1596 if (!initialisation && m_states[t.to()].block->c.onstellation!=to_constln)
1597 {
1598 mCRL2log(log::debug) << " -- error: different target " << m_states[t.to()].block->c.onstellation->debug_id(*this);
1599 }
1600 if (it->start_same_saC->start_same_saC == it)
1601 {
1602 // Transition t must be the beginning and/or the end of a saC-slice
1603 if (it->start_same_saC >= it && it > m_outgoing_transitions.cbegin())
1604 {
1605 // Transition t must be the beginning of a saC-slice
1606 const transition& prev_t=m_aut.get_transitions()[
1607 !initialisation ? *std::prev(it)->ref.BLC_transitions
1608 : std::prev(it)->ref.transitions];
1609 if (prev_t.from()==t.from() &&
1610 label_or_divergence(prev_t)==t_label &&
1611 (initialisation ||
1612 m_states[prev_t.to()].block->c.onstellation==
1613 m_states[t.to()].block->c.onstellation))
1614 {
1615 mCRL2log(log::debug) << " -- error: not the beginning of a saC-slice";
1616 }
1617 }
1618 if (it->start_same_saC <= it && std::next(it) < m_outgoing_transitions.end())
1619 {
1620 // Transition t must be the end of a saC-slice
1621 const transition& next_t=m_aut.get_transitions()[
1622 !initialisation ? *std::next(it)->ref.BLC_transitions
1623 : std::next(it)->ref.transitions];
1624 if (next_t.from()==t.from() &&
1625 label_or_divergence(next_t)==t_label &&
1626 (initialisation ||
1627 m_states[next_t.to()].block->c.onstellation==
1628 m_states[t.to()].block->c.onstellation))
1629 {
1630 mCRL2log(log::debug) << " -- error: not the end of a saC-slice";
1631 }
1632 }
1633 }
1634 else if (it->start_same_saC > it ? it->start_same_saC->start_same_saC > it : it->start_same_saC->start_same_saC < it)
1635 {
1636 mCRL2log(log::debug) << " -- error: not pointing to its own saC-slice";
1637 }
1638 }
1639 mCRL2log(log::debug) << '\n';
1640 }
1641 mCRL2log(log::debug) << " Ref states in blocks: " << std::distance<fixed_vector<state_type_gj>::const_iterator>(m_states.cbegin(), m_states[si].ref_states_in_blocks->ref_state) << ". Must be " << si <<".\n";
1642 mCRL2log(log::debug) << "---------------------------------------------------\n";
1643 }
1644 mCRL2log(log::debug) << "++++++++++++++++++++ Transitions ++++++++++++++++++++++++++++\n";
1645 for(transition_index ti=0; ti<m_aut.num_transitions(); ++ti)
1646 {
1647 const transition& t=m_aut.get_transitions()[ti];
1648 mCRL2log(log::debug) << "Transition " << ti <<": " << t.from()
1649 << " -" << m_aut.action_label(t.label()) << "-> "
1650 << t.to() << "\n";
1651 }
1652
1653 mCRL2log(log::debug) << "++++++++++++++++++++ Blocks ++++++++++++++++++++++++++++\n";
1654 for (const state_in_block_pointer* si=m_states_in_blocks.data();
1655 m_states_in_blocks.data_end()!=si; si=si->ref_state->block->end_states)
1656 {
1657 block_type* const bi=si->ref_state->block;
1658 mCRL2log(log::debug) << " Block " << bi;
1659 if (!initialisation) {
1660 mCRL2log(log::debug) << " (" << bi->c.onstellation->debug_id(*this) << ')';
1661 }
1662 mCRL2log(log::debug) << ":\n " << std::distance(bi->start_bottom_states,
1664 << (m_branching ? " Bottom state" : " State")
1665 << (1==std::distance(bi->start_bottom_states,
1666 bi->sta.rt_non_bottom_states) ? ": " : "s: ");
1667 for (const state_in_block_pointer*
1668 sit=bi->start_bottom_states; sit!=bi->sta.rt_non_bottom_states; ++sit)
1669 {
1670 mCRL2log(log::debug) << sit->ref_state->debug_id_short(*this) << " ";
1671 }
1672 if (m_branching)
1673 {
1674 mCRL2log(log::debug) << "\n " << std::distance
1676 << " Non-bottom state" << (1==std::distance
1678 ? ": " : "s: ");
1679 for (const state_in_block_pointer*
1680 sit=bi->sta.rt_non_bottom_states; sit!=bi->end_states; ++sit)
1681 {
1682 mCRL2log(log::debug) << sit->ref_state->debug_id_short(*this) << " ";
1683 }
1684 }
1685 else
1686 {
1687 assert(bi->sta.rt_non_bottom_states==bi->end_states);
1688 }
1689 if (!initialisation)
1690 {
1691 display_BLC_list(bi);
1692 }
1693 mCRL2log(log::debug) << "\n";
1694 }
1695
1696 mCRL2log(log::debug) << "++++++++++++++++++++ Constellations ++++++++++++++++++++++++++++\n";
1697 for (const state_in_block_pointer* si=m_states_in_blocks.data();
1698 m_states_in_blocks.data_end()!=si;
1699 si=si->ref_state->block->c.onstellation->end_const_states)
1700 {
1701 const constellation_type* const ci=si->ref_state->block->c.onstellation;
1702 mCRL2log(log::debug) << " " << ci->debug_id(*this) << ":\n";
1703 mCRL2log(log::debug) << " Blocks in constellation:";
1704 for (const state_in_block_pointer*
1705 constln_it=ci->start_const_states;
1706 constln_it<ci->end_const_states; )
1707 {
1708 const block_type* const bi=constln_it->ref_state->block;
1709 mCRL2log(log::debug) << " " << bi->debug_id(*this);
1710 constln_it = bi->end_states;
1711 }
1712 mCRL2log(log::debug) << "\n";
1713 }
1714 mCRL2log(log::debug) << "Non-trivial constellations:";
1716 {
1717 mCRL2log(log::debug) << " " << ci->debug_id(*this);
1718 }
1719
1721 "\n++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++\n"
1722 "Outgoing transitions:\n";
1723
1725 pi < m_outgoing_transitions.cend(); ++pi)
1726 {
1727 const transition& t=m_aut.get_transitions()[!initialisation
1728 ? *pi->ref.BLC_transitions : pi->ref.transitions];
1729 mCRL2log(log::debug) << " " << t.from() << " -"
1730 << m_aut.action_label(t.label()) << "-> " << t.to();
1731 if (m_outgoing_transitions.cbegin()<=pi->start_same_saC &&
1732 pi->start_same_saC<m_outgoing_transitions.end())
1733 {
1734 const transition& t1=m_aut.get_transitions()[!initialisation
1735 ? *pi->start_same_saC->ref.BLC_transitions
1736 : pi->start_same_saC->ref.transitions];
1737 mCRL2log(log::debug) << " \t(same saC: " << t1.from() << " -" << m_aut.action_label(t1.label()) << "-> " << t1.to();
1738 const label_index t_label = label_or_divergence(t);
1739 if (pi->start_same_saC->start_same_saC == pi)
1740 {
1741 // Transition t must be the beginning and/or the end of a saC-slice
1742 if (pi->start_same_saC >= pi && pi > m_outgoing_transitions.cbegin())
1743 {
1744 // Transition t must be the beginning of a saC-slice
1745 const transition& prev_t=m_aut.get_transitions()[
1746 !initialisation ? *std::prev(pi)->ref.BLC_transitions
1747 : std::prev(pi)->ref.transitions];
1748 if (prev_t.from()==t.from() &&
1749 label_or_divergence(prev_t)==t_label &&
1750 (initialisation ||
1751 m_states[prev_t.to()].block->c.onstellation==
1752 m_states[t.to()].block->c.onstellation))
1753 {
1754 mCRL2log(log::debug) << " -- error: not the beginning of a saC-slice";
1755 }
1756 }
1757 if (pi->start_same_saC <= pi && std::next(pi) < m_outgoing_transitions.end())
1758 {
1759 // Transition t must be the end of a saC-slice
1760 const transition& next_t=m_aut.get_transitions()[
1761 !initialisation ? *std::next(pi)->ref.BLC_transitions
1762 : std::next(pi)->ref.transitions];
1763 if (next_t.from()==t.from() &&
1764 label_or_divergence(next_t)==t_label &&
1765 (initialisation ||
1766 m_states[next_t.to()].block->c.onstellation==
1767 m_states[t.to()].block->c.onstellation))
1768 {
1769 mCRL2log(log::debug) << " -- error: not the end of a saC-slice";
1770 }
1771 }
1772 }
1773 else if (pi->start_same_saC > pi ? pi->start_same_saC->start_same_saC > pi : pi->start_same_saC->start_same_saC < pi)
1774 {
1775 mCRL2log(log::debug) << " -- error: not in its own saC-slice";
1776 }
1777 mCRL2log(log::debug) << ')';
1778 }
1779 mCRL2log(log::debug) << '\n';
1780 }
1781 mCRL2log(log::debug) << "++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++\n"
1782 "New bottom blocks to be investigated:";
1783
1785 {
1786 mCRL2log(log::debug) << " " << bi->debug_id(*this) << '\n';
1787 }
1788
1789 mCRL2log(log::debug) << "\n========= END PRINT DATASTRUCTURE: " << header << " =======================================\n";
1790 }
1791 #endif // ifndef NDEBUG
1792 public:
1797 std::size_t num_eq_classes() const
1798 {
1799 return no_of_blocks;
1800 }
1801
1802
1811 { assert(si<m_states.size());
1812 return m_states[si].block->sta.te_in_reduced_LTS;
1813 }
1814
1815
1829 {
1830 // Assign numbers to the blocks (i.e. to the states of the reduced LTS)
1831 // One could devise a fancy scheme where the block containing state i
1832 // tries to get block number i; but let's just do something simple now.
1833 // We no longer need sta.rt_non_bottom_states at this moment, so we can
1834 // reuse that field to store the block number:
1835 state_index block_number=0;
1837 si=m_states_in_blocks.data(); m_states_in_blocks.data_end()!=si;
1838 si=si->ref_state->block->end_states)
1839 {
1840 block_type* const bi=si->ref_state->block;
1841 // destruct bi->sta.rt_non_bottom_states; -- trivial
1842 new (&bi->sta.te_in_reduced_LTS) state_index(block_number);
1843 ++block_number;
1844 }
1845
1846 // The transitions are most efficiently directly extracted from the
1847 // block.to_constellation lists in blocks.
1848 typename std::remove_reference<decltype(m_aut.get_transitions())>::type
1849 T;
1851 si=m_states_in_blocks.data(); m_states_in_blocks.data_end()!=si;
1852 si=si->ref_state->block->end_states)
1853 {
1854 const block_type& B=*si->ref_state->block; //mCRL2complexity(&B, add_work(..., 1), *this);
1855 // Because every block is touched exactly once, we do not store a
1856 // physical counter for this.
1857 for(const BLC_indicators blc_ind: B.block.to_constellation)
1858 { // mCRL2complexity(&blc_ind, add_work(..., 1), *this);
1859 // Because every BLC set is touched exactly once, we do not store
1860 // a physical counter for this.
1861 assert(blc_ind.start_same_BLC<blc_ind.end_same_BLC);
1862 const transition& t=m_aut.get_transitions()[*blc_ind.start_same_BLC];
1863 const state_index new_to=get_eq_class(t.to());
1864 if (!is_inert_during_init(t) || B.sta.te_in_reduced_LTS!=new_to)
1865 {
1866 T.emplace_back(B.sta.te_in_reduced_LTS, t.label(), new_to);
1867 }
1868 }
1869 }
1870 m_aut.clear_transitions();
1871 T.swap(m_aut.get_transitions());
1872 //
1873 // Merge the states, by setting the state labels of each state to the
1874 // concatenation of the state labels of its equivalence class.
1875
1876 if (m_aut.has_state_info()) // If there are no state labels
1877 { // this step is not needed
1878 /* Create a vector for the new labels */
1879 typename std::remove_reference<decltype(m_aut.state_labels())>::type
1880 new_labels(num_eq_classes());
1881
1882 for(std::size_t i=0; i<m_aut.num_states(); ++i)
1883 { //mCRL2complexity(&m_states[i], add_work(..., 1), *this);
1884 // Because every state is touched exactly once, we do not store a
1885 // physical counter for this.
1886 const state_index new_index(get_eq_class(i));
1887 new_labels[new_index]=new_labels[new_index]+m_aut.state_label(i);
1888 }
1889
1890 m_aut.set_num_states(num_eq_classes(), false); assert(0==m_aut.num_state_labels());
1891 //m_aut.clear_state_labels();
1892 new_labels.swap(m_aut.state_labels());
1893 }
1894 else
1895 {
1896 m_aut.set_num_states(num_eq_classes(), false);
1897 }
1898
1899 m_aut.set_initial_state(get_eq_class(m_aut.initial_state()));
1900 }
1901
1902
1907 bool in_same_class(state_index const s, state_index const t) const
1908 {
1909 return get_eq_class(s) == get_eq_class(t);
1910 }
1911 private:
1912 #ifndef NDEBUG
1913 std::string ptr(const transition& t) const
1914 {
1915 return std::to_string(t.from())+" -"+pp(m_aut.action_label(t.label()))+
1916 "-> "+std::to_string(t.to());
1917 }
1918 #endif
1919 /*--------------------------- main algorithm ----------------------------*/
1920
1921 /*----------------- splitB -- Algorithm 3 of [GJ 2024] -----------------*/
1922
1925 { assert(B.start_bottom_states<B.end_states);
1926 return std::distance(B.start_bottom_states, B.end_states);
1927 }
1928
1931 const
1933 return std::distance(C.start_const_states, C.end_const_states);
1934 }
1935
1939 { assert(m_states_in_blocks.data()<=pos1);
1940 std::swap(*pos1,*pos2); assert(pos1<m_states_in_blocks.data_end());
1941 pos1->ref_state->ref_states_in_blocks=pos1; assert(m_states_in_blocks.data()<=pos2);
1942 pos2->ref_state->ref_states_in_blocks=pos2; assert(pos2<m_states_in_blocks.data_end()); assert(pos1!=pos2);
1943 }
1944
1948 {
1949 if (pos1!=pos2)
1950 {
1952 }
1953 }
1954
1962 { assert(m_states_in_blocks.data()<=pos2); assert(pos2<pos3);
1963 assert(pos1<m_states_in_blocks.data_end());
1964 if (pos1==pos3)
1965 {
1966 std::swap(*pos1,*pos2);
1967 }
1968 else
1969 { assert(pos3<pos1);
1970 const state_in_block_pointer temp=*pos1;
1971 *pos1=*pos3;
1972 *pos3=*pos2;
1973 *pos2=temp;
1974
1975 pos3->ref_state->ref_states_in_blocks=pos3;
1976 }
1977 pos1->ref_state->ref_states_in_blocks=pos1;
1978 pos2->ref_state->ref_states_in_blocks=pos2;
1979 }
1980
1988 {
1989 if (pos2==pos3)
1990 {
1992 }
1993 else
1994 {
1996 }
1997 }
1998
2009 state_index count
2010 #if !defined(NDEBUG) || defined(COUNT_WORK_BALANCE)
2011 , const state_in_block_pointer* assign_work_to,
2012 unsigned char const max_B,
2014 multiple_swap_states_in_block__swap_state_in_small_block
2015 #endif
2016 )
2017 { assert(count<m_aut.num_states()); assert(m_states_in_blocks.data()<=pos1);
2018 /* if (pos1 > pos2) std::swap(pos1, pos2); */ assert(pos1<pos2); assert(pos2<=m_states_in_blocks.data_end()-count);
2019 {
2020 std::make_signed<state_index>::type
2021 overlap=std::distance(pos2, pos1)+count;
2022 if (overlap > 0)
2023 {
2024 count -= overlap;
2025 #if !defined(NDEBUG) || defined(COUNT_WORK_BALANCE)
2026 // If we do not change `assign_work_to`, then there should be no overlap
2027 // between the area starting at `pos2` and the one at `assign_work_to`;
2028 // otherwise it may happen that work is assigned to unexpected counters.
2029 if (pos2==assign_work_to) {
2030 assign_work_to+=overlap;
2031 } else { assert(assign_work_to+count<=pos2+overlap ||
2032 pos2+overlap+count<=assign_work_to); }
2033 #endif
2034 pos2 += overlap;
2035 }
2036 } assert(0 < count);
2037 state_in_block_pointer temp=*pos1;
2038 while (--count > 0)
2039 { mCRL2complexity(assign_work_to->ref_state, add_work(ctr, max_B), *this);
2040 #if !defined(NDEBUG) || defined(COUNT_WORK_BALANCE)
2041 ++assign_work_to;
2042 #endif
2043 *pos1 = *pos2;
2044 pos1->ref_state->ref_states_in_blocks=pos1;
2045 ++pos1;
2046 *pos2 = *pos1;
2047 pos2->ref_state->ref_states_in_blocks=pos2;
2048 ++pos2;
2049 }
2050 *pos1 = *pos2;
2051 pos1->ref_state->ref_states_in_blocks=pos1;
2052 *pos2 = temp;
2053 pos2->ref_state->ref_states_in_blocks=pos2;
2054 #ifndef NDEBUG
2056 si=m_states.cbegin(); si<m_states.cend(); ++si)
2057 {
2058 assert(si==si->ref_states_in_blocks->ref_state);
2059 }
2060 #endif
2061 }
2062
2067 {
2068 BLC_list_iterator old_pos = out_pos->ref.BLC_transitions;
2069 linked_list<BLC_indicators>::iterator ind =
2070 m_transitions[*old_pos].transitions_per_block_to_constellation; assert(ind->start_same_BLC<=old_pos);
2071 assert(old_pos<m_BLC_transitions.data_end());
2072 assert(old_pos<ind->end_same_BLC); assert(!ind->is_stable());
2073 if (old_pos < ind->start_marked_BLC)
2074 {
2075 /* The transition is not marked */ assert(ind->start_same_BLC<ind->start_marked_BLC);
2076 BLC_list_iterator new_pos = std::prev(ind->start_marked_BLC); assert(ind->start_same_BLC<=new_pos); assert(new_pos<ind->end_same_BLC);
2077 assert(new_pos<m_BLC_transitions.data_end());
2078 if (old_pos < new_pos)
2079 {
2080 std::swap(*old_pos, *new_pos);
2081 m_transitions[*old_pos].ref_outgoing_transitions->
2082 ref.BLC_transitions = old_pos; assert(out_pos==m_transitions[*new_pos].ref_outgoing_transitions);
2083 out_pos->ref.BLC_transitions = new_pos;
2084 }
2085 ind->start_marked_BLC--;
2086 }
2087
2088 #ifndef NDEBUG
2090 it<m_BLC_transitions.data_end(); ++it)
2091 {
2092 assert(m_transitions[*it].ref_outgoing_transitions->ref.BLC_transitions==
2093 it);
2094 assert(m_transitions[*it].transitions_per_block_to_constellation->
2095 start_same_BLC<=it);
2096 assert(it<
2097 m_transitions[*it].transitions_per_block_to_constellation->end_same_BLC);
2098 }
2099 #endif
2100 }
2101
2102#ifndef USE_FOUR_WAY_SPLIT
2108 template <bool initialisation=false>
2109 block_type* split_block_B_into_R_and_BminR(
2110 block_type* const B,
2111 state_in_block_pointer* first_bottom_state_in_R,
2112 state_in_block_pointer* last_bottom_state_in_R,
2113 const todo_state_vector& R)
2114 { assert(B->start_bottom_states<=first_bottom_state_in_R);
2115//std::cerr << "split_block_B_into_R_and_BminR" << (initialisation ? "<true>(" : "(") << B->debug_id(*this)
2116//<< ",&m_states_in_blocks[" << std::distance(m_states_in_blocks.begin(), first_bottom_state_in_R)
2117//<< "..." << std::distance(m_states_in_blocks.begin(), last_bottom_state_in_R)
2118//<< "),R = {";
2119//for(auto s:R){ std::cerr << ' ' << std::distance(m_states.begin(), s.ref_state); }
2120//std::cerr << " }"
2121//")\n";
2122//std::cerr << "SPLIT BLOCK " << B << " by removing "; for(auto s = first_bottom_state_in_R; s < last_bottom_state_in_R; ++s){ std::cerr << std::distance(m_states.begin(), s->ref_state) << ' ';} for(auto s:R){ std::cerr << ' ' << std::distance(m_states.begin(), s.ref_state); } std::cerr << '\n';
2123 /* Basic administration. Make a new block and add it to the current */ assert(first_bottom_state_in_R<=last_bottom_state_in_R);
2124 /* constellation. */ assert(last_bottom_state_in_R<=B->sta.rt_non_bottom_states);
2125 constellation_type* const ci=B->c.onstellation;
2126 block_type* const B_new=
2127 #ifdef USE_POOL_ALLOCATOR
2129 template construct<block_type>
2130 #else
2131 new block_type
2132 #endif
2133 (B->start_bottom_states, ci);
2134 ++no_of_blocks;
2135 #if !defined(NDEBUG) || defined(COUNT_WORK_BALANCE)
2136 B_new->work_counter=B->work_counter;
2137 #endif
2138 assert(m_states_in_blocks.data()<=ci->start_const_states);
2139 assert(ci->start_const_states<ci->end_const_states);
2140 assert(ci->end_const_states<=m_states_in_blocks.data_end());
2141 if (ci->start_const_states->ref_state->block==
2142 std::prev(ci->end_const_states)->ref_state->block)
2143 { assert(std::find(m_non_trivial_constellations.begin(),
2144 /* This constellation was trivial, as it will be split add it to the */ m_non_trivial_constellations.end(),
2145 /* non-trivial constellations. */ ci)==m_non_trivial_constellations.end());
2146 m_non_trivial_constellations.emplace_back(ci);
2147 } // The size of the new block is not yet number_of_states_in_block(...)
2148 #if !defined(NDEBUG) || defined(COUNT_WORK_BALANCE)
2149 unsigned char const max_B = check_complexity::log_n -
2150 check_complexity::ilog2(std::distance(first_bottom_state_in_R,
2151 /* Carry out the split. */ last_bottom_state_in_R) + R.size());
2152 #endif
2153 if (B->start_bottom_states!=first_bottom_state_in_R)
2154 { assert(first_bottom_state_in_R!=last_bottom_state_in_R);
2156 B->start_bottom_states, first_bottom_state_in_R,
2157 std::distance(first_bottom_state_in_R, last_bottom_state_in_R)
2158 #if !defined(NDEBUG) || defined(COUNT_WORK_BALANCE)
2159 , first_bottom_state_in_R, max_B
2160 #endif
2161 );
2162 last_bottom_state_in_R = B->start_bottom_states +
2163 std::distance(first_bottom_state_in_R, last_bottom_state_in_R);
2164 first_bottom_state_in_R = B->start_bottom_states;
2165 }
2166 assert(B_new->start_bottom_states==first_bottom_state_in_R);
2167 B_new->sta.rt_non_bottom_states=last_bottom_state_in_R;
2168 // Update the block pointers for R-bottom states:
2170 s_it=first_bottom_state_in_R; s_it<last_bottom_state_in_R; ++s_it)
2171 { mCRL2complexity(s_it->ref_state, add_work(check_complexity::
2172 split_block_B_into_R_and_BminR__carry_out_split, max_B), *this);
2173//std::cerr << "MOVE STATE TO NEW BLOCK: " << std::distance(m_states.begin(), s_it->ref_state) << "\n";
2174 assert(B==s_it->ref_state->block);
2175 s_it->ref_state->block=B_new; assert(s_it->ref_state->ref_states_in_blocks==s_it);
2176 }
2177 // Now the R bottom states are in the correct position, and we don't have
2178 // to look into them any more.
2179 B_new->end_states=last_bottom_state_in_R;
2180 B->start_bottom_states=last_bottom_state_in_R;
2181
2182 // Move the non-bottom states to their correct positions:
2183 for(state_in_block_pointer s: R)
2184 { mCRL2complexity(s.ref_state, add_work(check_complexity::
2185 split_block_B_into_R_and_BminR__carry_out_split, max_B), *this);
2186//std::cerr << "MOVE STATE TO NEW BLOCK: " << s << "\n";
2187 assert(B==s.ref_state->block);
2188 s.ref_state->block=B_new;
2189 state_in_block_pointer* pos=s.ref_state->ref_states_in_blocks; assert(pos>=B->sta.rt_non_bottom_states); // it's a non-bottom state
2190 // pos -> B.start_bottom_states -> B.sta.rt_non_bottom_states -> pos
2195 B_new->end_states++; assert(B->start_bottom_states<=B->sta.rt_non_bottom_states);
2196 assert(B_new->start_bottom_states<B_new->end_states);
2197 }
2198 return B_new;
2199 }
2200#endif
2201
2204 const BLC_list_iterator i1,
2205 const BLC_list_iterator i2,
2206 const BLC_list_iterator i3)
2207 { assert(i3<=i2); assert(i2<=i1);
2208 if (i1==i3)
2209 {
2210 return;
2211 }
2212 if ((i1==i2)||(i2==i3))
2213 {
2214 std::swap(*i1,*i3);
2215 m_transitions[*i1].ref_outgoing_transitions->ref.BLC_transitions = i1;
2216 m_transitions[*i3].ref_outgoing_transitions->ref.BLC_transitions = i3;
2217 }
2218 else // swap all three elements.
2219 {
2220 transition_index temp = *i1;
2221 *i1=*i2;
2222 *i2=*i3;
2223 *i3=temp;
2224 m_transitions[*i1].ref_outgoing_transitions->ref.BLC_transitions = i1;
2225 m_transitions[*i2].ref_outgoing_transitions->ref.BLC_transitions = i2;
2226 m_transitions[*i3].ref_outgoing_transitions->ref.BLC_transitions = i3;
2227 }
2228 }
2229
2247 [[nodiscard]]
2249 const transition_index ti,
2250 linked_list<BLC_indicators>::iterator new_BLC_block,
2251 linked_list<BLC_indicators>::iterator old_BLC_block)
2252 { assert(new_BLC_block->is_stable());
2253 BLC_list_iterator old_position=
2254 m_transitions[ti].ref_outgoing_transitions->ref.BLC_transitions; assert(old_BLC_block->start_same_BLC <= old_position);
2255 assert(old_position<old_BLC_block->end_same_BLC);
2256 assert(new_BLC_block->end_same_BLC==old_BLC_block->start_same_BLC);
2257 assert(m_transitions[ti].transitions_per_block_to_constellation == old_BLC_block);
2258 assert(ti == *old_position);
2259 if (!old_BLC_block->is_stable())
2260 { assert(old_BLC_block->start_same_BLC<=old_BLC_block->start_marked_BLC);
2261 assert(old_BLC_block->start_marked_BLC<=old_BLC_block->end_same_BLC);
2262 if (old_BLC_block->start_marked_BLC<=old_position)
2263 { assert(m_states[m_aut.get_transitions()[ti].from()].block->
2264 contains_new_bottom_states ||
2266 (*m_states[m_aut.get_transitions()[ti].from()].block)<=1);
2267 // It's ok to unmark transitions because they start in blocks with
2268 // new bottom states, or in blocks with only 1 state.
2269 // (However, it may happen that unmarked transitions from other blocks
2270 // are moved, even if there are marked transitions in the same BLC set---
2271 // namely for the co-splitters. So we can only unmark transitions if
2272 // transition ti itself is marked.)
2273 old_BLC_block->start_marked_BLC=old_BLC_block->end_same_BLC;
2274 }
2275 }
2276 if (old_position!=old_BLC_block->start_same_BLC)
2277 {
2278 std::swap(*old_position,*old_BLC_block->start_same_BLC);
2279 m_transitions[*old_position].ref_outgoing_transitions->
2280 ref.BLC_transitions = old_position;
2281 m_transitions[*old_BLC_block->start_same_BLC].
2282 ref_outgoing_transitions->ref.BLC_transitions =
2283 old_BLC_block->start_same_BLC;
2284 }
2285 new_BLC_block->end_same_BLC=++old_BLC_block->start_same_BLC;
2286 m_transitions[ti].transitions_per_block_to_constellation=new_BLC_block;
2287//std::cerr << " to new " << new_BLC_block->debug_id(*this) << '\n';
2288 return old_BLC_block->start_same_BLC==old_BLC_block->end_same_BLC;
2289 }
2290
2309 [[nodiscard]]
2311 block_type* const index_block_B,
2312 const transition& t,
2313 const transition_index ti)
2314 { assert(m_states[t.to()].block==index_block_B);
2315//std::cerr << "update_the_doubly_linked_list_LBC_new_constellation(" << index_block_B->debug_id(*this) << ',' << m_transitions[ti].debug_id(*this) << ")\n";
2316 block_type* const from_block=m_states[t.from()].block; assert(&m_aut.get_transitions()[ti] == &t);
2317 bool new_block_created = false; assert(from_block->block.to_constellation.check_linked_list());
2318 linked_list<BLC_indicators>::iterator this_block_to_constellation=
2319 m_transitions[ti].transitions_per_block_to_constellation;
2320 #ifndef NDEBUG
2321 // Check whether this_block_to_constellation is in the corresponding list
2322 for (linked_list<BLC_indicators>::const_iterator i=from_block->block.to_constellation.begin();
2323 i!=this_block_to_constellation; ++i)
2324 {
2325 assert(i!=from_block->block.to_constellation.end());
2326 }
2327 #endif
2328 assert(this_block_to_constellation!=from_block->block.to_constellation.end());
2329 assert(this_block_to_constellation->start_same_BLC <= m_transitions[ti].ref_outgoing_transitions->ref.BLC_transitions);
2330 // the transition is never marked:
2331 assert(from_block->contains_new_bottom_states ||
2332 number_of_states_in_block(*from_block)<=1 ||
2333 this_block_to_constellation->is_stable() ||
2334 m_transitions[ti].ref_outgoing_transitions->ref.BLC_transitions<this_block_to_constellation->start_marked_BLC);
2335 linked_list<BLC_indicators>::iterator next_block_to_constellation;
2336 // if this transition is inert, it is inserted in a block in front.
2337 // Otherwise, it is inserted after the current element in the list.
2338 if (is_inert_during_init(t) && from_block==index_block_B)
2339 {
2340 next_block_to_constellation=from_block->block.to_constellation.begin(); assert(next_block_to_constellation->start_same_BLC <
2341 next_block_to_constellation->end_same_BLC);
2342 assert(m_states[m_aut.get_transitions()[*(next_block_to_constellation->start_same_BLC)].from()].block==index_block_B);
2343 assert(m_aut.is_tau(m_aut_apply_hidden_label_map(m_aut.get_transitions()[*(next_block_to_constellation->start_same_BLC)].label())));
2344 if (next_block_to_constellation==this_block_to_constellation)
2345 {
2346 // Make a new entry in the list block.to_constellation, at the
2347 // beginning;
2348//std::cerr << "Creating new BLC set for inert " << m_transitions[ti].debug_id(*this) << ": ";
2349
2350 next_block_to_constellation=from_block->block.to_constellation.
2351 emplace_front(//first_block_to_constellation,
2352 this_block_to_constellation->start_same_BLC,
2353 this_block_to_constellation->start_same_BLC,true);
2354 #if !defined(NDEBUG) || defined(COUNT_WORK_BALANCE)
2355 next_block_to_constellation->work_counter = this_block_to_constellation->work_counter;
2356 #endif
2357 } else
2358 {
2359//std::cerr << "Extending existing BLC set for inert " << m_transitions[ti].debug_id(*this) << ": ";
2360 assert(m_states[m_aut.get_transitions()[*(next_block_to_constellation->start_same_BLC)].to()].block==index_block_B);
2361 }
2362 }
2363 else
2364 {
2365 // The transition is not constellation-inert.
2366 // The transition will be placed in a BLC set immediately after the BLC
2367 // set it came from, so that main splitters (with transitions to the
2368 // new constellation) come after co-splitters (with transitions to the
2369 // old constellation).
2370
2371 // This method also ensures that transitions from the old constellation
2372 // to the old constellation will remain at the beginning of their
2373 // respective BLC set.
2374 next_block_to_constellation=from_block->
2375 block.to_constellation.next(this_block_to_constellation);
2376 const transition* first_t;
2377 if (next_block_to_constellation==
2378 from_block->block.to_constellation.end() ||
2379 (first_t=&m_aut.get_transitions()
2380 [*(next_block_to_constellation->start_same_BLC)],
2381 assert(m_states[first_t->from()].block==from_block),
2382 m_states[first_t->to()].block!=index_block_B) ||
2384 {
2385//std::cerr << "Creating new BLC set for " << m_transitions[ti].debug_id(*this) << ": ";
2386 // Make a new entry in the list next_block_to_constellation, after the current list element.
2387 new_block_created = true;
2388 next_block_to_constellation=from_block->block.to_constellation.
2389 emplace_after(this_block_to_constellation,
2390 this_block_to_constellation->start_same_BLC,
2391 this_block_to_constellation->start_same_BLC,true);
2392 #if !defined(NDEBUG) || defined(COUNT_WORK_BALANCE)
2393 /* The entry will be marked as unstable later */ next_block_to_constellation->work_counter=
2394 this_block_to_constellation->work_counter;
2395 #endif
2397 }
2398//else { std::cerr << "Extending existing BLC set for " << m_transitions[ti].debug_id(*this) << ": "; }
2399 }
2400
2402 next_block_to_constellation, this_block_to_constellation))
2403 {
2404 from_block->block.to_constellation.erase(this_block_to_constellation);
2405//std::cerr << "Erasing the old BLC set";
2406 if (!is_inert_during_init(t) ||
2407 from_block->c.onstellation!=index_block_B->c.onstellation)
2408 // i.e. if transition t is not a (formerly) inert transition (in
2409 // that case, the old BLC set would havold BLC set did *not*
2410 // contain the (formerly) inert tau-transitions from the old to the
2411 // new constellation, or the still-inert tau-transitions of the
2412 // old constellation
2413 {
2415 }
2416 }
2417//std::cerr << '\n';
2418 #ifndef NDEBUG
2419 check_transitions(false, false, false);
2420 #endif
2421 return new_block_created;
2422 }
2423
2440 [[nodiscard]]
2442 const transition_index ti,
2443 linked_list<BLC_indicators>::iterator new_BLC_block,
2444 linked_list<BLC_indicators>::iterator old_BLC_block)
2445 { assert(new_BLC_block->end_same_BLC==old_BLC_block->start_same_BLC);
2446 BLC_list_iterator old_position =
2447 m_transitions[ti].ref_outgoing_transitions->ref.BLC_transitions; assert(old_BLC_block->start_same_BLC<=old_position);
2448 assert(old_position<old_BLC_block->end_same_BLC);
2449 #ifndef NDEBUG
2450 if (!old_BLC_block->is_stable())
2451 {
2452 assert(old_BLC_block->start_same_BLC<=old_BLC_block->start_marked_BLC);
2453 assert(old_BLC_block->start_marked_BLC<=old_BLC_block->end_same_BLC);
2454 }
2455 if (!new_BLC_block->is_stable())
2456 {
2457 assert(new_BLC_block->start_same_BLC<=new_BLC_block->start_marked_BLC);
2458 assert(new_BLC_block->start_marked_BLC<=new_BLC_block->end_same_BLC);
2459 }
2460 #endif
2461 assert(m_transitions[ti].transitions_per_block_to_constellation==
2462 old_BLC_block);
2463 //assert(new block==m_states[m_aut.get_transitions()[ti].from()].block);
2464 assert(ti==*old_position);
2465 if (old_BLC_block->is_stable() ||
2466 old_position<old_BLC_block->start_marked_BLC)
2467 { assert(old_BLC_block->start_same_BLC <= old_position);
2468//std::cerr << "Moving unmarked " << m_transitions[*old_position].debug_id(*this);
2469 if (new_BLC_block->is_stable())
2470 {
2471 #ifndef NDEBUG
2472 if (!old_BLC_block->is_stable())
2473 {
2474 // different stability status: check that ti is inert
2475 const transition& t=m_aut.get_transitions()[ti];
2476 assert(is_inert_during_init(t) &&
2477 (no_of_constellations<=1 /* i.e. initialisation */ ||
2478 m_states[t.from()].block->c.onstellation==
2479 m_states[t.to()].block->c.onstellation));
2480 }
2481 #endif
2482 if (old_position!=old_BLC_block->start_same_BLC)
2483 {
2484 std::swap(*old_position, *old_BLC_block->start_same_BLC);
2485 m_transitions[*old_position].ref_outgoing_transitions->
2486 ref.BLC_transitions=old_position;
2487 m_transitions[*old_BLC_block->start_same_BLC].
2488 ref_outgoing_transitions->ref.BLC_transitions=
2489 old_BLC_block->start_same_BLC;
2490 }
2491 }
2492 else
2493 { assert(new_BLC_block->start_marked_BLC<=old_BLC_block->start_same_BLC);
2494 #ifndef NDEBUG
2495 if (old_BLC_block->is_stable())
2496 {
2497 // different stability status: check that ti is inert
2498 const transition& t=m_aut.get_transitions()[ti];
2499 assert(is_inert_during_init(t) &&
2500 (/*initialisation ||*/
2501 m_states[t.from()].block->c.onstellation==
2502 m_states[t.to()].block->c.onstellation));
2503 }
2504 #endif
2506 old_BLC_block->start_same_BLC, new_BLC_block->start_marked_BLC++);
2507 }
2508 }
2509 else
2510 { assert(old_BLC_block->start_marked_BLC <= old_position);
2511//std::cerr << "Moving marked " << m_transitions[*old_position].debug_id(*this);
2512 assert(old_BLC_block->start_same_BLC <= old_BLC_block->start_marked_BLC);
2513 assert(!new_BLC_block->is_stable());
2515 old_BLC_block->start_marked_BLC++, old_BLC_block->start_same_BLC);
2516 }
2517 m_transitions[ti].transitions_per_block_to_constellation=new_BLC_block;
2518 new_BLC_block->end_same_BLC=++old_BLC_block->start_same_BLC;
2519//std::cerr << " to new " << new_BLC_block->debug_id(*this) << '\n';
2520 return old_BLC_block->start_same_BLC==old_BLC_block->end_same_BLC;
2521 }
2522
2557 block_type* const old_bi,
2558 block_type* const new_bi,
2559 const transition_index ti,
2560 constellation_type* old_constellation,
2561 constellation_type*const new_constellation
2562 // used to maintain the order of BLC sets:
2563 // main splitter BLC sets (target constellation == new constellation) follow immediately
2564 // after co-splitter BLC sets (target constellation == old_constellation) in the BLC sets
2565 )
2566 { assert(old_bi->block.to_constellation.check_linked_list());
2567//std::cerr << "update_the_doubly_linked_list_LBC_new_block(old_bi==" << old_bi << ", new_bi==" << new_bi << ", transition_index==" << ti << "==" << m_transitions[ti].debug_id_short(*this) << ", old_constellation==" << old_constellation->debug_id(*this) << ")\n";
2568 const transition& t=m_aut.get_transitions()[ti]; assert(new_bi->block.to_constellation.check_linked_list());
2569 assert(m_states[t.from()].block==new_bi);
2570 linked_list<BLC_indicators>::iterator this_block_to_constellation=
2571 m_transitions[ti].transitions_per_block_to_constellation;
2572//std::cerr << "This transition is originally in " << this_block_to_constellation->debug_id(*this, old_bi) << '\n';
2573 #ifndef NDEBUG
2574 // Check whether this_block_to_constellation is in the corresponding list
2575 for (linked_list<BLC_indicators>::const_iterator i=old_bi->block.to_constellation.begin();
2576 i!=this_block_to_constellation; ++i)
2577 {
2578 assert(i!=old_bi->block.to_constellation.end());
2579 }
2580 #endif
2582 constellation_type* const to_constln=
2583 m_states[t.to()].block->c.onstellation;
2584 linked_list<BLC_indicators>::iterator new_BLC_block;
2585 const bool t_is_inert=is_inert_during_init(t);
2586 if (t_is_inert && to_constln==new_bi->c.onstellation)
2587 {
2588 /* Before correcting the BLC lists, we already inserted an empty */ assert(this_block_to_constellation==old_bi->block.to_constellation.begin());
2589 /* BLC_indicator into the list to take the constellation-inert */
2590 /* transitions. */ assert(!new_bi->block.to_constellation.empty());
2591 new_BLC_block=new_bi->block.to_constellation.begin(); assert(this_block_to_constellation->start_same_BLC==new_BLC_block->end_same_BLC);
2592 #ifndef NDEBUG
2593//std::cerr << "This transition remains constellation-inert and is moved to " << new_BLC_block->debug_id(*this) << '\n';
2594 if (new_BLC_block->start_same_BLC<new_BLC_block->end_same_BLC)
2595 {
2596 const transition& inert_t=m_aut.get_transitions()[*new_BLC_block->start_same_BLC];
2597 assert(new_bi==m_states[inert_t.from()].block);
2598 assert(a==label_or_divergence(inert_t));
2599 assert(to_constln==m_states[inert_t.to()].block->c.onstellation);
2600 }
2601 #endif
2602 }
2603 else
2604 {
2605 transition_index perhaps_new_BLC_block_transition;
2606 const transition* perhaps_new_BLC_t;
2607 if (this_block_to_constellation->start_same_BLC!=
2608 m_BLC_transitions.data() &&
2609 (perhaps_new_BLC_block_transition=
2610 *std::prev(this_block_to_constellation->start_same_BLC),
2611 perhaps_new_BLC_t=
2612 &m_aut.get_transitions()[perhaps_new_BLC_block_transition],
2613 m_states[perhaps_new_BLC_t->from()].block==new_bi) &&
2614 a==label_or_divergence(*perhaps_new_BLC_t) &&
2615 to_constln==m_states
2616 [perhaps_new_BLC_t->to()].block->c.onstellation)
2617 {
2618 // Found the entry where the transition should go to
2619 // Move the current transition to the new list.
2620 new_BLC_block=m_transitions[perhaps_new_BLC_block_transition].
2621 transitions_per_block_to_constellation;
2622 #ifndef NDEBUG
2623//std::cerr << "It will be moved to the existing " << new_BLC_block->debug_id(*this) << '\n';
2624 if (this_block_to_constellation->is_stable()) { assert(new_BLC_block->is_stable()); }
2625 else { assert(!new_BLC_block->is_stable()); }
2626 #endif
2627 }
2628 else
2629 {
2630 // Make a new entry in the list next_block_to_constellation;
2631
2632 // We first calculate the position where the new BLC set should go to
2633 // in new_position.
2634 // Default position: at the beginning.
2635 linked_list<BLC_indicators>::iterator new_position=
2636 new_bi->block.to_constellation.end(); assert(!is_inert_during_init(t)||to_constln!=new_bi->c.onstellation);
2637#ifndef USE_FOUR_WAY_SPLIT
2638 BLC_list_iterator_or_null old_co_splitter_end=nullptr;
2639 if (this_block_to_constellation->is_stable())
2640 {
2641//std::cerr << "Transition is in a stable BLC set.\n";
2642 if (m_branching)
2643 {
2644 // We always inserted a new BLC set for the inert transitions,
2645 // so we can place the BLC set after that one.
2646 new_position=new_bi->block.to_constellation.begin();
2647//std::cerr << " and is placed after " << new_position->debug_id(*this) << '\n';
2648 }
2649 // Because the BLC set is stable, one does not need to keep main
2650 // splitter and co-splitter together.
2651 // If the BLC set becomes empty, one can immediately delete it.
2652 old_constellation=null_constellation;
2653 }
2654 else
2655#endif
2656 {
2657 if (new_bi->block.to_constellation.empty())
2658 { assert(!m_branching);
2659 /* This is the first transition that is moved. */ assert(new_bi->block.to_constellation.end()==new_position);
2660//std::cerr << "This is the first transition that is moved.\n";
2661 }
2662 else
2663 {
2664 // default position: place it at the end of the list
2665 new_position=new_bi->block.to_constellation.before_end(); assert(new_bi->block.to_constellation.end()!=new_position);
2666 }
2667 if (null_constellation!=old_constellation)
2668 {
2669 if (t_is_inert &&
2670 ((to_constln==new_constellation &&
2671 new_bi->c.onstellation==old_constellation) ||
2672 // < The transition goes from the old constellation to
2673 // the splitter block and was constellation-inert before.
2674 // It is in a main splitter without (unstable)
2675 // co-splitter. We do not need to find the co-splitter.
2676 (to_constln==old_constellation &&
2677 new_bi->c.onstellation==new_constellation)))
2678 // < The formerly constellation-inert transition goes
2679 // from the new constellation to the old constellation,
2680 // it is in a co-splitter without (unstable) main
2681 // splitter, and this co-splitter was handled as the
2682 // first splitting action.
2683 {
2684 old_constellation=null_constellation;
2685//std::cerr << (old_constellation==to_constln ? "This transition was constellation-inert earlier, so we do not need to find a main splitter.\n" : "This transition was constellation-inert earlier, so we do not need to find a co-splitter.\n");
2686 }
2687 else
2688 { assert(old_constellation!=new_constellation);
2689 // The following comments are all formulated for the case that
2690 // this_block_to_constellation is a main splitter (except when
2691 // indicated explicitly).
2692 linked_list<BLC_indicators>::const_iterator old_co_splitter;
2693 constellation_type* co_to_constln;
2694 if ((old_constellation==to_constln &&
2695 // i.e. `this_block_to_constellation` is a co-splitter
2696 (old_co_splitter=old_bi->block.to_constellation.
2697 next(this_block_to_constellation),
2698 co_to_constln=new_constellation,
2699//(std::cerr << "Transition is originally in a co-splitter; "),
2700 true)) ||
2701 (new_constellation==to_constln &&
2702 // i.e. `this_block_to_constellation` is a main splitter
2703 (old_co_splitter=old_bi->block.to_constellation.
2704 prev(this_block_to_constellation),
2705 co_to_constln=old_constellation,
2706//(std::cerr << "Transition is originally in a main splitter; "),
2707 true)))
2708 {
2709 if (old_bi->block.to_constellation.end()!=old_co_splitter)
2710 {
2711//std::cerr << (old_constellation == to_constln ? "Current old main splitter candidate: " : "Current old co-splitter candidate: ") << old_co_splitter->debug_id(*this, old_bi);
2712 // If the co-splitter belonging to
2713 // `this_block_to_constellation` exists, then it is
2714 // `old_co_splitter` (but if there is no such co-splitter,
2715 // `old_co_splitter` could be a different main splitter, a
2716 // different co-splitter without main splitter, or a
2717 // completely unrelated splitter).
2718
2719 // Try to find out whether there is already a corresponding
2720 // co-splitter in `new_bi->block.to_constellation`
2721 // This co-splitter would be just before `old_co_splitter`
2722 // in `m_BLC_transitions`.
2723 if (new_bi->block.to_constellation.end()!=new_position &&
2724 m_BLC_transitions.data()<
2725 old_co_splitter->start_same_BLC)
2726 // i.e. this is not the first transition -- neither the
2727 // first to be moved to the new block nor the first in
2728 // m_BLC_transitions
2729 {
2730 // Check the transition in the potential corresponding
2731 // new co-splitter:
2732 const transition_index perhaps_new_co_spl_transition=
2733 *std::prev(old_co_splitter->start_same_BLC);
2734 const transition& perhaps_new_co_spl_t=
2735 m_aut.get_transitions()[perhaps_new_co_spl_transition];
2736 if(new_bi==m_states[perhaps_new_co_spl_t.from()].block &&
2737 a==label_or_divergence(perhaps_new_co_spl_t) &&
2738 co_to_constln==m_states
2739 [perhaps_new_co_spl_t.to()].block->c.onstellation)
2740 {
2741 // `perhaps_new_co_spl_transition` is in the
2742 // corresponding new co-splitter; place the new BLC set
2743 // immediately after this co-splitter in the list
2744 // `new_bi->block.to_constellation`.
2745 new_position=m_transitions
2746 [perhaps_new_co_spl_transition].
2747 transitions_per_block_to_constellation;
2748 if (old_constellation==to_constln)
2749 {
2750 // (`this_block_to_constellation` was a co-splitter:)
2751 // `perhaps_new_co_spl_transition` is in the new main
2752 // splitter; place the new BLC set immediately before
2753 // this main splitter in the list
2754 // `new_bi->block.to_constellation`.
2755 new_position=new_bi->block.to_constellation.
2756 prev(new_position);
2757//std::cerr << ". This is a real old main splitter.\n";
2758 }
2759//else { std::cerr << ". This is a real old co-splitter.\n"; }
2760 #ifndef NDEBUG
2761 /* The new co-splitter was found, and */ if (old_co_splitter->start_same_BLC<old_co_splitter->end_same_BLC)
2762 /* `old_co_splitter` must have been the old */ {
2763 /* co-splitter. */ const transition& co_t=m_aut.get_transitions()
2764 [*old_co_splitter->start_same_BLC];
2765 /* Now the new main splitter is about to be created. */ assert(old_bi==m_states[co_t.from()].block ||
2766 /* In this case it is ok to delete */ new_bi==m_states[co_t.from()].block);
2767 /* `this_block_to_constellation` when it becomes */ assert(a==label_or_divergence(co_t));
2768 /* empty; therefore we set `old_constellation` in a */ assert(co_to_constln==m_states[co_t.to()].block->c.onstellation);
2769 /* way that it's going to delete it immediately: */ }
2770 #endif
2771 old_constellation=null_constellation;
2772 // We should not use `old_constellation` for anything
2773 // else after this point.
2774 }
2775#ifdef USE_FOUR_WAY_SPLIT
2776//else { std::cerr << ". The transition just before it in m_BLC_transitions (" << m_transitions[perhaps_new_co_spl_transition].debug_id_short(*this) << ") does not belong to the new co-splitter.\n"; }
2777#else
2778 else if (new_constellation==to_constln)
2779 {
2780 const transition* co_t;
2781 if (old_co_splitter->start_same_BLC<
2782 old_co_splitter->end_same_BLC &&
2783 (co_t=&m_aut.get_transitions()
2784 [*old_co_splitter->start_same_BLC], assert(old_bi==m_states[co_t->from()].block ||
2785 new_bi==m_states[co_t->from()].block),
2786 a==label_or_divergence(*co_t)) &&
2787 old_constellation==m_states
2788 [co_t->to()].block->c.onstellation)
2789 {
2790//std::cerr << ". The transition just before it in m_BLC_transitions (" << m_transitions[perhaps_new_co_spl_transition].debug_id_short(*this) << ") does not belong to the new co-splitter.\n";
2791 old_co_splitter_end=old_co_splitter->end_same_BLC;
2792 }
2793 else
2794 {
2795//std::cerr << ". In fact, it is not a true candidate.\n";
2796 // As there is no old co-splitter, we do not need to keep an empty old main splitter.
2797 old_constellation=null_constellation;
2798 }
2799 } else
2800 {
2801 assert(old_constellation==to_constln);
2802//std::cerr << ". The transition just before it in m_BLC_transitions (" << m_transitions[perhaps_new_co_spl_transition].debug_id_short(*this) << ") does not belong to the new main splitter.\n";
2803 }
2804#endif
2805 }
2806#ifdef USE_FOUR_WAY_SPLIT
2807//else { std::cerr << ". This candidate is at the beginning of m_BLC_transitions, or it is the first transition. There is no new co-splitter yet.\n"; }
2808#else
2809 else if (new_constellation==to_constln)
2810 {
2811 const transition* co_t;
2812 if (old_co_splitter->start_same_BLC<
2813 old_co_splitter->end_same_BLC &&
2814 (co_t=&m_aut.get_transitions()
2815 [*old_co_splitter->start_same_BLC], assert(old_bi==m_states[co_t->from()].block ||
2816 new_bi==m_states[co_t->from()].block),
2817 a==label_or_divergence(*co_t)) &&
2818 old_constellation==m_states
2819 [co_t->to()].block->c.onstellation)
2820 {
2821//std::cerr << ". This candidate is at the beginning of m_BLC_transitions, or it is the first transition. There is no new co-splitter yet.\n";
2822 old_co_splitter_end=old_co_splitter->end_same_BLC;
2823 }
2824 else
2825 {
2826//std::cerr << ". In fact, it is a false candidate.\n";
2827 // As there is no old co-splitter, we do not need to
2828 // keep an empty old main splitter.
2829 old_constellation=null_constellation;
2830 }
2831 } else
2832 {
2833 assert(old_constellation==to_constln);
2834//std::cerr << ". This candidate is at the beginning of m_BLC_transitions, or it is the first transition. There is no new main splitter yet.\n";
2835 }
2836#endif
2837 }
2838 else
2839 {
2840 // this_block_to_constellation is a main splitter
2841 // but it has no corresponding co-splitter.
2842 // If it becomes empty, one can immediately delete it.
2843 old_constellation=null_constellation;
2844//std::cerr << (old_constellation == to_constln ? "There is no candidate old main splitter.\n" : "There is no candidate old co-splitter.\n");
2845 }
2846 }
2847 else
2848 {
2849 // this_block_to_constellation is neither a main splitter nor
2850 // a co-splitter. If it becomes empty, one can immediately
2851 // delete it.
2852 old_constellation=null_constellation;
2853 }
2854 }
2855 }
2856#ifdef USE_FOUR_WAY_SPLIT
2857 else if (this_block_to_constellation->is_stable())
2858 {
2859 // default position during new bottom splits: at the beginning of
2860 // the list (but after the BLC set of inert transitions)
2861 new_position=m_branching ? new_bi->block.to_constellation.begin()
2862 : new_bi->block.to_constellation.end();
2863 }
2864#endif
2865 } assert(!m_branching || new_bi->block.to_constellation.end()!=new_position);
2866 const BLC_list_iterator old_BLC_start=this_block_to_constellation->start_same_BLC;
2867 new_BLC_block=new_bi->block.to_constellation.emplace_after
2868 (new_position, old_BLC_start, old_BLC_start,
2869 this_block_to_constellation->is_stable());
2870 #if !defined(NDEBUG) || defined(COUNT_WORK_BALANCE)
2871 new_BLC_block->work_counter=this_block_to_constellation->work_counter;
2872 #endif
2874#ifndef USE_FOUR_WAY_SPLIT
2875 if (null_constellation!=old_constellation)
2876 {
2877 // This is a new main splitter without new co-splitter, or a new
2878 // co-splitter without new main splitter
2879 if (old_constellation==to_constln)
2880 {
2881 // this is a new co-splitter but we haven't yet found the new
2882 // main splitter. Perhaps we will have to stabilize it later.
2883 m_co_splitters_to_be_checked.emplace_back
2884 (new_BLC_block->start_same_BLC, new_bi);
2885//std::cerr << "We will check the stability of the new and old co-splitters later.\n";
2886 /* We may also have to stabilize the old co-splitter later. */ assert(m_BLC_transitions.data()<this_block_to_constellation->end_same_BLC);
2887 m_co_splitters_to_be_checked.emplace_back
2888 (std::prev(this_block_to_constellation->end_same_BLC), old_bi);
2889 }
2890 else
2891 { assert(new_constellation==to_constln);
2892 /* this is a new main splitter but we haven't yet found the new*/ assert(nullptr!=old_co_splitter_end);
2893 /* co-splitter. Perhaps we will have to stabilize the old */ assert(m_BLC_transitions.data()<old_co_splitter_end);
2894 /* co-splitter later. */ assert(old_co_splitter_end<=m_BLC_transitions.data_end());
2895 m_co_splitters_to_be_checked.emplace_back
2896 (std::prev(old_co_splitter_end), old_bi);
2897//std::cerr << "We will check the stability of the old co-splitter later.\n";
2898 }
2899 }
2900#endif
2901 }
2902 }
2903 const bool last_element_removed=
2905 new_BLC_block, this_block_to_constellation);
2906
2907 if (last_element_removed)
2908 {
2909//std::cerr << "This was the last element in the old BLC set, which ";
2910 if (null_constellation != old_constellation)
2911 {
2912 // Sometimes we could still remove this_block_to_constellation
2913 // immediately (namely if the new main splitter and the new
2914 // co-splitter already exist, or if the old co-splitter does not
2915 // exist at all). A few such cases are handled above, but other
2916 // cases would require additional, possibly extensive, checks:
2917 // if (co_block_found) {
2918 // copy more or less the code from above that decides
2919 // whether this_block_to_constellation is a main splitter
2920 // that has an old co-splitter but not a new co-splitter
2921 // or vice versa.
2922 // }
2923//std::cerr << "will be deleted later.";
2925 (this_block_to_constellation);
2926 }
2927 else
2928 {
2929//std::cerr << "is deleted immediately.";
2930 // Remove this element.
2931 old_bi->block.to_constellation.erase(this_block_to_constellation);
2932 }
2933 if (!t_is_inert || to_constln!=new_bi->c.onstellation)
2934 {
2936 }
2937//std::cerr << '\n';
2938 } assert(old_bi->block.to_constellation.check_linked_list());
2939 #ifndef NDEBUG
2940 assert(new_bi->block.to_constellation.check_linked_list());
2941 check_transitions(no_of_constellations<=1, false, false);
2942 #endif
2943 return;
2944 }
2945
2946#ifdef USE_FOUR_WAY_SPLIT
2953 std::vector<state_in_block_pointer>::const_iterator begin,
2954 std::vector<state_in_block_pointer>::const_iterator const end,
2955 block_type* const block)
2956 {
2957 (void) block; // avoid unused parameter warning
2958 while (begin!=end)
2959 { assert(block==begin->ref_state->block);
2960 begin->ref_state->counter=undefined;
2961 ++begin;
2962 }
2963 }
2964
2965#else
2966
2975 void clear_state_counters(bool restrict_to_R=false)
2976 {
2977 for(const state_in_block_pointer& si: m_R)
2978 { assert(Rmarked == si.ref_state->counter);
2979 // The work in this loop is accounted for by the same action that set
2980 // the counter to `Rmarked`
2981 si.ref_state->counter=undefined;
2982 }
2983 if (restrict_to_R)
2984 { assert(m_U_counter_reset_vector.empty());
2985 return;
2986 }
2987 for(const state_in_block_pointer& si: m_U_counter_reset_vector)
2988 { assert(undefined!=si.ref_state->counter || m_R.find(si));
2989 /* The work in this loop is accounted for by the action that set the */ assert(Rmarked!=si.ref_state->counter);
2990 // counter to some defined value (possibly to `Rmarked`, but then it
2991 // has already been changed back to `undefined` by the loop above).
2992 si.ref_state->counter=undefined;
2993 }
2994 clear(m_U_counter_reset_vector);
2995 }
2996
3023 template <bool initialisation=false,
3024 class Iterator=linked_list<BLC_indicators>::iterator>
3025 block_type* simple_splitB(block_type* const B,
3026 Iterator splitter,
3027 state_in_block_pointer* const first_unmarked_bottom_state,
3028 const BLC_list_iterator_or_null splitter_end_unmarked_BLC,
3029 block_type*& ptr_to_new_block)
3030 {
3031//std::cerr << "simple_splitB" << (initialisation ? "<true>(" : "(") << B->debug_id(*this) << ",...)\n";
3032 typedef enum { initializing, state_checking, aborted,
3033 aborted_after_initialisation,
3034 incoming_inert_transition_checking,
3035 outgoing_action_constellation_check } status_type; assert(1 < number_of_states_in_block(*B));
3036 status_type U_status=state_checking; assert(!B->contains_new_bottom_states);
3037 status_type R_status=initializing; assert(m_U.empty()); assert(m_U_counter_reset_vector.empty());
3038 BLC_list_iterator M_it;
3039 if constexpr (initialisation)
3040 {
3041 M_it=splitter_end_unmarked_BLC;
3042 }
3043 else
3044 { assert(splitter_end_unmarked_BLC<=splitter->end_same_BLC);
3045 M_it=splitter->start_same_BLC; assert(splitter_end_unmarked_BLC==splitter->start_marked_BLC ||
3046 splitter_end_unmarked_BLC==M_it);
3047 assert(m_BLC_transitions.data()<=M_it); assert(!splitter->is_stable());
3048 assert(splitter->start_same_BLC<splitter->end_same_BLC);
3049 assert(splitter->end_same_BLC<=m_BLC_transitions.data_end());
3050 assert(m_states[m_aut.get_transitions()
3051 [*splitter->start_same_BLC].from()].block==B);
3052 }
3053 std::vector<transition>::iterator current_U_incoming_transition_iterator;
3054 std::vector<transition>::iterator
3055 current_U_incoming_transition_iterator_end; assert(M_it <= splitter_end_unmarked_BLC);
3056 state_in_block_pointer current_U_outgoing_state;
3057 outgoing_transitions_it current_U_outgoing_transition_iterator;
3058 outgoing_transitions_it current_U_outgoing_transition_iterator_end;
3059 std::vector<transition>::iterator current_R_incoming_transition_iterator;
3060 state_in_block_pointer* current_R_incoming_bottom_state_iterator=
3061 B->start_bottom_states; assert(current_R_incoming_bottom_state_iterator<=first_unmarked_bottom_state);
3062 std::vector<transition>::iterator
3063 current_R_incoming_transition_iterator_end; assert(current_R_incoming_bottom_state_iterator<first_unmarked_bottom_state ||
3064 !m_R.empty() || M_it<splitter_end_unmarked_BLC);
3065 state_in_block_pointer* current_U_incoming_bottom_state_iterator=
3066 first_unmarked_bottom_state; assert(current_U_incoming_bottom_state_iterator<B->sta.rt_non_bottom_states);
3067 const std::make_signed<state_index>::type max_R_nonbottom_size=
3069 std::distance(B->start_bottom_states,
3070 first_unmarked_bottom_state); // can underflow
3071 if (max_R_nonbottom_size <
3072 static_cast<std::make_signed<state_index>::type>(m_R.size()))
3073 { assert(number_of_states_in_block(*B)/2 < std::distance(
3074 B->start_bottom_states, first_unmarked_bottom_state)+m_R.size());
3075 R_status = (M_it == splitter_end_unmarked_BLC)
3076 ? aborted_after_initialisation : aborted;
3077 }
3078 else
3079 { assert(number_of_states_in_block(*B)/2 >= std::distance(
3080 B->start_bottom_states, first_unmarked_bottom_state)+m_R.size());
3082 { assert(B->start_bottom_states<first_unmarked_bottom_state);
3083 /* There are no non-bottom states, hence we do not need to carry */ assert(std::distance(B->start_bottom_states,
3084 /* out the tau closure. */ first_unmarked_bottom_state) <=
3085 /* Also, there cannot be any new bottom states. */ std::distance(first_unmarked_bottom_state, B->sta.rt_non_bottom_states));
3086 assert(m_R.empty()); // m_R.clear(); is superfluous
3087 assert(m_U.empty()); // m_U.clear(); is superfluous;
3088 assert(m_U_counter_reset_vector.empty()); // clear_state_counters(); is superfluous
3089 // split_block B into R and B\R.
3090 ptr_to_new_block=split_block_B_into_R_and_BminR<initialisation>
3091 (B, B->start_bottom_states, first_unmarked_bottom_state, m_R);
3092 return ptr_to_new_block;
3093 }
3094 else if (M_it==splitter_end_unmarked_BLC)
3095 {
3096 // There are no more transitions in the splitter whose source states
3097 // need to be added to R.
3098 //assert(B->start_bottom_states < first_unmarked_bottom_state || !m_R.empty());
3099 R_status=state_checking;
3100 }
3101 }
3102 const std::make_signed<state_index>::type max_U_nonbottom_size=
3104 std::distance(first_unmarked_bottom_state,
3105 B->sta.rt_non_bottom_states); // can underflow
3106 if (max_U_nonbottom_size<0)
3107 { assert(static_cast<std::make_signed<state_index>::type>
3109 std::distance(first_unmarked_bottom_state, B->sta.rt_non_bottom_states));
3110 U_status=aborted_after_initialisation; assert(aborted != R_status); assert(aborted_after_initialisation != R_status);
3111 }
3112 else
3113 { assert(static_cast<std::make_signed<state_index>::type>
3114 (number_of_states_in_block(*B)/2) >=
3115 std::distance(first_unmarked_bottom_state, B->sta.rt_non_bottom_states));
3117 { assert(first_unmarked_bottom_state<B->sta.rt_non_bottom_states);
3118 /* There are no non-bottom states, hence we do not need to carry */ assert(std::distance(first_unmarked_bottom_state,
3119 /* out the tau closure. */ B->sta.rt_non_bottom_states) <=
3120 /* Also, there cannot be any new bottom states. */ std::distance(B->start_bottom_states, first_unmarked_bottom_state));
3121 assert(m_R.empty()); // m_R.clear(); is superfluous
3122 assert(m_U.empty()); // m_U.clear(); is superfluous;
3123 assert(m_U_counter_reset_vector.empty()); // clear_state_counters(); is superfluous
3124 // split_block B into U and B\U.
3125 ptr_to_new_block=split_block_B_into_R_and_BminR<initialisation>
3126 (B, first_unmarked_bottom_state, B->end_states, m_U);
3127 return B;
3128 }
3129 } assert(B->sta.rt_non_bottom_states<B->end_states);
3132 if constexpr (!initialisation)
3133 { assert(splitter->start_same_BLC<splitter->end_same_BLC);
3134 const transition& first_t=
3135 m_aut.get_transitions()[*splitter->start_same_BLC];
3136 a=label_or_divergence(first_t);
3137 C=m_states[first_t.to()].block->c.onstellation;
3138 } else{assert(state_checking==R_status||aborted_after_initialisation==R_status);}
3139 #ifndef NDEBUG
3140 if constexpr (!initialisation)
3141 {
3142 if (initializing==R_status || aborted==R_status)
3143 {
3144 // For constellation-inert splitters (which only happens for the special
3145 /* Algorithm 3, line 3.2 left. */ // split to separate new from old bottom states), one has to mark all
3146 // transitions in the splitter.
3147 /* start coroutines. Each co-routine handles one state, and then gives */ assert(!m_aut.is_tau(a) || B->c.onstellation != C);
3148 /* control to the other co-routine. The coroutines can be found */ }
3149 /* sequentially below surrounded by a while loop. */ }
3150 #endif
3151//std::cerr << "simple_splitB() before while\n";
3152 while (true)
3153 { assert(U_status!=aborted_after_initialisation ||
3154 (R_status!=aborted && R_status!=aborted_after_initialisation));
3155 #ifndef NDEBUG
3156 for(state_in_block_pointer si=state_in_block_pointer(m_states.begin()); si.ref_state<m_states.end(); ++si.ref_state)
3157 {
3158 if (si.ref_state->block!=B || 0==si.ref_state->no_of_outgoing_block_inert_transitions)
3159 {
3160 assert((initialisation &&
3161 0!=si.ref_state->no_of_outgoing_block_inert_transitions) ||
3162 undefined==si.ref_state->counter);
3163 assert(!m_R.find(si));
3164 assert(!m_U.find(si));
3165 assert(std::find(m_U_counter_reset_vector.begin(), m_U_counter_reset_vector.end(), si) == m_U_counter_reset_vector.end());
3166 }
3167 else
3168 {
3169 switch(si.ref_state->counter)
3170 {
3171 case undefined: assert(!m_U.find(si)); assert(!m_R.find(si));
3172 assert(std::find(m_U_counter_reset_vector.begin(), m_U_counter_reset_vector.end(), si) == m_U_counter_reset_vector.end());
3173 break;
3174 case Rmarked: assert( m_R.find(si)); assert(!m_U.find(si));
3175 // It can happen that the state has a tau-transition
3176 // to a U-state, then it will be in the
3177 // m_U_counter_reset_vector.
3178 break;
3179 case Umarked: assert(!m_R.find(si)); // It can happen that the state
3180 // is in U or is not in U
3181 assert(std::find(m_U_counter_reset_vector.begin(), m_U_counter_reset_vector.end(), si) != m_U_counter_reset_vector.end());
3182 break;
3183 default: assert(!m_R.find(si)); assert(!m_U.find(si));
3184 assert(std::find(m_U_counter_reset_vector.begin(), m_U_counter_reset_vector.end(), si) != m_U_counter_reset_vector.end());
3185 break;
3186 }
3187 }
3188 /* The code for the right co-routine. */ }
3189 #endif
3190 if (incoming_inert_transition_checking==R_status) // 18178 times (large 1394-fin.lts example: 372431 times)
3191 { assert(current_R_incoming_transition_iterator <
3192 current_R_incoming_transition_iterator_end);
3193 mCRL2complexity(&m_transitions[std::distance(m_aut.get_transitions().begin(), current_R_incoming_transition_iterator)],
3195 assert(m_aut.is_tau(m_aut_apply_hidden_label_map(current_R_incoming_transition_iterator->label())));
3196 assert(m_states[current_R_incoming_transition_iterator->to()].block==B);
3197 const transition& tr=*current_R_incoming_transition_iterator;
3198 if (m_states[tr.from()].block==B &&
3199 !(m_preserve_divergence && tr.from() == tr.to()))
3200 {
3201 if (m_states[tr.from()].counter!=Rmarked)
3202 { assert(!m_R.find(m_states.begin() + tr.from()));
3203//std::cerr << "R_todo2 insert: " << tr.from() << "\n";
3204 m_R.add_todo(m_states.begin() + tr.from());
3205 m_states[tr.from()].counter=Rmarked;
3206
3207 // Algorithm 3, line 3.10 and line 3.11, right.
3208 if (m_R.size()>static_cast<state_index>(max_R_nonbottom_size))
3209 { assert(aborted_after_initialisation!=U_status);
3210 R_status=aborted_after_initialisation;
3211 goto R_handled_and_is_not_state_checking;
3212 }
3213 } else { assert(m_R.find(m_states.begin() + tr.from())); }
3214 assert(!m_U.find(m_states.begin() + tr.from()));
3215 }
3216 ++current_R_incoming_transition_iterator;
3217 if (current_R_incoming_transition_iterator!=
3218 current_R_incoming_transition_iterator_end &&
3220 (current_R_incoming_transition_iterator->label())))
3221 {
3222 goto R_handled_and_is_not_state_checking;
3223 }
3224 R_status=state_checking;
3225 }
3226 else if (state_checking==R_status) // 18014 times (large 1394-fin.lts example: 331708 times)
3227 {
3228 const state_in_block_pointer s=
3229 (current_R_incoming_bottom_state_iterator<
3230 first_unmarked_bottom_state
3231 ? *current_R_incoming_bottom_state_iterator++
3232 : m_R.move_from_todo());
3234//std::cerr << "R insert: " << s.ref_state->debug_id(*this) << "\n";
3235 assert(s.ref_state->block==B);
3236 if (std::next(s.ref_state)==m_states.end())
3237 {
3238 current_R_incoming_transition_iterator_end=
3239 m_aut.get_transitions().end();
3240 }
3241 else
3242 {
3243 current_R_incoming_transition_iterator_end=
3244 std::next(s.ref_state)->start_incoming_transitions;
3245 }
3246 current_R_incoming_transition_iterator=
3247 s.ref_state->start_incoming_transitions;
3248 if (current_R_incoming_transition_iterator!=
3249 current_R_incoming_transition_iterator_end &&
3251 (current_R_incoming_transition_iterator->label())))
3252 {
3253 R_status=incoming_inert_transition_checking;
3254 goto R_handled_and_is_not_state_checking;
3255 }
3256 }
3257 else if (initializing!=R_status)
3258 {
3259 assert(aborted_after_initialisation==R_status || // 10280 times (large 1394-fin.lts example: 550 times)
3260 aborted==R_status); // 663 times (large 1394-fin.lts example: 584 times)
3261 goto R_handled_and_is_not_state_checking;
3262 }
3263 else // initializing==R_status: 2742 times (large 1394-fin.lts example: 1200 times)
3264 { assert(M_it<splitter_end_unmarked_BLC);
3265 // Algorithm 3, line 3.3, right.
3267 si(m_states.begin()+m_aut.get_transitions()[*M_it].from()); mCRL2complexity(&m_transitions[*M_it], add_work(check_complexity::
3268 simple_splitB_R__handle_transition_from_R_state, 1), *this);
3269 assert(si.ref_state->block==B);
3270 assert(!is_inert_during_init(m_aut.get_transitions()[*M_it]) ||
3271 B->c.onstellation!=
3272 m_states[m_aut.get_transitions()[*M_it].to()].block->c.onstellation);
3273 ++M_it;
3274 if (0==si.ref_state->no_of_outgoing_block_inert_transitions)
3275 { assert(B->start_bottom_states<=si.ref_state->ref_states_in_blocks);
3276 /* The state is a bottom state, it should be in R already */ assert(si.ref_state->ref_states_in_blocks<first_unmarked_bottom_state);
3277 assert(!m_R.find(si));
3278 }
3279 else if (si.ref_state->counter!=Rmarked)
3280 { assert(B->sta.rt_non_bottom_states<=si.ref_state->ref_states_in_blocks);
3281 /* The state is a nonbottom state that is not yet in R */ assert(si.ref_state->ref_states_in_blocks<B->end_states);
3282 m_R.add_todo(si);
3283 si.ref_state->counter=Rmarked;
3284//std::cerr << "R_todo1 insert: " << si.ref_state->debug_id(*this) << "\n";
3285 if (m_R.size()>static_cast<state_index>(max_R_nonbottom_size))
3286 { assert(aborted_after_initialisation!=U_status);
3287 R_status=aborted;
3288 goto R_handled_and_is_not_state_checking;
3289 }
3290 } else { assert(m_R.find(si)); }
3291 assert(!m_U.find(si));
3292 if (M_it!=splitter_end_unmarked_BLC)
3293 {
3294 goto R_handled_and_is_not_state_checking;
3295 } assert(B->sta.rt_non_bottom_states<B->end_states);
3296 R_status=state_checking;
3297 } assert(state_checking==R_status);
3298 if (current_R_incoming_bottom_state_iterator==
3299 first_unmarked_bottom_state && m_R.todo_is_empty())
3300 { assert(0 < std::distance(B->start_bottom_states,
3301 first_unmarked_bottom_state) + m_R.size());
3302//std::cerr << "R empty: " << "\n";
3303 /* split_block B into R and B\R. */ assert(std::distance(B->start_bottom_states, first_unmarked_bottom_state) +
3304 m_R.size() <= number_of_states_in_block(*B)/2);
3305 ptr_to_new_block=split_block_B_into_R_and_BminR
3306 <initialisation>(B, B->start_bottom_states,
3307 first_unmarked_bottom_state, m_R);
3309 m_R.clear();
3310 m_U.clear();
3311 return ptr_to_new_block;
3312 }
3313 R_handled_and_is_not_state_checking: assert(state_checking!=R_status ||
3314 current_R_incoming_bottom_state_iterator!=first_unmarked_bottom_state ||
3315 !m_R.todo_is_empty());
3316 #ifndef NDEBUG
3318 si.ref_state<m_states.end(); ++si.ref_state)
3319 {
3320 if (si.ref_state->block!=B ||
3321 0==si.ref_state->no_of_outgoing_block_inert_transitions)
3322 {
3323 assert((initialisation &&
3324 0!=si.ref_state->no_of_outgoing_block_inert_transitions) ||
3325 undefined==si.ref_state->counter);
3326 assert(!m_R.find(si));
3327 assert(!m_U.find(si));
3328 assert(std::find(m_U_counter_reset_vector.begin(),
3329 m_U_counter_reset_vector.end(), si)==m_U_counter_reset_vector.end());
3330 }
3331 else
3332 {
3333 switch(si.ref_state->counter)
3334 {
3335 case undefined: assert(!m_U.find(si)); assert(!m_R.find(si));
3336 assert(std::find(m_U_counter_reset_vector.begin(), m_U_counter_reset_vector.end(), si) == m_U_counter_reset_vector.end());
3337 break;
3338 case Rmarked: assert( m_R.find(si)); assert(!m_U.find(si));
3339 // It can happen that the state has a tau-transition
3340 // to a U-state, then it will be in the
3341 // m_U_counter_reset_vector.
3342 break;
3343 case Umarked: assert(!m_R.find(si)); // It can happen that the state
3344 // is in U or is not in U
3345 assert(std::find(m_U_counter_reset_vector.begin(), m_U_counter_reset_vector.end(), si) != m_U_counter_reset_vector.end());
3346 break;
3347 default: assert(!m_R.find(si)); assert(!m_U.find(si));
3348 assert(std::find(m_U_counter_reset_vector.begin(), m_U_counter_reset_vector.end(), si) != m_U_counter_reset_vector.end());
3349 break;
3350 }
3351 }
3352 }
3353 #endif
3354 // The code for the left co-routine.
3355 if (incoming_inert_transition_checking==U_status) // 20299 times (large 1394-fin.lts example: 360327 times)
3356 { assert(current_U_incoming_transition_iterator<
3357 current_U_incoming_transition_iterator_end);
3358//std::cerr << "U_incoming_inert_transition_checking\n";
3359 /* Algorithm 3, line 3.8, left. */ assert(m_aut.is_tau(m_aut_apply_hidden_label_map
3360 (current_U_incoming_transition_iterator->label())));
3361 /* Check one incoming transition. */ mCRL2complexity(&m_transitions[std::distance(m_aut.get_transitions().begin(),
3362 /* Algorithm 3, line 3.12, left. */ current_U_incoming_transition_iterator)],
3363 add_work(check_complexity::
3364 simple_splitB_U__handle_transition_to_U_state, 1), *this);
3365 current_U_outgoing_state=state_in_block_pointer(m_states.begin()+
3366 current_U_incoming_transition_iterator->from()); assert(m_states[current_U_incoming_transition_iterator->to()].block==B);
3367 current_U_incoming_transition_iterator++;
3368//std::cerr << "FROM " << std::distance(m_states.begin(), current_U_outgoing_state.ref_state) << "\n";
3369 if (current_U_outgoing_state.ref_state->block==B &&
3371 std::prev(current_U_incoming_transition_iterator)->from()==
3372 std::prev(current_U_incoming_transition_iterator)->to()))
3373 { assert(!m_U.find(current_U_outgoing_state));
3374 if (current_U_outgoing_state.ref_state->counter!=Rmarked)
3375 {
3376 if (current_U_outgoing_state.ref_state->counter==undefined)
3377 {
3378 // Algorithm 3, line 3.13, left.
3379 // Algorithm 3, line 3.15 and 3.18, left.
3380 current_U_outgoing_state.ref_state->counter=Umarked-1+
3381 current_U_outgoing_state.ref_state->
3382 no_of_outgoing_block_inert_transitions;
3383 m_U_counter_reset_vector.push_back(current_U_outgoing_state);
3384 }
3385 else
3386 { assert(std::find(m_U_counter_reset_vector.begin(),
3387 m_U_counter_reset_vector.end(),
3388 current_U_outgoing_state) != m_U_counter_reset_vector.end());
3389 /* Algorithm 3, line 3.18, left. */ assert(current_U_outgoing_state.ref_state->counter>Umarked);
3390 current_U_outgoing_state.ref_state->counter--;
3391 }
3392//std::cerr << "COUNTER " << current_U_outgoing_state.ref_state->counter << "\n";
3393 // Algorithm 3, line 3.19, left.
3394 if (current_U_outgoing_state.ref_state->counter==Umarked)
3395 {
3396 if (initializing==R_status || aborted==R_status)
3397 {
3398 // Start searching for an outgoing transition with action a
3399 // to constellation C.
3400 current_U_outgoing_transition_iterator=
3401 current_U_outgoing_state.ref_state->
3402 start_outgoing_transitions; assert(m_outgoing_transitions.begin()<=current_U_outgoing_transition_iterator);
3403 assert(current_U_outgoing_transition_iterator<m_outgoing_transitions.end());
3404 current_U_outgoing_transition_iterator_end=
3405 (std::next(current_U_outgoing_state.ref_state)>=
3406 m_states.end()
3408 : std::next(current_U_outgoing_state.ref_state)->
3409 start_outgoing_transitions); assert(current_U_outgoing_transition_iterator<
3410 current_U_outgoing_transition_iterator_end);
3411 assert(m_states.begin()+m_aut.get_transitions()[*current_U_outgoing_transition_iterator->ref.BLC_transitions].from()==current_U_outgoing_state.ref_state);
3412 U_status=outgoing_action_constellation_check;
3413 goto U_handled_and_is_not_state_checking;
3414 }
3415 // The state can be added to U_todo immediately.
3416 #ifndef NDEBUG
3417 if constexpr (!initialisation)
3418 {
3419 // check that the state has no transition in the splitter
3420 const outgoing_transitions_it out_it_end =
3421 std::next(current_U_outgoing_state.ref_state)>=m_states.end()
3423 : std::next(current_U_outgoing_state.ref_state)->
3424 start_outgoing_transitions;
3425 for (outgoing_transitions_it out_it=current_U_outgoing_state.ref_state->
3426 start_outgoing_transitions;
3427 out_it<out_it_end; ++out_it)
3428 {
3429 assert(m_outgoing_transitions.begin()<=out_it);
3430 assert(out_it<m_outgoing_transitions.end());
3431 assert(m_BLC_transitions.data()<=out_it->ref.BLC_transitions);
3432 assert(out_it->ref.BLC_transitions<m_BLC_transitions.data_end());
3433 /* assert(0<=*out_it->ref.BLC_transitions); Always true */
3434 assert(*out_it->ref.BLC_transitions<m_aut.num_transitions());
3435 assert(m_transitions[*out_it->ref.BLC_transitions].ref_outgoing_transitions==out_it);
3436 const transition& t=m_aut.get_transitions()[*out_it->ref.BLC_transitions];
3437 assert(m_states.begin()+t.from()==current_U_outgoing_state.ref_state);
3438 if (a==label_or_divergence(t) &&
3439 C==m_states[t.to()].block->c.onstellation)
3440 {
3441 // The transition is in the splitter, so it must be in the part of the splitter that is disregarded.
3442//std::cerr << "State " << std::distance(m_states.begin(), current_U_outgoing_state.ref_state) << " has a transition in the splitter, namely " << m_transitions[*out_it->ref.BLC_transitions].debug_id_short(*this) << '\n';
3443 assert(out_it->ref.BLC_transitions>=splitter_end_unmarked_BLC);
3444 assert(splitter->start_same_BLC<=out_it->ref.BLC_transitions);
3445 assert(out_it->ref.BLC_transitions<splitter->end_same_BLC);
3446 }
3447 }
3448 }
3449 #endif
3450 m_U.add_todo(current_U_outgoing_state);
3451 // Algorithm 3, line 3.10 and line 3.11 left.
3452 if (m_U.size()>static_cast<state_index>(max_U_nonbottom_size))
3453 { assert(aborted!=R_status); assert(aborted_after_initialisation!=R_status);
3454 U_status=aborted_after_initialisation;
3455 goto U_handled_and_is_not_state_checking;
3456 }
3457 }
3458 } else { assert(m_R.find(current_U_outgoing_state)); }
3459 }
3460 if (current_U_incoming_transition_iterator!=
3461 current_U_incoming_transition_iterator_end &&
3463 current_U_incoming_transition_iterator->label())))
3464 { assert(incoming_inert_transition_checking==U_status);
3465 goto U_handled_and_is_not_state_checking;
3466 }
3467 U_status=state_checking;
3468 }
3469 else if (state_checking==U_status) // 18920 times (large 1394-fin.lts example: 340893 times)
3470 {
3471//std::cerr << "U_state_checking\n";
3472
3473 // Algorithm 3, line 3.23 and line 3.24, left.
3474 const state_in_block_pointer s=
3475 (current_U_incoming_bottom_state_iterator<
3476 B->sta.rt_non_bottom_states
3477 ? *current_U_incoming_bottom_state_iterator++
3478 : m_U.move_from_todo()); assert(!m_R.find(s));
3480//std::cerr << "U insert/ U_todo_remove: " << s.ref_state->debug_id(*this) << "\n";
3481 current_U_incoming_transition_iterator=
3482 s.ref_state->start_incoming_transitions;
3483 current_U_incoming_transition_iterator_end=
3484 (std::next(s.ref_state)>=m_states.end()
3485 ? m_aut.get_transitions().end()
3486 : std::next(s.ref_state)->start_incoming_transitions);
3487 if (current_U_incoming_transition_iterator!=
3488 current_U_incoming_transition_iterator_end &&
3490 current_U_incoming_transition_iterator->label())))
3491 {
3492 U_status=incoming_inert_transition_checking;
3493 goto U_handled_and_is_not_state_checking;
3494 }
3495 }
3496 else if (initialisation ||
3497 aborted_after_initialisation==U_status) // 6284 times (large 1394-fin.lts example: 2500 times)
3498 { assert(aborted_after_initialisation==U_status);
3499 goto U_handled_and_is_not_state_checking;
3500 }
3501 else if constexpr (!initialisation) // the condition holds always
3502 { assert(outgoing_action_constellation_check==U_status); // 911 times (large 1394-fin.lts example: 912 times)
3503//std::cerr << "U_outgoing_action_constellation_check\n";
3504 assert(current_U_outgoing_transition_iterator!=
3505 current_U_outgoing_transition_iterator_end);
3506 // will only be used if the transitions are not constellation-inert:
3507 assert(!m_aut.is_tau(a) || B->c.onstellation!=C);
3508 assert(splitter_end_unmarked_BLC==splitter->start_marked_BLC);
3509 mCRL2complexity((&m_transitions[*current_U_outgoing_transition_iterator->ref.BLC_transitions]), add_work(check_complexity::simple_splitB_U__handle_transition_from_potential_U_state, 1), *this);
3510 #ifndef NDEBUG
3511 // This is one step in the coroutine, so we should assign the work to exactly one transition.
3512 // But to make sure, we also mark the other transitions that we skipped in the optimisation.
3513 for (outgoing_transitions_it out_it=current_U_outgoing_transition_iterator; out_it<current_U_outgoing_transition_iterator->start_same_saC; )
3514 {
3515 ++out_it;
3516 mCRL2complexity(&m_transitions[*out_it->ref.BLC_transitions], add_work_notemporary(check_complexity::simple_splitB_U__handle_transition_from_potential_U_state, 1), *this);
3517 }
3518 #endif
3519 const transition& t_local=m_aut.get_transitions()
3520 [*current_U_outgoing_transition_iterator->ref.BLC_transitions];
3521 current_U_outgoing_transition_iterator= // This is an optimisation.
3522 current_U_outgoing_transition_iterator->start_same_saC;
3523 ++current_U_outgoing_transition_iterator; assert(m_states.begin()+t_local.from()==current_U_outgoing_state.ref_state);
3524 assert(m_branching);
3525 if (m_states[t_local.to()].block->c.onstellation==C &&
3526 label_or_divergence(t_local) == a)
3527 {
3528 // This state must be blocked.
3529 }
3530 else if (current_U_outgoing_transition_iterator==
3531 current_U_outgoing_transition_iterator_end)
3532 { assert(!m_U.find(current_U_outgoing_state));
3533//std::cerr << "U_todo4 insert: " << std::distance(m_states.begin(), current_U_outgoing_state.ref_state) << " " << m_U.size() << " " << number_of_states_in_block(*B) << "\n";
3534 m_U.add_todo(current_U_outgoing_state);
3535 // Algorithm 3, line 3.10 and line 3.11 left.
3536 if (m_U.size()>static_cast<state_index>(max_U_nonbottom_size))
3537 { assert(aborted!=R_status); assert(aborted_after_initialisation!=R_status);
3538 U_status=aborted_after_initialisation;
3539 goto U_handled_and_is_not_state_checking;
3540 }
3541 }
3542 else
3543 {
3544 goto U_handled_and_is_not_state_checking;
3545 }
3546
3547 if (current_U_incoming_transition_iterator!=
3548 current_U_incoming_transition_iterator_end &&
3550 current_U_incoming_transition_iterator->label())))
3551 {
3552 U_status = incoming_inert_transition_checking;
3553 goto U_handled_and_is_not_state_checking;
3554 }
3555 U_status=state_checking;
3556 } else { assert(0); }
3557 assert(state_checking==U_status);
3558 if (current_U_incoming_bottom_state_iterator==
3559 B->sta.rt_non_bottom_states && m_U.todo_is_empty())
3560 {
3561 // split_block B into U and B\U.
3562//std::cerr << "U_todo empty:\n";
3563 assert(0 < std::distance(first_unmarked_bottom_state,
3564 B->sta.rt_non_bottom_states) + m_U.size());
3565 assert(std::distance(first_unmarked_bottom_state, B->sta.rt_non_bottom_states)+
3566 m_U.size() <= number_of_states_in_block(*B)/2);
3567 ptr_to_new_block=split_block_B_into_R_and_BminR<initialisation>
3568 (B, first_unmarked_bottom_state, B->sta.rt_non_bottom_states, m_U);
3570 m_R.clear();
3571 m_U.clear();
3572 return B;
3573 }
3574 U_handled_and_is_not_state_checking: assert(state_checking!=U_status ||
3575 current_U_incoming_bottom_state_iterator!=B->sta.rt_non_bottom_states ||
3576 !m_U.todo_is_empty());
3577 } assert(0);
3578 }
3579
3580 // Mark all states that are sources of the marked transitions in splitter
3581 // and move them to the beginning.
3582 // (Marking of bottom states is done by moving them to the beginning of the
3583 // bottom states. Marking of non-bottom states is done by adding them to m_R
3584 // and setting m_states[*].counter = Rmarked.)
3585 // Also make the splitter stable and move it to the beginning of the respective
3586 // list of BLC sets.
3587 // If all bottom states are marked, reset markers and m_R.
3588 // Otherwise, if not all
3589 // bottom states are touched, leave the marked non-bottom states in m_R and leave
3590 // the markers in m_states[*].counter in place.
3591 // The marked bottom states are moved to the front in m_states_in_blocks and
3592 // the return value indicates the position (in m_states_in_blocks)
3593 // of the first non-marked bottom state.
3594 [[nodiscard]]
3596 not_all_bottom_states_are_touched(
3597 block_type* const bi,
3598 linked_list<BLC_indicators>::iterator splitter
3599 #ifndef NDEBUG
3600 , const BLC_list_const_iterator splitter_end_unmarked_BLC
3601 // = splitter->start_marked_BLC -- this default argument is not allowed
3602 #endif
3603 )
3604 { assert(!splitter->is_stable());
3605//std::cerr << "not_all_bottom_states_are_touched(" << bi << ',' << splitter->debug_id(*this) << ")\n";
3606 assert(bi==
3607 m_states[m_aut.get_transitions()[*splitter->start_same_BLC].from()].block);
3608 assert(splitter_end_unmarked_BLC<=splitter->start_marked_BLC);
3609 block_type& B=*bi; assert(m_R.empty()); assert(m_U.empty());
3610 assert(1 < number_of_states_in_block(B));
3611 // If the above assertion is false, one can just: return B.end_states;
3612 state_in_block_pointer*first_unmarked_bottom_state=B.start_bottom_states; assert(!B.contains_new_bottom_states);
3613 // Now go through the marked transitions in detail:
3614 BLC_list_iterator marked_t_it = splitter->start_marked_BLC;
3615 for(; marked_t_it<splitter->end_same_BLC; ++marked_t_it)
3616 {
3617 const transition& t = m_aut.get_transitions()[*marked_t_it]; // mCRL2complexity(&m_transitions[*marked_t_it], add_work(...), *this);
3618 // not needed as this work can be attributed to marking of the transition
3619 const state_in_block_pointer s(m_states.begin()+t.from());
3620 state_in_block_pointer* const pos_s=s.ref_state->ref_states_in_blocks; assert(B.start_bottom_states<=pos_s); assert(pos_s<B.end_states);
3621 if (first_unmarked_bottom_state<=pos_s)
3622 {
3623 if (0==s.ref_state->no_of_outgoing_block_inert_transitions)
3624 { assert(pos_s<B.sta.rt_non_bottom_states);
3625 swap_states_in_states_in_block(first_unmarked_bottom_state, pos_s); assert(undefined==s.ref_state->counter);
3626 first_unmarked_bottom_state++;
3627 }
3628 else if (Rmarked!=s.ref_state->counter)
3629 { assert(B.sta.rt_non_bottom_states<=pos_s);
3630 m_R.add_todo(s); assert(undefined==s.ref_state->counter);
3631 s.ref_state->counter=Rmarked;
3632 }
3633 }
3634 }
3635 #ifndef NDEBUG
3636 // ensure that the unmarked transitions do not add any bottom states.
3637 assert(splitter->start_same_BLC <= splitter_end_unmarked_BLC);
3638 for(BLC_list_const_iterator i = splitter->start_same_BLC; i < splitter_end_unmarked_BLC; ++i)
3639 {
3640 assert(splitter_end_unmarked_BLC == splitter->start_marked_BLC);
3641 const transition& t = m_aut.get_transitions()[*i];
3642 const state_in_block_pointer s(m_states.begin()+t.from());
3643 assert(s.ref_state->block == bi);
3644 const state_in_block_pointer*const pos_s=s.ref_state->ref_states_in_blocks;
3645 assert(*pos_s==s);
3646 assert(B.start_bottom_states <= pos_s);
3647 assert(pos_s < B.end_states);
3648 if (0==s.ref_state->no_of_outgoing_block_inert_transitions)
3649 {
3650 // State s is a bottom state. It should already have been marked.
3651 assert(pos_s<first_unmarked_bottom_state);
3652 }
3653 else
3654 {
3655 assert(B.sta.rt_non_bottom_states <= pos_s);
3656 }
3657 }
3658 #endif
3659 if (first_unmarked_bottom_state==B.sta.rt_non_bottom_states)
3660 {
3661 // All bottom states are marked. No splitting is possible. Reset m_R,
3662 // m_states[s].counter for s in m_R.
3664 m_R.clear();
3665 }
3666 return first_unmarked_bottom_state;
3667 }
3668
3672 void make_transition_non_block_inert(const transition& t)
3673 { assert(is_inert_during_init(t));
3674 assert(m_states[t.to()].block!=m_states[t.from()].block);
3675 m_states[t.from()].no_of_outgoing_block_inert_transitions--; assert(no_of_constellations<=1 /* initialisation */ ||
3676 m_states[t.to()].block->c.onstellation ==
3677 m_states[t.from()].block->c.onstellation);
3678//std::cerr << " " << m_transitions[std::distance<const transition *>(m_aut.get_transitions().data(), &t)].debug_id(*this) << " has become non-block-inert\n";
3679 }
3680#endif
3681
3687 { assert(m_states.begin()<=si);
3688//std::cerr << "change_non_bottom_state_to_bottom_state(" << si->debug_id(*this) << ")\n";
3689 block_type* bi = si->block; assert(si<m_states.end());
3690 swap_states_in_states_in_block(si->ref_states_in_blocks,
3691 bi->sta.rt_non_bottom_states); assert(0 == si->no_of_outgoing_block_inert_transitions);
3694 }
3695
3698 const linked_list<BLC_indicators>::iterator splitter)
3699 { assert(from_block->block.to_constellation.end()!=splitter);
3700 splitter->make_stable(); assert(splitter->start_same_BLC<splitter->end_same_BLC);
3701 #ifndef NDEBUG
3702 const transition& t=m_aut.get_transitions()[*splitter->start_same_BLC];
3703 assert(from_block==m_states[t.from()].block);
3704 #endif
3705 linked_list<BLC_indicators>& btc=from_block->block.to_constellation; assert(!btc.empty());
3706 if (splitter!=btc.begin())
3707 {
3708 linked_list<BLC_indicators>::iterator move_splitter_after=btc.end();
3709 if (m_branching)
3710 { // The following assertion may fail because we sometimes make a splitter
3711 const transition& perhaps_inert_t= // stable before all BLC sets are split:
3712 m_aut.get_transitions()[*btc.begin()->start_same_BLC]; // assert(m_states[perhaps_inert_t.from()].block==from_block);
3713 if (is_inert_during_init_if_branching(perhaps_inert_t) &&
3714 m_states[perhaps_inert_t.to()].block->c.onstellation==
3715 from_block->c.onstellation)
3716 {
3717 move_splitter_after=btc.begin();
3718 }
3719 }
3720 btc.splice_to_after(move_splitter_after, btc, splitter);
3721 }
3722 }
3723
3724#ifndef USE_FOUR_WAY_SPLIT
3728 template<bool initialisation, class Iterator>
3729 inline constellation_type* target_constellation(Iterator splitter) const
3730 {
3731 if constexpr (initialisation)
3732 { assert(0);
3733 return nullptr;
3734 }
3735 else
3736 {
3737 return m_states[m_aut.get_transitions()
3738 [*splitter->start_same_BLC].to()].block->c.onstellation;
3739 }
3740 }
3741
3766 template <bool initialisation=false,
3767 class Iterator=linked_list<BLC_indicators>::iterator>
3768 block_type* splitB(block_type* const B,
3769 Iterator splitter,
3770 state_in_block_pointer* first_unmarked_bottom_state,
3771 const BLC_list_iterator splitter_end_unmarked_BLC
3772 /* =splitter->start_marked_BLC -- but
3773 this default argument is not allowed */,
3774 block_type*& bi,
3775 constellation_type* const old_constellation,
3776 constellation_type* const new_constellation,
3777 const bool split_off_new_bottom_states=true)
3778 {
3779//std::cerr << (initialisation ? "splitB<true>(" : "splitB(") << B->debug_id(*this) << ", splitter==";
3780//if constexpr (!initialisation) { std::cerr << splitter->debug_id(*this); }
3781//else { std::cerr << splitter; }
3782//std::cerr << ", first_unmarked_bottom_state==" << first_unmarked_bottom_state->ref_state->debug_id(*this) << ", splitter_end_unmarked_BLC==";
3783//if constexpr (!initialisation) { std::cerr << (split_off_new_bottom_states && splitter_end_unmarked_BLC == splitter->start_marked_BLC ? "start_marked_BLC" : (splitter_end_unmarked_BLC == splitter->start_same_BLC ? "start_same_BLC" : "?")); }
3784//else { std::cerr << splitter_end_unmarked_BLC; }
3785//std::cerr << ", old_constellation==" << old_constellation->debug_id(*this) << ", split_off_new_bottom_states==" << split_off_new_bottom_states << ")\n";
3786//std::cerr << (m_branching ? "Marked bottom states:" : "Marked states:"); for (const state_in_block_pointer* it=B->start_bottom_states; it!=first_unmarked_bottom_state; ++it) { std::cerr << ' ' << std::distance(m_states.begin(), it->ref_state); }
3787//std::cerr << (m_branching ? "\nUnmarked bottom states:" : "\nUnmarked states:"); for (const state_in_block_pointer* it=first_unmarked_bottom_state; it!=B->sta.rt_non_bottom_states; ++it) { std::cerr << ' ' << std::distance(m_states.begin(), it->ref_state); } std::cerr << '\n';
3788//if (m_branching) { std::cerr << "Additionally, " << m_R.size() << " non-bottom states have been marked.\n"; }
3789 if constexpr (initialisation) {
3790 assert(no_of_constellations==1);
3791 assert(null_constellation==old_constellation);
3792 assert(!split_off_new_bottom_states);
3793 } else {
3794 assert(split_off_new_bottom_states ||
3795 B->block.to_constellation.begin()==splitter);
3796 }
3797 if constexpr (!initialisation) {
3798 assert(m_states[m_aut.get_transitions()[*splitter->start_same_BLC].from()].
3799 block==B);
3800 assert(!splitter->is_stable());
3801 assert(!split_off_new_bottom_states ||
3802 splitter_end_unmarked_BLC==splitter->start_marked_BLC);
3803 }
3804 if (1 >= number_of_states_in_block(*B))
3805 {
3806 mCRL2log(log::debug) << "Trying to split up singleton block "<<B<<'\n';
3808 return null_block;
3809 }
3810 block_type* R_block=simple_splitB<initialisation, Iterator>(B, splitter,
3811 first_unmarked_bottom_state, splitter_end_unmarked_BLC, bi); assert(bi == R_block || B == R_block);
3812//std::cerr << "Split block of size " << number_of_states_in_block(*B) + number_of_states_in_block(*bi) << " taking away " << number_of_states_in_block(*bi) << " states (namely"; for (auto it=bi->start_bottom_states; it<bi->end_states; ++it) { std::cerr << ' ' << std::distance(m_states.begin(), it->ref_state); } std::cerr << (bi == R_block ? ", the R-subblock)\n" : ", the U-subblock)\n");
3814 // Because we visit all states of block bi and almost all their incoming and
3815 // outgoing transitions, we subsume all this bookkeeping in a single block
3816 // counter:
3817 mCRL2complexity(bi, add_work(check_complexity::
3818 /* Update the BLC_list, and bottom states, and invariant on inert */ splitB__update_BLC_of_smaller_subblock, check_complexity::log_n -
3819 /* transitions. */ check_complexity::ilog2(number_of_states_in_block(*bi))), *this);
3821 start_new_bottom_states=R_block->sta.rt_non_bottom_states;
3822 linked_list<BLC_indicators>::iterator R_to_U_tau_splitter =
3823 R_block->block.to_constellation.end(); assert(bi->block.to_constellation.empty());
3824 bool skip_transitions_in_splitter=false; assert(m_BLC_indicators_to_be_deleted.empty());
3825 bool make_splitter_stable_early=false;
3826
3827 if (initialisation || !split_off_new_bottom_states ||
3828 null_constellation==old_constellation ||
3829 target_constellation<initialisation, Iterator>(splitter)==
3830 old_constellation)
3831 {
3832 // splitter is not a main splitter. So it can be stabilized already
3833 // (as we do not need to maintain the order of main/co-splitter).
3834 make_splitter_stable_early=true;
3835//std::cerr << "Making splitter ";
3836 if constexpr (!initialisation)
3837 {
3838//std::cerr << splitter->debug_id(*this) << " stable early.\n";
3839 // However, we may need to move the splitter to its new constellation
3840 // at the same time.
3841 if (bi==R_block &&
3842 (initialisation || split_off_new_bottom_states ||
3843 splitter_end_unmarked_BLC==splitter->start_marked_BLC))
3844 {
3845 splitter->make_stable();
3846 // move splitter as a whole from its current list to the new list
3847 // where it will be part of:
3848 // (This only works if the whole splitter BLC set has moved.)
3849 R_block->block.to_constellation.splice(
3850 R_block->block.to_constellation.end(),
3851 B->block.to_constellation, splitter);
3852 skip_transitions_in_splitter=true;
3853//std::cerr << " and moving it to the BLC list of " << R_block << '\n';
3854 }
3855 else if (!initialisation && split_off_new_bottom_states &&
3856 null_constellation==old_constellation)
3857 {
3858 // Only during new bottom state splits, we really need to move the
3859 // splitter to the beginning of the list of splitters.
3860 make_stable_and_move_to_start_of_BLC(R_block, splitter);
3861 }
3862 else
3863 {
3864 // During initialisation, we do not bother to move the splitter to
3865 // the beginning of the BLC list, as every splitter will become
3866 // stable.
3867 splitter->make_stable();
3868 }
3869 } else
3870 {
3871 assert(nullptr==splitter);
3872//std::cerr << "(nullptr) stable early.\n";
3873 }
3874 }
3875 else if constexpr (!initialisation) // actually the condition always
3876 // holds, but we need "constexpr"
3877 { assert(!splitter->is_stable());
3878 // unmark all states in the splitter (needed to avoid that parts of the
3879 // splitter will retain marked states)
3880 splitter->start_marked_BLC=splitter->end_same_BLC;
3881 if (bi==R_block)
3882 {
3883 // insert a dummy old main splitter to help with placing the
3884 // corresponding co-splitter correctly:
3885//std::cerr << "Inserting a dummy old main splitter at m_BLC_transitions[" << std::distance(m_BLC_transitions.data(), splitter->end_same_BLC) << "]\n";
3887 B->block.to_constellation.emplace_after(splitter,
3888 splitter->end_same_BLC, splitter->end_same_BLC, false)
3889 ); // ...->work_counter=... -- not needed, as the set will be deleted soon again.
3890 // ++no_of_non_constellation_inert_BLC_sets; -- not needed, as BLC sets in m_BLC_indicators_to_be_deleted are not counted
3891 // move splitter as a whole from its current list to the new list
3892 // where it will be part of:
3893 // (This only works if the whole splitter BLC set has moved, i.e.
3894 // only if split_off_new_bottom_states == true.)
3895 R_block->block.to_constellation.splice(
3896 R_block->block.to_constellation.end(),
3897 B->block.to_constellation, splitter);
3898 skip_transitions_in_splitter=true;
3899 }
3900 } else { assert(0); }
3901 assert(!initialisation || make_splitter_stable_early);
3902//std::cerr << "initialisation==" << initialisation
3903//<< ", m_branching==" << m_branching << ", split_off_new_bottom_states==" << split_off_new_bottom_states
3904//<< ", make_splitter_stable_early==" << make_splitter_stable_early << ", skip_transitions_in_splitter==" << skip_transitions_in_splitter << '\n';
3905 if (!initialisation && m_branching)
3906 {
3907 if (initialisation || split_off_new_bottom_states ||
3908 !make_splitter_stable_early || !skip_transitions_in_splitter)
3909 {
3910 #ifndef NDEBUG
3911 /* insert an empty BLC set into bi->block.to_constellation */ if (!B->block.to_constellation.empty()) { const transition* t;
3912 /* for the inert transitions out of *bi (unless the */ assert(bi->block.to_constellation.empty() ||
3913 /* splitter inserted above was the set of inert transitions), */ (t=&m_aut.get_transitions()[*bi->
3914 /* to avoid the need to check whether such a BLC set already exists*/ block.to_constellation.begin()->start_same_BLC],
3915 /* in update_the_doubly_linked_list_LBC_new_block(). */ !is_inert_during_init(*t)) ||
3916 /* This set, if it remains empty, will need to be deleted */ m_states[t->from()].block->c.onstellation!=
3917 /* after updating the BLC sets. */ m_states[t->to()].block->c.onstellation); }
3918 #endif
3919//std::cerr << "Inserting an empty BLC set for the constellation-inert transitions into the BLC list of " << bi->debug_id(*this) << '\n';
3920 BLC_list_iterator start_inert_BLC=
3921 B->block.to_constellation.empty()
3922 ? m_BLC_transitions.data_end() // there are no inert transitions but we still insert a dummy set
3923 : B->block.to_constellation.begin()->start_same_BLC; // if there are inert transitions, they are here
3924 linked_list<BLC_indicators>::iterator new_inert_BLC_set=
3925 bi->block.to_constellation.emplace_front(start_inert_BLC,
3926 start_inert_BLC, true);
3927 #if !defined(NDEBUG) || defined(COUNT_WORK_BALANCE)
3928 if (!B->block.to_constellation.empty() &&
3929 start_inert_BLC<B->block.to_constellation.begin()->end_same_BLC)
3930 // the first element that's currently in the old BLC list could be the
3931 // dummy main splitter
3932 {
3933 const transition&perhaps_inert_t=m_aut.get_transitions()[*start_inert_BLC];
3934//std::cerr << "perhaps_inert_t==" << m_transitions[*start_inert_BLC].debug_id(*this) << '\n';
3935 assert(m_states[perhaps_inert_t.from()].block==B ||
3936 m_states[perhaps_inert_t.from()].block==bi);
3937 if(is_inert_during_init(perhaps_inert_t) &&
3938 m_states[perhaps_inert_t.to()].block->c.onstellation==B->c.onstellation)
3939 {
3940 // This are really the inert transitions, so we should copy the work
3941 // counter
3942 new_inert_BLC_set->work_counter=
3943 B->block.to_constellation.begin()->work_counter;
3944 }
3945 }
3946 #else
3947 (void) new_inert_BLC_set; // avoid unused variable warning
3948 #endif
3949 // ++no_of_non_constellation_inert_BLC_sets; -- not needed, as this BLC set will contain constellation-inert transitions
3950 } else
3951 {
3952 #ifndef NDEBUG
3953 assert(!bi->block.to_constellation.empty());
3954 assert(bi->block.to_constellation.begin()->start_same_BLC<
3955 bi->block.to_constellation.begin()->end_same_BLC);
3956 const transition& t=m_aut.get_transitions()
3957 [*bi->block.to_constellation.begin()->start_same_BLC];
3958 assert(is_inert_during_init(t));
3959 assert(m_states[t.from()].block->c.onstellation==
3960 m_states[t.to()].block->c.onstellation);
3961 #endif
3962 }
3963 }
3964 #ifndef USE_FOUR_WAY_SPLIT
3965 /* Recall new LBC positions. */ assert(m_co_splitters_to_be_checked.empty());
3966 #endif
3968 ssi=bi->start_bottom_states; ssi!=bi->end_states; ++ssi)
3969 {
3970 state_type_gj& s=*ssi->ref_state; assert(s.ref_states_in_blocks==ssi);
3971 // mCRL2complexity(s, add_work(..., max_bi_counter), *this);
3972 const outgoing_transitions_it end_it= // is subsumed in the above call
3973 (std::next(ssi->ref_state)==m_states.end())
3975 : std::next(ssi->ref_state)->start_outgoing_transitions; assert(s.block==bi);
3976 if constexpr (!initialisation)
3977 {
3978 // update the BLC_lists.
3980 ti!=end_it; ti++)
3981 { assert(m_states.begin()+m_aut.get_transitions()
3982 [*ti->ref.BLC_transitions].from()==ssi->ref_state);
3983 // mCRL2complexity(&m_transitions[*ti->ref.BLC_transitions],
3984 // add_work(..., max_bi_counter), *this);
3985 if (!skip_transitions_in_splitter || // is subsumed in the above call
3986 m_transitions[*ti->ref.BLC_transitions].
3987 transitions_per_block_to_constellation!=splitter)
3988 {
3990 *ti->ref.BLC_transitions, old_constellation, new_constellation);
3991 }
3992 }
3993 }
3994
3995 // Situation below is only relevant if the new block contains the
3996 // R-states:
3997 if (bi==R_block)
3998 {
4000 { assert(ssi>=R_block->sta.rt_non_bottom_states);
4001 // si is a non_bottom_state in the smallest block containing M.
4002//std::cerr << "Checking whether outgoing transitions of " << s.debug_id(*this) << " have become non-block-inert:\n";
4003 bool non_bottom_state_becomes_bottom_state=true; assert(m_branching);
4004
4006 ti!=end_it; ti++)
4007 { // mCRL2complexity(&m_transitions[*ti->ref.BLC_transitions],
4008 // add_work(..., max_bi_counter), *this);
4009 const transition& t=m_aut.get_transitions()[!initialisation // is subsumed in the above call
4010 ? *ti->ref.BLC_transitions : ti->ref.transitions]; assert(m_states.begin()+t.from()==ssi->ref_state);
4011 if (!m_aut.is_tau(m_aut_apply_hidden_label_map(t.label())))
4012 {
4013 break;
4014 }
4015 if (m_states[t.to()].block==B)
4016 {
4017 // if (is_inert_during_init(t)) -- always true because it
4018 // cannot be a tau-self-loop
4019 {
4020 // This is a transition that has become non-block-inert.
4021 // (However, it is still constellation-inert.)
4022 make_transition_non_block_inert(t);
4023 if (!initialisation && split_off_new_bottom_states)
4024 {
4025 const linked_list<BLC_indicators>::iterator new_splitter=
4026 m_transitions[*ti->ref.BLC_transitions].
4027 transitions_per_block_to_constellation; assert(R_block->block.to_constellation.begin()==new_splitter);
4028 if (R_to_U_tau_splitter==
4029 R_block->block.to_constellation.end())
4030 {
4031 R_to_U_tau_splitter=new_splitter;
4032 R_to_U_tau_splitter->make_unstable();
4033 } else
4034 {
4035 assert(R_to_U_tau_splitter==new_splitter);
4036 /* immediately mark this transition, in case we get */ assert(!R_to_U_tau_splitter->is_stable());
4037 /* new bottom states: */ }
4039 }
4040 }
4041 }
4042 else if (m_preserve_divergence && t.from()==t.to())
4043 { assert(!is_inert_during_init(t));
4044 // transition is a divergent tau-self-loop. It never was inert.
4045 }
4046 else if (m_states[t.to()].block==R_block)
4047 { assert(is_inert_during_init(t));
4048 // There is an outgoing inert tau. State remains non-bottom.
4049 non_bottom_state_becomes_bottom_state=false;
4050 }
4051 }
4052 if (non_bottom_state_becomes_bottom_state)
4053 { assert(initialisation || !split_off_new_bottom_states ||
4054 /* The state at si has become a bottom_state. */ R_block->block.to_constellation.end()!=R_to_U_tau_splitter);
4056 }
4057 }
4058 }
4059 // Investigate the incoming formerly inert tau transitions.
4060 else if (R_block->sta.rt_non_bottom_states<R_block->end_states)
4061 {
4062//std::cerr << "Checking whether incoming transitions of " << s.debug_id(*this) << " have become non-block-inert:\n";
4063 const std::vector<transition>::iterator it_end =
4064 std::next(ssi->ref_state)>=m_states.end()
4065 ? m_aut.get_transitions().end()
4066 : std::next(ssi->ref_state)->start_incoming_transitions;
4067 for(std::vector<transition>::iterator it =
4068 s.start_incoming_transitions; it!=it_end; it++)
4069 { // mCRL2complexity(&m_transitions[std::distance(m_aut.get_transitions().
4070 // begin(), it)], add_work(..., max_bi_counter), *this);
4071 // subsumed in the above call
4072 const transition& t=*it; assert(m_states.begin()+t.to()==ssi->ref_state);
4073 if (!m_aut.is_tau(m_aut_apply_hidden_label_map(t.label())))
4074 {
4075 break; // All tau transitions have been investigated.
4076 }
4077
4079 (m_states.begin()+t.from());
4080 if (from->block==R_block)
4081 {
4082 // This transition did become non-block-inert.
4083 // (However, it is still constellation-inert.)
4084 make_transition_non_block_inert(t);
4085 if (!initialisation && split_off_new_bottom_states)
4086 {
4087 const linked_list<BLC_indicators>::iterator new_splitter=
4088 m_transitions[std::distance(m_aut.get_transitions().begin(),
4089 it)].transitions_per_block_to_constellation; assert(R_block->block.to_constellation.begin()==new_splitter);
4090 if (R_to_U_tau_splitter==R_block->block.to_constellation.end())
4091 {
4092 R_to_U_tau_splitter=new_splitter;
4093 R_to_U_tau_splitter->make_unstable();
4094 } else
4095 {
4096 assert(R_to_U_tau_splitter==new_splitter);
4097 /* immediately mark this transition, in case we get new */ assert(!R_to_U_tau_splitter->is_stable());
4098 /* bottom states: */ }
4100 [std::distance(m_aut.get_transitions().begin(), it)].
4101 ref_outgoing_transitions);
4102 }
4103 // Check whether from is a new bottom state.
4104 if (from->no_of_outgoing_block_inert_transitions==0)
4105 {
4106 // This state has no more outgoing inert transitions. It
4107 // becomes a bottom state.
4109 }
4110 }
4111 }
4112 }
4113 } assert(R_block->start_bottom_states<R_block->sta.rt_non_bottom_states);
4114 if constexpr (!initialisation)
4115 {
4116 if (m_branching)
4117 { assert(!bi->block.to_constellation.empty());
4118 // Before the loop we inserted an empty BLC set for the inert
4119 // transitions into bi->block.to_constellation.
4120 // If it is still empty, we have to remove it again.
4121 linked_list<BLC_indicators>::iterator
4122 inert_ind=bi->block.to_constellation.begin();
4123 if (inert_ind->start_same_BLC==inert_ind->end_same_BLC)
4124 { assert(inert_ind->is_stable());
4125 bi->block.to_constellation.erase(inert_ind);
4126 } else
4127 { // assert(perhaps_inert_ind->is_stable()); -- it may be unstable
4128 // if there are tau-transitions from R_block==bi to U
4129 #ifndef NDEBUG
4130 const transition& inert_t=
4131 m_aut.get_transitions()[*inert_ind->start_same_BLC];
4132 assert(is_inert_during_init(inert_t));
4133 assert(bi==m_states[inert_t.from()].block);
4134 assert(bi->c.onstellation==m_states[inert_t.to()].block->c.onstellation);
4135 #endif
4136 }
4137 }
4138 for (std::vector<linked_list<BLC_indicators>::iterator>::iterator
4140 it<m_BLC_indicators_to_be_deleted.end(); ++it)
4141 {