set_printer = nullptr) const;
/// \brief Construct an acc_code from a string.
///
/// The string can follow the following grammar:
///
///
/// acc ::= "t"
/// | "f"
/// | "Inf" "(" num ")"
/// | "Fin" "(" num ")"
/// | "(" acc ")"
/// | acc "&" acc
/// | acc "|" acc
///
///
/// Where num is an integer and "&" has priority over "|". Note that
/// "Fin(!x)" and "Inf(!x)" are not supported by this parser.
///
/// Or the string can be the name of an acceptance condition, as
/// speficied in the HOA format. (E.g. "Rabin 2", "parity max odd 3",
/// "generalized-Rabin 4 2 1", etc.).
///
/// A spot::parse_error is thrown on syntax error.
acc_code(const char* input);
/// \brief Build an empty acceptance condition.
///
/// This is the same as t().
acc_code()
{
}
// Calls to_text
SPOT_API
friend std::ostream& operator<<(std::ostream& os, const acc_code& code);
};
acc_cond(unsigned n_sets = 0, const acc_code& code = {})
: num_(0U), all_(0U), code_(code)
{
add_sets(n_sets);
uses_fin_acceptance_ = check_fin_acceptance();
}
acc_cond(const acc_code& code)
: num_(0U), all_(0U), code_(code)
{
add_sets(code.used_sets().max_set());
uses_fin_acceptance_ = check_fin_acceptance();
}
acc_cond(const acc_cond& o)
: num_(o.num_), all_(o.all_), code_(o.code_),
uses_fin_acceptance_(o.uses_fin_acceptance_)
{
}
acc_cond& operator=(const acc_cond& o)
{
num_ = o.num_;
all_ = o.all_;
code_ = o.code_;
uses_fin_acceptance_ = o.uses_fin_acceptance_;
return *this;
}
~acc_cond()
{
}
void set_acceptance(const acc_code& code)
{
code_ = code;
uses_fin_acceptance_ = check_fin_acceptance();
}
const acc_code& get_acceptance() const
{
return code_;
}
acc_code& get_acceptance()
{
return code_;
}
bool uses_fin_acceptance() const
{
return uses_fin_acceptance_;
}
bool is_t() const
{
return code_.is_t();
}
bool is_all() const
{
return num_ == 0 && is_t();
}
bool is_f() const
{
return code_.is_f();
}
bool is_none() const
{
return num_ == 0 && is_f();
}
bool is_buchi() const
{
unsigned s = code_.size();
return num_ == 1 &&
s == 2 && code_[1].sub.op == acc_op::Inf && code_[0].mark == all_sets();
}
bool is_co_buchi() const
{
return num_ == 1 && is_generalized_co_buchi();
}
void set_generalized_buchi()
{
set_acceptance(inf(all_sets()));
}
bool is_generalized_buchi() const
{
unsigned s = code_.size();
return (s == 0 && num_ == 0) || (s == 2 && code_[1].sub.op == acc_op::Inf
&& code_[0].mark == all_sets());
}
bool is_generalized_co_buchi() const
{
unsigned s = code_.size();
return (s == 2 &&
code_[1].sub.op == acc_op::Fin && code_[0].mark == all_sets());
}
// Returns a number of pairs (>=0) if Rabin, or -1 else.
int is_rabin() const;
// Returns a number of pairs (>=0) if Streett, or -1 else.
int is_streett() const;
/// \brief Rabin/streett pairs used by is_rabin_like and is_streett_like.
///
/// These pairs hold two marks which can each contain one or no set.
///
/// For instance is_streett_like() rewrites Inf(0)&(Inf(2)|Fin(1))&Fin(3)
/// as three pairs: [(fin={},inf={0}),(fin={1},inf={2}),(fin={3},inf={})].
///
/// Empty marks should be interpreted in a way that makes them
/// false in Streett, and true in Rabin.
struct SPOT_API rs_pair
{
rs_pair() = default;
rs_pair(const rs_pair&) = default;
rs_pair(acc_cond::mark_t fin, acc_cond::mark_t inf):
fin(fin),
inf(inf)
{}
acc_cond::mark_t fin;
acc_cond::mark_t inf;
bool operator==(rs_pair o) const
{
return fin == o.fin && inf == o.inf;
}
bool operator!=(rs_pair o) const
{
return fin != o.fin || inf != o.inf;
}
bool operator<(rs_pair o) const
{
return fin < o.fin || (!(o.fin < fin) && inf < o.inf);
}
bool operator<=(rs_pair o) const
{
return !(o < *this);
}
bool operator>(rs_pair o) const
{
return o < *this;
}
bool operator>=(rs_pair o) const
{
return !(*this < o);
}
};
/// \brief Test whether an acceptance condition is Streett-like
/// and returns each Streett pair in an std::vector.
///
/// An acceptance condition is Streett-like if it can be transformed into
/// a Streett acceptance with little modification to its automaton.
/// A Streett-like acceptance condition follows one of those rules:
/// -It is a conjunction of disjunctive clauses containing at most one
/// Inf and at most one Fin.
/// -It is true (with 0 pair)
/// -It is false (1 pair [0U, 0U])
bool is_streett_like(std::vector& pairs) const;
/// \brief Test whether an acceptance condition is Rabin-like
/// and returns each Rabin pair in an std::vector.
///
/// An acceptance condition is Rabin-like if it can be transformed into
/// a Rabin acceptance with little modification to its automaton.
/// A Rabin-like acceptance condition follows one of those rules:
/// -It is a disjunction of conjunctive clauses containing at most one
/// Inf and at most one Fin.
/// -It is true (1 pair [0U, 0U])
/// -It is false (0 pairs)
bool is_rabin_like(std::vector& pairs) const;
// Return the number of Inf in each pair.
bool is_generalized_rabin(std::vector& pairs) const;
// If EQUIV is false, this return true iff the acceptance
// condition is a parity condition written in the canonical way
// given in the HOA specifications. If EQUIV is true, then we
// check whether the condition is logically equivalent to some
// parity acceptance condition.
bool is_parity(bool& max, bool& odd, bool equiv = false) const;
bool is_parity() const
{
bool max;
bool odd;
return is_parity(max, odd);
}
// Return (true, m) if there exist some acceptance mark m that
// does not satisfy the acceptance condition. Return (false, 0U)
// otherwise.
std::pair unsat_mark() const;
protected:
bool check_fin_acceptance() const;
public:
static acc_code inf(mark_t mark)
{
return acc_code::inf(mark);
}
static acc_code inf(std::initializer_list vals)
{
return inf(mark_t(vals.begin(), vals.end()));
}
static acc_code inf_neg(mark_t mark)
{
return acc_code::inf_neg(mark);
}
static acc_code inf_neg(std::initializer_list vals)
{
return inf_neg(mark_t(vals.begin(), vals.end()));
}
static acc_code fin(mark_t mark)
{
return acc_code::fin(mark);
}
static acc_code fin(std::initializer_list vals)
{
return fin(mark_t(vals.begin(), vals.end()));
}
static acc_code fin_neg(mark_t mark)
{
return acc_code::fin_neg(mark);
}
static acc_code fin_neg(std::initializer_list vals)
{
return fin_neg(mark_t(vals.begin(), vals.end()));
}
unsigned add_sets(unsigned num)
{
if (num == 0)
return -1U;
unsigned j = num_;
num_ += num;
if (num_ > 8 * sizeof(mark_t::id))
throw std::runtime_error("Too many acceptance sets used.");
all_ = all_sets_();
return j;
}
unsigned add_set()
{
return add_sets(1);
}
mark_t mark(unsigned u) const
{
SPOT_ASSERT(u < num_sets());
return 1U << u;
}
mark_t comp(mark_t l) const
{
return all_ ^ l.id;
}
mark_t all_sets() const
{
return all_;
}
bool accepting(mark_t inf) const
{
return code_.accepting(inf);
}
bool inf_satisfiable(mark_t inf) const
{
return code_.inf_satisfiable(inf);
}
trival maybe_accepting(mark_t infinitely_often, mark_t always_present) const
{
return code_.maybe_accepting(infinitely_often, always_present);
}
mark_t accepting_sets(mark_t inf) const;
std::ostream& format(std::ostream& os, mark_t m) const
{
auto a = m;
if (a == 0U)
return os;
return os << m;
}
std::string format(mark_t m) const
{
std::ostringstream os;
format(os, m);
return os.str();
}
unsigned num_sets() const
{
return num_;
}
template
mark_t useless(iterator begin, iterator end) const
{
mark_t::value_t u = 0U; // The set of useless marks.
for (unsigned x = 0; x < num_; ++x)
{
// Skip marks that are already known to be useless.
if (u & (1 << x))
continue;
unsigned all = all_ ^ (u | (1 << x));
for (iterator y = begin; y != end; ++y)
{
auto v = y->id;
if (v & (1 << x))
{
all &= v;
if (!all)
break;
}
}
u |= all;
}
return u;
}
// Remove all the acceptance sets in rem.
//
// If MISSING is set, the acceptance sets are assumed to be
// missing from the automaton, and the acceptance is updated to
// reflect this. For instance (Inf(1)&Inf(2))|Fin(3) will
// become Fin(3) if we remove 2 because it is missing from this
// automaton, because there is no way to fulfill Inf(1)&Inf(2)
// in this case. So essentially MISSING causes Inf(rem) to
// become f, and Fin(rem) to become t.
//
// If MISSING is unset, Inf(rem) become t while Fin(rem) become
// f. Removing 2 from (Inf(1)&Inf(2))|Fin(3) would then give
// Inf(1)|Fin(3).
acc_cond remove(mark_t rem, bool missing) const
{
return {num_sets(), code_.remove(rem, missing)};
}
// Same as remove, but also shift the set numbers
acc_cond strip(mark_t rem, bool missing) const
{
return
{ num_sets() - (all_sets() & rem).count(), code_.strip(rem, missing) };
}
// Restrict an acceptance condition to a subset of set numbers
// that are occurring at some point.
acc_cond restrict_to(mark_t rem) const
{
return {num_sets(), code_.remove(all_sets() - rem, true)};
}
protected:
mark_t::value_t all_sets_() const
{
if (num_ == 0)
return 0;
return -1U >> (8 * sizeof(mark_t::value_t) - num_);
}
unsigned num_;
mark_t::value_t all_;
acc_code code_;
bool uses_fin_acceptance_ = false;
};
struct rs_pairs_view {
typedef std::vector rs_pairs;
// Creates view of pairs 'p' with restriction only to marks in 'm'
explicit rs_pairs_view(const rs_pairs& p, const acc_cond::mark_t& m)
: pairs_(p), view_marks_(m) {}
// Creates view of pairs without restriction to marks
explicit rs_pairs_view(const rs_pairs& p)
: rs_pairs_view(p, std::numeric_limits::max()) {}
acc_cond::mark_t infs() const
{
return do_view([&](const acc_cond::rs_pair& p)
{
return visible(p.inf) ? p.inf : 0U;
});
}
acc_cond::mark_t fins() const
{
return do_view([&](const acc_cond::rs_pair& p)
{
return visible(p.fin) ? p.fin : 0U;
});
}
acc_cond::mark_t fins_alone() const
{
return do_view([&](const acc_cond::rs_pair& p)
{
return !visible(p.inf) && visible(p.fin) ? p.fin : 0U;
});
}
acc_cond::mark_t infs_alone() const
{
return do_view([&](const acc_cond::rs_pair& p)
{
return !visible(p.fin) && visible(p.inf) ? p.inf : 0U;
});
}
acc_cond::mark_t paired_with(unsigned mark) const
{
acc_cond::mark_t res = 0U;
for (const auto& p: pairs_)
{
if (visible(p.fin) && visible(p.inf))
{
if (p.fin.has(mark))
res |= p.inf;
if (p.inf.has(mark))
res |= p.fin;
}
}
return res;
}
const rs_pairs& pairs() const
{
return pairs_;
}
private:
template
acc_cond::mark_t do_view(const filter& filt) const
{
acc_cond::mark_t res = 0U;
for (const auto& p: pairs_)
res |= filt(p);
return res;
}
bool visible(const acc_cond::mark_t& v) const
{
return (view_marks_ & v) != 0;
}
const rs_pairs& pairs_;
acc_cond::mark_t view_marks_;
};
SPOT_API
std::ostream& operator<<(std::ostream& os, const acc_cond& acc);
namespace internal
{
class SPOT_API mark_iterator
{
public:
typedef unsigned value_type;
typedef const value_type& reference;
typedef const value_type* pointer;
typedef std::ptrdiff_t difference_type;
typedef std::forward_iterator_tag iterator_category;
mark_iterator() noexcept
: m_(0U)
{
}
mark_iterator(acc_cond::mark_t m) noexcept
: m_(m)
{
}
bool operator==(mark_iterator m) const
{
return m_ == m.m_;
}
bool operator!=(mark_iterator m) const
{
return m_ != m.m_;
}
value_type operator*() const
{
SPOT_ASSERT(m_);
return m_.min_set() - 1;
}
mark_iterator operator++()
{
m_.id &= m_.id - 1;
return *this;
}
mark_iterator operator++(int)
{
mark_iterator it = *this;
++(*this);
return it;
}
private:
acc_cond::mark_t m_;
};
class SPOT_API mark_container
{
public:
mark_container(spot::acc_cond::mark_t m) noexcept
: m_(m)
{
}
mark_iterator begin() const
{
return {m_};
}
mark_iterator end() const
{
return {};
}
private:
spot::acc_cond::mark_t m_;
};
}
inline spot::internal::mark_container acc_cond::mark_t::sets() const
{
return {*this};
}
}
namespace std
{
template<>
struct hash
{
size_t operator()(spot::acc_cond::mark_t m) const
{
std::hash h;
return h(m.id);
}
};
}