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