1 // -*- coding: utf-8 -*-
2 // Copyright (C) 2014-2018, 2020, 2021 Laboratoire de Recherche et
3 // Développement de l'Epita.
4 //
5 // This file is part of Spot, a model checking library.
6 //
7 // Spot is free software; you can redistribute it and/or modify it
8 // under the terms of the GNU General Public License as published by
9 // the Free Software Foundation; either version 3 of the License, or
10 // (at your option) any later version.
11 //
12 // Spot is distributed in the hope that it will be useful, but WITHOUT
13 // ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
14 // or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public
15 // License for more details.
16 //
17 // You should have received a copy of the GNU General Public License
18 // along with this program.  If not, see <http://www.gnu.org/licenses/>.
19 
20 #pragma once
21 
22 #include <spot/misc/common.hh>
23 #include <vector>
24 #include <type_traits>
25 #include <tuple>
26 #include <cassert>
27 #include <iterator>
28 #include <algorithm>
29 #include <map>
30 #include <iostream>
31 
32 namespace spot
33 {
34   template <typename State_Data, typename Edge_Data>
35   class SPOT_API digraph;
36 
37   namespace internal
38   {
39 #ifndef SWIG
40     template <typename Of, typename ...Args>
41     struct first_is_base_of
42     {
43       static const bool value = false;
44     };
45 
46     template <typename Of, typename Arg1, typename ...Args>
47     struct first_is_base_of<Of, Arg1, Args...>
48     {
49       static const bool value =
50         std::is_base_of<Of, typename std::decay<Arg1>::type>::value;
51     };
52 #endif
53 
54     // The boxed_label class stores Data as an attribute called
55     // "label" if boxed is true.  It is an empty class if Data is
56     // void, and it simply inherits from Data if boxed is false.
57     //
58     // The data() method offers an homogeneous access to the Data
59     // instance.
60     template <typename Data, bool boxed = !std::is_class<Data>::value>
61     struct SPOT_API boxed_label
62     {
63       typedef Data data_t;
64       Data label;
65 
66 #ifndef SWIG
67       template <typename... Args,
68                 typename = typename std::enable_if<
69                   !first_is_base_of<boxed_label, Args...>::value>::type>
boxed_labelspot::internal::boxed_label70         boxed_label(Args&&... args)
71         noexcept(std::is_nothrow_constructible<Data, Args...>::value)
72         : label{std::forward<Args>(args)...}
73       {
74       }
75 #endif
76 
77       // if Data is a POD type, G++ 4.8.2 wants default values for all
78       // label fields unless we define this default constructor here.
boxed_labelspot::internal::boxed_label79       explicit boxed_label()
80         noexcept(std::is_nothrow_constructible<Data>::value)
81       {
82       }
83 
dataspot::internal::boxed_label84       Data& data()
85       {
86         return label;
87       }
88 
dataspot::internal::boxed_label89       const Data& data() const
90       {
91         return label;
92       }
93 
operator <spot::internal::boxed_label94       bool operator<(const boxed_label& other) const
95       {
96         return label < other.label;
97       }
98     };
99 
100     template <>
101     struct SPOT_API boxed_label<void, true>: public std::tuple<>
102     {
103       typedef std::tuple<> data_t;
dataspot::internal::boxed_label104       std::tuple<>& data()
105       {
106         return *this;
107       }
108 
dataspot::internal::boxed_label109       const std::tuple<>& data() const
110       {
111         return *this;
112       }
113 
114     };
115 
116     template <typename Data>
117     struct SPOT_API boxed_label<Data, false>: public Data
118     {
119       typedef Data data_t;
120 
121 #ifndef SWIG
122       template <typename... Args,
123                 typename = typename std::enable_if<
124                   !first_is_base_of<boxed_label, Args...>::value>::type>
boxed_labelspot::internal::boxed_label125         boxed_label(Args&&... args)
126         noexcept(std::is_nothrow_constructible<Data, Args...>::value)
127         : Data{std::forward<Args>(args)...}
128       {
129       }
130 #endif
131 
132       // if Data is a POD type, G++ 4.8.2 wants default values for all
133       // label fields unless we define this default constructor here.
boxed_labelspot::internal::boxed_label134       explicit boxed_label()
135         noexcept(std::is_nothrow_constructible<Data>::value)
136       {
137       }
138 
dataspot::internal::boxed_label139       Data& data()
140       {
141         return *this;
142       }
143 
dataspot::internal::boxed_label144       const Data& data() const
145       {
146         return *this;
147       }
148     };
149 
150     //////////////////////////////////////////////////
151     // State storage for digraphs
152     //////////////////////////////////////////////////
153 
154     // We have two implementations, one with attached State_Data, and
155     // one without.
156 
157     template <typename Edge, typename State_Data>
158     struct SPOT_API distate_storage final: public State_Data
159     {
160       Edge succ = 0; // First outgoing edge (used when iterating)
161       Edge succ_tail = 0;        // Last outgoing edge (used for
162                                 // appending new edges)
163 #ifndef SWIG
164       template <typename... Args,
165                 typename = typename std::enable_if<
166                   !first_is_base_of<distate_storage, Args...>::value>::type>
distate_storagespot::internal::distate_storage167       distate_storage(Args&&... args)
168         noexcept(std::is_nothrow_constructible<State_Data, Args...>::value)
169         : State_Data{std::forward<Args>(args)...}
170       {
171       }
172 #endif
173     };
174 
175     //////////////////////////////////////////////////
176     // Edge storage
177     //////////////////////////////////////////////////
178 
179     // Again two implementation: one with label, and one without.
180 
181     template <typename StateIn,
182               typename StateOut, typename Edge, typename Edge_Data>
183     struct SPOT_API edge_storage final: public Edge_Data
184     {
185       typedef Edge edge;
186 
187       StateOut dst;                // destination
188       Edge next_succ;        // next outgoing edge with same
189                                 // source, or 0
190       StateIn src;                // source
191 
edge_storagespot::internal::edge_storage192       explicit edge_storage()
193         noexcept(std::is_nothrow_constructible<Edge_Data>::value)
194         : Edge_Data{}
195       {
196       }
197 
198 #ifndef SWIG
199       template <typename... Args>
edge_storagespot::internal::edge_storage200       edge_storage(StateOut dst, Edge next_succ,
201                     StateIn src, Args&&... args)
202         noexcept(std::is_nothrow_constructible<Edge_Data, Args...>::value
203                  && std::is_nothrow_constructible<StateOut, StateOut>::value
204                  && std::is_nothrow_constructible<Edge, Edge>::value)
205         : Edge_Data{std::forward<Args>(args)...},
206         dst(dst), next_succ(next_succ), src(src)
207       {
208       }
209 #endif
210 
operator <spot::internal::edge_storage211       bool operator<(const edge_storage& other) const
212       {
213         if (src < other.src)
214           return true;
215         if (src > other.src)
216           return false;
217         // This might be costly if the destination is a vector
218         if (dst < other.dst)
219           return true;
220         if (dst > other.dst)
221           return false;
222         return this->data() < other.data();
223       }
224 
operator ==spot::internal::edge_storage225       bool operator==(const edge_storage& other) const
226       {
227         return src == other.src &&
228           dst == other.dst &&
229           this->data() == other.data();
230       }
231     };
232 
233     //////////////////////////////////////////////////
234     // Edge iterator
235     //////////////////////////////////////////////////
236 
237     // This holds a graph and a edge number that is the start of
238     // a list, and it iterates over all the edge_storage_t elements
239     // of that list.
240 
241     template <typename Graph>
242     class SPOT_API edge_iterator
243     {
244     public:
245       typedef typename std::conditional<std::is_const<Graph>::value,
246                                         const typename Graph::edge_storage_t,
247                                         typename Graph::edge_storage_t>::type
248         value_type;
249       typedef value_type& reference;
250       typedef value_type* pointer;
251       typedef std::ptrdiff_t difference_type;
252       typedef std::forward_iterator_tag iterator_category;
253 
254       typedef typename Graph::edge edge;
255 
edge_iterator()256       edge_iterator() noexcept
257         : g_(nullptr), t_(0)
258       {
259       }
260 
edge_iterator(Graph * g,edge t)261       edge_iterator(Graph* g, edge t) noexcept
262         : g_(g), t_(t)
263       {
264       }
265 
operator ==(edge_iterator o) const266       bool operator==(edge_iterator o) const
267       {
268         return t_ == o.t_;
269       }
270 
operator !=(edge_iterator o) const271       bool operator!=(edge_iterator o) const
272       {
273         return t_ != o.t_;
274       }
275 
operator *() const276       reference operator*() const
277       {
278         return g_->edge_storage(t_);
279       }
280 
operator ->() const281       pointer      operator->() const
282       {
283         return &g_->edge_storage(t_);
284       }
285 
operator ++()286       edge_iterator operator++()
287       {
288         t_ = operator*().next_succ;
289         return *this;
290       }
291 
operator ++(int)292       edge_iterator operator++(int)
293       {
294         edge_iterator ti = *this;
295         t_ = operator*().next_succ;
296         return ti;
297       }
298 
operator bool() const299       operator bool() const
300       {
301         return t_;
302       }
303 
trans() const304       edge trans() const
305       {
306         return t_;
307       }
308 
309     protected:
310       Graph* g_;
311       edge t_;
312     };
313 
314     template <typename Graph>
315     class SPOT_API killer_edge_iterator: public edge_iterator<Graph>
316     {
317       typedef edge_iterator<Graph> super;
318     public:
319       typedef typename Graph::state_storage_t state_storage_t;
320       typedef typename Graph::edge edge;
321 
killer_edge_iterator(Graph * g,edge t,state_storage_t & src)322       killer_edge_iterator(Graph* g, edge t, state_storage_t& src) noexcept
323         : super(g, t), src_(src), prev_(0)
324       {
325       }
326 
operator ++()327       killer_edge_iterator operator++()
328       {
329         prev_ = this->t_;
330         this->t_ = this->operator*().next_succ;
331         return *this;
332       }
333 
operator ++(int)334       killer_edge_iterator operator++(int)
335       {
336         killer_edge_iterator ti = *this;
337         ++*this;
338         return ti;
339       }
340 
341       // Erase the current edge and advance the iterator.
erase()342       void erase()
343       {
344         edge next = this->operator*().next_succ;
345 
346         // Update source state and previous edges
347         if (prev_)
348           {
349             this->g_->edge_storage(prev_).next_succ = next;
350           }
351         else
352           {
353             if (src_.succ == this->t_)
354               src_.succ = next;
355           }
356         if (src_.succ_tail == this->t_)
357           {
358             src_.succ_tail = prev_;
359             SPOT_ASSERT(next == 0);
360           }
361 
362         // Erased edges have themselves as next_succ.
363         this->operator*().next_succ = this->t_;
364 
365         // Advance iterator to next edge.
366         this->t_ = next;
367 
368         ++this->g_->killed_edge_;
369       }
370 
371     protected:
372       state_storage_t& src_;
373       edge prev_;
374     };
375 
376 
377     //////////////////////////////////////////////////
378     // State OUT
379     //////////////////////////////////////////////////
380 
381     // Fake container listing the outgoing edges of a state.
382 
383     template <typename Graph>
384     class SPOT_API state_out
385     {
386     public:
387       typedef typename Graph::edge edge;
state_out(Graph * g,edge t)388       state_out(Graph* g, edge t) noexcept
389         : g_(g), t_(t)
390       {
391       }
392 
begin() const393       edge_iterator<Graph> begin() const
394       {
395         return {g_, t_};
396       }
397 
end() const398       edge_iterator<Graph> end() const
399       {
400         return {};
401       }
402 
recycle(edge t)403       void recycle(edge t)
404       {
405         t_ = t;
406       }
407 
408     protected:
409       Graph* g_;
410       edge t_;
411     };
412 
413     //////////////////////////////////////////////////
414     // all_trans
415     //////////////////////////////////////////////////
416 
417     template <typename Graph>
418     class SPOT_API all_edge_iterator
419     {
420     public:
421       typedef typename std::conditional<std::is_const<Graph>::value,
422                                         const typename Graph::edge_storage_t,
423                                         typename Graph::edge_storage_t>::type
424         value_type;
425       typedef value_type& reference;
426       typedef value_type* pointer;
427       typedef std::ptrdiff_t difference_type;
428       typedef std::forward_iterator_tag iterator_category;
429 
430     protected:
431       typedef typename std::conditional<std::is_const<Graph>::value,
432                                         const typename Graph::edge_vector_t,
433                                         typename Graph::edge_vector_t>::type
434         tv_t;
435 
436       unsigned t_;
437       tv_t& tv_;
438 
skip_()439       void skip_()
440       {
441         unsigned s = tv_.size();
442         do
443           ++t_;
444         while (t_ < s && tv_[t_].next_succ == t_);
445       }
446 
447     public:
all_edge_iterator(unsigned pos,tv_t & tv)448       all_edge_iterator(unsigned pos, tv_t& tv) noexcept
449         : t_(pos), tv_(tv)
450       {
451         skip_();
452       }
453 
all_edge_iterator(tv_t & tv)454       all_edge_iterator(tv_t& tv) noexcept
455         : t_(tv.size()), tv_(tv)
456       {
457       }
458 
operator ++()459       all_edge_iterator& operator++()
460       {
461         skip_();
462         return *this;
463       }
464 
operator ++(int)465       all_edge_iterator operator++(int)
466       {
467         all_edge_iterator old = *this;
468         ++*this;
469         return old;
470       }
471 
operator ==(all_edge_iterator o) const472       bool operator==(all_edge_iterator o) const
473       {
474         return t_ == o.t_;
475       }
476 
operator !=(all_edge_iterator o) const477       bool operator!=(all_edge_iterator o) const
478       {
479         return t_ != o.t_;
480       }
481 
operator *() const482       reference operator*() const
483       {
484         return tv_[t_];
485       }
486 
operator ->() const487       pointer operator->() const
488       {
489         return &tv_[t_];
490       }
491     };
492 
493 
494     template <typename Graph>
495     class SPOT_API all_trans
496     {
497     public:
498       typedef typename std::conditional<std::is_const<Graph>::value,
499                                         const typename Graph::edge_vector_t,
500                                         typename Graph::edge_vector_t>::type
501         tv_t;
502       typedef all_edge_iterator<Graph> iter_t;
503     private:
504       tv_t& tv_;
505     public:
506 
all_trans(tv_t & tv)507       all_trans(tv_t& tv) noexcept
508         : tv_(tv)
509       {
510       }
511 
begin() const512       iter_t begin() const
513       {
514         return {0, tv_};
515       }
516 
end() const517       iter_t end() const
518       {
519         return {tv_};
520       }
521     };
522 
523     class SPOT_API const_universal_dests
524     {
525     private:
526       const unsigned* begin_;
527       const unsigned* end_;
528       unsigned tmp_;
529     public:
const_universal_dests(const unsigned * begin,const unsigned * end)530       const_universal_dests(const unsigned* begin, const unsigned* end) noexcept
531         : begin_(begin), end_(end)
532       {
533       }
534 
const_universal_dests(unsigned state)535       const_universal_dests(unsigned state) noexcept
536         : begin_(&tmp_), end_(&tmp_ + 1), tmp_(state)
537       {
538       }
539 
begin() const540       const unsigned* begin() const
541       {
542         return begin_;
543       }
544 
end() const545       const unsigned* end() const
546       {
547         return end_;
548       }
549     };
550 
551     template<class G>
552     class univ_dest_mapper
553     {
554       std::map<std::vector<unsigned>, unsigned> uniq_;
555       G& g_;
556     public:
557 
univ_dest_mapper(G & graph)558       univ_dest_mapper(G& graph)
559         : g_(graph)
560       {
561       }
562 
563       template<class I>
new_univ_dests(I begin,I end)564       unsigned new_univ_dests(I begin, I end)
565       {
566         std::vector<unsigned> tmp(begin, end);
567         std::sort(tmp.begin(), tmp.end());
568         tmp.erase(std::unique(tmp.begin(), tmp.end()), tmp.end());
569         auto p = uniq_.emplace(tmp, 0);
570         if (p.second)
571           p.first->second = g_.new_univ_dests(tmp.begin(), tmp.end());
572         return p.first->second;
573       }
574 
575     };
576 
577   } // namespace internal
578 
579 
580   /// \brief A directed graph
581   ///
582   /// \tparam State_Data data to attach to states
583   /// \tparam Edge_Data data to attach to edges
584   template <typename State_Data, typename Edge_Data>
585   class digraph
586   {
587     friend class internal::edge_iterator<digraph>;
588     friend class internal::edge_iterator<const digraph>;
589     friend class internal::killer_edge_iterator<digraph>;
590 
591   public:
592     typedef internal::edge_iterator<digraph> iterator;
593     typedef internal::edge_iterator<const digraph> const_iterator;
594 
595     // Extra data to store on each state or edge.
596     typedef State_Data state_data_t;
597     typedef Edge_Data edge_data_t;
598 
599     // State and edges are identified by their indices in some
600     // vector.
601     typedef unsigned state;
602     typedef unsigned edge;
603 
604     typedef internal::distate_storage<edge,
605                                       internal::boxed_label<State_Data>>
606       state_storage_t;
607     typedef internal::edge_storage<state, state, edge,
608                                    internal::boxed_label<Edge_Data>>
609       edge_storage_t;
610     typedef std::vector<state_storage_t> state_vector;
611     typedef std::vector<edge_storage_t> edge_vector_t;
612 
613     // A sequence of universal destination groups of the form:
614     //   (n state_1 state_2 ... state_n)*
615     typedef std::vector<unsigned> dests_vector_t;
616 
617   protected:
618     state_vector states_;
619     edge_vector_t edges_;
620     dests_vector_t dests_;      // Only used by alternating automata.
621     // Number of erased edges.
622     unsigned killed_edge_;
623   public:
624     /// \brief Construct an empty graph
625     ///
626     /// Construct an empty graph, and reserve space for \a max_states
627     /// states and \a max_trans edges.  These are not hard
628     /// limits, but just hints to pre-allocate a data structure that
629     /// may hold that much items.
digraph(unsigned max_states=10,unsigned max_trans=0)630     digraph(unsigned max_states = 10, unsigned max_trans = 0)
631       : killed_edge_(0)
632     {
633       states_.reserve(max_states);
634       if (max_trans == 0)
635         max_trans = max_states * 2;
636       edges_.reserve(max_trans + 1);
637       // Edge number 0 is not used, because we use this index
638       // to mark the absence of a edge.
639       edges_.resize(1);
640       // This causes edge 0 to be considered as dead.
641       edges_[0].next_succ = 0;
642     }
643 
644     /// The number of states in the automaton
num_states() const645     unsigned num_states() const
646     {
647       return states_.size();
648     }
649 
650     /// \brief The number of edges in the automaton
651     ///
652     /// Killed edges are omitted.
num_edges() const653     unsigned num_edges() const
654     {
655       return edges_.size() - killed_edge_ - 1;
656     }
657 
658     /// Whether the automaton uses only existential branching.
is_existential() const659     bool is_existential() const
660     {
661       return dests_.empty();
662     }
663 
664     /// \brief Create a new states
665     ///
666     /// All arguments are forwarded to the State_Data constructor.
667     ///
668     /// \return a state number
669     template <typename... Args>
new_state(Args &&...args)670     state new_state(Args&&... args)
671     {
672       state s = states_.size();
673       states_.emplace_back(std::forward<Args>(args)...);
674       return s;
675     }
676 
677     /// \brief Create n new states
678     ///
679     /// All arguments are forwarded to the State_Data constructor of
680     /// each of the n states.
681     ///
682     /// \return the first state number
683     template <typename... Args>
new_states(unsigned n,Args &&...args)684     state new_states(unsigned n, Args&&... args)
685     {
686       state s = states_.size();
687       states_.reserve(s + n);
688       while (n--)
689         states_.emplace_back(std::forward<Args>(args)...);
690       return s;
691     }
692 
693     /// @{
694     /// \brief return a reference to the storage of a state
695     ///
696     /// The storage includes any of the user-supplied State_Data, plus
697     /// some custom fields needed to find the outgoing transitions.
698     state_storage_t&
state_storage(state s)699     state_storage(state s)
700     {
701       return states_[s];
702     }
703 
704     const state_storage_t&
state_storage(state s) const705     state_storage(state s) const
706     {
707       return states_[s];
708     }
709     ///@}
710 
711     ///@{
712     /// \brief return the State_Data associated to a state
713     ///
714     /// This does not use State_Data& as return type, because
715     /// State_Data might be void.
716     typename state_storage_t::data_t&
state_data(state s)717     state_data(state s)
718     {
719       return states_[s].data();
720     }
721 
722     const typename state_storage_t::data_t&
state_data(state s) const723     state_data(state s) const
724     {
725       return states_[s].data();
726     }
727     ///@}
728 
729     ///@{
730     /// \brief return a reference to the storage of an edge
731     ///
732     /// The storage includes any of the user-supplied Edge_Data, plus
733     /// some custom fields needed to find the next transitions.
734     edge_storage_t&
edge_storage(edge s)735     edge_storage(edge s)
736     {
737       return edges_[s];
738     }
739 
740     const edge_storage_t&
edge_storage(edge s) const741     edge_storage(edge s) const
742     {
743       return edges_[s];
744     }
745     ///@}
746 
747     ///@{
748     /// \brief return the Edgeg_Data of an edge.
749     ///
750     /// This does not use Edge_Data& as return type, because
751     /// Edge_Data might be void.
752     typename edge_storage_t::data_t&
edge_data(edge s)753     edge_data(edge s)
754     {
755       return edges_[s].data();
756     }
757 
758     const typename edge_storage_t::data_t&
edge_data(edge s) const759     edge_data(edge s) const
760     {
761       return edges_[s].data();
762     }
763     ///@}
764 
765     /// \brief Create a new edge
766     ///
767     /// \param src the source state
768     /// \param dst the destination state
769     /// \param args arguments to forward to the Edge_Data constructor
770     template <typename... Args>
771     edge
new_edge(state src,state dst,Args &&...args)772     new_edge(state src, state dst, Args&&... args)
773     {
774       edge t = edges_.size();
775       edges_.emplace_back(dst, 0, src, std::forward<Args>(args)...);
776 
777       edge st = states_[src].succ_tail;
778       SPOT_ASSERT(st < t || !st);
779       if (!st)
780         states_[src].succ = t;
781       else
782         edges_[st].next_succ = t;
783       states_[src].succ_tail = t;
784       return t;
785     }
786 
787     /// \brief Create a new universal destination group
788     ///
789     /// The resulting state number can be used as the destination of
790     /// an edge.
791     ///
792     /// \param dst_begin start of a non-empty container of destination states
793     /// \param dst_end end of a non-empty container of destination states
794     template <typename I>
795     state
new_univ_dests(I dst_begin,I dst_end)796     new_univ_dests(I dst_begin, I dst_end)
797     {
798       unsigned sz = std::distance(dst_begin, dst_end);
799       if (sz == 1)
800         return *dst_begin;
801       SPOT_ASSERT(sz > 1);
802       unsigned d = dests_.size();
803       dests_.emplace_back(sz);
804       dests_.insert(dests_.end(), dst_begin, dst_end);
805       return ~d;
806     }
807 
808     /// \brief Create a new universal edge
809     ///
810     /// \param src the source state
811     /// \param dst_begin start of a non-empty container of destination states
812     /// \param dst_end end of a non-empty container of destination states
813     /// \param args arguments to forward to the Edge_Data constructor
814     template <typename I, typename... Args>
815     edge
new_univ_edge(state src,I dst_begin,I dst_end,Args &&...args)816     new_univ_edge(state src, I dst_begin, I dst_end, Args&&... args)
817     {
818       return new_edge(src, new_univ_dests(dst_begin, dst_end),
819                       std::forward<Args>(args)...);
820     }
821 
822     /// \brief Create a new universal edge
823     ///
824     /// \param src the source state
825     /// \param dsts a non-empty list of destination states
826     /// \param args arguments to forward to the Edge_Data constructor
827     template <typename... Args>
828     edge
new_univ_edge(state src,const std::initializer_list<state> & dsts,Args &&...args)829     new_univ_edge(state src, const std::initializer_list<state>& dsts,
830                   Args&&... args)
831     {
832       return new_univ_edge(src, dsts.begin(), dsts.end(),
833                            std::forward<Args>(args)...);
834     }
835 
univ_dests(state src) const836     internal::const_universal_dests univ_dests(state src) const
837     {
838       if ((int)src < 0)
839         {
840           unsigned pos = ~src;
841           const unsigned* d = dests_.data();
842           d += pos;
843           unsigned num = *d;
844           return { d + 1, d + num + 1 };
845         }
846       else
847         {
848           return src;
849         }
850     }
851 
univ_dests(const edge_storage_t & e) const852     internal::const_universal_dests univ_dests(const edge_storage_t& e) const
853     {
854       return univ_dests(e.dst);
855     }
856 
857     /// Convert a storage reference into a state number
index_of_state(const state_storage_t & ss) const858     state index_of_state(const state_storage_t& ss) const
859     {
860       SPOT_ASSERT(!states_.empty());
861       return &ss - &states_.front();
862     }
863 
864     /// Convert a storage reference into an edge number
index_of_edge(const edge_storage_t & tt) const865     edge index_of_edge(const edge_storage_t& tt) const
866     {
867       SPOT_ASSERT(!edges_.empty());
868       return &tt - &edges_.front();
869     }
870 
871     /// @{
872     /// \brief Return a fake container with all edges leaving \a src
873     internal::state_out<digraph>
out(state src)874     out(state src)
875     {
876       return {this, states_[src].succ};
877     }
878 
879     internal::state_out<digraph>
out(state_storage_t & src)880     out(state_storage_t& src)
881     {
882       return out(index_of_state(src));
883     }
884 
885     internal::state_out<const digraph>
out(state src) const886     out(state src) const
887     {
888       return {this, states_[src].succ};
889     }
890 
891     internal::state_out<const digraph>
out(state_storage_t & src) const892     out(state_storage_t& src) const
893     {
894       return out(index_of_state(src));
895     }
896     /// @}
897 
898     /// @{
899     ///
900     /// \brief Return a fake container with all edges leaving \a src,
901     /// allowing erasure.
902     internal::killer_edge_iterator<digraph>
out_iteraser(state_storage_t & src)903     out_iteraser(state_storage_t& src)
904     {
905       return {this, src.succ, src};
906     }
907 
908     internal::killer_edge_iterator<digraph>
out_iteraser(state src)909     out_iteraser(state src)
910     {
911       return out_iteraser(state_storage(src));
912     }
913     ///@}
914 
915     /// @{
916     ///
917     /// \brief Return the vector of states.
states() const918     const state_vector& states() const
919     {
920       return states_;
921     }
922 
states()923     state_vector& states()
924     {
925       return states_;
926     }
927     /// @}
928 
929     /// @{
930     ///
931     /// \brief Return a fake container with all edges (exluding erased
932     /// edges)
edges() const933     internal::all_trans<const digraph> edges() const
934     {
935       return edges_;
936     }
937 
edges()938     internal::all_trans<digraph> edges()
939     {
940       return edges_;
941     }
942     /// @}
943 
944     /// @{
945     /// \brief Return the vector of all edges.
946     ///
947     /// When using this method, beware that the first entry (edge #0)
948     /// is not a real edge, and that any edge with next_succ pointing
949     /// to itself is an erased edge.
950     ///
951     /// You should probably use edges() instead.
edge_vector() const952     const edge_vector_t& edge_vector() const
953     {
954       return edges_;
955     }
956 
edge_vector()957     edge_vector_t& edge_vector()
958     {
959       return edges_;
960     }
961     /// @}
962 
963     /// \brief Test whether the given edge is valid.
964     ///
965     /// An edge is valid if its number is less than the total number
966     /// of edges, and it does not correspond to an erased (dead) edge.
967     ///
968     /// \see is_dead_edge()
is_valid_edge(edge t) const969     bool is_valid_edge(edge t) const
970     {
971       // Erased edges have their next_succ pointing to
972       // themselves.
973       return (t < edges_.size() &&
974               edges_[t].next_succ != t);
975     }
976 
977     /// @{
978     /// \brief Tests whether an edge has been erased.
979     ///
980     /// \see is_valid_edge
is_dead_edge(unsigned t) const981     bool is_dead_edge(unsigned t) const
982     {
983       return edges_[t].next_succ == t;
984     }
985 
is_dead_edge(const edge_storage_t & t) const986     bool is_dead_edge(const edge_storage_t& t) const
987     {
988       return t.next_succ == index_of_edge(t);
989     }
990     /// @}
991 
992     /// @{
993     /// \brief The vector used to store universal destinations.
994     ///
995     /// The actual way those destinations are stored is an
996     /// implementation detail you should not rely on.
dests_vector() const997     const dests_vector_t& dests_vector() const
998     {
999       return dests_;
1000     }
1001 
dests_vector()1002     dests_vector_t& dests_vector()
1003     {
1004       return dests_;
1005     }
1006     /// @}
1007 
1008     /// Dump the state and edge storage for debugging
dump_storage(std::ostream & o) const1009     void dump_storage(std::ostream& o) const
1010     {
1011       unsigned tend = edges_.size();
1012       for (unsigned t = 1; t < tend; ++t)
1013         {
1014           o << 't' << t << ": (s"
1015             << edges_[t].src << ", ";
1016           int d = edges_[t].dst;
1017           if (d < 0)
1018             o << 'd' << ~d;
1019           else
1020             o << 's' << d;
1021           o << ") t" << edges_[t].next_succ << '\n';
1022         }
1023       unsigned send = states_.size();
1024       for (unsigned s = 0; s < send; ++s)
1025         {
1026           o << 's' << s << ": t"
1027             << states_[s].succ << " t"
1028             << states_[s].succ_tail << '\n';
1029         }
1030       unsigned dend = dests_.size();
1031       unsigned size = 0;
1032       for (unsigned s = 0; s < dend; ++s)
1033         {
1034           o << 'd' << s << ": ";
1035           if (size == 0)
1036             {
1037               o << '#';
1038               size = dests_[s];
1039             }
1040           else
1041             {
1042               o << 's';
1043               --size;
1044             }
1045           o << dests_[s] << '\n';
1046         }
1047     }
1048 
1049     enum dump_storage_items {
1050       DSI_GraphHeader = 1,
1051       DSI_GraphFooter = 2,
1052       DSI_StatesHeader = 4,
1053       DSI_StatesBody = 8,
1054       DSI_StatesFooter = 16,
1055       DSI_States = DSI_StatesHeader | DSI_StatesBody | DSI_StatesFooter,
1056       DSI_EdgesHeader = 32,
1057       DSI_EdgesBody = 64,
1058       DSI_EdgesFooter = 128,
1059       DSI_Edges = DSI_EdgesHeader | DSI_EdgesBody | DSI_EdgesFooter,
1060       DSI_DestsHeader = 256,
1061       DSI_DestsBody = 512,
1062       DSI_DestsFooter = 1024,
1063       DSI_Dests = DSI_DestsHeader | DSI_DestsBody | DSI_DestsFooter,
1064       DSI_All =
1065       DSI_GraphHeader | DSI_States | DSI_Edges | DSI_Dests | DSI_GraphFooter,
1066     };
1067 
1068     /// Dump the state and edge storage for debugging
dump_storage_as_dot(std::ostream & o,int dsi=DSI_All) const1069     void dump_storage_as_dot(std::ostream& o, int dsi = DSI_All) const
1070     {
1071       if (dsi & DSI_GraphHeader)
1072         o << "digraph g { \nnode [shape=plaintext]\n";
1073       unsigned send = states_.size();
1074       if (dsi & DSI_StatesHeader)
1075         {
1076           o << ("states [label=<\n"
1077                 "<table border='0' cellborder='1' cellspacing='0'>\n"
1078                 "<tr><td sides='b' bgcolor='yellow' port='s'>states</td>\n");
1079           for (unsigned s = 0; s < send; ++s)
1080             o << "<td sides='b' bgcolor='yellow' port='s" << s << "'>"
1081               << s << "</td>\n";
1082           o << "</tr>\n";
1083         }
1084       if (dsi & DSI_StatesBody)
1085         {
1086           o << "<tr><td port='ss'>succ</td>\n";
1087           for (unsigned s = 0; s < send; ++s)
1088             {
1089               o << "<td port='ss" << s;
1090               if (states_[s].succ)
1091                 o << "' bgcolor='cyan";
1092               o << "'>" << states_[s].succ << "</td>\n";
1093             }
1094           o << "</tr><tr><td port='st'>succ_tail</td>\n";
1095           for (unsigned s = 0; s < send; ++s)
1096             {
1097               o << "<td port='st" << s;
1098               if (states_[s].succ_tail)
1099                 o << "' bgcolor='cyan";
1100               o << "'>" << states_[s].succ_tail << "</td>\n";
1101             }
1102           o << "</tr>\n";
1103         }
1104       if (dsi & DSI_StatesFooter)
1105         o << "</table>>]\n";
1106       unsigned eend = edges_.size();
1107       if (dsi & DSI_EdgesHeader)
1108         {
1109           o << ("edges [label=<\n"
1110                 "<table border='0' cellborder='1' cellspacing='0'>\n"
1111                 "<tr><td sides='b' bgcolor='cyan' port='e'>edges</td>\n");
1112           for (unsigned e = 1; e < eend; ++e)
1113             {
1114               o << "<td sides='b' bgcolor='"
1115                 << (e != edges_[e].next_succ ? "cyan" : "gray")
1116                 << "' port='e" << e << "'>" << e << "</td>\n";
1117             }
1118           o << "</tr>";
1119         }
1120       if (dsi & DSI_EdgesBody)
1121         {
1122           o << "<tr><td port='ed'>dst</td>\n";
1123           for (unsigned e = 1; e < eend; ++e)
1124             {
1125               o << "<td port='ed" << e;
1126               int d = edges_[e].dst;
1127               if (d < 0)
1128                 o << "' bgcolor='pink'>~" << ~d;
1129               else
1130                 o << "' bgcolor='yellow'>" << d;
1131               o << "</td>\n";
1132             }
1133           o << "</tr><tr><td port='en'>next_succ</td>\n";
1134           for (unsigned e = 1; e < eend; ++e)
1135             {
1136               o << "<td port='en" << e;
1137               if (edges_[e].next_succ)
1138                 {
1139                   if (edges_[e].next_succ != e)
1140                     o << "' bgcolor='cyan";
1141                   else
1142                     o << "' bgcolor='gray";
1143                 }
1144               o << "'>" << edges_[e].next_succ << "</td>\n";
1145             }
1146           o << "</tr><tr><td port='es'>src</td>\n";
1147           for (unsigned e = 1; e < eend; ++e)
1148             o << "<td port='es" << e << "' bgcolor='yellow'>"
1149               << edges_[e].src << "</td>\n";
1150           o << "</tr>\n";
1151         }
1152       if (dsi & DSI_EdgesFooter)
1153         o << "</table>>]\n";
1154       if (!dests_.empty())
1155         {
1156           unsigned dend = dests_.size();
1157           if (dsi & DSI_DestsHeader)
1158             {
1159               o << ("dests [label=<\n"
1160                     "<table border='0' cellborder='1' cellspacing='0'>\n"
1161                     "<tr><td sides='b' bgcolor='pink' port='d'>dests</td>\n");
1162               unsigned d = 0;
1163               while (d < dend)
1164                 {
1165                   o << "<td sides='b' bgcolor='pink' port='d"
1166                     << d << "'>~" << d << "</td>\n";
1167                   unsigned cnt = dests_[d];
1168                   d += cnt + 1;
1169                   while (cnt--)
1170                     o << "<td sides='b'></td>\n";
1171                 }
1172               o << "</tr>\n";
1173             }
1174           if (dsi & DSI_DestsBody)
1175             {
1176               o << "<tr><td port='dd'>#cnt/dst</td>\n";
1177               unsigned d = 0;
1178               while (d < dend)
1179                 {
1180                   unsigned cnt = dests_[d];
1181                   o << "<td port='d'>#" << cnt << "</td>\n";
1182                   ++d;
1183                   while (cnt--)
1184                     {
1185                       o << "<td bgcolor='yellow' port='dd"
1186                         << d << "'>" << dests_[d] << "</td>\n";
1187                       ++d;
1188                     }
1189                 }
1190               o << "</tr>\n";
1191             }
1192           if (dsi & DSI_DestsFooter)
1193             o << "</table>>]\n";
1194         }
1195       if (dsi & DSI_GraphFooter)
1196         o << "}\n";
1197     }
1198 
1199     /// \brief Remove all dead edges.
1200     ///
1201     /// The edges_ vector is left in a state that is incorrect and
1202     /// should eventually be fixed by a call to chain_edges_() before
1203     /// any iteration on the successor of a state is performed.
remove_dead_edges_()1204     void remove_dead_edges_()
1205     {
1206       if (killed_edge_ == 0)
1207         return;
1208       auto i = std::remove_if(edges_.begin() + 1, edges_.end(),
1209                               [this](const edge_storage_t& t) {
1210                                 return this->is_dead_edge(t);
1211                               });
1212       edges_.erase(i, edges_.end());
1213       killed_edge_ = 0;
1214     }
1215 
1216     /// \brief Sort all edges according to a predicate
1217     ///
1218     /// This will invalidate all iterators, and also destroy edge
1219     /// chains.  Call chain_edges_() immediately afterwards unless you
1220     /// know what you are doing.
1221     template<class Predicate = std::less<edge_storage_t>>
sort_edges_(Predicate p=Predicate ())1222     void sort_edges_(Predicate p = Predicate())
1223     {
1224       //std::cerr << "\nbefore\n";
1225       //dump_storage(std::cerr);
1226       std::stable_sort(edges_.begin() + 1, edges_.end(), p);
1227     }
1228 
1229     /// \brief Sort edges of the given states
1230     ///
1231     /// \tparam Predicate : Comparison type
1232     /// \param p : Comparison callable
1233     /// \param to_sort_ptr : which states to sort. If null, all will be sorted
1234     /// \note No need to call chain_edges_, they are in a coherent state.
1235     /// todo: If pred does not involve bdd action other than id -> parallelize
1236     template<bool Stable = false, class Predicate = std::less<edge_storage_t>>
sort_edges_of_(Predicate p=Predicate (),const std::vector<bool> * to_sort_ptr=nullptr)1237     void sort_edges_of_(Predicate p = Predicate(),
1238                         const std::vector<bool>* to_sort_ptr = nullptr)
1239     {
1240       SPOT_ASSERT((to_sort_ptr == nullptr)
1241                   || (to_sort_ptr->size() == num_states()));
1242       //std::cerr << "\nbefore\n";
1243       //dump_storage(std::cerr);
1244       auto pi = [&](unsigned t1, unsigned t2)
1245           {return p(edges_[t1], edges_[t2]); };
1246       std::vector<unsigned> sort_idx_;
1247       for (unsigned i = 0; i < num_states(); ++i)
1248         {
1249           if (to_sort_ptr && !(*to_sort_ptr)[i])
1250             continue;
1251 
1252           sort_idx_.clear();
1253           unsigned t = states_[i].succ;
1254           do
1255             {
1256                sort_idx_.push_back(t);
1257                t = edges_[t].next_succ;
1258             } while (t != 0);
1259           if constexpr (Stable)
1260             std::stable_sort(sort_idx_.begin(), sort_idx_.end(), pi);
1261           else
1262             std::sort(sort_idx_.begin(), sort_idx_.end(), pi);
1263           // Update the graph
1264           states_[i].succ = sort_idx_.front();
1265           states_[i].succ_tail = sort_idx_.back();
1266           const unsigned n_outs_n1 = sort_idx_.size() - 1;
1267           for (unsigned k = 0; k < n_outs_n1; ++k)
1268             edges_[sort_idx_[k]].next_succ = sort_idx_[k+1];
1269           edges_[sort_idx_.back()].next_succ = 0; // terminal
1270         }
1271       // Done
1272     }
1273 
1274     /// \brief Reconstruct the chain of outgoing edges
1275     ///
1276     /// Should be called only when it is known that all edges
1277     /// with the same source are consecutive in the vector.
chain_edges_()1278     void chain_edges_()
1279     {
1280       state last_src = -1U;
1281       edge tend = edges_.size();
1282       for (edge t = 1; t < tend; ++t)
1283         {
1284           state src = edges_[t].src;
1285           if (src != last_src)
1286             {
1287               states_[src].succ = t;
1288               if (last_src != -1U)
1289                 {
1290                   states_[last_src].succ_tail = t - 1;
1291                   edges_[t - 1].next_succ = 0;
1292                 }
1293               while (++last_src != src)
1294                 {
1295                   states_[last_src].succ = 0;
1296                   states_[last_src].succ_tail = 0;
1297                 }
1298             }
1299           else
1300             {
1301               edges_[t - 1].next_succ = t;
1302             }
1303         }
1304       if (last_src != -1U)
1305         {
1306           states_[last_src].succ_tail = tend - 1;
1307           edges_[tend - 1].next_succ = 0;
1308         }
1309       unsigned send = states_.size();
1310       while (++last_src != send)
1311         {
1312           states_[last_src].succ = 0;
1313           states_[last_src].succ_tail = 0;
1314         }
1315       //std::cerr << "\nafter\n";
1316       //dump_storage(std::cerr);
1317     }
1318 
1319     /// \brief Rename all the states in the edge vector.
1320     ///
1321     /// The edges_ vector is left in a state that is incorrect and
1322     /// should eventually be fixed by a call to chain_edges_() before
1323     /// any iteration on the successor of a state is performed.
rename_states_(const std::vector<unsigned> & newst)1324     void rename_states_(const std::vector<unsigned>& newst)
1325     {
1326       SPOT_ASSERT(newst.size() == states_.size());
1327       unsigned tend = edges_.size();
1328       for (unsigned t = 1; t < tend; t++)
1329         {
1330           edges_[t].dst = newst[edges_[t].dst];
1331           edges_[t].src = newst[edges_[t].src];
1332         }
1333     }
1334 
1335     /// \brief Rename and remove states.
1336     ///
1337     /// This method is used to remove some states that have been
1338     /// previously detected to be unreachable in order to "defragment"
1339     /// the state vector.  When a state is removed, all its outgoing
1340     /// transition are removed as well.  Removing reachable states
1341     /// should NOT be attempted, because the incoming edges will be
1342     /// dangling.
1343     ///
1344     /// \param newst A vector indicating how each state should be
1345     /// renumbered.  Use -1U to erase an unreachable state.  All other
1346     /// numbers are expected to satisfy newst[i] ≤ i for all i.
1347     ///
1348     /// \param used_states the number of states used (after
1349     /// renumbering)
1350     ///
1351     ///@{
defrag_states(const std::vector<unsigned> & newst,unsigned used_states)1352     void defrag_states(const std::vector<unsigned>& newst, unsigned used_states)
1353     {
1354       SPOT_ASSERT(newst.size() >= states_.size());
1355       SPOT_ASSERT(used_states > 0);
1356 
1357       //std::cerr << "\nbefore defrag\n";
1358       //dump_storage(std::cerr);
1359 
1360       // Shift all states in states_, as indicated by newst.
1361       unsigned send = states_.size();
1362       for (state s = 0; s < send; ++s)
1363         {
1364           state dst = newst[s];
1365           if (dst == s)
1366             continue;
1367           if (dst == -1U)
1368             {
1369               // This is an erased state.  Mark all its edges as
1370               // dead (i.e., t.next_succ should point to t for each of
1371               // them).
1372               auto t = states_[s].succ;
1373               while (t)
1374                 std::swap(t, edges_[t].next_succ);
1375               continue;
1376             }
1377           states_[dst] = std::move(states_[s]);
1378         }
1379       states_.resize(used_states);
1380 
1381       // Shift all edges in edges_.  The algorithm is
1382       // similar to remove_if, but it also keeps the correspondence
1383       // between the old and new index as newidx[old] = new.
1384       unsigned tend = edges_.size();
1385       std::vector<edge> newidx(tend);
1386       unsigned dest = 1;
1387       for (edge t = 1; t < tend; ++t)
1388         {
1389           if (is_dead_edge(t))
1390             continue;
1391           if (t != dest)
1392             edges_[dest] = std::move(edges_[t]);
1393           newidx[t] = dest;
1394           ++dest;
1395         }
1396       edges_.resize(dest);
1397       killed_edge_ = 0;
1398 
1399       // Adjust next_succ and dst pointers in all edges.
1400       for (edge t = 1; t < dest; ++t)
1401         {
1402           auto& tr = edges_[t];
1403           tr.src = newst[tr.src];
1404           tr.dst = newst[tr.dst];
1405           tr.next_succ = newidx[tr.next_succ];
1406         }
1407 
1408       // Adjust succ and succ_tails pointers in all states.
1409       for (auto& s: states_)
1410         {
1411           s.succ = newidx[s.succ];
1412           s.succ_tail = newidx[s.succ_tail];
1413         }
1414 
1415       //std::cerr << "\nafter defrag\n";
1416       //dump_storage(std::cerr);
1417     }
1418 
1419     // prototype was changed in Spot 2.10
1420     SPOT_DEPRECATED("use reference version of this method")
defrag_states(std::vector<unsigned> && newst,unsigned used_states)1421     void defrag_states(std::vector<unsigned>&& newst, unsigned used_states)
1422     {
1423       return defrag_states(newst, used_states);
1424     }
1425     ///@}
1426   };
1427 }
1428