From cb392101663f74e46c5ff2a30e2b709b394c4878 Mon Sep 17 00:00:00 2001 From: Alexandre Duret-Lutz Date: Mon, 28 Sep 2015 16:20:53 +0200 Subject: [PATCH] kill the ltl namespace * NEWS: Mention it. * bench/stutter/stutter_invariance_formulas.cc, bench/stutter/stutter_invariance_randomgraph.cc, doc/mainpage.dox, doc/org/tut01.org, doc/org/tut02.org, doc/org/tut10.org, doc/tl/tl.tex, iface/ltsmin/ltsmin.cc, iface/ltsmin/ltsmin.hh, iface/ltsmin/modelcheck.cc, src/bin/autfilt.cc, src/bin/common_aoutput.cc, src/bin/common_aoutput.hh, src/bin/common_finput.cc, src/bin/common_finput.hh, src/bin/common_output.cc, src/bin/common_output.hh, src/bin/common_r.hh, src/bin/common_trans.cc, src/bin/common_trans.hh, src/bin/dstar2tgba.cc, src/bin/genltl.cc, src/bin/ltl2tgba.cc, src/bin/ltl2tgta.cc, src/bin/ltlcross.cc, src/bin/ltldo.cc, src/bin/ltlfilt.cc, src/bin/ltlgrind.cc, src/bin/randaut.cc, src/bin/randltl.cc, src/kripke/kripkeexplicit.cc, src/kripke/kripkeexplicit.hh, src/kripkeparse/kripkeparse.yy, src/kripkeparse/public.hh, src/ltlparse/fmterror.cc, src/ltlparse/ltlparse.yy, src/ltlparse/ltlscan.ll, src/ltlparse/parsedecl.hh, src/ltlparse/public.hh, src/parseaut/parseaut.yy, src/parseaut/public.hh, src/tests/checkpsl.cc, src/tests/checkta.cc, src/tests/complementation.cc, src/tests/consterm.cc, src/tests/emptchk.cc, src/tests/equalsf.cc, src/tests/ikwiad.cc, src/tests/kind.cc, src/tests/length.cc, src/tests/ltlprod.cc, src/tests/ltlrel.cc, src/tests/parse.test, src/tests/parse_print_test.cc, src/tests/randtgba.cc, src/tests/readltl.cc, src/tests/reduc.cc, src/tests/syntimpl.cc, src/tests/taatgba.cc, src/tests/tostring.cc, src/tests/tostring.test, src/tl/apcollect.cc, src/tl/apcollect.hh, src/tl/contain.cc, src/tl/contain.hh, src/tl/declenv.cc, src/tl/declenv.hh, src/tl/defaultenv.cc, src/tl/defaultenv.hh, src/tl/dot.cc, src/tl/dot.hh, src/tl/environment.hh, src/tl/exclusive.cc, src/tl/exclusive.hh, src/tl/formula.cc, src/tl/formula.hh, src/tl/length.cc, src/tl/length.hh, src/tl/mark.cc, src/tl/mark.hh, src/tl/mutation.cc, src/tl/mutation.hh, src/tl/nenoform.cc, src/tl/nenoform.hh, src/tl/print.cc, src/tl/print.hh, src/tl/randomltl.cc, src/tl/randomltl.hh, src/tl/relabel.cc, src/tl/relabel.hh, src/tl/remove_x.cc, src/tl/remove_x.hh, src/tl/simpfg.cc, src/tl/simpfg.hh, src/tl/simplify.cc, src/tl/simplify.hh, src/tl/snf.cc, src/tl/snf.hh, src/tl/unabbrev.cc, src/tl/unabbrev.hh, src/twa/bdddict.cc, src/twa/bdddict.hh, src/twa/bddprint.cc, src/twa/formula2bdd.cc, src/twa/formula2bdd.hh, src/twa/taatgba.cc, src/twa/taatgba.hh, src/twa/twa.hh, src/twa/twagraph.cc, src/twa/twagraph.hh, src/twaalgos/compsusp.cc, src/twaalgos/compsusp.hh, src/twaalgos/ltl2taa.cc, src/twaalgos/ltl2taa.hh, src/twaalgos/ltl2tgba_fm.cc, src/twaalgos/ltl2tgba_fm.hh, src/twaalgos/minimize.cc, src/twaalgos/minimize.hh, src/twaalgos/neverclaim.cc, src/twaalgos/postproc.cc, src/twaalgos/postproc.hh, src/twaalgos/powerset.cc, src/twaalgos/powerset.hh, src/twaalgos/randomgraph.cc, src/twaalgos/randomgraph.hh, src/twaalgos/relabel.cc, src/twaalgos/relabel.hh, src/twaalgos/remprop.cc, src/twaalgos/remprop.hh, src/twaalgos/stats.cc, src/twaalgos/stats.hh, src/twaalgos/stutter.cc, src/twaalgos/stutter.hh, src/twaalgos/translate.cc, src/twaalgos/translate.hh, wrap/python/spot_impl.i: Remove the ltl namespace. --- NEWS | 17 +- bench/stutter/stutter_invariance_formulas.cc | 8 +- .../stutter/stutter_invariance_randomgraph.cc | 2 +- doc/mainpage.dox | 8 +- doc/org/tut01.org | 42 +- doc/org/tut02.org | 16 +- doc/org/tut10.org | 6 +- doc/tl/tl.tex | 2 +- iface/ltsmin/ltsmin.cc | 12 +- iface/ltsmin/ltsmin.hh | 4 +- iface/ltsmin/modelcheck.cc | 22 +- src/bin/autfilt.cc | 2 +- src/bin/common_aoutput.cc | 2 +- src/bin/common_aoutput.hh | 4 +- src/bin/common_finput.cc | 14 +- src/bin/common_finput.hh | 6 +- src/bin/common_output.cc | 26 +- src/bin/common_output.hh | 10 +- src/bin/common_r.hh | 2 +- src/bin/common_trans.cc | 10 +- src/bin/common_trans.hh | 2 +- src/bin/dstar2tgba.cc | 2 +- src/bin/genltl.cc | 1 - src/bin/ltl2tgba.cc | 6 +- src/bin/ltl2tgta.cc | 6 +- src/bin/ltlcross.cc | 22 +- src/bin/ltldo.cc | 14 +- src/bin/ltlfilt.cc | 66 +- src/bin/ltlgrind.cc | 20 +- src/bin/randaut.cc | 8 +- src/bin/randltl.cc | 10 +- src/kripke/kripkeexplicit.cc | 2 +- src/kripke/kripkeexplicit.hh | 2 +- src/kripkeparse/kripkeparse.yy | 10 +- src/kripkeparse/public.hh | 6 +- src/ltlparse/fmterror.cc | 149 +- src/ltlparse/ltlparse.yy | 192 +- src/ltlparse/ltlscan.ll | 10 +- src/ltlparse/parsedecl.hh | 2 +- src/ltlparse/public.hh | 325 +- src/parseaut/parseaut.yy | 16 +- src/parseaut/public.hh | 12 +- src/tests/checkpsl.cc | 10 +- src/tests/checkta.cc | 8 +- src/tests/complementation.cc | 28 +- src/tests/consterm.cc | 8 +- src/tests/emptchk.cc | 8 +- src/tests/equalsf.cc | 46 +- src/tests/ikwiad.cc | 24 +- src/tests/kind.cc | 10 +- src/tests/length.cc | 12 +- src/tests/ltlprod.cc | 16 +- src/tests/ltlrel.cc | 20 +- src/tests/parse.test | 2 +- src/tests/parse_print_test.cc | 2 +- src/tests/randtgba.cc | 36 +- src/tests/readltl.cc | 12 +- src/tests/reduc.cc | 48 +- src/tests/syntimpl.cc | 24 +- src/tests/taatgba.cc | 6 +- src/tests/tostring.cc | 16 +- src/tests/tostring.test | 2 +- src/tl/apcollect.cc | 67 +- src/tl/apcollect.hh | 55 +- src/tl/contain.cc | 182 +- src/tl/contain.hh | 91 +- src/tl/declenv.cc | 60 +- src/tl/declenv.hh | 46 +- src/tl/defaultenv.cc | 47 +- src/tl/defaultenv.hh | 38 +- src/tl/dot.cc | 187 +- src/tl/dot.hh | 21 +- src/tl/environment.hh | 52 +- src/tl/exclusive.cc | 26 +- src/tl/exclusive.hh | 6 +- src/tl/formula.cc | 3116 ++++---- src/tl/formula.hh | 2079 +++--- src/tl/length.cc | 97 +- src/tl/length.hh | 43 +- src/tl/mark.cc | 316 +- src/tl/mark.hh | 30 +- src/tl/mutation.cc | 619 +- src/tl/mutation.hh | 37 +- src/tl/nenoform.cc | 16 +- src/tl/nenoform.hh | 37 +- src/tl/print.cc | 2152 +++--- src/tl/print.hh | 359 +- src/tl/randomltl.cc | 1009 ++- src/tl/randomltl.hh | 564 +- src/tl/relabel.cc | 838 ++- src/tl/relabel.hh | 41 +- src/tl/remove_x.cc | 137 +- src/tl/remove_x.hh | 43 +- src/tl/simpfg.cc | 32 +- src/tl/simpfg.hh | 27 +- src/tl/simplify.cc | 6421 ++++++++--------- src/tl/simplify.hh | 311 +- src/tl/snf.cc | 214 +- src/tl/snf.hh | 54 +- src/tl/unabbrev.cc | 427 +- src/tl/unabbrev.hh | 74 +- src/twa/bdddict.cc | 12 +- src/twa/bdddict.hh | 22 +- src/twa/bddprint.cc | 6 +- src/twa/formula2bdd.cc | 10 +- src/twa/formula2bdd.hh | 8 +- src/twa/taatgba.cc | 4 +- src/twa/taatgba.hh | 10 +- src/twa/twa.hh | 8 +- src/twa/twagraph.cc | 2 +- src/twa/twagraph.hh | 6 +- src/twaalgos/compsusp.cc | 58 +- src/twaalgos/compsusp.hh | 2 +- src/twaalgos/ltl2taa.cc | 6 +- src/twaalgos/ltl2taa.hh | 2 +- src/twaalgos/ltl2tgba_fm.cc | 4 +- src/twaalgos/ltl2tgba_fm.hh | 8 +- src/twaalgos/minimize.cc | 4 +- src/twaalgos/minimize.hh | 2 +- src/twaalgos/neverclaim.cc | 2 +- src/twaalgos/postproc.cc | 2 +- src/twaalgos/postproc.hh | 2 +- src/twaalgos/powerset.cc | 4 +- src/twaalgos/powerset.hh | 2 +- src/twaalgos/randomgraph.cc | 2 +- src/twaalgos/randomgraph.hh | 2 +- src/twaalgos/relabel.cc | 2 +- src/twaalgos/relabel.hh | 2 +- src/twaalgos/remprop.cc | 6 +- src/twaalgos/remprop.hh | 6 +- src/twaalgos/stats.cc | 4 +- src/twaalgos/stats.hh | 6 +- src/twaalgos/stutter.cc | 14 +- src/twaalgos/stutter.hh | 4 +- src/twaalgos/translate.cc | 10 +- src/twaalgos/translate.hh | 10 +- wrap/python/spot_impl.i | 29 +- 137 files changed, 10771 insertions(+), 10919 deletions(-) diff --git a/NEWS b/NEWS index 149b4a74b..95bacca2b 100644 --- a/NEWS +++ b/NEWS @@ -28,9 +28,9 @@ New in spot 1.99.3a (not yet released) * The class hierarchy for temporal formulas has been entirely rewritten. This change is actually quite massive (~13200 lines removed, ~8200 lines added), and brings some nice benefits: - - LTL/PSL formulas are now represented by lightweight ltl::formula - objects (instead of ltl::formula* pointers) that perform - reference counting automatically. + - LTL/PSL formulas are now represented by lightweight formula + objects (instead of pointers to children of an abstract formula + class) that perform reference counting automatically. - There is no hierachy anymore: all operators are represented by a single type of node in the syntax tree, and an enumerator is used to distinguish between operators. @@ -41,10 +41,13 @@ New in spot 1.99.3a (not yet released) more friendly, and several algorithms that spanned a few pages have been reduced to a few lines. - * The source directories ltlast/, ltlenv/, and ltlvisit/, have been - merged into a single tl/ directory (for temporal logic). This is - motivated by the fact that these formulas are not restricted to - LTL, and by the fact that we no longuer use the "visitor" pattern. + * Directories ltlast/, ltlenv/, and ltlvisit/, have been merged into + a single tl/ directory (for temporal logic). This is motivated by + the fact that these formulas are not restricted to LTL, and by the + fact that we no longuer use the "visitor" pattern. + + * For similar reasons, the spot::ltl namespace has been merged + with the spot namespace. New in spot 1.99.3 (2015-08-26) diff --git a/bench/stutter/stutter_invariance_formulas.cc b/bench/stutter/stutter_invariance_formulas.cc index c23190d5d..d8b8f811f 100644 --- a/bench/stutter/stutter_invariance_formulas.cc +++ b/bench/stutter/stutter_invariance_formulas.cc @@ -62,12 +62,12 @@ namespace } int - process_formula(spot::ltl::formula f, const char*, int) + process_formula(spot::formula f, const char*, int) { spot::twa_graph_ptr a = trans.run(f); - spot::twa_graph_ptr na = trans.run(spot::ltl::formula::Not(f)); - spot::ltl::atomic_prop_set* ap = spot::ltl::atomic_prop_collect(f); - bdd apdict = spot::ltl::atomic_prop_collect_as_bdd(f, a); + spot::twa_graph_ptr na = trans.run(spot::formula::Not(f)); + spot::atomic_prop_set* ap = spot::atomic_prop_collect(f); + bdd apdict = spot::atomic_prop_collect_as_bdd(f, a); std::cout << formula << ',' << ap->size() << ','; stats.print(a); diff --git a/bench/stutter/stutter_invariance_randomgraph.cc b/bench/stutter/stutter_invariance_randomgraph.cc index 5575bfce0..b3b22f354 100644 --- a/bench/stutter/stutter_invariance_randomgraph.cc +++ b/bench/stutter/stutter_invariance_randomgraph.cc @@ -55,7 +55,7 @@ main(int argc, char** argv) constexpr unsigned n = 10; // random ap set - auto ap = spot::ltl::create_atomic_prop_set(props_n); + auto ap = spot::create_atomic_prop_set(props_n); // ap set as bdd bdd apdict = bddtrue; for (auto& i: ap) diff --git a/doc/mainpage.dox b/doc/mainpage.dox index f125bdade..341730484 100644 --- a/doc/mainpage.dox +++ b/doc/mainpage.dox @@ -19,12 +19,12 @@ /// /// \section pointers Handy starting points /// -/// \li spot::ltl::formula Base class for an LTL or PSL formula. -/// \li spot::ltl::parse_infix_psl Parsing a text string into a -/// spot::ltl::formula. +/// \li spot::formula Base class for an LTL or PSL formula. +/// \li spot::parse_infix_psl Parsing a text string into a +/// spot::formula. /// \li spot::twa Base class for Transition-based /// ω-Automata. -/// \li spot::translator Convert a spot::ltl::formula into a +/// \li spot::translator Convert a spot::formula into a /// spot::tgba. /// \li spot::kripke Base class for Kripke structures. /// \li spot::twa_product On-the-fly product of two spot::twa. diff --git a/doc/org/tut01.org b/doc/org/tut01.org index 2c91a597b..6fb23f2c9 100644 --- a/doc/org/tut01.org +++ b/doc/org/tut01.org @@ -71,8 +71,8 @@ exceptions. int main() { - print_latex_psl(std::cout, spot::ltl::parse_formula("[]<>p0 || <>[]p1")) << '\n'; - spot::ltl::formula f = spot::ltl::parse_formula("& & G p0 p1 p2"); + print_latex_psl(std::cout, spot::parse_formula("[]<>p0 || <>[]p1")) << '\n'; + spot::formula f = spot::parse_formula("& & G p0 p1 p2"); print_lbt_ltl(std::cout, f) << '\n'; print_spin_ltl(std::cout, f, true) << '\n'; return 0; @@ -110,9 +110,9 @@ Here is how to call the infix parser explicitly,: int main() { std::string input = "[]<>p0 || <>[]p1"; - spot::ltl::parse_error_list pel; - spot::ltl::formula f = spot::ltl::parse_infix_psl(input, pel); - if (spot::ltl::format_parse_errors(std::cerr, input, pel)) + spot::parse_error_list pel; + spot::formula f = spot::parse_infix_psl(input, pel); + if (spot::format_parse_errors(std::cerr, input, pel)) return 1; print_latex_psl(std::cout, f) << '\n'; return 0; @@ -152,11 +152,11 @@ with the "fixed" formula if you wish. Here is an example: int main() { std::string input = "(a U b))"; - spot::ltl::parse_error_list pel; - spot::ltl::formula f = spot::ltl::parse_infix_psl(input, pel); + spot::parse_error_list pel; + spot::formula f = spot::parse_infix_psl(input, pel); // Use std::cout instead of std::cerr because we can only // show the output of std::cout in this documentation. - (void) spot::ltl::format_parse_errors(std::cout, input, pel); + (void) spot::format_parse_errors(std::cout, input, pel); if (f == nullptr) return 1; std::cout << "Parsed formula: "; @@ -194,9 +194,9 @@ of =parse_infix_psl()=. int main() { std::string input = "& & G p0 p1 p2"; - spot::ltl::parse_error_list pel; - spot::ltl::formula f = spot::ltl::parse_prefix_ltl(input, pel); - if (spot::ltl::format_parse_errors(std::cerr, input, pel)) + spot::parse_error_list pel; + spot::formula f = spot::parse_prefix_ltl(input, pel); + if (spot::format_parse_errors(std::cerr, input, pel)) return 1; print_lbt_ltl(std::cout, f) << '\n'; print_spin_ltl(std::cout, f, true) << '\n'; @@ -236,9 +236,9 @@ For instance, let's see what happens if a PSL formulas is passed to int main() { std::string input = "{a*;b}<>->(a U (b & GF c))"; - spot::ltl::parse_error_list pel; - spot::ltl::formula f = spot::ltl::parse_infix_psl(input, pel); - if (spot::ltl::format_parse_errors(std::cerr, input, pel)) + spot::parse_error_list pel; + spot::formula f = spot::parse_infix_psl(input, pel); + if (spot::format_parse_errors(std::cerr, input, pel)) return 1; print_spin_ltl(std::cout, f) << '\n'; return 0; @@ -266,9 +266,9 @@ The first is to simply diagnose non-LTL formulas. int main() { std::string input = "{a*;b}<>->(a U (b & GF c))"; - spot::ltl::parse_error_list pel; - spot::ltl::formula f = spot::ltl::parse_infix_psl(input, pel); - if (spot::ltl::format_parse_errors(std::cerr, input, pel)) + spot::parse_error_list pel; + spot::formula f = spot::parse_infix_psl(input, pel); + if (spot::format_parse_errors(std::cerr, input, pel)) return 1; if (!f.is_ltl_formula()) { @@ -296,13 +296,13 @@ prepared to reject the formula any way. In our example, we are lucky int main() { std::string input = "{a*;b}<>->(a U (b & GF c))"; - spot::ltl::parse_error_list pel; - spot::ltl::formula f = spot::ltl::parse_infix_psl(input, pel); - if (spot::ltl::format_parse_errors(std::cerr, input, pel)) + spot::parse_error_list pel; + spot::formula f = spot::parse_infix_psl(input, pel); + if (spot::format_parse_errors(std::cerr, input, pel)) return 1; if (!f.is_ltl_formula()) { - spot::ltl::ltl_simplifier simp; + spot::ltl_simplifier simp; f = simp.simplify(f); } if (!f.is_ltl_formula()) diff --git a/doc/org/tut02.org b/doc/org/tut02.org index 3e6c032d2..79b894bf3 100644 --- a/doc/org/tut02.org +++ b/doc/org/tut02.org @@ -74,7 +74,7 @@ print(g.to_str('spin', True)) * C++ -The =spot::ltl::relabeling_map= is just a =std::map= with a custom +The =spot::relabeling_map= is just a =std::map= with a custom destructor. #+BEGIN_SRC C++ :results verbatim :exports both @@ -87,12 +87,12 @@ destructor. int main() { std::string input = "\"Proc@Here\" U (\"var > 10\" | \"var < 4\")"; - spot::ltl::parse_error_list pel; - spot::ltl::formula f = spot::ltl::parse_infix_psl(input, pel); - if (spot::ltl::format_parse_errors(std::cerr, input, pel)) + spot::parse_error_list pel; + spot::formula f = spot::parse_infix_psl(input, pel); + if (spot::format_parse_errors(std::cerr, input, pel)) return 1; - spot::ltl::relabeling_map m; - f = spot::ltl::relabel(f, spot::ltl::Pnn, &m); + spot::relabeling_map m; + f = spot::relabel(f, spot::Pnn, &m); for (auto& i: m) { std::cout << "#define "; @@ -115,8 +115,8 @@ destructor. ** Two ways to name atomic propositions - Instead of =--relabel=pnn= (or =spot.Pnn=, or =spot::ltl::Pnn=), you can - actually use =--relabel=abc= (or =spot.Abc=, or =spot::ltl::Abc=) to have + Instead of =--relabel=pnn= (or =spot.Pnn=, or =spot::Pnn=), you can + actually use =--relabel=abc= (or =spot.Abc=, or =spot::Abc=) to have the atomic propositions named =a=, =b=, =c=, etc. ** Relabeling Boolean sub-expressions diff --git a/doc/org/tut10.org b/doc/org/tut10.org index e210bd14d..d01ae5e23 100644 --- a/doc/org/tut10.org +++ b/doc/org/tut10.org @@ -137,9 +137,9 @@ never claim is done via the =print_never_claim= function. int main() { std::string input = "[]<>p0 || <>[]p1"; - spot::ltl::parse_error_list pel; - spot::ltl::formula f = spot::ltl::parse_infix_psl(input, pel); - if (spot::ltl::format_parse_errors(std::cerr, input, pel)) + spot::parse_error_list pel; + spot::formula f = spot::parse_infix_psl(input, pel); + if (spot::format_parse_errors(std::cerr, input, pel)) return 1; spot::translator trans; trans.set_type(spot::postprocessor::BA); diff --git a/doc/tl/tl.tex b/doc/tl/tl.tex index 0d7c92f36..a2cc0e983 100644 --- a/doc/tl/tl.tex +++ b/doc/tl/tl.tex @@ -1001,7 +1001,7 @@ both right-associative, other have only $\IMPLIES$ as right-associative. When Spot builds a formula (represented by an AST with shared subtrees) it computes a set of properties for each node. These -properties can be queried from any \texttt{spot::ltl::formula} +properties can be queried from any \texttt{spot::formula} instance using the following methods: \noindent diff --git a/iface/ltsmin/ltsmin.cc b/iface/ltsmin/ltsmin.cc index 44abec4d4..f5f381f67 100644 --- a/iface/ltsmin/ltsmin.cc +++ b/iface/ltsmin/ltsmin.cc @@ -323,10 +323,10 @@ namespace spot int - convert_aps(const ltl::atomic_prop_set* aps, + convert_aps(const atomic_prop_set* aps, const spins_interface* d, bdd_dict_ptr dict, - ltl::formula dead, + formula dead, prop_set& out) { int errors = 0; @@ -353,7 +353,7 @@ namespace spot enum_map[i].emplace(d->get_type_value_name(i, j), j); } - for (ltl::atomic_prop_set::const_iterator ap = aps->begin(); + for (atomic_prop_set::const_iterator ap = aps->begin(); ap != aps->end(); ++ap) { if (*ap == dead) @@ -602,7 +602,7 @@ namespace spot public: spins_kripke(const spins_interface* d, const bdd_dict_ptr& dict, - const spot::prop_set* ps, ltl::formula dead, + const spot::prop_set* ps, formula dead, int compress) : kripke(dict), d_(d), @@ -1016,8 +1016,8 @@ namespace spot kripke_ptr load_ltsmin(const std::string& file_arg, const bdd_dict_ptr& dict, - const ltl::atomic_prop_set* to_observe, - const ltl::formula dead, int compress, bool verbose) + const atomic_prop_set* to_observe, + const formula dead, int compress, bool verbose) { std::string file; if (file_arg.find_first_of("/\\") != std::string::npos) diff --git a/iface/ltsmin/ltsmin.hh b/iface/ltsmin/ltsmin.hh index cdc6985dc..264dc1259 100644 --- a/iface/ltsmin/ltsmin.hh +++ b/iface/ltsmin/ltsmin.hh @@ -57,7 +57,7 @@ namespace spot // \a verbose whether to output verbose messages SPOT_API kripke_ptr load_ltsmin(const std::string& file, const bdd_dict_ptr& dict, - const ltl::atomic_prop_set* to_observe, - ltl::formula dead = ltl::formula::tt(), + const atomic_prop_set* to_observe, + formula dead = formula::tt(), int compress = 0, bool verbose = true); } diff --git a/iface/ltsmin/modelcheck.cc b/iface/ltsmin/modelcheck.cc index 3e1c19b7c..3f6f61946 100644 --- a/iface/ltsmin/modelcheck.cc +++ b/iface/ltsmin/modelcheck.cc @@ -150,11 +150,11 @@ checked_main(int argc, char **argv) if (argc != 3) syntax(argv[0]); - spot::ltl::default_environment& env = - spot::ltl::default_environment::instance(); + spot::default_environment& env = + spot::default_environment::instance(); - spot::ltl::atomic_prop_set ap; + spot::atomic_prop_set ap; auto dict = spot::make_bdd_dict(); spot::const_kripke_ptr model = nullptr; spot::const_twa_ptr prop = nullptr; @@ -162,16 +162,16 @@ checked_main(int argc, char **argv) spot::emptiness_check_instantiator_ptr echeck_inst = nullptr; int exit_code = 0; spot::postprocessor post; - spot::ltl::formula deadf = nullptr; - spot::ltl::formula f = nullptr; + spot::formula deadf = nullptr; + spot::formula f = nullptr; if (!dead || !strcasecmp(dead, "true")) { - deadf = spot::ltl::formula::tt(); + deadf = spot::formula::tt(); } else if (!strcasecmp(dead, "false")) { - deadf = spot::ltl::formula::ff(); + deadf = spot::formula::ff(); } else { @@ -193,9 +193,9 @@ checked_main(int argc, char **argv) tm.start("parsing formula"); { - spot::ltl::parse_error_list pel; - f = spot::ltl::parse_infix_psl(argv[2], pel, env, false); - exit_code = spot::ltl::format_parse_errors(std::cerr, argv[2], pel); + spot::parse_error_list pel; + f = spot::parse_infix_psl(argv[2], pel, env, false); + exit_code = spot::format_parse_errors(std::cerr, argv[2], pel); } tm.stop("parsing formula"); @@ -366,6 +366,6 @@ main(int argc, char **argv) auto exit_code = checked_main(argc, argv); // Additional checks to debug reference counts in formulas. - assert(spot::ltl::fnode::instances_check()); + assert(spot::fnode::instances_check()); exit(exit_code); } diff --git a/src/bin/autfilt.cc b/src/bin/autfilt.cc index de42801ac..39c04d214 100644 --- a/src/bin/autfilt.cc +++ b/src/bin/autfilt.cc @@ -505,7 +505,7 @@ namespace } int - process_formula(spot::ltl::formula, const char*, int) + process_formula(spot::formula, const char*, int) { SPOT_UNREACHABLE(); } diff --git a/src/bin/common_aoutput.cc b/src/bin/common_aoutput.cc index 526238168..7273519c0 100644 --- a/src/bin/common_aoutput.cc +++ b/src/bin/common_aoutput.cc @@ -287,7 +287,7 @@ automaton_printer::automaton_printer(stat_style input) void automaton_printer::print(const spot::twa_graph_ptr& aut, - spot::ltl::formula f, + spot::formula f, // Input location for errors and statistics. const char* filename, int loc, diff --git a/src/bin/common_aoutput.hh b/src/bin/common_aoutput.hh index c41cd227e..ee11b71e7 100644 --- a/src/bin/common_aoutput.hh +++ b/src/bin/common_aoutput.hh @@ -107,7 +107,7 @@ public: std::ostream& print(const spot::const_parsed_aut_ptr& haut, const spot::const_twa_graph_ptr& aut, - spot::ltl::formula f, + spot::formula f, const char* filename, int loc, double run_time) { filename_ = filename ? filename : ""; @@ -225,7 +225,7 @@ public: void print(const spot::twa_graph_ptr& aut, - spot::ltl::formula f = nullptr, + spot::formula f = nullptr, // Input location for errors and statistics. const char* filename = nullptr, int loc = -1, diff --git a/src/bin/common_finput.cc b/src/bin/common_finput.cc index 2546943a2..7c602412f 100644 --- a/src/bin/common_finput.cc +++ b/src/bin/common_finput.cc @@ -77,14 +77,14 @@ parse_opt_finput(int key, char* arg, struct argp_state*) return 0; } -spot::ltl::formula -parse_formula(const std::string& s, spot::ltl::parse_error_list& pel) +spot::formula +parse_formula(const std::string& s, spot::parse_error_list& pel) { if (lbt_input) - return spot::ltl::parse_prefix_ltl(s, pel); + return spot::parse_prefix_ltl(s, pel); else - return spot::ltl::parse_infix_psl - (s, pel, spot::ltl::default_environment::instance(), false, lenient); + return spot::parse_infix_psl + (s, pel, spot::default_environment::instance(), false, lenient); } job_processor::job_processor() @@ -109,14 +109,14 @@ job_processor::process_string(const std::string& input, const char* filename, int linenum) { - spot::ltl::parse_error_list pel; + spot::parse_error_list pel; auto f = parse_formula(input, pel); if (!f || !pel.empty()) { if (filename) error_at_line(0, 0, filename, linenum, "parse error:"); - spot::ltl::format_parse_errors(std::cerr, input, pel); + spot::format_parse_errors(std::cerr, input, pel); return 1; } return process_formula(f, filename, linenum); diff --git a/src/bin/common_finput.hh b/src/bin/common_finput.hh index 6130ed049..015307384 100644 --- a/src/bin/common_finput.hh +++ b/src/bin/common_finput.hh @@ -44,8 +44,8 @@ extern const struct argp finput_argp; int parse_opt_finput(int key, char* arg, struct argp_state* state); -spot::ltl::formula -parse_formula(const std::string& s, spot::ltl::parse_error_list& error_list); +spot::formula +parse_formula(const std::string& s, spot::parse_error_list& error_list); class job_processor @@ -58,7 +58,7 @@ public: virtual ~job_processor(); virtual int - process_formula(spot::ltl::formula f, + process_formula(spot::formula f, const char* filename = nullptr, int linenum = 0) = 0; virtual int diff --git a/src/bin/common_output.cc b/src/bin/common_output.cc index 9f2c70960..c67d901dd 100644 --- a/src/bin/common_output.cc +++ b/src/bin/common_output.cc @@ -70,10 +70,10 @@ const struct argp output_argp = { options, parse_opt_output, static void -report_not_ltl(spot::ltl::formula f, +report_not_ltl(spot::formula f, const char* filename, int linenum, const char* syn) { - std::string s = spot::ltl::str_psl(f); + std::string s = spot::str_psl(f); static const char msg[] = "formula '%s' cannot be written %s's syntax because it is not LTL"; if (filename) @@ -84,36 +84,36 @@ report_not_ltl(spot::ltl::formula f, std::ostream& stream_formula(std::ostream& out, - spot::ltl::formula f, const char* filename, int linenum) + spot::formula f, const char* filename, int linenum) { switch (output_format) { case lbt_output: if (f.is_ltl_formula()) - spot::ltl::print_lbt_ltl(out, f); + spot::print_lbt_ltl(out, f); else report_not_ltl(f, filename, linenum, "LBT"); break; case spot_output: - spot::ltl::print_psl(out, f, full_parenth); + spot::print_psl(out, f, full_parenth); break; case spin_output: if (f.is_ltl_formula()) - spot::ltl::print_spin_ltl(out, f, full_parenth); + spot::print_spin_ltl(out, f, full_parenth); else report_not_ltl(f, filename, linenum, "Spin"); break; case wring_output: if (f.is_ltl_formula()) - spot::ltl::print_wring_ltl(out, f); + spot::print_wring_ltl(out, f); else report_not_ltl(f, filename, linenum, "Wring"); break; case utf8_output: - spot::ltl::print_utf8_psl(out, f, full_parenth); + spot::print_utf8_psl(out, f, full_parenth); break; case latex_output: - spot::ltl::print_latex_psl(out, f, full_parenth); + spot::print_latex_psl(out, f, full_parenth); break; case count_output: case quiet_output: @@ -124,7 +124,7 @@ stream_formula(std::ostream& out, static void stream_escapable_formula(std::ostream& os, - spot::ltl::formula f, + spot::formula f, const char* filename, int linenum) { if (escape_csv) @@ -146,7 +146,7 @@ namespace { struct formula_with_location { - spot::ltl::formula f; + spot::formula f; const char* filename; int line; const char* prefix; @@ -260,7 +260,7 @@ parse_opt_output(int key, char* arg, struct argp_state*) static void output_formula(std::ostream& out, - spot::ltl::formula f, + spot::formula f, const char* filename = nullptr, int linenum = 0, const char* prefix = nullptr, const char* suffix = nullptr) { @@ -286,7 +286,7 @@ void } void -output_formula_checked(spot::ltl::formula f, +output_formula_checked(spot::formula f, const char* filename, int linenum, const char* prefix, const char* suffix) { diff --git a/src/bin/common_output.hh b/src/bin/common_output.hh index 09989320f..795e05ff9 100644 --- a/src/bin/common_output.hh +++ b/src/bin/common_output.hh @@ -43,20 +43,20 @@ int parse_opt_output(int key, char* arg, struct argp_state* state); // Low-level output std::ostream& stream_formula(std::ostream& out, - spot::ltl::formula f, const char* filename, int linenum); + spot::formula f, const char* filename, int linenum); -void output_formula_checked(spot::ltl::formula f, +void output_formula_checked(spot::formula f, const char* filename = nullptr, int linenum = 0, const char* prefix = nullptr, const char* suffix = nullptr); class printable_formula: - public spot::printable_value + public spot::printable_value { public: printable_formula& - operator=(spot::ltl::formula new_val) + operator=(spot::formula new_val) { val_ = new_val; return *this; @@ -79,7 +79,7 @@ public: std::ostream& print(const spot::const_twa_graph_ptr& aut, - spot::ltl::formula f = nullptr, + spot::formula f = nullptr, double run_time = -1.) { formula_ = f; diff --git a/src/bin/common_r.hh b/src/bin/common_r.hh index e35da9591..9fb7d8950 100644 --- a/src/bin/common_r.hh +++ b/src/bin/common_r.hh @@ -44,4 +44,4 @@ extern int simplification_level; void parse_r(const char* arg); -spot::ltl::ltl_simplifier_options simplifier_options(); +spot::ltl_simplifier_options simplifier_options(); diff --git a/src/bin/common_trans.cc b/src/bin/common_trans.cc index fe06a55a6..0dfa5944c 100644 --- a/src/bin/common_trans.cc +++ b/src/bin/common_trans.cc @@ -290,16 +290,16 @@ translator_runner::formula() const } void -translator_runner::round_formula(spot::ltl::formula f, unsigned serial) +translator_runner::round_formula(spot::formula f, unsigned serial) { if (has('f') || has('F')) - string_ltl_spot = spot::ltl::str_psl(f, true); + string_ltl_spot = spot::str_psl(f, true); if (has('s') || has('S')) - string_ltl_spin = spot::ltl::str_spin_ltl(f, true); + string_ltl_spin = spot::str_spin_ltl(f, true); if (has('l') || has('L')) - string_ltl_lbt = spot::ltl::str_lbt_ltl(f); + string_ltl_lbt = spot::str_lbt_ltl(f); if (has('w') || has('W')) - string_ltl_wring = spot::ltl::str_wring_ltl(f); + string_ltl_wring = spot::str_wring_ltl(f); if (has('F')) string_to_tmp(string_ltl_spot, serial, filename_ltl_spot); if (has('S')) diff --git a/src/bin/common_trans.hh b/src/bin/common_trans.hh index 53e215629..11ffe2125 100644 --- a/src/bin/common_trans.hh +++ b/src/bin/common_trans.hh @@ -94,7 +94,7 @@ public: bool no_output_allowed = false); void string_to_tmp(std::string& str, unsigned n, std::string& tmpname); const std::string& formula() const; - void round_formula(spot::ltl::formula f, unsigned serial); + void round_formula(spot::formula f, unsigned serial); }; diff --git a/src/bin/dstar2tgba.cc b/src/bin/dstar2tgba.cc index 09493de40..df15a07c2 100644 --- a/src/bin/dstar2tgba.cc +++ b/src/bin/dstar2tgba.cc @@ -139,7 +139,7 @@ namespace } int - process_formula(spot::ltl::formula, const char*, int) + process_formula(spot::formula, const char*, int) { SPOT_UNREACHABLE(); } diff --git a/src/bin/genltl.cc b/src/bin/genltl.cc index 2590d11ed..d75f3065d 100644 --- a/src/bin/genltl.cc +++ b/src/bin/genltl.cc @@ -90,7 +90,6 @@ #include "tl/relabel.hh" using namespace spot; -using namespace spot::ltl; const char argp_program_doc[] ="\ Generate temporal logic formulas from predefined scalable patterns."; diff --git a/src/bin/ltl2tgba.cc b/src/bin/ltl2tgba.cc index d26dd1025..62e4a8b3e 100644 --- a/src/bin/ltl2tgba.cc +++ b/src/bin/ltl2tgba.cc @@ -139,16 +139,16 @@ namespace } int - process_formula(spot::ltl::formula f, + process_formula(spot::formula f, const char* filename = nullptr, int linenum = 0) { // This should not happen, because the parser we use can only - // read PSL/LTL formula, but since our ltl::formula type can + // read PSL/LTL formula, but since our formula type can // represent more than PSL formula, let's make this // future-proof. if (!f.is_psl_formula()) { - std::string s = spot::ltl::str_psl(f); + std::string s = spot::str_psl(f); error_at_line(2, 0, filename, linenum, "formula '%s' is not an LTL or PSL formula", s.c_str()); diff --git a/src/bin/ltl2tgta.cc b/src/bin/ltl2tgta.cc index 7dbc309c9..6edb9d821 100644 --- a/src/bin/ltl2tgta.cc +++ b/src/bin/ltl2tgta.cc @@ -170,18 +170,18 @@ namespace } int - process_formula(spot::ltl::formula f, + process_formula(spot::formula f, const char* filename = nullptr, int linenum = 0) { auto aut = trans.run(&f); // This should not happen, because the parser we use can only - // read PSL/LTL formula, but since our ltl::formula type can + // read PSL/LTL formula, but since our formula type can // represent more than PSL formula, let's make this // future-proof. if (!f.is_psl_formula()) { - std::string s = spot::ltl::str_psl(f); + std::string s = spot::str_psl(f); error_at_line(2, 0, filename, linenum, "formula '%s' is not an LTL or PSL formula", s.c_str()); diff --git a/src/bin/ltlcross.cc b/src/bin/ltlcross.cc index 40740068b..6067ba184 100644 --- a/src/bin/ltlcross.cc +++ b/src/bin/ltlcross.cc @@ -819,7 +819,7 @@ namespace } typedef - std::unordered_set fset_t; + std::unordered_set fset_t; class processor: public job_processor @@ -838,14 +838,14 @@ namespace const char* filename, int linenum) { - spot::ltl::parse_error_list pel; - spot::ltl::formula f = parse_formula(input, pel); + spot::parse_error_list pel; + spot::formula f = parse_formula(input, pel); if (!f || !pel.empty()) { if (filename) error_at_line(0, 0, filename, linenum, "parse error:"); - spot::ltl::format_parse_errors(std::cerr, input, pel); + spot::format_parse_errors(std::cerr, input, pel); return 1; } @@ -856,7 +856,7 @@ namespace if (res && grind_output) { std::string bogus = input; - std::vector mutations; + std::vector mutations; unsigned mutation_count; unsigned mutation_max; while (res) @@ -886,9 +886,9 @@ namespace if (res) { if (lbt_input) - bogus = spot::ltl::str_lbt_ltl(f); + bogus = spot::str_lbt_ltl(f); else - bogus = spot::ltl::str_psl(f); + bogus = spot::str_psl(f); if (bogus_output) bogus_output->ostream() << bogus << std::endl; } @@ -938,7 +938,7 @@ namespace } int - process_formula(spot::ltl::formula f, + process_formula(spot::formula f, const char* filename = nullptr, int linenum = 0) { static unsigned round = 0; @@ -947,7 +947,7 @@ namespace // output, relabel the formula. if (!f.has_lbt_atomic_props() && (runner.has('l') || runner.has('L') || runner.has('T'))) - f = spot::ltl::relabel(f, spot::ltl::Pnn); + f = spot::relabel(f, spot::Pnn); // ---------- Positive Formula ---------- @@ -1028,7 +1028,7 @@ namespace nstats = &vstats[n + 1]; nstats->resize(m); - spot::ltl::formula nf = spot::ltl::formula::Not(f); + spot::formula nf = spot::formula::Not(f); if (!allow_dups) { @@ -1143,7 +1143,7 @@ namespace std::cerr << "Gathering statistics..." << std::endl; } - spot::ltl::atomic_prop_set* ap = spot::ltl::atomic_prop_collect(f); + spot::atomic_prop_set* ap = spot::atomic_prop_collect(f); if (want_stats) for (size_t i = 0; i < m; ++i) diff --git a/src/bin/ltldo.cc b/src/bin/ltldo.cc index e07549766..bf1b7838e 100644 --- a/src/bin/ltldo.cc +++ b/src/bin/ltldo.cc @@ -237,14 +237,14 @@ namespace const char* filename, int linenum) { - spot::ltl::parse_error_list pel; - spot::ltl::formula f = parse_formula(input, pel); + spot::parse_error_list pel; + spot::formula f = parse_formula(input, pel); if (!f || !pel.empty()) { if (filename) error_at_line(0, 0, filename, linenum, "parse error:"); - spot::ltl::format_parse_errors(std::cerr, input, pel); + spot::format_parse_errors(std::cerr, input, pel); return 1; } @@ -255,10 +255,10 @@ namespace int - process_formula(spot::ltl::formula f, + process_formula(spot::formula f, const char* filename = nullptr, int linenum = 0) { - std::unique_ptr relmap; + std::unique_ptr relmap; // If atomic propositions are incompatible with one of the // output, relabel the formula. @@ -267,8 +267,8 @@ namespace || (!f.has_spin_atomic_props() && (runner.has('s') || runner.has('S')))) { - relmap.reset(new spot::ltl::relabeling_map); - f = spot::ltl::relabel(f, spot::ltl::Pnn, relmap.get()); + relmap.reset(new spot::relabeling_map); + f = spot::relabel(f, spot::Pnn, relmap.get()); } static unsigned round = 1; diff --git a/src/bin/ltlfilt.cc b/src/bin/ltlfilt.cc index e179b27a5..81f3e8507 100644 --- a/src/bin/ltlfilt.cc +++ b/src/bin/ltlfilt.cc @@ -251,7 +251,7 @@ static int bsize_min = -1; static int bsize_max = -1; enum relabeling_mode { NoRelabeling = 0, ApRelabeling, BseRelabeling }; static relabeling_mode relabeling = NoRelabeling; -static spot::ltl::relabeling_style style = spot::ltl::Abc; +static spot::relabeling_style style = spot::Abc; static bool remove_x = false; static bool stutter_insensitive = false; static bool ap = false; @@ -262,16 +262,16 @@ static spot::exclusive_ap excl_ap; static std::unique_ptr output_define = nullptr; static std::string unabbreviate; -static spot::ltl::formula implied_by = nullptr; -static spot::ltl::formula imply = nullptr; -static spot::ltl::formula equivalent_to = nullptr; +static spot::formula implied_by = nullptr; +static spot::formula imply = nullptr; +static spot::formula equivalent_to = nullptr; -static spot::ltl::formula +static spot::formula parse_formula_arg(const std::string& input) { - spot::ltl::parse_error_list pel; - spot::ltl::formula f = parse_formula(input, pel); - if (spot::ltl::format_parse_errors(std::cerr, input, pel)) + spot::parse_error_list pel; + spot::formula f = parse_formula(input, pel); + if (spot::format_parse_errors(std::cerr, input, pel)) error(2, 0, "parse error when parsing an argument"); return f; } @@ -343,16 +343,16 @@ parse_opt(int key, char* arg, struct argp_state*) break; case OPT_IMPLIED_BY: { - spot::ltl::formula i = parse_formula_arg(arg); + spot::formula i = parse_formula_arg(arg); // a→c∧b→c ≡ (a∨b)→c - implied_by = spot::ltl::formula::Or({implied_by, i}); + implied_by = spot::formula::Or({implied_by, i}); break; } case OPT_IMPLY: { // a→b∧a→c ≡ a→(b∧c) - spot::ltl::formula i = parse_formula_arg(arg); - imply = spot::ltl::formula::And({imply, i}); + spot::formula i = parse_formula_arg(arg); + imply = spot::formula::And({imply, i}); break; } case OPT_LTL: @@ -371,9 +371,9 @@ parse_opt(int key, char* arg, struct argp_state*) case OPT_RELABEL_BOOL: relabeling = (key == OPT_RELABEL_BOOL ? BseRelabeling : ApRelabeling); if (!arg || !strncasecmp(arg, "abc", 6)) - style = spot::ltl::Abc; + style = spot::Abc; else if (!strncasecmp(arg, "pnn", 4)) - style = spot::ltl::Pnn; + style = spot::Pnn; else error(2, 0, "invalid argument for --relabel%s: '%s'", (key == OPT_RELABEL_BOOL ? "-bool" : ""), @@ -404,7 +404,7 @@ parse_opt(int key, char* arg, struct argp_state*) if (arg) unabbreviate += arg; else - unabbreviate += spot::ltl::default_unabbrev_string; + unabbreviate += spot::default_unabbrev_string; break; case OPT_AP_N: ap = true; @@ -438,18 +438,18 @@ parse_opt(int key, char* arg, struct argp_state*) } typedef -std::unordered_set fset_t; +std::unordered_set fset_t; namespace { class ltl_processor: public job_processor { public: - spot::ltl::ltl_simplifier& simpl; + spot::ltl_simplifier& simpl; fset_t unique_set; - spot::ltl::relabeling_map relmap; + spot::relabeling_map relmap; - ltl_processor(spot::ltl::ltl_simplifier& simpl) + ltl_processor(spot::ltl_simplifier& simpl) : simpl(simpl) { } @@ -458,8 +458,8 @@ namespace process_string(const std::string& input, const char* filename = nullptr, int linenum = 0) { - spot::ltl::parse_error_list pel; - spot::ltl::formula f = parse_formula(input, pel); + spot::parse_error_list pel; + spot::formula f = parse_formula(input, pel); if (!f || pel.size() > 0) { @@ -467,7 +467,7 @@ namespace { if (filename) error_at_line(0, 0, filename, linenum, "parse error:"); - spot::ltl::format_parse_errors(std::cerr, input, pel); + spot::format_parse_errors(std::cerr, input, pel); } if (error_style == skip_errors) @@ -489,7 +489,7 @@ namespace } int - process_formula(spot::ltl::formula f, + process_formula(spot::formula f, const char* filename = nullptr, int linenum = 0) { if (opt_max_count >= 0 && match_count >= opt_max_count) @@ -499,14 +499,14 @@ namespace } if (negate) - f = spot::ltl::formula::Not(f); + f = spot::formula::Not(f); if (remove_x) { // If simplification are enabled, we do them before and after. if (simplification_level) f = simpl.simplify(f); - f = spot::ltl::remove_x(f); + f = spot::remove_x(f); } if (simplification_level || boolean_to_isop) @@ -520,13 +520,13 @@ namespace case ApRelabeling: { relmap.clear(); - f = spot::ltl::relabel(f, style, &relmap); + f = spot::relabel(f, style, &relmap); break; } case BseRelabeling: { relmap.clear(); - f = spot::ltl::relabel_bse(f, style, &relmap); + f = spot::relabel_bse(f, style, &relmap); break; } case NoRelabeling: @@ -534,7 +534,7 @@ namespace } if (!unabbreviate.empty()) - f = spot::ltl::unabbreviate(f, unabbreviate.c_str()); + f = spot::unabbreviate(f, unabbreviate.c_str()); if (!excl_ap.empty()) f = excl_ap.constrain(f); @@ -556,14 +556,14 @@ namespace if (matched && (size_min > 0 || size_max >= 0)) { - int l = spot::ltl::length(f); + int l = spot::length(f); matched &= (size_min <= 0) || (l >= size_min); matched &= (size_max < 0) || (l <= size_max); } if (matched && (bsize_min > 0 || bsize_max >= 0)) { - int l = spot::ltl::length_boolone(f); + int l = spot::length_boolone(f); matched &= (bsize_min <= 0) || (l >= bsize_min); matched &= (bsize_max < 0) || (l <= bsize_max); } @@ -606,7 +606,7 @@ namespace && output_format != quiet_output) { // Sort the formulas alphabetically. - std::map m; + std::map m; for (auto& p: relmap) m.emplace(str_psl(p.first), p.second); for (auto& p: m) @@ -641,9 +641,9 @@ main(int argc, char** argv) if (boolean_to_isop && simplification_level == 0) simplification_level = 1; - spot::ltl::ltl_simplifier_options opt(simplification_level); + spot::ltl_simplifier_options opt(simplification_level); opt.boolean_to_isop = boolean_to_isop; - spot::ltl::ltl_simplifier simpl(opt); + spot::ltl_simplifier simpl(opt); ltl_processor processor(simpl); if (processor.run()) diff --git a/src/bin/ltlgrind.cc b/src/bin/ltlgrind.cc index ff70ac21c..83bcfa03a 100644 --- a/src/bin/ltlgrind.cc +++ b/src/bin/ltlgrind.cc @@ -43,7 +43,7 @@ enum { static unsigned mutation_nb = 1; static unsigned max_output = -1U; -static unsigned opt_all = spot::ltl::Mut_All; +static unsigned opt_all = spot::Mut_All; static unsigned mut_opts = 0; static bool opt_sort = false; @@ -96,11 +96,11 @@ namespace { public: int - process_formula(spot::ltl::formula f, const char* filename = nullptr, + process_formula(spot::formula f, const char* filename = nullptr, int linenum = 0) { auto mutations = - spot::ltl::mutate(f, mut_opts, max_output, mutation_nb, opt_sort); + spot::mutate(f, mut_opts, max_output, mutation_nb, opt_sort); for (auto g: mutations) output_formula_checked(g, filename, linenum); return 0; @@ -121,31 +121,31 @@ parse_opt(int key, char* arg, struct argp_state*) break; case OPT_AP2CONST: opt_all = 0; - mut_opts |= spot::ltl::Mut_Ap2Const; + mut_opts |= spot::Mut_Ap2Const; break; case OPT_REMOVE_ONE_AP: opt_all = 0; - mut_opts |= spot::ltl::Mut_Remove_One_Ap; + mut_opts |= spot::Mut_Remove_One_Ap; break; case OPT_REMOVE_MULTOP_OPERANDS: opt_all = 0; - mut_opts |= spot::ltl::Mut_Remove_Multop_Operands; + mut_opts |= spot::Mut_Remove_Multop_Operands; break; case OPT_REMOVE_OPS: opt_all = 0; - mut_opts |= spot::ltl::Mut_Remove_Ops; + mut_opts |= spot::Mut_Remove_Ops; break; case OPT_SPLIT_OPS: opt_all = 0; - mut_opts |= spot::ltl::Mut_Split_Ops; + mut_opts |= spot::Mut_Split_Ops; break; case OPT_REWRITE_OPS: opt_all = 0; - mut_opts |= spot::ltl::Mut_Rewrite_Ops; + mut_opts |= spot::Mut_Rewrite_Ops; break; case OPT_SIMPLIFY_BOUNDS: opt_all = 0; - mut_opts |= spot::ltl::Mut_Simplify_Bounds; + mut_opts |= spot::Mut_Simplify_Bounds; break; case OPT_SORT: opt_sort = true; diff --git a/src/bin/randaut.cc b/src/bin/randaut.cc index a0b2e200c..f55ab6122 100644 --- a/src/bin/randaut.cc +++ b/src/bin/randaut.cc @@ -135,7 +135,7 @@ static const struct argp_child children[] = static const char* opt_acceptance = nullptr; typedef spot::twa_graph::graph_t::edge_storage_t tr_t; typedef std::set> unique_aut_t; -static spot::ltl::atomic_prop_set aprops; +static spot::atomic_prop_set aprops; static range ap_count_given = {-1, -2}; // Must be two different negative val static int opt_seed = 0; static const char* opt_seed_str = "0"; @@ -249,10 +249,10 @@ parse_opt(int key, char* arg, struct argp_state* as) ap_count_given = parse_range(arg); // Create the set once if the count is fixed. if (ap_count_given.min == ap_count_given.max) - aprops = spot::ltl::create_atomic_prop_set(ap_count_given.min); + aprops = spot::create_atomic_prop_set(ap_count_given.min); break; } - aprops.insert(spot::ltl::formula::ap(arg)); + aprops.insert(spot::formula::ap(arg)); break; default: @@ -329,7 +329,7 @@ main(int argc, char** argv) && ap_count_given.min != ap_count_given.max) { int c = spot::rrand(ap_count_given.min, ap_count_given.max); - aprops = spot::ltl::create_atomic_prop_set(c); + aprops = spot::create_atomic_prop_set(c); } int size = opt_states.min; diff --git a/src/bin/randltl.cc b/src/bin/randltl.cc index 54fdeeed9..d32d9cbf2 100644 --- a/src/bin/randltl.cc +++ b/src/bin/randltl.cc @@ -130,7 +130,7 @@ const struct argp_child children[] = { nullptr, 0, nullptr, 0 } }; -spot::ltl::atomic_prop_set aprops; +spot::atomic_prop_set aprops; static int output = OUTPUTLTL; static char* opt_pL = nullptr; static char* opt_pS = nullptr; @@ -209,11 +209,11 @@ parse_opt(int key, char* arg, struct argp_state* as) if (!*endptr && res >= 0) // arg is a number { ap_count_given = true; - aprops = spot::ltl::create_atomic_prop_set(res); + aprops = spot::create_atomic_prop_set(res); break; } } - aprops.insert(spot::ltl::default_environment::instance().require(arg)); + aprops.insert(spot::default_environment::instance().require(arg)); break; default: return ARGP_ERR_UNKNOWN; @@ -241,7 +241,7 @@ main(int argc, char** argv) spot::srand(opt_seed); try { - spot::ltl::randltlgenerator rg + spot::randltlgenerator rg (aprops, [&] (){ spot::option_map opts; @@ -292,7 +292,7 @@ main(int argc, char** argv) while (opt_formulas < 0 || opt_formulas--) { static int count = 0; - spot::ltl::formula f = rg.next(); + spot::formula f = rg.next(); if (!f) { error(2, 0, "failed to generate a new unique formula after %d " \ diff --git a/src/kripke/kripkeexplicit.cc b/src/kripke/kripkeexplicit.cc index 4d1d085d3..4075f3cb8 100644 --- a/src/kripke/kripkeexplicit.cc +++ b/src/kripke/kripkeexplicit.cc @@ -260,7 +260,7 @@ namespace spot } - void kripke_explicit::add_condition(ltl::formula f, std::string on_me) + void kripke_explicit::add_condition(formula f, std::string on_me) { add_conditions(formula_to_bdd(f, get_dict(), this), on_me); } diff --git a/src/kripke/kripkeexplicit.hh b/src/kripke/kripkeexplicit.hh index c432d8beb..3a801d84a 100644 --- a/src/kripke/kripkeexplicit.hh +++ b/src/kripke/kripkeexplicit.hh @@ -151,7 +151,7 @@ namespace spot /// /// \param f the formula to add. /// \param on_me the state where to add. - void add_condition(ltl::formula f, std::string on_me); + void add_condition(formula f, std::string on_me); /// \brief Return map between states and their names. const std::map& diff --git a/src/kripkeparse/kripkeparse.yy b/src/kripkeparse/kripkeparse.yy index d7ff6b26a..27683befa 100644 --- a/src/kripkeparse/kripkeparse.yy +++ b/src/kripkeparse/kripkeparse.yy @@ -40,7 +40,7 @@ typedef std::map formula_cache; } %parse-param {spot::kripke_parse_error_list& error_list} -%parse-param {spot::ltl::environment& parse_environment} +%parse-param {spot::environment& parse_environment} %parse-param {spot::kripke_explicit_ptr& result} %parse-param {formula_cache& fcache} @@ -62,9 +62,9 @@ typedef std::map formula_cache; before parsedecl.hh uses it. */ #include "parsedecl.hh" -using namespace spot::ltl; +using namespace spot; #include - //typedef std::pair pair; + //typedef std::pair pair; } %token STRING UNTERMINATED_STRING IDENT @@ -109,7 +109,7 @@ strident "," condition "," follow_list ";" if (i == fcache.end()) { parse_error_list pel; - formula f = spot::ltl::parse_infix_boolean(*$3, pel, + formula f = spot::parse_infix_boolean(*$3, pel, parse_environment); for (parse_error_list::iterator i = pel.begin(); i != pel.end(); ++i) @@ -207,7 +207,7 @@ namespace spot error_list.push_back (kripke_parse_error(spot::location(), std::string("Cannot open file ") + name)); - return 0; + return nullptr; } formula_cache fcache; auto result = make_kripke_explicit(dict); diff --git a/src/kripkeparse/public.hh b/src/kripkeparse/public.hh index 9bea2491d..457825eb4 100644 --- a/src/kripkeparse/public.hh +++ b/src/kripkeparse/public.hh @@ -41,15 +41,15 @@ namespace spot kripke_parse(const std::string& name, kripke_parse_error_list& error_list, const bdd_dict_ptr& dict, - ltl::environment& env - = ltl::default_environment::instance(), + environment& env + = default_environment::instance(), bool debug = false); /// \brief Format diagnostics produced by spot::kripke_parse. /// \param os Where diagnostics should be output. /// \param filename The filename that should appear in the diagnostics. - /// \param error_list The error list filled by spot::ltl::parse while + /// \param error_list The error list filled by spot::parse while /// parsing \a ltl_string. /// \return \c true if any diagnostic was output. SPOT_API diff --git a/src/ltlparse/fmterror.cc b/src/ltlparse/fmterror.cc index 406ce103d..6c1c3de9c 100644 --- a/src/ltlparse/fmterror.cc +++ b/src/ltlparse/fmterror.cc @@ -28,90 +28,85 @@ namespace spot { - namespace ltl + void + fix_utf8_locations(const std::string& ltl_string, + parse_error_list& error_list) { + // LUT to convert byte positions to utf8 positions. + // (The +2 is to account for position 0, not used, + // and position ltl_string.size()+1 denoting EOS.) + std::vector b2u(ltl_string.size() + 2); - void - fix_utf8_locations(const std::string& ltl_string, - parse_error_list& error_list) + // i will iterate over all utf8 characters between b and e + std::string::const_iterator b = ltl_string.begin(); + std::string::const_iterator i = b; + std::string::const_iterator e = ltl_string.end(); + + unsigned n = 0; // number of current utf8 character + unsigned prev = 0; // last byte of previous utf8 character + while (i != e) + { + utf8::next(i, e); + ++n; + unsigned d = std::distance(b, i); + while (prev < d) + b2u[++prev] = n; + } + b2u[++prev] = ++n; + + // use b2u to update error_list + parse_error_list::iterator it; + for (it = error_list.begin(); it != error_list.end(); ++it) + { + location& l = it->first; + l.begin.column = b2u[l.begin.column]; + l.end.column = b2u[l.end.column]; + } + } + + namespace + { + bool + format_parse_errors_aux(std::ostream& os, + const std::string& ltl_string, + const parse_error_list& error_list) { - // LUT to convert byte positions to utf8 positions. - // (The +2 is to account for position 0, not used, - // and position ltl_string.size()+1 denoting EOS.) - std::vector b2u(ltl_string.size() + 2); - - // i will iterate over all utf8 characters between b and e - std::string::const_iterator b = ltl_string.begin(); - std::string::const_iterator i = b; - std::string::const_iterator e = ltl_string.end(); - - unsigned n = 0; // number of current utf8 character - unsigned prev = 0; // last byte of previous utf8 character - while (i != e) - { - utf8::next(i, e); - ++n; - unsigned d = std::distance(b, i); - while (prev < d) - b2u[++prev] = n; - } - b2u[++prev] = ++n; - - // use b2u to update error_list - parse_error_list::iterator it; + bool printed = false; + parse_error_list::const_iterator it; for (it = error_list.begin(); it != error_list.end(); ++it) { - location& l = it->first; - l.begin.column = b2u[l.begin.column]; - l.end.column = b2u[l.end.column]; - } - } + os << ">>> " << ltl_string << std::endl; + const location& l = it->first; - namespace - { - bool - format_parse_errors_aux(std::ostream& os, - const std::string& ltl_string, - const parse_error_list& error_list) - { - bool printed = false; - parse_error_list::const_iterator it; - for (it = error_list.begin(); it != error_list.end(); ++it) - { - os << ">>> " << ltl_string << std::endl; - const location& l = it->first; - - unsigned n = 1; - for (; n < 4 + l.begin.column; ++n) - os << ' '; - // Write at least one '^', even if begin==end. + unsigned n = 1; + for (; n < 4 + l.begin.column; ++n) + os << ' '; + // Write at least one '^', even if begin==end. + os << '^'; + ++n; + for (; n < 4 + l.end.column; ++n) os << '^'; - ++n; - for (; n < 4 + l.end.column; ++n) - os << '^'; - os << std::endl << it->second << std::endl << std::endl; - printed = true; - } - return printed; + os << std::endl << it->second << std::endl << std::endl; + printed = true; + } + return printed; + } + } + + bool + format_parse_errors(std::ostream& os, + const std::string& ltl_string, + const parse_error_list& error_list) + { + if (utf8::is_valid(ltl_string.begin(), ltl_string.end())) + { + parse_error_list fixed = error_list; + fix_utf8_locations(ltl_string, fixed); + return format_parse_errors_aux(os, ltl_string, fixed); + } + else + { + return format_parse_errors_aux(os, ltl_string, error_list); } - } - - bool - format_parse_errors(std::ostream& os, - const std::string& ltl_string, - const parse_error_list& error_list) - { - if (utf8::is_valid(ltl_string.begin(), ltl_string.end())) - { - parse_error_list fixed = error_list; - fix_utf8_locations(ltl_string, fixed); - return format_parse_errors_aux(os, ltl_string, fixed); - } - else - { - return format_parse_errors_aux(os, ltl_string, error_list); - } - } - } } diff --git a/src/ltlparse/ltlparse.yy b/src/ltlparse/ltlparse.yy index a5bbc81bc..e54af8351 100644 --- a/src/ltlparse/ltlparse.yy +++ b/src/ltlparse/ltlparse.yy @@ -27,7 +27,7 @@ %debug %error-verbose %expect 0 -%lex-param { spot::ltl::parse_error_list& error_list } +%lex-param { spot::parse_error_list& error_list } %define api.location.type "spot::location" %code requires @@ -41,13 +41,13 @@ struct minmax_t { unsigned min, max; }; } -%parse-param {spot::ltl::parse_error_list &error_list} -%parse-param {spot::ltl::environment &parse_environment} -%parse-param {spot::ltl::formula &result} +%parse-param {spot::parse_error_list &error_list} +%parse-param {spot::environment &parse_environment} +%parse-param {spot::formula &result} %union { std::string* str; - const spot::ltl::fnode* ltl; + const spot::fnode* ltl; unsigned num; minmax_t minmax; } @@ -57,7 +57,7 @@ We mut ensure that YYSTYPE is declared (by the above %union) before parsedecl.hh uses it. */ #include "parsedecl.hh" -using namespace spot::ltl; +using namespace spot; #define missing_right_op_msg(op, str) \ error_list.emplace_back(op, \ @@ -94,10 +94,10 @@ using namespace spot::ltl; static formula try_recursive_parse(const std::string& str, const spot::location& location, - spot::ltl::environment& env, + spot::environment& env, bool debug, parser_type type, - spot::ltl::parse_error_list& error_list) + spot::parse_error_list& error_list) { // We want to parse a U (b U c) as two until operators applied // to the atomic propositions a, b, and c. We also want to @@ -120,18 +120,18 @@ using namespace spot::ltl; return nullptr; } - spot::ltl::parse_error_list suberror; + spot::parse_error_list suberror; formula f; switch (type) { case parser_sere: - f = spot::ltl::parse_infix_sere(str, suberror, env, debug, true); + f = spot::parse_infix_sere(str, suberror, env, debug, true); break; case parser_bool: - f = spot::ltl::parse_infix_boolean(str, suberror, env, debug, true); + f = spot::parse_infix_boolean(str, suberror, env, debug, true); break; case parser_ltl: - f = spot::ltl::parse_infix_psl(str, suberror, env, debug, true); + f = spot::parse_infix_psl(str, suberror, env, debug, true); break; } @@ -237,8 +237,8 @@ using namespace spot::ltl; %destructor { $$->destroy(); } %printer { debug_stream() << *$$; } -%printer { spot::ltl::print_psl(debug_stream(), formula($$)); } -%printer { spot::ltl::print_sere(debug_stream(), formula($$)); } sere bracedsere +%printer { print_psl(debug_stream(), formula($$)); } +%printer { print_sere(debug_stream(), formula($$)); } sere bracedsere %printer { debug_stream() << $$; } %printer { debug_stream() << $$.min << ".." << $$.max; } @@ -993,95 +993,91 @@ ltlyy::parser::error(const location_type& location, const std::string& message) namespace spot { - namespace ltl + formula + parse_infix_psl(const std::string& ltl_string, + parse_error_list& error_list, + environment& env, + bool debug, bool lenient) { - formula - parse_infix_psl(const std::string& ltl_string, - parse_error_list& error_list, - environment& env, - bool debug, bool lenient) - { - formula result = nullptr; - flex_set_buffer(ltl_string, - ltlyy::parser::token::START_LTL, - lenient); - ltlyy::parser parser(error_list, env, result); - parser.set_debug_level(debug); - parser.parse(); - flex_unset_buffer(); - return result; - } + formula result = nullptr; + flex_set_buffer(ltl_string, + ltlyy::parser::token::START_LTL, + lenient); + ltlyy::parser parser(error_list, env, result); + parser.set_debug_level(debug); + parser.parse(); + flex_unset_buffer(); + return result; + } - formula - parse_infix_boolean(const std::string& ltl_string, - parse_error_list& error_list, - environment& env, - bool debug, bool lenient) - { - formula result = nullptr; - flex_set_buffer(ltl_string, - ltlyy::parser::token::START_BOOL, - lenient); - ltlyy::parser parser(error_list, env, result); - parser.set_debug_level(debug); - parser.parse(); - flex_unset_buffer(); - return result; - } + formula + parse_infix_boolean(const std::string& ltl_string, + parse_error_list& error_list, + environment& env, + bool debug, bool lenient) + { + formula result = nullptr; + flex_set_buffer(ltl_string, + ltlyy::parser::token::START_BOOL, + lenient); + ltlyy::parser parser(error_list, env, result); + parser.set_debug_level(debug); + parser.parse(); + flex_unset_buffer(); + return result; + } - formula - parse_prefix_ltl(const std::string& ltl_string, - parse_error_list& error_list, - environment& env, - bool debug) - { - formula result = nullptr; - flex_set_buffer(ltl_string, - ltlyy::parser::token::START_LBT, - false); - ltlyy::parser parser(error_list, env, result); - parser.set_debug_level(debug); - parser.parse(); - flex_unset_buffer(); - return result; - } + formula + parse_prefix_ltl(const std::string& ltl_string, + parse_error_list& error_list, + environment& env, + bool debug) + { + formula result = nullptr; + flex_set_buffer(ltl_string, + ltlyy::parser::token::START_LBT, + false); + ltlyy::parser parser(error_list, env, result); + parser.set_debug_level(debug); + parser.parse(); + flex_unset_buffer(); + return result; + } - formula - parse_infix_sere(const std::string& sere_string, - parse_error_list& error_list, - environment& env, - bool debug, - bool lenient) - { - formula result = nullptr; - flex_set_buffer(sere_string, - ltlyy::parser::token::START_SERE, - lenient); - ltlyy::parser parser(error_list, env, result); - parser.set_debug_level(debug); - parser.parse(); - flex_unset_buffer(); - return result; - } - - formula - parse_formula(const std::string& ltl_string, environment& env) - { - parse_error_list pel; - formula f = parse_infix_psl(ltl_string, pel, env); - std::ostringstream s; - if (format_parse_errors(s, ltl_string, pel)) - { - parse_error_list pel2; - formula g = parse_prefix_ltl(ltl_string, pel2, env); - if (pel2.empty()) - return g; - else - throw parse_error(s.str()); - } - return f; - } + formula + parse_infix_sere(const std::string& sere_string, + parse_error_list& error_list, + environment& env, + bool debug, + bool lenient) + { + formula result = nullptr; + flex_set_buffer(sere_string, + ltlyy::parser::token::START_SERE, + lenient); + ltlyy::parser parser(error_list, env, result); + parser.set_debug_level(debug); + parser.parse(); + flex_unset_buffer(); + return result; + } + formula + parse_formula(const std::string& ltl_string, environment& env) + { + parse_error_list pel; + formula f = parse_infix_psl(ltl_string, pel, env); + std::ostringstream s; + if (format_parse_errors(s, ltl_string, pel)) + { + parse_error_list pel2; + formula g = parse_prefix_ltl(ltl_string, pel2, env); + if (pel2.empty()) + return g; + else + throw parse_error(s.str()); + } + return f; } } diff --git a/src/ltlparse/ltlscan.ll b/src/ltlparse/ltlscan.ll index 89f9b3f5f..f207009f7 100644 --- a/src/ltlparse/ltlscan.ll +++ b/src/ltlparse/ltlscan.ll @@ -99,7 +99,7 @@ eol2 (\n\r)+|(\r\n)+ <> { BEGIN(orig_cond); error_list.push_back( - spot::ltl::one_parse_error(*yylloc, + spot::one_parse_error(*yylloc, "unclosed comment")); return 0; } @@ -141,7 +141,7 @@ eol2 (\n\r)+|(\r\n)+ unput(')'); if (!missing_parent) error_list.push_back( - spot::ltl::one_parse_error(*yylloc, + spot::one_parse_error(*yylloc, "missing closing parenthese")); missing_parent = true; } @@ -195,7 +195,7 @@ eol2 (\n\r)+|(\r\n)+ unput(')'); if (!missing_parent) error_list.push_back( - spot::ltl::one_parse_error(*yylloc, + spot::one_parse_error(*yylloc, "missing closing brace")); missing_parent = true; } @@ -238,7 +238,7 @@ eol2 (\n\r)+|(\r\n)+ if (errno || yylval->num != n) { error_list.push_back( - spot::ltl::one_parse_error(*yylloc, + spot::one_parse_error(*yylloc, "value too large ignored")); // Skip this number and read next token yylloc->step(); @@ -347,7 +347,7 @@ eol2 (\n\r)+|(\r\n)+ [^\\\"\n\r]+ s.append(yytext, yyleng); <> { error_list.push_back( - spot::ltl::one_parse_error(*yylloc, + spot::one_parse_error(*yylloc, "unclosed string")); BEGIN(orig_cond); yylval->str = new std::string(s); diff --git a/src/ltlparse/parsedecl.hh b/src/ltlparse/parsedecl.hh index 7d599bf31..46345ad9d 100644 --- a/src/ltlparse/parsedecl.hh +++ b/src/ltlparse/parsedecl.hh @@ -28,7 +28,7 @@ # define YY_DECL \ int ltlyylex (ltlyy::parser::semantic_type *yylval, \ spot::location *yylloc, \ - spot::ltl::parse_error_list& error_list) + spot::parse_error_list& error_list) YY_DECL; void flex_set_buffer(const std::string& buf, int start_tok, bool lenient); diff --git a/src/ltlparse/public.hh b/src/ltlparse/public.hh index 4f41ea695..125587246 100644 --- a/src/ltlparse/public.hh +++ b/src/ltlparse/public.hh @@ -32,180 +32,177 @@ namespace spot { - namespace ltl - { - /// \addtogroup ltl_io - /// @{ + /// \addtogroup ltl_io + /// @{ #ifndef SWIG - /// \brief A parse diagnostic with its location. - typedef std::pair one_parse_error; - /// \brief A list of parser diagnostics, as filled by parse. - typedef std::list parse_error_list; + /// \brief A parse diagnostic with its location. + typedef std::pair one_parse_error; + /// \brief A list of parser diagnostics, as filled by parse. + typedef std::list parse_error_list; #else - // Turn parse_error_list into an opaque type for Swig. - struct parse_error_list {}; + // Turn parse_error_list into an opaque type for Swig. + struct parse_error_list {}; #endif - /// \brief Build a formula from an LTL string. - /// \param ltl_string The string to parse. - /// \param error_list A list that will be filled with - /// parse errors that occured during parsing. - /// \param env The environment into which parsing should take place. - /// \param debug When true, causes the parser to trace its execution. - /// \param lenient When true, parenthesized blocks that cannot be - /// parsed as subformulas will be considered as - /// atomic propositions. - /// \return A formula built from \a ltl_string, or - /// formula(nullptr) if the input was unparsable. - /// - /// Note that the parser usually tries to recover from errors. It can - /// return a non zero value even if it encountered error during the - /// parsing of \a ltl_string. If you want to make sure \a ltl_string - /// was parsed succesfully, check \a error_list for emptiness. - /// - /// \warning This function is not reentrant. - SPOT_API - formula parse_infix_psl(const std::string& ltl_string, - parse_error_list& error_list, - environment& env = - default_environment::instance(), - bool debug = false, - bool lenient = false); + /// \brief Build a formula from an LTL string. + /// \param ltl_string The string to parse. + /// \param error_list A list that will be filled with + /// parse errors that occured during parsing. + /// \param env The environment into which parsing should take place. + /// \param debug When true, causes the parser to trace its execution. + /// \param lenient When true, parenthesized blocks that cannot be + /// parsed as subformulas will be considered as + /// atomic propositions. + /// \return A formula built from \a ltl_string, or + /// formula(nullptr) if the input was unparsable. + /// + /// Note that the parser usually tries to recover from errors. It can + /// return a non zero value even if it encountered error during the + /// parsing of \a ltl_string. If you want to make sure \a ltl_string + /// was parsed succesfully, check \a error_list for emptiness. + /// + /// \warning This function is not reentrant. + SPOT_API + formula parse_infix_psl(const std::string& ltl_string, + parse_error_list& error_list, + environment& env = + default_environment::instance(), + bool debug = false, + bool lenient = false); - /// \brief Build a Boolean formula from a string. - /// \param ltl_string The string to parse. - /// \param error_list A list that will be filled with - /// parse errors that occured during parsing. - /// \param env The environment into which parsing should take place. - /// \param debug When true, causes the parser to trace its execution. - /// \param lenient When true, parenthesized blocks that cannot be - /// parsed as subformulas will be considered as - /// atomic propositions. - /// \return A formula built from \a ltl_string, or - /// formula(nullptr) if the input was unparsable. - /// - /// Note that the parser usually tries to recover from errors. It can - /// return a non zero value even if it encountered error during the - /// parsing of \a ltl_string. If you want to make sure \a ltl_string - /// was parsed succesfully, check \a error_list for emptiness. - /// - /// \warning This function is not reentrant. - SPOT_API - formula parse_infix_boolean(const std::string& ltl_string, - parse_error_list& error_list, - environment& env = - default_environment::instance(), - bool debug = false, - bool lenient = false); + /// \brief Build a Boolean formula from a string. + /// \param ltl_string The string to parse. + /// \param error_list A list that will be filled with + /// parse errors that occured during parsing. + /// \param env The environment into which parsing should take place. + /// \param debug When true, causes the parser to trace its execution. + /// \param lenient When true, parenthesized blocks that cannot be + /// parsed as subformulas will be considered as + /// atomic propositions. + /// \return A formula built from \a ltl_string, or + /// formula(nullptr) if the input was unparsable. + /// + /// Note that the parser usually tries to recover from errors. It can + /// return a non zero value even if it encountered error during the + /// parsing of \a ltl_string. If you want to make sure \a ltl_string + /// was parsed succesfully, check \a error_list for emptiness. + /// + /// \warning This function is not reentrant. + SPOT_API + formula parse_infix_boolean(const std::string& ltl_string, + parse_error_list& error_list, + environment& env = + default_environment::instance(), + bool debug = false, + bool lenient = false); - /// \brief Build a formula from an LTL string in LBT's format. - /// \param ltl_string The string to parse. - /// \param error_list A list that will be filled with - /// parse errors that occured during parsing. - /// \param env The environment into which parsing should take place. - /// \param debug When true, causes the parser to trace its execution. - /// \return A formula built from \a ltl_string, or - /// formula(nullptr) if the input was unparsable. - /// - /// Note that the parser usually tries to recover from errors. It can - /// return an non zero value even if it encountered error during the - /// parsing of \a ltl_string. If you want to make sure \a ltl_string - /// was parsed succesfully, check \a error_list for emptiness. - /// - /// The LBT syntax, also used by the lbtt and scheck tools, is - /// extended to support W, and M operators (as done in lbtt), and - /// double-quoted atomic propositions that do not start with 'p'. - /// - /// \warning This function is not reentrant. - SPOT_API - formula parse_prefix_ltl(const std::string& ltl_string, - parse_error_list& error_list, - environment& env = - default_environment::instance(), - bool debug = false); + /// \brief Build a formula from an LTL string in LBT's format. + /// \param ltl_string The string to parse. + /// \param error_list A list that will be filled with + /// parse errors that occured during parsing. + /// \param env The environment into which parsing should take place. + /// \param debug When true, causes the parser to trace its execution. + /// \return A formula built from \a ltl_string, or + /// formula(nullptr) if the input was unparsable. + /// + /// Note that the parser usually tries to recover from errors. It can + /// return an non zero value even if it encountered error during the + /// parsing of \a ltl_string. If you want to make sure \a ltl_string + /// was parsed succesfully, check \a error_list for emptiness. + /// + /// The LBT syntax, also used by the lbtt and scheck tools, is + /// extended to support W, and M operators (as done in lbtt), and + /// double-quoted atomic propositions that do not start with 'p'. + /// + /// \warning This function is not reentrant. + SPOT_API + formula parse_prefix_ltl(const std::string& ltl_string, + parse_error_list& error_list, + environment& env = + default_environment::instance(), + bool debug = false); - /// \brief A simple wrapper to parse_infix_psl() and parse_prefix_ltl(). - /// - /// This is mostly meant for interactive use. It first tries - /// parse_infix_psl(); if this fails it tries parse_prefix_ltl(); - /// and if both fails it returns the errors of the first call to - /// parse_infix_psl() as a parse_error exception. - SPOT_API formula - parse_formula(const std::string& ltl_string, - environment& env = default_environment::instance()); + /// \brief A simple wrapper to parse_infix_psl() and parse_prefix_ltl(). + /// + /// This is mostly meant for interactive use. It first tries + /// parse_infix_psl(); if this fails it tries parse_prefix_ltl(); + /// and if both fails it returns the errors of the first call to + /// parse_infix_psl() as a parse_error exception. + SPOT_API formula + parse_formula(const std::string& ltl_string, + environment& env = default_environment::instance()); - /// \brief Build a formula from a string representing a SERE. - /// \param sere_string The string to parse. - /// \param error_list A list that will be filled with - /// parse errors that occured during parsing. - /// \param env The environment into which parsing should take place. - /// \param debug When true, causes the parser to trace its execution. - /// \param lenient When true, parenthesized blocks that cannot be - /// parsed as subformulas will be considered as - /// atomic propositions. - /// \return A formula built from \a sere_string, or - /// formula(0) if the input was unparsable. - /// - /// Note that the parser usually tries to recover from errors. It can - /// return an non zero value even if it encountered error during the - /// parsing of \a ltl_string. If you want to make sure \a ltl_string - /// was parsed succesfully, check \a error_list for emptiness. - /// - /// \warning This function is not reentrant. - SPOT_API - formula parse_infix_sere(const std::string& sere_string, - parse_error_list& error_list, - environment& env = - default_environment::instance(), - bool debug = false, - bool lenient = false); + /// \brief Build a formula from a string representing a SERE. + /// \param sere_string The string to parse. + /// \param error_list A list that will be filled with + /// parse errors that occured during parsing. + /// \param env The environment into which parsing should take place. + /// \param debug When true, causes the parser to trace its execution. + /// \param lenient When true, parenthesized blocks that cannot be + /// parsed as subformulas will be considered as + /// atomic propositions. + /// \return A formula built from \a sere_string, or + /// formula(0) if the input was unparsable. + /// + /// Note that the parser usually tries to recover from errors. It can + /// return an non zero value even if it encountered error during the + /// parsing of \a ltl_string. If you want to make sure \a ltl_string + /// was parsed succesfully, check \a error_list for emptiness. + /// + /// \warning This function is not reentrant. + SPOT_API + formula parse_infix_sere(const std::string& sere_string, + parse_error_list& error_list, + environment& env = + default_environment::instance(), + bool debug = false, + bool lenient = false); - /// \brief Format diagnostics produced by spot::ltl::parse - /// or spot::ltl::ratexp - /// - /// If the string is utf8 encoded, spot::ltl::fix_utf8_locations() - /// will be used to report correct utf8 locations (assuming the - /// output is utf8 aware). Nonetheless, the supplied \a - /// error_list will not be modified. - /// - /// \param os Where diagnostics should be output. - /// \param input_string The string that were parsed. - /// \param error_list The error list filled by spot::ltl::parse - /// or spot::ltl::parse_sere while parsing \a input_string. - /// \return \c true iff any diagnostic was output. - SPOT_API - bool format_parse_errors(std::ostream& os, - const std::string& input_string, - const parse_error_list& error_list); + /// \brief Format diagnostics produced by spot::parse + /// or spot::ratexp + /// + /// If the string is utf8 encoded, spot::fix_utf8_locations() + /// will be used to report correct utf8 locations (assuming the + /// output is utf8 aware). Nonetheless, the supplied \a + /// error_list will not be modified. + /// + /// \param os Where diagnostics should be output. + /// \param input_string The string that were parsed. + /// \param error_list The error list filled by spot::parse + /// or spot::parse_sere while parsing \a input_string. + /// \return \c true iff any diagnostic was output. + SPOT_API + bool format_parse_errors(std::ostream& os, + const std::string& input_string, + const parse_error_list& error_list); - /// \brief Fix location of diagnostics assuming the input is utf8. - /// - /// The different parser functions return a parse_error_list that - /// contain locations specified at the byte level. Although these - /// parser recognize some utf8 characters they only work byte by - /// byte and will report positions by counting byte. - /// - /// This function fixes the positions returned by the parser to - /// look correct when the string is interpreted as a utf8-encoded - /// string. - /// - /// It is invalid to call this function on a string that is not - /// valid utf8. - /// - /// You should NOT call this function before calling - /// spot::ltl::format_parse_errors() because it is already called - /// inside if needed. You may need this function only if you want - /// to write your own error reporting code. - /// - /// \param input_string The string that were parsed. - /// \param error_list The error list filled by spot::ltl::parse - /// or spot::ltl::parse_sere while parsing \a input_string. - SPOT_API - void - fix_utf8_locations(const std::string& input_string, - parse_error_list& error_list); + /// \brief Fix location of diagnostics assuming the input is utf8. + /// + /// The different parser functions return a parse_error_list that + /// contain locations specified at the byte level. Although these + /// parser recognize some utf8 characters they only work byte by + /// byte and will report positions by counting byte. + /// + /// This function fixes the positions returned by the parser to + /// look correct when the string is interpreted as a utf8-encoded + /// string. + /// + /// It is invalid to call this function on a string that is not + /// valid utf8. + /// + /// You should NOT call this function before calling + /// spot::format_parse_errors() because it is already called + /// inside if needed. You may need this function only if you want + /// to write your own error reporting code. + /// + /// \param input_string The string that were parsed. + /// \param error_list The error list filled by spot::parse + /// or spot::parse_sere while parsing \a input_string. + SPOT_API + void + fix_utf8_locations(const std::string& input_string, + parse_error_list& error_list); - /// @} - } + /// @} } diff --git a/src/parseaut/parseaut.yy b/src/parseaut/parseaut.yy index 57d139b97..76bda205e 100644 --- a/src/parseaut/parseaut.yy +++ b/src/parseaut/parseaut.yy @@ -44,7 +44,7 @@ typedef std::map map_t; /* Cache parsed formulae. Labels on arcs are frequently identical - and it would be a waste of time to parse them to ltl::formula + and it would be a waste of time to parse them to formula over and over, and to register all their atomic_propositions in the bdd_dict. Keep the bdd result around so we can reuse it. */ @@ -71,7 +71,7 @@ spot::location used_loc; }; spot::parsed_aut_ptr h; - spot::ltl::environment* env; + spot::environment* env; formula_cache fcache; named_tgba_t* namer = nullptr; spot::acc_mapper_int* acc_mapper = nullptr; @@ -1403,8 +1403,8 @@ nc-formula: nc-formula-or-ident auto i = res.fcache.find(*$1); if (i == res.fcache.end()) { - spot::ltl::parse_error_list pel; - auto f = spot::ltl::parse_infix_boolean(*$1, pel, *res.env, + spot::parse_error_list pel; + auto f = spot::parse_infix_boolean(*$1, pel, *res.env, debug_level(), true); for (auto& j: pel) { @@ -1578,8 +1578,8 @@ lbtt-acc: { $$ = 0U; } } lbtt-guard: STRING { - spot::ltl::parse_error_list pel; - auto f = spot::ltl::parse_prefix_ltl(*$1, pel, *res.env); + spot::parse_error_list pel; + auto f = spot::parse_prefix_ltl(*$1, pel, *res.env); if (!f || !pel.empty()) { std::string s = "failed to parse guard: "; @@ -1884,7 +1884,7 @@ namespace spot parsed_aut_ptr automaton_stream_parser::parse(parse_aut_error_list& error_list, const bdd_dict_ptr& dict, - ltl::environment& env, + environment& env, bool debug) { restart: @@ -1927,7 +1927,7 @@ namespace spot twa_graph_ptr automaton_stream_parser::parse_strict(const bdd_dict_ptr& dict, - ltl::environment& env, + environment& env, bool debug) { parse_aut_error_list pel; diff --git a/src/parseaut/public.hh b/src/parseaut/public.hh index 31cfbb4d6..f43a8611a 100644 --- a/src/parseaut/public.hh +++ b/src/parseaut/public.hh @@ -79,13 +79,13 @@ namespace spot ~automaton_stream_parser(); parsed_aut_ptr parse(parse_aut_error_list& error_list, const bdd_dict_ptr& dict, - ltl::environment& env = - ltl::default_environment::instance(), + environment& env = + default_environment::instance(), bool debug = false); // Raises a parse_error on any syntax error twa_graph_ptr parse_strict(const bdd_dict_ptr& dict, - ltl::environment& env = - ltl::default_environment::instance(), + environment& env = + default_environment::instance(), bool debug = false); }; @@ -120,7 +120,7 @@ namespace spot parse_aut(const std::string& filename, parse_aut_error_list& error_list, const bdd_dict_ptr& dict, - ltl::environment& env = ltl::default_environment::instance(), + environment& env = default_environment::instance(), bool debug = false) { try @@ -138,7 +138,7 @@ namespace spot /// \brief Format diagnostics produced by spot::parse_aut. /// \param os Where diagnostics should be output. /// \param filename The filename that should appear in the diagnostics. - /// \param error_list The error list filled by spot::ltl::parse while + /// \param error_list The error list filled by spot::parse while /// parsing \a ltl_string. /// \return \c true iff any diagnostic was output. SPOT_API bool diff --git a/src/tests/checkpsl.cc b/src/tests/checkpsl.cc index a6b407704..bdbd5195b 100644 --- a/src/tests/checkpsl.cc +++ b/src/tests/checkpsl.cc @@ -60,13 +60,13 @@ main(int argc, char** argv) if (s.empty() || s[0] == '#') // Skip comments continue; - spot::ltl::parse_error_list pe; - auto fpos = spot::ltl::parse_infix_psl(s, pe); + spot::parse_error_list pe; + auto fpos = spot::parse_infix_psl(s, pe); - if (spot::ltl::format_parse_errors(std::cerr, s, pe)) + if (spot::format_parse_errors(std::cerr, s, pe)) return 2; - auto fneg = spot::ltl::formula::Not(fpos); + auto fneg = spot::formula::Not(fpos); { auto apos = scc_filter(ltl_to_tgba_fm(fpos, d)); @@ -104,6 +104,6 @@ main(int argc, char** argv) } } - assert(spot::ltl::fnode::instances_check()); + assert(spot::fnode::instances_check()); return 0; } diff --git a/src/tests/checkta.cc b/src/tests/checkta.cc index 3155cc077..8e3eabe73 100644 --- a/src/tests/checkta.cc +++ b/src/tests/checkta.cc @@ -83,10 +83,10 @@ main(int argc, char** argv) if (s.empty() || s[0] == '#') // Skip comments continue; - spot::ltl::parse_error_list pe; - auto f = spot::ltl::parse_infix_psl(s, pe); + spot::parse_error_list pe; + auto f = spot::parse_infix_psl(s, pe); - if (spot::ltl::format_parse_errors(std::cerr, s, pe)) + if (spot::format_parse_errors(std::cerr, s, pe)) return 2; @@ -220,6 +220,6 @@ main(int argc, char** argv) } } - assert(spot::ltl::fnode::instances_check()); + assert(spot::fnode::instances_check()); return 0; } diff --git a/src/tests/complementation.cc b/src/tests/complementation.cc index f354705b1..63a3e0e5c 100644 --- a/src/tests/complementation.cc +++ b/src/tests/complementation.cc @@ -119,7 +119,7 @@ int main(int argc, char* argv[]) auto dict = spot::make_bdd_dict(); if (print_automaton || print_safra) { - spot::ltl::environment& env(spot::ltl::default_environment::instance()); + spot::environment& env(spot::default_environment::instance()); spot::parse_aut_error_list pel; auto h = spot::parse_aut(file, pel, dict, env); if (spot::format_parse_aut_errors(std::cerr, file, pel)) @@ -147,10 +147,10 @@ int main(int argc, char* argv[]) } else if (print_formula) { - spot::ltl::parse_error_list p1; - auto f1 = spot::ltl::parse_infix_psl(file, p1); + spot::parse_error_list p1; + auto f1 = spot::parse_infix_psl(file, p1); - if (spot::ltl::format_parse_errors(std::cerr, file, p1)) + if (spot::format_parse_errors(std::cerr, file, p1)) return 2; auto a = spot::ltl_to_tgba_fm(f1, dict); @@ -162,14 +162,14 @@ int main(int argc, char* argv[]) else if (stats) { spot::twa_graph_ptr a; - spot::ltl::formula f1 = nullptr; + spot::formula f1 = nullptr; if (formula) { - spot::ltl::parse_error_list p1; - f1 = spot::ltl::parse_infix_psl(file, p1); + spot::parse_error_list p1; + f1 = spot::parse_infix_psl(file, p1); - if (spot::ltl::format_parse_errors(std::cerr, file, p1)) + if (spot::format_parse_errors(std::cerr, file, p1)) return 2; a = spot::ltl_to_tgba_fm(f1, dict); @@ -177,7 +177,7 @@ int main(int argc, char* argv[]) else { spot::parse_aut_error_list pel; - spot::ltl::environment& env(spot::ltl::default_environment::instance()); + spot::environment& env(spot::default_environment::instance()); auto h = spot::parse_aut(file, pel, dict, env); if (spot::format_parse_aut_errors(std::cerr, file, pel)) return 2; @@ -209,7 +209,7 @@ int main(int argc, char* argv[]) if (formula) { - auto a2 = spot::ltl_to_tgba_fm(spot::ltl::formula::Not(f1), dict); + auto a2 = spot::ltl_to_tgba_fm(spot::formula::Not(f1), dict); spot::tgba_statistics a_size = spot::stats_reachable(a2); std::cout << "Not Formula: " << a_size.states << ", " @@ -220,14 +220,14 @@ int main(int argc, char* argv[]) } else { - spot::ltl::parse_error_list p1; - auto f1 = spot::ltl::parse_infix_psl(file, p1); + spot::parse_error_list p1; + auto f1 = spot::parse_infix_psl(file, p1); - if (spot::ltl::format_parse_errors(std::cerr, file, p1)) + if (spot::format_parse_errors(std::cerr, file, p1)) return 2; auto Af = spot::ltl_to_tgba_fm(f1, dict); - auto nf1 = spot::ltl::formula::Not(f1); + auto nf1 = spot::formula::Not(f1); auto Anf = spot::ltl_to_tgba_fm(nf1, dict); auto nAf = spot::make_safra_complement(Af); auto nAnf = spot::make_safra_complement(Anf); diff --git a/src/tests/consterm.cc b/src/tests/consterm.cc index 5b566b0f1..6ffb3ff3e 100644 --- a/src/tests/consterm.cc +++ b/src/tests/consterm.cc @@ -58,9 +58,9 @@ main(int argc, char **argv) std::getline(ss, form, ','); ss >> expected; - spot::ltl::parse_error_list p1; - auto f1 = spot::ltl::parse_infix_sere(form, p1); - if (spot::ltl::format_parse_errors(std::cerr, form, p1)) + spot::parse_error_list p1; + auto f1 = spot::parse_infix_sere(form, p1); + if (spot::format_parse_errors(std::cerr, form, p1)) return 2; bool b = f1.accepts_eword(); @@ -73,6 +73,6 @@ main(int argc, char **argv) } } - assert(spot::ltl::fnode::instances_check()); + assert(spot::fnode::instances_check()); return 0; } diff --git a/src/tests/emptchk.cc b/src/tests/emptchk.cc index c8490a3cc..c39aa9893 100644 --- a/src/tests/emptchk.cc +++ b/src/tests/emptchk.cc @@ -89,9 +89,9 @@ main(int argc, char** argv) int runs = atoi(tokens[0].c_str()); - spot::ltl::parse_error_list pe; - auto f = spot::ltl::parse_infix_psl(tokens[1], pe); - if (spot::ltl::format_parse_errors(std::cerr, tokens[1], pe)) + spot::parse_error_list pe; + auto f = spot::parse_infix_psl(tokens[1], pe); + if (spot::format_parse_errors(std::cerr, tokens[1], pe)) return 2; auto d = spot::make_bdd_dict(); @@ -199,6 +199,6 @@ main(int argc, char** argv) } } - assert(spot::ltl::fnode::instances_check()); + assert(spot::fnode::instances_check()); return 0; } diff --git a/src/tests/equalsf.cc b/src/tests/equalsf.cc index c46385423..5eefee0d7 100644 --- a/src/tests/equalsf.cc +++ b/src/tests/equalsf.cc @@ -95,47 +95,47 @@ main(int argc, char** argv) return 2; } - spot::ltl::parse_error_list p2; - auto f2 = spot::ltl::parse_infix_psl(formulas[size - 1], p2); + spot::parse_error_list p2; + auto f2 = spot::parse_infix_psl(formulas[size - 1], p2); - if (spot::ltl::format_parse_errors(std::cerr, formulas[size - 1], p2)) + if (spot::format_parse_errors(std::cerr, formulas[size - 1], p2)) return 2; for (unsigned n = 0; n < size - 1; ++n) { - spot::ltl::parse_error_list p1; - auto f1 = spot::ltl::parse_infix_psl(formulas[n], p1); + spot::parse_error_list p1; + auto f1 = spot::parse_infix_psl(formulas[n], p1); if (check_first && - spot::ltl::format_parse_errors(std::cerr, formulas[n], p1)) + spot::format_parse_errors(std::cerr, formulas[n], p1)) return 2; int exit_code = 0; { #if defined UNABBREV || defined NENOFORM - spot::ltl::formula tmp; + spot::formula tmp; #endif #ifdef UNABBREV tmp = f1; - f1 = spot::ltl::unabbreviate(f1, UNABBREV); + f1 = spot::unabbreviate(f1, UNABBREV); f1.dump(std::cout) << std::endl; #endif #ifdef NENOFORM tmp = f1; - f1 = spot::ltl::negative_normal_form(f1); + f1 = spot::negative_normal_form(f1); f1.dump(std::cout) << std::endl; #endif #ifdef REDUC - spot::ltl::ltl_simplifier_options opt(true, true, true, + spot::ltl_simplifier_options opt(true, true, true, false, false); # ifdef EVENT_UNIV opt.favor_event_univ = true; # endif - spot::ltl::ltl_simplifier simp(opt); + spot::ltl_simplifier simp(opt); { - spot::ltl::formula tmp; + spot::formula tmp; tmp = f1; f1 = simp.simplify(f1); @@ -143,18 +143,18 @@ main(int argc, char** argv) { std::cerr << "Source and simplified formulae are not equivalent!\n"; - spot::ltl::print_psl(std::cerr << "Simplified: ", f1) << '\n'; + spot::print_psl(std::cerr << "Simplified: ", f1) << '\n'; exit_code = 1; } } f1.dump(std::cout) << std::endl; #endif #ifdef REDUC_TAU - spot::ltl::ltl_simplifier_options opt(false, false, false, + spot::ltl_simplifier_options opt(false, false, false, true, false); - spot::ltl::ltl_simplifier simp(opt); + spot::ltl_simplifier simp(opt); { - spot::ltl::formula tmp; + spot::formula tmp; tmp = f1; f1 = simp.simplify(f1); @@ -162,18 +162,18 @@ main(int argc, char** argv) { std::cerr << "Source and simplified formulae are not equivalent!\n"; - spot::ltl::print_psl(std::cerr << "Simplified: ", f1) << '\n'; + spot::print_psl(std::cerr << "Simplified: ", f1) << '\n'; exit_code = 1; } } f1.dump(std::cout) << std::endl; #endif #ifdef REDUC_TAUSTR - spot::ltl::ltl_simplifier_options opt(false, false, false, + spot::ltl_simplifier_options opt(false, false, false, true, true); - spot::ltl::ltl_simplifier simp(opt); + spot::ltl_simplifier simp(opt); { - spot::ltl::formula tmp; + spot::formula tmp; tmp = f1; f1 = simp.simplify(f1); @@ -181,7 +181,7 @@ main(int argc, char** argv) { std::cerr << "Source and simplified formulae are not equivalent!\n"; - spot::ltl::print_psl(std::cerr << "Simplified: ", f1) << '\n'; + spot::print_psl(std::cerr << "Simplified: ", f1) << '\n'; exit_code = 1; } } @@ -191,7 +191,7 @@ main(int argc, char** argv) exit_code |= f1 != f2; #if (!defined(REDUC) && !defined(REDUC_TAU) && !defined(REDUC_TAUSTR)) - spot::ltl::ltl_simplifier simp; + spot::ltl_simplifier simp; #endif if (!simp.are_equivalent(f1, f2)) @@ -215,6 +215,6 @@ main(int argc, char** argv) } } - assert(spot::ltl::fnode::instances_check()); + assert(spot::fnode::instances_check()); return 0; } diff --git a/src/tests/ikwiad.cc b/src/tests/ikwiad.cc index 309f2c4e2..9502431df 100644 --- a/src/tests/ikwiad.cc +++ b/src/tests/ikwiad.cc @@ -342,7 +342,7 @@ checked_main(int argc, char** argv) bool nra2nba = false; bool scc_filter = false; bool simpltl = false; - spot::ltl::ltl_simplifier_options redopt(false, false, false, false, + spot::ltl_simplifier_options redopt(false, false, false, false, false, false, false); bool simpcache_stats = false; bool scc_filter_all = false; @@ -363,8 +363,8 @@ checked_main(int argc, char** argv) bool opt_stutterize = false; const char* opt_never = nullptr; const char* hoa_opt = nullptr; - auto& env = spot::ltl::default_environment::instance(); - spot::ltl::atomic_prop_set* unobservables = nullptr; + auto& env = spot::default_environment::instance(); + spot::atomic_prop_set* unobservables = nullptr; spot::twa_ptr system_aut = nullptr; auto dict = spot::make_bdd_dict(); spot::timer_map tm; @@ -790,7 +790,7 @@ checked_main(int argc, char** argv) } else if (!strncmp(argv[formula_index], "-U", 2)) { - unobservables = new spot::ltl::atomic_prop_set; + unobservables = new spot::atomic_prop_set; translation = TransFM; // Parse -U's argument. const char* tok = strtok(argv[formula_index] + 2, ", \t;"); @@ -923,7 +923,7 @@ checked_main(int argc, char** argv) input = argv[formula_index]; } - spot::ltl::formula f = nullptr; + spot::formula f = nullptr; if (!from_file) // Reading a formula, not reading an automaton from a file. { switch (translation) @@ -932,11 +932,11 @@ checked_main(int argc, char** argv) case TransTAA: case TransCompo: { - spot::ltl::parse_error_list pel; + spot::parse_error_list pel; tm.start("parsing formula"); - f = spot::ltl::parse_infix_psl(input, pel, env, debug_opt); + f = spot::parse_infix_psl(input, pel, env, debug_opt); tm.stop("parsing formula"); - exit_code = spot::ltl::format_parse_errors(std::cerr, input, pel); + exit_code = spot::format_parse_errors(std::cerr, input, pel); } break; } @@ -964,14 +964,14 @@ checked_main(int argc, char** argv) } else { - spot::ltl::ltl_simplifier* simp = nullptr; + spot::ltl_simplifier* simp = nullptr; if (simpltl) - simp = new spot::ltl::ltl_simplifier(redopt, dict); + simp = new spot::ltl_simplifier(redopt, dict); if (simp) { tm.start("reducing formula"); - spot::ltl::formula t = simp->simplify(f); + spot::formula t = simp->simplify(f); tm.stop("reducing formula"); f = t; if (display_reduced_form) @@ -1659,6 +1659,6 @@ int main(int argc, char** argv) { int exit_code = checked_main(argc, argv); - assert(spot::ltl::fnode::instances_check()); + assert(spot::fnode::instances_check()); return exit_code; } diff --git a/src/tests/kind.cc b/src/tests/kind.cc index 10e6e18f5..16f411284 100644 --- a/src/tests/kind.cc +++ b/src/tests/kind.cc @@ -58,13 +58,13 @@ main(int argc, char **argv) std::getline(ss, form, ','); std::getline(ss, expected); - spot::ltl::parse_error_list p1; - auto f1 = spot::ltl::parse_infix_psl(form, p1); - if (spot::ltl::format_parse_errors(std::cerr, form, p1)) + spot::parse_error_list p1; + auto f1 = spot::parse_infix_psl(form, p1); + if (spot::format_parse_errors(std::cerr, form, p1)) return 2; std::ostringstream so; - spot::ltl::print_formula_props(so, f1, true); + spot::print_formula_props(so, f1, true); auto sost = so.str(); std::cout << form << ',' << sost << '\n'; if (sost != expected) @@ -74,6 +74,6 @@ main(int argc, char **argv) return 2; } } - assert(spot::ltl::fnode::instances_check()); + assert(spot::fnode::instances_check()); return 0; } diff --git a/src/tests/length.cc b/src/tests/length.cc index 2646a8546..a752d9e8b 100644 --- a/src/tests/length.cc +++ b/src/tests/length.cc @@ -45,18 +45,18 @@ main(int argc, char **argv) } { - spot::ltl::parse_error_list p1; - auto f1 = spot::ltl::parse_infix_psl(argv[1], p1); + spot::parse_error_list p1; + auto f1 = spot::parse_infix_psl(argv[1], p1); - if (spot::ltl::format_parse_errors(std::cerr, argv[1], p1)) + if (spot::format_parse_errors(std::cerr, argv[1], p1)) return 2; if (boolone) - std::cout << spot::ltl::length_boolone(f1) << std::endl; + std::cout << spot::length_boolone(f1) << std::endl; else - std::cout << spot::ltl::length(f1) << std::endl; + std::cout << spot::length(f1) << std::endl; } - assert(spot::ltl::fnode::instances_check()); + assert(spot::fnode::instances_check()); return 0; } diff --git a/src/tests/ltlprod.cc b/src/tests/ltlprod.cc index 0831523e9..830bbef7f 100644 --- a/src/tests/ltlprod.cc +++ b/src/tests/ltlprod.cc @@ -44,18 +44,18 @@ main(int argc, char** argv) syntax(argv[0]); { - spot::ltl::environment& env(spot::ltl::default_environment::instance()); + spot::environment& env(spot::default_environment::instance()); - spot::ltl::parse_error_list pel1; - auto f1 = spot::ltl::parse_infix_psl(argv[1], pel1, env); + spot::parse_error_list pel1; + auto f1 = spot::parse_infix_psl(argv[1], pel1, env); - if (spot::ltl::format_parse_errors(std::cerr, argv[1], pel1)) + if (spot::format_parse_errors(std::cerr, argv[1], pel1)) return 2; - spot::ltl::parse_error_list pel2; - auto f2 = spot::ltl::parse_infix_psl(argv[2], pel2, env); + spot::parse_error_list pel2; + auto f2 = spot::parse_infix_psl(argv[2], pel2, env); - if (spot::ltl::format_parse_errors(std::cerr, argv[2], pel2)) + if (spot::format_parse_errors(std::cerr, argv[2], pel2)) return 2; auto dict = spot::make_bdd_dict(); @@ -65,6 +65,6 @@ main(int argc, char** argv) spot::print_dot(std::cout, product(a1, a2)); } } - assert(spot::ltl::fnode::instances_check()); + assert(spot::fnode::instances_check()); return exit_code; } diff --git a/src/tests/ltlrel.cc b/src/tests/ltlrel.cc index fa1f1cb47..3f8e732c5 100644 --- a/src/tests/ltlrel.cc +++ b/src/tests/ltlrel.cc @@ -38,29 +38,29 @@ main(int argc, char **argv) syntax(argv[0]); { - spot::ltl::parse_error_list p1; - auto f1 = spot::ltl::parse_infix_psl(argv[1], p1); + spot::parse_error_list p1; + auto f1 = spot::parse_infix_psl(argv[1], p1); - if (spot::ltl::format_parse_errors(std::cerr, argv[1], p1)) + if (spot::format_parse_errors(std::cerr, argv[1], p1)) return 2; - spot::ltl::relabeling_map* m = new spot::ltl::relabeling_map; - auto f2 = spot::ltl::relabel_bse(f1, spot::ltl::Pnn, m); - spot::ltl::print_psl(std::cout, f2) << '\n'; + spot::relabeling_map* m = new spot::relabeling_map; + auto f2 = spot::relabel_bse(f1, spot::Pnn, m); + spot::print_psl(std::cout, f2) << '\n'; typedef std::map map_t; map_t sorted_map; - for (spot::ltl::relabeling_map::const_iterator i = m->begin(); + for (spot::relabeling_map::const_iterator i = m->begin(); i != m->end(); ++i) - sorted_map[spot::ltl::str_psl(i->first)] = - spot::ltl::str_psl(i->second); + sorted_map[spot::str_psl(i->first)] = + spot::str_psl(i->second); for (map_t::const_iterator i = sorted_map.begin(); i != sorted_map.end(); ++i) std::cout << " " << i->first << " -> " << i->second << '\n'; delete m; } - assert(spot::ltl::fnode::instances_check()); + assert(spot::fnode::instances_check()); return 0; } diff --git a/src/tests/parse.test b/src/tests/parse.test index 9310b01bb..48287dcd9 100755 --- a/src/tests/parse.test +++ b/src/tests/parse.test @@ -22,7 +22,7 @@ # along with this program. If not, see . -# Check that spot::ltl::parse succeed on valid input, and that +# Check that spot::parse succeed on valid input, and that # dump and dotty will work with the resulting trees. Note that # this doesn't check that the tree is correct w.r.t. the formula. diff --git a/src/tests/parse_print_test.cc b/src/tests/parse_print_test.cc index 9e2a6f564..ed01fd032 100644 --- a/src/tests/parse_print_test.cc +++ b/src/tests/parse_print_test.cc @@ -42,6 +42,6 @@ int main(int argc, char** argv) kripke_save_reachable(std::cout, k); } - assert(spot::ltl::fnode::instances_check()); + assert(spot::fnode::instances_check()); return return_value; } diff --git a/src/tests/randtgba.cc b/src/tests/randtgba.cc index 1c98fdb0e..d8406e1da 100644 --- a/src/tests/randtgba.cc +++ b/src/tests/randtgba.cc @@ -486,9 +486,9 @@ print_ar_stats(ar_stats_type& ar_stats, const std::string& s) std::cout << std::setiosflags(old); } -spot::ltl::formula -generate_formula(const spot::ltl::random_ltl& rl, - spot::ltl::ltl_simplifier& simp, +spot::formula +generate_formula(const spot::random_ltl& rl, + spot::ltl_simplifier& simp, int opt_f, int opt_s, int opt_l = 0, bool opt_u = false) { @@ -498,7 +498,7 @@ generate_formula(const spot::ltl::random_ltl& rl, while (max_tries_u--) { spot::srand(opt_s++); - spot::ltl::formula f; + spot::formula f; int max_tries_l = 1000; while (max_tries_l--) { @@ -506,12 +506,12 @@ generate_formula(const spot::ltl::random_ltl& rl, if (opt_l) { f = simp.simplify(f); - if (spot::ltl::length(f) < opt_l) + if (spot::length(f) < opt_l) continue; } else { - assert(spot::ltl::length(f) <= opt_f); + assert(spot::length(f) <= opt_f); } break; } @@ -522,7 +522,7 @@ generate_formula(const spot::ltl::random_ltl& rl, << "of size " << opt_l << " or more." << std::endl; return nullptr; } - std::string txt = spot::ltl::str_psl(f); + std::string txt = spot::str_psl(f); if (!opt_u || unique.insert(txt).second) return f; } @@ -576,12 +576,12 @@ main(int argc, char** argv) spot::option_map options; - auto& env = spot::ltl::default_environment::instance(); - spot::ltl::atomic_prop_set* ap = new spot::ltl::atomic_prop_set; + auto& env = spot::default_environment::instance(); + spot::atomic_prop_set* ap = new spot::atomic_prop_set; auto dict = spot::make_bdd_dict(); - spot::ltl::ltl_simplifier_options simpopt(true, true, true, true, true); - spot::ltl::ltl_simplifier simp(simpopt); + spot::ltl_simplifier_options simpopt(true, true, true, true, true); + spot::ltl_simplifier simp(simpopt); if (argc <= 1) syntax(argv[0]); @@ -788,7 +788,7 @@ main(int argc, char** argv) } } - spot::ltl::random_ltl rl(ap); + spot::random_ltl rl(ap); const char* tok = rl.parse_options(opt_p); if (tok) { @@ -824,7 +824,7 @@ main(int argc, char** argv) spot::timer_map tm_ar; std::set failed_seeds; int init_opt_ec = opt_ec; - spot::ltl::atomic_prop_set* apf = new spot::ltl::atomic_prop_set; + spot::atomic_prop_set* apf = new spot::atomic_prop_set; if (opt_ec) { @@ -847,7 +847,7 @@ main(int argc, char** argv) { if (opt_F) { - spot::ltl::formula f = + spot::formula f = generate_formula(rl, simp, opt_f, opt_ec_seed, opt_l, opt_u); if (!f) exit(1); @@ -862,15 +862,15 @@ main(int argc, char** argv) break; else if (input == "") break; - spot::ltl::parse_error_list pel; - auto f = spot::ltl::parse_infix_psl(input, pel, env); - if (spot::ltl::format_parse_errors(std::cerr, input, pel)) + spot::parse_error_list pel; + auto f = spot::parse_infix_psl(input, pel, env); + if (spot::format_parse_errors(std::cerr, input, pel)) { exit_code = 1; break; } formula = spot::ltl_to_tgba_fm(f, dict, true); - auto* tmp = spot::ltl::atomic_prop_collect(f); + auto* tmp = spot::atomic_prop_collect(f); for (auto i: *tmp) apf->insert(i); delete tmp; diff --git a/src/tests/readltl.cc b/src/tests/readltl.cc index b196c8cb1..f65599ce8 100644 --- a/src/tests/readltl.cc +++ b/src/tests/readltl.cc @@ -54,18 +54,18 @@ main(int argc, char** argv) } { - spot::ltl::environment& env(spot::ltl::default_environment::instance()); - spot::ltl::parse_error_list pel; - auto f = spot::ltl::parse_infix_psl(argv[formula_index], pel, env, debug); + spot::environment& env(spot::default_environment::instance()); + spot::parse_error_list pel; + auto f = spot::parse_infix_psl(argv[formula_index], pel, env, debug); exit_code = - spot::ltl::format_parse_errors(std::cerr, argv[formula_index], pel); + spot::format_parse_errors(std::cerr, argv[formula_index], pel); if (f) { #ifdef DOTTY - spot::ltl::print_dot_psl(std::cout, f); + spot::print_dot_psl(std::cout, f); #else f.dump(std::cout) << std::endl; #endif @@ -76,6 +76,6 @@ main(int argc, char** argv) } } - assert(spot::ltl::fnode::instances_check()); + assert(spot::fnode::instances_check()); return exit_code; } diff --git a/src/tests/reduc.cc b/src/tests/reduc.cc index c960916a9..2de7cdd9f 100644 --- a/src/tests/reduc.cc +++ b/src/tests/reduc.cc @@ -45,7 +45,7 @@ main(int argc, char** argv) bool hidereduc = false; unsigned long sum_before = 0; unsigned long sum_after = 0; - spot::ltl::ltl_simplifier_options o(false, false, false, false, false); + spot::ltl_simplifier_options o(false, false, false, false, false); if (argc < 3) syntax(argv[0]); @@ -147,12 +147,12 @@ main(int argc, char** argv) int exit_code = 0; { - spot::ltl::ltl_simplifier* simp = new spot::ltl::ltl_simplifier(o); + spot::ltl_simplifier* simp = new spot::ltl_simplifier(o); o.reduce_size_strictly = true; - spot::ltl::ltl_simplifier* simp_size = new spot::ltl::ltl_simplifier(o); + spot::ltl_simplifier* simp_size = new spot::ltl_simplifier(o); - spot::ltl::formula f1 = nullptr; - spot::ltl::formula f2 = nullptr; + spot::formula f1 = nullptr; + spot::formula f2 = nullptr; std::ifstream* fin = nullptr; @@ -178,16 +178,16 @@ main(int argc, char** argv) } while (input == ""); - spot::ltl::parse_error_list p1; - f1 = spot::ltl::parse_infix_psl(input, p1); - if (spot::ltl::format_parse_errors(std::cerr, input, p1)) + spot::parse_error_list p1; + f1 = spot::parse_infix_psl(input, p1); + if (spot::format_parse_errors(std::cerr, input, p1)) return 2; } else { - spot::ltl::parse_error_list p1; - f1 = spot::ltl::parse_infix_psl(argv[2], p1); - if (spot::ltl::format_parse_errors(std::cerr, argv[2], p1)) + spot::parse_error_list p1; + f1 = spot::parse_infix_psl(argv[2], p1); + if (spot::format_parse_errors(std::cerr, argv[2], p1)) return 2; } @@ -199,23 +199,23 @@ main(int argc, char** argv) exit(2); } - spot::ltl::parse_error_list p2; - f2 = spot::ltl::parse_infix_psl(argv[3], p2); - if (spot::ltl::format_parse_errors(std::cerr, argv[3], p2)) + spot::parse_error_list p2; + f2 = spot::parse_infix_psl(argv[3], p2); + if (spot::format_parse_errors(std::cerr, argv[3], p2)) return 2; } { - spot::ltl::formula ftmp1; + spot::formula ftmp1; ftmp1 = f1; f1 = simp_size->negative_normal_form(f1, false); - int length_f1_before = spot::ltl::length(f1); - std::string f1s_before = spot::ltl::str_psl(f1); + int length_f1_before = spot::length(f1); + std::string f1s_before = spot::str_psl(f1); std::string f1l; - spot::ltl::formula input_f = f1; + spot::formula input_f = f1; f1 = simp_size->simplify(input_f); if (!simp_size->are_equivalent(input_f, f1)) { @@ -226,8 +226,8 @@ main(int argc, char** argv) } else { - spot::ltl::formula maybe_larger = simp->simplify(input_f); - f1l = spot::ltl::str_psl(maybe_larger); + spot::formula maybe_larger = simp->simplify(input_f); + f1l = spot::str_psl(maybe_larger); if (!simp->are_equivalent(input_f, maybe_larger)) { std::cerr << "Incorrect reduction (reduce_size_strictly=0) from `" @@ -236,15 +236,15 @@ main(int argc, char** argv) } } - int length_f1_after = spot::ltl::length(f1); - std::string f1s_after = spot::ltl::str_psl(f1); + int length_f1_after = spot::length(f1); + std::string f1s_after = spot::str_psl(f1); std::string f2s = ""; if (f2) { ftmp1 = f2; f2 = simp_size->negative_normal_form(f2, false); - f2s = spot::ltl::str_psl(f2); + f2s = spot::str_psl(f2); } sum_before += length_f1_before; @@ -308,6 +308,6 @@ main(int argc, char** argv) } } - assert(spot::ltl::fnode::instances_check()); + assert(spot::fnode::instances_check()); return exit_code; } diff --git a/src/tests/syntimpl.cc b/src/tests/syntimpl.cc index bee75f051..328883740 100644 --- a/src/tests/syntimpl.cc +++ b/src/tests/syntimpl.cc @@ -45,25 +45,25 @@ main(int argc, char** argv) int exit_return = 0; { - spot::ltl::parse_error_list p1; - auto ftmp1 = spot::ltl::parse_infix_psl(argv[2], p1); + spot::parse_error_list p1; + auto ftmp1 = spot::parse_infix_psl(argv[2], p1); - if (spot::ltl::format_parse_errors(std::cerr, argv[2], p1)) + if (spot::format_parse_errors(std::cerr, argv[2], p1)) return 2; - spot::ltl::parse_error_list p2; - auto ftmp2 = spot::ltl::parse_infix_psl(argv[3], p2); + spot::parse_error_list p2; + auto ftmp2 = spot::parse_infix_psl(argv[3], p2); - if (spot::ltl::format_parse_errors(std::cerr, argv[3], p2)) + if (spot::format_parse_errors(std::cerr, argv[3], p2)) return 2; - spot::ltl::formula f1 = spot::ltl::negative_normal_form(ftmp1); - spot::ltl::formula f2 = spot::ltl::negative_normal_form(ftmp2); + spot::formula f1 = spot::negative_normal_form(ftmp1); + spot::formula f2 = spot::negative_normal_form(ftmp2); - std::string f1s = spot::ltl::str_psl(f1); - std::string f2s = spot::ltl::str_psl(f2); + std::string f1s = spot::str_psl(f1); + std::string f2s = spot::str_psl(f2); - spot::ltl::ltl_simplifier* c = new spot::ltl::ltl_simplifier; + spot::ltl_simplifier* c = new spot::ltl_simplifier; switch (opt) { @@ -102,6 +102,6 @@ main(int argc, char** argv) delete c; } - assert(spot::ltl::fnode::instances_check()); + assert(spot::fnode::instances_check()); return exit_return; } diff --git a/src/tests/taatgba.cc b/src/tests/taatgba.cc index 52e551429..57a822058 100644 --- a/src/tests/taatgba.cc +++ b/src/tests/taatgba.cc @@ -28,8 +28,8 @@ int main() { { - spot::ltl::default_environment& e = - spot::ltl::default_environment::instance(); + spot::default_environment& e = + spot::default_environment::instance(); auto a = spot::make_taa_tgba_string(spot::make_bdd_dict()); typedef spot::taa_tgba::transition trans; @@ -48,6 +48,6 @@ main() spot::print_dot(std::cout, a); } - assert(spot::ltl::fnode::instances_check()); + assert(spot::fnode::instances_check()); return 0; } diff --git a/src/tests/tostring.cc b/src/tests/tostring.cc index 8585b6d11..60193473e 100644 --- a/src/tests/tostring.cc +++ b/src/tests/tostring.cc @@ -40,21 +40,21 @@ main(int argc, char **argv) syntax(argv[0]); { - spot::ltl::parse_error_list p1; - auto f1 = spot::ltl::parse_infix_psl(argv[1], p1); + spot::parse_error_list p1; + auto f1 = spot::parse_infix_psl(argv[1], p1); - if (spot::ltl::format_parse_errors(std::cerr, argv[1], p1)) + if (spot::format_parse_errors(std::cerr, argv[1], p1)) return 2; // The string generated from an abstract tree should be parsable // again. - std::string f1s = spot::ltl::str_psl(f1); + std::string f1s = spot::str_psl(f1); std::cout << f1s << '\n'; - auto f2 = spot::ltl::parse_infix_psl(f1s, p1); + auto f2 = spot::parse_infix_psl(f1s, p1); - if (spot::ltl::format_parse_errors(std::cerr, f1s, p1)) + if (spot::format_parse_errors(std::cerr, f1s, p1)) return 2; // This second abstract tree should be equal to the first. @@ -64,13 +64,13 @@ main(int argc, char **argv) // It should also map to the same string. - std::string f2s = spot::ltl::str_psl(f2); + std::string f2s = spot::str_psl(f2); std::cout << f2s << '\n'; if (f2s != f1s) return 1; } - assert(spot::ltl::fnode::instances_check()); + assert(spot::fnode::instances_check()); return 0; } diff --git a/src/tests/tostring.test b/src/tests/tostring.test index a3d76125f..928ee6ea3 100755 --- a/src/tests/tostring.test +++ b/src/tests/tostring.test @@ -22,7 +22,7 @@ # along with this program. If not, see . -# Check for spot::ltl::tostring. +# Check for spot::tostring. . ./defs || exit 1 diff --git a/src/tl/apcollect.cc b/src/tl/apcollect.cc index 9abd22578..0f0d3dfe7 100644 --- a/src/tl/apcollect.cc +++ b/src/tl/apcollect.cc @@ -26,43 +26,40 @@ namespace spot { - namespace ltl + atomic_prop_set create_atomic_prop_set(unsigned n) { - atomic_prop_set create_atomic_prop_set(unsigned n) - { - atomic_prop_set res; - for (unsigned i = 0; i < n; ++i) - { - std::ostringstream p; - p << 'p' << i; - res.insert(formula::ap(p.str())); - } - return res; - } + atomic_prop_set res; + for (unsigned i = 0; i < n; ++i) + { + std::ostringstream p; + p << 'p' << i; + res.insert(formula::ap(p.str())); + } + return res; + } - atomic_prop_set* - atomic_prop_collect(formula f, atomic_prop_set* s) - { - if (!s) - s = new atomic_prop_set; - f.traverse([&](const formula& f) - { - if (f.is(op::ap)) - s->insert(f); - return false; - }); - return s; - } + atomic_prop_set* + atomic_prop_collect(formula f, atomic_prop_set* s) + { + if (!s) + s = new atomic_prop_set; + f.traverse([&](const formula& f) + { + if (f.is(op::ap)) + s->insert(f); + return false; + }); + return s; + } - bdd - atomic_prop_collect_as_bdd(formula f, const twa_ptr& a) - { - spot::ltl::atomic_prop_set aps; - atomic_prop_collect(f, &aps); - bdd res = bddtrue; - for (auto f: aps) - res &= bdd_ithvar(a->register_ap(f)); - return res; - } + bdd + atomic_prop_collect_as_bdd(formula f, const twa_ptr& a) + { + spot::atomic_prop_set aps; + atomic_prop_collect(f, &aps); + bdd res = bddtrue; + for (auto f: aps) + res &= bdd_ithvar(a->register_ap(f)); + return res; } } diff --git a/src/tl/apcollect.hh b/src/tl/apcollect.hh index 70cc2cd84..067146fda 100644 --- a/src/tl/apcollect.hh +++ b/src/tl/apcollect.hh @@ -29,38 +29,35 @@ namespace spot { - namespace ltl - { - /// \addtogroup ltl_misc - /// @{ + /// \addtogroup ltl_misc + /// @{ - /// Set of atomic propositions. - typedef std::set atomic_prop_set; + /// Set of atomic propositions. + typedef std::set atomic_prop_set; - /// \brief construct an atomic_prop_set with n propositions - SPOT_API - atomic_prop_set create_atomic_prop_set(unsigned n); + /// \brief construct an atomic_prop_set with n propositions + SPOT_API + atomic_prop_set create_atomic_prop_set(unsigned n); - /// \brief Return the set of atomic propositions occurring in a formula. - /// - /// \param f the formula to inspect - /// \param s an existing set to fill with atomic_propositions discovered, - /// or 0 if the set should be allocated by the function. - /// \return A pointer to the supplied set, \c s, augmented with - /// atomic propositions occurring in \c f; or a newly allocated - /// set containing all these atomic propositions if \c s is 0. - SPOT_API atomic_prop_set* - atomic_prop_collect(formula f, atomic_prop_set* s = nullptr); + /// \brief Return the set of atomic propositions occurring in a formula. + /// + /// \param f the formula to inspect + /// \param s an existing set to fill with atomic_propositions discovered, + /// or 0 if the set should be allocated by the function. + /// \return A pointer to the supplied set, \c s, augmented with + /// atomic propositions occurring in \c f; or a newly allocated + /// set containing all these atomic propositions if \c s is 0. + SPOT_API atomic_prop_set* + atomic_prop_collect(formula f, atomic_prop_set* s = nullptr); - /// \brief Return the set of atomic propositions occurring in a - /// formula, as a BDD. - /// - /// \param f the formula to inspect - /// \param a that automaton that should register the BDD variables used. - /// \return A conjunction the atomic propositions. - SPOT_API bdd - atomic_prop_collect_as_bdd(formula f, const twa_ptr& a); + /// \brief Return the set of atomic propositions occurring in a + /// formula, as a BDD. + /// + /// \param f the formula to inspect + /// \param a that automaton that should register the BDD variables used. + /// \return A conjunction the atomic propositions. + SPOT_API bdd + atomic_prop_collect_as_bdd(formula f, const twa_ptr& a); - /// @} - } + /// @} } diff --git a/src/tl/contain.cc b/src/tl/contain.cc index 6b87992ad..ca7829ad9 100644 --- a/src/tl/contain.cc +++ b/src/tl/contain.cc @@ -27,114 +27,110 @@ namespace spot { - namespace ltl - { - - language_containment_checker::language_containment_checker - (const bdd_dict_ptr& dict, bool exprop, bool symb_merge, - bool branching_postponement, bool fair_loop_approx) - : dict_(dict), exprop_(exprop), symb_merge_(symb_merge), + language_containment_checker::language_containment_checker + (const bdd_dict_ptr& dict, bool exprop, bool symb_merge, + bool branching_postponement, bool fair_loop_approx) + : dict_(dict), exprop_(exprop), symb_merge_(symb_merge), branching_postponement_(branching_postponement), fair_loop_approx_(fair_loop_approx) - { - } + { + } - language_containment_checker::~language_containment_checker() - { - clear(); - } + language_containment_checker::~language_containment_checker() + { + clear(); + } - void - language_containment_checker::clear() - { - translated_.clear(); - } + void + language_containment_checker::clear() + { + translated_.clear(); + } - bool - language_containment_checker::incompatible_(record_* l, record_* g) - { - record_::incomp_map::const_iterator i = l->incompatible.find(g); - if (i != l->incompatible.end()) - return i->second; + bool + language_containment_checker::incompatible_(record_* l, record_* g) + { + record_::incomp_map::const_iterator i = l->incompatible.find(g); + if (i != l->incompatible.end()) + return i->second; - bool res = product(l->translation, g->translation)->is_empty(); - l->incompatible[g] = res; - g->incompatible[l] = res; - return res; - } + bool res = product(l->translation, g->translation)->is_empty(); + l->incompatible[g] = res; + g->incompatible[l] = res; + return res; + } - // Check whether L(l) is a subset of L(g). - bool - language_containment_checker::contained(formula l, - formula g) - { - if (l == g) - return true; - record_* rl = register_formula_(l); - record_* rng = register_formula_(formula::Not(g)); - return incompatible_(rl, rng); - } + // Check whether L(l) is a subset of L(g). + bool + language_containment_checker::contained(formula l, + formula g) + { + if (l == g) + return true; + record_* rl = register_formula_(l); + record_* rng = register_formula_(formula::Not(g)); + return incompatible_(rl, rng); + } - // Check whether L(!l) is a subset of L(g). - bool - language_containment_checker::neg_contained(formula l, - formula g) - { - if (l == g) - return false; - formula nl = formula::Not(l); - record_* rnl = register_formula_(nl); - record_* rng = register_formula_(formula::Not(g)); - if (nl == g) - return true; - return incompatible_(rnl, rng); - } + // Check whether L(!l) is a subset of L(g). + bool + language_containment_checker::neg_contained(formula l, + formula g) + { + if (l == g) + return false; + formula nl = formula::Not(l); + record_* rnl = register_formula_(nl); + record_* rng = register_formula_(formula::Not(g)); + if (nl == g) + return true; + return incompatible_(rnl, rng); + } - // Check whether L(l) is a subset of L(!g). - bool - language_containment_checker::contained_neg(formula l, - formula g) - { - if (l == g) - return false; - record_* rl = register_formula_(l); - record_* rg = register_formula_(g); - return incompatible_(rl, rg); - } + // Check whether L(l) is a subset of L(!g). + bool + language_containment_checker::contained_neg(formula l, + formula g) + { + if (l == g) + return false; + record_* rl = register_formula_(l); + record_* rg = register_formula_(g); + return incompatible_(rl, rg); + } - // Check whether L(l) = L(g). - bool - language_containment_checker::equal(formula l, formula g) - { - return contained(l, g) && contained(g, l); - } + // Check whether L(l) = L(g). + bool + language_containment_checker::equal(formula l, formula g) + { + return contained(l, g) && contained(g, l); + } - language_containment_checker::record_* - language_containment_checker::register_formula_(formula f) - { - trans_map::iterator i = translated_.find(f); - if (i != translated_.end()) - return &i->second; + language_containment_checker::record_* + language_containment_checker::register_formula_(formula f) + { + trans_map::iterator i = translated_.find(f); + if (i != translated_.end()) + return &i->second; - auto e = ltl_to_tgba_fm(f, dict_, exprop_, symb_merge_, - branching_postponement_, fair_loop_approx_); - record_& r = translated_[f]; - r.translation = e; - return &r; - } + auto e = ltl_to_tgba_fm(f, dict_, exprop_, symb_merge_, + branching_postponement_, fair_loop_approx_); + record_& r = translated_[f]; + r.translation = e; + return &r; + } - formula - reduce_tau03(formula f, bool stronger) - { - if (!f.is_psl_formula()) - return f; + formula + reduce_tau03(formula f, bool stronger) + { + if (!f.is_psl_formula()) + return f; - ltl_simplifier_options opt(false, false, false, - true, stronger); - ltl_simplifier simpl(opt); - return simpl.simplify(f); - } + ltl_simplifier_options opt(false, false, false, + true, stronger); + ltl_simplifier simpl(opt); + return simpl.simplify(f); } } diff --git a/src/tl/contain.hh b/src/tl/contain.hh index 8ea6939b9..6c013c235 100644 --- a/src/tl/contain.hh +++ b/src/tl/contain.hh @@ -29,54 +29,51 @@ namespace spot { - namespace ltl + /// Check containment between LTL formulae. + class SPOT_API language_containment_checker { - /// Check containment between LTL formulae. - class SPOT_API language_containment_checker + struct record_ { - struct record_ - { - const_twa_graph_ptr translation; - typedef std::map incomp_map; - incomp_map incompatible; - }; - typedef std::unordered_map trans_map; - public: - /// This class uses spot::ltl_to_tgba_fm to translate LTL - /// formulae. See that function for the meaning of these options. - language_containment_checker(const bdd_dict_ptr& dict, bool exprop, - bool symb_merge, - bool branching_postponement, - bool fair_loop_approx); - - ~language_containment_checker(); - - /// Clear the cache. - void clear(); - - /// Check whether L(l) is a subset of L(g). - bool contained(formula l, formula g); - /// Check whether L(!l) is a subset of L(g). - bool neg_contained(formula l, formula g); - /// Check whether L(l) is a subset of L(!g). - bool contained_neg(formula l, formula g); - - /// Check whether L(l) = L(g). - bool equal(formula l, formula g); - - protected: - bool incompatible_(record_* l, record_* g); - - record_* register_formula_(formula f); - - /* Translation options */ - bdd_dict_ptr dict_; - bool exprop_; - bool symb_merge_; - bool branching_postponement_; - bool fair_loop_approx_; - /* Translation Maps */ - trans_map translated_; + const_twa_graph_ptr translation; + typedef std::map incomp_map; + incomp_map incompatible; }; - } + typedef std::unordered_map trans_map; + public: + /// This class uses spot::ltl_to_tgba_fm to translate LTL + /// formulae. See that function for the meaning of these options. + language_containment_checker(const bdd_dict_ptr& dict, bool exprop, + bool symb_merge, + bool branching_postponement, + bool fair_loop_approx); + + ~language_containment_checker(); + + /// Clear the cache. + void clear(); + + /// Check whether L(l) is a subset of L(g). + bool contained(formula l, formula g); + /// Check whether L(!l) is a subset of L(g). + bool neg_contained(formula l, formula g); + /// Check whether L(l) is a subset of L(!g). + bool contained_neg(formula l, formula g); + + /// Check whether L(l) = L(g). + bool equal(formula l, formula g); + + protected: + bool incompatible_(record_* l, record_* g); + + record_* register_formula_(formula f); + + /* Translation options */ + bdd_dict_ptr dict_; + bool exprop_; + bool symb_merge_; + bool branching_postponement_; + bool fair_loop_approx_; + /* Translation Maps */ + trans_map translated_; + }; } diff --git a/src/tl/declenv.cc b/src/tl/declenv.cc index b1e96463b..e5ee5f9d4 100644 --- a/src/tl/declenv.cc +++ b/src/tl/declenv.cc @@ -24,42 +24,38 @@ namespace spot { - namespace ltl + declarative_environment::declarative_environment() { + } - declarative_environment::declarative_environment() - { - } + bool + declarative_environment::declare(const std::string& prop_str) + { + if (props_.find(prop_str) != props_.end()) + return false; + props_[prop_str] = formula::ap(prop_str); + return true; + } - bool - declarative_environment::declare(const std::string& prop_str) - { - if (props_.find(prop_str) != props_.end()) - return false; - props_[prop_str] = formula::ap(prop_str); - return true; - } + formula + declarative_environment::require(const std::string& prop_str) + { + prop_map::iterator i = props_.find(prop_str); + if (i == props_.end()) + return nullptr; + return i->second; + } - formula - declarative_environment::require(const std::string& prop_str) - { - prop_map::iterator i = props_.find(prop_str); - if (i == props_.end()) - return nullptr; - return i->second; - } + const std::string& + declarative_environment::name() const + { + static std::string name("declarative environment"); + return name; + } - const std::string& - declarative_environment::name() const - { - static std::string name("declarative environment"); - return name; - } - - const declarative_environment::prop_map& - declarative_environment::get_prop_map() const - { - return props_; - } + const declarative_environment::prop_map& + declarative_environment::get_prop_map() const + { + return props_; } } diff --git a/src/tl/declenv.hh b/src/tl/declenv.hh index 0fc706639..304f1bc5f 100644 --- a/src/tl/declenv.hh +++ b/src/tl/declenv.hh @@ -29,36 +29,32 @@ namespace spot { - namespace ltl + /// \ingroup ltl_environment + /// \brief A declarative environment. + /// + /// This environment recognizes all atomic propositions + /// that have been previously declared. It will reject other. + class SPOT_API declarative_environment : public environment { + public: + declarative_environment(); + ~declarative_environment() = default; - /// \ingroup ltl_environment - /// \brief A declarative environment. - /// - /// This environment recognizes all atomic propositions - /// that have been previously declared. It will reject other. - class SPOT_API declarative_environment : public environment - { - public: - declarative_environment(); - ~declarative_environment() = default; + /// Declare an atomic proposition. Return false iff the + /// proposition was already declared. + bool declare(const std::string& prop_str); - /// Declare an atomic proposition. Return false iff the - /// proposition was already declared. - bool declare(const std::string& prop_str); + virtual formula require(const std::string& prop_str); - virtual formula require(const std::string& prop_str); + /// Get the name of the environment. + virtual const std::string& name() const; - /// Get the name of the environment. - virtual const std::string& name() const; + typedef std::map prop_map; - typedef std::map prop_map; + /// Get the map of atomic proposition known to this environment. + const prop_map& get_prop_map() const; - /// Get the map of atomic proposition known to this environment. - const prop_map& get_prop_map() const; - - private: - prop_map props_; - }; - } + private: + prop_map props_; + }; } diff --git a/src/tl/defaultenv.cc b/src/tl/defaultenv.cc index bbb5f0118..6f73baee1 100644 --- a/src/tl/defaultenv.cc +++ b/src/tl/defaultenv.cc @@ -24,36 +24,31 @@ namespace spot { - namespace ltl + default_environment::~default_environment() { + } - default_environment::~default_environment() - { - } + formula + default_environment::require(const std::string& s) + { + return formula::ap(s); + } - formula - default_environment::require(const std::string& s) - { - return formula::ap(s); - } + const std::string& + default_environment::name() const + { + static std::string name("default environment"); + return name; + } - const std::string& - default_environment::name() const - { - static std::string name("default environment"); - return name; - } - - default_environment::default_environment() - { - } - - default_environment& - default_environment::instance() - { - static default_environment* singleton = new default_environment(); - return *singleton; - } + default_environment::default_environment() + { + } + default_environment& + default_environment::instance() + { + static default_environment* singleton = new default_environment(); + return *singleton; } } diff --git a/src/tl/defaultenv.hh b/src/tl/defaultenv.hh index e54bc0f59..e8c477758 100644 --- a/src/tl/defaultenv.hh +++ b/src/tl/defaultenv.hh @@ -27,27 +27,23 @@ namespace spot { - namespace ltl + /// \ingroup ltl_environment + /// \brief A laxist environment. + /// + /// This environment recognizes all atomic propositions. + /// + /// This is a singleton. Use default_environment::instance() + /// to obtain the instance. + class SPOT_API default_environment final: public environment { + public: + virtual ~default_environment(); + virtual formula require(const std::string& prop_str); + virtual const std::string& name() const; - /// \ingroup ltl_environment - /// \brief A laxist environment. - /// - /// This environment recognizes all atomic propositions. - /// - /// This is a singleton. Use default_environment::instance() - /// to obtain the instance. - class SPOT_API default_environment final: public environment - { - public: - virtual ~default_environment(); - virtual formula require(const std::string& prop_str); - virtual const std::string& name() const; - - /// Get the sole instance of spot::ltl::default_environment. - static default_environment& instance(); - protected: - default_environment(); - }; - } + /// Get the sole instance of spot::default_environment. + static default_environment& instance(); + protected: + default_environment(); + }; } diff --git a/src/tl/dot.cc b/src/tl/dot.cc index de7da2b6a..da5b4077f 100644 --- a/src/tl/dot.cc +++ b/src/tl/dot.cc @@ -28,110 +28,107 @@ namespace spot { - namespace ltl + namespace { - namespace + struct dot_printer final { - struct dot_printer final - { - std::ostream& os_; - std::unordered_map node_; - std::ostringstream* sinks_; + std::ostream& os_; + std::unordered_map node_; + std::ostringstream* sinks_; - dot_printer(std::ostream& os, formula f) - : os_(os), sinks_(new std::ostringstream) - { - os_ << "digraph G {\n"; - rec(f); - os_ << " subgraph atoms {\n rank=sink;\n" - << sinks_->str() << " }\n}\n"; - } + dot_printer(std::ostream& os, formula f) + : os_(os), sinks_(new std::ostringstream) + { + os_ << "digraph G {\n"; + rec(f); + os_ << " subgraph atoms {\n rank=sink;\n" + << sinks_->str() << " }\n}\n"; + } - ~dot_printer() + ~dot_printer() { delete sinks_; } - int rec(formula f) - { - auto i = node_.emplace(f, node_.size()); - int src = i.first->second; - if (!i.second) - return src; - - op o = f.kind(); - std::string str = (o == op::ap) ? f.ap_name() : f.kindstr(); - - if (o == op::ap || f.is_constant()) - *sinks_ << " " << src << " [label=\"" - << str << "\", shape=box];\n"; - else - os_ << " " << src << " [label=\"" << str << "\"];\n"; - - int childnum = 0; - switch (o) - { - case op::ff: - case op::tt: - case op::eword: - case op::ap: - case op::Not: - case op::X: - case op::F: - case op::G: - case op::Closure: - case op::NegClosure: - case op::NegClosureMarked: - case op::Or: - case op::OrRat: - case op::And: - case op::AndRat: - case op::AndNLM: - case op::Star: - case op::FStar: - childnum = 0; // No number for children - break; - case op::Xor: - case op::Implies: - case op::Equiv: - case op::U: - case op::R: - case op::W: - case op::M: - case op::EConcat: - case op::EConcatMarked: - case op::UConcat: - childnum = -2; // L and R markers - break; - case op::Concat: - case op::Fusion: - childnum = 1; // Numbered children - break; - } - - for (auto c: f) - { - os_ << " " << src << " -> " << rec(c); - if (childnum > 0) - os_ << " [taillabel=\"" << childnum << "\"]"; - if (childnum == -2) - os_ << " [taillabel=\"L\"]"; - else if (childnum == -1) - os_ << " [taillabel=\"R\"]"; - os_ << ";\n"; - ++childnum; - } - + int rec(formula f) + { + auto i = node_.emplace(f, node_.size()); + int src = i.first->second; + if (!i.second) return src; - } - }; - } - std::ostream& - print_dot_psl(std::ostream& os, formula f) - { - dot_printer p(os, f); - return os; - } + op o = f.kind(); + std::string str = (o == op::ap) ? f.ap_name() : f.kindstr(); + + if (o == op::ap || f.is_constant()) + *sinks_ << " " << src << " [label=\"" + << str << "\", shape=box];\n"; + else + os_ << " " << src << " [label=\"" << str << "\"];\n"; + + int childnum = 0; + switch (o) + { + case op::ff: + case op::tt: + case op::eword: + case op::ap: + case op::Not: + case op::X: + case op::F: + case op::G: + case op::Closure: + case op::NegClosure: + case op::NegClosureMarked: + case op::Or: + case op::OrRat: + case op::And: + case op::AndRat: + case op::AndNLM: + case op::Star: + case op::FStar: + childnum = 0; // No number for children + break; + case op::Xor: + case op::Implies: + case op::Equiv: + case op::U: + case op::R: + case op::W: + case op::M: + case op::EConcat: + case op::EConcatMarked: + case op::UConcat: + childnum = -2; // L and R markers + break; + case op::Concat: + case op::Fusion: + childnum = 1; // Numbered children + break; + } + + for (auto c: f) + { + os_ << " " << src << " -> " << rec(c); + if (childnum > 0) + os_ << " [taillabel=\"" << childnum << "\"]"; + if (childnum == -2) + os_ << " [taillabel=\"L\"]"; + else if (childnum == -1) + os_ << " [taillabel=\"R\"]"; + os_ << ";\n"; + ++childnum; + } + + return src; + } + }; + } + + std::ostream& + print_dot_psl(std::ostream& os, formula f) + { + dot_printer p(os, f); + return os; } } diff --git a/src/tl/dot.hh b/src/tl/dot.hh index 13438ebbd..d79f4278f 100644 --- a/src/tl/dot.hh +++ b/src/tl/dot.hh @@ -26,16 +26,13 @@ namespace spot { - namespace ltl - { - /// \ingroup ltl_io - /// \brief Write a formula tree using dot's syntax. - /// \param os The stream where it should be output. - /// \param f The formula to translate. - /// - /// \c dot is part of the GraphViz package - /// http://www.graphviz.org/ - SPOT_API - std::ostream& print_dot_psl(std::ostream& os, formula f); - } + /// \ingroup ltl_io + /// \brief Write a formula tree using dot's syntax. + /// \param os The stream where it should be output. + /// \param f The formula to translate. + /// + /// \c dot is part of the GraphViz package + /// http://www.graphviz.org/ + SPOT_API + std::ostream& print_dot_psl(std::ostream& os, formula f); } diff --git a/src/tl/environment.hh b/src/tl/environment.hh index f6551c29d..49adf54d2 100644 --- a/src/tl/environment.hh +++ b/src/tl/environment.hh @@ -27,34 +27,30 @@ namespace spot { - namespace ltl + /// \ingroup ltl_essential + /// \brief An environment that describes atomic propositions. + class environment { - /// \ingroup ltl_essential - /// \brief An environment that describes atomic propositions. - class environment + public: + /// \brief Obtain the formula associated to \a prop_str + /// + /// Usually \a prop_str, is the name of an atomic proposition, + /// and spot::require simply returns the associated + /// spot::formula. + /// + /// Note this is not a \c const method. Some environments will + /// "create" the atomic proposition when requested. + /// + /// \return 0 iff \a prop_str is not part of the environment, + /// or the associated spot::formula otherwise. + virtual formula require(const std::string& prop_str) = 0; + + /// Get the name of the environment. + virtual const std::string& name() const = 0; + + virtual + ~environment() { - public: - /// \brief Obtain the formula associated to \a prop_str - /// - /// Usually \a prop_str, is the name of an atomic proposition, - /// and spot::ltl::require simply returns the associated - /// spot::ltl::formula. - /// - /// Note this is not a \c const method. Some environments will - /// "create" the atomic proposition when requested. - /// - /// \return 0 iff \a prop_str is not part of the environment, - /// or the associated spot::ltl::formula otherwise. - virtual formula require(const std::string& prop_str) = 0; - - /// Get the name of the environment. - virtual const std::string& name() const = 0; - - virtual - ~environment() - { - } - }; - - } + } + }; } diff --git a/src/tl/exclusive.cc b/src/tl/exclusive.cc index 58ca2d03a..de3b37b0e 100644 --- a/src/tl/exclusive.cc +++ b/src/tl/exclusive.cc @@ -27,10 +27,10 @@ namespace spot { namespace { - static const std::vector + static const std::vector split_aps(const char* arg) { - std::vector group; + std::vector group; auto start = arg; while (*start) { @@ -61,7 +61,7 @@ namespace spot throw std::invalid_argument(s); } std::string ap(start, end - start); - group.emplace_back(ltl::formula::ap(ap)); + group.emplace_back(formula::ap(ap)); do ++end; while (*end == ' ' || *end == '\t'); @@ -86,7 +86,7 @@ namespace spot while (rend > start && (rend[-1] == ' ' || rend[-1] == '\t')) --rend; std::string ap(start, rend - start); - group.emplace_back(ltl::formula::ap(ap)); + group.emplace_back(formula::ap(ap)); if (*end == ',') start = end + 1; else @@ -102,27 +102,27 @@ namespace spot add_group(split_aps(ap_csv)); } - void exclusive_ap::add_group(std::vector ap) + void exclusive_ap::add_group(std::vector ap) { groups.push_back(ap); } namespace { - ltl::formula - nand(ltl::formula lhs, ltl::formula rhs) + formula + nand(formula lhs, formula rhs) { - return ltl::formula::Not(ltl::formula::And({lhs, rhs})); + return formula::Not(formula::And({lhs, rhs})); } } - ltl::formula - exclusive_ap::constrain(ltl::formula f) const + formula + exclusive_ap::constrain(formula f) const { auto* s = atomic_prop_collect(f); - std::vector group; - std::vector v; + std::vector group; + std::vector v; for (auto& g: groups) { @@ -139,7 +139,7 @@ namespace spot }; delete s; - return ltl::formula::And({f, ltl::formula::G(ltl::formula::And(v))}); + return formula::And({f, formula::G(formula::And(v))}); } twa_graph_ptr exclusive_ap::constrain(const_twa_graph_ptr aut, diff --git a/src/tl/exclusive.hh b/src/tl/exclusive.hh index c420c076e..aef2c33b6 100644 --- a/src/tl/exclusive.hh +++ b/src/tl/exclusive.hh @@ -27,10 +27,10 @@ namespace spot { class SPOT_API exclusive_ap final { - std::vector> groups; + std::vector> groups; public: #ifndef SWIG - void add_group(std::vector ap); + void add_group(std::vector ap); #endif void add_group(const char* ap_csv); @@ -39,7 +39,7 @@ namespace spot return groups.empty(); } - ltl::formula constrain(ltl::formula f) const; + formula constrain(formula f) const; twa_graph_ptr constrain(const_twa_graph_ptr aut, bool simplify_guards = false) const; }; diff --git a/src/tl/formula.cc b/src/tl/formula.cc index de16f1732..4a5145134 100644 --- a/src/tl/formula.cc +++ b/src/tl/formula.cc @@ -37,7 +37,7 @@ namespace spot { namespace { - typedef std::vector vec; + typedef std::vector vec; // Compare two formulas, by looking at their operators and // children. This does not use id for the top-level operator, @@ -45,14 +45,14 @@ namespace spot // formula. struct formula_cmp { - bool operator()(const ltl::fnode* l, const ltl::fnode* r) const + bool operator()(const fnode* l, const fnode* r) const { - ltl::op opl = l->kind(); - ltl::op opr = r->kind(); + op opl = l->kind(); + op opr = r->kind(); if (opl != opr) return opl < opr; - if (SPOT_UNLIKELY(opl == ltl::op::Star || opl == ltl::op::FStar)) + if (SPOT_UNLIKELY(opl == op::Star || opl == op::FStar)) { { auto minl = l->min(); @@ -87,15 +87,15 @@ namespace spot struct maps_t final { - std::map name2ap; + std::map name2ap; std::map ap2name; - std::set uniq; + std::set uniq; }; static maps_t m; static void - gather_bool(vec& v, ltl::op o) + gather_bool(vec& v, op o) { // Gather all boolean terms. vec b; @@ -119,1632 +119,1628 @@ namespace spot // - OrRat(Exps1...,Bool1,Exps2...,Bool2,Exps3...) = // AndRat(Or(Bool1,Bool2),Exps1...,Exps2...,Exps3...) if (!b.empty()) - v.insert(v.begin(), ltl::fnode::multop(o, std::move(b))); + v.insert(v.begin(), fnode::multop(o, std::move(b))); } } - namespace ltl + const fnode* fnode::unique(const fnode* f) { - const fnode* fnode::unique(const fnode* f) - { - auto ires = m.uniq.emplace(f); - if (!ires.second) - { - //(*ires.first)->dump(std::cerr << "UNI: ") << '\n'; - for (auto c: *f) - c->destroy(); - delete f; - return (*ires.first)->clone(); - } - //f->dump(std::cerr << "INS: ") << '\n'; - return f; - } + auto ires = m.uniq.emplace(f); + if (!ires.second) + { + //(*ires.first)->dump(std::cerr << "UNI: ") << '\n'; + for (auto c: *f) + c->destroy(); + delete f; + return (*ires.first)->clone(); + } + //f->dump(std::cerr << "INS: ") << '\n'; + return f; + } - void - fnode::destroy_aux() const - { - if (SPOT_UNLIKELY(is(op::ap))) - { - auto i = m.ap2name.find(id()); - auto n = m.name2ap.erase(i->second); - assert(n == 1); - (void)n; - m.ap2name.erase(i); - } - else - { - auto n = m.uniq.erase(this); - assert(n == 1); - (void)n; - for (auto c: *this) - c->destroy(); - } - delete this; - } + void + fnode::destroy_aux() const + { + if (SPOT_UNLIKELY(is(op::ap))) + { + auto i = m.ap2name.find(id()); + auto n = m.name2ap.erase(i->second); + assert(n == 1); + (void)n; + m.ap2name.erase(i); + } + else + { + auto n = m.uniq.erase(this); + assert(n == 1); + (void)n; + for (auto c: *this) + c->destroy(); + } + delete this; + } - std::string fnode::kindstr() const - { - switch (op_) - { + std::string fnode::kindstr() const + { + switch (op_) + { #define C(x) \ case op::x: \ return #x; \ break - C(ff); - C(tt); - C(eword); - C(ap); - C(Not); - C(X); - C(F); - C(G); - C(Closure); - C(NegClosure); - C(NegClosureMarked); - C(Xor); - C(Implies); - C(Equiv); - C(U); - C(R); - C(W); - C(M); - C(EConcat); - C(EConcatMarked); - C(UConcat); - C(Or); - C(OrRat); - C(And); - C(AndRat); - C(AndNLM); - C(Concat); - C(Fusion); - C(Star); - C(FStar); + C(ff); + C(tt); + C(eword); + C(ap); + C(Not); + C(X); + C(F); + C(G); + C(Closure); + C(NegClosure); + C(NegClosureMarked); + C(Xor); + C(Implies); + C(Equiv); + C(U); + C(R); + C(W); + C(M); + C(EConcat); + C(EConcatMarked); + C(UConcat); + C(Or); + C(OrRat); + C(And); + C(AndRat); + C(AndNLM); + C(Concat); + C(Fusion); + C(Star); + C(FStar); #undef C + } + SPOT_UNREACHABLE(); + } + + const fnode* + fnode::multop(op o, vec v) + { + // Inline children of same kind. + // + // When we construct a formula such as Multop(Op,X,Multop(Op,Y,Z)) + // we will want to inline it as Multop(Op,X,Y,Z). + { + vec inlined; + vec::iterator i = v.begin(); + while (i != v.end()) + { + // Some simplification routines erase terms using null + // pointers that we must ignore. + if ((*i) == nullptr) + { + // FIXME: For commutative operators we should replace + // the pointer by the first non-null value at the end + // of the array instead of calling erase. + i = v.erase(i); + continue; + } + if ((*i)->is(o)) + { + unsigned ps = (*i)->size(); + for (unsigned n = 0; n < ps; ++n) + inlined.push_back((*i)->nth(n)->clone()); + (*i)->destroy(); + // FIXME: Do not use erase. See previous FIXME. + i = v.erase(i); + continue; + } + // All operators except "Concat" and "Fusion" are + // commutative, so we just keep a list of the inlined + // arguments that should later be added to the vector. + // For concat we have to keep track of the order of + // all the arguments. + if (o == op::Concat || o == op::Fusion) + inlined.push_back(*i); + ++i; } - SPOT_UNREACHABLE(); + if (o == op::Concat || o == op::Fusion) + v.swap(inlined); + else + v.insert(v.end(), inlined.begin(), inlined.end()); } - const fnode* - fnode::multop(op o, vec v) - { - // Inline children of same kind. - // - // When we construct a formula such as Multop(Op,X,Multop(Op,Y,Z)) - // we will want to inline it as Multop(Op,X,Y,Z). + if (o != op::Concat && o != op::Fusion) + std::sort(v.begin(), v.end(), formula_ptr_less_than_bool_first()); + + unsigned orig_size = v.size(); + + const fnode* neutral; + const fnode* neutral2; + const fnode* abs; + const fnode* abs2; + const fnode* weak_abs; + switch (o) { - vec inlined; - vec::iterator i = v.begin(); - while (i != v.end()) - { - // Some simplification routines erase terms using null - // pointers that we must ignore. - if ((*i) == nullptr) - { - // FIXME: For commutative operators we should replace - // the pointer by the first non-null value at the end - // of the array instead of calling erase. - i = v.erase(i); - continue; - } - if ((*i)->is(o)) - { - unsigned ps = (*i)->size(); - for (unsigned n = 0; n < ps; ++n) - inlined.push_back((*i)->nth(n)->clone()); - (*i)->destroy(); - // FIXME: Do not use erase. See previous FIXME. - i = v.erase(i); - continue; - } - // All operators except "Concat" and "Fusion" are - // commutative, so we just keep a list of the inlined - // arguments that should later be added to the vector. - // For concat we have to keep track of the order of - // all the arguments. - if (o == op::Concat || o == op::Fusion) - inlined.push_back(*i); - ++i; - } - if (o == op::Concat || o == op::Fusion) - v.swap(inlined); - else - v.insert(v.end(), inlined.begin(), inlined.end()); - } + case op::And: + neutral = tt(); + neutral2 = nullptr; + abs = ff(); + abs2 = nullptr; + weak_abs = nullptr; + break; + case op::AndRat: + neutral = one_star(); + neutral2 = nullptr; + abs = ff(); + abs2 = nullptr; + weak_abs = eword(); + gather_bool(v, op::And); + break; + case op::AndNLM: + neutral = eword(); + neutral2 = nullptr; + abs = ff(); + abs2 = nullptr; + weak_abs = tt(); + gather_bool(v, op::And); + break; + case op::Or: + neutral = ff(); + neutral2 = nullptr; + abs = tt(); + abs2 = nullptr; + weak_abs = nullptr; + break; + case op::OrRat: + neutral = ff(); + neutral2 = nullptr; + abs = one_star(); + abs2 = nullptr; + weak_abs = nullptr; + gather_bool(v, op::Or); + break; + case op::Concat: + neutral = eword(); + neutral2 = nullptr; + abs = ff(); + abs2 = nullptr; + weak_abs = nullptr; - if (o != op::Concat && o != op::Fusion) - std::sort(v.begin(), v.end(), formula_ptr_less_than_bool_first()); - - unsigned orig_size = v.size(); - - const fnode* neutral; - const fnode* neutral2; - const fnode* abs; - const fnode* abs2; - const fnode* weak_abs; - switch (o) + // - Concat(Exps1...,FExps2...,1[*],FExps3...,Exps4) = + // Concat(Exps1...,1[*],Exps4) + // If FExps2... and FExps3 all accept [*0]. { - case op::And: - neutral = tt(); - neutral2 = nullptr; - abs = ff(); - abs2 = nullptr; - weak_abs = nullptr; - break; - case op::AndRat: - neutral = one_star(); - neutral2 = nullptr; - abs = ff(); - abs2 = nullptr; - weak_abs = eword(); - gather_bool(v, op::And); - break; - case op::AndNLM: - neutral = eword(); - neutral2 = nullptr; - abs = ff(); - abs2 = nullptr; - weak_abs = tt(); - gather_bool(v, op::And); - break; - case op::Or: - neutral = ff(); - neutral2 = nullptr; - abs = tt(); - abs2 = nullptr; - weak_abs = nullptr; - break; - case op::OrRat: - neutral = ff(); - neutral2 = nullptr; - abs = one_star(); - abs2 = nullptr; - weak_abs = nullptr; - gather_bool(v, op::Or); - break; - case op::Concat: - neutral = eword(); - neutral2 = nullptr; - abs = ff(); - abs2 = nullptr; - weak_abs = nullptr; - - // - Concat(Exps1...,FExps2...,1[*],FExps3...,Exps4) = - // Concat(Exps1...,1[*],Exps4) - // If FExps2... and FExps3 all accept [*0]. - { - vec::iterator i = v.begin(); - const fnode* os = one_star(); - while (i != v.end()) - { - while (i != v.end() && !(*i)->accepts_eword()) - ++i; - if (i == v.end()) - break; - vec::iterator b = i; - // b is the first expressions that accepts [*0]. - // let's find more, and locate the position of - // 1[*] at the same time. - bool os_seen = false; - do - { - os_seen |= (*i == os); - ++i; - } - while (i != v.end() && (*i)->accepts_eword()); - - if (os_seen) // [b..i) is a range that contains [*]. - { - // Place [*] at the start of the range, and erase - // all other formulae. - (*b)->destroy(); - *b++ = os->clone(); - for (vec::iterator c = b; c < i; ++c) - (*c)->destroy(); - i = v.erase(b, i); - } - } - } - - break; - case op::Fusion: - neutral = tt(); - neutral2 = nullptr; - abs = ff(); - abs2 = eword(); - weak_abs = nullptr; - - // Make a first pass to group adjacent Boolean formulae. - // - Fusion(Exps1...,BoolExp1...BoolExpN,Exps2,Exps3...) = - // Fusion(Exps1...,And(BoolExp1...BoolExpN),Exps2,Exps3...) - { - vec::iterator i = v.begin(); - while (i != v.end()) - { - if ((*i)->is_boolean()) - { - vec::iterator first = i; - ++i; - if (i == v.end()) - break; - if (!(*i)->is_boolean()) - { - ++i; - continue; - } - do - ++i; - while (i != v.end() && (*i)->is_boolean()); - // We have at least two adjacent Boolean formulae. - // Replace the first one by the conjunction of all. - // FIXME: Investigate the removal of the temporary - // vector, by allowing range to be passed to - // multop() instead of entire containers. We - // should then able to do - // *first = multop(op::And, first, i); - // i = v.erase(first + 1, i); - vec b; - b.insert(b.begin(), first, i); - i = v.erase(first + 1, i); - *first = multop(op::And, b); - } - else - { - ++i; - } - } - } - - break; - - default: - neutral = nullptr; - neutral2 = nullptr; - abs = nullptr; - abs2 = nullptr; - weak_abs = nullptr; - break; - } - - // Remove duplicates (except for Concat and Fusion). We can't use - // std::unique(), because we must destroy() any formula we drop. - // Also ignore neutral elements and handle absorbent elements. - { - const fnode* last = nullptr; - vec::iterator i = v.begin(); - bool weak_abs_seen = false; - while (i != v.end()) - { - if ((*i == neutral) || (*i == neutral2) || (*i == last)) - { - (*i)->destroy(); - i = v.erase(i); - } - else if (*i == abs || *i == abs2) - { - for (i = v.begin(); i != v.end(); ++i) - (*i)->destroy(); - return abs->clone(); - } - else - { - weak_abs_seen |= (*i == weak_abs); - if (o != op::Concat && o != op::Fusion) - // Don't remove duplicates - last = *i; + vec::iterator i = v.begin(); + const fnode* os = one_star(); + while (i != v.end()) + { + while (i != v.end() && !(*i)->accepts_eword()) ++i; - } - } - - if (weak_abs_seen) - { - if (o == op::AndRat) - { - // We have a* && [*0] && c = 0 - // and a* && [*0] && c* = [*0] - // So if [*0] has been seen, check if alls term - // recognize the empty word. - bool acc_eword = true; - for (i = v.begin(); i != v.end(); ++i) - { - acc_eword &= (*i)->accepts_eword(); - (*i)->destroy(); - } - if (acc_eword) - return weak_abs; - else - return abs; - } - else - { - // Similarly, a* & 1 & (c;d) = c;d - // a* & 1 & c* = 1 - assert(o == op::AndNLM); - vec tmp; - for (auto i: v) - { - if (i == weak_abs) - continue; - if (i->accepts_eword()) - { - i->destroy(); - continue; - } - tmp.push_back(i); - } - if (tmp.empty()) - tmp.push_back(weak_abs); - v.swap(tmp); - } - } - else if (o == op::Concat || o == op::Fusion) - { - // Perform an extra loop to merge starable items. - // f;f -> f[*2] - // f;f[*i..j] -> f[*i+1..j+1] - // f[*i..j];f -> f[*i+1..j+1] - // f[*i..j];f[*k..l] -> f[*i+k..j+l] - // same for FStar: - // f:f -> f[:*2] - // f:f[*i..j] -> f[:*i+1..j+1] - // f[:*i..j];f -> f[:*i+1..j+1] - // f[:*i..j];f[:*k..l] -> f[:*i+k..j+l] - op bop = o == op::Concat ? op::Star : op::FStar; - i = v.begin(); - while (i != v.end()) - { - vec::iterator fpos = i; - const fnode* f; - unsigned min; - unsigned max; - bool changed = false; - if ((*i)->is(bop)) - { - f = (*i)->nth(0); - min = (*i)->min(); - max = (*i)->max(); - } - else - { - f = *i; - min = max = 1; - } - - ++i; - while (i != v.end()) - { - const fnode* f2; - unsigned min2; - unsigned max2; - if ((*i)->is(bop)) - { - f2 = (*i)->nth(0); - if (f2 != f) - break; - min2 = (*i)->min(); - max2 = (*i)->max(); - } - else - { - f2 = *i; - if (f2 != f) - break; - min2 = max2 = 1; - } - if (min2 == unbounded()) - min = unbounded(); - else if (min != unbounded()) - min += min2; - if (max2 == unbounded()) - max = unbounded(); - else if (max != unbounded()) - max += max2; - (*i)->destroy(); - i = v.erase(i); - changed = true; - } - if (changed) - { - const fnode* newfs = bunop(bop, f->clone(), min, max); - (*fpos)->destroy(); - *fpos = newfs; - } - } - } - } - - vec::size_type s = v.size(); - if (s == 0) - { - assert(neutral != nullptr); - return neutral->clone(); - } - else if (s == 1) - { - // Simply replace Multop(Op,X) by X. - // Except we should never reduce the - // arguments of a Fusion operator to - // a list with a single formula that - // accepts [*0]. - const fnode* res = v[0]; - if (o != op::Fusion || orig_size == 1 - || !res->accepts_eword()) - return res; - // If Fusion(f, ...) reduces to Fusion(f), emit Fusion(1,f). - // to ensure that [*0] is not accepted. - v.insert(v.begin(), tt()); - } - - - auto mem = operator new(sizeof(fnode) - + (v.size() - 1)*sizeof(*children)); - return unique(new(mem) fnode(o, v.begin(), v.end())); - } - - const fnode* - fnode::bunop(op o, const fnode* child, uint8_t min, uint8_t max) - { - assert(min <= max); - - const fnode* neutral = nullptr; - switch (o) - { - case op::Star: - neutral = eword(); - break; - case op::FStar: - neutral = tt(); - break; - default: - SPOT_UNREACHABLE(); - } - - - // common trivial simplifications - - // - [*0][*min..max] = [*0] - // - [*0][:*0..max] = 1 - // - [*0][:*min..max] = 0 if min > 0 - if (child->is_eword()) - switch (o) - { - case op::Star: - return neutral; - case op::FStar: - if (min == 0) - return neutral; - else - return ff(); - default: - SPOT_UNREACHABLE(); - } - - // - 0[*0..max] = [*0] - // - 0[*min..max] = 0 if min > 0 - // - b[:*0..max] = 1 - // - b[:*min..max] = 0 if min > 0 - if (child->is_ff() - || (o == op::FStar && child->is_boolean())) - { - if (min == 0) - { - child->destroy(); - return neutral; - } - return child; - } - - // - Exp[*0] = [*0] - // - Exp[:*0] = 1 - if (max == 0) - { - child->destroy(); - return neutral; - } - - // - Exp[*1] = Exp - // - Exp[:*1] = Exp if Exp does not accept [*0] - if (min == 1 && max == 1) - if (o == op::Star || !child->accepts_eword()) - return child; - - // - Exp[*i..j][*k..l] = Exp[*ik..jl] if i*(k+1)<=jk+1. - // - Exp[:*i..j][:*k..l] = Exp[:*ik..jl] if i*(k+1)<=jk+1. - if (child->is(o)) - { - unsigned i = child->min(); - unsigned j = child->max(); - - // Exp has to be true between i*min and j*min - // then between i*(min+1) and j*(min+1) - // ... - // finally between i*max and j*max - // - // We can merge these intervals into [i*min..j*max] iff the - // first are adjacent or overlap, i.e. iff - // i*(min+1) <= j*min+1. - // (Because i<=j, this entails that the other intervals also - // overlap). - - const fnode* exp = child->nth(0); - if (j == unbounded()) - { - min *= i; - max = unbounded(); - - // Exp[*min..max] - exp->clone(); - child->destroy(); - child = exp; - } - else - { - if (i * (min + 1) <= (j * min) + 1) + if (i == v.end()) + break; + vec::iterator b = i; + // b is the first expressions that accepts [*0]. + // let's find more, and locate the position of + // 1[*] at the same time. + bool os_seen = false; + do { - min *= i; - if (max != unbounded()) - { - if (j == unbounded()) - max = unbounded(); - else - max *= j; - } - exp->clone(); - child->destroy(); - child = exp; + os_seen |= (*i == os); + ++i; + } + while (i != v.end() && (*i)->accepts_eword()); + + if (os_seen) // [b..i) is a range that contains [*]. + { + // Place [*] at the start of the range, and erase + // all other formulae. + (*b)->destroy(); + *b++ = os->clone(); + for (vec::iterator c = b; c < i; ++c) + (*c)->destroy(); + i = v.erase(b, i); } } } - return unique(new fnode(o, child, min, max)); + break; + case op::Fusion: + neutral = tt(); + neutral2 = nullptr; + abs = ff(); + abs2 = eword(); + weak_abs = nullptr; + + // Make a first pass to group adjacent Boolean formulae. + // - Fusion(Exps1...,BoolExp1...BoolExpN,Exps2,Exps3...) = + // Fusion(Exps1...,And(BoolExp1...BoolExpN),Exps2,Exps3...) + { + vec::iterator i = v.begin(); + while (i != v.end()) + { + if ((*i)->is_boolean()) + { + vec::iterator first = i; + ++i; + if (i == v.end()) + break; + if (!(*i)->is_boolean()) + { + ++i; + continue; + } + do + ++i; + while (i != v.end() && (*i)->is_boolean()); + // We have at least two adjacent Boolean formulae. + // Replace the first one by the conjunction of all. + // FIXME: Investigate the removal of the temporary + // vector, by allowing range to be passed to + // multop() instead of entire containers. We + // should then able to do + // *first = multop(op::And, first, i); + // i = v.erase(first + 1, i); + vec b; + b.insert(b.begin(), first, i); + i = v.erase(first + 1, i); + *first = multop(op::And, b); + } + else + { + ++i; + } + } + } + + break; + + default: + neutral = nullptr; + neutral2 = nullptr; + abs = nullptr; + abs2 = nullptr; + weak_abs = nullptr; + break; + } + + // Remove duplicates (except for Concat and Fusion). We can't use + // std::unique(), because we must destroy() any formula we drop. + // Also ignore neutral elements and handle absorbent elements. + { + const fnode* last = nullptr; + vec::iterator i = v.begin(); + bool weak_abs_seen = false; + while (i != v.end()) + { + if ((*i == neutral) || (*i == neutral2) || (*i == last)) + { + (*i)->destroy(); + i = v.erase(i); + } + else if (*i == abs || *i == abs2) + { + for (i = v.begin(); i != v.end(); ++i) + (*i)->destroy(); + return abs->clone(); + } + else + { + weak_abs_seen |= (*i == weak_abs); + if (o != op::Concat && o != op::Fusion) + // Don't remove duplicates + last = *i; + ++i; + } + } + + if (weak_abs_seen) + { + if (o == op::AndRat) + { + // We have a* && [*0] && c = 0 + // and a* && [*0] && c* = [*0] + // So if [*0] has been seen, check if alls term + // recognize the empty word. + bool acc_eword = true; + for (i = v.begin(); i != v.end(); ++i) + { + acc_eword &= (*i)->accepts_eword(); + (*i)->destroy(); + } + if (acc_eword) + return weak_abs; + else + return abs; + } + else + { + // Similarly, a* & 1 & (c;d) = c;d + // a* & 1 & c* = 1 + assert(o == op::AndNLM); + vec tmp; + for (auto i: v) + { + if (i == weak_abs) + continue; + if (i->accepts_eword()) + { + i->destroy(); + continue; + } + tmp.push_back(i); + } + if (tmp.empty()) + tmp.push_back(weak_abs); + v.swap(tmp); + } + } + else if (o == op::Concat || o == op::Fusion) + { + // Perform an extra loop to merge starable items. + // f;f -> f[*2] + // f;f[*i..j] -> f[*i+1..j+1] + // f[*i..j];f -> f[*i+1..j+1] + // f[*i..j];f[*k..l] -> f[*i+k..j+l] + // same for FStar: + // f:f -> f[:*2] + // f:f[*i..j] -> f[:*i+1..j+1] + // f[:*i..j];f -> f[:*i+1..j+1] + // f[:*i..j];f[:*k..l] -> f[:*i+k..j+l] + op bop = o == op::Concat ? op::Star : op::FStar; + i = v.begin(); + while (i != v.end()) + { + vec::iterator fpos = i; + const fnode* f; + unsigned min; + unsigned max; + bool changed = false; + if ((*i)->is(bop)) + { + f = (*i)->nth(0); + min = (*i)->min(); + max = (*i)->max(); + } + else + { + f = *i; + min = max = 1; + } + + ++i; + while (i != v.end()) + { + const fnode* f2; + unsigned min2; + unsigned max2; + if ((*i)->is(bop)) + { + f2 = (*i)->nth(0); + if (f2 != f) + break; + min2 = (*i)->min(); + max2 = (*i)->max(); + } + else + { + f2 = *i; + if (f2 != f) + break; + min2 = max2 = 1; + } + if (min2 == unbounded()) + min = unbounded(); + else if (min != unbounded()) + min += min2; + if (max2 == unbounded()) + max = unbounded(); + else if (max != unbounded()) + max += max2; + (*i)->destroy(); + i = v.erase(i); + changed = true; + } + if (changed) + { + const fnode* newfs = bunop(bop, f->clone(), min, max); + (*fpos)->destroy(); + *fpos = newfs; + } + } + } } - const fnode* - fnode::unop(op o, const fnode* f) - { - // Some trivial simplifications. + vec::size_type s = v.size(); + if (s == 0) + { + assert(neutral != nullptr); + return neutral->clone(); + } + else if (s == 1) + { + // Simply replace Multop(Op,X) by X. + // Except we should never reduce the + // arguments of a Fusion operator to + // a list with a single formula that + // accepts [*0]. + const fnode* res = v[0]; + if (o != op::Fusion || orig_size == 1 + || !res->accepts_eword()) + return res; + // If Fusion(f, ...) reduces to Fusion(f), emit Fusion(1,f). + // to ensure that [*0] is not accepted. + v.insert(v.begin(), tt()); + } + + + auto mem = operator new(sizeof(fnode) + + (v.size() - 1)*sizeof(*children)); + return unique(new(mem) fnode(o, v.begin(), v.end())); + } + + const fnode* + fnode::bunop(op o, const fnode* child, uint8_t min, uint8_t max) + { + assert(min <= max); + + const fnode* neutral = nullptr; + switch (o) + { + case op::Star: + neutral = eword(); + break; + case op::FStar: + neutral = tt(); + break; + default: + SPOT_UNREACHABLE(); + } + + + // common trivial simplifications + + // - [*0][*min..max] = [*0] + // - [*0][:*0..max] = 1 + // - [*0][:*min..max] = 0 if min > 0 + if (child->is_eword()) switch (o) { - case op::F: - case op::G: - { - // F and G are idempotent. - if (f->is(o)) - return f; - - // F(0) = G(0) = 0 - // F(1) = G(1) = 1 - if (f->is_ff() || f->is_tt()) - return f; - - assert(!f->is_eword()); - } - break; - - case op::Not: - { - // !1 = 0 - if (f->is_tt()) - return ff(); - // !0 = 1 - if (f->is_ff()) - return tt(); - // ![*0] = 1[+] - if (f->is_eword()) - return bunop(op::Star, tt(), 1); - - auto fop = f->kind(); - // "Not" is an involution. - if (fop == o) - { - auto c = f->nth(0)->clone(); - f->destroy(); - return c; - } - // !Closure(Exp) = NegClosure(Exp) - if (fop == op::Closure) - { - const fnode* c = unop(op::NegClosure, f->nth(0)->clone()); - f->destroy(); - return c; - } - // !NegClosure(Exp) = Closure(Exp) - if (fop == op::NegClosure || fop == op::NegClosureMarked) - { - const fnode* c = unop(op::Closure, f->nth(0)->clone()); - f->destroy(); - return c; - } - break; - } - case op::X: - // X(1) = 1, X(0) = 0 - if (f->is_tt() || f->is_ff()) - return f; - assert(!f->is_eword()); - break; - - case op::Closure: - // {0} = 0, {1} = 1, {b} = b - if (f->is_boolean()) - return f; - // {[*0]} = 0 - if (f->is_eword()) + case op::Star: + return neutral; + case op::FStar: + if (min == 0) + return neutral; + else return ff(); - break; + default: + SPOT_UNREACHABLE(); + } - case op::NegClosure: - case op::NegClosureMarked: - // {1} = 0 + // - 0[*0..max] = [*0] + // - 0[*min..max] = 0 if min > 0 + // - b[:*0..max] = 1 + // - b[:*min..max] = 0 if min > 0 + if (child->is_ff() + || (o == op::FStar && child->is_boolean())) + { + if (min == 0) + { + child->destroy(); + return neutral; + } + return child; + } + + // - Exp[*0] = [*0] + // - Exp[:*0] = 1 + if (max == 0) + { + child->destroy(); + return neutral; + } + + // - Exp[*1] = Exp + // - Exp[:*1] = Exp if Exp does not accept [*0] + if (min == 1 && max == 1) + if (o == op::Star || !child->accepts_eword()) + return child; + + // - Exp[*i..j][*k..l] = Exp[*ik..jl] if i*(k+1)<=jk+1. + // - Exp[:*i..j][:*k..l] = Exp[:*ik..jl] if i*(k+1)<=jk+1. + if (child->is(o)) + { + unsigned i = child->min(); + unsigned j = child->max(); + + // Exp has to be true between i*min and j*min + // then between i*(min+1) and j*(min+1) + // ... + // finally between i*max and j*max + // + // We can merge these intervals into [i*min..j*max] iff the + // first are adjacent or overlap, i.e. iff + // i*(min+1) <= j*min+1. + // (Because i<=j, this entails that the other intervals also + // overlap). + + const fnode* exp = child->nth(0); + if (j == unbounded()) + { + min *= i; + max = unbounded(); + + // Exp[*min..max] + exp->clone(); + child->destroy(); + child = exp; + } + else + { + if (i * (min + 1) <= (j * min) + 1) + { + min *= i; + if (max != unbounded()) + { + if (j == unbounded()) + max = unbounded(); + else + max *= j; + } + exp->clone(); + child->destroy(); + child = exp; + } + } + } + + return unique(new fnode(o, child, min, max)); + } + + const fnode* + fnode::unop(op o, const fnode* f) + { + // Some trivial simplifications. + switch (o) + { + case op::F: + case op::G: + { + // F and G are idempotent. + if (f->is(o)) + return f; + + // F(0) = G(0) = 0 + // F(1) = G(1) = 1 + if (f->is_ff() || f->is_tt()) + return f; + + assert(!f->is_eword()); + } + break; + + case op::Not: + { + // !1 = 0 if (f->is_tt()) return ff(); - // {0} = 1, {[*0]} = 1 - if (f->is_ff() || f->is_eword()) + // !0 = 1 + if (f->is_ff()) return tt(); - // {b} = !b - if (f->is_boolean()) - return unop(op::Not, f); + // ![*0] = 1[+] + if (f->is_eword()) + return bunop(op::Star, tt(), 1); + + auto fop = f->kind(); + // "Not" is an involution. + if (fop == o) + { + auto c = f->nth(0)->clone(); + f->destroy(); + return c; + } + // !Closure(Exp) = NegClosure(Exp) + if (fop == op::Closure) + { + const fnode* c = unop(op::NegClosure, f->nth(0)->clone()); + f->destroy(); + return c; + } + // !NegClosure(Exp) = Closure(Exp) + if (fop == op::NegClosure || fop == op::NegClosureMarked) + { + const fnode* c = unop(op::Closure, f->nth(0)->clone()); + f->destroy(); + return c; + } break; - default: - SPOT_UNREACHABLE(); } + case op::X: + // X(1) = 1, X(0) = 0 + if (f->is_tt() || f->is_ff()) + return f; + assert(!f->is_eword()); + break; - return unique(new fnode(o, {f})); - } + case op::Closure: + // {0} = 0, {1} = 1, {b} = b + if (f->is_boolean()) + return f; + // {[*0]} = 0 + if (f->is_eword()) + return ff(); + break; - const fnode* - fnode::binop(op o, const fnode* first, const fnode* second) - { - // Sort the operands of commutative operators, so that for - // example the formula instance for 'a xor b' is the same as - // that for 'b xor a'. + case op::NegClosure: + case op::NegClosureMarked: + // {1} = 0 + if (f->is_tt()) + return ff(); + // {0} = 1, {[*0]} = 1 + if (f->is_ff() || f->is_eword()) + return tt(); + // {b} = !b + if (f->is_boolean()) + return unop(op::Not, f); + break; + default: + SPOT_UNREACHABLE(); + } - // Trivial identities: - switch (o) + return unique(new fnode(o, {f})); + } + + const fnode* + fnode::binop(op o, const fnode* first, const fnode* second) + { + // Sort the operands of commutative operators, so that for + // example the formula instance for 'a xor b' is the same as + // that for 'b xor a'. + + // Trivial identities: + switch (o) + { + case op::Xor: { - case op::Xor: - { - // Xor is commutative: sort operands. - formula_ptr_less_than_bool_first cmp; - if (cmp(second, first)) - std::swap(second, first); - } - // - (1 ^ Exp) = !Exp - // - (0 ^ Exp) = Exp - if (first->is_tt()) - return unop(op::Not, second); - if (first->is_ff()) - return second; - if (first == second) - { - first->destroy(); - second->destroy(); - return ff(); - } - // We expect constants to appear first, because they are - // instantiated first. - assert(!second->is_constant()); - break; - case op::Equiv: - { - // Equiv is commutative: sort operands. - formula_ptr_less_than_bool_first cmp; - if (cmp(second, first)) - std::swap(second, first); - } - // - (0 <=> Exp) = !Exp - // - (1 <=> Exp) = Exp - // - (Exp <=> Exp) = 1 - if (first->is_ff()) - return unop(op::Not, second); - if (first->is_tt()) - return second; - if (first == second) - { - first->destroy(); - second->destroy(); - return tt(); - } - // We expect constants to appear first, because they are - // instantiated first. - assert(!second->is_constant()); - break; - case op::Implies: - // - (1 => Exp) = Exp - // - (0 => Exp) = 1 - // - (Exp => 1) = 1 - // - (Exp => 0) = !Exp - // - (Exp => Exp) = 1 - if (first->is_tt()) - return second; - if (first->is_ff()) - { - second->destroy(); - return tt(); - } - if (second->is_tt()) - { - first->destroy(); - return second; - } - if (second->is_ff()) - return unop(op::Not, first); - if (first == second) - { - first->destroy(); - second->destroy(); - return tt(); - } - break; - case op::U: - // - (Exp U 1) = 1 - // - (Exp U 0) = 0 - // - (0 U Exp) = Exp - // - (Exp U Exp) = Exp - if (second->is_tt() - || second->is_ff() - || first->is_ff() - || first == second) - { - first->destroy(); - return second; - } - break; - case op::W: - // - (Exp W 1) = 1 - // - (0 W Exp) = Exp - // - (1 W Exp) = 1 - // - (Exp W Exp) = Exp - if (second->is_tt() - || first->is_ff() - || first == second) - { - first->destroy(); - return second; - } - if (first->is_tt()) - { - second->destroy(); - return first; - } - break; - case op::R: - // - (Exp R 1) = 1 - // - (Exp R 0) = 0 - // - (1 R Exp) = Exp - // - (Exp R Exp) = Exp - if (second->is_tt() - || second->is_ff() - || first->is_tt() - || first == second) - { - first->destroy(); - return second; - } - break; - case op::M: - // - (Exp M 0) = 0 - // - (1 M Exp) = Exp - // - (0 M Exp) = 0 - // - (Exp M Exp) = Exp - if (second->is_ff() - || first->is_tt() - || first == second) - { - first->destroy(); - return second; - } - if (first->is_ff()) - { - second->destroy(); - return first; - } - break; - case op::EConcat: - case op::EConcatMarked: - // - 0 <>-> Exp = 0 - // - 1 <>-> Exp = Exp - // - [*0] <>-> Exp = 0 - // - Exp <>-> 0 = 0 - // - boolExp <>-> Exp = boolExp & Exp - if (first->is_tt()) - return second; - if (first->is_ff() - || first->is_eword()) - { - second->destroy(); - return ff(); - } - if (second->is_ff()) - { - first->destroy(); - return second; - } - if (first->is_boolean()) - return multop(op::And, {first, second}); - break; - case op::UConcat: - // - 0 []-> Exp = 1 - // - 1 []-> Exp = Exp - // - [*0] []-> Exp = 1 - // - Exp []-> 1 = 1 - // - boolExp []-> Exp = !boolExp | Exp - if (first->is_tt()) - return second; - if (first->is_ff() - || first->is_eword()) - { - second->destroy(); - return tt(); - } - if (second->is_tt()) - { - first->destroy(); - return second; - } - if (first->is_boolean()) - return multop(op::Or, {unop(op::Not, first), second}); - break; - default: - SPOT_UNREACHABLE(); + // Xor is commutative: sort operands. + formula_ptr_less_than_bool_first cmp; + if (cmp(second, first)) + std::swap(second, first); } - - auto mem = operator new(sizeof(fnode) + sizeof(*children)); - return unique(new(mem) fnode(o, {first, second})); - } - - - const fnode* - fnode::ap(const std::string& name) - { - auto ires = m.name2ap.emplace(name, nullptr); - if (!ires.second) - return ires.first->second->clone(); - // Name the formula before creating it, because - // the constructor will call ap_name(). - m.ap2name.emplace(next_id_, name); - // next_id_ is incremented by setup_props(), called by the - // constructor of fnode - return ires.first->second = new fnode(op::ap, {}); - } - - const std::string& - fnode::ap_name() const - { - if (op_ != op::ap) - throw std::runtime_error("ap_name() called on non-AP formula"); - auto i = m.ap2name.find(id()); - assert(i != m.ap2name.end()); - return i->second; - } - - size_t fnode::next_id_ = 0U; - const fnode* fnode::ff_ = new fnode(op::ff, {}); - const fnode* fnode::tt_ = new fnode(op::tt, {}); - const fnode* fnode::ew_ = new fnode(op::eword, {}); - const fnode* fnode::one_star_ = nullptr; // Only built when necessary. - - void fnode::setup_props(op o) - { - op_ = o; - id_ = next_id_++; - // If the counter of formulae ever loops, we want to skip the - // first three values, because they are permanently associated - // to constants, and it is convenient to have constants - // smaller than all other formulas. - if (next_id_ == 0) - next_id_ = 3; - - switch (op_) + // - (1 ^ Exp) = !Exp + // - (0 ^ Exp) = Exp + if (first->is_tt()) + return unop(op::Not, second); + if (first->is_ff()) + return second; + if (first == second) + { + first->destroy(); + second->destroy(); + return ff(); + } + // We expect constants to appear first, because they are + // instantiated first. + assert(!second->is_constant()); + break; + case op::Equiv: { - case op::ff: - case op::tt: - is_.boolean = true; - is_.sugar_free_boolean = true; - is_.in_nenoform = true; - is_.syntactic_si = true; // for LTL (not PSL) - is_.sugar_free_ltl = true; - is_.ltl_formula = true; - is_.psl_formula = true; - is_.sere_formula = true; - is_.finite = true; - is_.eventual = true; - is_.universal = true; - is_.syntactic_safety = true; - is_.syntactic_guarantee = true; - is_.syntactic_obligation = true; - is_.syntactic_recurrence = true; - is_.syntactic_persistence = true; - is_.not_marked = true; - is_.accepting_eword = false; - is_.lbt_atomic_props = true; - is_.spin_atomic_props = true; + // Equiv is commutative: sort operands. + formula_ptr_less_than_bool_first cmp; + if (cmp(second, first)) + std::swap(second, first); + } + // - (0 <=> Exp) = !Exp + // - (1 <=> Exp) = Exp + // - (Exp <=> Exp) = 1 + if (first->is_ff()) + return unop(op::Not, second); + if (first->is_tt()) + return second; + if (first == second) + { + first->destroy(); + second->destroy(); + return tt(); + } + // We expect constants to appear first, because they are + // instantiated first. + assert(!second->is_constant()); + break; + case op::Implies: + // - (1 => Exp) = Exp + // - (0 => Exp) = 1 + // - (Exp => 1) = 1 + // - (Exp => 0) = !Exp + // - (Exp => Exp) = 1 + if (first->is_tt()) + return second; + if (first->is_ff()) + { + second->destroy(); + return tt(); + } + if (second->is_tt()) + { + first->destroy(); + return second; + } + if (second->is_ff()) + return unop(op::Not, first); + if (first == second) + { + first->destroy(); + second->destroy(); + return tt(); + } + break; + case op::U: + // - (Exp U 1) = 1 + // - (Exp U 0) = 0 + // - (0 U Exp) = Exp + // - (Exp U Exp) = Exp + if (second->is_tt() + || second->is_ff() + || first->is_ff() + || first == second) + { + first->destroy(); + return second; + } + break; + case op::W: + // - (Exp W 1) = 1 + // - (0 W Exp) = Exp + // - (1 W Exp) = 1 + // - (Exp W Exp) = Exp + if (second->is_tt() + || first->is_ff() + || first == second) + { + first->destroy(); + return second; + } + if (first->is_tt()) + { + second->destroy(); + return first; + } + break; + case op::R: + // - (Exp R 1) = 1 + // - (Exp R 0) = 0 + // - (1 R Exp) = Exp + // - (Exp R Exp) = Exp + if (second->is_tt() + || second->is_ff() + || first->is_tt() + || first == second) + { + first->destroy(); + return second; + } + break; + case op::M: + // - (Exp M 0) = 0 + // - (1 M Exp) = Exp + // - (0 M Exp) = 0 + // - (Exp M Exp) = Exp + if (second->is_ff() + || first->is_tt() + || first == second) + { + first->destroy(); + return second; + } + if (first->is_ff()) + { + second->destroy(); + return first; + } + break; + case op::EConcat: + case op::EConcatMarked: + // - 0 <>-> Exp = 0 + // - 1 <>-> Exp = Exp + // - [*0] <>-> Exp = 0 + // - Exp <>-> 0 = 0 + // - boolExp <>-> Exp = boolExp & Exp + if (first->is_tt()) + return second; + if (first->is_ff() + || first->is_eword()) + { + second->destroy(); + return ff(); + } + if (second->is_ff()) + { + first->destroy(); + return second; + } + if (first->is_boolean()) + return multop(op::And, {first, second}); + break; + case op::UConcat: + // - 0 []-> Exp = 1 + // - 1 []-> Exp = Exp + // - [*0] []-> Exp = 1 + // - Exp []-> 1 = 1 + // - boolExp []-> Exp = !boolExp | Exp + if (first->is_tt()) + return second; + if (first->is_ff() + || first->is_eword()) + { + second->destroy(); + return tt(); + } + if (second->is_tt()) + { + first->destroy(); + return second; + } + if (first->is_boolean()) + return multop(op::Or, {unop(op::Not, first), second}); + break; + default: + SPOT_UNREACHABLE(); + } + + auto mem = operator new(sizeof(fnode) + sizeof(*children)); + return unique(new(mem) fnode(o, {first, second})); + } + + const fnode* + fnode::ap(const std::string& name) + { + auto ires = m.name2ap.emplace(name, nullptr); + if (!ires.second) + return ires.first->second->clone(); + // Name the formula before creating it, because + // the constructor will call ap_name(). + m.ap2name.emplace(next_id_, name); + // next_id_ is incremented by setup_props(), called by the + // constructor of fnode + return ires.first->second = new fnode(op::ap, {}); + } + + const std::string& + fnode::ap_name() const + { + if (op_ != op::ap) + throw std::runtime_error("ap_name() called on non-AP formula"); + auto i = m.ap2name.find(id()); + assert(i != m.ap2name.end()); + return i->second; + } + + size_t fnode::next_id_ = 0U; + const fnode* fnode::ff_ = new fnode(op::ff, {}); + const fnode* fnode::tt_ = new fnode(op::tt, {}); + const fnode* fnode::ew_ = new fnode(op::eword, {}); + const fnode* fnode::one_star_ = nullptr; // Only built when necessary. + + void fnode::setup_props(op o) + { + op_ = o; + id_ = next_id_++; + // If the counter of formulae ever loops, we want to skip the + // first three values, because they are permanently associated + // to constants, and it is convenient to have constants + // smaller than all other formulas. + if (next_id_ == 0) + next_id_ = 3; + + switch (op_) + { + case op::ff: + case op::tt: + is_.boolean = true; + is_.sugar_free_boolean = true; + is_.in_nenoform = true; + is_.syntactic_si = true; // for LTL (not PSL) + is_.sugar_free_ltl = true; + is_.ltl_formula = true; + is_.psl_formula = true; + is_.sere_formula = true; + is_.finite = true; + is_.eventual = true; + is_.universal = true; + is_.syntactic_safety = true; + is_.syntactic_guarantee = true; + is_.syntactic_obligation = true; + is_.syntactic_recurrence = true; + is_.syntactic_persistence = true; + is_.not_marked = true; + is_.accepting_eword = false; + is_.lbt_atomic_props = true; + is_.spin_atomic_props = true; + break; + case op::eword: + is_.boolean = false; + is_.sugar_free_boolean = false; + is_.in_nenoform = true; + is_.syntactic_si = true; + is_.sugar_free_ltl = true; + is_.ltl_formula = false; + is_.psl_formula = false; + is_.sere_formula = true; + is_.finite = true; + is_.eventual = false; + is_.syntactic_safety = false; + is_.syntactic_guarantee = false; + is_.syntactic_obligation = false; + is_.syntactic_recurrence = false; + is_.syntactic_persistence = false; + is_.universal = false; + is_.not_marked = true; + is_.accepting_eword = true; + is_.lbt_atomic_props = true; + is_.spin_atomic_props = true; + break; + case op::ap: + is_.boolean = true; + is_.sugar_free_boolean = true; + is_.in_nenoform = true; + is_.syntactic_si = true; // Assuming LTL (for PSL a Boolean + // term is not stared will be regarded + // as not stuttering were this + // matters.) + is_.sugar_free_ltl = true; + is_.ltl_formula = true; + is_.psl_formula = true; + is_.sere_formula = true; + is_.finite = true; + is_.eventual = false; + is_.universal = false; + is_.syntactic_safety = true; + is_.syntactic_guarantee = true; + is_.syntactic_obligation = true; + is_.syntactic_recurrence = true; + is_.syntactic_persistence = true; + is_.not_marked = true; + is_.accepting_eword = false; + { + // is_.lbt_atomic_props should be true if the name has the + // form pNN where NN is any number of digit. + const std::string& n = ap_name(); + std::string::const_iterator pos = n.begin(); + bool lbtap = (pos != n.end() && *pos++ == 'p'); + while (lbtap && pos != n.end()) + { + char l = *pos++; + lbtap = (l >= '0' && l <= '9'); + } + is_.lbt_atomic_props = lbtap; + is_.spin_atomic_props = lbtap || is_spin_ap(n.c_str()); + } + break; + case op::Not: + props = children[0]->props; + is_.not_marked = true; + is_.eventual = children[0]->is_universal(); + is_.universal = children[0]->is_eventual(); + is_.in_nenoform = (children[0]->is(op::ap)); + is_.sere_formula = is_.boolean; + + is_.syntactic_safety = children[0]->is_syntactic_guarantee(); + is_.syntactic_guarantee = children[0]->is_syntactic_safety(); + // is_.syntactic_obligation inherited from child + is_.syntactic_recurrence = children[0]->is_syntactic_persistence(); + is_.syntactic_persistence = children[0]->is_syntactic_recurrence(); + + is_.accepting_eword = false; + break; + case op::X: + props = children[0]->props; + is_.not_marked = true; + is_.boolean = false; + is_.syntactic_si = false; + is_.sere_formula = false; + // is_.syntactic_safety inherited + // is_.syntactic_guarantee inherited + // is_.syntactic_obligation inherited + // is_.syntactic_recurrence inherited + // is_.syntactic_persistence inherited + is_.accepting_eword = false; + break; + case op::F: + props = children[0]->props; + is_.not_marked = true; + is_.boolean = false; + is_.sere_formula = false; + is_.finite = false; + is_.sugar_free_ltl = false; + is_.eventual = true; + is_.syntactic_safety = false; + // is_.syntactic_guarantee inherited + is_.syntactic_obligation = is_.syntactic_guarantee; + is_.syntactic_recurrence = is_.syntactic_guarantee; + // is_.syntactic_persistence inherited + is_.accepting_eword = false; + break; + case op::G: + props = children[0]->props; + is_.not_marked = true; + is_.boolean = false; + is_.sere_formula = false; + is_.finite = false; + is_.sugar_free_ltl = false; + is_.universal = true; + // is_.syntactic_safety inherited + is_.syntactic_guarantee = false; + is_.syntactic_obligation = is_.syntactic_safety; + // is_.syntactic_recurrence inherited + is_.syntactic_persistence = is_.syntactic_safety; + is_.accepting_eword = false; + break; + case op::NegClosure: + case op::NegClosureMarked: + props = children[0]->props; + is_.not_marked = (op_ == op::NegClosure); + is_.boolean = false; + is_.ltl_formula = false; + is_.psl_formula = true; + is_.sere_formula = false; + is_.syntactic_safety = is_.finite; + is_.syntactic_guarantee = true; + is_.syntactic_obligation = true; + is_.syntactic_recurrence = true; + is_.syntactic_persistence = true; + is_.accepting_eword = false; + assert(children[0]->is_sere_formula()); + assert(!children[0]->is_boolean()); + break; + case op::Closure: + props = children[0]->props; + is_.not_marked = true; + is_.boolean = false; + is_.ltl_formula = false; + is_.psl_formula = true; + is_.sere_formula = false; + is_.syntactic_safety = true; + is_.syntactic_guarantee = is_.finite; + is_.syntactic_obligation = true; + is_.syntactic_recurrence = true; + is_.syntactic_persistence = true; + is_.accepting_eword = false; + assert(children[0]->is_sere_formula()); + assert(!children[0]->is_boolean()); + break; + case op::Xor: + case op::Equiv: + props = children[0]->props & children[1]->props; + is_.eventual = false; + is_.universal = false; + is_.sere_formula = is_.boolean; + is_.sugar_free_boolean = false; + is_.in_nenoform = false; + // is_.syntactic_obligation inherited; + is_.accepting_eword = false; + if (is_.syntactic_obligation) + { + // Only formula that are in the intersection of + // guarantee and safety are closed by Xor and <=>. + bool sg = is_.syntactic_safety && is_.syntactic_guarantee; + is_.syntactic_safety = sg; + is_.syntactic_guarantee = sg; + assert(is_.syntactic_recurrence == true); + assert(is_.syntactic_persistence == true); + } + else + { + is_.syntactic_safety = false; + is_.syntactic_guarantee = false; + is_.syntactic_recurrence = false; + is_.syntactic_persistence = false; + } + break; + case op::Implies: + props = children[0]->props & children[1]->props; + is_.eventual = false; + is_.universal = false; + is_.sere_formula = is_.boolean; + is_.sugar_free_boolean = false; + is_.in_nenoform = false; + is_.syntactic_safety = (children[0]->is_syntactic_guarantee() + && children[1]->is_syntactic_safety()); + is_.syntactic_guarantee = (children[0]->is_syntactic_safety() + && children[1]->is_syntactic_guarantee()); + // is_.syntactic_obligation inherited + is_.syntactic_persistence = children[0]->is_syntactic_recurrence() + && children[1]->is_syntactic_persistence(); + is_.syntactic_recurrence = children[0]->is_syntactic_persistence() + && children[1]->is_syntactic_recurrence(); + is_.accepting_eword = false; + break; + case op::EConcatMarked: + case op::EConcat: + props = children[0]->props & children[1]->props; + is_.not_marked = (op_ != op::EConcatMarked); + is_.ltl_formula = false; + is_.boolean = false; + is_.sere_formula = false; + is_.accepting_eword = false; + is_.psl_formula = true; + + is_.syntactic_guarantee = children[1]->is_syntactic_guarantee(); + is_.syntactic_persistence = children[1]->is_syntactic_persistence(); + if (children[0]->is_finite()) + { + is_.syntactic_safety = children[1]->is_syntactic_safety(); + is_.syntactic_obligation = children[1]->is_syntactic_obligation(); + is_.syntactic_recurrence = children[1]->is_syntactic_recurrence(); + } + else + { + is_.syntactic_safety = false; + is_.syntactic_obligation = children[1]->is_syntactic_guarantee(); + is_.syntactic_recurrence = children[1]->is_syntactic_guarantee(); + } + assert(children[0]->is_sere_formula()); + assert(children[1]->is_psl_formula()); + if (children[0]->is_boolean()) + is_.syntactic_si = false; + break; + case op::UConcat: + props = children[0]->props & children[1]->props; + is_.not_marked = true; + is_.ltl_formula = false; + is_.boolean = false; + is_.sere_formula = false; + is_.accepting_eword = false; + is_.psl_formula = true; + + is_.syntactic_safety = children[1]->is_syntactic_safety(); + is_.syntactic_recurrence = children[1]->is_syntactic_recurrence(); + if (children[0]->is_finite()) + { + is_.syntactic_guarantee = children[1]->is_syntactic_guarantee(); + is_.syntactic_obligation = children[1]->is_syntactic_obligation(); + is_.syntactic_persistence = + children[1]->is_syntactic_persistence(); + } + else + { + is_.syntactic_guarantee = false; + is_.syntactic_obligation = children[1]->is_syntactic_safety(); + is_.syntactic_persistence = children[1]->is_syntactic_safety(); + } + assert(children[0]->is_sere_formula()); + assert(children[1]->is_psl_formula()); + if (children[0]->is_boolean()) + is_.syntactic_si = false; + break; + case op::U: + // Beware: (f U g) is a pure eventuality if both operands + // are pure eventualities, unlike in the proceedings of + // Concur'00. (The revision of the paper available at + // http://www.bell-labs.com/project/TMP/ is fixed.) See + // also http://arxiv.org/abs/1011.4214v2 for a discussion + // about this problem. (Which we fixed in 2005 thanks + // to LBTT.) + // This means that we can use the following line to handle + // all cases of (f U g), (f R g), (f W g), (f M g) for + // universality and eventuality. + props = children[0]->props & children[1]->props; + // The matter can be further refined because: + // (f U g) is a pure eventuality if + // g is a pure eventuality (regardless of f), + // or f == 1 + // (g M f) is a pure eventuality if f and g are, + // or f == 1 + // (g R f) is purely universal if + // f is purely universal (regardless of g) + // or g == 0 + // (f W g) is purely universal if f and g are + // or g == 0 + is_.not_marked = true; + // f U g is universal if g is eventual, or if f == 1. + is_.eventual = children[1]->is_eventual(); + is_.eventual |= children[0]->is_tt(); + is_.boolean = false; + is_.sere_formula = false; + is_.finite = false; + is_.accepting_eword = false; + + is_.syntactic_safety = false; + // is_.syntactic_guarantee = Guarantee U Guarantee + is_.syntactic_obligation = // Obligation U Guarantee + children[0]->is_syntactic_obligation() + && children[1]->is_syntactic_guarantee(); + is_.syntactic_recurrence = // Recurrence U Guarantee + children[0]->is_syntactic_recurrence() + && children[1]->is_syntactic_guarantee(); + // is_.syntactic_persistence = Persistence U Persistance + break; + case op::W: + // See comment for op::U. + props = children[0]->props & children[1]->props; + is_.not_marked = true; + // f W g is universal if f and g are, or if g == 0. + is_.universal |= children[1]->is_ff(); + is_.boolean = false; + is_.sere_formula = false; + is_.finite = false; + is_.accepting_eword = false; + + // is_.syntactic_safety = Safety W Safety; + is_.syntactic_guarantee = false; + is_.syntactic_obligation = // Safety W Obligation + children[0]->is_syntactic_safety() + && children[1]->is_syntactic_obligation(); + // is_.syntactic_recurrence = Recurrence W Recurrence + is_.syntactic_persistence = // Safety W Persistance + children[0]->is_syntactic_safety() + && children[1]->is_syntactic_persistence(); + + break; + case op::R: + // See comment for op::U. + props = children[0]->props & children[1]->props; + is_.not_marked = true; + // g R f is universal if f is universal, or if g == 0. + is_.universal = children[1]->is_universal(); + is_.universal |= children[0]->is_ff(); + is_.boolean = false; + is_.sere_formula = false; + is_.finite = false; + is_.accepting_eword = false; + + // is_.syntactic_safety = Safety R Safety; + is_.syntactic_guarantee = false; + is_.syntactic_obligation = // Obligation R Safety + children[0]->is_syntactic_obligation() + && children[1]->is_syntactic_safety(); + //is_.syntactic_recurrence = Recurrence R Recurrence + is_.syntactic_persistence = // Persistence R Safety + children[0]->is_syntactic_persistence() + && children[1]->is_syntactic_safety(); + + break; + case op::M: + // See comment for op::U. + props = children[0]->props & children[1]->props; + is_.not_marked = true; + // g M f is eventual if both g and f are eventual, or if f == 1. + is_.eventual |= children[1]->is_tt(); + is_.boolean = false; + is_.sere_formula = false; + is_.finite = false; + is_.accepting_eword = false; + + is_.syntactic_safety = false; + // is_.syntactic_guarantee = Guarantee M Guarantee + is_.syntactic_obligation = // Guarantee M Obligation + children[0]->is_syntactic_guarantee() + && children[1]->is_syntactic_obligation(); + is_.syntactic_recurrence = // Guarantee M Recurrence + children[0]->is_syntactic_guarantee() + && children[1]->is_syntactic_recurrence(); + // is_.syntactic_persistence = Persistence M Persistance + + break; + case op::Or: + { + props = children[0]->props; + unsigned s = size_; + bool ew = children[0]->accepts_eword(); + for (unsigned i = 1; i < s; ++i) + { + ew |= children[i]->accepts_eword(); + props &= children[i]->props; + } + is_.accepting_eword = ew; break; - case op::eword: + } + case op::OrRat: + { + props = children[0]->props; + unsigned s = size_; + bool syntactic_si = is_.syntactic_si && !is_.boolean; + // Note: OrRat(p1,p2) is a Boolean formula, but its is + // actually rewritten as Or(p1,p2) by trivial identities + // before this constructor is called. So at this point, + // AndNLM is always used with at most one Boolean argument, + // and the result is therefore NOT Boolean. is_.boolean = false; - is_.sugar_free_boolean = false; - is_.in_nenoform = true; - is_.syntactic_si = true; - is_.sugar_free_ltl = true; is_.ltl_formula = false; is_.psl_formula = false; - is_.sere_formula = true; - is_.finite = true; is_.eventual = false; + is_.universal = false; + + bool ew = children[0]->accepts_eword(); + for (unsigned i = 1; i < s; ++i) + { + ew |= children[i]->accepts_eword(); + syntactic_si &= children[i]->is_syntactic_stutter_invariant() + && !children[i]->is_boolean(); + props &= children[i]->props; + } + is_.accepting_eword = ew; + is_.syntactic_si = syntactic_si; + break; + } + case op::And: + { + props = children[0]->props; + unsigned s = size_; + for (unsigned i = 1; i < s; ++i) + props &= children[i]->props; + break; + } + case op::Fusion: + case op::Concat: + case op::AndNLM: + case op::AndRat: + { + props = children[0]->props; + unsigned s = size_; + bool syntactic_si = is_.syntactic_si && !is_.boolean; + // Note: AndNLM(p1,p2) and AndRat(p1,p2) are Boolean + // formulae, but they are actually rewritten as And(p1,p2) + // by trivial identities before this constructor is called. + // So at this point, AndNLM/AndRat are always used with at + // most one Boolean argument, and the result is therefore + // NOT Boolean. + is_.boolean = false; + is_.ltl_formula = false; + is_.psl_formula = false; + is_.eventual = false; + is_.universal = false; + + for (unsigned i = 1; i < s; ++i) + { + syntactic_si &= children[i]->is_syntactic_stutter_invariant() + && !children[i]->is_boolean(); + props &= children[i]->props; + } + is_.syntactic_si = syntactic_si; + if (op_ == op::Fusion) + is_.accepting_eword = false; + // A concatenation is an siSERE if it contains one stared + // Boolean, and the other operands are siSERE (i.e., + // sub-formulas that verify is_syntactic_stutter_invariant() and + // !is_boolean()); + if (op_ == op::Concat) + { + unsigned sb = 0; // stared Boolean formulas seen + for (unsigned i = 0; i < s; ++i) + { + auto ci = children[i]; + if (ci->is_syntactic_stutter_invariant() + && !ci->is_boolean()) + continue; + if (ci->is(op::Star)) + { + sb += ci->nth(0)->is_boolean(); + if (sb > 1) + break; + } + else + { + sb = 0; + break; + } + } + is_.syntactic_si = sb == 1; + } + break; + } + + case op::Star: + case op::FStar: + { + props = children[0]->props; + assert(is_.sere_formula); + is_.boolean = false; + is_.ltl_formula = false; + is_.psl_formula = false; + is_.eventual = false; + is_.universal = false; is_.syntactic_safety = false; is_.syntactic_guarantee = false; is_.syntactic_obligation = false; is_.syntactic_recurrence = false; is_.syntactic_persistence = false; - is_.universal = false; - is_.not_marked = true; - is_.accepting_eword = true; - is_.lbt_atomic_props = true; - is_.spin_atomic_props = true; - break; - case op::ap: - is_.boolean = true; - is_.sugar_free_boolean = true; - is_.in_nenoform = true; - is_.syntactic_si = true; // Assuming LTL (for PSL a Boolean - // term is not stared will be regarded - // as not stuttering were this - // matters.) - is_.sugar_free_ltl = true; - is_.ltl_formula = true; - is_.psl_formula = true; - is_.sere_formula = true; - is_.finite = true; - is_.eventual = false; - is_.universal = false; - is_.syntactic_safety = true; - is_.syntactic_guarantee = true; - is_.syntactic_obligation = true; - is_.syntactic_recurrence = true; - is_.syntactic_persistence = true; - is_.not_marked = true; - is_.accepting_eword = false; - { - // is_.lbt_atomic_props should be true if the name has the - // form pNN where NN is any number of digit. - const std::string& n = ap_name(); - std::string::const_iterator pos = n.begin(); - bool lbtap = (pos != n.end() && *pos++ == 'p'); - while (lbtap && pos != n.end()) - { - char l = *pos++; - lbtap = (l >= '0' && l <= '9'); - } - is_.lbt_atomic_props = lbtap; - is_.spin_atomic_props = lbtap || is_spin_ap(n.c_str()); - } - break; - case op::Not: - props = children[0]->props; - is_.not_marked = true; - is_.eventual = children[0]->is_universal(); - is_.universal = children[0]->is_eventual(); - is_.in_nenoform = (children[0]->is(op::ap)); - is_.sere_formula = is_.boolean; - is_.syntactic_safety = children[0]->is_syntactic_guarantee(); - is_.syntactic_guarantee = children[0]->is_syntactic_safety(); - // is_.syntactic_obligation inherited from child - is_.syntactic_recurrence = children[0]->is_syntactic_persistence(); - is_.syntactic_persistence = children[0]->is_syntactic_recurrence(); - - is_.accepting_eword = false; - break; - case op::X: - props = children[0]->props; - is_.not_marked = true; - is_.boolean = false; - is_.syntactic_si = false; - is_.sere_formula = false; - // is_.syntactic_safety inherited - // is_.syntactic_guarantee inherited - // is_.syntactic_obligation inherited - // is_.syntactic_recurrence inherited - // is_.syntactic_persistence inherited - is_.accepting_eword = false; - break; - case op::F: - props = children[0]->props; - is_.not_marked = true; - is_.boolean = false; - is_.sere_formula = false; - is_.finite = false; - is_.sugar_free_ltl = false; - is_.eventual = true; - is_.syntactic_safety = false; - // is_.syntactic_guarantee inherited - is_.syntactic_obligation = is_.syntactic_guarantee; - is_.syntactic_recurrence = is_.syntactic_guarantee; - // is_.syntactic_persistence inherited - is_.accepting_eword = false; - break; - case op::G: - props = children[0]->props; - is_.not_marked = true; - is_.boolean = false; - is_.sere_formula = false; - is_.finite = false; - is_.sugar_free_ltl = false; - is_.universal = true; - // is_.syntactic_safety inherited - is_.syntactic_guarantee = false; - is_.syntactic_obligation = is_.syntactic_safety; - // is_.syntactic_recurrence inherited - is_.syntactic_persistence = is_.syntactic_safety; - is_.accepting_eword = false; - break; - case op::NegClosure: - case op::NegClosureMarked: - props = children[0]->props; - is_.not_marked = (op_ == op::NegClosure); - is_.boolean = false; - is_.ltl_formula = false; - is_.psl_formula = true; - is_.sere_formula = false; - is_.syntactic_safety = is_.finite; - is_.syntactic_guarantee = true; - is_.syntactic_obligation = true; - is_.syntactic_recurrence = true; - is_.syntactic_persistence = true; - is_.accepting_eword = false; - assert(children[0]->is_sere_formula()); - assert(!children[0]->is_boolean()); - break; - case op::Closure: - props = children[0]->props; - is_.not_marked = true; - is_.boolean = false; - is_.ltl_formula = false; - is_.psl_formula = true; - is_.sere_formula = false; - is_.syntactic_safety = true; - is_.syntactic_guarantee = is_.finite; - is_.syntactic_obligation = true; - is_.syntactic_recurrence = true; - is_.syntactic_persistence = true; - is_.accepting_eword = false; - assert(children[0]->is_sere_formula()); - assert(!children[0]->is_boolean()); - break; - case op::Xor: - case op::Equiv: - props = children[0]->props & children[1]->props; - is_.eventual = false; - is_.universal = false; - is_.sere_formula = is_.boolean; - is_.sugar_free_boolean = false; - is_.in_nenoform = false; - // is_.syntactic_obligation inherited; - is_.accepting_eword = false; - if (is_.syntactic_obligation) + switch (op_) { - // Only formula that are in the intersection of - // guarantee and safety are closed by Xor and <=>. - bool sg = is_.syntactic_safety && is_.syntactic_guarantee; - is_.syntactic_safety = sg; - is_.syntactic_guarantee = sg; - assert(is_.syntactic_recurrence == true); - assert(is_.syntactic_persistence == true); - } - else - { - is_.syntactic_safety = false; - is_.syntactic_guarantee = false; - is_.syntactic_recurrence = false; - is_.syntactic_persistence = false; - } - break; - case op::Implies: - props = children[0]->props & children[1]->props; - is_.eventual = false; - is_.universal = false; - is_.sere_formula = is_.boolean; - is_.sugar_free_boolean = false; - is_.in_nenoform = false; - is_.syntactic_safety = (children[0]->is_syntactic_guarantee() - && children[1]->is_syntactic_safety()); - is_.syntactic_guarantee = (children[0]->is_syntactic_safety() - && children[1]->is_syntactic_guarantee()); - // is_.syntactic_obligation inherited - is_.syntactic_persistence = children[0]->is_syntactic_recurrence() - && children[1]->is_syntactic_persistence(); - is_.syntactic_recurrence = children[0]->is_syntactic_persistence() - && children[1]->is_syntactic_recurrence(); - is_.accepting_eword = false; - break; - case op::EConcatMarked: - case op::EConcat: - props = children[0]->props & children[1]->props; - is_.not_marked = (op_ != op::EConcatMarked); - is_.ltl_formula = false; - is_.boolean = false; - is_.sere_formula = false; - is_.accepting_eword = false; - is_.psl_formula = true; - - is_.syntactic_guarantee = children[1]->is_syntactic_guarantee(); - is_.syntactic_persistence = children[1]->is_syntactic_persistence(); - if (children[0]->is_finite()) - { - is_.syntactic_safety = children[1]->is_syntactic_safety(); - is_.syntactic_obligation = children[1]->is_syntactic_obligation(); - is_.syntactic_recurrence = children[1]->is_syntactic_recurrence(); - } - else - { - is_.syntactic_safety = false; - is_.syntactic_obligation = children[1]->is_syntactic_guarantee(); - is_.syntactic_recurrence = children[1]->is_syntactic_guarantee(); - } - assert(children[0]->is_sere_formula()); - assert(children[1]->is_psl_formula()); - if (children[0]->is_boolean()) - is_.syntactic_si = false; - break; - case op::UConcat: - props = children[0]->props & children[1]->props; - is_.not_marked = true; - is_.ltl_formula = false; - is_.boolean = false; - is_.sere_formula = false; - is_.accepting_eword = false; - is_.psl_formula = true; - - is_.syntactic_safety = children[1]->is_syntactic_safety(); - is_.syntactic_recurrence = children[1]->is_syntactic_recurrence(); - if (children[0]->is_finite()) - { - is_.syntactic_guarantee = children[1]->is_syntactic_guarantee(); - is_.syntactic_obligation = children[1]->is_syntactic_obligation(); - is_.syntactic_persistence = - children[1]->is_syntactic_persistence(); - } - else - { - is_.syntactic_guarantee = false; - is_.syntactic_obligation = children[1]->is_syntactic_safety(); - is_.syntactic_persistence = children[1]->is_syntactic_safety(); - } - assert(children[0]->is_sere_formula()); - assert(children[1]->is_psl_formula()); - if (children[0]->is_boolean()) - is_.syntactic_si = false; - break; - case op::U: - // Beware: (f U g) is a pure eventuality if both operands - // are pure eventualities, unlike in the proceedings of - // Concur'00. (The revision of the paper available at - // http://www.bell-labs.com/project/TMP/ is fixed.) See - // also http://arxiv.org/abs/1011.4214v2 for a discussion - // about this problem. (Which we fixed in 2005 thanks - // to LBTT.) - // This means that we can use the following line to handle - // all cases of (f U g), (f R g), (f W g), (f M g) for - // universality and eventuality. - props = children[0]->props & children[1]->props; - // The matter can be further refined because: - // (f U g) is a pure eventuality if - // g is a pure eventuality (regardless of f), - // or f == 1 - // (g M f) is a pure eventuality if f and g are, - // or f == 1 - // (g R f) is purely universal if - // f is purely universal (regardless of g) - // or g == 0 - // (f W g) is purely universal if f and g are - // or g == 0 - is_.not_marked = true; - // f U g is universal if g is eventual, or if f == 1. - is_.eventual = children[1]->is_eventual(); - is_.eventual |= children[0]->is_tt(); - is_.boolean = false; - is_.sere_formula = false; - is_.finite = false; - is_.accepting_eword = false; - - is_.syntactic_safety = false; - // is_.syntactic_guarantee = Guarantee U Guarantee - is_.syntactic_obligation = // Obligation U Guarantee - children[0]->is_syntactic_obligation() - && children[1]->is_syntactic_guarantee(); - is_.syntactic_recurrence = // Recurrence U Guarantee - children[0]->is_syntactic_recurrence() - && children[1]->is_syntactic_guarantee(); - // is_.syntactic_persistence = Persistence U Persistance - break; - case op::W: - // See comment for op::U. - props = children[0]->props & children[1]->props; - is_.not_marked = true; - // f W g is universal if f and g are, or if g == 0. - is_.universal |= children[1]->is_ff(); - is_.boolean = false; - is_.sere_formula = false; - is_.finite = false; - is_.accepting_eword = false; - - // is_.syntactic_safety = Safety W Safety; - is_.syntactic_guarantee = false; - is_.syntactic_obligation = // Safety W Obligation - children[0]->is_syntactic_safety() - && children[1]->is_syntactic_obligation(); - // is_.syntactic_recurrence = Recurrence W Recurrence - is_.syntactic_persistence = // Safety W Persistance - children[0]->is_syntactic_safety() - && children[1]->is_syntactic_persistence(); - - break; - case op::R: - // See comment for op::U. - props = children[0]->props & children[1]->props; - is_.not_marked = true; - // g R f is universal if f is universal, or if g == 0. - is_.universal = children[1]->is_universal(); - is_.universal |= children[0]->is_ff(); - is_.boolean = false; - is_.sere_formula = false; - is_.finite = false; - is_.accepting_eword = false; - - // is_.syntactic_safety = Safety R Safety; - is_.syntactic_guarantee = false; - is_.syntactic_obligation = // Obligation R Safety - children[0]->is_syntactic_obligation() - && children[1]->is_syntactic_safety(); - //is_.syntactic_recurrence = Recurrence R Recurrence - is_.syntactic_persistence = // Persistence R Safety - children[0]->is_syntactic_persistence() - && children[1]->is_syntactic_safety(); - - break; - case op::M: - // See comment for op::U. - props = children[0]->props & children[1]->props; - is_.not_marked = true; - // g M f is eventual if both g and f are eventual, or if f == 1. - is_.eventual |= children[1]->is_tt(); - is_.boolean = false; - is_.sere_formula = false; - is_.finite = false; - is_.accepting_eword = false; - - is_.syntactic_safety = false; - // is_.syntactic_guarantee = Guarantee M Guarantee - is_.syntactic_obligation = // Guarantee M Obligation - children[0]->is_syntactic_guarantee() - && children[1]->is_syntactic_obligation(); - is_.syntactic_recurrence = // Guarantee M Recurrence - children[0]->is_syntactic_guarantee() - && children[1]->is_syntactic_recurrence(); - // is_.syntactic_persistence = Persistence M Persistance - - break; - case op::Or: - { - props = children[0]->props; - unsigned s = size_; - bool ew = children[0]->accepts_eword(); - for (unsigned i = 1; i < s; ++i) - { - ew |= children[i]->accepts_eword(); - props &= children[i]->props; - } - is_.accepting_eword = ew; - break; - } - case op::OrRat: - { - props = children[0]->props; - unsigned s = size_; - bool syntactic_si = is_.syntactic_si && !is_.boolean; - // Note: OrRat(p1,p2) is a Boolean formula, but its is - // actually rewritten as Or(p1,p2) by trivial identities - // before this constructor is called. So at this point, - // AndNLM is always used with at most one Boolean argument, - // and the result is therefore NOT Boolean. - is_.boolean = false; - is_.ltl_formula = false; - is_.psl_formula = false; - is_.eventual = false; - is_.universal = false; - - bool ew = children[0]->accepts_eword(); - for (unsigned i = 1; i < s; ++i) - { - ew |= children[i]->accepts_eword(); - syntactic_si &= children[i]->is_syntactic_stutter_invariant() - && !children[i]->is_boolean(); - props &= children[i]->props; - } - is_.accepting_eword = ew; - is_.syntactic_si = syntactic_si; - break; - } - case op::And: - { - props = children[0]->props; - unsigned s = size_; - for (unsigned i = 1; i < s; ++i) - props &= children[i]->props; - break; - } - case op::Fusion: - case op::Concat: - case op::AndNLM: - case op::AndRat: - { - props = children[0]->props; - unsigned s = size_; - bool syntactic_si = is_.syntactic_si && !is_.boolean; - // Note: AndNLM(p1,p2) and AndRat(p1,p2) are Boolean - // formulae, but they are actually rewritten as And(p1,p2) - // by trivial identities before this constructor is called. - // So at this point, AndNLM/AndRat are always used with at - // most one Boolean argument, and the result is therefore - // NOT Boolean. - is_.boolean = false; - is_.ltl_formula = false; - is_.psl_formula = false; - is_.eventual = false; - is_.universal = false; - - for (unsigned i = 1; i < s; ++i) - { - syntactic_si &= children[i]->is_syntactic_stutter_invariant() - && !children[i]->is_boolean(); - props &= children[i]->props; - } - is_.syntactic_si = syntactic_si; - if (op_ == op::Fusion) - is_.accepting_eword = false; - // A concatenation is an siSERE if it contains one stared - // Boolean, and the other operands are siSERE (i.e., - // sub-formulas that verify is_syntactic_stutter_invariant() and - // !is_boolean()); - if (op_ == op::Concat) - { - unsigned sb = 0; // stared Boolean formulas seen - for (unsigned i = 0; i < s; ++i) - { - auto ci = children[i]; - if (ci->is_syntactic_stutter_invariant() - && !ci->is_boolean()) - continue; - if (ci->is(op::Star)) - { - sb += ci->nth(0)->is_boolean(); - if (sb > 1) - break; - } - else - { - sb = 0; - break; - } - } - is_.syntactic_si = sb == 1; - } - break; - } - - case op::Star: - case op::FStar: - { - props = children[0]->props; - assert(is_.sere_formula); - is_.boolean = false; - is_.ltl_formula = false; - is_.psl_formula = false; - is_.eventual = false; - is_.universal = false; - is_.syntactic_safety = false; - is_.syntactic_guarantee = false; - is_.syntactic_obligation = false; - is_.syntactic_recurrence = false; - is_.syntactic_persistence = false; - - switch (op_) - { - case op::Star: - if (max_ == unbounded()) - { - is_.finite = false; - is_.syntactic_si = min_ == 1 && children[0]->is_boolean(); - } - else - { - is_.syntactic_si = false; - } - if (min_ == 0) - is_.accepting_eword = true; - break; - case op::FStar: - is_.accepting_eword = false; - is_.syntactic_si &= !children[0]->is_boolean(); - if (max_ == unbounded()) + case op::Star: + if (max_ == unbounded()) + { is_.finite = false; - if (min_ == 0) + is_.syntactic_si = min_ == 1 && children[0]->is_boolean(); + } + else + { is_.syntactic_si = false; - break; - default: - SPOT_UNREACHABLE(); - } - } - break; - } - } - - - std::ostream& fnode::dump(std::ostream& os) const - { - os << kindstr() << "(@" << id_ << " #" << refs_; - if (op_ == op::Star || op_ == op::FStar) - { - os << ' ' << +min() << ".."; - auto m = max(); - if (m != unbounded()) - os << +m; - } - if (op_ == op::ap) - os << " \"" << ap_name() << '"'; - if (auto s = size()) - { - os << " ["; - for (auto c: *this) - { - c->dump(os); - if (--s) - os << ", "; + } + if (min_ == 0) + is_.accepting_eword = true; + break; + case op::FStar: + is_.accepting_eword = false; + is_.syntactic_si &= !children[0]->is_boolean(); + if (max_ == unbounded()) + is_.finite = false; + if (min_ == 0) + is_.syntactic_si = false; + break; + default: + SPOT_UNREACHABLE(); } - os << ']'; } - return os << ')'; - } + break; + } + } - const fnode* fnode::all_but(unsigned i) const - { - switch (op o = kind()) + + std::ostream& fnode::dump(std::ostream& os) const + { + os << kindstr() << "(@" << id_ << " #" << refs_; + if (op_ == op::Star || op_ == op::FStar) + { + os << ' ' << +min() << ".."; + auto m = max(); + if (m != unbounded()) + os << +m; + } + if (op_ == op::ap) + os << " \"" << ap_name() << '"'; + if (auto s = size()) + { + os << " ["; + for (auto c: *this) + { + c->dump(os); + if (--s) + os << ", "; + } + os << ']'; + } + return os << ')'; + } + + const fnode* fnode::all_but(unsigned i) const + { + switch (op o = kind()) + { + case op::Or: + case op::OrRat: + case op::And: + case op::AndRat: + case op::AndNLM: + case op::Concat: + case op::Fusion: { - case op::Or: - case op::OrRat: - case op::And: - case op::AndRat: - case op::AndNLM: - case op::Concat: - case op::Fusion: - { - unsigned s = size(); - assert(s > 1); - vec v; - v.reserve(s - 1); - for (unsigned j = 0; j < s; ++j) - if (i != j) - v.push_back(nth(j)->clone()); - return multop(o, v); - } - default: - throw - std::runtime_error("all_but() is incompatible with this operator"); + unsigned s = size(); + assert(s > 1); + vec v; + v.reserve(s - 1); + for (unsigned j = 0; j < s; ++j) + if (i != j) + v.push_back(nth(j)->clone()); + return multop(o, v); } - SPOT_UNREACHABLE(); - } - - const fnode* fnode::boolean_operands(unsigned* width) const - { - unsigned s = boolean_count(); - if (width) - *width = s; - if (s == 0) - return nullptr; - if (s == 1) - return nth(0)->clone(); - vec v(children, children + s); - for (auto c: v) - c->clone(); - return multop(op_, v); - } - - bool fnode::instances_check() - { - unsigned cnt = 0; - for (auto i: m.uniq) - if (i->id() > 3 && i != one_star_) - { - if (!cnt++) - std::cerr << "*** m.uniq is not empty ***\n"; - i->dump(std::cerr) << std::endl; - } - return cnt == 0; - } - - formula formula::sugar_goto(const formula& b, uint8_t min, uint8_t max) - { - if (!b.is_boolean()) + default: throw - std::runtime_error("sugar_goto() called with non-Boolean argument"); - // b[->min..max] is implemented as ((!b)[*];b)[*min..max] - return Star(Concat({Star(Not(b)), b}), min, max); - } + std::runtime_error("all_but() is incompatible with this operator"); + } + SPOT_UNREACHABLE(); + } - formula formula::sugar_equal(const formula& b, uint8_t min, uint8_t max) - { - if (!b.is_boolean()) - throw - std::runtime_error("sugar_equal() called with non-Boolean argument"); + const fnode* fnode::boolean_operands(unsigned* width) const + { + unsigned s = boolean_count(); + if (width) + *width = s; + if (s == 0) + return nullptr; + if (s == 1) + return nth(0)->clone(); + vec v(children, children + s); + for (auto c: v) + c->clone(); + return multop(op_, v); + } - // b[=0..] = 1[*] - if (min == 0 && max == unbounded()) - return one_star(); + bool fnode::instances_check() + { + unsigned cnt = 0; + for (auto i: m.uniq) + if (i->id() > 3 && i != one_star_) + { + if (!cnt++) + std::cerr << "*** m.uniq is not empty ***\n"; + i->dump(std::cerr) << std::endl; + } + return cnt == 0; + } - // b[=min..max] is implemented as ((!b)[*];b)[*min..max];(!b)[*] - formula s = Star(Not(b)); - return Concat({Star(Concat({s, b}), min, max), s}); - } + formula formula::sugar_goto(const formula& b, uint8_t min, uint8_t max) + { + if (!b.is_boolean()) + throw + std::runtime_error("sugar_goto() called with non-Boolean argument"); + // b[->min..max] is implemented as ((!b)[*];b)[*min..max] + return Star(Concat({Star(Not(b)), b}), min, max); + } - int atomic_prop_cmp(const fnode* f, const fnode* g) - { - return strverscmp(f->ap_name().c_str(), g->ap_name().c_str()); - } + formula formula::sugar_equal(const formula& b, uint8_t min, uint8_t max) + { + if (!b.is_boolean()) + throw + std::runtime_error("sugar_equal() called with non-Boolean argument"); + + // b[=0..] = 1[*] + if (min == 0 && max == unbounded()) + return one_star(); + + // b[=min..max] is implemented as ((!b)[*];b)[*min..max];(!b)[*] + formula s = Star(Not(b)); + return Concat({Star(Concat({s, b}), min, max), s}); + } + + int atomic_prop_cmp(const fnode* f, const fnode* g) + { + return strverscmp(f->ap_name().c_str(), g->ap_name().c_str()); + } #define printprops \ - proprint(is_boolean, "B", "Boolean formula"); \ - proprint(is_sugar_free_boolean, "&", "without Boolean sugar"); \ - proprint(is_in_nenoform, "!", "in negative normal form"); \ - proprint(is_syntactic_stutter_invariant, "x", \ - "syntactic stutter invariant"); \ - proprint(is_sugar_free_ltl, "f", "without LTL sugar"); \ - proprint(is_ltl_formula, "L", "LTL formula"); \ - proprint(is_psl_formula, "P", "PSL formula"); \ - proprint(is_sere_formula, "S", "SERE formula"); \ - proprint(is_finite, "F", "finite"); \ - proprint(is_eventual, "e", "pure eventuality"); \ - proprint(is_universal, "u", "purely universal"); \ - proprint(is_syntactic_safety, "s", "syntactic safety"); \ - proprint(is_syntactic_guarantee, "g", "syntactic guarantee"); \ - proprint(is_syntactic_obligation, "o", "syntactic obligation"); \ - proprint(is_syntactic_persistence, "p", "syntactic persistence"); \ - proprint(is_syntactic_recurrence, "r", "syntactic recurrence"); \ - proprint(is_marked, "+", "marked"); \ - proprint(accepts_eword, "0", "accepts the empty word"); \ - proprint(has_lbt_atomic_props, "l", \ - "has LBT-style atomic props"); \ - proprint(has_spin_atomic_props, "a", \ - "has Spin-style atomic props"); + proprint(is_boolean, "B", "Boolean formula"); \ + proprint(is_sugar_free_boolean, "&", "without Boolean sugar"); \ + proprint(is_in_nenoform, "!", "in negative normal form"); \ + proprint(is_syntactic_stutter_invariant, "x", \ + "syntactic stutter invariant"); \ + proprint(is_sugar_free_ltl, "f", "without LTL sugar"); \ + proprint(is_ltl_formula, "L", "LTL formula"); \ + proprint(is_psl_formula, "P", "PSL formula"); \ + proprint(is_sere_formula, "S", "SERE formula"); \ + proprint(is_finite, "F", "finite"); \ + proprint(is_eventual, "e", "pure eventuality"); \ + proprint(is_universal, "u", "purely universal"); \ + proprint(is_syntactic_safety, "s", "syntactic safety"); \ + proprint(is_syntactic_guarantee, "g", "syntactic guarantee"); \ + proprint(is_syntactic_obligation, "o", "syntactic obligation"); \ + proprint(is_syntactic_persistence, "p", "syntactic persistence"); \ + proprint(is_syntactic_recurrence, "r", "syntactic recurrence"); \ + proprint(is_marked, "+", "marked"); \ + proprint(accepts_eword, "0", "accepts the empty word"); \ + proprint(has_lbt_atomic_props, "l", \ + "has LBT-style atomic props"); \ + proprint(has_spin_atomic_props, "a", \ + "has Spin-style atomic props"); - std::list - list_formula_props(const formula& f) - { - std::list res; + std::list + list_formula_props(const formula& f) + { + std::list res; #define proprint(m, a, l) \ - if (f.m()) \ - res.emplace_back(l); - printprops; + if (f.m()) \ + res.emplace_back(l); + printprops; #undef proprint - return res; - } + return res; + } - std::ostream& - print_formula_props(std::ostream& out, const formula& f, bool abbr) - { - const char* comma = abbr ? "" : ", "; - const char* sep = ""; + std::ostream& + print_formula_props(std::ostream& out, const formula& f, bool abbr) + { + const char* comma = abbr ? "" : ", "; + const char* sep = ""; #define proprint(m, a, l) \ - if (f.m()) \ - { \ - out << sep; out << (abbr ? a : l); \ - sep = comma; \ - } - printprops; + if (f.m()) \ + { \ + out << sep; out << (abbr ? a : l); \ + sep = comma; \ + } + printprops; #undef proprint - return out; - } + return out; } } diff --git a/src/tl/formula.hh b/src/tl/formula.hh index 5a94fc0e9..f50b8c019 100644 --- a/src/tl/formula.hh +++ b/src/tl/formula.hh @@ -17,7 +17,7 @@ // You should have received a copy of the GNU General Public License // along with this program. If not, see . -/// \file tlformula.hh +/// \file tl/formula.hh /// \brief LTL/PSL formula interface #pragma once @@ -37,440 +37,438 @@ namespace spot { - namespace ltl + enum class op: uint8_t { - enum class op: uint8_t - { - ff, - tt, - eword, - ap, - // unary operators - Not, - X, - F, - G, - Closure, - NegClosure, - NegClosureMarked, - // binary operators - Xor, - Implies, - Equiv, - U, ///< until - R, ///< release (dual of until) - W, ///< weak until - M, ///< strong release (dual of weak until) - EConcat, ///< Seq - EConcatMarked, ///< Seq, Marked - UConcat, ///< Triggers - // n-ary operators - Or, ///< (omega-Rational) Or - OrRat, ///< Rational Or - And, ///< (omega-Rational) And - AndRat, ///< Rational And - AndNLM, ///< Non-Length-Matching Rational-And - Concat, - Fusion, - // star-like operators - Star, ///< Star - FStar, ///< Fustion Star - }; + ff, + tt, + eword, + ap, + // unary operators + Not, + X, + F, + G, + Closure, + NegClosure, + NegClosureMarked, + // binary operators + Xor, + Implies, + Equiv, + U, ///< until + R, ///< release (dual of until) + W, ///< weak until + M, ///< strong release (dual of weak until) + EConcat, ///< Seq + EConcatMarked, ///< Seq, Marked + UConcat, ///< Triggers + // n-ary operators + Or, ///< (omega-Rational) Or + OrRat, ///< Rational Or + And, ///< (omega-Rational) And + AndRat, ///< Rational And + AndNLM, ///< Non-Length-Matching Rational-And + Concat, + Fusion, + // star-like operators + Star, ///< Star + FStar, ///< Fustion Star + }; #ifndef SWIG - class SPOT_API fnode final + class SPOT_API fnode final + { + public: + const fnode* clone() const { - public: - const fnode* clone() const - { - ++refs_; - return this; - } + ++refs_; + return this; + } - void destroy() const - { - // last reference to a node that is not a constant? - if (SPOT_UNLIKELY(!refs_ && id_ > 2)) - destroy_aux(); - else - --refs_; - } + void destroy() const + { + // last reference to a node that is not a constant? + if (SPOT_UNLIKELY(!refs_ && id_ > 2)) + destroy_aux(); + else + --refs_; + } - static constexpr uint8_t unbounded() - { - return UINT8_MAX; - } + static constexpr uint8_t unbounded() + { + return UINT8_MAX; + } - static const fnode* ap(const std::string& name); - static const fnode* unop(op o, const fnode* f); - static const fnode* binop(op o, const fnode* f, const fnode* g); - static const fnode* multop(op o, std::vector l); - static const fnode* bunop(op o, const fnode* f, - uint8_t min, uint8_t max = unbounded()); + static const fnode* ap(const std::string& name); + static const fnode* unop(op o, const fnode* f); + static const fnode* binop(op o, const fnode* f, const fnode* g); + static const fnode* multop(op o, std::vector l); + static const fnode* bunop(op o, const fnode* f, + uint8_t min, uint8_t max = unbounded()); - op kind() const - { - return op_; - } + op kind() const + { + return op_; + } - std::string kindstr() const; + std::string kindstr() const; - bool is(op o) const - { - return op_ == o; - } + bool is(op o) const + { + return op_ == o; + } - bool is(op o1, op o2) const - { - return op_ == o1 || op_ == o2; - } + bool is(op o1, op o2) const + { + return op_ == o1 || op_ == o2; + } - bool is(std::initializer_list l) const - { - const fnode* n = this; - for (auto o: l) - { - if (!n->is(o)) - return false; - n = n->nth(0); - } - return true; - } + bool is(std::initializer_list l) const + { + const fnode* n = this; + for (auto o: l) + { + if (!n->is(o)) + return false; + n = n->nth(0); + } + return true; + } - /// \brief Remove operator \a o and return the child. - /// - /// This works only for unary operators. - const fnode* get_child_of(op o) const - { - if (op_ != o) - return nullptr; - assert(size_ == 1); - return nth(0); - } + /// \brief Remove operator \a o and return the child. + /// + /// This works only for unary operators. + const fnode* get_child_of(op o) const + { + if (op_ != o) + return nullptr; + assert(size_ == 1); + return nth(0); + } - /// \brief Remove all operators in \a l and return the child. - /// - /// This works only for a list of unary operators. - /// For instance if \c f is a formula for XG(a U b), - /// then f.get_child_of({op::X, op::G}) - /// will return the subformula a U b. - const fnode* get_child_of(std::initializer_list l) const - { - auto c = this; - for (auto o: l) - { - c = c->get_child_of(o); - if (c == nullptr) - return c; - } - return c; - } + /// \brief Remove all operators in \a l and return the child. + /// + /// This works only for a list of unary operators. + /// For instance if \c f is a formula for XG(a U b), + /// then f.get_child_of({op::X, op::G}) + /// will return the subformula a U b. + const fnode* get_child_of(std::initializer_list l) const + { + auto c = this; + for (auto o: l) + { + c = c->get_child_of(o); + if (c == nullptr) + return c; + } + return c; + } - uint8_t min() const - { - assert(op_ == op::FStar || op_ == op::Star); - return min_; - } + uint8_t min() const + { + assert(op_ == op::FStar || op_ == op::Star); + return min_; + } - uint8_t max() const - { - assert(op_ == op::FStar || op_ == op::Star); - return max_; - } + uint8_t max() const + { + assert(op_ == op::FStar || op_ == op::Star); + return max_; + } - uint8_t size() const - { - return size_; - } + uint8_t size() const + { + return size_; + } - size_t id() const - { - return id_; - } + size_t id() const + { + return id_; + } - const fnode*const* begin() const - { - return children; - } + const fnode*const* begin() const + { + return children; + } - const fnode*const* end() const - { - return children + size(); - } + const fnode*const* end() const + { + return children + size(); + } - const fnode* nth(unsigned i) const - { - if (i >= size()) - throw std::runtime_error("access to non-existing child"); - return children[i]; - } + const fnode* nth(unsigned i) const + { + if (i >= size()) + throw std::runtime_error("access to non-existing child"); + return children[i]; + } - static const fnode* ff() - { - return ff_; - } + static const fnode* ff() + { + return ff_; + } - bool is_ff() const - { - return op_ == op::ff; - } + bool is_ff() const + { + return op_ == op::ff; + } - static const fnode* tt() - { - return tt_; - } + static const fnode* tt() + { + return tt_; + } - bool is_tt() const - { - return op_ == op::tt; - } + bool is_tt() const + { + return op_ == op::tt; + } - static const fnode* eword() - { - return ew_; - } + static const fnode* eword() + { + return ew_; + } - bool is_eword() const - { - return op_ == op::eword; - } + bool is_eword() const + { + return op_ == op::eword; + } - bool is_constant() const - { - return op_ == op::ff || op_ == op::tt || op_ == op::eword; - } + bool is_constant() const + { + return op_ == op::ff || op_ == op::tt || op_ == op::eword; + } - bool is_Kleene_star() const - { - if (op_ != op::Star) - return false; - return min_ == 0 && max_ == unbounded(); - } + bool is_Kleene_star() const + { + if (op_ != op::Star) + return false; + return min_ == 0 && max_ == unbounded(); + } - static const fnode* one_star() - { - if (!one_star_) - one_star_ = bunop(op::Star, tt(), 0); - return one_star_; - } + static const fnode* one_star() + { + if (!one_star_) + one_star_ = bunop(op::Star, tt(), 0); + return one_star_; + } - const std::string& ap_name() const; - std::ostream& dump(std::ostream& os) const; + const std::string& ap_name() const; + std::ostream& dump(std::ostream& os) const; - const fnode* all_but(unsigned i) const; + const fnode* all_but(unsigned i) const; - unsigned boolean_count() const - { - unsigned pos = 0; - unsigned s = size(); - while (pos < s && children[pos]->is_boolean()) - ++pos; - return pos; - } + unsigned boolean_count() const + { + unsigned pos = 0; + unsigned s = size(); + while (pos < s && children[pos]->is_boolean()) + ++pos; + return pos; + } - const fnode* boolean_operands(unsigned* width = nullptr) const; + const fnode* boolean_operands(unsigned* width = nullptr) const; - /// return true if the unicity map contains only the globally - /// pre-allocated formulas. - static bool instances_check(); + /// return true if the unicity map contains only the globally + /// pre-allocated formulas. + static bool instances_check(); - //////////////// - // Properties // - //////////////// + //////////////// + // Properties // + //////////////// - /// Whether the formula use only boolean operators. - bool is_boolean() const - { - return is_.boolean; - } + /// Whether the formula use only boolean operators. + bool is_boolean() const + { + return is_.boolean; + } - /// Whether the formula use only AND, OR, and NOT operators. - bool is_sugar_free_boolean() const - { - return is_.sugar_free_boolean; - } + /// Whether the formula use only AND, OR, and NOT operators. + bool is_sugar_free_boolean() const + { + return is_.sugar_free_boolean; + } - /// \brief Whether the formula is in negative normal form. - /// - /// A formula is in negative normal form if the not operators - /// occur only in front of atomic propositions. - bool is_in_nenoform() const - { - return is_.in_nenoform; - } + /// \brief Whether the formula is in negative normal form. + /// + /// A formula is in negative normal form if the not operators + /// occur only in front of atomic propositions. + bool is_in_nenoform() const + { + return is_.in_nenoform; + } - /// Whether the formula is syntactically stutter_invariant - bool is_syntactic_stutter_invariant() const - { - return is_.syntactic_si; - } + /// Whether the formula is syntactically stutter_invariant + bool is_syntactic_stutter_invariant() const + { + return is_.syntactic_si; + } - /// Whether the formula avoids the F and G operators. - bool is_sugar_free_ltl() const - { - return is_.sugar_free_ltl; - } + /// Whether the formula avoids the F and G operators. + bool is_sugar_free_ltl() const + { + return is_.sugar_free_ltl; + } - /// Whether the formula uses only LTL operators. - bool is_ltl_formula() const - { - return is_.ltl_formula; - } + /// Whether the formula uses only LTL operators. + bool is_ltl_formula() const + { + return is_.ltl_formula; + } - /// Whether the formula uses only PSL operators. - bool is_psl_formula() const - { - return is_.psl_formula; - } + /// Whether the formula uses only PSL operators. + bool is_psl_formula() const + { + return is_.psl_formula; + } - /// Whether the formula uses only SERE operators. - bool is_sere_formula() const - { - return is_.sere_formula; - } + /// Whether the formula uses only SERE operators. + bool is_sere_formula() const + { + return is_.sere_formula; + } - /// Whether a SERE describes a finite language, or an LTL - /// formula uses no temporal operator but X. - bool is_finite() const - { - return is_.finite; - } + /// Whether a SERE describes a finite language, or an LTL + /// formula uses no temporal operator but X. + bool is_finite() const + { + return is_.finite; + } - /// \brief Whether the formula is purely eventual. - /// - /// Pure eventuality formulae are defined in - /** \verbatim - @InProceedings{ etessami.00.concur, - author = {Kousha Etessami and Gerard J. Holzmann}, - title = {Optimizing {B\"u}chi Automata}, - booktitle = {Proceedings of the 11th International Conference on - Concurrency Theory (Concur'2000)}, - pages = {153--167}, - year = {2000}, - editor = {C. Palamidessi}, - volume = {1877}, - series = {Lecture Notes in Computer Science}, - publisher = {Springer-Verlag} - } - \endverbatim */ - /// - /// A word that satisfies a pure eventuality can be prefixed by - /// anything and still satisfies the formula. - bool is_eventual() const - { - return is_.eventual; - } + /// \brief Whether the formula is purely eventual. + /// + /// Pure eventuality formulae are defined in + /** \verbatim + @InProceedings{ etessami.00.concur, + author = {Kousha Etessami and Gerard J. Holzmann}, + title = {Optimizing {B\"u}chi Automata}, + booktitle = {Proceedings of the 11th International Conference on + Concurrency Theory (Concur'2000)}, + pages = {153--167}, + year = {2000}, + editor = {C. Palamidessi}, + volume = {1877}, + series = {Lecture Notes in Computer Science}, + publisher = {Springer-Verlag} + } + \endverbatim */ + /// + /// A word that satisfies a pure eventuality can be prefixed by + /// anything and still satisfies the formula. + bool is_eventual() const + { + return is_.eventual; + } - /// \brief Whether a formula is purely universal. - /// - /// Purely universal formulae are defined in - /** \verbatim - @InProceedings{ etessami.00.concur, - author = {Kousha Etessami and Gerard J. Holzmann}, - title = {Optimizing {B\"u}chi Automata}, - booktitle = {Proceedings of the 11th International Conference on - Concurrency Theory (Concur'2000)}, - pages = {153--167}, - year = {2000}, - editor = {C. Palamidessi}, - volume = {1877}, - series = {Lecture Notes in Computer Science}, - publisher = {Springer-Verlag} - } - \endverbatim */ - /// - /// Any (non-empty) suffix of a word that satisfies a purely - /// universal formula also satisfies the formula. - bool is_universal() const - { - return is_.universal; - } + /// \brief Whether a formula is purely universal. + /// + /// Purely universal formulae are defined in + /** \verbatim + @InProceedings{ etessami.00.concur, + author = {Kousha Etessami and Gerard J. Holzmann}, + title = {Optimizing {B\"u}chi Automata}, + booktitle = {Proceedings of the 11th International Conference on + Concurrency Theory (Concur'2000)}, + pages = {153--167}, + year = {2000}, + editor = {C. Palamidessi}, + volume = {1877}, + series = {Lecture Notes in Computer Science}, + publisher = {Springer-Verlag} + } + \endverbatim */ + /// + /// Any (non-empty) suffix of a word that satisfies a purely + /// universal formula also satisfies the formula. + bool is_universal() const + { + return is_.universal; + } - /// Whether a PSL/LTL formula is syntactic safety property. - bool is_syntactic_safety() const - { - return is_.syntactic_safety; - } + /// Whether a PSL/LTL formula is syntactic safety property. + bool is_syntactic_safety() const + { + return is_.syntactic_safety; + } - /// Whether a PSL/LTL formula is syntactic guarantee property. - bool is_syntactic_guarantee() const - { - return is_.syntactic_guarantee; - } + /// Whether a PSL/LTL formula is syntactic guarantee property. + bool is_syntactic_guarantee() const + { + return is_.syntactic_guarantee; + } - /// Whether a PSL/LTL formula is syntactic obligation property. - bool is_syntactic_obligation() const - { - return is_.syntactic_obligation; - } + /// Whether a PSL/LTL formula is syntactic obligation property. + bool is_syntactic_obligation() const + { + return is_.syntactic_obligation; + } - /// Whether a PSL/LTL formula is syntactic recurrence property. - bool is_syntactic_recurrence() const - { - return is_.syntactic_recurrence; - } + /// Whether a PSL/LTL formula is syntactic recurrence property. + bool is_syntactic_recurrence() const + { + return is_.syntactic_recurrence; + } - /// Whether a PSL/LTL formula is syntactic persistence property. - bool is_syntactic_persistence() const - { - return is_.syntactic_persistence; - } + /// Whether a PSL/LTL formula is syntactic persistence property. + bool is_syntactic_persistence() const + { + return is_.syntactic_persistence; + } - /// Whether the formula has an occurrence of EConcatMarked. - bool is_marked() const - { - return !is_.not_marked; - } + /// Whether the formula has an occurrence of EConcatMarked. + bool is_marked() const + { + return !is_.not_marked; + } - /// Whether the formula accepts [*0]. - bool accepts_eword() const - { - return is_.accepting_eword; - } + /// Whether the formula accepts [*0]. + bool accepts_eword() const + { + return is_.accepting_eword; + } - bool has_lbt_atomic_props() const - { - return is_.lbt_atomic_props; - } + bool has_lbt_atomic_props() const + { + return is_.lbt_atomic_props; + } - bool has_spin_atomic_props() const - { - return is_.spin_atomic_props; - } + bool has_spin_atomic_props() const + { + return is_.spin_atomic_props; + } - private: - void setup_props(op o); - void destroy_aux() const; + private: + void setup_props(op o); + void destroy_aux() const; - static const fnode* unique(const fnode*); + static const fnode* unique(const fnode*); - // Destruction may only happen via destroy(). - ~fnode() = default; - // Disallow copies. - fnode(const fnode&) = delete; - fnode& operator=(const fnode&) = delete; + // Destruction may only happen via destroy(). + ~fnode() = default; + // Disallow copies. + fnode(const fnode&) = delete; + fnode& operator=(const fnode&) = delete; - template + template fnode(op o, iter begin, iter end) - { - size_t s = std::distance(begin, end); - if (s > (size_t) UINT16_MAX) - throw std::runtime_error("too many children for formula"); - size_ = s; - auto pos = children; - for (auto i = begin; i != end; ++i) - *pos++ = *i; - setup_props(o); - } + { + size_t s = std::distance(begin, end); + if (s > (size_t) UINT16_MAX) + throw std::runtime_error("too many children for formula"); + size_ = s; + auto pos = children; + for (auto i = begin; i != end; ++i) + *pos++ = *i; + setup_props(o); + } - fnode(op o, std::initializer_list l) - : fnode(o, l.begin(), l.end()) + fnode(op o, std::initializer_list l) + : fnode(o, l.begin(), l.end()) { } - fnode(op o, const fnode* f, uint8_t min, uint8_t max) + fnode(op o, const fnode* f, uint8_t min, uint8_t max) { size_ = 1; children[0] = f; @@ -479,195 +477,195 @@ namespace spot setup_props(o); } - static const fnode* ff_; - static const fnode* tt_; - static const fnode* ew_; - static const fnode* one_star_; + static const fnode* ff_; + static const fnode* tt_; + static const fnode* ew_; + static const fnode* one_star_; - op op_; // operator - uint8_t min_; // range minimum (for star-like operators) - uint8_t max_; // range maximum; - //uint8_t unused_; - uint16_t size_; // number of children - mutable uint16_t refs_ = 0; // reference count - 1; - size_t id_; // Also used as hash. - static size_t next_id_; + op op_; // operator + uint8_t min_; // range minimum (for star-like operators) + uint8_t max_; // range maximum; + //uint8_t unused_; + uint16_t size_; // number of children + mutable uint16_t refs_ = 0; // reference count - 1; + size_t id_; // Also used as hash. + static size_t next_id_; - struct ltl_prop - { - // All properties here should be expressed in such a a way - // that property(f && g) is just property(f)&property(g). - // This allows us to compute all properties of a compound - // formula in one operation. - // - // For instance we do not use a property that says "has - // temporal operator", because it would require an OR between - // the two arguments. Instead we have a property that - // says "no temporal operator", and that one is computed - // with an AND between the arguments. - // - // Also choose a name that makes sense when prefixed with - // "the formula is". - bool boolean:1; // No temporal operators. - bool sugar_free_boolean:1; // Only AND, OR, and NOT operators. - bool in_nenoform:1; // Negative Normal Form. - bool syntactic_si:1; // LTL-X or siPSL - bool sugar_free_ltl:1; // No F and G operators. - bool ltl_formula:1; // Only LTL operators. - bool psl_formula:1; // Only PSL operators. - bool sere_formula:1; // Only SERE operators. - bool finite:1; // Finite SERE formulae, or Bool+X forms. - bool eventual:1; // Purely eventual formula. - bool universal:1; // Purely universal formula. - bool syntactic_safety:1; // Syntactic Safety Property. - bool syntactic_guarantee:1; // Syntactic Guarantee Property. - bool syntactic_obligation:1; // Syntactic Obligation Property. - bool syntactic_recurrence:1; // Syntactic Recurrence Property. - bool syntactic_persistence:1; // Syntactic Persistence Property. - bool not_marked:1; // No occurrence of EConcatMarked. - bool accepting_eword:1; // Accepts the empty word. - bool lbt_atomic_props:1; // Use only atomic propositions like p42. - bool spin_atomic_props:1; // Use only spin-compatible atomic props. - }; - union - { - // Use an unsigned for fast computation of all properties. - unsigned props; - ltl_prop is_; - }; - - const fnode* children[1]; - }; - - /// Order two atomic propositions. - SPOT_API - int atomic_prop_cmp(const fnode* f, const fnode* g); - - /// \brief Strict Weak Ordering for const fnode* - /// inside n-ary operators - /// \ingroup ltl_essentials - /// - /// This is the comparison functor used by to order the - /// n-ary operands. It keeps Boolean formulae first in - /// order to speed up implication checks. - /// - /// Also keep literal alphabetically ordered. - struct formula_ptr_less_than_bool_first + struct ltl_prop { - bool - operator()(const fnode* left, const fnode* right) const - { - assert(left); - assert(right); - if (left == right) - return false; - - // We want Boolean formulae first. - bool lib = left->is_boolean(); - if (lib != right->is_boolean()) - return lib; - - // We have two Boolean formulae - if (lib) - { - bool lconst = left->is_constant(); - if (lconst != right->is_constant()) - return lconst; - if (!lconst) - { - auto get_literal = [](const fnode* f) -> const fnode* - { - if (f->is(op::Not)) - f = f->nth(0); - if (f->is(op::ap)) - return f; - return nullptr; - }; - // Literals should come first - const fnode* litl = get_literal(left); - const fnode* litr = get_literal(right); - if (!litl != !litr) - return litl; - if (litl) - { - // And they should be sorted alphabetically - int cmp = atomic_prop_cmp(litl, litr); - if (cmp) - return cmp < 0; - } - } - } - - size_t l = left->id(); - size_t r = right->id(); - if (l != r) - return l < r; - // Because the hash code assigned to each formula is the - // number of formulae constructed so far, it is very unlikely - // that we will ever reach a case were two different formulae - // have the same hash. This will happen only ever with have - // produced 256**sizeof(size_t) formulae (i.e. max_count has - // looped back to 0 and started over). In that case we can - // order two formulas by looking at their text representation. - // We could be more efficient and look at their AST, but it's - // not worth the burden. (Also ordering pointers is ruled out - // because it breaks the determinism of the implementation.) - std::ostringstream old; - left->dump(old); - std::ostringstream ord; - right->dump(ord); - return old.str() < ord.str(); - } + // All properties here should be expressed in such a a way + // that property(f && g) is just property(f)&property(g). + // This allows us to compute all properties of a compound + // formula in one operation. + // + // For instance we do not use a property that says "has + // temporal operator", because it would require an OR between + // the two arguments. Instead we have a property that + // says "no temporal operator", and that one is computed + // with an AND between the arguments. + // + // Also choose a name that makes sense when prefixed with + // "the formula is". + bool boolean:1; // No temporal operators. + bool sugar_free_boolean:1; // Only AND, OR, and NOT operators. + bool in_nenoform:1; // Negative Normal Form. + bool syntactic_si:1; // LTL-X or siPSL + bool sugar_free_ltl:1; // No F and G operators. + bool ltl_formula:1; // Only LTL operators. + bool psl_formula:1; // Only PSL operators. + bool sere_formula:1; // Only SERE operators. + bool finite:1; // Finite SERE formulae, or Bool+X forms. + bool eventual:1; // Purely eventual formula. + bool universal:1; // Purely universal formula. + bool syntactic_safety:1; // Syntactic Safety Property. + bool syntactic_guarantee:1; // Syntactic Guarantee Property. + bool syntactic_obligation:1; // Syntactic Obligation Property. + bool syntactic_recurrence:1; // Syntactic Recurrence Property. + bool syntactic_persistence:1; // Syntactic Persistence Property. + bool not_marked:1; // No occurrence of EConcatMarked. + bool accepting_eword:1; // Accepts the empty word. + bool lbt_atomic_props:1; // Use only atomic propositions like p42. + bool spin_atomic_props:1; // Use only spin-compatible atomic props. }; + union + { + // Use an unsigned for fast computation of all properties. + unsigned props; + ltl_prop is_; + }; + + const fnode* children[1]; + }; + + /// Order two atomic propositions. + SPOT_API + int atomic_prop_cmp(const fnode* f, const fnode* g); + + /// \brief Strict Weak Ordering for const fnode* + /// inside n-ary operators + /// \ingroup ltl_essentials + /// + /// This is the comparison functor used by to order the + /// n-ary operands. It keeps Boolean formulae first in + /// order to speed up implication checks. + /// + /// Also keep literal alphabetically ordered. + struct formula_ptr_less_than_bool_first + { + bool + operator()(const fnode* left, const fnode* right) const + { + assert(left); + assert(right); + if (left == right) + return false; + + // We want Boolean formulae first. + bool lib = left->is_boolean(); + if (lib != right->is_boolean()) + return lib; + + // We have two Boolean formulae + if (lib) + { + bool lconst = left->is_constant(); + if (lconst != right->is_constant()) + return lconst; + if (!lconst) + { + auto get_literal = [](const fnode* f) -> const fnode* + { + if (f->is(op::Not)) + f = f->nth(0); + if (f->is(op::ap)) + return f; + return nullptr; + }; + // Literals should come first + const fnode* litl = get_literal(left); + const fnode* litr = get_literal(right); + if (!litl != !litr) + return litl; + if (litl) + { + // And they should be sorted alphabetically + int cmp = atomic_prop_cmp(litl, litr); + if (cmp) + return cmp < 0; + } + } + } + + size_t l = left->id(); + size_t r = right->id(); + if (l != r) + return l < r; + // Because the hash code assigned to each formula is the + // number of formulae constructed so far, it is very unlikely + // that we will ever reach a case were two different formulae + // have the same hash. This will happen only ever with have + // produced 256**sizeof(size_t) formulae (i.e. max_count has + // looped back to 0 and started over). In that case we can + // order two formulas by looking at their text representation. + // We could be more efficient and look at their AST, but it's + // not worth the burden. (Also ordering pointers is ruled out + // because it breaks the determinism of the implementation.) + std::ostringstream old; + left->dump(old); + std::ostringstream ord; + right->dump(ord); + return old.str() < ord.str(); + } + }; #endif // SWIG - class SPOT_API formula final + class SPOT_API formula final + { + const fnode* ptr_; + public: + explicit formula(const fnode* f) noexcept + : ptr_(f) { - const fnode* ptr_; - public: - explicit formula(const fnode* f) noexcept - : ptr_(f) + } + + formula(std::nullptr_t) noexcept + : ptr_(nullptr) { } - formula(std::nullptr_t) noexcept - : ptr_(nullptr) + formula() noexcept + : ptr_(nullptr) { } - formula() noexcept - : ptr_(nullptr) - { - } - - formula(const formula& f) noexcept - : ptr_(f.ptr_) + formula(const formula& f) noexcept + : ptr_(f.ptr_) { if (ptr_) ptr_->clone(); } - formula(formula&& f) noexcept - : ptr_(f.ptr_) + formula(formula&& f) noexcept + : ptr_(f.ptr_) { f.ptr_ = nullptr; } - ~formula() + ~formula() { if (ptr_) ptr_->destroy(); } - const formula& operator=(std::nullptr_t) + const formula& operator=(std::nullptr_t) { this->~formula(); ptr_ = nullptr; return *this; } - const formula& operator=(const formula& f) + const formula& operator=(const formula& f) { this->~formula(); if ((ptr_ = f.ptr_)) @@ -675,601 +673,600 @@ namespace spot return *this; } - const formula& operator=(formula&& f) noexcept + const formula& operator=(formula&& f) noexcept { std::swap(f.ptr_, ptr_); return *this; } - bool operator<(const formula& other) const noexcept - { - if (SPOT_UNLIKELY(!other.ptr_)) - return false; - if (SPOT_UNLIKELY(!ptr_)) - return true; - return id() < other.id(); - } + bool operator<(const formula& other) const noexcept + { + if (SPOT_UNLIKELY(!other.ptr_)) + return false; + if (SPOT_UNLIKELY(!ptr_)) + return true; + return id() < other.id(); + } - bool operator<=(const formula& other) const noexcept - { - if (SPOT_UNLIKELY(!other.ptr_)) - return !ptr_; - if (SPOT_UNLIKELY(!ptr_)) - return true; - return id() <= other.id(); - } + bool operator<=(const formula& other) const noexcept + { + if (SPOT_UNLIKELY(!other.ptr_)) + return !ptr_; + if (SPOT_UNLIKELY(!ptr_)) + return true; + return id() <= other.id(); + } - bool operator>(const formula& other) const noexcept - { - if (SPOT_UNLIKELY(!ptr_)) - return false; - if (SPOT_UNLIKELY(!other.ptr_)) - return true; - return id() > other.id(); - } + bool operator>(const formula& other) const noexcept + { + if (SPOT_UNLIKELY(!ptr_)) + return false; + if (SPOT_UNLIKELY(!other.ptr_)) + return true; + return id() > other.id(); + } - bool operator>=(const formula& other) const noexcept - { - if (SPOT_UNLIKELY(!ptr_)) - return !!other.ptr_; - if (SPOT_UNLIKELY(!other.ptr_)) - return true; - return id() >= other.id(); - } + bool operator>=(const formula& other) const noexcept + { + if (SPOT_UNLIKELY(!ptr_)) + return !!other.ptr_; + if (SPOT_UNLIKELY(!other.ptr_)) + return true; + return id() >= other.id(); + } - bool operator==(const formula& other) const noexcept - { - return other.ptr_ == ptr_; - } + bool operator==(const formula& other) const noexcept + { + return other.ptr_ == ptr_; + } - bool operator==(std::nullptr_t) const noexcept - { - return ptr_ == nullptr; - } + bool operator==(std::nullptr_t) const noexcept + { + return ptr_ == nullptr; + } - bool operator!=(const formula& other) const noexcept - { - return other.ptr_ != ptr_; - } + bool operator!=(const formula& other) const noexcept + { + return other.ptr_ != ptr_; + } - bool operator!=(std::nullptr_t) const noexcept - { - return ptr_ != nullptr; - } + bool operator!=(std::nullptr_t) const noexcept + { + return ptr_ != nullptr; + } - operator bool() const - { - return ptr_ != nullptr; - } + operator bool() const + { + return ptr_ != nullptr; + } - ///////////////////////// - // Forwarded functions // - ///////////////////////// + ///////////////////////// + // Forwarded functions // + ///////////////////////// - static constexpr uint8_t unbounded() - { - return fnode::unbounded(); - } + static constexpr uint8_t unbounded() + { + return fnode::unbounded(); + } - static formula ap(const std::string& name) - { - return formula(fnode::ap(name)); - } + static formula ap(const std::string& name) + { + return formula(fnode::ap(name)); + } - static formula unop(op o, const formula& f) - { - return formula(fnode::unop(o, f.ptr_->clone())); - } + static formula unop(op o, const formula& f) + { + return formula(fnode::unop(o, f.ptr_->clone())); + } #ifndef SWIG - static formula unop(op o, formula&& f) - { - return formula(fnode::unop(o, f.to_node_())); - } + static formula unop(op o, formula&& f) + { + return formula(fnode::unop(o, f.to_node_())); + } #endif // !SWIG #ifdef SWIG #define SPOT_DEF_UNOP(Name) \ - static formula Name(const formula& f) \ - { \ - return unop(op::Name, f); \ - } + static formula Name(const formula& f) \ + { \ + return unop(op::Name, f); \ + } #else // !SWIG #define SPOT_DEF_UNOP(Name) \ - static formula Name(const formula& f) \ - { \ - return unop(op::Name, f); \ - } \ - static formula Name(formula&& f) \ - { \ - return unop(op::Name, std::move(f)); \ - } + static formula Name(const formula& f) \ + { \ + return unop(op::Name, f); \ + } \ + static formula Name(formula&& f) \ + { \ + return unop(op::Name, std::move(f)); \ + } #endif // !SWIG - SPOT_DEF_UNOP(Not); - SPOT_DEF_UNOP(X); - SPOT_DEF_UNOP(F); - SPOT_DEF_UNOP(G); - SPOT_DEF_UNOP(Closure); - SPOT_DEF_UNOP(NegClosure); - SPOT_DEF_UNOP(NegClosureMarked); + SPOT_DEF_UNOP(Not); + SPOT_DEF_UNOP(X); + SPOT_DEF_UNOP(F); + SPOT_DEF_UNOP(G); + SPOT_DEF_UNOP(Closure); + SPOT_DEF_UNOP(NegClosure); + SPOT_DEF_UNOP(NegClosureMarked); #undef SPOT_DEF_UNOP - static formula binop(op o, const formula& f, const formula& g) - { - return formula(fnode::binop(o, f.ptr_->clone(), g.ptr_->clone())); - } + static formula binop(op o, const formula& f, const formula& g) + { + return formula(fnode::binop(o, f.ptr_->clone(), g.ptr_->clone())); + } #ifndef SWIG - static formula binop(op o, const formula& f, formula&& g) - { - return formula(fnode::binop(o, f.ptr_->clone(), g.to_node_())); - } + static formula binop(op o, const formula& f, formula&& g) + { + return formula(fnode::binop(o, f.ptr_->clone(), g.to_node_())); + } - static formula binop(op o, formula&& f, const formula& g) - { - return formula(fnode::binop(o, f.to_node_(), g.ptr_->clone())); - } + static formula binop(op o, formula&& f, const formula& g) + { + return formula(fnode::binop(o, f.to_node_(), g.ptr_->clone())); + } - static formula binop(op o, formula&& f, formula&& g) - { - return formula(fnode::binop(o, f.to_node_(), g.to_node_())); - } + static formula binop(op o, formula&& f, formula&& g) + { + return formula(fnode::binop(o, f.to_node_(), g.to_node_())); + } #endif //SWIG #ifdef SWIG #define SPOT_DEF_BINOP(Name) \ - static formula Name(const formula& f, const formula& g) \ - { \ - return binop(op::Name, f, g); \ - } + static formula Name(const formula& f, const formula& g) \ + { \ + return binop(op::Name, f, g); \ + } #else // !SWIG #define SPOT_DEF_BINOP(Name) \ - static formula Name(const formula& f, const formula& g) \ - { \ - return binop(op::Name, f, g); \ - } \ - static formula Name(const formula& f, formula&& g) \ - { \ - return binop(op::Name, f, std::move(g)); \ - } \ - static formula Name(formula&& f, const formula& g) \ - { \ - return binop(op::Name, std::move(f), g); \ - } \ - static formula Name(formula&& f, formula&& g) \ - { \ - return binop(op::Name, std::move(f), std::move(g)); \ - } + static formula Name(const formula& f, const formula& g) \ + { \ + return binop(op::Name, f, g); \ + } \ + static formula Name(const formula& f, formula&& g) \ + { \ + return binop(op::Name, f, std::move(g)); \ + } \ + static formula Name(formula&& f, const formula& g) \ + { \ + return binop(op::Name, std::move(f), g); \ + } \ + static formula Name(formula&& f, formula&& g) \ + { \ + return binop(op::Name, std::move(f), std::move(g)); \ + } #endif // !SWIG - SPOT_DEF_BINOP(Xor); - SPOT_DEF_BINOP(Implies); - SPOT_DEF_BINOP(Equiv); - SPOT_DEF_BINOP(U); - SPOT_DEF_BINOP(R); - SPOT_DEF_BINOP(W); - SPOT_DEF_BINOP(M); - SPOT_DEF_BINOP(EConcat); - SPOT_DEF_BINOP(EConcatMarked); - SPOT_DEF_BINOP(UConcat); + SPOT_DEF_BINOP(Xor); + SPOT_DEF_BINOP(Implies); + SPOT_DEF_BINOP(Equiv); + SPOT_DEF_BINOP(U); + SPOT_DEF_BINOP(R); + SPOT_DEF_BINOP(W); + SPOT_DEF_BINOP(M); + SPOT_DEF_BINOP(EConcat); + SPOT_DEF_BINOP(EConcatMarked); + SPOT_DEF_BINOP(UConcat); #undef SPOT_DEF_BINOP - static formula multop(op o, const std::vector& l) - { - std::vector tmp; - tmp.reserve(l.size()); - for (auto f: l) - if (f.ptr_) - tmp.push_back(f.ptr_->clone()); - return formula(fnode::multop(o, std::move(tmp))); - } + static formula multop(op o, const std::vector& l) + { + std::vector tmp; + tmp.reserve(l.size()); + for (auto f: l) + if (f.ptr_) + tmp.push_back(f.ptr_->clone()); + return formula(fnode::multop(o, std::move(tmp))); + } #ifndef SWIG - static formula multop(op o, std::vector&& l) - { - std::vector tmp; - tmp.reserve(l.size()); - for (auto f: l) - if (f.ptr_) - tmp.push_back(f.to_node_()); - return formula(fnode::multop(o, std::move(tmp))); - } + static formula multop(op o, std::vector&& l) + { + std::vector tmp; + tmp.reserve(l.size()); + for (auto f: l) + if (f.ptr_) + tmp.push_back(f.to_node_()); + return formula(fnode::multop(o, std::move(tmp))); + } #endif // !SWIG #ifdef SWIG -#define SPOT_DEF_MULTOP(Name) \ - static formula Name(const std::vector& l) \ - { \ - return multop(op::Name, l); \ - } +#define SPOT_DEF_MULTOP(Name) \ + static formula Name(const std::vector& l) \ + { \ + return multop(op::Name, l); \ + } #else // !SWIG -#define SPOT_DEF_MULTOP(Name) \ - static formula Name(const std::vector& l) \ - { \ - return multop(op::Name, l); \ - } \ - \ - static formula Name(std::vector&& l) \ - { \ - return multop(op::Name, std::move(l)); \ - } +#define SPOT_DEF_MULTOP(Name) \ + static formula Name(const std::vector& l) \ + { \ + return multop(op::Name, l); \ + } \ + \ + static formula Name(std::vector&& l) \ + { \ + return multop(op::Name, std::move(l)); \ + } #endif // !SWIG - SPOT_DEF_MULTOP(Or); - SPOT_DEF_MULTOP(OrRat); - SPOT_DEF_MULTOP(And); - SPOT_DEF_MULTOP(AndRat); - SPOT_DEF_MULTOP(AndNLM); - SPOT_DEF_MULTOP(Concat); - SPOT_DEF_MULTOP(Fusion); + SPOT_DEF_MULTOP(Or); + SPOT_DEF_MULTOP(OrRat); + SPOT_DEF_MULTOP(And); + SPOT_DEF_MULTOP(AndRat); + SPOT_DEF_MULTOP(AndNLM); + SPOT_DEF_MULTOP(Concat); + SPOT_DEF_MULTOP(Fusion); #undef SPOT_DEF_MULTOP - static formula bunop(op o, const formula& f, - uint8_t min = 0U, - uint8_t max = unbounded()) - { - return formula(fnode::bunop(o, f.ptr_->clone(), min, max)); - } + static formula bunop(op o, const formula& f, + uint8_t min = 0U, + uint8_t max = unbounded()) + { + return formula(fnode::bunop(o, f.ptr_->clone(), min, max)); + } #ifndef SWIG - static formula bunop(op o, formula&& f, - uint8_t min = 0U, - uint8_t max = unbounded()) - { - return formula(fnode::bunop(o, f.to_node_(), min, max)); - } + static formula bunop(op o, formula&& f, + uint8_t min = 0U, + uint8_t max = unbounded()) + { + return formula(fnode::bunop(o, f.to_node_(), min, max)); + } #endif // !SWIG #if SWIG #define SPOT_DEF_BUNOP(Name) \ - static formula Name(const formula& f, \ - uint8_t min = 0U, \ - uint8_t max = unbounded()) \ - { \ - return bunop(op::Name, f, min, max); \ - } + static formula Name(const formula& f, \ + uint8_t min = 0U, \ + uint8_t max = unbounded()) \ + { \ + return bunop(op::Name, f, min, max); \ + } #else // !SWIG #define SPOT_DEF_BUNOP(Name) \ - static formula Name(const formula& f, \ - uint8_t min = 0U, \ - uint8_t max = unbounded()) \ - { \ - return bunop(op::Name, f, min, max); \ - } \ - static formula Name(formula&& f, \ - uint8_t min = 0U, \ - uint8_t max = unbounded()) \ - { \ - return bunop(op::Name, std::move(f), min, max); \ - } + static formula Name(const formula& f, \ + uint8_t min = 0U, \ + uint8_t max = unbounded()) \ + { \ + return bunop(op::Name, f, min, max); \ + } \ + static formula Name(formula&& f, \ + uint8_t min = 0U, \ + uint8_t max = unbounded()) \ + { \ + return bunop(op::Name, std::move(f), min, max); \ + } #endif - SPOT_DEF_BUNOP(Star); - SPOT_DEF_BUNOP(FStar); + SPOT_DEF_BUNOP(Star); + SPOT_DEF_BUNOP(FStar); #undef SPOT_DEF_BUNOP - static formula sugar_goto(const formula& b, uint8_t min, uint8_t max); + static formula sugar_goto(const formula& b, uint8_t min, uint8_t max); - static formula sugar_equal(const formula& b, uint8_t min, uint8_t max); + static formula sugar_equal(const formula& b, uint8_t min, uint8_t max); #ifndef SWIG - const fnode* to_node_() + const fnode* to_node_() + { + auto tmp = ptr_; + ptr_ = nullptr; + return tmp; + } +#endif + + op kind() const + { + return ptr_->kind(); + } + + std::string kindstr() const + { + return ptr_->kindstr(); + } + + bool is(op o) const + { + return ptr_->is(o); + } + +#ifndef SWIG + bool is(op o1, op o2) const + { + return ptr_->is(o1, o2); + } + + bool is(std::initializer_list l) const + { + return ptr_->is(l); + } +#endif + + formula get_child_of(op o) const + { + auto f = ptr_->get_child_of(o); + if (f) + f->clone(); + return formula(f); + } + +#ifndef SWIG + formula get_child_of(std::initializer_list l) const + { + auto f = ptr_->get_child_of(l); + if (f) + f->clone(); + return formula(f); + } +#endif + + uint8_t min() const + { + return ptr_->min(); + } + + uint8_t max() const + { + return ptr_->max(); + } + + uint8_t size() const + { + return ptr_->size(); + } + + size_t id() const + { + return ptr_->id(); + } + +#ifndef SWIG + class SPOT_API formula_child_iterator final + { + const fnode*const* ptr_; + public: + formula_child_iterator() + : ptr_(nullptr) + { + } + + formula_child_iterator(const fnode*const* f) + : ptr_(f) + { + } + + bool operator==(formula_child_iterator o) { - auto tmp = ptr_; - ptr_ = nullptr; + return ptr_ == o.ptr_; + } + + bool operator!=(formula_child_iterator o) + { + return ptr_ != o.ptr_; + } + + formula operator*() + { + return formula((*ptr_)->clone()); + } + + formula_child_iterator operator++() + { + ++ptr_; + return *this; + } + + formula_child_iterator operator++(int) + { + auto tmp = *this; + ++ptr_; return tmp; } -#endif - - op kind() const - { - return ptr_->kind(); - } - - std::string kindstr() const - { - return ptr_->kindstr(); - } - - bool is(op o) const - { - return ptr_->is(o); - } - -#ifndef SWIG - bool is(op o1, op o2) const - { - return ptr_->is(o1, o2); - } - - bool is(std::initializer_list l) const - { - return ptr_->is(l); - } -#endif - - formula get_child_of(op o) const - { - auto f = ptr_->get_child_of(o); - if (f) - f->clone(); - return formula(f); - } - -#ifndef SWIG - formula get_child_of(std::initializer_list l) const - { - auto f = ptr_->get_child_of(l); - if (f) - f->clone(); - return formula(f); - } -#endif - - uint8_t min() const - { - return ptr_->min(); - } - - uint8_t max() const - { - return ptr_->max(); - } - - uint8_t size() const - { - return ptr_->size(); - } - - size_t id() const - { - return ptr_->id(); - } - -#ifndef SWIG - class SPOT_API formula_child_iterator final - { - const fnode*const* ptr_; - public: - formula_child_iterator() - : ptr_(nullptr) - { - } - - formula_child_iterator(const fnode*const* f) - : ptr_(f) - { - } - - bool operator==(formula_child_iterator o) - { - return ptr_ == o.ptr_; - } - - bool operator!=(formula_child_iterator o) - { - return ptr_ != o.ptr_; - } - - formula operator*() - { - return formula((*ptr_)->clone()); - } - - formula_child_iterator operator++() - { - ++ptr_; - return *this; - } - - formula_child_iterator operator++(int) - { - auto tmp = *this; - ++ptr_; - return tmp; - } - }; - - formula_child_iterator begin() const - { - return ptr_->begin(); - } - - formula_child_iterator end() const - { - return ptr_->end(); - } - - formula operator[](unsigned i) const - { - return formula(ptr_->nth(i)->clone()); - } -#endif - - static formula ff() - { - return formula(fnode::ff()); - } - - bool is_ff() const - { - return ptr_->is_ff(); - } - - static formula tt() - { - return formula(fnode::tt()); - } - - bool is_tt() const - { - return ptr_->is_tt(); - } - - static formula eword() - { - return formula(fnode::eword()); - } - - bool is_eword() const - { - return ptr_->is_eword(); - } - - bool is_constant() const - { - return ptr_->is_constant(); - } - - bool is_Kleene_star() const - { - return ptr_->is_Kleene_star(); - } - - static formula one_star() - { - return formula(fnode::one_star()->clone()); - } - - bool is_literal() - { - return (is(op::ap) || - // If f is in nenoform, Not can only occur in front of - // an atomic proposition. So this way we do not have - // to check the type of the child. - (is(op::Not) && is_boolean() && is_in_nenoform())); - } - - const std::string& ap_name() const - { - return ptr_->ap_name(); - } - - std::ostream& dump(std::ostream& os) const - { - return ptr_->dump(os); - } - - formula all_but(unsigned i) const - { - return formula(ptr_->all_but(i)); - } - - unsigned boolean_count() const - { - return ptr_->boolean_count(); - } - - formula boolean_operands(unsigned* width = nullptr) const - { - return formula(ptr_->boolean_operands(width)); - } - -#define SPOT_DEF_PROP(Name) \ - bool Name() const \ - { \ - return ptr_->Name(); \ - } - SPOT_DEF_PROP(is_boolean); - SPOT_DEF_PROP(is_sugar_free_boolean); - SPOT_DEF_PROP(is_in_nenoform); - SPOT_DEF_PROP(is_syntactic_stutter_invariant); - SPOT_DEF_PROP(is_sugar_free_ltl); - SPOT_DEF_PROP(is_ltl_formula); - SPOT_DEF_PROP(is_psl_formula); - SPOT_DEF_PROP(is_sere_formula); - SPOT_DEF_PROP(is_finite); - SPOT_DEF_PROP(is_eventual); - SPOT_DEF_PROP(is_universal); - SPOT_DEF_PROP(is_syntactic_safety); - SPOT_DEF_PROP(is_syntactic_guarantee); - SPOT_DEF_PROP(is_syntactic_obligation); - SPOT_DEF_PROP(is_syntactic_recurrence); - SPOT_DEF_PROP(is_syntactic_persistence); - SPOT_DEF_PROP(is_marked); - SPOT_DEF_PROP(accepts_eword); - SPOT_DEF_PROP(has_lbt_atomic_props); - SPOT_DEF_PROP(has_spin_atomic_props); -#undef SPOT_DEF_PROP - - template - formula map(Trans trans) - { - switch (op o = kind()) - { - case op::ff: - case op::tt: - case op::eword: - case op::ap: - return *this; - case op::Not: - case op::X: - case op::F: - case op::G: - case op::Closure: - case op::NegClosure: - case op::NegClosureMarked: - return unop(o, trans((*this)[0])); - case op::Xor: - case op::Implies: - case op::Equiv: - case op::U: - case op::R: - case op::W: - case op::M: - case op::EConcat: - case op::EConcatMarked: - case op::UConcat: - { - formula tmp = trans((*this)[0]); - return binop(o, tmp, trans((*this)[1])); - } - case op::Or: - case op::OrRat: - case op::And: - case op::AndRat: - case op::AndNLM: - case op::Concat: - case op::Fusion: - { - std::vector tmp; - tmp.reserve(size()); - for (auto f: *this) - tmp.push_back(trans(f)); - return multop(o, std::move(tmp)); - } - case op::Star: - case op::FStar: - return bunop(o, trans((*this)[0]), min(), max()); - } - SPOT_UNREACHABLE(); - } - - template - void traverse(Func func) - { - if (func(*this)) - return; - for (auto f: *this) - f.traverse(func); - } }; - /// Print the properties of formula \a f on stream \a out. - SPOT_API - std::ostream& print_formula_props(std::ostream& out, const formula& f, - bool abbreviated = false); + formula_child_iterator begin() const + { + return ptr_->begin(); + } - /// List the properties of formula \a f. - SPOT_API - std::list list_formula_props(const formula& f); - } + formula_child_iterator end() const + { + return ptr_->end(); + } + + formula operator[](unsigned i) const + { + return formula(ptr_->nth(i)->clone()); + } +#endif + + static formula ff() + { + return formula(fnode::ff()); + } + + bool is_ff() const + { + return ptr_->is_ff(); + } + + static formula tt() + { + return formula(fnode::tt()); + } + + bool is_tt() const + { + return ptr_->is_tt(); + } + + static formula eword() + { + return formula(fnode::eword()); + } + + bool is_eword() const + { + return ptr_->is_eword(); + } + + bool is_constant() const + { + return ptr_->is_constant(); + } + + bool is_Kleene_star() const + { + return ptr_->is_Kleene_star(); + } + + static formula one_star() + { + return formula(fnode::one_star()->clone()); + } + + bool is_literal() + { + return (is(op::ap) || + // If f is in nenoform, Not can only occur in front of + // an atomic proposition. So this way we do not have + // to check the type of the child. + (is(op::Not) && is_boolean() && is_in_nenoform())); + } + + const std::string& ap_name() const + { + return ptr_->ap_name(); + } + + std::ostream& dump(std::ostream& os) const + { + return ptr_->dump(os); + } + + formula all_but(unsigned i) const + { + return formula(ptr_->all_but(i)); + } + + unsigned boolean_count() const + { + return ptr_->boolean_count(); + } + + formula boolean_operands(unsigned* width = nullptr) const + { + return formula(ptr_->boolean_operands(width)); + } + +#define SPOT_DEF_PROP(Name) \ + bool Name() const \ + { \ + return ptr_->Name(); \ + } + SPOT_DEF_PROP(is_boolean); + SPOT_DEF_PROP(is_sugar_free_boolean); + SPOT_DEF_PROP(is_in_nenoform); + SPOT_DEF_PROP(is_syntactic_stutter_invariant); + SPOT_DEF_PROP(is_sugar_free_ltl); + SPOT_DEF_PROP(is_ltl_formula); + SPOT_DEF_PROP(is_psl_formula); + SPOT_DEF_PROP(is_sere_formula); + SPOT_DEF_PROP(is_finite); + SPOT_DEF_PROP(is_eventual); + SPOT_DEF_PROP(is_universal); + SPOT_DEF_PROP(is_syntactic_safety); + SPOT_DEF_PROP(is_syntactic_guarantee); + SPOT_DEF_PROP(is_syntactic_obligation); + SPOT_DEF_PROP(is_syntactic_recurrence); + SPOT_DEF_PROP(is_syntactic_persistence); + SPOT_DEF_PROP(is_marked); + SPOT_DEF_PROP(accepts_eword); + SPOT_DEF_PROP(has_lbt_atomic_props); + SPOT_DEF_PROP(has_spin_atomic_props); +#undef SPOT_DEF_PROP + + template + formula map(Trans trans) + { + switch (op o = kind()) + { + case op::ff: + case op::tt: + case op::eword: + case op::ap: + return *this; + case op::Not: + case op::X: + case op::F: + case op::G: + case op::Closure: + case op::NegClosure: + case op::NegClosureMarked: + return unop(o, trans((*this)[0])); + case op::Xor: + case op::Implies: + case op::Equiv: + case op::U: + case op::R: + case op::W: + case op::M: + case op::EConcat: + case op::EConcatMarked: + case op::UConcat: + { + formula tmp = trans((*this)[0]); + return binop(o, tmp, trans((*this)[1])); + } + case op::Or: + case op::OrRat: + case op::And: + case op::AndRat: + case op::AndNLM: + case op::Concat: + case op::Fusion: + { + std::vector tmp; + tmp.reserve(size()); + for (auto f: *this) + tmp.push_back(trans(f)); + return multop(o, std::move(tmp)); + } + case op::Star: + case op::FStar: + return bunop(o, trans((*this)[0]), min(), max()); + } + SPOT_UNREACHABLE(); + } + + template + void traverse(Func func) + { + if (func(*this)) + return; + for (auto f: *this) + f.traverse(func); + } + }; + + /// Print the properties of formula \a f on stream \a out. + SPOT_API + std::ostream& print_formula_props(std::ostream& out, const formula& f, + bool abbreviated = false); + + /// List the properties of formula \a f. + SPOT_API + std::list list_formula_props(const formula& f); } #ifndef SWIG namespace std { template <> - struct hash + struct hash { - size_t operator()(const spot::ltl::formula& x) const noexcept + size_t operator()(const spot::formula& x) const noexcept { return x.id(); } diff --git a/src/tl/length.cc b/src/tl/length.cc index c345952db..224f96aef 100644 --- a/src/tl/length.cc +++ b/src/tl/length.cc @@ -25,56 +25,53 @@ namespace spot { - namespace ltl + int + length(formula f) { - int - length(formula f) - { - int len = 0; - f.traverse([&len](const formula& x) - { - auto s = x.size(); - if (s > 1) - len += s - 1; - else - ++len; - return false; - }); - return len; - } - - int - length_boolone(formula f) - { - int len = 0; - f.traverse([&len](const formula& x) - { - if (x.is_boolean()) - { - ++len; - return true; - } - auto s = x.size(); - if (s > 2) - { - int b = 0; - for (const auto& y: x) - if (y.is_boolean()) - ++b; - len += s - b * 2 + 1; - } - else if (s > 1) - { - len += s - 1; - } - else - { - ++len; - } - return false; - }); - return len; - } - + int len = 0; + f.traverse([&len](const formula& x) + { + auto s = x.size(); + if (s > 1) + len += s - 1; + else + ++len; + return false; + }); + return len; } + + int + length_boolone(formula f) + { + int len = 0; + f.traverse([&len](const formula& x) + { + if (x.is_boolean()) + { + ++len; + return true; + } + auto s = x.size(); + if (s > 2) + { + int b = 0; + for (const auto& y: x) + if (y.is_boolean()) + ++b; + len += s - b * 2 + 1; + } + else if (s > 1) + { + len += s - 1; + } + else + { + ++len; + } + return false; + }); + return len; + } + } diff --git a/src/tl/length.hh b/src/tl/length.hh index 5a42cdd84..559e8a17e 100644 --- a/src/tl/length.hh +++ b/src/tl/length.hh @@ -26,28 +26,25 @@ namespace spot { - namespace ltl - { - /// \ingroup ltl_misc - /// \brief Compute the length of a formula. - /// - /// The length of a formula is the number of atomic propositions, - /// constants, and operators (logical and temporal) occurring in - /// the formula. n-ary operators count for n-1; for instance - /// a | b | c has length 5, even if there is only as - /// single | node internally. - /// - /// If squash_boolean is set, all Boolean formulae are assumed - /// to have length one. - SPOT_API - int length(formula f); + /// \ingroup ltl_misc + /// \brief Compute the length of a formula. + /// + /// The length of a formula is the number of atomic propositions, + /// constants, and operators (logical and temporal) occurring in + /// the formula. n-ary operators count for n-1; for instance + /// a | b | c has length 5, even if there is only as + /// single | node internally. + /// + /// If squash_boolean is set, all Boolean formulae are assumed + /// to have length one. + SPOT_API + int length(formula f); - /// \ingroup ltl_misc - /// \brief Compute the length of a formula, squashing Boolean formulae - /// - /// This is similar to spot::ltl::length(), except all Boolean - /// formulae are assumed to have length one. - SPOT_API - int length_boolone(formula f); - } + /// \ingroup ltl_misc + /// \brief Compute the length of a formula, squashing Boolean formulae + /// + /// This is similar to spot::length(), except all Boolean + /// formulae are assumed to have length one. + SPOT_API + int length_boolone(formula f); } diff --git a/src/tl/mark.cc b/src/tl/mark.cc index b970d402d..79d6ee36f 100644 --- a/src/tl/mark.cc +++ b/src/tl/mark.cc @@ -25,170 +25,166 @@ namespace spot { - namespace ltl + formula + mark_tools::mark_concat_ops(formula f) { - formula - mark_tools::mark_concat_ops(formula f) - { - f2f_map::iterator i = markops_.find(f); - if (i != markops_.end()) - return i->second; + f2f_map::iterator i = markops_.find(f); + if (i != markops_.end()) + return i->second; - ltl::formula res; - switch (f.kind()) + formula res; + switch (f.kind()) + { + case op::ff: + case op::tt: + case op::eword: + case op::ap: + case op::Not: + case op::X: + case op::F: + case op::G: + case op::Closure: + case op::NegClosureMarked: + case op::OrRat: + case op::AndRat: + case op::AndNLM: + case op::Star: + case op::FStar: + case op::U: + case op::R: + case op::W: + case op::M: + case op::EConcatMarked: + case op::UConcat: + case op::Concat: + case op::Fusion: + res = f; + break; + case op::NegClosure: + res = formula::NegClosureMarked(f[0]); + break; + case op::EConcat: + res = formula::EConcatMarked(f[0], f[1]); + break; + case op::Or: + case op::And: + res = f.map([this](formula f) + { + return this->mark_concat_ops(f); + }); + break; + case op::Xor: + case op::Implies: + case op::Equiv: + SPOT_UNIMPLEMENTED(); + } + + markops_[f] = res; + return res; + } + + formula + mark_tools::simplify_mark(formula f) + { + if (!f.is_marked()) + return f; + + f2f_map::iterator i = simpmark_.find(f); + if (i != simpmark_.end()) + return i->second; + + auto recurse = [this](formula f) + { + return this->simplify_mark(f); + }; + + formula res; + switch (f.kind()) + { + case op::ff: + case op::tt: + case op::eword: + case op::ap: + case op::Not: + case op::X: + case op::F: + case op::G: + case op::Closure: + case op::NegClosure: + case op::NegClosureMarked: + case op::U: + case op::R: + case op::W: + case op::M: + case op::EConcat: + case op::EConcatMarked: + case op::UConcat: + res = f; + break; + case op::Or: + res = f.map(recurse); + break; + case op::And: { - case op::ff: - case op::tt: - case op::eword: - case op::ap: - case op::Not: - case op::X: - case op::F: - case op::G: - case op::Closure: - case op::NegClosureMarked: - case op::OrRat: - case op::AndRat: - case op::AndNLM: - case op::Star: - case op::FStar: - case op::U: - case op::R: - case op::W: - case op::M: - case op::EConcatMarked: - case op::UConcat: - case op::Concat: - case op::Fusion: - res = f; - break; - case op::NegClosure: - res = ltl::formula::NegClosureMarked(f[0]); - break; - case op::EConcat: - res = ltl::formula::EConcatMarked(f[0], f[1]); - break; - case op::Or: - case op::And: - res = f.map([this](formula f) - { - return this->mark_concat_ops(f); - }); - break; - case op::Xor: - case op::Implies: - case op::Equiv: - SPOT_UNIMPLEMENTED(); + std::set> empairs; + std::set nmset; + std::vector elist; + std::vector nlist; + std::vector v; + + for (auto c: f) + { + if (c.is(op::EConcatMarked)) + { + empairs.emplace(c[0], c[1]); + v.push_back(c.map(recurse)); + } + else if (c.is(op::EConcat)) + { + elist.push_back(c); + } + else if (c.is(op::NegClosureMarked)) + { + nmset.insert(c[0]); + v.push_back(c.map(recurse)); + } + else if (c.is(op::NegClosure)) + { + nlist.push_back(c); + } + else + { + v.push_back(c); + } + } + // Keep only the non-marked EConcat for which we + // have not seen a similar EConcatMarked. + for (auto e: elist) + if (empairs.find(std::make_pair(e[0], e[1])) + == empairs.end()) + v.push_back(e); + // Keep only the non-marked NegClosure for which we + // have not seen a similar NegClosureMarked. + for (auto n: nlist) + if (nmset.find(n[0]) == nmset.end()) + v.push_back(n); + res = formula::And(v); } + break; + case op::Xor: + case op::Implies: + case op::Equiv: + case op::OrRat: + case op::AndRat: + case op::AndNLM: + case op::Star: + case op::FStar: + case op::Concat: + case op::Fusion: + SPOT_UNIMPLEMENTED(); + } - markops_[f] = res; - return res; - } - - formula - mark_tools::simplify_mark(formula f) - { - if (!f.is_marked()) - return f; - - f2f_map::iterator i = simpmark_.find(f); - if (i != simpmark_.end()) - return i->second; - - auto recurse = [this](formula f) - { - return this->simplify_mark(f); - }; - - ltl::formula res; - switch (f.kind()) - { - case op::ff: - case op::tt: - case op::eword: - case op::ap: - case op::Not: - case op::X: - case op::F: - case op::G: - case op::Closure: - case op::NegClosure: - case op::NegClosureMarked: - case op::U: - case op::R: - case op::W: - case op::M: - case op::EConcat: - case op::EConcatMarked: - case op::UConcat: - res = f; - break; - case op::Or: - res = f.map(recurse); - break; - case op::And: - { - std::set> empairs; - std::set nmset; - std::vector elist; - std::vector nlist; - std::vector v; - - for (auto c: f) - { - if (c.is(op::EConcatMarked)) - { - empairs.emplace(c[0], c[1]); - v.push_back(c.map(recurse)); - } - else if (c.is(op::EConcat)) - { - elist.push_back(c); - } - else if (c.is(op::NegClosureMarked)) - { - nmset.insert(c[0]); - v.push_back(c.map(recurse)); - } - else if (c.is(op::NegClosure)) - { - nlist.push_back(c); - } - else - { - v.push_back(c); - } - } - // Keep only the non-marked EConcat for which we - // have not seen a similar EConcatMarked. - for (auto e: elist) - if (empairs.find(std::make_pair(e[0], e[1])) - == empairs.end()) - v.push_back(e); - // Keep only the non-marked NegClosure for which we - // have not seen a similar NegClosureMarked. - for (auto n: nlist) - if (nmset.find(n[0]) == nmset.end()) - v.push_back(n); - res = ltl::formula::And(v); - } - break; - case op::Xor: - case op::Implies: - case op::Equiv: - case op::OrRat: - case op::AndRat: - case op::AndNLM: - case op::Star: - case op::FStar: - case op::Concat: - case op::Fusion: - SPOT_UNIMPLEMENTED(); - } - - simpmark_[f] = res; - return res; - } - + simpmark_[f] = res; + return res; } } diff --git a/src/tl/mark.hh b/src/tl/mark.hh index 3431005d2..04ce0ebf7 100644 --- a/src/tl/mark.hh +++ b/src/tl/mark.hh @@ -24,24 +24,20 @@ namespace spot { - namespace ltl + class mark_tools final { - class mark_tools final - { - public: - /// \ingroup ltl_rewriting - /// \brief Mark operators NegClosure and EConcat. - /// - /// \param f The formula to rewrite. - formula mark_concat_ops(formula f); + public: + /// \ingroup ltl_rewriting + /// \brief Mark operators NegClosure and EConcat. + /// + /// \param f The formula to rewrite. + formula mark_concat_ops(formula f); - formula simplify_mark(formula f); + formula simplify_mark(formula f); - private: - typedef std::unordered_map f2f_map; - f2f_map simpmark_; - f2f_map markops_; - }; - - } + private: + typedef std::unordered_map f2f_map; + f2f_map simpmark_; + f2f_map markops_; + }; } diff --git a/src/tl/mutation.cc b/src/tl/mutation.cc index 42c7b907c..88b76a9c2 100644 --- a/src/tl/mutation.cc +++ b/src/tl/mutation.cc @@ -32,340 +32,337 @@ namespace spot { - namespace ltl + namespace { - namespace + formula substitute_ap(formula f, formula ap_src, formula ap_dst) { - formula substitute_ap(formula f, formula ap_src, formula ap_dst) + return f.map([&](formula f) + { + if (f == ap_src) + return ap_dst; + else + return substitute_ap(f, ap_src, ap_dst); + }); + } + + typedef std::vector vec; + class mutator final + { + int mutation_counter_ = 0; + formula f_; + unsigned opts_; + public: + mutator(formula f, unsigned opts) : f_(f), opts_(opts) { - return f.map([&](formula f) - { - if (f == ap_src) - return ap_dst; - else - return substitute_ap(f, ap_src, ap_dst); - }); } - typedef std::vector vec; - class mutator final + formula mutate(formula f) { - int mutation_counter_ = 0; - formula f_; - unsigned opts_; - public: - mutator(formula f, unsigned opts) : f_(f), opts_(opts) - { - } + auto recurse = [this](formula f) + { + return this->mutate(f); + }; - formula mutate(formula f) - { - auto recurse = [this](formula f) - { - return this->mutate(f); - }; - - switch (f.kind()) - { - case op::ff: - case op::tt: - case op::eword: + switch (f.kind()) + { + case op::ff: + case op::tt: + case op::eword: + return f; + case op::ap: + if (opts_ & Mut_Ap2Const) + { + if (mutation_counter_-- == 0) + return formula::tt(); + if (mutation_counter_-- == 0) + return formula::ff(); + } + return f; + case op::Not: + case op::X: + case op::F: + case op::G: + if ((opts_ & Mut_Remove_Ops) + && mutation_counter_-- == 0) + return f[0]; + // fall through + case op::Closure: + case op::NegClosure: + case op::NegClosureMarked: + if (mutation_counter_ < 0) return f; - case op::ap: - if (opts_ & Mut_Ap2Const) + else + return f.map(recurse); + case op::Or: + case op::OrRat: + case op::And: + case op::AndRat: + case op::AndNLM: + case op::Concat: + case op::Fusion: + { + int mos = f.size(); + if (opts_ & Mut_Remove_Multop_Operands) { - if (mutation_counter_-- == 0) - return formula::tt(); - if (mutation_counter_-- == 0) - return formula::ff(); + for (int i = 0; i < mos; ++i) + if (mutation_counter_-- == 0) + return f.all_but(i); } - return f; - case op::Not: - case op::X: - case op::F: - case op::G: - if ((opts_ & Mut_Remove_Ops) - && mutation_counter_-- == 0) - return f[0]; - // fall through - case op::Closure: - case op::NegClosure: - case op::NegClosureMarked: + + if (opts_ & Mut_Split_Ops && f.is(op::AndNLM)) + { + if (mutation_counter_ >= 0 + && mutation_counter_ < 2 * (mos - 1)) + { + vec v1; + vec v2; + v1.push_back(f[0]); + bool reverse = false; + int i = 1; + while (i < mos) + { + if (mutation_counter_-- == 0) + break; + if (mutation_counter_-- == 0) + { + reverse = true; + break; + } + v1.push_back(f[i++]); + } + for (; i < mos; ++i) + v2.push_back(f[i]); + formula first = AndNLM_(v1); + formula second = AndNLM_(v2); + formula ost = formula::one_star(); + if (!reverse) + return AndRat_(Concat_(first, ost), second); + else + return AndRat_(Concat_(second, ost), first); + } + else + { + mutation_counter_ -= 2 * (mos - 1); + } + } + if (mutation_counter_ < 0) return f; else return f.map(recurse); - case op::Or: - case op::OrRat: - case op::And: - case op::AndRat: - case op::AndNLM: - case op::Concat: - case op::Fusion: - { - int mos = f.size(); - if (opts_ & Mut_Remove_Multop_Operands) - { - for (int i = 0; i < mos; ++i) - if (mutation_counter_-- == 0) - return f.all_but(i); - } - - if (opts_ & Mut_Split_Ops && f.is(op::AndNLM)) - { - if (mutation_counter_ >= 0 - && mutation_counter_ < 2 * (mos - 1)) - { - vec v1; - vec v2; - v1.push_back(f[0]); - bool reverse = false; - int i = 1; - while (i < mos) - { - if (mutation_counter_-- == 0) - break; - if (mutation_counter_-- == 0) - { - reverse = true; - break; - } - v1.push_back(f[i++]); - } - for (; i < mos; ++i) - v2.push_back(f[i]); - formula first = AndNLM_(v1); - formula second = AndNLM_(v2); - formula ost = formula::one_star(); - if (!reverse) - return AndRat_(Concat_(first, ost), second); - else - return AndRat_(Concat_(second, ost), first); - } - else - { - mutation_counter_ -= 2 * (mos - 1); - } - } - - if (mutation_counter_ < 0) - return f; - else - return f.map(recurse); - } - case op::Xor: - case op::Implies: - case op::Equiv: - case op::U: - case op::R: - case op::W: - case op::M: - case op::EConcat: - case op::EConcatMarked: - case op::UConcat: - { - formula first = f[0]; - formula second = f[1]; - op o = f.kind(); - bool left_is_sere = o == op::EConcat - || o == op::EConcatMarked - || o == op::UConcat; - - if (opts_ & Mut_Remove_Ops && mutation_counter_-- == 0) - { - if (!left_is_sere) - return first; - else if (o == op::UConcat) - return formula::NegClosure(first); - else // EConcat or EConcatMarked - return formula::Closure(first); - } - if (opts_ & Mut_Remove_Ops && mutation_counter_-- == 0) - return second; - if (opts_ & Mut_Rewrite_Ops) - { - switch (o) - { - case op::U: - if (mutation_counter_-- == 0) - return formula::W(first, second); - break; - case op::M: - if (mutation_counter_-- == 0) - return formula::R(first, second); - if (mutation_counter_-- == 0) - return formula::U(second, first); - break; - case op::R: - if (mutation_counter_-- == 0) - return formula::W(second, first); - break; - default: - break; - } - } - if (opts_ & Mut_Split_Ops) - { - switch (o) - { - case op::Equiv: - if (mutation_counter_-- == 0) - return formula::Implies(first, second); - if (mutation_counter_-- == 0) - return formula::Implies(second, first); - if (mutation_counter_-- == 0) - return formula::And({first, second}); - if (mutation_counter_-- == 0) - { - // Negate the two argument sequentially (in this - // case right before left, otherwise different - // compilers will make different choices. - auto right = formula::Not(second); - return formula::And({formula::Not(first), right}); - } - break; - case op::Xor: - if (mutation_counter_-- == 0) - return formula::And({first, formula::Not(second)}); - if (mutation_counter_-- == 0) - return formula::And({formula::Not(first), second}); - break; - default: - break; - } - } - if (mutation_counter_ < 0) - return f; - else - return f.map(recurse); - } - case op::Star: - case op::FStar: - { - formula c = f[0]; - op o = f.kind(); - if (opts_ & Mut_Remove_Ops && mutation_counter_-- == 0) - return c; - if (opts_ & Mut_Simplify_Bounds) - { - auto min = f.min(); - auto max = f.max(); - if (min > 0) - { - if (mutation_counter_-- == 0) - return formula::bunop(o, c, min - 1, max); - if (mutation_counter_-- == 0) - return formula::bunop(o, c, 0, max); - } - if (max != formula::unbounded()) - { - if (max > min && mutation_counter_-- == 0) - return formula::bunop(o, c, min, max - 1); - if (mutation_counter_-- == 0) - return formula::bunop(o, c, min, - formula::unbounded()); - } - } - if (mutation_counter_ < 0) - return f; - else - return f.map(recurse); - } } - SPOT_UNREACHABLE(); - } + case op::Xor: + case op::Implies: + case op::Equiv: + case op::U: + case op::R: + case op::W: + case op::M: + case op::EConcat: + case op::EConcatMarked: + case op::UConcat: + { + formula first = f[0]; + formula second = f[1]; + op o = f.kind(); + bool left_is_sere = o == op::EConcat + || o == op::EConcatMarked + || o == op::UConcat; - formula - get_mutation(int n) - { - mutation_counter_ = n; - formula mut = mutate(f_); - if (mut == f_) - return nullptr; - return mut; - } - - }; - - bool - formula_length_less_than(formula left, formula right) - { - assert(left != nullptr); - assert(right != nullptr); - if (left == right) - return false; - auto ll = length(left); - auto lr = length(right); - if (ll < lr) - return true; - if (ll > lr) - return false; - return left < right; - } - - typedef std::set fset_t; - - void - single_mutation_rec(formula f, fset_t& mutations, unsigned opts, - unsigned& n, unsigned m) - { - if (m == 0) - { - if (mutations.insert(f).second) - --n; - } - else - { - formula mut; - int i = 0; - mutator mv(f, opts); - while (n > 0 && ((mut = mv.get_mutation(i++)) != nullptr)) - single_mutation_rec(mut, mutations, opts, n, m - 1); - } - } - - void - replace_ap_rec(formula f, fset_t& mutations, unsigned opts, - unsigned& n, unsigned m) - { - if (m == 0) - { - if (mutations.insert(f).second) - --n; - } - else - { - if (!n) - return; - auto aps = - std::unique_ptr(atomic_prop_collect(f)); - for (auto ap1: *aps) - for (auto ap2: *aps) + if (opts_ & Mut_Remove_Ops && mutation_counter_-- == 0) { - if (ap1 == ap2) - continue; - auto mut = substitute_ap(f, ap1, ap2); - replace_ap_rec(mut, mutations, opts, n, m - 1); - if (!n) - return; + if (!left_is_sere) + return first; + else if (o == op::UConcat) + return formula::NegClosure(first); + else // EConcat or EConcatMarked + return formula::Closure(first); } + if (opts_ & Mut_Remove_Ops && mutation_counter_-- == 0) + return second; + if (opts_ & Mut_Rewrite_Ops) + { + switch (o) + { + case op::U: + if (mutation_counter_-- == 0) + return formula::W(first, second); + break; + case op::M: + if (mutation_counter_-- == 0) + return formula::R(first, second); + if (mutation_counter_-- == 0) + return formula::U(second, first); + break; + case op::R: + if (mutation_counter_-- == 0) + return formula::W(second, first); + break; + default: + break; + } + } + if (opts_ & Mut_Split_Ops) + { + switch (o) + { + case op::Equiv: + if (mutation_counter_-- == 0) + return formula::Implies(first, second); + if (mutation_counter_-- == 0) + return formula::Implies(second, first); + if (mutation_counter_-- == 0) + return formula::And({first, second}); + if (mutation_counter_-- == 0) + { + // Negate the two argument sequentially (in this + // case right before left, otherwise different + // compilers will make different choices. + auto right = formula::Not(second); + return formula::And({formula::Not(first), right}); + } + break; + case op::Xor: + if (mutation_counter_-- == 0) + return formula::And({first, formula::Not(second)}); + if (mutation_counter_-- == 0) + return formula::And({formula::Not(first), second}); + break; + default: + break; + } + } + if (mutation_counter_ < 0) + return f; + else + return f.map(recurse); + } + case op::Star: + case op::FStar: + { + formula c = f[0]; + op o = f.kind(); + if (opts_ & Mut_Remove_Ops && mutation_counter_-- == 0) + return c; + if (opts_ & Mut_Simplify_Bounds) + { + auto min = f.min(); + auto max = f.max(); + if (min > 0) + { + if (mutation_counter_-- == 0) + return formula::bunop(o, c, min - 1, max); + if (mutation_counter_-- == 0) + return formula::bunop(o, c, 0, max); + } + if (max != formula::unbounded()) + { + if (max > min && mutation_counter_-- == 0) + return formula::bunop(o, c, min, max - 1); + if (mutation_counter_-- == 0) + return formula::bunop(o, c, min, + formula::unbounded()); + } + } + if (mutation_counter_ < 0) + return f; + else + return f.map(recurse); + } } + SPOT_UNREACHABLE(); } + + formula + get_mutation(int n) + { + mutation_counter_ = n; + formula mut = mutate(f_); + if (mut == f_) + return nullptr; + return mut; + } + + }; + + bool + formula_length_less_than(formula left, formula right) + { + assert(left != nullptr); + assert(right != nullptr); + if (left == right) + return false; + auto ll = length(left); + auto lr = length(right); + if (ll < lr) + return true; + if (ll > lr) + return false; + return left < right; } - std::vector - mutate(formula f, unsigned opts, unsigned max_output, - unsigned mutation_count, bool sort) - { - fset_t mutations; - single_mutation_rec(f, mutations, opts, max_output, mutation_count); - if (opts & Mut_Remove_One_Ap) - replace_ap_rec(f, mutations, opts, max_output, mutation_count); + typedef std::set fset_t; - vec res(mutations.begin(), mutations.end()); - if (sort) - std::sort(res.begin(), res.end(), formula_length_less_than); - return res; + void + single_mutation_rec(formula f, fset_t& mutations, unsigned opts, + unsigned& n, unsigned m) + { + if (m == 0) + { + if (mutations.insert(f).second) + --n; + } + else + { + formula mut; + int i = 0; + mutator mv(f, opts); + while (n > 0 && ((mut = mv.get_mutation(i++)) != nullptr)) + single_mutation_rec(mut, mutations, opts, n, m - 1); + } + } + + void + replace_ap_rec(formula f, fset_t& mutations, unsigned opts, + unsigned& n, unsigned m) + { + if (m == 0) + { + if (mutations.insert(f).second) + --n; + } + else + { + if (!n) + return; + auto aps = + std::unique_ptr(atomic_prop_collect(f)); + for (auto ap1: *aps) + for (auto ap2: *aps) + { + if (ap1 == ap2) + continue; + auto mut = substitute_ap(f, ap1, ap2); + replace_ap_rec(mut, mutations, opts, n, m - 1); + if (!n) + return; + } + } } } + + std::vector + mutate(formula f, unsigned opts, unsigned max_output, + unsigned mutation_count, bool sort) + { + fset_t mutations; + single_mutation_rec(f, mutations, opts, max_output, mutation_count); + if (opts & Mut_Remove_One_Ap) + replace_ap_rec(f, mutations, opts, max_output, mutation_count); + + vec res(mutations.begin(), mutations.end()); + if (sort) + std::sort(res.begin(), res.end(), formula_length_less_than); + return res; + } } diff --git a/src/tl/mutation.hh b/src/tl/mutation.hh index 250ae2bd6..99d23cad6 100644 --- a/src/tl/mutation.hh +++ b/src/tl/mutation.hh @@ -24,25 +24,22 @@ namespace spot { - namespace ltl - { - enum mut_opts - { - Mut_Ap2Const = 1U<<0, - Mut_Simplify_Bounds = 1U<<1, - Mut_Remove_Multop_Operands = 1U<<2, - Mut_Remove_Ops = 1U<<3, - Mut_Split_Ops = 1U<<4, - Mut_Rewrite_Ops = 1U<<5, - Mut_Remove_One_Ap = 1U<<6, - Mut_All = -1U - }; + enum mut_opts + { + Mut_Ap2Const = 1U<<0, + Mut_Simplify_Bounds = 1U<<1, + Mut_Remove_Multop_Operands = 1U<<2, + Mut_Remove_Ops = 1U<<3, + Mut_Split_Ops = 1U<<4, + Mut_Rewrite_Ops = 1U<<5, + Mut_Remove_One_Ap = 1U<<6, + Mut_All = -1U + }; - SPOT_API - std::vector mutate(formula f, - unsigned opts = Mut_All, - unsigned max_output = -1U, - unsigned mutation_count = 1, - bool sort = true); - } + SPOT_API + std::vector mutate(formula f, + unsigned opts = Mut_All, + unsigned max_output = -1U, + unsigned mutation_count = 1, + bool sort = true); } diff --git a/src/tl/nenoform.cc b/src/tl/nenoform.cc index a2163a82b..bd939b49a 100644 --- a/src/tl/nenoform.cc +++ b/src/tl/nenoform.cc @@ -25,17 +25,13 @@ namespace spot { - namespace ltl + formula + negative_normal_form(formula f, bool negated) { - formula - negative_normal_form(formula f, bool negated) - { - if (!negated && f.is_in_nenoform()) - return f; - - ltl_simplifier s; - return s.negative_normal_form(f, negated); - } + if (!negated && f.is_in_nenoform()) + return f; + ltl_simplifier s; + return s.negative_normal_form(f, negated); } } diff --git a/src/tl/nenoform.hh b/src/tl/nenoform.hh index 2eb6ce636..ec27c3be8 100644 --- a/src/tl/nenoform.hh +++ b/src/tl/nenoform.hh @@ -26,24 +26,21 @@ namespace spot { - namespace ltl - { - /// \ingroup ltl_rewriting - /// \brief Build the negative normal form of \a f. - /// - /// All negations of the formula are pushed in front of the - /// atomic propositions. - /// - /// \param f The formula to normalize. - /// \param negated If \c true, return the negative normal form of - /// \c !f - /// - /// Note that this will not remove abbreviated operators. If you - /// want to remove abbreviations, call spot::ltl::unabbreviate - /// first. (Calling this function after - /// spot::ltl::negative_normal_form would likely produce a formula - /// which is not in negative normal form.) - SPOT_API formula - negative_normal_form(formula f, bool negated = false); - } + /// \ingroup ltl_rewriting + /// \brief Build the negative normal form of \a f. + /// + /// All negations of the formula are pushed in front of the + /// atomic propositions. + /// + /// \param f The formula to normalize. + /// \param negated If \c true, return the negative normal form of + /// \c !f + /// + /// Note that this will not remove abbreviated operators. If you + /// want to remove abbreviations, call spot::unabbreviate + /// first. (Calling this function after + /// spot::negative_normal_form would likely produce a formula + /// which is not in negative normal form.) + SPOT_API formula + negative_normal_form(formula f, bool negated = false); } diff --git a/src/tl/print.cc b/src/tl/print.cc index ca9aacacd..5743fc9fa 100644 --- a/src/tl/print.cc +++ b/src/tl/print.cc @@ -31,1114 +31,1110 @@ namespace spot { - namespace ltl + namespace { - namespace + enum keyword { + KFalse = 0, + KTrue = 1, + KEmptyWord = 2, + KXor, + KImplies, + KEquiv, + KU, + KR, + KW, + KM, + KSeq, + KSeqNext, + KSeqMarked, + KSeqMarkedNext, + KTriggers, + KTriggersNext, + KNot, + KX, + KF, + KG, + KOr, + KOrRat, + KAnd, + KAndRat, + KAndNLM, + KConcat, + KFusion, + KOpenSERE, + KCloseSERE, + KCloseBunop, + KStarBunop, + KPlusBunop, + KFStarBunop, + KFPlusBunop, + KEqualBunop, + KGotoBunop, + }; + + const char* spot_kw[] = { + "0", + "1", + "[*0]", + " xor ", + " -> ", + " <-> ", + " U ", + " R ", + " W ", + " M ", + "<>-> ", + "<>=> ", + "<>+> ", + "<>=+> ", + "[]-> ", + "[]=> ", + "!", + "X", + "F", + "G", + " | ", + " | ", + " & ", + " && ", + " & ", + ";", + ":", + "{", + "}", + "]", + "[*", + "[+]", + "[:*", + "[:+]", + "[=", + "[->", + }; + + const char* spin_kw[] = { + "false", // 0 doesn't work from the command line + "true", // 1 doesn't work from the command line + "[*0]", // not supported + " xor ", // rewritten + " -> ", + " <-> ", + " U ", + " V ", + " W ", // rewritten + " M ", // rewritten + "<>-> ", // not supported + "<>=> ", // not supported + "<>+> ", // not supported + "<>=+> ", // not supported + "[]-> ", // not supported + "[]=> ", // not supported + "!", + "X", + "<>", + "[]", + " || ", + " || ", + " && ", + " && ", // not supported + " & ", // not supported + ";", // not supported + ":", // not supported + "{", // not supported + "}", // not supported + "]", // not supported + "[*", // not supported + "[+]", // not supported + "[:*", // not supported + "[:+]", // not supported + "[=", // not supported + "[->", // not supported + }; + + const char* wring_kw[] = { + "FALSE", + "TRUE", + "[*0]", // not supported + " ^ ", + " -> ", + " <-> ", + " U ", + " R ", + " W ", // rewritten + " M ", // rewritten + "<>-> ", // not supported + "<>=> ", // not supported + "<>+> ", // not supported + "<>=+> ", // not supported + "[]-> ", // not supported + "[]=> ", // not supported + "!", + "X", + "F", + "G", + " + ", + " | ", // not supported + " * ", + " && ", // not supported + " & ", // not supported + ";", // not supported + ":", // not supported + "{", // not supported + "}", // not supported + "]", // not supported + "[*", // not supported + "[+]", // not supported + "[:*", // not supported + "[:+]", // not supported + "[=", // not supported + "[->", // not supported + }; + + const char* utf8_kw[] = { + "0", + "1", + "[*0]", + "⊕", + " → ", + " ↔ ", + " U ", + " R ", + " W ", + " M ", + "◇→ ", + "◇⇒ ", + "◇→̃ ", + "◇⇒̃ ", + "□→ ", + "□⇒ ", + "¬", + "○", + "◇", + "□", + "∨", + " | ", + "∧", + " ∩ ", + " & ", + ";", + ":", + "{", + "}", + "]", + "[*", + "[+]", + "[:*", + "[:+]", + "[=", + "[->", + }; + + const char* latex_kw[] = { + "\\ffalse", + "\\ttrue", + "\\eword", + " \\lxor ", + " \\limplies ", + " \\liff ", + " \\U ", + " \\R ", + " \\W ", + " \\M ", + "\\seq ", + "\\seqX ", + "\\seqM ", + "\\seqXM ", + "\\triggers ", + "\\triggersX ", + "\\lnot ", + "\\X ", + "\\F ", + "\\G ", + " \\lor ", + " \\SereOr ", + " \\land ", + " \\SereAnd ", + " \\SereAndNLM ", + " \\SereConcat ", + " \\SereFusion ", + "\\{", + "\\}", + "}", + "\\SereStar{", + "\\SerePlus{}", + "\\SereFStar{", + "\\SereFPlus{}", + "\\SereEqual{", + "\\SereGoto{", + }; + + const char* sclatex_kw[] = { + "\\bot", + "\\top", + "\\varepsilon", + " \\oplus ", + " \\rightarrow ", + " \\leftrightarrow ", + " \\mathbin{\\mathsf{U}} ", + " \\mathbin{\\mathsf{R}} ", + " \\mathbin{\\mathsf{W}} ", + " \\mathbin{\\mathsf{M}} ", + ("\\mathrel{\\Diamond\\kern-1.7pt\\raise.4pt" + "\\hbox{$\\mathord{\\rightarrow}$}} "), + ("\\mathrel{\\Diamond\\kern-1.7pt\\raise.4pt" + "\\hbox{$\\mathord{\\Rightarrow}$}} "), + "\\seqM ", + "\\seqXM ", + ("\\mathrel{\\Box\\kern-1.7pt\\raise.4pt" + "\\hbox{$\\mathord{\\rightarrow}$}} "), + ("\\mathrel{\\Box\\kern-1.7pt\\raise.4pt" + "\\hbox{$\\mathord{\\Rightarrow}$}} "), + "\\lnot ", + "\\mathsf{X} ", + "\\mathsf{F} ", + "\\mathsf{G} ", + " \\lor ", + " \\cup ", + " \\land ", + " \\cap ", + " \\mathbin{\\mathsf{\\&}} ", + " \\mathbin{\\mathsf{;}} ", + " \\mathbin{\\mathsf{:}} ", + "\\{", + "\\}", + "}", + "^{\\star", + "^+", + "^{\\mathsf{:}\\star", + "^{\\mathsf{:}+}", + "^{=", + "^{\\to", + }; + + static bool + is_bare_word(const char* str) { - enum keyword { - KFalse = 0, - KTrue = 1, - KEmptyWord = 2, - KXor, - KImplies, - KEquiv, - KU, - KR, - KW, - KM, - KSeq, - KSeqNext, - KSeqMarked, - KSeqMarkedNext, - KTriggers, - KTriggersNext, - KNot, - KX, - KF, - KG, - KOr, - KOrRat, - KAnd, - KAndRat, - KAndNLM, - KConcat, - KFusion, - KOpenSERE, - KCloseSERE, - KCloseBunop, - KStarBunop, - KPlusBunop, - KFStarBunop, - KFPlusBunop, - KEqualBunop, - KGotoBunop, - }; - - const char* spot_kw[] = { - "0", - "1", - "[*0]", - " xor ", - " -> ", - " <-> ", - " U ", - " R ", - " W ", - " M ", - "<>-> ", - "<>=> ", - "<>+> ", - "<>=+> ", - "[]-> ", - "[]=> ", - "!", - "X", - "F", - "G", - " | ", - " | ", - " & ", - " && ", - " & ", - ";", - ":", - "{", - "}", - "]", - "[*", - "[+]", - "[:*", - "[:+]", - "[=", - "[->", - }; - - const char* spin_kw[] = { - "false", // 0 doesn't work from the command line - "true", // 1 doesn't work from the command line - "[*0]", // not supported - " xor ", // rewritten - " -> ", - " <-> ", - " U ", - " V ", - " W ", // rewritten - " M ", // rewritten - "<>-> ", // not supported - "<>=> ", // not supported - "<>+> ", // not supported - "<>=+> ", // not supported - "[]-> ", // not supported - "[]=> ", // not supported - "!", - "X", - "<>", - "[]", - " || ", - " || ", - " && ", - " && ", // not supported - " & ", // not supported - ";", // not supported - ":", // not supported - "{", // not supported - "}", // not supported - "]", // not supported - "[*", // not supported - "[+]", // not supported - "[:*", // not supported - "[:+]", // not supported - "[=", // not supported - "[->", // not supported - }; - - const char* wring_kw[] = { - "FALSE", - "TRUE", - "[*0]", // not supported - " ^ ", - " -> ", - " <-> ", - " U ", - " R ", - " W ", // rewritten - " M ", // rewritten - "<>-> ", // not supported - "<>=> ", // not supported - "<>+> ", // not supported - "<>=+> ", // not supported - "[]-> ", // not supported - "[]=> ", // not supported - "!", - "X", - "F", - "G", - " + ", - " | ", // not supported - " * ", - " && ", // not supported - " & ", // not supported - ";", // not supported - ":", // not supported - "{", // not supported - "}", // not supported - "]", // not supported - "[*", // not supported - "[+]", // not supported - "[:*", // not supported - "[:+]", // not supported - "[=", // not supported - "[->", // not supported - }; - - const char* utf8_kw[] = { - "0", - "1", - "[*0]", - "⊕", - " → ", - " ↔ ", - " U ", - " R ", - " W ", - " M ", - "◇→ ", - "◇⇒ ", - "◇→̃ ", - "◇⇒̃ ", - "□→ ", - "□⇒ ", - "¬", - "○", - "◇", - "□", - "∨", - " | ", - "∧", - " ∩ ", - " & ", - ";", - ":", - "{", - "}", - "]", - "[*", - "[+]", - "[:*", - "[:+]", - "[=", - "[->", - }; - - const char* latex_kw[] = { - "\\ffalse", - "\\ttrue", - "\\eword", - " \\lxor ", - " \\limplies ", - " \\liff ", - " \\U ", - " \\R ", - " \\W ", - " \\M ", - "\\seq ", - "\\seqX ", - "\\seqM ", - "\\seqXM ", - "\\triggers ", - "\\triggersX ", - "\\lnot ", - "\\X ", - "\\F ", - "\\G ", - " \\lor ", - " \\SereOr ", - " \\land ", - " \\SereAnd ", - " \\SereAndNLM ", - " \\SereConcat ", - " \\SereFusion ", - "\\{", - "\\}", - "}", - "\\SereStar{", - "\\SerePlus{}", - "\\SereFStar{", - "\\SereFPlus{}", - "\\SereEqual{", - "\\SereGoto{", - }; - - const char* sclatex_kw[] = { - "\\bot", - "\\top", - "\\varepsilon", - " \\oplus ", - " \\rightarrow ", - " \\leftrightarrow ", - " \\mathbin{\\mathsf{U}} ", - " \\mathbin{\\mathsf{R}} ", - " \\mathbin{\\mathsf{W}} ", - " \\mathbin{\\mathsf{M}} ", - ("\\mathrel{\\Diamond\\kern-1.7pt\\raise.4pt" - "\\hbox{$\\mathord{\\rightarrow}$}} "), - ("\\mathrel{\\Diamond\\kern-1.7pt\\raise.4pt" - "\\hbox{$\\mathord{\\Rightarrow}$}} "), - "\\seqM ", - "\\seqXM ", - ("\\mathrel{\\Box\\kern-1.7pt\\raise.4pt" - "\\hbox{$\\mathord{\\rightarrow}$}} "), - ("\\mathrel{\\Box\\kern-1.7pt\\raise.4pt" - "\\hbox{$\\mathord{\\Rightarrow}$}} "), - "\\lnot ", - "\\mathsf{X} ", - "\\mathsf{F} ", - "\\mathsf{G} ", - " \\lor ", - " \\cup ", - " \\land ", - " \\cap ", - " \\mathbin{\\mathsf{\\&}} ", - " \\mathbin{\\mathsf{;}} ", - " \\mathbin{\\mathsf{:}} ", - "\\{", - "\\}", - "}", - "^{\\star", - "^+", - "^{\\mathsf{:}\\star", - "^{\\mathsf{:}+}", - "^{=", - "^{\\to", - }; - - static bool - is_bare_word(const char* str) - { - // Bare words cannot be empty, start with the letter of a - // unary operator, or be the name of an existing constant or - // operator. Also they should start with an letter. - if (!*str - || *str == 'F' - || *str == 'G' - || *str == 'X' - || !(isalpha(*str) || *str == '_' || *str == '.') - || ((*str == 'U' || *str == 'W' || *str == 'M' || *str == 'R') - && str[1] == 0) - || !strcasecmp(str, "true") - || !strcasecmp(str, "false")) + // Bare words cannot be empty, start with the letter of a + // unary operator, or be the name of an existing constant or + // operator. Also they should start with an letter. + if (!*str + || *str == 'F' + || *str == 'G' + || *str == 'X' + || !(isalpha(*str) || *str == '_' || *str == '.') + || ((*str == 'U' || *str == 'W' || *str == 'M' || *str == 'R') + && str[1] == 0) + || !strcasecmp(str, "true") + || !strcasecmp(str, "false")) + return false; + // The remaining of the word must be alphanumeric. + while (*++str) + if (!(isalnum(*str) || *str == '_' || *str == '.')) return false; - // The remaining of the word must be alphanumeric. - while (*++str) - if (!(isalnum(*str) || *str == '_' || *str == '.')) - return false; - return true; - } + return true; + } - // If the formula has the form (!b)[*], return b. - static formula - strip_star_not(formula f) - { - return f.get_child_of({op::Star, op::Not}); - } + // If the formula has the form (!b)[*], return b. + static formula + strip_star_not(formula f) + { + return f.get_child_of({op::Star, op::Not}); + } - // If the formula at position i in mo has the form - // (!b)[*];b with b being a Boolean formula, return b. - static formula - match_goto(formula mo, unsigned i) - { - assert(i + 1 < mo.size()); - formula b = strip_star_not(mo[i]); - if (b == nullptr || !b.is_boolean()) - return nullptr; - if (mo[i + 1] == b) - return b; + // If the formula at position i in mo has the form + // (!b)[*];b with b being a Boolean formula, return b. + static formula + match_goto(formula mo, unsigned i) + { + assert(i + 1 < mo.size()); + formula b = strip_star_not(mo[i]); + if (b == nullptr || !b.is_boolean()) return nullptr; + if (mo[i + 1] == b) + return b; + return nullptr; + } + + class to_string_visitor final + { + public: + to_string_visitor(std::ostream& os, + bool full_parent = false, + bool ratexp = false, + const char** kw = spot_kw) + : os_(os), top_level_(true), + full_parent_(full_parent), in_ratexp_(ratexp), + kw_(kw) + { + } + + void + openp() const + { + if (in_ratexp_) + emit(KOpenSERE); + else + os_ << '('; } - class to_string_visitor final - { - public: - to_string_visitor(std::ostream& os, - bool full_parent = false, - bool ratexp = false, - const char** kw = spot_kw) - : os_(os), top_level_(true), - full_parent_(full_parent), in_ratexp_(ratexp), - kw_(kw) - { - } - - void - openp() const - { - if (in_ratexp_) - emit(KOpenSERE); - else - os_ << '('; - } - - void + void closep() const - { - if (in_ratexp_) - emit(KCloseSERE); - else - os_ << ')'; - } - - std::ostream& - emit(int symbol) const - { - return os_ << kw_[symbol]; - } - - void - visit(formula f) - { - bool top_level = top_level_; - top_level_ = false; - - auto s = f.size(); - bool want_par = (full_parent_ || s > 1) && !top_level; - if (want_par) - openp(); - - auto emit_bunop_child = [this](formula b) - { - // b[*] is OK, no need to print {b}[*]. However we want - // braces for {!b}[*], the only unary operator that can - // be nested with Star/FStar. - // If full_parent_ is set, we do NOT emit those extra - // braces, because they are already output for the node - // below. - bool need_parent = (!full_parent_ && b.is(op::Not)); - if (need_parent) - openp(); - this->visit(b); - if (need_parent) - closep(); - }; - - op o = f.kind(); - switch (o) - { - case op::ff: - emit(KFalse); - break; - case op::tt: - emit(KTrue); - break; - case op::eword: - emit(KEmptyWord); - break; - case op::ap: - { - const std::string& str = f.ap_name(); - if (!is_bare_word(str.c_str())) - { - // Spin 6 supports atomic propositions such as (a == 0) - // as long as they are enclosed in parentheses. - if (kw_ == sclatex_kw || kw_ == latex_kw) - escape_latex(os_ << "``\\mathit{", str) - << "}\\textrm{''}"; - else if (kw_ != spin_kw) - escape_str(os_ << '"', str) << '"'; - else if (!full_parent_) - os_ << '(' << str << ')'; - else - os_ << str; - } - else - { - if (kw_ == latex_kw || kw_ == sclatex_kw) - { - size_t s = str.size(); - while (str[s - 1] >= '0' && str[s - 1] <= '9') - { - --s; - // bare words cannot start with digits - assert(s != 0); - } - if (s > 1) - os_ << "\\mathit{"; - escape_latex(os_, str.substr(0, s)); - if (s > 1) - os_ << '}'; - if (s != str.size()) - os_ << "_{" - << str.substr(s) - << '}'; - } - else - { - os_ << str; - } - } - if (kw_ == wring_kw) - os_ << "=1"; - - } - break; - case op::Not: - { - formula c = f[0]; - if (c.is(op::ap)) - { - // If we negate a single letter in UTF-8, use a - // combining overline. - if (!full_parent_ && kw_ == utf8_kw) - { - auto& name = c.ap_name(); - if (name.size() == 1 && is_bare_word(name.c_str())) - { - os_ << name << "̅"; - break; - } - } - // If we negate an atomic proposition for Wring, - // output prop=0. - if (kw_ == wring_kw) - { - auto& name = c.ap_name(); - if (is_bare_word(name.c_str())) - { - os_ << name << "=0"; - break; - } - } - } - emit(KNot); - visit(c); - break; - } - case op::X: - emit(KX); - visit(f[0]); - break; - case op::F: - emit(KF); - visit(f[0]); - break; - case op::G: - emit(KG); - visit(f[0]); - break; - case op::NegClosure: - case op::NegClosureMarked: - emit(KNot); - if (o == op::NegClosureMarked) - os_ << (kw_ == utf8_kw ? "̃": "+"); - // Fall through - case op::Closure: - os_ << '{'; - in_ratexp_ = true; - top_level_ = true; - visit(f[0]); - os_ << '}'; - in_ratexp_ = false; - top_level_ = false; - break; - case op::Xor: - visit(f[0]); - emit(KXor); - visit(f[1]); - break; - case op::Implies: - visit(f[0]); - emit(KImplies); - visit(f[1]); - break; - case op::Equiv: - visit(f[0]); - emit(KEquiv); - visit(f[1]); - break; - case op::U: - visit(f[0]); - emit(KU); - visit(f[1]); - break; - case op::R: - visit(f[0]); - emit(KR); - visit(f[1]); - break; - case op::W: - visit(f[0]); - emit(KW); - visit(f[1]); - break; - case op::M: - visit(f[0]); - emit(KM); - visit(f[1]); - break; - case op::EConcat: - case op::EConcatMarked: - case op::UConcat: - { - in_ratexp_ = true; - openp(); - top_level_ = true; - formula left = f[0]; - formula right = f[1]; - unsigned last = left.size() - 1; - bool onelast = false; - if (left.is(op::Concat) && left[last].is_tt()) - { - visit(left.all_but(last)); - onelast = true; - } - else - { - visit(left); - } - top_level_ = false; - closep(); - in_ratexp_ = false; - if (o == op::UConcat) - { - emit(onelast ? KTriggersNext : KTriggers); - visit(right); - } - else if (o == op::EConcatMarked) - { - emit(onelast ? KSeqMarkedNext : KSeqMarked); - visit(right); - } - else if (o == op::EConcat) - { - if (f[1].is_tt()) - { - os_ << '!'; - // No recursion on right. - } - else - { - emit(onelast ? KSeqNext : KSeq); - visit(right); - } - } - else - { - SPOT_UNREACHABLE(); - } - } - break; - case op::Or: - case op::OrRat: - case op::And: - case op::AndRat: - case op::AndNLM: - case op::Fusion: - { - visit(f[0]); - keyword k = KFalse; // Initialize to something to please GCC. - switch (o) - { - case op::Or: - k = KOr; - break; - case op::OrRat: - k = KOrRat; - break; - case op::And: - k = in_ratexp_ ? KAndRat : KAnd; - break; - case op::AndRat: - k = KAndRat; - break; - case op::AndNLM: - k = KAndNLM; - break; - case op::Fusion: - k = KFusion; - break; - default: - SPOT_UNREACHABLE(); - } - assert(k != KFalse); - - unsigned max = f.size(); - for (unsigned n = 1; n < max; ++n) - { - emit(k); - visit(f[n]); - } - break; - } - case op::Concat: - { - unsigned max = f.size(); - - for (unsigned i = 0; i < max; ++i) - { - if (i > 0) - emit(KConcat); - if (i + 1 < max) - { - // Try to match (!b)[*];b - formula b = match_goto(f, i); - if (b != nullptr) - { - emit_bunop_child(b); - - // Wait... maybe we are looking at (!b)[*];b;(!b)[*] - // in which case it's b[=1]. - if (i + 2 < max && f[i] == f[i + 2]) - { - emit(KEqualBunop); - os_ << '1'; - emit(KCloseBunop); - i += 2; - } - else - { - emit(KGotoBunop); - emit(KCloseBunop); - ++i; - } - continue; - } - // Try to match ((!b)[*];b)[*i..j];(!b)[*] - formula fi = f[i]; - if (fi.is(op::Star)) - { - if (formula b2 = strip_star_not(f[i + 1])) - { - formula fic = fi[0]; - if (fic.is(op::Concat)) - if (formula b1 = match_goto(fic, 0)) - if (b1 == b2) - { - emit_bunop_child(b1); - emit(KEqualBunop); - unsigned min = fi.min(); - os_ << min; - unsigned max = fi.max(); - if (max != min) - { - os_ << ".."; - if (max != formula::unbounded()) - os_ << max; - } - emit(KCloseBunop); - ++i; - continue; - } - } - } - } - visit(f[i]); - } - break; - } - case op::Star: - case op::FStar: - { - formula c = f[0]; - enum { Star, FStar, Goto } sugar = Star; - unsigned default_min = 0; - unsigned default_max = formula::unbounded(); - - // Abbreviate "1[*]" as "[*]". - if (!c.is_tt() || o != op::Star) - { - if (o == op::Star) - { - // Is this a Goto? - if (c.is(op::Concat)) - { - unsigned s = c.size(); - if (s == 2) - if (formula b = match_goto(c, 0)) - { - c = b; - sugar = Goto; - } - } - } - else if (o == op::FStar) - { - sugar = FStar; - } - else - { - SPOT_UNREACHABLE(); - } - emit_bunop_child(c); - } - - unsigned min = f.min(); - unsigned max = f.max(); - bool range = true; - switch (sugar) - { - case Star: - if (min == 1 && max == formula::unbounded()) - { - range = false; - emit(KPlusBunop); - } - else - { - emit(KStarBunop); - } - break; - case FStar: - if (min == 1 && max == formula::unbounded()) - { - range = false; - emit(KFPlusBunop); - } - else - { - emit(KFStarBunop); - } - break; - case Goto: - emit(KGotoBunop); - default_min = 1; - default_max = 1; - break; - } - - // Beware that the default parameters of the Goto operator are - // not the same as Star or Equal: - // - // [->] = [->1..1] - // [->..] = [->1..unbounded] - // [*] = [*0..unbounded] - // [*..] = [*0..unbounded] - // [=] = [=0..unbounded] - // [=..] = [=0..unbounded] - // - // Strictly speaking [=] is not specified by PSL, and anyway we - // automatically rewrite Exp[=0..unbounded] as - // Exp[*0..unbounded], so we should never have to print [=] - // here. - // - // Also - // [*..] = [*0..unbounded] - - if (range) - { - if (min != default_min || max != default_max) - { - // Always print the min_, even when it is equal to - // default_min, this way we avoid ambiguities (like - // when reading [*..3] near [->..2]) - os_ << min; - if (min != max) - { - os_ << ".."; - if (max != formula::unbounded()) - os_ << max; - } - } - emit(KCloseBunop); - } - } - break; - } - if (want_par) - closep(); - } - - protected: - std::ostream& os_; - bool top_level_; - bool full_parent_; - bool in_ratexp_; - const char** kw_; - }; - + { + if (in_ratexp_) + emit(KCloseSERE); + else + os_ << ')'; + } std::ostream& - printer_(std::ostream& os, formula f, bool full_parent, - bool ratexp, const char** kw) + emit(int symbol) const { - to_string_visitor v(os, full_parent, ratexp, kw); - v.visit(f); - return os; + return os_ << kw_[symbol]; } - std::string - str_(formula f, bool full_parent, bool ratexp, const char** kw) - { - std::ostringstream os; - printer_(os, f, full_parent, ratexp, kw); - return os.str(); - } - - } // anonymous - - std::ostream& - print_psl(std::ostream& os, formula f, bool full_parent) - { - return printer_(os, f, full_parent, false, spot_kw); - } - - std::string - str_psl(formula f, bool full_parent) - { - return str_(f, full_parent, false, spot_kw); - } - - std::ostream& - print_sere(std::ostream& os, formula f, bool full_parent) - { - return printer_(os, f, full_parent, true, spot_kw); - } - - std::string - str_sere(formula f, bool full_parent) - { - return str_(f, full_parent, false, spot_kw); - } - - - std::ostream& - print_utf8_psl(std::ostream& os, formula f, bool full_parent) - { - return printer_(os, f, full_parent, false, utf8_kw); - } - - std::string - str_utf8_psl(formula f, bool full_parent) - { - return str_(f, full_parent, false, utf8_kw); - } - - std::ostream& - print_utf8_sere(std::ostream& os, formula f, bool full_parent) - { - return printer_(os, f, full_parent, true, utf8_kw); - } - - std::string - str_utf8_sere(formula f, bool full_parent) - { - return str_(f, full_parent, false, utf8_kw); - } - - - std::ostream& - print_spin_ltl(std::ostream& os, formula f, bool full_parent) - { - to_string_visitor v(os, full_parent, false, spin_kw); - v.visit(unabbreviate(f, "^MW")); - return os; - } - - std::string - str_spin_ltl(formula f, bool full_parent) - { - std::ostringstream os; - print_spin_ltl(os, f, full_parent); - return os.str(); - } - - std::ostream& - print_wring_ltl(std::ostream& os, formula f) - { - to_string_visitor v(os, true, false, wring_kw); - v.visit(unabbreviate(f, "MW")); - return os; - } - - std::string - str_wring_ltl(formula f) - { - std::ostringstream os; - print_wring_ltl(os, f); - return os.str(); - } - - std::ostream& - print_latex_psl(std::ostream& os, formula f, bool full_parent) - { - return printer_(os, f, full_parent, false, latex_kw); - } - - std::string - str_latex_psl(formula f, bool full_parent) - { - return str_(f, full_parent, false, latex_kw); - } - - std::ostream& - print_latex_sere(std::ostream& os, formula f, bool full_parent) - { - return printer_(os, f, full_parent, true, latex_kw); - } - - std::string - str_latex_sere(formula f, bool full_parent) - { - return str_(f, full_parent, true, latex_kw); - } - - - std::ostream& - print_sclatex_psl(std::ostream& os, formula f, bool full_parent) - { - return printer_(os, f, full_parent, false, sclatex_kw); - } - - std::string - str_sclatex_psl(formula f, bool full_parent) - { - return str_(f, full_parent, false, sclatex_kw); - } - - std::ostream& - print_sclatex_sere(std::ostream& os, formula f, bool full_parent) - { - return printer_(os, f, full_parent, true, sclatex_kw); - } - - std::string - str_sclatex_sere(formula f, bool full_parent) - { - return str_(f, full_parent, true, sclatex_kw); - } - - namespace - { - // Does str match p[0-9]+ ? - static bool - is_pnum(const char* str) - { - if (str[0] != 'p' || str[1] == 0) - return false; - while (*++str) - if (*str < '0' || *str > '9') - return false; - return true; - } - - class lbt_visitor final - { - protected: - std::ostream& os_; - bool first_; - public: - - lbt_visitor(std::ostream& os) - : os_(os), first_(true) - { - } - - void + void visit(formula f) - { - if (first_) - first_ = false; - else - os_ << ' '; + { + bool top_level = top_level_; + top_level_ = false; - op o = f.kind(); - switch (o) + auto s = f.size(); + bool want_par = (full_parent_ || s > 1) && !top_level; + if (want_par) + openp(); + + auto emit_bunop_child = [this](formula b) + { + // b[*] is OK, no need to print {b}[*]. However we want + // braces for {!b}[*], the only unary operator that can + // be nested with Star/FStar. + // If full_parent_ is set, we do NOT emit those extra + // braces, because they are already output for the node + // below. + bool need_parent = (!full_parent_ && b.is(op::Not)); + if (need_parent) + openp(); + this->visit(b); + if (need_parent) + closep(); + }; + + op o = f.kind(); + switch (o) + { + case op::ff: + emit(KFalse); + break; + case op::tt: + emit(KTrue); + break; + case op::eword: + emit(KEmptyWord); + break; + case op::ap: { - case op::ff: - os_ << 'f'; - break; - case op::tt: - os_ << 't'; - break; - case op::ap: - { - const std::string& str = f.ap_name(); - if (!is_pnum(str.c_str())) - escape_str(os_ << '"', str) << '"'; - else - os_ << str; - break; - } - case op::Not: - os_ << '!'; - break; - case op::X: - os_ << 'X'; - break; - case op::F: - os_ << 'F'; - break; - case op::G: - os_ << 'G'; - break; - case op::Xor: - os_ << '^'; - break; - case op::Implies: - os_ << 'i'; - break; - case op::Equiv: - os_ << 'e'; - break; - case op::U: - os_ << 'U'; - break; - case op::R: - os_ << 'V'; - break; - case op::W: - os_ << 'W'; - break; - case op::M: - os_ << 'M'; - break; - case op::Or: - for (unsigned i = f.size() - 1; i != 0; --i) - os_ << "| "; - first_ = true; - break; - case op::And: - for (unsigned i = f.size() - 1; i != 0; --i) - os_ << "& "; - first_ = true; - break; - case op::eword: - case op::Closure: - case op::NegClosure: - case op::NegClosureMarked: - case op::EConcat: - case op::EConcatMarked: - case op::UConcat: - case op::OrRat: - case op::AndRat: - case op::AndNLM: - case op::Concat: - case op::Fusion: - case op::Star: - case op::FStar: - SPOT_UNIMPLEMENTED(); - } - for (auto c: f) - visit(c); - } - }; + const std::string& str = f.ap_name(); + if (!is_bare_word(str.c_str())) + { + // Spin 6 supports atomic propositions such as (a == 0) + // as long as they are enclosed in parentheses. + if (kw_ == sclatex_kw || kw_ == latex_kw) + escape_latex(os_ << "``\\mathit{", str) + << "}\\textrm{''}"; + else if (kw_ != spin_kw) + escape_str(os_ << '"', str) << '"'; + else if (!full_parent_) + os_ << '(' << str << ')'; + else + os_ << str; + } + else + { + if (kw_ == latex_kw || kw_ == sclatex_kw) + { + size_t s = str.size(); + while (str[s - 1] >= '0' && str[s - 1] <= '9') + { + --s; + // bare words cannot start with digits + assert(s != 0); + } + if (s > 1) + os_ << "\\mathit{"; + escape_latex(os_, str.substr(0, s)); + if (s > 1) + os_ << '}'; + if (s != str.size()) + os_ << "_{" + << str.substr(s) + << '}'; + } + else + { + os_ << str; + } + } + if (kw_ == wring_kw) + os_ << "=1"; + + } + break; + case op::Not: + { + formula c = f[0]; + if (c.is(op::ap)) + { + // If we negate a single letter in UTF-8, use a + // combining overline. + if (!full_parent_ && kw_ == utf8_kw) + { + auto& name = c.ap_name(); + if (name.size() == 1 && is_bare_word(name.c_str())) + { + os_ << name << "̅"; + break; + } + } + // If we negate an atomic proposition for Wring, + // output prop=0. + if (kw_ == wring_kw) + { + auto& name = c.ap_name(); + if (is_bare_word(name.c_str())) + { + os_ << name << "=0"; + break; + } + } + } + emit(KNot); + visit(c); + break; + } + case op::X: + emit(KX); + visit(f[0]); + break; + case op::F: + emit(KF); + visit(f[0]); + break; + case op::G: + emit(KG); + visit(f[0]); + break; + case op::NegClosure: + case op::NegClosureMarked: + emit(KNot); + if (o == op::NegClosureMarked) + os_ << (kw_ == utf8_kw ? "̃": "+"); + // Fall through + case op::Closure: + os_ << '{'; + in_ratexp_ = true; + top_level_ = true; + visit(f[0]); + os_ << '}'; + in_ratexp_ = false; + top_level_ = false; + break; + case op::Xor: + visit(f[0]); + emit(KXor); + visit(f[1]); + break; + case op::Implies: + visit(f[0]); + emit(KImplies); + visit(f[1]); + break; + case op::Equiv: + visit(f[0]); + emit(KEquiv); + visit(f[1]); + break; + case op::U: + visit(f[0]); + emit(KU); + visit(f[1]); + break; + case op::R: + visit(f[0]); + emit(KR); + visit(f[1]); + break; + case op::W: + visit(f[0]); + emit(KW); + visit(f[1]); + break; + case op::M: + visit(f[0]); + emit(KM); + visit(f[1]); + break; + case op::EConcat: + case op::EConcatMarked: + case op::UConcat: + { + in_ratexp_ = true; + openp(); + top_level_ = true; + formula left = f[0]; + formula right = f[1]; + unsigned last = left.size() - 1; + bool onelast = false; + if (left.is(op::Concat) && left[last].is_tt()) + { + visit(left.all_but(last)); + onelast = true; + } + else + { + visit(left); + } + top_level_ = false; + closep(); + in_ratexp_ = false; + if (o == op::UConcat) + { + emit(onelast ? KTriggersNext : KTriggers); + visit(right); + } + else if (o == op::EConcatMarked) + { + emit(onelast ? KSeqMarkedNext : KSeqMarked); + visit(right); + } + else if (o == op::EConcat) + { + if (f[1].is_tt()) + { + os_ << '!'; + // No recursion on right. + } + else + { + emit(onelast ? KSeqNext : KSeq); + visit(right); + } + } + else + { + SPOT_UNREACHABLE(); + } + } + break; + case op::Or: + case op::OrRat: + case op::And: + case op::AndRat: + case op::AndNLM: + case op::Fusion: + { + visit(f[0]); + keyword k = KFalse; // Initialize to something to please GCC. + switch (o) + { + case op::Or: + k = KOr; + break; + case op::OrRat: + k = KOrRat; + break; + case op::And: + k = in_ratexp_ ? KAndRat : KAnd; + break; + case op::AndRat: + k = KAndRat; + break; + case op::AndNLM: + k = KAndNLM; + break; + case op::Fusion: + k = KFusion; + break; + default: + SPOT_UNREACHABLE(); + } + assert(k != KFalse); + + unsigned max = f.size(); + for (unsigned n = 1; n < max; ++n) + { + emit(k); + visit(f[n]); + } + break; + } + case op::Concat: + { + unsigned max = f.size(); + + for (unsigned i = 0; i < max; ++i) + { + if (i > 0) + emit(KConcat); + if (i + 1 < max) + { + // Try to match (!b)[*];b + formula b = match_goto(f, i); + if (b != nullptr) + { + emit_bunop_child(b); + + // Wait... maybe we are looking at (!b)[*];b;(!b)[*] + // in which case it's b[=1]. + if (i + 2 < max && f[i] == f[i + 2]) + { + emit(KEqualBunop); + os_ << '1'; + emit(KCloseBunop); + i += 2; + } + else + { + emit(KGotoBunop); + emit(KCloseBunop); + ++i; + } + continue; + } + // Try to match ((!b)[*];b)[*i..j];(!b)[*] + formula fi = f[i]; + if (fi.is(op::Star)) + { + if (formula b2 = strip_star_not(f[i + 1])) + { + formula fic = fi[0]; + if (fic.is(op::Concat)) + if (formula b1 = match_goto(fic, 0)) + if (b1 == b2) + { + emit_bunop_child(b1); + emit(KEqualBunop); + unsigned min = fi.min(); + os_ << min; + unsigned max = fi.max(); + if (max != min) + { + os_ << ".."; + if (max != formula::unbounded()) + os_ << max; + } + emit(KCloseBunop); + ++i; + continue; + } + } + } + } + visit(f[i]); + } + break; + } + case op::Star: + case op::FStar: + { + formula c = f[0]; + enum { Star, FStar, Goto } sugar = Star; + unsigned default_min = 0; + unsigned default_max = formula::unbounded(); + + // Abbreviate "1[*]" as "[*]". + if (!c.is_tt() || o != op::Star) + { + if (o == op::Star) + { + // Is this a Goto? + if (c.is(op::Concat)) + { + unsigned s = c.size(); + if (s == 2) + if (formula b = match_goto(c, 0)) + { + c = b; + sugar = Goto; + } + } + } + else if (o == op::FStar) + { + sugar = FStar; + } + else + { + SPOT_UNREACHABLE(); + } + emit_bunop_child(c); + } + + unsigned min = f.min(); + unsigned max = f.max(); + bool range = true; + switch (sugar) + { + case Star: + if (min == 1 && max == formula::unbounded()) + { + range = false; + emit(KPlusBunop); + } + else + { + emit(KStarBunop); + } + break; + case FStar: + if (min == 1 && max == formula::unbounded()) + { + range = false; + emit(KFPlusBunop); + } + else + { + emit(KFStarBunop); + } + break; + case Goto: + emit(KGotoBunop); + default_min = 1; + default_max = 1; + break; + } + + // Beware that the default parameters of the Goto operator are + // not the same as Star or Equal: + // + // [->] = [->1..1] + // [->..] = [->1..unbounded] + // [*] = [*0..unbounded] + // [*..] = [*0..unbounded] + // [=] = [=0..unbounded] + // [=..] = [=0..unbounded] + // + // Strictly speaking [=] is not specified by PSL, and anyway we + // automatically rewrite Exp[=0..unbounded] as + // Exp[*0..unbounded], so we should never have to print [=] + // here. + // + // Also + // [*..] = [*0..unbounded] + + if (range) + { + if (min != default_min || max != default_max) + { + // Always print the min_, even when it is equal to + // default_min, this way we avoid ambiguities (like + // when reading [*..3] near [->..2]) + os_ << min; + if (min != max) + { + os_ << ".."; + if (max != formula::unbounded()) + os_ << max; + } + } + emit(KCloseBunop); + } + } + break; + } + if (want_par) + closep(); + } + + protected: + std::ostream& os_; + bool top_level_; + bool full_parent_; + bool in_ratexp_; + const char** kw_; + }; - } // anonymous std::ostream& - print_lbt_ltl(std::ostream& os, formula f) + printer_(std::ostream& os, formula f, bool full_parent, + bool ratexp, const char** kw) { - assert(f.is_ltl_formula()); - lbt_visitor v(os); + to_string_visitor v(os, full_parent, ratexp, kw); v.visit(f); return os; } std::string - str_lbt_ltl(formula f) + str_(formula f, bool full_parent, bool ratexp, const char** kw) { std::ostringstream os; - print_lbt_ltl(os, f); + printer_(os, f, full_parent, ratexp, kw); return os.str(); } + } // anonymous + + std::ostream& + print_psl(std::ostream& os, formula f, bool full_parent) + { + return printer_(os, f, full_parent, false, spot_kw); + } + + std::string + str_psl(formula f, bool full_parent) + { + return str_(f, full_parent, false, spot_kw); + } + + std::ostream& + print_sere(std::ostream& os, formula f, bool full_parent) + { + return printer_(os, f, full_parent, true, spot_kw); + } + + std::string + str_sere(formula f, bool full_parent) + { + return str_(f, full_parent, false, spot_kw); + } + + + std::ostream& + print_utf8_psl(std::ostream& os, formula f, bool full_parent) + { + return printer_(os, f, full_parent, false, utf8_kw); + } + + std::string + str_utf8_psl(formula f, bool full_parent) + { + return str_(f, full_parent, false, utf8_kw); + } + + std::ostream& + print_utf8_sere(std::ostream& os, formula f, bool full_parent) + { + return printer_(os, f, full_parent, true, utf8_kw); + } + + std::string + str_utf8_sere(formula f, bool full_parent) + { + return str_(f, full_parent, false, utf8_kw); + } + + + std::ostream& + print_spin_ltl(std::ostream& os, formula f, bool full_parent) + { + to_string_visitor v(os, full_parent, false, spin_kw); + v.visit(unabbreviate(f, "^MW")); + return os; + } + + std::string + str_spin_ltl(formula f, bool full_parent) + { + std::ostringstream os; + print_spin_ltl(os, f, full_parent); + return os.str(); + } + + std::ostream& + print_wring_ltl(std::ostream& os, formula f) + { + to_string_visitor v(os, true, false, wring_kw); + v.visit(unabbreviate(f, "MW")); + return os; + } + + std::string + str_wring_ltl(formula f) + { + std::ostringstream os; + print_wring_ltl(os, f); + return os.str(); + } + + std::ostream& + print_latex_psl(std::ostream& os, formula f, bool full_parent) + { + return printer_(os, f, full_parent, false, latex_kw); + } + + std::string + str_latex_psl(formula f, bool full_parent) + { + return str_(f, full_parent, false, latex_kw); + } + + std::ostream& + print_latex_sere(std::ostream& os, formula f, bool full_parent) + { + return printer_(os, f, full_parent, true, latex_kw); + } + + std::string + str_latex_sere(formula f, bool full_parent) + { + return str_(f, full_parent, true, latex_kw); + } + + + std::ostream& + print_sclatex_psl(std::ostream& os, formula f, bool full_parent) + { + return printer_(os, f, full_parent, false, sclatex_kw); + } + + std::string + str_sclatex_psl(formula f, bool full_parent) + { + return str_(f, full_parent, false, sclatex_kw); + } + + std::ostream& + print_sclatex_sere(std::ostream& os, formula f, bool full_parent) + { + return printer_(os, f, full_parent, true, sclatex_kw); + } + + std::string + str_sclatex_sere(formula f, bool full_parent) + { + return str_(f, full_parent, true, sclatex_kw); + } + + namespace + { + // Does str match p[0-9]+ ? + static bool + is_pnum(const char* str) + { + if (str[0] != 'p' || str[1] == 0) + return false; + while (*++str) + if (*str < '0' || *str > '9') + return false; + return true; + } + + class lbt_visitor final + { + protected: + std::ostream& os_; + bool first_; + public: + + lbt_visitor(std::ostream& os) + : os_(os), first_(true) + { + } + + void + visit(formula f) + { + if (first_) + first_ = false; + else + os_ << ' '; + + op o = f.kind(); + switch (o) + { + case op::ff: + os_ << 'f'; + break; + case op::tt: + os_ << 't'; + break; + case op::ap: + { + const std::string& str = f.ap_name(); + if (!is_pnum(str.c_str())) + escape_str(os_ << '"', str) << '"'; + else + os_ << str; + break; + } + case op::Not: + os_ << '!'; + break; + case op::X: + os_ << 'X'; + break; + case op::F: + os_ << 'F'; + break; + case op::G: + os_ << 'G'; + break; + case op::Xor: + os_ << '^'; + break; + case op::Implies: + os_ << 'i'; + break; + case op::Equiv: + os_ << 'e'; + break; + case op::U: + os_ << 'U'; + break; + case op::R: + os_ << 'V'; + break; + case op::W: + os_ << 'W'; + break; + case op::M: + os_ << 'M'; + break; + case op::Or: + for (unsigned i = f.size() - 1; i != 0; --i) + os_ << "| "; + first_ = true; + break; + case op::And: + for (unsigned i = f.size() - 1; i != 0; --i) + os_ << "& "; + first_ = true; + break; + case op::eword: + case op::Closure: + case op::NegClosure: + case op::NegClosureMarked: + case op::EConcat: + case op::EConcatMarked: + case op::UConcat: + case op::OrRat: + case op::AndRat: + case op::AndNLM: + case op::Concat: + case op::Fusion: + case op::Star: + case op::FStar: + SPOT_UNIMPLEMENTED(); + } + for (auto c: f) + visit(c); + } + }; + + } // anonymous + + std::ostream& + print_lbt_ltl(std::ostream& os, formula f) + { + assert(f.is_ltl_formula()); + lbt_visitor v(os); + v.visit(f); + return os; + } + + std::string + str_lbt_ltl(formula f) + { + std::ostringstream os; + print_lbt_ltl(os, f); + return os.str(); } } diff --git a/src/tl/print.hh b/src/tl/print.hh index 8491dc2de..22ba658c6 100644 --- a/src/tl/print.hh +++ b/src/tl/print.hh @@ -27,198 +27,195 @@ namespace spot { - namespace ltl - { - /// \addtogroup ltl_io - /// @{ + /// \addtogroup ltl_io + /// @{ - /// \brief Output a PSL formula as a string which is parsable. - /// \param os The stream where it should be output. - /// \param f The formula to translate. - /// \param full_parent Whether or not the string should by fully - /// parenthesized. - SPOT_API std::ostream& - print_psl(std::ostream& os, formula f, bool full_parent = false); + /// \brief Output a PSL formula as a string which is parsable. + /// \param os The stream where it should be output. + /// \param f The formula to translate. + /// \param full_parent Whether or not the string should by fully + /// parenthesized. + SPOT_API std::ostream& + print_psl(std::ostream& os, formula f, bool full_parent = false); - /// \brief Convert a PSL formula into a string which is parsable - /// \param f The formula to translate. - /// \param full_parent Whether or not the string should by fully - /// parenthesized. - SPOT_API std::string - str_psl(formula f, bool full_parent = false); + /// \brief Convert a PSL formula into a string which is parsable + /// \param f The formula to translate. + /// \param full_parent Whether or not the string should by fully + /// parenthesized. + SPOT_API std::string + str_psl(formula f, bool full_parent = false); - /// \brief Output a PSL formula as an utf-8 string which is parsable. - /// \param os The stream where it should be output. - /// \param f The formula to translate. - /// \param full_parent Whether or not the string should by fully - /// parenthesized. - SPOT_API std::ostream& - print_utf8_psl(std::ostream& os, formula f, + /// \brief Output a PSL formula as an utf-8 string which is parsable. + /// \param os The stream where it should be output. + /// \param f The formula to translate. + /// \param full_parent Whether or not the string should by fully + /// parenthesized. + SPOT_API std::ostream& + print_utf8_psl(std::ostream& os, formula f, + bool full_parent = false); + + /// \brief Convert a PSL formula into a utf-8 string which is parsable + /// \param f The formula to translate. + /// \param full_parent Whether or not the string should by fully + /// parenthesized. + SPOT_API std::string + str_utf8_psl(formula f, bool full_parent = false); + + /// \brief Output a SERE formula as a string which is parsable. + /// \param f The formula to translate. + /// \param os The stream where it should be output. + /// \param full_parent Whether or not the string should by fully + /// parenthesized. + SPOT_API std::ostream& + print_sere(std::ostream& os, formula f, bool full_parent = false); + + /// \brief Convert a SERE formula into a string which is parsable + /// \param f The formula to translate. + /// \param full_parent Whether or not the string should by fully + /// parenthesized. + SPOT_API std::string + str_sere(formula f, bool full_parent = false); + + /// \brief Output a SERE formula as a utf-8 string which is parsable. + /// \param os The stream where it should be output. + /// \param f The formula to translate. + /// \param full_parent Whether or not the string should by fully + /// parenthesized. + SPOT_API std::ostream& + print_utf8_sere(std::ostream& os, formula f, + bool full_parent = false); + + /// \brief Convert a SERE formula into a string which is parsable + /// \param f The formula to translate. + /// \param full_parent Whether or not the string should by fully + /// parenthesized. + SPOT_API std::string + str_utf8_sere(formula f, bool full_parent = false); + + /// \brief Output an LTL formula as a string parsable by Spin. + /// \param os The stream where it should be output. + /// \param f The formula to translate. + /// \param full_parent Whether or not the string should by fully + /// parenthesized. + SPOT_API std::ostream& + print_spin_ltl(std::ostream& os, formula f, + bool full_parent = false); + + /// \brief Convert an LTL formula into a string parsable by Spin. + /// \param f The formula to translate. + /// \param full_parent Whether or not the string should by fully + /// parenthesized. + SPOT_API std::string + str_spin_ltl(formula f, bool full_parent = false); + + /// \brief Output an LTL formula as a string parsable by Wring. + /// \param os The stream where it should be output. + /// \param f The formula to translate. + SPOT_API std::ostream& + print_wring_ltl(std::ostream& os, formula f); + + /// \brief Convert a formula into a string parsable by Wring + /// \param f The formula to translate. + SPOT_API std::string + str_wring_ltl(formula f); + + /// \brief Output a PSL formula as a LaTeX string. + /// \param os The stream where it should be output. + /// \param f The formula to translate. + /// \param full_parent Whether or not the string should by fully + /// parenthesized. + SPOT_API std::ostream& + print_latex_psl(std::ostream& os, formula f, + bool full_parent = false); + + /// \brief Output a formula as a LaTeX string which is parsable. + /// unless the formula contains automaton operators (used in ELTL formulae). + /// \param f The formula to translate. + /// \param full_parent Whether or not the string should by fully + /// parenthesized. + SPOT_API std::string + str_latex_psl(formula f, bool full_parent = false); + + /// \brief Output a SERE formula as a LaTeX string. + /// \param os The stream where it should be output. + /// \param f The formula to translate. + /// \param full_parent Whether or not the string should by fully + /// parenthesized. + SPOT_API std::ostream& + print_latex_sere(std::ostream& os, formula f, bool full_parent = false); - /// \brief Convert a PSL formula into a utf-8 string which is parsable - /// \param f The formula to translate. - /// \param full_parent Whether or not the string should by fully - /// parenthesized. - SPOT_API std::string - str_utf8_psl(formula f, bool full_parent = false); + /// \brief Output a SERE formula as a LaTeX string which is parsable. + /// unless the formula contains automaton operators (used in ELTL formulae). + /// \param f The formula to translate. + /// \param full_parent Whether or not the string should by fully + /// parenthesized. + SPOT_API std::string + str_latex_sere(formula f, bool full_parent = false); - /// \brief Output a SERE formula as a string which is parsable. - /// \param f The formula to translate. - /// \param os The stream where it should be output. - /// \param full_parent Whether or not the string should by fully - /// parenthesized. - SPOT_API std::ostream& - print_sere(std::ostream& os, formula f, bool full_parent = false); - - /// \brief Convert a SERE formula into a string which is parsable - /// \param f The formula to translate. - /// \param full_parent Whether or not the string should by fully - /// parenthesized. - SPOT_API std::string - str_sere(formula f, bool full_parent = false); - - /// \brief Output a SERE formula as a utf-8 string which is parsable. - /// \param os The stream where it should be output. - /// \param f The formula to translate. - /// \param full_parent Whether or not the string should by fully - /// parenthesized. - SPOT_API std::ostream& - print_utf8_sere(std::ostream& os, formula f, + /// \brief Output a PSL formula as a self-contained LaTeX string. + /// + /// The result cannot be parsed back. + /// \param os The stream where it should be output. + /// \param f The formula to translate. + /// \param full_parent Whether or not the string should by fully + /// parenthesized. + SPOT_API std::ostream& + print_sclatex_psl(std::ostream& os, formula f, bool full_parent = false); - /// \brief Convert a SERE formula into a string which is parsable - /// \param f The formula to translate. - /// \param full_parent Whether or not the string should by fully - /// parenthesized. - SPOT_API std::string - str_utf8_sere(formula f, bool full_parent = false); + /// \brief Output a PSL formula as a self-contained LaTeX string. + /// + /// The result cannot be parsed bacl. + /// \param f The formula to translate. + /// \param full_parent Whether or not the string should by fully + /// parenthesized. + SPOT_API std::string + str_sclatex_psl(formula f, bool full_parent = false); - /// \brief Output an LTL formula as a string parsable by Spin. - /// \param os The stream where it should be output. - /// \param f The formula to translate. - /// \param full_parent Whether or not the string should by fully - /// parenthesized. - SPOT_API std::ostream& - print_spin_ltl(std::ostream& os, formula f, - bool full_parent = false); - - /// \brief Convert an LTL formula into a string parsable by Spin. - /// \param f The formula to translate. - /// \param full_parent Whether or not the string should by fully - /// parenthesized. - SPOT_API std::string - str_spin_ltl(formula f, bool full_parent = false); - - /// \brief Output an LTL formula as a string parsable by Wring. - /// \param os The stream where it should be output. - /// \param f The formula to translate. - SPOT_API std::ostream& - print_wring_ltl(std::ostream& os, formula f); - - /// \brief Convert a formula into a string parsable by Wring - /// \param f The formula to translate. - SPOT_API std::string - str_wring_ltl(formula f); - - /// \brief Output a PSL formula as a LaTeX string. - /// \param os The stream where it should be output. - /// \param f The formula to translate. - /// \param full_parent Whether or not the string should by fully - /// parenthesized. - SPOT_API std::ostream& - print_latex_psl(std::ostream& os, formula f, - bool full_parent = false); - - /// \brief Output a formula as a LaTeX string which is parsable. - /// unless the formula contains automaton operators (used in ELTL formulae). - /// \param f The formula to translate. - /// \param full_parent Whether or not the string should by fully - /// parenthesized. - SPOT_API std::string - str_latex_psl(formula f, bool full_parent = false); - - /// \brief Output a SERE formula as a LaTeX string. - /// \param os The stream where it should be output. - /// \param f The formula to translate. - /// \param full_parent Whether or not the string should by fully - /// parenthesized. - SPOT_API std::ostream& - print_latex_sere(std::ostream& os, formula f, + /// \brief Output a SERE formula as a self-contained LaTeX string. + /// + /// The result cannot be parsed back. + /// \param os The stream where it should be output. + /// \param f The formula to translate. + /// \param full_parent Whether or not the string should by fully + /// parenthesized. + SPOT_API std::ostream& + print_sclatex_sere(std::ostream& os, formula f, bool full_parent = false); - /// \brief Output a SERE formula as a LaTeX string which is parsable. - /// unless the formula contains automaton operators (used in ELTL formulae). - /// \param f The formula to translate. - /// \param full_parent Whether or not the string should by fully - /// parenthesized. - SPOT_API std::string - str_latex_sere(formula f, bool full_parent = false); + /// \brief Output a SERE formula as a self-contained LaTeX string. + /// + /// The result cannot be parsed bacl. + /// \param f The formula to translate. + /// \param full_parent Whether or not the string should by fully + /// parenthesized. + SPOT_API std::string + str_sclatex_sere(formula f, bool full_parent = false); - /// \brief Output a PSL formula as a self-contained LaTeX string. - /// - /// The result cannot be parsed back. - /// \param os The stream where it should be output. - /// \param f The formula to translate. - /// \param full_parent Whether or not the string should by fully - /// parenthesized. - SPOT_API std::ostream& - print_sclatex_psl(std::ostream& os, formula f, - bool full_parent = false); + /// \brief Output an LTL formula as a string in LBT's format. + /// + /// The formula must be an LTL formula (ELTL and PSL operators + /// are not supported). The M and W operator will be output + /// as-is, because this is accepted by LBTT, however if you + /// plan to use the output with other tools, you should probably + /// rewrite these two operators using unabbreviate_wm(). + /// + /// \param f The formula to translate. + /// \param os The stream where it should be output. + SPOT_API std::ostream& + print_lbt_ltl(std::ostream& os, formula f); - /// \brief Output a PSL formula as a self-contained LaTeX string. - /// - /// The result cannot be parsed bacl. - /// \param f The formula to translate. - /// \param full_parent Whether or not the string should by fully - /// parenthesized. - SPOT_API std::string - str_sclatex_psl(formula f, bool full_parent = false); - - /// \brief Output a SERE formula as a self-contained LaTeX string. - /// - /// The result cannot be parsed back. - /// \param os The stream where it should be output. - /// \param f The formula to translate. - /// \param full_parent Whether or not the string should by fully - /// parenthesized. - SPOT_API std::ostream& - print_sclatex_sere(std::ostream& os, formula f, - bool full_parent = false); - - /// \brief Output a SERE formula as a self-contained LaTeX string. - /// - /// The result cannot be parsed bacl. - /// \param f The formula to translate. - /// \param full_parent Whether or not the string should by fully - /// parenthesized. - SPOT_API std::string - str_sclatex_sere(formula f, bool full_parent = false); - - /// \brief Output an LTL formula as a string in LBT's format. - /// - /// The formula must be an LTL formula (ELTL and PSL operators - /// are not supported). The M and W operator will be output - /// as-is, because this is accepted by LBTT, however if you - /// plan to use the output with other tools, you should probably - /// rewrite these two operators using unabbreviate_wm(). - /// - /// \param f The formula to translate. - /// \param os The stream where it should be output. - SPOT_API std::ostream& - print_lbt_ltl(std::ostream& os, formula f); - - /// \brief Output an LTL formula as a string in LBT's format. - /// - /// The formula must be an LTL formula (ELTL and PSL operators - /// are not supported). The M and W operator will be output - /// as-is, because this is accepted by LBTT, however if you - /// plan to use the output with other tools, you should probably - /// rewrite these two operators using unabbreviate_wm(). - /// - /// \param f The formula to translate. - SPOT_API std::string - str_lbt_ltl(formula f); - /// @} - } + /// \brief Output an LTL formula as a string in LBT's format. + /// + /// The formula must be an LTL formula (ELTL and PSL operators + /// are not supported). The M and W operator will be output + /// as-is, because this is accepted by LBTT, however if you + /// plan to use the output with other tools, you should probably + /// rewrite these two operators using unabbreviate_wm(). + /// + /// \param f The formula to translate. + SPOT_API std::string + str_lbt_ltl(formula f); + /// @} } diff --git a/src/tl/randomltl.cc b/src/tl/randomltl.cc index 3e1355e7b..910f94bd0 100644 --- a/src/tl/randomltl.cc +++ b/src/tl/randomltl.cc @@ -32,555 +32,552 @@ namespace spot { - namespace ltl + namespace { - namespace + static formula + ap_builder(const random_formula* rl, int n) { - static formula - ap_builder(const random_formula* rl, int n) - { - assert(n == 1); - (void) n; - atomic_prop_set::const_iterator i = rl->ap()->begin(); - std::advance(i, mrand(rl->ap()->size())); - return *i; - } - - static formula - true_builder(const random_formula*, int n) - { - assert(n == 1); - (void) n; - return formula::tt(); - } - - static formula - false_builder(const random_formula*, int n) - { - assert(n == 1); - (void) n; - return formula::ff(); - } - - static formula - eword_builder(const random_formula*, int n) - { - assert(n == 1); - (void) n; - return formula::eword(); - } - - static formula - boolform_builder(const random_formula* rl, int n) - { - assert(n >= 1); - const random_sere* rs = static_cast(rl); - return rs->rb.generate(n); - } - - template - static formula - unop_builder(const random_formula* rl, int n) - { - assert(n >= 2); - return formula::unop(Op, rl->generate(n - 1)); - } - - static formula - closure_builder(const random_formula* rl, int n) - { - assert(n >= 2); - const random_psl* rp = static_cast(rl); - return formula::Closure(rp->rs.generate(n - 1)); - } - - template - static formula - binop_builder(const random_formula* rl, int n) - { - assert(n >= 3); - --n; - int l = rrand(1, n - 1); - // Force the order of generation of operands to be right, then - // left. This is historical, because gcc evaluates argument - // from right to left and we used to make the two calls to - // generate() inside of the call to instance() before - // discovering that clang would perform the nested calls from - // left to right. - auto right = rl->generate(n - l); - return formula::binop(Op, rl->generate(l), right); - } - - template - static formula - binop_SERELTL_builder(const random_formula* rl, int n) - { - assert(n >= 3); - --n; - const random_psl* rp = static_cast(rl); - int l = rrand(1, n - 1); - // See comment in binop_builder. - auto right = rl->generate(n - l); - return formula::binop(Op, rp->rs.generate(l), right); - } - - template - static formula - bunop_unbounded_builder(const random_formula* rl, int n) - { - assert(n >= 2); - return formula::bunop(Op, rl->generate(n - 1)); - } - - template - static formula - bunop_bounded_builder(const random_formula* rl, int n) - { - assert(n >= 2); - int min = rrand(0, 2); - int max = rrand(min, 3); - return formula::bunop(Op, rl->generate(n - 1), min, max); - } - - template - static formula - bunop_bool_bounded_builder(const random_formula* rl, int n) - { - assert(n >= 2); - int min = rrand(0, 2); - int max = rrand(min, 3); - const random_sere* rp = static_cast(rl); - return formula::bunop(Op, rp->rb.generate(n - 1), min, max); - } - - - template - static formula - multop_builder(const random_formula* rl, int n) - { - assert(n >= 3); - --n; - int l = rrand(1, n - 1); - // See comment in binop_builder. - auto right = rl->generate(n - l); - return formula::multop(Op, {rl->generate(l), right}); - } - - } // anonymous - - void - random_formula::op_proba::setup(const char* name, int min_n, builder build) - { - this->name = name; - this->min_n = min_n; - this->proba = 1.0; - this->build = build; + assert(n == 1); + (void) n; + atomic_prop_set::const_iterator i = rl->ap()->begin(); + std::advance(i, mrand(rl->ap()->size())); + return *i; } - void - random_formula::update_sums() + static formula + true_builder(const random_formula*, int n) { - total_1_ = 0.0; - total_2_ = 0.0; - total_2_and_more_ = 0.0; - for (unsigned i = 0; i < proba_size_; ++i) - { - if (proba_[i].min_n == 1) - { - total_1_ += proba_[i].proba; - if (proba_ + i >= proba_2_) - total_2_ += proba_[i].proba; - if (proba_ + i >= proba_2_or_more_) - total_2_and_more_ += proba_[i].proba; - } - else if (proba_[i].min_n == 2) - { + assert(n == 1); + (void) n; + return formula::tt(); + } + + static formula + false_builder(const random_formula*, int n) + { + assert(n == 1); + (void) n; + return formula::ff(); + } + + static formula + eword_builder(const random_formula*, int n) + { + assert(n == 1); + (void) n; + return formula::eword(); + } + + static formula + boolform_builder(const random_formula* rl, int n) + { + assert(n >= 1); + const random_sere* rs = static_cast(rl); + return rs->rb.generate(n); + } + + template + static formula + unop_builder(const random_formula* rl, int n) + { + assert(n >= 2); + return formula::unop(Op, rl->generate(n - 1)); + } + + static formula + closure_builder(const random_formula* rl, int n) + { + assert(n >= 2); + const random_psl* rp = static_cast(rl); + return formula::Closure(rp->rs.generate(n - 1)); + } + + template + static formula + binop_builder(const random_formula* rl, int n) + { + assert(n >= 3); + --n; + int l = rrand(1, n - 1); + // Force the order of generation of operands to be right, then + // left. This is historical, because gcc evaluates argument + // from right to left and we used to make the two calls to + // generate() inside of the call to instance() before + // discovering that clang would perform the nested calls from + // left to right. + auto right = rl->generate(n - l); + return formula::binop(Op, rl->generate(l), right); + } + + template + static formula + binop_SERELTL_builder(const random_formula* rl, int n) + { + assert(n >= 3); + --n; + const random_psl* rp = static_cast(rl); + int l = rrand(1, n - 1); + // See comment in binop_builder. + auto right = rl->generate(n - l); + return formula::binop(Op, rp->rs.generate(l), right); + } + + template + static formula + bunop_unbounded_builder(const random_formula* rl, int n) + { + assert(n >= 2); + return formula::bunop(Op, rl->generate(n - 1)); + } + + template + static formula + bunop_bounded_builder(const random_formula* rl, int n) + { + assert(n >= 2); + int min = rrand(0, 2); + int max = rrand(min, 3); + return formula::bunop(Op, rl->generate(n - 1), min, max); + } + + template + static formula + bunop_bool_bounded_builder(const random_formula* rl, int n) + { + assert(n >= 2); + int min = rrand(0, 2); + int max = rrand(min, 3); + const random_sere* rp = static_cast(rl); + return formula::bunop(Op, rp->rb.generate(n - 1), min, max); + } + + + template + static formula + multop_builder(const random_formula* rl, int n) + { + assert(n >= 3); + --n; + int l = rrand(1, n - 1); + // See comment in binop_builder. + auto right = rl->generate(n - l); + return formula::multop(Op, {rl->generate(l), right}); + } + + } // anonymous + + void + random_formula::op_proba::setup(const char* name, int min_n, builder build) + { + this->name = name; + this->min_n = min_n; + this->proba = 1.0; + this->build = build; + } + + void + random_formula::update_sums() + { + total_1_ = 0.0; + total_2_ = 0.0; + total_2_and_more_ = 0.0; + for (unsigned i = 0; i < proba_size_; ++i) + { + if (proba_[i].min_n == 1) + { + total_1_ += proba_[i].proba; + if (proba_ + i >= proba_2_) total_2_ += proba_[i].proba; - if (proba_ + i >= proba_2_or_more_) - total_2_and_more_ += proba_[i].proba; - } - else if (proba_[i].min_n > 2) - total_2_and_more_ += proba_[i].proba; - else - SPOT_UNREACHABLE(); // unexpected max_n - } - assert(total_2_and_more_ >= total_2_); - } + if (proba_ + i >= proba_2_or_more_) + total_2_and_more_ += proba_[i].proba; + } + else if (proba_[i].min_n == 2) + { + total_2_ += proba_[i].proba; + if (proba_ + i >= proba_2_or_more_) + total_2_and_more_ += proba_[i].proba; + } + else if (proba_[i].min_n > 2) + total_2_and_more_ += proba_[i].proba; + else + SPOT_UNREACHABLE(); // unexpected max_n + } + assert(total_2_and_more_ >= total_2_); + } - formula - random_formula::generate(int n) const - { - assert(n > 0); + formula + random_formula::generate(int n) const + { + assert(n > 0); - double r = drand(); - op_proba* p; + double r = drand(); + op_proba* p; - // Approximate impossible cases. - if (n == 1 && total_1_ == 0.0) - { - if (total_2_ != 0.0) - n = 2; - else - n = 3; - } - else if (n == 2 && total_2_ == 0.0) - { - if (total_1_ != 0.0) - n = 1; - else - n = 3; - } - else if (n > 2 && total_2_and_more_ == 0.0) - { - if (total_1_ != 0.0) - n = 1; - else - assert(total_2_ == 0.0); - } + // Approximate impossible cases. + if (n == 1 && total_1_ == 0.0) + { + if (total_2_ != 0.0) + n = 2; + else + n = 3; + } + else if (n == 2 && total_2_ == 0.0) + { + if (total_1_ != 0.0) + n = 1; + else + n = 3; + } + else if (n > 2 && total_2_and_more_ == 0.0) + { + if (total_1_ != 0.0) + n = 1; + else + assert(total_2_ == 0.0); + } - if (n == 1) - { - r *= total_1_; - p = proba_; - } - else if (n == 2) - { - r *= total_2_; - p = proba_2_; - } - else - { - r *= total_2_and_more_; - p = proba_2_or_more_; - } + if (n == 1) + { + r *= total_1_; + p = proba_; + } + else if (n == 2) + { + r *= total_2_; + p = proba_2_; + } + else + { + r *= total_2_and_more_; + p = proba_2_or_more_; + } - double s = p->proba; - while (s < r) - { - ++p; - s += p->proba; - } + double s = p->proba; + while (s < r) + { + ++p; + s += p->proba; + } - return p->build(this, n); - } + return p->build(this, n); + } - const char* - random_formula::parse_options(char* options) - { - if (!options) - return nullptr; - char* key = strtok(options, "=\t, :;"); - while (key) - { - char* value = strtok(nullptr, "=\t, :;"); - if (!value) - return key; - - char* endptr; - double res = strtod(value, &endptr); - if (*endptr) - return value; - - unsigned i; - for (i = 0; i < proba_size_; ++i) - { - if (('a' <= *proba_[i].name && *proba_[i].name <= 'z' - && !strcasecmp(proba_[i].name, key)) - || !strcmp(proba_[i].name, key)) - { - proba_[i].proba = res; - break; - } - } - if (i == proba_size_) - return key; - - key = strtok(nullptr, "=\t, :;"); - } - update_sums(); + const char* + random_formula::parse_options(char* options) + { + if (!options) return nullptr; - } + char* key = strtok(options, "=\t, :;"); + while (key) + { + char* value = strtok(nullptr, "=\t, :;"); + if (!value) + return key; - std::ostream& - random_formula::dump_priorities(std::ostream& os) const - { - for (unsigned i = 0; i < proba_size_; ++i) - os << proba_[i].name << '\t' << proba_[i].proba << '\n'; - return os; - } + char* endptr; + double res = strtod(value, &endptr); + if (*endptr) + return value; - // SEREs - random_sere::random_sere(const atomic_prop_set* ap) - : random_formula(11, ap), rb(ap) - { - proba_[0].setup("eword", 1, eword_builder); - proba_2_ = proba_ + 1; - proba_2_or_more_ = proba_ + 1; - proba_[1].setup("boolform", 1, boolform_builder); - proba_[2].setup("star", 2, bunop_unbounded_builder); - proba_[3].setup("star_b", 2, bunop_bounded_builder); - proba_[4].setup("fstar", 2, bunop_unbounded_builder); - proba_[5].setup("fstar_b", 2, bunop_bounded_builder); - proba_[6].setup("and", 3, multop_builder); - proba_[7].setup("andNLM", 3, multop_builder); - proba_[8].setup("or", 3, multop_builder); - proba_[9].setup("concat", 3, multop_builder); - proba_[10].setup("fusion", 3, multop_builder); + unsigned i; + for (i = 0; i < proba_size_; ++i) + { + if (('a' <= *proba_[i].name && *proba_[i].name <= 'z' + && !strcasecmp(proba_[i].name, key)) + || !strcmp(proba_[i].name, key)) + { + proba_[i].proba = res; + break; + } + } + if (i == proba_size_) + return key; - update_sums(); - } + key = strtok(nullptr, "=\t, :;"); + } + update_sums(); + return nullptr; + } - // Boolean formulae - random_boolean::random_boolean(const atomic_prop_set* ap) - : random_formula(9, ap) - { - proba_[0].setup("ap", 1, ap_builder); - proba_[0].proba = ap_->size(); - proba_[1].setup("false", 1, false_builder); - proba_[2].setup("true", 1, true_builder); - proba_2_or_more_ = proba_2_ = proba_ + 3; - proba_[3].setup("not", 2, unop_builder); - proba_[4].setup("equiv", 3, binop_builder); - proba_[5].setup("implies", 3, binop_builder); - proba_[6].setup("xor", 3, binop_builder); - proba_[7].setup("and", 3, multop_builder); - proba_[8].setup("or", 3, multop_builder); + std::ostream& + random_formula::dump_priorities(std::ostream& os) const + { + for (unsigned i = 0; i < proba_size_; ++i) + os << proba_[i].name << '\t' << proba_[i].proba << '\n'; + return os; + } - update_sums(); - } + // SEREs + random_sere::random_sere(const atomic_prop_set* ap) + : random_formula(11, ap), rb(ap) + { + proba_[0].setup("eword", 1, eword_builder); + proba_2_ = proba_ + 1; + proba_2_or_more_ = proba_ + 1; + proba_[1].setup("boolform", 1, boolform_builder); + proba_[2].setup("star", 2, bunop_unbounded_builder); + proba_[3].setup("star_b", 2, bunop_bounded_builder); + proba_[4].setup("fstar", 2, bunop_unbounded_builder); + proba_[5].setup("fstar_b", 2, bunop_bounded_builder); + proba_[6].setup("and", 3, multop_builder); + proba_[7].setup("andNLM", 3, multop_builder); + proba_[8].setup("or", 3, multop_builder); + proba_[9].setup("concat", 3, multop_builder); + proba_[10].setup("fusion", 3, multop_builder); - // LTL formulae - void - random_ltl::setup_proba_() - { - proba_[0].setup("ap", 1, ap_builder); - proba_[0].proba = ap_->size(); - proba_[1].setup("false", 1, false_builder); - proba_[2].setup("true", 1, true_builder); - proba_2_or_more_ = proba_2_ = proba_ + 3; - proba_[3].setup("not", 2, unop_builder); - proba_[4].setup("F", 2, unop_builder); - proba_[5].setup("G", 2, unop_builder); - proba_[6].setup("X", 2, unop_builder); - proba_[7].setup("equiv", 3, binop_builder); - proba_[8].setup("implies", 3, binop_builder); - proba_[9].setup("xor", 3, binop_builder); - proba_[10].setup("R", 3, binop_builder); - proba_[11].setup("U", 3, binop_builder); - proba_[12].setup("W", 3, binop_builder); - proba_[13].setup("M", 3, binop_builder); - proba_[14].setup("and", 3, multop_builder); - proba_[15].setup("or", 3, multop_builder); - } + update_sums(); + } - random_ltl::random_ltl(const atomic_prop_set* ap) - : random_formula(16, ap) - { - setup_proba_(); - update_sums(); - } + // Boolean formulae + random_boolean::random_boolean(const atomic_prop_set* ap) + : random_formula(9, ap) + { + proba_[0].setup("ap", 1, ap_builder); + proba_[0].proba = ap_->size(); + proba_[1].setup("false", 1, false_builder); + proba_[2].setup("true", 1, true_builder); + proba_2_or_more_ = proba_2_ = proba_ + 3; + proba_[3].setup("not", 2, unop_builder); + proba_[4].setup("equiv", 3, binop_builder); + proba_[5].setup("implies", 3, binop_builder); + proba_[6].setup("xor", 3, binop_builder); + proba_[7].setup("and", 3, multop_builder); + proba_[8].setup("or", 3, multop_builder); - random_ltl::random_ltl(int size, const atomic_prop_set* ap) - : random_formula(size, ap) - { - setup_proba_(); - // No call to update_sums(), this functions is always - // called by the random_psl constructor. - } + update_sums(); + } - // PSL - random_psl::random_psl(const atomic_prop_set* ap) - : random_ltl(19, ap), rs(ap) - { - // FIXME: This looks very fragile. - memmove(proba_ + 8, proba_ + 7, - ((proba_ + 16) - (proba_ + 7)) * sizeof(*proba_)); + // LTL formulae + void + random_ltl::setup_proba_() + { + proba_[0].setup("ap", 1, ap_builder); + proba_[0].proba = ap_->size(); + proba_[1].setup("false", 1, false_builder); + proba_[2].setup("true", 1, true_builder); + proba_2_or_more_ = proba_2_ = proba_ + 3; + proba_[3].setup("not", 2, unop_builder); + proba_[4].setup("F", 2, unop_builder); + proba_[5].setup("G", 2, unop_builder); + proba_[6].setup("X", 2, unop_builder); + proba_[7].setup("equiv", 3, binop_builder); + proba_[8].setup("implies", 3, binop_builder); + proba_[9].setup("xor", 3, binop_builder); + proba_[10].setup("R", 3, binop_builder); + proba_[11].setup("U", 3, binop_builder); + proba_[12].setup("W", 3, binop_builder); + proba_[13].setup("M", 3, binop_builder); + proba_[14].setup("and", 3, multop_builder); + proba_[15].setup("or", 3, multop_builder); + } - proba_[7].setup("Closure", 2, closure_builder); - proba_[17].setup("EConcat", 3, binop_SERELTL_builder); - proba_[18].setup("UConcat", 3, binop_SERELTL_builder); - update_sums(); - } + random_ltl::random_ltl(const atomic_prop_set* ap) + : random_formula(16, ap) + { + setup_proba_(); + update_sums(); + } - randltlgenerator::randltlgenerator(atomic_prop_set aprops, - const option_map& opts, - char* opt_pL, - char* opt_pS, - char* opt_pB) - { - aprops_ = aprops; - output_ = opts.get("output", OUTPUTLTL); - opt_seed_ = opts.get("seed", 0); - opt_tree_size_min_ = opts.get("tree_size_min", 15); - opt_tree_size_max_ = opts.get("tree_size_max", 15); - opt_unique_ = opts.get("unique", true); - opt_wf_ = opts.get("wf", false); - opt_simpl_level_ = opts.get("simplification_level", 3); + random_ltl::random_ltl(int size, const atomic_prop_set* ap) + : random_formula(size, ap) + { + setup_proba_(); + // No call to update_sums(), this functions is always + // called by the random_psl constructor. + } - const char* tok_pL = nullptr; - const char* tok_pS = nullptr; - const char* tok_pB = nullptr; + // PSL + random_psl::random_psl(const atomic_prop_set* ap) + : random_ltl(19, ap), rs(ap) + { + // FIXME: This looks very fragile. + memmove(proba_ + 8, proba_ + 7, + ((proba_ + 16) - (proba_ + 7)) * sizeof(*proba_)); - switch (output_) - { - case OUTPUTLTL: - rf_ = new random_ltl(&aprops_); - if (opt_pS) - throw std::invalid_argument("Cannot set sere priorities with " - "LTL output"); - if (opt_pB) - throw std::invalid_argument("Cannot set boolean priorities with " - "LTL output"); - tok_pL = rf_->parse_options(opt_pL); - break; - case OUTPUTBOOL: - rf_ = new random_boolean(&aprops_); - tok_pB = rf_->parse_options(opt_pB); - if (opt_pL) - throw std::invalid_argument("Cannot set ltl priorities with " - "Boolean output"); - if (opt_pS) - throw std::invalid_argument("Cannot set sere priorities " - "with Boolean output"); - break; - case OUTPUTSERE: - rf_ = rs_ = new random_sere(&aprops_); - tok_pS = rs_->parse_options(opt_pS); - tok_pB = rs_->rb.parse_options(opt_pB); - if (opt_pL) - throw std::invalid_argument("Cannot set ltl priorities " - "with SERE output"); - break; - case OUTPUTPSL: - rf_ = rp_ = new random_psl(&aprops_); - rs_ = &rp_->rs; - tok_pL = rp_->parse_options(opt_pL); - tok_pS = rs_->parse_options(opt_pS); - tok_pB = rs_->rb.parse_options(opt_pB); - break; - } + proba_[7].setup("Closure", 2, closure_builder); + proba_[17].setup("EConcat", 3, binop_SERELTL_builder); + proba_[18].setup("UConcat", 3, binop_SERELTL_builder); + update_sums(); + } - if (tok_pL) - throw std::invalid_argument("failed to parse LTL priorities near " - + std::string(tok_pL)); - if (tok_pS) - throw std::invalid_argument("failed to parse SERE priorities near " - + std::string(tok_pS)); - if (tok_pB) - throw std::invalid_argument("failed to parse Boolean priorities near " - + std::string(tok_pB)); + randltlgenerator::randltlgenerator(atomic_prop_set aprops, + const option_map& opts, + char* opt_pL, + char* opt_pS, + char* opt_pB) + { + aprops_ = aprops; + output_ = opts.get("output", OUTPUTLTL); + opt_seed_ = opts.get("seed", 0); + opt_tree_size_min_ = opts.get("tree_size_min", 15); + opt_tree_size_max_ = opts.get("tree_size_max", 15); + opt_unique_ = opts.get("unique", true); + opt_wf_ = opts.get("wf", false); + opt_simpl_level_ = opts.get("simplification_level", 3); - spot::srand(opt_seed_); - ltl_simplifier_options simpl_opts(opt_simpl_level_); - ltl_simplifier simpl_(simpl_opts); - } + const char* tok_pL = nullptr; + const char* tok_pS = nullptr; + const char* tok_pB = nullptr; - randltlgenerator::randltlgenerator(int aprops_n, - const option_map& opts, - char* opt_pL, - char* opt_pS, - char* opt_pB) - : randltlgenerator(create_atomic_prop_set(aprops_n), opts, - opt_pL, opt_pS, opt_pB) - { - } + switch (output_) + { + case OUTPUTLTL: + rf_ = new random_ltl(&aprops_); + if (opt_pS) + throw std::invalid_argument("Cannot set sere priorities with " + "LTL output"); + if (opt_pB) + throw std::invalid_argument("Cannot set boolean priorities with " + "LTL output"); + tok_pL = rf_->parse_options(opt_pL); + break; + case OUTPUTBOOL: + rf_ = new random_boolean(&aprops_); + tok_pB = rf_->parse_options(opt_pB); + if (opt_pL) + throw std::invalid_argument("Cannot set ltl priorities with " + "Boolean output"); + if (opt_pS) + throw std::invalid_argument("Cannot set sere priorities " + "with Boolean output"); + break; + case OUTPUTSERE: + rf_ = rs_ = new random_sere(&aprops_); + tok_pS = rs_->parse_options(opt_pS); + tok_pB = rs_->rb.parse_options(opt_pB); + if (opt_pL) + throw std::invalid_argument("Cannot set ltl priorities " + "with SERE output"); + break; + case OUTPUTPSL: + rf_ = rp_ = new random_psl(&aprops_); + rs_ = &rp_->rs; + tok_pL = rp_->parse_options(opt_pL); + tok_pS = rs_->parse_options(opt_pS); + tok_pB = rs_->rb.parse_options(opt_pB); + break; + } - randltlgenerator::~randltlgenerator() - { - delete rf_; - } + if (tok_pL) + throw std::invalid_argument("failed to parse LTL priorities near " + + std::string(tok_pL)); + if (tok_pS) + throw std::invalid_argument("failed to parse SERE priorities near " + + std::string(tok_pS)); + if (tok_pB) + throw std::invalid_argument("failed to parse Boolean priorities near " + + std::string(tok_pB)); - formula randltlgenerator::next() - { - unsigned trials = MAX_TRIALS; - bool ignore; - formula f = nullptr; - do - { - ignore = false; - int size = opt_tree_size_min_; - if (size != opt_tree_size_max_) - size = spot::rrand(size, opt_tree_size_max_); - f = rf_->generate(size); + spot::srand(opt_seed_); + ltl_simplifier_options simpl_opts(opt_simpl_level_); + ltl_simplifier simpl_(simpl_opts); + } - if (opt_wf_) - { - atomic_prop_set s = aprops_; - remove_some_props(s); - f = formula::And({f, GF_n()}); - } + randltlgenerator::randltlgenerator(int aprops_n, + const option_map& opts, + char* opt_pL, + char* opt_pS, + char* opt_pB) + : randltlgenerator(create_atomic_prop_set(aprops_n), opts, + opt_pL, opt_pS, opt_pB) + { + } - if (opt_simpl_level_) - f = simpl_.simplify(f); + randltlgenerator::~randltlgenerator() + { + delete rf_; + } - if (opt_unique_ && !unique_set_.insert(f).second) - ignore = true; - } while (ignore && --trials); - if (trials <= 0) - return nullptr; - return f; - } + formula randltlgenerator::next() + { + unsigned trials = MAX_TRIALS; + bool ignore; + formula f = nullptr; + do + { + ignore = false; + int size = opt_tree_size_min_; + if (size != opt_tree_size_max_) + size = spot::rrand(size, opt_tree_size_max_); + f = rf_->generate(size); - void - randltlgenerator::remove_some_props(atomic_prop_set& s) - { - // How many propositions to remove from s? - // (We keep at least one.) - size_t n = spot::mrand(aprops_.size()); + if (opt_wf_) + { + atomic_prop_set s = aprops_; + remove_some_props(s); + f = formula::And({f, GF_n()}); + } - while (n--) - { - auto i = s.begin(); - std::advance(i, spot::mrand(s.size())); - s.erase(i); - } - } + if (opt_simpl_level_) + f = simpl_.simplify(f); - // GF(p_1) & GF(p_2) & ... & GF(p_n) - formula - randltlgenerator::GF_n() - { - formula res = nullptr; - for (auto v: aprops_) - { - formula f = formula::G(formula::F(v)); - if (res) - res = formula::And({f, res}); - else - res = f; - } - return res; - } + if (opt_unique_ && !unique_set_.insert(f).second) + ignore = true; + } while (ignore && --trials); + if (trials <= 0) + return nullptr; + return f; + } - void - randltlgenerator::dump_ltl_priorities(std::ostream& os) - { - rf_->dump_priorities(os); - } + void + randltlgenerator::remove_some_props(atomic_prop_set& s) + { + // How many propositions to remove from s? + // (We keep at least one.) + size_t n = spot::mrand(aprops_.size()); - void - randltlgenerator::dump_bool_priorities(std::ostream& os) - { - rf_->dump_priorities(os); - } + while (n--) + { + auto i = s.begin(); + std::advance(i, spot::mrand(s.size())); + s.erase(i); + } + } - void - randltlgenerator::dump_psl_priorities(std::ostream& os) - { - rp_->dump_priorities(os); - } + // GF(p_1) & GF(p_2) & ... & GF(p_n) + formula + randltlgenerator::GF_n() + { + formula res = nullptr; + for (auto v: aprops_) + { + formula f = formula::G(formula::F(v)); + if (res) + res = formula::And({f, res}); + else + res = f; + } + return res; + } - void - randltlgenerator::dump_sere_priorities(std::ostream& os) - { - rs_->dump_priorities(os); - } + void + randltlgenerator::dump_ltl_priorities(std::ostream& os) + { + rf_->dump_priorities(os); + } - void - randltlgenerator::dump_sere_bool_priorities(std::ostream& os) - { - rs_->rb.dump_priorities(os); - } + void + randltlgenerator::dump_bool_priorities(std::ostream& os) + { + rf_->dump_priorities(os); + } + + void + randltlgenerator::dump_psl_priorities(std::ostream& os) + { + rp_->dump_priorities(os); + } + + void + randltlgenerator::dump_sere_priorities(std::ostream& os) + { + rs_->dump_priorities(os); + } + + void + randltlgenerator::dump_sere_bool_priorities(std::ostream& os) + { + rs_->rb.dump_priorities(os); } } diff --git a/src/tl/randomltl.hh b/src/tl/randomltl.hh index e6f2655ce..ba1c25bec 100644 --- a/src/tl/randomltl.hh +++ b/src/tl/randomltl.hh @@ -38,316 +38,312 @@ namespace spot { - namespace ltl + /// \ingroup ltl_io + /// \brief Base class for random formula generators + class SPOT_API random_formula { - - /// \ingroup ltl_io - /// \brief Base class for random formula generators - class SPOT_API random_formula + public: + random_formula(unsigned proba_size, + const atomic_prop_set* ap): + proba_size_(proba_size), proba_(new op_proba[proba_size_]), ap_(ap) { - public: - random_formula(unsigned proba_size, - const atomic_prop_set* ap): - proba_size_(proba_size), proba_(new op_proba[proba_size_]), ap_(ap) - { - } + } - virtual ~random_formula() - { - delete[] proba_; - } + virtual ~random_formula() + { + delete[] proba_; + } - /// Return the set of atomic proposition used to build formulae. - const atomic_prop_set* + /// Return the set of atomic proposition used to build formulae. + const atomic_prop_set* ap() const - { - return ap_; - } - - /// \brief Generate a formula of size \a n. - /// - /// It is possible to obtain formulae that are smaller than \a - /// n, because some simple simplifications are performed by the - /// AST. (For instance the formula a | a is - /// automatically reduced to a by spot::ltl::multop.) - formula generate(int n) const; - - /// \brief Print the priorities of each operator, constants, - /// and atomic propositions. - std::ostream& dump_priorities(std::ostream& os) const; - - /// \brief Update the priorities used to generate the formulae. - /// - /// \a options should be comma-separated list of KEY=VALUE - /// assignments, using keys from the above list. - /// For instance "xor=0, F=3" will prevent \c xor - /// from being used, and will raise the relative probability of - /// occurrences of the \c F operator. - const char* parse_options(char* options); - - protected: - void update_sums(); - - struct op_proba - { - const char* name; - int min_n; - double proba; - typedef formula (*builder)(const random_formula* rl, int n); - builder build; - void setup(const char* name, int min_n, builder build); - }; - unsigned proba_size_; - op_proba* proba_; - double total_1_; - op_proba* proba_2_; - double total_2_; - op_proba* proba_2_or_more_; - double total_2_and_more_; - const atomic_prop_set* ap_; - }; - - - /// \ingroup ltl_io - /// \brief Generate random LTL formulae. - /// - /// This class recursively constructs LTL formulae of a given - /// size. The formulae will use the use atomic propositions from - /// the set of propositions passed to the constructor, in addition - /// to the constant and all LTL operators supported by Spot. - /// - /// By default each operator has equal chance to be selected. - /// Also, each atomic proposition has as much chance as each - /// constant (i.e., true and false) to be picked. This can be - /// tuned using parse_options(). - class SPOT_API random_ltl: public random_formula { - public: - /// Create a random LTL generator using atomic propositions from \a ap. - /// - /// The default priorities are defined as follows: - /// - /** \verbatim - ap n - false 1 - true 1 - not 1 - F 1 - G 1 - X 1 - equiv 1 - implies 1 - xor 1 - R 1 - U 1 - W 1 - M 1 - and 1 - or 1 - \endverbatim */ - /// - /// Where \c n is the number of atomic propositions in the - /// set passed to the constructor. - /// - /// This means that each operator has equal chance to be - /// selected. Also, each atomic proposition has as much chance - /// as each constant (i.e., true and false) to be picked. - /// - /// These priorities can be changed use the parse_options method. - random_ltl(const atomic_prop_set* ap); + return ap_; + } - protected: - void setup_proba_(); - random_ltl(int size, const atomic_prop_set* ap); - }; + /// \brief Generate a formula of size \a n. + /// + /// It is possible to obtain formulae that are smaller than \a + /// n, because some simple simplifications are performed by the + /// AST. (For instance the formula a | a is + /// automatically reduced to a by spot::multop.) + formula generate(int n) const; - /// \ingroup ltl_io - /// \brief Generate random Boolean formulae. + /// \brief Print the priorities of each operator, constants, + /// and atomic propositions. + std::ostream& dump_priorities(std::ostream& os) const; + + /// \brief Update the priorities used to generate the formulae. /// - /// This class recursively constructs Boolean formulae of a given size. - /// The formulae will use the use atomic propositions from the - /// set of propositions passed to the constructor, in addition to the - /// constant and all Boolean operators supported by Spot. - /// - /// By default each operator has equal chance to be selected. - class SPOT_API random_boolean: public random_formula + /// \a options should be comma-separated list of KEY=VALUE + /// assignments, using keys from the above list. + /// For instance "xor=0, F=3" will prevent \c xor + /// from being used, and will raise the relative probability of + /// occurrences of the \c F operator. + const char* parse_options(char* options); + + protected: + void update_sums(); + + struct op_proba { - public: - /// Create a random Boolean formula generator using atomic - /// propositions from \a ap. - /// - /// The default priorities are defined as follows: - /// - /** \verbatim - ap n - false 1 - true 1 - not 1 - equiv 1 - implies 1 - xor 1 - and 1 - or 1 - \endverbatim */ - /// - /// Where \c n is the number of atomic propositions in the - /// set passed to the constructor. - /// - /// This means that each operator has equal chance to be - /// selected. Also, each atomic proposition has as much chance - /// as each constant (i.e., true and false) to be picked. - /// - /// These priorities can be changed use the parse_options method. - random_boolean(const atomic_prop_set* ap); + const char* name; + int min_n; + double proba; + typedef formula (*builder)(const random_formula* rl, int n); + builder build; + void setup(const char* name, int min_n, builder build); }; + unsigned proba_size_; + op_proba* proba_; + double total_1_; + op_proba* proba_2_; + double total_2_; + op_proba* proba_2_or_more_; + double total_2_and_more_; + const atomic_prop_set* ap_; + }; - /// \ingroup ltl_io - /// \brief Generate random SERE. + + /// \ingroup ltl_io + /// \brief Generate random LTL formulae. + /// + /// This class recursively constructs LTL formulae of a given + /// size. The formulae will use the use atomic propositions from + /// the set of propositions passed to the constructor, in addition + /// to the constant and all LTL operators supported by Spot. + /// + /// By default each operator has equal chance to be selected. + /// Also, each atomic proposition has as much chance as each + /// constant (i.e., true and false) to be picked. This can be + /// tuned using parse_options(). + class SPOT_API random_ltl: public random_formula + { + public: + /// Create a random LTL generator using atomic propositions from \a ap. /// - /// This class recursively constructs SERE of a given size. - /// The formulae will use the use atomic propositions from the - /// set of propositions passed to the constructor, in addition to the - /// constant and all SERE operators supported by Spot. + /// The default priorities are defined as follows: /// - /// By default each operator has equal chance to be selected. - class SPOT_API random_sere: public random_formula - { - public: - /// Create a random SERE genere using atomic propositions from \a ap. - /// - /// The default priorities are defined as follows: - /// - /** \verbatim - eword 1 - boolform 1 - star 1 - star_b 1 - equal_b 1 - goto_b 1 - and 1 - andNLM 1 - or 1 - concat 1 - fusion 1 - \endverbatim */ - /// - /// Where "boolfrom" designates a Boolean formula generated - /// by random_boolean. - /// - /// These priorities can be changed use the parse_options method. - /// - /// In addition, you can set the properties of the Boolean - /// formula generator used to build Boolean subformulae using - /// the parse_options method of the \c rb attribute. - random_sere(const atomic_prop_set* ap); - - random_boolean rb; - }; - - /// \ingroup ltl_io - /// \brief Generate random PSL formulae. + /** \verbatim + ap n + false 1 + true 1 + not 1 + F 1 + G 1 + X 1 + equiv 1 + implies 1 + xor 1 + R 1 + U 1 + W 1 + M 1 + and 1 + or 1 + \endverbatim */ /// - /// This class recursively constructs PSL formulae of a given size. - /// The formulae will use the use atomic propositions from the - /// set of propositions passed to the constructor, in addition to the - /// constant and all PSL operators supported by Spot. - class SPOT_API random_psl: public random_ltl - { - public: - /// Create a random PSL generator using atomic propositions from \a ap. - /// - /// PSL formulae are built by combining LTL operators, plus - /// three operators (EConcat, UConcat, Closure) taking a SERE - /// as parameter. - /// - /// The default priorities are defined as follows: - /// - /** \verbatim - ap n - false 1 - true 1 - not 1 - F 1 - G 1 - X 1 - Closure 1 - equiv 1 - implies 1 - xor 1 - R 1 - U 1 - W 1 - M 1 - and 1 - or 1 - EConcat 1 - UConcat 1 - \endverbatim */ - /// - /// Where \c n is the number of atomic propositions in the - /// set passed to the constructor. - /// - /// This means that each operator has equal chance to be - /// selected. Also, each atomic proposition has as much chance - /// as each constant (i.e., true and false) to be picked. - /// - /// These priorities can be changed use the parse_options method. - /// - /// In addition, you can set the properties of the SERE generator - /// used to build SERE subformulae using the parse_options method - /// of the \c rs attribute. - random_psl(const atomic_prop_set* ap); + /// Where \c n is the number of atomic propositions in the + /// set passed to the constructor. + /// + /// This means that each operator has equal chance to be + /// selected. Also, each atomic proposition has as much chance + /// as each constant (i.e., true and false) to be picked. + /// + /// These priorities can be changed use the parse_options method. + random_ltl(const atomic_prop_set* ap); - /// The SERE generator used to generate SERE subformulae. - random_sere rs; - }; + protected: + void setup_proba_(); + random_ltl(int size, const atomic_prop_set* ap); + }; - class SPOT_API randltlgenerator - { - typedef std::unordered_set fset_t; + /// \ingroup ltl_io + /// \brief Generate random Boolean formulae. + /// + /// This class recursively constructs Boolean formulae of a given size. + /// The formulae will use the use atomic propositions from the + /// set of propositions passed to the constructor, in addition to the + /// constant and all Boolean operators supported by Spot. + /// + /// By default each operator has equal chance to be selected. + class SPOT_API random_boolean: public random_formula + { + public: + /// Create a random Boolean formula generator using atomic + /// propositions from \a ap. + /// + /// The default priorities are defined as follows: + /// + /** \verbatim + ap n + false 1 + true 1 + not 1 + equiv 1 + implies 1 + xor 1 + and 1 + or 1 + \endverbatim */ + /// + /// Where \c n is the number of atomic propositions in the + /// set passed to the constructor. + /// + /// This means that each operator has equal chance to be + /// selected. Also, each atomic proposition has as much chance + /// as each constant (i.e., true and false) to be picked. + /// + /// These priorities can be changed use the parse_options method. + random_boolean(const atomic_prop_set* ap); + }; + + /// \ingroup ltl_io + /// \brief Generate random SERE. + /// + /// This class recursively constructs SERE of a given size. + /// The formulae will use the use atomic propositions from the + /// set of propositions passed to the constructor, in addition to the + /// constant and all SERE operators supported by Spot. + /// + /// By default each operator has equal chance to be selected. + class SPOT_API random_sere: public random_formula + { + public: + /// Create a random SERE genere using atomic propositions from \a ap. + /// + /// The default priorities are defined as follows: + /// + /** \verbatim + eword 1 + boolform 1 + star 1 + star_b 1 + equal_b 1 + goto_b 1 + and 1 + andNLM 1 + or 1 + concat 1 + fusion 1 + \endverbatim */ + /// + /// Where "boolfrom" designates a Boolean formula generated + /// by random_boolean. + /// + /// These priorities can be changed use the parse_options method. + /// + /// In addition, you can set the properties of the Boolean + /// formula generator used to build Boolean subformulae using + /// the parse_options method of the \c rb attribute. + random_sere(const atomic_prop_set* ap); + + random_boolean rb; + }; + + /// \ingroup ltl_io + /// \brief Generate random PSL formulae. + /// + /// This class recursively constructs PSL formulae of a given size. + /// The formulae will use the use atomic propositions from the + /// set of propositions passed to the constructor, in addition to the + /// constant and all PSL operators supported by Spot. + class SPOT_API random_psl: public random_ltl + { + public: + /// Create a random PSL generator using atomic propositions from \a ap. + /// + /// PSL formulae are built by combining LTL operators, plus + /// three operators (EConcat, UConcat, Closure) taking a SERE + /// as parameter. + /// + /// The default priorities are defined as follows: + /// + /** \verbatim + ap n + false 1 + true 1 + not 1 + F 1 + G 1 + X 1 + Closure 1 + equiv 1 + implies 1 + xor 1 + R 1 + U 1 + W 1 + M 1 + and 1 + or 1 + EConcat 1 + UConcat 1 + \endverbatim */ + /// + /// Where \c n is the number of atomic propositions in the + /// set passed to the constructor. + /// + /// This means that each operator has equal chance to be + /// selected. Also, each atomic proposition has as much chance + /// as each constant (i.e., true and false) to be picked. + /// + /// These priorities can be changed use the parse_options method. + /// + /// In addition, you can set the properties of the SERE generator + /// used to build SERE subformulae using the parse_options method + /// of the \c rs attribute. + random_psl(const atomic_prop_set* ap); + + /// The SERE generator used to generate SERE subformulae. + random_sere rs; + }; + + class SPOT_API randltlgenerator + { + typedef std::unordered_set fset_t; - public: - randltlgenerator(int aprops_n, const option_map& opts, - char* opt_pL = nullptr, - char* opt_pS = nullptr, - char* opt_pB = nullptr); + public: + randltlgenerator(int aprops_n, const option_map& opts, + char* opt_pL = nullptr, + char* opt_pS = nullptr, + char* opt_pB = nullptr); - randltlgenerator(atomic_prop_set aprops, const option_map& opts, - char* opt_pL = nullptr, - char* opt_pS = nullptr, - char* opt_pB = nullptr); + randltlgenerator(atomic_prop_set aprops, const option_map& opts, + char* opt_pL = nullptr, + char* opt_pS = nullptr, + char* opt_pB = nullptr); - ~randltlgenerator(); + ~randltlgenerator(); - formula next(); + formula next(); - void dump_ltl_priorities(std::ostream& os); - void dump_bool_priorities(std::ostream& os); - void dump_psl_priorities(std::ostream& os); - void dump_sere_priorities(std::ostream& os); - void dump_sere_bool_priorities(std::ostream& os); - void remove_some_props(atomic_prop_set& s); + void dump_ltl_priorities(std::ostream& os); + void dump_bool_priorities(std::ostream& os); + void dump_psl_priorities(std::ostream& os); + void dump_sere_priorities(std::ostream& os); + void dump_sere_bool_priorities(std::ostream& os); + void remove_some_props(atomic_prop_set& s); - formula GF_n(); + formula GF_n(); - private: - fset_t unique_set_; - atomic_prop_set aprops_; + private: + fset_t unique_set_; + atomic_prop_set aprops_; - int opt_seed_; - int opt_tree_size_min_; - int opt_tree_size_max_; - bool opt_unique_; - bool opt_wf_; - int opt_simpl_level_; - ltl_simplifier simpl_; + int opt_seed_; + int opt_tree_size_min_; + int opt_tree_size_max_; + bool opt_unique_; + bool opt_wf_; + int opt_simpl_level_; + ltl_simplifier simpl_; - int output_; + int output_; - random_formula* rf_ = nullptr; - random_psl* rp_ = nullptr; - random_sere* rs_ = nullptr; - }; - } + random_formula* rf_ = nullptr; + random_psl* rp_ = nullptr; + random_sere* rs_ = nullptr; + }; } diff --git a/src/tl/relabel.cc b/src/tl/relabel.cc index f34c0cad4..a3a05bab3 100644 --- a/src/tl/relabel.cc +++ b/src/tl/relabel.cc @@ -27,461 +27,457 @@ namespace spot { - namespace ltl + ////////////////////////////////////////////////////////////////////// + // Basic relabeler + ////////////////////////////////////////////////////////////////////// + + namespace { - - ////////////////////////////////////////////////////////////////////// - // Basic relabeler - ////////////////////////////////////////////////////////////////////// - - namespace + struct ap_generator { - struct ap_generator - { - virtual formula next() = 0; - virtual ~ap_generator() {} - }; + virtual formula next() = 0; + virtual ~ap_generator() {} + }; - struct pnn_generator final: ap_generator - { - unsigned nn; - pnn_generator() - : nn(0) - { - } - - formula next() - { - std::ostringstream s; - s << 'p' << nn++; - return formula::ap(s.str()); - } - }; - - struct abc_generator final: ap_generator - { - public: - abc_generator() - : nn(0) - { - } - - unsigned nn; - - formula next() - { - std::string s; - unsigned n = nn++; - do - { - s.push_back('a' + (n % 26)); - n /= 26; - } - while (n); - return formula::ap(s); - } - }; - - - class relabeler - { - public: - typedef std::unordered_map map; - map newname; - ap_generator* gen; - relabeling_map* oldnames; - - relabeler(ap_generator* gen, relabeling_map* m) - : gen(gen), oldnames(m) - { - } - - ~relabeler() - { - delete gen; - } - - formula rename(formula old) - { - auto r = newname.emplace(old, nullptr); - if (!r.second) - { - return r.first->second; - } - else - { - formula res = gen->next(); - r.first->second = res; - if (oldnames) - (*oldnames)[res] = old; - return res; - } - } - - formula - visit(formula f) - { - if (f.is(op::ap)) - return rename(f); - else - return f.map([this](formula f) - { - return this->visit(f); - }); - } - - }; - - } - - - formula - relabel(formula f, relabeling_style style, relabeling_map* m) + struct pnn_generator final: ap_generator { - ap_generator* gen = nullptr; - switch (style) + unsigned nn; + pnn_generator() + : nn(0) { - case Pnn: - gen = new pnn_generator; - break; - case Abc: - gen = new abc_generator; - break; } - relabeler r(gen, m); - return r.visit(f); - } - - ////////////////////////////////////////////////////////////////////// - // Boolean-subexpression relabeler - ////////////////////////////////////////////////////////////////////// - - // Here we want to rewrite a formula such as - // "a & b & X(c & d) & GF(c & d)" into "p0 & Xp1 & GFp1" - // where Boolean subexpressions are replaced by fresh propositions. - // - // Detecting Boolean subexpressions is not a problem. - // Furthermore, because we are already representing LTL formulas - // with sharing of identical sub-expressions we can easily rename - // a subexpression (such as c&d above) only once. However this - // scheme has two problems: - // - // 1. It will not detect inter-dependent Boolean subexpressions. - // For instance it will mistakenly relabel "(a & b) U (a & !b)" - // as "p0 U p1", hiding the dependency between a&b and a&!b. - // - // 2. Because of our n-ary operators, it will fail to - // notice that (a & b) is a sub-expression of (a & b & c). - // - // The code below only addresses point 1 so that interdependent - // subexpressions are not relabeled. Point 2 could be improved in - // a future version of somebody feels inclined to do so. - // - // The way we compute the subexpressions that can be relabeled is - // by transforming the formula syntax tree into an undirected - // graph, and computing the cut points of this graph. The cut - // points (or articulation points) are the nodes whose removal - // would split the graph in two components. To ensure that a - // Boolean operator is only considered as a cut point if it would - // separate all of its children from the rest of the graph, we - // connect all the children of Boolean operators. - // - // For instance (a & b) U (c & d) has two (Boolean) cut points - // corresponding to the two AND operators: - // - // (a&b)U(c&d) - // ╱ ╲ - // a&b c&d - // ╱ ╲ ╱ ╲ - // a─────b c─────d - // - // (The root node is also a cut-point, but we only consider Boolean - // cut-points for relabeling.) - // - // On the other hand, (a & b) U (b & !c) has only one Boolean - // cut-point which corresponds to the NOT operator: - // - // (a&b)U(b&!c) - // ╱ ╲ - // a&b b&c - // ╱ ╲ ╱ ╲ - // a─────b────!c - // │ - // c - // - // Note that if the children of a&b and b&c were not connected, - // a&b and b&c would be considered as cut points because they - // separate "a" or "!c" from the rest of the graph. - // - // The relabeling of a formula is therefore done in 3 passes: - // 1. convert the formula's syntax tree into an undirected graph, - // adding links between children of Boolean operators - // 2. compute the (Boolean) cut points of that graph, using the - // Hopcroft-Tarjan algorithm (see below for a reference) - // 3. recursively scan the formula's tree until we reach - // either a (Boolean) cut point or an atomic proposition, and - // replace that node by a fresh atomic proposition. - // - // In the example above (a&b)U(b&!c), the last recursion - // stop a, b, and !c, producing (p0&p1)U(p1&p2). - namespace - { - typedef std::vector succ_vec; - typedef std::map fgraph; - - // Convert the formula's syntax tree into an undirected graph - // labeled by subformulas. - class formula_to_fgraph final + formula next() { - public: - fgraph& g; - std::stack s; + std::ostringstream s; + s << 'p' << nn++; + return formula::ap(s.str()); + } + }; - formula_to_fgraph(fgraph& g): - g(g) + struct abc_generator final: ap_generator + { + public: + abc_generator() + : nn(0) { } - ~formula_to_fgraph() - { - } + unsigned nn; - void - visit(formula f) - { + formula next() + { + std::string s; + unsigned n = nn++; + do { - // Connect to parent - auto in = g.emplace(f, succ_vec()); - if (!s.empty()) - { - formula top = s.top(); - in.first->second.push_back(top); - g[top].push_back(f); - if (!in.second) - return; - } - else - { - assert(in.second); - } + s.push_back('a' + (n % 26)); + n /= 26; } - s.push(f); + while (n); + return formula::ap(s); + } + }; - unsigned sz = f.size(); - unsigned i = 0; - if (sz > 2 && !f.is_boolean()) + + class relabeler + { + public: + typedef std::unordered_map map; + map newname; + ap_generator* gen; + relabeling_map* oldnames; + + relabeler(ap_generator* gen, relabeling_map* m) + : gen(gen), oldnames(m) + { + } + + ~relabeler() + { + delete gen; + } + + formula rename(formula old) + { + auto r = newname.emplace(old, nullptr); + if (!r.second) + { + return r.first->second; + } + else + { + formula res = gen->next(); + r.first->second = res; + if (oldnames) + (*oldnames)[res] = old; + return res; + } + } + + formula + visit(formula f) + { + if (f.is(op::ap)) + return rename(f); + else + return f.map([this](formula f) + { + return this->visit(f); + }); + } + + }; + + } + + + formula + relabel(formula f, relabeling_style style, relabeling_map* m) + { + ap_generator* gen = nullptr; + switch (style) + { + case Pnn: + gen = new pnn_generator; + break; + case Abc: + gen = new abc_generator; + break; + } + + relabeler r(gen, m); + return r.visit(f); + } + + ////////////////////////////////////////////////////////////////////// + // Boolean-subexpression relabeler + ////////////////////////////////////////////////////////////////////// + + // Here we want to rewrite a formula such as + // "a & b & X(c & d) & GF(c & d)" into "p0 & Xp1 & GFp1" + // where Boolean subexpressions are replaced by fresh propositions. + // + // Detecting Boolean subexpressions is not a problem. + // Furthermore, because we are already representing LTL formulas + // with sharing of identical sub-expressions we can easily rename + // a subexpression (such as c&d above) only once. However this + // scheme has two problems: + // + // 1. It will not detect inter-dependent Boolean subexpressions. + // For instance it will mistakenly relabel "(a & b) U (a & !b)" + // as "p0 U p1", hiding the dependency between a&b and a&!b. + // + // 2. Because of our n-ary operators, it will fail to + // notice that (a & b) is a sub-expression of (a & b & c). + // + // The code below only addresses point 1 so that interdependent + // subexpressions are not relabeled. Point 2 could be improved in + // a future version of somebody feels inclined to do so. + // + // The way we compute the subexpressions that can be relabeled is + // by transforming the formula syntax tree into an undirected + // graph, and computing the cut points of this graph. The cut + // points (or articulation points) are the nodes whose removal + // would split the graph in two components. To ensure that a + // Boolean operator is only considered as a cut point if it would + // separate all of its children from the rest of the graph, we + // connect all the children of Boolean operators. + // + // For instance (a & b) U (c & d) has two (Boolean) cut points + // corresponding to the two AND operators: + // + // (a&b)U(c&d) + // ╱ ╲ + // a&b c&d + // ╱ ╲ ╱ ╲ + // a─────b c─────d + // + // (The root node is also a cut-point, but we only consider Boolean + // cut-points for relabeling.) + // + // On the other hand, (a & b) U (b & !c) has only one Boolean + // cut-point which corresponds to the NOT operator: + // + // (a&b)U(b&!c) + // ╱ ╲ + // a&b b&c + // ╱ ╲ ╱ ╲ + // a─────b────!c + // │ + // c + // + // Note that if the children of a&b and b&c were not connected, + // a&b and b&c would be considered as cut points because they + // separate "a" or "!c" from the rest of the graph. + // + // The relabeling of a formula is therefore done in 3 passes: + // 1. convert the formula's syntax tree into an undirected graph, + // adding links between children of Boolean operators + // 2. compute the (Boolean) cut points of that graph, using the + // Hopcroft-Tarjan algorithm (see below for a reference) + // 3. recursively scan the formula's tree until we reach + // either a (Boolean) cut point or an atomic proposition, and + // replace that node by a fresh atomic proposition. + // + // In the example above (a&b)U(b&!c), the last recursion + // stop a, b, and !c, producing (p0&p1)U(p1&p2). + namespace + { + typedef std::vector succ_vec; + typedef std::map fgraph; + + // Convert the formula's syntax tree into an undirected graph + // labeled by subformulas. + class formula_to_fgraph final + { + public: + fgraph& g; + std::stack s; + + formula_to_fgraph(fgraph& g): + g(g) + { + } + + ~formula_to_fgraph() + { + } + + void + visit(formula f) + { + { + // Connect to parent + auto in = g.emplace(f, succ_vec()); + if (!s.empty()) { - /// If we have a formula like (a & b & Xc), consider - /// it as ((a & b) & Xc) in the graph to isolate the - /// Boolean operands as a single node. - formula b = f.boolean_operands(&i); - if (b) - visit(b); + formula top = s.top(); + in.first->second.push_back(top); + g[top].push_back(f); + if (!in.second) + return; } - for (; i < sz; ++i) - visit(f[i]); - if (sz > 1 && f.is_boolean()) + else { - // For Boolean nodes, connect all children in a - // loop. This way the node can only be a cut-point - // if it separates all children from the reset of - // the graph (not only one). - formula pred = f[0]; - for (i = 1; i < sz; ++i) + assert(in.second); + } + } + s.push(f); + + unsigned sz = f.size(); + unsigned i = 0; + if (sz > 2 && !f.is_boolean()) + { + /// If we have a formula like (a & b & Xc), consider + /// it as ((a & b) & Xc) in the graph to isolate the + /// Boolean operands as a single node. + formula b = f.boolean_operands(&i); + if (b) + visit(b); + } + for (; i < sz; ++i) + visit(f[i]); + if (sz > 1 && f.is_boolean()) + { + // For Boolean nodes, connect all children in a + // loop. This way the node can only be a cut-point + // if it separates all children from the reset of + // the graph (not only one). + formula pred = f[0]; + for (i = 1; i < sz; ++i) + { + formula next = f[i]; + // Note that we only add an edge in one + // direction, because we are building a cycle + // between all children anyway. + g[pred].push_back(next); + pred = next; + } + g[pred].push_back(f[0]); + } + s.pop(); + } + }; + + + typedef std::set fset; + struct data_entry // for each node of the graph + { + unsigned num; // serial number, in pre-order + unsigned low; // lowest number accessible via unstacked descendants + data_entry(unsigned num = 0, unsigned low = 0) + : num(num), low(low) + { + } + }; + typedef std::unordered_map fmap_t; + struct stack_entry + { + formula grand_parent; + formula parent; // current node + succ_vec::const_iterator current_child; + succ_vec::const_iterator last_child; + }; + typedef std::stack stack_t; + + // Fill c with the Boolean cutpoints of g, starting from start. + // + // This is based no "Efficient Algorithms for Graph + // Manipulation", J. Hopcroft & R. Tarjan, in Communications of + // the ACM, 16 (6), June 1973. + // + // It differs from the original algorithm by returning only the + // Boolean cutpoints, and not dealing with the initial state + // properly (our initial state will always be considered as a + // cut-point, but since we only return Boolean cut-points it's + // OK: if the top-most formula is Boolean we want to replace it + // as a whole). + void cut_points(const fgraph& g, fset& c, formula start) + { + stack_t s; + + unsigned num = 0; + fmap_t data; + data_entry d = { num, num }; + data[start] = d; + ++num; + const succ_vec& children = g.find(start)->second; + stack_entry e = { start, start, children.begin(), children.end() }; + s.push(e); + + while (!s.empty()) + { + stack_entry& e = s.top(); + if (e.current_child != e.last_child) + { + // Skip the edge if it is just the reverse of the one + // we took. + formula child = *e.current_child; + if (child == e.grand_parent) { - formula next = f[i]; - // Note that we only add an edge in one - // direction, because we are building a cycle - // between all children anyway. - g[pred].push_back(next); - pred = next; + ++e.current_child; + continue; } - g[pred].push_back(f[0]); - } - s.pop(); - } - }; - - - typedef std::set fset; - struct data_entry // for each node of the graph - { - unsigned num; // serial number, in pre-order - unsigned low; // lowest number accessible via unstacked descendants - data_entry(unsigned num = 0, unsigned low = 0) - : num(num), low(low) - { - } - }; - typedef std::unordered_map fmap_t; - struct stack_entry - { - formula grand_parent; - formula parent; // current node - succ_vec::const_iterator current_child; - succ_vec::const_iterator last_child; - }; - typedef std::stack stack_t; - - // Fill c with the Boolean cutpoints of g, starting from start. - // - // This is based no "Efficient Algorithms for Graph - // Manipulation", J. Hopcroft & R. Tarjan, in Communications of - // the ACM, 16 (6), June 1973. - // - // It differs from the original algorithm by returning only the - // Boolean cutpoints, and not dealing with the initial state - // properly (our initial state will always be considered as a - // cut-point, but since we only return Boolean cut-points it's - // OK: if the top-most formula is Boolean we want to replace it - // as a whole). - void cut_points(const fgraph& g, fset& c, formula start) - { - stack_t s; - - unsigned num = 0; - fmap_t data; - data_entry d = { num, num }; - data[start] = d; - ++num; - const succ_vec& children = g.find(start)->second; - stack_entry e = { start, start, children.begin(), children.end() }; - s.push(e); - - while (!s.empty()) - { - stack_entry& e = s.top(); - if (e.current_child != e.last_child) - { - // Skip the edge if it is just the reverse of the one - // we took. - formula child = *e.current_child; - if (child == e.grand_parent) - { - ++e.current_child; - continue; - } - auto i = data.emplace(std::piecewise_construct, - std::forward_as_tuple(child), - std::forward_as_tuple(num, num)); - if (i.second) // New destination. - { - ++num; - const succ_vec& children = g.find(child)->second; - stack_entry newe = { e.parent, child, - children.begin(), children.end() }; - s.push(newe); - } - else // Destination exists. - { - data_entry& dparent = data[e.parent]; - data_entry& dchild = i.first->second; - // If this is a back-edge, update - // the low field of the parent. - if (dchild.num <= dparent.num) - if (dparent.low > dchild.num) - dparent.low = dchild.num; - } - ++e.current_child; - } - else - { - formula grand_parent = e.grand_parent; - formula parent = e.parent; - s.pop(); - if (!s.empty()) - { - data_entry& dparent = data[parent]; - data_entry& dgrand_parent = data[grand_parent]; - if (dparent.low >= dgrand_parent.num // cut-point - && grand_parent.is_boolean()) - c.insert(grand_parent); - if (dparent.low < dgrand_parent.low) - dgrand_parent.low = dparent.low; - } - } - } - } - - - class bse_relabeler final: public relabeler - { - public: - fset& c; - bse_relabeler(ap_generator* gen, fset& c, - relabeling_map* m) - : relabeler(gen, m), c(c) - { - } - - using relabeler::visit; - - formula - visit(formula f) - { - if (f.is(op::ap) || (c.find(f) != c.end())) - return rename(f); - - unsigned sz = f.size(); - if (sz <= 2) - return f.map([this](formula f) - { - return visit(f); - }); - - unsigned i = 0; - std::vector res; - /// If we have a formula like (a & b & Xc), consider - /// it as ((a & b) & Xc) in the graph to isolate the - /// Boolean operands as a single node. - formula b = f.boolean_operands(&i); - if (b) - { - res.reserve(sz - i + 1); - res.push_back(visit(b)); + auto i = data.emplace(std::piecewise_construct, + std::forward_as_tuple(child), + std::forward_as_tuple(num, num)); + if (i.second) // New destination. + { + ++num; + const succ_vec& children = g.find(child)->second; + stack_entry newe = { e.parent, child, + children.begin(), children.end() }; + s.push(newe); + } + else // Destination exists. + { + data_entry& dparent = data[e.parent]; + data_entry& dchild = i.first->second; + // If this is a back-edge, update + // the low field of the parent. + if (dchild.num <= dparent.num) + if (dparent.low > dchild.num) + dparent.low = dchild.num; + } + ++e.current_child; } else { - res.reserve(sz); + formula grand_parent = e.grand_parent; + formula parent = e.parent; + s.pop(); + if (!s.empty()) + { + data_entry& dparent = data[parent]; + data_entry& dgrand_parent = data[grand_parent]; + if (dparent.low >= dgrand_parent.num // cut-point + && grand_parent.is_boolean()) + c.insert(grand_parent); + if (dparent.low < dgrand_parent.low) + dgrand_parent.low = dparent.low; + } } - for (; i < sz; ++i) - res.push_back(visit(f[i])); - return formula::multop(f.kind(), res); } - }; } - formula - relabel_bse(formula f, relabeling_style style, relabeling_map* m) + class bse_relabeler final: public relabeler { - fgraph g; - - // Build the graph g from the formula f. + public: + fset& c; + bse_relabeler(ap_generator* gen, fset& c, + relabeling_map* m) + : relabeler(gen, m), c(c) { - formula_to_fgraph conv(g); - conv.visit(f); } - // Compute its cut-points - fset c; - cut_points(g, c, f); + using relabeler::visit; - // Relabel the formula recursively, stopping - // at cut-points or atomic propositions. - ap_generator* gen = nullptr; - switch (style) - { - case Pnn: - gen = new pnn_generator; - break; - case Abc: - gen = new abc_generator; - break; - } - bse_relabeler rel(gen, c, m); - return rel.visit(f); + formula + visit(formula f) + { + if (f.is(op::ap) || (c.find(f) != c.end())) + return rename(f); + + unsigned sz = f.size(); + if (sz <= 2) + return f.map([this](formula f) + { + return visit(f); + }); + + unsigned i = 0; + std::vector res; + /// If we have a formula like (a & b & Xc), consider + /// it as ((a & b) & Xc) in the graph to isolate the + /// Boolean operands as a single node. + formula b = f.boolean_operands(&i); + if (b) + { + res.reserve(sz - i + 1); + res.push_back(visit(b)); + } + else + { + res.reserve(sz); + } + for (; i < sz; ++i) + res.push_back(visit(f[i])); + return formula::multop(f.kind(), res); + } + }; + } + + + formula + relabel_bse(formula f, relabeling_style style, relabeling_map* m) + { + fgraph g; + + // Build the graph g from the formula f. + { + formula_to_fgraph conv(g); + conv.visit(f); } + + // Compute its cut-points + fset c; + cut_points(g, c, f); + + // Relabel the formula recursively, stopping + // at cut-points or atomic propositions. + ap_generator* gen = nullptr; + switch (style) + { + case Pnn: + gen = new pnn_generator; + break; + case Abc: + gen = new abc_generator; + break; + } + bse_relabeler rel(gen, c, m); + return rel.visit(f); } } diff --git a/src/tl/relabel.hh b/src/tl/relabel.hh index e26b1b234..1c1638e2b 100644 --- a/src/tl/relabel.hh +++ b/src/tl/relabel.hh @@ -25,30 +25,27 @@ namespace spot { - namespace ltl - { - enum relabeling_style { Abc, Pnn }; + enum relabeling_style { Abc, Pnn }; - typedef std::map relabeling_map; + typedef std::map relabeling_map; - /// \ingroup ltl_rewriting - /// \brief Relabel the atomic propositions in a formula. - /// - /// If \a m is non-null, it is filled with correspondence - /// between the new names (keys) and the old names (values). - SPOT_API - formula relabel(formula f, relabeling_style style, - relabeling_map* m = nullptr); + /// \ingroup ltl_rewriting + /// \brief Relabel the atomic propositions in a formula. + /// + /// If \a m is non-null, it is filled with correspondence + /// between the new names (keys) and the old names (values). + SPOT_API + formula relabel(formula f, relabeling_style style, + relabeling_map* m = nullptr); - /// \ingroup ltl_rewriting - /// \brief Relabel Boolean subexpressions in a formula using - /// atomic propositions. - /// - /// If \a m is non-null, it is filled with correspondence - /// between the new names (keys) and the old names (values). - SPOT_API - formula relabel_bse(formula f, relabeling_style style, - relabeling_map* m = nullptr); - } + /// \ingroup ltl_rewriting + /// \brief Relabel Boolean subexpressions in a formula using + /// atomic propositions. + /// + /// If \a m is non-null, it is filled with correspondence + /// between the new names (keys) and the old names (values). + SPOT_API + formula relabel_bse(formula f, relabeling_style style, + relabeling_map* m = nullptr); } diff --git a/src/tl/remove_x.cc b/src/tl/remove_x.cc index f2700173d..5e662fdb3 100644 --- a/src/tl/remove_x.cc +++ b/src/tl/remove_x.cc @@ -23,80 +23,77 @@ namespace spot { - namespace ltl + namespace { - namespace - { - static formula - remove_x_rec(formula f, atomic_prop_set& aps) - { - if (f.is_syntactic_stutter_invariant()) - return f; - - auto rec = [&aps](formula f) - { - return remove_x_rec(f, aps); - }; - - if (!f.is(op::X)) - return f.map(rec); - - formula c = rec(f[0]); - - std::vector vo; - for (auto i: aps) - { - // First line - std::vector va1; - formula npi = formula::Not(i); - va1.push_back(i); - va1.push_back(formula::U(i, formula::And({npi, c}))); - - for (auto j: aps) - if (j != i) - { - // make sure the arguments of OR are created in a - // deterministic order - auto tmp = formula::U(formula::Not(j), npi); - va1.push_back(formula::Or({formula::U(j, npi), tmp})); - } - vo.push_back(formula::And(va1)); - // Second line - std::vector va2; - va2.push_back(npi); - va2.push_back(formula::U(npi, formula::And({i, c}))); - for (auto j: aps) - if (j != i) - { - // make sure the arguments of OR are created in a - // deterministic order - auto tmp = formula::U(formula::Not(j), i); - va2.push_back(formula::Or({formula::U(j, i), tmp})); - } - vo.push_back(formula::And(va2)); - } - // Third line - std::vector va3; - for (auto i: aps) - { - // make sure the arguments of OR are created in a - // deterministic order - auto tmp = formula::G(formula::Not(i)); - va3.push_back(formula::Or({formula::G(i), tmp})); - } - va3.push_back(c); - vo.push_back(formula::And(va3)); - return formula::Or(vo); - } - } - - formula remove_x(formula f) + static formula + remove_x_rec(formula f, atomic_prop_set& aps) { if (f.is_syntactic_stutter_invariant()) return f; - atomic_prop_set aps; - atomic_prop_collect(f, &aps); - return remove_x_rec(f, aps); + + auto rec = [&aps](formula f) + { + return remove_x_rec(f, aps); + }; + + if (!f.is(op::X)) + return f.map(rec); + + formula c = rec(f[0]); + + std::vector vo; + for (auto i: aps) + { + // First line + std::vector va1; + formula npi = formula::Not(i); + va1.push_back(i); + va1.push_back(formula::U(i, formula::And({npi, c}))); + + for (auto j: aps) + if (j != i) + { + // make sure the arguments of OR are created in a + // deterministic order + auto tmp = formula::U(formula::Not(j), npi); + va1.push_back(formula::Or({formula::U(j, npi), tmp})); + } + vo.push_back(formula::And(va1)); + // Second line + std::vector va2; + va2.push_back(npi); + va2.push_back(formula::U(npi, formula::And({i, c}))); + for (auto j: aps) + if (j != i) + { + // make sure the arguments of OR are created in a + // deterministic order + auto tmp = formula::U(formula::Not(j), i); + va2.push_back(formula::Or({formula::U(j, i), tmp})); + } + vo.push_back(formula::And(va2)); + } + // Third line + std::vector va3; + for (auto i: aps) + { + // make sure the arguments of OR are created in a + // deterministic order + auto tmp = formula::G(formula::Not(i)); + va3.push_back(formula::Or({formula::G(i), tmp})); + } + va3.push_back(c); + vo.push_back(formula::And(va3)); + return formula::Or(vo); } } + + formula remove_x(formula f) + { + if (f.is_syntactic_stutter_invariant()) + return f; + atomic_prop_set aps; + atomic_prop_collect(f, &aps); + return remove_x_rec(f, aps); + } } diff --git a/src/tl/remove_x.hh b/src/tl/remove_x.hh index dce03c90b..aa8403387 100644 --- a/src/tl/remove_x.hh +++ b/src/tl/remove_x.hh @@ -23,27 +23,24 @@ namespace spot { - namespace ltl - { - /// \brief Rewrite a stutter-insensitive formula \a f without - /// using the X operator. - /// - /// This function may also be applied to stutter-sensitive formulas, - /// but in that case the resulting formula is not equivalent. - /// - /** \verbatim - @Article{ etessami.00.ipl, - author = {Kousha Etessami}, - title = {A note on a question of {P}eled and {W}ilke regarding - stutter-invariant {LTL}}, - journal = {Information Processing Letters}, - volume = {75}, - number = {6}, - year = {2000}, - pages = {261--263} - } - \endverbatim */ - SPOT_API - formula remove_x(formula f); - } + /// \brief Rewrite a stutter-insensitive formula \a f without + /// using the X operator. + /// + /// This function may also be applied to stutter-sensitive formulas, + /// but in that case the resulting formula is not equivalent. + /// + /** \verbatim + @Article{ etessami.00.ipl, + author = {Kousha Etessami}, + title = {A note on a question of {P}eled and {W}ilke regarding + stutter-invariant {LTL}}, + journal = {Information Processing Letters}, + volume = {75}, + number = {6}, + year = {2000}, + pages = {261--263} + } + \endverbatim */ + SPOT_API + formula remove_x(formula f); } diff --git a/src/tl/simpfg.cc b/src/tl/simpfg.cc index 9d5be9003..ed682a56f 100644 --- a/src/tl/simpfg.cc +++ b/src/tl/simpfg.cc @@ -24,24 +24,20 @@ namespace spot { - namespace ltl + formula simplify_f_g(formula p) { - formula simplify_f_g(formula p) - { - // 1 U p = Fp - if (p.is(op::U) && p[0].is_tt()) - return formula::F(p[1]); - // 0 R p = Gp - if (p.is(op::R) && p[0].is_ff()) - return formula::G(p[1]); - // p W 0 = Gp - if (p.is(op::W) && p[1].is_ff()) - return formula::G(p[0]); - // p M 1 = Fp - if (p.is(op::M) && p[1].is_tt()) - return formula::F(p[0]); - return p.map(simplify_f_g); - } - + // 1 U p = Fp + if (p.is(op::U) && p[0].is_tt()) + return formula::F(p[1]); + // 0 R p = Gp + if (p.is(op::R) && p[0].is_ff()) + return formula::G(p[1]); + // p W 0 = Gp + if (p.is(op::W) && p[1].is_ff()) + return formula::G(p[0]); + // p M 1 = Fp + if (p.is(op::M) && p[1].is_tt()) + return formula::F(p[0]); + return p.map(simplify_f_g); } } diff --git a/src/tl/simpfg.hh b/src/tl/simpfg.hh index d2480cdd4..9fa139fc9 100644 --- a/src/tl/simpfg.hh +++ b/src/tl/simpfg.hh @@ -26,19 +26,16 @@ namespace spot { - namespace ltl - { - /// \ingroup ltl_rewriting - /// \brief Replace true U f and false R g by - /// F f and G g. - /// - /// Perform the following rewriting (from left to right): - /// - /// - true U a = F a - /// - a M true = F a - /// - false R a = G a - /// - a W false = G a - /// - SPOT_API formula simplify_f_g(formula f); - } + /// \ingroup ltl_rewriting + /// \brief Replace true U f and false R g by + /// F f and G g. + /// + /// Perform the following rewriting (from left to right): + /// + /// - true U a = F a + /// - a M true = F a + /// - false R a = G a + /// - a W false = G a + /// + SPOT_API formula simplify_f_g(formula f); } diff --git a/src/tl/simplify.cc b/src/tl/simplify.cc index 7feec746b..8abea1d91 100644 --- a/src/tl/simplify.cc +++ b/src/tl/simplify.cc @@ -36,2999 +36,2997 @@ namespace spot { - namespace ltl + typedef std::vector vec; + + // The name of this class is public, but not its contents. + class ltl_simplifier_cache { - typedef std::vector vec; + typedef std::unordered_map f2f_map; + typedef std::unordered_map f2b_map; + typedef std::pair pairf; + typedef std::map syntimpl_cache_t; + public: + bdd_dict_ptr dict; + ltl_simplifier_options options; + language_containment_checker lcc; - // The name of this class is public, but not its contents. - class ltl_simplifier_cache + ~ltl_simplifier_cache() { - typedef std::unordered_map f2f_map; - typedef std::unordered_map f2b_map; - typedef std::pair pairf; - typedef std::map syntimpl_cache_t; - public: - bdd_dict_ptr dict; - ltl_simplifier_options options; - language_containment_checker lcc; + dict->unregister_all_my_variables(this); + } - ~ltl_simplifier_cache() - { - dict->unregister_all_my_variables(this); - } + ltl_simplifier_cache(const bdd_dict_ptr& d) + : dict(d), lcc(d, true, true, false, false) + { + } - ltl_simplifier_cache(const bdd_dict_ptr& d) - : dict(d), lcc(d, true, true, false, false) - { - } + ltl_simplifier_cache(const bdd_dict_ptr& d, + const ltl_simplifier_options& opt) + : dict(d), options(opt), lcc(d, true, true, false, false) + { + options.containment_checks |= options.containment_checks_stronger; + options.event_univ |= options.favor_event_univ; + } - ltl_simplifier_cache(const bdd_dict_ptr& d, - const ltl_simplifier_options& opt) - : dict(d), options(opt), lcc(d, true, true, false, false) - { - options.containment_checks |= options.containment_checks_stronger; - options.event_univ |= options.favor_event_univ; - } + void + print_stats(std::ostream& os) const + { + os << "simplified formulae: " << simplified_.size() << " entries\n" + << "negative normal form: " << nenoform_.size() << " entries\n" + << "syntactic implications: " << syntimpl_.size() << " entries\n" + << "boolean to bdd: " << as_bdd_.size() << " entries\n" + << "star normal form: " << snf_cache_.size() << " entries\n" + << "boolean isop: " << bool_isop_.size() << " entries\n"; + } - void - print_stats(std::ostream& os) const - { - os << "simplified formulae: " << simplified_.size() << " entries\n" - << "negative normal form: " << nenoform_.size() << " entries\n" - << "syntactic implications: " << syntimpl_.size() << " entries\n" - << "boolean to bdd: " << as_bdd_.size() << " entries\n" - << "star normal form: " << snf_cache_.size() << " entries\n" - << "boolean isop: " << bool_isop_.size() << " entries\n"; - } + void + clear_as_bdd_cache() + { + as_bdd_.clear(); + } - void - clear_as_bdd_cache() - { - as_bdd_.clear(); - } + // Convert a Boolean formula into a BDD for easier comparison. + bdd + as_bdd(formula f) + { + // Lookup the result in case it has already been computed. + f2b_map::const_iterator it = as_bdd_.find(f); + if (it != as_bdd_.end()) + return it->second; - // Convert a Boolean formula into a BDD for easier comparison. - bdd - as_bdd(formula f) - { - // Lookup the result in case it has already been computed. - f2b_map::const_iterator it = as_bdd_.find(f); - if (it != as_bdd_.end()) - return it->second; + bdd result = bddfalse; - bdd result = bddfalse; - - switch (f.kind()) + switch (f.kind()) + { + case op::tt: + result = bddtrue; + break; + case op::ff: + result = bddfalse; + break; + case op::ap: + result = bdd_ithvar(dict->register_proposition(f, this)); + break; + case op::Not: + result = !as_bdd(f[0]); + break; + case op::Xor: + result = bdd_apply(as_bdd(f[0]), as_bdd(f[1]), bddop_xor); + break; + case op::Implies: + result = bdd_apply(as_bdd(f[0]), as_bdd(f[1]), bddop_imp); + break; + case op::Equiv: + result = bdd_apply(as_bdd(f[0]), as_bdd(f[1]), bddop_biimp); + break; + case op::And: { - case op::tt: result = bddtrue; + for (auto c: f) + result &= as_bdd(c); break; - case op::ff: + } + case op::Or: + { result = bddfalse; + for (auto c: f) + result |= as_bdd(c); break; - case op::ap: - result = bdd_ithvar(dict->register_proposition(f, this)); - break; - case op::Not: - result = !as_bdd(f[0]); - break; - case op::Xor: - result = bdd_apply(as_bdd(f[0]), as_bdd(f[1]), bddop_xor); - break; - case op::Implies: - result = bdd_apply(as_bdd(f[0]), as_bdd(f[1]), bddop_imp); - break; - case op::Equiv: - result = bdd_apply(as_bdd(f[0]), as_bdd(f[1]), bddop_biimp); - break; - case op::And: - { - result = bddtrue; - for (auto c: f) - result &= as_bdd(c); - break; - } - case op::Or: - { - result = bddfalse; - for (auto c: f) - result |= as_bdd(c); - break; - } - default: - SPOT_UNIMPLEMENTED(); } + default: + SPOT_UNIMPLEMENTED(); + } - // Cache the result before returning. - as_bdd_[f] = result; - return result; - } + // Cache the result before returning. + as_bdd_[f] = result; + return result; + } - formula - lookup_nenoform(formula f) - { - f2f_map::const_iterator i = nenoform_.find(f); - if (i == nenoform_.end()) - return nullptr; - return i->second; - } - - void - cache_nenoform(formula orig, formula nenoform) - { - nenoform_[orig] = nenoform; - } - - // Return true iff the option set (syntactic implication - // or containment checks) allow to prove that f1 => f2. - bool - implication(formula f1, formula f2) - { - trace << "[->] does " << str_psl(f1) << " implies " - << str_psl(f2) << " ?" << std::endl; - if ((options.synt_impl && syntactic_implication(f1, f2)) - || (options.containment_checks && contained(f1, f2))) - { - trace << "[->] Yes" << std::endl; - return true; - } - trace << "[->] No" << std::endl; - return false; - } - - // Return true if f1 => f2 syntactically - bool - syntactic_implication(formula f1, formula f2); - bool - syntactic_implication_aux(formula f1, formula f2); - - // Return true if f1 => f2 - bool - contained(formula f1, formula f2) - { - if (!f1.is_psl_formula() || !f2.is_psl_formula()) - return false; - return lcc.contained(f1, f2); - } - - // If right==false, true if !f1 => f2, false otherwise. - // If right==true, true if f1 => !f2, false otherwise. - bool - syntactic_implication_neg(formula f1, formula f2, - bool right); - - // Return true if f1 => !f2 - bool contained_neg(formula f1, formula f2) - { - if (!f1.is_psl_formula() || !f2.is_psl_formula()) - return false; - trace << "[CN] Does (" << str_psl(f1) << ") implies !(" - << str_psl(f2) << ") ?" << std::endl; - if (lcc.contained_neg(f1, f2)) - { - trace << "[CN] Yes" << std::endl; - return true; - } - else - { - trace << "[CN] No" << std::endl; - return false; - } - } - - // Return true if f1 => !f2 - bool neg_contained(formula f1, formula f2) - { - if (!f1.is_psl_formula() || !f2.is_psl_formula()) - return false; - trace << "[NC] Does (" << str_psl(f1) << ") implies !(" - << str_psl(f2) << ") ?" << std::endl; - if (lcc.neg_contained(f1, f2)) - { - trace << "[NC] Yes" << std::endl; - return true; - } - else - { - trace << "[NC] No" << std::endl; - return false; - } - } - - // Return true iff the option set (syntactic implication - // or containment checks) allow to prove that - // - !f1 => f2 (case where right=false) - // - f1 => !f2 (case where right=true) - bool - implication_neg(formula f1, formula f2, bool right) - { - trace << "[IN] Does " << (right ? "(" : "!(") - << str_psl(f1) << ") implies " - << (right ? "!(" : "(") << str_psl(f2) << ") ?" - << std::endl; - if ((options.synt_impl && syntactic_implication_neg(f1, f2, right)) - || (options.containment_checks && right && contained_neg(f1, f2)) - || (options.containment_checks && !right && neg_contained(f1, f2))) - { - trace << "[IN] Yes" << std::endl; - return true; - } - else - { - trace << "[IN] No" << std::endl; - return false; - } - } - - formula - lookup_simplified(formula f) - { - f2f_map::const_iterator i = simplified_.find(f); - if (i == simplified_.end()) - return nullptr; - return i->second; - } - - void - cache_simplified(formula orig, formula simplified) - { - simplified_[orig] = simplified; - } - - formula - star_normal_form(formula f) - { - return ltl::star_normal_form(f, &snf_cache_); - } - - formula - star_normal_form_bounded(formula f) - { - return ltl::star_normal_form_bounded(f, &snfb_cache_); - } - - - formula - boolean_to_isop(formula f) - { - f2f_map::const_iterator it = bool_isop_.find(f); - if (it != bool_isop_.end()) - return it->second; - - assert(f.is_boolean()); - formula res = bdd_to_formula(as_bdd(f), dict); - bool_isop_[f] = res; - return res; - } - - private: - f2b_map as_bdd_; - f2f_map simplified_; - f2f_map nenoform_; - syntimpl_cache_t syntimpl_; - snf_cache snf_cache_; - snf_cache snfb_cache_; - f2f_map bool_isop_; - }; - - - namespace + formula + lookup_nenoform(formula f) { - ////////////////////////////////////////////////////////////////////// - // - // NEGATIVE_NORMAL_FORM - // - ////////////////////////////////////////////////////////////////////// - - formula - nenoform_rec(formula f, bool negated, ltl_simplifier_cache* c); - - formula equiv_or_xor(bool equiv, formula f1, formula f2, - ltl_simplifier_cache* c) - { - auto rec = [c](formula f, bool negated) - { - return nenoform_rec(f, negated, c); - }; - - if (equiv) - { - // Rewrite a<=>b as (a&b)|(!a&!b) - auto recurse_f1_true = rec(f1, true); - auto recurse_f1_false = rec(f1, false); - auto recurse_f2_true = rec(f2, true); - auto recurse_f2_false = rec(f2, false); - auto left = formula::And({recurse_f1_false, recurse_f2_false}); - auto right = formula::And({recurse_f1_true, recurse_f2_true}); - return formula::Or({left, right}); - } - else - { - // Rewrite a^b as (a&!b)|(!a&b) - auto recurse_f1_true = rec(f1, true); - auto recurse_f1_false = rec(f1, false); - auto recurse_f2_true = rec(f2, true); - auto recurse_f2_false = rec(f2, false); - auto left = formula::And({recurse_f1_false, recurse_f2_true}); - auto right = formula::And({recurse_f1_true, recurse_f2_false}); - return formula::Or({left, right}); - } - } - - formula - nenoform_rec(formula f, bool negated, ltl_simplifier_cache* c) - { - if (f.is(op::Not)) - { - negated = !negated; - f = f[0]; - } - - formula key = f; - if (negated) - key = formula::Not(f); - formula result = c->lookup_nenoform(key); - if (result) - return result; - - if (key.is_in_nenoform() - || (c->options.nenoform_stop_on_boolean && key.is_boolean())) - { - result = key; - } - else - { - auto rec = [c](formula f, bool neg) - { - return nenoform_rec(f, neg, c); - }; - - switch (op o = f.kind()) - { - case op::ff: - case op::tt: - // Negation of constants is taken care of in the - // constructor of unop::Not, so these cases should be - // caught by nenoform_recursively(). - assert(!negated); - result = f; - break; - case op::ap: - result = negated ? formula::Not(f) : f; - break; - case op::X: - // !Xa == X!a - result = formula::X(rec(f[0], negated)); - break; - case op::F: - // !Fa == G!a - result = formula::unop(negated ? op::G : op::F, - rec(f[0], negated)); - break; - case op::G: - // !Ga == F!a - result = formula::unop(negated ? op::F : op::G, - rec(f[0], negated)); - break; - case op::Closure: - result = formula::unop(negated ? - op::NegClosure : op::Closure, - rec(f[0], false)); - break; - case op::NegClosure: - case op::NegClosureMarked: - result = formula::unop(negated ? op::Closure : o, - rec(f[0], false)); - break; - - case op::Implies: - if (negated) - // !(a => b) == a & !b - { - auto f2 = rec(f[1], true); - result = formula::And({rec(f[0], false), f2}); - } - else // a => b == !a | b - { - auto f2 = rec(f[1], false); - result = formula::Or({rec(f[0], true), f2}); - } - break; - case op::Xor: - { - // !(a ^ b) == a <=> b - result = equiv_or_xor(negated, f[0], f[1], c); - break; - } - case op::Equiv: - { - // !(a <=> b) == a ^ b - result = equiv_or_xor(!negated, f[0], f[1], c); - break; - } - case op::U: - { - // !(a U b) == !a R !b - auto f1 = rec(f[0], negated); - auto f2 = rec(f[1], negated); - result = formula::binop(negated ? op::R : op::U, f1, f2); - break; - } - case op::R: - { - // !(a R b) == !a U !b - auto f1 = rec(f[0], negated); - auto f2 = rec(f[1], negated); - result = formula::binop(negated ? op::U : op::R, f1, f2); - break; - } - case op::W: - { - // !(a W b) == !a M !b - auto f1 = rec(f[0], negated); - auto f2 = rec(f[1], negated); - result = formula::binop(negated ? op::M : op::W, f1, f2); - break; - } - case op::M: - { - // !(a M b) == !a W !b - auto f1 = rec(f[0], negated); - auto f2 = rec(f[1], negated); - result = formula::binop(negated ? op::W : op::M, f1, f2); - break; - } - case op::Or: - case op::And: - { - unsigned mos = f.size(); - vec v; - for (unsigned i = 0; i < mos; ++i) - v.push_back(rec(f[i], negated)); - op on = o; - if (negated) - on = o == op::Or ? op::And : op::Or; - result = formula::multop(on, v); - break; - } - case op::OrRat: - case op::AndRat: - case op::AndNLM: - case op::Concat: - case op::Fusion: - case op::Star: - case op::FStar: - // !(a*) etc. should never occur. - { - assert(!negated); - result = f.map([c](formula f) - { - return nenoform_rec(f, false, c); - }); - break; - } - case op::EConcat: - case op::EConcatMarked: - { - // !(a <>-> b) == a[]-> !b - auto f1 = f[0]; - auto f2 = f[1]; - result = formula::binop(negated ? op::UConcat : o, - rec(f1, false), rec(f2, negated)); - break; - } - case op::UConcat: - { - // !(a []-> b) == a<>-> !b - auto f1 = f[0]; - auto f2 = f[1]; - result = formula::binop(negated ? op::EConcat : op::UConcat, - rec(f1, false), rec(f2, negated)); - break; - } - case op::eword: - case op::Not: - SPOT_UNREACHABLE(); - } - } - - c->cache_nenoform(key, result); - return result; - } - - ////////////////////////////////////////////////////////////////////// - // - // SIMPLIFY_VISITOR - // - ////////////////////////////////////////////////////////////////////// - - // Forward declaration. - formula - simplify_recursively(formula f, ltl_simplifier_cache* c); - - // X(a) R b or X(a) M b - // This returns a. - formula - is_XRM(formula f) - { - if (!f.is(op::R, op::M)) - return nullptr; - auto left = f[0]; - if (!left.is(op::X)) - return nullptr; - return left[0]; - } - - // X(a) W b or X(a) U b - // This returns a. - formula - is_XWU(formula f) - { - if (!f.is(op::W, op::U)) - return nullptr; - auto left = f[0]; - if (!left.is(op::X)) - return nullptr; - return left[0]; - } - - // b & X(b W a) or b & X(b U a) - // This returns (b W a) or (b U a). - formula - is_bXbWU(formula f) - { - if (!f.is(op::And)) - return nullptr; - unsigned s = f.size(); - for (unsigned pos = 0; pos < s; ++pos) - { - auto p = f[pos]; - if (!(p.is(op::X))) - continue; - auto c = p[0]; - if (!c.is(op::U, op::W)) - continue; - formula b = f.all_but(pos); - if (b == c[0]) - return c; - } + f2f_map::const_iterator i = nenoform_.find(f); + if (i == nenoform_.end()) return nullptr; - } + return i->second; + } - // b | X(b R a) or b | X(b M a) - // This returns (b R a) or (b M a). - formula - is_bXbRM(formula f) - { - if (!f.is(op::Or)) - return nullptr; - unsigned s = f.size(); - for (unsigned pos = 0; pos < s; ++pos) - { - auto p = f[pos]; - if (!(p.is(op::X))) - continue; - auto c = p[0]; - if (!c.is(op::R, op::M)) - continue; - formula b = f.all_but(pos); - if (b == c[0]) - return c; - } + void + cache_nenoform(formula orig, formula nenoform) + { + nenoform_[orig] = nenoform; + } + + // Return true iff the option set (syntactic implication + // or containment checks) allow to prove that f1 => f2. + bool + implication(formula f1, formula f2) + { + trace << "[->] does " << str_psl(f1) << " implies " + << str_psl(f2) << " ?" << std::endl; + if ((options.synt_impl && syntactic_implication(f1, f2)) + || (options.containment_checks && contained(f1, f2))) + { + trace << "[->] Yes" << std::endl; + return true; + } + trace << "[->] No" << std::endl; + return false; + } + + // Return true if f1 => f2 syntactically + bool + syntactic_implication(formula f1, formula f2); + bool + syntactic_implication_aux(formula f1, formula f2); + + // Return true if f1 => f2 + bool + contained(formula f1, formula f2) + { + if (!f1.is_psl_formula() || !f2.is_psl_formula()) + return false; + return lcc.contained(f1, f2); + } + + // If right==false, true if !f1 => f2, false otherwise. + // If right==true, true if f1 => !f2, false otherwise. + bool + syntactic_implication_neg(formula f1, formula f2, + bool right); + + // Return true if f1 => !f2 + bool contained_neg(formula f1, formula f2) + { + if (!f1.is_psl_formula() || !f2.is_psl_formula()) + return false; + trace << "[CN] Does (" << str_psl(f1) << ") implies !(" + << str_psl(f2) << ") ?" << std::endl; + if (lcc.contained_neg(f1, f2)) + { + trace << "[CN] Yes" << std::endl; + return true; + } + else + { + trace << "[CN] No" << std::endl; + return false; + } + } + + // Return true if f1 => !f2 + bool neg_contained(formula f1, formula f2) + { + if (!f1.is_psl_formula() || !f2.is_psl_formula()) + return false; + trace << "[NC] Does (" << str_psl(f1) << ") implies !(" + << str_psl(f2) << ") ?" << std::endl; + if (lcc.neg_contained(f1, f2)) + { + trace << "[NC] Yes" << std::endl; + return true; + } + else + { + trace << "[NC] No" << std::endl; + return false; + } + } + + // Return true iff the option set (syntactic implication + // or containment checks) allow to prove that + // - !f1 => f2 (case where right=false) + // - f1 => !f2 (case where right=true) + bool + implication_neg(formula f1, formula f2, bool right) + { + trace << "[IN] Does " << (right ? "(" : "!(") + << str_psl(f1) << ") implies " + << (right ? "!(" : "(") << str_psl(f2) << ") ?" + << std::endl; + if ((options.synt_impl && syntactic_implication_neg(f1, f2, right)) + || (options.containment_checks && right && contained_neg(f1, f2)) + || (options.containment_checks && !right && neg_contained(f1, f2))) + { + trace << "[IN] Yes" << std::endl; + return true; + } + else + { + trace << "[IN] No" << std::endl; + return false; + } + } + + formula + lookup_simplified(formula f) + { + f2f_map::const_iterator i = simplified_.find(f); + if (i == simplified_.end()) return nullptr; - } + return i->second; + } - formula - unop_multop(op uop, op mop, vec v) - { - return formula::unop(uop, formula::multop(mop, v)); - } + void + cache_simplified(formula orig, formula simplified) + { + simplified_[orig] = simplified; + } - formula - unop_unop_multop(op uop1, op uop2, op mop, vec v) - { - return formula::unop(uop1, unop_multop(uop2, mop, v)); - } + formula + star_normal_form(formula f) + { + return spot::star_normal_form(f, &snf_cache_); + } - formula - unop_unop(op uop1, op uop2, formula f) - { - return formula::unop(uop1, formula::unop(uop2, f)); - } + formula + star_normal_form_bounded(formula f) + { + return spot::star_normal_form_bounded(f, &snfb_cache_); + } - struct mospliter - { - enum what { Split_GF = (1 << 0), - Strip_GF = (1 << 1) | (1 << 0), - Split_FG = (1 << 2), - Strip_FG = (1 << 3) | (1 << 2), - Split_F = (1 << 4), - Strip_F = (1 << 5) | (1 << 4), - Split_G = (1 << 6), - Strip_G = (1 << 7) | (1 << 6), - Strip_X = (1 << 8), - Split_U_or_W = (1 << 9), - Split_R_or_M = (1 << 10), - Split_EventUniv = (1 << 11), - Split_Event = (1 << 12), - Split_Univ = (1 << 13), - Split_Bool = (1 << 14) + + formula + boolean_to_isop(formula f) + { + f2f_map::const_iterator it = bool_isop_.find(f); + if (it != bool_isop_.end()) + return it->second; + + assert(f.is_boolean()); + formula res = bdd_to_formula(as_bdd(f), dict); + bool_isop_[f] = res; + return res; + } + + private: + f2b_map as_bdd_; + f2f_map simplified_; + f2f_map nenoform_; + syntimpl_cache_t syntimpl_; + snf_cache snf_cache_; + snf_cache snfb_cache_; + f2f_map bool_isop_; + }; + + + namespace + { + ////////////////////////////////////////////////////////////////////// + // + // NEGATIVE_NORMAL_FORM + // + ////////////////////////////////////////////////////////////////////// + + formula + nenoform_rec(formula f, bool negated, ltl_simplifier_cache* c); + + formula equiv_or_xor(bool equiv, formula f1, formula f2, + ltl_simplifier_cache* c) + { + auto rec = [c](formula f, bool negated) + { + return nenoform_rec(f, negated, c); }; - private: - mospliter(unsigned split, ltl_simplifier_cache* cache) - : split_(split), c_(cache), - res_GF{(split_ & Split_GF) ? new vec : nullptr}, - res_FG{(split_ & Split_FG) ? new vec : nullptr}, - res_F{(split_ & Split_F) ? new vec : nullptr}, - res_G{(split_ & Split_G) ? new vec : nullptr}, - res_X{(split_ & Strip_X) ? new vec : nullptr}, - res_U_or_W{(split_ & Split_U_or_W) ? new vec : nullptr}, - res_R_or_M{(split_ & Split_R_or_M) ? new vec : nullptr}, - res_Event{(split_ & Split_Event) ? new vec : nullptr}, - res_Univ{(split_ & Split_Univ) ? new vec : nullptr}, - res_EventUniv{(split_ & Split_EventUniv) ? new vec : nullptr}, - res_Bool{(split_ & Split_Bool) ? new vec : nullptr}, - res_other{new vec} + if (equiv) { + // Rewrite a<=>b as (a&b)|(!a&!b) + auto recurse_f1_true = rec(f1, true); + auto recurse_f1_false = rec(f1, false); + auto recurse_f2_true = rec(f2, true); + auto recurse_f2_false = rec(f2, false); + auto left = formula::And({recurse_f1_false, recurse_f2_false}); + auto right = formula::And({recurse_f1_true, recurse_f2_true}); + return formula::Or({left, right}); + } + else + { + // Rewrite a^b as (a&!b)|(!a&b) + auto recurse_f1_true = rec(f1, true); + auto recurse_f1_false = rec(f1, false); + auto recurse_f2_true = rec(f2, true); + auto recurse_f2_false = rec(f2, false); + auto left = formula::And({recurse_f1_false, recurse_f2_true}); + auto right = formula::And({recurse_f1_true, recurse_f2_false}); + return formula::Or({left, right}); + } + } + + formula + nenoform_rec(formula f, bool negated, ltl_simplifier_cache* c) + { + if (f.is(op::Not)) + { + negated = !negated; + f = f[0]; } - public: - mospliter(unsigned split, vec v, ltl_simplifier_cache* cache) - : mospliter(split, cache) + formula key = f; + if (negated) + key = formula::Not(f); + formula result = c->lookup_nenoform(key); + if (result) + return result; + + if (key.is_in_nenoform() + || (c->options.nenoform_stop_on_boolean && key.is_boolean())) { - for (auto f: v) - { - if (f) // skip null pointers left by previous simplifications - process(f); - } + result = key; } - - mospliter(unsigned split, formula mo, - ltl_simplifier_cache* cache) - : mospliter(split, cache) + else { - unsigned mos = mo.size(); - for (unsigned i = 0; i < mos; ++i) + auto rec = [c](formula f, bool neg) { - formula f = simplify_recursively(mo[i], cache); - process(f); - } - } - - void process(formula f) - { - bool e = f.is_eventual(); - bool u = f.is_universal(); - bool eu = res_EventUniv && e & u && c_->options.favor_event_univ; - switch (f.kind()) - { - case op::X: - if (res_X && !eu) - { - res_X->push_back(f[0]); - return; - } - break; - case op::F: - { - formula c = f[0]; - if (res_FG && u && c.is(op::G)) - { - res_FG->push_back(((split_ & Strip_FG) == Strip_FG - ? c[0] : f)); - return; - } - if (res_F && !eu) - { - res_F->push_back(((split_ & Strip_F) == Strip_F - ? c : f)); - return; - } - break; - } - case op::G: - { - formula c = f[0]; - if (res_GF && e && c.is(op::F)) - { - res_GF->push_back(((split_ & Strip_GF) == Strip_GF - ? c[0] : f)); - return; - } - if (res_G && !eu) - { - res_G->push_back(((split_ & Strip_G) == Strip_G - ? c : f)); - return; - } - break; - } - case op::U: - case op::W: - if (res_U_or_W) - { - res_U_or_W->push_back(f); - return; - } - break; - case op::R: - case op::M: - if (res_R_or_M) - { - res_R_or_M->push_back(f); - return; - } - break; - default: - if (res_Bool && f.is_boolean()) - { - res_Bool->push_back(f); - return; - } - break; - } - if (c_->options.event_univ) - { - if (res_EventUniv && e && u) - { - res_EventUniv->push_back(f); - return; - } - if (res_Event && e) - { - res_Event->push_back(f); - return; - } - if (res_Univ && u) - { - res_Univ->push_back(f); - return; - } - } - res_other->push_back(f); - } - - unsigned split_; - ltl_simplifier_cache* c_; - std::unique_ptr res_GF; - std::unique_ptr res_FG; - std::unique_ptr res_F; - std::unique_ptr res_G; - std::unique_ptr res_X; - std::unique_ptr res_U_or_W; - std::unique_ptr res_R_or_M; - std::unique_ptr res_Event; - std::unique_ptr res_Univ; - std::unique_ptr res_EventUniv; - std::unique_ptr res_Bool; - std::unique_ptr res_other; - }; - - class simplify_visitor final - { - public: - - simplify_visitor(ltl_simplifier_cache* cache) - : c_(cache), opt_(cache->options) - { - } - - // if !neg build c&X(c&X(...&X(tail))) with n occurences of c - // if neg build !c|X(!c|X(...|X(tail))). - formula - dup_b_x_tail(bool neg, formula c, formula tail, unsigned n) - { - op mop; - if (neg) - { - c = formula::Not(c); - mop = op::Or; - } - else - { - mop = op::And; - } - while (n--) - tail = // b&X(tail) or !b|X(tail) - formula::multop(mop, {c, formula::X(tail)}); - return tail; - } - - formula - visit(formula f) - { - formula result = f; - - auto recurse = [this](formula f) - { - return simplify_recursively(f, c_); + return nenoform_rec(f, neg, c); }; - f = f.map(recurse); - switch (op o = f.kind()) { - case op::ff: - case op::tt: - case op::eword: - case op::ap: - case op::Not: - case op::FStar: - return f; - case op::X: + case op::ff: + case op::tt: + // Negation of constants is taken care of in the + // constructor of unop::Not, so these cases should be + // caught by nenoform_recursively(). + assert(!negated); + result = f; + break; + case op::ap: + result = negated ? formula::Not(f) : f; + break; + case op::X: + // !Xa == X!a + result = formula::X(rec(f[0], negated)); + break; + case op::F: + // !Fa == G!a + result = formula::unop(negated ? op::G : op::F, + rec(f[0], negated)); + break; + case op::G: + // !Ga == F!a + result = formula::unop(negated ? op::F : op::G, + rec(f[0], negated)); + break; + case op::Closure: + result = formula::unop(negated ? + op::NegClosure : op::Closure, + rec(f[0], false)); + break; + case op::NegClosure: + case op::NegClosureMarked: + result = formula::unop(negated ? op::Closure : o, + rec(f[0], false)); + break; + + case op::Implies: + if (negated) + // !(a => b) == a & !b { - formula c = f[0]; - // Xf = f if f is both eventual and universal. - if (c.is_universal() && c.is_eventual()) - { - if (opt_.event_univ) - return c; - // If EventUniv simplification is disabled, use - // only the following basic rewriting rules: - // XGF(f) = GF(f) and XFG(f) = FG(f) - // The former comes from Somenzi&Bloem (CAV'00). - // It's not clear why they do not list the second. - if (opt_.reduce_basics && - (c.is({op::G, op::F}) || c.is({op::F, op::G}))) - return c; - } - - // If Xa = a, keep only a. - if (opt_.containment_checks_stronger - && c_->lcc.equal(f, c)) - return c; - - // X(f1 & GF(f2)) = X(f1) & GF(f2) - // X(f1 | GF(f2)) = X(f1) | GF(f2) - // X(f1 & FG(f2)) = X(f1) & FG(f2) - // X(f1 | FG(f2)) = X(f1) | FG(f2) - // - // The above usually make more sense when reversed (see - // them in the And and Or rewritings), except when we - // try to maximaze the size of subformula that do not - // have EventUniv formulae. - if (opt_.favor_event_univ) - if (c.is(op::Or, op::And)) - { - mospliter s(mospliter::Split_EventUniv, c, c_); - op oc = c.kind(); - s.res_EventUniv-> - push_back(unop_multop(op::X, oc, - std::move(*s.res_other))); - formula result = - formula::multop(oc, - std::move(*s.res_EventUniv)); - if (result != f) - result = recurse(result); - return result; - } - return f; + auto f2 = rec(f[1], true); + result = formula::And({rec(f[0], false), f2}); } - case op::F: + else // a => b == !a | b { - formula c = f[0]; - // If f is a pure eventuality formula then F(f)=f. - if (opt_.event_univ && c.is_eventual()) + auto f2 = rec(f[1], false); + result = formula::Or({rec(f[0], true), f2}); + } + break; + case op::Xor: + { + // !(a ^ b) == a <=> b + result = equiv_or_xor(negated, f[0], f[1], c); + break; + } + case op::Equiv: + { + // !(a <=> b) == a ^ b + result = equiv_or_xor(!negated, f[0], f[1], c); + break; + } + case op::U: + { + // !(a U b) == !a R !b + auto f1 = rec(f[0], negated); + auto f2 = rec(f[1], negated); + result = formula::binop(negated ? op::R : op::U, f1, f2); + break; + } + case op::R: + { + // !(a R b) == !a U !b + auto f1 = rec(f[0], negated); + auto f2 = rec(f[1], negated); + result = formula::binop(negated ? op::U : op::R, f1, f2); + break; + } + case op::W: + { + // !(a W b) == !a M !b + auto f1 = rec(f[0], negated); + auto f2 = rec(f[1], negated); + result = formula::binop(negated ? op::M : op::W, f1, f2); + break; + } + case op::M: + { + // !(a M b) == !a W !b + auto f1 = rec(f[0], negated); + auto f2 = rec(f[1], negated); + result = formula::binop(negated ? op::W : op::M, f1, f2); + break; + } + case op::Or: + case op::And: + { + unsigned mos = f.size(); + vec v; + for (unsigned i = 0; i < mos; ++i) + v.push_back(rec(f[i], negated)); + op on = o; + if (negated) + on = o == op::Or ? op::And : op::Or; + result = formula::multop(on, v); + break; + } + case op::OrRat: + case op::AndRat: + case op::AndNLM: + case op::Concat: + case op::Fusion: + case op::Star: + case op::FStar: + // !(a*) etc. should never occur. + { + assert(!negated); + result = f.map([c](formula f) + { + return nenoform_rec(f, false, c); + }); + break; + } + case op::EConcat: + case op::EConcatMarked: + { + // !(a <>-> b) == a[]-> !b + auto f1 = f[0]; + auto f2 = f[1]; + result = formula::binop(negated ? op::UConcat : o, + rec(f1, false), rec(f2, negated)); + break; + } + case op::UConcat: + { + // !(a []-> b) == a<>-> !b + auto f1 = f[0]; + auto f2 = f[1]; + result = formula::binop(negated ? op::EConcat : op::UConcat, + rec(f1, false), rec(f2, negated)); + break; + } + case op::eword: + case op::Not: + SPOT_UNREACHABLE(); + } + } + + c->cache_nenoform(key, result); + return result; + } + + ////////////////////////////////////////////////////////////////////// + // + // SIMPLIFY_VISITOR + // + ////////////////////////////////////////////////////////////////////// + + // Forward declaration. + formula + simplify_recursively(formula f, ltl_simplifier_cache* c); + + // X(a) R b or X(a) M b + // This returns a. + formula + is_XRM(formula f) + { + if (!f.is(op::R, op::M)) + return nullptr; + auto left = f[0]; + if (!left.is(op::X)) + return nullptr; + return left[0]; + } + + // X(a) W b or X(a) U b + // This returns a. + formula + is_XWU(formula f) + { + if (!f.is(op::W, op::U)) + return nullptr; + auto left = f[0]; + if (!left.is(op::X)) + return nullptr; + return left[0]; + } + + // b & X(b W a) or b & X(b U a) + // This returns (b W a) or (b U a). + formula + is_bXbWU(formula f) + { + if (!f.is(op::And)) + return nullptr; + unsigned s = f.size(); + for (unsigned pos = 0; pos < s; ++pos) + { + auto p = f[pos]; + if (!(p.is(op::X))) + continue; + auto c = p[0]; + if (!c.is(op::U, op::W)) + continue; + formula b = f.all_but(pos); + if (b == c[0]) + return c; + } + return nullptr; + } + + // b | X(b R a) or b | X(b M a) + // This returns (b R a) or (b M a). + formula + is_bXbRM(formula f) + { + if (!f.is(op::Or)) + return nullptr; + unsigned s = f.size(); + for (unsigned pos = 0; pos < s; ++pos) + { + auto p = f[pos]; + if (!(p.is(op::X))) + continue; + auto c = p[0]; + if (!c.is(op::R, op::M)) + continue; + formula b = f.all_but(pos); + if (b == c[0]) + return c; + } + return nullptr; + } + + formula + unop_multop(op uop, op mop, vec v) + { + return formula::unop(uop, formula::multop(mop, v)); + } + + formula + unop_unop_multop(op uop1, op uop2, op mop, vec v) + { + return formula::unop(uop1, unop_multop(uop2, mop, v)); + } + + formula + unop_unop(op uop1, op uop2, formula f) + { + return formula::unop(uop1, formula::unop(uop2, f)); + } + + struct mospliter + { + enum what { Split_GF = (1 << 0), + Strip_GF = (1 << 1) | (1 << 0), + Split_FG = (1 << 2), + Strip_FG = (1 << 3) | (1 << 2), + Split_F = (1 << 4), + Strip_F = (1 << 5) | (1 << 4), + Split_G = (1 << 6), + Strip_G = (1 << 7) | (1 << 6), + Strip_X = (1 << 8), + Split_U_or_W = (1 << 9), + Split_R_or_M = (1 << 10), + Split_EventUniv = (1 << 11), + Split_Event = (1 << 12), + Split_Univ = (1 << 13), + Split_Bool = (1 << 14) + }; + + private: + mospliter(unsigned split, ltl_simplifier_cache* cache) + : split_(split), c_(cache), + res_GF{(split_ & Split_GF) ? new vec : nullptr}, + res_FG{(split_ & Split_FG) ? new vec : nullptr}, + res_F{(split_ & Split_F) ? new vec : nullptr}, + res_G{(split_ & Split_G) ? new vec : nullptr}, + res_X{(split_ & Strip_X) ? new vec : nullptr}, + res_U_or_W{(split_ & Split_U_or_W) ? new vec : nullptr}, + res_R_or_M{(split_ & Split_R_or_M) ? new vec : nullptr}, + res_Event{(split_ & Split_Event) ? new vec : nullptr}, + res_Univ{(split_ & Split_Univ) ? new vec : nullptr}, + res_EventUniv{(split_ & Split_EventUniv) ? new vec : nullptr}, + res_Bool{(split_ & Split_Bool) ? new vec : nullptr}, + res_other{new vec} + { + } + + public: + mospliter(unsigned split, vec v, ltl_simplifier_cache* cache) + : mospliter(split, cache) + { + for (auto f: v) + { + if (f) // skip null pointers left by previous simplifications + process(f); + } + } + + mospliter(unsigned split, formula mo, + ltl_simplifier_cache* cache) + : mospliter(split, cache) + { + unsigned mos = mo.size(); + for (unsigned i = 0; i < mos; ++i) + { + formula f = simplify_recursively(mo[i], cache); + process(f); + } + } + + void process(formula f) + { + bool e = f.is_eventual(); + bool u = f.is_universal(); + bool eu = res_EventUniv && e & u && c_->options.favor_event_univ; + switch (f.kind()) + { + case op::X: + if (res_X && !eu) + { + res_X->push_back(f[0]); + return; + } + break; + case op::F: + { + formula c = f[0]; + if (res_FG && u && c.is(op::G)) + { + res_FG->push_back(((split_ & Strip_FG) == Strip_FG + ? c[0] : f)); + return; + } + if (res_F && !eu) + { + res_F->push_back(((split_ & Strip_F) == Strip_F + ? c : f)); + return; + } + break; + } + case op::G: + { + formula c = f[0]; + if (res_GF && e && c.is(op::F)) + { + res_GF->push_back(((split_ & Strip_GF) == Strip_GF + ? c[0] : f)); + return; + } + if (res_G && !eu) + { + res_G->push_back(((split_ & Strip_G) == Strip_G + ? c : f)); + return; + } + break; + } + case op::U: + case op::W: + if (res_U_or_W) + { + res_U_or_W->push_back(f); + return; + } + break; + case op::R: + case op::M: + if (res_R_or_M) + { + res_R_or_M->push_back(f); + return; + } + break; + default: + if (res_Bool && f.is_boolean()) + { + res_Bool->push_back(f); + return; + } + break; + } + if (c_->options.event_univ) + { + if (res_EventUniv && e && u) + { + res_EventUniv->push_back(f); + return; + } + if (res_Event && e) + { + res_Event->push_back(f); + return; + } + if (res_Univ && u) + { + res_Univ->push_back(f); + return; + } + } + res_other->push_back(f); + } + + unsigned split_; + ltl_simplifier_cache* c_; + std::unique_ptr res_GF; + std::unique_ptr res_FG; + std::unique_ptr res_F; + std::unique_ptr res_G; + std::unique_ptr res_X; + std::unique_ptr res_U_or_W; + std::unique_ptr res_R_or_M; + std::unique_ptr res_Event; + std::unique_ptr res_Univ; + std::unique_ptr res_EventUniv; + std::unique_ptr res_Bool; + std::unique_ptr res_other; + }; + + class simplify_visitor final + { + public: + + simplify_visitor(ltl_simplifier_cache* cache) + : c_(cache), opt_(cache->options) + { + } + + // if !neg build c&X(c&X(...&X(tail))) with n occurences of c + // if neg build !c|X(!c|X(...|X(tail))). + formula + dup_b_x_tail(bool neg, formula c, formula tail, unsigned n) + { + op mop; + if (neg) + { + c = formula::Not(c); + mop = op::Or; + } + else + { + mop = op::And; + } + while (n--) + tail = // b&X(tail) or !b|X(tail) + formula::multop(mop, {c, formula::X(tail)}); + return tail; + } + + formula + visit(formula f) + { + formula result = f; + + auto recurse = [this](formula f) + { + return simplify_recursively(f, c_); + }; + + f = f.map(recurse); + + switch (op o = f.kind()) + { + case op::ff: + case op::tt: + case op::eword: + case op::ap: + case op::Not: + case op::FStar: + return f; + case op::X: + { + formula c = f[0]; + // Xf = f if f is both eventual and universal. + if (c.is_universal() && c.is_eventual()) + { + if (opt_.event_univ) return c; + // If EventUniv simplification is disabled, use + // only the following basic rewriting rules: + // XGF(f) = GF(f) and XFG(f) = FG(f) + // The former comes from Somenzi&Bloem (CAV'00). + // It's not clear why they do not list the second. + if (opt_.reduce_basics && + (c.is({op::G, op::F}) || c.is({op::F, op::G}))) + return c; + } - if (opt_.reduce_basics) + // If Xa = a, keep only a. + if (opt_.containment_checks_stronger + && c_->lcc.equal(f, c)) + return c; + + // X(f1 & GF(f2)) = X(f1) & GF(f2) + // X(f1 | GF(f2)) = X(f1) | GF(f2) + // X(f1 & FG(f2)) = X(f1) & FG(f2) + // X(f1 | FG(f2)) = X(f1) | FG(f2) + // + // The above usually make more sense when reversed (see + // them in the And and Or rewritings), except when we + // try to maximaze the size of subformula that do not + // have EventUniv formulae. + if (opt_.favor_event_univ) + if (c.is(op::Or, op::And)) + { + mospliter s(mospliter::Split_EventUniv, c, c_); + op oc = c.kind(); + s.res_EventUniv-> + push_back(unop_multop(op::X, oc, + std::move(*s.res_other))); + formula result = + formula::multop(oc, + std::move(*s.res_EventUniv)); + if (result != f) + result = recurse(result); + return result; + } + return f; + } + case op::F: + { + formula c = f[0]; + // If f is a pure eventuality formula then F(f)=f. + if (opt_.event_univ && c.is_eventual()) + return c; + + if (opt_.reduce_basics) + { + // F(a U b) = F(b) + if (c.is(op::U)) + return recurse(formula::F(c[1])); + + // F(a M b) = F(a & b) + if (c.is(op::M)) + return recurse(unop_multop(op::F, op::And, + {c[0], c[1]})); + + // FX(a) = XF(a) + // FXX(a) = XXF(a) ... + // FXG(a) = XFG(a) = FG(a) ... + if (c.is(op::X)) + return recurse(unop_unop(op::X, op::F, c[0])); + + // FG(a & Xb) = FG(a & b) + // FG(a & Gb) = FG(a & b) + if (c.is({op::G, op::And})) { - // F(a U b) = F(b) - if (c.is(op::U)) - return recurse(formula::F(c[1])); - - // F(a M b) = F(a & b) - if (c.is(op::M)) - return recurse(unop_multop(op::F, op::And, - {c[0], c[1]})); - - // FX(a) = XF(a) - // FXX(a) = XXF(a) ... - // FXG(a) = XFG(a) = FG(a) ... - if (c.is(op::X)) - return recurse(unop_unop(op::X, op::F, c[0])); - - // FG(a & Xb) = FG(a & b) - // FG(a & Gb) = FG(a & b) - if (c.is({op::G, op::And})) + formula m = c[0]; + if (!m.is_boolean()) { - formula m = c[0]; - if (!m.is_boolean()) - { - formula out = m.map([](formula f) - { - if (f.is(op::X, op::G)) - return f[0]; - return f; - }); - if (out != m) - return recurse(unop_unop(op::F, op::G, out)); - } + formula out = m.map([](formula f) + { + if (f.is(op::X, op::G)) + return f[0]; + return f; + }); + if (out != m) + return recurse(unop_unop(op::F, op::G, out)); } } - // if Fa => a, keep a. - if (opt_.containment_checks_stronger - && c_->lcc.contained(f, c)) - return c; + } + // if Fa => a, keep a. + if (opt_.containment_checks_stronger + && c_->lcc.contained(f, c)) + return c; - // Disabled by default: - // F(f1 & GF(f2)) = F(f1) & GF(f2) - // - // As is, these two formulae are translated into - // equivalent Büchi automata so the rewriting is - // useless. - // - // However when taken in a larger formula such - // as F(f1 & GF(f2)) | F(a & GF(b)), this - // rewriting used to produce (F(f1) & GF(f2)) | - // (F(a) & GF(b)), missing the opportunity to - // apply the F(E1)|F(E2) = F(E1|E2) rule which - // really helps the translation. F((f1 & GF(f2)) - // | (a & GF(b))) is indeed easier to translate. - // - // So we do not consider this rewriting rule by - // default. However if favor_event_univ is set, - // we want to move the GF out of the F. + // Disabled by default: + // F(f1 & GF(f2)) = F(f1) & GF(f2) + // + // As is, these two formulae are translated into + // equivalent Büchi automata so the rewriting is + // useless. + // + // However when taken in a larger formula such + // as F(f1 & GF(f2)) | F(a & GF(b)), this + // rewriting used to produce (F(f1) & GF(f2)) | + // (F(a) & GF(b)), missing the opportunity to + // apply the F(E1)|F(E2) = F(E1|E2) rule which + // really helps the translation. F((f1 & GF(f2)) + // | (a & GF(b))) is indeed easier to translate. + // + // So we do not consider this rewriting rule by + // default. However if favor_event_univ is set, + // we want to move the GF out of the F. + if (opt_.favor_event_univ) + // F(f1&f2&FG(f3)&FG(f4)&f5&f6) = + // F(f1&f2) & FG(f3&f4) & f5 & f6 + // if f5 and f6 are both eventual and universal. + if (c.is(op::And)) + { + mospliter s(mospliter::Strip_FG | + mospliter::Split_EventUniv, + c, c_); + s.res_EventUniv-> + push_back(unop_multop(op::F, op::And, + std::move(*s.res_other))); + s.res_EventUniv-> + push_back(unop_unop_multop(op::F, op::G, op::And, + std::move(*s.res_FG))); + formula res = + formula::And(std::move(*s.res_EventUniv)); + if (res != f) + return recurse(res); + } + // If u3 and u4 are universal formulae and h is not: + // F(f1 | f2 | Fu3 | u4 | FGg | Fh) + // = F(f1 | f2 | u3 | u4 | Gg | h) + // or + // F(f1 | f2 | Fu3 | u4 | FGg | Fh) + // = F(f1 | f2 | h) | F(u3 | u4 | Gg) + // depending on whether favor_event_univ is set. + if (c.is(op::Or)) + { + int w = mospliter::Strip_F; if (opt_.favor_event_univ) - // F(f1&f2&FG(f3)&FG(f4)&f5&f6) = - // F(f1&f2) & FG(f3&f4) & f5 & f6 - // if f5 and f6 are both eventual and universal. - if (c.is(op::And)) - { - mospliter s(mospliter::Strip_FG | - mospliter::Split_EventUniv, - c, c_); - s.res_EventUniv-> - push_back(unop_multop(op::F, op::And, - std::move(*s.res_other))); - s.res_EventUniv-> - push_back(unop_unop_multop(op::F, op::G, op::And, - std::move(*s.res_FG))); - formula res = - formula::And(std::move(*s.res_EventUniv)); - if (res != f) - return recurse(res); - } - // If u3 and u4 are universal formulae and h is not: - // F(f1 | f2 | Fu3 | u4 | FGg | Fh) - // = F(f1 | f2 | u3 | u4 | Gg | h) - // or - // F(f1 | f2 | Fu3 | u4 | FGg | Fh) - // = F(f1 | f2 | h) | F(u3 | u4 | Gg) - // depending on whether favor_event_univ is set. + w |= mospliter::Split_Univ; + mospliter s(w, c, c_); + s.res_other->insert(s.res_other->end(), + s.res_F->begin(), s.res_F->end()); + formula res = unop_multop(op::F, op::Or, + std::move(*s.res_other)); + if (s.res_Univ) + { + // Strip any F. + for (auto& g: *s.res_Univ) + if (g.is(op::F)) + g = g[0]; + formula fu = unop_multop(op::F, op::Or, + std::move(*s.res_Univ)); + res = formula::Or({res, fu}); + } + if (res != f) + return recurse(res); + } + } + return f; + case op::G: + { + formula c = f[0]; + // If f is a pure universality formula then G(f)=f. + if (opt_.event_univ && c.is_universal()) + return c; + + if (opt_.reduce_basics) + { + // G(a R b) = G(b) + if (c.is(op::R)) + return recurse(formula::G(c[1])); + + // G(a W b) = G(a | b) + if (c.is(op::W)) + return recurse(unop_multop(op::G, op::Or, + {c[0], c[1]})); + + // GX(a) = XG(a) + // GXX(a) = XXG(a) ... + // GXF(a) = XGF(a) = GF(a) ... + if (c.is(op::X)) + return recurse(unop_unop(op::X, op::G, c[0])); + + // G(f1|f2|GF(f3)|GF(f4)|f5|f6) = + // G(f1|f2) | GF(f3|f4) | f5 | f6 + // if f5 and f6 are both eventual and universal. if (c.is(op::Or)) { - int w = mospliter::Strip_F; + mospliter s(mospliter::Strip_GF | + mospliter::Split_EventUniv, + c, c_); + s.res_EventUniv-> + push_back(unop_multop(op::G, op::Or, + std::move(*s.res_other))); + s.res_EventUniv-> + push_back(unop_unop_multop(op::G, op::F, op::Or, + std::move(*s.res_GF))); + formula res = + formula::Or(std::move(*s.res_EventUniv)); + + if (res != f) + return recurse(res); + } + // If e3 and e4 are eventual formulae and h is not: + // G(f1 & f2 & Ge3 & e4 & GFg & Gh) + // = G(f1 & f2 & e3 & e4 & Fg & h) + // or + // G(f1 & f2 & Ge3 & e4 & GFg & Gh) + // = G(f1 & f2 & h) & G(e3 & e4 & Fg) + // depending on whether favor_event_univ is set. + else if (c.is(op::And)) + { + int w = mospliter::Strip_G; if (opt_.favor_event_univ) - w |= mospliter::Split_Univ; + w |= mospliter::Split_Event; mospliter s(w, c, c_); s.res_other->insert(s.res_other->end(), - s.res_F->begin(), s.res_F->end()); - formula res = unop_multop(op::F, op::Or, + s.res_G->begin(), s.res_G->end()); + formula res = unop_multop(op::G, op::And, std::move(*s.res_other)); - if (s.res_Univ) + + if (s.res_Event) { - // Strip any F. - for (auto& g: *s.res_Univ) - if (g.is(op::F)) + // Strip any G. + for (auto& g: *s.res_Event) + if (g.is(op::G)) g = g[0]; - formula fu = unop_multop(op::F, op::Or, - std::move(*s.res_Univ)); - res = formula::Or({res, fu}); + formula ge = + unop_multop(op::G, op::And, + std::move(*s.res_Event)); + res = formula::And({res, ge}); } if (res != f) return recurse(res); } - } - return f; - case op::G: - { - formula c = f[0]; - // If f is a pure universality formula then G(f)=f. - if (opt_.event_univ && c.is_universal()) - return c; - if (opt_.reduce_basics) + // GF(a | Xb) = GF(a | b) + // GF(a | Fb) = GF(a | b) + if (c.is({op::F, op::Or})) { - // G(a R b) = G(b) - if (c.is(op::R)) - return recurse(formula::G(c[1])); - - // G(a W b) = G(a | b) - if (c.is(op::W)) - return recurse(unop_multop(op::G, op::Or, - {c[0], c[1]})); - - // GX(a) = XG(a) - // GXX(a) = XXG(a) ... - // GXF(a) = XGF(a) = GF(a) ... - if (c.is(op::X)) - return recurse(unop_unop(op::X, op::G, c[0])); - - // G(f1|f2|GF(f3)|GF(f4)|f5|f6) = - // G(f1|f2) | GF(f3|f4) | f5 | f6 - // if f5 and f6 are both eventual and universal. - if (c.is(op::Or)) + formula m = c[0]; + if (!m.is_boolean()) { - mospliter s(mospliter::Strip_GF | - mospliter::Split_EventUniv, - c, c_); - s.res_EventUniv-> - push_back(unop_multop(op::G, op::Or, - std::move(*s.res_other))); - s.res_EventUniv-> - push_back(unop_unop_multop(op::G, op::F, op::Or, - std::move(*s.res_GF))); - formula res = - formula::Or(std::move(*s.res_EventUniv)); - - if (res != f) - return recurse(res); - } - // If e3 and e4 are eventual formulae and h is not: - // G(f1 & f2 & Ge3 & e4 & GFg & Gh) - // = G(f1 & f2 & e3 & e4 & Fg & h) - // or - // G(f1 & f2 & Ge3 & e4 & GFg & Gh) - // = G(f1 & f2 & h) & G(e3 & e4 & Fg) - // depending on whether favor_event_univ is set. - else if (c.is(op::And)) - { - int w = mospliter::Strip_G; - if (opt_.favor_event_univ) - w |= mospliter::Split_Event; - mospliter s(w, c, c_); - s.res_other->insert(s.res_other->end(), - s.res_G->begin(), s.res_G->end()); - formula res = unop_multop(op::G, op::And, - std::move(*s.res_other)); - - if (s.res_Event) - { - // Strip any G. - for (auto& g: *s.res_Event) - if (g.is(op::G)) - g = g[0]; - formula ge = - unop_multop(op::G, op::And, - std::move(*s.res_Event)); - res = formula::And({res, ge}); - } - if (res != f) - return recurse(res); - } - - // GF(a | Xb) = GF(a | b) - // GF(a | Fb) = GF(a | b) - if (c.is({op::F, op::Or})) - { - formula m = c[0]; - if (!m.is_boolean()) - { - formula out = m.map([](formula f) - { - if (f.is(op::X, op::F)) - return f[0]; - return f; - }); - if (out != m) - return recurse(unop_unop(op::G, op::F, out)); - } + formula out = m.map([](formula f) + { + if (f.is(op::X, op::F)) + return f[0]; + return f; + }); + if (out != m) + return recurse(unop_unop(op::G, op::F, out)); } } - // if a => Ga, keep a. - if (opt_.containment_checks_stronger - && c_->lcc.contained(c, f)) - return c; } - return f; - case op::Closure: - case op::NegClosure: - case op::NegClosureMarked: - { - formula c = f[0]; - // {e[*]} = {e} - // !{e[*]} = !{e} - if (c.accepts_eword() && c.is(op::Star)) - return recurse(formula::unop(o, c[0])); + // if a => Ga, keep a. + if (opt_.containment_checks_stronger + && c_->lcc.contained(c, f)) + return c; + } + return f; + case op::Closure: + case op::NegClosure: + case op::NegClosureMarked: + { + formula c = f[0]; + // {e[*]} = {e} + // !{e[*]} = !{e} + if (c.accepts_eword() && c.is(op::Star)) + return recurse(formula::unop(o, c[0])); - if (!opt_.reduce_size_strictly) - if (c.is(op::OrRat)) - { - // {a₁|a₂} = {a₁}| {a₂} - // !{a₁|a₂} = !{a₁}&!{a₂} - unsigned s = c.size(); - vec v; - for (unsigned n = 0; n < s; ++n) - v.push_back(formula::unop(o, c[n])); - return recurse(formula::multop(o == op::Closure - ? op::Or : op::And, v)); - } - if (c.is(op::Concat)) + if (!opt_.reduce_size_strictly) + if (c.is(op::OrRat)) + { + // {a₁|a₂} = {a₁}| {a₂} + // !{a₁|a₂} = !{a₁}&!{a₂} + unsigned s = c.size(); + vec v; + for (unsigned n = 0; n < s; ++n) + v.push_back(formula::unop(o, c[n])); + return recurse(formula::multop(o == op::Closure + ? op::Or : op::And, v)); + } + if (c.is(op::Concat)) + { + if (c.accepts_eword()) { - if (c.accepts_eword()) + if (opt_.reduce_size_strictly) + return f; + // If all terms accept the empty word, we have + // {e₁;e₂;e₃} = {e₁}|{e₂}|{e₃} + // !{e₁;e₂;e₃} = !{e₁}&!{e₂}&!{e₃} + vec v; + unsigned end = c.size(); + v.reserve(end); + for (unsigned i = 0; i < end; ++i) + v.push_back(formula::unop(o, c[i])); + return recurse(formula::multop(o == op::Closure ? + op::Or : op::And, v)); + } + + // Some term does not accept the empty word. + unsigned end = c.size() - 1; + // {b₁;b₂;e₁;f₁;e₂;f₂;e₂;e₃;e₄} + // = b₁&X(b₂&X({e₁;f₁;e₂;f₂})) + // !{b₁;b₂;e₁;f₁;e₂;f₂;e₂;e₃;e₄} + // = !b₁|X(!b₂|X(!{e₁;f₁;e₂;f₂})) + // if e denotes a term that accepts [*0] + // and b denotes a Boolean formula. + while (c[end].accepts_eword()) + --end; + unsigned start = 0; + while (start <= end) + { + formula r = c[start]; + if (r.is_boolean() && !opt_.reduce_size_strictly) + ++start; + else + break; + } + unsigned s = end + 1 - start; + if (s != c.size()) + { + bool doneg = o != op::Closure; + formula tail; + if (s > 0) { - if (opt_.reduce_size_strictly) - return f; - // If all terms accept the empty word, we have - // {e₁;e₂;e₃} = {e₁}|{e₂}|{e₃} - // !{e₁;e₂;e₃} = !{e₁}&!{e₂}&!{e₃} vec v; - unsigned end = c.size(); - v.reserve(end); - for (unsigned i = 0; i < end; ++i) - v.push_back(formula::unop(o, c[i])); - return recurse(formula::multop(o == op::Closure ? - op::Or : op::And, v)); + v.reserve(s); + for (unsigned n = start; n <= end; ++n) + v.push_back(c[n]); + tail = formula::Concat(v); + tail = formula::unop(o, tail); } - - // Some term does not accept the empty word. - unsigned end = c.size() - 1; - // {b₁;b₂;e₁;f₁;e₂;f₂;e₂;e₃;e₄} - // = b₁&X(b₂&X({e₁;f₁;e₂;f₂})) - // !{b₁;b₂;e₁;f₁;e₂;f₂;e₂;e₃;e₄} - // = !b₁|X(!b₂|X(!{e₁;f₁;e₂;f₂})) - // if e denotes a term that accepts [*0] - // and b denotes a Boolean formula. - while (c[end].accepts_eword()) - --end; - unsigned start = 0; - while (start <= end) + else { - formula r = c[start]; - if (r.is_boolean() && !opt_.reduce_size_strictly) - ++start; - else - break; + tail = doneg ? formula::ff() : formula::tt(); } - unsigned s = end + 1 - start; - if (s != c.size()) + + for (unsigned n = start; n > 0;) { - bool doneg = o != op::Closure; - formula tail; - if (s > 0) + --n; + formula e = c[n]; + // {b;f} = b & X{f} + // !{b;f} = !b | X!{f} + if (e.is_boolean()) { - vec v; - v.reserve(s); - for (unsigned n = start; n <= end; ++n) - v.push_back(c[n]); - tail = formula::Concat(v); - tail = formula::unop(o, tail); + tail = formula::X(tail); + if (doneg) + tail = formula::Or({formula::Not(e), tail}); + else + tail = formula::And({e, tail}); } - else - { - tail = doneg ? formula::ff() : formula::tt(); - } - - for (unsigned n = start; n > 0;) - { - --n; - formula e = c[n]; - // {b;f} = b & X{f} - // !{b;f} = !b | X!{f} - if (e.is_boolean()) - { - tail = formula::X(tail); - if (doneg) - tail = formula::Or({formula::Not(e), tail}); - else - tail = formula::And({e, tail}); - } - } - return recurse(tail); } - - // {b[*i..j];c} = b&X(b&X(... b&X{b[*0..j-i];c})) - // !{b[*i..j];c} = !b&X(!b&X(... !b&X!{b[*0..j-i];c})) - if (!opt_.reduce_size_strictly) - if (c[0].is(op::Star)) - { - formula s = c[0]; - formula sc = s[0]; - unsigned min = s.min(); - if (sc.is_boolean() && min > 0) - { - unsigned max = s.max(); - if (max != formula::unbounded()) - max -= min; - unsigned ss = c.size(); - vec v; - v.reserve(ss); - v.push_back(formula::Star(sc, 0, max)); - for (unsigned n = 1; n < ss; ++n) - v.push_back(c[n]); - formula tail = formula::Concat(v); - tail = // {b[*0..j-i]} or !{b[*0..j-i]} - formula::unop(o, tail); - tail = - dup_b_x_tail(o != op::Closure, - sc, tail, min); - return recurse(tail); - } - } + return recurse(tail); } - // {b[*i..j]} = b&X(b&X(... b)) with i occurences of b - // !{b[*i..j]} = !b&X(!b&X(... !b)) + + // {b[*i..j];c} = b&X(b&X(... b&X{b[*0..j-i];c})) + // !{b[*i..j];c} = !b&X(!b&X(... !b&X!{b[*0..j-i];c})) if (!opt_.reduce_size_strictly) - if (c.is(op::Star)) + if (c[0].is(op::Star)) { - formula cs = c[0]; - if (cs.is_boolean()) + formula s = c[0]; + formula sc = s[0]; + unsigned min = s.min(); + if (sc.is_boolean() && min > 0) { - unsigned min = c.min(); - assert(min > 0); - formula tail; - if (o == op::Closure) - tail = dup_b_x_tail(false, cs, - formula::tt(), min); - else - tail = dup_b_x_tail(true, cs, - formula::ff(), min); + unsigned max = s.max(); + if (max != formula::unbounded()) + max -= min; + unsigned ss = c.size(); + vec v; + v.reserve(ss); + v.push_back(formula::Star(sc, 0, max)); + for (unsigned n = 1; n < ss; ++n) + v.push_back(c[n]); + formula tail = formula::Concat(v); + tail = // {b[*0..j-i]} or !{b[*0..j-i]} + formula::unop(o, tail); + tail = + dup_b_x_tail(o != op::Closure, + sc, tail, min); return recurse(tail); } } - return f; } - case op::Xor: - case op::Implies: - case op::Equiv: - case op::U: - case op::R: - case op::W: - case op::M: - case op::EConcat: - case op::EConcatMarked: - case op::UConcat: - return visit_binop(f); - case op::Or: - case op::OrRat: - case op::And: - case op::AndRat: - case op::AndNLM: - case op::Concat: - return visit_multop(f); - case op::Fusion: - return f; - case op::Star: - { - formula h = f[0]; - auto min = f.min(); - if (h.accepts_eword()) - min = 0; - if (min == 0) - h = f.max() == formula::unbounded() - ? c_->star_normal_form(h) - : c_->star_normal_form_bounded(h); - return formula::Star(h, min, f.max()); - } - } - SPOT_UNREACHABLE(); - } - - formula reduce_sere_ltl(formula orig) - { - op bindop = orig.kind(); - formula a = orig[0]; - formula b = orig[1]; - - auto recurse = [this](formula f) - { - return simplify_recursively(f, c_); - }; - - // All this function is documented assuming bindop == - // UConcat, but by changing the following variables it can - // perform the rules for EConcat as well. - op op_g; - op op_w; - op op_r; - op op_and; - bool doneg; - if (bindop == op::UConcat) - { - op_g = op::G; - op_w = op::W; - op_r = op::R; - op_and = op::And; - doneg = true; - } - else // EConcat & EConcatMarked - { - op_g = op::F; - op_w = op::M; - op_r = op::U; - op_and = op::Or; - doneg = false; - } - - if (!opt_.reduce_basics) - return orig; - if (a.is(op::Star)) - { - // {[*]}[]->b = Gb - if (a == formula::one_star()) - return recurse(formula::unop(op_g, b)); - - formula s = a[0]; - unsigned min = a.min(); - unsigned max = a.max(); - // {s[*]}[]->b = b W !s if s is Boolean. - // {s[+]}[]->b = b W !s if s is Boolean. - if (s.is_boolean() && max == formula::unbounded() && min <= 1) - { - formula ns = doneg ? formula::Not(s) : s; - // b W !s - return recurse(formula::binop(op_w, b, ns)); - } - if (opt_.reduce_size_strictly) - return orig; - // {s[*i..j]}[]->b = {s;s;...;s[*1..j-i+1]}[]->b - // = {s}[]->X({s}[]->X(...[]->X({s[*1..j-i+1]}[]->b))) - // if i>0 and s does not accept the empty word - if (min == 0 || s.accepts_eword()) - return orig; - --min; - if (max != formula::unbounded()) - max -= min; // j-i+1 - // Don't rewrite s[1..]. - if (min == 0) - return orig; - formula tail = // {s[*1..j-i]}[]->b - formula::binop(bindop, formula::Star(s, 1, max), b); - for (unsigned n = 0; n < min; ++n) - tail = // {s}[]->X(tail) - formula::binop(bindop, s, formula::X(tail)); - return recurse(tail); - } - else if (a.is(op::Concat)) - { - unsigned s = a.size() - 1; - formula last = a[s]; - // {r;[*]}[]->b = {r}[]->Gb - if (last == formula::one_star()) - return recurse(formula::binop(bindop, a.all_but(s), - formula::unop(op_g, b))); - - formula first = a[0]; - // {[*];r}[]->b = G({r}[]->b) - if (first == formula::one_star()) - return recurse(formula::unop(op_g, - formula::binop(bindop, - a.all_but(0), b))); - - if (opt_.reduce_size_strictly) - return orig; - - // {r;s[*]}[]->b = {r}[]->(b & X(b W !s)) - // if s is Boolean and r does not accept [*0]; - if (last.is_Kleene_star()) // l = s[*] - if (last[0].is_boolean()) - { - formula r = a.all_but(s); - if (!r.accepts_eword()) - { - formula ns = // !s - doneg ? formula::Not(last[0]) : last[0]; - formula w = // b W !s - formula::binop(op_w, b, ns); - formula x = // X(b W !s) - formula::X(w); - formula d = // b & X(b W !s) - formula::multop(op_and, {b, x}); - // {r}[]->(b & X(b W !s)) - return recurse(formula::binop(bindop, r, d)); - } - } - // {s[*];r}[]->b = !s R ({r}[]->b) - // if s is Boolean and r does not accept [*0]; - if (first.is_Kleene_star()) - if (first[0].is_boolean()) - { - formula r = a.all_but(0); - if (!r.accepts_eword()) - { - formula ns = // !s - doneg - ? formula::Not(first[0]) - : first[0]; - formula u = // {r}[]->b - formula::binop(bindop, r, b); - // !s R ({r}[]->b) - return recurse(formula::binop(op_r, ns, u)); - } - } - - // {r₁;r₂;r₃}[]->b = {r₁}[]->X({r₂}[]->X({r₃}[]->b)) - // if r₁, r₂, r₃ do not accept [*0]. - if (!a.accepts_eword()) - { - unsigned count = 0; - for (unsigned n = 0; n <= s; ++n) - count += !a[n].accepts_eword(); - assert(count > 0); - if (count == 1) - return orig; - // Let e denote a term that accepts [*0] - // and let f denote a term that do not. - // A formula such as {e₁;f₁;e₂;e₃;f₂;e₄}[]->b - // in which count==2 will be grouped - // as follows: r₁ = e₁;f₁;e₂;e₃ - // r₂ = f₂;e₄ - // this way we have - // {e₁;f₁;e₂;e₃;f₂;e₄}[]->b = {r₁;r₂;r₃}[]->b - // where r₁ and r₂ do not accept [*0]. - unsigned pos = s + 1; - - // We compute the r formulas from the right - // (i.e., r₂ before r₁.) - vec r; - do - r.insert(r.begin(), a[--pos]); - while (r.front().accepts_eword()); - formula tail = // {r₂}[]->b - formula::binop(bindop, formula::Concat(r), b); - while (--count) - { - vec r; - do - r.insert(r.begin(), a[--pos]); - while (r.front().accepts_eword()); - // If it's the last block, take all leading - // formulae as well. - if (count == 1) - while (pos > 0) - { - r.insert(r.begin(), a[--pos]); - assert(r.front().accepts_eword()); - } - - tail = // X({r₂}[]->b) - formula::X(tail); - tail = // {r₁}[]->X({r₂}[]->b) - formula::binop(bindop, formula::Concat(r), tail); - } - return recurse(tail); - } - } - else if (opt_.reduce_size_strictly) - { - return orig; - } - else if (a.is(op::Fusion)) - { - // {r₁:r₂:r₃}[]->b = {r₁}[]->({r₂}[]->({r₃}[]->b)) - unsigned s = a.size(); - formula tail = b; - do - { - --s; - tail = formula::binop(bindop, a[s], tail); - } - while (s != 0); - return recurse(tail); - } - else if (a.is(op::OrRat)) - { - // {r₁|r₂|r₃}[]->b = ({r₁}[]->b)&({r₂}[]->b)&({r₃}[]->b) - unsigned s = a.size(); - vec v; - for (unsigned n = 0; n < s; ++n) - // {r₁}[]->b - v.push_back(formula::binop(bindop, a[n], b)); - return recurse(formula::multop(op_and, v)); - } - return orig; - } - - formula - visit_binop(formula bo) - { - auto recurse = [this](formula f) - { - return simplify_recursively(f, c_); - }; - op o = bo.kind(); - formula b = bo[1]; - if (opt_.event_univ) - { - trace << "bo: trying eventuniv rules" << std::endl; - /* If b is a pure eventuality formula then a U b = b. - If b is a pure universality formula a R b = b. */ - if ((b.is_eventual() && bo.is(op::U)) - || (b.is_universal() && bo.is(op::R))) - return b; - } - - formula a = bo[0]; - if (opt_.event_univ) - { - /* If a is a pure eventuality formula then a M b = a & b. - If a is a pure universality formula a W b = a | b. */ - if (a.is_eventual() && bo.is(op::M)) - return recurse(formula::And({a, b})); - if (a.is_universal() && bo.is(op::W)) - return recurse(formula::Or({a, b})); - - // e₁ W e₂ = Ge₁ | e₂ - // u₁ M u₂ = Fu₁ & u₂ + // {b[*i..j]} = b&X(b&X(... b)) with i occurences of b + // !{b[*i..j]} = !b&X(!b&X(... !b)) if (!opt_.reduce_size_strictly) - { - if (bo.is(op::W) && a.is_eventual() && b.is_eventual()) - return recurse(formula::Or({formula::G(a), b})); - if (bo.is(op::M) && a.is_universal() && b.is_universal()) - return recurse(formula::And({formula::F(a), b})); - } - - // In the following rewritings we assume that - // - e is a pure eventuality - // - u is purely universal - // - q is purely universal pure eventuality - // (a U (b|e)) = (a U b)|e - // (a W (b|e)) = (a W b)|e - // (a U (b&q)) = (a U b)&q - // ((a&q) M b) = (a M b)&q - // (a R (b&u)) = (a R b)&u - // (a M (b&u)) = (a M b)&u - if (opt_.favor_event_univ) - { - if (bo.is(op::U, op::W)) - if (b.is(op::Or)) + if (c.is(op::Star)) + { + formula cs = c[0]; + if (cs.is_boolean()) { - mospliter s(mospliter::Split_Event, b, c_); - formula b2 = formula::Or(std::move(*s.res_other)); - if (b2 != b) - { - s.res_Event->push_back(formula::binop(o, a, b2)); - return recurse - (formula::Or(std::move(*s.res_Event))); - } + unsigned min = c.min(); + assert(min > 0); + formula tail; + if (o == op::Closure) + tail = dup_b_x_tail(false, cs, + formula::tt(), min); + else + tail = dup_b_x_tail(true, cs, + formula::ff(), min); + return recurse(tail); } - if (bo.is(op::U)) - if (b.is(op::And)) - { - mospliter s(mospliter::Split_EventUniv, b, c_); - formula b2 = formula::And(std::move(*s.res_other)); - if (b2 != b) - { - s.res_EventUniv->push_back(formula::binop(o, - a, b2)); - return recurse - (formula::And(std::move(*s.res_EventUniv))); - } - } - if (bo.is(op::M)) - if (a.is(op::And)) - { - mospliter s(mospliter::Split_EventUniv, a, c_); - formula a2 = formula::And(std::move(*s.res_other)); - if (a2 != a) - { - s.res_EventUniv->push_back(formula::binop(o, - a2, b)); - return recurse - (formula::And(std::move(*s.res_EventUniv))); - } - } - if (bo.is(op::R, op::M)) - if (b.is(op::And)) - { - mospliter s(mospliter::Split_Univ, b, c_); - formula b2 = formula::And(std::move(*s.res_other)); - if (b2 != b) - { - s.res_Univ->push_back(formula::binop(o, a, b2)); - return recurse - (formula::And(std::move(*s.res_Univ))); - } - } - } - trace << "bo: no eventuniv rule matched" << std::endl; + } + return f; } - - // Inclusion-based rules - if (opt_.synt_impl | opt_.containment_checks) + case op::Xor: + case op::Implies: + case op::Equiv: + case op::U: + case op::R: + case op::W: + case op::M: + case op::EConcat: + case op::EConcatMarked: + case op::UConcat: + return visit_binop(f); + case op::Or: + case op::OrRat: + case op::And: + case op::AndRat: + case op::AndNLM: + case op::Concat: + return visit_multop(f); + case op::Fusion: + return f; + case op::Star: { - trace << "bo: trying inclusion-based rules" << std::endl; - switch (o) - { - case op::Xor: - case op::Equiv: - case op::Implies: - SPOT_UNIMPLEMENTED(); - break; - - case op::U: - // if a => b, then a U b = b - // if (a U b) => b, then a U b = b (for stronger containment) - if (c_->implication(a, b) - || (opt_.containment_checks_stronger - && c_->contained(bo, b))) - return b; - // if !a => b, then a U b = Fb - if (c_->implication_neg(a, b, false)) - return formula::F(b); - // if a => b, then a U (b U c) = (b U c) - // if a => b, then a U (b W c) = (b W c) - if (b.is(op::U, op::W) && c_->implication(a, b[0])) - return b; - // if b => a, then a U (b U c) = (a U c) - if (b.is(op::U) && c_->implication(b[0], a)) - return recurse(formula::U(a, b[1])); - // if a => c, then a U (b R (c U d)) = (b R (c U d)) - // if a => c, then a U (b R (c W d)) = (b R (c W d)) - // if a => c, then a U (b M (c U d)) = (b M (c U d)) - // if a => c, then a U (b M (c W d)) = (b M (c W d)) - if (b.is(op::R, op::M)) - { - auto c1 = b[1]; - if (c1.is(op::U, op::W) && c_->implication(a, c1[0])) - return b; - } - // if a => b, then (a U c) U b = c U b - // if a => b, then (a W c) U b = c U b - if (a.is(op::U, op::W) && c_->implication(a[0], b)) - return recurse(formula::U(a[1], b)); - // if c => b, then (a U c) U b = (a U c) | b - if (a.is(op::U) && c_->implication(a[1], b)) - return recurse(formula::Or({a, b})); - break; - - case op::R: - // if b => a, then a R b = b - if (c_->implication(b, a)) - return b; - // if b => !a, then a R b = Gb - if (c_->implication_neg(b, a, true)) - return recurse(formula::G(b)); - // if b => a, then a R (b R c) = b R c - // if b => a, then a R (b M c) = b M c - if (b.is(op::R, op::M) && c_->implication(b[0], a)) - return b; - // if a => b, then a R (b R c) = a R c - if (b.is(op::R) && c_->implication(a, b[0])) - return recurse(formula::R(a, b[1])); - // if b => a, then (a R c) R b = c R b - // if b => a, then (a M c) R b = c R b - // if c => b, then (a R c) R b = (a & c) R b - // if c => b, then (a M c) R b = (a & c) R b - if (a.is(op::R, op::M)) - { - if (c_->implication(b, a[0])) - return recurse(formula::R(a[1], b)); - if (c_->implication(a[1], b)) - { - formula ac = formula::And({a[0], a[1]}); - return recurse(formula::R(ac, b)); - } - } - break; - - case op::W: - // if a => b, then a W b = b - // if a W b => b, then a W b = b (for stronger containment) - if (c_->implication(a, b) - || (opt_.containment_checks_stronger - && c_->contained(bo, b))) - return b; - // if !a => b then a W b = 1 - if (c_->implication_neg(a, b, false)) - return formula::tt(); - // if a => b, then a W (b W c) = b W c - // (Beware: even if a => b we do not have a W (b U c) = b U c) - if (b.is(op::W) && c_->implication(a, b[0])) - return b; - // if b => a, then a W (b U c) = a W c - // if b => a, then a W (b W c) = a W c - if (b.is(op::U, op::W) && c_->implication(b[0], a)) - return recurse(formula::W(a, b[1])); - // if a => b, then (a U c) W b = c W b - // if a => b, then (a W c) W b = c W b - if (a.is(op::U, op::W) && c_->implication(a[0], b)) - return recurse(formula::W(a[1], b)); - // if c => b, then (a W c) W b = (a W c) | b - // if c => b, then (a U c) W b = (a U c) | b - if (a.is(op::U, op::W) && c_->implication(a[1], b)) - return recurse(formula::Or({a, b})); - break; - - case op::M: - // if b => a, then a M b = b - if (c_->implication(b, a)) - return b; - // if b => !a, then a M b = 0 - if (c_->implication_neg(b, a, true)) - return formula::ff(); - // if b => a, then a M (b M c) = b M c - if (b.is(op::M) && c_->implication(b[0], a)) - return b; - // if a => b, then a M (b M c) = a M c - // if a => b, then a M (b R c) = a M c - if (b.is(op::M, op::R) && c_->implication(a, b[0])) - return recurse(formula::M(a, b[1])); - // if b => a, then (a R c) M b = c M b - // if b => a, then (a M c) M b = c M b - if (a.is(op::R, op::M) && c_->implication(b, a[0])) - return recurse(formula::M(a[1], b)); - // if c => b, then (a M c) M b = (a & c) M b - if (a.is(op::M) && c_->implication(a[1], b)) - return - recurse(formula::M(formula::And({a[0], a[1]}), - b)); - break; - - default: - break; - } - trace << "bo: no inclusion-based rules matched" << std::endl; + formula h = f[0]; + auto min = f.min(); + if (h.accepts_eword()) + min = 0; + if (min == 0) + h = f.max() == formula::unbounded() + ? c_->star_normal_form(h) + : c_->star_normal_form_bounded(h); + return formula::Star(h, min, f.max()); } + } + SPOT_UNREACHABLE(); + } - if (!opt_.reduce_basics) - { - trace << "bo: basic reductions disabled" << std::endl; - return bo; - } + formula reduce_sere_ltl(formula orig) + { + op bindop = orig.kind(); + formula a = orig[0]; + formula b = orig[1]; - trace << "bo: trying basic reductions" << std::endl; - // Rewrite U,R,W,M as F or G when possible. - // true U b == F(b) - if (bo.is(op::U) && a.is_tt()) - return recurse(formula::F(b)); - // false R b == G(b) - if (bo.is(op::R) && a.is_ff()) - return recurse(formula::G(b)); - // a W false == G(a) - if (bo.is(op::W) && b.is_ff()) - return recurse(formula::G(a)); - // a M true == F(a) - if (bo.is(op::M) && b.is_tt()) - return recurse(formula::F(a)); + auto recurse = [this](formula f) + { + return simplify_recursively(f, c_); + }; - if (bo.is(op::W, op::M) || bo.is(op::U, op::R)) - { - // X(a) U X(b) = X(a U b) - // X(a) R X(b) = X(a R b) - // X(a) W X(b) = X(a W b) - // X(a) M X(b) = X(a M b) - if (a.is(op::X) && b.is(op::X)) - return recurse(formula::X(formula::binop(o, - a[0], b[0]))); + // All this function is documented assuming bindop == + // UConcat, but by changing the following variables it can + // perform the rules for EConcat as well. + op op_g; + op op_w; + op op_r; + op op_and; + bool doneg; + if (bindop == op::UConcat) + { + op_g = op::G; + op_w = op::W; + op_r = op::R; + op_and = op::And; + doneg = true; + } + else // EConcat & EConcatMarked + { + op_g = op::F; + op_w = op::M; + op_r = op::U; + op_and = op::Or; + doneg = false; + } - if (bo.is(op::U, op::W)) - { - // a U Ga = Ga - // a W Ga = Ga - if (b.is(op::G) && a == b[0]) - return b; - // a U (b | c | G(a)) = a W (b | c) - // a W (b | c | G(a)) = a W (b | c) - if (b.is(op::Or)) - for (int i = 0, s = b.size(); i < s; ++i) - { - formula c = b[i]; - if (c.is(op::G) && c[0] == a) - return recurse(formula::W(a, b.all_but(i))); - } - // a U (b & a & c) == (b & c) M a - // a W (b & a & c) == (b & c) R a - if (b.is(op::And)) - for (int i = 0, s = b.size(); i < s; ++i) - if (b[i] == a) - return recurse(formula::binop(o == op::U ? - op::M : op::R, - b.all_but(i), a)); - // If b is Boolean: - // (Xc) U b = b | X(b M c) - // (Xc) W b = b | X(b R c) - if (!opt_.reduce_size_strictly - && a.is(op::X) && b.is_boolean()) - { - formula x = formula::X(formula::binop(o == op::U ? - op::M : op::R, - b, a[0])); - return recurse(formula::Or({b, x})); - } - } - else if (bo.is(op::M, op::R)) - { - // a R Fa = Fa - // a M Fa = Fa - if (b.is(op::F) && b[0] == a) - return b; + if (!opt_.reduce_basics) + return orig; + if (a.is(op::Star)) + { + // {[*]}[]->b = Gb + if (a == formula::one_star()) + return recurse(formula::unop(op_g, b)); - // a R (b & c & F(a)) = a M (b & c) - // a M (b & c & F(a)) = a M (b & c) - if (b.is(op::And)) - for (int i = 0, s = b.size(); i < s; ++i) - { - formula c = b[i]; - if (c.is(op::F) && c[0] == a) - return recurse(formula::M(a, b.all_but(i))); - } - // a M (b | a | c) == (b | c) U a - // a R (b | a | c) == (b | c) W a - if (b.is(op::Or)) - for (int i = 0, s = b.size(); i < s; ++i) - if (b[i] == a) - return recurse(formula::binop(o == op::M ? - op::U : op::W, - b.all_but(i), a)); - // If b is Boolean: - // (Xc) R b = b & X(b W c) - // (Xc) M b = b & X(b U c) - if (!opt_.reduce_size_strictly - && a.is(op::X) && b.is_boolean()) - { - formula x = - formula::X(formula::binop(o == op::M ? op::U : op::W, - b, a[0])); - return recurse(formula::And({b, x})); - } - } - } - if (bo.is(op::UConcat) || bo.is(op::EConcat, op::EConcatMarked)) - return reduce_sere_ltl(bo); - return bo; - } - - formula - visit_multop(formula mo) - { - auto recurse = [this](formula f) - { - return simplify_recursively(f, c_); - }; - - unsigned mos = mo.size(); - - if ((opt_.synt_impl | opt_.containment_checks) - && mo.is(op::Or, op::And)) - for (unsigned i = 0; i < mos; ++i) + formula s = a[0]; + unsigned min = a.min(); + unsigned max = a.max(); + // {s[*]}[]->b = b W !s if s is Boolean. + // {s[+]}[]->b = b W !s if s is Boolean. + if (s.is_boolean() && max == formula::unbounded() && min <= 1) { - formula fi = mo[i]; - formula fo = mo.all_but(i); - // if fi => fo, then fi | fo = fo - // if fo => fi, then fi & fo = fo - if ((mo.is(op::Or) && c_->implication(fi, fo)) - || (mo.is(op::And) && c_->implication(fo, fi))) - return recurse(fo); - // if fi => !fo, then fi & fo = false - // if fo => !fi, then fi & fo = false - // if !fi => fo, then fi | fo = true - // if !fo => fi, then fi | fo = true - bool is_and = mo.is(op::And); - if (c_->implication_neg(fi, fo, is_and) - || c_->implication_neg(fo, fi, is_and)) - return recurse(is_and ? formula::ff() : formula::tt()); + formula ns = doneg ? formula::Not(s) : s; + // b W !s + return recurse(formula::binop(op_w, b, ns)); + } + if (opt_.reduce_size_strictly) + return orig; + // {s[*i..j]}[]->b = {s;s;...;s[*1..j-i+1]}[]->b + // = {s}[]->X({s}[]->X(...[]->X({s[*1..j-i+1]}[]->b))) + // if i>0 and s does not accept the empty word + if (min == 0 || s.accepts_eword()) + return orig; + --min; + if (max != formula::unbounded()) + max -= min; // j-i+1 + // Don't rewrite s[1..]. + if (min == 0) + return orig; + formula tail = // {s[*1..j-i]}[]->b + formula::binop(bindop, formula::Star(s, 1, max), b); + for (unsigned n = 0; n < min; ++n) + tail = // {s}[]->X(tail) + formula::binop(bindop, s, formula::X(tail)); + return recurse(tail); + } + else if (a.is(op::Concat)) + { + unsigned s = a.size() - 1; + formula last = a[s]; + // {r;[*]}[]->b = {r}[]->Gb + if (last == formula::one_star()) + return recurse(formula::binop(bindop, a.all_but(s), + formula::unop(op_g, b))); + + formula first = a[0]; + // {[*];r}[]->b = G({r}[]->b) + if (first == formula::one_star()) + return recurse(formula::unop(op_g, + formula::binop(bindop, + a.all_but(0), b))); + + if (opt_.reduce_size_strictly) + return orig; + + // {r;s[*]}[]->b = {r}[]->(b & X(b W !s)) + // if s is Boolean and r does not accept [*0]; + if (last.is_Kleene_star()) // l = s[*] + if (last[0].is_boolean()) + { + formula r = a.all_but(s); + if (!r.accepts_eword()) + { + formula ns = // !s + doneg ? formula::Not(last[0]) : last[0]; + formula w = // b W !s + formula::binop(op_w, b, ns); + formula x = // X(b W !s) + formula::X(w); + formula d = // b & X(b W !s) + formula::multop(op_and, {b, x}); + // {r}[]->(b & X(b W !s)) + return recurse(formula::binop(bindop, r, d)); + } + } + // {s[*];r}[]->b = !s R ({r}[]->b) + // if s is Boolean and r does not accept [*0]; + if (first.is_Kleene_star()) + if (first[0].is_boolean()) + { + formula r = a.all_but(0); + if (!r.accepts_eword()) + { + formula ns = // !s + doneg + ? formula::Not(first[0]) + : first[0]; + formula u = // {r}[]->b + formula::binop(bindop, r, b); + // !s R ({r}[]->b) + return recurse(formula::binop(op_r, ns, u)); + } + } + + // {r₁;r₂;r₃}[]->b = {r₁}[]->X({r₂}[]->X({r₃}[]->b)) + // if r₁, r₂, r₃ do not accept [*0]. + if (!a.accepts_eword()) + { + unsigned count = 0; + for (unsigned n = 0; n <= s; ++n) + count += !a[n].accepts_eword(); + assert(count > 0); + if (count == 1) + return orig; + // Let e denote a term that accepts [*0] + // and let f denote a term that do not. + // A formula such as {e₁;f₁;e₂;e₃;f₂;e₄}[]->b + // in which count==2 will be grouped + // as follows: r₁ = e₁;f₁;e₂;e₃ + // r₂ = f₂;e₄ + // this way we have + // {e₁;f₁;e₂;e₃;f₂;e₄}[]->b = {r₁;r₂;r₃}[]->b + // where r₁ and r₂ do not accept [*0]. + unsigned pos = s + 1; + + // We compute the r formulas from the right + // (i.e., r₂ before r₁.) + vec r; + do + r.insert(r.begin(), a[--pos]); + while (r.front().accepts_eword()); + formula tail = // {r₂}[]->b + formula::binop(bindop, formula::Concat(r), b); + while (--count) + { + vec r; + do + r.insert(r.begin(), a[--pos]); + while (r.front().accepts_eword()); + // If it's the last block, take all leading + // formulae as well. + if (count == 1) + while (pos > 0) + { + r.insert(r.begin(), a[--pos]); + assert(r.front().accepts_eword()); + } + + tail = // X({r₂}[]->b) + formula::X(tail); + tail = // {r₁}[]->X({r₂}[]->b) + formula::binop(bindop, formula::Concat(r), tail); + } + return recurse(tail); + } + } + else if (opt_.reduce_size_strictly) + { + return orig; + } + else if (a.is(op::Fusion)) + { + // {r₁:r₂:r₃}[]->b = {r₁}[]->({r₂}[]->({r₃}[]->b)) + unsigned s = a.size(); + formula tail = b; + do + { + --s; + tail = formula::binop(bindop, a[s], tail); + } + while (s != 0); + return recurse(tail); + } + else if (a.is(op::OrRat)) + { + // {r₁|r₂|r₃}[]->b = ({r₁}[]->b)&({r₂}[]->b)&({r₃}[]->b) + unsigned s = a.size(); + vec v; + for (unsigned n = 0; n < s; ++n) + // {r₁}[]->b + v.push_back(formula::binop(bindop, a[n], b)); + return recurse(formula::multop(op_and, v)); + } + return orig; + } + + formula + visit_binop(formula bo) + { + auto recurse = [this](formula f) + { + return simplify_recursively(f, c_); + }; + op o = bo.kind(); + formula b = bo[1]; + if (opt_.event_univ) + { + trace << "bo: trying eventuniv rules" << std::endl; + /* If b is a pure eventuality formula then a U b = b. + If b is a pure universality formula a R b = b. */ + if ((b.is_eventual() && bo.is(op::U)) + || (b.is_universal() && bo.is(op::R))) + return b; + } + + formula a = bo[0]; + if (opt_.event_univ) + { + /* If a is a pure eventuality formula then a M b = a & b. + If a is a pure universality formula a W b = a | b. */ + if (a.is_eventual() && bo.is(op::M)) + return recurse(formula::And({a, b})); + if (a.is_universal() && bo.is(op::W)) + return recurse(formula::Or({a, b})); + + // e₁ W e₂ = Ge₁ | e₂ + // u₁ M u₂ = Fu₁ & u₂ + if (!opt_.reduce_size_strictly) + { + if (bo.is(op::W) && a.is_eventual() && b.is_eventual()) + return recurse(formula::Or({formula::G(a), b})); + if (bo.is(op::M) && a.is_universal() && b.is_universal()) + return recurse(formula::And({formula::F(a), b})); } - vec res; - res.reserve(mos); - for (auto f: mo) - res.push_back(f); - op o = mo.kind(); + // In the following rewritings we assume that + // - e is a pure eventuality + // - u is purely universal + // - q is purely universal pure eventuality + // (a U (b|e)) = (a U b)|e + // (a W (b|e)) = (a W b)|e + // (a U (b&q)) = (a U b)&q + // ((a&q) M b) = (a M b)&q + // (a R (b&u)) = (a R b)&u + // (a M (b&u)) = (a M b)&u + if (opt_.favor_event_univ) + { + if (bo.is(op::U, op::W)) + if (b.is(op::Or)) + { + mospliter s(mospliter::Split_Event, b, c_); + formula b2 = formula::Or(std::move(*s.res_other)); + if (b2 != b) + { + s.res_Event->push_back(formula::binop(o, a, b2)); + return recurse + (formula::Or(std::move(*s.res_Event))); + } + } + if (bo.is(op::U)) + if (b.is(op::And)) + { + mospliter s(mospliter::Split_EventUniv, b, c_); + formula b2 = formula::And(std::move(*s.res_other)); + if (b2 != b) + { + s.res_EventUniv->push_back(formula::binop(o, + a, b2)); + return recurse + (formula::And(std::move(*s.res_EventUniv))); + } + } + if (bo.is(op::M)) + if (a.is(op::And)) + { + mospliter s(mospliter::Split_EventUniv, a, c_); + formula a2 = formula::And(std::move(*s.res_other)); + if (a2 != a) + { + s.res_EventUniv->push_back(formula::binop(o, + a2, b)); + return recurse + (formula::And(std::move(*s.res_EventUniv))); + } + } + if (bo.is(op::R, op::M)) + if (b.is(op::And)) + { + mospliter s(mospliter::Split_Univ, b, c_); + formula b2 = formula::And(std::move(*s.res_other)); + if (b2 != b) + { + s.res_Univ->push_back(formula::binop(o, a, b2)); + return recurse + (formula::And(std::move(*s.res_Univ))); + } + } + } + trace << "bo: no eventuniv rule matched" << std::endl; + } - // basics reduction do not concern Boolean formulas, - // so don't waste time trying to apply them. - if (opt_.reduce_basics && !mo.is_boolean()) - { - switch (o) - { - case op::And: - assert(!mo.is_sere_formula()); + // Inclusion-based rules + if (opt_.synt_impl | opt_.containment_checks) + { + trace << "bo: trying inclusion-based rules" << std::endl; + switch (o) + { + case op::Xor: + case op::Equiv: + case op::Implies: + SPOT_UNIMPLEMENTED(); + break; + + case op::U: + // if a => b, then a U b = b + // if (a U b) => b, then a U b = b (for stronger containment) + if (c_->implication(a, b) + || (opt_.containment_checks_stronger + && c_->contained(bo, b))) + return b; + // if !a => b, then a U b = Fb + if (c_->implication_neg(a, b, false)) + return formula::F(b); + // if a => b, then a U (b U c) = (b U c) + // if a => b, then a U (b W c) = (b W c) + if (b.is(op::U, op::W) && c_->implication(a, b[0])) + return b; + // if b => a, then a U (b U c) = (a U c) + if (b.is(op::U) && c_->implication(b[0], a)) + return recurse(formula::U(a, b[1])); + // if a => c, then a U (b R (c U d)) = (b R (c U d)) + // if a => c, then a U (b R (c W d)) = (b R (c W d)) + // if a => c, then a U (b M (c U d)) = (b M (c U d)) + // if a => c, then a U (b M (c W d)) = (b M (c W d)) + if (b.is(op::R, op::M)) { - // a & X(G(a&b...) & c...) = Ga & X(G(b...) & c...) - // a & (Xa W b) = b R a - // a & (Xa U b) = b M a - // a & (b | X(b R a)) = b R a - // a & (b | X(b M a)) = b M a - if (!mo.is_syntactic_stutter_invariant()) // Skip if no X. + auto c1 = b[1]; + if (c1.is(op::U, op::W) && c_->implication(a, c1[0])) + return b; + } + // if a => b, then (a U c) U b = c U b + // if a => b, then (a W c) U b = c U b + if (a.is(op::U, op::W) && c_->implication(a[0], b)) + return recurse(formula::U(a[1], b)); + // if c => b, then (a U c) U b = (a U c) | b + if (a.is(op::U) && c_->implication(a[1], b)) + return recurse(formula::Or({a, b})); + break; + + case op::R: + // if b => a, then a R b = b + if (c_->implication(b, a)) + return b; + // if b => !a, then a R b = Gb + if (c_->implication_neg(b, a, true)) + return recurse(formula::G(b)); + // if b => a, then a R (b R c) = b R c + // if b => a, then a R (b M c) = b M c + if (b.is(op::R, op::M) && c_->implication(b[0], a)) + return b; + // if a => b, then a R (b R c) = a R c + if (b.is(op::R) && c_->implication(a, b[0])) + return recurse(formula::R(a, b[1])); + // if b => a, then (a R c) R b = c R b + // if b => a, then (a M c) R b = c R b + // if c => b, then (a R c) R b = (a & c) R b + // if c => b, then (a M c) R b = (a & c) R b + if (a.is(op::R, op::M)) + { + if (c_->implication(b, a[0])) + return recurse(formula::R(a[1], b)); + if (c_->implication(a[1], b)) { - typedef std::unordered_set fset_t; - typedef std::unordered_map> fmap_t; - fset_t xgset; // XG(...) - fset_t xset; // X(...) - fmap_t wuset; // (X...)W(...) or (X...)U(...) + formula ac = formula::And({a[0], a[1]}); + return recurse(formula::R(ac, b)); + } + } + break; - std::vector tokill(mos); + case op::W: + // if a => b, then a W b = b + // if a W b => b, then a W b = b (for stronger containment) + if (c_->implication(a, b) + || (opt_.containment_checks_stronger + && c_->contained(bo, b))) + return b; + // if !a => b then a W b = 1 + if (c_->implication_neg(a, b, false)) + return formula::tt(); + // if a => b, then a W (b W c) = b W c + // (Beware: even if a => b we do not have a W (b U c) = b U c) + if (b.is(op::W) && c_->implication(a, b[0])) + return b; + // if b => a, then a W (b U c) = a W c + // if b => a, then a W (b W c) = a W c + if (b.is(op::U, op::W) && c_->implication(b[0], a)) + return recurse(formula::W(a, b[1])); + // if a => b, then (a U c) W b = c W b + // if a => b, then (a W c) W b = c W b + if (a.is(op::U, op::W) && c_->implication(a[0], b)) + return recurse(formula::W(a[1], b)); + // if c => b, then (a W c) W b = (a W c) | b + // if c => b, then (a U c) W b = (a U c) | b + if (a.is(op::U, op::W) && c_->implication(a[1], b)) + return recurse(formula::Or({a, b})); + break; - // Make a pass to search for subterms - // of the form XGa or X(... & G(...&a&...) & ...) - for (unsigned n = 0; n < mos; ++n) - { - if (!res[n]) + case op::M: + // if b => a, then a M b = b + if (c_->implication(b, a)) + return b; + // if b => !a, then a M b = 0 + if (c_->implication_neg(b, a, true)) + return formula::ff(); + // if b => a, then a M (b M c) = b M c + if (b.is(op::M) && c_->implication(b[0], a)) + return b; + // if a => b, then a M (b M c) = a M c + // if a => b, then a M (b R c) = a M c + if (b.is(op::M, op::R) && c_->implication(a, b[0])) + return recurse(formula::M(a, b[1])); + // if b => a, then (a R c) M b = c M b + // if b => a, then (a M c) M b = c M b + if (a.is(op::R, op::M) && c_->implication(b, a[0])) + return recurse(formula::M(a[1], b)); + // if c => b, then (a M c) M b = (a & c) M b + if (a.is(op::M) && c_->implication(a[1], b)) + return + recurse(formula::M(formula::And({a[0], a[1]}), + b)); + break; + + default: + break; + } + trace << "bo: no inclusion-based rules matched" << std::endl; + } + + if (!opt_.reduce_basics) + { + trace << "bo: basic reductions disabled" << std::endl; + return bo; + } + + trace << "bo: trying basic reductions" << std::endl; + // Rewrite U,R,W,M as F or G when possible. + // true U b == F(b) + if (bo.is(op::U) && a.is_tt()) + return recurse(formula::F(b)); + // false R b == G(b) + if (bo.is(op::R) && a.is_ff()) + return recurse(formula::G(b)); + // a W false == G(a) + if (bo.is(op::W) && b.is_ff()) + return recurse(formula::G(a)); + // a M true == F(a) + if (bo.is(op::M) && b.is_tt()) + return recurse(formula::F(a)); + + if (bo.is(op::W, op::M) || bo.is(op::U, op::R)) + { + // X(a) U X(b) = X(a U b) + // X(a) R X(b) = X(a R b) + // X(a) W X(b) = X(a W b) + // X(a) M X(b) = X(a M b) + if (a.is(op::X) && b.is(op::X)) + return recurse(formula::X(formula::binop(o, + a[0], b[0]))); + + if (bo.is(op::U, op::W)) + { + // a U Ga = Ga + // a W Ga = Ga + if (b.is(op::G) && a == b[0]) + return b; + // a U (b | c | G(a)) = a W (b | c) + // a W (b | c | G(a)) = a W (b | c) + if (b.is(op::Or)) + for (int i = 0, s = b.size(); i < s; ++i) + { + formula c = b[i]; + if (c.is(op::G) && c[0] == a) + return recurse(formula::W(a, b.all_but(i))); + } + // a U (b & a & c) == (b & c) M a + // a W (b & a & c) == (b & c) R a + if (b.is(op::And)) + for (int i = 0, s = b.size(); i < s; ++i) + if (b[i] == a) + return recurse(formula::binop(o == op::U ? + op::M : op::R, + b.all_but(i), a)); + // If b is Boolean: + // (Xc) U b = b | X(b M c) + // (Xc) W b = b | X(b R c) + if (!opt_.reduce_size_strictly + && a.is(op::X) && b.is_boolean()) + { + formula x = formula::X(formula::binop(o == op::U ? + op::M : op::R, + b, a[0])); + return recurse(formula::Or({b, x})); + } + } + else if (bo.is(op::M, op::R)) + { + // a R Fa = Fa + // a M Fa = Fa + if (b.is(op::F) && b[0] == a) + return b; + + // a R (b & c & F(a)) = a M (b & c) + // a M (b & c & F(a)) = a M (b & c) + if (b.is(op::And)) + for (int i = 0, s = b.size(); i < s; ++i) + { + formula c = b[i]; + if (c.is(op::F) && c[0] == a) + return recurse(formula::M(a, b.all_but(i))); + } + // a M (b | a | c) == (b | c) U a + // a R (b | a | c) == (b | c) W a + if (b.is(op::Or)) + for (int i = 0, s = b.size(); i < s; ++i) + if (b[i] == a) + return recurse(formula::binop(o == op::M ? + op::U : op::W, + b.all_but(i), a)); + // If b is Boolean: + // (Xc) R b = b & X(b W c) + // (Xc) M b = b & X(b U c) + if (!opt_.reduce_size_strictly + && a.is(op::X) && b.is_boolean()) + { + formula x = + formula::X(formula::binop(o == op::M ? op::U : op::W, + b, a[0])); + return recurse(formula::And({b, x})); + } + } + } + if (bo.is(op::UConcat) || bo.is(op::EConcat, op::EConcatMarked)) + return reduce_sere_ltl(bo); + return bo; + } + + formula + visit_multop(formula mo) + { + auto recurse = [this](formula f) + { + return simplify_recursively(f, c_); + }; + + unsigned mos = mo.size(); + + if ((opt_.synt_impl | opt_.containment_checks) + && mo.is(op::Or, op::And)) + for (unsigned i = 0; i < mos; ++i) + { + formula fi = mo[i]; + formula fo = mo.all_but(i); + // if fi => fo, then fi | fo = fo + // if fo => fi, then fi & fo = fo + if ((mo.is(op::Or) && c_->implication(fi, fo)) + || (mo.is(op::And) && c_->implication(fo, fi))) + return recurse(fo); + // if fi => !fo, then fi & fo = false + // if fo => !fi, then fi & fo = false + // if !fi => fo, then fi | fo = true + // if !fo => fi, then fi | fo = true + bool is_and = mo.is(op::And); + if (c_->implication_neg(fi, fo, is_and) + || c_->implication_neg(fo, fi, is_and)) + return recurse(is_and ? formula::ff() : formula::tt()); + } + + vec res; + res.reserve(mos); + for (auto f: mo) + res.push_back(f); + op o = mo.kind(); + + // basics reduction do not concern Boolean formulas, + // so don't waste time trying to apply them. + if (opt_.reduce_basics && !mo.is_boolean()) + { + switch (o) + { + case op::And: + assert(!mo.is_sere_formula()); + { + // a & X(G(a&b...) & c...) = Ga & X(G(b...) & c...) + // a & (Xa W b) = b R a + // a & (Xa U b) = b M a + // a & (b | X(b R a)) = b R a + // a & (b | X(b M a)) = b M a + if (!mo.is_syntactic_stutter_invariant()) // Skip if no X. + { + typedef std::unordered_set fset_t; + typedef std::unordered_map> fmap_t; + fset_t xgset; // XG(...) + fset_t xset; // X(...) + fmap_t wuset; // (X...)W(...) or (X...)U(...) + + std::vector tokill(mos); + + // Make a pass to search for subterms + // of the form XGa or X(... & G(...&a&...) & ...) + for (unsigned n = 0; n < mos; ++n) + { + if (!res[n]) + continue; + if (res[n].is_syntactic_stutter_invariant()) + continue; + + if (formula xarg = is_XWU(res[n])) + { + wuset[xarg].insert(n); continue; - if (res[n].is_syntactic_stutter_invariant()) + } + + // Now we are looking for + // - X(...) + // - b | X(b R ...) + // - b | X(b M ...) + if (formula barg = is_bXbRM(res[n])) + { + wuset[barg[1]].insert(n); continue; + } - if (formula xarg = is_XWU(res[n])) - { - wuset[xarg].insert(n); - continue; - } + if (!res[n].is(op::X)) + continue; - // Now we are looking for - // - X(...) - // - b | X(b R ...) - // - b | X(b M ...) - if (formula barg = is_bXbRM(res[n])) - { - wuset[barg[1]].insert(n); - continue; - } + formula c = res[n][0]; + auto handle_G = [&xgset](formula c) + { + formula a2 = c[0]; + if (a2.is(op::And)) + for (auto c: a2) + xgset.insert(c); + else + xgset.insert(a2); + }; - if (!res[n].is(op::X)) - continue; - - formula c = res[n][0]; - auto handle_G = [&xgset](formula c) - { - formula a2 = c[0]; - if (a2.is(op::And)) - for (auto c: a2) - xgset.insert(c); + if (c.is(op::G)) + { + handle_G(c); + } + else if (c.is(op::And)) + { + for (auto cc: c) + if (cc.is(op::G)) + handle_G(cc); else - xgset.insert(a2); - }; + xset.insert(cc); + } + else + { + xset.insert(c); + } + res[n] = nullptr; + } + // Make a second pass to check if the "a" + // terms can be used to simplify "Xa W b", + // "Xa U b", "b | X(b R a)", or "b | X(b M a)". + vec resorig(res); + for (unsigned n = 0; n < mos; ++n) + { + formula x = resorig[n]; + if (!x) + continue; + fmap_t::const_iterator gs = wuset.find(x); + if (gs == wuset.end()) + continue; - if (c.is(op::G)) - { - handle_G(c); - } - else if (c.is(op::And)) - { - for (auto cc: c) - if (cc.is(op::G)) - handle_G(cc); - else - xset.insert(cc); - } - else - { - xset.insert(c); - } - res[n] = nullptr; - } - // Make a second pass to check if the "a" - // terms can be used to simplify "Xa W b", - // "Xa U b", "b | X(b R a)", or "b | X(b M a)". - vec resorig(res); - for (unsigned n = 0; n < mos; ++n) + for (unsigned pos: gs->second) + { + formula wu = resorig[pos]; + if (wu.is(op::W, op::U)) + { + // a & (Xa W b) = b R a + // a & (Xa U b) = b M a + op t = wu.is(op::U) ? op::M : op::R; + assert(wu[0].is(op::X)); + formula a = wu[0][0]; + formula b = wu[1]; + res[pos] = formula::binop(t, b, a); + } + else + { + // a & (b | X(b R a)) = b R a + // a & (b | X(b M a)) = b M a + wu = is_bXbRM(resorig[pos]); + assert(wu); + res[pos] = wu; + } + // Remember to kill "a". + tokill[n] = true; + } + } + + // Make third pass to search for terms 'a' + // that also appears as 'XGa'. Replace them + // by 'Ga' and delete XGa. + for (unsigned n = 0; n < mos; ++n) + { + formula x = res[n]; + if (!x) + continue; + fset_t::const_iterator g = xgset.find(x); + if (g != xgset.end()) + { + // x can appear only once. + formula gf = *g; + xgset.erase(g); + res[n] = formula::G(x); + } + else if (tokill[n]) + { + res[n] = nullptr; + } + } + + vec xv; + unsigned xgs = xgset.size(); + xv.reserve(xset.size() + 1); + if (xgs > 0) + { + vec xgv; + xgv.reserve(xgs); + for (auto f: xgset) + xgv.push_back(f); + xv.emplace_back(unop_multop(op::G, op::And, xgv)); + } + for (auto f: xset) + xv.emplace_back(f); + res.push_back(unop_multop(op::X, op::And, xv)); + } + + // Gather all operands by type. + mospliter s(mospliter::Strip_X | + mospliter::Strip_FG | + mospliter::Strip_G | + mospliter::Split_F | + mospliter::Split_U_or_W | + mospliter::Split_R_or_M | + mospliter::Split_EventUniv, + res, c_); + + // FG(a) & FG(b) = FG(a & b) + formula allFG = unop_unop_multop(op::F, op::G, op::And, + std::move(*s.res_FG)); + // Xa & Xb = X(a & b) + // Xa & Xb & FG(c) = X(a & b & FG(c)) + // For Universal&Eventual formulae f1...fn we also have: + // Xa & Xb & f1...fn = X(a & b & f1...fn) + if (!s.res_X->empty() && !opt_.favor_event_univ) + { + s.res_X->push_back(allFG); + allFG = nullptr; + s.res_X->insert(s.res_X->begin(), + s.res_EventUniv->begin(), + s.res_EventUniv->end()); + } + else + // If f1...fn are event&univ formulae, with at least + // one formula of the form G(...), + // Rewrite g & f1...fn as g & G(f1..fn) while + // stripping any leading G from f1...fn. + // This gathers eventual&universal formulae + // under the same term. + { + vec eu; + bool seen_g = false; + for (auto f: *s.res_EventUniv) + { + if (f.is_eventual() && f.is_universal()) + { + if (f.is(op::G)) + { + seen_g = true; + eu.push_back(f[0]); + } + else + { + eu.push_back(f); + } + } + else + { + s.res_other->push_back(f); + } + } + if (seen_g) + { + eu.push_back(allFG); + allFG = nullptr; + s.res_other->push_back(unop_multop(op::G, op::And, + eu)); + } + else + { + s.res_other->insert(s.res_other->end(), + eu.begin(), eu.end()); + } + } + + // Xa & Xb & f1...fn = X(a & b & f1...fn) + // is built at the end of this op::And case. + // G(a) & G(b) = G(a & b) + // is built at the end of this op::And case. + + // The following three loops perform these rewritings: + // (a U b) & (c U b) = (a & c) U b + // (a U b) & (c W b) = (a & c) U b + // (a W b) & (c W b) = (a & c) W b + // (a R b) & (a R c) = a R (b & c) + // (a R b) & (a M c) = a M (b & c) + // (a M b) & (a M c) = a M (b & c) + // F(a) & (a R b) = a M b + // F(a) & (a M b) = a M b + // F(b) & (a W b) = a U b + // F(b) & (a U b) = a U b + typedef std::unordered_map fmap_t; + fmap_t uwmap; // associates "b" to "a U b" or "a W b" + fmap_t rmmap; // associates "a" to "a R b" or "a M b" + // (a U b) & (c U b) = (a & c) U b + // (a U b) & (c W b) = (a & c) U b + // (a W b) & (c W b) = (a & c) W b + for (auto i = s.res_U_or_W->begin(); + i != s.res_U_or_W->end(); ++i) + { + formula b = (*i)[1]; + auto j = uwmap.find(b); + if (j == uwmap.end()) + { + // First occurrence. + uwmap[b] = i; + continue; + } + // We already have one occurrence. Merge them. + formula old = *j->second; + op o = op::W; + if (i->is(op::U) || old.is(op::U)) + o = op::U; + formula fst_arg = formula::And({old[0], (*i)[0]}); + *j->second = formula::binop(o, fst_arg, b); + assert(j->second->is(o)); + *i = nullptr; + } + // (a R b) & (a R c) = a R (b & c) + // (a R b) & (a M c) = a M (b & c) + // (a M b) & (a M c) = a M (b & c) + for (auto i = s.res_R_or_M->begin(); + i != s.res_R_or_M->end(); ++i) + { + formula a = (*i)[0]; + auto j = rmmap.find(a); + if (j == rmmap.end()) + { + // First occurrence. + rmmap[a] = i; + continue; + } + // We already have one occurrence. Merge them. + formula old = *j->second; + op o = op::R; + if (i->is(op::M) || old.is(op::M)) + o = op::M; + formula snd_arg = formula::And({old[1], (*i)[1]}); + *j->second = formula::binop(o, a, snd_arg); + assert(j->second->is(o)); + *i = nullptr; + } + // F(b) & (a W b) = a U b + // F(b) & (a U b) = a U b + // F(a) & (a R b) = a M b + // F(a) & (a M b) = a M b + for (auto& f: *s.res_F) + { + bool superfluous = false; + formula c = f[0]; + + fmap_t::iterator j = uwmap.find(c); + if (j != uwmap.end()) + { + superfluous = true; + formula bo = *j->second; + if (bo.is(op::W)) + { + *j->second = formula::U(bo[0], bo[1]); + assert(j->second->is(op::U)); + } + } + j = rmmap.find(c); + if (j != rmmap.end()) + { + superfluous = true; + formula bo = *j->second; + if (bo.is(op::R)) + { + *j->second = formula::M(bo[0], bo[1]); + assert(j->second->is(op::M)); + } + } + if (superfluous) + f = nullptr; + } + + s.res_other->reserve(s.res_other->size() + + s.res_F->size() + + s.res_U_or_W->size() + + s.res_R_or_M->size() + + 3); + s.res_other->insert(s.res_other->end(), + s.res_F->begin(), + s.res_F->end()); + s.res_other->insert(s.res_other->end(), + s.res_U_or_W->begin(), + s.res_U_or_W->end()); + s.res_other->insert(s.res_other->end(), + s.res_R_or_M->begin(), + s.res_R_or_M->end()); + + // Those "G" formulae that are eventual can be + // postponed inside the X term if there is one. + // + // In effect we rewrite + // Xa&Xb&GFc&GFd&Ge as X(a&b&G(Fc&Fd))&Ge + if (!s.res_X->empty() && !opt_.favor_event_univ) + { + vec event; + for (auto& f: *s.res_G) + if (f.is_eventual()) { - formula x = resorig[n]; - if (!x) - continue; - fmap_t::const_iterator gs = wuset.find(x); - if (gs == wuset.end()) - continue; - - for (unsigned pos: gs->second) - { - formula wu = resorig[pos]; - if (wu.is(op::W, op::U)) - { - // a & (Xa W b) = b R a - // a & (Xa U b) = b M a - op t = wu.is(op::U) ? op::M : op::R; - assert(wu[0].is(op::X)); - formula a = wu[0][0]; - formula b = wu[1]; - res[pos] = formula::binop(t, b, a); - } - else - { - // a & (b | X(b R a)) = b R a - // a & (b | X(b M a)) = b M a - wu = is_bXbRM(resorig[pos]); - assert(wu); - res[pos] = wu; - } - // Remember to kill "a". - tokill[n] = true; - } + event.push_back(f); + f = nullptr; // Remove it from res_G. } + s.res_X->push_back(unop_multop(op::G, op::And, + std::move(event))); + } + // G(a) & G(b) & ... = G(a & b & ...) + formula allG = unop_multop(op::G, op::And, + std::move(*s.res_G)); + // Xa & Xb & ... = X(a & b & ...) + formula allX = unop_multop(op::X, op::And, + std::move(*s.res_X)); + s.res_other->push_back(allX); + s.res_other->push_back(allG); + s.res_other->push_back(allFG); + formula r = formula::And(std::move(*s.res_other)); + // If we altered the formula in some way, process + // it another time. + if (r != mo) + return recurse(r); + return r; + } + case op::AndRat: + { + mospliter s(mospliter::Split_Bool, res, c_); + if (!s.res_Bool->empty()) + { + // b1 & b2 & b3 = b1 ∧ b2 ∧ b3 + formula b = formula::And(std::move(*s.res_Bool)); + + vec ares; + for (auto& f: *s.res_other) + switch (f.kind()) + { + case op::Star: + // b && r[*i..j] = b & r if i<=1<=j + // = 0 otherwise + if (f.min() > 1 || f.max() < 1) + return formula::ff(); + ares.push_back(f[0]); + f = nullptr; + break; + case op::Fusion: + // b && {r1:..:rn} = b && r1 && .. && rn + for (auto ri: f) + ares.push_back(ri); + f = nullptr; + break; + case op::Concat: + // b && {r1;...;rn} = + // - b && ri if there is only one ri + // that does not accept [*0] + // - b && (r1|...|rn) if all ri + // do not accept [*0] + // - 0 if more than one ri accept [*0] + { + formula ri = nullptr; + unsigned nonempty = 0; + unsigned rs = f.size(); + for (unsigned j = 0; j < rs; ++j) + { + formula jf = f[j]; + if (!jf.accepts_eword()) + { + ri = jf; + ++nonempty; + } + } + if (nonempty == 1) + { + ares.push_back(ri); + } + else if (nonempty == 0) + { + vec sum; + for (auto j: f) + sum.push_back(j); + ares.emplace_back(formula::OrRat(sum)); + } + else + { + return formula::ff(); + } + f = nullptr; + break; + } + default: + ares.push_back(f); + f = nullptr; + break; + } + ares.push_back(b); + auto r = formula::AndRat(std::move(ares)); + // If we altered the formula in some way, process + // it another time. + if (r != mo) + return recurse(r); + return r; + } + // No Boolean as argument of &&. + + // Look for occurrences of {b;r} or {b:r}. We have + // {b1;r1}&&{b2;r2} = {b1&&b2};{r1&&r2} + // head1 tail1 + // {b1:r1}&&{b2:r2} = {b1&&b2}:{r1&&r2} + // head2 tail2 + vec head1; + vec tail1; + vec head2; + vec tail2; + for (auto& i: *s.res_other) + { + if (!i) + continue; + if (!i.is(op::Concat, op::Fusion)) + continue; + formula h = i[0]; + if (!h.is_boolean()) + continue; + if (i.is(op::Concat)) + { + head1.push_back(h); + tail1.push_back(i.all_but(0)); + } + else // op::Fusion + { + head2.push_back(h); + tail2.push_back(i.all_but(0)); + } + i = nullptr; + } + if (!head1.empty()) + { + formula h = formula::And(std::move(head1)); + formula t = formula::AndRat(std::move(tail1)); + s.res_other->push_back(formula::Concat({h, t})); + } + if (!head2.empty()) + { + formula h = formula::And(std::move(head2)); + formula t = formula::AndRat(std::move(tail2)); + s.res_other->push_back(formula::Fusion({h, t})); + } + + // {r1;b1}&&{r2;b2} = {r1&&r2};{b1∧b2} + // head3 tail3 + // {r1:b1}&&{r2:b2} = {r1&&r2}:{b1∧b2} + // head4 tail4 + vec head3; + vec tail3; + vec head4; + vec tail4; + for (auto& i: *s.res_other) + { + if (!i) + continue; + if (!i.is(op::Concat, op::Fusion)) + continue; + unsigned s = i.size() - 1; + formula t = i[s]; + if (!t.is_boolean()) + continue; + if (i.is(op::Concat)) + { + tail3.push_back(t); + head3.push_back(i.all_but(s)); + } + else // op::Fusion + { + tail4.push_back(t); + head4.push_back(i.all_but(s)); + } + i = nullptr; + } + if (!head3.empty()) + { + formula h = formula::AndRat(std::move(head3)); + formula t = formula::And(std::move(tail3)); + s.res_other->push_back(formula::Concat({h, t})); + } + if (!head4.empty()) + { + formula h = formula::AndRat(std::move(head4)); + formula t = formula::And(std::move(tail4)); + s.res_other->push_back(formula::Fusion({h, t})); + } + + auto r = formula::AndRat(std::move(*s.res_other)); + // If we altered the formula in some way, process + // it another time. + if (r != mo) + return recurse(r); + return r; + } + case op::Or: + { + // a | X(F(a) | c...) = Fa | X(c...) + // a | (Xa R b) = b W a + // a | (Xa M b) = b U a + // a | (b & X(b W a)) = b W a + // a | (b & X(b U a)) = b U a + if (!mo.is_syntactic_stutter_invariant()) // Skip if no X + { + typedef std::unordered_set fset_t; + typedef std::unordered_map> fmap_t; + fset_t xfset; // XF(...) + fset_t xset; // X(...) + fmap_t rmset; // (X...)R(...) or (X...)M(...) or + // b & X(b W ...) or b & X(b U ...) + + std::vector tokill(mos); + + // Make a pass to search for subterms + // of the form XFa or X(... | F(...|a|...) | ...) + for (unsigned n = 0; n < mos; ++n) + { + if (!res[n]) + continue; + if (res[n].is_syntactic_stutter_invariant()) + continue; + + if (formula xarg = is_XRM(res[n])) + { + rmset[xarg].insert(n); + continue; + } + + // Now we are looking for + // - X(...) + // - b & X(b W ...) + // - b & X(b U ...) + if (formula barg = is_bXbWU(res[n])) + { + rmset[barg[1]].insert(n); + continue; + } + + if (!res[n].is(op::X)) + continue; + + formula c = res[n][0]; + + auto handle_F = [&xfset](formula c) + { + formula a2 = c[0]; + if (a2.is(op::Or)) + for (auto c: a2) + xfset.insert(c); + else + xfset.insert(a2); + }; + + if (c.is(op::F)) + { + handle_F(c); + } + else if (c.is(op::Or)) + { + for (auto cc: c) + if (cc.is(op::F)) + handle_F(cc); + else + xset.insert(cc); + } + else + { + xset.insert(c); + } + res[n] = nullptr; + } + // Make a second pass to check if we can + // remove all instance of XF(a). + unsigned allofthem = xfset.size(); + vec resorig(res); + for (unsigned n = 0; n < mos; ++n) + { + formula x = resorig[n]; + if (!x) + continue; + fset_t::const_iterator f = xfset.find(x); + if (f != xfset.end()) + --allofthem; + assert(allofthem != -1U); + // At the same time, check if "a" can also + // be used to simplify "Xa R b", "Xa M b". + // "b & X(b W a)", or "b & X(b U a)". + fmap_t::const_iterator gs = rmset.find(x); + if (gs == rmset.end()) + continue; + for (unsigned pos: gs->second) + { + formula rm = resorig[pos]; + if (rm.is(op::M, op::R)) + { + // a | (Xa R b) = b W a + // a | (Xa M b) = b U a + op t = rm.is(op::M) ? op::U : op::W; + assert(rm[0].is(op::X)); + formula a = rm[0][0]; + formula b = rm[1]; + res[pos] = formula::binop(t, b, a); + } + else + { + // a | (b & X(b W a)) = b W a + // a | (b & X(b U a)) = b U a + rm = is_bXbWU(resorig[pos]); + assert(rm); + res[pos] = rm; + } + // Remember to kill "a". + tokill[n] = true; + } + } + + // If we can remove all of them... + if (allofthem == 0) // Make third pass to search for terms 'a' - // that also appears as 'XGa'. Replace them - // by 'Ga' and delete XGa. + // that also appears as 'XFa'. Replace them + // by 'Fa' and delete XFa. for (unsigned n = 0; n < mos; ++n) { formula x = res[n]; - if (!x) - continue; - fset_t::const_iterator g = xgset.find(x); - if (g != xgset.end()) - { - // x can appear only once. - formula gf = *g; - xgset.erase(g); - res[n] = formula::G(x); - } - else if (tokill[n]) - { - res[n] = nullptr; - } - } - - vec xv; - unsigned xgs = xgset.size(); - xv.reserve(xset.size() + 1); - if (xgs > 0) - { - vec xgv; - xgv.reserve(xgs); - for (auto f: xgset) - xgv.push_back(f); - xv.emplace_back(unop_multop(op::G, op::And, xgv)); - } - for (auto f: xset) - xv.emplace_back(f); - res.push_back(unop_multop(op::X, op::And, xv)); - } - - // Gather all operands by type. - mospliter s(mospliter::Strip_X | - mospliter::Strip_FG | - mospliter::Strip_G | - mospliter::Split_F | - mospliter::Split_U_or_W | - mospliter::Split_R_or_M | - mospliter::Split_EventUniv, - res, c_); - - // FG(a) & FG(b) = FG(a & b) - formula allFG = unop_unop_multop(op::F, op::G, op::And, - std::move(*s.res_FG)); - // Xa & Xb = X(a & b) - // Xa & Xb & FG(c) = X(a & b & FG(c)) - // For Universal&Eventual formulae f1...fn we also have: - // Xa & Xb & f1...fn = X(a & b & f1...fn) - if (!s.res_X->empty() && !opt_.favor_event_univ) - { - s.res_X->push_back(allFG); - allFG = nullptr; - s.res_X->insert(s.res_X->begin(), - s.res_EventUniv->begin(), - s.res_EventUniv->end()); - } - else - // If f1...fn are event&univ formulae, with at least - // one formula of the form G(...), - // Rewrite g & f1...fn as g & G(f1..fn) while - // stripping any leading G from f1...fn. - // This gathers eventual&universal formulae - // under the same term. - { - vec eu; - bool seen_g = false; - for (auto f: *s.res_EventUniv) - { - if (f.is_eventual() && f.is_universal()) - { - if (f.is(op::G)) - { - seen_g = true; - eu.push_back(f[0]); - } - else - { - eu.push_back(f); - } - } - else - { - s.res_other->push_back(f); - } - } - if (seen_g) - { - eu.push_back(allFG); - allFG = nullptr; - s.res_other->push_back(unop_multop(op::G, op::And, - eu)); - } - else - { - s.res_other->insert(s.res_other->end(), - eu.begin(), eu.end()); - } - } - - // Xa & Xb & f1...fn = X(a & b & f1...fn) - // is built at the end of this op::And case. - // G(a) & G(b) = G(a & b) - // is built at the end of this op::And case. - - // The following three loops perform these rewritings: - // (a U b) & (c U b) = (a & c) U b - // (a U b) & (c W b) = (a & c) U b - // (a W b) & (c W b) = (a & c) W b - // (a R b) & (a R c) = a R (b & c) - // (a R b) & (a M c) = a M (b & c) - // (a M b) & (a M c) = a M (b & c) - // F(a) & (a R b) = a M b - // F(a) & (a M b) = a M b - // F(b) & (a W b) = a U b - // F(b) & (a U b) = a U b - typedef std::unordered_map fmap_t; - fmap_t uwmap; // associates "b" to "a U b" or "a W b" - fmap_t rmmap; // associates "a" to "a R b" or "a M b" - // (a U b) & (c U b) = (a & c) U b - // (a U b) & (c W b) = (a & c) U b - // (a W b) & (c W b) = (a & c) W b - for (auto i = s.res_U_or_W->begin(); - i != s.res_U_or_W->end(); ++i) - { - formula b = (*i)[1]; - auto j = uwmap.find(b); - if (j == uwmap.end()) - { - // First occurrence. - uwmap[b] = i; - continue; - } - // We already have one occurrence. Merge them. - formula old = *j->second; - op o = op::W; - if (i->is(op::U) || old.is(op::U)) - o = op::U; - formula fst_arg = formula::And({old[0], (*i)[0]}); - *j->second = formula::binop(o, fst_arg, b); - assert(j->second->is(o)); - *i = nullptr; - } - // (a R b) & (a R c) = a R (b & c) - // (a R b) & (a M c) = a M (b & c) - // (a M b) & (a M c) = a M (b & c) - for (auto i = s.res_R_or_M->begin(); - i != s.res_R_or_M->end(); ++i) - { - formula a = (*i)[0]; - auto j = rmmap.find(a); - if (j == rmmap.end()) - { - // First occurrence. - rmmap[a] = i; - continue; - } - // We already have one occurrence. Merge them. - formula old = *j->second; - op o = op::R; - if (i->is(op::M) || old.is(op::M)) - o = op::M; - formula snd_arg = formula::And({old[1], (*i)[1]}); - *j->second = formula::binop(o, a, snd_arg); - assert(j->second->is(o)); - *i = nullptr; - } - // F(b) & (a W b) = a U b - // F(b) & (a U b) = a U b - // F(a) & (a R b) = a M b - // F(a) & (a M b) = a M b - for (auto& f: *s.res_F) - { - bool superfluous = false; - formula c = f[0]; - - fmap_t::iterator j = uwmap.find(c); - if (j != uwmap.end()) - { - superfluous = true; - formula bo = *j->second; - if (bo.is(op::W)) - { - *j->second = formula::U(bo[0], bo[1]); - assert(j->second->is(op::U)); - } - } - j = rmmap.find(c); - if (j != rmmap.end()) - { - superfluous = true; - formula bo = *j->second; - if (bo.is(op::R)) - { - *j->second = formula::M(bo[0], bo[1]); - assert(j->second->is(op::M)); - } - } - if (superfluous) - f = nullptr; - } - - s.res_other->reserve(s.res_other->size() - + s.res_F->size() - + s.res_U_or_W->size() - + s.res_R_or_M->size() - + 3); - s.res_other->insert(s.res_other->end(), - s.res_F->begin(), - s.res_F->end()); - s.res_other->insert(s.res_other->end(), - s.res_U_or_W->begin(), - s.res_U_or_W->end()); - s.res_other->insert(s.res_other->end(), - s.res_R_or_M->begin(), - s.res_R_or_M->end()); - - // Those "G" formulae that are eventual can be - // postponed inside the X term if there is one. - // - // In effect we rewrite - // Xa&Xb&GFc&GFd&Ge as X(a&b&G(Fc&Fd))&Ge - if (!s.res_X->empty() && !opt_.favor_event_univ) - { - vec event; - for (auto& f: *s.res_G) - if (f.is_eventual()) - { - event.push_back(f); - f = nullptr; // Remove it from res_G. - } - s.res_X->push_back(unop_multop(op::G, op::And, - std::move(event))); - } - - // G(a) & G(b) & ... = G(a & b & ...) - formula allG = unop_multop(op::G, op::And, - std::move(*s.res_G)); - // Xa & Xb & ... = X(a & b & ...) - formula allX = unop_multop(op::X, op::And, - std::move(*s.res_X)); - s.res_other->push_back(allX); - s.res_other->push_back(allG); - s.res_other->push_back(allFG); - formula r = formula::And(std::move(*s.res_other)); - // If we altered the formula in some way, process - // it another time. - if (r != mo) - return recurse(r); - return r; - } - case op::AndRat: - { - mospliter s(mospliter::Split_Bool, res, c_); - if (!s.res_Bool->empty()) - { - // b1 & b2 & b3 = b1 ∧ b2 ∧ b3 - formula b = formula::And(std::move(*s.res_Bool)); - - vec ares; - for (auto& f: *s.res_other) - switch (f.kind()) - { - case op::Star: - // b && r[*i..j] = b & r if i<=1<=j - // = 0 otherwise - if (f.min() > 1 || f.max() < 1) - return formula::ff(); - ares.push_back(f[0]); - f = nullptr; - break; - case op::Fusion: - // b && {r1:..:rn} = b && r1 && .. && rn - for (auto ri: f) - ares.push_back(ri); - f = nullptr; - break; - case op::Concat: - // b && {r1;...;rn} = - // - b && ri if there is only one ri - // that does not accept [*0] - // - b && (r1|...|rn) if all ri - // do not accept [*0] - // - 0 if more than one ri accept [*0] - { - formula ri = nullptr; - unsigned nonempty = 0; - unsigned rs = f.size(); - for (unsigned j = 0; j < rs; ++j) - { - formula jf = f[j]; - if (!jf.accepts_eword()) - { - ri = jf; - ++nonempty; - } - } - if (nonempty == 1) - { - ares.push_back(ri); - } - else if (nonempty == 0) - { - vec sum; - for (auto j: f) - sum.push_back(j); - ares.emplace_back(formula::OrRat(sum)); - } - else - { - return formula::ff(); - } - f = nullptr; - break; - } - default: - ares.push_back(f); - f = nullptr; - break; - } - ares.push_back(b); - auto r = formula::AndRat(std::move(ares)); - // If we altered the formula in some way, process - // it another time. - if (r != mo) - return recurse(r); - return r; - } - // No Boolean as argument of &&. - - // Look for occurrences of {b;r} or {b:r}. We have - // {b1;r1}&&{b2;r2} = {b1&&b2};{r1&&r2} - // head1 tail1 - // {b1:r1}&&{b2:r2} = {b1&&b2}:{r1&&r2} - // head2 tail2 - vec head1; - vec tail1; - vec head2; - vec tail2; - for (auto& i: *s.res_other) - { - if (!i) - continue; - if (!i.is(op::Concat, op::Fusion)) - continue; - formula h = i[0]; - if (!h.is_boolean()) - continue; - if (i.is(op::Concat)) - { - head1.push_back(h); - tail1.push_back(i.all_but(0)); - } - else // op::Fusion - { - head2.push_back(h); - tail2.push_back(i.all_but(0)); - } - i = nullptr; - } - if (!head1.empty()) - { - formula h = formula::And(std::move(head1)); - formula t = formula::AndRat(std::move(tail1)); - s.res_other->push_back(formula::Concat({h, t})); - } - if (!head2.empty()) - { - formula h = formula::And(std::move(head2)); - formula t = formula::AndRat(std::move(tail2)); - s.res_other->push_back(formula::Fusion({h, t})); - } - - // {r1;b1}&&{r2;b2} = {r1&&r2};{b1∧b2} - // head3 tail3 - // {r1:b1}&&{r2:b2} = {r1&&r2}:{b1∧b2} - // head4 tail4 - vec head3; - vec tail3; - vec head4; - vec tail4; - for (auto& i: *s.res_other) - { - if (!i) - continue; - if (!i.is(op::Concat, op::Fusion)) - continue; - unsigned s = i.size() - 1; - formula t = i[s]; - if (!t.is_boolean()) - continue; - if (i.is(op::Concat)) - { - tail3.push_back(t); - head3.push_back(i.all_but(s)); - } - else // op::Fusion - { - tail4.push_back(t); - head4.push_back(i.all_but(s)); - } - i = nullptr; - } - if (!head3.empty()) - { - formula h = formula::AndRat(std::move(head3)); - formula t = formula::And(std::move(tail3)); - s.res_other->push_back(formula::Concat({h, t})); - } - if (!head4.empty()) - { - formula h = formula::AndRat(std::move(head4)); - formula t = formula::And(std::move(tail4)); - s.res_other->push_back(formula::Fusion({h, t})); - } - - auto r = formula::AndRat(std::move(*s.res_other)); - // If we altered the formula in some way, process - // it another time. - if (r != mo) - return recurse(r); - return r; - } - case op::Or: - { - // a | X(F(a) | c...) = Fa | X(c...) - // a | (Xa R b) = b W a - // a | (Xa M b) = b U a - // a | (b & X(b W a)) = b W a - // a | (b & X(b U a)) = b U a - if (!mo.is_syntactic_stutter_invariant()) // Skip if no X - { - typedef std::unordered_set fset_t; - typedef std::unordered_map> fmap_t; - fset_t xfset; // XF(...) - fset_t xset; // X(...) - fmap_t rmset; // (X...)R(...) or (X...)M(...) or - // b & X(b W ...) or b & X(b U ...) - - std::vector tokill(mos); - - // Make a pass to search for subterms - // of the form XFa or X(... | F(...|a|...) | ...) - for (unsigned n = 0; n < mos; ++n) - { - if (!res[n]) - continue; - if (res[n].is_syntactic_stutter_invariant()) - continue; - - if (formula xarg = is_XRM(res[n])) - { - rmset[xarg].insert(n); - continue; - } - - // Now we are looking for - // - X(...) - // - b & X(b W ...) - // - b & X(b U ...) - if (formula barg = is_bXbWU(res[n])) - { - rmset[barg[1]].insert(n); - continue; - } - - if (!res[n].is(op::X)) - continue; - - formula c = res[n][0]; - - auto handle_F = [&xfset](formula c) - { - formula a2 = c[0]; - if (a2.is(op::Or)) - for (auto c: a2) - xfset.insert(c); - else - xfset.insert(a2); - }; - - if (c.is(op::F)) - { - handle_F(c); - } - else if (c.is(op::Or)) - { - for (auto cc: c) - if (cc.is(op::F)) - handle_F(cc); - else - xset.insert(cc); - } - else - { - xset.insert(c); - } - res[n] = nullptr; - } - // Make a second pass to check if we can - // remove all instance of XF(a). - unsigned allofthem = xfset.size(); - vec resorig(res); - for (unsigned n = 0; n < mos; ++n) - { - formula x = resorig[n]; if (!x) continue; fset_t::const_iterator f = xfset.find(x); if (f != xfset.end()) - --allofthem; - assert(allofthem != -1U); - // At the same time, check if "a" can also - // be used to simplify "Xa R b", "Xa M b". - // "b & X(b W a)", or "b & X(b U a)". - fmap_t::const_iterator gs = rmset.find(x); - if (gs == rmset.end()) - continue; - for (unsigned pos: gs->second) { - formula rm = resorig[pos]; - if (rm.is(op::M, op::R)) - { - // a | (Xa R b) = b W a - // a | (Xa M b) = b U a - op t = rm.is(op::M) ? op::U : op::W; - assert(rm[0].is(op::X)); - formula a = rm[0][0]; - formula b = rm[1]; - res[pos] = formula::binop(t, b, a); - } - else - { - // a | (b & X(b W a)) = b W a - // a | (b & X(b U a)) = b U a - rm = is_bXbWU(resorig[pos]); - assert(rm); - res[pos] = rm; - } - // Remember to kill "a". - tokill[n] = true; + // x can appear only once. + formula ff = *f; + xfset.erase(f); + res[n] = formula::F(x); + // We don't need to kill "a" anymore. + tokill[n] = false; } } + // Kill any remaining "a", used to simplify Xa R b + // or Xa M b. + for (unsigned n = 0; n < mos; ++n) + if (tokill[n] && res[n]) + res[n] = nullptr; - // If we can remove all of them... - if (allofthem == 0) - // Make third pass to search for terms 'a' - // that also appears as 'XFa'. Replace them - // by 'Fa' and delete XFa. - for (unsigned n = 0; n < mos; ++n) + // Now rebuild the formula that remains. + vec xv; + size_t xfs = xfset.size(); + xv.reserve(xset.size() + 1); + if (xfs > 0) + { + // Group all XF(a)|XF(b|c|...)|... as XF(a|b|c|...) + vec xfv; + xfv.reserve(xfs); + for (auto f: xfset) + xfv.push_back(f); + xv.push_back(unop_multop(op::F, op::Or, xfv)); + } + // Also gather the remaining Xa | X(b|c) as X(b|c). + for (auto f: xset) + xv.push_back(f); + res.push_back(unop_multop(op::X, op::Or, xv)); + } + + // Gather all operand by type. + mospliter s(mospliter::Strip_X | + mospliter::Strip_GF | + mospliter::Strip_F | + mospliter::Split_G | + mospliter::Split_U_or_W | + mospliter::Split_R_or_M | + mospliter::Split_EventUniv, + res, c_); + // GF(a) | GF(b) = GF(a | b) + formula allGF = unop_unop_multop(op::G, op::F, op::Or, + std::move(*s.res_GF)); + + // Xa | Xb = X(a | b) + // Xa | Xb | GF(c) = X(a | b | GF(c)) + // For Universal&Eventual formula f1...fn we also have: + // Xa | Xb | f1...fn = X(a | b | f1...fn) + if (!s.res_X->empty() && !opt_.favor_event_univ) + { + s.res_X->push_back(allGF); + allGF = nullptr; + s.res_X->insert(s.res_X->end(), + s.res_EventUniv->begin(), + s.res_EventUniv->end()); + } + else if (!opt_.favor_event_univ + && !s.res_F->empty() + && s.res_G->empty() + && s.res_U_or_W->empty() + && s.res_R_or_M->empty() + && s.res_other->empty()) + { + // If there is no X but some F and only + // eventual&universal formulae f1...fn|GF(c), do: + // Fa|Fb|f1...fn|GF(c) = F(a|b|f1...fn|GF(c)) + // + // The reasoning here is that if we should + // move f1...fn|GF(c) inside the "F" only + // if it allows us to move all terms under F, + // allowing a nice initial self-loop. + // + // For instance: + // F(a|GFb) 3st.6tr. with initial self-loop + // Fa|GFb 4st.8tr. without initial self-loop + // + // However, if other terms are presents they will + // prevent the formation of a self-loop, and the + // rewriting is unwelcome: + // F(a|GFb)|Gc 5st.11tr. without initial self-loop + // Fa|GFb|Gc 5st.10tr. without initial self-loop + // (counting the number of "subtransitions" + // or, degeneralizing the automaton amplifies + // these differences) + s.res_F->push_back(allGF); + allGF = nullptr; + s.res_F->insert(s.res_F->end(), + s.res_EventUniv->begin(), + s.res_EventUniv->end()); + } + else if (opt_.favor_event_univ) + { + s.res_EventUniv->push_back(allGF); + allGF = nullptr; + bool seen_f = false; + if (s.res_EventUniv->size() > 1) + { + // If some of the EventUniv formulae start + // with an F, Gather them all under the + // same F. Striping any leading F. + for (auto& f: *s.res_EventUniv) + if (f.is(op::F)) + { + seen_f = true; + f = f[0]; + } + if (seen_f) { - formula x = res[n]; - if (!x) - continue; - fset_t::const_iterator f = xfset.find(x); - if (f != xfset.end()) - { - // x can appear only once. - formula ff = *f; - xfset.erase(f); - res[n] = formula::F(x); - // We don't need to kill "a" anymore. - tokill[n] = false; - } + formula eu = + unop_multop(op::F, op::Or, + std::move(*s.res_EventUniv)); + s.res_other->push_back(eu); } - // Kill any remaining "a", used to simplify Xa R b - // or Xa M b. - for (unsigned n = 0; n < mos; ++n) - if (tokill[n] && res[n]) - res[n] = nullptr; - - // Now rebuild the formula that remains. - vec xv; - size_t xfs = xfset.size(); - xv.reserve(xset.size() + 1); - if (xfs > 0) - { - // Group all XF(a)|XF(b|c|...)|... as XF(a|b|c|...) - vec xfv; - xfv.reserve(xfs); - for (auto f: xfset) - xfv.push_back(f); - xv.push_back(unop_multop(op::F, op::Or, xfv)); - } - // Also gather the remaining Xa | X(b|c) as X(b|c). - for (auto f: xset) - xv.push_back(f); - res.push_back(unop_multop(op::X, op::Or, xv)); - } - - // Gather all operand by type. - mospliter s(mospliter::Strip_X | - mospliter::Strip_GF | - mospliter::Strip_F | - mospliter::Split_G | - mospliter::Split_U_or_W | - mospliter::Split_R_or_M | - mospliter::Split_EventUniv, - res, c_); - // GF(a) | GF(b) = GF(a | b) - formula allGF = unop_unop_multop(op::G, op::F, op::Or, - std::move(*s.res_GF)); - - // Xa | Xb = X(a | b) - // Xa | Xb | GF(c) = X(a | b | GF(c)) - // For Universal&Eventual formula f1...fn we also have: - // Xa | Xb | f1...fn = X(a | b | f1...fn) - if (!s.res_X->empty() && !opt_.favor_event_univ) - { - s.res_X->push_back(allGF); - allGF = nullptr; - s.res_X->insert(s.res_X->end(), - s.res_EventUniv->begin(), - s.res_EventUniv->end()); - } - else if (!opt_.favor_event_univ - && !s.res_F->empty() - && s.res_G->empty() - && s.res_U_or_W->empty() - && s.res_R_or_M->empty() - && s.res_other->empty()) - { - // If there is no X but some F and only - // eventual&universal formulae f1...fn|GF(c), do: - // Fa|Fb|f1...fn|GF(c) = F(a|b|f1...fn|GF(c)) - // - // The reasoning here is that if we should - // move f1...fn|GF(c) inside the "F" only - // if it allows us to move all terms under F, - // allowing a nice initial self-loop. - // - // For instance: - // F(a|GFb) 3st.6tr. with initial self-loop - // Fa|GFb 4st.8tr. without initial self-loop - // - // However, if other terms are presents they will - // prevent the formation of a self-loop, and the - // rewriting is unwelcome: - // F(a|GFb)|Gc 5st.11tr. without initial self-loop - // Fa|GFb|Gc 5st.10tr. without initial self-loop - // (counting the number of "subtransitions" - // or, degeneralizing the automaton amplifies - // these differences) - s.res_F->push_back(allGF); - allGF = nullptr; - s.res_F->insert(s.res_F->end(), - s.res_EventUniv->begin(), - s.res_EventUniv->end()); - } - else if (opt_.favor_event_univ) - { - s.res_EventUniv->push_back(allGF); - allGF = nullptr; - bool seen_f = false; - if (s.res_EventUniv->size() > 1) - { - // If some of the EventUniv formulae start - // with an F, Gather them all under the - // same F. Striping any leading F. - for (auto& f: *s.res_EventUniv) - if (f.is(op::F)) - { - seen_f = true; - f = f[0]; - } - if (seen_f) - { - formula eu = - unop_multop(op::F, op::Or, - std::move(*s.res_EventUniv)); - s.res_other->push_back(eu); - } - } - if (!seen_f) - s.res_other->insert(s.res_other->end(), - s.res_EventUniv->begin(), - s.res_EventUniv->end()); - } - else - { + } + if (!seen_f) s.res_other->insert(s.res_other->end(), s.res_EventUniv->begin(), s.res_EventUniv->end()); - } - // Xa | Xb | f1...fn = X(a | b | f1...fn) - // is built at the end of this multop::Or case. - // F(a) | F(b) = F(a | b) - // is built at the end of this multop::Or case. + } + else + { + s.res_other->insert(s.res_other->end(), + s.res_EventUniv->begin(), + s.res_EventUniv->end()); + } + // Xa | Xb | f1...fn = X(a | b | f1...fn) + // is built at the end of this multop::Or case. + // F(a) | F(b) = F(a | b) + // is built at the end of this multop::Or case. - // The following three loops perform these rewritings: - // (a U b) | (a U c) = a U (b | c) - // (a W b) | (a U c) = a W (b | c) - // (a W b) | (a W c) = a W (b | c) - // (a R b) | (c R b) = (a | c) R b - // (a R b) | (c M b) = (a | c) R b - // (a M b) | (c M b) = (a | c) M b - // G(a) | (a U b) = a W b - // G(a) | (a W b) = a W b - // G(b) | (a R b) = a R b. - // G(b) | (a M b) = a R b. - typedef std::unordered_map fmap_t; - fmap_t uwmap; // associates "a" to "a U b" or "a W b" - fmap_t rmmap; // associates "b" to "a R b" or "a M b" - // (a U b) | (a U c) = a U (b | c) - // (a W b) | (a U c) = a W (b | c) - // (a W b) | (a W c) = a W (b | c) - for (auto i = s.res_U_or_W->begin(); - i != s.res_U_or_W->end(); ++i) - { - formula a = (*i)[0]; - auto j = uwmap.find(a); - if (j == uwmap.end()) - { - // First occurrence. - uwmap[a] = i; - continue; - } - // We already have one occurrence. Merge them. - formula old = *j->second; - op o = op::U; - if (i->is(op::W) || old.is(op::W)) - o = op::W; - formula snd_arg = formula::Or({old[1], (*i)[1]}); - *j->second = formula::binop(o, a, snd_arg); - assert(j->second->is(o)); - *i = nullptr; - } - // (a R b) | (c R b) = (a | c) R b - // (a R b) | (c M b) = (a | c) R b - // (a M b) | (c M b) = (a | c) M b - for (auto i = s.res_R_or_M->begin(); - i != s.res_R_or_M->end(); ++i) - { - formula b = (*i)[1]; - auto j = rmmap.find(b); - if (j == rmmap.end()) - { - // First occurrence. - rmmap[b] = i; - continue; - } - // We already have one occurrence. Merge them. - formula old = *j->second; - op o = op::M; - if (i->is(op::R) || old.is(op::R)) - o = op::R; - formula fst_arg = formula::Or({old[0], (*i)[0]}); - *j->second = formula::binop(o, fst_arg, b); - assert(j->second->is(o)); - *i = nullptr; - } - // G(a) | (a U b) = a W b - // G(a) | (a W b) = a W b - // G(b) | (a R b) = a R b. - // G(b) | (a M b) = a R b. - for (auto& f: *s.res_G) - { - bool superfluous = false; - formula c = f[0]; + // The following three loops perform these rewritings: + // (a U b) | (a U c) = a U (b | c) + // (a W b) | (a U c) = a W (b | c) + // (a W b) | (a W c) = a W (b | c) + // (a R b) | (c R b) = (a | c) R b + // (a R b) | (c M b) = (a | c) R b + // (a M b) | (c M b) = (a | c) M b + // G(a) | (a U b) = a W b + // G(a) | (a W b) = a W b + // G(b) | (a R b) = a R b. + // G(b) | (a M b) = a R b. + typedef std::unordered_map fmap_t; + fmap_t uwmap; // associates "a" to "a U b" or "a W b" + fmap_t rmmap; // associates "b" to "a R b" or "a M b" + // (a U b) | (a U c) = a U (b | c) + // (a W b) | (a U c) = a W (b | c) + // (a W b) | (a W c) = a W (b | c) + for (auto i = s.res_U_or_W->begin(); + i != s.res_U_or_W->end(); ++i) + { + formula a = (*i)[0]; + auto j = uwmap.find(a); + if (j == uwmap.end()) + { + // First occurrence. + uwmap[a] = i; + continue; + } + // We already have one occurrence. Merge them. + formula old = *j->second; + op o = op::U; + if (i->is(op::W) || old.is(op::W)) + o = op::W; + formula snd_arg = formula::Or({old[1], (*i)[1]}); + *j->second = formula::binop(o, a, snd_arg); + assert(j->second->is(o)); + *i = nullptr; + } + // (a R b) | (c R b) = (a | c) R b + // (a R b) | (c M b) = (a | c) R b + // (a M b) | (c M b) = (a | c) M b + for (auto i = s.res_R_or_M->begin(); + i != s.res_R_or_M->end(); ++i) + { + formula b = (*i)[1]; + auto j = rmmap.find(b); + if (j == rmmap.end()) + { + // First occurrence. + rmmap[b] = i; + continue; + } + // We already have one occurrence. Merge them. + formula old = *j->second; + op o = op::M; + if (i->is(op::R) || old.is(op::R)) + o = op::R; + formula fst_arg = formula::Or({old[0], (*i)[0]}); + *j->second = formula::binop(o, fst_arg, b); + assert(j->second->is(o)); + *i = nullptr; + } + // G(a) | (a U b) = a W b + // G(a) | (a W b) = a W b + // G(b) | (a R b) = a R b. + // G(b) | (a M b) = a R b. + for (auto& f: *s.res_G) + { + bool superfluous = false; + formula c = f[0]; - fmap_t::iterator j = uwmap.find(c); - if (j != uwmap.end()) - { - superfluous = true; - formula bo = *j->second; - if (bo.is(op::U)) - { - *j->second = formula::W(bo[0], bo[1]); - assert(j->second->is(op::W)); - } - } - j = rmmap.find(c); - if (j != rmmap.end()) - { - superfluous = true; - formula bo = *j->second; - if (bo.is(op::M)) - { - *j->second = formula::R(bo[0], bo[1]); - assert(j->second->is(op::R)); - } - } - if (superfluous) - f = nullptr; - } - - s.res_other->reserve(s.res_other->size() - + s.res_G->size() - + s.res_U_or_W->size() - + s.res_R_or_M->size() - + 3); - s.res_other->insert(s.res_other->end(), - s.res_G->begin(), - s.res_G->end()); - s.res_other->insert(s.res_other->end(), - s.res_U_or_W->begin(), - s.res_U_or_W->end()); - s.res_other->insert(s.res_other->end(), - s.res_R_or_M->begin(), - s.res_R_or_M->end()); - - // Those "F" formulae that are universal can be - // postponed inside the X term if there is one. - // - // In effect we rewrite - // Xa|Xb|FGc|FGd|Fe as X(a|b|F(Gc|Gd))|Fe - if (!s.res_X->empty()) - { - vec univ; - for (auto& f: *s.res_F) - if (f.is_universal()) + fmap_t::iterator j = uwmap.find(c); + if (j != uwmap.end()) + { + superfluous = true; + formula bo = *j->second; + if (bo.is(op::U)) { - univ.push_back(f); - f = nullptr; // Remove it from res_F. + *j->second = formula::W(bo[0], bo[1]); + assert(j->second->is(op::W)); } - s.res_X->push_back(unop_multop(op::F, op::Or, - std::move(univ))); - } + } + j = rmmap.find(c); + if (j != rmmap.end()) + { + superfluous = true; + formula bo = *j->second; + if (bo.is(op::M)) + { + *j->second = formula::R(bo[0], bo[1]); + assert(j->second->is(op::R)); + } + } + if (superfluous) + f = nullptr; + } - // F(a) | F(b) | ... = F(a | b | ...) - formula allF = unop_multop(op::F, op::Or, - std::move(*s.res_F)); - // Xa | Xb | ... = X(a | b | ...) - formula allX = unop_multop(op::X, op::Or, - std::move(*s.res_X)); - s.res_other->push_back(allX); - s.res_other->push_back(allF); - s.res_other->push_back(allGF); - formula r = formula::Or(std::move(*s.res_other)); - // If we altered the formula in some way, process - // it another time. - if (r != mo) - return recurse(r); - return r; - } - case op::AndNLM: - { - mospliter s(mospliter::Split_Bool, res, c_); - if (!s.res_Bool->empty()) - { - // b1 & b2 & b3 = b1 ∧ b2 ∧ b3 - formula b = formula::And(std::move(*s.res_Bool)); + s.res_other->reserve(s.res_other->size() + + s.res_G->size() + + s.res_U_or_W->size() + + s.res_R_or_M->size() + + 3); + s.res_other->insert(s.res_other->end(), + s.res_G->begin(), + s.res_G->end()); + s.res_other->insert(s.res_other->end(), + s.res_U_or_W->begin(), + s.res_U_or_W->end()); + s.res_other->insert(s.res_other->end(), + s.res_R_or_M->begin(), + s.res_R_or_M->end()); - // now we just consider b & rest - formula rest = formula::AndNLM(std::move(*s.res_other)); - - // We have b & rest = b : rest if rest does not - // accept [*0]. Otherwise b & rest = b | (b : rest) - // FIXME: It would be nice to remove [*0] from rest. - formula r = nullptr; - if (rest.accepts_eword()) + // Those "F" formulae that are universal can be + // postponed inside the X term if there is one. + // + // In effect we rewrite + // Xa|Xb|FGc|FGd|Fe as X(a|b|F(Gc|Gd))|Fe + if (!s.res_X->empty()) + { + vec univ; + for (auto& f: *s.res_F) + if (f.is_universal()) { - // The b & rest = b | (b : rest) rewriting - // augment the size, so do that only when - // explicitly requested. - if (!opt_.reduce_size_strictly) - return recurse(formula::OrRat - ({b, formula::Fusion({b, rest})})); - else - return mo; + univ.push_back(f); + f = nullptr; // Remove it from res_F. } - else - { - return recurse(formula::Fusion({b, rest})); - } - } - // No Boolean as argument of &&. + s.res_X->push_back(unop_multop(op::F, op::Or, + std::move(univ))); + } - // Look for occurrences of {b;r} or {b:r}. We have - // {b1;r1}&{b2;r2} = {b1∧b2};{r1&r2} - // head1 tail1 - // {b1:r1}&{b2:r2} = {b1∧b2}:{r1&r2} - // head2 tail2 - // BEWARE: The second rule is correct only when - // both r1 and r2 do not accept [*0]. - - vec head1; - vec tail1; - vec head2; - vec tail2; - for (auto& i: *s.res_other) - { - if (!i) - continue; - if (!i.is(op::Concat, op::Fusion)) - continue; - formula h = i[0]; - if (!h.is_boolean()) - continue; - if (i.is(op::Concat)) - { - head1.push_back(h); - tail1.push_back(i.all_but(0)); - } - else // op::Fusion - { - formula t = i.all_but(0); - if (t.accepts_eword()) - continue; - head2.push_back(h); - tail2.push_back(t); - } - i = nullptr; - } - if (!head1.empty()) - { - formula h = formula::And(std::move(head1)); - formula t = formula::AndNLM(std::move(tail1)); - s.res_other->push_back(formula::Concat({h, t})); - } - if (!head2.empty()) - { - formula h = formula::And(std::move(head2)); - formula t = formula::AndNLM(std::move(tail2)); - s.res_other->push_back(formula::Fusion({h, t})); - } - - formula r = formula::AndNLM(std::move(*s.res_other)); - // If we altered the formula in some way, process - // it another time. - if (r != mo) - return recurse(r); - return r; - } - case op::OrRat: - case op::Concat: - case op::Fusion: - // FIXME: No simplifications yet. - return mo; - default: - SPOT_UNIMPLEMENTED(); - return nullptr; + // F(a) | F(b) | ... = F(a | b | ...) + formula allF = unop_multop(op::F, op::Or, + std::move(*s.res_F)); + // Xa | Xb | ... = X(a | b | ...) + formula allX = unop_multop(op::X, op::Or, + std::move(*s.res_X)); + s.res_other->push_back(allX); + s.res_other->push_back(allF); + s.res_other->push_back(allGF); + formula r = formula::Or(std::move(*s.res_other)); + // If we altered the formula in some way, process + // it another time. + if (r != mo) + return recurse(r); + return r; } - SPOT_UNREACHABLE(); - } - return mo; - } + case op::AndNLM: + { + mospliter s(mospliter::Split_Bool, res, c_); + if (!s.res_Bool->empty()) + { + // b1 & b2 & b3 = b1 ∧ b2 ∧ b3 + formula b = formula::And(std::move(*s.res_Bool)); - protected: - ltl_simplifier_cache* c_; - const ltl_simplifier_options& opt_; - }; + // now we just consider b & rest + formula rest = formula::AndNLM(std::move(*s.res_other)); + // We have b & rest = b : rest if rest does not + // accept [*0]. Otherwise b & rest = b | (b : rest) + // FIXME: It would be nice to remove [*0] from rest. + formula r = nullptr; + if (rest.accepts_eword()) + { + // The b & rest = b | (b : rest) rewriting + // augment the size, so do that only when + // explicitly requested. + if (!opt_.reduce_size_strictly) + return recurse(formula::OrRat + ({b, formula::Fusion({b, rest})})); + else + return mo; + } + else + { + return recurse(formula::Fusion({b, rest})); + } + } + // No Boolean as argument of &&. - formula - simplify_recursively(formula f, - ltl_simplifier_cache* c) - { -#ifdef TRACE - static int srec = 0; - for (int i = srec; i; --i) - trace << ' '; - trace << "** simplify_recursively(" << str_psl(f) << ')'; -#endif + // Look for occurrences of {b;r} or {b:r}. We have + // {b1;r1}&{b2;r2} = {b1∧b2};{r1&r2} + // head1 tail1 + // {b1:r1}&{b2:r2} = {b1∧b2}:{r1&r2} + // head2 tail2 + // BEWARE: The second rule is correct only when + // both r1 and r2 do not accept [*0]. - formula result = c->lookup_simplified(f); - if (result) - { - trace << " cached: " << str_psl(result) << std::endl; - return result; + vec head1; + vec tail1; + vec head2; + vec tail2; + for (auto& i: *s.res_other) + { + if (!i) + continue; + if (!i.is(op::Concat, op::Fusion)) + continue; + formula h = i[0]; + if (!h.is_boolean()) + continue; + if (i.is(op::Concat)) + { + head1.push_back(h); + tail1.push_back(i.all_but(0)); + } + else // op::Fusion + { + formula t = i.all_but(0); + if (t.accepts_eword()) + continue; + head2.push_back(h); + tail2.push_back(t); + } + i = nullptr; + } + if (!head1.empty()) + { + formula h = formula::And(std::move(head1)); + formula t = formula::AndNLM(std::move(tail1)); + s.res_other->push_back(formula::Concat({h, t})); + } + if (!head2.empty()) + { + formula h = formula::And(std::move(head2)); + formula t = formula::AndNLM(std::move(tail2)); + s.res_other->push_back(formula::Fusion({h, t})); + } + + formula r = formula::AndNLM(std::move(*s.res_other)); + // If we altered the formula in some way, process + // it another time. + if (r != mo) + return recurse(r); + return r; + } + case op::OrRat: + case op::Concat: + case op::Fusion: + // FIXME: No simplifications yet. + return mo; + default: + SPOT_UNIMPLEMENTED(); + return nullptr; + } + SPOT_UNREACHABLE(); } - else - { - trace << " miss" << std::endl; - } - -#ifdef TRACE - ++srec; -#endif - - if (f.is_boolean() && c->options.boolean_to_isop) - { - result = c->boolean_to_isop(f); - } - else - { - simplify_visitor v(c); - result = v.visit(f); - } - -#ifdef TRACE - --srec; - for (int i = srec; i; --i) - trace << ' '; - trace << "** simplify_recursively(" << str_psl(f) << ") result: " - << str_psl(result) << std::endl; -#endif - - c->cache_simplified(f, result); - return result; + return mo; } - } // anonymous namespace + protected: + ltl_simplifier_cache* c_; + const ltl_simplifier_options& opt_; + }; + + + formula + simplify_recursively(formula f, + ltl_simplifier_cache* c) + { +#ifdef TRACE + static int srec = 0; + for (int i = srec; i; --i) + trace << ' '; + trace << "** simplify_recursively(" << str_psl(f) << ')'; +#endif + + formula result = c->lookup_simplified(f); + if (result) + { + trace << " cached: " << str_psl(result) << std::endl; + return result; + } + else + { + trace << " miss" << std::endl; + } + +#ifdef TRACE + ++srec; +#endif + + if (f.is_boolean() && c->options.boolean_to_isop) + { + result = c->boolean_to_isop(f); + } + else + { + simplify_visitor v(c); + result = v.visit(f); + } + +#ifdef TRACE + --srec; + for (int i = srec; i; --i) + trace << ' '; + trace << "** simplify_recursively(" << str_psl(f) << ") result: " + << str_psl(result) << std::endl; +#endif + + c->cache_simplified(f, result); + return result; + } + + } // anonymous namespace ////////////////////////////////////////////////////////////////////// // ltl_simplifier_cache @@ -3037,413 +3035,412 @@ namespace spot // This implements the recursive rules for syntactic implication. // (To follow this code please look at the table given as an // appendix in the documentation for temporal logic operators.) - inline - bool - ltl_simplifier_cache::syntactic_implication_aux(formula f, formula g) - { - // We first process all lines from the table except the - // first two, and then we process the first two as a fallback. - // - // However for Boolean formulas we skip the bottom lines - // (keeping only the first one) to prevent them from being - // further split. - if (!f.is_boolean()) - // Deal with all lines of the table except the first two. - switch (f.kind()) + inline + bool + ltl_simplifier_cache::syntactic_implication_aux(formula f, formula g) + { + // We first process all lines from the table except the + // first two, and then we process the first two as a fallback. + // + // However for Boolean formulas we skip the bottom lines + // (keeping only the first one) to prevent them from being + // further split. + if (!f.is_boolean()) + // Deal with all lines of the table except the first two. + switch (f.kind()) + { + case op::X: + if (g.is_eventual() && syntactic_implication(f[0], g)) + return true; + if (g.is(op::X) && syntactic_implication(f[0], g[0])) + return true; + break; + + case op::F: + if (g.is_eventual() && syntactic_implication(f[0], g)) + return true; + break; + + case op::G: + if (g.is(op::U, op::R) && syntactic_implication(f[0], g[1])) + return true; + if (g.is(op::W) && (syntactic_implication(f[0], g[0]) + || syntactic_implication(f[0], g[1]))) + return true; + if (g.is(op::M) && (syntactic_implication(f[0], g[0]) + && syntactic_implication(f[0], g[1]))) + return true; + // First column. + if (syntactic_implication(f[0], g)) + return true; + break; + + case op::U: { - case op::X: - if (g.is_eventual() && syntactic_implication(f[0], g)) + formula f1 = f[0]; + formula f2 = f[1]; + if (g.is(op::U, op::W) + && syntactic_implication(f1, g[0]) + && syntactic_implication(f2, g[1])) return true; - if (g.is(op::X) && syntactic_implication(f[0], g[0])) + if (g.is(op::M, op::R) + && syntactic_implication(f1, g[1]) + && syntactic_implication(f2, g[0]) + && syntactic_implication(f2, g[1])) return true; - break; - - case op::F: - if (g.is_eventual() && syntactic_implication(f[0], g)) - return true; - break; - - case op::G: - if (g.is(op::U, op::R) && syntactic_implication(f[0], g[1])) - return true; - if (g.is(op::W) && (syntactic_implication(f[0], g[0]) - || syntactic_implication(f[0], g[1]))) - return true; - if (g.is(op::M) && (syntactic_implication(f[0], g[0]) - && syntactic_implication(f[0], g[1]))) + if (g.is(op::F) && syntactic_implication(f2, g[0])) return true; // First column. - if (syntactic_implication(f[0], g)) + if (syntactic_implication(f1, g) && syntactic_implication(f2, g)) return true; break; - - case op::U: - { - formula f1 = f[0]; - formula f2 = f[1]; - if (g.is(op::U, op::W) - && syntactic_implication(f1, g[0]) - && syntactic_implication(f2, g[1])) - return true; - if (g.is(op::M, op::R) - && syntactic_implication(f1, g[1]) - && syntactic_implication(f2, g[0]) - && syntactic_implication(f2, g[1])) - return true; - if (g.is(op::F) && syntactic_implication(f2, g[0])) - return true; - // First column. - if (syntactic_implication(f1, g) && syntactic_implication(f2, g)) - return true; - break; - } - case op::W: - { - formula f1 = f[0]; - formula f2 = f[1]; - if (g.is(op::U) && (syntactic_implication(f1, g[1]) - && syntactic_implication(f2, g[1]))) - return true; - if (g.is(op::W) && (syntactic_implication(f1, g[0]) - && syntactic_implication(f2, g[1]))) - return true; - if (g.is(op::R) && (syntactic_implication(f1, g[1]) - && syntactic_implication(f2, g[0]) - && syntactic_implication(f2, g[1]))) - return true; - if (g.is(op::F) && (syntactic_implication(f1, g[0]) - && syntactic_implication(f2, g[0]))) - return true; - // First column. - if (syntactic_implication(f1, g) && syntactic_implication(f2, g)) - return true; - break; - } - case op::R: - { - formula f1 = f[0]; - formula f2 = f[1]; - if (g.is(op::W) && (syntactic_implication(f1, g[1]) - && syntactic_implication(f2, g[0]))) - return true; - if (g.is(op::R) && (syntactic_implication(f1, g[0]) - && syntactic_implication(f2, g[1]))) - return true; - if (g.is(op::M) && (syntactic_implication(f2, g[0]) - && syntactic_implication(f2, g[1]))) - return true; - if (g.is(op::F) && syntactic_implication(f2, g[0])) - return true; - // First column. - if (syntactic_implication(f2, g)) - return true; - break; - } - case op::M: - { - formula f1 = f[0]; - formula f2 = f[1]; - if (g.is(op::U, op::W) && (syntactic_implication(f1, g[1]) - && syntactic_implication(f2, - g[0]))) - return true; - if (g.is(op::R, op::M) && (syntactic_implication(f1, g[0]) - && syntactic_implication(f2, - g[1]))) - return true; - if (g.is(op::F) && (syntactic_implication(f1, g[0]) - || syntactic_implication(f2, g[0]))) - return true; - // First column. - if (syntactic_implication(f2, g)) - return true; - break; - } - case op::Or: - { - // If we are checking something like - // (a | b | Xc) => g, - // split it into - // (a | b) => g - // Xc => g - unsigned i = 0; - if (formula bops = f.boolean_operands(&i)) - if (!syntactic_implication(bops, g)) - break; - bool b = true; - unsigned fs = f.size(); - for (; i < fs; ++i) - if (!syntactic_implication(f[i], g)) - { - b = false; - break; - } - if (b) - return true; - break; - } - case op::And: - { - // If we are checking something like - // (a & b & Xc) => g, - // split it into - // (a & b) => g - // Xc => g - unsigned i = 0; - if (formula bops = f.boolean_operands(&i)) - if (syntactic_implication(bops, g)) - return true; - unsigned fs = f.size(); - for (; i < fs; ++i) - if (syntactic_implication(f[i], g)) - return true; - break; - } - default: - break; } - // First two lines of the table. - // (Don't check equality, it has already be done.) - if (!g.is_boolean()) - switch (g.kind()) + case op::W: { - case op::F: - if (syntactic_implication(f, g[0])) + formula f1 = f[0]; + formula f2 = f[1]; + if (g.is(op::U) && (syntactic_implication(f1, g[1]) + && syntactic_implication(f2, g[1]))) return true; - break; - - case op::G: - case op::X: - if (f.is_universal() && syntactic_implication(f, g[0])) + if (g.is(op::W) && (syntactic_implication(f1, g[0]) + && syntactic_implication(f2, g[1]))) return true; - break; - - case op::U: - case op::W: - if (syntactic_implication(f, g[1])) + if (g.is(op::R) && (syntactic_implication(f1, g[1]) + && syntactic_implication(f2, g[0]) + && syntactic_implication(f2, g[1]))) return true; - break; - - case op::M: - case op::R: - if (syntactic_implication(f, g[0]) - && syntactic_implication(f, g[1])) + if (g.is(op::F) && (syntactic_implication(f1, g[0]) + && syntactic_implication(f2, g[0]))) + return true; + // First column. + if (syntactic_implication(f1, g) && syntactic_implication(f2, g)) return true; - break; - - case op::And: - { - // If we are checking something like - // f => (a & b & Xc), - // split it into - // f => (a & b) - // f => Xc - unsigned i = 0; - if (formula bops = g.boolean_operands(&i)) - if (!syntactic_implication(f, bops)) - break; - bool b = true; - unsigned gs = g.size(); - for (; i < gs; ++i) - if (!syntactic_implication(f, g[i])) - { - b = false; - break; - } - if (b) - return true; - break; - } - - case op::Or: - { - // If we are checking something like - // f => (a | b | Xc), - // split it into - // f => (a | b) - // f => Xc - unsigned i = 0; - if (formula bops = g.boolean_operands(&i)) - if (syntactic_implication(f, bops)) - return true; - unsigned gs = g.size(); - for (; i < gs; ++i) - if (syntactic_implication(f, g[i])) - return true; - break; - } - default: break; } + case op::R: + { + formula f1 = f[0]; + formula f2 = f[1]; + if (g.is(op::W) && (syntactic_implication(f1, g[1]) + && syntactic_implication(f2, g[0]))) + return true; + if (g.is(op::R) && (syntactic_implication(f1, g[0]) + && syntactic_implication(f2, g[1]))) + return true; + if (g.is(op::M) && (syntactic_implication(f2, g[0]) + && syntactic_implication(f2, g[1]))) + return true; + if (g.is(op::F) && syntactic_implication(f2, g[0])) + return true; + // First column. + if (syntactic_implication(f2, g)) + return true; + break; + } + case op::M: + { + formula f1 = f[0]; + formula f2 = f[1]; + if (g.is(op::U, op::W) && (syntactic_implication(f1, g[1]) + && syntactic_implication(f2, + g[0]))) + return true; + if (g.is(op::R, op::M) && (syntactic_implication(f1, g[0]) + && syntactic_implication(f2, + g[1]))) + return true; + if (g.is(op::F) && (syntactic_implication(f1, g[0]) + || syntactic_implication(f2, g[0]))) + return true; + // First column. + if (syntactic_implication(f2, g)) + return true; + break; + } + case op::Or: + { + // If we are checking something like + // (a | b | Xc) => g, + // split it into + // (a | b) => g + // Xc => g + unsigned i = 0; + if (formula bops = f.boolean_operands(&i)) + if (!syntactic_implication(bops, g)) + break; + bool b = true; + unsigned fs = f.size(); + for (; i < fs; ++i) + if (!syntactic_implication(f[i], g)) + { + b = false; + break; + } + if (b) + return true; + break; + } + case op::And: + { + // If we are checking something like + // (a & b & Xc) => g, + // split it into + // (a & b) => g + // Xc => g + unsigned i = 0; + if (formula bops = f.boolean_operands(&i)) + if (syntactic_implication(bops, g)) + return true; + unsigned fs = f.size(); + for (; i < fs; ++i) + if (syntactic_implication(f[i], g)) + return true; + break; + } + default: + break; + } + // First two lines of the table. + // (Don't check equality, it has already be done.) + if (!g.is_boolean()) + switch (g.kind()) + { + case op::F: + if (syntactic_implication(f, g[0])) + return true; + break; + + case op::G: + case op::X: + if (f.is_universal() && syntactic_implication(f, g[0])) + return true; + break; + + case op::U: + case op::W: + if (syntactic_implication(f, g[1])) + return true; + break; + + case op::M: + case op::R: + if (syntactic_implication(f, g[0]) + && syntactic_implication(f, g[1])) + return true; + break; + + case op::And: + { + // If we are checking something like + // f => (a & b & Xc), + // split it into + // f => (a & b) + // f => Xc + unsigned i = 0; + if (formula bops = g.boolean_operands(&i)) + if (!syntactic_implication(f, bops)) + break; + bool b = true; + unsigned gs = g.size(); + for (; i < gs; ++i) + if (!syntactic_implication(f, g[i])) + { + b = false; + break; + } + if (b) + return true; + break; + } + + case op::Or: + { + // If we are checking something like + // f => (a | b | Xc), + // split it into + // f => (a | b) + // f => Xc + unsigned i = 0; + if (formula bops = g.boolean_operands(&i)) + if (syntactic_implication(f, bops)) + return true; + unsigned gs = g.size(); + for (; i < gs; ++i) + if (syntactic_implication(f, g[i])) + return true; + break; + } + default: + break; + } + return false; + } + + // Return true if f => g syntactically + bool + ltl_simplifier_cache::syntactic_implication(formula f, + formula g) + { + // We cannot run syntactic_implication on SERE formulae, + // except on Boolean formulae. + if (f.is_sere_formula() && !f.is_boolean()) + return false; + if (g.is_sere_formula() && !g.is_boolean()) return false; - } - // Return true if f => g syntactically - bool - ltl_simplifier_cache::syntactic_implication(formula f, - formula g) + if (f == g) + return true; + if (g.is_tt() || f.is_ff()) + return true; + if (g.is_ff() || f.is_tt()) + return false; + + // Often we compare a literal (an atomic_prop or its negation) + // to another literal. The result is necessarily false. To be + // true, the two literals would have to be equal, but we have + // already checked that. + if (f.is_literal() && g.is_literal()) + return false; + + // Cache lookup { - // We cannot run syntactic_implication on SERE formulae, - // except on Boolean formulae. - if (f.is_sere_formula() && !f.is_boolean()) - return false; - if (g.is_sere_formula() && !g.is_boolean()) - return false; - - if (f == g) - return true; - if (g.is_tt() || f.is_ff()) - return true; - if (g.is_ff() || f.is_tt()) - return false; - - // Often we compare a literal (an atomic_prop or its negation) - // to another literal. The result is necessarily false. To be - // true, the two literals would have to be equal, but we have - // already checked that. - if (f.is_literal() && g.is_literal()) - return false; - - // Cache lookup - { - pairf p(f, g); - syntimpl_cache_t::const_iterator i = syntimpl_.find(p); - if (i != syntimpl_.end()) - return i->second; - } - - bool result; - - if (f.is_boolean() && g.is_boolean()) - result = bdd_implies(as_bdd(f), as_bdd(g)); - else - result = syntactic_implication_aux(f, g); - - // Cache result - { - pairf p(f, g); - syntimpl_[p] = result; - // std::cerr << str_psl(f) << (result ? " ==> " : " =/=> ") - // << str_psl(g) << std::endl; - } - - return result; + pairf p(f, g); + syntimpl_cache_t::const_iterator i = syntimpl_.find(p); + if (i != syntimpl_.end()) + return i->second; } - // If right==false, true if !f1 => f2, false otherwise. - // If right==true, true if f1 => !f2, false otherwise. - bool - ltl_simplifier_cache::syntactic_implication_neg(formula f1, - formula f2, - bool right) + bool result; + + if (f.is_boolean() && g.is_boolean()) + result = bdd_implies(as_bdd(f), as_bdd(g)); + else + result = syntactic_implication_aux(f, g); + + // Cache result { - // We cannot run syntactic_implication_neg on SERE formulae, - // except on Boolean formulae. - if (f1.is_sere_formula() && !f1.is_boolean()) - return false; - if (f2.is_sere_formula() && !f2.is_boolean()) - return false; - if (right) - f2 = nenoform_rec(f2, true, this); - else - f1 = nenoform_rec(f1, true, this); - return syntactic_implication(f1, f2); + pairf p(f, g); + syntimpl_[p] = result; + // std::cerr << str_psl(f) << (result ? " ==> " : " =/=> ") + // << str_psl(g) << std::endl; } + return result; + } - ///////////////////////////////////////////////////////////////////// - // ltl_simplifier + // If right==false, true if !f1 => f2, false otherwise. + // If right==true, true if f1 => !f2, false otherwise. + bool + ltl_simplifier_cache::syntactic_implication_neg(formula f1, + formula f2, + bool right) + { + // We cannot run syntactic_implication_neg on SERE formulae, + // except on Boolean formulae. + if (f1.is_sere_formula() && !f1.is_boolean()) + return false; + if (f2.is_sere_formula() && !f2.is_boolean()) + return false; + if (right) + f2 = nenoform_rec(f2, true, this); + else + f1 = nenoform_rec(f1, true, this); + return syntactic_implication(f1, f2); + } - ltl_simplifier::ltl_simplifier(const bdd_dict_ptr& d) - { - cache_ = new ltl_simplifier_cache(d); - } - ltl_simplifier::ltl_simplifier(const ltl_simplifier_options& opt, - bdd_dict_ptr d) - { - cache_ = new ltl_simplifier_cache(d, opt); - } + ///////////////////////////////////////////////////////////////////// + // ltl_simplifier - ltl_simplifier::~ltl_simplifier() - { - delete cache_; - } + ltl_simplifier::ltl_simplifier(const bdd_dict_ptr& d) + { + cache_ = new ltl_simplifier_cache(d); + } - formula - ltl_simplifier::simplify(formula f) - { - if (!f.is_in_nenoform()) - f = negative_normal_form(f, false); - return simplify_recursively(f, cache_); - } + ltl_simplifier::ltl_simplifier(const ltl_simplifier_options& opt, + bdd_dict_ptr d) + { + cache_ = new ltl_simplifier_cache(d, opt); + } - formula - ltl_simplifier::negative_normal_form(formula f, bool negated) - { - return nenoform_rec(f, negated, cache_); - } + ltl_simplifier::~ltl_simplifier() + { + delete cache_; + } - bool - ltl_simplifier::syntactic_implication(formula f1, formula f2) - { - return cache_->syntactic_implication(f1, f2); - } + formula + ltl_simplifier::simplify(formula f) + { + if (!f.is_in_nenoform()) + f = negative_normal_form(f, false); + return simplify_recursively(f, cache_); + } - bool - ltl_simplifier::syntactic_implication_neg(formula f1, - formula f2, bool right) - { - return cache_->syntactic_implication_neg(f1, f2, right); - } + formula + ltl_simplifier::negative_normal_form(formula f, bool negated) + { + return nenoform_rec(f, negated, cache_); + } - bool - ltl_simplifier::are_equivalent(formula f, formula g) - { - return cache_->lcc.equal(f, g); - } + bool + ltl_simplifier::syntactic_implication(formula f1, formula f2) + { + return cache_->syntactic_implication(f1, f2); + } - bool - ltl_simplifier::implication(formula f, formula g) - { - return cache_->lcc.contained(f, g); - } + bool + ltl_simplifier::syntactic_implication_neg(formula f1, + formula f2, bool right) + { + return cache_->syntactic_implication_neg(f1, f2, right); + } - bdd - ltl_simplifier::as_bdd(formula f) - { - return cache_->as_bdd(f); - } + bool + ltl_simplifier::are_equivalent(formula f, formula g) + { + return cache_->lcc.equal(f, g); + } - formula - ltl_simplifier::star_normal_form(formula f) - { - return cache_->star_normal_form(f); - } + bool + ltl_simplifier::implication(formula f, formula g) + { + return cache_->lcc.contained(f, g); + } - formula - ltl_simplifier::boolean_to_isop(formula f) - { - return cache_->boolean_to_isop(f); - } + bdd + ltl_simplifier::as_bdd(formula f) + { + return cache_->as_bdd(f); + } - bdd_dict_ptr - ltl_simplifier::get_dict() const - { - return cache_->dict; - } + formula + ltl_simplifier::star_normal_form(formula f) + { + return cache_->star_normal_form(f); + } - void - ltl_simplifier::print_stats(std::ostream& os) const - { - cache_->print_stats(os); - } + formula + ltl_simplifier::boolean_to_isop(formula f) + { + return cache_->boolean_to_isop(f); + } - void - ltl_simplifier::clear_as_bdd_cache() - { - cache_->clear_as_bdd_cache(); - cache_->lcc.clear(); - } + bdd_dict_ptr + ltl_simplifier::get_dict() const + { + return cache_->dict; + } + + void + ltl_simplifier::print_stats(std::ostream& os) const + { + cache_->print_stats(os); + } + + void + ltl_simplifier::clear_as_bdd_cache() + { + cache_->clear_as_bdd_cache(); + cache_->lcc.clear(); } } diff --git a/src/tl/simplify.hh b/src/tl/simplify.hh index 7f865c898..0867b2503 100644 --- a/src/tl/simplify.hh +++ b/src/tl/simplify.hh @@ -26,179 +26,176 @@ namespace spot { - namespace ltl + class ltl_simplifier_options { - class ltl_simplifier_options + public: + ltl_simplifier_options(bool basics = true, + bool synt_impl = true, + bool event_univ = true, + bool containment_checks = false, + bool containment_checks_stronger = false, + bool nenoform_stop_on_boolean = false, + bool reduce_size_strictly = false, + bool boolean_to_isop = false, + bool favor_event_univ = false) + : reduce_basics(basics), + synt_impl(synt_impl), + event_univ(event_univ), + containment_checks(containment_checks), + containment_checks_stronger(containment_checks_stronger), + nenoform_stop_on_boolean(nenoform_stop_on_boolean), + reduce_size_strictly(reduce_size_strictly), + boolean_to_isop(boolean_to_isop), + favor_event_univ(favor_event_univ) { - public: - ltl_simplifier_options(bool basics = true, - bool synt_impl = true, - bool event_univ = true, - bool containment_checks = false, - bool containment_checks_stronger = false, - bool nenoform_stop_on_boolean = false, - bool reduce_size_strictly = false, - bool boolean_to_isop = false, - bool favor_event_univ = false) - : reduce_basics(basics), - synt_impl(synt_impl), - event_univ(event_univ), - containment_checks(containment_checks), - containment_checks_stronger(containment_checks_stronger), - nenoform_stop_on_boolean(nenoform_stop_on_boolean), - reduce_size_strictly(reduce_size_strictly), - boolean_to_isop(boolean_to_isop), - favor_event_univ(favor_event_univ) - { - } + } - ltl_simplifier_options(int level) : - ltl_simplifier_options(false, false, false) - { - switch (level) - { - case 3: - containment_checks = true; - containment_checks_stronger = true; - // fall through - case 2: - synt_impl = true; - // fall through - case 1: - reduce_basics = true; - event_univ = true; - // fall through - default: - break; - } - } - - bool reduce_basics; - bool synt_impl; - bool event_univ; - bool containment_checks; - bool containment_checks_stronger; - // If true, Boolean subformulae will not be put into - // negative normal form. - bool nenoform_stop_on_boolean; - // If true, some rules that produce slightly larger formulae - // will be disabled. Those larger formulae are normally easier - // to translate, so we recommend to set this to false. - bool reduce_size_strictly; - // If true, Boolean subformulae will be rewritten in ISOP form. - bool boolean_to_isop; - // Try to isolate subformulae that are eventual and universal. - bool favor_event_univ; - }; - - // fwd declaration to hide technical details. - class ltl_simplifier_cache; - - /// \ingroup ltl_rewriting - /// \brief Rewrite or simplify \a f in various ways. - class SPOT_API ltl_simplifier + ltl_simplifier_options(int level) : + ltl_simplifier_options(false, false, false) { - public: - ltl_simplifier(const bdd_dict_ptr& dict = make_bdd_dict()); - ltl_simplifier(const ltl_simplifier_options& opt, - bdd_dict_ptr dict = make_bdd_dict()); - ~ltl_simplifier(); + switch (level) + { + case 3: + containment_checks = true; + containment_checks_stronger = true; + // fall through + case 2: + synt_impl = true; + // fall through + case 1: + reduce_basics = true; + event_univ = true; + // fall through + default: + break; + } + } - /// Simplify the formula \a f (using options supplied to the - /// constructor). - formula simplify(formula f); + bool reduce_basics; + bool synt_impl; + bool event_univ; + bool containment_checks; + bool containment_checks_stronger; + // If true, Boolean subformulae will not be put into + // negative normal form. + bool nenoform_stop_on_boolean; + // If true, some rules that produce slightly larger formulae + // will be disabled. Those larger formulae are normally easier + // to translate, so we recommend to set this to false. + bool reduce_size_strictly; + // If true, Boolean subformulae will be rewritten in ISOP form. + bool boolean_to_isop; + // Try to isolate subformulae that are eventual and universal. + bool favor_event_univ; + }; - /// Build the negative normal form of formula \a f. - /// All negations of the formula are pushed in front of the - /// atomic propositions. Operators <=>, =>, xor are all removed - /// (calling spot::ltl::unabbreviate_ltl is not needed). - /// - /// \param f The formula to normalize. - /// \param negated If \c true, return the negative normal form of - /// \c !f - formula + // fwd declaration to hide technical details. + class ltl_simplifier_cache; + + /// \ingroup ltl_rewriting + /// \brief Rewrite or simplify \a f in various ways. + class SPOT_API ltl_simplifier + { + public: + ltl_simplifier(const bdd_dict_ptr& dict = make_bdd_dict()); + ltl_simplifier(const ltl_simplifier_options& opt, + bdd_dict_ptr dict = make_bdd_dict()); + ~ltl_simplifier(); + + /// Simplify the formula \a f (using options supplied to the + /// constructor). + formula simplify(formula f); + + /// Build the negative normal form of formula \a f. + /// All negations of the formula are pushed in front of the + /// atomic propositions. Operators <=>, =>, xor are all removed + /// (calling spot::unabbreviate_ltl is not needed). + /// + /// \param f The formula to normalize. + /// \param negated If \c true, return the negative normal form of + /// \c !f + formula negative_normal_form(formula f, bool negated = false); - /// \brief Syntactic implication. - /// - /// Returns whether \a f syntactically implies \a g. - /// - /// This is adapted from - /** \verbatim - @InProceedings{ somenzi.00.cav, - author = {Fabio Somenzi and Roderick Bloem}, - title = {Efficient {B\"u}chi Automata for {LTL} Formulae}, - booktitle = {Proceedings of the 12th International Conference on - Computer Aided Verification (CAV'00)}, - pages = {247--263}, - year = {2000}, - volume = {1855}, - series = {Lecture Notes in Computer Science}, - publisher = {Springer-Verlag} - } - \endverbatim */ - /// - bool syntactic_implication(formula f, formula g); - /// \brief Syntactic implication with one negated argument. - /// - /// If \a right is true, this method returns whether - /// \a f implies !\a g. If \a right is false, this returns - /// whether !\a f implies \a g. - bool syntactic_implication_neg(formula f, formula g, - bool right); + /// \brief Syntactic implication. + /// + /// Returns whether \a f syntactically implies \a g. + /// + /// This is adapted from + /** \verbatim + @InProceedings{ somenzi.00.cav, + author = {Fabio Somenzi and Roderick Bloem}, + title = {Efficient {B\"u}chi Automata for {LTL} Formulae}, + booktitle = {Proceedings of the 12th International Conference on + Computer Aided Verification (CAV'00)}, + pages = {247--263}, + year = {2000}, + volume = {1855}, + series = {Lecture Notes in Computer Science}, + publisher = {Springer-Verlag} + } + \endverbatim */ + /// + bool syntactic_implication(formula f, formula g); + /// \brief Syntactic implication with one negated argument. + /// + /// If \a right is true, this method returns whether + /// \a f implies !\a g. If \a right is false, this returns + /// whether !\a f implies \a g. + bool syntactic_implication_neg(formula f, formula g, + bool right); - /// \brief check whether two formulae are equivalent. - /// - /// This costly check performs up to four translations, - /// two products, and two emptiness checks. - bool are_equivalent(formula f, formula g); + /// \brief check whether two formulae are equivalent. + /// + /// This costly check performs up to four translations, + /// two products, and two emptiness checks. + bool are_equivalent(formula f, formula g); - /// \brief Check whether \a f implies \a g. - /// - /// This operation is costlier than syntactic_implication() - /// because it requires two translation, one product and one - /// emptiness check. - bool implication(formula f, formula g); + /// \brief Check whether \a f implies \a g. + /// + /// This operation is costlier than syntactic_implication() + /// because it requires two translation, one product and one + /// emptiness check. + bool implication(formula f, formula g); - /// \brief Convert a Boolean formula as a BDD. - /// - /// If you plan to use this method, be sure to pass a bdd_dict - /// to the constructor. - bdd as_bdd(formula f); + /// \brief Convert a Boolean formula as a BDD. + /// + /// If you plan to use this method, be sure to pass a bdd_dict + /// to the constructor. + bdd as_bdd(formula f); - /// \brief Clear the as_bdd() cache. - /// - /// Calling this function is recommended before running other - /// algorithms that create BDD variables in a more natural - /// order. For instance ltl_to_tgba_fm() will usually be more - /// efficient if the BDD variables for atomic propositions have - /// not been ordered before hand. - /// - /// This also clears the language containment cache. - void clear_as_bdd_cache(); + /// \brief Clear the as_bdd() cache. + /// + /// Calling this function is recommended before running other + /// algorithms that create BDD variables in a more natural + /// order. For instance ltl_to_tgba_fm() will usually be more + /// efficient if the BDD variables for atomic propositions have + /// not been ordered before hand. + /// + /// This also clears the language containment cache. + void clear_as_bdd_cache(); - /// Return the bdd_dict used. - bdd_dict_ptr get_dict() const; + /// Return the bdd_dict used. + bdd_dict_ptr get_dict() const; - /// Cached version of spot::ltl::star_normal_form(). - formula star_normal_form(formula f); + /// Cached version of spot::star_normal_form(). + formula star_normal_form(formula f); - /// \brief Rewrite a Boolean formula \a f into as an irredundant - /// sum of product. - /// - /// This uses a cache, so it is OK to call this with identical - /// arguments. - formula boolean_to_isop(formula f); + /// \brief Rewrite a Boolean formula \a f into as an irredundant + /// sum of product. + /// + /// This uses a cache, so it is OK to call this with identical + /// arguments. + formula boolean_to_isop(formula f); - /// Dump statistics about the caches. - void print_stats(std::ostream& os) const; + /// Dump statistics about the caches. + void print_stats(std::ostream& os) const; - private: - ltl_simplifier_cache* cache_; - // Copy disallowed. - ltl_simplifier(const ltl_simplifier&) SPOT_DELETED; - void operator=(const ltl_simplifier&) SPOT_DELETED; - }; - } + private: + ltl_simplifier_cache* cache_; + // Copy disallowed. + ltl_simplifier(const ltl_simplifier&) SPOT_DELETED; + void operator=(const ltl_simplifier&) SPOT_DELETED; + }; } diff --git a/src/tl/snf.cc b/src/tl/snf.cc index ff2f3a4ba..48392b772 100644 --- a/src/tl/snf.cc +++ b/src/tl/snf.cc @@ -21,129 +21,125 @@ namespace spot { - namespace ltl + namespace { - namespace + // E° if bounded=false + // E^□ if nounded=true + template + class snf_visitor { - // E° if bounded=false - // E^□ if nounded=true - template - class snf_visitor + protected: + formula result_; + snf_cache* cache_; + public: + snf_visitor(snf_cache* c) + : cache_(c) { - protected: - formula result_; - snf_cache* cache_; - public: - snf_visitor(snf_cache* c) - : cache_(c) - { - } + } - formula visit(formula f) - { - if (!f.accepts_eword()) - return f; + formula visit(formula f) + { + if (!f.accepts_eword()) + return f; - snf_cache::const_iterator i = cache_->find(f); - if (i != cache_->end()) - return i->second; + snf_cache::const_iterator i = cache_->find(f); + if (i != cache_->end()) + return i->second; - formula out; - switch (f.kind()) - { - case op::eword: - out = formula::ff(); - break; - case op::Star: - if (!bounded) - out = visit(f[0]); // Strip the star. - else - out = formula::Star(visit(f[0]), - std::max(unsigned(f.min()), 1U), f.max()); - break; - case op::Concat: - if (bounded) - { - out = f; - break; - } - // Fall through - case op::OrRat: - case op::AndNLM: - // Let F designate expressions that accept [*0], - // and G designate expressions that do not. - - // (G₁;G₂;G₃)° = G₁;G₂;G₃ - // (G₁;F₂;G₃)° = (G₁°);F₂;(G₃°) = G₁;F₂;G₃ - // because there is nothing to do recursively on a G. - // - // AndNLM can be dealt with similarly. - // - // The above cases are already handled by the - // accepts_eword() tests at the top of this method. So - // we reach this switch, we only have to deal with... - // - // (F₁;F₂;F₃)° = (F₁°)|(F₂°)|(F₃°) - // (F₁&F₂&F₃)° = (F₁°)|(F₂°)|(F₃°) - // (F₁|G₂|F₃)° = (F₁°)|(G₂°)|(F₃°) + formula out; + switch (f.kind()) + { + case op::eword: + out = formula::ff(); + break; + case op::Star: + if (!bounded) + out = visit(f[0]); // Strip the star. + else + out = formula::Star(visit(f[0]), + std::max(unsigned(f.min()), 1U), f.max()); + break; + case op::Concat: + if (bounded) { - unsigned s = f.size(); - std::vector v; - v.reserve(s); - for (unsigned pos = 0; pos < s; ++pos) - v.emplace_back(visit(f[pos])); - out = formula::OrRat(v); + out = f; break; } - case op::ff: - case op::tt: - case op::ap: - case op::Not: - case op::X: - case op::F: - case op::G: - case op::Closure: - case op::NegClosure: - case op::NegClosureMarked: - case op::Xor: - case op::Implies: - case op::Equiv: - case op::U: - case op::R: - case op::W: - case op::M: - case op::EConcat: - case op::EConcatMarked: - case op::UConcat: - case op::Fusion: - case op::Or: - case op::And: - SPOT_UNREACHABLE(); - case op::AndRat: // Can AndRat be handled better? - case op::FStar: // Can FStar be handled better? - out = f; + // Fall through + case op::OrRat: + case op::AndNLM: + // Let F designate expressions that accept [*0], + // and G designate expressions that do not. + + // (G₁;G₂;G₃)° = G₁;G₂;G₃ + // (G₁;F₂;G₃)° = (G₁°);F₂;(G₃°) = G₁;F₂;G₃ + // because there is nothing to do recursively on a G. + // + // AndNLM can be dealt with similarly. + // + // The above cases are already handled by the + // accepts_eword() tests at the top of this method. So + // we reach this switch, we only have to deal with... + // + // (F₁;F₂;F₃)° = (F₁°)|(F₂°)|(F₃°) + // (F₁&F₂&F₃)° = (F₁°)|(F₂°)|(F₃°) + // (F₁|G₂|F₃)° = (F₁°)|(G₂°)|(F₃°) + { + unsigned s = f.size(); + std::vector v; + v.reserve(s); + for (unsigned pos = 0; pos < s; ++pos) + v.emplace_back(visit(f[pos])); + out = formula::OrRat(v); break; } + case op::ff: + case op::tt: + case op::ap: + case op::Not: + case op::X: + case op::F: + case op::G: + case op::Closure: + case op::NegClosure: + case op::NegClosureMarked: + case op::Xor: + case op::Implies: + case op::Equiv: + case op::U: + case op::R: + case op::W: + case op::M: + case op::EConcat: + case op::EConcatMarked: + case op::UConcat: + case op::Fusion: + case op::Or: + case op::And: + SPOT_UNREACHABLE(); + case op::AndRat: // Can AndRat be handled better? + case op::FStar: // Can FStar be handled better? + out = f; + break; + } - return (*cache_)[f] = out; - } - }; - } + return (*cache_)[f] = out; + } + }; + } - formula - star_normal_form(formula sere, snf_cache* cache) - { - snf_visitor v(cache); - return v.visit(sere); - } - - formula - star_normal_form_bounded(formula sere, snf_cache* cache) - { - snf_visitor v(cache); - return v.visit(sere); - } + formula + star_normal_form(formula sere, snf_cache* cache) + { + snf_visitor v(cache); + return v.visit(sere); + } + formula + star_normal_form_bounded(formula sere, snf_cache* cache) + { + snf_visitor v(cache); + return v.visit(sere); } } diff --git a/src/tl/snf.hh b/src/tl/snf.hh index bdb2b8d34..ee6548f35 100644 --- a/src/tl/snf.hh +++ b/src/tl/snf.hh @@ -24,35 +24,31 @@ namespace spot { - namespace ltl - { + typedef std::unordered_map snf_cache; - typedef std::unordered_map snf_cache; + /// Helper to rewrite a sere in Star Normal Form. + /// + /// This should only be called on children of a Star operator. It + /// corresponds to the E° operation defined in the following + /// paper. + /// + /** \verbatim + @Article{ bruggeman.96.tcs, + author = {Anne Br{\"u}ggemann-Klein}, + title = {Regular Expressions into Finite Automata}, + journal = {Theoretical Computer Science}, + year = {1996}, + volume = {120}, + pages = {87--98} + } + \endverbatim */ + /// + /// \param sere the SERE to rewrite + /// \param cache an optional cache + SPOT_API formula + star_normal_form(formula sere, snf_cache* cache = nullptr); - /// Helper to rewrite a sere in Star Normal Form. - /// - /// This should only be called on children of a Star operator. It - /// corresponds to the E° operation defined in the following - /// paper. - /// - /** \verbatim - @Article{ bruggeman.96.tcs, - author = {Anne Br{\"u}ggemann-Klein}, - title = {Regular Expressions into Finite Automata}, - journal = {Theoretical Computer Science}, - year = {1996}, - volume = {120}, - pages = {87--98} - } - \endverbatim */ - /// - /// \param sere the SERE to rewrite - /// \param cache an optional cache - SPOT_API formula - star_normal_form(formula sere, snf_cache* cache = nullptr); - - /// A variant of star_normal_form() for r[*0..j] where j < ω. - SPOT_API formula - star_normal_form_bounded(formula sere, snf_cache* cache = nullptr); - } + /// A variant of star_normal_form() for r[*0..j] where j < ω. + SPOT_API formula + star_normal_form_bounded(formula sere, snf_cache* cache = nullptr); } diff --git a/src/tl/unabbrev.cc b/src/tl/unabbrev.cc index d24e93818..c1a25c72c 100644 --- a/src/tl/unabbrev.cc +++ b/src/tl/unabbrev.cc @@ -22,232 +22,229 @@ namespace spot { - namespace ltl + unabbreviator::unabbreviator(const char* opt) { - unabbreviator::unabbreviator(const char* opt) - { - while (*opt) - switch (char c = *opt++) + while (*opt) + switch (char c = *opt++) + { + case 'e': + re_e_ = true; + re_some_bool_ = true; + break; + case 'F': + re_f_ = true; + re_some_f_g_ = true; + break; + case 'G': + re_g_ = true; + re_some_f_g_ = true; + break; + case 'i': + re_i_ = true; + re_some_bool_ = true; + break; + case 'M': + re_m_ = true; + re_some_other_ = true; + break; + case 'R': + re_r_ = true; + re_some_other_ = true; + break; + case 'W': + re_w_ = true; + re_some_other_ = true; + break; + case '^': + re_xor_ = true; + re_some_bool_ = true; + break; + default: + throw std::runtime_error + (std::string("unknown unabbreviation option: ") + + c); + } + } + + formula unabbreviator::run(formula in) + { + auto entry = cache_.emplace(in, nullptr); + if (!entry.second) + return entry.first->second; + + // Skip recursion whenever possible + bool no_boolean_rewrite = !re_some_bool_ || in.is_sugar_free_boolean(); + bool no_f_g_rewrite = !re_some_f_g_ || in.is_sugar_free_ltl(); + if (no_boolean_rewrite + && (in.is_boolean() || (no_f_g_rewrite && !re_some_other_))) + return entry.first->second = in; + + auto rec = [this](formula f) + { + return this->run(f); + }; + + formula out = in; + if (in.size() > 0) + out = in.map(rec); + + switch (out.kind()) + { + case op::ff: + case op::tt: + case op::eword: + case op::ap: + case op::Not: + case op::X: + case op::Closure: + case op::NegClosure: + case op::NegClosureMarked: + case op::EConcat: + case op::EConcatMarked: + case op::UConcat: + case op::U: + case op::Or: + case op::OrRat: + case op::And: + case op::AndRat: + case op::AndNLM: + case op::Concat: + case op::Fusion: + case op::Star: + case op::FStar: + break; + case op::F: + // F f = true U f + if (!re_f_) + break; + out = formula::U(formula::tt(), out[0]); + break; + case op::G: + // G f = false R f + // G f = f W false + // G f = !F!f + // G f = !(true U !f) + if (!re_g_) + break; + if (!re_r_) { - case 'e': - re_e_ = true; - re_some_bool_ = true; + out = formula::R(formula::ff(), out[0]); + break; + } + if (!re_w_) + { + out = formula::W(out[0], formula::ff()); break; - case 'F': - re_f_ = true; - re_some_f_g_ = true; - break; - case 'G': - re_g_ = true; - re_some_f_g_ = true; - break; - case 'i': - re_i_ = true; - re_some_bool_ = true; - break; - case 'M': - re_m_ = true; - re_some_other_ = true; - break; - case 'R': - re_r_ = true; - re_some_other_ = true; - break; - case 'W': - re_w_ = true; - re_some_other_ = true; - break; - case '^': - re_xor_ = true; - re_some_bool_ = true; - break; - default: - throw std::runtime_error - (std::string("unknown unabbreviation option: ") - + c); } - } - - formula unabbreviator::run(formula in) - { - auto entry = cache_.emplace(in, nullptr); - if (!entry.second) - return entry.first->second; - - // Skip recursion whenever possible - bool no_boolean_rewrite = !re_some_bool_ || in.is_sugar_free_boolean(); - bool no_f_g_rewrite = !re_some_f_g_ || in.is_sugar_free_ltl(); - if (no_boolean_rewrite - && (in.is_boolean() || (no_f_g_rewrite && !re_some_other_))) - return entry.first->second = in; - - auto rec = [this](formula f) { - return this->run(f); - }; - - formula out = in; - if (in.size() > 0) - out = in.map(rec); - - switch (out.kind()) + auto nc = formula::Not(out[0]); + if (!re_f_) + { + out = formula::Not(formula::F(nc)); + break; + } + out = formula::Not(formula::U(formula::tt(), nc)); + break; + } + case op::Xor: + // f1 ^ f2 == !(f1 <-> f2) + // f1 ^ f2 == (f1 & !f2) | (f2 & !f1) + if (!re_xor_) + break; { - case op::ff: - case op::tt: - case op::eword: - case op::ap: - case op::Not: - case op::X: - case op::Closure: - case op::NegClosure: - case op::NegClosureMarked: - case op::EConcat: - case op::EConcatMarked: - case op::UConcat: - case op::U: - case op::Or: - case op::OrRat: - case op::And: - case op::AndRat: - case op::AndNLM: - case op::Concat: - case op::Fusion: - case op::Star: - case op::FStar: - break; - case op::F: - // F f = true U f - if (!re_f_) - break; - out = formula::U(formula::tt(), out[0]); - break; - case op::G: - // G f = false R f - // G f = f W false - // G f = !F!f - // G f = !(true U !f) - if (!re_g_) - break; - if (!re_r_) - { - out = formula::R(formula::ff(), out[0]); - break; - } - if (!re_w_) - { - out = formula::W(out[0], formula::ff()); - break; - } + auto f1 = out[0]; + auto f2 = out[1]; + if (!re_e_) { - auto nc = formula::Not(out[0]); - if (!re_f_) - { - out = formula::Not(formula::F(nc)); - break; - } - out = formula::Not(formula::U(formula::tt(), nc)); - break; + out = formula::Not(formula::Equiv(f1, f2)); } - case op::Xor: - // f1 ^ f2 == !(f1 <-> f2) - // f1 ^ f2 == (f1 & !f2) | (f2 & !f1) - if (!re_xor_) - break; + else { - auto f1 = out[0]; - auto f2 = out[1]; - if (!re_e_) - { - out = formula::Not(formula::Equiv(f1, f2)); - } - else - { - auto a = formula::And({f1, formula::Not(f2)}); - auto b = formula::And({f2, formula::Not(f1)}); - out = formula::Or({a, b}); - } - } - break; - case op::Implies: - // f1 => f2 == !f1 | f2 - if (!re_i_) - break; - out = formula::Or({formula::Not(out[0]), out[1]}); - break; - case op::Equiv: - // f1 <=> f2 == (f1 & f2) | (!f1 & !f2) - if (!re_e_) - break; - { - auto f1 = out[0]; - auto f2 = out[1]; - auto nf1 = formula::Not(f1); - auto nf2 = formula::Not(f2); - auto term1 = formula::And({f1, f2}); - auto term2 = formula::And({nf1, nf2}); - out = formula::Or({term1, term2}); - break; - } - case op::R: - // f1 R f2 = f2 W (f1 & f2) - // f1 R f2 = f2 U ((f1 & f2) | Gf2) - // f1 R f2 = f2 U ((f1 & f2) | !F!f2) - // f1 R f2 = f2 U ((f1 & f2) | !(1 U !f2)) - if (!re_r_) - break; - { - auto f1 = out[0]; - auto f2 = out[1]; - auto f12 = formula::And({f1, f2}); - if (!re_w_) - { - out = formula::W(f2, f12); - break; - } - auto gf2 = formula::G(f2); - if (re_g_) - gf2 = run(gf2); - out = formula::U(f2, formula::Or({f12, out})); - break; - } - case op::W: - // f1 W f2 = f2 R (f2 | f1) - // f1 W f2 = f1 U (f2 | G f1) - // f1 W f2 = f1 U (f2 | !F !f1) - // f1 W f2 = f1 U (f2 | !(1 U !f1)) - if (!re_w_) - break; - { - auto f1 = out[0]; - auto f2 = out[1]; - if (!re_r_) - { - out = formula::R(f2, formula::Or({f2, f1})); - break; - } - auto gf1 = formula::G(f1); - if (re_g_) - gf1 = rec(gf1); - out = formula::U(f1, formula::Or({f2, out})); - break; - } - case op::M: - // f1 M f2 = f2 U (g2 & f1) - if (!re_m_) - break; - { - auto f2 = out[1]; - out = formula::U(f2, formula::And({f2, out[0]})); - break; + auto a = formula::And({f1, formula::Not(f2)}); + auto b = formula::And({f2, formula::Not(f1)}); + out = formula::Or({a, b}); } } - return entry.first->second = out; - } + break; + case op::Implies: + // f1 => f2 == !f1 | f2 + if (!re_i_) + break; + out = formula::Or({formula::Not(out[0]), out[1]}); + break; + case op::Equiv: + // f1 <=> f2 == (f1 & f2) | (!f1 & !f2) + if (!re_e_) + break; + { + auto f1 = out[0]; + auto f2 = out[1]; + auto nf1 = formula::Not(f1); + auto nf2 = formula::Not(f2); + auto term1 = formula::And({f1, f2}); + auto term2 = formula::And({nf1, nf2}); + out = formula::Or({term1, term2}); + break; + } + case op::R: + // f1 R f2 = f2 W (f1 & f2) + // f1 R f2 = f2 U ((f1 & f2) | Gf2) + // f1 R f2 = f2 U ((f1 & f2) | !F!f2) + // f1 R f2 = f2 U ((f1 & f2) | !(1 U !f2)) + if (!re_r_) + break; + { + auto f1 = out[0]; + auto f2 = out[1]; + auto f12 = formula::And({f1, f2}); + if (!re_w_) + { + out = formula::W(f2, f12); + break; + } + auto gf2 = formula::G(f2); + if (re_g_) + gf2 = run(gf2); + out = formula::U(f2, formula::Or({f12, out})); + break; + } + case op::W: + // f1 W f2 = f2 R (f2 | f1) + // f1 W f2 = f1 U (f2 | G f1) + // f1 W f2 = f1 U (f2 | !F !f1) + // f1 W f2 = f1 U (f2 | !(1 U !f1)) + if (!re_w_) + break; + { + auto f1 = out[0]; + auto f2 = out[1]; + if (!re_r_) + { + out = formula::R(f2, formula::Or({f2, f1})); + break; + } + auto gf1 = formula::G(f1); + if (re_g_) + gf1 = rec(gf1); + out = formula::U(f1, formula::Or({f2, out})); + break; + } + case op::M: + // f1 M f2 = f2 U (g2 & f1) + if (!re_m_) + break; + { + auto f2 = out[1]; + out = formula::U(f2, formula::And({f2, out[0]})); + break; + } + } + return entry.first->second = out; + } - formula unabbreviate(formula in, const char* opt) - { - unabbreviator un(opt); - return un.run(in); - } + formula unabbreviate(formula in, const char* opt) + { + unabbreviator un(opt); + return un.run(in); } } diff --git a/src/tl/unabbrev.hh b/src/tl/unabbrev.hh index 635909e8a..85240d694 100644 --- a/src/tl/unabbrev.hh +++ b/src/tl/unabbrev.hh @@ -24,49 +24,45 @@ namespace spot { - namespace ltl + constexpr const char* default_unabbrev_string = "eFGiMW^"; + + /// \ingroup ltl_rewriting + /// \brief Clone and rewrite a formula to remove specified operators + /// logical operators. + class SPOT_API unabbreviator final { - constexpr const char* default_unabbrev_string = "eFGiMW^"; - - /// \ingroup ltl_rewriting - /// \brief Clone and rewrite a formula to remove specified operators - /// logical operators. - class SPOT_API unabbreviator final - { - private: - // What to rewrite? - bool re_e_ = false; - bool re_f_ = false; - bool re_g_ = false; - bool re_i_ = false; - bool re_m_ = false; - bool re_r_ = false; - bool re_w_ = false; - bool re_xor_ = false; - bool re_some_bool_ = false; // rewrite xor, i, or e - bool re_some_f_g_ = false; // rewrite F or G - bool re_some_other_ = false; // rewrite W, M, or R - // Cache of rewritten subformulas - std::unordered_map cache_; - public: - /// \brief Constructor - /// - /// The set of operators to remove should be passed as a string - /// which in which each letter denote an operator (using LBT's - /// convention). - unabbreviator(const char* opt = default_unabbrev_string); - formula run(formula in); - }; - - /// \ingroup ltl_rewriting - /// \brief Clone and rewrite a formula to remove specified operators - /// logical operators. + private: + // What to rewrite? + bool re_e_ = false; + bool re_f_ = false; + bool re_g_ = false; + bool re_i_ = false; + bool re_m_ = false; + bool re_r_ = false; + bool re_w_ = false; + bool re_xor_ = false; + bool re_some_bool_ = false; // rewrite xor, i, or e + bool re_some_f_g_ = false; // rewrite F or G + bool re_some_other_ = false; // rewrite W, M, or R + // Cache of rewritten subformulas + std::unordered_map cache_; + public: + /// \brief Constructor /// /// The set of operators to remove should be passed as a string /// which in which each letter denote an operator (using LBT's /// convention). - SPOT_API formula - unabbreviate(formula in, const char* opt= default_unabbrev_string); - } + unabbreviator(const char* opt = default_unabbrev_string); + formula run(formula in); + }; + /// \ingroup ltl_rewriting + /// \brief Clone and rewrite a formula to remove specified operators + /// logical operators. + /// + /// The set of operators to remove should be passed as a string + /// which in which each letter denote an operator (using LBT's + /// convention). + SPOT_API formula + unabbreviate(formula in, const char* opt= default_unabbrev_string); } diff --git a/src/twa/bdddict.cc b/src/twa/bdddict.cc index 9cb309a14..0f23a6d5e 100644 --- a/src/twa/bdddict.cc +++ b/src/twa/bdddict.cc @@ -95,7 +95,7 @@ namespace spot } int - bdd_dict::register_proposition(ltl::formula f, const void* for_me) + bdd_dict::register_proposition(formula f, const void* for_me) { int num; // Do not build a variable that already exists. @@ -117,7 +117,7 @@ namespace spot } int - bdd_dict::has_registered_proposition(ltl::formula f, + bdd_dict::has_registered_proposition(formula f, const void* me) { auto ssi = var_map.find(f); @@ -131,7 +131,7 @@ namespace spot } int - bdd_dict::register_acceptance_variable(ltl::formula f, + bdd_dict::register_acceptance_variable(formula f, const void* for_me) { int num; @@ -171,7 +171,7 @@ namespace spot register_acceptance_variables(bdd_low(f), for_me); } - ltl::formula + formula bdd_dict::oneacc_to_formula(int var) const { assert(unsigned(var) < bdd_map.size()); @@ -180,7 +180,7 @@ namespace spot return i.f; } - ltl::formula + formula bdd_dict::oneacc_to_formula(bdd oneacc) const { assert(oneacc != bddfalse); @@ -274,7 +274,7 @@ namespace spot // Let's free it. First, we need to find // if this is a Var or an Acc variable. int n = 1; - ltl::formula f = nullptr; + formula f = nullptr; switch (bdd_map[v].type) { case var: diff --git a/src/twa/bdddict.hh b/src/twa/bdddict.hh index 46608550a..a430cc9f4 100644 --- a/src/twa/bdddict.hh +++ b/src/twa/bdddict.hh @@ -66,9 +66,9 @@ namespace spot ~bdd_dict(); /// Formula-to-BDD-variable maps. - typedef std::map fv_map; + typedef std::map fv_map; /// BDD-variable-to-formula maps. - typedef std::map vf_map; + typedef std::map vf_map; fv_map var_map; ///< Maps atomic propositions to BDD variables fv_map acc_map; ///< Maps acceptance conditions to BDD variables @@ -80,7 +80,7 @@ namespace spot struct bdd_info { bdd_info() : type(anon) {} var_type type; - ltl::formula f; // Used unless t==anon. + formula f; // Used unless t==anon. ref_set refs; int clone_counts; }; @@ -99,10 +99,10 @@ namespace spot /// \return The variable number. Use bdd_ithvar() or bdd_nithvar() /// to convert this to a BDD. /// @{ - int register_proposition(ltl::formula f, const void* for_me); + int register_proposition(formula f, const void* for_me); template - int register_proposition(ltl::formula f, std::shared_ptr for_me) + int register_proposition(formula f, std::shared_ptr for_me) { return register_proposition(f, for_me.get()); } @@ -114,10 +114,10 @@ namespace spot /// a non-negative value that is the BDD variable number. /// Otherwise this returns -1. /// @{ - int has_registered_proposition(ltl::formula f, const void* me); + int has_registered_proposition(formula f, const void* me); template - int has_registered_proposition(ltl::formula f, std::shared_ptr for_me) + int has_registered_proposition(formula f, std::shared_ptr for_me) { return has_registered_proposition(f, for_me.get()); } @@ -134,10 +134,10 @@ namespace spot /// \return The variable number. Use bdd_ithvar() or bdd_nithvar() /// to convert this to a BDD. /// @{ - int register_acceptance_variable(ltl::formula f, const void* for_me); + int register_acceptance_variable(formula f, const void* for_me); template - int register_acceptance_variable(ltl::formula f, std::shared_ptr for_me) + int register_acceptance_variable(formula f, std::shared_ptr for_me) { return register_acceptance_variable(f, for_me.get()); } @@ -169,7 +169,7 @@ namespace spot /// /// The returned formula is not cloned, and is valid until the BDD /// variable used in \a oneacc are unregistered. - ltl::formula oneacc_to_formula(bdd oneacc) const; + formula oneacc_to_formula(bdd oneacc) const; /// \brief Convert one acceptance condition into the associated /// formula. @@ -179,7 +179,7 @@ namespace spot /// /// The returned formula is not cloned, and is valid until the BDD /// variable \a var is unregistered. - ltl::formula oneacc_to_formula(int var) const; + formula oneacc_to_formula(int var) const; /// \brief Register anonymous BDD variables. /// diff --git a/src/twa/bddprint.cc b/src/twa/bddprint.cc index 1b32736ec..388d02d70 100644 --- a/src/twa/bddprint.cc +++ b/src/twa/bddprint.cc @@ -40,12 +40,12 @@ namespace spot static bool utf8; static - std::ostream& print_(std::ostream& o, ltl::formula f) + std::ostream& print_(std::ostream& o, formula f) { if (utf8) - ltl::print_utf8_psl(o, f); + print_utf8_psl(o, f); else - ltl::print_psl(o, f); + print_psl(o, f); return o; } diff --git a/src/twa/formula2bdd.cc b/src/twa/formula2bdd.cc index 85d89a933..19c221e17 100644 --- a/src/twa/formula2bdd.cc +++ b/src/twa/formula2bdd.cc @@ -26,8 +26,6 @@ namespace spot { - using namespace ltl; - namespace { // Convert a BDD which is known to be a conjonction into a formula. @@ -111,16 +109,16 @@ namespace spot case op::And: case op::Or: { - int op = bddop_and; + int o = bddop_and; bdd res = bddtrue; - if (f.is(ltl::op::Or)) + if (f.is(op::Or)) { - op = bddop_or; + o = bddop_or; res = bddfalse; } unsigned s = f.size(); for (unsigned n = 0; n < s; ++n) - res = bdd_apply(res, recurse(f[n]), op); + res = bdd_apply(res, recurse(f[n]), o); return res; } } diff --git a/src/twa/formula2bdd.hh b/src/twa/formula2bdd.hh index 67eab157f..90f276500 100644 --- a/src/twa/formula2bdd.hh +++ b/src/twa/formula2bdd.hh @@ -39,11 +39,11 @@ namespace spot /// for_me. See bdd_dict::unregister_all_my_variables(). /// @{ SPOT_API bdd - formula_to_bdd(ltl::formula f, const bdd_dict_ptr& d, void* for_me); + formula_to_bdd(formula f, const bdd_dict_ptr& d, void* for_me); template SPOT_API bdd - formula_to_bdd(ltl::formula f, const bdd_dict_ptr& d, + formula_to_bdd(formula f, const bdd_dict_ptr& d, const std::shared_ptr& for_me) { return formula_to_bdd(f, d, for_me.get()); @@ -57,7 +57,7 @@ namespace spot /// into their atomic propositions. This works only for Boolean /// formulas, and all the BDD variables used in \a f should have /// been registered in \a d. Although the result has type - /// ltl::formula, it obviously does not use any temporal operator. + /// formula, it obviously does not use any temporal operator. SPOT_API - ltl::formula bdd_to_formula(bdd f, const bdd_dict_ptr d); + formula bdd_to_formula(bdd f, const bdd_dict_ptr d); } diff --git a/src/twa/taatgba.cc b/src/twa/taatgba.cc index 817af1660..c951fa62a 100644 --- a/src/twa/taatgba.cc +++ b/src/twa/taatgba.cc @@ -46,7 +46,7 @@ namespace spot } void - taa_tgba::add_condition(transition* t, ltl::formula f) + taa_tgba::add_condition(transition* t, formula f) { t->condition &= formula_to_bdd(f, get_dict(), this); } @@ -324,6 +324,6 @@ namespace spot std::string taa_tgba_formula::label_to_string(const label_t& label) const { - return ltl::str_psl(label); + return str_psl(label); } } diff --git a/src/twa/taatgba.hh b/src/twa/taatgba.hh index dd04effd8..6ea3b64d0 100644 --- a/src/twa/taatgba.hh +++ b/src/twa/taatgba.hh @@ -49,7 +49,7 @@ namespace spot const state_set* dst; }; - void add_condition(transition* t, ltl::formula f); + void add_condition(transition* t, formula f); /// TGBA interface. virtual ~taa_tgba(); @@ -66,7 +66,7 @@ namespace spot taa_tgba::state_set* init_; ss_vec state_set_vec_; - std::map acc_map_; + std::map acc_map_; private: // Disallow copy. @@ -192,7 +192,7 @@ namespace spot return create_transition(s, vec); } - void add_acceptance_condition(transition* t, ltl::formula f) + void add_acceptance_condition(transition* t, formula f) { auto p = acc_map_.emplace(f, 0); if (p.second) @@ -326,14 +326,14 @@ namespace spot class SPOT_API taa_tgba_formula final: #ifndef SWIG - public taa_tgba_labelled + public taa_tgba_labelled #else public taa_tgba #endif { public: taa_tgba_formula(const bdd_dict_ptr& dict) : - taa_tgba_labelled(dict) {} + taa_tgba_labelled(dict) {} ~taa_tgba_formula() {} protected: diff --git a/src/twa/twa.hh b/src/twa/twa.hh index 8b48fcce6..aa8add15b 100644 --- a/src/twa/twa.hh +++ b/src/twa/twa.hh @@ -595,7 +595,7 @@ namespace spot /// \brief Register an atomic proposition designated by formula \a ap. /// /// \return The BDD variable number. - int register_ap(ltl::formula ap) + int register_ap(formula ap) { aps_.push_back(ap); int res = dict_->register_proposition(ap, this); @@ -608,12 +608,12 @@ namespace spot /// \return The BDD variable number. int register_ap(std::string name) { - return register_ap(ltl::formula::ap(name)); + return register_ap(formula::ap(name)); } /// \brief Get the vector of atomic propositions used by this /// automaton. - const std::vector& ap() const + const std::vector& ap() const { return aps_; } @@ -745,7 +745,7 @@ namespace spot mutable const state* last_support_conditions_input_; private: mutable bdd last_support_conditions_output_; - std::vector aps_; + std::vector aps_; bdd bddaps_; protected: diff --git a/src/twa/twagraph.cc b/src/twa/twagraph.cc index 47b4d2420..5c03d71e7 100644 --- a/src/twa/twagraph.cc +++ b/src/twa/twagraph.cc @@ -23,7 +23,7 @@ namespace spot { void - twa_graph::release_formula_namer(namer* namer, + twa_graph::release_formula_namer(namer* namer, bool keep_names) { if (keep_names) diff --git a/src/twa/twagraph.hh b/src/twa/twagraph.hh index 513416cf4..70dffbc17 100644 --- a/src/twa/twagraph.hh +++ b/src/twa/twagraph.hh @@ -219,14 +219,14 @@ namespace spot return new named_graph(g_); } - namer* + namer* create_formula_namer() { - return create_namer(); + return create_namer(); } void - release_formula_namer(namer* namer, bool keep_names); + release_formula_namer(namer* namer, bool keep_names); #endif graph_t& get_graph() diff --git a/src/twaalgos/compsusp.cc b/src/twaalgos/compsusp.cc index aabdd5711..520dc666c 100644 --- a/src/twaalgos/compsusp.cc +++ b/src/twaalgos/compsusp.cc @@ -34,8 +34,8 @@ namespace spot { namespace { - typedef std::map formula_bdd_map; - typedef std::vector vec; + typedef std::map formula_bdd_map; + typedef std::vector vec; // Rewrite the suspendable subformulae "s" of an LTL formula in // the form Gg where "g" is an atomic proposition representing @@ -44,19 +44,19 @@ namespace spot class ltl_suspender_visitor final { public: - typedef std::map fmap_t; + typedef std::map fmap_t; ltl_suspender_visitor(fmap_t& g2s, fmap_t& a2o, bool oblig) : g2s_(g2s), a2o_(a2o), oblig_(oblig) { } - ltl::formula - visit(ltl::formula f) + formula + visit(formula f) { - switch (ltl::op op = f.kind()) + switch (op o = f.kind()) { - case ltl::op::Or: - case ltl::op::And: + case op::Or: + case op::And: { vec res; vec oblig; @@ -64,7 +64,7 @@ namespace spot unsigned mos = f.size(); for (unsigned i = 0; i < mos; ++i) { - ltl::formula c = f[i]; + formula c = f[i]; if (c.is_boolean()) res.push_back(c); else if (oblig_ && c.is_syntactic_obligation()) @@ -76,40 +76,40 @@ namespace spot } if (!oblig.empty()) { - res.push_back(recurse(ltl::formula::multop(op, oblig))); + res.push_back(recurse(formula::multop(o, oblig))); } if (!susp.empty()) { - ltl::formula o = ltl::formula::multop(op, susp); - // Rewrite 'o' as 'G"o"' - ltl::formula g = recurse(o); - if (op == ltl::op::And) + formula x = formula::multop(o, susp); + // Rewrite 'x' as 'G"x"' + formula g = recurse(x); + if (o == op::And) { res.push_back(g); } else { // res || susp -> (res && G![susp]) || G[susp]) - auto r = ltl::formula::multop(op, res); - auto gn = ltl::formula::G(ltl::formula::Not(g[0])); - return ltl::formula::Or({ltl::formula::And({r, gn}), g}); + auto r = formula::multop(o, res); + auto gn = formula::G(formula::Not(g[0])); + return formula::Or({formula::And({r, gn}), g}); } } - return ltl::formula::multop(op, res); + return formula::multop(o, res); } break; default: - return f.map([this](ltl::formula f) + return f.map([this](formula f) { return this->recurse(f); }); } } - ltl::formula - recurse(ltl::formula f) + formula + recurse(formula f) { - ltl::formula res; + formula res; if (f.is_boolean()) return f; if (oblig_ && f.is_syntactic_obligation()) @@ -120,7 +120,7 @@ namespace spot std::ostringstream s; print_psl(s << "〈", f) << "〉"; - res = ltl::formula::ap(s.str()); + res = formula::ap(s.str()); a2o_[res] = f; assoc_[f] = res; return res; @@ -129,14 +129,14 @@ namespace spot { fmap_t::const_iterator i = assoc_.find(f); if (i != assoc_.end()) - return ltl::formula::G(i->second); + return formula::G(i->second); std::ostringstream s; print_psl(s << '[', f) << "]$"; - res = ltl::formula::ap(s.str()); + res = formula::ap(s.str()); g2s_[res] = f; assoc_[f] = res; - return ltl::formula::G(res); + return formula::G(res); } return visit(f); } @@ -156,7 +156,7 @@ namespace spot static twa_graph_ptr - susp_prod(const const_twa_ptr& left, ltl::formula f, bdd v) + susp_prod(const const_twa_ptr& left, formula f, bdd v) { bdd_dict_ptr dict = left->get_dict(); auto right = @@ -266,7 +266,7 @@ namespace spot twa_graph_ptr - compsusp(ltl::formula f, const bdd_dict_ptr& dict, + compsusp(formula f, const bdd_dict_ptr& dict, bool no_wdba, bool no_simulation, bool early_susp, bool no_susp_product, bool wdba_smaller, bool oblig) @@ -274,7 +274,7 @@ namespace spot ltl_suspender_visitor::fmap_t g2s; ltl_suspender_visitor::fmap_t a2o; ltl_suspender_visitor v(g2s, a2o, oblig); - ltl::formula g = v.recurse(f); + formula g = v.recurse(f); // Translate the patched formula, and remove useless SCCs. twa_graph_ptr res = diff --git a/src/twaalgos/compsusp.hh b/src/twaalgos/compsusp.hh index 670b34f5a..2bc690e8d 100644 --- a/src/twaalgos/compsusp.hh +++ b/src/twaalgos/compsusp.hh @@ -49,7 +49,7 @@ namespace spot /// long-term stability should better use the services of the /// spot::translator class instead. SPOT_API twa_graph_ptr - compsusp(ltl::formula f, const bdd_dict_ptr& dict, + compsusp(formula f, const bdd_dict_ptr& dict, bool no_wdba = false, bool no_simulation = false, bool early_susp = false, bool no_susp_product = false, bool wdba_smaller = false, bool oblig = false); diff --git a/src/twaalgos/ltl2taa.cc b/src/twaalgos/ltl2taa.cc index bba9306ea..a250129b3 100644 --- a/src/twaalgos/ltl2taa.cc +++ b/src/twaalgos/ltl2taa.cc @@ -28,8 +28,6 @@ namespace spot { namespace { - using namespace ltl; - /// \brief Recursively translate a formula into a TAA. class ltl2taa_visitor { @@ -385,11 +383,11 @@ namespace spot } // anonymous taa_tgba_formula_ptr - ltl_to_taa(ltl::formula f, + ltl_to_taa(formula f, const bdd_dict_ptr& dict, bool refined_rules) { // TODO: implement translation of F and G - auto f2 = ltl::negative_normal_form(ltl::unabbreviate(f, "^ieFG")); + auto f2 = negative_normal_form(unabbreviate(f, "^ieFG")); auto res = make_taa_tgba_formula(dict); language_containment_checker* lcc = new language_containment_checker(make_bdd_dict(), diff --git a/src/twaalgos/ltl2taa.hh b/src/twaalgos/ltl2taa.hh index 891e69b8a..9e8dc8e87 100644 --- a/src/twaalgos/ltl2taa.hh +++ b/src/twaalgos/ltl2taa.hh @@ -48,6 +48,6 @@ namespace spot /// \param refined_rules If this parameter is set, refined rules are used. /// \return A spot::taa that recognizes the language of \a f. SPOT_API taa_tgba_formula_ptr - ltl_to_taa(ltl::formula f, const bdd_dict_ptr& dict, + ltl_to_taa(formula f, const bdd_dict_ptr& dict, bool refined_rules = false); } diff --git a/src/twaalgos/ltl2tgba_fm.cc b/src/twaalgos/ltl2tgba_fm.cc index 1b71184fa..9c1fa8e3e 100644 --- a/src/twaalgos/ltl2tgba_fm.cc +++ b/src/twaalgos/ltl2tgba_fm.cc @@ -39,8 +39,6 @@ namespace spot { - using namespace ltl; - namespace { typedef std::vector vec; @@ -264,7 +262,7 @@ namespace spot if (single_acc) { int num = dict->register_acceptance_variable - (ltl::formula::tt(), this); + (formula::tt(), this); a_set &= bdd_ithvar(num); auto p = bm.emplace(num, 0U); diff --git a/src/twaalgos/ltl2tgba_fm.hh b/src/twaalgos/ltl2tgba_fm.hh index 18ad7ecb8..c350516a2 100644 --- a/src/twaalgos/ltl2tgba_fm.hh +++ b/src/twaalgos/ltl2tgba_fm.hh @@ -101,7 +101,7 @@ namespace spot /// representing each state of the automaton will be simplified /// before computing the successor. \a simpl should be configured /// for the type of reduction you want, see - /// spot::ltl::ltl_simplifier. This idea is taken from the + /// spot::ltl_simplifier. This idea is taken from the /// following paper. /** \verbatim @InProceedings{ thirioux.02.fmics, @@ -140,11 +140,11 @@ namespace spot /// /// \return A spot::twa_graph that recognizes the language of \a f. SPOT_API twa_graph_ptr - ltl_to_tgba_fm(ltl::formula f, const bdd_dict_ptr& dict, + ltl_to_tgba_fm(formula f, const bdd_dict_ptr& dict, bool exprop = false, bool symb_merge = true, bool branching_postponement = false, bool fair_loop_approx = false, - const ltl::atomic_prop_set* unobs = nullptr, - ltl::ltl_simplifier* simplifier = nullptr, + const atomic_prop_set* unobs = nullptr, + ltl_simplifier* simplifier = nullptr, bool unambiguous = false); } diff --git a/src/twaalgos/minimize.cc b/src/twaalgos/minimize.cc index 01104d1b8..bdadbfbd6 100644 --- a/src/twaalgos/minimize.cc +++ b/src/twaalgos/minimize.cc @@ -592,7 +592,7 @@ namespace spot twa_graph_ptr minimize_obligation(const const_twa_graph_ptr& aut_f, - ltl::formula f, + formula f, const_twa_graph_ptr aut_neg_f, bool reject_bigger) { @@ -628,7 +628,7 @@ namespace spot { // If we know the formula, simply build the automaton for // its negation. - aut_neg_f = ltl_to_tgba_fm(ltl::formula::Not(f), aut_f->get_dict()); + aut_neg_f = ltl_to_tgba_fm(formula::Not(f), aut_f->get_dict()); // Remove useless SCCs. aut_neg_f = scc_filter(aut_neg_f, true); } diff --git a/src/twaalgos/minimize.hh b/src/twaalgos/minimize.hh index 87251556c..d0e099327 100644 --- a/src/twaalgos/minimize.hh +++ b/src/twaalgos/minimize.hh @@ -151,7 +151,7 @@ namespace spot /// that the minimized WDBA is correct. SPOT_API twa_graph_ptr minimize_obligation(const const_twa_graph_ptr& aut_f, - ltl::formula f = nullptr, + formula f = nullptr, const_twa_graph_ptr aut_neg_f = nullptr, bool reject_bigger = false); /// @} diff --git a/src/twaalgos/neverclaim.cc b/src/twaalgos/neverclaim.cc index 4bc85c0d3..94a43d897 100644 --- a/src/twaalgos/neverclaim.cc +++ b/src/twaalgos/neverclaim.cc @@ -151,7 +151,7 @@ namespace spot os_ << " :: atomic { ("; else os_ << " :: ("; - ltl::formula f = bdd_to_formula(t.cond, aut_->get_dict()); + formula f = bdd_to_formula(t.cond, aut_->get_dict()); // This is actually a Boolean formula, but the LTL printer // is all we have. print_spin_ltl(os_, f, true); diff --git a/src/twaalgos/postproc.cc b/src/twaalgos/postproc.cc index 9516ffdd6..fb3e3f0f6 100644 --- a/src/twaalgos/postproc.cc +++ b/src/twaalgos/postproc.cc @@ -144,7 +144,7 @@ namespace spot #define SBACC_ (pref_ & SBAcc) twa_graph_ptr - postprocessor::run(twa_graph_ptr a, ltl::formula f) + postprocessor::run(twa_graph_ptr a, formula f) { if (type_ != Generic && !a->acc().is_generalized_buchi()) a = to_generalized_buchi(a); diff --git a/src/twaalgos/postproc.hh b/src/twaalgos/postproc.hh index cd04ec039..236215f2f 100644 --- a/src/twaalgos/postproc.hh +++ b/src/twaalgos/postproc.hh @@ -99,7 +99,7 @@ namespace spot /// /// The returned automaton might be a new automaton, /// or an in-place modification of the \a input automaton. - twa_graph_ptr run(twa_graph_ptr input, ltl::formula f); + twa_graph_ptr run(twa_graph_ptr input, formula f); protected: twa_graph_ptr do_simul(const twa_graph_ptr& input, int opt); diff --git a/src/twaalgos/powerset.cc b/src/twaalgos/powerset.cc index bbd1a691a..53e9feda2 100644 --- a/src/twaalgos/powerset.cc +++ b/src/twaalgos/powerset.cc @@ -404,7 +404,7 @@ namespace spot tba_determinize_check(const twa_graph_ptr& aut, unsigned threshold_states, unsigned threshold_cycles, - ltl::formula f, + formula f, const_twa_graph_ptr neg_aut) { if (f == nullptr && neg_aut == nullptr) @@ -419,7 +419,7 @@ namespace spot if (neg_aut == nullptr) { - neg_aut = ltl_to_tgba_fm(ltl::formula::Not(f), aut->get_dict()); + neg_aut = ltl_to_tgba_fm(formula::Not(f), aut->get_dict()); // Remove useless SCCs. neg_aut = scc_filter(neg_aut, true); } diff --git a/src/twaalgos/powerset.hh b/src/twaalgos/powerset.hh index 30b063c1a..10f0caadb 100644 --- a/src/twaalgos/powerset.hh +++ b/src/twaalgos/powerset.hh @@ -133,7 +133,7 @@ namespace spot tba_determinize_check(const twa_graph_ptr& aut, unsigned threshold_states = 0, unsigned threshold_cycles = 0, - ltl::formula f = nullptr, + formula f = nullptr, const_twa_graph_ptr neg_aut = nullptr); } diff --git a/src/twaalgos/randomgraph.cc b/src/twaalgos/randomgraph.cc index cd8f2e4b8..71f5354b9 100644 --- a/src/twaalgos/randomgraph.cc +++ b/src/twaalgos/randomgraph.cc @@ -122,7 +122,7 @@ namespace spot twa_graph_ptr random_graph(int n, float d, - const ltl::atomic_prop_set* ap, const bdd_dict_ptr& dict, + const atomic_prop_set* ap, const bdd_dict_ptr& dict, unsigned n_accs, float a, float t, bool deterministic, bool state_acc, bool colored) { diff --git a/src/twaalgos/randomgraph.hh b/src/twaalgos/randomgraph.hh index ee034025c..a2d1e74c2 100644 --- a/src/twaalgos/randomgraph.hh +++ b/src/twaalgos/randomgraph.hh @@ -81,7 +81,7 @@ namespace spot /// successors one by one.) SPOT_API twa_graph_ptr random_graph(int n, float d, - const ltl::atomic_prop_set* ap, const bdd_dict_ptr& dict, + const atomic_prop_set* ap, const bdd_dict_ptr& dict, unsigned n_accs = 0, float a = 0.1, float t = 0.5, bool deterministic = false, bool state_acc = false, bool colored = false); diff --git a/src/twaalgos/relabel.cc b/src/twaalgos/relabel.cc index d5643b343..a038d5233 100644 --- a/src/twaalgos/relabel.cc +++ b/src/twaalgos/relabel.cc @@ -22,7 +22,7 @@ namespace spot { void - relabel_here(twa_graph_ptr& aut, ltl::relabeling_map* relmap) + relabel_here(twa_graph_ptr& aut, relabeling_map* relmap) { bddPair* pairs = bdd_newpair(); auto d = aut->get_dict(); diff --git a/src/twaalgos/relabel.hh b/src/twaalgos/relabel.hh index be11d9b10..f5e4273c7 100644 --- a/src/twaalgos/relabel.hh +++ b/src/twaalgos/relabel.hh @@ -27,5 +27,5 @@ namespace spot /// replace atomic propositions in an automaton SPOT_API void relabel_here(twa_graph_ptr& aut, - ltl::relabeling_map* relmap); + relabeling_map* relmap); } diff --git a/src/twaalgos/remprop.cc b/src/twaalgos/remprop.cc index 779af1eb5..ca2e09ed8 100644 --- a/src/twaalgos/remprop.cc +++ b/src/twaalgos/remprop.cc @@ -54,7 +54,7 @@ namespace spot break; if (*start == ',' || *start == '=') unexpected_char(arg, start); - ltl::formula the_ap = nullptr; + formula the_ap = nullptr; if (*start == '"') { @@ -72,7 +72,7 @@ namespace spot throw std::invalid_argument(s); } std::string ap(start, end - start); - the_ap = ltl::formula::ap(ap); + the_ap = formula::ap(ap); do ++end; while (*end == ' ' || *end == '\t'); @@ -87,7 +87,7 @@ namespace spot while (rend > start && (rend[-1] == ' ' || rend[-1] == '\t')) --rend; std::string ap(start, rend - start); - the_ap = ltl::formula::ap(ap); + the_ap = formula::ap(ap); start = end; } if (*start) diff --git a/src/twaalgos/remprop.hh b/src/twaalgos/remprop.hh index 98c8f8f6a..7ba1b37fe 100644 --- a/src/twaalgos/remprop.hh +++ b/src/twaalgos/remprop.hh @@ -27,9 +27,9 @@ namespace spot { class SPOT_API remove_ap { - std::set props_exist; - std::set props_pos; - std::set props_neg; + std::set props_exist; + std::set props_pos; + std::set props_neg; public: void add_ap(const char* ap_csv); diff --git a/src/twaalgos/stats.cc b/src/twaalgos/stats.cc index bde1d9ab5..8201a01bc 100644 --- a/src/twaalgos/stats.cc +++ b/src/twaalgos/stats.cc @@ -137,7 +137,7 @@ namespace spot void printable_formula::print(std::ostream& os, const char*) const { - ltl::print_psl(os, val_); + print_psl(os, val_); }; stat_printer::stat_printer(std::ostream& os, const char* format) @@ -162,7 +162,7 @@ namespace spot std::ostream& stat_printer::print(const const_twa_graph_ptr& aut, - ltl::formula f, double run_time) + formula f, double run_time) { form_ = f; run_time_ = run_time; diff --git a/src/twaalgos/stats.hh b/src/twaalgos/stats.hh index e2a24df87..b625454ff 100644 --- a/src/twaalgos/stats.hh +++ b/src/twaalgos/stats.hh @@ -55,11 +55,11 @@ namespace spot SPOT_API tgba_sub_statistics sub_stats_reachable(const const_twa_ptr& g); - class SPOT_API printable_formula: public printable_value + class SPOT_API printable_formula: public printable_value { public: printable_formula& - operator=(ltl::formula new_val) + operator=(formula new_val) { val_ = new_val; return *this; @@ -84,7 +84,7 @@ namespace spot /// The \a f argument is not needed if the Formula does not need /// to be output, and so is \a run_time). std::ostream& - print(const const_twa_graph_ptr& aut, ltl::formula f = nullptr, + print(const const_twa_graph_ptr& aut, formula f = nullptr, double run_time = -1.); private: diff --git a/src/twaalgos/stutter.cc b/src/twaalgos/stutter.cc index 4f711dff7..1866b7105 100644 --- a/src/twaalgos/stutter.cc +++ b/src/twaalgos/stutter.cc @@ -540,7 +540,7 @@ namespace spot } bool - is_stutter_invariant(ltl::formula f) + is_stutter_invariant(formula f) { if (f.is_ltl_formula() && f.is_syntactic_stutter_invariant()) return true; @@ -554,16 +554,16 @@ namespace spot throw std::runtime_error("Cannot use the syntactic " "stutter-invariance check " "for non-LTL formulas"); - ltl::formula g = remove_x(f); + formula g = remove_x(f); bool res; if (algo == 0) // Equivalence check { - ltl::ltl_simplifier ls; + ltl_simplifier ls; res = ls.are_equivalent(f, g); } else { - ltl::formula h = ltl::formula::Xor(f, g); + formula h = formula::Xor(f, g); res = ltl_to_tgba_fm(h, make_bdd_dict())->is_empty(); } return res; @@ -572,7 +572,7 @@ namespace spot // Prepare for an automata-based check. translator trans; auto aut_f = trans.run(f); - auto aut_nf = trans.run(ltl::formula::Not(f)); + auto aut_nf = trans.run(formula::Not(f)); bdd aps = atomic_prop_collect_as_bdd(f, aut_f); return is_stutter_invariant(std::move(aut_f), std::move(aut_nf), aps, algo); } @@ -618,7 +618,7 @@ namespace spot } bool - check_stutter_invariance(const twa_graph_ptr& aut, ltl::formula f) + check_stutter_invariance(const twa_graph_ptr& aut, formula f) { bool is_stut = aut->is_stutter_invariant(); if (is_stut) @@ -627,7 +627,7 @@ namespace spot twa_graph_ptr neg = nullptr; if (f) { - neg = translator(aut->get_dict()).run(ltl::formula::Not(f)); + neg = translator(aut->get_dict()).run(formula::Not(f)); } else { diff --git a/src/twaalgos/stutter.hh b/src/twaalgos/stutter.hh index b096e75eb..45842c850 100644 --- a/src/twaalgos/stutter.hh +++ b/src/twaalgos/stutter.hh @@ -51,7 +51,7 @@ namespace spot /// \ingroup ltl_misc /// \brief Check if a formula has the stutter invariance property. SPOT_API bool - is_stutter_invariant(ltl::formula f); + is_stutter_invariant(formula f); SPOT_API bool is_stutter_invariant(twa_graph_ptr&& aut_f, @@ -65,5 +65,5 @@ namespace spot /// of the automaton is updated and also returned. SPOT_API bool check_stutter_invariance(const twa_graph_ptr& aut, - ltl::formula f = nullptr); + formula f = nullptr); } diff --git a/src/twaalgos/translate.cc b/src/twaalgos/translate.cc index ef8b81f9b..6cce712fb 100644 --- a/src/twaalgos/translate.cc +++ b/src/twaalgos/translate.cc @@ -43,7 +43,7 @@ namespace spot void translator::build_simplifier(const bdd_dict_ptr& dict) { - ltl::ltl_simplifier_options options(false, false, false); + ltl_simplifier_options options(false, false, false); switch (level_) { case High: @@ -58,10 +58,10 @@ namespace spot options.event_univ = true; // fall through } - simpl_owned_ = simpl_ = new ltl::ltl_simplifier(options, dict); + simpl_owned_ = simpl_ = new ltl_simplifier(options, dict); } - twa_graph_ptr translator::run(ltl::formula* f) + twa_graph_ptr translator::run(formula* f) { bool unambiguous = (pref_ & postprocessor::Unambiguous); if (unambiguous && type_ == postprocessor::Monitor) @@ -72,7 +72,7 @@ namespace spot set_pref(pref_ | postprocessor::Deterministic); } - ltl::formula r = simpl_->simplify(*f); + formula r = simpl_->simplify(*f); *f = r; // This helps ltl_to_tgba_fm() to order BDD variables in a more @@ -102,7 +102,7 @@ namespace spot return aut; } - twa_graph_ptr translator::run(ltl::formula f) + twa_graph_ptr translator::run(formula f) { return run(&f); } diff --git a/src/twaalgos/translate.hh b/src/twaalgos/translate.hh index 728e75f20..c970d0193 100644 --- a/src/twaalgos/translate.hh +++ b/src/twaalgos/translate.hh @@ -47,7 +47,7 @@ namespace spot class SPOT_API translator: protected postprocessor { public: - translator(ltl::ltl_simplifier* simpl, const option_map* opt = nullptr) + translator(ltl_simplifier* simpl, const option_map* opt = nullptr) : postprocessor(opt), simpl_(simpl), simpl_owned_(nullptr) { assert(simpl); @@ -101,21 +101,21 @@ namespace spot /// \brief Convert \a f into an automaton. /// /// The formula \a f is simplified internally. - twa_graph_ptr run(ltl::formula f); + twa_graph_ptr run(formula f); /// \brief Convert \a f into an automaton, and update f. /// /// The formula *f is replaced /// by the simplified version. - twa_graph_ptr run(ltl::formula* f); + twa_graph_ptr run(formula* f); protected: void setup_opt(const option_map* opt); void build_simplifier(const bdd_dict_ptr& dict); private: - ltl::ltl_simplifier* simpl_; - ltl::ltl_simplifier* simpl_owned_; + ltl_simplifier* simpl_; + ltl_simplifier* simpl_owned_; int comp_susp_; int early_susp_; int skel_wdba_; diff --git a/wrap/python/spot_impl.i b/wrap/python/spot_impl.i index ddd73946e..56f79a676 100644 --- a/wrap/python/spot_impl.i +++ b/wrap/python/spot_impl.i @@ -145,7 +145,6 @@ #include "taalgos/stats.hh" #include "taalgos/minimize.hh" -using namespace spot::ltl; using namespace spot; %} @@ -209,15 +208,15 @@ using namespace spot; %include "misc/optionmap.hh" %include "misc/random.hh" -%implicitconv std::vector; +%implicitconv std::vector; %include "tl/formula.hh" namespace std { %template(liststr) list; - %template(vectorformula) vector; - %template(atomic_prop_set) set; - %template(relabeling_map) map; + %template(vectorformula) vector; + %template(atomic_prop_set) set; + %template(relabeling_map) map; } %include "tl/environment.hh" @@ -246,8 +245,6 @@ namespace std { %include "tl/remove_x.hh" %include "tl/relabel.hh" -// Help SWIG with namespace lookups. -#define ltl spot::ltl %include "twa/taatgba.hh" %include "twa/twaproduct.hh" %include "twa/twagraph.hh" @@ -295,7 +292,7 @@ namespace std { #undef ltl -%exception spot::ltl::formula::__getitem__ { +%exception spot::formula::__getitem__ { try { $action } @@ -305,19 +302,19 @@ namespace std { } } -%extend spot::ltl::formula { +%extend spot::formula { // __cmp__ is for Python 2.0 - int __cmp__(spot::ltl::formula b) { return self->id() - b.id(); } + int __cmp__(spot::formula b) { return self->id() - b.id(); } size_t __hash__() { return self->id(); } unsigned __len__() { return self->size(); } formula __getitem__(unsigned pos) { return (*self)[pos]; } - std::string __repr__() { return spot::ltl::str_psl(*self); } + std::string __repr__() { return spot::str_psl(*self); } std::string _repr_latex_() { - return std::string("$") + spot::ltl::str_sclatex_psl(*self) + '$'; + return std::string("$") + spot::str_sclatex_psl(*self) + '$'; } - std::string __str__() { return spot::ltl::str_psl(*self); } + std::string __str__() { return spot::str_psl(*self); } } %extend spot::acc_cond::acc_code { @@ -357,10 +354,10 @@ namespace std { %inline %{ bool fnode_instances_check() { - return spot::ltl::fnode::instances_check(); + return spot::fnode::instances_check(); } -spot::ltl::parse_error_list +spot::parse_error_list empty_parse_error_list() { parse_error_list l; @@ -424,7 +421,7 @@ unblock_signal(int signum) %} -%extend spot::ltl::parse_error_list { +%extend spot::parse_error_list { bool __nonzero__()