1 // -*- coding: utf-8 -*-
2 // Copyright (C) 2015-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 #include "config.h"
21 #include <algorithm>
22 #include <deque>
23 #include <stack>
24 #include <utility>
25 #include <unordered_map>
26 #include <set>
27 #include <map>
28 
29 #include <spot/misc/bddlt.hh>
30 #include <spot/twaalgos/sccinfo.hh>
31 #include <spot/twaalgos/determinize.hh>
32 #include <spot/twaalgos/degen.hh>
33 #include <spot/twaalgos/sccfilter.hh>
34 #include <spot/twaalgos/simulation.hh>
35 #include <spot/twaalgos/isdet.hh>
36 #include <spot/twaalgos/parity.hh>
37 #include <spot/priv/robin_hood.hh>
38 
39 namespace spot
40 {
41   namespace
42   {
43     // forward declaration
44     struct safra_build;
45     class compute_succs;
46   }
47 
48   class safra_state final
49   {
50   public:
51     // a helper method to check invariants
52     void
check() const53     check() const
54     {
55       // do not refer to braces that do not exist
56       for (const auto& p : nodes_)
57         if (p.second >= 0)
58           if (((unsigned)p.second) >= braces_.size())
59             assert(false);
60 
61       // braces_ describes the parenthood relation, -1 meaning toplevel
62       // so braces_[b] < b always, and -1 is the only negative number allowed
63       for (int b : braces_)
64         {
65           if (b < 0 && b != -1)
66             assert(false);
67           if (b >= 0 && braces_[b] > b)
68             assert(false);
69         }
70 
71       // no unused braces
72       std::set<int> used_braces;
73       for (const auto& n : nodes_)
74         {
75           int b = n.second;
76           while (b >= 0)
77             {
78               used_braces.insert(b);
79               b = braces_[b];
80             }
81         }
82       assert(used_braces.size() == braces_.size());
83     }
84 
85   public:
86     using state_t = unsigned;
87     using safra_node_t = std::pair<state_t, std::vector<int>>;
88 
89     bool operator<(const safra_state&) const;
90     bool operator==(const safra_state&) const;
91     size_t hash() const;
92 
93     // Print the number of states in each brace
94     // default constructor
95     safra_state();
96     safra_state(state_t state_number, bool acceptance_scc = false);
97     safra_state(const safra_build& s, const compute_succs& cs, unsigned& color,
98                 unsigned topbrace);
99     // Compute successor for transition ap
100     safra_state
101     compute_succ(const compute_succs& cs, const bdd& ap, unsigned& color) const;
102     void
103     merge_redundant_states(const std::vector<std::vector<char>>& implies);
104     unsigned
105     finalize_construction(const std::vector<int>& buildbraces,
106                           const compute_succs& cs, unsigned topbrace);
107 
108     // each brace points to its parent.
109     // braces_[i] is the parent of i
110     // Note that braces_[i] < i, -1 stands for "no parent" (top-level)
111     std::vector<int> braces_;
112     std::vector<std::pair<state_t, int>> nodes_;
113   };
114 
115   namespace
116   {
117     struct hash_safra
118     {
119       size_t
operator ()spot::__anonbe87bc0b0211::hash_safra120       operator()(const safra_state& s) const noexcept
121       {
122         return s.hash();
123       }
124     };
125 
126     template<class T>
127     struct ref_wrap_equal
128     {
129       bool
operator ()spot::__anonbe87bc0b0211::ref_wrap_equal130       operator()(const std::reference_wrapper<T>& x,
131                  const std::reference_wrapper<T>& y) const
132       {
133         return std::equal_to<T>()(x.get(), y.get());
134       }
135     };
136 
137     using power_set =
138       robin_hood::unordered_node_map<safra_state, unsigned, hash_safra>;
139 
140     std::string
141     nodes_to_string(const const_twa_graph_ptr& aut,
142                     const safra_state& states);
143 
144     // Returns true if lhs has a smaller nesting pattern than rhs
145     // If lhs and rhs are the same, return false.
146     // NB the nesting patterns are backwards.
nesting_cmp(const std::vector<int> & lhs,const std::vector<int> & rhs)147     bool nesting_cmp(const std::vector<int>& lhs,
148                      const std::vector<int>& rhs)
149     {
150       unsigned m = std::min(lhs.size(), rhs.size());
151       auto lit = lhs.rbegin();
152       auto rit = rhs.rbegin();
153       for (unsigned i = 0; i != m; ++i)
154         {
155           if (*lit != *rit)
156             return *lit < *rit;
157         }
158       return lhs.size() > rhs.size();
159     }
160 
161     // a helper class for building the successor of a safra_state
162     struct safra_build final
163     {
164       std::vector<int> braces_;
165       std::map<unsigned, int> nodes_;
166 
167       bool
compare_bracesspot::__anonbe87bc0b0211::safra_build168       compare_braces(int a, int b)
169       {
170         std::vector<int> a_pattern;
171         std::vector<int> b_pattern;
172         a_pattern.reserve(a+1);
173         b_pattern.reserve(b+1);
174         while (a != b)
175           {
176             if (a > b)
177               {
178                 a_pattern.emplace_back(a);
179                 a = braces_[a];
180               }
181             else
182               {
183                 b_pattern.emplace_back(b);
184                 b = braces_[b];
185               }
186           }
187         return nesting_cmp(a_pattern, b_pattern);
188       }
189 
190       // Used when creating the list of successors
191       // A new intermediate node is created with src's braces and with dst as id
192       // A merge is done if dst already existed in *this
193       void
update_succspot::__anonbe87bc0b0211::safra_build194       update_succ(int brace, unsigned dst, const acc_cond::mark_t& acc)
195       {
196         int newb = brace;
197         if (acc)
198           {
199             assert(acc.has(0) && acc.is_singleton() && "Only TBA are accepted");
200             // Accepting edges generate new braces: step A1
201             newb = braces_.size();
202             braces_.emplace_back(brace);
203           }
204         auto i = nodes_.emplace(dst, newb);
205         if (!i.second) // dst already exists
206           {
207             // Step A2: Only keep the smallest nesting pattern.
208             // Use nesting_cmp to compare nesting patterns.
209             if (compare_braces(newb, i.first->second))
210               {
211                 i.first->second = newb;
212               }
213             else
214               {
215                 if (newb != brace) // new brace was created but is not needed
216                   braces_.pop_back();
217               }
218           }
219       }
220 
221       // Same as above, specialized for brace == -1
222       // Acceptance parameter is passed as a template parameter to improve
223       // performance.
224       // If a node for dst already existed, the newly inserted node has smaller
225       // nesting pattern iff is_acc == true AND nodes_[dst] == -1
226       template<bool is_acc>
227       void
update_succ_toplevelspot::__anonbe87bc0b0211::safra_build228       update_succ_toplevel(unsigned dst)
229       {
230         if (is_acc)
231           {
232             // Accepting edges generate new braces: step A1
233             int newb = braces_.size();
234             auto i = nodes_.emplace(dst, newb);
235             if (i.second || i.first->second == -1)
236               {
237                 braces_.emplace_back(-1);
238                 i.first->second = newb;
239               }
240           }
241         else
242           {
243             nodes_.emplace(dst, -1);
244           }
245       }
246 
247     };
248 
249     // Given a certain transition_label, compute all the successors of a
250     // safra_state under that label, and return the new nodes in res.
251     class compute_succs final
252     {
253       friend class spot::safra_state;
254 
255       const safra_state* src;
256       const std::vector<bdd>* all_bdds;
257       const const_twa_graph_ptr& aut;
258       const power_set& seen;
259       const scc_info& scc;
260       const std::vector<std::vector<char>>& implies;
261       bool use_scc;
262       bool use_simulation;
263       bool use_stutter;
264 
265       // work vectors for safra_state::finalize_construction()
266       mutable std::vector<char> empty_green;
267       mutable std::vector<int> highest_green_ancestor;
268       mutable std::vector<unsigned> decr_by;
269       mutable safra_build ss;
270 
271     public:
compute_succs(const const_twa_graph_ptr & aut,const power_set & seen,const scc_info & scc,const std::vector<std::vector<char>> & implies,bool use_scc,bool use_simulation,bool use_stutter)272       compute_succs(const const_twa_graph_ptr& aut,
273                     const power_set& seen,
274                     const scc_info& scc,
275                     const std::vector<std::vector<char>>& implies,
276                     bool use_scc,
277                     bool use_simulation,
278                     bool use_stutter)
279       : src(nullptr)
280       , all_bdds(nullptr)
281       , aut(aut)
282       , seen(seen)
283       , scc(scc)
284       , implies(implies)
285       , use_scc(use_scc)
286       , use_simulation(use_simulation)
287       , use_stutter(use_stutter)
288       {}
289 
290       void
set(const safra_state & s,const std::vector<bdd> & v)291       set(const safra_state& s, const std::vector<bdd>& v)
292       {
293         src = &s;
294         all_bdds = &v;
295       }
296 
297       struct iterator
298       {
299         const compute_succs& cs_;
300         std::vector<bdd>::const_iterator bddit;
301         safra_state ss;
302         unsigned color_;
303 
iteratorspot::__anonbe87bc0b0211::compute_succs::iterator304         iterator(const compute_succs& c, std::vector<bdd>::const_iterator it)
305         : cs_(c)
306         , bddit(it)
307         {
308           compute_();
309         }
310 
311         bool
operator !=spot::__anonbe87bc0b0211::compute_succs::iterator312         operator!=(const iterator& other) const
313         {
314           return bddit != other.bddit;
315         }
316 
317         iterator&
operator ++spot::__anonbe87bc0b0211::compute_succs::iterator318         operator++()
319         {
320           ++bddit;
321           compute_();
322           return *this;
323         }
324         // no need to implement postfix increment
325 
326         const bdd&
condspot::__anonbe87bc0b0211::compute_succs::iterator327         cond() const
328         {
329           return *bddit;
330         }
331 
332         const safra_state&
operator *spot::__anonbe87bc0b0211::compute_succs::iterator333         operator*() const
334         {
335           return ss;
336         }
337         const safra_state*
operator ->spot::__anonbe87bc0b0211::compute_succs::iterator338         operator->() const
339         {
340           return &ss;
341         }
342 
343       private:
344         std::vector<safra_state> stutter_path_;
345 
346         void
compute_spot::__anonbe87bc0b0211::compute_succs::iterator347         compute_()
348         {
349           if (bddit == cs_.all_bdds->end())
350             return;
351 
352           const bdd& ap = *bddit;
353 
354           // In stutter-invariant automata, every time we follow a
355           // transition labeled by L, we can actually stutter the L
356           // label and jump further away.  The following code performs
357           // this stuttering until a cycle is found, and select one
358           // state of the cycle as the destination to jump to.
359           if (cs_.use_stutter && cs_.aut->prop_stutter_invariant())
360             {
361               ss = *cs_.src;
362               // The path is usually quite small (3-4 states), so it's
363               // not worth setting up a hash table to detect a cycle.
364               stutter_path_.clear();
365               std::vector<safra_state>::iterator cycle_seed;
366               unsigned mincolor = -1U;
367               // stutter forward until we cycle
368               for (;;)
369                 {
370                   // any duplicate value, if any, is usually close to
371                   // the end, so search backward.
372                   auto it = std::find(stutter_path_.rbegin(),
373                                       stutter_path_.rend(), ss);
374                   if (it != stutter_path_.rend())
375                     {
376                       cycle_seed = (it + 1).base();
377                       break;
378                     }
379                   stutter_path_.emplace_back(std::move(ss));
380                   ss = stutter_path_.back().compute_succ(cs_, ap, color_);
381                   mincolor = std::min(color_, mincolor);
382                 }
383               bool in_seen = cs_.seen.find(*cycle_seed) != cs_.seen.end();
384               for (auto it = cycle_seed + 1; it < stutter_path_.end(); ++it)
385                 {
386                   if (in_seen)
387                     {
388                       // if *cycle_seed is already in seen, replace
389                       // it with a smaller state also in seen.
390                       if (cs_.seen.find(*it) != cs_.seen.end()
391                           && *it < *cycle_seed)
392                         cycle_seed = it;
393                     }
394                   else
395                     {
396                       // if *cycle_seed is not in seen, replace it
397                       // either with a state in seen or with a smaller
398                       // state
399                       if (cs_.seen.find(*it) != cs_.seen.end())
400                         {
401                           cycle_seed = it;
402                           in_seen = true;
403                         }
404                       else if (*it < *cycle_seed)
405                         {
406                           cycle_seed = it;
407                         }
408                     }
409                 }
410               ss = std::move(*cycle_seed);
411               color_ = mincolor;
412             }
413           else
414             {
415               ss = cs_.src->compute_succ(cs_, ap, color_);
416             }
417         }
418       };
419 
420       iterator
begin() const421       begin() const
422       {
423         return iterator(*this, all_bdds->begin());
424       }
425       iterator
end() const426       end() const
427       {
428         return iterator(*this, all_bdds->end());
429       }
430     };
431 
432     const char* const sub[10] =
433       {
434         "\u2080",
435         "\u2081",
436         "\u2082",
437         "\u2083",
438         "\u2084",
439         "\u2085",
440         "\u2086",
441         "\u2087",
442         "\u2088",
443         "\u2089",
444       };
445 
subscript(unsigned start)446     std::string subscript(unsigned start)
447     {
448       std::string res;
449       do
450         {
451           res = sub[start % 10] + res;
452           start /= 10;
453         }
454       while (start);
455       return res;
456     }
457 
458     struct compare
459     {
460       bool
operator ()spot::__anonbe87bc0b0211::compare461       operator() (const safra_state::safra_node_t& lhs,
462                   const safra_state::safra_node_t& rhs) const
463       {
464         return lhs.second < rhs.second;
465       }
466     };
467 
468     // Return the nodes sorted in ascending order
469     std::vector<safra_state::safra_node_t>
sorted_nodes(const safra_state & s)470     sorted_nodes(const safra_state& s)
471     {
472       std::vector<safra_state::safra_node_t> res;
473       for (const auto& n: s.nodes_)
474         {
475           int brace = n.second;
476           std::vector<int> tmp;
477           while (brace >= 0)
478             {
479               // FIXME is not there a smarter way?
480               tmp.insert(tmp.begin(), brace);
481               brace = s.braces_[brace];
482             }
483           res.emplace_back(n.first, std::move(tmp));
484         }
485       std::sort(res.begin(), res.end(), compare());
486       return res;
487     }
488 
489     std::string
nodes_to_string(const const_twa_graph_ptr & aut,const safra_state & states)490     nodes_to_string(const const_twa_graph_ptr& aut,
491                     const safra_state& states)
492     {
493       auto copy = sorted_nodes(states);
494       std::ostringstream os;
495       std::stack<int> s;
496       bool first = true;
497       for (const auto& n: copy)
498         {
499           auto it = n.second.begin();
500           // Find brace on top of stack in vector
501           // If brace is not present, then we close it as no other ones of that
502           // type will be found since we ordered our vector
503           while (!s.empty())
504             {
505               it = std::lower_bound(n.second.begin(), n.second.end(),
506                                     s.top());
507               if (it == n.second.end() || *it != s.top())
508                 {
509                   os << subscript(s.top()) << '}';
510                   s.pop();
511                 }
512               else
513                 {
514                   if (*it == s.top())
515                     ++it;
516                   break;
517                 }
518             }
519           // Add new braces
520           while (it != n.second.end())
521             {
522               os << '{' << subscript(*it);
523               s.push(*it);
524               ++it;
525               first = true;
526             }
527           if (!first)
528             os << ' ';
529           os << aut->format_state(n.first);
530           first = false;
531         }
532       // Finish unwinding stack to print last braces
533       while (!s.empty())
534         {
535           os << subscript(s.top()) << '}';
536           s.pop();
537         }
538       return os.str();
539     }
540 
541     std::vector<std::string>*
print_debug(const const_twa_graph_ptr & aut,const power_set & states)542     print_debug(const const_twa_graph_ptr& aut,
543                 const power_set& states)
544     {
545       auto res = new std::vector<std::string>(states.size());
546       for (const auto& p: states)
547         (*res)[p.second] = nodes_to_string(aut, p.first);
548       return res;
549     }
550 
551     class safra_support
552     {
553       const std::vector<bdd>& state_supports;
554       robin_hood::unordered_flat_map<bdd, std::vector<bdd>, bdd_hash> cache;
555 
556     public:
safra_support(const std::vector<bdd> & s)557       safra_support(const std::vector<bdd>& s): state_supports(s) {}
558 
559       const std::vector<bdd>&
get(const safra_state & s)560       get(const safra_state& s)
561       {
562         bdd supp = bddtrue;
563         for (const auto& n : s.nodes_)
564           supp &= state_supports[n.first];
565         auto i = cache.emplace(supp, std::vector<bdd>());
566         if (i.second) // insertion took place
567           {
568             std::vector<bdd>& res = i.first->second;
569             for (bdd one: minterms_of(bddtrue, supp))
570               res.emplace_back(one);
571           }
572         return i.first->second;
573       }
574     };
575   }
576 
577   std::vector<char> find_scc_paths(const scc_info& scc);
578 
579   safra_state
compute_succ(const compute_succs & cs,const bdd & ap,unsigned & color) const580   safra_state::compute_succ(const compute_succs& cs,
581                             const bdd& ap, unsigned& color) const
582   {
583     safra_build& ss = cs.ss;
584     ss.braces_ = braces_; // copy
585     ss.nodes_.clear();
586     unsigned topbrace = braces_.size();
587     for (const auto& node: nodes_)
588       {
589         for (const auto& t: cs.aut->out(node.first))
590           {
591             if (!bdd_implies(ap, t.cond))
592               continue;
593             // Check if we are leaving the SCC, if so we delete all the
594             // braces as no cycles can be found with that node
595             if (cs.use_scc && cs.scc.scc_of(node.first) != cs.scc.scc_of(t.dst))
596               if (cs.scc.is_accepting_scc(cs.scc.scc_of(t.dst)))
597                 // Entering accepting SCC so add brace
598                 ss.update_succ_toplevel<true>(t.dst);
599               else
600                 // When entering non accepting SCC don't create any braces
601                 ss.update_succ_toplevel<false>(t.dst);
602             else
603               ss.update_succ(node.second, t.dst, t.acc);
604           }
605       }
606     return safra_state(ss, cs, color, topbrace);
607   }
608 
609   // When a node a implies a node b, remove the node a.
610   void
merge_redundant_states(const std::vector<std::vector<char>> & implies)611   safra_state::merge_redundant_states(
612       const std::vector<std::vector<char>>& implies)
613   {
614     auto it1 = nodes_.begin();
615     while (it1 != nodes_.end())
616       {
617         const auto& imp1 = implies[it1->first];
618         auto old_it1 = it1++;
619         if (imp1.empty())
620           continue;
621         for (auto it2 = nodes_.begin(); it2 != nodes_.end(); ++it2)
622           {
623             if (old_it1 == it2)
624               continue;
625             if (imp1[it2->first])
626               {
627                 it1 = nodes_.erase(old_it1);
628                 break;
629               }
630           }
631       }
632   }
633 
634   // Return the emitted color, red or green
635   unsigned
finalize_construction(const std::vector<int> & buildbraces,const compute_succs & cs,unsigned topbrace)636   safra_state::finalize_construction(const std::vector<int>& buildbraces,
637                                      const compute_succs& cs, unsigned topbrace)
638   {
639     unsigned red = -1U;
640     unsigned green = -1U;
641     // use std::vector<char> to avoid std::vector<bool>
642     // a char encodes several bools:
643     //  * first bit says whether the brace is empty and red
644     //  * second bit says whether the brace is green
645     // brackets removed from green pairs can be safely be marked as red,
646     // because their enclosing green has a lower number
647     // beware of pairs marked both as red and green: they are actually empty
648     constexpr char is_empty = 1;
649     constexpr char is_green = 2;
650     cs.empty_green.assign(buildbraces.size(), is_empty | is_green);
651 
652     for (const auto& n : nodes_)
653       if (n.second >= 0)
654         {
655           int brace = n.second;
656           // Step A4: For a brace to be green it must not contain states
657           // on its own.
658           cs.empty_green[brace] &= ~is_green;
659           while (brace >= 0 && (cs.empty_green[brace] & is_empty))
660             {
661               cs.empty_green[brace] &= ~is_empty;
662               brace = buildbraces[brace];
663             }
664         }
665 
666     // Step A4 Remove brackets within green pairs
667     // for each bracket, find its highest green ancestor
668     // 0 cannot be in a green pair, its highest green ancestor is itself
669     // Also find red and green signals to emit
670     // And compute the number of braces to remove for renumbering
671     cs.highest_green_ancestor.assign(buildbraces.size(), 0);
672     cs.decr_by.assign(buildbraces.size(), 0);
673     unsigned decr = 0;
674     for (unsigned b = 0; b != buildbraces.size(); ++b)
675       {
676         cs.highest_green_ancestor[b] = b;
677         const int& ancestor = buildbraces[b];
678         // Note that ancestor < b
679         if (ancestor >= 0
680             && (cs.highest_green_ancestor[ancestor] != ancestor
681                 || (cs.empty_green[ancestor] & is_green)))
682           {
683             cs.highest_green_ancestor[b] = cs.highest_green_ancestor[ancestor];
684             cs.empty_green[b] |= is_empty; // mark brace for removal
685           }
686 
687         if (cs.empty_green[b] & is_empty)
688           {
689             // Step A5 renumber braces
690             ++decr;
691 
692             // Any brace above topbrace was added while constructing
693             // this successor, so it should not emit any red.
694             if (b < topbrace)
695               // Step A3 emit red
696               red = std::min(red, 2*b);
697           }
698         else if (cs.empty_green[b] & is_green)
699           {
700             assert(b < topbrace);
701             // Step A4 emit green
702             green = std::min(green, 2*b+1);
703           }
704 
705         cs.decr_by[b] = decr;
706       }
707 
708     // Update nodes with new braces numbers
709     braces_ = std::vector<int>(buildbraces.size() - decr, -1);
710     for (auto& n : nodes_)
711       {
712         if (n.second >= 0)
713           {
714             unsigned i = cs.highest_green_ancestor[n.second];
715             int j = buildbraces[i] >=0
716                       ? buildbraces[i] - cs.decr_by[buildbraces[i]]
717                       : -1;
718             n.second = i - cs.decr_by[i];
719             braces_[n.second] = j;
720           }
721       }
722 
723     return std::min(red, green);
724   }
725 
safra_state()726   safra_state::safra_state()
727   : nodes_{std::make_pair(0, -1)}
728   {}
729 
730   // Called only to initialize first state
safra_state(state_t val,bool accepting_scc)731   safra_state::safra_state(state_t val, bool accepting_scc)
732   : nodes_{std::make_pair(val, -1)}
733   {
734     if (accepting_scc)
735       {
736         braces_.emplace_back(-1);
737         nodes_.back().second = 0;
738       }
739   }
740 
safra_state(const safra_build & s,const compute_succs & cs,unsigned & color,unsigned topbrace)741   safra_state::safra_state(const safra_build& s,
742                            const compute_succs& cs,
743                            unsigned& color,
744                            unsigned topbrace)
745   : nodes_(s.nodes_.begin(), s.nodes_.end())
746   {
747     if (cs.use_simulation)
748       merge_redundant_states(cs.implies);
749     color = finalize_construction(s.braces_, cs, topbrace);
750   }
751 
752   bool
operator <(const safra_state & other) const753   safra_state::operator<(const safra_state& other) const
754   {
755     // FIXME what is the right, if any, comparison to perform?
756     return braces_ == other.braces_ ? nodes_ < other.nodes_
757                                     : braces_ < other.braces_;
758   }
759   size_t
hash() const760   safra_state::hash() const
761   {
762     size_t res = 0;
763     //std::cerr << this << " [";
764     for (const auto& p : nodes_)
765       {
766         res ^= (res << 3) ^ p.first;
767         res ^= (res << 3) ^ p.second;
768         //  std::cerr << '(' << p.first << ',' << p.second << ')';
769       }
770     //    std::cerr << "][ ";
771     for (const auto& b : braces_)
772       {
773         res ^= (res << 3) ^ b;
774         //  std::cerr << b << ' ';
775       }
776     //    std::cerr << "]: " << std::hex << res << std::dec << '\n';
777     return res;
778   }
779 
780   bool
operator ==(const safra_state & other) const781   safra_state::operator==(const safra_state& other) const
782   {
783     return nodes_ == other.nodes_ && braces_ == other.braces_;
784   }
785 
786   // res[i + scccount*j] = 1 iff SCC i is reachable from SCC j
787   std::vector<char>
find_scc_paths(const scc_info & scc)788   find_scc_paths(const scc_info& scc)
789   {
790     unsigned scccount = scc.scc_count();
791     std::vector<char> res(scccount * scccount, 0);
792     for (unsigned i = 0; i != scccount; ++i)
793       res[i + scccount * i] = 1;
794     for (unsigned i = 0; i != scccount; ++i)
795       {
796         unsigned ibase = i * scccount;
797         for (unsigned d: scc.succ(i))
798           {
799             // we necessarily have d < i because of the way SCCs are
800             // numbered, so we can build the transitive closure by
801             // just ORing any SCC reachable from d.
802             unsigned dbase = d * scccount;
803             for (unsigned j = 0; j != scccount; ++j)
804               res[ibase + j] |= res[dbase + j];
805           }
806       }
807     return res;
808   }
809 
810   twa_graph_ptr
tgba_determinize(const const_twa_graph_ptr & a,bool pretty_print,bool use_scc,bool use_simulation,bool use_stutter,const output_aborter * aborter,int trans_pruning)811   tgba_determinize(const const_twa_graph_ptr& a,
812                    bool pretty_print, bool use_scc,
813                    bool use_simulation, bool use_stutter,
814                    const output_aborter* aborter,
815                    int trans_pruning)
816   {
817     if (!a->is_existential())
818       throw std::runtime_error
819         ("tgba_determinize() does not support alternation");
820     if (is_universal(a))
821       return std::const_pointer_cast<twa_graph>(a);
822 
823     // Degeneralize
824     const_twa_graph_ptr aut;
825     std::vector<bdd> implications;
826     {
827       twa_graph_ptr aut_tmp = spot::degeneralize_tba(a);
828       if (pretty_print)
829         aut_tmp->copy_state_names_from(a);
830       if (use_simulation)
831         {
832           aut_tmp = spot::scc_filter(aut_tmp);
833           auto aut2 = simulation(aut_tmp, &implications, trans_pruning);
834           if (pretty_print)
835             aut2->copy_state_names_from(aut_tmp);
836           aut_tmp = aut2;
837         }
838       aut = aut_tmp;
839     }
840     scc_info_options scc_opt = scc_info_options::TRACK_SUCCS;
841     // We do need to track states in SCC for stutter invariance (see below how
842     // supports are computed in this case)
843     if (use_stutter && aut->prop_stutter_invariant())
844       scc_opt = scc_info_options::TRACK_SUCCS | scc_info_options::TRACK_STATES;
845     scc_info scc = scc_info(aut, scc_opt);
846 
847     // If use_simulation is false, implications is empty, so nothing is built
848     std::vector<std::vector<char>> implies(
849         implications.size(),
850         std::vector<char>(implications.size(), 0));
851     {
852       std::vector<char> is_connected = find_scc_paths(scc);
853       unsigned sccs = scc.scc_count();
854       bool something_implies_something = false;
855       for (unsigned i = 0; i != implications.size(); ++i)
856         {
857           // NB spot::simulation() does not remove unreachable states, as it
858           // would invalidate the contents of 'implications'.
859           // so we need to explicitly test for unreachable states
860           // FIXME based on the scc_info, we could remove the unreachable
861           // states, both in the input automaton and in 'implications'
862           // to reduce the size of 'implies'.
863           if (!scc.reachable_state(i))
864             continue;
865           unsigned scc_of_i = scc.scc_of(i);
866           bool i_implies_something = false;
867           for (unsigned j = 0; j != implications.size(); ++j)
868             {
869               if (!scc.reachable_state(j))
870                 continue;
871 
872               bool i_implies_j = !is_connected[sccs * scc.scc_of(j) + scc_of_i]
873                 && bdd_implies(implications[i], implications[j]);
874               implies[i][j] = i_implies_j;
875               i_implies_something |= i_implies_j;
876             }
877           // Clear useless lines.
878           if (!i_implies_something)
879             implies[i].clear();
880           else
881             something_implies_something = true;
882         }
883       if (!something_implies_something)
884         {
885           implies.clear();
886           use_simulation = false;
887         }
888     }
889 
890 
891     // Compute the support of each state
892     std::vector<bdd> support(aut->num_states());
893     if (use_stutter && aut->prop_stutter_invariant())
894       {
895         // FIXME this could be improved
896         // supports of states should account for possible stuttering if we plan
897         // to use stuttering invariance
898         for (unsigned c = 0; c != scc.scc_count(); ++c)
899           {
900             bdd c_supp = scc.scc_ap_support(c);
901             for (const auto& su: scc.succ(c))
902               c_supp &= support[scc.one_state_of(su)];
903             for (unsigned st: scc.states_of(c))
904               support[st] = c_supp;
905           }
906       }
907     else
908       {
909         for (unsigned i = 0; i != aut->num_states(); ++i)
910           {
911             bdd res = bddtrue;
912             for (const auto& e : aut->out(i))
913               res &= bdd_support(e.cond);
914             support[i] = res;
915           }
916       }
917 
918     safra_support safra2letters(support);
919 
920     auto res = make_twa_graph(aut->get_dict());
921     res->copy_ap_of(aut);
922     res->prop_copy(aut,
923                    { false, // state based
924                        false, // inherently_weak
925                        false, false, // deterministic
926                        false, // complete
927                        true // stutter inv
928                        });
929     // completeness can only be improved.
930     if (aut->prop_complete().is_true())
931       res->prop_complete(true);
932 
933     // Given a safra_state get its associated state in output automata.
934     // Required to create new edges from 2 safra-state
935     power_set seen;
936     // As per the standard, references to elements in a std::unordered_set or
937     // std::unordered_map are invalidated by erasure only.
938     std::deque<std::reference_wrapper<power_set::value_type>> todo;
939     auto get_state = [&res, &seen, &todo](const safra_state& s) -> unsigned
940       {
941         auto it = seen.find(s);
942         if (it == seen.end())
943           {
944             unsigned dst_num = res->new_state();
945             it = seen.emplace(s, dst_num).first;
946             todo.emplace_back(*it);
947           }
948         return it->second;
949       };
950 
951     {
952       unsigned init_state = aut->get_init_state_number();
953       bool start_accepting =
954         !use_scc || scc.is_accepting_scc(scc.scc_of(init_state));
955       safra_state init(init_state, start_accepting);
956       unsigned num = get_state(init); // inserts both in seen and in todo
957       res->set_init_state(num);
958     }
959     unsigned sets = 0;
960 
961     compute_succs succs(aut, seen, scc, implies, use_scc, use_simulation,
962                         use_stutter);
963     // The main loop
964     while (!todo.empty())
965       {
966         if (aborter && aborter->too_large(res))
967           return nullptr;
968         const safra_state& curr = todo.front().get().first;
969         unsigned src_num = todo.front().get().second;
970         todo.pop_front();
971         succs.set(curr, safra2letters.get(curr));
972         for (auto s = succs.begin(); s != succs.end(); ++s)
973           {
974             // Don't construct sink state as complete does a better job at this
975             if (s->nodes_.empty())
976               continue;
977             unsigned dst_num = get_state(*s);
978             if (s.color_ != -1U)
979               {
980                 res->new_edge(src_num, dst_num, s.cond(), {s.color_});
981                 sets = std::max(s.color_ + 1, sets);
982               }
983             else
984               res->new_edge(src_num, dst_num, s.cond());
985           }
986       }
987     // Green and red colors work in pairs, so the number of parity conditions is
988     // necessarily even.
989     sets += sets & 1;
990     // Acceptance is now min(odd) since we can emit Red on paths 0 with new opti
991     res->set_acceptance(sets, acc_cond::acc_code::parity_min_odd(sets));
992     res->prop_universal(true);
993     res->prop_state_acc(false);
994 
995     cleanup_parity_here(res);
996 
997     if (pretty_print)
998       res->set_named_prop("state-names", print_debug(aut, seen));
999     return res;
1000   }
1001 }
1002