This helps working around missing C functions like strcasecmp that do not exist everywhere (e.g. on Cygwin), and for which lib/ supplies a replacement. Unfortunately we do not have such build in our current continuous integration suite, so we cannot easily detect files where such config.h inclusion would be useful. Therefore this patch simply makes it mandatory to include config.h in *.cc files. Including this in public *.hh file is currently forbidden. * spot/gen/automata.cc, spot/gen/formulas.cc, spot/kripke/fairkripke.cc, spot/kripke/kripke.cc, spot/ltsmin/ltsmin.cc, spot/misc/game.cc, spot/parseaut/fmterror.cc, spot/parsetl/fmterror.cc, spot/parsetl/parsetl.yy, spot/priv/bddalloc.cc, spot/priv/freelist.cc, spot/priv/satcommon.cc, spot/priv/trim.cc, spot/priv/weight.cc, spot/ta/ta.cc, spot/ta/taexplicit.cc, spot/ta/taproduct.cc, spot/ta/tgtaexplicit.cc, spot/ta/tgtaproduct.cc, spot/taalgos/dot.cc, spot/taalgos/emptinessta.cc, spot/taalgos/minimize.cc, spot/taalgos/reachiter.cc, spot/taalgos/statessetbuilder.cc, spot/taalgos/stats.cc, spot/taalgos/tgba2ta.cc, spot/tl/apcollect.cc, spot/tl/contain.cc, spot/tl/declenv.cc, spot/tl/defaultenv.cc, spot/tl/dot.cc, spot/tl/exclusive.cc, spot/tl/hierarchy.cc, spot/tl/length.cc, spot/tl/ltlf.cc, spot/tl/mark.cc, spot/tl/mutation.cc, spot/tl/nenoform.cc, spot/tl/print.cc, spot/tl/randomltl.cc, spot/tl/relabel.cc, spot/tl/remove_x.cc, spot/tl/simplify.cc, spot/tl/snf.cc, spot/tl/unabbrev.cc, spot/twa/acc.cc, spot/twa/bdddict.cc, spot/twa/bddprint.cc, spot/twa/formula2bdd.cc, spot/twa/taatgba.cc, spot/twa/twa.cc, spot/twa/twagraph.cc, spot/twa/twaproduct.cc, spot/twaalgos/aiger.cc, spot/twaalgos/alternation.cc, spot/twaalgos/are_isomorphic.cc, spot/twaalgos/bfssteps.cc, spot/twaalgos/canonicalize.cc, spot/twaalgos/cleanacc.cc, spot/twaalgos/cobuchi.cc, spot/twaalgos/complement.cc, spot/twaalgos/complete.cc, spot/twaalgos/compsusp.cc, spot/twaalgos/couvreurnew.cc, spot/twaalgos/cycles.cc, spot/twaalgos/degen.cc, spot/twaalgos/determinize.cc, spot/twaalgos/dot.cc, spot/twaalgos/dtbasat.cc, spot/twaalgos/dtwasat.cc, spot/twaalgos/dualize.cc, spot/twaalgos/emptiness.cc, spot/twaalgos/gtec/ce.cc, spot/twaalgos/gtec/gtec.cc, spot/twaalgos/gtec/sccstack.cc, spot/twaalgos/gtec/status.cc, spot/twaalgos/gv04.cc, spot/twaalgos/hoa.cc, spot/twaalgos/iscolored.cc, spot/twaalgos/isdet.cc, spot/twaalgos/isunamb.cc, spot/twaalgos/isweakscc.cc, spot/twaalgos/langmap.cc, spot/twaalgos/lbtt.cc, spot/twaalgos/ltl2taa.cc, spot/twaalgos/ltl2tgba_fm.cc, spot/twaalgos/magic.cc, spot/twaalgos/mask.cc, spot/twaalgos/minimize.cc, spot/twaalgos/neverclaim.cc, spot/twaalgos/parity.cc, spot/twaalgos/postproc.cc, spot/twaalgos/powerset.cc, spot/twaalgos/product.cc, spot/twaalgos/rabin2parity.cc, spot/twaalgos/randomgraph.cc, spot/twaalgos/randomize.cc, spot/twaalgos/reachiter.cc, spot/twaalgos/relabel.cc, spot/twaalgos/remfin.cc, spot/twaalgos/remprop.cc, spot/twaalgos/sbacc.cc, spot/twaalgos/sccfilter.cc, spot/twaalgos/sccinfo.cc, spot/twaalgos/se05.cc, spot/twaalgos/sepsets.cc, spot/twaalgos/simulation.cc, spot/twaalgos/split.cc, spot/twaalgos/stats.cc, spot/twaalgos/strength.cc, spot/twaalgos/stripacc.cc, spot/twaalgos/stutter.cc, spot/twaalgos/sum.cc, spot/twaalgos/tau03.cc, spot/twaalgos/tau03opt.cc, spot/twaalgos/totgba.cc, spot/twaalgos/toweak.cc, spot/twaalgos/translate.cc, spot/twaalgos/word.cc, tests/core/acc.cc, tests/core/bitvect.cc, tests/core/checkpsl.cc, tests/core/checkta.cc, tests/core/consterm.cc, tests/core/emptchk.cc, tests/core/equalsf.cc, tests/core/graph.cc, tests/core/ikwiad.cc, tests/core/intvcmp2.cc, tests/core/intvcomp.cc, tests/core/kind.cc, tests/core/kripkecat.cc, tests/core/length.cc, tests/core/ltlrel.cc, tests/core/ngraph.cc, tests/core/parity.cc, tests/core/randtgba.cc, tests/core/readltl.cc, tests/core/reduc.cc, tests/core/safra.cc, tests/core/sccif.cc, tests/core/syntimpl.cc, tests/core/taatgba.cc, tests/core/tostring.cc, tests/core/trival.cc, tests/core/twagraph.cc, tests/ltsmin/modelcheck.cc, spot/parseaut/scanaut.ll, spot/parsetl/scantl.ll: Include config.h. * spot/gen/Makefile.am, spot/graph/Makefile.am, spot/kripke/Makefile.am, spot/ltsmin/Makefile.am, spot/parseaut/Makefile.am, spot/parsetl/Makefile.am, spot/priv/Makefile.am, spot/ta/Makefile.am, spot/taalgos/Makefile.am, spot/tl/Makefile.am, spot/twa/Makefile.am, spot/twaalgos/Makefile.am, spot/twaalgos/gtec/Makefile.am, tests/Makefile.am: Add the -I lib/ flags. * tests/sanity/includes.test: Catch missing config.h in *.cc, and diagnose config.h in *.hh. * tests/sanity/style.test: Better diagnostics.
630 lines
21 KiB
C++
630 lines
21 KiB
C++
// -*- coding: utf-8 -*-
|
|
// Copyright (C) 2014-2018 Laboratoire de Recherche et Développement
|
|
// de l'Epita (LRDE).
|
|
//
|
|
// This file is part of Spot, a model checking library.
|
|
//
|
|
// Spot is free software; you can redistribute it and/or modify it
|
|
// under the terms of the GNU General Public License as published by
|
|
// the Free Software Foundation; either version 3 of the License, or
|
|
// (at your option) any later version.
|
|
//
|
|
// Spot is distributed in the hope that it will be useful, but WITHOUT
|
|
// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
|
// or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public
|
|
// License for more details.
|
|
//
|
|
// You should have received a copy of the GNU General Public License
|
|
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
|
|
#include "config.h"
|
|
#include <spot/twaalgos/sccinfo.hh>
|
|
#include <stack>
|
|
#include <algorithm>
|
|
#include <queue>
|
|
#include <spot/twa/bddprint.hh>
|
|
#include <spot/twaalgos/mask.hh>
|
|
#include <spot/misc/escape.hh>
|
|
|
|
|
|
namespace spot
|
|
{
|
|
void scc_info::report_need_track_states()
|
|
{
|
|
throw std::runtime_error
|
|
("scc_info was not run with option TRACK_STATES");
|
|
}
|
|
|
|
void scc_info::report_need_track_succs()
|
|
{
|
|
throw std::runtime_error
|
|
("scc_info was not run with option TRACK_SUCCS");
|
|
}
|
|
|
|
void scc_info::report_incompatible_stop_on_acc()
|
|
{
|
|
throw std::runtime_error
|
|
("scc_info was run with option STOP_ON_ACC");
|
|
}
|
|
|
|
namespace
|
|
{
|
|
struct scc
|
|
{
|
|
public:
|
|
scc(int index, acc_cond::mark_t in_acc):
|
|
in_acc(in_acc), index(index)
|
|
{
|
|
}
|
|
|
|
acc_cond::mark_t in_acc; // Acceptance sets on the incoming transition
|
|
acc_cond::mark_t acc = 0U; // union of all acceptance marks in the SCC
|
|
acc_cond::mark_t common = -1U; // intersection of all marks in the SCC
|
|
int index; // Index of the SCC
|
|
bool trivial = true; // Whether the SCC has no cycle
|
|
bool accepting = false; // Necessarily accepting
|
|
};
|
|
}
|
|
|
|
scc_info::scc_info(const_twa_graph_ptr aut,
|
|
unsigned initial_state,
|
|
edge_filter filter,
|
|
void* filter_data,
|
|
scc_info_options options)
|
|
: aut_(aut), initial_state_(initial_state),
|
|
filter_(filter), filter_data_(filter_data),
|
|
options_(options)
|
|
{
|
|
unsigned n = aut->num_states();
|
|
|
|
if (initial_state != -1U && n <= initial_state)
|
|
throw std::runtime_error
|
|
("scc_info: supplied initial state does not exist");
|
|
|
|
sccof_.resize(n, -1U);
|
|
|
|
if (!!(options & scc_info_options::TRACK_STATES_IF_FIN_USED)
|
|
&& aut->acc().uses_fin_acceptance())
|
|
options_ = options = options | scc_info_options::TRACK_STATES;
|
|
|
|
std::vector<unsigned> live;
|
|
live.reserve(n);
|
|
std::deque<scc> root_; // Stack of SCC roots.
|
|
std::vector<int> h_(n, 0);
|
|
// Map of visited states. Values > 0 designate maximal SCC.
|
|
// Values < 0 number states that are part of incomplete SCCs being
|
|
// completed. 0 denotes non-visited states.
|
|
|
|
int num_ = 0; // Number of visited nodes, negated.
|
|
|
|
struct stack_item {
|
|
unsigned src;
|
|
unsigned out_edge;
|
|
unsigned univ_pos;
|
|
};
|
|
// DFS stack. Holds (STATE, TRANS, UNIV_POS) pairs where TRANS is
|
|
// the current outgoing transition of STATE, and UNIV_POS is used
|
|
// when the transition is universal to iterate over all possible
|
|
// destinations.
|
|
std::stack<stack_item> todo_;
|
|
auto& gr = aut->get_graph();
|
|
|
|
std::deque<unsigned> init_states;
|
|
std::vector<bool> init_seen(n, false);
|
|
auto push_init = [&](unsigned s)
|
|
{
|
|
if (h_[s] != 0 || init_seen[s])
|
|
return;
|
|
init_seen[s] = true;
|
|
init_states.push_back(s);
|
|
};
|
|
|
|
bool track_states = !!(options & scc_info_options::TRACK_STATES);
|
|
bool track_succs = !!(options & scc_info_options::TRACK_SUCCS);
|
|
auto backtrack = [&](unsigned curr)
|
|
{
|
|
if (root_.back().index == h_[curr])
|
|
{
|
|
unsigned num = node_.size();
|
|
acc_cond::mark_t acc = root_.back().acc;
|
|
acc_cond::mark_t common = root_.back().common;
|
|
bool triv = root_.back().trivial;
|
|
node_.emplace_back(acc, common, triv);
|
|
|
|
auto& succ = node_.back().succ_;
|
|
unsigned np1 = num + 1;
|
|
auto s = live.rbegin();
|
|
do
|
|
{
|
|
sccof_[*s] = num;
|
|
h_[*s] = np1;
|
|
|
|
// Gather all successor SCCs
|
|
if (track_succs)
|
|
for (auto& t: aut->out(*s))
|
|
for (unsigned d: aut->univ_dests(t))
|
|
{
|
|
unsigned n = sccof_[d];
|
|
if (n == num || n == -1U)
|
|
continue;
|
|
// If edges are cut, we are not able to
|
|
// maintain proper successor information.
|
|
if (filter_)
|
|
switch (filter_(t, d, filter_data_))
|
|
{
|
|
case edge_filter_choice::keep:
|
|
break;
|
|
case edge_filter_choice::ignore:
|
|
case edge_filter_choice::cut:
|
|
continue;
|
|
}
|
|
succ.emplace_back(n);
|
|
}
|
|
}
|
|
while (*s++ != curr);
|
|
|
|
if (track_states)
|
|
{
|
|
auto& nbs = node_.back().states_;
|
|
nbs.insert(nbs.end(), s.base(), live.end());
|
|
}
|
|
|
|
node_.back().one_state_ = curr;
|
|
live.erase(s.base(), live.end());
|
|
|
|
if (track_succs)
|
|
{
|
|
std::sort(succ.begin(), succ.end());
|
|
succ.erase(std::unique(succ.begin(), succ.end()), succ.end());
|
|
}
|
|
|
|
bool accept = !triv && root_.back().accepting;
|
|
node_.back().accepting_ = accept;
|
|
if (accept)
|
|
one_acc_scc_ = num;
|
|
bool reject = triv ||
|
|
aut->acc().maybe_accepting(acc, common).is_false();
|
|
node_.back().rejecting_ = reject;
|
|
root_.pop_back();
|
|
}
|
|
};
|
|
|
|
// Setup depth-first search from the initial state. But we may
|
|
// have a conjunction of initial state in alternating automata.
|
|
if (initial_state_ == -1U)
|
|
initial_state_ = aut->get_init_state_number();
|
|
for (unsigned init: aut->univ_dests(initial_state_))
|
|
push_init(init);
|
|
|
|
while (!init_states.empty())
|
|
{
|
|
unsigned init = init_states.front();
|
|
init_states.pop_front();
|
|
int spi = h_[init];
|
|
if (spi > 0)
|
|
continue;
|
|
assert(spi == 0);
|
|
h_[init] = --num_;
|
|
root_.emplace_back(num_, 0U);
|
|
todo_.emplace(stack_item{init, gr.state_storage(init).succ, 0});
|
|
live.emplace_back(init);
|
|
|
|
while (!todo_.empty())
|
|
{
|
|
// We are looking at the next successor in SUCC.
|
|
unsigned tr_succ = todo_.top().out_edge;
|
|
|
|
// If there is no more successor, backtrack.
|
|
if (!tr_succ)
|
|
{
|
|
// We have explored all successors of state CURR.
|
|
unsigned curr = todo_.top().src;
|
|
|
|
// Backtrack TODO_.
|
|
todo_.pop();
|
|
|
|
// When backtracking the root of an SCC, we must also
|
|
// remove that SCC from the ARC/ROOT stacks. We must
|
|
// discard from H all reachable states from this SCC.
|
|
assert(!root_.empty());
|
|
backtrack(curr);
|
|
continue;
|
|
}
|
|
|
|
// We have a successor to look at.
|
|
// Fetch the values we are interested in...
|
|
auto& e = gr.edge_storage(tr_succ);
|
|
|
|
unsigned dest = e.dst;
|
|
if ((int) dest < 0)
|
|
{
|
|
// Iterate over all destinations of a universal edge.
|
|
if (todo_.top().univ_pos == 0)
|
|
todo_.top().univ_pos = ~dest + 1;
|
|
const auto& v = gr.dests_vector();
|
|
dest = v[todo_.top().univ_pos];
|
|
// Last universal destination?
|
|
if (~e.dst + v[~e.dst] == todo_.top().univ_pos)
|
|
{
|
|
todo_.top().out_edge = e.next_succ;
|
|
todo_.top().univ_pos = 0;
|
|
}
|
|
else
|
|
{
|
|
++todo_.top().univ_pos;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
todo_.top().out_edge = e.next_succ;
|
|
}
|
|
|
|
// Do we really want to look at this
|
|
if (filter_)
|
|
switch (filter_(e, dest, filter_data_))
|
|
{
|
|
case edge_filter_choice::keep:
|
|
break;
|
|
case edge_filter_choice::ignore:
|
|
continue;
|
|
case edge_filter_choice::cut:
|
|
push_init(e.dst);
|
|
continue;
|
|
}
|
|
|
|
acc_cond::mark_t acc = e.acc;
|
|
|
|
// Are we going to a new state?
|
|
int spi = h_[dest];
|
|
if (spi == 0)
|
|
{
|
|
// Yes. Number it, stack it, and register its successors
|
|
// for later processing.
|
|
h_[dest] = --num_;
|
|
root_.emplace_back(num_, acc);
|
|
todo_.emplace(stack_item{dest, gr.state_storage(dest).succ, 0});
|
|
live.emplace_back(dest);
|
|
continue;
|
|
}
|
|
|
|
// We already know the state.
|
|
|
|
// Have we reached a maximal SCC?
|
|
if (spi > 0)
|
|
continue;
|
|
|
|
// Now this is the most interesting case. We have reached a
|
|
// state S1 which is already part of a non-dead SCC. Any such
|
|
// non-dead SCC has necessarily been crossed by our path to
|
|
// this state: there is a state S2 in our path which belongs
|
|
// to this SCC too. We are going to merge all states between
|
|
// this S1 and S2 into this SCC..
|
|
//
|
|
// This merge is easy to do because the order of the SCC in
|
|
// ROOT is descending: we just have to merge all SCCs from the
|
|
// top of ROOT that have an index lesser than the one of
|
|
// the SCC of S2 (called the "threshold").
|
|
int threshold = spi;
|
|
bool is_accepting = false;
|
|
// If this is a self-loop, check its acceptance alone.
|
|
if (dest == e.src)
|
|
is_accepting = aut->acc().accepting(acc);
|
|
|
|
acc_cond::mark_t common = acc;
|
|
assert(!root_.empty());
|
|
while (threshold > root_.back().index)
|
|
{
|
|
acc |= root_.back().acc;
|
|
acc_cond::mark_t in_acc = root_.back().in_acc;
|
|
acc |= in_acc;
|
|
common &= root_.back().common;
|
|
common &= in_acc;
|
|
is_accepting |= root_.back().accepting;
|
|
root_.pop_back();
|
|
assert(!root_.empty());
|
|
}
|
|
|
|
// Note that we do not always have
|
|
// threshold == root_.back().index
|
|
// after this loop, the SCC whose index is threshold might have
|
|
// been merged with a higher SCC.
|
|
|
|
root_.back().acc |= acc;
|
|
root_.back().common &= common;
|
|
root_.back().accepting |= is_accepting
|
|
|| aut->acc().accepting(root_.back().acc);
|
|
// This SCC is no longer trivial.
|
|
root_.back().trivial = false;
|
|
|
|
if (root_.back().accepting
|
|
&& !!(options & scc_info_options::STOP_ON_ACC))
|
|
{
|
|
while (!todo_.empty())
|
|
{
|
|
unsigned curr = todo_.top().src;
|
|
todo_.pop();
|
|
backtrack(curr);
|
|
}
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
if (track_succs && !(options & scc_info_options::STOP_ON_ACC))
|
|
determine_usefulness();
|
|
}
|
|
|
|
void scc_info::determine_usefulness()
|
|
{
|
|
// An SCC is useful if it is not rejecting or it has a successor
|
|
// SCC that is useful.
|
|
unsigned scccount = scc_count();
|
|
for (unsigned i = 0; i < scccount; ++i)
|
|
{
|
|
if (!node_[i].is_rejecting())
|
|
{
|
|
node_[i].useful_ = true;
|
|
continue;
|
|
}
|
|
node_[i].useful_ = false;
|
|
for (unsigned j: node_[i].succ())
|
|
if (node_[j].is_useful())
|
|
{
|
|
node_[i].useful_ = true;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
std::set<acc_cond::mark_t> scc_info::marks_of(unsigned scc) const
|
|
{
|
|
std::set<acc_cond::mark_t> res;
|
|
for (auto& t: inner_edges_of(scc))
|
|
res.insert(t.acc);
|
|
return res;
|
|
}
|
|
|
|
std::vector<std::set<acc_cond::mark_t>> scc_info::marks() const
|
|
{
|
|
unsigned n = aut_->num_states();
|
|
std::vector<std::set<acc_cond::mark_t>> result(scc_count());
|
|
|
|
for (unsigned src = 0; src < n; ++src)
|
|
{
|
|
unsigned src_scc = scc_of(src);
|
|
if (src_scc == -1U || is_rejecting_scc(src_scc))
|
|
continue;
|
|
auto& s = result[src_scc];
|
|
for (auto& t: aut_->out(src))
|
|
{
|
|
if (scc_of(t.dst) != src_scc)
|
|
continue;
|
|
s.insert(t.acc);
|
|
}
|
|
}
|
|
return result;
|
|
}
|
|
|
|
std::vector<bool> scc_info::weak_sccs() const
|
|
{
|
|
unsigned n = scc_count();
|
|
std::vector<bool> result(scc_count());
|
|
auto acc = marks();
|
|
for (unsigned s = 0; s < n; ++s)
|
|
result[s] = is_rejecting_scc(s) || acc[s].size() == 1;
|
|
return result;
|
|
}
|
|
|
|
bdd scc_info::scc_ap_support(unsigned scc) const
|
|
{
|
|
bdd support = bddtrue;
|
|
for (auto& t: edges_of(scc))
|
|
support &= bdd_support(t.cond);
|
|
return support;
|
|
}
|
|
|
|
void scc_info::determine_unknown_acceptance()
|
|
{
|
|
std::vector<bool> k;
|
|
unsigned s = scc_count();
|
|
bool changed = false;
|
|
// iterate over SCCs in topological order
|
|
do
|
|
{
|
|
--s;
|
|
if (!is_rejecting_scc(s) && !is_accepting_scc(s))
|
|
{
|
|
if (SPOT_UNLIKELY(!aut_->is_existential()))
|
|
throw std::runtime_error(
|
|
"scc_info::determine_unknown_acceptance() "
|
|
"does not support alternating automata");
|
|
if (SPOT_UNLIKELY(!(options_ & scc_info_options::TRACK_STATES)))
|
|
report_need_track_states();
|
|
auto& node = node_[s];
|
|
if (k.empty())
|
|
k.resize(aut_->num_states());
|
|
for (auto i: node.states_)
|
|
k[i] = true;
|
|
if (mask_keep_accessible_states(aut_, k, node.one_state_)
|
|
->is_empty())
|
|
node.rejecting_ = true;
|
|
else
|
|
node.accepting_ = true;
|
|
changed = true;
|
|
}
|
|
}
|
|
while (s);
|
|
if (changed && !!(options_ & scc_info_options::TRACK_SUCCS))
|
|
determine_usefulness();
|
|
}
|
|
|
|
std::ostream&
|
|
dump_scc_info_dot(std::ostream& out,
|
|
const_twa_graph_ptr aut, scc_info* sccinfo)
|
|
{
|
|
scc_info* m = sccinfo ? sccinfo : new scc_info(aut);
|
|
|
|
out << "digraph G {\n i [label=\"\", style=invis, height=0]\n";
|
|
int start = m->scc_of(aut->get_init_state_number());
|
|
out << " i -> " << start << std::endl;
|
|
|
|
std::vector<bool> seen(m->scc_count());
|
|
seen[start] = true;
|
|
|
|
std::queue<int> q;
|
|
q.push(start);
|
|
while (!q.empty())
|
|
{
|
|
int state = q.front();
|
|
q.pop();
|
|
|
|
out << " " << state << " [shape=box,"
|
|
<< (aut->acc().accepting(m->acc_sets_of(state)) ?
|
|
"style=bold," : "")
|
|
<< "label=\"" << state;
|
|
{
|
|
size_t n = m->states_of(state).size();
|
|
out << " (" << n << " state";
|
|
if (n > 1)
|
|
out << 's';
|
|
out << ')';
|
|
}
|
|
out << "\"]\n";
|
|
|
|
for (unsigned dest: m->succ(state))
|
|
{
|
|
out << " " << state << " -> " << dest << '\n';
|
|
if (seen[dest])
|
|
continue;
|
|
seen[dest] = true;
|
|
q.push(dest);
|
|
}
|
|
}
|
|
|
|
out << "}\n";
|
|
if (!sccinfo)
|
|
delete m;
|
|
return out;
|
|
}
|
|
|
|
std::vector<twa_graph_ptr>
|
|
scc_info::split_on_sets(unsigned scc, acc_cond::mark_t sets,
|
|
bool preserve_names) const
|
|
{
|
|
if (SPOT_UNLIKELY(!(options_ & scc_info_options::TRACK_STATES)))
|
|
report_need_track_states();
|
|
std::vector<twa_graph_ptr> res;
|
|
|
|
std::vector<bool> seen(aut_->num_states(), false);
|
|
std::vector<bool> cur(aut_->num_states(), false);
|
|
|
|
for (unsigned init: states_of(scc))
|
|
{
|
|
if (seen[init])
|
|
continue;
|
|
cur.assign(aut_->num_states(), false);
|
|
|
|
auto copy = make_twa_graph(aut_->get_dict());
|
|
copy->copy_acceptance_of(aut_);
|
|
copy->prop_state_acc(aut_->prop_state_acc());
|
|
transform_accessible(aut_, copy, [&](unsigned src,
|
|
bdd& cond,
|
|
acc_cond::mark_t& m,
|
|
unsigned dst)
|
|
{
|
|
cur[src] = seen[src] = true;
|
|
if (scc_of(dst) != scc
|
|
|| (m & sets)
|
|
|| (seen[dst] && !cur[dst]))
|
|
{
|
|
cond = bddfalse;
|
|
return;
|
|
}
|
|
},
|
|
init);
|
|
if (copy->num_edges())
|
|
{
|
|
if (preserve_names)
|
|
copy->copy_state_names_from(aut_);
|
|
res.push_back(copy);
|
|
}
|
|
}
|
|
return res;
|
|
}
|
|
|
|
void
|
|
scc_info::states_on_acc_cycle_of_rec(unsigned scc,
|
|
acc_cond::mark_t all_fin,
|
|
acc_cond::mark_t all_inf,
|
|
unsigned nb_pairs,
|
|
std::vector<acc_cond::rs_pair>& pairs,
|
|
std::vector<unsigned>& res,
|
|
std::vector<unsigned>& old) const
|
|
{
|
|
if (is_useful_scc(scc) && !is_rejecting_scc(scc))
|
|
{
|
|
acc_cond::mark_t all_acc = acc_sets_of(scc);
|
|
acc_cond::mark_t fin = all_fin & all_acc;
|
|
acc_cond::mark_t inf = all_inf & all_acc;
|
|
|
|
// Get all Fin acceptance set that appears in the SCC and does not have
|
|
// their corresponding Inf appearing in the SCC.
|
|
acc_cond::mark_t m = 0u;
|
|
if (fin)
|
|
for (unsigned p = 0; p < nb_pairs; ++p)
|
|
if (fin & pairs[p].fin && !(inf & pairs[p].inf))
|
|
m |= pairs[p].fin;
|
|
|
|
if (m)
|
|
for (const twa_graph_ptr& aut : split_on_sets(scc, m))
|
|
{
|
|
auto orig_sts = aut->get_named_prop
|
|
<std::vector<unsigned>>("original-states");
|
|
|
|
// Update mapping of state numbers between the current automaton
|
|
// and the starting one.
|
|
for (unsigned i = 0; i < orig_sts->size(); ++i)
|
|
(*orig_sts)[i] = old[(*orig_sts)[i]];
|
|
|
|
scc_info si_tmp(aut, scc_info_options::TRACK_STATES
|
|
| scc_info_options::TRACK_SUCCS);
|
|
unsigned scccount_tmp = si_tmp.scc_count();
|
|
for (unsigned scc_tmp = 0; scc_tmp < scccount_tmp; ++scc_tmp)
|
|
si_tmp.states_on_acc_cycle_of_rec(scc_tmp, all_fin, all_inf,
|
|
nb_pairs, pairs, res,
|
|
*orig_sts);
|
|
}
|
|
|
|
else // Accepting cycle found.
|
|
for (unsigned s : states_of(scc))
|
|
res.push_back(old[s]);
|
|
}
|
|
}
|
|
|
|
std::vector<unsigned>
|
|
scc_info::states_on_acc_cycle_of(unsigned scc) const
|
|
{
|
|
std::vector<acc_cond::rs_pair> pairs;
|
|
if (!aut_->acc().is_streett_like(pairs))
|
|
throw std::runtime_error("states_on_acc_cycle_of only works with "
|
|
"Streett-like acceptance condition");
|
|
unsigned nb_pairs = pairs.size();
|
|
|
|
std::vector<unsigned> res;
|
|
if (is_useful_scc(scc) && !is_rejecting_scc(scc))
|
|
{
|
|
std::vector<unsigned> old;
|
|
unsigned nb_states = aut_->num_states();
|
|
for (unsigned i = 0; i < nb_states; ++i)
|
|
old.push_back(i);
|
|
|
|
acc_cond::mark_t all_fin = 0U;
|
|
acc_cond::mark_t all_inf = 0U;
|
|
std::tie(all_inf, all_fin) = aut_->get_acceptance().used_inf_fin_sets();
|
|
|
|
states_on_acc_cycle_of_rec(scc, all_fin, all_inf, nb_pairs, pairs, res,
|
|
old);
|
|
}
|
|
|
|
return res;
|
|
}
|
|
}
|