* iface/gspn/gspn.cc, src/ltlvisit/basicreduce.cc,
src/ltlvisit/destroy.cc, src/ltlvisit/dotty.cc,
src/ltlvisit/dump.cc, src/ltlvisit/length.cc,
src/ltlvisit/nenoform.cc, src/ltlvisit/reduce.cc,
src/ltlvisit/syntimpl.cc, src/ltlvisit/tostring.cc,
src/tgba/formula2bdd.cc, src/tgba/tgbabddconcreteproduct.cc,
src/tgba/tgbatba.cc, src/tgbaalgos/dotty.cc,
src/tgbaalgos/dupexp.cc, src/tgbaalgos/lbtt.cc,
src/tgbaalgos/ltl2tgba_lacim.cc, src/tgbaalgos/neverclaim.cc,
src/tgbaalgos/save.cc, src/tgbaalgos/stats.cc,
src/tgbaalgos/gtec/nsheap.cc, src/tgbaalgos/gtec/nsheap.hh:
Declare private classes and helper function in anonymous namespaces.
* HACKING, src/sanity/style.test: Document and check this.
Also check for trailing { after namespace or class.
* src/ltlast/predecl.hh, src/ltlast/visitor.hh,
src/tgba/tgbareduc.hh: Fix trailing {.
This commit is contained in:
parent
5176caf4d2
commit
7d27fd3796
28 changed files with 3128 additions and 3025 deletions
19
ChangeLog
19
ChangeLog
|
|
@ -1,3 +1,22 @@
|
||||||
|
2004-10-18 Alexandre Duret-Lutz <adl@src.lip6.fr>
|
||||||
|
|
||||||
|
* iface/gspn/gspn.cc, src/ltlvisit/basicreduce.cc,
|
||||||
|
src/ltlvisit/destroy.cc, src/ltlvisit/dotty.cc,
|
||||||
|
src/ltlvisit/dump.cc, src/ltlvisit/length.cc,
|
||||||
|
src/ltlvisit/nenoform.cc, src/ltlvisit/reduce.cc,
|
||||||
|
src/ltlvisit/syntimpl.cc, src/ltlvisit/tostring.cc,
|
||||||
|
src/tgba/formula2bdd.cc, src/tgba/tgbabddconcreteproduct.cc,
|
||||||
|
src/tgba/tgbatba.cc, src/tgbaalgos/dotty.cc,
|
||||||
|
src/tgbaalgos/dupexp.cc, src/tgbaalgos/lbtt.cc,
|
||||||
|
src/tgbaalgos/ltl2tgba_lacim.cc, src/tgbaalgos/neverclaim.cc,
|
||||||
|
src/tgbaalgos/save.cc, src/tgbaalgos/stats.cc,
|
||||||
|
src/tgbaalgos/gtec/nsheap.cc, src/tgbaalgos/gtec/nsheap.hh:
|
||||||
|
Declare private classes and helper function in anonymous namespaces.
|
||||||
|
* HACKING, src/sanity/style.test: Document and check this.
|
||||||
|
Also check for trailing { after namespace or class.
|
||||||
|
* src/ltlast/predecl.hh, src/ltlast/visitor.hh,
|
||||||
|
src/tgba/tgbareduc.hh: Fix trailing {.
|
||||||
|
|
||||||
2004-10-15 Alexandre Duret-Lutz <adl@src.lip6.fr>
|
2004-10-15 Alexandre Duret-Lutz <adl@src.lip6.fr>
|
||||||
|
|
||||||
* src/tgbaalgos/gtec/ce.cc: Reinstall change from 2004-09-21.
|
* src/tgbaalgos/gtec/ce.cc: Reinstall change from 2004-09-21.
|
||||||
|
|
|
||||||
6
HACKING
6
HACKING
|
|
@ -249,3 +249,9 @@ Other style recommandations
|
||||||
Prefer <iosfwd> when predeclarations are sufficient, and then
|
Prefer <iosfwd> when predeclarations are sufficient, and then
|
||||||
use for instance use just <ostream> in the corresponding .cc file.
|
use for instance use just <ostream> in the corresponding .cc file.
|
||||||
(A plain <iostream> is needed when using std::cout, std::cerr, etc.)
|
(A plain <iostream> is needed when using std::cout, std::cerr, etc.)
|
||||||
|
|
||||||
|
* Always declare helper functions and other local class definitions
|
||||||
|
(used in a single .cc files) in anonymous namespaces. (The risk
|
||||||
|
otherwise is to declare two classes with the same name: the linker
|
||||||
|
will ignore one of the two silently. The resulting bugs are often
|
||||||
|
difficult to understand.)
|
||||||
|
|
|
||||||
|
|
@ -27,439 +27,443 @@
|
||||||
namespace spot
|
namespace spot
|
||||||
{
|
{
|
||||||
|
|
||||||
// state_gspn
|
namespace
|
||||||
//////////////////////////////////////////////////////////////////////
|
|
||||||
|
|
||||||
class state_gspn: public state
|
|
||||||
{
|
{
|
||||||
public:
|
// state_gspn
|
||||||
state_gspn(State s)
|
//////////////////////////////////////////////////////////////////////
|
||||||
: state_(s)
|
|
||||||
|
class state_gspn: public state
|
||||||
{
|
{
|
||||||
|
public:
|
||||||
|
state_gspn(State s)
|
||||||
|
: state_(s)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual
|
||||||
|
~state_gspn()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual int
|
||||||
|
compare(const state* other) const
|
||||||
|
{
|
||||||
|
const state_gspn* o = dynamic_cast<const state_gspn*>(other);
|
||||||
|
assert(o);
|
||||||
|
return reinterpret_cast<char*>(o->get_state())
|
||||||
|
- reinterpret_cast<char*>(get_state());
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual size_t
|
||||||
|
hash() const
|
||||||
|
{
|
||||||
|
return reinterpret_cast<char*>(get_state()) - static_cast<char*>(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual state_gspn* clone() const
|
||||||
|
{
|
||||||
|
return new state_gspn(get_state());
|
||||||
|
}
|
||||||
|
|
||||||
|
State
|
||||||
|
get_state() const
|
||||||
|
{
|
||||||
|
return state_;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
State state_;
|
||||||
|
}; // state_gspn
|
||||||
|
|
||||||
|
|
||||||
|
// tgba_gspn_private_
|
||||||
|
//////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
struct tgba_gspn_private_
|
||||||
|
{
|
||||||
|
int refs; // reference count
|
||||||
|
|
||||||
|
bdd_dict* dict;
|
||||||
|
typedef std::pair<AtomicPropKind, bdd> ab_pair;
|
||||||
|
typedef std::map<AtomicProp, ab_pair> prop_map;
|
||||||
|
prop_map prop_dict;
|
||||||
|
AtomicProp *all_indexes;
|
||||||
|
size_t index_count;
|
||||||
|
const state_gspn* last_state_conds_input;
|
||||||
|
bdd last_state_conds_output;
|
||||||
|
bdd alive_prop;
|
||||||
|
bdd dead_prop;
|
||||||
|
|
||||||
|
tgba_gspn_private_(bdd_dict* dict, ltl::declarative_environment& env,
|
||||||
|
const std::string& dead)
|
||||||
|
: refs(1), dict(dict), all_indexes(0), last_state_conds_input(0)
|
||||||
|
{
|
||||||
|
const ltl::declarative_environment::prop_map& p = env.get_prop_map();
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
for (ltl::declarative_environment::prop_map::const_iterator i
|
||||||
|
= p.begin(); i != p.end(); ++i)
|
||||||
|
{
|
||||||
|
// Skip the DEAD proposition, GreatSPN knows nothing
|
||||||
|
// about it.
|
||||||
|
if (i->first == dead)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
int var = dict->register_proposition(i->second, this);
|
||||||
|
AtomicProp index;
|
||||||
|
int err = prop_index(i->first.c_str(), &index);
|
||||||
|
if (err)
|
||||||
|
throw gspn_exeption("prop_index(" + i->first + ")", err);
|
||||||
|
AtomicPropKind kind;
|
||||||
|
err = prop_kind(index, &kind);
|
||||||
|
if (err)
|
||||||
|
throw gspn_exeption("prop_kind(" + i->first + ")", err);
|
||||||
|
|
||||||
|
prop_dict[index] = ab_pair(kind, bdd_ithvar(var));
|
||||||
|
}
|
||||||
|
|
||||||
|
index_count = prop_dict.size();
|
||||||
|
all_indexes = new AtomicProp[index_count];
|
||||||
|
|
||||||
|
unsigned idx = 0;
|
||||||
|
for (prop_map::const_iterator i = prop_dict.begin();
|
||||||
|
i != prop_dict.end(); ++i)
|
||||||
|
all_indexes[idx++] = i->first;
|
||||||
|
}
|
||||||
|
catch (...)
|
||||||
|
{
|
||||||
|
// If an exception occurs during the loop, we need to clean
|
||||||
|
// all BDD variables which have been registered so far.
|
||||||
|
dict->unregister_all_my_variables(this);
|
||||||
|
throw;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Register the "dead" proposition. There are three cases to
|
||||||
|
// consider:
|
||||||
|
// * If DEAD is "false", it means we are not interested in finite
|
||||||
|
// sequences of the system.
|
||||||
|
// * If DEAD is "true", we want to check finite sequences as well
|
||||||
|
// as infinite sequences, but do not need to distinguish them.
|
||||||
|
// * If DEAD is any other string, this is the name a property
|
||||||
|
// that should be true when looping on a dead state, and false
|
||||||
|
// otherwise.
|
||||||
|
// We handle these three cases by setting ALIVE_PROP and DEAD_PROP
|
||||||
|
// appropriately. ALIVE_PROP is the bdd that should be ANDed
|
||||||
|
// to all transitions leaving a live state, while DEAD_PROP should
|
||||||
|
// be ANDed to all transitions leaving a dead state.
|
||||||
|
if (!strcasecmp(dead.c_str(), "false"))
|
||||||
|
{
|
||||||
|
alive_prop = bddtrue;
|
||||||
|
dead_prop = bddfalse;
|
||||||
|
}
|
||||||
|
else if (!strcasecmp(dead.c_str(), "true"))
|
||||||
|
{
|
||||||
|
alive_prop = bddtrue;
|
||||||
|
dead_prop = bddtrue;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
ltl::formula* f = env.require(dead);
|
||||||
|
assert(f);
|
||||||
|
int var = dict->register_proposition(f, this);
|
||||||
|
dead_prop = bdd_ithvar(var);
|
||||||
|
alive_prop = bdd_nithvar(var);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
tgba_gspn_private_::~tgba_gspn_private_()
|
||||||
|
{
|
||||||
|
dict->unregister_all_my_variables(this);
|
||||||
|
if (last_state_conds_input)
|
||||||
|
delete last_state_conds_input;
|
||||||
|
if (all_indexes)
|
||||||
|
delete[] all_indexes;
|
||||||
|
}
|
||||||
|
|
||||||
|
bdd index_to_bdd(AtomicProp index) const
|
||||||
|
{
|
||||||
|
if (index == EVENT_TRUE)
|
||||||
|
return bddtrue;
|
||||||
|
prop_map::const_iterator i = prop_dict.find(index);
|
||||||
|
assert(i != prop_dict.end());
|
||||||
|
return i->second.second;
|
||||||
|
}
|
||||||
|
|
||||||
|
bdd state_conds(const state_gspn* s)
|
||||||
|
{
|
||||||
|
// Use cached value if possible.
|
||||||
|
if (!last_state_conds_input ||
|
||||||
|
last_state_conds_input->compare(s) != 0)
|
||||||
|
{
|
||||||
|
// Build the BDD of the conditions available on this state.
|
||||||
|
unsigned char* cube = 0;
|
||||||
|
// FIXME: This is temporary. We ought to ask only what we need.
|
||||||
|
AtomicProp* want = all_indexes;
|
||||||
|
size_t count = index_count;
|
||||||
|
int res = satisfy(s->get_state(), want, &cube, count);
|
||||||
|
if (res)
|
||||||
|
throw gspn_exeption("satisfy()", res);
|
||||||
|
assert(cube);
|
||||||
|
last_state_conds_output = bddtrue;
|
||||||
|
for (size_t i = 0; i < count; ++i)
|
||||||
|
{
|
||||||
|
bdd v = index_to_bdd(want[i]);
|
||||||
|
last_state_conds_output &= cube[i] ? v : !v;
|
||||||
|
}
|
||||||
|
satisfy_free(cube);
|
||||||
|
|
||||||
|
if (last_state_conds_input)
|
||||||
|
delete last_state_conds_input;
|
||||||
|
last_state_conds_input = s->clone();
|
||||||
|
}
|
||||||
|
return last_state_conds_output;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
// tgba_succ_iterator_gspn
|
||||||
|
//////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
class tgba_succ_iterator_gspn: public tgba_succ_iterator
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
tgba_succ_iterator_gspn(bdd state_conds, State state,
|
||||||
|
tgba_gspn_private_* data)
|
||||||
|
: state_conds_(state_conds),
|
||||||
|
successors_(0),
|
||||||
|
size_(0),
|
||||||
|
current_(0),
|
||||||
|
data_(data),
|
||||||
|
from_state_(state)
|
||||||
|
{
|
||||||
|
int res = succ(state, &successors_, &size_);
|
||||||
|
if (res)
|
||||||
|
throw gspn_exeption("succ()", res);
|
||||||
|
// GreatSPN should return successors_ == 0 and size_ == 0 when a
|
||||||
|
// state has no successors.
|
||||||
|
assert((size_ <= 0) ^ (successors_ != 0));
|
||||||
|
// If we have to stutter on a dead state, we have one successor.
|
||||||
|
if (size_ <= 0 && data_->dead_prop != bddfalse)
|
||||||
|
size_ = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual
|
||||||
|
~tgba_succ_iterator_gspn()
|
||||||
|
{
|
||||||
|
if (successors_)
|
||||||
|
succ_free(successors_);
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual void
|
||||||
|
first()
|
||||||
|
{
|
||||||
|
current_ = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual void
|
||||||
|
next()
|
||||||
|
{
|
||||||
|
assert(!done());
|
||||||
|
++current_;
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual bool
|
||||||
|
done() const
|
||||||
|
{
|
||||||
|
return current_ >= size_;
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual state*
|
||||||
|
current_state() const
|
||||||
|
{
|
||||||
|
// If GreatSPN returned no successor, we stutter on the dead state.
|
||||||
|
return
|
||||||
|
new state_gspn(successors_ ? successors_[current_].s : from_state_);
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual bdd
|
||||||
|
current_condition() const
|
||||||
|
{
|
||||||
|
if (successors_)
|
||||||
|
{
|
||||||
|
bdd p = data_->index_to_bdd(successors_[current_].p);
|
||||||
|
return state_conds_ & p & data_->alive_prop;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return state_conds_ & data_->dead_prop;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual bdd
|
||||||
|
current_acceptance_conditions() const
|
||||||
|
{
|
||||||
|
// There is no acceptance conditions in GSPN systems.
|
||||||
|
return bddfalse;
|
||||||
|
}
|
||||||
|
private:
|
||||||
|
bdd state_conds_; /// All conditions known on STATE.
|
||||||
|
EventPropSucc* successors_; /// array of successors
|
||||||
|
size_t size_; /// size of successors_
|
||||||
|
size_t current_; /// current position in successors_
|
||||||
|
tgba_gspn_private_* data_;
|
||||||
|
State from_state_;
|
||||||
|
}; // tgba_succ_iterator_gspn
|
||||||
|
|
||||||
|
|
||||||
|
// tgba_gspn
|
||||||
|
//////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/// Data private to tgba_gspn.
|
||||||
|
struct tgba_gspn_private_;
|
||||||
|
|
||||||
|
class tgba_gspn: public tgba
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
tgba_gspn(bdd_dict* dict, ltl::declarative_environment& env,
|
||||||
|
const std::string& dead);
|
||||||
|
tgba_gspn(const tgba_gspn& other);
|
||||||
|
tgba_gspn& operator=(const tgba_gspn& other);
|
||||||
|
virtual ~tgba_gspn();
|
||||||
|
virtual state* get_init_state() const;
|
||||||
|
virtual tgba_succ_iterator*
|
||||||
|
succ_iter(const state* local_state,
|
||||||
|
const state* global_state = 0,
|
||||||
|
const tgba* global_automaton = 0) const;
|
||||||
|
virtual bdd_dict* get_dict() const;
|
||||||
|
virtual std::string format_state(const state* state) const;
|
||||||
|
virtual bdd all_acceptance_conditions() const;
|
||||||
|
virtual bdd neg_acceptance_conditions() const;
|
||||||
|
protected:
|
||||||
|
virtual bdd compute_support_conditions(const spot::state* state) const;
|
||||||
|
virtual bdd compute_support_variables(const spot::state* state) const;
|
||||||
|
private:
|
||||||
|
tgba_gspn_private_* data_;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
tgba_gspn::tgba_gspn(bdd_dict* dict, ltl::declarative_environment& env,
|
||||||
|
const std::string& dead)
|
||||||
|
{
|
||||||
|
data_ = new tgba_gspn_private_(dict, env, dead);
|
||||||
}
|
}
|
||||||
|
|
||||||
virtual
|
tgba_gspn::tgba_gspn(const tgba_gspn& other)
|
||||||
~state_gspn()
|
: tgba()
|
||||||
{
|
{
|
||||||
|
data_ = other.data_;
|
||||||
|
++data_->refs;
|
||||||
}
|
}
|
||||||
|
|
||||||
virtual int
|
tgba_gspn::~tgba_gspn()
|
||||||
compare(const state* other) const
|
|
||||||
{
|
{
|
||||||
const state_gspn* o = dynamic_cast<const state_gspn*>(other);
|
if (--data_->refs == 0)
|
||||||
assert(o);
|
delete data_;
|
||||||
return reinterpret_cast<char*>(o->get_state())
|
|
||||||
- reinterpret_cast<char*>(get_state());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
virtual size_t
|
tgba_gspn&
|
||||||
hash() const
|
tgba_gspn::operator=(const tgba_gspn& other)
|
||||||
{
|
{
|
||||||
return reinterpret_cast<char*>(get_state()) - static_cast<char*>(0);
|
if (&other == this)
|
||||||
|
return *this;
|
||||||
|
this->~tgba_gspn();
|
||||||
|
new (this) tgba_gspn(other);
|
||||||
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
virtual state_gspn* clone() const
|
state* tgba_gspn::get_init_state() const
|
||||||
{
|
{
|
||||||
return new state_gspn(get_state());
|
State s;
|
||||||
|
int err = initial_state(&s);
|
||||||
|
if (err)
|
||||||
|
throw gspn_exeption("initial_state()", err);
|
||||||
|
return new state_gspn(s);
|
||||||
}
|
}
|
||||||
|
|
||||||
State
|
tgba_succ_iterator*
|
||||||
get_state() const
|
tgba_gspn::succ_iter(const state* state,
|
||||||
|
const state* global_state,
|
||||||
|
const tgba* global_automaton) const
|
||||||
{
|
{
|
||||||
return state_;
|
const state_gspn* s = dynamic_cast<const state_gspn*>(state);
|
||||||
|
assert(s);
|
||||||
|
(void) global_state;
|
||||||
|
(void) global_automaton;
|
||||||
|
// FIXME: Should pass global_automaton->support_variables(state)
|
||||||
|
// to state_conds.
|
||||||
|
bdd state_conds = data_->state_conds(s);
|
||||||
|
return new tgba_succ_iterator_gspn(state_conds, s->get_state(), data_);
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
bdd
|
||||||
State state_;
|
tgba_gspn::compute_support_conditions(const spot::state* state) const
|
||||||
}; // state_gspn
|
|
||||||
|
|
||||||
|
|
||||||
// tgba_gspn_private_
|
|
||||||
//////////////////////////////////////////////////////////////////////
|
|
||||||
|
|
||||||
struct tgba_gspn_private_
|
|
||||||
{
|
|
||||||
int refs; // reference count
|
|
||||||
|
|
||||||
bdd_dict* dict;
|
|
||||||
typedef std::pair<AtomicPropKind, bdd> ab_pair;
|
|
||||||
typedef std::map<AtomicProp, ab_pair> prop_map;
|
|
||||||
prop_map prop_dict;
|
|
||||||
AtomicProp *all_indexes;
|
|
||||||
size_t index_count;
|
|
||||||
const state_gspn* last_state_conds_input;
|
|
||||||
bdd last_state_conds_output;
|
|
||||||
bdd alive_prop;
|
|
||||||
bdd dead_prop;
|
|
||||||
|
|
||||||
tgba_gspn_private_(bdd_dict* dict, ltl::declarative_environment& env,
|
|
||||||
const std::string& dead)
|
|
||||||
: refs(1), dict(dict), all_indexes(0), last_state_conds_input(0)
|
|
||||||
{
|
{
|
||||||
const ltl::declarative_environment::prop_map& p = env.get_prop_map();
|
const state_gspn* s = dynamic_cast<const state_gspn*>(state);
|
||||||
|
assert(s);
|
||||||
try
|
return data_->state_conds(s);
|
||||||
{
|
|
||||||
for (ltl::declarative_environment::prop_map::const_iterator i
|
|
||||||
= p.begin(); i != p.end(); ++i)
|
|
||||||
{
|
|
||||||
// Skip the DEAD proposition, GreatSPN knows nothing
|
|
||||||
// about it.
|
|
||||||
if (i->first == dead)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
int var = dict->register_proposition(i->second, this);
|
|
||||||
AtomicProp index;
|
|
||||||
int err = prop_index(i->first.c_str(), &index);
|
|
||||||
if (err)
|
|
||||||
throw gspn_exeption("prop_index(" + i->first + ")", err);
|
|
||||||
AtomicPropKind kind;
|
|
||||||
err = prop_kind(index, &kind);
|
|
||||||
if (err)
|
|
||||||
throw gspn_exeption("prop_kind(" + i->first + ")", err);
|
|
||||||
|
|
||||||
prop_dict[index] = ab_pair(kind, bdd_ithvar(var));
|
|
||||||
}
|
|
||||||
|
|
||||||
index_count = prop_dict.size();
|
|
||||||
all_indexes = new AtomicProp[index_count];
|
|
||||||
|
|
||||||
unsigned idx = 0;
|
|
||||||
for (prop_map::const_iterator i = prop_dict.begin();
|
|
||||||
i != prop_dict.end(); ++i)
|
|
||||||
all_indexes[idx++] = i->first;
|
|
||||||
}
|
|
||||||
catch (...)
|
|
||||||
{
|
|
||||||
// If an exception occurs during the loop, we need to clean
|
|
||||||
// all BDD variables which have been registered so far.
|
|
||||||
dict->unregister_all_my_variables(this);
|
|
||||||
throw;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Register the "dead" proposition. There are three cases to
|
|
||||||
// consider:
|
|
||||||
// * If DEAD is "false", it means we are not interested in finite
|
|
||||||
// sequences of the system.
|
|
||||||
// * If DEAD is "true", we want to check finite sequences as well
|
|
||||||
// as infinite sequences, but do not need to distinguish them.
|
|
||||||
// * If DEAD is any other string, this is the name a property
|
|
||||||
// that should be true when looping on a dead state, and false
|
|
||||||
// otherwise.
|
|
||||||
// We handle these three cases by setting ALIVE_PROP and DEAD_PROP
|
|
||||||
// appropriately. ALIVE_PROP is the bdd that should be ANDed
|
|
||||||
// to all transitions leaving a live state, while DEAD_PROP should
|
|
||||||
// be ANDed to all transitions leaving a dead state.
|
|
||||||
if (!strcasecmp(dead.c_str(), "false"))
|
|
||||||
{
|
|
||||||
alive_prop = bddtrue;
|
|
||||||
dead_prop = bddfalse;
|
|
||||||
}
|
|
||||||
else if (!strcasecmp(dead.c_str(), "true"))
|
|
||||||
{
|
|
||||||
alive_prop = bddtrue;
|
|
||||||
dead_prop = bddtrue;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
ltl::formula* f = env.require(dead);
|
|
||||||
assert(f);
|
|
||||||
int var = dict->register_proposition(f, this);
|
|
||||||
dead_prop = bdd_ithvar(var);
|
|
||||||
alive_prop = bdd_nithvar(var);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
tgba_gspn_private_::~tgba_gspn_private_()
|
bdd
|
||||||
|
tgba_gspn::compute_support_variables(const spot::state* state) const
|
||||||
{
|
{
|
||||||
dict->unregister_all_my_variables(this);
|
// FIXME: At the time of writing, only tgba_gspn calls
|
||||||
if (last_state_conds_input)
|
// support_variables on the root of a product to gather the
|
||||||
delete last_state_conds_input;
|
// variables used by all other automata and let GPSN compute only
|
||||||
if (all_indexes)
|
// these. Because support_variables() is recursive over the
|
||||||
delete[] all_indexes;
|
// product treee, tgba_gspn::support_variables should not output
|
||||||
|
// all the variables known by GSPN; this would ruin the sole
|
||||||
|
// purpose of this function.
|
||||||
|
// However this works because we assume there is at most one
|
||||||
|
// tgba_gspn automata in a product (a legitimate assumption
|
||||||
|
// since the GSPN API is not re-entrant) and only this automata
|
||||||
|
// need to call support_variables (now _this_ is shady).
|
||||||
|
(void) state;
|
||||||
|
return bddtrue;
|
||||||
}
|
}
|
||||||
|
|
||||||
bdd index_to_bdd(AtomicProp index) const
|
bdd_dict*
|
||||||
|
tgba_gspn::get_dict() const
|
||||||
{
|
{
|
||||||
if (index == EVENT_TRUE)
|
return data_->dict;
|
||||||
return bddtrue;
|
|
||||||
prop_map::const_iterator i = prop_dict.find(index);
|
|
||||||
assert(i != prop_dict.end());
|
|
||||||
return i->second.second;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bdd state_conds(const state_gspn* s)
|
std::string
|
||||||
|
tgba_gspn::format_state(const state* state) const
|
||||||
{
|
{
|
||||||
// Use cached value if possible.
|
const state_gspn* s = dynamic_cast<const state_gspn*>(state);
|
||||||
if (!last_state_conds_input ||
|
assert(s);
|
||||||
last_state_conds_input->compare(s) != 0)
|
char* str;
|
||||||
{
|
int err = print_state(s->get_state(), &str);
|
||||||
// Build the BDD of the conditions available on this state.
|
if (err)
|
||||||
unsigned char* cube = 0;
|
throw gspn_exeption("print_state()", err);
|
||||||
// FIXME: This is temporary. We ought to ask only what we need.
|
// Strip trailing \n...
|
||||||
AtomicProp* want = all_indexes;
|
unsigned len = strlen(str);
|
||||||
size_t count = index_count;
|
while (str[--len] == '\n')
|
||||||
int res = satisfy(s->get_state(), want, &cube, count);
|
str[len] = 0;
|
||||||
if (res)
|
std::string res(str);
|
||||||
throw gspn_exeption("satisfy()", res);
|
free(str);
|
||||||
assert(cube);
|
return res;
|
||||||
last_state_conds_output = bddtrue;
|
|
||||||
for (size_t i = 0; i < count; ++i)
|
|
||||||
{
|
|
||||||
bdd v = index_to_bdd(want[i]);
|
|
||||||
last_state_conds_output &= cube[i] ? v : !v;
|
|
||||||
}
|
|
||||||
satisfy_free(cube);
|
|
||||||
|
|
||||||
if (last_state_conds_input)
|
|
||||||
delete last_state_conds_input;
|
|
||||||
last_state_conds_input = s->clone();
|
|
||||||
}
|
|
||||||
return last_state_conds_output;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
// tgba_succ_iterator_gspn
|
|
||||||
//////////////////////////////////////////////////////////////////////
|
|
||||||
|
|
||||||
class tgba_succ_iterator_gspn: public tgba_succ_iterator
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
tgba_succ_iterator_gspn(bdd state_conds, State state,
|
|
||||||
tgba_gspn_private_* data)
|
|
||||||
: state_conds_(state_conds),
|
|
||||||
successors_(0),
|
|
||||||
size_(0),
|
|
||||||
current_(0),
|
|
||||||
data_(data),
|
|
||||||
from_state_(state)
|
|
||||||
{
|
|
||||||
int res = succ(state, &successors_, &size_);
|
|
||||||
if (res)
|
|
||||||
throw gspn_exeption("succ()", res);
|
|
||||||
// GreatSPN should return successors_ == 0 and size_ == 0 when a
|
|
||||||
// state has no successors.
|
|
||||||
assert((size_ <= 0) ^ (successors_ != 0));
|
|
||||||
// If we have to stutter on a dead state, we have one successor.
|
|
||||||
if (size_ <= 0 && data_->dead_prop != bddfalse)
|
|
||||||
size_ = 1;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
virtual
|
bdd
|
||||||
~tgba_succ_iterator_gspn()
|
tgba_gspn::all_acceptance_conditions() const
|
||||||
{
|
|
||||||
if (successors_)
|
|
||||||
succ_free(successors_);
|
|
||||||
}
|
|
||||||
|
|
||||||
virtual void
|
|
||||||
first()
|
|
||||||
{
|
|
||||||
current_ = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
virtual void
|
|
||||||
next()
|
|
||||||
{
|
|
||||||
assert(!done());
|
|
||||||
++current_;
|
|
||||||
}
|
|
||||||
|
|
||||||
virtual bool
|
|
||||||
done() const
|
|
||||||
{
|
|
||||||
return current_ >= size_;
|
|
||||||
}
|
|
||||||
|
|
||||||
virtual state*
|
|
||||||
current_state() const
|
|
||||||
{
|
|
||||||
// If GreatSPN returned no successor, we stutter on the dead state.
|
|
||||||
return
|
|
||||||
new state_gspn(successors_ ? successors_[current_].s : from_state_);
|
|
||||||
}
|
|
||||||
|
|
||||||
virtual bdd
|
|
||||||
current_condition() const
|
|
||||||
{
|
|
||||||
if (successors_)
|
|
||||||
{
|
|
||||||
bdd p = data_->index_to_bdd(successors_[current_].p);
|
|
||||||
return state_conds_ & p & data_->alive_prop;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
return state_conds_ & data_->dead_prop;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
virtual bdd
|
|
||||||
current_acceptance_conditions() const
|
|
||||||
{
|
{
|
||||||
// There is no acceptance conditions in GSPN systems.
|
// There is no acceptance conditions in GSPN systems.
|
||||||
return bddfalse;
|
return bddfalse;
|
||||||
}
|
}
|
||||||
private:
|
|
||||||
bdd state_conds_; /// All conditions known on STATE.
|
|
||||||
EventPropSucc* successors_; /// array of successors
|
|
||||||
size_t size_; /// size of successors_
|
|
||||||
size_t current_; /// current position in successors_
|
|
||||||
tgba_gspn_private_* data_;
|
|
||||||
State from_state_;
|
|
||||||
}; // tgba_succ_iterator_gspn
|
|
||||||
|
|
||||||
|
bdd
|
||||||
|
tgba_gspn::neg_acceptance_conditions() const
|
||||||
|
{
|
||||||
|
// There is no acceptance conditions in GSPN systems.
|
||||||
|
return bddtrue;
|
||||||
|
}
|
||||||
|
|
||||||
// tgba_gspn
|
} // anonymous
|
||||||
//////////////////////////////////////////////////////////////////////
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/// Data private to tgba_gspn.
|
|
||||||
struct tgba_gspn_private_;
|
|
||||||
|
|
||||||
class tgba_gspn: public tgba
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
tgba_gspn(bdd_dict* dict, ltl::declarative_environment& env,
|
|
||||||
const std::string& dead);
|
|
||||||
tgba_gspn(const tgba_gspn& other);
|
|
||||||
tgba_gspn& operator=(const tgba_gspn& other);
|
|
||||||
virtual ~tgba_gspn();
|
|
||||||
virtual state* get_init_state() const;
|
|
||||||
virtual tgba_succ_iterator*
|
|
||||||
succ_iter(const state* local_state,
|
|
||||||
const state* global_state = 0,
|
|
||||||
const tgba* global_automaton = 0) const;
|
|
||||||
virtual bdd_dict* get_dict() const;
|
|
||||||
virtual std::string format_state(const state* state) const;
|
|
||||||
virtual bdd all_acceptance_conditions() const;
|
|
||||||
virtual bdd neg_acceptance_conditions() const;
|
|
||||||
protected:
|
|
||||||
virtual bdd compute_support_conditions(const spot::state* state) const;
|
|
||||||
virtual bdd compute_support_variables(const spot::state* state) const;
|
|
||||||
private:
|
|
||||||
tgba_gspn_private_* data_;
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
tgba_gspn::tgba_gspn(bdd_dict* dict, ltl::declarative_environment& env,
|
|
||||||
const std::string& dead)
|
|
||||||
{
|
|
||||||
data_ = new tgba_gspn_private_(dict, env, dead);
|
|
||||||
}
|
|
||||||
|
|
||||||
tgba_gspn::tgba_gspn(const tgba_gspn& other)
|
|
||||||
: tgba()
|
|
||||||
{
|
|
||||||
data_ = other.data_;
|
|
||||||
++data_->refs;
|
|
||||||
}
|
|
||||||
|
|
||||||
tgba_gspn::~tgba_gspn()
|
|
||||||
{
|
|
||||||
if (--data_->refs == 0)
|
|
||||||
delete data_;
|
|
||||||
}
|
|
||||||
|
|
||||||
tgba_gspn&
|
|
||||||
tgba_gspn::operator=(const tgba_gspn& other)
|
|
||||||
{
|
|
||||||
if (&other == this)
|
|
||||||
return *this;
|
|
||||||
this->~tgba_gspn();
|
|
||||||
new (this) tgba_gspn(other);
|
|
||||||
return *this;
|
|
||||||
}
|
|
||||||
|
|
||||||
state* tgba_gspn::get_init_state() const
|
|
||||||
{
|
|
||||||
State s;
|
|
||||||
int err = initial_state(&s);
|
|
||||||
if (err)
|
|
||||||
throw gspn_exeption("initial_state()", err);
|
|
||||||
return new state_gspn(s);
|
|
||||||
}
|
|
||||||
|
|
||||||
tgba_succ_iterator*
|
|
||||||
tgba_gspn::succ_iter(const state* state,
|
|
||||||
const state* global_state,
|
|
||||||
const tgba* global_automaton) const
|
|
||||||
{
|
|
||||||
const state_gspn* s = dynamic_cast<const state_gspn*>(state);
|
|
||||||
assert(s);
|
|
||||||
(void) global_state;
|
|
||||||
(void) global_automaton;
|
|
||||||
// FIXME: Should pass global_automaton->support_variables(state)
|
|
||||||
// to state_conds.
|
|
||||||
bdd state_conds = data_->state_conds(s);
|
|
||||||
return new tgba_succ_iterator_gspn(state_conds, s->get_state(), data_);
|
|
||||||
}
|
|
||||||
|
|
||||||
bdd
|
|
||||||
tgba_gspn::compute_support_conditions(const spot::state* state) const
|
|
||||||
{
|
|
||||||
const state_gspn* s = dynamic_cast<const state_gspn*>(state);
|
|
||||||
assert(s);
|
|
||||||
return data_->state_conds(s);
|
|
||||||
}
|
|
||||||
|
|
||||||
bdd
|
|
||||||
tgba_gspn::compute_support_variables(const spot::state* state) const
|
|
||||||
{
|
|
||||||
// FIXME: At the time of writing, only tgba_gspn calls
|
|
||||||
// support_variables on the root of a product to gather the
|
|
||||||
// variables used by all other automata and let GPSN compute only
|
|
||||||
// these. Because support_variables() is recursive over the
|
|
||||||
// product treee, tgba_gspn::support_variables should not output
|
|
||||||
// all the variables known by GSPN; this would ruin the sole
|
|
||||||
// purpose of this function.
|
|
||||||
// However this works because we assume there is at most one
|
|
||||||
// tgba_gspn automata in a product (a legitimate assumption
|
|
||||||
// since the GSPN API is not re-entrant) and only this automata
|
|
||||||
// need to call support_variables (now _this_ is shady).
|
|
||||||
(void) state;
|
|
||||||
return bddtrue;
|
|
||||||
}
|
|
||||||
|
|
||||||
bdd_dict*
|
|
||||||
tgba_gspn::get_dict() const
|
|
||||||
{
|
|
||||||
return data_->dict;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string
|
|
||||||
tgba_gspn::format_state(const state* state) const
|
|
||||||
{
|
|
||||||
const state_gspn* s = dynamic_cast<const state_gspn*>(state);
|
|
||||||
assert(s);
|
|
||||||
char* str;
|
|
||||||
int err = print_state(s->get_state(), &str);
|
|
||||||
if (err)
|
|
||||||
throw gspn_exeption("print_state()", err);
|
|
||||||
// Strip trailing \n...
|
|
||||||
unsigned len = strlen(str);
|
|
||||||
while (str[--len] == '\n')
|
|
||||||
str[len] = 0;
|
|
||||||
std::string res(str);
|
|
||||||
free(str);
|
|
||||||
return res;
|
|
||||||
}
|
|
||||||
|
|
||||||
bdd
|
|
||||||
tgba_gspn::all_acceptance_conditions() const
|
|
||||||
{
|
|
||||||
// There is no acceptance conditions in GSPN systems.
|
|
||||||
return bddfalse;
|
|
||||||
}
|
|
||||||
|
|
||||||
bdd
|
|
||||||
tgba_gspn::neg_acceptance_conditions() const
|
|
||||||
{
|
|
||||||
// There is no acceptance conditions in GSPN systems.
|
|
||||||
return bddtrue;
|
|
||||||
}
|
|
||||||
|
|
||||||
// gspn_interface
|
// gspn_interface
|
||||||
//////////////////////////////////////////////////////////////////////
|
//////////////////////////////////////////////////////////////////////
|
||||||
|
|
|
||||||
|
|
@ -29,8 +29,10 @@
|
||||||
#ifndef SPOT_LTLAST_PREDECL_HH
|
#ifndef SPOT_LTLAST_PREDECL_HH
|
||||||
# define SPOT_LTLAST_PREDECL_HH
|
# define SPOT_LTLAST_PREDECL_HH
|
||||||
|
|
||||||
namespace spot {
|
namespace spot
|
||||||
namespace ltl {
|
{
|
||||||
|
namespace ltl
|
||||||
|
{
|
||||||
struct visitor;
|
struct visitor;
|
||||||
struct const_visitor;
|
struct const_visitor;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -26,9 +26,10 @@
|
||||||
|
|
||||||
#include "predecl.hh"
|
#include "predecl.hh"
|
||||||
|
|
||||||
namespace spot {
|
namespace spot
|
||||||
namespace ltl {
|
{
|
||||||
|
namespace ltl
|
||||||
|
{
|
||||||
/// \brief Formula visitor that can modify the formula.
|
/// \brief Formula visitor that can modify the formula.
|
||||||
///
|
///
|
||||||
/// Writing visitors is the prefered way
|
/// Writing visitors is the prefered way
|
||||||
|
|
|
||||||
File diff suppressed because it is too large
Load diff
|
|
@ -1,4 +1,4 @@
|
||||||
// Copyright (C) 2003 Laboratoire d'Informatique de Paris 6 (LIP6),
|
// Copyright (C) 2003, 2004 Laboratoire d'Informatique de Paris 6 (LIP6),
|
||||||
// département Systèmes Répartis Coopératifs (SRC), Université Pierre
|
// département Systèmes Répartis Coopératifs (SRC), Université Pierre
|
||||||
// et Marie Curie.
|
// et Marie Curie.
|
||||||
//
|
//
|
||||||
|
|
@ -25,16 +25,18 @@ namespace spot
|
||||||
{
|
{
|
||||||
namespace ltl
|
namespace ltl
|
||||||
{
|
{
|
||||||
|
namespace
|
||||||
class destroy_visitor : public postfix_visitor
|
|
||||||
{
|
{
|
||||||
public:
|
class destroy_visitor: public postfix_visitor
|
||||||
virtual void
|
|
||||||
doit_default(formula* c)
|
|
||||||
{
|
{
|
||||||
formula::unref(c);
|
public:
|
||||||
}
|
virtual void
|
||||||
};
|
doit_default(formula* c)
|
||||||
|
{
|
||||||
|
formula::unref(c);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
destroy(const formula* f)
|
destroy(const formula* f)
|
||||||
|
|
|
||||||
|
|
@ -29,99 +29,101 @@ namespace spot
|
||||||
{
|
{
|
||||||
namespace ltl
|
namespace ltl
|
||||||
{
|
{
|
||||||
|
namespace
|
||||||
class dotty_visitor : public const_visitor
|
|
||||||
{
|
{
|
||||||
public:
|
class dotty_visitor: public const_visitor
|
||||||
typedef Sgi::hash_map<const formula*, int, ptr_hash<formula> > map;
|
|
||||||
dotty_visitor(std::ostream& os, map& m)
|
|
||||||
: os_(os), father_(-1), node_(m)
|
|
||||||
{
|
{
|
||||||
}
|
public:
|
||||||
|
typedef Sgi::hash_map<const formula*, int, ptr_hash<formula> > map;
|
||||||
|
dotty_visitor(std::ostream& os, map& m)
|
||||||
|
: os_(os), father_(-1), node_(m)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
virtual
|
virtual
|
||||||
~dotty_visitor()
|
~dotty_visitor()
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
visit(const atomic_prop* ap)
|
visit(const atomic_prop* ap)
|
||||||
{
|
{
|
||||||
draw_node_(ap, ap->name(), true);
|
draw_node_(ap, ap->name(), true);
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
visit(const constant* c)
|
visit(const constant* c)
|
||||||
{
|
{
|
||||||
draw_node_(c, c->val_name(), true);
|
draw_node_(c, c->val_name(), true);
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
visit(const binop* bo)
|
visit(const binop* bo)
|
||||||
{
|
{
|
||||||
if (draw_node_(bo, bo->op_name()))
|
if (draw_node_(bo, bo->op_name()))
|
||||||
{
|
{
|
||||||
dotty_visitor v(*this);
|
dotty_visitor v(*this);
|
||||||
bo->first()->accept(v);
|
bo->first()->accept(v);
|
||||||
bo->second()->accept(*this);
|
bo->second()->accept(*this);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
visit(const unop* uo)
|
visit(const unop* uo)
|
||||||
{
|
{
|
||||||
if (draw_node_(uo, uo->op_name()))
|
if (draw_node_(uo, uo->op_name()))
|
||||||
uo->child()->accept(*this);
|
uo->child()->accept(*this);
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
visit(const multop* mo)
|
visit(const multop* mo)
|
||||||
{
|
{
|
||||||
if (!draw_node_(mo, mo->op_name()))
|
if (!draw_node_(mo, mo->op_name()))
|
||||||
return;
|
return;
|
||||||
unsigned max = mo->size();
|
unsigned max = mo->size();
|
||||||
for (unsigned n = 0; n < max; ++n)
|
for (unsigned n = 0; n < max; ++n)
|
||||||
{
|
{
|
||||||
dotty_visitor v(*this);
|
dotty_visitor v(*this);
|
||||||
mo->nth(n)->accept(v);
|
mo->nth(n)->accept(v);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
private:
|
private:
|
||||||
std::ostream& os_;
|
std::ostream& os_;
|
||||||
int father_;
|
int father_;
|
||||||
map& node_;
|
map& node_;
|
||||||
|
|
||||||
bool
|
bool
|
||||||
draw_node_(const formula* f, const std::string& str, bool rec = false)
|
draw_node_(const formula* f, const std::string& str, bool rec = false)
|
||||||
{
|
{
|
||||||
map::iterator i = node_.find(f);
|
map::iterator i = node_.find(f);
|
||||||
int node;
|
int node;
|
||||||
bool node_exists = false;
|
bool node_exists = false;
|
||||||
if (i != node_.end())
|
if (i != node_.end())
|
||||||
{
|
{
|
||||||
node = i->second;
|
node = i->second;
|
||||||
node_exists = true;
|
node_exists = true;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
node = node_.size();
|
node = node_.size();
|
||||||
node_[f] = node;
|
node_[f] = node;
|
||||||
}
|
}
|
||||||
// the link
|
// the link
|
||||||
if (father_ >= 0)
|
if (father_ >= 0)
|
||||||
os_ << " " << father_ << " -> " << node << ";" << std::endl;
|
os_ << " " << father_ << " -> " << node << ";" << std::endl;
|
||||||
father_ = node;
|
father_ = node;
|
||||||
// the node
|
// the node
|
||||||
if (node_exists)
|
if (node_exists)
|
||||||
return false;
|
return false;
|
||||||
os_ << " " << node
|
os_ << " " << node
|
||||||
<< " [label=\"" << str << "\"";
|
<< " [label=\"" << str << "\"";
|
||||||
if (rec)
|
if (rec)
|
||||||
os_ << ", shape=box";
|
os_ << ", shape=box";
|
||||||
os_ << "];" << std::endl;
|
os_ << "];" << std::endl;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
}
|
||||||
|
|
||||||
std::ostream&
|
std::ostream&
|
||||||
dotty(std::ostream& os, const formula* f)
|
dotty(std::ostream& os, const formula* f)
|
||||||
|
|
|
||||||
|
|
@ -28,66 +28,68 @@ namespace spot
|
||||||
{
|
{
|
||||||
namespace ltl
|
namespace ltl
|
||||||
{
|
{
|
||||||
|
namespace
|
||||||
class dump_visitor : public const_visitor
|
|
||||||
{
|
{
|
||||||
public:
|
class dump_visitor: public const_visitor
|
||||||
dump_visitor(std::ostream& os)
|
|
||||||
: os_(os)
|
|
||||||
{
|
{
|
||||||
}
|
public:
|
||||||
|
dump_visitor(std::ostream& os)
|
||||||
|
: os_(os)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
virtual
|
virtual
|
||||||
~dump_visitor()
|
~dump_visitor()
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
visit(const atomic_prop* ap)
|
visit(const atomic_prop* ap)
|
||||||
{
|
{
|
||||||
os_ << "AP(" << ap->name() << ")";
|
os_ << "AP(" << ap->name() << ")";
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
visit(const constant* c)
|
visit(const constant* c)
|
||||||
{
|
{
|
||||||
os_ << "constant(" << c->val_name() << ")";
|
os_ << "constant(" << c->val_name() << ")";
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
visit(const binop* bo)
|
visit(const binop* bo)
|
||||||
{
|
{
|
||||||
os_ << "binop(" << bo->op_name() << ", ";
|
os_ << "binop(" << bo->op_name() << ", ";
|
||||||
bo->first()->accept(*this);
|
bo->first()->accept(*this);
|
||||||
os_ << ", ";
|
os_ << ", ";
|
||||||
bo->second()->accept(*this);
|
bo->second()->accept(*this);
|
||||||
os_ << ")";
|
os_ << ")";
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
visit(const unop* uo)
|
visit(const unop* uo)
|
||||||
{
|
{
|
||||||
os_ << "unop(" << uo->op_name() << ", ";
|
os_ << "unop(" << uo->op_name() << ", ";
|
||||||
uo->child()->accept(*this);
|
uo->child()->accept(*this);
|
||||||
os_ << ")";
|
os_ << ")";
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
visit(const multop* mo)
|
visit(const multop* mo)
|
||||||
{
|
{
|
||||||
os_ << "multop(" << mo->op_name() << ", ";
|
os_ << "multop(" << mo->op_name() << ", ";
|
||||||
unsigned max = mo->size();
|
unsigned max = mo->size();
|
||||||
mo->nth(0)->accept(*this);
|
mo->nth(0)->accept(*this);
|
||||||
for (unsigned n = 1; n < max; ++n)
|
for (unsigned n = 1; n < max; ++n)
|
||||||
{
|
{
|
||||||
os_ << ", ";
|
os_ << ", ";
|
||||||
mo->nth(n)->accept(*this);
|
mo->nth(n)->accept(*this);
|
||||||
}
|
}
|
||||||
os_ << ")";
|
os_ << ")";
|
||||||
}
|
}
|
||||||
private:
|
private:
|
||||||
std::ostream& os_;
|
std::ostream& os_;
|
||||||
};
|
};
|
||||||
|
}
|
||||||
|
|
||||||
std::ostream&
|
std::ostream&
|
||||||
dump(std::ostream& os, const formula* f)
|
dump(std::ostream& os, const formula* f)
|
||||||
|
|
|
||||||
|
|
@ -27,62 +27,64 @@ namespace spot
|
||||||
{
|
{
|
||||||
namespace ltl
|
namespace ltl
|
||||||
{
|
{
|
||||||
|
namespace
|
||||||
class length_visitor : public const_visitor
|
|
||||||
{
|
{
|
||||||
public:
|
class length_visitor: public const_visitor
|
||||||
|
|
||||||
length_visitor()
|
|
||||||
{
|
{
|
||||||
result_ = 0;
|
public:
|
||||||
}
|
|
||||||
|
|
||||||
virtual
|
length_visitor()
|
||||||
~length_visitor()
|
{
|
||||||
{
|
result_ = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
int
|
virtual
|
||||||
result() const
|
~length_visitor()
|
||||||
{
|
{
|
||||||
return result_;
|
}
|
||||||
}
|
|
||||||
|
|
||||||
void
|
int
|
||||||
visit(const atomic_prop*)
|
result() const
|
||||||
{
|
{
|
||||||
result_ = 1;
|
return result_;
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
visit(const constant*)
|
visit(const atomic_prop*)
|
||||||
{
|
{
|
||||||
result_ = 1;
|
result_ = 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
visit(const unop* uo)
|
visit(const constant*)
|
||||||
{
|
{
|
||||||
result_ = 1 + length(uo->child());
|
result_ = 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
visit(const binop* bo)
|
visit(const unop* uo)
|
||||||
{
|
{
|
||||||
result_ = 1 + length(bo->first()) + length(bo->second());
|
result_ = 1 + length(uo->child());
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
visit(const multop* mo)
|
visit(const binop* bo)
|
||||||
{
|
{
|
||||||
unsigned mos = mo->size();
|
result_ = 1 + length(bo->first()) + length(bo->second());
|
||||||
for (unsigned i = 0; i < mos; ++i)
|
}
|
||||||
result_ += length(mo->nth(i));
|
|
||||||
}
|
|
||||||
|
|
||||||
protected:
|
void
|
||||||
int result_; // size of the formula
|
visit(const multop* mo)
|
||||||
};
|
{
|
||||||
|
unsigned mos = mo->size();
|
||||||
|
for (unsigned i = 0; i < mos; ++i)
|
||||||
|
result_ += length(mo->nth(i));
|
||||||
|
}
|
||||||
|
|
||||||
|
protected:
|
||||||
|
int result_; // size of the formula
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
int
|
int
|
||||||
length(const formula* f)
|
length(const formula* f)
|
||||||
|
|
|
||||||
|
|
@ -27,166 +27,170 @@ namespace spot
|
||||||
{
|
{
|
||||||
namespace ltl
|
namespace ltl
|
||||||
{
|
{
|
||||||
|
namespace
|
||||||
class negative_normal_form_visitor : public visitor
|
|
||||||
{
|
{
|
||||||
public:
|
class negative_normal_form_visitor: public visitor
|
||||||
negative_normal_form_visitor(bool negated)
|
|
||||||
: negated_(negated)
|
|
||||||
{
|
{
|
||||||
}
|
public:
|
||||||
|
negative_normal_form_visitor(bool negated)
|
||||||
|
: negated_(negated)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
virtual
|
virtual
|
||||||
~negative_normal_form_visitor()
|
~negative_normal_form_visitor()
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
formula* result() const
|
formula* result() const
|
||||||
{
|
{
|
||||||
return result_;
|
return result_;
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
visit(atomic_prop* ap)
|
visit(atomic_prop* ap)
|
||||||
{
|
{
|
||||||
formula* f = ap->ref();
|
formula* f = ap->ref();
|
||||||
if (negated_)
|
if (negated_)
|
||||||
result_ = unop::instance(unop::Not, f);
|
result_ = unop::instance(unop::Not, f);
|
||||||
else
|
else
|
||||||
result_ = f;
|
result_ = f;
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
visit(constant* c)
|
visit(constant* c)
|
||||||
{
|
{
|
||||||
if (!negated_)
|
if (!negated_)
|
||||||
{
|
|
||||||
result_ = c;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
switch (c->val())
|
|
||||||
{
|
|
||||||
case constant::True:
|
|
||||||
result_ = constant::false_instance();
|
|
||||||
return;
|
|
||||||
case constant::False:
|
|
||||||
result_ = constant::true_instance();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
/* Unreachable code. */
|
|
||||||
assert(0);
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
visit(unop* uo)
|
|
||||||
{
|
|
||||||
formula* f = uo->child();
|
|
||||||
switch (uo->op())
|
|
||||||
{
|
|
||||||
case unop::Not:
|
|
||||||
result_ = recurse_(f, negated_ ^ true);
|
|
||||||
return;
|
|
||||||
case unop::X:
|
|
||||||
/* !Xa == X!a */
|
|
||||||
result_ = unop::instance(unop::X, recurse(f));
|
|
||||||
return;
|
|
||||||
case unop::F:
|
|
||||||
/* !Fa == G!a */
|
|
||||||
result_ = unop::instance(negated_ ? unop::G : unop::F, recurse(f));
|
|
||||||
return;
|
|
||||||
case unop::G:
|
|
||||||
/* !Ga == F!a */
|
|
||||||
result_ = unop::instance(negated_ ? unop::F : unop::G, recurse(f));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
/* Unreachable code. */
|
|
||||||
assert(0);
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
visit(binop* bo)
|
|
||||||
{
|
|
||||||
formula* f1 = bo->first();
|
|
||||||
formula* f2 = bo->second();
|
|
||||||
switch (bo->op())
|
|
||||||
{
|
|
||||||
case binop::Xor:
|
|
||||||
/* !(a ^ b) == a <=> b */
|
|
||||||
result_ = binop::instance(negated_ ? binop::Equiv : binop::Xor,
|
|
||||||
recurse_(f1, false),
|
|
||||||
recurse_(f2, false));
|
|
||||||
return;
|
|
||||||
case binop::Equiv:
|
|
||||||
/* !(a <=> b) == a ^ b */
|
|
||||||
result_ = binop::instance(negated_ ? binop::Xor : binop::Equiv,
|
|
||||||
recurse_(f1, false),
|
|
||||||
recurse_(f2, false));
|
|
||||||
return;
|
|
||||||
case binop::Implies:
|
|
||||||
if (negated_)
|
|
||||||
/* !(a => b) == a & !b */
|
|
||||||
result_ = multop::instance(multop::And,
|
|
||||||
recurse_(f1, false),
|
|
||||||
recurse_(f2, true));
|
|
||||||
else
|
|
||||||
result_ = binop::instance(binop::Implies,
|
|
||||||
recurse(f1), recurse(f2));
|
|
||||||
return;
|
|
||||||
case binop::U:
|
|
||||||
/* !(a U b) == !a R !b */
|
|
||||||
result_ = binop::instance(negated_ ? binop::R : binop::U,
|
|
||||||
recurse(f1), recurse(f2));
|
|
||||||
return;
|
|
||||||
case binop::R:
|
|
||||||
/* !(a R b) == !a U !b */
|
|
||||||
result_ = binop::instance(negated_ ? binop::U : binop::R,
|
|
||||||
recurse(f1), recurse(f2));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
/* Unreachable code. */
|
|
||||||
assert(0);
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
visit(multop* mo)
|
|
||||||
{
|
|
||||||
/* !(a & b & c) == !a | !b | !c */
|
|
||||||
/* !(a | b | c) == !a & !b & !c */
|
|
||||||
multop::type op = mo->op();
|
|
||||||
if (negated_)
|
|
||||||
switch (op)
|
|
||||||
{
|
{
|
||||||
case multop::And:
|
result_ = c;
|
||||||
op = multop::Or;
|
return;
|
||||||
break;
|
|
||||||
case multop::Or:
|
|
||||||
op = multop::And;
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
multop::vec* res = new multop::vec;
|
|
||||||
unsigned mos = mo->size();
|
|
||||||
for (unsigned i = 0; i < mos; ++i)
|
|
||||||
res->push_back(recurse(mo->nth(i)));
|
|
||||||
result_ = multop::instance(op, res);
|
|
||||||
}
|
|
||||||
|
|
||||||
formula*
|
switch (c->val())
|
||||||
recurse_(formula* f, bool negated)
|
{
|
||||||
{
|
case constant::True:
|
||||||
return negative_normal_form(f, negated);
|
result_ = constant::false_instance();
|
||||||
}
|
return;
|
||||||
|
case constant::False:
|
||||||
|
result_ = constant::true_instance();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
/* Unreachable code. */
|
||||||
|
assert(0);
|
||||||
|
}
|
||||||
|
|
||||||
formula*
|
void
|
||||||
recurse(formula* f)
|
visit(unop* uo)
|
||||||
{
|
{
|
||||||
return recurse_(f, negated_);
|
formula* f = uo->child();
|
||||||
}
|
switch (uo->op())
|
||||||
|
{
|
||||||
|
case unop::Not:
|
||||||
|
result_ = recurse_(f, negated_ ^ true);
|
||||||
|
return;
|
||||||
|
case unop::X:
|
||||||
|
/* !Xa == X!a */
|
||||||
|
result_ = unop::instance(unop::X, recurse(f));
|
||||||
|
return;
|
||||||
|
case unop::F:
|
||||||
|
/* !Fa == G!a */
|
||||||
|
result_ = unop::instance(negated_ ? unop::G : unop::F,
|
||||||
|
recurse(f));
|
||||||
|
return;
|
||||||
|
case unop::G:
|
||||||
|
/* !Ga == F!a */
|
||||||
|
result_ = unop::instance(negated_ ? unop::F : unop::G,
|
||||||
|
recurse(f));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
/* Unreachable code. */
|
||||||
|
assert(0);
|
||||||
|
}
|
||||||
|
|
||||||
protected:
|
void
|
||||||
formula* result_;
|
visit(binop* bo)
|
||||||
bool negated_;
|
{
|
||||||
};
|
formula* f1 = bo->first();
|
||||||
|
formula* f2 = bo->second();
|
||||||
|
switch (bo->op())
|
||||||
|
{
|
||||||
|
case binop::Xor:
|
||||||
|
/* !(a ^ b) == a <=> b */
|
||||||
|
result_ = binop::instance(negated_ ? binop::Equiv : binop::Xor,
|
||||||
|
recurse_(f1, false),
|
||||||
|
recurse_(f2, false));
|
||||||
|
return;
|
||||||
|
case binop::Equiv:
|
||||||
|
/* !(a <=> b) == a ^ b */
|
||||||
|
result_ = binop::instance(negated_ ? binop::Xor : binop::Equiv,
|
||||||
|
recurse_(f1, false),
|
||||||
|
recurse_(f2, false));
|
||||||
|
return;
|
||||||
|
case binop::Implies:
|
||||||
|
if (negated_)
|
||||||
|
/* !(a => b) == a & !b */
|
||||||
|
result_ = multop::instance(multop::And,
|
||||||
|
recurse_(f1, false),
|
||||||
|
recurse_(f2, true));
|
||||||
|
else
|
||||||
|
result_ = binop::instance(binop::Implies,
|
||||||
|
recurse(f1), recurse(f2));
|
||||||
|
return;
|
||||||
|
case binop::U:
|
||||||
|
/* !(a U b) == !a R !b */
|
||||||
|
result_ = binop::instance(negated_ ? binop::R : binop::U,
|
||||||
|
recurse(f1), recurse(f2));
|
||||||
|
return;
|
||||||
|
case binop::R:
|
||||||
|
/* !(a R b) == !a U !b */
|
||||||
|
result_ = binop::instance(negated_ ? binop::U : binop::R,
|
||||||
|
recurse(f1), recurse(f2));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
/* Unreachable code. */
|
||||||
|
assert(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
visit(multop* mo)
|
||||||
|
{
|
||||||
|
/* !(a & b & c) == !a | !b | !c */
|
||||||
|
/* !(a | b | c) == !a & !b & !c */
|
||||||
|
multop::type op = mo->op();
|
||||||
|
if (negated_)
|
||||||
|
switch (op)
|
||||||
|
{
|
||||||
|
case multop::And:
|
||||||
|
op = multop::Or;
|
||||||
|
break;
|
||||||
|
case multop::Or:
|
||||||
|
op = multop::And;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
multop::vec* res = new multop::vec;
|
||||||
|
unsigned mos = mo->size();
|
||||||
|
for (unsigned i = 0; i < mos; ++i)
|
||||||
|
res->push_back(recurse(mo->nth(i)));
|
||||||
|
result_ = multop::instance(op, res);
|
||||||
|
}
|
||||||
|
|
||||||
|
formula*
|
||||||
|
recurse_(formula* f, bool negated)
|
||||||
|
{
|
||||||
|
return negative_normal_form(f, negated);
|
||||||
|
}
|
||||||
|
|
||||||
|
formula*
|
||||||
|
recurse(formula* f)
|
||||||
|
{
|
||||||
|
return recurse_(f, negated_);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected:
|
||||||
|
formula* result_;
|
||||||
|
bool negated_;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
formula*
|
formula*
|
||||||
negative_normal_form(const formula* f, bool negated)
|
negative_normal_form(const formula* f, bool negated)
|
||||||
|
|
|
||||||
|
|
@ -34,258 +34,261 @@ namespace spot
|
||||||
{
|
{
|
||||||
namespace ltl
|
namespace ltl
|
||||||
{
|
{
|
||||||
|
namespace
|
||||||
class reduce_visitor : public visitor
|
|
||||||
{
|
{
|
||||||
public:
|
class reduce_visitor: public visitor
|
||||||
|
|
||||||
reduce_visitor(int opt)
|
|
||||||
: opt_(opt)
|
|
||||||
{
|
{
|
||||||
}
|
public:
|
||||||
|
|
||||||
virtual ~reduce_visitor()
|
reduce_visitor(int opt)
|
||||||
{
|
: opt_(opt)
|
||||||
}
|
{
|
||||||
|
}
|
||||||
|
|
||||||
formula*
|
virtual ~reduce_visitor()
|
||||||
result() const
|
{
|
||||||
{
|
}
|
||||||
return result_;
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
formula*
|
||||||
visit(atomic_prop* ap)
|
result() const
|
||||||
{
|
{
|
||||||
formula* f = ap->ref();
|
return result_;
|
||||||
result_ = f;
|
}
|
||||||
}
|
|
||||||
|
|
||||||
void
|
void
|
||||||
visit(constant* c)
|
visit(atomic_prop* ap)
|
||||||
{
|
{
|
||||||
result_ = c;
|
formula* f = ap->ref();
|
||||||
}
|
result_ = f;
|
||||||
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
visit(unop* uo)
|
visit(constant* c)
|
||||||
{
|
{
|
||||||
result_ = recurse(uo->child());
|
result_ = c;
|
||||||
|
}
|
||||||
|
|
||||||
switch (uo->op())
|
void
|
||||||
{
|
visit(unop* uo)
|
||||||
case unop::Not:
|
{
|
||||||
result_ = unop::instance(unop::Not, result_);
|
result_ = recurse(uo->child());
|
||||||
return;
|
|
||||||
|
|
||||||
case unop::X:
|
switch (uo->op())
|
||||||
result_ = unop::instance(unop::X, result_);
|
{
|
||||||
return;
|
case unop::Not:
|
||||||
|
result_ = unop::instance(unop::Not, result_);
|
||||||
|
return;
|
||||||
|
|
||||||
case unop::F:
|
case unop::X:
|
||||||
/* If f is a pure eventuality formula then F(f)=f. */
|
result_ = unop::instance(unop::X, result_);
|
||||||
if (!(opt_ & Reduce_Eventuality_And_Universality)
|
return;
|
||||||
|| !is_eventual(result_))
|
|
||||||
result_ = unop::instance(unop::F, result_);
|
|
||||||
return;
|
|
||||||
|
|
||||||
case unop::G:
|
case unop::F:
|
||||||
/* If f is a pure universality formula then G(f)=f. */
|
/* If f is a pure eventuality formula then F(f)=f. */
|
||||||
if (!(opt_ & Reduce_Eventuality_And_Universality)
|
if (!(opt_ & Reduce_Eventuality_And_Universality)
|
||||||
|| !is_universal(result_))
|
|| !is_eventual(result_))
|
||||||
result_ = unop::instance(unop::G, result_);
|
result_ = unop::instance(unop::F, result_);
|
||||||
return;
|
return;
|
||||||
}
|
|
||||||
/* Unreachable code. */
|
|
||||||
assert(0);
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
case unop::G:
|
||||||
visit(binop* bo)
|
/* If f is a pure universality formula then G(f)=f. */
|
||||||
{
|
if (!(opt_ & Reduce_Eventuality_And_Universality)
|
||||||
formula* f2 = recurse(bo->second());
|
|| !is_universal(result_))
|
||||||
|
result_ = unop::instance(unop::G, result_);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
/* Unreachable code. */
|
||||||
|
assert(0);
|
||||||
|
}
|
||||||
|
|
||||||
/* If b is a pure eventuality formula then a U b = b.
|
void
|
||||||
If b is a pure universality formula a R b = b. */
|
visit(binop* bo)
|
||||||
if ((opt_ & Reduce_Eventuality_And_Universality)
|
{
|
||||||
&& ((is_eventual(f2) && ((bo->op()) == binop::U))
|
formula* f2 = recurse(bo->second());
|
||||||
|| (is_universal(f2) && ((bo->op()) == binop::R))))
|
|
||||||
{
|
|
||||||
result_ = f2;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
/* case of implies */
|
|
||||||
formula* f1 = recurse(bo->first());
|
|
||||||
|
|
||||||
if (opt_ & Reduce_Syntactic_Implications)
|
/* If b is a pure eventuality formula then a U b = b.
|
||||||
{
|
If b is a pure universality formula a R b = b. */
|
||||||
// FIXME: These should be done only when needed.
|
if ((opt_ & Reduce_Eventuality_And_Universality)
|
||||||
bool inf = syntactic_implication(f1, f2);
|
&& ((is_eventual(f2) && ((bo->op()) == binop::U))
|
||||||
bool infinv = syntactic_implication(f2, f1);
|
|| (is_universal(f2) && ((bo->op()) == binop::R))))
|
||||||
bool infnegleft = syntactic_implication_neg(f2, f1, false);
|
{
|
||||||
bool infnegright = syntactic_implication_neg(f2, f1, true);
|
result_ = f2;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
/* case of implies */
|
||||||
|
formula* f1 = recurse(bo->first());
|
||||||
|
|
||||||
switch (bo->op())
|
if (opt_ & Reduce_Syntactic_Implications)
|
||||||
{
|
{
|
||||||
case binop::Xor:
|
// FIXME: These should be done only when needed.
|
||||||
case binop::Equiv:
|
bool inf = syntactic_implication(f1, f2);
|
||||||
case binop::Implies:
|
bool infinv = syntactic_implication(f2, f1);
|
||||||
break;
|
bool infnegleft = syntactic_implication_neg(f2, f1, false);
|
||||||
|
bool infnegright = syntactic_implication_neg(f2, f1, true);
|
||||||
|
|
||||||
case binop::U:
|
switch (bo->op())
|
||||||
/* a < b => a U b = b */
|
|
||||||
if (inf)
|
|
||||||
{
|
|
||||||
result_ = f2;
|
|
||||||
destroy(f1);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
/* !b < a => a U b = Fb */
|
|
||||||
if (infnegleft)
|
|
||||||
{
|
|
||||||
result_ = unop::instance(unop::F, f2);
|
|
||||||
destroy(f1);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
/* a < b => a U (b U c) = (b U c) */
|
|
||||||
{
|
{
|
||||||
binop* bo = dynamic_cast<binop*>(f2);
|
case binop::Xor:
|
||||||
if (bo && bo->op() == binop::U
|
case binop::Equiv:
|
||||||
&& syntactic_implication(f1, bo->first()))
|
case binop::Implies:
|
||||||
|
break;
|
||||||
|
|
||||||
|
case binop::U:
|
||||||
|
/* a < b => a U b = b */
|
||||||
|
if (inf)
|
||||||
{
|
{
|
||||||
result_ = f2;
|
result_ = f2;
|
||||||
destroy(f1);
|
destroy(f1);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
/* !b < a => a U b = Fb */
|
||||||
break;
|
if (infnegleft)
|
||||||
|
{
|
||||||
|
result_ = unop::instance(unop::F, f2);
|
||||||
|
destroy(f1);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
/* a < b => a U (b U c) = (b U c) */
|
||||||
|
{
|
||||||
|
binop* bo = dynamic_cast<binop*>(f2);
|
||||||
|
if (bo && bo->op() == binop::U
|
||||||
|
&& syntactic_implication(f1, bo->first()))
|
||||||
|
{
|
||||||
|
result_ = f2;
|
||||||
|
destroy(f1);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
case binop::R:
|
case binop::R:
|
||||||
/* b < a => a R b = b */
|
/* b < a => a R b = b */
|
||||||
if (infinv)
|
if (infinv)
|
||||||
{
|
|
||||||
result_ = f2;
|
|
||||||
destroy(f1);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
/* b < !a => a R b = Gb */
|
|
||||||
if (infnegright)
|
|
||||||
{
|
|
||||||
result_ = unop::instance(unop::G, f2);
|
|
||||||
destroy(f1);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
/* b < a => a R (b R c) = b R c */
|
|
||||||
{
|
|
||||||
binop* bo = dynamic_cast<binop*>(f2);
|
|
||||||
if (bo && bo->op() == binop::R
|
|
||||||
&& syntactic_implication(bo->first(), f1))
|
|
||||||
{
|
{
|
||||||
result_ = f2;
|
result_ = f2;
|
||||||
destroy(f1);
|
destroy(f1);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
/* b < !a => a R b = Gb */
|
||||||
|
if (infnegright)
|
||||||
|
{
|
||||||
|
result_ = unop::instance(unop::G, f2);
|
||||||
|
destroy(f1);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
/* b < a => a R (b R c) = b R c */
|
||||||
|
{
|
||||||
|
binop* bo = dynamic_cast<binop*>(f2);
|
||||||
|
if (bo && bo->op() == binop::R
|
||||||
|
&& syntactic_implication(bo->first(), f1))
|
||||||
|
{
|
||||||
|
result_ = f2;
|
||||||
|
destroy(f1);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
break;
|
}
|
||||||
}
|
result_ = binop::instance(bo->op(), f1, f2);
|
||||||
}
|
}
|
||||||
result_ = binop::instance(bo->op(), f1, f2);
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
void
|
||||||
visit(multop* mo)
|
visit(multop* mo)
|
||||||
{
|
{
|
||||||
unsigned mos = mo->size();
|
unsigned mos = mo->size();
|
||||||
multop::vec* res = new multop::vec;
|
multop::vec* res = new multop::vec;
|
||||||
|
|
||||||
for (unsigned i = 0; i < mos; ++i)
|
for (unsigned i = 0; i < mos; ++i)
|
||||||
res->push_back(recurse(mo->nth(i)));
|
res->push_back(recurse(mo->nth(i)));
|
||||||
|
|
||||||
if (opt_ & Reduce_Syntactic_Implications)
|
if (opt_ & Reduce_Syntactic_Implications)
|
||||||
{
|
{
|
||||||
|
|
||||||
bool removed = true;
|
bool removed = true;
|
||||||
multop::vec::iterator f1;
|
multop::vec::iterator f1;
|
||||||
multop::vec::iterator f2;
|
multop::vec::iterator f2;
|
||||||
|
|
||||||
while (removed)
|
while (removed)
|
||||||
{
|
{
|
||||||
removed = false;
|
removed = false;
|
||||||
f2 = f1 = res->begin();
|
f2 = f1 = res->begin();
|
||||||
++f1;
|
++f1;
|
||||||
while (f1 != res->end())
|
while (f1 != res->end())
|
||||||
{
|
{
|
||||||
assert(f1 != f2);
|
assert(f1 != f2);
|
||||||
// a < b => a + b = b
|
// a < b => a + b = b
|
||||||
// a < b => a & b = a
|
// a < b => a & b = a
|
||||||
if ((syntactic_implication(*f1, *f2) && // f1 < f2
|
if ((syntactic_implication(*f1, *f2) && // f1 < f2
|
||||||
(mo->op() == multop::Or)) ||
|
(mo->op() == multop::Or)) ||
|
||||||
((syntactic_implication(*f2, *f1)) && // f2 < f1
|
((syntactic_implication(*f2, *f1)) && // f2 < f1
|
||||||
(mo->op() == multop::And)))
|
(mo->op() == multop::And)))
|
||||||
{
|
{
|
||||||
// We keep f2
|
// We keep f2
|
||||||
destroy(*f1);
|
destroy(*f1);
|
||||||
res->erase(f1);
|
res->erase(f1);
|
||||||
removed = true;
|
removed = true;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
else if ((syntactic_implication(*f2, *f1) && // f2 < f1
|
else if ((syntactic_implication(*f2, *f1) && // f2 < f1
|
||||||
(mo->op() == multop::Or)) ||
|
(mo->op() == multop::Or)) ||
|
||||||
((syntactic_implication(*f1, *f2)) && // f1 < f2
|
((syntactic_implication(*f1, *f2)) && // f1 < f2
|
||||||
(mo->op() == multop::And)))
|
(mo->op() == multop::And)))
|
||||||
{
|
{
|
||||||
// We keep f1
|
// We keep f1
|
||||||
destroy(*f2);
|
destroy(*f2);
|
||||||
res->erase(f2);
|
res->erase(f2);
|
||||||
removed = true;
|
removed = true;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
++f1;
|
++f1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// FIXME
|
// FIXME
|
||||||
/* f1 < !f2 => f1 & f2 = false
|
/* f1 < !f2 => f1 & f2 = false
|
||||||
!f1 < f2 => f1 | f2 = true */
|
!f1 < f2 => f1 | f2 = true */
|
||||||
for (f1 = res->begin(); f1 != res->end(); f1++)
|
for (f1 = res->begin(); f1 != res->end(); f1++)
|
||||||
for (f2 = res->begin(); f2 != res->end(); f2++)
|
for (f2 = res->begin(); f2 != res->end(); f2++)
|
||||||
if (f1 != f2 &&
|
if (f1 != f2 &&
|
||||||
syntactic_implication_neg(*f1, *f2,
|
syntactic_implication_neg(*f1, *f2,
|
||||||
mo->op() != multop::Or))
|
mo->op() != multop::Or))
|
||||||
{
|
{
|
||||||
for (multop::vec::iterator j = res->begin();
|
for (multop::vec::iterator j = res->begin();
|
||||||
j != res->end(); j++)
|
j != res->end(); j++)
|
||||||
destroy(*j);
|
destroy(*j);
|
||||||
res->clear();
|
res->clear();
|
||||||
delete res;
|
delete res;
|
||||||
if (mo->op() == multop::Or)
|
if (mo->op() == multop::Or)
|
||||||
result_ = constant::true_instance();
|
result_ = constant::true_instance();
|
||||||
else
|
else
|
||||||
result_ = constant::false_instance();
|
result_ = constant::false_instance();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!res->empty())
|
if (!res->empty())
|
||||||
{
|
{
|
||||||
result_ = multop::instance(mo->op(), res);
|
result_ = multop::instance(mo->op(), res);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
assert(0);
|
assert(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
formula*
|
formula*
|
||||||
recurse(formula* f)
|
recurse(formula* f)
|
||||||
{
|
{
|
||||||
return reduce(f, opt_);
|
return reduce(f, opt_);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
formula* result_;
|
formula* result_;
|
||||||
int opt_;
|
int opt_;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
} // anonymous
|
||||||
|
|
||||||
formula*
|
formula*
|
||||||
reduce(const formula* f, int opt)
|
reduce(const formula* f, int opt)
|
||||||
|
|
|
||||||
|
|
@ -32,135 +32,489 @@ namespace spot
|
||||||
{
|
{
|
||||||
namespace ltl
|
namespace ltl
|
||||||
{
|
{
|
||||||
|
namespace
|
||||||
class eventual_universal_visitor : public const_visitor
|
|
||||||
{
|
{
|
||||||
public:
|
|
||||||
|
|
||||||
eventual_universal_visitor()
|
class eventual_universal_visitor: public const_visitor
|
||||||
: eventual(false), universal(false)
|
|
||||||
{
|
{
|
||||||
}
|
public:
|
||||||
|
|
||||||
virtual
|
eventual_universal_visitor()
|
||||||
~eventual_universal_visitor()
|
: eventual(false), universal(false)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
bool
|
virtual
|
||||||
is_eventual() const
|
~eventual_universal_visitor()
|
||||||
{
|
{
|
||||||
return eventual;
|
}
|
||||||
}
|
|
||||||
|
|
||||||
bool
|
bool
|
||||||
is_universal() const
|
is_eventual() const
|
||||||
{
|
{
|
||||||
return universal;
|
return eventual;
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
bool
|
||||||
visit(const atomic_prop*)
|
is_universal() const
|
||||||
{
|
{
|
||||||
}
|
return universal;
|
||||||
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
visit(const constant*)
|
visit(const atomic_prop*)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
visit(const unop* uo)
|
visit(const constant*)
|
||||||
{
|
{
|
||||||
const formula* f1 = uo->child();
|
}
|
||||||
if (uo->op() == unop::F)
|
|
||||||
{
|
|
||||||
eventual = true;
|
|
||||||
universal = recurse_un(f1);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (uo->op() == unop::G)
|
|
||||||
{
|
|
||||||
universal = true;
|
|
||||||
eventual = recurse_ev(f1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
void
|
||||||
visit(const binop* bo)
|
visit(const unop* uo)
|
||||||
{
|
{
|
||||||
const formula* f1 = bo->first();
|
const formula* f1 = uo->child();
|
||||||
const formula* f2 = bo->second();
|
if (uo->op() == unop::F)
|
||||||
switch (bo->op())
|
{
|
||||||
{
|
|
||||||
case binop::Xor:
|
|
||||||
case binop::Equiv:
|
|
||||||
case binop::Implies:
|
|
||||||
universal = recurse_un(f1) & recurse_un(f2);
|
|
||||||
eventual = recurse_ev(f1) & recurse_ev(f2);
|
|
||||||
return;
|
|
||||||
case binop::U:
|
|
||||||
universal = recurse_un(f1) & recurse_un(f2);
|
|
||||||
if ((f1 == constant::true_instance()) ||
|
|
||||||
(recurse_ev(f1)))
|
|
||||||
eventual = true;
|
eventual = true;
|
||||||
return;
|
universal = recurse_un(f1);
|
||||||
case binop::R:
|
return;
|
||||||
eventual = recurse_ev(f1) & recurse_ev(f2);
|
}
|
||||||
if ((f1 == constant::false_instance()))
|
if (uo->op() == unop::G)
|
||||||
//||
|
{
|
||||||
//(recurse_un(f1)))
|
|
||||||
universal = true;
|
universal = true;
|
||||||
if (!universal)
|
eventual = recurse_ev(f1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
visit(const binop* bo)
|
||||||
|
{
|
||||||
|
const formula* f1 = bo->first();
|
||||||
|
const formula* f2 = bo->second();
|
||||||
|
switch (bo->op())
|
||||||
|
{
|
||||||
|
case binop::Xor:
|
||||||
|
case binop::Equiv:
|
||||||
|
case binop::Implies:
|
||||||
universal = recurse_un(f1) & recurse_un(f2);
|
universal = recurse_un(f1) & recurse_un(f2);
|
||||||
return;
|
eventual = recurse_ev(f1) & recurse_ev(f2);
|
||||||
}
|
return;
|
||||||
/* Unreachable code. */
|
case binop::U:
|
||||||
assert(0);
|
universal = recurse_un(f1) & recurse_un(f2);
|
||||||
}
|
if ((f1 == constant::true_instance()) ||
|
||||||
|
(recurse_ev(f1)))
|
||||||
|
eventual = true;
|
||||||
|
return;
|
||||||
|
case binop::R:
|
||||||
|
eventual = recurse_ev(f1) & recurse_ev(f2);
|
||||||
|
if ((f1 == constant::false_instance()))
|
||||||
|
//||
|
||||||
|
//(recurse_un(f1)))
|
||||||
|
universal = true;
|
||||||
|
if (!universal)
|
||||||
|
universal = recurse_un(f1) & recurse_un(f2);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
/* Unreachable code. */
|
||||||
|
assert(0);
|
||||||
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
visit(const multop* mo)
|
visit(const multop* mo)
|
||||||
|
{
|
||||||
|
unsigned mos = mo->size();
|
||||||
|
|
||||||
|
eventual = true;
|
||||||
|
universal = true;
|
||||||
|
for (unsigned i = 0; i < mos; ++i)
|
||||||
|
if (!recurse_ev(mo->nth(i)))
|
||||||
|
{
|
||||||
|
eventual = false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
for (unsigned i = 0; i < mos; ++i)
|
||||||
|
if (!recurse_un(mo->nth(i)))
|
||||||
|
{
|
||||||
|
universal = false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool
|
||||||
|
recurse_ev(const formula* f)
|
||||||
|
{
|
||||||
|
eventual_universal_visitor v;
|
||||||
|
const_cast<formula*>(f)->accept(v);
|
||||||
|
return v.is_eventual();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool
|
||||||
|
recurse_un(const formula* f)
|
||||||
|
{
|
||||||
|
eventual_universal_visitor v;
|
||||||
|
const_cast<formula*>(f)->accept(v);
|
||||||
|
return v.is_universal();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected:
|
||||||
|
bool eventual;
|
||||||
|
bool universal;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
/////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
|
||||||
|
class inf_right_recurse_visitor: public const_visitor
|
||||||
{
|
{
|
||||||
unsigned mos = mo->size();
|
public:
|
||||||
|
|
||||||
eventual = true;
|
inf_right_recurse_visitor(const formula *f)
|
||||||
universal = true;
|
: result_(false), f(f)
|
||||||
for (unsigned i = 0; i < mos; ++i)
|
{
|
||||||
if (!recurse_ev(mo->nth(i)))
|
}
|
||||||
|
|
||||||
|
virtual
|
||||||
|
~inf_right_recurse_visitor()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
result() const
|
||||||
|
{
|
||||||
|
return result_;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
visit(const atomic_prop* ap)
|
||||||
|
{
|
||||||
|
if (dynamic_cast<const atomic_prop*>(f) == ap)
|
||||||
|
result_ = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
visit(const constant* c)
|
||||||
|
{
|
||||||
|
switch (c->val())
|
||||||
{
|
{
|
||||||
eventual = false;
|
case constant::True:
|
||||||
|
result_ = true;
|
||||||
|
return;
|
||||||
|
case constant::False:
|
||||||
|
result_ = false;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
visit(const unop* uo)
|
||||||
|
{
|
||||||
|
const formula* f1 = uo->child();
|
||||||
|
switch (uo->op())
|
||||||
|
{
|
||||||
|
case unop::Not:
|
||||||
|
if (uo == f)
|
||||||
|
result_ = true;
|
||||||
|
return;
|
||||||
|
case unop::X:
|
||||||
|
{
|
||||||
|
const unop* op = dynamic_cast<const unop*>(f);
|
||||||
|
if (op && op->op() == unop::X)
|
||||||
|
result_ = syntactic_implication(op->child(), f1);
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
case unop::F:
|
||||||
|
/* F(a) = true U a */
|
||||||
|
result_ = syntactic_implication(f, f1);
|
||||||
|
return;
|
||||||
|
case unop::G:
|
||||||
|
/* G(a) = false R a */
|
||||||
|
if (syntactic_implication(f, constant::false_instance()))
|
||||||
|
result_ = true;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
/* Unreachable code. */
|
||||||
|
assert(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
visit(const binop* bo)
|
||||||
|
{
|
||||||
|
const formula* f1 = bo->first();
|
||||||
|
const formula* f2 = bo->second();
|
||||||
|
const binop* fb = dynamic_cast<const binop*>(f);
|
||||||
|
const unop* fu = dynamic_cast<const unop*>(f);
|
||||||
|
switch (bo->op())
|
||||||
|
{
|
||||||
|
case binop::Xor:
|
||||||
|
case binop::Equiv:
|
||||||
|
case binop::Implies:
|
||||||
|
return;
|
||||||
|
case binop::U:
|
||||||
|
if (syntactic_implication(f, f2))
|
||||||
|
result_ = true;
|
||||||
|
return;
|
||||||
|
case binop::R:
|
||||||
|
if (fb && fb->op() == binop::R)
|
||||||
|
if (syntactic_implication(fb->first(), f1) &&
|
||||||
|
syntactic_implication(fb->second(), f2))
|
||||||
|
{
|
||||||
|
result_ = true;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (fu && fu->op() == unop::G)
|
||||||
|
if (f1 == constant::false_instance() &&
|
||||||
|
syntactic_implication(fu->child(), f2))
|
||||||
|
{
|
||||||
|
result_ = true;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (syntactic_implication(f, f1)
|
||||||
|
&& syntactic_implication(f, f2))
|
||||||
|
result_ = true;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
/* Unreachable code. */
|
||||||
|
assert(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
visit(const multop* mo)
|
||||||
|
{
|
||||||
|
multop::type op = mo->op();
|
||||||
|
unsigned mos = mo->size();
|
||||||
|
switch (op)
|
||||||
|
{
|
||||||
|
case multop::And:
|
||||||
|
for (unsigned i = 0; i < mos; ++i)
|
||||||
|
if (!syntactic_implication(f, mo->nth(i)))
|
||||||
|
return;
|
||||||
|
result_ = true;
|
||||||
|
break;
|
||||||
|
case multop::Or:
|
||||||
|
for (unsigned i = 0; i < mos && !result_; ++i)
|
||||||
|
if (syntactic_implication(f, mo->nth(i)))
|
||||||
|
result_ = true;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
for (unsigned i = 0; i < mos; ++i)
|
}
|
||||||
if (!recurse_un(mo->nth(i)))
|
|
||||||
|
bool
|
||||||
|
recurse(const formula* f1, const formula* f2)
|
||||||
|
{
|
||||||
|
if (f1 == f2)
|
||||||
|
return true;
|
||||||
|
inf_right_recurse_visitor v(f2);
|
||||||
|
const_cast<formula*>(f1)->accept(v);
|
||||||
|
return v.result();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected:
|
||||||
|
bool result_; /* true if f < f1, false otherwise. */
|
||||||
|
const formula* f;
|
||||||
|
};
|
||||||
|
|
||||||
|
/////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
class inf_left_recurse_visitor: public const_visitor
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
|
||||||
|
inf_left_recurse_visitor(const formula *f)
|
||||||
|
: result_(false), f(f)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual
|
||||||
|
~inf_left_recurse_visitor()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
bool
|
||||||
|
special_case(const formula* f2)
|
||||||
|
{
|
||||||
|
const binop* fb = dynamic_cast<const binop*>(f);
|
||||||
|
const binop* f2b = dynamic_cast<const binop*>(f2);
|
||||||
|
if (fb && f2b && fb->op() == f2b->op()
|
||||||
|
&& syntactic_implication(f2b->first(), fb->first())
|
||||||
|
&& syntactic_implication(f2b->second(), fb->second()))
|
||||||
|
return true;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
result() const
|
||||||
|
{
|
||||||
|
return result_;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
visit(const atomic_prop* ap)
|
||||||
|
{
|
||||||
|
inf_right_recurse_visitor v(ap);
|
||||||
|
const_cast<formula*>(f)->accept(v);
|
||||||
|
result_ = v.result();
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
visit(const constant* c)
|
||||||
|
{
|
||||||
|
inf_right_recurse_visitor v(c);
|
||||||
|
switch (c->val())
|
||||||
{
|
{
|
||||||
universal = false;
|
case constant::True:
|
||||||
|
const_cast<formula*>(f)->accept(v);
|
||||||
|
result_ = v.result();
|
||||||
|
return;
|
||||||
|
case constant::False:
|
||||||
|
result_ = true;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
/* Unreachable code. */
|
||||||
|
assert(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
visit(const unop* uo)
|
||||||
|
{
|
||||||
|
const formula* f1 = uo->child();
|
||||||
|
inf_right_recurse_visitor v(uo);
|
||||||
|
switch (uo->op())
|
||||||
|
{
|
||||||
|
case unop::Not:
|
||||||
|
if (uo == f)
|
||||||
|
result_ = true;
|
||||||
|
return;
|
||||||
|
case unop::X:
|
||||||
|
{
|
||||||
|
const unop* op = dynamic_cast<const unop*>(f);
|
||||||
|
if (op && op->op() == unop::X)
|
||||||
|
result_ = syntactic_implication(f1, op->child());
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
case unop::F:
|
||||||
|
{
|
||||||
|
/* F(a) = true U a */
|
||||||
|
const formula* tmp = binop::instance(binop::U,
|
||||||
|
constant::true_instance(),
|
||||||
|
clone(f1));
|
||||||
|
if (special_case(tmp))
|
||||||
|
{
|
||||||
|
result_ = true;
|
||||||
|
destroy(tmp);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (syntactic_implication(tmp, f))
|
||||||
|
result_ = true;
|
||||||
|
destroy(tmp);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
case unop::G:
|
||||||
|
{
|
||||||
|
/* G(a) = false R a */
|
||||||
|
const formula* tmp = binop::instance(binop::R,
|
||||||
|
constant::false_instance(),
|
||||||
|
clone(f1));
|
||||||
|
if (special_case(tmp))
|
||||||
|
{
|
||||||
|
result_ = true;
|
||||||
|
destroy(tmp);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (syntactic_implication(tmp, f))
|
||||||
|
result_ = true;
|
||||||
|
destroy(tmp);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/* Unreachable code. */
|
||||||
|
assert(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
visit(const binop* bo)
|
||||||
|
{
|
||||||
|
if (special_case(bo))
|
||||||
|
{
|
||||||
|
result_ = true;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const formula* f1 = bo->first();
|
||||||
|
const formula* f2 = bo->second();
|
||||||
|
const binop* fb = dynamic_cast<const binop*>(f);
|
||||||
|
const unop* fu = dynamic_cast<const unop*>(f);
|
||||||
|
switch (bo->op())
|
||||||
|
{
|
||||||
|
case binop::Xor:
|
||||||
|
case binop::Equiv:
|
||||||
|
case binop::Implies:
|
||||||
|
return;
|
||||||
|
case binop::U:
|
||||||
|
/* (a < c) && (c < d) => a U b < c U d */
|
||||||
|
if (fb && fb->op() == binop::U)
|
||||||
|
if (syntactic_implication(f1, fb->first()) &&
|
||||||
|
syntactic_implication(f2, fb->second()))
|
||||||
|
{
|
||||||
|
result_ = true;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (fu && fu->op() == unop::F)
|
||||||
|
if (f1 == constant::true_instance() &&
|
||||||
|
syntactic_implication(f2, fu->child()))
|
||||||
|
{
|
||||||
|
result_ = true;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (syntactic_implication(f1, f)
|
||||||
|
&& syntactic_implication(f2, f))
|
||||||
|
result_ = true;
|
||||||
|
return;
|
||||||
|
case binop::R:
|
||||||
|
if (fu && fu->op() == unop::G)
|
||||||
|
if (f1 == constant::false_instance() &&
|
||||||
|
syntactic_implication(f2, fu->child()))
|
||||||
|
{
|
||||||
|
result_ = true;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (syntactic_implication(f2, f))
|
||||||
|
result_ = true;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
/* Unreachable code. */
|
||||||
|
assert(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
visit(const multop* mo)
|
||||||
|
{
|
||||||
|
multop::type op = mo->op();
|
||||||
|
unsigned mos = mo->size();
|
||||||
|
switch (op)
|
||||||
|
{
|
||||||
|
case multop::And:
|
||||||
|
for (unsigned i = 0; (i < mos) && !result_; ++i)
|
||||||
|
if (syntactic_implication(mo->nth(i), f))
|
||||||
|
result_ = true;
|
||||||
|
break;
|
||||||
|
case multop::Or:
|
||||||
|
for (unsigned i = 0; i < mos; ++i)
|
||||||
|
if (!syntactic_implication(mo->nth(i), f))
|
||||||
|
return;
|
||||||
|
result_ = true;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool
|
protected:
|
||||||
recurse_ev(const formula* f)
|
bool result_; /* true if f1 < f, 1 otherwise. */
|
||||||
{
|
const formula* f;
|
||||||
eventual_universal_visitor v;
|
};
|
||||||
const_cast<formula*>(f)->accept(v);
|
|
||||||
return v.is_eventual();
|
|
||||||
}
|
|
||||||
|
|
||||||
bool
|
|
||||||
recurse_un(const formula* f)
|
|
||||||
{
|
|
||||||
eventual_universal_visitor v;
|
|
||||||
const_cast<formula*>(f)->accept(v);
|
|
||||||
return v.is_universal();
|
|
||||||
}
|
|
||||||
|
|
||||||
protected:
|
|
||||||
bool eventual;
|
|
||||||
bool universal;
|
|
||||||
};
|
|
||||||
|
|
||||||
|
} // anonymous
|
||||||
|
|
||||||
bool
|
bool
|
||||||
is_eventual(const formula* f)
|
is_eventual(const formula* f)
|
||||||
|
|
@ -178,356 +532,6 @@ namespace spot
|
||||||
return v.is_universal();
|
return v.is_universal();
|
||||||
}
|
}
|
||||||
|
|
||||||
/////////////////////////////////////////////////////////////////////////
|
|
||||||
|
|
||||||
|
|
||||||
class inf_right_recurse_visitor : public const_visitor
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
|
|
||||||
inf_right_recurse_visitor(const formula *f)
|
|
||||||
: result_(false), f(f)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
virtual
|
|
||||||
~inf_right_recurse_visitor()
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
int
|
|
||||||
result() const
|
|
||||||
{
|
|
||||||
return result_;
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
visit(const atomic_prop* ap)
|
|
||||||
{
|
|
||||||
if (dynamic_cast<const atomic_prop*>(f) == ap)
|
|
||||||
result_ = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
visit(const constant* c)
|
|
||||||
{
|
|
||||||
switch (c->val())
|
|
||||||
{
|
|
||||||
case constant::True:
|
|
||||||
result_ = true;
|
|
||||||
return;
|
|
||||||
case constant::False:
|
|
||||||
result_ = false;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
visit(const unop* uo)
|
|
||||||
{
|
|
||||||
const formula* f1 = uo->child();
|
|
||||||
switch (uo->op())
|
|
||||||
{
|
|
||||||
case unop::Not:
|
|
||||||
if (uo == f)
|
|
||||||
result_ = true;
|
|
||||||
return;
|
|
||||||
case unop::X:
|
|
||||||
{
|
|
||||||
const unop* op = dynamic_cast<const unop*>(f);
|
|
||||||
if (op && op->op() == unop::X)
|
|
||||||
result_ = syntactic_implication(op->child(), f1);
|
|
||||||
}
|
|
||||||
return;
|
|
||||||
case unop::F:
|
|
||||||
/* F(a) = true U a */
|
|
||||||
result_ = syntactic_implication(f, f1);
|
|
||||||
return;
|
|
||||||
case unop::G:
|
|
||||||
/* G(a) = false R a */
|
|
||||||
if (syntactic_implication(f, constant::false_instance()))
|
|
||||||
result_ = true;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
/* Unreachable code. */
|
|
||||||
assert(0);
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
visit(const binop* bo)
|
|
||||||
{
|
|
||||||
const formula* f1 = bo->first();
|
|
||||||
const formula* f2 = bo->second();
|
|
||||||
const binop* fb = dynamic_cast<const binop*>(f);
|
|
||||||
const unop* fu = dynamic_cast<const unop*>(f);
|
|
||||||
switch (bo->op())
|
|
||||||
{
|
|
||||||
case binop::Xor:
|
|
||||||
case binop::Equiv:
|
|
||||||
case binop::Implies:
|
|
||||||
return;
|
|
||||||
case binop::U:
|
|
||||||
if (syntactic_implication(f, f2))
|
|
||||||
result_ = true;
|
|
||||||
return;
|
|
||||||
case binop::R:
|
|
||||||
if (fb && fb->op() == binop::R)
|
|
||||||
if (syntactic_implication(fb->first(), f1) &&
|
|
||||||
syntactic_implication(fb->second(), f2))
|
|
||||||
{
|
|
||||||
result_ = true;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (fu && fu->op() == unop::G)
|
|
||||||
if (f1 == constant::false_instance() &&
|
|
||||||
syntactic_implication(fu->child(), f2))
|
|
||||||
{
|
|
||||||
result_ = true;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (syntactic_implication(f, f1)
|
|
||||||
&& syntactic_implication(f, f2))
|
|
||||||
result_ = true;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
/* Unreachable code. */
|
|
||||||
assert(0);
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
visit(const multop* mo)
|
|
||||||
{
|
|
||||||
multop::type op = mo->op();
|
|
||||||
unsigned mos = mo->size();
|
|
||||||
switch (op)
|
|
||||||
{
|
|
||||||
case multop::And:
|
|
||||||
for (unsigned i = 0; i < mos; ++i)
|
|
||||||
if (!syntactic_implication(f, mo->nth(i)))
|
|
||||||
return;
|
|
||||||
result_ = true;
|
|
||||||
break;
|
|
||||||
case multop::Or:
|
|
||||||
for (unsigned i = 0; i < mos && !result_; ++i)
|
|
||||||
if (syntactic_implication(f, mo->nth(i)))
|
|
||||||
result_ = true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
bool
|
|
||||||
recurse(const formula* f1, const formula* f2)
|
|
||||||
{
|
|
||||||
if (f1 == f2)
|
|
||||||
return true;
|
|
||||||
inf_right_recurse_visitor v(f2);
|
|
||||||
const_cast<formula*>(f1)->accept(v);
|
|
||||||
return v.result();
|
|
||||||
}
|
|
||||||
|
|
||||||
protected:
|
|
||||||
bool result_; /* true if f < f1, false otherwise. */
|
|
||||||
const formula* f;
|
|
||||||
};
|
|
||||||
|
|
||||||
/////////////////////////////////////////////////////////////////////////
|
|
||||||
|
|
||||||
class inf_left_recurse_visitor : public const_visitor
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
|
|
||||||
inf_left_recurse_visitor(const formula *f)
|
|
||||||
: result_(false), f(f)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
virtual
|
|
||||||
~inf_left_recurse_visitor()
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
bool
|
|
||||||
special_case(const formula* f2)
|
|
||||||
{
|
|
||||||
const binop* fb = dynamic_cast<const binop*>(f);
|
|
||||||
const binop* f2b = dynamic_cast<const binop*>(f2);
|
|
||||||
if (fb && f2b && fb->op() == f2b->op()
|
|
||||||
&& syntactic_implication(f2b->first(), fb->first())
|
|
||||||
&& syntactic_implication(f2b->second(), fb->second()))
|
|
||||||
return true;
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
int
|
|
||||||
result() const
|
|
||||||
{
|
|
||||||
return result_;
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
visit(const atomic_prop* ap)
|
|
||||||
{
|
|
||||||
inf_right_recurse_visitor v(ap);
|
|
||||||
const_cast<formula*>(f)->accept(v);
|
|
||||||
result_ = v.result();
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
visit(const constant* c)
|
|
||||||
{
|
|
||||||
inf_right_recurse_visitor v(c);
|
|
||||||
switch (c->val())
|
|
||||||
{
|
|
||||||
case constant::True:
|
|
||||||
const_cast<formula*>(f)->accept(v);
|
|
||||||
result_ = v.result();
|
|
||||||
return;
|
|
||||||
case constant::False:
|
|
||||||
result_ = true;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
/* Unreachable code. */
|
|
||||||
assert(0);
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
visit(const unop* uo)
|
|
||||||
{
|
|
||||||
const formula* f1 = uo->child();
|
|
||||||
inf_right_recurse_visitor v(uo);
|
|
||||||
switch (uo->op())
|
|
||||||
{
|
|
||||||
case unop::Not:
|
|
||||||
if (uo == f)
|
|
||||||
result_ = true;
|
|
||||||
return;
|
|
||||||
case unop::X:
|
|
||||||
{
|
|
||||||
const unop* op = dynamic_cast<const unop*>(f);
|
|
||||||
if (op && op->op() == unop::X)
|
|
||||||
result_ = syntactic_implication(f1, op->child());
|
|
||||||
}
|
|
||||||
return;
|
|
||||||
case unop::F:
|
|
||||||
{
|
|
||||||
/* F(a) = true U a */
|
|
||||||
const formula* tmp = binop::instance(binop::U,
|
|
||||||
constant::true_instance(),
|
|
||||||
clone(f1));
|
|
||||||
if (special_case(tmp))
|
|
||||||
{
|
|
||||||
result_ = true;
|
|
||||||
destroy(tmp);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (syntactic_implication(tmp, f))
|
|
||||||
result_ = true;
|
|
||||||
destroy(tmp);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
case unop::G:
|
|
||||||
{
|
|
||||||
/* G(a) = false R a */
|
|
||||||
const formula* tmp = binop::instance(binop::R,
|
|
||||||
constant::false_instance(),
|
|
||||||
clone(f1));
|
|
||||||
if (special_case(tmp))
|
|
||||||
{
|
|
||||||
result_ = true;
|
|
||||||
destroy(tmp);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (syntactic_implication(tmp, f))
|
|
||||||
result_ = true;
|
|
||||||
destroy(tmp);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
/* Unreachable code. */
|
|
||||||
assert(0);
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
visit(const binop* bo)
|
|
||||||
{
|
|
||||||
if (special_case(bo))
|
|
||||||
{
|
|
||||||
result_ = true;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const formula* f1 = bo->first();
|
|
||||||
const formula* f2 = bo->second();
|
|
||||||
const binop* fb = dynamic_cast<const binop*>(f);
|
|
||||||
const unop* fu = dynamic_cast<const unop*>(f);
|
|
||||||
switch (bo->op())
|
|
||||||
{
|
|
||||||
case binop::Xor:
|
|
||||||
case binop::Equiv:
|
|
||||||
case binop::Implies:
|
|
||||||
return;
|
|
||||||
case binop::U:
|
|
||||||
/* (a < c) && (c < d) => a U b < c U d */
|
|
||||||
if (fb && fb->op() == binop::U)
|
|
||||||
if (syntactic_implication(f1, fb->first()) &&
|
|
||||||
syntactic_implication(f2, fb->second()))
|
|
||||||
{
|
|
||||||
result_ = true;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (fu && fu->op() == unop::F)
|
|
||||||
if (f1 == constant::true_instance() &&
|
|
||||||
syntactic_implication(f2, fu->child()))
|
|
||||||
{
|
|
||||||
result_ = true;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (syntactic_implication(f1, f)
|
|
||||||
&& syntactic_implication(f2, f))
|
|
||||||
result_ = true;
|
|
||||||
return;
|
|
||||||
case binop::R:
|
|
||||||
if (fu && fu->op() == unop::G)
|
|
||||||
if (f1 == constant::false_instance() &&
|
|
||||||
syntactic_implication(f2, fu->child()))
|
|
||||||
{
|
|
||||||
result_ = true;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (syntactic_implication(f2, f))
|
|
||||||
result_ = true;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
/* Unreachable code. */
|
|
||||||
assert(0);
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
visit(const multop* mo)
|
|
||||||
{
|
|
||||||
multop::type op = mo->op();
|
|
||||||
unsigned mos = mo->size();
|
|
||||||
switch (op)
|
|
||||||
{
|
|
||||||
case multop::And:
|
|
||||||
for (unsigned i = 0; (i < mos) && !result_; ++i)
|
|
||||||
if (syntactic_implication(mo->nth(i), f))
|
|
||||||
result_ = true;
|
|
||||||
break;
|
|
||||||
case multop::Or:
|
|
||||||
for (unsigned i = 0; i < mos; ++i)
|
|
||||||
if (!syntactic_implication(mo->nth(i), f))
|
|
||||||
return;
|
|
||||||
result_ = true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
protected:
|
|
||||||
bool result_; /* true if f1 < f, 1 otherwise. */
|
|
||||||
const formula* f;
|
|
||||||
};
|
|
||||||
|
|
||||||
// This is called by syntactic_implication() after the
|
// This is called by syntactic_implication() after the
|
||||||
// formulae have been normalized.
|
// formulae have been normalized.
|
||||||
bool
|
bool
|
||||||
|
|
|
||||||
|
|
@ -32,159 +32,161 @@ namespace spot
|
||||||
{
|
{
|
||||||
namespace ltl
|
namespace ltl
|
||||||
{
|
{
|
||||||
|
namespace
|
||||||
static bool
|
|
||||||
is_bare_word(const char* str)
|
|
||||||
{
|
{
|
||||||
// Bare words cannot be empty, start with the letter of a unary
|
static bool
|
||||||
// operator, or be the name of an existing constant. Also they
|
is_bare_word(const char* str)
|
||||||
// should start with an letter.
|
{
|
||||||
if (!*str
|
// Bare words cannot be empty, start with the letter of a unary
|
||||||
|| *str == 'F'
|
// operator, or be the name of an existing constant. Also they
|
||||||
|| *str == 'G'
|
// should start with an letter.
|
||||||
|| *str == 'X'
|
if (!*str
|
||||||
|| !(isalpha(*str) || *str == '_')
|
|| *str == 'F'
|
||||||
|| !strcasecmp(str, "true")
|
|| *str == 'G'
|
||||||
|| !strcasecmp(str, "false"))
|
|| *str == 'X'
|
||||||
return false;
|
|| !(isalpha(*str) || *str == '_')
|
||||||
// The remaining of the word must be alphanumeric.
|
|| !strcasecmp(str, "true")
|
||||||
while (*++str)
|
|| !strcasecmp(str, "false"))
|
||||||
if (!(isalnum(*str) || *str == '_'))
|
|
||||||
return false;
|
return false;
|
||||||
return true;
|
// The remaining of the word must be alphanumeric.
|
||||||
|
while (*++str)
|
||||||
|
if (!(isalnum(*str) || *str == '_'))
|
||||||
|
return false;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
class to_string_visitor: public const_visitor
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
to_string_visitor(std::ostream& os)
|
||||||
|
: os_(os), top_level_(true)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual
|
||||||
|
~to_string_visitor()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
visit(const atomic_prop* ap)
|
||||||
|
{
|
||||||
|
std::string str = ap->name();
|
||||||
|
if (!is_bare_word(str.c_str()))
|
||||||
|
{
|
||||||
|
os_ << '"' << str << '"';
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
os_ << str;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
visit(const constant* c)
|
||||||
|
{
|
||||||
|
os_ << c->val_name();
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
visit(const binop* bo)
|
||||||
|
{
|
||||||
|
bool top_level = top_level_;
|
||||||
|
top_level_ = false;
|
||||||
|
if (!top_level)
|
||||||
|
os_ << "(";
|
||||||
|
|
||||||
|
bo->first()->accept(*this);
|
||||||
|
|
||||||
|
switch (bo->op())
|
||||||
|
{
|
||||||
|
case binop::Xor:
|
||||||
|
os_ << " ^ ";
|
||||||
|
break;
|
||||||
|
case binop::Implies:
|
||||||
|
os_ << " => ";
|
||||||
|
break;
|
||||||
|
case binop::Equiv:
|
||||||
|
os_ << " <=> ";
|
||||||
|
break;
|
||||||
|
case binop::U:
|
||||||
|
os_ << " U ";
|
||||||
|
break;
|
||||||
|
case binop::R:
|
||||||
|
os_ << " R ";
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
bo->second()->accept(*this);
|
||||||
|
if (!top_level)
|
||||||
|
os_ << ")";
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
visit(const unop* uo)
|
||||||
|
{
|
||||||
|
// The parser treats F0, F1, G0, G1, X0, and X1 as atomic
|
||||||
|
// propositions. So make sure we output F(0), G(1), etc.
|
||||||
|
bool need_parent = !!dynamic_cast<const constant*>(uo->child());
|
||||||
|
switch (uo->op())
|
||||||
|
{
|
||||||
|
case unop::Not:
|
||||||
|
os_ << "!";
|
||||||
|
need_parent = false;
|
||||||
|
break;
|
||||||
|
case unop::X:
|
||||||
|
os_ << "X";
|
||||||
|
break;
|
||||||
|
case unop::F:
|
||||||
|
os_ << "F";
|
||||||
|
break;
|
||||||
|
case unop::G:
|
||||||
|
os_ << "G";
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
top_level_ = false;
|
||||||
|
if (need_parent)
|
||||||
|
os_ << "(";
|
||||||
|
uo->child()->accept(*this);
|
||||||
|
if (need_parent)
|
||||||
|
os_ << ")";
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
visit(const multop* mo)
|
||||||
|
{
|
||||||
|
bool top_level = top_level_;
|
||||||
|
top_level_ = false;
|
||||||
|
if (!top_level)
|
||||||
|
os_ << "(";
|
||||||
|
unsigned max = mo->size();
|
||||||
|
mo->nth(0)->accept(*this);
|
||||||
|
const char* ch = " ";
|
||||||
|
switch (mo->op())
|
||||||
|
{
|
||||||
|
case multop::Or:
|
||||||
|
ch = " | ";
|
||||||
|
break;
|
||||||
|
case multop::And:
|
||||||
|
ch = " & ";
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (unsigned n = 1; n < max; ++n)
|
||||||
|
{
|
||||||
|
os_ << ch;
|
||||||
|
mo->nth(n)->accept(*this);
|
||||||
|
}
|
||||||
|
if (!top_level)
|
||||||
|
os_ << ")";
|
||||||
|
}
|
||||||
|
protected:
|
||||||
|
std::ostream& os_;
|
||||||
|
bool top_level_;
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
class to_string_visitor : public const_visitor
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
to_string_visitor(std::ostream& os)
|
|
||||||
: os_(os), top_level_(true)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
virtual
|
|
||||||
~to_string_visitor()
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
visit(const atomic_prop* ap)
|
|
||||||
{
|
|
||||||
std::string str = ap->name();
|
|
||||||
if (!is_bare_word(str.c_str()))
|
|
||||||
{
|
|
||||||
os_ << '"' << str << '"';
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
os_ << str;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
visit(const constant* c)
|
|
||||||
{
|
|
||||||
os_ << c->val_name();
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
visit(const binop* bo)
|
|
||||||
{
|
|
||||||
bool top_level = top_level_;
|
|
||||||
top_level_ = false;
|
|
||||||
if (!top_level)
|
|
||||||
os_ << "(";
|
|
||||||
|
|
||||||
bo->first()->accept(*this);
|
|
||||||
|
|
||||||
switch (bo->op())
|
|
||||||
{
|
|
||||||
case binop::Xor:
|
|
||||||
os_ << " ^ ";
|
|
||||||
break;
|
|
||||||
case binop::Implies:
|
|
||||||
os_ << " => ";
|
|
||||||
break;
|
|
||||||
case binop::Equiv:
|
|
||||||
os_ << " <=> ";
|
|
||||||
break;
|
|
||||||
case binop::U:
|
|
||||||
os_ << " U ";
|
|
||||||
break;
|
|
||||||
case binop::R:
|
|
||||||
os_ << " R ";
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
bo->second()->accept(*this);
|
|
||||||
if (!top_level)
|
|
||||||
os_ << ")";
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
visit(const unop* uo)
|
|
||||||
{
|
|
||||||
// The parser treats F0, F1, G0, G1, X0, and X1 as atomic
|
|
||||||
// propositions. So make sure we output F(0), G(1), etc.
|
|
||||||
bool need_parent = !!dynamic_cast<const constant*>(uo->child());
|
|
||||||
switch (uo->op())
|
|
||||||
{
|
|
||||||
case unop::Not:
|
|
||||||
os_ << "!";
|
|
||||||
need_parent = false;
|
|
||||||
break;
|
|
||||||
case unop::X:
|
|
||||||
os_ << "X";
|
|
||||||
break;
|
|
||||||
case unop::F:
|
|
||||||
os_ << "F";
|
|
||||||
break;
|
|
||||||
case unop::G:
|
|
||||||
os_ << "G";
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
top_level_ = false;
|
|
||||||
if (need_parent)
|
|
||||||
os_ << "(";
|
|
||||||
uo->child()->accept(*this);
|
|
||||||
if (need_parent)
|
|
||||||
os_ << ")";
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
visit(const multop* mo)
|
|
||||||
{
|
|
||||||
bool top_level = top_level_;
|
|
||||||
top_level_ = false;
|
|
||||||
if (!top_level)
|
|
||||||
os_ << "(";
|
|
||||||
unsigned max = mo->size();
|
|
||||||
mo->nth(0)->accept(*this);
|
|
||||||
const char* ch = " ";
|
|
||||||
switch (mo->op())
|
|
||||||
{
|
|
||||||
case multop::Or:
|
|
||||||
ch = " | ";
|
|
||||||
break;
|
|
||||||
case multop::And:
|
|
||||||
ch = " & ";
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (unsigned n = 1; n < max; ++n)
|
|
||||||
{
|
|
||||||
os_ << ch;
|
|
||||||
mo->nth(n)->accept(*this);
|
|
||||||
}
|
|
||||||
if (!top_level)
|
|
||||||
os_ << ")";
|
|
||||||
}
|
|
||||||
protected:
|
|
||||||
std::ostream& os_;
|
|
||||||
bool top_level_;
|
|
||||||
};
|
|
||||||
|
|
||||||
std::ostream&
|
std::ostream&
|
||||||
to_string(const formula* f, std::ostream& os)
|
to_string(const formula* f, std::ostream& os)
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -72,6 +72,12 @@ for dir in "${INCDIR-..}" "${INCDIR-..}"/../iface; do
|
||||||
grep '[ ]switch (.*).*{' $tmp &&
|
grep '[ ]switch (.*).*{' $tmp &&
|
||||||
diag 'Opening { should be on its own line.'
|
diag 'Opening { should be on its own line.'
|
||||||
|
|
||||||
|
grep 'namespace .*{' $tmp &&
|
||||||
|
diag 'Opening { should be on its own line.'
|
||||||
|
|
||||||
|
grep 'class .*{' $tmp &&
|
||||||
|
diag 'Opening { should be on its own line.'
|
||||||
|
|
||||||
grep '( ' $tmp &&
|
grep '( ' $tmp &&
|
||||||
diag 'No space after opening (.'
|
diag 'No space after opening (.'
|
||||||
|
|
||||||
|
|
@ -137,6 +143,15 @@ for dir in "${INCDIR-..}" "${INCDIR-..}"/../iface; do
|
||||||
diag 'Avoid <iostream> in headers, better use <iosfwd>.'
|
diag 'Avoid <iostream> in headers, better use <iosfwd>.'
|
||||||
fi
|
fi
|
||||||
;;
|
;;
|
||||||
|
*.cc)
|
||||||
|
if grep 'namespace$' $tmp >/dev/null; then
|
||||||
|
:
|
||||||
|
else
|
||||||
|
# We only check classes, but the rule should apply to functions too
|
||||||
|
grep '^[ ]*class[ ]' $tmp &&
|
||||||
|
diag 'Private definitions must be in anonymous namespace.'
|
||||||
|
fi
|
||||||
|
;;
|
||||||
esac
|
esac
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
// Copyright (C) 2003 Laboratoire d'Informatique de Paris 6 (LIP6),
|
// Copyright (C) 2003, 2004 Laboratoire d'Informatique de Paris 6 (LIP6),
|
||||||
// département Systèmes Répartis Coopératifs (SRC), Université Pierre
|
// département Systèmes Répartis Coopératifs (SRC), Université Pierre
|
||||||
// et Marie Curie.
|
// et Marie Curie.
|
||||||
//
|
//
|
||||||
|
|
@ -30,125 +30,161 @@ namespace spot
|
||||||
{
|
{
|
||||||
using namespace ltl;
|
using namespace ltl;
|
||||||
|
|
||||||
class formula_to_bdd_visitor : public ltl::const_visitor
|
namespace
|
||||||
{
|
{
|
||||||
public:
|
class formula_to_bdd_visitor: public ltl::const_visitor
|
||||||
formula_to_bdd_visitor(bdd_dict* d, void* owner)
|
|
||||||
: d_(d), owner_(owner)
|
|
||||||
{
|
{
|
||||||
}
|
public:
|
||||||
|
formula_to_bdd_visitor(bdd_dict* d, void* owner)
|
||||||
|
: d_(d), owner_(owner)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
virtual
|
virtual
|
||||||
~formula_to_bdd_visitor()
|
~formula_to_bdd_visitor()
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
virtual void
|
virtual void
|
||||||
visit(const atomic_prop* node)
|
visit(const atomic_prop* node)
|
||||||
{
|
{
|
||||||
res_ = bdd_ithvar(d_->register_proposition(node, owner_));
|
res_ = bdd_ithvar(d_->register_proposition(node, owner_));
|
||||||
}
|
}
|
||||||
|
|
||||||
virtual void
|
virtual void
|
||||||
visit(const constant* node)
|
visit(const constant* node)
|
||||||
{
|
{
|
||||||
switch (node->val())
|
switch (node->val())
|
||||||
{
|
|
||||||
case constant::True:
|
|
||||||
res_ = bddtrue;
|
|
||||||
return;
|
|
||||||
case constant::False:
|
|
||||||
res_ = bddfalse;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
/* Unreachable code. */
|
|
||||||
assert(0);
|
|
||||||
}
|
|
||||||
|
|
||||||
virtual void
|
|
||||||
visit(const unop* node)
|
|
||||||
{
|
|
||||||
switch (node->op())
|
|
||||||
{
|
|
||||||
case unop::F:
|
|
||||||
case unop::G:
|
|
||||||
case unop::X:
|
|
||||||
assert(!"unsupported operator");
|
|
||||||
case unop::Not:
|
|
||||||
{
|
{
|
||||||
res_ = bdd_not(recurse(node->child()));
|
case constant::True:
|
||||||
|
res_ = bddtrue;
|
||||||
|
return;
|
||||||
|
case constant::False:
|
||||||
|
res_ = bddfalse;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
/* Unreachable code. */
|
||||||
/* Unreachable code. */
|
assert(0);
|
||||||
assert(0);
|
}
|
||||||
}
|
|
||||||
|
|
||||||
virtual void
|
virtual void
|
||||||
visit(const binop* node)
|
visit(const unop* node)
|
||||||
|
{
|
||||||
|
switch (node->op())
|
||||||
|
{
|
||||||
|
case unop::F:
|
||||||
|
case unop::G:
|
||||||
|
case unop::X:
|
||||||
|
assert(!"unsupported operator");
|
||||||
|
case unop::Not:
|
||||||
|
{
|
||||||
|
res_ = bdd_not(recurse(node->child()));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/* Unreachable code. */
|
||||||
|
assert(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual void
|
||||||
|
visit(const binop* node)
|
||||||
|
{
|
||||||
|
bdd f1 = recurse(node->first());
|
||||||
|
bdd f2 = recurse(node->second());
|
||||||
|
|
||||||
|
switch (node->op())
|
||||||
|
{
|
||||||
|
case binop::Xor:
|
||||||
|
res_ = bdd_apply(f1, f2, bddop_xor);
|
||||||
|
return;
|
||||||
|
case binop::Implies:
|
||||||
|
res_ = bdd_apply(f1, f2, bddop_imp);
|
||||||
|
return;
|
||||||
|
case binop::Equiv:
|
||||||
|
res_ = bdd_apply(f1, f2, bddop_biimp);
|
||||||
|
return;
|
||||||
|
case binop::U:
|
||||||
|
case binop::R:
|
||||||
|
assert(!"unsupported operator");
|
||||||
|
}
|
||||||
|
/* Unreachable code. */
|
||||||
|
assert(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual void
|
||||||
|
visit(const multop* node)
|
||||||
|
{
|
||||||
|
int op = -1;
|
||||||
|
switch (node->op())
|
||||||
|
{
|
||||||
|
case multop::And:
|
||||||
|
op = bddop_and;
|
||||||
|
res_ = bddtrue;
|
||||||
|
break;
|
||||||
|
case multop::Or:
|
||||||
|
op = bddop_or;
|
||||||
|
res_ = bddfalse;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
assert(op != -1);
|
||||||
|
unsigned s = node->size();
|
||||||
|
for (unsigned n = 0; n < s; ++n)
|
||||||
|
{
|
||||||
|
res_ = bdd_apply(res_, recurse(node->nth(n)), op);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bdd
|
||||||
|
result() const
|
||||||
|
{
|
||||||
|
return res_;
|
||||||
|
}
|
||||||
|
|
||||||
|
bdd
|
||||||
|
recurse(const formula* f) const
|
||||||
|
{
|
||||||
|
return formula_to_bdd(f, d_, owner_);
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
bdd_dict* d_;
|
||||||
|
void* owner_;
|
||||||
|
bdd res_;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Convert a BDD which is known to be a conjonction into a formula.
|
||||||
|
static ltl::formula*
|
||||||
|
conj_to_formula(bdd b, const bdd_dict* d)
|
||||||
{
|
{
|
||||||
bdd f1 = recurse(node->first());
|
if (b == bddfalse)
|
||||||
bdd f2 = recurse(node->second());
|
return constant::false_instance();
|
||||||
|
multop::vec* v = new multop::vec;
|
||||||
switch (node->op())
|
while (b != bddtrue)
|
||||||
{
|
{
|
||||||
case binop::Xor:
|
int var = bdd_var(b);
|
||||||
res_ = bdd_apply(f1, f2, bddop_xor);
|
bdd_dict::vf_map::const_iterator isi = d->var_formula_map.find(var);
|
||||||
return;
|
assert(isi != d->var_formula_map.end());
|
||||||
case binop::Implies:
|
formula* res = clone(isi->second);
|
||||||
res_ = bdd_apply(f1, f2, bddop_imp);
|
|
||||||
return;
|
bdd high = bdd_high(b);
|
||||||
case binop::Equiv:
|
if (high == bddfalse)
|
||||||
res_ = bdd_apply(f1, f2, bddop_biimp);
|
{
|
||||||
return;
|
res = unop::instance(unop::Not, res);
|
||||||
case binop::U:
|
b = bdd_low(b);
|
||||||
case binop::R:
|
}
|
||||||
assert(!"unsupported operator");
|
else
|
||||||
|
{
|
||||||
|
// If bdd_low is not false, then b was not a conjunction.
|
||||||
|
assert(bdd_low(b) == bddfalse);
|
||||||
|
b = high;
|
||||||
|
}
|
||||||
|
assert(b != bddfalse);
|
||||||
|
v->push_back(res);
|
||||||
}
|
}
|
||||||
/* Unreachable code. */
|
return multop::instance(multop::And, v);
|
||||||
assert(0);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
virtual void
|
} // anonymous
|
||||||
visit(const multop* node)
|
|
||||||
{
|
|
||||||
int op = -1;
|
|
||||||
switch (node->op())
|
|
||||||
{
|
|
||||||
case multop::And:
|
|
||||||
op = bddop_and;
|
|
||||||
res_ = bddtrue;
|
|
||||||
break;
|
|
||||||
case multop::Or:
|
|
||||||
op = bddop_or;
|
|
||||||
res_ = bddfalse;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
assert(op != -1);
|
|
||||||
unsigned s = node->size();
|
|
||||||
for (unsigned n = 0; n < s; ++n)
|
|
||||||
{
|
|
||||||
res_ = bdd_apply(res_, recurse(node->nth(n)), op);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
bdd
|
|
||||||
result() const
|
|
||||||
{
|
|
||||||
return res_;
|
|
||||||
}
|
|
||||||
|
|
||||||
bdd
|
|
||||||
recurse(const formula* f) const
|
|
||||||
{
|
|
||||||
return formula_to_bdd(f, d_, owner_);
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
|
||||||
bdd_dict* d_;
|
|
||||||
void* owner_;
|
|
||||||
bdd res_;
|
|
||||||
};
|
|
||||||
|
|
||||||
bdd
|
bdd
|
||||||
formula_to_bdd(const formula* f, bdd_dict* d, void* for_me)
|
formula_to_bdd(const formula* f, bdd_dict* d, void* for_me)
|
||||||
|
|
@ -158,38 +194,6 @@ namespace spot
|
||||||
return v.result();
|
return v.result();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Convert a BDD which is known to be a conjonction into a formula.
|
|
||||||
static ltl::formula*
|
|
||||||
conj_to_formula(bdd b, const bdd_dict* d)
|
|
||||||
{
|
|
||||||
if (b == bddfalse)
|
|
||||||
return constant::false_instance();
|
|
||||||
multop::vec* v = new multop::vec;
|
|
||||||
while (b != bddtrue)
|
|
||||||
{
|
|
||||||
int var = bdd_var(b);
|
|
||||||
bdd_dict::vf_map::const_iterator isi = d->var_formula_map.find(var);
|
|
||||||
assert(isi != d->var_formula_map.end());
|
|
||||||
formula* res = clone(isi->second);
|
|
||||||
|
|
||||||
bdd high = bdd_high(b);
|
|
||||||
if (high == bddfalse)
|
|
||||||
{
|
|
||||||
res = unop::instance(unop::Not, res);
|
|
||||||
b = bdd_low(b);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// If bdd_low is not false, then b was not a conjunction.
|
|
||||||
assert(bdd_low(b) == bddfalse);
|
|
||||||
b = high;
|
|
||||||
}
|
|
||||||
assert(b != bddfalse);
|
|
||||||
v->push_back(res);
|
|
||||||
}
|
|
||||||
return multop::instance(multop::And, v);
|
|
||||||
}
|
|
||||||
|
|
||||||
const formula*
|
const formula*
|
||||||
bdd_to_formula(bdd f, const bdd_dict* d)
|
bdd_to_formula(bdd f, const bdd_dict* d)
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
// Copyright (C) 2003 Laboratoire d'Informatique de Paris 6 (LIP6),
|
// Copyright (C) 2003, 2004 Laboratoire d'Informatique de Paris 6 (LIP6),
|
||||||
// département Systèmes Répartis Coopératifs (SRC), Université Pierre
|
// département Systèmes Répartis Coopératifs (SRC), Université Pierre
|
||||||
// et Marie Curie.
|
// et Marie Curie.
|
||||||
//
|
//
|
||||||
|
|
@ -24,55 +24,58 @@
|
||||||
|
|
||||||
namespace spot
|
namespace spot
|
||||||
{
|
{
|
||||||
/// \brief Helper class for product().
|
namespace
|
||||||
///
|
|
||||||
/// As both automata are encoded using BDD, we just have
|
|
||||||
/// to homogenize the variable numbers before ANDing the
|
|
||||||
/// relations and initial states.
|
|
||||||
class tgba_bdd_product_factory: public tgba_bdd_factory
|
|
||||||
{
|
{
|
||||||
public:
|
/// \brief Helper class for product().
|
||||||
tgba_bdd_product_factory(const tgba_bdd_concrete* left,
|
///
|
||||||
const tgba_bdd_concrete* right)
|
/// As both automata are encoded using BDD, we just have
|
||||||
: dict_(left->get_dict()),
|
/// to homogenize the variable numbers before ANDing the
|
||||||
left_(left),
|
/// relations and initial states.
|
||||||
right_(right),
|
class tgba_bdd_product_factory: public tgba_bdd_factory
|
||||||
data_(left_->get_core_data(), right_->get_core_data()),
|
|
||||||
init_(left_->get_init_bdd() & right_->get_init_bdd())
|
|
||||||
{
|
{
|
||||||
assert(dict_ == right->get_dict());
|
public:
|
||||||
}
|
tgba_bdd_product_factory(const tgba_bdd_concrete* left,
|
||||||
|
const tgba_bdd_concrete* right)
|
||||||
|
: dict_(left->get_dict()),
|
||||||
|
left_(left),
|
||||||
|
right_(right),
|
||||||
|
data_(left_->get_core_data(), right_->get_core_data()),
|
||||||
|
init_(left_->get_init_bdd() & right_->get_init_bdd())
|
||||||
|
{
|
||||||
|
assert(dict_ == right->get_dict());
|
||||||
|
}
|
||||||
|
|
||||||
virtual
|
virtual
|
||||||
~tgba_bdd_product_factory()
|
~tgba_bdd_product_factory()
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
const tgba_bdd_core_data&
|
const tgba_bdd_core_data&
|
||||||
get_core_data() const
|
get_core_data() const
|
||||||
{
|
{
|
||||||
return data_;
|
return data_;
|
||||||
}
|
}
|
||||||
|
|
||||||
bdd_dict*
|
bdd_dict*
|
||||||
get_dict() const
|
get_dict() const
|
||||||
{
|
{
|
||||||
return dict_;
|
return dict_;
|
||||||
}
|
}
|
||||||
|
|
||||||
bdd
|
bdd
|
||||||
get_init_state() const
|
get_init_state() const
|
||||||
{
|
{
|
||||||
return init_;
|
return init_;
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
bdd_dict* dict_;
|
bdd_dict* dict_;
|
||||||
const tgba_bdd_concrete* left_;
|
const tgba_bdd_concrete* left_;
|
||||||
const tgba_bdd_concrete* right_;
|
const tgba_bdd_concrete* right_;
|
||||||
tgba_bdd_core_data data_;
|
tgba_bdd_core_data data_;
|
||||||
bdd init_;
|
bdd init_;
|
||||||
};
|
};
|
||||||
|
}
|
||||||
|
|
||||||
tgba_bdd_concrete*
|
tgba_bdd_concrete*
|
||||||
product(const tgba_bdd_concrete* left, const tgba_bdd_concrete* right)
|
product(const tgba_bdd_concrete* left, const tgba_bdd_concrete* right)
|
||||||
|
|
|
||||||
|
|
@ -39,8 +39,12 @@ namespace spot
|
||||||
typedef Sgi::vector<state_couple*> delayed_simulation_relation;
|
typedef Sgi::vector<state_couple*> delayed_simulation_relation;
|
||||||
*/
|
*/
|
||||||
|
|
||||||
class direct_simulation_relation : public simulation_relation{};
|
class direct_simulation_relation: public simulation_relation
|
||||||
class delayed_simulation_relation : public simulation_relation{};
|
{
|
||||||
|
};
|
||||||
|
class delayed_simulation_relation: public simulation_relation
|
||||||
|
{
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
class tgba_reduc: public tgba_explicit,
|
class tgba_reduc: public tgba_explicit,
|
||||||
|
|
|
||||||
|
|
@ -26,171 +26,174 @@
|
||||||
|
|
||||||
namespace spot
|
namespace spot
|
||||||
{
|
{
|
||||||
|
namespace
|
||||||
/// \brief A state for spot::tgba_tba_proxy.
|
|
||||||
///
|
|
||||||
/// This state is in fact a pair of state: the state from the tgba
|
|
||||||
/// automaton, and a state of the "counter" (we use a pointer
|
|
||||||
/// to the position in the cycle_acc_ list).
|
|
||||||
class state_tba_proxy : public state
|
|
||||||
{
|
{
|
||||||
typedef tgba_tba_proxy::cycle_list::const_iterator iterator;
|
/// \brief A state for spot::tgba_tba_proxy.
|
||||||
public:
|
///
|
||||||
state_tba_proxy(state* s, iterator acc)
|
/// This state is in fact a pair of state: the state from the tgba
|
||||||
: s_(s), acc_(acc)
|
/// automaton, and a state of the "counter" (we use a pointer
|
||||||
|
/// to the position in the cycle_acc_ list).
|
||||||
|
class state_tba_proxy: public state
|
||||||
{
|
{
|
||||||
}
|
typedef tgba_tba_proxy::cycle_list::const_iterator iterator;
|
||||||
|
public:
|
||||||
|
state_tba_proxy(state* s, iterator acc)
|
||||||
|
: s_(s), acc_(acc)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
/// Copy constructor
|
/// Copy constructor
|
||||||
state_tba_proxy(const state_tba_proxy& o)
|
state_tba_proxy(const state_tba_proxy& o)
|
||||||
: state(),
|
: state(),
|
||||||
s_(o.real_state()->clone()),
|
s_(o.real_state()->clone()),
|
||||||
acc_(o.acceptance_iterator())
|
acc_(o.acceptance_iterator())
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual
|
||||||
|
~state_tba_proxy()
|
||||||
|
{
|
||||||
|
delete s_;
|
||||||
|
}
|
||||||
|
|
||||||
|
state*
|
||||||
|
real_state() const
|
||||||
|
{
|
||||||
|
return s_;
|
||||||
|
}
|
||||||
|
|
||||||
|
bdd
|
||||||
|
acceptance_cond() const
|
||||||
|
{
|
||||||
|
return *acc_;
|
||||||
|
}
|
||||||
|
|
||||||
|
iterator
|
||||||
|
acceptance_iterator() const
|
||||||
|
{
|
||||||
|
return acc_;
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual int
|
||||||
|
compare(const state* other) const
|
||||||
|
{
|
||||||
|
const state_tba_proxy* o = dynamic_cast<const state_tba_proxy*>(other);
|
||||||
|
assert(o);
|
||||||
|
int res = s_->compare(o->real_state());
|
||||||
|
if (res != 0)
|
||||||
|
return res;
|
||||||
|
return acc_->id() - o->acceptance_cond().id();
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual size_t
|
||||||
|
hash() const
|
||||||
|
{
|
||||||
|
// We expect to have many more states than acceptance conditions.
|
||||||
|
// Hence we keep only 8 bits for acceptance conditions.
|
||||||
|
return (s_->hash() << 8) + (acc_->id() & 0xFF);
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual
|
||||||
|
state_tba_proxy* clone() const
|
||||||
|
{
|
||||||
|
return new state_tba_proxy(*this);
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
state* s_;
|
||||||
|
iterator acc_;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
/// \brief Iterate over the successors of tgba_tba_proxy computed
|
||||||
|
/// on the fly.
|
||||||
|
class tgba_tba_proxy_succ_iterator: public tgba_succ_iterator
|
||||||
{
|
{
|
||||||
}
|
typedef tgba_tba_proxy::cycle_list list;
|
||||||
|
typedef tgba_tba_proxy::cycle_list::const_iterator iterator;
|
||||||
|
public:
|
||||||
|
tgba_tba_proxy_succ_iterator(tgba_succ_iterator* it,
|
||||||
|
iterator expected, iterator end,
|
||||||
|
bdd the_acceptance_cond)
|
||||||
|
: it_(it), expected_(expected), end_(end),
|
||||||
|
the_acceptance_cond_(the_acceptance_cond)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
virtual
|
virtual
|
||||||
~state_tba_proxy()
|
~tgba_tba_proxy_succ_iterator()
|
||||||
{
|
{
|
||||||
delete s_;
|
delete it_;
|
||||||
}
|
}
|
||||||
|
|
||||||
state*
|
// iteration
|
||||||
real_state() const
|
|
||||||
{
|
|
||||||
return s_;
|
|
||||||
}
|
|
||||||
|
|
||||||
bdd
|
void
|
||||||
acceptance_cond() const
|
first()
|
||||||
{
|
{
|
||||||
return *acc_;
|
it_->first();
|
||||||
}
|
}
|
||||||
|
|
||||||
iterator
|
void
|
||||||
acceptance_iterator() const
|
next()
|
||||||
{
|
{
|
||||||
return acc_;
|
it_->next();
|
||||||
}
|
}
|
||||||
|
|
||||||
virtual int
|
bool
|
||||||
compare(const state* other) const
|
done() const
|
||||||
{
|
{
|
||||||
const state_tba_proxy* o = dynamic_cast<const state_tba_proxy*>(other);
|
return it_->done();
|
||||||
assert(o);
|
}
|
||||||
int res = s_->compare(o->real_state());
|
|
||||||
if (res != 0)
|
|
||||||
return res;
|
|
||||||
return acc_->id() - o->acceptance_cond().id();
|
|
||||||
}
|
|
||||||
|
|
||||||
virtual size_t
|
// inspection
|
||||||
hash() const
|
|
||||||
{
|
|
||||||
// We expect to have many more states than acceptance conditions.
|
|
||||||
// Hence we keep only 8 bits for acceptance conditions.
|
|
||||||
return (s_->hash() << 8) + (acc_->id() & 0xFF);
|
|
||||||
}
|
|
||||||
|
|
||||||
virtual
|
state_tba_proxy*
|
||||||
state_tba_proxy* clone() const
|
current_state() const
|
||||||
{
|
{
|
||||||
return new state_tba_proxy(*this);
|
// A transition in the *EXPECTED acceptance set should be directed
|
||||||
}
|
// to the next acceptance set. If the current transition is also
|
||||||
|
// in the next acceptance set, then go the one after, etc.
|
||||||
|
//
|
||||||
|
// See Denis Oddoux's PhD thesis for a nice explanation (in French).
|
||||||
|
// @PhDThesis{ oddoux.03.phd,
|
||||||
|
// author = {Denis Oddoux},
|
||||||
|
// title = {Utilisation des automates alternants pour un
|
||||||
|
// model-checking efficace des logiques temporelles
|
||||||
|
// lin{\'e}aires.},
|
||||||
|
// school = {Universit{\'e}e Paris 7},
|
||||||
|
// year = {2003},
|
||||||
|
// address = {Paris, France},
|
||||||
|
// month = {December}
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
iterator next = expected_;
|
||||||
|
bdd acc = it_->current_acceptance_conditions();
|
||||||
|
while ((acc & *next) == *next && next != end_)
|
||||||
|
++next;
|
||||||
|
return new state_tba_proxy(it_->current_state(), next);
|
||||||
|
}
|
||||||
|
|
||||||
private:
|
bdd
|
||||||
state* s_;
|
current_condition() const
|
||||||
iterator acc_;
|
{
|
||||||
};
|
return it_->current_condition();
|
||||||
|
}
|
||||||
|
|
||||||
|
bdd
|
||||||
|
current_acceptance_conditions() const
|
||||||
|
{
|
||||||
|
return the_acceptance_cond_;
|
||||||
|
}
|
||||||
|
|
||||||
/// \brief Iterate over the successors of tgba_tba_proxy computed on the fly.
|
protected:
|
||||||
class tgba_tba_proxy_succ_iterator: public tgba_succ_iterator
|
tgba_succ_iterator* it_;
|
||||||
{
|
const iterator expected_;
|
||||||
typedef tgba_tba_proxy::cycle_list list;
|
const iterator end_;
|
||||||
typedef tgba_tba_proxy::cycle_list::const_iterator iterator;
|
const bdd the_acceptance_cond_;
|
||||||
public:
|
};
|
||||||
tgba_tba_proxy_succ_iterator(tgba_succ_iterator* it,
|
|
||||||
iterator expected, iterator end,
|
|
||||||
bdd the_acceptance_cond)
|
|
||||||
: it_(it), expected_(expected), end_(end),
|
|
||||||
the_acceptance_cond_(the_acceptance_cond)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
virtual
|
|
||||||
~tgba_tba_proxy_succ_iterator()
|
|
||||||
{
|
|
||||||
delete it_;
|
|
||||||
}
|
|
||||||
|
|
||||||
// iteration
|
|
||||||
|
|
||||||
void
|
|
||||||
first()
|
|
||||||
{
|
|
||||||
it_->first();
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
next()
|
|
||||||
{
|
|
||||||
it_->next();
|
|
||||||
}
|
|
||||||
|
|
||||||
bool
|
|
||||||
done() const
|
|
||||||
{
|
|
||||||
return it_->done();
|
|
||||||
}
|
|
||||||
|
|
||||||
// inspection
|
|
||||||
|
|
||||||
state_tba_proxy*
|
|
||||||
current_state() const
|
|
||||||
{
|
|
||||||
// A transition in the *EXPECTED acceptance set should be directed
|
|
||||||
// to the next acceptance set. If the current transition is also
|
|
||||||
// in the next acceptance set, then go the one after, etc.
|
|
||||||
//
|
|
||||||
// See Denis Oddoux's PhD thesis for a nice explanation (in French).
|
|
||||||
// @PhDThesis{ oddoux.03.phd,
|
|
||||||
// author = {Denis Oddoux},
|
|
||||||
// title = {Utilisation des automates alternants pour un
|
|
||||||
// model-checking efficace des logiques temporelles
|
|
||||||
// lin{\'e}aires.},
|
|
||||||
// school = {Universit{\'e}e Paris 7},
|
|
||||||
// year = {2003},
|
|
||||||
// address = {Paris, France},
|
|
||||||
// month = {December}
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
iterator next = expected_;
|
|
||||||
bdd acc = it_->current_acceptance_conditions();
|
|
||||||
while ((acc & *next) == *next && next != end_)
|
|
||||||
++next;
|
|
||||||
return new state_tba_proxy(it_->current_state(), next);
|
|
||||||
}
|
|
||||||
|
|
||||||
bdd
|
|
||||||
current_condition() const
|
|
||||||
{
|
|
||||||
return it_->current_condition();
|
|
||||||
}
|
|
||||||
|
|
||||||
bdd
|
|
||||||
current_acceptance_conditions() const
|
|
||||||
{
|
|
||||||
return the_acceptance_cond_;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected:
|
|
||||||
tgba_succ_iterator* it_;
|
|
||||||
const iterator expected_;
|
|
||||||
const iterator end_;
|
|
||||||
const bdd the_acceptance_cond_;
|
|
||||||
};
|
|
||||||
|
|
||||||
|
} // anonymous
|
||||||
|
|
||||||
tgba_tba_proxy::tgba_tba_proxy(const tgba* a)
|
tgba_tba_proxy::tgba_tba_proxy(const tgba* a)
|
||||||
: a_(a)
|
: a_(a)
|
||||||
|
|
|
||||||
|
|
@ -28,51 +28,53 @@
|
||||||
|
|
||||||
namespace spot
|
namespace spot
|
||||||
{
|
{
|
||||||
|
namespace
|
||||||
class dotty_bfs : public tgba_reachable_iterator_breadth_first
|
|
||||||
{
|
{
|
||||||
public:
|
class dotty_bfs : public tgba_reachable_iterator_breadth_first
|
||||||
dotty_bfs(const tgba* a, std::ostream& os)
|
|
||||||
: tgba_reachable_iterator_breadth_first(a), os_(os)
|
|
||||||
{
|
{
|
||||||
}
|
public:
|
||||||
|
dotty_bfs(const tgba* a, std::ostream& os)
|
||||||
|
: tgba_reachable_iterator_breadth_first(a), os_(os)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
start()
|
start()
|
||||||
{
|
{
|
||||||
os_ << "digraph G {" << std::endl;
|
os_ << "digraph G {" << std::endl;
|
||||||
os_ << " 0 [label=\"\", style=invis, height=0]" << std::endl;
|
os_ << " 0 [label=\"\", style=invis, height=0]" << std::endl;
|
||||||
os_ << " 0 -> 1" << std::endl;
|
os_ << " 0 -> 1" << std::endl;
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
end()
|
end()
|
||||||
{
|
{
|
||||||
os_ << "}" << std::endl;
|
os_ << "}" << std::endl;
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
process_state(const state* s, int n, tgba_succ_iterator*)
|
process_state(const state* s, int n, tgba_succ_iterator*)
|
||||||
{
|
{
|
||||||
os_ << " " << n << " [label=\"";
|
os_ << " " << n << " [label=\"";
|
||||||
escape_str(os_, automata_->format_state(s)) << "\"]" << std::endl;
|
escape_str(os_, automata_->format_state(s)) << "\"]" << std::endl;
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
process_link(int in, int out, const tgba_succ_iterator* si)
|
process_link(int in, int out, const tgba_succ_iterator* si)
|
||||||
{
|
{
|
||||||
os_ << " " << in << " -> " << out << " [label=\"";
|
os_ << " " << in << " -> " << out << " [label=\"";
|
||||||
escape_str(os_, bdd_format_formula(automata_->get_dict(),
|
escape_str(os_, bdd_format_formula(automata_->get_dict(),
|
||||||
si->current_condition())) << "\\n";
|
si->current_condition())) << "\\n";
|
||||||
escape_str(os_,
|
escape_str(os_,
|
||||||
bdd_format_accset(automata_->get_dict(),
|
bdd_format_accset(automata_->get_dict(),
|
||||||
si->current_acceptance_conditions()))
|
si->current_acceptance_conditions()))
|
||||||
<< "\"]" << std::endl;
|
<< "\"]" << std::endl;
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
std::ostream& os_;
|
std::ostream& os_;
|
||||||
};
|
};
|
||||||
|
}
|
||||||
|
|
||||||
std::ostream&
|
std::ostream&
|
||||||
dotty_reachable(std::ostream& os, const tgba* g)
|
dotty_reachable(std::ostream& os, const tgba* g)
|
||||||
|
|
|
||||||
|
|
@ -25,73 +25,77 @@
|
||||||
#include <map>
|
#include <map>
|
||||||
#include "reachiter.hh"
|
#include "reachiter.hh"
|
||||||
|
|
||||||
namespace spot {
|
namespace spot
|
||||||
|
{
|
||||||
template <class T>
|
namespace
|
||||||
class dupexp_iter: public T
|
|
||||||
{
|
{
|
||||||
public:
|
template <class T>
|
||||||
dupexp_iter(const tgba* a)
|
class dupexp_iter: public T
|
||||||
: T(a), out_(new tgba_explicit(a->get_dict()))
|
|
||||||
{
|
{
|
||||||
}
|
public:
|
||||||
|
dupexp_iter(const tgba* a)
|
||||||
|
: T(a), out_(new tgba_explicit(a->get_dict()))
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
tgba_explicit*
|
tgba_explicit*
|
||||||
result()
|
result()
|
||||||
{
|
{
|
||||||
return out_;
|
return out_;
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
process_state(const state* s, int n, tgba_succ_iterator*)
|
process_state(const state* s, int n, tgba_succ_iterator*)
|
||||||
{
|
{
|
||||||
std::ostringstream os;
|
std::ostringstream os;
|
||||||
os << "(#" << n << ") " << this->automata_->format_state(s);
|
os << "(#" << n << ") " << this->automata_->format_state(s);
|
||||||
name_[n] = os.str();
|
name_[n] = os.str();
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string
|
std::string
|
||||||
declare_state(const state* s, int n)
|
declare_state(const state* s, int n)
|
||||||
{
|
{
|
||||||
std::string str;
|
std::string str;
|
||||||
name_map_::const_iterator i = name_.find(n);
|
name_map_::const_iterator i = name_.find(n);
|
||||||
if (i == name_.end())
|
if (i == name_.end())
|
||||||
{
|
{
|
||||||
std::ostringstream os;
|
std::ostringstream os;
|
||||||
os << "(#" << n << ") " << this->automata_->format_state(s);
|
os << "(#" << n << ") " << this->automata_->format_state(s);
|
||||||
name_[n] = str = os.str();
|
name_[n] = str = os.str();
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
str = i->second;
|
str = i->second;
|
||||||
}
|
}
|
||||||
delete s;
|
delete s;
|
||||||
return str;
|
return str;
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
process_link(int in, int out, const tgba_succ_iterator* si)
|
process_link(int in, int out, const tgba_succ_iterator* si)
|
||||||
{
|
{
|
||||||
// We might need to format out before process_state is called.
|
// We might need to format out before process_state is called.
|
||||||
name_map_::const_iterator i = name_.find(out);
|
name_map_::const_iterator i = name_.find(out);
|
||||||
if (i == name_.end())
|
if (i == name_.end())
|
||||||
{
|
{
|
||||||
const state* s = si->current_state();
|
const state* s = si->current_state();
|
||||||
process_state(s, out, 0);
|
process_state(s, out, 0);
|
||||||
delete s;
|
delete s;
|
||||||
}
|
}
|
||||||
|
|
||||||
tgba_explicit::transition* t =
|
tgba_explicit::transition* t =
|
||||||
out_->create_transition(name_[in], name_[out]);
|
out_->create_transition(name_[in], name_[out]);
|
||||||
out_->add_conditions(t, si->current_condition());
|
out_->add_conditions(t, si->current_condition());
|
||||||
out_->add_acceptance_conditions(t, si->current_acceptance_conditions());
|
out_->add_acceptance_conditions(t, si->current_acceptance_conditions());
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
tgba_explicit* out_;
|
tgba_explicit* out_;
|
||||||
typedef std::map<int, std::string> name_map_;
|
typedef std::map<int, std::string> name_map_;
|
||||||
std::map<int, std::string> name_;
|
std::map<int, std::string> name_;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
} // anonymous
|
||||||
|
|
||||||
tgba_explicit*
|
tgba_explicit*
|
||||||
tgba_dupexp_bfs(const tgba* aut)
|
tgba_dupexp_bfs(const tgba* aut)
|
||||||
|
|
|
||||||
|
|
@ -23,54 +23,57 @@
|
||||||
|
|
||||||
namespace spot
|
namespace spot
|
||||||
{
|
{
|
||||||
class numbered_state_heap_hash_map_const_iterator :
|
namespace
|
||||||
public numbered_state_heap_const_iterator
|
|
||||||
{
|
{
|
||||||
public:
|
class numbered_state_heap_hash_map_const_iterator:
|
||||||
numbered_state_heap_hash_map_const_iterator
|
public numbered_state_heap_const_iterator
|
||||||
(const numbered_state_heap_hash_map::hash_type& h)
|
|
||||||
: numbered_state_heap_const_iterator(), h(h)
|
|
||||||
{
|
{
|
||||||
}
|
public:
|
||||||
|
numbered_state_heap_hash_map_const_iterator
|
||||||
|
(const numbered_state_heap_hash_map::hash_type& h)
|
||||||
|
: numbered_state_heap_const_iterator(), h(h)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
~numbered_state_heap_hash_map_const_iterator()
|
~numbered_state_heap_hash_map_const_iterator()
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
virtual void
|
virtual void
|
||||||
first()
|
first()
|
||||||
{
|
{
|
||||||
i = h.begin();
|
i = h.begin();
|
||||||
}
|
}
|
||||||
|
|
||||||
virtual void
|
virtual void
|
||||||
next()
|
next()
|
||||||
{
|
{
|
||||||
++i;
|
++i;
|
||||||
}
|
}
|
||||||
|
|
||||||
virtual bool
|
virtual bool
|
||||||
done() const
|
done() const
|
||||||
{
|
{
|
||||||
return i == h.end();
|
return i == h.end();
|
||||||
}
|
}
|
||||||
|
|
||||||
virtual const state*
|
virtual const state*
|
||||||
get_state() const
|
get_state() const
|
||||||
{
|
{
|
||||||
return i->first;
|
return i->first;
|
||||||
}
|
}
|
||||||
|
|
||||||
virtual int
|
virtual int
|
||||||
get_index() const
|
get_index() const
|
||||||
{
|
{
|
||||||
return i->second;
|
return i->second;
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
numbered_state_heap_hash_map::hash_type::const_iterator i;
|
numbered_state_heap_hash_map::hash_type::const_iterator i;
|
||||||
const numbered_state_heap_hash_map::hash_type& h;
|
const numbered_state_heap_hash_map::hash_type& h;
|
||||||
};
|
};
|
||||||
|
} // anonymous
|
||||||
|
|
||||||
numbered_state_heap_hash_map::~numbered_state_heap_hash_map()
|
numbered_state_heap_hash_map::~numbered_state_heap_hash_map()
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -119,12 +119,11 @@ namespace spot
|
||||||
virtual int size() const;
|
virtual int size() const;
|
||||||
|
|
||||||
virtual numbered_state_heap_const_iterator* iterator() const;
|
virtual numbered_state_heap_const_iterator* iterator() const;
|
||||||
protected:
|
|
||||||
typedef Sgi::hash_map<const state*, int,
|
typedef Sgi::hash_map<const state*, int,
|
||||||
state_ptr_hash, state_ptr_equal> hash_type;
|
state_ptr_hash, state_ptr_equal> hash_type;
|
||||||
|
protected:
|
||||||
hash_type h; ///< Map of visited states.
|
hash_type h; ///< Map of visited states.
|
||||||
|
|
||||||
friend class numbered_state_heap_hash_map_const_iterator;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
/// \brief Factory for numbered_state_heap_hash_map.
|
/// \brief Factory for numbered_state_heap_hash_map.
|
||||||
|
|
|
||||||
|
|
@ -30,129 +30,132 @@
|
||||||
|
|
||||||
namespace spot
|
namespace spot
|
||||||
{
|
{
|
||||||
// At some point we'll need to print an acceptance set into LBTT's
|
namespace
|
||||||
// format. LBTT expects numbered acceptance sets, so first we'll
|
|
||||||
// number each acceptance condition, and latter when we have to print
|
|
||||||
// them we'll just have to look up each of them.
|
|
||||||
class acceptance_cond_splitter
|
|
||||||
{
|
{
|
||||||
public:
|
// At some point we'll need to print an acceptance set into LBTT's
|
||||||
acceptance_cond_splitter(bdd all_acc)
|
// format. LBTT expects numbered acceptance sets, so first we'll
|
||||||
|
// number each acceptance condition, and latter when we have to print
|
||||||
|
// them we'll just have to look up each of them.
|
||||||
|
class acceptance_cond_splitter
|
||||||
{
|
{
|
||||||
unsigned count = 0;
|
public:
|
||||||
while (all_acc != bddfalse)
|
acceptance_cond_splitter(bdd all_acc)
|
||||||
{
|
|
||||||
bdd acc = bdd_satone(all_acc);
|
|
||||||
all_acc -= acc;
|
|
||||||
sm[acc] = count++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
std::ostream&
|
|
||||||
split(std::ostream& os, bdd b)
|
|
||||||
{
|
|
||||||
while (b != bddfalse)
|
|
||||||
{
|
|
||||||
bdd acc = bdd_satone(b);
|
|
||||||
b -= acc;
|
|
||||||
os << sm[acc] << " ";
|
|
||||||
}
|
|
||||||
return os;
|
|
||||||
}
|
|
||||||
|
|
||||||
unsigned
|
|
||||||
count() const
|
|
||||||
{
|
|
||||||
return sm.size();
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
|
||||||
typedef std::map<bdd, unsigned, bdd_less_than> split_map;
|
|
||||||
split_map sm;
|
|
||||||
};
|
|
||||||
|
|
||||||
// Convert a BDD formula to the syntax used by LBTT's transition guards.
|
|
||||||
// Conjunctions are printed by bdd_format_sat, so we just have
|
|
||||||
// to handle the other cases.
|
|
||||||
static std::string
|
|
||||||
bdd_to_lbtt(bdd b, const bdd_dict* d)
|
|
||||||
{
|
|
||||||
if (b == bddfalse)
|
|
||||||
return "f";
|
|
||||||
else if (b == bddtrue)
|
|
||||||
return "t";
|
|
||||||
else
|
|
||||||
{
|
{
|
||||||
bdd cube = bdd_satone(b);
|
unsigned count = 0;
|
||||||
b -= cube;
|
while (all_acc != bddfalse)
|
||||||
if (b != bddfalse)
|
|
||||||
{
|
{
|
||||||
return "| " + bdd_to_lbtt(b, d) + " " + bdd_to_lbtt(cube, d);
|
bdd acc = bdd_satone(all_acc);
|
||||||
}
|
all_acc -= acc;
|
||||||
else
|
sm[acc] = count++;
|
||||||
{
|
|
||||||
std::string res = "";
|
|
||||||
for (int count = bdd_nodecount(cube); count > 1; --count)
|
|
||||||
res += "& ";
|
|
||||||
return res + bdd_format_sat(d, cube);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
std::ostream&
|
||||||
|
split(std::ostream& os, bdd b)
|
||||||
|
{
|
||||||
|
while (b != bddfalse)
|
||||||
|
{
|
||||||
|
bdd acc = bdd_satone(b);
|
||||||
|
b -= acc;
|
||||||
|
os << sm[acc] << " ";
|
||||||
|
}
|
||||||
|
return os;
|
||||||
|
}
|
||||||
|
|
||||||
class lbtt_bfs : public tgba_reachable_iterator_breadth_first
|
unsigned
|
||||||
{
|
count() const
|
||||||
public:
|
{
|
||||||
lbtt_bfs(const tgba* a, std::ostream& os)
|
return sm.size();
|
||||||
: tgba_reachable_iterator_breadth_first(a),
|
}
|
||||||
os_(os),
|
|
||||||
acc_count_(0),
|
|
||||||
acs_(a->all_acceptance_conditions())
|
|
||||||
|
|
||||||
|
private:
|
||||||
|
typedef std::map<bdd, unsigned, bdd_less_than> split_map;
|
||||||
|
split_map sm;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Convert a BDD formula to the syntax used by LBTT's transition guards.
|
||||||
|
// Conjunctions are printed by bdd_format_sat, so we just have
|
||||||
|
// to handle the other cases.
|
||||||
|
static std::string
|
||||||
|
bdd_to_lbtt(bdd b, const bdd_dict* d)
|
||||||
{
|
{
|
||||||
// Count the number of acceptance_conditions.
|
if (b == bddfalse)
|
||||||
bdd all = a->all_acceptance_conditions();
|
return "f";
|
||||||
while (all != bddfalse)
|
else if (b == bddtrue)
|
||||||
{
|
return "t";
|
||||||
bdd one = bdd_satone(all);
|
|
||||||
all -= one;
|
|
||||||
++acc_count_;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
process_state(const state*, int n, tgba_succ_iterator*)
|
|
||||||
{
|
|
||||||
--n;
|
|
||||||
if (n == 0)
|
|
||||||
body_ << "0 1" << std::endl;
|
|
||||||
else
|
else
|
||||||
body_ << "-1" << std::endl << n << " 0" << std::endl;
|
{
|
||||||
|
bdd cube = bdd_satone(b);
|
||||||
|
b -= cube;
|
||||||
|
if (b != bddfalse)
|
||||||
|
{
|
||||||
|
return "| " + bdd_to_lbtt(b, d) + " " + bdd_to_lbtt(cube, d);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
std::string res = "";
|
||||||
|
for (int count = bdd_nodecount(cube); count > 1; --count)
|
||||||
|
res += "& ";
|
||||||
|
return res + bdd_format_sat(d, cube);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
class lbtt_bfs : public tgba_reachable_iterator_breadth_first
|
||||||
process_link(int, int out, const tgba_succ_iterator* si)
|
|
||||||
{
|
{
|
||||||
body_ << out - 1 << " ";
|
public:
|
||||||
acs_.split(body_, si->current_acceptance_conditions());
|
lbtt_bfs(const tgba* a, std::ostream& os)
|
||||||
body_ << "-1 " << bdd_to_lbtt(si->current_condition(),
|
: tgba_reachable_iterator_breadth_first(a),
|
||||||
automata_->get_dict()) << std::endl;
|
os_(os),
|
||||||
}
|
acc_count_(0),
|
||||||
|
acs_(a->all_acceptance_conditions())
|
||||||
|
|
||||||
void
|
{
|
||||||
end()
|
// Count the number of acceptance_conditions.
|
||||||
{
|
bdd all = a->all_acceptance_conditions();
|
||||||
os_ << seen.size() << " " << acc_count_ << "t" << std::endl
|
while (all != bddfalse)
|
||||||
<< body_.str() << "-1" << std::endl;
|
{
|
||||||
}
|
bdd one = bdd_satone(all);
|
||||||
|
all -= one;
|
||||||
|
++acc_count_;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private:
|
void
|
||||||
std::ostream& os_;
|
process_state(const state*, int n, tgba_succ_iterator*)
|
||||||
std::ostringstream body_;
|
{
|
||||||
unsigned acc_count_;
|
--n;
|
||||||
acceptance_cond_splitter acs_;
|
if (n == 0)
|
||||||
};
|
body_ << "0 1" << std::endl;
|
||||||
|
else
|
||||||
|
body_ << "-1" << std::endl << n << " 0" << std::endl;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
process_link(int, int out, const tgba_succ_iterator* si)
|
||||||
|
{
|
||||||
|
body_ << out - 1 << " ";
|
||||||
|
acs_.split(body_, si->current_acceptance_conditions());
|
||||||
|
body_ << "-1 " << bdd_to_lbtt(si->current_condition(),
|
||||||
|
automata_->get_dict()) << std::endl;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
end()
|
||||||
|
{
|
||||||
|
os_ << seen.size() << " " << acc_count_ << "t" << std::endl
|
||||||
|
<< body_.str() << "-1" << std::endl;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::ostream& os_;
|
||||||
|
std::ostringstream body_;
|
||||||
|
unsigned acc_count_;
|
||||||
|
acceptance_cond_splitter acs_;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // anonymous
|
||||||
|
|
||||||
std::ostream&
|
std::ostream&
|
||||||
lbtt_reachable(std::ostream& os, const tgba* g)
|
lbtt_reachable(std::ostream& os, const tgba* g)
|
||||||
|
|
@ -161,6 +164,4 @@ namespace spot
|
||||||
b.run();
|
b.run();
|
||||||
return os;
|
return os;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -31,225 +31,228 @@
|
||||||
|
|
||||||
namespace spot
|
namespace spot
|
||||||
{
|
{
|
||||||
using namespace ltl;
|
namespace
|
||||||
|
|
||||||
/// \brief Recursively translate a formula into a BDD.
|
|
||||||
///
|
|
||||||
/// The algorithm used here is adapted from Jean-Michel Couvreur's
|
|
||||||
/// Probataf tool.
|
|
||||||
class ltl_trad_visitor: public const_visitor
|
|
||||||
{
|
{
|
||||||
public:
|
using namespace ltl;
|
||||||
ltl_trad_visitor(tgba_bdd_concrete_factory& fact, bool root = false)
|
|
||||||
: fact_(fact), root_(root)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
virtual
|
/// \brief Recursively translate a formula into a BDD.
|
||||||
~ltl_trad_visitor()
|
///
|
||||||
|
/// The algorithm used here is adapted from Jean-Michel Couvreur's
|
||||||
|
/// Probataf tool.
|
||||||
|
class ltl_trad_visitor: public const_visitor
|
||||||
{
|
{
|
||||||
}
|
public:
|
||||||
|
ltl_trad_visitor(tgba_bdd_concrete_factory& fact, bool root = false)
|
||||||
|
: fact_(fact), root_(root)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
bdd
|
virtual
|
||||||
result()
|
~ltl_trad_visitor()
|
||||||
{
|
{
|
||||||
return res_;
|
}
|
||||||
}
|
|
||||||
|
|
||||||
void
|
bdd
|
||||||
visit(const atomic_prop* node)
|
result()
|
||||||
{
|
{
|
||||||
res_ = bdd_ithvar(fact_.create_atomic_prop(node));
|
return res_;
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
visit(const constant* node)
|
visit(const atomic_prop* node)
|
||||||
{
|
{
|
||||||
switch (node->val())
|
res_ = bdd_ithvar(fact_.create_atomic_prop(node));
|
||||||
{
|
}
|
||||||
case constant::True:
|
|
||||||
res_ = bddtrue;
|
|
||||||
return;
|
|
||||||
case constant::False:
|
|
||||||
res_ = bddfalse;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
/* Unreachable code. */
|
|
||||||
assert(0);
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
void
|
||||||
visit(const unop* node)
|
visit(const constant* node)
|
||||||
{
|
{
|
||||||
switch (node->op())
|
switch (node->val())
|
||||||
{
|
|
||||||
case unop::F:
|
|
||||||
{
|
{
|
||||||
/*
|
case constant::True:
|
||||||
Fx <=> x | XFx
|
res_ = bddtrue;
|
||||||
In other words:
|
return;
|
||||||
now <=> x | next
|
case constant::False:
|
||||||
*/
|
res_ = bddfalse;
|
||||||
int v = fact_.create_state(node);
|
|
||||||
bdd now = bdd_ithvar(v);
|
|
||||||
bdd next = bdd_ithvar(v + 1);
|
|
||||||
bdd x = recurse(node->child());
|
|
||||||
fact_.constrain_relation(bdd_apply(now, x | next, bddop_biimp));
|
|
||||||
/*
|
|
||||||
`x | next', doesn't actually encode the fact that x
|
|
||||||
should be fulfilled eventually. We ensure this by
|
|
||||||
creating a new generalized Büchi acceptance set, Acc[x],
|
|
||||||
and leave out of this set any transition going off NOW
|
|
||||||
without checking X. Such acceptance conditions are
|
|
||||||
checked for during the emptiness check.
|
|
||||||
*/
|
|
||||||
fact_.declare_acceptance_condition(x | !now, node->child());
|
|
||||||
res_ = now;
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
case unop::G:
|
/* Unreachable code. */
|
||||||
{
|
assert(0);
|
||||||
bdd child = recurse(node->child());
|
}
|
||||||
// If G occurs at the top of the formula we don't
|
|
||||||
// need Now/Next variables. We just constrain
|
|
||||||
// the relation so that the child always happens.
|
|
||||||
// This saves 2 BDD variables.
|
|
||||||
if (root_)
|
|
||||||
{
|
|
||||||
fact_.constrain_relation(child);
|
|
||||||
res_ = child;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
// Gx <=> x && XGx
|
|
||||||
int v = fact_.create_state(node);
|
|
||||||
bdd now = bdd_ithvar(v);
|
|
||||||
bdd next = bdd_ithvar(v + 1);
|
|
||||||
fact_.constrain_relation(bdd_apply(now, child & next,
|
|
||||||
bddop_biimp));
|
|
||||||
res_ = now;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
case unop::Not:
|
|
||||||
{
|
|
||||||
res_ = bdd_not(recurse(node->child()));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
case unop::X:
|
|
||||||
{
|
|
||||||
int v = fact_.create_state(node->child());
|
|
||||||
bdd now = bdd_ithvar(v);
|
|
||||||
bdd next = bdd_ithvar(v + 1);
|
|
||||||
fact_.constrain_relation(bdd_apply(now, recurse(node->child()),
|
|
||||||
bddop_biimp));
|
|
||||||
res_ = next;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
/* Unreachable code. */
|
|
||||||
assert(0);
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
void
|
||||||
visit(const binop* node)
|
visit(const unop* node)
|
||||||
{
|
{
|
||||||
bdd f1 = recurse(node->first());
|
switch (node->op())
|
||||||
bdd f2 = recurse(node->second());
|
|
||||||
|
|
||||||
switch (node->op())
|
|
||||||
{
|
|
||||||
case binop::Xor:
|
|
||||||
res_ = bdd_apply(f1, f2, bddop_xor);
|
|
||||||
return;
|
|
||||||
case binop::Implies:
|
|
||||||
res_ = bdd_apply(f1, f2, bddop_imp);
|
|
||||||
return;
|
|
||||||
case binop::Equiv:
|
|
||||||
res_ = bdd_apply(f1, f2, bddop_biimp);
|
|
||||||
return;
|
|
||||||
case binop::U:
|
|
||||||
{
|
{
|
||||||
/*
|
case unop::F:
|
||||||
f1 U f2 <=> f2 | (f1 & X(f1 U f2))
|
{
|
||||||
In other words:
|
/*
|
||||||
now <=> f2 | (f1 & next)
|
Fx <=> x | XFx
|
||||||
*/
|
In other words:
|
||||||
int v = fact_.create_state(node);
|
now <=> x | next
|
||||||
bdd now = bdd_ithvar(v);
|
*/
|
||||||
bdd next = bdd_ithvar(v + 1);
|
int v = fact_.create_state(node);
|
||||||
fact_.constrain_relation(bdd_apply(now, f2 | (f1 & next),
|
bdd now = bdd_ithvar(v);
|
||||||
bddop_biimp));
|
bdd next = bdd_ithvar(v + 1);
|
||||||
/*
|
bdd x = recurse(node->child());
|
||||||
The rightmost conjunction, f1 & next, doesn't actually
|
fact_.constrain_relation(bdd_apply(now, x | next, bddop_biimp));
|
||||||
encode the fact that f2 should be fulfilled eventually.
|
/*
|
||||||
We declare an acceptance condition for this purpose (see
|
`x | next', doesn't actually encode the fact that x
|
||||||
the comment in the unop::F case).
|
should be fulfilled eventually. We ensure this by
|
||||||
*/
|
creating a new generalized Büchi acceptance set, Acc[x],
|
||||||
fact_.declare_acceptance_condition(f2 | !now, node->second());
|
and leave out of this set any transition going off NOW
|
||||||
res_ = now;
|
without checking X. Such acceptance conditions are
|
||||||
return;
|
checked for during the emptiness check.
|
||||||
|
*/
|
||||||
|
fact_.declare_acceptance_condition(x | !now, node->child());
|
||||||
|
res_ = now;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
case unop::G:
|
||||||
|
{
|
||||||
|
bdd child = recurse(node->child());
|
||||||
|
// If G occurs at the top of the formula we don't
|
||||||
|
// need Now/Next variables. We just constrain
|
||||||
|
// the relation so that the child always happens.
|
||||||
|
// This saves 2 BDD variables.
|
||||||
|
if (root_)
|
||||||
|
{
|
||||||
|
fact_.constrain_relation(child);
|
||||||
|
res_ = child;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// Gx <=> x && XGx
|
||||||
|
int v = fact_.create_state(node);
|
||||||
|
bdd now = bdd_ithvar(v);
|
||||||
|
bdd next = bdd_ithvar(v + 1);
|
||||||
|
fact_.constrain_relation(bdd_apply(now, child & next,
|
||||||
|
bddop_biimp));
|
||||||
|
res_ = now;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
case unop::Not:
|
||||||
|
{
|
||||||
|
res_ = bdd_not(recurse(node->child()));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
case unop::X:
|
||||||
|
{
|
||||||
|
int v = fact_.create_state(node->child());
|
||||||
|
bdd now = bdd_ithvar(v);
|
||||||
|
bdd next = bdd_ithvar(v + 1);
|
||||||
|
fact_.constrain_relation(bdd_apply(now, recurse(node->child()),
|
||||||
|
bddop_biimp));
|
||||||
|
res_ = next;
|
||||||
|
return;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
case binop::R:
|
/* Unreachable code. */
|
||||||
|
assert(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
visit(const binop* node)
|
||||||
|
{
|
||||||
|
bdd f1 = recurse(node->first());
|
||||||
|
bdd f2 = recurse(node->second());
|
||||||
|
|
||||||
|
switch (node->op())
|
||||||
{
|
{
|
||||||
/*
|
case binop::Xor:
|
||||||
f1 R f2 <=> f2 & (f1 | X(f1 R f2))
|
res_ = bdd_apply(f1, f2, bddop_xor);
|
||||||
In other words:
|
|
||||||
now <=> f2 & (f1 | next)
|
|
||||||
*/
|
|
||||||
int v = fact_.create_state(node);
|
|
||||||
bdd now = bdd_ithvar(v);
|
|
||||||
bdd next = bdd_ithvar(v + 1);
|
|
||||||
fact_.constrain_relation(bdd_apply(now, f2 & (f1 | next),
|
|
||||||
bddop_biimp));
|
|
||||||
res_ = now;
|
|
||||||
return;
|
return;
|
||||||
|
case binop::Implies:
|
||||||
|
res_ = bdd_apply(f1, f2, bddop_imp);
|
||||||
|
return;
|
||||||
|
case binop::Equiv:
|
||||||
|
res_ = bdd_apply(f1, f2, bddop_biimp);
|
||||||
|
return;
|
||||||
|
case binop::U:
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
f1 U f2 <=> f2 | (f1 & X(f1 U f2))
|
||||||
|
In other words:
|
||||||
|
now <=> f2 | (f1 & next)
|
||||||
|
*/
|
||||||
|
int v = fact_.create_state(node);
|
||||||
|
bdd now = bdd_ithvar(v);
|
||||||
|
bdd next = bdd_ithvar(v + 1);
|
||||||
|
fact_.constrain_relation(bdd_apply(now, f2 | (f1 & next),
|
||||||
|
bddop_biimp));
|
||||||
|
/*
|
||||||
|
The rightmost conjunction, f1 & next, doesn't actually
|
||||||
|
encode the fact that f2 should be fulfilled eventually.
|
||||||
|
We declare an acceptance condition for this purpose (see
|
||||||
|
the comment in the unop::F case).
|
||||||
|
*/
|
||||||
|
fact_.declare_acceptance_condition(f2 | !now, node->second());
|
||||||
|
res_ = now;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
case binop::R:
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
f1 R f2 <=> f2 & (f1 | X(f1 R f2))
|
||||||
|
In other words:
|
||||||
|
now <=> f2 & (f1 | next)
|
||||||
|
*/
|
||||||
|
int v = fact_.create_state(node);
|
||||||
|
bdd now = bdd_ithvar(v);
|
||||||
|
bdd next = bdd_ithvar(v + 1);
|
||||||
|
fact_.constrain_relation(bdd_apply(now, f2 & (f1 | next),
|
||||||
|
bddop_biimp));
|
||||||
|
res_ = now;
|
||||||
|
return;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
/* Unreachable code. */
|
||||||
/* Unreachable code. */
|
assert(0);
|
||||||
assert(0);
|
}
|
||||||
}
|
|
||||||
|
|
||||||
void
|
void
|
||||||
visit(const multop* node)
|
visit(const multop* node)
|
||||||
{
|
{
|
||||||
int op = -1;
|
int op = -1;
|
||||||
bool root = false;
|
bool root = false;
|
||||||
switch (node->op())
|
switch (node->op())
|
||||||
{
|
{
|
||||||
case multop::And:
|
case multop::And:
|
||||||
op = bddop_and;
|
op = bddop_and;
|
||||||
res_ = bddtrue;
|
res_ = bddtrue;
|
||||||
// When the root formula is a conjunction it's ok to
|
// When the root formula is a conjunction it's ok to
|
||||||
// consider all children as root formulae. This allows the
|
// consider all children as root formulae. This allows the
|
||||||
// root-G trick to save many more variable. (See the
|
// root-G trick to save many more variable. (See the
|
||||||
// translation of G.)
|
// translation of G.)
|
||||||
root = root_;
|
root = root_;
|
||||||
break;
|
break;
|
||||||
case multop::Or:
|
case multop::Or:
|
||||||
op = bddop_or;
|
op = bddop_or;
|
||||||
res_ = bddfalse;
|
res_ = bddfalse;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
assert(op != -1);
|
assert(op != -1);
|
||||||
unsigned s = node->size();
|
unsigned s = node->size();
|
||||||
for (unsigned n = 0; n < s; ++n)
|
for (unsigned n = 0; n < s; ++n)
|
||||||
{
|
{
|
||||||
res_ = bdd_apply(res_, recurse(node->nth(n), root), op);
|
res_ = bdd_apply(res_, recurse(node->nth(n), root), op);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bdd
|
bdd
|
||||||
recurse(const formula* f, bool root = false)
|
recurse(const formula* f, bool root = false)
|
||||||
{
|
{
|
||||||
ltl_trad_visitor v(fact_, root);
|
ltl_trad_visitor v(fact_, root);
|
||||||
f->accept(v);
|
f->accept(v);
|
||||||
return v.result();
|
return v.result();
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
bdd res_;
|
bdd res_;
|
||||||
tgba_bdd_concrete_factory& fact_;
|
tgba_bdd_concrete_factory& fact_;
|
||||||
bool root_;
|
bool root_;
|
||||||
};
|
};
|
||||||
|
} // anonymous
|
||||||
|
|
||||||
tgba_bdd_concrete*
|
tgba_bdd_concrete*
|
||||||
ltl_to_tgba_lacim(const ltl::formula* f, bdd_dict* dict)
|
ltl_to_tgba_lacim(const ltl::formula* f, bdd_dict* dict)
|
||||||
|
|
|
||||||
|
|
@ -32,153 +32,155 @@
|
||||||
|
|
||||||
namespace spot
|
namespace spot
|
||||||
{
|
{
|
||||||
|
namespace
|
||||||
class never_claim_bfs : public tgba_reachable_iterator_breadth_first
|
|
||||||
{
|
{
|
||||||
public:
|
class never_claim_bfs : public tgba_reachable_iterator_breadth_first
|
||||||
never_claim_bfs(const tgba_tba_proxy* a, std::ostream& os,
|
|
||||||
const ltl::formula* f)
|
|
||||||
: tgba_reachable_iterator_breadth_first(a),
|
|
||||||
os_(os), f_(f), accept_all_(-1), fi_needed_(false)
|
|
||||||
{
|
{
|
||||||
}
|
public:
|
||||||
|
never_claim_bfs(const tgba_tba_proxy* a, std::ostream& os,
|
||||||
|
const ltl::formula* f)
|
||||||
|
: tgba_reachable_iterator_breadth_first(a),
|
||||||
|
os_(os), f_(f), accept_all_(-1), fi_needed_(false)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
start()
|
start()
|
||||||
{
|
{
|
||||||
os_ << "never {";
|
os_ << "never {";
|
||||||
if (f_)
|
if (f_)
|
||||||
{
|
{
|
||||||
os_ << " /* ";
|
os_ << " /* ";
|
||||||
to_string(f_, os_);
|
to_string(f_, os_);
|
||||||
os_ << " */";
|
os_ << " */";
|
||||||
}
|
}
|
||||||
os_ << std::endl;
|
os_ << std::endl;
|
||||||
init_ = automata_->get_init_state();
|
init_ = automata_->get_init_state();
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
end()
|
end()
|
||||||
{
|
{
|
||||||
if (fi_needed_)
|
if (fi_needed_)
|
||||||
os_ << " fi;" << std::endl;
|
os_ << " fi;" << std::endl;
|
||||||
if (accept_all_ != -1)
|
if (accept_all_ != -1)
|
||||||
{
|
{
|
||||||
os_ << "accept_all:" << std::endl;
|
os_ << "accept_all:" << std::endl;
|
||||||
os_ << " skip" << std::endl;
|
os_ << " skip" << std::endl;
|
||||||
}
|
}
|
||||||
os_ << "}" << std::endl;
|
os_ << "}" << std::endl;
|
||||||
delete init_;
|
delete init_;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool
|
bool
|
||||||
state_is_accepting(const state *s)
|
state_is_accepting(const state *s)
|
||||||
{
|
{
|
||||||
return
|
return
|
||||||
dynamic_cast<const tgba_tba_proxy*>(automata_)->state_is_accepting(s);
|
dynamic_cast<const tgba_tba_proxy*>(automata_)->state_is_accepting(s);
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string
|
std::string
|
||||||
get_state_label(const state* s, int n)
|
get_state_label(const state* s, int n)
|
||||||
{
|
{
|
||||||
std::string label;
|
std::string label;
|
||||||
if (s->compare(init_) == 0)
|
if (s->compare(init_) == 0)
|
||||||
if (state_is_accepting(s))
|
if (state_is_accepting(s))
|
||||||
label = "accept_init";
|
label = "accept_init";
|
||||||
else
|
else
|
||||||
label = "T0_init";
|
label = "T0_init";
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
std::ostringstream ost;
|
std::ostringstream ost;
|
||||||
ost << n;
|
ost << n;
|
||||||
std::string ns(ost.str());
|
std::string ns(ost.str());
|
||||||
|
|
||||||
if (state_is_accepting(s))
|
if (state_is_accepting(s))
|
||||||
{
|
{
|
||||||
tgba_succ_iterator* it = automata_->succ_iter(s);
|
tgba_succ_iterator* it = automata_->succ_iter(s);
|
||||||
it->first();
|
it->first();
|
||||||
if (it->done())
|
if (it->done())
|
||||||
label = "accept_S" + ns;
|
label = "accept_S" + ns;
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
state* current = it->current_state();
|
state* current = it->current_state();
|
||||||
if (it->current_condition() != bddtrue
|
if (it->current_condition() != bddtrue
|
||||||
|| s->compare(current) != 0)
|
|| s->compare(current) != 0)
|
||||||
label = "accept_S" + ns;
|
label = "accept_S" + ns;
|
||||||
else
|
else
|
||||||
label = "accept_all";
|
label = "accept_all";
|
||||||
delete current;
|
delete current;
|
||||||
}
|
}
|
||||||
delete it;
|
delete it;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
label = "T0_S" + ns;
|
label = "T0_S" + ns;
|
||||||
}
|
}
|
||||||
return label;
|
return label;
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
process_state(const state* s, int n, tgba_succ_iterator*)
|
process_state(const state* s, int n, tgba_succ_iterator*)
|
||||||
{
|
{
|
||||||
tgba_succ_iterator* it = automata_->succ_iter(s);
|
tgba_succ_iterator* it = automata_->succ_iter(s);
|
||||||
it->first();
|
it->first();
|
||||||
if (it->done())
|
if (it->done())
|
||||||
{
|
{
|
||||||
if (fi_needed_ != 0)
|
if (fi_needed_ != 0)
|
||||||
os_ << " fi;" << std::endl;
|
os_ << " fi;" << std::endl;
|
||||||
os_ << get_state_label(s, n) << ": ";
|
os_ << get_state_label(s, n) << ": ";
|
||||||
os_ << "/* " << automata_->format_state(s) << " */";
|
os_ << "/* " << automata_->format_state(s) << " */";
|
||||||
os_ << std::endl;
|
os_ << std::endl;
|
||||||
os_ << " if" << std::endl;
|
os_ << " if" << std::endl;
|
||||||
os_ << " :: (0) -> goto " << get_state_label(s, n) << std::endl;
|
os_ << " :: (0) -> goto " << get_state_label(s, n) << std::endl;
|
||||||
fi_needed_ = true;
|
fi_needed_ = true;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
state* current =it->current_state();
|
state* current =it->current_state();
|
||||||
if (state_is_accepting(s)
|
if (state_is_accepting(s)
|
||||||
&& it->current_condition() == bddtrue
|
&& it->current_condition() == bddtrue
|
||||||
&& s->compare(init_) != 0
|
&& s->compare(init_) != 0
|
||||||
&& s->compare(current) == 0)
|
&& s->compare(current) == 0)
|
||||||
accept_all_ = n;
|
accept_all_ = n;
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
if (fi_needed_)
|
if (fi_needed_)
|
||||||
os_ << " fi;" << std::endl;
|
os_ << " fi;" << std::endl;
|
||||||
os_ << get_state_label(s, n) << ": ";
|
os_ << get_state_label(s, n) << ": ";
|
||||||
os_ << "/* " << automata_->format_state(s) << " */";
|
os_ << "/* " << automata_->format_state(s) << " */";
|
||||||
os_ << std::endl;
|
os_ << std::endl;
|
||||||
os_ << " if" << std::endl;
|
os_ << " if" << std::endl;
|
||||||
fi_needed_ = true;
|
fi_needed_ = true;
|
||||||
}
|
}
|
||||||
delete current;
|
delete current;
|
||||||
}
|
}
|
||||||
delete it;
|
delete it;
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
process_link(int in, int out, const tgba_succ_iterator* si)
|
process_link(int in, int out, const tgba_succ_iterator* si)
|
||||||
{
|
{
|
||||||
if (in != accept_all_)
|
if (in != accept_all_)
|
||||||
{
|
{
|
||||||
os_ << " :: (";
|
os_ << " :: (";
|
||||||
const ltl::formula* f = bdd_to_formula(si->current_condition(),
|
const ltl::formula* f = bdd_to_formula(si->current_condition(),
|
||||||
automata_->get_dict());
|
automata_->get_dict());
|
||||||
to_spin_string(f, os_);
|
to_spin_string(f, os_);
|
||||||
destroy(f);
|
destroy(f);
|
||||||
state* current = si->current_state();
|
state* current = si->current_state();
|
||||||
os_ << ") -> goto " << get_state_label(current, out) << std::endl;
|
os_ << ") -> goto " << get_state_label(current, out) << std::endl;
|
||||||
delete current;
|
delete current;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
std::ostream& os_;
|
std::ostream& os_;
|
||||||
const ltl::formula* f_;
|
const ltl::formula* f_;
|
||||||
int accept_all_;
|
int accept_all_;
|
||||||
bool fi_needed_;
|
bool fi_needed_;
|
||||||
state* init_;
|
state* init_;
|
||||||
};
|
};
|
||||||
|
} // anonymous
|
||||||
|
|
||||||
std::ostream&
|
std::ostream&
|
||||||
never_claim_reachable(std::ostream& os, const tgba_tba_proxy* g,
|
never_claim_reachable(std::ostream& os, const tgba_tba_proxy* g,
|
||||||
|
|
|
||||||
|
|
@ -28,72 +28,73 @@
|
||||||
|
|
||||||
namespace spot
|
namespace spot
|
||||||
{
|
{
|
||||||
|
namespace
|
||||||
class save_bfs : public tgba_reachable_iterator_breadth_first
|
|
||||||
{
|
{
|
||||||
public:
|
class save_bfs: public tgba_reachable_iterator_breadth_first
|
||||||
save_bfs(const tgba* a, std::ostream& os)
|
|
||||||
: tgba_reachable_iterator_breadth_first(a), os_(os)
|
|
||||||
{
|
{
|
||||||
}
|
public:
|
||||||
|
save_bfs(const tgba* a, std::ostream& os)
|
||||||
|
: tgba_reachable_iterator_breadth_first(a), os_(os)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
start()
|
start()
|
||||||
{
|
{
|
||||||
os_ << "acc =";
|
os_ << "acc =";
|
||||||
print_acc(automata_->all_acceptance_conditions()) << ";" << std::endl;
|
print_acc(automata_->all_acceptance_conditions()) << ";" << std::endl;
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
process_state(const state* s, int, tgba_succ_iterator* si)
|
process_state(const state* s, int, tgba_succ_iterator* si)
|
||||||
{
|
{
|
||||||
const bdd_dict* d = automata_->get_dict();
|
const bdd_dict* d = automata_->get_dict();
|
||||||
std::string cur = automata_->format_state(s);
|
std::string cur = automata_->format_state(s);
|
||||||
for (si->first(); !si->done(); si->next())
|
for (si->first(); !si->done(); si->next())
|
||||||
{
|
{
|
||||||
state* dest = si->current_state();
|
state* dest = si->current_state();
|
||||||
os_ << "\"" << cur << "\", \""
|
os_ << "\"" << cur << "\", \""
|
||||||
<< automata_->format_state(dest) << "\", \"";
|
<< automata_->format_state(dest) << "\", \"";
|
||||||
escape_str(os_, bdd_format_formula(d, si->current_condition()));
|
escape_str(os_, bdd_format_formula(d, si->current_condition()));
|
||||||
os_ << "\",";
|
os_ << "\",";
|
||||||
print_acc(si->current_acceptance_conditions()) << ";" << std::endl;
|
print_acc(si->current_acceptance_conditions()) << ";" << std::endl;
|
||||||
delete dest;
|
delete dest;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
std::ostream& os_;
|
std::ostream& os_;
|
||||||
|
|
||||||
std::ostream&
|
|
||||||
print_acc(bdd acc)
|
|
||||||
{
|
|
||||||
const bdd_dict* d = automata_->get_dict();
|
|
||||||
while (acc != bddfalse)
|
|
||||||
{
|
|
||||||
bdd cube = bdd_satone(acc);
|
|
||||||
acc -= cube;
|
|
||||||
while (cube != bddtrue)
|
|
||||||
{
|
|
||||||
assert(cube != bddfalse);
|
|
||||||
// Display the first variable that is positive.
|
|
||||||
// There should be only one per satisfaction.
|
|
||||||
if (bdd_high(cube) != bddfalse)
|
|
||||||
{
|
|
||||||
int v = bdd_var(cube);
|
|
||||||
bdd_dict::vf_map::const_iterator vi =
|
|
||||||
d->acc_formula_map.find(v);
|
|
||||||
assert(vi != d->acc_formula_map.end());
|
|
||||||
os_ << " \"";
|
|
||||||
escape_str(os_, ltl::to_string(vi->second)) << "\"";
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
cube = bdd_low(cube);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return os_;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
|
std::ostream&
|
||||||
|
print_acc(bdd acc)
|
||||||
|
{
|
||||||
|
const bdd_dict* d = automata_->get_dict();
|
||||||
|
while (acc != bddfalse)
|
||||||
|
{
|
||||||
|
bdd cube = bdd_satone(acc);
|
||||||
|
acc -= cube;
|
||||||
|
while (cube != bddtrue)
|
||||||
|
{
|
||||||
|
assert(cube != bddfalse);
|
||||||
|
// Display the first variable that is positive.
|
||||||
|
// There should be only one per satisfaction.
|
||||||
|
if (bdd_high(cube) != bddfalse)
|
||||||
|
{
|
||||||
|
int v = bdd_var(cube);
|
||||||
|
bdd_dict::vf_map::const_iterator vi =
|
||||||
|
d->acc_formula_map.find(v);
|
||||||
|
assert(vi != d->acc_formula_map.end());
|
||||||
|
os_ << " \"";
|
||||||
|
escape_str(os_, ltl::to_string(vi->second)) << "\"";
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
cube = bdd_low(cube);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return os_;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
std::ostream&
|
std::ostream&
|
||||||
tgba_save_reachable(std::ostream& os, const tgba* g)
|
tgba_save_reachable(std::ostream& os, const tgba* g)
|
||||||
|
|
|
||||||
|
|
@ -25,30 +25,32 @@
|
||||||
|
|
||||||
namespace spot
|
namespace spot
|
||||||
{
|
{
|
||||||
|
namespace
|
||||||
class stats_bfs : public tgba_reachable_iterator_breadth_first
|
|
||||||
{
|
{
|
||||||
public:
|
class stats_bfs: public tgba_reachable_iterator_breadth_first
|
||||||
stats_bfs(const tgba* a, tgba_statistics& s)
|
|
||||||
: tgba_reachable_iterator_breadth_first(a), s_(s)
|
|
||||||
{
|
{
|
||||||
}
|
public:
|
||||||
|
stats_bfs(const tgba* a, tgba_statistics& s)
|
||||||
|
: tgba_reachable_iterator_breadth_first(a), s_(s)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
process_state(const state*, int, tgba_succ_iterator*)
|
process_state(const state*, int, tgba_succ_iterator*)
|
||||||
{
|
{
|
||||||
++s_.states;
|
++s_.states;
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
process_link(int, int, const tgba_succ_iterator*)
|
process_link(int, int, const tgba_succ_iterator*)
|
||||||
{
|
{
|
||||||
++s_.transitions;
|
++s_.transitions;
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
tgba_statistics& s_;
|
tgba_statistics& s_;
|
||||||
};
|
};
|
||||||
|
} // anonymous
|
||||||
|
|
||||||
tgba_statistics
|
tgba_statistics
|
||||||
stats_reachable(const tgba* g)
|
stats_reachable(const tgba* g)
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue