rename src/ as spot/ and use include <spot/...>
* NEWS: Mention the change. * src/: Rename as ... * spot/: ... this, adjust all headers to include <spot/...> instead of "...", and adjust all Makefile.am to search headers from the top-level directory. * HACKING: Add conventions about #include. * spot/sanity/style.test: Add a few more grep to catch cases that do not follow these conventions. * .gitignore, Makefile.am, README, bench/stutter/Makefile.am, bench/stutter/stutter_invariance_formulas.cc, bench/stutter/stutter_invariance_randomgraph.cc, configure.ac, debian/rules, doc/Doxyfile.in, doc/Makefile.am, doc/org/.dir-locals.el.in, doc/org/g++wrap.in, doc/org/init.el.in, doc/org/tut01.org, doc/org/tut02.org, doc/org/tut03.org, doc/org/tut10.org, doc/org/tut20.org, doc/org/tut21.org, doc/org/tut22.org, doc/org/tut30.org, iface/ltsmin/Makefile.am, iface/ltsmin/kripke.test, iface/ltsmin/ltsmin.cc, iface/ltsmin/ltsmin.hh, iface/ltsmin/modelcheck.cc, wrap/python/Makefile.am, wrap/python/ajax/spotcgi.in, wrap/python/spot_impl.i, wrap/python/tests/ltl2tgba.py, wrap/python/tests/randgen.py, wrap/python/tests/run.in: Adjust.
This commit is contained in:
parent
1fddfe60ec
commit
f120dd3206
529 changed files with 1308 additions and 1262 deletions
8
spot/.gitignore
vendored
Normal file
8
spot/.gitignore
vendored
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
.deps
|
||||
Makefile
|
||||
Makefile.in
|
||||
.libs
|
||||
libspot.la
|
||||
_.cc
|
||||
*.log
|
||||
*.dir
|
||||
49
spot/Makefile.am
Normal file
49
spot/Makefile.am
Normal file
|
|
@ -0,0 +1,49 @@
|
|||
## -*- coding: utf-8 -*-
|
||||
## Copyright (C) 2009, 2010, 2012, 2013, 2014, 2015 Laboratoire de Recherche
|
||||
## et Développement de l'Epita (LRDE).
|
||||
## Copyright (C) 2003, 2004 Laboratoire d'Informatique de Paris 6 (LIP6),
|
||||
## département Systèmes Répartis Coopératifs (SRC), Université Pierre
|
||||
## et Marie Curie.
|
||||
##
|
||||
## This file is part of Spot, a model checking library.
|
||||
##
|
||||
## Spot is free software; you can redistribute it and/or modify it
|
||||
## under the terms of the GNU General Public License as published by
|
||||
## the Free Software Foundation; either version 3 of the License, or
|
||||
## (at your option) any later version.
|
||||
##
|
||||
## Spot is distributed in the hope that it will be useful, but WITHOUT
|
||||
## ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
||||
## or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public
|
||||
## License for more details.
|
||||
##
|
||||
## You should have received a copy of the GNU General Public License
|
||||
## along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
AUTOMAKE_OPTIONS = subdir-objects
|
||||
|
||||
# List directories in the order they must be built. Keep tests at the
|
||||
# end, after building '.' (since the current directory contains
|
||||
# libspot.la needed by the tests)
|
||||
SUBDIRS = misc priv tl graph twa twaalgos ta taalgos kripke \
|
||||
parseaut parsetl . bin tests sanity
|
||||
|
||||
lib_LTLIBRARIES = libspot.la
|
||||
libspot_la_SOURCES =
|
||||
libspot_la_LDFLAGS = $(BUDDY_LDFLAGS) -no-undefined $(SYMBOLIC_LDFLAGS)
|
||||
libspot_la_LIBADD = \
|
||||
kripke/libkripke.la \
|
||||
misc/libmisc.la \
|
||||
parseaut/libparseaut.la \
|
||||
parsetl/libparsetl.la \
|
||||
priv/libpriv.la \
|
||||
taalgos/libtaalgos.la \
|
||||
ta/libta.la \
|
||||
tl/libtl.la \
|
||||
twaalgos/libtwaalgos.la \
|
||||
twa/libtwa.la \
|
||||
../lib/libgnu.la
|
||||
|
||||
# Dummy C++ source to cause C++ linking.
|
||||
nodist_EXTRA_libspot_la_SOURCES = _.cc
|
||||
_.cc:; touch $@
|
||||
16
spot/bin/.gitignore
vendored
Normal file
16
spot/bin/.gitignore
vendored
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
autfilt
|
||||
dstar2tgba
|
||||
genltl
|
||||
ltl2tgba
|
||||
ltl2tgta
|
||||
ltlcross
|
||||
ltldo
|
||||
ltlfilt
|
||||
ltlgrind
|
||||
randaut
|
||||
randltl
|
||||
spot-x
|
||||
*.a
|
||||
*.1
|
||||
*.7
|
||||
lck-*
|
||||
87
spot/bin/Makefile.am
Normal file
87
spot/bin/Makefile.am
Normal file
|
|
@ -0,0 +1,87 @@
|
|||
## -*- coding: utf-8 -*-
|
||||
## Copyright (C) 2012, 2013, 2014, 2015 Laboratoire de Recherche et
|
||||
## Développement de l'Epita (LRDE).
|
||||
##
|
||||
## This file is part of Spot, a model checking library.
|
||||
##
|
||||
## Spot is free software; you can redistribute it and/or modify it
|
||||
## under the terms of the GNU General Public License as published by
|
||||
## the Free Software Foundation; either version 3 of the License, or
|
||||
## (at your option) any later version.
|
||||
##
|
||||
## Spot is distributed in the hope that it will be useful, but WITHOUT
|
||||
## ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
||||
## or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public
|
||||
## License for more details.
|
||||
##
|
||||
## You should have received a copy of the GNU General Public License
|
||||
## along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
SUBDIRS = . man
|
||||
|
||||
AM_CPPFLAGS = -I$(top_builddir) -I$(top_srcdir) $(BUDDY_CPPFLAGS) \
|
||||
-I$(top_builddir)/lib -I$(top_srcdir)/lib
|
||||
AM_CXXFLAGS = $(WARNING_CXXFLAGS)
|
||||
LDADD = libcommon.a $(top_builddir)/lib/libgnu.la ../libspot.la
|
||||
|
||||
noinst_LIBRARIES = libcommon.a
|
||||
libcommon_a_SOURCES = \
|
||||
common_aoutput.cc \
|
||||
common_aoutput.hh \
|
||||
common_conv.hh \
|
||||
common_conv.cc \
|
||||
common_cout.hh \
|
||||
common_cout.cc \
|
||||
common_file.cc \
|
||||
common_file.hh \
|
||||
common_finput.cc \
|
||||
common_finput.hh \
|
||||
common_hoaread.cc \
|
||||
common_hoaread.hh \
|
||||
common_output.cc \
|
||||
common_output.hh \
|
||||
common_post.cc \
|
||||
common_post.hh \
|
||||
common_range.cc \
|
||||
common_range.hh \
|
||||
common_r.cc \
|
||||
common_r.hh \
|
||||
common_setup.cc \
|
||||
common_setup.hh \
|
||||
common_sys.hh \
|
||||
common_trans.cc \
|
||||
common_trans.hh
|
||||
|
||||
bin_PROGRAMS = \
|
||||
autfilt \
|
||||
dstar2tgba \
|
||||
genltl \
|
||||
ltl2tgba \
|
||||
ltl2tgta \
|
||||
ltlcross \
|
||||
ltldo \
|
||||
ltlfilt \
|
||||
ltlgrind \
|
||||
randaut \
|
||||
randltl
|
||||
|
||||
# Dummy program used just to generate man/spot-x.7 in a way that is
|
||||
# consistent with the other man pages (e.g., with a version number that
|
||||
# is automatically updated).
|
||||
noinst_PROGRAMS = spot-x
|
||||
|
||||
autfilt_SOURCES = autfilt.cc
|
||||
ltlfilt_SOURCES = ltlfilt.cc
|
||||
genltl_SOURCES = genltl.cc
|
||||
randaut_SOURCES = randaut.cc
|
||||
randltl_SOURCES = randltl.cc
|
||||
ltl2tgba_SOURCES = ltl2tgba.cc
|
||||
ltl2tgta_SOURCES = ltl2tgta.cc
|
||||
ltlcross_SOURCES = ltlcross.cc
|
||||
ltlgrind_SOURCES = ltlgrind.cc
|
||||
ltldo_SOURCES = ltldo.cc
|
||||
dstar2tgba_SOURCES = dstar2tgba.cc
|
||||
spot_x_SOURCES = spot-x.cc
|
||||
ltlcross_LDADD = $(LDADD) $(LIB_GETHRXTIME)
|
||||
|
||||
EXTRA_DIST = options.py
|
||||
52
spot/bin/README
Normal file
52
spot/bin/README
Normal file
|
|
@ -0,0 +1,52 @@
|
|||
This directory contains the source of some command-line tools that
|
||||
expose some of Spot's algorithms to Unix users.
|
||||
|
||||
Man pages are generated from the --help output of each tool,
|
||||
supplemented by any text in the man/*.x files. Usually the extra text
|
||||
contains either some bibliographical references, some formal
|
||||
definitions or some examples that are too long for --help. Having a
|
||||
few short examples at the end of --help is good.
|
||||
|
||||
This directory also build some non-installed binaries, like spot-x,
|
||||
whose purpose is just to generate a man-page with the same format as
|
||||
the other man pages (this includes keeping the version number
|
||||
up-to-date).
|
||||
|
||||
There is also a script called 'options.py' that summerizes how the
|
||||
different short options are used among the tools.
|
||||
|
||||
Routines that are shared by multiple command-line tools are stored in
|
||||
files called common_*.{cc,hh}.
|
||||
|
||||
|
||||
Recommendations when adding new tools or features:
|
||||
--------------------------------------------------
|
||||
|
||||
- Tools should be designed to work on multiple inputs (e.g., read
|
||||
different outputs from multiple files, and accept many inputs from
|
||||
the same file, including stdin). They should also all be designed
|
||||
to produce several outputs, usually one per input. This way they
|
||||
can be piped one onto the other easily.
|
||||
|
||||
- When naming an option, seek inspiration from the POSIX standard, or
|
||||
from GNU extensions. For instance ltlfilt and autfilt both have a
|
||||
-v option to invert the filter; this is inspired from grep's -v
|
||||
option. The long version of this option (--invert-match) is also
|
||||
the same as in grep.
|
||||
|
||||
- When adding a new option, implement only the --long-option by
|
||||
default. Do not add a short version unless
|
||||
(1) you are sure it will be frequently used interactively
|
||||
(if it is only used in scripts, then a long option is enough)
|
||||
(2) this option can be shared by multiple tools.
|
||||
|
||||
- As much as possible, use the same option names across tools. Use
|
||||
the script options.py in this directory to check what short options
|
||||
are used. It's OK if the same short option correspond to different
|
||||
long names in the various tools, as long as the intent is similar.
|
||||
For instance -n has different long options depending on the tool:
|
||||
autfilt -n N means --max-count=N
|
||||
randltl -n N means --formulas=N
|
||||
randaut -n N means --automata=N
|
||||
but in all cases, the intent is to specify the number of items
|
||||
to output.
|
||||
773
spot/bin/autfilt.cc
Normal file
773
spot/bin/autfilt.cc
Normal file
|
|
@ -0,0 +1,773 @@
|
|||
// -*- coding: utf-8 -*-
|
||||
// Copyright (C) 2013, 2014, 2015 Laboratoire de Recherche et
|
||||
// Développement de l'Epita (LRDE).
|
||||
//
|
||||
// This file is part of Spot, a model checking library.
|
||||
//
|
||||
// Spot is free software; you can redistribute it and/or modify it
|
||||
// under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation; either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// Spot is distributed in the hope that it will be useful, but WITHOUT
|
||||
// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
||||
// or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public
|
||||
// License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
#include "common_sys.hh"
|
||||
|
||||
#include <string>
|
||||
#include <iostream>
|
||||
#include <limits>
|
||||
#include <set>
|
||||
#include <memory>
|
||||
|
||||
#include <argp.h>
|
||||
#include "error.h"
|
||||
|
||||
#include "common_setup.hh"
|
||||
#include "common_finput.hh"
|
||||
#include "common_cout.hh"
|
||||
#include "common_aoutput.hh"
|
||||
#include "common_range.hh"
|
||||
#include "common_post.hh"
|
||||
#include "common_conv.hh"
|
||||
#include "common_hoaread.hh"
|
||||
|
||||
#include <spot/twaalgos/product.hh>
|
||||
#include <spot/twaalgos/isdet.hh>
|
||||
#include <spot/twaalgos/stutter.hh>
|
||||
#include <spot/twaalgos/isunamb.hh>
|
||||
#include <spot/misc/optionmap.hh>
|
||||
#include <spot/misc/timer.hh>
|
||||
#include <spot/misc/random.hh>
|
||||
#include <spot/parseaut/public.hh>
|
||||
#include <spot/tl/exclusive.hh>
|
||||
#include <spot/twaalgos/remprop.hh>
|
||||
#include <spot/twaalgos/randomize.hh>
|
||||
#include <spot/twaalgos/are_isomorphic.hh>
|
||||
#include <spot/twaalgos/canonicalize.hh>
|
||||
#include <spot/twaalgos/mask.hh>
|
||||
#include <spot/twaalgos/sepsets.hh>
|
||||
#include <spot/twaalgos/stripacc.hh>
|
||||
#include <spot/twaalgos/remfin.hh>
|
||||
#include <spot/twaalgos/cleanacc.hh>
|
||||
#include <spot/twaalgos/dtwasat.hh>
|
||||
#include <spot/twaalgos/complement.hh>
|
||||
#include <spot/twaalgos/strength.hh>
|
||||
|
||||
static const char argp_program_doc[] ="\
|
||||
Convert, transform, and filter omega-automata.\v\
|
||||
Exit status:\n\
|
||||
0 if some automata were output\n\
|
||||
1 if no automata were output (no match)\n\
|
||||
2 if any error has been reported";
|
||||
|
||||
// Keep this list sorted
|
||||
enum {
|
||||
OPT_ACC_SETS = 256,
|
||||
OPT_ARE_ISOMORPHIC,
|
||||
OPT_CLEAN_ACC,
|
||||
OPT_CNF_ACC,
|
||||
OPT_COMPLEMENT,
|
||||
OPT_COMPLEMENT_ACC,
|
||||
OPT_COUNT,
|
||||
OPT_DECOMPOSE_STRENGTH,
|
||||
OPT_DESTUT,
|
||||
OPT_DNF_ACC,
|
||||
OPT_EDGES,
|
||||
OPT_EXCLUSIVE_AP,
|
||||
OPT_GENERIC,
|
||||
OPT_INSTUT,
|
||||
OPT_INTERSECT,
|
||||
OPT_IS_COMPLETE,
|
||||
OPT_IS_DETERMINISTIC,
|
||||
OPT_IS_EMPTY,
|
||||
OPT_IS_UNAMBIGUOUS,
|
||||
OPT_IS_TERMINAL,
|
||||
OPT_IS_WEAK,
|
||||
OPT_KEEP_STATES,
|
||||
OPT_MASK_ACC,
|
||||
OPT_MERGE,
|
||||
OPT_PRODUCT_AND,
|
||||
OPT_PRODUCT_OR,
|
||||
OPT_RANDOMIZE,
|
||||
OPT_REM_AP,
|
||||
OPT_REM_DEAD,
|
||||
OPT_REM_UNREACH,
|
||||
OPT_REM_FIN,
|
||||
OPT_SAT_MINIMIZE,
|
||||
OPT_SEED,
|
||||
OPT_SEP_SETS,
|
||||
OPT_SIMPLIFY_EXCLUSIVE_AP,
|
||||
OPT_STATES,
|
||||
OPT_STRIPACC,
|
||||
};
|
||||
|
||||
static const argp_option options[] =
|
||||
{
|
||||
/**************************************************/
|
||||
{ nullptr, 0, nullptr, 0, "Input:", 1 },
|
||||
{ "file", 'F', "FILENAME", 0,
|
||||
"process the automaton in FILENAME", 0 },
|
||||
/**************************************************/
|
||||
{ "count", 'c', nullptr, 0, "print only a count of matched automata", 3 },
|
||||
{ "max-count", 'n', "NUM", 0, "output at most NUM automata", 3 },
|
||||
/**************************************************/
|
||||
{ nullptr, 0, nullptr, 0, "Transformations:", 5 },
|
||||
{ "merge-transitions", OPT_MERGE, nullptr, 0,
|
||||
"merge transitions with same destination and acceptance", 0 },
|
||||
{ "product", OPT_PRODUCT_AND, "FILENAME", 0,
|
||||
"build the product with the automaton in FILENAME "
|
||||
"to intersect languages", 0 },
|
||||
{ "product-and", 0, nullptr, OPTION_ALIAS, nullptr, 0 },
|
||||
{ "product-or", OPT_PRODUCT_OR, "FILENAME", 0,
|
||||
"build the product with the automaton in FILENAME "
|
||||
"to sum languages", 0 },
|
||||
{ "randomize", OPT_RANDOMIZE, "s|t", OPTION_ARG_OPTIONAL,
|
||||
"randomize states and transitions (specify 's' or 't' to "
|
||||
"randomize only states or transitions)", 0 },
|
||||
{ "instut", OPT_INSTUT, "1|2", OPTION_ARG_OPTIONAL,
|
||||
"allow more stuttering (two possible algorithms)", 0 },
|
||||
{ "destut", OPT_DESTUT, nullptr, 0, "allow less stuttering", 0 },
|
||||
{ "mask-acc", OPT_MASK_ACC, "NUM[,NUM...]", 0,
|
||||
"remove all transitions in specified acceptance sets", 0 },
|
||||
{ "strip-acceptance", OPT_STRIPACC, nullptr, 0,
|
||||
"remove the acceptance condition and all acceptance sets", 0 },
|
||||
{ "keep-states", OPT_KEEP_STATES, "NUM[,NUM...]", 0,
|
||||
"only keep specified states. The first state will be the new "\
|
||||
"initial state. Implies --remove-unreachable-states.", 0 },
|
||||
{ "dnf-acceptance", OPT_DNF_ACC, nullptr, 0,
|
||||
"put the acceptance condition in Disjunctive Normal Form", 0 },
|
||||
{ "cnf-acceptance", OPT_CNF_ACC, nullptr, 0,
|
||||
"put the acceptance condition in Conjunctive Normal Form", 0 },
|
||||
{ "remove-fin", OPT_REM_FIN, nullptr, 0,
|
||||
"rewrite the automaton without using Fin acceptance", 0 },
|
||||
{ "cleanup-acceptance", OPT_CLEAN_ACC, nullptr, 0,
|
||||
"remove unused acceptance sets from the automaton", 0 },
|
||||
{ "complement", OPT_COMPLEMENT, nullptr, 0,
|
||||
"complement each automaton (currently support only deterministic "
|
||||
"automata)", 0 },
|
||||
{ "complement-acceptance", OPT_COMPLEMENT_ACC, nullptr, 0,
|
||||
"complement the acceptance condition (without touching the automaton)",
|
||||
0 },
|
||||
{ "decompose-strength", OPT_DECOMPOSE_STRENGTH, "t|w|s", 0,
|
||||
"extract the (t) terminal, (w) weak, or (s) strong part of an automaton"
|
||||
" (letters may be combined to combine more strengths in the output)", 0 },
|
||||
{ "exclusive-ap", OPT_EXCLUSIVE_AP, "AP,AP,...", 0,
|
||||
"if any of those APs occur in the automaton, restrict all edges to "
|
||||
"ensure two of them may not be true at the same time. Use this option "
|
||||
"multiple times to declare independent groups of exclusive "
|
||||
"propositions.", 0 },
|
||||
{ "simplify-exclusive-ap", OPT_SIMPLIFY_EXCLUSIVE_AP, nullptr, 0,
|
||||
"if --exclusive-ap is used, assume those AP groups are actually exclusive"
|
||||
" in the system to simplify the expression of transition labels (implies "
|
||||
"--merge-transitions)", 0 },
|
||||
{ "remove-ap", OPT_REM_AP, "AP[=0|=1][,AP...]", 0,
|
||||
"remove atomic propositions either by existential quantification, or "
|
||||
"by assigning them 0 or 1", 0 },
|
||||
{ "remove-unreachable-states", OPT_REM_UNREACH, nullptr, 0,
|
||||
"remove states that are unreachable from the initial state", 0 },
|
||||
{ "remove-dead-states", OPT_REM_DEAD, nullptr, 0,
|
||||
"remove states that are unreachable, or that cannot belong to an "
|
||||
"infinite path", 0 },
|
||||
{ "separate-sets", OPT_SEP_SETS, nullptr, 0,
|
||||
"if both Inf(x) and Fin(x) appear in the acceptance condition, replace "
|
||||
"Fin(x) by a new Fin(y) and adjust the automaton", 0 },
|
||||
{ "sat-minimize", OPT_SAT_MINIMIZE, "options", OPTION_ARG_OPTIONAL,
|
||||
"minimize the automaton using a SAT solver (only work for deterministic"
|
||||
" automata)", 0 },
|
||||
/**************************************************/
|
||||
{ nullptr, 0, nullptr, 0, "Filtering options:", 6 },
|
||||
{ "are-isomorphic", OPT_ARE_ISOMORPHIC, "FILENAME", 0,
|
||||
"keep automata that are isomorphic to the automaton in FILENAME", 0 },
|
||||
{ "isomorphic", 0, nullptr, OPTION_ALIAS | OPTION_HIDDEN, nullptr, 0 },
|
||||
{ "unique", 'u', nullptr, 0,
|
||||
"do not output the same automaton twice (same in the sense that they "\
|
||||
"are isomorphic)", 0 },
|
||||
{ "is-complete", OPT_IS_COMPLETE, nullptr, 0,
|
||||
"keep complete automata", 0 },
|
||||
{ "is-deterministic", OPT_IS_DETERMINISTIC, nullptr, 0,
|
||||
"keep deterministic automata", 0 },
|
||||
{ "is-empty", OPT_IS_EMPTY, nullptr, 0,
|
||||
"keep automata with an empty language", 0 },
|
||||
{ "is-unambiguous", OPT_IS_UNAMBIGUOUS, nullptr, 0,
|
||||
"keep only unambiguous automata", 0 },
|
||||
{ "is-terminal", OPT_IS_TERMINAL, nullptr, 0,
|
||||
"keep only terminal automata", 0 },
|
||||
{ "is-weak", OPT_IS_WEAK, nullptr, 0,
|
||||
"keep only weak automata", 0 },
|
||||
{ "intersect", OPT_INTERSECT, "FILENAME", 0,
|
||||
"keep automata whose languages have an non-empty intersection with"
|
||||
" the automaton from FILENAME", 0 },
|
||||
{ "invert-match", 'v', nullptr, 0, "select non-matching automata", 0 },
|
||||
{ "states", OPT_STATES, "RANGE", 0,
|
||||
"keep automata whose number of states are in RANGE", 0 },
|
||||
{ "edges", OPT_EDGES, "RANGE", 0,
|
||||
"keep automata whose number of edges are in RANGE", 0 },
|
||||
{ "acc-sets", OPT_ACC_SETS, "RANGE", 0,
|
||||
"keep automata whose number of acceptance sets are in RANGE", 0 },
|
||||
RANGE_DOC_FULL,
|
||||
{ nullptr, 0, nullptr, 0,
|
||||
"If any option among --small, --deterministic, or --any is given, "
|
||||
"then the simplification level defaults to --high unless specified "
|
||||
"otherwise. If any option among --low, --medium, or --high is given, "
|
||||
"then the simplification goal defaults to --small unless specified "
|
||||
"otherwise. If none of those options are specified, then autfilt "
|
||||
"acts as is --any --low were given: these actually disable the "
|
||||
"simplification routines.", 22 },
|
||||
/**************************************************/
|
||||
{ nullptr, 0, nullptr, 0, "Miscellaneous options:", -1 },
|
||||
{ "extra-options", 'x', "OPTS", 0,
|
||||
"fine-tuning options (see spot-x (7))", 0 },
|
||||
{ "seed", OPT_SEED, "INT", 0,
|
||||
"seed for the random number generator (0)", 0 },
|
||||
{ nullptr, 0, nullptr, 0, nullptr, 0 }
|
||||
};
|
||||
|
||||
static const struct argp_child children[] =
|
||||
{
|
||||
{ &hoaread_argp, 0, nullptr, 0 },
|
||||
{ &aoutput_argp, 0, nullptr, 0 },
|
||||
{ &aoutput_io_format_argp, 0, nullptr, 4 },
|
||||
{ &post_argp_disabled, 0, nullptr, 0 },
|
||||
{ &misc_argp, 0, nullptr, -1 },
|
||||
{ nullptr, 0, nullptr, 0 }
|
||||
};
|
||||
|
||||
typedef spot::twa_graph::graph_t::edge_storage_t tr_t;
|
||||
typedef std::set<std::vector<tr_t>> unique_aut_t;
|
||||
static long int match_count = 0;
|
||||
static spot::option_map extra_options;
|
||||
static bool randomize_st = false;
|
||||
static bool randomize_tr = false;
|
||||
static int opt_seed = 0;
|
||||
|
||||
// We want all these variable to be destroyed when we exit main, to
|
||||
// make sure it happens before all other global variables (like the
|
||||
// atomic propositions maps) are destroyed. Otherwise we risk
|
||||
// accessing deleted stuff.
|
||||
static struct opt_t
|
||||
{
|
||||
spot::bdd_dict_ptr dict = spot::make_bdd_dict();
|
||||
spot::twa_graph_ptr product_and = nullptr;
|
||||
spot::twa_graph_ptr product_or = nullptr;
|
||||
spot::twa_graph_ptr intersect = nullptr;
|
||||
spot::twa_graph_ptr are_isomorphic = nullptr;
|
||||
std::unique_ptr<spot::isomorphism_checker>
|
||||
isomorphism_checker = nullptr;
|
||||
std::unique_ptr<unique_aut_t> uniq = nullptr;
|
||||
}* opt;
|
||||
|
||||
static bool opt_merge = false;
|
||||
static bool opt_is_complete = false;
|
||||
static bool opt_is_deterministic = false;
|
||||
static bool opt_is_unambiguous = false;
|
||||
static bool opt_is_terminal = false;
|
||||
static bool opt_is_weak = false;
|
||||
static bool opt_invert = false;
|
||||
static range opt_states = { 0, std::numeric_limits<int>::max() };
|
||||
static range opt_edges = { 0, std::numeric_limits<int>::max() };
|
||||
static range opt_accsets = { 0, std::numeric_limits<int>::max() };
|
||||
static int opt_max_count = -1;
|
||||
static bool opt_destut = false;
|
||||
static char opt_instut = 0;
|
||||
static bool opt_is_empty = false;
|
||||
static bool opt_stripacc = false;
|
||||
static bool opt_dnf_acc = false;
|
||||
static bool opt_cnf_acc = false;
|
||||
static bool opt_rem_fin = false;
|
||||
static bool opt_clean_acc = false;
|
||||
static bool opt_complement = false;
|
||||
static bool opt_complement_acc = false;
|
||||
static char* opt_decompose_strength = nullptr;
|
||||
static spot::acc_cond::mark_t opt_mask_acc = 0U;
|
||||
static std::vector<bool> opt_keep_states = {};
|
||||
static unsigned int opt_keep_states_initial = 0;
|
||||
static spot::exclusive_ap excl_ap;
|
||||
static spot::remove_ap rem_ap;
|
||||
static bool opt_simplify_exclusive_ap = false;
|
||||
static bool opt_rem_dead = false;
|
||||
static bool opt_rem_unreach = false;
|
||||
static bool opt_sep_sets = false;
|
||||
static const char* opt_sat_minimize = nullptr;
|
||||
|
||||
static int
|
||||
parse_opt(int key, char* arg, struct argp_state*)
|
||||
{
|
||||
// This switch is alphabetically-ordered.
|
||||
switch (key)
|
||||
{
|
||||
case 'c':
|
||||
automaton_format = Count;
|
||||
break;
|
||||
case 'F':
|
||||
jobs.emplace_back(arg, true);
|
||||
break;
|
||||
case 'n':
|
||||
opt_max_count = to_pos_int(arg);
|
||||
break;
|
||||
case 'u':
|
||||
opt->uniq =
|
||||
std::unique_ptr<unique_aut_t>(new std::set<std::vector<tr_t>>());
|
||||
break;
|
||||
case 'v':
|
||||
opt_invert = true;
|
||||
break;
|
||||
case 'x':
|
||||
{
|
||||
const char* opt = extra_options.parse_options(arg);
|
||||
if (opt)
|
||||
error(2, 0, "failed to parse --options near '%s'", opt);
|
||||
}
|
||||
break;
|
||||
case OPT_ACC_SETS:
|
||||
opt_accsets = parse_range(arg, 0, std::numeric_limits<int>::max());
|
||||
break;
|
||||
case OPT_ARE_ISOMORPHIC:
|
||||
opt->are_isomorphic = read_automaton(arg, opt->dict);
|
||||
break;
|
||||
case OPT_CLEAN_ACC:
|
||||
opt_clean_acc = true;
|
||||
break;
|
||||
case OPT_CNF_ACC:
|
||||
opt_dnf_acc = false;
|
||||
opt_cnf_acc = true;
|
||||
break;
|
||||
case OPT_COMPLEMENT:
|
||||
opt_complement = true;
|
||||
break;
|
||||
case OPT_COMPLEMENT_ACC:
|
||||
opt_complement_acc = true;
|
||||
break;
|
||||
case OPT_DECOMPOSE_STRENGTH:
|
||||
opt_decompose_strength = arg;
|
||||
break;
|
||||
case OPT_DESTUT:
|
||||
opt_destut = true;
|
||||
break;
|
||||
case OPT_DNF_ACC:
|
||||
opt_dnf_acc = true;
|
||||
opt_cnf_acc = false;
|
||||
break;
|
||||
case OPT_EDGES:
|
||||
opt_edges = parse_range(arg, 0, std::numeric_limits<int>::max());
|
||||
break;
|
||||
case OPT_EXCLUSIVE_AP:
|
||||
excl_ap.add_group(arg);
|
||||
break;
|
||||
case OPT_INSTUT:
|
||||
if (!arg || (arg[0] == '1' && arg[1] == 0))
|
||||
opt_instut = 1;
|
||||
else if (arg[0] == '2' && arg[1] == 0)
|
||||
opt_instut = 2;
|
||||
else
|
||||
error(2, 0, "unknown argument for --instut: %s", arg);
|
||||
break;
|
||||
case OPT_INTERSECT:
|
||||
opt->intersect = read_automaton(arg, opt->dict);
|
||||
break;
|
||||
case OPT_IS_COMPLETE:
|
||||
opt_is_complete = true;
|
||||
break;
|
||||
case OPT_IS_DETERMINISTIC:
|
||||
opt_is_deterministic = true;
|
||||
break;
|
||||
case OPT_IS_EMPTY:
|
||||
opt_is_empty = true;
|
||||
break;
|
||||
case OPT_IS_UNAMBIGUOUS:
|
||||
opt_is_unambiguous = true;
|
||||
break;
|
||||
case OPT_IS_TERMINAL:
|
||||
opt_is_terminal = true;
|
||||
break;
|
||||
case OPT_IS_WEAK:
|
||||
opt_is_weak = true;
|
||||
break;
|
||||
case OPT_MERGE:
|
||||
opt_merge = true;
|
||||
break;
|
||||
case OPT_MASK_ACC:
|
||||
{
|
||||
for (auto res : to_longs(arg))
|
||||
{
|
||||
if (res < 0)
|
||||
error(2, 0, "acceptance sets should be non-negative:"
|
||||
" --mask-acc=%ld", res);
|
||||
if (static_cast<unsigned long>(res)
|
||||
> sizeof(spot::acc_cond::mark_t::value_t))
|
||||
error(2, 0, "this implementation does not support that many"
|
||||
" acceptance sets: --mask-acc=%ld", res);
|
||||
opt_mask_acc.set(res);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case OPT_KEEP_STATES:
|
||||
{
|
||||
std::vector<long> values = to_longs(arg);
|
||||
if (!values.empty())
|
||||
opt_keep_states_initial = values[0];
|
||||
for (auto res : values)
|
||||
{
|
||||
if (res < 0)
|
||||
error(2, 0, "state ids should be non-negative:"
|
||||
" --mask-acc=%ld", res);
|
||||
// We don't know yet how many states the automata contain.
|
||||
if (opt_keep_states.size() <= static_cast<unsigned long>(res))
|
||||
opt_keep_states.resize(res + 1, false);
|
||||
opt_keep_states[res] = true;
|
||||
}
|
||||
opt_rem_unreach = true;
|
||||
break;
|
||||
}
|
||||
case OPT_PRODUCT_AND:
|
||||
{
|
||||
auto a = read_automaton(arg, opt->dict);
|
||||
if (!opt->product_and)
|
||||
opt->product_and = std::move(a);
|
||||
else
|
||||
opt->product_and = spot::product(std::move(opt->product_and),
|
||||
std::move(a));
|
||||
}
|
||||
break;
|
||||
case OPT_PRODUCT_OR:
|
||||
{
|
||||
auto a = read_automaton(arg, opt->dict);
|
||||
if (!opt->product_or)
|
||||
opt->product_or = std::move(a);
|
||||
else
|
||||
opt->product_or = spot::product_or(std::move(opt->product_or),
|
||||
std::move(a));
|
||||
}
|
||||
break;
|
||||
case OPT_RANDOMIZE:
|
||||
if (arg)
|
||||
{
|
||||
for (auto p = arg; *p; ++p)
|
||||
switch (*p)
|
||||
{
|
||||
case 's':
|
||||
randomize_st = true;
|
||||
break;
|
||||
case 't':
|
||||
randomize_tr = true;
|
||||
break;
|
||||
default:
|
||||
error(2, 0, "unknown argument for --randomize: '%c'", *p);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
randomize_tr = true;
|
||||
randomize_st = true;
|
||||
}
|
||||
break;
|
||||
case OPT_REM_AP:
|
||||
rem_ap.add_ap(arg);
|
||||
break;
|
||||
case OPT_REM_DEAD:
|
||||
opt_rem_dead = true;
|
||||
break;
|
||||
case OPT_REM_FIN:
|
||||
opt_rem_fin = true;
|
||||
break;
|
||||
case OPT_REM_UNREACH:
|
||||
opt_rem_unreach = true;
|
||||
break;
|
||||
case OPT_SAT_MINIMIZE:
|
||||
opt_sat_minimize = arg ? arg : "";
|
||||
break;
|
||||
case OPT_SEED:
|
||||
opt_seed = to_int(arg);
|
||||
break;
|
||||
case OPT_SEP_SETS:
|
||||
opt_sep_sets = true;
|
||||
break;
|
||||
case OPT_SIMPLIFY_EXCLUSIVE_AP:
|
||||
opt_simplify_exclusive_ap = true;
|
||||
opt_merge = true;
|
||||
break;
|
||||
case OPT_STATES:
|
||||
opt_states = parse_range(arg, 0, std::numeric_limits<int>::max());
|
||||
break;
|
||||
case OPT_STRIPACC:
|
||||
opt_stripacc = true;
|
||||
break;
|
||||
case ARGP_KEY_ARG:
|
||||
jobs.emplace_back(arg, true);
|
||||
break;
|
||||
|
||||
default:
|
||||
return ARGP_ERR_UNKNOWN;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
namespace
|
||||
{
|
||||
class hoa_processor: public job_processor
|
||||
{
|
||||
private:
|
||||
spot::postprocessor& post;
|
||||
automaton_printer printer;
|
||||
public:
|
||||
|
||||
hoa_processor(spot::postprocessor& post)
|
||||
: post(post), printer(aut_input)
|
||||
{
|
||||
}
|
||||
|
||||
int
|
||||
process_formula(spot::formula, const char*, int)
|
||||
{
|
||||
SPOT_UNREACHABLE();
|
||||
}
|
||||
|
||||
int
|
||||
process_automaton(const spot::const_parsed_aut_ptr& haut,
|
||||
const char* filename)
|
||||
{
|
||||
spot::stopwatch sw;
|
||||
sw.start();
|
||||
|
||||
// If --stats or --name is used, duplicate the automaton so we
|
||||
// never modify the original automaton (e.g. with
|
||||
// merge_edges()) and the statistics about it make sense.
|
||||
auto aut = ((automaton_format == Stats) || opt_name)
|
||||
? spot::make_twa_graph(haut->aut, spot::twa::prop_set::all())
|
||||
: haut->aut;
|
||||
|
||||
// Preprocessing.
|
||||
|
||||
if (opt_stripacc)
|
||||
spot::strip_acceptance_here(aut);
|
||||
if (opt_merge)
|
||||
aut->merge_edges();
|
||||
if (opt_clean_acc || opt_rem_fin)
|
||||
cleanup_acceptance_here(aut);
|
||||
if (opt_sep_sets)
|
||||
separate_sets_here(aut);
|
||||
if (opt_complement_acc)
|
||||
aut->set_acceptance(aut->acc().num_sets(),
|
||||
aut->get_acceptance().complement());
|
||||
if (opt_complement)
|
||||
{
|
||||
if (!spot::is_deterministic(aut))
|
||||
{
|
||||
std::cerr << filename << ':'
|
||||
<< haut->loc << (": --complement currently supports "
|
||||
"only deterministic automata\n");
|
||||
exit(2);
|
||||
}
|
||||
aut = spot::dtwa_complement(aut);
|
||||
}
|
||||
if (opt_rem_fin)
|
||||
aut = remove_fin(aut);
|
||||
if (opt_dnf_acc)
|
||||
aut->set_acceptance(aut->acc().num_sets(),
|
||||
aut->get_acceptance().to_dnf());
|
||||
if (opt_cnf_acc)
|
||||
aut->set_acceptance(aut->acc().num_sets(),
|
||||
aut->get_acceptance().to_cnf());
|
||||
|
||||
// Filters.
|
||||
|
||||
bool matched = true;
|
||||
|
||||
matched &= opt_states.contains(aut->num_states());
|
||||
matched &= opt_edges.contains(aut->num_edges());
|
||||
matched &= opt_accsets.contains(aut->acc().num_sets());
|
||||
if (opt_is_complete)
|
||||
matched &= is_complete(aut);
|
||||
if (opt_is_deterministic)
|
||||
matched &= is_deterministic(aut);
|
||||
if (opt_is_deterministic)
|
||||
matched &= is_deterministic(aut);
|
||||
else if (opt_is_unambiguous)
|
||||
matched &= is_unambiguous(aut);
|
||||
if (opt_is_terminal)
|
||||
matched &= is_terminal_automaton(aut);
|
||||
else if (opt_is_weak)
|
||||
matched &= is_weak_automaton(aut);
|
||||
if (opt->are_isomorphic)
|
||||
matched &= opt->isomorphism_checker->is_isomorphic(aut);
|
||||
if (opt_is_empty)
|
||||
matched &= aut->is_empty();
|
||||
if (opt->intersect)
|
||||
matched &= !spot::product(aut, opt->intersect)->is_empty();
|
||||
|
||||
// Drop or keep matched automata depending on the --invert option
|
||||
if (matched == opt_invert)
|
||||
return 0;
|
||||
|
||||
// Postprocessing.
|
||||
|
||||
if (opt_mask_acc)
|
||||
aut = mask_acc_sets(aut, opt_mask_acc & aut->acc().all_sets());
|
||||
|
||||
if (!excl_ap.empty())
|
||||
aut = excl_ap.constrain(aut, opt_simplify_exclusive_ap);
|
||||
|
||||
if (!rem_ap.empty())
|
||||
aut = rem_ap.strip(aut);
|
||||
|
||||
if (opt_destut)
|
||||
aut = spot::closure(std::move(aut));
|
||||
if (opt_instut == 1)
|
||||
aut = spot::sl(std::move(aut));
|
||||
else if (opt_instut == 2)
|
||||
aut = spot::sl2(std::move(aut));
|
||||
|
||||
if (!opt_keep_states.empty())
|
||||
aut = mask_keep_states(aut, opt_keep_states, opt_keep_states_initial);
|
||||
if (opt_rem_dead)
|
||||
aut->purge_dead_states();
|
||||
else if (opt_rem_unreach)
|
||||
aut->purge_unreachable_states();
|
||||
|
||||
if (opt->product_and)
|
||||
aut = spot::product(std::move(aut), opt->product_and);
|
||||
if (opt->product_or)
|
||||
aut = spot::product_or(std::move(aut), opt->product_or);
|
||||
|
||||
if (opt_decompose_strength)
|
||||
{
|
||||
aut = decompose_strength(aut, opt_decompose_strength);
|
||||
if (!aut)
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (opt_sat_minimize)
|
||||
{
|
||||
aut = spot::sat_minimize(aut, opt_sat_minimize, sbacc);
|
||||
if (!aut)
|
||||
return 0;
|
||||
}
|
||||
|
||||
aut = post.run(aut, nullptr);
|
||||
|
||||
if (randomize_st || randomize_tr)
|
||||
spot::randomize(aut, randomize_st, randomize_tr);
|
||||
|
||||
const double conversion_time = sw.stop();
|
||||
|
||||
if (opt->uniq)
|
||||
{
|
||||
auto tmp =
|
||||
spot::canonicalize(make_twa_graph(aut,
|
||||
spot::twa::prop_set::all()));
|
||||
if (!opt->uniq->emplace(tmp->edge_vector().begin() + 1,
|
||||
tmp->edge_vector().end()).second)
|
||||
return 0;
|
||||
}
|
||||
|
||||
++match_count;
|
||||
|
||||
printer.print(aut, nullptr, filename, -1, conversion_time, haut);
|
||||
|
||||
if (opt_max_count >= 0 && match_count >= opt_max_count)
|
||||
abort_run = true;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
aborted(const spot::const_parsed_aut_ptr& h, const char* filename)
|
||||
{
|
||||
std::cerr << filename << ':' << h->loc << ": aborted input automaton\n";
|
||||
return 2;
|
||||
}
|
||||
|
||||
int
|
||||
process_file(const char* filename)
|
||||
{
|
||||
auto hp = spot::automaton_stream_parser(filename, opt_parse);
|
||||
int err = 0;
|
||||
while (!abort_run)
|
||||
{
|
||||
auto haut = hp.parse(opt->dict);
|
||||
if (!haut->aut && haut->errors.empty())
|
||||
break;
|
||||
if (haut->format_errors(std::cerr))
|
||||
err = 2;
|
||||
if (!haut->aut)
|
||||
error(2, 0, "failed to read automaton from %s", filename);
|
||||
else if (haut->aborted)
|
||||
err = std::max(err, aborted(haut, filename));
|
||||
else
|
||||
process_automaton(haut, filename);
|
||||
}
|
||||
return err;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
int
|
||||
main(int argc, char** argv)
|
||||
{
|
||||
setup(argv);
|
||||
|
||||
const argp ap = { options, parse_opt, "[FILENAMES...]",
|
||||
argp_program_doc, children, nullptr, nullptr };
|
||||
|
||||
try
|
||||
{
|
||||
// This will ensure that all objects stored in this struct are
|
||||
// destroyed before global variables.
|
||||
opt_t o;
|
||||
opt = &o;
|
||||
|
||||
// Disable post-processing as much as possible by default.
|
||||
level = spot::postprocessor::Low;
|
||||
pref = spot::postprocessor::Any;
|
||||
type = spot::postprocessor::Generic;
|
||||
if (int err = argp_parse(&ap, argc, argv, ARGP_NO_HELP, nullptr, nullptr))
|
||||
exit(err);
|
||||
|
||||
if (level_set && !pref_set)
|
||||
pref = spot::postprocessor::Small;
|
||||
if (pref_set && !level_set)
|
||||
level = spot::postprocessor::High;
|
||||
|
||||
if (jobs.empty())
|
||||
jobs.emplace_back("-", true);
|
||||
|
||||
if (opt->are_isomorphic)
|
||||
{
|
||||
if (opt_merge)
|
||||
opt->are_isomorphic->merge_edges();
|
||||
opt->isomorphism_checker = std::unique_ptr<spot::isomorphism_checker>
|
||||
(new spot::isomorphism_checker(opt->are_isomorphic));
|
||||
}
|
||||
|
||||
|
||||
spot::srand(opt_seed);
|
||||
|
||||
spot::postprocessor post(&extra_options);
|
||||
post.set_pref(pref | comp | sbacc);
|
||||
post.set_type(type);
|
||||
post.set_level(level);
|
||||
|
||||
hoa_processor processor(post);
|
||||
if (processor.run())
|
||||
return 2;
|
||||
}
|
||||
catch (const std::runtime_error& e)
|
||||
{
|
||||
error(2, 0, "%s", e.what());
|
||||
}
|
||||
catch (const std::invalid_argument& e)
|
||||
{
|
||||
error(2, 0, "%s", e.what());
|
||||
}
|
||||
|
||||
if (automaton_format == Count)
|
||||
std::cout << match_count << std::endl;
|
||||
|
||||
return !match_count;
|
||||
}
|
||||
368
spot/bin/common_aoutput.cc
Normal file
368
spot/bin/common_aoutput.cc
Normal file
|
|
@ -0,0 +1,368 @@
|
|||
// -*- coding: utf-8 -*-
|
||||
// Copyright (C) 2012, 2013, 2014, 2015 Laboratoire de Recherche et
|
||||
// Développement de l'Epita (LRDE).
|
||||
//
|
||||
// This file is part of Spot, a model checking library.
|
||||
//
|
||||
// Spot is free software; you can redistribute it and/or modify it
|
||||
// under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation; either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// Spot is distributed in the hope that it will be useful, but WITHOUT
|
||||
// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
||||
// or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public
|
||||
// License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
#include "common_sys.hh"
|
||||
#include "error.h"
|
||||
#include "argmatch.h"
|
||||
|
||||
#include "common_aoutput.hh"
|
||||
#include "common_post.hh"
|
||||
#include "common_cout.hh"
|
||||
|
||||
#include <spot/twa/bddprint.hh>
|
||||
|
||||
#include <spot/twaalgos/dot.hh>
|
||||
#include <spot/twaalgos/lbtt.hh>
|
||||
#include <spot/twaalgos/hoa.hh>
|
||||
#include <spot/twaalgos/neverclaim.hh>
|
||||
#include <spot/twaalgos/stutter.hh>
|
||||
#include <spot/twaalgos/isunamb.hh>
|
||||
#include <spot/twaalgos/strength.hh>
|
||||
|
||||
automaton_format_t automaton_format = Dot;
|
||||
static const char* opt_dot = nullptr;
|
||||
static const char* opt_never = nullptr;
|
||||
static const char* hoa_opt = nullptr;
|
||||
static const char* opt_lbtt = nullptr;
|
||||
const char* opt_name = nullptr;
|
||||
static const char* opt_output = nullptr;
|
||||
static const char* stats = "";
|
||||
enum check_type
|
||||
{
|
||||
check_unambiguous = (1 << 0),
|
||||
check_stutter = (1 << 1),
|
||||
check_strength = (1 << 2),
|
||||
check_all = -1U,
|
||||
};
|
||||
static char const *const check_args[] =
|
||||
{
|
||||
"unambiguous",
|
||||
"stutter-invariant", "stuttering-invariant",
|
||||
"stutter-insensitive", "stuttering-insensitive",
|
||||
"stutter-sensitive", "stuttering-sensitive",
|
||||
"strength", "weak", "terminal",
|
||||
"all",
|
||||
nullptr
|
||||
};
|
||||
static check_type const check_types[] =
|
||||
{
|
||||
check_unambiguous,
|
||||
check_stutter, check_stutter,
|
||||
check_stutter, check_stutter,
|
||||
check_stutter, check_stutter,
|
||||
check_strength, check_strength, check_strength,
|
||||
check_all
|
||||
};
|
||||
ARGMATCH_VERIFY(check_args, check_types);
|
||||
unsigned opt_check = 0U;
|
||||
|
||||
enum {
|
||||
OPT_DOT = 1,
|
||||
OPT_LBTT,
|
||||
OPT_NAME,
|
||||
OPT_STATS,
|
||||
OPT_CHECK,
|
||||
};
|
||||
|
||||
static const argp_option options[] =
|
||||
{
|
||||
/**************************************************/
|
||||
{ nullptr, 0, nullptr, 0, "Output format:", 3 },
|
||||
{ "dot", OPT_DOT, "1|a|b|B|c|e|f(FONT)|h|n|N|o|r|R|s|t|v|+INT",
|
||||
OPTION_ARG_OPTIONAL,
|
||||
"GraphViz's format (default). Add letters for "
|
||||
"(1) force numbered states, "
|
||||
"(a) acceptance display, (b) acceptance sets as bullets, "
|
||||
"(B) bullets except for Büchi/co-Büchi automata, "
|
||||
"(c) force circular nodes, (e) force elliptic nodes, "
|
||||
"(f(FONT)) use FONT, (h) horizontal layout, "
|
||||
"(v) vertical layout, (n) with name, (N) without name, "
|
||||
"(o) ordered transitions, "
|
||||
"(r) rainbow colors for acceptance sets, "
|
||||
"(R) color acceptance sets by Inf/Fin, (s) with SCCs, "
|
||||
"(t) force transition-based acceptance, "
|
||||
"(+INT) add INT to all set numbers", 0 },
|
||||
{ "hoaf", 'H', "i|l|m|s|t|v", OPTION_ARG_OPTIONAL,
|
||||
"Output the automaton in HOA format. Add letters to select "
|
||||
"(i) use implicit labels for complete deterministic automata, "
|
||||
"(s) prefer state-based acceptance when possible [default], "
|
||||
"(t) force transition-based acceptance, "
|
||||
"(m) mix state and transition-based acceptance, "
|
||||
"(k) use state labels when possible, "
|
||||
"(l) single-line output, "
|
||||
"(v) verbose properties", 0 },
|
||||
{ "lbtt", OPT_LBTT, "t", OPTION_ARG_OPTIONAL,
|
||||
"LBTT's format (add =t to force transition-based acceptance even"
|
||||
" on Büchi automata)", 0 },
|
||||
{ "name", OPT_NAME, "FORMAT", 0,
|
||||
"set the name of the output automaton", 0 },
|
||||
{ "output", 'o', "FORMAT", 0,
|
||||
"send output to a file named FORMAT instead of standard output. The"
|
||||
" first automaton sent to a file truncates it unless FORMAT starts"
|
||||
" with '>>'.", 0 },
|
||||
{ "quiet", 'q', nullptr, 0, "suppress all normal output", 0 },
|
||||
{ "spin", 's', "6|c", OPTION_ARG_OPTIONAL, "Spin neverclaim (implies --ba)."
|
||||
" Add letters to select (6) Spin's 6.2.4 style, (c) comments on states",
|
||||
0 },
|
||||
{ "utf8", '8', nullptr, 0, "enable UTF-8 characters in output "
|
||||
"(ignored with --lbtt or --spin)", 0 },
|
||||
{ "stats", OPT_STATS, "FORMAT", 0,
|
||||
"output statistics about the automaton", 0 },
|
||||
{ "check", OPT_CHECK, "PROP", OPTION_ARG_OPTIONAL,
|
||||
"test for the additional property PROP and output the result "
|
||||
"in the HOA format (implies -H). PROP may be any prefix of "
|
||||
"'all' (default), 'unambiguous', 'stutter-invariant', or 'strength'.",
|
||||
0 },
|
||||
{ nullptr, 0, nullptr, 0, nullptr, 0 }
|
||||
};
|
||||
|
||||
const struct argp aoutput_argp = { options, parse_opt_aoutput, nullptr, nullptr,
|
||||
nullptr, nullptr, nullptr };
|
||||
|
||||
// Those can be overridden by individual tools. E.g. randaut has no
|
||||
// notion of input file, so %F and %L represent something else.
|
||||
char F_doc[32] = "name of the input file";
|
||||
char L_doc[32] = "location in the input file";
|
||||
|
||||
static const argp_option io_options[] =
|
||||
{
|
||||
/**************************************************/
|
||||
{ nullptr, 0, nullptr, 0, "Any FORMAT string may use "\
|
||||
"the following interpreted sequences (capitals for input,"
|
||||
" minuscules for output):", 4 },
|
||||
{ "%F", 0, nullptr, OPTION_DOC | OPTION_NO_USAGE, F_doc, 0 },
|
||||
{ "%L", 0, nullptr, OPTION_DOC | OPTION_NO_USAGE, L_doc, 0 },
|
||||
{ "%M, %m", 0, nullptr, OPTION_DOC | OPTION_NO_USAGE,
|
||||
"name of the automaton", 0 },
|
||||
{ "%S, %s", 0, nullptr, OPTION_DOC | OPTION_NO_USAGE,
|
||||
"number of states", 0 },
|
||||
{ "%E, %e", 0, nullptr, OPTION_DOC | OPTION_NO_USAGE,
|
||||
"number of edges", 0 },
|
||||
{ "%T, %t", 0, nullptr, OPTION_DOC | OPTION_NO_USAGE,
|
||||
"number of transitions", 0 },
|
||||
{ "%A, %a", 0, nullptr, OPTION_DOC | OPTION_NO_USAGE,
|
||||
"number of acceptance sets", 0 },
|
||||
{ "%G, %g", 0, nullptr, OPTION_DOC | OPTION_NO_USAGE,
|
||||
"acceptance condition (in HOA syntax)", 0 },
|
||||
{ "%C, %c", 0, nullptr, OPTION_DOC | OPTION_NO_USAGE,
|
||||
"number of SCCs", 0 },
|
||||
{ "%n", 0, nullptr, OPTION_DOC | OPTION_NO_USAGE,
|
||||
"number of nondeterministic states in output", 0 },
|
||||
{ "%d", 0, nullptr, OPTION_DOC | OPTION_NO_USAGE,
|
||||
"1 if the output is deterministic, 0 otherwise", 0 },
|
||||
{ "%p", 0, nullptr, OPTION_DOC | OPTION_NO_USAGE,
|
||||
"1 if the output is complete, 0 otherwise", 0 },
|
||||
{ "%r", 0, nullptr, OPTION_DOC | OPTION_NO_USAGE,
|
||||
"processing time (excluding parsing) in seconds", 0 },
|
||||
{ "%w", 0, nullptr, OPTION_DOC | OPTION_NO_USAGE,
|
||||
"one word accepted by the output automaton", 0 },
|
||||
{ "%%", 0, nullptr, OPTION_DOC | OPTION_NO_USAGE,
|
||||
"a single %", 0 },
|
||||
{ nullptr, 0, nullptr, 0, nullptr, 0 }
|
||||
};
|
||||
|
||||
const struct argp aoutput_io_format_argp = { io_options, nullptr, nullptr,
|
||||
nullptr, nullptr,
|
||||
nullptr, nullptr };
|
||||
|
||||
static const argp_option o_options[] =
|
||||
{
|
||||
/**************************************************/
|
||||
{ nullptr, 0, nullptr, 0, "Any FORMAT string may use "\
|
||||
"the following interpreted sequences:", 4 },
|
||||
{ "%F", 0, nullptr, OPTION_DOC | OPTION_NO_USAGE, F_doc, 0 },
|
||||
{ "%L", 0, nullptr, OPTION_DOC | OPTION_NO_USAGE, L_doc, 0 },
|
||||
{ "%m", 0, nullptr, OPTION_DOC | OPTION_NO_USAGE,
|
||||
"name of the automaton", 0 },
|
||||
{ "%s", 0, nullptr, OPTION_DOC | OPTION_NO_USAGE,
|
||||
"number of states", 0 },
|
||||
{ "%e", 0, nullptr, OPTION_DOC | OPTION_NO_USAGE,
|
||||
"number of edges", 0 },
|
||||
{ "%t", 0, nullptr, OPTION_DOC | OPTION_NO_USAGE,
|
||||
"number of transitions", 0 },
|
||||
{ "%a", 0, nullptr, OPTION_DOC | OPTION_NO_USAGE,
|
||||
"number of acceptance sets", 0 },
|
||||
{ "%g", 0, nullptr, OPTION_DOC | OPTION_NO_USAGE,
|
||||
"acceptance condition (in HOA syntax)", 0 },
|
||||
{ "%c", 0, nullptr, OPTION_DOC | OPTION_NO_USAGE,
|
||||
"number of SCCs", 0 },
|
||||
{ "%n", 0, nullptr, OPTION_DOC | OPTION_NO_USAGE,
|
||||
"number of nondeterministic states in output", 0 },
|
||||
{ "%d", 0, nullptr, OPTION_DOC | OPTION_NO_USAGE,
|
||||
"1 if the output is deterministic, 0 otherwise", 0 },
|
||||
{ "%p", 0, nullptr, OPTION_DOC | OPTION_NO_USAGE,
|
||||
"1 if the output is complete, 0 otherwise", 0 },
|
||||
{ "%r", 0, nullptr, OPTION_DOC | OPTION_NO_USAGE,
|
||||
"processing time (excluding parsing) in seconds", 0 },
|
||||
{ "%w", 0, nullptr, OPTION_DOC | OPTION_NO_USAGE,
|
||||
"one word accepted by the output automaton", 0 },
|
||||
{ "%%", 0, nullptr, OPTION_DOC | OPTION_NO_USAGE,
|
||||
"a single %", 0 },
|
||||
{ nullptr, 0, nullptr, 0, nullptr, 0 }
|
||||
};
|
||||
|
||||
const struct argp aoutput_o_format_argp = { o_options,
|
||||
nullptr, nullptr, nullptr,
|
||||
nullptr, nullptr, nullptr };
|
||||
|
||||
int parse_opt_aoutput(int key, char* arg, struct argp_state*)
|
||||
{
|
||||
// This switch is alphabetically-ordered.
|
||||
switch (key)
|
||||
{
|
||||
case '8':
|
||||
spot::enable_utf8();
|
||||
break;
|
||||
case 'H':
|
||||
automaton_format = Hoa;
|
||||
hoa_opt = arg;
|
||||
break;
|
||||
case 'q':
|
||||
automaton_format = Quiet;
|
||||
break;
|
||||
case 's':
|
||||
automaton_format = Spin;
|
||||
if (type != spot::postprocessor::Monitor)
|
||||
type = spot::postprocessor::BA;
|
||||
if (arg)
|
||||
opt_never = arg;
|
||||
break;
|
||||
case 'o':
|
||||
opt_output = arg;
|
||||
break;
|
||||
case OPT_CHECK:
|
||||
automaton_format = Hoa;
|
||||
if (arg)
|
||||
opt_check |= XARGMATCH("--check", arg, check_args, check_types);
|
||||
else
|
||||
opt_check |= check_all;
|
||||
break;
|
||||
case OPT_DOT:
|
||||
automaton_format = Dot;
|
||||
opt_dot = arg;
|
||||
break;
|
||||
case OPT_LBTT:
|
||||
automaton_format = Lbtt;
|
||||
opt_lbtt = arg;
|
||||
// This test could be removed when more options are added,
|
||||
// because print_lbtt will raise an exception anyway. The
|
||||
// error message is slightly better in the current way.
|
||||
if (arg && (arg[0] != 't' || arg[1] != 0))
|
||||
error(2, 0, "unknown argument for --lbtt: '%s'", arg);
|
||||
break;
|
||||
case OPT_NAME:
|
||||
opt_name = arg;
|
||||
break;
|
||||
case OPT_STATS:
|
||||
if (!*arg)
|
||||
error(2, 0, "empty format string for --stats");
|
||||
stats = arg;
|
||||
automaton_format = Stats;
|
||||
break;
|
||||
default:
|
||||
return ARGP_ERR_UNKNOWN;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
automaton_printer::automaton_printer(stat_style input)
|
||||
: statistics(std::cout, stats, input),
|
||||
namer(name, opt_name, input),
|
||||
outputnamer(outputname, opt_output, input)
|
||||
{
|
||||
if (automaton_format == Count && opt_output)
|
||||
throw std::runtime_error
|
||||
("options --output and --count are incompatible");
|
||||
}
|
||||
|
||||
void
|
||||
automaton_printer::print(const spot::twa_graph_ptr& aut,
|
||||
spot::formula f,
|
||||
// Input location for errors and statistics.
|
||||
const char* filename,
|
||||
int loc,
|
||||
// Time and input automaton for statistics
|
||||
double time,
|
||||
const spot::const_parsed_aut_ptr& haut)
|
||||
{
|
||||
if (opt_check)
|
||||
{
|
||||
if (opt_check & check_stutter)
|
||||
spot::check_stutter_invariance(aut, f);
|
||||
if (opt_check & check_unambiguous)
|
||||
spot::check_unambiguous(aut);
|
||||
if (opt_check & check_strength)
|
||||
spot::check_strength(aut);
|
||||
}
|
||||
|
||||
// Name the output automaton.
|
||||
if (opt_name)
|
||||
{
|
||||
name.str("");
|
||||
namer.print(haut, aut, f, filename, loc, time);
|
||||
aut->set_named_prop("automaton-name", new std::string(name.str()));
|
||||
}
|
||||
|
||||
std::ostream* out = &std::cout;
|
||||
if (opt_output)
|
||||
{
|
||||
outputname.str("");
|
||||
outputnamer.print(haut, aut, f, filename, loc, time);
|
||||
std::string fname = outputname.str();
|
||||
auto p = outputfiles.emplace(fname, nullptr);
|
||||
if (p.second)
|
||||
p.first->second.reset(new output_file(fname.c_str()));
|
||||
out = &p.first->second->ostream();
|
||||
}
|
||||
|
||||
// Output it.
|
||||
switch (automaton_format)
|
||||
{
|
||||
case Count:
|
||||
case Quiet:
|
||||
// Do not output anything.
|
||||
break;
|
||||
case Dot:
|
||||
spot::print_dot(*out, aut, opt_dot);
|
||||
break;
|
||||
case Lbtt:
|
||||
spot::print_lbtt(*out, aut, opt_lbtt);
|
||||
break;
|
||||
case Hoa:
|
||||
spot::print_hoa(*out, aut, hoa_opt) << '\n';
|
||||
break;
|
||||
case Spin:
|
||||
spot::print_never_claim(*out, aut, opt_never);
|
||||
break;
|
||||
case Stats:
|
||||
statistics.set_output(*out);
|
||||
statistics.print(haut, aut, f, filename, loc, time) << '\n';
|
||||
break;
|
||||
}
|
||||
flush_cout();
|
||||
}
|
||||
|
||||
void automaton_printer::add_stat(char c, const spot::printable* p)
|
||||
{
|
||||
namer.declare(c, p);
|
||||
statistics.declare(c, p);
|
||||
outputnamer.declare(c, p);
|
||||
}
|
||||
235
spot/bin/common_aoutput.hh
Normal file
235
spot/bin/common_aoutput.hh
Normal file
|
|
@ -0,0 +1,235 @@
|
|||
// -*- coding: utf-8 -*-
|
||||
// Copyright (C) 2014, 2015 Laboratoire de Recherche et Développement
|
||||
// de l'Epita (LRDE).
|
||||
//
|
||||
// This file is part of Spot, a model checking library.
|
||||
//
|
||||
// Spot is free software; you can redistribute it and/or modify it
|
||||
// under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation; either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// Spot is distributed in the hope that it will be useful, but WITHOUT
|
||||
// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
||||
// or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public
|
||||
// License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "common_sys.hh"
|
||||
|
||||
#include <argp.h>
|
||||
#include <memory>
|
||||
|
||||
#include <spot/parseaut/public.hh>
|
||||
|
||||
#include <spot/twaalgos/stats.hh>
|
||||
#include <spot/twaalgos/sccinfo.hh>
|
||||
#include <spot/twaalgos/gtec/gtec.hh>
|
||||
#include <spot/twaalgos/word.hh>
|
||||
#include <spot/twaalgos/isdet.hh>
|
||||
#include "common_file.hh"
|
||||
|
||||
|
||||
// Format for automaton output
|
||||
enum automaton_format_t {
|
||||
Dot,
|
||||
Lbtt,
|
||||
Spin,
|
||||
Stats,
|
||||
Hoa,
|
||||
Quiet,
|
||||
Count,
|
||||
};
|
||||
|
||||
// The format to use in output_automaton()
|
||||
extern automaton_format_t automaton_format;
|
||||
// Set to the argument of --name, else nullptr.
|
||||
extern const char* opt_name;
|
||||
// Output options
|
||||
extern const struct argp aoutput_argp;
|
||||
|
||||
// help text for %F and %L
|
||||
extern char F_doc[32];
|
||||
extern char L_doc[32];
|
||||
|
||||
// FORMAT help text
|
||||
extern const struct argp aoutput_io_format_argp;
|
||||
extern const struct argp aoutput_o_format_argp;
|
||||
|
||||
// Parse output options
|
||||
int parse_opt_aoutput(int key, char* arg, struct argp_state* state);
|
||||
|
||||
|
||||
enum stat_style { no_input, aut_input, ltl_input };
|
||||
|
||||
/// \brief prints various statistics about a TGBA
|
||||
///
|
||||
/// This object can be configured to display various statistics
|
||||
/// about a TGBA. Some %-sequence of characters are interpreted in
|
||||
/// the format string, and replaced by the corresponding statistics.
|
||||
class hoa_stat_printer: protected spot::stat_printer
|
||||
{
|
||||
public:
|
||||
hoa_stat_printer(std::ostream& os, const char* format,
|
||||
stat_style input = no_input)
|
||||
: spot::stat_printer(os, format)
|
||||
{
|
||||
if (input == aut_input)
|
||||
{
|
||||
declare('A', &haut_acc_);
|
||||
declare('C', &haut_scc_);
|
||||
declare('E', &haut_edges_);
|
||||
declare('G', &haut_gen_acc_);
|
||||
declare('M', &haut_name_);
|
||||
declare('S', &haut_states_);
|
||||
declare('T', &haut_trans_);
|
||||
}
|
||||
declare('F', &filename_);
|
||||
declare('L', &location_);
|
||||
if (input != ltl_input)
|
||||
declare('f', &filename_); // Override the formula printer.
|
||||
declare('m', &aut_name_);
|
||||
declare('w', &aut_word_);
|
||||
}
|
||||
|
||||
using spot::formater::declare;
|
||||
using spot::formater::set_output;
|
||||
|
||||
/// \brief print the configured statistics.
|
||||
///
|
||||
/// The \a f argument is not needed if the Formula does not need
|
||||
/// to be output.
|
||||
std::ostream&
|
||||
print(const spot::const_parsed_aut_ptr& haut,
|
||||
const spot::const_twa_graph_ptr& aut,
|
||||
spot::formula f,
|
||||
const char* filename, int loc, double run_time)
|
||||
{
|
||||
filename_ = filename ? filename : "";
|
||||
if (loc >= 0 && has('L'))
|
||||
{
|
||||
std::ostringstream os;
|
||||
os << loc;
|
||||
location_ = os.str();
|
||||
}
|
||||
if (haut)
|
||||
{
|
||||
if (loc < 0 && has('L'))
|
||||
{
|
||||
std::ostringstream os;
|
||||
os << haut->loc;
|
||||
location_ = os.str();
|
||||
}
|
||||
|
||||
if (has('T'))
|
||||
{
|
||||
spot::twa_sub_statistics s = sub_stats_reachable(haut->aut);
|
||||
haut_states_ = s.states;
|
||||
haut_edges_ = s.edges;
|
||||
haut_trans_ = s.transitions;
|
||||
}
|
||||
else if (has('E'))
|
||||
{
|
||||
spot::twa_sub_statistics s = sub_stats_reachable(haut->aut);
|
||||
haut_states_ = s.states;
|
||||
haut_edges_ = s.edges;
|
||||
}
|
||||
if (has('M'))
|
||||
{
|
||||
auto n = haut->aut->get_named_prop<std::string>("automaton-name");
|
||||
if (n)
|
||||
haut_name_ = *n;
|
||||
else
|
||||
haut_name_.val().clear();
|
||||
}
|
||||
if (has('S'))
|
||||
haut_states_ = haut->aut->num_states();
|
||||
|
||||
if (has('A'))
|
||||
haut_acc_ = haut->aut->acc().num_sets();
|
||||
|
||||
if (has('C'))
|
||||
haut_scc_ = spot::scc_info(haut->aut).scc_count();
|
||||
|
||||
if (has('G'))
|
||||
{
|
||||
std::ostringstream os;
|
||||
os << haut->aut->get_acceptance();
|
||||
haut_gen_acc_ = os.str();
|
||||
}
|
||||
}
|
||||
|
||||
if (has('m'))
|
||||
{
|
||||
auto n = aut->get_named_prop<std::string>("automaton-name");
|
||||
if (n)
|
||||
aut_name_ = *n;
|
||||
else
|
||||
aut_name_.val().clear();
|
||||
}
|
||||
if (has('w'))
|
||||
{
|
||||
auto res = spot::couvreur99(aut)->check();
|
||||
if (res)
|
||||
{
|
||||
auto run = res->accepting_run();
|
||||
assert(run);
|
||||
spot::twa_word w(run->reduce());
|
||||
w.simplify();
|
||||
std::ostringstream out;
|
||||
out << w;
|
||||
aut_word_ = out.str();
|
||||
}
|
||||
else
|
||||
{
|
||||
aut_word_.val().clear();
|
||||
}
|
||||
}
|
||||
|
||||
return this->spot::stat_printer::print(aut, f, run_time);
|
||||
}
|
||||
|
||||
private:
|
||||
spot::printable_value<const char*> filename_;
|
||||
spot::printable_value<std::string> location_;
|
||||
spot::printable_value<std::string> haut_name_;
|
||||
spot::printable_value<std::string> aut_name_;
|
||||
spot::printable_value<std::string> aut_word_;
|
||||
spot::printable_value<std::string> haut_gen_acc_;
|
||||
spot::printable_value<unsigned> haut_states_;
|
||||
spot::printable_value<unsigned> haut_edges_;
|
||||
spot::printable_value<unsigned> haut_trans_;
|
||||
spot::printable_value<unsigned> haut_acc_;
|
||||
spot::printable_value<unsigned> haut_scc_;
|
||||
};
|
||||
|
||||
|
||||
class automaton_printer
|
||||
{
|
||||
hoa_stat_printer statistics;
|
||||
std::ostringstream name;
|
||||
hoa_stat_printer namer;
|
||||
std::ostringstream outputname;
|
||||
hoa_stat_printer outputnamer;
|
||||
std::map<std::string, std::unique_ptr<output_file>> outputfiles;
|
||||
|
||||
public:
|
||||
|
||||
automaton_printer(stat_style input = no_input);
|
||||
|
||||
void
|
||||
print(const spot::twa_graph_ptr& aut,
|
||||
spot::formula f = nullptr,
|
||||
// Input location for errors and statistics.
|
||||
const char* filename = nullptr,
|
||||
int loc = -1,
|
||||
// Time and input automaton for statistics
|
||||
double time = 0.0,
|
||||
const spot::const_parsed_aut_ptr& haut = nullptr);
|
||||
|
||||
void add_stat(char c, const spot::printable* p);
|
||||
};
|
||||
88
spot/bin/common_conv.cc
Normal file
88
spot/bin/common_conv.cc
Normal file
|
|
@ -0,0 +1,88 @@
|
|||
// -*- coding: utf-8 -*-
|
||||
// Copyright (C) 2015 Laboratoire de Recherche et Développement de
|
||||
// l'Epita (LRDE).
|
||||
//
|
||||
// This file is part of Spot, a model checking library.
|
||||
//
|
||||
// Spot is free software; you can redistribute it and/or modify it
|
||||
// under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation; either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// Spot is distributed in the hope that it will be useful, but WITHOUT
|
||||
// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
||||
// or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public
|
||||
// License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
#include "common_conv.hh"
|
||||
#include <cstdlib>
|
||||
#include "error.h"
|
||||
|
||||
int
|
||||
to_int(const char* s)
|
||||
{
|
||||
char* endptr;
|
||||
int res = strtol(s, &endptr, 10);
|
||||
if (*endptr)
|
||||
error(2, 0, "failed to parse '%s' as an integer.", s);
|
||||
return res;
|
||||
}
|
||||
|
||||
int
|
||||
to_pos_int(const char* s)
|
||||
{
|
||||
int res = to_int(s);
|
||||
if (res < 0)
|
||||
error(2, 0, "%d is not positive", res);
|
||||
return res;
|
||||
}
|
||||
|
||||
unsigned
|
||||
to_unsigned (const char *s)
|
||||
{
|
||||
char* endptr;
|
||||
unsigned res = strtoul(s, &endptr, 10);
|
||||
if (*endptr)
|
||||
error(2, 0, "failed to parse '%s' as an unsigned integer.", s);
|
||||
return res;
|
||||
}
|
||||
|
||||
float
|
||||
to_float(const char* s)
|
||||
{
|
||||
char* endptr;
|
||||
float res = strtof(s, &endptr);
|
||||
if (*endptr)
|
||||
error(2, 0, "failed to parse '%s' as a float.", s);
|
||||
return res;
|
||||
}
|
||||
|
||||
float
|
||||
to_probability(const char* s)
|
||||
{
|
||||
float res = to_float(s);
|
||||
if (res < 0.0 || res > 1.0)
|
||||
error(2, 0, "%f is not between 0 and 1.", res);
|
||||
return res;
|
||||
}
|
||||
|
||||
std::vector<long>
|
||||
to_longs(const char* arg)
|
||||
{
|
||||
std::vector<long> res;
|
||||
while (*arg)
|
||||
{
|
||||
char* endptr;
|
||||
long value = strtol(arg, &endptr, 10);
|
||||
if (endptr == arg)
|
||||
error(2, 0, "failed to parse '%s' as an integer.", arg);
|
||||
res.push_back(value);
|
||||
while (*endptr == ' ' || *endptr == ',')
|
||||
++endptr;
|
||||
arg = endptr;
|
||||
}
|
||||
return res;
|
||||
}
|
||||
32
spot/bin/common_conv.hh
Normal file
32
spot/bin/common_conv.hh
Normal file
|
|
@ -0,0 +1,32 @@
|
|||
// -*- coding: utf-8 -*-
|
||||
// Copyright (C) 2015 Laboratoire de Recherche et Développement de
|
||||
// l'Epita (LRDE).
|
||||
//
|
||||
// This file is part of Spot, a model checking library.
|
||||
//
|
||||
// Spot is free software; you can redistribute it and/or modify it
|
||||
// under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation; either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// Spot is distributed in the hope that it will be useful, but WITHOUT
|
||||
// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
||||
// or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public
|
||||
// License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "common_sys.hh"
|
||||
#include <spot/twa/twagraph.hh>
|
||||
|
||||
int to_int(const char* s);
|
||||
int to_pos_int(const char* s);
|
||||
unsigned to_unsigned (const char *s);
|
||||
float to_float(const char* s);
|
||||
float to_probability(const char* s);
|
||||
|
||||
// Parse the comma or space seperate string of numbers.
|
||||
std::vector<long> to_longs(const char* s);
|
||||
37
spot/bin/common_cout.cc
Normal file
37
spot/bin/common_cout.cc
Normal file
|
|
@ -0,0 +1,37 @@
|
|||
// -*- coding: utf-8 -*-
|
||||
// Copyright (C) 2012 Laboratoire de Recherche et Développement de
|
||||
// l'Epita (LRDE).
|
||||
//
|
||||
// This file is part of Spot, a model checking library.
|
||||
//
|
||||
// Spot is free software; you can redistribute it and/or modify it
|
||||
// under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation; either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// Spot is distributed in the hope that it will be useful, but WITHOUT
|
||||
// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
||||
// or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public
|
||||
// License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
#include "common_sys.hh"
|
||||
#include "common_cout.hh"
|
||||
#include <iostream>
|
||||
#include "error.h"
|
||||
|
||||
void check_cout()
|
||||
{
|
||||
// Make sure we abort if we can't write to std::cout anymore
|
||||
// (like disk full or broken pipe with SIGPIPE ignored).
|
||||
if (!std::cout)
|
||||
error(2, 0, "error writing to standard output");
|
||||
}
|
||||
|
||||
void flush_cout()
|
||||
{
|
||||
std::cout.flush();
|
||||
check_cout();
|
||||
}
|
||||
23
spot/bin/common_cout.hh
Normal file
23
spot/bin/common_cout.hh
Normal file
|
|
@ -0,0 +1,23 @@
|
|||
// -*- coding: utf-8 -*-
|
||||
// Copyright (C) 2012 Laboratoire de Recherche et Développement de
|
||||
// l'Epita (LRDE).
|
||||
//
|
||||
// This file is part of Spot, a model checking library.
|
||||
//
|
||||
// Spot is free software; you can redistribute it and/or modify it
|
||||
// under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation; either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// Spot is distributed in the hope that it will be useful, but WITHOUT
|
||||
// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
||||
// or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public
|
||||
// License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
#pragma once
|
||||
|
||||
void check_cout();
|
||||
void flush_cout();
|
||||
43
spot/bin/common_file.cc
Normal file
43
spot/bin/common_file.cc
Normal file
|
|
@ -0,0 +1,43 @@
|
|||
// -*- coding: utf-8 -*-
|
||||
// Copyright (C) 2015 Laboratoire de Recherche et Développement de
|
||||
// l'Epita (LRDE).
|
||||
//
|
||||
// This file is part of Spot, a model checking library.
|
||||
//
|
||||
// Spot is free software; you can redistribute it and/or modify it
|
||||
// under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation; either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// Spot is distributed in the hope that it will be useful, but WITHOUT
|
||||
// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
||||
// or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public
|
||||
// License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
#include "common_file.hh"
|
||||
#include <error.h>
|
||||
#include <iostream>
|
||||
|
||||
|
||||
output_file::output_file(const char* name)
|
||||
{
|
||||
std::ios_base::openmode mode = std::ios_base::trunc;
|
||||
if (name[0] == '>' && name[1] == '>')
|
||||
{
|
||||
mode = std::ios_base::app;
|
||||
append_ = true;
|
||||
name += 2;
|
||||
}
|
||||
if (name[0] == '-' && name[1] == 0)
|
||||
{
|
||||
os_ = &std::cout;
|
||||
return;
|
||||
}
|
||||
of_ = new std::ofstream(name, mode);
|
||||
if (!*of_)
|
||||
error(2, errno, "cannot open '%s'", name);
|
||||
os_ = of_;
|
||||
}
|
||||
51
spot/bin/common_file.hh
Normal file
51
spot/bin/common_file.hh
Normal file
|
|
@ -0,0 +1,51 @@
|
|||
// -*- coding: utf-8 -*-
|
||||
// Copyright (C) 2015 Laboratoire de Recherche et Développement de
|
||||
// l'Epita (LRDE).
|
||||
//
|
||||
// This file is part of Spot, a model checking library.
|
||||
//
|
||||
// Spot is free software; you can redistribute it and/or modify it
|
||||
// under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation; either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// Spot is distributed in the hope that it will be useful, but WITHOUT
|
||||
// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
||||
// or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public
|
||||
// License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "common_sys.hh"
|
||||
#include <iosfwd>
|
||||
#include <fstream>
|
||||
|
||||
class output_file
|
||||
{
|
||||
std::ostream* os_;
|
||||
std::ofstream* of_ = nullptr;
|
||||
bool append_ = false;
|
||||
public:
|
||||
// Open a file for output. "-" is interpreted as stdout.
|
||||
// Names that start with ">>" are opened for append.
|
||||
// The function calls error() on... error.
|
||||
output_file(const char* name);
|
||||
|
||||
~output_file()
|
||||
{
|
||||
delete of_;
|
||||
}
|
||||
|
||||
bool append() const
|
||||
{
|
||||
return append_;
|
||||
}
|
||||
|
||||
std::ostream& ostream()
|
||||
{
|
||||
return *os_;
|
||||
}
|
||||
};
|
||||
356
spot/bin/common_finput.cc
Normal file
356
spot/bin/common_finput.cc
Normal file
|
|
@ -0,0 +1,356 @@
|
|||
// -*- coding: utf-8 -*-
|
||||
// Copyright (C) 2012, 2013, 2014, 2015 Laboratoire de Recherche et
|
||||
// Développement de l'Epita (LRDE).
|
||||
//
|
||||
// This file is part of Spot, a model checking library.
|
||||
//
|
||||
// Spot is free software; you can redistribute it and/or modify it
|
||||
// under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation; either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// Spot is distributed in the hope that it will be useful, but WITHOUT
|
||||
// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
||||
// or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public
|
||||
// License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
#include "common_finput.hh"
|
||||
#include "error.h"
|
||||
|
||||
#include <fstream>
|
||||
#include <cstring>
|
||||
|
||||
enum {
|
||||
OPT_LBT = 1,
|
||||
OPT_LENIENT,
|
||||
};
|
||||
|
||||
jobs_t jobs;
|
||||
bool lbt_input = false;
|
||||
static bool lenient = false;
|
||||
|
||||
static const argp_option options[] =
|
||||
{
|
||||
{ nullptr, 0, nullptr, 0, "Input options:", 1 },
|
||||
{ "formula", 'f', "STRING", 0, "process the formula STRING", 0 },
|
||||
{ "file", 'F', "FILENAME[/COL]", 0,
|
||||
"process each line of FILENAME as a formula; if COL is a "
|
||||
"positive integer, assume a CSV file and read column COL; use "
|
||||
"a negative COL to drop the first line of the CSV file", 0 },
|
||||
{ "lbt-input", OPT_LBT, nullptr, 0,
|
||||
"read all formulas using LBT's prefix syntax", 0 },
|
||||
{ "lenient", OPT_LENIENT, nullptr, 0,
|
||||
"parenthesized blocks that cannot be parsed as subformulas "
|
||||
"are considered as atomic properties", 0 },
|
||||
{ nullptr, 0, nullptr, 0, nullptr, 0 }
|
||||
};
|
||||
|
||||
const struct argp finput_argp = { options, parse_opt_finput,
|
||||
nullptr, nullptr, nullptr,
|
||||
nullptr, nullptr };
|
||||
|
||||
int
|
||||
parse_opt_finput(int key, char* arg, struct argp_state*)
|
||||
{
|
||||
// This switch is alphabetically-ordered.
|
||||
switch (key)
|
||||
{
|
||||
case 'f':
|
||||
jobs.emplace_back(arg, false);
|
||||
break;
|
||||
case 'F':
|
||||
jobs.emplace_back(arg, true);
|
||||
break;
|
||||
case OPT_LBT:
|
||||
lbt_input = true;
|
||||
break;
|
||||
case OPT_LENIENT:
|
||||
lenient = true;
|
||||
break;
|
||||
default:
|
||||
return ARGP_ERR_UNKNOWN;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
spot::formula
|
||||
parse_formula(const std::string& s, spot::parse_error_list& pel)
|
||||
{
|
||||
if (lbt_input)
|
||||
return spot::parse_prefix_ltl(s, pel);
|
||||
else
|
||||
return spot::parse_infix_psl
|
||||
(s, pel, spot::default_environment::instance(), false, lenient);
|
||||
}
|
||||
|
||||
job_processor::job_processor()
|
||||
: abort_run(false), real_filename(nullptr),
|
||||
col_to_read(0), prefix(nullptr), suffix(nullptr)
|
||||
{
|
||||
}
|
||||
|
||||
job_processor::~job_processor()
|
||||
{
|
||||
if (real_filename)
|
||||
free(real_filename);
|
||||
if (prefix)
|
||||
free(prefix);
|
||||
if (suffix)
|
||||
free(suffix);
|
||||
}
|
||||
|
||||
|
||||
int
|
||||
job_processor::process_string(const std::string& input,
|
||||
const char* filename,
|
||||
int linenum)
|
||||
{
|
||||
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::format_parse_errors(std::cerr, input, pel);
|
||||
return 1;
|
||||
}
|
||||
return process_formula(f, filename, linenum);
|
||||
}
|
||||
|
||||
int
|
||||
job_processor::process_stream(std::istream& is,
|
||||
const char* filename)
|
||||
{
|
||||
int error = 0;
|
||||
int linenum = 1;
|
||||
std::string line;
|
||||
|
||||
// Discard the first line of a CSV file if requested.
|
||||
if (col_to_read < 0)
|
||||
{
|
||||
std::getline(is, line);
|
||||
col_to_read = -col_to_read;
|
||||
++linenum;
|
||||
}
|
||||
|
||||
// Each line of the file and send them to process_string,
|
||||
// optionally extracting a column of a CSV file.
|
||||
while (!abort_run && std::getline(is, line))
|
||||
if (!line.empty())
|
||||
{
|
||||
if (col_to_read == 0)
|
||||
{
|
||||
error |= process_string(line, filename, linenum++);
|
||||
}
|
||||
else // We are reading column COL_TO_READ in a CSV file.
|
||||
{
|
||||
// If the line we have read contains an odd number
|
||||
// of double-quotes, then it is an incomplete CSV line
|
||||
// that should be completed by the next lines.
|
||||
unsigned dquotes = 0;
|
||||
std::string fullline;
|
||||
unsigned csvlines = 0;
|
||||
do
|
||||
{
|
||||
++csvlines;
|
||||
size_t s = line.size();
|
||||
for (unsigned i = 0; i < s; ++i)
|
||||
dquotes += line[i] == '"';
|
||||
if (fullline.empty())
|
||||
fullline = line;
|
||||
else
|
||||
(fullline += '\n') += line;
|
||||
if (!(dquotes &= 1))
|
||||
break;
|
||||
}
|
||||
while (std::getline(is, line));
|
||||
if (dquotes)
|
||||
error_at_line(2, errno, filename, linenum,
|
||||
"mismatched double-quote, "
|
||||
"reached EOF while parsing this line");
|
||||
|
||||
// Now that we have a full CSV line, extract the right
|
||||
// column.
|
||||
|
||||
const char* str = fullline.c_str();
|
||||
const char* col1_start = str;
|
||||
// Delimiters for the extracted column.
|
||||
const char* coln_start = str;
|
||||
const char* coln_end = nullptr;
|
||||
// The current column. (1-based)
|
||||
int colnum = 1;
|
||||
// Whether we are parsing a double-quoted string.
|
||||
bool instring = false;
|
||||
// Note that RFC 4180 has strict rules about
|
||||
// double-quotes: ① if a field is double-quoted, the first
|
||||
// and last characters of the field should be
|
||||
// double-quotes; ② if a field contains a double-quote
|
||||
// then it should be double quoted, and the occurrences
|
||||
// of double-quotes should be doubled. Therefore a CSV file
|
||||
// may no contain a line such as:
|
||||
// foo,bar"ba""z",12
|
||||
// Tools have different interpretation of such a line.
|
||||
// For instance Python's pandas.read_csv() function will
|
||||
// load the second field verbatim as the string 'bar"ba""z"',
|
||||
// while R's read.csv() function will load it as the
|
||||
// string 'barba"z'. We use this second interpretation, because
|
||||
// it also makes it possible to parse CSVs fields formatted
|
||||
// with leading spaces (often for cosmetic purpose). When
|
||||
// extracting the second field of
|
||||
// foo, "ba""z", 12
|
||||
// we will return ' baz' and the leading space will be ignored
|
||||
// by our LTL formula parser.
|
||||
while (*str)
|
||||
{
|
||||
switch (*str)
|
||||
{
|
||||
case '"':
|
||||
// Doubled double-quotes are used to escape
|
||||
// double-quotes.
|
||||
if (instring && str[1] == '"')
|
||||
++str;
|
||||
else
|
||||
instring = !instring;
|
||||
break;
|
||||
case ',':
|
||||
if (!instring)
|
||||
{
|
||||
if (col_to_read == colnum)
|
||||
coln_end = str;
|
||||
++colnum;
|
||||
if (col_to_read == colnum)
|
||||
coln_start = str + 1;
|
||||
}
|
||||
break;
|
||||
}
|
||||
// Once we have the end delimiter for our target
|
||||
// column, we have all we need.
|
||||
if (coln_end)
|
||||
break;
|
||||
++str;
|
||||
}
|
||||
if (!*str)
|
||||
{
|
||||
if (colnum != col_to_read)
|
||||
// Skip this line as it has no enough columns.
|
||||
continue;
|
||||
else
|
||||
// The target columns ends at the end of the line.
|
||||
coln_end = str;
|
||||
}
|
||||
|
||||
// Skip the line if it has an empty field.
|
||||
if (coln_start == coln_end)
|
||||
continue;
|
||||
|
||||
// save the contents before and after that columns for the
|
||||
// %< and %> escapes (ignoring the trailing and leading
|
||||
// commas).
|
||||
prefix = (col_to_read != 1) ?
|
||||
strndup(col1_start, coln_start - col1_start - 1) : nullptr;
|
||||
suffix = (*coln_end != 0) ? strdup(coln_end + 1) : nullptr;
|
||||
std::string field(coln_start, coln_end);
|
||||
// Remove double-quotes if any.
|
||||
if (field.find('"') != std::string::npos)
|
||||
{
|
||||
unsigned dst = 0;
|
||||
bool instring = false;
|
||||
for (; coln_start != coln_end; ++coln_start)
|
||||
if (*coln_start == '"')
|
||||
// A doubled double-quote instead a double-quoted
|
||||
// string is an escaped double-quote.
|
||||
if (instring && coln_start[1] == '"')
|
||||
field[dst++] = *++coln_start;
|
||||
else
|
||||
instring = !instring;
|
||||
else
|
||||
field[dst++] = *coln_start;
|
||||
field.resize(dst);
|
||||
}
|
||||
error |= process_string(field, filename, linenum);
|
||||
linenum += csvlines;
|
||||
if (prefix)
|
||||
{
|
||||
free(prefix);
|
||||
prefix = nullptr;
|
||||
}
|
||||
if (suffix)
|
||||
{
|
||||
free(suffix);
|
||||
suffix = nullptr;
|
||||
}
|
||||
}
|
||||
}
|
||||
return error;
|
||||
}
|
||||
|
||||
int
|
||||
job_processor::process_file(const char* filename)
|
||||
{
|
||||
// Special case for stdin.
|
||||
if (filename[0] == '-' && filename[1] == 0)
|
||||
return process_stream(std::cin, filename);
|
||||
|
||||
errno = 0;
|
||||
std::ifstream input(filename);
|
||||
if (input)
|
||||
return process_stream(input, filename);
|
||||
int saved_errno = errno;
|
||||
|
||||
// If we have a filename like "foo/NN" such
|
||||
// that:
|
||||
// ① foo/NN is not a file (already the case),
|
||||
// ② NN is a number > 0,
|
||||
// ③ foo is a file,
|
||||
// then it means we want to open foo as
|
||||
// a CSV file and process column NN.
|
||||
|
||||
if (const char* slash = strrchr(filename, '/'))
|
||||
{
|
||||
char* end;
|
||||
errno = 0;
|
||||
long int col = strtol(slash + 1, &end, 10);
|
||||
// strtol ate all remaining characters and NN is positive
|
||||
if (errno == 0 && !*end && col != 0)
|
||||
{
|
||||
col_to_read = col;
|
||||
if (real_filename)
|
||||
free(real_filename);
|
||||
real_filename = strndup(filename, slash - filename);
|
||||
|
||||
// Special case for stdin.
|
||||
if (real_filename[0] == '-' && real_filename[1] == 0)
|
||||
return process_stream(std::cin, real_filename);
|
||||
|
||||
std::ifstream input(real_filename);
|
||||
if (input)
|
||||
return process_stream(input, real_filename);
|
||||
|
||||
error(2, errno, "cannot open '%s' nor '%s'",
|
||||
filename, real_filename);
|
||||
}
|
||||
}
|
||||
|
||||
error(2, saved_errno, "cannot open '%s'", filename);
|
||||
return -1;
|
||||
}
|
||||
|
||||
int
|
||||
job_processor::run()
|
||||
{
|
||||
int error = 0;
|
||||
for (auto& j: jobs)
|
||||
{
|
||||
if (!j.file_p)
|
||||
error |= process_string(j.str);
|
||||
else
|
||||
error |= process_file(j.str);
|
||||
if (abort_run)
|
||||
break;
|
||||
}
|
||||
return error;
|
||||
}
|
||||
80
spot/bin/common_finput.hh
Normal file
80
spot/bin/common_finput.hh
Normal file
|
|
@ -0,0 +1,80 @@
|
|||
// -*- coding: utf-8 -*-
|
||||
// Copyright (C) 2012, 2013, 2015 Laboratoire de Recherche et
|
||||
// Développement de l'Epita (LRDE).
|
||||
//
|
||||
// This file is part of Spot, a model checking library.
|
||||
//
|
||||
// Spot is free software; you can redistribute it and/or modify it
|
||||
// under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation; either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// Spot is distributed in the hope that it will be useful, but WITHOUT
|
||||
// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
||||
// or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public
|
||||
// License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "common_sys.hh"
|
||||
|
||||
#include <argp.h>
|
||||
#include <vector>
|
||||
#include <spot/tl/parse.hh>
|
||||
|
||||
struct job
|
||||
{
|
||||
const char* str;
|
||||
bool file_p; // true if str is a filename, false if it is a formula
|
||||
|
||||
job(const char* str, bool file_p)
|
||||
: str(str), file_p(file_p)
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
typedef std::vector<job> jobs_t;
|
||||
extern jobs_t jobs;
|
||||
extern bool lbt_input;
|
||||
|
||||
extern const struct argp finput_argp;
|
||||
|
||||
int parse_opt_finput(int key, char* arg, struct argp_state* state);
|
||||
|
||||
spot::formula
|
||||
parse_formula(const std::string& s, spot::parse_error_list& error_list);
|
||||
|
||||
|
||||
class job_processor
|
||||
{
|
||||
protected:
|
||||
bool abort_run; // Set to true in process_formula() to abort run().
|
||||
public:
|
||||
job_processor();
|
||||
|
||||
virtual ~job_processor();
|
||||
|
||||
virtual int
|
||||
process_formula(spot::formula f,
|
||||
const char* filename = nullptr, int linenum = 0) = 0;
|
||||
|
||||
virtual int
|
||||
process_string(const std::string& str,
|
||||
const char* filename = nullptr, int linenum = 0);
|
||||
virtual int
|
||||
process_stream(std::istream& is, const char* filename);
|
||||
|
||||
virtual int
|
||||
process_file(const char* filename);
|
||||
|
||||
virtual int
|
||||
run();
|
||||
|
||||
char* real_filename;
|
||||
long int col_to_read;
|
||||
char* prefix;
|
||||
char* suffix;
|
||||
};
|
||||
90
spot/bin/common_hoaread.cc
Normal file
90
spot/bin/common_hoaread.cc
Normal file
|
|
@ -0,0 +1,90 @@
|
|||
// -*- coding: utf-8 -*-
|
||||
// Copyright (C) 2015 Laboratoire de Recherche et Développement de
|
||||
// l'Epita (LRDE).
|
||||
//
|
||||
// This file is part of Spot, a model checking library.
|
||||
//
|
||||
// Spot is free software; you can redistribute it and/or modify it
|
||||
// under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation; either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// Spot is distributed in the hope that it will be useful, but WITHOUT
|
||||
// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
||||
// or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public
|
||||
// License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
#include "common_hoaread.hh"
|
||||
#include "argmatch.h"
|
||||
#include "error.h"
|
||||
|
||||
enum
|
||||
{
|
||||
OPT_TRUST_HOA = 1,
|
||||
};
|
||||
|
||||
static const argp_option options[] =
|
||||
{
|
||||
{ "trust-hoa", OPT_TRUST_HOA, "BOOL", 0,
|
||||
"If False, properties listed in HOA files are ignored, "
|
||||
"unless they can be easily verified. If True (the default) "
|
||||
"any supported property is trusted.", 1 },
|
||||
{ nullptr, 0, nullptr, 0, nullptr, 0 }
|
||||
};
|
||||
|
||||
spot::automaton_parser_options opt_parse;
|
||||
|
||||
spot::twa_graph_ptr
|
||||
read_automaton(const char* filename, spot::bdd_dict_ptr& dict)
|
||||
{
|
||||
auto p = spot::parse_aut(filename, dict,
|
||||
spot::default_environment::instance(),
|
||||
opt_parse);
|
||||
if (p->format_errors(std::cerr))
|
||||
error(2, 0, "failed to read automaton from %s", filename);
|
||||
if (p->aborted)
|
||||
error(2, 0, "failed to read automaton from %s (--ABORT-- read)", filename);
|
||||
return std::move(p->aut);
|
||||
}
|
||||
|
||||
static bool parse_bool(const char* opt, const char* arg)
|
||||
{
|
||||
enum bool_type { bool_false, bool_true };
|
||||
static char const *const bool_args[] =
|
||||
{
|
||||
"false", "no", "0",
|
||||
"true", "yes", "1",
|
||||
nullptr
|
||||
};
|
||||
static bool_type const bool_types[] =
|
||||
{
|
||||
bool_false, bool_false, bool_false,
|
||||
bool_true, bool_true, bool_true,
|
||||
};
|
||||
ARGMATCH_VERIFY(bool_args, bool_types);
|
||||
bool_type bt = XARGMATCH(opt, arg, bool_args, bool_types);
|
||||
return bt == bool_true;
|
||||
}
|
||||
|
||||
static int
|
||||
parse_opt_hoaread(int key, char* arg, struct argp_state*)
|
||||
{
|
||||
// This switch is alphabetically-ordered.
|
||||
switch (key)
|
||||
{
|
||||
case OPT_TRUST_HOA:
|
||||
opt_parse.trust_hoa = parse_bool("--trust-hoa", arg);
|
||||
break;
|
||||
default:
|
||||
return ARGP_ERR_UNKNOWN;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
const struct argp hoaread_argp = { options, parse_opt_hoaread,
|
||||
nullptr, nullptr, nullptr,
|
||||
nullptr, nullptr };
|
||||
34
spot/bin/common_hoaread.hh
Normal file
34
spot/bin/common_hoaread.hh
Normal file
|
|
@ -0,0 +1,34 @@
|
|||
// -*- coding: utf-8 -*-
|
||||
// Copyright (C) 2015 Laboratoire de Recherche et Développement de
|
||||
// l'Epita (LRDE).
|
||||
//
|
||||
// This file is part of Spot, a model checking library.
|
||||
//
|
||||
// Spot is free software; you can redistribute it and/or modify it
|
||||
// under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation; either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// Spot is distributed in the hope that it will be useful, but WITHOUT
|
||||
// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
||||
// or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public
|
||||
// License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "common_sys.hh"
|
||||
|
||||
#include <argp.h>
|
||||
|
||||
#include <spot/parseaut/public.hh>
|
||||
|
||||
|
||||
extern const struct argp hoaread_argp;
|
||||
|
||||
extern spot::automaton_parser_options opt_parse;
|
||||
|
||||
spot::twa_graph_ptr
|
||||
read_automaton(const char* filename, spot::bdd_dict_ptr& dict);
|
||||
319
spot/bin/common_output.cc
Normal file
319
spot/bin/common_output.cc
Normal file
|
|
@ -0,0 +1,319 @@
|
|||
// -*- coding: utf-8 -*-
|
||||
// Copyright (C) 2012, 2013, 2014, 2015 Laboratoire de Recherche et
|
||||
// Développement de l'Epita (LRDE).
|
||||
//
|
||||
// This file is part of Spot, a model checking library.
|
||||
//
|
||||
// Spot is free software; you can redistribute it and/or modify it
|
||||
// under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation; either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// Spot is distributed in the hope that it will be useful, but WITHOUT
|
||||
// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
||||
// or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public
|
||||
// License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
#include "common_sys.hh"
|
||||
#include "common_output.hh"
|
||||
#include <iostream>
|
||||
#include <sstream>
|
||||
#include <spot/tl/print.hh>
|
||||
#include <spot/misc/formater.hh>
|
||||
#include <spot/misc/escape.hh>
|
||||
#include "common_cout.hh"
|
||||
#include "error.h"
|
||||
|
||||
enum {
|
||||
OPT_CSV = 1,
|
||||
OPT_FORMAT,
|
||||
OPT_LATEX,
|
||||
OPT_SPOT,
|
||||
OPT_WRING,
|
||||
};
|
||||
|
||||
output_format_t output_format = spot_output;
|
||||
bool full_parenth = false;
|
||||
bool escape_csv = false;
|
||||
char output_terminator = '\n';
|
||||
|
||||
static const argp_option options[] =
|
||||
{
|
||||
{ "full-parentheses", 'p', nullptr, 0,
|
||||
"output fully-parenthesized formulas", -20 },
|
||||
{ "spin", 's', nullptr, 0, "output in Spin's syntax", -20 },
|
||||
{ "spot", OPT_SPOT, nullptr, 0, "output in Spot's syntax (default)", -20 },
|
||||
{ "lbt", 'l', nullptr, 0, "output in LBT's syntax", -20 },
|
||||
{ "wring", OPT_WRING, nullptr, 0, "output in Wring's syntax", -20 },
|
||||
{ "utf8", '8', nullptr, 0, "output using UTF-8 characters", -20 },
|
||||
{ "latex", OPT_LATEX, nullptr, 0, "output using LaTeX macros", -20 },
|
||||
{ "csv-escape", OPT_CSV, nullptr, 0,
|
||||
"quote the formula for use in a CSV file", -20 },
|
||||
{ "format", OPT_FORMAT, "FORMAT", 0,
|
||||
"specify how each line should be output (default: \"%f\")", -20 },
|
||||
{ "output", 'o', "FORMAT", 0,
|
||||
"send output to a file named FORMAT instead of standard output. The"
|
||||
" first formula sent to a file truncates it unless FORMAT starts"
|
||||
" with '>>'.", 0 },
|
||||
{ "zero-terminated-output", '0', nullptr, 0,
|
||||
"separate output formulas with \\0 instead of \\n "
|
||||
"(for use with xargs -0)", 0 },
|
||||
{ nullptr, 0, nullptr, 0, nullptr, 0 }
|
||||
};
|
||||
|
||||
const struct argp output_argp = { options, parse_opt_output,
|
||||
nullptr, nullptr, nullptr,
|
||||
nullptr, nullptr };
|
||||
|
||||
static
|
||||
void
|
||||
report_not_ltl(spot::formula f,
|
||||
const char* filename, int linenum, const char* syn)
|
||||
{
|
||||
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)
|
||||
error_at_line(2, 0, filename, linenum, msg, s.c_str(), syn);
|
||||
else
|
||||
error(2, 0, msg, s.c_str(), syn);
|
||||
}
|
||||
|
||||
std::ostream&
|
||||
stream_formula(std::ostream& out,
|
||||
spot::formula f, const char* filename, int linenum)
|
||||
{
|
||||
switch (output_format)
|
||||
{
|
||||
case lbt_output:
|
||||
if (f.is_ltl_formula())
|
||||
spot::print_lbt_ltl(out, f);
|
||||
else
|
||||
report_not_ltl(f, filename, linenum, "LBT");
|
||||
break;
|
||||
case spot_output:
|
||||
spot::print_psl(out, f, full_parenth);
|
||||
break;
|
||||
case spin_output:
|
||||
if (f.is_ltl_formula())
|
||||
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::print_wring_ltl(out, f);
|
||||
else
|
||||
report_not_ltl(f, filename, linenum, "Wring");
|
||||
break;
|
||||
case utf8_output:
|
||||
spot::print_utf8_psl(out, f, full_parenth);
|
||||
break;
|
||||
case latex_output:
|
||||
spot::print_latex_psl(out, f, full_parenth);
|
||||
break;
|
||||
case count_output:
|
||||
case quiet_output:
|
||||
break;
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
static void
|
||||
stream_escapable_formula(std::ostream& os,
|
||||
spot::formula f,
|
||||
const char* filename, int linenum)
|
||||
{
|
||||
if (escape_csv)
|
||||
{
|
||||
std::ostringstream out;
|
||||
stream_formula(out, f, filename, linenum);
|
||||
os << '"';
|
||||
spot::escape_rfc4180(os, out.str());
|
||||
os << '"';
|
||||
}
|
||||
else
|
||||
{
|
||||
stream_formula(os, f, filename, linenum);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
namespace
|
||||
{
|
||||
struct formula_with_location
|
||||
{
|
||||
spot::formula f;
|
||||
const char* filename;
|
||||
int line;
|
||||
const char* prefix;
|
||||
const char* suffix;
|
||||
};
|
||||
|
||||
class printable_formula:
|
||||
public spot::printable_value<const formula_with_location*>
|
||||
{
|
||||
public:
|
||||
printable_formula&
|
||||
operator=(const formula_with_location* new_val)
|
||||
{
|
||||
val_ = new_val;
|
||||
return *this;
|
||||
}
|
||||
|
||||
virtual void
|
||||
print(std::ostream& os, const char*) const
|
||||
{
|
||||
stream_escapable_formula(os, val_->f, val_->filename, val_->line);
|
||||
}
|
||||
};
|
||||
|
||||
class formula_printer: protected spot::formater
|
||||
{
|
||||
public:
|
||||
formula_printer(std::ostream& os, const char* format)
|
||||
: format_(format)
|
||||
{
|
||||
declare('f', &fl_);
|
||||
declare('F', &filename_);
|
||||
declare('L', &line_);
|
||||
declare('<', &prefix_);
|
||||
declare('>', &suffix_);
|
||||
set_output(os);
|
||||
}
|
||||
|
||||
std::ostream&
|
||||
print(const formula_with_location& fl)
|
||||
{
|
||||
fl_ = &fl;
|
||||
filename_ = fl.filename ? fl.filename : "";
|
||||
line_ = fl.line;
|
||||
prefix_ = fl.prefix ? fl.prefix : "";
|
||||
suffix_ = fl.suffix ? fl.suffix : "";
|
||||
return format(format_);
|
||||
}
|
||||
|
||||
private:
|
||||
const char* format_;
|
||||
printable_formula fl_;
|
||||
spot::printable_value<const char*> filename_;
|
||||
spot::printable_value<int> line_;
|
||||
spot::printable_value<const char*> prefix_;
|
||||
spot::printable_value<const char*> suffix_;
|
||||
};
|
||||
}
|
||||
|
||||
static formula_printer* format = nullptr;
|
||||
static std::ostringstream outputname;
|
||||
static formula_printer* outputnamer = nullptr;
|
||||
static std::map<std::string, std::unique_ptr<output_file>> outputfiles;
|
||||
|
||||
int
|
||||
parse_opt_output(int key, char* arg, struct argp_state*)
|
||||
{
|
||||
// This switch is alphabetically-ordered.
|
||||
switch (key)
|
||||
{
|
||||
case '0':
|
||||
output_terminator = 0;
|
||||
break;
|
||||
case '8':
|
||||
output_format = utf8_output;
|
||||
break;
|
||||
case 'l':
|
||||
output_format = lbt_output;
|
||||
break;
|
||||
case 'o':
|
||||
outputnamer = new formula_printer(outputname, arg);
|
||||
break;
|
||||
case 'p':
|
||||
full_parenth = true;
|
||||
break;
|
||||
case 's':
|
||||
output_format = spin_output;
|
||||
break;
|
||||
case OPT_CSV:
|
||||
escape_csv = true;
|
||||
break;
|
||||
case OPT_LATEX:
|
||||
output_format = latex_output;
|
||||
break;
|
||||
case OPT_SPOT:
|
||||
output_format = spot_output;
|
||||
break;
|
||||
case OPT_WRING:
|
||||
output_format = wring_output;
|
||||
break;
|
||||
case OPT_FORMAT:
|
||||
delete format;
|
||||
format = new formula_printer(std::cout, arg);
|
||||
break;
|
||||
default:
|
||||
return ARGP_ERR_UNKNOWN;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
output_formula(std::ostream& out,
|
||||
spot::formula f,
|
||||
const char* filename = nullptr, int linenum = 0,
|
||||
const char* prefix = nullptr, const char* suffix = nullptr)
|
||||
{
|
||||
if (!format)
|
||||
{
|
||||
if (prefix)
|
||||
out << prefix << ',';
|
||||
stream_escapable_formula(out, f, filename, linenum);
|
||||
if (suffix)
|
||||
out << ',' << suffix;
|
||||
}
|
||||
else
|
||||
{
|
||||
formula_with_location fl = { f, filename, linenum, prefix, suffix };
|
||||
format->print(fl);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
::printable_formula::print(std::ostream& os, const char*) const
|
||||
{
|
||||
output_formula(os, val_);
|
||||
}
|
||||
|
||||
void
|
||||
output_formula_checked(spot::formula f,
|
||||
const char* filename, int linenum,
|
||||
const char* prefix, const char* suffix)
|
||||
{
|
||||
if (output_format == count_output)
|
||||
{
|
||||
if (outputnamer)
|
||||
throw std::runtime_error
|
||||
("options --output and --count are incompatible");
|
||||
return;
|
||||
}
|
||||
if (output_format == quiet_output)
|
||||
return;
|
||||
std::ostream* out = &std::cout;
|
||||
if (outputnamer)
|
||||
{
|
||||
outputname.str("");
|
||||
formula_with_location fl = { f, filename, linenum, prefix, suffix };
|
||||
outputnamer->print(fl);
|
||||
std::string fname = outputname.str();
|
||||
auto p = outputfiles.emplace(fname, nullptr);
|
||||
if (p.second)
|
||||
p.first->second.reset(new output_file(fname.c_str()));
|
||||
out = &p.first->second->ostream();
|
||||
}
|
||||
output_formula(*out, f, filename, linenum, prefix, suffix);
|
||||
*out << output_terminator;
|
||||
// Make sure we abort if we can't write to std::cout anymore
|
||||
// (like disk full or broken pipe with SIGPIPE ignored).
|
||||
check_cout();
|
||||
}
|
||||
90
spot/bin/common_output.hh
Normal file
90
spot/bin/common_output.hh
Normal file
|
|
@ -0,0 +1,90 @@
|
|||
// -*- coding: utf-8 -*-
|
||||
// Copyright (C) 2012, 2013, 2014, 2015 Laboratoire de Recherche et
|
||||
// Développement de l'Epita (LRDE).
|
||||
//
|
||||
// This file is part of Spot, a model checking library.
|
||||
//
|
||||
// Spot is free software; you can redistribute it and/or modify it
|
||||
// under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation; either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// Spot is distributed in the hope that it will be useful, but WITHOUT
|
||||
// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
||||
// or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public
|
||||
// License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "common_sys.hh"
|
||||
|
||||
#include <argp.h>
|
||||
#include <map>
|
||||
#include <memory>
|
||||
#include <spot/tl/formula.hh>
|
||||
#include <spot/twaalgos/stats.hh>
|
||||
#include "common_output.hh"
|
||||
#include "common_file.hh"
|
||||
|
||||
enum output_format_t { spot_output, spin_output, utf8_output,
|
||||
lbt_output, wring_output, latex_output,
|
||||
quiet_output, count_output };
|
||||
extern output_format_t output_format;
|
||||
extern bool full_parenth;
|
||||
extern bool escape_csv;
|
||||
|
||||
extern const struct argp output_argp;
|
||||
|
||||
int parse_opt_output(int key, char* arg, struct argp_state* state);
|
||||
|
||||
// Low-level output
|
||||
std::ostream&
|
||||
stream_formula(std::ostream& out,
|
||||
spot::formula f, const char* filename, int linenum);
|
||||
|
||||
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<spot::formula>
|
||||
{
|
||||
public:
|
||||
printable_formula&
|
||||
operator=(spot::formula new_val)
|
||||
{
|
||||
val_ = new_val;
|
||||
return *this;
|
||||
}
|
||||
|
||||
virtual void
|
||||
print(std::ostream& os, const char*) const;
|
||||
};
|
||||
|
||||
class aut_stat_printer: protected spot::stat_printer
|
||||
{
|
||||
public:
|
||||
aut_stat_printer(std::ostream& os, const char* format)
|
||||
: spot::stat_printer(os, format)
|
||||
{
|
||||
declare('f', &formula_); // Override the formula printer.
|
||||
}
|
||||
|
||||
using spot::formater::set_output;
|
||||
|
||||
std::ostream&
|
||||
print(const spot::const_twa_graph_ptr& aut,
|
||||
spot::formula f = nullptr,
|
||||
double run_time = -1.)
|
||||
{
|
||||
formula_ = f;
|
||||
return this->spot::stat_printer::print(aut, f, run_time);
|
||||
}
|
||||
|
||||
printable_formula formula_;
|
||||
};
|
||||
166
spot/bin/common_post.cc
Normal file
166
spot/bin/common_post.cc
Normal file
|
|
@ -0,0 +1,166 @@
|
|||
// -*- coding: utf-8 -*-
|
||||
// Copyright (C) 2012, 2013, 2014, 2015 Laboratoire de Recherche et
|
||||
// Développement de l'Epita (LRDE).
|
||||
//
|
||||
// This file is part of Spot, a model checking library.
|
||||
//
|
||||
// Spot is free software; you can redistribute it and/or modify it
|
||||
// under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation; either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// Spot is distributed in the hope that it will be useful, but WITHOUT
|
||||
// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
||||
// or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public
|
||||
// License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
#include "common_post.hh"
|
||||
#include "common_r.hh"
|
||||
#include "common_aoutput.hh"
|
||||
#include "error.h"
|
||||
|
||||
spot::postprocessor::output_type type = spot::postprocessor::TGBA;
|
||||
spot::postprocessor::output_pref pref = spot::postprocessor::Small;
|
||||
spot::postprocessor::output_pref comp = spot::postprocessor::Any;
|
||||
spot::postprocessor::output_pref sbacc = spot::postprocessor::Any;
|
||||
spot::postprocessor::optimization_level level = spot::postprocessor::High;
|
||||
|
||||
bool level_set = false;
|
||||
bool pref_set = false;
|
||||
|
||||
enum {
|
||||
OPT_GENERIC = 1,
|
||||
OPT_HIGH,
|
||||
OPT_LOW,
|
||||
OPT_MEDIUM,
|
||||
OPT_SMALL,
|
||||
OPT_TGBA,
|
||||
};
|
||||
|
||||
static const argp_option options[] =
|
||||
{
|
||||
/**************************************************/
|
||||
{ nullptr, 0, nullptr, 0, "Output automaton type:", 2 },
|
||||
{ "tgba", OPT_TGBA, nullptr, 0,
|
||||
"Transition-based Generalized Büchi Automaton (default)", 0 },
|
||||
{ "ba", 'B', nullptr, 0,
|
||||
"Büchi Automaton (implies -S)", 0 },
|
||||
{ "monitor", 'M', nullptr, 0, "Monitor (accepts all finite prefixes "
|
||||
"of the given property)", 0 },
|
||||
{ "complete", 'C', nullptr, 0, "output a complete automaton", 0 },
|
||||
{ "state-based-acceptance", 'S', nullptr, 0,
|
||||
"define the acceptance using states", 0 },
|
||||
{ "sbacc", 0, nullptr, OPTION_ALIAS, nullptr, 0 },
|
||||
/**************************************************/
|
||||
{ nullptr, 0, nullptr, 0, "Simplification goal:", 20 },
|
||||
{ "small", OPT_SMALL, nullptr, 0, "prefer small automata (default)", 0 },
|
||||
{ "deterministic", 'D', nullptr, 0, "prefer deterministic automata", 0 },
|
||||
{ "any", 'a', nullptr, 0, "no preference, do not bother making it small "
|
||||
"or deterministic", 0 },
|
||||
/**************************************************/
|
||||
{ nullptr, 0, nullptr, 0, "Simplification level:", 21 },
|
||||
{ "low", OPT_LOW, nullptr, 0, "minimal optimizations (fast)", 0 },
|
||||
{ "medium", OPT_MEDIUM, nullptr, 0, "moderate optimizations", 0 },
|
||||
{ "high", OPT_HIGH, nullptr, 0,
|
||||
"all available optimizations (slow, default)", 0 },
|
||||
{ nullptr, 0, nullptr, 0, nullptr, 0 }
|
||||
};
|
||||
|
||||
static const argp_option options_disabled[] =
|
||||
{
|
||||
/**************************************************/
|
||||
{ nullptr, 0, nullptr, 0, "Output automaton type:", 2 },
|
||||
{ "generic", OPT_GENERIC, nullptr, 0,
|
||||
"any acceptance is allowed (default)", 0 },
|
||||
{ "tgba", OPT_TGBA, nullptr, 0,
|
||||
"Transition-based Generalized Büchi Automaton", 0 },
|
||||
{ "ba", 'B', nullptr, 0,
|
||||
"Büchi Automaton (with state-based acceptance)", 0 },
|
||||
{ "monitor", 'M', nullptr, 0, "Monitor (accepts all finite prefixes "
|
||||
"of the given property)", 0 },
|
||||
{ "complete", 'C', nullptr, 0, "output a complete automaton", 0 },
|
||||
{ "state-based-acceptance", 'S', nullptr, 0,
|
||||
"define the acceptance using states", 0 },
|
||||
{ "sbacc", 0, nullptr, OPTION_ALIAS, nullptr, 0 },
|
||||
/**************************************************/
|
||||
{ nullptr, 0, nullptr, 0, "Simplification goal:", 20 },
|
||||
{ "small", OPT_SMALL, nullptr, 0, "prefer small automata", 0 },
|
||||
{ "deterministic", 'D', nullptr, 0, "prefer deterministic automata", 0 },
|
||||
{ "any", 'a', nullptr, 0, "no preference, do not bother making it small "
|
||||
"or deterministic", 0 },
|
||||
/**************************************************/
|
||||
{ nullptr, 0, nullptr, 0, "Simplification level:", 21 },
|
||||
{ "low", OPT_LOW, nullptr, 0, "minimal optimizations (fast)", 0 },
|
||||
{ "medium", OPT_MEDIUM, nullptr, 0, "moderate optimizations", 0 },
|
||||
{ "high", OPT_HIGH, nullptr, 0,
|
||||
"all available optimizations (slow)", 0 },
|
||||
{ nullptr, 0, nullptr, 0, nullptr, 0 }
|
||||
};
|
||||
|
||||
static int
|
||||
parse_opt_post(int key, char*, struct argp_state*)
|
||||
{
|
||||
// This switch is alphabetically-ordered.
|
||||
switch (key)
|
||||
{
|
||||
case 'a':
|
||||
pref = spot::postprocessor::Any;
|
||||
pref_set = true;
|
||||
break;
|
||||
case 'B':
|
||||
type = spot::postprocessor::BA;
|
||||
break;
|
||||
case 'C':
|
||||
comp = spot::postprocessor::Complete;
|
||||
break;
|
||||
case 'D':
|
||||
pref = spot::postprocessor::Deterministic;
|
||||
pref_set = true;
|
||||
break;
|
||||
case 'M':
|
||||
type = spot::postprocessor::Monitor;
|
||||
break;
|
||||
case 'S':
|
||||
sbacc = spot::postprocessor::SBAcc;
|
||||
break;
|
||||
case OPT_GENERIC:
|
||||
type = spot::postprocessor::Generic;
|
||||
break;
|
||||
case OPT_HIGH:
|
||||
level = spot::postprocessor::High;
|
||||
simplification_level = 3;
|
||||
level_set = true;
|
||||
break;
|
||||
case OPT_LOW:
|
||||
level = spot::postprocessor::Low;
|
||||
simplification_level = 1;
|
||||
level_set = true;
|
||||
break;
|
||||
case OPT_MEDIUM:
|
||||
level = spot::postprocessor::Medium;
|
||||
simplification_level = 2;
|
||||
level_set = true;
|
||||
break;
|
||||
case OPT_SMALL:
|
||||
pref = spot::postprocessor::Small;
|
||||
pref_set = true;
|
||||
break;
|
||||
case OPT_TGBA:
|
||||
if (automaton_format == Spin)
|
||||
error(2, 0, "--spin and --tgba are incompatible");
|
||||
type = spot::postprocessor::TGBA;
|
||||
break;
|
||||
default:
|
||||
return ARGP_ERR_UNKNOWN;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
const struct argp post_argp = { options, parse_opt_post,
|
||||
nullptr, nullptr, nullptr, nullptr, nullptr };
|
||||
const struct argp post_argp_disabled = { options_disabled, parse_opt_post,
|
||||
nullptr, nullptr, nullptr,
|
||||
nullptr, nullptr };
|
||||
37
spot/bin/common_post.hh
Normal file
37
spot/bin/common_post.hh
Normal file
|
|
@ -0,0 +1,37 @@
|
|||
// -*- coding: utf-8 -*-
|
||||
// Copyright (C) 2012, 2013, 2014, 2015 Laboratoire de Recherche et
|
||||
// Développement de l'Epita (LRDE).
|
||||
//
|
||||
// This file is part of Spot, a model checking library.
|
||||
//
|
||||
// Spot is free software; you can redistribute it and/or modify it
|
||||
// under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation; either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// Spot is distributed in the hope that it will be useful, but WITHOUT
|
||||
// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
||||
// or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public
|
||||
// License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "common_sys.hh"
|
||||
#include <spot/twaalgos/postproc.hh>
|
||||
#include <argp.h>
|
||||
|
||||
extern const struct argp post_argp; // postprocessing enabled
|
||||
extern const struct argp post_argp_disabled; // postprocessing disabled
|
||||
|
||||
extern spot::postprocessor::output_type type;
|
||||
extern spot::postprocessor::output_pref pref;
|
||||
extern spot::postprocessor::output_pref comp;
|
||||
extern spot::postprocessor::output_pref sbacc;
|
||||
extern spot::postprocessor::optimization_level level;
|
||||
// True if --low, --medium, or --high has been given
|
||||
extern bool level_set;
|
||||
// True if --any, --small, or --deterministic has been given
|
||||
extern bool pref_set;
|
||||
40
spot/bin/common_r.cc
Normal file
40
spot/bin/common_r.cc
Normal file
|
|
@ -0,0 +1,40 @@
|
|||
// -*- coding: utf-8 -*-
|
||||
// Copyright (C) 2012, 2013, 2014 Laboratoire de Recherche et Développement
|
||||
// de l'Epita (LRDE).
|
||||
//
|
||||
// This file is part of Spot, a model checking library.
|
||||
//
|
||||
// Spot is free software; you can redistribute it and/or modify it
|
||||
// under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation; either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// Spot is distributed in the hope that it will be useful, but WITHOUT
|
||||
// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
||||
// or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public
|
||||
// License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
#include "common_sys.hh"
|
||||
#include "error.h"
|
||||
#include "common_r.hh"
|
||||
|
||||
int simplification_level = 0;
|
||||
|
||||
void
|
||||
parse_r(const char* arg)
|
||||
{
|
||||
if (!arg)
|
||||
{
|
||||
simplification_level = 3;
|
||||
return;
|
||||
}
|
||||
if (arg[1] == 0 && arg[0] >= '0' && arg[0] <= '3')
|
||||
{
|
||||
simplification_level = arg[0] - '0';
|
||||
return;
|
||||
}
|
||||
error(2, 0, "invalid simplification level '%s'", arg);
|
||||
}
|
||||
47
spot/bin/common_r.hh
Normal file
47
spot/bin/common_r.hh
Normal file
|
|
@ -0,0 +1,47 @@
|
|||
// -*- coding: utf-8 -*-
|
||||
// Copyright (C) 2012, 2015 Laboratoire de Recherche et Développement
|
||||
// de l'Epita (LRDE).
|
||||
//
|
||||
// This file is part of Spot, a model checking library.
|
||||
//
|
||||
// Spot is free software; you can redistribute it and/or modify it
|
||||
// under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation; either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// Spot is distributed in the hope that it will be useful, but WITHOUT
|
||||
// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
||||
// or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public
|
||||
// License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "common_sys.hh"
|
||||
#include <spot/tl/simplify.hh>
|
||||
|
||||
#define OPT_R 'r'
|
||||
|
||||
#define DECLARE_OPT_R \
|
||||
{ "simplify", OPT_R, "LEVEL", OPTION_ARG_OPTIONAL, \
|
||||
"simplify formulas according to LEVEL (see below); LEVEL is " \
|
||||
"set to 3 if omitted", 0 }
|
||||
|
||||
#define LEVEL_DOC(g) \
|
||||
{ nullptr, 0, nullptr, 0, \
|
||||
"The simplification LEVEL may be set as follows.", g }, \
|
||||
{ " 0", 0, nullptr, OPTION_DOC | OPTION_NO_USAGE, \
|
||||
"No rewriting", 0 }, \
|
||||
{ " 1", 0, nullptr, OPTION_DOC | OPTION_NO_USAGE, \
|
||||
"basic rewritings and eventual/universal rules", 0 }, \
|
||||
{ " 2", 0, nullptr, OPTION_DOC | OPTION_NO_USAGE, \
|
||||
"additional syntactic implication rules", 0 }, \
|
||||
{ " 3", 0, nullptr, OPTION_DOC | OPTION_NO_USAGE, \
|
||||
"better implications using containment", 0 }
|
||||
|
||||
extern int simplification_level;
|
||||
|
||||
void parse_r(const char* arg);
|
||||
spot::tl_simplifier_options simplifier_options();
|
||||
81
spot/bin/common_range.cc
Normal file
81
spot/bin/common_range.cc
Normal file
|
|
@ -0,0 +1,81 @@
|
|||
// -*- coding: utf-8 -*-
|
||||
// Copyright (C) 2012, 2014 Laboratoire de Recherche et Développement de
|
||||
// l'Epita (LRDE).
|
||||
//
|
||||
// This file is part of Spot, a model checking library.
|
||||
//
|
||||
// Spot is free software; you can redistribute it and/or modify it
|
||||
// under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation; either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// Spot is distributed in the hope that it will be useful, but WITHOUT
|
||||
// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
||||
// or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public
|
||||
// License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
#include "common_sys.hh"
|
||||
#include "error.h"
|
||||
|
||||
#include "common_range.hh"
|
||||
#include <iostream>
|
||||
#include <cstdlib>
|
||||
|
||||
// The range should have the form INT..INT or INT:INT, with
|
||||
// "42" standing for "42..42",
|
||||
// "..42" meaning "missing_left..42".
|
||||
// and "42.." meaning "42..missing_right".
|
||||
//
|
||||
// As an exception, if missing_right is 0, then missing right bounds
|
||||
// are disallowed.
|
||||
range
|
||||
parse_range(const char* str, int missing_left, int missing_right)
|
||||
{
|
||||
range res;
|
||||
char* end;
|
||||
res.min = strtol(str, &end, 10);
|
||||
if (end == str)
|
||||
{
|
||||
// No leading number. It's OK as long as the string is not
|
||||
// empty.
|
||||
if (!*end)
|
||||
error(1, 0, "invalid empty range");
|
||||
res.min = missing_left;
|
||||
}
|
||||
if (!*end)
|
||||
{
|
||||
// Only one number.
|
||||
res.max = res.min;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Skip : or ..
|
||||
if (end[0] == ':')
|
||||
++end;
|
||||
else if (end[0] == '.' && end[1] == '.')
|
||||
end += 2;
|
||||
|
||||
if (!*end && missing_right != 0)
|
||||
{
|
||||
res.max = missing_right;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Parse the next integer.
|
||||
char* end2;
|
||||
res.max = strtol(end, &end2, 10);
|
||||
if (end == end2)
|
||||
error(1, 0, "invalid range '%s' (missing end?)", str);
|
||||
if (*end2)
|
||||
error(1, 0, "invalid range '%s' (trailing garbage?)", str);
|
||||
}
|
||||
}
|
||||
|
||||
if (res.min < 0 || res.max < 0)
|
||||
error(1, 0, "invalid range '%s': values must be positive", str);
|
||||
|
||||
return res;
|
||||
}
|
||||
50
spot/bin/common_range.hh
Normal file
50
spot/bin/common_range.hh
Normal file
|
|
@ -0,0 +1,50 @@
|
|||
// -*- coding: utf-8 -*-
|
||||
// Copyright (C) 2012, 2014, 2015 Laboratoire de Recherche et
|
||||
// Développement de l'Epita (LRDE).
|
||||
//
|
||||
// This file is part of Spot, a model checking library.
|
||||
//
|
||||
// Spot is free software; you can redistribute it and/or modify it
|
||||
// under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation; either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// Spot is distributed in the hope that it will be useful, but WITHOUT
|
||||
// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
||||
// or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public
|
||||
// License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
#pragma once
|
||||
|
||||
#define RANGE_DOC \
|
||||
{ nullptr, 0, nullptr, 0, \
|
||||
"RANGE may have one of the following forms: 'INT', " \
|
||||
"'INT..INT', or '..INT'.\nIn the latter case, the missing number " \
|
||||
"is assumed to be 1.", 0 }
|
||||
|
||||
#define RANGE_DOC_FULL \
|
||||
{ nullptr, 0, nullptr, 0, \
|
||||
"RANGE may have one of the following forms: 'INT', " \
|
||||
"'INT..INT', '..INT', or 'INT..'", 0 }
|
||||
|
||||
struct range
|
||||
{
|
||||
int min;
|
||||
int max;
|
||||
|
||||
bool contains(int val)
|
||||
{
|
||||
return val >= min && val <= max;
|
||||
}
|
||||
};
|
||||
|
||||
// INT, INT..INT, ..INT, or INT..
|
||||
//
|
||||
// The missing_left and missing_right argument gives the default bound
|
||||
// values. Additionally, if missing_right == 0, then the INT.. form
|
||||
// is disallowed.
|
||||
range parse_range(const char* str,
|
||||
int missing_left = 1, int missing_right = 0);
|
||||
146
spot/bin/common_setup.cc
Normal file
146
spot/bin/common_setup.cc
Normal file
|
|
@ -0,0 +1,146 @@
|
|||
// -*- coding: utf-8 -*-
|
||||
// Copyright (C) 2012, 2013, 2014, 2015 Laboratoire de Recherche et
|
||||
// Développement de l'Epita (LRDE).
|
||||
//
|
||||
// This file is part of Spot, a model checking library.
|
||||
//
|
||||
// Spot is free software; you can redistribute it and/or modify it
|
||||
// under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation; either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// Spot is distributed in the hope that it will be useful, but WITHOUT
|
||||
// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
||||
// or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public
|
||||
// License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
#include "common_setup.hh"
|
||||
#include "argp.h"
|
||||
#include <cstdlib>
|
||||
#include <iostream>
|
||||
#include <signal.h>
|
||||
#include <sys/wait.h>
|
||||
#include <spot/misc/tmpfile.hh>
|
||||
|
||||
const char* argp_program_bug_address = "<" PACKAGE_BUGREPORT ">";
|
||||
|
||||
static void
|
||||
display_version(FILE *stream, struct argp_state*)
|
||||
{
|
||||
fputs(program_name, stream);
|
||||
fputs(" (" PACKAGE_STRING ")\n\
|
||||
\n\
|
||||
Copyright (C) 2015 Laboratoire de Recherche et Développement de l'Epita.\n\
|
||||
License GPLv3+: \
|
||||
GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>.\n\
|
||||
This is free software: you are free to change and redistribute it.\n\
|
||||
There is NO WARRANTY, to the extent permitted by law.\n", stream);
|
||||
}
|
||||
|
||||
#ifdef HAVE_SIGACTION
|
||||
static void sig_handler(int sig)
|
||||
{
|
||||
spot::cleanup_tmpfiles();
|
||||
// Send the signal again, this time to the default handler, so that
|
||||
// we return a meaningful error code.
|
||||
raise(sig);
|
||||
}
|
||||
|
||||
static void setup_sig_handler()
|
||||
{
|
||||
struct sigaction sa;
|
||||
sa.sa_handler = sig_handler;
|
||||
sigemptyset(&sa.sa_mask);
|
||||
sa.sa_flags = SA_RESETHAND;
|
||||
// Catch termination signals, so we can cleanup temporary files.
|
||||
sigaction(SIGALRM, &sa, nullptr);
|
||||
sigaction(SIGHUP, &sa, nullptr);
|
||||
sigaction(SIGINT, &sa, nullptr);
|
||||
sigaction(SIGPIPE, &sa, nullptr);
|
||||
sigaction(SIGQUIT, &sa, nullptr);
|
||||
sigaction(SIGTERM, &sa, nullptr);
|
||||
}
|
||||
#else
|
||||
# define setup_sig_handler() while (0);
|
||||
#endif
|
||||
|
||||
void
|
||||
setup(char** argv)
|
||||
{
|
||||
// Simplify the program name, because argp() uses it to report
|
||||
// errors and display help text.
|
||||
set_program_name(argv[0]);
|
||||
argv[0] = const_cast<char*>(program_name);
|
||||
|
||||
argp_program_version_hook = display_version;
|
||||
|
||||
argp_err_exit_status = 2;
|
||||
|
||||
std::ios_base::sync_with_stdio(false);
|
||||
|
||||
setup_sig_handler();
|
||||
}
|
||||
|
||||
|
||||
// argp's default behavior of offering -? for --help is just too silly.
|
||||
// I mean, come on, why not also add -* to Darwinise more shell users?
|
||||
// We disable this option as well as -V (because --version don't need
|
||||
// a short version).
|
||||
enum {
|
||||
OPT_HELP = 1,
|
||||
OPT_USAGE,
|
||||
OPT_VERSION,
|
||||
};
|
||||
|
||||
static const argp_option options[] =
|
||||
{
|
||||
{ "version", OPT_VERSION, nullptr, 0, "print program version", -1 },
|
||||
{ "help", OPT_HELP, nullptr, 0, "print this help", -1 },
|
||||
// We support this option just in case, but we don't advertise it.
|
||||
{ "usage", OPT_USAGE, nullptr, OPTION_HIDDEN, "show short usage", -1 },
|
||||
{ nullptr, 0, nullptr, 0, nullptr, 0 }
|
||||
};
|
||||
|
||||
static const argp_option options_hidden[] =
|
||||
{
|
||||
{ "version", OPT_VERSION, nullptr, OPTION_HIDDEN,
|
||||
"print program version", -1 },
|
||||
{ "help", OPT_HELP, nullptr, OPTION_HIDDEN, "print this help", -1 },
|
||||
// We support this option just in case, but we don't advertise it.
|
||||
{ "usage", OPT_USAGE, nullptr, OPTION_HIDDEN, "show short usage", -1 },
|
||||
{ nullptr, 0, nullptr, 0, nullptr, 0 }
|
||||
};
|
||||
|
||||
static int
|
||||
parse_opt_misc(int key, char*, struct argp_state* state)
|
||||
{
|
||||
// This switch is alphabetically-ordered.
|
||||
switch (key)
|
||||
{
|
||||
case OPT_HELP:
|
||||
argp_state_help(state, state->out_stream, ARGP_HELP_STD_HELP);
|
||||
break;
|
||||
case OPT_USAGE:
|
||||
argp_state_help(state, state->out_stream,
|
||||
ARGP_HELP_USAGE | ARGP_HELP_EXIT_OK);
|
||||
break;
|
||||
case OPT_VERSION:
|
||||
display_version(state->out_stream, state);
|
||||
exit(0);
|
||||
break;
|
||||
default:
|
||||
return ARGP_ERR_UNKNOWN;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
const struct argp misc_argp = { options, parse_opt_misc,
|
||||
nullptr, nullptr, nullptr, nullptr, nullptr };
|
||||
|
||||
const struct argp misc_argp_hidden = { options_hidden, parse_opt_misc,
|
||||
nullptr, nullptr, nullptr,
|
||||
nullptr, nullptr };
|
||||
28
spot/bin/common_setup.hh
Normal file
28
spot/bin/common_setup.hh
Normal file
|
|
@ -0,0 +1,28 @@
|
|||
// -*- coding: utf-8 -*-
|
||||
// Copyright (C) 2012, 2013 Laboratoire de Recherche et Développement de
|
||||
// l'Epita (LRDE).
|
||||
//
|
||||
// This file is part of Spot, a model checking library.
|
||||
//
|
||||
// Spot is free software; you can redistribute it and/or modify it
|
||||
// under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation; either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// Spot is distributed in the hope that it will be useful, but WITHOUT
|
||||
// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
||||
// or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public
|
||||
// License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "common_sys.hh"
|
||||
#include "progname.h"
|
||||
|
||||
void setup(char** progname);
|
||||
|
||||
extern const struct argp misc_argp;
|
||||
extern const struct argp misc_argp_hidden;
|
||||
24
spot/bin/common_sys.hh
Normal file
24
spot/bin/common_sys.hh
Normal file
|
|
@ -0,0 +1,24 @@
|
|||
// -*- coding: utf-8 -*-
|
||||
// Copyright (C) 2012 Laboratoire de Recherche et Développement de
|
||||
// l'Epita (LRDE).
|
||||
//
|
||||
// This file is part of Spot, a model checking library.
|
||||
//
|
||||
// Spot is free software; you can redistribute it and/or modify it
|
||||
// under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation; either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// Spot is distributed in the hope that it will be useful, but WITHOUT
|
||||
// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
||||
// or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public
|
||||
// License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
#pragma once
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
# include "config.h"
|
||||
#endif
|
||||
440
spot/bin/common_trans.cc
Normal file
440
spot/bin/common_trans.cc
Normal file
|
|
@ -0,0 +1,440 @@
|
|||
// -*- coding: utf-8 -*-
|
||||
// Copyright (C) 2015 Laboratoire de Recherche et Développement de
|
||||
// l'Epita (LRDE).
|
||||
//
|
||||
// This file is part of Spot, a model checking library.
|
||||
//
|
||||
// Spot is free software; you can redistribute it and/or modify it
|
||||
// under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation; either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// Spot is distributed in the hope that it will be useful, but WITHOUT
|
||||
// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
||||
// or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public
|
||||
// License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
#include "common_trans.hh"
|
||||
#include <cstring>
|
||||
#include <cstdlib>
|
||||
#include <cassert>
|
||||
#include <unistd.h>
|
||||
#include <signal.h>
|
||||
#include <sys/wait.h>
|
||||
#include <iomanip>
|
||||
|
||||
#include "error.h"
|
||||
|
||||
#include <spot/tl/print.hh>
|
||||
#include "common_conv.hh"
|
||||
#include <spot/misc/escape.hh>
|
||||
|
||||
// A set of tools for which we know the correct output
|
||||
static struct shorthands_t
|
||||
{
|
||||
const char* prefix;
|
||||
const char* suffix;
|
||||
}
|
||||
shorthands[] = {
|
||||
{ "lbt", " <%L>%O" },
|
||||
{ "ltl2ba", " -f %s>%O" },
|
||||
{ "ltl2dstar", " --output-format=hoa %L %O"},
|
||||
{ "ltl2tgba", " -H %f>%O" },
|
||||
{ "ltl3ba", " -f %s>%O" },
|
||||
{ "ltl3dra", " -f %s>%O" },
|
||||
{ "modella", " %L %O" },
|
||||
{ "spin", " -f %s>%O" },
|
||||
};
|
||||
|
||||
static void show_shorthands()
|
||||
{
|
||||
std::cout
|
||||
<< ("If a COMMANDFMT does not use any %-sequence, and starts with one of\n"
|
||||
"the following words, then the string on the right is appended.\n\n");
|
||||
for (auto& s: shorthands)
|
||||
std::cout << " "
|
||||
<< std::left << std::setw(12) << s.prefix
|
||||
<< s.suffix << '\n';
|
||||
std::cout
|
||||
<< ("\nAny {name} and directory component is skipped for the purpose of\n"
|
||||
"matching those prefixes. So for instance\n"
|
||||
" '{DRA} ~/mytools/ltl2dstar-0.5.2'\n"
|
||||
"will changed into\n"
|
||||
" '{DRA} ~/mytools/ltl2dstar-0.5.2 --output-format=hoa %L %O'\n");
|
||||
}
|
||||
|
||||
|
||||
translator_spec::translator_spec(const char* spec)
|
||||
: spec(spec), cmd(spec), name(spec)
|
||||
{
|
||||
if (*cmd == '{')
|
||||
{
|
||||
// Match the closing '}'
|
||||
const char* pos = cmd;
|
||||
unsigned count = 1;
|
||||
while (*++pos)
|
||||
{
|
||||
if (*pos == '{')
|
||||
++count;
|
||||
else if (*pos == '}')
|
||||
if (!--count)
|
||||
{
|
||||
name = strndup(cmd + 1, pos - cmd - 1);
|
||||
cmd = pos + 1;
|
||||
while (*cmd == ' ' || *cmd == '\t')
|
||||
++cmd;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
// If there is no % in the string, look for a known
|
||||
// command from our shorthand list. If we find it,
|
||||
// add the suffix.
|
||||
bool allocated = false;
|
||||
if (!strchr(cmd, '%'))
|
||||
{
|
||||
// Skip any leading directory name.
|
||||
auto basename = cmd;
|
||||
auto pos = cmd;
|
||||
while (*pos)
|
||||
{
|
||||
if (*pos == '/')
|
||||
basename = pos + 1;
|
||||
else if (*pos == ' ')
|
||||
break;
|
||||
++pos;
|
||||
}
|
||||
// Match a shorthand.
|
||||
for (auto& p: shorthands)
|
||||
{
|
||||
int n = strlen(p.prefix);
|
||||
if (strncmp(basename, p.prefix, n) == 0)
|
||||
{
|
||||
int m = strlen(p.suffix);
|
||||
int q = strlen(cmd);
|
||||
char* tmp = static_cast<char*>(malloc(q + m + 1));
|
||||
strcpy(tmp, cmd);
|
||||
strcpy(tmp + q, p.suffix);
|
||||
cmd = tmp;
|
||||
allocated = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!allocated)
|
||||
cmd = strdup(cmd);
|
||||
}
|
||||
|
||||
translator_spec::translator_spec(const translator_spec& other)
|
||||
: spec(other.spec), cmd(other.cmd), name(other.name)
|
||||
{
|
||||
if (name != spec)
|
||||
name = strdup(name);
|
||||
if (cmd != spec)
|
||||
cmd = strdup(cmd);
|
||||
}
|
||||
|
||||
translator_spec::~translator_spec()
|
||||
{
|
||||
if (name != spec)
|
||||
free(const_cast<char*>(name));
|
||||
if (cmd != spec)
|
||||
free(const_cast<char*>(cmd));
|
||||
}
|
||||
|
||||
std::vector<translator_spec> translators;
|
||||
|
||||
void
|
||||
quoted_string::print(std::ostream& os, const char*) const
|
||||
{
|
||||
spot::quote_shell_string(os, val().c_str());
|
||||
}
|
||||
|
||||
printable_result_filename::printable_result_filename()
|
||||
{
|
||||
val_ = nullptr;
|
||||
}
|
||||
|
||||
printable_result_filename::~printable_result_filename()
|
||||
{
|
||||
delete val_;
|
||||
}
|
||||
|
||||
void printable_result_filename::reset(unsigned n)
|
||||
{
|
||||
translator_num = n;
|
||||
}
|
||||
|
||||
void printable_result_filename::cleanup()
|
||||
{
|
||||
delete val_;
|
||||
val_ = nullptr;
|
||||
}
|
||||
|
||||
void
|
||||
printable_result_filename::print(std::ostream& os, const char*) const
|
||||
{
|
||||
char prefix[30];
|
||||
snprintf(prefix, sizeof prefix, "lcr-o%u-", translator_num);
|
||||
const_cast<printable_result_filename*>(this)->val_ =
|
||||
spot::create_tmpfile(prefix);
|
||||
spot::quote_shell_string(os, val()->name());
|
||||
}
|
||||
|
||||
|
||||
translator_runner::translator_runner(spot::bdd_dict_ptr dict,
|
||||
bool no_output_allowed)
|
||||
: dict(dict)
|
||||
{
|
||||
declare('f', &string_ltl_spot);
|
||||
declare('s', &string_ltl_spin);
|
||||
declare('l', &string_ltl_lbt);
|
||||
declare('w', &string_ltl_wring);
|
||||
declare('F', &filename_ltl_spot);
|
||||
declare('S', &filename_ltl_spin);
|
||||
declare('L', &filename_ltl_lbt);
|
||||
declare('W', &filename_ltl_wring);
|
||||
declare('D', &output);
|
||||
declare('H', &output);
|
||||
declare('N', &output);
|
||||
declare('T', &output);
|
||||
declare('O', &output);
|
||||
|
||||
size_t s = translators.size();
|
||||
assert(s);
|
||||
for (size_t n = 0; n < s; ++n)
|
||||
{
|
||||
// Check that each translator uses at least one input and
|
||||
// one output.
|
||||
std::vector<bool> has(256);
|
||||
const translator_spec& t = translators[n];
|
||||
scan(t.cmd, has);
|
||||
if (!(has['f'] || has['s'] || has['l'] || has['w']
|
||||
|| has['F'] || has['S'] || has['L'] || has['W']))
|
||||
error(2, 0, "no input %%-sequence in '%s'.\n Use "
|
||||
"one of %%f,%%s,%%l,%%w,%%F,%%S,%%L,%%W to indicate how "
|
||||
"to pass the formula.", t.spec);
|
||||
if (!no_output_allowed
|
||||
&& !(has['O'] ||
|
||||
// backward-compatibility
|
||||
has['D'] || has['N'] || has['T'] || has['H']))
|
||||
error(2, 0, "no output %%-sequence in '%s'.\n Use "
|
||||
"%%O to indicate where the automaton is output.",
|
||||
t.spec);
|
||||
// Remember the %-sequences used by all translators.
|
||||
prime(t.cmd);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
translator_runner::string_to_tmp(std::string& str, unsigned n,
|
||||
std::string& tmpname)
|
||||
{
|
||||
char prefix[30];
|
||||
snprintf(prefix, sizeof prefix, "lcr-i%u-", n);
|
||||
spot::open_temporary_file* tmpfile = spot::create_open_tmpfile(prefix);
|
||||
tmpname = tmpfile->name();
|
||||
int fd = tmpfile->fd();
|
||||
ssize_t s = str.size();
|
||||
if (write(fd, str.c_str(), s) != s
|
||||
|| write(fd, "\n", 1) != 1)
|
||||
error(2, errno, "failed to write into %s", tmpname.c_str());
|
||||
tmpfile->close();
|
||||
}
|
||||
|
||||
const std::string&
|
||||
translator_runner::formula() const
|
||||
{
|
||||
// Pick the most readable format we have...
|
||||
if (!string_ltl_spot.val().empty())
|
||||
return string_ltl_spot;
|
||||
if (!string_ltl_spin.val().empty())
|
||||
return string_ltl_spin;
|
||||
if (!string_ltl_wring.val().empty())
|
||||
return string_ltl_wring;
|
||||
if (!string_ltl_lbt.val().empty())
|
||||
return string_ltl_lbt;
|
||||
SPOT_UNREACHABLE();
|
||||
return string_ltl_spot;
|
||||
}
|
||||
|
||||
void
|
||||
translator_runner::round_formula(spot::formula f, unsigned serial)
|
||||
{
|
||||
if (has('f') || has('F'))
|
||||
string_ltl_spot = spot::str_psl(f, true);
|
||||
if (has('s') || has('S'))
|
||||
string_ltl_spin = spot::str_spin_ltl(f, true);
|
||||
if (has('l') || has('L'))
|
||||
string_ltl_lbt = spot::str_lbt_ltl(f);
|
||||
if (has('w') || has('W'))
|
||||
string_ltl_wring = spot::str_wring_ltl(f);
|
||||
if (has('F'))
|
||||
string_to_tmp(string_ltl_spot, serial, filename_ltl_spot);
|
||||
if (has('S'))
|
||||
string_to_tmp(string_ltl_spin, serial, filename_ltl_spin);
|
||||
if (has('L'))
|
||||
string_to_tmp(string_ltl_lbt, serial, filename_ltl_lbt);
|
||||
if (has('W'))
|
||||
string_to_tmp(string_ltl_wring, serial, filename_ltl_wring);
|
||||
}
|
||||
|
||||
|
||||
|
||||
volatile bool timed_out = false;
|
||||
unsigned timeout_count = 0;
|
||||
|
||||
static unsigned timeout = 0;
|
||||
#if ENABLE_TIMEOUT
|
||||
static volatile int alarm_on = 0;
|
||||
static int child_pid = -1;
|
||||
|
||||
static void
|
||||
sig_handler(int sig)
|
||||
{
|
||||
if (child_pid == 0)
|
||||
error(2, 0, "received signal %d before starting child", sig);
|
||||
|
||||
if (sig == SIGALRM && alarm_on)
|
||||
{
|
||||
timed_out = true;
|
||||
if (--alarm_on)
|
||||
{
|
||||
// Send SIGTERM to children.
|
||||
kill(-child_pid, SIGTERM);
|
||||
// Try again later if it didn't work. (alarm() will be reset
|
||||
// if it did work and the call to wait() returns)
|
||||
alarm(2);
|
||||
}
|
||||
else
|
||||
{
|
||||
// After a few gentle tries, really kill that child.
|
||||
kill(-child_pid, SIGKILL);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// forward signal
|
||||
kill(-child_pid, sig);
|
||||
// cleanup files
|
||||
spot::cleanup_tmpfiles();
|
||||
// and die verbosely
|
||||
error(2, 0, "received signal %d", sig);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
setup_sig_handler()
|
||||
{
|
||||
struct sigaction sa;
|
||||
sa.sa_handler = sig_handler;
|
||||
sigemptyset(&sa.sa_mask);
|
||||
sa.sa_flags = SA_RESTART; // So that wait() doesn't get aborted by SIGALRM.
|
||||
sigaction(SIGALRM, &sa, nullptr);
|
||||
// Catch termination signals, so we can kill the subprocess.
|
||||
sigaction(SIGHUP, &sa, nullptr);
|
||||
sigaction(SIGINT, &sa, nullptr);
|
||||
sigaction(SIGQUIT, &sa, nullptr);
|
||||
sigaction(SIGTERM, &sa, nullptr);
|
||||
}
|
||||
|
||||
int
|
||||
exec_with_timeout(const char* cmd)
|
||||
{
|
||||
int status;
|
||||
|
||||
timed_out = false;
|
||||
|
||||
child_pid = fork();
|
||||
if (child_pid == -1)
|
||||
error(2, errno, "failed to fork()");
|
||||
|
||||
if (child_pid == 0)
|
||||
{
|
||||
setpgid(0, 0);
|
||||
execlp("sh", "sh", "-c", cmd, nullptr);
|
||||
error(2, errno, "failed to run 'sh'");
|
||||
// never reached
|
||||
return -1;
|
||||
}
|
||||
else
|
||||
{
|
||||
alarm(timeout);
|
||||
// Upon SIGALRM, the child will receive up to 3
|
||||
// signals: SIGTERM, SIGTERM, SIGKILL.
|
||||
alarm_on = 3;
|
||||
int w = waitpid(child_pid, &status, 0);
|
||||
alarm_on = 0;
|
||||
|
||||
if (w == -1)
|
||||
error(2, errno, "error during wait()");
|
||||
|
||||
alarm(0);
|
||||
}
|
||||
return status;
|
||||
}
|
||||
#endif // ENABLE_TIMEOUT
|
||||
|
||||
enum {
|
||||
OPT_LIST = 1,
|
||||
};
|
||||
static const argp_option options[] =
|
||||
{
|
||||
/**************************************************/
|
||||
{ nullptr, 0, nullptr, 0, "Specifying translators to call:", 2 },
|
||||
{ "translator", 't', "COMMANDFMT", 0,
|
||||
"register one translator to call", 0 },
|
||||
{ "timeout", 'T', "NUMBER", 0, "kill translators after NUMBER seconds", 0 },
|
||||
{ "list-shorthands", OPT_LIST, nullptr, 0,
|
||||
"list availabled shorthands to use in COMMANDFMT", 0},
|
||||
/**************************************************/
|
||||
{ nullptr, 0, nullptr, 0,
|
||||
"COMMANDFMT should specify input and output arguments using the "
|
||||
"following character sequences:", 3 },
|
||||
{ "%f,%s,%l,%w", 0, nullptr, OPTION_DOC | OPTION_NO_USAGE,
|
||||
"the formula as a (quoted) string in Spot, Spin, LBT, or Wring's syntax",
|
||||
0 },
|
||||
{ "%F,%S,%L,%W", 0, nullptr, OPTION_DOC | OPTION_NO_USAGE,
|
||||
"the formula as a file in Spot, Spin, LBT, or Wring's syntax", 0 },
|
||||
{ "%O", 0, nullptr, OPTION_DOC | OPTION_NO_USAGE,
|
||||
"the automaton is output in HOA, never claim, LBTT, or ltl2dstar's "
|
||||
"format", 0 },
|
||||
{ "%%", 0, nullptr, OPTION_DOC | OPTION_NO_USAGE, "a single %", 0 },
|
||||
{ nullptr, 0, nullptr, 0,
|
||||
"If either %l, %L, or %T are used, any input formula that does "
|
||||
"not use LBT-style atomic propositions (i.e. p0, p1, ...) will be "
|
||||
"relabeled automatically.\n"
|
||||
"Furthermore, if COMMANDFMT has the form \"{NAME}CMD\", then only CMD "
|
||||
"will be passed to the shell, and NAME will be used to name the tool "
|
||||
"in the output.", 4 },
|
||||
{ nullptr, 0, nullptr, 0, nullptr, 0 }
|
||||
};
|
||||
|
||||
static int parse_opt_trans(int key, char* arg, struct argp_state*)
|
||||
{
|
||||
switch (key)
|
||||
{
|
||||
case 't':
|
||||
translators.push_back(arg);
|
||||
break;
|
||||
case 'T':
|
||||
timeout = to_pos_int(arg);
|
||||
#if !ENABLE_TIMEOUT
|
||||
std::cerr << "warning: setting a timeout is not supported "
|
||||
<< "on your platform" << std::endl;
|
||||
#endif
|
||||
break;
|
||||
case OPT_LIST:
|
||||
show_shorthands();
|
||||
exit(0);
|
||||
default:
|
||||
return ARGP_ERR_UNKNOWN;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
const struct argp trans_argp = { options, parse_opt_trans, nullptr, nullptr,
|
||||
nullptr, nullptr, nullptr };
|
||||
117
spot/bin/common_trans.hh
Normal file
117
spot/bin/common_trans.hh
Normal file
|
|
@ -0,0 +1,117 @@
|
|||
// -*- coding: utf-8 -*-
|
||||
// Copyright (C) 2015 Laboratoire de Recherche et Développement de
|
||||
// l'Epita (LRDE).
|
||||
//
|
||||
// This file is part of Spot, a model checking library.
|
||||
//
|
||||
// Spot is free software; you can redistribute it and/or modify it
|
||||
// under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation; either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// Spot is distributed in the hope that it will be useful, but WITHOUT
|
||||
// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
||||
// or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public
|
||||
// License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "common_sys.hh"
|
||||
#include <vector>
|
||||
#include <argp.h>
|
||||
|
||||
#include <spot/misc/formater.hh>
|
||||
#include <spot/misc/tmpfile.hh>
|
||||
#include <spot/twa/twagraph.hh>
|
||||
|
||||
|
||||
extern const struct argp trans_argp;
|
||||
|
||||
struct translator_spec
|
||||
{
|
||||
// The translator command, as specified on the command-line.
|
||||
// If this has the form of
|
||||
// {name}cmd
|
||||
// then it is split in two components.
|
||||
// Otherwise, spec=cmd=name.
|
||||
const char* spec;
|
||||
// actual shell command (or spec)
|
||||
const char* cmd;
|
||||
// name of the translator (or spec)
|
||||
const char* name;
|
||||
|
||||
translator_spec(const char* spec);
|
||||
translator_spec(const translator_spec& other);
|
||||
~translator_spec();
|
||||
};
|
||||
|
||||
extern std::vector<translator_spec> translators;
|
||||
|
||||
struct quoted_string final: public spot::printable_value<std::string>
|
||||
{
|
||||
using spot::printable_value<std::string>::operator=;
|
||||
void print(std::ostream& os, const char* pos) const override;
|
||||
};
|
||||
|
||||
struct printable_result_filename final:
|
||||
public spot::printable_value<spot::temporary_file*>
|
||||
{
|
||||
unsigned translator_num;
|
||||
|
||||
printable_result_filename();
|
||||
~printable_result_filename();
|
||||
void reset(unsigned n);
|
||||
void cleanup();
|
||||
|
||||
void print(std::ostream& os, const char* pos) const override;
|
||||
};
|
||||
|
||||
|
||||
class translator_runner: protected spot::formater
|
||||
{
|
||||
protected:
|
||||
spot::bdd_dict_ptr dict;
|
||||
// Round-specific variables
|
||||
quoted_string string_ltl_spot;
|
||||
quoted_string string_ltl_spin;
|
||||
quoted_string string_ltl_lbt;
|
||||
quoted_string string_ltl_wring;
|
||||
quoted_string filename_ltl_spot;
|
||||
quoted_string filename_ltl_spin;
|
||||
quoted_string filename_ltl_lbt;
|
||||
quoted_string filename_ltl_wring;
|
||||
// Run-specific variables
|
||||
printable_result_filename output;
|
||||
public:
|
||||
using spot::formater::has;
|
||||
|
||||
translator_runner(spot::bdd_dict_ptr dict,
|
||||
// whether we accept the absence of output
|
||||
// specifier
|
||||
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::formula f, unsigned serial);
|
||||
};
|
||||
|
||||
|
||||
// Disable handling of timeout on systems that miss kill() or alarm().
|
||||
// For instance MinGW.
|
||||
#if HAVE_KILL && HAVE_ALARM
|
||||
# define ENABLE_TIMEOUT 1
|
||||
#else
|
||||
# define ENABLE_TIMEOUT 0
|
||||
#endif
|
||||
|
||||
extern volatile bool timed_out;
|
||||
extern unsigned timeout_count;
|
||||
#if ENABLE_TIMEOUT
|
||||
void setup_sig_handler();
|
||||
int exec_with_timeout(const char* cmd);
|
||||
#else // !ENABLE_TIMEOUT
|
||||
#define exec_with_timeout(cmd) system(cmd)
|
||||
#define setup_sig_handler() while (0);
|
||||
#endif // !ENABLE_TIMEOUT
|
||||
202
spot/bin/dstar2tgba.cc
Normal file
202
spot/bin/dstar2tgba.cc
Normal file
|
|
@ -0,0 +1,202 @@
|
|||
// -*- coding: utf-8 -*-
|
||||
// Copyright (C) 2013, 2014, 2015 Laboratoire de Recherche et
|
||||
// Développement de l'Epita (LRDE).
|
||||
//
|
||||
// This file is part of Spot, a model checking library.
|
||||
//
|
||||
// Spot is free software; you can redistribute it and/or modify it
|
||||
// under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation; either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// Spot is distributed in the hope that it will be useful, but WITHOUT
|
||||
// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
||||
// or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public
|
||||
// License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
#include "common_sys.hh"
|
||||
|
||||
#include <string>
|
||||
#include <iostream>
|
||||
#include <memory>
|
||||
|
||||
#include <argp.h>
|
||||
#include "error.h"
|
||||
|
||||
#include "common_setup.hh"
|
||||
#include "common_finput.hh"
|
||||
#include "common_cout.hh"
|
||||
#include "common_aoutput.hh"
|
||||
#include "common_post.hh"
|
||||
#include "common_file.hh"
|
||||
#include "common_hoaread.hh"
|
||||
|
||||
#include <spot/twaalgos/dot.hh>
|
||||
#include <spot/twaalgos/lbtt.hh>
|
||||
#include <spot/twaalgos/hoa.hh>
|
||||
#include <spot/twaalgos/neverclaim.hh>
|
||||
#include <spot/twaalgos/stats.hh>
|
||||
#include <spot/twaalgos/totgba.hh>
|
||||
#include <spot/twa/bddprint.hh>
|
||||
#include <spot/misc/optionmap.hh>
|
||||
#include <spot/misc/timer.hh>
|
||||
#include <spot/parseaut/public.hh>
|
||||
#include <spot/twaalgos/sccinfo.hh>
|
||||
|
||||
static const char argp_program_doc[] ="\
|
||||
Convert automata with any acceptance condition into variants of \
|
||||
Büchi automata.\n\nThis reads automata into any supported format \
|
||||
(HOA, LBTT, ltl2dstar, never claim) and outputs a \
|
||||
Transition-based Generalized Büchi Automata in GraphViz's format by default. \
|
||||
Each supplied file may contain multiple automata.";
|
||||
|
||||
static const argp_option options[] =
|
||||
{
|
||||
/**************************************************/
|
||||
{ nullptr, 0, nullptr, 0, "Input:", 1 },
|
||||
{ "file", 'F', "FILENAME", 0,
|
||||
"process the automaton in FILENAME", 0 },
|
||||
/**************************************************/
|
||||
{ nullptr, 0, nullptr, 0, "Miscellaneous options:", -1 },
|
||||
{ "extra-options", 'x', "OPTS", 0,
|
||||
"fine-tuning options (see spot-x (7))", 0 },
|
||||
{ nullptr, 0, nullptr, 0, nullptr, 0 }
|
||||
};
|
||||
|
||||
static const struct argp_child children[] =
|
||||
{
|
||||
{ &hoaread_argp, 0, nullptr, 0 },
|
||||
{ &aoutput_argp, 0, nullptr, 0 },
|
||||
{ &aoutput_io_format_argp, 0, nullptr, 4 },
|
||||
{ &post_argp, 0, nullptr, 0 },
|
||||
{ &misc_argp, 0, nullptr, -1 },
|
||||
{ nullptr, 0, nullptr, 0 }
|
||||
};
|
||||
|
||||
static spot::option_map extra_options;
|
||||
|
||||
static int
|
||||
parse_opt(int key, char* arg, struct argp_state*)
|
||||
{
|
||||
// This switch is alphabetically-ordered.
|
||||
switch (key)
|
||||
{
|
||||
case 'F':
|
||||
jobs.emplace_back(arg, true);
|
||||
break;
|
||||
case 'x':
|
||||
{
|
||||
const char* opt = extra_options.parse_options(arg);
|
||||
if (opt)
|
||||
error(2, 0, "failed to parse --options near '%s'", opt);
|
||||
}
|
||||
break;
|
||||
case ARGP_KEY_ARG:
|
||||
jobs.emplace_back(arg, true);
|
||||
break;
|
||||
default:
|
||||
return ARGP_ERR_UNKNOWN;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
namespace
|
||||
{
|
||||
class dstar_processor: public job_processor
|
||||
{
|
||||
public:
|
||||
spot::postprocessor& post;
|
||||
automaton_printer printer;
|
||||
|
||||
dstar_processor(spot::postprocessor& post)
|
||||
: post(post), printer(aut_input)
|
||||
{
|
||||
}
|
||||
|
||||
int
|
||||
process_formula(spot::formula, const char*, int)
|
||||
{
|
||||
SPOT_UNREACHABLE();
|
||||
}
|
||||
|
||||
int
|
||||
process_automaton(const spot::const_parsed_aut_ptr& haut,
|
||||
const char* filename)
|
||||
{
|
||||
spot::stopwatch sw;
|
||||
sw.start();
|
||||
auto nba = spot::to_generalized_buchi(haut->aut);
|
||||
auto aut = post.run(nba, nullptr);
|
||||
const double conversion_time = sw.stop();
|
||||
|
||||
printer.print(aut, nullptr, filename, -1, conversion_time, haut);
|
||||
flush_cout();
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
aborted(const spot::const_parsed_aut_ptr& h, const char* filename)
|
||||
{
|
||||
std::cerr << filename << ':' << h->loc << ": aborted input automaton\n";
|
||||
return 2;
|
||||
}
|
||||
|
||||
int
|
||||
process_file(const char* filename)
|
||||
{
|
||||
auto hp = spot::automaton_stream_parser(filename, opt_parse);
|
||||
int err = 0;
|
||||
while (!abort_run)
|
||||
{
|
||||
auto haut = hp.parse(spot::make_bdd_dict());
|
||||
if (!haut->aut && haut->errors.empty())
|
||||
break;
|
||||
if (haut->format_errors(std::cerr))
|
||||
err = 2;
|
||||
if (!haut->aut)
|
||||
error(2, 0, "failed to read automaton from %s", filename);
|
||||
else if (haut->aborted)
|
||||
err = std::max(err, aborted(haut, filename));
|
||||
else
|
||||
process_automaton(haut, filename);
|
||||
}
|
||||
return err;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
int
|
||||
main(int argc, char** argv)
|
||||
{
|
||||
setup(argv);
|
||||
|
||||
const argp ap = { options, parse_opt, "[FILENAMES...]",
|
||||
argp_program_doc, children, nullptr, nullptr };
|
||||
|
||||
if (int err = argp_parse(&ap, argc, argv, ARGP_NO_HELP, nullptr, nullptr))
|
||||
exit(err);
|
||||
|
||||
if (jobs.empty())
|
||||
jobs.emplace_back("-", true);
|
||||
|
||||
spot::postprocessor post(&extra_options);
|
||||
post.set_pref(pref | comp | sbacc);
|
||||
post.set_type(type);
|
||||
post.set_level(level);
|
||||
|
||||
try
|
||||
{
|
||||
dstar_processor processor(post);
|
||||
if (processor.run())
|
||||
return 2;
|
||||
}
|
||||
catch (const std::runtime_error& e)
|
||||
{
|
||||
error(2, 0, "%s", e.what());
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
834
spot/bin/genltl.cc
Normal file
834
spot/bin/genltl.cc
Normal file
|
|
@ -0,0 +1,834 @@
|
|||
// -*- coding: utf-8 -*-
|
||||
// Copyright (C) 2012, 2013, 2015 Laboratoire de Recherche et Développement
|
||||
// de l'Epita (LRDE).
|
||||
//
|
||||
// This file is part of Spot, a model checking library.
|
||||
//
|
||||
// Spot is free software; you can redistribute it and/or modify it
|
||||
// under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation; either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// Spot is distributed in the hope that it will be useful, but WITHOUT
|
||||
// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
||||
// or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public
|
||||
// License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
// Families defined here come from the following papers:
|
||||
//
|
||||
// @InProceedings{cichon.09.depcos,
|
||||
// author = {Jacek Cicho{\'n} and Adam Czubak and Andrzej Jasi{\'n}ski},
|
||||
// title = {Minimal {B\"uchi} Automata for Certain Classes of {LTL} Formulas},
|
||||
// booktitle = {Proceedings of the Fourth International Conference on
|
||||
// Dependability of Computer Systems},
|
||||
// pages = {17--24},
|
||||
// year = 2009,
|
||||
// publisher = {IEEE Computer Society},
|
||||
// }
|
||||
//
|
||||
// @InProceedings{geldenhuys.06.spin,
|
||||
// author = {Jaco Geldenhuys and Henri Hansen},
|
||||
// title = {Larger Automata and Less Work for LTL Model Checking},
|
||||
// booktitle = {Proceedings of the 13th International SPIN Workshop},
|
||||
// year = {2006},
|
||||
// pages = {53--70},
|
||||
// series = {Lecture Notes in Computer Science},
|
||||
// volume = {3925},
|
||||
// publisher = {Springer}
|
||||
// }
|
||||
//
|
||||
// @InProceedings{gastin.01.cav,
|
||||
// author = {Paul Gastin and Denis Oddoux},
|
||||
// title = {Fast {LTL} to {B\"u}chi Automata Translation},
|
||||
// booktitle = {Proceedings of the 13th International Conference on
|
||||
// Computer Aided Verification (CAV'01)},
|
||||
// pages = {53--65},
|
||||
// year = 2001,
|
||||
// editor = {G. Berry and H. Comon and A. Finkel},
|
||||
// volume = {2102},
|
||||
// series = {Lecture Notes in Computer Science},
|
||||
// address = {Paris, France},
|
||||
// publisher = {Springer-Verlag}
|
||||
// }
|
||||
//
|
||||
// @InProceedings{rozier.07.spin,
|
||||
// author = {Kristin Y. Rozier and Moshe Y. Vardi},
|
||||
// title = {LTL Satisfiability Checking},
|
||||
// booktitle = {Proceedings of the 12th International SPIN Workshop on
|
||||
// Model Checking of Software (SPIN'07)},
|
||||
// pages = {149--167},
|
||||
// year = {2007},
|
||||
// volume = {4595},
|
||||
// series = {Lecture Notes in Computer Science},
|
||||
// publisher = {Springer-Verlag}
|
||||
// }
|
||||
|
||||
#include "common_sys.hh"
|
||||
|
||||
#include <iostream>
|
||||
#include <fstream>
|
||||
#include <argp.h>
|
||||
#include <cstdlib>
|
||||
#include "error.h"
|
||||
#include <vector>
|
||||
|
||||
#include "common_setup.hh"
|
||||
#include "common_output.hh"
|
||||
#include "common_range.hh"
|
||||
|
||||
#include <cassert>
|
||||
#include <iostream>
|
||||
#include <sstream>
|
||||
#include <set>
|
||||
#include <string>
|
||||
#include <cstdlib>
|
||||
#include <cstring>
|
||||
#include <spot/tl/formula.hh>
|
||||
#include <spot/tl/relabel.hh>
|
||||
|
||||
using namespace spot;
|
||||
|
||||
const char argp_program_doc[] ="\
|
||||
Generate temporal logic formulas from predefined scalable patterns.";
|
||||
|
||||
enum {
|
||||
OPT_AND_F = 1,
|
||||
OPT_AND_FG,
|
||||
OPT_AND_GF,
|
||||
OPT_CCJ_ALPHA,
|
||||
OPT_CCJ_BETA,
|
||||
OPT_CCJ_BETA_PRIME,
|
||||
OPT_GH_Q,
|
||||
OPT_GH_R,
|
||||
OPT_GO_THETA,
|
||||
OPT_OR_FG,
|
||||
OPT_OR_G,
|
||||
OPT_OR_GF,
|
||||
OPT_R_LEFT,
|
||||
OPT_R_RIGHT,
|
||||
OPT_RV_COUNTER,
|
||||
OPT_RV_COUNTER_CARRY,
|
||||
OPT_RV_COUNTER_CARRY_LINEAR,
|
||||
OPT_RV_COUNTER_LINEAR,
|
||||
OPT_U_LEFT,
|
||||
OPT_U_RIGHT,
|
||||
LAST_CLASS,
|
||||
};
|
||||
|
||||
const char* const class_name[LAST_CLASS] =
|
||||
{
|
||||
"and-f",
|
||||
"and-fg",
|
||||
"and-gf",
|
||||
"ccj-alpha",
|
||||
"ccj-beta",
|
||||
"ccj-beta-prime",
|
||||
"gh-q",
|
||||
"gh-r",
|
||||
"go-theta",
|
||||
"or-fg",
|
||||
"or-g",
|
||||
"or-gf",
|
||||
"or-r-left",
|
||||
"or-r-right",
|
||||
"rv-counter",
|
||||
"rv-counter-carry",
|
||||
"rv-counter-carry-linear",
|
||||
"rv-counter-linear",
|
||||
"u-left",
|
||||
"u-right",
|
||||
};
|
||||
|
||||
|
||||
#define OPT_ALIAS(o) { #o, 0, nullptr, OPTION_ALIAS, nullptr, 0 }
|
||||
|
||||
static const argp_option options[] =
|
||||
{
|
||||
/**************************************************/
|
||||
// Keep this alphabetically sorted (expect for aliases).
|
||||
{ nullptr, 0, nullptr, 0, "Pattern selection:", 1},
|
||||
// J. Geldenhuys and H. Hansen (Spin'06): Larger automata and less
|
||||
// work for LTL model checking.
|
||||
{ "and-f", OPT_AND_F, "RANGE", 0, "F(p1)&F(p2)&...&F(pn)", 0 },
|
||||
OPT_ALIAS(gh-e),
|
||||
{ "and-fg", OPT_AND_FG, "RANGE", 0, "FG(p1)&FG(p2)&...&FG(pn)", 0 },
|
||||
{ "and-gf", OPT_AND_GF, "RANGE", 0, "GF(p1)&GF(p2)&...&GF(pn)", 0 },
|
||||
OPT_ALIAS(ccj-phi),
|
||||
OPT_ALIAS(gh-c2),
|
||||
{ "ccj-alpha", OPT_CCJ_ALPHA, "RANGE", 0,
|
||||
"F(p1&F(p2&F(p3&...F(pn)))) & F(q1&F(q2&F(q3&...F(qn))))", 0 },
|
||||
{ "ccj-beta", OPT_CCJ_BETA, "RANGE", 0,
|
||||
"F(p&X(p&X(p&...X(p)))) & F(q&X(q&X(q&...X(q))))", 0 },
|
||||
{ "ccj-beta-prime", OPT_CCJ_BETA_PRIME, "RANGE", 0,
|
||||
"F(p&(Xp)&(XXp)&...(X...X(p))) & F(q&(Xq)&(XXq)&...(X...X(q)))", 0 },
|
||||
{ "gh-q", OPT_GH_Q, "RANGE", 0,
|
||||
"(F(p1)|G(p2))&(F(p2)|G(p3))&... &(F(pn)|G(p{n+1}))", 0 },
|
||||
{ "gh-r", OPT_GH_R, "RANGE", 0,
|
||||
"(GF(p1)|FG(p2))&(GF(p2)|FG(p3))&... &(GF(pn)|FG(p{n+1}))", 0},
|
||||
{ "go-theta", OPT_GO_THETA, "RANGE", 0,
|
||||
"!((GF(p1)&GF(p2)&...&GF(pn)) -> G(q->F(r)))", 0 },
|
||||
{ "or-fg", OPT_OR_FG, "RANGE", 0, "FG(p1)|FG(p2)|...|FG(pn)", 0 },
|
||||
OPT_ALIAS(ccj-xi),
|
||||
{ "or-g", OPT_OR_G, "RANGE", 0, "G(p1)|G(p2)|...|G(pn)", 0 },
|
||||
OPT_ALIAS(gh-s),
|
||||
{ "or-gf", OPT_OR_GF, "RANGE", 0, "GF(p1)|GF(p2)|...|GF(pn)", 0 },
|
||||
OPT_ALIAS(gh-c1),
|
||||
{ "r-left", OPT_R_LEFT, "RANGE", 0, "(((p1 R p2) R p3) ... R pn)", 0 },
|
||||
{ "r-right", OPT_R_RIGHT, "RANGE", 0, "(p1 R (p2 R (... R pn)))", 0 },
|
||||
{ "rv-counter", OPT_RV_COUNTER, "RANGE", 0,
|
||||
"n-bit counter", 0 },
|
||||
{ "rv-counter-carry", OPT_RV_COUNTER_CARRY, "RANGE", 0,
|
||||
"n-bit counter w/ carry", 0 },
|
||||
{ "rv-counter-carry-linear", OPT_RV_COUNTER_CARRY_LINEAR, "RANGE", 0,
|
||||
"n-bit counter w/ carry (linear size)", 0 },
|
||||
{ "rv-counter-linear", OPT_RV_COUNTER_LINEAR, "RANGE", 0,
|
||||
"n-bit counter (linear size)", 0 },
|
||||
{ "u-left", OPT_U_LEFT, "RANGE", 0, "(((p1 U p2) U p3) ... U pn)", 0 },
|
||||
OPT_ALIAS(gh-u),
|
||||
{ "u-right", OPT_U_RIGHT, "RANGE", 0, "(p1 U (p2 U (... U pn)))", 0 },
|
||||
OPT_ALIAS(gh-u2),
|
||||
OPT_ALIAS(go-phi),
|
||||
RANGE_DOC,
|
||||
/**************************************************/
|
||||
{ nullptr, 0, nullptr, 0, "Output options:", -20 },
|
||||
{ nullptr, 0, nullptr, 0, "The FORMAT string passed to --format may use "
|
||||
"the following interpreted sequences:", -19 },
|
||||
{ "%f", 0, nullptr, OPTION_DOC | OPTION_NO_USAGE,
|
||||
"the formula (in the selected syntax)", 0 },
|
||||
{ "%F", 0, nullptr, OPTION_DOC | OPTION_NO_USAGE,
|
||||
"the name of the pattern", 0 },
|
||||
{ "%L", 0, nullptr, OPTION_DOC | OPTION_NO_USAGE,
|
||||
"the argument of the pattern", 0 },
|
||||
{ "%%", 0, nullptr, OPTION_DOC | OPTION_NO_USAGE,
|
||||
"a single %", 0 },
|
||||
{ nullptr, 0, nullptr, 0, "Miscellaneous options:", -1 },
|
||||
{ nullptr, 0, nullptr, 0, nullptr, 0 }
|
||||
};
|
||||
|
||||
struct job
|
||||
{
|
||||
int pattern;
|
||||
struct range range;
|
||||
};
|
||||
|
||||
typedef std::vector<job> jobs_t;
|
||||
static jobs_t jobs;
|
||||
|
||||
|
||||
const struct argp_child children[] =
|
||||
{
|
||||
{ &output_argp, 0, nullptr, -20 },
|
||||
{ &misc_argp, 0, nullptr, -1 },
|
||||
{ nullptr, 0, nullptr, 0 }
|
||||
};
|
||||
|
||||
static void
|
||||
enqueue_job(int pattern, const char* range_str)
|
||||
{
|
||||
job j;
|
||||
j.pattern = pattern;
|
||||
j.range = parse_range(range_str);
|
||||
jobs.push_back(j);
|
||||
}
|
||||
|
||||
static int
|
||||
parse_opt(int key, char* arg, struct argp_state*)
|
||||
{
|
||||
// This switch is alphabetically-ordered.
|
||||
switch (key)
|
||||
{
|
||||
case OPT_AND_F:
|
||||
case OPT_AND_FG:
|
||||
case OPT_AND_GF:
|
||||
case OPT_CCJ_ALPHA:
|
||||
case OPT_CCJ_BETA:
|
||||
case OPT_CCJ_BETA_PRIME:
|
||||
case OPT_GH_Q:
|
||||
case OPT_GH_R:
|
||||
case OPT_GO_THETA:
|
||||
case OPT_OR_FG:
|
||||
case OPT_OR_G:
|
||||
case OPT_OR_GF:
|
||||
case OPT_R_LEFT:
|
||||
case OPT_R_RIGHT:
|
||||
case OPT_RV_COUNTER:
|
||||
case OPT_RV_COUNTER_CARRY:
|
||||
case OPT_RV_COUNTER_CARRY_LINEAR:
|
||||
case OPT_RV_COUNTER_LINEAR:
|
||||
case OPT_U_LEFT:
|
||||
case OPT_U_RIGHT:
|
||||
enqueue_job(key, arg);
|
||||
break;
|
||||
default:
|
||||
return ARGP_ERR_UNKNOWN;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
#define G_(x) formula::G(x)
|
||||
#define F_(x) formula::F(x)
|
||||
#define X_(x) formula::X(x)
|
||||
#define Not_(x) formula::Not(x)
|
||||
|
||||
#define Implies_(x, y) formula::Implies((x), (y))
|
||||
#define Equiv_(x, y) formula::Equiv((x), (y))
|
||||
#define And_(x, y) formula::And({(x), (y)})
|
||||
#define Or_(x, y) formula::Or({(x), (y)})
|
||||
#define U_(x, y) formula::U((x), (y))
|
||||
|
||||
// F(p_1 & F(p_2 & F(p_3 & ... F(p_n))))
|
||||
static formula
|
||||
E_n(std::string name, int n)
|
||||
{
|
||||
if (n <= 0)
|
||||
return formula::tt();
|
||||
|
||||
formula result = nullptr;
|
||||
|
||||
for (; n > 0; --n)
|
||||
{
|
||||
std::ostringstream p;
|
||||
p << name << n;
|
||||
formula f = formula::ap(p.str());
|
||||
if (result)
|
||||
result = And_(f, result);
|
||||
else
|
||||
result = f;
|
||||
result = F_(result);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
// p & X(p & X(p & ... X(p)))
|
||||
static formula
|
||||
phi_n(std::string name, int n)
|
||||
{
|
||||
if (n <= 0)
|
||||
return formula::tt();
|
||||
|
||||
formula result = nullptr;
|
||||
formula p = formula::ap(name);
|
||||
for (; n > 0; --n)
|
||||
{
|
||||
if (result)
|
||||
result = And_(p, X_(result));
|
||||
else
|
||||
result = p;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
static formula
|
||||
N_n(std::string name, int n)
|
||||
{
|
||||
return formula::F(phi_n(name, n));
|
||||
}
|
||||
|
||||
// p & X(p) & XX(p) & XXX(p) & ... X^n(p)
|
||||
static formula
|
||||
phi_prime_n(std::string name, int n)
|
||||
{
|
||||
if (n <= 0)
|
||||
return formula::tt();
|
||||
|
||||
formula result = nullptr;
|
||||
formula p = formula::ap(name);
|
||||
for (; n > 0; --n)
|
||||
{
|
||||
if (result)
|
||||
{
|
||||
p = X_(p);
|
||||
result = And_(result, p);
|
||||
}
|
||||
else
|
||||
{
|
||||
result = p;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
static formula
|
||||
N_prime_n(std::string name, int n)
|
||||
{
|
||||
return F_(phi_prime_n(name, n));
|
||||
}
|
||||
|
||||
|
||||
// GF(p_1) & GF(p_2) & ... & GF(p_n) if conj == true
|
||||
// GF(p_1) | GF(p_2) | ... | GF(p_n) if conj == false
|
||||
static formula
|
||||
GF_n(std::string name, int n, bool conj = true)
|
||||
{
|
||||
if (n <= 0)
|
||||
return conj ? formula::tt() : formula::ff();
|
||||
|
||||
formula result = nullptr;
|
||||
|
||||
op o = conj ? op::And : op::Or;
|
||||
|
||||
for (int i = 1; i <= n; ++i)
|
||||
{
|
||||
std::ostringstream p;
|
||||
p << name << i;
|
||||
formula f = G_(F_(formula::ap(p.str())));
|
||||
|
||||
if (result)
|
||||
result = formula::multop(o, {f, result});
|
||||
else
|
||||
result = f;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
// FG(p_1) | FG(p_2) | ... | FG(p_n) if conj == false
|
||||
// FG(p_1) & FG(p_2) & ... & FG(p_n) if conj == true
|
||||
static formula
|
||||
FG_n(std::string name, int n, bool conj = false)
|
||||
{
|
||||
if (n <= 0)
|
||||
return conj ? formula::tt() : formula::ff();
|
||||
|
||||
formula result = nullptr;
|
||||
|
||||
op o = conj ? op::And : op::Or;
|
||||
|
||||
for (int i = 1; i <= n; ++i)
|
||||
{
|
||||
std::ostringstream p;
|
||||
p << name << i;
|
||||
formula f = F_(G_(formula::ap(p.str())));
|
||||
|
||||
if (result)
|
||||
result = formula::multop(o, {f, result});
|
||||
else
|
||||
result = f;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
// (((p1 OP p2) OP p3)...OP pn) if right_assoc == false
|
||||
// (p1 OP (p2 OP (p3 OP (... pn) if right_assoc == true
|
||||
static formula
|
||||
bin_n(std::string name, int n, op o, bool right_assoc = false)
|
||||
{
|
||||
if (n <= 0)
|
||||
n = 1;
|
||||
|
||||
formula result = nullptr;
|
||||
|
||||
for (int i = 1; i <= n; ++i)
|
||||
{
|
||||
std::ostringstream p;
|
||||
p << name << (right_assoc ? (n + 1 - i) : i);
|
||||
formula f = formula::ap(p.str());
|
||||
if (!result)
|
||||
result = f;
|
||||
else if (right_assoc)
|
||||
result = formula::binop(o, f, result);
|
||||
else
|
||||
result = formula::binop(o, result, f);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
// (GF(p1)|FG(p2))&(GF(p2)|FG(p3))&...&(GF(pn)|FG(p{n+1}))"
|
||||
static formula
|
||||
R_n(std::string name, int n)
|
||||
{
|
||||
if (n <= 0)
|
||||
return formula::tt();
|
||||
|
||||
formula pi;
|
||||
|
||||
{
|
||||
std::ostringstream p;
|
||||
p << name << 1;
|
||||
pi = formula::ap(p.str());
|
||||
}
|
||||
|
||||
formula result = nullptr;
|
||||
|
||||
for (int i = 1; i <= n; ++i)
|
||||
{
|
||||
formula gf = G_(F_(pi));
|
||||
std::ostringstream p;
|
||||
p << name << i + 1;
|
||||
pi = formula::ap(p.str());
|
||||
|
||||
formula fg = F_(G_(pi));
|
||||
|
||||
formula f = Or_(gf, fg);
|
||||
|
||||
if (result)
|
||||
result = And_(f, result);
|
||||
else
|
||||
result = f;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
// (F(p1)|G(p2))&(F(p2)|G(p3))&...&(F(pn)|G(p{n+1}))"
|
||||
static formula
|
||||
Q_n(std::string name, int n)
|
||||
{
|
||||
if (n <= 0)
|
||||
return formula::tt();
|
||||
|
||||
formula pi;
|
||||
|
||||
{
|
||||
std::ostringstream p;
|
||||
p << name << 1;
|
||||
pi = formula::ap(p.str());
|
||||
}
|
||||
|
||||
formula result = nullptr;
|
||||
|
||||
for (int i = 1; i <= n; ++i)
|
||||
{
|
||||
formula f = F_(pi);
|
||||
|
||||
std::ostringstream p;
|
||||
p << name << i + 1;
|
||||
pi = formula::ap(p.str());
|
||||
|
||||
formula g = G_(pi);
|
||||
|
||||
f = Or_(f, g);
|
||||
|
||||
if (result)
|
||||
result = And_(f, result);
|
||||
else
|
||||
result = f;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
// OP(p1) | OP(p2) | ... | OP(Pn) if conj == false
|
||||
// OP(p1) & OP(p2) & ... & OP(Pn) if conj == true
|
||||
static formula
|
||||
combunop_n(std::string name, int n, op o, bool conj = false)
|
||||
{
|
||||
if (n <= 0)
|
||||
return conj ? formula::tt() : formula::ff();
|
||||
|
||||
formula result = nullptr;
|
||||
|
||||
op cop = conj ? op::And : op::Or;
|
||||
|
||||
for (int i = 1; i <= n; ++i)
|
||||
{
|
||||
std::ostringstream p;
|
||||
p << name << i;
|
||||
formula f = formula::unop(o, formula::ap(p.str()));
|
||||
|
||||
if (result)
|
||||
result = formula::multop(cop, {f, result});
|
||||
else
|
||||
result = f;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
// !((GF(p1)&GF(p2)&...&GF(pn))->G(q -> F(r)))
|
||||
// From "Fast LTL to Büchi Automata Translation" [gastin.01.cav]
|
||||
static formula
|
||||
fair_response(std::string p, std::string q, std::string r, int n)
|
||||
{
|
||||
formula fair = GF_n(p, n);
|
||||
formula resp = G_(Implies_(formula::ap(q), F_(formula::ap(r))));
|
||||
return Not_(Implies_(fair, resp));
|
||||
}
|
||||
|
||||
|
||||
// Builds X(X(...X(p))) with n occurrences of X.
|
||||
static formula
|
||||
X_n(formula p, int n)
|
||||
{
|
||||
assert(n >= 0);
|
||||
formula res = p;
|
||||
while (n--)
|
||||
res = X_(res);
|
||||
return res;
|
||||
}
|
||||
|
||||
// Based on LTLcounter.pl from Kristin Rozier.
|
||||
// http://shemesh.larc.nasa.gov/people/kyr/benchmarking_scripts/
|
||||
static formula
|
||||
ltl_counter(std::string bit, std::string marker, int n, bool linear)
|
||||
{
|
||||
formula b = formula::ap(bit);
|
||||
formula neg_b = Not_(b);
|
||||
formula m = formula::ap(marker);
|
||||
formula neg_m = Not_(m);
|
||||
|
||||
std::vector<formula> res(4);
|
||||
|
||||
// The marker starts with "1", followed by n-1 "0", then "1" again,
|
||||
// n-1 "0", etc.
|
||||
if (!linear)
|
||||
{
|
||||
// G(m -> X(!m)&XX(!m)&XXX(m)) [if n = 3]
|
||||
std::vector<formula> v(n);
|
||||
for (int i = 0; i + 1 < n; ++i)
|
||||
v[i] = X_n(neg_m, i + 1);
|
||||
v[n - 1] = X_n(m, n);
|
||||
res[0] = And_(m, G_(Implies_(m, formula::And(std::move(v)))));
|
||||
}
|
||||
else
|
||||
{
|
||||
// G(m -> X(!m & X(!m X(m)))) [if n = 3]
|
||||
formula p = m;
|
||||
for (int i = n - 1; i > 0; --i)
|
||||
p = And_(neg_m, X_(p));
|
||||
res[0] = And_(m, G_(Implies_(m, X_(p))));
|
||||
}
|
||||
|
||||
// All bits are initially zero.
|
||||
if (!linear)
|
||||
{
|
||||
// !b & X(!b) & XX(!b) [if n = 3]
|
||||
std::vector<formula> v2(n);
|
||||
for (int i = 0; i < n; ++i)
|
||||
v2[i] = X_n(neg_b, i);
|
||||
res[1] = formula::And(std::move(v2));
|
||||
}
|
||||
else
|
||||
{
|
||||
// !b & X(!b & X(!b)) [if n = 3]
|
||||
formula p = neg_b;
|
||||
for (int i = n - 1; i > 0; --i)
|
||||
p = And_(neg_b, X_(p));
|
||||
res[1] = p;
|
||||
}
|
||||
|
||||
#define AndX_(x, y) (linear ? X_(And_((x), (y))) : And_(X_(x), X_(y)))
|
||||
|
||||
// If the least significant bit is 0, it will be 1 at the next time,
|
||||
// and other bits stay the same.
|
||||
formula Xnm1_b = X_n(b, n - 1);
|
||||
formula Xn_b = X_(Xnm1_b);
|
||||
res[2] = G_(Implies_(And_(m, neg_b),
|
||||
AndX_(Xnm1_b, U_(And_(Not_(m), Equiv_(b, Xn_b)), m))));
|
||||
|
||||
// From the least significant bit to the first 0, all the bits
|
||||
// are flipped on the next value. Remaining bits are identical.
|
||||
formula Xnm1_negb = X_n(neg_b, n - 1);
|
||||
formula Xn_negb = X_(Xnm1_negb);
|
||||
res[3] = G_(Implies_(And_(m, b),
|
||||
AndX_(Xnm1_negb,
|
||||
U_(And_(And_(b, neg_m), Xn_negb),
|
||||
Or_(m, And_(And_(neg_m, neg_b),
|
||||
AndX_(Xnm1_b,
|
||||
U_(And_(neg_m,
|
||||
Equiv_(b, Xn_b)),
|
||||
m))))))));
|
||||
return formula::And(std::move(res));
|
||||
}
|
||||
|
||||
static formula
|
||||
ltl_counter_carry(std::string bit, std::string marker,
|
||||
std::string carry, int n, bool linear)
|
||||
{
|
||||
formula b = formula::ap(bit);
|
||||
formula neg_b = Not_(b);
|
||||
formula m = formula::ap(marker);
|
||||
formula neg_m = Not_(m);
|
||||
formula c = formula::ap(carry);
|
||||
formula neg_c = Not_(c);
|
||||
|
||||
std::vector<formula> res(6);
|
||||
|
||||
// The marker starts with "1", followed by n-1 "0", then "1" again,
|
||||
// n-1 "0", etc.
|
||||
if (!linear)
|
||||
{
|
||||
// G(m -> X(!m)&XX(!m)&XXX(m)) [if n = 3]
|
||||
std::vector<formula> v(n);
|
||||
for (int i = 0; i + 1 < n; ++i)
|
||||
v[i] = X_n(neg_m, i + 1);
|
||||
v[n - 1] = X_n(m, n);
|
||||
res[0] = And_(m, G_(Implies_(m, formula::And(std::move(v)))));
|
||||
}
|
||||
else
|
||||
{
|
||||
// G(m -> X(!m & X(!m X(m)))) [if n = 3]
|
||||
formula p = m;
|
||||
for (int i = n - 1; i > 0; --i)
|
||||
p = And_(neg_m, X_(p));
|
||||
res[0] = And_(m, G_(Implies_(m, X_(p))));
|
||||
}
|
||||
|
||||
// All bits are initially zero.
|
||||
if (!linear)
|
||||
{
|
||||
// !b & X(!b) & XX(!b) [if n = 3]
|
||||
std::vector<formula> v2(n);
|
||||
for (int i = 0; i < n; ++i)
|
||||
v2[i] = X_n(neg_b, i);
|
||||
res[1] = formula::And(std::move(v2));
|
||||
}
|
||||
else
|
||||
{
|
||||
// !b & X(!b & X(!b)) [if n = 3]
|
||||
formula p = neg_b;
|
||||
for (int i = n - 1; i > 0; --i)
|
||||
p = And_(neg_b, X_(p));
|
||||
res[1] = p;
|
||||
}
|
||||
|
||||
formula Xn_b = X_n(b, n);
|
||||
formula Xn_negb = X_n(neg_b, n);
|
||||
|
||||
// If m is 1 and b is 0 then c is 0 and n steps later b is 1.
|
||||
res[2] = G_(Implies_(And_(m, neg_b), And_(neg_c, Xn_b)));
|
||||
|
||||
// If m is 1 and b is 1 then c is 1 and n steps later b is 0.
|
||||
res[3] = G_(Implies_(And_(m, b), And_(c, Xn_negb)));
|
||||
|
||||
if (!linear)
|
||||
{
|
||||
// If there's no carry, then all of the bits stay the same n steps later.
|
||||
res[4] = G_(Implies_(And_(neg_c, X_(neg_m)),
|
||||
And_(X_(Not_(c)), Equiv_(X_(b), X_(Xn_b)))));
|
||||
|
||||
// If there's a carry, then add one: flip the bits of b and
|
||||
// adjust the carry.
|
||||
res[5] = G_(Implies_(c, And_(Implies_(X_(neg_b),
|
||||
And_(X_(neg_c), X_(Xn_b))),
|
||||
Implies_(X_(b),
|
||||
And_(X_(c), X_(Xn_negb))))));
|
||||
}
|
||||
else
|
||||
{
|
||||
// If there's no carry, then all of the bits stay the same n steps later.
|
||||
res[4] = G_(Implies_(And_(neg_c, X_(neg_m)),
|
||||
X_(And_(Not_(c), Equiv_(b, Xn_b)))));
|
||||
// If there's a carry, then add one: flip the bits of b and
|
||||
// adjust the carry.
|
||||
res[5] = G_(Implies_(c, X_(And_(Implies_(neg_b, And_(neg_c, Xn_b)),
|
||||
Implies_(b, And_(c, Xn_negb))))));
|
||||
}
|
||||
return formula::And(std::move(res));
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
output_pattern(int pattern, int n)
|
||||
{
|
||||
formula f = nullptr;
|
||||
switch (pattern)
|
||||
{
|
||||
// Keep this alphabetically-ordered!
|
||||
case OPT_AND_F:
|
||||
f = combunop_n("p", n, op::F, true);
|
||||
break;
|
||||
case OPT_AND_FG:
|
||||
f = FG_n("p", n, true);
|
||||
break;
|
||||
case OPT_AND_GF:
|
||||
f = GF_n("p", n, true);
|
||||
break;
|
||||
case OPT_CCJ_ALPHA:
|
||||
f = formula::And({E_n("p", n), E_n("q", n)});
|
||||
break;
|
||||
case OPT_CCJ_BETA:
|
||||
f = formula::And({N_n("p", n), N_n("q", n)});
|
||||
break;
|
||||
case OPT_CCJ_BETA_PRIME:
|
||||
f = formula::And({N_prime_n("p", n), N_prime_n("q", n)});
|
||||
break;
|
||||
case OPT_GH_Q:
|
||||
f = Q_n("p", n);
|
||||
break;
|
||||
case OPT_GH_R:
|
||||
f = R_n("p", n);
|
||||
break;
|
||||
case OPT_GO_THETA:
|
||||
f = fair_response("p", "q", "r", n);
|
||||
break;
|
||||
case OPT_OR_FG:
|
||||
f = FG_n("p", n, false);
|
||||
break;
|
||||
case OPT_OR_G:
|
||||
f = combunop_n("p", n, op::G, false);
|
||||
break;
|
||||
case OPT_OR_GF:
|
||||
f = GF_n("p", n, false);
|
||||
break;
|
||||
case OPT_R_LEFT:
|
||||
f = bin_n("p", n, op::R, false);
|
||||
break;
|
||||
case OPT_R_RIGHT:
|
||||
f = bin_n("p", n, op::R, true);
|
||||
break;
|
||||
case OPT_RV_COUNTER_CARRY:
|
||||
f = ltl_counter_carry("b", "m", "c", n, false);
|
||||
break;
|
||||
case OPT_RV_COUNTER_CARRY_LINEAR:
|
||||
f = ltl_counter_carry("b", "m", "c", n, true);
|
||||
break;
|
||||
case OPT_RV_COUNTER:
|
||||
f = ltl_counter("b", "m", n, false);
|
||||
break;
|
||||
case OPT_RV_COUNTER_LINEAR:
|
||||
f = ltl_counter("b", "m", n, true);
|
||||
break;
|
||||
case OPT_U_LEFT:
|
||||
f = bin_n("p", n, op::U, false);
|
||||
break;
|
||||
case OPT_U_RIGHT:
|
||||
f = bin_n("p", n, op::U, true);
|
||||
break;
|
||||
default:
|
||||
error(100, 0, "internal error: pattern not implemented");
|
||||
}
|
||||
|
||||
// Make sure we use only "p42"-style of atomic propositions
|
||||
// in lbt's output.
|
||||
if (output_format == lbt_output && !f.has_lbt_atomic_props())
|
||||
f = relabel(f, Pnn);
|
||||
|
||||
output_formula_checked(f, class_name[pattern - 1], n);
|
||||
}
|
||||
|
||||
static void
|
||||
run_jobs()
|
||||
{
|
||||
for (auto& j: jobs)
|
||||
{
|
||||
int inc = (j.range.max < j.range.min) ? -1 : 1;
|
||||
int n = j.range.min;
|
||||
for (;;)
|
||||
{
|
||||
output_pattern(j.pattern, n);
|
||||
if (n == j.range.max)
|
||||
break;
|
||||
n += inc;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
int
|
||||
main(int argc, char** argv)
|
||||
{
|
||||
setup(argv);
|
||||
|
||||
const argp ap = { options, parse_opt, nullptr, argp_program_doc,
|
||||
children, nullptr, nullptr };
|
||||
|
||||
if (int err = argp_parse(&ap, argc, argv, ARGP_NO_HELP, nullptr, nullptr))
|
||||
exit(err);
|
||||
|
||||
if (jobs.empty())
|
||||
error(1, 0, "Nothing to do. Try '%s --help' for more information.",
|
||||
program_name);
|
||||
|
||||
run_jobs();
|
||||
return 0;
|
||||
}
|
||||
180
spot/bin/ltl2tgba.cc
Normal file
180
spot/bin/ltl2tgba.cc
Normal file
|
|
@ -0,0 +1,180 @@
|
|||
// -*- coding: utf-8 -*-
|
||||
// Copyright (C) 2012, 2013, 2014, 2015 Laboratoire de Recherche et
|
||||
// Développement de l'Epita (LRDE).
|
||||
//
|
||||
// This file is part of Spot, a model checking library.
|
||||
//
|
||||
// Spot is free software; you can redistribute it and/or modify it
|
||||
// under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation; either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// Spot is distributed in the hope that it will be useful, but WITHOUT
|
||||
// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
||||
// or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public
|
||||
// License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
#include "common_sys.hh"
|
||||
|
||||
#include <string>
|
||||
#include <iostream>
|
||||
|
||||
#include <argp.h>
|
||||
#include "error.h"
|
||||
|
||||
#include "common_setup.hh"
|
||||
#include "common_r.hh"
|
||||
#include "common_cout.hh"
|
||||
#include "common_finput.hh"
|
||||
#include "common_output.hh"
|
||||
#include "common_aoutput.hh"
|
||||
#include "common_post.hh"
|
||||
|
||||
#include <spot/tl/formula.hh>
|
||||
#include <spot/tl/print.hh>
|
||||
#include <spot/twaalgos/translate.hh>
|
||||
#include <spot/misc/optionmap.hh>
|
||||
#include <spot/misc/timer.hh>
|
||||
|
||||
static const char argp_program_doc[] ="\
|
||||
Translate linear-time formulas (LTL/PSL) into Büchi automata.\n\n\
|
||||
By default it will apply all available optimizations to output \
|
||||
the smallest Transition-based Generalized Büchi Automata, \
|
||||
in GraphViz's format.\n\
|
||||
If multiple formulas are supplied, several automata will be output.";
|
||||
|
||||
|
||||
static const argp_option options[] =
|
||||
{
|
||||
/**************************************************/
|
||||
{ "%f", 0, nullptr, OPTION_DOC | OPTION_NO_USAGE,
|
||||
"the formula, in Spot's syntax", 4 },
|
||||
/**************************************************/
|
||||
{ "unambiguous", 'U', nullptr, 0, "output unambiguous automata", 2 },
|
||||
{ nullptr, 0, nullptr, 0, "Miscellaneous options:", -1 },
|
||||
{ "extra-options", 'x', "OPTS", 0,
|
||||
"fine-tuning options (see spot-x (7))", 0 },
|
||||
{ nullptr, 0, nullptr, 0, nullptr, 0 }
|
||||
};
|
||||
|
||||
const struct argp_child children[] =
|
||||
{
|
||||
{ &finput_argp, 0, nullptr, 1 },
|
||||
{ &aoutput_argp, 0, nullptr, 0 },
|
||||
{ &aoutput_o_format_argp, 0, nullptr, 0 },
|
||||
{ &post_argp, 0, nullptr, 0 },
|
||||
{ &misc_argp, 0, nullptr, -1 },
|
||||
{ nullptr, 0, nullptr, 0 }
|
||||
};
|
||||
|
||||
static spot::option_map extra_options;
|
||||
static spot::postprocessor::output_pref unambig = 0;
|
||||
|
||||
static int
|
||||
parse_opt(int key, char* arg, struct argp_state*)
|
||||
{
|
||||
// This switch is alphabetically-ordered.
|
||||
switch (key)
|
||||
{
|
||||
case 'U':
|
||||
unambig = spot::postprocessor::Unambiguous;
|
||||
break;
|
||||
case 'x':
|
||||
{
|
||||
const char* opt = extra_options.parse_options(arg);
|
||||
if (opt)
|
||||
error(2, 0, "failed to parse --options near '%s'", opt);
|
||||
}
|
||||
break;
|
||||
case ARGP_KEY_ARG:
|
||||
// FIXME: use stat() to distinguish filename from string?
|
||||
jobs.emplace_back(arg, false);
|
||||
break;
|
||||
|
||||
default:
|
||||
return ARGP_ERR_UNKNOWN;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
namespace
|
||||
{
|
||||
class trans_processor: public job_processor
|
||||
{
|
||||
public:
|
||||
spot::translator& trans;
|
||||
automaton_printer printer;
|
||||
|
||||
trans_processor(spot::translator& trans)
|
||||
: trans(trans), printer(ltl_input)
|
||||
{
|
||||
}
|
||||
|
||||
int
|
||||
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 formula type can
|
||||
// represent more than PSL formula, let's make this
|
||||
// future-proof.
|
||||
if (!f.is_psl_formula())
|
||||
{
|
||||
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());
|
||||
}
|
||||
|
||||
spot::stopwatch sw;
|
||||
sw.start();
|
||||
auto aut = trans.run(&f);
|
||||
const double translation_time = sw.stop();
|
||||
|
||||
printer.print(aut, f, filename, linenum, translation_time, nullptr);
|
||||
return 0;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
int
|
||||
main(int argc, char** argv)
|
||||
{
|
||||
// By default we name automata using the formula.
|
||||
opt_name = "%f";
|
||||
|
||||
setup(argv);
|
||||
|
||||
const argp ap = { options, parse_opt, "[FORMULA...]",
|
||||
argp_program_doc, children, nullptr, nullptr };
|
||||
|
||||
simplification_level = 3;
|
||||
|
||||
if (int err = argp_parse(&ap, argc, argv, ARGP_NO_HELP, nullptr, nullptr))
|
||||
exit(err);
|
||||
|
||||
if (jobs.empty())
|
||||
error(2, 0, "No formula to translate? Run '%s --help' for usage.",
|
||||
program_name);
|
||||
|
||||
spot::translator trans(&extra_options);
|
||||
trans.set_pref(pref | comp | sbacc | unambig);
|
||||
trans.set_type(type);
|
||||
trans.set_level(level);
|
||||
|
||||
try
|
||||
{
|
||||
trans_processor processor(trans);
|
||||
if (processor.run())
|
||||
return 2;
|
||||
}
|
||||
catch (const std::runtime_error& e)
|
||||
{
|
||||
error(2, 0, "%s", e.what());
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
242
spot/bin/ltl2tgta.cc
Normal file
242
spot/bin/ltl2tgta.cc
Normal file
|
|
@ -0,0 +1,242 @@
|
|||
// -*- coding: utf-8 -*-
|
||||
// Copyright (C) 2012, 2013, 2014, 2015 Laboratoire de Recherche et
|
||||
// Développement de l'Epita (LRDE).
|
||||
//
|
||||
// This file is part of Spot, a model checking library.
|
||||
//
|
||||
// Spot is free software; you can redistribute it and/or modify it
|
||||
// under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation; either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// Spot is distributed in the hope that it will be useful, but WITHOUT
|
||||
// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
||||
// or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public
|
||||
// License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
#include "common_sys.hh"
|
||||
|
||||
#include <string>
|
||||
#include <iostream>
|
||||
#include <fstream>
|
||||
|
||||
#include <argp.h>
|
||||
#include "error.h"
|
||||
|
||||
#include "common_setup.hh"
|
||||
#include "common_r.hh"
|
||||
#include "common_cout.hh"
|
||||
#include "common_finput.hh"
|
||||
#include "common_post.hh"
|
||||
|
||||
#include <spot/tl/parse.hh>
|
||||
#include <spot/tl/print.hh>
|
||||
#include <spot/tl/simplify.hh>
|
||||
#include <spot/twaalgos/dot.hh>
|
||||
#include <spot/twaalgos/ltl2tgba_fm.hh>
|
||||
#include <spot/twaalgos/translate.hh>
|
||||
#include <spot/twa/bddprint.hh>
|
||||
|
||||
#include <spot/taalgos/tgba2ta.hh>
|
||||
#include <spot/taalgos/dot.hh>
|
||||
#include <spot/taalgos/minimize.hh>
|
||||
#include <spot/misc/optionmap.hh>
|
||||
|
||||
const char argp_program_doc[] ="\
|
||||
Translate linear-time formulas (LTL/PSL) into Testing Automata.\n\n\
|
||||
By default it outputs a transition-based generalized Testing Automaton \
|
||||
the smallest Transition-based Generalized Büchi Automata, \
|
||||
in GraphViz's format. The input formula is assumed to be \
|
||||
stuttering-insensitive.";
|
||||
|
||||
enum {
|
||||
OPT_GTA = 1,
|
||||
OPT_INIT,
|
||||
OPT_SPLV,
|
||||
OPT_SPNO,
|
||||
OPT_TA,
|
||||
OPT_TGTA,
|
||||
};
|
||||
|
||||
static const argp_option options[] =
|
||||
{
|
||||
/**************************************************/
|
||||
{ nullptr, 0, nullptr, 0, "Automaton type:", 1 },
|
||||
{ "tgta", OPT_TGTA, nullptr, 0,
|
||||
"Transition-based Generalized Testing Automaton (default)", 0 },
|
||||
{ "ta", OPT_TA, nullptr, 0, "Testing Automaton", 0 },
|
||||
{ "gta", OPT_GTA, nullptr, 0, "Generalized Testing Automaton", 0 },
|
||||
/**************************************************/
|
||||
{ nullptr, 0, nullptr, 0, "Options for TA and GTA creation:", 3 },
|
||||
{ "single-pass-lv", OPT_SPLV, nullptr, 0,
|
||||
"add an artificial livelock state to obtain a single-pass (G)TA", 0 },
|
||||
{ "single-pass", OPT_SPNO, nullptr, 0,
|
||||
"create a single-pass (G)TA without artificial livelock state", 0 },
|
||||
{ "multiple-init", OPT_INIT, nullptr, 0,
|
||||
"do not create the fake initial state", 0 },
|
||||
/**************************************************/
|
||||
{ nullptr, 0, nullptr, 0, "Output options:", 4 },
|
||||
{ "utf8", '8', nullptr, 0, "enable UTF-8 characters in output", 0 },
|
||||
/**************************************************/
|
||||
{ nullptr, 0, nullptr, 0, "Miscellaneous options:", -1 },
|
||||
{ "extra-options", 'x', "OPTS", 0,
|
||||
"fine-tuning options (see spot-x (7))", 0 },
|
||||
{ nullptr, 0, nullptr, 0, nullptr, 0 }
|
||||
};
|
||||
|
||||
const struct argp_child children[] =
|
||||
{
|
||||
{ &finput_argp, 0, nullptr, 1 },
|
||||
{ &post_argp, 0, nullptr, 20 },
|
||||
{ &misc_argp, 0, nullptr, -1 },
|
||||
{ nullptr, 0, nullptr, 0 }
|
||||
};
|
||||
|
||||
enum ta_types { TGTA, GTA, TA };
|
||||
ta_types ta_type = TGTA;
|
||||
|
||||
bool utf8 = false;
|
||||
const char* stats = "";
|
||||
spot::option_map extra_options;
|
||||
bool opt_with_artificial_initial_state = true;
|
||||
bool opt_single_pass_emptiness_check = false;
|
||||
bool opt_with_artificial_livelock = false;
|
||||
|
||||
static int
|
||||
parse_opt(int key, char* arg, struct argp_state*)
|
||||
{
|
||||
// This switch is alphabetically-ordered.
|
||||
switch (key)
|
||||
{
|
||||
case '8':
|
||||
spot::enable_utf8();
|
||||
break;
|
||||
case 'B':
|
||||
type = spot::postprocessor::BA;
|
||||
break;
|
||||
case 'x':
|
||||
{
|
||||
const char* opt = extra_options.parse_options(arg);
|
||||
if (opt)
|
||||
error(2, 0, "failed to parse --options near '%s'", opt);
|
||||
}
|
||||
break;
|
||||
case OPT_TGTA:
|
||||
ta_type = TGTA;
|
||||
type = spot::postprocessor::TGBA;
|
||||
break;
|
||||
case OPT_GTA:
|
||||
ta_type = GTA;
|
||||
type = spot::postprocessor::TGBA;
|
||||
break;
|
||||
case OPT_TA:
|
||||
ta_type = TA;
|
||||
type = spot::postprocessor::BA;
|
||||
break;
|
||||
case OPT_INIT:
|
||||
opt_with_artificial_initial_state = false;
|
||||
break;
|
||||
case OPT_SPLV:
|
||||
opt_with_artificial_livelock = true;
|
||||
break;
|
||||
case OPT_SPNO:
|
||||
opt_single_pass_emptiness_check = true;
|
||||
break;
|
||||
case ARGP_KEY_ARG:
|
||||
// FIXME: use stat() to distinguish filename from string?
|
||||
jobs.emplace_back(arg, false);
|
||||
break;
|
||||
|
||||
default:
|
||||
return ARGP_ERR_UNKNOWN;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
namespace
|
||||
{
|
||||
class trans_processor: public job_processor
|
||||
{
|
||||
public:
|
||||
spot::translator& trans;
|
||||
|
||||
trans_processor(spot::translator& trans)
|
||||
: trans(trans)
|
||||
{
|
||||
}
|
||||
|
||||
int
|
||||
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 formula type can
|
||||
// represent more than PSL formula, let's make this
|
||||
// future-proof.
|
||||
if (!f.is_psl_formula())
|
||||
{
|
||||
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());
|
||||
}
|
||||
|
||||
bdd ap_set = atomic_prop_collect_as_bdd(f, aut);
|
||||
|
||||
if (ta_type != TGTA)
|
||||
{
|
||||
auto testing_automaton =
|
||||
tgba_to_ta(aut, ap_set, type == spot::postprocessor::BA,
|
||||
opt_with_artificial_initial_state,
|
||||
opt_single_pass_emptiness_check,
|
||||
opt_with_artificial_livelock);
|
||||
if (level != spot::postprocessor::Low)
|
||||
testing_automaton = spot::minimize_ta(testing_automaton);
|
||||
spot::print_dot(std::cout, testing_automaton);
|
||||
}
|
||||
else
|
||||
{
|
||||
auto tgta = tgba_to_tgta(aut, ap_set);
|
||||
if (level != spot::postprocessor::Low)
|
||||
tgta = spot::minimize_tgta(tgta);
|
||||
spot::print_dot(std::cout, tgta->get_ta());
|
||||
}
|
||||
flush_cout();
|
||||
return 0;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
int
|
||||
main(int argc, char** argv)
|
||||
{
|
||||
setup(argv);
|
||||
|
||||
const argp ap = { options, parse_opt, "[FORMULA...]",
|
||||
argp_program_doc, children, nullptr, nullptr };
|
||||
|
||||
simplification_level = 3;
|
||||
|
||||
if (int err = argp_parse(&ap, argc, argv, ARGP_NO_HELP, nullptr, nullptr))
|
||||
exit(err);
|
||||
|
||||
if (jobs.empty())
|
||||
error(2, 0, "No formula to translate? Run '%s --help' for usage.",
|
||||
program_name);
|
||||
|
||||
spot::translator trans(&extra_options);
|
||||
trans.set_pref(pref | comp | sbacc);
|
||||
trans.set_type(type);
|
||||
trans.set_level(level);
|
||||
|
||||
trans_processor processor(trans);
|
||||
if (processor.run())
|
||||
return 2;
|
||||
return 0;
|
||||
}
|
||||
1479
spot/bin/ltlcross.cc
Normal file
1479
spot/bin/ltlcross.cc
Normal file
File diff suppressed because it is too large
Load diff
337
spot/bin/ltldo.cc
Normal file
337
spot/bin/ltldo.cc
Normal file
|
|
@ -0,0 +1,337 @@
|
|||
// -*- coding: utf-8 -*-
|
||||
// Copyright (C) 2015 Laboratoire de Recherche et Développement de
|
||||
// l'Epita (LRDE).
|
||||
//
|
||||
// This file is part of Spot, a model checking library.
|
||||
//
|
||||
// Spot is free software; you can redistribute it and/or modify it
|
||||
// under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation; either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// Spot is distributed in the hope that it will be useful, but WITHOUT
|
||||
// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
||||
// or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public
|
||||
// License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
#include "common_sys.hh"
|
||||
|
||||
#include <string>
|
||||
#include <iostream>
|
||||
#include <sstream>
|
||||
#include <fstream>
|
||||
#include <sys/wait.h>
|
||||
|
||||
#include "error.h"
|
||||
|
||||
#include "common_setup.hh"
|
||||
#include "common_cout.hh"
|
||||
#include "common_conv.hh"
|
||||
#include "common_finput.hh"
|
||||
#include "common_aoutput.hh"
|
||||
#include "common_post.hh"
|
||||
#include "common_trans.hh"
|
||||
#include "common_hoaread.hh"
|
||||
|
||||
#include <spot/tl/relabel.hh>
|
||||
#include <spot/misc/bareword.hh>
|
||||
#include <spot/misc/timer.hh>
|
||||
#include <spot/twaalgos/lbtt.hh>
|
||||
#include <spot/twaalgos/relabel.hh>
|
||||
#include <spot/twaalgos/totgba.hh>
|
||||
#include <spot/parseaut/public.hh>
|
||||
|
||||
const char argp_program_doc[] ="\
|
||||
Run LTL/PSL formulas through another program, performing conversion\n\
|
||||
of input and output as required.";
|
||||
|
||||
static const argp_option options[] =
|
||||
{
|
||||
{ nullptr, 0, nullptr, 0, "Miscellaneous options:", -1 },
|
||||
{ nullptr, 0, nullptr, 0, nullptr, 0 }
|
||||
};
|
||||
|
||||
|
||||
static const argp_option more_o_format[] =
|
||||
{
|
||||
{ "%R", 0, nullptr, OPTION_DOC | OPTION_NO_USAGE,
|
||||
"serial number of the formula translated", 0 },
|
||||
{ "%T", 0, nullptr, OPTION_DOC | OPTION_NO_USAGE,
|
||||
"tool used for translation", 0 },
|
||||
{ "%f", 0, nullptr, OPTION_DOC | OPTION_NO_USAGE,
|
||||
"formula translated", 0 },
|
||||
{ nullptr, 0, nullptr, 0, nullptr, 0 }
|
||||
};
|
||||
|
||||
// This is not very elegant, but we need to add the above %-escape
|
||||
// sequences to those of aoutput_o_fromat_argp for the --help output.
|
||||
// So far I've failed to instruct argp to merge those two lists into a
|
||||
// single block.
|
||||
static const struct argp*
|
||||
build_percent_list()
|
||||
{
|
||||
const argp_option* iter = aoutput_o_format_argp.options;
|
||||
unsigned count = 0;
|
||||
while (iter->name || iter->doc)
|
||||
{
|
||||
++count;
|
||||
++iter;
|
||||
}
|
||||
|
||||
unsigned s = count * sizeof(argp_option);
|
||||
argp_option* d =
|
||||
static_cast<argp_option*>(malloc(sizeof(more_o_format) + s));
|
||||
memcpy(d, aoutput_o_format_argp.options, s);
|
||||
memcpy(d + count, more_o_format, sizeof(more_o_format));
|
||||
|
||||
static const struct argp more_o_format_argp =
|
||||
{ d, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr };
|
||||
return &more_o_format_argp;
|
||||
}
|
||||
|
||||
const struct argp_child children[] =
|
||||
{
|
||||
{ &hoaread_argp, 0, "Parsing of automata:", 3 },
|
||||
{ &finput_argp, 0, nullptr, 1 },
|
||||
{ &trans_argp, 0, nullptr, 3 },
|
||||
{ &aoutput_argp, 0, nullptr, 4 },
|
||||
{ build_percent_list(), 0, nullptr, 5 },
|
||||
{ &misc_argp, 0, nullptr, -1 },
|
||||
{ nullptr, 0, nullptr, 0 }
|
||||
};
|
||||
|
||||
static int
|
||||
parse_opt(int key, char* arg, struct argp_state*)
|
||||
{
|
||||
switch (key)
|
||||
{
|
||||
case ARGP_KEY_ARG:
|
||||
translators.push_back(arg);
|
||||
break;
|
||||
default:
|
||||
return ARGP_ERR_UNKNOWN;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
namespace
|
||||
{
|
||||
class xtranslator_runner: public translator_runner
|
||||
{
|
||||
public:
|
||||
xtranslator_runner(spot::bdd_dict_ptr dict)
|
||||
: translator_runner(dict, true)
|
||||
{
|
||||
}
|
||||
|
||||
spot::twa_graph_ptr
|
||||
translate(unsigned int translator_num, bool& problem, double& duration)
|
||||
{
|
||||
output.reset(translator_num);
|
||||
|
||||
std::ostringstream command;
|
||||
format(command, translators[translator_num].cmd);
|
||||
|
||||
std::string cmd = command.str();
|
||||
//std::cerr << "Running [" << l << translator_num << "]: "
|
||||
// << cmd << std::endl;
|
||||
spot::stopwatch sw;
|
||||
sw.start();
|
||||
int es = exec_with_timeout(cmd.c_str());
|
||||
duration = sw.stop();
|
||||
|
||||
spot::twa_graph_ptr res = nullptr;
|
||||
if (timed_out)
|
||||
{
|
||||
problem = false; // A timeout is considered benign
|
||||
std::cerr << "warning: timeout during execution of command \""
|
||||
<< cmd << "\"\n";
|
||||
++timeout_count;
|
||||
}
|
||||
else if (WIFSIGNALED(es))
|
||||
{
|
||||
problem = true;
|
||||
es = WTERMSIG(es);
|
||||
std::cerr << "error: execution of command \"" << cmd
|
||||
<< "\" terminated by signal " << es << ".\n";
|
||||
}
|
||||
else if (WIFEXITED(es) && WEXITSTATUS(es) != 0)
|
||||
{
|
||||
problem = true;
|
||||
es = WEXITSTATUS(es);
|
||||
std::cerr << "error: execution of command \"" << cmd
|
||||
<< "\" returned exit code " << es << ".\n";
|
||||
}
|
||||
else if (output.val())
|
||||
{
|
||||
problem = false;
|
||||
auto aut = spot::parse_aut(output.val()->name(), dict,
|
||||
spot::default_environment::instance(),
|
||||
opt_parse);
|
||||
if (!aut->errors.empty())
|
||||
{
|
||||
problem = true;
|
||||
std::cerr << "error: failed to parse the automaton "
|
||||
"produced by \"" << cmd << "\".\n";
|
||||
aut->format_errors(std::cerr);
|
||||
res = nullptr;
|
||||
}
|
||||
else if (aut->aborted)
|
||||
{
|
||||
problem = true;
|
||||
std::cerr << "error: command \"" << cmd
|
||||
<< "\" aborted its output.\n";
|
||||
res = nullptr;
|
||||
}
|
||||
else
|
||||
{
|
||||
res = aut->aut;
|
||||
}
|
||||
}
|
||||
else // No automaton output
|
||||
{
|
||||
problem = false;
|
||||
res = nullptr;
|
||||
}
|
||||
|
||||
output.cleanup();
|
||||
return res;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
class processor: public job_processor
|
||||
{
|
||||
spot::bdd_dict_ptr dict = spot::make_bdd_dict();
|
||||
xtranslator_runner runner;
|
||||
automaton_printer printer;
|
||||
spot::postprocessor& post;
|
||||
spot::printable_value<std::string> cmdname;
|
||||
spot::printable_value<unsigned> roundval;
|
||||
spot::printable_value<std::string> inputf;
|
||||
|
||||
public:
|
||||
processor(spot::postprocessor& post)
|
||||
: runner(dict), post(post)
|
||||
{
|
||||
printer.add_stat('T', &cmdname);
|
||||
printer.add_stat('R', &roundval);
|
||||
printer.add_stat('f', &inputf);
|
||||
}
|
||||
|
||||
~processor()
|
||||
{
|
||||
}
|
||||
|
||||
int
|
||||
process_string(const std::string& input,
|
||||
const char* filename,
|
||||
int linenum)
|
||||
{
|
||||
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::format_parse_errors(std::cerr, input, pel);
|
||||
return 1;
|
||||
}
|
||||
|
||||
inputf = input;
|
||||
process_formula(f, filename, linenum);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
int
|
||||
process_formula(spot::formula f,
|
||||
const char* filename = nullptr, int linenum = 0)
|
||||
{
|
||||
std::unique_ptr<spot::relabeling_map> relmap;
|
||||
|
||||
// If atomic propositions are incompatible with one of the
|
||||
// output, relabel the formula.
|
||||
if ((!f.has_lbt_atomic_props() &&
|
||||
(runner.has('l') || runner.has('L') || runner.has('T')))
|
||||
|| (!f.has_spin_atomic_props() &&
|
||||
(runner.has('s') || runner.has('S'))))
|
||||
{
|
||||
relmap.reset(new spot::relabeling_map);
|
||||
f = spot::relabel(f, spot::Pnn, relmap.get());
|
||||
}
|
||||
|
||||
static unsigned round = 1;
|
||||
runner.round_formula(f, round);
|
||||
|
||||
unsigned ts = translators.size();
|
||||
for (unsigned t = 0; t < ts; ++t)
|
||||
{
|
||||
bool problem;
|
||||
double translation_time;
|
||||
auto aut = runner.translate(t, problem, translation_time);
|
||||
if (problem)
|
||||
error_at_line(2, 0, filename, linenum, "aborting here");
|
||||
if (aut)
|
||||
{
|
||||
if (relmap)
|
||||
relabel_here(aut, relmap.get());
|
||||
aut = post.run(aut, f);
|
||||
cmdname = translators[t].name;
|
||||
roundval = round;
|
||||
printer.print(aut, f, filename, linenum, translation_time,
|
||||
nullptr);
|
||||
};
|
||||
}
|
||||
spot::cleanup_tmpfiles();
|
||||
++round;
|
||||
return 0;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
int
|
||||
main(int argc, char** argv)
|
||||
{
|
||||
setup(argv);
|
||||
|
||||
const argp ap = { options, parse_opt, "[COMMANDFMT...]",
|
||||
argp_program_doc, children, nullptr, nullptr };
|
||||
|
||||
// Disable post-processing as much as possible by default.
|
||||
level = spot::postprocessor::Low;
|
||||
pref = spot::postprocessor::Any;
|
||||
type = spot::postprocessor::Generic;
|
||||
if (int err = argp_parse(&ap, argc, argv, ARGP_NO_HELP, nullptr, nullptr))
|
||||
exit(err);
|
||||
|
||||
if (jobs.empty())
|
||||
jobs.emplace_back("-", 1);
|
||||
|
||||
if (translators.empty())
|
||||
error(2, 0, "No translator to run? Run '%s --help' for usage.",
|
||||
program_name);
|
||||
|
||||
setup_sig_handler();
|
||||
|
||||
spot::postprocessor post;
|
||||
post.set_pref(pref | comp | sbacc);
|
||||
post.set_type(type);
|
||||
post.set_level(level);
|
||||
|
||||
try
|
||||
{
|
||||
processor p(post);
|
||||
if (p.run())
|
||||
return 2;
|
||||
}
|
||||
catch (const std::runtime_error& e)
|
||||
{
|
||||
error(2, 0, "%s", e.what());
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
665
spot/bin/ltlfilt.cc
Normal file
665
spot/bin/ltlfilt.cc
Normal file
|
|
@ -0,0 +1,665 @@
|
|||
// -*- coding: utf-8 -*-
|
||||
// Copyright (C) 2012, 2013, 2014, 2015 Laboratoire de Recherche et
|
||||
// Développement de l'Epita (LRDE).
|
||||
//
|
||||
// This file is part of Spot, a model checking library.
|
||||
//
|
||||
// Spot is free software; you can redistribute it and/or modify it
|
||||
// under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation; either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// Spot is distributed in the hope that it will be useful, but WITHOUT
|
||||
// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
||||
// or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public
|
||||
// License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
#include "common_sys.hh"
|
||||
|
||||
#include <cstdlib>
|
||||
#include <string>
|
||||
#include <iostream>
|
||||
#include <fstream>
|
||||
#include <argp.h>
|
||||
#include <cstring>
|
||||
#include "error.h"
|
||||
|
||||
#include "common_setup.hh"
|
||||
#include "common_finput.hh"
|
||||
#include "common_output.hh"
|
||||
#include "common_cout.hh"
|
||||
#include "common_conv.hh"
|
||||
#include "common_r.hh"
|
||||
|
||||
#include <spot/misc/hash.hh>
|
||||
#include <spot/tl/simplify.hh>
|
||||
#include <spot/tl/length.hh>
|
||||
#include <spot/tl/relabel.hh>
|
||||
#include <spot/tl/unabbrev.hh>
|
||||
#include <spot/tl/remove_x.hh>
|
||||
#include <spot/tl/apcollect.hh>
|
||||
#include <spot/tl/exclusive.hh>
|
||||
#include <spot/tl/print.hh>
|
||||
#include <spot/twaalgos/ltl2tgba_fm.hh>
|
||||
#include <spot/twaalgos/minimize.hh>
|
||||
#include <spot/twaalgos/strength.hh>
|
||||
#include <spot/twaalgos/stutter.hh>
|
||||
|
||||
const char argp_program_doc[] ="\
|
||||
Read a list of formulas and output them back after some optional processing.\v\
|
||||
Exit status:\n\
|
||||
0 if some formulas were output (skipped syntax errors do not count)\n\
|
||||
1 if no formulas were output (no match)\n\
|
||||
2 if any error has been reported";
|
||||
|
||||
enum {
|
||||
OPT_AP_N = 256,
|
||||
OPT_BOOLEAN,
|
||||
OPT_BOOLEAN_TO_ISOP,
|
||||
OPT_BSIZE_MAX,
|
||||
OPT_BSIZE_MIN,
|
||||
OPT_DEFINE,
|
||||
OPT_DROP_ERRORS,
|
||||
OPT_EQUIVALENT_TO,
|
||||
OPT_EXCLUSIVE_AP,
|
||||
OPT_EVENTUAL,
|
||||
OPT_GUARANTEE,
|
||||
OPT_IGNORE_ERRORS,
|
||||
OPT_IMPLIED_BY,
|
||||
OPT_IMPLY,
|
||||
OPT_LTL,
|
||||
OPT_NEGATE,
|
||||
OPT_NNF,
|
||||
OPT_OBLIGATION,
|
||||
OPT_RELABEL,
|
||||
OPT_RELABEL_BOOL,
|
||||
OPT_REMOVE_WM,
|
||||
OPT_REMOVE_X,
|
||||
OPT_SAFETY,
|
||||
OPT_SIZE_MAX,
|
||||
OPT_SIZE_MIN,
|
||||
OPT_SKIP_ERRORS,
|
||||
OPT_STUTTER_INSENSITIVE,
|
||||
OPT_SYNTACTIC_GUARANTEE,
|
||||
OPT_SYNTACTIC_OBLIGATION,
|
||||
OPT_SYNTACTIC_PERSISTENCE,
|
||||
OPT_SYNTACTIC_RECURRENCE,
|
||||
OPT_SYNTACTIC_SAFETY,
|
||||
OPT_SYNTACTIC_SI,
|
||||
OPT_UNABBREVIATE,
|
||||
OPT_UNIVERSAL,
|
||||
};
|
||||
|
||||
static const argp_option options[] =
|
||||
{
|
||||
/**************************************************/
|
||||
{ nullptr, 0, nullptr, 0, "Error handling:", 2 },
|
||||
{ "skip-errors", OPT_SKIP_ERRORS, nullptr, 0,
|
||||
"output erroneous lines as-is without processing", 0 },
|
||||
{ "drop-errors", OPT_DROP_ERRORS, nullptr, 0,
|
||||
"discard erroneous lines (default)", 0 },
|
||||
{ "ignore-errors", OPT_IGNORE_ERRORS, nullptr, 0,
|
||||
"do not report syntax errors", 0 },
|
||||
/**************************************************/
|
||||
{ nullptr, 0, nullptr, 0, "Transformation options:", 3 },
|
||||
{ "negate", OPT_NEGATE, nullptr, 0, "negate each formula", 0 },
|
||||
{ "nnf", OPT_NNF, nullptr, 0,
|
||||
"rewrite formulas in negative normal form", 0 },
|
||||
{ "relabel", OPT_RELABEL, "abc|pnn", OPTION_ARG_OPTIONAL,
|
||||
"relabel all atomic propositions, alphabetically unless " \
|
||||
"specified otherwise", 0 },
|
||||
{ "relabel-bool", OPT_RELABEL_BOOL, "abc|pnn", OPTION_ARG_OPTIONAL,
|
||||
"relabel Boolean subexpressions, alphabetically unless " \
|
||||
"specified otherwise", 0 },
|
||||
{ "define", OPT_DEFINE, "FILENAME", OPTION_ARG_OPTIONAL,
|
||||
"when used with --relabel or --relabel-bool, output the relabeling map "
|
||||
"using #define statements", 0 },
|
||||
{ "remove-wm", OPT_REMOVE_WM, nullptr, 0,
|
||||
"rewrite operators W and M using U and R (this is an alias for "
|
||||
"--unabbreviate=WM)", 0 },
|
||||
{ "boolean-to-isop", OPT_BOOLEAN_TO_ISOP, nullptr, 0,
|
||||
"rewrite Boolean subformulas as irredundant sum of products "
|
||||
"(implies at least -r1)", 0 },
|
||||
{ "remove-x", OPT_REMOVE_X, nullptr, 0,
|
||||
"remove X operators (valid only for stutter-insensitive properties)",
|
||||
0 },
|
||||
{ "unabbreviate", OPT_UNABBREVIATE, "STR", OPTION_ARG_OPTIONAL,
|
||||
"remove all occurrences of the operators specified by STR, which "
|
||||
"must be a substring of \"eFGiMRW^\", where 'e', 'i', and '^' stand "
|
||||
"respectively for <->, ->, and xor. If no argument is passed, "
|
||||
"the subset \"eFGiMW^\" is used.", 0 },
|
||||
{ "exclusive-ap", OPT_EXCLUSIVE_AP, "AP,AP,...", 0,
|
||||
"if any of those APs occur in the formula, add a term ensuring "
|
||||
"two of them may not be true at the same time. Use this option "
|
||||
"multiple times to declare independent groups of exclusive "
|
||||
"propositions.", 0 },
|
||||
DECLARE_OPT_R,
|
||||
LEVEL_DOC(4),
|
||||
/**************************************************/
|
||||
{ nullptr, 0, nullptr, 0,
|
||||
"Filtering options (matching is done after transformation):", 5 },
|
||||
{ "ltl", OPT_LTL, nullptr, 0,
|
||||
"match only LTL formulas (no PSL operator)", 0 },
|
||||
{ "boolean", OPT_BOOLEAN, nullptr, 0, "match Boolean formulas", 0 },
|
||||
{ "eventual", OPT_EVENTUAL, nullptr, 0, "match pure eventualities", 0 },
|
||||
{ "universal", OPT_UNIVERSAL, nullptr, 0,
|
||||
"match purely universal formulas", 0 },
|
||||
{ "syntactic-safety", OPT_SYNTACTIC_SAFETY, nullptr, 0,
|
||||
"match syntactic-safety formulas", 0 },
|
||||
{ "syntactic-guarantee", OPT_SYNTACTIC_GUARANTEE, nullptr, 0,
|
||||
"match syntactic-guarantee formulas", 0 },
|
||||
{ "syntactic-obligation", OPT_SYNTACTIC_OBLIGATION, nullptr, 0,
|
||||
"match syntactic-obligation formulas", 0 },
|
||||
{ "syntactic-recurrence", OPT_SYNTACTIC_RECURRENCE, nullptr, 0,
|
||||
"match syntactic-recurrence formulas", 0 },
|
||||
{ "syntactic-persistence", OPT_SYNTACTIC_PERSISTENCE, nullptr, 0,
|
||||
"match syntactic-persistence formulas", 0 },
|
||||
{ "syntactic-stutter-invariant", OPT_SYNTACTIC_SI, nullptr, 0,
|
||||
"match stutter-invariant formulas syntactically (LTL-X or siPSL)", 0 },
|
||||
{ "nox", 0, nullptr, OPTION_ALIAS, nullptr, 0 },
|
||||
{ "safety", OPT_SAFETY, nullptr, 0,
|
||||
"match safety formulas (even pathological)", 0 },
|
||||
{ "guarantee", OPT_GUARANTEE, nullptr, 0,
|
||||
"match guarantee formulas (even pathological)", 0 },
|
||||
{ "obligation", OPT_OBLIGATION, nullptr, 0,
|
||||
"match obligation formulas (even pathological)", 0 },
|
||||
{ "size-max", OPT_SIZE_MAX, "INT", 0,
|
||||
"match formulas with size <= INT", 0 },
|
||||
{ "size-min", OPT_SIZE_MIN, "INT", 0,
|
||||
"match formulas with size >= INT", 0 },
|
||||
{ "bsize-max", OPT_BSIZE_MAX, "INT", 0,
|
||||
"match formulas with Boolean size <= INT", 0 },
|
||||
{ "bsize-min", OPT_BSIZE_MIN, "INT", 0,
|
||||
"match formulas with Boolean size >= INT", 0 },
|
||||
{ "implied-by", OPT_IMPLIED_BY, "FORMULA", 0,
|
||||
"match formulas implied by FORMULA", 0 },
|
||||
{ "imply", OPT_IMPLY, "FORMULA", 0,
|
||||
"match formulas implying FORMULA", 0 },
|
||||
{ "equivalent-to", OPT_EQUIVALENT_TO, "FORMULA", 0,
|
||||
"match formulas equivalent to FORMULA", 0 },
|
||||
{ "stutter-insensitive", OPT_STUTTER_INSENSITIVE, nullptr, 0,
|
||||
"match stutter-insensitive LTL formulas", 0 },
|
||||
{ "stutter-invariant", 0, nullptr, OPTION_ALIAS, nullptr, 0 },
|
||||
{ "ap", OPT_AP_N, "N", 0,
|
||||
"match formulas which use exactly N atomic propositions", 0 },
|
||||
{ "invert-match", 'v', nullptr, 0, "select non-matching formulas", 0},
|
||||
{ "unique", 'u', nullptr, 0,
|
||||
"drop formulas that have already been output (not affected by -v)", 0 },
|
||||
/**************************************************/
|
||||
{ nullptr, 0, nullptr, 0, "Output options:", -20 },
|
||||
{ "count", 'c', nullptr, 0, "print only a count of matched formulas", 0 },
|
||||
{ "quiet", 'q', nullptr, 0, "suppress all normal output", 0 },
|
||||
{ "max-count", 'n', "NUM", 0, "output at most NUM formulas", 0 },
|
||||
{ nullptr, 0, nullptr, 0, "The FORMAT string passed to --format may use "\
|
||||
"the following interpreted sequences:", -19 },
|
||||
{ "%f", 0, nullptr, OPTION_DOC | OPTION_NO_USAGE,
|
||||
"the formula (in the selected syntax)", 0 },
|
||||
{ "%F", 0, nullptr, OPTION_DOC | OPTION_NO_USAGE,
|
||||
"the name of the input file", 0 },
|
||||
{ "%L", 0, nullptr, OPTION_DOC | OPTION_NO_USAGE,
|
||||
"the original line number in the input file", 0 },
|
||||
{ "%<", 0, nullptr, OPTION_DOC | OPTION_NO_USAGE,
|
||||
"the part of the line before the formula if it "
|
||||
"comes from a column extracted from a CSV file", 0 },
|
||||
{ "%>", 0, nullptr, OPTION_DOC | OPTION_NO_USAGE,
|
||||
"the part of the line after the formula if it "
|
||||
"comes from a column extracted from a CSV file", 0 },
|
||||
{ "%%", 0, nullptr, OPTION_DOC | OPTION_NO_USAGE,
|
||||
"a single %", 0 },
|
||||
{ nullptr, 0, nullptr, 0, "Miscellaneous options:", -1 },
|
||||
{ nullptr, 0, nullptr, 0, nullptr, 0 }
|
||||
};
|
||||
|
||||
const struct argp_child children[] =
|
||||
{
|
||||
{ &finput_argp, 0, nullptr, 1 },
|
||||
{ &output_argp, 0, nullptr, -20 },
|
||||
{ &misc_argp, 0, nullptr, -1 },
|
||||
{ nullptr, 0, nullptr, 0 }
|
||||
};
|
||||
|
||||
static bool one_match = false;
|
||||
|
||||
enum error_style_t { drop_errors, skip_errors };
|
||||
static error_style_t error_style = drop_errors;
|
||||
static bool ignore_errors = false;
|
||||
static bool nnf = false;
|
||||
static bool negate = false;
|
||||
static bool boolean_to_isop = false;
|
||||
static bool unique = false;
|
||||
static bool psl = false;
|
||||
static bool ltl = false;
|
||||
static bool invert = false;
|
||||
static bool boolean = false;
|
||||
static bool universal = false;
|
||||
static bool eventual = false;
|
||||
static bool syntactic_safety = false;
|
||||
static bool syntactic_guarantee = false;
|
||||
static bool syntactic_obligation = false;
|
||||
static bool syntactic_recurrence = false;
|
||||
static bool syntactic_persistence = false;
|
||||
static bool syntactic_si = false;
|
||||
static bool safety = false;
|
||||
static bool guarantee = false;
|
||||
static bool obligation = false;
|
||||
static int size_min = -1;
|
||||
static int size_max = -1;
|
||||
static int bsize_min = -1;
|
||||
static int bsize_max = -1;
|
||||
enum relabeling_mode { NoRelabeling = 0, ApRelabeling, BseRelabeling };
|
||||
static relabeling_mode relabeling = NoRelabeling;
|
||||
static spot::relabeling_style style = spot::Abc;
|
||||
static bool remove_x = false;
|
||||
static bool stutter_insensitive = false;
|
||||
static bool ap = false;
|
||||
static unsigned ap_n = 0;
|
||||
static int opt_max_count = -1;
|
||||
static long int match_count = 0;
|
||||
static spot::exclusive_ap excl_ap;
|
||||
static std::unique_ptr<output_file> output_define = nullptr;
|
||||
static std::string unabbreviate;
|
||||
|
||||
static spot::formula implied_by = nullptr;
|
||||
static spot::formula imply = nullptr;
|
||||
static spot::formula equivalent_to = nullptr;
|
||||
|
||||
static spot::formula
|
||||
parse_formula_arg(const std::string& input)
|
||||
{
|
||||
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;
|
||||
}
|
||||
|
||||
static int
|
||||
parse_opt(int key, char* arg, struct argp_state*)
|
||||
{
|
||||
// This switch is alphabetically-ordered.
|
||||
switch (key)
|
||||
{
|
||||
case 'c':
|
||||
output_format = count_output;
|
||||
break;
|
||||
case 'n':
|
||||
opt_max_count = to_pos_int(arg);
|
||||
break;
|
||||
case 'q':
|
||||
output_format = quiet_output;
|
||||
break;
|
||||
case OPT_R:
|
||||
parse_r(arg);
|
||||
break;
|
||||
case 'u':
|
||||
unique = true;
|
||||
break;
|
||||
case 'v':
|
||||
invert = true;
|
||||
break;
|
||||
case ARGP_KEY_ARG:
|
||||
// FIXME: use stat() to distinguish filename from string?
|
||||
jobs.emplace_back(arg, true);
|
||||
break;
|
||||
case OPT_BOOLEAN:
|
||||
boolean = true;
|
||||
break;
|
||||
case OPT_BOOLEAN_TO_ISOP:
|
||||
boolean_to_isop = true;
|
||||
break;
|
||||
case OPT_BSIZE_MIN:
|
||||
bsize_min = to_int(arg);
|
||||
break;
|
||||
case OPT_BSIZE_MAX:
|
||||
bsize_max = to_int(arg);
|
||||
break;
|
||||
case OPT_DEFINE:
|
||||
output_define.reset(new output_file(arg ? arg : "-"));
|
||||
break;
|
||||
case OPT_DROP_ERRORS:
|
||||
error_style = drop_errors;
|
||||
break;
|
||||
case OPT_EVENTUAL:
|
||||
eventual = true;
|
||||
break;
|
||||
case OPT_EQUIVALENT_TO:
|
||||
{
|
||||
if (equivalent_to)
|
||||
error(2, 0, "only one --equivalent-to option can be given");
|
||||
equivalent_to = parse_formula_arg(arg);
|
||||
break;
|
||||
}
|
||||
case OPT_EXCLUSIVE_AP:
|
||||
excl_ap.add_group(arg);
|
||||
break;
|
||||
case OPT_GUARANTEE:
|
||||
guarantee = obligation = true;
|
||||
break;
|
||||
case OPT_IGNORE_ERRORS:
|
||||
ignore_errors = true;
|
||||
break;
|
||||
case OPT_IMPLIED_BY:
|
||||
{
|
||||
spot::formula i = parse_formula_arg(arg);
|
||||
// a→c∧b→c ≡ (a∨b)→c
|
||||
implied_by = spot::formula::Or({implied_by, i});
|
||||
break;
|
||||
}
|
||||
case OPT_IMPLY:
|
||||
{
|
||||
// a→b∧a→c ≡ a→(b∧c)
|
||||
spot::formula i = parse_formula_arg(arg);
|
||||
imply = spot::formula::And({imply, i});
|
||||
break;
|
||||
}
|
||||
case OPT_LTL:
|
||||
ltl = true;
|
||||
break;
|
||||
case OPT_NEGATE:
|
||||
negate = true;
|
||||
break;
|
||||
case OPT_NNF:
|
||||
nnf = true;
|
||||
break;
|
||||
case OPT_OBLIGATION:
|
||||
obligation = true;
|
||||
break;
|
||||
case OPT_RELABEL:
|
||||
case OPT_RELABEL_BOOL:
|
||||
relabeling = (key == OPT_RELABEL_BOOL ? BseRelabeling : ApRelabeling);
|
||||
if (!arg || !strncasecmp(arg, "abc", 6))
|
||||
style = spot::Abc;
|
||||
else if (!strncasecmp(arg, "pnn", 4))
|
||||
style = spot::Pnn;
|
||||
else
|
||||
error(2, 0, "invalid argument for --relabel%s: '%s'",
|
||||
(key == OPT_RELABEL_BOOL ? "-bool" : ""),
|
||||
arg);
|
||||
break;
|
||||
case OPT_REMOVE_WM:
|
||||
unabbreviate += "MW";
|
||||
break;
|
||||
case OPT_REMOVE_X:
|
||||
remove_x = true;
|
||||
break;
|
||||
case OPT_SAFETY:
|
||||
safety = obligation = true;
|
||||
break;
|
||||
case OPT_SIZE_MIN:
|
||||
size_min = to_int(arg);
|
||||
break;
|
||||
case OPT_SIZE_MAX:
|
||||
size_max = to_int(arg);
|
||||
break;
|
||||
case OPT_SKIP_ERRORS:
|
||||
error_style = skip_errors;
|
||||
break;
|
||||
case OPT_STUTTER_INSENSITIVE:
|
||||
stutter_insensitive = true;
|
||||
break;
|
||||
case OPT_UNABBREVIATE:
|
||||
if (arg)
|
||||
unabbreviate += arg;
|
||||
else
|
||||
unabbreviate += spot::default_unabbrev_string;
|
||||
break;
|
||||
case OPT_AP_N:
|
||||
ap = true;
|
||||
ap_n = to_int(arg);
|
||||
break;
|
||||
case OPT_SYNTACTIC_SAFETY:
|
||||
syntactic_safety = true;
|
||||
break;
|
||||
case OPT_SYNTACTIC_GUARANTEE:
|
||||
syntactic_guarantee = true;
|
||||
break;
|
||||
case OPT_SYNTACTIC_OBLIGATION:
|
||||
syntactic_obligation = true;
|
||||
break;
|
||||
case OPT_SYNTACTIC_RECURRENCE:
|
||||
syntactic_recurrence = true;
|
||||
break;
|
||||
case OPT_SYNTACTIC_PERSISTENCE:
|
||||
syntactic_persistence = true;
|
||||
break;
|
||||
case OPT_SYNTACTIC_SI:
|
||||
syntactic_si = true;
|
||||
break;
|
||||
case OPT_UNIVERSAL:
|
||||
universal = true;
|
||||
break;
|
||||
default:
|
||||
return ARGP_ERR_UNKNOWN;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
typedef
|
||||
std::unordered_set<spot::formula> fset_t;
|
||||
|
||||
namespace
|
||||
{
|
||||
class ltl_processor: public job_processor
|
||||
{
|
||||
public:
|
||||
spot::tl_simplifier& simpl;
|
||||
fset_t unique_set;
|
||||
spot::relabeling_map relmap;
|
||||
|
||||
ltl_processor(spot::tl_simplifier& simpl)
|
||||
: simpl(simpl)
|
||||
{
|
||||
}
|
||||
|
||||
int
|
||||
process_string(const std::string& input,
|
||||
const char* filename = nullptr, int linenum = 0)
|
||||
{
|
||||
spot::parse_error_list pel;
|
||||
spot::formula f = parse_formula(input, pel);
|
||||
|
||||
if (!f || pel.size() > 0)
|
||||
{
|
||||
if (!ignore_errors)
|
||||
{
|
||||
if (filename)
|
||||
error_at_line(0, 0, filename, linenum, "parse error:");
|
||||
spot::format_parse_errors(std::cerr, input, pel);
|
||||
}
|
||||
|
||||
if (error_style == skip_errors)
|
||||
std::cout << input << std::endl;
|
||||
else
|
||||
assert(error_style == drop_errors);
|
||||
check_cout();
|
||||
return !ignore_errors;
|
||||
}
|
||||
try
|
||||
{
|
||||
return process_formula(f, filename, linenum);
|
||||
}
|
||||
catch (const std::runtime_error& e)
|
||||
{
|
||||
error_at_line(2, 0, filename, linenum, "%s", e.what());
|
||||
SPOT_UNREACHABLE();
|
||||
}
|
||||
}
|
||||
|
||||
int
|
||||
process_formula(spot::formula f,
|
||||
const char* filename = nullptr, int linenum = 0)
|
||||
{
|
||||
if (opt_max_count >= 0 && match_count >= opt_max_count)
|
||||
{
|
||||
abort_run = true;
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (negate)
|
||||
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::remove_x(f);
|
||||
}
|
||||
|
||||
if (simplification_level || boolean_to_isop)
|
||||
f = simpl.simplify(f);
|
||||
|
||||
if (nnf)
|
||||
f = simpl.negative_normal_form(f);
|
||||
|
||||
switch (relabeling)
|
||||
{
|
||||
case ApRelabeling:
|
||||
{
|
||||
relmap.clear();
|
||||
f = spot::relabel(f, style, &relmap);
|
||||
break;
|
||||
}
|
||||
case BseRelabeling:
|
||||
{
|
||||
relmap.clear();
|
||||
f = spot::relabel_bse(f, style, &relmap);
|
||||
break;
|
||||
}
|
||||
case NoRelabeling:
|
||||
break;
|
||||
}
|
||||
|
||||
if (!unabbreviate.empty())
|
||||
f = spot::unabbreviate(f, unabbreviate.c_str());
|
||||
|
||||
if (!excl_ap.empty())
|
||||
f = excl_ap.constrain(f);
|
||||
|
||||
bool matched = true;
|
||||
|
||||
matched &= !ltl || f.is_ltl_formula();
|
||||
matched &= !psl || f.is_psl_formula();
|
||||
matched &= !boolean || f.is_boolean();
|
||||
matched &= !universal || f.is_universal();
|
||||
matched &= !eventual || f.is_eventual();
|
||||
matched &= !syntactic_safety || f.is_syntactic_safety();
|
||||
matched &= !syntactic_guarantee || f.is_syntactic_guarantee();
|
||||
matched &= !syntactic_obligation || f.is_syntactic_obligation();
|
||||
matched &= !syntactic_recurrence || f.is_syntactic_recurrence();
|
||||
matched &= !syntactic_persistence || f.is_syntactic_persistence();
|
||||
matched &= !syntactic_si || f.is_syntactic_stutter_invariant();
|
||||
matched &= !ap || atomic_prop_collect(f)->size() == ap_n;
|
||||
|
||||
if (matched && (size_min > 0 || size_max >= 0))
|
||||
{
|
||||
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::length_boolone(f);
|
||||
matched &= (bsize_min <= 0) || (l >= bsize_min);
|
||||
matched &= (bsize_max < 0) || (l <= bsize_max);
|
||||
}
|
||||
|
||||
matched &= !implied_by || simpl.implication(implied_by, f);
|
||||
matched &= !imply || simpl.implication(f, imply);
|
||||
matched &= !equivalent_to || simpl.are_equivalent(f, equivalent_to);
|
||||
matched &= !stutter_insensitive || spot::is_stutter_invariant(f);
|
||||
|
||||
// Match obligations and subclasses using WDBA minimization.
|
||||
// Because this is costly, we compute it later, so that we don't
|
||||
// have to compute it on formulas that have been discarded for
|
||||
// other reasons.
|
||||
if (matched && obligation)
|
||||
{
|
||||
auto aut = ltl_to_tgba_fm(f, simpl.get_dict());
|
||||
auto min = minimize_obligation(aut, f);
|
||||
assert(min);
|
||||
if (aut == min)
|
||||
{
|
||||
// Not an obligation
|
||||
matched = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
matched &= !guarantee || is_terminal_automaton(min);
|
||||
matched &= !safety || is_safety_mwdba(min);
|
||||
}
|
||||
}
|
||||
|
||||
matched ^= invert;
|
||||
|
||||
if (unique && !unique_set.insert(f).second)
|
||||
matched = false;
|
||||
|
||||
if (matched)
|
||||
{
|
||||
if (output_define
|
||||
&& output_format != count_output
|
||||
&& output_format != quiet_output)
|
||||
{
|
||||
// Sort the formulas alphabetically.
|
||||
std::map<std::string, spot::formula> m;
|
||||
for (auto& p: relmap)
|
||||
m.emplace(str_psl(p.first), p.second);
|
||||
for (auto& p: m)
|
||||
stream_formula(output_define->ostream()
|
||||
<< "#define " << p.first << " (",
|
||||
p.second, filename, linenum) << ")\n";
|
||||
}
|
||||
one_match = true;
|
||||
output_formula_checked(f, filename, linenum, prefix, suffix);
|
||||
++match_count;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
int
|
||||
main(int argc, char** argv)
|
||||
{
|
||||
setup(argv);
|
||||
|
||||
const argp ap = { options, parse_opt, "[FILENAME[/COL]...]",
|
||||
argp_program_doc, children, nullptr, nullptr };
|
||||
|
||||
try
|
||||
{
|
||||
if (int err = argp_parse(&ap, argc, argv, ARGP_NO_HELP, nullptr, nullptr))
|
||||
exit(err);
|
||||
|
||||
if (jobs.empty())
|
||||
jobs.emplace_back("-", 1);
|
||||
|
||||
if (boolean_to_isop && simplification_level == 0)
|
||||
simplification_level = 1;
|
||||
spot::tl_simplifier_options opt(simplification_level);
|
||||
opt.boolean_to_isop = boolean_to_isop;
|
||||
spot::tl_simplifier simpl(opt);
|
||||
|
||||
ltl_processor processor(simpl);
|
||||
if (processor.run())
|
||||
return 2;
|
||||
}
|
||||
catch (const std::runtime_error& e)
|
||||
{
|
||||
error(2, 0, "%s", e.what());
|
||||
}
|
||||
catch (const std::invalid_argument& e)
|
||||
{
|
||||
error(2, 0, "%s", e.what());
|
||||
}
|
||||
|
||||
if (output_format == count_output)
|
||||
std::cout << match_count << std::endl;
|
||||
|
||||
return one_match ? 0 : 1;
|
||||
}
|
||||
199
spot/bin/ltlgrind.cc
Normal file
199
spot/bin/ltlgrind.cc
Normal file
|
|
@ -0,0 +1,199 @@
|
|||
// -*- coding: utf-8 -*-
|
||||
// Copyright (C) 2014, 2015 Laboratoire de Recherche et Développement de
|
||||
// l'Epita (LRDE).
|
||||
//
|
||||
// This file is part of Spot, a model checking library.
|
||||
//
|
||||
// Spot is free software; you can redistribute it and/or modify it
|
||||
// under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation; either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// Spot is distributed in the hope that it will be useful, but WITHOUT
|
||||
// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
||||
// or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public
|
||||
// License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
|
||||
#include "common_sys.hh"
|
||||
#include <argp.h>
|
||||
#include "error.h"
|
||||
|
||||
#include "common_setup.hh"
|
||||
#include "common_finput.hh"
|
||||
#include "common_output.hh"
|
||||
#include "common_conv.hh"
|
||||
|
||||
#include <spot/tl/mutation.hh>
|
||||
|
||||
enum {
|
||||
OPT_AP2CONST = 1,
|
||||
OPT_SIMPLIFY_BOUNDS,
|
||||
OPT_REMOVE_MULTOP_OPERANDS,
|
||||
OPT_REMOVE_OPS,
|
||||
OPT_SPLIT_OPS,
|
||||
OPT_REWRITE_OPS,
|
||||
OPT_REMOVE_ONE_AP,
|
||||
OPT_SORT,
|
||||
};
|
||||
|
||||
static unsigned mutation_nb = 1;
|
||||
static unsigned max_output = -1U;
|
||||
|
||||
static unsigned opt_all = spot::Mut_All;
|
||||
static unsigned mut_opts = 0;
|
||||
static bool opt_sort = false;
|
||||
|
||||
static const char * argp_program_doc =
|
||||
"List formulas that are similar to but simpler than a given formula.";
|
||||
|
||||
static const argp_option options[] = {
|
||||
{ nullptr, 0, nullptr, 0,
|
||||
"Mutation rules (all enabled unless those options are used):", 15},
|
||||
{ "ap-to-const", OPT_AP2CONST, nullptr, 0,
|
||||
"atomic propositions are replaced with true/false", 15 },
|
||||
{ "remove-one-ap", OPT_REMOVE_ONE_AP, nullptr, 0,
|
||||
"all occurrences of an atomic proposition are replaced with another " \
|
||||
"atomic proposition used in the formula", 15 },
|
||||
{ "remove-multop-operands", OPT_REMOVE_MULTOP_OPERANDS, nullptr, 0,
|
||||
"remove one operand from multops", 15 },
|
||||
{ "remove-ops", OPT_REMOVE_OPS, nullptr, 0,
|
||||
"replace unary/binary operators with one of their operands", 15 },
|
||||
{ "split-ops", OPT_SPLIT_OPS, nullptr, 0,
|
||||
"when an operator can be expressed as a conjunction/disjunction using "
|
||||
"simpler operators, each term of the conjunction/disjunction is a "
|
||||
"mutation. e.g. a <-> b can be written as ((a & b) | (!a & !b)) or as "
|
||||
"((a -> b) & (b -> a)) so those four terms can be a mutation of a <-> b",
|
||||
0 },
|
||||
{ "rewrite-ops", OPT_REWRITE_OPS, nullptr, 0,
|
||||
"rewrite operators that have a semantically simpler form: a U b becomes "
|
||||
"a W b, etc.", 0 },
|
||||
{ "simplify-bounds", OPT_SIMPLIFY_BOUNDS, nullptr, 0,
|
||||
"on a bounded unary operator, decrement one of the bounds, or set min to "
|
||||
"0 or max to unbounded", 15 },
|
||||
{ nullptr, 0, nullptr, 0, "Output options:", 20 },
|
||||
{ "max-count", 'n', "NUM", 0, "maximum number of mutations to output", 20 },
|
||||
{ "mutations", 'm', "NUM", 0, "number of mutations to apply to the "
|
||||
"formulae (default: 1)", 0 },
|
||||
{ "sort", OPT_SORT, nullptr, 0, "sort the result by formula size", 0 },
|
||||
{ nullptr, 0, nullptr, 0, "The FORMAT string passed to --format may use "
|
||||
"the following interpreted sequences:", 21 },
|
||||
{ "%f", 0, nullptr, OPTION_DOC | OPTION_NO_USAGE,
|
||||
"the formula (in the selected syntax)", 0 },
|
||||
{ "%F", 0, nullptr, OPTION_DOC | OPTION_NO_USAGE,
|
||||
"the name of the input file", 0 },
|
||||
{ "%L", 0, nullptr, OPTION_DOC | OPTION_NO_USAGE,
|
||||
"the original line number in the input file", 0 },
|
||||
{ "%<", 0, nullptr, OPTION_DOC | OPTION_NO_USAGE,
|
||||
"the part of the line before the formula if it "
|
||||
"comes from a column extracted from a CSV file", 0 },
|
||||
{ "%>", 0, nullptr, OPTION_DOC | OPTION_NO_USAGE,
|
||||
"the part of the line after the formula if it "
|
||||
"comes from a column extracted from a CSV file", 0 },
|
||||
{ "%%", 0, nullptr, OPTION_DOC | OPTION_NO_USAGE,
|
||||
"a single %", 0 },
|
||||
{nullptr, 0, nullptr, 0, "Miscellaneous options:", -1},
|
||||
{nullptr, 0, nullptr, 0, nullptr, 0}
|
||||
};
|
||||
|
||||
static const argp_child children[] = {
|
||||
{ &finput_argp, 0, nullptr, 10 },
|
||||
{ &output_argp, 0, nullptr, 20 },
|
||||
{ &misc_argp, 0, nullptr, -1 },
|
||||
{ nullptr, 0, nullptr, 0 }
|
||||
};
|
||||
|
||||
namespace
|
||||
{
|
||||
class mutate_processor: public job_processor
|
||||
{
|
||||
public:
|
||||
int
|
||||
process_formula(spot::formula f, const char* filename = nullptr,
|
||||
int linenum = 0)
|
||||
{
|
||||
auto mutations =
|
||||
spot::mutate(f, mut_opts, max_output, mutation_nb, opt_sort);
|
||||
for (auto g: mutations)
|
||||
output_formula_checked(g, filename, linenum, prefix, suffix);
|
||||
return 0;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
static int
|
||||
parse_opt(int key, char* arg, struct argp_state*)
|
||||
{
|
||||
switch (key)
|
||||
{
|
||||
case 'm':
|
||||
mutation_nb = to_unsigned(arg);
|
||||
break;
|
||||
case 'n':
|
||||
max_output = to_int(arg);
|
||||
break;
|
||||
case ARGP_KEY_ARG:
|
||||
// FIXME: use stat() to distinguish filename from string?
|
||||
jobs.emplace_back(arg, true);
|
||||
break;
|
||||
case OPT_AP2CONST:
|
||||
opt_all = 0;
|
||||
mut_opts |= spot::Mut_Ap2Const;
|
||||
break;
|
||||
case OPT_REMOVE_ONE_AP:
|
||||
opt_all = 0;
|
||||
mut_opts |= spot::Mut_Remove_One_Ap;
|
||||
break;
|
||||
case OPT_REMOVE_MULTOP_OPERANDS:
|
||||
opt_all = 0;
|
||||
mut_opts |= spot::Mut_Remove_Multop_Operands;
|
||||
break;
|
||||
case OPT_REMOVE_OPS:
|
||||
opt_all = 0;
|
||||
mut_opts |= spot::Mut_Remove_Ops;
|
||||
break;
|
||||
case OPT_SPLIT_OPS:
|
||||
opt_all = 0;
|
||||
mut_opts |= spot::Mut_Split_Ops;
|
||||
break;
|
||||
case OPT_REWRITE_OPS:
|
||||
opt_all = 0;
|
||||
mut_opts |= spot::Mut_Rewrite_Ops;
|
||||
break;
|
||||
case OPT_SIMPLIFY_BOUNDS:
|
||||
opt_all = 0;
|
||||
mut_opts |= spot::Mut_Simplify_Bounds;
|
||||
break;
|
||||
case OPT_SORT:
|
||||
opt_sort = true;
|
||||
break;
|
||||
default:
|
||||
return ARGP_ERR_UNKNOWN;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
main(int argc, char* argv[])
|
||||
{
|
||||
setup(argv);
|
||||
|
||||
const argp ap = { options, parse_opt, "[FILENAME[/COL]...]", argp_program_doc,
|
||||
children, nullptr, nullptr };
|
||||
|
||||
if (int err = argp_parse(&ap, argc, argv, ARGP_NO_HELP, nullptr, nullptr))
|
||||
exit(err);
|
||||
|
||||
mut_opts |= opt_all;
|
||||
|
||||
if (jobs.empty())
|
||||
jobs.push_back(job("-", true));
|
||||
|
||||
mutate_processor processor;
|
||||
if (processor.run())
|
||||
return 2;
|
||||
return 0;
|
||||
}
|
||||
79
spot/bin/man/Makefile.am
Normal file
79
spot/bin/man/Makefile.am
Normal file
|
|
@ -0,0 +1,79 @@
|
|||
## -*- coding: utf-8 -*-
|
||||
## Copyright (C) 2012, 2013, 2014, 2015 Laboratoire de Recherche et
|
||||
## Développement de l'Epita (LRDE).
|
||||
##
|
||||
## This file is part of Spot, a model checking library.
|
||||
##
|
||||
## Spot is free software; you can redistribute it and/or modify it
|
||||
## under the terms of the GNU General Public License as published by
|
||||
## the Free Software Foundation; either version 3 of the License, or
|
||||
## (at your option) any later version.
|
||||
##
|
||||
## Spot is distributed in the hope that it will be useful, but WITHOUT
|
||||
## ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
||||
## or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public
|
||||
## License for more details.
|
||||
##
|
||||
## You should have received a copy of the GNU General Public License
|
||||
## along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
common_dep = $(top_srcdir)/configure.ac
|
||||
x_to_1 = $(top_builddir)/tools/x-to-1
|
||||
convman = ARGP_HELP_FMT=header-col=0 $(SHELL) "$(x_to_1)" \
|
||||
"$(PERL)" "$(top_srcdir)/tools/help2man -N -L 'en_US.UTF-8'"
|
||||
convman7 = ARGP_HELP_FMT=header-col=0 $(SHELL) "$(x_to_1)" \
|
||||
"$(PERL)" "$(top_srcdir)/tools/help2man -s7 -N -L 'en_US.UTF-8'"
|
||||
|
||||
dist_man1_MANS = \
|
||||
autfilt.1 \
|
||||
dstar2tgba.1 \
|
||||
genltl.1 \
|
||||
ltl2tgba.1 \
|
||||
ltl2tgta.1 \
|
||||
ltlcross.1 \
|
||||
ltldo.1 \
|
||||
ltlfilt.1 \
|
||||
ltlgrind.1 \
|
||||
randaut.1 \
|
||||
randltl.1
|
||||
dist_man7_MANS = \
|
||||
spot-x.7
|
||||
|
||||
MAINTAINERCLEANFILES = $(dist_man1_MANS) $(dist_man7_MANS)
|
||||
EXTRA_DIST = $(dist_man1_MANS:.1=.x) $(dist_man7_MANS:.7=.x)
|
||||
|
||||
autfilt.1: $(common_dep) $(srcdir)/autfilt.x $(srcdir)/../autfilt.cc
|
||||
$(convman) ../autfilt$(EXEEXT) $(srcdir)/autfilt.x $@
|
||||
|
||||
dstar2tgba.1: $(common_dep) $(srcdir)/dstar2tgba.x $(srcdir)/../dstar2tgba.cc
|
||||
$(convman) ../dstar2tgba$(EXEEXT) $(srcdir)/dstar2tgba.x $@
|
||||
|
||||
ltl2tgba.1: $(common_dep) $(srcdir)/ltl2tgba.x $(srcdir)/../ltl2tgba.cc
|
||||
$(convman) ../ltl2tgba$(EXEEXT) $(srcdir)/ltl2tgba.x $@
|
||||
|
||||
ltl2tgta.1: $(common_dep) $(srcdir)/ltl2tgta.x $(srcdir)/../ltl2tgta.cc
|
||||
$(convman) ../ltl2tgta$(EXEEXT) $(srcdir)/ltl2tgta.x $@
|
||||
|
||||
ltlcross.1: $(common_dep) $(srcdir)/ltlcross.x $(srcdir)/../ltlcross.cc
|
||||
$(convman) ../ltlcross$(EXEEXT) $(srcdir)/ltlcross.x $@
|
||||
|
||||
ltldo.1: $(common_dep) $(srcdir)/ltlcross.x $(srcdir)/../ltldo.cc
|
||||
$(convman) ../ltldo$(EXEEXT) $(srcdir)/ltldo.x $@
|
||||
|
||||
ltlfilt.1: $(common_dep) $(srcdir)/ltlfilt.x $(srcdir)/../ltlfilt.cc
|
||||
$(convman) ../ltlfilt$(EXEEXT) $(srcdir)/ltlfilt.x $@
|
||||
|
||||
genltl.1: $(common_dep) $(srcdir)/genltl.x $(srcdir)/../genltl.cc
|
||||
$(convman) ../genltl$(EXEEXT) $(srcdir)/genltl.x $@
|
||||
|
||||
randltl.1: $(common_dep) $(srcdir)/randltl.x $(srcdir)/../randltl.cc
|
||||
$(convman) ../randltl$(EXEEXT) $(srcdir)/randltl.x $@
|
||||
|
||||
randaut.1: $(common_dep) $(srcdir)/randaut.x $(srcdir)/../randaut.cc
|
||||
$(convman) ../randaut$(EXEEXT) $(srcdir)/randaut.x $@
|
||||
|
||||
spot-x.7: $(common_dep) $(srcdir)/spot-x.x $(srcdir)/../spot-x.cc
|
||||
$(convman7) ../spot-x$(EXEEXT) $(srcdir)/spot-x.x $@
|
||||
|
||||
ltlgrind.1: $(common_dep) $(srcdir)/ltlgrind.x $(srcdir)/../ltlgrind.cc
|
||||
$(convman) ../ltlgrind$(EXEEXT) $(srcdir)/ltlgrind.x $@
|
||||
29
spot/bin/man/autfilt.x
Normal file
29
spot/bin/man/autfilt.x
Normal file
|
|
@ -0,0 +1,29 @@
|
|||
.\" -*- coding: utf-8 -*-
|
||||
[NAME]
|
||||
autfilt \- filter, convert, and transform omega-automata
|
||||
[DESCRIPTION]
|
||||
.\" Add any additional description here
|
||||
[BIBLIOGRAPHY]
|
||||
The following papers are related to some of the transformations implemented
|
||||
in autfilt.
|
||||
|
||||
.TP
|
||||
\(bu
|
||||
Etienne Renault, Alexandre Duret-Lutz, Fabrice Kordon, and Denis Poitrenaud:
|
||||
Strength-based decomposition of the property Büchi automaton for faster
|
||||
model checking. Proceedings of TACAS'13. LNCS 7795.
|
||||
|
||||
The \fB\-\-strength\-decompose\fR option implements the definitions
|
||||
given in the above paper.
|
||||
.TP
|
||||
\(bu
|
||||
František Blahoudek, Alexandre Duret-Lutz, Vojtčech Rujbr, and Jan Strejček:
|
||||
On refinement of Büchi automata for explicit model checking.
|
||||
Proceedings of SPIN'15. LNCS 9232.
|
||||
|
||||
That paper gives the motivation for options \fB\-\-exclusive\-ap\fR
|
||||
and \fB\-\-simplify\-exclusive\-ap\fR.
|
||||
|
||||
[SEE ALSO]
|
||||
.BR spot-x (7)
|
||||
.BR dstar2tgba (1)
|
||||
99
spot/bin/man/dstar2tgba.x
Normal file
99
spot/bin/man/dstar2tgba.x
Normal file
|
|
@ -0,0 +1,99 @@
|
|||
[NAME]
|
||||
dstar2tgba \- convert automata into Büchi automata
|
||||
[HISTORY]
|
||||
.B dstar2tgba
|
||||
was introduced in Spot 1.2 as a command that reads automata
|
||||
in
|
||||
.BR ltl2dstar 's
|
||||
format, and converts them into TGBA. At this time it was
|
||||
the only command-line tool being able to read automata.
|
||||
.PP
|
||||
In Spot 1.99.1 the
|
||||
.B autfilt
|
||||
command was introduced, but could only read automata
|
||||
in the HOA format, or in
|
||||
.BR lbtt 's
|
||||
format, or as never claims. So
|
||||
.B dstar2tgba
|
||||
was still the only way to process automata
|
||||
in
|
||||
.BR ltl2dstar 's
|
||||
format.
|
||||
.PP
|
||||
In Spot 1.99.4 the parser for
|
||||
.BR ltl2dstar 's
|
||||
format was finally merged with the parser
|
||||
used by
|
||||
.B autfilt
|
||||
for reading the other format. This implies not only
|
||||
that
|
||||
.B autfilt
|
||||
can now read
|
||||
.BR ltl2dstar's
|
||||
format, but also that
|
||||
.B dstar2tgba
|
||||
can read the other formats as well.
|
||||
|
||||
Nowadays, the command
|
||||
.PP
|
||||
.in +4n
|
||||
.nf
|
||||
.ft C
|
||||
% dstar2tgba some files
|
||||
.fi
|
||||
.PP
|
||||
can be used as a shorthand for
|
||||
.PP
|
||||
.in +4n
|
||||
.nf
|
||||
.ft C
|
||||
% autfilt \-\-tgba \-\-high \-\-small some files
|
||||
.fi
|
||||
.PP
|
||||
The name
|
||||
.B dstar2tgba
|
||||
is kept for backward compatibility and because it is used
|
||||
in at least one published paper, but naming this tool
|
||||
.B aut2tgba
|
||||
would make more sense.
|
||||
|
||||
[BIBLIOGRAPHY]
|
||||
.TP
|
||||
1.
|
||||
<http://www.ltl2dstar.de/docs/ltl2dstar.html>
|
||||
|
||||
Documents the output format of ltl2dstar.
|
||||
|
||||
.TP
|
||||
2.
|
||||
Chistof Löding: Mehods for the Transformation of ω-Automata:
|
||||
Complexity and Connection to Second Order Logic. Diploma Thesis.
|
||||
University of Kiel. 1998.
|
||||
|
||||
Describes various tranformations from non-deterministic Rabin and
|
||||
Streett automata to Büchi automata. Slightly optimized variants of
|
||||
these transformations are used by dstar2tgba for the general cases.
|
||||
|
||||
.TP
|
||||
3.
|
||||
Sriram C. Krishnan, Anuj Puri, and Robert K. Brayton: Deterministic
|
||||
ω-automata vis-a-vis Deterministic Büchi Automata. ISAAC'94.
|
||||
|
||||
Explains how to preserve the determinism of Rabin and Streett automata
|
||||
when the property can be repreted by a Deterministic automaton.
|
||||
dstar2tgba implements this for the Rabin case only. In other words,
|
||||
translating a deterministic Rabin automaton with dstar2tgba will
|
||||
produce a deterministic TGBA or BA if such a automaton exists.
|
||||
|
||||
.TP
|
||||
4.
|
||||
Souheib Baarir and Alexandre Duret-Lutz: Mechanizing the minimization
|
||||
of deterministic generalized Büchi automata. Proceedings of FORTE'14.
|
||||
LNCS 8461.
|
||||
|
||||
Explains the SAT-based minimization techniques that can be used (on
|
||||
request only) by dstar2tgba to minimize deterministic Büchi automata.
|
||||
|
||||
[SEE ALSO]
|
||||
.BR spot-x (7),
|
||||
.BR autfilt (1)
|
||||
31
spot/bin/man/genltl.x
Normal file
31
spot/bin/man/genltl.x
Normal file
|
|
@ -0,0 +1,31 @@
|
|||
[NAME]
|
||||
genltl \- generate LTL formulas from scalable patterns
|
||||
[DESCRIPTION]
|
||||
.\" Add any additional description here
|
||||
[BIBLIOGRAPHY]
|
||||
If you would like to give a reference to this tool in an article,
|
||||
we suggest you cite the following paper:
|
||||
.TP
|
||||
\(bu
|
||||
Alexandre Duret-Lutz: Manipulating LTL formulas using Spot 1.0.
|
||||
Proceedings of ATVA'13. LNCS 8172.
|
||||
.PP
|
||||
Prefixes used in pattern names refer to the following papers:
|
||||
.TP
|
||||
gh
|
||||
J. Geldenhuys and H. Hansen: Larger automata and less
|
||||
work for LTL model checking. Proceedings of Spin'06. LNCS 3925.
|
||||
.TP
|
||||
ccj
|
||||
J. Cichoń, A. Czubak, and A. Jasiński (DepCoS'09): Minimal Büchi
|
||||
Automata for Certain Classes of LTL Formulas. Proceedings of DepCoS'09.
|
||||
.TP
|
||||
go
|
||||
P. Gastin and D. Oddoux: Fast LTL to Büchi Automata Translation.
|
||||
Proceedings of CAV'01. LNCS 2102.
|
||||
.TP
|
||||
rv
|
||||
K. Rozier and M. Vardi: LTL Satisfiability Checking.
|
||||
Proceedings of Spin'07. LNCS 4595.
|
||||
[SEE ALSO]
|
||||
.BR randltl (1)
|
||||
202
spot/bin/man/ltl2tgba.x
Normal file
202
spot/bin/man/ltl2tgba.x
Normal file
|
|
@ -0,0 +1,202 @@
|
|||
.\" -*- coding: utf-8 -*-
|
||||
[NAME]
|
||||
ltl2tgba \- translate LTL/PSL formulas into Büchi automata
|
||||
[NOTE ON TGBA]
|
||||
TGBA stands for Transition-based Generalized Büchi Automaton. The
|
||||
name was coined by Dimitra Giannakopoulou and Flavio Lerda in their
|
||||
FORTE'02 paper (From States to Transitions: Improving Translation of
|
||||
LTL Formulae to Büchi Automata), although similar automata have been
|
||||
used under different names long before that.
|
||||
.PP
|
||||
As its name implies a TGBA uses a generalized Büchi acceptance
|
||||
condition, meanings that a run of the automaton is accepted iff it
|
||||
visits ininitely often multiple acceptance sets, and it also uses
|
||||
transition-based acceptance, i.e., those acceptance sets are sets of
|
||||
transitions. TGBA are often more consise than traditional Büchi
|
||||
automata. For instance the LTL formula \f(CWGFa & GFb\fR can be
|
||||
translated into a single-state TGBA while a traditional Büchi
|
||||
automaton would need 3 states. Compare
|
||||
.PP
|
||||
.in +4n
|
||||
.nf
|
||||
.ft C
|
||||
% ltl2tgba 'GFa & GFb'
|
||||
.fi
|
||||
.PP
|
||||
with
|
||||
.PP
|
||||
.in +4n
|
||||
.ft C
|
||||
.nf
|
||||
% ltl2tgba --ba 'GFa & GFb'
|
||||
.fi
|
||||
.PP
|
||||
In the dot output produced by the above commands, the membership of
|
||||
the transitions to the various acceptance sets is denoted using names
|
||||
in braces. The actuall names do not really matter as they may be
|
||||
produced by the translation algorithm or altered by any latter
|
||||
postprocessing.
|
||||
.PP
|
||||
When the \fB\-\-ba\fR option is used to request a Büchi automaton, Spot
|
||||
builds a TGBA with a single acceptance set, and in which for any state
|
||||
either all outgoing transitions are accepting (this is equivalent to
|
||||
the state being accepting) or none of them are. Double circles are
|
||||
used to highlight accepting states in the output, but the braces
|
||||
denoting the accepting transitions are still shown because the
|
||||
underling structure really is a TGBA.
|
||||
|
||||
[NOTE ON LBTT'S FORMAT]
|
||||
.UR http://www.tcs.hut.fi/Software/lbtt/doc/html/Format-for-automata.html
|
||||
LBTT's format
|
||||
.UE
|
||||
has support for both transition-based and state based generalized acceptance.
|
||||
.PP
|
||||
Because Spot uses transition-based generalized Büchi automata
|
||||
internally, it will normally use the transition-based flavor of that
|
||||
format, indicated with a 't' flag after the number of acceptance sets.
|
||||
For instance:
|
||||
.PP
|
||||
.in +4n
|
||||
.ft C
|
||||
.nf
|
||||
% ltl2tgba --lbtt 'GFp0 & GFp1 & FGp2'
|
||||
2 2t // 2 states, 2 transition-based acceptance sets
|
||||
0 1 // state 0: initial
|
||||
0 -1 t // trans. to state 0, no acc., label: true
|
||||
1 -1 | & p0 p2 & p1 p2 // trans. to state 1, no acc., label: (p0&p2)|(p1&p2)
|
||||
-1 // end of state 0
|
||||
1 0 // state 1: not initial
|
||||
1 0 1 -1 & & p0 p1 p2 // trans. to state 1, acc. 0 and 1, label: p0&p1&p2
|
||||
1 0 -1 & & p1 p2 ! p0 // trans. to state 1, acc. 0, label: !p0&p1&p2
|
||||
1 1 -1 & & p0 p2 ! p1 // trans. to state 1, acc. 1, label: p0&!p1&p2
|
||||
1 -1 & & p2 ! p0 ! p1 // trans. to state 1, no acc., label: !p0&!p1&p2
|
||||
-1 // end if state 1
|
||||
.fi
|
||||
.PP
|
||||
Here, the two acceptance sets are represented by the numbers 0 and 1,
|
||||
and they each contain two transitions (the first transition of state 1
|
||||
belongs to both sets).
|
||||
.PP
|
||||
When both \fB\-\-ba\fR and \fB\-\-lbtt\fR options are used,
|
||||
the state-based flavor of
|
||||
the format is used instead. Note that the LBTT format supports
|
||||
generalized acceptance conditions on states, but Spot only use this
|
||||
format for Büchi automata, where there is always only one acceptance
|
||||
set. Unlike in the LBTT documentation, we do not use the
|
||||
optional '\fBs\fR' flag to indicate the state-based acceptance, this way our
|
||||
output is also compatible with that of
|
||||
.UR http://www.tcs.hut.fi/Software/maria/tools/lbt/
|
||||
LBT
|
||||
.UE .
|
||||
.PP
|
||||
.in +4n
|
||||
.ft C
|
||||
.nf
|
||||
% ltl2tgba --ba --lbtt FGp0
|
||||
2 1 // 2 states, 1 (state-based) accepance set
|
||||
0 1 -1 // state 0: initial, non-accepting
|
||||
0 t // trans. to state 0, label: true
|
||||
1 p0 // trans. to state 1, label: p0
|
||||
-1 // end of state 0
|
||||
1 0 0 -1 // state 1: not initial, in acceptance set 0
|
||||
1 p0 // trans. to state 0, label: p0
|
||||
-1 // end if state 1
|
||||
.fi
|
||||
.PP
|
||||
You can force ltl2tgba to use the transition-based flavor of the
|
||||
format even for Büchi automaton using \fB\-\-lbtt=t\fR.
|
||||
.PP
|
||||
.in +4n
|
||||
.ft C
|
||||
.nf
|
||||
% ltl2tgba --ba --lbtt=t FGp0
|
||||
2 1t // 2 states, 1 transition-based accepance set.
|
||||
0 1 // state 0: initial
|
||||
0 -1 t // trans. to state 0, no acc., label: true
|
||||
1 -1 p0 // trans. to state 1, no acc., label: p0
|
||||
-1 // end of state 0
|
||||
1 0 // state 1: not initial
|
||||
1 0 -1 p0 // trans. to state 1, acc. 0, label: p0
|
||||
-1 // end if state 1
|
||||
.fi
|
||||
.PP
|
||||
When representing a Büchi automaton using transition-based acceptance,
|
||||
all transitions leaving accepting states are put into the acceptance set.
|
||||
.PP
|
||||
A final note concerns the name of the atomic propositions. The
|
||||
original LBTT and LBT formats require these atomic propositions to
|
||||
have names such as '\fBp0\fR', '\fBp32\fR', ... We extend the format to accept
|
||||
atomic proposition with arbitrary names that do not conflict with
|
||||
LBT's operators (e.g. '\fBi\fR' is the symbol of the implication operator so
|
||||
it may not be used as an atomic proposition), or as double-quoted
|
||||
strings. Spot will always output atomic-proposition that do not match
|
||||
\fBp[0-9]+\fR as double-quoted strings.
|
||||
.PP
|
||||
.in +4n
|
||||
.ft C
|
||||
.nf
|
||||
% ltl2tgba --lbtt 'GFa & GFb'
|
||||
1 2t
|
||||
0 1
|
||||
0 0 1 -1 & "a" "b"
|
||||
0 0 -1 & "b" ! "a"
|
||||
0 1 -1 & "a" ! "b"
|
||||
0 -1 & ! "b" ! "a"
|
||||
-1
|
||||
.fi
|
||||
|
||||
[NOTE ON GENERATING MONITORS]
|
||||
The monitors generated with option \fB\-M\fR are finite state automata
|
||||
used to reject finite words that cannot be extended to infinite words
|
||||
compatible with the supplied formula. The idea is that the monitor
|
||||
should progress alongside the system, and can only make decisions
|
||||
based on the finite prefix read so far.
|
||||
.PP
|
||||
Monitors can be seen as Büchi automata in which all recognized runs are
|
||||
accepting. As such, the only infinite words they can reject are those
|
||||
are not recognized, i.e., infinite words that start with a bad prefix.
|
||||
.PP
|
||||
Because of this limited expressiveness, a monitor for some given LTL
|
||||
or PSL formula may accept a larger language than the one specified by
|
||||
the formula. For instance a monitor for the LTL formula \f(CWa U b\fR
|
||||
will reject (for instance) any word starting with \f(CW!a&!b\fR as
|
||||
there is no way such a word can validate the formula, but it will not
|
||||
reject a finite prefix repeating only \f(CWa&!b\fR as such a prefix
|
||||
could be extented in a way that is comptible with \f(CWa U b\fR.
|
||||
.PP
|
||||
For more information about monitors, we refer the readers to the
|
||||
following two papers (the first paper describes the construction of
|
||||
the second paper in a more concise way):
|
||||
.TP
|
||||
\(bu
|
||||
Deian Tabakov and Moshe Y. Vardi: Optimized Temporal Monitors for SystemC.
|
||||
Proceedings of RV'10. LNCS 6418.
|
||||
.TP
|
||||
\(bu
|
||||
Marcelo d’Amorim and Grigoire Roşu: Efficient monitoring of
|
||||
ω-languages. Proceedings of CAV'05. LNCS 3576.
|
||||
|
||||
[BIBLIOGRAPHY]
|
||||
If you would like to give a reference to this tool in an article,
|
||||
we suggest you cite one of the following papers:
|
||||
.TP
|
||||
\(bu
|
||||
Alexandre Duret-Lutz: LTL translation improvements in Spot 1.0.
|
||||
Int. J. on Critical Computer-Based Systems, 5(1/2):31--54, March 2014.
|
||||
.TP
|
||||
\(bu
|
||||
Alexandre Duret-Lutz: Manipulating LTL formulas using Spot 1.0.
|
||||
Proceedings of ATVA'13. LNCS 8172.
|
||||
.TP
|
||||
\(bu
|
||||
Tomáš Babiak, Thomas Badie, Alexandre Duret-Lutz, Mojmír Křetínský,
|
||||
and Jan Strejček: Compositional approach to suspension and other
|
||||
improvements to LTL translation. Proceedings of SPIN'13. LNCS 7976.
|
||||
.TP
|
||||
\(bu
|
||||
Souheib Baarir and Alexandre Duret-Lutz: Mechanizing the minimization
|
||||
of deterministic generalized Büchi automata. Proceedings of FORTE'14.
|
||||
LNCS 8461.
|
||||
|
||||
[SEE ALSO]
|
||||
.BR spot-x (7)
|
||||
14
spot/bin/man/ltl2tgta.x
Normal file
14
spot/bin/man/ltl2tgta.x
Normal file
|
|
@ -0,0 +1,14 @@
|
|||
[NAME]
|
||||
ltl2tgta \- translate LTL/PSL formulas into Testing Automata
|
||||
[DESCRIPTION]
|
||||
.\" Add any additional description here
|
||||
[BIBLIOGRAPHY]
|
||||
If you would like to give a reference to this tool in an article,
|
||||
we suggest you cite the following paper:
|
||||
.TP
|
||||
\(bu
|
||||
Ala Eddine Ben Salem, Alexandre Duret-Lutz, and Fabrice Kordon: Model
|
||||
checking using generalized testing automata. Transactions on Petri
|
||||
Nets and Other Models of Concurrency (ToPNoC VI), 7400:94-112, 2012.
|
||||
[SEE ALSO]
|
||||
.BR spot-x (7)
|
||||
270
spot/bin/man/ltlcross.x
Normal file
270
spot/bin/man/ltlcross.x
Normal file
|
|
@ -0,0 +1,270 @@
|
|||
.\" -*- coding: utf-8 -*-
|
||||
[NAME]
|
||||
ltlcross \- cross-compare LTL/PSL translators to Büchi automata
|
||||
[EXAMPLES]
|
||||
The following commands compare never claims produced by
|
||||
.BR ltl2tgba (1),
|
||||
.BR spin (1),
|
||||
and
|
||||
.B lbt
|
||||
for 100 random formulas, using a timeout of 2 minutes. Because
|
||||
.B ltlcross
|
||||
knows those tools, there is no need to specify their input and
|
||||
output. A trace of the execution of the two tools, including any
|
||||
potential issue detected, is reported on standard error, while
|
||||
statistics are written to \f(CWresults.json\fR.
|
||||
.PP
|
||||
.in +4n
|
||||
.nf
|
||||
.ft C
|
||||
% randltl \-n100 \-\-tree\-size=20..30 a b c | \e
|
||||
ltlcross \-T120 ltl2tgba spin lbt \-\-json=results.json
|
||||
.fi
|
||||
.PP
|
||||
The next command compares
|
||||
.BR lbt ,
|
||||
.BR ltl3ba ,
|
||||
and
|
||||
.BR ltl2tgba (1)
|
||||
on a set of formulas saved in file \f(CWinput.ltl\fR.
|
||||
Statistics are again writen
|
||||
as CSV into \f(CWresults.csv\fR. This examples specify the
|
||||
input and output for each tool, to show how this can be done.
|
||||
Note the use of \f(CW%L\fR to indicate that the formula passed t
|
||||
for the formula in
|
||||
.BR spin (1)'s
|
||||
format, and \f(CW%f\fR for the
|
||||
formula in Spot's format. Each of these tool produces an
|
||||
automaton in a different format (respectively, LBTT's format,
|
||||
Spin's never claims, and HOA format), but Spot's parser can
|
||||
distinguish and understand these three formats.
|
||||
.PP
|
||||
.in +4n
|
||||
.nf
|
||||
.ft C
|
||||
% ltlcross \-F input.ltl \-\-csv=results.csv \e
|
||||
'lbt <%L >%O' \e
|
||||
'ltl3ba \-f %s >%O' \e
|
||||
'ltl2tgba \-H %f >%O'
|
||||
.fi
|
||||
.PP
|
||||
Rabin or Streett automata output by
|
||||
.B ltl2dstar
|
||||
in its historical format can be read from a
|
||||
file specified with \f(CW%D\fR instead of \f(CW%O\fR. For instance:
|
||||
.PP
|
||||
.in +4n
|
||||
.nf
|
||||
.ft C
|
||||
% ltlcross \-F input.ltl \e
|
||||
'ltl2dstar \-\-ltl2nba=spin:ltl2tgba@\-Ds %L %D' \e
|
||||
'ltl2dstar \-\-automata=streett \-\-ltl2nba=spin:ltl2tgba@\-Ds %L %D' \e
|
||||
.fi
|
||||
.PP
|
||||
However, we now recommand to use the HOA output of
|
||||
.BR ltl2dstar ,
|
||||
as supported since version 0.5.2:
|
||||
.PP
|
||||
.in +4n
|
||||
.nf
|
||||
.ft C
|
||||
% ltlcross \-F input.ltl \e
|
||||
'ltl2dstar \-\-output\-format=hoa \-\-ltl2nba=spin:ltl2tgba@\-Ds %L %O' \e
|
||||
'ltl2dstar \-\-output\-format=hoa \-\-automata=streett \-\-ltl2nba=spin:ltl2tgba@\-Ds %L %O' \e
|
||||
.fi
|
||||
.PP
|
||||
In more recent versions of ltl2dstar, \fB\-\-output\-format=hoa\fR can
|
||||
be abbreviated \fB-H\fR.
|
||||
|
||||
[ENVIRONMENT VARIABLES]
|
||||
.TP
|
||||
\fBSPOT_TMPDIR\fR, \fBTMPDIR\fR
|
||||
These variables control in which directory temporary files (e.g.,
|
||||
those who contain the input and output when interfacing with
|
||||
translators) are created. \fBTMPDIR\fR is only read if
|
||||
\fBSPOT_TMPDIR\fR does not exist. If none of these environment
|
||||
variables exist, or if their value is empty, files are created in the
|
||||
current directory.
|
||||
.TP
|
||||
\fBSPOT_TMPKEEP\fR
|
||||
When this variable is defined, temporary files are not removed.
|
||||
This is mostly useful for debugging.
|
||||
|
||||
[OUTPUT DATA]
|
||||
The following columns are output in the CSV or JSON files.
|
||||
.TP
|
||||
\fBformula\fR
|
||||
The formula translated.
|
||||
.TP
|
||||
\fBtool\fR
|
||||
The tool used to translate this formula. This is either the value of the
|
||||
full \fICOMMANDFMT\fR string specified on the command-line, or,
|
||||
if \fICOMMANDFMT\fR has the form \f(CW{\fISHORTNAME\fR\f(CW}\fR\fICMD\fR,
|
||||
the value of \fISHORTNAME\fR.
|
||||
.TP
|
||||
\fBexit_status\fR, \fBexit_code\fR
|
||||
Information about how the execution of the translator went. If the
|
||||
option \fB\-\-omit\-missing\fR is given, these two columns are omitted
|
||||
and only the lines corresponding to successful translation are output.
|
||||
Otherwise, \fBexit_status\fR is a string that can take the following
|
||||
values:
|
||||
.RS
|
||||
.TP
|
||||
\f(CW"ok"\fR
|
||||
The translator ran succesfully (this does not imply that the produced
|
||||
automaton is correct) and ltlcross could parse the resulting
|
||||
automaton. In this case \fBexit_code\fR is always 0.
|
||||
.TP
|
||||
\f(CW"timeout"\fR
|
||||
The translator ran for more than the number of seconds
|
||||
specified with the \fB\-\-timeout\fR option. In this
|
||||
case \fBexit_code\fR is always -1.
|
||||
.TP
|
||||
\f(CW"exit code"\fR
|
||||
The translator terminated with a non-zero exit code.
|
||||
\fBexit_code\fR contains that value.
|
||||
.TP
|
||||
\f(CW"signal"\fR
|
||||
The translator terminated with a signal.
|
||||
\fBexit_code\fR contains that signal's number.
|
||||
.TP
|
||||
\f(CW"parse error"\fR
|
||||
The translator terminated normally, but ltlcross could not
|
||||
parse its output. In this case \fBexit_code\fR is always -1.
|
||||
.TP
|
||||
\f(CW"no output"\fR
|
||||
The translator terminated normally, but without creating the specified
|
||||
output file. In this case \fBexit_code\fR is always -1.
|
||||
.RE
|
||||
.TP
|
||||
\fBtime\fR
|
||||
A floating point number giving the run time of the translator in seconds.
|
||||
This is reported for all executions, even failling ones.
|
||||
.PP
|
||||
Unless the \fB\-\-omit\-missing\fR option is used, data for all the
|
||||
following columns might be missing.
|
||||
.TP
|
||||
\fBstates\fR, \fBedges\fR, \fBtransitions\fR, \fBacc\fR
|
||||
The number of states, edges, transitions, and acceptance sets in the
|
||||
translated automaton. Column \fBedges\fR counts the number of edges
|
||||
(labeled by Boolean formulas) in the automaton seen as a graph, while
|
||||
\fBtransitions\fR counts the number of assignment-labeled transitions
|
||||
that might have been merged into a formula-labeled edge. For instance
|
||||
an edge labeled by \f(CWtrue\fR will be counted as 2^3=8 transitions if
|
||||
the automaton mention 3 atomic propositions.
|
||||
.PP
|
||||
If the translator produced a Streett or Rabin automaton, these columns
|
||||
contains the size of a TGBA (or BA) produced by ltlcross from that
|
||||
Streett or Rabin automaton. Check \fBin_states\fR, \fBin_edges\fR,
|
||||
\fBin_transitions\fR, and \fBin_acc\fR for statistics about the actual
|
||||
input automaton.
|
||||
.TP
|
||||
\fBscc\fR, \fBnonacc_scc\fR, \fBterminal_scc\fR, \fBweak_scc\fR, \fBstrong_scc\fR
|
||||
The number of strongly connected components in the automaton. The
|
||||
\fBscc\fR column gives the total number, while the other columns only
|
||||
count the SCCs that are non-accepting (a.k.a. transiant), terminal
|
||||
(recognizes and accepts all words), weak (do not recognize all words,
|
||||
but accepts all recognized words), or strong (accept some words, but
|
||||
reject some recognized words).
|
||||
.TP
|
||||
\fBnondet_states\fR, \fBnondet_aut\fR
|
||||
The number of nondeterministic states, and a Boolean indicating whether the
|
||||
automaton is nondeterministic.
|
||||
.TP
|
||||
\fBterminal_aut\fR, \fBweak_aut\fR, \fBstrong_aut\fR
|
||||
Three Boolean used to indicate whether the automaton is terminal (no
|
||||
weak nor strong SCCs), weak (some weak SCCs but no strong SCCs), or strong
|
||||
(some strong SCCs).
|
||||
.TP
|
||||
\fBproduct_states\fR, \fBproduct_transitions\fR, \fBproduct_scc\fR
|
||||
Size of the product between the translated automaton and a randomly
|
||||
generated state-space. For a given formula, the same state-space is
|
||||
of course used the result of each translator. When the
|
||||
\fB\-\-products\fR=\fIN\fR option is used, these values are averaged
|
||||
over the \fIN\fR products performed.
|
||||
|
||||
[DEPRECATED OUTPUT SPECIFIERS]
|
||||
Until version 1.2.6, the output of translators was specifed using the
|
||||
following escape sequences.
|
||||
.PP
|
||||
.TP
|
||||
%N
|
||||
An output file containing a never claim.
|
||||
.TP
|
||||
%T
|
||||
An output file in \fBlbtt\fR's format.
|
||||
.TP
|
||||
%D
|
||||
An output file in \fBltl2dstar\fR's format.
|
||||
.PP
|
||||
Some development versions leading to 1.99.1 also featured
|
||||
.PP
|
||||
.TP
|
||||
%H
|
||||
An output file in the HOA format.
|
||||
.PP
|
||||
Different specifiers were needed because Spot implemented
|
||||
different parsers for these formats. Nowadays, these parsers
|
||||
have been merged into a single parser that is able to
|
||||
distinguish these four formats automatically.
|
||||
.B ltlcross
|
||||
officially supports only one output specifier:
|
||||
.TP
|
||||
%O
|
||||
An output file in either \fBlbtt\fR's format, \fBltl2dstar\fR's format,
|
||||
as a never claim, or in the HOA format
|
||||
.P
|
||||
For backward compatibility, the sequences %D, %H, %N, and %T, are
|
||||
still supported (as aliases for %O), but are deprecated.
|
||||
|
||||
[SEE ALSO]
|
||||
.BR randltl (1),
|
||||
.BR genltl (1),
|
||||
.BR ltlfilt (1),
|
||||
.BR ltl2tgba (1),
|
||||
.BR ltldo (1)
|
||||
|
||||
[BIBLIOGRAPHY]
|
||||
If you would like to give a reference to this tool in an article,
|
||||
we suggest you cite the following paper:
|
||||
.PP
|
||||
.TP
|
||||
\(bu
|
||||
Alexandre Duret-Lutz: Manipulating LTL formulas using Spot 1.0.
|
||||
Proceedings of ATVA'13. LNCS 8172.
|
||||
.PP
|
||||
.B ltlcross
|
||||
is a Spot-based reimplementation of a tool called LBTT. LBTT
|
||||
was developped by Heikki Tauriainen at the Helsinki University of
|
||||
Technology. The main motivation for the reimplementation was to
|
||||
support PSL, and output more statistics about the translations.
|
||||
.PP
|
||||
The sanity checks performed on the result of each translator (by
|
||||
either LBTT or ltlcross) are described in the following paper:
|
||||
.PP
|
||||
.TP
|
||||
\(bu
|
||||
H. Tauriainen and K. Heljanko: Testing LTL formula translation into
|
||||
Büchi automata. Int. J. on Software Tools for Technology Transfer.
|
||||
Volume 4, number 1, October 2002.
|
||||
.PP
|
||||
LBTT did not implement Test 2 described in this paper. ltlcross
|
||||
implements a slight variation: when an automaton produced by some
|
||||
translator is deterministic, its complement is built and used for
|
||||
additional cross-comparisons with other tools. If the translation P1
|
||||
of the positive formula and the translation N1 of the negative formula
|
||||
both yield deterministic automata (this may only happen for obligation
|
||||
properties) then the emptiness check of Comp(P1)*Comp(N1) is
|
||||
equivalent to Test 2 of Tauriainen and Heljanko. If only one
|
||||
automaton is deterministic, say P1, it can still be used to check we
|
||||
can be used to check the result of another translators, for instance
|
||||
checking the emptiness of Comp(P1)*P2.
|
||||
.PP
|
||||
Our implementation will detect and reports problems (like
|
||||
inconsistencies between two translations) but unlike LBTT it does not
|
||||
offer an interactive mode to investigate such problems.
|
||||
.PP
|
||||
Another major difference with LBTT is that ltlcross is
|
||||
not restricted to generalized Büchi acceptance. It supports
|
||||
Rabin and Streett automata via ltl2dstar's format, and automata
|
||||
with arbitrary acceptance conditions via the HOA format.
|
||||
8
spot/bin/man/ltldo.x
Normal file
8
spot/bin/man/ltldo.x
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
[NAME]
|
||||
ltldo \- run LTL/PSL formulas through other tools
|
||||
[SEE ALSO]
|
||||
.BR randltl (1),
|
||||
.BR genltl (1),
|
||||
.BR ltlfilt (1),
|
||||
.BR ltl2tgba (1),
|
||||
.BR ltldo (1)
|
||||
48
spot/bin/man/ltlfilt.x
Normal file
48
spot/bin/man/ltlfilt.x
Normal file
|
|
@ -0,0 +1,48 @@
|
|||
[NAME]
|
||||
ltlfilt \- filter files or lists of LTL/PSL formulas
|
||||
[DESCRIPTION]
|
||||
.\" Add any additional description here
|
||||
[BIBLIOGRAPHY]
|
||||
If you would like to give a reference to this tool in an article,
|
||||
we suggest you cite the following paper:
|
||||
.TP
|
||||
\(bu
|
||||
Alexandre Duret-Lutz: Manipulating LTL formulas using Spot 1.0.
|
||||
Proceedings of ATVA'13. LNCS 8172.
|
||||
.PP
|
||||
The following papers describe algorithms used by ltlfilt:
|
||||
.TP
|
||||
\(bu
|
||||
Kousha Etessami: A note on a question of Peled and Wilke regarding
|
||||
stutter-invariant LTL. Information Processing Letters 75(6): 261-263
|
||||
(2000).
|
||||
|
||||
Describes the transformation behind the \fB\-\-remove\-x\fR option.
|
||||
.TP
|
||||
\(bu
|
||||
Christian Dax, Jochen Eisinger, Felix Klaedtke: Mechanizing the
|
||||
Powerset Construction for Restricted Classes of
|
||||
ω-Automata. Proceedings of ATVA'07. LNCS 4762.
|
||||
|
||||
Describes the checks implemented by the \fB\-\-safety\fR,
|
||||
\fB\-\-guarantee\fR, and \fB\-\-obligation\fR options.
|
||||
.TP
|
||||
\(bu
|
||||
Ivana Černá, Radek Pelánek: Relating Hierarchy of Temporal Properties
|
||||
to Model Checking. Proceedings of MFCS'03. LNCS 2747.
|
||||
|
||||
Describes the syntactic LTL classes matched by the
|
||||
\fB\-\-syntactic\-safety\fR, \fB\-\-syntactic\-guarantee\fR,
|
||||
\fB\-\-syntactic\-obligation\fR options,
|
||||
\fB\-\-syntactic\-persistence\fR, and \fB\-\-syntactic\-recurrence\fR
|
||||
options.
|
||||
.TP
|
||||
\(bu
|
||||
Kousha Etessami, Gerard J. Holzmann: Optimizing Büchi
|
||||
Automata. Proceedings of CONCUR'00. LNCS 1877.
|
||||
|
||||
Describe the syntactic LTL classes matched by \fB\-\-eventual\fR, and
|
||||
\fB\-\-universal\fR.
|
||||
[SEE ALSO]
|
||||
.BR randltl (1),
|
||||
.BR ltldo (1)
|
||||
6
spot/bin/man/ltlgrind.x
Normal file
6
spot/bin/man/ltlgrind.x
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
[NAME]
|
||||
ltlgrind \- list mutations of a formula.
|
||||
[DESCRIPTION]
|
||||
.\" Add any additional description here
|
||||
[SEE ALSO]
|
||||
.BR ltlcross (1)
|
||||
7
spot/bin/man/randaut.x
Normal file
7
spot/bin/man/randaut.x
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
[NAME]
|
||||
randaut \- generate random automata
|
||||
[DESCRIPTION]
|
||||
.\" Add any additional description here
|
||||
[SEE ALSO]
|
||||
.BR randltl (1),
|
||||
.BR autfilt (1)
|
||||
15
spot/bin/man/randltl.x
Normal file
15
spot/bin/man/randltl.x
Normal file
|
|
@ -0,0 +1,15 @@
|
|||
[NAME]
|
||||
randltl \- generate random LTL/PSL formulas
|
||||
[DESCRIPTION]
|
||||
.\" Add any additional description here
|
||||
[BIBLIOGRAPHY]
|
||||
If you would like to give a reference to this tool in an article,
|
||||
we suggest you cite the following paper:
|
||||
.TP
|
||||
\(bu
|
||||
Alexandre Duret-Lutz: Manipulating LTL formulas using Spot 1.0.
|
||||
Proceedings of ATVA'13. LNCS 8172.
|
||||
[SEE ALSO]
|
||||
.BR genltl (1),
|
||||
.BR ltlfilt (1),
|
||||
.BR randaut (1)
|
||||
123
spot/bin/man/spot-x.x
Normal file
123
spot/bin/man/spot-x.x
Normal file
|
|
@ -0,0 +1,123 @@
|
|||
[NAME]
|
||||
spot-x \- Common fine-tuning options.
|
||||
|
||||
[SYNOPSIS]
|
||||
.B \-\-extra-options STRING
|
||||
.br
|
||||
.B \-x STRING
|
||||
|
||||
[DESCRIPTION]
|
||||
.\" Add any additional description here
|
||||
|
||||
[ENVIRONMENT VARIABLES]
|
||||
|
||||
.TP
|
||||
\fBSPOT_DOTDEFAULT\fR
|
||||
Whenever the \f(CW--dot\fR option is used without argument (even
|
||||
implicitely), the contents of this variable is used as default
|
||||
argument. If you have some default setting in \fBSPOT_DOTDEFAULT\fR
|
||||
but want to alter them temporarily for one call, use
|
||||
\f(CW--dot=.yyy\fR: the dot character will be replaced by the contents
|
||||
of the \f(CWSPOT_DOTDEFAULT\fR environment variable.
|
||||
|
||||
.TP
|
||||
\fBSPOT_DOTEXTRA\fR
|
||||
The contents of this variable is added to any dot output, immediately
|
||||
before the first state is output. This makes it easy to override
|
||||
global attributes of the graph.
|
||||
|
||||
.TP
|
||||
\fBSPOT_SATLOG\fR
|
||||
If set to a filename, the SAT-based minimization routines will append
|
||||
statistics about each iteration to the named file. Each line lists
|
||||
the following comma-separated values: requested number of states,
|
||||
number of reachable states in the output, number of edges in the
|
||||
output, number of transitions in the output, number of variables in
|
||||
the SAT problem, number of clauses in the SAT problem, user time for
|
||||
encoding the SAT problem, system time for encoding the SAT problem,
|
||||
user time for solving the SAT problem, system time for solving the SAT
|
||||
problem.
|
||||
|
||||
.TP
|
||||
\fBSPOT_SATSOLVER\fR
|
||||
If set, this variable should indicate how to call a SAT\-solver. This
|
||||
is used by the sat\-minimize option described above. The default
|
||||
value is \f(CW"glucose -verb=0 -model %I >%O"\fR, it is correct for
|
||||
glucose version 3.0 (for older versions, remove the \fCW(-model\fR
|
||||
option). The escape sequences \f(CW%I\fR and \f(CW%O\fR respectively
|
||||
denote the names of the input and output files. These temporary files
|
||||
are created in the directory specified by \fBSPOT_TMPDIR\fR or
|
||||
\fBTMPDIR\fR (see below). The SAT-solver should follow the convention
|
||||
of the SAT Competition for its input and output format.
|
||||
|
||||
.TP
|
||||
\fBSPOT_STREETT_CONV_MIN\fR
|
||||
The number of Streett pairs above which conversion from Streett
|
||||
acceptance to generalized-Büchi acceptance should be made with a
|
||||
dedicated algorithm. By default this is 3, i.e., if a Streett
|
||||
automaton with 3 acceptance pairs or more has to be converted into
|
||||
generalized-Büchi, the dedicated algorithm is used. This algorithm is
|
||||
close to the classical conversion from Streett to Büchi, but with
|
||||
several tweaks. When this algorithm is not used, the standard
|
||||
"Fin-removal" approach is used instead: first the acceptance condition
|
||||
is converted into disjunctive normal form (DNF), then Fin acceptance
|
||||
is removed like for Rabin automata, yielding a disjuction of
|
||||
generalized Büchi acceptance, and the result is finally converted into
|
||||
conjunctive normal form (CNF) to obtain a generalized Büchi
|
||||
acceptance. Both algorithms have a worst-case size that is
|
||||
exponential in the number of Streett pairs, but in practice the
|
||||
dedicated algorithm works better for most Streett automata with 3 or
|
||||
more pairs (and many 2-pair Streett automata as well, but the
|
||||
difference here is less clear). Setting this variable to 0 will
|
||||
disable the dedicated algorithm. Setting it to 1 will enable it for
|
||||
all Streett automata, however we do not recommand setting it to less
|
||||
than 2, because the "Fin-removal" approach is better for single-pair
|
||||
Streett automata.
|
||||
|
||||
.TP
|
||||
\fBSPOT_TMPDIR\fR, \fBTMPDIR\fR
|
||||
These variables control in which directory temporary files (e.g.,
|
||||
those who contain the input and output when interfacing with
|
||||
translators) are created. \fBTMPDIR\fR is only read if
|
||||
\fBSPOT_TMPDIR\fR does not exist. If none of these environment
|
||||
variables exist, or if their value is empty, files are created in the
|
||||
current directory.
|
||||
|
||||
.TP
|
||||
\fBSPOT_TMPKEEP\fR
|
||||
When this variable is defined, temporary files are not removed.
|
||||
This is mostly useful for debugging.
|
||||
|
||||
[BIBLIOGRAPHY]
|
||||
.TP
|
||||
1.
|
||||
Christian Dax, Jochen Eisinger, Felix Klaedtke: Mechanizing the
|
||||
Powerset Construction for Restricted Classes of
|
||||
ω-Automata. Proceedings of ATVA'07. LNCS 4762.
|
||||
|
||||
Describes the WDBA-minimization algorithm implemented in Spot. The
|
||||
algorithm used for the tba-det options is also a generalization (to
|
||||
TBA instead of BA) of what they describe in sections 3.2 and 3.3.
|
||||
|
||||
.TP
|
||||
2.
|
||||
Tomáš Babiak, Thomas Badie, Alexandre Duret-Lutz, Mojmír Křetínský,
|
||||
Jan Strejček: Compositional Approach to Suspension and Other
|
||||
Improvements to LTL Translation. Proceedings of SPIN'13. LNCS 7976.
|
||||
|
||||
Describes the compositional suspension, the simulation-based
|
||||
reductions, and the SCC-based simplifications.
|
||||
|
||||
.TP
|
||||
3.
|
||||
Rüdiger Ehlers: Minimising Deterministic Büchi Automata Precisely using
|
||||
SAT Solving. Proceedings of SAT'10. LNCS 6175.
|
||||
|
||||
Our SAT-based minimization procedures are generalizations of this
|
||||
paper to deal with TBA or TGBA.
|
||||
|
||||
[SEE ALSO]
|
||||
.BR ltl2tgba (1)
|
||||
.BR ltl2tgta (1)
|
||||
.BR dstar2tgba (1)
|
||||
.BR autfilt (1)
|
||||
71
spot/bin/options.py
Executable file
71
spot/bin/options.py
Executable file
|
|
@ -0,0 +1,71 @@
|
|||
#!/usr/bin/python
|
||||
# -*- coding: utf-8 -*-
|
||||
# Copyright (C) 2014 Laboratoire de Recherche et
|
||||
# Développement de l'Epita (LRDE).
|
||||
#
|
||||
# This file is part of Spot, a model checking library.
|
||||
#
|
||||
# Spot is free software; you can redistribute it and/or modify it
|
||||
# under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation; either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# Spot is distributed in the hope that it will be useful, but WITHOUT
|
||||
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
||||
# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public
|
||||
# License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
|
||||
# Run all binaries, and collect the long option associated to each
|
||||
# short option for easy comparison.
|
||||
# This script should work with both Python 2 and 3.
|
||||
|
||||
from sys import stdout as out
|
||||
import re
|
||||
import subprocess
|
||||
|
||||
with open('Makefile.am', 'r') as mf:
|
||||
lines = mf.read()
|
||||
|
||||
lines = re.sub('\s*\\\\\s*', ' ', lines)
|
||||
bin_programs = re.search('bin_PROGRAMS\s*=([\w \t]*)', lines).group(1).split()
|
||||
|
||||
optre = re.compile('(-\w), (--[\w=-]+)')
|
||||
|
||||
d = {}
|
||||
|
||||
for tool in bin_programs:
|
||||
args = ('./' + tool, '--help')
|
||||
try:
|
||||
popen = subprocess.Popen(args, stdout=subprocess.PIPE)
|
||||
except OSError:
|
||||
print("Cannot execute " + tool + ", is it compiled?")
|
||||
exit(1)
|
||||
popen.wait()
|
||||
output = popen.communicate()[0].decode('utf-8')
|
||||
|
||||
for match in optre.finditer(output):
|
||||
shortname, longname = match.group(1), match.group(2)
|
||||
if not shortname in d:
|
||||
d[shortname] = { longname: tool }
|
||||
elif not longname in d[shortname]:
|
||||
d[shortname][longname] = tool
|
||||
else:
|
||||
w = ('%29s' % '') + d[shortname][longname]
|
||||
w = w[w.rfind('\n') + 1 : -1]
|
||||
if len(w + ' ' + tool) < 80:
|
||||
d[shortname][longname] += ' ' + tool
|
||||
else:
|
||||
d[shortname][longname] += '\n%29s%s' % ('', tool)
|
||||
|
||||
# The lambda function works around the fact that x might be an str or
|
||||
# a unicode object depending on the Python implementation.
|
||||
for shortname in sorted(d, key=lambda x: x.lower()):
|
||||
out.write(shortname)
|
||||
first=''
|
||||
for longname in sorted(d[shortname]):
|
||||
out.write('%s %-24s %s\n' % (first, longname, d[shortname][longname]))
|
||||
first=' '
|
||||
397
spot/bin/randaut.cc
Normal file
397
spot/bin/randaut.cc
Normal file
|
|
@ -0,0 +1,397 @@
|
|||
// -*- coding: utf-8 -*-
|
||||
// Copyright (C) 2012, 2013, 2014, 2015 Laboratoire de Recherche et
|
||||
// Développement de l'Epita (LRDE).
|
||||
//
|
||||
// This file is part of Spot, a model checking library.
|
||||
//
|
||||
// Spot is free software; you can redistribute it and/or modify it
|
||||
// under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation; either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// Spot is distributed in the hope that it will be useful, but WITHOUT
|
||||
// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
||||
// or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public
|
||||
// License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
#include "common_sys.hh"
|
||||
|
||||
#include <iostream>
|
||||
#include <fstream>
|
||||
#include <argp.h>
|
||||
#include <cstdlib>
|
||||
#include <sstream>
|
||||
#include <iterator>
|
||||
#include "error.h"
|
||||
#include "argmatch.h"
|
||||
|
||||
#include "common_setup.hh"
|
||||
#include "common_range.hh"
|
||||
#include "common_cout.hh"
|
||||
#include "common_aoutput.hh"
|
||||
#include "common_conv.hh"
|
||||
|
||||
#include <spot/misc/timer.hh>
|
||||
#include <spot/misc/random.hh>
|
||||
|
||||
#include <spot/twa/bddprint.hh>
|
||||
#include <spot/twaalgos/randomgraph.hh>
|
||||
#include <spot/twaalgos/canonicalize.hh>
|
||||
|
||||
|
||||
const char argp_program_doc[] = "\
|
||||
Generate random connected automata.\n\n\
|
||||
The automata are built over the atomic propositions named by PROPS...\n\
|
||||
or, if N is a nonnegative number, using N arbitrary names.\n\
|
||||
If the density is set to D, and the number of states to Q, the degree\n\
|
||||
of each state follows a normal distribution with mean 1+(Q-1)D and\n\
|
||||
variance (Q-1)D(1-D). In particular, for D=0 all states have a single\n\
|
||||
successor, while for D=1 all states are interconnected.\v\
|
||||
Examples:\n\
|
||||
\n\
|
||||
This builds a random neverclaim with 4 states and labeled using the two\n\
|
||||
atomic propositions \"a\" and \"b\":\n\
|
||||
% randaut --spin -Q4 a b\n\
|
||||
\n\
|
||||
This builds three random, complete, and deterministic TGBA with 5 to 10\n\
|
||||
states, 1 to 3 acceptance sets, and three atomic propositions:\n\
|
||||
% randaut -n3 -D -H -Q5..10 -A1..3 3\n\
|
||||
\n\
|
||||
Build 3 random, complete, and deterministic Rabin automata\n\
|
||||
with 2 to 3 acceptance pairs, state-based acceptance, 8 states, \n\
|
||||
a high density of edges, and 3 to 4 atomic propositions:\n\
|
||||
% randaut -n3 -D -H -Q8 -d.8 -S -A 'Rabin 2..3' 3..4\n\
|
||||
";
|
||||
|
||||
enum {
|
||||
OPT_SEED = 1,
|
||||
OPT_COLORED,
|
||||
};
|
||||
|
||||
static const argp_option options[] =
|
||||
{
|
||||
/**************************************************/
|
||||
{ nullptr, 0, nullptr, 0, "Generation:", 1 },
|
||||
{ "acceptance", 'A', "ACCEPTANCE", 0,
|
||||
"specify the acceptance type of the automaton", 0 },
|
||||
{ "acc-probability", 'a', "FLOAT", 0,
|
||||
"probability that an edge belongs to one acceptance set (0.2)", 0 },
|
||||
{ "automata", 'n', "INT", 0, "number of automata to output (1)\n"\
|
||||
"use a negative value for unbounded generation", 0 },
|
||||
{ "ba", 'B', nullptr, 0,
|
||||
"build a Buchi automaton (implies --acceptance=Buchi --state-acc)", 0 },
|
||||
{ "colored", OPT_COLORED, nullptr, 0,
|
||||
"build an automaton in which each edge (or state if combined with "
|
||||
"-S) belong to a single acceptance set", 0 },
|
||||
{ "density", 'd', "FLOAT", 0, "density of the edges (0.2)", 0 },
|
||||
{ "deterministic", 'D', nullptr, 0,
|
||||
"build a complete, deterministic automaton ", 0 },
|
||||
{ "unique", 'u', nullptr, 0,
|
||||
"do not output the same automaton twice (same in the sense that they "\
|
||||
"are isomorphic)", 0 },
|
||||
{ "seed", OPT_SEED, "INT", 0,
|
||||
"seed for the random number generator (0)", 0 },
|
||||
{ "states", 'Q', "RANGE", 0, "number of states to output (10)", 0 },
|
||||
{ "state-based-acceptance", 'S', nullptr, 0,
|
||||
"used state-based acceptance", 0 },
|
||||
{ "sbacc", 0, nullptr, OPTION_ALIAS, nullptr, 0 },
|
||||
RANGE_DOC,
|
||||
{ nullptr, 0, nullptr, 0, "ACCEPTANCE may be either a RANGE (in which case "
|
||||
"generalized Büchi is assumed), or an arbitrary acceptance formula "
|
||||
"such as 'Fin(0)|Inf(1)&Fin(2)' in the same syntax as in the HOA "
|
||||
"format, or one of the following patterns:\n"
|
||||
" none\n"
|
||||
" all\n"
|
||||
" Buchi\n"
|
||||
" co-Buchi\n"
|
||||
" generalized-Buchi RANGE\n"
|
||||
" generalized-co-Buchi RANGE\n"
|
||||
" Rabin RANGE\n"
|
||||
" Streett RANGE\n"
|
||||
" generalized-Rabin INT RANGE RANGE ... RANGE\n"
|
||||
" parity (min|max|rand) (odd|even|rand) RANGE\n"
|
||||
" random RANGE\n"
|
||||
" random RANGE PROBABILITY\n"
|
||||
"The random acceptance condition uses each set only once, "
|
||||
"unless a probability (to reuse the set again every time it is used) "
|
||||
"is given.", 2 },
|
||||
/**************************************************/
|
||||
{ nullptr, 0, nullptr, 0, "Miscellaneous options:", -1 },
|
||||
{ nullptr, 0, nullptr, 0, nullptr, 0 }
|
||||
};
|
||||
|
||||
|
||||
static const struct argp_child children[] =
|
||||
{
|
||||
{ &aoutput_argp, 0, nullptr, 3 },
|
||||
{ &aoutput_o_format_argp, 0, nullptr, 4 },
|
||||
{ &misc_argp, 0, nullptr, -1 },
|
||||
{ nullptr, 0, nullptr, 0 }
|
||||
};
|
||||
|
||||
static const char* opt_acceptance = nullptr;
|
||||
typedef spot::twa_graph::graph_t::edge_storage_t tr_t;
|
||||
typedef std::set<std::vector<tr_t>> unique_aut_t;
|
||||
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";
|
||||
static int opt_automata = 1;
|
||||
static range opt_states = { 10, 10 };
|
||||
static float opt_density = 0.2;
|
||||
static range opt_acc_sets = { -1, 0 };
|
||||
static float opt_acc_prob = 0.2;
|
||||
static bool opt_deterministic = false;
|
||||
static bool opt_state_acc = false;
|
||||
static bool opt_colored = false;
|
||||
static bool ba_wanted = false;
|
||||
static bool generic_wanted = false;
|
||||
static bool gba_wanted = false;
|
||||
static std::unique_ptr<unique_aut_t> opt_uniq = nullptr;
|
||||
|
||||
static void
|
||||
ba_options()
|
||||
{
|
||||
opt_acc_sets = { 1, 1 };
|
||||
opt_state_acc = true;
|
||||
}
|
||||
|
||||
// Range should have the form 12..34 or 12:34, maybe with spaces. The
|
||||
// characters between '.' and ':' include all digits plus '/', but the
|
||||
// parser will later choke on '/' if it is used, so let's not worry
|
||||
// here.
|
||||
static bool
|
||||
looks_like_a_range(const char* str)
|
||||
{
|
||||
while (*str == ' ' || (*str >= '.' && *str <= ':'))
|
||||
++str;
|
||||
return !*str;
|
||||
}
|
||||
|
||||
static int
|
||||
parse_opt(int key, char* arg, struct argp_state* as)
|
||||
{
|
||||
// This switch is alphabetically-ordered.
|
||||
switch (key)
|
||||
{
|
||||
case '8':
|
||||
spot::enable_utf8();
|
||||
break;
|
||||
case 'a':
|
||||
opt_acc_prob = to_float(arg);
|
||||
if (opt_acc_prob < 0.0 || opt_acc_prob > 1.0)
|
||||
error(2, 0, "probability of acceptance set membership "
|
||||
"should be between 0.0 and 1.0");
|
||||
break;
|
||||
case 'A':
|
||||
if (looks_like_a_range(arg))
|
||||
{
|
||||
opt_acc_sets = parse_range(arg);
|
||||
if (opt_acc_sets.min > opt_acc_sets.max)
|
||||
std::swap(opt_acc_sets.min, opt_acc_sets.max);
|
||||
if (opt_acc_sets.min < 0)
|
||||
error(2, 0, "number of acceptance sets should be positive");
|
||||
gba_wanted = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
opt_acceptance = arg;
|
||||
generic_wanted = true;
|
||||
}
|
||||
break;
|
||||
case 'B':
|
||||
ba_options();
|
||||
ba_wanted = true;
|
||||
break;
|
||||
case 'd':
|
||||
opt_density = to_float(arg);
|
||||
if (opt_density < 0.0 || opt_density > 1.0)
|
||||
error(2, 0, "density should be between 0.0 and 1.0");
|
||||
break;
|
||||
case 'D':
|
||||
opt_deterministic = true;
|
||||
break;
|
||||
case 'n':
|
||||
opt_automata = to_int(arg);
|
||||
break;
|
||||
case 'Q':
|
||||
opt_states = parse_range(arg);
|
||||
if (opt_states.min > opt_states.max)
|
||||
std::swap(opt_states.min, opt_states.max);
|
||||
if (opt_states.min == 0)
|
||||
error(1, 0, "cannot build an automaton with 0 states");
|
||||
break;
|
||||
case 'S':
|
||||
opt_state_acc = true;
|
||||
break;
|
||||
case 'u':
|
||||
opt_uniq =
|
||||
std::unique_ptr<unique_aut_t>(new std::set<std::vector<tr_t>>());
|
||||
break;
|
||||
case OPT_COLORED:
|
||||
opt_colored = true;
|
||||
break;
|
||||
case OPT_SEED:
|
||||
opt_seed = to_int(arg);
|
||||
opt_seed_str = arg;
|
||||
break;
|
||||
case ARGP_KEY_ARG:
|
||||
// If this is the unique non-option argument, it can
|
||||
// be a number of atomic propositions to build.
|
||||
//
|
||||
// argp reorganizes argv[] so that options always come before
|
||||
// non-options. So if as->argc == as->next we know this is the
|
||||
// last non-option argument, and if aprops.empty() we know this
|
||||
// is the also the first one.
|
||||
if (aprops.empty() && as->argc == as->next && looks_like_a_range(arg))
|
||||
{
|
||||
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::create_atomic_prop_set(ap_count_given.min);
|
||||
break;
|
||||
}
|
||||
aprops.insert(spot::formula::ap(arg));
|
||||
break;
|
||||
|
||||
default:
|
||||
return ARGP_ERR_UNKNOWN;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
int
|
||||
main(int argc, char** argv)
|
||||
{
|
||||
strcpy(F_doc, "seed number");
|
||||
strcpy(L_doc, "automaton number");
|
||||
setup(argv);
|
||||
|
||||
const argp ap = { options, parse_opt, "N|PROP...", argp_program_doc,
|
||||
children, nullptr, nullptr };
|
||||
|
||||
if (int err = argp_parse(&ap, argc, argv, ARGP_NO_HELP, nullptr, nullptr))
|
||||
exit(err);
|
||||
|
||||
// running 'randaut 0' is one way to generate automata using no
|
||||
// atomic propositions so do not complain in that case.
|
||||
if (aprops.empty() && ap_count_given.max < 0)
|
||||
error(2, 0, "No atomic proposition supplied? Run '%s --help' for usage.",
|
||||
program_name);
|
||||
|
||||
if (generic_wanted && automaton_format == Spin)
|
||||
error(2, 0, "--spin implies --ba so should not be used with --acceptance");
|
||||
if (generic_wanted && ba_wanted)
|
||||
error(2, 0, "--acceptance and --ba may not be used together");
|
||||
|
||||
if (automaton_format == Spin && opt_acc_sets.max > 1)
|
||||
error(2, 0, "--spin is incompatible with --acceptance=%d..%d",
|
||||
opt_acc_sets.min, opt_acc_sets.max);
|
||||
if (ba_wanted && opt_acc_sets.min != 1 && opt_acc_sets.max != 1)
|
||||
error(2, 0, "--ba is incompatible with --acceptance=%d..%d",
|
||||
opt_acc_sets.min, opt_acc_sets.max);
|
||||
if (ba_wanted && generic_wanted)
|
||||
error(2, 0, "--ba is incompatible with --acceptance=%s", opt_acceptance);
|
||||
|
||||
if (automaton_format == Spin)
|
||||
ba_options();
|
||||
|
||||
if (opt_colored && opt_acc_sets.min == -1 && !generic_wanted)
|
||||
error(2, 0, "--colored requires at least one acceptance set; "
|
||||
"use --acceptance");
|
||||
if (opt_colored && opt_acc_sets.min == 0)
|
||||
error(2, 0, "--colored requires at least one acceptance set; "
|
||||
"fix the range of --acceptance");
|
||||
|
||||
if (opt_acc_sets.min == -1)
|
||||
opt_acc_sets.min = 0;
|
||||
|
||||
|
||||
|
||||
try
|
||||
{
|
||||
spot::srand(opt_seed);
|
||||
auto d = spot::make_bdd_dict();
|
||||
|
||||
automaton_printer printer;
|
||||
|
||||
constexpr unsigned max_trials = 10000;
|
||||
unsigned trials = max_trials;
|
||||
|
||||
int automaton_num = 0;
|
||||
|
||||
for (;;)
|
||||
{
|
||||
spot::stopwatch sw;
|
||||
sw.start();
|
||||
|
||||
if (ap_count_given.max > 0
|
||||
&& ap_count_given.min != ap_count_given.max)
|
||||
{
|
||||
int c = spot::rrand(ap_count_given.min, ap_count_given.max);
|
||||
aprops = spot::create_atomic_prop_set(c);
|
||||
}
|
||||
|
||||
int size = opt_states.min;
|
||||
if (size != opt_states.max)
|
||||
size = spot::rrand(size, opt_states.max);
|
||||
|
||||
int accs = opt_acc_sets.min;
|
||||
if (accs != opt_acc_sets.max)
|
||||
accs = spot::rrand(accs, opt_acc_sets.max);
|
||||
|
||||
spot::acc_cond::acc_code code;
|
||||
if (opt_acceptance)
|
||||
{
|
||||
code = spot::parse_acc_code(opt_acceptance);
|
||||
accs = code.used_sets().max_set();
|
||||
if (opt_colored && accs == 0)
|
||||
error(2, 0, "--colored requires at least one acceptance set; "
|
||||
"fix the range of --acceptance");
|
||||
}
|
||||
|
||||
auto aut =
|
||||
spot::random_graph(size, opt_density, &aprops, d,
|
||||
accs, opt_acc_prob, 0.5,
|
||||
opt_deterministic, opt_state_acc,
|
||||
opt_colored);
|
||||
|
||||
if (opt_acceptance)
|
||||
aut->set_acceptance(accs, code);
|
||||
|
||||
if (opt_uniq)
|
||||
{
|
||||
auto tmp = spot::canonicalize
|
||||
(make_twa_graph(aut, spot::twa::prop_set::all()));
|
||||
std::vector<tr_t> trans(tmp->edge_vector().begin() + 1,
|
||||
tmp->edge_vector().end());
|
||||
if (!opt_uniq->emplace(trans).second)
|
||||
{
|
||||
--trials;
|
||||
if (trials == 0)
|
||||
error(2, 0, "failed to generate a new unique automaton"
|
||||
" after %d trials", max_trials);
|
||||
continue;
|
||||
}
|
||||
trials = max_trials;
|
||||
}
|
||||
|
||||
auto runtime = sw.stop();
|
||||
|
||||
printer.print(aut, nullptr,
|
||||
opt_seed_str, automaton_num, runtime, nullptr);
|
||||
|
||||
++automaton_num;
|
||||
if (opt_automata > 0 && automaton_num >= opt_automata)
|
||||
break;
|
||||
}
|
||||
}
|
||||
catch (const std::runtime_error& e)
|
||||
{
|
||||
error(2, 0, "%s", e.what());
|
||||
}
|
||||
}
|
||||
317
spot/bin/randltl.cc
Normal file
317
spot/bin/randltl.cc
Normal file
|
|
@ -0,0 +1,317 @@
|
|||
// -*- coding: utf-8 -*-
|
||||
// Copyright (C) 2012, 2013, 2014, 2015 Laboratoire de Recherche et
|
||||
// Développement de l'Epita (LRDE).
|
||||
//
|
||||
// This file is part of Spot, a model checking library.
|
||||
//
|
||||
// Spot is free software; you can redistribute it and/or modify it
|
||||
// under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation; either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// Spot is distributed in the hope that it will be useful, but WITHOUT
|
||||
// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
||||
// or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public
|
||||
// License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
#include "common_sys.hh"
|
||||
|
||||
#include <iostream>
|
||||
#include <fstream>
|
||||
#include <argp.h>
|
||||
#include <cstdlib>
|
||||
#include <iterator>
|
||||
#include "error.h"
|
||||
|
||||
#include "common_setup.hh"
|
||||
#include "common_output.hh"
|
||||
#include "common_range.hh"
|
||||
#include "common_r.hh"
|
||||
#include "common_conv.hh"
|
||||
|
||||
#include <sstream>
|
||||
#include <spot/tl/randomltl.hh>
|
||||
#include <spot/tl/simplify.hh>
|
||||
#include <spot/misc/random.hh>
|
||||
#include <spot/misc/optionmap.hh>
|
||||
|
||||
const char argp_program_doc[] ="\
|
||||
Generate random temporal logic formulas.\n\n\
|
||||
The formulas are built over the atomic propositions named by PROPS...\n\
|
||||
or, if N is a nonnegative number, using N arbitrary names.\v\
|
||||
Examples:\n\
|
||||
\n\
|
||||
The following generates 10 random LTL formulas over the propositions a, b,\n\
|
||||
and c, with the default tree-size, and all available operators.\n\
|
||||
% randltl -n10 a b c\n\
|
||||
\n\
|
||||
If you do not mind about the name of the atomic propositions, just give\n\
|
||||
a number instead:\n\
|
||||
% randltl -n10 3\n\
|
||||
\n\
|
||||
You can disable or favor certain operators by changing their priority.\n\
|
||||
The following disables xor, implies, and equiv, and multiply the probability\n\
|
||||
of X to occur by 10.\n\
|
||||
% randltl --ltl-priorities='xor=0, implies=0, equiv=0, X=10' -n10 a b c\n\
|
||||
";
|
||||
|
||||
enum {
|
||||
OPT_BOOLEAN_PRIORITIES = 1,
|
||||
OPT_DUMP_PRIORITIES,
|
||||
OPT_DUPS,
|
||||
OPT_LTL_PRIORITIES,
|
||||
OPT_PSL_PRIORITIES,
|
||||
OPT_SEED,
|
||||
OPT_SERE_PRIORITIES,
|
||||
OPT_TREE_SIZE,
|
||||
OPT_WF,
|
||||
};
|
||||
|
||||
static const argp_option options[] =
|
||||
{
|
||||
// Keep this alphabetically sorted (expect for aliases).
|
||||
/**************************************************/
|
||||
{ nullptr, 0, nullptr, 0, "Type of formula to generate:", 1 },
|
||||
{ "boolean", 'B', nullptr, 0, "generate Boolean formulas", 0 },
|
||||
{ "ltl", 'L', nullptr, 0, "generate LTL formulas (default)", 0 },
|
||||
{ "sere", 'S', nullptr, 0, "generate SERE", 0 },
|
||||
{ "psl", 'P', nullptr, 0, "generate PSL formulas", 0 },
|
||||
/**************************************************/
|
||||
{ nullptr, 0, nullptr, 0, "Generation:", 2 },
|
||||
{ "weak-fairness", OPT_WF, nullptr, 0,
|
||||
"append some weak-fairness conditions", 0 },
|
||||
{ "formulas", 'n', "INT", 0, "number of formulas to output (1)\n"\
|
||||
"use a negative value for unbounded generation", 0 },
|
||||
{ "seed", OPT_SEED, "INT", 0,
|
||||
"seed for the random number generator (0)", 0 },
|
||||
{ "tree-size", OPT_TREE_SIZE, "RANGE", 0,
|
||||
"tree size of the formulas generated, before mandatory "\
|
||||
"trivial simplifications (15)", 0 },
|
||||
{ "allow-dups", OPT_DUPS, nullptr, 0,
|
||||
"allow duplicate formulas to be output", 0 },
|
||||
DECLARE_OPT_R,
|
||||
RANGE_DOC,
|
||||
LEVEL_DOC(3),
|
||||
/**************************************************/
|
||||
{ nullptr, 0, nullptr, 0, "Adjusting probabilities:", 4 },
|
||||
{ "dump-priorities", OPT_DUMP_PRIORITIES, nullptr, 0,
|
||||
"show current priorities, do not generate any formula", 0 },
|
||||
{ "ltl-priorities", OPT_LTL_PRIORITIES, "STRING", 0,
|
||||
"set priorities for LTL formulas", 0 },
|
||||
{ "sere-priorities", OPT_SERE_PRIORITIES, "STRING", 0,
|
||||
"set priorities for SERE formulas", 0 },
|
||||
{ "boolean-priorities", OPT_BOOLEAN_PRIORITIES, "STRING", 0,
|
||||
"set priorities for Boolean formulas", 0 },
|
||||
{ nullptr, 0, nullptr, 0, "STRING should be a comma-separated list of "
|
||||
"assignments, assigning integer priorities to the tokens "
|
||||
"listed by --dump-priorities.", 0 },
|
||||
/**************************************************/
|
||||
{ nullptr, 0, nullptr, 0, "Output options:", -20 },
|
||||
{ nullptr, 0, nullptr, 0, "The FORMAT string passed to --format may use "
|
||||
"the following interpreted sequences:", -19 },
|
||||
{ "%f", 0, nullptr, OPTION_DOC | OPTION_NO_USAGE,
|
||||
"the formula (in the selected syntax)", 0 },
|
||||
{ "%L", 0, nullptr, OPTION_DOC | OPTION_NO_USAGE,
|
||||
"the (serial) number of the formula", 0 },
|
||||
{ "%%", 0, nullptr, OPTION_DOC | OPTION_NO_USAGE,
|
||||
"a single %", 0 },
|
||||
{ nullptr, 0, nullptr, 0, "Miscellaneous options:", -1 },
|
||||
{ nullptr, 0, nullptr, 0, nullptr, 0 }
|
||||
};
|
||||
|
||||
|
||||
const struct argp_child children[] =
|
||||
{
|
||||
{ &output_argp, 0, nullptr, -20 },
|
||||
{ &misc_argp, 0, nullptr, -1 },
|
||||
{ nullptr, 0, nullptr, 0 }
|
||||
};
|
||||
|
||||
spot::atomic_prop_set aprops;
|
||||
static int output = OUTPUTLTL;
|
||||
static char* opt_pL = nullptr;
|
||||
static char* opt_pS = nullptr;
|
||||
static char* opt_pB = nullptr;
|
||||
static bool opt_dump_priorities = false;
|
||||
static int opt_formulas = 1;
|
||||
static int opt_seed = 0;
|
||||
static range opt_tree_size = { 15, 15 };
|
||||
static bool opt_unique = true;
|
||||
static bool opt_wf = false;
|
||||
static bool ap_count_given = false;
|
||||
|
||||
static int
|
||||
parse_opt(int key, char* arg, struct argp_state* as)
|
||||
{
|
||||
// This switch is alphabetically-ordered.
|
||||
switch (key)
|
||||
{
|
||||
case 'B':
|
||||
output = OUTPUTBOOL;
|
||||
break;
|
||||
case 'L':
|
||||
output = OUTPUTLTL;
|
||||
break;
|
||||
case 'n':
|
||||
opt_formulas = to_int(arg);
|
||||
break;
|
||||
case 'P':
|
||||
output = OUTPUTPSL;
|
||||
break;
|
||||
case OPT_R:
|
||||
parse_r(arg);
|
||||
break;
|
||||
case 'S':
|
||||
output = OUTPUTSERE;
|
||||
break;
|
||||
case OPT_BOOLEAN_PRIORITIES:
|
||||
opt_pB = arg;
|
||||
break;
|
||||
case OPT_DUPS:
|
||||
opt_unique = false;
|
||||
break;
|
||||
case OPT_LTL_PRIORITIES:
|
||||
opt_pL = arg;
|
||||
break;
|
||||
case OPT_DUMP_PRIORITIES:
|
||||
opt_dump_priorities = true;
|
||||
break;
|
||||
// case OPT_PSL_PRIORITIES: break;
|
||||
case OPT_SERE_PRIORITIES:
|
||||
opt_pS = arg;
|
||||
break;
|
||||
case OPT_SEED:
|
||||
opt_seed = to_int(arg);
|
||||
break;
|
||||
case OPT_TREE_SIZE:
|
||||
opt_tree_size = parse_range(arg);
|
||||
if (opt_tree_size.min > opt_tree_size.max)
|
||||
std::swap(opt_tree_size.min, opt_tree_size.max);
|
||||
break;
|
||||
case OPT_WF:
|
||||
opt_wf = true;
|
||||
break;
|
||||
case ARGP_KEY_ARG:
|
||||
// If this is the unique non-option argument, it can
|
||||
// be a number of atomic propositions to build.
|
||||
//
|
||||
// argp reorganizes argv[] so that options always come before
|
||||
// non-options. So if as->argc == as->next we know this is the
|
||||
// last non-option argument, and if aprops.empty() we know this
|
||||
// is the also the first one.
|
||||
if (aprops.empty() && as->argc == as->next)
|
||||
{
|
||||
char* endptr;
|
||||
int res = strtol(arg, &endptr, 10);
|
||||
if (!*endptr && res >= 0) // arg is a number
|
||||
{
|
||||
ap_count_given = true;
|
||||
aprops = spot::create_atomic_prop_set(res);
|
||||
break;
|
||||
}
|
||||
}
|
||||
aprops.insert(spot::default_environment::instance().require(arg));
|
||||
break;
|
||||
default:
|
||||
return ARGP_ERR_UNKNOWN;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
main(int argc, char** argv)
|
||||
{
|
||||
setup(argv);
|
||||
|
||||
const argp ap = { options, parse_opt, "N|PROP...", argp_program_doc,
|
||||
children, nullptr, nullptr };
|
||||
|
||||
if (int err = argp_parse(&ap, argc, argv, ARGP_NO_HELP, nullptr, nullptr))
|
||||
exit(err);
|
||||
|
||||
// running 'randltl 0' is one way to generate formulas using no
|
||||
// atomic propositions so do not complain in that case.
|
||||
if (aprops.empty() && !ap_count_given)
|
||||
error(2, 0, "No atomic proposition supplied? Run '%s --help' for usage.",
|
||||
program_name);
|
||||
|
||||
spot::srand(opt_seed);
|
||||
try
|
||||
{
|
||||
spot::randltlgenerator rg
|
||||
(aprops,
|
||||
[&] (){
|
||||
spot::option_map opts;
|
||||
opts.set("output", output);
|
||||
opts.set("tree_size_min", opt_tree_size.min);
|
||||
opts.set("tree_size_max", opt_tree_size.max);
|
||||
opts.set("wf", opt_wf);
|
||||
opts.set("seed", opt_seed);
|
||||
opts.set("simplification_level", simplification_level);
|
||||
return opts;
|
||||
}(), opt_pL, opt_pS, opt_pB);
|
||||
|
||||
if (opt_dump_priorities)
|
||||
{
|
||||
switch (output)
|
||||
{
|
||||
case OUTPUTLTL:
|
||||
std::cout <<
|
||||
"Use --ltl-priorities to set the following LTL priorities:\n";
|
||||
rg.dump_ltl_priorities(std::cout);
|
||||
break;
|
||||
case OUTPUTBOOL:
|
||||
std::cout <<
|
||||
"Use --boolean-priorities to set the following Boolean "
|
||||
"formula priorities:\n";
|
||||
rg.dump_bool_priorities(std::cout);
|
||||
break;
|
||||
case OUTPUTPSL:
|
||||
std::cout <<
|
||||
"Use --ltl-priorities to set the following LTL priorities:\n";
|
||||
rg.dump_psl_priorities(std::cout);
|
||||
// Fall through.
|
||||
case OUTPUTSERE:
|
||||
std::cout <<
|
||||
"Use --sere-priorities to set the following SERE priorities:\n";
|
||||
rg.dump_sere_priorities(std::cout);
|
||||
std::cout <<
|
||||
"Use --boolean-priorities to set the following Boolean "
|
||||
"formula priorities:\n";
|
||||
rg.dump_sere_bool_priorities(std::cout);
|
||||
break;
|
||||
default:
|
||||
error(2, 0, "internal error: unknown type of output");
|
||||
}
|
||||
exit(0);
|
||||
}
|
||||
|
||||
while (opt_formulas < 0 || opt_formulas--)
|
||||
{
|
||||
static int count = 0;
|
||||
spot::formula f = rg.next();
|
||||
if (!f)
|
||||
{
|
||||
error(2, 0, "failed to generate a new unique formula after %d " \
|
||||
"trials", MAX_TRIALS);
|
||||
}
|
||||
else
|
||||
{
|
||||
output_formula_checked(f, nullptr, ++count);
|
||||
}
|
||||
};
|
||||
}
|
||||
catch (const std::runtime_error& e)
|
||||
{
|
||||
error(2, 0, "%s", e.what());
|
||||
}
|
||||
catch (const std::invalid_argument& e)
|
||||
{
|
||||
error(2, 0, "%s", e.what());
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
161
spot/bin/spot-x.cc
Normal file
161
spot/bin/spot-x.cc
Normal file
|
|
@ -0,0 +1,161 @@
|
|||
// -*- coding: utf-8 -*-
|
||||
// Copyright (C) 2013, 2014, 2015 Laboratoire de Recherche et
|
||||
// Développement de l'Epita (LRDE).
|
||||
//
|
||||
// This file is part of Spot, a model checking library.
|
||||
//
|
||||
// Spot is free software; you can redistribute it and/or modify it
|
||||
// under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation; either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// Spot is distributed in the hope that it will be useful, but WITHOUT
|
||||
// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
||||
// or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public
|
||||
// License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
#include "common_sys.hh"
|
||||
#include <string>
|
||||
#include <iostream>
|
||||
#include <cstdlib>
|
||||
#include <argp.h>
|
||||
#include "common_setup.hh"
|
||||
|
||||
const char argp_program_doc[] ="\
|
||||
Common fine-tuning options for binaries built with Spot.\n\
|
||||
\n\
|
||||
The argument of -x or --extra-options is a comma-separated list of KEY=INT \
|
||||
assignments that are passed to the post-processing routines (they may \
|
||||
be passed to other algorithms in the future). These options are \
|
||||
mostly used for benchmarking and debugging purpose. KEYR (without any \
|
||||
value) is a shorthand for KEY=1, while !KEY is a shorthand for KEY=0.";
|
||||
|
||||
#define DOC(NAME, TXT) NAME, 0, nullptr, OPTION_DOC | OPTION_NO_USAGE, TXT, 0
|
||||
|
||||
static const argp_option options[] =
|
||||
{
|
||||
{ nullptr, 0, nullptr, 0, "Translation options:", 0 },
|
||||
{ DOC("comp-susp", "Set to 1 to enable compositional suspension, \
|
||||
as described in our SPIN'13 paper (see Bibliography below). Set to 2, \
|
||||
to build only the skeleton TGBA without composing it. Set to 0 (the \
|
||||
default) to disable.") },
|
||||
{ DOC("early-susp", "When set to 1, start compositional suspension on \
|
||||
the transitions that enter accepting SCCs, and not only on the transitions \
|
||||
inside accepting SCCs. This option defaults to 0, and is only used when \
|
||||
comp-susp=1.") },
|
||||
{ DOC("skel-simul", "Default to 1. Set to 0 to disable simulation \
|
||||
on the skeleton automaton during compositional suspension. Only used when \
|
||||
comp-susp=1.") },
|
||||
{ DOC("skel-wdba", "Set to 0 to disable WDBA \
|
||||
minimization on the skeleton automaton during compositional suspension. \
|
||||
Set to 1 always WDBA-minimize the skeleton . Set to 2 to keep the WDBA \
|
||||
only if it is smaller than the original skeleton. This option is only \
|
||||
used when comp-susp=1 and default to 1 or 2 depending on whether --small \
|
||||
or --deterministic is specified.") },
|
||||
{ nullptr, 0, nullptr, 0, "Postprocessing options:", 0 },
|
||||
{ DOC("scc-filter", "Set to 1 (the default) to enable \
|
||||
SCC-pruning and acceptance simplification at the beginning of \
|
||||
post-processing. Transitions that are outside of accepting SCC are \
|
||||
removed from accepting sets, except those that enter into an accepting \
|
||||
SCC. Set to 2 to remove even these entering transition from the \
|
||||
accepting sets. Set to 0 to disable this SCC-pruning and acceptance \
|
||||
simpification pass.") },
|
||||
{ DOC("degen-reset", "If non-zero (the default), the \
|
||||
degeneralization algorithm will reset its level any time it exits \
|
||||
an SCC.") },
|
||||
{ DOC("degen-lcache", "If non-zero (the default is 1), whenever the \
|
||||
degeneralization algorithm enters an SCC on a state that has already \
|
||||
been associated to a level elsewhere, it should reuse that level. \
|
||||
Different values can be used to select which level to reuse: 1 always \
|
||||
uses the first level created, 2 uses the minimum level seen so far, and \
|
||||
3 uses the maximum level seen so far. The \"lcache\" stands for \
|
||||
\"level cache\".") },
|
||||
{ DOC("degen-order", "If non-zero, the degeneralization algorithm \
|
||||
will compute an independent degeneralization order for each SCC it \
|
||||
processes. This is currently disabled by default.") },
|
||||
{ DOC("degen-lskip", "If non-zero (the default), the degeneralization \
|
||||
algorithm will skip as much levels as possible for each transition. This \
|
||||
is enabled by default as it very often reduce the number of resulting \
|
||||
states. A consequence of skipping levels is that the degeneralized \
|
||||
automaton tends to have smaller cycles around the accepting states. \
|
||||
Disabling skipping will produce automata with large cycles, and often \
|
||||
with more states.") },
|
||||
{ DOC("degen-lowinit", "Whenever the degeneralization algorihm enters \
|
||||
a new SCC (or starts from the initial state), it starts on some level L that \
|
||||
is compatible with all outgoing transitions. If degen-lowinit is zero \
|
||||
(the default) and the corresponding state (in the generalized automaton) \
|
||||
has an accepting self-loop, then level L is replaced by the accepting \
|
||||
level, as it might favor finding accepting cycles earlier. If \
|
||||
degen-lowinit is non-zero, then level L is always used without looking \
|
||||
for the presence of an accepting self-loop.") },
|
||||
{ DOC("simul", "Set to 0 to disable simulation-based reductions. \
|
||||
Set to 1 to use only direct simulation. Set to 2 to use only reverse \
|
||||
simulation. Set to 3 to iterate both direct and reverse simulations. \
|
||||
The default is 3, except when option --low is specified, in which case \
|
||||
the default is 1.") },
|
||||
{ DOC("ba-simul", "Set to 0 to disable simulation-based reductions \
|
||||
on the Büchi automaton (i.e., after degeneralization has been performed). \
|
||||
Set to 1 to use only direct simulation. Set to 2 to use only reverse \
|
||||
simulation. Set to 3 to iterate both direct and reverse simulations. \
|
||||
The default is 3 in --high mode, and 0 otherwise.") },
|
||||
{ DOC("wdba-minimize", "Set to 0 to disable WDBA-minimization. \
|
||||
Enabled by default.") },
|
||||
{ DOC("tba-det", "Set to 1 to attempt a powerset determinization \
|
||||
if the TGBA is not already deterministic. Doing so will degeneralize \
|
||||
the automaton. This is disabled by default, unless sat-minimize is set.") },
|
||||
{ DOC("sat-minimize",
|
||||
"Set to 1 to enable SAT-based minimization of deterministic \
|
||||
TGBA: it starts with the number of states of the input, and iteratively \
|
||||
tries to find a deterministic TGBA with one less state. Set to 2 to perform \
|
||||
a binary search instead. Disabled (0) by default. The sat solver to use \
|
||||
can be set with the SPOT_SATSOLVER environment variable (see below). By \
|
||||
default the procedure looks for a TGBA with the same number of acceptance \
|
||||
set; this can be changed with the sat-acc option, or of course by using -B \
|
||||
to construct a Büchi automaton. Enabling SAT-based minimization will \
|
||||
also enable tba-det.") },
|
||||
{ DOC("sat-states",
|
||||
"When this is set to some positive integer, the SAT-based \
|
||||
minimization will attempt to construct a TGBA with the given number of \
|
||||
states. It may however return an automaton with less states if some of \
|
||||
these are unreachable or useless. Setting sat-states automatically \
|
||||
enables sat-minimize, but no iteration is performed. If no equivalent \
|
||||
automaton could be constructed with the given number of states, the original \
|
||||
automaton is returned.") },
|
||||
{ DOC("sat-acc",
|
||||
"When this is set to some positive integer, the SAT-based will \
|
||||
attempt to construct a TGBA with the given number of acceptance sets. \
|
||||
states. It may however return an automaton with less acceptance sets if \
|
||||
some of these are useless. Setting sat-acc automatically \
|
||||
sets sat-minimize to 1 if not set differently.") },
|
||||
{ DOC("state-based",
|
||||
"Set to 1 to instruct the SAT-minimization procedure to produce \
|
||||
a TGBA where all outgoing transition of a state have the same acceptance \
|
||||
sets. By default this is only enabled when option -B is used.") },
|
||||
{ nullptr, 0, nullptr, 0, nullptr, 0 }
|
||||
};
|
||||
|
||||
const struct argp_child children[] =
|
||||
{
|
||||
{ &misc_argp_hidden, 0, nullptr, -1 },
|
||||
{ nullptr, 0, nullptr, 0 }
|
||||
};
|
||||
|
||||
int
|
||||
main(int argc, char** argv)
|
||||
{
|
||||
setup(argv);
|
||||
|
||||
const argp ap = { options, nullptr, nullptr, argp_program_doc, children,
|
||||
nullptr, nullptr };
|
||||
|
||||
if (int err = argp_parse(&ap, argc, argv, ARGP_NO_HELP, nullptr, nullptr))
|
||||
exit(err);
|
||||
|
||||
std::cerr << "This binary serves no purpose other than generating"
|
||||
<< " the spot-x.7 manpage.\n";
|
||||
|
||||
return 1;
|
||||
}
|
||||
28
spot/graph/Makefile.am
Normal file
28
spot/graph/Makefile.am
Normal file
|
|
@ -0,0 +1,28 @@
|
|||
## -*- coding: utf-8 -*-
|
||||
## Copyright (C) 2014 Laboratoire de Recherche et Développement de
|
||||
## l'Epita (LRDE).
|
||||
##
|
||||
## This file is part of Spot, a model checking library.
|
||||
##
|
||||
## Spot is free software; you can redistribute it and/or modify it
|
||||
## under the terms of the GNU General Public License as published by
|
||||
## the Free Software Foundation; either version 3 of the License, or
|
||||
## (at your option) any later version.
|
||||
##
|
||||
## Spot is distributed in the hope that it will be useful, but WITHOUT
|
||||
## ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
||||
## or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public
|
||||
## License for more details.
|
||||
##
|
||||
## You should have received a copy of the GNU General Public License
|
||||
## along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
|
||||
AM_CPPFLAGS = -I$(top_builddir) -I$(top_srcdir)
|
||||
AM_CXXFLAGS = $(WARNING_CXXFLAGS)
|
||||
|
||||
graphdir = $(pkgincludedir)/graph
|
||||
|
||||
graph_HEADERS = \
|
||||
graph.hh \
|
||||
ngraph.hh
|
||||
973
spot/graph/graph.hh
Normal file
973
spot/graph/graph.hh
Normal file
|
|
@ -0,0 +1,973 @@
|
|||
// -*- coding: utf-8 -*-
|
||||
// Copyright (C) 2014, 2015 Laboratoire de Recherche et Développement
|
||||
// de l'Epita.
|
||||
//
|
||||
// This file is part of Spot, a model checking library.
|
||||
//
|
||||
// Spot is free software; you can redistribute it and/or modify it
|
||||
// under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation; either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// Spot is distributed in the hope that it will be useful, but WITHOUT
|
||||
// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
||||
// or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public
|
||||
// License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <spot/misc/common.hh>
|
||||
#include <vector>
|
||||
#include <type_traits>
|
||||
#include <tuple>
|
||||
#include <cassert>
|
||||
#include <iterator>
|
||||
#include <algorithm>
|
||||
#include <iostream>
|
||||
#include <type_traits>
|
||||
|
||||
namespace spot
|
||||
{
|
||||
template <typename State_Data, typename Edge_Data, bool Alternating = false>
|
||||
class SPOT_API digraph;
|
||||
|
||||
namespace internal
|
||||
{
|
||||
template <typename Of, typename ...Args>
|
||||
struct first_is_base_of
|
||||
{
|
||||
static const bool value = false;
|
||||
};
|
||||
|
||||
template <typename Of, typename Arg1, typename ...Args>
|
||||
struct first_is_base_of<Of, Arg1, Args...>
|
||||
{
|
||||
static const bool value =
|
||||
std::is_base_of<Of, typename std::decay<Arg1>::type>::value;
|
||||
};
|
||||
|
||||
|
||||
// The boxed_label class stores Data as an attribute called
|
||||
// "label" if boxed is true. It is an empty class if Data is
|
||||
// void, and it simply inherits from Data if boxed is false.
|
||||
//
|
||||
// The data() method offers an homogeneous access to the Data
|
||||
// instance.
|
||||
template <typename Data, bool boxed = !std::is_class<Data>::value>
|
||||
struct SPOT_API boxed_label
|
||||
{
|
||||
typedef Data data_t;
|
||||
Data label;
|
||||
|
||||
template <typename... Args,
|
||||
typename = typename std::enable_if<
|
||||
!first_is_base_of<boxed_label, Args...>::value>::type>
|
||||
boxed_label(Args&&... args)
|
||||
noexcept(std::is_nothrow_constructible<Data, Args...>::value)
|
||||
: label{std::forward<Args>(args)...}
|
||||
{
|
||||
}
|
||||
|
||||
// if Data is a POD type, G++ 4.8.2 wants default values for all
|
||||
// label fields unless we define this default constructor here.
|
||||
explicit boxed_label()
|
||||
noexcept(std::is_nothrow_constructible<Data>::value)
|
||||
{
|
||||
}
|
||||
|
||||
Data& data()
|
||||
{
|
||||
return label;
|
||||
}
|
||||
|
||||
const Data& data() const
|
||||
{
|
||||
return label;
|
||||
}
|
||||
|
||||
bool operator<(const boxed_label& other) const
|
||||
{
|
||||
return label < other.label;
|
||||
}
|
||||
};
|
||||
|
||||
template <>
|
||||
struct SPOT_API boxed_label<void, true>: public std::tuple<>
|
||||
{
|
||||
typedef std::tuple<> data_t;
|
||||
std::tuple<>& data()
|
||||
{
|
||||
return *this;
|
||||
}
|
||||
|
||||
const std::tuple<>& data() const
|
||||
{
|
||||
return *this;
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
template <typename Data>
|
||||
struct SPOT_API boxed_label<Data, false>: public Data
|
||||
{
|
||||
typedef Data data_t;
|
||||
|
||||
template <typename... Args,
|
||||
typename = typename std::enable_if<
|
||||
!first_is_base_of<boxed_label, Args...>::value>::type>
|
||||
boxed_label(Args&&... args)
|
||||
noexcept(std::is_nothrow_constructible<Data, Args...>::value)
|
||||
: Data{std::forward<Args>(args)...}
|
||||
{
|
||||
}
|
||||
|
||||
// if Data is a POD type, G++ 4.8.2 wants default values for all
|
||||
// label fields unless we define this default constructor here.
|
||||
explicit boxed_label()
|
||||
noexcept(std::is_nothrow_constructible<Data>::value)
|
||||
{
|
||||
}
|
||||
|
||||
Data& data()
|
||||
{
|
||||
return *this;
|
||||
}
|
||||
|
||||
const Data& data() const
|
||||
{
|
||||
return *this;
|
||||
}
|
||||
};
|
||||
|
||||
//////////////////////////////////////////////////
|
||||
// State storage for digraphs
|
||||
//////////////////////////////////////////////////
|
||||
|
||||
// We have two implementations, one with attached State_Data, and
|
||||
// one without.
|
||||
|
||||
template <typename Edge, typename State_Data>
|
||||
struct SPOT_API distate_storage final: public State_Data
|
||||
{
|
||||
Edge succ = 0; // First outgoing edge (used when iterating)
|
||||
Edge succ_tail = 0; // Last outgoing edge (used for
|
||||
// appending new edges)
|
||||
|
||||
template <typename... Args,
|
||||
typename = typename std::enable_if<
|
||||
!first_is_base_of<distate_storage, Args...>::value>::type>
|
||||
distate_storage(Args&&... args)
|
||||
noexcept(std::is_nothrow_constructible<State_Data, Args...>::value)
|
||||
: State_Data{std::forward<Args>(args)...}
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
//////////////////////////////////////////////////
|
||||
// Edge storage
|
||||
//////////////////////////////////////////////////
|
||||
|
||||
// Again two implementation: one with label, and one without.
|
||||
|
||||
template <typename StateIn,
|
||||
typename StateOut, typename Edge, typename Edge_Data>
|
||||
struct SPOT_API edge_storage final: public Edge_Data
|
||||
{
|
||||
typedef Edge edge;
|
||||
|
||||
StateOut dst; // destination
|
||||
Edge next_succ; // next outgoing edge with same
|
||||
// source, or 0
|
||||
StateIn src; // source
|
||||
|
||||
explicit edge_storage()
|
||||
noexcept(std::is_nothrow_constructible<Edge_Data>::value)
|
||||
: Edge_Data{}
|
||||
{
|
||||
}
|
||||
|
||||
template <typename... Args>
|
||||
edge_storage(StateOut dst, Edge next_succ,
|
||||
StateIn src, Args&&... args)
|
||||
noexcept(std::is_nothrow_constructible<Edge_Data, Args...>::value
|
||||
&& std::is_nothrow_constructible<StateOut, StateOut>::value
|
||||
&& std::is_nothrow_constructible<Edge, Edge>::value)
|
||||
: Edge_Data{std::forward<Args>(args)...},
|
||||
dst(dst), next_succ(next_succ), src(src)
|
||||
{
|
||||
}
|
||||
|
||||
bool operator<(const edge_storage& other) const
|
||||
{
|
||||
if (src < other.src)
|
||||
return true;
|
||||
if (src > other.src)
|
||||
return false;
|
||||
// This might be costly if the destination is a vector
|
||||
if (dst < other.dst)
|
||||
return true;
|
||||
if (dst > other.dst)
|
||||
return false;
|
||||
return this->data() < other.data();
|
||||
}
|
||||
|
||||
bool operator==(const edge_storage& other) const
|
||||
{
|
||||
return src == other.src &&
|
||||
dst == other.dst &&
|
||||
this->data() == other.data();
|
||||
}
|
||||
};
|
||||
|
||||
//////////////////////////////////////////////////
|
||||
// Edge iterator
|
||||
//////////////////////////////////////////////////
|
||||
|
||||
// This holds a graph and a edge number that is the start of
|
||||
// a list, and it iterates over all the edge_storage_t elements
|
||||
// of that list.
|
||||
|
||||
template <typename Graph>
|
||||
class SPOT_API edge_iterator:
|
||||
std::iterator<std::forward_iterator_tag,
|
||||
typename
|
||||
std::conditional<std::is_const<Graph>::value,
|
||||
const typename Graph::edge_storage_t,
|
||||
typename Graph::edge_storage_t>::type>
|
||||
{
|
||||
typedef
|
||||
std::iterator<std::forward_iterator_tag,
|
||||
typename
|
||||
std::conditional<std::is_const<Graph>::value,
|
||||
const typename Graph::edge_storage_t,
|
||||
typename Graph::edge_storage_t>::type>
|
||||
super;
|
||||
public:
|
||||
typedef typename Graph::edge edge;
|
||||
|
||||
edge_iterator() noexcept
|
||||
: g_(nullptr), t_(0)
|
||||
{
|
||||
}
|
||||
|
||||
edge_iterator(Graph* g, edge t) noexcept
|
||||
: g_(g), t_(t)
|
||||
{
|
||||
}
|
||||
|
||||
bool operator==(edge_iterator o) const
|
||||
{
|
||||
return t_ == o.t_;
|
||||
}
|
||||
|
||||
bool operator!=(edge_iterator o) const
|
||||
{
|
||||
return t_ != o.t_;
|
||||
}
|
||||
|
||||
typename super::reference
|
||||
operator*()
|
||||
{
|
||||
return g_->edge_storage(t_);
|
||||
}
|
||||
|
||||
typename super::pointer
|
||||
operator->()
|
||||
{
|
||||
return &g_->edge_storage(t_);
|
||||
}
|
||||
|
||||
edge_iterator operator++()
|
||||
{
|
||||
t_ = operator*().next_succ;
|
||||
return *this;
|
||||
}
|
||||
|
||||
edge_iterator operator++(int)
|
||||
{
|
||||
edge_iterator ti = *this;
|
||||
t_ = operator*().next_succ;
|
||||
return ti;
|
||||
}
|
||||
|
||||
operator bool() const
|
||||
{
|
||||
return t_;
|
||||
}
|
||||
|
||||
edge trans() const
|
||||
{
|
||||
return t_;
|
||||
}
|
||||
|
||||
protected:
|
||||
Graph* g_;
|
||||
edge t_;
|
||||
};
|
||||
|
||||
template <typename Graph>
|
||||
class SPOT_API killer_edge_iterator: public edge_iterator<Graph>
|
||||
{
|
||||
typedef edge_iterator<Graph> super;
|
||||
public:
|
||||
typedef typename Graph::state_storage_t state_storage_t;
|
||||
typedef typename Graph::edge edge;
|
||||
|
||||
killer_edge_iterator(Graph* g, edge t, state_storage_t& src) noexcept
|
||||
: super(g, t), src_(src), prev_(0)
|
||||
{
|
||||
}
|
||||
|
||||
killer_edge_iterator operator++()
|
||||
{
|
||||
prev_ = this->t_;
|
||||
this->t_ = this->operator*().next_succ;
|
||||
return *this;
|
||||
}
|
||||
|
||||
killer_edge_iterator operator++(int)
|
||||
{
|
||||
killer_edge_iterator ti = *this;
|
||||
++*this;
|
||||
return ti;
|
||||
}
|
||||
|
||||
// Erase the current edge and advance the iterator.
|
||||
void erase()
|
||||
{
|
||||
edge next = this->operator*().next_succ;
|
||||
|
||||
// Update source state and previous edges
|
||||
if (prev_)
|
||||
{
|
||||
this->g_->edge_storage(prev_).next_succ = next;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (src_.succ == this->t_)
|
||||
src_.succ = next;
|
||||
}
|
||||
if (src_.succ_tail == this->t_)
|
||||
{
|
||||
src_.succ_tail = prev_;
|
||||
assert(next == 0);
|
||||
}
|
||||
|
||||
// Erased edges have themselves as next_succ.
|
||||
this->operator*().next_succ = this->t_;
|
||||
|
||||
// Advance iterator to next edge.
|
||||
this->t_ = next;
|
||||
|
||||
++this->g_->killed_edge_;
|
||||
}
|
||||
|
||||
protected:
|
||||
state_storage_t& src_;
|
||||
edge prev_;
|
||||
};
|
||||
|
||||
|
||||
//////////////////////////////////////////////////
|
||||
// State OUT
|
||||
//////////////////////////////////////////////////
|
||||
|
||||
// Fake container listing the outgoing edges of a state.
|
||||
|
||||
template <typename Graph>
|
||||
class SPOT_API state_out
|
||||
{
|
||||
public:
|
||||
typedef typename Graph::edge edge;
|
||||
state_out(Graph* g, edge t) noexcept
|
||||
: g_(g), t_(t)
|
||||
{
|
||||
}
|
||||
|
||||
edge_iterator<Graph> begin()
|
||||
{
|
||||
return {g_, t_};
|
||||
}
|
||||
|
||||
edge_iterator<Graph> end()
|
||||
{
|
||||
return {};
|
||||
}
|
||||
|
||||
void recycle(edge t)
|
||||
{
|
||||
t_ = t;
|
||||
}
|
||||
|
||||
protected:
|
||||
Graph* g_;
|
||||
edge t_;
|
||||
};
|
||||
|
||||
//////////////////////////////////////////////////
|
||||
// all_trans
|
||||
//////////////////////////////////////////////////
|
||||
|
||||
template <typename Graph>
|
||||
class SPOT_API all_edge_iterator:
|
||||
std::iterator<std::forward_iterator_tag,
|
||||
typename
|
||||
std::conditional<std::is_const<Graph>::value,
|
||||
const typename Graph::edge_storage_t,
|
||||
typename Graph::edge_storage_t>::type>
|
||||
{
|
||||
typedef
|
||||
std::iterator<std::forward_iterator_tag,
|
||||
typename
|
||||
std::conditional<std::is_const<Graph>::value,
|
||||
const typename Graph::edge_storage_t,
|
||||
typename Graph::edge_storage_t>::type>
|
||||
super;
|
||||
|
||||
typedef typename std::conditional<std::is_const<Graph>::value,
|
||||
const typename Graph::edge_vector_t,
|
||||
typename Graph::edge_vector_t>::type
|
||||
tv_t;
|
||||
|
||||
unsigned t_;
|
||||
tv_t& tv_;
|
||||
|
||||
void skip_()
|
||||
{
|
||||
unsigned s = tv_.size();
|
||||
do
|
||||
++t_;
|
||||
while (t_ < s && tv_[t_].next_succ == t_);
|
||||
}
|
||||
|
||||
public:
|
||||
all_edge_iterator(unsigned pos, tv_t& tv) noexcept
|
||||
: t_(pos), tv_(tv)
|
||||
{
|
||||
skip_();
|
||||
}
|
||||
|
||||
all_edge_iterator(tv_t& tv) noexcept
|
||||
: t_(tv.size()), tv_(tv)
|
||||
{
|
||||
}
|
||||
|
||||
all_edge_iterator& operator++()
|
||||
{
|
||||
skip_();
|
||||
return *this;
|
||||
}
|
||||
|
||||
all_edge_iterator operator++(int)
|
||||
{
|
||||
all_edge_iterator old = *this;
|
||||
++*this;
|
||||
return old;
|
||||
}
|
||||
|
||||
bool operator==(all_edge_iterator o) const
|
||||
{
|
||||
return t_ == o.t_;
|
||||
}
|
||||
|
||||
bool operator!=(all_edge_iterator o) const
|
||||
{
|
||||
return t_ != o.t_;
|
||||
}
|
||||
|
||||
typename super::reference
|
||||
operator*()
|
||||
{
|
||||
return tv_[t_];
|
||||
}
|
||||
|
||||
typename super::pointer
|
||||
operator->()
|
||||
{
|
||||
return &tv_[t_];
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
template <typename Graph>
|
||||
class SPOT_API all_trans
|
||||
{
|
||||
typedef typename std::conditional<std::is_const<Graph>::value,
|
||||
const typename Graph::edge_vector_t,
|
||||
typename Graph::edge_vector_t>::type
|
||||
tv_t;
|
||||
typedef all_edge_iterator<Graph> iter_t;
|
||||
tv_t& tv_;
|
||||
public:
|
||||
|
||||
all_trans(tv_t& tv) noexcept
|
||||
: tv_(tv)
|
||||
{
|
||||
}
|
||||
|
||||
iter_t begin()
|
||||
{
|
||||
return {0, tv_};
|
||||
}
|
||||
|
||||
iter_t end()
|
||||
{
|
||||
return {tv_};
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
|
||||
// The actual graph implementation
|
||||
|
||||
template <typename State_Data, typename Edge_Data, bool Alternating>
|
||||
class digraph
|
||||
{
|
||||
friend class internal::edge_iterator<digraph>;
|
||||
friend class internal::edge_iterator<const digraph>;
|
||||
friend class internal::killer_edge_iterator<digraph>;
|
||||
|
||||
public:
|
||||
typedef internal::edge_iterator<digraph> iterator;
|
||||
typedef internal::edge_iterator<const digraph> const_iterator;
|
||||
|
||||
static constexpr bool alternating()
|
||||
{
|
||||
return Alternating;
|
||||
}
|
||||
|
||||
// Extra data to store on each state or edge.
|
||||
typedef State_Data state_data_t;
|
||||
typedef Edge_Data edge_data_t;
|
||||
|
||||
// State and edges are identified by their indices in some
|
||||
// vector.
|
||||
typedef unsigned state;
|
||||
typedef unsigned edge;
|
||||
|
||||
// The type of an output state (when seen from a edge)
|
||||
// depends on the kind of graph we build
|
||||
typedef typename std::conditional<Alternating,
|
||||
std::vector<state>,
|
||||
state>::type out_state;
|
||||
|
||||
typedef internal::distate_storage<edge,
|
||||
internal::boxed_label<State_Data>>
|
||||
state_storage_t;
|
||||
typedef internal::edge_storage<state, out_state, edge,
|
||||
internal::boxed_label<Edge_Data>>
|
||||
edge_storage_t;
|
||||
typedef std::vector<state_storage_t> state_vector;
|
||||
typedef std::vector<edge_storage_t> edge_vector_t;
|
||||
protected:
|
||||
state_vector states_;
|
||||
edge_vector_t edges_;
|
||||
// Number of erased edges.
|
||||
unsigned killed_edge_;
|
||||
public:
|
||||
/// \brief construct an empty graph
|
||||
///
|
||||
/// Construct an empty graph, and reserve space for \a max_states
|
||||
/// states and \a max_trans edges. These are not hard
|
||||
/// limits, but just hints to pre-allocate a data structure that
|
||||
/// may hold that much items.
|
||||
digraph(unsigned max_states = 10, unsigned max_trans = 0)
|
||||
: killed_edge_(0)
|
||||
{
|
||||
states_.reserve(max_states);
|
||||
if (max_trans == 0)
|
||||
max_trans = max_states * 2;
|
||||
edges_.reserve(max_trans + 1);
|
||||
// Edge number 0 is not used, because we use this index
|
||||
// to mark the absence of a edge.
|
||||
edges_.resize(1);
|
||||
// This causes edge 0 to be considered as dead.
|
||||
edges_[0].next_succ = 0;
|
||||
}
|
||||
|
||||
unsigned num_states() const
|
||||
{
|
||||
return states_.size();
|
||||
}
|
||||
|
||||
unsigned num_edges() const
|
||||
{
|
||||
return edges_.size() - killed_edge_ - 1;
|
||||
}
|
||||
|
||||
bool valid_trans(edge t) const
|
||||
{
|
||||
// Erased edges have their next_succ pointing to
|
||||
// themselves.
|
||||
return (t < edges_.size() &&
|
||||
edges_[t].next_succ != t);
|
||||
}
|
||||
|
||||
template <typename... Args>
|
||||
state new_state(Args&&... args)
|
||||
{
|
||||
state s = states_.size();
|
||||
states_.emplace_back(std::forward<Args>(args)...);
|
||||
return s;
|
||||
}
|
||||
|
||||
template <typename... Args>
|
||||
state new_states(unsigned n, Args&&... args)
|
||||
{
|
||||
state s = states_.size();
|
||||
states_.reserve(s + n);
|
||||
while (n--)
|
||||
states_.emplace_back(std::forward<Args>(args)...);
|
||||
return s;
|
||||
}
|
||||
|
||||
state_storage_t&
|
||||
state_storage(state s)
|
||||
{
|
||||
assert(s < states_.size());
|
||||
return states_[s];
|
||||
}
|
||||
|
||||
const state_storage_t&
|
||||
state_storage(state s) const
|
||||
{
|
||||
assert(s < states_.size());
|
||||
return states_[s];
|
||||
}
|
||||
|
||||
// Do not use State_Data& as return type, because State_Data might
|
||||
// be void.
|
||||
typename state_storage_t::data_t&
|
||||
state_data(state s)
|
||||
{
|
||||
assert(s < states_.size());
|
||||
return states_[s].data();
|
||||
}
|
||||
|
||||
// May not be called on states with no data.
|
||||
const typename state_storage_t::data_t&
|
||||
state_data(state s) const
|
||||
{
|
||||
assert(s < states_.size());
|
||||
return states_[s].data();
|
||||
}
|
||||
|
||||
edge_storage_t&
|
||||
edge_storage(edge s)
|
||||
{
|
||||
assert(s < edges_.size());
|
||||
return edges_[s];
|
||||
}
|
||||
|
||||
const edge_storage_t&
|
||||
edge_storage(edge s) const
|
||||
{
|
||||
assert(s < edges_.size());
|
||||
return edges_[s];
|
||||
}
|
||||
|
||||
typename edge_storage_t::data_t&
|
||||
edge_data(edge s)
|
||||
{
|
||||
assert(s < edges_.size());
|
||||
return edges_[s].data();
|
||||
}
|
||||
|
||||
const typename edge_storage_t::data_t&
|
||||
edge_data(edge s) const
|
||||
{
|
||||
assert(s < edges_.size());
|
||||
return edges_[s].data();
|
||||
}
|
||||
|
||||
template <typename... Args>
|
||||
edge
|
||||
new_edge(state src, out_state dst, Args&&... args)
|
||||
{
|
||||
assert(src < states_.size());
|
||||
|
||||
edge t = edges_.size();
|
||||
edges_.emplace_back(dst, 0, src, std::forward<Args>(args)...);
|
||||
|
||||
edge st = states_[src].succ_tail;
|
||||
assert(st < t || !st);
|
||||
if (!st)
|
||||
states_[src].succ = t;
|
||||
else
|
||||
edges_[st].next_succ = t;
|
||||
states_[src].succ_tail = t;
|
||||
return t;
|
||||
}
|
||||
|
||||
state index_of_state(const state_storage_t& ss) const
|
||||
{
|
||||
assert(!states_.empty());
|
||||
return &ss - &states_.front();
|
||||
}
|
||||
|
||||
edge index_of_edge(const edge_storage_t& tt) const
|
||||
{
|
||||
assert(!edges_.empty());
|
||||
return &tt - &edges_.front();
|
||||
}
|
||||
|
||||
internal::state_out<digraph>
|
||||
out(state src)
|
||||
{
|
||||
return {this, states_[src].succ};
|
||||
}
|
||||
|
||||
internal::state_out<digraph>
|
||||
out(state_storage_t& src)
|
||||
{
|
||||
return out(index_of_state(src));
|
||||
}
|
||||
|
||||
internal::state_out<const digraph>
|
||||
out(state src) const
|
||||
{
|
||||
return {this, states_[src].succ};
|
||||
}
|
||||
|
||||
internal::state_out<const digraph>
|
||||
out(state_storage_t& src) const
|
||||
{
|
||||
return out(index_of_state(src));
|
||||
}
|
||||
|
||||
internal::killer_edge_iterator<digraph>
|
||||
out_iteraser(state_storage_t& src)
|
||||
{
|
||||
return {this, src.succ, src};
|
||||
}
|
||||
|
||||
internal::killer_edge_iterator<digraph>
|
||||
out_iteraser(state src)
|
||||
{
|
||||
return out_iteraser(state_storage(src));
|
||||
}
|
||||
|
||||
const state_vector& states() const
|
||||
{
|
||||
return states_;
|
||||
}
|
||||
|
||||
state_vector& states()
|
||||
{
|
||||
return states_;
|
||||
}
|
||||
|
||||
internal::all_trans<const digraph> edges() const
|
||||
{
|
||||
return edges_;
|
||||
}
|
||||
|
||||
internal::all_trans<digraph> edges()
|
||||
{
|
||||
return edges_;
|
||||
}
|
||||
|
||||
// When using this method, beware that the first entry (edge
|
||||
// #0) is not a real edge, and that any edge with
|
||||
// next_succ pointing to itself is an erased edge.
|
||||
//
|
||||
// You should probably use edges() instead.
|
||||
const edge_vector_t& edge_vector() const
|
||||
{
|
||||
return edges_;
|
||||
}
|
||||
|
||||
edge_vector_t& edge_vector()
|
||||
{
|
||||
return edges_;
|
||||
}
|
||||
|
||||
bool is_dead_edge(unsigned t) const
|
||||
{
|
||||
return edges_[t].next_succ == t;
|
||||
}
|
||||
|
||||
bool is_dead_edge(const edge_storage_t& t) const
|
||||
{
|
||||
return t.next_succ == index_of_edge(t);
|
||||
}
|
||||
|
||||
|
||||
// To help debugging
|
||||
void dump_storage(std::ostream& o) const
|
||||
{
|
||||
unsigned tend = edges_.size();
|
||||
for (unsigned t = 1; t < tend; ++t)
|
||||
{
|
||||
o << 't' << t << ": (s"
|
||||
<< edges_[t].src << ", s"
|
||||
<< edges_[t].dst << ") t"
|
||||
<< edges_[t].next_succ << '\n';
|
||||
}
|
||||
unsigned send = states_.size();
|
||||
for (unsigned s = 0; s < send; ++s)
|
||||
{
|
||||
o << 's' << s << ": t"
|
||||
<< states_[s].succ << " t"
|
||||
<< states_[s].succ_tail << '\n';
|
||||
}
|
||||
}
|
||||
|
||||
// Remove all dead edges. The edges_ vector is left
|
||||
// in a state that is incorrect and should eventually be fixed by
|
||||
// a call to chain_edges_() before any iteration on the
|
||||
// successor of a state is performed.
|
||||
void remove_dead_edges_()
|
||||
{
|
||||
if (killed_edge_ == 0)
|
||||
return;
|
||||
auto i = std::remove_if(edges_.begin() + 1, edges_.end(),
|
||||
[this](const edge_storage_t& t) {
|
||||
return this->is_dead_edge(t);
|
||||
});
|
||||
edges_.erase(i, edges_.end());
|
||||
killed_edge_ = 0;
|
||||
}
|
||||
|
||||
// This will invalidate all iterators, and also destroy edge
|
||||
// chains. Call chain_edges_() immediately afterwards
|
||||
// unless you know what you are doing.
|
||||
template<class Predicate = std::less<edge_storage_t>>
|
||||
void sort_edges_(Predicate p = Predicate())
|
||||
{
|
||||
//std::cerr << "\nbefore\n";
|
||||
//dump_storage(std::cerr);
|
||||
std::stable_sort(edges_.begin() + 1, edges_.end(), p);
|
||||
}
|
||||
|
||||
// Should be called only when it is known that all edges
|
||||
// with the same destination are consecutive in the vector.
|
||||
void chain_edges_()
|
||||
{
|
||||
state last_src = -1U;
|
||||
edge tend = edges_.size();
|
||||
for (edge t = 1; t < tend; ++t)
|
||||
{
|
||||
state src = edges_[t].src;
|
||||
if (src != last_src)
|
||||
{
|
||||
states_[src].succ = t;
|
||||
if (last_src != -1U)
|
||||
{
|
||||
states_[last_src].succ_tail = t - 1;
|
||||
edges_[t - 1].next_succ = 0;
|
||||
}
|
||||
while (++last_src != src)
|
||||
{
|
||||
states_[last_src].succ = 0;
|
||||
states_[last_src].succ_tail = 0;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
edges_[t - 1].next_succ = t;
|
||||
}
|
||||
}
|
||||
if (last_src != -1U)
|
||||
{
|
||||
states_[last_src].succ_tail = tend - 1;
|
||||
edges_[tend - 1].next_succ = 0;
|
||||
}
|
||||
unsigned send = states_.size();
|
||||
while (++last_src != send)
|
||||
{
|
||||
states_[last_src].succ = 0;
|
||||
states_[last_src].succ_tail = 0;
|
||||
}
|
||||
//std::cerr << "\nafter\n";
|
||||
//dump_storage(std::cerr);
|
||||
}
|
||||
|
||||
// Rename all the states in the edge vector. The
|
||||
// edges_ vector is left in a state that is incorrect and
|
||||
// should eventually be fixed by a call to chain_edges_()
|
||||
// before any iteration on the successor of a state is performed.
|
||||
void rename_states_(const std::vector<unsigned>& newst)
|
||||
{
|
||||
assert(newst.size() == states_.size());
|
||||
unsigned tend = edges_.size();
|
||||
for (unsigned t = 1; t < tend; t++)
|
||||
{
|
||||
edges_[t].dst = newst[edges_[t].dst];
|
||||
edges_[t].src = newst[edges_[t].src];
|
||||
}
|
||||
}
|
||||
|
||||
void defrag_states(std::vector<unsigned>&& newst, unsigned used_states)
|
||||
{
|
||||
assert(newst.size() == states_.size());
|
||||
assert(used_states > 0);
|
||||
|
||||
//std::cerr << "\nbefore defrag\n";
|
||||
//dump_storage(std::cerr);
|
||||
|
||||
// Shift all states in states_, as indicated by newst.
|
||||
unsigned send = states_.size();
|
||||
for (state s = 0; s < send; ++s)
|
||||
{
|
||||
state dst = newst[s];
|
||||
if (dst == s)
|
||||
continue;
|
||||
if (dst == -1U)
|
||||
{
|
||||
// This is an erased state. Mark all its edges as
|
||||
// dead (i.e., t.next_succ should point to t for each of
|
||||
// them).
|
||||
auto t = states_[s].succ;
|
||||
while (t)
|
||||
std::swap(t, edges_[t].next_succ);
|
||||
continue;
|
||||
}
|
||||
states_[dst] = std::move(states_[s]);
|
||||
}
|
||||
states_.resize(used_states);
|
||||
|
||||
// Shift all edges in edges_. The algorithm is
|
||||
// similar to remove_if, but it also keeps the correspondence
|
||||
// between the old and new index as newidx[old] = new.
|
||||
unsigned tend = edges_.size();
|
||||
std::vector<edge> newidx(tend);
|
||||
unsigned dest = 1;
|
||||
for (edge t = 1; t < tend; ++t)
|
||||
{
|
||||
if (is_dead_edge(t))
|
||||
continue;
|
||||
if (t != dest)
|
||||
edges_[dest] = std::move(edges_[t]);
|
||||
newidx[t] = dest;
|
||||
++dest;
|
||||
}
|
||||
edges_.resize(dest);
|
||||
killed_edge_ = 0;
|
||||
|
||||
// Adjust next_succ and dst pointers in all edges.
|
||||
for (edge t = 1; t < dest; ++t)
|
||||
{
|
||||
auto& tr = edges_[t];
|
||||
tr.next_succ = newidx[tr.next_succ];
|
||||
tr.dst = newst[tr.dst];
|
||||
tr.src = newst[tr.src];
|
||||
assert(tr.dst != -1U);
|
||||
}
|
||||
|
||||
// Adjust succ and succ_tails pointers in all states.
|
||||
for (auto& s: states_)
|
||||
{
|
||||
s.succ = newidx[s.succ];
|
||||
s.succ_tail = newidx[s.succ_tail];
|
||||
}
|
||||
|
||||
//std::cerr << "\nafter defrag\n";
|
||||
//dump_storage(std::cerr);
|
||||
}
|
||||
};
|
||||
}
|
||||
163
spot/graph/ngraph.hh
Normal file
163
spot/graph/ngraph.hh
Normal file
|
|
@ -0,0 +1,163 @@
|
|||
// -*- coding: utf-8 -*-
|
||||
// Copyright (C) 2014, 2015 Laboratoire de Recherche et Développement
|
||||
// de l'Epita.
|
||||
//
|
||||
// This file is part of Spot, a model checking library.
|
||||
//
|
||||
// Spot is free software; you can redistribute it and/or modify it
|
||||
// under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation; either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// Spot is distributed in the hope that it will be useful, but WITHOUT
|
||||
// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
||||
// or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public
|
||||
// License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <unordered_map>
|
||||
#include <vector>
|
||||
#include <spot/graph/graph.hh>
|
||||
|
||||
namespace spot
|
||||
{
|
||||
template <typename Graph,
|
||||
typename State_Name,
|
||||
typename Name_Hash = std::hash<State_Name>,
|
||||
typename Name_Equal = std::equal_to<State_Name>>
|
||||
class SPOT_API named_graph
|
||||
{
|
||||
protected:
|
||||
Graph& g_;
|
||||
public:
|
||||
|
||||
typedef typename Graph::state state;
|
||||
typedef typename Graph::edge edge;
|
||||
typedef State_Name name;
|
||||
|
||||
typedef std::unordered_map<name, state,
|
||||
Name_Hash, Name_Equal> name_to_state_t;
|
||||
name_to_state_t name_to_state;
|
||||
typedef std::vector<name> state_to_name_t;
|
||||
state_to_name_t state_to_name;
|
||||
|
||||
named_graph(Graph& g)
|
||||
: g_(g)
|
||||
{
|
||||
}
|
||||
|
||||
Graph& graph()
|
||||
{
|
||||
return g_;
|
||||
}
|
||||
|
||||
Graph& graph() const
|
||||
{
|
||||
return g_;
|
||||
}
|
||||
|
||||
template <typename... Args>
|
||||
state new_state(name n, Args&&... args)
|
||||
{
|
||||
auto p = name_to_state.emplace(n, 0U);
|
||||
if (p.second)
|
||||
{
|
||||
unsigned s = g_.new_state(std::forward<Args>(args)...);
|
||||
p.first->second = s;
|
||||
if (state_to_name.size() < s + 1)
|
||||
state_to_name.resize(s + 1);
|
||||
state_to_name[s] = n;
|
||||
return s;
|
||||
}
|
||||
return p.first->second;
|
||||
}
|
||||
|
||||
/// \brief Give an alternate name to a state.
|
||||
/// \return true iff the newname state was already existing
|
||||
/// (in this case the existing newname state will be merged
|
||||
/// with state s: the newname will be unreachable and without
|
||||
/// successors.)
|
||||
bool alias_state(state s, name newname)
|
||||
{
|
||||
auto p = name_to_state.emplace(newname, s);
|
||||
if (!p.second)
|
||||
{
|
||||
// The state already exists. Change its number.
|
||||
auto old = p.first->second;
|
||||
p.first->second = s;
|
||||
// Add the successor of OLD to those of S.
|
||||
auto& trans = g_.edge_vector();
|
||||
auto& states = g_.states();
|
||||
trans[states[s].succ_tail].next_succ = states[old].succ;
|
||||
states[s].succ_tail = states[old].succ_tail;
|
||||
states[old].succ = 0;
|
||||
states[old].succ_tail = 0;
|
||||
// Remove all references to old in edges:
|
||||
unsigned tend = trans.size();
|
||||
for (unsigned t = 1; t < tend; ++t)
|
||||
{
|
||||
if (trans[t].src == old)
|
||||
trans[t].src = s;
|
||||
if (trans[t].dst == old)
|
||||
trans[t].dst = s;
|
||||
}
|
||||
}
|
||||
return !p.second;
|
||||
}
|
||||
|
||||
state get_state(name n) const
|
||||
{
|
||||
return name_to_state.at(n);
|
||||
}
|
||||
|
||||
name get_name(state s) const
|
||||
{
|
||||
return state_to_name.at(s);
|
||||
}
|
||||
|
||||
bool has_state(name n) const
|
||||
{
|
||||
return name_to_state.find(n) != name_to_state.end();
|
||||
}
|
||||
|
||||
const state_to_name_t& names() const
|
||||
{
|
||||
return state_to_name;
|
||||
}
|
||||
|
||||
template <typename... Args>
|
||||
edge
|
||||
new_edge(name src, name dst, Args&&... args)
|
||||
{
|
||||
return g_.new_edge(get_state(src), get_state(dst),
|
||||
std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
template <typename... Args>
|
||||
edge
|
||||
new_edge(name src, const std::vector<State_Name>& dst, Args&&... args)
|
||||
{
|
||||
std::vector<State_Name> d;
|
||||
d.reserve(dst.size());
|
||||
for (auto n: dst)
|
||||
d.push_back(get_state(n));
|
||||
return g_.new_edge(get_state(src), d, std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
template <typename... Args>
|
||||
edge
|
||||
new_edge(name src,
|
||||
const std::initializer_list<State_Name>& dst, Args&&... args)
|
||||
{
|
||||
std::vector<state> d;
|
||||
d.reserve(dst.size());
|
||||
for (auto n: dst)
|
||||
d.push_back(get_state(n));
|
||||
return g_.new_edge(get_state(src), d, std::forward<Args>(args)...);
|
||||
}
|
||||
};
|
||||
}
|
||||
34
spot/kripke/Makefile.am
Normal file
34
spot/kripke/Makefile.am
Normal file
|
|
@ -0,0 +1,34 @@
|
|||
## -*- coding: utf-8 -*-
|
||||
## Copyright (C) 2009, 2011, 2013, 2014, 2015 Laboratoire de Recherche
|
||||
## et Developpement de l'Epita (LRDE).
|
||||
##
|
||||
## This file is part of Spot, a model checking library.
|
||||
##
|
||||
## Spot is free software; you can redistribute it and/or modify it
|
||||
## under the terms of the GNU General Public License as published by
|
||||
## the Free Software Foundation; either version 3 of the License, or
|
||||
## (at your option) any later version.
|
||||
##
|
||||
## Spot is distributed in the hope that it will be useful, but WITHOUT
|
||||
## ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
||||
## or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public
|
||||
## License for more details.
|
||||
##
|
||||
## You should have received a copy of the GNU General Public License
|
||||
## along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
AM_CPPFLAGS = -I$(top_builddir) -I$(top_srcdir) $(BUDDY_CPPFLAGS)
|
||||
AM_CXXFLAGS = $(WARNING_CXXFLAGS)
|
||||
|
||||
kripkedir = $(pkgincludedir)/kripke
|
||||
|
||||
kripke_HEADERS = \
|
||||
fairkripke.hh \
|
||||
fwd.hh \
|
||||
kripke.hh \
|
||||
kripkegraph.hh
|
||||
|
||||
noinst_LTLIBRARIES = libkripke.la
|
||||
libkripke_la_SOURCES = \
|
||||
fairkripke.cc \
|
||||
kripke.cc
|
||||
57
spot/kripke/fairkripke.cc
Normal file
57
spot/kripke/fairkripke.cc
Normal file
|
|
@ -0,0 +1,57 @@
|
|||
// -*- coding: utf-8 -*-
|
||||
// Copyright (C) 2009, 2010, 2014 Laboratoire de Recherche et
|
||||
// Developpement de l'Epita
|
||||
//
|
||||
// This file is part of Spot, a model checking library.
|
||||
//
|
||||
// Spot is free software; you can redistribute it and/or modify it
|
||||
// under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation; either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// Spot is distributed in the hope that it will be useful, but WITHOUT
|
||||
// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
||||
// or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public
|
||||
// License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
#include <spot/kripke/fairkripke.hh>
|
||||
|
||||
namespace spot
|
||||
{
|
||||
|
||||
fair_kripke_succ_iterator::fair_kripke_succ_iterator
|
||||
(const bdd& cond, acc_cond::mark_t acc_cond)
|
||||
: cond_(cond), acc_cond_(acc_cond)
|
||||
{
|
||||
}
|
||||
|
||||
fair_kripke_succ_iterator::~fair_kripke_succ_iterator()
|
||||
{
|
||||
}
|
||||
|
||||
bdd
|
||||
fair_kripke_succ_iterator::cond() const
|
||||
{
|
||||
// Do not assert(!done()) here. It is OK to call
|
||||
// this function on a state without successor.
|
||||
return cond_;
|
||||
}
|
||||
|
||||
acc_cond::mark_t
|
||||
fair_kripke_succ_iterator::acc() const
|
||||
{
|
||||
// Do not assert(!done()) here. It is OK to call
|
||||
// this function on a state without successor.
|
||||
return acc_cond_;
|
||||
}
|
||||
|
||||
bdd
|
||||
fair_kripke::compute_support_conditions(const state* s) const
|
||||
{
|
||||
return state_condition(s);
|
||||
}
|
||||
|
||||
}
|
||||
108
spot/kripke/fairkripke.hh
Normal file
108
spot/kripke/fairkripke.hh
Normal file
|
|
@ -0,0 +1,108 @@
|
|||
// -*- coding: utf-8 -*-
|
||||
// Copyright (C) 2009, 2010, 2013, 2014, 2015 Laboratoire de Recherche et
|
||||
// Developpement de l'Epita
|
||||
//
|
||||
// This file is part of Spot, a model checking library.
|
||||
//
|
||||
// Spot is free software; you can redistribute it and/or modify it
|
||||
// under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation; either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// Spot is distributed in the hope that it will be useful, but WITHOUT
|
||||
// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
||||
// or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public
|
||||
// License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <spot/twa/twa.hh>
|
||||
#include <spot/kripke/fwd.hh>
|
||||
|
||||
/// \addtogroup kripke Kripke Structures
|
||||
/// \ingroup twa
|
||||
|
||||
namespace spot
|
||||
{
|
||||
/// \ingroup kripke
|
||||
/// \brief Iterator code for a Fair Kripke structure.
|
||||
///
|
||||
/// This iterator can be used to simplify the writing
|
||||
/// of an iterator on a Fair Kripke structure (or lookalike).
|
||||
///
|
||||
/// If you inherit from this iterator, you should only
|
||||
/// redefine
|
||||
///
|
||||
/// - fair_kripke_succ_iterator::first()
|
||||
/// - fair_kripke_succ_iterator::next()
|
||||
/// - fair_kripke_succ_iterator::done()
|
||||
/// - fair_kripke_succ_iterator::dst()
|
||||
///
|
||||
/// This class implements fair_kripke_succ_iterator::cond(),
|
||||
/// and fair_kripke_succ_iterator::acc().
|
||||
class SPOT_API fair_kripke_succ_iterator : public twa_succ_iterator
|
||||
{
|
||||
public:
|
||||
/// \brief Constructor
|
||||
///
|
||||
/// The \a cond and \a acc_cond arguments will be those returned
|
||||
/// by fair_kripke_succ_iterator::cond(),
|
||||
/// and fair_kripke_succ_iterator::acc().
|
||||
fair_kripke_succ_iterator(const bdd& cond, acc_cond::mark_t acc_cond);
|
||||
virtual ~fair_kripke_succ_iterator();
|
||||
|
||||
virtual bdd cond() const;
|
||||
virtual acc_cond::mark_t acc() const;
|
||||
protected:
|
||||
bdd cond_;
|
||||
acc_cond::mark_t acc_cond_;
|
||||
};
|
||||
|
||||
/// \ingroup kripke
|
||||
/// \brief Interface for a Fair Kripke structure.
|
||||
///
|
||||
/// A Kripke structure is a graph in which each node (=state) is
|
||||
/// labeled by a conjunction of atomic proposition, and a set of
|
||||
/// acceptance conditions.
|
||||
///
|
||||
/// Such a structure can be seen as spot::tgba by pushing all labels
|
||||
/// to the outgoing transitions.
|
||||
///
|
||||
/// A programmer that develops an instance of Fair Kripke structure
|
||||
/// needs just provide an implementation for the following methods:
|
||||
///
|
||||
/// - kripke::get_init_state()
|
||||
/// - kripke::succ_iter()
|
||||
/// - kripke::state_condition()
|
||||
/// - kripke::state_acceptance_conditions()
|
||||
/// - kripke::format_state()
|
||||
/// - and optionally kripke::transition_annotation()
|
||||
///
|
||||
/// The other methods of the tgba interface are supplied by this
|
||||
/// class and need not be defined.
|
||||
///
|
||||
/// See also spot::fair_kripke_succ_iterator.
|
||||
class SPOT_API fair_kripke: public twa
|
||||
{
|
||||
public:
|
||||
fair_kripke(const bdd_dict_ptr& d)
|
||||
: twa(d)
|
||||
{
|
||||
}
|
||||
|
||||
/// \brief The condition that label the state \a s.
|
||||
///
|
||||
/// This should be a conjunction of atomic propositions.
|
||||
virtual bdd state_condition(const state* s) const = 0;
|
||||
|
||||
/// \brief The set of acceptance conditions that label the state \a s.
|
||||
virtual acc_cond::mark_t
|
||||
state_acceptance_conditions(const state* s) const = 0;
|
||||
|
||||
protected:
|
||||
virtual bdd compute_support_conditions(const state* s) const;
|
||||
};
|
||||
}
|
||||
37
spot/kripke/fwd.hh
Normal file
37
spot/kripke/fwd.hh
Normal file
|
|
@ -0,0 +1,37 @@
|
|||
// -*- coding: utf-8 -*-
|
||||
// Copyright (C) 2014 Laboratoire de Recherche et Développement de
|
||||
// l'Epita (LRDE).
|
||||
//
|
||||
// This file is part of Spot, a model checking library.
|
||||
//
|
||||
// Spot is free software; you can redistribute it and/or modify it
|
||||
// under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation; either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// Spot is distributed in the hope that it will be useful, but WITHOUT
|
||||
// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
||||
// or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public
|
||||
// License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <memory>
|
||||
|
||||
namespace spot
|
||||
{
|
||||
class fair_kripke;
|
||||
typedef std::shared_ptr<fair_kripke> fair_kripke_ptr;
|
||||
typedef std::shared_ptr<const fair_kripke> const_fair_kripke_ptr;
|
||||
|
||||
class kripke;
|
||||
typedef std::shared_ptr<const kripke> const_kripke_ptr;
|
||||
typedef std::shared_ptr<kripke> tgba_kripke_ptr;
|
||||
|
||||
class kripke_explicit;
|
||||
typedef std::shared_ptr<const kripke_explicit> const_kripke_explicit_ptr;
|
||||
typedef std::shared_ptr<kripke_explicit> kripke_explicit_ptr;
|
||||
}
|
||||
54
spot/kripke/kripke.cc
Normal file
54
spot/kripke/kripke.cc
Normal file
|
|
@ -0,0 +1,54 @@
|
|||
// -*- coding: utf-8 -*-
|
||||
// Copyright (C) 2009, 2010, 2014 Laboratoire de Recherche et
|
||||
// Developpement de l'Epita
|
||||
//
|
||||
// This file is part of Spot, a model checking library.
|
||||
//
|
||||
// Spot is free software; you can redistribute it and/or modify it
|
||||
// under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation; either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// Spot is distributed in the hope that it will be useful, but WITHOUT
|
||||
// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
||||
// or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public
|
||||
// License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
#include <spot/kripke/kripke.hh>
|
||||
|
||||
namespace spot
|
||||
{
|
||||
|
||||
kripke_succ_iterator::~kripke_succ_iterator()
|
||||
{
|
||||
}
|
||||
|
||||
bdd
|
||||
kripke_succ_iterator::cond() const
|
||||
{
|
||||
// Do not assert(!done()) here. It is OK to call
|
||||
// this function on a state without successor.
|
||||
return cond_;
|
||||
}
|
||||
|
||||
acc_cond::mark_t
|
||||
kripke_succ_iterator::acc() const
|
||||
{
|
||||
// Do not assert(!done()) here. It is OK to call
|
||||
// this function on a state without successor.
|
||||
return 0U;
|
||||
}
|
||||
|
||||
kripke::~kripke()
|
||||
{
|
||||
}
|
||||
|
||||
acc_cond::mark_t
|
||||
kripke::state_acceptance_conditions(const state*) const
|
||||
{
|
||||
return 0U;
|
||||
}
|
||||
}
|
||||
106
spot/kripke/kripke.hh
Normal file
106
spot/kripke/kripke.hh
Normal file
|
|
@ -0,0 +1,106 @@
|
|||
// -*- coding: utf-8 -*-
|
||||
// Copyright (C) 2009, 2010, 2013, 2014 Laboratoire de Recherche et
|
||||
// Developpement de l'Epita
|
||||
//
|
||||
// This file is part of Spot, a model checking library.
|
||||
//
|
||||
// Spot is free software; you can redistribute it and/or modify it
|
||||
// under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation; either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// Spot is distributed in the hope that it will be useful, but WITHOUT
|
||||
// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
||||
// or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public
|
||||
// License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <spot/kripke/fairkripke.hh>
|
||||
|
||||
namespace spot
|
||||
{
|
||||
|
||||
/// \ingroup kripke
|
||||
/// \brief Iterator code for Kripke structure
|
||||
///
|
||||
/// This iterator can be used to simplify the writing
|
||||
/// of an iterator on a Kripke structure (or lookalike).
|
||||
///
|
||||
/// If you inherit from this iterator, you should only
|
||||
/// redefine
|
||||
///
|
||||
/// - kripke_succ_iterator::first()
|
||||
/// - kripke_succ_iterator::next()
|
||||
/// - kripke_succ_iterator::done()
|
||||
/// - kripke_succ_iterator::dst()
|
||||
///
|
||||
/// This class implements kripke_succ_iterator::cond(),
|
||||
/// and kripke_succ_iterator::acc().
|
||||
class SPOT_API kripke_succ_iterator : public twa_succ_iterator
|
||||
{
|
||||
public:
|
||||
/// \brief Constructor
|
||||
///
|
||||
/// The \a cond argument will be the one returned
|
||||
/// by kripke_succ_iterator::cond().
|
||||
kripke_succ_iterator(const bdd& cond)
|
||||
: cond_(cond)
|
||||
{
|
||||
}
|
||||
|
||||
void recycle(const bdd& cond)
|
||||
{
|
||||
cond_ = cond;
|
||||
}
|
||||
|
||||
virtual ~kripke_succ_iterator();
|
||||
|
||||
virtual bdd cond() const;
|
||||
virtual acc_cond::mark_t acc() const;
|
||||
protected:
|
||||
bdd cond_;
|
||||
};
|
||||
|
||||
/// \ingroup kripke
|
||||
/// \brief Interface for a Kripke structure
|
||||
///
|
||||
/// A Kripke structure is a graph in which each node (=state) is
|
||||
/// labeled by a conjunction of atomic proposition.
|
||||
///
|
||||
/// Such a structure can be seen as spot::tgba without
|
||||
/// any acceptance condition.
|
||||
///
|
||||
/// A programmer that develops an instance of Kripke structure needs
|
||||
/// just provide an implementation for the following methods:
|
||||
///
|
||||
/// - kripke::get_init_state()
|
||||
/// - kripke::succ_iter()
|
||||
/// - kripke::state_condition()
|
||||
/// - kripke::format_state()
|
||||
/// - and optionally kripke::transition_annotation()
|
||||
///
|
||||
/// The other methods of the tgba interface (like those dealing with
|
||||
/// acceptance conditions) are supplied by this kripke class and
|
||||
/// need not be defined.
|
||||
///
|
||||
/// See also spot::kripke_succ_iterator.
|
||||
class SPOT_API kripke: public fair_kripke
|
||||
{
|
||||
public:
|
||||
kripke(const bdd_dict_ptr& d)
|
||||
: fair_kripke(d)
|
||||
{
|
||||
}
|
||||
|
||||
virtual ~kripke();
|
||||
|
||||
virtual acc_cond::mark_t state_acceptance_conditions(const state*) const;
|
||||
};
|
||||
|
||||
typedef std::shared_ptr<kripke> kripke_ptr;
|
||||
typedef std::shared_ptr<const kripke> const_kripke_ptr;
|
||||
}
|
||||
282
spot/kripke/kripkegraph.hh
Normal file
282
spot/kripke/kripkegraph.hh
Normal file
|
|
@ -0,0 +1,282 @@
|
|||
// -*- coding: utf-8 -*-
|
||||
// Copyright (C) 2011, 2012, 2013, 2014, 2015 Laboratoire de Recherche
|
||||
// et Développement de l'Epita (LRDE)
|
||||
//
|
||||
// This file is part of Spot, a model checking library.
|
||||
//
|
||||
// Spot is free software; you can redistribute it and/or modify it
|
||||
// under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation; either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// Spot is distributed in the hope that it will be useful, but WITHOUT
|
||||
// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
||||
// or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public
|
||||
// License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <iosfwd>
|
||||
#include <spot/kripke/kripke.hh>
|
||||
#include <spot/graph/graph.hh>
|
||||
#include <spot/tl/formula.hh>
|
||||
|
||||
namespace spot
|
||||
{
|
||||
/// \brief Concrete class for kripke_graph states.
|
||||
struct SPOT_API kripke_graph_state: public spot::state
|
||||
{
|
||||
public:
|
||||
kripke_graph_state(bdd cond = bddfalse) noexcept
|
||||
: cond_(cond)
|
||||
{
|
||||
}
|
||||
|
||||
virtual ~kripke_graph_state() noexcept
|
||||
{
|
||||
}
|
||||
|
||||
virtual int compare(const spot::state* other) const
|
||||
{
|
||||
auto o = down_cast<const kripke_graph_state*>(other);
|
||||
assert(o);
|
||||
|
||||
// Do not simply return "other - this", it might not fit in an int.
|
||||
if (o < this)
|
||||
return -1;
|
||||
if (o > this)
|
||||
return 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
virtual size_t hash() const
|
||||
{
|
||||
return
|
||||
reinterpret_cast<const char*>(this) - static_cast<const char*>(nullptr);
|
||||
}
|
||||
|
||||
virtual kripke_graph_state*
|
||||
clone() const
|
||||
{
|
||||
return const_cast<kripke_graph_state*>(this);
|
||||
}
|
||||
|
||||
virtual void destroy() const
|
||||
{
|
||||
}
|
||||
|
||||
bdd cond() const
|
||||
{
|
||||
return cond_;
|
||||
}
|
||||
|
||||
void cond(bdd c)
|
||||
{
|
||||
cond_ = c;
|
||||
}
|
||||
|
||||
private:
|
||||
bdd cond_;
|
||||
};
|
||||
|
||||
template<class Graph>
|
||||
class SPOT_API kripke_graph_succ_iterator final: public kripke_succ_iterator
|
||||
{
|
||||
private:
|
||||
typedef typename Graph::edge edge;
|
||||
typedef typename Graph::state_data_t state;
|
||||
const Graph* g_;
|
||||
edge t_;
|
||||
edge p_;
|
||||
public:
|
||||
kripke_graph_succ_iterator(const Graph* g,
|
||||
const typename Graph::state_storage_t* s):
|
||||
kripke_succ_iterator(s->cond()),
|
||||
g_(g),
|
||||
t_(s->succ)
|
||||
{
|
||||
}
|
||||
|
||||
~kripke_graph_succ_iterator()
|
||||
{
|
||||
}
|
||||
|
||||
virtual void recycle(const typename Graph::state_storage_t* s)
|
||||
{
|
||||
cond_ = s->cond();
|
||||
t_ = s->succ;
|
||||
}
|
||||
|
||||
virtual bool first()
|
||||
{
|
||||
p_ = t_;
|
||||
return p_;
|
||||
}
|
||||
|
||||
virtual bool next()
|
||||
{
|
||||
p_ = g_->edge_storage(p_).next_succ;
|
||||
return p_;
|
||||
}
|
||||
|
||||
virtual bool done() const
|
||||
{
|
||||
return !p_;
|
||||
}
|
||||
|
||||
virtual kripke_graph_state* dst() const
|
||||
{
|
||||
assert(!done());
|
||||
return const_cast<kripke_graph_state*>
|
||||
(&g_->state_data(g_->edge_storage(p_).dst));
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/// \class kripke_graph
|
||||
/// \brief Kripke Structure.
|
||||
class SPOT_API kripke_graph : public kripke
|
||||
{
|
||||
public:
|
||||
typedef digraph<kripke_graph_state, void> graph_t;
|
||||
typedef graph_t::edge_storage_t edge_storage_t;
|
||||
protected:
|
||||
graph_t g_;
|
||||
mutable unsigned init_number_;
|
||||
public:
|
||||
kripke_graph(const bdd_dict_ptr& d)
|
||||
: kripke(d), init_number_(0)
|
||||
{
|
||||
}
|
||||
|
||||
virtual ~kripke_graph()
|
||||
{
|
||||
get_dict()->unregister_all_my_variables(this);
|
||||
}
|
||||
|
||||
unsigned num_states() const
|
||||
{
|
||||
return g_.num_states();
|
||||
}
|
||||
|
||||
unsigned num_edges() const
|
||||
{
|
||||
return g_.num_edges();
|
||||
}
|
||||
|
||||
void set_init_state(graph_t::state s)
|
||||
{
|
||||
assert(s < num_states());
|
||||
init_number_ = s;
|
||||
}
|
||||
|
||||
graph_t::state get_init_state_number() const
|
||||
{
|
||||
if (num_states() == 0)
|
||||
const_cast<graph_t&>(g_).new_state();
|
||||
return init_number_;
|
||||
}
|
||||
|
||||
virtual const kripke_graph_state* get_init_state() const
|
||||
{
|
||||
if (num_states() == 0)
|
||||
const_cast<graph_t&>(g_).new_state();
|
||||
return state_from_number(init_number_);
|
||||
}
|
||||
|
||||
/// \brief Allow to get an iterator on the state we passed in
|
||||
/// parameter.
|
||||
virtual kripke_graph_succ_iterator<graph_t>*
|
||||
succ_iter(const spot::state* st) const
|
||||
{
|
||||
auto s = down_cast<const typename graph_t::state_storage_t*>(st);
|
||||
assert(s);
|
||||
assert(!s->succ || g_.valid_trans(s->succ));
|
||||
|
||||
if (this->iter_cache_)
|
||||
{
|
||||
auto it =
|
||||
down_cast<kripke_graph_succ_iterator<graph_t>*>(this->iter_cache_);
|
||||
it->recycle(s);
|
||||
this->iter_cache_ = nullptr;
|
||||
return it;
|
||||
}
|
||||
return new kripke_graph_succ_iterator<graph_t>(&g_, s);
|
||||
|
||||
}
|
||||
|
||||
graph_t::state
|
||||
state_number(const state* st) const
|
||||
{
|
||||
auto s = down_cast<const typename graph_t::state_storage_t*>(st);
|
||||
assert(s);
|
||||
return s - &g_.state_storage(0);
|
||||
}
|
||||
|
||||
const kripke_graph_state*
|
||||
state_from_number(graph_t::state n) const
|
||||
{
|
||||
return &g_.state_data(n);
|
||||
}
|
||||
|
||||
kripke_graph_state*
|
||||
state_from_number(graph_t::state n)
|
||||
{
|
||||
return &g_.state_data(n);
|
||||
}
|
||||
|
||||
std::string format_state(unsigned n) const
|
||||
{
|
||||
std::stringstream ss;
|
||||
ss << n;
|
||||
return ss.str();
|
||||
}
|
||||
|
||||
virtual std::string format_state(const state* st) const
|
||||
{
|
||||
return format_state(state_number(st));
|
||||
}
|
||||
|
||||
/// \brief Get the condition on the state
|
||||
virtual bdd state_condition(const state* s) const
|
||||
{
|
||||
return down_cast<const kripke_graph_state*>(s)->cond();
|
||||
}
|
||||
|
||||
edge_storage_t& edge_storage(unsigned t)
|
||||
{
|
||||
return g_.edge_storage(t);
|
||||
}
|
||||
|
||||
const edge_storage_t edge_storage(unsigned t) const
|
||||
{
|
||||
return g_.edge_storage(t);
|
||||
}
|
||||
|
||||
unsigned new_state(bdd cond)
|
||||
{
|
||||
return g_.new_state(cond);
|
||||
}
|
||||
|
||||
unsigned new_states(unsigned n, bdd cond)
|
||||
{
|
||||
return g_.new_states(n, cond);
|
||||
}
|
||||
|
||||
unsigned new_edge(unsigned src, unsigned dst)
|
||||
{
|
||||
return g_.new_edge(src, dst);
|
||||
}
|
||||
};
|
||||
|
||||
typedef std::shared_ptr<kripke_graph> kripke_graph_ptr;
|
||||
|
||||
inline kripke_graph_ptr
|
||||
make_kripke_graph(const bdd_dict_ptr& d)
|
||||
{
|
||||
return std::make_shared<kripke_graph>(d);
|
||||
}
|
||||
}
|
||||
7
spot/misc/.gitignore
vendored
Normal file
7
spot/misc/.gitignore
vendored
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
Makefile
|
||||
Makefile.in
|
||||
.deps
|
||||
.libs
|
||||
*.lo
|
||||
*.la
|
||||
_config.h
|
||||
73
spot/misc/Makefile.am
Normal file
73
spot/misc/Makefile.am
Normal file
|
|
@ -0,0 +1,73 @@
|
|||
## -*- coding: utf-8 -*-
|
||||
## Copyright (C) 2011, 2012, 2013, 2014 Laboratoire de Recherche et
|
||||
## Développement de l'Epita (LRDE).
|
||||
## Copyright (C) 2003, 2004, 2005, 2006 Laboratoire d'Informatique de
|
||||
## Paris 6 (LIP6), département Systèmes Répartis Coopératifs (SRC),
|
||||
## Université Pierre et Marie Curie.
|
||||
##
|
||||
## This file is part of Spot, a model checking library.
|
||||
##
|
||||
## Spot is free software; you can redistribute it and/or modify it
|
||||
## under the terms of the GNU General Public License as published by
|
||||
## the Free Software Foundation; either version 3 of the License, or
|
||||
## (at your option) any later version.
|
||||
##
|
||||
## Spot is distributed in the hope that it will be useful, but WITHOUT
|
||||
## ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
||||
## or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public
|
||||
## License for more details.
|
||||
##
|
||||
## You should have received a copy of the GNU General Public License
|
||||
## along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
AM_CPPFLAGS = -I$(top_builddir) -I$(top_srcdir) $(BUDDY_CPPFLAGS) \
|
||||
-I$(top_builddir)/lib -I$(top_srcdir)/lib
|
||||
AM_CXXFLAGS = $(WARNING_CXXFLAGS)
|
||||
|
||||
miscdir = $(pkgincludedir)/misc
|
||||
|
||||
nodist_misc_HEADERS = _config.h
|
||||
DISTCLEANFILES = _config.h
|
||||
|
||||
misc_HEADERS = \
|
||||
bareword.hh \
|
||||
bddlt.hh \
|
||||
bitvect.hh \
|
||||
casts.hh \
|
||||
common.hh \
|
||||
escape.hh \
|
||||
fixpool.hh \
|
||||
formater.hh \
|
||||
hash.hh \
|
||||
hashfunc.hh \
|
||||
intvcomp.hh \
|
||||
intvcmp2.hh \
|
||||
location.hh \
|
||||
ltstr.hh \
|
||||
minato.hh \
|
||||
memusage.hh \
|
||||
mspool.hh \
|
||||
optionmap.hh \
|
||||
position.hh \
|
||||
random.hh \
|
||||
satsolver.hh \
|
||||
timer.hh \
|
||||
tmpfile.hh \
|
||||
version.hh
|
||||
|
||||
noinst_LTLIBRARIES = libmisc.la
|
||||
libmisc_la_SOURCES = \
|
||||
bareword.cc \
|
||||
bitvect.cc \
|
||||
escape.cc \
|
||||
formater.cc \
|
||||
intvcomp.cc \
|
||||
intvcmp2.cc \
|
||||
memusage.cc \
|
||||
minato.cc \
|
||||
optionmap.cc \
|
||||
random.cc \
|
||||
satsolver.cc \
|
||||
timer.cc \
|
||||
tmpfile.cc \
|
||||
version.cc
|
||||
65
spot/misc/bareword.cc
Normal file
65
spot/misc/bareword.cc
Normal file
|
|
@ -0,0 +1,65 @@
|
|||
// -*- coding: utf-8 -*-
|
||||
// Copyright (C) 2011, 2013, 2015 Laboratoire de Recherche et Développement
|
||||
// de l'Epita (LRDE).
|
||||
// Copyright (C) 2004 Laboratoire d'Informatique de Paris 6 (LIP6),
|
||||
// département Systèmes Répartis Coopératifs (SRC), Université Pierre
|
||||
// et Marie Curie.
|
||||
//
|
||||
// This file is part of Spot, a model checking library.
|
||||
//
|
||||
// Spot is free software; you can redistribute it and/or modify it
|
||||
// under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation; either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// Spot is distributed in the hope that it will be useful, but WITHOUT
|
||||
// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
||||
// or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public
|
||||
// License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
#include "config.h"
|
||||
#include <spot/misc/bareword.hh>
|
||||
#include <ctype.h>
|
||||
#include <spot/misc/escape.hh>
|
||||
|
||||
namespace spot
|
||||
{
|
||||
bool
|
||||
is_bare_word(const char* str)
|
||||
{
|
||||
// Bare words cannot be empty and should start with a letter.
|
||||
if (!*str
|
||||
|| !(isalpha(*str) || *str == '_' || *str == '.'))
|
||||
return false;
|
||||
// The remaining of the word must be alphanumeric.
|
||||
while (*++str)
|
||||
if (!(isalnum(*str) || *str == '_' || *str == '.'))
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
std::string
|
||||
quote_unless_bare_word(const std::string& str)
|
||||
{
|
||||
if (is_bare_word(str.c_str()))
|
||||
return str;
|
||||
else
|
||||
return "\"" + escape_str(str) + "\"";
|
||||
}
|
||||
|
||||
// This is for Spin 5. Spin 6 has a relaxed parser that can
|
||||
// accept any parenthesized block as an atomic propoistion.
|
||||
bool is_spin_ap(const char* str)
|
||||
{
|
||||
if (!str || !islower(*str))
|
||||
return false;
|
||||
while (*++str)
|
||||
if (!(isalnum(*str) || *str == '_'))
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
53
spot/misc/bareword.hh
Normal file
53
spot/misc/bareword.hh
Normal file
|
|
@ -0,0 +1,53 @@
|
|||
// -*- coding: utf-8 -*-
|
||||
// Copyright (C) 2013, 2015 Laboratoire de Recherche et Développement
|
||||
// de l'Epita (LRDE).
|
||||
// Copyright (C) 2004, 2005 Laboratoire d'Informatique de Paris 6 (LIP6),
|
||||
// département Systèmes Répartis Coopératifs (SRC), Université Pierre
|
||||
// et Marie Curie.
|
||||
//
|
||||
// This file is part of Spot, a model checking library.
|
||||
//
|
||||
// Spot is free software; you can redistribute it and/or modify it
|
||||
// under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation; either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// Spot is distributed in the hope that it will be useful, but WITHOUT
|
||||
// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
||||
// or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public
|
||||
// License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <spot/misc/common.hh>
|
||||
#include <string>
|
||||
|
||||
namespace spot
|
||||
{
|
||||
/// \defgroup misc_tools Miscellaneous helper functions
|
||||
|
||||
|
||||
/// \ingroup misc_tools
|
||||
/// @{
|
||||
/// \brief Whether a word is bare.
|
||||
///
|
||||
/// Bare words should start with a letter, an underscore, or a dot,
|
||||
/// and consist solely of alphanumeric characters, underscores, and
|
||||
/// dots.
|
||||
SPOT_API bool is_bare_word(const char* str);
|
||||
|
||||
/// \brief Double-quote words that are not bare.
|
||||
/// \see is_bare_word
|
||||
SPOT_API std::string quote_unless_bare_word(const std::string& str);
|
||||
|
||||
/// \brief Whether a word can be used as an atomic proposition for Spin 5.
|
||||
///
|
||||
/// In Spin 5 (hence in ltl2ba and ltl3ba as well) atomic
|
||||
/// propositions should start with a lowercase letter, and can then
|
||||
/// consist solely of alphanumeric characters and underscores.
|
||||
SPOT_API bool is_spin_ap(const char* str);
|
||||
/// @}
|
||||
}
|
||||
53
spot/misc/bddlt.hh
Normal file
53
spot/misc/bddlt.hh
Normal file
|
|
@ -0,0 +1,53 @@
|
|||
// -*- coding: utf-8 -*-
|
||||
// Copyright (C) 2011, 2014 Laboratoire de Recherche et Developpement de
|
||||
// l'Epita (LRDE).
|
||||
// Copyright (C) 2003, 2004 Laboratoire d'Informatique de Paris 6 (LIP6),
|
||||
// département Systèmes Répartis Coopératifs (SRC), Université Pierre
|
||||
// et Marie Curie.
|
||||
//
|
||||
// This file is part of Spot, a model checking library.
|
||||
//
|
||||
// Spot is free software; you can redistribute it and/or modify it
|
||||
// under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation; either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// Spot is distributed in the hope that it will be useful, but WITHOUT
|
||||
// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
||||
// or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public
|
||||
// License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <bddx.h>
|
||||
#include <functional>
|
||||
|
||||
namespace spot
|
||||
{
|
||||
/// \ingroup misc_tools
|
||||
/// \brief Comparison functor for BDDs.
|
||||
struct bdd_less_than :
|
||||
public std::binary_function<const bdd&, const bdd&, bool>
|
||||
{
|
||||
bool
|
||||
operator()(const bdd& left, const bdd& right) const
|
||||
{
|
||||
return left.id() < right.id();
|
||||
}
|
||||
};
|
||||
|
||||
/// \ingroup misc_tools
|
||||
/// \brief Hash functor for BDDs.
|
||||
struct bdd_hash :
|
||||
public std::unary_function<const bdd&, size_t>
|
||||
{
|
||||
size_t
|
||||
operator()(const bdd& b) const
|
||||
{
|
||||
return b.id();
|
||||
}
|
||||
};
|
||||
}
|
||||
192
spot/misc/bitvect.cc
Normal file
192
spot/misc/bitvect.cc
Normal file
|
|
@ -0,0 +1,192 @@
|
|||
// -*- coding: utf-8 -*-
|
||||
// Copyright (C) 2013, 2014 Laboratoire de Recherche et Développement
|
||||
// de l'Epita (LRDE).
|
||||
//
|
||||
// This file is part of Spot, a model checking library.
|
||||
//
|
||||
// Spot is free software; you can redistribute it and/or modify it
|
||||
// under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation; either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// Spot is distributed in the hope that it will be useful, but WITHOUT
|
||||
// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
||||
// or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public
|
||||
// License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
# include "config.h"
|
||||
#endif
|
||||
#include <spot/misc/bitvect.hh>
|
||||
#include <new>
|
||||
#include <iostream>
|
||||
#include <cmath>
|
||||
#include <hashfunc.hh>
|
||||
#include <cstring>
|
||||
#include <climits>
|
||||
|
||||
namespace spot
|
||||
{
|
||||
namespace
|
||||
{
|
||||
|
||||
// How many block_t do we need to store BITCOUNT bits?
|
||||
size_t block_needed(size_t bitcount)
|
||||
{
|
||||
const size_t bpb = 8 * sizeof(bitvect::block_t);
|
||||
size_t n = (bitcount + bpb - 1) / bpb;
|
||||
if (n < 1)
|
||||
return 1;
|
||||
return n;
|
||||
}
|
||||
|
||||
// Fowler-Noll-Vo hash parameters.
|
||||
// Add specializations as needed.
|
||||
template<int numbytes>
|
||||
struct fnv
|
||||
{
|
||||
};
|
||||
|
||||
// Do not define the following if ULONG_MAX cannot
|
||||
// hold a 64-bit value, otherwise the parser will
|
||||
// choke when parsing the constants.
|
||||
#if ULONG_MAX >> 31 >> 31 >> 1 > 0
|
||||
// Fowler-Noll-Vo hash parameters for 64bits
|
||||
template<>
|
||||
struct fnv<8>
|
||||
{
|
||||
static unsigned long init()
|
||||
{
|
||||
return 14695981039346656037UL;
|
||||
}
|
||||
|
||||
static unsigned long prime()
|
||||
{
|
||||
return 1099511628211UL;
|
||||
}
|
||||
};
|
||||
#endif
|
||||
|
||||
// Fowler-Noll-Vo hash parameters for 32bits
|
||||
template<>
|
||||
struct fnv<4>
|
||||
{
|
||||
static unsigned long init()
|
||||
{
|
||||
return 2166136261UL;
|
||||
}
|
||||
|
||||
static unsigned long prime()
|
||||
{
|
||||
return 16777619UL;
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
bitvect::bitvect(size_t size, size_t block_count):
|
||||
size_(size),
|
||||
block_count_(block_count),
|
||||
storage_(&local_storage_)
|
||||
{
|
||||
clear_all();
|
||||
}
|
||||
|
||||
bitvect::bitvect(size_t size, size_t block_count, bool):
|
||||
size_(size),
|
||||
block_count_(block_count),
|
||||
storage_(&local_storage_)
|
||||
{
|
||||
}
|
||||
|
||||
bitvect* bitvect::clone() const
|
||||
{
|
||||
size_t n = block_needed(size_);
|
||||
// Allocate some memory for the bitvect. The instance
|
||||
// already contains one int of local_storage_, but
|
||||
// we allocate n-1 more so that we store the table.
|
||||
void* mem = operator new(sizeof(bitvect)
|
||||
+ (n - 1) * sizeof(bitvect::block_t));
|
||||
bitvect* res = new(mem) bitvect(size_, n, true);
|
||||
memcpy(res->storage_, storage_, res->block_count_ * sizeof(block_t));
|
||||
return res;
|
||||
}
|
||||
|
||||
size_t bitvect::hash() const
|
||||
{
|
||||
|
||||
block_t res = fnv<sizeof(block_t)>::init();
|
||||
size_t i;
|
||||
size_t m = used_blocks();
|
||||
if (m == 0)
|
||||
return res;
|
||||
for (i = 0; i < m - 1; ++i)
|
||||
{
|
||||
res ^= storage_[i];
|
||||
res *= fnv<sizeof(block_t)>::prime();
|
||||
}
|
||||
// Deal with the last block, that might not be fully used.
|
||||
// Compute the number n of bits used in the last block.
|
||||
const size_t bpb = 8 * sizeof(bitvect::block_t);
|
||||
size_t n = size() % bpb;
|
||||
// Use only the least n bits from storage_[i].
|
||||
res ^= storage_[i] & ((1UL << n) - 1);
|
||||
return res;
|
||||
}
|
||||
|
||||
bitvect* make_bitvect(size_t bitcount)
|
||||
{
|
||||
size_t n = block_needed(bitcount);
|
||||
// Allocate some memory for the bitvect. The instance
|
||||
// already contains one int of local_storage_, but
|
||||
// we allocate n-1 more so that we store the table.
|
||||
void* mem = operator new(sizeof(bitvect)
|
||||
+ (n - 1) * sizeof(bitvect::block_t));
|
||||
return new(mem) bitvect(bitcount, n);
|
||||
}
|
||||
|
||||
|
||||
bitvect_array* make_bitvect_array(size_t bitcount, size_t vectcount)
|
||||
{
|
||||
size_t n = block_needed(bitcount);
|
||||
// Size of one bitvect.
|
||||
size_t bvsize = sizeof(bitvect) + (n - 1) * sizeof(bitvect::block_t);
|
||||
// Allocate the bitvect_array with enough space at the end
|
||||
// to store all bitvect instances.
|
||||
void* mem = operator new(sizeof(bitvect_array) + bvsize * vectcount);
|
||||
bitvect_array* bva = new(mem) bitvect_array(vectcount, bvsize);
|
||||
// Initialize all the bitvect instances.
|
||||
for (size_t i = 0; i < vectcount; ++i)
|
||||
new(bva->storage() + i * bvsize) bitvect(bitcount, n);
|
||||
return bva;
|
||||
}
|
||||
|
||||
std::ostream& operator<<(std::ostream& os , const bitvect& v)
|
||||
{
|
||||
for (size_t i = 0, end = v.size(); i != end; ++i)
|
||||
os << (v.get(i) ? '1' : '0');
|
||||
return os;
|
||||
}
|
||||
|
||||
std::ostream& operator<<(std::ostream& os , const bitvect_array& a)
|
||||
{
|
||||
size_t end = a.size();
|
||||
if (end == 0)
|
||||
{
|
||||
os << "empty\n";
|
||||
return os;
|
||||
}
|
||||
int w = floor(log10(end - 1)) + 1;
|
||||
for (size_t i = 0; i != end; ++i)
|
||||
{
|
||||
os.width(w);
|
||||
os << i;
|
||||
os.width(1);
|
||||
os << ": " << a.at(i) << '\n';
|
||||
}
|
||||
return os;
|
||||
}
|
||||
}
|
||||
534
spot/misc/bitvect.hh
Normal file
534
spot/misc/bitvect.hh
Normal file
|
|
@ -0,0 +1,534 @@
|
|||
// -*- coding: utf-8 -*-
|
||||
// Copyright (C) 2013, 2014, 2015 Laboratoire de Recherche et
|
||||
// Développement de l'Epita (LRDE).
|
||||
//
|
||||
// This file is part of Spot, a model checking library.
|
||||
//
|
||||
// Spot is free software; you can redistribute it and/or modify it
|
||||
// under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation; either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// Spot is distributed in the hope that it will be useful, but WITHOUT
|
||||
// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
||||
// or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public
|
||||
// License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <spot/misc/common.hh>
|
||||
#include <cstddef>
|
||||
#include <cstdlib>
|
||||
#include <cassert>
|
||||
#include <iosfwd>
|
||||
#include <iostream>
|
||||
#include <algorithm>
|
||||
|
||||
namespace spot
|
||||
{
|
||||
/// \ingroup misc_tools
|
||||
/// @{
|
||||
|
||||
class bitvect;
|
||||
class bitvect_array;
|
||||
|
||||
///\brief Allocate a bit-vector of \a bitcount bits.
|
||||
///
|
||||
/// The resulting object should be released with <code>delete</code>.
|
||||
SPOT_API bitvect* make_bitvect(size_t bitcount);
|
||||
|
||||
/// \brief Allocate \a vectcount bit-vectors of \a bitcount bits.
|
||||
///
|
||||
/// The resulting bitvect_array should be released with <code>delete</code>.
|
||||
SPOT_API bitvect_array* make_bitvect_array(size_t bitcount,
|
||||
size_t vectcount);
|
||||
|
||||
/// \brief A bit vector
|
||||
class SPOT_API bitvect
|
||||
{
|
||||
private:
|
||||
// Used by make_bitvect to construct a large bitvect in place.
|
||||
bitvect(size_t size, size_t block_count);
|
||||
bitvect(size_t size, size_t block_count, bool);
|
||||
|
||||
public:
|
||||
typedef unsigned long block_t;
|
||||
|
||||
bitvect():
|
||||
size_(0),
|
||||
block_count_(1),
|
||||
storage_(&local_storage_),
|
||||
local_storage_(0)
|
||||
{
|
||||
}
|
||||
|
||||
bitvect(const bitvect& other):
|
||||
size_(other.size_),
|
||||
block_count_(1),
|
||||
storage_(&local_storage_)
|
||||
{
|
||||
*this = other;
|
||||
}
|
||||
|
||||
bitvect* clone() const;
|
||||
|
||||
void make_empty()
|
||||
{
|
||||
size_ = 0;
|
||||
}
|
||||
|
||||
bitvect& operator=(const bitvect& other)
|
||||
{
|
||||
reserve_blocks(other.block_count_);
|
||||
size_ = other.size();
|
||||
for (size_t i = 0; i < block_count_; ++i)
|
||||
storage_[i] = other.storage_[i];
|
||||
return *this;
|
||||
}
|
||||
|
||||
~bitvect()
|
||||
{
|
||||
if (storage_ != &local_storage_)
|
||||
free(storage_);
|
||||
}
|
||||
|
||||
/// Grow the bitvector to \a new_block_count blocks.
|
||||
///
|
||||
/// This only changes the capacity of the bitvector, not its size.
|
||||
void reserve_blocks(size_t new_block_count)
|
||||
{
|
||||
if (new_block_count < block_count_)
|
||||
return;
|
||||
if (storage_ == &local_storage_)
|
||||
{
|
||||
block_t* new_storage_ = static_cast<block_t*>
|
||||
(malloc(new_block_count * sizeof(block_t)));
|
||||
for (size_t i = 0; i < block_count_; ++i)
|
||||
new_storage_[i] = storage_[i];
|
||||
storage_ = new_storage_;
|
||||
}
|
||||
else
|
||||
{
|
||||
storage_ = static_cast<block_t*>
|
||||
(realloc(storage_, new_block_count * sizeof(block_t)));
|
||||
}
|
||||
block_count_ = new_block_count;
|
||||
}
|
||||
|
||||
private:
|
||||
void grow()
|
||||
{
|
||||
size_t new_block_count_ = (block_count_ + 1) * 7 / 5;
|
||||
reserve_blocks(new_block_count_);
|
||||
}
|
||||
|
||||
public:
|
||||
|
||||
size_t used_blocks() const
|
||||
{
|
||||
const size_t bpb = 8 * sizeof(block_t);
|
||||
return (size_ + bpb - 1) / bpb;
|
||||
}
|
||||
|
||||
/// Append one bit.
|
||||
void push_back(bool val)
|
||||
{
|
||||
if (size() == capacity())
|
||||
grow();
|
||||
size_t pos = size_++;
|
||||
if (val)
|
||||
set(pos);
|
||||
else
|
||||
clear(pos);
|
||||
}
|
||||
|
||||
/// \brief Append the lowest \a count bits of \a data.
|
||||
void push_back(block_t data, unsigned count)
|
||||
{
|
||||
if (size() + count > capacity())
|
||||
grow();
|
||||
const size_t bpb = 8 * sizeof(block_t);
|
||||
|
||||
// Clear the higher bits.
|
||||
if (count != bpb)
|
||||
data &= (1UL << count) - 1;
|
||||
|
||||
size_t n = size() % bpb;
|
||||
size_t i = size_ / bpb;
|
||||
size_ += count;
|
||||
if (n == 0) // Aligned on block_t boundary
|
||||
{
|
||||
storage_[i] = data;
|
||||
}
|
||||
else // Only (bpb-n) bits free in this block.
|
||||
{
|
||||
// Take the lower bpb-n bits of data...
|
||||
block_t mask = (1UL << (bpb - n)) - 1;
|
||||
block_t data1 = (data & mask) << n;
|
||||
mask <<= n;
|
||||
// ... write them on the higher bpb-n bits of last block.
|
||||
storage_[i] = (storage_[i] & ~mask) | data1;
|
||||
// Write the remaining bits in the next block.
|
||||
if (bpb - n < count)
|
||||
storage_[i + 1] = data >> (bpb - n);
|
||||
}
|
||||
}
|
||||
|
||||
size_t size() const
|
||||
{
|
||||
return size_;
|
||||
}
|
||||
|
||||
size_t capacity() const
|
||||
{
|
||||
return 8 * block_count_ * sizeof(block_t);
|
||||
}
|
||||
|
||||
size_t hash() const;
|
||||
|
||||
bool get(size_t pos) const
|
||||
{
|
||||
assert(pos < size_);
|
||||
const size_t bpb = 8 * sizeof(block_t);
|
||||
return storage_[pos / bpb] & (1UL << (pos % bpb));
|
||||
}
|
||||
|
||||
void clear_all()
|
||||
{
|
||||
for (size_t i = 0; i < block_count_; ++i)
|
||||
storage_[i] = 0;
|
||||
}
|
||||
|
||||
bool is_fully_clear() const
|
||||
{
|
||||
size_t i;
|
||||
const size_t bpb = 8 * sizeof(bitvect::block_t);
|
||||
size_t rest = size() % bpb;
|
||||
for (i = 0; i < block_count_ - !!rest; ++i)
|
||||
if (storage_[i] != 0)
|
||||
return false;
|
||||
// The last block might not be fully used, compare only the
|
||||
// relevant portion.
|
||||
if (!rest)
|
||||
return true;
|
||||
block_t mask = (1UL << rest) - 1;
|
||||
return (storage_[i] & mask) == 0;
|
||||
}
|
||||
|
||||
bool is_fully_set() const
|
||||
{
|
||||
size_t i;
|
||||
const size_t bpb = 8 * sizeof(bitvect::block_t);
|
||||
size_t rest = size() % bpb;
|
||||
for (i = 0; i < block_count_ - !!rest; ++i)
|
||||
if (storage_[i] != -1UL)
|
||||
return false;
|
||||
if (!rest)
|
||||
return true;
|
||||
// The last block might not be fully used, compare only the
|
||||
// relevant portion.
|
||||
block_t mask = (1UL << rest) - 1;
|
||||
return ((~storage_[i]) & mask) == 0;
|
||||
}
|
||||
|
||||
void set_all()
|
||||
{
|
||||
for (size_t i = 0; i < block_count_; ++i)
|
||||
storage_[i] = -1UL;
|
||||
}
|
||||
|
||||
void flip_all()
|
||||
{
|
||||
for (size_t i = 0; i < block_count_; ++i)
|
||||
storage_[i] = ~storage_[i];
|
||||
}
|
||||
|
||||
void set(size_t pos)
|
||||
{
|
||||
assert(pos < size_);
|
||||
const size_t bpb = 8 * sizeof(block_t);
|
||||
storage_[pos / bpb] |= 1UL << (pos % bpb);
|
||||
}
|
||||
|
||||
void clear(size_t pos)
|
||||
{
|
||||
assert(pos < size_);
|
||||
const size_t bpb = 8 * sizeof(block_t);
|
||||
storage_[pos / bpb] &= ~(1UL << (pos % bpb));
|
||||
}
|
||||
|
||||
void flip(size_t pos)
|
||||
{
|
||||
assert(pos < size_);
|
||||
const size_t bpb = 8 * sizeof(block_t);
|
||||
storage_[pos / bpb] ^= (1UL << (pos % bpb));
|
||||
}
|
||||
|
||||
|
||||
bitvect& operator|=(const bitvect& other)
|
||||
{
|
||||
assert(other.size_ <= size_);
|
||||
unsigned m = std::min(other.block_count_, block_count_);
|
||||
for (size_t i = 0; i < m; ++i)
|
||||
storage_[i] |= other.storage_[i];
|
||||
return *this;
|
||||
}
|
||||
|
||||
bitvect& operator&=(const bitvect& other)
|
||||
{
|
||||
assert(other.size_ <= size_);
|
||||
unsigned m = std::min(other.block_count_, block_count_);
|
||||
for (size_t i = 0; i < m; ++i)
|
||||
storage_[i] &= other.storage_[i];
|
||||
return *this;
|
||||
}
|
||||
|
||||
bitvect& operator^=(const bitvect& other)
|
||||
{
|
||||
assert(other.size_ <= size_);
|
||||
unsigned m = std::min(other.block_count_, block_count_);
|
||||
for (size_t i = 0; i < m; ++i)
|
||||
storage_[i] ^= other.storage_[i];
|
||||
return *this;
|
||||
}
|
||||
|
||||
bitvect& operator-=(const bitvect& other)
|
||||
{
|
||||
assert(other.block_count_ <= block_count_);
|
||||
for (size_t i = 0; i < other.block_count_; ++i)
|
||||
storage_[i] &= ~other.storage_[i];
|
||||
return *this;
|
||||
}
|
||||
|
||||
bool is_subset_of(const bitvect& other) const
|
||||
{
|
||||
assert(other.block_count_ >= block_count_);
|
||||
|
||||
size_t i;
|
||||
const size_t bpb = 8 * sizeof(bitvect::block_t);
|
||||
size_t rest = size() % bpb;
|
||||
for (i = 0; i < block_count_ - !!rest; ++i)
|
||||
if ((storage_[i] & other.storage_[i]) != other.storage_[i])
|
||||
return false;
|
||||
if (!rest)
|
||||
return true;
|
||||
|
||||
// The last block might not be fully used, compare only the
|
||||
// relevant portion.
|
||||
block_t mask = (1UL << rest) - 1;
|
||||
return ((storage_[i] & mask & other.storage_[i])
|
||||
== (other.storage_[i] & mask));
|
||||
}
|
||||
|
||||
bool operator==(const bitvect& other) const
|
||||
{
|
||||
if (other.size_ != size_)
|
||||
return false;
|
||||
if (size_ == 0)
|
||||
return true;
|
||||
size_t i;
|
||||
size_t m = other.used_blocks();
|
||||
const size_t bpb = 8 * sizeof(bitvect::block_t);
|
||||
size_t rest = size() % bpb;
|
||||
for (i = 0; i < m - !!rest; ++i)
|
||||
if (storage_[i] != other.storage_[i])
|
||||
return false;
|
||||
if (!rest)
|
||||
return true;
|
||||
// The last block might not be fully used, compare only the
|
||||
// relevant portion.
|
||||
block_t mask = (1UL << rest) - 1;
|
||||
return (storage_[i] & mask) == (other.storage_[i] & mask);
|
||||
}
|
||||
|
||||
bool operator!=(const bitvect& other) const
|
||||
{
|
||||
return !(*this == other);
|
||||
}
|
||||
|
||||
bool operator<(const bitvect& other) const
|
||||
{
|
||||
if (size_ != other.size_)
|
||||
return size_ < other.size_;
|
||||
if (size_ == 0)
|
||||
return false;
|
||||
size_t i;
|
||||
size_t m = other.used_blocks();
|
||||
const size_t bpb = 8 * sizeof(bitvect::block_t);
|
||||
size_t rest = size() % bpb;
|
||||
for (i = 0; i < m - !!rest; ++i)
|
||||
if (storage_[i] > other.storage_[i])
|
||||
return false;
|
||||
if (!rest)
|
||||
return true;
|
||||
// The last block might not be fully used, compare only the
|
||||
// relevant portion.
|
||||
block_t mask = (1UL << rest) - 1;
|
||||
return (storage_[i] & mask) < (other.storage_[i] & mask);
|
||||
}
|
||||
|
||||
bool operator>=(const bitvect& other) const
|
||||
{
|
||||
return !(*this < other);
|
||||
}
|
||||
|
||||
bool operator>(const bitvect& other) const
|
||||
{
|
||||
return other < *this;
|
||||
}
|
||||
|
||||
bool operator<=(const bitvect& other) const
|
||||
{
|
||||
return !(other < *this);
|
||||
}
|
||||
|
||||
// \brief Extract a range of bits.
|
||||
//
|
||||
// Build a new bit-vector using the bits from \a begin (included)
|
||||
// to \a end (excluded).
|
||||
bitvect* extract_range(size_t begin, size_t end)
|
||||
{
|
||||
assert(begin <= end);
|
||||
assert(end <= size());
|
||||
size_t count = end - begin;
|
||||
bitvect* res = make_bitvect(count);
|
||||
res->make_empty();
|
||||
|
||||
if (end == begin)
|
||||
return res;
|
||||
|
||||
const size_t bpb = 8 * sizeof(bitvect::block_t);
|
||||
|
||||
size_t indexb = begin / bpb;
|
||||
unsigned bitb = begin % bpb;
|
||||
size_t indexe = (end - 1) / bpb;
|
||||
|
||||
if (indexb == indexe)
|
||||
{
|
||||
block_t data = storage_[indexb];
|
||||
data >>= bitb;
|
||||
res->push_back(data, count);
|
||||
}
|
||||
else
|
||||
{
|
||||
block_t data = storage_[indexb];
|
||||
data >>= bitb;
|
||||
res->push_back(data, bpb - bitb);
|
||||
count -= bpb - bitb;
|
||||
while (count >= bpb)
|
||||
{
|
||||
++indexb;
|
||||
res->push_back(storage_[indexb], bpb);
|
||||
count -= bpb;
|
||||
assert(indexb != indexe || count == 0);
|
||||
}
|
||||
if (count > 0)
|
||||
{
|
||||
++indexb;
|
||||
assert(indexb == indexe);
|
||||
assert(count == end % bpb);
|
||||
res->push_back(storage_[indexb], count);
|
||||
}
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
friend SPOT_API bitvect*
|
||||
::spot::make_bitvect(size_t bitcount);
|
||||
|
||||
/// Print a bitvect.
|
||||
friend SPOT_API std::ostream& operator<<(std::ostream&,
|
||||
const bitvect&);
|
||||
|
||||
private:
|
||||
friend SPOT_API bitvect_array*
|
||||
::spot::make_bitvect_array(size_t bitcount,
|
||||
size_t vectcount);
|
||||
|
||||
size_t size_;
|
||||
size_t block_count_;
|
||||
// storage_ points to local_storage_ as long as size_ <= block_count_ * 8.
|
||||
block_t* storage_;
|
||||
// Keep this at the end of the structure: when make_bitvect is used,
|
||||
// it may allocate more block_t at the end of this structure.
|
||||
block_t local_storage_;
|
||||
};
|
||||
|
||||
class SPOT_API bitvect_array
|
||||
{
|
||||
private:
|
||||
/// Private constructor used by make_bitvect_array().
|
||||
bitvect_array(size_t size, size_t bvsize):
|
||||
size_(size),
|
||||
bvsize_(bvsize)
|
||||
{
|
||||
}
|
||||
|
||||
SPOT_LOCAL bitvect_array(const bitvect_array&) = delete;
|
||||
SPOT_LOCAL void operator=(const bitvect_array&) = delete;
|
||||
|
||||
// Extra storage has been allocated at the end of the struct.
|
||||
char* storage()
|
||||
{
|
||||
return reinterpret_cast<char*>(this) + sizeof(*this);
|
||||
}
|
||||
|
||||
const char* storage() const
|
||||
{
|
||||
return reinterpret_cast<const char*>(this) + sizeof(*this);
|
||||
}
|
||||
|
||||
public:
|
||||
~bitvect_array()
|
||||
{
|
||||
for (size_t i = 0; i < size_; ++i)
|
||||
at(i).~bitvect();
|
||||
}
|
||||
|
||||
/// The number of bitvect in the array.
|
||||
size_t size() const
|
||||
{
|
||||
return size_;
|
||||
}
|
||||
|
||||
/// Return the bit-vector at \a index.
|
||||
bitvect& at(const size_t index)
|
||||
{
|
||||
assert(index < size_);
|
||||
return *reinterpret_cast<bitvect*>(storage() + index * bvsize_);
|
||||
}
|
||||
|
||||
void clear_all()
|
||||
{
|
||||
// FIXME: This could be changed into a large memset if the
|
||||
// individual vectors where not allowed to be reallocated.
|
||||
for (unsigned s = 0; s < size_; s++)
|
||||
at(s).clear_all();
|
||||
}
|
||||
|
||||
/// Return the bit-vector at \a index.
|
||||
const bitvect& at(const size_t index) const
|
||||
{
|
||||
assert(index < size_);
|
||||
return *reinterpret_cast<const bitvect*>(storage() + index * bvsize_);
|
||||
}
|
||||
|
||||
friend SPOT_API bitvect_array*
|
||||
::spot::make_bitvect_array(size_t bitcount,
|
||||
size_t vectcount);
|
||||
|
||||
|
||||
/// Print a bitvect_array.
|
||||
friend SPOT_API std::ostream& operator<<(std::ostream&,
|
||||
const bitvect_array&);
|
||||
|
||||
private:
|
||||
size_t size_;
|
||||
size_t bvsize_;
|
||||
};
|
||||
|
||||
/// @}
|
||||
}
|
||||
35
spot/misc/casts.hh
Normal file
35
spot/misc/casts.hh
Normal file
|
|
@ -0,0 +1,35 @@
|
|||
// -*- coding: utf-8 -*-
|
||||
// Copyright (C) 2011, 2015 Laboratoire de Recherche et Développement
|
||||
// de l'Epita (LRDE).
|
||||
//
|
||||
// This file is part of Spot, a model checking library.
|
||||
//
|
||||
// Spot is free software; you can redistribute it and/or modify it
|
||||
// under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation; either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// Spot is distributed in the hope that it will be useful, but WITHOUT
|
||||
// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
||||
// or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public
|
||||
// License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
#pragma once
|
||||
|
||||
// We usually write code like
|
||||
// subclass* i = down_cast<subclass*>(m);
|
||||
// assert(i);
|
||||
// ... use i ...
|
||||
// When NDEBUG is set, the down_cast is a fast static_cast
|
||||
// and the assert has no effect.
|
||||
// Otherwise, the down_cast is a dynamic_cast and may return 0
|
||||
// on error, which the assert catches.
|
||||
|
||||
#if NDEBUG
|
||||
# define down_cast static_cast
|
||||
#else
|
||||
# define down_cast dynamic_cast
|
||||
#endif
|
||||
118
spot/misc/common.hh
Normal file
118
spot/misc/common.hh
Normal file
|
|
@ -0,0 +1,118 @@
|
|||
// -*- coding: utf-8 -*-
|
||||
// Copyright (C) 2013, 2014, 2015 Laboratoire de Recherche et
|
||||
// Développement de l'Epita (LRDE).
|
||||
//
|
||||
// This file is part of Spot, a model checking library.
|
||||
//
|
||||
// Spot is free software; you can redistribute it and/or modify it
|
||||
// under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation; either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// Spot is distributed in the hope that it will be useful, but WITHOUT
|
||||
// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
||||
// or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public
|
||||
// License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
#include <cstdlib>
|
||||
#include <stdexcept>
|
||||
|
||||
#pragma once
|
||||
|
||||
#ifdef __GNUC__
|
||||
#define SPOT_LIKELY(expr) __builtin_expect(!!(expr), 1)
|
||||
#define SPOT_UNLIKELY(expr) __builtin_expect(!!(expr), 0)
|
||||
#else
|
||||
#define SPOT_LIKELY(expr) (expr)
|
||||
#define SPOT_UNLIKELY(expr) (expr)
|
||||
#endif
|
||||
|
||||
#ifdef __GNUC__
|
||||
#define SPOT_DEPRECATED __attribute__ ((deprecated))
|
||||
#elif defined(_MSC_VER)
|
||||
#define SPOT_DEPRECATED __declspec(deprecated)
|
||||
#else
|
||||
#define SPOT_DEPRECATED func
|
||||
#endif
|
||||
|
||||
#if defined _WIN32 || defined __CYGWIN__
|
||||
#define SPOT_HELPER_DLL_IMPORT __declspec(dllimport)
|
||||
#define SPOT_HELPER_DLL_EXPORT __declspec(dllexport)
|
||||
#define SPOT_HELPER_DLL_LOCAL
|
||||
#else
|
||||
#if __GNUC__ >= 4
|
||||
#define SPOT_HELPER_DLL_IMPORT __attribute__ ((visibility ("default")))
|
||||
#define SPOT_HELPER_DLL_EXPORT __attribute__ ((visibility ("default")))
|
||||
#define SPOT_HELPER_DLL_LOCAL __attribute__ ((visibility ("hidden")))
|
||||
#else
|
||||
#define SPOT_HELPER_DLL_IMPORT
|
||||
#define SPOT_HELPER_DLL_EXPORT
|
||||
#define SPOT_HELPER_DLL_LOCAL
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#ifdef SPOT_BUILD
|
||||
#define SPOT_DLL
|
||||
#endif
|
||||
|
||||
// SPOT_API is used for the public API symbols. It either DLL imports
|
||||
// or DLL exports (or does nothing for static build) SPOT_LOCAL is
|
||||
// used for non-api symbols that may occur in header files.
|
||||
#ifdef SPOT_DLL
|
||||
#ifdef SPOT_BUILD
|
||||
#define SPOT_API SPOT_HELPER_DLL_EXPORT
|
||||
#else
|
||||
#define SPOT_API SPOT_HELPER_DLL_IMPORT
|
||||
#endif
|
||||
#define SPOT_LOCAL SPOT_HELPER_DLL_LOCAL
|
||||
#else
|
||||
#define SPOT_API
|
||||
#define SPOT_LOCAL
|
||||
#endif
|
||||
#define SPOT_API_VAR extern SPOT_API
|
||||
|
||||
|
||||
// Swig 3.0.2 does not understand 'final' when used
|
||||
// at class definition.
|
||||
#ifdef SWIG
|
||||
#define final
|
||||
#endif
|
||||
|
||||
|
||||
// Do not use those in code, prefer SPOT_UNREACHABLE() instead.
|
||||
#if defined __clang__ || defined __GNU__
|
||||
# define SPOT_UNREACHABLE_BUILTIN() __builtin_unreachable()
|
||||
# elif defined _MSC_VER
|
||||
# define SPOT_UNREACHABLE_BUILTIN() __assume(0)
|
||||
# else
|
||||
# define SPOT_UNREACHABLE_BUILTIN() abort()
|
||||
#endif
|
||||
|
||||
// The extra parentheses in assert() is so that this
|
||||
// pattern is not caught by the style checker.
|
||||
#define SPOT_UNREACHABLE() do { \
|
||||
assert(!("unreachable code reached")); \
|
||||
SPOT_UNREACHABLE_BUILTIN(); \
|
||||
} while (0)
|
||||
|
||||
#define SPOT_UNIMPLEMENTED() throw std::runtime_error("unimplemented");
|
||||
|
||||
|
||||
// Useful when forwarding methods such as:
|
||||
// auto func(int param) SPOT_RETURN(implem_.func(param));
|
||||
#define SPOT_RETURN(code) -> decltype(code) { return code; }
|
||||
|
||||
|
||||
namespace spot
|
||||
{
|
||||
struct SPOT_API parse_error: public std::runtime_error
|
||||
{
|
||||
parse_error(const std::string& s)
|
||||
: std::runtime_error(s)
|
||||
{
|
||||
}
|
||||
};
|
||||
}
|
||||
162
spot/misc/escape.cc
Normal file
162
spot/misc/escape.cc
Normal file
|
|
@ -0,0 +1,162 @@
|
|||
// -*- coding: utf-8 -*-
|
||||
// Copyright (C) 2012, 2013, 2015 Laboratoire de Recherche et Developpement de
|
||||
// l'Epita (LRDE)
|
||||
// Copyright (C) 2004 Laboratoire d'Informatique de Paris 6 (LIP6),
|
||||
// département Systèmes Répartis Coopératifs (SRC), Université Pierre
|
||||
// et Marie Curie.
|
||||
//
|
||||
// This file is part of Spot, a model checking library.
|
||||
//
|
||||
// Spot is free software; you can redistribute it and/or modify it
|
||||
// under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation; either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// Spot is distributed in the hope that it will be useful, but WITHOUT
|
||||
// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
||||
// or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public
|
||||
// License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
#include "config.h"
|
||||
#include <sstream>
|
||||
#include <ostream>
|
||||
#include <cstring>
|
||||
#include <spot/misc/escape.hh>
|
||||
|
||||
namespace spot
|
||||
{
|
||||
std::ostream&
|
||||
escape_rfc4180(std::ostream& os, const std::string& str)
|
||||
{
|
||||
for (auto i: str)
|
||||
switch (i)
|
||||
{
|
||||
case '"':
|
||||
os << "\"\"";
|
||||
break;
|
||||
default:
|
||||
os << i;
|
||||
break;
|
||||
}
|
||||
return os;
|
||||
}
|
||||
|
||||
std::ostream&
|
||||
escape_latex(std::ostream& os, const std::string& str)
|
||||
{
|
||||
for (auto i: str)
|
||||
switch (i)
|
||||
{
|
||||
case '~':
|
||||
os << "\\text{\\textasciitilde}";
|
||||
break;
|
||||
case '^':
|
||||
os << "\\text{\\textasciicircum}";
|
||||
break;
|
||||
case '\\':
|
||||
os << "\\text{\\textbackslash}";
|
||||
break;
|
||||
case '&':
|
||||
case '%':
|
||||
case '$':
|
||||
case '#':
|
||||
case '_':
|
||||
case '{':
|
||||
case '}':
|
||||
os << '\\';
|
||||
default:
|
||||
os << i;
|
||||
break;
|
||||
}
|
||||
return os;
|
||||
}
|
||||
|
||||
std::ostream&
|
||||
escape_html(std::ostream& os, const std::string& str)
|
||||
{
|
||||
for (auto i: str)
|
||||
switch (i)
|
||||
{
|
||||
case '&':
|
||||
os << "&";
|
||||
break;
|
||||
case '"':
|
||||
os << """;
|
||||
break;
|
||||
case '<':
|
||||
os << "<";
|
||||
break;
|
||||
case '>':
|
||||
os << ">";
|
||||
break;
|
||||
default:
|
||||
os << i;
|
||||
break;
|
||||
}
|
||||
return os;
|
||||
}
|
||||
|
||||
std::ostream&
|
||||
escape_str(std::ostream& os, const std::string& str)
|
||||
{
|
||||
for (auto i: str)
|
||||
switch (i)
|
||||
{
|
||||
case '\\':
|
||||
os << "\\\\";
|
||||
break;
|
||||
case '"':
|
||||
os << "\\\"";
|
||||
break;
|
||||
case '\n':
|
||||
os << "\\n";
|
||||
break;
|
||||
default:
|
||||
os << i;
|
||||
break;
|
||||
}
|
||||
return os;
|
||||
}
|
||||
|
||||
std::string
|
||||
escape_str(const std::string& str)
|
||||
{
|
||||
std::ostringstream os;
|
||||
escape_str(os, str);
|
||||
return os.str();
|
||||
}
|
||||
|
||||
std::ostream&
|
||||
quote_shell_string(std::ostream& os, const char* str)
|
||||
{
|
||||
// Single quotes are best, unless the string to quote contains one.
|
||||
if (!strchr(str, '\''))
|
||||
{
|
||||
os << '\'' << str << '\'';
|
||||
}
|
||||
else
|
||||
{
|
||||
// In double quotes we have to escape $ ` " or \.
|
||||
os << '"';
|
||||
while (*str)
|
||||
switch (*str)
|
||||
{
|
||||
case '$':
|
||||
case '`':
|
||||
case '"':
|
||||
case '\\':
|
||||
os << '\\';
|
||||
// fall through
|
||||
default:
|
||||
os << *str++;
|
||||
break;
|
||||
}
|
||||
os << '"';
|
||||
}
|
||||
return os;
|
||||
}
|
||||
|
||||
}
|
||||
73
spot/misc/escape.hh
Normal file
73
spot/misc/escape.hh
Normal file
|
|
@ -0,0 +1,73 @@
|
|||
// -*- coding: utf-8 -*-
|
||||
// Copyright (C) 2011, 2012, 2013, 2015 Laboratoire de Recherche et
|
||||
// Developpement de l'Epita (LRDE).
|
||||
// Copyright (C) 2004 Laboratoire d'Informatique de Paris 6 (LIP6),
|
||||
// département Systèmes Répartis Coopératifs (SRC), Université Pierre
|
||||
// et Marie Curie.
|
||||
//
|
||||
// This file is part of Spot, a model checking library.
|
||||
//
|
||||
// Spot is free software; you can redistribute it and/or modify it
|
||||
// under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation; either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// Spot is distributed in the hope that it will be useful, but WITHOUT
|
||||
// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
||||
// or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public
|
||||
// License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <spot/misc/common.hh>
|
||||
#include <iosfwd>
|
||||
#include <string>
|
||||
|
||||
namespace spot
|
||||
{
|
||||
/// \ingroup misc_tools
|
||||
/// @{
|
||||
|
||||
/// \brief Double characters <code>"</code> in strings.
|
||||
///
|
||||
/// In CSV files, as defined by RFC4180, double-quoted string that
|
||||
/// contain double-quotes should simply duplicate those quotes.
|
||||
SPOT_API std::ostream&
|
||||
escape_rfc4180(std::ostream& os, const std::string& str);
|
||||
|
||||
/// \brief Escape special LaTeX characters.
|
||||
///
|
||||
/// The following characters are rewritten:
|
||||
/// <code>& % $ # _ { } ~ ^ \\</code>
|
||||
SPOT_API std::ostream&
|
||||
escape_latex(std::ostream& os, const std::string& str);
|
||||
|
||||
/// \brief Escape special HTML characters.
|
||||
///
|
||||
/// The following characters are rewritten:
|
||||
/// <code>> < " &</code>
|
||||
SPOT_API std::ostream&
|
||||
escape_html(std::ostream& os, const std::string& str);
|
||||
|
||||
/// \brief Escape characters <code>"</code>, <code>\\</code>, and
|
||||
/// <code>\\n</code> in \a str.
|
||||
SPOT_API std::ostream&
|
||||
escape_str(std::ostream& os, const std::string& str);
|
||||
|
||||
/// \brief Escape characters <code>"</code>, <code>\\</code>, and
|
||||
/// <code>\\n</code> in \a str.
|
||||
SPOT_API std::string
|
||||
escape_str(const std::string& str);
|
||||
|
||||
/// \brief Output \a str between simple quote or double quotes
|
||||
///
|
||||
/// Simple quotes are preferred unless \a str contains some simple
|
||||
/// quotes. In that case we use double quotes and escape anything
|
||||
/// that needs to be escaped.
|
||||
SPOT_API std::ostream&
|
||||
quote_shell_string(std::ostream& os, const char* str);
|
||||
/// @}
|
||||
}
|
||||
112
spot/misc/fixpool.hh
Normal file
112
spot/misc/fixpool.hh
Normal file
|
|
@ -0,0 +1,112 @@
|
|||
// -*- coding: utf-8 -*-
|
||||
// Copyright (C) 2011, 2015 Laboratoire de Recherche et Développement
|
||||
// de l'Epita (LRDE)
|
||||
//
|
||||
// This file is part of Spot, a model checking library.
|
||||
//
|
||||
// Spot is free software; you can redistribute it and/or modify it
|
||||
// under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation; either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// Spot is distributed in the hope that it will be useful, but WITHOUT
|
||||
// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
||||
// or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public
|
||||
// License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <new>
|
||||
#include <cstddef>
|
||||
#include <cstdlib>
|
||||
#include <cassert>
|
||||
|
||||
namespace spot
|
||||
{
|
||||
|
||||
/// A fixed-size memory pool implementation.
|
||||
class fixed_size_pool
|
||||
{
|
||||
public:
|
||||
/// Create a pool allocating objects of \a size bytes.
|
||||
fixed_size_pool(size_t size)
|
||||
: freelist_(nullptr), free_start_(nullptr),
|
||||
free_end_(nullptr), chunklist_(nullptr)
|
||||
{
|
||||
const size_t alignement = 2 * sizeof(size_t);
|
||||
size_ = ((size >= sizeof(block_) ? size : sizeof(block_))
|
||||
+ alignement - 1) & ~(alignement - 1);
|
||||
}
|
||||
|
||||
/// Free any memory allocated by this pool.
|
||||
~fixed_size_pool()
|
||||
{
|
||||
while (chunklist_)
|
||||
{
|
||||
chunk_* prev = chunklist_->prev;
|
||||
free(chunklist_);
|
||||
chunklist_ = prev;
|
||||
}
|
||||
}
|
||||
|
||||
/// Allocate \a size bytes of memory.
|
||||
void*
|
||||
allocate()
|
||||
{
|
||||
block_* f = freelist_;
|
||||
// If we have free blocks available, return the first one.
|
||||
if (f)
|
||||
{
|
||||
freelist_ = f->next;
|
||||
return f;
|
||||
}
|
||||
|
||||
// Else, create a block out of the last chunk of allocated
|
||||
// memory.
|
||||
|
||||
// If all the last chunk has been used, allocate one more.
|
||||
if (free_start_ + size_ > free_end_)
|
||||
{
|
||||
const size_t requested = (size_ > 128 ? size_ : 128) * 8192 - 64;
|
||||
chunk_* c = reinterpret_cast<chunk_*>(malloc(requested));
|
||||
if (!c)
|
||||
throw std::bad_alloc();
|
||||
c->prev = chunklist_;
|
||||
chunklist_ = c;
|
||||
|
||||
free_start_ = c->data_ + size_;
|
||||
free_end_ = c->data_ + requested;
|
||||
}
|
||||
|
||||
void* res = free_start_;
|
||||
free_start_ += size_;
|
||||
return res;
|
||||
}
|
||||
|
||||
/// \brief Recycle \a size bytes of memory.
|
||||
///
|
||||
/// Despite the name, the memory is not really deallocated in the
|
||||
/// "delete" sense: it is still owned by the pool and will be
|
||||
/// reused by allocate as soon as possible. The memory is only
|
||||
/// freed when the pool is destroyed.
|
||||
void
|
||||
deallocate (const void* ptr)
|
||||
{
|
||||
assert(ptr);
|
||||
block_* b = reinterpret_cast<block_*>(const_cast<void*>(ptr));
|
||||
b->next = freelist_;
|
||||
freelist_ = b;
|
||||
}
|
||||
|
||||
private:
|
||||
size_t size_;
|
||||
struct block_ { block_* next; }* freelist_;
|
||||
char* free_start_;
|
||||
char* free_end_;
|
||||
// chunk = several agglomerated blocks
|
||||
union chunk_ { chunk_* prev; char data_[1]; }* chunklist_;
|
||||
};
|
||||
}
|
||||
62
spot/misc/formater.cc
Normal file
62
spot/misc/formater.cc
Normal file
|
|
@ -0,0 +1,62 @@
|
|||
// -*- coding: utf-8 -*-
|
||||
// Copyright (C) 2012, 2013 Laboratoire de Recherche et Développement
|
||||
// de l'Epita (LRDE).
|
||||
//
|
||||
// This file is part of Spot, a model checking library.
|
||||
//
|
||||
// Spot is free software; you can redistribute it and/or modify it
|
||||
// under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation; either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// Spot is distributed in the hope that it will be useful, but WITHOUT
|
||||
// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
||||
// or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public
|
||||
// License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
#include "config.h"
|
||||
#include <spot/misc/formater.hh>
|
||||
#include <iostream>
|
||||
|
||||
namespace spot
|
||||
{
|
||||
void
|
||||
formater::scan(const char* fmt, std::vector<bool>& has) const
|
||||
{
|
||||
for (const char* pos = fmt; *pos; ++pos)
|
||||
if (*pos == '%')
|
||||
{
|
||||
char c = *++pos;
|
||||
has[c] = true;
|
||||
if (!c)
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
formater::prime(const char* fmt)
|
||||
{
|
||||
scan(fmt, has_);
|
||||
}
|
||||
|
||||
std::ostream&
|
||||
formater::format(const char* fmt)
|
||||
{
|
||||
for (const char* pos = fmt; *pos; ++pos)
|
||||
if (*pos != '%')
|
||||
{
|
||||
*output_ << *pos;
|
||||
}
|
||||
else
|
||||
{
|
||||
char c = *++pos;
|
||||
call_[c]->print(*output_, pos);
|
||||
if (!c)
|
||||
break;
|
||||
}
|
||||
return *output_;
|
||||
}
|
||||
}
|
||||
200
spot/misc/formater.hh
Normal file
200
spot/misc/formater.hh
Normal file
|
|
@ -0,0 +1,200 @@
|
|||
// -*- coding: utf-8 -*-
|
||||
// Copyright (C) 2012, 2013 Laboratoire de Recherche et Développement
|
||||
// de l'Epita (LRDE).
|
||||
//
|
||||
// This file is part of Spot, a model checking library.
|
||||
//
|
||||
// Spot is free software; you can redistribute it and/or modify it
|
||||
// under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation; either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// Spot is distributed in the hope that it will be useful, but WITHOUT
|
||||
// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
||||
// or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public
|
||||
// License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <spot/misc/common.hh>
|
||||
#include <iostream>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
namespace spot
|
||||
{
|
||||
class printable
|
||||
{
|
||||
public:
|
||||
virtual ~printable()
|
||||
{
|
||||
}
|
||||
|
||||
virtual void
|
||||
print(std::ostream&, const char*) const = 0;
|
||||
};
|
||||
|
||||
|
||||
template <class T>
|
||||
class printable_value: public printable
|
||||
{
|
||||
protected:
|
||||
T val_;
|
||||
public:
|
||||
const T& val() const
|
||||
{
|
||||
return val_;
|
||||
}
|
||||
|
||||
T& val()
|
||||
{
|
||||
return val_;
|
||||
}
|
||||
|
||||
operator const T&() const
|
||||
{
|
||||
return val();
|
||||
}
|
||||
|
||||
operator T&()
|
||||
{
|
||||
return val();
|
||||
}
|
||||
|
||||
printable_value&
|
||||
operator=(const T& new_val)
|
||||
{
|
||||
val_ = new_val;
|
||||
return *this;
|
||||
}
|
||||
|
||||
virtual void
|
||||
print(std::ostream& os, const char*) const
|
||||
{
|
||||
os << val_;
|
||||
}
|
||||
};
|
||||
|
||||
/// The default callback simply writes "%c".
|
||||
class printable_id: public printable
|
||||
{
|
||||
public:
|
||||
virtual void
|
||||
print(std::ostream& os, const char* x) const
|
||||
{
|
||||
os << '%' << *x;
|
||||
}
|
||||
};
|
||||
|
||||
/// Called by default for "%%" and "%\0".
|
||||
class printable_percent: public printable
|
||||
{
|
||||
public:
|
||||
virtual void
|
||||
print(std::ostream& os, const char*) const
|
||||
{
|
||||
os << '%';
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
class SPOT_API formater
|
||||
{
|
||||
printable_id id;
|
||||
printable_percent percent;
|
||||
public:
|
||||
|
||||
formater()
|
||||
: has_(256), call_(256, &id)
|
||||
{
|
||||
call_['%'] = call_[0] = &percent;
|
||||
}
|
||||
|
||||
virtual ~formater()
|
||||
{
|
||||
}
|
||||
|
||||
/// \brief Scan the %-sequences occuring in \a fmt.
|
||||
///
|
||||
/// Set has['c'] for each %c in \a fmt. \a has must
|
||||
/// be 256 wide.
|
||||
/// @{
|
||||
void
|
||||
scan(const char* fmt, std::vector<bool>& has) const;
|
||||
|
||||
void
|
||||
scan(const std::string& fmt, std::vector<bool>& has) const
|
||||
{
|
||||
scan(fmt.c_str(), has);
|
||||
}
|
||||
/// @}
|
||||
|
||||
/// Collect the %-sequences occurring in \a fmt.
|
||||
/// @{
|
||||
void
|
||||
prime(const char* fmt);
|
||||
|
||||
void
|
||||
prime(const std::string& fmt)
|
||||
{
|
||||
prime(fmt.c_str());
|
||||
}
|
||||
/// @}
|
||||
|
||||
/// Whether %c occurred in the primed formats.
|
||||
bool
|
||||
has(char c) const
|
||||
{
|
||||
return has_[c];
|
||||
}
|
||||
|
||||
/// Declare a callback function for %c.
|
||||
void
|
||||
declare(char c, const printable* f)
|
||||
{
|
||||
call_[c] = f;
|
||||
}
|
||||
|
||||
/// Remember where to output any string.
|
||||
void
|
||||
set_output(std::ostream& output)
|
||||
{
|
||||
output_ = &output;
|
||||
}
|
||||
|
||||
/// Expand the %-sequences in \a fmt, write the result on \a output_.
|
||||
std::ostream&
|
||||
format(const char* fmt);
|
||||
|
||||
/// Expand the %-sequences in \a fmt, write the result on \a output.
|
||||
std::ostream&
|
||||
format(std::ostream& output, const char* fmt)
|
||||
{
|
||||
set_output(output);
|
||||
return format(fmt);
|
||||
}
|
||||
|
||||
/// Expand the %-sequences in \a fmt, write the result on \a output_.
|
||||
std::ostream&
|
||||
format(const std::string& fmt)
|
||||
{
|
||||
return format(fmt.c_str());
|
||||
}
|
||||
|
||||
/// Expand the %-sequences in \a fmt, write the result on \a output.
|
||||
std::ostream&
|
||||
format(std::ostream& output, const std::string& fmt)
|
||||
{
|
||||
return format(output, fmt.c_str());
|
||||
}
|
||||
|
||||
private:
|
||||
std::vector<bool> has_;
|
||||
std::vector<const printable*> call_;
|
||||
protected:
|
||||
std::ostream* output_;
|
||||
};
|
||||
}
|
||||
91
spot/misc/hash.hh
Normal file
91
spot/misc/hash.hh
Normal file
|
|
@ -0,0 +1,91 @@
|
|||
// -*- coding: utf-8 -*-
|
||||
// Copyright (C) 2008, 2011, 2014, 2015 Laboratoire de Recherche et
|
||||
// Développement de l'Epita (LRDE).
|
||||
// Copyright (C) 2003, 2004, 2005 Laboratoire d'Informatique de
|
||||
// Paris 6 (LIP6), département Systèmes Répartis Coopératifs (SRC),
|
||||
// Université Pierre et Marie Curie.
|
||||
//
|
||||
// This file is part of Spot, a model checking library.
|
||||
//
|
||||
// Spot is free software; you can redistribute it and/or modify it
|
||||
// under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation; either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// Spot is distributed in the hope that it will be useful, but WITHOUT
|
||||
// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
||||
// or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public
|
||||
// License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
#include <functional>
|
||||
#include <spot/misc/hashfunc.hh>
|
||||
#include <spot/misc/_config.h>
|
||||
|
||||
#include <unordered_map>
|
||||
#include <unordered_set>
|
||||
|
||||
namespace spot
|
||||
{
|
||||
|
||||
/// \ingroup hash_funcs
|
||||
/// \brief A hash function for pointers.
|
||||
template <class T>
|
||||
struct ptr_hash :
|
||||
public std::unary_function<const T*, size_t>
|
||||
{
|
||||
// A default constructor is needed if the ptr_hash object is
|
||||
// stored in a const member. This occur with the clang version
|
||||
// installed by OS X 10.9.
|
||||
ptr_hash()
|
||||
{
|
||||
}
|
||||
|
||||
size_t operator()(const T* p) const
|
||||
{
|
||||
return knuth32_hash(reinterpret_cast<const char*>(p)
|
||||
- static_cast<const char*>(nullptr));
|
||||
}
|
||||
};
|
||||
|
||||
/// \ingroup hash_funcs
|
||||
/// \brief A hash function for strings.
|
||||
typedef std::hash<std::string> string_hash;
|
||||
|
||||
/// \ingroup hash_funcs
|
||||
/// \brief A hash function that returns identity
|
||||
template<typename T>
|
||||
struct identity_hash:
|
||||
public std::unary_function<const T&, size_t>
|
||||
{
|
||||
// A default constructor is needed if the string_hash object is
|
||||
// stored in a const member.
|
||||
identity_hash()
|
||||
{
|
||||
}
|
||||
|
||||
size_t operator()(const T& s) const
|
||||
{
|
||||
return s;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
struct pair_hash
|
||||
{
|
||||
template<typename T, typename U>
|
||||
std::size_t operator()(const std::pair<T, U> &p) const
|
||||
{
|
||||
std::hash<T> th;
|
||||
std::hash<U> uh;
|
||||
|
||||
return wang32_hash(static_cast<size_t>(th(p.first)) ^
|
||||
static_cast<size_t>(uh(p.second)));
|
||||
}
|
||||
};
|
||||
}
|
||||
66
spot/misc/hashfunc.hh
Normal file
66
spot/misc/hashfunc.hh
Normal file
|
|
@ -0,0 +1,66 @@
|
|||
// -*- coding: utf-8 -*-
|
||||
// Copyright (C) 2015 Laboratoire de Recherche et Développement
|
||||
// de l'Epita (LRDE)
|
||||
// Copyright (C) 2004, 2005 Laboratoire d'Informatique de Paris 6 (LIP6),
|
||||
// département Systèmes Répartis Coopératifs (SRC), Université Pierre
|
||||
// et Marie Curie.
|
||||
//
|
||||
// This file is part of Spot, a model checking library.
|
||||
//
|
||||
// Spot is free software; you can redistribute it and/or modify it
|
||||
// under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation; either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// Spot is distributed in the hope that it will be useful, but WITHOUT
|
||||
// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
||||
// or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public
|
||||
// License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <cstddef>
|
||||
|
||||
namespace spot
|
||||
{
|
||||
/// \defgroup hash_funcs Hashing functions
|
||||
/// \ingroup misc_tools
|
||||
|
||||
/// \ingroup hash_funcs
|
||||
/// @{
|
||||
|
||||
/// \brief Thomas Wang's 32 bit hash function.
|
||||
///
|
||||
/// Hash an integer amongst the integers.
|
||||
/// http://web.archive.org/web/2011/concentric.net/~Ttwang/tech/inthash.htm
|
||||
inline size_t
|
||||
wang32_hash(size_t key)
|
||||
{
|
||||
// We assume that size_t has at least 32bits.
|
||||
key += ~(key << 15);
|
||||
key ^= (key >> 10);
|
||||
key += (key << 3);
|
||||
key ^= (key >> 6);
|
||||
key += ~(key << 11);
|
||||
key ^= (key >> 16);
|
||||
return key;
|
||||
}
|
||||
|
||||
/// \brief Knuth's Multiplicative hash function.
|
||||
///
|
||||
/// This function is suitable for hashing values whose
|
||||
/// high order bits do not vary much (ex. addresses of
|
||||
/// memory objects). Prefer spot::wang32_hash() otherwise.
|
||||
/// http://web.archive.org/web/2011/concentric.net/~Ttwang/tech/addrhash.htm
|
||||
inline size_t
|
||||
knuth32_hash(size_t key)
|
||||
{
|
||||
// 2654435761 is the golden ratio of 2^32. The right shift of 3
|
||||
// bits assumes that all objects are aligned on a 8 byte boundary.
|
||||
return (key >> 3) * 2654435761U;
|
||||
}
|
||||
/// @}
|
||||
}
|
||||
551
spot/misc/intvcmp2.cc
Normal file
551
spot/misc/intvcmp2.cc
Normal file
|
|
@ -0,0 +1,551 @@
|
|||
// -*- coding: utf-8 -*-
|
||||
// Copyright (C) 2011, 2013, 2014, 2015 Laboratoire de Recherche et
|
||||
// Développement de l'Epita (LRDE).
|
||||
//
|
||||
// This file is part of Spot, a model checking library.
|
||||
//
|
||||
// Spot is free software; you can redistribute it and/or modify it
|
||||
// under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation; either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// Spot is distributed in the hope that it will be useful, but WITHOUT
|
||||
// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
||||
// or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public
|
||||
// License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
#include "config.h"
|
||||
#include <spot/misc/common.hh>
|
||||
#include <cstddef>
|
||||
#include <cassert>
|
||||
#include <spot/misc/intvcmp2.hh>
|
||||
|
||||
namespace spot
|
||||
{
|
||||
namespace
|
||||
{
|
||||
// This implements integer compression inspired from "Simple-9".
|
||||
//
|
||||
// The first bits of an integer tell how the rest of the integer is coded:
|
||||
// 00: 30 1-bit values id=0
|
||||
// 01: 10 3-bit values id=1
|
||||
// 10: 6 5-bit values id=2
|
||||
// 1100: 4 7-bit values id=3
|
||||
// 1101: 3 9-bit values (1 bit lost) id=4
|
||||
// 1110: 2 14-bit values id=5
|
||||
// 1111: 1 28-bit value id=6
|
||||
|
||||
template <class Self>
|
||||
class stream_compression_base
|
||||
{
|
||||
public:
|
||||
stream_compression_base(size_t size)
|
||||
: size_(size)
|
||||
{
|
||||
}
|
||||
|
||||
void run()
|
||||
{
|
||||
static const unsigned bits_width[7] = { 1, 3, 5, 7, 9, 14, 28 };
|
||||
static const unsigned max_count[8] = { 30, 10, 6, 4, 3, 2, 1, 0 };
|
||||
static const unsigned max_allowed[8] = { 1,
|
||||
(1 << 3) - 1,
|
||||
(1 << 5) - 1,
|
||||
(1 << 7) - 1,
|
||||
(1 << 9) - 1,
|
||||
(1 << 14) - 1,
|
||||
(1 << 28) - 1,
|
||||
-1U };
|
||||
// If we have only X data to compress and they fit with the
|
||||
// current bit width, the following table tells us we should
|
||||
// use bits_width[count_to_level[X - 1]] to limit the number
|
||||
// of trailing zeros we encode. E.g. count_to_level[5 - 1]
|
||||
// is 2, which mean that 5 values should be encoded with
|
||||
// bits_width[2] == 5 bits.
|
||||
static const unsigned count_to_level[30] =
|
||||
{
|
||||
6, // 1
|
||||
5, // 2
|
||||
4, // 3
|
||||
3, // 4
|
||||
2, // 5
|
||||
2, // 6
|
||||
1, // 7
|
||||
1, // 8
|
||||
1, // 9
|
||||
1, // 10
|
||||
0, 0, 0, 0, 0, // 11-15
|
||||
0, 0, 0, 0, 0, // 16-20
|
||||
0, 0, 0, 0, 0, // 21-25
|
||||
0, 0, 0, 0, 0, // 26-30
|
||||
};
|
||||
|
||||
while (size_ > 0)
|
||||
{
|
||||
unsigned id = 0; // Current level in the above two tables.
|
||||
unsigned curmax_allowed = max_allowed[id];
|
||||
unsigned compressable = 0; // Number of integers ready to pack.
|
||||
do
|
||||
{
|
||||
unsigned int val = self().data_at(compressable);
|
||||
++compressable;
|
||||
while (val > curmax_allowed)
|
||||
{
|
||||
curmax_allowed = max_allowed[++id];
|
||||
|
||||
if (compressable > max_count[id])
|
||||
goto fast_encode;
|
||||
}
|
||||
if (compressable >= max_count[id])
|
||||
goto fast_encode;
|
||||
}
|
||||
while (SPOT_LIKELY(compressable < size_));
|
||||
|
||||
assert(compressable < max_count[id]);
|
||||
|
||||
// Since we have less data than the current "id" allows,
|
||||
// try to use more bits so we can encode faster.
|
||||
|
||||
id = count_to_level[compressable - 1];
|
||||
|
||||
if (compressable == max_count[id])
|
||||
goto fast_encode;
|
||||
|
||||
// Slow compression for situations where we have
|
||||
// compressable < max_count[id]. We can only be in
|
||||
// one of the 3 first "id" (1, 3, or 5 bits);
|
||||
{
|
||||
assert(id <= 2);
|
||||
unsigned bits = bits_width[id];
|
||||
unsigned finalshifts = (max_count[id] - compressable) * bits;
|
||||
size_t pos = 0;
|
||||
unsigned output = self().data_at(pos);
|
||||
while (--compressable)
|
||||
{
|
||||
output <<= bits;
|
||||
output += self().data_at(++pos);
|
||||
}
|
||||
output <<= finalshifts;
|
||||
output += id << 30;
|
||||
self().push_data(output);
|
||||
return;
|
||||
}
|
||||
|
||||
fast_encode:
|
||||
switch (id)
|
||||
{
|
||||
case 0: // 30 1-bit values
|
||||
{
|
||||
// This code has been tuned so that the compiler can
|
||||
// efficiently encode it as a series of MOV+LEA
|
||||
// instructions, without shifts. For instance
|
||||
//
|
||||
// output <<= 1;
|
||||
// output += self().data_at(4);
|
||||
//
|
||||
// translates to (assuming %eax points to the input,
|
||||
// and %edx holds the output) the following:
|
||||
//
|
||||
// mov ecx, [eax+16]
|
||||
// lea edx, [ecx+edx*2]
|
||||
//
|
||||
// This optimization is the reason why we use 'output +='
|
||||
// instead of the more intuitive 'output |=' everywhere in
|
||||
// this file.
|
||||
|
||||
unsigned int output = 0x00 << 1; // 00
|
||||
output += self().data_at(0);
|
||||
output <<= 1;
|
||||
output += self().data_at(1);
|
||||
output <<= 1;
|
||||
output += self().data_at(2);
|
||||
output <<= 1;
|
||||
output += self().data_at(3);
|
||||
output <<= 1;
|
||||
output += self().data_at(4);
|
||||
output <<= 1;
|
||||
output += self().data_at(5);
|
||||
output <<= 1;
|
||||
output += self().data_at(6);
|
||||
output <<= 1;
|
||||
output += self().data_at(7);
|
||||
output <<= 1;
|
||||
output += self().data_at(8);
|
||||
output <<= 1;
|
||||
output += self().data_at(9);
|
||||
output <<= 1;
|
||||
output += self().data_at(10);
|
||||
output <<= 1;
|
||||
output += self().data_at(11);
|
||||
output <<= 1;
|
||||
output += self().data_at(12);
|
||||
output <<= 1;
|
||||
output += self().data_at(13);
|
||||
output <<= 1;
|
||||
output += self().data_at(14);
|
||||
output <<= 1;
|
||||
output += self().data_at(15);
|
||||
output <<= 1;
|
||||
output += self().data_at(16);
|
||||
output <<= 1;
|
||||
output += self().data_at(17);
|
||||
output <<= 1;
|
||||
output += self().data_at(18);
|
||||
output <<= 1;
|
||||
output += self().data_at(19);
|
||||
output <<= 1;
|
||||
output += self().data_at(20);
|
||||
output <<= 1;
|
||||
output += self().data_at(21);
|
||||
output <<= 1;
|
||||
output += self().data_at(22);
|
||||
output <<= 1;
|
||||
output += self().data_at(23);
|
||||
output <<= 1;
|
||||
output += self().data_at(24);
|
||||
output <<= 1;
|
||||
output += self().data_at(25);
|
||||
output <<= 1;
|
||||
output += self().data_at(26);
|
||||
output <<= 1;
|
||||
output += self().data_at(27);
|
||||
output <<= 1;
|
||||
output += self().data_at(28);
|
||||
output <<= 1;
|
||||
output += self().data_at(29);
|
||||
self().push_data(output);
|
||||
}
|
||||
break;
|
||||
case 1: // 10 3-bit values
|
||||
{
|
||||
// This code has been tuned so that the compiler can
|
||||
// efficiently encode it as a series of MOV+LEA
|
||||
// instructions, without shifts. For instance
|
||||
//
|
||||
// output <<= 3;
|
||||
// output += self().data_at(4);
|
||||
//
|
||||
// translates to (assuming %eax points to the input,
|
||||
// and %edx holds the output) the following:
|
||||
//
|
||||
// mov ecx, [eax+16]
|
||||
// lea edx, [ecx+edx*8]
|
||||
|
||||
unsigned int output = 0x01 << 3; // 01
|
||||
output += self().data_at(0);
|
||||
output <<= 3;
|
||||
output += self().data_at(1);
|
||||
output <<= 3;
|
||||
output += self().data_at(2);
|
||||
output <<= 3;
|
||||
output += self().data_at(3);
|
||||
output <<= 3;
|
||||
output += self().data_at(4);
|
||||
output <<= 3;
|
||||
output += self().data_at(5);
|
||||
output <<= 3;
|
||||
output += self().data_at(6);
|
||||
output <<= 3;
|
||||
output += self().data_at(7);
|
||||
output <<= 3;
|
||||
output += self().data_at(8);
|
||||
output <<= 3;
|
||||
output += self().data_at(9);
|
||||
self().push_data(output);
|
||||
}
|
||||
break;
|
||||
case 2: // 6 5-bit values
|
||||
{
|
||||
unsigned int output = 0x02U << 30; // 10
|
||||
output += self().data_at(0) << 25;
|
||||
output += self().data_at(1) << 20;
|
||||
output += self().data_at(2) << 15;
|
||||
output += self().data_at(3) << 10;
|
||||
output += self().data_at(4) << 5;
|
||||
output += self().data_at(5);
|
||||
self().push_data(output);
|
||||
}
|
||||
break;
|
||||
case 3: // 4 7-bit values
|
||||
{
|
||||
unsigned int output = 0x0CU << 28; // 1100
|
||||
output += self().data_at(0) << 21;
|
||||
output += self().data_at(1) << 14;
|
||||
output += self().data_at(2) << 7;
|
||||
output += self().data_at(3);
|
||||
self().push_data(output);
|
||||
}
|
||||
break;
|
||||
case 4: // 3 9-bit values
|
||||
{
|
||||
unsigned int output = 0x0DU << 28; // 1101x (1 bit lost)
|
||||
output += self().data_at(0) << 18;
|
||||
output += self().data_at(1) << 9;
|
||||
output += self().data_at(2);
|
||||
self().push_data(output);
|
||||
}
|
||||
break;
|
||||
case 5: // 2 14-bit values
|
||||
{
|
||||
unsigned int output = 0x0EU << 28; // 1110
|
||||
output += self().data_at(0) << 14;
|
||||
output += self().data_at(1);
|
||||
self().push_data(output);
|
||||
}
|
||||
break;
|
||||
case 6: // one 28-bit value
|
||||
{
|
||||
unsigned int output = 0x0FU << 28; // 1111
|
||||
output += self().data_at(0);
|
||||
self().push_data(output);
|
||||
}
|
||||
break;
|
||||
}
|
||||
self().forward(max_count[id]);
|
||||
size_ -= max_count[id];
|
||||
}
|
||||
}
|
||||
|
||||
protected:
|
||||
|
||||
size_t size_;
|
||||
|
||||
Self& self()
|
||||
{
|
||||
return static_cast<Self&>(*this);
|
||||
}
|
||||
|
||||
const Self& self() const
|
||||
{
|
||||
return static_cast<const Self&>(*this);
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
|
||||
class int_array_array_compression:
|
||||
public stream_compression_base<int_array_array_compression>
|
||||
{
|
||||
public:
|
||||
int_array_array_compression(const int* array, size_t n,
|
||||
int* dest, size_t& dest_n)
|
||||
: stream_compression_base<int_array_array_compression>(n),
|
||||
array_(array), result_size_(dest_n),
|
||||
result_(dest), result_end_(dest + dest_n)
|
||||
{
|
||||
result_size_ = 0; // this resets dest_n.
|
||||
}
|
||||
|
||||
void push_data(unsigned int i)
|
||||
{
|
||||
assert(result_ < result_end_);
|
||||
++result_size_;
|
||||
*result_++ = static_cast<int>(i);
|
||||
}
|
||||
|
||||
unsigned int data_at(size_t offset)
|
||||
{
|
||||
return static_cast<unsigned int>(array_[offset]);
|
||||
}
|
||||
|
||||
void forward(size_t offset)
|
||||
{
|
||||
array_ += offset;
|
||||
}
|
||||
|
||||
protected:
|
||||
const int* array_;
|
||||
size_t& result_size_;
|
||||
int* result_;
|
||||
int* result_end_;
|
||||
};
|
||||
|
||||
} // anonymous
|
||||
|
||||
|
||||
void
|
||||
int_array_array_compress2(const int* array, size_t n,
|
||||
int* dest, size_t& dest_size)
|
||||
{
|
||||
int_array_array_compression c(array, n, dest, dest_size);
|
||||
c.run();
|
||||
}
|
||||
|
||||
|
||||
|
||||
namespace
|
||||
{
|
||||
|
||||
template<class Self>
|
||||
class stream_decompression_base
|
||||
{
|
||||
public:
|
||||
|
||||
void run()
|
||||
{
|
||||
while (SPOT_LIKELY(self().have_comp_data()))
|
||||
{
|
||||
unsigned val = self().next_comp_data();
|
||||
|
||||
unsigned id = val >> 28;
|
||||
switch (id)
|
||||
{
|
||||
case 0x00: // 00xx - 30 1-bit values.
|
||||
case 0x01:
|
||||
case 0x02:
|
||||
case 0x03:
|
||||
self().write_data_at(0, !!(val & (1 << 29)));
|
||||
self().write_data_at(1, !!(val & (1 << 28)));
|
||||
self().write_data_at(2, !!(val & (1 << 27)));
|
||||
self().write_data_at(3, !!(val & (1 << 26)));
|
||||
self().write_data_at(4, !!(val & (1 << 25)));
|
||||
self().write_data_at(5, !!(val & (1 << 24)));
|
||||
self().write_data_at(6, !!(val & (1 << 23)));
|
||||
self().write_data_at(7, !!(val & (1 << 22)));
|
||||
self().write_data_at(8, !!(val & (1 << 21)));
|
||||
self().write_data_at(9, !!(val & (1 << 20)));
|
||||
self().write_data_at(10, !!(val & (1 << 19)));
|
||||
self().write_data_at(11, !!(val & (1 << 18)));
|
||||
self().write_data_at(12, !!(val & (1 << 17)));
|
||||
self().write_data_at(13, !!(val & (1 << 16)));
|
||||
self().write_data_at(14, !!(val & (1 << 15)));
|
||||
self().write_data_at(15, !!(val & (1 << 14)));
|
||||
self().write_data_at(16, !!(val & (1 << 13)));
|
||||
self().write_data_at(17, !!(val & (1 << 12)));
|
||||
self().write_data_at(18, !!(val & (1 << 11)));
|
||||
self().write_data_at(19, !!(val & (1 << 10)));
|
||||
self().write_data_at(20, !!(val & (1 << 9)));
|
||||
self().write_data_at(21, !!(val & (1 << 8)));
|
||||
self().write_data_at(22, !!(val & (1 << 7)));
|
||||
self().write_data_at(23, !!(val & (1 << 6)));
|
||||
self().write_data_at(24, !!(val & (1 << 5)));
|
||||
self().write_data_at(25, !!(val & (1 << 4)));
|
||||
self().write_data_at(26, !!(val & (1 << 3)));
|
||||
self().write_data_at(27, !!(val & (1 << 2)));
|
||||
self().write_data_at(28, !!(val & (1 << 1)));
|
||||
self().write_data_at(29, !!(val & (1 << 0)));
|
||||
self().forward(30);
|
||||
break;
|
||||
case 0x04: // 01xx - 10 3-bit values.
|
||||
case 0x05:
|
||||
case 0x06:
|
||||
case 0x07:
|
||||
self().write_data_at(0, (val >> 27) & 0x07);
|
||||
self().write_data_at(1, (val >> 24) & 0x07);
|
||||
self().write_data_at(2, (val >> 21) & 0x07);
|
||||
self().write_data_at(3, (val >> 18) & 0x07);
|
||||
self().write_data_at(4, (val >> 15) & 0x07);
|
||||
self().write_data_at(5, (val >> 12) & 0x07);
|
||||
self().write_data_at(6, (val >> 9) & 0x07);
|
||||
self().write_data_at(7, (val >> 6) & 0x07);
|
||||
self().write_data_at(8, (val >> 3) & 0x07);
|
||||
self().write_data_at(9, (val >> 0) & 0x07);
|
||||
self().forward(10);
|
||||
break;
|
||||
case 0x08: // 10xx - 6 5-bit values.
|
||||
case 0x09:
|
||||
case 0x0A:
|
||||
case 0x0B:
|
||||
self().write_data_at(0, (val >> 25) & 0x1F);
|
||||
self().write_data_at(1, (val >> 20) & 0x1F);
|
||||
self().write_data_at(2, (val >> 15) & 0x1F);
|
||||
self().write_data_at(3, (val >> 10) & 0x1F);
|
||||
self().write_data_at(4, (val >> 5) & 0x1F);
|
||||
self().write_data_at(5, (val >> 0) & 0x1F);
|
||||
self().forward(6);
|
||||
break;
|
||||
case 0x0C: // 1100 - 4 7-bit values
|
||||
self().write_data_at(0, (val >> 21) & 0x7F);
|
||||
self().write_data_at(1, (val >> 14) & 0x7F);
|
||||
self().write_data_at(2, (val >> 7) & 0x7F);
|
||||
self().write_data_at(3, (val >> 0) & 0x7F);
|
||||
self().forward(4);
|
||||
break;
|
||||
case 0x0D: // 1101x - 3 9-bit values.
|
||||
self().write_data_at(0, (val >> 18) & 0x1FF);
|
||||
self().write_data_at(1, (val >> 9) & 0x1FF);
|
||||
self().write_data_at(2, (val >> 0) & 0x1FF);
|
||||
self().forward(3);
|
||||
break;
|
||||
case 0x0E: // 110x - 2 14-bit values.
|
||||
self().write_data_at(0, (val >> 14) & 0x3FFF);
|
||||
self().write_data_at(1, (val >> 0) & 0x3FFF);
|
||||
self().forward(2);
|
||||
break;
|
||||
case 0x0F: // 1100 - 1 28-bit value.
|
||||
self().write_data_at(0, val & 0xFFFFFFF);
|
||||
self().forward(1);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
protected:
|
||||
Self& self()
|
||||
{
|
||||
return static_cast<Self&>(*this);
|
||||
}
|
||||
|
||||
const Self& self() const
|
||||
{
|
||||
return static_cast<const Self&>(*this);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
class int_array_array_decompression:
|
||||
public stream_decompression_base<int_array_array_decompression>
|
||||
{
|
||||
public:
|
||||
int_array_array_decompression(const int* array,
|
||||
size_t array_size,
|
||||
int* res)
|
||||
: array_(array), n_(array_size), pos_(0), result_(res)
|
||||
{
|
||||
}
|
||||
|
||||
void write_data_at(size_t pos, unsigned int i)
|
||||
{
|
||||
result_[pos] = i;
|
||||
}
|
||||
|
||||
void forward(size_t i)
|
||||
{
|
||||
result_ += i;
|
||||
}
|
||||
|
||||
bool have_comp_data() const
|
||||
{
|
||||
return pos_ < n_;
|
||||
}
|
||||
|
||||
unsigned int next_comp_data()
|
||||
{
|
||||
return array_[pos_++];
|
||||
}
|
||||
|
||||
protected:
|
||||
const int* array_;
|
||||
size_t n_;
|
||||
size_t pos_;
|
||||
int* result_;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
int_array_array_decompress2(const int* array, size_t array_size, int* res,
|
||||
size_t)
|
||||
{
|
||||
int_array_array_decompression c(array, array_size, res);
|
||||
c.run();
|
||||
}
|
||||
|
||||
|
||||
|
||||
} // spot
|
||||
50
spot/misc/intvcmp2.hh
Normal file
50
spot/misc/intvcmp2.hh
Normal file
|
|
@ -0,0 +1,50 @@
|
|||
// -*- coding: utf-8 -*-
|
||||
// Copyright (C) 2011, 2013, 2015 Laboratoire de Recherche et
|
||||
// Developpement de l'Epita (LRDE).
|
||||
//
|
||||
// This file is part of Spot, a model checking library.
|
||||
//
|
||||
// Spot is free software; you can redistribute it and/or modify it
|
||||
// under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation; either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// Spot is distributed in the hope that it will be useful, but WITHOUT
|
||||
// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
||||
// or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public
|
||||
// License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <spot/misc/common.hh>
|
||||
#include <vector>
|
||||
#include <stddef.h>
|
||||
|
||||
namespace spot
|
||||
{
|
||||
/// \ingroup misc_tools
|
||||
/// @{
|
||||
|
||||
/// \brief Compress an int array of size \a n into a int array.
|
||||
///
|
||||
/// The destination array should be at least \a dest_size large An
|
||||
/// assert will be triggered if \a dest_size is too small. On
|
||||
/// return, \a dest_size will be set to the actually number of int
|
||||
/// filled in \a dest
|
||||
SPOT_API void
|
||||
int_array_array_compress2(const int* array, size_t n,
|
||||
int* dest, size_t& dest_size);
|
||||
|
||||
/// \brief Uncompress an int array of size \a array_size into a int
|
||||
/// array of size \a size.
|
||||
///
|
||||
/// \a size must be the exact expected size of uncompressed array.
|
||||
SPOT_API void
|
||||
int_array_array_decompress2(const int* array, size_t array_size,
|
||||
int* res, size_t size);
|
||||
|
||||
/// @}
|
||||
}
|
||||
703
spot/misc/intvcomp.cc
Normal file
703
spot/misc/intvcomp.cc
Normal file
|
|
@ -0,0 +1,703 @@
|
|||
// -*- coding: utf-8 -*-
|
||||
// Copyright (C) 2011, 2013, 2014 Laboratoire de Recherche et
|
||||
// Développement de l'Epita (LRDE).
|
||||
//
|
||||
// This file is part of Spot, a model checking library.
|
||||
//
|
||||
// Spot is free software; you can redistribute it and/or modify it
|
||||
// under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation; either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// Spot is distributed in the hope that it will be useful, but WITHOUT
|
||||
// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
||||
// or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public
|
||||
// License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
#include "config.h"
|
||||
#include <spot/misc/common.hh>
|
||||
#include <cstddef>
|
||||
#include <cassert>
|
||||
#include <spot/misc/intvcomp.hh>
|
||||
|
||||
namespace spot
|
||||
{
|
||||
|
||||
// Compression scheme
|
||||
// ------------------
|
||||
//
|
||||
// Assumptions:
|
||||
// - small and positive values are more frequent than negative
|
||||
// and large values.
|
||||
// - 0 is the most frequent value
|
||||
// - repeated values (esp. repeated 0s occur often).
|
||||
//
|
||||
// 00 encodes "value 0"
|
||||
// 010 encodes "value 1"
|
||||
// 011 encodes "a value in [2..5]" followed by 2 bits
|
||||
// 100 encodes "a value in [6..22]" followed by 4 bits
|
||||
// 101 encodes "repeat prev. value [1..8] times" followed by 3 bits count
|
||||
// 110 encodes "repeat prev. value [9..40] times" followed by 5 bits count
|
||||
// 111 encodes "an int value" followed by 32 bits
|
||||
//
|
||||
// If 101 or 110 occur at the start, the value to repeat is 0.
|
||||
|
||||
namespace
|
||||
{
|
||||
|
||||
template <class Self>
|
||||
class stream_compression_base
|
||||
{
|
||||
static const unsigned int max_bits = sizeof(unsigned int) * 8;
|
||||
|
||||
public:
|
||||
stream_compression_base()
|
||||
: cur_(0), bits_left_(max_bits)
|
||||
{
|
||||
}
|
||||
|
||||
void emit(unsigned int val)
|
||||
{
|
||||
if (val == 0)
|
||||
{
|
||||
self().push_bits(0x0, 2, 0x3);
|
||||
}
|
||||
else if (val == 1)
|
||||
{
|
||||
self().push_bits(0x2, 3, 0x7);
|
||||
}
|
||||
else if (val >= 2 && val <= 5)
|
||||
{
|
||||
self().push_bits(0x3, 3, 0x7);
|
||||
self().push_bits(val - 2, 2, 0x3);
|
||||
}
|
||||
else if (val >= 6 && val <= 22)
|
||||
{
|
||||
self().push_bits(0x4, 3, 0x7);
|
||||
self().push_bits(val - 6, 4, 0xf);
|
||||
}
|
||||
else
|
||||
{
|
||||
assert(val > 22);
|
||||
self().push_bits(0x7, 3, 0x7);
|
||||
self().push_bits(val, 32, -1U);
|
||||
}
|
||||
}
|
||||
|
||||
void run()
|
||||
{
|
||||
unsigned int last_val = 0;
|
||||
|
||||
while (SPOT_LIKELY(self().have_data()))
|
||||
{
|
||||
unsigned int val = self().next_data();
|
||||
// Repeated value? Try to find more.
|
||||
if (val == last_val)
|
||||
{
|
||||
unsigned int count = 1;
|
||||
while (count < 40 && self().skip_if(val))
|
||||
++count;
|
||||
|
||||
if ((val == 0 && count < 3) || (val == 1 && count == 1))
|
||||
{
|
||||
// it is more efficient to emit 0 once or twice directly
|
||||
// (e.g., 00 00 vs. 011 11)
|
||||
// for value 1, repetition is worthwhile for count > 1
|
||||
// (e.g., 010 010 vs. 011 00)
|
||||
while (count--)
|
||||
emit(val);
|
||||
}
|
||||
else if (count < 9)
|
||||
{
|
||||
self().push_bits(0x5, 3, 0x7);
|
||||
self().push_bits(count - 1, 3, 0x7);
|
||||
}
|
||||
else
|
||||
{
|
||||
self().push_bits(0x6, 3, 0x7);
|
||||
self().push_bits(count - 9, 5, 0x1f);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
emit(val);
|
||||
last_val = val;
|
||||
}
|
||||
}
|
||||
flush();
|
||||
}
|
||||
|
||||
// This version assumes there is at least n bits free in cur_.
|
||||
void
|
||||
push_bits_unchecked(unsigned int bits, unsigned int n, unsigned int mask)
|
||||
{
|
||||
cur_ <<= n;
|
||||
cur_ |= (bits & mask);
|
||||
if (SPOT_LIKELY(bits_left_ -= n))
|
||||
return;
|
||||
|
||||
self().push_data(cur_);
|
||||
cur_ = 0;
|
||||
bits_left_ = max_bits;
|
||||
}
|
||||
|
||||
void
|
||||
push_bits(unsigned int bits, unsigned int n, unsigned int mask)
|
||||
{
|
||||
if (SPOT_LIKELY(n <= bits_left_))
|
||||
{
|
||||
push_bits_unchecked(bits, n, mask);
|
||||
return;
|
||||
}
|
||||
|
||||
// bits_left_ < n
|
||||
|
||||
unsigned int right_bit_count = n - bits_left_;
|
||||
unsigned int left = bits >> right_bit_count;
|
||||
push_bits_unchecked(left, bits_left_, (1 << bits_left_) - 1);
|
||||
push_bits_unchecked(bits, right_bit_count, (1 << right_bit_count) - 1);
|
||||
}
|
||||
|
||||
void flush()
|
||||
{
|
||||
if (bits_left_ == max_bits)
|
||||
return;
|
||||
cur_ <<= bits_left_;
|
||||
self().push_data(cur_);
|
||||
}
|
||||
|
||||
protected:
|
||||
Self& self()
|
||||
{
|
||||
return static_cast<Self&>(*this);
|
||||
}
|
||||
|
||||
const Self& self() const
|
||||
{
|
||||
return static_cast<const Self&>(*this);
|
||||
}
|
||||
|
||||
unsigned int cur_;
|
||||
unsigned int bits_left_;
|
||||
};
|
||||
|
||||
class int_array_vector_compression:
|
||||
public stream_compression_base<int_array_vector_compression>
|
||||
{
|
||||
public:
|
||||
int_array_vector_compression(const int* array, size_t n)
|
||||
: array_(array), n_(n), pos_(0), result_(new std::vector<unsigned int>)
|
||||
{
|
||||
}
|
||||
|
||||
void push_data(unsigned int i)
|
||||
{
|
||||
result_->push_back(i);
|
||||
}
|
||||
|
||||
const std::vector<unsigned int>*
|
||||
result() const
|
||||
{
|
||||
return result_;
|
||||
}
|
||||
|
||||
bool have_data() const
|
||||
{
|
||||
return pos_ < n_;
|
||||
}
|
||||
|
||||
unsigned int next_data()
|
||||
{
|
||||
return static_cast<unsigned int>(array_[pos_++]);
|
||||
}
|
||||
|
||||
bool skip_if(unsigned int val)
|
||||
{
|
||||
if (SPOT_UNLIKELY(!have_data()))
|
||||
return false;
|
||||
|
||||
if (static_cast<unsigned int>(array_[pos_]) != val)
|
||||
return false;
|
||||
|
||||
++pos_;
|
||||
return true;
|
||||
}
|
||||
|
||||
protected:
|
||||
const int* array_;
|
||||
size_t n_;
|
||||
size_t pos_;
|
||||
std::vector<unsigned int>* result_;
|
||||
};
|
||||
|
||||
class int_vector_vector_compression:
|
||||
public stream_compression_base<int_vector_vector_compression>
|
||||
{
|
||||
public:
|
||||
int_vector_vector_compression(const std::vector<int>& input,
|
||||
std::vector<unsigned int>& output)
|
||||
: input_(input), pos_(input.begin()), end_(input.end()), output_(output)
|
||||
{
|
||||
}
|
||||
|
||||
void push_data(unsigned int i)
|
||||
{
|
||||
output_.push_back(i);
|
||||
}
|
||||
|
||||
bool have_data() const
|
||||
{
|
||||
return pos_ < end_;
|
||||
}
|
||||
|
||||
unsigned int next_data()
|
||||
{
|
||||
return static_cast<unsigned int>(*pos_++);
|
||||
}
|
||||
|
||||
bool skip_if(unsigned int val)
|
||||
{
|
||||
if (SPOT_UNLIKELY(!have_data()))
|
||||
return false;
|
||||
|
||||
if (static_cast<unsigned int>(*pos_) != val)
|
||||
return false;
|
||||
|
||||
++pos_;
|
||||
return true;
|
||||
}
|
||||
|
||||
protected:
|
||||
const std::vector<int>& input_;
|
||||
std::vector<int>::const_iterator pos_;
|
||||
std::vector<int>::const_iterator end_;
|
||||
std::vector<unsigned int>& output_;
|
||||
};
|
||||
|
||||
class int_array_array_compression:
|
||||
public stream_compression_base<int_array_array_compression>
|
||||
{
|
||||
public:
|
||||
int_array_array_compression(const int* array, size_t n,
|
||||
int* dest, size_t& dest_n)
|
||||
: array_(array), n_(n), pos_(0),
|
||||
result_size_(dest_n), result_(dest), result_end_(dest + dest_n)
|
||||
{
|
||||
result_size_ = 0; // this resets dest_n.
|
||||
}
|
||||
|
||||
void push_data(unsigned int i)
|
||||
{
|
||||
assert(result_ < result_end_);
|
||||
++result_size_;
|
||||
*result_++ = static_cast<int>(i);
|
||||
}
|
||||
|
||||
bool have_data() const
|
||||
{
|
||||
return pos_ < n_;
|
||||
}
|
||||
|
||||
unsigned int next_data()
|
||||
{
|
||||
return static_cast<unsigned int>(array_[pos_++]);
|
||||
}
|
||||
|
||||
bool skip_if(unsigned int val)
|
||||
{
|
||||
if (SPOT_UNLIKELY(!have_data()))
|
||||
return false;
|
||||
|
||||
if (static_cast<unsigned int>(array_[pos_]) != val)
|
||||
return false;
|
||||
|
||||
++pos_;
|
||||
return true;
|
||||
}
|
||||
|
||||
protected:
|
||||
const int* array_;
|
||||
size_t n_;
|
||||
size_t pos_;
|
||||
size_t& result_size_;
|
||||
int* result_;
|
||||
int* result_end_;
|
||||
};
|
||||
}
|
||||
|
||||
void
|
||||
int_vector_vector_compress(const std::vector<int>& input,
|
||||
std::vector<unsigned>& output)
|
||||
{
|
||||
int_vector_vector_compression c(input, output);
|
||||
c.run();
|
||||
}
|
||||
|
||||
const std::vector<unsigned int>*
|
||||
int_array_vector_compress(const int* array, size_t n)
|
||||
{
|
||||
int_array_vector_compression c(array, n);
|
||||
c.run();
|
||||
return c.result();
|
||||
}
|
||||
|
||||
void
|
||||
int_array_array_compress(const int* array, size_t n,
|
||||
int* dest, size_t& dest_size)
|
||||
{
|
||||
int_array_array_compression c(array, n, dest, dest_size);
|
||||
c.run();
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
|
||||
namespace
|
||||
{
|
||||
|
||||
template<class Self>
|
||||
class stream_decompression_base
|
||||
{
|
||||
static const unsigned int max_bits = sizeof(unsigned int) * 8;
|
||||
|
||||
public:
|
||||
void refill()
|
||||
{
|
||||
if (SPOT_UNLIKELY(look_bits_ == 0))
|
||||
{
|
||||
look_bits_ = max_bits;
|
||||
look_ = buffer_;
|
||||
|
||||
if (SPOT_LIKELY(self().have_comp_data()))
|
||||
buffer_ = self().next_comp_data();
|
||||
|
||||
if (SPOT_LIKELY(buffer_bits_ != max_bits))
|
||||
{
|
||||
unsigned int fill_size = max_bits - buffer_bits_;
|
||||
look_ <<= fill_size;
|
||||
look_ |= buffer_ >> buffer_bits_;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
unsigned int fill_size = max_bits - look_bits_;
|
||||
if (fill_size > buffer_bits_)
|
||||
fill_size = buffer_bits_;
|
||||
|
||||
look_ <<= fill_size;
|
||||
buffer_bits_ -= fill_size;
|
||||
look_ |= (buffer_ >> buffer_bits_) & ((1 << fill_size) - 1);
|
||||
look_bits_ += fill_size;
|
||||
|
||||
if (buffer_bits_ == 0)
|
||||
{
|
||||
if (SPOT_LIKELY(self().have_comp_data()))
|
||||
buffer_ = self().next_comp_data();
|
||||
|
||||
unsigned int left = max_bits - look_bits_;
|
||||
if (left != 0)
|
||||
{
|
||||
look_ <<= left;
|
||||
look_ |= buffer_ >> look_bits_;
|
||||
buffer_bits_ = look_bits_;
|
||||
look_bits_ = max_bits;
|
||||
}
|
||||
else
|
||||
{
|
||||
buffer_bits_ = max_bits;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
unsigned int look_n_bits(unsigned int n)
|
||||
{
|
||||
if (SPOT_UNLIKELY(look_bits_ < n))
|
||||
refill();
|
||||
assert(n <= look_bits_);
|
||||
return (look_ >> (look_bits_ - n)) & ((1 << n) - 1);
|
||||
}
|
||||
|
||||
void skip_n_bits(unsigned int n)
|
||||
{
|
||||
assert (n <= look_bits_);
|
||||
look_bits_ -= n;
|
||||
}
|
||||
|
||||
unsigned int get_n_bits(unsigned int n)
|
||||
{
|
||||
if (SPOT_UNLIKELY(look_bits_ < n))
|
||||
refill();
|
||||
look_bits_ -= n;
|
||||
return (look_ >> look_bits_) & ((1 << n) - 1);
|
||||
}
|
||||
|
||||
unsigned int get_32_bits()
|
||||
{
|
||||
// std::cerr << "get_32" << std::endl;
|
||||
if (SPOT_LIKELY(look_bits_ < 32))
|
||||
refill();
|
||||
unsigned int val = look_;
|
||||
look_bits_ = 0;
|
||||
refill();
|
||||
return val;
|
||||
}
|
||||
|
||||
void run()
|
||||
{
|
||||
if (SPOT_UNLIKELY(!self().have_comp_data()))
|
||||
return;
|
||||
|
||||
look_ = self().next_comp_data();
|
||||
look_bits_ = max_bits;
|
||||
if (SPOT_LIKELY(self().have_comp_data()))
|
||||
{
|
||||
buffer_ = self().next_comp_data();
|
||||
buffer_bits_ = max_bits;
|
||||
}
|
||||
else
|
||||
{
|
||||
buffer_ = 0;
|
||||
buffer_bits_ = 0;
|
||||
}
|
||||
|
||||
while (SPOT_LIKELY(!self().complete()))
|
||||
{
|
||||
unsigned int token = look_n_bits(3);
|
||||
switch (token)
|
||||
{
|
||||
case 0x0: // 00[0]
|
||||
case 0x1: // 00[1]
|
||||
skip_n_bits(2);
|
||||
self().push_data(0);
|
||||
break;
|
||||
case 0x2: // 010
|
||||
skip_n_bits(3);
|
||||
self().push_data(1);
|
||||
break;
|
||||
case 0x3: // 011
|
||||
skip_n_bits(3);
|
||||
self().push_data(2 + get_n_bits(2));
|
||||
break;
|
||||
case 0x4: // 100
|
||||
skip_n_bits(3);
|
||||
self().push_data(6 + get_n_bits(4));
|
||||
break;
|
||||
case 0x5: // 101
|
||||
skip_n_bits(3);
|
||||
self().repeat(1 + get_n_bits(3));
|
||||
break;
|
||||
case 0x6: // 110
|
||||
skip_n_bits(3);
|
||||
self().repeat(9 + get_n_bits(5));
|
||||
break;
|
||||
case 0x7: // 111
|
||||
skip_n_bits(3);
|
||||
self().push_data(get_32_bits());
|
||||
break;
|
||||
default:
|
||||
SPOT_UNREACHABLE();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
protected:
|
||||
Self& self()
|
||||
{
|
||||
return static_cast<Self&>(*this);
|
||||
}
|
||||
|
||||
const Self& self() const
|
||||
{
|
||||
return static_cast<const Self&>(*this);
|
||||
}
|
||||
|
||||
unsigned int look_;
|
||||
unsigned int look_bits_;
|
||||
unsigned int buffer_;
|
||||
unsigned int buffer_bits_;
|
||||
};
|
||||
|
||||
class int_vector_vector_decompression:
|
||||
public stream_decompression_base<int_vector_vector_decompression>
|
||||
{
|
||||
public:
|
||||
int_vector_vector_decompression(const std::vector<unsigned int>& array,
|
||||
std::vector<int>& res, size_t size)
|
||||
: prev_(0), array_(array),
|
||||
pos_(array.begin()), end_(array.end()),
|
||||
result_(res), size_(size)
|
||||
{
|
||||
result_.reserve(size);
|
||||
}
|
||||
|
||||
bool complete() const
|
||||
{
|
||||
return size_ == 0;
|
||||
}
|
||||
|
||||
void push_data(int i)
|
||||
{
|
||||
prev_ = i;
|
||||
result_.push_back(i);
|
||||
--size_;
|
||||
}
|
||||
|
||||
void repeat(unsigned int i)
|
||||
{
|
||||
size_ -= i;
|
||||
while (i--)
|
||||
result_.push_back(prev_);
|
||||
}
|
||||
|
||||
bool have_comp_data() const
|
||||
{
|
||||
return pos_ != end_;
|
||||
}
|
||||
|
||||
unsigned int next_comp_data()
|
||||
{
|
||||
return *pos_++;
|
||||
}
|
||||
|
||||
protected:
|
||||
int prev_;
|
||||
const std::vector<unsigned int>& array_;
|
||||
std::vector<unsigned int>::const_iterator pos_;
|
||||
std::vector<unsigned int>::const_iterator end_;
|
||||
std::vector<int>& result_;
|
||||
size_t size_;
|
||||
};
|
||||
|
||||
class int_vector_array_decompression:
|
||||
public stream_decompression_base<int_vector_array_decompression>
|
||||
{
|
||||
public:
|
||||
int_vector_array_decompression(const std::vector<unsigned int>* array,
|
||||
int* res,
|
||||
size_t size)
|
||||
: prev_(0), array_(array), n_(array->size()), pos_(0), result_(res),
|
||||
size_(size)
|
||||
{
|
||||
}
|
||||
|
||||
bool complete() const
|
||||
{
|
||||
return size_ == 0;
|
||||
}
|
||||
|
||||
void push_data(int i)
|
||||
{
|
||||
prev_ = i;
|
||||
*result_++ = i;
|
||||
--size_;
|
||||
}
|
||||
|
||||
void repeat(unsigned int i)
|
||||
{
|
||||
size_ -= i;
|
||||
while (i--)
|
||||
*result_++ = prev_;
|
||||
}
|
||||
|
||||
bool have_comp_data() const
|
||||
{
|
||||
return pos_ < n_;
|
||||
}
|
||||
|
||||
unsigned int next_comp_data()
|
||||
{
|
||||
return (*array_)[pos_++];
|
||||
}
|
||||
|
||||
protected:
|
||||
int prev_;
|
||||
const std::vector<unsigned int>* array_;
|
||||
size_t n_;
|
||||
size_t pos_;
|
||||
int* result_;
|
||||
size_t size_;
|
||||
};
|
||||
|
||||
class int_array_array_decompression:
|
||||
public stream_decompression_base<int_array_array_decompression>
|
||||
{
|
||||
public:
|
||||
int_array_array_decompression(const int* array,
|
||||
size_t array_size,
|
||||
int* res,
|
||||
size_t size)
|
||||
: prev_(0), array_(array), n_(array_size), pos_(0), result_(res),
|
||||
size_(size)
|
||||
{
|
||||
}
|
||||
|
||||
bool complete() const
|
||||
{
|
||||
return size_ == 0;
|
||||
}
|
||||
|
||||
void push_data(int i)
|
||||
{
|
||||
prev_ = i;
|
||||
*result_++ = i;
|
||||
--size_;
|
||||
}
|
||||
|
||||
void repeat(unsigned int i)
|
||||
{
|
||||
size_ -= i;
|
||||
while (i--)
|
||||
*result_++ = prev_;
|
||||
}
|
||||
|
||||
bool have_comp_data() const
|
||||
{
|
||||
return pos_ < n_;
|
||||
}
|
||||
|
||||
unsigned int next_comp_data()
|
||||
{
|
||||
return array_[pos_++];
|
||||
}
|
||||
|
||||
protected:
|
||||
int prev_;
|
||||
const int* array_;
|
||||
size_t n_;
|
||||
size_t pos_;
|
||||
int* result_;
|
||||
size_t size_;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
void
|
||||
int_vector_vector_decompress(const std::vector<unsigned int>& input,
|
||||
std::vector<int>& output, size_t size)
|
||||
{
|
||||
int_vector_vector_decompression c(input, output, size);
|
||||
c.run();
|
||||
}
|
||||
|
||||
void
|
||||
int_vector_array_decompress(const std::vector<unsigned int>* array, int* res,
|
||||
size_t size)
|
||||
{
|
||||
int_vector_array_decompression c(array, res, size);
|
||||
c.run();
|
||||
}
|
||||
|
||||
void
|
||||
int_array_array_decompress(const int* array, size_t array_size,
|
||||
int* res, size_t size)
|
||||
{
|
||||
int_array_array_decompression c(array, array_size, res, size);
|
||||
c.run();
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
75
spot/misc/intvcomp.hh
Normal file
75
spot/misc/intvcomp.hh
Normal file
|
|
@ -0,0 +1,75 @@
|
|||
// -*- coding: utf-8 -*-
|
||||
// Copyright (C) 2011, 2013, 2015 Laboratoire de Recherche et
|
||||
// Developpement de l'Epita (LRDE).
|
||||
//
|
||||
// This file is part of Spot, a model checking library.
|
||||
//
|
||||
// Spot is free software; you can redistribute it and/or modify it
|
||||
// under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation; either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// Spot is distributed in the hope that it will be useful, but WITHOUT
|
||||
// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
||||
// or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public
|
||||
// License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <spot/misc/common.hh>
|
||||
#include <vector>
|
||||
#include <stddef.h>
|
||||
|
||||
namespace spot
|
||||
{
|
||||
/// \ingroup misc_tools
|
||||
/// @{
|
||||
|
||||
/// Compress an int vector into a vector of unsigned int.
|
||||
SPOT_API void
|
||||
int_vector_vector_compress(const std::vector<int>& input,
|
||||
std::vector<unsigned int>& output);
|
||||
|
||||
/// \brief Uncompress a vector of unsigned int into a vector of
|
||||
/// size \a size.
|
||||
///
|
||||
/// \a size must be the exact expected size of uncompressed array.
|
||||
SPOT_API void
|
||||
int_vector_vector_decompress(const std::vector<unsigned int>& array,
|
||||
std::vector<int>& output, size_t size);
|
||||
|
||||
/// Compress an int array if size \a n into a vector of unsigned int.
|
||||
SPOT_API const std::vector<unsigned int>*
|
||||
int_array_vector_compress(const int* array, size_t n);
|
||||
|
||||
/// \brief Uncompress a vector of unsigned int into an int array of
|
||||
/// size \a size.
|
||||
///
|
||||
/// \a size must be the exact expected size of uncompressed array.
|
||||
SPOT_API void
|
||||
int_vector_array_decompress(const std::vector<unsigned int>* array,
|
||||
int* res, size_t size);
|
||||
|
||||
/// \brief Compress an int array of size \a n into a int array.
|
||||
///
|
||||
/// The destination array should be at least \a dest_size large An
|
||||
/// assert will be triggered if \a dest_size is too small. On
|
||||
/// return, \a dest_size will be set to the actually number of int
|
||||
/// filled in \a dest
|
||||
SPOT_API void
|
||||
int_array_array_compress(const int* array, size_t n,
|
||||
int* dest, size_t& dest_size);
|
||||
|
||||
/// \brief Uncompress an int array of size \a array_size into a int
|
||||
/// array of size \a size.
|
||||
///
|
||||
/// \a size must be the exact expected size of uncompressed array.
|
||||
SPOT_API void
|
||||
int_array_array_decompress(const int* array, size_t array_size,
|
||||
int* res, size_t size);
|
||||
|
||||
/// @}
|
||||
}
|
||||
169
spot/misc/location.hh
Normal file
169
spot/misc/location.hh
Normal file
|
|
@ -0,0 +1,169 @@
|
|||
// Note: this file was comped from A Bison parser, made by GNU Bison
|
||||
// 2.7.12-4996. It is shared by all the parsers in Spot. Unfortunately,
|
||||
// at the time of writing there is no Bison option to generate this
|
||||
// file an update it.
|
||||
|
||||
/* Locations for Bison parsers in C++
|
||||
|
||||
Copyright (C) 2002-2007, 2009-2013 Free Software Foundation, Inc.
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>. */
|
||||
|
||||
/* As a special exception, you may create a larger work that contains
|
||||
part or all of the Bison parser skeleton and distribute that work
|
||||
under terms of your choice, so long as that work isn't itself a
|
||||
parser generator using the skeleton or a modified version thereof
|
||||
as a parser skeleton. Alternatively, if you modify or redistribute
|
||||
the parser skeleton itself, you may (at your option) remove this
|
||||
special exception, which will cause the skeleton and the resulting
|
||||
Bison output files to be licensed under the GNU General Public
|
||||
License without this special exception.
|
||||
|
||||
This special exception was added by the Free Software Foundation in
|
||||
version 2.2 of Bison. */
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <spot/misc/position.hh>
|
||||
|
||||
namespace spot
|
||||
{
|
||||
/// Abstract a location.
|
||||
class location
|
||||
{
|
||||
public:
|
||||
|
||||
/// Construct a location from \a b to \a e.
|
||||
location (const position& b, const position& e)
|
||||
: begin (b)
|
||||
, end (e)
|
||||
{
|
||||
}
|
||||
|
||||
/// Construct a 0-width location in \a p.
|
||||
explicit location (const position& p = position ())
|
||||
: begin (p)
|
||||
, end (p)
|
||||
{
|
||||
}
|
||||
|
||||
/// Construct a 0-width location in \a f, \a l, \a c.
|
||||
explicit location (std::string* f,
|
||||
unsigned int l = 1u,
|
||||
unsigned int c = 1u)
|
||||
: begin (f, l, c)
|
||||
, end (f, l, c)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
/// Initialization.
|
||||
void initialize (std::string* f = YY_NULL,
|
||||
unsigned int l = 1u,
|
||||
unsigned int c = 1u)
|
||||
{
|
||||
begin.initialize (f, l, c);
|
||||
end = begin;
|
||||
}
|
||||
|
||||
/** \name Line and Column related manipulators
|
||||
** \{ */
|
||||
public:
|
||||
/// Reset initial location to final location.
|
||||
void step ()
|
||||
{
|
||||
begin = end;
|
||||
}
|
||||
|
||||
/// Extend the current location to the COUNT next columns.
|
||||
void columns (unsigned int count = 1)
|
||||
{
|
||||
end += count;
|
||||
}
|
||||
|
||||
/// Extend the current location to the COUNT next lines.
|
||||
void lines (unsigned int count = 1)
|
||||
{
|
||||
end.lines (count);
|
||||
}
|
||||
/** \} */
|
||||
|
||||
|
||||
public:
|
||||
/// Beginning of the located region.
|
||||
position begin;
|
||||
/// End of the located region.
|
||||
position end;
|
||||
};
|
||||
|
||||
/// Join two location objects to create a location.
|
||||
inline const location operator+ (const location& begin, const location& end)
|
||||
{
|
||||
location res = begin;
|
||||
res.end = end.end;
|
||||
return res;
|
||||
}
|
||||
|
||||
/// Add two location objects.
|
||||
inline const location operator+ (const location& begin, unsigned int width)
|
||||
{
|
||||
location res = begin;
|
||||
res.columns (width);
|
||||
return res;
|
||||
}
|
||||
|
||||
/// Add and assign a location.
|
||||
inline location& operator+= (location& res, unsigned int width)
|
||||
{
|
||||
res.columns (width);
|
||||
return res;
|
||||
}
|
||||
|
||||
/// Compare two location objects.
|
||||
inline bool
|
||||
operator== (const location& loc1, const location& loc2)
|
||||
{
|
||||
return loc1.begin == loc2.begin && loc1.end == loc2.end;
|
||||
}
|
||||
|
||||
/// Compare two location objects.
|
||||
inline bool
|
||||
operator!= (const location& loc1, const location& loc2)
|
||||
{
|
||||
return !(loc1 == loc2);
|
||||
}
|
||||
|
||||
/** \brief Intercept output stream redirection.
|
||||
** \param ostr the destination output stream
|
||||
** \param loc a reference to the location to redirect
|
||||
**
|
||||
** Avoid duplicate information.
|
||||
*/
|
||||
template <typename YYChar>
|
||||
inline std::basic_ostream<YYChar>&
|
||||
operator<< (std::basic_ostream<YYChar>& ostr, const location& loc)
|
||||
{
|
||||
position last = loc.end - 1;
|
||||
ostr << loc.begin;
|
||||
if (last.filename
|
||||
&& (!loc.begin.filename
|
||||
|| *loc.begin.filename != *last.filename))
|
||||
ostr << '-' << last;
|
||||
else if (loc.begin.line != last.line)
|
||||
ostr << '-' << last.line << '.' << last.column;
|
||||
else if (loc.begin.column != last.column)
|
||||
ostr << '-' << last.column;
|
||||
return ostr;
|
||||
}
|
||||
}
|
||||
51
spot/misc/ltstr.hh
Normal file
51
spot/misc/ltstr.hh
Normal file
|
|
@ -0,0 +1,51 @@
|
|||
// -*- coding: utf-8 -*-
|
||||
// Copyright (C) 2015 Laboratoire de Recherche et Développement
|
||||
// de l'Epita (LRDE)
|
||||
// Copyright (C) 2005 Laboratoire d'Informatique de Paris 6 (LIP6),
|
||||
// département Systèmes Répartis Coopératifs (SRC), Université Pierre
|
||||
// et Marie Curie.
|
||||
//
|
||||
// This file is part of Spot, a model checking library.
|
||||
//
|
||||
// Spot is free software; you can redistribute it and/or modify it
|
||||
// under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation; either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// Spot is distributed in the hope that it will be useful, but WITHOUT
|
||||
// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
||||
// or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public
|
||||
// License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <cstring>
|
||||
#include <functional>
|
||||
|
||||
namespace spot
|
||||
{
|
||||
|
||||
/// \ingroup misc_tools
|
||||
/// \brief Strict Weak Ordering for \c char*.
|
||||
///
|
||||
/// This is meant to be used as a comparison functor for
|
||||
/// STL \c map whose key are of type <code>const char*</code>.
|
||||
///
|
||||
/// For instance here is how one could declare
|
||||
/// a map of <code>const state*</code>.
|
||||
/// \code
|
||||
/// std::map<const char*, int, spot::state_ptr_less_than> seen;
|
||||
/// \endcode
|
||||
struct char_ptr_less_than:
|
||||
public std::binary_function<const char*, const char*, bool>
|
||||
{
|
||||
bool
|
||||
operator()(const char* left, const char* right) const
|
||||
{
|
||||
return strcmp(left, right) < 0;
|
||||
}
|
||||
};
|
||||
}
|
||||
44
spot/misc/memusage.cc
Normal file
44
spot/misc/memusage.cc
Normal file
|
|
@ -0,0 +1,44 @@
|
|||
// -*- coding: utf-8 -*-
|
||||
// Copyright (C) 2013 Laboratoire de Recherche et Developpement de
|
||||
// l'Epita (LRDE).
|
||||
// Copyright (C) 2006 Laboratoire d'Informatique de Paris 6 (LIP6),
|
||||
// département Systèmes Répartis Coopératifs (SRC), Université Pierre
|
||||
// et Marie Curie.
|
||||
//
|
||||
// This file is part of Spot, a model checking library.
|
||||
//
|
||||
// Spot is free software; you can redistribute it and/or modify it
|
||||
// under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation; either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// Spot is distributed in the hope that it will be useful, but WITHOUT
|
||||
// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
||||
// or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public
|
||||
// License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
#include "config.h"
|
||||
#include <spot/misc/memusage.hh>
|
||||
#include <cstdio>
|
||||
|
||||
namespace spot
|
||||
{
|
||||
int
|
||||
memusage()
|
||||
{
|
||||
int size;
|
||||
|
||||
FILE* file = fopen("/proc/self/statm", "r");
|
||||
if (!file)
|
||||
return -1;
|
||||
int res = fscanf(file, "%d", &size);
|
||||
(void) fclose(file);
|
||||
if (res != 1)
|
||||
return -1;
|
||||
return size;
|
||||
}
|
||||
|
||||
}
|
||||
34
spot/misc/memusage.hh
Normal file
34
spot/misc/memusage.hh
Normal file
|
|
@ -0,0 +1,34 @@
|
|||
// -*- coding: utf-8 -*-
|
||||
// Copyright (C) 2013 Laboratoire de Recherche et Developpement de
|
||||
// l'Epita (LRDE).
|
||||
// Copyright (C) 2006 Laboratoire d'Informatique de Paris 6 (LIP6),
|
||||
// département Systèmes Répartis Coopératifs (SRC), Université Pierre
|
||||
// et Marie Curie.
|
||||
//
|
||||
// This file is part of Spot, a model checking library.
|
||||
//
|
||||
// Spot is free software; you can redistribute it and/or modify it
|
||||
// under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation; either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// Spot is distributed in the hope that it will be useful, but WITHOUT
|
||||
// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
||||
// or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public
|
||||
// License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <spot/misc/common.hh>
|
||||
|
||||
namespace spot
|
||||
{
|
||||
/// \brief Total number of pages in use by the program.
|
||||
///
|
||||
/// \return The total number of pages in use by the program if known.
|
||||
/// -1 otherwise.
|
||||
SPOT_API int memusage();
|
||||
}
|
||||
178
spot/misc/minato.cc
Normal file
178
spot/misc/minato.cc
Normal file
|
|
@ -0,0 +1,178 @@
|
|||
// -*- coding: utf-8 -*-
|
||||
// Copyright (C) 2009, 2013, 2014, 2015 Laboratoire de Recherche et
|
||||
// Développement de l'Epita (LRDE).
|
||||
// Copyright (C) 2003, 2004 Laboratoire d'Informatique de Paris
|
||||
// 6 (LIP6), département Systèmes Répartis Coopératifs (SRC),
|
||||
// Université Pierre et Marie Curie.
|
||||
//
|
||||
// This file is part of Spot, a model checking library.
|
||||
//
|
||||
// Spot is free software; you can redistribute it and/or modify it
|
||||
// under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation; either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// Spot is distributed in the hope that it will be useful, but WITHOUT
|
||||
// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
||||
// or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public
|
||||
// License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
#include "config.h"
|
||||
#include <spot/misc/minato.hh>
|
||||
#include <cassert>
|
||||
|
||||
namespace spot
|
||||
{
|
||||
|
||||
minato_isop::minato_isop(bdd input)
|
||||
: minato_isop(input, bdd_support(input))
|
||||
{
|
||||
}
|
||||
|
||||
minato_isop::minato_isop(bdd input, bdd vars)
|
||||
: ret_(bddfalse)
|
||||
{
|
||||
// If INPUT has the form a&b&c&(binary function) we want to
|
||||
// compute the ISOP of the only binary and prepend a&b&c latter.
|
||||
//
|
||||
// Calling bdd_satprefix (it returns a&b&c and modify input to
|
||||
// point to function) this way is an optimization to the
|
||||
// original algorithm, because in many cases we are trying to
|
||||
// build ISOPs out of formulae that are already cubes.
|
||||
cube_.push(bdd_satprefix(input));
|
||||
todo_.emplace(input, input, vars);
|
||||
}
|
||||
|
||||
minato_isop::minato_isop(bdd input_min, bdd input_max, bool)
|
||||
: ret_(bddfalse)
|
||||
{
|
||||
if (input_min == input_max)
|
||||
{
|
||||
cube_.push(bdd_satprefix(input_min));
|
||||
input_max = input_min;
|
||||
}
|
||||
else
|
||||
{
|
||||
cube_.push(bddtrue);
|
||||
}
|
||||
bdd common = input_min & input_max;
|
||||
todo_.emplace(input_min, input_max, bdd_support(common));
|
||||
}
|
||||
|
||||
bdd
|
||||
minato_isop::next()
|
||||
{
|
||||
while (!todo_.empty())
|
||||
{
|
||||
local_vars& l = todo_.top();
|
||||
switch (l.step)
|
||||
{
|
||||
case local_vars::FirstStep:
|
||||
next_var:
|
||||
{
|
||||
if (l.f_min == bddfalse)
|
||||
{
|
||||
ret_ = bddfalse;
|
||||
todo_.pop();
|
||||
continue;
|
||||
}
|
||||
if (l.vars == bddtrue || l.f_max == bddtrue)
|
||||
{
|
||||
ret_ = l.f_max;
|
||||
todo_.pop();
|
||||
return cube_.top() & ret_;
|
||||
}
|
||||
assert(l.vars != bddfalse);
|
||||
|
||||
// Pick the first variable in VARS that is used by F_MIN
|
||||
// or F_MAX. We know that VARS, F_MIN or F_MAX are not
|
||||
// constants (bddtrue or bddfalse) because one of the
|
||||
// two above `if' would have matched; so it's ok to call
|
||||
// bdd_var().
|
||||
int v = bdd_var(l.vars);
|
||||
l.vars = bdd_high(l.vars);
|
||||
int v_min = bdd_var(l.f_min);
|
||||
int v_max = bdd_var(l.f_max);
|
||||
if (v < v_min && v < v_max)
|
||||
// Do not use a while() for this goto, because we want
|
||||
// `continue' to be relative to the outermost while().
|
||||
goto next_var;
|
||||
|
||||
l.step = local_vars::SecondStep;
|
||||
|
||||
bdd v0 = bdd_nithvar(v);
|
||||
l.v1 = bdd_ithvar(v);
|
||||
|
||||
// All the following should be equivalent to
|
||||
// f0_min = bdd_restrict(f_min, v0);
|
||||
// f0_max = bdd_restrict(f_max, v0);
|
||||
// f1_min = bdd_restrict(f_min, v1);
|
||||
// f1_max = bdd_restrict(f_max, v1);
|
||||
// but we try to avoid bdd_restrict when possible.
|
||||
if (v == v_min)
|
||||
{
|
||||
l.f0_min = bdd_low(l.f_min);
|
||||
l.f1_min = bdd_high(l.f_min);
|
||||
}
|
||||
else if (v_min < v)
|
||||
{
|
||||
l.f0_min = bdd_restrict(l.f_min, v0);
|
||||
l.f1_min = bdd_restrict(l.f_min, l.v1);
|
||||
}
|
||||
else
|
||||
{
|
||||
l.f1_min = l.f0_min = l.f_min;
|
||||
}
|
||||
if (v == v_max)
|
||||
{
|
||||
l.f0_max = bdd_low(l.f_max);
|
||||
l.f1_max = bdd_high(l.f_max);
|
||||
}
|
||||
else if (v_max < v)
|
||||
{
|
||||
l.f0_max = bdd_restrict(l.f_max, v0);
|
||||
l.f1_max = bdd_restrict(l.f_max, l.v1);
|
||||
}
|
||||
else
|
||||
{
|
||||
l.f1_max = l.f0_max = l.f_max;
|
||||
}
|
||||
|
||||
cube_.push(cube_.top() & v0);
|
||||
todo_.emplace(l.f0_min - l.f1_max, l.f0_max, l.vars);
|
||||
}
|
||||
continue;
|
||||
|
||||
case local_vars::SecondStep:
|
||||
l.step = local_vars::ThirdStep;
|
||||
l.g0 = ret_;
|
||||
cube_.pop();
|
||||
cube_.push(cube_.top() & l.v1);
|
||||
todo_.emplace(l.f1_min - l.f0_max, l.f1_max, l.vars);
|
||||
continue;
|
||||
|
||||
case local_vars::ThirdStep:
|
||||
l.step = local_vars::FourthStep;
|
||||
l.g1 = ret_;
|
||||
cube_.pop();
|
||||
{
|
||||
bdd fs_max = l.f0_max & l.f1_max;
|
||||
bdd fs_min = fs_max & ((l.f0_min - l.g0) | (l.f1_min - l.g1));
|
||||
todo_.emplace(fs_min, fs_max, l.vars);
|
||||
}
|
||||
continue;
|
||||
|
||||
case local_vars::FourthStep:
|
||||
ret_ |= (l.g0 - l.v1) | (l.g1 & l.v1);
|
||||
todo_.pop();
|
||||
continue;
|
||||
}
|
||||
SPOT_UNREACHABLE();
|
||||
}
|
||||
return bddfalse;
|
||||
}
|
||||
|
||||
}
|
||||
100
spot/misc/minato.hh
Normal file
100
spot/misc/minato.hh
Normal file
|
|
@ -0,0 +1,100 @@
|
|||
// -*- coding: utf-8 -*-
|
||||
// Copyright (C) 2009, 2013, 2014, 2015 Laboratoire de Recherche et
|
||||
// Développement de l'Epita (LRDE).
|
||||
// Copyright (C) 2003, 2004 Laboratoire d'Informatique de Paris 6 (LIP6),
|
||||
// département Systèmes Répartis Coopératifs (SRC), Université Pierre
|
||||
// et Marie Curie.
|
||||
//
|
||||
// This file is part of Spot, a model checking library.
|
||||
//
|
||||
// Spot is free software; you can redistribute it and/or modify it
|
||||
// under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation; either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// Spot is distributed in the hope that it will be useful, but WITHOUT
|
||||
// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
||||
// or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public
|
||||
// License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <spot/misc/common.hh>
|
||||
#include <bddx.h>
|
||||
#include <stack>
|
||||
|
||||
namespace spot
|
||||
{
|
||||
/// \ingroup misc_tools
|
||||
/// \brief Generate an irredundant sum-of-products (ISOP) form of a
|
||||
/// BDD function.
|
||||
///
|
||||
/// This algorithm implements a derecursived version the Minato-Morreale
|
||||
/// algorithm presented in the following paper.
|
||||
/** \verbatim
|
||||
@InProceedings{ minato.92.sasimi,
|
||||
author = {Shin-ichi Minato},
|
||||
title = {Fast Generation of Irredundant Sum-of-Products Forms
|
||||
from Binary Decision Diagrams},
|
||||
booktitle = {Proceedings of the third Synthesis and Simulation
|
||||
and Meeting International Interchange workshop
|
||||
(SASIMI'92)},
|
||||
pages = {64--73},
|
||||
year = {1992},
|
||||
address = {Kobe, Japan},
|
||||
month = {April}
|
||||
}
|
||||
\endverbatim */
|
||||
class SPOT_API minato_isop
|
||||
{
|
||||
public:
|
||||
/// \brief Conctructor.
|
||||
/// \arg input The BDD function to translate in ISOP.
|
||||
minato_isop(bdd input);
|
||||
/// \brief Conctructor.
|
||||
/// \arg input The BDD function to translate in ISOP.
|
||||
/// \arg vars The set of BDD variables to factorize in \a input.
|
||||
minato_isop(bdd input, bdd vars);
|
||||
/// \brief Conctructor.
|
||||
///
|
||||
/// This version allow some flexibility in computing the ISOP.
|
||||
/// the result must be within \a input_min and \a input_max.
|
||||
/// \arg input_min The minimum BDD function to translate in ISOP.
|
||||
/// \arg input_max The maximum BDD function to translate in ISOP.
|
||||
minato_isop(bdd input_min, bdd input_max, bool);
|
||||
/// \brief Compute the next sum term of the ISOP form.
|
||||
/// Return \c bddfalse when all terms have been output.
|
||||
bdd next();
|
||||
|
||||
private:
|
||||
/// Internal variables for minato_isop.
|
||||
struct local_vars
|
||||
{
|
||||
// If you are following the paper, f_min and f_max correspond
|
||||
// to the pair of BDD functions used to encode the ternary function f
|
||||
// (see Section 3.4).
|
||||
// Also note that f0, f0', and f0'' all share the same _max function.
|
||||
// Likewise for f1, f1', and f1''.
|
||||
bdd f_min, f_max;
|
||||
// Because we need a non-recursive version of the algorithm,
|
||||
// we had to split it in four steps (each step is separated
|
||||
// from the other by a call to ISOP in the original algorithm).
|
||||
enum { FirstStep, SecondStep, ThirdStep, FourthStep } step;
|
||||
// The list of variables to factorize. This is an addition to
|
||||
// the original algorithm.
|
||||
bdd vars;
|
||||
bdd v1;
|
||||
bdd f0_min, f0_max;
|
||||
bdd f1_min, f1_max;
|
||||
bdd g0, g1;
|
||||
local_vars(bdd f_min, bdd f_max, bdd vars)
|
||||
: f_min(f_min), f_max(f_max), step(FirstStep), vars(vars) {}
|
||||
};
|
||||
std::stack<local_vars> todo_;
|
||||
std::stack<bdd> cube_;
|
||||
bdd ret_;
|
||||
};
|
||||
}
|
||||
126
spot/misc/mspool.hh
Normal file
126
spot/misc/mspool.hh
Normal file
|
|
@ -0,0 +1,126 @@
|
|||
// -*- coding: utf-8 -*-
|
||||
// Copyright (C) 2011, 2013, 2015 Laboratoire de Recherche et Developpement
|
||||
// de l'Epita (LRDE)
|
||||
//
|
||||
// This file is part of Spot, a model checking library.
|
||||
//
|
||||
// Spot is free software; you can redistribute it and/or modify it
|
||||
// under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation; either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// Spot is distributed in the hope that it will be useful, but WITHOUT
|
||||
// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
||||
// or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public
|
||||
// License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <new>
|
||||
#include <cstddef>
|
||||
#include <cstdlib>
|
||||
#include <cassert>
|
||||
#include <spot/misc/hash.hh>
|
||||
|
||||
namespace spot
|
||||
{
|
||||
|
||||
/// A multiple-size memory pool implementation.
|
||||
class multiple_size_pool
|
||||
{
|
||||
static const size_t alignment_ = 2 * sizeof(size_t) - 1;
|
||||
public:
|
||||
/// Create a pool.
|
||||
multiple_size_pool()
|
||||
: free_start_(nullptr), free_end_(nullptr), chunklist_(nullptr)
|
||||
{
|
||||
}
|
||||
|
||||
/// Free any memory allocated by this pool.
|
||||
~multiple_size_pool()
|
||||
{
|
||||
while (chunklist_)
|
||||
{
|
||||
chunk_* prev = chunklist_->prev;
|
||||
free(chunklist_);
|
||||
chunklist_ = prev;
|
||||
}
|
||||
}
|
||||
|
||||
size_t fixsize(size_t size) const
|
||||
{
|
||||
if (size < sizeof(block_))
|
||||
size = sizeof(block_);
|
||||
|
||||
return (size + alignment_ - 1) & ~(alignment_ - 1);
|
||||
}
|
||||
|
||||
/// Allocate \a size bytes of memory.
|
||||
void*
|
||||
allocate(size_t size)
|
||||
{
|
||||
size = fixsize(size);
|
||||
|
||||
block_*& f = freelist_[size];
|
||||
// If we have free blocks available, return the first one.
|
||||
if (f)
|
||||
{
|
||||
block_* first = f;
|
||||
f = f->next;
|
||||
return first;
|
||||
}
|
||||
|
||||
// Else, create a block out of the last chunk of allocated
|
||||
// memory.
|
||||
|
||||
// If all the last chunk has been used, allocate one more.
|
||||
if (free_start_ + size > free_end_)
|
||||
{
|
||||
const size_t requested = (size > 128 ? size : 128) * 8192 - 64;
|
||||
chunk_* c = reinterpret_cast<chunk_*>(malloc(requested));
|
||||
if (!c)
|
||||
throw std::bad_alloc();
|
||||
c->prev = chunklist_;
|
||||
chunklist_ = c;
|
||||
|
||||
free_start_ = c->data_ + size;
|
||||
free_end_ = c->data_ + requested;
|
||||
}
|
||||
|
||||
void* res = free_start_;
|
||||
free_start_ += size;
|
||||
return res;
|
||||
}
|
||||
|
||||
/// \brief Recycle \a size bytes of memory.
|
||||
///
|
||||
/// Despite the name, the memory is not really deallocated in the
|
||||
/// "delete" sense: it is still owned by the pool and will be
|
||||
/// reused by allocate as soon as possible. The memory is only
|
||||
/// freed when the pool is destroyed.
|
||||
///
|
||||
/// The size argument should be the same as the one passed to
|
||||
/// allocate().
|
||||
void
|
||||
deallocate (const void* ptr, size_t size)
|
||||
{
|
||||
assert(ptr);
|
||||
size = fixsize(size);
|
||||
block_* b = reinterpret_cast<block_*>(const_cast<void*>(ptr));
|
||||
block_*& f = freelist_[size];
|
||||
b->next = f;
|
||||
f = b;
|
||||
}
|
||||
|
||||
private:
|
||||
struct block_ { block_* next; };
|
||||
std::unordered_map<size_t, block_*> freelist_;
|
||||
char* free_start_;
|
||||
char* free_end_;
|
||||
// chunk = several agglomerated blocks
|
||||
union chunk_ { chunk_* prev; char data_[1]; }* chunklist_;
|
||||
};
|
||||
}
|
||||
188
spot/misc/optionmap.cc
Normal file
188
spot/misc/optionmap.cc
Normal file
|
|
@ -0,0 +1,188 @@
|
|||
// -*- coding: utf-8 -*-
|
||||
// Copyright (C) 2008, 2013, 2014, 2015 Laboratoire de Recherche et
|
||||
// Développement de l'Epita (LRDE).
|
||||
// Copyright (C) 2005 Laboratoire d'Informatique de Paris 6 (LIP6),
|
||||
// département Systèmes Répartis Coopératifs (SRC), Université Pierre
|
||||
// et Marie Curie.
|
||||
//
|
||||
// This file is part of Spot, a model checking library.
|
||||
//
|
||||
// Spot is free software; you can redistribute it and/or modify it
|
||||
// under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation; either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// Spot is distributed in the hope that it will be useful, but WITHOUT
|
||||
// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
||||
// or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public
|
||||
// License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
#include "config.h"
|
||||
#include <cstring>
|
||||
#include <iostream>
|
||||
#include <cstdlib>
|
||||
#include <spot/misc/optionmap.hh>
|
||||
|
||||
namespace spot
|
||||
{
|
||||
const char*
|
||||
option_map::parse_options(const char* options)
|
||||
{
|
||||
while (*options)
|
||||
{
|
||||
// Skip leading separators.
|
||||
while (*options && strchr(" \t\n,;", *options))
|
||||
++options;
|
||||
|
||||
// `!foo' is a shorthand for `foo=0'.
|
||||
const char* negated = nullptr;
|
||||
if (*options == '!')
|
||||
{
|
||||
// Skip spaces.
|
||||
while (*options && strchr(" \t\n", *options))
|
||||
++options;
|
||||
negated = options++;
|
||||
}
|
||||
|
||||
if (!*options)
|
||||
{
|
||||
if (negated)
|
||||
return negated;
|
||||
else
|
||||
break;
|
||||
}
|
||||
|
||||
const char* name_start = options;
|
||||
|
||||
// Find the end of the name.
|
||||
while (*options && !strchr(", \t\n;=", *options))
|
||||
++options;
|
||||
|
||||
std::string name(name_start, options);
|
||||
|
||||
// Skip spaces.
|
||||
while (*options && strchr(" \t\n", *options))
|
||||
++options;
|
||||
|
||||
if (*options != '=')
|
||||
{
|
||||
options_[name] = (negated ? 0 : 1);
|
||||
}
|
||||
else if (negated)
|
||||
{
|
||||
return negated;
|
||||
}
|
||||
else
|
||||
{
|
||||
++options;
|
||||
// Skip spaces.
|
||||
while (*options && strchr(" \t\n", *options))
|
||||
++options;
|
||||
if (!*options)
|
||||
return name_start;
|
||||
|
||||
if (*options == '\'' || *options == '"')
|
||||
{
|
||||
auto sep = *options;
|
||||
auto start = options + 1;
|
||||
do
|
||||
++options;
|
||||
while (*options && *options != sep);
|
||||
if (*options != sep)
|
||||
return start - 1;
|
||||
std::string val(start, options);
|
||||
options_str_[name] = val;
|
||||
if (*options)
|
||||
++options;
|
||||
}
|
||||
else
|
||||
{
|
||||
char* val_end;
|
||||
int val = strtol(options, &val_end, 10);
|
||||
if (val_end == options)
|
||||
return name_start;
|
||||
|
||||
if (*val_end == 'K')
|
||||
{
|
||||
val *= 1024;
|
||||
++val_end;
|
||||
}
|
||||
else if (*val_end == 'M')
|
||||
{
|
||||
val *= 1024 * 1024;
|
||||
++val_end;
|
||||
}
|
||||
else if (*val_end && !strchr(" \t\n,;", *val_end))
|
||||
{
|
||||
return options;
|
||||
}
|
||||
options = val_end;
|
||||
options_[name] = val;
|
||||
}
|
||||
}
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
int
|
||||
option_map::get(const char* option, int def) const
|
||||
{
|
||||
auto it = options_.find(option);
|
||||
return (it == options_.end()) ? def : it->second;
|
||||
}
|
||||
|
||||
std::string
|
||||
option_map::get_str(const char* option, std::string def) const
|
||||
{
|
||||
auto it = options_str_.find(option);
|
||||
return (it == options_str_.end()) ? def : it->second;
|
||||
}
|
||||
|
||||
int
|
||||
option_map::operator[](const char* option) const
|
||||
{
|
||||
return get(option);
|
||||
}
|
||||
|
||||
int
|
||||
option_map::set(const char* option, int val, int def)
|
||||
{
|
||||
int old = get(option, def);
|
||||
options_[option] = val;
|
||||
return old;
|
||||
}
|
||||
|
||||
std::string
|
||||
option_map::set_str(const char* option, std::string val, std::string def)
|
||||
{
|
||||
std::string old = get_str(option, def);
|
||||
options_str_[option] = val;
|
||||
return old;
|
||||
}
|
||||
|
||||
void
|
||||
option_map::set(const option_map& o)
|
||||
{
|
||||
options_ = o.options_;
|
||||
options_str_ = o.options_str_;
|
||||
}
|
||||
|
||||
int&
|
||||
option_map::operator[](const char* option)
|
||||
{
|
||||
return options_[option];
|
||||
}
|
||||
|
||||
std::ostream&
|
||||
operator<<(std::ostream& os, const option_map& m)
|
||||
{
|
||||
for (auto p: m.options_)
|
||||
os << '"' << p.first << "\" = " << p.second << '\n';
|
||||
for (auto p: m.options_str_)
|
||||
os << '"' << p.first << "\" = \"" << p.second << "\"\n";
|
||||
return os;
|
||||
}
|
||||
}
|
||||
103
spot/misc/optionmap.hh
Normal file
103
spot/misc/optionmap.hh
Normal file
|
|
@ -0,0 +1,103 @@
|
|||
// -*- coding: utf-8 -*-
|
||||
// Copyright (C) 2013, 2015 Laboratoire de Recherche et Developpement de
|
||||
// l'Epita (LRDE)
|
||||
// Copyright (C) 2005 Laboratoire d'Informatique de Paris 6 (LIP6),
|
||||
// département Systèmes Répartis Coopératifs (SRC), Université Pierre
|
||||
// et Marie Curie.
|
||||
//
|
||||
// This file is part of Spot, a model checking library.
|
||||
//
|
||||
// Spot is free software; you can redistribute it and/or modify it
|
||||
// under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation; either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// Spot is distributed in the hope that it will be useful, but WITHOUT
|
||||
// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
||||
// or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public
|
||||
// License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <spot/misc/common.hh>
|
||||
#include <string>
|
||||
#include <map>
|
||||
#include <iosfwd>
|
||||
|
||||
namespace spot
|
||||
{
|
||||
/// \ingroup misc_tools
|
||||
/// \brief Manage a map of options.
|
||||
///
|
||||
/// Each option is defined by a string and is associated to an integer value.
|
||||
class SPOT_API option_map
|
||||
{
|
||||
public:
|
||||
/// \brief Add the parsed options to the map.
|
||||
///
|
||||
/// \a options are separated by a space, comma, semicolon or tabulation and
|
||||
/// can be optionnaly followed by an integer value (preceded by an equal
|
||||
/// sign). If not specified, the default value is 1.
|
||||
///
|
||||
/// The following three lines are equivalent.
|
||||
/** \verbatim
|
||||
optA !optB optC=4194304
|
||||
optA=1, optB=0, optC=4096K
|
||||
optC = 4M; optA !optB
|
||||
\endverbatim */
|
||||
///
|
||||
/// \return A non-null pointer to the option for which an expected integer
|
||||
/// value cannot be parsed.
|
||||
const char* parse_options(const char* options);
|
||||
|
||||
/// \brief Get the value of \a option.
|
||||
///
|
||||
/// \return The value associated to \a option if it exists,
|
||||
/// \a def otherwise.
|
||||
/// \see operator[]()
|
||||
int get(const char* option, int def = 0) const;
|
||||
|
||||
/// \brief Get the value of \a option.
|
||||
///
|
||||
/// \return The value associated to \a option if it exists,
|
||||
/// \a def otherwise.
|
||||
/// \see operator[]()
|
||||
std::string get_str(const char* option, std::string def = {}) const;
|
||||
|
||||
/// \brief Get the value of \a option.
|
||||
///
|
||||
/// \return The value associated to \a option if it exists, 0 otherwise.
|
||||
/// \see get()
|
||||
int operator[](const char* option) const;
|
||||
|
||||
/// \brief Set the value of \a option to \a val.
|
||||
///
|
||||
/// \return The previous value associated to \a option if declared,
|
||||
/// or \a def otherwise.
|
||||
int set(const char* option, int val, int def = 0);
|
||||
|
||||
/// \brief Set the value of a string \a option to \a val.
|
||||
///
|
||||
/// \return The previous value associated to \a option if declared,
|
||||
/// or \a def otherwise.
|
||||
std::string set_str(const char* option,
|
||||
std::string val, std::string def = {});
|
||||
|
||||
/// Acquire all the settings of \a o.
|
||||
void set(const option_map& o);
|
||||
|
||||
/// \brief Get a reference to the current value of \a option.
|
||||
int& operator[](const char* option);
|
||||
|
||||
/// \brief Print the option_map \a m.
|
||||
friend SPOT_API std::ostream&
|
||||
operator<<(std::ostream& os, const option_map& m);
|
||||
|
||||
private:
|
||||
std::map<std::string, int> options_;
|
||||
std::map<std::string, std::string> options_str_;
|
||||
};
|
||||
}
|
||||
164
spot/misc/position.hh
Normal file
164
spot/misc/position.hh
Normal file
|
|
@ -0,0 +1,164 @@
|
|||
// Note: this file was comped from A Bison parser, made by GNU Bison
|
||||
// 2.7.12-4996. It is shared by all the parsers in Spot. Unfortunately,
|
||||
// at the time of writing there is no Bison option to generate this
|
||||
// file an update it.
|
||||
|
||||
/* Positions for Bison parsers in C++
|
||||
|
||||
Copyright (C) 2002-2007, 2009-2013 Free Software Foundation, Inc.
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>. */
|
||||
|
||||
/* As a special exception, you may create a larger work that contains
|
||||
part or all of the Bison parser skeleton and distribute that work
|
||||
under terms of your choice, so long as that work isn't itself a
|
||||
parser generator using the skeleton or a modified version thereof
|
||||
as a parser skeleton. Alternatively, if you modify or redistribute
|
||||
the parser skeleton itself, you may (at your option) remove this
|
||||
special exception, which will cause the skeleton and the resulting
|
||||
Bison output files to be licensed under the GNU General Public
|
||||
License without this special exception.
|
||||
|
||||
This special exception was added by the Free Software Foundation in
|
||||
version 2.2 of Bison. */
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <algorithm> // std::max
|
||||
#include <iostream>
|
||||
#include <string>
|
||||
|
||||
#ifndef YY_NULL
|
||||
# if defined __cplusplus && 201103L <= __cplusplus
|
||||
# define YY_NULL nullptr
|
||||
# else
|
||||
# define YY_NULL 0
|
||||
# endif
|
||||
#endif
|
||||
|
||||
|
||||
namespace spot
|
||||
{
|
||||
|
||||
/// Abstract a position.
|
||||
class position
|
||||
{
|
||||
public:
|
||||
|
||||
/// Construct a position.
|
||||
explicit position (std::string* f = YY_NULL,
|
||||
unsigned int l = 1u,
|
||||
unsigned int c = 1u)
|
||||
: filename (f)
|
||||
, line (l)
|
||||
, column (c)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
/// Initialization.
|
||||
void initialize (std::string* fn = YY_NULL,
|
||||
unsigned int l = 1u,
|
||||
unsigned int c = 1u)
|
||||
{
|
||||
filename = fn;
|
||||
line = l;
|
||||
column = c;
|
||||
}
|
||||
|
||||
/** \name Line and Column related manipulators
|
||||
** \{ */
|
||||
/// (line related) Advance to the COUNT next lines.
|
||||
void lines (int count = 1)
|
||||
{
|
||||
column = 1u;
|
||||
line += count;
|
||||
}
|
||||
|
||||
/// (column related) Advance to the COUNT next columns.
|
||||
void columns (int count = 1)
|
||||
{
|
||||
column = std::max (1u, column + count);
|
||||
}
|
||||
/** \} */
|
||||
|
||||
/// File name to which this position refers.
|
||||
std::string* filename;
|
||||
/// Current line number.
|
||||
unsigned int line;
|
||||
/// Current column number.
|
||||
unsigned int column;
|
||||
};
|
||||
|
||||
/// Add and assign a position.
|
||||
inline position&
|
||||
operator+= (position& res, const int width)
|
||||
{
|
||||
res.columns (width);
|
||||
return res;
|
||||
}
|
||||
|
||||
/// Add two position objects.
|
||||
inline const position
|
||||
operator+ (const position& begin, const int width)
|
||||
{
|
||||
position res = begin;
|
||||
return res += width;
|
||||
}
|
||||
|
||||
/// Add and assign a position.
|
||||
inline position&
|
||||
operator-= (position& res, const int width)
|
||||
{
|
||||
return res += -width;
|
||||
}
|
||||
|
||||
/// Add two position objects.
|
||||
inline const position
|
||||
operator- (const position& begin, const int width)
|
||||
{
|
||||
return begin + -width;
|
||||
}
|
||||
|
||||
/// Compare two position objects.
|
||||
inline bool
|
||||
operator== (const position& pos1, const position& pos2)
|
||||
{
|
||||
return (pos1.line == pos2.line
|
||||
&& pos1.column == pos2.column
|
||||
&& (pos1.filename == pos2.filename
|
||||
|| (pos1.filename && pos2.filename
|
||||
&& *pos1.filename == *pos2.filename)));
|
||||
}
|
||||
|
||||
/// Compare two position objects.
|
||||
inline bool
|
||||
operator!= (const position& pos1, const position& pos2)
|
||||
{
|
||||
return !(pos1 == pos2);
|
||||
}
|
||||
|
||||
/** \brief Intercept output stream redirection.
|
||||
** \param ostr the destination output stream
|
||||
** \param pos a reference to the position to redirect
|
||||
*/
|
||||
template <typename YYChar>
|
||||
inline std::basic_ostream<YYChar>&
|
||||
operator<< (std::basic_ostream<YYChar>& ostr, const position& pos)
|
||||
{
|
||||
if (pos.filename)
|
||||
ostr << *pos.filename << ':';
|
||||
return ostr << pos.line << '.' << pos.column;
|
||||
}
|
||||
}
|
||||
132
spot/misc/random.cc
Normal file
132
spot/misc/random.cc
Normal file
|
|
@ -0,0 +1,132 @@
|
|||
// -*- coding: utf-8 -*-
|
||||
// Copyright (C) 2011, 2012, 2013, 2014, 2015 Laboratoire de Recherche et
|
||||
// Développement de l'Epita (LRDE).
|
||||
// Copyright (C) 2004 Laboratoire d'Informatique de Paris 6 (LIP6),
|
||||
// département Systèmes Répartis Coopératifs (SRC), Université Pierre
|
||||
// et Marie Curie.
|
||||
//
|
||||
// This file is part of Spot, a model checking library.
|
||||
//
|
||||
// Spot is free software; you can redistribute it and/or modify it
|
||||
// under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation; either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// Spot is distributed in the hope that it will be useful, but WITHOUT
|
||||
// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
||||
// or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public
|
||||
// License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
#include "config.h"
|
||||
#include <spot/misc/random.hh>
|
||||
#include <random>
|
||||
|
||||
namespace spot
|
||||
{
|
||||
static std::mt19937 gen;
|
||||
|
||||
void
|
||||
srand(unsigned int seed)
|
||||
{
|
||||
gen.seed(seed);
|
||||
}
|
||||
|
||||
double
|
||||
drand()
|
||||
{
|
||||
return gen() / (1.0 + gen.max());
|
||||
}
|
||||
|
||||
int
|
||||
mrand(int max)
|
||||
{
|
||||
return static_cast<int>(max * drand());
|
||||
}
|
||||
|
||||
int
|
||||
rrand(int min, int max)
|
||||
{
|
||||
return min + static_cast<int>((max - min + 1) * drand());
|
||||
}
|
||||
|
||||
double
|
||||
nrand()
|
||||
{
|
||||
const double r = drand();
|
||||
|
||||
const double lim = 1.e-20;
|
||||
if (r < lim)
|
||||
return -1./lim;
|
||||
if (r > 1.0 - lim)
|
||||
return 1./lim;
|
||||
|
||||
double t;
|
||||
if (r < 0.5)
|
||||
t = sqrt(-2.0 * log(r));
|
||||
else
|
||||
t = sqrt(-2.0 * log(1.0 - r));
|
||||
|
||||
const double p0 = 0.322232431088;
|
||||
const double p1 = 1.0;
|
||||
const double p2 = 0.342242088547;
|
||||
const double p3 = 0.204231210245e-1;
|
||||
const double p4 = 0.453642210148e-4;
|
||||
const double q0 = 0.099348462606;
|
||||
const double q1 = 0.588581570495;
|
||||
const double q2 = 0.531103462366;
|
||||
const double q3 = 0.103537752850;
|
||||
const double q4 = 0.385607006340e-2;
|
||||
const double p = p0 + t * (p1 + t * (p2 + t * (p3 + t * p4)));
|
||||
const double q = q0 + t * (q1 + t * (q2 + t * (q3 + t * q4)));
|
||||
|
||||
if (r < 0.5)
|
||||
return (p / q) - t;
|
||||
else
|
||||
return t - (p / q);
|
||||
}
|
||||
|
||||
double
|
||||
bmrand()
|
||||
{
|
||||
static double next;
|
||||
static bool has_next = false;
|
||||
|
||||
if (has_next)
|
||||
{
|
||||
has_next = false;
|
||||
return next;
|
||||
}
|
||||
|
||||
double x;
|
||||
double y;
|
||||
double r;
|
||||
do
|
||||
{
|
||||
x = 2.0 * drand() - 1.0;
|
||||
y = 2.0 * drand() - 1.0;
|
||||
r = x * x + y * y;
|
||||
}
|
||||
while (r >= 1.0 || r == 0.0);
|
||||
r = sqrt(-2 * log(r) / r);
|
||||
next = y * r;
|
||||
has_next = true;
|
||||
return x * r;
|
||||
}
|
||||
|
||||
int
|
||||
prand(double p)
|
||||
{
|
||||
double s = 0.0;
|
||||
long x = 0;
|
||||
|
||||
while (s < p)
|
||||
{
|
||||
s -= log(1.0 - drand());
|
||||
++x;
|
||||
}
|
||||
return x - 1;
|
||||
}
|
||||
}
|
||||
139
spot/misc/random.hh
Normal file
139
spot/misc/random.hh
Normal file
|
|
@ -0,0 +1,139 @@
|
|||
// -*- coding: utf-8 -*-
|
||||
// Copyright (C) 2015 Laboratoire de Recherche et Développement
|
||||
// de l'Epita (LRDE).
|
||||
// Copyright (C) 2004 Laboratoire d'Informatique de Paris 6 (LIP6),
|
||||
// département Systèmes Répartis Coopératifs (SRC), Université Pierre
|
||||
// et Marie Curie.
|
||||
//
|
||||
// This file is part of Spot, a model checking library.
|
||||
//
|
||||
// Spot is free software; you can redistribute it and/or modify it
|
||||
// under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation; either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// Spot is distributed in the hope that it will be useful, but WITHOUT
|
||||
// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
||||
// or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public
|
||||
// License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <spot/misc/common.hh>
|
||||
#include <cassert>
|
||||
#include <cmath>
|
||||
#include <vector>
|
||||
|
||||
namespace spot
|
||||
{
|
||||
/// \defgroup random Random functions
|
||||
/// \ingroup misc_tools
|
||||
|
||||
/// \ingroup random
|
||||
/// @{
|
||||
|
||||
/// \brief Reset the seed of the pseudo-random number generator.
|
||||
///
|
||||
/// \see drand, mrand, rrand
|
||||
SPOT_API void srand(unsigned int seed);
|
||||
|
||||
/// \brief Compute a pseudo-random integer value between \a min and
|
||||
/// \a max included.
|
||||
///
|
||||
/// \see drand, mrand, srand
|
||||
SPOT_API int rrand(int min, int max);
|
||||
|
||||
/// \brief Compute a pseudo-random integer value between 0 and
|
||||
/// \a max-1 included.
|
||||
///
|
||||
/// \see drand, rrand, srand
|
||||
SPOT_API int mrand(int max);
|
||||
|
||||
/// \brief Compute a pseudo-random double value
|
||||
/// between 0.0 and 1.0 (1.0 excluded).
|
||||
///
|
||||
/// \see mrand, rrand, srand
|
||||
SPOT_API double drand();
|
||||
|
||||
/// \brief Compute a pseudo-random double value
|
||||
/// following a standard normal distribution. (Odeh & Evans)
|
||||
///
|
||||
/// This uses a polynomial approximation of the inverse cumulated
|
||||
/// density function from Odeh & Evans, Journal of Applied
|
||||
/// Statistics, 1974, vol 23, pp 96-97.
|
||||
SPOT_API double nrand();
|
||||
|
||||
/// \brief Compute a pseudo-random double value
|
||||
/// following a standard normal distribution. (Box-Muller)
|
||||
///
|
||||
/// This uses the polar form of the Box-Muller transform
|
||||
/// to generate random values.
|
||||
SPOT_API double bmrand();
|
||||
|
||||
/// \brief Compute pseudo-random integer value between 0
|
||||
/// and \a n included, following a binomial distribution
|
||||
/// with probability \a p.
|
||||
///
|
||||
/// \a gen must be a random function computing a pseudo-random
|
||||
/// double value following a standard normal distribution.
|
||||
/// Use nrand() or bmrand().
|
||||
///
|
||||
/// Usually approximating a binomial distribution using a normal
|
||||
/// distribution and is accurate only if <code>n*p</code> and
|
||||
/// <code>n*(1-p)</code> are greater than 5.
|
||||
template<double (*gen)()>
|
||||
class barand
|
||||
{
|
||||
public:
|
||||
barand(int n, double p)
|
||||
: n_(n), m_(n * p), s_(sqrt(n * p * (1 - p)))
|
||||
{
|
||||
}
|
||||
|
||||
int
|
||||
rand() const
|
||||
{
|
||||
for (;;)
|
||||
{
|
||||
int x = round(gen() * s_ + m_);
|
||||
if (x < 0)
|
||||
continue;
|
||||
if (x <= n_)
|
||||
return x;
|
||||
}
|
||||
SPOT_UNREACHABLE();
|
||||
return 0;
|
||||
}
|
||||
protected:
|
||||
const int n_;
|
||||
const double m_;
|
||||
const double s_;
|
||||
};
|
||||
|
||||
/// \brief Return a pseudo-random positive integer value
|
||||
/// following a Poisson distribution with parameter \a p.
|
||||
///
|
||||
/// \pre <code>p > 0</code>
|
||||
SPOT_API int prand(double p);
|
||||
|
||||
/// \brief Shuffle the container using mrand function above.
|
||||
/// This allows to get rid off shuffle or random_shuffle that use
|
||||
/// uniform_distribution and RandomIterator that are not portables.
|
||||
template<class iterator_type>
|
||||
SPOT_API void mrandom_shuffle(iterator_type&& first, iterator_type&& last)
|
||||
{
|
||||
auto d = std::distance(first, last);
|
||||
if (d > 1)
|
||||
{
|
||||
for (--last; first < last; ++first, --d)
|
||||
{
|
||||
auto i = mrand(d);
|
||||
std::swap(*first, *(first + i));
|
||||
}
|
||||
}
|
||||
}
|
||||
/// @}
|
||||
}
|
||||
168
spot/misc/satsolver.cc
Normal file
168
spot/misc/satsolver.cc
Normal file
|
|
@ -0,0 +1,168 @@
|
|||
// -*- coding: utf-8 -*-
|
||||
// Copyright (C) 2013, 2014, 2015 Laboratoire de Recherche et Développement
|
||||
// de l'Epita.
|
||||
//
|
||||
// This file is part of Spot, a model checking library.
|
||||
//
|
||||
// Spot is free software; you can redistribute it and/or modify it
|
||||
// under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation; either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// Spot is distributed in the hope that it will be useful, but WITHOUT
|
||||
// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
||||
// or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public
|
||||
// License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
#include "config.h"
|
||||
#include <spot/misc/formater.hh>
|
||||
#include <cstdlib>
|
||||
#include <sstream>
|
||||
#include <stdexcept>
|
||||
#include <spot/misc/satsolver.hh>
|
||||
#include <fstream>
|
||||
#include <limits>
|
||||
#include <sys/wait.h>
|
||||
|
||||
namespace spot
|
||||
{
|
||||
namespace
|
||||
{
|
||||
struct satsolver_command: formater
|
||||
{
|
||||
const char* satsolver;
|
||||
|
||||
satsolver_command()
|
||||
{
|
||||
satsolver = getenv("SPOT_SATSOLVER");
|
||||
if (!satsolver)
|
||||
{
|
||||
satsolver = "glucose -verb=0 -model %I >%O";
|
||||
return;
|
||||
}
|
||||
prime(satsolver);
|
||||
if (!has('I'))
|
||||
throw std::runtime_error("SPOT_SATSOLVER should contain %I to "
|
||||
"indicate how to use the input filename.");
|
||||
if (!has('O'))
|
||||
throw std::runtime_error("SPOT_SATSOLVER should contain %O to "
|
||||
"indicate how to use the output filename.");
|
||||
}
|
||||
|
||||
int
|
||||
run(printable* in, printable* out)
|
||||
{
|
||||
declare('I', in);
|
||||
declare('O', out);
|
||||
std::ostringstream s;
|
||||
format(s, satsolver);
|
||||
int res = system(s.str().c_str());
|
||||
if (res < 0 || (WIFEXITED(res) && WEXITSTATUS(res) == 127))
|
||||
{
|
||||
s << ": failed to execute";
|
||||
throw std::runtime_error(s.str());
|
||||
}
|
||||
// For POSIX shells, "The exit status of a command that
|
||||
// terminated because it received a signal shall be reported
|
||||
// as greater than 128."
|
||||
if (WIFEXITED(res) && WEXITSTATUS(res) >= 128)
|
||||
{
|
||||
s << ": terminated by signal";
|
||||
throw std::runtime_error(s.str());
|
||||
}
|
||||
if (WIFSIGNALED(res))
|
||||
{
|
||||
s << ": terminated by signal " << WTERMSIG(res);
|
||||
throw std::runtime_error(s.str());
|
||||
}
|
||||
return res;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
satsolver::solution
|
||||
satsolver_get_solution(const char* filename)
|
||||
{
|
||||
satsolver::solution sol;
|
||||
std::istream* in;
|
||||
if (filename[0] == '-' && filename[1] == 0)
|
||||
in = &std::cin;
|
||||
else
|
||||
in = new std::fstream(filename, std::ios_base::in);
|
||||
|
||||
int c;
|
||||
while ((c = in->get()) != EOF)
|
||||
{
|
||||
// If a line does not start with 'v ', ignore it.
|
||||
if (c != 'v' || in->get() != ' ')
|
||||
{
|
||||
in->ignore(std::numeric_limits<std::streamsize>::max(), '\n');
|
||||
continue;
|
||||
}
|
||||
// Otherwise, read integers one by one.
|
||||
int i;
|
||||
while (*in >> i)
|
||||
{
|
||||
if (i == 0)
|
||||
goto done;
|
||||
sol.push_back(i);
|
||||
}
|
||||
if (!in->eof())
|
||||
// If we haven't reached end-of-file, then we just attempted
|
||||
// to extract something that wasn't an integer. Clear the
|
||||
// fail bit so that will loop over.
|
||||
in->clear();
|
||||
}
|
||||
done:
|
||||
if (in != &std::cin)
|
||||
delete in;
|
||||
return sol;
|
||||
}
|
||||
|
||||
satsolver::satsolver()
|
||||
: cnf_tmp_(nullptr), cnf_stream_(nullptr)
|
||||
{
|
||||
start();
|
||||
}
|
||||
|
||||
void satsolver::start()
|
||||
{
|
||||
cnf_tmp_ = create_tmpfile("sat-", ".cnf");
|
||||
cnf_stream_ = new std::fstream(cnf_tmp_->name(),
|
||||
std::ios_base::trunc | std::ios_base::out);
|
||||
cnf_stream_->exceptions(std::ifstream::failbit | std::ifstream::badbit);
|
||||
}
|
||||
|
||||
satsolver::~satsolver()
|
||||
{
|
||||
delete cnf_tmp_;
|
||||
delete cnf_stream_;
|
||||
}
|
||||
|
||||
std::ostream& satsolver::operator()()
|
||||
{
|
||||
return *cnf_stream_;
|
||||
}
|
||||
|
||||
satsolver::solution_pair
|
||||
satsolver::get_solution()
|
||||
{
|
||||
delete cnf_stream_; // Close the file.
|
||||
cnf_stream_ = nullptr;
|
||||
|
||||
temporary_file* output = create_tmpfile("sat-", ".out");
|
||||
solution_pair p;
|
||||
|
||||
// Make this static, so the SPOT_SATSOLVER lookup is done only on
|
||||
// the first call to run_sat().
|
||||
static satsolver_command cmd;
|
||||
|
||||
p.first = cmd.run(cnf_tmp_, output);
|
||||
p.second = satsolver_get_solution(output->name());
|
||||
delete output;
|
||||
return p;
|
||||
}
|
||||
}
|
||||
100
spot/misc/satsolver.hh
Normal file
100
spot/misc/satsolver.hh
Normal file
|
|
@ -0,0 +1,100 @@
|
|||
// -*- coding: utf-8 -*-
|
||||
// Copyright (C) 2013 Laboratoire de Recherche et Développement
|
||||
// de l'Epita.
|
||||
//
|
||||
// This file is part of Spot, a model checking library.
|
||||
//
|
||||
// Spot is free software; you can redistribute it and/or modify it
|
||||
// under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation; either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// Spot is distributed in the hope that it will be useful, but WITHOUT
|
||||
// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
||||
// or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public
|
||||
// License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <spot/misc/common.hh>
|
||||
#include <spot/misc/tmpfile.hh>
|
||||
#include <vector>
|
||||
#include <stdexcept>
|
||||
#include <iosfwd>
|
||||
|
||||
namespace spot
|
||||
{
|
||||
class printable;
|
||||
|
||||
class clause_counter
|
||||
{
|
||||
private:
|
||||
int count_;
|
||||
|
||||
public:
|
||||
clause_counter()
|
||||
: count_(0)
|
||||
{
|
||||
}
|
||||
|
||||
void check() const
|
||||
{
|
||||
if (count_ < 0)
|
||||
throw std::runtime_error("too many SAT clauses (more than INT_MAX)");
|
||||
}
|
||||
|
||||
clause_counter& operator++()
|
||||
{
|
||||
++count_;
|
||||
check();
|
||||
return *this;
|
||||
}
|
||||
|
||||
clause_counter& operator+=(int n)
|
||||
{
|
||||
count_ += n;
|
||||
check();
|
||||
return *this;
|
||||
}
|
||||
|
||||
int nb_clauses() const
|
||||
{
|
||||
return count_;
|
||||
}
|
||||
};
|
||||
|
||||
/// \brief Interface with a SAT solver.
|
||||
///
|
||||
/// Call start() to create some temporary file, then send DIMACs
|
||||
/// text to the stream returned by operator(), and finally call
|
||||
/// get_solution().
|
||||
///
|
||||
/// The satsolver called can be configured via the
|
||||
/// <code>SPOT_SATSOLVER</code> environment variable. It
|
||||
/// defaults to
|
||||
/// "satsolver -verb=0 %I >%O"
|
||||
/// where %I and %O are replaced by input and output files.
|
||||
class SPOT_API satsolver
|
||||
{
|
||||
public:
|
||||
satsolver();
|
||||
~satsolver();
|
||||
|
||||
void start();
|
||||
std::ostream& operator()();
|
||||
|
||||
typedef std::vector<int> solution;
|
||||
typedef std::pair<int, solution> solution_pair;
|
||||
solution_pair get_solution();
|
||||
private:
|
||||
temporary_file* cnf_tmp_;
|
||||
std::ostream* cnf_stream_;
|
||||
};
|
||||
|
||||
/// \brief Extract the solution of a SAT solver output.
|
||||
SPOT_API satsolver::solution
|
||||
satsolver_get_solution(const char* filename);
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue